Load libraries

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.3     ✓ dplyr   1.0.7
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.1
── Conflicts ─────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(phyloseq)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
library(phangorn)
Loading required package: ape
library(readr)
library(ape)
library(vegan)
Loading required package: permute
Loading required package: lattice
This is vegan 2.5-7

Attaching package: ‘vegan’

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

    diversity, treedist
library(RColorBrewer)
library(microbiome)

microbiome R package (microbiome.github.com)
    


 Copyright (C) 2011-2020 Leo Lahti, 
    Sudarshan Shetty et al. <microbiome.github.io>


Attaching package: ‘microbiome’

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

    diversity

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

    diversity

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

    alpha

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

    transform
library(compositions)
Welcome to compositions, a package for compositional data analysis.
Find an intro with "? compositions"


Attaching package: ‘compositions’

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

    balance

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

    cor, cov, dist, var

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

    %*%, norm, scale, scale.default
library(SpiecEasi)

Attaching package: ‘SpiecEasi’

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

    alr, clr
library(otuSummary)
library(psych)

Attaching package: ‘psych’

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

    cor2cov, shannon

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

    ellipses, pairwisePlot

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

    alpha

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

    %+%, alpha
library(Matrix)

Attaching package: ‘Matrix’

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

    tril, triu

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

    expand, pack, unpack
library(igraph)

Attaching package: ‘igraph’

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

    make_graph

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

    normalize

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

    diversity

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

    diversity

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

    permute

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

    diversity

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

    edges, mst, ring

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

    as_data_frame, groups, union

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

    compose, simplify

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

    crossing

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

    as_data_frame

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

    decompose, spectrum

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

    union
library(plotly)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

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

    groups

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

    last_plot

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

    filter

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

    layout
library(egg)
Loading required package: gridExtra

Attaching package: ‘gridExtra’

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

    combine
library(ggvegan)


# Helper functions from J. Cram https://biovcnet.github.io/_pages/NetworkScience_SparCC.nb
pass <- function(x){x} 
# Get lower triangle of the correlation matrix
  get_lower_tri<-function(cormat){
    cormat[upper.tri(cormat)] <- NA
    return(cormat)
  }
# Get upper triangle of the correlation matrix
  get_upper_tri <- function(cormat){
    cormat[lower.tri(cormat)]<- NA
    return(cormat)
  }

reorder_cormat <- function(cormat){
# Use correlation between variables as distance
dd <- as.dist((1-cormat)/2)
hc <- hclust(dd)
cormat <-cormat[hc$order, hc$order]
}

reorder_cor_and_p <- function(cormat, pmat){
  dd <- as.dist((1-cormat)/2)
  hc <- hclust(dd)
  cormat <-cormat[hc$order, hc$order]
  pmat <- pmat[hc$order, hc$order]
  list(r = cormat, p = pmat)
}

# Report versions of packages
sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggvegan_0.1-0      egg_0.4.5          gridExtra_2.3      plotly_4.9.2.2     igraph_1.2.6       Matrix_1.3-0       psych_2.1.3       
 [8] otuSummary_0.1.1   SpiecEasi_1.1.1    compositions_2.0-0 microbiome_1.10.0  RColorBrewer_1.1-2 vegan_2.5-7        lattice_0.20-41   
[15] permute_0.9-5      phangorn_2.5.5     ape_5.4-1          phyloseq_1.32.0    forcats_0.5.1      stringr_1.4.0      dplyr_1.0.7       
[22] purrr_0.3.4        readr_1.4.0        tidyr_1.1.3        tibble_3.1.3       ggplot2_3.3.5      tidyverse_1.3.1   

loaded via a namespace (and not attached):
 [1] Rtsne_0.15          VGAM_1.1-5          colorspace_2.0-2    ellipsis_0.3.2      XVector_0.28.0      fs_1.5.0            rstudioapi_0.13    
 [8] ggrepel_0.9.1       fansi_0.5.0         lubridate_1.7.10    xml2_1.3.2          codetools_0.2-18    splines_4.0.2       mnormt_2.0.2       
[15] robustbase_0.93-6   knitr_1.30          ade4_1.7-16         jsonlite_1.7.2      broom_0.7.9         cluster_2.1.0       dbplyr_2.1.1       
[22] compiler_4.0.2      httr_1.4.2          backports_1.2.1     assertthat_0.2.1    lazyeval_0.2.2      cli_3.0.1           htmltools_0.5.1.1  
[29] prettyunits_1.1.1   tools_4.0.2         gtable_0.3.0        glue_1.4.2          reshape2_1.4.4      fastmatch_1.1-0     Rcpp_1.0.7         
[36] Biobase_2.48.0      cellranger_1.1.0    vctrs_0.3.8         Biostrings_2.56.0   multtest_2.44.0     nlme_3.1-151        iterators_1.0.13   
[43] tensorA_0.36.2      xfun_0.24           rvest_1.0.1         lifecycle_1.0.0     DEoptimR_1.0-8      zlibbioc_1.34.0     MASS_7.3-53        
[50] scales_1.1.1        hms_1.1.0           parallel_4.0.2      biomformat_1.16.0   rhdf5_2.32.4        huge_1.3.4.1        stringi_1.7.3      
[57] S4Vectors_0.26.1    foreach_1.5.1       BiocGenerics_0.34.0 shape_1.4.6         rlang_0.4.11        pkgconfig_2.0.3     Rhdf5lib_1.10.1    
[64] htmlwidgets_1.5.3   tidyselect_1.1.1    plyr_1.8.6          magrittr_2.0.1      R6_2.5.0            IRanges_2.22.2      generics_0.1.0     
[71] DBI_1.1.1           pillar_1.6.2        haven_2.3.1         withr_2.4.2         mgcv_1.8-33         survival_3.2-7      bayesm_3.1-4       
[78] modelr_0.1.8        pulsar_0.3.7        crayon_1.4.1        utf8_1.2.2          tmvnsim_1.0-2       progress_1.2.2      grid_4.0.2         
[85] readxl_1.3.1        data.table_1.13.4   reprex_2.0.1        digest_0.6.27       stats4_4.0.2        munsell_0.5.0       glmnet_4.1-1       
[92] viridisLite_0.4.0   quadprog_1.5-8     

Prepare the data

Import

Metadata:

metadata <- read_csv("Metadata.csv")

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  `Sample Name` = col_character(),
  Replicate = col_character(),
  Type = col_character(),
  SizeFraction = col_character(),
  Season = col_character(),
  OxCond = col_character()
)
ℹ Use `spec()` for the full column specifications.

Import SRA table and match SRA IDs with sample IDs in metadata file

SRARunTable <- read_csv("sra_data/SraRunTable.txt")

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  AvgSpotLen = col_double(),
  Bases = col_double(),
  Bytes = col_double(),
  ReleaseDate = col_datetime(format = ""),
  Depth_m = col_double(),
  CH4_uM = col_double(),
  H2S_Um = col_double(),
  Oxygen_uM = col_double(),
  Particulate_Sulfur_uM = col_double(),
  salinity = col_double(),
  Temperature_degree_C = col_double(),
  TZVS_uM = col_double()
)
ℹ Use `spec()` for the full column specifications.
metadata <- left_join(metadata, SRARunTable, by = 'Sample Name')

DADA2 results:

# Import Count table. Skip first row of tsv file, which is just some text
count_table <- read_tsv(file="dada2_export/ASVs_counts.tsv")
Missing column names filled in: 'X1' [1]
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  X1 = col_character()
)
ℹ Use `spec()` for the full column specifications.
# And specify that the first column of data are rownames
count_table <- column_to_rownames(count_table, var = colnames(count_table)[1])

# Import taxonomy of ASVs
taxonomy <- read_tsv(file="dada2_export/ASVs_taxonomy.tsv")
Missing column names filled in: 'X1' [1]
── Column specification ─────────────────────────────────────────────────────────────────────────────────────────
cols(
  X1 = col_character(),
  Kingdom = col_character(),
  Supergroup = col_character(),
  Division = col_character(),
  Class = col_character(),
  Order = col_character(),
  Family = col_character(),
  Genus = col_character(),
  Species = col_character()
)
# And specify that the first column of data are rownames
taxonomy <- column_to_rownames(taxonomy, var = colnames(taxonomy)[1])

QC and Filtering

Rarefaction curves

# Use rarecurve, from the Vegan package. Rarcurve expects the dataset as a dataframe so we need to use as.data.frame again:
count_table_df <- as.data.frame(count_table)

# Plot the rarefaction curves, color-coding by the colors listed in sample_info_tab, which indicate sample type, and transforming using t() again
# Running this 5-10 samples at a time because otherwise it takes a long time to render
rarecurve(t(count_table_df), step=100, cex=0.5, ylab="ASVs", label=T)

Remove singletons

count_table_no_singletons <- filter(count_table,rowSums(count_table)>1)
# retains all ASVs (out of 14176)

and change sample names from NCBI ID to our internal sample IDs

# Modify taxa names in count_table_no_singletons, which are the NCBI SRA numbers. Want to use our internal sample key
key <- SRARunTable %>% select(Run, 'Sample Name')

x <- (t(count_table_no_singletons))
x <- as.data.frame(cbind(x, Run = rownames(x)))

y <- t(left_join(x, key, by = "Run"))
colnames(y) <- y['Sample Name',]
y <- y[ !(rownames(y) %in% c('Sample Name', 'Run')), ]

count_table_2 <- type_convert(as.data.frame(y))

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double()
)
ℹ Use `spec()` for the full column specifications.

Make Phylo Tree

This process takes a LONG time so run once and save .RData object In the Dada2 tools, there are no options to build a tree (unlike in Qiime2) but we can build it here using DECIPHER and phangorn

(Based on https://f1000research.com/articles/5-1492/v2)

Make an alignment using tools from Decipher (Note- alignment step takes several hours. Commented out for now. Only need to run once)

## import fasta
# fas <- "dada2_export/ASVs.fa"
# seqs <- readDNAStringSet(fas)
# seqs
# 
# # perform the alignment
# aligned <- AlignSeqs(seqs) # automatically detects and uses all cores
# 
# # view the alignment in a browser (optional)
# BrowseSeqs(aligned, highlight=0)
# 
# # write out aligned sequence file
# writeXStringSet(aligned, file="ASVs.aligned.fasta")

Use phangorn package to build tree. Here we are building a maximum likelihood neighbor-joining tree. (Also takes a while to run. Comment out for now.)

# phang.align <- phyDat(as(aligned, "matrix"), type="DNA") # convert to phyDat format
# dm <- dist.ml(phang.align) # calculate pairwise distance matrix
# treeNJ <- NJ(dm) # perform neighbor-joining tree method
# fit = pml(treeNJ, data=phang.align) # compute intermal max likelihood

Save and re-load dataset

Since the step above takes a long time, save all variables up to this point in environment as RData object

save.image("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_tree.RData")

Re-load

load("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_tree.RData")

Make phyloseq objects

Here we will do ordinations using the phyloseq package, which first requires making phyloseq objects out of each of our input data tables (in the last tutorial, I imported the tree using phyloseq so it is already a phyloseq object)

ASV =   otu_table(count_table_2, taxa_are_rows =  TRUE)
TAX =   tax_table(as.matrix(taxonomy))
META    =   sample_data(data.frame(metadata, row.names = metadata$`Sample Name`))
TREE = phy_tree(fit$tree)

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] "ASV_1" "ASV_2" "ASV_3" "ASV_4" "ASV_5" "ASV_6"
head(taxa_names(ASV))
[1] "ASV_1" "ASV_2" "ASV_3" "ASV_4" "ASV_5" "ASV_6"
head(taxa_names(TREE))
[1] "ASV_1" "ASV_2" "ASV_3" "ASV_4" "ASV_5" "ASV_6"

And check sample names were also detected

head(sample_names(ASV))
[1] "AE3a103A"  "AE3b103A"  "AE1b900AM" "AE3a103B"  "AE3b103B"  "AE3a198B" 
head(sample_names(META))
[1] "AE3a103A" "AE3b103A" "AE3a198A" "AE3b198A" "AE3a234A" "AE3b234A"

And make the phyloseq object

ps <- phyloseq(ASV, TAX,    META , TREE)

Check some features of the phyloseq object

rank_names(ps)
[1] "Kingdom"    "Supergroup" "Division"   "Class"      "Order"      "Family"     "Genus"      "Species"   
table(tax_table(ps)[, "Supergroup"], exclude = NULL)

     Alveolata      Amoebozoa       Apusozoa Archaeplastida       Excavata       Hacrobia   Opisthokonta 
          8880              9             45            108              9            395            768 
      Rhizaria  Stramenopiles           <NA> 
          2405           1086            471 
unique(tax_table(ps)[, "Supergroup"])
Taxonomy Table:     [10 taxa by 1 taxonomic ranks]:
         Supergroup      
ASV_1    "Alveolata"     
ASV_2    "Rhizaria"      
ASV_6    "Stramenopiles" 
ASV_18   "Opisthokonta"  
ASV_78   "Hacrobia"      
ASV_148  "Archaeplastida"
ASV_193  NA              
ASV_557  "Apusozoa"      
ASV_1114 "Amoebozoa"     
ASV_2665 "Excavata"      

Filter out those ambigious Supergroup annotations- losing 471 ASVs

ps <- subset_taxa(ps, !is.na(Supergroup) & !Supergroup %in% c("", "NA"))
table(tax_table(ps)[, "Supergroup"], exclude = NULL)

     Alveolata      Amoebozoa       Apusozoa Archaeplastida       Excavata       Hacrobia   Opisthokonta 
          8880              9             45            108              9            395            768 
      Rhizaria  Stramenopiles 
          2405           1086 

Check out the Division names

table(tax_table(ps)[, "Division"], exclude = NULL)

        Apicomplexa      Apusomonadidae      Centroheliozoa            Cercozoa         Chlorophyta 
                 29                  26                  40                 246                  64 
   Choanoflagellida          Ciliophora         Cryptophyta      Dinoflagellata             Discoba 
                 54                 407                  50                8330                   1 
       Foraminifera               Fungi          Haptophyta         Hilomonadea Katablepharidophyta 
                  2                  57                 215                  17                   2 
             Lobosa       Mesomycetozoa          Metamonada             Metazoa          Ochrophyta 
                  9                  17                   8                 561                 453 
           Opalozoa      Opisthokonta_X           Perkinsea             Picozoa         Pseudofungi 
                216                  14                   5                  61                  72 
         Radiolaria          Rhodophyta           Sagenista     Stramenopiles_X        Streptophyta 
               2155                   4                 186                  61                  38 
          Telonemia                <NA> 
                 27                 278 

Filter out any with “NA” as Division

ps <- subset_taxa(ps, !is.na(Division) & !Division %in% c(""))
table(tax_table(ps)[, "Division"], exclude = NULL)

        Apicomplexa      Apusomonadidae      Centroheliozoa            Cercozoa         Chlorophyta 
                 29                  26                  40                 246                  64 
   Choanoflagellida          Ciliophora         Cryptophyta      Dinoflagellata             Discoba 
                 54                 407                  50                8330                   1 
       Foraminifera               Fungi          Haptophyta         Hilomonadea Katablepharidophyta 
                  2                  57                 215                  17                   2 
             Lobosa       Mesomycetozoa          Metamonada             Metazoa          Ochrophyta 
                  9                  17                   8                 561                 453 
           Opalozoa      Opisthokonta_X           Perkinsea             Picozoa         Pseudofungi 
                216                  14                   5                  61                  72 
         Radiolaria          Rhodophyta           Sagenista     Stramenopiles_X        Streptophyta 
               2155                   4                 186                  61                  38 
          Telonemia 
                 27 

After the above, 13,427 ASVs remain from the original 14,177

Eliminate the libraries that didn’t have many sequences, AE3a198A, AE3b314A, AE2a200A, AE2b900AN, AE2a200B, AE2a267B, AE2a900BN

taxa_to_keep <- !sample_names(ps) %in% c("AE3a198A","AE3b314A","AE2a200A","AE2b900AN","AE2a200B","AE2a267B","AE2a900BN")
ps <- prune_samples(taxa_to_keep, ps)

41 samples remain and stil 13,427 ASVs

Check rarefaction curve again to make sure those low-sqeuencing-effort samples have been removed

rarecurve(t(otu_table(ps)), step=100, cex=0.5, ylab="ASVs", label=T)

Re-root tree

Have to do this because you may have removed the root of your tree when pruning). (I found this handy function from here which picks the longest branch to root from).

# first define function from link above to find furthest outgroup
pick_new_outgroup <- function(tree.unrooted){
require("magrittr")
require("data.table")
require("ape") # ape::Ntip
# tablify parts of tree that we need.
treeDT <- 
     cbind(
         data.table(tree.unrooted$edge),
         data.table(length = tree.unrooted$edge.length)
     )[1:Ntip(tree.unrooted)] %>% 
 cbind(data.table(id = tree.unrooted$tip.label))
 # Take the longest terminal branch as outgroup
 new.outgroup <- treeDT[which.max(length)]$id
 return(new.outgroup) }

# then run on my phyloseq tree
my.tree <- phy_tree(ps)
out.group <- pick_new_outgroup(my.tree)
Loading required package: magrittr

Attaching package: ‘magrittr’

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

    set_names

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

    extract

Loading required package: data.table
data.table 1.13.4 using 1 threads (see ?getDTthreads).  Latest news: r-datatable.com
**********
This installation of data.table has not detected OpenMP support. It should still work but in single-threaded mode.
This is a Mac. Please read https://mac.r-project.org/openmp/. Please engage with Apple and ask them for support. Check r-datatable.com for updates, and our Mac instructions here: https://github.com/Rdatatable/data.table/wiki/Installation. After several years of many reports of installation problems on Mac, it's time to gingerly point out that there have been no similar problems on Windows or Linux.
**********

Attaching package: ‘data.table’

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

    between, first, last

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

    transpose
out.group
[1] "ASV_10740"
# Then use this outgroup to root the tree
new.tree1 <- ape::root(my.tree, outgroup=out.group, resolve.root=TRUE)


phy_tree(ps) <- new.tree1

# Check if tree is binary (dichotomous not multichotomous)
is.binary.tree(phy_tree(ps))
[1] TRUE
# If false, would have to run
# new.tree2 <- ape::multi2di(new.tree1)
# phy_tree(ps) <- new.tree2
# phy_tree(ps)

Check phyla using bar plots

Check overall how the phyla are distributed among samples. Phyloseq makes this easy

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

# There are many phyla here, so have to make a custom color palette by interpolating from an existing one in RColorBrewer
colourCount = length(table(tax_table(ps)[, "Division"], exclude = NULL))
getPalette = colorRampPalette(brewer.pal(11, "Spectral"))
DivisionPalette = getPalette(colourCount)

# and plot
plot_bar(DivisionGlommed, x = "Sample", fill = "Division") + 
  scale_fill_manual(values = DivisionPalette)

Plot compositional (relative abundances) instead of absolute abundance using microbiome::transform

ps_ra <- microbiome::transform(ps, transform = "compositional")
(otu_table(ps_ra))[1:5,1:5]
OTU Table:          [5 taxa and 5 samples]
                     taxa are rows
          AE3a103A    AE3b103A    AE1b900AM     AE3a103B     AE3b103B
ASV_1 4.046390e-04 0.000105531 2.462054e-05 0.000000e+00 2.400346e-05
ASV_2 0.000000e+00 0.000000000 3.132963e-02 0.000000e+00 5.600807e-05
ASV_3 6.674871e-03 0.014117702 2.265089e-02 3.696079e-03 1.055352e-02
ASV_4 1.244014e-03 0.001524337 1.231027e-05 4.769134e-05 6.720968e-04
ASV_5 2.675299e-05 0.000000000 0.000000e+00 7.948557e-06 1.040150e-04
# Then aglomerate the ASVs at the phylum level using the phyloseq function, tax_glom
DivisionGlommed_RA = tax_glom(ps_ra, "Division")
# and plot
Division_barplot <- plot_bar(DivisionGlommed_RA, x = "Sample", fill = "Division") + 
  scale_fill_manual(values = DivisionPalette) +
  theme(legend.text = element_text(size = 10))
  

Division_barplot

# export
ggsave("Figures/Division_barplot.eps",Division_barplot, width = 15, height = 5, units = c("in"))

Lots of dinoflagellates and radiolaria. Makes sense. But the above is the distribution from all samples. Next make plots that indicate distributions across environmental gradients. Calculate averages and use bubble plots

Calculate averages from replicates

Get average relative abundances from sample replicates


otu_table_mean_ra <- 
  mutate(data.frame(otu_table(ps_ra)), "103A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a103A","AE3b103A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "198A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3b198A")), na.rm = TRUE))  %>% # Sample AE3a198A was removed
  mutate(data.frame(otu_table(ps_ra)), "234A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a234A","AE3b234A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "295A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a295A","AE3b295A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "314A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a314A")), na.rm = TRUE)) %>%  # Sample AE3b314A was removed
  mutate(data.frame(otu_table(ps_ra)), "900AM" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a900AM","AE1b900AM")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "103B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a103B","AE3b103B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "198B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a198B","AE3b198B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "234B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a234B","AE3b234B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "295B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a295B","AE3b295B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "314B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a314B","AE3b314B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "900BM" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a900BM","AE1b900BM")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "143A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a143A","AE2b143A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "200A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2b200A")), na.rm = TRUE)) %>% # AE2a200A was removed
  mutate(data.frame(otu_table(ps_ra)), "237A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a237A","AE2b237A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "247A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a247A","AE2b247A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "267A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a267A","AE2b267A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "900AN" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a900AN")), na.rm = TRUE)) %>% # AE2b900AN was removed
  mutate(data.frame(otu_table(ps_ra)), "143B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a143B","AE2b143B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "200B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2b200B")), na.rm = TRUE)) %>% # AE2a200B was removed
  mutate(data.frame(otu_table(ps_ra)), "237B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a237B","AE2b237B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "247B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a247B","AE2b247B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "267B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2b267B")), na.rm = TRUE)) %>% # AE2a267B was removed
  mutate(data.frame(otu_table(ps_ra)), "900BN" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2b900BN")), na.rm = TRUE)) # AE2a900BN was removed

otu_table_mean_ra <- otu_table_mean_ra[,unique(metadata$Replicate)]

otu_table_mean_ra

Make into new phyloseq object

metadata2 <- unique(select(metadata,!c('Sample Name',Type,colnames(SRARunTable))))
META2 <- sample_data(data.frame(metadata2, row.names = metadata2$Replicate))

ps_ra_mean <- phyloseq(otu_table(otu_table_mean_ra, taxa_are_rows = TRUE), TAX, TREE, META2)
# First aglomerate the ASVs at the phylum level using the phyloseq function, tax_glom
ps_ra_mean_division <- tax_glom(ps_ra_mean, "Division")


# and check by bar plotting
plot_bar(ps_ra_mean_division, x = "Sample", fill = "Division") + 
  scale_fill_manual(values = DivisionPalette)

Abundance Plots

Divisions

Prepare data

Extract mean relative abundance, glommed by division, from the phyloseq object and pair it to taxonomic data

division_df <- data.frame(otu_table(ps_ra_mean_division))
colnames(division_df) <- colnames(otu_table(ps_ra_mean_division))
division_df$ASV <- rownames(division_df)

otu_table_mean_ra <- left_join(division_df, as_tibble(taxonomy, rownames = "ASV"), by = "ASV")
otu_table_mean_ra

Some manual curating for plottin

# Make a new column that has Supergroup-Division in same colum
otu_table_mean_ra$SupergroupDivision <- paste(otu_table_mean_ra$Supergroup, otu_table_mean_ra$Division)

otu_table_mean_ra

Pivot longer

otu_table_mean_ra <- pivot_longer(otu_table_mean_ra, cols = unique(metadata$Replicate), names_to = "Replicate", values_to = "Mean_RA")
otu_table_mean_ra

Join metadata

otu_table_mean_ra <- left_join(otu_table_mean_ra, unique(select(metadata, c("Replicate", "Depth", "SizeFraction", "Season", "OxCond", "Fluorescence", "BeamAtt", "O2", "Temp", "Salinity", "H2S", "ParticulateS", "TZVS", "CH4", "NO3", "NO2", "NH4", "PO4", "Chemoautotrophy", "BNP", "MicroAbun(x10^8 L^-1)", "FlagAbun(x10^5 L-1)", "VLP(x10^8 L-1)"))), by = "Replicate")

# Replace zeroes in RA with NA (better for plotting)
otu_table_mean_ra$Mean_RA[otu_table_mean_ra$Mean_RA == 0] <- NA

otu_table_mean_ra

Bubble Plot of Divisions

# reorder some factors to make them plot in the order I want
otu_table_mean_ra$OxCond <- factor(otu_table_mean_ra$OxCond, levels = c("Oxycline", "ShallowAnoxic", "Euxinic"))
otu_table_mean_ra$SizeFraction <- factor(otu_table_mean_ra$SizeFraction, levels = c("PA", "FL"))

euk_divisions_bubbleplot_color <- ggplot(otu_table_mean_ra,aes (x = as.character(Depth), y = reorder(SupergroupDivision, Mean_RA, function(x){sum(x,na.rm = TRUE)}), color = OxCond)) + 
  geom_point(aes(size =Mean_RA))+
  facet_wrap(Season~SizeFraction, scales = "free_x", drop= TRUE, ncol = 4) +
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.25,.5,.75,1), max_size = 6) +
  xlab("Depth") +
  ylab("") +
  labs(size="Relative Abundance", color = "Redox Condition") +
   scale_color_manual(values = c("blue", "red", "brown4")) +
  theme_bw() +
  theme(axis.text = element_text(size=8),
        axis.text.x = element_text(size=8, angle = 45, hjust = 1),
        axis.title = element_text(size=8),
        legend.title = element_text(size=8),
        legend.text = element_text(size=8),
        strip.text = element_text(size = 8),
        legend.margin=margin(0,0,0,2),
        legend.box.margin=margin(-10,-10,-10,-10),
        plot.margin=grid::unit(c(0,0,0,0), "mm"))
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
euk_divisions_bubbleplot_color

Save figure

# set explicit panel size so they will be consistent for all figures
euk_divisions_bubbleplot_color <- set_panel_size(euk_divisions_bubbleplot_color, width  = unit(22, "mm"), height = unit(100, "mm"))  
Removed 249 rows containing missing values (geom_point).
ggsave(filename = "Figures/euk_divisions_bubbleplot_color.eps", plot = euk_divisions_bubbleplot_color, units = c("mm"), width = 180, height = 125, dpi = 300)

Orders within Alveolata

Prepare data

Filter to only Alveolates; glom by order

keeptaxa <- taxa_names(ps_ra_mean)[(as.data.frame(tax_table(ps_ra_mean))$Supergroup %in% c("Alveolata"))]
ps_ra_mean_alveolates <- prune_taxa(keeptaxa, ps_ra_mean)
ps_ra_mean_alveolate_orders <- tax_glom(ps_ra_mean_alveolates, "Order")

aveloates_df <- data.frame(otu_table(ps_ra_mean_alveolate_orders))
colnames(aveloates_df) <- colnames(otu_table(ps_ra_mean_alveolate_orders))
aveloates_df$ASV <- rownames(aveloates_df)

otu_table_mean_ra <- left_join(aveloates_df, as_tibble(taxonomy, rownames = "ASV"), by = "ASV")
otu_table_mean_ra

Some manual curating for plottin

# Make a new column that has descriptive taxonomy
otu_table_mean_ra$Descriptive <- paste(otu_table_mean_ra$Division, otu_table_mean_ra$Class, otu_table_mean_ra$Order)
otu_table_mean_ra

Pivot longer

otu_table_mean_ra <- pivot_longer(otu_table_mean_ra, cols = unique(metadata$Replicate), names_to = "Replicate", values_to = "Mean_RA")
otu_table_mean_ra

Join metadata

otu_table_mean_ra <- left_join(otu_table_mean_ra, unique(select(metadata, c("Replicate", "Depth", "SizeFraction", "Season", "OxCond", "Fluorescence", "BeamAtt", "O2", "Temp", "Salinity", "H2S", "ParticulateS", "TZVS", "CH4", "NO3", "NO2", "NH4", "PO4", "Chemoautotrophy", "BNP", "MicroAbun(x10^8 L^-1)", "FlagAbun(x10^5 L-1)", "VLP(x10^8 L-1)"))), by = "Replicate")

# Replace zeroes in RA with NA (better for plotting)
otu_table_mean_ra$Mean_RA[otu_table_mean_ra$Mean_RA == 0] <- NA

otu_table_mean_ra

Shorten some labels to make space in plot

otu_table_mean_ra[otu_table_mean_ra == c("Ciliophora Cyclotrichium_like_organism Cyclotrichium_like_organism_X")] <- c("Cilio. Cyclotrichium_like Cyclotrichium_like")

otu_table_mean_ra[otu_table_mean_ra == c("Apicomplexa Gregarinomorphea Gregarines_GRE2")] <- c("Apicom. Gregarinomorphea GRE2")

otu_table_mean_ra[otu_table_mean_ra == c("Apicomplexa Gregarinomorphea Eugregarinorida")] <- c("Apicom. Gregarinomorphea Eugregarinorida")

otu_table_mean_ra[otu_table_mean_ra == c("Apicomplexa Coccidiomorphea Agamococcidiorida")] <- c("Apicom. Coccidiomorphea Agamococcidiorida")

otu_table_mean_ra[otu_table_mean_ra == c("Dinoflagellata Ellobiophyceae Thalassomycetales")] <- c("Dino. Ellobiophyceae Thalassomycetales")

otu_table_mean_ra[otu_table_mean_ra == c("Ciliophora Oligohymenophorea Scuticociliatia_1")] <- c("Cilio. Oligohymenophorea Scuticociliatia_1")

otu_table_mean_ra[otu_table_mean_ra == c("Apicomplexa Apicomplexa_X Apicomplexa_XX")] <- c("Apicom. Apicomplexa_X Apicomplexa_XX")

otu_table_mean_ra

Bubble Plot of Orders within Alveolata

# reorder some factors to make them plot in the order I want
otu_table_mean_ra$OxCond <- factor(otu_table_mean_ra$OxCond, levels = c("Oxycline", "ShallowAnoxic", "Euxinic"))
otu_table_mean_ra$SizeFraction <- factor(otu_table_mean_ra$SizeFraction, levels = c("PA", "FL"))


alveolata_bubbleplot_color <- ggplot(otu_table_mean_ra,aes (x = as.character(Depth), y = reorder(Descriptive, Mean_RA, function(x){sum(x,na.rm = TRUE)}), color = OxCond)) + 
  geom_point(aes(size =Mean_RA))+
  facet_wrap(Season~SizeFraction, scales = "free_x", drop= TRUE, ncol = 4) +
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.25,.5,.75,1), max_size = 6) +
  xlab("Depth") +
  ylab("") +
  labs(size="Relative Abundance", color = "Redox Condition") +
   scale_color_manual(values = c("blue", "red", "brown4")) +
  theme_bw() +
  theme(axis.text = element_text(size=8),
        axis.text.x = element_text(size=8, angle = 45, hjust = 1),
        axis.title = element_text(size=8),
        legend.title = element_text(size=8),
        legend.text = element_text(size=8),
        strip.text = element_text(size = 8),
        legend.margin=margin(0,0,0,2),
        legend.box.margin=margin(-10,-10,-10,-10),
        plot.margin=grid::unit(c(0,0,0,0), "mm"))
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
alveolata_bubbleplot_color

Save figure

# set explicit panel size so they will be consistent for all figures
alveolata_bubbleplot_color <- set_panel_size(alveolata_bubbleplot_color, width  = unit(20, "mm"), height = unit(125, "mm"))  
Removed 724 rows containing missing values (geom_point).
ggsave(filename = "Figures/alveolata_bubbleplot_color.eps", plot = alveolata_bubbleplot_color, units = c("mm"), width = 180, height = 150, dpi = 300)

Orders within Rhizaria

Prepare data

Filter to only Rhizaria; glom by order

keeptaxa <- taxa_names(ps_ra_mean)[(as.data.frame(tax_table(ps_ra_mean))$Supergroup %in% c("Rhizaria"))]
ps_ra_mean_rhizaria <- prune_taxa(keeptaxa, ps_ra_mean)
ps_ra_mean_rhizaria_orders <- tax_glom(ps_ra_mean_rhizaria, "Order")

rhizaria_df <- data.frame(otu_table(ps_ra_mean_rhizaria_orders))
colnames(rhizaria_df) <- colnames(otu_table(ps_ra_mean_rhizaria_orders))
rhizaria_df$ASV <- rownames(rhizaria_df)

otu_table_mean_ra <- left_join(rhizaria_df, as_tibble(taxonomy, rownames = "ASV"), by = "ASV")
otu_table_mean_ra

Some manual curating for plotting

# Make a new column that has descriptive taxonomy
otu_table_mean_ra$Descriptive <- paste(otu_table_mean_ra$Division, otu_table_mean_ra$Class, otu_table_mean_ra$Order)
otu_table_mean_ra

Pivot longer

otu_table_mean_ra <- pivot_longer(otu_table_mean_ra, cols = unique(metadata$Replicate), names_to = "Replicate", values_to = "Mean_RA")
otu_table_mean_ra

Join metadata

otu_table_mean_ra <- left_join(otu_table_mean_ra, unique(select(metadata, c("Replicate", "Depth", "SizeFraction", "Season", "OxCond", "Fluorescence", "BeamAtt", "O2", "Temp", "Salinity", "H2S", "ParticulateS", "TZVS", "CH4", "NO3", "NO2", "NH4", "PO4", "Chemoautotrophy", "BNP", "MicroAbun(x10^8 L^-1)", "FlagAbun(x10^5 L-1)", "VLP(x10^8 L-1)"))), by = "Replicate")

# Replace zeroes in RA with NA (better for plotting)
otu_table_mean_ra$Mean_RA[otu_table_mean_ra$Mean_RA == 0] <- NA

otu_table_mean_ra

Shorten some labels to make space in plot

otu_table_mean_ra[otu_table_mean_ra == c("Radiolaria Acantharea Arthracanthida-Symphyacanthida")] <- c("Radiolaria Acantharea A-S")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Chlorarachniophyceae Chlorarachniophyceae_X")] <- c("Cercozoa Chlor. Chlor._X")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Thecofilosea Filosa-Thecofilosea_X")] <- c("Cercozoa F-T F-T_X")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Granofilosea Filosa-Granofilosea_X")] <- c("Cercozoa F-G. F-G._X")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Chlorarachniophyceae Chlorarachnida")] <- c("Cercozoa Chlor. Chlorarachnida")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Imbricatea Thaumatomonadida")] <- c("Cercozoa F-I Thaumatomonadida")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Imbricatea Filosa-Imbricatea_X")] <- c("Cercozoa F-I F-I_X")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Endomyxa-Phytomyxea Phagomyxida")] <- c("Cercozoa E-P Phagomyxida")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Sarcomonadea Cercomonadida")] <- c("Cercozoa F-S Cercomonadida")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Novel-clade-10-12 Novel-clade-12")] <- c("Cercozoa N-C−10−12 N-C−12")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Thecofilosea Ventricleftida")] <- c("Cercozoa F-T Ventricleftida")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Imbricatea Marimonadida")] <- c("Cercozoa F-I Marimonadida")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Thecofilosea Cryomonadida")] <- c("Cercozoa F-T Cryomonadida")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Sarcomonadea Glissomonadida")] <- c("Cercozoa F-S Glissomonadida")

otu_table_mean_ra[otu_table_mean_ra == c("Cercozoa Filosa-Imbricatea Euglyphida")] <- c("Cercozoa F-I Euglyphida")


otu_table_mean_ra

Bubble Plot of Orders within Rhizaria

# reorder some factors to make them plot in the order I want
otu_table_mean_ra$OxCond <- factor(otu_table_mean_ra$OxCond, levels = c("Oxycline", "ShallowAnoxic", "Euxinic"))
otu_table_mean_ra$SizeFraction <- factor(otu_table_mean_ra$SizeFraction, levels = c("PA", "FL"))

rhizaria_bubbleplot_color <- ggplot(otu_table_mean_ra,aes (x = as.character(Depth), y = reorder(Descriptive, Mean_RA, function(x){sum(x,na.rm = TRUE)}), color = OxCond)) + 
  geom_point(aes(size =Mean_RA))+
  facet_wrap(Season~SizeFraction, scales = "free_x", drop= TRUE, ncol = 4) +
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.25,.5,.75,1), max_size = 6) +
  xlab("Depth") +
  ylab("") +
  labs(size="Relative Abundance", color = "Redox Condition") +
   scale_color_manual(values = c("blue", "red", "brown4")) +
  theme_bw() +
  theme(axis.text = element_text(size=8),
        axis.text.x = element_text(size=8, angle = 45, hjust = 1),
        axis.title = element_text(size=8),
        legend.title = element_text(size=8),
        legend.text = element_text(size=8),
        strip.text = element_text(size = 8),
        legend.margin=margin(0,0,0,2),
        legend.box.margin=margin(-10,-10,-10,-10),
        plot.margin=grid::unit(c(0,0,0,0), "mm"))
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
rhizaria_bubbleplot_color

Save figure

# set explicit panel size so they will be consistent for all figures
rhizaria_bubbleplot_color <- set_panel_size(rhizaria_bubbleplot_color, width  = unit(20, "mm"), height = unit(100, "mm"))  

ggsave(filename = "Figures/rhizaria_bubbleplot_color.eps", plot = rhizaria_bubbleplot_color, units = c("mm"), width = 180, height = 125, dpi = 300)

Orders within Opisthokonta

Prepare data

Filter to only Opisthokonta; glom by order

There were 29 warnings (use warnings() to see them)
keeptaxa <- taxa_names(ps_ra_mean)[(as.data.frame(tax_table(ps_ra_mean))$Supergroup %in% c("Opisthokonta"))]
ps_ra_mean_opisthokonta <- prune_taxa(keeptaxa, ps_ra_mean)
ps_ra_mean_opisthokonta_orders <- tax_glom(ps_ra_mean_opisthokonta, "Order")

opisthokonta_df <- data.frame(otu_table(ps_ra_mean_opisthokonta_orders))
colnames(opisthokonta_df) <- colnames(otu_table(ps_ra_mean_opisthokonta_orders))
opisthokonta_df$ASV <- rownames(opisthokonta_df)

otu_table_mean_ra <- left_join(opisthokonta_df, as_tibble(taxonomy, rownames = "ASV"), by = "ASV")
otu_table_mean_ra

Some manual curating for plottin

# Make a new column that has descriptive taxonomy
otu_table_mean_ra$Descriptive <- paste(otu_table_mean_ra$Division, otu_table_mean_ra$Class, otu_table_mean_ra$Order)
otu_table_mean_ra

Pivot longer

otu_table_mean_ra <- pivot_longer(otu_table_mean_ra, cols = unique(metadata$Replicate), names_to = "Replicate", values_to = "Mean_RA")
otu_table_mean_ra

Join metadata

otu_table_mean_ra <- left_join(otu_table_mean_ra, unique(select(metadata, c("Replicate", "Depth", "SizeFraction", "Season", "OxCond", "Fluorescence", "BeamAtt", "O2", "Temp", "Salinity", "H2S", "ParticulateS", "TZVS", "CH4", "NO3", "NO2", "NH4", "PO4", "Chemoautotrophy", "BNP", "MicroAbun(x10^8 L^-1)", "FlagAbun(x10^5 L-1)", "VLP(x10^8 L-1)"))), by = "Replicate")

# Replace zeroes in RA with NA (better for plotting)
otu_table_mean_ra$Mean_RA[otu_table_mean_ra$Mean_RA == 0] <- NA

otu_table_mean_ra

Shorten some labels to make space in plot

otu_table_mean_ra[otu_table_mean_ra == c("Choanoflagellida Choanoflagellatea Acanthoecida")] <- c("Choanof. Ch. Acanthoecida")

otu_table_mean_ra[otu_table_mean_ra == c("Choanoflagellida Choanoflagellatea Craspedida")] <- c("Choanof. Ch. Craspedida")

otu_table_mean_ra[otu_table_mean_ra == c("Choanoflagellida Choanoflagellatea Choanoflagellatea_X")] <- c("Choanof. Ch. Choanoflagellatea_X")

otu_table_mean_ra[otu_table_mean_ra == c("Choanoflagellida Choanoflagellida_X Choanoflagellida_XX")] <- c("Choanof. Choanof._X Choanof._XX")

otu_table_mean_ra[otu_table_mean_ra == c("Mesomycetozoa Ichthyosporea Ichthyosphonida")] <- c("Mesomy. Ichthyosporea Ichthyosphonida")

otu_table_mean_ra[otu_table_mean_ra == c("Opisthokonta_X Opisthokonta_XX Opisthokonta_XXX")] <- c("Opis._X Opis._XX Opis._XXX")

otu_table_mean_ra

Bubble Plot of Orders within Opisthokonta

# reorder some factors to make them plot in the order I want
otu_table_mean_ra$OxCond <- factor(otu_table_mean_ra$OxCond, levels = c("Oxycline", "ShallowAnoxic", "Euxinic"))
otu_table_mean_ra$SizeFraction <- factor(otu_table_mean_ra$SizeFraction, levels = c("PA", "FL"))

opithokonta_bubbleplot_color <- ggplot(otu_table_mean_ra,aes (x = as.character(Depth), y = reorder(Descriptive, Mean_RA, function(x){sum(x,na.rm = TRUE)}), color = OxCond)) + 
  geom_point(aes(size =Mean_RA))+
  facet_wrap(Season~SizeFraction, scales = "free_x", drop= TRUE, ncol = 4) +
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.1,.2,.3), max_size = 6) +
  xlab("Depth") +
  ylab("") +
  labs(size="Relative Abundance", color = "Redox Condition") +
   scale_color_manual(values = c("blue", "red", "brown4")) +
  theme_bw() +
  theme(axis.text = element_text(size=8),
        axis.text.x = element_text(size=8, angle = 45, hjust = 1),
        axis.title = element_text(size=8),
        legend.title = element_text(size=8),
        legend.text = element_text(size=8),
        strip.text = element_text(size = 8),
        legend.margin=margin(0,0,0,2),
        legend.box.margin=margin(-10,-10,-10,-10),
        plot.margin=grid::unit(c(0,0,0,0), "mm"))
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
opithokonta_bubbleplot_color

Save figure

# set explicit panel size so they will be consistent for all figures
opithokonta_bubbleplot_color <- set_panel_size(opithokonta_bubbleplot_color, width  = unit(20, "mm"), height = unit(100, "mm"))  
Removed 447 rows containing missing values (geom_point).
ggsave(filename = "Figures/opithokonta_bubbleplot_color.eps", plot = opithokonta_bubbleplot_color, units = c("mm"), width = 180, height = 125, dpi = 300)

Classes within Stramenopiles

Prepare data

Filter to only Stramenopiles; glom by class (more meaningful than Order in this case)

There were 29 warnings (use warnings() to see them)
keeptaxa <- taxa_names(ps_ra_mean)[(as.data.frame(tax_table(ps_ra_mean))$Supergroup %in% c("Stramenopiles"))]
ps_ra_mean_stramenopiles <- prune_taxa(keeptaxa, ps_ra_mean)
ps_ra_mean_stramenopiles_classes <- tax_glom(ps_ra_mean_stramenopiles, "Class")

stramenopiles_df <- data.frame(otu_table(ps_ra_mean_stramenopiles_classes))
colnames(stramenopiles_df) <- colnames(otu_table(ps_ra_mean_stramenopiles_classes))
stramenopiles_df$ASV <- rownames(stramenopiles_df)

otu_table_mean_ra <- left_join(stramenopiles_df, as_tibble(taxonomy, rownames = "ASV"), by = "ASV")
otu_table_mean_ra

Some manual curating for plottin

# Make a new column that has descriptive taxonomy
otu_table_mean_ra$Descriptive <- paste(otu_table_mean_ra$Division, otu_table_mean_ra$Class)
otu_table_mean_ra

Pivot longer

otu_table_mean_ra <- pivot_longer(otu_table_mean_ra, cols = unique(metadata$Replicate), names_to = "Replicate", values_to = "Mean_RA")
otu_table_mean_ra

Join metadata

otu_table_mean_ra <- left_join(otu_table_mean_ra, unique(select(metadata, c("Replicate", "Depth", "SizeFraction", "Season", "OxCond", "Fluorescence", "BeamAtt", "O2", "Temp", "Salinity", "H2S", "ParticulateS", "TZVS", "CH4", "NO3", "NO2", "NH4", "PO4", "Chemoautotrophy", "BNP", "MicroAbun(x10^8 L^-1)", "FlagAbun(x10^5 L-1)", "VLP(x10^8 L-1)"))), by = "Replicate")

# Replace zeroes in RA with NA (better for plotting)
otu_table_mean_ra$Mean_RA[otu_table_mean_ra$Mean_RA == 0] <- NA

otu_table_mean_ra

Shorten some labels to make space in plot

otu_table_mean_ra[otu_table_mean_ra == c("Stramenopiles_X Stramenopiles_XX")] <- c("Strameno._X Strameno._XX")

otu_table_mean_ra[otu_table_mean_ra == c("Stramenopiles_X Stramenopiles_X-Group-7")] <- c("Strameno._X Strameno._X−Group−7")


otu_table_mean_ra[otu_table_mean_ra == c("Stramenopiles_X MAST-21")] <- c("Strameno._X MAST−21")


otu_table_mean_ra[otu_table_mean_ra == c("Stramenopiles_X MAST-25")] <- c("Strameno._X MAST-25")


otu_table_mean_ra[otu_table_mean_ra == c("Stramenopiles_X Stramenopiles_X-Group-4")] <- c("Strameno._X Strameno._X−Group−4")


otu_table_mean_ra[otu_table_mean_ra == c("Stramenopiles_X Stramenopiles_X-Group-6")] <- c("Strameno._X Strameno._X−Group−6")


otu_table_mean_ra[otu_table_mean_ra == c("Stramenopiles_X Stramenopiles_X-Group-8")] <- c("Strameno._X Strameno._X−Group−8")


otu_table_mean_ra

Bubble Plot of Orders within Stramenopiles

# reorder some factors to make them plot in the order I want
otu_table_mean_ra$OxCond <- factor(otu_table_mean_ra$OxCond, levels = c("Oxycline", "ShallowAnoxic", "Euxinic"))
otu_table_mean_ra$SizeFraction <- factor(otu_table_mean_ra$SizeFraction, levels = c("PA", "FL"))

stramenopiles_bubbleplot_color <- ggplot(otu_table_mean_ra,aes (x = as.character(Depth), y = reorder(Descriptive, Mean_RA, function(x){sum(x,na.rm = TRUE)}), color = OxCond)) + 
  geom_point(aes(size =Mean_RA))+
  facet_wrap(Season~SizeFraction, scales = "free_x", drop= TRUE, ncol = 4) +
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.1,.2,.3), max_size = 6) +
  xlab("Depth") +
  ylab("") +
  labs(size="Relative Abundance", color = "Redox Condition") +
   scale_color_manual(values = c("blue", "red", "brown4")) +
  theme_bw() +
  theme(axis.text = element_text(size=8),
        axis.text.x = element_text(size=8, angle = 45, hjust = 1),
        axis.title = element_text(size=8),
        legend.title = element_text(size=8),
        legend.text = element_text(size=8),
        strip.text = element_text(size = 8),
        legend.margin=margin(0,0,0,2),
        legend.box.margin=margin(-10,-10,-10,-10),
        plot.margin=grid::unit(c(0,0,0,0), "mm"))
Scale for 'size' is already present. Adding another scale for 'size', which will replace the existing scale.
stramenopiles_bubbleplot_color

Save figure

# set explicit panel size so they will be consistent for all figures
stramenopiles_bubbleplot_color <- set_panel_size(stramenopiles_bubbleplot_color, width  = unit(22, "mm"), height = unit(115, "mm"))  

ggsave(filename = "Figures/stramenopiles_bubbleplot_color.eps", plot = stramenopiles_bubbleplot_color, units = c("mm"), width = 180, height = 150, dpi = 300)

Diversity

Calculate Shannon’s diversity using vegan

shannons <- vegan::diversity(t(otu_table(ps)), index = "shannon")
shannons <- t(shannons)
shannons
     AE3a103A AE3b103A AE1b900AM AE3a103B AE3b103B AE3a198B AE3b198B AE3a234B AE3b234B AE3a295B AE3b295B AE3a314B
[1,] 4.871221 4.956114  2.916447 4.192101 5.048457 5.352167 5.143548 5.169616 4.959116 2.736109  3.53949 2.780448
     AE3b198A AE3b314B AE3a900BM AE1b900BM AE2a143A AE2b143A AE2b200A AE2a237A AE2b237A AE2a247A AE3a234A AE2b247A
[1,] 4.391812 3.143426  3.137984  2.137569 3.083671 4.690686 3.128682 4.191647 4.308389 2.398659 5.334367  2.36533
     AE2a267A AE2b267A AE2a900AN AE2a143B AE2b143B AE2b200B AE2a237B AE3b234A AE2b237B AE2a247B AE2b247B AE2b267B
[1,] 3.826925 3.929226  3.047765 4.962882 3.019449 4.772924 2.413723  4.62931  3.37624 2.595961 2.714695 4.361093
     AE2b900BN AE3a295A AE3b295A AE3a314A AE3a900AM
[1,]  4.492629  3.07776 2.638438 4.522401  3.592396

Average the replicates

shannons_mean <-  
  mutate(data.frame(shannons), "103A" = rowMeans(select(data.frame(shannons), c("AE3a103A","AE3b103A")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "198A" = rowMeans(select(data.frame(shannons), c("AE3b198A")), na.rm = TRUE))  %>% # Sample AE3a198A was removed
  mutate(data.frame(shannons), "234A" = rowMeans(select(data.frame(shannons), c("AE3a234A","AE3b234A")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "295A" = rowMeans(select(data.frame(shannons), c("AE3a295A","AE3b295A")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "314A" = rowMeans(select(data.frame(shannons), c("AE3a314A")), na.rm = TRUE)) %>%  # Sample AE3b314A was removed
  mutate(data.frame(shannons), "900AM" = rowMeans(select(data.frame(shannons), c("AE3a900AM","AE1b900AM")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "103B" = rowMeans(select(data.frame(shannons), c("AE3a103B","AE3b103B")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "198B" = rowMeans(select(data.frame(shannons), c("AE3a198B","AE3b198B")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "234B" = rowMeans(select(data.frame(shannons), c("AE3a234B","AE3b234B")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "295B" = rowMeans(select(data.frame(shannons), c("AE3a295B","AE3b295B")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "314B" = rowMeans(select(data.frame(shannons), c("AE3a314B","AE3b314B")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "900BM" = rowMeans(select(data.frame(shannons), c("AE3a900BM","AE1b900BM")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "143A" = rowMeans(select(data.frame(shannons), c("AE2a143A","AE2b143A")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "200A" = rowMeans(select(data.frame(shannons), c("AE2b200A")), na.rm = TRUE)) %>% # AE2a200A was removed
  mutate(data.frame(shannons), "237A" = rowMeans(select(data.frame(shannons), c("AE2a237A","AE2b237A")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "247A" = rowMeans(select(data.frame(shannons), c("AE2a247A","AE2b247A")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "267A" = rowMeans(select(data.frame(shannons), c("AE2a267A","AE2b267A")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "900AN" = rowMeans(select(data.frame(shannons), c("AE2a900AN")), na.rm = TRUE)) %>% # AE2b900AN was removed
  mutate(data.frame(shannons), "143B" = rowMeans(select(data.frame(shannons), c("AE2a143B","AE2b143B")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "200B" = rowMeans(select(data.frame(shannons), c("AE2b200B")), na.rm = TRUE)) %>% # AE2a200B was removed
  mutate(data.frame(shannons), "237B" = rowMeans(select(data.frame(shannons), c("AE2a237B","AE2b237B")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "247B" = rowMeans(select(data.frame(shannons), c("AE2a247B","AE2b247B")), na.rm = TRUE)) %>%
  mutate(data.frame(shannons), "267B" = rowMeans(select(data.frame(shannons), c("AE2b267B")), na.rm = TRUE)) %>% # AE2a267B was removed
  mutate(data.frame(shannons), "900BN" = rowMeans(select(data.frame(shannons), c("AE2b900BN")), na.rm = TRUE)) # AE2a900BN was removed

shannons_mean <- shannons_mean[,unique(metadata$Replicate)]

shannons_mean

Prepare data for plotting

# Pivot longer
shannons_mean <- pivot_longer(shannons_mean, cols = unique(metadata$Replicate), names_to = "Replicate", values_to = "Shannons")

# Join metadata
shannons_mean <- left_join(shannons_mean, unique(select(metadata, c("Replicate", "Depth", "SizeFraction", "Season", "OxCond", "Fluorescence", "BeamAtt", "O2", "Temp", "Salinity", "H2S", "ParticulateS", "TZVS", "CH4", "NO3", "NO2", "NH4", "PO4", "Chemoautotrophy", "BNP", "MicroAbun(x10^8 L^-1)", "FlagAbun(x10^5 L-1)", "VLP(x10^8 L-1)"))), by = "Replicate")

shannons_mean

Plot

# reorder some factors to make them plot in the order I want
shannons_mean$OxCond <- factor(shannons_mean$OxCond, levels = c("Oxycline", "ShallowAnoxic", "Euxinic"))
shannons_mean$SizeFraction <- factor(shannons_mean$SizeFraction, levels = c("PA", "FL"))

ytitle <- expression(paste("Shannon's Diversity Index (",italic("H'"),")"))

shannonsplot <- ggplot(shannons_mean, aes(x = Depth, y = Shannons, color = OxCond)) +
  geom_line(size=1, color = "black", lty = "dotted") +
  geom_point(size=3, shape = c(16)) +
  labs(y= ytitle, x = "Depth (m)") +
  scale_x_reverse(expand = c(0, 0)) +
  coord_flip(xlim = c(910, 100)) + 
  theme_bw() +
  theme(legend.position = "right",
        axis.text = element_text(size=8),
        axis.text.x = element_text(size=8),
        axis.title = element_text(size=8),
        legend.title = element_text(size=8),
        legend.text = element_text(size=8),
        strip.text = element_text(size = 8),
        legend.margin=margin(0,0,0,2)) +
  facet_wrap(Season~SizeFraction, drop= TRUE, ncol = 4) +
  scale_color_manual(values = c("blue", "red", "brown4")) +
  labs(color = "Redox Condition")

shannonsplot

NA
NA

Export Plot

# set explicit panel size so they will be consistent for all figures
shannonsplot <- set_panel_size(shannonsplot,  width  = unit(22, "mm"), height = unit(60, "mm"))  

ggsave(filename = "Figures/shannonsplot.eps", plot = shannonsplot, units = c("mm"), width = 180, height = 80, dpi = 300)

Ordinations

Filter

McMurdie and Holmes (2013) filter out taxa that were not seen with more than 3 counts in at least 20% of the samples. Also add a pseduocount of 1 to all counts. This is so that later when we do different calculations (log, division, etc) we don’t get back errors due to zeroes

ps_filtered = filter_taxa(ps, function(x) sum(x > 3) > (0.2*length(x)), TRUE) 
ps_filtered <- transform_sample_counts(ps_filtered, function(x) x+1)
# Also make a filtered version of the relative abundance count table (for plotting purposes)
ps_ra_filtered <- prune_taxa(taxa_names(ps_filtered),ps_ra) # prune from ps_ra object (relative abundances)
# check number of ASVs in each
ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 13427 taxa and 41 samples ]
sample_data() Sample Data:       [ 41 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 13427 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 13427 tips and 13426 internal nodes ]
ps_filtered
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 979 taxa and 41 samples ]
sample_data() Sample Data:       [ 41 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 979 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 979 tips and 978 internal nodes ]
ps_ra_filtered
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 979 taxa and 41 samples ]
sample_data() Sample Data:       [ 41 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 979 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 979 tips and 978 internal nodes ]

Reduced from 13,427 to 979 ASVs

Test for compositionality

based on Coenen et al. tutorials for clustering. See repo

# Estimate covariance matrix for OTUs
covariance_matrix <- as.matrix(otu_table(ps_filtered)) %*% t(otu_table(ps_filtered))
# %*% = matrix multiplication sign in R; used here to multiply OTU/ASV data matrix to itself to estimate covariance.
# Evaluate determinant of covariance matrix
cov_determinant <- det(covariance_matrix)
cov_determinant
[1] 0

The determinant of the covariance matrix (what we just calculated) is equivalent to the product of the proportion of variance explained by every PCA axis. If the determinant is 0, that means there is an axis which explains 0 variance that we can’t separate from the other axes. This means the data need to be transformed to be suitable for PCA.

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.

First do a CLR, centered log ratio transformation of the absolute abundance data (after filtering), as suggested by Gloor et al. 2017 and check the determinant of this matrix. Compare it to the determinant without any transformation.

# Estimate covariance matrix for absolute abundance ASV table
covariance_matrix <- as.matrix(otu_table(ps_filtered)) %*% t(otu_table(ps_filtered))

# Evaluate determinant of covariance matrix
cov_determinant <- det(covariance_matrix)

# Estimate covariance matrix for CLR-transformed ASV table
clr_asv_table_ps_filtered <- data.frame(compositions::clr(t(otu_table(ps_filtered))))

## Check new determinant of clr transformed table
new_covdet <- det(as.matrix(clr_asv_table_ps_filtered) %*% t(clr_asv_table_ps_filtered))

# Compare
cov_determinant #Original Count Data
[1] 0
new_covdet # New
[1] 1.939146e+130

The determinant of the CLR-transformed table is not zero, so we can proceed with PCA of the CLR-transformed data.

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_filtered) 
# 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")

First two axes explain a decent proportion of variance: 24.8 + 13.4 = 38.2

Visualize the PCA

# extract PC values
pca_lograt_frame <- data.frame(lograt_pca$x) %>% 
  rownames_to_column(var = "Sample Name")
# Merge metadata into the pca data table
pca_lograt_frame <- left_join(pca_lograt_frame, metadata, by = "Sample Name")


# reorder some factors to make them plot in the order I want
pca_lograt_frame <- pca_lograt_frame %>%
  mutate(SizeFraction = fct_relevel(SizeFraction, "PA", "FL")) %>%
  mutate(OxCond = fct_relevel(OxCond, "Oxycline", "ShallowAnoxic", "Euxinic"))
pca_lograt_frame



# Plot PCA with Redox Regime and Size fraction
pca_lograt_plot <- ggplot(pca_lograt_frame, aes(x = PC1, y = PC2, color = OxCond)) +
  geom_point(aes(shape = SizeFraction), size = 4) +
  ylab(paste0('PC2 ', round(lograt_variances[2,2]*100,2),'%')) + #Extract y axis value from variance
  xlab(paste0('PC1 ', round(lograt_variances[1,2]*100,2),'%')) + #Extract x axis value from variance
  ggtitle('CLR-Euclidean PCA') +
  scale_color_manual(values = c("blue", "red", "brown4")) +
  coord_fixed(ratio = 1) +
  theme_bw()

pca_lograt_plot

EnvFit

Use vegan’s envfit to determine relationships between the ordination and environmental variables

# make metadata_ordinations, trimmed from metadata to only samples that are in PCA 
metadata_ordinations <- metadata[metadata$`Sample Name` %in% sample_data(ps_filtered)$Sample.Name,]

# reorder some factors in metadata_ordinations to make them plot in the order I want
metadata_ordinations <- metadata_ordinations %>%
  mutate(SizeFraction = fct_relevel(SizeFraction, "PA", "FL")) %>%
  mutate(OxCond = fct_relevel(OxCond, "Oxycline", "ShallowAnoxic", "Euxinic"))

# sort clr_asv_table_ps_filtered in same order as metadata 
clr_asv_table_ps_filtered <- clr_asv_table_ps_filtered[metadata_ordinations$"Sample Name",]

# re-run the PCA on clr_asv_table_ps_filtered 
lograt_pca <- prcomp(clr_asv_table_ps_filtered) 


# remove metadata that don't make sense to test (eg. NCBI sample IDs, etc.),  repetitive variables (eg. Particulate S and TZVS), and those that didn't work on both cruises (like fluorescence, beam attenuation, etc)
metadata_ordinations <- select(metadata_ordinations, -Replicate, -Fluorescence, -BeamAtt, -TZVS, -Run, -"Assay Type", -AvgSpotLen, -Bases, -BioProject, -BioSample, -BioSampleModel, -Bytes, -"Center Name", -Collection_Date, -Consent, -"DATASTORE filetype", -"DATASTORE provider", -"DATASTORE region", -Experiment, -geo_loc_name_country, -geo_loc_name_country_continent, -geo_loc_name, -Instrument, -isolation_source, -lat_lon, -"Library Name", -LibraryLayout, -LibrarySelection, -LibrarySource, -Organism, -Platform, -ReleaseDate, -samp_collect_device, -"SRA Study", -Depth_m, -replicate, -size_fraction, -CH4_uM, -H2S_Um, -oxygen, -Oxygen_uM, -Particulate_Sulfur_uM, -salinity, -Temperature_degree_C, -TZVS_uM)

# change the name of some variables to make them easier to plot
metadata_ordinations <- rename(metadata_ordinations, PartS = ParticulateS, MicroAbun = "MicroAbun(x10^8 L^-1)",  FlagAbun = "FlagAbun(x10^5 L-1)", VLPAbun = "VLP(x10^8 L-1)", Chemo = "Chemoautotrophy")


# fit environmental factors and save stats output
set.seed(10010)
pca_envfit <- envfit(lograt_pca, metadata_ordinations, permutations = 1000)
capture.output(pca_envfit, file = "stats_results/PCA_envfit_stat.txt")
pca_envfit

***VECTORS

               PC1      PC2     r2   Pr(>r)    
Depth     -0.98577 -0.16812 0.1464 0.053946 .  
O2         0.38556  0.92268 0.6792 0.000999 ***
Temp       0.50233  0.86468 0.4698 0.000999 ***
Salinity   0.38721  0.92199 0.4349 0.000999 ***
H2S       -0.93149  0.36378 0.0622 0.284715    
PartS     -0.96687 -0.25527 0.2378 0.008991 ** 
CH4       -0.92338  0.38389 0.1143 0.099900 .  
NO3        0.87658  0.48127 0.7438 0.000999 ***
NO2        0.65514 -0.75551 0.0489 0.378621    
NH4       -0.92121  0.38907 0.0956 0.140859    
PO4       -0.89466 -0.44674 0.3551 0.001998 ** 
Chemo     -0.98789 -0.15514 0.2174 0.009990 ** 
BNP       -0.50518  0.86301 0.1857 0.021978 *  
MicroAbun -0.38662  0.92224 0.1379 0.064935 .  
FlagAbun  -0.88594 -0.46381 0.2942 0.001998 ** 
VLPAbun   -0.34508 -0.93857 0.2224 0.011988 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Permutation: free
Number of permutations: 1000

***FACTORS:

Centroids:
                          PC1      PC2
Sample.NameAE1b900AM -29.8546   9.9007
Sample.NameAE1b900BM -10.2525   1.2934
Sample.NameAE2a143A    3.5890  13.6286
Sample.NameAE2a143B   44.7696 -15.1082
Sample.NameAE2a237A  -13.4858 -29.6025
Sample.NameAE2a237B   13.6807 -42.2385
Sample.NameAE2a247A  -26.4642 -14.7376
Sample.NameAE2a247B  -13.9878  -7.3599
Sample.NameAE2a267A  -22.8582  -4.8656
Sample.NameAE2a900AN -14.7861   1.3805
Sample.NameAE2b143A    5.5093  23.1323
Sample.NameAE2b143B   14.9987  11.5463
Sample.NameAE2b200A   15.0768   2.4830
Sample.NameAE2b200B   35.1975 -24.0981
Sample.NameAE2b237A  -12.4369 -25.4440
Sample.NameAE2b237B   12.4311 -32.6782
Sample.NameAE2b247A  -18.1375 -14.0838
Sample.NameAE2b247B  -17.2418 -20.6486
Sample.NameAE2b267A  -27.1824   0.6048
Sample.NameAE2b267B  -22.0115  -7.5764
Sample.NameAE2b900BN  -0.1983  -5.0092
Sample.NameAE3a103A   -0.6935  58.4628
Sample.NameAE3a103B   24.8367  33.9070
Sample.NameAE3a198B   56.1304   4.2180
Sample.NameAE3a234A   20.4610   4.2756
Sample.NameAE3a234B   58.9877 -22.5333
Sample.NameAE3a295A  -44.5164  -0.6909
Sample.NameAE3a295B  -16.6237  -3.2697
Sample.NameAE3a314A  -41.6693   9.1064
Sample.NameAE3a314B  -21.9304  -2.6987
Sample.NameAE3a900AM -31.7213  18.2451
Sample.NameAE3a900BM -20.0520   3.1981
Sample.NameAE3b103A    3.4583  51.4401
Sample.NameAE3b103B   24.9704  35.9596
Sample.NameAE3b198A   30.4747  23.7773
Sample.NameAE3b198B   56.7512  -3.2900
Sample.NameAE3b234A   21.6594  11.7876
Sample.NameAE3b234B   52.9667 -20.3142
Sample.NameAE3b295A  -28.3927  -6.7477
Sample.NameAE3b295B  -29.9989 -12.2357
Sample.NameAE3b314B  -31.4534  -3.1165
TypeAnoxMay1FL       -21.9304  -2.6987
TypeAnoxMay1PA       -41.6693   9.1064
TypeAnoxMay2FL       -31.4534  -3.1165
TypeAnoxNov1PA       -22.8582  -4.8656
TypeAnoxNov2FL       -22.0115  -7.5764
TypeAnoxNov2PA       -27.1824   0.6048
TypeDeepMay1FL       -20.0520   3.1981
TypeDeepMay1PA       -31.7213  18.2451
TypeDeepMay2FL       -10.2525   1.2934
TypeDeepMay2PA       -29.8546   9.9007
TypeDeepNov1PA       -14.7861   1.3805
TypeDeepNov2FL        -0.1983  -5.0092
TypeIntMay1FL        -16.6237  -3.2697
TypeIntMay1PA        -44.5164  -0.6909
TypeIntMay2FL        -29.9989 -12.2357
TypeIntMay2PA        -28.3927  -6.7477
TypeIntNov1FL        -13.9878  -7.3599
TypeIntNov1PA        -26.4642 -14.7376
TypeIntNov2FL        -17.2418 -20.6486
TypeIntNov2PA        -18.1375 -14.0838
TypeMicroOxMay1FL     56.1304   4.2180
TypeMicroOxMay2FL     56.7512  -3.2900
TypeMicroOxMay2PA     30.4747  23.7773
TypeMicroOxNov2FL     35.1975 -24.0981
TypeMicroOxNov2PA     15.0768   2.4830
TypeOxicMay1FL        24.8367  33.9070
TypeOxicMay1PA        -0.6935  58.4628
TypeOxicMay2FL        24.9704  35.9596
TypeOxicMay2PA         3.4583  51.4401
TypeOxicNov1FL        44.7696 -15.1082
TypeOxicNov1PA         3.5890  13.6286
TypeOxicNov2FL        14.9987  11.5463
TypeOxicNov2PA         5.5093  23.1323
TypeSubOxMay1FL       58.9877 -22.5333
TypeSubOxMay1PA       20.4610   4.2756
TypeSubOxMay2FL       52.9667 -20.3142
TypeSubOxMay2PA       21.6594  11.7876
TypeSubOxNov1FL       13.6807 -42.2385
TypeSubOxNov1PA      -13.4858 -29.6025
TypeSubOxNov2FL       12.4311 -32.6782
TypeSubOxNov2PA      -12.4369 -25.4440
SizeFractionPA       -10.5985   6.6026
SizeFractionFL        10.0938  -6.2882
SeasonMay              1.9790   8.6671
SeasonNov             -2.2915 -10.0355
OxCondOxycline        22.3492   2.8243
OxCondShallowAnoxic  -25.8906  -6.3086
OxCondEuxinic        -17.8108   4.8348

Goodness of fit:
                 r2   Pr(>r)    
Sample.Name  1.0000 1.000000    
Type         1.0000 1.000000    
SizeFraction 0.1190 0.008991 ** 
Season       0.0733 0.044955 *  
OxCond       0.4424 0.000999 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Permutation: free
Number of permutations: 1000
# significant vector variables, at the p<0.01 level: O2, Temp, Salinity, Particulate S, NO3, PO4, Chemoautotrophy, Flagellate Abundance
# significant centroid variables at the p<0.01 level: OxCond and SizeFraction

# fit species and save stats output
pca_sppfit <- envfit(lograt_pca, clr_asv_table_ps_filtered, permutations = 1000)
capture.output(pca_sppfit, file = "stats_results/PCA_sppfit_stat.txt")
pca_sppfit

***VECTORS

              PC1      PC2     r2   Pr(>r)    
ASV_1    -0.94879 -0.31590 0.6367 0.000999 ***
ASV_2    -0.95743 -0.28866 0.5478 0.000999 ***
ASV_3    -0.66103 -0.75036 0.4248 0.000999 ***
ASV_4     0.91119  0.41198 0.3182 0.000999 ***
ASV_5    -0.13944 -0.99023 0.5837 0.000999 ***
ASV_6    -0.72934  0.68415 0.3824 0.000999 ***
ASV_7    -0.89988 -0.43614 0.5795 0.000999 ***
ASV_8    -0.99973 -0.02323 0.3232 0.000999 ***
ASV_9     0.79352  0.60855 0.3634 0.000999 ***
ASV_10    0.35944 -0.93317 0.0182 0.693307    
ASV_11    0.81570  0.57847 0.3390 0.000999 ***
ASV_12   -0.99954 -0.03042 0.2399 0.011988 *  
ASV_13   -0.35551  0.93467 0.2923 0.000999 ***
ASV_14   -0.90892 -0.41696 0.5135 0.000999 ***
ASV_15   -0.54155 -0.84067 0.1551 0.038961 *  
ASV_16   -0.12825 -0.99174 0.5570 0.000999 ***
ASV_17    0.85455  0.51936 0.4011 0.000999 ***
ASV_18   -0.93204  0.36236 0.4675 0.000999 ***
ASV_19    0.76290  0.64652 0.4054 0.000999 ***
ASV_20    0.74332  0.66893 0.3693 0.000999 ***
ASV_21    0.94112  0.33808 0.7068 0.000999 ***
ASV_22   -0.03575  0.99936 0.6102 0.000999 ***
ASV_23   -0.96906  0.24683 0.3277 0.001998 ** 
ASV_24    0.75409  0.65678 0.4173 0.000999 ***
ASV_25   -0.70979 -0.70441 0.1603 0.030969 *  
ASV_26    0.77164  0.63606 0.3377 0.001998 ** 
ASV_27   -0.72076 -0.69319 0.1532 0.048951 *  
ASV_28   -0.91095 -0.41251 0.3518 0.001998 ** 
ASV_29   -0.89941 -0.43710 0.5228 0.000999 ***
ASV_30    0.14112 -0.98999 0.6551 0.000999 ***
ASV_31    0.74503  0.66703 0.5581 0.000999 ***
ASV_32    0.61333  0.78983 0.0922 0.149850    
ASV_33    0.96796  0.25110 0.5839 0.000999 ***
ASV_34    0.48068  0.87690 0.1288 0.070929 .  
ASV_35   -0.96144 -0.27501 0.4856 0.000999 ***
ASV_36    0.17479 -0.98461 0.1455 0.050949 .  
ASV_37   -0.57080 -0.82109 0.4490 0.000999 ***
ASV_38   -0.55821 -0.82970 0.3634 0.000999 ***
ASV_39    0.89786 -0.44029 0.7727 0.000999 ***
ASV_40    0.54765  0.83671 0.2576 0.000999 ***
ASV_41    0.85857  0.51269 0.4075 0.000999 ***
ASV_42   -0.03359  0.99944 0.6764 0.000999 ***
ASV_43    0.79848  0.60202 0.7293 0.000999 ***
ASV_44    0.91567 -0.40193 0.6031 0.000999 ***
ASV_45    0.58674 -0.80978 0.6323 0.000999 ***
ASV_47    0.76868  0.63964 0.4841 0.000999 ***
ASV_48   -0.01144  0.99993 0.0110 0.807193    
ASV_49   -0.28654  0.95807 0.3425 0.001998 ** 
ASV_50   -0.95827 -0.28585 0.4911 0.000999 ***
ASV_51   -0.15109  0.98852 0.6496 0.000999 ***
ASV_52   -0.42255 -0.90634 0.4405 0.000999 ***
ASV_53   -0.85901 -0.51196 0.4958 0.000999 ***
ASV_54    0.99845  0.05559 0.5791 0.000999 ***
ASV_55    0.08476  0.99640 0.4400 0.000999 ***
ASV_56    0.18670  0.98242 0.4767 0.000999 ***
ASV_57    0.87367 -0.48653 0.6257 0.000999 ***
ASV_58   -0.33160 -0.94342 0.4368 0.000999 ***
ASV_59    0.87362 -0.48660 0.0266 0.581419    
ASV_61    0.99530 -0.09684 0.8206 0.000999 ***
ASV_62    0.67265  0.73996 0.3537 0.000999 ***
ASV_63   -0.95850 -0.28510 0.3152 0.000999 ***
ASV_64    0.65845 -0.75262 0.8012 0.000999 ***
ASV_65   -0.99096  0.13417 0.4403 0.000999 ***
ASV_66    0.98308  0.18319 0.3301 0.001998 ** 
ASV_67    0.99228  0.12403 0.5932 0.000999 ***
ASV_68   -0.07256  0.99736 0.6304 0.000999 ***
ASV_69    0.92668  0.37584 0.6110 0.000999 ***
ASV_70    0.75387 -0.65702 0.3063 0.000999 ***
ASV_71    0.70881 -0.70539 0.1778 0.019980 *  
ASV_72    0.62361 -0.78173 0.2473 0.003996 ** 
ASV_73    0.86964  0.49369 0.7019 0.000999 ***
ASV_75   -0.97001  0.24308 0.4272 0.000999 ***
ASV_76    0.94362 -0.33102 0.5229 0.000999 ***
ASV_77   -0.77761  0.62875 0.2168 0.008991 ** 
ASV_78   -0.92119 -0.38910 0.5809 0.000999 ***
ASV_79   -0.99411 -0.10833 0.5673 0.000999 ***
ASV_80    0.46163  0.88707 0.5916 0.000999 ***
ASV_81   -0.97935 -0.20218 0.3814 0.000999 ***
ASV_82    0.75332 -0.65765 0.7328 0.000999 ***
ASV_83    0.91717  0.39849 0.7793 0.000999 ***
ASV_84   -0.92403 -0.38233 0.3370 0.000999 ***
ASV_85    0.25193  0.96774 0.2175 0.007992 ** 
ASV_86    0.94191 -0.33587 0.6329 0.000999 ***
ASV_87    0.58716 -0.80947 0.5471 0.000999 ***
ASV_88    0.48617  0.87386 0.4838 0.000999 ***
ASV_89   -0.70961 -0.70460 0.1628 0.031968 *  
ASV_90    0.94964 -0.31335 0.6680 0.000999 ***
ASV_91   -0.15797 -0.98744 0.4723 0.000999 ***
ASV_92    0.83089 -0.55643 0.1673 0.019980 *  
ASV_93    0.15178  0.98841 0.2552 0.003996 ** 
ASV_94    0.36042 -0.93279 0.6799 0.000999 ***
ASV_95   -0.98471 -0.17421 0.4095 0.000999 ***
ASV_96    0.08413  0.99645 0.3041 0.001998 ** 
ASV_97    0.98829  0.15262 0.7834 0.000999 ***
ASV_99    0.81168  0.58410 0.4439 0.000999 ***
ASV_100  -0.97026 -0.24205 0.4647 0.000999 ***
ASV_101   0.57438 -0.81859 0.7301 0.000999 ***
ASV_102   0.69424  0.71974 0.6313 0.000999 ***
ASV_103  -0.90516  0.42508 0.5250 0.000999 ***
ASV_104  -0.49163 -0.87080 0.5500 0.000999 ***
ASV_105  -0.96963 -0.24457 0.2440 0.004995 ** 
ASV_106  -0.04813  0.99884 0.0125 0.783217    
ASV_107   0.99655  0.08299 0.8209 0.000999 ***
ASV_108   0.90255  0.43059 0.3793 0.000999 ***
ASV_109   0.18840 -0.98209 0.5633 0.000999 ***
ASV_110  -0.99856  0.05364 0.3955 0.000999 ***
ASV_111  -0.79184 -0.61073 0.3650 0.000999 ***
ASV_113   0.78091  0.62464 0.4847 0.000999 ***
ASV_114   0.73484  0.67824 0.4835 0.000999 ***
ASV_115   0.09745 -0.99524 0.6475 0.000999 ***
ASV_116   0.93013  0.36723 0.7964 0.000999 ***
ASV_117   0.42481  0.90528 0.1028 0.111888    
ASV_118   0.99069 -0.13612 0.1310 0.068931 .  
ASV_119  -0.33032 -0.94387 0.4257 0.000999 ***
ASV_120  -0.75767  0.65263 0.1544 0.046953 *  
ASV_122   0.98160  0.19096 0.8455 0.000999 ***
ASV_123  -0.02325  0.99973 0.1943 0.017982 *  
ASV_125   0.42248 -0.90637 0.1769 0.030969 *  
ASV_126  -0.48526 -0.87437 0.3396 0.000999 ***
ASV_127  -0.72493  0.68883 0.5072 0.000999 ***
ASV_129   0.85586 -0.51720 0.4291 0.000999 ***
ASV_130   0.67202 -0.74053 0.6871 0.000999 ***
ASV_131  -0.57484 -0.81826 0.5689 0.000999 ***
ASV_132  -0.93034 -0.36669 0.5766 0.000999 ***
ASV_133  -0.77173  0.63595 0.4962 0.000999 ***
ASV_134   0.98676  0.16221 0.2293 0.008991 ** 
ASV_135   0.65189 -0.75831 0.8054 0.000999 ***
ASV_136   0.34613  0.93819 0.3783 0.000999 ***
ASV_137  -0.98260  0.18572 0.5150 0.000999 ***
ASV_138   0.91768  0.39731 0.7674 0.000999 ***
ASV_139  -0.15862  0.98734 0.1217 0.084915 .  
ASV_140   0.90861 -0.41765 0.5751 0.000999 ***
ASV_141   0.57542 -0.81786 0.1002 0.137862    
ASV_142   0.79995 -0.60006 0.8416 0.000999 ***
ASV_143   0.79364 -0.60839 0.6547 0.000999 ***
ASV_144   0.45676 -0.88959 0.3956 0.000999 ***
ASV_145   0.53528  0.84468 0.4380 0.000999 ***
ASV_146   0.68811 -0.72560 0.7750 0.000999 ***
ASV_147   0.36469 -0.93113 0.6131 0.000999 ***
ASV_148  -0.90620 -0.42284 0.1703 0.038961 *  
ASV_149  -0.13172  0.99129 0.4334 0.000999 ***
ASV_150  -0.94312  0.33246 0.4157 0.000999 ***
ASV_151  -0.20501  0.97876 0.5520 0.000999 ***
ASV_152   0.95131 -0.30823 0.0038 0.938062    
ASV_153   0.41155  0.91139 0.3053 0.000999 ***
ASV_154  -0.98120 -0.19299 0.4128 0.000999 ***
ASV_155   0.99400 -0.10938 0.1196 0.089910 .  
ASV_156  -0.03421  0.99941 0.5686 0.000999 ***
ASV_157   0.72295 -0.69090 0.6207 0.000999 ***
ASV_158  -0.53908 -0.84225 0.5685 0.000999 ***
ASV_159  -0.77708  0.62940 0.4091 0.000999 ***
ASV_161   0.67556 -0.73731 0.8036 0.000999 ***
ASV_162   0.80096 -0.59872 0.6939 0.000999 ***
ASV_163  -0.89141  0.45320 0.4723 0.000999 ***
ASV_164  -0.35116  0.93631 0.1788 0.016983 *  
ASV_165  -0.60354 -0.79733 0.6197 0.000999 ***
ASV_167   0.90755 -0.41994 0.2492 0.002997 ** 
ASV_168   0.94770  0.31917 0.6323 0.000999 ***
ASV_169   0.96934 -0.24570 0.4338 0.000999 ***
ASV_170   0.98435  0.17620 0.2656 0.004995 ** 
ASV_171  -0.82003  0.57232 0.4193 0.000999 ***
ASV_172  -0.96491  0.26258 0.2857 0.001998 ** 
ASV_173   0.92938 -0.36913 0.5319 0.000999 ***
ASV_174   0.48785  0.87293 0.1821 0.023976 *  
ASV_175   0.87786  0.47891 0.1631 0.022977 *  
ASV_176   0.39528  0.91856 0.1710 0.032967 *  
ASV_177   0.82178 -0.56981 0.3760 0.001998 ** 
ASV_178   0.82885 -0.55948 0.1752 0.022977 *  
ASV_179   0.66025 -0.75105 0.7251 0.000999 ***
ASV_180  -0.49284 -0.87012 0.4924 0.000999 ***
ASV_181  -0.96710 -0.25439 0.4136 0.000999 ***
ASV_183  -0.56267 -0.82668 0.5489 0.000999 ***
ASV_184   0.02021  0.99980 0.3986 0.000999 ***
ASV_185   0.40544  0.91412 0.5613 0.000999 ***
ASV_186   0.80750 -0.58986 0.6950 0.000999 ***
ASV_187  -0.99940  0.03464 0.3257 0.001998 ** 
ASV_188  -0.90566  0.42400 0.3680 0.001998 ** 
ASV_189  -0.42612 -0.90467 0.4463 0.000999 ***
ASV_190   0.83403 -0.55172 0.2928 0.000999 ***
ASV_191  -0.53648  0.84391 0.0279 0.601399    
ASV_192   0.99195 -0.12663 0.8316 0.000999 ***
ASV_194  -0.77532  0.63157 0.3241 0.001998 ** 
ASV_195   0.05248  0.99862 0.0819 0.188811    
ASV_197   0.97344 -0.22895 0.7982 0.000999 ***
ASV_198   0.09697  0.99529 0.2453 0.006993 ** 
ASV_199  -0.90727  0.42056 0.4735 0.000999 ***
ASV_200  -0.57495  0.81819 0.2380 0.009990 ** 
ASV_201   0.27134  0.96248 0.2029 0.023976 *  
ASV_202   0.82275 -0.56841 0.5077 0.000999 ***
ASV_203  -0.79991  0.60012 0.1394 0.055944 .  
ASV_204  -0.49194 -0.87063 0.2146 0.008991 ** 
ASV_205   0.87207 -0.48938 0.5432 0.000999 ***
ASV_206  -0.30909  0.95103 0.3950 0.000999 ***
ASV_207  -0.98314 -0.18286 0.1984 0.011988 *  
ASV_208  -0.76094  0.64882 0.4574 0.000999 ***
ASV_210  -0.98383  0.17912 0.5124 0.000999 ***
ASV_211   0.80921  0.58752 0.2800 0.000999 ***
ASV_212  -0.01332  0.99991 0.0049 0.904096    
ASV_213  -0.70411 -0.71009 0.3093 0.001998 ** 
ASV_214  -0.51767 -0.85558 0.5341 0.000999 ***
 [ reached getOption("max.print") -- omitted 779 rows ]
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Permutation: free
Number of permutations: 1000

Many of the typical variables that indicate redox condition are significant (O2, NO3, Particulate S,etc), plus size fraction. There are many species that are sig

Make individual envfit objects for all the vectors that will be plotted

# vectors
pca_envfit_O2 <- envfit(lograt_pca~O2, metadata_ordinations, permutations = 1000)
pca_envfit_partS <- envfit(lograt_pca~PartS, metadata_ordinations, permutations = 1000)
pca_envfit_NO3 <- envfit(lograt_pca~NO3, metadata_ordinations, permutations = 1000)
pca_envfit_PO4 <- envfit(lograt_pca~PO4, metadata_ordinations, permutations = 1000)
pca_envfit_temp <- envfit(lograt_pca~Temp, metadata_ordinations, permutations = 1000)
pca_envfit_sal <- envfit(lograt_pca~Salinity, metadata_ordinations, permutations = 1000)
pca_envfit_chemo <- envfit(lograt_pca~Chemo, metadata_ordinations, permutations = 1000)
pca_envfit_FlagAbun <- envfit(lograt_pca~FlagAbun, metadata_ordinations, permutations = 1000)

Next, trim the sppfit vegan object to just include those species with r2 value greater than 0.60 I got this function from here. Later, when plotting, I can also trim by p-value/

#__FUNCTION: select.envfit__#
# function (select.envit) filters the resulting list of function (envfit) based on their p values. This allows to display only significant values in the final plot.
# just run this
select.envfit<-function(fit, r.select){ #needs two sorts of input: fit= result of envfit, r.select= numeric, correlation minimum threshold
for (i in 1:length(fit$vectors$r)) { #run for-loop through the entire length of the column r in object fit$vectors$r starting at i=1
if (fit$vectors$r[i]<r.select) { #Check wether r<r.select, i.e. if the correlation is weaker than the threshold value. Change this Parameter for r-based selection
fit$vectors$arrows[i,]=NA #If the above statement is TRUE, i.e. r is smaller than r.select, then the coordinates of the vectors are set to NA, so they cannot be displayed
i=i+1 #increase the running parameter i from 1 to 2, i.e. check the next value in the column until every value has been checked
} #close if-loop
} #close for-loop
return(fit) #return fit as the result of the function
} #close the function

pca_sppfit_trim<-select.envfit(pca_sppfit, 0.6) 

Plot

Complicated to plot vegan output in ggplot. Plot in base R

# 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(OxCond))
[1] "Oxycline"      "ShallowAnoxic" "Euxinic"      
with(as.data.frame(metadata_ordinations), levels(SizeFraction))
[1] "PA" "FL"
# Define colors and shapes for plot 
colvec <- c("blue", "red", "brown4")
shapevec <- c(16,17)


# Plot here in notebook
# Set up 2x2 panels
op <- par(oma=c(0,0,0,1),# Room for the title and legend
  mfrow=c(2,2),
  mai=c(.65,.65,.1,0))
# Panel 1- Add first half of envfit vectors
with(metadata_ordinations, plot(scores(lograt_pca, display = "sites"), col = colvec[OxCond], pch = shapevec[SizeFraction], cex = 1.5, cex.lab = .8, cex.axis = .8, xlab = "", ylab = paste0('PC2 ', round(lograt_variances[2,2]*100,2),'%'), xaxt='n', xlim=c(-60,60)))
plot(pca_envfit_O2, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_partS, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_NO3, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_PO4, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
title("A", line = -1, adj = 0.02)
# Panel 2- Add rest of envfit vectors
with(metadata_ordinations, plot(scores(lograt_pca, display = "sites"), col = colvec[OxCond], pch = shapevec[SizeFraction], cex = 1.5, cex.lab = .8, cex.axis = .8, xlab = "", ylab = "", xaxt='n', yaxt='n', xlim=c(-60,60)))
plot(pca_envfit_temp, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_sal, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_FlagAbun, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_chemo, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
title("B", line = -1, adj = 0.02)
# Panel 3- Add spider lines indicating envfit centroids for Size Fraction
with(metadata_ordinations, plot(scores(lograt_pca, display = "sites"), col = colvec[OxCond], pch = shapevec[SizeFraction], cex = 1.5, cex.lab = .8, cex.axis = .8, xlab = paste0('PC1 ', round(lograt_variances[1,2]*100,2),'%'), ylab = paste0('PC2 ', round(lograt_variances[2,2]*100,2),'%'), xlim=c(-60,60)))
with(metadata_ordinations, ordispider(lograt_pca, SizeFraction, lwd = 1.5, lty = c(1,2), label = TRUE, cex = 0.6))
title("C", line = -1, adj = 0.02)
# Panel 4 -Add vectors indicating significant spp
with(metadata_ordinations, plot(scores(lograt_pca, display = "sites"), col = colvec[OxCond], pch = shapevec[SizeFraction], cex = 1.5, cex.lab = .8, cex.axis = .8, xlab = paste0('PC1 ', round(lograt_variances[1,2]*100,2),'%'), ylab = "", yaxt='n', xlim=c(-60,60)))
plot(pca_sppfit_trim, p.max = 0.001, col = "black", cex = 0.6)
# annotate the 3 clusters of ASVs in panel D
text(x=c(0), y=c(45), labels=c("Cluster I"), adj = 0.5, font = 2, cex = 0.8)
text(x=c(50), y=c(30), labels=c("Cluster II"), adj = 0.5, font = 2, cex = 0.8)
text(x=c(48), y=c(-35), labels=c("Cluster III"), adj = 0.5, font = 2, cex = 0.8)
text(x=c(-48), y=c(-18), labels=c("Cluster IV"), adj = 0.5, font = 2, cex = 0.8)
text(x=c(-48), y=c(10), labels=c("Cluster V"), adj = 0.5, font = 2, cex = 0.8)
title("D", line = -1, adj = 0.02)
# Add legend
par(op) # Leave the last plot
op <- par(usr=c(0,1,0,1), # Reset the coordinates
          xpd=NA)         # Allow plotting outside the plot region
legend(-0.018,.57, c("PA", "FL", "Oxycline", "Shallow Anoxic", "Euxinic"), col=c("black", "black","blue", "red", "brown4"), pch = c(16, 17, 15, 15, 15), box.col=NA, cex = .8, horiz = T, x.intersp = c(0.3), text.width = c(0, 0.18, 0.18, 0.18, 0.2))


# Set up EPS and make plot
setEPS(width = 6, height = 6)
postscript("Figures/PCA_envfit.eps")
# Set up 2x2 panels
op <- par(oma=c(0,0,0,1),# Room for the title and legend
  mfrow=c(2,2),
  mai=c(.65,.65,.1,0))
# Panel 1- Add first half of envfit vectors
with(metadata_ordinations, plot(scores(lograt_pca, display = "sites"), col = colvec[OxCond], pch = shapevec[SizeFraction], cex = 1.5, cex.lab = .8, cex.axis = .8, xlab = "", ylab = paste0('PC2 ', round(lograt_variances[2,2]*100,2),'%'), xaxt='n', xlim=c(-60,60)))
plot(pca_envfit_O2, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_partS, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_NO3, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_PO4, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
title("A", line = -1, adj = 0.02)
# Panel 2- Add rest of envfit vectors
with(metadata_ordinations, plot(scores(lograt_pca, display = "sites"), col = colvec[OxCond], pch = shapevec[SizeFraction], cex = 1.5, cex.lab = .8, cex.axis = .8, xlab = "", ylab = "", xaxt='n', yaxt='n', xlim=c(-60,60)))
plot(pca_envfit_temp, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_sal, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_FlagAbun, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
plot(pca_envfit_chemo, p.max = 0.1, lwd = 2, col = "black", cex = 0.6)
title("B", line = -1, adj = 0.02)
# Panel 3- Add spider lines indicating envfit centroids for Size Fraction
with(metadata_ordinations, plot(scores(lograt_pca, display = "sites"), col = colvec[OxCond], pch = shapevec[SizeFraction], cex = 1.5, cex.lab = .8, cex.axis = .8, xlab = paste0('PC1 ', round(lograt_variances[1,2]*100,2),'%'), ylab = paste0('PC2 ', round(lograt_variances[2,2]*100,2),'%'), xlim=c(-60,60)))
with(metadata_ordinations, ordispider(lograt_pca, SizeFraction, lwd = 1.5, lty = c(1,2), label = TRUE, cex = 0.6))
title("C", line = -1, adj = 0.02)
# Panel 4 -Add vectors indicating significant spp
with(metadata_ordinations, plot(scores(lograt_pca, display = "sites"), col = colvec[OxCond], pch = shapevec[SizeFraction], cex = 1.5, cex.lab = .8, cex.axis = .8, xlab = paste0('PC1 ', round(lograt_variances[1,2]*100,2),'%'), ylab = "", yaxt='n', xlim=c(-60,60)))
plot(pca_sppfit_trim, p.max = 0.001, col = "black", cex = 0.6)
# annotate the 3 clusters of ASVs in panel D
text(x=c(0), y=c(45), labels=c("Cluster I"), adj = 0.5, font = 2, cex = 0.8)
text(x=c(50), y=c(30), labels=c("Cluster II"), adj = 0.5, font = 2, cex = 0.8)
text(x=c(48), y=c(-35), labels=c("Cluster III"), adj = 0.5, font = 2, cex = 0.8)
text(x=c(-48), y=c(-18), labels=c("Cluster IV"), adj = 0.5, font = 2, cex = 0.8)
text(x=c(-48), y=c(10), labels=c("Cluster V"), adj = 0.5, font = 2, cex = 0.8)
title("D", line = -1, adj = 0.02)
# Add legend
par(op) # Leave the last plot
op <- par(usr=c(0,1,0,1), # Reset the coordinates
          xpd=NA)         # Allow plotting outside the plot region
legend(-0.018,.57, c("PA", "FL", "Oxycline", "Shallow Anoxic", "Euxinic"), col=c("black", "black","blue", "red", "brown4"), pch = c(16, 17, 15, 15, 15), box.col=NA, cex = .8, horiz = T, x.intersp = c(0.3), text.width = c(0, 0.18, 0.18, 0.18, 0.2))

dev.off()
quartz_off_screen 
                2 

For the manuscript, I want to discuss what these significant species are. Make a table:

# extract p-values for each species
fit_pvals <- pca_sppfit$vectors$pvals %>% 
  as.data.frame() %>% 
  rownames_to_column("ASVID") %>% 
  dplyr::rename("pvals" = ".")

# extract r2 values
fit_r2vals <- pca_sppfit$vectors$r %>% 
  as.data.frame() %>% 
  rownames_to_column("ASVID") %>% 
  dplyr::rename("r2vals" = ".")

# only keep species with p-val < 0.001 and r2 value >0.6
fit_spp <- pca_sppfit %>% 
  scores(., display = "vectors") %>% 
  as.data.frame() %>% 
  rownames_to_column("ASVID") %>% 
  full_join(., fit_pvals, by = "ASVID") %>% 
  full_join(., fit_r2vals, by = "ASVID") %>% 
  filter(pvals < 0.001) %>%
  filter(r2vals > 0.6) 
# --> filters to 107 species

# put in ASV identifying information
pca_sig_ASVs <- taxonomy %>% 
  mutate(ASVID = rownames(taxonomy)) %>% 
  right_join(fit_spp, by = "ASVID") 

# sort by PC2 to differentiate those above and below the PC2= 0 axis
pca_sig_ASVs <- pca_sig_ASVs %>%
 arrange(desc(PC2))

pca_sig_ASVs

# the vegan plot also scales the species scores to fit the current plot (which is why PC values don't match what is seen in plot) Get these scaled PC values
ordiArrowMul(lograt_pca, display = "species") #7.636856
[1] 7.636856
ordiArrowMul(pca_sppfit, display = "vectors") #0.8291121
[1] 0.8291121
# export as table
write.csv(pca_sig_ASVs, file="stats_results/pca_sig_ASVs.csv", row.names=FALSE)

Correlation Analyses

Prepare the data

Import prokaryote dataset from Suter et al. 2018

Import

arch_counts <- read_csv("Suter_2018_count_tables/Cariaco_AA_updated_raw.csv");
bac_counts <- read_csv("Suter_2018_count_tables/Cariaco_AB_updated_raw.csv");

Get sample names

bac_samples <- colnames(bac_counts)[2:49]
arch_samples <- colnames(arch_counts)[2:47]

bac_samples
arch_samples

Make separate taxonomy and count variables

arch_OTU <- arch_counts[,c("#OTU ID",arch_samples)]
arch_taxonomy <-  arch_counts %>%
  select(-arch_samples)  %>%
  select(-Sum)

arch_OTU
arch_taxonomy

bac_OTU <- bac_counts[,c("#OTU ID",bac_samples)]
bac_taxonomy <-  bac_counts %>%
  select(-bac_samples)  %>%
  select(-Sum) %>%
  select(-"Interesting close relatives")

bac_OTU
bac_taxonomy

Make into phyloseq objects

bac_OTU <- type_convert(as.data.frame(bac_OTU))
rownames(bac_OTU) <- bac_OTU$`#OTU ID`
bac_OTU <- bac_OTU[,!names(bac_OTU) %in% (c("#OTU ID"))]

bac_OTU =   otu_table(bac_OTU, taxa_are_rows =  TRUE)
#
arch_OTU <- type_convert(as.data.frame(arch_OTU))
rownames(arch_OTU) <- arch_OTU$`#OTU ID`
arch_OTU <- arch_OTU[,!names(arch_OTU) %in% (c("#OTU ID"))]

arch_OTU    =   otu_table(arch_OTU, taxa_are_rows =  TRUE)
#
bac_TAX <- type_convert(as.data.frame(bac_taxonomy))
rownames(bac_TAX) <- bac_TAX$`#OTU ID`
bac_TAX <- bac_TAX[,!names(bac_TAX) %in% (c("#OTU ID"))]

bac_TAX =   tax_table(as.matrix(bac_TAX))
#
arch_TAX <- type_convert(as.data.frame(arch_taxonomy))
rownames(arch_TAX) <- arch_TAX$`#OTU ID`
arch_TAX <- arch_TAX[,!names(arch_TAX) %in% (c("#OTU ID"))]

arch_TAX    =   tax_table(as.matrix(arch_TAX))
#
META    =   sample_data(data.frame(metadata, row.names = metadata$`Sample Name`))
#

ps_bac <- phyloseq(bac_OTU, bac_TAX,    META)
ps_arch <- phyloseq(arch_OTU,   arch_TAX,   META)

Filter out the samples with low sequencing effort. These were previously identified for itags paper

taxa_to_keep_b <- !sample_names(ps_bac) %in% c("AB3a900A","AB2a200A","AB2b267A")
ps_bac <- prune_samples(taxa_to_keep_b, ps_bac)

taxa_to_keep_a <- !sample_names(ps_arch) %in% c("AA2b900AN","AA2a247B","AA2a900BN","AA2b900BN")
ps_arch <- prune_samples(taxa_to_keep_a, ps_arch)

Filtering

First calculate relative abdunance of bac and arch OTU tables

ps_bac_ra <- microbiome::transform(ps_bac, transform = "compositional")
(otu_table(ps_bac_ra))[1:5,1:5]

ps_arch_ra <- microbiome::transform(ps_arch, transform = "compositional")
(otu_table(ps_arch_ra))[1:5,1:5]

Filter low abundance species from full dataset

Remove rows of glommed taxa from the full dataframe if their sum across all samples doesn’t exceed 5% (RA > 0.05)

# Bacteria
x <- taxa_sums(ps_bac_ra)
# keepTaxa <-  base::which(x  > .05)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_bac_ra_pruned <-  prune_taxa(keepTaxa, ps_bac_ra)
ps_bac_pruned <-  prune_taxa(keepTaxa, ps_bac)
ps_bac_ra_pruned
ps_bac_pruned

# Archaea
x <- taxa_sums(ps_arch_ra)
# keepTaxa <-  base::which(x  > .05)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_arch_ra_pruned <-  prune_taxa(keepTaxa, ps_arch_ra)
ps_arch_pruned <-  prune_taxa(keepTaxa, ps_arch)
ps_arch_ra_pruned
ps_arch_pruned

# Eukaryotes
x <- taxa_sums(ps_ra)
# keepTaxa <-  base::which(x  > .05)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_euk_ra_pruned <-  prune_taxa(keepTaxa, ps_ra)
ps_euk_pruned <-  prune_taxa(keepTaxa, ps)
ps_euk_ra_pruned
ps_euk_pruned

Trimmed to 124 bacteria OTUs, 52 archaea OTUs, and 123 eukaryotic ASVs (299 total). Proceed with this dataset of the most abundant OTUs for correlations and network analyses…

To do the multi-domain analysis, the sample names from each phyloseq object must match. These currently have “B” for bacteria, A, E etc. Remove this letter from sample names so that “AE2a247B”, “AA2a247B”, “AB2a247B” all become just “Type” from the metadata sheet [IntNov1FL in this case- for Interface, November, rep 1, free-living].

Import my SampleKey

samplekey <- read_csv("SampleKey.csv")

Change the sample names in the otu tables to sample “Type”

# Archaea
# remove missing archaea samples from samplekey_A
samplekey_A <- filter(samplekey, SampleID_arch  %in% colnames(otu_table(ps_arch_ra_pruned)))
# sort SampleKey by order of column names from ps_arch_ra_pruned
samplekey_A <- samplekey_A %>% arrange(factor(SampleID_arch, levels = colnames(otu_table(ps_arch_ra_pruned))))
# replace col names of otu table from ps_arch_ra_pruned
sample_names(ps_arch_ra_pruned) <- samplekey_A$Type
# and ps_arch_pruned
sample_names(ps_arch_pruned) <- samplekey_A$Type


# Bacteria
samplekey_B <- filter(samplekey, SampleID_bac  %in% colnames(otu_table(ps_bac_ra_pruned)))
samplekey_B <- samplekey_B %>% arrange(factor(SampleID_bac, levels = colnames(otu_table(ps_bac_ra_pruned))))
sample_names(ps_bac_ra_pruned) <- samplekey_B$Type
sample_names(ps_bac_pruned) <- samplekey_B$Type


# Eukaryotes
samplekey_E <- filter(samplekey, SampleID_euk  %in% colnames(otu_table(ps_euk_ra_pruned)))
samplekey_E <- samplekey_E %>% arrange(factor(SampleID_euk, levels = colnames(otu_table(ps_euk_ra_pruned))))
sample_names(ps_euk_ra_pruned) <- samplekey_E$Type
sample_names(ps_euk_pruned) <- samplekey_E$Type

Move all pruned otu tables into one table by matching the sample Type- will use this for SparCC Make one for the 3-domain analysis and one for the 2-domain analysis (bacteria and archaea only)

alldomains_df <- bind_rows(data.frame(otu_table(ps_bac_pruned)), data.frame(otu_table(ps_arch_pruned)), data.frame(otu_table(ps_euk_pruned)))
alldomains_df

twodomains_df <- bind_rows(data.frame(otu_table(ps_bac_pruned)), data.frame(otu_table(ps_arch_pruned)))
twodomains_df

Change row names from “denovoXXX” to meaningful names

alldomains_df_full <- cbind(ID = rownames(alldomains_df), alldomains_df)
twodomains_df_full <- cbind(ID = rownames(twodomains_df), twodomains_df)


# start with only first rows, which are bacteria. make one column of meaningful labels
temp1 <- left_join(alldomains_df_full[1:dim(otu_table(ps_bac_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
temp1$New_ID <- paste(temp1$ID, temp1$"taxonomy-2", temp1$"taxonomy-3", temp1$"taxonomy-4")
temp1 <- select(temp1,-colnames(bac_taxonomy[,2:11]))

# next rows are the archaea 
temp2 <- left_join(alldomains_df_full[sum(dim(otu_table(ps_bac_pruned))[1],1):sum(dim(otu_table(ps_bac_pruned))[1],dim(otu_table(ps_arch_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 
temp2$New_ID <- paste(temp2$ID, temp2$"taxonomy-2", temp2$"taxonomy-3")
temp2 <- select(temp2,-colnames(arch_taxonomy[,2:9]))


# last rows are eukarya
euk_taxonomy <- cbind("#ASV ID" = rownames(taxonomy), taxonomy)
temp3 <- left_join(alldomains_df_full[sum(dim(otu_table(ps_arch_pruned))[1], dim(otu_table(ps_bac_pruned))[1],1):sum(dim(otu_table(ps_arch_pruned))[1], dim(otu_table(ps_bac_pruned))[1],dim(otu_table(ps_euk_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
temp3$New_ID <- paste(temp3$ID, temp3$"Supergroup", temp3$"Division", temp3$"Class", temp3$"Order")
temp3 <- select(temp3,-colnames(euk_taxonomy[,2:9]))

# combine back all 3 domains, with new names as row names in a dataframe
alldomains_df_full <- rbind(temp1, temp2, temp3)
alldomains_df_full <- data.frame(alldomains_df_full)
rownames(alldomains_df_full) <- alldomains_df_full$New_ID
alldomains_df_full <- select(alldomains_df_full, -c("ID","New_ID"))

# and make one for the 2-domain dataset
twodomains_df_full <- rbind(temp1, temp2)
twodomains_df_full <- data.frame(twodomains_df_full)
rownames(twodomains_df_full) <- twodomains_df_full$New_ID
twodomains_df_full <- select(twodomains_df_full, -c("ID","New_ID"))

Remove columns with NAs. These are samples for which the library for at least one domain didn’t work (can’t do correlations with missing values in columns)

alldomains_df_full <- alldomains_df_full %>%
    select_if(~ !any(is.na(.)))
alldomains_df_full

alldomains_df <- alldomains_df %>%
    select_if(~ !any(is.na(.)))
alldomains_df

twodomains_df_full <- twodomains_df_full %>%
    select_if(~ !any(is.na(.)))
twodomains_df_full

twodomains_df <- twodomains_df %>%
    select_if(~ !any(is.na(.)))
twodomains_df

Filter low abundance species- oxycline depths only

Simlarly, make pruned datasets of the most abundant OTUs/ASVs in the oxycline, anoxic, and euxinic samples as separate datasets

Pull out samples and taxa from each redox regime

# Pull out oxycline bacteria sample IDs
oxyclinetypes_bac <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_bac)) %>%
  filter(OxCond == "Oxycline") %>% 
  select("Sample Name")
oxyclinetypes_bac <- unlist(c(unique(oxyclinetypes_bac)), use.names = FALSE)

# Pull out all bacteria from oxycline
ps_bac_oxycline <-  prune_samples(oxyclinetypes_bac, ps_bac)
ps_bac_ra_oxycline <-  prune_samples(oxyclinetypes_bac, ps_bac_ra)


# Pull out oxycline archaea sample IDs
oxyclinetypes_arch <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_arch)) %>%
  filter(OxCond == "Oxycline") %>% 
  select("Sample Name")
oxyclinetypes_arch <- unlist(c(unique(oxyclinetypes_arch)), use.names = FALSE)

# Pull out all archaea from oxycline
ps_arch_oxycline <-  prune_samples(oxyclinetypes_arch, ps_arch)
ps_arch_ra_oxycline <-  prune_samples(oxyclinetypes_arch, ps_arch_ra)


# Pull out oxycline eukaryotic sample IDs
oxyclinetypes_euk <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps)) %>%
  filter(OxCond == "Oxycline") %>% 
  select("Sample Name")
oxyclinetypes_euk <- unlist(c(unique(oxyclinetypes_euk)), use.names = FALSE)

# Pull out all eukaryotes from oxycline
ps_euk_oxycline <-  prune_samples(oxyclinetypes_euk, ps)
ps_euk_ra_oxycline <-  prune_samples(oxyclinetypes_euk, ps_ra)

Filter out low abundance taxa from the oxycline samples. Use 5% as cutoff

# Bacteria
x <- taxa_sums(ps_bac_ra_oxycline)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_bac_ra_oxycline_pruned <-  prune_taxa(keepTaxa, ps_bac_ra_oxycline)
ps_bac_oxycline_pruned <-  prune_taxa(keepTaxa, ps_bac_oxycline)
ps_bac_ra_oxycline_pruned
ps_bac_oxycline_pruned

# Archaea
x <- taxa_sums(ps_arch_ra_oxycline)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_arch_ra_oxycline_pruned <-  prune_taxa(keepTaxa, ps_arch_ra_oxycline)
ps_arch_oxycline_pruned <-  prune_taxa(keepTaxa, ps_arch_oxycline)
ps_arch_ra_oxycline_pruned
ps_arch_oxycline_pruned

# Eukaryotes
x <- taxa_sums(ps_euk_ra_oxycline)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_euk_ra_oxycline_pruned <-  prune_taxa(keepTaxa, ps_euk_ra_oxycline)
ps_euk_oxycline_pruned <-  prune_taxa(keepTaxa, ps_euk_oxycline)
ps_euk_ra_oxycline_pruned
ps_euk_oxycline_pruned

79 bacteria, 36 archaea, 76 eukaryota remain

Change the sample names in the otu tables to “Type”

# Archaea
# remove missing archaea samples from samplekey_A
samplekey_A <- filter(samplekey, SampleID_arch  %in% colnames(otu_table(ps_arch_ra_oxycline_pruned)))
# sort SampleKey by order of column names from ps_arch_ra_oxycline_pruned
samplekey_A <- samplekey_A %>% arrange(factor(SampleID_arch, levels = colnames(otu_table(ps_arch_ra_oxycline_pruned))))
# replace col names of otu table from ps_arch_ra_oxycline_pruned
sample_names(ps_arch_ra_oxycline_pruned) <- samplekey_A$Type
# and ps_arch_pruned
sample_names(ps_arch_oxycline_pruned) <- samplekey_A$Type


# Bacteria
samplekey_B <- filter(samplekey, SampleID_bac  %in% colnames(otu_table(ps_bac_ra_oxycline_pruned)))
samplekey_B <- samplekey_B %>% arrange(factor(SampleID_bac, levels = colnames(otu_table(ps_bac_ra_oxycline_pruned))))
sample_names(ps_bac_ra_oxycline_pruned) <- samplekey_B$Type
sample_names(ps_bac_oxycline_pruned) <- samplekey_B$Type


# Eukaryotes
samplekey_E <- filter(samplekey, SampleID_euk  %in% colnames(otu_table(ps_euk_ra_oxycline_pruned)))
samplekey_E <- samplekey_E %>% arrange(factor(SampleID_euk, levels = colnames(otu_table(ps_euk_ra_oxycline_pruned))))
sample_names(ps_euk_ra_oxycline_pruned) <- samplekey_E$Type
sample_names(ps_euk_oxycline_pruned) <- samplekey_E$Type

Move all pruned otu tables into one table by matching the sample Type- will use this for SparCC

alldomains_df_oxycline <- bind_rows(data.frame(otu_table(ps_bac_oxycline_pruned)), data.frame(otu_table(ps_arch_oxycline_pruned)), data.frame(otu_table(ps_euk_oxycline_pruned)))
alldomains_df_oxycline

Change row names from “denovoXXX” to meaningful names

alldomains_df_full_oxycline <- cbind(ID = rownames(alldomains_df_oxycline), alldomains_df_oxycline)

# start with only first rows, which are bacteria. make one column of meaningful labels
temp1 <- left_join(alldomains_df_full_oxycline[1:dim(otu_table(ps_bac_oxycline_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
temp1$New_ID <- paste(temp1$ID, temp1$"taxonomy-2", temp1$"taxonomy-3", temp1$"taxonomy-4")
temp1 <- select(temp1,-colnames(bac_taxonomy[,2:11]))

# next rows are the archaea 
temp2 <- left_join(alldomains_df_full_oxycline[sum(dim(otu_table(ps_bac_oxycline_pruned))[1],1):sum(dim(otu_table(ps_bac_oxycline_pruned))[1],dim(otu_table(ps_arch_oxycline_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 
temp2$New_ID <- paste(temp2$ID, temp2$"taxonomy-2", temp2$"taxonomy-3")
temp2 <- select(temp2,-colnames(arch_taxonomy[,2:9]))


# last rows are eukarya
euk_taxonomy <- cbind("#ASV ID" = rownames(taxonomy), taxonomy)
temp3 <- left_join(alldomains_df_full_oxycline[sum(dim(otu_table(ps_arch_oxycline_pruned))[1], dim(otu_table(ps_bac_oxycline_pruned))[1],1):sum(dim(otu_table(ps_arch_oxycline_pruned))[1], dim(otu_table(ps_bac_oxycline_pruned))[1],dim(otu_table(ps_euk_oxycline_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
temp3$New_ID <- paste(temp3$ID, temp3$"Supergroup", temp3$"Division", temp3$"Class", temp3$"Order")
temp3 <- select(temp3,-colnames(euk_taxonomy[,2:9]))

# combine back all 3 domains, with new names as row names in a dataframe
alldomains_df_full_oxycline <- rbind(temp1, temp2, temp3)
alldomains_df_full_oxycline <- data.frame(alldomains_df_full_oxycline)
rownames(alldomains_df_full_oxycline) <- alldomains_df_full_oxycline$New_ID
alldomains_df_full_oxycline <- select(alldomains_df_full_oxycline, -c("ID","New_ID"))
alldomains_df_full_oxycline

Remove columns with NAs. These are samples for which the library for at least one domain didn’t work (can’t do correlations with missing values in columns)

alldomains_df_full_oxycline <- alldomains_df_full_oxycline %>%
    select_if(~ !any(is.na(.)))
alldomains_df_full_oxycline

alldomains_df_oxycline <- alldomains_df_oxycline %>%
    select_if(~ !any(is.na(.)))
alldomains_df_oxycline

21 samples remain for correlation

Filter low abundance species- anoxic depths only

Pull out samples from shallow anoxic regime

# Pull out anoxic layer bacteria sample IDs
anoxictypes_bac <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_bac)) %>%
  filter(OxCond == "ShallowAnoxic") %>% 
  select("Sample Name")
anoxictypes_bac <- unlist(c(unique(anoxictypes_bac)), use.names = FALSE)

# Pull out all bacteria from anoxic layer
ps_bac_anoxic <-  prune_samples(anoxictypes_bac, ps_bac)
ps_bac_ra_anoxic <-  prune_samples(anoxictypes_bac, ps_bac_ra)


# Pull out anoxic layer archaea sample IDs
anoxictypes_arch <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_arch)) %>%
  filter(OxCond == "ShallowAnoxic") %>% 
  select("Sample Name")
anoxictypes_arch <- unlist(c(unique(anoxictypes_arch)), use.names = FALSE)

# Pull out all archaea from anoxic layer
ps_arch_anoxic<-  prune_samples(anoxictypes_arch, ps_arch)
ps_arch_ra_anoxic <-  prune_samples(anoxictypes_arch, ps_arch_ra)


# Pull out anoxic layer eukaryotic sample IDs
anoxictypes_euk <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps)) %>%
  filter(OxCond == "ShallowAnoxic") %>% 
  select("Sample Name")
anoxictypes_euk <- unlist(c(unique(anoxictypes_euk)), use.names = FALSE)

# Pull out all eukaryotes from anoxic layer
ps_euk_anoxic <-  prune_samples(anoxictypes_euk, ps)
ps_euk_ra_anoxic <-  prune_samples(anoxictypes_euk, ps_ra)

Filter out low abundance taxa from the oxycline samples. Use 5% as cutoff

# Bacteria
x <- taxa_sums(ps_bac_ra_anoxic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_bac_ra_anoxic_pruned <-  prune_taxa(keepTaxa, ps_bac_ra_anoxic)
ps_bac_anoxic_pruned <-  prune_taxa(keepTaxa, ps_bac_anoxic)
ps_bac_ra_anoxic_pruned
ps_bac_anoxic_pruned

# Archaea
x <- taxa_sums(ps_arch_ra_anoxic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_arch_ra_anoxic_pruned <-  prune_taxa(keepTaxa, ps_arch_ra_anoxic)
ps_arch_anoxic_pruned <-  prune_taxa(keepTaxa, ps_arch_anoxic)
ps_arch_ra_anoxic_pruned
ps_arch_anoxic_pruned

# Eukaryotes
x <- taxa_sums(ps_euk_ra_anoxic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_euk_ra_anoxic_pruned <-  prune_taxa(keepTaxa, ps_euk_ra_anoxic)
ps_euk_anoxic_pruned <-  prune_taxa(keepTaxa, ps_euk_anoxic)
ps_euk_ra_anoxic_pruned
ps_euk_anoxic_pruned

32 bacteria, 19 archaea, 37 eukaryota remain

Change the sample names in the otu tables to “Type”

# Archaea
# remove missing archaea samples from samplekey_A
samplekey_A <- filter(samplekey, SampleID_arch  %in% colnames(otu_table(ps_arch_ra_anoxic_pruned)))
# sort SampleKey by order of column names from ps_arch_ra_anoxic_pruned
samplekey_A <- samplekey_A %>% arrange(factor(SampleID_arch, levels = colnames(otu_table(ps_arch_ra_anoxic_pruned))))
# replace col names of otu table from ps_arch_ra_anoxic_pruned
sample_names(ps_arch_ra_anoxic_pruned) <- samplekey_A$Type
# and ps_arch_pruned
sample_names(ps_arch_anoxic_pruned) <- samplekey_A$Type


# Bacteria
samplekey_B <- filter(samplekey, SampleID_bac  %in% colnames(otu_table(ps_bac_ra_anoxic_pruned)))
samplekey_B <- samplekey_B %>% arrange(factor(SampleID_bac, levels = colnames(otu_table(ps_bac_ra_anoxic_pruned))))
sample_names(ps_bac_ra_anoxic_pruned) <- samplekey_B$Type
sample_names(ps_bac_anoxic_pruned) <- samplekey_B$Type


# Eukaryotes
samplekey_E <- filter(samplekey, SampleID_euk  %in% colnames(otu_table(ps_euk_ra_anoxic_pruned)))
samplekey_E <- samplekey_E %>% arrange(factor(SampleID_euk, levels = colnames(otu_table(ps_euk_ra_anoxic_pruned))))
sample_names(ps_euk_ra_anoxic_pruned) <- samplekey_E$Type
sample_names(ps_euk_anoxic_pruned) <- samplekey_E$Type

Move all pruned otu tables into one table by matching the sample Type- will use this for SparCC

alldomains_df_anoxic <- bind_rows(data.frame(otu_table(ps_bac_anoxic_pruned)), data.frame(otu_table(ps_arch_anoxic_pruned)), data.frame(otu_table(ps_euk_anoxic_pruned)))
alldomains_df_anoxic

Change row names from “denovoXXX” to meaningful names

alldomains_df_full_anoxic <- cbind(ID = rownames(alldomains_df_anoxic), alldomains_df_anoxic)

# start with only first rows, which are bacteria. make one column of meaningful labels
temp1 <- left_join(alldomains_df_full_anoxic[1:dim(otu_table(ps_bac_anoxic_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
temp1$New_ID <- paste(temp1$ID, temp1$"taxonomy-2", temp1$"taxonomy-3", temp1$"taxonomy-4")
temp1 <- select(temp1,-colnames(bac_taxonomy[,2:11]))

# next rows are the archaea 
temp2 <- left_join(alldomains_df_full_anoxic[sum(dim(otu_table(ps_bac_anoxic_pruned))[1],1):sum(dim(otu_table(ps_bac_anoxic_pruned))[1],dim(otu_table(ps_arch_anoxic_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 
temp2$New_ID <- paste(temp2$ID, temp2$"taxonomy-2", temp2$"taxonomy-3")
temp2 <- select(temp2,-colnames(arch_taxonomy[,2:9]))


# last rows are eukarya
euk_taxonomy <- cbind("#ASV ID" = rownames(taxonomy), taxonomy)
temp3 <- left_join(alldomains_df_full_anoxic[sum(dim(otu_table(ps_arch_anoxic_pruned))[1], dim(otu_table(ps_bac_anoxic_pruned))[1],1):sum(dim(otu_table(ps_arch_anoxic_pruned))[1], dim(otu_table(ps_bac_anoxic_pruned))[1],dim(otu_table(ps_euk_anoxic_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
temp3$New_ID <- paste(temp3$ID, temp3$"Supergroup", temp3$"Division", temp3$"Class", temp3$"Order")
temp3 <- select(temp3,-colnames(euk_taxonomy[,2:9]))

# combine back all 3 domains, with new names as row names in a dataframe
alldomains_df_full_anoxic <- rbind(temp1, temp2, temp3)
alldomains_df_full_anoxic <- data.frame(alldomains_df_full_anoxic)
rownames(alldomains_df_full_anoxic) <- alldomains_df_full_anoxic$New_ID
alldomains_df_full_anoxic <- select(alldomains_df_full_anoxic, -c("ID","New_ID"))
alldomains_df_full_anoxic

Remove columns with NAs. These are samples for which the library for at least one domain didn’t work (can’t do correlations with missing values in columns)

alldomains_df_full_anoxic <- alldomains_df_full_anoxic %>%
    select_if(~ !any(is.na(.)))
alldomains_df_full_anoxic

alldomains_df_anoxic <- alldomains_df_anoxic %>%
    select_if(~ !any(is.na(.)))
alldomains_df_anoxic

11 samples remain for correlation

Filter low abundance species- euxinic depths only

Pull out samples from shallow anoxic regime

# Pull out anoxic layer bacteria sample IDs
euxinictypes_bac <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_bac)) %>%
  filter(OxCond == "Euxinic") %>% 
  select("Sample Name")
euxinictypes_bac <- unlist(c(unique(euxinictypes_bac)), use.names = FALSE)

# Pull out all bacteria from euxinic layer
ps_bac_euxinic <-  prune_samples(euxinictypes_bac, ps_bac)
ps_bac_ra_euxinic <-  prune_samples(euxinictypes_bac, ps_bac_ra)


# Pull out euxinic layer archaea sample IDs
euxinictypes_arch <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_arch)) %>%
  filter(OxCond == "Euxinic") %>% 
  select("Sample Name")
euxinictypes_arch <- unlist(c(unique(euxinictypes_arch)), use.names = FALSE)

# Pull out all archaea from euxinic layer
ps_arch_euxinic<-  prune_samples(euxinictypes_arch, ps_arch)
ps_arch_ra_euxinic <-  prune_samples(euxinictypes_arch, ps_arch_ra)


# Pull out euxinic layer eukaryotic sample IDs
euxinictypes_euk <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps)) %>%
  filter(OxCond == "Euxinic") %>% 
  select("Sample Name")
euxinictypes_euk <- unlist(c(unique(euxinictypes_euk)), use.names = FALSE)

# Pull out all eukaryotes from euxinic layer
ps_euk_euxinic <-  prune_samples(euxinictypes_euk, ps)
ps_euk_ra_euxinic <-  prune_samples(euxinictypes_euk, ps_ra)

Filter out low abundance taxa from the oxycline samples. Use 5% as cutoff

# Bacteria
x <- taxa_sums(ps_bac_ra_euxinic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_bac_ra_euxinic_pruned <-  prune_taxa(keepTaxa, ps_bac_ra_euxinic)
ps_bac_euxinic_pruned <-  prune_taxa(keepTaxa, ps_bac_euxinic)
ps_bac_ra_euxinic_pruned
ps_bac_euxinic_pruned

# Archaea
x <- taxa_sums(ps_arch_ra_euxinic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_arch_ra_euxinic_pruned <-  prune_taxa(keepTaxa, ps_arch_ra_euxinic)
ps_arch_euxinic_pruned <-  prune_taxa(keepTaxa, ps_arch_euxinic)
ps_arch_ra_euxinic_pruned
ps_arch_euxinic_pruned

# Eukaryotes
x <- taxa_sums(ps_euk_ra_euxinic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_euk_ra_euxinic_pruned <-  prune_taxa(keepTaxa, ps_euk_ra_euxinic)
ps_euk_euxinic_pruned <-  prune_taxa(keepTaxa, ps_euk_euxinic)
ps_euk_ra_euxinic_pruned
ps_euk_euxinic_pruned

16 bacteria, 16 archaea, 20 eukaryota remain

Change the sample names in the otu tables to “Type”

# Archaea
# remove missing archaea samples from samplekey_A
samplekey_A <- filter(samplekey, SampleID_arch  %in% colnames(otu_table(ps_arch_ra_euxinic_pruned)))
# sort SampleKey by order of column names from ps_arch_ra_euxinic_pruned
samplekey_A <- samplekey_A %>% arrange(factor(SampleID_arch, levels = colnames(otu_table(ps_arch_ra_euxinic_pruned))))
# replace col names of otu table from ps_arch_ra_euxinic_pruned
sample_names(ps_arch_ra_euxinic_pruned) <- samplekey_A$Type
# and ps_arch_pruned
sample_names(ps_arch_euxinic_pruned) <- samplekey_A$Type


# Bacteria
samplekey_B <- filter(samplekey, SampleID_bac  %in% colnames(otu_table(ps_bac_ra_euxinic_pruned)))
samplekey_B <- samplekey_B %>% arrange(factor(SampleID_bac, levels = colnames(otu_table(ps_bac_ra_euxinic_pruned))))
sample_names(ps_bac_ra_euxinic_pruned) <- samplekey_B$Type
sample_names(ps_bac_euxinic_pruned) <- samplekey_B$Type


# Eukaryotes
samplekey_E <- filter(samplekey, SampleID_euk  %in% colnames(otu_table(ps_euk_ra_euxinic_pruned)))
samplekey_E <- samplekey_E %>% arrange(factor(SampleID_euk, levels = colnames(otu_table(ps_euk_ra_euxinic_pruned))))
sample_names(ps_euk_ra_euxinic_pruned) <- samplekey_E$Type
sample_names(ps_euk_euxinic_pruned) <- samplekey_E$Type

Move all pruned otu tables into one table by matching the sample Type- will use this for SparCC

alldomains_df_euxinic <- bind_rows(data.frame(otu_table(ps_bac_euxinic_pruned)), data.frame(otu_table(ps_arch_euxinic_pruned)), data.frame(otu_table(ps_euk_euxinic_pruned)))
alldomains_df_euxinic

Change row names from “denovoXXX” to meaningful names

alldomains_df_full_euxinic <- cbind(ID = rownames(alldomains_df_euxinic), alldomains_df_euxinic)

# start with only first rows, which are bacteria. make one column of meaningful labels
temp1 <- left_join(alldomains_df_full_euxinic[1:dim(otu_table(ps_bac_euxinic_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
temp1$New_ID <- paste(temp1$ID, temp1$"taxonomy-2", temp1$"taxonomy-3", temp1$"taxonomy-4")
temp1 <- select(temp1,-colnames(bac_taxonomy[,2:11]))

# next rows are the archaea 
temp2 <- left_join(alldomains_df_full_euxinic[sum(dim(otu_table(ps_bac_euxinic_pruned))[1],1):sum(dim(otu_table(ps_bac_euxinic_pruned))[1],dim(otu_table(ps_arch_euxinic_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 
temp2$New_ID <- paste(temp2$ID, temp2$"taxonomy-2", temp2$"taxonomy-3")
temp2 <- select(temp2,-colnames(arch_taxonomy[,2:9]))


# last rows are eukarya
euk_taxonomy <- cbind("#ASV ID" = rownames(taxonomy), taxonomy)
temp3 <- left_join(alldomains_df_full_euxinic[sum(dim(otu_table(ps_arch_euxinic_pruned))[1], dim(otu_table(ps_bac_euxinic_pruned))[1],1):sum(dim(otu_table(ps_arch_euxinic_pruned))[1], dim(otu_table(ps_bac_euxinic_pruned))[1],dim(otu_table(ps_euk_euxinic_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
temp3$New_ID <- paste(temp3$ID, temp3$"Supergroup", temp3$"Division", temp3$"Class", temp3$"Order")
temp3 <- select(temp3,-colnames(euk_taxonomy[,2:9]))

# combine back all 3 domains, with new names as row names in a dataframe
alldomains_df_full_euxinic <- rbind(temp1, temp2, temp3)
alldomains_df_full_euxinic <- data.frame(alldomains_df_full_euxinic)
rownames(alldomains_df_full_euxinic) <- alldomains_df_full_euxinic$New_ID
alldomains_df_full_euxinic <- select(alldomains_df_full_euxinic, -c("ID","New_ID"))
alldomains_df_full_euxinic

Remove columns with NAs. These are samples for which the library for at least one domain didn’t work (can’t do correlations with missing values in columns)

alldomains_df_full_euxinic <- alldomains_df_full_euxinic %>%
    select_if(~ !any(is.na(.)))
alldomains_df_full_euxinic

alldomains_df_euxinic <- alldomains_df_euxinic %>%
    select_if(~ !any(is.na(.)))
alldomains_df_euxinic

4 samples remain for correlation

SparCC

SparCC on full dataset

This is largely based on BVCN tutorials NOTE- input for SparCC should be raw count data (after filtering out low-abundance ASVs). The function does a log-ratio transformation to account for compositionality

sparcctable_alldomains <- sparcc(t(alldomains_df))

Put sample names back into result tables

rownames(sparcctable_alldomains$Cor) <- rownames(alldomains_df_full)
colnames(sparcctable_alldomains$Cor) <- rownames(alldomains_df_full)
rownames(sparcctable_alldomains$Cov) <- rownames(alldomains_df_full)
colnames(sparcctable_alldomains$Cov) <- rownames(alldomains_df_full)

sparcctable_alldomains$Cor[1:2,1:2]

Plot correlation

plotableSparcc <- sparcctable_alldomains$Cor %>% reorder_cormat %>% get_upper_tri() %>% reshape2::melt() %>% na.omit()

Sparcc_plot <- plotableSparcc %>% ggplot(aes(x = Var2, y = Var1, fill = value)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))

Sparcc_plot

# ggsave("figures/sparcc_corr_alldomains.eps",Sparcc_plot, width = 35, height = 35, units = c("in"))

Calculate Sparcc p-values by bootstrapping- TAKES A LONG TIME

# tp0 <- proc.time()
# out2 <- sparccboot(t(alldomains_df), R = 1000, ncpus = 2)
# tp1 <- proc.time()
# tp1 - tp0

The above took ~14 hours to run 1000 iterations

Extract p-values

outP <- pval.sparccboot(out2)
data.frame(outP$cors, outP$pvals) %>% head
cors <- outP$cors
pvals <- outP$pvals
sparCCpcors <- diag(0.5, nrow = dim(sparcctable_alldomains$Cor)[1], ncol = dim(sparcctable_alldomains$Cor)[1])
sparCCpcors[upper.tri(sparCCpcors, diag=FALSE)] <- cors
sparCCpcors <- sparCCpcors + t(sparCCpcors)

sparCCpval <- diag(0.5, nrow = dim(sparcctable_alldomains$Cor)[1], ncol = dim(sparcctable_alldomains$Cor)[1])
sparCCpval[upper.tri(sparCCpval, diag=FALSE)] <- pvals
sparCCpval <- sparCCpval + t(sparCCpval)

rownames(sparCCpcors) <- rownames(alldomains_df_full)
colnames(sparCCpcors) <- rownames(alldomains_df_full)
rownames(sparCCpval) <- rownames(alldomains_df_full)
colnames(sparCCpval) <- rownames(alldomains_df_full)

sparCCpcors[1:2, 1:2]
sparCCpval[1:2, 1:2]

Reorder for plotting

reordered_all_sparcc <- reorder_cor_and_p(sparCCpcors, sparCCpval)
reordered_sparccCor <- reordered_all_sparcc$r
reordered_sparccP<- reordered_all_sparcc$p


sparccCor_processed <- reordered_sparccCor  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(cor = value)
sparccP_processed <- reordered_sparccP  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(p = value)

# join the two data frames

SparccP <- left_join(sparccCor_processed, sparccP_processed, by = c("Var1", "Var2")) %>%
  # # remove self correlations
  # filter(Var1 != Var2) %>% 
  # calculate the false discovery rate to adjust for multiple p values
  mutate(fdr = p.adjust(p, method = "BH"))

And plot correlation with p-values. Circles mean that the relationship is sig. at p = 0.05 level, based on bootstrapping

fdrThresh <- 0.01 # fdr threshold
sparccOkP <- SparccP%>% filter(fdr < fdrThresh) 

SparccP_plot <- SparccP %>% ggplot(aes(x = Var2, y = Var1, fill = cor)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + geom_point(data = sparccOkP, shape = 1)

SparccP_plot

ggsave("figures/sparcc_corr_alldomains_w_pvals.eps",SparccP_plot, width = 35, height = 35, units = c("in"))

Save environment again

# save.image("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_sparcc_bootstrap.RData")

Or load if coming back

load("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_sparcc_bootstrap.RData")

SpiecEasi

Try the SpiecEasi method, which accounts for sparse data, as described in the SpiecEasi publication, spieceasi github, and BVCN lessons 1.2. This reduces the clumps (eg. sparse relationships that are secondary or teriary, not direct relationships).

Make functions from tutorial

convertSEToTable <- function(se_out,sp.names){
  #This is just a fancy helper function to get the data in a comparable format to the output of lesson 1 so we can make a similar plot. We will cover other methods for visualizing this type of output in future lessons.
  secor <- cov2cor(as.matrix(getOptCov(se_out))) # See spieceasi documentation for how to pull out weights for comparison
  elist     <- summary(triu(secor*getRefit(se_out), k=1))
  elist[,1] <- sp.names[elist[,1]]
  elist[,2] <- sp.names[elist[,2]]
  elist[,4] <- paste(elist[,1],elist[,2])
  full_e <- expand.grid(sp.names,sp.names)
  rownames(full_e) <- paste(full_e[,1],full_e[,2])
  full_e[,"Weight"] <- 0
  full_e[elist[,4],"Weight"] <- elist[,3]
  x <- expand.grid(1:length(sp.names),1:length(sp.names))
  full_e[x[,"Var1"]>x[,"Var2"],"Weight"] <- NA
  return(as.data.frame(full_e,stringsAsFactors=F))
}

SpiecEasi on full dataset

Follow the spieceasi documentation to find optimal parameters. Also, because I want to compare networks, this convo on using optimal parameters for different network comparisons is helpful.

Remove samples from the phyloseq objects that are not in all 3 domains and reorder samples so they are in same order in all 3 objects

bac_arch_common <- intersect(sample_names(ps_bac_ra_pruned), sample_names(ps_arch_ra_pruned))
all_common <- intersect(bac_arch_common, sample_names(ps_euk_ra_pruned))

ps_bac_pruned_3domains <- prune_samples(all_common, ps_bac_pruned)
ps_arch_pruned_3domains <- prune_samples(all_common, ps_arch_pruned)
ps_euk_pruned_3domains <- prune_samples(all_common, ps_euk_pruned)

ps_bac_ra_pruned_3domains <- prune_samples(all_common, ps_bac_ra_pruned)
ps_arch_ra_pruned_3domains <- prune_samples(all_common, ps_arch_ra_pruned)
ps_euk_ra_pruned_3domains <- prune_samples(all_common, ps_euk_ra_pruned)


otu_table(ps_arch_pruned_3domains) <- otu_table(ps_arch_pruned_3domains)[,sample_names(ps_bac_ra_pruned_3domains)]
otu_table(ps_euk_pruned_3domains) <- otu_table(ps_euk_pruned_3domains)[,sample_names(ps_bac_ra_pruned_3domains)]

sample_data(ps_bac_pruned_3domains)
sample_data(ps_arch_pruned_3domains)
sample_data(ps_euk_pruned_3domains)
#Run Spieceasi
pargs <- list(seed=10010)
se <- spiec.easi(list(ps_bac_pruned_3domains, ps_arch_pruned_3domains, ps_euk_pruned_3domains), method='glasso', lambda.min.ratio=1e-2, nlambda=100, pulsar.params=pargs)
getStability(se)

the above takes a while to run (20-30 mins). Using parameters above, the stability along the lambda path crosses the 0.05 threshold and the final stability value (0.044) is sufficiently close to 0.05

#This is just a fancy helper function to get the data in a comparable format to the output of above
tab.se <- convertSEToTable(se,sp.names=colnames(t(alldomains_df_full))) 

#Plot 
plot.se <- ggplot(tab.se,aes(x = Var1, y = Var2, fill = Weight)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
plot(plot.se)

ggsave("figures/spieceasi_alldomains.eps",plot.se, width = 35, height = 35, units = c("in"))

Note- only the significant values above show up in the heatmap above (ie. there is no “p-value”)

SpiecEasi on bacteria and archaea only

Remove samples from the phyloseq objects that are not in both domains and reorder samples so they are in same order in all 3 objects

bac_arch_common <- intersect(sample_names(ps_bac_ra_pruned), sample_names(ps_arch_ra_pruned))

ps_bac_pruned_2domains <- prune_samples(bac_arch_common, ps_bac_pruned)
ps_arch_pruned_2domains <- prune_samples(bac_arch_common, ps_arch_pruned)

ps_bac_ra_pruned_2domains <- prune_samples(bac_arch_common, ps_bac_ra_pruned)
ps_arch_ra_pruned_2domains <- prune_samples(bac_arch_common, ps_arch_ra_pruned)


otu_table(ps_arch_pruned_2domains) <- otu_table(ps_arch_pruned_2domains)[,sample_names(ps_bac_ra_pruned_3domains)]

sample_data(ps_bac_pruned_2domains)
sample_data(ps_arch_pruned_2domains)
#Run Spieceasi
pargs <- list(seed=10010)
se.2domains <- spiec.easi(list(ps_bac_pruned_2domains, ps_arch_pruned_2domains), method='glasso', lambda.min.ratio=1e-2, nlambda=200, pulsar.params=pargs)
getStability(se.2domains)

the above takes a while to run . Using parameters above, the stability along the lambda path crosses the 0.05 threshold and the final stability value (0.046) is close to 0.05

#This is just a fancy helper function to get the data in a comparable format to the output of above
tab.se <- convertSEToTable(se.2domains,sp.names=colnames(t(twodomains_df_full))) 

#Plot 
plot.se <- ggplot(tab.se,aes(x = Var1, y = Var2, fill = Weight)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
plot(plot.se)

ggsave("figures/spieceasi_2domains.eps",plot.se, width = 35, height = 35, units = c("in"))

Note- only the significant values above show up in the heatmap above (ie. there is no “p-value”)

SpiecEasi on oxycline with 3 domains

bac_arch_common <- intersect(sample_names(ps_bac_oxycline_pruned), sample_names(ps_arch_oxycline_pruned))
all_common <- intersect(bac_arch_common, sample_names(ps_euk_oxycline_pruned))

ps_bac_oxycline_pruned <- prune_samples(all_common, ps_bac_oxycline_pruned)
ps_arch_oxycline_pruned <- prune_samples(all_common, ps_arch_oxycline_pruned)
ps_euk_oxycline_pruned <- prune_samples(all_common, ps_euk_oxycline_pruned)


otu_table(ps_arch_oxycline_pruned) <- otu_table(ps_arch_oxycline_pruned)[,sample_names(ps_bac_oxycline_pruned)]
otu_table(ps_euk_oxycline_pruned) <- otu_table(ps_euk_oxycline_pruned)[,sample_names(ps_bac_oxycline_pruned)]

sample_data(ps_bac_oxycline_pruned)
sample_data(ps_arch_oxycline_pruned)
sample_data(ps_euk_oxycline_pruned)
#Run Spieceasi
pargs <- list(seed=10010)
se.oxycline <- spiec.easi(list(ps_bac_oxycline_pruned, ps_arch_oxycline_pruned, ps_euk_oxycline_pruned), method='glasso', lambda.min.ratio=5e-3, nlambda=300, pulsar.params=pargs)
getStability(se.oxycline)

the above takes a couple of minutes to run. Stability and stability along lambda path are very similar to the full dataset spieceasi object (se) with these parameters above. Continue with these.

#This is just a fancy helper function to get the data in a comparable format to the output of above
tab.se.oxycline <- convertSEToTable(se.oxycline, sp.names=colnames(t(alldomains_df_full_oxycline))) 

#Plot 
plot.se.oxycline <- ggplot(tab.se.oxycline,aes(x = Var1, y = Var2, fill = Weight)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
plot(plot.se.oxycline)

ggsave("figures/spieceasi_alldomains_oxycline.eps",plot.se.oxycline, width = 35, height = 35, units = c("in"))

SpiecEasi on anoxic depths with 3 domains

bac_arch_common <- intersect(sample_names(ps_bac_anoxic_pruned), sample_names(ps_arch_anoxic_pruned))
all_common <- intersect(bac_arch_common, sample_names(ps_euk_anoxic_pruned))

ps_bac_anoxic_pruned <- prune_samples(all_common, ps_bac_anoxic_pruned)
ps_arch_anoxic_pruned <- prune_samples(all_common, ps_arch_anoxic_pruned)
ps_euk_anoxic_pruned <- prune_samples(all_common, ps_euk_anoxic_pruned)


otu_table(ps_arch_anoxic_pruned) <- otu_table(ps_arch_anoxic_pruned)[,sample_names(ps_bac_anoxic_pruned)]
otu_table(ps_euk_anoxic_pruned) <- otu_table(ps_euk_anoxic_pruned)[,sample_names(ps_bac_anoxic_pruned)]

sample_data(ps_bac_anoxic_pruned)
sample_data(ps_arch_anoxic_pruned)
sample_data(ps_euk_anoxic_pruned)
#Run Spieceasi
pargs <- list(seed=10010)
se.anoxic <- spiec.easi(list(ps_bac_anoxic_pruned, ps_arch_anoxic_pruned, ps_euk_anoxic_pruned), method='glasso', lambda.min.ratio=1e-1, nlambda=300, pulsar.params=pargs)
getStability(se.anoxic)

the above takes a couple of minutes to run

#This is just a fancy helper function to get the data in a comparable format to the output of above
tab.se.anoxic <- convertSEToTable(se.anoxic, sp.names=colnames(t(alldomains_df_full_anoxic))) 

#Plot 
plot.se.anoxic <- ggplot(tab.se.anoxic,aes(x = Var1, y = Var2, fill = Weight)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
plot(plot.se.anoxic)

ggsave("figures/spieceasi_alldomains_anoxic.eps",plot.se.anoxic, width = 35, height = 35, units = c("in"))

SpiecEasi on euxinic depths with 3 domains

bac_arch_common <- intersect(sample_names(ps_bac_euxinic_pruned), sample_names(ps_arch_euxinic_pruned))
all_common <- intersect(bac_arch_common, sample_names(ps_euk_euxinic_pruned))

ps_bac_euxinic_pruned <- prune_samples(all_common, ps_bac_euxinic_pruned)
ps_arch_euxinic_pruned <- prune_samples(all_common, ps_arch_euxinic_pruned)
ps_euk_euxinic_pruned <- prune_samples(all_common, ps_euk_euxinic_pruned)


otu_table(ps_arch_euxinic_pruned) <- otu_table(ps_arch_euxinic_pruned)[,sample_names(ps_bac_euxinic_pruned)]
otu_table(ps_euk_euxinic_pruned) <- otu_table(ps_euk_euxinic_pruned)[,sample_names(ps_bac_euxinic_pruned)]

sample_data(ps_bac_euxinic_pruned)
sample_data(ps_arch_euxinic_pruned)
sample_data(ps_euk_euxinic_pruned)
#Run Spieceasi
pargs <- list(seed=10010)
se.euxinic <- spiec.easi(list(ps_bac_euxinic_pruned, ps_arch_euxinic_pruned, ps_euk_euxinic_pruned), method='glasso', lambda.min.ratio=1e-5,nlambda=20, pulsar.params=pargs)
getStability(se.euxinic)

I tried many parameters on the above but cannot get a satisfactory solution. There are just too few samples after quality filtering to do SpiecEasi on the euxinic depths only.

Save and re-load environment

save.image("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_spieceasi.RData")

Or load if coming back

load("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_spieceasi.RData")

Network Analysis

Build networks from the SpiecEasi association matrices using iGraph

3 Domain Network- All depths

#Extract adjacency matrix from spiecEasi output
adj.mat <- getRefit(se)
table(as.numeric(adj.mat))

    0     1 
83721  5680 
# Extract weighted adjacency
se.cor  <- cov2cor(as.matrix(getOptCov(se)))
weighted.adj.mat <- se.cor*getRefit(se)

#Convert to graph objects
grph.unweighted <- adj2igraph(adj.mat)
grph <- adj2igraph(weighted.adj.mat)


# Put back in species names
V(grph)$name <- rownames(alldomains_df)
# V(grph)

# Make size of nodes proportional to degree (number of connections)
V(grph)$size <- (degree(grph) + 1) # the +1 avoids size zero vertices

# Change width of edges to be proportional to their weights
E(grph)$width <- abs(E(grph)$weight)*10

# Scale node sizes to be smaller
V(grph)$size <- V(grph)$size/2

# Remove low-weight edges (you decide what threshold is right for your network):
# weight_threshold <- 0.07
# grph <- delete.edges(grph,which(abs(E(grph)$weight)<weight_threshold))

# Join taxonomy data of each node
# Convert graph to datafram
grph_df <- igraph::as_data_frame(grph, 'both')
# make formatted taxonomy table for each domain
ps_bac_pruned_tax_table <- as.data.frame(tax_table(ps_bac_pruned)) %>%
  mutate(name = rownames(tax_table(ps_bac_pruned)))
ps_arch_pruned_tax_table <- as.data.frame(tax_table(ps_arch_pruned)) %>%
  mutate(name = rownames(tax_table(ps_arch_pruned)))
ps_euk_pruned_tax_table <- as.data.frame(tax_table(ps_euk_pruned)) %>%
  mutate(name = rownames(tax_table(ps_euk_pruned)))
# link graph data frame to formatted taxonomy tables
bac_temp <- left_join(grph_df$vertices[1:ntaxa(ps_bac_pruned),],
ps_bac_pruned_tax_table, by = "name")
  # delete columns that don't match other tax tables
  bac_temp <- select(bac_temp, -"taxonomy-9", -"Refined taxonomy")
arch_temp <- left_join(grph_df$vertices[ntaxa(ps_bac_pruned)+1:ntaxa(ps_arch_pruned),],
ps_arch_pruned_tax_table, by = "name")
euk_temp <- left_join(grph_df$vertices[ntaxa(ps_bac_pruned)+ntaxa(ps_arch_pruned)+1:ntaxa(ps_euk_pruned),],
ps_euk_pruned_tax_table, by = "name")
  # rename column names in euk table to match others
  euk_temp <- euk_temp %>%
    rename("taxonomy-1" = Kingdom, "taxonomy-2" = Supergroup, "taxonomy-3" = Division, "taxonomy-4" = Class, "taxonomy-5" = Order, "taxonomy-6" = Family, "taxonomy-7" = Genus, "taxonomy-8" = Species)
# build full dataframe with all 3 domains
all_temp <- rbind(bac_temp, arch_temp, euk_temp)
# remake into graph
grph <- graph_from_data_frame(grph_df$edges,
                                   directed = F,
                                   vertices = all_temp)

# Make color palette for domain
dtype = c("red", "green", "blue", "yellow")
# Make color vector
domain_color <- dtype[as.numeric(as.factor(V(grph)$"taxonomy-1"))]
# check
domain_color
  [1] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [11] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [21] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [31] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [41] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [51] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [61] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [71] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [81] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
 [91] "green"  "green"  "green"  "green"  "yellow" "green"  "green"  "green"  "green"  "green" 
[101] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
[111] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green"  "green" 
[121] "green"  "green"  "green"  "green"  "red"    "red"    "red"    "red"    "red"    "red"   
[131] "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"   
[141] "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"   
[151] "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"   
[161] "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"    "red"   
[171] "red"    "red"    "red"    "red"    "red"    "red"    "blue"   "blue"   "blue"   "blue"  
[181] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[191] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[201] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[211] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[221] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[231] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[241] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[251] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[261] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[271] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[281] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
[291] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"  
as.factor(V(grph)$"taxonomy-1")
  [1] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
  [8] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [15] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [22] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [29] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [36] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [43] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [50] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [57] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [64] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [71] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [78] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [85] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
 [92] Bacteria     Bacteria     Bacteria     No blast hit Bacteria     Bacteria     Bacteria    
 [99] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
[106] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
[113] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Bacteria    
[120] Bacteria     Bacteria     Bacteria     Bacteria     Bacteria     Archaea      Archaea     
[127] Archaea      Archaea      Archaea      Archaea      Archaea      Archaea      Archaea     
[134] Archaea      Archaea      Archaea      Archaea      Archaea      Archaea      Archaea     
[141] Archaea      Archaea      Archaea      Archaea      Archaea      Archaea      Archaea     
[148] Archaea      Archaea      Archaea      Archaea      Archaea      Archaea      Archaea     
[155] Archaea      Archaea      Archaea      Archaea      Archaea      Archaea      Archaea     
[162] Archaea      Archaea      Archaea      Archaea      Archaea      Archaea      Archaea     
[169] Archaea      Archaea      Archaea      Archaea      Archaea      Archaea      Archaea     
[176] Archaea      Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[183] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[190] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[197] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[204] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[211] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[218] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[225] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[232] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[239] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[246] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[253] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[260] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[267] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[274] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[281] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[288] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
[295] Eukaryota    Eukaryota    Eukaryota    Eukaryota    Eukaryota   
Levels: Archaea Bacteria Eukaryota No blast hit
# Plot
plot(grph,
     vertex.label=NA,
     layout=layout_with_graphopt(grph),
     vertex.color=domain_color)
title("SpiecEasi Network: All domains, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "Eukarya", "No Blast Hit"),
       fill=c("red", "green", "blue", "yellow"), border=NA)

# Save plot
setEPS()
postscript(file = "Figures/3domains_alldepths_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph,
     vertex.label=NA,
     layout=layout_with_graphopt(grph),
     vertex.color=domain_color)
title("SpiecEasi Network: All domains, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "Eukarya", "No Blast Hit"),
       fill=c("red", "green", "blue", "yellow"), border=NA)
dev.off()
quartz_off_screen 
                2 

Positive and negative edges separately

# Subset based on pos or neg edges
grph.pos <-delete.edges(grph, which(E(grph)$weight<0))
grph.neg <-delete.edges(grph, which(E(grph)$weight>0))

# For each subsetted graph, remove those nodes that are no longer connected to anything
grph.pos <- delete.vertices(grph.pos, which(degree(grph.pos)==0))
grph.neg <- delete.vertices(grph.neg, which(degree(grph.neg)==0))

# Make color vector for each
domain_color_pos <- dtype[as.numeric(as.factor(V(grph.pos)$"taxonomy-1"))]
domain_color_neg <- dtype[as.numeric(as.factor(V(grph.neg)$"taxonomy-1"))]

# Plot pos
plot(grph.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.pos),
     vertex.color=domain_color_pos)
title("SpiecEasi Network: All domains, Positive Edges, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "Eukarya", "No Blast Hit"),
       fill=c("red", "green", "blue", "yellow"), border=NA)


# Plot neg
plot(grph.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.neg),
     vertex.color=domain_color_neg)
title("SpiecEasi Network: All domains, Negative Edges, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "Eukarya", "No Blast Hit"),
       fill=c("red", "green", "blue", "yellow"), border=NA)


# Save plots
setEPS()
postscript(file = "Figures/3domains_alldepths_posedges_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.pos),
     vertex.color=domain_color_pos)
title("SpiecEasi Network: All domains, Positive Edges, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Bacteria","Archaea", "Eukarya"),
       fill=c("red","green","blue"), border=NA)
dev.off()
quartz_off_screen 
                2 
setEPS()
postscript(file = "Figures/3domains_alldepths_negedges_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.neg),
     vertex.color=domain_color_neg)
title("SpiecEasi Network: All domains, Negative Edges, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "Eukarya", "No Blast Hit"),
       fill=c("red", "green", "blue", "yellow"), border=NA)

dev.off()
quartz_off_screen 
                2 

2 Domain Network- All depths

Remove eukaryotes to see impact on network

#Extract adjacency matrix from spiecEasi output
adj.mat <- getRefit(se.2domains)
table(as.numeric(adj.mat))

    0     1 
28568  2408 
# Extract weighted adjacency
se.cor  <- cov2cor(as.matrix(getOptCov(se.2domains)))
weighted.adj.mat <- se.cor*getRefit(se.2domains)

#Convert to graph objects
grph.unweighted <- adj2igraph(adj.mat)
grph.2domains <- adj2igraph(weighted.adj.mat)


# Put back in species names
V(grph.2domains)$name <- rownames(twodomains_df)
# V(grph.2domains)

# Make size of nodes proportional to degree (number of connections)
V(grph.2domains)$size <- (degree(grph.2domains) + 1) # the +1 avoids size zero vertices

# Color edges by connection (positive or negative) 
# E(grph.2domains)$color <- custombluegreen
# E(grph.2domains)$color[E(grph.2domains)$weight<0] <- customreddishpurple

# Change width of edges to be proportional to their weights
E(grph.2domains)$width <- abs(E(grph.2domains)$weight)*10

# Scale node sizes to be smaller
V(grph.2domains)$size <- V(grph.2domains)$size/2

# Remove low-weight edges (you decide what threshold is right for your network):
# weight_threshold <- 0.07
# grph.2domains <- delete.edges(grph.2domains,which(abs(E(grph.2domains)$weight)<weight_threshold))

# Join taxonomy data of each node
# Convert graph to datafram
grph.2domains_df <- igraph::as_data_frame(grph.2domains, 'both')
# make formatted taxonomy table for each domain
ps_bac_pruned_2domains_tax_table <- as.data.frame(tax_table(ps_bac_pruned_2domains)) %>%
  mutate(name = rownames(tax_table(ps_bac_pruned_2domains)))
ps_arch_2domains_pruned_tax_table <- as.data.frame(tax_table(ps_arch_pruned_2domains)) %>%
  mutate(name = rownames(tax_table(ps_arch_pruned_2domains)))
# link graph data frame to formatted taxonomy tables
bac_temp <- left_join(grph.2domains_df$vertices[1:ntaxa(ps_bac_pruned_2domains),],
ps_bac_pruned_2domains_tax_table, by = "name")
  # delete columns that don't match other tax tables
  bac_temp <- select(bac_temp, -"taxonomy-9", -"Refined taxonomy")
arch_temp <- left_join(grph.2domains_df$vertices[ntaxa(ps_bac_pruned_2domains)+1:ntaxa(ps_arch_pruned_2domains),], ps_arch_2domains_pruned_tax_table, by = "name")
# build full dataframe with all 3 domains
all_temp <- rbind(bac_temp, arch_temp)
# remake into graph
grph.2domains <- graph_from_data_frame(grph.2domains_df$edges,
                                   directed = F,
                                   vertices = all_temp)

# Make color paletter for domain
dtype = c("red", "green", "yellow")

# Make color vector
domain_color_2domains <- dtype[as.numeric(as.factor(V(grph.2domains)$"taxonomy-1"))]

# Plot
plot(grph.2domains,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains),
     vertex.color=domain_color_2domains)
title("SpiecEasi Network: Bacteria and Archaea, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "No blast hit"),
       fill=c("red","green", "yellow"), border=NA)

# Save plot
setEPS()
postscript(file = "Figures/2domains_alldepths_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.2domains,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains),
     vertex.color=domain_color_2domains)
title("SpiecEasi Network: Bacteria and Archaea, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "No blast hit"),
       fill=c("red","green", "yellow"), border=NA)
dev.off()
quartz_off_screen 
                2 

Positive and negative edges separately

# Subset based on pos or neg edges
grph.2domains.pos <-delete.edges(grph.2domains, which(E(grph.2domains)$weight<0))
grph.2domains.neg <-delete.edges(grph.2domains, which(E(grph.2domains)$weight>0))

# For each subsetted graph, remove those nodes that are no longer connected to anything
grph.2domains.pos <- delete.vertices(grph.2domains.pos, which(degree(grph.2domains.pos)==0))
grph.2domains.neg <- delete.vertices(grph.2domains.neg, which(degree(grph.2domains.neg)==0))

# Make color vector for each
domain_color_2domains_pos <- dtype[as.numeric(as.factor(V(grph.2domains.pos)$"taxonomy-1"))]
domain_color_2domains_neg <- dtype[as.numeric(as.factor(V(grph.2domains.neg)$"taxonomy-1"))]

# Plot pos
plot(grph.2domains.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains.pos),
     vertex.color=domain_color_2domains_pos)
title("SpiecEasi Network: Bacteria and Archaea, Positive Edges Only, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "No Blast Hit"),
       fill=c("green","red","yellow"), border=NA)


# Plot neg
plot(grph.2domains.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains.neg),
     vertex.color=domain_color_2domains_neg)
title("SpiecEasi Network: Bacteria and Archaea, Negative Edges Only, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "No Blast Hit"),
       fill=c("green","red","yellow"), border=NA)


# Save plots
setEPS()
postscript(file = "Figures/2domains_alldepths_posedges_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.2domains.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains.pos),
     vertex.color=domain_color_2domains_pos)
title("SpiecEasi Network: Bacteria and Archaea, Positive Edges Only, Whole Water Column")
legend("topright",bty = "n",
       legend=c("Archaea","Bacteria", "No Blast Hit"),
       fill=c("green","red","yellow"), border=NA)
dev.off()
quartz_off_screen 
                2 
setEPS()
postscript(file = "Figures/2domains_alldepths_negedges_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.2domains.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains.neg),
     vertex.color=domain_color_2domains_neg)
title("SpiecEasi Network: Bacteria and Archaea, Negative Edges Only, Whole Water Column")
legend("topright",bty = "n",
        legend=c("Archaea","Bacteria", "No Blast Hit"),
       fill=c("green","red","yellow"), border=NA)
dev.off()
quartz_off_screen 
                2 

3 Domain Network- Oxycline

#Extract adjacency matrix from spiecEasi output
adj.mat <- getRefit(se.oxycline)
table(as.numeric(adj.mat))

    0     1 
35241  1240 
# Extract weighted adjacency
se.cor  <- cov2cor(as.matrix(getOptCov(se.oxycline)))
weighted.adj.mat <- se.cor*getRefit(se.oxycline)

#Convert to graph objects
grph.unweighted.oxycline <- adj2igraph(adj.mat)
grph.oxycline <- adj2igraph(weighted.adj.mat)


# Put back in species names
V(grph.oxycline)$name <- rownames(alldomains_df_oxycline)
# V(grph.oxycline)

# Make size of nodes proportional to degree (number of connections)
V(grph.oxycline)$size <- (degree(grph.oxycline) + 1) # the +1 avoids size zero vertices

# Color edges by connection (positive or negative) 
# E(grph.oxycline)$color <- custombluegreen
# E(grph.oxycline)$color[E(grph.oxycline)$weight<0] <- customreddishpurple

# Change width of edges to be proportional to their weights
E(grph.oxycline)$width <- abs(E(grph.oxycline)$weight)*10

# Scale node sizes to be smaller
V(grph.oxycline)$size <- V(grph.oxycline)$size/2

# Remove low-weight edges (you decide what threshold is right for your network):
# weight_threshold <- 0.07
# grph.oxycline <- delete.edges(grph.oxycline,which(abs(E(grph.oxycline)$weight)<weight_threshold))

# Join taxonomy data of each node
# Convert graph to datafram
grph.oxycline_df <- igraph::as_data_frame(grph.oxycline, 'both')
# make formatted taxonomy table for each domain
ps_bac_oxycline_pruned_tax_table <- as.data.frame(tax_table(ps_bac_oxycline_pruned)) %>%
  mutate(name = rownames(tax_table(ps_bac_oxycline_pruned)))
ps_arch_oxycline_pruned_tax_table <- as.data.frame(tax_table(ps_arch_oxycline_pruned)) %>%
  mutate(name = rownames(tax_table(ps_arch_oxycline_pruned)))
ps_euk_oxycline_pruned_tax_table <- as.data.frame(tax_table(ps_euk_oxycline_pruned)) %>%
  mutate(name = rownames(tax_table(ps_euk_oxycline_pruned)))
# link graph data frame to formatted taxonomy tables
bac_temp <- left_join(grph.oxycline_df$vertices[1:ntaxa(ps_bac_oxycline_pruned),],
ps_bac_oxycline_pruned_tax_table, by = "name")
  # delete columns that don't match other tax tables
  bac_temp <- select(bac_temp, -"taxonomy-9", -"Refined taxonomy")
arch_temp <- left_join(grph.oxycline_df$vertices[ntaxa(ps_bac_oxycline_pruned)+1:ntaxa(ps_arch_oxycline_pruned),],ps_arch_oxycline_pruned_tax_table, by = "name")
euk_temp <- left_join(grph.oxycline_df$vertices[ntaxa(ps_bac_oxycline_pruned)+ntaxa(ps_arch_oxycline_pruned)+1:ntaxa(ps_euk_oxycline_pruned),], ps_euk_oxycline_pruned_tax_table, by = "name")
  # rename column names in euk table to match others
  euk_temp <- euk_temp %>%
    rename("taxonomy-1" = Kingdom, "taxonomy-2" = Supergroup, "taxonomy-3" = Division, "taxonomy-4" = Class, "taxonomy-5" = Order, "taxonomy-6" = Family, "taxonomy-7" = Genus, "taxonomy-8" = Species)
# build full dataframe with all 3 domains
all_temp <- rbind(bac_temp, arch_temp, euk_temp)
# remake into graph
grph.oxycline <- graph_from_data_frame(grph.oxycline_df$edges,
                                   directed = F,
                                   vertices = all_temp)

# Make color paletter for domain
dtype = c("red", "green", "blue")
# Make color vector
domain_color_oxycline <- dtype[as.numeric(as.factor(V(grph.oxycline)$"taxonomy-1"))]


# Plot
plot(grph.oxycline,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline),
     vertex.color=domain_color_oxycline)
title("SpiecEasi Network: All domains, Oxycline")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)

# Save plot
setEPS()
postscript(file = "Figures/3domains_oxycline_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.oxycline,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline),
     vertex.color=domain_color_oxycline)
title("SpiecEasi Network: All domains, Oxycline")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)
dev.off()
quartz_off_screen 
                2 

Positive and negative edges separately

# Subset based on pos or neg edges
grph.oxycline.pos <-delete.edges(grph.oxycline, which(E(grph.oxycline)$weight<0))
grph.oxycline.neg <-delete.edges(grph.oxycline, which(E(grph.oxycline)$weight>0))

# For each subsetted graph, remove those nodes that are no longer connected to anything
grph.oxycline.pos <- delete.vertices(grph.oxycline.pos, which(degree(grph.oxycline.pos)==0))
grph.oxycline.neg <- delete.vertices(grph.oxycline.neg, which(degree(grph.oxycline.neg)==0))

# Make color vector for each
domain_color_oxycline_pos <- dtype[as.numeric(as.factor(V(grph.oxycline.pos)$"taxonomy-1"))]
domain_color_oxycline_neg <- dtype[as.numeric(as.factor(V(grph.oxycline.neg)$"taxonomy-1"))]

# Plot pos
plot(grph.oxycline.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline.pos),
     vertex.color=domain_color_oxycline_pos)
title("SpiecEasi Network: All domains, Positive Edges only, Oxycline")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)


# Plot neg
plot(grph.oxycline.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline.neg),
     vertex.color=domain_color_oxycline_neg)
title("SpiecEasi Network: All domains, Negative Edges Only, Oxycline")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)


# Save plots
setEPS()
postscript(file = "Figures/3domains_oxycline_posedges_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.oxycline.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline.pos),
     vertex.color=domain_color_oxycline_pos)
title("SpiecEasi Network: All domains, Positive Edges Only, Oxycline")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)
dev.off()
quartz_off_screen 
                2 
setEPS()
postscript(file = "Figures/3domains_oxycline_negedges_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.oxycline.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline.neg),
     vertex.color=domain_color_oxycline_neg)
title("SpiecEasi Network: All domains, Negative Edges Only, Oxycline")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)

dev.off()
quartz_off_screen 
                2 

3 Domain Network- Anoxic

#Extract adjacency matrix from spiecEasi output
adj.mat <- getRefit(se.anoxic)
table(as.numeric(adj.mat))

   0    1 
7566  178 
# Extract weighted adjacency
se.cor  <- cov2cor(as.matrix(getOptCov(se.anoxic)))
weighted.adj.mat <- se.cor*getRefit(se.anoxic)

#Convert to graph objects
grph.unweighted.anoxic <- adj2igraph(adj.mat)
grph.anoxic <- adj2igraph(weighted.adj.mat)


# Put back in species names
V(grph.anoxic)$name <- rownames(alldomains_df_anoxic)
# V(grph.anoxic)


# Make size of nodes proportional to degree (number of connections)
V(grph.anoxic)$size <- (degree(grph.anoxic) + 1) # the +1 avoids size zero vertices

# Color edges by connection (positive or negative) 
# E(grph.anoxic)$color <- custombluegreen
# E(grph.anoxic)$color[E(grph.anoxic)$weight<0] <- customreddishpurple

# Change width of edges to be proportional to their weights
E(grph.anoxic)$width <- abs(E(grph.anoxic)$weight)*10

# Scale node sizes to be smaller
V(grph.anoxic)$size <- V(grph.anoxic)$size/2

# Remove low-weight edges (you decide what threshold is right for your network):
# weight_threshold <- 0.07
# grph.anoxic <- delete.edges(grph.anoxic,which(abs(E(grph.anoxic)$weight)<weight_threshold))

# Join taxonomy data of each node
# Convert graph to datafram
grph.anoxic_df <- igraph::as_data_frame(grph.anoxic, 'both')
# make formatted taxonomy table for each domain
ps_bac_anoxic_pruned_tax_table <- as.data.frame(tax_table(ps_bac_anoxic_pruned)) %>%
  mutate(name = rownames(tax_table(ps_bac_anoxic_pruned)))
ps_arch_anoxic_pruned_tax_table <- as.data.frame(tax_table(ps_arch_anoxic_pruned)) %>%
  mutate(name = rownames(tax_table(ps_arch_anoxic_pruned)))
ps_euk_anoxic_pruned_tax_table <- as.data.frame(tax_table(ps_euk_anoxic_pruned)) %>%
  mutate(name = rownames(tax_table(ps_euk_anoxic_pruned)))
# link graph data frame to formatted taxonomy tables
bac_temp <- left_join(grph.anoxic_df$vertices[1:ntaxa(ps_bac_anoxic_pruned),],ps_bac_anoxic_pruned_tax_table, by = "name")
  # delete columns that don't match other tax tables
  bac_temp <- select(bac_temp, -"taxonomy-9", -"Refined taxonomy")
arch_temp <- left_join(grph.anoxic_df$vertices[ntaxa(ps_bac_anoxic_pruned)+1:ntaxa(ps_arch_anoxic_pruned),],ps_arch_anoxic_pruned_tax_table, by = "name")
euk_temp <- left_join(grph.anoxic_df$vertices[ntaxa(ps_bac_anoxic_pruned)+ntaxa(ps_arch_anoxic_pruned)+1:ntaxa(ps_euk_anoxic_pruned),], ps_euk_anoxic_pruned_tax_table, by = "name")
  # rename column names in euk table to match others
  euk_temp <- euk_temp %>%
    rename("taxonomy-1" = Kingdom, "taxonomy-2" = Supergroup, "taxonomy-3" = Division, "taxonomy-4" = Class, "taxonomy-5" = Order, "taxonomy-6" = Family, "taxonomy-7" = Genus, "taxonomy-8" = Species)
# build full dataframe with all 3 domains
all_temp <- rbind(bac_temp, arch_temp, euk_temp)
# remake into graph
grph.anoxic <- graph_from_data_frame(grph.anoxic_df$edges,
                                   directed = F,
                                   vertices = all_temp)

# Make color paletter for domain
dtype = c("red", "green", "blue")
# Make color vector
domain_color_anoxic <- dtype[as.numeric(as.factor(V(grph.anoxic)$"taxonomy-1"))]

# Plot
plot(grph.anoxic,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic),
     vertex.color=domain_color_anoxic)
title("SpiecEasi Network: All domains, Anoxic Layer")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)

# Save plot
setEPS()
postscript(file = "Figures/3domains_anoxic_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.anoxic,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic),
     vertex.color=domain_color_anoxic)
title("SpiecEasi Network: All domains, Anoxic")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)
dev.off()
quartz_off_screen 
                2 

Positive and negative edges separately

# Subset based on pos or neg edges
grph.anoxic.pos <-delete.edges(grph.anoxic, which(E(grph.anoxic)$weight<0))
grph.anoxic.neg <-delete.edges(grph.anoxic, which(E(grph.anoxic)$weight>0))

# For each subsetted graph, remove those nodes that are no longer connected to anything
grph.anoxic.pos <- delete.vertices(grph.anoxic.pos, which(degree(grph.anoxic.pos)==0))
grph.anoxic.neg <- delete.vertices(grph.anoxic.neg, which(degree(grph.anoxic.neg)==0))

# Make color vector for each
domain_color_anoxic_pos <- dtype[as.numeric(as.factor(V(grph.anoxic.pos)$"taxonomy-1"))]
domain_color_anoxic_neg <- dtype[as.numeric(as.factor(V(grph.anoxic.neg)$"taxonomy-1"))]

# Plot pos
plot(grph.anoxic.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic.pos),
     vertex.color=domain_color_anoxic_pos)
title("SpiecEasi Network: All domains, Positive Edges only, anoxic")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)


# Plot neg
plot(grph.anoxic.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic.neg),
     vertex.color=domain_color_anoxic_neg)
title("SpiecEasi Network: All domains, Negative Edges Only, anoxic")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)


# Save plots
setEPS()
postscript(file = "Figures/3domains_anoxic_posedges_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.anoxic.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic.pos),
     vertex.color=domain_color_anoxic_pos)
title("SpiecEasi Network: All domains, Positive Edges Only, Shallow Anoxic")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)
dev.off()
quartz_off_screen 
                2 
setEPS()
postscript(file = "Figures/3domains_anoxic_negedges_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.anoxic.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic.neg),
     vertex.color=domain_color_anoxic_neg)
title("SpiecEasi Network: All domains, Negative Edges Only, Shallow Anoxic")
legend("topright",bty = "n",
       legend=c("Archaea", "Bacteria", "Eukarya"),
       fill=c("red","green","blue"), border=NA)
dev.off()
quartz_off_screen 
                2 

Summary Network figure

# Set up in panels
op <- par(oma=c(2,.5,.5,0),# Room for the titles and legend
  mfrow=c(4,2),
  mai=c(.15,.3,.15,.1))
# Panel 1- All depths, positive network
plot(grph.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.pos),
     vertex.color=domain_color_pos)
mtext ("Positive", side = 3, outer = TRUE, line = -1, adj = 0.22, cex = .8)
mtext("All depths", side=2, cex = .8, line = 1.5)
# Panel 2- All depths, negative network
plot(grph.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.neg),
     vertex.color=domain_color_neg)
mtext ("Negative", side = 3, outer = TRUE, line = -1, adj = .8, cex = .8)
# Panel 3- Oxycline, positive network
plot(grph.oxycline.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline.pos),
     vertex.color=domain_color_oxycline_pos)
mtext("Oxycline", side=2, cex = .8, line = 1.5)
# Panel 4- Oxycline, negative network
plot(grph.oxycline.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline.neg),
     vertex.color=domain_color_oxycline_neg)
# Panel 5- Anoxic, positive network
plot(grph.anoxic.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic.pos),
     vertex.color=domain_color_anoxic_pos)
mtext("Anoxic", side=2, cex = .8, line = 1.5)
# Panel 6- Anoxic, negative network
plot(grph.anoxic.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic.neg),
     vertex.color=domain_color_anoxic_neg)
# Panel 7- 2 Domains, positive network
plot(grph.2domains.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains.pos),
     vertex.color=domain_color_2domains_pos)
mtext("Prok Only", side=2, cex = .8, line = 1.5)
# Panel 8- 2 Domains, negative network
plot(grph.2domains.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains.pos),
     vertex.color=domain_color_2domains_neg)
# Add legend
par(op) # Leave the last plot
op <- par(usr=c(0,1,0,1), # Reset the coordinates
          xpd=NA)         # Allow plotting outside the plot region
legend(.15,-0.04, c("Archaea","Bacteria", "Eukarya", "No Blast Hit"), col=c("red", "green", "blue", "yellow"), pch = c(16), box.col=NA, cex = .8, horiz = T, x.intersp = c(0.3))




# Save figure
# Set up EPS and make plot
setEPS(width = 6, height = 9)
postscript("Figures/Networks_pos_neg.eps")
op <- par(oma=c(2,.5,.5,0),# Room for the titles and legend
  mfrow=c(4,2),
  mai=c(.15,.3,.15,.1))
# Panel 1- All depths, positive network
plot(grph.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.pos),
     vertex.color=domain_color_pos)
mtext ("Positive", side = 3, outer = TRUE, line = -1, adj = 0.22, cex = .8)
mtext("All depths", side=2, cex = .8, line = 1.5)
# Panel 2- All depths, negative network
plot(grph.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.neg),
     vertex.color=domain_color_neg)
mtext ("Negative", side = 3, outer = TRUE, line = -1, adj = .8, cex = .8)
# Panel 3- Oxycline, positive network
plot(grph.oxycline.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline.pos),
     vertex.color=domain_color_oxycline_pos)
mtext("Oxycline", side=2, cex = .8, line = 1.5)
# Panel 4- Oxycline, negative network
plot(grph.oxycline.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline.neg),
     vertex.color=domain_color_oxycline_neg)
# Panel 5- Anoxic, positive network
plot(grph.anoxic.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic.pos),
     vertex.color=domain_color_anoxic_pos)
mtext("Anoxic", side=2, cex = .8, line = 1.5)
# Panel 6- Anoxic, negative network
plot(grph.anoxic.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic.neg),
     vertex.color=domain_color_anoxic_neg)
# Panel 7- 2 Domains, positive network
plot(grph.2domains.pos,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains.pos),
     vertex.color=domain_color_2domains_pos)
mtext("Prok Only", side=2, cex = .8, line = 1.5)
# Panel 8- 2 Domains, negative network
plot(grph.2domains.neg,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.2domains.pos),
     vertex.color=domain_color_2domains_neg)
# Add legend
par(op) # Leave the last plot
op <- par(usr=c(0,1,0,1), # Reset the coordinates
          xpd=NA)         # Allow plotting outside the plot region
legend(.15,-0.04, c("Archaea","Bacteria", "Eukarya", "No Blast Hit"), col=c("red", "green", "blue", "yellow"), pch = c(16), box.col=NA, cex = .8, horiz = T, x.intersp = c(0.3))
dev.off()
quartz_off_screen 
                2 

Collect network parameters

Number of edges

the number of edges and how many are positive vs negative

# total number of edges in full dataset network
length(E(grph)$weight)
[1] 2840
# percent of neg edges 
(sum(E(grph)$weight<0)/length(E(grph)$weight))*100
[1] 34.40141
# total number of edges in 2-domain dataset network
length(E(grph.2domains)$weight)
[1] 1204
# percent of neg edges 
(sum(E(grph.2domains)$weight<0)/length(E(grph.2domains)$weight))*100
[1] 34.5515
# total number of edges in oxycline network
length(E(grph.oxycline)$weight)
[1] 620
# percent of neg edges 
(sum(E(grph.oxycline)$weight<0)/length(E(grph.oxycline)$weight))*100
[1] 37.74194
# total number of edges in anoxic network
length(E(grph.anoxic)$weight)
[1] 89
# percent of neg edges 
(sum(E(grph.anoxic)$weight<0)/length(E(grph.anoxic)$weight))*100
[1] 34.83146

Declining number of total edges going from full dataset –> oxycline only –> anoxic only. But the percentage of negative associations is similar (34.4-37.7%). Most associations (~65%) in each network are positive.

Edge density

the number of edges relatives to total number of possible edges

edge_density(grph)*100
[1] 6.374717
edge_density(grph.2domains)*100
[1] 7.818182
edge_density(grph.oxycline)*100
[1] 3.416919
edge_density(grph.anoxic)*100
[1] 2.324974

The full dataset has the highest edge density, then oxycline, then anoxic

Component

The size of the components, or “clumps,” in the network, and how many members in each

# full dataset
components(grph)$no
[1] 27
components(grph)$csize
 [1] 262   9   1   1   1   1   1   1   2   1   1   1   1   1   1   1   1   3   1   1   1   1   1   1
[25]   1   1   1
# 2 domains
components(grph.2domains)$no
[1] 30
components(grph.2domains)$csize
 [1] 131   1   1   2   1   1   1   1   1   1   1   3   1   1   1   1   1   1   1   1   1   1   1   6
[25]   1   9   1   1   1   1
# oxycline
components(grph.oxycline)$no
[1] 32
components(grph.oxycline)$csize
 [1] 144   1   2   1   1   1   1   1   1   1   1   1   1   1   2   2  11   1   2   1   1   1   1   1
[25]   1   1   1   1   3   1   1   1
# anoxic
components(grph.anoxic)$no
[1] 48
components(grph.anoxic)$csize
 [1] 24  2  2  1  1  1  1  1  1  1  4  1  1  1  1  2  1  3  3  1  1  2  1  1  1  1  1  1  1  1  1  1
[33]  1  1  1  1  1  1  1  4  1  1  1  4  1  1  1  1

The anoxic network is most disjointed, with 48 clumps and the largest containing only 24 members. The next is oxycline, with 32 clumps and the largest with 144 members. Then the full dataset has only 27 clumps and the largest clump contains 262 members.

Average path length

Path is the shortest distance between two nodes (fewest number of edges). Average path length of a network gives a sense of how connected every node is to another. Unconnected hubs in the netowrk will have “infinite” paths from other hubs. The function mean_distance ignores the infinite edges and calculates the average of all other edges

mean_distance(grph)
[1] 3.353247
mean_distance(grph.2domains)
[1] 3.11972
mean_distance(grph.oxycline)
[1] 3.976057
mean_distance(grph.anoxic)
[1] 2.703947

The longest average path length is in the oxycline, followed by the whole dataset and then anoxic. Meaning the nodes in the anoxic are more closely associated with each other. Even though there are more hubs in anoxic, as shown above, the nodes in the hubs are close to each other. The oxycline hubs have the longest average distances between nodes.

Which ASVs are in positive vs negative networks?

# Positive network- full dataset
grph.pos_df <- igraph::as_data_frame(grph.pos, 'both')
grph.pos_df_vert <- grph.pos_df$vertices
# How many Syndiniales and Polycystinea?
as.data.frame(table(grph.pos_df_vert$"taxonomy-5"))
# 37 Dino-Group-II
# 3 Dino-Group-I
# 34 Spumellarida

# Negative network- full dataset
grph.neg_df <- igraph::as_data_frame(grph.neg, 'both')
grph.neg_df_vert <- grph.neg_df$vertices
# How many Syndiniales and Polycystinea?
as.data.frame(table(grph.neg_df_vert$"taxonomy-5"))
# 18 Dino-Group-II
# 4 Dino-Group-I
# 17 Spumellarida



# Positive network- oxycline
grph.oxycline.pos_df <- igraph::as_data_frame(grph.oxycline.pos, 'both')
grph.oxycline.pos_df_vert <- grph.oxycline.pos_df$vertices
# How many Syndiniales and Polycystinea?
as.data.frame(table(grph.oxycline.pos_df_vert$"taxonomy-5"))
# 34 Dino-Group-II
# 1 Dino-Group-I
# 24 Spumellarida

# Negative network- oxycline
grph.oxycline.neg_df <- igraph::as_data_frame(grph.oxycline.neg, 'both')
grph.oxycline.neg_df_vert <- grph.oxycline.neg_df$vertices
# How many Syndiniales and Polycystinea?
as.data.frame(table(grph.oxycline.neg_df_vert$"taxonomy-5"))
# 24 Dino-Group-II
# 1 Dino-Group-I
# 9 Spumellarida



# Positive network- anoxic
grph.anoxic.pos_df <- igraph::as_data_frame(grph.anoxic.pos, 'both')
grph.anoxic.pos_df_vert <- grph.anoxic.pos_df$vertices
# How many Syndiniales and Polycystinea?
as.data.frame(table(grph.anoxic.pos_df_vert$"taxonomy-5"))
# 0 Dino-Group-II
# 1 Dino-Group-I
# 8 Spumellarida

# Negative network- anoxic
grph.anoxic.neg_df <- igraph::as_data_frame(grph.anoxic.neg, 'both')
grph.anoxic.neg_df_vert <- grph.anoxic.neg_df$vertices
# How many Syndiniales and Polycystinea?
as.data.frame(table(grph.anoxic.neg_df_vert$"taxonomy-5"))
# 0 Dino-Group-II
# 0 Dino-Group-I
# 0 Spumellarida

What are the interestined eukaryotic groups connected to?

# Positive associations: Syndiniales
# Pull out names of Syndiniales and Spumellarida ASVs
grph.pos_df_vert_synd <- filter(grph.pos_df_vert, `taxonomy-4` == "Syndiniales")
# filter graph to include only edges connected to those nodes
grph.pos_synd_edges <- E(grph.pos)[from(grph.pos_df_vert_synd$name)] # get edges
grph.pos.synd_subgraph <- subgraph.edges(grph.pos, grph.pos_synd_edges) # filter graph
# get taxonomy of remaining nodes, removing the Syndiniales from table (eg. only connected nodes) and grouping by taxonomy
grph.pos_df_vert %>%
  filter(`name` %in% V(grph.pos.synd_subgraph)$name & !`taxonomy-4` %in% c("Syndiniales")) %>%
  count(`taxonomy-2`,`taxonomy-3`,`taxonomy-4`,`taxonomy-5`, name = "count", sort = TRUE)

# Positive associations: Spumellarida
grph.pos_df_vert_spum <- filter(grph.pos_df_vert, `taxonomy-5` == "Spumellarida")
grph.pos_spum_edges <- E(grph.pos)[from(grph.pos_df_vert_spum$name)] 
grph.pos.spum_subgraph <- subgraph.edges(grph.pos, grph.pos_spum_edges) 
grph.pos_df_vert  %>% 
  filter(`name` %in% V(grph.pos.spum_subgraph)$name & !`taxonomy-5` %in% c("Spumellarida")) %>%
  count(`taxonomy-2`,`taxonomy-3`,`taxonomy-4`,`taxonomy-5`, name = "count", sort = TRUE)

# Negative associations: Syndiniales
grph.neg_df_vert_synd <- filter(grph.neg_df_vert, `taxonomy-4` == "Syndiniales")
grph.neg_synd_edges <- E(grph.neg)[from(grph.neg_df_vert_synd$name)] 
grph.neg.synd_subgraph <- subgraph.edges(grph.neg, grph.neg_synd_edges) 
grph.neg_df_vert %>%
  filter(`name` %in% V(grph.neg.synd_subgraph)$name & !`taxonomy-4` %in% c("Syndiniales")) %>%
  count(`taxonomy-2`,`taxonomy-3`,`taxonomy-4`,`taxonomy-5`, name = "count", sort = TRUE)

# Negative associations: Spumellarida
grph.neg_df_vert_spum <- filter(grph.neg_df_vert, `taxonomy-5` == "Spumellarida")
grph.neg_spum_edges <- E(grph.neg)[from(grph.neg_df_vert_spum$name)] 
grph.neg.spum_subgraph <- subgraph.edges(grph.neg, grph.neg_spum_edges) 
grph.neg_df_vert %>% 
  filter(`name` %in% V(grph.neg.spum_subgraph)$name & !`taxonomy-5` %in% c("Spumellarida")) %>%
  count(`taxonomy-2`,`taxonomy-3`,`taxonomy-4`,`taxonomy-5`, name = "count", sort = TRUE)


# Also ckeck out Cariacotrichea to hypothesize about possible symbiosis partners
# Positive associations only
grph.pos_df_vert_cari <- filter(grph.pos_df_vert, `taxonomy-4` == "Cariacotrichea")
grph.pos_cari_edges <- E(grph.pos)[from(grph.pos_df_vert_cari$name)] 
grph.pos.cari_subgraph <- subgraph.edges(grph.pos, grph.pos_cari_edges) 
grph.pos_df_vert %>% 
  filter(`name` %in% V(grph.pos.cari_subgraph)$name & !`taxonomy-4` %in% c("Cariacotrichea"))%>%
  count(`taxonomy-2`,`taxonomy-3`,`taxonomy-4`,`taxonomy-5`, name = "count", sort = TRUE)

Analyze node-level measures for keystone-ness

Calculate 4 parameters for each individual node:

  • degree (de) = number of edges connected to the node
  • strength (st) = sum of the weights of all the edges connected to the node
  • betweenness centrality (be) = the number of shortest paths (between all other nodes) that go through the node
  • closeness centrality (cc) = average distance (number of edges) of the node to any other node
  • local clustering coefficient (l.cluster) , aka transitivity = the proportion of two nodes that are connected by a third node, of being connected to each other (eg. how many of a node’s “friends” know each other?)
# First change the weights of the edges (the strength of association) to absolute value. This won't work if negative edge weights are left with the negative signs
E(grph)$weight <- abs(E(grph)$weight)

# calculate parameters
names=V(grph)$name
de=degree(grph)
st=graph.strength(grph)
be=betweenness(grph, normalized=T)
cc = closeness(grph)
At centrality.c:2617 :closeness centrality is not well-defined for disconnected graphs
l.cluster=transitivity(grph, "local")


# assemble dataset and match full taxonomy
fulldateset_node_measures <- data.frame(ID=names, degree=de, strength=st, betweenness=be, closeness = cc, clustering_coefficient = l.cluster) 

# Put back bac taxaonomy
temp1 <- left_join(fulldateset_node_measures[1:dim(otu_table(ps_bac_pruned_3domains))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
# delete "Taxonomy-9" and "refined Taxonomy" columns 
temp1 <- select(temp1, -"taxonomy-9", -"Refined taxonomy")


temp2 <- left_join(fulldateset_node_measures[sum(dim(otu_table(ps_bac_pruned_3domains))[1],1):sum(dim(otu_table(ps_bac_pruned_3domains))[1],dim(otu_table(ps_arch_pruned_3domains))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 


temp3 <- left_join(fulldateset_node_measures[sum(dim(otu_table(ps_arch_pruned_3domains))[1], dim(otu_table(ps_bac_pruned_3domains))[1],1):sum(dim(otu_table(ps_arch_pruned_3domains))[1], dim(otu_table(ps_bac_pruned_3domains))[1],dim(otu_table(ps_euk_pruned_3domains))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
# Rename col names to match those from Bac and Arch
temp3 <- temp3 %>%
  rename("taxonomy-1" = Kingdom, "taxonomy-2" = Supergroup, "taxonomy-3" = Division, "taxonomy-4" = Class, "taxonomy-5" = Order, "taxonomy-6" = Family, "taxonomy-7" = Genus, "taxonomy-8" = Species)

# combine back all 3 domains, with new names as row names in a dataframe
fulldateset_node_measures <- rbind(temp1, temp2, temp3)
fulldateset_node_measures

Plot betweeness vs degree for each node. - Tipton et al. argue that nodes with high betweenness are “bottlenecks” or important connectors and nodes with high degree are “hubs” - Berry et al. argue that nodes with low betweenness, high degree, high closeness, and high transitivity are candidate keystone species - Add in closeness into the node’s plotly label since these don’t vary much node-to-node and wouldn’t make sense to plot

# replace NA in taxonomy with unidentified 
# remove nodes with 0 betweenness (can't calculate log10 of 0)
# replace NaN clustering coefs with 0
fulldateset_node_measures <- fulldateset_node_measures %>% 
  replace(is.na(.), "unidentified") %>%
  filter(!betweenness == 0) 

# get enough colors and randomly rearrange so they are easier to separate on the plot
mycolors <- colorRampPalette(brewer.pal(12, "Paired"))(length(unique(fulldateset_node_measures$`taxonomy-3`)))
set.seed(123)
mycolors <-  sample(mycolors)

# plot with plotly and so I can hover over points and determine which taxa they are
p <- ggplot(fulldateset_node_measures, aes(x = degree, y = betweenness, ID = ID, shape = `taxonomy-1`, `taxonomy-2` = `taxonomy-2`, color = `taxonomy-3`, `taxonomy-4` = `taxonomy-4`, `taxonomy-5` = `taxonomy-5`)) +
  geom_point(size = 4) +
  scale_y_continuous(trans='log10') +
  scale_color_manual(values = mycolors) +
  theme(legend.title = element_blank()) +
  theme_bw()
p

ggplotly(p, tooltip = c("ID","taxonomy-2", "taxonomy-3", "taxonomy-4", "taxonomy-5"))
NA

Make static figure for manuscript


p2 <- ggplot(fulldateset_node_measures, aes(x = degree, y = betweenness, shape = `taxonomy-1`, color = `taxonomy-3`)) +
  geom_point(size = 4) +
  scale_y_continuous(trans='log10') +
  scale_color_manual(values = mycolors, name = "") +
  scale_shape_manual(values = c(19,17,15,18), name = "") +
  theme(legend.title=element_blank(),
        axis.text = element_text(size=8),
        axis.text.x = element_text(size=8, angle = 45, hjust = 1),
        axis.title = element_text(size=8),
        legend.text = element_text(size=8),
        strip.text = element_text(size = 8),
        legend.margin=margin(0,0,0,2),
        legend.box.margin=margin(-10,-10,-10,-10),
        plot.margin=grid::unit(c(0,0,0,0), "mm")) +
  theme_bw() 
p2

ggsave("figures/betweenness_vs_degree.eps",p2, width = 10, height = 6, units = c("in"))

How many edges does each group have as an aggregate

de_df <- as.data.frame(de)
de_df$name <- rownames(de_df)
de_df <- left_join(de_df, all_temp, by = "name")

de_df %>%
  group_by(`taxonomy-1`) %>%
  summarise(degreesum = sum(de)) %>%
  arrange(desc(degreesum))

de_df %>%
  group_by(`taxonomy-2`) %>%
  summarise(degreesum = sum(de)) %>%
  arrange(desc(degreesum))

de_df %>%
  group_by(`taxonomy-3`) %>%
  summarise(degreesum = sum(de)) %>%
  arrange(desc(degreesum))

de_df %>%
  group_by(`taxonomy-4`) %>%
  summarise(degreesum = sum(de)) %>%
  arrange(desc(degreesum))

de_df %>%
  group_by(`taxonomy-5`) %>%
  summarise(degreesum = sum(de)) %>%
  arrange(desc(degreesum))
NA

Save and re-load environment

save.image("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_nodelevelmeasures.RData")

Or load if coming back

load("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_nodelevelmeasures.RData")
LS0tCnRpdGxlOiAiRWNvbG9naWNhbGwgQW5hbHlzaXMgb2YgQ2FyaWFjbyBFdWthcnlvdGljIDE4UyBsaWJyYXJpZXMiCmF1dGhvcjogIkxpeiBTdXRlciIKZGF0ZTogIlN0YXJ0ZWQgNS8yNi8yMDIwOyBDb21wbGV0ZWQgc3VtbWVyIDIwMjEiCm91dHB1dDogaHRtbF9ub3RlYm9vawplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCiMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwaHlsb3NlcSkKbGlicmFyeShwaGFuZ29ybikKbGlicmFyeShyZWFkcikKbGlicmFyeShhcGUpCmxpYnJhcnkodmVnYW4pCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KG1pY3JvYmlvbWUpCmxpYnJhcnkoY29tcG9zaXRpb25zKQpsaWJyYXJ5KFNwaWVjRWFzaSkKbGlicmFyeShvdHVTdW1tYXJ5KQpsaWJyYXJ5KHBzeWNoKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGVnZykKbGlicmFyeShnZ3ZlZ2FuKQoKCiMgSGVscGVyIGZ1bmN0aW9ucyBmcm9tIEouIENyYW0gaHR0cHM6Ly9iaW92Y25ldC5naXRodWIuaW8vX3BhZ2VzL05ldHdvcmtTY2llbmNlX1NwYXJDQy5uYgpwYXNzIDwtIGZ1bmN0aW9uKHgpe3h9IAojIEdldCBsb3dlciB0cmlhbmdsZSBvZiB0aGUgY29ycmVsYXRpb24gbWF0cml4CiAgZ2V0X2xvd2VyX3RyaTwtZnVuY3Rpb24oY29ybWF0KXsKICAgIGNvcm1hdFt1cHBlci50cmkoY29ybWF0KV0gPC0gTkEKICAgIHJldHVybihjb3JtYXQpCiAgfQojIEdldCB1cHBlciB0cmlhbmdsZSBvZiB0aGUgY29ycmVsYXRpb24gbWF0cml4CiAgZ2V0X3VwcGVyX3RyaSA8LSBmdW5jdGlvbihjb3JtYXQpewogICAgY29ybWF0W2xvd2VyLnRyaShjb3JtYXQpXTwtIE5BCiAgICByZXR1cm4oY29ybWF0KQogIH0KCnJlb3JkZXJfY29ybWF0IDwtIGZ1bmN0aW9uKGNvcm1hdCl7CiMgVXNlIGNvcnJlbGF0aW9uIGJldHdlZW4gdmFyaWFibGVzIGFzIGRpc3RhbmNlCmRkIDwtIGFzLmRpc3QoKDEtY29ybWF0KS8yKQpoYyA8LSBoY2x1c3QoZGQpCmNvcm1hdCA8LWNvcm1hdFtoYyRvcmRlciwgaGMkb3JkZXJdCn0KCnJlb3JkZXJfY29yX2FuZF9wIDwtIGZ1bmN0aW9uKGNvcm1hdCwgcG1hdCl7CiAgZGQgPC0gYXMuZGlzdCgoMS1jb3JtYXQpLzIpCiAgaGMgPC0gaGNsdXN0KGRkKQogIGNvcm1hdCA8LWNvcm1hdFtoYyRvcmRlciwgaGMkb3JkZXJdCiAgcG1hdCA8LSBwbWF0W2hjJG9yZGVyLCBoYyRvcmRlcl0KICBsaXN0KHIgPSBjb3JtYXQsIHAgPSBwbWF0KQp9CgojIFJlcG9ydCB2ZXJzaW9ucyBvZiBwYWNrYWdlcwpzZXNzaW9uSW5mbygpCmBgYAoKLSBOb3RlIGZvciBTcGllY0Vhc2ksIGluc3RhbGxhdGlvbiBpbnN0cnVjdGlvbnMgYXJlIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vemRrMTIzL1NwaWVjRWFzaSkuIE9uIE1hYywgdGhlcmUgYXJlIGlzc3VlcyB3aXRoIGEgZGVwZW5kZW5jeSB3aGljaCBJIGFsc28gaGFkIHRvIHVwZGF0ZSB1c2luZyB0aGVzZSBbaW5zdHJ1Y3Rpb25zXShodHRwczovL3RoZWNvYXRsZXNzcHJvZmVzc29yLmNvbS9wcm9ncmFtbWluZy9jcHAvci1jb21waWxlci10b29scy1mb3ItcmNwcC1vbi1tYWNvcy8pCgoKIyBQcmVwYXJlIHRoZSBkYXRhCgojIyBJbXBvcnQgCgpNZXRhZGF0YToKYGBge3J9Cm1ldGFkYXRhIDwtIHJlYWRfY3N2KCJNZXRhZGF0YS5jc3YiKQpgYGAKCkltcG9ydCBTUkEgdGFibGUgYW5kIG1hdGNoIFNSQSBJRHMgd2l0aCBzYW1wbGUgSURzIGluIG1ldGFkYXRhIGZpbGUKYGBge3J9ClNSQVJ1blRhYmxlIDwtIHJlYWRfY3N2KCJzcmFfZGF0YS9TcmFSdW5UYWJsZS50eHQiKQptZXRhZGF0YSA8LSBsZWZ0X2pvaW4obWV0YWRhdGEsIFNSQVJ1blRhYmxlLCBieSA9ICdTYW1wbGUgTmFtZScpCmBgYAoKCgpEQURBMiByZXN1bHRzOgpgYGB7cn0KIyBJbXBvcnQgQ291bnQgdGFibGUuIFNraXAgZmlyc3Qgcm93IG9mIHRzdiBmaWxlLCB3aGljaCBpcyBqdXN0IHNvbWUgdGV4dApjb3VudF90YWJsZSA8LSByZWFkX3RzdihmaWxlPSJkYWRhMl9leHBvcnQvQVNWc19jb3VudHMudHN2IikKIyBBbmQgc3BlY2lmeSB0aGF0IHRoZSBmaXJzdCBjb2x1bW4gb2YgZGF0YSBhcmUgcm93bmFtZXMKY291bnRfdGFibGUgPC0gY29sdW1uX3RvX3Jvd25hbWVzKGNvdW50X3RhYmxlLCB2YXIgPSBjb2xuYW1lcyhjb3VudF90YWJsZSlbMV0pCgojIEltcG9ydCB0YXhvbm9teSBvZiBBU1ZzCnRheG9ub215IDwtIHJlYWRfdHN2KGZpbGU9ImRhZGEyX2V4cG9ydC9BU1ZzX3RheG9ub215LnRzdiIpCiMgQW5kIHNwZWNpZnkgdGhhdCB0aGUgZmlyc3QgY29sdW1uIG9mIGRhdGEgYXJlIHJvd25hbWVzCnRheG9ub215IDwtIGNvbHVtbl90b19yb3duYW1lcyh0YXhvbm9teSwgdmFyID0gY29sbmFtZXModGF4b25vbXkpWzFdKQpgYGAKCgojIyBRQyBhbmQgRmlsdGVyaW5nCgojIyMgUmFyZWZhY3Rpb24gY3VydmVzCgpgYGB7cn0KIyBVc2UgcmFyZWN1cnZlLCBmcm9tIHRoZSBWZWdhbiBwYWNrYWdlLiBSYXJjdXJ2ZSBleHBlY3RzIHRoZSBkYXRhc2V0IGFzIGEgZGF0YWZyYW1lIHNvIHdlIG5lZWQgdG8gdXNlIGFzLmRhdGEuZnJhbWUgYWdhaW46CmNvdW50X3RhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY291bnRfdGFibGUpCgojIFBsb3QgdGhlIHJhcmVmYWN0aW9uIGN1cnZlcywgY29sb3ItY29kaW5nIGJ5IHRoZSBjb2xvcnMgbGlzdGVkIGluIHNhbXBsZV9pbmZvX3RhYiwgd2hpY2ggaW5kaWNhdGUgc2FtcGxlIHR5cGUsIGFuZCB0cmFuc2Zvcm1pbmcgdXNpbmcgdCgpIGFnYWluCiMgUnVubmluZyB0aGlzIDUtMTAgc2FtcGxlcyBhdCBhIHRpbWUgYmVjYXVzZSBvdGhlcndpc2UgaXQgdGFrZXMgYSBsb25nIHRpbWUgdG8gcmVuZGVyCnJhcmVjdXJ2ZSh0KGNvdW50X3RhYmxlX2RmKSwgc3RlcD0xMDAsIGNleD0wLjUsIHlsYWI9IkFTVnMiLCBsYWJlbD1UKQpgYGAKCgojIyMgUmVtb3ZlIHNpbmdsZXRvbnMKCmBgYHtyfQpjb3VudF90YWJsZV9ub19zaW5nbGV0b25zIDwtIGZpbHRlcihjb3VudF90YWJsZSxyb3dTdW1zKGNvdW50X3RhYmxlKT4xKQojIHJldGFpbnMgYWxsIEFTVnMgKG91dCBvZiAxNDE3NikKYGBgCgphbmQgY2hhbmdlIHNhbXBsZSBuYW1lcyBmcm9tIE5DQkkgSUQgdG8gb3VyIGludGVybmFsIHNhbXBsZSBJRHMKYGBge3J9CiMgTW9kaWZ5IHRheGEgbmFtZXMgaW4gY291bnRfdGFibGVfbm9fc2luZ2xldG9ucywgd2hpY2ggYXJlIHRoZSBOQ0JJIFNSQSBudW1iZXJzLiBXYW50IHRvIHVzZSBvdXIgaW50ZXJuYWwgc2FtcGxlIGtleQprZXkgPC0gU1JBUnVuVGFibGUgJT4lIHNlbGVjdChSdW4sICdTYW1wbGUgTmFtZScpCgp4IDwtICh0KGNvdW50X3RhYmxlX25vX3NpbmdsZXRvbnMpKQp4IDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoeCwgUnVuID0gcm93bmFtZXMoeCkpKQoKeSA8LSB0KGxlZnRfam9pbih4LCBrZXksIGJ5ID0gIlJ1biIpKQpjb2xuYW1lcyh5KSA8LSB5WydTYW1wbGUgTmFtZScsXQp5IDwtIHlbICEocm93bmFtZXMoeSkgJWluJSBjKCdTYW1wbGUgTmFtZScsICdSdW4nKSksIF0KCmNvdW50X3RhYmxlXzIgPC0gdHlwZV9jb252ZXJ0KGFzLmRhdGEuZnJhbWUoeSkpCmBgYAoKIyMjIE1ha2UgUGh5bG8gVHJlZSAgClRoaXMgcHJvY2VzcyB0YWtlcyBhIExPTkcgdGltZSBzbyBydW4gb25jZSBhbmQgc2F2ZSAuUkRhdGEgb2JqZWN0CkluIHRoZSBEYWRhMiB0b29scywgdGhlcmUgYXJlIG5vIG9wdGlvbnMgdG8gYnVpbGQgYSB0cmVlICh1bmxpa2UgaW4gUWlpbWUyKSBidXQgd2UgY2FuIGJ1aWxkIGl0IGhlcmUgdXNpbmcgREVDSVBIRVIgYW5kIHBoYW5nb3JuCgooQmFzZWQgb24gW2h0dHBzOi8vZjEwMDByZXNlYXJjaC5jb20vYXJ0aWNsZXMvNS0xNDkyL3YyXShodHRwczovL2YxMDAwcmVzZWFyY2guY29tL2FydGljbGVzLzUtMTQ5Mi92MikpCgoKTWFrZSBhbiBhbGlnbm1lbnQgdXNpbmcgdG9vbHMgZnJvbSBEZWNpcGhlciAoTm90ZS0gYWxpZ25tZW50IHN0ZXAgdGFrZXMgc2V2ZXJhbCBob3Vycy4gQ29tbWVudGVkIG91dCBmb3Igbm93LiBPbmx5IG5lZWQgdG8gcnVuIG9uY2UpIApgYGB7cn0KIyMgaW1wb3J0IGZhc3RhCiMgZmFzIDwtICJkYWRhMl9leHBvcnQvQVNWcy5mYSIKIyBzZXFzIDwtIHJlYWRETkFTdHJpbmdTZXQoZmFzKQojIHNlcXMKIyAKIyAjIHBlcmZvcm0gdGhlIGFsaWdubWVudAojIGFsaWduZWQgPC0gQWxpZ25TZXFzKHNlcXMpICMgYXV0b21hdGljYWxseSBkZXRlY3RzIGFuZCB1c2VzIGFsbCBjb3JlcwojIAojICMgdmlldyB0aGUgYWxpZ25tZW50IGluIGEgYnJvd3NlciAob3B0aW9uYWwpCiMgQnJvd3NlU2VxcyhhbGlnbmVkLCBoaWdobGlnaHQ9MCkKIyAKIyAjIHdyaXRlIG91dCBhbGlnbmVkIHNlcXVlbmNlIGZpbGUKIyB3cml0ZVhTdHJpbmdTZXQoYWxpZ25lZCwgZmlsZT0iQVNWcy5hbGlnbmVkLmZhc3RhIikKYGBgCgoKVXNlIHBoYW5nb3JuIHBhY2thZ2UgdG8gYnVpbGQgdHJlZS4gSGVyZSB3ZSBhcmUgYnVpbGRpbmcgYSBtYXhpbXVtIGxpa2VsaWhvb2QgbmVpZ2hib3Itam9pbmluZyB0cmVlLiAoQWxzbyB0YWtlcyBhIHdoaWxlIHRvIHJ1bi4gQ29tbWVudCBvdXQgZm9yIG5vdy4pCgpgYGB7cn0KIyBwaGFuZy5hbGlnbiA8LSBwaHlEYXQoYXMoYWxpZ25lZCwgIm1hdHJpeCIpLCB0eXBlPSJETkEiKSAjIGNvbnZlcnQgdG8gcGh5RGF0IGZvcm1hdAojIGRtIDwtIGRpc3QubWwocGhhbmcuYWxpZ24pICMgY2FsY3VsYXRlIHBhaXJ3aXNlIGRpc3RhbmNlIG1hdHJpeAojIHRyZWVOSiA8LSBOSihkbSkgIyBwZXJmb3JtIG5laWdoYm9yLWpvaW5pbmcgdHJlZSBtZXRob2QKIyBmaXQgPSBwbWwodHJlZU5KLCBkYXRhPXBoYW5nLmFsaWduKSAjIGNvbXB1dGUgaW50ZXJtYWwgbWF4IGxpa2VsaWhvb2QKYGBgCgojIyMjIFNhdmUgYW5kIHJlLWxvYWQgZGF0YXNldApTaW5jZSB0aGUgc3RlcCBhYm92ZSB0YWtlcyBhIGxvbmcgdGltZSwgc2F2ZSBhbGwgdmFyaWFibGVzIHVwIHRvIHRoaXMgcG9pbnQgaW4gZW52aXJvbm1lbnQgYXMgUkRhdGEgb2JqZWN0CmBgYHtyfQojIHNhdmUuaW1hZ2UoIkVudmlyb25tZW50QmFja3Vwcy9DYXJpYWNvRXVrc19wb3N0YW5hbHlzaXNfdmFyc191cHRvX3RyZWUuUkRhdGEiKQpgYGAKClJlLWxvYWQKYGBge3J9CmxvYWQoIkVudmlyb25tZW50QmFja3Vwcy9DYXJpYWNvRXVrc19wb3N0YW5hbHlzaXNfdmFyc191cHRvX3RyZWUuUkRhdGEiKQpgYGAKCgojIyMgTWFrZSBwaHlsb3NlcSBvYmplY3RzCgpIZXJlIHdlIHdpbGwgZG8gb3JkaW5hdGlvbnMgdXNpbmcgdGhlIHBoeWxvc2VxIHBhY2thZ2UsIHdoaWNoIGZpcnN0IHJlcXVpcmVzIG1ha2luZyBwaHlsb3NlcSBvYmplY3RzIG91dCBvZiBlYWNoIG9mIG91ciBpbnB1dCBkYXRhIHRhYmxlcyAoaW4gdGhlIGxhc3QgdHV0b3JpYWwsIEkgaW1wb3J0ZWQgdGhlIHRyZWUgdXNpbmcgcGh5bG9zZXEgc28gaXQgaXMgYWxyZWFkeSBhIHBoeWxvc2VxIG9iamVjdCkKCmBgYHtyfQpBU1YJPQlvdHVfdGFibGUoY291bnRfdGFibGVfMiwgdGF4YV9hcmVfcm93cyA9ICBUUlVFKQpUQVgJPQl0YXhfdGFibGUoYXMubWF0cml4KHRheG9ub215KSkKTUVUQQk9CXNhbXBsZV9kYXRhKGRhdGEuZnJhbWUobWV0YWRhdGEsIHJvdy5uYW1lcyA9IG1ldGFkYXRhJGBTYW1wbGUgTmFtZWApKQpUUkVFID0gcGh5X3RyZWUoZml0JHRyZWUpCmBgYAoKCgpGaXJzdCBjaGVjayB0aGF0IHRoZSBpbnB1dHMgYXJlIGluIGNvbXBhdGlibGUgZm9ybWF0cyBieSBjaGVja2luZyBmb3IgQVNWIG5hbWVzIHdpdGggdGhlIHBoeWxvc2VxIGZ1bmN0aW9uLCB0YXhhX25hbWVzCmBgYHtyfQpoZWFkKHRheGFfbmFtZXMoVEFYKSkKaGVhZCh0YXhhX25hbWVzKEFTVikpCmhlYWQodGF4YV9uYW1lcyhUUkVFKSkKYGBgCgoKQW5kIGNoZWNrIHNhbXBsZSBuYW1lcyB3ZXJlIGFsc28gZGV0ZWN0ZWQKYGBge3J9CmhlYWQoc2FtcGxlX25hbWVzKEFTVikpCmhlYWQoc2FtcGxlX25hbWVzKE1FVEEpKQpgYGAKCkFuZCBtYWtlIHRoZSBwaHlsb3NlcSBvYmplY3QKYGBge3J9CnBzIDwtIHBoeWxvc2VxKEFTViwJVEFYLAlNRVRBICwgVFJFRSkKYGBgCgoKCkNoZWNrIHNvbWUgZmVhdHVyZXMgb2YgdGhlIHBoeWxvc2VxIG9iamVjdApgYGB7cn0KcmFua19uYW1lcyhwcykKdGFibGUodGF4X3RhYmxlKHBzKVssICJTdXBlcmdyb3VwIl0sIGV4Y2x1ZGUgPSBOVUxMKQp1bmlxdWUodGF4X3RhYmxlKHBzKVssICJTdXBlcmdyb3VwIl0pCmBgYAoKCkZpbHRlciBvdXQgdGhvc2UgYW1iaWdpb3VzIFN1cGVyZ3JvdXAgYW5ub3RhdGlvbnMtIGxvc2luZyA0NzEgQVNWcwpgYGB7cn0KcHMgPC0gc3Vic2V0X3RheGEocHMsICFpcy5uYShTdXBlcmdyb3VwKSAmICFTdXBlcmdyb3VwICVpbiUgYygiIiwgIk5BIikpCnRhYmxlKHRheF90YWJsZShwcylbLCAiU3VwZXJncm91cCJdLCBleGNsdWRlID0gTlVMTCkKYGBgCgpDaGVjayBvdXQgdGhlIERpdmlzaW9uIG5hbWVzCmBgYHtyfQp0YWJsZSh0YXhfdGFibGUocHMpWywgIkRpdmlzaW9uIl0sIGV4Y2x1ZGUgPSBOVUxMKQpgYGAKCgpGaWx0ZXIgb3V0IGFueSB3aXRoICJOQSIgYXMgRGl2aXNpb24gCmBgYHtyfQpwcyA8LSBzdWJzZXRfdGF4YShwcywgIWlzLm5hKERpdmlzaW9uKSAmICFEaXZpc2lvbiAlaW4lIGMoIiIpKQp0YWJsZSh0YXhfdGFibGUocHMpWywgIkRpdmlzaW9uIl0sIGV4Y2x1ZGUgPSBOVUxMKQpgYGAKQWZ0ZXIgdGhlIGFib3ZlLCAxMyw0MjcgQVNWcyByZW1haW4gZnJvbSB0aGUgb3JpZ2luYWwgMTQsMTc3CgoKRWxpbWluYXRlIHRoZSBsaWJyYXJpZXMgdGhhdCBkaWRuJ3QgaGF2ZSBtYW55IHNlcXVlbmNlcywgQUUzYTE5OEEsIEFFM2IzMTRBLCBBRTJhMjAwQSwgQUUyYjkwMEFOLCBBRTJhMjAwQiwgQUUyYTI2N0IsIEFFMmE5MDBCTgpgYGB7cn0KdGF4YV90b19rZWVwIDwtICFzYW1wbGVfbmFtZXMocHMpICVpbiUgYygiQUUzYTE5OEEiLCJBRTNiMzE0QSIsIkFFMmEyMDBBIiwiQUUyYjkwMEFOIiwiQUUyYTIwMEIiLCJBRTJhMjY3QiIsIkFFMmE5MDBCTiIpCnBzIDwtIHBydW5lX3NhbXBsZXModGF4YV90b19rZWVwLCBwcykKYGBgCjQxIHNhbXBsZXMgcmVtYWluIGFuZCBzdGlsIDEzLDQyNyBBU1ZzCgoKCkNoZWNrIHJhcmVmYWN0aW9uIGN1cnZlIGFnYWluIHRvIG1ha2Ugc3VyZSB0aG9zZSBsb3ctc3FldWVuY2luZy1lZmZvcnQgc2FtcGxlcyBoYXZlIGJlZW4gcmVtb3ZlZApgYGB7cn0KcmFyZWN1cnZlKHQob3R1X3RhYmxlKHBzKSksIHN0ZXA9MTAwLCBjZXg9MC41LCB5bGFiPSJBU1ZzIiwgbGFiZWw9VCkKYGBgCgoKCiMjIyBSZS1yb290IHRyZWUgCkhhdmUgdG8gZG8gdGhpcyBiZWNhdXNlIHlvdSBtYXkgaGF2ZSByZW1vdmVkIHRoZSByb290IG9mIHlvdXIgdHJlZSB3aGVuIHBydW5pbmcpLiAKKEkgZm91bmQgdGhpcyBoYW5keSBmdW5jdGlvbiBmcm9tIFtoZXJlXShodHRwczovL2pvaG4tcXVlbnNlbi5jb20vci91bmlmcmFjLWFuZC10cmVlLXJvb3RzLykgd2hpY2ggcGlja3MgdGhlIGxvbmdlc3QgYnJhbmNoIHRvIHJvb3QgZnJvbSkuIAoKYGBge3J9CiMgZmlyc3QgZGVmaW5lIGZ1bmN0aW9uIGZyb20gbGluayBhYm92ZSB0byBmaW5kIGZ1cnRoZXN0IG91dGdyb3VwCnBpY2tfbmV3X291dGdyb3VwIDwtIGZ1bmN0aW9uKHRyZWUudW5yb290ZWQpewpyZXF1aXJlKCJtYWdyaXR0ciIpCnJlcXVpcmUoImRhdGEudGFibGUiKQpyZXF1aXJlKCJhcGUiKSAjIGFwZTo6TnRpcAojIHRhYmxpZnkgcGFydHMgb2YgdHJlZSB0aGF0IHdlIG5lZWQuCnRyZWVEVCA8LSAKICAgICBjYmluZCgKICAgICAgICAgZGF0YS50YWJsZSh0cmVlLnVucm9vdGVkJGVkZ2UpLAogICAgICAgICBkYXRhLnRhYmxlKGxlbmd0aCA9IHRyZWUudW5yb290ZWQkZWRnZS5sZW5ndGgpCiAgICAgKVsxOk50aXAodHJlZS51bnJvb3RlZCldICU+JSAKIGNiaW5kKGRhdGEudGFibGUoaWQgPSB0cmVlLnVucm9vdGVkJHRpcC5sYWJlbCkpCiAjIFRha2UgdGhlIGxvbmdlc3QgdGVybWluYWwgYnJhbmNoIGFzIG91dGdyb3VwCiBuZXcub3V0Z3JvdXAgPC0gdHJlZURUW3doaWNoLm1heChsZW5ndGgpXSRpZAogcmV0dXJuKG5ldy5vdXRncm91cCkgfQoKIyB0aGVuIHJ1biBvbiBteSBwaHlsb3NlcSB0cmVlCm15LnRyZWUgPC0gcGh5X3RyZWUocHMpCm91dC5ncm91cCA8LSBwaWNrX25ld19vdXRncm91cChteS50cmVlKQpvdXQuZ3JvdXAKCiMgVGhlbiB1c2UgdGhpcyBvdXRncm91cCB0byByb290IHRoZSB0cmVlCm5ldy50cmVlMSA8LSBhcGU6OnJvb3QobXkudHJlZSwgb3V0Z3JvdXA9b3V0Lmdyb3VwLCByZXNvbHZlLnJvb3Q9VFJVRSkKCgpwaHlfdHJlZShwcykgPC0gbmV3LnRyZWUxCgojIENoZWNrIGlmIHRyZWUgaXMgYmluYXJ5IChkaWNob3RvbW91cyBub3QgbXVsdGljaG90b21vdXMpCmlzLmJpbmFyeS50cmVlKHBoeV90cmVlKHBzKSkKCiMgSWYgZmFsc2UsIHdvdWxkIGhhdmUgdG8gcnVuCiMgbmV3LnRyZWUyIDwtIGFwZTo6bXVsdGkyZGkobmV3LnRyZWUxKQojIHBoeV90cmVlKHBzKSA8LSBuZXcudHJlZTIKIyBwaHlfdHJlZShwcykKCgpgYGAKCiMjIyBDaGVjayBwaHlsYSB1c2luZyBiYXIgcGxvdHMKCkNoZWNrIG92ZXJhbGwgaG93IHRoZSBwaHlsYSBhcmUgZGlzdHJpYnV0ZWQgYW1vbmcgc2FtcGxlcy4gUGh5bG9zZXEgbWFrZXMgdGhpcyBlYXN5CgpgYGB7cn0KIyBGaXJzdCBhZ2xvbWVyYXRlIHRoZSBBU1ZzIGF0IHRoZSBwaHlsdW0gbGV2ZWwgdXNpbmcgdGhlIHBoeWxvc2VxIGZ1bmN0aW9uLCB0YXhfZ2xvbQpEaXZpc2lvbkdsb21tZWQgPSB0YXhfZ2xvbShwcywgIkRpdmlzaW9uIikKCiMgVGhlcmUgYXJlIG1hbnkgcGh5bGEgaGVyZSwgc28gaGF2ZSB0byBtYWtlIGEgY3VzdG9tIGNvbG9yIHBhbGV0dGUgYnkgaW50ZXJwb2xhdGluZyBmcm9tIGFuIGV4aXN0aW5nIG9uZSBpbiBSQ29sb3JCcmV3ZXIKY29sb3VyQ291bnQgPSBsZW5ndGgodGFibGUodGF4X3RhYmxlKHBzKVssICJEaXZpc2lvbiJdLCBleGNsdWRlID0gTlVMTCkpCmdldFBhbGV0dGUgPSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoMTEsICJTcGVjdHJhbCIpKQpEaXZpc2lvblBhbGV0dGUgPSBnZXRQYWxldHRlKGNvbG91ckNvdW50KQoKIyBhbmQgcGxvdApwbG90X2JhcihEaXZpc2lvbkdsb21tZWQsIHggPSAiU2FtcGxlIiwgZmlsbCA9ICJEaXZpc2lvbiIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gRGl2aXNpb25QYWxldHRlKQpgYGAKCgoKUGxvdCBjb21wb3NpdGlvbmFsIChyZWxhdGl2ZSBhYnVuZGFuY2VzKSBpbnN0ZWFkIG9mIGFic29sdXRlIGFidW5kYW5jZSB1c2luZyBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0KCmBgYHtyfQpwc19yYSA8LSBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0ocHMsIHRyYW5zZm9ybSA9ICJjb21wb3NpdGlvbmFsIikKKG90dV90YWJsZShwc19yYSkpWzE6NSwxOjVdCmBgYAoKCmBgYHtyfQojIFRoZW4gYWdsb21lcmF0ZSB0aGUgQVNWcyBhdCB0aGUgcGh5bHVtIGxldmVsIHVzaW5nIHRoZSBwaHlsb3NlcSBmdW5jdGlvbiwgdGF4X2dsb20KRGl2aXNpb25HbG9tbWVkX1JBID0gdGF4X2dsb20ocHNfcmEsICJEaXZpc2lvbiIpCiMgYW5kIHBsb3QKRGl2aXNpb25fYmFycGxvdCA8LSBwbG90X2JhcihEaXZpc2lvbkdsb21tZWRfUkEsIHggPSAiU2FtcGxlIiwgZmlsbCA9ICJEaXZpc2lvbiIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gRGl2aXNpb25QYWxldHRlKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKICAKCkRpdmlzaW9uX2JhcnBsb3QKCiMgZXhwb3J0Cmdnc2F2ZSgiRmlndXJlcy9EaXZpc2lvbl9iYXJwbG90LmVwcyIsRGl2aXNpb25fYmFycGxvdCwgd2lkdGggPSAxNSwgaGVpZ2h0ID0gNSwgdW5pdHMgPSBjKCJpbiIpKQpgYGAKCkxvdHMgb2YgZGlub2ZsYWdlbGxhdGVzIGFuZCByYWRpb2xhcmlhLiBNYWtlcyBzZW5zZS4gQnV0IHRoZSBhYm92ZSBpcyB0aGUgZGlzdHJpYnV0aW9uIGZyb20gYWxsIHNhbXBsZXMuIE5leHQgbWFrZSBwbG90cyB0aGF0IGluZGljYXRlIGRpc3RyaWJ1dGlvbnMgYWNyb3NzIGVudmlyb25tZW50YWwgZ3JhZGllbnRzLiBDYWxjdWxhdGUgYXZlcmFnZXMgYW5kIHVzZSBidWJibGUgcGxvdHMKCgoKCgojIyMgQ2FsY3VsYXRlIGF2ZXJhZ2VzIGZyb20gcmVwbGljYXRlcwpHZXQgYXZlcmFnZSByZWxhdGl2ZSBhYnVuZGFuY2VzIGZyb20gc2FtcGxlIHJlcGxpY2F0ZXMKYGBge3J9CgpvdHVfdGFibGVfbWVhbl9yYSA8LSAKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjEwM0EiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2ExMDNBIiwiQUUzYjEwM0EiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMTk4QSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUzYjE5OEEiKSksIG5hLnJtID0gVFJVRSkpICAlPiUgIyBTYW1wbGUgQUUzYTE5OEEgd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjIzNEEiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2EyMzRBIiwiQUUzYjIzNEEiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMjk1QSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUzYTI5NUEiLCJBRTNiMjk1QSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIzMTRBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhMzE0QSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICAjIFNhbXBsZSBBRTNiMzE0QSB3YXMgcmVtb3ZlZAogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiOTAwQU0iID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2E5MDBBTSIsIkFFMWI5MDBBTSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIxMDNCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhMTAzQiIsIkFFM2IxMDNCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjE5OEIiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2ExOThCIiwiQUUzYjE5OEIiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMjM0QiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUzYTIzNEIiLCJBRTNiMjM0QiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIyOTVCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhMjk1QiIsIkFFM2IyOTVCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjMxNEIiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2EzMTRCIiwiQUUzYjMxNEIiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiOTAwQk0iID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2E5MDBCTSIsIkFFMWI5MDBCTSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIxNDNBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJhMTQzQSIsIkFFMmIxNDNBIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjIwMEEiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFMmIyMDBBIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUgIyBBRTJhMjAwQSB3YXMgcmVtb3ZlZAogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMjM3QSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUyYTIzN0EiLCJBRTJiMjM3QSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIyNDdBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJhMjQ3QSIsIkFFMmIyNDdBIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjI2N0EiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFMmEyNjdBIiwiQUUyYjI2N0EiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiOTAwQU4iID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFMmE5MDBBTiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICMgQUUyYjkwMEFOIHdhcyByZW1vdmVkCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIxNDNCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJhMTQzQiIsIkFFMmIxNDNCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjIwMEIiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFMmIyMDBCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUgIyBBRTJhMjAwQiB3YXMgcmVtb3ZlZAogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMjM3QiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUyYTIzN0IiLCJBRTJiMjM3QiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIyNDdCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJhMjQ3QiIsIkFFMmIyNDdCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjI2N0IiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFMmIyNjdCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUgIyBBRTJhMjY3QiB3YXMgcmVtb3ZlZAogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiOTAwQk4iID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFMmI5MDBCTiIpKSwgbmEucm0gPSBUUlVFKSkgIyBBRTJhOTAwQk4gd2FzIHJlbW92ZWQKCm90dV90YWJsZV9tZWFuX3JhIDwtIG90dV90YWJsZV9tZWFuX3JhWyx1bmlxdWUobWV0YWRhdGEkUmVwbGljYXRlKV0KCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKTWFrZSBpbnRvIG5ldyBwaHlsb3NlcSBvYmplY3QKYGBge3J9Cm1ldGFkYXRhMiA8LSB1bmlxdWUoc2VsZWN0KG1ldGFkYXRhLCFjKCdTYW1wbGUgTmFtZScsVHlwZSxjb2xuYW1lcyhTUkFSdW5UYWJsZSkpKSkKTUVUQTIgPC0gc2FtcGxlX2RhdGEoZGF0YS5mcmFtZShtZXRhZGF0YTIsIHJvdy5uYW1lcyA9IG1ldGFkYXRhMiRSZXBsaWNhdGUpKQoKcHNfcmFfbWVhbiA8LSBwaHlsb3NlcShvdHVfdGFibGUob3R1X3RhYmxlX21lYW5fcmEsIHRheGFfYXJlX3Jvd3MgPSBUUlVFKSwgVEFYLCBUUkVFLCBNRVRBMikKYGBgCgoKYGBge3J9CiMgRmlyc3QgYWdsb21lcmF0ZSB0aGUgQVNWcyBhdCB0aGUgcGh5bHVtIGxldmVsIHVzaW5nIHRoZSBwaHlsb3NlcSBmdW5jdGlvbiwgdGF4X2dsb20KcHNfcmFfbWVhbl9kaXZpc2lvbiA8LSB0YXhfZ2xvbShwc19yYV9tZWFuLCAiRGl2aXNpb24iKQoKCiMgYW5kIGNoZWNrIGJ5IGJhciBwbG90dGluZwpwbG90X2Jhcihwc19yYV9tZWFuX2RpdmlzaW9uLCB4ID0gIlNhbXBsZSIsIGZpbGwgPSAiRGl2aXNpb24iKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IERpdmlzaW9uUGFsZXR0ZSkKYGBgCgojIEFidW5kYW5jZSBQbG90cwoKIyMgRGl2aXNpb25zCiMjIyBQcmVwYXJlIGRhdGEKRXh0cmFjdCBtZWFuIHJlbGF0aXZlIGFidW5kYW5jZSwgZ2xvbW1lZCBieSBkaXZpc2lvbiwgZnJvbSB0aGUgcGh5bG9zZXEgb2JqZWN0IGFuZCBwYWlyIGl0IHRvIHRheG9ub21pYyBkYXRhCmBgYHtyfQpkaXZpc2lvbl9kZiA8LSBkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYV9tZWFuX2RpdmlzaW9uKSkKY29sbmFtZXMoZGl2aXNpb25fZGYpIDwtIGNvbG5hbWVzKG90dV90YWJsZShwc19yYV9tZWFuX2RpdmlzaW9uKSkKZGl2aXNpb25fZGYkQVNWIDwtIHJvd25hbWVzKGRpdmlzaW9uX2RmKQoKb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKGRpdmlzaW9uX2RmLCBhc190aWJibGUodGF4b25vbXksIHJvd25hbWVzID0gIkFTViIpLCBieSA9ICJBU1YiKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKClNvbWUgbWFudWFsIGN1cmF0aW5nIGZvciBwbG90dGluCmBgYHtyfQojIE1ha2UgYSBuZXcgY29sdW1uIHRoYXQgaGFzIFN1cGVyZ3JvdXAtRGl2aXNpb24gaW4gc2FtZSBjb2x1bQpvdHVfdGFibGVfbWVhbl9yYSRTdXBlcmdyb3VwRGl2aXNpb24gPC0gcGFzdGUob3R1X3RhYmxlX21lYW5fcmEkU3VwZXJncm91cCwgb3R1X3RhYmxlX21lYW5fcmEkRGl2aXNpb24pCgpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgoKUGl2b3QgbG9uZ2VyCmBgYHtyfQpvdHVfdGFibGVfbWVhbl9yYSA8LSBwaXZvdF9sb25nZXIob3R1X3RhYmxlX21lYW5fcmEsIGNvbHMgPSB1bmlxdWUobWV0YWRhdGEkUmVwbGljYXRlKSwgbmFtZXNfdG8gPSAiUmVwbGljYXRlIiwgdmFsdWVzX3RvID0gIk1lYW5fUkEiKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgpKb2luIG1ldGFkYXRhIApgYGB7cn0Kb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKG90dV90YWJsZV9tZWFuX3JhLCB1bmlxdWUoc2VsZWN0KG1ldGFkYXRhLCBjKCJSZXBsaWNhdGUiLCAiRGVwdGgiLCAiU2l6ZUZyYWN0aW9uIiwgIlNlYXNvbiIsICJPeENvbmQiLCAiRmx1b3Jlc2NlbmNlIiwgIkJlYW1BdHQiLCAiTzIiLCAiVGVtcCIsICJTYWxpbml0eSIsICJIMlMiLCAiUGFydGljdWxhdGVTIiwgIlRaVlMiLCAiQ0g0IiwgIk5PMyIsICJOTzIiLCAiTkg0IiwgIlBPNCIsICJDaGVtb2F1dG90cm9waHkiLCAiQk5QIiwgIk1pY3JvQWJ1bih4MTBeOCBMXi0xKSIsICJGbGFnQWJ1bih4MTBeNSBMLTEpIiwgIlZMUCh4MTBeOCBMLTEpIikpKSwgYnkgPSAiUmVwbGljYXRlIikKCiMgUmVwbGFjZSB6ZXJvZXMgaW4gUkEgd2l0aCBOQSAoYmV0dGVyIGZvciBwbG90dGluZykKb3R1X3RhYmxlX21lYW5fcmEkTWVhbl9SQVtvdHVfdGFibGVfbWVhbl9yYSRNZWFuX1JBID09IDBdIDwtIE5BCgpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgojIyMgQnViYmxlIFBsb3Qgb2YgRGl2aXNpb25zCgpgYGB7cn0KIyByZW9yZGVyIHNvbWUgZmFjdG9ycyB0byBtYWtlIHRoZW0gcGxvdCBpbiB0aGUgb3JkZXIgSSB3YW50Cm90dV90YWJsZV9tZWFuX3JhJE94Q29uZCA8LSBmYWN0b3Iob3R1X3RhYmxlX21lYW5fcmEkT3hDb25kLCBsZXZlbHMgPSBjKCJPeHljbGluZSIsICJTaGFsbG93QW5veGljIiwgIkV1eGluaWMiKSkKb3R1X3RhYmxlX21lYW5fcmEkU2l6ZUZyYWN0aW9uIDwtIGZhY3RvcihvdHVfdGFibGVfbWVhbl9yYSRTaXplRnJhY3Rpb24sIGxldmVscyA9IGMoIlBBIiwgIkZMIikpCgpldWtfZGl2aXNpb25zX2J1YmJsZXBsb3RfY29sb3IgPC0gZ2dwbG90KG90dV90YWJsZV9tZWFuX3JhLGFlcyAoeCA9IGFzLmNoYXJhY3RlcihEZXB0aCksIHkgPSByZW9yZGVyKFN1cGVyZ3JvdXBEaXZpc2lvbiwgTWVhbl9SQSwgZnVuY3Rpb24oeCl7c3VtKHgsbmEucm0gPSBUUlVFKX0pLCBjb2xvciA9IE94Q29uZCkpICsgCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9TWVhbl9SQSkpKwogIGZhY2V0X3dyYXAoU2Vhc29uflNpemVGcmFjdGlvbiwgc2NhbGVzID0gImZyZWVfeCIsIGRyb3A9IFRSVUUsIG5jb2wgPSA0KSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMSwxNSkpICsKICBzY2FsZV9zaXplX2FyZWEoYnJlYWtzID0gYygwLC4yNSwuNSwuNzUsMSksIG1heF9zaXplID0gNikgKwogIHhsYWIoIkRlcHRoIikgKwogIHlsYWIoIiIpICsKICBsYWJzKHNpemU9IlJlbGF0aXZlIEFidW5kYW5jZSIsIGNvbG9yID0gIlJlZG94IENvbmRpdGlvbiIpICsKICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIiwgImJyb3duNCIpKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT04LCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIGxlZ2VuZC5tYXJnaW49bWFyZ2luKDAsMCwwLDIpLAogICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luPW1hcmdpbigtMTAsLTEwLC0xMCwtMTApLAogICAgICAgIHBsb3QubWFyZ2luPWdyaWQ6OnVuaXQoYygwLDAsMCwwKSwgIm1tIikpCgpldWtfZGl2aXNpb25zX2J1YmJsZXBsb3RfY29sb3IKYGBgCgpTYXZlIGZpZ3VyZQpgYGB7cn0KIyBzZXQgZXhwbGljaXQgcGFuZWwgc2l6ZSBzbyB0aGV5IHdpbGwgYmUgY29uc2lzdGVudCBmb3IgYWxsIGZpZ3VyZXMKZXVrX2RpdmlzaW9uc19idWJibGVwbG90X2NvbG9yIDwtIHNldF9wYW5lbF9zaXplKGV1a19kaXZpc2lvbnNfYnViYmxlcGxvdF9jb2xvciwgd2lkdGggID0gdW5pdCgyMiwgIm1tIiksIGhlaWdodCA9IHVuaXQoMTAwLCAibW0iKSkgIAoKZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvZXVrX2RpdmlzaW9uc19idWJibGVwbG90X2NvbG9yLmVwcyIsIHBsb3QgPSBldWtfZGl2aXNpb25zX2J1YmJsZXBsb3RfY29sb3IsIHVuaXRzID0gYygibW0iKSwgd2lkdGggPSAxODAsIGhlaWdodCA9IDEyNSwgZHBpID0gMzAwKQpgYGAKCgoKIyMgT3JkZXJzIHdpdGhpbiBBbHZlb2xhdGEKIyMjIFByZXBhcmUgZGF0YQpGaWx0ZXIgdG8gb25seSBBbHZlb2xhdGVzOyBnbG9tIGJ5IG9yZGVyCmBgYHtyfQprZWVwdGF4YSA8LSB0YXhhX25hbWVzKHBzX3JhX21lYW4pWyhhcy5kYXRhLmZyYW1lKHRheF90YWJsZShwc19yYV9tZWFuKSkkU3VwZXJncm91cCAlaW4lIGMoIkFsdmVvbGF0YSIpKV0KcHNfcmFfbWVhbl9hbHZlb2xhdGVzIDwtIHBydW5lX3RheGEoa2VlcHRheGEsIHBzX3JhX21lYW4pCnBzX3JhX21lYW5fYWx2ZW9sYXRlX29yZGVycyA8LSB0YXhfZ2xvbShwc19yYV9tZWFuX2FsdmVvbGF0ZXMsICJPcmRlciIpCgphdmVsb2F0ZXNfZGYgPC0gZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmFfbWVhbl9hbHZlb2xhdGVfb3JkZXJzKSkKY29sbmFtZXMoYXZlbG9hdGVzX2RmKSA8LSBjb2xuYW1lcyhvdHVfdGFibGUocHNfcmFfbWVhbl9hbHZlb2xhdGVfb3JkZXJzKSkKYXZlbG9hdGVzX2RmJEFTViA8LSByb3duYW1lcyhhdmVsb2F0ZXNfZGYpCgpvdHVfdGFibGVfbWVhbl9yYSA8LSBsZWZ0X2pvaW4oYXZlbG9hdGVzX2RmLCBhc190aWJibGUodGF4b25vbXksIHJvd25hbWVzID0gIkFTViIpLCBieSA9ICJBU1YiKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKClNvbWUgbWFudWFsIGN1cmF0aW5nIGZvciBwbG90dGluCmBgYHtyfQojIE1ha2UgYSBuZXcgY29sdW1uIHRoYXQgaGFzIGRlc2NyaXB0aXZlIHRheG9ub215Cm90dV90YWJsZV9tZWFuX3JhJERlc2NyaXB0aXZlIDwtIHBhc3RlKG90dV90YWJsZV9tZWFuX3JhJERpdmlzaW9uLCBvdHVfdGFibGVfbWVhbl9yYSRDbGFzcywgb3R1X3RhYmxlX21lYW5fcmEkT3JkZXIpCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKCgpQaXZvdCBsb25nZXIKYGBge3J9Cm90dV90YWJsZV9tZWFuX3JhIDwtIHBpdm90X2xvbmdlcihvdHVfdGFibGVfbWVhbl9yYSwgY29scyA9IHVuaXF1ZShtZXRhZGF0YSRSZXBsaWNhdGUpLCBuYW1lc190byA9ICJSZXBsaWNhdGUiLCB2YWx1ZXNfdG8gPSAiTWVhbl9SQSIpCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKCkpvaW4gbWV0YWRhdGEgCmBgYHtyfQpvdHVfdGFibGVfbWVhbl9yYSA8LSBsZWZ0X2pvaW4ob3R1X3RhYmxlX21lYW5fcmEsIHVuaXF1ZShzZWxlY3QobWV0YWRhdGEsIGMoIlJlcGxpY2F0ZSIsICJEZXB0aCIsICJTaXplRnJhY3Rpb24iLCAiU2Vhc29uIiwgIk94Q29uZCIsICJGbHVvcmVzY2VuY2UiLCAiQmVhbUF0dCIsICJPMiIsICJUZW1wIiwgIlNhbGluaXR5IiwgIkgyUyIsICJQYXJ0aWN1bGF0ZVMiLCAiVFpWUyIsICJDSDQiLCAiTk8zIiwgIk5PMiIsICJOSDQiLCAiUE80IiwgIkNoZW1vYXV0b3Ryb3BoeSIsICJCTlAiLCAiTWljcm9BYnVuKHgxMF44IExeLTEpIiwgIkZsYWdBYnVuKHgxMF41IEwtMSkiLCAiVkxQKHgxMF44IEwtMSkiKSkpLCBieSA9ICJSZXBsaWNhdGUiKQoKIyBSZXBsYWNlIHplcm9lcyBpbiBSQSB3aXRoIE5BIChiZXR0ZXIgZm9yIHBsb3R0aW5nKQpvdHVfdGFibGVfbWVhbl9yYSRNZWFuX1JBW290dV90YWJsZV9tZWFuX3JhJE1lYW5fUkEgPT0gMF0gPC0gTkEKCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKU2hvcnRlbiBzb21lIGxhYmVscyB0byBtYWtlIHNwYWNlIGluIHBsb3QKYGBge3J9Cm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkNpbGlvcGhvcmEgQ3ljbG90cmljaGl1bV9saWtlX29yZ2FuaXNtIEN5Y2xvdHJpY2hpdW1fbGlrZV9vcmdhbmlzbV9YIildIDwtIGMoIkNpbGlvLiBDeWNsb3RyaWNoaXVtX2xpa2UgQ3ljbG90cmljaGl1bV9saWtlIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkFwaWNvbXBsZXhhIEdyZWdhcmlub21vcnBoZWEgR3JlZ2FyaW5lc19HUkUyIildIDwtIGMoIkFwaWNvbS4gR3JlZ2FyaW5vbW9ycGhlYSBHUkUyIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkFwaWNvbXBsZXhhIEdyZWdhcmlub21vcnBoZWEgRXVncmVnYXJpbm9yaWRhIildIDwtIGMoIkFwaWNvbS4gR3JlZ2FyaW5vbW9ycGhlYSBFdWdyZWdhcmlub3JpZGEiKQoKb3R1X3RhYmxlX21lYW5fcmFbb3R1X3RhYmxlX21lYW5fcmEgPT0gYygiQXBpY29tcGxleGEgQ29jY2lkaW9tb3JwaGVhIEFnYW1vY29jY2lkaW9yaWRhIildIDwtIGMoIkFwaWNvbS4gQ29jY2lkaW9tb3JwaGVhIEFnYW1vY29jY2lkaW9yaWRhIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkRpbm9mbGFnZWxsYXRhIEVsbG9iaW9waHljZWFlIFRoYWxhc3NvbXljZXRhbGVzIildIDwtIGMoIkRpbm8uIEVsbG9iaW9waHljZWFlIFRoYWxhc3NvbXljZXRhbGVzIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkNpbGlvcGhvcmEgT2xpZ29oeW1lbm9waG9yZWEgU2N1dGljb2NpbGlhdGlhXzEiKV0gPC0gYygiQ2lsaW8uIE9saWdvaHltZW5vcGhvcmVhIFNjdXRpY29jaWxpYXRpYV8xIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkFwaWNvbXBsZXhhIEFwaWNvbXBsZXhhX1ggQXBpY29tcGxleGFfWFgiKV0gPC0gYygiQXBpY29tLiBBcGljb21wbGV4YV9YIEFwaWNvbXBsZXhhX1hYIikKCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKCiMjIyBCdWJibGUgUGxvdCBvZiBPcmRlcnMgd2l0aGluIEFsdmVvbGF0YQoKYGBge3J9CiMgcmVvcmRlciBzb21lIGZhY3RvcnMgdG8gbWFrZSB0aGVtIHBsb3QgaW4gdGhlIG9yZGVyIEkgd2FudApvdHVfdGFibGVfbWVhbl9yYSRPeENvbmQgPC0gZmFjdG9yKG90dV90YWJsZV9tZWFuX3JhJE94Q29uZCwgbGV2ZWxzID0gYygiT3h5Y2xpbmUiLCAiU2hhbGxvd0Fub3hpYyIsICJFdXhpbmljIikpCm90dV90YWJsZV9tZWFuX3JhJFNpemVGcmFjdGlvbiA8LSBmYWN0b3Iob3R1X3RhYmxlX21lYW5fcmEkU2l6ZUZyYWN0aW9uLCBsZXZlbHMgPSBjKCJQQSIsICJGTCIpKQoKCmFsdmVvbGF0YV9idWJibGVwbG90X2NvbG9yIDwtIGdncGxvdChvdHVfdGFibGVfbWVhbl9yYSxhZXMgKHggPSBhcy5jaGFyYWN0ZXIoRGVwdGgpLCB5ID0gcmVvcmRlcihEZXNjcmlwdGl2ZSwgTWVhbl9SQSwgZnVuY3Rpb24oeCl7c3VtKHgsbmEucm0gPSBUUlVFKX0pLCBjb2xvciA9IE94Q29uZCkpICsgCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9TWVhbl9SQSkpKwogIGZhY2V0X3dyYXAoU2Vhc29uflNpemVGcmFjdGlvbiwgc2NhbGVzID0gImZyZWVfeCIsIGRyb3A9IFRSVUUsIG5jb2wgPSA0KSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMSwxNSkpICsKICBzY2FsZV9zaXplX2FyZWEoYnJlYWtzID0gYygwLC4yNSwuNSwuNzUsMSksIG1heF9zaXplID0gNikgKwogIHhsYWIoIkRlcHRoIikgKwogIHlsYWIoIiIpICsKICBsYWJzKHNpemU9IlJlbGF0aXZlIEFidW5kYW5jZSIsIGNvbG9yID0gIlJlZG94IENvbmRpdGlvbiIpICsKICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIiwgImJyb3duNCIpKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT04LCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIGxlZ2VuZC5tYXJnaW49bWFyZ2luKDAsMCwwLDIpLAogICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luPW1hcmdpbigtMTAsLTEwLC0xMCwtMTApLAogICAgICAgIHBsb3QubWFyZ2luPWdyaWQ6OnVuaXQoYygwLDAsMCwwKSwgIm1tIikpCgphbHZlb2xhdGFfYnViYmxlcGxvdF9jb2xvcgpgYGAKClNhdmUgZmlndXJlCmBgYHtyfQojIHNldCBleHBsaWNpdCBwYW5lbCBzaXplIHNvIHRoZXkgd2lsbCBiZSBjb25zaXN0ZW50IGZvciBhbGwgZmlndXJlcwphbHZlb2xhdGFfYnViYmxlcGxvdF9jb2xvciA8LSBzZXRfcGFuZWxfc2l6ZShhbHZlb2xhdGFfYnViYmxlcGxvdF9jb2xvciwgd2lkdGggID0gdW5pdCgyMCwgIm1tIiksIGhlaWdodCA9IHVuaXQoMTI1LCAibW0iKSkgIAoKZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvYWx2ZW9sYXRhX2J1YmJsZXBsb3RfY29sb3IuZXBzIiwgcGxvdCA9IGFsdmVvbGF0YV9idWJibGVwbG90X2NvbG9yLCB1bml0cyA9IGMoIm1tIiksIHdpZHRoID0gMTgwLCBoZWlnaHQgPSAxNTAsIGRwaSA9IDMwMCkKYGBgCgoKCgoKCiMjIE9yZGVycyB3aXRoaW4gUmhpemFyaWEKIyMjIFByZXBhcmUgZGF0YQpGaWx0ZXIgdG8gb25seSBSaGl6YXJpYTsgZ2xvbSBieSBvcmRlcgpgYGB7cn0Ka2VlcHRheGEgPC0gdGF4YV9uYW1lcyhwc19yYV9tZWFuKVsoYXMuZGF0YS5mcmFtZSh0YXhfdGFibGUocHNfcmFfbWVhbikpJFN1cGVyZ3JvdXAgJWluJSBjKCJSaGl6YXJpYSIpKV0KcHNfcmFfbWVhbl9yaGl6YXJpYSA8LSBwcnVuZV90YXhhKGtlZXB0YXhhLCBwc19yYV9tZWFuKQpwc19yYV9tZWFuX3JoaXphcmlhX29yZGVycyA8LSB0YXhfZ2xvbShwc19yYV9tZWFuX3JoaXphcmlhLCAiT3JkZXIiKQoKcmhpemFyaWFfZGYgPC0gZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmFfbWVhbl9yaGl6YXJpYV9vcmRlcnMpKQpjb2xuYW1lcyhyaGl6YXJpYV9kZikgPC0gY29sbmFtZXMob3R1X3RhYmxlKHBzX3JhX21lYW5fcmhpemFyaWFfb3JkZXJzKSkKcmhpemFyaWFfZGYkQVNWIDwtIHJvd25hbWVzKHJoaXphcmlhX2RmKQoKb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKHJoaXphcmlhX2RmLCBhc190aWJibGUodGF4b25vbXksIHJvd25hbWVzID0gIkFTViIpLCBieSA9ICJBU1YiKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKClNvbWUgbWFudWFsIGN1cmF0aW5nIGZvciBwbG90dGluZwpgYGB7cn0KIyBNYWtlIGEgbmV3IGNvbHVtbiB0aGF0IGhhcyBkZXNjcmlwdGl2ZSB0YXhvbm9teQpvdHVfdGFibGVfbWVhbl9yYSREZXNjcmlwdGl2ZSA8LSBwYXN0ZShvdHVfdGFibGVfbWVhbl9yYSREaXZpc2lvbiwgb3R1X3RhYmxlX21lYW5fcmEkQ2xhc3MsIG90dV90YWJsZV9tZWFuX3JhJE9yZGVyKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgoKUGl2b3QgbG9uZ2VyCmBgYHtyfQpvdHVfdGFibGVfbWVhbl9yYSA8LSBwaXZvdF9sb25nZXIob3R1X3RhYmxlX21lYW5fcmEsIGNvbHMgPSB1bmlxdWUobWV0YWRhdGEkUmVwbGljYXRlKSwgbmFtZXNfdG8gPSAiUmVwbGljYXRlIiwgdmFsdWVzX3RvID0gIk1lYW5fUkEiKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgpKb2luIG1ldGFkYXRhIApgYGB7cn0Kb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKG90dV90YWJsZV9tZWFuX3JhLCB1bmlxdWUoc2VsZWN0KG1ldGFkYXRhLCBjKCJSZXBsaWNhdGUiLCAiRGVwdGgiLCAiU2l6ZUZyYWN0aW9uIiwgIlNlYXNvbiIsICJPeENvbmQiLCAiRmx1b3Jlc2NlbmNlIiwgIkJlYW1BdHQiLCAiTzIiLCAiVGVtcCIsICJTYWxpbml0eSIsICJIMlMiLCAiUGFydGljdWxhdGVTIiwgIlRaVlMiLCAiQ0g0IiwgIk5PMyIsICJOTzIiLCAiTkg0IiwgIlBPNCIsICJDaGVtb2F1dG90cm9waHkiLCAiQk5QIiwgIk1pY3JvQWJ1bih4MTBeOCBMXi0xKSIsICJGbGFnQWJ1bih4MTBeNSBMLTEpIiwgIlZMUCh4MTBeOCBMLTEpIikpKSwgYnkgPSAiUmVwbGljYXRlIikKCiMgUmVwbGFjZSB6ZXJvZXMgaW4gUkEgd2l0aCBOQSAoYmV0dGVyIGZvciBwbG90dGluZykKb3R1X3RhYmxlX21lYW5fcmEkTWVhbl9SQVtvdHVfdGFibGVfbWVhbl9yYSRNZWFuX1JBID09IDBdIDwtIE5BCgpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgpTaG9ydGVuIHNvbWUgbGFiZWxzIHRvIG1ha2Ugc3BhY2UgaW4gcGxvdApgYGB7cn0Kb3R1X3RhYmxlX21lYW5fcmFbb3R1X3RhYmxlX21lYW5fcmEgPT0gYygiUmFkaW9sYXJpYSBBY2FudGhhcmVhIEFydGhyYWNhbnRoaWRhLVN5bXBoeWFjYW50aGlkYSIpXSA8LSBjKCJSYWRpb2xhcmlhIEFjYW50aGFyZWEgQS1TIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkNlcmNvem9hIENobG9yYXJhY2huaW9waHljZWFlIENobG9yYXJhY2huaW9waHljZWFlX1giKV0gPC0gYygiQ2VyY296b2EgQ2hsb3IuIENobG9yLl9YIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkNlcmNvem9hIEZpbG9zYS1UaGVjb2ZpbG9zZWEgRmlsb3NhLVRoZWNvZmlsb3NlYV9YIildIDwtIGMoIkNlcmNvem9hIEYtVCBGLVRfWCIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDZXJjb3pvYSBGaWxvc2EtR3Jhbm9maWxvc2VhIEZpbG9zYS1HcmFub2ZpbG9zZWFfWCIpXSA8LSBjKCJDZXJjb3pvYSBGLUcuIEYtRy5fWCIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDZXJjb3pvYSBDaGxvcmFyYWNobmlvcGh5Y2VhZSBDaGxvcmFyYWNobmlkYSIpXSA8LSBjKCJDZXJjb3pvYSBDaGxvci4gQ2hsb3JhcmFjaG5pZGEiKQoKb3R1X3RhYmxlX21lYW5fcmFbb3R1X3RhYmxlX21lYW5fcmEgPT0gYygiQ2VyY296b2EgRmlsb3NhLUltYnJpY2F0ZWEgVGhhdW1hdG9tb25hZGlkYSIpXSA8LSBjKCJDZXJjb3pvYSBGLUkgVGhhdW1hdG9tb25hZGlkYSIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDZXJjb3pvYSBGaWxvc2EtSW1icmljYXRlYSBGaWxvc2EtSW1icmljYXRlYV9YIildIDwtIGMoIkNlcmNvem9hIEYtSSBGLUlfWCIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDZXJjb3pvYSBFbmRvbXl4YS1QaHl0b215eGVhIFBoYWdvbXl4aWRhIildIDwtIGMoIkNlcmNvem9hIEUtUCBQaGFnb215eGlkYSIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDZXJjb3pvYSBGaWxvc2EtU2FyY29tb25hZGVhIENlcmNvbW9uYWRpZGEiKV0gPC0gYygiQ2VyY296b2EgRi1TIENlcmNvbW9uYWRpZGEiKQoKb3R1X3RhYmxlX21lYW5fcmFbb3R1X3RhYmxlX21lYW5fcmEgPT0gYygiQ2VyY296b2EgTm92ZWwtY2xhZGUtMTAtMTIgTm92ZWwtY2xhZGUtMTIiKV0gPC0gYygiQ2VyY296b2EgTi1D4oiSMTDiiJIxMiBOLUPiiJIxMiIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDZXJjb3pvYSBGaWxvc2EtVGhlY29maWxvc2VhIFZlbnRyaWNsZWZ0aWRhIildIDwtIGMoIkNlcmNvem9hIEYtVCBWZW50cmljbGVmdGlkYSIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDZXJjb3pvYSBGaWxvc2EtSW1icmljYXRlYSBNYXJpbW9uYWRpZGEiKV0gPC0gYygiQ2VyY296b2EgRi1JIE1hcmltb25hZGlkYSIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDZXJjb3pvYSBGaWxvc2EtVGhlY29maWxvc2VhIENyeW9tb25hZGlkYSIpXSA8LSBjKCJDZXJjb3pvYSBGLVQgQ3J5b21vbmFkaWRhIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkNlcmNvem9hIEZpbG9zYS1TYXJjb21vbmFkZWEgR2xpc3NvbW9uYWRpZGEiKV0gPC0gYygiQ2VyY296b2EgRi1TIEdsaXNzb21vbmFkaWRhIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkNlcmNvem9hIEZpbG9zYS1JbWJyaWNhdGVhIEV1Z2x5cGhpZGEiKV0gPC0gYygiQ2VyY296b2EgRi1JIEV1Z2x5cGhpZGEiKQoKCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKCgojIyMgQnViYmxlIFBsb3Qgb2YgT3JkZXJzIHdpdGhpbiBSaGl6YXJpYQoKYGBge3J9CiMgcmVvcmRlciBzb21lIGZhY3RvcnMgdG8gbWFrZSB0aGVtIHBsb3QgaW4gdGhlIG9yZGVyIEkgd2FudApvdHVfdGFibGVfbWVhbl9yYSRPeENvbmQgPC0gZmFjdG9yKG90dV90YWJsZV9tZWFuX3JhJE94Q29uZCwgbGV2ZWxzID0gYygiT3h5Y2xpbmUiLCAiU2hhbGxvd0Fub3hpYyIsICJFdXhpbmljIikpCm90dV90YWJsZV9tZWFuX3JhJFNpemVGcmFjdGlvbiA8LSBmYWN0b3Iob3R1X3RhYmxlX21lYW5fcmEkU2l6ZUZyYWN0aW9uLCBsZXZlbHMgPSBjKCJQQSIsICJGTCIpKQoKcmhpemFyaWFfYnViYmxlcGxvdF9jb2xvciA8LSBnZ3Bsb3Qob3R1X3RhYmxlX21lYW5fcmEsYWVzICh4ID0gYXMuY2hhcmFjdGVyKERlcHRoKSwgeSA9IHJlb3JkZXIoRGVzY3JpcHRpdmUsIE1lYW5fUkEsIGZ1bmN0aW9uKHgpe3N1bSh4LG5hLnJtID0gVFJVRSl9KSwgY29sb3IgPSBPeENvbmQpKSArIAogIGdlb21fcG9pbnQoYWVzKHNpemUgPU1lYW5fUkEpKSsKICBmYWNldF93cmFwKFNlYXNvbn5TaXplRnJhY3Rpb24sIHNjYWxlcyA9ICJmcmVlX3giLCBkcm9wPSBUUlVFLCBuY29sID0gNCkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDEsMTUpKSArCiAgc2NhbGVfc2l6ZV9hcmVhKGJyZWFrcyA9IGMoMCwuMjUsLjUsLjc1LDEpLCBtYXhfc2l6ZSA9IDYpICsKICB4bGFiKCJEZXB0aCIpICsKICB5bGFiKCIiKSArCiAgbGFicyhzaXplPSJSZWxhdGl2ZSBBYnVuZGFuY2UiLCBjb2xvciA9ICJSZWRveCBDb25kaXRpb24iKSArCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIsICJicm93bjQiKSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9OCwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwKICAgICAgICBsZWdlbmQubWFyZ2luPW1hcmdpbigwLDAsMCwyKSwKICAgICAgICBsZWdlbmQuYm94Lm1hcmdpbj1tYXJnaW4oLTEwLC0xMCwtMTAsLTEwKSwKICAgICAgICBwbG90Lm1hcmdpbj1ncmlkOjp1bml0KGMoMCwwLDAsMCksICJtbSIpKQoKcmhpemFyaWFfYnViYmxlcGxvdF9jb2xvcgpgYGAKClNhdmUgZmlndXJlCmBgYHtyfQojIHNldCBleHBsaWNpdCBwYW5lbCBzaXplIHNvIHRoZXkgd2lsbCBiZSBjb25zaXN0ZW50IGZvciBhbGwgZmlndXJlcwpyaGl6YXJpYV9idWJibGVwbG90X2NvbG9yIDwtIHNldF9wYW5lbF9zaXplKHJoaXphcmlhX2J1YmJsZXBsb3RfY29sb3IsIHdpZHRoICA9IHVuaXQoMjAsICJtbSIpLCBoZWlnaHQgPSB1bml0KDEwMCwgIm1tIikpICAKCmdnc2F2ZShmaWxlbmFtZSA9ICJGaWd1cmVzL3JoaXphcmlhX2J1YmJsZXBsb3RfY29sb3IuZXBzIiwgcGxvdCA9IHJoaXphcmlhX2J1YmJsZXBsb3RfY29sb3IsIHVuaXRzID0gYygibW0iKSwgd2lkdGggPSAxODAsIGhlaWdodCA9IDEyNSwgZHBpID0gMzAwKQpgYGAKCgoKCiMjIE9yZGVycyB3aXRoaW4gT3Bpc3Rob2tvbnRhCiMjIyBQcmVwYXJlIGRhdGEKRmlsdGVyIHRvIG9ubHkgT3Bpc3Rob2tvbnRhOyBnbG9tIGJ5IG9yZGVyCmBgYHtyfQprZWVwdGF4YSA8LSB0YXhhX25hbWVzKHBzX3JhX21lYW4pWyhhcy5kYXRhLmZyYW1lKHRheF90YWJsZShwc19yYV9tZWFuKSkkU3VwZXJncm91cCAlaW4lIGMoIk9waXN0aG9rb250YSIpKV0KcHNfcmFfbWVhbl9vcGlzdGhva29udGEgPC0gcHJ1bmVfdGF4YShrZWVwdGF4YSwgcHNfcmFfbWVhbikKcHNfcmFfbWVhbl9vcGlzdGhva29udGFfb3JkZXJzIDwtIHRheF9nbG9tKHBzX3JhX21lYW5fb3Bpc3Rob2tvbnRhLCAiT3JkZXIiKQoKb3Bpc3Rob2tvbnRhX2RmIDwtIGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhX21lYW5fb3Bpc3Rob2tvbnRhX29yZGVycykpCmNvbG5hbWVzKG9waXN0aG9rb250YV9kZikgPC0gY29sbmFtZXMob3R1X3RhYmxlKHBzX3JhX21lYW5fb3Bpc3Rob2tvbnRhX29yZGVycykpCm9waXN0aG9rb250YV9kZiRBU1YgPC0gcm93bmFtZXMob3Bpc3Rob2tvbnRhX2RmKQoKb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKG9waXN0aG9rb250YV9kZiwgYXNfdGliYmxlKHRheG9ub215LCByb3duYW1lcyA9ICJBU1YiKSwgYnkgPSAiQVNWIikKb3R1X3RhYmxlX21lYW5fcmEKYGBgCgpTb21lIG1hbnVhbCBjdXJhdGluZyBmb3IgcGxvdHRpbgpgYGB7cn0KIyBNYWtlIGEgbmV3IGNvbHVtbiB0aGF0IGhhcyBkZXNjcmlwdGl2ZSB0YXhvbm9teQpvdHVfdGFibGVfbWVhbl9yYSREZXNjcmlwdGl2ZSA8LSBwYXN0ZShvdHVfdGFibGVfbWVhbl9yYSREaXZpc2lvbiwgb3R1X3RhYmxlX21lYW5fcmEkQ2xhc3MsIG90dV90YWJsZV9tZWFuX3JhJE9yZGVyKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgoKUGl2b3QgbG9uZ2VyCmBgYHtyfQpvdHVfdGFibGVfbWVhbl9yYSA8LSBwaXZvdF9sb25nZXIob3R1X3RhYmxlX21lYW5fcmEsIGNvbHMgPSB1bmlxdWUobWV0YWRhdGEkUmVwbGljYXRlKSwgbmFtZXNfdG8gPSAiUmVwbGljYXRlIiwgdmFsdWVzX3RvID0gIk1lYW5fUkEiKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgpKb2luIG1ldGFkYXRhIApgYGB7cn0Kb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKG90dV90YWJsZV9tZWFuX3JhLCB1bmlxdWUoc2VsZWN0KG1ldGFkYXRhLCBjKCJSZXBsaWNhdGUiLCAiRGVwdGgiLCAiU2l6ZUZyYWN0aW9uIiwgIlNlYXNvbiIsICJPeENvbmQiLCAiRmx1b3Jlc2NlbmNlIiwgIkJlYW1BdHQiLCAiTzIiLCAiVGVtcCIsICJTYWxpbml0eSIsICJIMlMiLCAiUGFydGljdWxhdGVTIiwgIlRaVlMiLCAiQ0g0IiwgIk5PMyIsICJOTzIiLCAiTkg0IiwgIlBPNCIsICJDaGVtb2F1dG90cm9waHkiLCAiQk5QIiwgIk1pY3JvQWJ1bih4MTBeOCBMXi0xKSIsICJGbGFnQWJ1bih4MTBeNSBMLTEpIiwgIlZMUCh4MTBeOCBMLTEpIikpKSwgYnkgPSAiUmVwbGljYXRlIikKCiMgUmVwbGFjZSB6ZXJvZXMgaW4gUkEgd2l0aCBOQSAoYmV0dGVyIGZvciBwbG90dGluZykKb3R1X3RhYmxlX21lYW5fcmEkTWVhbl9SQVtvdHVfdGFibGVfbWVhbl9yYSRNZWFuX1JBID09IDBdIDwtIE5BCgpvdHVfdGFibGVfbWVhbl9yYQpgYGAKClNob3J0ZW4gc29tZSBsYWJlbHMgdG8gbWFrZSBzcGFjZSBpbiBwbG90CmBgYHtyfQpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDaG9hbm9mbGFnZWxsaWRhIENob2Fub2ZsYWdlbGxhdGVhIEFjYW50aG9lY2lkYSIpXSA8LSBjKCJDaG9hbm9mLiBDaC4gQWNhbnRob2VjaWRhIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIkNob2Fub2ZsYWdlbGxpZGEgQ2hvYW5vZmxhZ2VsbGF0ZWEgQ3Jhc3BlZGlkYSIpXSA8LSBjKCJDaG9hbm9mLiBDaC4gQ3Jhc3BlZGlkYSIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJDaG9hbm9mbGFnZWxsaWRhIENob2Fub2ZsYWdlbGxhdGVhIENob2Fub2ZsYWdlbGxhdGVhX1giKV0gPC0gYygiQ2hvYW5vZi4gQ2guIENob2Fub2ZsYWdlbGxhdGVhX1giKQoKb3R1X3RhYmxlX21lYW5fcmFbb3R1X3RhYmxlX21lYW5fcmEgPT0gYygiQ2hvYW5vZmxhZ2VsbGlkYSBDaG9hbm9mbGFnZWxsaWRhX1ggQ2hvYW5vZmxhZ2VsbGlkYV9YWCIpXSA8LSBjKCJDaG9hbm9mLiBDaG9hbm9mLl9YIENob2Fub2YuX1hYIikKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIk1lc29teWNldG96b2EgSWNodGh5b3Nwb3JlYSBJY2h0aHlvc3Bob25pZGEiKV0gPC0gYygiTWVzb215LiBJY2h0aHlvc3BvcmVhIEljaHRoeW9zcGhvbmlkYSIpCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJPcGlzdGhva29udGFfWCBPcGlzdGhva29udGFfWFggT3Bpc3Rob2tvbnRhX1hYWCIpXSA8LSBjKCJPcGlzLl9YIE9waXMuX1hYIE9waXMuX1hYWCIpCgpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCiMjIyBCdWJibGUgUGxvdCBvZiBPcmRlcnMgd2l0aGluIE9waXN0aG9rb250YQoKYGBge3J9CiMgcmVvcmRlciBzb21lIGZhY3RvcnMgdG8gbWFrZSB0aGVtIHBsb3QgaW4gdGhlIG9yZGVyIEkgd2FudApvdHVfdGFibGVfbWVhbl9yYSRPeENvbmQgPC0gZmFjdG9yKG90dV90YWJsZV9tZWFuX3JhJE94Q29uZCwgbGV2ZWxzID0gYygiT3h5Y2xpbmUiLCAiU2hhbGxvd0Fub3hpYyIsICJFdXhpbmljIikpCm90dV90YWJsZV9tZWFuX3JhJFNpemVGcmFjdGlvbiA8LSBmYWN0b3Iob3R1X3RhYmxlX21lYW5fcmEkU2l6ZUZyYWN0aW9uLCBsZXZlbHMgPSBjKCJQQSIsICJGTCIpKQoKb3BpdGhva29udGFfYnViYmxlcGxvdF9jb2xvciA8LSBnZ3Bsb3Qob3R1X3RhYmxlX21lYW5fcmEsYWVzICh4ID0gYXMuY2hhcmFjdGVyKERlcHRoKSwgeSA9IHJlb3JkZXIoRGVzY3JpcHRpdmUsIE1lYW5fUkEsIGZ1bmN0aW9uKHgpe3N1bSh4LG5hLnJtID0gVFJVRSl9KSwgY29sb3IgPSBPeENvbmQpKSArIAogIGdlb21fcG9pbnQoYWVzKHNpemUgPU1lYW5fUkEpKSsKICBmYWNldF93cmFwKFNlYXNvbn5TaXplRnJhY3Rpb24sIHNjYWxlcyA9ICJmcmVlX3giLCBkcm9wPSBUUlVFLCBuY29sID0gNCkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDEsMTUpKSArCiAgc2NhbGVfc2l6ZV9hcmVhKGJyZWFrcyA9IGMoMCwuMSwuMiwuMyksIG1heF9zaXplID0gNikgKwogIHhsYWIoIkRlcHRoIikgKwogIHlsYWIoIiIpICsKICBsYWJzKHNpemU9IlJlbGF0aXZlIEFidW5kYW5jZSIsIGNvbG9yID0gIlJlZG94IENvbmRpdGlvbiIpICsKICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIiwgImJyb3duNCIpKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT04LCBhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIGxlZ2VuZC5tYXJnaW49bWFyZ2luKDAsMCwwLDIpLAogICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luPW1hcmdpbigtMTAsLTEwLC0xMCwtMTApLAogICAgICAgIHBsb3QubWFyZ2luPWdyaWQ6OnVuaXQoYygwLDAsMCwwKSwgIm1tIikpCgpvcGl0aG9rb250YV9idWJibGVwbG90X2NvbG9yCmBgYAoKU2F2ZSBmaWd1cmUKYGBge3J9CiMgc2V0IGV4cGxpY2l0IHBhbmVsIHNpemUgc28gdGhleSB3aWxsIGJlIGNvbnNpc3RlbnQgZm9yIGFsbCBmaWd1cmVzCm9waXRob2tvbnRhX2J1YmJsZXBsb3RfY29sb3IgPC0gc2V0X3BhbmVsX3NpemUob3BpdGhva29udGFfYnViYmxlcGxvdF9jb2xvciwgd2lkdGggID0gdW5pdCgyMCwgIm1tIiksIGhlaWdodCA9IHVuaXQoMTAwLCAibW0iKSkgIAoKZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvb3BpdGhva29udGFfYnViYmxlcGxvdF9jb2xvci5lcHMiLCBwbG90ID0gb3BpdGhva29udGFfYnViYmxlcGxvdF9jb2xvciwgdW5pdHMgPSBjKCJtbSIpLCB3aWR0aCA9IDE4MCwgaGVpZ2h0ID0gMTI1LCBkcGkgPSAzMDApCmBgYAoKCgoKIyMgQ2xhc3NlcyB3aXRoaW4gU3RyYW1lbm9waWxlcwojIyMgUHJlcGFyZSBkYXRhCkZpbHRlciB0byBvbmx5IFN0cmFtZW5vcGlsZXM7IGdsb20gYnkgY2xhc3MgKG1vcmUgbWVhbmluZ2Z1bCB0aGFuIE9yZGVyIGluIHRoaXMgY2FzZSkKYGBge3J9CmtlZXB0YXhhIDwtIHRheGFfbmFtZXMocHNfcmFfbWVhbilbKGFzLmRhdGEuZnJhbWUodGF4X3RhYmxlKHBzX3JhX21lYW4pKSRTdXBlcmdyb3VwICVpbiUgYygiU3RyYW1lbm9waWxlcyIpKV0KcHNfcmFfbWVhbl9zdHJhbWVub3BpbGVzIDwtIHBydW5lX3RheGEoa2VlcHRheGEsIHBzX3JhX21lYW4pCnBzX3JhX21lYW5fc3RyYW1lbm9waWxlc19jbGFzc2VzIDwtIHRheF9nbG9tKHBzX3JhX21lYW5fc3RyYW1lbm9waWxlcywgIkNsYXNzIikKCnN0cmFtZW5vcGlsZXNfZGYgPC0gZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmFfbWVhbl9zdHJhbWVub3BpbGVzX2NsYXNzZXMpKQpjb2xuYW1lcyhzdHJhbWVub3BpbGVzX2RmKSA8LSBjb2xuYW1lcyhvdHVfdGFibGUocHNfcmFfbWVhbl9zdHJhbWVub3BpbGVzX2NsYXNzZXMpKQpzdHJhbWVub3BpbGVzX2RmJEFTViA8LSByb3duYW1lcyhzdHJhbWVub3BpbGVzX2RmKQoKb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKHN0cmFtZW5vcGlsZXNfZGYsIGFzX3RpYmJsZSh0YXhvbm9teSwgcm93bmFtZXMgPSAiQVNWIiksIGJ5ID0gIkFTViIpCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKU29tZSBtYW51YWwgY3VyYXRpbmcgZm9yIHBsb3R0aW4KYGBge3J9CiMgTWFrZSBhIG5ldyBjb2x1bW4gdGhhdCBoYXMgZGVzY3JpcHRpdmUgdGF4b25vbXkKb3R1X3RhYmxlX21lYW5fcmEkRGVzY3JpcHRpdmUgPC0gcGFzdGUob3R1X3RhYmxlX21lYW5fcmEkRGl2aXNpb24sIG90dV90YWJsZV9tZWFuX3JhJENsYXNzKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgoKUGl2b3QgbG9uZ2VyCmBgYHtyfQpvdHVfdGFibGVfbWVhbl9yYSA8LSBwaXZvdF9sb25nZXIob3R1X3RhYmxlX21lYW5fcmEsIGNvbHMgPSB1bmlxdWUobWV0YWRhdGEkUmVwbGljYXRlKSwgbmFtZXNfdG8gPSAiUmVwbGljYXRlIiwgdmFsdWVzX3RvID0gIk1lYW5fUkEiKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgpKb2luIG1ldGFkYXRhIApgYGB7cn0Kb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKG90dV90YWJsZV9tZWFuX3JhLCB1bmlxdWUoc2VsZWN0KG1ldGFkYXRhLCBjKCJSZXBsaWNhdGUiLCAiRGVwdGgiLCAiU2l6ZUZyYWN0aW9uIiwgIlNlYXNvbiIsICJPeENvbmQiLCAiRmx1b3Jlc2NlbmNlIiwgIkJlYW1BdHQiLCAiTzIiLCAiVGVtcCIsICJTYWxpbml0eSIsICJIMlMiLCAiUGFydGljdWxhdGVTIiwgIlRaVlMiLCAiQ0g0IiwgIk5PMyIsICJOTzIiLCAiTkg0IiwgIlBPNCIsICJDaGVtb2F1dG90cm9waHkiLCAiQk5QIiwgIk1pY3JvQWJ1bih4MTBeOCBMXi0xKSIsICJGbGFnQWJ1bih4MTBeNSBMLTEpIiwgIlZMUCh4MTBeOCBMLTEpIikpKSwgYnkgPSAiUmVwbGljYXRlIikKCiMgUmVwbGFjZSB6ZXJvZXMgaW4gUkEgd2l0aCBOQSAoYmV0dGVyIGZvciBwbG90dGluZykKb3R1X3RhYmxlX21lYW5fcmEkTWVhbl9SQVtvdHVfdGFibGVfbWVhbl9yYSRNZWFuX1JBID09IDBdIDwtIE5BCgpvdHVfdGFibGVfbWVhbl9yYQpgYGAKClNob3J0ZW4gc29tZSBsYWJlbHMgdG8gbWFrZSBzcGFjZSBpbiBwbG90CmBgYHtyfQpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJTdHJhbWVub3BpbGVzX1ggU3RyYW1lbm9waWxlc19YWCIpXSA8LSBjKCJTdHJhbWVuby5fWCBTdHJhbWVuby5fWFgiKQoKb3R1X3RhYmxlX21lYW5fcmFbb3R1X3RhYmxlX21lYW5fcmEgPT0gYygiU3RyYW1lbm9waWxlc19YIFN0cmFtZW5vcGlsZXNfWC1Hcm91cC03IildIDwtIGMoIlN0cmFtZW5vLl9YIFN0cmFtZW5vLl9Y4oiSR3JvdXDiiJI3IikKCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJTdHJhbWVub3BpbGVzX1ggTUFTVC0yMSIpXSA8LSBjKCJTdHJhbWVuby5fWCBNQVNU4oiSMjEiKQoKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIlN0cmFtZW5vcGlsZXNfWCBNQVNULTI1IildIDwtIGMoIlN0cmFtZW5vLl9YIE1BU1QtMjUiKQoKCm90dV90YWJsZV9tZWFuX3JhW290dV90YWJsZV9tZWFuX3JhID09IGMoIlN0cmFtZW5vcGlsZXNfWCBTdHJhbWVub3BpbGVzX1gtR3JvdXAtNCIpXSA8LSBjKCJTdHJhbWVuby5fWCBTdHJhbWVuby5fWOKIkkdyb3Vw4oiSNCIpCgoKb3R1X3RhYmxlX21lYW5fcmFbb3R1X3RhYmxlX21lYW5fcmEgPT0gYygiU3RyYW1lbm9waWxlc19YIFN0cmFtZW5vcGlsZXNfWC1Hcm91cC02IildIDwtIGMoIlN0cmFtZW5vLl9YIFN0cmFtZW5vLl9Y4oiSR3JvdXDiiJI2IikKCgpvdHVfdGFibGVfbWVhbl9yYVtvdHVfdGFibGVfbWVhbl9yYSA9PSBjKCJTdHJhbWVub3BpbGVzX1ggU3RyYW1lbm9waWxlc19YLUdyb3VwLTgiKV0gPC0gYygiU3RyYW1lbm8uX1ggU3RyYW1lbm8uX1jiiJJHcm91cOKIkjgiKQoKCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKIyMjIEJ1YmJsZSBQbG90IG9mIE9yZGVycyB3aXRoaW4gU3RyYW1lbm9waWxlcwoKYGBge3J9CiMgcmVvcmRlciBzb21lIGZhY3RvcnMgdG8gbWFrZSB0aGVtIHBsb3QgaW4gdGhlIG9yZGVyIEkgd2FudApvdHVfdGFibGVfbWVhbl9yYSRPeENvbmQgPC0gZmFjdG9yKG90dV90YWJsZV9tZWFuX3JhJE94Q29uZCwgbGV2ZWxzID0gYygiT3h5Y2xpbmUiLCAiU2hhbGxvd0Fub3hpYyIsICJFdXhpbmljIikpCm90dV90YWJsZV9tZWFuX3JhJFNpemVGcmFjdGlvbiA8LSBmYWN0b3Iob3R1X3RhYmxlX21lYW5fcmEkU2l6ZUZyYWN0aW9uLCBsZXZlbHMgPSBjKCJQQSIsICJGTCIpKQoKc3RyYW1lbm9waWxlc19idWJibGVwbG90X2NvbG9yIDwtIGdncGxvdChvdHVfdGFibGVfbWVhbl9yYSxhZXMgKHggPSBhcy5jaGFyYWN0ZXIoRGVwdGgpLCB5ID0gcmVvcmRlcihEZXNjcmlwdGl2ZSwgTWVhbl9SQSwgZnVuY3Rpb24oeCl7c3VtKHgsbmEucm0gPSBUUlVFKX0pLCBjb2xvciA9IE94Q29uZCkpICsgCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9TWVhbl9SQSkpKwogIGZhY2V0X3dyYXAoU2Vhc29uflNpemVGcmFjdGlvbiwgc2NhbGVzID0gImZyZWVfeCIsIGRyb3A9IFRSVUUsIG5jb2wgPSA0KSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMSwxNSkpICsKICBzY2FsZV9zaXplX2FyZWEoYnJlYWtzID0gYygwLC4xLC4yLC4zKSwgbWF4X3NpemUgPSA2KSArCiAgeGxhYigiRGVwdGgiKSArCiAgeWxhYigiIikgKwogIGxhYnMoc2l6ZT0iUmVsYXRpdmUgQWJ1bmRhbmNlIiwgY29sb3IgPSAiUmVkb3ggQ29uZGl0aW9uIikgKwogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmx1ZSIsICJyZWQiLCAiYnJvd240IikpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTgsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgbGVnZW5kLm1hcmdpbj1tYXJnaW4oMCwwLDAsMiksCiAgICAgICAgbGVnZW5kLmJveC5tYXJnaW49bWFyZ2luKC0xMCwtMTAsLTEwLC0xMCksCiAgICAgICAgcGxvdC5tYXJnaW49Z3JpZDo6dW5pdChjKDAsMCwwLDApLCAibW0iKSkKCnN0cmFtZW5vcGlsZXNfYnViYmxlcGxvdF9jb2xvcgpgYGAKClNhdmUgZmlndXJlCmBgYHtyfQojIHNldCBleHBsaWNpdCBwYW5lbCBzaXplIHNvIHRoZXkgd2lsbCBiZSBjb25zaXN0ZW50IGZvciBhbGwgZmlndXJlcwpzdHJhbWVub3BpbGVzX2J1YmJsZXBsb3RfY29sb3IgPC0gc2V0X3BhbmVsX3NpemUoc3RyYW1lbm9waWxlc19idWJibGVwbG90X2NvbG9yLCB3aWR0aCAgPSB1bml0KDIyLCAibW0iKSwgaGVpZ2h0ID0gdW5pdCgxMTUsICJtbSIpKSAgCgpnZ3NhdmUoZmlsZW5hbWUgPSAiRmlndXJlcy9zdHJhbWVub3BpbGVzX2J1YmJsZXBsb3RfY29sb3IuZXBzIiwgcGxvdCA9IHN0cmFtZW5vcGlsZXNfYnViYmxlcGxvdF9jb2xvciwgdW5pdHMgPSBjKCJtbSIpLCB3aWR0aCA9IDE4MCwgaGVpZ2h0ID0gMTUwLCBkcGkgPSAzMDApCmBgYAoKCiMgRGl2ZXJzaXR5CgojIyBDYWxjdWxhdGUgU2hhbm5vbidzIGRpdmVyc2l0eSB1c2luZyB2ZWdhbgpgYGB7cn0Kc2hhbm5vbnMgPC0gdmVnYW46OmRpdmVyc2l0eSh0KG90dV90YWJsZShwcykpLCBpbmRleCA9ICJzaGFubm9uIikKc2hhbm5vbnMgPC0gdChzaGFubm9ucykKc2hhbm5vbnMKYGBgCgojIyBBdmVyYWdlIHRoZSByZXBsaWNhdGVzCmBgYHtyfQpzaGFubm9uc19tZWFuIDwtICAKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICIxMDNBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKHNoYW5ub25zKSwgYygiQUUzYTEwM0EiLCJBRTNiMTAzQSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUoc2hhbm5vbnMpLCAiMTk4QSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFM2IxOThBIikpLCBuYS5ybSA9IFRSVUUpKSAgJT4lICMgU2FtcGxlIEFFM2ExOThBIHdhcyByZW1vdmVkCiAgbXV0YXRlKGRhdGEuZnJhbWUoc2hhbm5vbnMpLCAiMjM0QSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFM2EyMzRBIiwiQUUzYjIzNEEiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKHNoYW5ub25zKSwgIjI5NUEiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUoc2hhbm5vbnMpLCBjKCJBRTNhMjk1QSIsIkFFM2IyOTVBIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICIzMTRBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKHNoYW5ub25zKSwgYygiQUUzYTMxNEEiKSksIG5hLnJtID0gVFJVRSkpICU+JSAgIyBTYW1wbGUgQUUzYjMxNEEgd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICI5MDBBTSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFM2E5MDBBTSIsIkFFMWI5MDBBTSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUoc2hhbm5vbnMpLCAiMTAzQiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFM2ExMDNCIiwiQUUzYjEwM0IiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKHNoYW5ub25zKSwgIjE5OEIiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUoc2hhbm5vbnMpLCBjKCJBRTNhMTk4QiIsIkFFM2IxOThCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICIyMzRCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKHNoYW5ub25zKSwgYygiQUUzYTIzNEIiLCJBRTNiMjM0QiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUoc2hhbm5vbnMpLCAiMjk1QiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFM2EyOTVCIiwiQUUzYjI5NUIiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKHNoYW5ub25zKSwgIjMxNEIiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUoc2hhbm5vbnMpLCBjKCJBRTNhMzE0QiIsIkFFM2IzMTRCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICI5MDBCTSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFM2E5MDBCTSIsIkFFMWI5MDBCTSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUoc2hhbm5vbnMpLCAiMTQzQSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFMmExNDNBIiwiQUUyYjE0M0EiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKHNoYW5ub25zKSwgIjIwMEEiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUoc2hhbm5vbnMpLCBjKCJBRTJiMjAwQSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICMgQUUyYTIwMEEgd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICIyMzdBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKHNoYW5ub25zKSwgYygiQUUyYTIzN0EiLCJBRTJiMjM3QSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUoc2hhbm5vbnMpLCAiMjQ3QSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFMmEyNDdBIiwiQUUyYjI0N0EiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKHNoYW5ub25zKSwgIjI2N0EiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUoc2hhbm5vbnMpLCBjKCJBRTJhMjY3QSIsIkFFMmIyNjdBIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICI5MDBBTiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFMmE5MDBBTiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICMgQUUyYjkwMEFOIHdhcyByZW1vdmVkCiAgbXV0YXRlKGRhdGEuZnJhbWUoc2hhbm5vbnMpLCAiMTQzQiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFMmExNDNCIiwiQUUyYjE0M0IiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKHNoYW5ub25zKSwgIjIwMEIiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUoc2hhbm5vbnMpLCBjKCJBRTJiMjAwQiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICMgQUUyYTIwMEIgd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICIyMzdCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKHNoYW5ub25zKSwgYygiQUUyYTIzN0IiLCJBRTJiMjM3QiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUoc2hhbm5vbnMpLCAiMjQ3QiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFMmEyNDdCIiwiQUUyYjI0N0IiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKHNoYW5ub25zKSwgIjI2N0IiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUoc2hhbm5vbnMpLCBjKCJBRTJiMjY3QiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICMgQUUyYTI2N0Igd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShzaGFubm9ucyksICI5MDBCTiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShzaGFubm9ucyksIGMoIkFFMmI5MDBCTiIpKSwgbmEucm0gPSBUUlVFKSkgIyBBRTJhOTAwQk4gd2FzIHJlbW92ZWQKCnNoYW5ub25zX21lYW4gPC0gc2hhbm5vbnNfbWVhblssdW5pcXVlKG1ldGFkYXRhJFJlcGxpY2F0ZSldCgpzaGFubm9uc19tZWFuCmBgYAoKIyMgUHJlcGFyZSBkYXRhIGZvciBwbG90dGluZwpgYGB7cn0KIyBQaXZvdCBsb25nZXIKc2hhbm5vbnNfbWVhbiA8LSBwaXZvdF9sb25nZXIoc2hhbm5vbnNfbWVhbiwgY29scyA9IHVuaXF1ZShtZXRhZGF0YSRSZXBsaWNhdGUpLCBuYW1lc190byA9ICJSZXBsaWNhdGUiLCB2YWx1ZXNfdG8gPSAiU2hhbm5vbnMiKQoKIyBKb2luIG1ldGFkYXRhCnNoYW5ub25zX21lYW4gPC0gbGVmdF9qb2luKHNoYW5ub25zX21lYW4sIHVuaXF1ZShzZWxlY3QobWV0YWRhdGEsIGMoIlJlcGxpY2F0ZSIsICJEZXB0aCIsICJTaXplRnJhY3Rpb24iLCAiU2Vhc29uIiwgIk94Q29uZCIsICJGbHVvcmVzY2VuY2UiLCAiQmVhbUF0dCIsICJPMiIsICJUZW1wIiwgIlNhbGluaXR5IiwgIkgyUyIsICJQYXJ0aWN1bGF0ZVMiLCAiVFpWUyIsICJDSDQiLCAiTk8zIiwgIk5PMiIsICJOSDQiLCAiUE80IiwgIkNoZW1vYXV0b3Ryb3BoeSIsICJCTlAiLCAiTWljcm9BYnVuKHgxMF44IExeLTEpIiwgIkZsYWdBYnVuKHgxMF41IEwtMSkiLCAiVkxQKHgxMF44IEwtMSkiKSkpLCBieSA9ICJSZXBsaWNhdGUiKQoKc2hhbm5vbnNfbWVhbgpgYGAKCiMjIFBsb3QKYGBge3J9CiMgcmVvcmRlciBzb21lIGZhY3RvcnMgdG8gbWFrZSB0aGVtIHBsb3QgaW4gdGhlIG9yZGVyIEkgd2FudApzaGFubm9uc19tZWFuJE94Q29uZCA8LSBmYWN0b3Ioc2hhbm5vbnNfbWVhbiRPeENvbmQsIGxldmVscyA9IGMoIk94eWNsaW5lIiwgIlNoYWxsb3dBbm94aWMiLCAiRXV4aW5pYyIpKQpzaGFubm9uc19tZWFuJFNpemVGcmFjdGlvbiA8LSBmYWN0b3Ioc2hhbm5vbnNfbWVhbiRTaXplRnJhY3Rpb24sIGxldmVscyA9IGMoIlBBIiwgIkZMIikpCgp5dGl0bGUgPC0gZXhwcmVzc2lvbihwYXN0ZSgiU2hhbm5vbidzIERpdmVyc2l0eSBJbmRleCAoIixpdGFsaWMoIkgnIiksIikiKSkKCnNoYW5ub25zcGxvdCA8LSBnZ3Bsb3Qoc2hhbm5vbnNfbWVhbiwgYWVzKHggPSBEZXB0aCwgeSA9IFNoYW5ub25zLCBjb2xvciA9IE94Q29uZCkpICsKICBnZW9tX2xpbmUoc2l6ZT0xLCBjb2xvciA9ICJibGFjayIsIGx0eSA9ICJkb3R0ZWQiKSArCiAgZ2VvbV9wb2ludChzaXplPTMsIHNoYXBlID0gYygxNikpICsKICBsYWJzKHk9IHl0aXRsZSwgeCA9ICJEZXB0aCAobSkiKSArCiAgc2NhbGVfeF9yZXZlcnNlKGV4cGFuZCA9IGMoMCwgMCkpICsKICBjb29yZF9mbGlwKHhsaW0gPSBjKDkxMCwgMTAwKSkgKyAKICB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgbGVnZW5kLm1hcmdpbj1tYXJnaW4oMCwwLDAsMikpICsKICBmYWNldF93cmFwKFNlYXNvbn5TaXplRnJhY3Rpb24sIGRyb3A9IFRSVUUsIG5jb2wgPSA0KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIiwgImJyb3duNCIpKSArCiAgbGFicyhjb2xvciA9ICJSZWRveCBDb25kaXRpb24iKQoKc2hhbm5vbnNwbG90CgoKYGBgCgpFeHBvcnQgUGxvdApgYGB7cn0KIyBzZXQgZXhwbGljaXQgcGFuZWwgc2l6ZSBzbyB0aGV5IHdpbGwgYmUgY29uc2lzdGVudCBmb3IgYWxsIGZpZ3VyZXMKc2hhbm5vbnNwbG90IDwtIHNldF9wYW5lbF9zaXplKHNoYW5ub25zcGxvdCwgIHdpZHRoICA9IHVuaXQoMjIsICJtbSIpLCBoZWlnaHQgPSB1bml0KDYwLCAibW0iKSkgIAoKZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvc2hhbm5vbnNwbG90LmVwcyIsIHBsb3QgPSBzaGFubm9uc3Bsb3QsIHVuaXRzID0gYygibW0iKSwgd2lkdGggPSAxODAsIGhlaWdodCA9IDgwLCBkcGkgPSAzMDApCgpgYGAKCgoKCiMgT3JkaW5hdGlvbnMKCiMjIEZpbHRlciAKTWNNdXJkaWUgYW5kIEhvbG1lcyAoMjAxMykgZmlsdGVyIG91dCB0YXhhIHRoYXQgd2VyZSBub3Qgc2VlbiB3aXRoIG1vcmUgdGhhbiAzIGNvdW50cyBpbiBhdCBsZWFzdCAyMCUgb2YgdGhlIHNhbXBsZXMuIEFsc28gYWRkIGEgcHNlZHVvY291bnQgb2YgMSB0byBhbGwgY291bnRzLiBUaGlzIGlzIHNvIHRoYXQgbGF0ZXIgd2hlbiB3ZSBkbyBkaWZmZXJlbnQgY2FsY3VsYXRpb25zIChsb2csIGRpdmlzaW9uLCBldGMpIHdlIGRvbid0IGdldCBiYWNrIGVycm9ycyBkdWUgdG8gemVyb2VzCmBgYHtyfQpwc19maWx0ZXJlZCA9IGZpbHRlcl90YXhhKHBzLCBmdW5jdGlvbih4KSBzdW0oeCA+IDMpID4gKDAuMipsZW5ndGgoeCkpLCBUUlVFKSAKcHNfZmlsdGVyZWQgPC0gdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMocHNfZmlsdGVyZWQsIGZ1bmN0aW9uKHgpIHgrMSkKIyBBbHNvIG1ha2UgYSBmaWx0ZXJlZCB2ZXJzaW9uIG9mIHRoZSByZWxhdGl2ZSBhYnVuZGFuY2UgY291bnQgdGFibGUgKGZvciBwbG90dGluZyBwdXJwb3NlcykKcHNfcmFfZmlsdGVyZWQgPC0gcHJ1bmVfdGF4YSh0YXhhX25hbWVzKHBzX2ZpbHRlcmVkKSxwc19yYSkgIyBwcnVuZSBmcm9tIHBzX3JhIG9iamVjdCAocmVsYXRpdmUgYWJ1bmRhbmNlcykKIyBjaGVjayBudW1iZXIgb2YgQVNWcyBpbiBlYWNoCnBzCnBzX2ZpbHRlcmVkCnBzX3JhX2ZpbHRlcmVkCmBgYApSZWR1Y2VkIGZyb20gMTMsNDI3IHRvIDk3OSBBU1ZzCgoKCgojIyBUZXN0IGZvciBjb21wb3NpdGlvbmFsaXR5CmJhc2VkIG9uIENvZW5lbiBldCBhbC4gdHV0b3JpYWxzIGZvciBjbHVzdGVyaW5nLiBTZWUgW3JlcG9dKGh0dHBzOi8vZ2l0aHViLmNvbS9XZWl0ekdyb3VwL2FuYWx5emluZ19taWNyb2Jpb21lX3RpbWVzZXJpZXMpCmBgYHtyfQojIEVzdGltYXRlIGNvdmFyaWFuY2UgbWF0cml4IGZvciBPVFVzCmNvdmFyaWFuY2VfbWF0cml4IDwtIGFzLm1hdHJpeChvdHVfdGFibGUocHNfZmlsdGVyZWQpKSAlKiUgdChvdHVfdGFibGUocHNfZmlsdGVyZWQpKQojICUqJSA9IG1hdHJpeCBtdWx0aXBsaWNhdGlvbiBzaWduIGluIFI7IHVzZWQgaGVyZSB0byBtdWx0aXBseSBPVFUvQVNWIGRhdGEgbWF0cml4IHRvIGl0c2VsZiB0byBlc3RpbWF0ZSBjb3ZhcmlhbmNlLgojIEV2YWx1YXRlIGRldGVybWluYW50IG9mIGNvdmFyaWFuY2UgbWF0cml4CmNvdl9kZXRlcm1pbmFudCA8LSBkZXQoY292YXJpYW5jZV9tYXRyaXgpCmNvdl9kZXRlcm1pbmFudApgYGAKVGhlIGRldGVybWluYW50IG9mIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCAod2hhdCB3ZSBqdXN0IGNhbGN1bGF0ZWQpIGlzIGVxdWl2YWxlbnQgdG8gdGhlIHByb2R1Y3Qgb2YgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGV2ZXJ5IFBDQSBheGlzLiBJZiB0aGUgZGV0ZXJtaW5hbnQgaXMgMCwgdGhhdCBtZWFucyB0aGVyZSBpcyBhbiBheGlzIHdoaWNoIGV4cGxhaW5zIDAgdmFyaWFuY2UgdGhhdCB3ZSBjYW4ndCBzZXBhcmF0ZSBmcm9tIHRoZSBvdGhlciBheGVzLiBUaGlzIG1lYW5zIHRoZSBkYXRhIG5lZWQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gYmUgc3VpdGFibGUgZm9yIFBDQS4gCgoKIyMgUENBClBDQSBpcyBlc3NlbnRpYWxseSBhIHR5cGUgb2YgUENvQSAgdXNpbmcgdGhlIEV1Y2xpZGVhbiBkaXN0YW5jZSBtYXRyaXggYXMgaW5wdXQuIFdoZW4gY29tYmluZWQgd2l0aCBhIGxvZy1yYXRpbyB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgY291bnQgdGFibGUsIHRoaXMgaXMgZGVlbWVkIGFwcHJvcHJpYXRlIGZvciAqY29tcG9zaXRpb25hbCogZGF0YXNldHMuCgpGaXJzdCBkbyBhICoqQ0xSLCBjZW50ZXJlZCBsb2cgcmF0aW8qKiB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgYWJzb2x1dGUgYWJ1bmRhbmNlIGRhdGEgKGFmdGVyIGZpbHRlcmluZyksIGFzIHN1Z2dlc3RlZCBieSBbR2xvb3IgZXQgYWwuIDIwMTddKGh0dHBzOi8vd3d3LmZyb250aWVyc2luLm9yZy9hcnRpY2xlcy8xMC4zMzg5L2ZtaWNiLjIwMTcuMDIyMjQvZnVsbCkgYW5kIGNoZWNrIHRoZSBkZXRlcm1pbmFudCBvZiB0aGlzIG1hdHJpeC4gQ29tcGFyZSBpdCB0byB0aGUgZGV0ZXJtaW5hbnQgd2l0aG91dCBhbnkgdHJhbnNmb3JtYXRpb24uCgpgYGB7cn0KIyBFc3RpbWF0ZSBjb3ZhcmlhbmNlIG1hdHJpeCBmb3IgYWJzb2x1dGUgYWJ1bmRhbmNlIEFTViB0YWJsZQpjb3ZhcmlhbmNlX21hdHJpeCA8LSBhcy5tYXRyaXgob3R1X3RhYmxlKHBzX2ZpbHRlcmVkKSkgJSolIHQob3R1X3RhYmxlKHBzX2ZpbHRlcmVkKSkKCiMgRXZhbHVhdGUgZGV0ZXJtaW5hbnQgb2YgY292YXJpYW5jZSBtYXRyaXgKY292X2RldGVybWluYW50IDwtIGRldChjb3ZhcmlhbmNlX21hdHJpeCkKCiMgRXN0aW1hdGUgY292YXJpYW5jZSBtYXRyaXggZm9yIENMUi10cmFuc2Zvcm1lZCBBU1YgdGFibGUKY2xyX2Fzdl90YWJsZV9wc19maWx0ZXJlZCA8LSBkYXRhLmZyYW1lKGNvbXBvc2l0aW9uczo6Y2xyKHQob3R1X3RhYmxlKHBzX2ZpbHRlcmVkKSkpKQoKIyMgQ2hlY2sgbmV3IGRldGVybWluYW50IG9mIGNsciB0cmFuc2Zvcm1lZCB0YWJsZQpuZXdfY292ZGV0IDwtIGRldChhcy5tYXRyaXgoY2xyX2Fzdl90YWJsZV9wc19maWx0ZXJlZCkgJSolIHQoY2xyX2Fzdl90YWJsZV9wc19maWx0ZXJlZCkpCgojIENvbXBhcmUKY292X2RldGVybWluYW50ICNPcmlnaW5hbCBDb3VudCBEYXRhCm5ld19jb3ZkZXQgIyBOZXcKYGBgClRoZSBkZXRlcm1pbmFudCBvZiB0aGUgQ0xSLXRyYW5zZm9ybWVkIHRhYmxlIGlzIG5vdCB6ZXJvLCBzbyB3ZSBjYW4gcHJvY2VlZCB3aXRoIFBDQSBvZiB0aGUgQ0xSLXRyYW5zZm9ybWVkIGRhdGEuIAoKR2VuZXJhdGUgdGhlIFBDQSBhbmQgdmlzdWFsaXplIGF4ZXMKYGBge3J9CiMgR2VuZXJhdGUgYSBQcmluY2lwbGUgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpIGFuZCBldmFsdWF0ZWQgYmFzZWQgb24gdGhlIGVpZ2VuIGRlY29tcG9zaXRpb24gZnJvbSBzYW1wbGUgY292YXJpYW5jZSBtYXRyaXguIApsb2dyYXRfcGNhIDwtIHByY29tcChjbHJfYXN2X3RhYmxlX3BzX2ZpbHRlcmVkKSAKIyBOT1RFLSB0aGlzIGlzIGVxdWl2YWxlbnQgdG8gZmlyc3QgbWFraW5nIGEgRXVjbGlkZWFuIGRpc3RhbmNlIG1hdHJpeCB1c2luZyB0aGUgQ0xSIGRhdGEgdGFibGUgYW5kIHRoZW4gcnVubmluZyBhIFBDb0EuIEEgRXVjbGlkZWFuIGRpc3RhbmNlIG1hdHJpeCBvZiBhIGxvZy10cmFuc2Zvcm1lZCBkYXRhIHRhYmxlID0gYW4gQWl0Y2hpc29uIGRpc3RhbmNlIG1hdHJpeC4gU28gdGhpcyBpcyBlcXVpdmFsZW50IHRvIHRoZSBjb21wb3NpdGlvbmFsIG1ldGhvZHMgbGlzdGVkIGluIEdsb29yIGV0IGFsLgojIFZpc3VhbCByZXByZXNlbnRhdGlvbiB3aXRoIGEgc2NyZWVwbG90CmxvZ3JhdF92YXJpYW5jZXMgPC0gYXMuZGF0YS5mcmFtZShsb2dyYXRfcGNhJHNkZXZeMi9zdW0obG9ncmF0X3BjYSRzZGV2XjIpKSAlPiUgI0V4dHJhY3QgYXhlcwogICMgRm9ybWF0IHRvIHBsb3QKICBzZWxlY3QoUGVyY1ZhciA9ICdsb2dyYXRfcGNhJHNkZXZeMi9zdW0obG9ncmF0X3BjYSRzZGV2XjIpJykgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiUENheGlzIikgJT4lIAogIGRhdGEuZnJhbWUKaGVhZChsb2dyYXRfdmFyaWFuY2VzKQojIFBsb3Qgc2NyZWVwbG90CmdncGxvdChsb2dyYXRfdmFyaWFuY2VzLCBhZXMoeCA9IGFzLm51bWVyaWMoUENheGlzKSwgeSA9IFBlcmNWYXIpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImdyZXkiLCBjb2xvciA9ICJibGFjayIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHggPSAiUEMgYXhpcyIsIHkgPSAiJSBWYXJpYW5jZSIsIHRpdGxlID0gIkxvZy1SYXRpbyBQQ0EgU2NyZWVwbG90LCBDTFIgVHJhbmZvcm1hdGlvbiIpCmBgYApGaXJzdCB0d28gYXhlcyBleHBsYWluIGEgZGVjZW50IHByb3BvcnRpb24gb2YgdmFyaWFuY2U6IDI0LjggKyAxMy40ID0gMzguMgoKCgpWaXN1YWxpemUgdGhlIFBDQQpgYGB7cn0KIyBleHRyYWN0IFBDIHZhbHVlcwpwY2FfbG9ncmF0X2ZyYW1lIDwtIGRhdGEuZnJhbWUobG9ncmF0X3BjYSR4KSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJTYW1wbGUgTmFtZSIpCiMgTWVyZ2UgbWV0YWRhdGEgaW50byB0aGUgcGNhIGRhdGEgdGFibGUKcGNhX2xvZ3JhdF9mcmFtZSA8LSBsZWZ0X2pvaW4ocGNhX2xvZ3JhdF9mcmFtZSwgbWV0YWRhdGEsIGJ5ID0gIlNhbXBsZSBOYW1lIikKCgojIHJlb3JkZXIgc29tZSBmYWN0b3JzIHRvIG1ha2UgdGhlbSBwbG90IGluIHRoZSBvcmRlciBJIHdhbnQKcGNhX2xvZ3JhdF9mcmFtZSA8LSBwY2FfbG9ncmF0X2ZyYW1lICU+JQogIG11dGF0ZShTaXplRnJhY3Rpb24gPSBmY3RfcmVsZXZlbChTaXplRnJhY3Rpb24sICJQQSIsICJGTCIpKSAlPiUKICBtdXRhdGUoT3hDb25kID0gZmN0X3JlbGV2ZWwoT3hDb25kLCAiT3h5Y2xpbmUiLCAiU2hhbGxvd0Fub3hpYyIsICJFdXhpbmljIikpCnBjYV9sb2dyYXRfZnJhbWUKCgoKIyBQbG90IFBDQSB3aXRoIFJlZG94IFJlZ2ltZSBhbmQgU2l6ZSBmcmFjdGlvbgpwY2FfbG9ncmF0X3Bsb3QgPC0gZ2dwbG90KHBjYV9sb2dyYXRfZnJhbWUsIGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvciA9IE94Q29uZCkpICsKICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IFNpemVGcmFjdGlvbiksIHNpemUgPSA0KSArCiAgeWxhYihwYXN0ZTAoJ1BDMiAnLCByb3VuZChsb2dyYXRfdmFyaWFuY2VzWzIsMl0qMTAwLDIpLCclJykpICsgI0V4dHJhY3QgeSBheGlzIHZhbHVlIGZyb20gdmFyaWFuY2UKICB4bGFiKHBhc3RlMCgnUEMxICcsIHJvdW5kKGxvZ3JhdF92YXJpYW5jZXNbMSwyXSoxMDAsMiksJyUnKSkgKyAjRXh0cmFjdCB4IGF4aXMgdmFsdWUgZnJvbSB2YXJpYW5jZQogIGdndGl0bGUoJ0NMUi1FdWNsaWRlYW4gUENBJykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIsICJicm93bjQiKSkgKwogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgKwogIHRoZW1lX2J3KCkKCnBjYV9sb2dyYXRfcGxvdApgYGAKCgojIyMgRW52Rml0ClVzZSB2ZWdhbidzIGVudmZpdCB0byBkZXRlcm1pbmUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBvcmRpbmF0aW9uIGFuZCBlbnZpcm9ubWVudGFsIHZhcmlhYmxlcwoKYGBge3J9CiMgbWFrZSBtZXRhZGF0YV9vcmRpbmF0aW9ucywgdHJpbW1lZCBmcm9tIG1ldGFkYXRhIHRvIG9ubHkgc2FtcGxlcyB0aGF0IGFyZSBpbiBQQ0EgCm1ldGFkYXRhX29yZGluYXRpb25zIDwtIG1ldGFkYXRhW21ldGFkYXRhJGBTYW1wbGUgTmFtZWAgJWluJSBzYW1wbGVfZGF0YShwc19maWx0ZXJlZCkkU2FtcGxlLk5hbWUsXQoKIyByZW9yZGVyIHNvbWUgZmFjdG9ycyBpbiBtZXRhZGF0YV9vcmRpbmF0aW9ucyB0byBtYWtlIHRoZW0gcGxvdCBpbiB0aGUgb3JkZXIgSSB3YW50Cm1ldGFkYXRhX29yZGluYXRpb25zIDwtIG1ldGFkYXRhX29yZGluYXRpb25zICU+JQogIG11dGF0ZShTaXplRnJhY3Rpb24gPSBmY3RfcmVsZXZlbChTaXplRnJhY3Rpb24sICJQQSIsICJGTCIpKSAlPiUKICBtdXRhdGUoT3hDb25kID0gZmN0X3JlbGV2ZWwoT3hDb25kLCAiT3h5Y2xpbmUiLCAiU2hhbGxvd0Fub3hpYyIsICJFdXhpbmljIikpCgojIHNvcnQgY2xyX2Fzdl90YWJsZV9wc19maWx0ZXJlZCBpbiBzYW1lIG9yZGVyIGFzIG1ldGFkYXRhIApjbHJfYXN2X3RhYmxlX3BzX2ZpbHRlcmVkIDwtIGNscl9hc3ZfdGFibGVfcHNfZmlsdGVyZWRbbWV0YWRhdGFfb3JkaW5hdGlvbnMkIlNhbXBsZSBOYW1lIixdCgojIHJlLXJ1biB0aGUgUENBIG9uIGNscl9hc3ZfdGFibGVfcHNfZmlsdGVyZWQgCmxvZ3JhdF9wY2EgPC0gcHJjb21wKGNscl9hc3ZfdGFibGVfcHNfZmlsdGVyZWQpIAoKCiMgcmVtb3ZlIG1ldGFkYXRhIHRoYXQgZG9uJ3QgbWFrZSBzZW5zZSB0byB0ZXN0IChlZy4gTkNCSSBzYW1wbGUgSURzLCBldGMuKSwgIHJlcGV0aXRpdmUgdmFyaWFibGVzIChlZy4gUGFydGljdWxhdGUgUyBhbmQgVFpWUyksIGFuZCB0aG9zZSB0aGF0IGRpZG4ndCB3b3JrIG9uIGJvdGggY3J1aXNlcyAobGlrZSBmbHVvcmVzY2VuY2UsIGJlYW0gYXR0ZW51YXRpb24sIGV0YykKbWV0YWRhdGFfb3JkaW5hdGlvbnMgPC0gc2VsZWN0KG1ldGFkYXRhX29yZGluYXRpb25zLCAtUmVwbGljYXRlLCAtRmx1b3Jlc2NlbmNlLCAtQmVhbUF0dCwgLVRaVlMsIC1SdW4sIC0iQXNzYXkgVHlwZSIsIC1BdmdTcG90TGVuLCAtQmFzZXMsIC1CaW9Qcm9qZWN0LCAtQmlvU2FtcGxlLCAtQmlvU2FtcGxlTW9kZWwsIC1CeXRlcywgLSJDZW50ZXIgTmFtZSIsIC1Db2xsZWN0aW9uX0RhdGUsIC1Db25zZW50LCAtIkRBVEFTVE9SRSBmaWxldHlwZSIsIC0iREFUQVNUT1JFIHByb3ZpZGVyIiwgLSJEQVRBU1RPUkUgcmVnaW9uIiwgLUV4cGVyaW1lbnQsIC1nZW9fbG9jX25hbWVfY291bnRyeSwgLWdlb19sb2NfbmFtZV9jb3VudHJ5X2NvbnRpbmVudCwgLWdlb19sb2NfbmFtZSwgLUluc3RydW1lbnQsIC1pc29sYXRpb25fc291cmNlLCAtbGF0X2xvbiwgLSJMaWJyYXJ5IE5hbWUiLCAtTGlicmFyeUxheW91dCwgLUxpYnJhcnlTZWxlY3Rpb24sIC1MaWJyYXJ5U291cmNlLCAtT3JnYW5pc20sIC1QbGF0Zm9ybSwgLVJlbGVhc2VEYXRlLCAtc2FtcF9jb2xsZWN0X2RldmljZSwgLSJTUkEgU3R1ZHkiLCAtRGVwdGhfbSwgLXJlcGxpY2F0ZSwgLXNpemVfZnJhY3Rpb24sIC1DSDRfdU0sIC1IMlNfVW0sIC1veHlnZW4sIC1PeHlnZW5fdU0sIC1QYXJ0aWN1bGF0ZV9TdWxmdXJfdU0sIC1zYWxpbml0eSwgLVRlbXBlcmF0dXJlX2RlZ3JlZV9DLCAtVFpWU191TSkKCiMgY2hhbmdlIHRoZSBuYW1lIG9mIHNvbWUgdmFyaWFibGVzIHRvIG1ha2UgdGhlbSBlYXNpZXIgdG8gcGxvdAptZXRhZGF0YV9vcmRpbmF0aW9ucyA8LSByZW5hbWUobWV0YWRhdGFfb3JkaW5hdGlvbnMsIFBhcnRTID0gUGFydGljdWxhdGVTLCBNaWNyb0FidW4gPSAiTWljcm9BYnVuKHgxMF44IExeLTEpIiwgIEZsYWdBYnVuID0gIkZsYWdBYnVuKHgxMF41IEwtMSkiLCBWTFBBYnVuID0gIlZMUCh4MTBeOCBMLTEpIiwgQ2hlbW8gPSAiQ2hlbW9hdXRvdHJvcGh5IikKCgojIGZpdCBlbnZpcm9ubWVudGFsIGZhY3RvcnMgYW5kIHNhdmUgc3RhdHMgb3V0cHV0CnNldC5zZWVkKDEwMDEwKQpwY2FfZW52Zml0IDwtIGVudmZpdChsb2dyYXRfcGNhLCBtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGVybXV0YXRpb25zID0gMTAwMCkKY2FwdHVyZS5vdXRwdXQocGNhX2VudmZpdCwgZmlsZSA9ICJzdGF0c19yZXN1bHRzL1BDQV9lbnZmaXRfc3RhdC50eHQiKQpwY2FfZW52Zml0CiMgc2lnbmlmaWNhbnQgdmVjdG9yIHZhcmlhYmxlcywgYXQgdGhlIHA8MC4wMSBsZXZlbDogTzIsIFRlbXAsIFNhbGluaXR5LCBQYXJ0aWN1bGF0ZSBTLCBOTzMsIFBPNCwgQ2hlbW9hdXRvdHJvcGh5LCBGbGFnZWxsYXRlIEFidW5kYW5jZQojIHNpZ25pZmljYW50IGNlbnRyb2lkIHZhcmlhYmxlcyBhdCB0aGUgcDwwLjAxIGxldmVsOiBPeENvbmQgYW5kIFNpemVGcmFjdGlvbgoKIyBmaXQgc3BlY2llcyBhbmQgc2F2ZSBzdGF0cyBvdXRwdXQKcGNhX3NwcGZpdCA8LSBlbnZmaXQobG9ncmF0X3BjYSwgY2xyX2Fzdl90YWJsZV9wc19maWx0ZXJlZCwgcGVybXV0YXRpb25zID0gMTAwMCkKY2FwdHVyZS5vdXRwdXQocGNhX3NwcGZpdCwgZmlsZSA9ICJzdGF0c19yZXN1bHRzL1BDQV9zcHBmaXRfc3RhdC50eHQiKQpwY2Ffc3BwZml0CmBgYApNYW55IG9mIHRoZSB0eXBpY2FsIHZhcmlhYmxlcyB0aGF0IGluZGljYXRlIHJlZG94IGNvbmRpdGlvbiBhcmUgc2lnbmlmaWNhbnQgKE8yLCBOTzMsIFBhcnRpY3VsYXRlIFMsZXRjKSwgcGx1cyBzaXplIGZyYWN0aW9uLgpUaGVyZSBhcmUgbWFueSBzcGVjaWVzIHRoYXQgYXJlIHNpZwoKTWFrZSBpbmRpdmlkdWFsIGVudmZpdCBvYmplY3RzIGZvciBhbGwgdGhlIHZlY3RvcnMgdGhhdCB3aWxsIGJlIHBsb3R0ZWQKYGBge3J9CiMgdmVjdG9ycwpwY2FfZW52Zml0X08yIDwtIGVudmZpdChsb2dyYXRfcGNhfk8yLCBtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGVybXV0YXRpb25zID0gMTAwMCkKcGNhX2VudmZpdF9wYXJ0UyA8LSBlbnZmaXQobG9ncmF0X3BjYX5QYXJ0UywgbWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBlcm11dGF0aW9ucyA9IDEwMDApCnBjYV9lbnZmaXRfTk8zIDwtIGVudmZpdChsb2dyYXRfcGNhfk5PMywgbWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBlcm11dGF0aW9ucyA9IDEwMDApCnBjYV9lbnZmaXRfUE80IDwtIGVudmZpdChsb2dyYXRfcGNhflBPNCwgbWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBlcm11dGF0aW9ucyA9IDEwMDApCnBjYV9lbnZmaXRfdGVtcCA8LSBlbnZmaXQobG9ncmF0X3BjYX5UZW1wLCBtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGVybXV0YXRpb25zID0gMTAwMCkKcGNhX2VudmZpdF9zYWwgPC0gZW52Zml0KGxvZ3JhdF9wY2F+U2FsaW5pdHksIG1ldGFkYXRhX29yZGluYXRpb25zLCBwZXJtdXRhdGlvbnMgPSAxMDAwKQpwY2FfZW52Zml0X2NoZW1vIDwtIGVudmZpdChsb2dyYXRfcGNhfkNoZW1vLCBtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGVybXV0YXRpb25zID0gMTAwMCkKcGNhX2VudmZpdF9GbGFnQWJ1biA8LSBlbnZmaXQobG9ncmF0X3BjYX5GbGFnQWJ1biwgbWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBlcm11dGF0aW9ucyA9IDEwMDApCmBgYAoKCk5leHQsIHRyaW0gdGhlIHNwcGZpdCB2ZWdhbiBvYmplY3QgdG8ganVzdCBpbmNsdWRlIHRob3NlIHNwZWNpZXMgd2l0aCByMiB2YWx1ZSBncmVhdGVyIHRoYW4gMC42MApJIGdvdCB0aGlzIGZ1bmN0aW9uIGZyb20gW2hlcmVdKGh0dHBzOi8vd3d3LnJlc2VhcmNoZ2F0ZS5uZXQvcG9zdC9Ib3dfZG9fSV9zZXRfY3V0b2ZmX3JfdmFsdWVzX2Zvcl9wbG90dGluZ19hcnJvd3NfZnJvbV9mdW5jdGlvbl9lbnZmaXRfaW5fUikuCkxhdGVyLCB3aGVuIHBsb3R0aW5nLCBJIGNhbiBhbHNvIHRyaW0gYnkgcC12YWx1ZS8KYGBge3J9CiNfX0ZVTkNUSU9OOiBzZWxlY3QuZW52Zml0X18jCiMgZnVuY3Rpb24gKHNlbGVjdC5lbnZpdCkgZmlsdGVycyB0aGUgcmVzdWx0aW5nIGxpc3Qgb2YgZnVuY3Rpb24gKGVudmZpdCkgYmFzZWQgb24gdGhlaXIgcCB2YWx1ZXMuIFRoaXMgYWxsb3dzIHRvIGRpc3BsYXkgb25seSBzaWduaWZpY2FudCB2YWx1ZXMgaW4gdGhlIGZpbmFsIHBsb3QuCiMganVzdCBydW4gdGhpcwpzZWxlY3QuZW52Zml0PC1mdW5jdGlvbihmaXQsIHIuc2VsZWN0KXsgI25lZWRzIHR3byBzb3J0cyBvZiBpbnB1dDogZml0PSByZXN1bHQgb2YgZW52Zml0LCByLnNlbGVjdD0gbnVtZXJpYywgY29ycmVsYXRpb24gbWluaW11bSB0aHJlc2hvbGQKZm9yIChpIGluIDE6bGVuZ3RoKGZpdCR2ZWN0b3JzJHIpKSB7ICNydW4gZm9yLWxvb3AgdGhyb3VnaCB0aGUgZW50aXJlIGxlbmd0aCBvZiB0aGUgY29sdW1uIHIgaW4gb2JqZWN0IGZpdCR2ZWN0b3JzJHIgc3RhcnRpbmcgYXQgaT0xCmlmIChmaXQkdmVjdG9ycyRyW2ldPHIuc2VsZWN0KSB7ICNDaGVjayB3ZXRoZXIgcjxyLnNlbGVjdCwgaS5lLiBpZiB0aGUgY29ycmVsYXRpb24gaXMgd2Vha2VyIHRoYW4gdGhlIHRocmVzaG9sZCB2YWx1ZS4gQ2hhbmdlIHRoaXMgUGFyYW1ldGVyIGZvciByLWJhc2VkIHNlbGVjdGlvbgpmaXQkdmVjdG9ycyRhcnJvd3NbaSxdPU5BICNJZiB0aGUgYWJvdmUgc3RhdGVtZW50IGlzIFRSVUUsIGkuZS4gciBpcyBzbWFsbGVyIHRoYW4gci5zZWxlY3QsIHRoZW4gdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSB2ZWN0b3JzIGFyZSBzZXQgdG8gTkEsIHNvIHRoZXkgY2Fubm90IGJlIGRpc3BsYXllZAppPWkrMSAjaW5jcmVhc2UgdGhlIHJ1bm5pbmcgcGFyYW1ldGVyIGkgZnJvbSAxIHRvIDIsIGkuZS4gY2hlY2sgdGhlIG5leHQgdmFsdWUgaW4gdGhlIGNvbHVtbiB1bnRpbCBldmVyeSB2YWx1ZSBoYXMgYmVlbiBjaGVja2VkCn0gI2Nsb3NlIGlmLWxvb3AKfSAjY2xvc2UgZm9yLWxvb3AKcmV0dXJuKGZpdCkgI3JldHVybiBmaXQgYXMgdGhlIHJlc3VsdCBvZiB0aGUgZnVuY3Rpb24KfSAjY2xvc2UgdGhlIGZ1bmN0aW9uCgpwY2Ffc3BwZml0X3RyaW08LXNlbGVjdC5lbnZmaXQocGNhX3NwcGZpdCwgMC42KSAKYGBgCgoKIyMjIyBQbG90CkNvbXBsaWNhdGVkIHRvIHBsb3QgdmVnYW4gb3V0cHV0IGluIGdncGxvdC4gUGxvdCBpbiBiYXNlIFIKYGBge3J9CiMgQ29udmVydCBjaGFyYWN0ZXJzIGluIG1ldGFkYXRhIHRvIGZhY3RvcnMKbWV0YWRhdGFfb3JkaW5hdGlvbnMgPC0gbWV0YWRhdGFfb3JkaW5hdGlvbnMgJT4lIG11dGF0ZV9pZihzYXBwbHkobWV0YWRhdGFfb3JkaW5hdGlvbnMsIGlzLmNoYXJhY3RlciksIGFzLmZhY3RvcikKd2l0aChhcy5kYXRhLmZyYW1lKG1ldGFkYXRhX29yZGluYXRpb25zKSwgbGV2ZWxzKE94Q29uZCkpCndpdGgoYXMuZGF0YS5mcmFtZShtZXRhZGF0YV9vcmRpbmF0aW9ucyksIGxldmVscyhTaXplRnJhY3Rpb24pKQoKIyBEZWZpbmUgY29sb3JzIGFuZCBzaGFwZXMgZm9yIHBsb3QgCmNvbHZlYyA8LSBjKCJibHVlIiwgInJlZCIsICJicm93bjQiKQpzaGFwZXZlYyA8LSBjKDE2LDE3KQoKCiMgUGxvdCBoZXJlIGluIG5vdGVib29rCiMgU2V0IHVwIDJ4MiBwYW5lbHMKb3AgPC0gcGFyKG9tYT1jKDAsMCwwLDEpLCMgUm9vbSBmb3IgdGhlIHRpdGxlIGFuZCBsZWdlbmQKICBtZnJvdz1jKDIsMiksCiAgbWFpPWMoLjY1LC42NSwuMSwwKSkKIyBQYW5lbCAxLSBBZGQgZmlyc3QgaGFsZiBvZiBlbnZmaXQgdmVjdG9ycwp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBwbG90KHNjb3Jlcyhsb2dyYXRfcGNhLCBkaXNwbGF5ID0gInNpdGVzIiksIGNvbCA9IGNvbHZlY1tPeENvbmRdLCBwY2ggPSBzaGFwZXZlY1tTaXplRnJhY3Rpb25dLCBjZXggPSAxLjUsIGNleC5sYWIgPSAuOCwgY2V4LmF4aXMgPSAuOCwgeGxhYiA9ICIiLCB5bGFiID0gcGFzdGUwKCdQQzIgJywgcm91bmQobG9ncmF0X3ZhcmlhbmNlc1syLDJdKjEwMCwyKSwnJScpLCB4YXh0PSduJywgeGxpbT1jKC02MCw2MCkpKQpwbG90KHBjYV9lbnZmaXRfTzIsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siLCBjZXggPSAwLjYpCnBsb3QocGNhX2VudmZpdF9wYXJ0UywgcC5tYXggPSAwLjEsIGx3ZCA9IDIsIGNvbCA9ICJibGFjayIsIGNleCA9IDAuNikKcGxvdChwY2FfZW52Zml0X05PMywgcC5tYXggPSAwLjEsIGx3ZCA9IDIsIGNvbCA9ICJibGFjayIsIGNleCA9IDAuNikKcGxvdChwY2FfZW52Zml0X1BPNCwgcC5tYXggPSAwLjEsIGx3ZCA9IDIsIGNvbCA9ICJibGFjayIsIGNleCA9IDAuNikKdGl0bGUoIkEiLCBsaW5lID0gLTEsIGFkaiA9IDAuMDIpCiMgUGFuZWwgMi0gQWRkIHJlc3Qgb2YgZW52Zml0IHZlY3RvcnMKd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGxvdChzY29yZXMobG9ncmF0X3BjYSwgZGlzcGxheSA9ICJzaXRlcyIpLCBjb2wgPSBjb2x2ZWNbT3hDb25kXSwgcGNoID0gc2hhcGV2ZWNbU2l6ZUZyYWN0aW9uXSwgY2V4ID0gMS41LCBjZXgubGFiID0gLjgsIGNleC5heGlzID0gLjgsIHhsYWIgPSAiIiwgeWxhYiA9ICIiLCB4YXh0PSduJywgeWF4dD0nbicsIHhsaW09YygtNjAsNjApKSkKcGxvdChwY2FfZW52Zml0X3RlbXAsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siLCBjZXggPSAwLjYpCnBsb3QocGNhX2VudmZpdF9zYWwsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siLCBjZXggPSAwLjYpCnBsb3QocGNhX2VudmZpdF9GbGFnQWJ1biwgcC5tYXggPSAwLjEsIGx3ZCA9IDIsIGNvbCA9ICJibGFjayIsIGNleCA9IDAuNikKcGxvdChwY2FfZW52Zml0X2NoZW1vLCBwLm1heCA9IDAuMSwgbHdkID0gMiwgY29sID0gImJsYWNrIiwgY2V4ID0gMC42KQp0aXRsZSgiQiIsIGxpbmUgPSAtMSwgYWRqID0gMC4wMikKIyBQYW5lbCAzLSBBZGQgc3BpZGVyIGxpbmVzIGluZGljYXRpbmcgZW52Zml0IGNlbnRyb2lkcyBmb3IgU2l6ZSBGcmFjdGlvbgp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBwbG90KHNjb3Jlcyhsb2dyYXRfcGNhLCBkaXNwbGF5ID0gInNpdGVzIiksIGNvbCA9IGNvbHZlY1tPeENvbmRdLCBwY2ggPSBzaGFwZXZlY1tTaXplRnJhY3Rpb25dLCBjZXggPSAxLjUsIGNleC5sYWIgPSAuOCwgY2V4LmF4aXMgPSAuOCwgeGxhYiA9IHBhc3RlMCgnUEMxICcsIHJvdW5kKGxvZ3JhdF92YXJpYW5jZXNbMSwyXSoxMDAsMiksJyUnKSwgeWxhYiA9IHBhc3RlMCgnUEMyICcsIHJvdW5kKGxvZ3JhdF92YXJpYW5jZXNbMiwyXSoxMDAsMiksJyUnKSwgeGxpbT1jKC02MCw2MCkpKQp3aXRoKG1ldGFkYXRhX29yZGluYXRpb25zLCBvcmRpc3BpZGVyKGxvZ3JhdF9wY2EsIFNpemVGcmFjdGlvbiwgbHdkID0gMS41LCBsdHkgPSBjKDEsMiksIGxhYmVsID0gVFJVRSwgY2V4ID0gMC42KSkKdGl0bGUoIkMiLCBsaW5lID0gLTEsIGFkaiA9IDAuMDIpCiMgUGFuZWwgNCAtQWRkIHZlY3RvcnMgaW5kaWNhdGluZyBzaWduaWZpY2FudCBzcHAKd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGxvdChzY29yZXMobG9ncmF0X3BjYSwgZGlzcGxheSA9ICJzaXRlcyIpLCBjb2wgPSBjb2x2ZWNbT3hDb25kXSwgcGNoID0gc2hhcGV2ZWNbU2l6ZUZyYWN0aW9uXSwgY2V4ID0gMS41LCBjZXgubGFiID0gLjgsIGNleC5heGlzID0gLjgsIHhsYWIgPSBwYXN0ZTAoJ1BDMSAnLCByb3VuZChsb2dyYXRfdmFyaWFuY2VzWzEsMl0qMTAwLDIpLCclJyksIHlsYWIgPSAiIiwgeWF4dD0nbicsIHhsaW09YygtNjAsNjApKSkKcGxvdChwY2Ffc3BwZml0X3RyaW0sIHAubWF4ID0gMC4wMDEsIGNvbCA9ICJibGFjayIsIGNleCA9IDAuNikKIyBhbm5vdGF0ZSB0aGUgMyBjbHVzdGVycyBvZiBBU1ZzIGluIHBhbmVsIEQKdGV4dCh4PWMoMCksIHk9Yyg0NSksIGxhYmVscz1jKCJDbHVzdGVyIEkiKSwgYWRqID0gMC41LCBmb250ID0gMiwgY2V4ID0gMC44KQp0ZXh0KHg9Yyg1MCksIHk9YygzMCksIGxhYmVscz1jKCJDbHVzdGVyIElJIiksIGFkaiA9IDAuNSwgZm9udCA9IDIsIGNleCA9IDAuOCkKdGV4dCh4PWMoNDgpLCB5PWMoLTM1KSwgbGFiZWxzPWMoIkNsdXN0ZXIgSUlJIiksIGFkaiA9IDAuNSwgZm9udCA9IDIsIGNleCA9IDAuOCkKdGV4dCh4PWMoLTQ4KSwgeT1jKC0xOCksIGxhYmVscz1jKCJDbHVzdGVyIElWIiksIGFkaiA9IDAuNSwgZm9udCA9IDIsIGNleCA9IDAuOCkKdGV4dCh4PWMoLTQ4KSwgeT1jKDEwKSwgbGFiZWxzPWMoIkNsdXN0ZXIgViIpLCBhZGogPSAwLjUsIGZvbnQgPSAyLCBjZXggPSAwLjgpCnRpdGxlKCJEIiwgbGluZSA9IC0xLCBhZGogPSAwLjAyKQojIEFkZCBsZWdlbmQKcGFyKG9wKSAjIExlYXZlIHRoZSBsYXN0IHBsb3QKb3AgPC0gcGFyKHVzcj1jKDAsMSwwLDEpLCAjIFJlc2V0IHRoZSBjb29yZGluYXRlcwogICAgICAgICAgeHBkPU5BKSAgICAgICAgICMgQWxsb3cgcGxvdHRpbmcgb3V0c2lkZSB0aGUgcGxvdCByZWdpb24KbGVnZW5kKC0wLjAxOCwuNTcsIGMoIlBBIiwgIkZMIiwgIk94eWNsaW5lIiwgIlNoYWxsb3cgQW5veGljIiwgIkV1eGluaWMiKSwgY29sPWMoImJsYWNrIiwgImJsYWNrIiwiYmx1ZSIsICJyZWQiLCAiYnJvd240IiksIHBjaCA9IGMoMTYsIDE3LCAxNSwgMTUsIDE1KSwgYm94LmNvbD1OQSwgY2V4ID0gLjgsIGhvcml6ID0gVCwgeC5pbnRlcnNwID0gYygwLjMpLCB0ZXh0LndpZHRoID0gYygwLCAwLjE4LCAwLjE4LCAwLjE4LCAwLjIpKQoKCiMgU2V0IHVwIEVQUyBhbmQgbWFrZSBwbG90CnNldEVQUyh3aWR0aCA9IDYsIGhlaWdodCA9IDYpCnBvc3RzY3JpcHQoIkZpZ3VyZXMvUENBX2VudmZpdC5lcHMiKQojIFNldCB1cCAyeDIgcGFuZWxzCm9wIDwtIHBhcihvbWE9YygwLDAsMCwxKSwjIFJvb20gZm9yIHRoZSB0aXRsZSBhbmQgbGVnZW5kCiAgbWZyb3c9YygyLDIpLAogIG1haT1jKC42NSwuNjUsLjEsMCkpCiMgUGFuZWwgMS0gQWRkIGZpcnN0IGhhbGYgb2YgZW52Zml0IHZlY3RvcnMKd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGxvdChzY29yZXMobG9ncmF0X3BjYSwgZGlzcGxheSA9ICJzaXRlcyIpLCBjb2wgPSBjb2x2ZWNbT3hDb25kXSwgcGNoID0gc2hhcGV2ZWNbU2l6ZUZyYWN0aW9uXSwgY2V4ID0gMS41LCBjZXgubGFiID0gLjgsIGNleC5heGlzID0gLjgsIHhsYWIgPSAiIiwgeWxhYiA9IHBhc3RlMCgnUEMyICcsIHJvdW5kKGxvZ3JhdF92YXJpYW5jZXNbMiwyXSoxMDAsMiksJyUnKSwgeGF4dD0nbicsIHhsaW09YygtNjAsNjApKSkKcGxvdChwY2FfZW52Zml0X08yLCBwLm1heCA9IDAuMSwgbHdkID0gMiwgY29sID0gImJsYWNrIiwgY2V4ID0gMC42KQpwbG90KHBjYV9lbnZmaXRfcGFydFMsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siLCBjZXggPSAwLjYpCnBsb3QocGNhX2VudmZpdF9OTzMsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siLCBjZXggPSAwLjYpCnBsb3QocGNhX2VudmZpdF9QTzQsIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siLCBjZXggPSAwLjYpCnRpdGxlKCJBIiwgbGluZSA9IC0xLCBhZGogPSAwLjAyKQojIFBhbmVsIDItIEFkZCByZXN0IG9mIGVudmZpdCB2ZWN0b3JzCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBsb3Qoc2NvcmVzKGxvZ3JhdF9wY2EsIGRpc3BsYXkgPSAic2l0ZXMiKSwgY29sID0gY29sdmVjW094Q29uZF0sIHBjaCA9IHNoYXBldmVjW1NpemVGcmFjdGlvbl0sIGNleCA9IDEuNSwgY2V4LmxhYiA9IC44LCBjZXguYXhpcyA9IC44LCB4bGFiID0gIiIsIHlsYWIgPSAiIiwgeGF4dD0nbicsIHlheHQ9J24nLCB4bGltPWMoLTYwLDYwKSkpCnBsb3QocGNhX2VudmZpdF90ZW1wLCBwLm1heCA9IDAuMSwgbHdkID0gMiwgY29sID0gImJsYWNrIiwgY2V4ID0gMC42KQpwbG90KHBjYV9lbnZmaXRfc2FsLCBwLm1heCA9IDAuMSwgbHdkID0gMiwgY29sID0gImJsYWNrIiwgY2V4ID0gMC42KQpwbG90KHBjYV9lbnZmaXRfRmxhZ0FidW4sIHAubWF4ID0gMC4xLCBsd2QgPSAyLCBjb2wgPSAiYmxhY2siLCBjZXggPSAwLjYpCnBsb3QocGNhX2VudmZpdF9jaGVtbywgcC5tYXggPSAwLjEsIGx3ZCA9IDIsIGNvbCA9ICJibGFjayIsIGNleCA9IDAuNikKdGl0bGUoIkIiLCBsaW5lID0gLTEsIGFkaiA9IDAuMDIpCiMgUGFuZWwgMy0gQWRkIHNwaWRlciBsaW5lcyBpbmRpY2F0aW5nIGVudmZpdCBjZW50cm9pZHMgZm9yIFNpemUgRnJhY3Rpb24Kd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgcGxvdChzY29yZXMobG9ncmF0X3BjYSwgZGlzcGxheSA9ICJzaXRlcyIpLCBjb2wgPSBjb2x2ZWNbT3hDb25kXSwgcGNoID0gc2hhcGV2ZWNbU2l6ZUZyYWN0aW9uXSwgY2V4ID0gMS41LCBjZXgubGFiID0gLjgsIGNleC5heGlzID0gLjgsIHhsYWIgPSBwYXN0ZTAoJ1BDMSAnLCByb3VuZChsb2dyYXRfdmFyaWFuY2VzWzEsMl0qMTAwLDIpLCclJyksIHlsYWIgPSBwYXN0ZTAoJ1BDMiAnLCByb3VuZChsb2dyYXRfdmFyaWFuY2VzWzIsMl0qMTAwLDIpLCclJyksIHhsaW09YygtNjAsNjApKSkKd2l0aChtZXRhZGF0YV9vcmRpbmF0aW9ucywgb3JkaXNwaWRlcihsb2dyYXRfcGNhLCBTaXplRnJhY3Rpb24sIGx3ZCA9IDEuNSwgbHR5ID0gYygxLDIpLCBsYWJlbCA9IFRSVUUsIGNleCA9IDAuNikpCnRpdGxlKCJDIiwgbGluZSA9IC0xLCBhZGogPSAwLjAyKQojIFBhbmVsIDQgLUFkZCB2ZWN0b3JzIGluZGljYXRpbmcgc2lnbmlmaWNhbnQgc3BwCndpdGgobWV0YWRhdGFfb3JkaW5hdGlvbnMsIHBsb3Qoc2NvcmVzKGxvZ3JhdF9wY2EsIGRpc3BsYXkgPSAic2l0ZXMiKSwgY29sID0gY29sdmVjW094Q29uZF0sIHBjaCA9IHNoYXBldmVjW1NpemVGcmFjdGlvbl0sIGNleCA9IDEuNSwgY2V4LmxhYiA9IC44LCBjZXguYXhpcyA9IC44LCB4bGFiID0gcGFzdGUwKCdQQzEgJywgcm91bmQobG9ncmF0X3ZhcmlhbmNlc1sxLDJdKjEwMCwyKSwnJScpLCB5bGFiID0gIiIsIHlheHQ9J24nLCB4bGltPWMoLTYwLDYwKSkpCnBsb3QocGNhX3NwcGZpdF90cmltLCBwLm1heCA9IDAuMDAxLCBjb2wgPSAiYmxhY2siLCBjZXggPSAwLjYpCiMgYW5ub3RhdGUgdGhlIDMgY2x1c3RlcnMgb2YgQVNWcyBpbiBwYW5lbCBECnRleHQoeD1jKDApLCB5PWMoNDUpLCBsYWJlbHM9YygiQ2x1c3RlciBJIiksIGFkaiA9IDAuNSwgZm9udCA9IDIsIGNleCA9IDAuOCkKdGV4dCh4PWMoNTApLCB5PWMoMzApLCBsYWJlbHM9YygiQ2x1c3RlciBJSSIpLCBhZGogPSAwLjUsIGZvbnQgPSAyLCBjZXggPSAwLjgpCnRleHQoeD1jKDQ4KSwgeT1jKC0zNSksIGxhYmVscz1jKCJDbHVzdGVyIElJSSIpLCBhZGogPSAwLjUsIGZvbnQgPSAyLCBjZXggPSAwLjgpCnRleHQoeD1jKC00OCksIHk9YygtMTgpLCBsYWJlbHM9YygiQ2x1c3RlciBJViIpLCBhZGogPSAwLjUsIGZvbnQgPSAyLCBjZXggPSAwLjgpCnRleHQoeD1jKC00OCksIHk9YygxMCksIGxhYmVscz1jKCJDbHVzdGVyIFYiKSwgYWRqID0gMC41LCBmb250ID0gMiwgY2V4ID0gMC44KQp0aXRsZSgiRCIsIGxpbmUgPSAtMSwgYWRqID0gMC4wMikKIyBBZGQgbGVnZW5kCnBhcihvcCkgIyBMZWF2ZSB0aGUgbGFzdCBwbG90Cm9wIDwtIHBhcih1c3I9YygwLDEsMCwxKSwgIyBSZXNldCB0aGUgY29vcmRpbmF0ZXMKICAgICAgICAgIHhwZD1OQSkgICAgICAgICAjIEFsbG93IHBsb3R0aW5nIG91dHNpZGUgdGhlIHBsb3QgcmVnaW9uCmxlZ2VuZCgtMC4wMTgsLjU3LCBjKCJQQSIsICJGTCIsICJPeHljbGluZSIsICJTaGFsbG93IEFub3hpYyIsICJFdXhpbmljIiksIGNvbD1jKCJibGFjayIsICJibGFjayIsImJsdWUiLCAicmVkIiwgImJyb3duNCIpLCBwY2ggPSBjKDE2LCAxNywgMTUsIDE1LCAxNSksIGJveC5jb2w9TkEsIGNleCA9IC44LCBob3JpeiA9IFQsIHguaW50ZXJzcCA9IGMoMC4zKSwgdGV4dC53aWR0aCA9IGMoMCwgMC4xOCwgMC4xOCwgMC4xOCwgMC4yKSkKCmRldi5vZmYoKQoKYGBgCgpGb3IgdGhlIG1hbnVzY3JpcHQsIEkgd2FudCB0byBkaXNjdXNzIHdoYXQgdGhlc2Ugc2lnbmlmaWNhbnQgc3BlY2llcyBhcmUuIE1ha2UgYSB0YWJsZToKYGBge3J9CiMgZXh0cmFjdCBwLXZhbHVlcyBmb3IgZWFjaCBzcGVjaWVzCmZpdF9wdmFscyA8LSBwY2Ffc3BwZml0JHZlY3RvcnMkcHZhbHMgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJBU1ZJRCIpICU+JSAKICBkcGx5cjo6cmVuYW1lKCJwdmFscyIgPSAiLiIpCgojIGV4dHJhY3QgcjIgdmFsdWVzCmZpdF9yMnZhbHMgPC0gcGNhX3NwcGZpdCR2ZWN0b3JzJHIgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJBU1ZJRCIpICU+JSAKICBkcGx5cjo6cmVuYW1lKCJyMnZhbHMiID0gIi4iKQoKIyBvbmx5IGtlZXAgc3BlY2llcyB3aXRoIHAtdmFsIDwgMC4wMDEgYW5kIHIyIHZhbHVlID4wLjYKZml0X3NwcCA8LSBwY2Ffc3BwZml0ICU+JSAKICBzY29yZXMoLiwgZGlzcGxheSA9ICJ2ZWN0b3JzIikgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJBU1ZJRCIpICU+JSAKICBmdWxsX2pvaW4oLiwgZml0X3B2YWxzLCBieSA9ICJBU1ZJRCIpICU+JSAKICBmdWxsX2pvaW4oLiwgZml0X3IydmFscywgYnkgPSAiQVNWSUQiKSAlPiUgCiAgZmlsdGVyKHB2YWxzIDwgMC4wMDEpICU+JQogIGZpbHRlcihyMnZhbHMgPiAwLjYpIAojIC0tPiBmaWx0ZXJzIHRvIDEwNyBzcGVjaWVzCgojIHB1dCBpbiBBU1YgaWRlbnRpZnlpbmcgaW5mb3JtYXRpb24KcGNhX3NpZ19BU1ZzIDwtIHRheG9ub215ICU+JSAKICBtdXRhdGUoQVNWSUQgPSByb3duYW1lcyh0YXhvbm9teSkpICU+JSAKICByaWdodF9qb2luKGZpdF9zcHAsIGJ5ID0gIkFTVklEIikgCgojIHNvcnQgYnkgUEMyIHRvIGRpZmZlcmVudGlhdGUgdGhvc2UgYWJvdmUgYW5kIGJlbG93IHRoZSBQQzI9IDAgYXhpcwpwY2Ffc2lnX0FTVnMgPC0gcGNhX3NpZ19BU1ZzICU+JQogYXJyYW5nZShkZXNjKFBDMikpCgpwY2Ffc2lnX0FTVnMKCiMgdGhlIHZlZ2FuIHBsb3QgYWxzbyBzY2FsZXMgdGhlIHNwZWNpZXMgc2NvcmVzIHRvIGZpdCB0aGUgY3VycmVudCBwbG90ICh3aGljaCBpcyB3aHkgUEMgdmFsdWVzIGRvbid0IG1hdGNoIHdoYXQgaXMgc2VlbiBpbiBwbG90KSBHZXQgdGhlc2Ugc2NhbGVkIFBDIHZhbHVlcwpvcmRpQXJyb3dNdWwobG9ncmF0X3BjYSwgZGlzcGxheSA9ICJzcGVjaWVzIikgIzcuNjM2ODU2Cm9yZGlBcnJvd011bChwY2Ffc3BwZml0LCBkaXNwbGF5ID0gInZlY3RvcnMiKSAjMC44MjkxMTIxCgojIGV4cG9ydCBhcyB0YWJsZQp3cml0ZS5jc3YocGNhX3NpZ19BU1ZzLCBmaWxlPSJzdGF0c19yZXN1bHRzL3BjYV9zaWdfQVNWcy5jc3YiLCByb3cubmFtZXM9RkFMU0UpCmBgYAoKCgoKCiMgQ29ycmVsYXRpb24gQW5hbHlzZXMKCiMjIFByZXBhcmUgdGhlIGRhdGEKCiMjIyBJbXBvcnQgcHJva2FyeW90ZSBkYXRhc2V0IGZyb20gU3V0ZXIgZXQgYWwuIDIwMTgKCkltcG9ydApgYGB7cn0KYXJjaF9jb3VudHMgPC0gcmVhZF9jc3YoIlN1dGVyXzIwMThfY291bnRfdGFibGVzL0NhcmlhY29fQUFfdXBkYXRlZF9yYXcuY3N2Iik7CmJhY19jb3VudHMgPC0gcmVhZF9jc3YoIlN1dGVyXzIwMThfY291bnRfdGFibGVzL0NhcmlhY29fQUJfdXBkYXRlZF9yYXcuY3N2Iik7CmBgYAoKR2V0IHNhbXBsZSBuYW1lcwpgYGB7cn0KYmFjX3NhbXBsZXMgPC0gY29sbmFtZXMoYmFjX2NvdW50cylbMjo0OV0KYXJjaF9zYW1wbGVzIDwtIGNvbG5hbWVzKGFyY2hfY291bnRzKVsyOjQ3XQoKYmFjX3NhbXBsZXMKYXJjaF9zYW1wbGVzCmBgYAoKTWFrZSBzZXBhcmF0ZSB0YXhvbm9teSBhbmQgY291bnQgdmFyaWFibGVzCmBgYHtyfQphcmNoX09UVSA8LSBhcmNoX2NvdW50c1ssYygiI09UVSBJRCIsYXJjaF9zYW1wbGVzKV0KYXJjaF90YXhvbm9teSA8LSAgYXJjaF9jb3VudHMgJT4lCiAgc2VsZWN0KC1hcmNoX3NhbXBsZXMpICAlPiUKICBzZWxlY3QoLVN1bSkKCmFyY2hfT1RVCmFyY2hfdGF4b25vbXkKCmJhY19PVFUgPC0gYmFjX2NvdW50c1ssYygiI09UVSBJRCIsYmFjX3NhbXBsZXMpXQpiYWNfdGF4b25vbXkgPC0gIGJhY19jb3VudHMgJT4lCiAgc2VsZWN0KC1iYWNfc2FtcGxlcykgICU+JQogIHNlbGVjdCgtU3VtKSAlPiUKICBzZWxlY3QoLSJJbnRlcmVzdGluZyBjbG9zZSByZWxhdGl2ZXMiKQoKYmFjX09UVQpiYWNfdGF4b25vbXkKYGBgCgoKIyMjIE1ha2UgaW50byBwaHlsb3NlcSBvYmplY3RzCmBgYHtyfQpiYWNfT1RVIDwtIHR5cGVfY29udmVydChhcy5kYXRhLmZyYW1lKGJhY19PVFUpKQpyb3duYW1lcyhiYWNfT1RVKSA8LSBiYWNfT1RVJGAjT1RVIElEYApiYWNfT1RVIDwtIGJhY19PVFVbLCFuYW1lcyhiYWNfT1RVKSAlaW4lIChjKCIjT1RVIElEIikpXQoKYmFjX09UVQk9CW90dV90YWJsZShiYWNfT1RVLCB0YXhhX2FyZV9yb3dzID0gIFRSVUUpCiMKYXJjaF9PVFUgPC0gdHlwZV9jb252ZXJ0KGFzLmRhdGEuZnJhbWUoYXJjaF9PVFUpKQpyb3duYW1lcyhhcmNoX09UVSkgPC0gYXJjaF9PVFUkYCNPVFUgSURgCmFyY2hfT1RVIDwtIGFyY2hfT1RVWywhbmFtZXMoYXJjaF9PVFUpICVpbiUgKGMoIiNPVFUgSUQiKSldCgphcmNoX09UVQk9CW90dV90YWJsZShhcmNoX09UVSwgdGF4YV9hcmVfcm93cyA9ICBUUlVFKQojCmJhY19UQVggPC0gdHlwZV9jb252ZXJ0KGFzLmRhdGEuZnJhbWUoYmFjX3RheG9ub215KSkKcm93bmFtZXMoYmFjX1RBWCkgPC0gYmFjX1RBWCRgI09UVSBJRGAKYmFjX1RBWCA8LSBiYWNfVEFYWywhbmFtZXMoYmFjX1RBWCkgJWluJSAoYygiI09UVSBJRCIpKV0KCmJhY19UQVgJPQl0YXhfdGFibGUoYXMubWF0cml4KGJhY19UQVgpKQojCmFyY2hfVEFYIDwtIHR5cGVfY29udmVydChhcy5kYXRhLmZyYW1lKGFyY2hfdGF4b25vbXkpKQpyb3duYW1lcyhhcmNoX1RBWCkgPC0gYXJjaF9UQVgkYCNPVFUgSURgCmFyY2hfVEFYIDwtIGFyY2hfVEFYWywhbmFtZXMoYXJjaF9UQVgpICVpbiUgKGMoIiNPVFUgSUQiKSldCgphcmNoX1RBWAk9CXRheF90YWJsZShhcy5tYXRyaXgoYXJjaF9UQVgpKQojCk1FVEEJPQlzYW1wbGVfZGF0YShkYXRhLmZyYW1lKG1ldGFkYXRhLCByb3cubmFtZXMgPSBtZXRhZGF0YSRgU2FtcGxlIE5hbWVgKSkKIwoKcHNfYmFjIDwtIHBoeWxvc2VxKGJhY19PVFUsCWJhY19UQVgsCU1FVEEpCnBzX2FyY2ggPC0gcGh5bG9zZXEoYXJjaF9PVFUsCWFyY2hfVEFYLAlNRVRBKQpgYGAKCkZpbHRlciBvdXQgdGhlIHNhbXBsZXMgd2l0aCBsb3cgc2VxdWVuY2luZyBlZmZvcnQuIFRoZXNlIHdlcmUgcHJldmlvdXNseSBpZGVudGlmaWVkIGZvciBpdGFncyBwYXBlcgoKYGBge3J9CnRheGFfdG9fa2VlcF9iIDwtICFzYW1wbGVfbmFtZXMocHNfYmFjKSAlaW4lIGMoIkFCM2E5MDBBIiwiQUIyYTIwMEEiLCJBQjJiMjY3QSIpCnBzX2JhYyA8LSBwcnVuZV9zYW1wbGVzKHRheGFfdG9fa2VlcF9iLCBwc19iYWMpCgp0YXhhX3RvX2tlZXBfYSA8LSAhc2FtcGxlX25hbWVzKHBzX2FyY2gpICVpbiUgYygiQUEyYjkwMEFOIiwiQUEyYTI0N0IiLCJBQTJhOTAwQk4iLCJBQTJiOTAwQk4iKQpwc19hcmNoIDwtIHBydW5lX3NhbXBsZXModGF4YV90b19rZWVwX2EsIHBzX2FyY2gpCmBgYAoKCiMjIyBGaWx0ZXJpbmcKCkZpcnN0IGNhbGN1bGF0ZSByZWxhdGl2ZSBhYmR1bmFuY2Ugb2YgYmFjIGFuZCBhcmNoIE9UVSB0YWJsZXMKYGBge3J9CnBzX2JhY19yYSA8LSBtaWNyb2Jpb21lOjp0cmFuc2Zvcm0ocHNfYmFjLCB0cmFuc2Zvcm0gPSAiY29tcG9zaXRpb25hbCIpCihvdHVfdGFibGUocHNfYmFjX3JhKSlbMTo1LDE6NV0KCnBzX2FyY2hfcmEgPC0gbWljcm9iaW9tZTo6dHJhbnNmb3JtKHBzX2FyY2gsIHRyYW5zZm9ybSA9ICJjb21wb3NpdGlvbmFsIikKKG90dV90YWJsZShwc19hcmNoX3JhKSlbMTo1LDE6NV0KYGBgCgoKIyMjIEZpbHRlciBsb3cgYWJ1bmRhbmNlIHNwZWNpZXMgZnJvbSBmdWxsIGRhdGFzZXQKClJlbW92ZSByb3dzIG9mIGdsb21tZWQgdGF4YSBmcm9tIHRoZSBmdWxsIGRhdGFmcmFtZSBpZiB0aGVpciBzdW0gYWNyb3NzIGFsbCBzYW1wbGVzIGRvZXNuJ3QgZXhjZWVkIDUlIChSQSA+IDAuMDUpIApgYGB7cn0KIyBCYWN0ZXJpYQp4IDwtIHRheGFfc3Vtcyhwc19iYWNfcmEpCiMga2VlcFRheGEgPC0gIGJhc2U6OndoaWNoKHggID4gLjA1KQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2JhY19yYV9wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2JhY19yYSkKcHNfYmFjX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYmFjKQpwc19iYWNfcmFfcHJ1bmVkCnBzX2JhY19wcnVuZWQKCiMgQXJjaGFlYQp4IDwtIHRheGFfc3Vtcyhwc19hcmNoX3JhKQojIGtlZXBUYXhhIDwtICBiYXNlOjp3aGljaCh4ICA+IC4wNSkKa2VlcFRheGEgPC0gIHg+LjA1ICMgcHJ1bmVfdGF4YSByZXF1aXJlIGEgbG9naWNhbCBub3QgYSBsaXN0IG9mIElEcy4gY29tcGFyZSB0byBrZWVwVGF4YSBhYm92ZSB0byBjaGVjawpwc19hcmNoX3JhX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYXJjaF9yYSkKcHNfYXJjaF9wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2FyY2gpCnBzX2FyY2hfcmFfcHJ1bmVkCnBzX2FyY2hfcHJ1bmVkCgojIEV1a2FyeW90ZXMKeCA8LSB0YXhhX3N1bXMocHNfcmEpCiMga2VlcFRheGEgPC0gIGJhc2U6OndoaWNoKHggID4gLjA1KQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2V1a19yYV9wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX3JhKQpwc19ldWtfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwcykKcHNfZXVrX3JhX3BydW5lZApwc19ldWtfcHJ1bmVkCmBgYApUcmltbWVkIHRvIDEyNCBiYWN0ZXJpYSBPVFVzLCA1MiBhcmNoYWVhIE9UVXMsIGFuZCAxMjMgZXVrYXJ5b3RpYyBBU1ZzICgyOTkgdG90YWwpLiBQcm9jZWVkIHdpdGggdGhpcyBkYXRhc2V0IG9mIHRoZSBtb3N0IGFidW5kYW50IE9UVXMgZm9yIGNvcnJlbGF0aW9ucyBhbmQgbmV0d29yayBhbmFseXNlcy4uLgoKVG8gZG8gdGhlIG11bHRpLWRvbWFpbiBhbmFseXNpcywgdGhlIHNhbXBsZSBuYW1lcyBmcm9tIGVhY2ggcGh5bG9zZXEgb2JqZWN0IG11c3QgbWF0Y2guIFRoZXNlIGN1cnJlbnRseSBoYXZlICJCIiBmb3IgYmFjdGVyaWEsIEEsIEUgZXRjLiBSZW1vdmUgdGhpcyBsZXR0ZXIgZnJvbSBzYW1wbGUgbmFtZXMgc28gdGhhdCAiQUUyYTI0N0IiLCAiQUEyYTI0N0IiLCAiQUIyYTI0N0IiIGFsbCBiZWNvbWUganVzdCAiVHlwZSIgZnJvbSB0aGUgbWV0YWRhdGEgc2hlZXQgW0ludE5vdjFGTCBpbiB0aGlzIGNhc2UtIGZvciBJbnRlcmZhY2UsIE5vdmVtYmVyLCByZXAgMSwgZnJlZS1saXZpbmddLiAKCkltcG9ydCBteSBTYW1wbGVLZXkKYGBge3J9CnNhbXBsZWtleSA8LSByZWFkX2NzdigiU2FtcGxlS2V5LmNzdiIpCmBgYAoKCkNoYW5nZSB0aGUgc2FtcGxlIG5hbWVzIGluIHRoZSBvdHUgdGFibGVzIHRvIHNhbXBsZSAiVHlwZSIKYGBge3J9CiMgQXJjaGFlYQojIHJlbW92ZSBtaXNzaW5nIGFyY2hhZWEgc2FtcGxlcyBmcm9tIHNhbXBsZWtleV9BCnNhbXBsZWtleV9BIDwtIGZpbHRlcihzYW1wbGVrZXksIFNhbXBsZUlEX2FyY2ggICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2FyY2hfcmFfcHJ1bmVkKSkpCiMgc29ydCBTYW1wbGVLZXkgYnkgb3JkZXIgb2YgY29sdW1uIG5hbWVzIGZyb20gcHNfYXJjaF9yYV9wcnVuZWQKc2FtcGxla2V5X0EgPC0gc2FtcGxla2V5X0EgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2FyY2gsIGxldmVscyA9IGNvbG5hbWVzKG90dV90YWJsZShwc19hcmNoX3JhX3BydW5lZCkpKSkKIyByZXBsYWNlIGNvbCBuYW1lcyBvZiBvdHUgdGFibGUgZnJvbSBwc19hcmNoX3JhX3BydW5lZApzYW1wbGVfbmFtZXMocHNfYXJjaF9yYV9wcnVuZWQpIDwtIHNhbXBsZWtleV9BJFR5cGUKIyBhbmQgcHNfYXJjaF9wcnVuZWQKc2FtcGxlX25hbWVzKHBzX2FyY2hfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQSRUeXBlCgoKIyBCYWN0ZXJpYQpzYW1wbGVrZXlfQiA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9iYWMgICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2JhY19yYV9wcnVuZWQpKSkKc2FtcGxla2V5X0IgPC0gc2FtcGxla2V5X0IgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2JhYywgbGV2ZWxzID0gY29sbmFtZXMob3R1X3RhYmxlKHBzX2JhY19yYV9wcnVuZWQpKSkpCnNhbXBsZV9uYW1lcyhwc19iYWNfcmFfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQiRUeXBlCnNhbXBsZV9uYW1lcyhwc19iYWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQiRUeXBlCgoKIyBFdWthcnlvdGVzCnNhbXBsZWtleV9FIDwtIGZpbHRlcihzYW1wbGVrZXksIFNhbXBsZUlEX2V1ayAgJWluJSBjb2xuYW1lcyhvdHVfdGFibGUocHNfZXVrX3JhX3BydW5lZCkpKQpzYW1wbGVrZXlfRSA8LSBzYW1wbGVrZXlfRSAlPiUgYXJyYW5nZShmYWN0b3IoU2FtcGxlSURfZXVrLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfZXVrX3JhX3BydW5lZCkpKSkKc2FtcGxlX25hbWVzKHBzX2V1a19yYV9wcnVuZWQpIDwtIHNhbXBsZWtleV9FJFR5cGUKc2FtcGxlX25hbWVzKHBzX2V1a19wcnVuZWQpIDwtIHNhbXBsZWtleV9FJFR5cGUKCgpgYGAKCgpNb3ZlIGFsbCBwcnVuZWQgb3R1IHRhYmxlcyBpbnRvIG9uZSB0YWJsZSBieSBtYXRjaGluZyB0aGUgc2FtcGxlIFR5cGUtIHdpbGwgdXNlIHRoaXMgZm9yIFNwYXJDQwpNYWtlIG9uZSBmb3IgdGhlIDMtZG9tYWluIGFuYWx5c2lzIGFuZCBvbmUgZm9yIHRoZSAyLWRvbWFpbiBhbmFseXNpcyAoYmFjdGVyaWEgYW5kIGFyY2hhZWEgb25seSkKYGBge3J9CmFsbGRvbWFpbnNfZGYgPC0gYmluZF9yb3dzKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2JhY19wcnVuZWQpKSwgZGF0YS5mcmFtZShvdHVfdGFibGUocHNfYXJjaF9wcnVuZWQpKSwgZGF0YS5mcmFtZShvdHVfdGFibGUocHNfZXVrX3BydW5lZCkpKQphbGxkb21haW5zX2RmCgp0d29kb21haW5zX2RmIDwtIGJpbmRfcm93cyhkYXRhLmZyYW1lKG90dV90YWJsZShwc19iYWNfcHJ1bmVkKSksIGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2FyY2hfcHJ1bmVkKSkpCnR3b2RvbWFpbnNfZGYKYGBgCgpDaGFuZ2Ugcm93IG5hbWVzIGZyb20gImRlbm92b1hYWCIgdG8gbWVhbmluZ2Z1bCBuYW1lcwpgYGB7cn0KYWxsZG9tYWluc19kZl9mdWxsIDwtIGNiaW5kKElEID0gcm93bmFtZXMoYWxsZG9tYWluc19kZiksIGFsbGRvbWFpbnNfZGYpCnR3b2RvbWFpbnNfZGZfZnVsbCA8LSBjYmluZChJRCA9IHJvd25hbWVzKHR3b2RvbWFpbnNfZGYpLCB0d29kb21haW5zX2RmKQoKCiMgc3RhcnQgd2l0aCBvbmx5IGZpcnN0IHJvd3MsIHdoaWNoIGFyZSBiYWN0ZXJpYS4gbWFrZSBvbmUgY29sdW1uIG9mIG1lYW5pbmdmdWwgbGFiZWxzCnRlbXAxIDwtIGxlZnRfam9pbihhbGxkb21haW5zX2RmX2Z1bGxbMTpkaW0ob3R1X3RhYmxlKHBzX2JhY19wcnVuZWQpKVsxXSxdLCBiYWNfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNPVFUgSUQiKSkgCnRlbXAxJE5ld19JRCA8LSBwYXN0ZSh0ZW1wMSRJRCwgdGVtcDEkInRheG9ub215LTIiLCB0ZW1wMSQidGF4b25vbXktMyIsIHRlbXAxJCJ0YXhvbm9teS00IikKdGVtcDEgPC0gc2VsZWN0KHRlbXAxLC1jb2xuYW1lcyhiYWNfdGF4b25vbXlbLDI6MTFdKSkKCiMgbmV4dCByb3dzIGFyZSB0aGUgYXJjaGFlYSAKdGVtcDIgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbFtzdW0oZGltKG90dV90YWJsZShwc19iYWNfcHJ1bmVkKSlbMV0sMSk6c3VtKGRpbShvdHVfdGFibGUocHNfYmFjX3BydW5lZCkpWzFdLGRpbShvdHVfdGFibGUocHNfYXJjaF9wcnVuZWQpKVsxXSksXSwgYXJjaF90YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI09UVSBJRCIpKSAKdGVtcDIkTmV3X0lEIDwtIHBhc3RlKHRlbXAyJElELCB0ZW1wMiQidGF4b25vbXktMiIsIHRlbXAyJCJ0YXhvbm9teS0zIikKdGVtcDIgPC0gc2VsZWN0KHRlbXAyLC1jb2xuYW1lcyhhcmNoX3RheG9ub215WywyOjldKSkKCgojIGxhc3Qgcm93cyBhcmUgZXVrYXJ5YQpldWtfdGF4b25vbXkgPC0gY2JpbmQoIiNBU1YgSUQiID0gcm93bmFtZXModGF4b25vbXkpLCB0YXhvbm9teSkKdGVtcDMgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbFtzdW0oZGltKG90dV90YWJsZShwc19hcmNoX3BydW5lZCkpWzFdLCBkaW0ob3R1X3RhYmxlKHBzX2JhY19wcnVuZWQpKVsxXSwxKTpzdW0oZGltKG90dV90YWJsZShwc19hcmNoX3BydW5lZCkpWzFdLCBkaW0ob3R1X3RhYmxlKHBzX2JhY19wcnVuZWQpKVsxXSxkaW0ob3R1X3RhYmxlKHBzX2V1a19wcnVuZWQpKVsxXSksXSwgZXVrX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjQVNWIElEIikpIAp0ZW1wMyROZXdfSUQgPC0gcGFzdGUodGVtcDMkSUQsIHRlbXAzJCJTdXBlcmdyb3VwIiwgdGVtcDMkIkRpdmlzaW9uIiwgdGVtcDMkIkNsYXNzIiwgdGVtcDMkIk9yZGVyIikKdGVtcDMgPC0gc2VsZWN0KHRlbXAzLC1jb2xuYW1lcyhldWtfdGF4b25vbXlbLDI6OV0pKQoKIyBjb21iaW5lIGJhY2sgYWxsIDMgZG9tYWlucywgd2l0aCBuZXcgbmFtZXMgYXMgcm93IG5hbWVzIGluIGEgZGF0YWZyYW1lCmFsbGRvbWFpbnNfZGZfZnVsbCA8LSByYmluZCh0ZW1wMSwgdGVtcDIsIHRlbXAzKQphbGxkb21haW5zX2RmX2Z1bGwgPC0gZGF0YS5mcmFtZShhbGxkb21haW5zX2RmX2Z1bGwpCnJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbCkgPC0gYWxsZG9tYWluc19kZl9mdWxsJE5ld19JRAphbGxkb21haW5zX2RmX2Z1bGwgPC0gc2VsZWN0KGFsbGRvbWFpbnNfZGZfZnVsbCwgLWMoIklEIiwiTmV3X0lEIikpCgojIGFuZCBtYWtlIG9uZSBmb3IgdGhlIDItZG9tYWluIGRhdGFzZXQKdHdvZG9tYWluc19kZl9mdWxsIDwtIHJiaW5kKHRlbXAxLCB0ZW1wMikKdHdvZG9tYWluc19kZl9mdWxsIDwtIGRhdGEuZnJhbWUodHdvZG9tYWluc19kZl9mdWxsKQpyb3duYW1lcyh0d29kb21haW5zX2RmX2Z1bGwpIDwtIHR3b2RvbWFpbnNfZGZfZnVsbCROZXdfSUQKdHdvZG9tYWluc19kZl9mdWxsIDwtIHNlbGVjdCh0d29kb21haW5zX2RmX2Z1bGwsIC1jKCJJRCIsIk5ld19JRCIpKQoKCmBgYAoKCgpSZW1vdmUgY29sdW1ucyB3aXRoIE5Bcy4gVGhlc2UgYXJlIHNhbXBsZXMgZm9yIHdoaWNoIHRoZSBsaWJyYXJ5IGZvciBhdCBsZWFzdCBvbmUgZG9tYWluIGRpZG4ndCB3b3JrIChjYW4ndCBkbyBjb3JyZWxhdGlvbnMgd2l0aCBtaXNzaW5nIHZhbHVlcyBpbiBjb2x1bW5zKQpgYGB7cn0KYWxsZG9tYWluc19kZl9mdWxsIDwtIGFsbGRvbWFpbnNfZGZfZnVsbCAlPiUKICAgIHNlbGVjdF9pZih+ICFhbnkoaXMubmEoLikpKQphbGxkb21haW5zX2RmX2Z1bGwKCmFsbGRvbWFpbnNfZGYgPC0gYWxsZG9tYWluc19kZiAlPiUKICAgIHNlbGVjdF9pZih+ICFhbnkoaXMubmEoLikpKQphbGxkb21haW5zX2RmCgp0d29kb21haW5zX2RmX2Z1bGwgPC0gdHdvZG9tYWluc19kZl9mdWxsICU+JQogICAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpCnR3b2RvbWFpbnNfZGZfZnVsbAoKdHdvZG9tYWluc19kZiA8LSB0d29kb21haW5zX2RmICU+JQogICAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpCnR3b2RvbWFpbnNfZGYKYGBgCgoKCiMjIyBGaWx0ZXIgbG93IGFidW5kYW5jZSBzcGVjaWVzLSBveHljbGluZSBkZXB0aHMgb25seQpTaW1sYXJseSwgbWFrZSBwcnVuZWQgZGF0YXNldHMgb2YgdGhlIG1vc3QgYWJ1bmRhbnQgT1RVcy9BU1ZzIGluIHRoZSBveHljbGluZSwgYW5veGljLCBhbmQgZXV4aW5pYyBzYW1wbGVzIGFzIHNlcGFyYXRlIGRhdGFzZXRzCgpQdWxsIG91dCBzYW1wbGVzIGFuZCB0YXhhIGZyb20gZWFjaCByZWRveCByZWdpbWUKYGBge3J9CiMgUHVsbCBvdXQgb3h5Y2xpbmUgYmFjdGVyaWEgc2FtcGxlIElEcwpveHljbGluZXR5cGVzX2JhYyA8LSBtZXRhZGF0YSAlPiUgCiAgZmlsdGVyKGBTYW1wbGUgTmFtZWAgJWluJSBzYW1wbGVfbmFtZXMocHNfYmFjKSkgJT4lCiAgZmlsdGVyKE94Q29uZCA9PSAiT3h5Y2xpbmUiKSAlPiUgCiAgc2VsZWN0KCJTYW1wbGUgTmFtZSIpCm94eWNsaW5ldHlwZXNfYmFjIDwtIHVubGlzdChjKHVuaXF1ZShveHljbGluZXR5cGVzX2JhYykpLCB1c2UubmFtZXMgPSBGQUxTRSkKCiMgUHVsbCBvdXQgYWxsIGJhY3RlcmlhIGZyb20gb3h5Y2xpbmUKcHNfYmFjX294eWNsaW5lIDwtICBwcnVuZV9zYW1wbGVzKG94eWNsaW5ldHlwZXNfYmFjLCBwc19iYWMpCnBzX2JhY19yYV9veHljbGluZSA8LSAgcHJ1bmVfc2FtcGxlcyhveHljbGluZXR5cGVzX2JhYywgcHNfYmFjX3JhKQoKCiMgUHVsbCBvdXQgb3h5Y2xpbmUgYXJjaGFlYSBzYW1wbGUgSURzCm94eWNsaW5ldHlwZXNfYXJjaCA8LSBtZXRhZGF0YSAlPiUgCiAgZmlsdGVyKGBTYW1wbGUgTmFtZWAgJWluJSBzYW1wbGVfbmFtZXMocHNfYXJjaCkpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIk94eWNsaW5lIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQpveHljbGluZXR5cGVzX2FyY2ggPC0gdW5saXN0KGModW5pcXVlKG94eWNsaW5ldHlwZXNfYXJjaCkpLCB1c2UubmFtZXMgPSBGQUxTRSkKCiMgUHVsbCBvdXQgYWxsIGFyY2hhZWEgZnJvbSBveHljbGluZQpwc19hcmNoX294eWNsaW5lIDwtICBwcnVuZV9zYW1wbGVzKG94eWNsaW5ldHlwZXNfYXJjaCwgcHNfYXJjaCkKcHNfYXJjaF9yYV9veHljbGluZSA8LSAgcHJ1bmVfc2FtcGxlcyhveHljbGluZXR5cGVzX2FyY2gsIHBzX2FyY2hfcmEpCgoKIyBQdWxsIG91dCBveHljbGluZSBldWthcnlvdGljIHNhbXBsZSBJRHMKb3h5Y2xpbmV0eXBlc19ldWsgPC0gbWV0YWRhdGEgJT4lIAogIGZpbHRlcihgU2FtcGxlIE5hbWVgICVpbiUgc2FtcGxlX25hbWVzKHBzKSkgJT4lCiAgZmlsdGVyKE94Q29uZCA9PSAiT3h5Y2xpbmUiKSAlPiUgCiAgc2VsZWN0KCJTYW1wbGUgTmFtZSIpCm94eWNsaW5ldHlwZXNfZXVrIDwtIHVubGlzdChjKHVuaXF1ZShveHljbGluZXR5cGVzX2V1aykpLCB1c2UubmFtZXMgPSBGQUxTRSkKCiMgUHVsbCBvdXQgYWxsIGV1a2FyeW90ZXMgZnJvbSBveHljbGluZQpwc19ldWtfb3h5Y2xpbmUgPC0gIHBydW5lX3NhbXBsZXMob3h5Y2xpbmV0eXBlc19ldWssIHBzKQpwc19ldWtfcmFfb3h5Y2xpbmUgPC0gIHBydW5lX3NhbXBsZXMob3h5Y2xpbmV0eXBlc19ldWssIHBzX3JhKQoKYGBgCgpGaWx0ZXIgb3V0IGxvdyBhYnVuZGFuY2UgdGF4YSBmcm9tIHRoZSBveHljbGluZSBzYW1wbGVzLiBVc2UgNSUgYXMgY3V0b2ZmIApgYGB7cn0KIyBCYWN0ZXJpYQp4IDwtIHRheGFfc3Vtcyhwc19iYWNfcmFfb3h5Y2xpbmUpCmtlZXBUYXhhIDwtICB4Pi4wNSAjIHBydW5lX3RheGEgcmVxdWlyZSBhIGxvZ2ljYWwgbm90IGEgbGlzdCBvZiBJRHMuIGNvbXBhcmUgdG8ga2VlcFRheGEgYWJvdmUgdG8gY2hlY2sKcHNfYmFjX3JhX294eWNsaW5lX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYmFjX3JhX294eWNsaW5lKQpwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19iYWNfb3h5Y2xpbmUpCnBzX2JhY19yYV9veHljbGluZV9wcnVuZWQKcHNfYmFjX294eWNsaW5lX3BydW5lZAoKIyBBcmNoYWVhCnggPC0gdGF4YV9zdW1zKHBzX2FyY2hfcmFfb3h5Y2xpbmUpCmtlZXBUYXhhIDwtICB4Pi4wNSAjIHBydW5lX3RheGEgcmVxdWlyZSBhIGxvZ2ljYWwgbm90IGEgbGlzdCBvZiBJRHMuIGNvbXBhcmUgdG8ga2VlcFRheGEgYWJvdmUgdG8gY2hlY2sKcHNfYXJjaF9yYV9veHljbGluZV9wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2FyY2hfcmFfb3h5Y2xpbmUpCnBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19hcmNoX294eWNsaW5lKQpwc19hcmNoX3JhX294eWNsaW5lX3BydW5lZApwc19hcmNoX294eWNsaW5lX3BydW5lZAoKIyBFdWthcnlvdGVzCnggPC0gdGF4YV9zdW1zKHBzX2V1a19yYV9veHljbGluZSkKa2VlcFRheGEgPC0gIHg+LjA1ICMgcHJ1bmVfdGF4YSByZXF1aXJlIGEgbG9naWNhbCBub3QgYSBsaXN0IG9mIElEcy4gY29tcGFyZSB0byBrZWVwVGF4YSBhYm92ZSB0byBjaGVjawpwc19ldWtfcmFfb3h5Y2xpbmVfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19ldWtfcmFfb3h5Y2xpbmUpCnBzX2V1a19veHljbGluZV9wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2V1a19veHljbGluZSkKcHNfZXVrX3JhX294eWNsaW5lX3BydW5lZApwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkCgpgYGAKNzkgYmFjdGVyaWEsIDM2IGFyY2hhZWEsIDc2IGV1a2FyeW90YSByZW1haW4KCgpDaGFuZ2UgdGhlIHNhbXBsZSBuYW1lcyBpbiB0aGUgb3R1IHRhYmxlcyB0byAiVHlwZSIKYGBge3J9CiMgQXJjaGFlYQojIHJlbW92ZSBtaXNzaW5nIGFyY2hhZWEgc2FtcGxlcyBmcm9tIHNhbXBsZWtleV9BCnNhbXBsZWtleV9BIDwtIGZpbHRlcihzYW1wbGVrZXksIFNhbXBsZUlEX2FyY2ggICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2FyY2hfcmFfb3h5Y2xpbmVfcHJ1bmVkKSkpCiMgc29ydCBTYW1wbGVLZXkgYnkgb3JkZXIgb2YgY29sdW1uIG5hbWVzIGZyb20gcHNfYXJjaF9yYV9veHljbGluZV9wcnVuZWQKc2FtcGxla2V5X0EgPC0gc2FtcGxla2V5X0EgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2FyY2gsIGxldmVscyA9IGNvbG5hbWVzKG90dV90YWJsZShwc19hcmNoX3JhX294eWNsaW5lX3BydW5lZCkpKSkKIyByZXBsYWNlIGNvbCBuYW1lcyBvZiBvdHUgdGFibGUgZnJvbSBwc19hcmNoX3JhX294eWNsaW5lX3BydW5lZApzYW1wbGVfbmFtZXMocHNfYXJjaF9yYV9veHljbGluZV9wcnVuZWQpIDwtIHNhbXBsZWtleV9BJFR5cGUKIyBhbmQgcHNfYXJjaF9wcnVuZWQKc2FtcGxlX25hbWVzKHBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQSRUeXBlCgoKIyBCYWN0ZXJpYQpzYW1wbGVrZXlfQiA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9iYWMgICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2JhY19yYV9veHljbGluZV9wcnVuZWQpKSkKc2FtcGxla2V5X0IgPC0gc2FtcGxla2V5X0IgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2JhYywgbGV2ZWxzID0gY29sbmFtZXMob3R1X3RhYmxlKHBzX2JhY19yYV9veHljbGluZV9wcnVuZWQpKSkpCnNhbXBsZV9uYW1lcyhwc19iYWNfcmFfb3h5Y2xpbmVfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQiRUeXBlCnNhbXBsZV9uYW1lcyhwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQiRUeXBlCgoKIyBFdWthcnlvdGVzCnNhbXBsZWtleV9FIDwtIGZpbHRlcihzYW1wbGVrZXksIFNhbXBsZUlEX2V1ayAgJWluJSBjb2xuYW1lcyhvdHVfdGFibGUocHNfZXVrX3JhX294eWNsaW5lX3BydW5lZCkpKQpzYW1wbGVrZXlfRSA8LSBzYW1wbGVrZXlfRSAlPiUgYXJyYW5nZShmYWN0b3IoU2FtcGxlSURfZXVrLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfZXVrX3JhX294eWNsaW5lX3BydW5lZCkpKSkKc2FtcGxlX25hbWVzKHBzX2V1a19yYV9veHljbGluZV9wcnVuZWQpIDwtIHNhbXBsZWtleV9FJFR5cGUKc2FtcGxlX25hbWVzKHBzX2V1a19veHljbGluZV9wcnVuZWQpIDwtIHNhbXBsZWtleV9FJFR5cGUKCmBgYAoKCk1vdmUgYWxsIHBydW5lZCBvdHUgdGFibGVzIGludG8gb25lIHRhYmxlIGJ5IG1hdGNoaW5nIHRoZSBzYW1wbGUgVHlwZS0gd2lsbCB1c2UgdGhpcyBmb3IgU3BhckNDCmBgYHtyfQphbGxkb21haW5zX2RmX294eWNsaW5lIDwtIGJpbmRfcm93cyhkYXRhLmZyYW1lKG90dV90YWJsZShwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKSksIGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkKSksIGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2V1a19veHljbGluZV9wcnVuZWQpKSkKYWxsZG9tYWluc19kZl9veHljbGluZQpgYGAKCkNoYW5nZSByb3cgbmFtZXMgZnJvbSAiZGVub3ZvWFhYIiB0byBtZWFuaW5nZnVsIG5hbWVzCmBgYHtyfQphbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUgPC0gY2JpbmQoSUQgPSByb3duYW1lcyhhbGxkb21haW5zX2RmX294eWNsaW5lKSwgYWxsZG9tYWluc19kZl9veHljbGluZSkKCiMgc3RhcnQgd2l0aCBvbmx5IGZpcnN0IHJvd3MsIHdoaWNoIGFyZSBiYWN0ZXJpYS4gbWFrZSBvbmUgY29sdW1uIG9mIG1lYW5pbmdmdWwgbGFiZWxzCnRlbXAxIDwtIGxlZnRfam9pbihhbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmVbMTpkaW0ob3R1X3RhYmxlKHBzX2JhY19veHljbGluZV9wcnVuZWQpKVsxXSxdLCBiYWNfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNPVFUgSUQiKSkgCnRlbXAxJE5ld19JRCA8LSBwYXN0ZSh0ZW1wMSRJRCwgdGVtcDEkInRheG9ub215LTIiLCB0ZW1wMSQidGF4b25vbXktMyIsIHRlbXAxJCJ0YXhvbm9teS00IikKdGVtcDEgPC0gc2VsZWN0KHRlbXAxLC1jb2xuYW1lcyhiYWNfdGF4b25vbXlbLDI6MTFdKSkKCiMgbmV4dCByb3dzIGFyZSB0aGUgYXJjaGFlYSAKdGVtcDIgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZVtzdW0oZGltKG90dV90YWJsZShwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKSlbMV0sMSk6c3VtKGRpbShvdHVfdGFibGUocHNfYmFjX294eWNsaW5lX3BydW5lZCkpWzFdLGRpbShvdHVfdGFibGUocHNfYXJjaF9veHljbGluZV9wcnVuZWQpKVsxXSksXSwgYXJjaF90YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI09UVSBJRCIpKSAKdGVtcDIkTmV3X0lEIDwtIHBhc3RlKHRlbXAyJElELCB0ZW1wMiQidGF4b25vbXktMiIsIHRlbXAyJCJ0YXhvbm9teS0zIikKdGVtcDIgPC0gc2VsZWN0KHRlbXAyLC1jb2xuYW1lcyhhcmNoX3RheG9ub215WywyOjldKSkKCgojIGxhc3Qgcm93cyBhcmUgZXVrYXJ5YQpldWtfdGF4b25vbXkgPC0gY2JpbmQoIiNBU1YgSUQiID0gcm93bmFtZXModGF4b25vbXkpLCB0YXhvbm9teSkKdGVtcDMgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZVtzdW0oZGltKG90dV90YWJsZShwc19hcmNoX294eWNsaW5lX3BydW5lZCkpWzFdLCBkaW0ob3R1X3RhYmxlKHBzX2JhY19veHljbGluZV9wcnVuZWQpKVsxXSwxKTpzdW0oZGltKG90dV90YWJsZShwc19hcmNoX294eWNsaW5lX3BydW5lZCkpWzFdLCBkaW0ob3R1X3RhYmxlKHBzX2JhY19veHljbGluZV9wcnVuZWQpKVsxXSxkaW0ob3R1X3RhYmxlKHBzX2V1a19veHljbGluZV9wcnVuZWQpKVsxXSksXSwgZXVrX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjQVNWIElEIikpIAp0ZW1wMyROZXdfSUQgPC0gcGFzdGUodGVtcDMkSUQsIHRlbXAzJCJTdXBlcmdyb3VwIiwgdGVtcDMkIkRpdmlzaW9uIiwgdGVtcDMkIkNsYXNzIiwgdGVtcDMkIk9yZGVyIikKdGVtcDMgPC0gc2VsZWN0KHRlbXAzLC1jb2xuYW1lcyhldWtfdGF4b25vbXlbLDI6OV0pKQoKIyBjb21iaW5lIGJhY2sgYWxsIDMgZG9tYWlucywgd2l0aCBuZXcgbmFtZXMgYXMgcm93IG5hbWVzIGluIGEgZGF0YWZyYW1lCmFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZSA8LSByYmluZCh0ZW1wMSwgdGVtcDIsIHRlbXAzKQphbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUgPC0gZGF0YS5mcmFtZShhbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUpCnJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZSkgPC0gYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lJE5ld19JRAphbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUgPC0gc2VsZWN0KGFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZSwgLWMoIklEIiwiTmV3X0lEIikpCmFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZQpgYGAKCgoKUmVtb3ZlIGNvbHVtbnMgd2l0aCBOQXMuIFRoZXNlIGFyZSBzYW1wbGVzIGZvciB3aGljaCB0aGUgbGlicmFyeSBmb3IgYXQgbGVhc3Qgb25lIGRvbWFpbiBkaWRuJ3Qgd29yayAoY2FuJ3QgZG8gY29ycmVsYXRpb25zIHdpdGggbWlzc2luZyB2YWx1ZXMgaW4gY29sdW1ucykKYGBge3J9CmFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZSA8LSBhbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUgJT4lCiAgICBzZWxlY3RfaWYofiAhYW55KGlzLm5hKC4pKSkKYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lCgphbGxkb21haW5zX2RmX294eWNsaW5lIDwtIGFsbGRvbWFpbnNfZGZfb3h5Y2xpbmUgJT4lCiAgICBzZWxlY3RfaWYofiAhYW55KGlzLm5hKC4pKSkKYWxsZG9tYWluc19kZl9veHljbGluZQpgYGAKMjEgc2FtcGxlcyByZW1haW4gZm9yIGNvcnJlbGF0aW9uCgoKCgoKCgoKIyMjIEZpbHRlciBsb3cgYWJ1bmRhbmNlIHNwZWNpZXMtIGFub3hpYyBkZXB0aHMgb25seQoKUHVsbCBvdXQgc2FtcGxlcyBmcm9tIHNoYWxsb3cgYW5veGljIHJlZ2ltZQpgYGB7cn0KIyBQdWxsIG91dCBhbm94aWMgbGF5ZXIgYmFjdGVyaWEgc2FtcGxlIElEcwphbm94aWN0eXBlc19iYWMgPC0gbWV0YWRhdGEgJT4lIAogIGZpbHRlcihgU2FtcGxlIE5hbWVgICVpbiUgc2FtcGxlX25hbWVzKHBzX2JhYykpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIlNoYWxsb3dBbm94aWMiKSAlPiUgCiAgc2VsZWN0KCJTYW1wbGUgTmFtZSIpCmFub3hpY3R5cGVzX2JhYyA8LSB1bmxpc3QoYyh1bmlxdWUoYW5veGljdHlwZXNfYmFjKSksIHVzZS5uYW1lcyA9IEZBTFNFKQoKIyBQdWxsIG91dCBhbGwgYmFjdGVyaWEgZnJvbSBhbm94aWMgbGF5ZXIKcHNfYmFjX2Fub3hpYyA8LSAgcHJ1bmVfc2FtcGxlcyhhbm94aWN0eXBlc19iYWMsIHBzX2JhYykKcHNfYmFjX3JhX2Fub3hpYyA8LSAgcHJ1bmVfc2FtcGxlcyhhbm94aWN0eXBlc19iYWMsIHBzX2JhY19yYSkKCgojIFB1bGwgb3V0IGFub3hpYyBsYXllciBhcmNoYWVhIHNhbXBsZSBJRHMKYW5veGljdHlwZXNfYXJjaCA8LSBtZXRhZGF0YSAlPiUgCiAgZmlsdGVyKGBTYW1wbGUgTmFtZWAgJWluJSBzYW1wbGVfbmFtZXMocHNfYXJjaCkpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIlNoYWxsb3dBbm94aWMiKSAlPiUgCiAgc2VsZWN0KCJTYW1wbGUgTmFtZSIpCmFub3hpY3R5cGVzX2FyY2ggPC0gdW5saXN0KGModW5pcXVlKGFub3hpY3R5cGVzX2FyY2gpKSwgdXNlLm5hbWVzID0gRkFMU0UpCgojIFB1bGwgb3V0IGFsbCBhcmNoYWVhIGZyb20gYW5veGljIGxheWVyCnBzX2FyY2hfYW5veGljPC0gIHBydW5lX3NhbXBsZXMoYW5veGljdHlwZXNfYXJjaCwgcHNfYXJjaCkKcHNfYXJjaF9yYV9hbm94aWMgPC0gIHBydW5lX3NhbXBsZXMoYW5veGljdHlwZXNfYXJjaCwgcHNfYXJjaF9yYSkKCgojIFB1bGwgb3V0IGFub3hpYyBsYXllciBldWthcnlvdGljIHNhbXBsZSBJRHMKYW5veGljdHlwZXNfZXVrIDwtIG1ldGFkYXRhICU+JSAKICBmaWx0ZXIoYFNhbXBsZSBOYW1lYCAlaW4lIHNhbXBsZV9uYW1lcyhwcykpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIlNoYWxsb3dBbm94aWMiKSAlPiUgCiAgc2VsZWN0KCJTYW1wbGUgTmFtZSIpCmFub3hpY3R5cGVzX2V1ayA8LSB1bmxpc3QoYyh1bmlxdWUoYW5veGljdHlwZXNfZXVrKSksIHVzZS5uYW1lcyA9IEZBTFNFKQoKIyBQdWxsIG91dCBhbGwgZXVrYXJ5b3RlcyBmcm9tIGFub3hpYyBsYXllcgpwc19ldWtfYW5veGljIDwtICBwcnVuZV9zYW1wbGVzKGFub3hpY3R5cGVzX2V1aywgcHMpCnBzX2V1a19yYV9hbm94aWMgPC0gIHBydW5lX3NhbXBsZXMoYW5veGljdHlwZXNfZXVrLCBwc19yYSkKCmBgYAoKRmlsdGVyIG91dCBsb3cgYWJ1bmRhbmNlIHRheGEgZnJvbSB0aGUgb3h5Y2xpbmUgc2FtcGxlcy4gVXNlIDUlIGFzIGN1dG9mZiAKYGBge3J9CiMgQmFjdGVyaWEKeCA8LSB0YXhhX3N1bXMocHNfYmFjX3JhX2Fub3hpYykKa2VlcFRheGEgPC0gIHg+LjA1ICMgcHJ1bmVfdGF4YSByZXF1aXJlIGEgbG9naWNhbCBub3QgYSBsaXN0IG9mIElEcy4gY29tcGFyZSB0byBrZWVwVGF4YSBhYm92ZSB0byBjaGVjawpwc19iYWNfcmFfYW5veGljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYmFjX3JhX2Fub3hpYykKcHNfYmFjX2Fub3hpY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2JhY19hbm94aWMpCnBzX2JhY19yYV9hbm94aWNfcHJ1bmVkCnBzX2JhY19hbm94aWNfcHJ1bmVkCgojIEFyY2hhZWEKeCA8LSB0YXhhX3N1bXMocHNfYXJjaF9yYV9hbm94aWMpCmtlZXBUYXhhIDwtICB4Pi4wNSAjIHBydW5lX3RheGEgcmVxdWlyZSBhIGxvZ2ljYWwgbm90IGEgbGlzdCBvZiBJRHMuIGNvbXBhcmUgdG8ga2VlcFRheGEgYWJvdmUgdG8gY2hlY2sKcHNfYXJjaF9yYV9hbm94aWNfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19hcmNoX3JhX2Fub3hpYykKcHNfYXJjaF9hbm94aWNfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19hcmNoX2Fub3hpYykKcHNfYXJjaF9yYV9hbm94aWNfcHJ1bmVkCnBzX2FyY2hfYW5veGljX3BydW5lZAoKIyBFdWthcnlvdGVzCnggPC0gdGF4YV9zdW1zKHBzX2V1a19yYV9hbm94aWMpCmtlZXBUYXhhIDwtICB4Pi4wNSAjIHBydW5lX3RheGEgcmVxdWlyZSBhIGxvZ2ljYWwgbm90IGEgbGlzdCBvZiBJRHMuIGNvbXBhcmUgdG8ga2VlcFRheGEgYWJvdmUgdG8gY2hlY2sKcHNfZXVrX3JhX2Fub3hpY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2V1a19yYV9hbm94aWMpCnBzX2V1a19hbm94aWNfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19ldWtfYW5veGljKQpwc19ldWtfcmFfYW5veGljX3BydW5lZApwc19ldWtfYW5veGljX3BydW5lZAoKYGBgCjMyIGJhY3RlcmlhLCAxOSBhcmNoYWVhLCAzNyBldWthcnlvdGEgcmVtYWluCgoKQ2hhbmdlIHRoZSBzYW1wbGUgbmFtZXMgaW4gdGhlIG90dSB0YWJsZXMgdG8gIlR5cGUiCmBgYHtyfQojIEFyY2hhZWEKIyByZW1vdmUgbWlzc2luZyBhcmNoYWVhIHNhbXBsZXMgZnJvbSBzYW1wbGVrZXlfQQpzYW1wbGVrZXlfQSA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9hcmNoICAlaW4lIGNvbG5hbWVzKG90dV90YWJsZShwc19hcmNoX3JhX2Fub3hpY19wcnVuZWQpKSkKIyBzb3J0IFNhbXBsZUtleSBieSBvcmRlciBvZiBjb2x1bW4gbmFtZXMgZnJvbSBwc19hcmNoX3JhX2Fub3hpY19wcnVuZWQKc2FtcGxla2V5X0EgPC0gc2FtcGxla2V5X0EgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2FyY2gsIGxldmVscyA9IGNvbG5hbWVzKG90dV90YWJsZShwc19hcmNoX3JhX2Fub3hpY19wcnVuZWQpKSkpCiMgcmVwbGFjZSBjb2wgbmFtZXMgb2Ygb3R1IHRhYmxlIGZyb20gcHNfYXJjaF9yYV9hbm94aWNfcHJ1bmVkCnNhbXBsZV9uYW1lcyhwc19hcmNoX3JhX2Fub3hpY19wcnVuZWQpIDwtIHNhbXBsZWtleV9BJFR5cGUKIyBhbmQgcHNfYXJjaF9wcnVuZWQKc2FtcGxlX25hbWVzKHBzX2FyY2hfYW5veGljX3BydW5lZCkgPC0gc2FtcGxla2V5X0EkVHlwZQoKCiMgQmFjdGVyaWEKc2FtcGxla2V5X0IgPC0gZmlsdGVyKHNhbXBsZWtleSwgU2FtcGxlSURfYmFjICAlaW4lIGNvbG5hbWVzKG90dV90YWJsZShwc19iYWNfcmFfYW5veGljX3BydW5lZCkpKQpzYW1wbGVrZXlfQiA8LSBzYW1wbGVrZXlfQiAlPiUgYXJyYW5nZShmYWN0b3IoU2FtcGxlSURfYmFjLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYmFjX3JhX2Fub3hpY19wcnVuZWQpKSkpCnNhbXBsZV9uYW1lcyhwc19iYWNfcmFfYW5veGljX3BydW5lZCkgPC0gc2FtcGxla2V5X0IkVHlwZQpzYW1wbGVfbmFtZXMocHNfYmFjX2Fub3hpY19wcnVuZWQpIDwtIHNhbXBsZWtleV9CJFR5cGUKCgojIEV1a2FyeW90ZXMKc2FtcGxla2V5X0UgPC0gZmlsdGVyKHNhbXBsZWtleSwgU2FtcGxlSURfZXVrICAlaW4lIGNvbG5hbWVzKG90dV90YWJsZShwc19ldWtfcmFfYW5veGljX3BydW5lZCkpKQpzYW1wbGVrZXlfRSA8LSBzYW1wbGVrZXlfRSAlPiUgYXJyYW5nZShmYWN0b3IoU2FtcGxlSURfZXVrLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfZXVrX3JhX2Fub3hpY19wcnVuZWQpKSkpCnNhbXBsZV9uYW1lcyhwc19ldWtfcmFfYW5veGljX3BydW5lZCkgPC0gc2FtcGxla2V5X0UkVHlwZQpzYW1wbGVfbmFtZXMocHNfZXVrX2Fub3hpY19wcnVuZWQpIDwtIHNhbXBsZWtleV9FJFR5cGUKCmBgYAoKCk1vdmUgYWxsIHBydW5lZCBvdHUgdGFibGVzIGludG8gb25lIHRhYmxlIGJ5IG1hdGNoaW5nIHRoZSBzYW1wbGUgVHlwZS0gd2lsbCB1c2UgdGhpcyBmb3IgU3BhckNDCmBgYHtyfQphbGxkb21haW5zX2RmX2Fub3hpYyA8LSBiaW5kX3Jvd3MoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfYmFjX2Fub3hpY19wcnVuZWQpKSwgZGF0YS5mcmFtZShvdHVfdGFibGUocHNfYXJjaF9hbm94aWNfcHJ1bmVkKSksIGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2V1a19hbm94aWNfcHJ1bmVkKSkpCmFsbGRvbWFpbnNfZGZfYW5veGljCmBgYAoKQ2hhbmdlIHJvdyBuYW1lcyBmcm9tICJkZW5vdm9YWFgiIHRvIG1lYW5pbmdmdWwgbmFtZXMKYGBge3J9CmFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMgPC0gY2JpbmQoSUQgPSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Fub3hpYyksIGFsbGRvbWFpbnNfZGZfYW5veGljKQoKIyBzdGFydCB3aXRoIG9ubHkgZmlyc3Qgcm93cywgd2hpY2ggYXJlIGJhY3RlcmlhLiBtYWtlIG9uZSBjb2x1bW4gb2YgbWVhbmluZ2Z1bCBsYWJlbHMKdGVtcDEgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWNbMTpkaW0ob3R1X3RhYmxlKHBzX2JhY19hbm94aWNfcHJ1bmVkKSlbMV0sXSwgYmFjX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjT1RVIElEIikpIAp0ZW1wMSROZXdfSUQgPC0gcGFzdGUodGVtcDEkSUQsIHRlbXAxJCJ0YXhvbm9teS0yIiwgdGVtcDEkInRheG9ub215LTMiLCB0ZW1wMSQidGF4b25vbXktNCIpCnRlbXAxIDwtIHNlbGVjdCh0ZW1wMSwtY29sbmFtZXMoYmFjX3RheG9ub215WywyOjExXSkpCgojIG5leHQgcm93cyBhcmUgdGhlIGFyY2hhZWEgCnRlbXAyIDwtIGxlZnRfam9pbihhbGxkb21haW5zX2RmX2Z1bGxfYW5veGljW3N1bShkaW0ob3R1X3RhYmxlKHBzX2JhY19hbm94aWNfcHJ1bmVkKSlbMV0sMSk6c3VtKGRpbShvdHVfdGFibGUocHNfYmFjX2Fub3hpY19wcnVuZWQpKVsxXSxkaW0ob3R1X3RhYmxlKHBzX2FyY2hfYW5veGljX3BydW5lZCkpWzFdKSxdLCBhcmNoX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjT1RVIElEIikpIAp0ZW1wMiROZXdfSUQgPC0gcGFzdGUodGVtcDIkSUQsIHRlbXAyJCJ0YXhvbm9teS0yIiwgdGVtcDIkInRheG9ub215LTMiKQp0ZW1wMiA8LSBzZWxlY3QodGVtcDIsLWNvbG5hbWVzKGFyY2hfdGF4b25vbXlbLDI6OV0pKQoKCiMgbGFzdCByb3dzIGFyZSBldWthcnlhCmV1a190YXhvbm9teSA8LSBjYmluZCgiI0FTViBJRCIgPSByb3duYW1lcyh0YXhvbm9teSksIHRheG9ub215KQp0ZW1wMyA8LSBsZWZ0X2pvaW4oYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpY1tzdW0oZGltKG90dV90YWJsZShwc19hcmNoX2Fub3hpY19wcnVuZWQpKVsxXSwgZGltKG90dV90YWJsZShwc19iYWNfYW5veGljX3BydW5lZCkpWzFdLDEpOnN1bShkaW0ob3R1X3RhYmxlKHBzX2FyY2hfYW5veGljX3BydW5lZCkpWzFdLCBkaW0ob3R1X3RhYmxlKHBzX2JhY19hbm94aWNfcHJ1bmVkKSlbMV0sZGltKG90dV90YWJsZShwc19ldWtfYW5veGljX3BydW5lZCkpWzFdKSxdLCBldWtfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNBU1YgSUQiKSkgCnRlbXAzJE5ld19JRCA8LSBwYXN0ZSh0ZW1wMyRJRCwgdGVtcDMkIlN1cGVyZ3JvdXAiLCB0ZW1wMyQiRGl2aXNpb24iLCB0ZW1wMyQiQ2xhc3MiLCB0ZW1wMyQiT3JkZXIiKQp0ZW1wMyA8LSBzZWxlY3QodGVtcDMsLWNvbG5hbWVzKGV1a190YXhvbm9teVssMjo5XSkpCgojIGNvbWJpbmUgYmFjayBhbGwgMyBkb21haW5zLCB3aXRoIG5ldyBuYW1lcyBhcyByb3cgbmFtZXMgaW4gYSBkYXRhZnJhbWUKYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYyA8LSByYmluZCh0ZW1wMSwgdGVtcDIsIHRlbXAzKQphbGxkb21haW5zX2RmX2Z1bGxfYW5veGljIDwtIGRhdGEuZnJhbWUoYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYykKcm93bmFtZXMoYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYykgPC0gYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYyROZXdfSUQKYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYyA8LSBzZWxlY3QoYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYywgLWMoIklEIiwiTmV3X0lEIikpCmFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMKYGBgCgoKClJlbW92ZSBjb2x1bW5zIHdpdGggTkFzLiBUaGVzZSBhcmUgc2FtcGxlcyBmb3Igd2hpY2ggdGhlIGxpYnJhcnkgZm9yIGF0IGxlYXN0IG9uZSBkb21haW4gZGlkbid0IHdvcmsgKGNhbid0IGRvIGNvcnJlbGF0aW9ucyB3aXRoIG1pc3NpbmcgdmFsdWVzIGluIGNvbHVtbnMpCmBgYHtyfQphbGxkb21haW5zX2RmX2Z1bGxfYW5veGljIDwtIGFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMgJT4lCiAgICBzZWxlY3RfaWYofiAhYW55KGlzLm5hKC4pKSkKYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYwoKYWxsZG9tYWluc19kZl9hbm94aWMgPC0gYWxsZG9tYWluc19kZl9hbm94aWMgJT4lCiAgICBzZWxlY3RfaWYofiAhYW55KGlzLm5hKC4pKSkKYWxsZG9tYWluc19kZl9hbm94aWMKYGBgCjExIHNhbXBsZXMgcmVtYWluIGZvciBjb3JyZWxhdGlvbgoKCgoKCiMjIyBGaWx0ZXIgbG93IGFidW5kYW5jZSBzcGVjaWVzLSBldXhpbmljIGRlcHRocyBvbmx5CgpQdWxsIG91dCBzYW1wbGVzIGZyb20gc2hhbGxvdyBhbm94aWMgcmVnaW1lCmBgYHtyfQojIFB1bGwgb3V0IGFub3hpYyBsYXllciBiYWN0ZXJpYSBzYW1wbGUgSURzCmV1eGluaWN0eXBlc19iYWMgPC0gbWV0YWRhdGEgJT4lIAogIGZpbHRlcihgU2FtcGxlIE5hbWVgICVpbiUgc2FtcGxlX25hbWVzKHBzX2JhYykpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIkV1eGluaWMiKSAlPiUgCiAgc2VsZWN0KCJTYW1wbGUgTmFtZSIpCmV1eGluaWN0eXBlc19iYWMgPC0gdW5saXN0KGModW5pcXVlKGV1eGluaWN0eXBlc19iYWMpKSwgdXNlLm5hbWVzID0gRkFMU0UpCgojIFB1bGwgb3V0IGFsbCBiYWN0ZXJpYSBmcm9tIGV1eGluaWMgbGF5ZXIKcHNfYmFjX2V1eGluaWMgPC0gIHBydW5lX3NhbXBsZXMoZXV4aW5pY3R5cGVzX2JhYywgcHNfYmFjKQpwc19iYWNfcmFfZXV4aW5pYyA8LSAgcHJ1bmVfc2FtcGxlcyhldXhpbmljdHlwZXNfYmFjLCBwc19iYWNfcmEpCgoKIyBQdWxsIG91dCBldXhpbmljIGxheWVyIGFyY2hhZWEgc2FtcGxlIElEcwpldXhpbmljdHlwZXNfYXJjaCA8LSBtZXRhZGF0YSAlPiUgCiAgZmlsdGVyKGBTYW1wbGUgTmFtZWAgJWluJSBzYW1wbGVfbmFtZXMocHNfYXJjaCkpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIkV1eGluaWMiKSAlPiUgCiAgc2VsZWN0KCJTYW1wbGUgTmFtZSIpCmV1eGluaWN0eXBlc19hcmNoIDwtIHVubGlzdChjKHVuaXF1ZShldXhpbmljdHlwZXNfYXJjaCkpLCB1c2UubmFtZXMgPSBGQUxTRSkKCiMgUHVsbCBvdXQgYWxsIGFyY2hhZWEgZnJvbSBldXhpbmljIGxheWVyCnBzX2FyY2hfZXV4aW5pYzwtICBwcnVuZV9zYW1wbGVzKGV1eGluaWN0eXBlc19hcmNoLCBwc19hcmNoKQpwc19hcmNoX3JhX2V1eGluaWMgPC0gIHBydW5lX3NhbXBsZXMoZXV4aW5pY3R5cGVzX2FyY2gsIHBzX2FyY2hfcmEpCgoKIyBQdWxsIG91dCBldXhpbmljIGxheWVyIGV1a2FyeW90aWMgc2FtcGxlIElEcwpldXhpbmljdHlwZXNfZXVrIDwtIG1ldGFkYXRhICU+JSAKICBmaWx0ZXIoYFNhbXBsZSBOYW1lYCAlaW4lIHNhbXBsZV9uYW1lcyhwcykpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIkV1eGluaWMiKSAlPiUgCiAgc2VsZWN0KCJTYW1wbGUgTmFtZSIpCmV1eGluaWN0eXBlc19ldWsgPC0gdW5saXN0KGModW5pcXVlKGV1eGluaWN0eXBlc19ldWspKSwgdXNlLm5hbWVzID0gRkFMU0UpCgojIFB1bGwgb3V0IGFsbCBldWthcnlvdGVzIGZyb20gZXV4aW5pYyBsYXllcgpwc19ldWtfZXV4aW5pYyA8LSAgcHJ1bmVfc2FtcGxlcyhldXhpbmljdHlwZXNfZXVrLCBwcykKcHNfZXVrX3JhX2V1eGluaWMgPC0gIHBydW5lX3NhbXBsZXMoZXV4aW5pY3R5cGVzX2V1aywgcHNfcmEpCgpgYGAKCkZpbHRlciBvdXQgbG93IGFidW5kYW5jZSB0YXhhIGZyb20gdGhlIG94eWNsaW5lIHNhbXBsZXMuIFVzZSA1JSBhcyBjdXRvZmYgCmBgYHtyfQojIEJhY3RlcmlhCnggPC0gdGF4YV9zdW1zKHBzX2JhY19yYV9ldXhpbmljKQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2JhY19yYV9ldXhpbmljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYmFjX3JhX2V1eGluaWMpCnBzX2JhY19ldXhpbmljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYmFjX2V1eGluaWMpCnBzX2JhY19yYV9ldXhpbmljX3BydW5lZApwc19iYWNfZXV4aW5pY19wcnVuZWQKCiMgQXJjaGFlYQp4IDwtIHRheGFfc3Vtcyhwc19hcmNoX3JhX2V1eGluaWMpCmtlZXBUYXhhIDwtICB4Pi4wNSAjIHBydW5lX3RheGEgcmVxdWlyZSBhIGxvZ2ljYWwgbm90IGEgbGlzdCBvZiBJRHMuIGNvbXBhcmUgdG8ga2VlcFRheGEgYWJvdmUgdG8gY2hlY2sKcHNfYXJjaF9yYV9ldXhpbmljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYXJjaF9yYV9ldXhpbmljKQpwc19hcmNoX2V1eGluaWNfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19hcmNoX2V1eGluaWMpCnBzX2FyY2hfcmFfZXV4aW5pY19wcnVuZWQKcHNfYXJjaF9ldXhpbmljX3BydW5lZAoKIyBFdWthcnlvdGVzCnggPC0gdGF4YV9zdW1zKHBzX2V1a19yYV9ldXhpbmljKQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2V1a19yYV9ldXhpbmljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfZXVrX3JhX2V1eGluaWMpCnBzX2V1a19ldXhpbmljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfZXVrX2V1eGluaWMpCnBzX2V1a19yYV9ldXhpbmljX3BydW5lZApwc19ldWtfZXV4aW5pY19wcnVuZWQKCmBgYAoxNiBiYWN0ZXJpYSwgMTYgYXJjaGFlYSwgMjAgZXVrYXJ5b3RhIHJlbWFpbgoKCkNoYW5nZSB0aGUgc2FtcGxlIG5hbWVzIGluIHRoZSBvdHUgdGFibGVzIHRvICJUeXBlIgpgYGB7cn0KIyBBcmNoYWVhCiMgcmVtb3ZlIG1pc3NpbmcgYXJjaGFlYSBzYW1wbGVzIGZyb20gc2FtcGxla2V5X0EKc2FtcGxla2V5X0EgPC0gZmlsdGVyKHNhbXBsZWtleSwgU2FtcGxlSURfYXJjaCAgJWluJSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYXJjaF9yYV9ldXhpbmljX3BydW5lZCkpKQojIHNvcnQgU2FtcGxlS2V5IGJ5IG9yZGVyIG9mIGNvbHVtbiBuYW1lcyBmcm9tIHBzX2FyY2hfcmFfZXV4aW5pY19wcnVuZWQKc2FtcGxla2V5X0EgPC0gc2FtcGxla2V5X0EgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2FyY2gsIGxldmVscyA9IGNvbG5hbWVzKG90dV90YWJsZShwc19hcmNoX3JhX2V1eGluaWNfcHJ1bmVkKSkpKQojIHJlcGxhY2UgY29sIG5hbWVzIG9mIG90dSB0YWJsZSBmcm9tIHBzX2FyY2hfcmFfZXV4aW5pY19wcnVuZWQKc2FtcGxlX25hbWVzKHBzX2FyY2hfcmFfZXV4aW5pY19wcnVuZWQpIDwtIHNhbXBsZWtleV9BJFR5cGUKIyBhbmQgcHNfYXJjaF9wcnVuZWQKc2FtcGxlX25hbWVzKHBzX2FyY2hfZXV4aW5pY19wcnVuZWQpIDwtIHNhbXBsZWtleV9BJFR5cGUKCgojIEJhY3RlcmlhCnNhbXBsZWtleV9CIDwtIGZpbHRlcihzYW1wbGVrZXksIFNhbXBsZUlEX2JhYyAgJWluJSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYmFjX3JhX2V1eGluaWNfcHJ1bmVkKSkpCnNhbXBsZWtleV9CIDwtIHNhbXBsZWtleV9CICU+JSBhcnJhbmdlKGZhY3RvcihTYW1wbGVJRF9iYWMsIGxldmVscyA9IGNvbG5hbWVzKG90dV90YWJsZShwc19iYWNfcmFfZXV4aW5pY19wcnVuZWQpKSkpCnNhbXBsZV9uYW1lcyhwc19iYWNfcmFfZXV4aW5pY19wcnVuZWQpIDwtIHNhbXBsZWtleV9CJFR5cGUKc2FtcGxlX25hbWVzKHBzX2JhY19ldXhpbmljX3BydW5lZCkgPC0gc2FtcGxla2V5X0IkVHlwZQoKCiMgRXVrYXJ5b3RlcwpzYW1wbGVrZXlfRSA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9ldWsgICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2V1a19yYV9ldXhpbmljX3BydW5lZCkpKQpzYW1wbGVrZXlfRSA8LSBzYW1wbGVrZXlfRSAlPiUgYXJyYW5nZShmYWN0b3IoU2FtcGxlSURfZXVrLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfZXVrX3JhX2V1eGluaWNfcHJ1bmVkKSkpKQpzYW1wbGVfbmFtZXMocHNfZXVrX3JhX2V1eGluaWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfRSRUeXBlCnNhbXBsZV9uYW1lcyhwc19ldWtfZXV4aW5pY19wcnVuZWQpIDwtIHNhbXBsZWtleV9FJFR5cGUKCmBgYAoKCk1vdmUgYWxsIHBydW5lZCBvdHUgdGFibGVzIGludG8gb25lIHRhYmxlIGJ5IG1hdGNoaW5nIHRoZSBzYW1wbGUgVHlwZS0gd2lsbCB1c2UgdGhpcyBmb3IgU3BhckNDCmBgYHtyfQphbGxkb21haW5zX2RmX2V1eGluaWMgPC0gYmluZF9yb3dzKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2JhY19ldXhpbmljX3BydW5lZCkpLCBkYXRhLmZyYW1lKG90dV90YWJsZShwc19hcmNoX2V1eGluaWNfcHJ1bmVkKSksIGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2V1a19ldXhpbmljX3BydW5lZCkpKQphbGxkb21haW5zX2RmX2V1eGluaWMKYGBgCgpDaGFuZ2Ugcm93IG5hbWVzIGZyb20gImRlbm92b1hYWCIgdG8gbWVhbmluZ2Z1bCBuYW1lcwpgYGB7cn0KYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMgPC0gY2JpbmQoSUQgPSByb3duYW1lcyhhbGxkb21haW5zX2RmX2V1eGluaWMpLCBhbGxkb21haW5zX2RmX2V1eGluaWMpCgojIHN0YXJ0IHdpdGggb25seSBmaXJzdCByb3dzLCB3aGljaCBhcmUgYmFjdGVyaWEuIG1ha2Ugb25lIGNvbHVtbiBvZiBtZWFuaW5nZnVsIGxhYmVscwp0ZW1wMSA8LSBsZWZ0X2pvaW4oYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWNbMTpkaW0ob3R1X3RhYmxlKHBzX2JhY19ldXhpbmljX3BydW5lZCkpWzFdLF0sIGJhY190YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI09UVSBJRCIpKSAKdGVtcDEkTmV3X0lEIDwtIHBhc3RlKHRlbXAxJElELCB0ZW1wMSQidGF4b25vbXktMiIsIHRlbXAxJCJ0YXhvbm9teS0zIiwgdGVtcDEkInRheG9ub215LTQiKQp0ZW1wMSA8LSBzZWxlY3QodGVtcDEsLWNvbG5hbWVzKGJhY190YXhvbm9teVssMjoxMV0pKQoKIyBuZXh0IHJvd3MgYXJlIHRoZSBhcmNoYWVhIAp0ZW1wMiA8LSBsZWZ0X2pvaW4oYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWNbc3VtKGRpbShvdHVfdGFibGUocHNfYmFjX2V1eGluaWNfcHJ1bmVkKSlbMV0sMSk6c3VtKGRpbShvdHVfdGFibGUocHNfYmFjX2V1eGluaWNfcHJ1bmVkKSlbMV0sZGltKG90dV90YWJsZShwc19hcmNoX2V1eGluaWNfcHJ1bmVkKSlbMV0pLF0sIGFyY2hfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNPVFUgSUQiKSkgCnRlbXAyJE5ld19JRCA8LSBwYXN0ZSh0ZW1wMiRJRCwgdGVtcDIkInRheG9ub215LTIiLCB0ZW1wMiQidGF4b25vbXktMyIpCnRlbXAyIDwtIHNlbGVjdCh0ZW1wMiwtY29sbmFtZXMoYXJjaF90YXhvbm9teVssMjo5XSkpCgoKIyBsYXN0IHJvd3MgYXJlIGV1a2FyeWEKZXVrX3RheG9ub215IDwtIGNiaW5kKCIjQVNWIElEIiA9IHJvd25hbWVzKHRheG9ub215KSwgdGF4b25vbXkpCnRlbXAzIDwtIGxlZnRfam9pbihhbGxkb21haW5zX2RmX2Z1bGxfZXV4aW5pY1tzdW0oZGltKG90dV90YWJsZShwc19hcmNoX2V1eGluaWNfcHJ1bmVkKSlbMV0sIGRpbShvdHVfdGFibGUocHNfYmFjX2V1eGluaWNfcHJ1bmVkKSlbMV0sMSk6c3VtKGRpbShvdHVfdGFibGUocHNfYXJjaF9ldXhpbmljX3BydW5lZCkpWzFdLCBkaW0ob3R1X3RhYmxlKHBzX2JhY19ldXhpbmljX3BydW5lZCkpWzFdLGRpbShvdHVfdGFibGUocHNfZXVrX2V1eGluaWNfcHJ1bmVkKSlbMV0pLF0sIGV1a190YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI0FTViBJRCIpKSAKdGVtcDMkTmV3X0lEIDwtIHBhc3RlKHRlbXAzJElELCB0ZW1wMyQiU3VwZXJncm91cCIsIHRlbXAzJCJEaXZpc2lvbiIsIHRlbXAzJCJDbGFzcyIsIHRlbXAzJCJPcmRlciIpCnRlbXAzIDwtIHNlbGVjdCh0ZW1wMywtY29sbmFtZXMoZXVrX3RheG9ub215WywyOjldKSkKCiMgY29tYmluZSBiYWNrIGFsbCAzIGRvbWFpbnMsIHdpdGggbmV3IG5hbWVzIGFzIHJvdyBuYW1lcyBpbiBhIGRhdGFmcmFtZQphbGxkb21haW5zX2RmX2Z1bGxfZXV4aW5pYyA8LSByYmluZCh0ZW1wMSwgdGVtcDIsIHRlbXAzKQphbGxkb21haW5zX2RmX2Z1bGxfZXV4aW5pYyA8LSBkYXRhLmZyYW1lKGFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljKQpyb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGxfZXV4aW5pYykgPC0gYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMkTmV3X0lECmFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljIDwtIHNlbGVjdChhbGxkb21haW5zX2RmX2Z1bGxfZXV4aW5pYywgLWMoIklEIiwiTmV3X0lEIikpCmFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljCmBgYAoKCgpSZW1vdmUgY29sdW1ucyB3aXRoIE5Bcy4gVGhlc2UgYXJlIHNhbXBsZXMgZm9yIHdoaWNoIHRoZSBsaWJyYXJ5IGZvciBhdCBsZWFzdCBvbmUgZG9tYWluIGRpZG4ndCB3b3JrIChjYW4ndCBkbyBjb3JyZWxhdGlvbnMgd2l0aCBtaXNzaW5nIHZhbHVlcyBpbiBjb2x1bW5zKQpgYGB7cn0KYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMgPC0gYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMgJT4lCiAgICBzZWxlY3RfaWYofiAhYW55KGlzLm5hKC4pKSkKYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMKCmFsbGRvbWFpbnNfZGZfZXV4aW5pYyA8LSBhbGxkb21haW5zX2RmX2V1eGluaWMgJT4lCiAgICBzZWxlY3RfaWYofiAhYW55KGlzLm5hKC4pKSkKYWxsZG9tYWluc19kZl9ldXhpbmljCmBgYAo0IHNhbXBsZXMgcmVtYWluIGZvciBjb3JyZWxhdGlvbgoKCgoKCgojIyBTcGFyQ0MgCiMjIyBTcGFyQ0Mgb24gZnVsbCBkYXRhc2V0ClRoaXMgaXMgbGFyZ2VseSBiYXNlZCBvbiBbQlZDTiB0dXRvcmlhbHNdKGh0dHBzOi8vZ2l0aHViLmNvbS9iaW92Y25ldC9iaW92Y25ldC5naXRodWIuaW8vd2lraS9UT1BJQyUzQS1OZXR3b3JrcykKTk9URS0gaW5wdXQgZm9yIFNwYXJDQyBzaG91bGQgYmUgcmF3IGNvdW50IGRhdGEgKGFmdGVyIGZpbHRlcmluZyBvdXQgbG93LWFidW5kYW5jZSBBU1ZzKS4gVGhlIGZ1bmN0aW9uIGRvZXMgYSBsb2ctcmF0aW8gdHJhbnNmb3JtYXRpb24gdG8gYWNjb3VudCBmb3IgY29tcG9zaXRpb25hbGl0eQoKYGBge3J9CnNwYXJjY3RhYmxlX2FsbGRvbWFpbnMgPC0gc3BhcmNjKHQoYWxsZG9tYWluc19kZikpCmBgYAoKUHV0IHNhbXBsZSBuYW1lcyBiYWNrIGludG8gcmVzdWx0IHRhYmxlcwpgYGB7cn0Kcm93bmFtZXMoc3BhcmNjdGFibGVfYWxsZG9tYWlucyRDb3IpIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbCkKY29sbmFtZXMoc3BhcmNjdGFibGVfYWxsZG9tYWlucyRDb3IpIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbCkKcm93bmFtZXMoc3BhcmNjdGFibGVfYWxsZG9tYWlucyRDb3YpIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbCkKY29sbmFtZXMoc3BhcmNjdGFibGVfYWxsZG9tYWlucyRDb3YpIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbCkKCnNwYXJjY3RhYmxlX2FsbGRvbWFpbnMkQ29yWzE6MiwxOjJdCgpgYGAKCgoKUGxvdCBjb3JyZWxhdGlvbgpgYGB7cn0KcGxvdGFibGVTcGFyY2MgPC0gc3BhcmNjdGFibGVfYWxsZG9tYWlucyRDb3IgJT4lIHJlb3JkZXJfY29ybWF0ICU+JSBnZXRfdXBwZXJfdHJpKCkgJT4lIHJlc2hhcGUyOjptZWx0KCkgJT4lIG5hLm9taXQoKQoKU3BhcmNjX3Bsb3QgPC0gcGxvdGFibGVTcGFyY2MgJT4lIGdncGxvdChhZXMoeCA9IFZhcjIsIHkgPSBWYXIxLCBmaWxsID0gdmFsdWUpKSArIGdlb21fdGlsZSgpICsgc2NhbGVfZmlsbF9ncmFkaWVudDIoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCgpTcGFyY2NfcGxvdAoKIyBnZ3NhdmUoImZpZ3VyZXMvc3BhcmNjX2NvcnJfYWxsZG9tYWlucy5lcHMiLFNwYXJjY19wbG90LCB3aWR0aCA9IDM1LCBoZWlnaHQgPSAzNSwgdW5pdHMgPSBjKCJpbiIpKQpgYGAKCgoKQ2FsY3VsYXRlIFNwYXJjYyBwLXZhbHVlcyBieSBib290c3RyYXBwaW5nLSBUQUtFUyBBIExPTkcgVElNRQpgYGB7cn0KIyB0cDAgPC0gcHJvYy50aW1lKCkKIyBvdXQyIDwtIHNwYXJjY2Jvb3QodChhbGxkb21haW5zX2RmKSwgUiA9IDEwMDAsIG5jcHVzID0gMikKIyB0cDEgPC0gcHJvYy50aW1lKCkKIyB0cDEgLSB0cDAKYGBgClRoZSBhYm92ZSB0b29rIH4xNCBob3VycyB0byBydW4gMTAwMCBpdGVyYXRpb25zCgoKRXh0cmFjdCBwLXZhbHVlcyAKYGBge3J9Cm91dFAgPC0gcHZhbC5zcGFyY2Nib290KG91dDIpCmRhdGEuZnJhbWUob3V0UCRjb3JzLCBvdXRQJHB2YWxzKSAlPiUgaGVhZApjb3JzIDwtIG91dFAkY29ycwpwdmFscyA8LSBvdXRQJHB2YWxzCnNwYXJDQ3Bjb3JzIDwtIGRpYWcoMC41LCBucm93ID0gZGltKHNwYXJjY3RhYmxlX2FsbGRvbWFpbnMkQ29yKVsxXSwgbmNvbCA9IGRpbShzcGFyY2N0YWJsZV9hbGxkb21haW5zJENvcilbMV0pCnNwYXJDQ3Bjb3JzW3VwcGVyLnRyaShzcGFyQ0NwY29ycywgZGlhZz1GQUxTRSldIDwtIGNvcnMKc3BhckNDcGNvcnMgPC0gc3BhckNDcGNvcnMgKyB0KHNwYXJDQ3Bjb3JzKQoKc3BhckNDcHZhbCA8LSBkaWFnKDAuNSwgbnJvdyA9IGRpbShzcGFyY2N0YWJsZV9hbGxkb21haW5zJENvcilbMV0sIG5jb2wgPSBkaW0oc3BhcmNjdGFibGVfYWxsZG9tYWlucyRDb3IpWzFdKQpzcGFyQ0NwdmFsW3VwcGVyLnRyaShzcGFyQ0NwdmFsLCBkaWFnPUZBTFNFKV0gPC0gcHZhbHMKc3BhckNDcHZhbCA8LSBzcGFyQ0NwdmFsICsgdChzcGFyQ0NwdmFsKQoKcm93bmFtZXMoc3BhckNDcGNvcnMpIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbCkKY29sbmFtZXMoc3BhckNDcGNvcnMpIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbCkKcm93bmFtZXMoc3BhckNDcHZhbCkgPC0gcm93bmFtZXMoYWxsZG9tYWluc19kZl9mdWxsKQpjb2xuYW1lcyhzcGFyQ0NwdmFsKSA8LSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGwpCgpzcGFyQ0NwY29yc1sxOjIsIDE6Ml0Kc3BhckNDcHZhbFsxOjIsIDE6Ml0KYGBgCgpSZW9yZGVyIGZvciBwbG90dGluZwpgYGB7cn0KcmVvcmRlcmVkX2FsbF9zcGFyY2MgPC0gcmVvcmRlcl9jb3JfYW5kX3Aoc3BhckNDcGNvcnMsIHNwYXJDQ3B2YWwpCnJlb3JkZXJlZF9zcGFyY2NDb3IgPC0gcmVvcmRlcmVkX2FsbF9zcGFyY2MkcgpyZW9yZGVyZWRfc3BhcmNjUDwtIHJlb3JkZXJlZF9hbGxfc3BhcmNjJHAKCgpzcGFyY2NDb3JfcHJvY2Vzc2VkIDwtIHJlb3JkZXJlZF9zcGFyY2NDb3IgICU+JSBnZXRfdXBwZXJfdHJpKCkgJT4lIHJlc2hhcGUyOjptZWx0KCkgJT4lIG5hLm9taXQoKSAlPiUgcmVuYW1lKGNvciA9IHZhbHVlKQpzcGFyY2NQX3Byb2Nlc3NlZCA8LSByZW9yZGVyZWRfc3BhcmNjUCAgJT4lIGdldF91cHBlcl90cmkoKSAlPiUgcmVzaGFwZTI6Om1lbHQoKSAlPiUgbmEub21pdCgpICU+JSByZW5hbWUocCA9IHZhbHVlKQoKIyBqb2luIHRoZSB0d28gZGF0YSBmcmFtZXMKClNwYXJjY1AgPC0gbGVmdF9qb2luKHNwYXJjY0Nvcl9wcm9jZXNzZWQsIHNwYXJjY1BfcHJvY2Vzc2VkLCBieSA9IGMoIlZhcjEiLCAiVmFyMiIpKSAlPiUKICAjICMgcmVtb3ZlIHNlbGYgY29ycmVsYXRpb25zCiAgIyBmaWx0ZXIoVmFyMSAhPSBWYXIyKSAlPiUgCiAgIyBjYWxjdWxhdGUgdGhlIGZhbHNlIGRpc2NvdmVyeSByYXRlIHRvIGFkanVzdCBmb3IgbXVsdGlwbGUgcCB2YWx1ZXMKICBtdXRhdGUoZmRyID0gcC5hZGp1c3QocCwgbWV0aG9kID0gIkJIIikpCmBgYAoKCkFuZCBwbG90IGNvcnJlbGF0aW9uIHdpdGggcC12YWx1ZXMuIENpcmNsZXMgbWVhbiB0aGF0IHRoZSByZWxhdGlvbnNoaXAgaXMgc2lnLiBhdCBwID0gMC4wNSBsZXZlbCwgYmFzZWQgb24gYm9vdHN0cmFwcGluZwpgYGB7cn0KZmRyVGhyZXNoIDwtIDAuMDEgIyBmZHIgdGhyZXNob2xkCnNwYXJjY09rUCA8LSBTcGFyY2NQJT4lIGZpbHRlcihmZHIgPCBmZHJUaHJlc2gpIAoKU3BhcmNjUF9wbG90IDwtIFNwYXJjY1AgJT4lIGdncGxvdChhZXMoeCA9IFZhcjIsIHkgPSBWYXIxLCBmaWxsID0gY29yKSkgKyBnZW9tX3RpbGUoKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArIGdlb21fcG9pbnQoZGF0YSA9IHNwYXJjY09rUCwgc2hhcGUgPSAxKQoKU3BhcmNjUF9wbG90CgpnZ3NhdmUoImZpZ3VyZXMvc3BhcmNjX2NvcnJfYWxsZG9tYWluc193X3B2YWxzLmVwcyIsU3BhcmNjUF9wbG90LCB3aWR0aCA9IDM1LCBoZWlnaHQgPSAzNSwgdW5pdHMgPSBjKCJpbiIpKQoKYGBgCgoKClNhdmUgZW52aXJvbm1lbnQgYWdhaW4KYGBge3J9CiMgc2F2ZS5pbWFnZSgiRW52aXJvbm1lbnRCYWNrdXBzL0NhcmlhY29FdWtzX3Bvc3RhbmFseXNpc192YXJzX3VwdG9fc3BhcmNjX2Jvb3RzdHJhcC5SRGF0YSIpCmBgYAoKT3IgbG9hZCBpZiBjb21pbmcgYmFjawpgYGB7cn0KbG9hZCgiRW52aXJvbm1lbnRCYWNrdXBzL0NhcmlhY29FdWtzX3Bvc3RhbmFseXNpc192YXJzX3VwdG9fc3BhcmNjX2Jvb3RzdHJhcC5SRGF0YSIpCmBgYAoKCgoKCiMjIFNwaWVjRWFzaQoKVHJ5IHRoZSBTcGllY0Vhc2kgbWV0aG9kLCB3aGljaCBhY2NvdW50cyBmb3Igc3BhcnNlIGRhdGEsIGFzIGRlc2NyaWJlZCBpbiB0aGUgW1NwaWVjRWFzaSBwdWJsaWNhdGlvbl0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zY29tcGJpb2wvYXJ0aWNsZT9pZD0xMC4xMzcxL2pvdXJuYWwucGNiaS4xMDA0MjI2KSwgW3NwaWVjZWFzaSBnaXRodWJdKGh0dHBzOi8vZ2l0aHViLmNvbS96ZGsxMjMvU3BpZWNFYXNpKSwgYW5kIFtCVkNOIGxlc3NvbnMgMS4yXShodHRwczovL2Jpb3ZjbmV0LmdpdGh1Yi5pby9fcGFnZXMvTmV0d29ya1NjaWVuY2VfZ2xhc3NvKS4gVGhpcyByZWR1Y2VzIHRoZSBjbHVtcHMgKGVnLiBzcGFyc2UgcmVsYXRpb25zaGlwcyB0aGF0IGFyZSBzZWNvbmRhcnkgb3IgdGVyaWFyeSwgbm90IGRpcmVjdCByZWxhdGlvbnNoaXBzKS4KCk1ha2UgZnVuY3Rpb25zIGZyb20gdHV0b3JpYWwKYGBge3J9CmNvbnZlcnRTRVRvVGFibGUgPC0gZnVuY3Rpb24oc2Vfb3V0LHNwLm5hbWVzKXsKICAjVGhpcyBpcyBqdXN0IGEgZmFuY3kgaGVscGVyIGZ1bmN0aW9uIHRvIGdldCB0aGUgZGF0YSBpbiBhIGNvbXBhcmFibGUgZm9ybWF0IHRvIHRoZSBvdXRwdXQgb2YgbGVzc29uIDEgc28gd2UgY2FuIG1ha2UgYSBzaW1pbGFyIHBsb3QuIFdlIHdpbGwgY292ZXIgb3RoZXIgbWV0aG9kcyBmb3IgdmlzdWFsaXppbmcgdGhpcyB0eXBlIG9mIG91dHB1dCBpbiBmdXR1cmUgbGVzc29ucy4KICBzZWNvciA8LSBjb3YyY29yKGFzLm1hdHJpeChnZXRPcHRDb3Yoc2Vfb3V0KSkpICMgU2VlIHNwaWVjZWFzaSBkb2N1bWVudGF0aW9uIGZvciBob3cgdG8gcHVsbCBvdXQgd2VpZ2h0cyBmb3IgY29tcGFyaXNvbgogIGVsaXN0ICAgICA8LSBzdW1tYXJ5KHRyaXUoc2Vjb3IqZ2V0UmVmaXQoc2Vfb3V0KSwgaz0xKSkKICBlbGlzdFssMV0gPC0gc3AubmFtZXNbZWxpc3RbLDFdXQogIGVsaXN0WywyXSA8LSBzcC5uYW1lc1tlbGlzdFssMl1dCiAgZWxpc3RbLDRdIDwtIHBhc3RlKGVsaXN0WywxXSxlbGlzdFssMl0pCiAgZnVsbF9lIDwtIGV4cGFuZC5ncmlkKHNwLm5hbWVzLHNwLm5hbWVzKQogIHJvd25hbWVzKGZ1bGxfZSkgPC0gcGFzdGUoZnVsbF9lWywxXSxmdWxsX2VbLDJdKQogIGZ1bGxfZVssIldlaWdodCJdIDwtIDAKICBmdWxsX2VbZWxpc3RbLDRdLCJXZWlnaHQiXSA8LSBlbGlzdFssM10KICB4IDwtIGV4cGFuZC5ncmlkKDE6bGVuZ3RoKHNwLm5hbWVzKSwxOmxlbmd0aChzcC5uYW1lcykpCiAgZnVsbF9lW3hbLCJWYXIxIl0+eFssIlZhcjIiXSwiV2VpZ2h0Il0gPC0gTkEKICByZXR1cm4oYXMuZGF0YS5mcmFtZShmdWxsX2Usc3RyaW5nc0FzRmFjdG9ycz1GKSkKfQpgYGAKCiMjIyBTcGllY0Vhc2kgb24gZnVsbCBkYXRhc2V0CkZvbGxvdyB0aGUgc3BpZWNlYXNpIFtkb2N1bWVudGF0aW9uXShodHRwczovL2dpdGh1Yi5jb20vemRrMTIzL1NwaWVjRWFzaSkgdG8gZmluZCBvcHRpbWFsIHBhcmFtZXRlcnMuIEFsc28sIGJlY2F1c2UgSSB3YW50IHRvIGNvbXBhcmUgbmV0d29ya3MsIHRoaXMgW2NvbnZvXShodHRwczovL2dpdGh1Yi5jb20vemRrMTIzL1NwaWVjRWFzaS9pc3N1ZXMvNzcpIG9uIHVzaW5nIG9wdGltYWwgcGFyYW1ldGVycyBmb3IgZGlmZmVyZW50IG5ldHdvcmsgY29tcGFyaXNvbnMgaXMgaGVscGZ1bC4KClJlbW92ZSBzYW1wbGVzIGZyb20gdGhlIHBoeWxvc2VxIG9iamVjdHMgdGhhdCBhcmUgbm90IGluIGFsbCAzIGRvbWFpbnMgYW5kIHJlb3JkZXIgc2FtcGxlcyBzbyB0aGV5IGFyZSBpbiBzYW1lIG9yZGVyIGluIGFsbCAzIG9iamVjdHMKYGBge3J9CmJhY19hcmNoX2NvbW1vbiA8LSBpbnRlcnNlY3Qoc2FtcGxlX25hbWVzKHBzX2JhY19yYV9wcnVuZWQpLCBzYW1wbGVfbmFtZXMocHNfYXJjaF9yYV9wcnVuZWQpKQphbGxfY29tbW9uIDwtIGludGVyc2VjdChiYWNfYXJjaF9jb21tb24sIHNhbXBsZV9uYW1lcyhwc19ldWtfcmFfcHJ1bmVkKSkKCnBzX2JhY19wcnVuZWRfM2RvbWFpbnMgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19iYWNfcHJ1bmVkKQpwc19hcmNoX3BydW5lZF8zZG9tYWlucyA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2FyY2hfcHJ1bmVkKQpwc19ldWtfcHJ1bmVkXzNkb21haW5zIDwtIHBydW5lX3NhbXBsZXMoYWxsX2NvbW1vbiwgcHNfZXVrX3BydW5lZCkKCnBzX2JhY19yYV9wcnVuZWRfM2RvbWFpbnMgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19iYWNfcmFfcHJ1bmVkKQpwc19hcmNoX3JhX3BydW5lZF8zZG9tYWlucyA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2FyY2hfcmFfcHJ1bmVkKQpwc19ldWtfcmFfcHJ1bmVkXzNkb21haW5zIDwtIHBydW5lX3NhbXBsZXMoYWxsX2NvbW1vbiwgcHNfZXVrX3JhX3BydW5lZCkKCgpvdHVfdGFibGUocHNfYXJjaF9wcnVuZWRfM2RvbWFpbnMpIDwtIG90dV90YWJsZShwc19hcmNoX3BydW5lZF8zZG9tYWlucylbLHNhbXBsZV9uYW1lcyhwc19iYWNfcmFfcHJ1bmVkXzNkb21haW5zKV0Kb3R1X3RhYmxlKHBzX2V1a19wcnVuZWRfM2RvbWFpbnMpIDwtIG90dV90YWJsZShwc19ldWtfcHJ1bmVkXzNkb21haW5zKVssc2FtcGxlX25hbWVzKHBzX2JhY19yYV9wcnVuZWRfM2RvbWFpbnMpXQoKc2FtcGxlX2RhdGEocHNfYmFjX3BydW5lZF8zZG9tYWlucykKc2FtcGxlX2RhdGEocHNfYXJjaF9wcnVuZWRfM2RvbWFpbnMpCnNhbXBsZV9kYXRhKHBzX2V1a19wcnVuZWRfM2RvbWFpbnMpCmBgYAoKCgpgYGB7cn0KI1J1biBTcGllY2Vhc2kKcGFyZ3MgPC0gbGlzdChzZWVkPTEwMDEwKQpzZSA8LSBzcGllYy5lYXNpKGxpc3QocHNfYmFjX3BydW5lZF8zZG9tYWlucywgcHNfYXJjaF9wcnVuZWRfM2RvbWFpbnMsIHBzX2V1a19wcnVuZWRfM2RvbWFpbnMpLCBtZXRob2Q9J2dsYXNzbycsIGxhbWJkYS5taW4ucmF0aW89MWUtMiwgbmxhbWJkYT0xMDAsIHB1bHNhci5wYXJhbXM9cGFyZ3MpCmdldFN0YWJpbGl0eShzZSkKYGBgCgp0aGUgYWJvdmUgdGFrZXMgYSB3aGlsZSB0byBydW4gKDIwLTMwIG1pbnMpLiBVc2luZyBwYXJhbWV0ZXJzIGFib3ZlLCB0aGUgc3RhYmlsaXR5IGFsb25nIHRoZSBsYW1iZGEgcGF0aCBjcm9zc2VzIHRoZSAwLjA1IHRocmVzaG9sZCBhbmQgdGhlIGZpbmFsIHN0YWJpbGl0eSB2YWx1ZSAoMC4wNDQpIGlzIHN1ZmZpY2llbnRseSBjbG9zZSB0byAwLjA1CgoKCmBgYHtyfQojVGhpcyBpcyBqdXN0IGEgZmFuY3kgaGVscGVyIGZ1bmN0aW9uIHRvIGdldCB0aGUgZGF0YSBpbiBhIGNvbXBhcmFibGUgZm9ybWF0IHRvIHRoZSBvdXRwdXQgb2YgYWJvdmUKdGFiLnNlIDwtIGNvbnZlcnRTRVRvVGFibGUoc2Usc3AubmFtZXM9Y29sbmFtZXModChhbGxkb21haW5zX2RmX2Z1bGwpKSkgCgojUGxvdCAKcGxvdC5zZSA8LSBnZ3Bsb3QodGFiLnNlLGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIsIGZpbGwgPSBXZWlnaHQpKSArIGdlb21fdGlsZSgpICsgc2NhbGVfZmlsbF9ncmFkaWVudDIoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCnBsb3QocGxvdC5zZSkKCmdnc2F2ZSgiZmlndXJlcy9zcGllY2Vhc2lfYWxsZG9tYWlucy5lcHMiLHBsb3Quc2UsIHdpZHRoID0gMzUsIGhlaWdodCA9IDM1LCB1bml0cyA9IGMoImluIikpCgpgYGAKTm90ZS0gb25seSB0aGUgc2lnbmlmaWNhbnQgdmFsdWVzIGFib3ZlIHNob3cgdXAgaW4gdGhlIGhlYXRtYXAgYWJvdmUgKGllLiB0aGVyZSBpcyBubyAicC12YWx1ZSIpCgoKCiMjIyBTcGllY0Vhc2kgb24gYmFjdGVyaWEgYW5kIGFyY2hhZWEgb25seQpSZW1vdmUgc2FtcGxlcyBmcm9tIHRoZSBwaHlsb3NlcSBvYmplY3RzIHRoYXQgYXJlIG5vdCBpbiBib3RoIGRvbWFpbnMgYW5kIHJlb3JkZXIgc2FtcGxlcyBzbyB0aGV5IGFyZSBpbiBzYW1lIG9yZGVyIGluIGFsbCAzIG9iamVjdHMKYGBge3J9CmJhY19hcmNoX2NvbW1vbiA8LSBpbnRlcnNlY3Qoc2FtcGxlX25hbWVzKHBzX2JhY19yYV9wcnVuZWQpLCBzYW1wbGVfbmFtZXMocHNfYXJjaF9yYV9wcnVuZWQpKQoKcHNfYmFjX3BydW5lZF8yZG9tYWlucyA8LSBwcnVuZV9zYW1wbGVzKGJhY19hcmNoX2NvbW1vbiwgcHNfYmFjX3BydW5lZCkKcHNfYXJjaF9wcnVuZWRfMmRvbWFpbnMgPC0gcHJ1bmVfc2FtcGxlcyhiYWNfYXJjaF9jb21tb24sIHBzX2FyY2hfcHJ1bmVkKQoKcHNfYmFjX3JhX3BydW5lZF8yZG9tYWlucyA8LSBwcnVuZV9zYW1wbGVzKGJhY19hcmNoX2NvbW1vbiwgcHNfYmFjX3JhX3BydW5lZCkKcHNfYXJjaF9yYV9wcnVuZWRfMmRvbWFpbnMgPC0gcHJ1bmVfc2FtcGxlcyhiYWNfYXJjaF9jb21tb24sIHBzX2FyY2hfcmFfcHJ1bmVkKQoKCm90dV90YWJsZShwc19hcmNoX3BydW5lZF8yZG9tYWlucykgPC0gb3R1X3RhYmxlKHBzX2FyY2hfcHJ1bmVkXzJkb21haW5zKVssc2FtcGxlX25hbWVzKHBzX2JhY19yYV9wcnVuZWRfM2RvbWFpbnMpXQoKc2FtcGxlX2RhdGEocHNfYmFjX3BydW5lZF8yZG9tYWlucykKc2FtcGxlX2RhdGEocHNfYXJjaF9wcnVuZWRfMmRvbWFpbnMpCmBgYAoKCgpgYGB7cn0KI1J1biBTcGllY2Vhc2kKcGFyZ3MgPC0gbGlzdChzZWVkPTEwMDEwKQpzZS4yZG9tYWlucyA8LSBzcGllYy5lYXNpKGxpc3QocHNfYmFjX3BydW5lZF8yZG9tYWlucywgcHNfYXJjaF9wcnVuZWRfMmRvbWFpbnMpLCBtZXRob2Q9J2dsYXNzbycsIGxhbWJkYS5taW4ucmF0aW89MWUtMiwgbmxhbWJkYT0yMDAsIHB1bHNhci5wYXJhbXM9cGFyZ3MpCmdldFN0YWJpbGl0eShzZS4yZG9tYWlucykKYGBgCgp0aGUgYWJvdmUgdGFrZXMgYSB3aGlsZSB0byBydW4gLiBVc2luZyBwYXJhbWV0ZXJzIGFib3ZlLCB0aGUgc3RhYmlsaXR5IGFsb25nIHRoZSBsYW1iZGEgcGF0aCBjcm9zc2VzIHRoZSAwLjA1IHRocmVzaG9sZCBhbmQgdGhlIGZpbmFsIHN0YWJpbGl0eSB2YWx1ZSAoMC4wNDYpIGlzIGNsb3NlIHRvIDAuMDUKCgoKYGBge3J9CiNUaGlzIGlzIGp1c3QgYSBmYW5jeSBoZWxwZXIgZnVuY3Rpb24gdG8gZ2V0IHRoZSBkYXRhIGluIGEgY29tcGFyYWJsZSBmb3JtYXQgdG8gdGhlIG91dHB1dCBvZiBhYm92ZQp0YWIuc2UgPC0gY29udmVydFNFVG9UYWJsZShzZS4yZG9tYWlucyxzcC5uYW1lcz1jb2xuYW1lcyh0KHR3b2RvbWFpbnNfZGZfZnVsbCkpKSAKCiNQbG90IApwbG90LnNlIDwtIGdncGxvdCh0YWIuc2UsYWVzKHggPSBWYXIxLCB5ID0gVmFyMiwgZmlsbCA9IFdlaWdodCkpICsgZ2VvbV90aWxlKCkgKyBzY2FsZV9maWxsX2dyYWRpZW50MigpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKcGxvdChwbG90LnNlKQoKZ2dzYXZlKCJmaWd1cmVzL3NwaWVjZWFzaV8yZG9tYWlucy5lcHMiLHBsb3Quc2UsIHdpZHRoID0gMzUsIGhlaWdodCA9IDM1LCB1bml0cyA9IGMoImluIikpCgpgYGAKTm90ZS0gb25seSB0aGUgc2lnbmlmaWNhbnQgdmFsdWVzIGFib3ZlIHNob3cgdXAgaW4gdGhlIGhlYXRtYXAgYWJvdmUgKGllLiB0aGVyZSBpcyBubyAicC12YWx1ZSIpCgoKCgojIyMgU3BpZWNFYXNpIG9uIG94eWNsaW5lICB3aXRoIDMgZG9tYWlucwoKYGBge3J9CmJhY19hcmNoX2NvbW1vbiA8LSBpbnRlcnNlY3Qoc2FtcGxlX25hbWVzKHBzX2JhY19veHljbGluZV9wcnVuZWQpLCBzYW1wbGVfbmFtZXMocHNfYXJjaF9veHljbGluZV9wcnVuZWQpKQphbGxfY29tbW9uIDwtIGludGVyc2VjdChiYWNfYXJjaF9jb21tb24sIHNhbXBsZV9uYW1lcyhwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKSkKCnBzX2JhY19veHljbGluZV9wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKQpwc19hcmNoX294eWNsaW5lX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkKQpwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkIDwtIHBydW5lX3NhbXBsZXMoYWxsX2NvbW1vbiwgcHNfZXVrX294eWNsaW5lX3BydW5lZCkKCgpvdHVfdGFibGUocHNfYXJjaF9veHljbGluZV9wcnVuZWQpIDwtIG90dV90YWJsZShwc19hcmNoX294eWNsaW5lX3BydW5lZClbLHNhbXBsZV9uYW1lcyhwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKV0Kb3R1X3RhYmxlKHBzX2V1a19veHljbGluZV9wcnVuZWQpIDwtIG90dV90YWJsZShwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKVssc2FtcGxlX25hbWVzKHBzX2JhY19veHljbGluZV9wcnVuZWQpXQoKc2FtcGxlX2RhdGEocHNfYmFjX294eWNsaW5lX3BydW5lZCkKc2FtcGxlX2RhdGEocHNfYXJjaF9veHljbGluZV9wcnVuZWQpCnNhbXBsZV9kYXRhKHBzX2V1a19veHljbGluZV9wcnVuZWQpCmBgYAoKCgpgYGB7cn0KI1J1biBTcGllY2Vhc2kKcGFyZ3MgPC0gbGlzdChzZWVkPTEwMDEwKQpzZS5veHljbGluZSA8LSBzcGllYy5lYXNpKGxpc3QocHNfYmFjX294eWNsaW5lX3BydW5lZCwgcHNfYXJjaF9veHljbGluZV9wcnVuZWQsIHBzX2V1a19veHljbGluZV9wcnVuZWQpLCBtZXRob2Q9J2dsYXNzbycsIGxhbWJkYS5taW4ucmF0aW89NWUtMywgbmxhbWJkYT0zMDAsIHB1bHNhci5wYXJhbXM9cGFyZ3MpCmdldFN0YWJpbGl0eShzZS5veHljbGluZSkKYGBgCgp0aGUgYWJvdmUgdGFrZXMgYSBjb3VwbGUgb2YgbWludXRlcyB0byBydW4uIFN0YWJpbGl0eSBhbmQgc3RhYmlsaXR5IGFsb25nIGxhbWJkYSBwYXRoIGFyZSB2ZXJ5IHNpbWlsYXIgdG8gdGhlIGZ1bGwgZGF0YXNldCBzcGllY2Vhc2kgb2JqZWN0IChzZSkgd2l0aCB0aGVzZSBwYXJhbWV0ZXJzIGFib3ZlLiBDb250aW51ZSB3aXRoIHRoZXNlLgoKCmBgYHtyfQojVGhpcyBpcyBqdXN0IGEgZmFuY3kgaGVscGVyIGZ1bmN0aW9uIHRvIGdldCB0aGUgZGF0YSBpbiBhIGNvbXBhcmFibGUgZm9ybWF0IHRvIHRoZSBvdXRwdXQgb2YgYWJvdmUKdGFiLnNlLm94eWNsaW5lIDwtIGNvbnZlcnRTRVRvVGFibGUoc2Uub3h5Y2xpbmUsIHNwLm5hbWVzPWNvbG5hbWVzKHQoYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lKSkpIAoKI1Bsb3QgCnBsb3Quc2Uub3h5Y2xpbmUgPC0gZ2dwbG90KHRhYi5zZS5veHljbGluZSxhZXMoeCA9IFZhcjEsIHkgPSBWYXIyLCBmaWxsID0gV2VpZ2h0KSkgKyBnZW9tX3RpbGUoKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpwbG90KHBsb3Quc2Uub3h5Y2xpbmUpCgpnZ3NhdmUoImZpZ3VyZXMvc3BpZWNlYXNpX2FsbGRvbWFpbnNfb3h5Y2xpbmUuZXBzIixwbG90LnNlLm94eWNsaW5lLCB3aWR0aCA9IDM1LCBoZWlnaHQgPSAzNSwgdW5pdHMgPSBjKCJpbiIpKQoKYGBgCgoKCiMjIyBTcGllY0Vhc2kgb24gYW5veGljIGRlcHRocyB3aXRoIDMgZG9tYWlucwoKYGBge3J9CmJhY19hcmNoX2NvbW1vbiA8LSBpbnRlcnNlY3Qoc2FtcGxlX25hbWVzKHBzX2JhY19hbm94aWNfcHJ1bmVkKSwgc2FtcGxlX25hbWVzKHBzX2FyY2hfYW5veGljX3BydW5lZCkpCmFsbF9jb21tb24gPC0gaW50ZXJzZWN0KGJhY19hcmNoX2NvbW1vbiwgc2FtcGxlX25hbWVzKHBzX2V1a19hbm94aWNfcHJ1bmVkKSkKCnBzX2JhY19hbm94aWNfcHJ1bmVkIDwtIHBydW5lX3NhbXBsZXMoYWxsX2NvbW1vbiwgcHNfYmFjX2Fub3hpY19wcnVuZWQpCnBzX2FyY2hfYW5veGljX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2FyY2hfYW5veGljX3BydW5lZCkKcHNfZXVrX2Fub3hpY19wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19ldWtfYW5veGljX3BydW5lZCkKCgpvdHVfdGFibGUocHNfYXJjaF9hbm94aWNfcHJ1bmVkKSA8LSBvdHVfdGFibGUocHNfYXJjaF9hbm94aWNfcHJ1bmVkKVssc2FtcGxlX25hbWVzKHBzX2JhY19hbm94aWNfcHJ1bmVkKV0Kb3R1X3RhYmxlKHBzX2V1a19hbm94aWNfcHJ1bmVkKSA8LSBvdHVfdGFibGUocHNfZXVrX2Fub3hpY19wcnVuZWQpWyxzYW1wbGVfbmFtZXMocHNfYmFjX2Fub3hpY19wcnVuZWQpXQoKc2FtcGxlX2RhdGEocHNfYmFjX2Fub3hpY19wcnVuZWQpCnNhbXBsZV9kYXRhKHBzX2FyY2hfYW5veGljX3BydW5lZCkKc2FtcGxlX2RhdGEocHNfZXVrX2Fub3hpY19wcnVuZWQpCmBgYAoKCgpgYGB7cn0KI1J1biBTcGllY2Vhc2kKcGFyZ3MgPC0gbGlzdChzZWVkPTEwMDEwKQpzZS5hbm94aWMgPC0gc3BpZWMuZWFzaShsaXN0KHBzX2JhY19hbm94aWNfcHJ1bmVkLCBwc19hcmNoX2Fub3hpY19wcnVuZWQsIHBzX2V1a19hbm94aWNfcHJ1bmVkKSwgbWV0aG9kPSdnbGFzc28nLCBsYW1iZGEubWluLnJhdGlvPTFlLTEsIG5sYW1iZGE9MzAwLCBwdWxzYXIucGFyYW1zPXBhcmdzKQpnZXRTdGFiaWxpdHkoc2UuYW5veGljKQpgYGAKCnRoZSBhYm92ZSB0YWtlcyBhIGNvdXBsZSBvZiBtaW51dGVzIHRvIHJ1bgoKCgoKCmBgYHtyfQojVGhpcyBpcyBqdXN0IGEgZmFuY3kgaGVscGVyIGZ1bmN0aW9uIHRvIGdldCB0aGUgZGF0YSBpbiBhIGNvbXBhcmFibGUgZm9ybWF0IHRvIHRoZSBvdXRwdXQgb2YgYWJvdmUKdGFiLnNlLmFub3hpYyA8LSBjb252ZXJ0U0VUb1RhYmxlKHNlLmFub3hpYywgc3AubmFtZXM9Y29sbmFtZXModChhbGxkb21haW5zX2RmX2Z1bGxfYW5veGljKSkpIAoKI1Bsb3QgCnBsb3Quc2UuYW5veGljIDwtIGdncGxvdCh0YWIuc2UuYW5veGljLGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIsIGZpbGwgPSBXZWlnaHQpKSArIGdlb21fdGlsZSgpICsgc2NhbGVfZmlsbF9ncmFkaWVudDIoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCnBsb3QocGxvdC5zZS5hbm94aWMpCgpnZ3NhdmUoImZpZ3VyZXMvc3BpZWNlYXNpX2FsbGRvbWFpbnNfYW5veGljLmVwcyIscGxvdC5zZS5hbm94aWMsIHdpZHRoID0gMzUsIGhlaWdodCA9IDM1LCB1bml0cyA9IGMoImluIikpCgpgYGAKCgoKIyMjIFNwaWVjRWFzaSBvbiBldXhpbmljIGRlcHRocyB3aXRoIDMgZG9tYWlucwoKYGBge3J9CmJhY19hcmNoX2NvbW1vbiA8LSBpbnRlcnNlY3Qoc2FtcGxlX25hbWVzKHBzX2JhY19ldXhpbmljX3BydW5lZCksIHNhbXBsZV9uYW1lcyhwc19hcmNoX2V1eGluaWNfcHJ1bmVkKSkKYWxsX2NvbW1vbiA8LSBpbnRlcnNlY3QoYmFjX2FyY2hfY29tbW9uLCBzYW1wbGVfbmFtZXMocHNfZXVrX2V1eGluaWNfcHJ1bmVkKSkKCnBzX2JhY19ldXhpbmljX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2JhY19ldXhpbmljX3BydW5lZCkKcHNfYXJjaF9ldXhpbmljX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2FyY2hfZXV4aW5pY19wcnVuZWQpCnBzX2V1a19ldXhpbmljX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2V1a19ldXhpbmljX3BydW5lZCkKCgpvdHVfdGFibGUocHNfYXJjaF9ldXhpbmljX3BydW5lZCkgPC0gb3R1X3RhYmxlKHBzX2FyY2hfZXV4aW5pY19wcnVuZWQpWyxzYW1wbGVfbmFtZXMocHNfYmFjX2V1eGluaWNfcHJ1bmVkKV0Kb3R1X3RhYmxlKHBzX2V1a19ldXhpbmljX3BydW5lZCkgPC0gb3R1X3RhYmxlKHBzX2V1a19ldXhpbmljX3BydW5lZClbLHNhbXBsZV9uYW1lcyhwc19iYWNfZXV4aW5pY19wcnVuZWQpXQoKc2FtcGxlX2RhdGEocHNfYmFjX2V1eGluaWNfcHJ1bmVkKQpzYW1wbGVfZGF0YShwc19hcmNoX2V1eGluaWNfcHJ1bmVkKQpzYW1wbGVfZGF0YShwc19ldWtfZXV4aW5pY19wcnVuZWQpCmBgYAoKCgpgYGB7cn0KI1J1biBTcGllY2Vhc2kKcGFyZ3MgPC0gbGlzdChzZWVkPTEwMDEwKQpzZS5ldXhpbmljIDwtIHNwaWVjLmVhc2kobGlzdChwc19iYWNfZXV4aW5pY19wcnVuZWQsIHBzX2FyY2hfZXV4aW5pY19wcnVuZWQsIHBzX2V1a19ldXhpbmljX3BydW5lZCksIG1ldGhvZD0nZ2xhc3NvJywgbGFtYmRhLm1pbi5yYXRpbz0xZS01LG5sYW1iZGE9MjAsIHB1bHNhci5wYXJhbXM9cGFyZ3MpCmdldFN0YWJpbGl0eShzZS5ldXhpbmljKQpgYGAKCkkgdHJpZWQgbWFueSBwYXJhbWV0ZXJzIG9uIHRoZSBhYm92ZSBidXQgY2Fubm90IGdldCBhIHNhdGlzZmFjdG9yeSBzb2x1dGlvbi4gVGhlcmUgYXJlIGp1c3QgdG9vIGZldyBzYW1wbGVzIGFmdGVyIHF1YWxpdHkgZmlsdGVyaW5nIHRvIGRvIFNwaWVjRWFzaSBvbiB0aGUgZXV4aW5pYyBkZXB0aHMgb25seS4KCgojIyMgU2F2ZSBhbmQgcmUtbG9hZCBlbnZpcm9ubWVudApgYGB7cn0KIyBzYXZlLmltYWdlKCJFbnZpcm9ubWVudEJhY2t1cHMvQ2FyaWFjb0V1a3NfcG9zdGFuYWx5c2lzX3ZhcnNfdXB0b19zcGllY2Vhc2kuUkRhdGEiKQpgYGAKCk9yIGxvYWQgaWYgY29taW5nIGJhY2sKYGBge3J9CmxvYWQoIkVudmlyb25tZW50QmFja3Vwcy9DYXJpYWNvRXVrc19wb3N0YW5hbHlzaXNfdmFyc191cHRvX3NwaWVjZWFzaS5SRGF0YSIpCmBgYAoKCiMjIE5ldHdvcmsgQW5hbHlzaXMKQnVpbGQgbmV0d29ya3MgZnJvbSB0aGUgU3BpZWNFYXNpIGFzc29jaWF0aW9uIG1hdHJpY2VzIHVzaW5nIGlHcmFwaAoKIyMjIDMgRG9tYWluIE5ldHdvcmstIEFsbCBkZXB0aHMKYGBge3J9CiNFeHRyYWN0IGFkamFjZW5jeSBtYXRyaXggZnJvbSBzcGllY0Vhc2kgb3V0cHV0CmFkai5tYXQgPC0gZ2V0UmVmaXQoc2UpCnRhYmxlKGFzLm51bWVyaWMoYWRqLm1hdCkpCgojIEV4dHJhY3Qgd2VpZ2h0ZWQgYWRqYWNlbmN5CnNlLmNvciAgPC0gY292MmNvcihhcy5tYXRyaXgoZ2V0T3B0Q292KHNlKSkpCndlaWdodGVkLmFkai5tYXQgPC0gc2UuY29yKmdldFJlZml0KHNlKQoKI0NvbnZlcnQgdG8gZ3JhcGggb2JqZWN0cwpncnBoLnVud2VpZ2h0ZWQgPC0gYWRqMmlncmFwaChhZGoubWF0KQpncnBoIDwtIGFkajJpZ3JhcGgod2VpZ2h0ZWQuYWRqLm1hdCkKCgojIFB1dCBiYWNrIGluIHNwZWNpZXMgbmFtZXMKVihncnBoKSRuYW1lIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGYpCiMgVihncnBoKQoKIyBNYWtlIHNpemUgb2Ygbm9kZXMgcHJvcG9ydGlvbmFsIHRvIGRlZ3JlZSAobnVtYmVyIG9mIGNvbm5lY3Rpb25zKQpWKGdycGgpJHNpemUgPC0gKGRlZ3JlZShncnBoKSArIDEpICMgdGhlICsxIGF2b2lkcyBzaXplIHplcm8gdmVydGljZXMKCiMgQ2hhbmdlIHdpZHRoIG9mIGVkZ2VzIHRvIGJlIHByb3BvcnRpb25hbCB0byB0aGVpciB3ZWlnaHRzCkUoZ3JwaCkkd2lkdGggPC0gYWJzKEUoZ3JwaCkkd2VpZ2h0KSoxMAoKIyBTY2FsZSBub2RlIHNpemVzIHRvIGJlIHNtYWxsZXIKVihncnBoKSRzaXplIDwtIFYoZ3JwaCkkc2l6ZS8yCgojIFJlbW92ZSBsb3ctd2VpZ2h0IGVkZ2VzICh5b3UgZGVjaWRlIHdoYXQgdGhyZXNob2xkIGlzIHJpZ2h0IGZvciB5b3VyIG5ldHdvcmspOgojIHdlaWdodF90aHJlc2hvbGQgPC0gMC4wNwojIGdycGggPC0gZGVsZXRlLmVkZ2VzKGdycGgsd2hpY2goYWJzKEUoZ3JwaCkkd2VpZ2h0KTx3ZWlnaHRfdGhyZXNob2xkKSkKCiMgSm9pbiB0YXhvbm9teSBkYXRhIG9mIGVhY2ggbm9kZQojIENvbnZlcnQgZ3JhcGggdG8gZGF0YWZyYW0KZ3JwaF9kZiA8LSBpZ3JhcGg6OmFzX2RhdGFfZnJhbWUoZ3JwaCwgJ2JvdGgnKQojIG1ha2UgZm9ybWF0dGVkIHRheG9ub215IHRhYmxlIGZvciBlYWNoIGRvbWFpbgpwc19iYWNfcHJ1bmVkX3RheF90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRheF90YWJsZShwc19iYWNfcHJ1bmVkKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSByb3duYW1lcyh0YXhfdGFibGUocHNfYmFjX3BydW5lZCkpKQpwc19hcmNoX3BydW5lZF90YXhfdGFibGUgPC0gYXMuZGF0YS5mcmFtZSh0YXhfdGFibGUocHNfYXJjaF9wcnVuZWQpKSAlPiUKICBtdXRhdGUobmFtZSA9IHJvd25hbWVzKHRheF90YWJsZShwc19hcmNoX3BydW5lZCkpKQpwc19ldWtfcHJ1bmVkX3RheF90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRheF90YWJsZShwc19ldWtfcHJ1bmVkKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSByb3duYW1lcyh0YXhfdGFibGUocHNfZXVrX3BydW5lZCkpKQojIGxpbmsgZ3JhcGggZGF0YSBmcmFtZSB0byBmb3JtYXR0ZWQgdGF4b25vbXkgdGFibGVzCmJhY190ZW1wIDwtIGxlZnRfam9pbihncnBoX2RmJHZlcnRpY2VzWzE6bnRheGEocHNfYmFjX3BydW5lZCksXSwKcHNfYmFjX3BydW5lZF90YXhfdGFibGUsIGJ5ID0gIm5hbWUiKQogICMgZGVsZXRlIGNvbHVtbnMgdGhhdCBkb24ndCBtYXRjaCBvdGhlciB0YXggdGFibGVzCiAgYmFjX3RlbXAgPC0gc2VsZWN0KGJhY190ZW1wLCAtInRheG9ub215LTkiLCAtIlJlZmluZWQgdGF4b25vbXkiKQphcmNoX3RlbXAgPC0gbGVmdF9qb2luKGdycGhfZGYkdmVydGljZXNbbnRheGEocHNfYmFjX3BydW5lZCkrMTpudGF4YShwc19hcmNoX3BydW5lZCksXSwKcHNfYXJjaF9wcnVuZWRfdGF4X3RhYmxlLCBieSA9ICJuYW1lIikKZXVrX3RlbXAgPC0gbGVmdF9qb2luKGdycGhfZGYkdmVydGljZXNbbnRheGEocHNfYmFjX3BydW5lZCkrbnRheGEocHNfYXJjaF9wcnVuZWQpKzE6bnRheGEocHNfZXVrX3BydW5lZCksXSwKcHNfZXVrX3BydW5lZF90YXhfdGFibGUsIGJ5ID0gIm5hbWUiKQogICMgcmVuYW1lIGNvbHVtbiBuYW1lcyBpbiBldWsgdGFibGUgdG8gbWF0Y2ggb3RoZXJzCiAgZXVrX3RlbXAgPC0gZXVrX3RlbXAgJT4lCiAgICByZW5hbWUoInRheG9ub215LTEiID0gS2luZ2RvbSwgInRheG9ub215LTIiID0gU3VwZXJncm91cCwgInRheG9ub215LTMiID0gRGl2aXNpb24sICJ0YXhvbm9teS00IiA9IENsYXNzLCAidGF4b25vbXktNSIgPSBPcmRlciwgInRheG9ub215LTYiID0gRmFtaWx5LCAidGF4b25vbXktNyIgPSBHZW51cywgInRheG9ub215LTgiID0gU3BlY2llcykKIyBidWlsZCBmdWxsIGRhdGFmcmFtZSB3aXRoIGFsbCAzIGRvbWFpbnMKYWxsX3RlbXAgPC0gcmJpbmQoYmFjX3RlbXAsIGFyY2hfdGVtcCwgZXVrX3RlbXApCiMgcmVtYWtlIGludG8gZ3JhcGgKZ3JwaCA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZ3JwaF9kZiRlZGdlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGljZXMgPSBhbGxfdGVtcCkKCiMgTWFrZSBjb2xvciBwYWxldHRlIGZvciBkb21haW4KZHR5cGUgPSBjKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIsICJ5ZWxsb3ciKQojIE1ha2UgY29sb3IgdmVjdG9yCmRvbWFpbl9jb2xvciA8LSBkdHlwZVthcy5udW1lcmljKGFzLmZhY3RvcihWKGdycGgpJCJ0YXhvbm9teS0xIikpXQojIGNoZWNrCmRvbWFpbl9jb2xvcgphcy5mYWN0b3IoVihncnBoKSQidGF4b25vbXktMSIpCgoKIyBQbG90CnBsb3QoZ3JwaCwKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGgpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3IpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIFdob2xlIFdhdGVyIENvbHVtbiIpCmxlZ2VuZCgidG9wcmlnaHQiLGJ0eSA9ICJuIiwKICAgICAgIGxlZ2VuZD1jKCJBcmNoYWVhIiwiQmFjdGVyaWEiLCAiRXVrYXJ5YSIsICJObyBCbGFzdCBIaXQiKSwKICAgICAgIGZpbGw9YygicmVkIiwgImdyZWVuIiwgImJsdWUiLCAieWVsbG93IiksIGJvcmRlcj1OQSkKCiMgU2F2ZSBwbG90CnNldEVQUygpCnBvc3RzY3JpcHQoZmlsZSA9ICJGaWd1cmVzLzNkb21haW5zX2FsbGRlcHRoc19zcGllY2Vhc2lfbmV0d29yay5lcHMiLCB3aWR0aCA9IDUuNSwgaGVpZ2h0ID0gNSkKcGxvdChncnBoLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaCksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcikKdGl0bGUoIlNwaWVjRWFzaSBOZXR3b3JrOiBBbGwgZG9tYWlucywgV2hvbGUgV2F0ZXIgQ29sdW1uIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCJCYWN0ZXJpYSIsICJFdWthcnlhIiwgIk5vIEJsYXN0IEhpdCIpLAogICAgICAgZmlsbD1jKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIsICJ5ZWxsb3ciKSwgYm9yZGVyPU5BKQpkZXYub2ZmKCkKYGBgCgojIyMjIFBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBlZGdlcyBzZXBhcmF0ZWx5CmBgYHtyfQojIFN1YnNldCBiYXNlZCBvbiBwb3Mgb3IgbmVnIGVkZ2VzCmdycGgucG9zIDwtZGVsZXRlLmVkZ2VzKGdycGgsIHdoaWNoKEUoZ3JwaCkkd2VpZ2h0PDApKQpncnBoLm5lZyA8LWRlbGV0ZS5lZGdlcyhncnBoLCB3aGljaChFKGdycGgpJHdlaWdodD4wKSkKCiMgRm9yIGVhY2ggc3Vic2V0dGVkIGdyYXBoLCByZW1vdmUgdGhvc2Ugbm9kZXMgdGhhdCBhcmUgbm8gbG9uZ2VyIGNvbm5lY3RlZCB0byBhbnl0aGluZwpncnBoLnBvcyA8LSBkZWxldGUudmVydGljZXMoZ3JwaC5wb3MsIHdoaWNoKGRlZ3JlZShncnBoLnBvcyk9PTApKQpncnBoLm5lZyA8LSBkZWxldGUudmVydGljZXMoZ3JwaC5uZWcsIHdoaWNoKGRlZ3JlZShncnBoLm5lZyk9PTApKQoKIyBNYWtlIGNvbG9yIHZlY3RvciBmb3IgZWFjaApkb21haW5fY29sb3JfcG9zIDwtIGR0eXBlW2FzLm51bWVyaWMoYXMuZmFjdG9yKFYoZ3JwaC5wb3MpJCJ0YXhvbm9teS0xIikpXQpkb21haW5fY29sb3JfbmVnIDwtIGR0eXBlW2FzLm51bWVyaWMoYXMuZmFjdG9yKFYoZ3JwaC5uZWcpJCJ0YXhvbm9teS0xIikpXQoKIyBQbG90IHBvcwpwbG90KGdycGgucG9zLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5wb3MpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfcG9zKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBQb3NpdGl2ZSBFZGdlcywgV2hvbGUgV2F0ZXIgQ29sdW1uIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCJCYWN0ZXJpYSIsICJFdWthcnlhIiwgIk5vIEJsYXN0IEhpdCIpLAogICAgICAgZmlsbD1jKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIsICJ5ZWxsb3ciKSwgYm9yZGVyPU5BKQoKIyBQbG90IG5lZwpwbG90KGdycGgubmVnLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfbmVnKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBOZWdhdGl2ZSBFZGdlcywgV2hvbGUgV2F0ZXIgQ29sdW1uIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCJCYWN0ZXJpYSIsICJFdWthcnlhIiwgIk5vIEJsYXN0IEhpdCIpLAogICAgICAgZmlsbD1jKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIsICJ5ZWxsb3ciKSwgYm9yZGVyPU5BKQoKCiMgU2F2ZSBwbG90cwpzZXRFUFMoKQpwb3N0c2NyaXB0KGZpbGUgPSAiRmlndXJlcy8zZG9tYWluc19hbGxkZXB0aHNfcG9zZWRnZXNfc3BpZWNlYXNpX25ldHdvcmsuZXBzIiwgd2lkdGggPSA1LjUsIGhlaWdodCA9IDUpCnBsb3QoZ3JwaC5wb3MsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLnBvcyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl9wb3MpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIFBvc2l0aXZlIEVkZ2VzLCBXaG9sZSBXYXRlciBDb2x1bW4iKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQmFjdGVyaWEiLCJBcmNoYWVhIiwgIkV1a2FyeWEiKSwKICAgICAgIGZpbGw9YygicmVkIiwiZ3JlZW4iLCJibHVlIiksIGJvcmRlcj1OQSkKZGV2Lm9mZigpCgpzZXRFUFMoKQpwb3N0c2NyaXB0KGZpbGUgPSAiRmlndXJlcy8zZG9tYWluc19hbGxkZXB0aHNfbmVnZWRnZXNfc3BpZWNlYXNpX25ldHdvcmsuZXBzIiwgd2lkdGggPSA1LjUsIGhlaWdodCA9IDUpCnBsb3QoZ3JwaC5uZWcsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLm5lZyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl9uZWcpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIE5lZ2F0aXZlIEVkZ2VzLCBXaG9sZSBXYXRlciBDb2x1bW4iKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQXJjaGFlYSIsIkJhY3RlcmlhIiwgIkV1a2FyeWEiLCAiTm8gQmxhc3QgSGl0IiksCiAgICAgICBmaWxsPWMoInJlZCIsICJncmVlbiIsICJibHVlIiwgInllbGxvdyIpLCBib3JkZXI9TkEpCgpkZXYub2ZmKCkKYGBgCgoKIyMjIDIgRG9tYWluIE5ldHdvcmstIEFsbCBkZXB0aHMKUmVtb3ZlIGV1a2FyeW90ZXMgdG8gc2VlIGltcGFjdCBvbiBuZXR3b3JrCmBgYHtyfQojRXh0cmFjdCBhZGphY2VuY3kgbWF0cml4IGZyb20gc3BpZWNFYXNpIG91dHB1dAphZGoubWF0IDwtIGdldFJlZml0KHNlLjJkb21haW5zKQp0YWJsZShhcy5udW1lcmljKGFkai5tYXQpKQoKIyBFeHRyYWN0IHdlaWdodGVkIGFkamFjZW5jeQpzZS5jb3IgIDwtIGNvdjJjb3IoYXMubWF0cml4KGdldE9wdENvdihzZS4yZG9tYWlucykpKQp3ZWlnaHRlZC5hZGoubWF0IDwtIHNlLmNvcipnZXRSZWZpdChzZS4yZG9tYWlucykKCiNDb252ZXJ0IHRvIGdyYXBoIG9iamVjdHMKZ3JwaC51bndlaWdodGVkIDwtIGFkajJpZ3JhcGgoYWRqLm1hdCkKZ3JwaC4yZG9tYWlucyA8LSBhZGoyaWdyYXBoKHdlaWdodGVkLmFkai5tYXQpCgoKIyBQdXQgYmFjayBpbiBzcGVjaWVzIG5hbWVzClYoZ3JwaC4yZG9tYWlucykkbmFtZSA8LSByb3duYW1lcyh0d29kb21haW5zX2RmKQojIFYoZ3JwaC4yZG9tYWlucykKCiMgTWFrZSBzaXplIG9mIG5vZGVzIHByb3BvcnRpb25hbCB0byBkZWdyZWUgKG51bWJlciBvZiBjb25uZWN0aW9ucykKVihncnBoLjJkb21haW5zKSRzaXplIDwtIChkZWdyZWUoZ3JwaC4yZG9tYWlucykgKyAxKSAjIHRoZSArMSBhdm9pZHMgc2l6ZSB6ZXJvIHZlcnRpY2VzCgojIENvbG9yIGVkZ2VzIGJ5IGNvbm5lY3Rpb24gKHBvc2l0aXZlIG9yIG5lZ2F0aXZlKSAKIyBFKGdycGguMmRvbWFpbnMpJGNvbG9yIDwtIGN1c3RvbWJsdWVncmVlbgojIEUoZ3JwaC4yZG9tYWlucykkY29sb3JbRShncnBoLjJkb21haW5zKSR3ZWlnaHQ8MF0gPC0gY3VzdG9tcmVkZGlzaHB1cnBsZQoKIyBDaGFuZ2Ugd2lkdGggb2YgZWRnZXMgdG8gYmUgcHJvcG9ydGlvbmFsIHRvIHRoZWlyIHdlaWdodHMKRShncnBoLjJkb21haW5zKSR3aWR0aCA8LSBhYnMoRShncnBoLjJkb21haW5zKSR3ZWlnaHQpKjEwCgojIFNjYWxlIG5vZGUgc2l6ZXMgdG8gYmUgc21hbGxlcgpWKGdycGguMmRvbWFpbnMpJHNpemUgPC0gVihncnBoLjJkb21haW5zKSRzaXplLzIKCiMgUmVtb3ZlIGxvdy13ZWlnaHQgZWRnZXMgKHlvdSBkZWNpZGUgd2hhdCB0aHJlc2hvbGQgaXMgcmlnaHQgZm9yIHlvdXIgbmV0d29yayk6CiMgd2VpZ2h0X3RocmVzaG9sZCA8LSAwLjA3CiMgZ3JwaC4yZG9tYWlucyA8LSBkZWxldGUuZWRnZXMoZ3JwaC4yZG9tYWlucyx3aGljaChhYnMoRShncnBoLjJkb21haW5zKSR3ZWlnaHQpPHdlaWdodF90aHJlc2hvbGQpKQoKIyBKb2luIHRheG9ub215IGRhdGEgb2YgZWFjaCBub2RlCiMgQ29udmVydCBncmFwaCB0byBkYXRhZnJhbQpncnBoLjJkb21haW5zX2RmIDwtIGlncmFwaDo6YXNfZGF0YV9mcmFtZShncnBoLjJkb21haW5zLCAnYm90aCcpCiMgbWFrZSBmb3JtYXR0ZWQgdGF4b25vbXkgdGFibGUgZm9yIGVhY2ggZG9tYWluCnBzX2JhY19wcnVuZWRfMmRvbWFpbnNfdGF4X3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUodGF4X3RhYmxlKHBzX2JhY19wcnVuZWRfMmRvbWFpbnMpKSAlPiUKICBtdXRhdGUobmFtZSA9IHJvd25hbWVzKHRheF90YWJsZShwc19iYWNfcHJ1bmVkXzJkb21haW5zKSkpCnBzX2FyY2hfMmRvbWFpbnNfcHJ1bmVkX3RheF90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRheF90YWJsZShwc19hcmNoX3BydW5lZF8yZG9tYWlucykpICU+JQogIG11dGF0ZShuYW1lID0gcm93bmFtZXModGF4X3RhYmxlKHBzX2FyY2hfcHJ1bmVkXzJkb21haW5zKSkpCiMgbGluayBncmFwaCBkYXRhIGZyYW1lIHRvIGZvcm1hdHRlZCB0YXhvbm9teSB0YWJsZXMKYmFjX3RlbXAgPC0gbGVmdF9qb2luKGdycGguMmRvbWFpbnNfZGYkdmVydGljZXNbMTpudGF4YShwc19iYWNfcHJ1bmVkXzJkb21haW5zKSxdLApwc19iYWNfcHJ1bmVkXzJkb21haW5zX3RheF90YWJsZSwgYnkgPSAibmFtZSIpCiAgIyBkZWxldGUgY29sdW1ucyB0aGF0IGRvbid0IG1hdGNoIG90aGVyIHRheCB0YWJsZXMKICBiYWNfdGVtcCA8LSBzZWxlY3QoYmFjX3RlbXAsIC0idGF4b25vbXktOSIsIC0iUmVmaW5lZCB0YXhvbm9teSIpCmFyY2hfdGVtcCA8LSBsZWZ0X2pvaW4oZ3JwaC4yZG9tYWluc19kZiR2ZXJ0aWNlc1tudGF4YShwc19iYWNfcHJ1bmVkXzJkb21haW5zKSsxOm50YXhhKHBzX2FyY2hfcHJ1bmVkXzJkb21haW5zKSxdLCBwc19hcmNoXzJkb21haW5zX3BydW5lZF90YXhfdGFibGUsIGJ5ID0gIm5hbWUiKQojIGJ1aWxkIGZ1bGwgZGF0YWZyYW1lIHdpdGggYWxsIDMgZG9tYWlucwphbGxfdGVtcCA8LSByYmluZChiYWNfdGVtcCwgYXJjaF90ZW1wKQojIHJlbWFrZSBpbnRvIGdyYXBoCmdycGguMmRvbWFpbnMgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGdycGguMmRvbWFpbnNfZGYkZWRnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0ZWQgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcnRpY2VzID0gYWxsX3RlbXApCgojIE1ha2UgY29sb3IgcGFsZXR0ZXIgZm9yIGRvbWFpbgpkdHlwZSA9IGMoInJlZCIsICJncmVlbiIsICJ5ZWxsb3ciKQoKIyBNYWtlIGNvbG9yIHZlY3Rvcgpkb21haW5fY29sb3JfMmRvbWFpbnMgPC0gZHR5cGVbYXMubnVtZXJpYyhhcy5mYWN0b3IoVihncnBoLjJkb21haW5zKSQidGF4b25vbXktMSIpKV0KCiMgUGxvdApwbG90KGdycGguMmRvbWFpbnMsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLjJkb21haW5zKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yXzJkb21haW5zKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEJhY3RlcmlhIGFuZCBBcmNoYWVhLCBXaG9sZSBXYXRlciBDb2x1bW4iKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQXJjaGFlYSIsIkJhY3RlcmlhIiwgIk5vIGJsYXN0IGhpdCIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsICJ5ZWxsb3ciKSwgYm9yZGVyPU5BKQoKIyBTYXZlIHBsb3QKc2V0RVBTKCkKcG9zdHNjcmlwdChmaWxlID0gIkZpZ3VyZXMvMmRvbWFpbnNfYWxsZGVwdGhzX3NwaWVjZWFzaV9uZXR3b3JrLmVwcyIsIHdpZHRoID0gNS41LCBoZWlnaHQgPSA1KQpwbG90KGdycGguMmRvbWFpbnMsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLjJkb21haW5zKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yXzJkb21haW5zKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEJhY3RlcmlhIGFuZCBBcmNoYWVhLCBXaG9sZSBXYXRlciBDb2x1bW4iKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQXJjaGFlYSIsIkJhY3RlcmlhIiwgIk5vIGJsYXN0IGhpdCIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsICJ5ZWxsb3ciKSwgYm9yZGVyPU5BKQpkZXYub2ZmKCkKYGBgCgoKIyMjIyBQb3NpdGl2ZSBhbmQgbmVnYXRpdmUgZWRnZXMgc2VwYXJhdGVseQpgYGB7cn0KIyBTdWJzZXQgYmFzZWQgb24gcG9zIG9yIG5lZyBlZGdlcwpncnBoLjJkb21haW5zLnBvcyA8LWRlbGV0ZS5lZGdlcyhncnBoLjJkb21haW5zLCB3aGljaChFKGdycGguMmRvbWFpbnMpJHdlaWdodDwwKSkKZ3JwaC4yZG9tYWlucy5uZWcgPC1kZWxldGUuZWRnZXMoZ3JwaC4yZG9tYWlucywgd2hpY2goRShncnBoLjJkb21haW5zKSR3ZWlnaHQ+MCkpCgojIEZvciBlYWNoIHN1YnNldHRlZCBncmFwaCwgcmVtb3ZlIHRob3NlIG5vZGVzIHRoYXQgYXJlIG5vIGxvbmdlciBjb25uZWN0ZWQgdG8gYW55dGhpbmcKZ3JwaC4yZG9tYWlucy5wb3MgPC0gZGVsZXRlLnZlcnRpY2VzKGdycGguMmRvbWFpbnMucG9zLCB3aGljaChkZWdyZWUoZ3JwaC4yZG9tYWlucy5wb3MpPT0wKSkKZ3JwaC4yZG9tYWlucy5uZWcgPC0gZGVsZXRlLnZlcnRpY2VzKGdycGguMmRvbWFpbnMubmVnLCB3aGljaChkZWdyZWUoZ3JwaC4yZG9tYWlucy5uZWcpPT0wKSkKCiMgTWFrZSBjb2xvciB2ZWN0b3IgZm9yIGVhY2gKZG9tYWluX2NvbG9yXzJkb21haW5zX3BvcyA8LSBkdHlwZVthcy5udW1lcmljKGFzLmZhY3RvcihWKGdycGguMmRvbWFpbnMucG9zKSQidGF4b25vbXktMSIpKV0KZG9tYWluX2NvbG9yXzJkb21haW5zX25lZyA8LSBkdHlwZVthcy5udW1lcmljKGFzLmZhY3RvcihWKGdycGguMmRvbWFpbnMubmVnKSQidGF4b25vbXktMSIpKV0KCiMgUGxvdCBwb3MKcGxvdChncnBoLjJkb21haW5zLnBvcywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGguMmRvbWFpbnMucG9zKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yXzJkb21haW5zX3BvcykKdGl0bGUoIlNwaWVjRWFzaSBOZXR3b3JrOiBCYWN0ZXJpYSBhbmQgQXJjaGFlYSwgUG9zaXRpdmUgRWRnZXMgT25seSwgV2hvbGUgV2F0ZXIgQ29sdW1uIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCJCYWN0ZXJpYSIsICJObyBCbGFzdCBIaXQiKSwKICAgICAgIGZpbGw9YygiZ3JlZW4iLCJyZWQiLCJ5ZWxsb3ciKSwgYm9yZGVyPU5BKQoKIyBQbG90IG5lZwpwbG90KGdycGguMmRvbWFpbnMubmVnLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC4yZG9tYWlucy5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfMmRvbWFpbnNfbmVnKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEJhY3RlcmlhIGFuZCBBcmNoYWVhLCBOZWdhdGl2ZSBFZGdlcyBPbmx5LCBXaG9sZSBXYXRlciBDb2x1bW4iKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQXJjaGFlYSIsIkJhY3RlcmlhIiwgIk5vIEJsYXN0IEhpdCIpLAogICAgICAgZmlsbD1jKCJncmVlbiIsInJlZCIsInllbGxvdyIpLCBib3JkZXI9TkEpCgoKIyBTYXZlIHBsb3RzCnNldEVQUygpCnBvc3RzY3JpcHQoZmlsZSA9ICJGaWd1cmVzLzJkb21haW5zX2FsbGRlcHRoc19wb3NlZGdlc19zcGllY2Vhc2lfbmV0d29yay5lcHMiLCB3aWR0aCA9IDUuNSwgaGVpZ2h0ID0gNSkKcGxvdChncnBoLjJkb21haW5zLnBvcywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGguMmRvbWFpbnMucG9zKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yXzJkb21haW5zX3BvcykKdGl0bGUoIlNwaWVjRWFzaSBOZXR3b3JrOiBCYWN0ZXJpYSBhbmQgQXJjaGFlYSwgUG9zaXRpdmUgRWRnZXMgT25seSwgV2hvbGUgV2F0ZXIgQ29sdW1uIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCJCYWN0ZXJpYSIsICJObyBCbGFzdCBIaXQiKSwKICAgICAgIGZpbGw9YygiZ3JlZW4iLCJyZWQiLCJ5ZWxsb3ciKSwgYm9yZGVyPU5BKQpkZXYub2ZmKCkKCnNldEVQUygpCnBvc3RzY3JpcHQoZmlsZSA9ICJGaWd1cmVzLzJkb21haW5zX2FsbGRlcHRoc19uZWdlZGdlc19zcGllY2Vhc2lfbmV0d29yay5lcHMiLCB3aWR0aCA9IDUuNSwgaGVpZ2h0ID0gNSkKcGxvdChncnBoLjJkb21haW5zLm5lZywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGguMmRvbWFpbnMubmVnKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yXzJkb21haW5zX25lZykKdGl0bGUoIlNwaWVjRWFzaSBOZXR3b3JrOiBCYWN0ZXJpYSBhbmQgQXJjaGFlYSwgTmVnYXRpdmUgRWRnZXMgT25seSwgV2hvbGUgV2F0ZXIgQ29sdW1uIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgIGxlZ2VuZD1jKCJBcmNoYWVhIiwiQmFjdGVyaWEiLCAiTm8gQmxhc3QgSGl0IiksCiAgICAgICBmaWxsPWMoImdyZWVuIiwicmVkIiwieWVsbG93IiksIGJvcmRlcj1OQSkKZGV2Lm9mZigpCmBgYAoKCgoKCiMjIyAzIERvbWFpbiBOZXR3b3JrLSBPeHljbGluZQpgYGB7cn0KI0V4dHJhY3QgYWRqYWNlbmN5IG1hdHJpeCBmcm9tIHNwaWVjRWFzaSBvdXRwdXQKYWRqLm1hdCA8LSBnZXRSZWZpdChzZS5veHljbGluZSkKdGFibGUoYXMubnVtZXJpYyhhZGoubWF0KSkKCiMgRXh0cmFjdCB3ZWlnaHRlZCBhZGphY2VuY3kKc2UuY29yICA8LSBjb3YyY29yKGFzLm1hdHJpeChnZXRPcHRDb3Yoc2Uub3h5Y2xpbmUpKSkKd2VpZ2h0ZWQuYWRqLm1hdCA8LSBzZS5jb3IqZ2V0UmVmaXQoc2Uub3h5Y2xpbmUpCgojQ29udmVydCB0byBncmFwaCBvYmplY3RzCmdycGgudW53ZWlnaHRlZC5veHljbGluZSA8LSBhZGoyaWdyYXBoKGFkai5tYXQpCmdycGgub3h5Y2xpbmUgPC0gYWRqMmlncmFwaCh3ZWlnaHRlZC5hZGoubWF0KQoKCiMgUHV0IGJhY2sgaW4gc3BlY2llcyBuYW1lcwpWKGdycGgub3h5Y2xpbmUpJG5hbWUgPC0gcm93bmFtZXMoYWxsZG9tYWluc19kZl9veHljbGluZSkKIyBWKGdycGgub3h5Y2xpbmUpCgojIE1ha2Ugc2l6ZSBvZiBub2RlcyBwcm9wb3J0aW9uYWwgdG8gZGVncmVlIChudW1iZXIgb2YgY29ubmVjdGlvbnMpClYoZ3JwaC5veHljbGluZSkkc2l6ZSA8LSAoZGVncmVlKGdycGgub3h5Y2xpbmUpICsgMSkgIyB0aGUgKzEgYXZvaWRzIHNpemUgemVybyB2ZXJ0aWNlcwoKIyBDb2xvciBlZGdlcyBieSBjb25uZWN0aW9uIChwb3NpdGl2ZSBvciBuZWdhdGl2ZSkgCiMgRShncnBoLm94eWNsaW5lKSRjb2xvciA8LSBjdXN0b21ibHVlZ3JlZW4KIyBFKGdycGgub3h5Y2xpbmUpJGNvbG9yW0UoZ3JwaC5veHljbGluZSkkd2VpZ2h0PDBdIDwtIGN1c3RvbXJlZGRpc2hwdXJwbGUKCiMgQ2hhbmdlIHdpZHRoIG9mIGVkZ2VzIHRvIGJlIHByb3BvcnRpb25hbCB0byB0aGVpciB3ZWlnaHRzCkUoZ3JwaC5veHljbGluZSkkd2lkdGggPC0gYWJzKEUoZ3JwaC5veHljbGluZSkkd2VpZ2h0KSoxMAoKIyBTY2FsZSBub2RlIHNpemVzIHRvIGJlIHNtYWxsZXIKVihncnBoLm94eWNsaW5lKSRzaXplIDwtIFYoZ3JwaC5veHljbGluZSkkc2l6ZS8yCgojIFJlbW92ZSBsb3ctd2VpZ2h0IGVkZ2VzICh5b3UgZGVjaWRlIHdoYXQgdGhyZXNob2xkIGlzIHJpZ2h0IGZvciB5b3VyIG5ldHdvcmspOgojIHdlaWdodF90aHJlc2hvbGQgPC0gMC4wNwojIGdycGgub3h5Y2xpbmUgPC0gZGVsZXRlLmVkZ2VzKGdycGgub3h5Y2xpbmUsd2hpY2goYWJzKEUoZ3JwaC5veHljbGluZSkkd2VpZ2h0KTx3ZWlnaHRfdGhyZXNob2xkKSkKCiMgSm9pbiB0YXhvbm9teSBkYXRhIG9mIGVhY2ggbm9kZQojIENvbnZlcnQgZ3JhcGggdG8gZGF0YWZyYW0KZ3JwaC5veHljbGluZV9kZiA8LSBpZ3JhcGg6OmFzX2RhdGFfZnJhbWUoZ3JwaC5veHljbGluZSwgJ2JvdGgnKQojIG1ha2UgZm9ybWF0dGVkIHRheG9ub215IHRhYmxlIGZvciBlYWNoIGRvbWFpbgpwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkX3RheF90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRheF90YWJsZShwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSByb3duYW1lcyh0YXhfdGFibGUocHNfYmFjX294eWNsaW5lX3BydW5lZCkpKQpwc19hcmNoX294eWNsaW5lX3BydW5lZF90YXhfdGFibGUgPC0gYXMuZGF0YS5mcmFtZSh0YXhfdGFibGUocHNfYXJjaF9veHljbGluZV9wcnVuZWQpKSAlPiUKICBtdXRhdGUobmFtZSA9IHJvd25hbWVzKHRheF90YWJsZShwc19hcmNoX294eWNsaW5lX3BydW5lZCkpKQpwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkX3RheF90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRheF90YWJsZShwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSByb3duYW1lcyh0YXhfdGFibGUocHNfZXVrX294eWNsaW5lX3BydW5lZCkpKQojIGxpbmsgZ3JhcGggZGF0YSBmcmFtZSB0byBmb3JtYXR0ZWQgdGF4b25vbXkgdGFibGVzCmJhY190ZW1wIDwtIGxlZnRfam9pbihncnBoLm94eWNsaW5lX2RmJHZlcnRpY2VzWzE6bnRheGEocHNfYmFjX294eWNsaW5lX3BydW5lZCksXSwKcHNfYmFjX294eWNsaW5lX3BydW5lZF90YXhfdGFibGUsIGJ5ID0gIm5hbWUiKQogICMgZGVsZXRlIGNvbHVtbnMgdGhhdCBkb24ndCBtYXRjaCBvdGhlciB0YXggdGFibGVzCiAgYmFjX3RlbXAgPC0gc2VsZWN0KGJhY190ZW1wLCAtInRheG9ub215LTkiLCAtIlJlZmluZWQgdGF4b25vbXkiKQphcmNoX3RlbXAgPC0gbGVmdF9qb2luKGdycGgub3h5Y2xpbmVfZGYkdmVydGljZXNbbnRheGEocHNfYmFjX294eWNsaW5lX3BydW5lZCkrMTpudGF4YShwc19hcmNoX294eWNsaW5lX3BydW5lZCksXSxwc19hcmNoX294eWNsaW5lX3BydW5lZF90YXhfdGFibGUsIGJ5ID0gIm5hbWUiKQpldWtfdGVtcCA8LSBsZWZ0X2pvaW4oZ3JwaC5veHljbGluZV9kZiR2ZXJ0aWNlc1tudGF4YShwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKStudGF4YShwc19hcmNoX294eWNsaW5lX3BydW5lZCkrMTpudGF4YShwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKSxdLCBwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkX3RheF90YWJsZSwgYnkgPSAibmFtZSIpCiAgIyByZW5hbWUgY29sdW1uIG5hbWVzIGluIGV1ayB0YWJsZSB0byBtYXRjaCBvdGhlcnMKICBldWtfdGVtcCA8LSBldWtfdGVtcCAlPiUKICAgIHJlbmFtZSgidGF4b25vbXktMSIgPSBLaW5nZG9tLCAidGF4b25vbXktMiIgPSBTdXBlcmdyb3VwLCAidGF4b25vbXktMyIgPSBEaXZpc2lvbiwgInRheG9ub215LTQiID0gQ2xhc3MsICJ0YXhvbm9teS01IiA9IE9yZGVyLCAidGF4b25vbXktNiIgPSBGYW1pbHksICJ0YXhvbm9teS03IiA9IEdlbnVzLCAidGF4b25vbXktOCIgPSBTcGVjaWVzKQojIGJ1aWxkIGZ1bGwgZGF0YWZyYW1lIHdpdGggYWxsIDMgZG9tYWlucwphbGxfdGVtcCA8LSByYmluZChiYWNfdGVtcCwgYXJjaF90ZW1wLCBldWtfdGVtcCkKIyByZW1ha2UgaW50byBncmFwaApncnBoLm94eWNsaW5lIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShncnBoLm94eWNsaW5lX2RmJGVkZ2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGVkID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ0aWNlcyA9IGFsbF90ZW1wKQoKIyBNYWtlIGNvbG9yIHBhbGV0dGVyIGZvciBkb21haW4KZHR5cGUgPSBjKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIpCiMgTWFrZSBjb2xvciB2ZWN0b3IKZG9tYWluX2NvbG9yX294eWNsaW5lIDwtIGR0eXBlW2FzLm51bWVyaWMoYXMuZmFjdG9yKFYoZ3JwaC5veHljbGluZSkkInRheG9ub215LTEiKSldCgoKIyBQbG90CnBsb3QoZ3JwaC5veHljbGluZSwKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGgub3h5Y2xpbmUpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3Jfb3h5Y2xpbmUpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIE94eWNsaW5lIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCAiQmFjdGVyaWEiLCAiRXVrYXJ5YSIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsImJsdWUiKSwgYm9yZGVyPU5BKQoKIyBTYXZlIHBsb3QKc2V0RVBTKCkKcG9zdHNjcmlwdChmaWxlID0gIkZpZ3VyZXMvM2RvbWFpbnNfb3h5Y2xpbmVfc3BpZWNlYXNpX25ldHdvcmsuZXBzIiwgd2lkdGggPSA1LjUsIGhlaWdodCA9IDUpCnBsb3QoZ3JwaC5veHljbGluZSwKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGgub3h5Y2xpbmUpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3Jfb3h5Y2xpbmUpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIE94eWNsaW5lIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCAiQmFjdGVyaWEiLCAiRXVrYXJ5YSIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsImJsdWUiKSwgYm9yZGVyPU5BKQpkZXYub2ZmKCkKYGBgCgoKCiMjIyMgUG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGVkZ2VzIHNlcGFyYXRlbHkKYGBge3J9CiMgU3Vic2V0IGJhc2VkIG9uIHBvcyBvciBuZWcgZWRnZXMKZ3JwaC5veHljbGluZS5wb3MgPC1kZWxldGUuZWRnZXMoZ3JwaC5veHljbGluZSwgd2hpY2goRShncnBoLm94eWNsaW5lKSR3ZWlnaHQ8MCkpCmdycGgub3h5Y2xpbmUubmVnIDwtZGVsZXRlLmVkZ2VzKGdycGgub3h5Y2xpbmUsIHdoaWNoKEUoZ3JwaC5veHljbGluZSkkd2VpZ2h0PjApKQoKIyBGb3IgZWFjaCBzdWJzZXR0ZWQgZ3JhcGgsIHJlbW92ZSB0aG9zZSBub2RlcyB0aGF0IGFyZSBubyBsb25nZXIgY29ubmVjdGVkIHRvIGFueXRoaW5nCmdycGgub3h5Y2xpbmUucG9zIDwtIGRlbGV0ZS52ZXJ0aWNlcyhncnBoLm94eWNsaW5lLnBvcywgd2hpY2goZGVncmVlKGdycGgub3h5Y2xpbmUucG9zKT09MCkpCmdycGgub3h5Y2xpbmUubmVnIDwtIGRlbGV0ZS52ZXJ0aWNlcyhncnBoLm94eWNsaW5lLm5lZywgd2hpY2goZGVncmVlKGdycGgub3h5Y2xpbmUubmVnKT09MCkpCgojIE1ha2UgY29sb3IgdmVjdG9yIGZvciBlYWNoCmRvbWFpbl9jb2xvcl9veHljbGluZV9wb3MgPC0gZHR5cGVbYXMubnVtZXJpYyhhcy5mYWN0b3IoVihncnBoLm94eWNsaW5lLnBvcykkInRheG9ub215LTEiKSldCmRvbWFpbl9jb2xvcl9veHljbGluZV9uZWcgPC0gZHR5cGVbYXMubnVtZXJpYyhhcy5mYWN0b3IoVihncnBoLm94eWNsaW5lLm5lZykkInRheG9ub215LTEiKSldCgojIFBsb3QgcG9zCnBsb3QoZ3JwaC5veHljbGluZS5wb3MsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLm94eWNsaW5lLnBvcyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl9veHljbGluZV9wb3MpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIFBvc2l0aXZlIEVkZ2VzIG9ubHksIE94eWNsaW5lIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCAiQmFjdGVyaWEiLCAiRXVrYXJ5YSIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsImJsdWUiKSwgYm9yZGVyPU5BKQoKIyBQbG90IG5lZwpwbG90KGdycGgub3h5Y2xpbmUubmVnLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5veHljbGluZS5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3Jfb3h5Y2xpbmVfbmVnKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBOZWdhdGl2ZSBFZGdlcyBPbmx5LCBPeHljbGluZSIpCmxlZ2VuZCgidG9wcmlnaHQiLGJ0eSA9ICJuIiwKICAgICAgIGxlZ2VuZD1jKCJBcmNoYWVhIiwgIkJhY3RlcmlhIiwgIkV1a2FyeWEiKSwKICAgICAgIGZpbGw9YygicmVkIiwiZ3JlZW4iLCJibHVlIiksIGJvcmRlcj1OQSkKCgojIFNhdmUgcGxvdHMKc2V0RVBTKCkKcG9zdHNjcmlwdChmaWxlID0gIkZpZ3VyZXMvM2RvbWFpbnNfb3h5Y2xpbmVfcG9zZWRnZXNfc3BpZWNlYXNpX25ldHdvcmsuZXBzIiwgd2lkdGggPSA1LjUsIGhlaWdodCA9IDUpCnBsb3QoZ3JwaC5veHljbGluZS5wb3MsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLm94eWNsaW5lLnBvcyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl9veHljbGluZV9wb3MpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIFBvc2l0aXZlIEVkZ2VzIE9ubHksIE94eWNsaW5lIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCAiQmFjdGVyaWEiLCAiRXVrYXJ5YSIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsImJsdWUiKSwgYm9yZGVyPU5BKQpkZXYub2ZmKCkKCnNldEVQUygpCnBvc3RzY3JpcHQoZmlsZSA9ICJGaWd1cmVzLzNkb21haW5zX294eWNsaW5lX25lZ2VkZ2VzX3NwaWVjZWFzaV9uZXR3b3JrLmVwcyIsIHdpZHRoID0gNS41LCBoZWlnaHQgPSA1KQpwbG90KGdycGgub3h5Y2xpbmUubmVnLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5veHljbGluZS5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3Jfb3h5Y2xpbmVfbmVnKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBOZWdhdGl2ZSBFZGdlcyBPbmx5LCBPeHljbGluZSIpCmxlZ2VuZCgidG9wcmlnaHQiLGJ0eSA9ICJuIiwKICAgICAgIGxlZ2VuZD1jKCJBcmNoYWVhIiwgIkJhY3RlcmlhIiwgIkV1a2FyeWEiKSwKICAgICAgIGZpbGw9YygicmVkIiwiZ3JlZW4iLCJibHVlIiksIGJvcmRlcj1OQSkKCmRldi5vZmYoKQpgYGAKCgoKCgoKCiMjIyAzIERvbWFpbiBOZXR3b3JrLSBBbm94aWMKYGBge3J9CiNFeHRyYWN0IGFkamFjZW5jeSBtYXRyaXggZnJvbSBzcGllY0Vhc2kgb3V0cHV0CmFkai5tYXQgPC0gZ2V0UmVmaXQoc2UuYW5veGljKQp0YWJsZShhcy5udW1lcmljKGFkai5tYXQpKQoKIyBFeHRyYWN0IHdlaWdodGVkIGFkamFjZW5jeQpzZS5jb3IgIDwtIGNvdjJjb3IoYXMubWF0cml4KGdldE9wdENvdihzZS5hbm94aWMpKSkKd2VpZ2h0ZWQuYWRqLm1hdCA8LSBzZS5jb3IqZ2V0UmVmaXQoc2UuYW5veGljKQoKI0NvbnZlcnQgdG8gZ3JhcGggb2JqZWN0cwpncnBoLnVud2VpZ2h0ZWQuYW5veGljIDwtIGFkajJpZ3JhcGgoYWRqLm1hdCkKZ3JwaC5hbm94aWMgPC0gYWRqMmlncmFwaCh3ZWlnaHRlZC5hZGoubWF0KQoKCiMgUHV0IGJhY2sgaW4gc3BlY2llcyBuYW1lcwpWKGdycGguYW5veGljKSRuYW1lIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGZfYW5veGljKQojIFYoZ3JwaC5hbm94aWMpCgoKIyBNYWtlIHNpemUgb2Ygbm9kZXMgcHJvcG9ydGlvbmFsIHRvIGRlZ3JlZSAobnVtYmVyIG9mIGNvbm5lY3Rpb25zKQpWKGdycGguYW5veGljKSRzaXplIDwtIChkZWdyZWUoZ3JwaC5hbm94aWMpICsgMSkgIyB0aGUgKzEgYXZvaWRzIHNpemUgemVybyB2ZXJ0aWNlcwoKIyBDb2xvciBlZGdlcyBieSBjb25uZWN0aW9uIChwb3NpdGl2ZSBvciBuZWdhdGl2ZSkgCiMgRShncnBoLmFub3hpYykkY29sb3IgPC0gY3VzdG9tYmx1ZWdyZWVuCiMgRShncnBoLmFub3hpYykkY29sb3JbRShncnBoLmFub3hpYykkd2VpZ2h0PDBdIDwtIGN1c3RvbXJlZGRpc2hwdXJwbGUKCiMgQ2hhbmdlIHdpZHRoIG9mIGVkZ2VzIHRvIGJlIHByb3BvcnRpb25hbCB0byB0aGVpciB3ZWlnaHRzCkUoZ3JwaC5hbm94aWMpJHdpZHRoIDwtIGFicyhFKGdycGguYW5veGljKSR3ZWlnaHQpKjEwCgojIFNjYWxlIG5vZGUgc2l6ZXMgdG8gYmUgc21hbGxlcgpWKGdycGguYW5veGljKSRzaXplIDwtIFYoZ3JwaC5hbm94aWMpJHNpemUvMgoKIyBSZW1vdmUgbG93LXdlaWdodCBlZGdlcyAoeW91IGRlY2lkZSB3aGF0IHRocmVzaG9sZCBpcyByaWdodCBmb3IgeW91ciBuZXR3b3JrKToKIyB3ZWlnaHRfdGhyZXNob2xkIDwtIDAuMDcKIyBncnBoLmFub3hpYyA8LSBkZWxldGUuZWRnZXMoZ3JwaC5hbm94aWMsd2hpY2goYWJzKEUoZ3JwaC5hbm94aWMpJHdlaWdodCk8d2VpZ2h0X3RocmVzaG9sZCkpCgojIEpvaW4gdGF4b25vbXkgZGF0YSBvZiBlYWNoIG5vZGUKIyBDb252ZXJ0IGdyYXBoIHRvIGRhdGFmcmFtCmdycGguYW5veGljX2RmIDwtIGlncmFwaDo6YXNfZGF0YV9mcmFtZShncnBoLmFub3hpYywgJ2JvdGgnKQojIG1ha2UgZm9ybWF0dGVkIHRheG9ub215IHRhYmxlIGZvciBlYWNoIGRvbWFpbgpwc19iYWNfYW5veGljX3BydW5lZF90YXhfdGFibGUgPC0gYXMuZGF0YS5mcmFtZSh0YXhfdGFibGUocHNfYmFjX2Fub3hpY19wcnVuZWQpKSAlPiUKICBtdXRhdGUobmFtZSA9IHJvd25hbWVzKHRheF90YWJsZShwc19iYWNfYW5veGljX3BydW5lZCkpKQpwc19hcmNoX2Fub3hpY19wcnVuZWRfdGF4X3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUodGF4X3RhYmxlKHBzX2FyY2hfYW5veGljX3BydW5lZCkpICU+JQogIG11dGF0ZShuYW1lID0gcm93bmFtZXModGF4X3RhYmxlKHBzX2FyY2hfYW5veGljX3BydW5lZCkpKQpwc19ldWtfYW5veGljX3BydW5lZF90YXhfdGFibGUgPC0gYXMuZGF0YS5mcmFtZSh0YXhfdGFibGUocHNfZXVrX2Fub3hpY19wcnVuZWQpKSAlPiUKICBtdXRhdGUobmFtZSA9IHJvd25hbWVzKHRheF90YWJsZShwc19ldWtfYW5veGljX3BydW5lZCkpKQojIGxpbmsgZ3JhcGggZGF0YSBmcmFtZSB0byBmb3JtYXR0ZWQgdGF4b25vbXkgdGFibGVzCmJhY190ZW1wIDwtIGxlZnRfam9pbihncnBoLmFub3hpY19kZiR2ZXJ0aWNlc1sxOm50YXhhKHBzX2JhY19hbm94aWNfcHJ1bmVkKSxdLHBzX2JhY19hbm94aWNfcHJ1bmVkX3RheF90YWJsZSwgYnkgPSAibmFtZSIpCiAgIyBkZWxldGUgY29sdW1ucyB0aGF0IGRvbid0IG1hdGNoIG90aGVyIHRheCB0YWJsZXMKICBiYWNfdGVtcCA8LSBzZWxlY3QoYmFjX3RlbXAsIC0idGF4b25vbXktOSIsIC0iUmVmaW5lZCB0YXhvbm9teSIpCmFyY2hfdGVtcCA8LSBsZWZ0X2pvaW4oZ3JwaC5hbm94aWNfZGYkdmVydGljZXNbbnRheGEocHNfYmFjX2Fub3hpY19wcnVuZWQpKzE6bnRheGEocHNfYXJjaF9hbm94aWNfcHJ1bmVkKSxdLHBzX2FyY2hfYW5veGljX3BydW5lZF90YXhfdGFibGUsIGJ5ID0gIm5hbWUiKQpldWtfdGVtcCA8LSBsZWZ0X2pvaW4oZ3JwaC5hbm94aWNfZGYkdmVydGljZXNbbnRheGEocHNfYmFjX2Fub3hpY19wcnVuZWQpK250YXhhKHBzX2FyY2hfYW5veGljX3BydW5lZCkrMTpudGF4YShwc19ldWtfYW5veGljX3BydW5lZCksXSwgcHNfZXVrX2Fub3hpY19wcnVuZWRfdGF4X3RhYmxlLCBieSA9ICJuYW1lIikKICAjIHJlbmFtZSBjb2x1bW4gbmFtZXMgaW4gZXVrIHRhYmxlIHRvIG1hdGNoIG90aGVycwogIGV1a190ZW1wIDwtIGV1a190ZW1wICU+JQogICAgcmVuYW1lKCJ0YXhvbm9teS0xIiA9IEtpbmdkb20sICJ0YXhvbm9teS0yIiA9IFN1cGVyZ3JvdXAsICJ0YXhvbm9teS0zIiA9IERpdmlzaW9uLCAidGF4b25vbXktNCIgPSBDbGFzcywgInRheG9ub215LTUiID0gT3JkZXIsICJ0YXhvbm9teS02IiA9IEZhbWlseSwgInRheG9ub215LTciID0gR2VudXMsICJ0YXhvbm9teS04IiA9IFNwZWNpZXMpCiMgYnVpbGQgZnVsbCBkYXRhZnJhbWUgd2l0aCBhbGwgMyBkb21haW5zCmFsbF90ZW1wIDwtIHJiaW5kKGJhY190ZW1wLCBhcmNoX3RlbXAsIGV1a190ZW1wKQojIHJlbWFrZSBpbnRvIGdyYXBoCmdycGguYW5veGljIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShncnBoLmFub3hpY19kZiRlZGdlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3RlZCA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVydGljZXMgPSBhbGxfdGVtcCkKCiMgTWFrZSBjb2xvciBwYWxldHRlciBmb3IgZG9tYWluCmR0eXBlID0gYygicmVkIiwgImdyZWVuIiwgImJsdWUiKQojIE1ha2UgY29sb3IgdmVjdG9yCmRvbWFpbl9jb2xvcl9hbm94aWMgPC0gZHR5cGVbYXMubnVtZXJpYyhhcy5mYWN0b3IoVihncnBoLmFub3hpYykkInRheG9ub215LTEiKSldCgojIFBsb3QKcGxvdChncnBoLmFub3hpYywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGguYW5veGljKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yX2Fub3hpYykKdGl0bGUoIlNwaWVjRWFzaSBOZXR3b3JrOiBBbGwgZG9tYWlucywgQW5veGljIExheWVyIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCAiQmFjdGVyaWEiLCAiRXVrYXJ5YSIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsImJsdWUiKSwgYm9yZGVyPU5BKQoKIyBTYXZlIHBsb3QKc2V0RVBTKCkKcG9zdHNjcmlwdChmaWxlID0gIkZpZ3VyZXMvM2RvbWFpbnNfYW5veGljX3NwaWVjZWFzaV9uZXR3b3JrLmVwcyIsIHdpZHRoID0gNS41LCBoZWlnaHQgPSA1KQpwbG90KGdycGguYW5veGljLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5hbm94aWMpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfYW5veGljKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBBbm94aWMiKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQXJjaGFlYSIsICJCYWN0ZXJpYSIsICJFdWthcnlhIiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZWVuIiwiYmx1ZSIpLCBib3JkZXI9TkEpCmRldi5vZmYoKQpgYGAKCgojIyMjIFBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBlZGdlcyBzZXBhcmF0ZWx5CgpgYGB7cn0KIyBTdWJzZXQgYmFzZWQgb24gcG9zIG9yIG5lZyBlZGdlcwpncnBoLmFub3hpYy5wb3MgPC1kZWxldGUuZWRnZXMoZ3JwaC5hbm94aWMsIHdoaWNoKEUoZ3JwaC5hbm94aWMpJHdlaWdodDwwKSkKZ3JwaC5hbm94aWMubmVnIDwtZGVsZXRlLmVkZ2VzKGdycGguYW5veGljLCB3aGljaChFKGdycGguYW5veGljKSR3ZWlnaHQ+MCkpCgojIEZvciBlYWNoIHN1YnNldHRlZCBncmFwaCwgcmVtb3ZlIHRob3NlIG5vZGVzIHRoYXQgYXJlIG5vIGxvbmdlciBjb25uZWN0ZWQgdG8gYW55dGhpbmcKZ3JwaC5hbm94aWMucG9zIDwtIGRlbGV0ZS52ZXJ0aWNlcyhncnBoLmFub3hpYy5wb3MsIHdoaWNoKGRlZ3JlZShncnBoLmFub3hpYy5wb3MpPT0wKSkKZ3JwaC5hbm94aWMubmVnIDwtIGRlbGV0ZS52ZXJ0aWNlcyhncnBoLmFub3hpYy5uZWcsIHdoaWNoKGRlZ3JlZShncnBoLmFub3hpYy5uZWcpPT0wKSkKCiMgTWFrZSBjb2xvciB2ZWN0b3IgZm9yIGVhY2gKZG9tYWluX2NvbG9yX2Fub3hpY19wb3MgPC0gZHR5cGVbYXMubnVtZXJpYyhhcy5mYWN0b3IoVihncnBoLmFub3hpYy5wb3MpJCJ0YXhvbm9teS0xIikpXQpkb21haW5fY29sb3JfYW5veGljX25lZyA8LSBkdHlwZVthcy5udW1lcmljKGFzLmZhY3RvcihWKGdycGguYW5veGljLm5lZykkInRheG9ub215LTEiKSldCgojIFBsb3QgcG9zCnBsb3QoZ3JwaC5hbm94aWMucG9zLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5hbm94aWMucG9zKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yX2Fub3hpY19wb3MpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIFBvc2l0aXZlIEVkZ2VzIG9ubHksIGFub3hpYyIpCmxlZ2VuZCgidG9wcmlnaHQiLGJ0eSA9ICJuIiwKICAgICAgIGxlZ2VuZD1jKCJBcmNoYWVhIiwgIkJhY3RlcmlhIiwgIkV1a2FyeWEiKSwKICAgICAgIGZpbGw9YygicmVkIiwiZ3JlZW4iLCJibHVlIiksIGJvcmRlcj1OQSkKCiMgUGxvdCBuZWcKcGxvdChncnBoLmFub3hpYy5uZWcsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLmFub3hpYy5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfYW5veGljX25lZykKdGl0bGUoIlNwaWVjRWFzaSBOZXR3b3JrOiBBbGwgZG9tYWlucywgTmVnYXRpdmUgRWRnZXMgT25seSwgYW5veGljIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCAiQmFjdGVyaWEiLCAiRXVrYXJ5YSIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsImJsdWUiKSwgYm9yZGVyPU5BKQoKCiMgU2F2ZSBwbG90cwpzZXRFUFMoKQpwb3N0c2NyaXB0KGZpbGUgPSAiRmlndXJlcy8zZG9tYWluc19hbm94aWNfcG9zZWRnZXNfc3BpZWNlYXNpX25ldHdvcmsuZXBzIiwgd2lkdGggPSA1LjUsIGhlaWdodCA9IDUpCnBsb3QoZ3JwaC5hbm94aWMucG9zLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5hbm94aWMucG9zKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yX2Fub3hpY19wb3MpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIFBvc2l0aXZlIEVkZ2VzIE9ubHksIFNoYWxsb3cgQW5veGljIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkFyY2hhZWEiLCAiQmFjdGVyaWEiLCAiRXVrYXJ5YSIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsImJsdWUiKSwgYm9yZGVyPU5BKQpkZXYub2ZmKCkKCnNldEVQUygpCnBvc3RzY3JpcHQoZmlsZSA9ICJGaWd1cmVzLzNkb21haW5zX2Fub3hpY19uZWdlZGdlc19zcGllY2Vhc2lfbmV0d29yay5lcHMiLCB3aWR0aCA9IDUuNSwgaGVpZ2h0ID0gNSkKcGxvdChncnBoLmFub3hpYy5uZWcsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLmFub3hpYy5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfYW5veGljX25lZykKdGl0bGUoIlNwaWVjRWFzaSBOZXR3b3JrOiBBbGwgZG9tYWlucywgTmVnYXRpdmUgRWRnZXMgT25seSwgU2hhbGxvdyBBbm94aWMiKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQXJjaGFlYSIsICJCYWN0ZXJpYSIsICJFdWthcnlhIiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZWVuIiwiYmx1ZSIpLCBib3JkZXI9TkEpCmRldi5vZmYoKQpgYGAKCgojIyMgU3VtbWFyeSBOZXR3b3JrIGZpZ3VyZQoKYGBge3J9CiMgU2V0IHVwIGluIHBhbmVscwpvcCA8LSBwYXIob21hPWMoMiwuNSwuNSwwKSwjIFJvb20gZm9yIHRoZSB0aXRsZXMgYW5kIGxlZ2VuZAogIG1mcm93PWMoNCwyKSwKICBtYWk9YyguMTUsLjMsLjE1LC4xKSkKIyBQYW5lbCAxLSBBbGwgZGVwdGhzLCBwb3NpdGl2ZSBuZXR3b3JrCnBsb3QoZ3JwaC5wb3MsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLnBvcyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl9wb3MpCm10ZXh0ICgiUG9zaXRpdmUiLCBzaWRlID0gMywgb3V0ZXIgPSBUUlVFLCBsaW5lID0gLTEsIGFkaiA9IDAuMjIsIGNleCA9IC44KQptdGV4dCgiQWxsIGRlcHRocyIsIHNpZGU9MiwgY2V4ID0gLjgsIGxpbmUgPSAxLjUpCiMgUGFuZWwgMi0gQWxsIGRlcHRocywgbmVnYXRpdmUgbmV0d29yawpwbG90KGdycGgubmVnLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfbmVnKQptdGV4dCAoIk5lZ2F0aXZlIiwgc2lkZSA9IDMsIG91dGVyID0gVFJVRSwgbGluZSA9IC0xLCBhZGogPSAuOCwgY2V4ID0gLjgpCiMgUGFuZWwgMy0gT3h5Y2xpbmUsIHBvc2l0aXZlIG5ldHdvcmsKcGxvdChncnBoLm94eWNsaW5lLnBvcywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGgub3h5Y2xpbmUucG9zKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yX294eWNsaW5lX3BvcykKbXRleHQoIk94eWNsaW5lIiwgc2lkZT0yLCBjZXggPSAuOCwgbGluZSA9IDEuNSkKIyBQYW5lbCA0LSBPeHljbGluZSwgbmVnYXRpdmUgbmV0d29yawpwbG90KGdycGgub3h5Y2xpbmUubmVnLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5veHljbGluZS5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3Jfb3h5Y2xpbmVfbmVnKQojIFBhbmVsIDUtIEFub3hpYywgcG9zaXRpdmUgbmV0d29yawpwbG90KGdycGguYW5veGljLnBvcywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGguYW5veGljLnBvcyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl9hbm94aWNfcG9zKQptdGV4dCgiQW5veGljIiwgc2lkZT0yLCBjZXggPSAuOCwgbGluZSA9IDEuNSkKIyBQYW5lbCA2LSBBbm94aWMsIG5lZ2F0aXZlIG5ldHdvcmsKcGxvdChncnBoLmFub3hpYy5uZWcsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLmFub3hpYy5uZWcpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfYW5veGljX25lZykKIyBQYW5lbCA3LSAyIERvbWFpbnMsIHBvc2l0aXZlIG5ldHdvcmsKcGxvdChncnBoLjJkb21haW5zLnBvcywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGguMmRvbWFpbnMucG9zKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yXzJkb21haW5zX3BvcykKbXRleHQoIlByb2sgT25seSIsIHNpZGU9MiwgY2V4ID0gLjgsIGxpbmUgPSAxLjUpCiMgUGFuZWwgOC0gMiBEb21haW5zLCBuZWdhdGl2ZSBuZXR3b3JrCnBsb3QoZ3JwaC4yZG9tYWlucy5uZWcsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLjJkb21haW5zLnBvcyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl8yZG9tYWluc19uZWcpCiMgQWRkIGxlZ2VuZApwYXIob3ApICMgTGVhdmUgdGhlIGxhc3QgcGxvdApvcCA8LSBwYXIodXNyPWMoMCwxLDAsMSksICMgUmVzZXQgdGhlIGNvb3JkaW5hdGVzCiAgICAgICAgICB4cGQ9TkEpICAgICAgICAgIyBBbGxvdyBwbG90dGluZyBvdXRzaWRlIHRoZSBwbG90IHJlZ2lvbgpsZWdlbmQoLjE1LC0wLjA0LCBjKCJBcmNoYWVhIiwiQmFjdGVyaWEiLCAiRXVrYXJ5YSIsICJObyBCbGFzdCBIaXQiKSwgY29sPWMoInJlZCIsICJncmVlbiIsICJibHVlIiwgInllbGxvdyIpLCBwY2ggPSBjKDE2KSwgYm94LmNvbD1OQSwgY2V4ID0gLjgsIGhvcml6ID0gVCwgeC5pbnRlcnNwID0gYygwLjMpKQoKCgoKIyBTYXZlIGZpZ3VyZQojIFNldCB1cCBFUFMgYW5kIG1ha2UgcGxvdApzZXRFUFMod2lkdGggPSA2LCBoZWlnaHQgPSA5KQpwb3N0c2NyaXB0KCJGaWd1cmVzL05ldHdvcmtzX3Bvc19uZWcuZXBzIikKb3AgPC0gcGFyKG9tYT1jKDIsLjUsLjUsMCksIyBSb29tIGZvciB0aGUgdGl0bGVzIGFuZCBsZWdlbmQKICBtZnJvdz1jKDQsMiksCiAgbWFpPWMoLjE1LC4zLC4xNSwuMSkpCiMgUGFuZWwgMS0gQWxsIGRlcHRocywgcG9zaXRpdmUgbmV0d29yawpwbG90KGdycGgucG9zLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5wb3MpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfcG9zKQptdGV4dCAoIlBvc2l0aXZlIiwgc2lkZSA9IDMsIG91dGVyID0gVFJVRSwgbGluZSA9IC0xLCBhZGogPSAwLjIyLCBjZXggPSAuOCkKbXRleHQoIkFsbCBkZXB0aHMiLCBzaWRlPTIsIGNleCA9IC44LCBsaW5lID0gMS41KQojIFBhbmVsIDItIEFsbCBkZXB0aHMsIG5lZ2F0aXZlIG5ldHdvcmsKcGxvdChncnBoLm5lZywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGgubmVnKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yX25lZykKbXRleHQgKCJOZWdhdGl2ZSIsIHNpZGUgPSAzLCBvdXRlciA9IFRSVUUsIGxpbmUgPSAtMSwgYWRqID0gLjgsIGNleCA9IC44KQojIFBhbmVsIDMtIE94eWNsaW5lLCBwb3NpdGl2ZSBuZXR3b3JrCnBsb3QoZ3JwaC5veHljbGluZS5wb3MsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLm94eWNsaW5lLnBvcyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl9veHljbGluZV9wb3MpCm10ZXh0KCJPeHljbGluZSIsIHNpZGU9MiwgY2V4ID0gLjgsIGxpbmUgPSAxLjUpCiMgUGFuZWwgNC0gT3h5Y2xpbmUsIG5lZ2F0aXZlIG5ldHdvcmsKcGxvdChncnBoLm94eWNsaW5lLm5lZywKICAgICB2ZXJ0ZXgubGFiZWw9TkEsCiAgICAgbGF5b3V0PWxheW91dF93aXRoX2dyYXBob3B0KGdycGgub3h5Y2xpbmUubmVnKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yX294eWNsaW5lX25lZykKIyBQYW5lbCA1LSBBbm94aWMsIHBvc2l0aXZlIG5ldHdvcmsKcGxvdChncnBoLmFub3hpYy5wb3MsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLmFub3hpYy5wb3MpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfYW5veGljX3BvcykKbXRleHQoIkFub3hpYyIsIHNpZGU9MiwgY2V4ID0gLjgsIGxpbmUgPSAxLjUpCiMgUGFuZWwgNi0gQW5veGljLCBuZWdhdGl2ZSBuZXR3b3JrCnBsb3QoZ3JwaC5hbm94aWMubmVnLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5hbm94aWMubmVnKSwKICAgICB2ZXJ0ZXguY29sb3I9ZG9tYWluX2NvbG9yX2Fub3hpY19uZWcpCiMgUGFuZWwgNy0gMiBEb21haW5zLCBwb3NpdGl2ZSBuZXR3b3JrCnBsb3QoZ3JwaC4yZG9tYWlucy5wb3MsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLjJkb21haW5zLnBvcyksCiAgICAgdmVydGV4LmNvbG9yPWRvbWFpbl9jb2xvcl8yZG9tYWluc19wb3MpCm10ZXh0KCJQcm9rIE9ubHkiLCBzaWRlPTIsIGNleCA9IC44LCBsaW5lID0gMS41KQojIFBhbmVsIDgtIDIgRG9tYWlucywgbmVnYXRpdmUgbmV0d29yawpwbG90KGdycGguMmRvbWFpbnMubmVnLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC4yZG9tYWlucy5wb3MpLAogICAgIHZlcnRleC5jb2xvcj1kb21haW5fY29sb3JfMmRvbWFpbnNfbmVnKQojIEFkZCBsZWdlbmQKcGFyKG9wKSAjIExlYXZlIHRoZSBsYXN0IHBsb3QKb3AgPC0gcGFyKHVzcj1jKDAsMSwwLDEpLCAjIFJlc2V0IHRoZSBjb29yZGluYXRlcwogICAgICAgICAgeHBkPU5BKSAgICAgICAgICMgQWxsb3cgcGxvdHRpbmcgb3V0c2lkZSB0aGUgcGxvdCByZWdpb24KbGVnZW5kKC4xNSwtMC4wNCwgYygiQXJjaGFlYSIsIkJhY3RlcmlhIiwgIkV1a2FyeWEiLCAiTm8gQmxhc3QgSGl0IiksIGNvbD1jKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIsICJ5ZWxsb3ciKSwgcGNoID0gYygxNiksIGJveC5jb2w9TkEsIGNleCA9IC44LCBob3JpeiA9IFQsIHguaW50ZXJzcCA9IGMoMC4zKSkKZGV2Lm9mZigpCgoKCmBgYAoKCgoKIyMjIENvbGxlY3QgbmV0d29yayBwYXJhbWV0ZXJzCgojIyMjIE51bWJlciBvZiBlZGdlcwp0aGUgbnVtYmVyIG9mIGVkZ2VzIGFuZCBob3cgbWFueSBhcmUgcG9zaXRpdmUgdnMgbmVnYXRpdmUKYGBge3J9CiMgdG90YWwgbnVtYmVyIG9mIGVkZ2VzIGluIGZ1bGwgZGF0YXNldCBuZXR3b3JrCmxlbmd0aChFKGdycGgpJHdlaWdodCkKIyBwZXJjZW50IG9mIG5lZyBlZGdlcyAKKHN1bShFKGdycGgpJHdlaWdodDwwKS9sZW5ndGgoRShncnBoKSR3ZWlnaHQpKSoxMDAKCiMgdG90YWwgbnVtYmVyIG9mIGVkZ2VzIGluIDItZG9tYWluIGRhdGFzZXQgbmV0d29yawpsZW5ndGgoRShncnBoLjJkb21haW5zKSR3ZWlnaHQpCiMgcGVyY2VudCBvZiBuZWcgZWRnZXMgCihzdW0oRShncnBoLjJkb21haW5zKSR3ZWlnaHQ8MCkvbGVuZ3RoKEUoZ3JwaC4yZG9tYWlucykkd2VpZ2h0KSkqMTAwCgojIHRvdGFsIG51bWJlciBvZiBlZGdlcyBpbiBveHljbGluZSBuZXR3b3JrCmxlbmd0aChFKGdycGgub3h5Y2xpbmUpJHdlaWdodCkKIyBwZXJjZW50IG9mIG5lZyBlZGdlcyAKKHN1bShFKGdycGgub3h5Y2xpbmUpJHdlaWdodDwwKS9sZW5ndGgoRShncnBoLm94eWNsaW5lKSR3ZWlnaHQpKSoxMDAKCiMgdG90YWwgbnVtYmVyIG9mIGVkZ2VzIGluIGFub3hpYyBuZXR3b3JrCmxlbmd0aChFKGdycGguYW5veGljKSR3ZWlnaHQpCiMgcGVyY2VudCBvZiBuZWcgZWRnZXMgCihzdW0oRShncnBoLmFub3hpYykkd2VpZ2h0PDApL2xlbmd0aChFKGdycGguYW5veGljKSR3ZWlnaHQpKSoxMDAKCmBgYAoKRGVjbGluaW5nIG51bWJlciBvZiB0b3RhbCBlZGdlcyBnb2luZyBmcm9tIGZ1bGwgZGF0YXNldCAtLT4gb3h5Y2xpbmUgb25seSAtLT4gYW5veGljIG9ubHkuIEJ1dCB0aGUgcGVyY2VudGFnZSBvZiBuZWdhdGl2ZSBhc3NvY2lhdGlvbnMgaXMgc2ltaWxhciAoMzQuNC0zNy43JSkuIE1vc3QgYXNzb2NpYXRpb25zICh+NjUlKSBpbiBlYWNoIG5ldHdvcmsgYXJlICpwb3NpdGl2ZSouCgojIyMjIEVkZ2UgZGVuc2l0eQp0aGUgbnVtYmVyIG9mIGVkZ2VzIHJlbGF0aXZlcyB0byB0b3RhbCBudW1iZXIgb2YgcG9zc2libGUgZWRnZXMKYGBge3J9CmVkZ2VfZGVuc2l0eShncnBoKSoxMDAKZWRnZV9kZW5zaXR5KGdycGguMmRvbWFpbnMpKjEwMAplZGdlX2RlbnNpdHkoZ3JwaC5veHljbGluZSkqMTAwCmVkZ2VfZGVuc2l0eShncnBoLmFub3hpYykqMTAwCmBgYApUaGUgZnVsbCBkYXRhc2V0IGhhcyB0aGUgaGlnaGVzdCBlZGdlIGRlbnNpdHksIHRoZW4gb3h5Y2xpbmUsIHRoZW4gYW5veGljCgojIyMjIENvbXBvbmVudApUaGUgc2l6ZSBvZiB0aGUgY29tcG9uZW50cywgb3IgImNsdW1wcywiIGluIHRoZSBuZXR3b3JrLCBhbmQgaG93IG1hbnkgbWVtYmVycyBpbiBlYWNoCmBgYHtyfQojIGZ1bGwgZGF0YXNldApjb21wb25lbnRzKGdycGgpJG5vCmNvbXBvbmVudHMoZ3JwaCkkY3NpemUKCiMgMiBkb21haW5zCmNvbXBvbmVudHMoZ3JwaC4yZG9tYWlucykkbm8KY29tcG9uZW50cyhncnBoLjJkb21haW5zKSRjc2l6ZQoKIyBveHljbGluZQpjb21wb25lbnRzKGdycGgub3h5Y2xpbmUpJG5vCmNvbXBvbmVudHMoZ3JwaC5veHljbGluZSkkY3NpemUKCiMgYW5veGljCmNvbXBvbmVudHMoZ3JwaC5hbm94aWMpJG5vCmNvbXBvbmVudHMoZ3JwaC5hbm94aWMpJGNzaXplCmBgYAoKVGhlIGFub3hpYyBuZXR3b3JrIGlzIG1vc3QgZGlzam9pbnRlZCwgd2l0aCA0OCBjbHVtcHMgYW5kIHRoZSBsYXJnZXN0IGNvbnRhaW5pbmcgb25seSAyNCBtZW1iZXJzLiBUaGUgbmV4dCBpcyBveHljbGluZSwgd2l0aCAzMiBjbHVtcHMgYW5kIHRoZSBsYXJnZXN0IHdpdGggMTQ0IG1lbWJlcnMuIFRoZW4gdGhlIGZ1bGwgZGF0YXNldCBoYXMgb25seSAyNyBjbHVtcHMgYW5kIHRoZSBsYXJnZXN0IGNsdW1wIGNvbnRhaW5zIDI2MiBtZW1iZXJzLgoKCgojIyMjIEF2ZXJhZ2UgcGF0aCBsZW5ndGgKUGF0aCBpcyB0aGUgc2hvcnRlc3QgZGlzdGFuY2UgYmV0d2VlbiB0d28gbm9kZXMgKGZld2VzdCBudW1iZXIgb2YgZWRnZXMpLiBBdmVyYWdlIHBhdGggbGVuZ3RoIG9mIGEgbmV0d29yayBnaXZlcyBhIHNlbnNlIG9mIGhvdyBjb25uZWN0ZWQgZXZlcnkgbm9kZSBpcyB0byBhbm90aGVyLiBVbmNvbm5lY3RlZCBodWJzIGluIHRoZSBuZXRvd3JrIHdpbGwgaGF2ZSAiaW5maW5pdGUiIHBhdGhzIGZyb20gb3RoZXIgaHVicy4gVGhlIGZ1bmN0aW9uIGBtZWFuX2Rpc3RhbmNlYCBpZ25vcmVzIHRoZSBpbmZpbml0ZSBlZGdlcyBhbmQgY2FsY3VsYXRlcyB0aGUgYXZlcmFnZSBvZiBhbGwgb3RoZXIgZWRnZXMKYGBge3J9Cm1lYW5fZGlzdGFuY2UoZ3JwaCkKbWVhbl9kaXN0YW5jZShncnBoLjJkb21haW5zKQptZWFuX2Rpc3RhbmNlKGdycGgub3h5Y2xpbmUpCm1lYW5fZGlzdGFuY2UoZ3JwaC5hbm94aWMpCmBgYApUaGUgbG9uZ2VzdCBhdmVyYWdlIHBhdGggbGVuZ3RoIGlzIGluIHRoZSBveHljbGluZSwgZm9sbG93ZWQgYnkgdGhlIHdob2xlIGRhdGFzZXQgYW5kIHRoZW4gYW5veGljLiBNZWFuaW5nIHRoZSBub2RlcyBpbiB0aGUgYW5veGljIGFyZSBtb3JlIGNsb3NlbHkgYXNzb2NpYXRlZCB3aXRoIGVhY2ggb3RoZXIuIEV2ZW4gdGhvdWdoIHRoZXJlIGFyZSBtb3JlIGh1YnMgaW4gYW5veGljLCBhcyBzaG93biBhYm92ZSwgdGhlIG5vZGVzIGluIHRoZSBodWJzIGFyZSBjbG9zZSB0byBlYWNoIG90aGVyLiBUaGUgb3h5Y2xpbmUgaHVicyBoYXZlIHRoZSBsb25nZXN0IGF2ZXJhZ2UgZGlzdGFuY2VzIGJldHdlZW4gbm9kZXMuCgoKCiMjIyBXaGljaCBBU1ZzIGFyZSBpbiBwb3NpdGl2ZSB2cyBuZWdhdGl2ZSBuZXR3b3Jrcz8KYGBge3J9CiMgUG9zaXRpdmUgbmV0d29yay0gZnVsbCBkYXRhc2V0CmdycGgucG9zX2RmIDwtIGlncmFwaDo6YXNfZGF0YV9mcmFtZShncnBoLnBvcywgJ2JvdGgnKQpncnBoLnBvc19kZl92ZXJ0IDwtIGdycGgucG9zX2RmJHZlcnRpY2VzCiMgSG93IG1hbnkgU3luZGluaWFsZXMgYW5kIFBvbHljeXN0aW5lYT8KYXMuZGF0YS5mcmFtZSh0YWJsZShncnBoLnBvc19kZl92ZXJ0JCJ0YXhvbm9teS01IikpCiMgMzcgRGluby1Hcm91cC1JSQojIDMgRGluby1Hcm91cC1JCiMgMzQgU3B1bWVsbGFyaWRhCgojIE5lZ2F0aXZlIG5ldHdvcmstIGZ1bGwgZGF0YXNldApncnBoLm5lZ19kZiA8LSBpZ3JhcGg6OmFzX2RhdGFfZnJhbWUoZ3JwaC5uZWcsICdib3RoJykKZ3JwaC5uZWdfZGZfdmVydCA8LSBncnBoLm5lZ19kZiR2ZXJ0aWNlcwojIEhvdyBtYW55IFN5bmRpbmlhbGVzIGFuZCBQb2x5Y3lzdGluZWE/CmFzLmRhdGEuZnJhbWUodGFibGUoZ3JwaC5uZWdfZGZfdmVydCQidGF4b25vbXktNSIpKQojIDE4IERpbm8tR3JvdXAtSUkKIyA0IERpbm8tR3JvdXAtSQojIDE3IFNwdW1lbGxhcmlkYQoKCgojIFBvc2l0aXZlIG5ldHdvcmstIG94eWNsaW5lCmdycGgub3h5Y2xpbmUucG9zX2RmIDwtIGlncmFwaDo6YXNfZGF0YV9mcmFtZShncnBoLm94eWNsaW5lLnBvcywgJ2JvdGgnKQpncnBoLm94eWNsaW5lLnBvc19kZl92ZXJ0IDwtIGdycGgub3h5Y2xpbmUucG9zX2RmJHZlcnRpY2VzCiMgSG93IG1hbnkgU3luZGluaWFsZXMgYW5kIFBvbHljeXN0aW5lYT8KYXMuZGF0YS5mcmFtZSh0YWJsZShncnBoLm94eWNsaW5lLnBvc19kZl92ZXJ0JCJ0YXhvbm9teS01IikpCiMgMzQgRGluby1Hcm91cC1JSQojIDEgRGluby1Hcm91cC1JCiMgMjQgU3B1bWVsbGFyaWRhCgojIE5lZ2F0aXZlIG5ldHdvcmstIG94eWNsaW5lCmdycGgub3h5Y2xpbmUubmVnX2RmIDwtIGlncmFwaDo6YXNfZGF0YV9mcmFtZShncnBoLm94eWNsaW5lLm5lZywgJ2JvdGgnKQpncnBoLm94eWNsaW5lLm5lZ19kZl92ZXJ0IDwtIGdycGgub3h5Y2xpbmUubmVnX2RmJHZlcnRpY2VzCiMgSG93IG1hbnkgU3luZGluaWFsZXMgYW5kIFBvbHljeXN0aW5lYT8KYXMuZGF0YS5mcmFtZSh0YWJsZShncnBoLm94eWNsaW5lLm5lZ19kZl92ZXJ0JCJ0YXhvbm9teS01IikpCiMgMjQgRGluby1Hcm91cC1JSQojIDEgRGluby1Hcm91cC1JCiMgOSBTcHVtZWxsYXJpZGEKCgoKIyBQb3NpdGl2ZSBuZXR3b3JrLSBhbm94aWMKZ3JwaC5hbm94aWMucG9zX2RmIDwtIGlncmFwaDo6YXNfZGF0YV9mcmFtZShncnBoLmFub3hpYy5wb3MsICdib3RoJykKZ3JwaC5hbm94aWMucG9zX2RmX3ZlcnQgPC0gZ3JwaC5hbm94aWMucG9zX2RmJHZlcnRpY2VzCiMgSG93IG1hbnkgU3luZGluaWFsZXMgYW5kIFBvbHljeXN0aW5lYT8KYXMuZGF0YS5mcmFtZSh0YWJsZShncnBoLmFub3hpYy5wb3NfZGZfdmVydCQidGF4b25vbXktNSIpKQojIDAgRGluby1Hcm91cC1JSQojIDEgRGluby1Hcm91cC1JCiMgOCBTcHVtZWxsYXJpZGEKCiMgTmVnYXRpdmUgbmV0d29yay0gYW5veGljCmdycGguYW5veGljLm5lZ19kZiA8LSBpZ3JhcGg6OmFzX2RhdGFfZnJhbWUoZ3JwaC5hbm94aWMubmVnLCAnYm90aCcpCmdycGguYW5veGljLm5lZ19kZl92ZXJ0IDwtIGdycGguYW5veGljLm5lZ19kZiR2ZXJ0aWNlcwojIEhvdyBtYW55IFN5bmRpbmlhbGVzIGFuZCBQb2x5Y3lzdGluZWE/CmFzLmRhdGEuZnJhbWUodGFibGUoZ3JwaC5hbm94aWMubmVnX2RmX3ZlcnQkInRheG9ub215LTUiKSkKIyAwIERpbm8tR3JvdXAtSUkKIyAwIERpbm8tR3JvdXAtSQojIDAgU3B1bWVsbGFyaWRhCmBgYAoKIyMjIFdoYXQgYXJlIHRoZSBpbnRlcmVzdGluZWQgZXVrYXJ5b3RpYyBncm91cHMgY29ubmVjdGVkIHRvPwpgYGB7cn0KIyBQb3NpdGl2ZSBhc3NvY2lhdGlvbnM6IFN5bmRpbmlhbGVzCiMgUHVsbCBvdXQgbmFtZXMgb2YgU3luZGluaWFsZXMgYW5kIFNwdW1lbGxhcmlkYSBBU1ZzCmdycGgucG9zX2RmX3ZlcnRfc3luZCA8LSBmaWx0ZXIoZ3JwaC5wb3NfZGZfdmVydCwgYHRheG9ub215LTRgID09ICJTeW5kaW5pYWxlcyIpCiMgZmlsdGVyIGdyYXBoIHRvIGluY2x1ZGUgb25seSBlZGdlcyBjb25uZWN0ZWQgdG8gdGhvc2Ugbm9kZXMKZ3JwaC5wb3Nfc3luZF9lZGdlcyA8LSBFKGdycGgucG9zKVtmcm9tKGdycGgucG9zX2RmX3ZlcnRfc3luZCRuYW1lKV0gIyBnZXQgZWRnZXMKZ3JwaC5wb3Muc3luZF9zdWJncmFwaCA8LSBzdWJncmFwaC5lZGdlcyhncnBoLnBvcywgZ3JwaC5wb3Nfc3luZF9lZGdlcykgIyBmaWx0ZXIgZ3JhcGgKIyBnZXQgdGF4b25vbXkgb2YgcmVtYWluaW5nIG5vZGVzLCByZW1vdmluZyB0aGUgU3luZGluaWFsZXMgZnJvbSB0YWJsZSAoZWcuIG9ubHkgY29ubmVjdGVkIG5vZGVzKSBhbmQgZ3JvdXBpbmcgYnkgdGF4b25vbXkKZ3JwaC5wb3NfZGZfdmVydCAlPiUKICBmaWx0ZXIoYG5hbWVgICVpbiUgVihncnBoLnBvcy5zeW5kX3N1YmdyYXBoKSRuYW1lICYgIWB0YXhvbm9teS00YCAlaW4lIGMoIlN5bmRpbmlhbGVzIikpICU+JQogIGNvdW50KGB0YXhvbm9teS0yYCxgdGF4b25vbXktM2AsYHRheG9ub215LTRgLGB0YXhvbm9teS01YCwgbmFtZSA9ICJjb3VudCIsIHNvcnQgPSBUUlVFKQoKIyBQb3NpdGl2ZSBhc3NvY2lhdGlvbnM6IFNwdW1lbGxhcmlkYQpncnBoLnBvc19kZl92ZXJ0X3NwdW0gPC0gZmlsdGVyKGdycGgucG9zX2RmX3ZlcnQsIGB0YXhvbm9teS01YCA9PSAiU3B1bWVsbGFyaWRhIikKZ3JwaC5wb3Nfc3B1bV9lZGdlcyA8LSBFKGdycGgucG9zKVtmcm9tKGdycGgucG9zX2RmX3ZlcnRfc3B1bSRuYW1lKV0gCmdycGgucG9zLnNwdW1fc3ViZ3JhcGggPC0gc3ViZ3JhcGguZWRnZXMoZ3JwaC5wb3MsIGdycGgucG9zX3NwdW1fZWRnZXMpIApncnBoLnBvc19kZl92ZXJ0ICAlPiUgCiAgZmlsdGVyKGBuYW1lYCAlaW4lIFYoZ3JwaC5wb3Muc3B1bV9zdWJncmFwaCkkbmFtZSAmICFgdGF4b25vbXktNWAgJWluJSBjKCJTcHVtZWxsYXJpZGEiKSkgJT4lCiAgY291bnQoYHRheG9ub215LTJgLGB0YXhvbm9teS0zYCxgdGF4b25vbXktNGAsYHRheG9ub215LTVgLCBuYW1lID0gImNvdW50Iiwgc29ydCA9IFRSVUUpCgojIE5lZ2F0aXZlIGFzc29jaWF0aW9uczogU3luZGluaWFsZXMKZ3JwaC5uZWdfZGZfdmVydF9zeW5kIDwtIGZpbHRlcihncnBoLm5lZ19kZl92ZXJ0LCBgdGF4b25vbXktNGAgPT0gIlN5bmRpbmlhbGVzIikKZ3JwaC5uZWdfc3luZF9lZGdlcyA8LSBFKGdycGgubmVnKVtmcm9tKGdycGgubmVnX2RmX3ZlcnRfc3luZCRuYW1lKV0gCmdycGgubmVnLnN5bmRfc3ViZ3JhcGggPC0gc3ViZ3JhcGguZWRnZXMoZ3JwaC5uZWcsIGdycGgubmVnX3N5bmRfZWRnZXMpIApncnBoLm5lZ19kZl92ZXJ0ICU+JQogIGZpbHRlcihgbmFtZWAgJWluJSBWKGdycGgubmVnLnN5bmRfc3ViZ3JhcGgpJG5hbWUgJiAhYHRheG9ub215LTRgICVpbiUgYygiU3luZGluaWFsZXMiKSkgJT4lCiAgY291bnQoYHRheG9ub215LTJgLGB0YXhvbm9teS0zYCxgdGF4b25vbXktNGAsYHRheG9ub215LTVgLCBuYW1lID0gImNvdW50Iiwgc29ydCA9IFRSVUUpCgojIE5lZ2F0aXZlIGFzc29jaWF0aW9uczogU3B1bWVsbGFyaWRhCmdycGgubmVnX2RmX3ZlcnRfc3B1bSA8LSBmaWx0ZXIoZ3JwaC5uZWdfZGZfdmVydCwgYHRheG9ub215LTVgID09ICJTcHVtZWxsYXJpZGEiKQpncnBoLm5lZ19zcHVtX2VkZ2VzIDwtIEUoZ3JwaC5uZWcpW2Zyb20oZ3JwaC5uZWdfZGZfdmVydF9zcHVtJG5hbWUpXSAKZ3JwaC5uZWcuc3B1bV9zdWJncmFwaCA8LSBzdWJncmFwaC5lZGdlcyhncnBoLm5lZywgZ3JwaC5uZWdfc3B1bV9lZGdlcykgCmdycGgubmVnX2RmX3ZlcnQgJT4lIAogIGZpbHRlcihgbmFtZWAgJWluJSBWKGdycGgubmVnLnNwdW1fc3ViZ3JhcGgpJG5hbWUgJiAhYHRheG9ub215LTVgICVpbiUgYygiU3B1bWVsbGFyaWRhIikpICU+JQogIGNvdW50KGB0YXhvbm9teS0yYCxgdGF4b25vbXktM2AsYHRheG9ub215LTRgLGB0YXhvbm9teS01YCwgbmFtZSA9ICJjb3VudCIsIHNvcnQgPSBUUlVFKQoKCiMgQWxzbyBja2VjayBvdXQgQ2FyaWFjb3RyaWNoZWEgdG8gaHlwb3RoZXNpemUgYWJvdXQgcG9zc2libGUgc3ltYmlvc2lzIHBhcnRuZXJzCiMgUG9zaXRpdmUgYXNzb2NpYXRpb25zIG9ubHkKZ3JwaC5wb3NfZGZfdmVydF9jYXJpIDwtIGZpbHRlcihncnBoLnBvc19kZl92ZXJ0LCBgdGF4b25vbXktNGAgPT0gIkNhcmlhY290cmljaGVhIikKZ3JwaC5wb3NfY2FyaV9lZGdlcyA8LSBFKGdycGgucG9zKVtmcm9tKGdycGgucG9zX2RmX3ZlcnRfY2FyaSRuYW1lKV0gCmdycGgucG9zLmNhcmlfc3ViZ3JhcGggPC0gc3ViZ3JhcGguZWRnZXMoZ3JwaC5wb3MsIGdycGgucG9zX2NhcmlfZWRnZXMpIApncnBoLnBvc19kZl92ZXJ0ICU+JSAKICBmaWx0ZXIoYG5hbWVgICVpbiUgVihncnBoLnBvcy5jYXJpX3N1YmdyYXBoKSRuYW1lICYgIWB0YXhvbm9teS00YCAlaW4lIGMoIkNhcmlhY290cmljaGVhIikpJT4lCiAgY291bnQoYHRheG9ub215LTJgLGB0YXhvbm9teS0zYCxgdGF4b25vbXktNGAsYHRheG9ub215LTVgLCBuYW1lID0gImNvdW50Iiwgc29ydCA9IFRSVUUpCmBgYAoKCgoKIyMjIEFuYWx5emUgbm9kZS1sZXZlbCBtZWFzdXJlcyBmb3Iga2V5c3RvbmUtbmVzcwpDYWxjdWxhdGUgNCBwYXJhbWV0ZXJzIGZvciBlYWNoIGluZGl2aWR1YWwgbm9kZToKCi0gZGVncmVlIChkZSkgPSBudW1iZXIgb2YgZWRnZXMgY29ubmVjdGVkIHRvIHRoZSBub2RlCi0gc3RyZW5ndGggKHN0KSA9IHN1bSBvZiB0aGUgd2VpZ2h0cyBvZiBhbGwgdGhlIGVkZ2VzIGNvbm5lY3RlZCB0byB0aGUgbm9kZQotIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkgKGJlKSA9IHRoZSBudW1iZXIgb2Ygc2hvcnRlc3QgcGF0aHMgKGJldHdlZW4gYWxsIG90aGVyIG5vZGVzKSB0aGF0IGdvIHRocm91Z2ggdGhlIG5vZGUKLSBjbG9zZW5lc3MgY2VudHJhbGl0eSAoY2MpID0gYXZlcmFnZSBkaXN0YW5jZSAobnVtYmVyIG9mIGVkZ2VzKSBvZiB0aGUgbm9kZSB0byBhbnkgb3RoZXIgbm9kZQotIGxvY2FsIGNsdXN0ZXJpbmcgY29lZmZpY2llbnQgKGwuY2x1c3RlcikgLCBha2EgdHJhbnNpdGl2aXR5ID0gdGhlIHByb3BvcnRpb24gb2YgdHdvIG5vZGVzIHRoYXQgYXJlIGNvbm5lY3RlZCBieSBhIHRoaXJkIG5vZGUsIG9mIGJlaW5nIGNvbm5lY3RlZCB0byBlYWNoIG90aGVyIChlZy4gaG93IG1hbnkgb2YgYSBub2RlJ3MgImZyaWVuZHMiIGtub3cgZWFjaCBvdGhlcj8pCgoKYGBge3J9CiMgRmlyc3QgY2hhbmdlIHRoZSB3ZWlnaHRzIG9mIHRoZSBlZGdlcyAodGhlIHN0cmVuZ3RoIG9mIGFzc29jaWF0aW9uKSB0byBhYnNvbHV0ZSB2YWx1ZS4gVGhpcyB3b24ndCB3b3JrIGlmIG5lZ2F0aXZlIGVkZ2Ugd2VpZ2h0cyBhcmUgbGVmdCB3aXRoIHRoZSBuZWdhdGl2ZSBzaWducwpFKGdycGgpJHdlaWdodCA8LSBhYnMoRShncnBoKSR3ZWlnaHQpCgojIGNhbGN1bGF0ZSBwYXJhbWV0ZXJzCm5hbWVzPVYoZ3JwaCkkbmFtZQpkZT1kZWdyZWUoZ3JwaCkKc3Q9Z3JhcGguc3RyZW5ndGgoZ3JwaCkKYmU9YmV0d2Vlbm5lc3MoZ3JwaCwgbm9ybWFsaXplZD1UKQpjYyA9IGNsb3NlbmVzcyhncnBoKQpsLmNsdXN0ZXI9dHJhbnNpdGl2aXR5KGdycGgsICJsb2NhbCIpCgoKIyBhc3NlbWJsZSBkYXRhc2V0IGFuZCBtYXRjaCBmdWxsIHRheG9ub215CmZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXMgPC0gZGF0YS5mcmFtZShJRD1uYW1lcywgZGVncmVlPWRlLCBzdHJlbmd0aD1zdCwgYmV0d2Vlbm5lc3M9YmUsIGNsb3NlbmVzcyA9IGNjLCBjbHVzdGVyaW5nX2NvZWZmaWNpZW50ID0gbC5jbHVzdGVyKSAKCiMgUHV0IGJhY2sgYmFjIHRheGFvbm9teQp0ZW1wMSA8LSBsZWZ0X2pvaW4oZnVsbGRhdGVzZXRfbm9kZV9tZWFzdXJlc1sxOmRpbShvdHVfdGFibGUocHNfYmFjX3BydW5lZF8zZG9tYWlucykpWzFdLF0sIGJhY190YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI09UVSBJRCIpKSAKIyBkZWxldGUgIlRheG9ub215LTkiIGFuZCAicmVmaW5lZCBUYXhvbm9teSIgY29sdW1ucyAKdGVtcDEgPC0gc2VsZWN0KHRlbXAxLCAtInRheG9ub215LTkiLCAtIlJlZmluZWQgdGF4b25vbXkiKQoKCnRlbXAyIDwtIGxlZnRfam9pbihmdWxsZGF0ZXNldF9ub2RlX21lYXN1cmVzW3N1bShkaW0ob3R1X3RhYmxlKHBzX2JhY19wcnVuZWRfM2RvbWFpbnMpKVsxXSwxKTpzdW0oZGltKG90dV90YWJsZShwc19iYWNfcHJ1bmVkXzNkb21haW5zKSlbMV0sZGltKG90dV90YWJsZShwc19hcmNoX3BydW5lZF8zZG9tYWlucykpWzFdKSxdLCBhcmNoX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjT1RVIElEIikpIAoKCnRlbXAzIDwtIGxlZnRfam9pbihmdWxsZGF0ZXNldF9ub2RlX21lYXN1cmVzW3N1bShkaW0ob3R1X3RhYmxlKHBzX2FyY2hfcHJ1bmVkXzNkb21haW5zKSlbMV0sIGRpbShvdHVfdGFibGUocHNfYmFjX3BydW5lZF8zZG9tYWlucykpWzFdLDEpOnN1bShkaW0ob3R1X3RhYmxlKHBzX2FyY2hfcHJ1bmVkXzNkb21haW5zKSlbMV0sIGRpbShvdHVfdGFibGUocHNfYmFjX3BydW5lZF8zZG9tYWlucykpWzFdLGRpbShvdHVfdGFibGUocHNfZXVrX3BydW5lZF8zZG9tYWlucykpWzFdKSxdLCBldWtfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNBU1YgSUQiKSkgCiMgUmVuYW1lIGNvbCBuYW1lcyB0byBtYXRjaCB0aG9zZSBmcm9tIEJhYyBhbmQgQXJjaAp0ZW1wMyA8LSB0ZW1wMyAlPiUKICByZW5hbWUoInRheG9ub215LTEiID0gS2luZ2RvbSwgInRheG9ub215LTIiID0gU3VwZXJncm91cCwgInRheG9ub215LTMiID0gRGl2aXNpb24sICJ0YXhvbm9teS00IiA9IENsYXNzLCAidGF4b25vbXktNSIgPSBPcmRlciwgInRheG9ub215LTYiID0gRmFtaWx5LCAidGF4b25vbXktNyIgPSBHZW51cywgInRheG9ub215LTgiID0gU3BlY2llcykKCiMgY29tYmluZSBiYWNrIGFsbCAzIGRvbWFpbnMsIHdpdGggbmV3IG5hbWVzIGFzIHJvdyBuYW1lcyBpbiBhIGRhdGFmcmFtZQpmdWxsZGF0ZXNldF9ub2RlX21lYXN1cmVzIDwtIHJiaW5kKHRlbXAxLCB0ZW1wMiwgdGVtcDMpCmZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXMKYGBgCgpQbG90IGJldHdlZW5lc3MgdnMgZGVncmVlIGZvciBlYWNoIG5vZGUuIAotIFRpcHRvbiBldCBhbC4gYXJndWUgdGhhdCBub2RlcyB3aXRoIGhpZ2ggYmV0d2Vlbm5lc3MgYXJlICJib3R0bGVuZWNrcyIgb3IgaW1wb3J0YW50IGNvbm5lY3RvcnMgYW5kIG5vZGVzIHdpdGggaGlnaCBkZWdyZWUgYXJlICJodWJzIgotIEJlcnJ5IGV0IGFsLiBhcmd1ZSB0aGF0IG5vZGVzIHdpdGggbG93IGJldHdlZW5uZXNzLCBoaWdoIGRlZ3JlZSwgaGlnaCBjbG9zZW5lc3MsIGFuZCBoaWdoIHRyYW5zaXRpdml0eSBhcmUgY2FuZGlkYXRlIGtleXN0b25lIHNwZWNpZXMKICAtIEFkZCBpbiBjbG9zZW5lc3MgaW50byB0aGUgbm9kZSdzIHBsb3RseSBsYWJlbCBzaW5jZSB0aGVzZSBkb24ndCB2YXJ5IG11Y2ggbm9kZS10by1ub2RlIGFuZCB3b3VsZG4ndCBtYWtlIHNlbnNlIHRvIHBsb3QKYGBge3J9CiMgcmVwbGFjZSBOQSBpbiB0YXhvbm9teSB3aXRoIHVuaWRlbnRpZmllZCAKIyByZW1vdmUgbm9kZXMgd2l0aCAwIGJldHdlZW5uZXNzIChjYW4ndCBjYWxjdWxhdGUgbG9nMTAgb2YgMCkKIyByZXBsYWNlIE5hTiBjbHVzdGVyaW5nIGNvZWZzIHdpdGggMApmdWxsZGF0ZXNldF9ub2RlX21lYXN1cmVzIDwtIGZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXMgJT4lIAogIHJlcGxhY2UoaXMubmEoLiksICJ1bmlkZW50aWZpZWQiKSAlPiUKICBmaWx0ZXIoIWJldHdlZW5uZXNzID09IDApIAoKIyBnZXQgZW5vdWdoIGNvbG9ycyBhbmQgcmFuZG9tbHkgcmVhcnJhbmdlIHNvIHRoZXkgYXJlIGVhc2llciB0byBzZXBhcmF0ZSBvbiB0aGUgcGxvdApteWNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoMTIsICJQYWlyZWQiKSkobGVuZ3RoKHVuaXF1ZShmdWxsZGF0ZXNldF9ub2RlX21lYXN1cmVzJGB0YXhvbm9teS0zYCkpKQpzZXQuc2VlZCgxMjMpCm15Y29sb3JzIDwtICBzYW1wbGUobXljb2xvcnMpCgojIHBsb3Qgd2l0aCBwbG90bHkgYW5kIHNvIEkgY2FuIGhvdmVyIG92ZXIgcG9pbnRzIGFuZCBkZXRlcm1pbmUgd2hpY2ggdGF4YSB0aGV5IGFyZQpwIDwtIGdncGxvdChmdWxsZGF0ZXNldF9ub2RlX21lYXN1cmVzLCBhZXMoeCA9IGRlZ3JlZSwgeSA9IGJldHdlZW5uZXNzLCBJRCA9IElELCBzaGFwZSA9IGB0YXhvbm9teS0xYCwgYHRheG9ub215LTJgID0gYHRheG9ub215LTJgLCBjb2xvciA9IGB0YXhvbm9teS0zYCwgYHRheG9ub215LTRgID0gYHRheG9ub215LTRgLCBgdGF4b25vbXktNWAgPSBgdGF4b25vbXktNWApKSArCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKwogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IG15Y29sb3JzKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWVfYncoKQpwCmdncGxvdGx5KHAsIHRvb2x0aXAgPSBjKCJJRCIsInRheG9ub215LTIiLCAidGF4b25vbXktMyIsICJ0YXhvbm9teS00IiwgInRheG9ub215LTUiKSkKCmBgYAoKTWFrZSBzdGF0aWMgZmlndXJlIGZvciBtYW51c2NyaXB0CmBgYHtyfQoKcDIgPC0gZ2dwbG90KGZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXMsIGFlcyh4ID0gZGVncmVlLCB5ID0gYmV0d2Vlbm5lc3MsIHNoYXBlID0gYHRheG9ub215LTFgLCBjb2xvciA9IGB0YXhvbm9teS0zYCkpICsKICBnZW9tX3BvaW50KHNpemUgPSA0KSArCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXljb2xvcnMsIG5hbWUgPSAiIikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE5LDE3LDE1LDE4KSwgbmFtZSA9ICIiKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTgsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgbGVnZW5kLm1hcmdpbj1tYXJnaW4oMCwwLDAsMiksCiAgICAgICAgbGVnZW5kLmJveC5tYXJnaW49bWFyZ2luKC0xMCwtMTAsLTEwLC0xMCksCiAgICAgICAgcGxvdC5tYXJnaW49Z3JpZDo6dW5pdChjKDAsMCwwLDApLCAibW0iKSkgKwogIHRoZW1lX2J3KCkgCnAyCgpnZ3NhdmUoImZpZ3VyZXMvYmV0d2Vlbm5lc3NfdnNfZGVncmVlLmVwcyIscDIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDYsIHVuaXRzID0gYygiaW4iKSkKCmBgYAoKCgogIAojIyMgSG93IG1hbnkgZWRnZXMgZG9lcyBlYWNoIGdyb3VwIGhhdmUgYXMgYW4gYWdncmVnYXRlCmBgYHtyfQpkZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGRlKQpkZV9kZiRuYW1lIDwtIHJvd25hbWVzKGRlX2RmKQpkZV9kZiA8LSBsZWZ0X2pvaW4oZGVfZGYsIGFsbF90ZW1wLCBieSA9ICJuYW1lIikKCmRlX2RmICU+JQogIGdyb3VwX2J5KGB0YXhvbm9teS0xYCkgJT4lCiAgc3VtbWFyaXNlKGRlZ3JlZXN1bSA9IHN1bShkZSkpICU+JQogIGFycmFuZ2UoZGVzYyhkZWdyZWVzdW0pKQoKZGVfZGYgJT4lCiAgZ3JvdXBfYnkoYHRheG9ub215LTJgKSAlPiUKICBzdW1tYXJpc2UoZGVncmVlc3VtID0gc3VtKGRlKSkgJT4lCiAgYXJyYW5nZShkZXNjKGRlZ3JlZXN1bSkpCgpkZV9kZiAlPiUKICBncm91cF9ieShgdGF4b25vbXktM2ApICU+JQogIHN1bW1hcmlzZShkZWdyZWVzdW0gPSBzdW0oZGUpKSAlPiUKICBhcnJhbmdlKGRlc2MoZGVncmVlc3VtKSkKCmRlX2RmICU+JQogIGdyb3VwX2J5KGB0YXhvbm9teS00YCkgJT4lCiAgc3VtbWFyaXNlKGRlZ3JlZXN1bSA9IHN1bShkZSkpICU+JQogIGFycmFuZ2UoZGVzYyhkZWdyZWVzdW0pKQoKZGVfZGYgJT4lCiAgZ3JvdXBfYnkoYHRheG9ub215LTVgKSAlPiUKICBzdW1tYXJpc2UoZGVncmVlc3VtID0gc3VtKGRlKSkgJT4lCiAgYXJyYW5nZShkZXNjKGRlZ3JlZXN1bSkpCgpgYGAKCgogIAojIyMgU2F2ZSBhbmQgcmUtbG9hZCBlbnZpcm9ubWVudApgYGB7cn0KIyBzYXZlLmltYWdlKCJFbnZpcm9ubWVudEJhY2t1cHMvQ2FyaWFjb0V1a3NfcG9zdGFuYWx5c2lzX3ZhcnNfdXB0b19ub2RlbGV2ZWxtZWFzdXJlcy5SRGF0YSIpCmBgYAoKT3IgbG9hZCBpZiBjb21pbmcgYmFjawpgYGB7cn0KbG9hZCgiRW52aXJvbm1lbnRCYWNrdXBzL0NhcmlhY29FdWtzX3Bvc3RhbmFseXNpc192YXJzX3VwdG9fbm9kZWxldmVsbWVhc3VyZXMuUkRhdGEiKQpgYGAK