Install & Load packages

#Set required packages
.cran_packages <- c("ggplot2", 
                    "gridExtra",
                    "tidyverse", 
                    "scales", 
                    "stringdist", 
                    "patchwork",
                    "seqinr",
                    "viridis", 
                    "ape", 
                    "data.table",
                    "RColorBrewer",
                    "vegan",
                    "geiger")
.bioc_packages <- c("dada2",
                    "phyloseq", 
                    "DECIPHER",
                    "Biostrings",
                    "ShortRead",
                    "Biostrings",
                    "ALDEx2")

.inst <- .cran_packages %in% installed.packages()
if(any(!.inst)) {
   install.packages(.cran_packages[!.inst])
}
.inst <- .bioc_packages %in% installed.packages()
if(any(!.inst)) {
  if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
  BiocManager::install(.bioc_packages[!.inst], ask = F)
}

#Load all packages
sapply(c(.cran_packages,.bioc_packages), require, character.only = TRUE)
##      ggplot2    gridExtra    tidyverse       scales   stringdist    patchwork 
##         TRUE         TRUE         TRUE         TRUE         TRUE         TRUE 
##       seqinr      viridis          ape   data.table RColorBrewer        vegan 
##         TRUE         TRUE         TRUE         TRUE         TRUE         TRUE 
##       geiger        dada2     phyloseq     DECIPHER   Biostrings    ShortRead 
##         TRUE         TRUE         TRUE         TRUE         TRUE         TRUE 
##   Biostrings       ALDEx2 
##         TRUE         TRUE
#Install and load github packages
devtools::install_github("alexpiper/seqateurs")
library(seqateurs)
devtools::install_github("tobiasgf/lulu")
library(lulu)
devtools::install_github("mikemc/speedyseq")
library(speedyseq)
devtools::install_github('ggloor/CoDaSeq/CoDaSeq')
library(CoDaSeq)

options(stringsAsFactors = FALSE)

Quality Control

Split files by seqrun

## R
samdf <- read.csv("sample_data/sample_info.csv")
run1 <- samdf %>%
  filter(seqrun==1) %>%
  pull(SampleID) %>%
  as.character()
write_lines(run1,path="Run1.txt")

run2 <- samdf %>%
  filter(seqrun==2) %>%
  pull(SampleID) %>%
  as.character()
write_lines(run2,path="Run2.txt")

run3 <- samdf %>%
  filter(seqrun==3) %>%
  pull(SampleID) %>%
  as.character()
write_lines(run3,path="Run3.txt")
## BASH
mkdir run_1
cat Run1.txt | while read i; do
payload=$(ls | grep $i)
echo $payload
mv $payload run_1
done

mkdir run_2
cat Run2.txt | while read i; do
payload=$(ls | grep $i)
echo $payload
mv $payload run_2
done

mkdir run_3
cat Run3.txt | while read i; do
payload=$(ls | grep $i)
echo $payload
mv $payload run_3
done

Sequencer ID & fastq structure for each run

run_1 - @M01895:3:000000000-AFED3:1:1101:11856:1005 1:N:0:89
run_2 - @M00598:140:000000000-ALTW6:1:1104:15177:9425 2:N:0:120
run_3 - @M00933:9:000000000-BFR4B:1:1101:13542:1780 1:N:0:86

Sequence quality control

library(ShortRead)
runs <- dir("data/", pattern="run_")

for (i in seq(along=runs)){
path <- paste0("data/", runs[i])

#Plot number of reads
dat <- as.data.frame(countLines(dirPath=path, pattern=".fastq")) %>%
  rownames_to_column()  %>%
   `colnames<-`(c("Sample", "Reads")) %>%
  filter(str_detect(Sample,"R1"))

#Plot pooling
gg.pooling <- ggplot(data=dat, aes(x=Sample,y=Reads),stat="identity") + 
  geom_bar(aes(fill=Reads),stat="identity")  + 
  scale_fill_viridis(name = "Reads", begin=0.1) + 
  theme(axis.text.x = element_text(angle=90, hjust=1), plot.title=element_text(hjust = 0.5), plot.subtitle =element_text(hjust = 0.5))+ 
  geom_hline(aes(yintercept = mean(Reads)))  +
  xlab("sample name")+
  ylab("Number of reads") + 
  labs(title= paste0("Pooling for : ", runs[i]), subtitle = paste0("Total Reads: ", sum(dat$Reads), " Average reads: ",  sprintf("%.0f",mean(dat$Reads))," Standard deviation: ", sprintf("%.0f",sd(dat$Reads)))) +
  coord_flip()

plot(gg.pooling)
}

Trim primers

DADA2 requires Non-biological nucleotides i.e. primers, adapters, linkers, etc to be removed. Prior to begining this workflow, samples were demultiplexed and illumina adapters were removed by the MiSeq software, however primer sequences still remain in the reads and must be removed prior to use with the DADA2 algorithm.

Primer sequences:

16S F CCTACGGGNGGCWGCAG
16S R GACTACHVGGGTATCTAATCC

All reads were trimmed to 300bp, then primers were removed. All reads for which primers were not detected were removed using the maxlength function.

#Install bbmap
seqateurs::bbmap_install()

#Loop over runs - Maxlength set to remove any untrimmed reads
runs <- dir("data/", pattern="run_")

for (i in seq(along=runs)){
  path <- paste0("data/",runs[i])
  
  #Trim forward primers - Set a maxlength to remove all those that werent trimemd
  fastqFs <- sort(list.files(path, pattern="_R1", full.names = TRUE))
  fastqRs <- sort(list.files(path, pattern="_R2_", full.names = TRUE))
  
  bbtools_trim(install="bin/bbmap", fwd=fastqFs,rev=fastqRs, primers=c("CCTACGGGNGGCWGCAG","GACTACHVGGGTATCTAATCC"), copyundefined=TRUE, outpath="trimmed",ktrim="l", ordered=TRUE, mink=FALSE, hdist=2, overwrite=TRUE, samelength=TRUE, forcetrimright = 300, maxlength = 285)
}

Plot read quality & lengths

runs <- dir("data/", pattern="run_")
readcounts <- vector("list", length=length(runs))

for (i in seq(along=runs)){
 path <- paste0("data/",runs[i],"/trimmed" )

  filtFs <- sort(list.files(path, pattern="_R1_", full.names = TRUE))
  filtRs <- sort(list.files(path, pattern="_R2_", full.names = TRUE))
  p1 <- plotQualityProfile(filtFs, aggregate = TRUE) + ggtitle(paste0(runs[i]," Forward Reads")) 
  p2 <- plotQualityProfile(filtRs, aggregate = TRUE) + ggtitle(paste0(runs[i]," Reverse Reads"))
  
  #output plots
  dir.create("output/figures/")
  pdf(paste0("output/figures/",runs[i],"_prefilt_quality.pdf"), width = 11, height = 8 , paper="a4r")
  plot(p1+p2)
  dev.off()
  
  #Get lengths
  readcounts[[i]] <- cbind(width(readFastq(file.path(path, fastqFs))), width(readFastq(file.path(path, fastqRs))))
}

The max expected error function is used as the primary quality filter, and all reads containing N bases were removed

In order to reduce the amount of reverse reads violating the MaxEE filter, the reverse reads were truncated at 200 to remove the quality crash that is typical of illumina sequencers

Total amplicon = 465bp Sequencing = 2x300bp = 600bp Primers = 17bp + 21bp = 38bp Read overlap = 600 - 465 - 38 = 97bp

reverse should potentiall be reduced further - (from 230 to 200)

runs <- dir("data/", pattern="run_")
filtered_out <- vector("list", length=length(runs))
readlengths <- vector("list", length=length(runs))

for (i in 1:length(runs)){
  path <- paste0("data/",runs[i],"/trimmed/") 
  filtpath <- file.path(path, "filtered")
  dir.create(filtpath)
  fastqFs <- sort(list.files(path, pattern="R1_001.*"))
  fastqRs <- sort(list.files(path, pattern="R2_001.*"))
  
  if(length(fastqFs) != length(fastqRs)) stop(paste0("Forward and reverse files for ",runs[i]," do not match."))
  
  filtered_out[[i]] <- (filterAndTrim(fwd=file.path(path, fastqFs), filt=file.path(filtpath, fastqFs),
                                      rev=file.path(path, fastqRs), filt.rev=file.path(filtpath, fastqRs),
                                      maxEE=c(2,3),truncQ = 0,truncLen=c(280,200), maxN = 0,  rm.phix=TRUE, compress=TRUE, verbose=TRUE))
  
  # post filtering plot
  filtFs <- sort(list.files(filtpath, pattern="R1_001.*", full.names = TRUE))
  filtRs <- sort(list.files(filtpath, pattern="R2_001.*", full.names = TRUE))
  p1 <- plotQualityProfile(filtFs, aggregate = TRUE) + ggtitle(paste0(runs[i]," Forward Reads")) 
  p2 <- plotQualityProfile(filtRs, aggregate = TRUE) + ggtitle(paste0(runs[i]," Reverse Reads"))
  
  #output plots
  dir.create("output/figures/")
  pdf(paste0("output/figures/",runs[i],"_postfilt_quality.pdf"), width = 11, height = 8 , paper="a4r")
  plot(p1+p2)
  dev.off()
  
  #Get lengths post filter
  readlengths[[i]] <- cbind(width(readFastq(file.path(filtFs))), width(readFastq(file.path(filtRs))))
}
print(filtered_out)

Sequence processing

Infer sequence variants for each run

runs <- dir("data/", pattern="run_")
set.seed(100)

for (i in seq(along=runs)){
 path <- paste0("data/",runs[i],"/trimmed/" )
  filtpath <- file.path(path, "filtered")
  
  filtFs <- list.files(filtpath, pattern="R1_001.*", full.names = TRUE)
  filtRs <- list.files(filtpath, pattern="R2_001.*", full.names = TRUE)
  
  # Learn error rates from samples
  errF <- learnErrors(filtFs, multithread=TRUE, randomize=TRUE)
  errR <- learnErrors(filtRs, multithread=TRUE, randomize=TRUE)
  
  ##Print error plots to see how well the algorithm modelled the errors in the different runs
  print(plotErrors(errF, nominalQ=TRUE)+ ggtitle(paste0(runs[i]," Forward Reads")))
  print(plotErrors(errR, nominalQ=TRUE)+ ggtitle(paste0(runs[i]," Reverse Reads")))
  
  #Error inference and merger of reads - Using pseudo pooling for increased sensitivity
  dadaFs <- dada(filtFs, err=errF, multithread=TRUE, pool="pseudo")
  dadaRs <- dada(filtRs, err=errR, multithread=TRUE, pool="pseudo")
  mergers <- mergePairs(dadaFs, filtFs, dadaRs, filtRs, verbose=TRUE, minOverlap = 20, trimOverhang = TRUE )
  
  # Construct sequence table
  seqtab <- makeSequenceTable(mergers)
 saveRDS(seqtab, paste0("output/rds/", runs[i], "_seqtab.rds"))
}

Merge Runs, Remove Chimeras

Now that the sequence tables are created for each run, they need to be merged into a larger table representing the entire study. Looking at the length of the sequences, we see some off target amplification. There are 3 large peaks, the first one at 280, second at 402bp, and third at 427. The 427bp peak contains the majority of the sequences and is the expected size, while the 402bp peak contains wolbachia which have a 24bp deletion. We will cut the sequences to between 400 and 450, which should take into account any length variation, and remove the 280bp peak (which is the length of the forward read and most likely artefactual). Following this, chimeric sequences are identified and removed using removeBimeraDenovo

set.seed(606)
seqtabs <- list.files("output/rds/", pattern="seqtab.rds", full.names = TRUE)
st.all <- mergeSequenceTables(tables=seqtabs)

#Remove chimeras
seqtab.nochim <- removeBimeraDenovo(st.all, method="consensus", multithread=TRUE, verbose=TRUE)

#Check output of chimera removal
print(paste(sum(seqtab.nochim)/sum(st.all),"of the abundance remaining after chimera removal"))

#Check complexity
hist(seqComplexity(seqtab.nochim), 100)

#Look at seqlengths
plot(table(nchar(getSequences(seqtab.nochim))))

#cut to expected size
#seqtab.nochim <- seqtab.nochim[,nchar(colnames(seqtab.nochim)) %in% 400:435]
#seqtab_cut <- seqtab.nochim[,nchar(colnames(seqtab.nochim)) %in% 400:435]
# Count hpw many this removed

print(paste(sum(seqtab.nochim)/sum(st.all),"of the abundance remaining after cutting"))

#Fix names -removing read name, sample number etcc
rownames(seqtab.nochim) <- rownames(seqtab.nochim) %>% 
  str_split_fixed("_",n=Inf) %>%
    as_tibble() %>%
  unite(col=SampleID, c("V1","V2"),sep="_") %>%
  pull(SampleID)

dir.create("output/rds/")
saveRDS(seqtab.nochim, "output/rds/seqtab_nocut.rds")

# summarise cleanup
cleanup <- st.all %>%
  as.data.frame() %>%
  pivot_longer( everything(),
    names_to = "OTU",
    values_to = "Abundance") %>%
  group_by(OTU) %>%
  summarise(Abundance = sum(Abundance)) %>%
  mutate(length  = nchar(OTU)) %>%
  mutate(type = case_when(
    !OTU %in% getSequences(seqtab.nochim) ~ "Chimera",
    TRUE ~ "Real"
  )) 

# Output length distribution plots
gg.abundance <- ggplot(cleanup, aes(x=length, y=Abundance, fill=type))+
              geom_bar(stat="identity") + 
              ggtitle("Abundance of sequences") +
              geom_vline(xintercept = 400, colour="red") +
              geom_vline(xintercept = 435, colour="red") 

gg.unique <- ggplot(cleanup, aes(x=length, fill=type))+
            geom_histogram(binwidth = 1) + 
            ggtitle("Number of unique sequences")+
              geom_vline(xintercept = 400, colour="red") +
              geom_vline(xintercept = 435, colour="red") 

plot(gg.abundance / gg.unique)

pdf(paste0("output/logs/seqtab_length_dist.pdf"), width = 11, height = 8 , paper="a4r")
  plot(gg.abundance / gg.unique)
try(dev.off(), silent=TRUE)

Assign taxonomy with IDTAXA

We will use the IDTAXA algorithm of Murali et al 2018 - https://doi.org/10.1186/s40168-018-0521-5 Folllowing phylum to genus assignment with IDTAXA, we will also use exact matching with a reference database to assign to species level.

#seqtab_final <- readRDS("output/rds/seqtab_final.rds")
seqtab_final <- readRDS("output/rds/seqtab_nocut.rds")

# Load trainingset
load("reference/SILVA_SSU_r138_2019.RData")

# Create a DNAStringSet from the ASVs
dna <- DNAStringSet(getSequences(seqtab_final)) 

# Assign taxonomy
library(DECIPHER)
ids <- IdTaxa(dna, trainingSet, processors=8, threshold = 60, verbose=TRUE)  

saveRDS(ids, "ids_nocut.rds")
# Output plot of ids
pdf(paste0("figs/idtaxa.pdf"), width = 11, height = 8 , paper="a4r")
  plot(ids)
try(dev.off(), silent=TRUE)

#Convert the output object of class "Taxa" to a matrix analogous to the output from assignTaxonomy
tax <- t(sapply(ids, function(x) {
    taxa <- paste0(x$taxon,"_", x$confidence)
    taxa[startsWith(taxa, "unclassified_")] <- NA
    taxa
  })) %>%
  purrr::map(unlist) %>%
  stringi::stri_list2matrix(byrow=TRUE, fill=NA) %>%
  magrittr::set_colnames(c("root", "domain", "phylum", "class", "order", "family", "genus")) %>%
  magrittr::set_rownames(getSequences(seqtab_final)) %>%
  as.data.frame()  %>%
  mutate_all(str_replace,pattern="(?:.(?!_))+$", replacement="") %>%
  #seqateurs::na_to_unclassified() %>% #Propagate high order ranks to unassigned ASV'
  magrittr::set_rownames(getSequences(seqtab_final)) %>%
  as.matrix()

# Write taxonomy table to disk
saveRDS(tax, "output/rds/tax_IdTaxa_nocut.rds") 

#Add species using exact matching
exact <- assignSpecies(seqtab_final, "reference/silva_species_assignment_v138.fa.gz", allowMultiple =TRUE, tryRC = TRUE, verbose = FALSE)

exact <- exact %>%
  as.data.frame(stringsAsFactors=FALSE) %>%
  rownames_to_column("otu") %>%
  mutate(species =  case_when(!is.na(Species) ~  paste0(Genus,"_",Species)))

tax_exact <- tax %>%
  as.data.frame(stringsAsFactors=FALSE) %>%
  rownames_to_column("otu") %>%
  left_join(exact %>% select(otu, species), by="otu") %>%
  magrittr::set_rownames(.$otu) %>%
  select(-otu) %>%
  na_to_unclassified() %>% #Propagate high order ranks to unassigned ASV'
  as.matrix()

saveRDS(tax_exact, "output/rds/tax_IdTaxaExact_nocut.rds") 

Compare assignment to blast top hit

seqtab_final <- readRDS("output/rds/seqtab_nocut.rds")
tax <- readRDS("output/rds/tax_IdTaxaExact_nocut.rds")

seqs <- insect::char2dna(colnames(seqtab_final))
names(seqs) <- colnames(seqtab_final)

out <- blast_top_hit(query=seqs, db="reference/silva_species_assignment_v138.fa.gz", threshold=60 )
saveRDS(out, "output/rds/blast_top_hit.rds")

# Blast counts only the aligned subsections when calculating % identity
# Therefore we need to set a minimum and maximum alignment length allowed
out <- readRDS("output/rds/blast_top_hit.rds")
hist(out$length)
allowed_lengths <- 220:500

joint <- out %>% 
  dplyr::select(OTU = qseqid, acc, blastspp = Species, pident, length, evalue) %>%
  left_join(tax %>% 
              seqateurs::unclassified_to_na(rownames=FALSE) %>%
              mutate(lowest = seqateurs::lowest_classified(.)), by="OTU") %>%
  dplyr::filter(length %in% allowed_lengths) 

#Write out comparison between BLAST and Heiarchial assignment
write_csv(joint, "output/csv/tax_assignment_comparison.csv")

gg.tophit <- joint %>%
  dplyr::select(pident, rank = lowest) %>%
  mutate(rank = factor(rank, levels = c("root", "domain", "phylum", "class", "order", "family", "genus", "species"))) %>%
  ggplot(aes(x=pident, fill=rank))+ 
  geom_histogram(colour="black", binwidth = 1, position = "stack") + 
  labs(title = "Top hit identity distribution",
       x = "BLAST top hit % identity",
       y = "OTUs") + 
  scale_x_continuous(breaks=seq(60,100,2)) +
  scale_fill_brewer(name = "Taxonomic \nAssignment", palette = "Spectral")

gg.tophit

pdf(paste0("output/figs/top_hit_tax_assignment.pdf"), width = 11, height = 8 , paper="a4r")
  gg.tophit
try(dev.off(), silent=TRUE)

Make Phyloseq object

Following taxonomic assignment, the sequence table and taxonomic table are merged into a single phyloseq object alongside the sample info csv.

# Load data
seqtab <- t(readRDS("output/rds/seqtab_nocut.rds"))
#seqtab <- readRDS("output/rds/seqtab_curated.rds")
tax <- readRDS("output/rds/tax_IdTaxaExact_nocut.rds")

seqs <- DNAStringSet(rownames(seqtab))
names(seqs) <- seqs
samdf <- read_csv("sample_data/Sample_info3.csv") %>%
  as.data.frame(stringsAsFactors=FALSE) %>%
  magrittr::set_rownames(.$SampleID) 

# Make phyloseq object
ps <- phyloseq(tax_table(tax),
               sample_data(samdf),
               otu_table(t(seqtab), taxa_are_rows = FALSE),
               refseq(seqs))

if(nrow(seqtab) > nrow(sample_data(ps))){warning("Warning: All samples not included in phyloseq object, check sample names match the sample metadata")}

#Rename taxa
#taxa_names(ps) <- paste0("SV", seq(ntaxa(ps)),"-",tax_table(ps)[,8])

##save phyloseq object
saveRDS(ps, "output/rds/ps.rds")

#Output tables of results
dir.create("output/csv")
dir.create("output/otu_tables/unfiltered/", recursive = TRUE)

##Export raw csv
speedyseq::psmelt(ps) %>%
  dplyr::filter(Abundance > 0) %>%
  write.csv(file = "output/otu_tables/rawdata.csv")

# Export species level summary
seqateurs::summarise_taxa(ps, "species", "SampleID") %>%
  spread(key="SampleID", value="totalRA") %>%
  write.csv(file = "output/otu_tables/unfiltered/spp_sum.csv")

# Export genus level summary
seqateurs::summarise_taxa(ps, "genus", "SampleID") %>%
  spread(key="SampleID", value="totalRA") %>%
  write.csv(file = "output/otu_tables/unfiltered/gen_sum.csv")

#Check carsonella presence
cars <- speedyseq::psmelt(ps) %>%
  filter(Abundance > 0) %>%
  group_by(psyllid_spp) %>%
  summarise(n = count(genus=="Candidatus Carsonella", na.rm = TRUE))

test <- speedyseq::psmelt(ps) %>%
  filter(Abundance > 0) %>%
  filter(genus=="Candidatus Carsonella")

Taxon Filtering

Remove all non-bacterial taxa, mitochondria, chloroplast and cyanobacteria. We also remove all taxa contained within the blank sample from other samples? - Check these

get_taxa_unique(ps, "domain")
ntaxa(ps) # Check the number of taxa prior to removal
ps0 <- ps %>%
  subset_taxa(
    domain == "Bacteria" & 
    family  != "Mitochondria" &
    order   != "Chloroplast" &
    phylum != "Cyanobacteria"
  )
#Check taxa were removed
ntaxa(ps0)
get_taxa_unique(ps0, "phylum")
get_taxa_unique(ps0, "class")
get_taxa_unique(ps0, "family")

Remove outlier Samples

Detecting and potentially removing samples outliers (those samples with underlying data that do not conform to experimental or biological expectations) can be useful for minimizing technical variance. This can be caused by a number of reasons, including low-reads assigned to that sample. In this case we remove all samples below 1000 reads, as these include all samples contributing to lower than usual ASV counts.

Rarefaction curves are useful to assess sensitivity of sample size to observed alpha-diversity estimates.

## Remove mocks
rm_mocks <- c("mockA_S51", "MockEven_S193", "Mock_S192", "PCRctrl_S191", "MockStaggered_S194", "PCRctrl_S191", "91_S167")

#check mocks
ps1 <- ps0 %>% 
  subset_samples(!sample_names(ps0) %in% rm_mocks) %>% #Remove mocks
  filter_taxa(function(x) mean(x) > 0, TRUE) #Drop missing taxa from table 

message((nsamples(ps0) - nsamples(ps1)), " outlier samples dropped")

#Plot rarefaction curve
out <- rarecurve(otu_table(ps1), step=100)

rare <- lapply(out, function(x){
  b <- as.data.frame(x)
  b <- data.frame(OTU = b[,1], count = rownames(b))
  b$count <- as.numeric(gsub("N", "",  b$count))
  return(b)
})
names(rare) <- sample_names(ps1)

rare <- map_dfr(rare, function(x){
  z <- data.frame(x)
  return(z)
}, .id = "sample")

# read threshold for sample removal
threshold = 1000

gg.rare <- ggplot(data = rare)+
  geom_line(aes(x = count, y = OTU, group=sample), alpha=0.5)+
  geom_point(data = rare %>% 
               group_by(sample) %>% 
               top_n(1, count),
             aes(x = count, y = OTU, colour=(count > threshold))) +
  scale_x_continuous(labels =  scales::label_number_si()) +
  geom_vline(xintercept=threshold, linetype="dashed") +
  labs(colour = "Sample kept?") +
  xlab("Sequence reads") +
  ylab("Observed ASV's")

#Write out figure
pdf(file="figs/rarefaction.pdf", width = 11, height = 8 , paper="a4r")
  plot(gg.rare)
try(dev.off(), silent=TRUE)

#Remove all samples under the minimum read threshold 
ps2 <- prune_samples(sample_sums(ps1)>=threshold, ps1) 
ps2 <- filter_taxa(ps2, function(x) mean(x) > 0, TRUE) #Drop missing taxa from table
message(nsamples(ps1) - nsamples(ps2), " Samples and ", ntaxa(ps1) - ntaxa(ps2), " taxa under read threshold Dropped")

Compare distances pre and post filtering

seqtab_final <- readRDS("output/rds/seqtab_nocut.rds")
#Get pre_filt distances
pre_filt <- as.matrix(zCompositions::cmultRepl(seqtab_final, method="BL", output="p-counts"))
pre_filt_dist <- as.matrix(vegdist(CoDaSeq::codaSeq.clr(pre_filt), method="euclidean"))

# Get post_filt distances
otutab <- otu_table(ps2) %>%
  as("matrix")

#otutab <- t(seqtab)
post_filt <- as.matrix(zCompositions::cmultRepl(otutab, method="BL", output="p-counts"))
post_filt_dist <- as.matrix(vegdist(CoDaSeq::codaSeq.clr(post_filt), method="euclidean"))

#Subset to only common samples
subsample <- intersect(colnames(pre_filt_dist), colnames(post_filt_dist))
as.data.frame(vegan::mantel(pre_filt_dist[subsample, subsample], post_filt_dist[subsample, subsample])[c("statistic","signif","permutations")])

Output fastas

#Write out fasta and align with silva online
seqs <- refseq(ps2)
writeXStringSet(seqs, "output/curated_asv.fasta", width=1000)

Align with SINA

#Create a virtual environment
module load Python/3.8.2-GCCcore-9.3.0 
virtualenv ~/SINA
source ~/SINA/bin/activate

#Install sina
wget https://github.com/epruesse/SINA/releases/download/v1.7.0/sina-1.7.0-linux.tar.gz
tar xf sina-1.7.0-linux.tar.gz
cd sina-1.7.0-linux

#Get referenceDB
wget https://www.arb-silva.de/fileadmin/silva_databases/release_138/ARB_files/SILVA_138_SSURef_NR99_05_01_20_opt.arb.gz


#Run sina
~/sina-1.7.0-linux/sina --search --add-relatives=5 --meta-fmt=header --fields=acc \
-i /group/pathogens/Alexp/Metabarcoding/Psyllid_microbiome/output/curated_asv.fasta \
-r /group/pathogens/Alexp/Metabarcoding/Psyllid_microbiome/reference/SILVA_138_SSURef_NR99_05_01_20_opt.arb \
-o /group/pathogens/Alexp/Metabarcoding/Psyllid_microbiome/output/aligned_asv.fasta

Create phylogenetic tree with fasttreee

module load FastTree
FastTree -gtr -cat 20 -quote -nt /group/pathogens/Alexp/Metabarcoding/Psyllid_microbiome/output/aligned_asv.fasta > /group/pathogens/Alexp/Metabarcoding/Psyllid_microbiome/output/sina_tree 

Date tree with PATHd8

# Date usign congruify
#tree <- read.tree("arb-silva.de_2020-07-28_id860849/arb-silva.de_2020-07-28_id860849.tree")
tree <- read.tree("output/sina_tree")
tree$tip.label <- tree$tip.label %>%
  str_split_fixed("\\[", n=Inf) %>%
  as.data.frame() %>%
  unite(V1, V2, col="name") %>%
  mutate(name = name %>%
           str_remove("_align.*$") %>%
           str_remove("^.*_acc=") %>%
           str_remove("\\]$")) %>%
  pull(name)

tree_pruned <- drop.tip(tree, tree$tip.label[duplicated(tree$tip.label)])

#Reference tree
ref_tree <- read.tree("bin/Dated trees/Bacteria_16S_SILVA_97sim_FastTree_PATHd8.tre")

ref_tree$tip.label <- ref_tree$tip.label %>% 
  str_extract("^(.*?)\\.") %>%
  str_remove("\\.$")

#2881  congruent tips
table(tree$tip.label %in% ref_tree$tip.label)

ref_tree_pruned <- drop.tip(ref_tree, c(ref_tree$tip.label[!ref_tree$tip.label %in% tree$tip.label],ref_tree$tip.label[duplicated(ref_tree$tip.label)]))

table(tree_pruned$tip.label %in% ref_tree_pruned$tip.label)

res <- congruify.phylo(reference=ref_tree_pruned, target=tree_pruned, scale="PATHd8")

tree2 <- drop.tip(res$phy, res$phy$tip.label[!res$phy$tip.label %in% names(refseq(ps2))])
write.tree(tree2, "output/phytree.nwk")

Merge technical replicates

With only 16 samples replicated, and only on the first 2 sequencing runs i dont think there is a way to explicitly take into account replicate variability, therefore all replicates were merged

This can be done with DADA2 - mergeSequenceTables(st1, st2, st3, repeats = “sum”

# Merge replicates
ps.merged <- ps2 %>%
    merge_samples(group = "Sample_Name", fun="sum")


#This loses the sample metadata - Need to add it agian
sample_data(ps.merged) <- sample_data(ps2) %>%
  as("matrix") %>%
  as.data.frame() %>%
  filter(!duplicated(Sample_Name)) %>%
  magrittr::set_rownames(.$Sample_Name)

seqs <- refseq(ps2)
tree <- read.tree("output/phytree.nwk")

#make new phyloseq object
ps2 <- phyloseq(tax_table(ps.merged),
               sample_data(ps.merged),
               otu_table(otu_table(ps.merged), taxa_are_rows = FALSE),
               refseq(seqs),
               phy_tree(tree))


saveRDS(ps2, "output/rds/ps2.rds")
LS0tDQp0aXRsZTogIlBzeWxsaWQgbWljcm9iaW9tZSINCnN1YnRpdGxlOiAiQmlvaW5mb3JtYXRpYyBzZXF1ZW5jZSBwcm9jZXNzaW5nIg0KYXV0aG9yOiAiQWxleGFuZGVyIFBpcGVyIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGluY2x1ZGVzOg0KICAgICAgYWZ0ZXJfYm9keTogZm9vdGVyLmh0bWwNCiAgICBoaWdobGlnaHRlcjogbnVsbA0KICAgIHRoZW1lOiAiZmxhdGx5Ig0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIEtuaXRyIGdsb2JhbCBzZXR1cCAtIGNoYW5nZSBldmFsIHRvIHRydWUgdG8gcnVuIGNvZGUNCmxpYnJhcnkoa25pdHIpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsZXJyb3I9RkFMU0UsZmlnLnNob3cgPSAiaG9sZCIsIGZpZy5rZWVwID0gImFsbCIpDQpvcHRzX2NodW5rJHNldChkZXYgPSAncG5nJykNCmBgYA0KDQojIEluc3RhbGwgJiBMb2FkIHBhY2thZ2VzIA0KDQpgYGB7ciBMb2FkIHBhY2thZ2VzLCBldmFsPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFfSANCiNTZXQgcmVxdWlyZWQgcGFja2FnZXMNCi5jcmFuX3BhY2thZ2VzIDwtIGMoImdncGxvdDIiLCANCiAgICAgICAgICAgICAgICAgICAgImdyaWRFeHRyYSIsDQogICAgICAgICAgICAgICAgICAgICJ0aWR5dmVyc2UiLCANCiAgICAgICAgICAgICAgICAgICAgInNjYWxlcyIsIA0KICAgICAgICAgICAgICAgICAgICAic3RyaW5nZGlzdCIsIA0KICAgICAgICAgICAgICAgICAgICAicGF0Y2h3b3JrIiwNCiAgICAgICAgICAgICAgICAgICAgInNlcWluciIsDQogICAgICAgICAgICAgICAgICAgICJ2aXJpZGlzIiwgDQogICAgICAgICAgICAgICAgICAgICJhcGUiLCANCiAgICAgICAgICAgICAgICAgICAgImRhdGEudGFibGUiLA0KICAgICAgICAgICAgICAgICAgICAiUkNvbG9yQnJld2VyIiwNCiAgICAgICAgICAgICAgICAgICAgInZlZ2FuIiwNCiAgICAgICAgICAgICAgICAgICAgImdlaWdlciIpDQouYmlvY19wYWNrYWdlcyA8LSBjKCJkYWRhMiIsDQogICAgICAgICAgICAgICAgICAgICJwaHlsb3NlcSIsIA0KICAgICAgICAgICAgICAgICAgICAiREVDSVBIRVIiLA0KICAgICAgICAgICAgICAgICAgICAiQmlvc3RyaW5ncyIsDQogICAgICAgICAgICAgICAgICAgICJTaG9ydFJlYWQiLA0KICAgICAgICAgICAgICAgICAgICAiQmlvc3RyaW5ncyIsDQogICAgICAgICAgICAgICAgICAgICJBTERFeDIiKQ0KDQouaW5zdCA8LSAuY3Jhbl9wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpDQppZihhbnkoIS5pbnN0KSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcyguY3Jhbl9wYWNrYWdlc1shLmluc3RdKQ0KfQ0KLmluc3QgPC0gLmJpb2NfcGFja2FnZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKQ0KaWYoYW55KCEuaW5zdCkpIHsNCiAgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpDQogIEJpb2NNYW5hZ2VyOjppbnN0YWxsKC5iaW9jX3BhY2thZ2VzWyEuaW5zdF0sIGFzayA9IEYpDQp9DQoNCiNMb2FkIGFsbCBwYWNrYWdlcw0Kc2FwcGx5KGMoLmNyYW5fcGFja2FnZXMsLmJpb2NfcGFja2FnZXMpLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpDQoNCiNJbnN0YWxsIGFuZCBsb2FkIGdpdGh1YiBwYWNrYWdlcw0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJhbGV4cGlwZXIvc2VxYXRldXJzIikNCmxpYnJhcnkoc2VxYXRldXJzKQ0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJ0b2JpYXNnZi9sdWx1IikNCmxpYnJhcnkobHVsdSkNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigibWlrZW1jL3NwZWVkeXNlcSIpDQpsaWJyYXJ5KHNwZWVkeXNlcSkNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YignZ2dsb29yL0NvRGFTZXEvQ29EYVNlcScpDQpsaWJyYXJ5KENvRGFTZXEpDQoNCm9wdGlvbnMoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KYGBgDQoNCiMgUXVhbGl0eSBDb250cm9sDQoNCiMjIFNwbGl0IGZpbGVzIGJ5IHNlcXJ1bg0KYGBge3IgU3BsaXQgZmlsZXN9DQojIyBSDQpzYW1kZiA8LSByZWFkLmNzdigic2FtcGxlX2RhdGEvc2FtcGxlX2luZm8uY3N2IikNCnJ1bjEgPC0gc2FtZGYgJT4lDQogIGZpbHRlcihzZXFydW49PTEpICU+JQ0KICBwdWxsKFNhbXBsZUlEKSAlPiUNCiAgYXMuY2hhcmFjdGVyKCkNCndyaXRlX2xpbmVzKHJ1bjEscGF0aD0iUnVuMS50eHQiKQ0KDQpydW4yIDwtIHNhbWRmICU+JQ0KICBmaWx0ZXIoc2VxcnVuPT0yKSAlPiUNCiAgcHVsbChTYW1wbGVJRCkgJT4lDQogIGFzLmNoYXJhY3RlcigpDQp3cml0ZV9saW5lcyhydW4yLHBhdGg9IlJ1bjIudHh0IikNCg0KcnVuMyA8LSBzYW1kZiAlPiUNCiAgZmlsdGVyKHNlcXJ1bj09MykgJT4lDQogIHB1bGwoU2FtcGxlSUQpICU+JQ0KICBhcy5jaGFyYWN0ZXIoKQ0Kd3JpdGVfbGluZXMocnVuMyxwYXRoPSJSdW4zLnR4dCIpDQpgYGANCg0KDQpgYGB7YmFzaCBzcGxpdCBmaWxlc30NCiMjIEJBU0gNCm1rZGlyIHJ1bl8xDQpjYXQgUnVuMS50eHQgfCB3aGlsZSByZWFkIGk7IGRvDQpwYXlsb2FkPSQobHMgfCBncmVwICRpKQ0KZWNobyAkcGF5bG9hZA0KbXYgJHBheWxvYWQgcnVuXzENCmRvbmUNCg0KbWtkaXIgcnVuXzINCmNhdCBSdW4yLnR4dCB8IHdoaWxlIHJlYWQgaTsgZG8NCnBheWxvYWQ9JChscyB8IGdyZXAgJGkpDQplY2hvICRwYXlsb2FkDQptdiAkcGF5bG9hZCBydW5fMg0KZG9uZQ0KDQpta2RpciBydW5fMw0KY2F0IFJ1bjMudHh0IHwgd2hpbGUgcmVhZCBpOyBkbw0KcGF5bG9hZD0kKGxzIHwgZ3JlcCAkaSkNCmVjaG8gJHBheWxvYWQNCm12ICRwYXlsb2FkIHJ1bl8zDQpkb25lDQoNCmBgYA0KDQpTZXF1ZW5jZXIgSUQgJiBmYXN0cSBzdHJ1Y3R1cmUgZm9yIGVhY2ggcnVuDQoNCiAgICBydW5fMSAtIEBNMDE4OTU6MzowMDAwMDAwMDAtQUZFRDM6MToxMTAxOjExODU2OjEwMDUgMTpOOjA6ODkNCiAgICBydW5fMiAtIEBNMDA1OTg6MTQwOjAwMDAwMDAwMC1BTFRXNjoxOjExMDQ6MTUxNzc6OTQyNSAyOk46MDoxMjANCiAgICBydW5fMyAtIEBNMDA5MzM6OTowMDAwMDAwMDAtQkZSNEI6MToxMTAxOjEzNTQyOjE3ODAgMTpOOjA6ODYNCg0KIyMgU2VxdWVuY2UgcXVhbGl0eSBjb250cm9sDQpgYGB7ciBRQ30NCmxpYnJhcnkoU2hvcnRSZWFkKQ0KcnVucyA8LSBkaXIoImRhdGEvIiwgcGF0dGVybj0icnVuXyIpDQoNCmZvciAoaSBpbiBzZXEoYWxvbmc9cnVucykpew0KcGF0aCA8LSBwYXN0ZTAoImRhdGEvIiwgcnVuc1tpXSkNCg0KI1Bsb3QgbnVtYmVyIG9mIHJlYWRzDQpkYXQgPC0gYXMuZGF0YS5mcmFtZShjb3VudExpbmVzKGRpclBhdGg9cGF0aCwgcGF0dGVybj0iLmZhc3RxIikpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oKSAgJT4lDQogICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIlJlYWRzIikpICU+JQ0KICBmaWx0ZXIoc3RyX2RldGVjdChTYW1wbGUsIlIxIikpDQoNCiNQbG90IHBvb2xpbmcNCmdnLnBvb2xpbmcgPC0gZ2dwbG90KGRhdGE9ZGF0LCBhZXMoeD1TYW1wbGUseT1SZWFkcyksc3RhdD0iaWRlbnRpdHkiKSArIA0KICBnZW9tX2JhcihhZXMoZmlsbD1SZWFkcyksc3RhdD0iaWRlbnRpdHkiKSAgKyANCiAgc2NhbGVfZmlsbF92aXJpZGlzKG5hbWUgPSAiUmVhZHMiLCBiZWdpbj0wLjEpICsgDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSwgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCBwbG90LnN1YnRpdGxlID1lbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSsgDQogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtZWFuKFJlYWRzKSkpICArDQogIHhsYWIoInNhbXBsZSBuYW1lIikrDQogIHlsYWIoIk51bWJlciBvZiByZWFkcyIpICsgDQogIGxhYnModGl0bGU9IHBhc3RlMCgiUG9vbGluZyBmb3IgOiAiLCBydW5zW2ldKSwgc3VidGl0bGUgPSBwYXN0ZTAoIlRvdGFsIFJlYWRzOiAiLCBzdW0oZGF0JFJlYWRzKSwgIiBBdmVyYWdlIHJlYWRzOiAiLCAgc3ByaW50ZigiJS4wZiIsbWVhbihkYXQkUmVhZHMpKSwiIFN0YW5kYXJkIGRldmlhdGlvbjogIiwgc3ByaW50ZigiJS4wZiIsc2QoZGF0JFJlYWRzKSkpKSArDQogIGNvb3JkX2ZsaXAoKQ0KDQpwbG90KGdnLnBvb2xpbmcpDQp9DQpgYGANCg0KIyMgVHJpbSBwcmltZXJzDQoNCkRBREEyIHJlcXVpcmVzIE5vbi1iaW9sb2dpY2FsIG51Y2xlb3RpZGVzIGkuZS4gcHJpbWVycywgYWRhcHRlcnMsIGxpbmtlcnMsIGV0YyB0byBiZSByZW1vdmVkLiBQcmlvciB0byBiZWdpbmluZyB0aGlzIHdvcmtmbG93LCBzYW1wbGVzIHdlcmUgZGVtdWx0aXBsZXhlZCBhbmQgaWxsdW1pbmEgYWRhcHRlcnMgd2VyZSByZW1vdmVkIGJ5IHRoZSBNaVNlcSBzb2Z0d2FyZSwgaG93ZXZlciBwcmltZXIgc2VxdWVuY2VzIHN0aWxsIHJlbWFpbiBpbiB0aGUgcmVhZHMgYW5kIG11c3QgYmUgcmVtb3ZlZCBwcmlvciB0byB1c2Ugd2l0aCB0aGUgREFEQTIgYWxnb3JpdGhtLg0KDQpQcmltZXIgc2VxdWVuY2VzOg0KDQogICAgMTZTIEYgQ0NUQUNHR0dOR0dDV0dDQUcNCiAgICAxNlMgUiBHQUNUQUNIVkdHR1RBVENUQUFUQ0MNCg0KQWxsIHJlYWRzIHdlcmUgdHJpbW1lZCB0byAzMDBicCwgdGhlbiBwcmltZXJzIHdlcmUgcmVtb3ZlZC4gQWxsIHJlYWRzIGZvciB3aGljaCBwcmltZXJzIHdlcmUgbm90IGRldGVjdGVkIHdlcmUgcmVtb3ZlZCB1c2luZyB0aGUgbWF4bGVuZ3RoIGZ1bmN0aW9uLg0KDQpgYGB7ciB0cmltIHByaW1lcnMsbWVzc2FnZT1GQUxTRX0NCiNJbnN0YWxsIGJibWFwDQpzZXFhdGV1cnM6OmJibWFwX2luc3RhbGwoKQ0KDQojTG9vcCBvdmVyIHJ1bnMgLSBNYXhsZW5ndGggc2V0IHRvIHJlbW92ZSBhbnkgdW50cmltbWVkIHJlYWRzDQpydW5zIDwtIGRpcigiZGF0YS8iLCBwYXR0ZXJuPSJydW5fIikNCg0KZm9yIChpIGluIHNlcShhbG9uZz1ydW5zKSl7DQogIHBhdGggPC0gcGFzdGUwKCJkYXRhLyIscnVuc1tpXSkNCiAgDQogICNUcmltIGZvcndhcmQgcHJpbWVycyAtIFNldCBhIG1heGxlbmd0aCB0byByZW1vdmUgYWxsIHRob3NlIHRoYXQgd2VyZW50IHRyaW1lbWQNCiAgZmFzdHFGcyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aCwgcGF0dGVybj0iX1IxIiwgZnVsbC5uYW1lcyA9IFRSVUUpKQ0KICBmYXN0cVJzIDwtIHNvcnQobGlzdC5maWxlcyhwYXRoLCBwYXR0ZXJuPSJfUjJfIiwgZnVsbC5uYW1lcyA9IFRSVUUpKQ0KICANCiAgYmJ0b29sc190cmltKGluc3RhbGw9ImJpbi9iYm1hcCIsIGZ3ZD1mYXN0cUZzLHJldj1mYXN0cVJzLCBwcmltZXJzPWMoIkNDVEFDR0dHTkdHQ1dHQ0FHIiwiR0FDVEFDSFZHR0dUQVRDVEFBVENDIiksIGNvcHl1bmRlZmluZWQ9VFJVRSwgb3V0cGF0aD0idHJpbW1lZCIsa3RyaW09ImwiLCBvcmRlcmVkPVRSVUUsIG1pbms9RkFMU0UsIGhkaXN0PTIsIG92ZXJ3cml0ZT1UUlVFLCBzYW1lbGVuZ3RoPVRSVUUsIGZvcmNldHJpbXJpZ2h0ID0gMzAwLCBtYXhsZW5ndGggPSAyODUpDQp9DQpgYGANCg0KIyMgUGxvdCByZWFkIHF1YWxpdHkgJiBsZW5ndGhzDQoNCmBgYHtyIFFBIHBsb3R9DQpydW5zIDwtIGRpcigiZGF0YS8iLCBwYXR0ZXJuPSJydW5fIikNCnJlYWRjb3VudHMgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoPWxlbmd0aChydW5zKSkNCg0KZm9yIChpIGluIHNlcShhbG9uZz1ydW5zKSl7DQogcGF0aCA8LSBwYXN0ZTAoImRhdGEvIixydW5zW2ldLCIvdHJpbW1lZCIgKQ0KDQogIGZpbHRGcyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aCwgcGF0dGVybj0iX1IxXyIsIGZ1bGwubmFtZXMgPSBUUlVFKSkNCiAgZmlsdFJzIDwtIHNvcnQobGlzdC5maWxlcyhwYXRoLCBwYXR0ZXJuPSJfUjJfIiwgZnVsbC5uYW1lcyA9IFRSVUUpKQ0KICBwMSA8LSBwbG90UXVhbGl0eVByb2ZpbGUoZmlsdEZzLCBhZ2dyZWdhdGUgPSBUUlVFKSArIGdndGl0bGUocGFzdGUwKHJ1bnNbaV0sIiBGb3J3YXJkIFJlYWRzIikpIA0KICBwMiA8LSBwbG90UXVhbGl0eVByb2ZpbGUoZmlsdFJzLCBhZ2dyZWdhdGUgPSBUUlVFKSArIGdndGl0bGUocGFzdGUwKHJ1bnNbaV0sIiBSZXZlcnNlIFJlYWRzIikpDQogIA0KICAjb3V0cHV0IHBsb3RzDQogIGRpci5jcmVhdGUoIm91dHB1dC9maWd1cmVzLyIpDQogIHBkZihwYXN0ZTAoIm91dHB1dC9maWd1cmVzLyIscnVuc1tpXSwiX3ByZWZpbHRfcXVhbGl0eS5wZGYiKSwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOCAsIHBhcGVyPSJhNHIiKQ0KICBwbG90KHAxK3AyKQ0KICBkZXYub2ZmKCkNCiAgDQogICNHZXQgbGVuZ3Rocw0KICByZWFkY291bnRzW1tpXV0gPC0gY2JpbmQod2lkdGgocmVhZEZhc3RxKGZpbGUucGF0aChwYXRoLCBmYXN0cUZzKSkpLCB3aWR0aChyZWFkRmFzdHEoZmlsZS5wYXRoKHBhdGgsIGZhc3RxUnMpKSkpDQp9DQpgYGANCg0KVGhlIG1heCBleHBlY3RlZCBlcnJvciBmdW5jdGlvbiBpcyB1c2VkIGFzIHRoZSBwcmltYXJ5IHF1YWxpdHkgZmlsdGVyLCBhbmQgYWxsIHJlYWRzIGNvbnRhaW5pbmcgTiBiYXNlcyB3ZXJlIHJlbW92ZWQNCg0KSW4gb3JkZXIgdG8gcmVkdWNlIHRoZSBhbW91bnQgb2YgIHJldmVyc2UgcmVhZHMgdmlvbGF0aW5nIHRoZSBNYXhFRSBmaWx0ZXIsIHRoZSByZXZlcnNlIHJlYWRzIHdlcmUgdHJ1bmNhdGVkIGF0IDIwMCB0byByZW1vdmUgdGhlIHF1YWxpdHkgY3Jhc2ggdGhhdCBpcyB0eXBpY2FsIG9mIGlsbHVtaW5hIHNlcXVlbmNlcnMNCg0KVG90YWwgYW1wbGljb24gPSA0NjVicCANClNlcXVlbmNpbmcgPSAyeDMwMGJwID0gNjAwYnANClByaW1lcnMgPSAxN2JwICsgMjFicCA9IDM4YnANClJlYWQgb3ZlcmxhcCA9IDYwMCAtIDQ2NSAtIDM4ID0gOTdicA0KDQpyZXZlcnNlIHNob3VsZCBwb3RlbnRpYWxsIGJlIHJlZHVjZWQgZnVydGhlciAtIChmcm9tIDIzMCB0byAyMDApDQoNCmBgYHtyIGZpbHRlciBhbmQgdHJpbX0NCnJ1bnMgPC0gZGlyKCJkYXRhLyIsIHBhdHRlcm49InJ1bl8iKQ0KZmlsdGVyZWRfb3V0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aD1sZW5ndGgocnVucykpDQpyZWFkbGVuZ3RocyA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGg9bGVuZ3RoKHJ1bnMpKQ0KDQpmb3IgKGkgaW4gMTpsZW5ndGgocnVucykpew0KICBwYXRoIDwtIHBhc3RlMCgiZGF0YS8iLHJ1bnNbaV0sIi90cmltbWVkLyIpIA0KICBmaWx0cGF0aCA8LSBmaWxlLnBhdGgocGF0aCwgImZpbHRlcmVkIikNCiAgZGlyLmNyZWF0ZShmaWx0cGF0aCkNCiAgZmFzdHFGcyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aCwgcGF0dGVybj0iUjFfMDAxLioiKSkNCiAgZmFzdHFScyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aCwgcGF0dGVybj0iUjJfMDAxLioiKSkNCiAgDQogIGlmKGxlbmd0aChmYXN0cUZzKSAhPSBsZW5ndGgoZmFzdHFScykpIHN0b3AocGFzdGUwKCJGb3J3YXJkIGFuZCByZXZlcnNlIGZpbGVzIGZvciAiLHJ1bnNbaV0sIiBkbyBub3QgbWF0Y2guIikpDQogIA0KICBmaWx0ZXJlZF9vdXRbW2ldXSA8LSAoZmlsdGVyQW5kVHJpbShmd2Q9ZmlsZS5wYXRoKHBhdGgsIGZhc3RxRnMpLCBmaWx0PWZpbGUucGF0aChmaWx0cGF0aCwgZmFzdHFGcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldj1maWxlLnBhdGgocGF0aCwgZmFzdHFScyksIGZpbHQucmV2PWZpbGUucGF0aChmaWx0cGF0aCwgZmFzdHFScyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heEVFPWMoMiwzKSx0cnVuY1EgPSAwLHRydW5jTGVuPWMoMjgwLDIwMCksIG1heE4gPSAwLCAgcm0ucGhpeD1UUlVFLCBjb21wcmVzcz1UUlVFLCB2ZXJib3NlPVRSVUUpKQ0KICANCiAgIyBwb3N0IGZpbHRlcmluZyBwbG90DQogIGZpbHRGcyA8LSBzb3J0KGxpc3QuZmlsZXMoZmlsdHBhdGgsIHBhdHRlcm49IlIxXzAwMS4qIiwgZnVsbC5uYW1lcyA9IFRSVUUpKQ0KICBmaWx0UnMgPC0gc29ydChsaXN0LmZpbGVzKGZpbHRwYXRoLCBwYXR0ZXJuPSJSMl8wMDEuKiIsIGZ1bGwubmFtZXMgPSBUUlVFKSkNCiAgcDEgPC0gcGxvdFF1YWxpdHlQcm9maWxlKGZpbHRGcywgYWdncmVnYXRlID0gVFJVRSkgKyBnZ3RpdGxlKHBhc3RlMChydW5zW2ldLCIgRm9yd2FyZCBSZWFkcyIpKSANCiAgcDIgPC0gcGxvdFF1YWxpdHlQcm9maWxlKGZpbHRScywgYWdncmVnYXRlID0gVFJVRSkgKyBnZ3RpdGxlKHBhc3RlMChydW5zW2ldLCIgUmV2ZXJzZSBSZWFkcyIpKQ0KICANCiAgI291dHB1dCBwbG90cw0KICBkaXIuY3JlYXRlKCJvdXRwdXQvZmlndXJlcy8iKQ0KICBwZGYocGFzdGUwKCJvdXRwdXQvZmlndXJlcy8iLHJ1bnNbaV0sIl9wb3N0ZmlsdF9xdWFsaXR5LnBkZiIpLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA4ICwgcGFwZXI9ImE0ciIpDQogIHBsb3QocDErcDIpDQogIGRldi5vZmYoKQ0KICANCiAgI0dldCBsZW5ndGhzIHBvc3QgZmlsdGVyDQogIHJlYWRsZW5ndGhzW1tpXV0gPC0gY2JpbmQod2lkdGgocmVhZEZhc3RxKGZpbGUucGF0aChmaWx0RnMpKSksIHdpZHRoKHJlYWRGYXN0cShmaWxlLnBhdGgoZmlsdFJzKSkpKQ0KfQ0KcHJpbnQoZmlsdGVyZWRfb3V0KQ0KYGBgDQoNCiMgU2VxdWVuY2UgcHJvY2Vzc2luZw0KDQojIyBJbmZlciBzZXF1ZW5jZSB2YXJpYW50cyBmb3IgZWFjaCBydW4NCg0KYGBge3IgTGVhcm4gZXJyb3IgcmF0ZXMgfQ0KcnVucyA8LSBkaXIoImRhdGEvIiwgcGF0dGVybj0icnVuXyIpDQpzZXQuc2VlZCgxMDApDQoNCmZvciAoaSBpbiBzZXEoYWxvbmc9cnVucykpew0KIHBhdGggPC0gcGFzdGUwKCJkYXRhLyIscnVuc1tpXSwiL3RyaW1tZWQvIiApDQogIGZpbHRwYXRoIDwtIGZpbGUucGF0aChwYXRoLCAiZmlsdGVyZWQiKQ0KICANCiAgZmlsdEZzIDwtIGxpc3QuZmlsZXMoZmlsdHBhdGgsIHBhdHRlcm49IlIxXzAwMS4qIiwgZnVsbC5uYW1lcyA9IFRSVUUpDQogIGZpbHRScyA8LSBsaXN0LmZpbGVzKGZpbHRwYXRoLCBwYXR0ZXJuPSJSMl8wMDEuKiIsIGZ1bGwubmFtZXMgPSBUUlVFKQ0KICANCiAgIyBMZWFybiBlcnJvciByYXRlcyBmcm9tIHNhbXBsZXMNCiAgZXJyRiA8LSBsZWFybkVycm9ycyhmaWx0RnMsIG11bHRpdGhyZWFkPVRSVUUsIHJhbmRvbWl6ZT1UUlVFKQ0KICBlcnJSIDwtIGxlYXJuRXJyb3JzKGZpbHRScywgbXVsdGl0aHJlYWQ9VFJVRSwgcmFuZG9taXplPVRSVUUpDQogIA0KICAjI1ByaW50IGVycm9yIHBsb3RzIHRvIHNlZSBob3cgd2VsbCB0aGUgYWxnb3JpdGhtIG1vZGVsbGVkIHRoZSBlcnJvcnMgaW4gdGhlIGRpZmZlcmVudCBydW5zDQogIHByaW50KHBsb3RFcnJvcnMoZXJyRiwgbm9taW5hbFE9VFJVRSkrIGdndGl0bGUocGFzdGUwKHJ1bnNbaV0sIiBGb3J3YXJkIFJlYWRzIikpKQ0KICBwcmludChwbG90RXJyb3JzKGVyclIsIG5vbWluYWxRPVRSVUUpKyBnZ3RpdGxlKHBhc3RlMChydW5zW2ldLCIgUmV2ZXJzZSBSZWFkcyIpKSkNCiAgDQogICNFcnJvciBpbmZlcmVuY2UgYW5kIG1lcmdlciBvZiByZWFkcyAtIFVzaW5nIHBzZXVkbyBwb29saW5nIGZvciBpbmNyZWFzZWQgc2Vuc2l0aXZpdHkNCiAgZGFkYUZzIDwtIGRhZGEoZmlsdEZzLCBlcnI9ZXJyRiwgbXVsdGl0aHJlYWQ9VFJVRSwgcG9vbD0icHNldWRvIikNCiAgZGFkYVJzIDwtIGRhZGEoZmlsdFJzLCBlcnI9ZXJyUiwgbXVsdGl0aHJlYWQ9VFJVRSwgcG9vbD0icHNldWRvIikNCiAgbWVyZ2VycyA8LSBtZXJnZVBhaXJzKGRhZGFGcywgZmlsdEZzLCBkYWRhUnMsIGZpbHRScywgdmVyYm9zZT1UUlVFLCBtaW5PdmVybGFwID0gMjAsIHRyaW1PdmVyaGFuZyA9IFRSVUUgKQ0KICANCiAgIyBDb25zdHJ1Y3Qgc2VxdWVuY2UgdGFibGUNCiAgc2VxdGFiIDwtIG1ha2VTZXF1ZW5jZVRhYmxlKG1lcmdlcnMpDQogc2F2ZVJEUyhzZXF0YWIsIHBhc3RlMCgib3V0cHV0L3Jkcy8iLCBydW5zW2ldLCAiX3NlcXRhYi5yZHMiKSkNCn0NCmBgYA0KDQojIyBNZXJnZSBSdW5zLCBSZW1vdmUgQ2hpbWVyYXMNCg0KTm93IHRoYXQgdGhlIHNlcXVlbmNlIHRhYmxlcyBhcmUgY3JlYXRlZCBmb3IgZWFjaCBydW4sIHRoZXkgbmVlZCB0byBiZSBtZXJnZWQgaW50byBhIGxhcmdlciB0YWJsZSByZXByZXNlbnRpbmcgdGhlIGVudGlyZSBzdHVkeS4gDQpMb29raW5nIGF0IHRoZSBsZW5ndGggb2YgdGhlIHNlcXVlbmNlcywgd2Ugc2VlIHNvbWUgb2ZmIHRhcmdldCBhbXBsaWZpY2F0aW9uLiBUaGVyZSBhcmUgMyBsYXJnZSBwZWFrcywgdGhlIGZpcnN0IG9uZSBhdCAyODAsIHNlY29uZCBhdCA0MDJicCwgYW5kIHRoaXJkIGF0IDQyNy4gVGhlIDQyN2JwIHBlYWsgY29udGFpbnMgdGhlIG1ham9yaXR5IG9mIHRoZSBzZXF1ZW5jZXMgYW5kIGlzIHRoZSBleHBlY3RlZCBzaXplLCB3aGlsZSB0aGUgNDAyYnAgcGVhayBjb250YWlucyB3b2xiYWNoaWEgd2hpY2ggaGF2ZSBhIDI0YnAgZGVsZXRpb24uIFdlIHdpbGwgY3V0IHRoZSBzZXF1ZW5jZXMgdG8gYmV0d2VlbiA0MDAgYW5kIDQ1MCwgd2hpY2ggc2hvdWxkIHRha2UgaW50byBhY2NvdW50IGFueSBsZW5ndGggdmFyaWF0aW9uLCBhbmQgcmVtb3ZlIHRoZSAyODBicCBwZWFrICh3aGljaCBpcyB0aGUgbGVuZ3RoIG9mIHRoZSBmb3J3YXJkIHJlYWQgYW5kIG1vc3QgbGlrZWx5IGFydGVmYWN0dWFsKS4gRm9sbG93aW5nIHRoaXMsIGNoaW1lcmljIHNlcXVlbmNlcyBhcmUgaWRlbnRpZmllZCBhbmQgcmVtb3ZlZCB1c2luZyByZW1vdmVCaW1lcmFEZW5vdm8NCg0KYGBge3IgbWVyZ2UgcnVucyBhbmQgcmVtb3ZlIGNoaW1lcmFzfQ0Kc2V0LnNlZWQoNjA2KQ0Kc2VxdGFicyA8LSBsaXN0LmZpbGVzKCJvdXRwdXQvcmRzLyIsIHBhdHRlcm49InNlcXRhYi5yZHMiLCBmdWxsLm5hbWVzID0gVFJVRSkNCnN0LmFsbCA8LSBtZXJnZVNlcXVlbmNlVGFibGVzKHRhYmxlcz1zZXF0YWJzKQ0KDQojUmVtb3ZlIGNoaW1lcmFzDQpzZXF0YWIubm9jaGltIDwtIHJlbW92ZUJpbWVyYURlbm92byhzdC5hbGwsIG1ldGhvZD0iY29uc2Vuc3VzIiwgbXVsdGl0aHJlYWQ9VFJVRSwgdmVyYm9zZT1UUlVFKQ0KDQojQ2hlY2sgb3V0cHV0IG9mIGNoaW1lcmEgcmVtb3ZhbA0KcHJpbnQocGFzdGUoc3VtKHNlcXRhYi5ub2NoaW0pL3N1bShzdC5hbGwpLCJvZiB0aGUgYWJ1bmRhbmNlIHJlbWFpbmluZyBhZnRlciBjaGltZXJhIHJlbW92YWwiKSkNCg0KI0NoZWNrIGNvbXBsZXhpdHkNCmhpc3Qoc2VxQ29tcGxleGl0eShzZXF0YWIubm9jaGltKSwgMTAwKQ0KDQojTG9vayBhdCBzZXFsZW5ndGhzDQpwbG90KHRhYmxlKG5jaGFyKGdldFNlcXVlbmNlcyhzZXF0YWIubm9jaGltKSkpKQ0KDQojY3V0IHRvIGV4cGVjdGVkIHNpemUNCiNzZXF0YWIubm9jaGltIDwtIHNlcXRhYi5ub2NoaW1bLG5jaGFyKGNvbG5hbWVzKHNlcXRhYi5ub2NoaW0pKSAlaW4lIDQwMDo0MzVdDQojc2VxdGFiX2N1dCA8LSBzZXF0YWIubm9jaGltWyxuY2hhcihjb2xuYW1lcyhzZXF0YWIubm9jaGltKSkgJWluJSA0MDA6NDM1XQ0KIyBDb3VudCBocHcgbWFueSB0aGlzIHJlbW92ZWQNCg0KcHJpbnQocGFzdGUoc3VtKHNlcXRhYi5ub2NoaW0pL3N1bShzdC5hbGwpLCJvZiB0aGUgYWJ1bmRhbmNlIHJlbWFpbmluZyBhZnRlciBjdXR0aW5nIikpDQoNCiNGaXggbmFtZXMgLXJlbW92aW5nIHJlYWQgbmFtZSwgc2FtcGxlIG51bWJlciBldGNjDQpyb3duYW1lcyhzZXF0YWIubm9jaGltKSA8LSByb3duYW1lcyhzZXF0YWIubm9jaGltKSAlPiUgDQogIHN0cl9zcGxpdF9maXhlZCgiXyIsbj1JbmYpICU+JQ0KICAgIGFzX3RpYmJsZSgpICU+JQ0KICB1bml0ZShjb2w9U2FtcGxlSUQsIGMoIlYxIiwiVjIiKSxzZXA9Il8iKSAlPiUNCiAgcHVsbChTYW1wbGVJRCkNCg0KZGlyLmNyZWF0ZSgib3V0cHV0L3Jkcy8iKQ0Kc2F2ZVJEUyhzZXF0YWIubm9jaGltLCAib3V0cHV0L3Jkcy9zZXF0YWJfbm9jdXQucmRzIikNCg0KIyBzdW1tYXJpc2UgY2xlYW51cA0KY2xlYW51cCA8LSBzdC5hbGwgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKCBldmVyeXRoaW5nKCksDQogICAgbmFtZXNfdG8gPSAiT1RVIiwNCiAgICB2YWx1ZXNfdG8gPSAiQWJ1bmRhbmNlIikgJT4lDQogIGdyb3VwX2J5KE9UVSkgJT4lDQogIHN1bW1hcmlzZShBYnVuZGFuY2UgPSBzdW0oQWJ1bmRhbmNlKSkgJT4lDQogIG11dGF0ZShsZW5ndGggID0gbmNoYXIoT1RVKSkgJT4lDQogIG11dGF0ZSh0eXBlID0gY2FzZV93aGVuKA0KICAgICFPVFUgJWluJSBnZXRTZXF1ZW5jZXMoc2VxdGFiLm5vY2hpbSkgfiAiQ2hpbWVyYSIsDQogICAgVFJVRSB+ICJSZWFsIg0KICApKSANCg0KIyBPdXRwdXQgbGVuZ3RoIGRpc3RyaWJ1dGlvbiBwbG90cw0KZ2cuYWJ1bmRhbmNlIDwtIGdncGxvdChjbGVhbnVwLCBhZXMoeD1sZW5ndGgsIHk9QWJ1bmRhbmNlLCBmaWxsPXR5cGUpKSsNCiAgICAgICAgICAgICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArIA0KICAgICAgICAgICAgICBnZ3RpdGxlKCJBYnVuZGFuY2Ugb2Ygc2VxdWVuY2VzIikgKw0KICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0MDAsIGNvbG91cj0icmVkIikgKw0KICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0MzUsIGNvbG91cj0icmVkIikgDQoNCmdnLnVuaXF1ZSA8LSBnZ3Bsb3QoY2xlYW51cCwgYWVzKHg9bGVuZ3RoLCBmaWxsPXR5cGUpKSsNCiAgICAgICAgICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKyANCiAgICAgICAgICAgIGdndGl0bGUoIk51bWJlciBvZiB1bmlxdWUgc2VxdWVuY2VzIikrDQogICAgICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQwMCwgY29sb3VyPSJyZWQiKSArDQogICAgICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQzNSwgY29sb3VyPSJyZWQiKSANCg0KcGxvdChnZy5hYnVuZGFuY2UgLyBnZy51bmlxdWUpDQoNCnBkZihwYXN0ZTAoIm91dHB1dC9sb2dzL3NlcXRhYl9sZW5ndGhfZGlzdC5wZGYiKSwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOCAsIHBhcGVyPSJhNHIiKQ0KICBwbG90KGdnLmFidW5kYW5jZSAvIGdnLnVuaXF1ZSkNCnRyeShkZXYub2ZmKCksIHNpbGVudD1UUlVFKQ0KYGBgDQoNCiMgQXNzaWduIHRheG9ub215IHdpdGggSURUQVhBDQoNCldlIHdpbGwgdXNlIHRoZSBJRFRBWEEgYWxnb3JpdGhtIG9mIE11cmFsaSBldCBhbCAyMDE4IC0gaHR0cHM6Ly9kb2kub3JnLzEwLjExODYvczQwMTY4LTAxOC0wNTIxLTUgRm9sbGxvd2luZyBwaHlsdW0gdG8gZ2VudXMgYXNzaWdubWVudCB3aXRoIElEVEFYQSwgd2Ugd2lsbCBhbHNvIHVzZSBleGFjdCBtYXRjaGluZyB3aXRoIGEgcmVmZXJlbmNlIGRhdGFiYXNlIHRvIGFzc2lnbiB0byBzcGVjaWVzIGxldmVsLg0KDQpgYGB7ciBJRFRBWEF9DQojc2VxdGFiX2ZpbmFsIDwtIHJlYWRSRFMoIm91dHB1dC9yZHMvc2VxdGFiX2ZpbmFsLnJkcyIpDQpzZXF0YWJfZmluYWwgPC0gcmVhZFJEUygib3V0cHV0L3Jkcy9zZXF0YWJfbm9jdXQucmRzIikNCg0KIyBMb2FkIHRyYWluaW5nc2V0DQpsb2FkKCJyZWZlcmVuY2UvU0lMVkFfU1NVX3IxMzhfMjAxOS5SRGF0YSIpDQoNCiMgQ3JlYXRlIGEgRE5BU3RyaW5nU2V0IGZyb20gdGhlIEFTVnMNCmRuYSA8LSBETkFTdHJpbmdTZXQoZ2V0U2VxdWVuY2VzKHNlcXRhYl9maW5hbCkpIA0KDQojIEFzc2lnbiB0YXhvbm9teQ0KbGlicmFyeShERUNJUEhFUikNCmlkcyA8LSBJZFRheGEoZG5hLCB0cmFpbmluZ1NldCwgcHJvY2Vzc29ycz04LCB0aHJlc2hvbGQgPSA2MCwgdmVyYm9zZT1UUlVFKSAgDQoNCnNhdmVSRFMoaWRzLCAiaWRzX25vY3V0LnJkcyIpDQojIE91dHB1dCBwbG90IG9mIGlkcw0KcGRmKHBhc3RlMCgiZmlncy9pZHRheGEucGRmIiksIHdpZHRoID0gMTEsIGhlaWdodCA9IDggLCBwYXBlcj0iYTRyIikNCiAgcGxvdChpZHMpDQp0cnkoZGV2Lm9mZigpLCBzaWxlbnQ9VFJVRSkNCg0KI0NvbnZlcnQgdGhlIG91dHB1dCBvYmplY3Qgb2YgY2xhc3MgIlRheGEiIHRvIGEgbWF0cml4IGFuYWxvZ291cyB0byB0aGUgb3V0cHV0IGZyb20gYXNzaWduVGF4b25vbXkNCnRheCA8LSB0KHNhcHBseShpZHMsIGZ1bmN0aW9uKHgpIHsNCiAgICB0YXhhIDwtIHBhc3RlMCh4JHRheG9uLCJfIiwgeCRjb25maWRlbmNlKQ0KICAgIHRheGFbc3RhcnRzV2l0aCh0YXhhLCAidW5jbGFzc2lmaWVkXyIpXSA8LSBOQQ0KICAgIHRheGENCiAgfSkpICU+JQ0KICBwdXJycjo6bWFwKHVubGlzdCkgJT4lDQogIHN0cmluZ2k6OnN0cmlfbGlzdDJtYXRyaXgoYnlyb3c9VFJVRSwgZmlsbD1OQSkgJT4lDQogIG1hZ3JpdHRyOjpzZXRfY29sbmFtZXMoYygicm9vdCIsICJkb21haW4iLCAicGh5bHVtIiwgImNsYXNzIiwgIm9yZGVyIiwgImZhbWlseSIsICJnZW51cyIpKSAlPiUNCiAgbWFncml0dHI6OnNldF9yb3duYW1lcyhnZXRTZXF1ZW5jZXMoc2VxdGFiX2ZpbmFsKSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAgJT4lDQogIG11dGF0ZV9hbGwoc3RyX3JlcGxhY2UscGF0dGVybj0iKD86Lig/IV8pKSskIiwgcmVwbGFjZW1lbnQ9IiIpICU+JQ0KICAjc2VxYXRldXJzOjpuYV90b191bmNsYXNzaWZpZWQoKSAlPiUgI1Byb3BhZ2F0ZSBoaWdoIG9yZGVyIHJhbmtzIHRvIHVuYXNzaWduZWQgQVNWJw0KICBtYWdyaXR0cjo6c2V0X3Jvd25hbWVzKGdldFNlcXVlbmNlcyhzZXF0YWJfZmluYWwpKSAlPiUNCiAgYXMubWF0cml4KCkNCg0KIyBXcml0ZSB0YXhvbm9teSB0YWJsZSB0byBkaXNrDQpzYXZlUkRTKHRheCwgIm91dHB1dC9yZHMvdGF4X0lkVGF4YV9ub2N1dC5yZHMiKSANCg0KI0FkZCBzcGVjaWVzIHVzaW5nIGV4YWN0IG1hdGNoaW5nDQpleGFjdCA8LSBhc3NpZ25TcGVjaWVzKHNlcXRhYl9maW5hbCwgInJlZmVyZW5jZS9zaWx2YV9zcGVjaWVzX2Fzc2lnbm1lbnRfdjEzOC5mYS5neiIsIGFsbG93TXVsdGlwbGUgPVRSVUUsIHRyeVJDID0gVFJVRSwgdmVyYm9zZSA9IEZBTFNFKQ0KDQpleGFjdCA8LSBleGFjdCAlPiUNCiAgYXMuZGF0YS5mcmFtZShzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJvdHUiKSAlPiUNCiAgbXV0YXRlKHNwZWNpZXMgPSAgY2FzZV93aGVuKCFpcy5uYShTcGVjaWVzKSB+ICBwYXN0ZTAoR2VudXMsIl8iLFNwZWNpZXMpKSkNCg0KdGF4X2V4YWN0IDwtIHRheCAlPiUNCiAgYXMuZGF0YS5mcmFtZShzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJvdHUiKSAlPiUNCiAgbGVmdF9qb2luKGV4YWN0ICU+JSBzZWxlY3Qob3R1LCBzcGVjaWVzKSwgYnk9Im90dSIpICU+JQ0KICBtYWdyaXR0cjo6c2V0X3Jvd25hbWVzKC4kb3R1KSAlPiUNCiAgc2VsZWN0KC1vdHUpICU+JQ0KICBuYV90b191bmNsYXNzaWZpZWQoKSAlPiUgI1Byb3BhZ2F0ZSBoaWdoIG9yZGVyIHJhbmtzIHRvIHVuYXNzaWduZWQgQVNWJw0KICBhcy5tYXRyaXgoKQ0KDQpzYXZlUkRTKHRheF9leGFjdCwgIm91dHB1dC9yZHMvdGF4X0lkVGF4YUV4YWN0X25vY3V0LnJkcyIpIA0KYGBgDQoNCg0KIyBDb21wYXJlIGFzc2lnbm1lbnQgdG8gYmxhc3QgdG9wIGhpdA0KYGBge3IgQ29tcGFyZSB0byBibGFzdCB0b3AgaGl0fQ0Kc2VxdGFiX2ZpbmFsIDwtIHJlYWRSRFMoIm91dHB1dC9yZHMvc2VxdGFiX25vY3V0LnJkcyIpDQp0YXggPC0gcmVhZFJEUygib3V0cHV0L3Jkcy90YXhfSWRUYXhhRXhhY3Rfbm9jdXQucmRzIikNCg0Kc2VxcyA8LSBpbnNlY3Q6OmNoYXIyZG5hKGNvbG5hbWVzKHNlcXRhYl9maW5hbCkpDQpuYW1lcyhzZXFzKSA8LSBjb2xuYW1lcyhzZXF0YWJfZmluYWwpDQoNCm91dCA8LSBibGFzdF90b3BfaGl0KHF1ZXJ5PXNlcXMsIGRiPSJyZWZlcmVuY2Uvc2lsdmFfc3BlY2llc19hc3NpZ25tZW50X3YxMzguZmEuZ3oiLCB0aHJlc2hvbGQ9NjAgKQ0Kc2F2ZVJEUyhvdXQsICJvdXRwdXQvcmRzL2JsYXN0X3RvcF9oaXQucmRzIikNCg0KIyBCbGFzdCBjb3VudHMgb25seSB0aGUgYWxpZ25lZCBzdWJzZWN0aW9ucyB3aGVuIGNhbGN1bGF0aW5nICUgaWRlbnRpdHkNCiMgVGhlcmVmb3JlIHdlIG5lZWQgdG8gc2V0IGEgbWluaW11bSBhbmQgbWF4aW11bSBhbGlnbm1lbnQgbGVuZ3RoIGFsbG93ZWQNCm91dCA8LSByZWFkUkRTKCJvdXRwdXQvcmRzL2JsYXN0X3RvcF9oaXQucmRzIikNCmhpc3Qob3V0JGxlbmd0aCkNCmFsbG93ZWRfbGVuZ3RocyA8LSAyMjA6NTAwDQoNCmpvaW50IDwtIG91dCAlPiUgDQogIGRwbHlyOjpzZWxlY3QoT1RVID0gcXNlcWlkLCBhY2MsIGJsYXN0c3BwID0gU3BlY2llcywgcGlkZW50LCBsZW5ndGgsIGV2YWx1ZSkgJT4lDQogIGxlZnRfam9pbih0YXggJT4lIA0KICAgICAgICAgICAgICBzZXFhdGV1cnM6OnVuY2xhc3NpZmllZF90b19uYShyb3duYW1lcz1GQUxTRSkgJT4lDQogICAgICAgICAgICAgIG11dGF0ZShsb3dlc3QgPSBzZXFhdGV1cnM6Omxvd2VzdF9jbGFzc2lmaWVkKC4pKSwgYnk9Ik9UVSIpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKGxlbmd0aCAlaW4lIGFsbG93ZWRfbGVuZ3RocykgDQoNCiNXcml0ZSBvdXQgY29tcGFyaXNvbiBiZXR3ZWVuIEJMQVNUIGFuZCBIZWlhcmNoaWFsIGFzc2lnbm1lbnQNCndyaXRlX2Nzdihqb2ludCwgIm91dHB1dC9jc3YvdGF4X2Fzc2lnbm1lbnRfY29tcGFyaXNvbi5jc3YiKQ0KDQpnZy50b3BoaXQgPC0gam9pbnQgJT4lDQogIGRwbHlyOjpzZWxlY3QocGlkZW50LCByYW5rID0gbG93ZXN0KSAlPiUNCiAgbXV0YXRlKHJhbmsgPSBmYWN0b3IocmFuaywgbGV2ZWxzID0gYygicm9vdCIsICJkb21haW4iLCAicGh5bHVtIiwgImNsYXNzIiwgIm9yZGVyIiwgImZhbWlseSIsICJnZW51cyIsICJzcGVjaWVzIikpKSAlPiUNCiAgZ2dwbG90KGFlcyh4PXBpZGVudCwgZmlsbD1yYW5rKSkrIA0KICBnZW9tX2hpc3RvZ3JhbShjb2xvdXI9ImJsYWNrIiwgYmlud2lkdGggPSAxLCBwb3NpdGlvbiA9ICJzdGFjayIpICsgDQogIGxhYnModGl0bGUgPSAiVG9wIGhpdCBpZGVudGl0eSBkaXN0cmlidXRpb24iLA0KICAgICAgIHggPSAiQkxBU1QgdG9wIGhpdCAlIGlkZW50aXR5IiwNCiAgICAgICB5ID0gIk9UVXMiKSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSg2MCwxMDAsMikpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIobmFtZSA9ICJUYXhvbm9taWMgXG5Bc3NpZ25tZW50IiwgcGFsZXR0ZSA9ICJTcGVjdHJhbCIpDQoNCmdnLnRvcGhpdA0KDQpwZGYocGFzdGUwKCJvdXRwdXQvZmlncy90b3BfaGl0X3RheF9hc3NpZ25tZW50LnBkZiIpLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA4ICwgcGFwZXI9ImE0ciIpDQogIGdnLnRvcGhpdA0KdHJ5KGRldi5vZmYoKSwgc2lsZW50PVRSVUUpDQoNCmBgYA0KDQojIE1ha2UgUGh5bG9zZXEgb2JqZWN0DQoNCkZvbGxvd2luZyB0YXhvbm9taWMgYXNzaWdubWVudCwgdGhlIHNlcXVlbmNlIHRhYmxlIGFuZCB0YXhvbm9taWMgdGFibGUgYXJlIG1lcmdlZCBpbnRvIGEgc2luZ2xlIHBoeWxvc2VxIG9iamVjdCBhbG9uZ3NpZGUgdGhlIHNhbXBsZSBpbmZvIGNzdi4NCg0KYGBge3IgY3JlYXRlIFBTLCBldmFsID0gRkFMU0V9DQojIExvYWQgZGF0YQ0Kc2VxdGFiIDwtIHQocmVhZFJEUygib3V0cHV0L3Jkcy9zZXF0YWJfbm9jdXQucmRzIikpDQojc2VxdGFiIDwtIHJlYWRSRFMoIm91dHB1dC9yZHMvc2VxdGFiX2N1cmF0ZWQucmRzIikNCnRheCA8LSByZWFkUkRTKCJvdXRwdXQvcmRzL3RheF9JZFRheGFFeGFjdF9ub2N1dC5yZHMiKQ0KDQpzZXFzIDwtIEROQVN0cmluZ1NldChyb3duYW1lcyhzZXF0YWIpKQ0KbmFtZXMoc2VxcykgPC0gc2Vxcw0Kc2FtZGYgPC0gcmVhZF9jc3YoInNhbXBsZV9kYXRhL1NhbXBsZV9pbmZvMy5jc3YiKSAlPiUNCiAgYXMuZGF0YS5mcmFtZShzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSAlPiUNCiAgbWFncml0dHI6OnNldF9yb3duYW1lcyguJFNhbXBsZUlEKSANCg0KIyBNYWtlIHBoeWxvc2VxIG9iamVjdA0KcHMgPC0gcGh5bG9zZXEodGF4X3RhYmxlKHRheCksDQogICAgICAgICAgICAgICBzYW1wbGVfZGF0YShzYW1kZiksDQogICAgICAgICAgICAgICBvdHVfdGFibGUodChzZXF0YWIpLCB0YXhhX2FyZV9yb3dzID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgcmVmc2VxKHNlcXMpKQ0KDQppZihucm93KHNlcXRhYikgPiBucm93KHNhbXBsZV9kYXRhKHBzKSkpe3dhcm5pbmcoIldhcm5pbmc6IEFsbCBzYW1wbGVzIG5vdCBpbmNsdWRlZCBpbiBwaHlsb3NlcSBvYmplY3QsIGNoZWNrIHNhbXBsZSBuYW1lcyBtYXRjaCB0aGUgc2FtcGxlIG1ldGFkYXRhIil9DQoNCiNSZW5hbWUgdGF4YQ0KI3RheGFfbmFtZXMocHMpIDwtIHBhc3RlMCgiU1YiLCBzZXEobnRheGEocHMpKSwiLSIsdGF4X3RhYmxlKHBzKVssOF0pDQoNCiMjc2F2ZSBwaHlsb3NlcSBvYmplY3QNCnNhdmVSRFMocHMsICJvdXRwdXQvcmRzL3BzLnJkcyIpDQoNCiNPdXRwdXQgdGFibGVzIG9mIHJlc3VsdHMNCmRpci5jcmVhdGUoIm91dHB1dC9jc3YiKQ0KZGlyLmNyZWF0ZSgib3V0cHV0L290dV90YWJsZXMvdW5maWx0ZXJlZC8iLCByZWN1cnNpdmUgPSBUUlVFKQ0KDQojI0V4cG9ydCByYXcgY3N2DQpzcGVlZHlzZXE6OnBzbWVsdChwcykgJT4lDQogIGRwbHlyOjpmaWx0ZXIoQWJ1bmRhbmNlID4gMCkgJT4lDQogIHdyaXRlLmNzdihmaWxlID0gIm91dHB1dC9vdHVfdGFibGVzL3Jhd2RhdGEuY3N2IikNCg0KIyBFeHBvcnQgc3BlY2llcyBsZXZlbCBzdW1tYXJ5DQpzZXFhdGV1cnM6OnN1bW1hcmlzZV90YXhhKHBzLCAic3BlY2llcyIsICJTYW1wbGVJRCIpICU+JQ0KICBzcHJlYWQoa2V5PSJTYW1wbGVJRCIsIHZhbHVlPSJ0b3RhbFJBIikgJT4lDQogIHdyaXRlLmNzdihmaWxlID0gIm91dHB1dC9vdHVfdGFibGVzL3VuZmlsdGVyZWQvc3BwX3N1bS5jc3YiKQ0KDQojIEV4cG9ydCBnZW51cyBsZXZlbCBzdW1tYXJ5DQpzZXFhdGV1cnM6OnN1bW1hcmlzZV90YXhhKHBzLCAiZ2VudXMiLCAiU2FtcGxlSUQiKSAlPiUNCiAgc3ByZWFkKGtleT0iU2FtcGxlSUQiLCB2YWx1ZT0idG90YWxSQSIpICU+JQ0KICB3cml0ZS5jc3YoZmlsZSA9ICJvdXRwdXQvb3R1X3RhYmxlcy91bmZpbHRlcmVkL2dlbl9zdW0uY3N2IikNCg0KI0NoZWNrIGNhcnNvbmVsbGEgcHJlc2VuY2UNCmNhcnMgPC0gc3BlZWR5c2VxOjpwc21lbHQocHMpICU+JQ0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMCkgJT4lDQogIGdyb3VwX2J5KHBzeWxsaWRfc3BwKSAlPiUNCiAgc3VtbWFyaXNlKG4gPSBjb3VudChnZW51cz09IkNhbmRpZGF0dXMgQ2Fyc29uZWxsYSIsIG5hLnJtID0gVFJVRSkpDQoNCnRlc3QgPC0gc3BlZWR5c2VxOjpwc21lbHQocHMpICU+JQ0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMCkgJT4lDQogIGZpbHRlcihnZW51cz09IkNhbmRpZGF0dXMgQ2Fyc29uZWxsYSIpDQpgYGANCg0KDQojIyBUYXhvbiBGaWx0ZXJpbmcNCg0KUmVtb3ZlIGFsbCBub24tYmFjdGVyaWFsIHRheGEsIG1pdG9jaG9uZHJpYSwgY2hsb3JvcGxhc3QgYW5kIGN5YW5vYmFjdGVyaWEuDQpXZSBhbHNvIHJlbW92ZSBhbGwgdGF4YSBjb250YWluZWQgd2l0aGluIHRoZSBibGFuayBzYW1wbGUgZnJvbSBvdGhlciBzYW1wbGVzPyAtIENoZWNrIHRoZXNlDQoNCmBgYHtyIHRheG9uLWNsZWFuaW5nfQ0KZ2V0X3RheGFfdW5pcXVlKHBzLCAiZG9tYWluIikNCm50YXhhKHBzKSAjIENoZWNrIHRoZSBudW1iZXIgb2YgdGF4YSBwcmlvciB0byByZW1vdmFsDQpwczAgPC0gcHMgJT4lDQogIHN1YnNldF90YXhhKA0KICAgIGRvbWFpbiA9PSAiQmFjdGVyaWEiICYgDQogICAgZmFtaWx5ICAhPSAiTWl0b2Nob25kcmlhIiAmDQogICAgb3JkZXIgICAhPSAiQ2hsb3JvcGxhc3QiICYNCiAgICBwaHlsdW0gIT0gIkN5YW5vYmFjdGVyaWEiDQogICkNCiNDaGVjayB0YXhhIHdlcmUgcmVtb3ZlZA0KbnRheGEocHMwKQ0KZ2V0X3RheGFfdW5pcXVlKHBzMCwgInBoeWx1bSIpDQpnZXRfdGF4YV91bmlxdWUocHMwLCAiY2xhc3MiKQ0KZ2V0X3RheGFfdW5pcXVlKHBzMCwgImZhbWlseSIpDQpgYGANCg0KIyMgUmVtb3ZlIG91dGxpZXIgU2FtcGxlcw0KDQpEZXRlY3RpbmcgYW5kIHBvdGVudGlhbGx5IHJlbW92aW5nIHNhbXBsZXMgb3V0bGllcnMgKHRob3NlIHNhbXBsZXMgd2l0aCB1bmRlcmx5aW5nIGRhdGEgdGhhdCBkbyBub3QgY29uZm9ybSB0byBleHBlcmltZW50YWwgb3IgYmlvbG9naWNhbCBleHBlY3RhdGlvbnMpIGNhbiBiZSB1c2VmdWwgZm9yIG1pbmltaXppbmcgdGVjaG5pY2FsIHZhcmlhbmNlLiBUaGlzIGNhbiBiZSBjYXVzZWQgYnkgYSBudW1iZXIgb2YgcmVhc29ucywgaW5jbHVkaW5nIGxvdy1yZWFkcyBhc3NpZ25lZCB0byB0aGF0IHNhbXBsZS4gSW4gdGhpcyBjYXNlIHdlIHJlbW92ZSBhbGwgc2FtcGxlcyBiZWxvdyAxMDAwIHJlYWRzLCBhcyB0aGVzZSBpbmNsdWRlIGFsbCBzYW1wbGVzIGNvbnRyaWJ1dGluZyB0byBsb3dlciB0aGFuIHVzdWFsIEFTViBjb3VudHMuDQoNClJhcmVmYWN0aW9uIGN1cnZlcyBhcmUgdXNlZnVsCXRvCWFzc2VzcwlzZW5zaXRpdml0eQlvZglzYW1wbGUJc2l6ZQl0bwlvYnNlcnZlZAlhbHBoYS1kaXZlcnNpdHkgZXN0aW1hdGVzLg0KYGBge3IgcmVtb3ZlIG91dGxpZXJzfQ0KIyMgUmVtb3ZlIG1vY2tzDQpybV9tb2NrcyA8LSBjKCJtb2NrQV9TNTEiLCAiTW9ja0V2ZW5fUzE5MyIsICJNb2NrX1MxOTIiLCAiUENSY3RybF9TMTkxIiwgIk1vY2tTdGFnZ2VyZWRfUzE5NCIsICJQQ1JjdHJsX1MxOTEiLCAiOTFfUzE2NyIpDQoNCiNjaGVjayBtb2Nrcw0KcHMxIDwtIHBzMCAlPiUgDQogIHN1YnNldF9zYW1wbGVzKCFzYW1wbGVfbmFtZXMocHMwKSAlaW4lIHJtX21vY2tzKSAlPiUgI1JlbW92ZSBtb2Nrcw0KICBmaWx0ZXJfdGF4YShmdW5jdGlvbih4KSBtZWFuKHgpID4gMCwgVFJVRSkgI0Ryb3AgbWlzc2luZyB0YXhhIGZyb20gdGFibGUgDQoNCm1lc3NhZ2UoKG5zYW1wbGVzKHBzMCkgLSBuc2FtcGxlcyhwczEpKSwgIiBvdXRsaWVyIHNhbXBsZXMgZHJvcHBlZCIpDQoNCiNQbG90IHJhcmVmYWN0aW9uIGN1cnZlDQpvdXQgPC0gcmFyZWN1cnZlKG90dV90YWJsZShwczEpLCBzdGVwPTEwMCkNCg0KcmFyZSA8LSBsYXBwbHkob3V0LCBmdW5jdGlvbih4KXsNCiAgYiA8LSBhcy5kYXRhLmZyYW1lKHgpDQogIGIgPC0gZGF0YS5mcmFtZShPVFUgPSBiWywxXSwgY291bnQgPSByb3duYW1lcyhiKSkNCiAgYiRjb3VudCA8LSBhcy5udW1lcmljKGdzdWIoIk4iLCAiIiwgIGIkY291bnQpKQ0KICByZXR1cm4oYikNCn0pDQpuYW1lcyhyYXJlKSA8LSBzYW1wbGVfbmFtZXMocHMxKQ0KDQpyYXJlIDwtIG1hcF9kZnIocmFyZSwgZnVuY3Rpb24oeCl7DQogIHogPC0gZGF0YS5mcmFtZSh4KQ0KICByZXR1cm4oeikNCn0sIC5pZCA9ICJzYW1wbGUiKQ0KDQojIHJlYWQgdGhyZXNob2xkIGZvciBzYW1wbGUgcmVtb3ZhbA0KdGhyZXNob2xkID0gMTAwMA0KDQpnZy5yYXJlIDwtIGdncGxvdChkYXRhID0gcmFyZSkrDQogIGdlb21fbGluZShhZXMoeCA9IGNvdW50LCB5ID0gT1RVLCBncm91cD1zYW1wbGUpLCBhbHBoYT0wLjUpKw0KICBnZW9tX3BvaW50KGRhdGEgPSByYXJlICU+JSANCiAgICAgICAgICAgICAgIGdyb3VwX2J5KHNhbXBsZSkgJT4lIA0KICAgICAgICAgICAgICAgdG9wX24oMSwgY291bnQpLA0KICAgICAgICAgICAgIGFlcyh4ID0gY291bnQsIHkgPSBPVFUsIGNvbG91cj0oY291bnQgPiB0aHJlc2hvbGQpKSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gIHNjYWxlczo6bGFiZWxfbnVtYmVyX3NpKCkpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXRocmVzaG9sZCwgbGluZXR5cGU9ImRhc2hlZCIpICsNCiAgbGFicyhjb2xvdXIgPSAiU2FtcGxlIGtlcHQ/IikgKw0KICB4bGFiKCJTZXF1ZW5jZSByZWFkcyIpICsNCiAgeWxhYigiT2JzZXJ2ZWQgQVNWJ3MiKQ0KDQojV3JpdGUgb3V0IGZpZ3VyZQ0KcGRmKGZpbGU9ImZpZ3MvcmFyZWZhY3Rpb24ucGRmIiwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOCAsIHBhcGVyPSJhNHIiKQ0KICBwbG90KGdnLnJhcmUpDQp0cnkoZGV2Lm9mZigpLCBzaWxlbnQ9VFJVRSkNCg0KI1JlbW92ZSBhbGwgc2FtcGxlcyB1bmRlciB0aGUgbWluaW11bSByZWFkIHRocmVzaG9sZCANCnBzMiA8LSBwcnVuZV9zYW1wbGVzKHNhbXBsZV9zdW1zKHBzMSk+PXRocmVzaG9sZCwgcHMxKSANCnBzMiA8LSBmaWx0ZXJfdGF4YShwczIsIGZ1bmN0aW9uKHgpIG1lYW4oeCkgPiAwLCBUUlVFKSAjRHJvcCBtaXNzaW5nIHRheGEgZnJvbSB0YWJsZQ0KbWVzc2FnZShuc2FtcGxlcyhwczEpIC0gbnNhbXBsZXMocHMyKSwgIiBTYW1wbGVzIGFuZCAiLCBudGF4YShwczEpIC0gbnRheGEocHMyKSwgIiB0YXhhIHVuZGVyIHJlYWQgdGhyZXNob2xkIERyb3BwZWQiKQ0KYGBgDQoNCg0KIyMgQ29tcGFyZSBkaXN0YW5jZXMgcHJlIGFuZCBwb3N0IGZpbHRlcmluZw0KDQpgYGB7ciBjb21wYXJlIGRpc3RzfQ0Kc2VxdGFiX2ZpbmFsIDwtIHJlYWRSRFMoIm91dHB1dC9yZHMvc2VxdGFiX25vY3V0LnJkcyIpDQojR2V0IHByZV9maWx0IGRpc3RhbmNlcw0KcHJlX2ZpbHQgPC0gYXMubWF0cml4KHpDb21wb3NpdGlvbnM6OmNtdWx0UmVwbChzZXF0YWJfZmluYWwsIG1ldGhvZD0iQkwiLCBvdXRwdXQ9InAtY291bnRzIikpDQpwcmVfZmlsdF9kaXN0IDwtIGFzLm1hdHJpeCh2ZWdkaXN0KENvRGFTZXE6OmNvZGFTZXEuY2xyKHByZV9maWx0KSwgbWV0aG9kPSJldWNsaWRlYW4iKSkNCg0KIyBHZXQgcG9zdF9maWx0IGRpc3RhbmNlcw0Kb3R1dGFiIDwtIG90dV90YWJsZShwczIpICU+JQ0KICBhcygibWF0cml4IikNCg0KI290dXRhYiA8LSB0KHNlcXRhYikNCnBvc3RfZmlsdCA8LSBhcy5tYXRyaXgoekNvbXBvc2l0aW9uczo6Y211bHRSZXBsKG90dXRhYiwgbWV0aG9kPSJCTCIsIG91dHB1dD0icC1jb3VudHMiKSkNCnBvc3RfZmlsdF9kaXN0IDwtIGFzLm1hdHJpeCh2ZWdkaXN0KENvRGFTZXE6OmNvZGFTZXEuY2xyKHBvc3RfZmlsdCksIG1ldGhvZD0iZXVjbGlkZWFuIikpDQoNCiNTdWJzZXQgdG8gb25seSBjb21tb24gc2FtcGxlcw0Kc3Vic2FtcGxlIDwtIGludGVyc2VjdChjb2xuYW1lcyhwcmVfZmlsdF9kaXN0KSwgY29sbmFtZXMocG9zdF9maWx0X2Rpc3QpKQ0KYXMuZGF0YS5mcmFtZSh2ZWdhbjo6bWFudGVsKHByZV9maWx0X2Rpc3Rbc3Vic2FtcGxlLCBzdWJzYW1wbGVdLCBwb3N0X2ZpbHRfZGlzdFtzdWJzYW1wbGUsIHN1YnNhbXBsZV0pW2MoInN0YXRpc3RpYyIsInNpZ25pZiIsInBlcm11dGF0aW9ucyIpXSkNCmBgYA0KDQojIyMgT3V0cHV0IGZhc3Rhcw0KDQpgYGB7ciBTYXZlIGZhc3Rhc30NCiNXcml0ZSBvdXQgZmFzdGEgYW5kIGFsaWduIHdpdGggc2lsdmEgb25saW5lDQpzZXFzIDwtIHJlZnNlcShwczIpDQp3cml0ZVhTdHJpbmdTZXQoc2VxcywgIm91dHB1dC9jdXJhdGVkX2Fzdi5mYXN0YSIsIHdpZHRoPTEwMDApDQpgYGANCg0KIyMgQWxpZ24gd2l0aCBTSU5BDQoNCmBgYHtiYXNoIGFsaWdufQ0KI0NyZWF0ZSBhIHZpcnR1YWwgZW52aXJvbm1lbnQNCm1vZHVsZSBsb2FkIFB5dGhvbi8zLjguMi1HQ0Njb3JlLTkuMy4wIA0KdmlydHVhbGVudiB+L1NJTkENCnNvdXJjZSB+L1NJTkEvYmluL2FjdGl2YXRlDQoNCiNJbnN0YWxsIHNpbmENCndnZXQgaHR0cHM6Ly9naXRodWIuY29tL2VwcnVlc3NlL1NJTkEvcmVsZWFzZXMvZG93bmxvYWQvdjEuNy4wL3NpbmEtMS43LjAtbGludXgudGFyLmd6DQp0YXIgeGYgc2luYS0xLjcuMC1saW51eC50YXIuZ3oNCmNkIHNpbmEtMS43LjAtbGludXgNCg0KI0dldCByZWZlcmVuY2VEQg0Kd2dldCBodHRwczovL3d3dy5hcmItc2lsdmEuZGUvZmlsZWFkbWluL3NpbHZhX2RhdGFiYXNlcy9yZWxlYXNlXzEzOC9BUkJfZmlsZXMvU0lMVkFfMTM4X1NTVVJlZl9OUjk5XzA1XzAxXzIwX29wdC5hcmIuZ3oNCg0KDQojUnVuIHNpbmENCn4vc2luYS0xLjcuMC1saW51eC9zaW5hIC0tc2VhcmNoIC0tYWRkLXJlbGF0aXZlcz01IC0tbWV0YS1mbXQ9aGVhZGVyIC0tZmllbGRzPWFjYyBcDQotaSAvZ3JvdXAvcGF0aG9nZW5zL0FsZXhwL01ldGFiYXJjb2RpbmcvUHN5bGxpZF9taWNyb2Jpb21lL291dHB1dC9jdXJhdGVkX2Fzdi5mYXN0YSBcDQotciAvZ3JvdXAvcGF0aG9nZW5zL0FsZXhwL01ldGFiYXJjb2RpbmcvUHN5bGxpZF9taWNyb2Jpb21lL3JlZmVyZW5jZS9TSUxWQV8xMzhfU1NVUmVmX05SOTlfMDVfMDFfMjBfb3B0LmFyYiBcDQotbyAvZ3JvdXAvcGF0aG9nZW5zL0FsZXhwL01ldGFiYXJjb2RpbmcvUHN5bGxpZF9taWNyb2Jpb21lL291dHB1dC9hbGlnbmVkX2Fzdi5mYXN0YQ0KYGBgDQoNCiMjIENyZWF0ZSBwaHlsb2dlbmV0aWMgdHJlZSB3aXRoIGZhc3R0cmVlZQ0KYGBge2Jhc2ggZmFzdHJlZX0NCm1vZHVsZSBsb2FkIEZhc3RUcmVlDQpGYXN0VHJlZSAtZ3RyIC1jYXQgMjAgLXF1b3RlIC1udCAvZ3JvdXAvcGF0aG9nZW5zL0FsZXhwL01ldGFiYXJjb2RpbmcvUHN5bGxpZF9taWNyb2Jpb21lL291dHB1dC9hbGlnbmVkX2Fzdi5mYXN0YSA+IC9ncm91cC9wYXRob2dlbnMvQWxleHAvTWV0YWJhcmNvZGluZy9Qc3lsbGlkX21pY3JvYmlvbWUvb3V0cHV0L3NpbmFfdHJlZSANCmBgYA0KDQojIyBEYXRlIHRyZWUgd2l0aCBQQVRIZDgNCg0KYGBge3IgUEFUSGQ4fQ0KIyBEYXRlIHVzaWduIGNvbmdydWlmeQ0KI3RyZWUgPC0gcmVhZC50cmVlKCJhcmItc2lsdmEuZGVfMjAyMC0wNy0yOF9pZDg2MDg0OS9hcmItc2lsdmEuZGVfMjAyMC0wNy0yOF9pZDg2MDg0OS50cmVlIikNCnRyZWUgPC0gcmVhZC50cmVlKCJvdXRwdXQvc2luYV90cmVlIikNCnRyZWUkdGlwLmxhYmVsIDwtIHRyZWUkdGlwLmxhYmVsICU+JQ0KICBzdHJfc3BsaXRfZml4ZWQoIlxcWyIsIG49SW5mKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICB1bml0ZShWMSwgVjIsIGNvbD0ibmFtZSIpICU+JQ0KICBtdXRhdGUobmFtZSA9IG5hbWUgJT4lDQogICAgICAgICAgIHN0cl9yZW1vdmUoIl9hbGlnbi4qJCIpICU+JQ0KICAgICAgICAgICBzdHJfcmVtb3ZlKCJeLipfYWNjPSIpICU+JQ0KICAgICAgICAgICBzdHJfcmVtb3ZlKCJcXF0kIikpICU+JQ0KICBwdWxsKG5hbWUpDQoNCnRyZWVfcHJ1bmVkIDwtIGRyb3AudGlwKHRyZWUsIHRyZWUkdGlwLmxhYmVsW2R1cGxpY2F0ZWQodHJlZSR0aXAubGFiZWwpXSkNCg0KI1JlZmVyZW5jZSB0cmVlDQpyZWZfdHJlZSA8LSByZWFkLnRyZWUoImJpbi9EYXRlZCB0cmVlcy9CYWN0ZXJpYV8xNlNfU0lMVkFfOTdzaW1fRmFzdFRyZWVfUEFUSGQ4LnRyZSIpDQoNCnJlZl90cmVlJHRpcC5sYWJlbCA8LSByZWZfdHJlZSR0aXAubGFiZWwgJT4lIA0KICBzdHJfZXh0cmFjdCgiXiguKj8pXFwuIikgJT4lDQogIHN0cl9yZW1vdmUoIlxcLiQiKQ0KDQojMjg4MSAgY29uZ3J1ZW50IHRpcHMNCnRhYmxlKHRyZWUkdGlwLmxhYmVsICVpbiUgcmVmX3RyZWUkdGlwLmxhYmVsKQ0KDQpyZWZfdHJlZV9wcnVuZWQgPC0gZHJvcC50aXAocmVmX3RyZWUsIGMocmVmX3RyZWUkdGlwLmxhYmVsWyFyZWZfdHJlZSR0aXAubGFiZWwgJWluJSB0cmVlJHRpcC5sYWJlbF0scmVmX3RyZWUkdGlwLmxhYmVsW2R1cGxpY2F0ZWQocmVmX3RyZWUkdGlwLmxhYmVsKV0pKQ0KDQp0YWJsZSh0cmVlX3BydW5lZCR0aXAubGFiZWwgJWluJSByZWZfdHJlZV9wcnVuZWQkdGlwLmxhYmVsKQ0KDQpyZXMgPC0gY29uZ3J1aWZ5LnBoeWxvKHJlZmVyZW5jZT1yZWZfdHJlZV9wcnVuZWQsIHRhcmdldD10cmVlX3BydW5lZCwgc2NhbGU9IlBBVEhkOCIpDQoNCnRyZWUyIDwtIGRyb3AudGlwKHJlcyRwaHksIHJlcyRwaHkkdGlwLmxhYmVsWyFyZXMkcGh5JHRpcC5sYWJlbCAlaW4lIG5hbWVzKHJlZnNlcShwczIpKV0pDQp3cml0ZS50cmVlKHRyZWUyLCAib3V0cHV0L3BoeXRyZWUubndrIikNCmBgYA0KDQoNCiMjIE1lcmdlIHRlY2huaWNhbCByZXBsaWNhdGVzDQoNCldpdGggb25seSAxNiBzYW1wbGVzIHJlcGxpY2F0ZWQsIGFuZCBvbmx5IG9uIHRoZSBmaXJzdCAyIHNlcXVlbmNpbmcgcnVucyBpIGRvbnQgdGhpbmsgdGhlcmUgaXMgYSB3YXkgdG8gZXhwbGljaXRseSB0YWtlIGludG8gYWNjb3VudCByZXBsaWNhdGUgdmFyaWFiaWxpdHksIHRoZXJlZm9yZSBhbGwgcmVwbGljYXRlcyB3ZXJlIG1lcmdlZA0KDQpUaGlzIGNhbiBiZSBkb25lIHdpdGggREFEQTIgLSBtZXJnZVNlcXVlbmNlVGFibGVzKHN0MSwgc3QyLCBzdDMsIHJlcGVhdHMgPSAic3VtIg0KDQpgYGB7ciByZXBsaWNhdGVzfQ0KIyBNZXJnZSByZXBsaWNhdGVzDQpwcy5tZXJnZWQgPC0gcHMyICU+JQ0KICAgIG1lcmdlX3NhbXBsZXMoZ3JvdXAgPSAiU2FtcGxlX05hbWUiLCBmdW49InN1bSIpDQoNCg0KI1RoaXMgbG9zZXMgdGhlIHNhbXBsZSBtZXRhZGF0YSAtIE5lZWQgdG8gYWRkIGl0IGFnaWFuDQpzYW1wbGVfZGF0YShwcy5tZXJnZWQpIDwtIHNhbXBsZV9kYXRhKHBzMikgJT4lDQogIGFzKCJtYXRyaXgiKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBmaWx0ZXIoIWR1cGxpY2F0ZWQoU2FtcGxlX05hbWUpKSAlPiUNCiAgbWFncml0dHI6OnNldF9yb3duYW1lcyguJFNhbXBsZV9OYW1lKQ0KDQpzZXFzIDwtIHJlZnNlcShwczIpDQp0cmVlIDwtIHJlYWQudHJlZSgib3V0cHV0L3BoeXRyZWUubndrIikNCg0KI21ha2UgbmV3IHBoeWxvc2VxIG9iamVjdA0KcHMyIDwtIHBoeWxvc2VxKHRheF90YWJsZShwcy5tZXJnZWQpLA0KICAgICAgICAgICAgICAgc2FtcGxlX2RhdGEocHMubWVyZ2VkKSwNCiAgICAgICAgICAgICAgIG90dV90YWJsZShvdHVfdGFibGUocHMubWVyZ2VkKSwgdGF4YV9hcmVfcm93cyA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgIHJlZnNlcShzZXFzKSwNCiAgICAgICAgICAgICAgIHBoeV90cmVlKHRyZWUpKQ0KDQoNCnNhdmVSRFMocHMyLCAib3V0cHV0L3Jkcy9wczIucmRzIikNCmBgYA0K
 

Copyright (C) 2020 Alexander M Piper

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/

alexander.piper@agriculture.vic.gov.au