Prepping and getting the data ready

First steps are to use iinit in terminal to connect to Data Store so you can access the fastq files Directions here

In summary, use the following when prompted:
Host name (DNS) of the server to connect to: data.cyverse.org
Port number: 1247
irods user name
irods zone: iplant
iRODS password

Install and load packages

No need to do any installations in VICE app. Packages should come pre-installed. But need to run libraries:

library(dada2)
Loading required package: Rcpp
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
library(DECIPHER)
Loading required package: Biostrings
Loading required package: BiocGenerics
Loading required package: parallel

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap,
    parApply, parCapply, parLapply, parLapplyLB, parRapply, parSapply, parSapplyLB

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname, do.call,
    duplicated, eval, evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted,
    lapply, Map, mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
    Position, rank, rbind, Reduce, rownames, sapply, setdiff, sort, table, tapply, union,
    unique, unsplit, which, which.max, which.min

Loading required package: S4Vectors
Loading required package: stats4

Attaching package: ‘S4Vectors’

The following object is masked from ‘package:base’:

    expand.grid

Loading required package: IRanges
Loading required package: XVector

Attaching package: ‘Biostrings’

The following object is masked from ‘package:base’:

    strsplit

Loading required package: RSQLite

Get sample names- run in Terminal

cd raw_data
ls *_1.fastq.gz | cut -f 1 -d "_" > ../samples

Take a look at the untrimmed reads

plotQualityProfile(forward_reads[1:5])
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing
scale.

plotQualityProfile(reverse_reads[1:5])
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing
scale.

From the above you can see the reads are ~300bp and the quality is OK, with the R reads being poorer than the F reads.

Removing primers using cutadapt- run in Terminal

Sequencing facility told me that the way that they sequence in a way that primers do not get sequenced. Still, check for and remove possible primers

Primers from Stoeck et al. 2010: TAReuk454FWD1: (5’- CCAGCASCYGCGGTAATTCC -3’)
TAReukREV3 (5’- ACTTTCGTTCTTGATYRA -3’)

(Also found a usefule website with euk primers from PR2 people:

Cut F primer and rev comp of R primer from F reads with -a option Cut R primer and rev comp of F primer from R reads with -A option

Set the min size. I tried setting it to be about 10% smaller than the amplicon length (~260) but after playing around and even going down to 200bp, it was throwing out most reads. Alternatively, I set NO min length but then it was writing empty reads to the fast1 file, which led to errors afterward. So decided on min length of 150 to compromise.

cd ..
mkdir trimmed_fastq
cd raw_data

# Run in loop

for sample in $(cat ../samples)
do
    echo "On sample: $sample"
cutadapt -a ^CCAGCASCYGCGGTAATTCC...TYRATCAAGAACGAAAGT \
-A ^ACTTTCGTTCTTGATYRA...GGAATTACCGCRGSTGCTGG \
--discard-untrimmed \
-m 150 \
-o ../trimmed_fastq/${sample}_1_trimmed.fastq.gz -p ../trimmed_fastq/${sample}_2_trimmed.fastq.gz \
${sample}_1.fastq.gz ${sample}_2.fastq.gz \
>> ../trimmed_fastq/cutadapt_primer_trimming_stats.txt 2>&1
done

Check output stats

paste ../samples <(grep "passing" ../trimmed_fastq/cutadapt_primer_trimming_stats.txt | cut -f3 -d "(" | tr -d ")") <(grep "filtered" ../trimmed_fastq/cutadapt_primer_trimming_stats.txt | cut -f3 -d "(" | tr -d ")")

Retained ~30-90% of reads in most cases

DADA2- back to R console

Set our working directory and list our files

setwd("~/")
list.files() # make sure what we think is here is actually here

Now let’s take a look at the trimmed reads.

forward_reads_trimmed <- paste0("eukaryote_amplicons/trimmed_fastq/", samples, "_1_trimmed.fastq.gz")
reverse_reads_trimmed <- paste0("eukaryote_amplicons/trimmed_fastq/", samples, "_2_trimmed.fastq.gz")

# And plot 
plotQualityProfile(forward_reads_trimmed[1:5])
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing
scale.

plotQualityProfile(reverse_reads_trimmed[1:5])
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing
scale.

Comparing the above to the pre-trimmed reads, they look very similar but got rid of short messiness in beginning of reads (primer). Can’t see much change in end of reads but I know they were trimmed based on output file. Overall quality is too low to tell.

Quality Filtering

Make a directory for filtered reads

cd ..
mkdir filtered_fastq

Make variables containing the file names for the new filtered forward and reverse reads that we will make

filtered_forward_reads <- paste0("eukaryote_amplicons/filtered_fastq/",samples, "_1_filtered.fastq.gz")
filtered_reverse_reads <- paste0("eukaryote_amplicons/filtered_fastq/",samples, "_2_filtered.fastq.gz")

Based on how the quality plots look, determine how much to cut from each side. Trim the F reads at 275 Trim the R reads to 200 Also I want to run this step to trim out the low-quality individual reads (set maxEE to 1 for both F and R reads). The rm.phix = TRUE option removes any leftover PhiX genomic DNA (that gets added as a standard during sequencing). Pick a min length ~shorter than the min trimmed length (in this case 140 for R reads). I also set truncQ to truncate any read that has a quality score less than 2. Multithreading for this function does not work well (even according to documentation) so needed to skip that. Takes a while to run

filtered_out <- filterAndTrim(forward_reads_trimmed, filtered_forward_reads,
                reverse_reads_trimmed, filtered_reverse_reads, maxEE=c(2,2),
                rm.phix=TRUE, minLen=140, truncLen=c(275,200), truncQ = 2, maxN=0)

Check out the quality profiles again.

filtered_out
                              reads.in reads.out
SRR3735256_1_trimmed.fastq.gz  1694740    672650
SRR3735257_1_trimmed.fastq.gz   939328    379858
SRR3735258_1_trimmed.fastq.gz   848296    357038
SRR3735259_1_trimmed.fastq.gz   912372    376890
SRR3735260_1_trimmed.fastq.gz  1268530    546022
SRR3735261_1_trimmed.fastq.gz  1211714    539356
SRR3735262_1_trimmed.fastq.gz  1502614    678796
SRR3735263_1_trimmed.fastq.gz  1618546    729742
SRR3735264_1_trimmed.fastq.gz   757200    339996
SRR3735265_1_trimmed.fastq.gz    64796     26770
SRR3735266_1_trimmed.fastq.gz   502478    213672
SRR3735268_1_trimmed.fastq.gz   292884    129708
SRR3735269_1_trimmed.fastq.gz   884870    312384
SRR3735270_1_trimmed.fastq.gz   494804    222178
SRR3735271_1_trimmed.fastq.gz   258638    106318
SRR3735272_1_trimmed.fastq.gz    96490     40056
SRR3735273_1_trimmed.fastq.gz   396728    155174
SRR3735274_1_trimmed.fastq.gz   299020    122274
SRR3735275_1_trimmed.fastq.gz      744       108
SRR3735276_1_trimmed.fastq.gz   717026    286730
SRR3735277_1_trimmed.fastq.gz   557174    242266
SRR3735278_1_trimmed.fastq.gz   255692    113588
SRR3735279_1_trimmed.fastq.gz   495828    233930
SRR3735280_1_trimmed.fastq.gz   930416    397086
SRR3735281_1_trimmed.fastq.gz   376996    180378
SRR3735283_1_trimmed.fastq.gz    69796     28740
SRR3735284_1_trimmed.fastq.gz   152264     60258
SRR3735285_1_trimmed.fastq.gz    54964     21634
SRR3735286_1_trimmed.fastq.gz     9232      3298
SRR3735287_1_trimmed.fastq.gz  2006172    846262
SRR3735288_1_trimmed.fastq.gz   477210    184942
SRR3735289_1_trimmed.fastq.gz     2982       102
SRR3735290_1_trimmed.fastq.gz   257834    116018
SRR3735292_1_trimmed.fastq.gz  1271286    589140
SRR3735293_1_trimmed.fastq.gz  1445290    604964
SRR3735294_1_trimmed.fastq.gz   393670    167378
SRR3735295_1_trimmed.fastq.gz   334082    133454
SRR3735296_1_trimmed.fastq.gz   669444    309080
SRR3735297_1_trimmed.fastq.gz     1030        76
SRR3735298_1_trimmed.fastq.gz   263486    110028
SRR3735299_1_trimmed.fastq.gz     2056       128
SRR3735300_1_trimmed.fastq.gz  1383836    557764
SRR3735301_1_trimmed.fastq.gz  1094722    498002
SRR3735302_1_trimmed.fastq.gz  1030404    474520
SRR3735303_1_trimmed.fastq.gz   303198    129620
SRR3735304_1_trimmed.fastq.gz      648        94
SRR3735305_1_trimmed.fastq.gz   824464    319248
plotQualityProfile(filtered_forward_reads[1:5])
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing
scale.

plotQualityProfile(filtered_reverse_reads[1:5])
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing
scale.

Look much better

Save workspace up to this point.

save.image(file = "Cariaco_euk18S_dada2_upto_filterfastq.RData")

And back up in data store just in case

iput -f Cariaco_euk18S_dada2_upto_filterfastq.RData
iput -f Cariaco_Euk_PR2_DECIPHER_annotation.Rmd
iput -f samples
iput -rf filtered_fastq
iput -rf trimmed_fastq
# load("Cariaco_euk18S_dada2_upto_filterfastq.RData")

Error profiling

Next, DADA2 tries to learn the error signature of our dataset. This step takes a while

err_forward_reads <- learnErrors(filtered_forward_reads, multithread=TRUE)
err_reverse_reads <- learnErrors(filtered_reverse_reads, multithread=TRUE)

Plot the error profiles

plotErrors(err_forward_reads, nominalQ=TRUE)

plotErrors(err_reverse_reads, nominalQ=TRUE)

The creators of DADA2 describe this here. The profiles are the error rates for each possible transition in the read (A->C, A->G, etc). Generally in the above plots, you want to see that the black dots (observed error rates for each quality score) match well with the black lines (the estimated error rate). The red line is what is expected based on the quality score.

Backup again since this step above takes awhile Save workspace up to this point.

save.image(file = "Cariaco_euk18S_dada2_upto_errorprofile.RData")

And back up in data store just in case

iput -f Cariaco_euk18S_dada2_upto_errorprofile.RData
iput -f Cariaco_Euk_PR2_DECIPHER_annotation.Rmd

Inferring ASVs

Use the dada command to infer ASVs. We are going to use the pooling option “psuedo” which is described here. This step also takes awhile

#setwd("20200710backup/") # only used this because had to resetup analysis
dada_forward <- dada(filtered_forward_reads, err=err_forward_reads, pool="pseudo", multithread=TRUE) 
dada_reverse <- dada(filtered_reverse_reads, err=err_reverse_reads, pool="pseudo", multithread=TRUE)
# and save in this code block incase I get logged out again!
save.image(file = "Cariaco_euk18S_dada2_upto_inferasv.RData")

And back up in data store just in case

iput Cariaco_euk18S_dada2_upto_inferasv.RData
iput -f Cariaco_Euk_PR2_DECIPHER_annotation.Rmd

Merge inferred reads

Dada2 will merge reads wherever the overlap is identical between the F and R reads. I trimmed the F reads to 275 and R reads to 200 . The full amplicon size (after removing primers) should be 963-583 = 380. So the F read should be sequenced from position ~583 to ~858 (583+275) and the R reads should be sequenced from ~ position 963 to 763 (963-200). This leaves overlap between position ~763 to 858 or about 95bp. Set minimum to about half this

#setwd("20200710backup/") # only used this because had to resetup analysis
merged_amplicons <- mergePairs(dada_forward, filtered_forward_reads, dada_reverse, filtered_reverse_reads, trimOverhang=TRUE, minOverlap=50, verbose = TRUE)

Back up again

save.image(file = "Cariaco_euk18S_upto_merge.RData")
iput HRE_sewage_microbiome_upto_merge.RData
iput -f HRE_Sewage_microbiome_DECIPHER_20200710.Rmd

Load from Data Store

load("eukaryote_amplicons/Cariaco_euk18S_upto_merge.RData")
names(merged_amplicons)
 [1] "SRR3735256" "SRR3735257" "SRR3735258" "SRR3735259" "SRR3735260" "SRR3735261" "SRR3735262"
 [8] "SRR3735263" "SRR3735264" "SRR3735265" "SRR3735266" "SRR3735268" "SRR3735269" "SRR3735270"
[15] "SRR3735271" "SRR3735272" "SRR3735273" "SRR3735274" "SRR3735275" "SRR3735276" "SRR3735277"
[22] "SRR3735278" "SRR3735279" "SRR3735280" "SRR3735281" "SRR3735283" "SRR3735284" "SRR3735285"
[29] "SRR3735286" "SRR3735287" "SRR3735288" "SRR3735289" "SRR3735290" "SRR3735292" "SRR3735293"
[36] "SRR3735294" "SRR3735295" "SRR3735296" "SRR3735297" "SRR3735298" "SRR3735299" "SRR3735300"
[43] "SRR3735301" "SRR3735302" "SRR3735303" "SRR3735304" "SRR3735305"
# Initially these names have the full name with `fastq.gz` in the name. Change to just sample name
names(merged_amplicons) <- samples

# Check some other things
length(merged_amplicons) # 47 elements in this list, one for each of our samples
[1] 47
class(merged_amplicons$SRR3735256) # each element of the list is a dataframe that can be accessed and manipulated like any ordinary dataframe
[1] "data.frame"
names(merged_amplicons$SRR3735256) # the names() function on a dataframe gives you the column names
[1] "sequence"  "abundance" "forward"   "reverse"   "nmatch"    "nmismatch" "nindel"    "prefer"   
[9] "accept"   
# "sequence"  "abundance" "forward"   "reverse"   "nmatch"    "nmismatch" "nindel"    "prefer"    "accept"

Creating a sequence table

seqtab <- makeSequenceTable(merged_amplicons)
class(seqtab) # matrix
[1] "matrix"
dim(seqtab) # 47 samples, 78182 unique ASVs
[1]    47 78182

Backup notebook

iput -f Cariaco_Euk_PR2_DECIPHER_annotation_20200827.Rmd

Removing chimeras

# though we a lot of unique sequences, we don't know if they held a lot in terms of abundance, this is one quick way to look at that
sum(seqtab.nochim)/sum(seqtab) 
[1] 0.8092498

Backup again since this step above takes awhile

save.image(file = "Cariaco_euk18S_upto_chimera.RData")
iput -f Cariaco_Euk_PR2_DECIPHER_annotation_20200827_b.Rmd
iput Cariaco_euk18S_upto_chimera.RData
load("eukaryote_amplicons/Cariaco_euk18S_upto_chimera.RData")

Summary of read counts through the pipeline

# set a little function
getN <- function(x) sum(getUniques(x))

# making a little table
summary_tab <- data.frame(row.names=samples, dada2_input=filtered_out[,1],
                          filtered=filtered_out[,2], dada_f=sapply(dada_forward, getN),
                          dada_r=sapply(dada_reverse, getN), merged=sapply(merged_amplicons, getN),
                          nonchim=rowSums(seqtab.nochim),
                          final_perc_reads_retained=round(rowSums(seqtab.nochim)/filtered_out[,1]*100, 1))

summary_tab

Retained abot 20-40% of input reads. Most were lost after quality filtering.

Assigning taxonomy using DECIPHER

Use the PR2 database (version 4.12.0) from here as input fasta for the AssignTaxonomy function. I put this in Data Store (instead of downloading here in the notebook because didn’t have github tools installed here). Syntax needs some small changes compared to using Silva databases. Dada2 developers describe those here.

taxa <- assignTaxonomy(seqtab.nochim, "eukaryote_amplicons/pr2_version_4.12.0_18S_dada2.fasta.gz", taxLevels = c("Kingdom","Supergroup","Division","Class","Order","Family","Genus","Species"), multithread=TRUE)

Check

rownames(taxa.print) <- NULL
head(taxa.print)
     Kingdom     Supergroup      Division         Class                 Order              
[1,] "Eukaryota" "Alveolata"     "Dinoflagellata" "Dinophyceae"         "Gymnodiniales"    
[2,] "Eukaryota" "Rhizaria"      "Cercozoa"       "Filosa-Thecofilosea" "Cryomonadida"     
[3,] "Eukaryota" "Alveolata"     "Dinoflagellata" "Syndiniales"         "Dino-Group-I"     
[4,] "Eukaryota" "Alveolata"     "Dinoflagellata" "Syndiniales"         "Dino-Group-II"    
[5,] "Eukaryota" "Alveolata"     "Dinoflagellata" "Syndiniales"         "Dino-Group-II"    
[6,] "Eukaryota" "Stramenopiles" "Ochrophyta"     "Bacillariophyta"     "Bacillariophyta_X"
     Family                       Genus                      Species                       
[1,] "Gymnodiniaceae"             "Gymnodinium"              "Gymnodinium_sp."             
[2,] "Cryomonadida_X"             "Cercozoa-01"              "Cercozoa-01_sp."             
[3,] "Dino-Group-I-Clade-1"       "Dino-Group-I-Clade-1_X"   "Dino-Group-I-Clade-1_X_sp."  
[4,] "Dino-Group-II_X"            "Dino-Group-II_XX"         "Dino-Group-II_XX_sp."        
[5,] "Dino-Group-II-Clade-14"     "Dino-Group-II-Clade-14_X" "Dino-Group-II-Clade-14_X_sp."
[6,] "Polar-centric-Mediophyceae" "Chaetoceros"              "Chaetoceros_socialis_debilis"

Extract the files

#giving our seq headers more manageable names (ASV_1, ASV_2...)
asv_seqs <- colnames(seqtab.nochim)
asv_headers <- vector(dim(seqtab.nochim)[2], mode="character")
for (i in 1:dim(seqtab.nochim)[2]) {
  asv_headers[i] <- paste(">ASV", i, sep="_")
}

# making and writing out a fasta of our final ASV seqs:
asv_fasta <- c(rbind(asv_headers, asv_seqs))
write(asv_fasta, "ASVs.fa")

# count table:
asv_tab <- t(seqtab.nochim)
row.names(asv_tab) <- sub(">", "", asv_headers)
write.table(asv_tab, "ASVs_counts.tsv", sep="\t", quote=F, col.names=NA)

# Taxonomy table
asv_tax <- taxa
row.names(asv_tax) <- sub(">", "", asv_headers)
write.table(asv_tax, "ASVs_taxonomy.tsv", sep="\t", quote=F, col.names=NA)

Backup and save to Data Store Save workspace up to this point.

save.image(file = "Cariaco_euk18S_final.RData")

And back up in data store just in case

iput Cariaco_euk18S_final.RData
iput -f Cariaco_Euk_PR2_DECIPHER_annotation.Rmd
iput ASVs.fa
iput ASVs_counts.tsv
iput ASVs_taxonomy.tsv
LS0tCnRpdGxlOiAiQW5ub3RhdGlvbiBvZiBDYXJpYWNvIEV1a2FyeW90aWMgMThTIEFtcGxpY29ucyB1c2luZyBBcHAgaW4gQ3l2ZXJzZSBEaXNjb3ZlcnkgRW52aXJvbm1lbnQiCmF1dGhvcjogIkxpeiBTdXRlciIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQo8YnI+CgojIFByZXBwaW5nIGFuZCBnZXR0aW5nIHRoZSBkYXRhIHJlYWR5CgpGaXJzdCBzdGVwcyBhcmUgdG8gdXNlIGBpaW5pdGAgaW4gdGVybWluYWwgdG8gY29ubmVjdCB0byBEYXRhIFN0b3JlIHNvIHlvdSBjYW4gYWNjZXNzIHRoZSBmYXN0cSBmaWxlcwpEaXJlY3Rpb25zIFtoZXJlXShodHRwczovL2xlYXJuaW5nLmN5dmVyc2Uub3JnL3Byb2plY3RzL2RhdGFfc3RvcmVfZ3VpZGUvZW4vbGF0ZXN0L3N0ZXAyLmh0bWwjaWNvbW1hbmRzLWZpcnN0LXRpbWUtY29uZmlndXJhdGlvbikKCkluIHN1bW1hcnksIHVzZSB0aGUgZm9sbG93aW5nIHdoZW4gcHJvbXB0ZWQ6ICAKSG9zdCBuYW1lIChETlMpIG9mIHRoZSBzZXJ2ZXIgdG8gY29ubmVjdCB0bzogZGF0YS5jeXZlcnNlLm9yZyAgClBvcnQgbnVtYmVyOiAxMjQ3ICAKaXJvZHMgdXNlciBuYW1lICAKaXJvZHMgem9uZTogaXBsYW50ICAKaVJPRFMgcGFzc3dvcmQgIAoKCgojIyMgSW5zdGFsbCBhbmQgbG9hZCBwYWNrYWdlcwoKTm8gbmVlZCB0byBkbyBhbnkgaW5zdGFsbGF0aW9ucyBpbiBWSUNFIGFwcC4gUGFja2FnZXMgc2hvdWxkIGNvbWUgcHJlLWluc3RhbGxlZC4gQnV0IG5lZWQgdG8gcnVuIGxpYnJhcmllczoKCmBgYHtyfQpsaWJyYXJ5KGRhZGEyKQpsaWJyYXJ5KERFQ0lQSEVSKQpgYGAKCgojIyMgR2V0IHNhbXBsZSBuYW1lcy0gcnVuIGluIFRlcm1pbmFsCmBgYGJhc2gKY2QgcmF3X2RhdGEKbHMgKl8xLmZhc3RxLmd6IHwgY3V0IC1mIDEgLWQgIl8iID4gLi4vc2FtcGxlcwpgYGAKCiMjIyBUYWtlIGEgbG9vayBhdCB0aGUgdW50cmltbWVkIHJlYWRzCmBgYHtyfQojIyBpbXBvcnQgc2FtcGxlIG5hbWVzIGFzIFIgdmFyaWFibGUKc2FtcGxlcyA8LSBzY2FuKCJzYW1wbGVzIiwgd2hhdD0iY2hhcmFjdGVyIikKCiMgbWFrZSB2YXJpYWJsZSBob2xkaW5nIHRoZSBmaWxlIG5hbWVzIG9mIGFsbCB0aGUgZm9yd2FyZCByZWFkIGZhc3RxIGZpbGVzLiBUaGVzZSBhcmUgaW4gYSBzdWJkaXJlY3RvcnksIHNvIEkgYW0gYWxzbyBhZGRpbmcgdGhlIG5hbWUgb2YgdGhlIHN1YiBkaXJlY3RvcnkgdG8gdGhlIGZpbGUgbmFtZQpmb3J3YXJkX3JlYWRzIDwtIHBhc3RlMCgicmF3X2RhdGEvIiwgc2FtcGxlcywgIl8xLmZhc3RxLmd6IikKIyBhbmQgb25lIHdpdGggdGhlIHJldmVyc2UKcmV2ZXJzZV9yZWFkcyA8LSBwYXN0ZTAoInJhd19kYXRhLyIsIHNhbXBsZXMsICJfMi5mYXN0cS5neiIpCgojIEFuZCBwbG90IHVzaW5nIGEgdG9vbCBmcm9tIGRhZGEyIChjaGVja2luZyBvbmx5IDUgc2FtcGxlcyBmb3IgcGxvdHRpbmcgcHVycG9zZXMpCnBsb3RRdWFsaXR5UHJvZmlsZShmb3J3YXJkX3JlYWRzWzE6NV0pCnBsb3RRdWFsaXR5UHJvZmlsZShyZXZlcnNlX3JlYWRzWzE6NV0pCmBgYAoKRnJvbSB0aGUgYWJvdmUgeW91IGNhbiBzZWUgdGhlIHJlYWRzIGFyZSB+MzAwYnAgYW5kIHRoZSBxdWFsaXR5IGlzIE9LLCB3aXRoIHRoZSBSIHJlYWRzIGJlaW5nIHBvb3JlciB0aGFuIHRoZSBGIHJlYWRzLiAKCiMjIyBSZW1vdmluZyBwcmltZXJzIHVzaW5nIGN1dGFkYXB0LSBydW4gaW4gVGVybWluYWwKU2VxdWVuY2luZyBmYWNpbGl0eSB0b2xkIG1lIHRoYXQgdGhlIHdheSB0aGF0IHRoZXkgc2VxdWVuY2UgaW4gYSB3YXkgdGhhdCBwcmltZXJzIGRvIG5vdCBnZXQgc2VxdWVuY2VkLiBTdGlsbCwgY2hlY2sgZm9yIGFuZCByZW1vdmUgcG9zc2libGUgcHJpbWVycwoKClByaW1lcnMgZnJvbSBbU3RvZWNrIGV0IGFsLiAyMDEwXShodHRwczovL2RvaS5vcmcvMTAuMTExMS9qLjEzNjUtMjk0WC4yMDA5LjA0NDgwLngpOgpUQVJldWs0NTRGV0QxOiAoNSctIENDQUdDQVNDWUdDR0dUQUFUVENDIC0zJykgIApUQVJldWtSRVYzICg1Jy0gQUNUVFRDR1RUQ1RUR0FUWVJBIC0zJykKCihBbHNvIGZvdW5kIGEgdXNlZnVsZSBbd2Vic2l0ZV0oaHR0cHM6Ly9naXRodWIuY29tL3ByMmRhdGFiYXNlL3ByMi1wcmltZXJzL3dpa2kvMThTLXJSTkEtcHJpbWVyLXNldHMpIHdpdGggZXVrIHByaW1lcnMgZnJvbSBQUjIgcGVvcGxlOiAKCkN1dCBGIHByaW1lciBhbmQgcmV2IGNvbXAgb2YgUiBwcmltZXIgZnJvbSBGIHJlYWRzIHdpdGggLWEgb3B0aW9uCkN1dCBSIHByaW1lciBhbmQgcmV2IGNvbXAgb2YgRiBwcmltZXIgZnJvbSBSIHJlYWRzIHdpdGggLUEgb3B0aW9uCgpTZXQgdGhlIG1pbiBzaXplLiBJIHRyaWVkIHNldHRpbmcgaXQgdG8gYmUgYWJvdXQgMTAlIHNtYWxsZXIgdGhhbiB0aGUgYW1wbGljb24gbGVuZ3RoICh+MjYwKSBidXQgYWZ0ZXIgcGxheWluZyBhcm91bmQgYW5kIGV2ZW4gZ29pbmcgZG93biB0byAyMDBicCwgaXQgd2FzIHRocm93aW5nIG91dCBtb3N0IHJlYWRzLiBBbHRlcm5hdGl2ZWx5LCBJIHNldCBOTyBtaW4gbGVuZ3RoIGJ1dCB0aGVuIGl0IHdhcyB3cml0aW5nIGVtcHR5IHJlYWRzIHRvIHRoZSBmYXN0MSBmaWxlLCB3aGljaCBsZWQgdG8gZXJyb3JzIGFmdGVyd2FyZC4gU28gZGVjaWRlZCBvbiBtaW4gbGVuZ3RoIG9mIDE1MCB0byBjb21wcm9taXNlLgoKYGBgYmFzaApjZCAuLgpta2RpciB0cmltbWVkX2Zhc3RxCmNkIHJhd19kYXRhCgojIFJ1biBpbiBsb29wCgpmb3Igc2FtcGxlIGluICQoY2F0IC4uL3NhbXBsZXMpCmRvCiAgICBlY2hvICJPbiBzYW1wbGU6ICRzYW1wbGUiCmN1dGFkYXB0IC1hIF5DQ0FHQ0FTQ1lHQ0dHVEFBVFRDQy4uLlRZUkFUQ0FBR0FBQ0dBQUFHVCBcCi1BIF5BQ1RUVENHVFRDVFRHQVRZUkEuLi5HR0FBVFRBQ0NHQ1JHU1RHQ1RHRyBcCi0tZGlzY2FyZC11bnRyaW1tZWQgXAotbSAxNTAgXAotbyAuLi90cmltbWVkX2Zhc3RxLyR7c2FtcGxlfV8xX3RyaW1tZWQuZmFzdHEuZ3ogLXAgLi4vdHJpbW1lZF9mYXN0cS8ke3NhbXBsZX1fMl90cmltbWVkLmZhc3RxLmd6IFwKJHtzYW1wbGV9XzEuZmFzdHEuZ3ogJHtzYW1wbGV9XzIuZmFzdHEuZ3ogXAo+PiAuLi90cmltbWVkX2Zhc3RxL2N1dGFkYXB0X3ByaW1lcl90cmltbWluZ19zdGF0cy50eHQgMj4mMQpkb25lCmBgYAoKCkNoZWNrIG91dHB1dCBzdGF0cwoKYGBgYmFzaApwYXN0ZSAuLi9zYW1wbGVzIDwoZ3JlcCAicGFzc2luZyIgLi4vdHJpbW1lZF9mYXN0cS9jdXRhZGFwdF9wcmltZXJfdHJpbW1pbmdfc3RhdHMudHh0IHwgY3V0IC1mMyAtZCAiKCIgfCB0ciAtZCAiKSIpIDwoZ3JlcCAiZmlsdGVyZWQiIC4uL3RyaW1tZWRfZmFzdHEvY3V0YWRhcHRfcHJpbWVyX3RyaW1taW5nX3N0YXRzLnR4dCB8IGN1dCAtZjMgLWQgIigiIHwgdHIgLWQgIikiKQpgYGAKClJldGFpbmVkIH4zMC05MCUgb2YgcmVhZHMgaW4gbW9zdCBjYXNlcyAKCiMgREFEQTItIGJhY2sgdG8gUiBjb25zb2xlCgojIyMgU2V0IG91ciB3b3JraW5nIGRpcmVjdG9yeSBhbmQgbGlzdCBvdXIgZmlsZXMKYGBge3J9CnNldHdkKCJ+LyIpCmxpc3QuZmlsZXMoKSAjIG1ha2Ugc3VyZSB3aGF0IHdlIHRoaW5rIGlzIGhlcmUgaXMgYWN0dWFsbHkgaGVyZQpgYGAKCk5vdyBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgdHJpbW1lZCByZWFkcy4KCmBgYHtyfQpmb3J3YXJkX3JlYWRzX3RyaW1tZWQgPC0gcGFzdGUwKCJ0cmltbWVkX2Zhc3RxLyIsIHNhbXBsZXMsICJfMV90cmltbWVkLmZhc3RxLmd6IikKcmV2ZXJzZV9yZWFkc190cmltbWVkIDwtIHBhc3RlMCgidHJpbW1lZF9mYXN0cS8iLCBzYW1wbGVzLCAiXzJfdHJpbW1lZC5mYXN0cS5neiIpCgojIEFuZCBwbG90IApwbG90UXVhbGl0eVByb2ZpbGUoZm9yd2FyZF9yZWFkc190cmltbWVkWzE6NV0pCnBsb3RRdWFsaXR5UHJvZmlsZShyZXZlcnNlX3JlYWRzX3RyaW1tZWRbMTo1XSkKYGBgCgpDb21wYXJpbmcgdGhlIGFib3ZlIHRvIHRoZSBwcmUtdHJpbW1lZCByZWFkcywgdGhleSBsb29rIHZlcnkgc2ltaWxhciBidXQgZ290IHJpZCBvZiBzaG9ydCBtZXNzaW5lc3MgaW4gYmVnaW5uaW5nIG9mIHJlYWRzIChwcmltZXIpLiBDYW4ndCBzZWUgbXVjaCBjaGFuZ2UgaW4gZW5kIG9mIHJlYWRzIGJ1dCBJIGtub3cgdGhleSB3ZXJlIHRyaW1tZWQgYmFzZWQgb24gb3V0cHV0IGZpbGUuIE92ZXJhbGwgcXVhbGl0eSBpcyB0b28gbG93IHRvIHRlbGwuCgojIyMgUXVhbGl0eSBGaWx0ZXJpbmcKTWFrZSBhIGRpcmVjdG9yeSBmb3IgZmlsdGVyZWQgcmVhZHMKYGBgYmFzaApjZCAuLgpta2RpciBmaWx0ZXJlZF9mYXN0cQpgYGAKCk1ha2UgdmFyaWFibGVzIGNvbnRhaW5pbmcgdGhlIGZpbGUgbmFtZXMgZm9yIHRoZSBuZXcgZmlsdGVyZWQgZm9yd2FyZCBhbmQgcmV2ZXJzZSByZWFkcyB0aGF0IHdlIHdpbGwgbWFrZQpgYGB7cn0KZmlsdGVyZWRfZm9yd2FyZF9yZWFkcyA8LSBwYXN0ZTAoImZpbHRlcmVkX2Zhc3RxLyIsc2FtcGxlcywgIl8xX2ZpbHRlcmVkLmZhc3RxLmd6IikKZmlsdGVyZWRfcmV2ZXJzZV9yZWFkcyA8LSBwYXN0ZTAoImZpbHRlcmVkX2Zhc3RxLyIsc2FtcGxlcywgIl8yX2ZpbHRlcmVkLmZhc3RxLmd6IikKYGBgCgoKQmFzZWQgb24gaG93IHRoZSBxdWFsaXR5IHBsb3RzIGxvb2ssIGRldGVybWluZSBob3cgbXVjaCB0byBjdXQgZnJvbSBlYWNoIHNpZGUuIFRyaW0gdGhlIEYgcmVhZHMgYXQgMjc1IFRyaW0gdGhlIFIgcmVhZHMgdG8gMjAwIEFsc28gSSB3YW50IHRvIHJ1biB0aGlzIHN0ZXAgdG8gdHJpbSBvdXQgdGhlIGxvdy1xdWFsaXR5IGluZGl2aWR1YWwgcmVhZHMgKHNldCBtYXhFRSB0byAxIGZvciBib3RoIEYgYW5kIFIgcmVhZHMpLiBUaGUgcm0ucGhpeCA9IFRSVUUgb3B0aW9uIHJlbW92ZXMgYW55IGxlZnRvdmVyICBQaGlYIGdlbm9taWMgRE5BICh0aGF0IGdldHMgYWRkZWQgYXMgYSBzdGFuZGFyZCBkdXJpbmcgc2VxdWVuY2luZykuIFBpY2sgYSBtaW4gbGVuZ3RoIH5zaG9ydGVyIHRoYW4gdGhlIG1pbiB0cmltbWVkIGxlbmd0aCAoaW4gdGhpcyBjYXNlIDE0MCBmb3IgUiByZWFkcykuIEkgYWxzbyBzZXQgdHJ1bmNRIHRvIHRydW5jYXRlIGFueSByZWFkIHRoYXQgaGFzIGEgcXVhbGl0eSBzY29yZSBsZXNzIHRoYW4gMi4gTXVsdGl0aHJlYWRpbmcgZm9yIHRoaXMgZnVuY3Rpb24gZG9lcyBub3Qgd29yayB3ZWxsIChldmVuIGFjY29yZGluZyB0byBkb2N1bWVudGF0aW9uKSBzbyBuZWVkZWQgdG8gc2tpcCB0aGF0LiBUYWtlcyBhIHdoaWxlIHRvIHJ1bgpgYGB7cn0KZmlsdGVyZWRfb3V0IDwtIGZpbHRlckFuZFRyaW0oZm9yd2FyZF9yZWFkc190cmltbWVkLCBmaWx0ZXJlZF9mb3J3YXJkX3JlYWRzLAogICAgICAgICAgICAgICAgcmV2ZXJzZV9yZWFkc190cmltbWVkLCBmaWx0ZXJlZF9yZXZlcnNlX3JlYWRzLCBtYXhFRT1jKDIsMiksCiAgICAgICAgICAgICAgICBybS5waGl4PVRSVUUsIG1pbkxlbj0xNDAsIHRydW5jTGVuPWMoMjc1LDIwMCksIHRydW5jUSA9IDIsIG1heE49MCkKYGBgCgpDaGVjayBvdXQgdGhlIHF1YWxpdHkgcHJvZmlsZXMgYWdhaW4uCmBgYHtyfQpmaWx0ZXJlZF9vdXQKCnBsb3RRdWFsaXR5UHJvZmlsZShmaWx0ZXJlZF9mb3J3YXJkX3JlYWRzWzE6NV0pCnBsb3RRdWFsaXR5UHJvZmlsZShmaWx0ZXJlZF9yZXZlcnNlX3JlYWRzWzE6NV0pCmBgYApMb29rIG11Y2ggYmV0dGVyCgpTYXZlIHdvcmtzcGFjZSB1cCB0byB0aGlzIHBvaW50LgpgYGB7cn0Kc2F2ZS5pbWFnZShmaWxlID0gIkNhcmlhY29fZXVrMThTX2RhZGEyX3VwdG9fZmlsdGVyZmFzdHEuUkRhdGEiKQpgYGAKCkFuZCBiYWNrIHVwIGluIGRhdGEgc3RvcmUganVzdCBpbiBjYXNlCmBgYGJhc2gKaXB1dCAtZiBDYXJpYWNvX2V1azE4U19kYWRhMl91cHRvX2ZpbHRlcmZhc3RxLlJEYXRhCmlwdXQgLWYgQ2FyaWFjb19FdWtfUFIyX0RFQ0lQSEVSX2Fubm90YXRpb24uUm1kCmlwdXQgLWYgc2FtcGxlcwppcHV0IC1yZiBmaWx0ZXJlZF9mYXN0cQppcHV0IC1yZiB0cmltbWVkX2Zhc3RxCmBgYAoKYGBge3J9CiMgbG9hZCgiQ2FyaWFjb19ldWsxOFNfZGFkYTJfdXB0b19maWx0ZXJmYXN0cS5SRGF0YSIpCmBgYAoKCiMjIyBFcnJvciBwcm9maWxpbmcgCk5leHQsIERBREEyIHRyaWVzIHRvIGxlYXJuIHRoZSBlcnJvciBzaWduYXR1cmUgb2Ygb3VyIGRhdGFzZXQuIFRoaXMgc3RlcCB0YWtlcyBhIHdoaWxlCmBgYHtyfQplcnJfZm9yd2FyZF9yZWFkcyA8LSBsZWFybkVycm9ycyhmaWx0ZXJlZF9mb3J3YXJkX3JlYWRzLCBtdWx0aXRocmVhZD1UUlVFKQplcnJfcmV2ZXJzZV9yZWFkcyA8LSBsZWFybkVycm9ycyhmaWx0ZXJlZF9yZXZlcnNlX3JlYWRzLCBtdWx0aXRocmVhZD1UUlVFKQpgYGAKCgoKUGxvdCB0aGUgZXJyb3IgcHJvZmlsZXMKYGBge3J9CnBsb3RFcnJvcnMoZXJyX2ZvcndhcmRfcmVhZHMsIG5vbWluYWxRPVRSVUUpCnBsb3RFcnJvcnMoZXJyX3JldmVyc2VfcmVhZHMsIG5vbWluYWxRPVRSVUUpCmBgYAoKVGhlIGNyZWF0b3JzIG9mIERBREEyIGRlc2NyaWJlIHRoaXMgW2hlcmVdKGh0dHBzOi8vYmVuampuZWIuZ2l0aHViLmlvL2RhZGEyL3R1dG9yaWFsLmh0bWwjbGVhcm4tdGhlLWVycm9yLXJhdGVzKS4gVGhlIHByb2ZpbGVzIGFyZSB0aGUgZXJyb3IgcmF0ZXMgZm9yIGVhY2ggcG9zc2libGUgdHJhbnNpdGlvbiBpbiB0aGUgcmVhZCAoQS0+QywgQS0+RywgZXRjKS4gR2VuZXJhbGx5IGluIHRoZSBhYm92ZSBwbG90cywgeW91IHdhbnQgdG8gc2VlIHRoYXQgdGhlIGJsYWNrIGRvdHMgKG9ic2VydmVkIGVycm9yIHJhdGVzIGZvciBlYWNoIHF1YWxpdHkgc2NvcmUpIG1hdGNoIHdlbGwgd2l0aCB0aGUgYmxhY2sgbGluZXMgKHRoZSBlc3RpbWF0ZWQgZXJyb3IgcmF0ZSkuIFRoZSByZWQgbGluZSBpcyB3aGF0IGlzIGV4cGVjdGVkIGJhc2VkIG9uIHRoZSBxdWFsaXR5IHNjb3JlLgoKQmFja3VwIGFnYWluIHNpbmNlIHRoaXMgc3RlcCBhYm92ZSB0YWtlcyBhd2hpbGUKU2F2ZSB3b3Jrc3BhY2UgdXAgdG8gdGhpcyBwb2ludC4KYGBge3J9CnNhdmUuaW1hZ2UoZmlsZSA9ICJDYXJpYWNvX2V1azE4U19kYWRhMl91cHRvX2Vycm9ycHJvZmlsZS5SRGF0YSIpCmBgYAoKQW5kIGJhY2sgdXAgaW4gZGF0YSBzdG9yZSBqdXN0IGluIGNhc2UKYGBgYmFzaAppcHV0IC1mIENhcmlhY29fZXVrMThTX2RhZGEyX3VwdG9fZXJyb3Jwcm9maWxlLlJEYXRhCmlwdXQgLWYgQ2FyaWFjb19FdWtfUFIyX0RFQ0lQSEVSX2Fubm90YXRpb24uUm1kCmBgYAoKCiMjIyBJbmZlcnJpbmcgQVNWcyAKVXNlIHRoZSBkYWRhIGNvbW1hbmQgdG8gaW5mZXIgQVNWcy4gV2UgYXJlIGdvaW5nIHRvIHVzZSB0aGUgcG9vbGluZyBvcHRpb24gInBzdWVkbyIgd2hpY2ggaXMgZGVzY3JpYmVkIFtoZXJlXShodHRwczovL2JlbmpqbmViLmdpdGh1Yi5pby9kYWRhMi9wc2V1ZG8uaHRtbCNQc2V1ZG8tcG9vbGluZykuIFRoaXMgc3RlcCBhbHNvIHRha2VzIGF3aGlsZQpgYGB7cn0KI3NldHdkKCIyMDIwMDcxMGJhY2t1cC8iKSAjIG9ubHkgdXNlZCB0aGlzIGJlY2F1c2UgaGFkIHRvIHJlc2V0dXAgYW5hbHlzaXMKZGFkYV9mb3J3YXJkIDwtIGRhZGEoZmlsdGVyZWRfZm9yd2FyZF9yZWFkcywgZXJyPWVycl9mb3J3YXJkX3JlYWRzLCBwb29sPSJwc2V1ZG8iLCBtdWx0aXRocmVhZD1UUlVFKSAKZGFkYV9yZXZlcnNlIDwtIGRhZGEoZmlsdGVyZWRfcmV2ZXJzZV9yZWFkcywgZXJyPWVycl9yZXZlcnNlX3JlYWRzLCBwb29sPSJwc2V1ZG8iLCBtdWx0aXRocmVhZD1UUlVFKQojIGFuZCBzYXZlIGluIHRoaXMgY29kZSBibG9jayBpbmNhc2UgSSBnZXQgbG9nZ2VkIG91dCBhZ2FpbiEKc2F2ZS5pbWFnZShmaWxlID0gIkNhcmlhY29fZXVrMThTX2RhZGEyX3VwdG9faW5mZXJhc3YuUkRhdGEiKQpgYGAKCgpBbmQgYmFjayB1cCBpbiBkYXRhIHN0b3JlIGp1c3QgaW4gY2FzZQpgYGB7YmFzaH0KaXB1dCBDYXJpYWNvX2V1azE4U19kYWRhMl91cHRvX2luZmVyYXN2LlJEYXRhCmlwdXQgLWYgQ2FyaWFjb19FdWtfUFIyX0RFQ0lQSEVSX2Fubm90YXRpb24uUm1kCmBgYAoKIyMjIE1lcmdlIGluZmVycmVkIHJlYWRzCkRhZGEyIHdpbGwgbWVyZ2UgcmVhZHMgd2hlcmV2ZXIgdGhlIG92ZXJsYXAgaXMgaWRlbnRpY2FsIGJldHdlZW4gdGhlIEYgYW5kIFIgcmVhZHMuIEkgdHJpbW1lZCB0aGUgRiByZWFkcyB0byAyNzUgYW5kIFIgcmVhZHMgdG8gMjAwIC4gVGhlIGZ1bGwgYW1wbGljb24gc2l6ZSAoYWZ0ZXIgcmVtb3ZpbmcgcHJpbWVycykgc2hvdWxkIGJlIDk2My01ODMgPSAzODAuIFNvIHRoZSBGIHJlYWQgc2hvdWxkIGJlIHNlcXVlbmNlZCBmcm9tIHBvc2l0aW9uIH41ODMgdG8gfjg1OCAoNTgzKzI3NSkgYW5kIHRoZSBSIHJlYWRzIHNob3VsZCBiZSBzZXF1ZW5jZWQgZnJvbSB+IHBvc2l0aW9uIDk2MyB0byA3NjMgKDk2My0yMDApLiBUaGlzIGxlYXZlcyBvdmVybGFwIGJldHdlZW4gcG9zaXRpb24gfjc2MyB0byA4NTggb3IgYWJvdXQgOTVicC4gU2V0IG1pbmltdW0gdG8gYWJvdXQgaGFsZiB0aGlzCgoKYGBge3J9CiNzZXR3ZCgiMjAyMDA3MTBiYWNrdXAvIikgIyBvbmx5IHVzZWQgdGhpcyBiZWNhdXNlIGhhZCB0byByZXNldHVwIGFuYWx5c2lzCm1lcmdlZF9hbXBsaWNvbnMgPC0gbWVyZ2VQYWlycyhkYWRhX2ZvcndhcmQsIGZpbHRlcmVkX2ZvcndhcmRfcmVhZHMsIGRhZGFfcmV2ZXJzZSwgZmlsdGVyZWRfcmV2ZXJzZV9yZWFkcywgdHJpbU92ZXJoYW5nPVRSVUUsIG1pbk92ZXJsYXA9NTAsIHZlcmJvc2UgPSBUUlVFKQpgYGAKCkJhY2sgdXAgYWdhaW4KYGBge3J9CnNhdmUuaW1hZ2UoZmlsZSA9ICJDYXJpYWNvX2V1azE4U191cHRvX21lcmdlLlJEYXRhIikKYGBgCgpgYGBiYXNoCmlwdXQgSFJFX3Nld2FnZV9taWNyb2Jpb21lX3VwdG9fbWVyZ2UuUkRhdGEKaXB1dCAtZiBIUkVfU2V3YWdlX21pY3JvYmlvbWVfREVDSVBIRVJfMjAyMDA3MTAuUm1kCmBgYAoKTG9hZCBmcm9tIERhdGEgU3RvcmUKYGBge3J9CmxvYWQoImV1a2FyeW90ZV9hbXBsaWNvbnMvQ2FyaWFjb19ldWsxOFNfdXB0b19tZXJnZS5SRGF0YSIpCmBgYAoKCgpgYGB7cn0KbmFtZXMobWVyZ2VkX2FtcGxpY29ucykKIyBJbml0aWFsbHkgdGhlc2UgbmFtZXMgaGF2ZSB0aGUgZnVsbCBuYW1lIHdpdGggYGZhc3RxLmd6YCBpbiB0aGUgbmFtZS4gQ2hhbmdlIHRvIGp1c3Qgc2FtcGxlIG5hbWUKbmFtZXMobWVyZ2VkX2FtcGxpY29ucykgPC0gc2FtcGxlcwoKIyBDaGVjayBzb21lIG90aGVyIHRoaW5ncwpsZW5ndGgobWVyZ2VkX2FtcGxpY29ucykgIyA0NyBlbGVtZW50cyBpbiB0aGlzIGxpc3QsIG9uZSBmb3IgZWFjaCBvZiBvdXIgc2FtcGxlcwpjbGFzcyhtZXJnZWRfYW1wbGljb25zJFNSUjM3MzUyNTYpICMgZWFjaCBlbGVtZW50IG9mIHRoZSBsaXN0IGlzIGEgZGF0YWZyYW1lIHRoYXQgY2FuIGJlIGFjY2Vzc2VkIGFuZCBtYW5pcHVsYXRlZCBsaWtlIGFueSBvcmRpbmFyeSBkYXRhZnJhbWUKbmFtZXMobWVyZ2VkX2FtcGxpY29ucyRTUlIzNzM1MjU2KSAjIHRoZSBuYW1lcygpIGZ1bmN0aW9uIG9uIGEgZGF0YWZyYW1lIGdpdmVzIHlvdSB0aGUgY29sdW1uIG5hbWVzCiMgInNlcXVlbmNlIiAgImFidW5kYW5jZSIgImZvcndhcmQiICAgInJldmVyc2UiICAgIm5tYXRjaCIgICAgIm5taXNtYXRjaCIgIm5pbmRlbCIgICAgInByZWZlciIgICAgImFjY2VwdCIKYGBgCgoKIyMjIENyZWF0aW5nIGEgc2VxdWVuY2UgdGFibGUKYGBge3J9CnNlcXRhYiA8LSBtYWtlU2VxdWVuY2VUYWJsZShtZXJnZWRfYW1wbGljb25zKQpjbGFzcyhzZXF0YWIpICMgbWF0cml4CmRpbShzZXF0YWIpICMgNDcgc2FtcGxlcywgNzgxODIgdW5pcXVlIEFTVnMKYGBgCgpCYWNrdXAgbm90ZWJvb2sKYGBge2Jhc2h9CmlwdXQgLWYgQ2FyaWFjb19FdWtfUFIyX0RFQ0lQSEVSX2Fubm90YXRpb25fMjAyMDA4MjcuUm1kCmBgYAoKCiMjIyBSZW1vdmluZyBjaGltZXJhcwpgYGB7cn0Kc2VxdGFiLm5vY2hpbSA8LSByZW1vdmVCaW1lcmFEZW5vdm8oc2VxdGFiLCB2ZXJib3NlPVRSVUUsIG11bHRpdGhyZWFkID0gVFJVRSkgCgojIElkZW50aWZpZWQgMzUxMSBiaW1lcmFzIG91dCBvZiAzMTQ3NSBpbnB1dCBzZXF1ZW5jZXMuCgojIHRob3VnaCB3ZSBhIGxvdCBvZiB1bmlxdWUgc2VxdWVuY2VzLCB3ZSBkb24ndCBrbm93IGlmIHRoZXkgaGVsZCBhIGxvdCBpbiB0ZXJtcyBvZiBhYnVuZGFuY2UsIHRoaXMgaXMgb25lIHF1aWNrIHdheSB0byBsb29rIGF0IHRoYXQKc3VtKHNlcXRhYi5ub2NoaW0pL3N1bShzZXF0YWIpIAojIDAuODA5MjQ5OAojIGxvc3QgYWJvdXQgMjAlIHdoaWNoIHdlcmUgY2hpbWVyYXMgKHNlZW1zIGhpZ2gpCmBgYAoKQmFja3VwIGFnYWluIHNpbmNlIHRoaXMgc3RlcCBhYm92ZSB0YWtlcyBhd2hpbGUKYGBge3J9CnNhdmUuaW1hZ2UoZmlsZSA9ICJDYXJpYWNvX2V1azE4U191cHRvX2NoaW1lcmEuUkRhdGEiKQpgYGAKCmBgYHtiYXNofQppcHV0IC1mIENhcmlhY29fRXVrX1BSMl9ERUNJUEhFUl9hbm5vdGF0aW9uXzIwMjAwODI3X2IuUm1kCmlwdXQgQ2FyaWFjb19ldWsxOFNfdXB0b19jaGltZXJhLlJEYXRhCmBgYAoKYGBge3J9CmxvYWQoImV1a2FyeW90ZV9hbXBsaWNvbnMvQ2FyaWFjb19ldWsxOFNfdXB0b19jaGltZXJhLlJEYXRhIikKYGBgCgoKIyBTdW1tYXJ5IG9mIHJlYWQgY291bnRzIHRocm91Z2ggdGhlIHBpcGVsaW5lCmBgYHtyfQojIHNldCBhIGxpdHRsZSBmdW5jdGlvbgpnZXROIDwtIGZ1bmN0aW9uKHgpIHN1bShnZXRVbmlxdWVzKHgpKQoKIyBtYWtpbmcgYSBsaXR0bGUgdGFibGUKc3VtbWFyeV90YWIgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9c2FtcGxlcywgZGFkYTJfaW5wdXQ9ZmlsdGVyZWRfb3V0WywxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJlZD1maWx0ZXJlZF9vdXRbLDJdLCBkYWRhX2Y9c2FwcGx5KGRhZGFfZm9yd2FyZCwgZ2V0TiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGFkYV9yPXNhcHBseShkYWRhX3JldmVyc2UsIGdldE4pLCBtZXJnZWQ9c2FwcGx5KG1lcmdlZF9hbXBsaWNvbnMsIGdldE4pLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5vbmNoaW09cm93U3VtcyhzZXF0YWIubm9jaGltKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBmaW5hbF9wZXJjX3JlYWRzX3JldGFpbmVkPXJvdW5kKHJvd1N1bXMoc2VxdGFiLm5vY2hpbSkvZmlsdGVyZWRfb3V0WywxXSoxMDAsIDEpKQoKc3VtbWFyeV90YWIKYGBgClJldGFpbmVkIGFib3QgMjAtNDAlIG9mIGlucHV0IHJlYWRzLiBNb3N0IHdlcmUgbG9zdCBhZnRlciBxdWFsaXR5IGZpbHRlcmluZy4KCiMjIyBBc3NpZ25pbmcgdGF4b25vbXkgdXNpbmcgREVDSVBIRVIKClVzZSB0aGUgUFIyIGRhdGFiYXNlICh2ZXJzaW9uIDQuMTIuMCkgZnJvbSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3ByMmRhdGFiYXNlL3ByMmRhdGFiYXNlL3JlbGVhc2VzKSBhcyBpbnB1dCBmYXN0YSBmb3IgdGhlIEFzc2lnblRheG9ub215IGZ1bmN0aW9uLiBJIHB1dCB0aGlzIGluIERhdGEgU3RvcmUgKGluc3RlYWQgb2YgZG93bmxvYWRpbmcgaGVyZSBpbiB0aGUgbm90ZWJvb2sgYmVjYXVzZSBkaWRuJ3QgaGF2ZSBnaXRodWIgdG9vbHMgaW5zdGFsbGVkIGhlcmUpLiBTeW50YXggbmVlZHMgc29tZSBzbWFsbCBjaGFuZ2VzIGNvbXBhcmVkIHRvIHVzaW5nIFNpbHZhIGRhdGFiYXNlcy4gRGFkYTIgZGV2ZWxvcGVycyBkZXNjcmliZSB0aG9zZSBbaGVyZV0oaHR0cHM6Ly9iZW5qam5lYi5naXRodWIuaW8vZGFkYTIvdHJhaW5pbmcuaHRtbCkuCgpgYGB7cn0KdGF4YSA8LSBhc3NpZ25UYXhvbm9teShzZXF0YWIubm9jaGltLCAiZXVrYXJ5b3RlX2FtcGxpY29ucy9wcjJfdmVyc2lvbl80LjEyLjBfMThTX2RhZGEyLmZhc3RhLmd6IiwgdGF4TGV2ZWxzID0gYygiS2luZ2RvbSIsIlN1cGVyZ3JvdXAiLCJEaXZpc2lvbiIsIkNsYXNzIiwiT3JkZXIiLCJGYW1pbHkiLCJHZW51cyIsIlNwZWNpZXMiKSwgbXVsdGl0aHJlYWQ9VFJVRSkKYGBgCgpDaGVjawpgYGB7cn0KdGF4YS5wcmludCA8LSB0YXhhICMgUmVtb3Zpbmcgc2VxdWVuY2Ugcm93bmFtZXMgZm9yIGRpc3BsYXkgb25seQpyb3duYW1lcyh0YXhhLnByaW50KSA8LSBOVUxMCmhlYWQodGF4YS5wcmludCkKYGBgCgoKIyMjIEV4dHJhY3QgdGhlIGZpbGVzIAoKYGBge3J9CiNnaXZpbmcgb3VyIHNlcSBoZWFkZXJzIG1vcmUgbWFuYWdlYWJsZSBuYW1lcyAoQVNWXzEsIEFTVl8yLi4uKQphc3Zfc2VxcyA8LSBjb2xuYW1lcyhzZXF0YWIubm9jaGltKQphc3ZfaGVhZGVycyA8LSB2ZWN0b3IoZGltKHNlcXRhYi5ub2NoaW0pWzJdLCBtb2RlPSJjaGFyYWN0ZXIiKQpmb3IgKGkgaW4gMTpkaW0oc2VxdGFiLm5vY2hpbSlbMl0pIHsKICBhc3ZfaGVhZGVyc1tpXSA8LSBwYXN0ZSgiPkFTViIsIGksIHNlcD0iXyIpCn0KCiMgbWFraW5nIGFuZCB3cml0aW5nIG91dCBhIGZhc3RhIG9mIG91ciBmaW5hbCBBU1Ygc2VxczoKYXN2X2Zhc3RhIDwtIGMocmJpbmQoYXN2X2hlYWRlcnMsIGFzdl9zZXFzKSkKd3JpdGUoYXN2X2Zhc3RhLCAiQVNWcy5mYSIpCgojIGNvdW50IHRhYmxlOgphc3ZfdGFiIDwtIHQoc2VxdGFiLm5vY2hpbSkKcm93Lm5hbWVzKGFzdl90YWIpIDwtIHN1YigiPiIsICIiLCBhc3ZfaGVhZGVycykKd3JpdGUudGFibGUoYXN2X3RhYiwgIkFTVnNfY291bnRzLnRzdiIsIHNlcD0iXHQiLCBxdW90ZT1GLCBjb2wubmFtZXM9TkEpCgojIFRheG9ub215IHRhYmxlCmFzdl90YXggPC0gdGF4YQpyb3cubmFtZXMoYXN2X3RheCkgPC0gc3ViKCI+IiwgIiIsIGFzdl9oZWFkZXJzKQp3cml0ZS50YWJsZShhc3ZfdGF4LCAiQVNWc190YXhvbm9teS50c3YiLCBzZXA9Ilx0IiwgcXVvdGU9RiwgY29sLm5hbWVzPU5BKQpgYGAKCkJhY2t1cCBhbmQgc2F2ZSB0byBEYXRhIFN0b3JlClNhdmUgd29ya3NwYWNlIHVwIHRvIHRoaXMgcG9pbnQuCmBgYHtyfQpzYXZlLmltYWdlKGZpbGUgPSAiQ2FyaWFjb19ldWsxOFNfZmluYWwuUkRhdGEiKQpgYGAKCkFuZCBiYWNrIHVwIGluIGRhdGEgc3RvcmUganVzdCBpbiBjYXNlCmBgYGJhc2gKaXB1dCBDYXJpYWNvX2V1azE4U19maW5hbC5SRGF0YQppcHV0IC1mIENhcmlhY29fRXVrX1BSMl9ERUNJUEhFUl9hbm5vdGF0aW9uLlJtZAppcHV0IEFTVnMuZmEKaXB1dCBBU1ZzX2NvdW50cy50c3YKaXB1dCBBU1ZzX3RheG9ub215LnRzdgpgYGAK