Introduction
This metabarcoding pipeline is based around the targets package,
which is a Make-like pipeline tool for R. The benefit of this is that
all the code is automatically run, and the pipeline skips costly runtime
for tasks that are already up to date.
This page lists the workflow for the Bee Health project (16S + ITS
primers)
Clone the pipeRline github repository
The first step is to clone this github repository, which contains the
required code and directory structure to run the pipeline. To do this,
you will need Git installed on your
computer. If you are running the pipeline in Rstudio, it is best to
create a new project from the github repository. This can be done by
going file > new project > version control >
git then adding https://github.com/alexpiper/piperline.git then change
the name of the project to whatever you are working with.
Alternatively, the repository can be cloned using the command line,
change the ‘folder-name’ to the desired name
# Change into the main directory you wish to make the project in
cd metabarcoding
# Clone the repository
git clone https://github.com/alexpiper/piperline.git folder-name
Prior to analysis
The PipeRline workflow assumes that your sequencing data meets
certain criteria:
- Samples have been demultiplexed, i.e. split into individual
per-sample fastq files. If you want the pipeline to calculate the
index-switching rate, the fastq files need to be re-demultiplexed as the
miseq does not put indexes in fasta headers by default
- For paired-end sequencing data, the forward and reverse reads are in
separate files (ie. not interleaved) with reads arranged in matched
order.
The output directory should be unique for each sequencing run, named
as the flowcell id, within a directory called data
For example:
root/
├── data/
├── CJL7D/
Demultiplex sequencing reads
For this workflow to run, we need to first demultiplex the miseq run
again as the miseq does not put indexes in fasta headers by default, and
also obtain some necessary files from the sequencing folder. The below
code is written for the Agriculture Victoria BASC server, and the
locations will be different if you are using a different HPC
cluster.
#load module
module load bcl2fastq2/2.20.0-foss-2018b
#raise amount of available file handles
ulimit -n 4000
###Run1
#Set up input and outputs
inputdir=/group/sequencing/230421_M03633_0645_000000000-L29KJ #CHANGE TO YOUR SEQ RUN
outputdir=/group/pathogens/IAWS/Personal/Alexp/jessi_dataset/data/L29KJ #CHANGE TO YOUR DATA FOLDER RUN
samplesheet=/group/pathogens/IAWS/Personal/Alexp/jessi_dataset/SampleSheet_L29KJ.csv #CHANGE TO YOUR SAMPLESHEET
# convert samplesheet to unix format
dos2unix $samplesheet
#Demultiplex
bcl2fastq -p 12 --runfolder-dir $inputdir \
--output-dir $outputdir \
--sample-sheet $samplesheet \
--no-lane-splitting --barcode-mismatches 1
# Copy other necessary files and move fastqs
cd $outputdir
cp -r $inputdir/InterOp $outputdir
cp $inputdir/RunInfo.xml $outputdir
cp $inputdir/[Rr]unParameters.xml $outputdir
cp $samplesheet $outputdir
mv **/*.fastq.gz $outputdir
# Append fcid to start of sample names if missing
fcid=$(echo $inputdir | sed 's/^.*-//')
for i in *.fastq.gz; do
if ! [[ $i == $fcid* ]]; then
new=$(echo ${fcid} ${i}) #append together
new=$(echo ${new// /_}) #remove any white space
mv -v "$i" "$new"
fi
done
Install and load R packages and setup
directories
# Load the targets and renv packages
library(targets)
library(tarchetypes)
# Load all packages using renv::restore() and source package list
renv::restore()
source("_targets_packages.R")
# Source ancillary functions
source("R/functions.R")
source("R/themes.R")
Create sample tracking sheet
In order to track samples and relevant QC statistics throughout the
metabarcoding pipeline, we will first create a new samplesheet from our
input samplesheets. This function requires both the SampleSheet.csv used
for the sequencing run, and the runParameters.xml, both of which should
have been automatically obtained from the demultiplexed sequencing run
folder in the bash step above
runs <- dir("data/") #Find all directories within data
SampleSheet <- list.files(paste0("data/", runs), pattern= "SampleSheet", full.names = TRUE)
runParameters <- list.files(paste0("data/", runs), pattern= "[Rr]unParameters.xml", full.names = TRUE)
# Create samplesheet containing samples and run parameters for all runs
samdf <- create_samplesheet(SampleSheet = SampleSheet, runParameters = runParameters, template = "V4") %>%
distinct()
# Check that sample_ids contain fcid, if not; attatch
samdf <- samdf %>%
mutate(sample_id = case_when(
!str_detect(sample_id, fcid) ~ paste0(fcid,"_",sample_id),
TRUE ~ sample_id
))
# Check that samples match samplesheet
fastqFs <- purrr::map(list.dirs("data", recursive=FALSE),
list.files, pattern="_R1_", full.names = TRUE) %>%
unlist() %>%
str_remove(pattern = "^(.*)\\/") %>%
str_remove(pattern = "(?:.(?!_S))+$")
# Filter undetermined reads from sample sheet
fastqFs <- fastqFs[!str_detect(fastqFs, "Undetermined")]
# Check for fastq files that are missing from samplesheet
if (length(setdiff(fastqFs, samdf$sample_id)) > 0) {warning("The fastq file/s: ", setdiff(fastqFs, samdf$sample_id), " are not in the sample sheet") }
# Check for sample_ids that dont have a corresponding fastq file
if (length(setdiff(samdf$sample_id, fastqFs)) > 0) {
warning(paste0("The fastq file: ",
setdiff(samdf$sample_id, fastqFs),
" is missing, dropping from samplesheet \n"))
samdf <- samdf %>%
filter(!sample_id %in% setdiff(samdf$sample_id, fastqFs))
}
# Write out sample tracking sheet
write_csv(samdf, "sample_data/Sample_info.csv")
Add PCR primers to sample sheet
This can either be done manually by editing the
sample_data/Sample_info.csv file, or it can be done in R as below.
If a single primer set was used across all samples, these can simply
be added using a mutate call (tab 1).
If different primers were used for different samples, these can be
set using pattern matching on the sample names with case_when
(tab 2).
If multiple primer sets are used per sample, before indexing, the
pipeline will conduct an extra round of demultiplexing to split each
sample by primer. This option can be set by splitting each primer set
with a semicolon (tab 3)
Multiple primer sets per sample
# Add PCR primers to sample sheet
samdf <- samdf %>%
mutate(pcr_primers = "16SFwd515-16SRev806;gITS7ngs-ITS4ngsUni",
for_primer_seq = "GTGCCAGCMGCCGCGGTAA;GTGARTCATCRARTYTTTG",
rev_primer_seq = "GGACTACHVGGGTWTCTAAT;CCTSCSCTTANTDATATGC"
)
write_csv(samdf, "sample_data/Sample_info.csv")
Create parameters file
The parameters file table the respective target gene, reference
databases, and filtering parameters for each primer set used to amplify
the samples. As the pcr_primers column is used in the pipeline to match
the parameters to the respective sample, it is critical that the primer
names match those set in the previous step.
If a single primer set was used across all samples, these can simply
be added to the table as below (tab 1).
If different primers were used for different samples, these can be
set as different rows in the table using c() when creatign the tibble
(tab 2).
If multiple primer reference databases are to be used for each
sample, these can be set by splitting each a semicolon. The taxonomic
assignment will be conducted sequentially through the databases from
left to right, with the second reference database only being used for
those ASVs that couldnt be assigned to species level using the first
databases (tab 3)
The genetic code parameter is used to check coding markers for stop
codons and frameshifts that commonly indicate pseudogenes. For COI
amplicons from insects, the invertebrate mitochondrial code is “SGC4”,
while the standard genetic code is “SGC0”. If you are targetting other
groups of organisms, all alternative genetic codes can be found by
running Biostrings::GENETIC_CODE_TABLE in R.
Different or multiple primer sets per sample
# Params to add in step_'add_parameters
params <- tibble(
# Run parameters
pcr_primers = c("16SFwd515-16SRev806", "gITS7ngs-ITS4ngsUni"),
target_gene=c("16S", "ITS2"),
# Read filtering
read_min_length = 50,
read_max_length = Inf,
read_max_ee = 1,
read_trunc_length = 225,
read_trim_left = 0,
read_trim_right = 0,
# ASV filtering
asv_min_length = NA_integer_,
asv_max_length = NA_integer_,
genetic_code = NA_character_,
coding = FALSE,
phmm = NA_character_,
# Taxonomic assignment
idtaxa_db = c("reference/GTDB_bac120_arc122_ssu_r202_idtaxa.rds","reference/unite_general_fungi_20221129_idtaxa.rds"),
ref_fasta = c("reference/GTDB_bac120_arc122_ssu_r202.fa.gz","reference/unite_general_fungi_20221129.fa.gz"),
idtaxa_confidence = 60,
run_blast=FALSE,
blast_min_identity = 97,
target_kingdom = NA_character_,
target_phylum = NA_character_,
target_class = NA_character_,
target_order = NA_character_,
target_family = NA_character_,
target_genus = NA_character_,
target_species= NA_character_,
# Sample & Taxon filtering
min_sample_reads = 1000,
min_taxa_reads= NA_integer_, # Minimum reads per ASV to retain
min_taxa_ra = NA_real_, # Minimum relative abundance per ASV to retain. 1e-4 is 0.01%
# General pipeline parameters
threads = 1
)
write_csv(params, "sample_data/loci_params.csv")
Results
See the general
introduction vignette for an in-depth explanation of the results and
quality control outputs
LS0tDQp0aXRsZTogIlBpcGVSbGluZSINCnN1YnRpdGxlOiAiQmVlIEhlYWx0aCAoMTZTICsgSVRTKSBleGFtcGxlIg0KYXV0aG9yOiAiQS5NLiBQaXBlciINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgaGlnaGxpZ2h0ZXI6IG51bGwNCiAgICB0aGVtZTogImZsYXRseSINCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiANCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQgICAgDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBLbml0ciBnbG9iYWwgc2V0dXAgLSBjaGFuZ2UgZXZhbCB0byB0cnVlIHRvIHJ1biBjb2RlDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeSh0YXJnZXRzKQ0KbGlicmFyeSh0YXJjaGV0eXBlcykNCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLGVycm9yPUZBTFNFLCBmaWcuc2hvdyA9ICJob2xkIiwgZmlnLmtlZXAgPSAiYWxsIikNCm9wdHNfY2h1bmskc2V0KGRldiA9ICdwbmcnKQ0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoaXMgbWV0YWJhcmNvZGluZyBwaXBlbGluZSBpcyBiYXNlZCBhcm91bmQgdGhlIHRhcmdldHMgcGFja2FnZSwgd2hpY2ggaXMgYSBNYWtlLWxpa2UgcGlwZWxpbmUgdG9vbCBmb3IgUi4gVGhlIGJlbmVmaXQgb2YgdGhpcyBpcyB0aGF0IGFsbCB0aGUgY29kZSBpcyBhdXRvbWF0aWNhbGx5IHJ1biwgYW5kIHRoZSBwaXBlbGluZSBza2lwcyBjb3N0bHkgcnVudGltZSBmb3IgdGFza3MgdGhhdCBhcmUgYWxyZWFkeSB1cCB0byBkYXRlLg0KDQpUaGlzIHBhZ2UgbGlzdHMgdGhlIHdvcmtmbG93IGZvciB0aGUgQmVlIEhlYWx0aCBwcm9qZWN0ICgxNlMgKyBJVFMgcHJpbWVycykNCg0KIyBDbG9uZSB0aGUgcGlwZVJsaW5lIGdpdGh1YiByZXBvc2l0b3J5DQoNClRoZSBmaXJzdCBzdGVwIGlzIHRvIGNsb25lIHRoaXMgZ2l0aHViIHJlcG9zaXRvcnksIHdoaWNoIGNvbnRhaW5zIHRoZSByZXF1aXJlZCBjb2RlIGFuZCBkaXJlY3Rvcnkgc3RydWN0dXJlIHRvIHJ1biB0aGUgcGlwZWxpbmUuIFRvIGRvIHRoaXMsIHlvdSB3aWxsIG5lZWQgW0dpdF0oaHR0cHM6Ly9naXQtc2NtLmNvbS8pIGluc3RhbGxlZCBvbiB5b3VyIGNvbXB1dGVyLiBJZiB5b3UgYXJlIHJ1bm5pbmcgdGhlIHBpcGVsaW5lIGluIFJzdHVkaW8sIGl0IGlzIGJlc3QgdG8gY3JlYXRlIGEgbmV3IHByb2plY3QgZnJvbSB0aGUgZ2l0aHViIHJlcG9zaXRvcnkuIFRoaXMgY2FuIGJlIGRvbmUgYnkgZ29pbmcgKipmaWxlID4gbmV3IHByb2plY3QgPiB2ZXJzaW9uIGNvbnRyb2wgPiBnaXQqKiB0aGVuIGFkZGluZyBodHRwczovL2dpdGh1Yi5jb20vYWxleHBpcGVyL3BpcGVybGluZS5naXQgdGhlbiBjaGFuZ2UgdGhlIG5hbWUgb2YgdGhlIHByb2plY3QgdG8gd2hhdGV2ZXIgeW91IGFyZSB3b3JraW5nIHdpdGguIA0KDQpBbHRlcm5hdGl2ZWx5LCB0aGUgcmVwb3NpdG9yeSBjYW4gYmUgY2xvbmVkIHVzaW5nIHRoZSBjb21tYW5kIGxpbmUsIGNoYW5nZSB0aGUgJ2ZvbGRlci1uYW1lJyB0byB0aGUgZGVzaXJlZCBuYW1lDQpgYGB7YmFzaH0NCiMgQ2hhbmdlIGludG8gdGhlIG1haW4gZGlyZWN0b3J5IHlvdSB3aXNoIHRvIG1ha2UgdGhlIHByb2plY3QgaW4NCmNkIG1ldGFiYXJjb2RpbmcNCg0KIyBDbG9uZSB0aGUgcmVwb3NpdG9yeQ0KZ2l0IGNsb25lIGh0dHBzOi8vZ2l0aHViLmNvbS9hbGV4cGlwZXIvcGlwZXJsaW5lLmdpdCBmb2xkZXItbmFtZQ0KYGBgDQoNCiMgUHJpb3IgdG8gYW5hbHlzaXMNClRoZSBQaXBlUmxpbmUgd29ya2Zsb3cgYXNzdW1lcyB0aGF0IHlvdXIgc2VxdWVuY2luZyBkYXRhIG1lZXRzIGNlcnRhaW4gY3JpdGVyaWE6DQoNCjEuIFNhbXBsZXMgaGF2ZSBiZWVuIGRlbXVsdGlwbGV4ZWQsIGkuZS4gc3BsaXQgaW50byBpbmRpdmlkdWFsIHBlci1zYW1wbGUgZmFzdHEgZmlsZXMuIElmIHlvdSB3YW50IHRoZSBwaXBlbGluZSB0byBjYWxjdWxhdGUgdGhlIGluZGV4LXN3aXRjaGluZyByYXRlLCB0aGUgZmFzdHEgZmlsZXMgbmVlZCB0byBiZSByZS1kZW11bHRpcGxleGVkIGFzIHRoZSBtaXNlcSBkb2VzIG5vdCBwdXQgaW5kZXhlcyBpbiBmYXN0YSBoZWFkZXJzIGJ5IGRlZmF1bHQNCjIuIEZvciBwYWlyZWQtZW5kIHNlcXVlbmNpbmcgZGF0YSwgdGhlIGZvcndhcmQgYW5kIHJldmVyc2UgcmVhZHMgYXJlIGluIHNlcGFyYXRlIGZpbGVzIChpZS4gbm90IGludGVybGVhdmVkKSB3aXRoIHJlYWRzIGFycmFuZ2VkIGluIG1hdGNoZWQgb3JkZXIuDQoNClRoZSBvdXRwdXQgZGlyZWN0b3J5IHNob3VsZCBiZSB1bmlxdWUgZm9yIGVhY2ggc2VxdWVuY2luZyBydW4sIG5hbWVkIGFzIHRoZSBmbG93Y2VsbCBpZCwgd2l0aGluIGEgZGlyZWN0b3J5IGNhbGxlZCBkYXRhDQoNCkZvciBleGFtcGxlOg0KDQogICAgcm9vdC8NCiAgICAgIOKUnOKUgOKUgCBkYXRhLw0KICAgICAgICAg4pSc4pSA4pSAIENKTDdELw0KICAgICAgICAgDQojIyBEZW11bHRpcGxleCBzZXF1ZW5jaW5nIHJlYWRzDQpGb3IgdGhpcyB3b3JrZmxvdyB0byBydW4sIHdlIG5lZWQgdG8gZmlyc3QgZGVtdWx0aXBsZXggdGhlIG1pc2VxIHJ1biBhZ2FpbiBhcyB0aGUgbWlzZXEgZG9lcyBub3QgcHV0IGluZGV4ZXMgaW4gZmFzdGEgaGVhZGVycyBieSBkZWZhdWx0LCBhbmQgYWxzbyBvYnRhaW4gc29tZSBuZWNlc3NhcnkgZmlsZXMgZnJvbSB0aGUgc2VxdWVuY2luZyBmb2xkZXIuIFRoZSBiZWxvdyBjb2RlIGlzIHdyaXR0ZW4gZm9yIHRoZSBBZ3JpY3VsdHVyZSBWaWN0b3JpYSBCQVNDIHNlcnZlciwgYW5kIHRoZSBsb2NhdGlvbnMgd2lsbCBiZSBkaWZmZXJlbnQgaWYgeW91IGFyZSB1c2luZyBhIGRpZmZlcmVudCBIUEMgY2x1c3Rlci4NCg0KYGBge2Jhc2ggZGVtdWx0aXBsZXggMSBtaXNtYXRjaH0NCiNsb2FkIG1vZHVsZQ0KbW9kdWxlIGxvYWQgYmNsMmZhc3RxMi8yLjIwLjAtZm9zcy0yMDE4Yg0KDQojcmFpc2UgYW1vdW50IG9mIGF2YWlsYWJsZSBmaWxlIGhhbmRsZXMNCnVsaW1pdCAtbiA0MDAwDQoNCiMjI1J1bjENCg0KI1NldCB1cCBpbnB1dCBhbmQgb3V0cHV0cw0KaW5wdXRkaXI9L2dyb3VwL3NlcXVlbmNpbmcvMjMwNDIxX00wMzYzM18wNjQ1XzAwMDAwMDAwMC1MMjlLSiAjQ0hBTkdFIFRPIFlPVVIgU0VRIFJVTg0Kb3V0cHV0ZGlyPS9ncm91cC9wYXRob2dlbnMvSUFXUy9QZXJzb25hbC9BbGV4cC9qZXNzaV9kYXRhc2V0L2RhdGEvTDI5S0ogI0NIQU5HRSBUTyBZT1VSIERBVEEgRk9MREVSIFJVTg0Kc2FtcGxlc2hlZXQ9L2dyb3VwL3BhdGhvZ2Vucy9JQVdTL1BlcnNvbmFsL0FsZXhwL2plc3NpX2RhdGFzZXQvU2FtcGxlU2hlZXRfTDI5S0ouY3N2ICNDSEFOR0UgVE8gWU9VUiBTQU1QTEVTSEVFVA0KDQojIGNvbnZlcnQgc2FtcGxlc2hlZXQgdG8gdW5peCBmb3JtYXQNCmRvczJ1bml4ICRzYW1wbGVzaGVldA0KDQojRGVtdWx0aXBsZXgNCmJjbDJmYXN0cSAtcCAxMiAtLXJ1bmZvbGRlci1kaXIgJGlucHV0ZGlyIFwNCi0tb3V0cHV0LWRpciAkb3V0cHV0ZGlyIFwNCi0tc2FtcGxlLXNoZWV0ICRzYW1wbGVzaGVldCBcDQotLW5vLWxhbmUtc3BsaXR0aW5nIC0tYmFyY29kZS1taXNtYXRjaGVzIDENCg0KIyBDb3B5IG90aGVyIG5lY2Vzc2FyeSBmaWxlcyBhbmQgbW92ZSBmYXN0cXMNCmNkICRvdXRwdXRkaXINCmNwIC1yICRpbnB1dGRpci9JbnRlck9wICRvdXRwdXRkaXINCmNwICRpbnB1dGRpci9SdW5JbmZvLnhtbCAkb3V0cHV0ZGlyDQpjcCAkaW5wdXRkaXIvW1JyXXVuUGFyYW1ldGVycy54bWwgJG91dHB1dGRpcg0KY3AgJHNhbXBsZXNoZWV0ICRvdXRwdXRkaXINCm12ICoqLyouZmFzdHEuZ3ogJG91dHB1dGRpcg0KDQojIEFwcGVuZCBmY2lkIHRvIHN0YXJ0IG9mIHNhbXBsZSBuYW1lcyBpZiBtaXNzaW5nDQpmY2lkPSQoZWNobyAkaW5wdXRkaXIgfCBzZWQgJ3MvXi4qLS8vJykNCmZvciBpIGluICouZmFzdHEuZ3o7IGRvDQogIGlmICEgW1sgJGkgPT0gJGZjaWQqIF1dOyB0aGVuDQogIG5ldz0kKGVjaG8gJHtmY2lkfSAke2l9KSAjYXBwZW5kIHRvZ2V0aGVyDQogIG5ldz0kKGVjaG8gJHtuZXcvLyAvX30pICNyZW1vdmUgYW55IHdoaXRlIHNwYWNlDQogIG12IC12ICIkaSIgIiRuZXciDQogIGZpDQpkb25lDQoNCmBgYA0KDQojIEluc3RhbGwgYW5kIGxvYWQgUiBwYWNrYWdlcyBhbmQgc2V0dXAgZGlyZWN0b3JpZXMgey50YWJzZXR9DQpgYGB7ciBNYW51YWwgaW5zdGFsbH0gDQojIExvYWQgdGhlIHRhcmdldHMgYW5kIHJlbnYgcGFja2FnZXMNCmxpYnJhcnkodGFyZ2V0cykNCmxpYnJhcnkodGFyY2hldHlwZXMpDQoNCiMgTG9hZCBhbGwgcGFja2FnZXMgdXNpbmcgcmVudjo6cmVzdG9yZSgpIGFuZCBzb3VyY2UgcGFja2FnZSBsaXN0DQpyZW52OjpyZXN0b3JlKCkNCnNvdXJjZSgiX3RhcmdldHNfcGFja2FnZXMuUiIpDQoNCiMgU291cmNlIGFuY2lsbGFyeSBmdW5jdGlvbnMNCnNvdXJjZSgiUi9mdW5jdGlvbnMuUiIpDQpzb3VyY2UoIlIvdGhlbWVzLlIiKQ0KYGBgDQoNCiMgQ3JlYXRlIHNhbXBsZSB0cmFja2luZyBzaGVldA0KDQpJbiBvcmRlciB0byB0cmFjayBzYW1wbGVzIGFuZCByZWxldmFudCBRQyBzdGF0aXN0aWNzIHRocm91Z2hvdXQgdGhlIG1ldGFiYXJjb2RpbmcgcGlwZWxpbmUsIHdlIHdpbGwgZmlyc3QgY3JlYXRlIGEgbmV3IHNhbXBsZXNoZWV0IGZyb20gb3VyIGlucHV0IHNhbXBsZXNoZWV0cy4gVGhpcyBmdW5jdGlvbiByZXF1aXJlcyBib3RoIHRoZSBTYW1wbGVTaGVldC5jc3YgdXNlZCBmb3IgdGhlIHNlcXVlbmNpbmcgcnVuLCBhbmQgdGhlIHJ1blBhcmFtZXRlcnMueG1sLCBib3RoIG9mIHdoaWNoIHNob3VsZCBoYXZlIGJlZW4gYXV0b21hdGljYWxseSBvYnRhaW5lZCBmcm9tIHRoZSBkZW11bHRpcGxleGVkIHNlcXVlbmNpbmcgcnVuIGZvbGRlciBpbiB0aGUgYmFzaCBzdGVwIGFib3ZlDQoNCmBgYHtyIGNyZWF0ZSBzYW1wbGVzaGVldH0NCnJ1bnMgPC0gZGlyKCJkYXRhLyIpICNGaW5kIGFsbCBkaXJlY3RvcmllcyB3aXRoaW4gZGF0YQ0KU2FtcGxlU2hlZXQgPC0gbGlzdC5maWxlcyhwYXN0ZTAoImRhdGEvIiwgcnVucyksIHBhdHRlcm49ICJTYW1wbGVTaGVldCIsIGZ1bGwubmFtZXMgPSBUUlVFKQ0KcnVuUGFyYW1ldGVycyA8LSBsaXN0LmZpbGVzKHBhc3RlMCgiZGF0YS8iLCBydW5zKSwgcGF0dGVybj0gIltScl11blBhcmFtZXRlcnMueG1sIiwgZnVsbC5uYW1lcyA9IFRSVUUpDQoNCiMgQ3JlYXRlIHNhbXBsZXNoZWV0IGNvbnRhaW5pbmcgc2FtcGxlcyBhbmQgcnVuIHBhcmFtZXRlcnMgZm9yIGFsbCBydW5zDQpzYW1kZiA8LSBjcmVhdGVfc2FtcGxlc2hlZXQoU2FtcGxlU2hlZXQgPSBTYW1wbGVTaGVldCwgcnVuUGFyYW1ldGVycyA9IHJ1blBhcmFtZXRlcnMsIHRlbXBsYXRlID0gIlY0IikgJT4lDQogIGRpc3RpbmN0KCkNCg0KIyBDaGVjayB0aGF0IHNhbXBsZV9pZHMgY29udGFpbiBmY2lkLCBpZiBub3Q7IGF0dGF0Y2gNCnNhbWRmIDwtIHNhbWRmICU+JQ0KICBtdXRhdGUoc2FtcGxlX2lkID0gY2FzZV93aGVuKA0KICAgICFzdHJfZGV0ZWN0KHNhbXBsZV9pZCwgZmNpZCkgfiBwYXN0ZTAoZmNpZCwiXyIsc2FtcGxlX2lkKSwNCiAgICBUUlVFIH4gc2FtcGxlX2lkDQogICkpDQoNCiMgQ2hlY2sgdGhhdCBzYW1wbGVzIG1hdGNoIHNhbXBsZXNoZWV0DQpmYXN0cUZzIDwtIHB1cnJyOjptYXAobGlzdC5kaXJzKCJkYXRhIiwgcmVjdXJzaXZlPUZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgICBsaXN0LmZpbGVzLCBwYXR0ZXJuPSJfUjFfIiwgZnVsbC5uYW1lcyA9IFRSVUUpICU+JQ0KICB1bmxpc3QoKSAlPiUNCiAgc3RyX3JlbW92ZShwYXR0ZXJuID0gIl4oLiopXFwvIikgJT4lDQogIHN0cl9yZW1vdmUocGF0dGVybiA9ICIoPzouKD8hX1MpKSskIikNCg0KIyBGaWx0ZXIgdW5kZXRlcm1pbmVkIHJlYWRzIGZyb20gc2FtcGxlIHNoZWV0DQpmYXN0cUZzIDwtIGZhc3RxRnNbIXN0cl9kZXRlY3QoZmFzdHFGcywgIlVuZGV0ZXJtaW5lZCIpXQ0KDQojIENoZWNrIGZvciBmYXN0cSBmaWxlcyB0aGF0IGFyZSBtaXNzaW5nIGZyb20gc2FtcGxlc2hlZXQNCmlmIChsZW5ndGgoc2V0ZGlmZihmYXN0cUZzLCBzYW1kZiRzYW1wbGVfaWQpKSA+IDApIHt3YXJuaW5nKCJUaGUgZmFzdHEgZmlsZS9zOiAiLCBzZXRkaWZmKGZhc3RxRnMsIHNhbWRmJHNhbXBsZV9pZCksICIgYXJlIG5vdCBpbiB0aGUgc2FtcGxlIHNoZWV0IikgfQ0KDQojIENoZWNrIGZvciBzYW1wbGVfaWRzIHRoYXQgZG9udCBoYXZlIGEgY29ycmVzcG9uZGluZyBmYXN0cSBmaWxlDQppZiAobGVuZ3RoKHNldGRpZmYoc2FtZGYkc2FtcGxlX2lkLCBmYXN0cUZzKSkgPiAwKSB7DQogIHdhcm5pbmcocGFzdGUwKCJUaGUgZmFzdHEgZmlsZTogIiwNCiAgICAgICAgICAgICAgICAgc2V0ZGlmZihzYW1kZiRzYW1wbGVfaWQsIGZhc3RxRnMpLA0KICAgICAgICAgICAgICAgICAiIGlzIG1pc3NpbmcsIGRyb3BwaW5nIGZyb20gc2FtcGxlc2hlZXQgXG4iKSkgDQogIHNhbWRmIDwtIHNhbWRmICU+JQ0KICAgIGZpbHRlcighc2FtcGxlX2lkICVpbiUgc2V0ZGlmZihzYW1kZiRzYW1wbGVfaWQsIGZhc3RxRnMpKQ0KfQ0KDQojIFdyaXRlIG91dCBzYW1wbGUgdHJhY2tpbmcgc2hlZXQNCndyaXRlX2NzdihzYW1kZiwgInNhbXBsZV9kYXRhL1NhbXBsZV9pbmZvLmNzdiIpDQpgYGANCg0KIyBBZGQgUENSIHByaW1lcnMgdG8gc2FtcGxlIHNoZWV0IHsudGFic2V0fQ0KDQpUaGlzIGNhbiBlaXRoZXIgYmUgZG9uZSBtYW51YWxseSBieSBlZGl0aW5nIHRoZSBzYW1wbGVfZGF0YS9TYW1wbGVfaW5mby5jc3YgZmlsZSwgb3IgaXQgY2FuIGJlIGRvbmUgaW4gUiBhcyBiZWxvdy4NCg0KSWYgYSBzaW5nbGUgcHJpbWVyIHNldCB3YXMgdXNlZCBhY3Jvc3MgYWxsIHNhbXBsZXMsIHRoZXNlIGNhbiBzaW1wbHkgYmUgYWRkZWQgdXNpbmcgYSBtdXRhdGUgY2FsbCAoKip0YWIgMSoqKS4NCg0KSWYgZGlmZmVyZW50IHByaW1lcnMgd2VyZSB1c2VkIGZvciBkaWZmZXJlbnQgc2FtcGxlcywgdGhlc2UgY2FuIGJlIHNldCB1c2luZyBwYXR0ZXJuIG1hdGNoaW5nIG9uIHRoZSBzYW1wbGUgbmFtZXMgd2l0aCBjYXNlX3doZW4gKCoqdGFiIDIqKikuDQoNCklmIG11bHRpcGxlIHByaW1lciBzZXRzIGFyZSB1c2VkIHBlciBzYW1wbGUsIGJlZm9yZSBpbmRleGluZywgdGhlIHBpcGVsaW5lIHdpbGwgY29uZHVjdCBhbiBleHRyYSByb3VuZCBvZiBkZW11bHRpcGxleGluZyB0byBzcGxpdCBlYWNoIHNhbXBsZSBieSBwcmltZXIuIFRoaXMgb3B0aW9uIGNhbiBiZSBzZXQgYnkgc3BsaXR0aW5nIGVhY2ggcHJpbWVyIHNldCB3aXRoIGEgc2VtaWNvbG9uICgqKnRhYiAzKiopIA0KDQoNCiMjIE11bHRpcGxlIHByaW1lciBzZXRzIHBlciBzYW1wbGUNCg0KYGBge1J9DQojIEFkZCBQQ1IgcHJpbWVycyB0byBzYW1wbGUgc2hlZXQNCnNhbWRmIDwtIHNhbWRmICU+JQ0KICBtdXRhdGUocGNyX3ByaW1lcnMgPSAiMTZTRndkNTE1LTE2U1JldjgwNjtnSVRTN25ncy1JVFM0bmdzVW5pIiwNCiAgZm9yX3ByaW1lcl9zZXEgPSAiR1RHQ0NBR0NNR0NDR0NHR1RBQTtHVEdBUlRDQVRDUkFSVFlUVFRHIiwNCiAgcmV2X3ByaW1lcl9zZXEgPSAiR0dBQ1RBQ0hWR0dHVFdUQ1RBQVQ7Q0NUU0NTQ1RUQU5UREFUQVRHQyINCiAgKQ0KDQp3cml0ZV9jc3Yoc2FtZGYsICJzYW1wbGVfZGF0YS9TYW1wbGVfaW5mby5jc3YiKQ0KYGBgDQoNCg0KIyBDcmVhdGUgcGFyYW1ldGVycyBmaWxlDQoNClRoZSBwYXJhbWV0ZXJzIGZpbGUgdGFibGUgdGhlIHJlc3BlY3RpdmUgdGFyZ2V0IGdlbmUsIHJlZmVyZW5jZSBkYXRhYmFzZXMsIGFuZCBmaWx0ZXJpbmcgcGFyYW1ldGVycyBmb3IgZWFjaCBwcmltZXIgc2V0IHVzZWQgdG8gYW1wbGlmeSB0aGUgc2FtcGxlcy4gQXMgdGhlIHBjcl9wcmltZXJzIGNvbHVtbiBpcyB1c2VkIGluIHRoZSBwaXBlbGluZSB0byBtYXRjaCB0aGUgcGFyYW1ldGVycyB0byB0aGUgcmVzcGVjdGl2ZSBzYW1wbGUsIGl0IGlzIGNyaXRpY2FsIHRoYXQgdGhlIHByaW1lciBuYW1lcyBtYXRjaCB0aG9zZSBzZXQgaW4gdGhlIHByZXZpb3VzIHN0ZXAuDQoNCklmIGEgc2luZ2xlIHByaW1lciBzZXQgd2FzIHVzZWQgYWNyb3NzIGFsbCBzYW1wbGVzLCB0aGVzZSBjYW4gc2ltcGx5IGJlIGFkZGVkIHRvIHRoZSB0YWJsZSBhcyBiZWxvdyAoKip0YWIgMSoqKS4NCg0KSWYgZGlmZmVyZW50IHByaW1lcnMgd2VyZSB1c2VkIGZvciBkaWZmZXJlbnQgc2FtcGxlcywgdGhlc2UgY2FuIGJlIHNldCBhcyBkaWZmZXJlbnQgcm93cyBpbiB0aGUgdGFibGUgdXNpbmcgYygpIHdoZW4gY3JlYXRpZ24gdGhlIHRpYmJsZSAoKip0YWIgMioqKS4NCg0KSWYgbXVsdGlwbGUgcHJpbWVyIHJlZmVyZW5jZSBkYXRhYmFzZXMgYXJlIHRvIGJlIHVzZWQgZm9yIGVhY2ggc2FtcGxlLCB0aGVzZSBjYW4gYmUgc2V0IGJ5IHNwbGl0dGluZyBlYWNoIGEgc2VtaWNvbG9uLiBUaGUgdGF4b25vbWljIGFzc2lnbm1lbnQgd2lsbCBiZSBjb25kdWN0ZWQgc2VxdWVudGlhbGx5IHRocm91Z2ggdGhlIGRhdGFiYXNlcyBmcm9tIGxlZnQgdG8gcmlnaHQsIHdpdGggdGhlIHNlY29uZCByZWZlcmVuY2UgZGF0YWJhc2Ugb25seSBiZWluZyB1c2VkIGZvciB0aG9zZSBBU1ZzIHRoYXQgY291bGRudCBiZSBhc3NpZ25lZCB0byBzcGVjaWVzIGxldmVsIHVzaW5nIHRoZSBmaXJzdCBkYXRhYmFzZXMgKCoqdGFiIDMqKikgDQoNClRoZSBnZW5ldGljIGNvZGUgcGFyYW1ldGVyIGlzIHVzZWQgdG8gY2hlY2sgY29kaW5nIG1hcmtlcnMgZm9yIHN0b3AgY29kb25zIGFuZCBmcmFtZXNoaWZ0cyB0aGF0IGNvbW1vbmx5IGluZGljYXRlIHBzZXVkb2dlbmVzLiBGb3IgQ09JIGFtcGxpY29ucyBmcm9tIGluc2VjdHMsIHRoZSBpbnZlcnRlYnJhdGUgbWl0b2Nob25kcmlhbCBjb2RlIGlzICJTR0M0Iiwgd2hpbGUgdGhlIHN0YW5kYXJkIGdlbmV0aWMgY29kZSBpcyAiU0dDMCIuIElmIHlvdSBhcmUgdGFyZ2V0dGluZyBvdGhlciBncm91cHMgb2Ygb3JnYW5pc21zLCBhbGwgYWx0ZXJuYXRpdmUgZ2VuZXRpYyBjb2RlcyBjYW4gYmUgZm91bmQgYnkgcnVubmluZyBCaW9zdHJpbmdzOjpHRU5FVElDX0NPREVfVEFCTEUgaW4gUi4NCg0KIyMgRGlmZmVyZW50IG9yIG11bHRpcGxlIHByaW1lciBzZXRzIHBlciBzYW1wbGUNCg0KYGBge1J9DQojIFBhcmFtcyB0byBhZGQgaW4gc3RlcF8nYWRkX3BhcmFtZXRlcnMNCnBhcmFtcyA8LSB0aWJibGUoDQogICMgUnVuIHBhcmFtZXRlcnMNCiAgcGNyX3ByaW1lcnMgPSBjKCIxNlNGd2Q1MTUtMTZTUmV2ODA2IiwgImdJVFM3bmdzLUlUUzRuZ3NVbmkiKSwNCiAgdGFyZ2V0X2dlbmU9YygiMTZTIiwgIklUUzIiKSwNCg0KICAjIFJlYWQgZmlsdGVyaW5nDQogIHJlYWRfbWluX2xlbmd0aCA9IDUwLA0KICByZWFkX21heF9sZW5ndGggPSBJbmYsDQogIHJlYWRfbWF4X2VlID0gMSwNCiAgcmVhZF90cnVuY19sZW5ndGggPSAyMjUsDQogIHJlYWRfdHJpbV9sZWZ0ID0gMCwNCiAgcmVhZF90cmltX3JpZ2h0ID0gMCwNCiAgDQogICMgQVNWIGZpbHRlcmluZw0KICBhc3ZfbWluX2xlbmd0aCA9IE5BX2ludGVnZXJfLA0KICBhc3ZfbWF4X2xlbmd0aCA9IE5BX2ludGVnZXJfLA0KICBnZW5ldGljX2NvZGUgPSBOQV9jaGFyYWN0ZXJfLA0KICBjb2RpbmcgPSBGQUxTRSwNCiAgcGhtbSA9IE5BX2NoYXJhY3Rlcl8sDQogIA0KICAjIFRheG9ub21pYyBhc3NpZ25tZW50DQogIGlkdGF4YV9kYiA9IGMoInJlZmVyZW5jZS9HVERCX2JhYzEyMF9hcmMxMjJfc3N1X3IyMDJfaWR0YXhhLnJkcyIsInJlZmVyZW5jZS91bml0ZV9nZW5lcmFsX2Z1bmdpXzIwMjIxMTI5X2lkdGF4YS5yZHMiKSwNCiAgcmVmX2Zhc3RhID0gYygicmVmZXJlbmNlL0dUREJfYmFjMTIwX2FyYzEyMl9zc3VfcjIwMi5mYS5neiIsInJlZmVyZW5jZS91bml0ZV9nZW5lcmFsX2Z1bmdpXzIwMjIxMTI5LmZhLmd6IiksDQogIGlkdGF4YV9jb25maWRlbmNlID0gNjAsDQogIHJ1bl9ibGFzdD1GQUxTRSwNCiAgYmxhc3RfbWluX2lkZW50aXR5ID0gOTcsDQogIHRhcmdldF9raW5nZG9tID0gTkFfY2hhcmFjdGVyXywNCiAgdGFyZ2V0X3BoeWx1bSA9IE5BX2NoYXJhY3Rlcl8sDQogIHRhcmdldF9jbGFzcyA9IE5BX2NoYXJhY3Rlcl8sDQogIHRhcmdldF9vcmRlciA9IE5BX2NoYXJhY3Rlcl8sDQogIHRhcmdldF9mYW1pbHkgPSBOQV9jaGFyYWN0ZXJfLCANCiAgdGFyZ2V0X2dlbnVzID0gTkFfY2hhcmFjdGVyXywgIA0KICB0YXJnZXRfc3BlY2llcz0gTkFfY2hhcmFjdGVyXywgIA0KICANCiAgIyBTYW1wbGUgJiBUYXhvbiBmaWx0ZXJpbmcNCiAgbWluX3NhbXBsZV9yZWFkcyA9IDEwMDAsDQogIG1pbl90YXhhX3JlYWRzPSBOQV9pbnRlZ2VyXywgIyBNaW5pbXVtIHJlYWRzIHBlciBBU1YgdG8gcmV0YWluDQogIG1pbl90YXhhX3JhID0gTkFfcmVhbF8sICMgTWluaW11bSByZWxhdGl2ZSBhYnVuZGFuY2UgcGVyIEFTViB0byByZXRhaW4uIDFlLTQgaXMgMC4wMSUNCiAgICANCiAgIyBHZW5lcmFsIHBpcGVsaW5lIHBhcmFtZXRlcnMNCiAgdGhyZWFkcyA9IDENCikNCg0Kd3JpdGVfY3N2KHBhcmFtcywgInNhbXBsZV9kYXRhL2xvY2lfcGFyYW1zLmNzdiIpDQpgYGANCg0KIyBSdW4gcGlwZWxpbmUNCg0KYGBge3J9DQp0YXJfbWFrZSgpDQpgYGANCg0KIyBSZXN1bHRzDQoNClNlZSB0aGUgW2dlbmVyYWwgaW50cm9kdWN0aW9uXShodHRwczovL2FsZXhwaXBlci5naXRodWIuaW8vcGlwZXJsaW5lL3ZpZ25ldHRlcy9nZW5lcmFsLmh0bWwpIHZpZ25ldHRlIGZvciBhbiBpbi1kZXB0aCBleHBsYW5hdGlvbiBvZiB0aGUgcmVzdWx0cyBhbmQgcXVhbGl0eSBjb250cm9sIG91dHB1dHM=