by Angus Ball

Hi This is the introductory tutorial on DADA2 using a single sample so you can do it on your computer before going and using the HPC This was stolen from: https://benjjneb.github.io/dada2/ITS_workflow.html

As a heads up this tutorial is exactly the same as the Miseq one EXCEPT when you start looking at error modeling

As always packages

library(dada2)
library(ShortRead)
library(Biostrings)

Start by importing your one sample there’ll be 4 files, 2 index files and 2 read files, one for the forward (I1, R1), and one for the reverse reads (I2, R2). That being said the indexes are only needed to demultiplex your files and since you’ve already done that we will just be filtering them out from our forward reads caragory (fnFs) and reverse reads category (fnRs)

path <- "C:\\Users\\angus\\OneDrive\\Documents\\Story\\lisatestdataset" #location of data
list.files(path)
 [1] "cutadapt"                                                      "filtN"                                                        
 [3] "MI.M03992_0831.001.FLD_ill_017_i7---IDT_i5_7.LW03_R1.fastq.gz" "MI.M03992_0831.001.FLD_ill_017_i7---IDT_i5_7.LW03_R2.fastq.gz"
 [5] "MI.M03992_0831.001.FLD_ill_022_i7---IDT_i5_7.LW67_R1.fastq.gz" "MI.M03992_0831.001.FLD_ill_022_i7---IDT_i5_7.LW67_R2.fastq.gz"
 [7] "plotqualitybeforefilter.rds"                                   "seqtab.nochim.rds"                                            
 [9] "UNITE.tgz"                                                     "UNITE_database.fastq"                                         
[11] "UNITE_database.fastq.gz"                                      
#organize files
fnFs <- sort(list.files(path, pattern = "R1.fastq.gz", full.names = TRUE)) #F orward
fnRs <- sort(list.files(path, pattern = "R2.fastq.gz", full.names = TRUE)) #R everse

Then we have to identify the primers used. In this study the ITS86F and ITS4 reverse primers were used so…

FWD <- "ACACTCTTTCCCTACACGACGCTCTTCCGATCTGTGAATCATCGAATCTTTGAA"
REV <- "GTGACTGGAGTTCAGACGTGTGCTCTTCCGATCTTCCTCCGCTTATTGATATGC"

Lets confirm we have the correct orientation of the primers and verify their presence This code makes reverse compliments of our dna

allOrients <- function(primer) {
    # Create all orientations of the input sequence
    require(Biostrings)
    dna <- DNAString(primer)  # The Biostrings works w/ DNAString objects rather than character vectors
    orients <- c(Forward = dna, Complement = Biostrings::complement(dna), Reverse = Biostrings::reverse(dna),
        RevComp = Biostrings::reverseComplement(dna))
    return(sapply(orients, toString))  # Convert back to character vector
}
FWD.orients <- allOrients(FWD)
REV.orients <- allOrients(REV)
FWD.orients
                                                 Forward                                               Complement 
"ACACTCTTTCCCTACACGACGCTCTTCCGATCTGTGAATCATCGAATCTTTGAA" "TGTGAGAAAGGGATGTGCTGCGAGAAGGCTAGACACTTAGTAGCTTAGAAACTT" 
                                                 Reverse                                                  RevComp 
"AAGTTTCTAAGCTACTAAGTGTCTAGCCTTCTCGCAGCACATCCCTTTCTCACA" "TTCAAAGATTCGATGATTCACAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT" 

Now we will prefilter the sequences to remove ambiguous bases (Ns)

fnFs.filtN <- file.path(path, "filtN", basename(fnFs)) # Put N-filtered files in filtN/ subdirectory
fnRs.filtN <- file.path(path, "filtN", basename(fnRs))
filterAndTrim(fnFs, fnFs.filtN, fnRs, fnRs.filtN, maxN = 0, multithread = FALSE)

Now this chunk of code goes through the sequences and counts the number of times the primers appear in the forward and reverse reads at all possible orientations.

primerHits <- function(primer, fn) {
    # Counts number of reads in which the primer is found
    nhits <- vcountPattern(primer, sread(readFastq(fn)), fixed = FALSE)
    return(sum(nhits > 0))
}
rbind(FWD.ForwardReads = sapply(FWD.orients, primerHits, fn = fnFs.filtN[[1]]), FWD.ReverseReads = sapply(FWD.orients,
    primerHits, fn = fnRs.filtN[[1]]), REV.ForwardReads = sapply(REV.orients, primerHits,
    fn = fnFs.filtN[[1]]), REV.ReverseReads = sapply(REV.orients, primerHits, fn = fnRs.filtN[[1]]))
                 Forward Complement Reverse RevComp
FWD.ForwardReads       0          0       0       0
FWD.ReverseReads       0          0       0       3
REV.ForwardReads       0          0       0       4
REV.ReverseReads       0          0       0       0

Sweet so the primers have already been removed from the sample at the start of each sequence, but since there is some bleed through of short its sequences measured in this study there are some hits of the reverse compliment in the respective strands.To cut the ends off the sequences from this bleed through we will be using a program called cut adapt. Cutadapt isnt an r package so its kinda a pain but alas. go here https://cutadapt.readthedocs.io/en/stable/installation.html#installation-on-windows to download it

cutadapt <- "C:\\Users\\angus\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\cutadapt" #location of cutadapt on you computer
system2(cutadapt, args = "--version") # Run shell commands from R
4.6

this cuts the sequences with the primers in them

path.cut <- file.path(path, "cutadapt")
if(!dir.exists(path.cut)) dir.create(path.cut)#this creates a cutadapt file
fnFs.cut <- file.path(path.cut, basename(fnFs))
fnRs.cut <- file.path(path.cut, basename(fnRs))

#Hey this whole thing breaks if you have alot of spaces in your path names so uhhh don't do that

FWD.RC <- dada2:::rc(FWD)
REV.RC <- dada2:::rc(REV)
# Trim FWD and the reverse-complement of REV off of R1 (forward reads)
R1.flags <- paste("-g", FWD, "-a", REV.RC) 
# Trim REV and the reverse-complement of FWD off of R2 (reverse reads)
R2.flags <- paste("-G", REV, "-A", FWD.RC) 
# Run Cutadapt
for(i in seq_along(fnFs)) {
  system2(cutadapt, args = c(R1.flags, R2.flags, "-n", 2, # -n 2 required to remove FWD and REV from reads
                             "-o", fnFs.cut[i], "-p", fnRs.cut[i], # output files
                             fnFs.filtN[i], fnRs.filtN[i])) # input files
}
This is cutadapt 4.6 with Python 3.12.1
Command line parameters: -g ACACTCTTTCCCTACACGACGCTCTTCCGATCTGTGAATCATCGAATCTTTGAA -a GCATATCAATAAGCGGAGGAAGATCGGAAGAGCACACGTCTGAACTCCAGTCAC -G GTGACTGGAGTTCAGACGTGTGCTCTTCCGATCTTCCTCCGCTTATTGATATGC -A TTCAAAGATTCGATGATTCACAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT -n 2 -o C:\Users\angus\OneDrive\Documents\Story\lisatestdataset/cutadapt/MI.M03992_0831.001.FLD_ill_017_i7---IDT_i5_7.LW03_R1.fastq.gz -p C:\Users\angus\OneDrive\Documents\Story\lisatestdataset/cutadapt/MI.M03992_0831.001.FLD_ill_017_i7---IDT_i5_7.LW03_R2.fastq.gz C:\Users\angus\OneDrive\Documents\Story\lisatestdataset/filtN/MI.M03992_0831.001.FLD_ill_017_i7---IDT_i5_7.LW03_R1.fastq.gz C:\Users\angus\OneDrive\Documents\Story\lisatestdataset/filtN/MI.M03992_0831.001.FLD_ill_017_i7---IDT_i5_7.LW03_R2.fastq.gz
Processing paired-end reads on 1 core ...
Finished in 7.008 s (89.923 µs/read; 0.67 M reads/minute).

=== Summary ===

Total read pairs processed:             77,936
  Read 1 with adapter:                  77,544 (99.5%)
  Read 2 with adapter:                  77,750 (99.8%)
Pairs written (passing filters):        77,936 (100.0%)

Total basepairs processed:    38,967,374 bp
  Read 1:    19,484,000 bp
  Read 2:    19,483,374 bp
Total written (filtered):     35,782,993 bp (91.8%)
  Read 1:    17,854,923 bp
  Read 2:    17,928,070 bp

=== First read: Adapter 1 ===

Sequence: ACACTCTTTCCCTACACGACGCTCTTCCGATCTGTGAATCATCGAATCTTTGAA; Type: regular 5'; Length: 54; Trimmed: 77540 times

Minimum overlap: 3
No. of allowed errors:
1-9 bp: 0; 10-19 bp: 1; 20-29 bp: 2; 30-39 bp: 3; 40-49 bp: 4; 50-54 bp: 5

Overview of removed sequences
length  count   expect  max.err error counts
5   1   76.1    0   1
17  1   0.0 1   0 1
18  1   0.0 1   0 0 1
19  38  0.0 1   6 5 27
20  733 0.0 2   102 607 24
21  76685   0.0 2   75705 939 41
22  79  0.0 2   2 75 2
23  1   0.0 2   0 1
24  1   0.0 2   0 0 1


=== First read: Adapter 2 ===

Sequence: GCATATCAATAAGCGGAGGAAGATCGGAAGAGCACACGTCTGAACTCCAGTCAC; Type: regular 3'; Length: 54; Trimmed: 78 times

Minimum overlap: 3
No. of allowed errors:
1-9 bp: 0; 10-19 bp: 1; 20-29 bp: 2; 30-39 bp: 3; 40-49 bp: 4; 50-54 bp: 5

Bases preceding removed adapters:
  A: 44.9%
  C: 17.9%
  G: 9.0%
  T: 28.2%
  none/other: 0.0%

Overview of removed sequences
length  count   expect  max.err error counts
3   38  1217.8  0   38
4   6   304.4   0   6
5   6   76.1    0   6
6   1   19.0    0   1
7   1   4.8 0   1
10  1   0.1 1   0 1
11  4   0.0 1   4
13  2   0.0 1   2
15  2   0.0 1   2
17  1   0.0 1   0 1
19  5   0.0 1   4 1
20  1   0.0 2   0 0 1
22  1   0.0 2   1
30  1   0.0 3   0 0 0 1
52  1   0.0 5   1
55  1   0.0 5   1
95  3   0.0 5   0 0 0 2 0 1
206 3   0.0 5   3


=== Second read: Adapter 3 ===

Sequence: GTGACTGGAGTTCAGACGTGTGCTCTTCCGATCTTCCTCCGCTTATTGATATGC; Type: regular 5'; Length: 54; Trimmed: 77746 times

Minimum overlap: 3
No. of allowed errors:
1-9 bp: 0; 10-19 bp: 1; 20-29 bp: 2; 30-39 bp: 3; 40-49 bp: 4; 50-54 bp: 5

Overview of removed sequences
length  count   expect  max.err error counts
3   1   1217.8  0   1
14  2   0.0 1   1 1
16  7   0.0 1   5 2
17  4   0.0 1   2 2
18  27  0.0 1   4 9 14
19  482 0.0 1   45 402 35
20  77161   0.0 2   75962 1104 95
21  62  0.0 2   5 53 4


=== Second read: Adapter 4 ===

Sequence: TTCAAAGATTCGATGATTCACAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT; Type: regular 3'; Length: 54; Trimmed: 47 times

Minimum overlap: 3
No. of allowed errors:
1-9 bp: 0; 10-19 bp: 1; 20-29 bp: 2; 30-39 bp: 3; 40-49 bp: 4; 50-54 bp: 5

Bases preceding removed adapters:
  A: 6.4%
  C: 10.6%
  G: 61.7%
  T: 21.3%
  none/other: 0.0%

Overview of removed sequences
length  count   expect  max.err error counts
3   21  1217.8  0   21
4   1   304.4   0   1
6   5   19.0    0   5
8   1   1.2 0   1
12  3   0.0 1   2 1
14  2   0.0 1   2
16  2   0.0 1   1 1
20  4   0.0 2   3 1
23  1   0.0 2   1
53  1   0.0 5   1
56  1   0.0 5   0 1
96  4   0.0 5   3 0 0 0 1
130 1   0.0 5   0 1
This is cutadapt 4.6 with Python 3.12.1
Command line parameters: -g ACACTCTTTCCCTACACGACGCTCTTCCGATCTGTGAATCATCGAATCTTTGAA -a GCATATCAATAAGCGGAGGAAGATCGGAAGAGCACACGTCTGAACTCCAGTCAC -G GTGACTGGAGTTCAGACGTGTGCTCTTCCGATCTTCCTCCGCTTATTGATATGC -A TTCAAAGATTCGATGATTCACAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT -n 2 -o C:\Users\angus\OneDrive\Documents\Story\lisatestdataset/cutadapt/MI.M03992_0831.001.FLD_ill_022_i7---IDT_i5_7.LW67_R1.fastq.gz -p C:\Users\angus\OneDrive\Documents\Story\lisatestdataset/cutadapt/MI.M03992_0831.001.FLD_ill_022_i7---IDT_i5_7.LW67_R2.fastq.gz C:\Users\angus\OneDrive\Documents\Story\lisatestdataset/filtN/MI.M03992_0831.001.FLD_ill_022_i7---IDT_i5_7.LW67_R1.fastq.gz C:\Users\angus\OneDrive\Documents\Story\lisatestdataset/filtN/MI.M03992_0831.001.FLD_ill_022_i7---IDT_i5_7.LW67_R2.fastq.gz
Processing paired-end reads on 1 core ...
Finished in 6.826 s (85.348 µs/read; 0.70 M reads/minute).

=== Summary ===

Total read pairs processed:             79,983
  Read 1 with adapter:                  79,594 (99.5%)
  Read 2 with adapter:                  79,789 (99.8%)
Pairs written (passing filters):        79,983 (100.0%)

Total basepairs processed:    39,990,603 bp
  Read 1:    19,995,750 bp
  Read 2:    19,994,853 bp
Total written (filtered):     36,719,697 bp (91.8%)
  Read 1:    18,321,996 bp
  Read 2:    18,397,701 bp

=== First read: Adapter 1 ===

Sequence: ACACTCTTTCCCTACACGACGCTCTTCCGATCTGTGAATCATCGAATCTTTGAA; Type: regular 5'; Length: 54; Trimmed: 79586 times

Minimum overlap: 3
No. of allowed errors:
1-9 bp: 0; 10-19 bp: 1; 20-29 bp: 2; 30-39 bp: 3; 40-49 bp: 4; 50-54 bp: 5

Overview of removed sequences
length  count   expect  max.err error counts
5   2   78.1    0   2
17  1   0.0 1   0 1
18  3   0.0 1   1 2
19  25  0.0 1   8 5 12
20  770 0.0 2   134 614 22
21  78693   0.0 2   77637 1002 54
22  92  0.0 2   1 86 5


=== First read: Adapter 2 ===

Sequence: GCATATCAATAAGCGGAGGAAGATCGGAAGAGCACACGTCTGAACTCCAGTCAC; Type: regular 3'; Length: 54; Trimmed: 136 times

Minimum overlap: 3
No. of allowed errors:
1-9 bp: 0; 10-19 bp: 1; 20-29 bp: 2; 30-39 bp: 3; 40-49 bp: 4; 50-54 bp: 5

Bases preceding removed adapters:
  A: 88.2%
  C: 3.7%
  G: 5.9%
  T: 2.2%
  none/other: 0.0%
WARNING:
    The adapter is preceded by 'A' extremely often.
    The provided adapter sequence could be incomplete at its 5' end.
    Ignore this warning when trimming primers.

Overview of removed sequences
length  count   expect  max.err error counts
3   12  1249.7  0   12
4   9   312.4   0   9
5   3   78.1    0   3
7   12  4.9 0   12
8   15  1.2 0   15
9   1   0.3 0   1
10  2   0.1 1   2
11  1   0.0 1   1
13  16  0.0 1   12 4
14  26  0.0 1   26
15  5   0.0 1   5
16  11  0.0 1   11
20  3   0.0 2   3
29  2   0.0 2   2
34  4   0.0 3   4
38  1   0.0 3   1
54  1   0.0 5   1
55  5   0.0 5   4 1
197 1   0.0 5   1
206 5   0.0 5   3 1 1
219 1   0.0 5   1


=== Second read: Adapter 3 ===

Sequence: GTGACTGGAGTTCAGACGTGTGCTCTTCCGATCTTCCTCCGCTTATTGATATGC; Type: regular 5'; Length: 54; Trimmed: 79788 times

Minimum overlap: 3
No. of allowed errors:
1-9 bp: 0; 10-19 bp: 1; 20-29 bp: 2; 30-39 bp: 3; 40-49 bp: 4; 50-54 bp: 5

Overview of removed sequences
length  count   expect  max.err error counts
3   4   1249.7  0   4
15  1   0.0 1   0 1
16  1   0.0 1   1
17  5   0.0 1   3 2
18  26  0.0 1   2 6 18
19  433 0.0 1   58 352 23
20  79257   0.0 2   77967 1131 159
21  58  0.0 2   6 49 3
23  1   0.0 2   0 0 1
24  1   0.0 2   0 1
64  1   0.0 5   0 0 0 0 1


=== Second read: Adapter 4 ===

Sequence: TTCAAAGATTCGATGATTCACAGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT; Type: regular 3'; Length: 54; Trimmed: 112 times

Minimum overlap: 3
No. of allowed errors:
1-9 bp: 0; 10-19 bp: 1; 20-29 bp: 2; 30-39 bp: 3; 40-49 bp: 4; 50-54 bp: 5

Bases preceding removed adapters:
  A: 4.5%
  C: 2.7%
  G: 88.4%
  T: 4.5%
  none/other: 0.0%
WARNING:
    The adapter is preceded by 'G' extremely often.
    The provided adapter sequence could be incomplete at its 5' end.
    Ignore this warning when trimming primers.

Overview of removed sequences
length  count   expect  max.err error counts
3   21  1249.7  0   21
8   8   1.2 0   8
9   14  0.3 0   14
10  2   0.1 1   2
11  1   0.0 1   1
12  1   0.0 1   1
14  12  0.0 1   6 6
15  23  0.0 1   21 2
16  4   0.0 1   4
17  9   0.0 1   9
21  3   0.0 2   3
30  2   0.0 3   2
35  4   0.0 3   4
39  1   0.0 3   1
55  1   0.0 5   1
56  5   0.0 5   3 0 1 1
197 1   0.0 5   0 0 0 0 1


WARNING:
    One or more of your adapter sequences may be incomplete.
    Please see the detailed output above.

so two primers had warning messages saying that there was a weird amount of one specific base after the primer. Option 1: you’re a fool! and didnt include the full length of your primer, so go add the bases you missed and reassess. Option 2 (this is us): Only the compliments of the primers threw warning messages which means that this probably isnt an artifact of your primers and more an artifact of natural diversity putting an A or G next.(if you’d missed a nucleotide on your primer, you’d see error messages on both the reverse and normal compliments i believe)

Lets double check if it worked!

rbind(FWD.ForwardReads = sapply(FWD.orients, primerHits, fn = fnFs.cut[[1]]), FWD.ReverseReads = sapply(FWD.orients,
    primerHits, fn = fnRs.cut[[1]]), REV.ForwardReads = sapply(REV.orients, primerHits,
    fn = fnFs.cut[[1]]), REV.ReverseReads = sapply(REV.orients, primerHits, fn = fnRs.cut[[1]]))
                 Forward Complement Reverse RevComp
FWD.ForwardReads       0          0       0       0
FWD.ReverseReads       0          0       0       0
REV.ForwardReads       0          0       0       0
REV.ReverseReads       0          0       0       0

Hey it worked!! there are no more primer sequences of the reverse compliments within our sequences.

This is some spicy ways to extract the names so we can use them (and name them to something not stupid)

# Forward and reverse fastq filenames have the format:
cutFs <- sort(list.files(path.cut, pattern = "R1.fastq.gz", full.names = TRUE))
cutRs <- sort(list.files(path.cut, pattern = "R2.fastq.gz", full.names = TRUE))

You’re samples will have different naming schemes, therefore you’ll have to change this code to fit your data. To rename these files we use regex expressions which are hard and complicated :(.

here is an unadultered sample name: MI.M03992_0831.001.FLD_ill_028_i7—IDT_i5_8.LW01_R1.fastq.gz

We want our name to be Lw43 only so…

the first function…

get.sample.name <- function(fname) strsplit(basename(fname), "_")[[1]][7] #"_"<-split value, [7]<-save this one
sample.names <- unname(sapply(cutFs, get.sample.name))

can be read as create an object with the function split the string (the name) at every _ and keep the 7th one i.e. you run the command and

MI.M03992_0831.001.FLD_ill_028_i7—IDT_i5_8.LW03_R1.fastq.gz

becomes

“MI.M03992” “0831.001.FLD” “ill” “028” “i7—IDT” “i5” “8.LW03” “R1.fastq.gz”

And since only the 7th is kept it becomes

“8.LW03”

Then we repeat just to save the LW43 part of the name, because thats what we care about

get.sample.name.2 <- function(fname) strsplit(basename(fname), "[.]")[[1]][2]
sample.names <- unname(sapply(sample.names, get.sample.name.2))

This takes

“8.LW03”

splits it by the period

Note: the period is special in regex expressions so I have to enclose it in square brackets (the square brackets are a special form of an escape character, this sentence won’t make sense unless you code in other languages tho :/ )

“8” “LW03”

Then we save the 2nd instance and the sample name becomes

“LW03”

Huzzah! actual names that are good

head(sample.names)
[1] NA     "LW03" "LW67"

(You can ignore the NA, its a byproduct of my creating these tutorials haphazardly)

Now we want to inspect the read quality profiles first the forward read

Then the reverse

SO how do you read these? the green line is the median quality score, the greyscale is a heatmap of the frequency of each quality score, the quartiles of the average quality are shown with the orange lines, and the read line at the bottom (with a seperate scale) shows the proportion of reads a that extend to at least that length. What is quality score? check out https://www.illumina.com/science/technology/next-generation-sequencing/plan-experiments/quality-scores.html and https://drive5.com/usearch/manual/exp_errs.html but briefly, Quality score = 10 means a wrong base is reported 10% of the time. Q20 is 1% and Q30 is 0.1% of the time. So anything under q20 should be removed. This is in theory correct; however it is better to delete samples based on the number of expected errors rather than error chance. (this still uses quality score information) https://academic.oup.com/bioinformatics/article/31/21/3476/194979?login=false

This sets some path directories that will be created within the filterandtrim command below

filtFs <- file.path(path.cut, "filtered", basename(cutFs))
filtRs <- file.path(path.cut, "filtered", basename(cutRs))

And now we can enter dada2 fully!!

out <- filterAndTrim(
  cutFs, #location of cut forward reads
  filtFs, #location of where filtered forward reads will go
  cutRs, #location of cut reverse reads
  filtRs, #location of where filtered reverse reads will go
  maxN = 0, #max number of ambiguous bases allowed in a read (dada2 REQUIRES 0 ambiguous bases)
  maxEE = c(2, 2), #maxEE is the command that uses expected error rates to determine if a sample should be deleted (rather than using Q scores absolutely), 2 errors is the recommended value. Why is it a list? one for the forward reads one for the reverse
  truncQ = 2, #truncates reads when any base pair quality score is 2 or below, why 2? illumina sequencing reports a 2 on really bad reads
  minLen = 50, #minimum length of read, note for ITS region there is no max length
  rm.phix = TRUE, #removes sequences of the bacteriophage PhiX which commonly contaminates NGS data
  compress = TRUE, #compresses your files when its done
  multithread = FALSE)  #multithreading cant be done on a windows machine
head(out)
                                                              reads.in reads.out
LW01_R1.fastq.gz                                                 61606     38621
MI.M03992_0831.001.FLD_ill_017_i7---IDT_i5_7.LW03_R1.fastq.gz    77936     42442
MI.M03992_0831.001.FLD_ill_022_i7---IDT_i5_7.LW67_R1.fastq.gz    79983     56495

if you want to learn more about truncQ read this: https://drive5.com/usearch/manual/exp_errs.html

OUCH! That removed two thirds of all the reads!!!!

lets take a look at the reads again

Less samples but they are certainly of a much higher quality!! in the dada2 tutorial only about 80% of the reads survived this step, Honestly this sounds like it sucks but looking at other peoples samples it’s not too bad in the grand scheme of things, we still have 38 thousands reads in this samples!!. Feel free to ask me (angus) about your specific loss rates when you finish the pipeline if thinks look bleak, there are some small things that we can do to up the number (or maybe some hidden error is eating your reads and who knows)!

oKAY! We are out of ITS specific commands now, everything from here on out (excluding what database you compare to) is the same for 16S and ITS sequencing. This also means we might get a bunch of warnings messages saying our reads are different lengths, thats okay we know it! DADA2 tries to learn the error rates of different nucleotide transitions (dont ask me how) so…

HEY, because you read my ENTIRE preamble on the dada2 git page you know Miseq and nextseq data needs to learn errors a bit differently. This is that step! Lets say you didn’t know this and ran your data like normal this’d what your quality plots would look like

#lets say you didn't know this and ran your data like normal this'd what your quality plots would look like
plot(ploterrF_PreLOESS)

plot(ploterrR_PreLOESS)

Brother ewww. Look at C2G or T2A (eg A2C is an adenine -> Cytosine transition)! what the hell is that squiggly ass line!?! These trend lines don’t match up with the datapoints or the red lines at all!plus them falling into levels is what I mean when i say plots fall into stepwise patterns (see documentation on the git).

lets run error modeling the correct way

This code is from https://github.com/benjjneb/dada2/issues/1307, it forces the trend lines to have monotonicity (i.e. they can only tren din one direction and not just up and down), and alters the loess function to make it run closer to the data points. Whats loess?? its kinda like linear regression trying to find a trend line between your data points but more complicated.

Just so we’re on the same page this isn’t changing the data, just the trend line through the data, so as long as you look and it better fits the data then its okay to use. There are some other options (i.e. only enforcing monotonicity or only fixing loess) that we can try but based on my research this seems like the best general solution

loessErrfun_mod4 <- function(trans) {
  qq <- as.numeric(colnames(trans))
  est <- matrix(0, nrow=0, ncol=length(qq))
  for(nti in c("A","C","G","T")) {
    for(ntj in c("A","C","G","T")) {
      if(nti != ntj) {
        errs <- trans[paste0(nti,"2",ntj),]
        tot <- colSums(trans[paste0(nti,"2",c("A","C","G","T")),])
        rlogp <- log10((errs+1)/tot)  # 1 psuedocount for each err, but if tot=0 will give NA
        rlogp[is.infinite(rlogp)] <- NA
        df <- data.frame(q=qq, errs=errs, tot=tot, rlogp=rlogp)
        
        # original
        # ###! mod.lo <- loess(rlogp ~ q, df, weights=errs) ###!
        # mod.lo <- loess(rlogp ~ q, df, weights=tot) ###!
        # #        mod.lo <- loess(rlogp ~ q, df)
        
        # jonalim's solution
        # https://github.com/benjjneb/dada2/issues/938
        mod.lo <- loess(rlogp ~ q, df, weights = log10(tot),degree = 1, span = 0.95)
        
        pred <- predict(mod.lo, qq)
        maxrli <- max(which(!is.na(pred)))
        minrli <- min(which(!is.na(pred)))
        pred[seq_along(pred)>maxrli] <- pred[[maxrli]]
        pred[seq_along(pred)<minrli] <- pred[[minrli]]
        est <- rbind(est, 10^pred)
      } # if(nti != ntj)
    } # for(ntj in c("A","C","G","T"))
  } # for(nti in c("A","C","G","T"))
  
  # HACKY
  MAX_ERROR_RATE <- 0.25
  MIN_ERROR_RATE <- 1e-7
  est[est>MAX_ERROR_RATE] <- MAX_ERROR_RATE
  est[est<MIN_ERROR_RATE] <- MIN_ERROR_RATE
  
  # enforce monotonicity
  # https://github.com/benjjneb/dada2/issues/791
  estorig <- est
  est <- est %>%
    data.frame() %>%
    mutate_all(funs(case_when(. < X40 ~ X40,
                              . >= X40 ~ .))) %>% as.matrix()
  rownames(est) <- rownames(estorig)
  colnames(est) <- colnames(estorig)
  
  # Expand the err matrix with the self-transition probs
  err <- rbind(1-colSums(est[1:3,]), est[1:3,],
               est[4,], 1-colSums(est[4:6,]), est[5:6,],
               est[7:8,], 1-colSums(est[7:9,]), est[9,],
               est[10:12,], 1-colSums(est[10:12,]))
  rownames(err) <- paste0(rep(c("A","C","G","T"), each=4), "2", c("A","C","G","T"))
  colnames(err) <- colnames(trans)
  # Return
  return(err)
}

This above code just makes an error model. Now we can actually run our error modeling approach

#note to run the error matrix with this new model, the three samples i've given the program aren't enough, you can only really run this with a full data set
errF <- learnErrors(
  filtFs,
  multithread = FALSE, #remember multitread = TRUE on big computers
  nbases = 1e9,
  errorEstimationFunction = loessErrfun_mod4,
  verbose = TRUE
)

errR <- learnErrors(
  filtRs,
  multithread = FALSE,
  nbases = 1e9,
  errorEstimationFunction = loessErrfun_mod4,
  verbose = TRUE
)

Then lets take a look…

plot(ploterrR_postloess)

Looking at these two graphs, they look pretty good, the actual values (grey dots) match up with the expected (red lines) pretty well for the different nucleotide transitions especially compared to the earlier ones. we see that A2G and T2C transitions are still kinda straight (especially compared to the red line), this is due to the nextseq itself and (the global) we don’t really know why yet. dada2 is still developing solutions to this issue (and these solutions have been in development since 2019…)

Now its time to run dada2 to find our unqiue sequences

dadaFs <- dada(filtFs, err=errF, multithread=FALSE)
#Sample 1 - 38621 reads in 12833 unique sequences.

(I’m lazy and wont rerun the whole thing just so the outputs are here on the bottom in the comments)

dadaRs <- dada(filtRs, err=errR, multithread=FALSE)
#Sample 1 - 38621 reads in 11222 unique sequences.

Sweet loads of unique sequences lets merge the forward and reverse reads

mergers <- mergePairs(dadaFs, filtFs, dadaRs, filtRs, verbose=TRUE)
#27156 paired-reads (in 168 unique pairings) successfully merged out of 37747 (in 524 pairings) input.


# Inspect the merger data.frame from the first sample
head(mergers[[1]])

#[1] "CGCACCTTGCGCTCTTTGGTATTCCGAAGAGCATGCCTGTTTGAGTGTCATGAAAATATCAACCTTGACTTGGGTTTAGTGCTCTTGTCTTGGCTTGGATTTGGCTGTTTGCCGCTCGAAAGAGTCGGCTCAGCTTAAAAGTATTAGCTGGATCTGTCTTTGAGACTTGGTTTGACTTGGCGTAATAAGTTATTTCGCTGAGGACAATCTTCGGATTGGCCGAGTTTCTGGGACGTTTGTCCGCTTTCTAATACAAGTTCTAGCTTGCTAGACATGACTTTTTTATTATCTGGCCTCAAATCAGGTAGGACTACCCGCTGAACTTAA"                     
#[2] "CGCACATTGCGCCCCTTGGTATTCCATGGGGCATGCCTGTTCGAGCGTCATTTGTACCTTCAAGCTTTGCTTGGTGTTGGGTGTTTGTCTCGCCTTGCGTGTAGACTCGCCTTAAAATAATTGGCAGCCGGCGTATTGATTTCGGAGCGCAGTACATCTCGCACTCTGTCCTCACAACGACGACGTCCAAAAGTACTTTTTACACTCTTGACCTCGGATCAGGTAGGGATACCCGCTGAACTTAA"                                                                                                       
#[3] "CGCACCTTGCGCCCTTTGGTATTCCGAAGGGCATGCCTGTTTGAGTGTCATGAAACCTCACCCCACTTGGGTTTTTGCCTGAGCGGTGGTGTATTGGGTGTTGCCTTGCTAAAGGCTCGCCTTAAAGACATAAGCACCTTGGATGTAATACGTTTCATCCTTCTGGGTGGCTGATAACCCCACATATTCATGATCTGGCCTCAAATCAGGTAGGGCTACCCGCTGAACTTAA"                                                                                                                    
#[4] "CGCACATTGCACTCTCTGGTATTCCGGAGAGTATGCTTGTTTGAGTATCTGTAAACATCTCAAATCTCTCGACTTTCGAGTTTGAGAGAATTGGACTTGAGCAATCCCAACTCCTTCGTCAGAAAGAGGAGGGTTGCTTGAAATGCAGGTGCAGCTGGGCATTCTTCTGAGCTAAAAGCATATTTATTTAGTCCCGTCAAACGGATTATTATTCTTGCTTCAGCTTACATAAAGGTTGAATGACTACCGAGGCTGATTGCAGGTAAACTCGTTCCGTTAAAAAGAACTTGTGAACTCGATCTCAAATCAAGTAAGACTACCCGCTGAACTTAA"               
#[5] "CGCACATTGCGCTCTCTAGTATTCTGGAGAGCATGCTTGTTTGAGTATCAGTAAACACCTCAATCCCCTAAATCTTGAAAAAGAAAGGGAATTGGACTTGAGCTATCCCAACAAACGTAAAACTTTGGCGGGTCGCTTGAAATGCAGGTGCAGCTGGAACTACTCTTCTGAGCTATAAGCATATCTATTTAGTCCCGTCAAACGGATTATTATTTTTTGCTGCAGCTAACATAAAGGAAAAGTAGATCCGCTATGCTGACTGATGCAGGAATTAGCAGTGAACGTCTCTTTGAGGCGCTTACTGTGAAACTCGATCTCAAATCAAGTAAGACTACCCGCTGAACTTAA"
#[6] "CGCACCTTGCGCCCTTTGGTATTCCGAAGGGCATGCCTGTTTGAGTGTCATGAAACCTCACCCCACTTGGGTTTTTACCTGAGCGGTGGTGTATTGGGTGTTGCCTTGCTAAAGGCTCGCCTTAAAGATATAAGCACCTTGGATGTAATACGTTTCATCCTTCTGGGTGGCTAATAACCCCACATATTCATGATCTGGCCTCAAATCAGGTAGGGCTACCCGCTGAACTTAA"   

sweet only lost 10’000 reads from that! Now with full sequences we can construct sequence tables and determine our amplicon sequence variants (ASV’s)

seqtab <- makeSequenceTable(mergers)
dim(seqtab)
#[1]   1 168

That means 168 unqiue ASV’s!

# Inspect distribution of sequence lengths
table(nchar(getSequences(seqtab)))

#196 211 214 215 216 222 229 232 233 234 236 237 238 239 240 241 242 243 244 245 246 247 248 249 252 254 256 258 259 262 265 266 270 272 273 
#  1   1   1   2   1   1   1   8   1   3   5   5  12   4   2   3   1   1   1   3   3   4   3   1   1   1   1   1   1   1   1   1   1   2   1 
#275 280 283 284 286 287 292 298 303 304 305 306 311 312 313 316 324 325 326 327 328 329 330 331 332 333 334 335 336 338 339 341 343 345 347 
#  1   2   1   1   1   1   1   1   2   1   1   2   2   1   1   1   1   2   2  11   9   5   1   3   1   6   3   3   3   2   1   1   1   2   2 
#348 349 356 370 373 
#  2   2   2   1   2 

We have so much variation in the amount of sequence lengths because its the ITS sequence we are using, but always check that the sequence length makes sense as weird sequence lengths can result from non-specific merging. The ITS2 region is between 160-390 bps so this all checks out https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7162488/#:~:text=In%20spite%20of%20its%20short,across%20all%20land%20plants%20(e.

But life is never perfectly easy, we must remove chimeras (i.e. sequences that have been merged but dont reflect biologically species). Chimeras are checked for by looking at ASVs with low abundance that can be made of combinations of highly abundant ASV’s. Why and how this happens is well explained here https://forum.qiime2.org/t/dada2-merging-plates-and-chimera-removal/21030/4

seqtab.nochim <- removeBimeraDenovo(seqtab, method="consensus", multithread=TRUE, verbose=TRUE)
#Identified 41 bimeras out of 168 input sequences.

dim(seqtab.nochim)
#[1]   1 127

41 chimeric sequences! This is 24.45% of the total amount of ASV’s!!

sum(seqtab.nochim)/sum(seqtab)
#[1] 0.9531595

However, removing these samples only resulted in a 4% loss of reads in total! These are normal numbers, and anything under 25% is no need for a second thought. Chimeras can be more or less of a sample based on the specific experimental procedures

Now lets do a whole look through all the reads throughout the pipeline

getN <- function(x) sum(getUniques(x))
track <- cbind(out, getN(dadaFs), getN(dadaRs), getN(mergers), rowSums(seqtab.nochim))
colnames(track) <- c("input", "filtered", "denoisedF", "denoisedR", "merged", "nonchim")
rownames(track) <- sample.names
head(track)



#     input filtered denoisedF denoisedR merged nonchim
#LW01 61606    38621     38058     38045  27156   25884

PS. I modified the above code to work on a single sample. you’ll have to change it for multiple samples, see the its big data tutorial

Then lets assign taxonomy! first we need to download the ITS database from UNITE https://unite.ut.ee/repository.php

you’ll want to download one of the general fasta releases. The differences between these files isn’t well explained. There seems to be two fungi and two eukaryote databases to download

I have the beans on the HPC to do what I want, so I’ll be using the slightly larger database, and perhaps thats how you should make your decision aswell, but if you have strong opinions tell me.

unite.ref <- "C:\\Users\\angus\\OneDrive\\Documents\\Story\\lisatestdataset\\UNITE_database.fastq.gz"  # CHANGE ME to location on your machine
taxa <- assignTaxonomy(seqtab.nochim, #Sequnces
                       unite.ref, #databse
                       multithread = FALSE, 
                       tryRC = TRUE) #try reverse compliment of sequences

My baby computer doesnt have the stuffs to process even one sample! But alas, we can see what it theoretically would look like with this

taxa.print <- taxa  # Removing sequence rownames for display only
rownames(taxa.print) <- NULL
head(taxa.print)

Then you can take your data and save it! Your seqtab.nochim, and taxa are the important files. seqtab.nochim is your otu_table, and taxa is your tax_table to save…

saveRDS(taxa, file = "C:\\Users\\angus\\OneDrive\\Documents\\Story\\lisatestdataset\\taxa.rds" )#add your file location here
saveRDS(seqtab.nochim, file = "C:\\Users\\angus\\OneDrive\\Documents\\Story\\lisatestdataset\\seqtabnochim.rds" )#add your file location here

Then to make a phyloseq object later on…

library(phyloseq)
ps <- phyloseq(otu_table(seqtab.nochim, taxa_are_rows = FALSE), tax_table(taxa))
saveRDS(ps, file = "C:\\Users\\angus\\OneDrive\\Documents\\Story\\lisatestdataset\\ps.rds" )#add your file location here
LS0tDQp0aXRsZTogIkRBREEyIHdpdGggTmV4dHNlcSBkYXRhIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCmJ5IEFuZ3VzIEJhbGwNCg0KSGkgVGhpcyBpcyB0aGUgaW50cm9kdWN0b3J5IHR1dG9yaWFsIG9uIERBREEyIHVzaW5nIGEgc2luZ2xlIHNhbXBsZSBzbyB5b3UgY2FuIGRvIGl0IG9uIHlvdXIgY29tcHV0ZXIgYmVmb3JlIGdvaW5nIGFuZCB1c2luZyB0aGUgSFBDDQpUaGlzIHdhcyBzdG9sZW4gZnJvbTogaHR0cHM6Ly9iZW5qam5lYi5naXRodWIuaW8vZGFkYTIvSVRTX3dvcmtmbG93Lmh0bWwNCg0KQXMgYSBoZWFkcyB1cCB0aGlzIHR1dG9yaWFsIGlzIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlIE1pc2VxIG9uZSBFWENFUFQgd2hlbiB5b3Ugc3RhcnQgbG9va2luZyBhdCBlcnJvciBtb2RlbGluZw0KDQoNCkFzIGFsd2F5cyBwYWNrYWdlcw0KYGBge3IsIGVjaG8gPSBULCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShkYWRhMikNCmxpYnJhcnkoU2hvcnRSZWFkKQ0KbGlicmFyeShCaW9zdHJpbmdzKQ0KYGBgDQoNClN0YXJ0IGJ5IGltcG9ydGluZyB5b3VyIG9uZSBzYW1wbGUNCnRoZXJlJ2xsIGJlIDQgZmlsZXMsIDIgaW5kZXggZmlsZXMgYW5kIDIgcmVhZCBmaWxlcywgb25lIGZvciB0aGUgZm9yd2FyZCAoSTEsIFIxKSwgYW5kIG9uZSBmb3IgdGhlIHJldmVyc2UgcmVhZHMgKEkyLCBSMikuIFRoYXQgYmVpbmcgc2FpZCB0aGUgaW5kZXhlcyBhcmUgb25seSBuZWVkZWQgdG8gZGVtdWx0aXBsZXggeW91ciBmaWxlcyBhbmQgc2luY2UgeW91J3ZlIGFscmVhZHkgZG9uZSB0aGF0IHdlIHdpbGwganVzdCBiZSBmaWx0ZXJpbmcgdGhlbSBvdXQgZnJvbSBvdXIgZm9yd2FyZCByZWFkcyBjYXJhZ29yeSAoZm5GcykgYW5kIHJldmVyc2UgcmVhZHMgY2F0ZWdvcnkgKGZuUnMpDQpgYGB7cn0NCnBhdGggPC0gIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlXFxEb2N1bWVudHNcXFN0b3J5XFxsaXNhdGVzdGRhdGFzZXQiICNsb2NhdGlvbiBvZiBkYXRhDQpsaXN0LmZpbGVzKHBhdGgpDQoNCiNvcmdhbml6ZSBmaWxlcw0KZm5GcyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aCwgcGF0dGVybiA9ICJSMS5mYXN0cS5neiIsIGZ1bGwubmFtZXMgPSBUUlVFKSkgI0Ygb3J3YXJkDQpmblJzIDwtIHNvcnQobGlzdC5maWxlcyhwYXRoLCBwYXR0ZXJuID0gIlIyLmZhc3RxLmd6IiwgZnVsbC5uYW1lcyA9IFRSVUUpKSAjUiBldmVyc2UNCmBgYA0KDQpUaGVuIHdlIGhhdmUgdG8gaWRlbnRpZnkgdGhlIHByaW1lcnMgdXNlZC4gSW4gdGhpcyBzdHVkeSB0aGUgSVRTODZGIGFuZCBJVFM0IHJldmVyc2UgcHJpbWVycyB3ZXJlIHVzZWQgc28uLi4NCmBgYHtyfQ0KRldEIDwtICJBQ0FDVENUVFRDQ0NUQUNBQ0dBQ0dDVENUVENDR0FUQ1RHVEdBQVRDQVRDR0FBVENUVFRHQUEiDQpSRVYgPC0gIkdUR0FDVEdHQUdUVENBR0FDR1RHVEdDVENUVENDR0FUQ1RUQ0NUQ0NHQ1RUQVRUR0FUQVRHQyINCmBgYA0KDQpMZXRzIGNvbmZpcm0gd2UgaGF2ZSB0aGUgY29ycmVjdCBvcmllbnRhdGlvbiBvZiB0aGUgcHJpbWVycyBhbmQgdmVyaWZ5IHRoZWlyIHByZXNlbmNlDQpUaGlzIGNvZGUgbWFrZXMgcmV2ZXJzZSBjb21wbGltZW50cyBvZiBvdXIgZG5hDQpgYGB7cn0NCmFsbE9yaWVudHMgPC0gZnVuY3Rpb24ocHJpbWVyKSB7DQogICAgIyBDcmVhdGUgYWxsIG9yaWVudGF0aW9ucyBvZiB0aGUgaW5wdXQgc2VxdWVuY2UNCiAgICByZXF1aXJlKEJpb3N0cmluZ3MpDQogICAgZG5hIDwtIEROQVN0cmluZyhwcmltZXIpICAjIFRoZSBCaW9zdHJpbmdzIHdvcmtzIHcvIEROQVN0cmluZyBvYmplY3RzIHJhdGhlciB0aGFuIGNoYXJhY3RlciB2ZWN0b3JzDQogICAgb3JpZW50cyA8LSBjKEZvcndhcmQgPSBkbmEsIENvbXBsZW1lbnQgPSBCaW9zdHJpbmdzOjpjb21wbGVtZW50KGRuYSksIFJldmVyc2UgPSBCaW9zdHJpbmdzOjpyZXZlcnNlKGRuYSksDQogICAgICAgIFJldkNvbXAgPSBCaW9zdHJpbmdzOjpyZXZlcnNlQ29tcGxlbWVudChkbmEpKQ0KICAgIHJldHVybihzYXBwbHkob3JpZW50cywgdG9TdHJpbmcpKSAgIyBDb252ZXJ0IGJhY2sgdG8gY2hhcmFjdGVyIHZlY3Rvcg0KfQ0KRldELm9yaWVudHMgPC0gYWxsT3JpZW50cyhGV0QpDQpSRVYub3JpZW50cyA8LSBhbGxPcmllbnRzKFJFVikNCkZXRC5vcmllbnRzDQpgYGANCk5vdyB3ZSB3aWxsIHByZWZpbHRlciB0aGUgc2VxdWVuY2VzIHRvIHJlbW92ZSBhbWJpZ3VvdXMgYmFzZXMgKE5zKQ0KYGBge3J9DQpmbkZzLmZpbHROIDwtIGZpbGUucGF0aChwYXRoLCAiZmlsdE4iLCBiYXNlbmFtZShmbkZzKSkgIyBQdXQgTi1maWx0ZXJlZCBmaWxlcyBpbiBmaWx0Ti8gc3ViZGlyZWN0b3J5DQpmblJzLmZpbHROIDwtIGZpbGUucGF0aChwYXRoLCAiZmlsdE4iLCBiYXNlbmFtZShmblJzKSkNCmZpbHRlckFuZFRyaW0oZm5GcywgZm5Gcy5maWx0TiwgZm5ScywgZm5Scy5maWx0TiwgbWF4TiA9IDAsIG11bHRpdGhyZWFkID0gRkFMU0UpDQpgYGANCg0KTm93IHRoaXMgY2h1bmsgb2YgY29kZSBnb2VzIHRocm91Z2ggdGhlIHNlcXVlbmNlcyBhbmQgY291bnRzIHRoZSBudW1iZXIgb2YgdGltZXMgdGhlIHByaW1lcnMgYXBwZWFyIGluIHRoZSBmb3J3YXJkIGFuZCByZXZlcnNlIHJlYWRzIGF0IGFsbCBwb3NzaWJsZSBvcmllbnRhdGlvbnMuDQpgYGB7cn0NCnByaW1lckhpdHMgPC0gZnVuY3Rpb24ocHJpbWVyLCBmbikgew0KICAgICMgQ291bnRzIG51bWJlciBvZiByZWFkcyBpbiB3aGljaCB0aGUgcHJpbWVyIGlzIGZvdW5kDQogICAgbmhpdHMgPC0gdmNvdW50UGF0dGVybihwcmltZXIsIHNyZWFkKHJlYWRGYXN0cShmbikpLCBmaXhlZCA9IEZBTFNFKQ0KICAgIHJldHVybihzdW0obmhpdHMgPiAwKSkNCn0NCnJiaW5kKEZXRC5Gb3J3YXJkUmVhZHMgPSBzYXBwbHkoRldELm9yaWVudHMsIHByaW1lckhpdHMsIGZuID0gZm5Gcy5maWx0TltbMV1dKSwgRldELlJldmVyc2VSZWFkcyA9IHNhcHBseShGV0Qub3JpZW50cywNCiAgICBwcmltZXJIaXRzLCBmbiA9IGZuUnMuZmlsdE5bWzFdXSksIFJFVi5Gb3J3YXJkUmVhZHMgPSBzYXBwbHkoUkVWLm9yaWVudHMsIHByaW1lckhpdHMsDQogICAgZm4gPSBmbkZzLmZpbHROW1sxXV0pLCBSRVYuUmV2ZXJzZVJlYWRzID0gc2FwcGx5KFJFVi5vcmllbnRzLCBwcmltZXJIaXRzLCBmbiA9IGZuUnMuZmlsdE5bWzFdXSkpDQpgYGANCg0KU3dlZXQgc28gdGhlIHByaW1lcnMgaGF2ZSBhbHJlYWR5IGJlZW4gcmVtb3ZlZCBmcm9tIHRoZSBzYW1wbGUgYXQgdGhlIHN0YXJ0IG9mIGVhY2ggc2VxdWVuY2UsIGJ1dCBzaW5jZSB0aGVyZSBpcyBzb21lIGJsZWVkIHRocm91Z2ggb2Ygc2hvcnQgaXRzIHNlcXVlbmNlcyBtZWFzdXJlZCBpbiB0aGlzIHN0dWR5IHRoZXJlIGFyZSBzb21lIGhpdHMgb2YgdGhlIHJldmVyc2UgY29tcGxpbWVudCBpbiB0aGUgcmVzcGVjdGl2ZSBzdHJhbmRzLlRvIGN1dCB0aGUgZW5kcyBvZmYgdGhlIHNlcXVlbmNlcyBmcm9tIHRoaXMgYmxlZWQgdGhyb3VnaCB3ZSB3aWxsIGJlIHVzaW5nIGEgcHJvZ3JhbSBjYWxsZWQgY3V0IGFkYXB0LiBDdXRhZGFwdCBpc250IGFuIHIgcGFja2FnZSBzbyBpdHMga2luZGEgYSBwYWluIGJ1dCBhbGFzLiBnbyBoZXJlIGh0dHBzOi8vY3V0YWRhcHQucmVhZHRoZWRvY3MuaW8vZW4vc3RhYmxlL2luc3RhbGxhdGlvbi5odG1sI2luc3RhbGxhdGlvbi1vbi13aW5kb3dzIHRvIGRvd25sb2FkIGl0DQoNCmBgYHtyfQ0KY3V0YWRhcHQgPC0gIkM6XFxVc2Vyc1xcYW5ndXNcXEFwcERhdGFcXExvY2FsXFxQcm9ncmFtc1xcUHl0aG9uXFxQeXRob24zMTJcXFNjcmlwdHNcXGN1dGFkYXB0IiAjbG9jYXRpb24gb2YgY3V0YWRhcHQgb24geW91IGNvbXB1dGVyDQpzeXN0ZW0yKGN1dGFkYXB0LCBhcmdzID0gIi0tdmVyc2lvbiIpICMgUnVuIHNoZWxsIGNvbW1hbmRzIGZyb20gUg0KYGBgDQp0aGlzIGN1dHMgdGhlIHNlcXVlbmNlcyB3aXRoIHRoZSBwcmltZXJzIGluIHRoZW0NCmBgYHtyLCBlY2hvID0gVCwgbWVzc2FnZT1GQUxTRX0NCnBhdGguY3V0IDwtIGZpbGUucGF0aChwYXRoLCAiY3V0YWRhcHQiKQ0KaWYoIWRpci5leGlzdHMocGF0aC5jdXQpKSBkaXIuY3JlYXRlKHBhdGguY3V0KSN0aGlzIGNyZWF0ZXMgYSBjdXRhZGFwdCBmaWxlDQpmbkZzLmN1dCA8LSBmaWxlLnBhdGgocGF0aC5jdXQsIGJhc2VuYW1lKGZuRnMpKQ0KZm5Scy5jdXQgPC0gZmlsZS5wYXRoKHBhdGguY3V0LCBiYXNlbmFtZShmblJzKSkNCg0KI0hleSB0aGlzIHdob2xlIHRoaW5nIGJyZWFrcyBpZiB5b3UgaGF2ZSBhbG90IG9mIHNwYWNlcyBpbiB5b3VyIHBhdGggbmFtZXMgc28gdWhoaCBkb24ndCBkbyB0aGF0DQoNCkZXRC5SQyA8LSBkYWRhMjo6OnJjKEZXRCkNClJFVi5SQyA8LSBkYWRhMjo6OnJjKFJFVikNCiMgVHJpbSBGV0QgYW5kIHRoZSByZXZlcnNlLWNvbXBsZW1lbnQgb2YgUkVWIG9mZiBvZiBSMSAoZm9yd2FyZCByZWFkcykNClIxLmZsYWdzIDwtIHBhc3RlKCItZyIsIEZXRCwgIi1hIiwgUkVWLlJDKSANCiMgVHJpbSBSRVYgYW5kIHRoZSByZXZlcnNlLWNvbXBsZW1lbnQgb2YgRldEIG9mZiBvZiBSMiAocmV2ZXJzZSByZWFkcykNClIyLmZsYWdzIDwtIHBhc3RlKCItRyIsIFJFViwgIi1BIiwgRldELlJDKSANCiMgUnVuIEN1dGFkYXB0DQpmb3IoaSBpbiBzZXFfYWxvbmcoZm5GcykpIHsNCiAgc3lzdGVtMihjdXRhZGFwdCwgYXJncyA9IGMoUjEuZmxhZ3MsIFIyLmZsYWdzLCAiLW4iLCAyLCAjIC1uIDIgcmVxdWlyZWQgdG8gcmVtb3ZlIEZXRCBhbmQgUkVWIGZyb20gcmVhZHMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi1vIiwgZm5Gcy5jdXRbaV0sICItcCIsIGZuUnMuY3V0W2ldLCAjIG91dHB1dCBmaWxlcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmbkZzLmZpbHROW2ldLCBmblJzLmZpbHROW2ldKSkgIyBpbnB1dCBmaWxlcw0KfQ0KYGBgDQpzbyB0d28gcHJpbWVycyBoYWQgd2FybmluZyBtZXNzYWdlcyBzYXlpbmcgdGhhdCB0aGVyZSB3YXMgYSB3ZWlyZCBhbW91bnQgb2Ygb25lIHNwZWNpZmljIGJhc2UgYWZ0ZXIgdGhlIHByaW1lci4gT3B0aW9uIDE6IHlvdSdyZSBhIGZvb2whIGFuZCBkaWRudCBpbmNsdWRlIHRoZSBmdWxsIGxlbmd0aCBvZiB5b3VyIHByaW1lciwgc28gZ28gYWRkIHRoZSBiYXNlcyB5b3UgbWlzc2VkIGFuZCByZWFzc2Vzcy4gT3B0aW9uIDIgKHRoaXMgaXMgdXMpOiBPbmx5IHRoZSBjb21wbGltZW50cyBvZiB0aGUgcHJpbWVycyB0aHJldyB3YXJuaW5nIG1lc3NhZ2VzIHdoaWNoIG1lYW5zIHRoYXQgdGhpcyBwcm9iYWJseSBpc250IGFuIGFydGlmYWN0IG9mIHlvdXIgcHJpbWVycyBhbmQgbW9yZSBhbiBhcnRpZmFjdCBvZiBuYXR1cmFsIGRpdmVyc2l0eSBwdXR0aW5nIGFuIEEgb3IgRyBuZXh0LihpZiB5b3UnZCBtaXNzZWQgYSBudWNsZW90aWRlIG9uIHlvdXIgcHJpbWVyLCB5b3UnZCBzZWUgZXJyb3IgbWVzc2FnZXMgb24gYm90aCB0aGUgcmV2ZXJzZSBhbmQgbm9ybWFsIGNvbXBsaW1lbnRzIGkgYmVsaWV2ZSkNCg0KDQoNCkxldHMgZG91YmxlIGNoZWNrIGlmIGl0IHdvcmtlZCENCmBgYHtyfQ0KcmJpbmQoRldELkZvcndhcmRSZWFkcyA9IHNhcHBseShGV0Qub3JpZW50cywgcHJpbWVySGl0cywgZm4gPSBmbkZzLmN1dFtbMV1dKSwgRldELlJldmVyc2VSZWFkcyA9IHNhcHBseShGV0Qub3JpZW50cywNCiAgICBwcmltZXJIaXRzLCBmbiA9IGZuUnMuY3V0W1sxXV0pLCBSRVYuRm9yd2FyZFJlYWRzID0gc2FwcGx5KFJFVi5vcmllbnRzLCBwcmltZXJIaXRzLA0KICAgIGZuID0gZm5Gcy5jdXRbWzFdXSksIFJFVi5SZXZlcnNlUmVhZHMgPSBzYXBwbHkoUkVWLm9yaWVudHMsIHByaW1lckhpdHMsIGZuID0gZm5Scy5jdXRbWzFdXSkpDQpgYGANCkhleSBpdCB3b3JrZWQhISB0aGVyZSBhcmUgbm8gbW9yZSBwcmltZXIgc2VxdWVuY2VzIG9mIHRoZSByZXZlcnNlIGNvbXBsaW1lbnRzIHdpdGhpbiBvdXIgc2VxdWVuY2VzLg0KDQoNClRoaXMgaXMgc29tZSBzcGljeSB3YXlzIHRvIGV4dHJhY3QgdGhlIG5hbWVzIHNvIHdlIGNhbiB1c2UgdGhlbSAoYW5kIG5hbWUgdGhlbSB0byBzb21ldGhpbmcgbm90IHN0dXBpZCkNCmBgYHtyfQ0KIyBGb3J3YXJkIGFuZCByZXZlcnNlIGZhc3RxIGZpbGVuYW1lcyBoYXZlIHRoZSBmb3JtYXQ6DQpjdXRGcyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aC5jdXQsIHBhdHRlcm4gPSAiUjEuZmFzdHEuZ3oiLCBmdWxsLm5hbWVzID0gVFJVRSkpDQpjdXRScyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aC5jdXQsIHBhdHRlcm4gPSAiUjIuZmFzdHEuZ3oiLCBmdWxsLm5hbWVzID0gVFJVRSkpDQoNCmBgYA0KDQpZb3UncmUgc2FtcGxlcyB3aWxsIGhhdmUgZGlmZmVyZW50IG5hbWluZyBzY2hlbWVzLCB0aGVyZWZvcmUgeW91J2xsIGhhdmUgdG8gY2hhbmdlIHRoaXMgY29kZSB0byBmaXQgeW91ciBkYXRhLiBUbyByZW5hbWUgdGhlc2UgZmlsZXMgd2UgdXNlIHJlZ2V4IGV4cHJlc3Npb25zIHdoaWNoIGFyZSBoYXJkIGFuZCBjb21wbGljYXRlZCA6KC4NCg0KaGVyZSBpcyBhbiB1bmFkdWx0ZXJlZCBzYW1wbGUgbmFtZTogTUkuTTAzOTkyXzA4MzEuMDAxLkZMRF9pbGxfMDI4X2k3LS0tSURUX2k1XzguTFcwMV9SMS5mYXN0cS5neg0KDQpXZSB3YW50IG91ciBuYW1lIHRvIGJlIEx3NDMgb25seSBzby4uLg0KDQp0aGUgZmlyc3QgZnVuY3Rpb24uLi4NCg0KYGBge3J9DQpnZXQuc2FtcGxlLm5hbWUgPC0gZnVuY3Rpb24oZm5hbWUpIHN0cnNwbGl0KGJhc2VuYW1lKGZuYW1lKSwgIl8iKVtbMV1dWzddICMiXyI8LXNwbGl0IHZhbHVlLCBbN108LXNhdmUgdGhpcyBvbmUNCnNhbXBsZS5uYW1lcyA8LSB1bm5hbWUoc2FwcGx5KGN1dEZzLCBnZXQuc2FtcGxlLm5hbWUpKQ0KYGBgDQoNCmNhbiBiZSByZWFkIGFzIGNyZWF0ZSBhbiBvYmplY3Qgd2l0aCB0aGUgZnVuY3Rpb24gc3BsaXQgdGhlIHN0cmluZyAodGhlIG5hbWUpIGF0IGV2ZXJ5IF8gYW5kIGtlZXAgdGhlIDd0aCBvbmUgaS5lLiB5b3UgcnVuIHRoZSBjb21tYW5kIGFuZA0KDQpNSS5NMDM5OTJfMDgzMS4wMDEuRkxEX2lsbF8wMjhfaTctLS1JRFRfaTVfOC5MVzAzX1IxLmZhc3RxLmd6DQoNCmJlY29tZXMNCg0KIk1JLk0wMzk5MiIgIjA4MzEuMDAxLkZMRCIgImlsbCIgIjAyOCIgImk3LS0tSURUIiAiaTUiICI4LkxXMDMiICJSMS5mYXN0cS5neiINCg0KQW5kIHNpbmNlIG9ubHkgdGhlIDd0aCBpcyBrZXB0IGl0IGJlY29tZXMNCg0KIjguTFcwMyINCg0KVGhlbiB3ZSByZXBlYXQganVzdCB0byBzYXZlIHRoZSBMVzQzIHBhcnQgb2YgdGhlIG5hbWUsIGJlY2F1c2UgdGhhdHMgd2hhdCB3ZSBjYXJlIGFib3V0DQoNCmBgYHtyfQ0KZ2V0LnNhbXBsZS5uYW1lLjIgPC0gZnVuY3Rpb24oZm5hbWUpIHN0cnNwbGl0KGJhc2VuYW1lKGZuYW1lKSwgIlsuXSIpW1sxXV1bMl0NCnNhbXBsZS5uYW1lcyA8LSB1bm5hbWUoc2FwcGx5KHNhbXBsZS5uYW1lcywgZ2V0LnNhbXBsZS5uYW1lLjIpKQ0KYGBgDQoNClRoaXMgdGFrZXMNCg0KIjguTFcwMyINCg0Kc3BsaXRzIGl0IGJ5IHRoZSBwZXJpb2QNCg0KTm90ZTogdGhlIHBlcmlvZCBpcyBzcGVjaWFsIGluIHJlZ2V4IGV4cHJlc3Npb25zIHNvIEkgaGF2ZSB0byBlbmNsb3NlIGl0IGluIHNxdWFyZSBicmFja2V0cyAodGhlIHNxdWFyZSBicmFja2V0cyBhcmUgYSBzcGVjaWFsIGZvcm0gb2YgYW4gZXNjYXBlIGNoYXJhY3RlciwgdGhpcyBzZW50ZW5jZSB3b24ndCBtYWtlIHNlbnNlIHVubGVzcyB5b3UgY29kZSBpbiBvdGhlciBsYW5ndWFnZXMgdGhvIDovICkNCg0KIjgiICJMVzAzIg0KDQpUaGVuIHdlIHNhdmUgdGhlIDJuZCBpbnN0YW5jZSBhbmQgdGhlIHNhbXBsZSBuYW1lIGJlY29tZXMNCg0KIkxXMDMiDQoNCkh1enphaCEgYWN0dWFsIG5hbWVzIHRoYXQgYXJlIGdvb2QNCg0KYGBge3J9DQpoZWFkKHNhbXBsZS5uYW1lcykNCmBgYA0KKFlvdSBjYW4gaWdub3JlIHRoZSBOQSwgaXRzIGEgYnlwcm9kdWN0IG9mIG15IGNyZWF0aW5nIHRoZXNlIHR1dG9yaWFscyBoYXBoYXphcmRseSkNCg0KDQpOb3cgd2Ugd2FudCB0byBpbnNwZWN0IHRoZSByZWFkIHF1YWxpdHkgcHJvZmlsZXMgZmlyc3QgdGhlIGZvcndhcmQgcmVhZA0KDQpgYGB7cn0NCnBsb3RRdWFsaXR5UHJvZmlsZShjdXRGcykNCmBgYA0KVGhlbiB0aGUgcmV2ZXJzZQ0KYGBge3J9DQpwbG90UXVhbGl0eVByb2ZpbGUoY3V0UnMpDQpgYGANClNPIGhvdyBkbyB5b3UgcmVhZCB0aGVzZT8gdGhlIGdyZWVuIGxpbmUgaXMgdGhlIG1lZGlhbiBxdWFsaXR5IHNjb3JlLCB0aGUgZ3JleXNjYWxlIGlzIGEgaGVhdG1hcCBvZiB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggcXVhbGl0eSBzY29yZSwgdGhlIHF1YXJ0aWxlcyBvZiB0aGUgYXZlcmFnZSBxdWFsaXR5IGFyZSBzaG93biB3aXRoIHRoZSBvcmFuZ2UgbGluZXMsIGFuZCB0aGUgcmVhZCBsaW5lIGF0IHRoZSBib3R0b20gKHdpdGggYSBzZXBlcmF0ZSBzY2FsZSkgc2hvd3MgdGhlIHByb3BvcnRpb24gb2YgcmVhZHMgYSB0aGF0IGV4dGVuZCB0byBhdCBsZWFzdCB0aGF0IGxlbmd0aC4gV2hhdCBpcyBxdWFsaXR5IHNjb3JlPyBjaGVjayAgb3V0DQpodHRwczovL3d3dy5pbGx1bWluYS5jb20vc2NpZW5jZS90ZWNobm9sb2d5L25leHQtZ2VuZXJhdGlvbi1zZXF1ZW5jaW5nL3BsYW4tZXhwZXJpbWVudHMvcXVhbGl0eS1zY29yZXMuaHRtbA0KYW5kIA0KaHR0cHM6Ly9kcml2ZTUuY29tL3VzZWFyY2gvbWFudWFsL2V4cF9lcnJzLmh0bWwNCmJ1dCBicmllZmx5LCBRdWFsaXR5IHNjb3JlID0gMTAgbWVhbnMgYSB3cm9uZyBiYXNlIGlzIHJlcG9ydGVkIDEwJSBvZiB0aGUgdGltZS4gUTIwIGlzIDElIGFuZCBRMzAgaXMgMC4xJSBvZiB0aGUgdGltZS4gU28gYW55dGhpbmcgdW5kZXIgcTIwIHNob3VsZCBiZSByZW1vdmVkKi4gKlRoaXMgaXMgaW4gdGhlb3J5IGNvcnJlY3Q7IGhvd2V2ZXIgaXQgaXMgYmV0dGVyIHRvIGRlbGV0ZSBzYW1wbGVzIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgZXhwZWN0ZWQgZXJyb3JzIHJhdGhlciB0aGFuIGVycm9yIGNoYW5jZS4gKHRoaXMgc3RpbGwgdXNlcyBxdWFsaXR5IHNjb3JlIGluZm9ybWF0aW9uKQ0KaHR0cHM6Ly9hY2FkZW1pYy5vdXAuY29tL2Jpb2luZm9ybWF0aWNzL2FydGljbGUvMzEvMjEvMzQ3Ni8xOTQ5Nzk/bG9naW49ZmFsc2UNCg0KVGhpcyBzZXRzIHNvbWUgcGF0aCBkaXJlY3RvcmllcyB0aGF0IHdpbGwgYmUgY3JlYXRlZCB3aXRoaW4gdGhlIGZpbHRlcmFuZHRyaW0gY29tbWFuZCBiZWxvdw0KYGBge3J9DQpmaWx0RnMgPC0gZmlsZS5wYXRoKHBhdGguY3V0LCAiZmlsdGVyZWQiLCBiYXNlbmFtZShjdXRGcykpDQpmaWx0UnMgPC0gZmlsZS5wYXRoKHBhdGguY3V0LCAiZmlsdGVyZWQiLCBiYXNlbmFtZShjdXRScykpDQpgYGANCg0KQW5kIG5vdyB3ZSBjYW4gZW50ZXIgZGFkYTIgZnVsbHkhIQ0KYGBge3J9DQpvdXQgPC0gZmlsdGVyQW5kVHJpbSgNCiAgY3V0RnMsICNsb2NhdGlvbiBvZiBjdXQgZm9yd2FyZCByZWFkcw0KICBmaWx0RnMsICNsb2NhdGlvbiBvZiB3aGVyZSBmaWx0ZXJlZCBmb3J3YXJkIHJlYWRzIHdpbGwgZ28NCiAgY3V0UnMsICNsb2NhdGlvbiBvZiBjdXQgcmV2ZXJzZSByZWFkcw0KICBmaWx0UnMsICNsb2NhdGlvbiBvZiB3aGVyZSBmaWx0ZXJlZCByZXZlcnNlIHJlYWRzIHdpbGwgZ28NCiAgbWF4TiA9IDAsICNtYXggbnVtYmVyIG9mIGFtYmlndW91cyBiYXNlcyBhbGxvd2VkIGluIGEgcmVhZCAoZGFkYTIgUkVRVUlSRVMgMCBhbWJpZ3VvdXMgYmFzZXMpDQogIG1heEVFID0gYygyLCAyKSwgI21heEVFIGlzIHRoZSBjb21tYW5kIHRoYXQgdXNlcyBleHBlY3RlZCBlcnJvciByYXRlcyB0byBkZXRlcm1pbmUgaWYgYSBzYW1wbGUgc2hvdWxkIGJlIGRlbGV0ZWQgKHJhdGhlciB0aGFuIHVzaW5nIFEgc2NvcmVzIGFic29sdXRlbHkpLCAyIGVycm9ycyBpcyB0aGUgcmVjb21tZW5kZWQgdmFsdWUuIFdoeSBpcyBpdCBhIGxpc3Q/IG9uZSBmb3IgdGhlIGZvcndhcmQgcmVhZHMgb25lIGZvciB0aGUgcmV2ZXJzZQ0KICB0cnVuY1EgPSAyLCAjdHJ1bmNhdGVzIHJlYWRzIHdoZW4gYW55IGJhc2UgcGFpciBxdWFsaXR5IHNjb3JlIGlzIDIgb3IgYmVsb3csIHdoeSAyPyBpbGx1bWluYSBzZXF1ZW5jaW5nIHJlcG9ydHMgYSAyIG9uIHJlYWxseSBiYWQgcmVhZHMNCiAgbWluTGVuID0gNTAsICNtaW5pbXVtIGxlbmd0aCBvZiByZWFkLCBub3RlIGZvciBJVFMgcmVnaW9uIHRoZXJlIGlzIG5vIG1heCBsZW5ndGgNCiAgcm0ucGhpeCA9IFRSVUUsICNyZW1vdmVzIHNlcXVlbmNlcyBvZiB0aGUgYmFjdGVyaW9waGFnZSBQaGlYIHdoaWNoIGNvbW1vbmx5IGNvbnRhbWluYXRlcyBOR1MgZGF0YQ0KICBjb21wcmVzcyA9IFRSVUUsICNjb21wcmVzc2VzIHlvdXIgZmlsZXMgd2hlbiBpdHMgZG9uZQ0KICBtdWx0aXRocmVhZCA9IEZBTFNFKSAgI211bHRpdGhyZWFkaW5nIGNhbnQgYmUgZG9uZSBvbiBhIHdpbmRvd3MgbWFjaGluZQ0KaGVhZChvdXQpDQpgYGANCg0KDQoNCmlmIHlvdSB3YW50IHRvIGxlYXJuIG1vcmUgYWJvdXQgdHJ1bmNRIHJlYWQgdGhpczogaHR0cHM6Ly9kcml2ZTUuY29tL3VzZWFyY2gvbWFudWFsL2V4cF9lcnJzLmh0bWwNCg0KT1VDSCEgVGhhdCByZW1vdmVkIHR3byB0aGlyZHMgb2YgYWxsIHRoZSByZWFkcyEhISENCg0KbGV0cyB0YWtlIGEgbG9vayBhdCB0aGUgcmVhZHMgYWdhaW4NCmBgYHtyfQ0KcGxvdFF1YWxpdHlQcm9maWxlKGZpbHRGcykNCmBgYA0KDQoNCg0KYGBge3J9DQpwbG90UXVhbGl0eVByb2ZpbGUoZmlsdFJzKQ0KYGBgDQpMZXNzIHNhbXBsZXMgYnV0IHRoZXkgYXJlIGNlcnRhaW5seSBvZiBhIG11Y2ggaGlnaGVyIHF1YWxpdHkhISBpbiB0aGUgZGFkYTIgdHV0b3JpYWwgb25seSBhYm91dCA4MCUgb2YgdGhlIHJlYWRzIHN1cnZpdmVkIHRoaXMgc3RlcCwgSG9uZXN0bHkgdGhpcyBzb3VuZHMgbGlrZSBpdCBzdWNrcyBidXQgbG9va2luZyBhdCBvdGhlciBwZW9wbGVzIHNhbXBsZXMgaXQncyBub3QgdG9vIGJhZCBpbiB0aGUgZ3JhbmQgc2NoZW1lIG9mIHRoaW5ncywgd2Ugc3RpbGwgaGF2ZSAzOCB0aG91c2FuZHMgcmVhZHMgaW4gdGhpcyBzYW1wbGVzISEuIEZlZWwgZnJlZSB0byBhc2sgbWUgKGFuZ3VzKSBhYm91dCB5b3VyIHNwZWNpZmljIGxvc3MgcmF0ZXMgd2hlbiB5b3UgZmluaXNoIHRoZSBwaXBlbGluZSBpZiB0aGlua3MgbG9vayBibGVhaywgdGhlcmUgYXJlIHNvbWUgc21hbGwgdGhpbmdzIHRoYXQgd2UgY2FuIGRvIHRvIHVwIHRoZSBudW1iZXIgKG9yIG1heWJlIHNvbWUgaGlkZGVuIGVycm9yIGlzIGVhdGluZyB5b3VyIHJlYWRzIGFuZCB3aG8ga25vd3MpIQ0KDQoNCm9LQVkhIFdlIGFyZSBvdXQgb2YgSVRTIHNwZWNpZmljIGNvbW1hbmRzIG5vdywgZXZlcnl0aGluZyBmcm9tIGhlcmUgb24gb3V0IChleGNsdWRpbmcgd2hhdCBkYXRhYmFzZSB5b3UgY29tcGFyZSB0bykgaXMgdGhlIHNhbWUgZm9yIDE2UyBhbmQgSVRTIHNlcXVlbmNpbmcuIFRoaXMgYWxzbyBtZWFucyB3ZSBtaWdodCBnZXQgYSBidW5jaCBvZiB3YXJuaW5ncyBtZXNzYWdlcyBzYXlpbmcgb3VyIHJlYWRzIGFyZSBkaWZmZXJlbnQgbGVuZ3RocywgdGhhdHMgb2theSB3ZSBrbm93IGl0IQ0KREFEQTIgdHJpZXMgdG8gbGVhcm4gdGhlIGVycm9yIHJhdGVzIG9mIGRpZmZlcmVudCBudWNsZW90aWRlIHRyYW5zaXRpb25zIChkb250IGFzayBtZSBob3cpIHNvLi4uDQoNCg0KSEVZLCBiZWNhdXNlIHlvdSByZWFkIG15IEVOVElSRSBwcmVhbWJsZSBvbiB0aGUgZGFkYTIgZ2l0IHBhZ2UgeW91IGtub3cgTWlzZXEgYW5kIG5leHRzZXEgZGF0YSBuZWVkcyB0byBsZWFybiBlcnJvcnMgYSBiaXQgZGlmZmVyZW50bHkuIFRoaXMgaXMgdGhhdCBzdGVwISBMZXRzIHNheSB5b3UgZGlkbid0IGtub3cgdGhpcyBhbmQgcmFuIHlvdXIgZGF0YSBsaWtlIG5vcm1hbCB0aGlzJ2Qgd2hhdCB5b3VyIHF1YWxpdHkgcGxvdHMgd291bGQgbG9vayBsaWtlDQpgYGB7cn0NCnBsb3QocGxvdGVyckZfUHJlTE9FU1MpDQpgYGANCmBgYHtyfQ0KcGxvdChwbG90ZXJyUl9QcmVMT0VTUykNCmBgYA0KDQpCcm90aGVyIGV3d3cuIExvb2sgYXQgQzJHIG9yIFQyQSAoZWcgQTJDIGlzIGFuIGFkZW5pbmUgLT4gQ3l0b3NpbmUgdHJhbnNpdGlvbikhIHdoYXQgdGhlIGhlbGwgaXMgdGhhdCBzcXVpZ2dseSBhc3MgbGluZSE/ISBUaGVzZSB0cmVuZCBsaW5lcyBkb24ndCBtYXRjaCB1cCB3aXRoIHRoZSBkYXRhcG9pbnRzIG9yIHRoZSByZWQgbGluZXMgYXQgYWxsIXBsdXMgdGhlbSBmYWxsaW5nIGludG8gbGV2ZWxzIGlzIHdoYXQgSSBtZWFuIHdoZW4gaSBzYXkgcGxvdHMgZmFsbCBpbnRvIHN0ZXB3aXNlIHBhdHRlcm5zIChzZWUgZG9jdW1lbnRhdGlvbiBvbiB0aGUgZ2l0KS4gDQoNCmxldHMgcnVuIGVycm9yIG1vZGVsaW5nIHRoZSBjb3JyZWN0IHdheQ0KDQpUaGlzIGNvZGUgaXMgZnJvbSBodHRwczovL2dpdGh1Yi5jb20vYmVuampuZWIvZGFkYTIvaXNzdWVzLzEzMDcsIGl0IGZvcmNlcyB0aGUgdHJlbmQgbGluZXMgdG8gaGF2ZSBtb25vdG9uaWNpdHkgKGkuZS4gdGhleSBjYW4gb25seSB0cmVuIGRpbiBvbmUgZGlyZWN0aW9uIGFuZCBub3QganVzdCB1cCBhbmQgZG93biksIGFuZCBhbHRlcnMgdGhlIGxvZXNzIGZ1bmN0aW9uIHRvIG1ha2UgaXQgcnVuIGNsb3NlciB0byB0aGUgZGF0YSBwb2ludHMuIFdoYXRzIGxvZXNzPz8gaXRzIGtpbmRhIGxpa2UgbGluZWFyIHJlZ3Jlc3Npb24gdHJ5aW5nIHRvIGZpbmQgYSB0cmVuZCBsaW5lIGJldHdlZW4geW91ciBkYXRhIHBvaW50cyBidXQgbW9yZSBjb21wbGljYXRlZC4gDQoNCkp1c3Qgc28gd2UncmUgb24gdGhlIHNhbWUgcGFnZSB0aGlzIGlzbid0IGNoYW5naW5nIHRoZSBkYXRhLCBqdXN0IHRoZSB0cmVuZCBsaW5lIHRocm91Z2ggdGhlIGRhdGEsIHNvIGFzIGxvbmcgYXMgeW91IGxvb2sgYW5kIGl0IGJldHRlciBmaXRzIHRoZSBkYXRhIHRoZW4gaXRzIG9rYXkgdG8gdXNlLiBUaGVyZSBhcmUgc29tZSBvdGhlciBvcHRpb25zIChpLmUuIG9ubHkgZW5mb3JjaW5nIG1vbm90b25pY2l0eSBvciBvbmx5IGZpeGluZyBsb2VzcykgdGhhdCB3ZSBjYW4gdHJ5IGJ1dCBiYXNlZCBvbiBteSByZXNlYXJjaCB0aGlzIHNlZW1zIGxpa2UgdGhlIGJlc3QgZ2VuZXJhbCBzb2x1dGlvbg0KYGBge3J9DQpsb2Vzc0VycmZ1bl9tb2Q0IDwtIGZ1bmN0aW9uKHRyYW5zKSB7DQogIHFxIDwtIGFzLm51bWVyaWMoY29sbmFtZXModHJhbnMpKQ0KICBlc3QgPC0gbWF0cml4KDAsIG5yb3c9MCwgbmNvbD1sZW5ndGgocXEpKQ0KICBmb3IobnRpIGluIGMoIkEiLCJDIiwiRyIsIlQiKSkgew0KICAgIGZvcihudGogaW4gYygiQSIsIkMiLCJHIiwiVCIpKSB7DQogICAgICBpZihudGkgIT0gbnRqKSB7DQogICAgICAgIGVycnMgPC0gdHJhbnNbcGFzdGUwKG50aSwiMiIsbnRqKSxdDQogICAgICAgIHRvdCA8LSBjb2xTdW1zKHRyYW5zW3Bhc3RlMChudGksIjIiLGMoIkEiLCJDIiwiRyIsIlQiKSksXSkNCiAgICAgICAgcmxvZ3AgPC0gbG9nMTAoKGVycnMrMSkvdG90KSAgIyAxIHBzdWVkb2NvdW50IGZvciBlYWNoIGVyciwgYnV0IGlmIHRvdD0wIHdpbGwgZ2l2ZSBOQQ0KICAgICAgICBybG9ncFtpcy5pbmZpbml0ZShybG9ncCldIDwtIE5BDQogICAgICAgIGRmIDwtIGRhdGEuZnJhbWUocT1xcSwgZXJycz1lcnJzLCB0b3Q9dG90LCBybG9ncD1ybG9ncCkNCiAgICAgICAgDQogICAgICAgICMgb3JpZ2luYWwNCiAgICAgICAgIyAjIyMhIG1vZC5sbyA8LSBsb2VzcyhybG9ncCB+IHEsIGRmLCB3ZWlnaHRzPWVycnMpICMjIyENCiAgICAgICAgIyBtb2QubG8gPC0gbG9lc3MocmxvZ3AgfiBxLCBkZiwgd2VpZ2h0cz10b3QpICMjIyENCiAgICAgICAgIyAjICAgICAgICBtb2QubG8gPC0gbG9lc3MocmxvZ3AgfiBxLCBkZikNCiAgICAgICAgDQogICAgICAgICMgam9uYWxpbSdzIHNvbHV0aW9uDQogICAgICAgICMgaHR0cHM6Ly9naXRodWIuY29tL2JlbmpqbmViL2RhZGEyL2lzc3Vlcy85MzgNCiAgICAgICAgbW9kLmxvIDwtIGxvZXNzKHJsb2dwIH4gcSwgZGYsIHdlaWdodHMgPSBsb2cxMCh0b3QpLGRlZ3JlZSA9IDEsIHNwYW4gPSAwLjk1KQ0KICAgICAgICANCiAgICAgICAgcHJlZCA8LSBwcmVkaWN0KG1vZC5sbywgcXEpDQogICAgICAgIG1heHJsaSA8LSBtYXgod2hpY2goIWlzLm5hKHByZWQpKSkNCiAgICAgICAgbWlucmxpIDwtIG1pbih3aGljaCghaXMubmEocHJlZCkpKQ0KICAgICAgICBwcmVkW3NlcV9hbG9uZyhwcmVkKT5tYXhybGldIDwtIHByZWRbW21heHJsaV1dDQogICAgICAgIHByZWRbc2VxX2Fsb25nKHByZWQpPG1pbnJsaV0gPC0gcHJlZFtbbWlucmxpXV0NCiAgICAgICAgZXN0IDwtIHJiaW5kKGVzdCwgMTBecHJlZCkNCiAgICAgIH0gIyBpZihudGkgIT0gbnRqKQ0KICAgIH0gIyBmb3IobnRqIGluIGMoIkEiLCJDIiwiRyIsIlQiKSkNCiAgfSAjIGZvcihudGkgaW4gYygiQSIsIkMiLCJHIiwiVCIpKQ0KICANCiAgIyBIQUNLWQ0KICBNQVhfRVJST1JfUkFURSA8LSAwLjI1DQogIE1JTl9FUlJPUl9SQVRFIDwtIDFlLTcNCiAgZXN0W2VzdD5NQVhfRVJST1JfUkFURV0gPC0gTUFYX0VSUk9SX1JBVEUNCiAgZXN0W2VzdDxNSU5fRVJST1JfUkFURV0gPC0gTUlOX0VSUk9SX1JBVEUNCiAgDQogICMgZW5mb3JjZSBtb25vdG9uaWNpdHkNCiAgIyBodHRwczovL2dpdGh1Yi5jb20vYmVuampuZWIvZGFkYTIvaXNzdWVzLzc5MQ0KICBlc3RvcmlnIDwtIGVzdA0KICBlc3QgPC0gZXN0ICU+JQ0KICAgIGRhdGEuZnJhbWUoKSAlPiUNCiAgICBtdXRhdGVfYWxsKGZ1bnMoY2FzZV93aGVuKC4gPCBYNDAgfiBYNDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuID49IFg0MCB+IC4pKSkgJT4lIGFzLm1hdHJpeCgpDQogIHJvd25hbWVzKGVzdCkgPC0gcm93bmFtZXMoZXN0b3JpZykNCiAgY29sbmFtZXMoZXN0KSA8LSBjb2xuYW1lcyhlc3RvcmlnKQ0KICANCiAgIyBFeHBhbmQgdGhlIGVyciBtYXRyaXggd2l0aCB0aGUgc2VsZi10cmFuc2l0aW9uIHByb2JzDQogIGVyciA8LSByYmluZCgxLWNvbFN1bXMoZXN0WzE6MyxdKSwgZXN0WzE6MyxdLA0KICAgICAgICAgICAgICAgZXN0WzQsXSwgMS1jb2xTdW1zKGVzdFs0OjYsXSksIGVzdFs1OjYsXSwNCiAgICAgICAgICAgICAgIGVzdFs3OjgsXSwgMS1jb2xTdW1zKGVzdFs3OjksXSksIGVzdFs5LF0sDQogICAgICAgICAgICAgICBlc3RbMTA6MTIsXSwgMS1jb2xTdW1zKGVzdFsxMDoxMixdKSkNCiAgcm93bmFtZXMoZXJyKSA8LSBwYXN0ZTAocmVwKGMoIkEiLCJDIiwiRyIsIlQiKSwgZWFjaD00KSwgIjIiLCBjKCJBIiwiQyIsIkciLCJUIikpDQogIGNvbG5hbWVzKGVycikgPC0gY29sbmFtZXModHJhbnMpDQogICMgUmV0dXJuDQogIHJldHVybihlcnIpDQp9DQoNCmBgYA0KVGhpcyBhYm92ZSBjb2RlIGp1c3QgbWFrZXMgYW4gZXJyb3IgbW9kZWwuIE5vdyB3ZSBjYW4gYWN0dWFsbHkgcnVuIG91ciBlcnJvciBtb2RlbGluZyBhcHByb2FjaA0KDQpgYGB7cn0NCiNub3RlIHRvIHJ1biB0aGUgZXJyb3IgbWF0cml4IHdpdGggdGhpcyBuZXcgbW9kZWwsIHRoZSB0aHJlZSBzYW1wbGVzIGkndmUgZ2l2ZW4gdGhlIHByb2dyYW0gYXJlbid0IGVub3VnaCwgeW91IGNhbiBvbmx5IHJlYWxseSBydW4gdGhpcyB3aXRoIGEgZnVsbCBkYXRhIHNldA0KZXJyRiA8LSBsZWFybkVycm9ycygNCiAgZmlsdEZzLA0KICBtdWx0aXRocmVhZCA9IEZBTFNFLCAjcmVtZW1iZXIgbXVsdGl0cmVhZCA9IFRSVUUgb24gYmlnIGNvbXB1dGVycw0KICBuYmFzZXMgPSAxZTksDQogIGVycm9yRXN0aW1hdGlvbkZ1bmN0aW9uID0gbG9lc3NFcnJmdW5fbW9kNCwNCiAgdmVyYm9zZSA9IFRSVUUNCikNCg0KZXJyUiA8LSBsZWFybkVycm9ycygNCiAgZmlsdFJzLA0KICBtdWx0aXRocmVhZCA9IEZBTFNFLA0KICBuYmFzZXMgPSAxZTksDQogIGVycm9yRXN0aW1hdGlvbkZ1bmN0aW9uID0gbG9lc3NFcnJmdW5fbW9kNCwNCiAgdmVyYm9zZSA9IFRSVUUNCikNCg0KYGBgDQoNCg0KDQpUaGVuIGxldHMgdGFrZSBhIGxvb2suLi4NCmBgYHtyfQ0KcGxvdEVycm9ycyhlcnJGLCBub21pbmFsUT1UUlVFKQ0KYGBgDQpgYGB7cn0NCnBsb3RFcnJvcnMoZXJyUiwgbm9taW5hbFE9VFJVRSkNCmBgYA0KDQoNCkxvb2tpbmcgYXQgdGhlc2UgdHdvIGdyYXBocywgdGhleSBsb29rIHByZXR0eSBnb29kLCB0aGUgYWN0dWFsIHZhbHVlcyAoZ3JleSBkb3RzKSBtYXRjaCB1cCB3aXRoIHRoZSBleHBlY3RlZCAocmVkIGxpbmVzKSBwcmV0dHkgd2VsbCBmb3IgdGhlIGRpZmZlcmVudCBudWNsZW90aWRlIHRyYW5zaXRpb25zIGVzcGVjaWFsbHkgY29tcGFyZWQgdG8gdGhlIGVhcmxpZXIgb25lcy4gd2Ugc2VlIHRoYXQgQTJHIGFuZCBUMkMgdHJhbnNpdGlvbnMgYXJlIHN0aWxsIGtpbmRhIHN0cmFpZ2h0IChlc3BlY2lhbGx5IGNvbXBhcmVkIHRvIHRoZSByZWQgbGluZSksIHRoaXMgaXMgZHVlIHRvIHRoZSBuZXh0c2VxIGl0c2VsZiBhbmQgKHRoZSBnbG9iYWwpIHdlIGRvbid0IHJlYWxseSBrbm93IHdoeSB5ZXQuIGRhZGEyIGlzIHN0aWxsIGRldmVsb3Bpbmcgc29sdXRpb25zIHRvIHRoaXMgaXNzdWUgKGFuZCB0aGVzZSBzb2x1dGlvbnMgaGF2ZSBiZWVuIGluIGRldmVsb3BtZW50IHNpbmNlIDIwMTkuLi4pDQoNCk5vdyBpdHMgdGltZSB0byBydW4gZGFkYTIgdG8gZmluZCBvdXIgdW5xaXVlIHNlcXVlbmNlcw0KYGBge3J9DQpkYWRhRnMgPC0gZGFkYShmaWx0RnMsIGVycj1lcnJGLCBtdWx0aXRocmVhZD1GQUxTRSkNCiNTYW1wbGUgMSAtIDM4NjIxIHJlYWRzIGluIDEyODMzIHVuaXF1ZSBzZXF1ZW5jZXMuDQpgYGANCg0KDQooSSdtIGxhenkgYW5kIHdvbnQgcmVydW4gdGhlIHdob2xlIHRoaW5nIGp1c3Qgc28gdGhlIG91dHB1dHMgYXJlIGhlcmUgb24gdGhlIGJvdHRvbSBpbiB0aGUgY29tbWVudHMpDQoNCmBgYHtyfQ0KZGFkYVJzIDwtIGRhZGEoZmlsdFJzLCBlcnI9ZXJyUiwgbXVsdGl0aHJlYWQ9RkFMU0UpDQojU2FtcGxlIDEgLSAzODYyMSByZWFkcyBpbiAxMTIyMiB1bmlxdWUgc2VxdWVuY2VzLg0KYGBgDQoNCg0KDQoNClN3ZWV0IGxvYWRzIG9mIHVuaXF1ZSBzZXF1ZW5jZXMNCmxldHMgbWVyZ2UgdGhlIGZvcndhcmQgYW5kIHJldmVyc2UgcmVhZHMNCmBgYHtyfQ0KbWVyZ2VycyA8LSBtZXJnZVBhaXJzKGRhZGFGcywgZmlsdEZzLCBkYWRhUnMsIGZpbHRScywgdmVyYm9zZT1UUlVFKQ0KIzI3MTU2IHBhaXJlZC1yZWFkcyAoaW4gMTY4IHVuaXF1ZSBwYWlyaW5ncykgc3VjY2Vzc2Z1bGx5IG1lcmdlZCBvdXQgb2YgMzc3NDcgKGluIDUyNCBwYWlyaW5ncykgaW5wdXQuDQoNCg0KIyBJbnNwZWN0IHRoZSBtZXJnZXIgZGF0YS5mcmFtZSBmcm9tIHRoZSBmaXJzdCBzYW1wbGUNCmhlYWQobWVyZ2Vyc1tbMV1dKQ0KDQojWzFdICJDR0NBQ0NUVEdDR0NUQ1RUVEdHVEFUVENDR0FBR0FHQ0FUR0NDVEdUVFRHQUdUR1RDQVRHQUFBQVRBVENBQUNDVFRHQUNUVEdHR1RUVEFHVEdDVENUVEdUQ1RUR0dDVFRHR0FUVFRHR0NUR1RUVEdDQ0dDVENHQUFBR0FHVENHR0NUQ0FHQ1RUQUFBQUdUQVRUQUdDVEdHQVRDVEdUQ1RUVEdBR0FDVFRHR1RUVEdBQ1RUR0dDR1RBQVRBQUdUVEFUVFRDR0NUR0FHR0FDQUFUQ1RUQ0dHQVRUR0dDQ0dBR1RUVENUR0dHQUNHVFRUR1RDQ0dDVFRUQ1RBQVRBQ0FBR1RUQ1RBR0NUVEdDVEFHQUNBVEdBQ1RUVFRUVEFUVEFUQ1RHR0NDVENBQUFUQ0FHR1RBR0dBQ1RBQ0NDR0NUR0FBQ1RUQUEiICAgICAgICAgICAgICAgICAgICAgDQojWzJdICJDR0NBQ0FUVEdDR0NDQ0NUVEdHVEFUVENDQVRHR0dHQ0FUR0NDVEdUVENHQUdDR1RDQVRUVEdUQUNDVFRDQUFHQ1RUVEdDVFRHR1RHVFRHR0dUR1RUVEdUQ1RDR0NDVFRHQ0dUR1RBR0FDVENHQ0NUVEFBQUFUQUFUVEdHQ0FHQ0NHR0NHVEFUVEdBVFRUQ0dHQUdDR0NBR1RBQ0FUQ1RDR0NBQ1RDVEdUQ0NUQ0FDQUFDR0FDR0FDR1RDQ0FBQUFHVEFDVFRUVFRBQ0FDVENUVEdBQ0NUQ0dHQVRDQUdHVEFHR0dBVEFDQ0NHQ1RHQUFDVFRBQSIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQojWzNdICJDR0NBQ0NUVEdDR0NDQ1RUVEdHVEFUVENDR0FBR0dHQ0FUR0NDVEdUVFRHQUdUR1RDQVRHQUFBQ0NUQ0FDQ0NDQUNUVEdHR1RUVFRUR0NDVEdBR0NHR1RHR1RHVEFUVEdHR1RHVFRHQ0NUVEdDVEFBQUdHQ1RDR0NDVFRBQUFHQUNBVEFBR0NBQ0NUVEdHQVRHVEFBVEFDR1RUVENBVENDVFRDVEdHR1RHR0NUR0FUQUFDQ0NDQUNBVEFUVENBVEdBVENUR0dDQ1RDQUFBVENBR0dUQUdHR0NUQUNDQ0dDVEdBQUNUVEFBIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQojWzRdICJDR0NBQ0FUVEdDQUNUQ1RDVEdHVEFUVENDR0dBR0FHVEFUR0NUVEdUVFRHQUdUQVRDVEdUQUFBQ0FUQ1RDQUFBVENUQ1RDR0FDVFRUQ0dBR1RUVEdBR0FHQUFUVEdHQUNUVEdBR0NBQVRDQ0NBQUNUQ0NUVENHVENBR0FBQUdBR0dBR0dHVFRHQ1RUR0FBQVRHQ0FHR1RHQ0FHQ1RHR0dDQVRUQ1RUQ1RHQUdDVEFBQUFHQ0FUQVRUVEFUVFRBR1RDQ0NHVENBQUFDR0dBVFRBVFRBVFRDVFRHQ1RUQ0FHQ1RUQUNBVEFBQUdHVFRHQUFUR0FDVEFDQ0dBR0dDVEdBVFRHQ0FHR1RBQUFDVENHVFRDQ0dUVEFBQUFBR0FBQ1RUR1RHQUFDVENHQVRDVENBQUFUQ0FBR1RBQUdBQ1RBQ0NDR0NUR0FBQ1RUQUEiICAgICAgICAgICAgICAgDQojWzVdICJDR0NBQ0FUVEdDR0NUQ1RDVEFHVEFUVENUR0dBR0FHQ0FUR0NUVEdUVFRHQUdUQVRDQUdUQUFBQ0FDQ1RDQUFUQ0NDQ1RBQUFUQ1RUR0FBQUFBR0FBQUdHR0FBVFRHR0FDVFRHQUdDVEFUQ0NDQUFDQUFBQ0dUQUFBQUNUVFRHR0NHR0dUQ0dDVFRHQUFBVEdDQUdHVEdDQUdDVEdHQUFDVEFDVENUVENUR0FHQ1RBVEFBR0NBVEFUQ1RBVFRUQUdUQ0NDR1RDQUFBQ0dHQVRUQVRUQVRUVFRUVEdDVEdDQUdDVEFBQ0FUQUFBR0dBQUFBR1RBR0FUQ0NHQ1RBVEdDVEdBQ1RHQVRHQ0FHR0FBVFRBR0NBR1RHQUFDR1RDVENUVFRHQUdHQ0dDVFRBQ1RHVEdBQUFDVENHQVRDVENBQUFUQ0FBR1RBQUdBQ1RBQ0NDR0NUR0FBQ1RUQUEiDQojWzZdICJDR0NBQ0NUVEdDR0NDQ1RUVEdHVEFUVENDR0FBR0dHQ0FUR0NDVEdUVFRHQUdUR1RDQVRHQUFBQ0NUQ0FDQ0NDQUNUVEdHR1RUVFRUQUNDVEdBR0NHR1RHR1RHVEFUVEdHR1RHVFRHQ0NUVEdDVEFBQUdHQ1RDR0NDVFRBQUFHQVRBVEFBR0NBQ0NUVEdHQVRHVEFBVEFDR1RUVENBVENDVFRDVEdHR1RHR0NUQUFUQUFDQ0NDQUNBVEFUVENBVEdBVENUR0dDQ1RDQUFBVENBR0dUQUdHR0NUQUNDQ0dDVEdBQUNUVEFBIiAgIA0KDQoNCmBgYA0KDQoNCg0KDQpzd2VldCBvbmx5IGxvc3QgMTAnMDAwIHJlYWRzIGZyb20gdGhhdCENCk5vdyB3aXRoIGZ1bGwgc2VxdWVuY2VzIHdlIGNhbiBjb25zdHJ1Y3Qgc2VxdWVuY2UgdGFibGVzIGFuZCBkZXRlcm1pbmUgb3VyIGFtcGxpY29uIHNlcXVlbmNlIHZhcmlhbnRzIChBU1YncykgDQpgYGB7cn0NCnNlcXRhYiA8LSBtYWtlU2VxdWVuY2VUYWJsZShtZXJnZXJzKQ0KZGltKHNlcXRhYikNCiNbMV0gICAxIDE2OA0KDQpgYGANClRoYXQgbWVhbnMgMTY4IHVucWl1ZSBBU1YncyENCmBgYHtyfQ0KIyBJbnNwZWN0IGRpc3RyaWJ1dGlvbiBvZiBzZXF1ZW5jZSBsZW5ndGhzDQp0YWJsZShuY2hhcihnZXRTZXF1ZW5jZXMoc2VxdGFiKSkpDQoNCiMxOTYgMjExIDIxNCAyMTUgMjE2IDIyMiAyMjkgMjMyIDIzMyAyMzQgMjM2IDIzNyAyMzggMjM5IDI0MCAyNDEgMjQyIDI0MyAyNDQgMjQ1IDI0NiAyNDcgMjQ4IDI0OSAyNTIgMjU0IDI1NiAyNTggMjU5IDI2MiAyNjUgMjY2IDI3MCAyNzIgMjczIA0KIyAgMSAgIDEgICAxICAgMiAgIDEgICAxICAgMSAgIDggICAxICAgMyAgIDUgICA1ICAxMiAgIDQgICAyICAgMyAgIDEgICAxICAgMSAgIDMgICAzICAgNCAgIDMgICAxICAgMSAgIDEgICAxICAgMSAgIDEgICAxICAgMSAgIDEgICAxICAgMiAgIDEgDQojMjc1IDI4MCAyODMgMjg0IDI4NiAyODcgMjkyIDI5OCAzMDMgMzA0IDMwNSAzMDYgMzExIDMxMiAzMTMgMzE2IDMyNCAzMjUgMzI2IDMyNyAzMjggMzI5IDMzMCAzMzEgMzMyIDMzMyAzMzQgMzM1IDMzNiAzMzggMzM5IDM0MSAzNDMgMzQ1IDM0NyANCiMgIDEgICAyICAgMSAgIDEgICAxICAgMSAgIDEgICAxICAgMiAgIDEgICAxICAgMiAgIDIgICAxICAgMSAgIDEgICAxICAgMiAgIDIgIDExICAgOSAgIDUgICAxICAgMyAgIDEgICA2ICAgMyAgIDMgICAzICAgMiAgIDEgICAxICAgMSAgIDIgICAyIA0KIzM0OCAzNDkgMzU2IDM3MCAzNzMgDQojICAyICAgMiAgIDIgICAxICAgMiANCmBgYA0KV2UgaGF2ZSBzbyBtdWNoIHZhcmlhdGlvbiBpbiB0aGUgYW1vdW50IG9mIHNlcXVlbmNlIGxlbmd0aHMgYmVjYXVzZSBpdHMgdGhlIElUUyBzZXF1ZW5jZSB3ZSBhcmUgdXNpbmcsIGJ1dCBhbHdheXMgY2hlY2sgdGhhdCB0aGUgc2VxdWVuY2UgbGVuZ3RoIG1ha2VzIHNlbnNlIGFzIHdlaXJkIHNlcXVlbmNlIGxlbmd0aHMgY2FuIHJlc3VsdCBmcm9tIG5vbi1zcGVjaWZpYyBtZXJnaW5nLiBUaGUgSVRTMiByZWdpb24gaXMgYmV0d2VlbiAxNjAtMzkwIGJwcyBzbyB0aGlzIGFsbCBjaGVja3Mgb3V0IA0KaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNzE2MjQ4OC8jOn46dGV4dD1JbiUyMHNwaXRlJTIwb2YlMjBpdHMlMjBzaG9ydCxhY3Jvc3MlMjBhbGwlMjBsYW5kJTIwcGxhbnRzJTIwKGUuDQoNCkJ1dCBsaWZlIGlzIG5ldmVyIHBlcmZlY3RseSBlYXN5LCB3ZSBtdXN0IHJlbW92ZSBjaGltZXJhcyAoaS5lLiBzZXF1ZW5jZXMgdGhhdCBoYXZlIGJlZW4gbWVyZ2VkIGJ1dCBkb250IHJlZmxlY3QgYmlvbG9naWNhbGx5IHNwZWNpZXMpLiBDaGltZXJhcyBhcmUgY2hlY2tlZCBmb3IgYnkgbG9va2luZyBhdCBBU1ZzIHdpdGggbG93IGFidW5kYW5jZSB0aGF0IGNhbiBiZSBtYWRlIG9mIGNvbWJpbmF0aW9ucyBvZiBoaWdobHkgYWJ1bmRhbnQgQVNWJ3MuIFdoeSBhbmQgaG93IHRoaXMgaGFwcGVucyBpcyB3ZWxsIGV4cGxhaW5lZCBoZXJlDQpodHRwczovL2ZvcnVtLnFpaW1lMi5vcmcvdC9kYWRhMi1tZXJnaW5nLXBsYXRlcy1hbmQtY2hpbWVyYS1yZW1vdmFsLzIxMDMwLzQNCmBgYHtyfQ0Kc2VxdGFiLm5vY2hpbSA8LSByZW1vdmVCaW1lcmFEZW5vdm8oc2VxdGFiLCBtZXRob2Q9ImNvbnNlbnN1cyIsIG11bHRpdGhyZWFkPVRSVUUsIHZlcmJvc2U9VFJVRSkNCiNJZGVudGlmaWVkIDQxIGJpbWVyYXMgb3V0IG9mIDE2OCBpbnB1dCBzZXF1ZW5jZXMuDQoNCmRpbShzZXF0YWIubm9jaGltKQ0KI1sxXSAgIDEgMTI3DQoNCmBgYA0KNDEgY2hpbWVyaWMgc2VxdWVuY2VzISBUaGlzIGlzIDI0LjQ1JSBvZiB0aGUgdG90YWwgYW1vdW50IG9mIEFTVidzISEgDQpgYGB7cn0NCnN1bShzZXF0YWIubm9jaGltKS9zdW0oc2VxdGFiKQ0KI1sxXSAwLjk1MzE1OTUNCmBgYA0KSG93ZXZlciwgcmVtb3ZpbmcgdGhlc2Ugc2FtcGxlcyBvbmx5IHJlc3VsdGVkIGluIGEgNCUgbG9zcyBvZiByZWFkcyBpbiB0b3RhbCEgVGhlc2UgYXJlIG5vcm1hbCBudW1iZXJzLCBhbmQgYW55dGhpbmcgdW5kZXIgMjUlIGlzIG5vIG5lZWQgZm9yIGEgc2Vjb25kIHRob3VnaHQuIENoaW1lcmFzIGNhbiBiZSBtb3JlIG9yIGxlc3Mgb2YgYSBzYW1wbGUgYmFzZWQgb24gdGhlIHNwZWNpZmljIGV4cGVyaW1lbnRhbCBwcm9jZWR1cmVzDQoNCk5vdyBsZXRzIGRvIGEgd2hvbGUgbG9vayB0aHJvdWdoIGFsbCB0aGUgcmVhZHMgdGhyb3VnaG91dCB0aGUgcGlwZWxpbmUNCmBgYHtyfQ0KZ2V0TiA8LSBmdW5jdGlvbih4KSBzdW0oZ2V0VW5pcXVlcyh4KSkNCnRyYWNrIDwtIGNiaW5kKG91dCwgZ2V0TihkYWRhRnMpLCBnZXROKGRhZGFScyksIGdldE4obWVyZ2VycyksIHJvd1N1bXMoc2VxdGFiLm5vY2hpbSkpDQpjb2xuYW1lcyh0cmFjaykgPC0gYygiaW5wdXQiLCAiZmlsdGVyZWQiLCAiZGVub2lzZWRGIiwgImRlbm9pc2VkUiIsICJtZXJnZWQiLCAibm9uY2hpbSIpDQpyb3duYW1lcyh0cmFjaykgPC0gc2FtcGxlLm5hbWVzDQpoZWFkKHRyYWNrKQ0KDQoNCg0KIyAgICAgaW5wdXQgZmlsdGVyZWQgZGVub2lzZWRGIGRlbm9pc2VkUiBtZXJnZWQgbm9uY2hpbQ0KI0xXMDEgNjE2MDYgICAgMzg2MjEgICAgIDM4MDU4ICAgICAzODA0NSAgMjcxNTYgICAyNTg4NA0KDQpgYGANClBTLiBJIG1vZGlmaWVkIHRoZSBhYm92ZSBjb2RlIHRvIHdvcmsgb24gYSBzaW5nbGUgc2FtcGxlLiB5b3UnbGwgaGF2ZSB0byBjaGFuZ2UgaXQgZm9yIG11bHRpcGxlIHNhbXBsZXMsIHNlZSB0aGUgaXRzIGJpZyBkYXRhIHR1dG9yaWFsDQoNCg0KDQoNCg0KVGhlbiBsZXRzIGFzc2lnbiB0YXhvbm9teSENCmZpcnN0IHdlIG5lZWQgdG8gZG93bmxvYWQgdGhlIElUUyBkYXRhYmFzZSBmcm9tIFVOSVRFIA0KaHR0cHM6Ly91bml0ZS51dC5lZS9yZXBvc2l0b3J5LnBocA0KDQp5b3UnbGwgd2FudCB0byBkb3dubG9hZCBvbmUgb2YgdGhlIGdlbmVyYWwgZmFzdGEgcmVsZWFzZXMuIFRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZXNlIGZpbGVzIGlzbid0IHdlbGwgZXhwbGFpbmVkLiBUaGVyZSBzZWVtcyB0byBiZSB0d28gZnVuZ2kgYW5kIHR3byBldWthcnlvdGUgZGF0YWJhc2VzIHRvIGRvd25sb2FkDQoNCkkgaGF2ZSB0aGUgYmVhbnMgb24gdGhlIEhQQyB0byBkbyB3aGF0IEkgd2FudCwgc28gSSdsbCBiZSB1c2luZyB0aGUgc2xpZ2h0bHkgbGFyZ2VyIGRhdGFiYXNlLCBhbmQgcGVyaGFwcyB0aGF0cyBob3cgeW91IHNob3VsZCBtYWtlIHlvdXIgZGVjaXNpb24gYXN3ZWxsLCBidXQgaWYgeW91IGhhdmUgc3Ryb25nIG9waW5pb25zIHRlbGwgbWUuDQpgYGB7cn0NCnVuaXRlLnJlZiA8LSAiQzpcXFVzZXJzXFxhbmd1c1xcT25lRHJpdmVcXERvY3VtZW50c1xcU3RvcnlcXGxpc2F0ZXN0ZGF0YXNldFxcVU5JVEVfZGF0YWJhc2UuZmFzdHEuZ3oiICAjIENIQU5HRSBNRSB0byBsb2NhdGlvbiBvbiB5b3VyIG1hY2hpbmUNCnRheGEgPC0gYXNzaWduVGF4b25vbXkoc2VxdGFiLm5vY2hpbSwgI1NlcXVuY2VzDQogICAgICAgICAgICAgICAgICAgICAgIHVuaXRlLnJlZiwgI2RhdGFic2UNCiAgICAgICAgICAgICAgICAgICAgICAgbXVsdGl0aHJlYWQgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgIHRyeVJDID0gVFJVRSkgI3RyeSByZXZlcnNlIGNvbXBsaW1lbnQgb2Ygc2VxdWVuY2VzDQpgYGANCk15IGJhYnkgY29tcHV0ZXIgZG9lc250IGhhdmUgdGhlIHN0dWZmcyB0byBwcm9jZXNzIGV2ZW4gb25lIHNhbXBsZSEgQnV0IGFsYXMsIHdlIGNhbiBzZWUgd2hhdCBpdCB0aGVvcmV0aWNhbGx5IHdvdWxkIGxvb2sgbGlrZSB3aXRoIHRoaXMgDQpgYGB7cn0NCnRheGEucHJpbnQgPC0gdGF4YSAgIyBSZW1vdmluZyBzZXF1ZW5jZSByb3duYW1lcyBmb3IgZGlzcGxheSBvbmx5DQpyb3duYW1lcyh0YXhhLnByaW50KSA8LSBOVUxMDQpoZWFkKHRheGEucHJpbnQpDQpgYGANCg0KVGhlbiB5b3UgY2FuIHRha2UgeW91ciBkYXRhIGFuZCBzYXZlIGl0ISBZb3VyIHNlcXRhYi5ub2NoaW0sIGFuZCB0YXhhIGFyZSB0aGUgaW1wb3J0YW50IGZpbGVzLiBzZXF0YWIubm9jaGltIGlzIHlvdXIgb3R1X3RhYmxlLCBhbmQgdGF4YSBpcyB5b3VyIHRheF90YWJsZQ0KdG8gc2F2ZS4uLg0KYGBge3J9DQpzYXZlUkRTKHRheGEsIGZpbGUgPSAiQzpcXFVzZXJzXFxhbmd1c1xcT25lRHJpdmVcXERvY3VtZW50c1xcU3RvcnlcXGxpc2F0ZXN0ZGF0YXNldFxcdGF4YS5yZHMiICkjYWRkIHlvdXIgZmlsZSBsb2NhdGlvbiBoZXJlDQpzYXZlUkRTKHNlcXRhYi5ub2NoaW0sIGZpbGUgPSAiQzpcXFVzZXJzXFxhbmd1c1xcT25lRHJpdmVcXERvY3VtZW50c1xcU3RvcnlcXGxpc2F0ZXN0ZGF0YXNldFxcc2VxdGFibm9jaGltLnJkcyIgKSNhZGQgeW91ciBmaWxlIGxvY2F0aW9uIGhlcmUNCmBgYA0KDQpUaGVuIHRvIG1ha2UgYSBwaHlsb3NlcSBvYmplY3QgbGF0ZXIgb24uLi4NCmBgYHtyfQ0KbGlicmFyeShwaHlsb3NlcSkNCnBzIDwtIHBoeWxvc2VxKG90dV90YWJsZShzZXF0YWIubm9jaGltLCB0YXhhX2FyZV9yb3dzID0gRkFMU0UpLCB0YXhfdGFibGUodGF4YSkpDQpzYXZlUkRTKHBzLCBmaWxlID0gIkM6XFxVc2Vyc1xcYW5ndXNcXE9uZURyaXZlXFxEb2N1bWVudHNcXFN0b3J5XFxsaXNhdGVzdGRhdGFzZXRcXHBzLnJkcyIgKSNhZGQgeW91ciBmaWxlIGxvY2F0aW9uIGhlcmUNCg0KYGBgDQoNCg0KDQoNCg==