Link to notebook

Link to github repo.


Table of Contents


Load packages

library(tidyverse)
library(readxl)
library(phyloseq)
library(Biostrings)
#library(phangorn)
library(readr)
library(seqinr)
#library(decontam)
library(ape)
library(vegan)
#library(philr)
library(RColorBrewer)
library(microbiome)
#library(DESeq2)
library(compositions);
#library(cowplot)
library(plotly)
library(htmlwidgets)
library(withr)
library(lubridate)

Import and prepare the data from eDNA

Import metadata

metadata <- read_csv("sample_data.csv")

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  SampleID = col_character(),
  `Year.Trawl#` = col_character(),
  Datecode = col_double(),
  Date = col_character(),
  Month = col_double(),
  Year = col_double(),
  Bayside = col_character(),
  Station = col_character(),
  Habitat = col_character(),
  DO = col_double(),
  Salinity = col_double(),
  Temperature = col_double()
)

Import DADA2 results

Import count table and taxonomy file. I slightly modified otutable.csv in Excel to otutable_mod.csv to remove the quotes around seq names and put NA placehoder as first col name (which was above row names)

# Import Count table. Skip first row of tsv file, which is just some text
count_table <- read_table2("results/otutable_mod.csv")
Missing column names filled in: 'X1' [1]
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  X1 = col_character()
)
ℹ Use `spec()` for the full column specifications.
colnames(count_table)[1] <- "SampleID"

# Import taxonomy of ASVs
taxonomy <- read_csv(file="results/tax_sequences_blast_taxonomy.csv")
Missing column names filled in: 'X1' [1]Duplicated column names deduplicated: 'RefSeq_Tax_ID' => 'RefSeq_Tax_ID_1' [18]
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  X1 = col_double(),
  ASV_ID = col_character(),
  ref_seq_ID = col_character(),
  PID = col_double(),
  alnmt_len = col_double(),
  mismatch = col_double(),
  eval = col_double(),
  bscore = col_double(),
  RefSeq_Tax_ID = col_double(),
  Ref_Seq_title = col_character(),
  superkingdom = col_character(),
  phylum = col_character(),
  class = col_character(),
  order = col_character(),
  family = col_character(),
  genus = col_character(),
  species = col_character(),
  RefSeq_Tax_ID_1 = col_double()
)
# remove first col of sequential numbers
taxonomy[,1] <- NULL
# filter out sequences with low PID (recommended by Sara)
taxonomy <- filter(taxonomy, PID > 92)

# remove BLAST metadata and just retain taxonomy (necessary for further processing below)
drop.cols <- c(colnames(taxonomy)[2:9],'RefSeq_Tax_ID_1')
taxonomy <-  select(taxonomy, -one_of(drop.cols))


# And import the Common names, as curated by Sara. Join to taxonomy
commonnames <- read_excel("Trawls MASTER 2020 _mod_ES.xlsx",7)
commonnames

taxonomy <- left_join(taxonomy, commonnames, by = "ASV_ID")
taxonomy
NA

Filtering removed seqs 110, 332 (Gobiosoma ginsburgi and Belone belone) Note for Sara should we consider setting this at 97% which is more robust and still leaves 334 unique ASVs (rather than 379 with the 92% cutoff in the settings above)

Preview datasets

count_table
taxonomy
metadata

Make phyloseq object

I want to use the phyloseq package for some plotting/ statistics, which first requires making phyloseq objects out of each of input data tables-

count_table_matrix <- as.matrix(count_table[,2:392]) # convert count table to matrix, leaving out character column of sample ID
rownames(count_table_matrix) <- count_table$SampleID # add back in Sample IDs as row names
ASV =   otu_table(count_table_matrix, taxa_are_rows =  FALSE)

taxonomy_matrix <- as.matrix(taxonomy[,2:9])
rownames(taxonomy_matrix) <- taxonomy$ASV_ID 
TAX =   tax_table(taxonomy_matrix)

# select only the metada rows with eDNA samples
metadata_edna <- metadata %>% filter(!is.na(SampleID))

META    =   sample_data(data.frame(metadata_edna, row.names = metadata_edna$`SampleID`))

First check that the inputs are in compatible formats by checking for ASV names with the phyloseq function, taxa_names

head(taxa_names(TAX))
[1] "Seq_1" "Seq_2" "Seq_3" "Seq_4" "Seq_5" "Seq_6"
head(taxa_names(ASV))
[1] "Seq_1" "Seq_2" "Seq_3" "Seq_4" "Seq_5" "Seq_6"

And check sample names were also detected

# Modify taxa names in ASV, which are formatted with the sample ID, underscor, fastq ID. Don't need this fastq ID anymore and want it to match the sample names from metadata
sample_names(ASV) <-  sample_names(ASV) %>%
  str_replace_all(pattern = "_S[:digit:]+",replacement = "")


head(sample_names(ASV))
[1] "T1PosCon" "T1S10"    "T1S11"    "T1S1"     "T1S2"     "T1S3"    
head(sample_names(META))
[1] "T1PosCon" "T1S1"     "T1S2"     "T1S3"     "T1S5"     "T1S6"    

And make the phyloseq object

ps <- phyloseq(ASV, TAX,    META)

QC and filtering eDNA dataset

Rarefaction curves

rarecurve(otu_table(ps), step=50, cex=0.5)
empty rows removed
# save as .eps
setEPS()
postscript("Figures/rarefaction.eps")
rarecurve(otu_table(ps), step=50, cex=0.5)
empty rows removed
dev.off()
quartz_off_screen 
                2 

Most samples look like they were sampled to completion. Be weary of T3S11, T1S2, and maybe T4S5

Filtering

Check some features of the phyloseq object

rank_names(ps)
[1] "superkingdom" "phylum"       "class"        "order"        "family"       "genus"        "species"      "CommonName"  
unique(tax_table(ps)[, "superkingdom"])
Taxonomy Table:     [2 taxa by 1 taxonomic ranks]:
        superkingdom
Seq_1   "Eukaryota" 
Seq_377 NA          
unique(tax_table(ps)[, "phylum"])
Taxonomy Table:     [3 taxa by 1 taxonomic ranks]:
        phylum      
Seq_1   "Chordata"  
Seq_368 "Arthropoda"
Seq_377 NA          
unique(tax_table(ps)[, "class"])
Taxonomy Table:     [5 taxa by 1 taxonomic ranks]:
        class           
Seq_1   "Actinopteri"   
Seq_63  "Mammalia"      
Seq_362 "Chondrichthyes"
Seq_368 "Insecta"       
Seq_377 NA              

There are some ASVs with NA as superkingdom, phylum, or class annotation- delete these.

ps <- subset_taxa(ps, !is.na(superkingdom) & !is.na(phylum) & !is.na(class))

unique(tax_table(ps)[, "superkingdom"])
Taxonomy Table:     [1 taxa by 1 taxonomic ranks]:
      superkingdom
Seq_1 "Eukaryota" 
unique(tax_table(ps)[, "phylum"])
Taxonomy Table:     [2 taxa by 1 taxonomic ranks]:
        phylum      
Seq_1   "Chordata"  
Seq_368 "Arthropoda"
unique(tax_table(ps)[, "class"])
Taxonomy Table:     [4 taxa by 1 taxonomic ranks]:
        class           
Seq_1   "Actinopteri"   
Seq_63  "Mammalia"      
Seq_362 "Chondrichthyes"
Seq_368 "Insecta"       
nrow(tax_table(ps)) # number of ASVs left
[1] 378

378 ASVs still remain…

Also check class Mammalia, to see if they are contamination or real:

tax_table(subset_taxa(ps, class == 'Mammalia'))
Taxonomy Table:     [8 taxa by 8 taxonomic ranks]:
        superkingdom phylum     class      order          family      genus   species        CommonName 
Seq_63  "Eukaryota"  "Chordata" "Mammalia" "Primates"     "Hominidae" "Homo"  "Homo sapiens" "Human"    
Seq_88  "Eukaryota"  "Chordata" "Mammalia" "Artiodactyla" "Suidae"    "Sus"   "Sus scrofa"   "Wild boar"
Seq_157 "Eukaryota"  "Chordata" "Mammalia" "Primates"     "Hominidae" "Homo"  "Homo sapiens" "Human"    
Seq_343 "Eukaryota"  "Chordata" "Mammalia" "Carnivora"    "Felidae"   "Felis" "Felis catus"  "Cat"      
Seq_369 "Eukaryota"  "Chordata" "Mammalia" "Artiodactyla" "Bovidae"   "Bos"   "Bos taurus"   "Cattle"   
Seq_378 "Eukaryota"  "Chordata" "Mammalia" "Primates"     "Hominidae" "Homo"  "Homo sapiens" "Human"    
Seq_383 "Eukaryota"  "Chordata" "Mammalia" "Primates"     "Hominidae" "Homo"  "Homo sapiens" "Human"    
Seq_389 "Eukaryota"  "Chordata" "Mammalia" "Primates"     "Hominidae" "Homo"  "Homo sapiens" "Human"    

These are human, wild boar, cat (ahem…cat lady), and cattle. All are contamination so delete all Mammalia

ps <- subset_taxa(ps, !class == 'Mammalia')
unique(tax_table(ps)[, "class"])
Taxonomy Table:     [3 taxa by 1 taxonomic ranks]:
        class           
Seq_1   "Actinopteri"   
Seq_362 "Chondrichthyes"
Seq_368 "Insecta"       

Next check the “Insecta” entries

tax_table(subset_taxa(ps, class == 'Insecta'))
Taxonomy Table:     [2 taxa by 8 taxonomic ranks]:
        superkingdom phylum       class     order         family       genus         species              CommonName
Seq_368 "Eukaryota"  "Arthropoda" "Insecta" "Hymenoptera" "Formicidae" "Linepithema" "Linepithema humile" "Ant"     
Seq_380 "Eukaryota"  "Arthropoda" "Insecta" "Hymenoptera" "Formicidae" "Linepithema" "Linepithema humile" "Ant"     

The onlly Insecta is Linepithema humile, which are ants so delete these too..

ps <- subset_taxa(ps, !class == 'Insecta')
unique(tax_table(ps)[, "class"])
Taxonomy Table:     [2 taxa by 1 taxonomic ranks]:
        class           
Seq_1   "Actinopteri"   
Seq_362 "Chondrichthyes"

Check sequencing effort

Check overall how many ASVs there are per sample

# First aglomerate the ASVs at the phylum level using the phyloseq function, tax_glom
superkingdomGlommed = tax_glom(ps, "superkingdom")

# and plot
plot_bar(superkingdomGlommed, x = "Sample")

ggsave(filename = "Figures/seqdepth.eps", plot = plot_bar(superkingdomGlommed, x = "Sample"), units = c("in"), width = 9, height = 6, dpi = 300, )# and save

Total sequences reveals certain samples had very low sequencing effort: T1S7, T1S8, T3S11, and, not as bad, T1S2 and T4S5

The rarefaction analysis also showed T1S2 and T4S5 samples were likely not sequenced to completion. Therefore remove these 5 samples from analysis

ps <- subset_samples(ps, !SampleID == "T1S7" & !SampleID == "T1S8" & !SampleID == "T3S11" & !SampleID == "T1S2" & !SampleID == "T4S5")

ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 368 taxa and 50 samples ]
sample_data() Sample Data:       [ 50 samples by 12 sample variables ]
tax_table()   Taxonomy Table:    [ 368 taxa by 8 taxonomic ranks ]

50 samples remaining with 368 ASVs

Remove Pos Controls (all hits in positive controls are the same family- I assume this is expected)

ps <- subset_samples(ps, !SampleID == "T1PosCon" & !SampleID == "T2PosCon" & !SampleID == "T3PosCon")
ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 368 taxa and 47 samples ]
sample_data() Sample Data:       [ 47 samples by 12 sample variables ]
tax_table()   Taxonomy Table:    [ 368 taxa by 8 taxonomic ranks ]

47 samples remaining with 368 unique ASVs

And lastly, correct some taxonomy: **First* according to Sara, Engraulis encrasicolus (European anchovy) and Engraulis mordax should be Anchoa mitchilli (Bay anchovy):

tax_table(ps) <- gsub(tax_table(ps), pattern = "Engraulis encrasicolus", replacement = "Anchoa mitchilli")  
tax_table(ps) <- gsub(tax_table(ps), pattern = "Engraulis mordax", replacement = "Anchoa mitchilli")  

Second the Fourhorn sculpin (Myoxocephalus quadricornis) is actually an Arctic species. This ASV has 100% PID and 100% query cover to Myoxocephalus quadricornis & Myoxocephalus scorpius (another Arctic species) and 99.4% PID, 100% query cover to Myoxocephalus aenaeus. This latter one is actually the regional species, so this is more likely to be the identity:

tax_table(ps) <- gsub(tax_table(ps), pattern = "Myoxocephalus quadricornis", replacement = "Myoxocephalus aenaeus") 
tax_table(ps) <- gsub(tax_table(ps), pattern = "Fourhorn sculpin", replacement = "Grubby sculpin") 

Third Scomber japonicus, the chub mackerel, is only found in the Indo-Pacific. While this is a commercial product and could be here due to sewage, it is more likely the Scomber colias (Atlantic chub mackerel), which is found regionally (in the open ocean Atlantic). The blast hit to Scomber japonicus has PID of 100% and query cover of 100% while the similarity to Scomber colias 100% query cover/ 99.41% PID.

tax_table(ps) <- gsub(tax_table(ps), pattern = "Scomber japonicus", replacement = "Scomber colias") 
tax_table(ps) <- gsub(tax_table(ps), pattern = "Chub mackerel", replacement = "Atlantic chub mackerel") 
ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 368 taxa and 47 samples ]
sample_data() Sample Data:       [ 47 samples by 12 sample variables ]
tax_table()   Taxonomy Table:    [ 368 taxa by 8 taxonomic ranks ]

47 samples remainwith 368 unique ASVs

Abundance plots eDNA

For plotting, use relative abundances (# of ASV sequences/sum total sequences in sample), calculated easily using microbiome::transform

ps_ra <- microbiome::transform(ps, transform = "compositional")

Export the relative abundance matrix so Sara can have it:

# Extract abundance matrix from the phyloseq object
RelAbun_matrix = as(otu_table(ps_ra), "matrix")

# Coerce to data.frame
RelAbun_dataframe = as.data.frame(RelAbun_matrix)

# Export
write.csv(RelAbun_dataframe,"results/otutable_relabun.csv", row.names = TRUE)

Abundance at family level

Then aglomerate the ASVs at the family level using the phyloseq function, tax_glom

familyGlommed_RA = tax_glom(ps_ra, "family")
family_barplot <- plot_bar(familyGlommed_RA, x = "Sample", fill = "family")
family_barplot

NOTES

  • There are some samples, (T1S3, T1S6, T2S11, T3S10, T3S4, T3S5, T3S9, T4S4, T4S7, T5S7) which are composed almost exclusively of 1 family. This might be fine, but I’m not used to seeing this with prokaroytic data. Just want to check with you

Agglomerate by species to take a look at the unique species

speciesGlommed_RA = tax_glom(ps_ra, "CommonName")
speciesGlommed_RA
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 41 taxa and 47 samples ]
sample_data() Sample Data:       [ 47 samples by 12 sample variables ]
tax_table()   Taxonomy Table:    [ 41 taxa by 8 taxonomic ranks ]
tax_table(speciesGlommed_RA)
Taxonomy Table:     [41 taxa by 8 taxonomic ranks]:
        superkingdom phylum     class            order                family            genus               
Seq_1   "Eukaryota"  "Chordata" "Actinopteri"    "Atheriniformes"     "Atherinopsidae"  "Menidia"           
Seq_2   "Eukaryota"  "Chordata" "Actinopteri"    "Clupeiformes"       "Clupeidae"       "Brevoortia"        
Seq_3   "Eukaryota"  "Chordata" "Actinopteri"    "Clupeiformes"       "Engraulidae"     "Engraulis"         
Seq_4   "Eukaryota"  "Chordata" "Actinopteri"    "Scombriformes"      "Pomatomidae"     "Pomatomus"         
Seq_5   "Eukaryota"  "Chordata" "Actinopteri"    "Lutjaniformes"      "Lutjanidae"      "Lutjanus"          
Seq_6   "Eukaryota"  "Chordata" "Actinopteri"    "Pleuronectiformes"  "Paralichthyidae" "Paralichthys"      
Seq_7   "Eukaryota"  "Chordata" "Actinopteri"    "Clupeiformes"       "Clupeidae"       "Alosa"             
Seq_9   "Eukaryota"  "Chordata" "Actinopteri"    "Gobiiformes"        "Gobiidae"        "Gobiosoma"         
Seq_10  "Eukaryota"  "Chordata" "Actinopteri"    "Pleuronectiformes"  "Scophthalmidae"  "Scophthalmus"      
Seq_11  "Eukaryota"  "Chordata" "Actinopteri"    "Perciformes"        "Serranidae"      "Centropristis"     
Seq_12  "Eukaryota"  "Chordata" "Actinopteri"    "Spariformes"        "Sparidae"        "Stenotomus"        
Seq_15  "Eukaryota"  "Chordata" "Actinopteri"    NA                   "Sciaenidae"      "Leiostomus"        
Seq_16  "Eukaryota"  "Chordata" "Actinopteri"    NA                   "Sciaenidae"      "Menticirrhus"      
Seq_17  "Eukaryota"  "Chordata" "Actinopteri"    "Labriformes"        "Labridae"        "Tautoga"           
Seq_19  "Eukaryota"  "Chordata" "Actinopteri"    "Perciformes"        "Cottidae"        "Myoxocephalus"     
Seq_20  "Eukaryota"  "Chordata" "Actinopteri"    "Pleuronectiformes"  "Pleuronectidae"  "Pseudopleuronectes"
Seq_21  "Eukaryota"  "Chordata" "Actinopteri"    NA                   "Moronidae"       "Morone"            
Seq_22  "Eukaryota"  "Chordata" "Actinopteri"    "Syngnathiformes"    "Syngnathidae"    "Syngnathus"        
Seq_30  "Eukaryota"  "Chordata" "Actinopteri"    "Pleuronectiformes"  "Paralichthyidae" "Etropus"           
Seq_33  "Eukaryota"  "Chordata" "Actinopteri"    NA                   "Sciaenidae"      "Cynoscion"         
Seq_34  "Eukaryota"  "Chordata" "Actinopteri"    "Labriformes"        "Labridae"        "Tautogolabrus"     
Seq_36  "Eukaryota"  "Chordata" "Actinopteri"    "Anguilliformes"     "Anguillidae"     "Anguilla"          
Seq_38  "Eukaryota"  "Chordata" "Actinopteri"    "Scombriformes"      "Scombridae"      "Thunnus"           
Seq_40  "Eukaryota"  "Chordata" "Actinopteri"    "Perciformes"        "Gasterosteidae"  "Apeltes"           
Seq_44  "Eukaryota"  "Chordata" "Actinopteri"    "Cyprinodontiformes" "Fundulidae"      "Fundulus"          
Seq_50  "Eukaryota"  "Chordata" "Actinopteri"    "Atheriniformes"     "Atherinopsidae"  "Membras"           
Seq_52  "Eukaryota"  "Chordata" "Actinopteri"    "Gadiformes"         "Phycidae"        "Urophycis"         
Seq_54  "Eukaryota"  "Chordata" "Actinopteri"    "Scombriformes"      "Scombridae"      "Scomber"           
Seq_57  "Eukaryota"  "Chordata" "Actinopteri"    "Perciformes"        "Triglidae"       "Prionotus"         
Seq_67  "Eukaryota"  "Chordata" "Actinopteri"    "Scombriformes"      "Scombridae"      "Thunnus"           
Seq_82  "Eukaryota"  "Chordata" "Actinopteri"    NA                   "Sciaenidae"      "Bairdiella"        
Seq_84  "Eukaryota"  "Chordata" "Actinopteri"    "Gadiformes"         "Gadidae"         "Microgadus"        
Seq_115 "Eukaryota"  "Chordata" "Actinopteri"    "Cyprinodontiformes" "Fundulidae"      "Fundulus"          
Seq_119 "Eukaryota"  "Chordata" "Actinopteri"    "Gadiformes"         "Phycidae"        "Urophycis"         
Seq_139 "Eukaryota"  "Chordata" "Actinopteri"    "Batrachoidiformes"  "Batrachoididae"  "Opsanus"           
Seq_141 "Eukaryota"  "Chordata" "Actinopteri"    "Scombriformes"      "Scombridae"      "Katsuwonus"        
Seq_181 "Eukaryota"  "Chordata" "Actinopteri"    "Tetraodontiformes"  "Tetraodontidae"  "Sphoeroides"       
Seq_231 "Eukaryota"  "Chordata" "Actinopteri"    "Gadiformes"         "Merlucciidae"    "Merluccius"        
Seq_359 "Eukaryota"  "Chordata" "Actinopteri"    "Perciformes"        "Triglidae"       "Prionotus"         
Seq_362 "Eukaryota"  "Chordata" "Chondrichthyes" "Myliobatiformes"    "Myliobatidae"    "Rhinoptera"        
Seq_372 "Eukaryota"  "Chordata" "Chondrichthyes" "Carcharhiniformes"  "Triakidae"       "Mustelus"          
        species                         CommonName                
Seq_1   "Menidia menidia"               "Atlantic silverside"     
Seq_2   "Brevoortia tyrannus"           "Atlantic menhaden"       
Seq_3   "Anchoa mitchilli"              "Bay anchovy"             
Seq_4   "Pomatomus saltatrix"           "Bluefish"                
Seq_5   "Lutjanus griseus"              "Grey snapper"            
Seq_6   "Paralichthys dentatus"         "Summer flounder"         
Seq_7   "Alosa mediocris"               "Hickory shad"            
Seq_9   "Gobiosoma ginsburgi"           "Seaboard goby"           
Seq_10  "Scophthalmus aquosus"          "Windowpane flounder"     
Seq_11  "Centropristis striata"         "Black seabass"           
Seq_12  "Stenotomus chrysops"           "Scup"                    
Seq_15  "Leiostomus xanthurus"          "Spot"                    
Seq_16  "Menticirrhus saxatilis"        "Northern kingfish"       
Seq_17  "Tautoga onitis"                "Tautog"                  
Seq_19  "Myoxocephalus aenaeus"         "Grubby sculpin"          
Seq_20  "Pseudopleuronectes americanus" "Winter flounder"         
Seq_21  "Morone saxatilis"              "Striped bass"            
Seq_22  "Syngnathus fuscus"             "Northern pipefish"       
Seq_30  "Etropus microstomus"           "Smallmouth flounder"     
Seq_33  "Cynoscion regalis"             "Weakfish"                
Seq_34  "Tautogolabrus adspersus"       "Cunner"                  
Seq_36  "Anguilla rostrata"             "American eel"            
Seq_38  "Thunnus obesus"                "Bigeye tuna"             
Seq_40  "Apeltes quadracus"             "Stickleback"             
Seq_44  "Fundulus majalis"              "Striped killifish"       
Seq_50  "Membras martinica"             "Rough silverside"        
Seq_52  "Urophycis floridana"           "Spotted hake"            
Seq_54  "Scomber colias"                "Atlantic chub mackerel"  
Seq_57  "Prionotus carolinus"           "Northern searobin"       
Seq_67  "Thunnus thynnus"               "Atlantic bluefin tuna"   
Seq_82  "Bairdiella chrysoura"          "American silver perch"   
Seq_84  "Microgadus tomcod"             "Atlantic tomcod"         
Seq_115 "Fundulus heteroclitus"         "Mummichog"               
Seq_119 "Urophycis floridana"           "Red hake"                
Seq_139 "Opsanus tau"                   "Oyster toadfish"         
Seq_141 "Katsuwonus pelamis"            "Skipjack tuna"           
Seq_181 "Sphoeroides maculatus"         "Northern puffer"         
Seq_231 "Merluccius bilinearis"         "Silver hake"             
Seq_359 "Prionotus evolans"             "Striped searobin"        
Seq_362 "Rhinoptera bonasus"            "Cownose ray"             
Seq_372 "Mustelus canis"                "Dusky smooth-hound shark"

Bubble plots

Based on my previous scripts with Cariaco Eukaryotic data

# convert ps object to dataframe using phyloseq's psmelt
species_df <- psmelt(speciesGlommed_RA)

# replace zeroes in the table with NA
species_df[species_df == 0] <- NA

# and remove rows with NAs in abundance  (this is so they don't appear as small dots in plot)
species_df <-  filter(species_df, !is.na(Abundance))

Plot by species, scientific name

speciesbubbleplot_eDNA_sciname <- ggplot(species_df, aes(x = Station, y = fct_rev(species), color = Station)) + # the fancy stuff around y (species) helps to present it in reverse order in the plot (from top to btm alphabetically)
  geom_point(aes(size = Abundance, fill = Station), color = "black", pch = 21)+
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.25,.5,.75,1), max_size = 6)+
  xlab("")+
  ylab("")+
  labs(size="Relative Abundance")+
  theme_bw() +
  scale_fill_brewer(palette="Paired") +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  facet_grid(Datecode~Bayside, scales = "free", space = "free", drop= TRUE)
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
speciesbubbleplot_eDNA_sciname

Plot by species common name

speciesbubbleplot_eDNA_comname <- ggplot(species_df, aes(x = Station, y = fct_rev(CommonName), color = Station)) + # the fancy stuff around y (CommonName) helps to present it in reverse order in the plot (from top to btm alphabetically)
  geom_point(aes(size = Abundance, fill = Station), color = "black", pch = 21)+
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.25,.5,.75,1), max_size = 6)+
  xlab("")+
  ylab("")+
  labs(size="Relative Abundance")+
  theme_bw() +
  scale_fill_brewer(palette="Paired") +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  facet_grid(Datecode~Bayside, scales = "free", space = "free", drop= TRUE)
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
speciesbubbleplot_eDNA_comname

Exportfigures

ggsave(filename = "Figures/speciesbubbleplot_eDNA_sciname.eps", plot = speciesbubbleplot_eDNA_sciname, units = c("in"), width = 7, height = 12, dpi = 300)

ggsave(filename = "Figures/speciesbubbleplot_eDNA_comname.eps", plot = speciesbubbleplot_eDNA_comname, units = c("in"), width = 7, height = 12, dpi = 300)

Bubble plot without Elasmobranchs

The above look good but they include two elasmobranchs, the dusky smooth-hound shark and cownose ray. While these are probably real, the MiFISH primers don’t actually target the elasmobranchs, so we can’t trust this assay to fairly represent these non-target species. Filter out and re-make the bubble plots:

ps_no_elasmo <- subset_taxa(ps, !CommonName == 'Cownose ray')
ps_no_elasmo <- subset_taxa(ps_no_elasmo, !CommonName =='Dusky smooth-hound shark')

ps_ra_no_elasmo <- subset_taxa(ps_ra, !CommonName == 'Cownose ray')
ps_ra_no_elasmo <- subset_taxa(ps_ra_no_elasmo, !CommonName =='Dusky smooth-hound shark')

# and check
speciesGlommed_RA_no_elasmo = tax_glom(ps_ra_no_elasmo, "CommonName")
speciesGlommed_RA_no_elasmo
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 39 taxa and 47 samples ]
sample_data() Sample Data:       [ 47 samples by 12 sample variables ]
tax_table()   Taxonomy Table:    [ 39 taxa by 8 taxonomic ranks ]
tax_table(speciesGlommed_RA_no_elasmo)
Taxonomy Table:     [39 taxa by 8 taxonomic ranks]:
        superkingdom phylum     class         order                family            genus               
Seq_1   "Eukaryota"  "Chordata" "Actinopteri" "Atheriniformes"     "Atherinopsidae"  "Menidia"           
Seq_2   "Eukaryota"  "Chordata" "Actinopteri" "Clupeiformes"       "Clupeidae"       "Brevoortia"        
Seq_3   "Eukaryota"  "Chordata" "Actinopteri" "Clupeiformes"       "Engraulidae"     "Engraulis"         
Seq_4   "Eukaryota"  "Chordata" "Actinopteri" "Scombriformes"      "Pomatomidae"     "Pomatomus"         
Seq_5   "Eukaryota"  "Chordata" "Actinopteri" "Lutjaniformes"      "Lutjanidae"      "Lutjanus"          
Seq_6   "Eukaryota"  "Chordata" "Actinopteri" "Pleuronectiformes"  "Paralichthyidae" "Paralichthys"      
Seq_7   "Eukaryota"  "Chordata" "Actinopteri" "Clupeiformes"       "Clupeidae"       "Alosa"             
Seq_9   "Eukaryota"  "Chordata" "Actinopteri" "Gobiiformes"        "Gobiidae"        "Gobiosoma"         
Seq_10  "Eukaryota"  "Chordata" "Actinopteri" "Pleuronectiformes"  "Scophthalmidae"  "Scophthalmus"      
Seq_11  "Eukaryota"  "Chordata" "Actinopteri" "Perciformes"        "Serranidae"      "Centropristis"     
Seq_12  "Eukaryota"  "Chordata" "Actinopteri" "Spariformes"        "Sparidae"        "Stenotomus"        
Seq_15  "Eukaryota"  "Chordata" "Actinopteri" NA                   "Sciaenidae"      "Leiostomus"        
Seq_16  "Eukaryota"  "Chordata" "Actinopteri" NA                   "Sciaenidae"      "Menticirrhus"      
Seq_17  "Eukaryota"  "Chordata" "Actinopteri" "Labriformes"        "Labridae"        "Tautoga"           
Seq_19  "Eukaryota"  "Chordata" "Actinopteri" "Perciformes"        "Cottidae"        "Myoxocephalus"     
Seq_20  "Eukaryota"  "Chordata" "Actinopteri" "Pleuronectiformes"  "Pleuronectidae"  "Pseudopleuronectes"
Seq_21  "Eukaryota"  "Chordata" "Actinopteri" NA                   "Moronidae"       "Morone"            
Seq_22  "Eukaryota"  "Chordata" "Actinopteri" "Syngnathiformes"    "Syngnathidae"    "Syngnathus"        
Seq_30  "Eukaryota"  "Chordata" "Actinopteri" "Pleuronectiformes"  "Paralichthyidae" "Etropus"           
Seq_33  "Eukaryota"  "Chordata" "Actinopteri" NA                   "Sciaenidae"      "Cynoscion"         
Seq_34  "Eukaryota"  "Chordata" "Actinopteri" "Labriformes"        "Labridae"        "Tautogolabrus"     
Seq_36  "Eukaryota"  "Chordata" "Actinopteri" "Anguilliformes"     "Anguillidae"     "Anguilla"          
Seq_38  "Eukaryota"  "Chordata" "Actinopteri" "Scombriformes"      "Scombridae"      "Thunnus"           
Seq_40  "Eukaryota"  "Chordata" "Actinopteri" "Perciformes"        "Gasterosteidae"  "Apeltes"           
Seq_44  "Eukaryota"  "Chordata" "Actinopteri" "Cyprinodontiformes" "Fundulidae"      "Fundulus"          
Seq_50  "Eukaryota"  "Chordata" "Actinopteri" "Atheriniformes"     "Atherinopsidae"  "Membras"           
Seq_52  "Eukaryota"  "Chordata" "Actinopteri" "Gadiformes"         "Phycidae"        "Urophycis"         
Seq_54  "Eukaryota"  "Chordata" "Actinopteri" "Scombriformes"      "Scombridae"      "Scomber"           
Seq_57  "Eukaryota"  "Chordata" "Actinopteri" "Perciformes"        "Triglidae"       "Prionotus"         
Seq_67  "Eukaryota"  "Chordata" "Actinopteri" "Scombriformes"      "Scombridae"      "Thunnus"           
Seq_82  "Eukaryota"  "Chordata" "Actinopteri" NA                   "Sciaenidae"      "Bairdiella"        
Seq_84  "Eukaryota"  "Chordata" "Actinopteri" "Gadiformes"         "Gadidae"         "Microgadus"        
Seq_115 "Eukaryota"  "Chordata" "Actinopteri" "Cyprinodontiformes" "Fundulidae"      "Fundulus"          
Seq_119 "Eukaryota"  "Chordata" "Actinopteri" "Gadiformes"         "Phycidae"        "Urophycis"         
Seq_139 "Eukaryota"  "Chordata" "Actinopteri" "Batrachoidiformes"  "Batrachoididae"  "Opsanus"           
Seq_141 "Eukaryota"  "Chordata" "Actinopteri" "Scombriformes"      "Scombridae"      "Katsuwonus"        
Seq_181 "Eukaryota"  "Chordata" "Actinopteri" "Tetraodontiformes"  "Tetraodontidae"  "Sphoeroides"       
Seq_231 "Eukaryota"  "Chordata" "Actinopteri" "Gadiformes"         "Merlucciidae"    "Merluccius"        
Seq_359 "Eukaryota"  "Chordata" "Actinopteri" "Perciformes"        "Triglidae"       "Prionotus"         
        species                         CommonName              
Seq_1   "Menidia menidia"               "Atlantic silverside"   
Seq_2   "Brevoortia tyrannus"           "Atlantic menhaden"     
Seq_3   "Anchoa mitchilli"              "Bay anchovy"           
Seq_4   "Pomatomus saltatrix"           "Bluefish"              
Seq_5   "Lutjanus griseus"              "Grey snapper"          
Seq_6   "Paralichthys dentatus"         "Summer flounder"       
Seq_7   "Alosa mediocris"               "Hickory shad"          
Seq_9   "Gobiosoma ginsburgi"           "Seaboard goby"         
Seq_10  "Scophthalmus aquosus"          "Windowpane flounder"   
Seq_11  "Centropristis striata"         "Black seabass"         
Seq_12  "Stenotomus chrysops"           "Scup"                  
Seq_15  "Leiostomus xanthurus"          "Spot"                  
Seq_16  "Menticirrhus saxatilis"        "Northern kingfish"     
Seq_17  "Tautoga onitis"                "Tautog"                
Seq_19  "Myoxocephalus aenaeus"         "Grubby sculpin"        
Seq_20  "Pseudopleuronectes americanus" "Winter flounder"       
Seq_21  "Morone saxatilis"              "Striped bass"          
Seq_22  "Syngnathus fuscus"             "Northern pipefish"     
Seq_30  "Etropus microstomus"           "Smallmouth flounder"   
Seq_33  "Cynoscion regalis"             "Weakfish"              
Seq_34  "Tautogolabrus adspersus"       "Cunner"                
Seq_36  "Anguilla rostrata"             "American eel"          
Seq_38  "Thunnus obesus"                "Bigeye tuna"           
Seq_40  "Apeltes quadracus"             "Stickleback"           
Seq_44  "Fundulus majalis"              "Striped killifish"     
Seq_50  "Membras martinica"             "Rough silverside"      
Seq_52  "Urophycis floridana"           "Spotted hake"          
Seq_54  "Scomber colias"                "Atlantic chub mackerel"
Seq_57  "Prionotus carolinus"           "Northern searobin"     
Seq_67  "Thunnus thynnus"               "Atlantic bluefin tuna" 
Seq_82  "Bairdiella chrysoura"          "American silver perch" 
Seq_84  "Microgadus tomcod"             "Atlantic tomcod"       
Seq_115 "Fundulus heteroclitus"         "Mummichog"             
Seq_119 "Urophycis floridana"           "Red hake"              
Seq_139 "Opsanus tau"                   "Oyster toadfish"       
Seq_141 "Katsuwonus pelamis"            "Skipjack tuna"         
Seq_181 "Sphoeroides maculatus"         "Northern puffer"       
Seq_231 "Merluccius bilinearis"         "Silver hake"           
Seq_359 "Prionotus evolans"             "Striped searobin"      

Remake bubble plots. First melt for tidyverse format

# convert ps object to dataframe using phyloseq's psmelt
species_df_no_elasmo <- psmelt(speciesGlommed_RA_no_elasmo)

# replace zeroes in the table with NA
species_df_no_elasmo[species_df_no_elasmo == 0] <- NA

# and remove rows with NAs in abundance  (this is so they don't appear as small dots in plot)
species_df_no_elasmo <-  filter(species_df_no_elasmo, !is.na(Abundance))

Plot by species, scientific name

speciesbubbleplot_eDNA_sciname_no_elasmo <- ggplot(species_df_no_elasmo, aes(x = Station, y = fct_rev(species), color = Station)) + # the fancy stuff around y (species) helps to present it in reverse order in the plot (from top to btm alphabetically)
  geom_point(aes(size = Abundance, fill = Station), color = "black", pch = 21)+
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.25,.5,.75,1), max_size = 6)+
  xlab("")+
  ylab("")+
  labs(size="Relative Abundance")+
  theme_bw() +
  scale_fill_brewer(palette="Paired") +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  facet_grid(Datecode~Bayside, scales = "free", space = "free", drop= TRUE)
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
speciesbubbleplot_eDNA_sciname_no_elasmo

Plot by species common name

speciesbubbleplot_eDNA_comname_no_elasmo <- ggplot(species_df_no_elasmo, aes(x = Station, y = fct_rev(CommonName), color = Station)) + # the fancy stuff around y (CommonName) helps to present it in reverse order in the plot (from top to btm alphabetically)
  geom_point(aes(size = Abundance, fill = Station), color = "black", pch = 21)+
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.25,.5,.75,1), max_size = 6)+
  xlab("")+
  ylab("")+
  labs(size="Relative Abundance")+
  theme_bw() +
  scale_fill_brewer(palette="Paired") +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  facet_grid(Datecode~Bayside, scales = "free", space = "free", drop= TRUE)
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
speciesbubbleplot_eDNA_comname_no_elasmo

Exportfigures

ggsave(filename = "Figures/speciesbubbleplot_eDNA_sciname_no_elasmo.eps", plot = speciesbubbleplot_eDNA_sciname_no_elasmo, units = c("in"), width = 7, height = 12, dpi = 300)

ggsave(filename = "Figures/speciesbubbleplot_eDNA_comname_no_elasmo.eps", plot = speciesbubbleplot_eDNA_comname_no_elasmo, units = c("in"), width = 7, height = 12, dpi = 300)

Import and prepare the data from trawls

Import Trawl Count Data

# import 4th sheet from  Excel file which contains morphometric data for each individual collected for every date
There were 40 warnings (use warnings() to see them)
trawl_master <- read_excel("Trawls MASTER 2020 _mod_ES.xlsx",4)

# and import 6th sheet which is station info
stations <- read_excel("Trawls MASTER 2020 _mod_ES.xlsx",6)

# and import shedding factor- an index determined by Sara that indicates how much the species sheds when handled (and therefore how likely it is to shed cells in water)
sheddingfactor <- read_excel("Allometric correction_mod.xlsx",5)

# Group station name and shedding factor into trawl_master table
trawl_master <- left_join(trawl_master, stations, by = "STATION_NO")
trawl_master <- left_join(trawl_master, sheddingfactor, by = "COMMONNAME")

trawl_master

Import station/ trawl information

station_data <- read_excel("Trawls MASTER 2020 _mod_ES.xlsx",1)
station_data

# Filter to only include DATECODE, Station_NO, Trawl_Min
station_data <- station_data %>% select(DATECODE, STATION_NO, Trawl_Min)
station_data

Combine station information to trawl_master in order to have the duration of each trawl (for calculating CPUE)

trawl_master <- left_join(trawl_master, station_data, by = c("DATECODE", "STATION_NO"))
trawl_master

Make a count table from trawl_master, grouping by date and location, summing the counts for every unique species, and summing the total length for each trawl/ species

trawl_counts <- trawl_master %>%
  group_by(DATECODE, STATION_NA, STATION_NO, Trawl_Min, BAYSIDE, CommonName, SheddingFactor) %>%
  summarize(TotalLength = sum(TL_CM))
`summarise()` has grouped output by 'DATECODE', 'STATION_NA', 'STATION_NO', 'Trawl_Min', 'BAYSIDE', 'CommonName'. You can override using the `.groups` argument.
counts <- trawl_master %>%
  group_by(DATECODE, STATION_NA, STATION_NO, CommonName) %>%
  tally(name = "count")

trawl_counts <- left_join(trawl_counts, counts, by = c ("DATECODE", "STATION_NA", "STATION_NO", "CommonName"))
trawl_counts

Calculate CPUE and put in new column. CPUE is the count divided by trawl time (in minutes)

trawl_counts <- trawl_counts %>%
  mutate (CPUE = count / Trawl_Min)
trawl_counts

Calculate the metric that Sara came up with: sum(total length) * shedding factor. This is a correction of the abundance that takes into account the sums of length of each fish for each date/trawl and multiplies by a factor determined by how much they shed.

trawl_counts <- trawl_counts %>%
  mutate ("TLxSF" = TotalLength*SheddingFactor)
trawl_counts

Then also divide the TotalLength and SumTL*SF by the trawl time in order to account for effort (similar to CPUE)

trawl_counts <- trawl_counts %>%
  mutate ("TLPUE" = TotalLength/Trawl_Min)

trawl_counts <- trawl_counts %>%
  mutate ("TLxSF.PUE" = TLxSF/Trawl_Min)

trawl_counts

Remove 09/16/20 since there is no equivalent eDNA from that date

trawl_counts <- trawl_counts %>%
  filter(DATECODE != "20200916")

Abundance plots Trawls

Bubble plots of Counts

speciesbubbleplot_trawl_comname <- ggplot(trawl_counts, aes(x = STATION_NA, y = fct_rev(CommonName), color = STATION_NA)) + 
  geom_point(aes(size = log10(count), fill = STATION_NA), color = "black", pch = 21)+
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(log10(1), log10(2), log10(5), log10(10), log10(25), log10(100)), max_size = 6, labels = c("1","2","5","10","25","100"))+
  xlab("")+
  ylab("")+
  labs(size="Abundance", fill = "Station")+
  theme_bw() +
  scale_fill_brewer(palette="Paired") +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  facet_grid(DATECODE~BAYSIDE, scales = "free", space = "free", drop= TRUE)
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
speciesbubbleplot_trawl_comname

Export figure

ggsave(filename = "Figures/speciesbubbleplot_trawl_abundance_comname.eps", plot = speciesbubbleplot_trawl_comname, units = c("in"), width = 6.75, height = 13, dpi = 300)

Bubble plots of CPUE

speciesbubbleplot_trawl_CPUE_comname <- ggplot(trawl_counts, aes(x = STATION_NA, y = fct_rev(CommonName), color = STATION_NA)) + 
  geom_point(aes(size = log10(CPUE), fill = STATION_NA), color = "black", pch = 21)+
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(log10(1), log10(2), log10(5), log10(10), log10(25), log10(100)), max_size = 6, labels = c("1","2","5","10","25","100"))+
  xlab("")+
  ylab("")+
  labs(size="CPUE", fill = "Station")+
  theme_bw() +
  scale_fill_brewer(palette="Paired") +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) +
  facet_grid(DATECODE~BAYSIDE, scales = "free", space = "free", drop= TRUE)
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
speciesbubbleplot_trawl_CPUE_comname

Looks good! Similar to “counts” figure but some adjustments that normalized for trawling time.

Export figure

ggsave(filename = "Figures/speciesbubbleplot_trawl_CPUE_comname.eps", plot = speciesbubbleplot_trawl_CPUE_comname, units = c("in"), width = 6.75, height = 13, dpi = 300)

Compare Trawl and eDNA

Prepare the data

First, remove the species from the trawls that are not targeted in the eDNA assay (invertebrates and elasmobranchs)

# import a list of the "OK" species that are targetted by MiFISh primers
mifish_spp <- read_excel("Trawl CPUE no elasmobranch_mod.xlsx",2)
mifish_spp

# filter rows from trawl_counts if the spp name doesn't match the MiFISh list
trawl_counts <- right_join(trawl_counts, mifish_spp, by = "CommonName")
trawl_counts

Then filter out stations from trawl data that were removed samples from eDNA analysis because of poor sequencing effort.

# Grab the eDNA sample IDs that remained after filtering
sampleIDs <- as_data_frame(sample_data(ps_no_elasmo)) %>% 
  select(SampleID, Datecode, Station)
Setting class(x) to multiple strings ("tbl_df", "tbl", ...); result will no longer be an S4 object
# Filter trawl_counts to only include those same samples
trawl_counts <- inner_join(trawl_counts, sampleIDs, by = c("DATECODE" = "Datecode", "STATION_NA" = "Station"))
trawl_counts

Make abundance table of each species across whole study

# sum hits across all dates in trawl
trawl_uniques <- trawl_counts %>%
  group_by(DATECODE, CommonName) %>%
  summarise(Trawl_Count = sum(count, na.rm=TRUE), Trawl_CPUE = sum(CPUE, na.rm = TRUE), Trawl_TLPUE = sum(TLPUE, na.rm = TRUE), Trawl_Allometric_Shedding = sum(TLxSF.PUE, na.rm = TRUE))
`summarise()` has grouped output by 'DATECODE'. You can override using the `.groups` argument.
trawl_uniques

# sum hits across all dates in eDNA
eDNA_uniques <- species_df_no_elasmo%>%
  group_by(Datecode, CommonName) %>%
  summarise(eDNA_RelAbun = sum(Abundance, na.rm=TRUE))
`summarise()` has grouped output by 'Datecode'. You can override using the `.groups` argument.
eDNA_uniques

# Combine into one dataframe
trawl_eDNA_abun_table <- full_join(trawl_uniques, eDNA_uniques, by=c("CommonName" = "CommonName", "DATECODE" = "Datecode"))

trawl_eDNA_abun_table

Species Richness

Count unique species across all stations, grouped by date, for each method, trawl& eDNA (use filtered trawl data so only comparing MiFISh spp to MiFISh spp).

Count total number of species from each method for each date

eDNA_richness <- tally(eDNA_uniques, name = "eDNA")
trawl_richness <- tally(trawl_uniques, name = "trawl")

speciesrichness <- full_join(eDNA_richness, trawl_richness, c("Datecode" = "DATECODE"))
speciesrichness <- pivot_longer(speciesrichness, !Datecode, names_to = "Method", values_to = "Richness")

speciesrichness$Datecode <- ymd(speciesrichness$Datecode) # convert to date format (better for plotting)

speciesrichness

Plot side-by-side

species_richness_plot <- ggplot(speciesrichness, aes(x =Datecode, y = Richness)) +
  geom_line(aes(color = Method), size = 3) +
  theme_bw() +
  xlab("") +
  ylab("Species Richness")

species_richness_plot

# export plot
ggsave(filename = "Figures/species_richness_plot.eps", plot = species_richness_plot, units = c("in"), width = 4, height = 3, dpi = 300)

Sum total number of species across all dates/ stations for entire study

species_sums_abun_table <- trawl_eDNA_abun_table %>%
  group_by(CommonName) %>%
  summarise(CPUE = sum(Trawl_CPUE, na.rm = TRUE), 
            "Total Length (TL) PUE" = sum(Trawl_TLPUE, na.rm = TRUE), 
            "TL * Shedding Factor PUE" = sum(Trawl_Allometric_Shedding, na.rm = TRUE), 
            eDNA = sum(eDNA_RelAbun, na.rm=TRUE)) %>%
  pivot_longer(!CommonName, names_to = "Method", values_to = "Abundance")
  
# turn zeroes to NA so they don't plot 
species_sums_abun_table <- na_if(species_sums_abun_table,0)

species_sums_abun_table

Species tally across whole study

For each species, plot side-by-side comparison of abundance (summed over whole study) using each method

# First create a custom color scale to make this pretty
myColors <- colorRampPalette(brewer.pal(11,"Spectral"))(40)
names(myColors) <- levels(unique(species_sums_abun_table$CommonName))
colScale <- scale_colour_manual(name = "CommonName",values = myColors)

species_abun_sum_plot <- ggplot(species_sums_abun_table, aes(x = Abundance, y = reorder(CommonName, Abundance, function(x){sum(x,na.rm = TRUE)}), color = CommonName)) +
  geom_point(size = 5) +
  facet_wrap(~factor(Method, levels = c('CPUE','Total Length (TL) PUE','TL * Shedding Factor PUE','eDNA')), scales = "free_x", ncol = 4) +
  theme_bw() +
  xlab("Abundance") +
  ylab("") + 
  colScale +
  theme(legend.position = "none")

species_abun_sum_plot

Export plot

ggsave(filename = "Figures/species_abun_sum_plot.eps", plot = species_abun_sum_plot, units = c("in"), width = 10, height = 6, dpi = 300)

Exploratory Analyses

Ordinations on eDNA

I will try PCoA, PCA (the Euclidean PCoA) and NMDS ordinations in combination with different tranformations and distance metrics in order to see which explain the most variance in the dataset.

  • NOTE- see this discussion and this paper on why CCA should not be used with CLR-transformed compositional data to explore correlations.

PCA

PCA is essentially a type of PCoA using the Euclidean distance matrix as input. When combined with a log-ratio transformation of the count table, this is deemed appropriate for compositional datasets. It is also recommended as a first step in exploratory analyses of sequencinging datasets.

First do a CLR, centered log ratio transformation of the absolute abundance data (after filtering), as suggested by Gloor et al. 2017

There were 20 warnings (use warnings() to see them)

Generate the PCA and visualize axes

# Generate a Principle Component Analysis (PCA) and evaluated based on the eigen decomposition from sample covariance matrix. 
lograt_pca <- prcomp(clr_asv_table_ps) 
# NOTE- this is equivalent to first making a Euclidean distance matrix using the CLR data table and then running a PCoA. A Euclidean distance matrix of a log-transformed data table = an Aitchison distance matrix. So this is equivalent to the compositional methods listed in Gloor et al.

# Visual representation with a screeplot
lograt_variances <- as.data.frame(lograt_pca$sdev^2/sum(lograt_pca$sdev^2)) %>% #Extract axes
  # Format to plot
  select(PercVar = 'lograt_pca$sdev^2/sum(lograt_pca$sdev^2)') %>% 
  rownames_to_column(var = "PCaxis") %>% 
  data.frame
head(lograt_variances)

# Plot screeplot
ggplot(lograt_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Log-Ratio PCA Screeplot, CLR Tranformation")

Total variance explained by first three axes= 15.8 + 10.7 + 10.1 = 36.6%. Since the second and third axes are similar, plot in 3D with 3 axes.

Visualize the PCA-

# Extract variances from the clr pca
pca_lograt_frame <- data.frame(lograt_pca$x) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pca_lograt_frame <- left_join(pca_lograt_frame, metadata, by = "SampleID")
head(pca_lograt_frame)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(lograt_variances[,2], digits = 4)*100

# Plotly - 3-D
pca_lograt <- plot_ly(pca_lograt_frame, type='scatter3d', mode='markers',
        x=~PC1,y=~PC2,z=~PC3,colors=~brewer.pal(11,'Paired'),
        color=~Station, symbols = c('circle','diamond'), symbol=~Bayside)%>%
  layout(font=list(size=12),
         title='CLR-Euclidean PCA',
         scene=list(xaxis=list(title=paste0('Co 2 ',eigenvalues[2],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    yaxis=list(title=paste0('Co 3 ',eigenvalues[3],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    zaxis=list(title=paste0('Co 1 ',eigenvalues[1],'%'),
                               showticklabels=FALSE,zerolinecolor='black')))
# pca_lograt

# save in "Embedded_figures" directory so that it can be hosted at Github and embedded in this notebook
withr::with_dir('Embedded_figures', htmlwidgets::saveWidget(as_widget(pca_lograt), file="pca_lograt_eDNA.html", selfcontained = F))

 

Summary The CLR-Euclidean PCA reveals there is some separation according to East vs West. The first 3 PCs only explain ~36% of the variance so keep going with different ordinations to see if there is a better representation

PCoA Jaccard

The more traditional approach to ordinations is to do a PCoA on a distance matrix such as Bray-Curtis, Jaccard, or Unifrac. When combined with a transformation, they become more appropriate for NGS data. One such common transformation is the Hellinger transformation.

The different distance matrices also tell you a few different things about the dataset so I will run try different one to try to see if I can tease those out.

Before calculating any distance matrix, do a transformation of the filtered count table. Hellinger transformation is the square root of the relative abundance, so calculate it based on the ps_ra object:

ps_hellinger <- transform_sample_counts(ps_ra_no_elasmo, function(x){sqrt(x)})

First, Jaccard, which builds the distance matrix based on presence/absence between samples. It does not take into account relative abundance of the taxa. Therefore this functions well for determining differences driven by rare taxa, which are weighed the same as abundant taxa.

jac_dmat<-vegdist(otu_table(ps_hellinger),method="jaccard") # Jaccard dist metric
pcoa_jac<-ape::pcoa(jac_dmat) # perform PCoA

# Extract variances from pcoa, from jaccard calculated dist. metric
jac_variances <- data.frame(pcoa_jac$values$Relative_eig) %>% 
  select(PercVar = 'pcoa_jac.values.Relative_eig') %>% 
  rownames_to_column(var = "PCaxis") %>% 
  data.frame
head(jac_variances)

# Make a screeplot
ggplot(jac_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Jaccard PCoA Screeplot")

The first two axes (19.0 + 9.7 = 28.7) are OK. But plot the first 3 axes since the 2nd and 3rd explain a similar amount of variance, (19.0 + 9.7 + 8.4 = 37.1% total variance explained)

Plot in 3D with Plotly

# Extract variances from the jaccard pcoa
pcoa_jac_df <- data.frame(pcoa_jac$vectors) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pcoa_jac_df <- left_join(pcoa_jac_df, metadata, by = "SampleID")
head(pcoa_jac_df)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(jac_variances[,2], digits = 4)*100

# Plotly - 3-D
pcoa_jaccard <- plot_ly(pcoa_jac_df, type='scatter3d', mode='markers',
        x=~Axis.2,y=~Axis.3,z=~Axis.1,colors=~brewer.pal(11,'Paired'),
        color=~Station, symbols = c('circle','diamond'), symbol=~Bayside)%>%
  layout(font=list(size=12),
         title='PCoA Jaccard Distance',
         scene=list(xaxis=list(title=paste0('Co 2 ',eigenvalues[2],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    yaxis=list(title=paste0('Co 3 ',eigenvalues[3],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    zaxis=list(title=paste0('Co 1 ',eigenvalues[1],'%'),
                               showticklabels=FALSE,zerolinecolor='black')))
# pcoa_jaccard

# save figure in "Embedded_figures" directory so that it can be hosted at Github and embedded in this notebook
withr::with_dir('Embedded_figures', htmlwidgets::saveWidget(as_widget(pcoa_jaccard), file="pcoa_jaccard_eDNA.html", selfcontained = F))

The Jaccard-PCoA shows some separation along axis 2 and axis 3 in East vs West differences. Very similar % variance explained to the PCA.

PCoA Bray Curtis

Next, try a Bray-Curtis distance matrix with PCoA, which builds the distance matrix based on presence/absence between samples and relative abundance differences. This ordination will represent well the differences in samples that are driven by taxa with high relative abundances.

NOTE: I need to use a correction here for negative eigenvalues. Read more here

bray_dmat<-vegdist(otu_table(ps_hellinger),method="bray") # Bray-Curtis dist metric
pcoa_bray<-ape::pcoa(bray_dmat) # perform PCoA in ape. But getting negative eigenvalues, so need to add correction. wcmdscale from base R also performs PCoA and can add cailliez correction
pcoa_bray <- wcmdscale(bray_dmat, eig = TRUE, add = "cailliez")

# check out summary of PCoA
eigenvals(pcoa_bray) %>%
  summary() -> ev
ev
Importance of components:
                        [,1]   [,2]    [,3]    [,4]    [,5]    [,6]    [,7]    [,8]    [,9]   [,10]   [,11]
Eigenvalue            6.3479 3.3005 2.85957 1.62805 1.33439 1.24855 1.00938 0.90346 0.87311 0.77992 0.71218
Proportion Explained  0.2112 0.1098 0.09512 0.05416 0.04439 0.04153 0.03358 0.03005 0.02904 0.02594 0.02369
Cumulative Proportion 0.2112 0.3210 0.41608 0.47023 0.51462 0.55615 0.58973 0.61978 0.64883 0.67477 0.69846
                        [,12]   [,13]   [,14]  [,15]   [,16]   [,17]   [,18]  [,19]   [,20]   [,21]   [,22]
Eigenvalue            0.65613 0.60610 0.54826 0.4989 0.44174 0.40567 0.39186 0.3667 0.34891 0.33706 0.33146
Proportion Explained  0.02183 0.02016 0.01824 0.0166 0.01469 0.01349 0.01304 0.0122 0.01161 0.01121 0.01103
Cumulative Proportion 0.72029 0.74045 0.75869 0.7753 0.78998 0.80347 0.81651 0.8287 0.84031 0.85152 0.86255
                        [,23]    [,24]    [,25]    [,26]    [,27]    [,28]    [,29]    [,30]    [,31]    [,32]
Eigenvalue            0.30199 0.284949 0.268734 0.255852 0.247955 0.239199 0.225418 0.217687 0.198673 0.194406
Proportion Explained  0.01005 0.009479 0.008939 0.008511 0.008248 0.007957 0.007498 0.007241 0.006609 0.006467
Cumulative Proportion 0.87259 0.882071 0.891010 0.899521 0.907769 0.915726 0.923224 0.930465 0.937074 0.943541
                         [,33]    [,34]    [,35]    [,36]    [,37]    [,38]    [,39]   [,40]   [,41]    [,42]
Eigenvalue            0.186256 0.166417 0.156276 0.152618 0.150887 0.139347 0.133210 0.12775 0.12414 0.110383
Proportion Explained  0.006196 0.005536 0.005198 0.005077 0.005019 0.004635 0.004431 0.00425 0.00413 0.003672
Cumulative Proportion 0.949737 0.955273 0.960471 0.965548 0.970567 0.975202 0.979634 0.98388 0.98801 0.991685
                         [,43]    [,44]    [,45]
Eigenvalue            0.106401 0.085699 0.057876
Proportion Explained  0.003539 0.002851 0.001925
Cumulative Proportion 0.995224 0.998075 1.000000
# extract variances and put in tibble
bray_variances <- NULL
for (i in 1:length(eigenvals(pcoa_bray))){
  bray_variances[i] <- eigenvals(pcoa_bray)[i]/sum(eigenvals(pcoa_bray))
}

# Extract variances from pcoa, from calculated dist. metric
bray_variances <- tibble(round(bray_variances,3)) %>%
  select(PercVar = 'round(bray_variances, 3)') %>%
  rownames_to_column(var = "PCaxis") %>%
  data.frame
head(bray_variances)

# Make a screeplot
ggplot(bray_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Bray-Curtis PCoA Screeplot")

The first two axes (21.1 + 11.0) are pretty good again but I am still going to experiment in the plot with the 3rd axis since it is similar to the second (9.5%; total variance explained = 41.6%)

Plot in 3D with Plotly

# Extract variances from the pcoa
pcoa_bray_df <- data.frame(pcoa_bray$points) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pcoa_bray_df <- left_join(pcoa_bray_df, metadata, by = "SampleID")
head(pcoa_bray_df)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(bray_variances[,2], digits = 4)*100

# Plotly - 3-D
pcoa_bray <- plot_ly(pcoa_bray_df, type='scatter3d', mode='markers', 
                     x=~Dim2, y=~Dim3, z=~Dim1, colors=~brewer.pal(11,'Paired'),
        color=~Station, symbols = c('circle','diamond'), symbol=~Bayside)%>%  
  layout(font=list(size=12),
         title='PCoA Bray-Curtis Distance',
         scene=list(xaxis=list(title=paste0('Co 2 ',eigenvalues[2],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    yaxis=list(title=paste0('Co 3 ',eigenvalues[3],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    zaxis=list(title=paste0('Co 1 ',eigenvalues[1],'%'),
                               showticklabels=FALSE,zerolinecolor='black')))
# pcoa_bray

# save in "Embedded_figures" directory so that it can be hosted at Github and embedded in this notebook
withr::with_dir('Embedded_figures', htmlwidgets::saveWidget(as_widget(pcoa_bray), file="pcoa_bray_eDNA.html", selfcontained = F))

These results along axes 1, 2, and 3 are similar to Jaccard, but there is more separation along axis 2, indicating that incorporating the differences in abundance helps explain more variance in the dataset. Total variance explained is highest so far.

NMDS Aitchison

Lastly, try a non-metric dimensional scaling ordination. PCA/PCoA are metric and attempt to rotate axes to fit the distance matrix distribution. An NMDS represents the data in 2-axes, by constraining the distribution of the points. Similar to above, this can be combined with different pre-treatment of the data.

First try the compositional approach, an NMDS on CLR-tranformed data using the Euclidean distances (aka Aitchison distance)

euc_dmat<-dist(clr_asv_table_ps, method = "euclidean") # Build the Aitchison distance matrix
euc_nmds <- metaMDS(euc_dmat, k=2, autotransform=FALSE) # Run the ordination
Run 0 stress 0.2095936 
Run 1 stress 0.230812 
Run 2 stress 0.2367264 
Run 3 stress 0.2096473 
... Procrustes: rmse 0.00967777  max resid 0.04766081 
Run 4 stress 0.2099595 
... Procrustes: rmse 0.01924625  max resid 0.05406619 
Run 5 stress 0.2365027 
Run 6 stress 0.2204494 
Run 7 stress 0.2096616 
... Procrustes: rmse 0.02491728  max resid 0.09196782 
Run 8 stress 0.2359077 
Run 9 stress 0.2112015 
Run 10 stress 0.2094389 
... New best solution
... Procrustes: rmse 0.01324573  max resid 0.05622952 
Run 11 stress 0.2152036 
Run 12 stress 0.2307547 
Run 13 stress 0.2143453 
Run 14 stress 0.2108001 
Run 15 stress 0.222541 
Run 16 stress 0.2138032 
Run 17 stress 0.2216532 
Run 18 stress 0.2228354 
Run 19 stress 0.211776 
Run 20 stress 0.212668 
*** No convergence -- monoMDS stopping criteria:
     1: no. of iterations >= maxit
    19: stress ratio > sratmax
euc_nmds$stress #Check the stress. Less than 0.1 is good. Less than 0.05 is better. This will be different each time, since it is iteratively finding a unique solution each time (although the should look similar)
[1] 0.2094389
# Extract points from nmds and merge into data frame with metadata 
euc_nmds_df <- data.frame(euc_nmds$points) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
euc_nmds_df <- left_join(euc_nmds_df, metadata, by = "SampleID")
head(euc_nmds_df)



## Plotting euclidean distance NMDS
nmds_aitch <- ggplot(euc_nmds_df,aes(x = MDS1, y = MDS2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = "NMDS 1", y = "NMDS 2", title = paste0('Aitchison Distance NMDS, Stress = ', round(euc_nmds$stress,2))) +
  coord_fixed(ratio = 1)

nmds_aitch

ggsave("figures/nmds_aitch_eDNA.eps",nmds_aitch, width = 7, height = 5, units = c("in"))

The above has a relatively high stress (>0.2) so should be interpreted with caution. But it does show some separation East vs West along NMDS 1.

NMDS Jacaard

Next try a Jaccard NMDS, which will represent differences in presence/absence among samples, emphasizing both abundant and rare taxa the same

jac_nmds <- metaMDS(jac_dmat, k=2, autotransform=FALSE) # Run the ordination. Distance matrix was already calculated above
Run 0 stress 0.1625677 
Run 1 stress 0.1849289 
Run 2 stress 0.1740042 
Run 3 stress 0.1907044 
Run 4 stress 0.1511937 
... New best solution
... Procrustes: rmse 0.08884194  max resid 0.3232323 
Run 5 stress 0.1495056 
... New best solution
... Procrustes: rmse 0.05343512  max resid 0.3128283 
Run 6 stress 0.1744721 
Run 7 stress 0.1779444 
Run 8 stress 0.1496721 
... Procrustes: rmse 0.05094231  max resid 0.3280469 
Run 9 stress 0.1699861 
Run 10 stress 0.1971628 
Run 11 stress 0.1496709 
... Procrustes: rmse 0.05096378  max resid 0.3286255 
Run 12 stress 0.1496706 
... Procrustes: rmse 0.05095018  max resid 0.3284939 
Run 13 stress 0.1662577 
Run 14 stress 0.1573862 
Run 15 stress 0.1496463 
... Procrustes: rmse 0.01251391  max resid 0.07479854 
Run 16 stress 0.1739596 
Run 17 stress 0.1496708 
... Procrustes: rmse 0.05095859  max resid 0.3285551 
Run 18 stress 0.1508378 
Run 19 stress 0.1744734 
Run 20 stress 0.1496306 
... Procrustes: rmse 0.01232568  max resid 0.07476111 
*** No convergence -- monoMDS stopping criteria:
    20: stress ratio > sratmax
jac_nmds$stress #Check the stress. Less than 0.1 is good. Less than 0.5 is better. This will be different each time, since it is iteratively finding a unique solution each time (although the should look similar)
[1] 0.1495056
# Extract points from nmds and merge into data frame with metadata 
jac_nmds_df <- data.frame(jac_nmds$points) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
jac_nmds_df <- left_join(jac_nmds_df, metadata, by = "SampleID")
head(jac_nmds_df)



## Plotting euclidean distance NMDS
nmds_jaccard <- ggplot(jac_nmds_df,aes(x = MDS1, y = MDS2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = "NMDS 1", y = "NMDS 2", title = paste0('Jaccard Distance NMDS, Stress = ', round(jac_nmds$stress,2))) +
  coord_fixed(ratio = 1)

nmds_jaccard

ggsave("figures/nmds_jaccard_eDNA.eps",nmds_jaccard, width = 7, height = 5, units = c("in"))

This is still a moderately high stress (>0.1) so should be interpreted with caution. Similar to Aitchison-distance nMDS but there is a little more separation of East vs West on NMDS 2 axis.

NMDS Bray Curtis

Next try a Bray-Curis NMDS, which will represent differences in presence/absence among samples and relative abundance, thus emphasizing impacts of highly abundant taxa.

bray_nmds <- metaMDS(bray_dmat, k=2, autotransform=FALSE) # Run the ordination. Distance matrix was already calculated above
Run 0 stress 0.1628464 
Run 1 stress 0.1671314 
Run 2 stress 0.1625677 
... New best solution
... Procrustes: rmse 0.02477208  max resid 0.1434767 
Run 3 stress 0.1512 
... New best solution
... Procrustes: rmse 0.08883111  max resid 0.3233087 
Run 4 stress 0.1889244 
Run 5 stress 0.1701812 
Run 6 stress 0.1573212 
Run 7 stress 0.1573862 
Run 8 stress 0.1945793 
Run 9 stress 0.1540051 
Run 10 stress 0.1568754 
Run 11 stress 0.1856705 
Run 12 stress 0.1832908 
Run 13 stress 0.1496461 
... New best solution
... Procrustes: rmse 0.05647177  max resid 0.3117366 
Run 14 stress 0.1496461 
... Procrustes: rmse 0.0001220942  max resid 0.000651973 
... Similar to previous best
Run 15 stress 0.1573375 
Run 16 stress 0.1660512 
Run 17 stress 0.1498107 
... Procrustes: rmse 0.05115396  max resid 0.3295766 
Run 18 stress 0.1787654 
Run 19 stress 0.149506 
... New best solution
... Procrustes: rmse 0.01269612  max resid 0.07602275 
Run 20 stress 0.1511933 
*** No convergence -- monoMDS stopping criteria:
    20: stress ratio > sratmax
bray_nmds$stress #Check the stress. Less than 0.1 is good. Less than 0.5 is better. This will be different each time, since it is iteratively finding a unique solution each time (although the should look similar)
[1] 0.149506
# Extract points from nmds and merge into data frame with metadata 
bray_nmds_df <- data.frame(bray_nmds$points) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
bray_nmds_df <- left_join(bray_nmds_df, metadata, by = "SampleID")
head(bray_nmds_df)



## Plotting euclidean distance NMDS
nmds_bray <- ggplot(bray_nmds_df,aes(x = MDS1, y = MDS2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = "NMDS 1", y = "NMDS 2", title = paste0('Bray-Curtis Distance NMDS, Stress = ', round(bray_nmds$stress,2))) +
  coord_fixed(ratio = 1)

nmds_bray

ggsave("figures/nmds_bray_eDNA.eps",nmds_bray, width = 7, height = 5, units = c("in"))

Very similar to Jaccard results. Moderately high stress (0.15)

eDNA Ordinations Summary

The ordination that explained the most variance in the eDNA dataset was the PCoA using the Bray-Curtis dissimilarity matrix after Hellinger transformation. This is similar to the approach presented in Lacoursière‐Roussel et al. 2018. Use this representation going forward.

  • Next: fit environmental vectors to this ordination to see which can be possibly explain some of the variation among samples and among species.

PCoA with Environmental Variables

Recreate, in 2D, the first two axes of the ordination (PCoA with Bray distance matrx/ Hellinger transformation) and use envfit from vegan to test and fit environmental variables.

If not making 3D plots, can do this directly in phyloseq ( example ). But phyloseq doesn’t allow for calliez correction of PCoA, so instead use example from G. Simpson to fit envfit on top of output from wcmdscale (PCoA in vegan).

Prepare the ordination variables

pcoa_bray <- wcmdscale(bray_dmat, eig = TRUE, add = "cailliez")

# trim metadata to remove samples that were removed during QC
metadata_ordinations <- metadata[metadata$SampleID %in% sample_data(ps_hellinger)$SampleID,]

# and remove repetitive metadata variables like Date/ Month/ Year/ Trawl #
metadata_ordinations <- select(metadata_ordinations, -"Year.Trawl#", -Date, -Month, -Year)

# sort metadata in same order as the distance matrix, bray_dmat
metadata_ordinations <- metadata_ordinations %>% arrange(factor(SampleID, levels = rownames(otu_table(ps_hellinger))))

# change the column name "Datecode" to "Date" (better for plotting)
colnames(metadata_ordinations)[2] <- "Date"

# fit environmental factors and save stats output
pcoa_bray_envfit <- envfit(pcoa_bray, metadata_ordinations, permutations = 1000)
capture.output(pcoa_bray_envfit, file = "stats_results/pcoa_bray_envfit_eDNA.txt")

# Signficant variables include Datecode (p = 0.023976), DO (p = 0.000999), Bayside (p = 0.004995), and Station (p = 0.041958)

# Make each of the interesting variables their own ordination variables for plotting (exclude Station. This will be a color variable anyway and it's not interesting)
pcoa_bray_envfit_date <- envfit(pcoa_bray~Date, metadata_ordinations, permutations = 1000)
pcoa_bray_envfit_DO <- envfit(pcoa_bray~DO, metadata_ordinations, permutations = 1000)
pcoa_bray_envfit_Bayside <- envfit(pcoa_bray~Bayside, metadata_ordinations, permutations = 1000)

Plot in 2D

# Convert characters in metadata to factors
metadata_ordinations <- metadata_ordinations %>% mutate_if(sapply(metadata_ordinations, is.character), as.factor)
with(as.data.frame(metadata_ordinations), levels(Station))
 [1] "CORMORANT POINT"  "DUNE ROAD"        "EAST MID BAY"     "INLET"            "LITTLE POND"     
 [6] "PINE NECK"        "PONQUOGUE BRIDGE" "SHINNECOCK HILLS" "SOUTH GRASS"      "WEST MID BAY"    
[11] "WEST TIANA"      
# Define plot parameters
colvec <- c(brewer.pal(11,'Paired')) # colors of stations
shapevec <- c(19,18) # shapes indicating Bayside

# Set up basic plot
par(xpd = T, mar = par()$mar + c(0,0,0,8)) # leave space to add legend. xpd = T allows legend to be outside of the plot
# Add the site scores
with(metadata_ordinations, plot(scores(pcoa_bray, display = "sites"), col = colvec[Station], pch = shapevec[Bayside], cex = 2, xlab = "Co1 21.1%", ylab = "Co2 11.0%"))
# Add the date vector
plot(pcoa_bray_envfit_date, p.max = 0.1, lwd = 2, col = "black")
# Add the DO vector
plot(pcoa_bray_envfit_DO, p.max = 0.1, lwd = 2, col = "black")
# Add the hulls indicating Bayside
with(metadata_ordinations, ordihull(pcoa_bray, Bayside, lwd = 2, lty = c(3,5), label = FALSE))
# Add legends
with(metadata_ordinations, legend(0.77, 0.6, legend = levels(Station), col = colvec, pch = c(19,18,19,19,19,18,19,19,19,18,18), bty = "n", pt.cex = 2, cex = .8))
legend(0.77, 0.8, c("EAST", "WEST"), col = c("black"), lty = c(3,5), lwd = 2, bty = "n", cex = .8) # Legend for Bayside hull lines- did this manually


# Export using base R/ vegan helpers
setEPS()
postscript("Figures/pcoa_bray_envfit_eDNA.eps", width = 7, height = 5)
par(xpd = T, mar = par()$mar + c(0,0,0,8))
with(metadata_ordinations, plot(scores(pcoa_bray, display = "sites"), col = colvec[Station], pch = shapevec[Bayside], cex = 2, xlab = "Co1 21.1%", ylab = "Co2 11.0%"))
plot(pcoa_bray_envfit_date, p.max = 0.1, lwd = 2, col = "black")
plot(pcoa_bray_envfit_DO, p.max = 0.1, lwd = 2, col = "black")
with(metadata_ordinations, ordihull(pcoa_bray, Bayside, lwd = 2, lty = c(3,5), label = FALSE))
with(metadata_ordinations, legend(0.77, 0.6, legend = levels(Station), col = colvec, pch = c(19,18,19,19,19,18,19,19,19,18,18), bty = "n", pt.cex = 2, cex = .8))
legend(0.77, 0.8, c("EAST", "WEST"), col = c("black"), lty = c(3,5), lwd = 2, bty = "n", cex = .8) 
dev.off()
quartz_off_screen 
                2 

Ordinations on Trawl Data

Prepare the trawl data

Does CPUE data need to be transformed before ordinations?

  • Sabel et al. 2020 do a log-transformation, then center and standardize counts before PCA
  • Gothues and Able 2020 also log transform ( log(y+1) ) and center and standardize to units of standard deviation before PCA. They also excluded rare species (less than 3 occurences at all sites)
  • Wegsheider et al. 2020 do no transformation, as far as I can tell, before PCoA
  • Mehdi et al. 2021 use a PCoA without transformation but with a Bray-Curtis dissimilarity matrix

Conclusion?

  • Center and standardize the log transformation of the counts for PCA
  • Use log-transformation of counts as input to Bray Curtis dissimilarity calculation before PCoA

Make a table of CPUE, TLPUE, and TLxSF.PUE in the style of OTU tables (samples in rows/ species in columms), of the CPUE data. Trawl_counts has already been filtered so that it only includes those samples and species that are relevant to the eDNA study

CPUE_table <- trawl_counts %>%
  as_data_frame()%>%
  select(SampleID, CommonName, CPUE) %>%
  pivot_wider(names_from = CommonName, values_from = CPUE) 
CPUE_table

TLPUE_table <- trawl_counts %>%
  as_data_frame()%>%
  select(SampleID, CommonName, TLPUE) %>%
  pivot_wider(names_from = CommonName, values_from = TLPUE) 
TLPUE_table

TLSF.PUE_table <- trawl_counts %>%
  as_data_frame()%>%
  select(SampleID, CommonName, TLxSF.PUE) %>%
  pivot_wider(names_from = CommonName, values_from = TLxSF.PUE) 
TLSF.PUE_table

Transformation by log transformation (log(x+1) in order to account for zeroes)

# set NAs to zeroes
CPUE_table[is.na(CPUE_table)] <- 0
TLPUE_table[is.na(TLPUE_table)] <- 0
TLSF.PUE_table[is.na(TLSF.PUE_table)] <- 0

# log transform
CPUE_table_transform <- CPUE_table
CPUE_table_transform[,2:length(CPUE_table)] <- log10(CPUE_table[,2:length(CPUE_table)]+1)

TLPUE_table_transform <- TLPUE_table
TLPUE_table_transform[,2:length(TLPUE_table)] <- log10(TLPUE_table[,2:length(TLPUE_table)]+1)

TLSF.PUE_table_transform <- TLSF.PUE_table
TLSF.PUE_table_transform[,2:length(TLSF.PUE_table)] <- log10(TLSF.PUE_table[,2:length(TLSF.PUE_table)]+1)

CPUE_table_transform
TLPUE_table_transform
TLSF.PUE_table_transform

Center (around mean) and standardize (by SD) the log-transformed CPUE data, similar to the references above.

CPUE_table_transform_cen_st <- CPUE_table_transform
CPUE_table_transform_cen_st[,2:length(CPUE_table_transform)] <- scale(CPUE_table_transform[,2:length(CPUE_table_transform)])

TLPUE_table_transform_cen_st <- TLPUE_table_transform
TLPUE_table_transform_cen_st[,2:length(TLPUE_table_transform)] <- scale(TLPUE_table_transform[,2:length(TLPUE_table_transform)])

TLSF.PUE_table_transform_cen_st <- TLSF.PUE_table_transform
TLSF.PUE_table_transform_cen_st[,2:length(TLSF.PUE_table_transform)] <- scale(TLSF.PUE_table_transform[,2:length(TLSF.PUE_table_transform)])


CPUE_table_transform_cen_st
TLPUE_table_transform_cen_st
TLSF.PUE_table_transform_cen_st

CPUE PCA

Generate the PCA and visualize axes

# convert to dataframe
CPUE_table_transform_cen_st <- data.frame(CPUE_table_transform_cen_st)
rownames(CPUE_table_transform_cen_st) <- c(CPUE_table_transform_cen_st$SampleID)
CPUE_table_transform_cen_st <- CPUE_table_transform_cen_st[,-1]

# run the PCA
log_transform_pca <- prcomp(CPUE_table_transform_cen_st)
                            
# Visual representation with a screeplot
log_transform_variances <- as.data.frame(log_transform_pca$sdev^2/sum(log_transform_pca$sdev^2)) %>% #Extract axes
  # Format to plot
  select(PercVar = 'log_transform_pca$sdev^2/sum(log_transform_pca$sdev^2)') %>% 
  rownames_to_column(var = "PCaxis") %>% 
  data.frame
head(log_transform_variances)

# Plot screeplot
ggplot(log_transform_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Log-Transformed PCA Screeplot, CPUE")

Total variance explained by first 2 axes = 17.5 + 11.3 = 28.8%. Total variance explained by first three axes= 17.5 + 11.3 + 10.6 = 39.4%.

Visualize the PCA in 3D:

# Extract variances from the pca
pca_logtransform_frame <- data.frame(log_transform_pca$x) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pca_logtransform_frame <- left_join(pca_logtransform_frame, metadata, by = "SampleID")
head(pca_logtransform_frame)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(log_transform_variances[,2], digits = 4)*100

# Plotly - 3-D
pca_log_transform <- plot_ly(pca_logtransform_frame, type='scatter3d', mode='markers',
        x=~PC1,y=~PC2,z=~PC3,colors=~brewer.pal(11,'Paired'),
        color=~Station, symbols = c('circle','diamond'), symbol=~Bayside)%>%
  layout(font=list(size=12),
         title='PCA on Log-transformed CPUE',
         scene=list(xaxis=list(title=paste0('Co 2 ',eigenvalues[2],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    yaxis=list(title=paste0('Co 3 ',eigenvalues[3],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    zaxis=list(title=paste0('Co 1 ',eigenvalues[1],'%'),
                               showticklabels=FALSE,zerolinecolor='black')))
# pca_log_transform

# save in "Embedded_figures" directory so that it can be hosted at Github and embedded in this notebook
withr::with_dir('Embedded_figures', htmlwidgets::saveWidget(as_widget(pca_log_transform), file="pca_log_transform_CPUE.html", selfcontained = F))

 

Also plot in 2D -

log_transform_frame_2D <- ggplot(pca_logtransform_frame,aes(x = PC1, y = PC2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = paste0('PC1 ', eigenvalues[1], '%'), y = paste0('PC2 ', eigenvalues[2], '%'), title = "PCA on Log-transformed CPUE") +
  coord_fixed(ratio = 1)

log_transform_frame_2D

ggsave("figures/pca_log_transform_2D.eps",log_transform_frame_2D, width = 7, height = 5, units = c("in"))

Summary: The percent variance explained by PCA is OK. Try a PCoA

TLPUE PCA

Generate the PCA and visualize axes

# convert to dataframe
TLPUE_table_transform_cen_st <- data.frame(TLPUE_table_transform_cen_st)
rownames(TLPUE_table_transform_cen_st) <- c(TLPUE_table_transform_cen_st$SampleID)
TLPUE_table_transform_cen_st <- TLPUE_table_transform_cen_st[,-1]

# run the PCA
log_transform_pca <- prcomp(TLPUE_table_transform_cen_st)
                            
# Visual representation with a screeplot
log_transform_variances <- as.data.frame(log_transform_pca$sdev^2/sum(log_transform_pca$sdev^2)) %>% #Extract axes
  # Format to plot
  select(PercVar = 'log_transform_pca$sdev^2/sum(log_transform_pca$sdev^2)') %>% 
  rownames_to_column(var = "PCaxis") %>% 
  data.frame
head(log_transform_variances)

# Plot screeplot
ggplot(log_transform_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Log-Transformed PCA Screeplot, TLPUE")

Total variance explained by first 2 axes = 17.4 + 10.8 = 28.8%. Total variance explained by first three axes= 17.4 + 10.8 + 9.6 = 37.8%. Very similar to the PCA on CPUE

Plot in 2D -

# Extract variances from the pca
pca_logtransform_frame <- data.frame(log_transform_pca$x) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pca_logtransform_frame <- left_join(pca_logtransform_frame, metadata, by = "SampleID")
head(pca_logtransform_frame)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(log_transform_variances[,2], digits = 4)*100

log_transform_frame_2D <- ggplot(pca_logtransform_frame,aes(x = PC1, y = PC2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = paste0('PC1 ', eigenvalues[1], '%'), y = paste0('PC2 ', eigenvalues[2], '%'), title = "PCA on Log-transformed Total Length PUE") +
  coord_fixed(ratio = 1)

log_transform_frame_2D

Summary: This is VERY similar to the distribution of points from the PCA on CPUE. Check the allometric data (total length) times the shedding factor (SF) just to be comprehensive…

TLxSF PUE PCA

Generate the PCA and visualize axes

# run the PCA
There were 50 or more warnings (use warnings() to see the first 50)
log_transform_pca <- prcomp(TLSF.PUE_table_transform_cen_st)
                            
# Visual representation with a screeplot
log_transform_variances <- as.data.frame(log_transform_pca$sdev^2/sum(log_transform_pca$sdev^2)) %>% #Extract axes
  # Format to plot
  select(PercVar = 'log_transform_pca$sdev^2/sum(log_transform_pca$sdev^2)') %>% 
  rownames_to_column(var = "PCaxis") %>% 
  data.frame
head(log_transform_variances)

# Plot screeplot
ggplot(log_transform_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Log-Transformed PCA Screeplot, TLPUE")

Total variance explained by first 2 axes = 17.5 + 10.7 = 28.2%. Total variance explained by first three axes= 17.4 + 10.8 + 9.7 = 37.9%. Very similar to the PCA on CPUE and on TLPUE

Plot in 2D -

# Extract variances from the pca
pca_logtransform_frame <- data.frame(log_transform_pca$x) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pca_logtransform_frame <- left_join(pca_logtransform_frame, metadata, by = "SampleID")
head(pca_logtransform_frame)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(log_transform_variances[,2], digits = 4)*100

log_transform_frame_2D <- ggplot(pca_logtransform_frame,aes(x = PC1, y = PC2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = paste0('PC1 ', eigenvalues[1], '%'), y = paste0('PC2 ', eigenvalues[2], '%'), title = "PCA on Log-transformed Total Length x Shedding Factor PUE") +
  coord_fixed(ratio = 1)

log_transform_frame_2D

Again, this is very similar to the PCA on CPUE and on TLPU. Stick with CPUE for simplicity.

CPUE PCoA Bray Curtis

Next, try a PCoA. Use the log-transformed abundance matrix to calculate a distance-matrix using the Bray-Curtis similarity metric. Then use this as input for PCoA

NOTE: Need to use a correction here for negative eigenvalues

# convert to dataframe
CPUE_table_transform <- data.frame(CPUE_table_transform)
rownames(CPUE_table_transform) <- c(CPUE_table$SampleID)
CPUE_table_transform <- CPUE_table_transform[,-1]

# Get Bray Curtis distance matrix from log-transformed CPUE data
bray_dmat<-vegdist(CPUE_table_transform,method="bray") 

# the normal PCoA results in negative eigenvalues, so need correction. use wcmdscale and add cailliez correction
pcoa_bray <- wcmdscale(bray_dmat, eig = TRUE, add = "cailliez")

# check out summary of PCoA
eigenvals(pcoa_bray) %>%
  summary() -> ev

# extract variances and put in tibble
bray_variances <- NULL
for (i in 1:length(eigenvals(pcoa_bray))){
  bray_variances[i] <- eigenvals(pcoa_bray)[i]/sum(eigenvals(pcoa_bray))
}

# Extract variances from pcoa, from calculated dist. metric
bray_variances <- tibble(round(bray_variances,3)) %>%
  select(PercVar = 'round(bray_variances, 3)') %>%
  rownames_to_column(var = "PCaxis") %>%
  data.frame
head(bray_variances)

# Make a screeplot
ggplot(bray_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Bray-Curtis PCoA Screeplot")

The first two axes (12.0+10.3+8.9) are not as good as PCA. Total variance explained by first 3 PCs = 31.2%)

Plot in 3D with Plotly

# Extract variances from the pcoa
pcoa_bray_df <- data.frame(pcoa_bray$points) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pcoa_bray_df <- left_join(pcoa_bray_df, metadata, by = "SampleID")
head(pcoa_bray_df)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(bray_variances[,2], digits = 4)*100

# Plotly - 3-D
pcoa_bray <- plot_ly(pcoa_bray_df, type='scatter3d', mode='markers', 
                     x=~Dim2, y=~Dim3, z=~Dim1, colors=~brewer.pal(11,'Paired'),
        color=~Station, symbols = c('circle','diamond'), symbol=~Bayside)%>%  
  layout(font=list(size=12),
         title='PCoA Bray-Curtis Distance on Log-Transformed CPUE',
         scene=list(xaxis=list(title=paste0('Co 2 ',eigenvalues[2],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    yaxis=list(title=paste0('Co 3 ',eigenvalues[3],'%'),
                               showticklabels=FALSE,zerolinecolor='black'),
                    zaxis=list(title=paste0('Co 1 ',eigenvalues[1],'%'),
                               showticklabels=FALSE,zerolinecolor='black')))
# pcoa_bray

# save in "Embedded_figures" directory so that it can be hosted at Github and embedded in this notebook
withr::with_dir('Embedded_figures', htmlwidgets::saveWidget(as_widget(pcoa_bray), file="pcoa_bray_CPUE.html", selfcontained = F))

Plot in 2D

pcoa_bray_2D <- ggplot(pcoa_bray_df,aes(x = Dim1, y = Dim2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = paste0('Co 1 ', eigenvalues[1], '%'), y = paste0('Co 2 ', eigenvalues[2], '%'), title = "PCoA on Log-transformed CPUE with Bray-Curtis Dissimilarity") +
  coord_fixed(ratio = 1)

pcoa_bray_2D

ggsave("figures/pcoa_bray_CPUE_2D.eps",pcoa_bray_2D, width = 7, height = 5, units = c("in"))

TLPUE PCoA Bray Curtis

# convert to dataframe
TLPUE_table_transform <- data.frame(TLPUE_table_transform)
rownames(TLPUE_table_transform) <- c(TLPUE_table$SampleID)
TLPUE_table_transform <- TLPUE_table_transform[,-1]

# Get Bray Curtis distance matrix from log-transformed CPUE data
bray_dmat<-vegdist(TLPUE_table_transform,method="bray") 

# the normal PCoA results in negative eigenvalues, so need correction. use wcmdscale and add cailliez correction
pcoa_bray <- wcmdscale(bray_dmat, eig = TRUE, add = "cailliez")

# check out summary of PCoA
eigenvals(pcoa_bray) %>%
  summary() -> ev

# extract variances and put in tibble
bray_variances <- NULL
for (i in 1:length(eigenvals(pcoa_bray))){
  bray_variances[i] <- eigenvals(pcoa_bray)[i]/sum(eigenvals(pcoa_bray))
}

# Extract variances from pcoa, from calculated dist. metric
bray_variances <- tibble(round(bray_variances,3)) %>%
  select(PercVar = 'round(bray_variances, 3)') %>%
  rownames_to_column(var = "PCaxis") %>%
  data.frame
head(bray_variances)

# Make a screeplot
ggplot(bray_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Bray-Curtis PCoA Screeplot")

The first two axes (14.7+10.3+9.1) are not as good as PCA. Total variance explained by first 3 PCs = 34.1%)

Plot in 2D

# Extract variances from the pcoa
pcoa_bray_df <- data.frame(pcoa_bray$points) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pcoa_bray_df <- left_join(pcoa_bray_df, metadata, by = "SampleID")
head(pcoa_bray_df)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(bray_variances[,2], digits = 4)*100

pcoa_bray_2D <- ggplot(pcoa_bray_df,aes(x = Dim1, y = Dim2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = paste0('Co 1 ', eigenvalues[1], '%'), y = paste0('Co 2 ', eigenvalues[2], '%'), title = "PCoA on Log-transformed Total Length PUE, with Bray-Curtis Dissimilarity") +
  coord_fixed(ratio = 1)

pcoa_bray_2D

The above is very similar to the Bray Curtis/ PCoA on CPUE (with Axis 2 flipped).

TLxSF PUE PCoA Bray Curtis

# Get Bray Curtis distance matrix from log-transformed CPUE data
bray_dmat<-vegdist(TLSF.PUE_table_transform,method="bray") 

# the normal PCoA results in negative eigenvalues, so need correction. use wcmdscale and add cailliez correction
pcoa_bray <- wcmdscale(bray_dmat, eig = TRUE, add = "cailliez")

# check out summary of PCoA
eigenvals(pcoa_bray) %>%
  summary() -> ev

# extract variances and put in tibble
bray_variances <- NULL
for (i in 1:length(eigenvals(pcoa_bray))){
  bray_variances[i] <- eigenvals(pcoa_bray)[i]/sum(eigenvals(pcoa_bray))
}

# Extract variances from pcoa, from calculated dist. metric
bray_variances <- tibble(round(bray_variances,3)) %>%
  select(PercVar = 'round(bray_variances, 3)') %>%
  rownames_to_column(var = "PCaxis") %>%
  data.frame
head(bray_variances)


# Make a screeplot
ggplot(bray_variances, aes(x = as.numeric(PCaxis), y = PercVar)) + 
  geom_bar(stat = "identity", fill = "grey", color = "black") +
  theme_minimal() +
  theme(axis.title = element_text(color = "black", face = "bold", size = 10),
        axis.text.y = element_text(color = "black", face = "bold"),
        axis.text.x = element_blank()) +
  labs(x = "PC axis", y = "% Variance", title = "Bray-Curtis PCoA Screeplot")

The first two axes (15.9+10.9+9.5) are not as good as PCA. Total variance explained by first 3 PCs = 36.3%)

Plot in 2D

# Extract variances from the pcoa
pcoa_bray_df <- data.frame(pcoa_bray$points) %>% 
  rownames_to_column(var = "SampleID")

# Merge metadata into the pcoa data table
pcoa_bray_df <- left_join(pcoa_bray_df, metadata, by = "SampleID")
head(pcoa_bray_df)

# Select eigenvalues from dataframe, round to 4 places and multiply by 100 for plotting. These will be the axes for the 3-D plot
eigenvalues<-round(bray_variances[,2], digits = 4)*100

pcoa_bray_2D <- ggplot(pcoa_bray_df,aes(x = Dim1, y = Dim2, color = Station, shape = Bayside)) +
  geom_point(size = 4) +
  scale_color_brewer(palette="Paired") +
  theme_bw() +
  labs(x = paste0('Co 1 ', eigenvalues[1], '%'), y = paste0('Co 2 ', eigenvalues[2], '%'), title = "PCoA on Log-transformed Total Length x Shedding Factor PUE, with Bray-Curtis Dissimilarity") +
  coord_fixed(ratio = 1)

pcoa_bray_2D

The above is very similar to the Bray Curtis/ PCoA on CPUE (with Axis 2 flipped) and on TPUE. Just continue with CPUE PCoA for simplicity.

CPUE Ordinations Summary

Both the PCA and PCoA explain similar percent variances using the first two axes (28.8% for PCA and 22.3%) for PCoA. Continue with both for the environmental fit for now to see if they lead to different interpretations.

Ordinations with Environmental Variables

CPUE PCA with Environmental Variables

# re-run the PCA
log_transform_pca <- prcomp(CPUE_table_transform_cen_st)

# trim metadata to remove samples that are not in CPUE data table
metadata_ordinations <- metadata[metadata$SampleID %in% rownames(log_transform_pca$x),]

# and remove repetitive metadata variables like Date/ Month/ Year/ Trawl #
metadata_ordinations <- select(metadata_ordinations, -"Year.Trawl#", -Date, -Month, -Year)

# sort metadata in same order as the pca matrix
metadata_ordinations <- metadata_ordinations %>% arrange(factor(SampleID, levels = rownames(log_transform_pca$x)))

# change the column name "Datecode" to "Date" (better for plotting)
colnames(metadata_ordinations)[2] <- "Date"

# fit environmental factors and save stats output
log_transform_pca_envfit <- envfit(log_transform_pca, metadata_ordinations, permutations = 1000)
capture.output(log_transform_pca_envfit, file = "stats_results/log_transform_pca_envfit_CPUE.txt")

## The only signficant variable is Station (p = 0.004995)

# Make an ordination object out of the envfit with sig variable
log_transform_pca_envfit_station <- envfit(log_transform_pca~Station, metadata_ordinations, permutations = 1000)

Plot in 2D. Actually didn’t put any vectors in this plot because the only significant variable, station, is already color-coded and there are 11 stations so vectors would be messy.

# Convert characters in metadata to factors
metadata_ordinations <- metadata_ordinations %>% mutate_if(sapply(metadata_ordinations, is.character), as.factor)
with(as.data.frame(metadata_ordinations), levels(Station))
 [1] "CORMORANT POINT"  "DUNE ROAD"        "EAST MID BAY"     "INLET"            "LITTLE POND"     
 [6] "PINE NECK"        "PONQUOGUE BRIDGE" "SHINNECOCK HILLS" "SOUTH GRASS"      "WEST MID BAY"    
[11] "WEST TIANA"      
# Define plot parameters
colvec <- c(brewer.pal(11,'Paired')) # colors of stations
shapevec <- c(19,18) # shapes indicating Bayside

# Set up basic plot
par(xpd = T, mar = par()$mar + c(0,0,0,8.5)) # leave space to add legend. xpd = T allows legend to be outside of the plot
# Add the site scores
with(metadata_ordinations, plot(scores(log_transform_pca, display = "sites"), col = colvec[Station], pch = shapevec[Bayside], cex = 2, xlab = "Co1 17.5%", ylab = "Co2 11.3%"))
# Add the hulls indicating Bayside
with(metadata_ordinations, ordihull(log_transform_pca, Bayside, lwd = 2, lty = c(3,5), label = FALSE))
# Add legends
with(metadata_ordinations, legend(1.8, 2, legend = levels(Station), col = colvec, pch = c(19,18,19,19,19,18,19,19,19,18,18), bty = "n", pt.cex = 2, cex = .8))
legend(1.8, 5, c("EAST", "WEST"), col = c("black"), lty = c(3,5), lwd = 2, bty = "n", cex = .8) # Legend for Bayside hull lines- did this manually



# Export using base R/ vegan helpers
setEPS()
postscript("Figures/pca_log_transform_CPUE_envfit.eps", width = 7, height = 5)
par(xpd = T, mar = par()$mar + c(0,0,0,8.5))
with(metadata_ordinations, plot(scores(log_transform_pca, display = "sites"), col = colvec[Station], pch = shapevec[Bayside], cex = 2, xlab = "Co1 17.5%", ylab = "Co2 11.3%"))
with(metadata_ordinations, ordihull(log_transform_pca, Bayside, lwd = 2, lty = c(3,5), label = FALSE))
with(metadata_ordinations, legend(1.8, 2, legend = levels(Station), col = colvec, pch = c(19,18,19,19,19,18,19,19,19,18,18), bty = "n", pt.cex = 2, cex = .8))
legend(1.8, 5, c("EAST", "WEST"), col = c("black"), lty = c(3,5), lwd = 2, bty = "n", cex = .8)
dev.off()
quartz_off_screen 
                3 

CPUE PCoA with Environmental Variables

There were 32 warnings (use warnings() to see them)
# Make an ordination object out of the envfit with sig variables
pcoa_bray_envfit_Date <- envfit(pcoa_bray~Date, metadata_ordinations, permutations = 1000)
pcoa_bray_envfit_Bayside <- envfit(pcoa_bray~Bayside, metadata_ordinations, permutations = 1000)
pcoa_bray_envfit_station <- envfit(pcoa_bray~Station, metadata_ordinations, permutations = 1000)

Plot in 2D. Actually didn’t put any vectors in this plot because the only significant variable, station, is already color-coded and there are 11 stations so vectors would be messy.

# Convert characters in metadata to factors
metadata_ordinations <- metadata_ordinations %>% mutate_if(sapply(metadata_ordinations, is.character), as.factor)
with(as.data.frame(metadata_ordinations), levels(Station))
 [1] "CORMORANT POINT"  "DUNE ROAD"        "EAST MID BAY"     "INLET"            "LITTLE POND"     
 [6] "PINE NECK"        "PONQUOGUE BRIDGE" "SHINNECOCK HILLS" "SOUTH GRASS"      "WEST MID BAY"    
[11] "WEST TIANA"      
# Define plot parameters
colvec <- c(brewer.pal(11,'Paired')) # colors of stations
shapevec <- c(19,18) # shapes indicating Bayside

# Set up basic plot
par(xpd = T, mar = par()$mar + c(0,0,0,8)) # leave space to add legend. xpd = T allows legend to be outside of the plot
# Add the site scores
with(metadata_ordinations, plot(scores(pcoa_bray, display = "sites"), col = colvec[Station], pch = shapevec[Bayside], cex = 2, xlab = "Co1 12.0%", ylab = "Co2 10.3%"))
# Add the date vector
plot(pcoa_bray_envfit_Date, p.max = 0.1, lwd = 2, col = "black")
# Add the hulls indicating Bayside
with(metadata_ordinations, ordihull(pcoa_bray, Bayside, lwd = 2, lty = c(3,5), label = FALSE))
# Add legends
with(metadata_ordinations, legend(0.77, 0.3, legend = levels(Station), col = colvec, pch = c(19,18,19,19,19,18,19,19,19,18,18), bty = "n", pt.cex = 2, cex = .8))
legend(0.77, 0.5, c("EAST", "WEST"), col = c("black"), lty = c(3,5), lwd = 2, bty = "n", cex = .8) # Legend for Bayside hull lines- did this manually


# Export using base R/ vegan helpers
setEPS()
postscript("Figures/pcoa_bray_envfit_CPUE.eps", width = 7, height = 5)
par(xpd = T, mar = par()$mar + c(0,0,0,8)) 
with(metadata_ordinations, plot(scores(pcoa_bray, display = "sites"), col = colvec[Station], pch = shapevec[Bayside], cex = 2, xlab = "Co1 12.0%", ylab = "Co2 10.3%"))
plot(pcoa_bray_envfit_Date, p.max = 0.1, lwd = 2, col = "black")
with(metadata_ordinations, ordihull(pcoa_bray, Bayside, lwd = 2, lty = c(3,5), label = FALSE))
with(metadata_ordinations, legend(0.77, 0.3, legend = levels(Station), col = colvec, pch = c(19,18,19,19,19,18,19,19,19,18,18), bty = "n", pt.cex = 2, cex = .8))
legend(0.77, 0.5, c("EAST", "WEST"), col = c("black"), lty = c(3,5), lwd = 2, bty = "n", cex = .8) 
dev.off()
quartz_off_screen 
                4 

Ordination Summary

For the trawl data, ordinations of the allometric corrections (sum of total length and sum total length x shedding factor) are VERY similar to the results using just CPUE. So, for simplicity, just present the CPUE.

The Bray-Curtis PCoA on the eDNA and trawl CPUE data are similar but not identical. Each ordination reveals that Bayside and Date are important correlated variables. The eDNA ordination was also sensitive to DO, which did not appear in the CPUE ordination. The first two axes of both ordinations explain some, but not a majority of the variance (32.1% for eDNA and 22.3% for CPUE).

LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyBSZXN1bHRzIGZyb20gREFEQTIgdG8gbWFrZSBwbG90cywgZG8gc29tZSBzdGF0aXN0aWNzIgphdXRob3I6ICJMaXogU3V0ZXIiCmRhdGU6ICJNYXkgMjYsIDIwMjEiCm91dHB1dDogaHRtbF9ub3RlYm9vawplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCltMaW5rXShodHRwczovL2xpenN1dGVyLmdpdGh1Yi5pby9maWxlcy9FY29sX2FuYWx5c2lzLm5iLmh0bWwpIHRvIG5vdGVib29rICAKCltMaW5rXShodHRwczovL2dpdGh1Yi5jb20vbGl6c3V0ZXIvU0NNX2VETkEpIHRvIGdpdGh1YiByZXBvLgoKCjxici8+CgojIFRhYmxlIG9mIENvbnRlbnRzCi0gW0xvYWQgcGFja2FnZXNdKCNsb2FkLXBhY2thZ2VzKQotIFtJbXBvcnQgYW5kIHByZXBhcmUgdGhlIGRhdGEgZnJvbSBlRE5BXSgjaW1wb3J0LWFuZC1wcmVwYXJlLXRoZS1kYXRhLWZyb20tZWRuYSkKICAtIFtJbXBvcnQgbWV0YWRhdGFdKCNpbXBvcnQtbWV0YWRhdGEpCiAgLSBbSW1wb3J0IERBREEyIHJlc3VsdHNdKCNpbXBvcnQtZGFkYTItcmVzdWx0cykKICAtIFtNYWtlIHBoeWxvc2VxIG9iamVjdF0oI21ha2UtcGh5bG9zZXEtb2JqZWN0KQotIFtRQyBhbmQgZmlsdGVyaW5nIGVETkEgZGF0YXNldF0oI3FjLWFuZC1maWx0ZXJpbmctZWRuYS1kYXRhc2V0KQogIC0gW1JhcmVmYWN0aW9uIGN1cnZlc10oI3JhcmVmYWN0aW9uLWN1cnZlcykKICAtIFtGaWx0ZXJpbmddKCNmaWx0ZXJpbmcpCiAgLSBbQ2hlY2sgc2VxdWVuY2luZyBlZmZvcnRdKCNjaGVjay1zZXF1ZW5jaW5nLWVmZm9ydCkKLSBbQWJ1bmRhbmNlIHBsb3RzIGVETkFdKCNhYnVuZGFuY2UtcGxvdHMtZWRuYSkKICAtIFtBYnVuZGFuY2UgYXQgZmFtaWx5IGxldmVsXSgjYWJ1bmRhbmNlLWF0LWZhbWlseS1sZXZlbCkKICAtIFtCdWJibGUgcGxvdHNdKCNidWJibGUtcGxvdHMpCiAgICAtIFtCdWJibGUgcGxvdCB3aXRob3V0IEVsYXNtb2JyYW5jaHNdKCNidWJibGUtcGxvdC13aXRob3V0LWVsYXNtb2JyYW5jaHMpCi0gW0ltcG9ydCBhbmQgcHJlcGFyZSB0aGUgZGF0YSBmcm9tIHRyYXdsc10oI2ltcG9ydC1hbmQtcHJlcGFyZS10aGUtZGF0YS1mcm9tLXRyYXdscykKICAtIFtJbXBvcnQgVHJhd2wgQ291bnQgRGF0YV0oI2ltcG9ydC10cmF3bC1jb3VudC1kYXRhKQotIFtBYnVuZGFuY2UgcGxvdHMgVHJhd2xzXSgjYWJ1bmRhbmNlLXBsb3RzLXRyYXdscykKICAtIFtCdWJibGUgcGxvdHMgb2YgQ291bnRzXSgjYnViYmxlLXBsb3RzLW9mLWNvdW50cykKICAtIFtCdWJibGUgcGxvdHMgb2YgQ1BVRV0oI2J1YmJsZS1wbG90cy1vZi1jcHVlKQotIFtDb21wYXJlIFRyYXdsIGFuZCBlRE5BXSgjY29tcGFyZS10cmF3bC1hbmQtZWRuYSkKICAtIFtQcmVwYXJlIHRoZSBkYXRhXSgjcHJlcGFyZS10aGUtZGF0YSkKICAtIFtTcGVjaWVzIFJpY2huZXNzXSgjc3BlY2llcy1yaWNobmVzcykKICAtIFtTcGVjaWVzIHRhbGx5IGFjcm9zcyB3aG9sZSBzdHVkeV0oI3NwZWNpZXMtdGFsbHktYWNyb3NzLXdob2xlLXN0dWR5KQotIFtFeHBsb3JhdG9yeSBBbmFseXNlc10oI2V4cGxvcmF0b3J5LWFuYWx5c2VzKQogIC0gW09yZGluYXRpb25zIG9uIGVETkFdKCNvcmRpbmF0aW9ucy1vbi1lZG5hKQogICAgICAtIFtQQ0FdKCNwY2EpCiAgICAgIC0gW1BDb0EgSmFjY2FyZF0oI3Bjb2EtamFjY2FyZCkKICAgICAgLSBbUENvQSBCcmF5IEN1cnRpc10oI3Bjb2EtYnJheS1jdXJ0aXMpCiAgICAgIC0gW05NRFMgQWl0Y2hpc29uXSgjbm1kcy1haXRjaGlzb24pCiAgICAgIC0gW05NRFMgSmFjYWFyZF0oI25tZHMtamFjYWFyZCkKICAgICAgLSBbTk1EUyBCcmF5IEN1cnRpc10oI25tZHMtYnJheS1jdXJ0aXMpCiAgICAgIC0gW2VETkEgT3JkaW5hdGlvbnMgU3VtbWFyeV0oI2VkbmEtb3JkaW5hdGlvbnMtc3VtbWFyeSkKICAgICAgLSBbUENvQSB3aXRoIEVudmlyb25tZW50YWwgVmFyaWFibGVzXSgjcGNvYS13aXRoLWVudmlyb25tZW50YWwtdmFyaWFibGVzKQogIC0gW09yZGluYXRpb25zIG9uIFRyYXdsIERhdGFdKCNvcmRpbmF0aW9ucy1vbi10cmF3bC1kYXRhKQogICAgLSBbUHJlcGFyZSB0aGUgVHJhd2wgRGF0YV0oI3ByZXBhcmUtdGhlLXRyYXdsLWRhdGEpCiAgICAtIFtDUFVFIFBDQV0oI2NwdWUtcGNhKQogICAgLSBbVG90YWwgTGVuZ3RoIChUTCkgUFVFIFBDQV0oI3RscHVlLXBjYSkKICAgIC0gW1RMeFNGIChzaGVkZGluZyBmYWN0b3IpIFBVRSBQQ0FdKCN0bHhzZi1wdWUtcGNhKQogICAgLSBbQ1BVRSBQQ29BIEJyYXkgQ3VydGlzXSgjY3B1ZS1wY29hLWJyYXktY3VydGlzKQogICAgLSBbVEwgUFVFIFBDb0EgQnJheSBDdXJ0aXNdKCN0bHB1ZS1wY29hLWJyYXktY3VydGlzKQogICAgLSBbVEx4U0YgUFVFIFBDb0EgQnJheSBDdXJ0aXNdKCN0bHhzZi1wdWUtcGNvYS1icmF5LWN1cnRpcykKICAgIC0gW0NQVUUgT3JkaW5hdGlvbnMgU3VtbWFyeV0oI2NwdWUtb3JkaW5hdGlvbnMtc3VtbWFyeSkKICAgIC0gW09yZGluYXRpb25zIHdpdGggRW52aXJvbm1lbnRhbCBWYXJpYWJsZXNdKCNvcmRpbmF0aW9ucy13aXRoLWVudmlyb25tZW50YWwtdmFyaWFibGVzKQogICAgICAtIFtDUFVFIFBDQSB3aXRoIEVudmlyb25tZW50YWwgVmFyaWFibGVzXSgjY3B1ZS1wY2Etd2l0aC1lbnZpcm9ubWVudGFsLXZhcmlhYmxlcykKICAgICAgLSBbQ1BVRSBQQ29BIHdpdGggRW52aXJvbm1lbnRhbCBWYXJpYWJsZXNdKCNjcHVlLXBjb2Etd2l0aC1lbnZpcm9ubWVudGFsLXZhcmlhYmxlcykKICAtIFtPcmRpbmF0aW9uIFN1bW1hcnldKCNvcmRpbmF0aW9uLXN1bW1hcnkpCgogICAgICAKPGJyLz4KCgoKIyBMb2FkIHBhY2thZ2VzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KHBoeWxvc2VxKQpsaWJyYXJ5KEJpb3N0cmluZ3MpCiNsaWJyYXJ5KHBoYW5nb3JuKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHNlcWlucikKI2xpYnJhcnkoZGVjb250YW0pCmxpYnJhcnkoYXBlKQpsaWJyYXJ5KHZlZ2FuKQojbGlicmFyeShwaGlscikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkobWljcm9iaW9tZSkKI2xpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KGNvbXBvc2l0aW9ucyk7CiNsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGh0bWx3aWRnZXRzKQpsaWJyYXJ5KHdpdGhyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKYGBgCgojICBJbXBvcnQgYW5kIHByZXBhcmUgdGhlIGRhdGEgZnJvbSBlRE5BIAoKIyMgSW1wb3J0IG1ldGFkYXRhCmBgYHtyfQptZXRhZGF0YSA8LSByZWFkX2Nzdigic2FtcGxlX2RhdGEuY3N2IikKYGBgCgoKIyMgSW1wb3J0IERBREEyIHJlc3VsdHMKSW1wb3J0IGNvdW50IHRhYmxlIGFuZCB0YXhvbm9teSBmaWxlLiBJIHNsaWdodGx5IG1vZGlmaWVkIG90dXRhYmxlLmNzdiBpbiBFeGNlbCB0byBvdHV0YWJsZV9tb2QuY3N2IHRvIHJlbW92ZSB0aGUgcXVvdGVzIGFyb3VuZCBzZXEgbmFtZXMgYW5kIHB1dCBOQSBwbGFjZWhvZGVyIGFzIGZpcnN0IGNvbCBuYW1lICh3aGljaCB3YXMgYWJvdmUgcm93IG5hbWVzKQpgYGB7cn0KIyBJbXBvcnQgQ291bnQgdGFibGUuIFNraXAgZmlyc3Qgcm93IG9mIHRzdiBmaWxlLCB3aGljaCBpcyBqdXN0IHNvbWUgdGV4dApjb3VudF90YWJsZSA8LSByZWFkX3RhYmxlMigicmVzdWx0cy9vdHV0YWJsZV9tb2QuY3N2IikKY29sbmFtZXMoY291bnRfdGFibGUpWzFdIDwtICJTYW1wbGVJRCIKCiMgSW1wb3J0IHRheG9ub215IG9mIEFTVnMKdGF4b25vbXkgPC0gcmVhZF9jc3YoZmlsZT0icmVzdWx0cy90YXhfc2VxdWVuY2VzX2JsYXN0X3RheG9ub215LmNzdiIpCiMgcmVtb3ZlIGZpcnN0IGNvbCBvZiBzZXF1ZW50aWFsIG51bWJlcnMKdGF4b25vbXlbLDFdIDwtIE5VTEwKIyBmaWx0ZXIgb3V0IHNlcXVlbmNlcyB3aXRoIGxvdyBQSUQgKHJlY29tbWVuZGVkIGJ5IFNhcmEpCnRheG9ub215IDwtIGZpbHRlcih0YXhvbm9teSwgUElEID4gOTIpCgojIHJlbW92ZSBCTEFTVCBtZXRhZGF0YSBhbmQganVzdCByZXRhaW4gdGF4b25vbXkgKG5lY2Vzc2FyeSBmb3IgZnVydGhlciBwcm9jZXNzaW5nIGJlbG93KQpkcm9wLmNvbHMgPC0gYyhjb2xuYW1lcyh0YXhvbm9teSlbMjo5XSwnUmVmU2VxX1RheF9JRF8xJykKdGF4b25vbXkgPC0gIHNlbGVjdCh0YXhvbm9teSwgLW9uZV9vZihkcm9wLmNvbHMpKQoKCiMgQW5kIGltcG9ydCB0aGUgQ29tbW9uIG5hbWVzLCBhcyBjdXJhdGVkIGJ5IFNhcmEuIEpvaW4gdG8gdGF4b25vbXkKY29tbW9ubmFtZXMgPC0gcmVhZF9leGNlbCgiVHJhd2xzIE1BU1RFUiAyMDIwIF9tb2RfRVMueGxzeCIsNykKY29tbW9ubmFtZXMKCnRheG9ub215IDwtIGxlZnRfam9pbih0YXhvbm9teSwgY29tbW9ubmFtZXMsIGJ5ID0gIkFTVl9JRCIpCnRheG9ub215CgpgYGAKRmlsdGVyaW5nIHJlbW92ZWQgc2VxcyAxMTAsIDMzMiAoR29iaW9zb21hIGdpbnNidXJnaSBhbmQgQmVsb25lIGJlbG9uZSkKKk5vdGUgZm9yIFNhcmEqIHNob3VsZCB3ZSBjb25zaWRlciBzZXR0aW5nIHRoaXMgYXQgOTclIHdoaWNoIGlzIG1vcmUgcm9idXN0IGFuZCBzdGlsbCBsZWF2ZXMgMzM0IHVuaXF1ZSBBU1ZzIChyYXRoZXIgdGhhbiAzNzkgd2l0aCB0aGUgOTIlIGN1dG9mZiBpbiB0aGUgc2V0dGluZ3MgYWJvdmUpCgpQcmV2aWV3IGRhdGFzZXRzCmBgYHtyfQpjb3VudF90YWJsZQp0YXhvbm9teQptZXRhZGF0YQpgYGAKCgoKCiMjIE1ha2UgcGh5bG9zZXEgb2JqZWN0CgpJIHdhbnQgdG8gdXNlIHRoZSBwaHlsb3NlcSBwYWNrYWdlIGZvciBzb21lIHBsb3R0aW5nLyBzdGF0aXN0aWNzLCB3aGljaCBmaXJzdCByZXF1aXJlcyBtYWtpbmcgcGh5bG9zZXEgb2JqZWN0cyBvdXQgb2YgZWFjaCBvZiBpbnB1dCBkYXRhIHRhYmxlcy0gCgpgYGB7cn0KY291bnRfdGFibGVfbWF0cml4IDwtIGFzLm1hdHJpeChjb3VudF90YWJsZVssMjozOTJdKSAjIGNvbnZlcnQgY291bnQgdGFibGUgdG8gbWF0cml4LCBsZWF2aW5nIG91dCBjaGFyYWN0ZXIgY29sdW1uIG9mIHNhbXBsZSBJRApyb3duYW1lcyhjb3VudF90YWJsZV9tYXRyaXgpIDwtIGNvdW50X3RhYmxlJFNhbXBsZUlEICMgYWRkIGJhY2sgaW4gU2FtcGxlIElEcyBhcyByb3cgbmFtZXMKQVNWCT0Jb3R1X3RhYmxlKGNvdW50X3RhYmxlX21hdHJpeCwgdGF4YV9hcmVfcm93cyA9ICBGQUxTRSkKCnRheG9ub215X21hdHJpeCA8LSBhcy5tYXRyaXgodGF4b25vbXlbLDI6OV0pCnJvd25hbWVzKHRheG9ub215X21hdHJpeCkgPC0gdGF4b25vbXkkQVNWX0lEIApUQVgJPQl0YXhfdGFibGUodGF4b25vbXlfbWF0cml4KQoKIyBzZWxlY3Qgb25seSB0aGUgbWV0YWRhIHJvd3Mgd2l0aCBlRE5BIHNhbXBsZXMKbWV0YWRhdGFfZWRuYSA8LSBtZXRhZGF0YSAlPiUgZmlsdGVyKCFpcy5uYShTYW1wbGVJRCkpCgpNRVRBCT0Jc2FtcGxlX2RhdGEoZGF0YS5mcmFtZShtZXRhZGF0YV9lZG5hLCByb3cubmFtZXMgPSBtZXRhZGF0YV9lZG5hJGBTYW1wbGVJRGApKQpgYGAKCgpGaXJzdCBjaGVjayB0aGF0IHRoZSBpbnB1dHMgYXJlIGluIGNvbXBhdGlibGUgZm9ybWF0cyBieSBjaGVja2luZyBmb3IgQVNWIG5hbWVzIHdpdGggdGhlIHBoeWxvc2VxIGZ1bmN0aW9uLCB0YXhhX25hbWVzCmBgYHtyfQpoZWFkKHRheGFfbmFtZXMoVEFYKSkKaGVhZCh0YXhhX25hbWVzKEFTVikpCmBgYAoKQW5kIGNoZWNrIHNhbXBsZSBuYW1lcyB3ZXJlIGFsc28gZGV0ZWN0ZWQKYGBge3J9CiMgTW9kaWZ5IHRheGEgbmFtZXMgaW4gQVNWLCB3aGljaCBhcmUgZm9ybWF0dGVkIHdpdGggdGhlIHNhbXBsZSBJRCwgdW5kZXJzY29yLCBmYXN0cSBJRC4gRG9uJ3QgbmVlZCB0aGlzIGZhc3RxIElEIGFueW1vcmUgYW5kIHdhbnQgaXQgdG8gbWF0Y2ggdGhlIHNhbXBsZSBuYW1lcyBmcm9tIG1ldGFkYXRhCnNhbXBsZV9uYW1lcyhBU1YpIDwtICBzYW1wbGVfbmFtZXMoQVNWKSAlPiUKICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybiA9ICJfU1s6ZGlnaXQ6XSsiLHJlcGxhY2VtZW50ID0gIiIpCgoKaGVhZChzYW1wbGVfbmFtZXMoQVNWKSkKaGVhZChzYW1wbGVfbmFtZXMoTUVUQSkpCmBgYAoKQW5kIG1ha2UgdGhlIHBoeWxvc2VxIG9iamVjdApgYGB7cn0KcHMgPC0gcGh5bG9zZXEoQVNWLAlUQVgsCU1FVEEpCmBgYAoKCgojIFFDIGFuZCBmaWx0ZXJpbmcgZUROQSBkYXRhc2V0CgojIyBSYXJlZmFjdGlvbiBjdXJ2ZXMKCmBgYHtyfQpyYXJlY3VydmUob3R1X3RhYmxlKHBzKSwgc3RlcD01MCwgY2V4PTAuNSkKCiMgc2F2ZSBhcyAuZXBzCnNldEVQUygpCnBvc3RzY3JpcHQoIkZpZ3VyZXMvcmFyZWZhY3Rpb24uZXBzIikKcmFyZWN1cnZlKG90dV90YWJsZShwcyksIHN0ZXA9NTAsIGNleD0wLjUpCmRldi5vZmYoKQpgYGAKTW9zdCBzYW1wbGVzIGxvb2sgbGlrZSB0aGV5IHdlcmUgc2FtcGxlZCB0byBjb21wbGV0aW9uLiBCZSB3ZWFyeSBvZiBUM1MxMSwgVDFTMiwgYW5kIG1heWJlIFQ0UzUKCgojIyBGaWx0ZXJpbmcKCkNoZWNrIHNvbWUgZmVhdHVyZXMgb2YgdGhlIHBoeWxvc2VxIG9iamVjdApgYGB7cn0KcmFua19uYW1lcyhwcykKCnVuaXF1ZSh0YXhfdGFibGUocHMpWywgInN1cGVya2luZ2RvbSJdKQp1bmlxdWUodGF4X3RhYmxlKHBzKVssICJwaHlsdW0iXSkKdW5pcXVlKHRheF90YWJsZShwcylbLCAiY2xhc3MiXSkKYGBgCgpUaGVyZSBhcmUgc29tZSBBU1ZzIHdpdGggYE5BYCBhcyBzdXBlcmtpbmdkb20sIHBoeWx1bSwgb3IgY2xhc3MgYW5ub3RhdGlvbi0gZGVsZXRlIHRoZXNlLiAKCmBgYHtyfQpwcyA8LSBzdWJzZXRfdGF4YShwcywgIWlzLm5hKHN1cGVya2luZ2RvbSkgJiAhaXMubmEocGh5bHVtKSAmICFpcy5uYShjbGFzcykpCgp1bmlxdWUodGF4X3RhYmxlKHBzKVssICJzdXBlcmtpbmdkb20iXSkKdW5pcXVlKHRheF90YWJsZShwcylbLCAicGh5bHVtIl0pCnVuaXF1ZSh0YXhfdGFibGUocHMpWywgImNsYXNzIl0pCm5yb3codGF4X3RhYmxlKHBzKSkgIyBudW1iZXIgb2YgQVNWcyBsZWZ0CmBgYAozNzggQVNWcyBzdGlsbCByZW1haW4uLi4KCgpBbHNvIGNoZWNrIGNsYXNzIE1hbW1hbGlhLCB0byBzZWUgaWYgdGhleSBhcmUgY29udGFtaW5hdGlvbiBvciByZWFsOgpgYGB7cn0KdGF4X3RhYmxlKHN1YnNldF90YXhhKHBzLCBjbGFzcyA9PSAnTWFtbWFsaWEnKSkKYGBgClRoZXNlIGFyZSBodW1hbiwgd2lsZCBib2FyLCBjYXQgKGFoZW0uLi5jYXQgbGFkeSksIGFuZCBjYXR0bGUuIEFsbCBhcmUgY29udGFtaW5hdGlvbiBzbyBkZWxldGUgYWxsIE1hbW1hbGlhCgpgYGB7cn0KcHMgPC0gc3Vic2V0X3RheGEocHMsICFjbGFzcyA9PSAnTWFtbWFsaWEnKQp1bmlxdWUodGF4X3RhYmxlKHBzKVssICJjbGFzcyJdKQpgYGAKCk5leHQgY2hlY2sgdGhlICJJbnNlY3RhIiBlbnRyaWVzCmBgYHtyfQp0YXhfdGFibGUoc3Vic2V0X3RheGEocHMsIGNsYXNzID09ICdJbnNlY3RhJykpCmBgYAoKVGhlIG9ubGx5IEluc2VjdGEgaXMgTGluZXBpdGhlbWEgaHVtaWxlLCB3aGljaCBhcmUgYW50cyBzbyBkZWxldGUgdGhlc2UgdG9vLi4KYGBge3J9CnBzIDwtIHN1YnNldF90YXhhKHBzLCAhY2xhc3MgPT0gJ0luc2VjdGEnKQp1bmlxdWUodGF4X3RhYmxlKHBzKVssICJjbGFzcyJdKQpgYGAKCgojIyBDaGVjayBzZXF1ZW5jaW5nIGVmZm9ydAoKQ2hlY2sgb3ZlcmFsbCBob3cgbWFueSBBU1ZzIHRoZXJlIGFyZSBwZXIgc2FtcGxlCgpgYGB7cn0KIyBGaXJzdCBhZ2xvbWVyYXRlIHRoZSBBU1ZzIGF0IHRoZSBwaHlsdW0gbGV2ZWwgdXNpbmcgdGhlIHBoeWxvc2VxIGZ1bmN0aW9uLCB0YXhfZ2xvbQpzdXBlcmtpbmdkb21HbG9tbWVkID0gdGF4X2dsb20ocHMsICJzdXBlcmtpbmdkb20iKQoKIyBhbmQgcGxvdApwbG90X2JhcihzdXBlcmtpbmdkb21HbG9tbWVkLCB4ID0gIlNhbXBsZSIpCgpnZ3NhdmUoZmlsZW5hbWUgPSAiRmlndXJlcy9zZXFkZXB0aC5lcHMiLCBwbG90ID0gcGxvdF9iYXIoc3VwZXJraW5nZG9tR2xvbW1lZCwgeCA9ICJTYW1wbGUiKSwgdW5pdHMgPSBjKCJpbiIpLCB3aWR0aCA9IDksIGhlaWdodCA9IDYsIGRwaSA9IDMwMCwgKSMgYW5kIHNhdmUKCmBgYApUb3RhbCBzZXF1ZW5jZXMgcmV2ZWFscyBjZXJ0YWluIHNhbXBsZXMgaGFkIHZlcnkgbG93IHNlcXVlbmNpbmcgZWZmb3J0OiBUMVM3LCBUMVM4LCBUM1MxMSwgYW5kLCBub3QgYXMgYmFkLCBUMVMyIGFuZCBUNFM1CgoKClRoZSByYXJlZmFjdGlvbiBhbmFseXNpcyBhbHNvIHNob3dlZCBUMVMyIGFuZCBUNFM1IHNhbXBsZXMgd2VyZSBsaWtlbHkgbm90IHNlcXVlbmNlZCB0byBjb21wbGV0aW9uLiBUaGVyZWZvcmUgcmVtb3ZlIHRoZXNlIDUgc2FtcGxlcyBmcm9tIGFuYWx5c2lzCmBgYHtyfQpwcyA8LSBzdWJzZXRfc2FtcGxlcyhwcywgIVNhbXBsZUlEID09ICJUMVM3IiAmICFTYW1wbGVJRCA9PSAiVDFTOCIgJiAhU2FtcGxlSUQgPT0gIlQzUzExIiAmICFTYW1wbGVJRCA9PSAiVDFTMiIgJiAhU2FtcGxlSUQgPT0gIlQ0UzUiKQoKcHMKYGBgCgo1MCBzYW1wbGVzIHJlbWFpbmluZyB3aXRoIDM2OCBBU1ZzCgoKUmVtb3ZlIFBvcyBDb250cm9scyAoYWxsIGhpdHMgaW4gcG9zaXRpdmUgY29udHJvbHMgYXJlIHRoZSBzYW1lIGZhbWlseS0gSSBhc3N1bWUgdGhpcyBpcyBleHBlY3RlZCkKYGBge3J9CnBzIDwtIHN1YnNldF9zYW1wbGVzKHBzLCAhU2FtcGxlSUQgPT0gIlQxUG9zQ29uIiAmICFTYW1wbGVJRCA9PSAiVDJQb3NDb24iICYgIVNhbXBsZUlEID09ICJUM1Bvc0NvbiIpCnBzCmBgYAoKNDcgc2FtcGxlcyByZW1haW5pbmcgd2l0aCAzNjggdW5pcXVlIEFTVnMKCgpBbmQgbGFzdGx5LCBjb3JyZWN0IHNvbWUgdGF4b25vbXk6ICoqRmlyc3QqIGFjY29yZGluZyB0byBTYXJhLCBFbmdyYXVsaXMgZW5jcmFzaWNvbHVzIChFdXJvcGVhbiBhbmNob3Z5KSBhbmQgRW5ncmF1bGlzIG1vcmRheCBzaG91bGQgYmUgQW5jaG9hIG1pdGNoaWxsaSAoQmF5IGFuY2hvdnkpOgoKYGBge3J9CnRheF90YWJsZShwcykgPC0gZ3N1Yih0YXhfdGFibGUocHMpLCBwYXR0ZXJuID0gIkVuZ3JhdWxpcyBlbmNyYXNpY29sdXMiLCByZXBsYWNlbWVudCA9ICJBbmNob2EgbWl0Y2hpbGxpIikgIAp0YXhfdGFibGUocHMpIDwtIGdzdWIodGF4X3RhYmxlKHBzKSwgcGF0dGVybiA9ICJFbmdyYXVsaXMgbW9yZGF4IiwgcmVwbGFjZW1lbnQgPSAiQW5jaG9hIG1pdGNoaWxsaSIpICAKYGBgCgoKKipTZWNvbmQqKiB0aGUgRm91cmhvcm4gc2N1bHBpbiAoTXlveG9jZXBoYWx1cyBxdWFkcmljb3JuaXMpIGlzIGFjdHVhbGx5IGFuIEFyY3RpYyBzcGVjaWVzLiBUaGlzIEFTViBoYXMgMTAwJSBQSUQgYW5kIDEwMCUgcXVlcnkgY292ZXIgdG8gTXlveG9jZXBoYWx1cyBxdWFkcmljb3JuaXMgJiBNeW94b2NlcGhhbHVzIHNjb3JwaXVzIChhbm90aGVyIEFyY3RpYyBzcGVjaWVzKSBhbmQgOTkuNCUgUElELCAxMDAlIHF1ZXJ5IGNvdmVyIHRvIE15b3hvY2VwaGFsdXMgYWVuYWV1cy4gVGhpcyBsYXR0ZXIgb25lIGlzIGFjdHVhbGx5IHRoZSByZWdpb25hbCBzcGVjaWVzLCBzbyB0aGlzIGlzIG1vcmUgbGlrZWx5IHRvIGJlIHRoZSBpZGVudGl0eToKYGBge3J9CnRheF90YWJsZShwcykgPC0gZ3N1Yih0YXhfdGFibGUocHMpLCBwYXR0ZXJuID0gIk15b3hvY2VwaGFsdXMgcXVhZHJpY29ybmlzIiwgcmVwbGFjZW1lbnQgPSAiTXlveG9jZXBoYWx1cyBhZW5hZXVzIikgCnRheF90YWJsZShwcykgPC0gZ3N1Yih0YXhfdGFibGUocHMpLCBwYXR0ZXJuID0gIkZvdXJob3JuIHNjdWxwaW4iLCByZXBsYWNlbWVudCA9ICJHcnViYnkgc2N1bHBpbiIpIApgYGAKCgoqKlRoaXJkKiogU2NvbWJlciBqYXBvbmljdXMsIHRoZSBjaHViIG1hY2tlcmVsLCBpcyBvbmx5IGZvdW5kIGluIHRoZSBJbmRvLVBhY2lmaWMuIFdoaWxlIHRoaXMgaXMgYSBjb21tZXJjaWFsIHByb2R1Y3QgYW5kIGNvdWxkIGJlIGhlcmUgZHVlIHRvIHNld2FnZSwgaXQgaXMgbW9yZSBsaWtlbHkgdGhlIFNjb21iZXIgY29saWFzIChBdGxhbnRpYyBjaHViIG1hY2tlcmVsKSwgd2hpY2ggaXMgZm91bmQgcmVnaW9uYWxseSAoaW4gdGhlIG9wZW4gb2NlYW4gQXRsYW50aWMpLiBUaGUgYmxhc3QgaGl0IHRvIFNjb21iZXIgamFwb25pY3VzIGhhcyBQSUQgb2YgMTAwJSBhbmQgcXVlcnkgY292ZXIgb2YgMTAwJSB3aGlsZSB0aGUgc2ltaWxhcml0eSB0byBTY29tYmVyIGNvbGlhcyAxMDAlIHF1ZXJ5IGNvdmVyLyA5OS40MSUgUElELgoKYGBge3J9CnRheF90YWJsZShwcykgPC0gZ3N1Yih0YXhfdGFibGUocHMpLCBwYXR0ZXJuID0gIlNjb21iZXIgamFwb25pY3VzIiwgcmVwbGFjZW1lbnQgPSAiU2NvbWJlciBjb2xpYXMiKSAKdGF4X3RhYmxlKHBzKSA8LSBnc3ViKHRheF90YWJsZShwcyksIHBhdHRlcm4gPSAiQ2h1YiBtYWNrZXJlbCIsIHJlcGxhY2VtZW50ID0gIkF0bGFudGljIGNodWIgbWFja2VyZWwiKSAKYGBgCgoKYGBge3J9CnBzCmBgYAoKNDcgc2FtcGxlcyByZW1haW53aXRoIDM2OCB1bmlxdWUgQVNWcwoKCgoKIyBBYnVuZGFuY2UgcGxvdHMgZUROQQoKRm9yIHBsb3R0aW5nLCB1c2UgKnJlbGF0aXZlIGFidW5kYW5jZXMqICgjIG9mIEFTViBzZXF1ZW5jZXMvc3VtIHRvdGFsIHNlcXVlbmNlcyBpbiBzYW1wbGUpLCBjYWxjdWxhdGVkIGVhc2lseSB1c2luZyBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0KCmBgYHtyfQpwc19yYSA8LSBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0ocHMsIHRyYW5zZm9ybSA9ICJjb21wb3NpdGlvbmFsIikKYGBgCgpFeHBvcnQgdGhlIHJlbGF0aXZlIGFidW5kYW5jZSBtYXRyaXggc28gU2FyYSBjYW4gaGF2ZSBpdDoKYGBge3J9CiMgRXh0cmFjdCBhYnVuZGFuY2UgbWF0cml4IGZyb20gdGhlIHBoeWxvc2VxIG9iamVjdApSZWxBYnVuX21hdHJpeCA9IGFzKG90dV90YWJsZShwc19yYSksICJtYXRyaXgiKQoKIyBDb2VyY2UgdG8gZGF0YS5mcmFtZQpSZWxBYnVuX2RhdGFmcmFtZSA9IGFzLmRhdGEuZnJhbWUoUmVsQWJ1bl9tYXRyaXgpCgojIEV4cG9ydAp3cml0ZS5jc3YoUmVsQWJ1bl9kYXRhZnJhbWUsInJlc3VsdHMvb3R1dGFibGVfcmVsYWJ1bi5jc3YiLCByb3cubmFtZXMgPSBUUlVFKQoKYGBgCgoKCiMjIEFidW5kYW5jZSBhdCBmYW1pbHkgbGV2ZWwKVGhlbiBhZ2xvbWVyYXRlIHRoZSBBU1ZzIGF0IHRoZSBmYW1pbHkgbGV2ZWwgdXNpbmcgdGhlIHBoeWxvc2VxIGZ1bmN0aW9uLCB0YXhfZ2xvbQpgYGB7cn0KZmFtaWx5R2xvbW1lZF9SQSA9IHRheF9nbG9tKHBzX3JhLCAiZmFtaWx5IikKZmFtaWx5X2JhcnBsb3QgPC0gcGxvdF9iYXIoZmFtaWx5R2xvbW1lZF9SQSwgeCA9ICJTYW1wbGUiLCBmaWxsID0gImZhbWlseSIpCmZhbWlseV9iYXJwbG90CgpgYGAKKipOT1RFUyoqCgotIFRoZXJlIGFyZSBzb21lIHNhbXBsZXMsIChUMVMzLCBUMVM2LCBUMlMxMSwgVDNTMTAsIFQzUzQsIFQzUzUsIFQzUzksIFQ0UzQsIFQ0UzcsIFQ1UzcpIHdoaWNoIGFyZSBjb21wb3NlZCBhbG1vc3QgZXhjbHVzaXZlbHkgb2YgMSBmYW1pbHkuIFRoaXMgbWlnaHQgYmUgZmluZSwgYnV0IEknbSBub3QgdXNlZCB0byBzZWVpbmcgdGhpcyB3aXRoIHByb2thcm95dGljIGRhdGEuIEp1c3Qgd2FudCB0byBjaGVjayB3aXRoIHlvdQoKCgpBZ2dsb21lcmF0ZSBieSBzcGVjaWVzIHRvIHRha2UgYSBsb29rIGF0IHRoZSB1bmlxdWUgc3BlY2llcwoKYGBge3J9CnNwZWNpZXNHbG9tbWVkX1JBID0gdGF4X2dsb20ocHNfcmEsICJDb21tb25OYW1lIikKc3BlY2llc0dsb21tZWRfUkEKdGF4X3RhYmxlKHNwZWNpZXNHbG9tbWVkX1JBKQoKYGBgCgoKCgojIyBCdWJibGUgcGxvdHMKCkJhc2VkIG9uIG15IHByZXZpb3VzIFtzY3JpcHRzXShodHRwczovL2dpdGh1Yi5jb20vbGl6c3V0ZXIvQ2FyaWFjb19FdWspIHdpdGggQ2FyaWFjbyBFdWthcnlvdGljIGRhdGEKYGBge3J9CiMgY29udmVydCBwcyBvYmplY3QgdG8gZGF0YWZyYW1lIHVzaW5nIHBoeWxvc2VxJ3MgcHNtZWx0CnNwZWNpZXNfZGYgPC0gcHNtZWx0KHNwZWNpZXNHbG9tbWVkX1JBKQoKIyByZXBsYWNlIHplcm9lcyBpbiB0aGUgdGFibGUgd2l0aCBOQQpzcGVjaWVzX2RmW3NwZWNpZXNfZGYgPT0gMF0gPC0gTkEKCiMgYW5kIHJlbW92ZSByb3dzIHdpdGggTkFzIGluIGFidW5kYW5jZSAgKHRoaXMgaXMgc28gdGhleSBkb24ndCBhcHBlYXIgYXMgc21hbGwgZG90cyBpbiBwbG90KQpzcGVjaWVzX2RmIDwtICBmaWx0ZXIoc3BlY2llc19kZiwgIWlzLm5hKEFidW5kYW5jZSkpCmBgYAoKCgpQbG90IGJ5IHNwZWNpZXMsIHNjaWVudGlmaWMgbmFtZQpgYGB7cn0Kc3BlY2llc2J1YmJsZXBsb3RfZUROQV9zY2luYW1lIDwtIGdncGxvdChzcGVjaWVzX2RmLCBhZXMoeCA9IFN0YXRpb24sIHkgPSBmY3RfcmV2KHNwZWNpZXMpLCBjb2xvciA9IFN0YXRpb24pKSArICMgdGhlIGZhbmN5IHN0dWZmIGFyb3VuZCB5IChzcGVjaWVzKSBoZWxwcyB0byBwcmVzZW50IGl0IGluIHJldmVyc2Ugb3JkZXIgaW4gdGhlIHBsb3QgKGZyb20gdG9wIHRvIGJ0bSBhbHBoYWJldGljYWxseSkKICBnZW9tX3BvaW50KGFlcyhzaXplID0gQWJ1bmRhbmNlLCBmaWxsID0gU3RhdGlvbiksIGNvbG9yID0gImJsYWNrIiwgcGNoID0gMjEpKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDEsMTUpKSArCiAgc2NhbGVfc2l6ZV9hcmVhKGJyZWFrcyA9IGMoMCwuMjUsLjUsLjc1LDEpLCBtYXhfc2l6ZSA9IDYpKwogIHhsYWIoIiIpKwogIHlsYWIoIiIpKwogIGxhYnMoc2l6ZT0iUmVsYXRpdmUgQWJ1bmRhbmNlIikrCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgKwogIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsKICBmYWNldF9ncmlkKERhdGVjb2RlfkJheXNpZGUsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIsIGRyb3A9IFRSVUUpCgpzcGVjaWVzYnViYmxlcGxvdF9lRE5BX3NjaW5hbWUKYGBgCgoKClBsb3QgYnkgc3BlY2llcyBjb21tb24gbmFtZQpgYGB7cn0Kc3BlY2llc2J1YmJsZXBsb3RfZUROQV9jb21uYW1lIDwtIGdncGxvdChzcGVjaWVzX2RmLCBhZXMoeCA9IFN0YXRpb24sIHkgPSBmY3RfcmV2KENvbW1vbk5hbWUpLCBjb2xvciA9IFN0YXRpb24pKSArICMgdGhlIGZhbmN5IHN0dWZmIGFyb3VuZCB5IChDb21tb25OYW1lKSBoZWxwcyB0byBwcmVzZW50IGl0IGluIHJldmVyc2Ugb3JkZXIgaW4gdGhlIHBsb3QgKGZyb20gdG9wIHRvIGJ0bSBhbHBoYWJldGljYWxseSkKICBnZW9tX3BvaW50KGFlcyhzaXplID0gQWJ1bmRhbmNlLCBmaWxsID0gU3RhdGlvbiksIGNvbG9yID0gImJsYWNrIiwgcGNoID0gMjEpKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDEsMTUpKSArCiAgc2NhbGVfc2l6ZV9hcmVhKGJyZWFrcyA9IGMoMCwuMjUsLjUsLjc1LDEpLCBtYXhfc2l6ZSA9IDYpKwogIHhsYWIoIiIpKwogIHlsYWIoIiIpKwogIGxhYnMoc2l6ZT0iUmVsYXRpdmUgQWJ1bmRhbmNlIikrCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgKwogIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpICsKICBmYWNldF9ncmlkKERhdGVjb2RlfkJheXNpZGUsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIsIGRyb3A9IFRSVUUpCgpzcGVjaWVzYnViYmxlcGxvdF9lRE5BX2NvbW5hbWUKYGBgCgoKRXhwb3J0ZmlndXJlcwpgYGB7cn0KZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvc3BlY2llc2J1YmJsZXBsb3RfZUROQV9zY2luYW1lLmVwcyIsIHBsb3QgPSBzcGVjaWVzYnViYmxlcGxvdF9lRE5BX3NjaW5hbWUsIHVuaXRzID0gYygiaW4iKSwgd2lkdGggPSA3LCBoZWlnaHQgPSAxMiwgZHBpID0gMzAwKQoKZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvc3BlY2llc2J1YmJsZXBsb3RfZUROQV9jb21uYW1lLmVwcyIsIHBsb3QgPSBzcGVjaWVzYnViYmxlcGxvdF9lRE5BX2NvbW5hbWUsIHVuaXRzID0gYygiaW4iKSwgd2lkdGggPSA3LCBoZWlnaHQgPSAxMiwgZHBpID0gMzAwKQpgYGAKCgoKCiMjIyBCdWJibGUgcGxvdCB3aXRob3V0IEVsYXNtb2JyYW5jaHMKVGhlIGFib3ZlIGxvb2sgZ29vZCBidXQgdGhleSBpbmNsdWRlIHR3byBlbGFzbW9icmFuY2hzLCB0aGUgZHVza3kgc21vb3RoLWhvdW5kIHNoYXJrIGFuZCBjb3dub3NlIHJheS4gV2hpbGUgdGhlc2UgYXJlIHByb2JhYmx5IHJlYWwsIHRoZSBNaUZJU0ggcHJpbWVycyBkb24ndCBhY3R1YWxseSB0YXJnZXQgdGhlIGVsYXNtb2JyYW5jaHMsIHNvIHdlIGNhbid0IHRydXN0IHRoaXMgYXNzYXkgdG8gZmFpcmx5IHJlcHJlc2VudCB0aGVzZSBub24tdGFyZ2V0IHNwZWNpZXMuIEZpbHRlciBvdXQgYW5kIHJlLW1ha2UgdGhlIGJ1YmJsZSBwbG90czoKCmBgYHtyfQpwc19ub19lbGFzbW8gPC0gc3Vic2V0X3RheGEocHMsICFDb21tb25OYW1lID09ICdDb3dub3NlIHJheScpCnBzX25vX2VsYXNtbyA8LSBzdWJzZXRfdGF4YShwc19ub19lbGFzbW8sICFDb21tb25OYW1lID09J0R1c2t5IHNtb290aC1ob3VuZCBzaGFyaycpCgpwc19yYV9ub19lbGFzbW8gPC0gc3Vic2V0X3RheGEocHNfcmEsICFDb21tb25OYW1lID09ICdDb3dub3NlIHJheScpCnBzX3JhX25vX2VsYXNtbyA8LSBzdWJzZXRfdGF4YShwc19yYV9ub19lbGFzbW8sICFDb21tb25OYW1lID09J0R1c2t5IHNtb290aC1ob3VuZCBzaGFyaycpCgojIGFuZCBjaGVjawpzcGVjaWVzR2xvbW1lZF9SQV9ub19lbGFzbW8gPSB0YXhfZ2xvbShwc19yYV9ub19lbGFzbW8sICJDb21tb25OYW1lIikKc3BlY2llc0dsb21tZWRfUkFfbm9fZWxhc21vCnRheF90YWJsZShzcGVjaWVzR2xvbW1lZF9SQV9ub19lbGFzbW8pCmBgYAoKUmVtYWtlIGJ1YmJsZSBwbG90cy4gRmlyc3QgbWVsdCBmb3IgdGlkeXZlcnNlIGZvcm1hdAoKYGBge3J9CiMgY29udmVydCBwcyBvYmplY3QgdG8gZGF0YWZyYW1lIHVzaW5nIHBoeWxvc2VxJ3MgcHNtZWx0CnNwZWNpZXNfZGZfbm9fZWxhc21vIDwtIHBzbWVsdChzcGVjaWVzR2xvbW1lZF9SQV9ub19lbGFzbW8pCgojIHJlcGxhY2UgemVyb2VzIGluIHRoZSB0YWJsZSB3aXRoIE5BCnNwZWNpZXNfZGZfbm9fZWxhc21vW3NwZWNpZXNfZGZfbm9fZWxhc21vID09IDBdIDwtIE5BCgojIGFuZCByZW1vdmUgcm93cyB3aXRoIE5BcyBpbiBhYnVuZGFuY2UgICh0aGlzIGlzIHNvIHRoZXkgZG9uJ3QgYXBwZWFyIGFzIHNtYWxsIGRvdHMgaW4gcGxvdCkKc3BlY2llc19kZl9ub19lbGFzbW8gPC0gIGZpbHRlcihzcGVjaWVzX2RmX25vX2VsYXNtbywgIWlzLm5hKEFidW5kYW5jZSkpCmBgYAoKCgpQbG90IGJ5IHNwZWNpZXMsIHNjaWVudGlmaWMgbmFtZQpgYGB7cn0Kc3BlY2llc2J1YmJsZXBsb3RfZUROQV9zY2luYW1lX25vX2VsYXNtbyA8LSBnZ3Bsb3Qoc3BlY2llc19kZl9ub19lbGFzbW8sIGFlcyh4ID0gU3RhdGlvbiwgeSA9IGZjdF9yZXYoc3BlY2llcyksIGNvbG9yID0gU3RhdGlvbikpICsgIyB0aGUgZmFuY3kgc3R1ZmYgYXJvdW5kIHkgKHNwZWNpZXMpIGhlbHBzIHRvIHByZXNlbnQgaXQgaW4gcmV2ZXJzZSBvcmRlciBpbiB0aGUgcGxvdCAoZnJvbSB0b3AgdG8gYnRtIGFscGhhYmV0aWNhbGx5KQogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBBYnVuZGFuY2UsIGZpbGwgPSBTdGF0aW9uKSwgY29sb3IgPSAiYmxhY2siLCBwY2ggPSAyMSkrCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMSwxNSkpICsKICBzY2FsZV9zaXplX2FyZWEoYnJlYWtzID0gYygwLC4yNSwuNSwuNzUsMSksIG1heF9zaXplID0gNikrCiAgeGxhYigiIikrCiAgeWxhYigiIikrCiAgbGFicyhzaXplPSJSZWxhdGl2ZSBBYnVuZGFuY2UiKSsKICB0aGVtZV9idygpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJQYWlyZWQiKSArCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgKwogIGZhY2V0X2dyaWQoRGF0ZWNvZGV+QmF5c2lkZSwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIiwgZHJvcD0gVFJVRSkKCnNwZWNpZXNidWJibGVwbG90X2VETkFfc2NpbmFtZV9ub19lbGFzbW8KYGBgCgoKClBsb3QgYnkgc3BlY2llcyBjb21tb24gbmFtZQpgYGB7cn0Kc3BlY2llc2J1YmJsZXBsb3RfZUROQV9jb21uYW1lX25vX2VsYXNtbyA8LSBnZ3Bsb3Qoc3BlY2llc19kZl9ub19lbGFzbW8sIGFlcyh4ID0gU3RhdGlvbiwgeSA9IGZjdF9yZXYoQ29tbW9uTmFtZSksIGNvbG9yID0gU3RhdGlvbikpICsgIyB0aGUgZmFuY3kgc3R1ZmYgYXJvdW5kIHkgKENvbW1vbk5hbWUpIGhlbHBzIHRvIHByZXNlbnQgaXQgaW4gcmV2ZXJzZSBvcmRlciBpbiB0aGUgcGxvdCAoZnJvbSB0b3AgdG8gYnRtIGFscGhhYmV0aWNhbGx5KQogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBBYnVuZGFuY2UsIGZpbGwgPSBTdGF0aW9uKSwgY29sb3IgPSAiYmxhY2siLCBwY2ggPSAyMSkrCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMSwxNSkpICsKICBzY2FsZV9zaXplX2FyZWEoYnJlYWtzID0gYygwLC4yNSwuNSwuNzUsMSksIG1heF9zaXplID0gNikrCiAgeGxhYigiIikrCiAgeWxhYigiIikrCiAgbGFicyhzaXplPSJSZWxhdGl2ZSBBYnVuZGFuY2UiKSsKICB0aGVtZV9idygpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJQYWlyZWQiKSArCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgKwogIGZhY2V0X2dyaWQoRGF0ZWNvZGV+QmF5c2lkZSwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIiwgZHJvcD0gVFJVRSkKCnNwZWNpZXNidWJibGVwbG90X2VETkFfY29tbmFtZV9ub19lbGFzbW8KYGBgCgoKRXhwb3J0ZmlndXJlcwpgYGB7cn0KZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvc3BlY2llc2J1YmJsZXBsb3RfZUROQV9zY2luYW1lX25vX2VsYXNtby5lcHMiLCBwbG90ID0gc3BlY2llc2J1YmJsZXBsb3RfZUROQV9zY2luYW1lX25vX2VsYXNtbywgdW5pdHMgPSBjKCJpbiIpLCB3aWR0aCA9IDcsIGhlaWdodCA9IDEyLCBkcGkgPSAzMDApCgpnZ3NhdmUoZmlsZW5hbWUgPSAiRmlndXJlcy9zcGVjaWVzYnViYmxlcGxvdF9lRE5BX2NvbW5hbWVfbm9fZWxhc21vLmVwcyIsIHBsb3QgPSBzcGVjaWVzYnViYmxlcGxvdF9lRE5BX2NvbW5hbWVfbm9fZWxhc21vLCB1bml0cyA9IGMoImluIiksIHdpZHRoID0gNywgaGVpZ2h0ID0gMTIsIGRwaSA9IDMwMCkKYGBgCgoKCiMgIEltcG9ydCBhbmQgcHJlcGFyZSB0aGUgZGF0YSBmcm9tIHRyYXdscyAKCiMjIEltcG9ydCBUcmF3bCBDb3VudCBEYXRhCmBgYHtyfQojIGltcG9ydCA0dGggc2hlZXQgZnJvbSAgRXhjZWwgZmlsZSB3aGljaCBjb250YWlucyBtb3JwaG9tZXRyaWMgZGF0YSBmb3IgZWFjaCBpbmRpdmlkdWFsIGNvbGxlY3RlZCBmb3IgZXZlcnkgZGF0ZQp0cmF3bF9tYXN0ZXIgPC0gcmVhZF9leGNlbCgiVHJhd2xzIE1BU1RFUiAyMDIwIF9tb2RfRVMueGxzeCIsNCkKCiMgYW5kIGltcG9ydCA2dGggc2hlZXQgd2hpY2ggaXMgc3RhdGlvbiBpbmZvCnN0YXRpb25zIDwtIHJlYWRfZXhjZWwoIlRyYXdscyBNQVNURVIgMjAyMCBfbW9kX0VTLnhsc3giLDYpCgojIGFuZCBpbXBvcnQgc2hlZGRpbmcgZmFjdG9yLSBhbiBpbmRleCBkZXRlcm1pbmVkIGJ5IFNhcmEgdGhhdCBpbmRpY2F0ZXMgaG93IG11Y2ggdGhlIHNwZWNpZXMgc2hlZHMgd2hlbiBoYW5kbGVkIChhbmQgdGhlcmVmb3JlIGhvdyBsaWtlbHkgaXQgaXMgdG8gc2hlZCBjZWxscyBpbiB3YXRlcikKc2hlZGRpbmdmYWN0b3IgPC0gcmVhZF9leGNlbCgiQWxsb21ldHJpYyBjb3JyZWN0aW9uX21vZC54bHN4Iiw1KQoKIyBHcm91cCBzdGF0aW9uIG5hbWUgYW5kIHNoZWRkaW5nIGZhY3RvciBpbnRvIHRyYXdsX21hc3RlciB0YWJsZQp0cmF3bF9tYXN0ZXIgPC0gbGVmdF9qb2luKHRyYXdsX21hc3Rlciwgc3RhdGlvbnMsIGJ5ID0gIlNUQVRJT05fTk8iKQp0cmF3bF9tYXN0ZXIgPC0gbGVmdF9qb2luKHRyYXdsX21hc3Rlciwgc2hlZGRpbmdmYWN0b3IsIGJ5ID0gIkNPTU1PTk5BTUUiKQoKdHJhd2xfbWFzdGVyCmBgYAoKSW1wb3J0IHN0YXRpb24vIHRyYXdsIGluZm9ybWF0aW9uCmBgYHtyfQpzdGF0aW9uX2RhdGEgPC0gcmVhZF9leGNlbCgiVHJhd2xzIE1BU1RFUiAyMDIwIF9tb2RfRVMueGxzeCIsMSkKc3RhdGlvbl9kYXRhCgojIEZpbHRlciB0byBvbmx5IGluY2x1ZGUgREFURUNPREUsIFN0YXRpb25fTk8sIFRyYXdsX01pbgpzdGF0aW9uX2RhdGEgPC0gc3RhdGlvbl9kYXRhICU+JSBzZWxlY3QoREFURUNPREUsIFNUQVRJT05fTk8sIFRyYXdsX01pbikKc3RhdGlvbl9kYXRhCmBgYAoKQ29tYmluZSBzdGF0aW9uIGluZm9ybWF0aW9uIHRvIHRyYXdsX21hc3RlciBpbiBvcmRlciB0byBoYXZlIHRoZSBkdXJhdGlvbiBvZiBlYWNoIHRyYXdsIChmb3IgY2FsY3VsYXRpbmcgQ1BVRSkKYGBge3J9CnRyYXdsX21hc3RlciA8LSBsZWZ0X2pvaW4odHJhd2xfbWFzdGVyLCBzdGF0aW9uX2RhdGEsIGJ5ID0gYygiREFURUNPREUiLCAiU1RBVElPTl9OTyIpKQp0cmF3bF9tYXN0ZXIKYGBgCgoKCgpNYWtlIGEgY291bnQgdGFibGUgZnJvbSB0cmF3bF9tYXN0ZXIsIGdyb3VwaW5nIGJ5IGRhdGUgYW5kIGxvY2F0aW9uLCBzdW1taW5nIHRoZSBjb3VudHMgZm9yIGV2ZXJ5IHVuaXF1ZSBzcGVjaWVzLCBhbmQgc3VtbWluZyB0aGUgdG90YWwgbGVuZ3RoIGZvciBlYWNoIHRyYXdsLyBzcGVjaWVzIApgYGB7cn0KdHJhd2xfY291bnRzIDwtIHRyYXdsX21hc3RlciAlPiUKICBncm91cF9ieShEQVRFQ09ERSwgU1RBVElPTl9OQSwgU1RBVElPTl9OTywgVHJhd2xfTWluLCBCQVlTSURFLCBDb21tb25OYW1lLCBTaGVkZGluZ0ZhY3RvcikgJT4lCiAgc3VtbWFyaXplKFRvdGFsTGVuZ3RoID0gc3VtKFRMX0NNKSkKCmNvdW50cyA8LSB0cmF3bF9tYXN0ZXIgJT4lCiAgZ3JvdXBfYnkoREFURUNPREUsIFNUQVRJT05fTkEsIFNUQVRJT05fTk8sIENvbW1vbk5hbWUpICU+JQogIHRhbGx5KG5hbWUgPSAiY291bnQiKQoKdHJhd2xfY291bnRzIDwtIGxlZnRfam9pbih0cmF3bF9jb3VudHMsIGNvdW50cywgYnkgPSBjICgiREFURUNPREUiLCAiU1RBVElPTl9OQSIsICJTVEFUSU9OX05PIiwgIkNvbW1vbk5hbWUiKSkKdHJhd2xfY291bnRzCmBgYAoKCkNhbGN1bGF0ZSBDUFVFIGFuZCBwdXQgaW4gbmV3IGNvbHVtbi4gQ1BVRSBpcyB0aGUgY291bnQgZGl2aWRlZCBieSB0cmF3bCB0aW1lIChpbiBtaW51dGVzKQpgYGB7cn0KdHJhd2xfY291bnRzIDwtIHRyYXdsX2NvdW50cyAlPiUKICBtdXRhdGUgKENQVUUgPSBjb3VudCAvIFRyYXdsX01pbikKdHJhd2xfY291bnRzCmBgYAoKCkNhbGN1bGF0ZSB0aGUgbWV0cmljIHRoYXQgU2FyYSBjYW1lIHVwIHdpdGg6IHN1bSh0b3RhbCBsZW5ndGgpICogc2hlZGRpbmcgZmFjdG9yLiBUaGlzIGlzIGEgY29ycmVjdGlvbiBvZiB0aGUgYWJ1bmRhbmNlIHRoYXQgdGFrZXMgaW50byBhY2NvdW50IHRoZSBzdW1zIG9mIGxlbmd0aCBvZiBlYWNoIGZpc2ggZm9yIGVhY2ggZGF0ZS90cmF3bCBhbmQgbXVsdGlwbGllcyBieSBhIGZhY3RvciBkZXRlcm1pbmVkIGJ5IGhvdyBtdWNoIHRoZXkgc2hlZC4gCmBgYHtyfQp0cmF3bF9jb3VudHMgPC0gdHJhd2xfY291bnRzICU+JQogIG11dGF0ZSAoIlRMeFNGIiA9IFRvdGFsTGVuZ3RoKlNoZWRkaW5nRmFjdG9yKQp0cmF3bF9jb3VudHMKYGBgCgpUaGVuIGFsc28gZGl2aWRlIHRoZSBUb3RhbExlbmd0aCBhbmQgU3VtVEwqU0YgYnkgdGhlIHRyYXdsIHRpbWUgaW4gb3JkZXIgdG8gYWNjb3VudCBmb3IgZWZmb3J0IChzaW1pbGFyIHRvIENQVUUpCgpgYGB7cn0KdHJhd2xfY291bnRzIDwtIHRyYXdsX2NvdW50cyAlPiUKICBtdXRhdGUgKCJUTFBVRSIgPSBUb3RhbExlbmd0aC9UcmF3bF9NaW4pCgp0cmF3bF9jb3VudHMgPC0gdHJhd2xfY291bnRzICU+JQogIG11dGF0ZSAoIlRMeFNGLlBVRSIgPSBUTHhTRi9UcmF3bF9NaW4pCgp0cmF3bF9jb3VudHMKYGBgCgoKUmVtb3ZlIDA5LzE2LzIwIHNpbmNlIHRoZXJlIGlzIG5vIGVxdWl2YWxlbnQgZUROQSBmcm9tIHRoYXQgZGF0ZQpgYGB7cn0KdHJhd2xfY291bnRzIDwtIHRyYXdsX2NvdW50cyAlPiUKICBmaWx0ZXIoREFURUNPREUgIT0gIjIwMjAwOTE2IikKYGBgCgoKCiMgQWJ1bmRhbmNlIHBsb3RzIFRyYXdscwoKIyMgQnViYmxlIHBsb3RzIG9mIENvdW50cwoKYGBge3J9CnNwZWNpZXNidWJibGVwbG90X3RyYXdsX2NvbW5hbWUgPC0gZ2dwbG90KHRyYXdsX2NvdW50cywgYWVzKHggPSBTVEFUSU9OX05BLCB5ID0gZmN0X3JldihDb21tb25OYW1lKSwgY29sb3IgPSBTVEFUSU9OX05BKSkgKyAKICBnZW9tX3BvaW50KGFlcyhzaXplID0gbG9nMTAoY291bnQpLCBmaWxsID0gU1RBVElPTl9OQSksIGNvbG9yID0gImJsYWNrIiwgcGNoID0gMjEpKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDEsMTUpKSArCiAgc2NhbGVfc2l6ZV9hcmVhKGJyZWFrcyA9IGMobG9nMTAoMSksIGxvZzEwKDIpLCBsb2cxMCg1KSwgbG9nMTAoMTApLCBsb2cxMCgyNSksIGxvZzEwKDEwMCkpLCBtYXhfc2l6ZSA9IDYsIGxhYmVscyA9IGMoIjEiLCIyIiwiNSIsIjEwIiwiMjUiLCIxMDAiKSkrCiAgeGxhYigiIikrCiAgeWxhYigiIikrCiAgbGFicyhzaXplPSJBYnVuZGFuY2UiLCBmaWxsID0gIlN0YXRpb24iKSsKICB0aGVtZV9idygpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJQYWlyZWQiKSArCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgKwogIGZhY2V0X2dyaWQoREFURUNPREV+QkFZU0lERSwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIiwgZHJvcD0gVFJVRSkKCnNwZWNpZXNidWJibGVwbG90X3RyYXdsX2NvbW5hbWUKCmBgYAoKRXhwb3J0IGZpZ3VyZQpgYGB7cn0KZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvc3BlY2llc2J1YmJsZXBsb3RfdHJhd2xfYWJ1bmRhbmNlX2NvbW5hbWUuZXBzIiwgcGxvdCA9IHNwZWNpZXNidWJibGVwbG90X3RyYXdsX2NvbW5hbWUsIHVuaXRzID0gYygiaW4iKSwgd2lkdGggPSA2Ljc1LCBoZWlnaHQgPSAxMywgZHBpID0gMzAwKQpgYGAKCgoKCgojIyBCdWJibGUgcGxvdHMgb2YgQ1BVRQoKYGBge3J9CnNwZWNpZXNidWJibGVwbG90X3RyYXdsX0NQVUVfY29tbmFtZSA8LSBnZ3Bsb3QodHJhd2xfY291bnRzLCBhZXMoeCA9IFNUQVRJT05fTkEsIHkgPSBmY3RfcmV2KENvbW1vbk5hbWUpLCBjb2xvciA9IFNUQVRJT05fTkEpKSArIAogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBsb2cxMChDUFVFKSwgZmlsbCA9IFNUQVRJT05fTkEpLCBjb2xvciA9ICJibGFjayIsIHBjaCA9IDIxKSsKICBzY2FsZV9zaXplKHJhbmdlID0gYygxLDE1KSkgKwogIHNjYWxlX3NpemVfYXJlYShicmVha3MgPSBjKGxvZzEwKDEpLCBsb2cxMCgyKSwgbG9nMTAoNSksIGxvZzEwKDEwKSwgbG9nMTAoMjUpLCBsb2cxMCgxMDApKSwgbWF4X3NpemUgPSA2LCBsYWJlbHMgPSBjKCIxIiwiMiIsIjUiLCIxMCIsIjI1IiwiMTAwIikpKwogIHhsYWIoIiIpKwogIHlsYWIoIiIpKwogIGxhYnMoc2l6ZT0iQ1BVRSIsIGZpbGwgPSAiU3RhdGlvbiIpKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlBhaXJlZCIpICsKICB0aGVtZShheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSArCiAgZmFjZXRfZ3JpZChEQVRFQ09ERX5CQVlTSURFLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiLCBkcm9wPSBUUlVFKQoKc3BlY2llc2J1YmJsZXBsb3RfdHJhd2xfQ1BVRV9jb21uYW1lCmBgYApMb29rcyBnb29kISBTaW1pbGFyIHRvICJjb3VudHMiIGZpZ3VyZSBidXQgc29tZSBhZGp1c3RtZW50cyB0aGF0IG5vcm1hbGl6ZWQgZm9yIHRyYXdsaW5nIHRpbWUuCgoKRXhwb3J0IGZpZ3VyZQpgYGB7cn0KZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvc3BlY2llc2J1YmJsZXBsb3RfdHJhd2xfQ1BVRV9jb21uYW1lLmVwcyIsIHBsb3QgPSBzcGVjaWVzYnViYmxlcGxvdF90cmF3bF9DUFVFX2NvbW5hbWUsIHVuaXRzID0gYygiaW4iKSwgd2lkdGggPSA2Ljc1LCBoZWlnaHQgPSAxMywgZHBpID0gMzAwKQpgYGAKCgojIENvbXBhcmUgVHJhd2wgYW5kIGVETkEKCiMjIFByZXBhcmUgdGhlIGRhdGEKRmlyc3QsIHJlbW92ZSB0aGUgc3BlY2llcyBmcm9tIHRoZSB0cmF3bHMgdGhhdCBhcmUgbm90IHRhcmdldGVkIGluIHRoZSBlRE5BIGFzc2F5IChpbnZlcnRlYnJhdGVzIGFuZCBlbGFzbW9icmFuY2hzKQpgYGB7cn0KIyBpbXBvcnQgYSBsaXN0IG9mIHRoZSAiT0siIHNwZWNpZXMgdGhhdCBhcmUgdGFyZ2V0dGVkIGJ5IE1pRklTaCBwcmltZXJzCm1pZmlzaF9zcHAgPC0gcmVhZF9leGNlbCgiVHJhd2wgQ1BVRSBubyBlbGFzbW9icmFuY2hfbW9kLnhsc3giLDIpCm1pZmlzaF9zcHAKCiMgZmlsdGVyIHJvd3MgZnJvbSB0cmF3bF9jb3VudHMgaWYgdGhlIHNwcCBuYW1lIGRvZXNuJ3QgbWF0Y2ggdGhlIE1pRklTaCBsaXN0CnRyYXdsX2NvdW50cyA8LSByaWdodF9qb2luKHRyYXdsX2NvdW50cywgbWlmaXNoX3NwcCwgYnkgPSAiQ29tbW9uTmFtZSIpCnRyYXdsX2NvdW50cwpgYGAKClRoZW4gZmlsdGVyIG91dCBzdGF0aW9ucyBmcm9tIHRyYXdsIGRhdGEgdGhhdCB3ZXJlIHJlbW92ZWQgc2FtcGxlcyBmcm9tIGVETkEgYW5hbHlzaXMgYmVjYXVzZSBvZiBwb29yIHNlcXVlbmNpbmcgZWZmb3J0LgoKYGBge3J9CiMgR3JhYiB0aGUgZUROQSBzYW1wbGUgSURzIHRoYXQgcmVtYWluZWQgYWZ0ZXIgZmlsdGVyaW5nCnNhbXBsZUlEcyA8LSBhc19kYXRhX2ZyYW1lKHNhbXBsZV9kYXRhKHBzX25vX2VsYXNtbykpICU+JSAKICBzZWxlY3QoU2FtcGxlSUQsIERhdGVjb2RlLCBTdGF0aW9uKQoKIyBGaWx0ZXIgdHJhd2xfY291bnRzIHRvIG9ubHkgaW5jbHVkZSB0aG9zZSBzYW1lIHNhbXBsZXMKdHJhd2xfY291bnRzIDwtIGlubmVyX2pvaW4odHJhd2xfY291bnRzLCBzYW1wbGVJRHMsIGJ5ID0gYygiREFURUNPREUiID0gIkRhdGVjb2RlIiwgIlNUQVRJT05fTkEiID0gIlN0YXRpb24iKSkKdHJhd2xfY291bnRzCmBgYAoKTWFrZSBhYnVuZGFuY2UgdGFibGUgb2YgZWFjaCBzcGVjaWVzIGFjcm9zcyB3aG9sZSBzdHVkeQpgYGB7cn0KIyBzdW0gaGl0cyBhY3Jvc3MgYWxsIGRhdGVzIGluIHRyYXdsCnRyYXdsX3VuaXF1ZXMgPC0gdHJhd2xfY291bnRzICU+JQogIGdyb3VwX2J5KERBVEVDT0RFLCBDb21tb25OYW1lKSAlPiUKICBzdW1tYXJpc2UoVHJhd2xfQ291bnQgPSBzdW0oY291bnQsIG5hLnJtPVRSVUUpLCBUcmF3bF9DUFVFID0gc3VtKENQVUUsIG5hLnJtID0gVFJVRSksIFRyYXdsX1RMUFVFID0gc3VtKFRMUFVFLCBuYS5ybSA9IFRSVUUpLCBUcmF3bF9BbGxvbWV0cmljX1NoZWRkaW5nID0gc3VtKFRMeFNGLlBVRSwgbmEucm0gPSBUUlVFKSkKdHJhd2xfdW5pcXVlcwoKIyBzdW0gaGl0cyBhY3Jvc3MgYWxsIGRhdGVzIGluIGVETkEKZUROQV91bmlxdWVzIDwtIHNwZWNpZXNfZGZfbm9fZWxhc21vJT4lCiAgZ3JvdXBfYnkoRGF0ZWNvZGUsIENvbW1vbk5hbWUpICU+JQogIHN1bW1hcmlzZShlRE5BX1JlbEFidW4gPSBzdW0oQWJ1bmRhbmNlLCBuYS5ybT1UUlVFKSkKZUROQV91bmlxdWVzCgojIENvbWJpbmUgaW50byBvbmUgZGF0YWZyYW1lCnRyYXdsX2VETkFfYWJ1bl90YWJsZSA8LSBmdWxsX2pvaW4odHJhd2xfdW5pcXVlcywgZUROQV91bmlxdWVzLCBieT1jKCJDb21tb25OYW1lIiA9ICJDb21tb25OYW1lIiwgIkRBVEVDT0RFIiA9ICJEYXRlY29kZSIpKQoKdHJhd2xfZUROQV9hYnVuX3RhYmxlCmBgYAoKCiMjIFNwZWNpZXMgUmljaG5lc3MKQ291bnQgdW5pcXVlIHNwZWNpZXMgYWNyb3NzIGFsbCBzdGF0aW9ucywgZ3JvdXBlZCBieSBkYXRlLCBmb3IgZWFjaCBtZXRob2QsIHRyYXdsJiBlRE5BICh1c2UgZmlsdGVyZWQgdHJhd2wgZGF0YSBzbyBvbmx5IGNvbXBhcmluZyBNaUZJU2ggc3BwIHRvIE1pRklTaCBzcHApLgoKQ291bnQgdG90YWwgbnVtYmVyIG9mIHNwZWNpZXMgZnJvbSBlYWNoIG1ldGhvZCBmb3IgZWFjaCBkYXRlCmBgYHtyfQplRE5BX3JpY2huZXNzIDwtIHRhbGx5KGVETkFfdW5pcXVlcywgbmFtZSA9ICJlRE5BIikKdHJhd2xfcmljaG5lc3MgPC0gdGFsbHkodHJhd2xfdW5pcXVlcywgbmFtZSA9ICJ0cmF3bCIpCgpzcGVjaWVzcmljaG5lc3MgPC0gZnVsbF9qb2luKGVETkFfcmljaG5lc3MsIHRyYXdsX3JpY2huZXNzLCBjKCJEYXRlY29kZSIgPSAiREFURUNPREUiKSkKc3BlY2llc3JpY2huZXNzIDwtIHBpdm90X2xvbmdlcihzcGVjaWVzcmljaG5lc3MsICFEYXRlY29kZSwgbmFtZXNfdG8gPSAiTWV0aG9kIiwgdmFsdWVzX3RvID0gIlJpY2huZXNzIikKCnNwZWNpZXNyaWNobmVzcyREYXRlY29kZSA8LSB5bWQoc3BlY2llc3JpY2huZXNzJERhdGVjb2RlKSAjIGNvbnZlcnQgdG8gZGF0ZSBmb3JtYXQgKGJldHRlciBmb3IgcGxvdHRpbmcpCgpzcGVjaWVzcmljaG5lc3MKYGBgCgoKUGxvdCBzaWRlLWJ5LXNpZGUKYGBge3J9CnNwZWNpZXNfcmljaG5lc3NfcGxvdCA8LSBnZ3Bsb3Qoc3BlY2llc3JpY2huZXNzLCBhZXMoeCA9RGF0ZWNvZGUsIHkgPSBSaWNobmVzcykpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gTWV0aG9kKSwgc2l6ZSA9IDMpICsKICB0aGVtZV9idygpICsKICB4bGFiKCIiKSArCiAgeWxhYigiU3BlY2llcyBSaWNobmVzcyIpCgpzcGVjaWVzX3JpY2huZXNzX3Bsb3QKCiMgZXhwb3J0IHBsb3QKZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvc3BlY2llc19yaWNobmVzc19wbG90LmVwcyIsIHBsb3QgPSBzcGVjaWVzX3JpY2huZXNzX3Bsb3QsIHVuaXRzID0gYygiaW4iKSwgd2lkdGggPSA0LCBoZWlnaHQgPSAzLCBkcGkgPSAzMDApCmBgYAoKCgpTdW0gdG90YWwgbnVtYmVyIG9mIHNwZWNpZXMgYWNyb3NzIGFsbCBkYXRlcy8gc3RhdGlvbnMgZm9yIGVudGlyZSBzdHVkeQpgYGB7cn0Kc3BlY2llc19zdW1zX2FidW5fdGFibGUgPC0gdHJhd2xfZUROQV9hYnVuX3RhYmxlICU+JQogIGdyb3VwX2J5KENvbW1vbk5hbWUpICU+JQogIHN1bW1hcmlzZShDUFVFID0gc3VtKFRyYXdsX0NQVUUsIG5hLnJtID0gVFJVRSksIAogICAgICAgICAgICAiVG90YWwgTGVuZ3RoIChUTCkgUFVFIiA9IHN1bShUcmF3bF9UTFBVRSwgbmEucm0gPSBUUlVFKSwgCiAgICAgICAgICAgICJUTCAqIFNoZWRkaW5nIEZhY3RvciBQVUUiID0gc3VtKFRyYXdsX0FsbG9tZXRyaWNfU2hlZGRpbmcsIG5hLnJtID0gVFJVRSksIAogICAgICAgICAgICBlRE5BID0gc3VtKGVETkFfUmVsQWJ1biwgbmEucm09VFJVRSkpICU+JQogIHBpdm90X2xvbmdlcighQ29tbW9uTmFtZSwgbmFtZXNfdG8gPSAiTWV0aG9kIiwgdmFsdWVzX3RvID0gIkFidW5kYW5jZSIpCiAgCiMgdHVybiB6ZXJvZXMgdG8gTkEgc28gdGhleSBkb24ndCBwbG90IApzcGVjaWVzX3N1bXNfYWJ1bl90YWJsZSA8LSBuYV9pZihzcGVjaWVzX3N1bXNfYWJ1bl90YWJsZSwwKQoKc3BlY2llc19zdW1zX2FidW5fdGFibGUKYGBgCgojIyBTcGVjaWVzIHRhbGx5IGFjcm9zcyB3aG9sZSBzdHVkeQoKRm9yIGVhY2ggc3BlY2llcywgcGxvdCBzaWRlLWJ5LXNpZGUgY29tcGFyaXNvbiBvZiBhYnVuZGFuY2UgKHN1bW1lZCBvdmVyIHdob2xlIHN0dWR5KSB1c2luZyBlYWNoIG1ldGhvZAoKYGBge3J9CiMgRmlyc3QgY3JlYXRlIGEgY3VzdG9tIGNvbG9yIHNjYWxlIHRvIG1ha2UgdGhpcyBwcmV0dHkKbXlDb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDExLCJTcGVjdHJhbCIpKSg0MCkKbmFtZXMobXlDb2xvcnMpIDwtIGxldmVscyh1bmlxdWUoc3BlY2llc19zdW1zX2FidW5fdGFibGUkQ29tbW9uTmFtZSkpCmNvbFNjYWxlIDwtIHNjYWxlX2NvbG91cl9tYW51YWwobmFtZSA9ICJDb21tb25OYW1lIix2YWx1ZXMgPSBteUNvbG9ycykKCnNwZWNpZXNfYWJ1bl9zdW1fcGxvdCA8LSBnZ3Bsb3Qoc3BlY2llc19zdW1zX2FidW5fdGFibGUsIGFlcyh4ID0gQWJ1bmRhbmNlLCB5ID0gcmVvcmRlcihDb21tb25OYW1lLCBBYnVuZGFuY2UsIGZ1bmN0aW9uKHgpe3N1bSh4LG5hLnJtID0gVFJVRSl9KSwgY29sb3IgPSBDb21tb25OYW1lKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDUpICsKICBmYWNldF93cmFwKH5mYWN0b3IoTWV0aG9kLCBsZXZlbHMgPSBjKCdDUFVFJywnVG90YWwgTGVuZ3RoIChUTCkgUFVFJywnVEwgKiBTaGVkZGluZyBGYWN0b3IgUFVFJywnZUROQScpKSwgc2NhbGVzID0gImZyZWVfeCIsIG5jb2wgPSA0KSArCiAgdGhlbWVfYncoKSArCiAgeGxhYigiQWJ1bmRhbmNlIikgKwogIHlsYWIoIiIpICsgCiAgY29sU2NhbGUgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCnNwZWNpZXNfYWJ1bl9zdW1fcGxvdApgYGAKCkV4cG9ydCBwbG90CmBgYHtyfQpnZ3NhdmUoZmlsZW5hbWUgPSAiRmlndXJlcy9zcGVjaWVzX2FidW5fc3VtX3Bsb3QuZXBzIiwgcGxvdCA9IHNwZWNpZXNfYWJ1bl9zdW1fcGxvdCwgdW5pdHMgPSBjKCJpbiIpLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2LCBkcGkgPSAzMDApCmBgYAoKCgoKIyBFeHBsb3JhdG9yeSBBbmFseXNlcwoKIyMgT3JkaW5hdGlvbnMgb24gZUROQQoKSSB3aWxsIHRyeSBQQ29BLCBQQ0EgKHRoZSBFdWNsaWRlYW4gUENvQSkgYW5kIE5NRFMgb3JkaW5hdGlvbnMgaW4gY29tYmluYXRpb24gd2l0aCBkaWZmZXJlbnQgdHJhbmZvcm1hdGlvbnMgYW5kIGRpc3RhbmNlIG1ldHJpY3MgaW4gb3JkZXIgdG8gc2VlIHdoaWNoIGV4cGxhaW4gdGhlIG1vc3QgdmFyaWFuY2UgaW4gdGhlIGRhdGFzZXQuCgotIE5PVEUtIHNlZSB0aGlzIFtkaXNjdXNzaW9uXShodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8zMDU5NjUvY2FuLWktdXNlLXRoZS1jbHItY2VudGVyZWQtbG9nLXJhdGlvLXRyYW5zZm9ybWF0aW9uLXRvLXByZXBhcmUtZGF0YS1mb3ItcGNhKSBhbmQgdGhpcyBbcGFwZXJdKGh0dHBzOi8vbGluay5zcHJpbmdlci5jb20vYXJ0aWNsZS8xMC4xMDA3L3MxMTAwNC0wMDgtOTE5Ni15KSBvbiB3aHkgQ0NBIHNob3VsZCBub3QgYmUgdXNlZCB3aXRoIENMUi10cmFuc2Zvcm1lZCBjb21wb3NpdGlvbmFsIGRhdGEgdG8gZXhwbG9yZSBjb3JyZWxhdGlvbnMuCgojIyMgUENBClBDQSBpcyBlc3NlbnRpYWxseSBhIHR5cGUgb2YgUENvQSAgdXNpbmcgdGhlIEV1Y2xpZGVhbiBkaXN0YW5jZSBtYXRyaXggYXMgaW5wdXQuIFdoZW4gY29tYmluZWQgd2l0aCBhIGxvZy1yYXRpbyB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgY291bnQgdGFibGUsIHRoaXMgaXMgZGVlbWVkIGFwcHJvcHJpYXRlIGZvciAqY29tcG9zaXRpb25hbCogZGF0YXNldHMuIEl0IGlzIGFsc28gW3JlY29tbWVuZGVkXShodHRwczovL3NpdGVzLmdvb2dsZS5jb20vc2l0ZS9tYjNndXN0YW1lL2luZGlyZWN0LWdyYWRpZW50LWFuYWx5c2lzL3BjYSkgYXMgYSBmaXJzdCBzdGVwIGluIGV4cGxvcmF0b3J5IGFuYWx5c2VzIG9mIHNlcXVlbmNpbmdpbmcgZGF0YXNldHMuCgpGaXJzdCBkbyBhICoqQ0xSLCBjZW50ZXJlZCBsb2cgcmF0aW8qKiB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgYWJzb2x1dGUgYWJ1bmRhbmNlIGRhdGEgKGFmdGVyIGZpbHRlcmluZyksIGFzIHN1Z2dlc3RlZCBieSBbR2xvb3IgZXQgYWwuIDIwMTddKGh0dHBzOi8vd3d3LmZyb250aWVyc2luLm9yZy9hcnRpY2xlcy8xMC4zMzg5L2ZtaWNiLjIwMTcuMDIyMjQvZnVsbCkgIApgYGB7cn0KIyBFc3RpbWF0ZSBjb3ZhcmlhbmNlIG1hdHJpeCBmb3IgQ0xSLXRyYW5zZm9ybWVkIEFTViB0YWJsZQpjbHJfYXN2X3RhYmxlX3BzIDwtIGRhdGEuZnJhbWUoY29tcG9zaXRpb25zOjpjbHIob3R1X3RhYmxlKHBzX25vX2VsYXNtbykpKQpgYGAKCgpHZW5lcmF0ZSB0aGUgUENBIGFuZCB2aXN1YWxpemUgYXhlcwpgYGB7cn0KIyBHZW5lcmF0ZSBhIFByaW5jaXBsZSBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgYW5kIGV2YWx1YXRlZCBiYXNlZCBvbiB0aGUgZWlnZW4gZGVjb21wb3NpdGlvbiBmcm9tIHNhbXBsZSBjb3ZhcmlhbmNlIG1hdHJpeC4gCmxvZ3JhdF9wY2EgPC0gcHJjb21wKGNscl9hc3ZfdGFibGVfcHMpIAojIE5PVEUtIHRoaXMgaXMgZXF1aXZhbGVudCB0byBmaXJzdCBtYWtpbmcgYSBFdWNsaWRlYW4gZGlzdGFuY2UgbWF0cml4IHVzaW5nIHRoZSBDTFIgZGF0YSB0YWJsZSBhbmQgdGhlbiBydW5uaW5nIGEgUENvQS4gQSBFdWNsaWRlYW4gZGlzdGFuY2UgbWF0cml4IG9mIGEgbG9nLXRyYW5zZm9ybWVkIGRhdGEgdGFibGUgPSBhbiBBaXRjaGlzb24gZGlzdGFuY2UgbWF0cml4LiBTbyB0aGlzIGlzIGVxdWl2YWxlbnQgdG8gdGhlIGNvbXBvc2l0aW9uYWwgbWV0aG9kcyBsaXN0ZWQgaW4gR2xvb3IgZXQgYWwuCgojIFZpc3VhbCByZXByZXNlbnRhdGlvbiB3aXRoIGEgc2NyZWVwbG90CmxvZ3JhdF92YXJpYW5jZXMgPC0gYXMuZGF0YS5mcmFtZShsb2dyYXRfcGNhJHNkZXZeMi9zdW0obG9ncmF0X3BjYSRzZGV2XjIpKSAlPiUgI0V4dHJhY3QgYXhlcwogICMgRm9ybWF0IHRvIHBsb3QKICBzZWxlY3QoUGVyY1ZhciA9ICdsb2dyYXRfcGNhJHNkZXZeMi9zdW0obG9ncmF0X3BjYSRzZGV2XjIpJykgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiUENheGlzIikgJT4lIAogIGRhdGEuZnJhbWUKaGVhZChsb2dyYXRfdmFyaWFuY2VzKQoKIyBQbG90IHNjcmVlcGxvdApnZ3Bsb3QobG9ncmF0X3ZhcmlhbmNlcywgYWVzKHggPSBhcy5udW1lcmljKFBDYXhpcyksIHkgPSBQZXJjVmFyKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJncmV5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh4ID0gIlBDIGF4aXMiLCB5ID0gIiUgVmFyaWFuY2UiLCB0aXRsZSA9ICJMb2ctUmF0aW8gUENBIFNjcmVlcGxvdCwgQ0xSIFRyYW5mb3JtYXRpb24iKQpgYGAKClRvdGFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBmaXJzdCB0aHJlZSBheGVzPSAxNS44ICsgMTAuNyArIDEwLjEgPSAqKjM2LjYlKiouIFNpbmNlIHRoZSBzZWNvbmQgYW5kIHRoaXJkIGF4ZXMgYXJlIHNpbWlsYXIsIHBsb3QgaW4gM0Qgd2l0aCAzIGF4ZXMuCgpWaXN1YWxpemUgdGhlIFBDQS0gCgpgYGB7cn0KIyBFeHRyYWN0IHZhcmlhbmNlcyBmcm9tIHRoZSBjbHIgcGNhCnBjYV9sb2dyYXRfZnJhbWUgPC0gZGF0YS5mcmFtZShsb2dyYXRfcGNhJHgpICU+JSAKICByb3duYW1lc190b19jb2x1bW4odmFyID0gIlNhbXBsZUlEIikKCiMgTWVyZ2UgbWV0YWRhdGEgaW50byB0aGUgcGNvYSBkYXRhIHRhYmxlCnBjYV9sb2dyYXRfZnJhbWUgPC0gbGVmdF9qb2luKHBjYV9sb2dyYXRfZnJhbWUsIG1ldGFkYXRhLCBieSA9ICJTYW1wbGVJRCIpCmhlYWQocGNhX2xvZ3JhdF9mcmFtZSkKCiMgU2VsZWN0IGVpZ2VudmFsdWVzIGZyb20gZGF0YWZyYW1lLCByb3VuZCB0byA0IHBsYWNlcyBhbmQgbXVsdGlwbHkgYnkgMTAwIGZvciBwbG90dGluZy4gVGhlc2Ugd2lsbCBiZSB0aGUgYXhlcyBmb3IgdGhlIDMtRCBwbG90CmVpZ2VudmFsdWVzPC1yb3VuZChsb2dyYXRfdmFyaWFuY2VzWywyXSwgZGlnaXRzID0gNCkqMTAwCgojIFBsb3RseSAtIDMtRApwY2FfbG9ncmF0IDwtIHBsb3RfbHkocGNhX2xvZ3JhdF9mcmFtZSwgdHlwZT0nc2NhdHRlcjNkJywgbW9kZT0nbWFya2VycycsCiAgICAgICAgeD1+UEMxLHk9flBDMix6PX5QQzMsY29sb3JzPX5icmV3ZXIucGFsKDExLCdQYWlyZWQnKSwKICAgICAgICBjb2xvcj1+U3RhdGlvbiwgc3ltYm9scyA9IGMoJ2NpcmNsZScsJ2RpYW1vbmQnKSwgc3ltYm9sPX5CYXlzaWRlKSU+JQogIGxheW91dChmb250PWxpc3Qoc2l6ZT0xMiksCiAgICAgICAgIHRpdGxlPSdDTFItRXVjbGlkZWFuIFBDQScsCiAgICAgICAgIHNjZW5lPWxpc3QoeGF4aXM9bGlzdCh0aXRsZT1wYXN0ZTAoJ0NvIDIgJyxlaWdlbnZhbHVlc1syXSwnJScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd3RpY2tsYWJlbHM9RkFMU0UsemVyb2xpbmVjb2xvcj0nYmxhY2snKSwKICAgICAgICAgICAgICAgICAgICB5YXhpcz1saXN0KHRpdGxlPXBhc3RlMCgnQ28gMyAnLGVpZ2VudmFsdWVzWzNdLCclJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93dGlja2xhYmVscz1GQUxTRSx6ZXJvbGluZWNvbG9yPSdibGFjaycpLAogICAgICAgICAgICAgICAgICAgIHpheGlzPWxpc3QodGl0bGU9cGFzdGUwKCdDbyAxICcsZWlnZW52YWx1ZXNbMV0sJyUnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3d0aWNrbGFiZWxzPUZBTFNFLHplcm9saW5lY29sb3I9J2JsYWNrJykpKQojIHBjYV9sb2dyYXQKCiMgc2F2ZSBpbiAiRW1iZWRkZWRfZmlndXJlcyIgZGlyZWN0b3J5IHNvIHRoYXQgaXQgY2FuIGJlIGhvc3RlZCBhdCBHaXRodWIgYW5kIGVtYmVkZGVkIGluIHRoaXMgbm90ZWJvb2sKd2l0aHI6OndpdGhfZGlyKCdFbWJlZGRlZF9maWd1cmVzJywgaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHBjYV9sb2dyYXQpLCBmaWxlPSJwY2FfbG9ncmF0X2VETkEuaHRtbCIsIHNlbGZjb250YWluZWQgPSBGKSkKCiAKYGBgCgoKPGlmcmFtZSBzcmM9IkVtYmVkZGVkX2ZpZ3VyZXMvcGNhX2xvZ3JhdF9lRE5BLmh0bWwiIGhlaWdodD0iNjAwcHgiIHdpZHRoPSIxMDAlIiBzdHlsZT0iYm9yZGVyOm5vbmU7Ij48L2lmcmFtZT4KCioqU3VtbWFyeSoqIFRoZSBDTFItRXVjbGlkZWFuIFBDQSByZXZlYWxzIHRoZXJlIGlzIHNvbWUgc2VwYXJhdGlvbiBhY2NvcmRpbmcgdG8gRWFzdCB2cyBXZXN0LiBUaGUgZmlyc3QgMyBQQ3Mgb25seSBleHBsYWluIH4zNiUgb2YgdGhlIHZhcmlhbmNlIHNvIGtlZXAgZ29pbmcgd2l0aCBkaWZmZXJlbnQgb3JkaW5hdGlvbnMgdG8gc2VlIGlmIHRoZXJlIGlzIGEgYmV0dGVyIHJlcHJlc2VudGF0aW9uCgoKCiMjIyBQQ29BIEphY2NhcmQKVGhlIG1vcmUgdHJhZGl0aW9uYWwgYXBwcm9hY2ggdG8gb3JkaW5hdGlvbnMgaXMgdG8gZG8gYSBQQ29BIG9uIGEgZGlzdGFuY2UgbWF0cml4IHN1Y2ggYXMgQnJheS1DdXJ0aXMsIEphY2NhcmQsIG9yIFVuaWZyYWMuIFdoZW4gY29tYmluZWQgd2l0aCBhIHRyYW5zZm9ybWF0aW9uLCB0aGV5IGJlY29tZSBtb3JlIGFwcHJvcHJpYXRlIGZvciBOR1MgZGF0YS4gT25lIHN1Y2ggY29tbW9uIHRyYW5zZm9ybWF0aW9uIGlzIHRoZSBIZWxsaW5nZXIgdHJhbnNmb3JtYXRpb24uCgpUaGUgZGlmZmVyZW50IGRpc3RhbmNlIG1hdHJpY2VzIGFsc28gdGVsbCB5b3UgYSBmZXcgZGlmZmVyZW50IHRoaW5ncyBhYm91dCB0aGUgZGF0YXNldCBzbyBJIHdpbGwgcnVuIHRyeSBkaWZmZXJlbnQgb25lIHRvIHRyeSB0byBzZWUgaWYgSSBjYW4gdGVhc2UgdGhvc2Ugb3V0LiAKCkJlZm9yZSBjYWxjdWxhdGluZyBhbnkgZGlzdGFuY2UgbWF0cml4LCBkbyBhIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSBmaWx0ZXJlZCBjb3VudCB0YWJsZS4gSGVsbGluZ2VyIHRyYW5zZm9ybWF0aW9uIGlzIHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgcmVsYXRpdmUgYWJ1bmRhbmNlLCBzbyBjYWxjdWxhdGUgaXQgYmFzZWQgb24gdGhlIHBzX3JhIG9iamVjdDoKCmBgYHtyfQpwc19oZWxsaW5nZXIgPC0gdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMocHNfcmFfbm9fZWxhc21vLCBmdW5jdGlvbih4KXtzcXJ0KHgpfSkKYGBgCgoKRmlyc3QsICoqSmFjY2FyZCoqLCB3aGljaCBidWlsZHMgdGhlIGRpc3RhbmNlIG1hdHJpeCBiYXNlZCBvbiBwcmVzZW5jZS9hYnNlbmNlIGJldHdlZW4gc2FtcGxlcy4gSXQgZG9lcyBub3QgdGFrZSBpbnRvIGFjY291bnQgcmVsYXRpdmUgYWJ1bmRhbmNlIG9mIHRoZSB0YXhhLiBUaGVyZWZvcmUgdGhpcyBmdW5jdGlvbnMgd2VsbCBmb3IgZGV0ZXJtaW5pbmcgZGlmZmVyZW5jZXMgZHJpdmVuIGJ5IHJhcmUgdGF4YSwgd2hpY2ggYXJlIHdlaWdoZWQgdGhlIHNhbWUgYXMgYWJ1bmRhbnQgdGF4YS4KYGBge3J9CmphY19kbWF0PC12ZWdkaXN0KG90dV90YWJsZShwc19oZWxsaW5nZXIpLG1ldGhvZD0iamFjY2FyZCIpICMgSmFjY2FyZCBkaXN0IG1ldHJpYwpwY29hX2phYzwtYXBlOjpwY29hKGphY19kbWF0KSAjIHBlcmZvcm0gUENvQQoKIyBFeHRyYWN0IHZhcmlhbmNlcyBmcm9tIHBjb2EsIGZyb20gamFjY2FyZCBjYWxjdWxhdGVkIGRpc3QuIG1ldHJpYwpqYWNfdmFyaWFuY2VzIDwtIGRhdGEuZnJhbWUocGNvYV9qYWMkdmFsdWVzJFJlbGF0aXZlX2VpZykgJT4lIAogIHNlbGVjdChQZXJjVmFyID0gJ3Bjb2FfamFjLnZhbHVlcy5SZWxhdGl2ZV9laWcnKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJQQ2F4aXMiKSAlPiUgCiAgZGF0YS5mcmFtZQpoZWFkKGphY192YXJpYW5jZXMpCgojIE1ha2UgYSBzY3JlZXBsb3QKZ2dwbG90KGphY192YXJpYW5jZXMsIGFlcyh4ID0gYXMubnVtZXJpYyhQQ2F4aXMpLCB5ID0gUGVyY1ZhcikpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiZ3JleSIsIGNvbG9yID0gImJsYWNrIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnMoeCA9ICJQQyBheGlzIiwgeSA9ICIlIFZhcmlhbmNlIiwgdGl0bGUgPSAiSmFjY2FyZCBQQ29BIFNjcmVlcGxvdCIpCmBgYApUaGUgZmlyc3QgdHdvIGF4ZXMgKDE5LjAgKyA5LjcgPSAyOC43KSBhcmUgT0suIEJ1dCBwbG90IHRoZSBmaXJzdCAzIGF4ZXMgc2luY2UgdGhlIDJuZCBhbmQgM3JkIGV4cGxhaW4gYSBzaW1pbGFyIGFtb3VudCBvZiB2YXJpYW5jZSwgKDE5LjAgKyA5LjcgKyA4LjQgPSAqKjM3LjElKiogdG90YWwgdmFyaWFuY2UgZXhwbGFpbmVkKSAKClBsb3QgaW4gM0Qgd2l0aCBQbG90bHkKYGBge3J9CiMgRXh0cmFjdCB2YXJpYW5jZXMgZnJvbSB0aGUgamFjY2FyZCBwY29hCnBjb2FfamFjX2RmIDwtIGRhdGEuZnJhbWUocGNvYV9qYWMkdmVjdG9ycykgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiU2FtcGxlSUQiKQoKIyBNZXJnZSBtZXRhZGF0YSBpbnRvIHRoZSBwY29hIGRhdGEgdGFibGUKcGNvYV9qYWNfZGYgPC0gbGVmdF9qb2luKHBjb2FfamFjX2RmLCBtZXRhZGF0YSwgYnkgPSAiU2FtcGxlSUQiKQpoZWFkKHBjb2FfamFjX2RmKQoKIyBTZWxlY3QgZWlnZW52YWx1ZXMgZnJvbSBkYXRhZnJhbWUsIHJvdW5kIHRvIDQgcGxhY2VzIGFuZCBtdWx0aXBseSBieSAxMDAgZm9yIHBsb3R0aW5nLiBUaGVzZSB3aWxsIGJlIHRoZSBheGVzIGZvciB0aGUgMy1EIHBsb3QKZWlnZW52YWx1ZXM8LXJvdW5kKGphY192YXJpYW5jZXNbLDJdLCBkaWdpdHMgPSA0KSoxMDAKCiMgUGxvdGx5IC0gMy1ECnBjb2FfamFjY2FyZCA8LSBwbG90X2x5KHBjb2FfamFjX2RmLCB0eXBlPSdzY2F0dGVyM2QnLCBtb2RlPSdtYXJrZXJzJywKICAgICAgICB4PX5BeGlzLjIseT1+QXhpcy4zLHo9fkF4aXMuMSxjb2xvcnM9fmJyZXdlci5wYWwoMTEsJ1BhaXJlZCcpLAogICAgICAgIGNvbG9yPX5TdGF0aW9uLCBzeW1ib2xzID0gYygnY2lyY2xlJywnZGlhbW9uZCcpLCBzeW1ib2w9fkJheXNpZGUpJT4lCiAgbGF5b3V0KGZvbnQ9bGlzdChzaXplPTEyKSwKICAgICAgICAgdGl0bGU9J1BDb0EgSmFjY2FyZCBEaXN0YW5jZScsCiAgICAgICAgIHNjZW5lPWxpc3QoeGF4aXM9bGlzdCh0aXRsZT1wYXN0ZTAoJ0NvIDIgJyxlaWdlbnZhbHVlc1syXSwnJScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd3RpY2tsYWJlbHM9RkFMU0UsemVyb2xpbmVjb2xvcj0nYmxhY2snKSwKICAgICAgICAgICAgICAgICAgICB5YXhpcz1saXN0KHRpdGxlPXBhc3RlMCgnQ28gMyAnLGVpZ2VudmFsdWVzWzNdLCclJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93dGlja2xhYmVscz1GQUxTRSx6ZXJvbGluZWNvbG9yPSdibGFjaycpLAogICAgICAgICAgICAgICAgICAgIHpheGlzPWxpc3QodGl0bGU9cGFzdGUwKCdDbyAxICcsZWlnZW52YWx1ZXNbMV0sJyUnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3d0aWNrbGFiZWxzPUZBTFNFLHplcm9saW5lY29sb3I9J2JsYWNrJykpKQojIHBjb2FfamFjY2FyZAoKIyBzYXZlIGZpZ3VyZSBpbiAiRW1iZWRkZWRfZmlndXJlcyIgZGlyZWN0b3J5IHNvIHRoYXQgaXQgY2FuIGJlIGhvc3RlZCBhdCBHaXRodWIgYW5kIGVtYmVkZGVkIGluIHRoaXMgbm90ZWJvb2sKd2l0aHI6OndpdGhfZGlyKCdFbWJlZGRlZF9maWd1cmVzJywgaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHBjb2FfamFjY2FyZCksIGZpbGU9InBjb2FfamFjY2FyZF9lRE5BLmh0bWwiLCBzZWxmY29udGFpbmVkID0gRikpCmBgYAoKPGlmcmFtZSBzcmM9IkVtYmVkZGVkX2ZpZ3VyZXMvcGNvYV9qYWNjYXJkX2VETkEuaHRtbCIgaGVpZ2h0PSI2MDBweCIgd2lkdGg9IjEwMCUiIHN0eWxlPSJib3JkZXI6bm9uZTsiPjwvaWZyYW1lPgoKVGhlIEphY2NhcmQtUENvQSBzaG93cyBzb21lIHNlcGFyYXRpb24gYWxvbmcgYXhpcyAyIGFuZCBheGlzIDMgaW4gRWFzdCB2cyBXZXN0IGRpZmZlcmVuY2VzLiBWZXJ5IHNpbWlsYXIgJSB2YXJpYW5jZSBleHBsYWluZWQgdG8gdGhlIFBDQS4KCgojIyMgUENvQSBCcmF5IEN1cnRpcwoKTmV4dCwgdHJ5IGEgKipCcmF5LUN1cnRpcyoqIGRpc3RhbmNlIG1hdHJpeCB3aXRoIFBDb0EsIHdoaWNoIGJ1aWxkcyB0aGUgZGlzdGFuY2UgbWF0cml4IGJhc2VkIG9uIHByZXNlbmNlL2Fic2VuY2UgYmV0d2VlbiBzYW1wbGVzICphbmQqIHJlbGF0aXZlIGFidW5kYW5jZSBkaWZmZXJlbmNlcy4gVGhpcyBvcmRpbmF0aW9uIHdpbGwgcmVwcmVzZW50IHdlbGwgdGhlIGRpZmZlcmVuY2VzIGluIHNhbXBsZXMgdGhhdCBhcmUgZHJpdmVuIGJ5IHRheGEgd2l0aCBoaWdoIHJlbGF0aXZlIGFidW5kYW5jZXMuCgpOT1RFOiBJIG5lZWQgdG8gdXNlIGEgY29ycmVjdGlvbiBoZXJlIGZvciBuZWdhdGl2ZSBlaWdlbnZhbHVlcy4gUmVhZCBtb3JlIFtoZXJlXShodHRwczovL2Zyb210aGVib3R0b21vZnRoZWhlYXAubmV0L3NsaWRlcy9pbnRyby12ZWdhbi13ZWJpbmFyLTIwMjAvaW50cm8tdG8tdmVnYW4uaHRtbCM1MykKYGBge3J9CmJyYXlfZG1hdDwtdmVnZGlzdChvdHVfdGFibGUocHNfaGVsbGluZ2VyKSxtZXRob2Q9ImJyYXkiKSAjIEJyYXktQ3VydGlzIGRpc3QgbWV0cmljCnBjb2FfYnJheTwtYXBlOjpwY29hKGJyYXlfZG1hdCkgIyBwZXJmb3JtIFBDb0EgaW4gYXBlLiBCdXQgZ2V0dGluZyBuZWdhdGl2ZSBlaWdlbnZhbHVlcywgc28gbmVlZCB0byBhZGQgY29ycmVjdGlvbi4gd2NtZHNjYWxlIGZyb20gYmFzZSBSIGFsc28gcGVyZm9ybXMgUENvQSBhbmQgY2FuIGFkZCBjYWlsbGlleiBjb3JyZWN0aW9uCnBjb2FfYnJheSA8LSB3Y21kc2NhbGUoYnJheV9kbWF0LCBlaWcgPSBUUlVFLCBhZGQgPSAiY2FpbGxpZXoiKQoKIyBjaGVjayBvdXQgc3VtbWFyeSBvZiBQQ29BCmVpZ2VudmFscyhwY29hX2JyYXkpICU+JQogIHN1bW1hcnkoKSAtPiBldgpldgoKIyBleHRyYWN0IHZhcmlhbmNlcyBhbmQgcHV0IGluIHRpYmJsZQpicmF5X3ZhcmlhbmNlcyA8LSBOVUxMCmZvciAoaSBpbiAxOmxlbmd0aChlaWdlbnZhbHMocGNvYV9icmF5KSkpewogIGJyYXlfdmFyaWFuY2VzW2ldIDwtIGVpZ2VudmFscyhwY29hX2JyYXkpW2ldL3N1bShlaWdlbnZhbHMocGNvYV9icmF5KSkKfQoKIyBFeHRyYWN0IHZhcmlhbmNlcyBmcm9tIHBjb2EsIGZyb20gY2FsY3VsYXRlZCBkaXN0LiBtZXRyaWMKYnJheV92YXJpYW5jZXMgPC0gdGliYmxlKHJvdW5kKGJyYXlfdmFyaWFuY2VzLDMpKSAlPiUKICBzZWxlY3QoUGVyY1ZhciA9ICdyb3VuZChicmF5X3ZhcmlhbmNlcywgMyknKSAlPiUKICByb3duYW1lc190b19jb2x1bW4odmFyID0gIlBDYXhpcyIpICU+JQogIGRhdGEuZnJhbWUKaGVhZChicmF5X3ZhcmlhbmNlcykKCiMgTWFrZSBhIHNjcmVlcGxvdApnZ3Bsb3QoYnJheV92YXJpYW5jZXMsIGFlcyh4ID0gYXMubnVtZXJpYyhQQ2F4aXMpLCB5ID0gUGVyY1ZhcikpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiZ3JleSIsIGNvbG9yID0gImJsYWNrIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnMoeCA9ICJQQyBheGlzIiwgeSA9ICIlIFZhcmlhbmNlIiwgdGl0bGUgPSAiQnJheS1DdXJ0aXMgUENvQSBTY3JlZXBsb3QiKQpgYGAKVGhlIGZpcnN0IHR3byBheGVzICgyMS4xICsgMTEuMCkgYXJlIHByZXR0eSBnb29kIGFnYWluIGJ1dCBJIGFtIHN0aWxsIGdvaW5nIHRvIGV4cGVyaW1lbnQgaW4gdGhlIHBsb3Qgd2l0aCB0aGUgM3JkIGF4aXMgc2luY2UgaXQgaXMgc2ltaWxhciB0byB0aGUgc2Vjb25kICg5LjUlOyB0b3RhbCB2YXJpYW5jZSBleHBsYWluZWQgPSAqKjQxLjYlKiopCgoKCgpQbG90IGluIDNEIHdpdGggUGxvdGx5CmBgYHtyfQojIEV4dHJhY3QgdmFyaWFuY2VzIGZyb20gdGhlIHBjb2EKcGNvYV9icmF5X2RmIDwtIGRhdGEuZnJhbWUocGNvYV9icmF5JHBvaW50cykgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiU2FtcGxlSUQiKQoKIyBNZXJnZSBtZXRhZGF0YSBpbnRvIHRoZSBwY29hIGRhdGEgdGFibGUKcGNvYV9icmF5X2RmIDwtIGxlZnRfam9pbihwY29hX2JyYXlfZGYsIG1ldGFkYXRhLCBieSA9ICJTYW1wbGVJRCIpCmhlYWQocGNvYV9icmF5X2RmKQoKIyBTZWxlY3QgZWlnZW52YWx1ZXMgZnJvbSBkYXRhZnJhbWUsIHJvdW5kIHRvIDQgcGxhY2VzIGFuZCBtdWx0aXBseSBieSAxMDAgZm9yIHBsb3R0aW5nLiBUaGVzZSB3aWxsIGJlIHRoZSBheGVzIGZvciB0aGUgMy1EIHBsb3QKZWlnZW52YWx1ZXM8LXJvdW5kKGJyYXlfdmFyaWFuY2VzWywyXSwgZGlnaXRzID0gNCkqMTAwCgojIFBsb3RseSAtIDMtRApwY29hX2JyYXkgPC0gcGxvdF9seShwY29hX2JyYXlfZGYsIHR5cGU9J3NjYXR0ZXIzZCcsIG1vZGU9J21hcmtlcnMnLCAKICAgICAgICAgICAgICAgICAgICAgeD1+RGltMiwgeT1+RGltMywgej1+RGltMSwgY29sb3JzPX5icmV3ZXIucGFsKDExLCdQYWlyZWQnKSwKICAgICAgICBjb2xvcj1+U3RhdGlvbiwgc3ltYm9scyA9IGMoJ2NpcmNsZScsJ2RpYW1vbmQnKSwgc3ltYm9sPX5CYXlzaWRlKSU+JSAgCiAgbGF5b3V0KGZvbnQ9bGlzdChzaXplPTEyKSwKICAgICAgICAgdGl0bGU9J1BDb0EgQnJheS1DdXJ0aXMgRGlzdGFuY2UnLAogICAgICAgICBzY2VuZT1saXN0KHhheGlzPWxpc3QodGl0bGU9cGFzdGUwKCdDbyAyICcsZWlnZW52YWx1ZXNbMl0sJyUnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3d0aWNrbGFiZWxzPUZBTFNFLHplcm9saW5lY29sb3I9J2JsYWNrJyksCiAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT1wYXN0ZTAoJ0NvIDMgJyxlaWdlbnZhbHVlc1szXSwnJScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd3RpY2tsYWJlbHM9RkFMU0UsemVyb2xpbmVjb2xvcj0nYmxhY2snKSwKICAgICAgICAgICAgICAgICAgICB6YXhpcz1saXN0KHRpdGxlPXBhc3RlMCgnQ28gMSAnLGVpZ2VudmFsdWVzWzFdLCclJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93dGlja2xhYmVscz1GQUxTRSx6ZXJvbGluZWNvbG9yPSdibGFjaycpKSkKIyBwY29hX2JyYXkKCiMgc2F2ZSBpbiAiRW1iZWRkZWRfZmlndXJlcyIgZGlyZWN0b3J5IHNvIHRoYXQgaXQgY2FuIGJlIGhvc3RlZCBhdCBHaXRodWIgYW5kIGVtYmVkZGVkIGluIHRoaXMgbm90ZWJvb2sKd2l0aHI6OndpdGhfZGlyKCdFbWJlZGRlZF9maWd1cmVzJywgaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHBjb2FfYnJheSksIGZpbGU9InBjb2FfYnJheV9lRE5BLmh0bWwiLCBzZWxmY29udGFpbmVkID0gRikpCmBgYAoKPGlmcmFtZSBzcmM9IkVtYmVkZGVkX2ZpZ3VyZXMvcGNvYV9icmF5X2VETkEuaHRtbCIgaGVpZ2h0PSI2MDBweCIgd2lkdGg9IjEwMCUiIHN0eWxlPSJib3JkZXI6bm9uZTsiPjwvaWZyYW1lPgoKClRoZXNlIHJlc3VsdHMgYWxvbmcgYXhlcyAxLCAyLCBhbmQgMyBhcmUgc2ltaWxhciB0byBKYWNjYXJkLCBidXQgdGhlcmUgaXMgbW9yZSBzZXBhcmF0aW9uIGFsb25nIGF4aXMgMiwgaW5kaWNhdGluZyB0aGF0IGluY29ycG9yYXRpbmcgdGhlIGRpZmZlcmVuY2VzIGluIGFidW5kYW5jZSBoZWxwcyBleHBsYWluIG1vcmUgdmFyaWFuY2UgaW4gdGhlIGRhdGFzZXQuIFRvdGFsIHZhcmlhbmNlIGV4cGxhaW5lZCBpcyBoaWdoZXN0IHNvIGZhci4KCgoKCiMjIyBOTURTIEFpdGNoaXNvbgpMYXN0bHksIHRyeSBhIG5vbi1tZXRyaWMgZGltZW5zaW9uYWwgc2NhbGluZyBvcmRpbmF0aW9uLiBQQ0EvUENvQSBhcmUgbWV0cmljIGFuZCBhdHRlbXB0IHRvIHJvdGF0ZSBheGVzIHRvIGZpdCB0aGUgZGlzdGFuY2UgbWF0cml4IGRpc3RyaWJ1dGlvbi4gQW4gTk1EUyByZXByZXNlbnRzIHRoZSBkYXRhIGluIDItYXhlcywgYnkgY29uc3RyYWluaW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHBvaW50cy4gU2ltaWxhciB0byBhYm92ZSwgdGhpcyBjYW4gYmUgY29tYmluZWQgd2l0aCBkaWZmZXJlbnQgcHJlLXRyZWF0bWVudCBvZiB0aGUgZGF0YS4KCkZpcnN0IHRyeSB0aGUgY29tcG9zaXRpb25hbCBhcHByb2FjaCwgYW4gKipOTURTIG9uIENMUi10cmFuZm9ybWVkIGRhdGEgdXNpbmcgdGhlIEV1Y2xpZGVhbiBkaXN0YW5jZXMqKiAoYWthIEFpdGNoaXNvbiBkaXN0YW5jZSkKCmBgYHtyfQpldWNfZG1hdDwtZGlzdChjbHJfYXN2X3RhYmxlX3BzLCBtZXRob2QgPSAiZXVjbGlkZWFuIikgIyBCdWlsZCB0aGUgQWl0Y2hpc29uIGRpc3RhbmNlIG1hdHJpeApldWNfbm1kcyA8LSBtZXRhTURTKGV1Y19kbWF0LCBrPTIsIGF1dG90cmFuc2Zvcm09RkFMU0UpICMgUnVuIHRoZSBvcmRpbmF0aW9uCmV1Y19ubWRzJHN0cmVzcyAjQ2hlY2sgdGhlIHN0cmVzcy4gTGVzcyB0aGFuIDAuMSBpcyBnb29kLiBMZXNzIHRoYW4gMC4wNSBpcyBiZXR0ZXIuIFRoaXMgd2lsbCBiZSBkaWZmZXJlbnQgZWFjaCB0aW1lLCBzaW5jZSBpdCBpcyBpdGVyYXRpdmVseSBmaW5kaW5nIGEgdW5pcXVlIHNvbHV0aW9uIGVhY2ggdGltZSAoYWx0aG91Z2ggdGhlIHNob3VsZCBsb29rIHNpbWlsYXIpCgojIEV4dHJhY3QgcG9pbnRzIGZyb20gbm1kcyBhbmQgbWVyZ2UgaW50byBkYXRhIGZyYW1lIHdpdGggbWV0YWRhdGEgCmV1Y19ubWRzX2RmIDwtIGRhdGEuZnJhbWUoZXVjX25tZHMkcG9pbnRzKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJTYW1wbGVJRCIpCgojIE1lcmdlIG1ldGFkYXRhIGludG8gdGhlIHBjb2EgZGF0YSB0YWJsZQpldWNfbm1kc19kZiA8LSBsZWZ0X2pvaW4oZXVjX25tZHNfZGYsIG1ldGFkYXRhLCBieSA9ICJTYW1wbGVJRCIpCmhlYWQoZXVjX25tZHNfZGYpCgoKCiMjIFBsb3R0aW5nIGV1Y2xpZGVhbiBkaXN0YW5jZSBOTURTCm5tZHNfYWl0Y2ggPC0gZ2dwbG90KGV1Y19ubWRzX2RmLGFlcyh4ID0gTURTMSwgeSA9IE1EUzIsIGNvbG9yID0gU3RhdGlvbiwgc2hhcGUgPSBCYXlzaWRlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9ICJOTURTIDEiLCB5ID0gIk5NRFMgMiIsIHRpdGxlID0gcGFzdGUwKCdBaXRjaGlzb24gRGlzdGFuY2UgTk1EUywgU3RyZXNzID0gJywgcm91bmQoZXVjX25tZHMkc3RyZXNzLDIpKSkgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkKCm5tZHNfYWl0Y2gKCmdnc2F2ZSgiZmlndXJlcy9ubWRzX2FpdGNoX2VETkEuZXBzIixubWRzX2FpdGNoLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUsIHVuaXRzID0gYygiaW4iKSkKYGBgClRoZSBhYm92ZSBoYXMgYSByZWxhdGl2ZWx5ICoqaGlnaCBzdHJlc3MgKD4wLjIpKiogc28gc2hvdWxkIGJlIGludGVycHJldGVkIHdpdGggY2F1dGlvbi4gQnV0IGl0IGRvZXMgc2hvdyBzb21lIHNlcGFyYXRpb24gRWFzdCB2cyBXZXN0IGFsb25nIE5NRFMgMS4KCiMjIyBOTURTIEphY2FhcmQKCgpOZXh0IHRyeSBhICoqSmFjY2FyZCBOTURTKiosIHdoaWNoIHdpbGwgcmVwcmVzZW50IGRpZmZlcmVuY2VzIGluIHByZXNlbmNlL2Fic2VuY2UgYW1vbmcgc2FtcGxlcywgZW1waGFzaXppbmcgYm90aCBhYnVuZGFudCBhbmQgcmFyZSB0YXhhIHRoZSBzYW1lCgpgYGB7cn0KamFjX25tZHMgPC0gbWV0YU1EUyhqYWNfZG1hdCwgaz0yLCBhdXRvdHJhbnNmb3JtPUZBTFNFKSAjIFJ1biB0aGUgb3JkaW5hdGlvbi4gRGlzdGFuY2UgbWF0cml4IHdhcyBhbHJlYWR5IGNhbGN1bGF0ZWQgYWJvdmUKamFjX25tZHMkc3RyZXNzICNDaGVjayB0aGUgc3RyZXNzLiBMZXNzIHRoYW4gMC4xIGlzIGdvb2QuIExlc3MgdGhhbiAwLjUgaXMgYmV0dGVyLiBUaGlzIHdpbGwgYmUgZGlmZmVyZW50IGVhY2ggdGltZSwgc2luY2UgaXQgaXMgaXRlcmF0aXZlbHkgZmluZGluZyBhIHVuaXF1ZSBzb2x1dGlvbiBlYWNoIHRpbWUgKGFsdGhvdWdoIHRoZSBzaG91bGQgbG9vayBzaW1pbGFyKQoKIyBFeHRyYWN0IHBvaW50cyBmcm9tIG5tZHMgYW5kIG1lcmdlIGludG8gZGF0YSBmcmFtZSB3aXRoIG1ldGFkYXRhIApqYWNfbm1kc19kZiA8LSBkYXRhLmZyYW1lKGphY19ubWRzJHBvaW50cykgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiU2FtcGxlSUQiKQoKIyBNZXJnZSBtZXRhZGF0YSBpbnRvIHRoZSBwY29hIGRhdGEgdGFibGUKamFjX25tZHNfZGYgPC0gbGVmdF9qb2luKGphY19ubWRzX2RmLCBtZXRhZGF0YSwgYnkgPSAiU2FtcGxlSUQiKQpoZWFkKGphY19ubWRzX2RmKQoKCgojIyBQbG90dGluZyBldWNsaWRlYW4gZGlzdGFuY2UgTk1EUwpubWRzX2phY2NhcmQgPC0gZ2dwbG90KGphY19ubWRzX2RmLGFlcyh4ID0gTURTMSwgeSA9IE1EUzIsIGNvbG9yID0gU3RhdGlvbiwgc2hhcGUgPSBCYXlzaWRlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9ICJOTURTIDEiLCB5ID0gIk5NRFMgMiIsIHRpdGxlID0gcGFzdGUwKCdKYWNjYXJkIERpc3RhbmNlIE5NRFMsIFN0cmVzcyA9ICcsIHJvdW5kKGphY19ubWRzJHN0cmVzcywyKSkpICsKICBjb29yZF9maXhlZChyYXRpbyA9IDEpCgpubWRzX2phY2NhcmQKCmdnc2F2ZSgiZmlndXJlcy9ubWRzX2phY2NhcmRfZUROQS5lcHMiLG5tZHNfamFjY2FyZCwgd2lkdGggPSA3LCBoZWlnaHQgPSA1LCB1bml0cyA9IGMoImluIikpCmBgYApUaGlzIGlzIHN0aWxsIGEgKiptb2RlcmF0ZWx5IGhpZ2ggc3RyZXNzICg+MC4xKSoqIHNvIHNob3VsZCBiZSBpbnRlcnByZXRlZCB3aXRoIGNhdXRpb24uIFNpbWlsYXIgdG8gQWl0Y2hpc29uLWRpc3RhbmNlIG5NRFMgYnV0IHRoZXJlIGlzIGEgbGl0dGxlIG1vcmUgc2VwYXJhdGlvbiBvZiBFYXN0IHZzIFdlc3Qgb24gTk1EUyAyIGF4aXMuCgojIyMgTk1EUyBCcmF5IEN1cnRpcwoKTmV4dCB0cnkgYSAqKkJyYXktQ3VyaXMgTk1EUyoqLCB3aGljaCB3aWxsIHJlcHJlc2VudCBkaWZmZXJlbmNlcyBpbiBwcmVzZW5jZS9hYnNlbmNlIGFtb25nIHNhbXBsZXMgKmFuZCogcmVsYXRpdmUgYWJ1bmRhbmNlLCB0aHVzIGVtcGhhc2l6aW5nIGltcGFjdHMgb2YgaGlnaGx5IGFidW5kYW50IHRheGEuCgpgYGB7cn0KYnJheV9ubWRzIDwtIG1ldGFNRFMoYnJheV9kbWF0LCBrPTIsIGF1dG90cmFuc2Zvcm09RkFMU0UpICMgUnVuIHRoZSBvcmRpbmF0aW9uLiBEaXN0YW5jZSBtYXRyaXggd2FzIGFscmVhZHkgY2FsY3VsYXRlZCBhYm92ZQpicmF5X25tZHMkc3RyZXNzICNDaGVjayB0aGUgc3RyZXNzLiBMZXNzIHRoYW4gMC4xIGlzIGdvb2QuIExlc3MgdGhhbiAwLjUgaXMgYmV0dGVyLiBUaGlzIHdpbGwgYmUgZGlmZmVyZW50IGVhY2ggdGltZSwgc2luY2UgaXQgaXMgaXRlcmF0aXZlbHkgZmluZGluZyBhIHVuaXF1ZSBzb2x1dGlvbiBlYWNoIHRpbWUgKGFsdGhvdWdoIHRoZSBzaG91bGQgbG9vayBzaW1pbGFyKQoKIyBFeHRyYWN0IHBvaW50cyBmcm9tIG5tZHMgYW5kIG1lcmdlIGludG8gZGF0YSBmcmFtZSB3aXRoIG1ldGFkYXRhIApicmF5X25tZHNfZGYgPC0gZGF0YS5mcmFtZShicmF5X25tZHMkcG9pbnRzKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJTYW1wbGVJRCIpCgojIE1lcmdlIG1ldGFkYXRhIGludG8gdGhlIHBjb2EgZGF0YSB0YWJsZQpicmF5X25tZHNfZGYgPC0gbGVmdF9qb2luKGJyYXlfbm1kc19kZiwgbWV0YWRhdGEsIGJ5ID0gIlNhbXBsZUlEIikKaGVhZChicmF5X25tZHNfZGYpCgoKCiMjIFBsb3R0aW5nIGV1Y2xpZGVhbiBkaXN0YW5jZSBOTURTCm5tZHNfYnJheSA8LSBnZ3Bsb3QoYnJheV9ubWRzX2RmLGFlcyh4ID0gTURTMSwgeSA9IE1EUzIsIGNvbG9yID0gU3RhdGlvbiwgc2hhcGUgPSBCYXlzaWRlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9ICJOTURTIDEiLCB5ID0gIk5NRFMgMiIsIHRpdGxlID0gcGFzdGUwKCdCcmF5LUN1cnRpcyBEaXN0YW5jZSBOTURTLCBTdHJlc3MgPSAnLCByb3VuZChicmF5X25tZHMkc3RyZXNzLDIpKSkgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkKCm5tZHNfYnJheQoKZ2dzYXZlKCJmaWd1cmVzL25tZHNfYnJheV9lRE5BLmVwcyIsbm1kc19icmF5LCB3aWR0aCA9IDcsIGhlaWdodCA9IDUsIHVuaXRzID0gYygiaW4iKSkKYGBgClZlcnkgc2ltaWxhciB0byBKYWNjYXJkIHJlc3VsdHMuICoqTW9kZXJhdGVseSBoaWdoIHN0cmVzcyAoMC4xNSkqKgoKCiMjIyBlRE5BIE9yZGluYXRpb25zIFN1bW1hcnkKVGhlIG9yZGluYXRpb24gdGhhdCBleHBsYWluZWQgdGhlIG1vc3QgdmFyaWFuY2UgaW4gdGhlIGVETkEgZGF0YXNldCB3YXMgdGhlIFBDb0EgdXNpbmcgdGhlIEJyYXktQ3VydGlzIGRpc3NpbWlsYXJpdHkgbWF0cml4IGFmdGVyIEhlbGxpbmdlciB0cmFuc2Zvcm1hdGlvbi4gVGhpcyBpcyBzaW1pbGFyIHRvIHRoZSBhcHByb2FjaCBwcmVzZW50ZWQgaW4gW0xhY291cnNpw6hyZeKAkFJvdXNzZWwgZXQgYWwuIDIwMThdKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL2Ficy8xMC4xMDAyL2VjZTMuNDIxMykuIFVzZSB0aGlzIHJlcHJlc2VudGF0aW9uIGdvaW5nIGZvcndhcmQuCgotIE5leHQ6IGZpdCBlbnZpcm9ubWVudGFsIHZlY3RvcnMgdG8gdGhpcyBvcmRpbmF0aW9uIHRvIHNlZSB3aGljaCBjYW4gYmUgcG9zc2libHkgZXhwbGFpbiBzb21lIG9mIHRoZSB2YXJpYXRpb24gYW1vbmcgc2FtcGxlcyBhbmQgYW1vbmcgc3BlY2llcy4KCgojIyMgUENvQSB3aXRoIEVudmlyb25tZW50YWwgVmFyaWFibGVzCgpSZWNyZWF0ZSwgaW4gMkQsIHRoZSBmaXJzdCB0d28gYXhlcyBvZiB0aGUgb3JkaW5hdGlvbiAoUENvQSB3aXRoIEJyYXkgZGlzdGFuY2UgbWF0cngvIEhlbGxpbmdlciB0cmFuc2Zvcm1hdGlvbikgYW5kIHVzZSBgZW52Zml0YCBmcm9tIHZlZ2FuIHRvIHRlc3QgYW5kIGZpdCBlbnZpcm9ubWVudGFsIHZhcmlhYmxlcy4KCklmIG5vdCBtYWtpbmcgM0QgcGxvdHMsIGNhbiBkbyB0aGlzIGRpcmVjdGx5IGluIHBoeWxvc2VxICggW2V4YW1wbGVdKGh0dHBzOi8vd3d3LmdkYy1kb2NzLmV0aHouY2gvTURBL2hhbmRvdXRzL01EQTIwX1BoeWxvc2VxRm9ybWF0aW9uX01haGVuZHJhX01hcmlhZGFzc291LnBkZikgKS4gQnV0IHBoeWxvc2VxIGRvZXNuJ3QgYWxsb3cgZm9yIGNhbGxpZXogY29ycmVjdGlvbiBvZiBQQ29BLCBzbyBpbnN0ZWFkIHVzZSBleGFtcGxlIGZyb20gW0cuIFNpbXBzb25dKGh0dHBzOi8vZnJvbXRoZWJvdHRvbW9mdGhlaGVhcC5uZXQvc2xpZGVzL2ludHJvLXZlZ2FuLXdlYmluYXItMjAyMC9pbnRyby10by12ZWdhbi5odG1sIzEpIHRvIGZpdCBlbnZmaXQgb24gdG9wIG9mIG91dHB1dCBmcm9tIHdjbWRzY2FsZSAoUENvQSBpbiB2ZWdhbikuCgpQcmVwYXJlIHRoZSBvcmRpbmF0aW9uIHZhcmlhYmxlcwpgYGB7cn0KcGNvYV9icmF5IDwtIHdjbWRzY2FsZShicmF5X2RtYXQsIGVpZyA9IFRSVUUsIGFkZCA9ICJjYWlsbGlleiIpCgojIHRyaW0gbWV0YWRhdGEgdG8gcmVtb3ZlIHNhbXBsZXMgdGhhdCB3ZXJlIHJlbW92ZWQgZHVyaW5nIFFDCm1ldGFkYXRhX29yZGluYXRpb25zIDwtIG1ldGFkYXRhW21ldGFkYXRhJFNhbXBsZUlEICVpbiUgc2FtcGxlX2RhdGEocHNfaGVsbGluZ2VyKSRTYW1wbGVJRCxdCgojIGFuZCByZW1vdmUgcmVwZXRpdGl2ZSBtZXRhZGF0YSB2YXJpYWJsZXMgbGlrZSBEYXRlLyBNb250aC8gWWVhci8gVHJhd2wgIwptZXRhZGF0YV9vcmRpbmF0aW9ucyA8LSBzZWxlY3QobWV0YWRhdGFfb3JkaW5hdGlvbnMsIC0iWWVhci5UcmF3bCMiLCAtRGF0ZSwgLU1vbnRoLCAtWWVhcikKCiMgc29ydCBtZXRhZGF0YSBpbiBzYW1lIG9yZGVyIGFzIHRoZSBkaXN0YW5jZSBtYXRyaXgsIGJyYXlfZG1hdAptZXRhZGF0YV9vcmRpbmF0aW9ucyA8LSBtZXRhZGF0YV9vcmRpbmF0aW9ucyAlPiUgYXJyYW5nZShmYWN0b3IoU2FtcGxlSUQsIGxldmVscyA9IHJvd25hbWVzKG90dV90YWJsZShwc19oZWxsaW5nZXIpKSkpCgojIGNoYW5nZSB0aGUgY29sdW1uIG5hbWUgIkRhdGVjb2RlIiB0byAiRGF0ZSIgKGJldHRlciBmb3IgcGxvdHRpbmcpCmNvbG5hbWVzKG1ldGFkYXRhX29yZGluYXRpb25zKVsyXSA8LSAiRGF0ZSIKCiMgZml0IGVudmlyb25tZW50YWwgZmFjdG9ycyBhbmQgc2F2ZSBzdGF0cyBvdXRwdXQKcGNvYV9icmF5X2VudmZpdCA8LSBlbnZmaXQocGNvYV9icmF5LCBtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGVybXV0YXRpb25zID0gMTAwMCkKY2FwdHVyZS5vdXRwdXQocGNvYV9icmF5X2VudmZpdCwgZmlsZSA9ICJzdGF0c19yZXN1bHRzL3Bjb2FfYnJheV9lbnZmaXRfZUROQS50eHQiKQoKIyBTaWduZmljYW50IHZhcmlhYmxlcyBpbmNsdWRlIERhdGVjb2RlIChwID0gMC4wMjM5NzYpLCBETyAocCA9IDAuMDAwOTk5KSwgQmF5c2lkZSAocCA9IDAuMDA0OTk1KSwgYW5kIFN0YXRpb24gKHAgPSAwLjA0MTk1OCkKCiMgTWFrZSBlYWNoIG9mIHRoZSBpbnRlcmVzdGluZyB2YXJpYWJsZXMgdGhlaXIgb3duIG9yZGluYXRpb24gdmFyaWFibGVzIGZvciBwbG90dGluZyAoZXhjbHVkZSBTdGF0aW9uLiBUaGlzIHdpbGwgYmUgYSBjb2xvciB2YXJpYWJsZSBhbnl3YXkgYW5kIGl0J3Mgbm90IGludGVyZXN0aW5nKQpwY29hX2JyYXlfZW52Zml0X2RhdGUgPC0gZW52Zml0KHBjb2FfYnJheX5EYXRlLCBtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGVybXV0YXRpb25zID0gMTAwMCkKcGNvYV9icmF5X2VudmZpdF9ETyA8LSBlbnZmaXQocGNvYV9icmF5fkRPLCBtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGVybXV0YXRpb25zID0gMTAwMCkKcGNvYV9icmF5X2VudmZpdF9CYXlzaWRlIDwtIGVudmZpdChwY29hX2JyYXl+QmF5c2lkZSwgbWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBlcm11dGF0aW9ucyA9IDEwMDApCgpgYGAKCgpQbG90IGluIDJECmBgYHtyfQojIENvbnZlcnQgY2hhcmFjdGVycyBpbiBtZXRhZGF0YSB0byBmYWN0b3JzCm1ldGFkYXRhX29yZGluYXRpb25zIDwtIG1ldGFkYXRhX29yZGluYXRpb25zICU+JSBtdXRhdGVfaWYoc2FwcGx5KG1ldGFkYXRhX29yZGluYXRpb25zLCBpcy5jaGFyYWN0ZXIpLCBhcy5mYWN0b3IpCndpdGgoYXMuZGF0YS5mcmFtZShtZXRhZGF0YV9vcmRpbmF0aW9ucyksIGxldmVscyhTdGF0aW9uKSkKCiMgRGVmaW5lIHBsb3QgcGFyYW1ldGVycwpjb2x2ZWMgPC0gYyhicmV3ZXIucGFsKDExLCdQYWlyZWQnKSkgIyBjb2xvcnMgb2Ygc3RhdGlvbnMKc2hhcGV2ZWMgPC0gYygxOSwxOCkgIyBzaGFwZXMgaW5kaWNhdGluZyBCYXlzaWRlCgojIFNldCB1cCBiYXNpYyBwbG90CnBhcih4cGQgPSBULCBtYXIgPSBwYXIoKSRtYXIgKyBjKDAsMCwwLDgpKSAjIGxlYXZlIHNwYWNlIHRvIGFkZCBsZWdlbmQuIHhwZCA9IFQgYWxsb3dzIGxlZ2VuZCB0byBiZSBvdXRzaWRlIG9mIHRoZSBwbG90CiMgQWRkIHRoZSBzaXRlIHNjb3Jlcwp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBwbG90KHNjb3JlcyhwY29hX2JyYXksIGRpc3BsYXkgPSAic2l0ZXMiKSwgY29sID0gY29sdmVjW1N0YXRpb25dLCBwY2ggPSBzaGFwZXZlY1tCYXlzaWRlXSwgY2V4ID0gMiwgeGxhYiA9ICJDbzEgMjEuMSUiLCB5bGFiID0gIkNvMiAxMS4wJSIpKQojIEFkZCB0aGUgZGF0ZSB2ZWN0b3IKcGxvdChwY29hX2JyYXlfZW52Zml0X2RhdGUsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siKQojIEFkZCB0aGUgRE8gdmVjdG9yCnBsb3QocGNvYV9icmF5X2VudmZpdF9ETywgcC5tYXggPSAwLjEsIGx3ZCA9IDIsIGNvbCA9ICJibGFjayIpCiMgQWRkIHRoZSBodWxscyBpbmRpY2F0aW5nIEJheXNpZGUKd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgb3JkaWh1bGwocGNvYV9icmF5LCBCYXlzaWRlLCBsd2QgPSAyLCBsdHkgPSBjKDMsNSksIGxhYmVsID0gRkFMU0UpKQojIEFkZCBsZWdlbmRzCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIGxlZ2VuZCgwLjc3LCAwLjYsIGxlZ2VuZCA9IGxldmVscyhTdGF0aW9uKSwgY29sID0gY29sdmVjLCBwY2ggPSBjKDE5LDE4LDE5LDE5LDE5LDE4LDE5LDE5LDE5LDE4LDE4KSwgYnR5ID0gIm4iLCBwdC5jZXggPSAyLCBjZXggPSAuOCkpCmxlZ2VuZCgwLjc3LCAwLjgsIGMoIkVBU1QiLCAiV0VTVCIpLCBjb2wgPSBjKCJibGFjayIpLCBsdHkgPSBjKDMsNSksIGx3ZCA9IDIsIGJ0eSA9ICJuIiwgY2V4ID0gLjgpICMgTGVnZW5kIGZvciBCYXlzaWRlIGh1bGwgbGluZXMtIGRpZCB0aGlzIG1hbnVhbGx5CgoKIyBFeHBvcnQgdXNpbmcgYmFzZSBSLyB2ZWdhbiBoZWxwZXJzCnNldEVQUygpCnBvc3RzY3JpcHQoIkZpZ3VyZXMvcGNvYV9icmF5X2VudmZpdF9lRE5BLmVwcyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gNSkKcGFyKHhwZCA9IFQsIG1hciA9IHBhcigpJG1hciArIGMoMCwwLDAsOCkpCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBsb3Qoc2NvcmVzKHBjb2FfYnJheSwgZGlzcGxheSA9ICJzaXRlcyIpLCBjb2wgPSBjb2x2ZWNbU3RhdGlvbl0sIHBjaCA9IHNoYXBldmVjW0JheXNpZGVdLCBjZXggPSAyLCB4bGFiID0gIkNvMSAyMS4xJSIsIHlsYWIgPSAiQ28yIDExLjAlIikpCnBsb3QocGNvYV9icmF5X2VudmZpdF9kYXRlLCBwLm1heCA9IDAuMSwgbHdkID0gMiwgY29sID0gImJsYWNrIikKcGxvdChwY29hX2JyYXlfZW52Zml0X0RPLCBwLm1heCA9IDAuMSwgbHdkID0gMiwgY29sID0gImJsYWNrIikKd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgb3JkaWh1bGwocGNvYV9icmF5LCBCYXlzaWRlLCBsd2QgPSAyLCBsdHkgPSBjKDMsNSksIGxhYmVsID0gRkFMU0UpKQp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBsZWdlbmQoMC43NywgMC42LCBsZWdlbmQgPSBsZXZlbHMoU3RhdGlvbiksIGNvbCA9IGNvbHZlYywgcGNoID0gYygxOSwxOCwxOSwxOSwxOSwxOCwxOSwxOSwxOSwxOCwxOCksIGJ0eSA9ICJuIiwgcHQuY2V4ID0gMiwgY2V4ID0gLjgpKQpsZWdlbmQoMC43NywgMC44LCBjKCJFQVNUIiwgIldFU1QiKSwgY29sID0gYygiYmxhY2siKSwgbHR5ID0gYygzLDUpLCBsd2QgPSAyLCBidHkgPSAibiIsIGNleCA9IC44KSAKZGV2Lm9mZigpCgpgYGAKCgoKIyMgT3JkaW5hdGlvbnMgb24gVHJhd2wgRGF0YQoKIyMjIFByZXBhcmUgdGhlIHRyYXdsIGRhdGEKCkRvZXMgQ1BVRSBkYXRhIG5lZWQgdG8gYmUgdHJhbnNmb3JtZWQgYmVmb3JlIG9yZGluYXRpb25zPwoKLSBbU2FiZWwgZXQgYWwuIDIwMjBdKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL2Z1bGwvMTAuMTExMS9md2IuMTM1MDEpIGRvIGEgbG9nLXRyYW5zZm9ybWF0aW9uLCB0aGVuIGNlbnRlciBhbmQgc3RhbmRhcmRpemUgY291bnRzIGJlZm9yZSBQQ0EKLSBbR290aHVlcyBhbmQgQWJsZSAyMDIwXShodHRwczovL29ubGluZWxpYnJhcnkud2lsZXkuY29tL2RvaS9mdWxsLzEwLjExMTEvcmVjLjEzMTYzP2Nhc2FfdG9rZW49VWVVQVVwOTl5ZWNBQUFBQSUzQVh1Skd6NmFpbFpEcTVCVzI3dmxyUnVkdWk5NkRjdi1qR2pBT1YwVVNTQVlwaUZicjNvTTdZMUdRZ2JsSmpPNG4xVmhxaGRoZ2VDOXZ0OEUpIGFsc28gbG9nIHRyYW5zZm9ybSAoIGxvZyh5KzEpICkgYW5kIGNlbnRlciBhbmQgc3RhbmRhcmRpemUgdG8gdW5pdHMgb2Ygc3RhbmRhcmQgZGV2aWF0aW9uIGJlZm9yZSBQQ0EuIFRoZXkgYWxzbyBleGNsdWRlZCByYXJlIHNwZWNpZXMgKGxlc3MgdGhhbiAzIG9jY3VyZW5jZXMgYXQgYWxsIHNpdGVzKQotIFtXZWdzaGVpZGVyIGV0IGFsLiAyMDIwXShodHRwczovL29ubGluZWxpYnJhcnkud2lsZXkuY29tL2RvaS9mdWxsLzEwLjEwMDIvcnJhLjM2MTU/Y2FzYV90b2tlbj0tRVZfSEVwUTNzZ0FBQUFBJTNBTHFlS0JzTmpGeXo2dDRLVnJnRnJlbEtUT2pfbUFqYVZDTHgtZm8wN3lWZHBfbl9SMHBLTXI0dTJHN0VucWd1LURkMUV0bDlsa2M1STJLVSkgZG8gbm8gdHJhbnNmb3JtYXRpb24sIGFzIGZhciBhcyBJIGNhbiB0ZWxsLCBiZWZvcmUgUENvQQotIFtNZWhkaSBldCBhbC4gMjAyMV0oaHR0cHM6Ly93d3cuc2NpZW5jZWRpcmVjdC5jb20vc2NpZW5jZS9hcnRpY2xlL3BpaS9TMDA0ODk2OTcyMDM2OTYxOD9jYXNhX3Rva2VuPVJpT2hNekxPS2tvQUFBQUE6dXEtMFM5bGxRTHNBSEEyMkhsSVhMXzZKUlBnZVB3enZHemtGMnhHelh3Ry1CTTVwY3lDOUN1WTlWd2E3SVpHOVR3Yi1YUVdUWXcjZjAwMTUpIHVzZSBhIFBDb0Egd2l0aG91dCB0cmFuc2Zvcm1hdGlvbiBidXQgd2l0aCBhIEJyYXktQ3VydGlzIGRpc3NpbWlsYXJpdHkgbWF0cml4CgpDb25jbHVzaW9uPyAgCgotIENlbnRlciBhbmQgc3RhbmRhcmRpemUgdGhlIGxvZyB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgY291bnRzIGZvciBQQ0EKLSBVc2UgbG9nLXRyYW5zZm9ybWF0aW9uIG9mIGNvdW50cyBhcyBpbnB1dCB0byBCcmF5IEN1cnRpcyBkaXNzaW1pbGFyaXR5IGNhbGN1bGF0aW9uIGJlZm9yZSBQQ29BCgoKTWFrZSBhIHRhYmxlIG9mIENQVUUsIFRMUFVFLCBhbmQgVEx4U0YuUFVFIGluIHRoZSBzdHlsZSBvZiBPVFUgdGFibGVzIChzYW1wbGVzIGluIHJvd3MvIHNwZWNpZXMgaW4gY29sdW1tcyksIG9mIHRoZSBDUFVFIGRhdGEuIFRyYXdsX2NvdW50cyBoYXMgYWxyZWFkeSBiZWVuIGZpbHRlcmVkIHNvIHRoYXQgaXQgb25seSBpbmNsdWRlcyB0aG9zZSBzYW1wbGVzIGFuZCBzcGVjaWVzIHRoYXQgYXJlIHJlbGV2YW50IHRvIHRoZSBlRE5BIHN0dWR5CmBgYHtyfQpDUFVFX3RhYmxlIDwtIHRyYXdsX2NvdW50cyAlPiUKICBhc19kYXRhX2ZyYW1lKCklPiUKICBzZWxlY3QoU2FtcGxlSUQsIENvbW1vbk5hbWUsIENQVUUpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBDb21tb25OYW1lLCB2YWx1ZXNfZnJvbSA9IENQVUUpIApDUFVFX3RhYmxlCgpUTFBVRV90YWJsZSA8LSB0cmF3bF9jb3VudHMgJT4lCiAgYXNfZGF0YV9mcmFtZSgpJT4lCiAgc2VsZWN0KFNhbXBsZUlELCBDb21tb25OYW1lLCBUTFBVRSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENvbW1vbk5hbWUsIHZhbHVlc19mcm9tID0gVExQVUUpIApUTFBVRV90YWJsZQoKVExTRi5QVUVfdGFibGUgPC0gdHJhd2xfY291bnRzICU+JQogIGFzX2RhdGFfZnJhbWUoKSU+JQogIHNlbGVjdChTYW1wbGVJRCwgQ29tbW9uTmFtZSwgVEx4U0YuUFVFKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29tbW9uTmFtZSwgdmFsdWVzX2Zyb20gPSBUTHhTRi5QVUUpIApUTFNGLlBVRV90YWJsZQpgYGAKCgpUcmFuc2Zvcm1hdGlvbiBieSBsb2cgdHJhbnNmb3JtYXRpb24gKGxvZyh4KzEpIGluIG9yZGVyIHRvIGFjY291bnQgZm9yIHplcm9lcykKYGBge3J9CiMgc2V0IE5BcyB0byB6ZXJvZXMKQ1BVRV90YWJsZVtpcy5uYShDUFVFX3RhYmxlKV0gPC0gMApUTFBVRV90YWJsZVtpcy5uYShUTFBVRV90YWJsZSldIDwtIDAKVExTRi5QVUVfdGFibGVbaXMubmEoVExTRi5QVUVfdGFibGUpXSA8LSAwCgojIGxvZyB0cmFuc2Zvcm0KQ1BVRV90YWJsZV90cmFuc2Zvcm0gPC0gQ1BVRV90YWJsZQpDUFVFX3RhYmxlX3RyYW5zZm9ybVssMjpsZW5ndGgoQ1BVRV90YWJsZSldIDwtIGxvZzEwKENQVUVfdGFibGVbLDI6bGVuZ3RoKENQVUVfdGFibGUpXSsxKQoKVExQVUVfdGFibGVfdHJhbnNmb3JtIDwtIFRMUFVFX3RhYmxlClRMUFVFX3RhYmxlX3RyYW5zZm9ybVssMjpsZW5ndGgoVExQVUVfdGFibGUpXSA8LSBsb2cxMChUTFBVRV90YWJsZVssMjpsZW5ndGgoVExQVUVfdGFibGUpXSsxKQoKVExTRi5QVUVfdGFibGVfdHJhbnNmb3JtIDwtIFRMU0YuUFVFX3RhYmxlClRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybVssMjpsZW5ndGgoVExTRi5QVUVfdGFibGUpXSA8LSBsb2cxMChUTFNGLlBVRV90YWJsZVssMjpsZW5ndGgoVExTRi5QVUVfdGFibGUpXSsxKQoKQ1BVRV90YWJsZV90cmFuc2Zvcm0KVExQVUVfdGFibGVfdHJhbnNmb3JtClRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybQpgYGAKCgpDZW50ZXIgKGFyb3VuZCBtZWFuKSBhbmQgc3RhbmRhcmRpemUgKGJ5IFNEKSB0aGUgbG9nLXRyYW5zZm9ybWVkIENQVUUgZGF0YSwgc2ltaWxhciB0byB0aGUgcmVmZXJlbmNlcyBhYm92ZS4gCgpgYGB7cn0KQ1BVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0IDwtIENQVUVfdGFibGVfdHJhbnNmb3JtCkNQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdFssMjpsZW5ndGgoQ1BVRV90YWJsZV90cmFuc2Zvcm0pXSA8LSBzY2FsZShDUFVFX3RhYmxlX3RyYW5zZm9ybVssMjpsZW5ndGgoQ1BVRV90YWJsZV90cmFuc2Zvcm0pXSkKClRMUFVFX3RhYmxlX3RyYW5zZm9ybV9jZW5fc3QgPC0gVExQVUVfdGFibGVfdHJhbnNmb3JtClRMUFVFX3RhYmxlX3RyYW5zZm9ybV9jZW5fc3RbLDI6bGVuZ3RoKFRMUFVFX3RhYmxlX3RyYW5zZm9ybSldIDwtIHNjYWxlKFRMUFVFX3RhYmxlX3RyYW5zZm9ybVssMjpsZW5ndGgoVExQVUVfdGFibGVfdHJhbnNmb3JtKV0pCgpUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0IDwtIFRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybQpUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0WywyOmxlbmd0aChUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm0pXSA8LSBzY2FsZShUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm1bLDI6bGVuZ3RoKFRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybSldKQoKCkNQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdApUTFBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0ClRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybV9jZW5fc3QKYGBgCgoKIyMjIENQVUUgUENBCkdlbmVyYXRlIHRoZSBQQ0EgYW5kIHZpc3VhbGl6ZSBheGVzCmBgYHtyfQojIGNvbnZlcnQgdG8gZGF0YWZyYW1lCkNQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdCA8LSBkYXRhLmZyYW1lKENQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdCkKcm93bmFtZXMoQ1BVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0KSA8LSBjKENQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdCRTYW1wbGVJRCkKQ1BVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0IDwtIENQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdFssLTFdCgojIHJ1biB0aGUgUENBCmxvZ190cmFuc2Zvcm1fcGNhIDwtIHByY29tcChDUFVFX3RhYmxlX3RyYW5zZm9ybV9jZW5fc3QpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKIyBWaXN1YWwgcmVwcmVzZW50YXRpb24gd2l0aCBhIHNjcmVlcGxvdApsb2dfdHJhbnNmb3JtX3ZhcmlhbmNlcyA8LSBhcy5kYXRhLmZyYW1lKGxvZ190cmFuc2Zvcm1fcGNhJHNkZXZeMi9zdW0obG9nX3RyYW5zZm9ybV9wY2Ekc2Rldl4yKSkgJT4lICNFeHRyYWN0IGF4ZXMKICAjIEZvcm1hdCB0byBwbG90CiAgc2VsZWN0KFBlcmNWYXIgPSAnbG9nX3RyYW5zZm9ybV9wY2Ekc2Rldl4yL3N1bShsb2dfdHJhbnNmb3JtX3BjYSRzZGV2XjIpJykgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiUENheGlzIikgJT4lIAogIGRhdGEuZnJhbWUKaGVhZChsb2dfdHJhbnNmb3JtX3ZhcmlhbmNlcykKCiMgUGxvdCBzY3JlZXBsb3QKZ2dwbG90KGxvZ190cmFuc2Zvcm1fdmFyaWFuY2VzLCBhZXMoeCA9IGFzLm51bWVyaWMoUENheGlzKSwgeSA9IFBlcmNWYXIpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImdyZXkiLCBjb2xvciA9ICJibGFjayIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHggPSAiUEMgYXhpcyIsIHkgPSAiJSBWYXJpYW5jZSIsIHRpdGxlID0gIkxvZy1UcmFuc2Zvcm1lZCBQQ0EgU2NyZWVwbG90LCBDUFVFIikKYGBgCgpUb3RhbCB2YXJpYW5jZSBleHBsYWluZWQgYnkgZmlyc3QgMiBheGVzID0gMTcuNSArIDExLjMgPSAqKjI4LjglKiouIFRvdGFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBmaXJzdCB0aHJlZSBheGVzPSAxNy41ICsgMTEuMyArIDEwLjYgPSAqKjM5LjQlKiouIAoKVmlzdWFsaXplIHRoZSBQQ0EgaW4gM0Q6IAoKYGBge3J9CiMgRXh0cmFjdCB2YXJpYW5jZXMgZnJvbSB0aGUgcGNhCnBjYV9sb2d0cmFuc2Zvcm1fZnJhbWUgPC0gZGF0YS5mcmFtZShsb2dfdHJhbnNmb3JtX3BjYSR4KSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJTYW1wbGVJRCIpCgojIE1lcmdlIG1ldGFkYXRhIGludG8gdGhlIHBjb2EgZGF0YSB0YWJsZQpwY2FfbG9ndHJhbnNmb3JtX2ZyYW1lIDwtIGxlZnRfam9pbihwY2FfbG9ndHJhbnNmb3JtX2ZyYW1lLCBtZXRhZGF0YSwgYnkgPSAiU2FtcGxlSUQiKQpoZWFkKHBjYV9sb2d0cmFuc2Zvcm1fZnJhbWUpCgojIFNlbGVjdCBlaWdlbnZhbHVlcyBmcm9tIGRhdGFmcmFtZSwgcm91bmQgdG8gNCBwbGFjZXMgYW5kIG11bHRpcGx5IGJ5IDEwMCBmb3IgcGxvdHRpbmcuIFRoZXNlIHdpbGwgYmUgdGhlIGF4ZXMgZm9yIHRoZSAzLUQgcGxvdAplaWdlbnZhbHVlczwtcm91bmQobG9nX3RyYW5zZm9ybV92YXJpYW5jZXNbLDJdLCBkaWdpdHMgPSA0KSoxMDAKCiMgUGxvdGx5IC0gMy1ECnBjYV9sb2dfdHJhbnNmb3JtIDwtIHBsb3RfbHkocGNhX2xvZ3RyYW5zZm9ybV9mcmFtZSwgdHlwZT0nc2NhdHRlcjNkJywgbW9kZT0nbWFya2VycycsCiAgICAgICAgeD1+UEMxLHk9flBDMix6PX5QQzMsY29sb3JzPX5icmV3ZXIucGFsKDExLCdQYWlyZWQnKSwKICAgICAgICBjb2xvcj1+U3RhdGlvbiwgc3ltYm9scyA9IGMoJ2NpcmNsZScsJ2RpYW1vbmQnKSwgc3ltYm9sPX5CYXlzaWRlKSU+JQogIGxheW91dChmb250PWxpc3Qoc2l6ZT0xMiksCiAgICAgICAgIHRpdGxlPSdQQ0Egb24gTG9nLXRyYW5zZm9ybWVkIENQVUUnLAogICAgICAgICBzY2VuZT1saXN0KHhheGlzPWxpc3QodGl0bGU9cGFzdGUwKCdDbyAyICcsZWlnZW52YWx1ZXNbMl0sJyUnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3d0aWNrbGFiZWxzPUZBTFNFLHplcm9saW5lY29sb3I9J2JsYWNrJyksCiAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT1wYXN0ZTAoJ0NvIDMgJyxlaWdlbnZhbHVlc1szXSwnJScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd3RpY2tsYWJlbHM9RkFMU0UsemVyb2xpbmVjb2xvcj0nYmxhY2snKSwKICAgICAgICAgICAgICAgICAgICB6YXhpcz1saXN0KHRpdGxlPXBhc3RlMCgnQ28gMSAnLGVpZ2VudmFsdWVzWzFdLCclJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93dGlja2xhYmVscz1GQUxTRSx6ZXJvbGluZWNvbG9yPSdibGFjaycpKSkKIyBwY2FfbG9nX3RyYW5zZm9ybQoKIyBzYXZlIGluICJFbWJlZGRlZF9maWd1cmVzIiBkaXJlY3Rvcnkgc28gdGhhdCBpdCBjYW4gYmUgaG9zdGVkIGF0IEdpdGh1YiBhbmQgZW1iZWRkZWQgaW4gdGhpcyBub3RlYm9vawp3aXRocjo6d2l0aF9kaXIoJ0VtYmVkZGVkX2ZpZ3VyZXMnLCBodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocGNhX2xvZ190cmFuc2Zvcm0pLCBmaWxlPSJwY2FfbG9nX3RyYW5zZm9ybV9DUFVFLmh0bWwiLCBzZWxmY29udGFpbmVkID0gRikpCgogCmBgYAoKPGlmcmFtZSBzcmM9IkVtYmVkZGVkX2ZpZ3VyZXMvcGNhX2xvZ190cmFuc2Zvcm1fQ1BVRS5odG1sIiBoZWlnaHQ9IjYwMHB4IiB3aWR0aD0iMTAwJSIgc3R5bGU9ImJvcmRlcjpub25lOyI+PC9pZnJhbWU+CgpBbHNvIHBsb3QgaW4gMkQgLQpgYGB7cn0KbG9nX3RyYW5zZm9ybV9mcmFtZV8yRCA8LSBnZ3Bsb3QocGNhX2xvZ3RyYW5zZm9ybV9mcmFtZSxhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBTdGF0aW9uLCBzaGFwZSA9IEJheXNpZGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJQYWlyZWQiKSArCiAgdGhlbWVfYncoKSArCiAgbGFicyh4ID0gcGFzdGUwKCdQQzEgJywgZWlnZW52YWx1ZXNbMV0sICclJyksIHkgPSBwYXN0ZTAoJ1BDMiAnLCBlaWdlbnZhbHVlc1syXSwgJyUnKSwgdGl0bGUgPSAiUENBIG9uIExvZy10cmFuc2Zvcm1lZCBDUFVFIikgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkKCmxvZ190cmFuc2Zvcm1fZnJhbWVfMkQKCmdnc2F2ZSgiZmlndXJlcy9wY2FfbG9nX3RyYW5zZm9ybV8yRC5lcHMiLGxvZ190cmFuc2Zvcm1fZnJhbWVfMkQsIHdpZHRoID0gNywgaGVpZ2h0ID0gNSwgdW5pdHMgPSBjKCJpbiIpKQpgYGAKCgpTdW1tYXJ5OiBUaGUgcGVyY2VudCB2YXJpYW5jZSBleHBsYWluZWQgYnkgUENBIGlzIE9LLiBUcnkgYSBQQ29BCgoKIyMjIFRMUFVFIFBDQQpHZW5lcmF0ZSB0aGUgUENBIGFuZCB2aXN1YWxpemUgYXhlcwpgYGB7cn0KIyBjb252ZXJ0IHRvIGRhdGFmcmFtZQpUTFBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0IDwtIGRhdGEuZnJhbWUoVExQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdCkKcm93bmFtZXMoVExQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdCkgPC0gYyhUTFBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0JFNhbXBsZUlEKQpUTFBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0IDwtIFRMUFVFX3RhYmxlX3RyYW5zZm9ybV9jZW5fc3RbLC0xXQoKIyBydW4gdGhlIFBDQQpsb2dfdHJhbnNmb3JtX3BjYSA8LSBwcmNvbXAoVExQVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIFZpc3VhbCByZXByZXNlbnRhdGlvbiB3aXRoIGEgc2NyZWVwbG90CmxvZ190cmFuc2Zvcm1fdmFyaWFuY2VzIDwtIGFzLmRhdGEuZnJhbWUobG9nX3RyYW5zZm9ybV9wY2Ekc2Rldl4yL3N1bShsb2dfdHJhbnNmb3JtX3BjYSRzZGV2XjIpKSAlPiUgI0V4dHJhY3QgYXhlcwogICMgRm9ybWF0IHRvIHBsb3QKICBzZWxlY3QoUGVyY1ZhciA9ICdsb2dfdHJhbnNmb3JtX3BjYSRzZGV2XjIvc3VtKGxvZ190cmFuc2Zvcm1fcGNhJHNkZXZeMiknKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJQQ2F4aXMiKSAlPiUgCiAgZGF0YS5mcmFtZQpoZWFkKGxvZ190cmFuc2Zvcm1fdmFyaWFuY2VzKQoKIyBQbG90IHNjcmVlcGxvdApnZ3Bsb3QobG9nX3RyYW5zZm9ybV92YXJpYW5jZXMsIGFlcyh4ID0gYXMubnVtZXJpYyhQQ2F4aXMpLCB5ID0gUGVyY1ZhcikpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiZ3JleSIsIGNvbG9yID0gImJsYWNrIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnMoeCA9ICJQQyBheGlzIiwgeSA9ICIlIFZhcmlhbmNlIiwgdGl0bGUgPSAiTG9nLVRyYW5zZm9ybWVkIFBDQSBTY3JlZXBsb3QsIFRMUFVFIikKYGBgCgpUb3RhbCB2YXJpYW5jZSBleHBsYWluZWQgYnkgZmlyc3QgMiBheGVzID0gMTcuNCArIDEwLjggPSAqKjI4LjglKiouIFRvdGFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBmaXJzdCB0aHJlZSBheGVzPSAxNy40ICsgMTAuOCArIDkuNiA9ICoqMzcuOCUqKi4gVmVyeSBzaW1pbGFyIHRvIHRoZSBQQ0Egb24gQ1BVRQoKCgpQbG90IGluIDJEIC0KYGBge3J9CiMgRXh0cmFjdCB2YXJpYW5jZXMgZnJvbSB0aGUgcGNhCnBjYV9sb2d0cmFuc2Zvcm1fZnJhbWUgPC0gZGF0YS5mcmFtZShsb2dfdHJhbnNmb3JtX3BjYSR4KSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJTYW1wbGVJRCIpCgojIE1lcmdlIG1ldGFkYXRhIGludG8gdGhlIHBjb2EgZGF0YSB0YWJsZQpwY2FfbG9ndHJhbnNmb3JtX2ZyYW1lIDwtIGxlZnRfam9pbihwY2FfbG9ndHJhbnNmb3JtX2ZyYW1lLCBtZXRhZGF0YSwgYnkgPSAiU2FtcGxlSUQiKQpoZWFkKHBjYV9sb2d0cmFuc2Zvcm1fZnJhbWUpCgojIFNlbGVjdCBlaWdlbnZhbHVlcyBmcm9tIGRhdGFmcmFtZSwgcm91bmQgdG8gNCBwbGFjZXMgYW5kIG11bHRpcGx5IGJ5IDEwMCBmb3IgcGxvdHRpbmcuIFRoZXNlIHdpbGwgYmUgdGhlIGF4ZXMgZm9yIHRoZSAzLUQgcGxvdAplaWdlbnZhbHVlczwtcm91bmQobG9nX3RyYW5zZm9ybV92YXJpYW5jZXNbLDJdLCBkaWdpdHMgPSA0KSoxMDAKCmxvZ190cmFuc2Zvcm1fZnJhbWVfMkQgPC0gZ2dwbG90KHBjYV9sb2d0cmFuc2Zvcm1fZnJhbWUsYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG9yID0gU3RhdGlvbiwgc2hhcGUgPSBCYXlzaWRlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9IHBhc3RlMCgnUEMxICcsIGVpZ2VudmFsdWVzWzFdLCAnJScpLCB5ID0gcGFzdGUwKCdQQzIgJywgZWlnZW52YWx1ZXNbMl0sICclJyksIHRpdGxlID0gIlBDQSBvbiBMb2ctdHJhbnNmb3JtZWQgVG90YWwgTGVuZ3RoIFBVRSIpICsKICBjb29yZF9maXhlZChyYXRpbyA9IDEpCgpsb2dfdHJhbnNmb3JtX2ZyYW1lXzJECmBgYAoKClN1bW1hcnk6IFRoaXMgaXMgVkVSWSBzaW1pbGFyIHRvIHRoZSBkaXN0cmlidXRpb24gb2YgcG9pbnRzIGZyb20gdGhlIFBDQSBvbiBDUFVFLiBDaGVjayB0aGUgYWxsb21ldHJpYyBkYXRhICh0b3RhbCBsZW5ndGgpIHRpbWVzIHRoZSBzaGVkZGluZyBmYWN0b3IgKFNGKSBqdXN0IHRvIGJlIGNvbXByZWhlbnNpdmUuLi4KCgoKCiMjIyBUTHhTRiBQVUUgUENBCkdlbmVyYXRlIHRoZSBQQ0EgYW5kIHZpc3VhbGl6ZSBheGVzCmBgYHtyfQojIGNvbnZlcnQgdG8gZGF0YWZyYW1lClRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybV9jZW5fc3QgPC0gZGF0YS5mcmFtZShUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0KQpyb3duYW1lcyhUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0KSA8LSBjKFRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybV9jZW5fc3QkU2FtcGxlSUQpClRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybV9jZW5fc3QgPC0gVExTRi5QVUVfdGFibGVfdHJhbnNmb3JtX2Nlbl9zdFssLTFdCgojIHJ1biB0aGUgUENBCmxvZ190cmFuc2Zvcm1fcGNhIDwtIHByY29tcChUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0KQogICAgICAgICAgICAgICAgICAgICAgICAgICAgCiMgVmlzdWFsIHJlcHJlc2VudGF0aW9uIHdpdGggYSBzY3JlZXBsb3QKbG9nX3RyYW5zZm9ybV92YXJpYW5jZXMgPC0gYXMuZGF0YS5mcmFtZShsb2dfdHJhbnNmb3JtX3BjYSRzZGV2XjIvc3VtKGxvZ190cmFuc2Zvcm1fcGNhJHNkZXZeMikpICU+JSAjRXh0cmFjdCBheGVzCiAgIyBGb3JtYXQgdG8gcGxvdAogIHNlbGVjdChQZXJjVmFyID0gJ2xvZ190cmFuc2Zvcm1fcGNhJHNkZXZeMi9zdW0obG9nX3RyYW5zZm9ybV9wY2Ekc2Rldl4yKScpICU+JSAKICByb3duYW1lc190b19jb2x1bW4odmFyID0gIlBDYXhpcyIpICU+JSAKICBkYXRhLmZyYW1lCmhlYWQobG9nX3RyYW5zZm9ybV92YXJpYW5jZXMpCgojIFBsb3Qgc2NyZWVwbG90CmdncGxvdChsb2dfdHJhbnNmb3JtX3ZhcmlhbmNlcywgYWVzKHggPSBhcy5udW1lcmljKFBDYXhpcyksIHkgPSBQZXJjVmFyKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJncmV5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh4ID0gIlBDIGF4aXMiLCB5ID0gIiUgVmFyaWFuY2UiLCB0aXRsZSA9ICJMb2ctVHJhbnNmb3JtZWQgUENBIFNjcmVlcGxvdCwgVExQVUUiKQpgYGAKClRvdGFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBmaXJzdCAyIGF4ZXMgPSAxNy41ICsgMTAuNyA9ICoqMjguMiUqKi4gVG90YWwgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGZpcnN0IHRocmVlIGF4ZXM9IDE3LjQgKyAxMC44ICsgOS43ID0gKiozNy45JSoqLiBWZXJ5IHNpbWlsYXIgdG8gdGhlIFBDQSBvbiBDUFVFIGFuZCBvbiBUTFBVRQoKCgpQbG90IGluIDJEIC0KYGBge3J9CiMgRXh0cmFjdCB2YXJpYW5jZXMgZnJvbSB0aGUgcGNhCnBjYV9sb2d0cmFuc2Zvcm1fZnJhbWUgPC0gZGF0YS5mcmFtZShsb2dfdHJhbnNmb3JtX3BjYSR4KSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJTYW1wbGVJRCIpCgojIE1lcmdlIG1ldGFkYXRhIGludG8gdGhlIHBjb2EgZGF0YSB0YWJsZQpwY2FfbG9ndHJhbnNmb3JtX2ZyYW1lIDwtIGxlZnRfam9pbihwY2FfbG9ndHJhbnNmb3JtX2ZyYW1lLCBtZXRhZGF0YSwgYnkgPSAiU2FtcGxlSUQiKQpoZWFkKHBjYV9sb2d0cmFuc2Zvcm1fZnJhbWUpCgojIFNlbGVjdCBlaWdlbnZhbHVlcyBmcm9tIGRhdGFmcmFtZSwgcm91bmQgdG8gNCBwbGFjZXMgYW5kIG11bHRpcGx5IGJ5IDEwMCBmb3IgcGxvdHRpbmcuIFRoZXNlIHdpbGwgYmUgdGhlIGF4ZXMgZm9yIHRoZSAzLUQgcGxvdAplaWdlbnZhbHVlczwtcm91bmQobG9nX3RyYW5zZm9ybV92YXJpYW5jZXNbLDJdLCBkaWdpdHMgPSA0KSoxMDAKCmxvZ190cmFuc2Zvcm1fZnJhbWVfMkQgPC0gZ2dwbG90KHBjYV9sb2d0cmFuc2Zvcm1fZnJhbWUsYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG9yID0gU3RhdGlvbiwgc2hhcGUgPSBCYXlzaWRlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9IHBhc3RlMCgnUEMxICcsIGVpZ2VudmFsdWVzWzFdLCAnJScpLCB5ID0gcGFzdGUwKCdQQzIgJywgZWlnZW52YWx1ZXNbMl0sICclJyksIHRpdGxlID0gIlBDQSBvbiBMb2ctdHJhbnNmb3JtZWQgVG90YWwgTGVuZ3RoIHggU2hlZGRpbmcgRmFjdG9yIFBVRSIpICsKICBjb29yZF9maXhlZChyYXRpbyA9IDEpCgpsb2dfdHJhbnNmb3JtX2ZyYW1lXzJECmBgYAoKQWdhaW4sIHRoaXMgaXMgdmVyeSBzaW1pbGFyIHRvIHRoZSBQQ0Egb24gQ1BVRSBhbmQgb24gVExQVS4gU3RpY2sgd2l0aCBDUFVFIGZvciBzaW1wbGljaXR5LgoKCgojIyMgQ1BVRSBQQ29BIEJyYXkgQ3VydGlzCgpOZXh0LCB0cnkgYSBQQ29BLiBVc2UgdGhlIGxvZy10cmFuc2Zvcm1lZCBhYnVuZGFuY2UgbWF0cml4IHRvIGNhbGN1bGF0ZSBhIGRpc3RhbmNlLW1hdHJpeCB1c2luZyB0aGUgKipCcmF5LUN1cnRpcyoqIHNpbWlsYXJpdHkgbWV0cmljLiBUaGVuIHVzZSB0aGlzIGFzIGlucHV0IGZvciBQQ29BCgpOT1RFOiBOZWVkIHRvIHVzZSBhIGNvcnJlY3Rpb24gaGVyZSBmb3IgbmVnYXRpdmUgZWlnZW52YWx1ZXMKYGBge3J9CiMgY29udmVydCB0byBkYXRhZnJhbWUKQ1BVRV90YWJsZV90cmFuc2Zvcm0gPC0gZGF0YS5mcmFtZShDUFVFX3RhYmxlX3RyYW5zZm9ybSkKcm93bmFtZXMoQ1BVRV90YWJsZV90cmFuc2Zvcm0pIDwtIGMoQ1BVRV90YWJsZSRTYW1wbGVJRCkKQ1BVRV90YWJsZV90cmFuc2Zvcm0gPC0gQ1BVRV90YWJsZV90cmFuc2Zvcm1bLC0xXQoKIyBHZXQgQnJheSBDdXJ0aXMgZGlzdGFuY2UgbWF0cml4IGZyb20gbG9nLXRyYW5zZm9ybWVkIENQVUUgZGF0YQpicmF5X2RtYXQ8LXZlZ2Rpc3QoQ1BVRV90YWJsZV90cmFuc2Zvcm0sbWV0aG9kPSJicmF5IikgCgojIHRoZSBub3JtYWwgUENvQSByZXN1bHRzIGluIG5lZ2F0aXZlIGVpZ2VudmFsdWVzLCBzbyBuZWVkIGNvcnJlY3Rpb24uIHVzZSB3Y21kc2NhbGUgYW5kIGFkZCBjYWlsbGlleiBjb3JyZWN0aW9uCnBjb2FfYnJheSA8LSB3Y21kc2NhbGUoYnJheV9kbWF0LCBlaWcgPSBUUlVFLCBhZGQgPSAiY2FpbGxpZXoiKQoKIyBjaGVjayBvdXQgc3VtbWFyeSBvZiBQQ29BCmVpZ2VudmFscyhwY29hX2JyYXkpICU+JQogIHN1bW1hcnkoKSAtPiBldgoKIyBleHRyYWN0IHZhcmlhbmNlcyBhbmQgcHV0IGluIHRpYmJsZQpicmF5X3ZhcmlhbmNlcyA8LSBOVUxMCmZvciAoaSBpbiAxOmxlbmd0aChlaWdlbnZhbHMocGNvYV9icmF5KSkpewogIGJyYXlfdmFyaWFuY2VzW2ldIDwtIGVpZ2VudmFscyhwY29hX2JyYXkpW2ldL3N1bShlaWdlbnZhbHMocGNvYV9icmF5KSkKfQoKIyBFeHRyYWN0IHZhcmlhbmNlcyBmcm9tIHBjb2EsIGZyb20gY2FsY3VsYXRlZCBkaXN0LiBtZXRyaWMKYnJheV92YXJpYW5jZXMgPC0gdGliYmxlKHJvdW5kKGJyYXlfdmFyaWFuY2VzLDMpKSAlPiUKICBzZWxlY3QoUGVyY1ZhciA9ICdyb3VuZChicmF5X3ZhcmlhbmNlcywgMyknKSAlPiUKICByb3duYW1lc190b19jb2x1bW4odmFyID0gIlBDYXhpcyIpICU+JQogIGRhdGEuZnJhbWUKaGVhZChicmF5X3ZhcmlhbmNlcykKCiMgTWFrZSBhIHNjcmVlcGxvdApnZ3Bsb3QoYnJheV92YXJpYW5jZXMsIGFlcyh4ID0gYXMubnVtZXJpYyhQQ2F4aXMpLCB5ID0gUGVyY1ZhcikpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiZ3JleSIsIGNvbG9yID0gImJsYWNrIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnMoeCA9ICJQQyBheGlzIiwgeSA9ICIlIFZhcmlhbmNlIiwgdGl0bGUgPSAiQnJheS1DdXJ0aXMgUENvQSBTY3JlZXBsb3QiKQpgYGAKVGhlIGZpcnN0IHR3byBheGVzICgxMi4wKzEwLjMrOC45KSBhcmUgbm90IGFzIGdvb2QgYXMgUENBLiBUb3RhbCB2YXJpYW5jZSBleHBsYWluZWQgYnkgZmlyc3QgMyBQQ3MgPSAqKjMxLjIlKiopCgoKCgpQbG90IGluIDNEIHdpdGggUGxvdGx5CmBgYHtyfQojIEV4dHJhY3QgdmFyaWFuY2VzIGZyb20gdGhlIHBjb2EKcGNvYV9icmF5X2RmIDwtIGRhdGEuZnJhbWUocGNvYV9icmF5JHBvaW50cykgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiU2FtcGxlSUQiKQoKIyBNZXJnZSBtZXRhZGF0YSBpbnRvIHRoZSBwY29hIGRhdGEgdGFibGUKcGNvYV9icmF5X2RmIDwtIGxlZnRfam9pbihwY29hX2JyYXlfZGYsIG1ldGFkYXRhLCBieSA9ICJTYW1wbGVJRCIpCmhlYWQocGNvYV9icmF5X2RmKQoKIyBTZWxlY3QgZWlnZW52YWx1ZXMgZnJvbSBkYXRhZnJhbWUsIHJvdW5kIHRvIDQgcGxhY2VzIGFuZCBtdWx0aXBseSBieSAxMDAgZm9yIHBsb3R0aW5nLiBUaGVzZSB3aWxsIGJlIHRoZSBheGVzIGZvciB0aGUgMy1EIHBsb3QKZWlnZW52YWx1ZXM8LXJvdW5kKGJyYXlfdmFyaWFuY2VzWywyXSwgZGlnaXRzID0gNCkqMTAwCgojIFBsb3RseSAtIDMtRApwY29hX2JyYXkgPC0gcGxvdF9seShwY29hX2JyYXlfZGYsIHR5cGU9J3NjYXR0ZXIzZCcsIG1vZGU9J21hcmtlcnMnLCAKICAgICAgICAgICAgICAgICAgICAgeD1+RGltMiwgeT1+RGltMywgej1+RGltMSwgY29sb3JzPX5icmV3ZXIucGFsKDExLCdQYWlyZWQnKSwKICAgICAgICBjb2xvcj1+U3RhdGlvbiwgc3ltYm9scyA9IGMoJ2NpcmNsZScsJ2RpYW1vbmQnKSwgc3ltYm9sPX5CYXlzaWRlKSU+JSAgCiAgbGF5b3V0KGZvbnQ9bGlzdChzaXplPTEyKSwKICAgICAgICAgdGl0bGU9J1BDb0EgQnJheS1DdXJ0aXMgRGlzdGFuY2Ugb24gTG9nLVRyYW5zZm9ybWVkIENQVUUnLAogICAgICAgICBzY2VuZT1saXN0KHhheGlzPWxpc3QodGl0bGU9cGFzdGUwKCdDbyAyICcsZWlnZW52YWx1ZXNbMl0sJyUnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3d0aWNrbGFiZWxzPUZBTFNFLHplcm9saW5lY29sb3I9J2JsYWNrJyksCiAgICAgICAgICAgICAgICAgICAgeWF4aXM9bGlzdCh0aXRsZT1wYXN0ZTAoJ0NvIDMgJyxlaWdlbnZhbHVlc1szXSwnJScpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd3RpY2tsYWJlbHM9RkFMU0UsemVyb2xpbmVjb2xvcj0nYmxhY2snKSwKICAgICAgICAgICAgICAgICAgICB6YXhpcz1saXN0KHRpdGxlPXBhc3RlMCgnQ28gMSAnLGVpZ2VudmFsdWVzWzFdLCclJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93dGlja2xhYmVscz1GQUxTRSx6ZXJvbGluZWNvbG9yPSdibGFjaycpKSkKIyBwY29hX2JyYXkKCiMgc2F2ZSBpbiAiRW1iZWRkZWRfZmlndXJlcyIgZGlyZWN0b3J5IHNvIHRoYXQgaXQgY2FuIGJlIGhvc3RlZCBhdCBHaXRodWIgYW5kIGVtYmVkZGVkIGluIHRoaXMgbm90ZWJvb2sKd2l0aHI6OndpdGhfZGlyKCdFbWJlZGRlZF9maWd1cmVzJywgaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHBjb2FfYnJheSksIGZpbGU9InBjb2FfYnJheV9DUFVFLmh0bWwiLCBzZWxmY29udGFpbmVkID0gRikpCmBgYAoKPGlmcmFtZSBzcmM9IkVtYmVkZGVkX2ZpZ3VyZXMvcGNvYV9icmF5X0NQVUUuaHRtbCIgaGVpZ2h0PSI2MDBweCIgd2lkdGg9IjEwMCUiIHN0eWxlPSJib3JkZXI6bm9uZTsiPjwvaWZyYW1lPgoKUGxvdCBpbiAyRApgYGB7cn0KcGNvYV9icmF5XzJEIDwtIGdncGxvdChwY29hX2JyYXlfZGYsYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sb3IgPSBTdGF0aW9uLCBzaGFwZSA9IEJheXNpZGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJQYWlyZWQiKSArCiAgdGhlbWVfYncoKSArCiAgbGFicyh4ID0gcGFzdGUwKCdDbyAxICcsIGVpZ2VudmFsdWVzWzFdLCAnJScpLCB5ID0gcGFzdGUwKCdDbyAyICcsIGVpZ2VudmFsdWVzWzJdLCAnJScpLCB0aXRsZSA9ICJQQ29BIG9uIExvZy10cmFuc2Zvcm1lZCBDUFVFIHdpdGggQnJheS1DdXJ0aXMgRGlzc2ltaWxhcml0eSIpICsKICBjb29yZF9maXhlZChyYXRpbyA9IDEpCgpwY29hX2JyYXlfMkQKCmdnc2F2ZSgiZmlndXJlcy9wY29hX2JyYXlfQ1BVRV8yRC5lcHMiLHBjb2FfYnJheV8yRCwgd2lkdGggPSA3LCBoZWlnaHQgPSA1LCB1bml0cyA9IGMoImluIikpCmBgYAoKCgojIyMgVExQVUUgUENvQSBCcmF5IEN1cnRpcwoKCmBgYHtyfQojIGNvbnZlcnQgdG8gZGF0YWZyYW1lClRMUFVFX3RhYmxlX3RyYW5zZm9ybSA8LSBkYXRhLmZyYW1lKFRMUFVFX3RhYmxlX3RyYW5zZm9ybSkKcm93bmFtZXMoVExQVUVfdGFibGVfdHJhbnNmb3JtKSA8LSBjKFRMUFVFX3RhYmxlJFNhbXBsZUlEKQpUTFBVRV90YWJsZV90cmFuc2Zvcm0gPC0gVExQVUVfdGFibGVfdHJhbnNmb3JtWywtMV0KCiMgR2V0IEJyYXkgQ3VydGlzIGRpc3RhbmNlIG1hdHJpeCBmcm9tIGxvZy10cmFuc2Zvcm1lZCBDUFVFIGRhdGEKYnJheV9kbWF0PC12ZWdkaXN0KFRMUFVFX3RhYmxlX3RyYW5zZm9ybSxtZXRob2Q9ImJyYXkiKSAKCiMgdGhlIG5vcm1hbCBQQ29BIHJlc3VsdHMgaW4gbmVnYXRpdmUgZWlnZW52YWx1ZXMsIHNvIG5lZWQgY29ycmVjdGlvbi4gdXNlIHdjbWRzY2FsZSBhbmQgYWRkIGNhaWxsaWV6IGNvcnJlY3Rpb24KcGNvYV9icmF5IDwtIHdjbWRzY2FsZShicmF5X2RtYXQsIGVpZyA9IFRSVUUsIGFkZCA9ICJjYWlsbGlleiIpCgojIGNoZWNrIG91dCBzdW1tYXJ5IG9mIFBDb0EKZWlnZW52YWxzKHBjb2FfYnJheSkgJT4lCiAgc3VtbWFyeSgpIC0+IGV2CgojIGV4dHJhY3QgdmFyaWFuY2VzIGFuZCBwdXQgaW4gdGliYmxlCmJyYXlfdmFyaWFuY2VzIDwtIE5VTEwKZm9yIChpIGluIDE6bGVuZ3RoKGVpZ2VudmFscyhwY29hX2JyYXkpKSl7CiAgYnJheV92YXJpYW5jZXNbaV0gPC0gZWlnZW52YWxzKHBjb2FfYnJheSlbaV0vc3VtKGVpZ2VudmFscyhwY29hX2JyYXkpKQp9CgojIEV4dHJhY3QgdmFyaWFuY2VzIGZyb20gcGNvYSwgZnJvbSBjYWxjdWxhdGVkIGRpc3QuIG1ldHJpYwpicmF5X3ZhcmlhbmNlcyA8LSB0aWJibGUocm91bmQoYnJheV92YXJpYW5jZXMsMykpICU+JQogIHNlbGVjdChQZXJjVmFyID0gJ3JvdW5kKGJyYXlfdmFyaWFuY2VzLCAzKScpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiUENheGlzIikgJT4lCiAgZGF0YS5mcmFtZQpoZWFkKGJyYXlfdmFyaWFuY2VzKQoKIyBNYWtlIGEgc2NyZWVwbG90CmdncGxvdChicmF5X3ZhcmlhbmNlcywgYWVzKHggPSBhcy5udW1lcmljKFBDYXhpcyksIHkgPSBQZXJjVmFyKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJncmV5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh4ID0gIlBDIGF4aXMiLCB5ID0gIiUgVmFyaWFuY2UiLCB0aXRsZSA9ICJCcmF5LUN1cnRpcyBQQ29BIFNjcmVlcGxvdCIpCmBgYApUaGUgZmlyc3QgdHdvIGF4ZXMgKDE0LjcrMTAuMys5LjEpIGFyZSBub3QgYXMgZ29vZCBhcyBQQ0EuIFRvdGFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBmaXJzdCAzIFBDcyA9ICoqMzQuMSUqKikKCgoKClBsb3QgaW4gMkQKYGBge3J9CiMgRXh0cmFjdCB2YXJpYW5jZXMgZnJvbSB0aGUgcGNvYQpwY29hX2JyYXlfZGYgPC0gZGF0YS5mcmFtZShwY29hX2JyYXkkcG9pbnRzKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJTYW1wbGVJRCIpCgojIE1lcmdlIG1ldGFkYXRhIGludG8gdGhlIHBjb2EgZGF0YSB0YWJsZQpwY29hX2JyYXlfZGYgPC0gbGVmdF9qb2luKHBjb2FfYnJheV9kZiwgbWV0YWRhdGEsIGJ5ID0gIlNhbXBsZUlEIikKaGVhZChwY29hX2JyYXlfZGYpCgojIFNlbGVjdCBlaWdlbnZhbHVlcyBmcm9tIGRhdGFmcmFtZSwgcm91bmQgdG8gNCBwbGFjZXMgYW5kIG11bHRpcGx5IGJ5IDEwMCBmb3IgcGxvdHRpbmcuIFRoZXNlIHdpbGwgYmUgdGhlIGF4ZXMgZm9yIHRoZSAzLUQgcGxvdAplaWdlbnZhbHVlczwtcm91bmQoYnJheV92YXJpYW5jZXNbLDJdLCBkaWdpdHMgPSA0KSoxMDAKCnBjb2FfYnJheV8yRCA8LSBnZ3Bsb3QocGNvYV9icmF5X2RmLGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbG9yID0gU3RhdGlvbiwgc2hhcGUgPSBCYXlzaWRlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgKwogIHRoZW1lX2J3KCkgKwogIGxhYnMoeCA9IHBhc3RlMCgnQ28gMSAnLCBlaWdlbnZhbHVlc1sxXSwgJyUnKSwgeSA9IHBhc3RlMCgnQ28gMiAnLCBlaWdlbnZhbHVlc1syXSwgJyUnKSwgdGl0bGUgPSAiUENvQSBvbiBMb2ctdHJhbnNmb3JtZWQgVG90YWwgTGVuZ3RoIFBVRSwgd2l0aCBCcmF5LUN1cnRpcyBEaXNzaW1pbGFyaXR5IikgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkKCnBjb2FfYnJheV8yRApgYGAKClRoZSBhYm92ZSBpcyB2ZXJ5IHNpbWlsYXIgdG8gdGhlIEJyYXkgQ3VydGlzLyBQQ29BIG9uIENQVUUgKHdpdGggQXhpcyAyIGZsaXBwZWQpLiAKCgoKCgoKIyMjIFRMeFNGIFBVRSBQQ29BIEJyYXkgQ3VydGlzCgoKYGBge3J9CiMgY29udmVydCB0byBkYXRhZnJhbWUKVExTRi5QVUVfdGFibGVfdHJhbnNmb3JtIDwtIGRhdGEuZnJhbWUoVExTRi5QVUVfdGFibGVfdHJhbnNmb3JtKQpyb3duYW1lcyhUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm0pIDwtIGMoVExTRi5QVUVfdGFibGUkU2FtcGxlSUQpClRMU0YuUFVFX3RhYmxlX3RyYW5zZm9ybSA8LSBUTFNGLlBVRV90YWJsZV90cmFuc2Zvcm1bLC0xXQoKIyBHZXQgQnJheSBDdXJ0aXMgZGlzdGFuY2UgbWF0cml4IGZyb20gbG9nLXRyYW5zZm9ybWVkIENQVUUgZGF0YQpicmF5X2RtYXQ8LXZlZ2Rpc3QoVExTRi5QVUVfdGFibGVfdHJhbnNmb3JtLG1ldGhvZD0iYnJheSIpIAoKIyB0aGUgbm9ybWFsIFBDb0EgcmVzdWx0cyBpbiBuZWdhdGl2ZSBlaWdlbnZhbHVlcywgc28gbmVlZCBjb3JyZWN0aW9uLiB1c2Ugd2NtZHNjYWxlIGFuZCBhZGQgY2FpbGxpZXogY29ycmVjdGlvbgpwY29hX2JyYXkgPC0gd2NtZHNjYWxlKGJyYXlfZG1hdCwgZWlnID0gVFJVRSwgYWRkID0gImNhaWxsaWV6IikKCiMgY2hlY2sgb3V0IHN1bW1hcnkgb2YgUENvQQplaWdlbnZhbHMocGNvYV9icmF5KSAlPiUKICBzdW1tYXJ5KCkgLT4gZXYKCiMgZXh0cmFjdCB2YXJpYW5jZXMgYW5kIHB1dCBpbiB0aWJibGUKYnJheV92YXJpYW5jZXMgPC0gTlVMTApmb3IgKGkgaW4gMTpsZW5ndGgoZWlnZW52YWxzKHBjb2FfYnJheSkpKXsKICBicmF5X3ZhcmlhbmNlc1tpXSA8LSBlaWdlbnZhbHMocGNvYV9icmF5KVtpXS9zdW0oZWlnZW52YWxzKHBjb2FfYnJheSkpCn0KCiMgRXh0cmFjdCB2YXJpYW5jZXMgZnJvbSBwY29hLCBmcm9tIGNhbGN1bGF0ZWQgZGlzdC4gbWV0cmljCmJyYXlfdmFyaWFuY2VzIDwtIHRpYmJsZShyb3VuZChicmF5X3ZhcmlhbmNlcywzKSkgJT4lCiAgc2VsZWN0KFBlcmNWYXIgPSAncm91bmQoYnJheV92YXJpYW5jZXMsIDMpJykgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJQQ2F4aXMiKSAlPiUKICBkYXRhLmZyYW1lCmhlYWQoYnJheV92YXJpYW5jZXMpCgojIE1ha2UgYSBzY3JlZXBsb3QKZ2dwbG90KGJyYXlfdmFyaWFuY2VzLCBhZXMoeCA9IGFzLm51bWVyaWMoUENheGlzKSwgeSA9IFBlcmNWYXIpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImdyZXkiLCBjb2xvciA9ICJibGFjayIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHggPSAiUEMgYXhpcyIsIHkgPSAiJSBWYXJpYW5jZSIsIHRpdGxlID0gIkJyYXktQ3VydGlzIFBDb0EgU2NyZWVwbG90IikKYGBgClRoZSBmaXJzdCB0d28gYXhlcyAoMTUuOSsxMC45KzkuNSkgYXJlIG5vdCBhcyBnb29kIGFzIFBDQS4gVG90YWwgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGZpcnN0IDMgUENzID0gKiozNi4zJSoqKQoKCgoKUGxvdCBpbiAyRApgYGB7cn0KIyBFeHRyYWN0IHZhcmlhbmNlcyBmcm9tIHRoZSBwY29hCnBjb2FfYnJheV9kZiA8LSBkYXRhLmZyYW1lKHBjb2FfYnJheSRwb2ludHMpICU+JSAKICByb3duYW1lc190b19jb2x1bW4odmFyID0gIlNhbXBsZUlEIikKCiMgTWVyZ2UgbWV0YWRhdGEgaW50byB0aGUgcGNvYSBkYXRhIHRhYmxlCnBjb2FfYnJheV9kZiA8LSBsZWZ0X2pvaW4ocGNvYV9icmF5X2RmLCBtZXRhZGF0YSwgYnkgPSAiU2FtcGxlSUQiKQpoZWFkKHBjb2FfYnJheV9kZikKCiMgU2VsZWN0IGVpZ2VudmFsdWVzIGZyb20gZGF0YWZyYW1lLCByb3VuZCB0byA0IHBsYWNlcyBhbmQgbXVsdGlwbHkgYnkgMTAwIGZvciBwbG90dGluZy4gVGhlc2Ugd2lsbCBiZSB0aGUgYXhlcyBmb3IgdGhlIDMtRCBwbG90CmVpZ2VudmFsdWVzPC1yb3VuZChicmF5X3ZhcmlhbmNlc1ssMl0sIGRpZ2l0cyA9IDQpKjEwMAoKcGNvYV9icmF5XzJEIDwtIGdncGxvdChwY29hX2JyYXlfZGYsYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sb3IgPSBTdGF0aW9uLCBzaGFwZSA9IEJheXNpZGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJQYWlyZWQiKSArCiAgdGhlbWVfYncoKSArCiAgbGFicyh4ID0gcGFzdGUwKCdDbyAxICcsIGVpZ2VudmFsdWVzWzFdLCAnJScpLCB5ID0gcGFzdGUwKCdDbyAyICcsIGVpZ2VudmFsdWVzWzJdLCAnJScpLCB0aXRsZSA9ICJQQ29BIG9uIExvZy10cmFuc2Zvcm1lZCBUb3RhbCBMZW5ndGggeCBTaGVkZGluZyBGYWN0b3IgUFVFLCB3aXRoIEJyYXktQ3VydGlzIERpc3NpbWlsYXJpdHkiKSArCiAgY29vcmRfZml4ZWQocmF0aW8gPSAxKQoKcGNvYV9icmF5XzJECmBgYAoKVGhlIGFib3ZlIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgQnJheSBDdXJ0aXMvIFBDb0Egb24gQ1BVRSAod2l0aCBBeGlzIDIgZmxpcHBlZCkgYW5kIG9uIFRQVUUuIEp1c3QgY29udGludWUgd2l0aCBDUFVFIFBDb0EgZm9yIHNpbXBsaWNpdHkuCgoKCgoKCgoKCgojIyMgQ1BVRSBPcmRpbmF0aW9ucyBTdW1tYXJ5CkJvdGggdGhlIFBDQSBhbmQgUENvQSBleHBsYWluIHNpbWlsYXIgcGVyY2VudCB2YXJpYW5jZXMgdXNpbmcgdGhlIGZpcnN0IHR3byBheGVzICgyOC44JSBmb3IgUENBIGFuZCAyMi4zJSkgZm9yIFBDb0EuIENvbnRpbnVlIHdpdGggYm90aCBmb3IgdGhlIGVudmlyb25tZW50YWwgZml0IGZvciBub3cgdG8gc2VlIGlmIHRoZXkgbGVhZCB0byBkaWZmZXJlbnQgaW50ZXJwcmV0YXRpb25zLgoKIyMjIE9yZGluYXRpb25zIHdpdGggRW52aXJvbm1lbnRhbCBWYXJpYWJsZXMKCiMjIyMgQ1BVRSBQQ0Egd2l0aCBFbnZpcm9ubWVudGFsIFZhcmlhYmxlcwpgYGB7cn0KIyByZS1ydW4gdGhlIFBDQQpsb2dfdHJhbnNmb3JtX3BjYSA8LSBwcmNvbXAoQ1BVRV90YWJsZV90cmFuc2Zvcm1fY2VuX3N0KQoKIyB0cmltIG1ldGFkYXRhIHRvIHJlbW92ZSBzYW1wbGVzIHRoYXQgYXJlIG5vdCBpbiBDUFVFIGRhdGEgdGFibGUKbWV0YWRhdGFfb3JkaW5hdGlvbnMgPC0gbWV0YWRhdGFbbWV0YWRhdGEkU2FtcGxlSUQgJWluJSByb3duYW1lcyhsb2dfdHJhbnNmb3JtX3BjYSR4KSxdCgojIGFuZCByZW1vdmUgcmVwZXRpdGl2ZSBtZXRhZGF0YSB2YXJpYWJsZXMgbGlrZSBEYXRlLyBNb250aC8gWWVhci8gVHJhd2wgIwptZXRhZGF0YV9vcmRpbmF0aW9ucyA8LSBzZWxlY3QobWV0YWRhdGFfb3JkaW5hdGlvbnMsIC0iWWVhci5UcmF3bCMiLCAtRGF0ZSwgLU1vbnRoLCAtWWVhcikKCiMgc29ydCBtZXRhZGF0YSBpbiBzYW1lIG9yZGVyIGFzIHRoZSBwY2EgbWF0cml4Cm1ldGFkYXRhX29yZGluYXRpb25zIDwtIG1ldGFkYXRhX29yZGluYXRpb25zICU+JSBhcnJhbmdlKGZhY3RvcihTYW1wbGVJRCwgbGV2ZWxzID0gcm93bmFtZXMobG9nX3RyYW5zZm9ybV9wY2EkeCkpKQoKIyBjaGFuZ2UgdGhlIGNvbHVtbiBuYW1lICJEYXRlY29kZSIgdG8gIkRhdGUiIChiZXR0ZXIgZm9yIHBsb3R0aW5nKQpjb2xuYW1lcyhtZXRhZGF0YV9vcmRpbmF0aW9ucylbMl0gPC0gIkRhdGUiCgojIGZpdCBlbnZpcm9ubWVudGFsIGZhY3RvcnMgYW5kIHNhdmUgc3RhdHMgb3V0cHV0CmxvZ190cmFuc2Zvcm1fcGNhX2VudmZpdCA8LSBlbnZmaXQobG9nX3RyYW5zZm9ybV9wY2EsIG1ldGFkYXRhX29yZGluYXRpb25zLCBwZXJtdXRhdGlvbnMgPSAxMDAwKQpjYXB0dXJlLm91dHB1dChsb2dfdHJhbnNmb3JtX3BjYV9lbnZmaXQsIGZpbGUgPSAic3RhdHNfcmVzdWx0cy9sb2dfdHJhbnNmb3JtX3BjYV9lbnZmaXRfQ1BVRS50eHQiKQoKIyMgVGhlIG9ubHkgc2lnbmZpY2FudCB2YXJpYWJsZSBpcyBTdGF0aW9uIChwID0gMC4wMDQ5OTUpCgojIE1ha2UgYW4gb3JkaW5hdGlvbiBvYmplY3Qgb3V0IG9mIHRoZSBlbnZmaXQgd2l0aCBzaWcgdmFyaWFibGUKbG9nX3RyYW5zZm9ybV9wY2FfZW52Zml0X3N0YXRpb24gPC0gZW52Zml0KGxvZ190cmFuc2Zvcm1fcGNhflN0YXRpb24sIG1ldGFkYXRhX29yZGluYXRpb25zLCBwZXJtdXRhdGlvbnMgPSAxMDAwKQpgYGAKCgpQbG90IGluIDJELiAKQWN0dWFsbHkgZGlkbid0IHB1dCBhbnkgdmVjdG9ycyBpbiB0aGlzIHBsb3QgYmVjYXVzZSB0aGUgb25seSBzaWduaWZpY2FudCB2YXJpYWJsZSwgc3RhdGlvbiwgaXMgYWxyZWFkeSBjb2xvci1jb2RlZCBhbmQgdGhlcmUgYXJlIDExIHN0YXRpb25zIHNvIHZlY3RvcnMgd291bGQgYmUgbWVzc3kuCmBgYHtyfQojIENvbnZlcnQgY2hhcmFjdGVycyBpbiBtZXRhZGF0YSB0byBmYWN0b3JzCm1ldGFkYXRhX29yZGluYXRpb25zIDwtIG1ldGFkYXRhX29yZGluYXRpb25zICU+JSBtdXRhdGVfaWYoc2FwcGx5KG1ldGFkYXRhX29yZGluYXRpb25zLCBpcy5jaGFyYWN0ZXIpLCBhcy5mYWN0b3IpCndpdGgoYXMuZGF0YS5mcmFtZShtZXRhZGF0YV9vcmRpbmF0aW9ucyksIGxldmVscyhTdGF0aW9uKSkKCiMgRGVmaW5lIHBsb3QgcGFyYW1ldGVycwpjb2x2ZWMgPC0gYyhicmV3ZXIucGFsKDExLCdQYWlyZWQnKSkgIyBjb2xvcnMgb2Ygc3RhdGlvbnMKc2hhcGV2ZWMgPC0gYygxOSwxOCkgIyBzaGFwZXMgaW5kaWNhdGluZyBCYXlzaWRlCgojIFNldCB1cCBiYXNpYyBwbG90CnBhcih4cGQgPSBULCBtYXIgPSBwYXIoKSRtYXIgKyBjKDAsMCwwLDguNSkpICMgbGVhdmUgc3BhY2UgdG8gYWRkIGxlZ2VuZC4geHBkID0gVCBhbGxvd3MgbGVnZW5kIHRvIGJlIG91dHNpZGUgb2YgdGhlIHBsb3QKIyBBZGQgdGhlIHNpdGUgc2NvcmVzCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBsb3Qoc2NvcmVzKGxvZ190cmFuc2Zvcm1fcGNhLCBkaXNwbGF5ID0gInNpdGVzIiksIGNvbCA9IGNvbHZlY1tTdGF0aW9uXSwgcGNoID0gc2hhcGV2ZWNbQmF5c2lkZV0sIGNleCA9IDIsIHhsYWIgPSAiQ28xIDE3LjUlIiwgeWxhYiA9ICJDbzIgMTEuMyUiKSkKIyBBZGQgdGhlIGh1bGxzIGluZGljYXRpbmcgQmF5c2lkZQp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBvcmRpaHVsbChsb2dfdHJhbnNmb3JtX3BjYSwgQmF5c2lkZSwgbHdkID0gMiwgbHR5ID0gYygzLDUpLCBsYWJlbCA9IEZBTFNFKSkKIyBBZGQgbGVnZW5kcwp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBsZWdlbmQoMS44LCAyLCBsZWdlbmQgPSBsZXZlbHMoU3RhdGlvbiksIGNvbCA9IGNvbHZlYywgcGNoID0gYygxOSwxOCwxOSwxOSwxOSwxOCwxOSwxOSwxOSwxOCwxOCksIGJ0eSA9ICJuIiwgcHQuY2V4ID0gMiwgY2V4ID0gLjgpKQpsZWdlbmQoMS44LCA1LCBjKCJFQVNUIiwgIldFU1QiKSwgY29sID0gYygiYmxhY2siKSwgbHR5ID0gYygzLDUpLCBsd2QgPSAyLCBidHkgPSAibiIsIGNleCA9IC44KSAjIExlZ2VuZCBmb3IgQmF5c2lkZSBodWxsIGxpbmVzLSBkaWQgdGhpcyBtYW51YWxseQoKCgojIEV4cG9ydCB1c2luZyBiYXNlIFIvIHZlZ2FuIGhlbHBlcnMKc2V0RVBTKCkKcG9zdHNjcmlwdCgiRmlndXJlcy9wY2FfbG9nX3RyYW5zZm9ybV9DUFVFX2VudmZpdC5lcHMiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUpCnBhcih4cGQgPSBULCBtYXIgPSBwYXIoKSRtYXIgKyBjKDAsMCwwLDguNSkpCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBsb3Qoc2NvcmVzKGxvZ190cmFuc2Zvcm1fcGNhLCBkaXNwbGF5ID0gInNpdGVzIiksIGNvbCA9IGNvbHZlY1tTdGF0aW9uXSwgcGNoID0gc2hhcGV2ZWNbQmF5c2lkZV0sIGNleCA9IDIsIHhsYWIgPSAiQ28xIDE3LjUlIiwgeWxhYiA9ICJDbzIgMTEuMyUiKSkKd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgb3JkaWh1bGwobG9nX3RyYW5zZm9ybV9wY2EsIEJheXNpZGUsIGx3ZCA9IDIsIGx0eSA9IGMoMyw1KSwgbGFiZWwgPSBGQUxTRSkpCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIGxlZ2VuZCgxLjgsIDIsIGxlZ2VuZCA9IGxldmVscyhTdGF0aW9uKSwgY29sID0gY29sdmVjLCBwY2ggPSBjKDE5LDE4LDE5LDE5LDE5LDE4LDE5LDE5LDE5LDE4LDE4KSwgYnR5ID0gIm4iLCBwdC5jZXggPSAyLCBjZXggPSAuOCkpCmxlZ2VuZCgxLjgsIDUsIGMoIkVBU1QiLCAiV0VTVCIpLCBjb2wgPSBjKCJibGFjayIpLCBsdHkgPSBjKDMsNSksIGx3ZCA9IDIsIGJ0eSA9ICJuIiwgY2V4ID0gLjgpCmRldi5vZmYoKQoKYGBgCgoKCiMjIyMgQ1BVRSBQQ29BIHdpdGggRW52aXJvbm1lbnRhbCBWYXJpYWJsZXMKYGBge3J9CiMgcmUtcnVuIHRoZSBvcmRpbmF0aW9uCkNQVUVfdGFibGVfdHJhbnNmb3JtIDwtIENQVUVfdGFibGUKQ1BVRV90YWJsZV90cmFuc2Zvcm1bLDI6bGVuZ3RoKENQVUVfdGFibGUpXSA8LSBsb2cxMChDUFVFX3RhYmxlWywyOmxlbmd0aChDUFVFX3RhYmxlKV0rMSkKQ1BVRV90YWJsZV90cmFuc2Zvcm0gPC0gZGF0YS5mcmFtZShDUFVFX3RhYmxlX3RyYW5zZm9ybSkKcm93bmFtZXMoQ1BVRV90YWJsZV90cmFuc2Zvcm0pIDwtIGMoQ1BVRV90YWJsZSRTYW1wbGVJRCkKQ1BVRV90YWJsZV90cmFuc2Zvcm0gPC0gQ1BVRV90YWJsZV90cmFuc2Zvcm1bLC0xXQpicmF5X2RtYXQ8LXZlZ2Rpc3QoQ1BVRV90YWJsZV90cmFuc2Zvcm0sbWV0aG9kPSJicmF5IikgCnBjb2FfYnJheSA8LSB3Y21kc2NhbGUoYnJheV9kbWF0LCBlaWcgPSBUUlVFLCBhZGQgPSAiY2FpbGxpZXoiKQoKIyB0cmltIG1ldGFkYXRhIHRvIHJlbW92ZSBzYW1wbGVzIHRoYXQgYXJlIG5vdCBpbiBDUFVFIGRhdGEgdGFibGUKbWV0YWRhdGFfb3JkaW5hdGlvbnMgPC0gbWV0YWRhdGFbbWV0YWRhdGEkU2FtcGxlSUQgJWluJSByb3duYW1lcyhDUFVFX3RhYmxlX3RyYW5zZm9ybSksXQoKIyBhbmQgcmVtb3ZlIHJlcGV0aXRpdmUgbWV0YWRhdGEgdmFyaWFibGVzIGxpa2UgRGF0ZS8gTW9udGgvIFllYXIvIFRyYXdsICMKbWV0YWRhdGFfb3JkaW5hdGlvbnMgPC0gc2VsZWN0KG1ldGFkYXRhX29yZGluYXRpb25zLCAtIlllYXIuVHJhd2wjIiwgLURhdGUsIC1Nb250aCwgLVllYXIpCgojIHNvcnQgbWV0YWRhdGEgaW4gc2FtZSBvcmRlciBhcyB0aGUgcGNhIG1hdHJpeAptZXRhZGF0YV9vcmRpbmF0aW9ucyA8LSBtZXRhZGF0YV9vcmRpbmF0aW9ucyAlPiUgYXJyYW5nZShmYWN0b3IoU2FtcGxlSUQsIGxldmVscyA9IHJvd25hbWVzKGxvZ190cmFuc2Zvcm1fcGNhJHgpKSkKCiMgY2hhbmdlIHRoZSBjb2x1bW4gbmFtZSAiRGF0ZWNvZGUiIHRvICJEYXRlIiAoYmV0dGVyIGZvciBwbG90dGluZykKY29sbmFtZXMobWV0YWRhdGFfb3JkaW5hdGlvbnMpWzJdIDwtICJEYXRlIgoKIyBmaXQgZW52aXJvbm1lbnRhbCBmYWN0b3JzIGFuZCBzYXZlIHN0YXRzIG91dHB1dApwY29hX2JyYXlfQ1BVRV9lbnZmaXQgPC0gZW52Zml0KHBjb2FfYnJheSwgbWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBlcm11dGF0aW9ucyA9IDEwMDApCmNhcHR1cmUub3V0cHV0KHBjb2FfYnJheV9DUFVFX2VudmZpdCwgZmlsZSA9ICJzdGF0c19yZXN1bHRzL3Bjb2FfYnJheV9lbnZmaXRfQ1BVRS50eHQiKQoKIyMgQmF5c2lkZSAocCA9IDAuMDA0OTk1KSwgYW5kIFN0YXRpb24gKHAgPSAwLjAwMzk5NikgYXJlIHNpZ25maWNhbnQuIERhdGUgKHAgPSAwLjA2NTkzKSBpcyBzbGlnaHRseSBhYm92ZSAwLjA1IHRocmVzaG9sZAoKIyBNYWtlIGFuIG9yZGluYXRpb24gb2JqZWN0IG91dCBvZiB0aGUgZW52Zml0IHdpdGggc2lnIHZhcmlhYmxlcwpwY29hX2JyYXlfZW52Zml0X0RhdGUgPC0gZW52Zml0KHBjb2FfYnJheX5EYXRlLCBtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGVybXV0YXRpb25zID0gMTAwMCkKcGNvYV9icmF5X2VudmZpdF9CYXlzaWRlIDwtIGVudmZpdChwY29hX2JyYXl+QmF5c2lkZSwgbWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBlcm11dGF0aW9ucyA9IDEwMDApCnBjb2FfYnJheV9lbnZmaXRfc3RhdGlvbiA8LSBlbnZmaXQocGNvYV9icmF5flN0YXRpb24sIG1ldGFkYXRhX29yZGluYXRpb25zLCBwZXJtdXRhdGlvbnMgPSAxMDAwKQpgYGAKCgpQbG90IGluIDJELiAKQWN0dWFsbHkgZGlkbid0IHB1dCBhbnkgdmVjdG9ycyBpbiB0aGlzIHBsb3QgYmVjYXVzZSB0aGUgb25seSBzaWduaWZpY2FudCB2YXJpYWJsZSwgc3RhdGlvbiwgaXMgYWxyZWFkeSBjb2xvci1jb2RlZCBhbmQgdGhlcmUgYXJlIDExIHN0YXRpb25zIHNvIHZlY3RvcnMgd291bGQgYmUgbWVzc3kuCmBgYHtyfQojIENvbnZlcnQgY2hhcmFjdGVycyBpbiBtZXRhZGF0YSB0byBmYWN0b3JzCm1ldGFkYXRhX29yZGluYXRpb25zIDwtIG1ldGFkYXRhX29yZGluYXRpb25zICU+JSBtdXRhdGVfaWYoc2FwcGx5KG1ldGFkYXRhX29yZGluYXRpb25zLCBpcy5jaGFyYWN0ZXIpLCBhcy5mYWN0b3IpCndpdGgoYXMuZGF0YS5mcmFtZShtZXRhZGF0YV9vcmRpbmF0aW9ucyksIGxldmVscyhTdGF0aW9uKSkKCiMgRGVmaW5lIHBsb3QgcGFyYW1ldGVycwpjb2x2ZWMgPC0gYyhicmV3ZXIucGFsKDExLCdQYWlyZWQnKSkgIyBjb2xvcnMgb2Ygc3RhdGlvbnMKc2hhcGV2ZWMgPC0gYygxOSwxOCkgIyBzaGFwZXMgaW5kaWNhdGluZyBCYXlzaWRlCgojIFNldCB1cCBiYXNpYyBwbG90CnBhcih4cGQgPSBULCBtYXIgPSBwYXIoKSRtYXIgKyBjKDAsMCwwLDgpKSAjIGxlYXZlIHNwYWNlIHRvIGFkZCBsZWdlbmQuIHhwZCA9IFQgYWxsb3dzIGxlZ2VuZCB0byBiZSBvdXRzaWRlIG9mIHRoZSBwbG90CiMgQWRkIHRoZSBzaXRlIHNjb3Jlcwp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBwbG90KHNjb3JlcyhwY29hX2JyYXksIGRpc3BsYXkgPSAic2l0ZXMiKSwgY29sID0gY29sdmVjW1N0YXRpb25dLCBwY2ggPSBzaGFwZXZlY1tCYXlzaWRlXSwgY2V4ID0gMiwgeGxhYiA9ICJDbzEgMTIuMCUiLCB5bGFiID0gIkNvMiAxMC4zJSIpKQojIEFkZCB0aGUgZGF0ZSB2ZWN0b3IKcGxvdChwY29hX2JyYXlfZW52Zml0X0RhdGUsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siKQojIEFkZCB0aGUgaHVsbHMgaW5kaWNhdGluZyBCYXlzaWRlCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIG9yZGlodWxsKHBjb2FfYnJheSwgQmF5c2lkZSwgbHdkID0gMiwgbHR5ID0gYygzLDUpLCBsYWJlbCA9IEZBTFNFKSkKIyBBZGQgbGVnZW5kcwp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBsZWdlbmQoMC43NywgMC4zLCBsZWdlbmQgPSBsZXZlbHMoU3RhdGlvbiksIGNvbCA9IGNvbHZlYywgcGNoID0gYygxOSwxOCwxOSwxOSwxOSwxOCwxOSwxOSwxOSwxOCwxOCksIGJ0eSA9ICJuIiwgcHQuY2V4ID0gMiwgY2V4ID0gLjgpKQpsZWdlbmQoMC43NywgMC41LCBjKCJFQVNUIiwgIldFU1QiKSwgY29sID0gYygiYmxhY2siKSwgbHR5ID0gYygzLDUpLCBsd2QgPSAyLCBidHkgPSAibiIsIGNleCA9IC44KSAjIExlZ2VuZCBmb3IgQmF5c2lkZSBodWxsIGxpbmVzLSBkaWQgdGhpcyBtYW51YWxseQoKCiMgRXhwb3J0IHVzaW5nIGJhc2UgUi8gdmVnYW4gaGVscGVycwpzZXRFUFMoKQpwb3N0c2NyaXB0KCJGaWd1cmVzL3Bjb2FfYnJheV9lbnZmaXRfQ1BVRS5lcHMiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUpCnBhcih4cGQgPSBULCBtYXIgPSBwYXIoKSRtYXIgKyBjKDAsMCwwLDgpKSAKd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGxvdChzY29yZXMocGNvYV9icmF5LCBkaXNwbGF5ID0gInNpdGVzIiksIGNvbCA9IGNvbHZlY1tTdGF0aW9uXSwgcGNoID0gc2hhcGV2ZWNbQmF5c2lkZV0sIGNleCA9IDIsIHhsYWIgPSAiQ28xIDEyLjAlIiwgeWxhYiA9ICJDbzIgMTAuMyUiKSkKcGxvdChwY29hX2JyYXlfZW52Zml0X0RhdGUsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siKQp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBvcmRpaHVsbChwY29hX2JyYXksIEJheXNpZGUsIGx3ZCA9IDIsIGx0eSA9IGMoMyw1KSwgbGFiZWwgPSBGQUxTRSkpCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIGxlZ2VuZCgwLjc3LCAwLjMsIGxlZ2VuZCA9IGxldmVscyhTdGF0aW9uKSwgY29sID0gY29sdmVjLCBwY2ggPSBjKDE5LDE4LDE5LDE5LDE5LDE4LDE5LDE5LDE5LDE4LDE4KSwgYnR5ID0gIm4iLCBwdC5jZXggPSAyLCBjZXggPSAuOCkpCmxlZ2VuZCgwLjc3LCAwLjUsIGMoIkVBU1QiLCAiV0VTVCIpLCBjb2wgPSBjKCJibGFjayIpLCBsdHkgPSBjKDMsNSksIGx3ZCA9IDIsIGJ0eSA9ICJuIiwgY2V4ID0gLjgpIApkZXYub2ZmKCkKYGBgCgoKCiMjIE9yZGluYXRpb24gU3VtbWFyeQpGb3IgdGhlIHRyYXdsIGRhdGEsIG9yZGluYXRpb25zIG9mIHRoZSBhbGxvbWV0cmljIGNvcnJlY3Rpb25zIChzdW0gb2YgdG90YWwgbGVuZ3RoIGFuZCBzdW0gdG90YWwgbGVuZ3RoIHggc2hlZGRpbmcgZmFjdG9yKSBhcmUgVkVSWSBzaW1pbGFyIHRvIHRoZSByZXN1bHRzIHVzaW5nIGp1c3QgQ1BVRS4gU28sIGZvciBzaW1wbGljaXR5LCBqdXN0IHByZXNlbnQgdGhlIENQVUUuICAKClRoZSBCcmF5LUN1cnRpcyBQQ29BIG9uIHRoZSBlRE5BIGFuZCB0cmF3bCBDUFVFIGRhdGEgYXJlIHNpbWlsYXIgYnV0IG5vdCBpZGVudGljYWwuIEVhY2ggb3JkaW5hdGlvbiByZXZlYWxzIHRoYXQgQmF5c2lkZSBhbmQgRGF0ZSBhcmUgaW1wb3J0YW50IGNvcnJlbGF0ZWQgdmFyaWFibGVzLiBUaGUgZUROQSBvcmRpbmF0aW9uIHdhcyBhbHNvIHNlbnNpdGl2ZSB0byBETywgd2hpY2ggZGlkIG5vdCBhcHBlYXIgaW4gdGhlIENQVUUgb3JkaW5hdGlvbi4gVGhlIGZpcnN0IHR3byBheGVzIG9mIGJvdGggb3JkaW5hdGlvbnMgZXhwbGFpbiBzb21lLCBidXQgbm90IGEgbWFqb3JpdHkgb2YgdGhlIHZhcmlhbmNlICgzMi4xJSBmb3IgZUROQSBhbmQgMjIuMyUgZm9yIENQVUUpLiAKCgoK