Read in phyloseq object
ps2 <- readRDS("output/rds/ps2.rds")
# export filtered
dir.create("output/otu_tables/filtered")
seqateurs::summarise_taxa(ps2, "species", "SampleID") %>%
spread(key="SampleID", value="totalRA") %>%
write.csv(file = "output/otu_tables/filtered/filtered_spp_sum.csv")
seqateurs::summarise_taxa(ps2, "genus", "SampleID") %>%
spread(key="SampleID", value="totalRA") %>%
write.csv(file = "output/otu_tables/filtered/filtered_gen_sum.csv")
#Rename taxa - only keep first 30 characters
taxa_names(ps2) <- substr(paste0("SV", seq(ntaxa(ps2)),"-",tax_table(ps2)[,7]), 1,30)
#Check carsonella presence
cars <- speedyseq::psmelt(ps2) %>%
filter(Abundance > 0) %>%
group_by(psyllid_spp) %>%
summarise(n = count(genus=="Candidatus Carsonella", na.rm = TRUE))
Create species merged table
# Merge species for beta diversity
ps.sppmerged <- ps2 %>%
merge_samples(group = "psyllid_spp", fun=mean)
#This loses the sample metadata - Need to add it agian
sample_data(ps.sppmerged) <- sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
filter(!duplicated(psyllid_spp)) %>%
magrittr::set_rownames(.$psyllid_spp)
seqs <- refseq(ps2)
tree <- phy_tree(ps2)
#make new phyloseq object
ps3 <- phyloseq(tax_table(ps.sppmerged),
sample_data(ps.sppmerged),
otu_table(otu_table(ps.sppmerged), taxa_are_rows = FALSE),
refseq(seqs),
phy_tree(tree))
Read in psyllid phylogeny
psyllid_tree <- read.tree(text=readLines("sample_data/psyllid_beast_tree.nwk"))
# Match names with sample sheet
psyllid_tree$tip.label <- psyllid_tree$tip.label %>%
str_squish() %>%
str_replace_all(pattern="\\.", replacement=" ") %>%
str_replace_all(pattern="Acizzia hakae", replacement="Acizzia hakeae") %>%
str_replace_all(pattern="POLLENISLAND", replacement="POLLEN ISLAND") %>%
str_replace_all(pattern="Ctenarytaina fuchsiae$", replacement="Ctenarytaina fuchsia A") %>%
str_replace_all(pattern="Ctenarytaina fuchsiaeB", replacement="Ctenarytaina fuchsia B") %>%
str_replace_all(pattern="Ctenarytaina fuchsiaeC", replacement="Ctenarytaina fuchsia C") %>%
str_replace_all(pattern="Ctenarytaina clavata", replacement="Ctenarytaina clavata sp ") %>%
str_replace_all(pattern="Ctenarytaina clavata sp $", replacement="Ctenarytaina clavata sp A") %>%
str_replace_all(pattern="Ctenarytaina sp$", replacement="Ctenarytaina sp ") %>%
str_replace_all(pattern="Ctenarytaina spA", replacement="Ctenarytaina sp A") %>%
str_replace_all(pattern="Ctenarytaina spB", replacement="Ctenarytaina sp B") %>%
str_replace_all(pattern="Ctenarytaina unknown", replacement="Ctenarytaina insularis") %>%
str_replace_all(pattern="Psylla apicalisA", replacement="Psylla frodobagginsi") %>%
str_replace_all(pattern="Psylla apicalisB", replacement="Psylla apicalis") %>%
str_replace_all(pattern="carmichaeliae", replacement="carmichaeliae ") %>%
str_replace_all(pattern="Trioza sp", replacement="Trioza sp ") %>%
str_replace_all(pattern="Trioza acutaB", replacement="Trioza acuta B") %>%
str_replace_all(pattern="Trioza gourlay", replacement="Trioza gourlayi") %>%
str_replace_all(pattern="BRENDAMAY", replacement="BRENDA MAY") %>%
str_replace_all(pattern="PRICES", replacement="PRICES VALLEY") %>%
str_replace_all(pattern="Acizzia sp", replacement="Acizzia errabunda") %>%
str_replace_all(pattern="Trioza ", replacement="Powellia ") %>%
str_replace_all(pattern="Triozid sp", replacement="Casuarinicola sp") %>%
str_replace_all(pattern="Powellia adventicia", replacement="Trioza adventicia") %>%
str_replace_all(pattern="Powellia curta", replacement="Trioza curta") %>%
str_replace_all(pattern=" ", replacement="_") %>%
trimws(which="right")
# Subset to only those in sample data
setdiff(psyllid_tree$tip.label,sample_data(ps2)$psyllid_spp)
setdiff(sample_data(ps2)$psyllid_spp, psyllid_tree$tip.label)
psyllid_tree$tip.label[!psyllid_tree$tip.label %in% sample_data(ps2)$psyllid_spp]
psyllid_tree$tip.label[!sample_data(ps2)$psyllid_spp %in% psyllid_tree$tip.label ]
pruned.tree <- drop.tip(psyllid_tree, psyllid_tree$tip.label[!psyllid_tree$tip.label %in% sample_data(ps2)$psyllid_spp] )
Summary statistics
# N unique species and samples
speedyseq::psmelt(ps2) %>%
summarise(ntaxa= n_distinct(psyllid_spp), n_samples = n_distinct(Sample_Name), n_hostplants = n_distinct(hostplant_spp))
# Spread of reads
speedyseq::psmelt(ps2) %>%
group_by(Sample_Name) %>%
summarise(Abundance = sum(Abundance)) %>%
ungroup() %>%
summarise(mean = mean(Abundance),
se = sd(Abundance)/sqrt(length(Abundance)),
max = max(Abundance),
min = min(Abundance))
# Spread of ASVs
speedyseq::psmelt(ps2) %>%
group_by(Sample_Name) %>%
dplyr::filter(Abundance > 0) %>%
summarise(counts = n_distinct(OTU)) %>%
ungroup() %>%
summarise(mean = mean(counts),
se = sd(counts)/sqrt(length(counts)),
max = max(counts),
min = min(counts))
#Fraction of reads assigned to each taxonomic rank
speedyseq::psmelt(ps2) %>%
gather("Rank","Name",rank_names(ps2)) %>%
group_by(Rank) %>%
mutate(Name = replace(Name, str_detect(Name, "__"),NA)) %>% # This line turns the "__" we added to lower ranks back to NA's
dplyr::summarise(Reads_classified = sum(Abundance * !is.na(Name))) %>%
mutate(Frac_reads = Reads_classified / sum(sample_sums(ps2))) %>%
mutate(Rank = factor(Rank, rank_names(ps2))) %>%
arrange(Rank)
#Fraction of ASV's assigned to each taxonomic rank
tax_table(ps2) %>%
as("matrix") %>%
as_tibble(rownames="OTU") %>%
gather("Rank","Name",rank_names(ps2)) %>%
group_by(Rank) %>%
mutate(Name = replace(Name, str_detect(Name, "__"), NA)) %>% # This line turns the "__" we added to lower ranks back to NA's
dplyr::summarise(OTUs_classified = sum(!is.na(Name))) %>%
mutate(Frac_OTUs = OTUs_classified / ntaxa(ps2)) %>%
mutate(Rank = factor(Rank, rank_names(ps2))) %>%
arrange(Rank)
# Unique taxa at each rank
speedyseq::psmelt(ps2) %>%
dplyr::select(rank_names(ps2)) %>%
pivot_longer(everything(),
names_to = "Rank",
values_to = "value") %>%
mutate(value = case_when(
str_detect(value, "__") ~ as.character(NA),
!str_detect(value, "__") ~ value
)) %>%
drop_na() %>%
group_by(Rank) %>%
summarise_all(funs(n_distinct)) %>%
mutate(Rank = factor(Rank, rank_names(ps2))) %>%
arrange(Rank)
# Each different phylum ranked by its overall relative abundance
sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
pull(psyllid_spp) %>%
table() %>%
sort()
# Transform to per sample relative abundance, then transform to whole dataset relative abundance
Prevalence / Abundance summary
View prevalence of different phyla across the dataset
# Calculate taxon prevalence across the data set at OTU level
prevdf <- apply(X = otu_table(ps2), MARGIN = ifelse(taxa_are_rows(ps2), yes = 1, no = 2), FUN = function(x){sum(x > 0)})
prevdf <- data.frame(Prevalence = prevdf,
TotalAbundance = taxa_sums(ps2),
tax_table(ps2))
#Prevalence plot
gg.prev <- subset(prevdf, phylum %in% get_taxa_unique(ps2, "phylum")) %>%
ggplot(aes(TotalAbundance, Prevalence / nsamples(ps2),color=order)) +
geom_point(size = 3, alpha = 0.7) +
scale_x_log10() +
xlab("Total Abundance") + ylab("Prevalence [Frac. Samples]") +
facet_wrap(~phylum) +
theme(legend.position="none") +
ggtitle("Phylum Prevalence in All Samples\nColored by Order")
pdf(file="figs/prevalence.pdf", width = 11, height = 8 , paper="a4r")
plot(gg.prev)
try(dev.off(), silent=TRUE)
# Prevalecne at phylum
ps.phylum <- speedyseq::tax_glom(ps2, taxrank="phylum")
prevdf_phylum <- apply(X = otu_table(ps.phylum ), MARGIN = ifelse(taxa_are_rows(ps.phylum ), yes = 1, no = 2), FUN = function(x){sum(x > 0)})
prevdf_phylum <- data.frame(Prevalence = prevdf_phylum,
TotalAbundance = taxa_sums(ps.phylum),
tax_table(ps.phylum)) %>%
dplyr::mutate(RA = TotalAbundance / sum(TotalAbundance)) %>%
remove_rownames() %>%
magrittr::set_rownames(.$phylum) %>%
dplyr::select(-rank_names(ps.phylum))
# Prevalence within Proteobacteria
ps.prot <- subset_taxa(ps2, phylum=="Proteobacteria") %>%
speedyseq::tax_glom(taxrank="order")
prevdf_prot <- apply(X = otu_table(ps.prot ), MARGIN = ifelse(taxa_are_rows(ps.prot ), yes = 1, no = 2), FUN = function(x){sum(x > 0)})
prevdf_prot <- data.frame(Prevalence = prevdf_prot,
TotalAbundance = taxa_sums(ps.prot),
tax_table(ps.prot)) %>%
dplyr::mutate(RA = TotalAbundance / sum(TotalAbundance)) %>%
remove_rownames() %>%
magrittr::set_rownames(.$order) %>%
dplyr::select(-rank_names(ps.prot))
# Genus Prevalence
ps.gen <- speedyseq::tax_glom(ps2, taxrank="genus")
prevdf_gen <- apply(X = otu_table(ps.gen ), MARGIN = ifelse(taxa_are_rows(ps.gen ), yes = 1, no = 2), FUN = function(x){sum(x > 0)})
prevdf_gen <- data.frame(Prevalence = prevdf_gen,
TotalAbundance = taxa_sums(ps.gen),
tax_table(ps.gen)) %>%
dplyr::mutate(RA = TotalAbundance / sum(TotalAbundance)) %>%
remove_rownames() %>%
mutate(genus = make.unique(genus)) %>%
magrittr::set_rownames(.$genus) %>%
dplyr::select(-rank_names(ps.gen))
# Genus Prevalence across species rather than specimens
ps.gen <- speedyseq::tax_glom(ps3, taxrank="genus")
prevdf_gen <- apply(X = otu_table(ps.gen ), MARGIN = ifelse(taxa_are_rows(ps.gen ), yes = 1, no = 2), FUN = function(x){sum(x > 0)})
prevdf_gen <- data.frame(Prevalence = prevdf_gen,
TotalAbundance = taxa_sums(ps.gen),
tax_table(ps.gen)) %>%
dplyr::mutate(RA = TotalAbundance / sum(TotalAbundance)) %>%
remove_rownames() %>%
mutate(genus = make.unique(genus)) %>%
magrittr::set_rownames(.$genus) %>%
dplyr::select(-rank_names(ps.gen))
# Prevalence of symbionts across psyllid species
speedyseq::psmelt(ps2) %>%
mutate(total_spp = n_distinct(psyllid_spp), total_specimen = n_distinct(Sample_Name)) %>%
filter(Abundance > 0) %>%
filter(genus %in% c("Candidatus Carsonella", "Arsenophonus", "Sodalis")) %>%
group_by(genus, total_spp, total_specimen)%>%
summarise(n_species = n_distinct(psyllid_spp), n_specimen = n_distinct(Sample_Name)) %>%
ungroup()%>%
mutate(prop_species = n_species / total_spp,
prop_specimen = n_specimen / total_specimen)
# Number of symbiont OTUs per psyllid species
speedyseq::psmelt(ps2) %>%
filter(Abundance > 0) %>%
filter(genus %in% c("Candidatus Carsonella", "Arsenophonus", "Sodalis")) %>%
group_by(psyllid_spp, genus) %>%
summarise(n = n_distinct(OTU)) %>%
ggplot(aes(x = psyllid_spp, y = n, fill=genus))+
geom_col(show.legend = FALSE)+
facet_grid(genus~.)+
theme(axis.text.x = element_text(angle=45, hjust=1)) +
labs(x = "Psyllid Species",
y = "Number of distinct ASVs")
se <- function(x) sqrt(var(x)/length(x))
# Mean abundance of genera
genera_abund <- speedyseq::psmelt(ps2) %>%
filter(Abundance > 0) %>%
group_by(SampleID) %>%
mutate_at(vars(Abundance), ~ . / sum(.) ) %>%
ungroup %>%
group_by(genus) %>%
summarise(mean_ra = mean(Abundance), upper = max(Abundance), lower = min(Abundance), se = se(Abundance))
Alpha diversity metrics
dir.create("output/alpha")
# Get richness measures
richness <- phyloseq::estimate_richness(ps2, measures=c("Shannon")) %>%
rownames_to_column("Sample_Name") %>%
mutate(Sample_Name = Sample_Name %>%
str_remove("^X") %>%
str_replace_all("\\.", " "))
#Set number of randomisations for calculating significance
# Calculate Faith's PD-index & Species richness - with Standard errors
#sespd <- picante::ses.pd(as(phyloseq::otu_table(ps2), "matrix"), phyloseq::phy_tree(ps2), null.model = "taxa.labels", include.root = F, runs = 99)
pd <- picante::pd(as(phyloseq::otu_table(ps2), "matrix"), phyloseq::phy_tree(ps2), include.root = FALSE)
# Join together
div_table <- pd %>%
rownames_to_column("Sample_Name") %>%
dplyr::select(Sample_Name, alpha = SR, pd = PD) %>%
left_join(richness, by="Sample_Name") %>%
left_join(sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
filter(!duplicated(Sample_Name)) %>%
dplyr::select(Sample_Name, psyllid_spp, psyllid_genus, psyllid_family, hostplant_spp, seqrun, genus_geo),
by = "Sample_Name")
# Summarise means
div_table %>%
summarise_if(is.numeric, mean)
# Difference between species for alpha diversity ANOVA
report::report(aov(alpha ~seqrun+psyllid_family+psyllid_genus+psyllid_spp, data=div_table))
report::report(aov(Shannon ~seqrun+psyllid_family+psyllid_genus+psyllid_spp, data=div_table))
report::report(aov(pd ~seqrun+psyllid_family+psyllid_genus+psyllid_spp, data=div_table))
# Difference between all genera for alpha diversity ANOVA
report::report(aov(alpha ~seqrun+psyllid_family+psyllid_genus, data=div_table))
report::report(aov(Shannon ~seqrun+psyllid_family+psyllid_genus, data=div_table))
report::report(aov(pd ~seqrun+psyllid_family+psyllid_genus, data=div_table))
# Difference between genus/geography factors ANOVA
report::report(aov(alpha ~seqrun+psyllid_family+genus_geo, data=div_table))
report::report(aov(Shannon ~seqrun+psyllid_family+genus_geo, data=div_table))
report::report(aov(pd ~seqrun+psyllid_family+genus_geo, data=div_table))
## Major genera only
# Difference between all major genera for alpha diversity ANOVA
div_table2 <- div_table %>%
dplyr::filter(psyllid_genus %in% c("Powellia", "Ctenarytaina", "Psylla"))
mg_div <- bind_rows(broom::tidy(TukeyHSD(aov(alpha ~seqrun+psyllid_family+psyllid_genus,
data=div_table2))) %>% mutate(type="Richness"),
broom::tidy(TukeyHSD(aov(Shannon ~seqrun+psyllid_family+psyllid_genus,
data=div_table2))) %>% mutate(type="Shannon"),
broom::tidy(TukeyHSD(aov(pd ~seqrun+psyllid_family+psyllid_genus,
data=div_table2))) %>% mutate(type="Phylogenetic"),
broom::tidy(TukeyHSD(aov(alpha ~seqrun+psyllid_family+genus_geo,
data=div_table2))) %>% mutate(type="Richness"),
broom::tidy(TukeyHSD(aov(alpha ~seqrun+psyllid_family+genus_geo,
data=div_table2))) %>% mutate(type="Shannon"),
broom::tidy(TukeyHSD(aov(pd ~seqrun+psyllid_family+genus_geo,
data=div_table2))) %>% mutate(type="Phylogenetic")
)
write_csv(mg_div, "output/alpha/major_genera_alpha.csv")
# Difference between major genera only ANOVA
report::report(aov(alpha ~seqrun+psyllid_family+psyllid_genus, data=div_table2))
report::report(aov(Shannon ~seqrun+psyllid_family+psyllid_genus, data=div_table2))
report::report(aov(pd ~seqrun+psyllid_family+psyllid_genus, data=div_table2))
# Difference between between major genera/geography factors ANOVA
report::report(aov(alpha ~seqrun+psyllid_family+genus_geo, data=div_table2))
report::report(aov(Shannon ~seqrun+psyllid_family+genus_geo, data=div_table2))
report::report(aov(alpha ~seqrun+psyllid_family+genus_geo, data=div_table2))
# Association with phylogeny
dat <- div_table %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>% #Subset to common
group_by(psyllid_spp) %>%
dplyr::select(-where(is.character)) %>%
summarise_all(mean) %>%
arrange(match(psyllid_spp, pruned.tree$tip.label)) %>%
as.data.frame() %>%
magrittr::set_rownames(.$psyllid_spp) %>%
dplyr::select(-psyllid_spp)
# Add positive and negative controls
dat$random <- rnorm(length(dat$alpha), sd = 10) #Random association
dat$bm <- rTraitCont(pruned.tree) #Brownian motion
# Make phylosignal object and measure signal between univariate traits.
p4d <- phylobase::phylo4d(pruned.tree, dat)
signal <- phylosignal::phyloSignal(p4d = p4d, methods = c("I", "Lambda", "K"), reps = 999)%>%
as.data.frame() %>%
rownames_to_column("measure")
# print phylogenetic signal
signal
write_csv(signal, "output/alpha/phylosignal.csv")
# Locate signal
lipa <- lipaMoran(p4d, reps=999)
lipa.p4d <- lipaMoran(p4d, as.p4d = TRUE, reps=999)
barplot.phylo4d(lipa.p4d, bar.col = (lipa$p.value < 0.05) + 1, center = FALSE, scale = FALSE) + title("Non-rarefied")
#write out lipa
lipa_out <- cbind(lipa$lipa %>%
as.data.frame %>%
rename_all(funs(paste0(., "_stat"))),
lipa$p.value %>%
as.data.frame %>%
rename_all(funs(paste0(., "_pval")))
)%>%
rownames_to_column("psyllid_spp")
write_csv(lipa_out, "output/alpha/lipa.csv")
Rarefied
See if the pattern holds even with rarefaction to lowest sample
# Rarefied richness
ps2_rare <- rarefy_even_depth(ps2, sample.size = min(sample_sums(ps2)),
rngseed = 666, replace = TRUE, trimOTUs = TRUE, verbose = TRUE)
# Get richness measures
richness_rare <- phyloseq::estimate_richness(ps2_rare, measures=c("Shannon")) %>%
rownames_to_column("Sample_Name") %>%
mutate(Sample_Name = Sample_Name %>%
str_remove("^X") %>%
str_replace_all("\\.", " "))
#Set number of randomisations for calculating significance
# Calculate Faith's PD-index & Species richness - with Standard errors
#sespd_rare <- picante::ses.pd(as(phyloseq::otu_table(ps2_rare), "matrix"), phyloseq::phy_tree(ps2_rare), null.model = #"taxa.labels", include.root = F, runs = 99)
pd_rare <- picante::pd(as(phyloseq::otu_table(ps2_rare), "matrix"), phyloseq::phy_tree(ps2_rare), include.root = FALSE)
# Join together
div_table_rare <- pd_rare %>%
rownames_to_column("Sample_Name") %>%
dplyr::select(Sample_Name, alpha = SR, pd = PD) %>%
left_join(richness, by="Sample_Name") %>%
left_join(sample_data(ps2_rare) %>%
as("matrix") %>%
as.data.frame() %>%
filter(!duplicated(Sample_Name)) %>%
dplyr::select(Sample_Name, psyllid_spp, psyllid_genus, genus_geo),
by = "Sample_Name")
# Summarise means
div_table_rare %>%
summarise_if(is.numeric, mean)
# Difference between all major genera for alpha diversity ANOVA
div_table_rare2 <- div_table_rare %>%
dplyr::filter(psyllid_genus %in% c("Powellia", "Ctenarytaina", "Psylla"))
mg_div_rare <- bind_rows(
broom::tidy(TukeyHSD(aov(alpha ~seqrun+psyllid_family+psyllid_genus,
data=div_table_rare2))) %>% mutate(type="Richness"),
broom::tidy(TukeyHSD(aov(Shannon ~seqrun+psyllid_family+psyllid_genus,
data=div_table_rare2))) %>% mutate(type="Shannon"),
broom::tidy(TukeyHSD(aov(pd ~seqrun+psyllid_family+psyllid_genus,
data=div_table_rare2))) %>% mutate(type="Phylogenetic"),
broom::tidy(TukeyHSD(aov(alpha ~seqrun+psyllid_family+genus_geo,
data=div_table_rare2))) %>% mutate(type="Richness"),
broom::tidy(TukeyHSD(aov(alpha ~seqrun+psyllid_family+genus_geo,
data=div_table_rare2))) %>% mutate(type="Shannon"),
broom::tidy(TukeyHSD(aov(pd ~seqrun+psyllid_family+genus_geo,
data=div_table_rare2))) %>% mutate(type="Phylogenetic")
)
write_csv(mg_div_rare, "output/alpha/major_genera_alpha_rarefied.csv")
# Difference between genus factors ANOVA
report::report(aov(alpha ~seqrun+psyllid_family+psyllid_genus, data=div_table_rare2))
report::report(aov(Shannon ~seqrun+psyllid_family+psyllid_genus, data=div_table_rare2))
report::report(aov(pd ~seqrun+psyllid_family+psyllid_genus, data=div_table_rare2))
# Difference between genus/geography factors ANOVA
report::report(aov(alpha ~seqrun+psyllid_family+genus_geo, data=div_table_rare2))
report::report(aov(Shannon ~seqrun+psyllid_family+genus_geo, data=div_table_rare2))
report::report(aov(alpha ~seqrun+psyllid_family+genus_geo, data=div_table_rare2))
dat <- div_table_rare %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>% #Subset to common
group_by(psyllid_spp) %>%
dplyr::select(-where(is.character)) %>%
summarise_all(mean) %>%
arrange(match(psyllid_spp, pruned.tree$tip.label)) %>%
as.data.frame() %>%
magrittr::set_rownames(.$psyllid_spp) %>%
dplyr::select(-psyllid_spp)
# Add positive and negative controls
dat$random <- rnorm(length(dat$alpha), sd = 10) #Random association
dat$bm <- rTraitCont(pruned.tree) #Brownian motion
# Make phylosignal object and measure signal between univariate traits.
p4d <- phylobase::phylo4d(pruned.tree, dat)
signal_rare <- phylosignal::phyloSignal(p4d = p4d, methods = c("I", "Lambda", "K"), reps = 999) %>%
as.data.frame() %>%
rownames_to_column("measure")
# print phylogenetic signal
signal_rare
write_csv(signal_rare, "output/alpha/phylosignal_rarefied.csv")
# Locate signal
lipa <- lipaMoran(p4d, reps=999)
lipa.p4d <- lipaMoran(p4d, as.p4d = TRUE, reps=999)
barplot.phylo4d(lipa.p4d, bar.col = (lipa$p.value < 0.05) + 1, center = FALSE, scale = FALSE) + title("Rarefied")
#write out lipa
lipa_out_rare <- cbind(lipa$lipa %>%
as.data.frame %>%
rename_all(funs(paste0(., "_stat"))),
lipa$p.value %>%
as.data.frame %>%
rename_all(funs(paste0(., "_pval")))
)%>%
rownames_to_column("psyllid_spp")
write_csv(lipa_out_rare, "output/alpha/lipa_rarefied.csv")
Alpha no Gammaproteobacteria
# Rarefied richness
ps2_subset <- ps2 %>%
subset_taxa(class != "Gammaproteobacteria") %>% #is this working?
filter_taxa(function(x) mean(x) > 0, TRUE)#Drop missing taxa from table
ps2_subset <- prune_samples(sample_sums(ps2_subset) >0 , ps2_subset)
message(nsamples(ps2) - nsamples(ps2_subset), " Samples and ", ntaxa(ps2) - ntaxa(ps2_subset), " taxa Dropped")
# Get richness measures
richness_subset <- phyloseq::estimate_richness(ps2_subset, measures=c("Shannon")) %>%
rownames_to_column("Sample_Name") %>%
mutate(Sample_Name = Sample_Name %>%
str_remove("^X") %>%
str_replace_all("\\.", " "))
#Set number of randomisations for calculating significance
# Calculate Faith's PD-index & Species richness - with Standard errors
#sespd_subset <- picante::ses.pd(as(phyloseq::otu_table(ps2_subset), "matrix"), phyloseq::phy_tree(ps2_subset), null.model = "taxa.labels", include.root = F, runs = 99)
pd_subset <- picante::pd(as(phyloseq::otu_table(ps2_subset), "matrix"), phyloseq::phy_tree(ps2_subset), include.root = FALSE)
# Join together
div_table_subset <- pd_subset %>%
rownames_to_column("Sample_Name") %>%
dplyr::select(Sample_Name, alpha = SR, pd = PD) %>%
left_join(richness, by="Sample_Name") %>%
left_join(sample_data(ps2_subset) %>%
as("matrix") %>%
as.data.frame() %>%
filter(!duplicated(Sample_Name)) %>%
dplyr::select(Sample_Name, psyllid_spp, psyllid_genus, genus_geo),
by = "Sample_Name")
# Summarise means
div_table_subset %>%
summarise_if(is.numeric, ~mean(.x, na.rm=TRUE))
# Difference between all major genera for alpha diversity ANOVA
div_table_subset2 <- div_table_subset %>%
dplyr::filter(psyllid_genus %in% c("Powellia", "Ctenarytaina", "Psylla"))
mg_div_subset <- bind_rows(
broom::tidy(TukeyHSD(aov(alpha ~psyllid_genus, data=div_table_subset2))) %>% mutate(type="Richness"),
broom::tidy(TukeyHSD(aov(Shannon ~psyllid_genus, data=div_table_subset2))) %>% mutate(type="Shannon"),
broom::tidy(TukeyHSD(aov(pd ~psyllid_genus, data=div_table_subset2))) %>% mutate(type="Phylogenetic"),
broom::tidy(TukeyHSD(aov(alpha ~genus_geo, data=div_table_subset2))) %>% mutate(type="Richness"),
broom::tidy(TukeyHSD(aov(alpha ~genus_geo, data=div_table_subset2))) %>% mutate(type="Shannon"),
broom::tidy(TukeyHSD(aov(pd ~genus_geo, data=div_table_subset2))) %>% mutate(type="Phylogenetic")
)
write_csv(mg_div_subset, "output/alpha/major_genera_alpha_nogamma.csv")
# Difference between genus factors ANOVA
report::report(aov(alpha ~psyllid_genus, data=div_table_subset2))
report::report(aov(Shannon ~psyllid_genus, data=div_table_subset2))
report::report(aov(pd ~psyllid_genus, data=div_table_subset2))
# Difference between genus/geography factors ANOVA
report::report(aov(alpha ~genus_geo, data=div_table_subset2))
report::report(aov(Shannon ~genus_geo, data=div_table_subset2))
report::report(aov(alpha ~genus_geo, data=div_table_subset2))
Beta diversity
Microbe distances
ps2_dist <- ps2
#ps2_dist <- ps2_filt
# Get OTU tables
otutab <- otu_table(ps2_dist)
#Impute zeroes for compositional distances
otutab_n0 <- as.matrix(zCompositions::cmultRepl(otutab, method="BL", output="p-counts"))
#Root & label phylogenetic tree
phy_tree(ps2_dist) <- multi2di(phy_tree(ps2_dist))
phy_tree(ps2_dist) <- makeNodeLabel(phy_tree(ps2_dist), method="number", prefix='n')
name.balance(phy_tree(ps2_dist), tax_table(ps2_dist), 'n1')
#Calculate different distance metrics
metrics <- c("Bray", "Jaccard", "Aitchison","Philr", "Unifrac", "WUnifrac")
distlist <- vector("list", length=length(metrics))
names(distlist) <- metrics
distlist$Jaccard <- as.matrix(vegdist(otutab, method="jac",binary = T))
distlist$Bray <- as.matrix(vegdist(otutab, method="bray"))
distlist$Aitchison <- as.matrix(vegdist(CoDaSeq::codaSeq.clr(otutab_n0), method="euclidean"))
distlist$Philr <- as.matrix(vegdist(philr::philr(otutab_n0, phy_tree(ps2_dist),
part.weights='enorm.x.gm.counts',
ilr.weights='blw.sqrt'), method="euclidean", na.rm=TRUE))
distlist$Unifrac <- as.matrix(phyloseq::UniFrac(ps2_dist, weighted=FALSE, parallel = TRUE))
distlist$WUnifrac <- as.matrix(phyloseq::UniFrac(ps2_dist, weighted=TRUE, parallel = TRUE))
# Create low abundance filtered dataset
filterfun1 <- function(x){
x[(x / sum(x)) < (1e-4)] <- 0
return(x)
}
ps2_filt <- transform_sample_counts(ps2, fun = filterfun1) %>%
filter_taxa(function(x) mean(x) > 0, TRUE) #Drop missing taxa from table
print(paste0((ntaxa(ps2)-ntaxa(ps2_filt)), " taxa under threshold removed"))
# Get OTU tables
otutab <- otu_table(ps2_filt)
#Impute zeroes for compositional distances
otutab_n0 <- as.matrix(zCompositions::cmultRepl(otutab, method="BL", output="p-counts"))
#Root & label phylogenetic tree
phy_tree(ps2_filt) <- multi2di(phy_tree(ps2_filt))
phy_tree(ps2_filt) <- makeNodeLabel(phy_tree(ps2_filt), method="number", prefix='n')
name.balance(phy_tree(ps2_filt), tax_table(ps2_filt), 'n1')
#Calculate different distance metrics
metrics <- c("Bray", "Jaccard", "Aitchison","Philr", "Unifrac", "WUnifrac")
distlist_filt <- vector("list", length=length(metrics))
names(distlist_filt) <- metrics
distlist_filt$Jaccard <- as.matrix(vegdist(otutab, method="jac",binary = T))
distlist_filt$Bray <- as.matrix(vegdist(otutab, method="bray"))
distlist_filt$Aitchison <- as.matrix(vegdist(CoDaSeq::codaSeq.clr(otutab_n0), method="euclidean"))
distlist_filt$Philr <- as.matrix(vegdist(philr::philr(otutab_n0, phy_tree(ps2_filt),
part.weights='enorm.x.gm.counts',
ilr.weights='blw.sqrt'), method="euclidean", na.rm=TRUE))
distlist_filt$Unifrac <- as.matrix(phyloseq::UniFrac(ps2_filt, weighted=FALSE, parallel = TRUE))
distlist_filt$WUnifrac <- as.matrix(phyloseq::UniFrac(ps2_filt, weighted=TRUE, parallel = TRUE))
# Create dataset without gammaproteoba
ps2_subset <- ps2 %>%
subset_taxa(class != "Gammaproteobacteria") %>% #is this working?
filter_taxa(function(x) mean(x) > 0, TRUE)#Drop missing taxa from table
ps2_subset <- prune_samples(sample_sums(ps2_subset) >0 , ps2_subset)
message(nsamples(ps2) - nsamples(ps2_subset), " Samples and ", ntaxa(ps2) - ntaxa(ps2_subset), " taxa Dropped")
# Get OTU tables
otutab_subset <- otu_table(ps2_subset)
#Impute zeroes for compositional distances
otutab_subset_n0 <- as.matrix(zCompositions::cmultRepl(otutab_subset, method="BL", output="p-counts"))
#Root phylogenetic tree
phy_tree(ps2_subset) <- multi2di(phy_tree(ps2_subset))
phy_tree(ps2_subset) <- makeNodeLabel(phy_tree(ps2_subset), method="number", prefix='n')
name.balance(phy_tree(ps2_subset), tax_table(ps2_subset), 'n1')
#Calculate different distance metrics
metrics <- c("Bray", "Jaccard", "Aitchison","Philr", "Unifrac", "WUnifrac")
distlist_subset <- vector("list", length=length(metrics))
names(distlist_subset) <- metrics
distlist_subset$Jaccard <- as.matrix(vegdist(otutab_subset, method="jac",binary = T))
distlist_subset$Bray <- as.matrix(vegdist(otutab_subset, method="bray"))
distlist_subset$Aitchison <- as.matrix(vegdist(CoDaSeq::codaSeq.clr(otutab_subset_n0), method="euclidean"))
distlist_subset$Philr <- as.matrix(vegdist(philr::philr(otutab_subset_n0, phy_tree(ps2_subset),
part.weights='enorm.x.gm.counts',
ilr.weights='blw.sqrt'), method="euclidean", na.rm=TRUE))
distlist_subset$Unifrac <- as.matrix(phyloseq::UniFrac(ps2_subset, weighted=FALSE, parallel = TRUE))
distlist_subset$WUnifrac <- as.matrix(phyloseq::UniFrac(ps2_subset, weighted=TRUE, parallel = TRUE))
# Mantel test to check concordance of beta diversity pre and post filtering
purrr::map2(distlist, distlist_filt,~{
subsample <- intersect(colnames(.x), colnames(.y))
as.data.frame(vegan::mantel(.x[subsample, subsample], .y[subsample, subsample])[c("statistic","signif","permutations")])
}) %>%
bind_rows(.id="dist")
# Mantel test to check concordance of beta diversity pre and post subset
purrr::map2(distlist, distlist_subset,~{
subsample <- intersect(colnames(.x), colnames(.y))
as.data.frame(vegan::mantel(.x[subsample, subsample], .y[subsample, subsample])[c("statistic","signif","permutations")])
}) %>%
bind_rows(.id="dist")
Adonis & Betadisper
# ADONIS is constructed heirarchially to marginalise techical variance then moving down the taxonomic ranks
# Adonis test
metadata <- sample_data(ps2) %>%
as("data.frame")
adonis_results <- distlist %>%
purrr::map(function(x) {
y <- as.dist(x[metadata$Sample_Name, metadata$Sample_Name])
bind_rows(
broom::tidy(adonis2(y~seqrun+psyllid_family+psyllid_genus+psyllid_spp, method="euclidean", data=metadata,
permutations=999, by="terms")) %>%
mutate(test = paste(term[!term %in% c("Residual", "Total")], collapse="-")),
#broom::tidy(adonis2(y~seqrun+hostplant_spp+psyllid_spp, method="euclidean", data=metadata,
# permutations=999, by="margin")) %>%
# mutate(test = paste(term[!term %in% c("Residual", "Total")], collapse="-")),
broom::tidy(adonis2(y~seqrun+hostplant_spp, method="euclidean", data=metadata,
permutations=999, by="terms")) %>%
mutate(test = paste(term[!term %in% c("Residual", "Total")], collapse="-"))
)
}) %>%
bind_rows(.id="dist")
# Check homogeneity
betadisper_results <- distlist %>%
purrr::map(function(x) {
y <- as.dist(x[metadata$Sample_Name, metadata$Sample_Name])
bind_rows(
as_tibble(permutest(vegan::betadisper(y, metadata$psyllid_spp))$tab, rownames="term") %>%
mutate(test="psyllid_spp"),
as_tibble(permutest(vegan::betadisper(y, metadata$hostplant_spp))$tab, rownames="term") %>%
mutate(test="hostplant_spp"),
)
}) %>%
bind_rows(.id="dist")
dir.create("output/beta")
write_csv(adonis_results, "output/beta/adonis_fulldata.csv")
write_csv(betadisper_results, "output/beta/betadisper_fulldata.csv")
Same tree dissimilarities
Look at the similarities in the microbiome of the psyllid specimens collected from the same host plant
hostplant_metadata <- metadata %>% mutate(ingroup = case_when(
Sample_Name %in% c("94","107", "113","93","106", "112") ~ "fraxini-fraxinicola",
Sample_Name %in% c("200big", "201big", "200small", "201small") ~ "apicalis-frodobagginsi",
TRUE ~ "other"
))
broom::tidy(adonis2(distlist$Aitchison~ingroup + psyllid_spp, method="euclidean",
data=hostplant_metadata))
pairwise.adonis2(distlist$Aitchison~ingroup + psyllid_spp, method="euclidean",
data=hostplant_metadata)
Barplot
# Plot tree
p <- ggtree(pruned.tree) + geom_tiplab(align=TRUE) + geom_nodelab(geom='label') +
scale_x_continuous(expand=c(0, 0.1))
# Plot bar
ps3_bar <- ps3 %>%
speedyseq::tax_glom(taxrank = "order") %>% # agglomerate at Order level
transform_sample_counts(function(x) {x/sum(x)} ) %>% # Transform to rel. abundance
speedyseq::psmelt() %>%
mutate(plotlabel = phylum) %>%
mutate(plotlabel = case_when(
Abundance >= 0.01 & phylum=="Proteobacteria" ~ paste0("P - ", order), # Change this to whatever taxrank we want
Abundance >= 0.01 & !phylum=="Proteobacteria"~ phylum ,
Abundance < 0.01 ~ "NA"
)) %>%
dplyr::na_if("NA") %>%
dplyr::select(psyllid_spp, plotlabel, phylum, order, Abundance) %>%
left_join(p$data %>%
as_data_frame %>%
dplyr::filter(isTip) %>%
dplyr::select(y, label) %>%
dplyr::rename(psyllid_spp=label))
gg.bar <- ggplot(ps3_bar, aes(x=y, y=Abundance, fill=plotlabel)) +
geom_col() +
coord_flip()+
scale_fill_manual(values=colorRampPalette(brewer.pal(9, "Set1"))(length(unique(ps3_bar$plotlabel))-1), na.value="grey") +
base_theme +
theme(legend.position = "bottom",
#panel.grid.major.x = element_line(colour="grey92", size=0.5, linetype="dashed"),
strip.background = element_rect(fill = "grey92",
colour = "black", size = 1),
axis.text.y = element_blank(),
axis.ticks.y=element_blank(),
axis.title.y=element_blank()) +
scale_y_continuous(expand=c(0,0), labels = scales::percent)+
scale_x_continuous(expand=c(0,0)) +
labs(x = NULL ,
y = "Relative Abundance",
fill = NULL)
# Make richness plots
gg.rich <- div_table %>%
group_by(psyllid_spp) %>%
summarise(alpha=mean(alpha), pd=mean(pd)/10e+7, Shannon=mean(Shannon)) %>%
ungroup() %>%
pivot_longer(-psyllid_spp, names_to="measure",
values_to = "value") %>%
left_join(lipa_out %>%
dplyr::select(psyllid_spp, alpha_pval, pd_pval, Shannon_pval) %>%
pivot_longer(-psyllid_spp,
names_to="measure",
values_to = "pval") %>%
mutate(measure = str_remove(measure, "_pval"))) %>%
left_join(p$data %>%
as_data_frame %>%
dplyr::filter(isTip) %>%
dplyr::select(y, label) %>%
dplyr::rename(psyllid_spp=label)) %>%
mutate_at(vars(measure), funs(factor(., levels=c("alpha","Shannon","pd")))) %>%
ggplot(aes(x=y, y=value, fill=pval<0.05)) +
geom_col() +
facet_grid(~measure, scales="free") +
coord_flip()+
base_theme +
theme(legend.position = "bottom",
panel.grid.major.x = element_line(colour="grey92", size=0.5, linetype="dashed"),
# strip.background = element_rect(fill = "grey92",
# colour = "black", size = 1),
axis.text.y = element_blank(),
axis.ticks.y=element_blank(),
axis.title.y=element_blank(),
axis.text.x = element_text(angle=45, hjust=1)) +
scale_fill_manual(values=c("darkgray", "darkred")) +
scale_x_continuous(breaks=scales::pretty_breaks(n=1))+
scale_y_continuous(expand=c(0,0))+
scale_x_continuous(expand=c(0,0))
## Collection_hist
gg.spp <- sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
dplyr::rename(label = psyllid_spp) %>%
group_by(label) %>%
summarise(n_species = n()) %>%
left_join(p$data %>%
filter(isTip) %>% dplyr::select(c(label, y))) %>%
filter(!is.na(y)) %>%
ggplot(aes(x=y, y=1, fill=n_species)) +
geom_tile() +
geom_text(aes(label=n_species))+
coord_flip() +
theme_void() +
theme(legend.position = "bottom") +
scale_fill_distiller(palette = "Reds", direction=1)+
scale_y_continuous(expand=c(0,0))+
scale_x_continuous(expand=c(0,0))
# Make tree with no underscores in name
p2 <- p
p2$data$label <- str_replace(p2$data$label, "_", " ")
#Arrange
Fig1 <- p2 + gg.spp + gg.bar + gg.rich + plot_layout(nrow=1, widths=c(1,0.08,2,0.6))
pdf(file="figs/Beta.pdf", width = 8, height = 11 , paper="a4")
plot(Fig1)
try(dev.off(), silent=TRUE)
Phylosymbiosis
Prepare distance matrices
## Psyllid phylogeny cophenetic distance matrix
phylo.dist <- cophenetic(pruned.tree) %>%
sqrt() %>%
as.data.frame() %>%
rownames_to_column("psyllid_spp.x") %>%
pivot_longer(cols=-psyllid_spp.x,
names_to="psyllid_spp.y",
values_to = "dist") %>%
right_join(sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
dplyr::select(Sample_Name, psyllid_spp) %>%
dplyr::rename(psyllid_spp.x = psyllid_spp, Sample_Name.x = Sample_Name),
by="psyllid_spp.x")%>%
right_join(sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
dplyr::select(Sample_Name, psyllid_spp) %>%
dplyr::rename(psyllid_spp.y = psyllid_spp, Sample_Name.y = Sample_Name),
by="psyllid_spp.y") %>%
filter(!is.na(dist)) %>%
dplyr::select(-psyllid_spp.y, -psyllid_spp.x) %>%
pivot_wider(names_from = Sample_Name.y, values_from = dist) %>%
column_to_rownames("Sample_Name.x") %>%
as.matrix()
plant.tree <- read.tree("sample_data/plant_tree.nwk")
plant.dist <- cophenetic(plant.tree) %>%
sqrt() %>%
as.data.frame() %>%
rownames_to_column("hostplant_spp.x") %>%
pivot_longer(cols=-hostplant_spp.x,
names_to="hostplant_spp.y",
values_to = "dist") %>%
right_join(sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
dplyr::select(Sample_Name, hostplant_spp) %>%
dplyr::rename(hostplant_spp.x = hostplant_spp, Sample_Name.x = Sample_Name),
by="hostplant_spp.x")%>%
right_join(sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
dplyr::select(Sample_Name, hostplant_spp) %>%
dplyr::rename(hostplant_spp.y = hostplant_spp, Sample_Name.y = Sample_Name),
by="hostplant_spp.y") %>%
filter(!is.na(dist))%>%
dplyr::select(-hostplant_spp.y, -hostplant_spp.x) %>%
pivot_wider(names_from = Sample_Name.y, values_from = dist) %>%
column_to_rownames("Sample_Name.x") %>%
as.matrix()
# Spatial distance matrix
envData <- sample_data(ps2) %>%
as("matrix") %>%
as.data.frame() %>%
dplyr::select(long, lat) %>%
mutate(long = as.numeric(long), lat=as.numeric(lat))%>%
drop_na()
spat.dist <- spDists(as.matrix(envData), longlat=TRUE) %>%
as.data.frame() %>%
magrittr::set_rownames(rownames(envData) %>% str_replace(pattern="\\_S(.*)$",replacement="") %>% make.unique()) %>%
magrittr::set_colnames(rownames(envData) %>% str_replace(pattern="\\_S(.*)$",replacement="") %>% make.unique()) %>%
rownames_to_column("SampleID") %>%
mutate(SampleID = SampleID %>% str_replace(pattern="\\_S(.*)$",replacement="") %>% make.unique()) %>%
column_to_rownames("SampleID") %>%
as.matrix()
Whole dataset
set.seed(909)
dir.create("output/phylosymbiosis")
# Matrix correlations
#only use samples present in all
subsample <- Reduce(intersect, list(rownames(otu_table(ps2)), colnames(phylo.dist), colnames(plant.dist), colnames(spat.dist)))
# Mantel test
mantel_results <- distlist %>%
purrr::map(function(x){
run_mantel(x, dists = c("phylo.dist", "plant.dist", "spat.dist"),
subsample = subsample, type ="mantel", nboot=1000)
}) %>%
bind_rows(.id="dist")
write_csv(mantel_results %>%
dplyr::select(-one_of("pval1","pval2")),#only keep two sided P values
"output/phylosymbiosis/mantel_fulldata.csv")
# Partial Mantel Test
pmantel_results <- distlist %>%
purrr::map(function(x){
run_mantel(x, dists = c("phylo.dist", "plant.dist", "spat.dist"),
subsample = subsample, type = "partial", nboot=1000)
}) %>%
bind_rows(.id="dist")
write_csv(pmantel_results %>% dplyr::select(-one_of("pval1","pval2")),
"output/phylosymbiosis/pmantel_fulldata.csv")
## Plot mantels
gg.mantels <- bind_rows(mantel_results, pmantel_results) %>%
dplyr::mutate(dist1 = case_when(
str_detect(dist1, "plant.dist") ~ "Hostplant phylogeny",
str_detect(dist1, "phylo.dist") ~ "Psyllid phylogeny",
str_detect(dist1, "spat.dist") ~ "Spatial distance"
)) %>%
filter(dist == "Aitchison") %>%
mutate(dist1 = factor(dist1, levels= c("Spatial distance","Hostplant phylogeny", "Psyllid phylogeny"))) %>%
mutate(type = type %>%
str_replace("mantel", "Mantel") %>%
str_replace("partial_Mantel", "Partial Mantel")) %>%
ggplot(aes(x=mantelr, y=dist1, colour=dist1)) +
geom_vline(xintercept = 0, colour="black", linetype=2) +
geom_pointrange(aes(xmin=`llim.2.5%`, xmax=`ulim.97.5%`), size=1) +
scale_color_manual(values=c("Hostplant phylogeny"="#b2df8a","Spatial distance"="#a6cee3", "Psyllid phylogeny"="#1f78b4"))+
facet_wrap(~type, ncol=2) +
labs( x = "Mantel R", y = NULL, colour=NULL) +
base_theme +
theme(legend.position = "none",
panel.grid.major = element_line())
gg.mantels
pdf(file="figs/fig3_mantels.pdf", width = 8, height = 4, paper="a4r")
plot(gg.mantels)
try(dev.off(), silent=TRUE)
Without Gammaproteobacteria
# Adonis test
metadata <- sample_data(ps2_subset) %>%
as("data.frame")
adonis_results <- distlist_subset %>%
purrr::map(function(x) {
y <- as.dist(x[metadata$Sample_Name, metadata$Sample_Name])
bind_rows(
broom::tidy(adonis2(y~seqrun+psyllid_family+psyllid_genus+psyllid_spp, method="euclidean", data=metadata,
permutations=999)) %>%
mutate(test = paste(term[!term %in% c("Residual", "Total")], collapse="-")),
broom::tidy(adonis2(y~seqrun+hostplant_spp, method="euclidean", data=metadata,
permutations=999)) %>%
mutate(test = paste(term[!term %in% c("Residual", "Total")], collapse="-"))
)
}) %>%
bind_rows(.id="dist")
write_csv(adonis_results, "output/beta/adonis_subset.csv")
# Matrix correlations
#only use samples present in all
subsample <- Reduce(intersect, list(rownames(otu_table(ps2_subset)), colnames(phylo.dist), colnames(plant.dist), colnames(spat.dist)))
# Mantel test
mantel_results <- distlist_subset %>%
purrr::map(function(x){
run_mantel(x, dists=c("phylo.dist", "plant.dist", "spat.dist"),
subsample=subsample, type="mantel")
}) %>%
bind_rows(.id="dist")
write_csv(mantel_results %>% dplyr::select(-pval1, -pval2), "output/phylosymbiosis/mantel_subset.csv")
# Partial Mantel Test
pmantel_results <- distlist_subset %>%
purrr::map(function(x){
run_mantel(x, dists=c("phylo.dist", "plant.dist", "spat.dist"),
subsample=subsample, type="partial")
}) %>%
bind_rows(.id="dist")
write_csv(pmantel_results %>% dplyr::select(-pval1,-pval2), "output/phylosymbiosis/pmantel_subset.csv")
PCA plots
#set distance
pca_dist <- distlist$Aitchison
#PCA
r.pcx <- prcomp(pca_dist)
pc_samp <- data.frame(Sample_Name = rownames(r.pcx$x), r.pcx$x[, 1:2])%>%
left_join(sample_data(ps2) %>%
as("matrix") %>%
as.data.frame(), by="Sample_Name")
# calculate percent variance explained for the axis labels
pc1 <- round(r.pcx$sdev[1]^2/sum(r.pcx$sdev^2),2)
pc2 <- round(r.pcx$sdev[2]^2/sum(r.pcx$sdev^2),2)
pc_ylab <- paste("PC1: ", pc1, sep="")
pc_xlab <- paste("PC2: ", pc2, sep="")
## colour by psyllid phylogeny
dend <- as.dendrogram(force.ultrametric(pruned.tree))
membership <- as.data.frame(cutree(dend, k=11)) %>%
rownames_to_column("psyllid_spp") %>%
magrittr::set_colnames(c("psyllid_spp", "cluster"))
pca_phylo <- pc_samp %>%
left_join(membership)
gg.pca <- ggplot(data=pca_phylo, aes(x=PC2, y=PC1, colour=as.factor(cluster))) +
geom_point(alpha=0.5, size=3) + #, shape=21, colour="black"
#geom_point(data=pc_otu,aes(PC1, PC2)) +
theme_classic() +
scale_colour_brewer(palette="Paired") +
geom_hline(yintercept = 0, linetype=2, alpha=0.5) +
geom_vline(xintercept = 0, linetype=2, alpha=0.5) +
xlab(pc_xlab) +
ylab(pc_ylab) +
theme(legend.position = "none") +
scale_y_reverse(position = "right")
#coord_fixed(ratio=pc2/pc1) # Scale plot by variance explained
p1 <- ggtree(pruned.tree) +
scale_x_continuous(expand=c(0, 0.2)) +
theme_tree2() +
theme(legend.position = "none")
colours_p1 <- p1$data %>%
left_join(membership %>%
dplyr::rename(label = psyllid_spp)
) %>%
mutate(new_label = label %>% str_replace_all("_", " "))
p1 <- p1 %<+% colours_p1 +
geom_tippoint(aes(colour=as.factor(cluster))) +
geom_tiplab(aes(colour=as.factor(cluster), label=new_label))+
scale_colour_brewer(palette="Paired")
gg.psyllid_pca <- p1 + gg.pca + plot_annotation(title="Psyllid phylogenetic distance")
gg.psyllid_pca
pdf(file="figs/psyllid_pca.pdf", width = 8, height = 11, paper="a4")
plot(gg.psyllid_pca)
try(dev.off(), silent=TRUE)
# Colour by plant phylogeny
plant.tree <- read.tree("sample_data/plant_tree.nwk")
plant.tree <- drop.tip(plant.tree, "Sophora_microphylla_-kowhai" )
plant.tree <- multi2di(plant.tree)
dend <- as.dendrogram(plant.tree )
#test <- color_branches(dend, k=12)
#plot(test)
membership <- as.data.frame(cutree(dend, k=12)) %>%
rownames_to_column("hostplant_spp") %>%
magrittr::set_colnames(c("hostplant_spp", "cluster"))
pca_phylo <- pc_samp %>%
left_join(membership)
gg.pca <- ggplot(data=pca_phylo, aes(x=PC2, y=PC1, colour=as.factor(cluster))) +
geom_point(alpha=0.5, size=3) + #, shape=21, colour="black"
#geom_point(data=pc_otu,aes(PC1, PC2)) +
theme_classic() +
scale_colour_brewer(palette="Paired") +
geom_hline(yintercept = 0, linetype=2, alpha=0.5) +
geom_vline(xintercept = 0, linetype=2, alpha=0.5) +
xlab(pc_xlab) +
ylab(pc_ylab) +
theme(legend.position = "none") +
scale_y_reverse(position = "right")
#coord_fixed(ratio=pc2/pc1) # Scale plot by variance explained
p2 <- ggtree(plant.tree) +
scale_x_continuous(expand=c(0, 70)) +
theme_tree2() +
theme(legend.position = "none")
colours_p2 <- p2$data %>%
left_join(membership %>%
dplyr::rename(label = hostplant_spp)
)%>%
mutate(new_label = label %>% str_replace_all("_", " "))
p2 <- p2 %<+% colours_p2 +
geom_tippoint(aes(colour=as.factor(cluster))) +
geom_tiplab(aes(colour=as.factor(cluster), label=new_label))+
scale_colour_brewer(palette="Paired")
gg.plant_pca <- p2 + gg.pca + plot_annotation(title="Plant phylogenetic distance")
gg.plant_pca
pdf(file="figs/plant_pca.pdf", width = 8, height = 11, paper="a4")
plot(gg.plant_pca)
try(dev.off(), silent=TRUE)
# HC of spatial distance
dend <- hclust(as.dist(spat.dist), method="average")
#test <- color_branches(dend, k=12)
#plot(test)
membership <- as.data.frame(cutree(dend, k=12)) %>%
rownames_to_column("Sample_Name") %>%
magrittr::set_colnames(c("Sample_Name", "cluster"))
pca_phylo <- pc_samp %>%
left_join(membership)
gg.pca <- ggplot(data=pca_phylo, aes(x=PC2, y=PC1, colour=as.factor(cluster))) +
geom_point(alpha=0.5, size=3) + #, shape=21, colour="black"
#geom_point(data=pc_otu,aes(PC1, PC2)) +
theme_classic() +
scale_colour_brewer(palette="Paired") +
geom_hline(yintercept = 0, linetype=2, alpha=0.5) +
geom_vline(xintercept = 0, linetype=2, alpha=0.5) +
xlab(pc_xlab) +
ylab(pc_ylab) +
theme(legend.position = "none") +
scale_y_reverse(position = "right")
#coord_fixed(ratio=pc2/pc1) # Scale plot by variance explained
p3 <- ggtree(as.phylo(dend) )+
scale_x_continuous(expand=c(0, 400)) +
theme_tree2() +
theme(legend.position = "none")
colours_p3 <- p3$data %>%
left_join(membership %>%
dplyr::rename(label = Sample_Name)
)%>%
mutate(new_label = label %>% str_replace_all("_", " "))
p3 <- p3 %<+% colours_p3 +
geom_tippoint(aes(colour=as.factor(cluster))) +
#geom_tiplab(aes(colour=as.factor(cluster), label=new_label))+
scale_colour_brewer(palette="Paired")
gg.spat_pca <- p3 + gg.pca + plot_annotation(title="Spatial Distance")
gg.spat_pca
pdf(file="figs/spatial_pca.pdf", width = 8, height = 11, paper="a4")
plot(gg.spat_pca)
try(dev.off(), silent=TRUE)
Microbiome 3 genera heatmap
filterfun1 <- function(x){
x[(x / sum(x)) < (1e-4)] <- 0 #1e-4 is 0.01% threshold
return(x)
}
ps3_filt <- transform_sample_counts(ps3, fun = filterfun1)%>%
filter_taxa(function(x) mean(x) > 0, TRUE) #Drop missing taxa from table
print(paste0((ntaxa(ps3)-ntaxa(ps3_filt)), " taxa under threshold removed"))
#Get co-occurance matrix
coocur <- ps3_filt %>%
subset_samples(psyllid_genus %in% c("Powellia", "Ctenarytaina", "Psylla")) %>%
filter_taxa(function(x) mean(x) > 0, TRUE) %>%
otu_table %>%
as.matrix() %>%
apply(2, function(x) ifelse(x > 0, 1, 0))
colnames(coocur) <- colnames(coocur) %>%
str_replace_all(" |-", "_")
rownames(coocur) <- rownames(coocur) %>%
str_replace_all(" |-", "_")
# H cophenetic distance
h_tree <- pruned.tree
h_tree$tip.label <- h_tree$tip.label %>%
str_replace_all(" |-", "_")
h_tree <- drop.tip(h_tree, setdiff(h_tree$tip.label, rownames(coocur)))
# P cophenetic distance
s_tree <- phy_tree(ps3)
s_tree$tip.label <- s_tree$tip.label %>%
str_replace_all(" |-", "_")
s_tree <- drop.tip(s_tree, setdiff(s_tree$tip.label, colnames(coocur)))
coocur <- coocur[h_tree$tip.label, s_tree$tip.label]
# Read in Paco runs
paco_run_powellia <- readRDS("output/cophylogeny/paco_powellia_microbe_filtered_asym.rds")
paco_run_cten <- readRDS("output/cophylogeny/paco_ctenarytaina_microbe_filtered_asym.rds")
paco_run_psylla <- readRDS("output/cophylogeny/paco_psylla_microbe_filtered_asym.rds")
paco_run_powellia$gof
paco_run_cten$gof
paco_run_psylla$gof
z_trans <- function(x){
(x - mean(x, na.rm=TRUE)) / sd(x, na.rm=TRUE)
}
# Get link importance
links <- do.call("list", mget(grep("paco_run_",names(.GlobalEnv),value=TRUE))) %>%
purrr::map(function(x){
genus_name <- rownames(x$H)[1] %>% str_remove("_.*$")
data.frame(
joint=names(x$jackknife),
values=unname(x$jackknife),
#upper=unname(x$jackknife$upper),
genus = genus_name
)
}) %>%
bind_rows() %>%
separate(joint, into=c("Sample_Name", "OTU"), sep="-", extra="merge", remove=FALSE) %>%
group_by(genus) %>%
mutate(values = values^2)%>%
mutate(mean_val = mean(values)) %>%
mutate(signif = case_when(
values < mean_val ~ 1,
values > mean_val ~ 0
))%>%
group_by(genus) %>%
mutate(
#st_dev = sd(values),
# z_values = values/st_dev
z_values = z_trans(values)
) %>%
ungroup()
# Plot links
links %>%
dplyr::rename(label = joint) %>%
mutate(label = as.factor(label),
label=reorder_within(label, values, genus))%>%
#arrange(-values) %>%
ggplot(aes(x=label, y=values, colour=as.factor(signif))) + #
geom_point(show.legend = FALSE) +
facet_wrap(~genus, scales = "free")+
#geom_errorbar(aes(ymin=values, ymax=upper)) +
geom_hline(aes(yintercept = mean_val), lty=3) +
scale_x_reordered()+
scale_color_manual(values=c("steelblue", "darkorange1"), name = "Significant") +
theme_classic()+
theme(axis.text.x = element_blank(), legend.position = "right")
#Get observed residuals of Procrustean superimposition
paco_residuals <- do.call("list", mget(grep("paco_run_",names(.GlobalEnv),value=TRUE))) %>%
purrr::map(function(x){
res <- residuals_paco(x$proc, type = "interaction")
genus_name <- rownames(x$H)[1] %>% str_remove("_.*$")
data.frame(
OTU=names(res),
values=unname(res),
genus=genus_name
)
})%>%
bind_rows()
# Plot residuals
ggplot(paco_residuals, aes(x=values))+
geom_density(fill='grey70')+
theme_bw()+
facet_grid(genus~.) +
xlab('Procrustes residuals')+
ylab('Frequency')
# Parafit run
PF_run_powellia <- readRDS("output/cophylogeny/parafit_powellia_microbe_filtered.rds")
PF_run_cten <- readRDS("output/cophylogeny/parafit_ctenarytaina_microbe_filtered.rds")
PF_run_psylla <- readRDS("output/cophylogeny/parafit_psylla_microbe_filtered.rds")
PF_run_powellia$ParaFitGlobal
PF_run_powellia$p.global
PF_run_cten$ParaFitGlobal
PF_run_cten$p.global
PF_run_psylla$ParaFitGlobal
PF_run_psylla$p.global
# Joining isnt working here because they were made separately - so getting the rownames from cooccur no logner works
PF_links <- do.call("list", mget(grep("PF_run_",names(.GlobalEnv),value=TRUE))) %>%
purrr::map(function(x){
genus_name <- names(x$para.per.host[1]) %>% str_remove("_.*$")
as.data.frame(x$link.table) %>%
left_join(enframe(names(x$para.per.host), name = "Host", value="psyllid_spp") %>%
mutate(Host = as.numeric(Host))) %>%
left_join(enframe(names(x$host.per.para), name = "Parasite", value="OTU") %>%
mutate(Parasite = as.numeric(Parasite))) %>%
mutate(genus = genus_name)
}) %>%
bind_rows() %>%
mutate(signif = case_when(
p.F1 < 0.05 ~ 1,
p.F1 > 0.05 ~ 0
)) %>%
group_by(genus) %>%
mutate(z_values = z_trans(F1.stat)) %>%
ungroup()
# Proportion of significant links
PF_links %>%
group_by(genus) %>%
summarise(s = count(signif > 0), ns = count(signif == 0))
# Cophyloplot
coocur.lut <- which(coocur ==1, arr.ind=TRUE)
assoc <- cbind(rownames(coocur)[coocur.lut[,1]], colnames(coocur)[coocur.lut[,2]])
# Rotate the nodes using phytools
library(phytools)
obj <- cophylo(tr1=h_tree, tr2=s_tree, assoc=assoc, rotate=FALSE)
tree1 <- obj[["trees"]][[1]]
p1 <- ggtree(tree1, ladderize=FALSE, aes(colour=links))
weights_p1 <- p1$data %>%
left_join(links %>%
group_by(Sample_Name) %>%
summarise(values = mean(signif)) %>%
#summarise(values = mean(z_values)) %>%
dplyr::rename(label = Sample_Name)
)
## Get values for higher nodes
weights_p1 <- weights_p1 %>%
left_join(data.frame(node=weights_p1$node, links = weights_p1$node %>% purrr::map_dbl(average_descendants, tree=tree1, df=weights_p1)))
p1 <- p1 %<+% weights_p1 +
scale_color_gradient(low="steelblue", high="darkorange1") +
# scale_colour_gradient(high="steelblue", low="darkorange1", trans="log10") +
theme(legend.position = "none")
# OTU tree
tree2 <- obj[["trees"]][[2]]
# make a clade label list
tax_groups <- as.data.frame(tax_table(ps3)) %>%
rownames_to_column("label") %>%
dplyr::mutate(label = label %>% str_replace_all("-", "_") %>%
str_replace_all(" ", "_")) %>%
dplyr::select(label, genus) %>%
filter(label %in% tree2$tip.label) %>%
group_by(genus)
group_name <- group_keys(tax_groups) %>%
mutate(group_name = genus %>% str_remove_all("\\[|\\]"))
cls <- tax_groups %>%
group_split() %>%
purrr::map(pull, label) %>%
set_names(group_name$group_name)
newtree <- groupOTU(tree2, cls)
p2 <- ggtree(newtree, ladderize=TRUE, aes(colour=links))
weights_p2 <- p2$data %>%
left_join(links %>%
group_by(OTU) %>%
summarise(values = mean(signif)) %>%
#summarise(values = mean(z_values)) %>%
dplyr::rename(label = OTU)
)
#Should be able to use castor to make this faster
weights_p2 <- weights_p2 %>%
left_join(data.frame(node=weights_p2$node, links = weights_p2$node %>% purrr::map_dbl(average_descendants, tree=tree2, df=weights_p2)))
p2 <- p2 %<+% weights_p2 +
#scale_colour_gradient(high="steelblue", low="darkorange1", trans="log10") +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# Plot heatmaps
heatmap_dat <- obj$assoc %>%
as_data_frame() %>%
magrittr::set_colnames(c("label.x", "label.y")) %>%
mutate(genus = label.x %>% str_remove("_.*$")) %>%
left_join(links %>%
dplyr::select(label.x = Sample_Name, label.y = OTU, signif_paco=signif, val_paco = z_values)) %>%
left_join(PF_links %>%
dplyr::select(label.x = psyllid_spp, label.y = OTU, signif_para=signif, val_para = z_values)) %>%
left_join(p1$data %>% dplyr::select(label, y) %>% dplyr::rename(label.x = label), by="label.x") %>%
left_join(p2$data %>% dplyr::select(label, y) %>% dplyr::rename(label.y = label), by="label.y") %>%
rownames_to_column("assoc") %>%
dplyr::rename(Sample_Name = label.x,
OTU = label.y,
pos_x = y.x,
pos_y = y.y) %>%
mutate(signif_paco = replace_na(signif_paco, 0),
signif_para = replace_na(signif_para, 0)) %>%
mutate(highlight = case_when(
signif_paco==0 & signif_para==0 ~ "NS",
signif_paco==0 & signif_para==1 ~ "para",
signif_paco==1 & signif_para==0 ~ "paco",
signif_paco==1 & signif_para==1 ~ "both"
))
gg.heatmap <- heatmap_dat %>%
mutate(OTU = OTU %>% str_replace_all("_", " "),
Sample_Name = Sample_Name %>% str_replace_all("_"," ")) %>%
dplyr::select(Sample_Name, OTU, genus, highlight, pos_x, pos_y, val_para, val_paco) %>%
dplyr::mutate(Sample_Name = factor(Sample_Name),
OTU = factor(OTU),
genus=factor(genus, levels=c("Psylla" ,"Powellia", "Ctenarytaina"))) %>%
ggplot(aes(x= fct_reorder(Sample_Name, pos_x), y=fct_reorder(OTU, pos_y), fill=highlight )) +
geom_tile() +
theme_bw() +
facet_grid(~genus, drop = TRUE, scales="free", space ="free")+
theme(axis.text.x = element_text(angle=45, hjust=1),
axis.title.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
legend.position = "none") +
scale_y_discrete(expand=c(0,0))+
scale_y_discrete(expand=c(0,0)) +
#scale_fill_gradient(high="steelblue", low="darkorange1")
scale_fill_manual(values=c("NS"="steelblue","paco"="darkorange1","para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)
gg.heatmap
# Density plot of mismatch
density_dat <- heatmap_dat %>%
left_join(p2$data %>% dplyr::select(OTU = label, y)) %>%
group_by(OTU, y) %>%
summarise(sum_paco = sum(signif_paco, na.rm=TRUE), sum_para = sum(signif_para, na.rm=TRUE)) %>%
mutate(total_sum = sum(sum_paco, sum_para, na.rm=TRUE)) %>%
#summarise(total_sum = mean(val_paco, na.rm=TRUE)) %>%
ungroup()
density_labels <- density_dat %>%
left_join(tax_table(ps3) %>%
as("matrix") %>%
as_tibble(rownames="OTU") %>%
mutate(OTU = OTU %>% str_replace_all(" |-", "_")))%>%
arrange(y)
chunk = 50
n <- nrow(density_labels)
r <- rep(1:ceiling(n/chunk),each=chunk)[1:n]
density_labels_chunked <- split(density_labels,r)
density_labels <- density_labels_chunked %>%
purrr::map(function(x){
df <- x %>%
mutate(zscore = (total_sum - mean(total_sum, na.rm=TRUE))/sd(total_sum, na.rm=TRUE)) %>%
filter( total_sum > 3, zscore > 3,) #,
if(nrow(df) > 0){
out <- df %>%
summarise(y = mean(y), total_sum = max(total_sum), annot = case_when(
length(unique(genus)) == 1 ~ names(which.max(table(genus))),
length(unique(genus)) > 1~ names(which.max(table(family)))))
return(out)
}
}) %>%
bind_rows()
gg.density <- density_dat %>%
filter(total_sum > 0) %>%
ggplot(aes(x = y, y=total_sum, colour=total_sum)) +
geom_point(size=0.01, alpha=1)+
scale_color_gradient(low="steelblue", high="darkorange1") +
geom_text(data=density_labels, aes(label = annot), hjust=0) +
scale_x_continuous(expand=c(0,0)) +
scale_y_continuous(expand=c(0,0)) +
theme_void() +
theme(legend.position = "none")+
coord_flip()
gg.density
# Instead of density could i label lineages with the greatest value?
top <- wrap_elements(grid::textGrob('')) +(p1+ coord_flip() + scale_x_reverse(expand=c(0,0))+ scale_y_continuous(expand=c(0,0))) + wrap_elements(grid::textGrob('')) +plot_layout(widths=c(0.5,3, 0.1))
bottom <- p2+ scale_y_continuous(expand=c(0,0)) + gg.heatmap + gg.density + plot_layout(widths=c(0.5,3, 0.1))
gg.treemap <- top / bottom + plot_layout(heights=c(0.5,3))
gg.treemap
pdf(file="figs/3genus_heatmaps.pdf", width = 8, height = 11, paper="a4")
plot(gg.treemap)
try(dev.off(), silent=TRUE)
# Make plot ranking links, and nodes
gg.host_links <- heatmap_dat %>%
dplyr::rename(label = Sample_Name) %>%
drop_na() %>%
group_by(label) %>%
summarise(signif_paco = sum(signif_paco), signif_para= sum(signif_para)) %>%
pivot_longer(starts_with("signif_"),
names_to="type",
values_to="values") %>%
mutate(type = type %>% str_remove("signif_")) %>%
mutate(label = label %>%
str_replace_all("_", " ") %>%
str_replace(" sp", " sp."))%>%
mutate(label = as.factor(label))%>%
filter(values > 0) %>%
arrange(-values) %>%
ggplot(aes(x=fct_reorder(label, values, sum ), y=values, fill=type)) +
geom_col() +
scale_fill_manual(values=c("NS"="steelblue","paco"="darkorange1","para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)+
theme_classic()+
theme(axis.text.y = element_text(face="italic"),
legend.position = "bottom")+
labs(y = "Number of Signficant links", x=NULL, title ="Psyllid taxa") +
coord_flip()
gg.sym_links <- heatmap_dat %>%
dplyr::rename(label = OTU) %>%
left_join(tax_groups %>% dplyr::rename(otu_genus = genus)) %>%
drop_na() %>%
group_by(otu_genus) %>%
summarise(signif_paco = sum(signif_paco), signif_para= sum(signif_para)) %>%
pivot_longer(starts_with("signif_"),
names_to="type",
values_to="values") %>%
dplyr::rename(label = otu_genus) %>%
dplyr::mutate(label = label %>%
str_remove("\\/.*$") %>%
str_replace("NA_canariense", "Bradyrhizobium_canariense") %>%
str_remove("^s__")
) %>%
mutate(type = type %>% str_remove("signif_")) %>%
mutate(label = as.factor(label),
label=fct_reorder(label, values, sum ))%>%
group_by(label) %>%
mutate(total = sum(values)) %>%
ungroup() %>%
filter(total > 0) %>%
top_n(80, total) %>%
arrange(-values) %>%
ggplot(aes(x=label, y=values, fill=type)) +
geom_col() +
scale_fill_manual(values=c("NS"="steelblue","paco"="darkorange1","para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)+
theme_classic()+
theme(axis.text.y = element_text(face="italic"),
legend.position = "bottom")+
labs(y = "Number of Signficant links", x=NULL, title ="Microbial genera") +
coord_flip()
gg.host_links + gg.sym_links
pdf(file="figs/3genus_fit_summary.pdf", width = 8, height = 11, paper="a4")
plot(gg.host_links + gg.sym_links)
try(dev.off(), silent=TRUE)
Tanglegrams
Psyllid ~ Carsonella
#Flag top abundance carsonella by sample
top_carson <- ps2 %>%
transform_sample_counts(function (x) x/sum(x)) %>%
speedyseq::psmelt() %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>%
filter(genus=="Candidatus Carsonella") %>%
group_by(Sample) %>%
filter(Abundance > 0) %>%
top_n(1, wt=Abundance) %>%
mutate(top = TRUE)
coocur <- ps2 %>%
transform_sample_counts(function (x) x/sum(x)) %>%
speedyseq::psmelt() %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>%
left_join(top_carson) %>%
filter(genus=="Candidatus Carsonella", top==TRUE) %>%
dplyr::select(OTU, psyllid_spp, SampleID, Abundance) %>%
mutate(OTU = OTU %>%
str_replace_all(" |-", "_")) %>%
dplyr::group_by(OTU, psyllid_spp) %>%
summarise(Abundance = sum(Abundance)) %>%
pivot_wider(id_cols = psyllid_spp,
names_from = OTU,
values_from=Abundance,
values_fill = list(Abundance = 0)) %>%
column_to_rownames("psyllid_spp") %>%
as.matrix() %>%
apply(2, function(x) ifelse(x > 0, 1, 0))
# H cophenetic distance
h_tree <- pruned.tree
h_tree$tip.label <- h_tree$tip.label %>%
str_replace_all(" |-", "_")
h_tree <- drop.tip(h_tree, setdiff(h_tree$tip.label, rownames(coocur)))
h_dist <- sqrt(cophenetic(h_tree))
# P cophenetic distance
s_tree <- phy_tree(ps3)
s_tree$tip.label <- s_tree$tip.label %>%
str_replace_all(" |-", "_")
s_tree <- drop.tip(s_tree, setdiff(s_tree$tip.label, colnames(coocur)))
s_dist <- sqrt(cophenetic(s_tree)/1e+6 ) ##convert to Mya so integers are small enough for PACO
coocur <- coocur[h_tree$tip.label, s_tree$tip.label]
# prepare paco data
D <- prepare_paco_data(H=h_dist, P=s_dist, HP=coocur)
# Add pcord
D <- add_pcoord(D, correction='none')
p_host <- ggplot(D$H_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw() +
ggtitle("H PCA")
p_para <- ggplot(D$P_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw()+
ggtitle("P PCA")
plot(p_host + p_para)
# run paco
paco_run <- PACo(D, nperm=999, seed=909, method='quasiswap', symmetric=FALSE)
#print overall significance
print(paco_run$gof)
# Get interaction-specific cophylogenetic contributions using jacknifing
paco_run <- paco_links(paco_run)
links <- data.frame(joint=names(paco_run$jackknife), #losing sv50 here?
values=unname(paco_run$jackknife)#,
#upper=unname(paco_run$jackknife)
) %>%
mutate(OTU = joint) %>%
separate(OTU, into=c("Sample_Name", "OTU"), sep="-", extra="merge") %>%
mutate(signif = case_when(
values < mean(.$values) ~ 1,
values > mean(.$values) ~ 0
))
# Plot links
links %>%
dplyr::rename(label = joint) %>%
mutate(label = as.factor(label),
label=fct_reorder(label, values, sum))%>%
arrange(-values) %>%
ggplot(aes(x=label, y=values, colour=as.factor(signif))) +
geom_point(show.legend = FALSE) +
#geom_errorbar(aes(ymin=values, ymax=upper)) +
geom_hline(yintercept = mean(links$values)) +
scale_color_manual(values=c("steelblue", "darkorange1")) +
theme_classic()+
theme(axis.text.x = element_blank())
dir.create("output/cophylogeny/psyllid_carsonella")
write_csv(links, "output/cophylogeny/psyllid_carsonella/psyllid_carsonella_links.csv")
write_csv(links %>%
group_by(OTU) %>%
summarise(values = mean(values)), "output/cophylogeny/psyllid_carsonella/carsonella_weights.csv")
write_csv(links %>%
group_by(Sample_Name) %>%
summarise(values = mean(values)), "output/cophylogeny/psyllid_carsonella/psyllid_weights.csv")
#Get observed residuals of Procrustean superimposition
paco_residuals <- residuals_paco(paco_run$proc, type = "interaction")
# Visualise residuals
res <- data.frame(OTU=names(paco_residuals), values=unname(paco_residuals)) %>%
separate(OTU, into=c("Sample_Name", "OTU"), sep="-", extra="merge")
ggplot(res, aes(x=values))+
geom_density(fill='grey70')+
theme_bw()+
xlab('Procrustes residuals')+
ylab('Frequency')
# Parafit run
PF_run <- parafit(h_dist, s_dist,coocur, nperm=999, test.links=TRUE)
PF_run$ParaFitGlobal
PF_run$p.global
PF_links <- as.data.frame(PF_run$link.table) %>%
left_join(enframe(names(PF_run$para.per.host), name = "Host", value="psyllid_spp") %>%
mutate(Host = as.numeric(Host))) %>%
left_join(enframe(names(PF_run$host.per.para), name = "Parasite", value="OTU") %>%
mutate(Parasite = as.numeric(Parasite)))%>%
mutate(signif = case_when(
p.F1 < 0.05 ~ 1,
p.F1 > 0.05 ~ 0
))
# Cophyloplot
coocur.lut <- which(coocur ==1, arr.ind=TRUE)
assoc <- cbind(rownames(coocur)[coocur.lut[,1]], colnames(coocur)[coocur.lut[,2]])
# Rotate the nodes using phytools
obj <- cophylo(tr1=h_tree, tr2=s_tree, assoc=assoc, rotate=TRUE)
# psyllid_tree
tree1 <- obj[["trees"]][[1]]
p1 <- ggtree(tree1)
atmeto_node <- p1$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(node)
root_node <- p1$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(parent)
tree1 <- groupOTU(tree1, .node=atmeto_node) # Make the atmeto dotted to indicate outgroup was rescaled
p1 <- ggtree(tree1, ladderize=FALSE, aes(colour=links,linetype=group))
weights_p1 <- p1$data %>%
left_join(links %>%
group_by(Sample_Name) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = Sample_Name)
)%>%
left_join(PF_links %>%
group_by(psyllid_spp) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = psyllid_spp)
) %>%
mutate(values = PA_values + PF_values)
#Scale the atmetocranium and root nodes to be shorter
p1$data[p1$data$node %in% atmeto_node, "x"] <- max(p1$data$x)
p1$data[p1$data$node %in% root_node, "x"] <- 0.2 #root
p1$data$node[p1$data$node]
## Get values for higher nodes
weights_p1 <- weights_p1 %>%
left_join(data.frame(node=weights_p1$node, links = weights_p1$node %>% purrr::map_dbl(average_descendants, tree=tree1, df=weights_p1)))
p1 <- p1 %<+% weights_p1 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# OTU tree
tree2 <- obj[["trees"]][[2]]
s_tree <- drop.tip(tree2, setdiff(tree2$tip.label, obj$assoc[,2]))
p2 <- ggtree(tree2 , ladderize=FALSE, aes(colour=links))
weights_p2 <- p2$data %>%
left_join(links %>%
group_by(OTU) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = OTU)
)%>%
left_join(PF_links %>%
group_by(OTU) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = OTU)
) %>%
mutate(values = PA_values + PF_values)
weights_p2 <- weights_p2 %>%
left_join(data.frame(node=weights_p2$node, links = weights_p2$node %>% purrr::map_dbl(average_descendants, tree=tree2, df=weights_p2)))
p2 <- p2 %<+% weights_p2 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# Tanglegram
tangle <- obj$assoc %>%
as_data_frame() %>%
magrittr::set_colnames(c("label.x", "label.y")) %>%
left_join(links %>%
dplyr::select(label.x = Sample_Name, label.y = OTU, signif_paco=signif)) %>%
left_join(PF_links %>%
dplyr::select(label.x = psyllid_spp, label.y = OTU, signif_para=signif)) %>%
left_join(p1$data %>% dplyr::select(label, y) %>% dplyr::rename(label.x = label), by="label.x") %>%
left_join(p2$data %>% dplyr::select(label, y) %>% dplyr::rename(label.y = label), by="label.y") %>%
rownames_to_column("assoc") %>%
rename_all(~str_replace(.x,pattern="\\.", replacement="_")) %>%
pivot_longer(ends_with(c("_x", "_y")),
names_to=c(".value", "tree"),
names_sep = "_"
) %>%
mutate(signif_paco = replace_na(signif_paco, 0),
signif_para = replace_na(signif_para, 0)) %>%
mutate(signif = case_when(
signif_paco==0 & signif_para==0 ~ "NS",
signif_paco==0 & signif_para==1 ~ "para",
signif_paco==1 & signif_para==0 ~ "paco",
signif_paco==1 & signif_para==1 ~ "both"
)) %>%
mutate(tree = tree %>%
str_replace("x", "host")%>%
str_replace("y", "microbe")) %>%
filter(!is.na(label))%>%
group_by(tree) %>%
mutate(y = y / max(y)) %>%
mutate(label = label %>% str_replace_all("_", " "))
gg.tangle <- ggplot(tangle, aes(x=tree, y=y, group=assoc, colour=as.factor(signif))) +
geom_line(alpha=0.8) +
geom_text(data = tangle %>%
filter(tree=="host"),
aes(label=label),stat = 'unique', hjust=1, check_overlap = TRUE)+
geom_text(data = tangle %>%
filter(tree=="microbe"),
aes(label=label),stat = 'unique', hjust=0, check_overlap = TRUE)+
scale_colour_manual(values=c("NS"="steelblue", "paco"="darkorange1", "para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)+
scale_x_discrete(expand = expansion(add=c(0.8,0.8))) +
theme_void() +
scale_y_continuous(expand=c(0.005,0.005))+
theme(legend.position = "bottom") +
labs(colour="Significance:")
gg.carson_tangle <- p1 + gg.tangle + (p2 + scale_x_reverse()) #+ plot_layout(widths = c(2, 1, 2))
gg.carson_tangle
pdf(file="figs/carsonella_tanglegram.pdf", width = 8, height = 11, paper="a4")
plot(gg.carson_tangle)
try(dev.off(), silent=TRUE)
Psyllid ~ Sodalis
#Flag top abundance sodalis by sample
top_sodalis <- ps2 %>%
transform_sample_counts(function (x) x/sum(x)) %>%
speedyseq::psmelt() %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>%
filter(genus=="Sodalis") %>%
group_by(Sample) %>%
filter(Abundance > 0) %>%
top_n(1, wt=Abundance) %>%
mutate(top = TRUE)
coocur <- ps2 %>%
transform_sample_counts(function (x) x/sum(x)) %>%
speedyseq::psmelt() %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>%
left_join(top_sodalis) %>%
filter(genus=="Sodalis", top==TRUE) %>%
dplyr::select(OTU, psyllid_spp, SampleID, Abundance) %>%
mutate(OTU = OTU %>%
str_replace_all(" |-", "_")) %>%
dplyr::group_by(OTU, psyllid_spp) %>%
summarise(Abundance = sum(Abundance)) %>%
pivot_wider(id_cols = psyllid_spp,
names_from = OTU,
values_from=Abundance,
values_fill = list(Abundance = 0)) %>%
column_to_rownames("psyllid_spp") %>%
as.matrix() %>%
apply(2, function(x) ifelse(x > 0, 1, 0))
coocur <- coocur[ rowSums(coocur) > 0,colSums(coocur) > 0]
# H cophenetic distance
h_tree <- pruned.tree
h_tree$tip.label <- h_tree$tip.label %>%
str_replace_all(" |-", "_")
h_tree <- drop.tip(h_tree, setdiff(h_tree$tip.label, rownames(coocur)))
h_dist <- sqrt(cophenetic(h_tree))
# P cophenetic distance
s_tree <- phy_tree(ps3)
s_tree$tip.label <- s_tree$tip.label %>%
str_replace_all(" |-", "_")
s_tree <- drop.tip(s_tree, setdiff(s_tree$tip.label, colnames(coocur)))
s_dist <- sqrt(cophenetic(s_tree)/1e+6 ) ##convert to Mya so integers are small enough for PACO
#alternatively - sqrt(cophenetic(s_tree))
coocur <- coocur[h_tree$tip.label, s_tree$tip.label]
# prepare paco data
D <- prepare_paco_data(H=h_dist, P=s_dist, HP=coocur)
# Add pcord
D <- add_pcoord(D, correction='none')
p_host <- ggplot(D$H_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw() +
ggtitle("H PCA")
p_para <- ggplot(D$P_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw()+
ggtitle("P PCA")
plot(p_host + p_para)
# run paco
paco_run <- PACo(D, nperm=999, seed=909, method='quasiswap', symmetric=TRUE)
#print overall significance
print(paco_run$gof)
# Get interaction-specific cophylogenetic contributions using jacknifing
paco_run <- paco_links(paco_run)
links <- data.frame(joint=names(paco_run$jackknife), #losing sv50 here?
values=unname(paco_run$jackknife)#,
#upper=unname(paco_run$jackknife$upper)
) %>%
mutate(OTU = joint) %>%
separate(OTU, into=c("Sample_Name", "OTU"), sep="-", extra="merge") %>%
mutate(signif = case_when(
values < mean(.$values) ~ 1,
values > mean(.$values) ~ 0
))
# Plot links
links %>%
dplyr::rename(label = joint) %>%
mutate(label = as.factor(label),
label=fct_reorder(label, values, sum))%>%
arrange(-values) %>%
ggplot(aes(x=label, y=values, colour=as.factor(signif))) +
geom_point(show.legend = FALSE) +
# geom_errorbar(aes(ymin=values, ymax=upper)) +
geom_hline(yintercept = mean(links$values)) +
scale_color_manual(values=c("steelblue", "darkorange1")) +
theme_classic()+
theme(axis.text.x = element_blank())
dir.create("output/cophylogeny/psyllid_secondary")
write_csv(links, "output/cophylogeny/psyllid_secondary/psyllid_secondary_links.csv")
write_csv(links %>%
group_by(OTU) %>%
summarise(values = mean(values)), "output/cophylogeny/psyllid_secondary/secondary_symbiont_weights.csv")
write_csv(links %>%
group_by(Sample_Name) %>%
summarise(values = mean(values)), "output/cophylogeny/psyllid_secondary/psyllid_weights.csv")
#Get observed residuals of Procrustean superimposition
paco_residuals <- residuals_paco(paco_run$proc, type = "interaction")
# Visualise residuals
res <- data.frame(OTU=names(paco_residuals), values=unname(paco_residuals)) %>%
separate(OTU, into=c("Sample_Name", "OTU"), sep="-", extra="merge")
ggplot(res, aes(x=values))+
geom_density(fill='grey70')+
theme_bw()+
xlab('Procrustes residuals')+
ylab('Frequency')
# Parafit run
PF_run <- parafit(h_dist, s_dist,coocur, nperm=999, test.links=TRUE)
PF_run$ParaFitGlobal
PF_run$p.global
PF_links <- as.data.frame(PF_run$link.table) %>%
left_join(enframe(names(PF_run$para.per.host), name = "Host", value="psyllid_spp") %>%
mutate(Host = as.numeric(Host))) %>%
left_join(enframe(names(PF_run$host.per.para), name = "Parasite", value="OTU") %>%
mutate(Parasite = as.numeric(Parasite)))%>%
mutate(signif = case_when(
p.F1 < 0.05 ~ 1,
p.F1 > 0.05 ~ 0
))
# Cophyloplot
coocur.lut <- which(coocur ==1, arr.ind=TRUE)
assoc <- cbind(rownames(coocur)[coocur.lut[,1]], colnames(coocur)[coocur.lut[,2]])
# Rotate the nodes using phytools
library(phytools)
obj <- cophylo(tr1=h_tree, tr2=s_tree, assoc=assoc, rotate=TRUE)
# psyllid_tree
tree1 <- obj[["trees"]][[1]]
p1 <- ggtree(tree1)
atmeto_node <- p1$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(node)
root_node <- p1$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(parent)
tree1 <- groupOTU(tree1, .node=atmeto_node) # Make the atmeto dotted to indicate outgroup was rescaled
p1 <- ggtree(tree1, ladderize=FALSE, aes(colour=links,linetype=group))
weights_p1 <- p1$data %>%
left_join(links %>%
group_by(Sample_Name) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = Sample_Name)
)%>%
left_join(PF_links %>%
group_by(psyllid_spp) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = psyllid_spp)
) %>%
mutate(values = PA_values + PF_values)
#Scale the atmetocranium and root nodes to be shorter
p1$data[p1$data$node %in% atmeto_node, "x"] <- max(p1$data$x)
p1$data[p1$data$node %in% root_node, "x"] <- 0.2 #root
p1$data$node[p1$data$node]
## Get values for higher nodes
weights_p1 <- weights_p1 %>%
left_join(data.frame(node=weights_p1$node, links = weights_p1$node %>% purrr::map_dbl(average_descendants, tree=tree1, df=weights_p1)))
p1 <- p1 %<+% weights_p1 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# OTU tree
tree2 <- obj[["trees"]][[2]]
s_tree <- drop.tip(tree2, setdiff(tree2$tip.label, obj$assoc[,2]))
p2 <- ggtree(tree2 , ladderize=FALSE, aes(colour=links))
weights_p2 <- p2$data %>%
left_join(links %>%
group_by(OTU) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = OTU)
)%>%
left_join(PF_links %>%
group_by(OTU) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = OTU)
) %>%
mutate(values = PA_values + PF_values)
weights_p2 <- weights_p2 %>%
left_join(data.frame(node=weights_p2$node, links = weights_p2$node %>% purrr::map_dbl(average_descendants, tree=tree2, df=weights_p2)))
p2 <- p2 %<+% weights_p2 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# Tanglegram
tangle <- obj$assoc %>%
as_data_frame() %>%
magrittr::set_colnames(c("label.x", "label.y")) %>%
left_join(links %>%
dplyr::select(label.x = Sample_Name, label.y = OTU, signif_paco=signif)) %>%
left_join(PF_links %>%
dplyr::select(label.x = psyllid_spp, label.y = OTU, signif_para=signif)) %>%
left_join(p1$data %>% dplyr::select(label, y) %>% dplyr::rename(label.x = label), by="label.x") %>%
left_join(p2$data %>% dplyr::select(label, y) %>% dplyr::rename(label.y = label), by="label.y") %>%
rownames_to_column("assoc") %>%
rename_all(~str_replace(.x,pattern="\\.", replacement="_")) %>%
pivot_longer(ends_with(c("_x", "_y")),
names_to=c(".value", "tree"),
names_sep = "_"
) %>%
mutate(signif_paco = replace_na(signif_paco, 0),
signif_para = replace_na(signif_para, 0)) %>%
mutate(signif = case_when(
signif_paco==0 & signif_para==0 ~ "NS",
signif_paco==0 & signif_para==1 ~ "para",
signif_paco==1 & signif_para==0 ~ "paco",
signif_paco==1 & signif_para==1 ~ "both"
)) %>%
mutate(tree = tree %>%
str_replace("x", "host")%>%
str_replace("y", "microbe")) %>%
filter(!is.na(label))%>%
group_by(tree) %>%
mutate(y = y / max(y))%>%
mutate(label = label %>% str_replace_all("_", " "))
gg.tangle <- ggplot(tangle, aes(x=tree, y=y, group=assoc, colour=as.factor(signif))) +
geom_line(alpha=0.8) +
geom_text(data = tangle %>%
filter(tree=="host"),
aes(label=label),stat = 'unique', hjust=1, check_overlap = TRUE)+
geom_text(data = tangle %>%
filter(tree=="microbe"),
aes(label=label),stat = 'unique', hjust=0, check_overlap = TRUE)+
scale_colour_manual(values=c("NS"="steelblue", "paco"="darkorange1", "para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)+
scale_x_discrete(expand = expansion(add=c(0.8,0.8))) +
theme_void() +
scale_y_continuous(expand=c(0.01,0.01))+
theme(legend.position = "bottom") +
labs(colour="Significance:")
gg.sodalis_tangle <- p1 + gg.tangle + (p2 + scale_x_reverse()) #+ plot_layout(widths = c(2, 1, 2))
gg.sodalis_tangle
pdf(file="figs/sodalis_tanglegram.pdf", width = 8, height = 11, paper="a4")
plot(gg.sodalis_tangle)
try(dev.off(), silent=TRUE)
Psyllid ~ Arsenophonus
# Subset to top arsenophonus
top_arse <- ps2 %>%
transform_sample_counts(function (x) x/sum(x)) %>%
speedyseq::psmelt() %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>%
filter(genus=="Arsenophonus") %>%
group_by(Sample) %>%
filter(Abundance > 0) %>%
top_n(1, wt=Abundance) %>%
mutate(top = TRUE)
coocur <- ps2 %>%
transform_sample_counts(function (x) x/sum(x)) %>%
speedyseq::psmelt() %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>%
left_join(top_arse) %>%
filter(genus=="Arsenophonus", top==TRUE) %>%
dplyr::select(OTU, psyllid_spp, SampleID, Abundance) %>%
mutate(OTU = OTU %>%
str_replace_all(" |-", "_")) %>%
dplyr::group_by(OTU, psyllid_spp) %>%
summarise(Abundance = sum(Abundance)) %>%
pivot_wider(id_cols = psyllid_spp,
names_from = OTU,
values_from=Abundance,
values_fill = list(Abundance = 0)) %>%
column_to_rownames("psyllid_spp") %>%
as.matrix() %>%
apply(2, function(x) ifelse(x > 0, 1, 0))
coocur <- coocur[ rowSums(coocur) > 0,colSums(coocur) > 0]
# H cophenetic distance
h_tree <- pruned.tree
h_tree$tip.label <- h_tree$tip.label %>%
str_replace_all(" |-", "_")
h_tree <- drop.tip(h_tree, setdiff(h_tree$tip.label, rownames(coocur)))
h_dist <- sqrt(cophenetic(h_tree))
# P cophenetic distance
s_tree <- phy_tree(ps3)
s_tree$tip.label <- s_tree$tip.label %>%
str_replace_all(" |-", "_")
s_tree <- drop.tip(s_tree, setdiff(s_tree$tip.label, colnames(coocur)))
s_dist <- sqrt(cophenetic(s_tree)/1e+6 ) ##convert to Mya so integers are small enough for PACO
#alternatively - sqrt(cophenetic(s_tree))
coocur <- coocur[h_tree$tip.label, s_tree$tip.label]
# prepare paco data
D <- prepare_paco_data(H=h_dist, P=s_dist, HP=coocur)
# Add pcord
D <- add_pcoord(D, correction='none')
p_host <- ggplot(D$H_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw() +
ggtitle("H PCA")
p_para <- ggplot(D$P_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw()+
ggtitle("P PCA")
plot(p_host + p_para)
# run paco
paco_run <- PACo(D, nperm=999, seed=909, method='quasiswap', symmetric=FALSE)
#print overall significance
print(paco_run$gof)
# Get interaction-specific cophylogenetic contributions using jacknifing
paco_run <- paco_links(paco_run)
links <- data.frame(joint=names(paco_run$jackknife),
values=unname(paco_run$jackknife)#,
#upper=unname(paco_run$jackknife$upper)
) %>%
mutate(OTU = joint) %>%
separate(OTU, into=c("Sample_Name", "OTU"), sep="-", extra="merge") %>%
mutate(signif = case_when(
values < mean(.$values) ~ 1,
values > mean(.$values) ~ 0
))
# Plot links
links %>%
dplyr::rename(label = joint) %>%
mutate(label = as.factor(label),
label=fct_reorder(label, values, sum))%>%
arrange(-values) %>%
ggplot(aes(x=label, y=values, colour=as.factor(signif))) +
geom_point(show.legend = FALSE) +
geom_errorbar(aes(ymin=values, ymax=upper)) +
geom_hline(yintercept = mean(links$values)) +
scale_color_manual(values=c("steelblue", "darkorange1")) +
theme_classic()+
theme(axis.text.x = element_blank())
dir.create("output/cophylogeny/psyllid_secondary")
write_csv(links, "output/cophylogeny/psyllid_secondary/psyllid_secondary_links.csv")
write_csv(links %>%
group_by(OTU) %>%
summarise(values = mean(values)), "output/cophylogeny/psyllid_secondary/secondary_symbiont_weights.csv")
write_csv(links %>%
group_by(Sample_Name) %>%
summarise(values = mean(values)), "output/cophylogeny/psyllid_secondary/psyllid_weights.csv")
#Get observed residuals of Procrustean superimposition
paco_residuals <- residuals_paco(paco_run$proc, type = "interaction")
# Visualise residuals
res <- data.frame(OTU=names(paco_residuals), values=unname(paco_residuals)) %>%
separate(OTU, into=c("Sample_Name", "OTU"), sep="-", extra="merge")
ggplot(res, aes(x=values))+
geom_density(fill='grey70')+
theme_bw()+
xlab('Procrustes residuals')+
ylab('Frequency')
# Parafit run
PF_run <- parafit(h_dist, s_dist,coocur, nperm=999, test.links=TRUE)
PF_run$ParaFitGlobal
PF_run$p.global
PF_links <- as.data.frame(PF_run$link.table) %>%
left_join(enframe(names(PF_run$para.per.host), name = "Host", value="psyllid_spp") %>%
mutate(Host = as.numeric(Host))) %>%
left_join(enframe(names(PF_run$host.per.para), name = "Parasite", value="OTU") %>%
mutate(Parasite = as.numeric(Parasite)))%>%
mutate(signif = case_when(
p.F1 < 0.05 ~ 1,
p.F1 > 0.05 ~ 0
))
# Cophyloplot
coocur.lut <- which(coocur ==1, arr.ind=TRUE)
assoc <- cbind(rownames(coocur)[coocur.lut[,1]], colnames(coocur)[coocur.lut[,2]])
# Rotate the nodes using phytools
library(phytools)
obj <- cophylo(tr1=h_tree, tr2=s_tree, assoc=assoc, rotate=TRUE)
# psyllid_tree
tree1 <- obj[["trees"]][[1]]
p1 <- ggtree(tree1)
atmeto_node <- p1$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(node)
root_node <- p1$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(parent)
tree1 <- groupOTU(tree1, .node=atmeto_node) # Make the atmeto dotted to indicate outgroup was rescaled
p1 <- ggtree(tree1, ladderize=FALSE, aes(colour=links,linetype=group))
weights_p1 <- p1$data %>%
left_join(links %>%
group_by(Sample_Name) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = Sample_Name)
)%>%
left_join(PF_links %>%
group_by(psyllid_spp) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = psyllid_spp)
) %>%
mutate(values = PA_values + PF_values)
#Scale the atmetocranium and root nodes to be shorter
p1$data[p1$data$node %in% atmeto_node, "x"] <- max(p1$data$x)
p1$data[p1$data$node %in% root_node, "x"] <- 0.2 #root
p1$data$node[p1$data$node]
## Get values for higher nodes
weights_p1 <- weights_p1 %>%
left_join(data.frame(node=weights_p1$node, links = weights_p1$node %>% purrr::map_dbl(average_descendants, tree=tree1, df=weights_p1)))
p1 <- p1 %<+% weights_p1 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# OTU tree
tree2 <- obj[["trees"]][[2]]
s_tree <- drop.tip(tree2, setdiff(tree2$tip.label, obj$assoc[,2]))
p2 <- ggtree(tree2 , ladderize=FALSE, aes(colour=links))
weights_p2 <- p2$data %>%
left_join(links %>%
group_by(OTU) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = OTU)
)%>%
left_join(PF_links %>%
group_by(OTU) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = OTU)
) %>%
mutate(values = PA_values + PF_values)
weights_p2 <- weights_p2 %>%
left_join(data.frame(node=weights_p2$node, links = weights_p2$node %>% purrr::map_dbl(average_descendants, tree=tree2, df=weights_p2)))
p2 <- p2 %<+% weights_p2 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# Tanglegram
tangle <- obj$assoc %>%
as_data_frame() %>%
magrittr::set_colnames(c("label.x", "label.y")) %>%
left_join(links %>%
dplyr::select(label.x = Sample_Name, label.y = OTU, signif_paco=signif)) %>%
left_join(PF_links %>%
dplyr::select(label.x = psyllid_spp, label.y = OTU, signif_para=signif)) %>%
left_join(p1$data %>% dplyr::select(label, y) %>% dplyr::rename(label.x = label), by="label.x") %>%
left_join(p2$data %>% dplyr::select(label, y) %>% dplyr::rename(label.y = label), by="label.y") %>%
rownames_to_column("assoc") %>%
rename_all(~str_replace(.x,pattern="\\.", replacement="_")) %>%
pivot_longer(ends_with(c("_x", "_y")),
names_to=c(".value", "tree"),
names_sep = "_"
) %>%
mutate(signif_paco = replace_na(signif_paco, 0),
signif_para = replace_na(signif_para, 0)) %>%
mutate(signif = case_when(
signif_paco==0 & signif_para==0 ~ "NS",
signif_paco==0 & signif_para==1 ~ "para",
signif_paco==1 & signif_para==0 ~ "paco",
signif_paco==1 & signif_para==1 ~ "both"
)) %>%
mutate(tree = tree %>%
str_replace("x", "host")%>%
str_replace("y", "microbe")) %>%
filter(!is.na(label))%>%
group_by(tree) %>%
mutate(y = y / max(y))%>%
mutate(label = label %>% str_replace_all("_", " "))
gg.tangle <- ggplot(tangle, aes(x=tree, y=y, group=assoc, colour=as.factor(signif))) +
geom_line(alpha=0.8) +
geom_text(data = tangle %>%
filter(tree=="host"),
aes(label=label),stat = 'unique', hjust=1, check_overlap = TRUE)+
geom_text(data = tangle %>%
filter(tree=="microbe"),
aes(label=label),stat = 'unique', hjust=0, check_overlap = TRUE)+
scale_colour_manual(values=c("NS"="steelblue", "paco"="darkorange1", "para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)+
scale_x_discrete(expand = expansion(add=c(0.8,0.8))) +
theme_void() +
scale_y_continuous(expand=c(0.005,0.005))+
theme(legend.position = "bottom") +
labs(colour="Significance:")
gg.arse_tangle <- p1 + gg.tangle + (p2 + scale_x_reverse()) #+ plot_layout(widths = c(2, 1, 2))
gg.arse_tangle
pdf(file="figs/arsenophonus_tanglegram.pdf", width = 8, height = 11, paper="a4")
plot(gg.arse_tangle)
try(dev.off(), silent=TRUE)
Psyllid ~ hostplant
Tanglegram of all plants and psyllids!
plant.tree <- read.tree("sample_data/plant_tree.nwk")
plant.tree <- drop.tip(plant.tree , "Sophora_microphylla_-kowhai" )
#Prepare co-occurance matrix
coocur <- sample_data(ps2) %>%
as_tibble() %>%
dplyr::select(psyllid_spp, hostplant_spp) %>%
filter(!is.na(psyllid_spp)) %>%
unique() %>%
mutate(psyllid_spp = psyllid_spp %>%
str_replace_all(" |-", "_"),
hostplant_spp = hostplant_spp %>%
str_replace_all(" |-", "_"),
presence = 1
) %>%
pivot_wider(names_from = "hostplant_spp",
values_from="presence",
values_fill = list(presence = 0)) %>%
column_to_rownames("psyllid_spp") %>%
t()
# H cophenetic distance
h_tree <- plant.tree
h_tree$tip.label <- h_tree$tip.label %>%
str_replace_all(" |-", "_")
h_tree <- drop.tip(h_tree, setdiff(h_tree$tip.label, rownames(coocur)))
h_dist <- sqrt(cophenetic(h_tree))
# P cophenetic distance
s_tree <- pruned.tree
s_tree$tip.label <- s_tree$tip.label %>%
str_replace_all(" |-", "_")
s_tree <- drop.tip(s_tree, setdiff(s_tree$tip.label, colnames(coocur)))
s_dist <- sqrt(cophenetic(s_tree))
coocur <- coocur[h_tree$tip.label, s_tree$tip.label]
# prepare paco data
D <- prepare_paco_data(H=h_dist, P=s_dist, HP=coocur)
# Add pcord
D <- add_pcoord(D, correction='none')
p_host <- ggplot(D$H_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw() +
ggtitle("H PCA")
p_para <- ggplot(D$P_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw()+
ggtitle("P PCA")
plot(p_host + p_para)
# run paco
paco_run <- PACo(D, nperm=999, seed=909, method='quasiswap', symmetric=FALSE)
#print overall significance
print(paco_run$gof)
# Get interaction-specific cophylogenetic contributions using leave-one-out jacknifing
paco_run <- paco_links(paco_run, .parallel = TRUE)
# get links
links <- data.frame(
joint=names(paco_run$jackknife),
values=unname(paco_run$jackknife)#,
#upper=unname(paco_run$jackknife$upper)
) %>%
mutate(OTU = joint) %>%
separate(OTU, into=c("hostplant_spp", "psyllid_spp"), sep="-", extra="merge") %>%
mutate(signif = case_when(
values < mean(.$values) ~ 1,
values > mean(.$values) ~ 0
))
# Plot links
links %>%
dplyr::rename(label = joint) %>%
mutate(label = as.factor(label),
label=fct_reorder(label, values, sum))%>%
arrange(-values) %>%
ggplot(aes(x=label, y=values, colour=as.factor(signif))) +
geom_point(show.legend = FALSE) +
# geom_errorbar(aes(ymin=values, ymax=upper)) +
geom_hline(yintercept = mean(links$values)) +
scale_color_manual(values=c("steelblue", "darkorange1")) +
theme_classic()+
theme(axis.text.x = element_blank())
dir.create("output/cophylogeny/psyllid_hostplant")
write_csv(links, "output/cophylogeny/psyllid_hostplant/psyllid_hostplant_links.csv")
write_csv(links %>%
group_by(psyllid_spp) %>%
summarise(values = mean(values)), "output/cophylogeny/psyllid_hostplant/psyllid_weights.csv")
write_csv(links %>%
group_by(hostplant_spp) %>%
summarise(values = mean(values)), "output/cophylogeny/psyllid_hostplant/hostplant_weights.csv")
#Get observed residuals of Procrustean superimposition
paco_residuals <- residuals_paco(paco_run$proc, type = "interaction")
# Visualise residuals
res <- data.frame(OTU=names(paco_residuals), values=unname(paco_residuals)) %>%
separate(OTU, into=c("hostplant_spp", "psyllid_spp"), sep="-", extra="merge")
ggplot(res, aes(x=values))+
geom_density(fill='grey70')+
#facet_wrap(~psyllid_genus) +
theme_bw()+
xlab('Procrustes residuals')+
ylab('Frequency')
# Parafit run
PF_run <- parafit(h_dist, s_dist, t(coocur), nperm=999, test.links=TRUE, silent=FALSE)
PF_run$ParaFitGlobal
PF_run$p.global
PF_links <- as.data.frame(PF_run$link.table) %>%
left_join(enframe(names(PF_run$para.per.host), name = "Host", value="hostplant_spp") %>%
mutate(Host = as.numeric(Host))) %>%
left_join(enframe(names(PF_run$host.per.para), name = "Parasite", value="psyllid_spp") %>%
mutate(Parasite = as.numeric(Parasite)))%>%
mutate(signif = case_when(
p.F1 < 0.05 ~ 1,
p.F1 > 0.05 ~ 0
))
# Cophyloplot
coocur.lut <- which(coocur ==1, arr.ind=TRUE)
assoc <- cbind(rownames(coocur)[coocur.lut[,1]], colnames(coocur)[coocur.lut[,2]])
# Rotate the nodes using phytools
obj <- cophylo(tr1=h_tree, tr2=s_tree, assoc=assoc, rotate=TRUE)
# Extract the goods for ggtree
# plant tree
tree1 <- obj[["trees"]][[1]]
p1 <- ggtree(tree1, ladderize=FALSE, aes(colour=links))
weights_p1 <- p1$data %>%
left_join(links %>%
group_by(hostplant_spp) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = hostplant_spp)
)%>%
left_join(PF_links %>%
group_by(hostplant_spp) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = hostplant_spp)
) %>%
mutate(values = PA_values + PF_values)
## Get values for higher nodes
weights_p1 <- weights_p1 %>%
left_join(data.frame(node=weights_p1$node, links = weights_p1$node %>% purrr::map_dbl(average_descendants, tree=tree1, df=weights_p1)))
p1 <- p1 %<+% weights_p1 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# psyllid_tree
tree2 <- obj[["trees"]][[2]]
s_tree <- drop.tip(tree2, setdiff(tree2$tip.label, obj$assoc[,2]))
p2 <- ggtree(tree2)
atmeto_node <- p2$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(node)
root_node <- p2$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(parent)
tree2 <- groupOTU(tree2, .node=atmeto_node) # Make the atmeto dotted to indicate outgroup was rescaled
p2 <- ggtree(tree2 , ladderize=TRUE, aes(colour=links, linetype=group))
weights_p2 <- p2$data %>%
left_join(links %>%
group_by(psyllid_spp) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = psyllid_spp)
)%>%
left_join(PF_links %>%
group_by(psyllid_spp) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = psyllid_spp)
) %>%
mutate(values = PA_values + PF_values)
#Scale the atmetocranium and root nodes to be shorter
p2$data[p2$data$node %in% atmeto_node, "x"] <- max(p2$data$x)
p2$data[p2$data$node %in% root_node, "x"] <- 0.2 #root
p2$data$node[p2$data$node]
weights_p2 <- weights_p2 %>%
left_join(data.frame(node=weights_p2$node, links = weights_p2$node %>% purrr::map_dbl(average_descendants, tree=tree2, df=weights_p2)))
p2 <- p2 %<+% weights_p2 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# Tanglegram
tangle <- obj$assoc %>%
as_data_frame() %>%
magrittr::set_colnames(c("label.x", "label.y")) %>%
left_join(links %>%
dplyr::select(label.x = hostplant_spp, label.y = psyllid_spp, signif_paco=signif)) %>%
left_join(PF_links %>%
dplyr::select(label.x = hostplant_spp, label.y = psyllid_spp, signif_para=signif)) %>%
left_join(p1$data %>% dplyr::select(label, y) %>% dplyr::rename(label.x = label), by="label.x") %>%
left_join(p2$data %>% dplyr::select(label, y) %>% dplyr::rename(label.y = label), by="label.y") %>%
rownames_to_column("assoc") %>%
rename_all(~str_replace(.x,pattern="\\.", replacement="_")) %>%
pivot_longer(ends_with(c("_x", "_y")),
names_to=c(".value", "tree"),
names_sep = "_"
) %>%
mutate(signif_paco = replace_na(signif_paco, 0),
signif_para = replace_na(signif_para, 0)) %>%
mutate(signif = case_when(
signif_paco==0 & signif_para==0 ~ "NS",
signif_paco==0 & signif_para==1 ~ "para",
signif_paco==1 & signif_para==0 ~ "paco",
signif_paco==1 & signif_para==1 ~ "both"
)) %>%
mutate(tree = tree %>%
str_replace("x", "host")%>%
str_replace("y", "microbe")) %>%
filter(!is.na(label))%>%
group_by(tree) %>%
mutate(y = y / max(y))%>%
mutate(label = label %>% str_replace_all("_", " "))
gg.tangle <- ggplot(tangle, aes(x=factor(tree, levels=c("microbe", "host")), y=y, group=assoc, colour=as.factor(signif))) +
geom_line(alpha=0.8) +
geom_text(data = tangle %>%
filter(tree=="host"),
aes(label=label),stat = 'unique', hjust=0, check_overlap = TRUE)+
geom_text(data = tangle %>%
filter(tree=="microbe"),
aes(label=label),stat = 'unique', hjust=1, check_overlap = TRUE)+
scale_colour_manual(values=c("NS"="steelblue", "paco"="darkorange1", "para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)+
scale_x_discrete(expand = expansion(add=c(0.8,0.8))) +
scale_y_continuous(expand=c(0.005,0.005))+
theme_void() +
theme(legend.position = "bottom") +
labs(colour="Significance")
gg.plant_tangle <- p2 + gg.tangle + (p1 + scale_x_reverse()) #+ plot_layout(widths = c(2, 1, 2))
gg.plant_tangle
pdf(file="figs/plant_tanglegram.pdf", width = 8, height = 11, paper="a4")
plot(gg.plant_tangle)
try(dev.off(), silent=TRUE)
Powellia
Powellia ~ Carsonella
#Flag top abundance carsonella by sample
top_carson <- ps2 %>%
transform_sample_counts(function (x) x/sum(x)) %>%
speedyseq::psmelt() %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>%
filter(genus=="Candidatus Carsonella") %>%
group_by(Sample) %>%
filter(Abundance > 0) %>%
top_n(1, wt=Abundance) %>%
mutate(top = TRUE)
coocur <- ps2 %>%
subset_samples(psyllid_genus == "Powellia") %>%
filter_taxa(function(x) mean(x) > 0, TRUE) %>%
transform_sample_counts(function (x) x/sum(x)) %>%
speedyseq::psmelt() %>%
filter(psyllid_spp %in% pruned.tree$tip.label) %>%
left_join(top_carson) %>%
filter(genus=="Candidatus Carsonella", top==TRUE) %>%
dplyr::select(OTU, psyllid_spp, SampleID, Abundance) %>%
mutate(OTU = OTU %>%
str_replace_all(" |-", "_")) %>%
dplyr::group_by(OTU, psyllid_spp) %>%
summarise(Abundance = sum(Abundance)) %>%
pivot_wider(id_cols = psyllid_spp,
names_from = OTU,
values_from=Abundance,
values_fill = list(Abundance = 0)) %>%
column_to_rownames("psyllid_spp") %>%
as.matrix() %>%
apply(2, function(x) ifelse(x > 0, 1, 0))
# H cophenetic distance
h_tree <- pruned.tree
h_tree$tip.label <- h_tree$tip.label %>%
str_replace_all(" |-", "_")
h_tree <- drop.tip(h_tree, setdiff(h_tree$tip.label, rownames(coocur)))
h_dist <- sqrt(cophenetic(h_tree))
# P cophenetic distance
s_tree <- phy_tree(ps3)
s_tree$tip.label <- s_tree$tip.label %>%
str_replace_all(" |-", "_")
s_tree <- drop.tip(s_tree, setdiff(s_tree$tip.label, colnames(coocur)))
s_dist <- sqrt(cophenetic(s_tree)/1e+6 ) ##convert to Mya so integers are small enough for PACO
coocur <- coocur[h_tree$tip.label, s_tree$tip.label]
# prepare paco data
D <- prepare_paco_data(H=h_dist, P=s_dist, HP=coocur)
# Add pcord
D <- add_pcoord(D, correction='none')
p_host <- ggplot(D$H_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw() +
ggtitle("H PCA")
p_para <- ggplot(D$P_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw()+
ggtitle("P PCA")
plot(p_host + p_para)
# run paco
paco_run <- PACo(D, nperm=999, seed=909, method='quasiswap', symmetric=FALSE)
#print overall significance
print(paco_run$gof)
# Get interaction-specific cophylogenetic contributions using jacknifing
paco_run <- paco_links(paco_run)
links <- data.frame(joint=names(paco_run$jackknife),
values=unname(paco_run$jackknife)#,
#upper=unname(paco_run$jackknife$upper)
) %>%
mutate(OTU = joint) %>%
separate(OTU, into=c("Sample_Name", "OTU"), sep="-", extra="merge") %>%
mutate(signif = case_when(
values < mean(.$values) ~ 1,
values > mean(.$values) ~ 0
))
# Plot links
links %>%
dplyr::rename(label = joint) %>%
mutate(label = as.factor(label),
label=fct_reorder(label, values, sum))%>%
arrange(-values) %>%
ggplot(aes(x=label, y=values, colour=as.factor(signif))) +
geom_point(show.legend = FALSE) +
#geom_errorbar(aes(ymin=values, ymax=upper)) +
geom_hline(yintercept = mean(links$values)) +
scale_color_manual(values=c("steelblue", "darkorange1")) +
theme_classic()+
theme(axis.text.x = element_blank())
dir.create("output/cophylogeny/trioza_carsonella")
write_csv(links, "output/cophylogeny/trioza_carsonella/trioza_carsonella_links.csv")
write_csv(links %>%
group_by(OTU) %>%
summarise(values = mean(values)), "output/cophylogeny/trioza_carsonella/carsonella_weights.csv")
write_csv(links %>%
group_by(Sample_Name) %>%
summarise(values = mean(values)), "output/cophylogeny/trioza_carsonella/psyllid_weights.csv")
#Get observed residuals of Procrustean superimposition
paco_residuals <- residuals_paco(paco_run$proc, type = "interaction")
# Visualise residuals
res <- data.frame(OTU=names(paco_residuals), values=unname(paco_residuals)) %>%
separate(OTU, into=c("Sample_Name", "OTU"), sep="-", extra="merge")
ggplot(res, aes(x=values))+
geom_density(fill='grey70')+
theme_bw()+
xlab('Procrustes residuals')+
ylab('Frequency')
# Parafit run
PF_run <- parafit(h_dist, s_dist, t(coocur), nperm=999, test.links=TRUE, silent=FALSE)
PF_run$ParaFitGlobal
PF_run$p.global
PF_links <- as.data.frame(PF_run$link.table) %>%
left_join(enframe(names(PF_run$para.per.host), name = "Host", value="psyllid_spp") %>%
mutate(Host = as.numeric(Host))) %>%
left_join(enframe(names(PF_run$host.per.para), name = "Parasite", value="OTU") %>%
mutate(Parasite = as.numeric(Parasite)))%>%
mutate(signif = case_when(
p.F1 < 0.05 ~ 1,
p.F1 > 0.05 ~ 0
))
# Cophyloplot
coocur.lut <- which(coocur ==1, arr.ind=TRUE)
assoc <- cbind(rownames(coocur)[coocur.lut[,1]], colnames(coocur)[coocur.lut[,2]])
# Rotate the nodes using phytools
obj <- cophylo(tr1=h_tree, tr2=s_tree, assoc=assoc, rotate=TRUE)
# psyllid_tree
tree1 <- obj[["trees"]][[1]]
p1 <- ggtree(tree1)
atmeto_node <- p1$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(node)
root_node <- p1$data %>%
filter(str_detect(label, "Atmeto")) %>%
pull(parent)
tree1 <- groupOTU(tree1, .node=atmeto_node) # Make the atmeto dotted to indicate outgroup was rescaled
p1 <- ggtree(tree1, ladderize=FALSE, aes(colour=links,linetype=group))
weights_p1 <- p1$data %>%
left_join(links %>%
group_by(Sample_Name) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = Sample_Name)
)%>%
left_join(PF_links %>%
group_by(psyllid_spp) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = psyllid_spp)
) %>%
mutate(values = PA_values + PF_values)
#Scale the atmetocranium and root nodes to be shorter
p1$data[p1$data$node %in% atmeto_node, "x"] <- max(p1$data$x)
p1$data[p1$data$node %in% root_node, "x"] <- 0.2 #root
p1$data$node[p1$data$node]
## Get values for higher nodes
weights_p1 <- weights_p1 %>%
left_join(data.frame(node=weights_p1$node, links = weights_p1$node %>% purrr::map_dbl(average_descendants, tree=tree1, df=weights_p1)))
p1 <- p1 %<+% weights_p1 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# OTU tree
tree2 <- obj[["trees"]][[2]]
s_tree <- drop.tip(tree2, setdiff(tree2$tip.label, obj$assoc[,2]))
p2 <- ggtree(tree2 , ladderize=TRUE, aes(colour=links))
weights_p2 <- p2$data %>%
left_join(links %>%
group_by(OTU) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = OTU)
)%>%
left_join(PF_links %>%
group_by(OTU) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = OTU)
) %>%
mutate(values = PA_values + PF_values)
weights_p2 <- weights_p2 %>%
left_join(data.frame(node=weights_p2$node, links = weights_p2$node %>% purrr::map_dbl(average_descendants, tree=tree2, df=weights_p2)))
p2 <- p2 %<+% weights_p2 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# Tanglegram
tangle <- obj$assoc %>%
as_data_frame() %>%
magrittr::set_colnames(c("label.x", "label.y")) %>%
left_join(links %>%
dplyr::select(label.x = Sample_Name, label.y = OTU, signif_paco=signif)) %>%
left_join(PF_links %>%
dplyr::select(label.x = psyllid_spp, label.y = OTU, signif_para=signif)) %>%
left_join(p1$data %>% dplyr::select(label, y) %>% dplyr::rename(label.x = label), by="label.x") %>%
left_join(p2$data %>% dplyr::select(label, y) %>% dplyr::rename(label.y = label), by="label.y") %>%
rownames_to_column("assoc") %>%
rename_all(~str_replace(.x,pattern="\\.", replacement="_")) %>%
pivot_longer(ends_with(c("_x", "_y")),
names_to=c(".value", "tree"),
names_sep = "_"
) %>%
mutate(signif_paco = replace_na(signif_paco, 0),
signif_para = replace_na(signif_para, 0)) %>%
mutate(signif = case_when(
signif_paco==0 & signif_para==0 ~ "NS",
signif_paco==0 & signif_para==1 ~ "para",
signif_paco==1 & signif_para==0 ~ "paco",
signif_paco==1 & signif_para==1 ~ "both"
)) %>%
mutate(tree = tree %>%
str_replace("x", "host")%>%
str_replace("y", "microbe")) %>%
filter(!is.na(label))%>%
group_by(tree) %>%
mutate(y = y / max(y))%>%
mutate(label = label %>% str_replace_all("_", " "))
gg.tangle <- ggplot(tangle, aes(x=tree, y=y, group=assoc, colour=as.factor(signif))) +
geom_line(alpha=0.8) +
geom_text(data = tangle %>%
filter(tree=="host"),
aes(label=label),stat = 'unique', hjust=1, check_overlap = TRUE)+
geom_text(data = tangle %>%
filter(tree=="microbe"),
aes(label=label),stat = 'unique', hjust=0, check_overlap = TRUE)+
scale_colour_manual(values=c("NS"="steelblue", "paco"="darkorange1", "para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)+
scale_x_discrete(expand = expansion(add=c(0.8,0.8))) +
theme_void() +
scale_y_continuous(expand=c(0.005,0.005))+
theme(legend.position = "bottom") +
labs(colour="Significance:")
gg.trioza_carson_tangle <- p1 + gg.tangle + (p2 + scale_x_reverse())
gg.trioza_carson_tangle
pdf(file="figs/powellia_carsonella_tanglegram.pdf", width = 8, height = 11, paper="a4")
plot(gg.trioza_carson_tangle)
try(dev.off(), silent=TRUE)
Powellia ~ hostplant
plant.tree <- read.tree("sample_data/plant_tree.nwk")
plant.tree <- drop.tip(plant.tree , "Sophora_microphylla_-kowhai" )
#Prepare co-occurance matrix
coocur <- sample_data(ps2) %>%
as_tibble() %>%
filter(psyllid_genus == "Powellia") %>%
dplyr::select(psyllid_spp, hostplant_spp) %>%
filter(!is.na(psyllid_spp)) %>%
unique() %>%
mutate(psyllid_spp = psyllid_spp %>%
str_replace_all(" |-", "_"),
hostplant_spp = hostplant_spp %>%
str_replace_all(" |-", "_"),
presence = 1
) %>%
pivot_wider(names_from = "hostplant_spp",
values_from="presence",
values_fill = list(presence = 0)) %>%
column_to_rownames("psyllid_spp") %>%
t()
# H cophenetic distance
h_tree <- plant.tree
h_tree$tip.label <- h_tree$tip.label %>%
str_replace_all(" |-", "_")
h_tree <- drop.tip(h_tree, setdiff(h_tree$tip.label, rownames(coocur)))
h_dist <- sqrt(cophenetic(h_tree))
# P cophenetic distance
s_tree <- pruned.tree
s_tree$tip.label <- s_tree$tip.label %>%
str_replace_all(" |-", "_")
s_tree <- drop.tip(s_tree, setdiff(s_tree$tip.label, colnames(coocur)))
s_dist <- sqrt(cophenetic(s_tree))
coocur <- coocur[h_tree$tip.label, s_tree$tip.label]
# prepare paco data
D <- prepare_paco_data(H=h_dist, P=s_dist, HP=coocur)
# Add pcord
D <- add_pcoord(D, correction='none')
p_host <- ggplot(D$H_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw() +
ggtitle("H PCA")
p_para <- ggplot(D$P_PCo[,c('Axis.1', 'Axis.2')] %>% as.data.frame, aes(Axis.1, Axis.2)) +
geom_point() +
theme_bw()+
ggtitle("P PCA")
plot(p_host + p_para)
# run paco
paco_run <- PACo(D, nperm=999, seed=909, method='quasiswap', symmetric=FALSE) #Symetric - is one meant to track the evolution of another?
#print overall significance
print(paco_run$gof)
# Procrustes diagnostic plots
plot(paco_run$proc)
plot(paco_run$proc, kind=2)
# Get interaction-specific cophylogenetic contributions using jacknifing
paco_run <- paco_links(paco_run)
links <- data.frame(
joint=names(paco_run$jackknife),
values=unname(paco_run$jackknife)#,
#upper=unname(paco_run$jackknife$upper)
) %>%
mutate(OTU = joint) %>%
separate(OTU, into=c("hostplant_spp", "psyllid_spp"), sep="-", extra="merge") %>%
mutate(signif = case_when(
values < mean(.$values) ~ 1,
values > mean(.$values) ~ 0
))
# Plot links
links %>%
dplyr::rename(label = joint) %>%
mutate(label = as.factor(label),
label=fct_reorder(label, values, sum))%>%
arrange(-values) %>%
ggplot(aes(x=label, y=values, colour=as.factor(signif))) +
geom_point(show.legend = FALSE) +
#geom_errorbar(aes(ymin=values, ymax=upper)) +
geom_hline(yintercept = mean(links$values)) +
scale_color_manual(values=c("steelblue", "darkorange1")) +
theme_classic()+
theme(axis.text.x = element_text(angle=45, hjust=1))
dir.create("output/cophylogeny/trioza_hostplant")
write_csv(links, "output/cophylogeny/trioza_hostplant/psyllid_hostplant_links.csv")
write_csv(links %>%
group_by(psyllid_spp) %>%
summarise(values = mean(values)), "output/cophylogeny/trioza_hostplant/psyllid_weights.csv")
write_csv(links %>%
group_by(hostplant_spp) %>%
summarise(values = mean(values)), "output/cophylogeny/trioza_hostplant/hostplant_weights.csv")
#Get observed residuals of Procrustean superimposition
paco_residuals <- residuals_paco(paco_run$proc, type = "interaction")
# Visualise residuals
res <- data.frame(OTU=names(paco_residuals), values=unname(paco_residuals)) %>%
separate(OTU, into=c("hostplant_spp", "psyllid_spp"), sep="-", extra="merge")
ggplot(res, aes(x=values))+
geom_density(fill='grey70')+
theme_bw()+
xlab('Procrustes residuals')+
ylab('Frequency')
# Parafit run
PF_run <- parafit(h_dist, s_dist, t(coocur), nperm=999, test.links=TRUE, silent=TRUE)
PF_run$ParaFitGlobal
PF_run$p.global
PF_links <- as.data.frame(PF_run$link.table) %>%
left_join(enframe(names(PF_run$para.per.host), name = "Host", value="hostplant_spp") %>%
mutate(Host = as.numeric(Host))) %>%
left_join(enframe(names(PF_run$host.per.para), name = "Parasite", value="psyllid_spp") %>%
mutate(Parasite = as.numeric(Parasite)))%>%
mutate(signif = case_when(
p.F1 < 0.05 ~ 1,
p.F1 > 0.05 ~ 0
))
# Cophyloplot
coocur.lut <- which(coocur ==1, arr.ind=TRUE)
assoc <- cbind(rownames(coocur)[coocur.lut[,1]], colnames(coocur)[coocur.lut[,2]])
# Rotate the nodes using phytools
obj <- cophylo(tr1=h_tree, tr2=s_tree, assoc=assoc, rotate=TRUE)
# Extract the goods for ggtree
# plant tree
tree1 <- obj[["trees"]][[1]]
p1 <- ggtree(tree1, ladderize=FALSE, aes(colour=links))
weights_p1 <- p1$data %>%
left_join(links %>%
group_by(hostplant_spp) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = hostplant_spp)
)%>%
left_join(PF_links %>%
group_by(hostplant_spp) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = hostplant_spp)
) %>%
mutate(values = PA_values + PF_values)
## Get values for higher nodes
weights_p1 <- weights_p1 %>%
left_join(data.frame(node=weights_p1$node, links = weights_p1$node %>% purrr::map_dbl(average_descendants, tree=tree1, df=weights_p1)))
p1 <- p1 %<+% weights_p1 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# psyllid_tree
tree2 <- obj[["trees"]][[2]]
s_tree <- drop.tip(tree2, setdiff(tree2$tip.label, obj$assoc[,2]))
p2 <- ggtree(tree2 , ladderize=TRUE, aes(colour=links))
weights_p2 <- p2$data %>%
left_join(links %>%
group_by(psyllid_spp) %>%
summarise(PA_values = mean(signif)) %>%
dplyr::rename(label = psyllid_spp)
)%>%
left_join(PF_links %>%
group_by(psyllid_spp) %>%
summarise(PF_values = mean(signif)) %>%
dplyr::rename(label = psyllid_spp)
) %>%
mutate(values = PA_values + PF_values)
#Scale the atmetocranium and root nodes to be shorter
p2$data[p2$data$node %in% atmeto_node, "x"] <- max(p2$data$x)
p2$data[p2$data$node %in% root_node, "x"] <- 0.2 #root
p2$data$node[p2$data$node]
weights_p2 <- weights_p2 %>%
left_join(data.frame(node=weights_p2$node, links = weights_p2$node %>% purrr::map_dbl(average_descendants, tree=tree2, df=weights_p2)))
p2 <- p2 %<+% weights_p2 + geom_tippoint(aes(colour=links)) +
scale_color_gradient(low="steelblue", high="darkorange1") +
theme(legend.position = "none")
# Tanglegram
tangle <- obj$assoc %>%
as_data_frame() %>%
magrittr::set_colnames(c("label.x", "label.y")) %>%
left_join(links %>%
dplyr::select(label.x = hostplant_spp, label.y = psyllid_spp, signif_paco=signif)) %>%
left_join(PF_links %>%
dplyr::select(label.x = hostplant_spp, label.y = psyllid_spp, signif_para=signif)) %>%
left_join(p1$data %>% dplyr::select(label, y) %>% dplyr::rename(label.x = label), by="label.x") %>%
left_join(p2$data %>% dplyr::select(label, y) %>% dplyr::rename(label.y = label), by="label.y") %>%
rownames_to_column("assoc") %>%
rename_all(~str_replace(.x,pattern="\\.", replacement="_")) %>%
pivot_longer(ends_with(c("_x", "_y")),
names_to=c(".value", "tree"),
names_sep = "_"
) %>%
mutate(signif_paco = replace_na(signif_paco, 0),
signif_para = replace_na(signif_para, 0)) %>%
mutate(signif = case_when(
signif_paco==0 & signif_para==0 ~ "NS",
signif_paco==0 & signif_para==1 ~ "para",
signif_paco==1 & signif_para==0 ~ "paco",
signif_paco==1 & signif_para==1 ~ "both"
)) %>%
mutate(tree = tree %>%
str_replace("x", "host")%>%
str_replace("y", "microbe")) %>%
filter(!is.na(label))%>%
group_by(tree) %>%
mutate(y = y / max(y))%>%
mutate(label = label %>% str_replace_all("_", " "))
gg.tangle <- ggplot(tangle, aes(x=factor(tree, levels=c("microbe", "host")), y=y, group=assoc, colour=as.factor(signif))) +
geom_line(alpha=0.8) +
geom_text(data = tangle %>%
filter(tree=="host"),
aes(label=label),stat = 'unique', hjust=0, check_overlap = TRUE)+
geom_text(data = tangle %>%
filter(tree=="microbe"),
aes(label=label),stat = 'unique', hjust=1, check_overlap = TRUE)+
scale_colour_manual(values=c("NS"="steelblue", "paco"="darkorange1", "para"="#da2b91", "both"="#91da2b"), na.translate=FALSE)+
scale_x_discrete(expand = expansion(add=c(0.8,0.8))) +
scale_y_continuous(expand=c(0.005,0.005))+
theme_void() +
theme(legend.position = "bottom") +
labs(colour="Significance")
gg.powellia_tangle <- p2 + gg.tangle + (p1 + scale_x_reverse())
gg.powellia_tangle
pdf(file="figs/powellia_plant_tanglegram.pdf", width = 8, height = 11, paper="a4")
plot(gg.powellia_tangle)
try(dev.off(), silent=TRUE)
LS0tDQp0aXRsZTogIlBzeWxsaWQgbWljcm9iaW9tZSINCnN1YnRpdGxlOiAiU3RhdGlzdGljYWwgYW5hbHlzaXMiDQphdXRob3I6ICJBbGV4YW5kZXIgUGlwZXIiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgaW5jbHVkZXM6DQogICAgICBhZnRlcl9ib2R5OiBmb290ZXIuaHRtbA0KICAgIGhpZ2hsaWdodGVyOiBudWxsDQogICAgdGhlbWU6ICJmbGF0bHkiDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogRkFMU0UNCiAgICAgIHNtb290aF9zY3JvbGw6IFRSVUUNCiAgICBkZl9wcmludDogcGFnZWQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCg0KDQpgYGB7ciBzZXR1cCwgZXZhbD1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRX0NCiMgS25pdHIgZ2xvYmFsIHNldHVwIC0gY2hhbmdlIGV2YWwgdG8gdHJ1ZSB0byBydW4gY29kZQ0KbGlicmFyeShrbml0cikNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0UsZmlnLnNob3cgPSAiaG9sZCIsIGZpZy5rZWVwID0gImFsbCIpDQpvcHRzX2NodW5rJHNldChkZXYgPSAncG5nJykNCmBgYA0KDQoNCmBgYHtyIGxvYWQgcGFja2FnZXN9DQojIExvYWQgcGFja2FnZXMNCg0KI1NldCByZXF1aXJlZCBwYWNrYWdlcw0KLmNyYW5fcGFja2FnZXMgPC0gYygidGlkeXZlcnNlIiwgDQogICAgICAgICAgICAgICAgICAgICJwYXRjaHdvcmsiLCANCiAgICAgICAgICAgICAgICAgICAgInZlZ2FuIiwgDQogICAgICAgICAgICAgICAgICAgICJzZXFpbnIiLA0KICAgICAgICAgICAgICAgICAgICAiYXBlIiwgDQogICAgICAgICAgICAgICAgICAgICJzcCIsDQogICAgICAgICAgICAgICAgICAgICJtYXB0b29scyIsDQogICAgICAgICAgICAgICAgICAgICJyZ2VvcyIsDQogICAgICAgICAgICAgICAgICAgICJkYXRhLnRhYmxlIiwgDQogICAgICAgICAgICAgICAgICAgICJSQ29sb3JCcmV3ZXIiLA0KICAgICAgICAgICAgICAgICAgICAiZ2d0cmVlIiwgDQogICAgICAgICAgICAgICAgICAgICJjYXN0b3IiLCANCiAgICAgICAgICAgICAgICAgICAgInBpY2FudGUiLA0KICAgICAgICAgICAgICAgICAgICAicGh5bG9zaWduYWwiLCANCiAgICAgICAgICAgICAgICAgICAgImFkZXBoeWxvIiwNCiAgICAgICAgICAgICAgICAgICAgImRlbmRleHRlbmQiLA0KICAgICAgICAgICAgICAgICAgICAicGFjbyIsDQogICAgICAgICAgICAgICAgICAgICJ0aWR5dGV4dCIsDQogICAgICAgICAgICAgICAgICAgICJwaHl0b29scyIsDQogICAgICAgICAgICAgICAgICAgICJlY29kaXN0IikNCi5iaW9jX3BhY2thZ2VzIDwtIGMoImRhZGEyIiwNCiAgICAgICAgICAgICAgICAgICAgIm1pY3JvYmlvbWUiLA0KICAgICAgICAgICAgICAgICAgICAicGh5bG9zZXEiLCANCiAgICAgICAgICAgICAgICAgICAgIkRFQ0lQSEVSIiwNCiAgICAgICAgICAgICAgICAgICAgIkJpb3N0cmluZ3MiLA0KICAgICAgICAgICAgICAgICAgICAiU2hvcnRSZWFkIiwgDQogICAgICAgICAgICAgICAgICAgICJwaGlsciIsDQogICAgICAgICAgICAgICAgICAgICJBTERFeDIiKQ0KDQojIEluc3RhbGwgYWxsIG1pc3NpbmcgcGFja2FnZXMNCi5pbnN0IDwtIC5jcmFuX3BhY2thZ2VzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkNCmlmKGFueSghLmluc3QpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKC5jcmFuX3BhY2thZ2VzWyEuaW5zdF0pDQp9DQouaW5zdCA8LSAuYmlvY19wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpDQppZihhbnkoIS5pbnN0KSkgew0KICBpZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQ0KICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCiAgQmlvY01hbmFnZXI6Omluc3RhbGwoLmJpb2NfcGFja2FnZXNbIS5pbnN0XSwgYXNrID0gRikNCn0NCg0KI0xvYWQgYWxsIHBhY2thZ2VzDQpzYXBwbHkoYyguY3Jhbl9wYWNrYWdlcywuYmlvY19wYWNrYWdlcyksIHJlcXVpcmUsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCg0KIyBHaXRodWIgcGFja2FnZXMNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWxleHBpcGVyL3RheHJldHVybiIpDQpsaWJyYXJ5KHRheHJldHVybikNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWxleHBpcGVyL3NlcWF0ZXVycyIpDQpsaWJyYXJ5KHNlcWF0ZXVycykNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigibWlrZW1jL3NwZWVkeXNlcSIpDQpsaWJyYXJ5KHNwZWVkeXNlcSkNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YignZ2dsb29yL0NvRGFTZXEvQ29EYVNlcScpDQpsaWJyYXJ5KENvRGFTZXEpDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImVhc3lzdGF0cy9yZXBvcnQiKQ0KbGlicmFyeShyZXBvcnQpDQoNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigicG1hcnRpbmV6YXJiaXp1L3BhaXJ3aXNlQWRvbmlzL3BhaXJ3aXNlQWRvbmlzIikNCmxpYnJhcnkocGFpcndpc2VBZG9uaXMpDQoNCiNTb3VyY2UgaW50ZXJuYWwgZnVuY3Rpb25zDQpzb3VyY2UoJ1IvQkRUVC5SJykNCnNvdXJjZSgnUi9waHlsb3N5bWJpb3Npcy5SJykNCnNvdXJjZSgnUi9oZWxwZXJfZnVuY3Rpb25zLlInKQ0Kc291cmNlKCdSL3RoZW1lcy5SJykNCm9wdGlvbnMoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KYGBgDQoNCiMgUmVhZCBpbiBwaHlsb3NlcSBvYmplY3QNCg0KYGBge3IgcmVhZCBpbiBwaHlsb3NlcX0NCnBzMiA8LSByZWFkUkRTKCJvdXRwdXQvcmRzL3BzMi5yZHMiKQ0KDQojIGV4cG9ydCBmaWx0ZXJlZA0KZGlyLmNyZWF0ZSgib3V0cHV0L290dV90YWJsZXMvZmlsdGVyZWQiKQ0Kc2VxYXRldXJzOjpzdW1tYXJpc2VfdGF4YShwczIsICJzcGVjaWVzIiwgIlNhbXBsZUlEIikgJT4lDQogIHNwcmVhZChrZXk9IlNhbXBsZUlEIiwgdmFsdWU9InRvdGFsUkEiKSAlPiUNCiAgd3JpdGUuY3N2KGZpbGUgPSAib3V0cHV0L290dV90YWJsZXMvZmlsdGVyZWQvZmlsdGVyZWRfc3BwX3N1bS5jc3YiKQ0KDQpzZXFhdGV1cnM6OnN1bW1hcmlzZV90YXhhKHBzMiwgImdlbnVzIiwgIlNhbXBsZUlEIikgJT4lDQogIHNwcmVhZChrZXk9IlNhbXBsZUlEIiwgdmFsdWU9InRvdGFsUkEiKSAlPiUNCiAgd3JpdGUuY3N2KGZpbGUgPSAib3V0cHV0L290dV90YWJsZXMvZmlsdGVyZWQvZmlsdGVyZWRfZ2VuX3N1bS5jc3YiKQ0KDQojUmVuYW1lIHRheGEgLSBvbmx5IGtlZXAgZmlyc3QgMzAgY2hhcmFjdGVycw0KdGF4YV9uYW1lcyhwczIpIDwtIHN1YnN0cihwYXN0ZTAoIlNWIiwgc2VxKG50YXhhKHBzMikpLCItIix0YXhfdGFibGUocHMyKVssN10pLCAxLDMwKQ0KDQojQ2hlY2sgY2Fyc29uZWxsYSBwcmVzZW5jZQ0KY2FycyA8LSBzcGVlZHlzZXE6OnBzbWVsdChwczIpICU+JQ0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMCkgJT4lDQogIGdyb3VwX2J5KHBzeWxsaWRfc3BwKSAlPiUNCiAgc3VtbWFyaXNlKG4gPSBjb3VudChnZW51cz09IkNhbmRpZGF0dXMgQ2Fyc29uZWxsYSIsIG5hLnJtID0gVFJVRSkpDQoNCmBgYA0KDQojIyBDcmVhdGUgc3BlY2llcyBtZXJnZWQgdGFibGUNCg0KYGBge3IgbWVyZ2Vkc3BwfQ0KIyBNZXJnZSBzcGVjaWVzIGZvciBiZXRhIGRpdmVyc2l0eQ0KcHMuc3BwbWVyZ2VkIDwtIHBzMiAlPiUNCiAgICBtZXJnZV9zYW1wbGVzKGdyb3VwID0gInBzeWxsaWRfc3BwIiwgZnVuPW1lYW4pDQoNCiNUaGlzIGxvc2VzIHRoZSBzYW1wbGUgbWV0YWRhdGEgLSBOZWVkIHRvIGFkZCBpdCBhZ2lhbg0Kc2FtcGxlX2RhdGEocHMuc3BwbWVyZ2VkKSA8LSBzYW1wbGVfZGF0YShwczIpICU+JQ0KICBhcygibWF0cml4IikgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgZmlsdGVyKCFkdXBsaWNhdGVkKHBzeWxsaWRfc3BwKSkgJT4lDQogIG1hZ3JpdHRyOjpzZXRfcm93bmFtZXMoLiRwc3lsbGlkX3NwcCkNCg0Kc2VxcyA8LSByZWZzZXEocHMyKQ0KdHJlZSA8LSBwaHlfdHJlZShwczIpDQojbWFrZSBuZXcgcGh5bG9zZXEgb2JqZWN0DQpwczMgPC0gcGh5bG9zZXEodGF4X3RhYmxlKHBzLnNwcG1lcmdlZCksDQogICAgICAgICAgICAgICBzYW1wbGVfZGF0YShwcy5zcHBtZXJnZWQpLA0KICAgICAgICAgICAgICAgb3R1X3RhYmxlKG90dV90YWJsZShwcy5zcHBtZXJnZWQpLCB0YXhhX2FyZV9yb3dzID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgcmVmc2VxKHNlcXMpLA0KICAgICAgICAgICAgICAgcGh5X3RyZWUodHJlZSkpDQoNCmBgYA0KDQojIyBSZWFkIGluIHBzeWxsaWQgcGh5bG9nZW55DQpgYGB7UiBwc3lsbGlkIHBoeWxvZ2VueX0NCnBzeWxsaWRfdHJlZSA8LSByZWFkLnRyZWUodGV4dD1yZWFkTGluZXMoInNhbXBsZV9kYXRhL3BzeWxsaWRfYmVhc3RfdHJlZS5ud2siKSkNCg0KIyBNYXRjaCBuYW1lcyB3aXRoIHNhbXBsZSBzaGVldA0KcHN5bGxpZF90cmVlJHRpcC5sYWJlbCA8LSBwc3lsbGlkX3RyZWUkdGlwLmxhYmVsICU+JQ0KICBzdHJfc3F1aXNoKCkgJT4lDQogIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSJcXC4iLCByZXBsYWNlbWVudD0iICIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iQWNpenppYSBoYWthZSIsIHJlcGxhY2VtZW50PSJBY2l6emlhIGhha2VhZSIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iUE9MTEVOSVNMQU5EIiwgcmVwbGFjZW1lbnQ9IlBPTExFTiBJU0xBTkQiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IkN0ZW5hcnl0YWluYSBmdWNoc2lhZSQiLCByZXBsYWNlbWVudD0iQ3RlbmFyeXRhaW5hIGZ1Y2hzaWEgQSIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iQ3RlbmFyeXRhaW5hIGZ1Y2hzaWFlQiIsIHJlcGxhY2VtZW50PSJDdGVuYXJ5dGFpbmEgZnVjaHNpYSBCIikgJT4lDQogIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSJDdGVuYXJ5dGFpbmEgZnVjaHNpYWVDIiwgcmVwbGFjZW1lbnQ9IkN0ZW5hcnl0YWluYSBmdWNoc2lhIEMiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IkN0ZW5hcnl0YWluYSBjbGF2YXRhIiwgcmVwbGFjZW1lbnQ9IkN0ZW5hcnl0YWluYSBjbGF2YXRhIHNwICIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iQ3RlbmFyeXRhaW5hIGNsYXZhdGEgc3AgJCIsIHJlcGxhY2VtZW50PSJDdGVuYXJ5dGFpbmEgY2xhdmF0YSBzcCBBIikgJT4lDQogIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSJDdGVuYXJ5dGFpbmEgc3AkIiwgcmVwbGFjZW1lbnQ9IkN0ZW5hcnl0YWluYSBzcCAiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IkN0ZW5hcnl0YWluYSBzcEEiLCByZXBsYWNlbWVudD0iQ3RlbmFyeXRhaW5hIHNwIEEiKSAlPiUNCiAgICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iQ3RlbmFyeXRhaW5hIHNwQiIsIHJlcGxhY2VtZW50PSJDdGVuYXJ5dGFpbmEgc3AgQiIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iQ3RlbmFyeXRhaW5hIHVua25vd24iLCByZXBsYWNlbWVudD0iQ3RlbmFyeXRhaW5hIGluc3VsYXJpcyIpICU+JSAgDQogIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSJQc3lsbGEgYXBpY2FsaXNBIiwgcmVwbGFjZW1lbnQ9IlBzeWxsYSBmcm9kb2JhZ2dpbnNpIikgJT4lDQogIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSJQc3lsbGEgYXBpY2FsaXNCIiwgcmVwbGFjZW1lbnQ9IlBzeWxsYSBhcGljYWxpcyIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iY2FybWljaGFlbGlhZSIsIHJlcGxhY2VtZW50PSJjYXJtaWNoYWVsaWFlICIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iVHJpb3phIHNwIiwgcmVwbGFjZW1lbnQ9IlRyaW96YSBzcCAiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IlRyaW96YSBhY3V0YUIiLCByZXBsYWNlbWVudD0iVHJpb3phIGFjdXRhIEIiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IlRyaW96YSBnb3VybGF5IiwgcmVwbGFjZW1lbnQ9IlRyaW96YSBnb3VybGF5aSIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iQlJFTkRBTUFZIiwgcmVwbGFjZW1lbnQ9IkJSRU5EQSBNQVkiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IlBSSUNFUyIsIHJlcGxhY2VtZW50PSJQUklDRVMgVkFMTEVZIikgJT4lICANCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IkFjaXp6aWEgc3AiLCByZXBsYWNlbWVudD0iQWNpenppYSBlcnJhYnVuZGEiKSAlPiUgDQogIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSJUcmlvemEgIiwgcmVwbGFjZW1lbnQ9IlBvd2VsbGlhICIpICU+JQ0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybj0iVHJpb3ppZCBzcCIsIHJlcGxhY2VtZW50PSJDYXN1YXJpbmljb2xhIHNwIikgJT4lDQogIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSJQb3dlbGxpYSBhZHZlbnRpY2lhIiwgcmVwbGFjZW1lbnQ9IlRyaW96YSBhZHZlbnRpY2lhIikgJT4lDQogIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuPSJQb3dlbGxpYSBjdXJ0YSIsIHJlcGxhY2VtZW50PSJUcmlvemEgY3VydGEiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm49IiAiLCByZXBsYWNlbWVudD0iXyIpICU+JQ0KICB0cmltd3Mod2hpY2g9InJpZ2h0IikNCg0KDQojIFN1YnNldCB0byBvbmx5IHRob3NlIGluIHNhbXBsZSBkYXRhDQpzZXRkaWZmKHBzeWxsaWRfdHJlZSR0aXAubGFiZWwsc2FtcGxlX2RhdGEocHMyKSRwc3lsbGlkX3NwcCkNCnNldGRpZmYoc2FtcGxlX2RhdGEocHMyKSRwc3lsbGlkX3NwcCwgcHN5bGxpZF90cmVlJHRpcC5sYWJlbCkNCg0KcHN5bGxpZF90cmVlJHRpcC5sYWJlbFshcHN5bGxpZF90cmVlJHRpcC5sYWJlbCAlaW4lIHNhbXBsZV9kYXRhKHBzMikkcHN5bGxpZF9zcHBdDQpwc3lsbGlkX3RyZWUkdGlwLmxhYmVsWyFzYW1wbGVfZGF0YShwczIpJHBzeWxsaWRfc3BwICVpbiUgcHN5bGxpZF90cmVlJHRpcC5sYWJlbCBdDQpwcnVuZWQudHJlZSA8LSBkcm9wLnRpcChwc3lsbGlkX3RyZWUsIHBzeWxsaWRfdHJlZSR0aXAubGFiZWxbIXBzeWxsaWRfdHJlZSR0aXAubGFiZWwgJWluJSBzYW1wbGVfZGF0YShwczIpJHBzeWxsaWRfc3BwXSApDQoNCmBgYA0KDQoNCiMjIFN1bW1hcnkgc3RhdGlzdGljcw0KDQpgYGB7ciBzdW0gdGF4YX0NCiMgTiB1bmlxdWUgc3BlY2llcyBhbmQgc2FtcGxlcw0Kc3BlZWR5c2VxOjpwc21lbHQocHMyKSAlPiUNCiAgc3VtbWFyaXNlKG50YXhhPSBuX2Rpc3RpbmN0KHBzeWxsaWRfc3BwKSwgbl9zYW1wbGVzID0gbl9kaXN0aW5jdChTYW1wbGVfTmFtZSksIG5faG9zdHBsYW50cyA9IG5fZGlzdGluY3QoaG9zdHBsYW50X3NwcCkpDQoNCiMgU3ByZWFkIG9mIHJlYWRzDQpzcGVlZHlzZXE6OnBzbWVsdChwczIpICU+JQ0KICBncm91cF9ieShTYW1wbGVfTmFtZSkgJT4lDQogIHN1bW1hcmlzZShBYnVuZGFuY2UgPSBzdW0oQWJ1bmRhbmNlKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKEFidW5kYW5jZSksIA0KICAgICAgICAgICAgc2UgPSBzZChBYnVuZGFuY2UpL3NxcnQobGVuZ3RoKEFidW5kYW5jZSkpLA0KICAgICAgICAgICAgbWF4ID0gbWF4KEFidW5kYW5jZSksDQogICAgICAgICAgICBtaW4gPSBtaW4oQWJ1bmRhbmNlKSkNCg0KIyBTcHJlYWQgb2YgQVNWcw0Kc3BlZWR5c2VxOjpwc21lbHQocHMyKSAlPiUNCiAgZ3JvdXBfYnkoU2FtcGxlX05hbWUpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKEFidW5kYW5jZSA+IDApICU+JQ0KICBzdW1tYXJpc2UoY291bnRzID0gbl9kaXN0aW5jdChPVFUpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBzdW1tYXJpc2UobWVhbiA9IG1lYW4oY291bnRzKSwgDQogICAgICAgICAgICBzZSA9IHNkKGNvdW50cykvc3FydChsZW5ndGgoY291bnRzKSksDQogICAgICAgICAgICBtYXggPSBtYXgoY291bnRzKSwNCiAgICAgICAgICAgIG1pbiA9IG1pbihjb3VudHMpKQ0KDQojRnJhY3Rpb24gb2YgcmVhZHMgYXNzaWduZWQgdG8gZWFjaCB0YXhvbm9taWMgcmFuaw0Kc3BlZWR5c2VxOjpwc21lbHQocHMyKSAlPiUNCiAgZ2F0aGVyKCJSYW5rIiwiTmFtZSIscmFua19uYW1lcyhwczIpKSAlPiUNCiAgZ3JvdXBfYnkoUmFuaykgJT4lIA0KICBtdXRhdGUoTmFtZSA9IHJlcGxhY2UoTmFtZSwgc3RyX2RldGVjdChOYW1lLCAiX18iKSxOQSkpICU+JSAjIFRoaXMgbGluZSB0dXJucyB0aGUgIl9fIiB3ZSBhZGRlZCB0byBsb3dlciByYW5rcyBiYWNrIHRvIE5BJ3MNCiAgZHBseXI6OnN1bW1hcmlzZShSZWFkc19jbGFzc2lmaWVkID0gc3VtKEFidW5kYW5jZSAqICFpcy5uYShOYW1lKSkpICU+JQ0KICBtdXRhdGUoRnJhY19yZWFkcyA9IFJlYWRzX2NsYXNzaWZpZWQgLyBzdW0oc2FtcGxlX3N1bXMocHMyKSkpICU+JQ0KICBtdXRhdGUoUmFuayA9IGZhY3RvcihSYW5rLCByYW5rX25hbWVzKHBzMikpKSAlPiUNCiAgYXJyYW5nZShSYW5rKQ0KDQojRnJhY3Rpb24gb2YgQVNWJ3MgYXNzaWduZWQgdG8gZWFjaCB0YXhvbm9taWMgcmFuaw0KdGF4X3RhYmxlKHBzMikgJT4lDQogIGFzKCJtYXRyaXgiKSAlPiUNCiAgYXNfdGliYmxlKHJvd25hbWVzPSJPVFUiKSAlPiUNCiAgZ2F0aGVyKCJSYW5rIiwiTmFtZSIscmFua19uYW1lcyhwczIpKSAlPiUNCiAgZ3JvdXBfYnkoUmFuaykgJT4lDQogIG11dGF0ZShOYW1lID0gcmVwbGFjZShOYW1lLCBzdHJfZGV0ZWN0KE5hbWUsICJfXyIpLCBOQSkpICU+JSAjIFRoaXMgbGluZSB0dXJucyB0aGUgIl9fIiB3ZSBhZGRlZCB0byBsb3dlciByYW5rcyBiYWNrIHRvIE5BJ3MNCiAgZHBseXI6OnN1bW1hcmlzZShPVFVzX2NsYXNzaWZpZWQgPSBzdW0oIWlzLm5hKE5hbWUpKSkgJT4lDQogIG11dGF0ZShGcmFjX09UVXMgPSBPVFVzX2NsYXNzaWZpZWQgLyBudGF4YShwczIpKSAlPiUNCiAgbXV0YXRlKFJhbmsgPSBmYWN0b3IoUmFuaywgcmFua19uYW1lcyhwczIpKSkgJT4lDQogIGFycmFuZ2UoUmFuaykNCg0KIyBVbmlxdWUgdGF4YSBhdCBlYWNoIHJhbmsNCnNwZWVkeXNlcTo6cHNtZWx0KHBzMikgJT4lDQogIGRwbHlyOjpzZWxlY3QocmFua19uYW1lcyhwczIpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlJhbmsiLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lDQogIG11dGF0ZSh2YWx1ZSA9IGNhc2Vfd2hlbigNCiAgICBzdHJfZGV0ZWN0KHZhbHVlLCAiX18iKSB+IGFzLmNoYXJhY3RlcihOQSksDQogICAgIXN0cl9kZXRlY3QodmFsdWUsICJfXyIpIH4gdmFsdWUNCiAgKSkgJT4lDQogIGRyb3BfbmEoKSAlPiUNCiAgZ3JvdXBfYnkoUmFuaykgJT4lDQogIHN1bW1hcmlzZV9hbGwoZnVucyhuX2Rpc3RpbmN0KSkgJT4lDQogIG11dGF0ZShSYW5rID0gZmFjdG9yKFJhbmssIHJhbmtfbmFtZXMocHMyKSkpICU+JQ0KICBhcnJhbmdlKFJhbmspDQoNCiMgRWFjaCBkaWZmZXJlbnQgcGh5bHVtIHJhbmtlZCBieSBpdHMgb3ZlcmFsbCByZWxhdGl2ZSBhYnVuZGFuY2UNCnNhbXBsZV9kYXRhKHBzMikgJT4lDQogIGFzKCJtYXRyaXgiKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBwdWxsKHBzeWxsaWRfc3BwKSAlPiUNCiAgdGFibGUoKSAlPiUNCiAgc29ydCgpDQojIFRyYW5zZm9ybSB0byBwZXIgc2FtcGxlIHJlbGF0aXZlIGFidW5kYW5jZSwgdGhlbiB0cmFuc2Zvcm0gdG8gd2hvbGUgZGF0YXNldCByZWxhdGl2ZSBhYnVuZGFuY2UNCmBgYA0KDQojIyBQcmV2YWxlbmNlIC8gQWJ1bmRhbmNlIHN1bW1hcnkNCg0KVmlldyBwcmV2YWxlbmNlIG9mIGRpZmZlcmVudCBwaHlsYSBhY3Jvc3MgdGhlIGRhdGFzZXQNCg0KYGBge3IgcHJldmFsZW5jZS1hc3Nlc3NtZW50fQ0KIyBDYWxjdWxhdGUgdGF4b24gcHJldmFsZW5jZSBhY3Jvc3MgdGhlIGRhdGEgc2V0IGF0IE9UVSBsZXZlbA0KcHJldmRmIDwtIGFwcGx5KFggPSBvdHVfdGFibGUocHMyKSwgTUFSR0lOID0gaWZlbHNlKHRheGFfYXJlX3Jvd3MocHMyKSwgeWVzID0gMSwgbm8gPSAyKSwgRlVOID0gZnVuY3Rpb24oeCl7c3VtKHggPiAwKX0pDQpwcmV2ZGYgPC0gZGF0YS5mcmFtZShQcmV2YWxlbmNlID0gcHJldmRmLCANCiAgICAgICAgICAgICAgICAgICAgIFRvdGFsQWJ1bmRhbmNlID0gdGF4YV9zdW1zKHBzMiksDQogICAgICAgICAgICAgICAgICAgICB0YXhfdGFibGUocHMyKSkNCiNQcmV2YWxlbmNlIHBsb3QNCmdnLnByZXYgPC0gc3Vic2V0KHByZXZkZiwgcGh5bHVtICVpbiUgZ2V0X3RheGFfdW5pcXVlKHBzMiwgInBoeWx1bSIpKSAlPiUNCiAgZ2dwbG90KGFlcyhUb3RhbEFidW5kYW5jZSwgUHJldmFsZW5jZSAvIG5zYW1wbGVzKHBzMiksY29sb3I9b3JkZXIpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC43KSArDQogIHNjYWxlX3hfbG9nMTAoKSArDQogIHhsYWIoIlRvdGFsIEFidW5kYW5jZSIpICsgeWxhYigiUHJldmFsZW5jZSBbRnJhYy4gU2FtcGxlc10iKSArDQogIGZhY2V0X3dyYXAofnBoeWx1bSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArDQogIGdndGl0bGUoIlBoeWx1bSBQcmV2YWxlbmNlIGluIEFsbCBTYW1wbGVzXG5Db2xvcmVkIGJ5IE9yZGVyIikNCg0KcGRmKGZpbGU9ImZpZ3MvcHJldmFsZW5jZS5wZGYiLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA4ICwgcGFwZXI9ImE0ciIpDQogIHBsb3QoZ2cucHJldikNCnRyeShkZXYub2ZmKCksIHNpbGVudD1UUlVFKQ0KICANCiAgDQojIFByZXZhbGVjbmUgYXQgcGh5bHVtDQpwcy5waHlsdW0gPC0gc3BlZWR5c2VxOjp0YXhfZ2xvbShwczIsIHRheHJhbms9InBoeWx1bSIpDQpwcmV2ZGZfcGh5bHVtIDwtIGFwcGx5KFggPSBvdHVfdGFibGUocHMucGh5bHVtICksIE1BUkdJTiA9IGlmZWxzZSh0YXhhX2FyZV9yb3dzKHBzLnBoeWx1bSApLCB5ZXMgPSAxLCBubyA9IDIpLCBGVU4gPSBmdW5jdGlvbih4KXtzdW0oeCA+IDApfSkNCnByZXZkZl9waHlsdW0gPC0gZGF0YS5mcmFtZShQcmV2YWxlbmNlID0gcHJldmRmX3BoeWx1bSwgDQogICAgICAgICAgICAgICAgICAgICBUb3RhbEFidW5kYW5jZSA9IHRheGFfc3Vtcyhwcy5waHlsdW0pLA0KICAgICAgICAgICAgICAgICAgICAgdGF4X3RhYmxlKHBzLnBoeWx1bSkpICU+JQ0KICBkcGx5cjo6bXV0YXRlKFJBID0gVG90YWxBYnVuZGFuY2UgLyBzdW0oVG90YWxBYnVuZGFuY2UpKSAlPiUNCiAgcmVtb3ZlX3Jvd25hbWVzKCkgJT4lDQogIG1hZ3JpdHRyOjpzZXRfcm93bmFtZXMoLiRwaHlsdW0pICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1yYW5rX25hbWVzKHBzLnBoeWx1bSkpDQoNCiMgUHJldmFsZW5jZSB3aXRoaW4gUHJvdGVvYmFjdGVyaWENCnBzLnByb3QgPC0gc3Vic2V0X3RheGEocHMyLCBwaHlsdW09PSJQcm90ZW9iYWN0ZXJpYSIpICU+JQ0KICAgICAgICAgIHNwZWVkeXNlcTo6dGF4X2dsb20odGF4cmFuaz0ib3JkZXIiKQ0KcHJldmRmX3Byb3QgPC0gYXBwbHkoWCA9IG90dV90YWJsZShwcy5wcm90ICksIE1BUkdJTiA9IGlmZWxzZSh0YXhhX2FyZV9yb3dzKHBzLnByb3QgKSwgeWVzID0gMSwgbm8gPSAyKSwgRlVOID0gZnVuY3Rpb24oeCl7c3VtKHggPiAwKX0pDQpwcmV2ZGZfcHJvdCA8LSBkYXRhLmZyYW1lKFByZXZhbGVuY2UgPSBwcmV2ZGZfcHJvdCwgDQogICAgICAgICAgICAgICAgICAgICBUb3RhbEFidW5kYW5jZSA9IHRheGFfc3Vtcyhwcy5wcm90KSwNCiAgICAgICAgICAgICAgICAgICAgIHRheF90YWJsZShwcy5wcm90KSkgJT4lDQogIGRwbHlyOjptdXRhdGUoUkEgPSBUb3RhbEFidW5kYW5jZSAvIHN1bShUb3RhbEFidW5kYW5jZSkpICU+JQ0KICByZW1vdmVfcm93bmFtZXMoKSAlPiUNCiAgbWFncml0dHI6OnNldF9yb3duYW1lcyguJG9yZGVyKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtcmFua19uYW1lcyhwcy5wcm90KSkNCiAgDQojIEdlbnVzIFByZXZhbGVuY2UNCnBzLmdlbiA8LSBzcGVlZHlzZXE6OnRheF9nbG9tKHBzMiwgdGF4cmFuaz0iZ2VudXMiKSANCnByZXZkZl9nZW4gPC0gYXBwbHkoWCA9IG90dV90YWJsZShwcy5nZW4gKSwgTUFSR0lOID0gaWZlbHNlKHRheGFfYXJlX3Jvd3MocHMuZ2VuICksIHllcyA9IDEsIG5vID0gMiksIEZVTiA9IGZ1bmN0aW9uKHgpe3N1bSh4ID4gMCl9KQ0KcHJldmRmX2dlbiA8LSBkYXRhLmZyYW1lKFByZXZhbGVuY2UgPSBwcmV2ZGZfZ2VuLCANCiAgICAgICAgICAgICAgICAgICAgIFRvdGFsQWJ1bmRhbmNlID0gdGF4YV9zdW1zKHBzLmdlbiksDQogICAgICAgICAgICAgICAgICAgICB0YXhfdGFibGUocHMuZ2VuKSkgJT4lDQogIGRwbHlyOjptdXRhdGUoUkEgPSBUb3RhbEFidW5kYW5jZSAvIHN1bShUb3RhbEFidW5kYW5jZSkpICU+JQ0KICByZW1vdmVfcm93bmFtZXMoKSAlPiUNCiAgbXV0YXRlKGdlbnVzID0gbWFrZS51bmlxdWUoZ2VudXMpKSAlPiUNCiAgbWFncml0dHI6OnNldF9yb3duYW1lcyguJGdlbnVzKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtcmFua19uYW1lcyhwcy5nZW4pKQ0KDQojIEdlbnVzIFByZXZhbGVuY2UgYWNyb3NzIHNwZWNpZXMgcmF0aGVyIHRoYW4gc3BlY2ltZW5zDQpwcy5nZW4gPC0gc3BlZWR5c2VxOjp0YXhfZ2xvbShwczMsIHRheHJhbms9ImdlbnVzIikgDQpwcmV2ZGZfZ2VuIDwtIGFwcGx5KFggPSBvdHVfdGFibGUocHMuZ2VuICksIE1BUkdJTiA9IGlmZWxzZSh0YXhhX2FyZV9yb3dzKHBzLmdlbiApLCB5ZXMgPSAxLCBubyA9IDIpLCBGVU4gPSBmdW5jdGlvbih4KXtzdW0oeCA+IDApfSkNCnByZXZkZl9nZW4gPC0gZGF0YS5mcmFtZShQcmV2YWxlbmNlID0gcHJldmRmX2dlbiwgDQogICAgICAgICAgICAgICAgICAgICBUb3RhbEFidW5kYW5jZSA9IHRheGFfc3Vtcyhwcy5nZW4pLA0KICAgICAgICAgICAgICAgICAgICAgdGF4X3RhYmxlKHBzLmdlbikpICU+JQ0KICBkcGx5cjo6bXV0YXRlKFJBID0gVG90YWxBYnVuZGFuY2UgLyBzdW0oVG90YWxBYnVuZGFuY2UpKSAlPiUNCiAgcmVtb3ZlX3Jvd25hbWVzKCkgJT4lDQogIG11dGF0ZShnZW51cyA9IG1ha2UudW5pcXVlKGdlbnVzKSkgJT4lDQogIG1hZ3JpdHRyOjpzZXRfcm93bmFtZXMoLiRnZW51cykgJT4lDQogIGRwbHlyOjpzZWxlY3QoLXJhbmtfbmFtZXMocHMuZ2VuKSkNCg0KIyBQcmV2YWxlbmNlIG9mIHN5bWJpb250cyBhY3Jvc3MgcHN5bGxpZCBzcGVjaWVzDQpzcGVlZHlzZXE6OnBzbWVsdChwczIpICU+JQ0KICBtdXRhdGUodG90YWxfc3BwICA9IG5fZGlzdGluY3QocHN5bGxpZF9zcHApLCB0b3RhbF9zcGVjaW1lbiA9IG5fZGlzdGluY3QoU2FtcGxlX05hbWUpKSAlPiUNCiAgZmlsdGVyKEFidW5kYW5jZSA+IDApICU+JQ0KICBmaWx0ZXIoZ2VudXMgJWluJSBjKCJDYW5kaWRhdHVzIENhcnNvbmVsbGEiLCAiQXJzZW5vcGhvbnVzIiwgIlNvZGFsaXMiKSkgJT4lDQogIGdyb3VwX2J5KGdlbnVzLCB0b3RhbF9zcHAsIHRvdGFsX3NwZWNpbWVuKSU+JQ0KICBzdW1tYXJpc2Uobl9zcGVjaWVzID0gbl9kaXN0aW5jdChwc3lsbGlkX3NwcCksIG5fc3BlY2ltZW4gPSBuX2Rpc3RpbmN0KFNhbXBsZV9OYW1lKSkgJT4lDQogIHVuZ3JvdXAoKSU+JQ0KICBtdXRhdGUocHJvcF9zcGVjaWVzID0gbl9zcGVjaWVzIC8gdG90YWxfc3BwLA0KICAgICAgICAgcHJvcF9zcGVjaW1lbiA9IG5fc3BlY2ltZW4gLyB0b3RhbF9zcGVjaW1lbikNCg0KIyBOdW1iZXIgb2Ygc3ltYmlvbnQgT1RVcyBwZXIgcHN5bGxpZCBzcGVjaWVzDQpzcGVlZHlzZXE6OnBzbWVsdChwczIpICU+JQ0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMCkgJT4lDQogIGZpbHRlcihnZW51cyAlaW4lIGMoIkNhbmRpZGF0dXMgQ2Fyc29uZWxsYSIsICJBcnNlbm9waG9udXMiLCAiU29kYWxpcyIpKSAlPiUNCiAgZ3JvdXBfYnkocHN5bGxpZF9zcHAsIGdlbnVzKSAlPiUNCiAgc3VtbWFyaXNlKG4gPSBuX2Rpc3RpbmN0KE9UVSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwc3lsbGlkX3NwcCwgeSA9IG4sIGZpbGw9Z2VudXMpKSsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkrDQogIGZhY2V0X2dyaWQoZ2VudXN+LikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBsYWJzKHggPSAiUHN5bGxpZCBTcGVjaWVzIiwNCiAgICAgICB5ID0gIk51bWJlciBvZiBkaXN0aW5jdCBBU1ZzIikNCg0Kc2UgPC0gZnVuY3Rpb24oeCkgc3FydCh2YXIoeCkvbGVuZ3RoKHgpKQ0KDQojIE1lYW4gYWJ1bmRhbmNlIG9mICBnZW5lcmENCmdlbmVyYV9hYnVuZCA8LSBzcGVlZHlzZXE6OnBzbWVsdChwczIpICU+JQ0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMCkgJT4lDQogIGdyb3VwX2J5KFNhbXBsZUlEKSAlPiUNCiAgICBtdXRhdGVfYXQodmFycyhBYnVuZGFuY2UpLCB+IC4gLyBzdW0oLikgKSAlPiUNCiAgdW5ncm91cCAlPiUNCiAgZ3JvdXBfYnkoZ2VudXMpICU+JQ0KICBzdW1tYXJpc2UobWVhbl9yYSA9IG1lYW4oQWJ1bmRhbmNlKSwgdXBwZXIgPSBtYXgoQWJ1bmRhbmNlKSwgbG93ZXIgPSBtaW4oQWJ1bmRhbmNlKSwgc2UgPSBzZShBYnVuZGFuY2UpKSANCiAgDQpgYGANCg0KIyMgQWxwaGEgZGl2ZXJzaXR5IG1ldHJpY3MNCg0KYGBge3IgYWxwaGEgZGl2ZXJzaXR5fQ0KZGlyLmNyZWF0ZSgib3V0cHV0L2FscGhhIikNCiMgR2V0IHJpY2huZXNzIG1lYXN1cmVzDQpyaWNobmVzcyA8LSBwaHlsb3NlcTo6ZXN0aW1hdGVfcmljaG5lc3MocHMyLCBtZWFzdXJlcz1jKCJTaGFubm9uIikpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZV9OYW1lIikgJT4lDQogIG11dGF0ZShTYW1wbGVfTmFtZSA9IFNhbXBsZV9OYW1lICU+JSANCiAgICAgICAgICAgc3RyX3JlbW92ZSgiXlgiKSAlPiUNCiAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKCJcXC4iLCAiICIpKQ0KDQojU2V0IG51bWJlciBvZiByYW5kb21pc2F0aW9ucyBmb3IgY2FsY3VsYXRpbmcgc2lnbmlmaWNhbmNlDQojIENhbGN1bGF0ZSBGYWl0aCdzIFBELWluZGV4ICYgU3BlY2llcyByaWNobmVzcyAtIHdpdGggU3RhbmRhcmQgZXJyb3JzDQojc2VzcGQgPC0gcGljYW50ZTo6c2VzLnBkKGFzKHBoeWxvc2VxOjpvdHVfdGFibGUocHMyKSwgIm1hdHJpeCIpLCAgcGh5bG9zZXE6OnBoeV90cmVlKHBzMiksIG51bGwubW9kZWwgPSAidGF4YS5sYWJlbHMiLCBpbmNsdWRlLnJvb3QgPSBGLCBydW5zID0gOTkpDQoNCnBkIDwtIHBpY2FudGU6OnBkKGFzKHBoeWxvc2VxOjpvdHVfdGFibGUocHMyKSwgIm1hdHJpeCIpLCAgcGh5bG9zZXE6OnBoeV90cmVlKHBzMiksIGluY2x1ZGUucm9vdCA9IEZBTFNFKQ0KDQojIEpvaW4gdG9nZXRoZXINCmRpdl90YWJsZSA8LSBwZCAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJTYW1wbGVfTmFtZSIpICU+JQ0KICBkcGx5cjo6c2VsZWN0KFNhbXBsZV9OYW1lLCBhbHBoYSA9IFNSLCBwZCA9IFBEKSAlPiUNCiAgbGVmdF9qb2luKHJpY2huZXNzLCBieT0iU2FtcGxlX05hbWUiKSAlPiUNCiAgbGVmdF9qb2luKHNhbXBsZV9kYXRhKHBzMikgJT4lIA0KICAgICAgICAgICAgICBhcygibWF0cml4IikgJT4lDQogICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgICAgICAgICAgICAgZmlsdGVyKCFkdXBsaWNhdGVkKFNhbXBsZV9OYW1lKSkgJT4lDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoU2FtcGxlX05hbWUsIHBzeWxsaWRfc3BwLCBwc3lsbGlkX2dlbnVzLCBwc3lsbGlkX2ZhbWlseSwgaG9zdHBsYW50X3NwcCwgc2VxcnVuLCBnZW51c19nZW8pLA0KICAgICAgICAgICAgYnkgPSAiU2FtcGxlX05hbWUiKSANCg0KIyBTdW1tYXJpc2UgbWVhbnMNCmRpdl90YWJsZSAlPiUNCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIG1lYW4pDQoNCg0KDQojIERpZmZlcmVuY2UgYmV0d2VlbiBzcGVjaWVzIGZvciBhbHBoYSBkaXZlcnNpdHkgQU5PVkENCnJlcG9ydDo6cmVwb3J0KGFvdihhbHBoYSB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K3BzeWxsaWRfZ2VudXMrcHN5bGxpZF9zcHAsIGRhdGE9ZGl2X3RhYmxlKSkNCnJlcG9ydDo6cmVwb3J0KGFvdihTaGFubm9uIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cytwc3lsbGlkX3NwcCwgZGF0YT1kaXZfdGFibGUpKQ0KcmVwb3J0OjpyZXBvcnQoYW92KHBkIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cytwc3lsbGlkX3NwcCwgZGF0YT1kaXZfdGFibGUpKQ0KDQojIERpZmZlcmVuY2UgYmV0d2VlbiBhbGwgZ2VuZXJhIGZvciBhbHBoYSBkaXZlcnNpdHkgQU5PVkENCnJlcG9ydDo6cmVwb3J0KGFvdihhbHBoYSB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K3BzeWxsaWRfZ2VudXMsIGRhdGE9ZGl2X3RhYmxlKSkNCnJlcG9ydDo6cmVwb3J0KGFvdihTaGFubm9uIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGUpKQ0KcmVwb3J0OjpyZXBvcnQoYW92KHBkIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGUpKQ0KDQojIERpZmZlcmVuY2UgYmV0d2VlbiBnZW51cy9nZW9ncmFwaHkgZmFjdG9ycyBBTk9WQQ0KcmVwb3J0OjpyZXBvcnQoYW92KGFscGhhIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrZ2VudXNfZ2VvLCBkYXRhPWRpdl90YWJsZSkpDQpyZXBvcnQ6OnJlcG9ydChhb3YoU2hhbm5vbiB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K2dlbnVzX2dlbywgZGF0YT1kaXZfdGFibGUpKQ0KcmVwb3J0OjpyZXBvcnQoYW92KHBkIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrZ2VudXNfZ2VvLCBkYXRhPWRpdl90YWJsZSkpDQoNCiMjIE1ham9yIGdlbmVyYSBvbmx5DQojIERpZmZlcmVuY2UgYmV0d2VlbiBhbGwgbWFqb3IgZ2VuZXJhIGZvciBhbHBoYSBkaXZlcnNpdHkgQU5PVkENCmRpdl90YWJsZTIgPC0gZGl2X3RhYmxlICU+JQ0KICBkcGx5cjo6ZmlsdGVyKHBzeWxsaWRfZ2VudXMgJWluJSBjKCJQb3dlbGxpYSIsICJDdGVuYXJ5dGFpbmEiLCAiUHN5bGxhIikpDQoNCm1nX2RpdiA8LSBiaW5kX3Jvd3MoYnJvb206OnRpZHkoVHVrZXlIU0QoYW92KGFscGhhIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZGl2X3RhYmxlMikpKSAlPiUgbXV0YXRlKHR5cGU9IlJpY2huZXNzIiksDQogICAgICAgICAgYnJvb206OnRpZHkoVHVrZXlIU0QoYW92KFNoYW5ub24gfnNlcXJ1bitwc3lsbGlkX2ZhbWlseStwc3lsbGlkX2dlbnVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWRpdl90YWJsZTIpKSkgJT4lIG11dGF0ZSh0eXBlPSJTaGFubm9uIiksDQogICAgICAgICAgYnJvb206OnRpZHkoVHVrZXlIU0QoYW92KHBkIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YT1kaXZfdGFibGUyKSkpICU+JSBtdXRhdGUodHlwZT0iUGh5bG9nZW5ldGljIiksDQogICAgICAgICAgYnJvb206OnRpZHkoVHVrZXlIU0QoYW92KGFscGhhIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrZ2VudXNfZ2VvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWRpdl90YWJsZTIpKSkgJT4lIG11dGF0ZSh0eXBlPSJSaWNobmVzcyIpLA0KICAgICAgICAgIGJyb29tOjp0aWR5KFR1a2V5SFNEKGFvdihhbHBoYSB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K2dlbnVzX2dlbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YT1kaXZfdGFibGUyKSkpICU+JSBtdXRhdGUodHlwZT0iU2hhbm5vbiIpLA0KICAgICAgICAgIGJyb29tOjp0aWR5KFR1a2V5SFNEKGFvdihwZCB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K2dlbnVzX2dlbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YT1kaXZfdGFibGUyKSkpICU+JSBtdXRhdGUodHlwZT0iUGh5bG9nZW5ldGljIikNCiAgICAgICAgICApDQp3cml0ZV9jc3YobWdfZGl2LCAib3V0cHV0L2FscGhhL21ham9yX2dlbmVyYV9hbHBoYS5jc3YiKQ0KDQojIERpZmZlcmVuY2UgYmV0d2VlbiBtYWpvciBnZW5lcmEgb25seSBBTk9WQQ0KcmVwb3J0OjpyZXBvcnQoYW92KGFscGhhIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGUyKSkNCnJlcG9ydDo6cmVwb3J0KGFvdihTaGFubm9uIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGUyKSkNCnJlcG9ydDo6cmVwb3J0KGFvdihwZCB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K3BzeWxsaWRfZ2VudXMsIGRhdGE9ZGl2X3RhYmxlMikpDQoNCiMgRGlmZmVyZW5jZSBiZXR3ZWVuIGJldHdlZW4gbWFqb3IgZ2VuZXJhL2dlb2dyYXBoeSBmYWN0b3JzIEFOT1ZBDQpyZXBvcnQ6OnJlcG9ydChhb3YoYWxwaGEgfnNlcXJ1bitwc3lsbGlkX2ZhbWlseStnZW51c19nZW8sIGRhdGE9ZGl2X3RhYmxlMikpDQpyZXBvcnQ6OnJlcG9ydChhb3YoU2hhbm5vbiB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K2dlbnVzX2dlbywgZGF0YT1kaXZfdGFibGUyKSkNCnJlcG9ydDo6cmVwb3J0KGFvdihhbHBoYSB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K2dlbnVzX2dlbywgZGF0YT1kaXZfdGFibGUyKSkNCg0KIyBBc3NvY2lhdGlvbiB3aXRoIHBoeWxvZ2VueQ0KZGF0IDwtIGRpdl90YWJsZSAgJT4lDQogIGZpbHRlcihwc3lsbGlkX3NwcCAlaW4lIHBydW5lZC50cmVlJHRpcC5sYWJlbCkgJT4lICNTdWJzZXQgdG8gY29tbW9uIA0KICBncm91cF9ieShwc3lsbGlkX3NwcCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoLXdoZXJlKGlzLmNoYXJhY3RlcikpICU+JQ0KICBzdW1tYXJpc2VfYWxsKG1lYW4pICU+JQ0KICBhcnJhbmdlKG1hdGNoKHBzeWxsaWRfc3BwLCBwcnVuZWQudHJlZSR0aXAubGFiZWwpKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBtYWdyaXR0cjo6c2V0X3Jvd25hbWVzKC4kcHN5bGxpZF9zcHApICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1wc3lsbGlkX3NwcCkNCg0KIyBBZGQgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGNvbnRyb2xzDQpkYXQkcmFuZG9tIDwtIHJub3JtKGxlbmd0aChkYXQkYWxwaGEpLCBzZCA9IDEwKSAjUmFuZG9tIGFzc29jaWF0aW9uDQpkYXQkYm0gPC0gclRyYWl0Q29udChwcnVuZWQudHJlZSkgI0Jyb3duaWFuIG1vdGlvbg0KDQojIE1ha2UgcGh5bG9zaWduYWwgb2JqZWN0IGFuZCBtZWFzdXJlIHNpZ25hbCBiZXR3ZWVuIHVuaXZhcmlhdGUgdHJhaXRzLg0KcDRkIDwtIHBoeWxvYmFzZTo6cGh5bG80ZChwcnVuZWQudHJlZSwgZGF0KQkNCnNpZ25hbCA8LSBwaHlsb3NpZ25hbDo6cGh5bG9TaWduYWwocDRkID0gcDRkLCBtZXRob2RzID0gYygiSSIsICJMYW1iZGEiLCAiSyIpLCByZXBzID0gOTk5KSU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigibWVhc3VyZSIpDQoNCiMgcHJpbnQgcGh5bG9nZW5ldGljIHNpZ25hbA0Kc2lnbmFsDQp3cml0ZV9jc3Yoc2lnbmFsLCAib3V0cHV0L2FscGhhL3BoeWxvc2lnbmFsLmNzdiIpDQoNCiMgTG9jYXRlIHNpZ25hbA0KbGlwYSA8LSBsaXBhTW9yYW4ocDRkLCByZXBzPTk5OSkNCmxpcGEucDRkIDwtIGxpcGFNb3JhbihwNGQsIGFzLnA0ZCA9IFRSVUUsIHJlcHM9OTk5KQ0KYmFycGxvdC5waHlsbzRkKGxpcGEucDRkLCBiYXIuY29sID0gKGxpcGEkcC52YWx1ZSA8IDAuMDUpICsgMSwgY2VudGVyID0gRkFMU0UsIHNjYWxlID0gRkFMU0UpICsgdGl0bGUoIk5vbi1yYXJlZmllZCIpDQoNCiN3cml0ZSBvdXQgbGlwYQ0KbGlwYV9vdXQgPC0gY2JpbmQobGlwYSRsaXBhICU+JQ0KICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lICU+JQ0KICAgICAgICAgICAgICAgICAgICByZW5hbWVfYWxsKGZ1bnMocGFzdGUwKC4sICJfc3RhdCIpKSksDQogICAgICAgICAgICAgICAgICBsaXBhJHAudmFsdWUgJT4lDQogICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUgJT4lDQogICAgICAgICAgICAgICAgICAgIHJlbmFtZV9hbGwoZnVucyhwYXN0ZTAoLiwgIl9wdmFsIikpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICklPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJwc3lsbGlkX3NwcCIpDQp3cml0ZV9jc3YobGlwYV9vdXQsICJvdXRwdXQvYWxwaGEvbGlwYS5jc3YiKQ0KYGBgDQoNCiMjIFJhcmVmaWVkDQoNClNlZSBpZiB0aGUgcGF0dGVybiBob2xkcyBldmVuIHdpdGggcmFyZWZhY3Rpb24gdG8gbG93ZXN0IHNhbXBsZQ0KDQpgYGB7ciBhbHBoYSByYXJlZmllZH0NCiMgUmFyZWZpZWQgcmljaG5lc3MNCnBzMl9yYXJlIDwtIHJhcmVmeV9ldmVuX2RlcHRoKHBzMiwgc2FtcGxlLnNpemUgPSBtaW4oc2FtcGxlX3N1bXMocHMyKSksDQogIHJuZ3NlZWQgPSA2NjYsIHJlcGxhY2UgPSBUUlVFLCB0cmltT1RVcyA9IFRSVUUsIHZlcmJvc2UgPSBUUlVFKQ0KDQojIEdldCByaWNobmVzcyBtZWFzdXJlcw0KcmljaG5lc3NfcmFyZSA8LSBwaHlsb3NlcTo6ZXN0aW1hdGVfcmljaG5lc3MocHMyX3JhcmUsIG1lYXN1cmVzPWMoIlNoYW5ub24iKSkgJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigiU2FtcGxlX05hbWUiKSAlPiUNCiAgbXV0YXRlKFNhbXBsZV9OYW1lID0gU2FtcGxlX05hbWUgJT4lIA0KICAgICAgICAgICBzdHJfcmVtb3ZlKCJeWCIpICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIlxcLiIsICIgIikpDQoNCiNTZXQgbnVtYmVyIG9mIHJhbmRvbWlzYXRpb25zIGZvciBjYWxjdWxhdGluZyBzaWduaWZpY2FuY2UNCiMgQ2FsY3VsYXRlIEZhaXRoJ3MgUEQtaW5kZXggJiBTcGVjaWVzIHJpY2huZXNzIC0gd2l0aCBTdGFuZGFyZCBlcnJvcnMNCiNzZXNwZF9yYXJlIDwtIHBpY2FudGU6OnNlcy5wZChhcyhwaHlsb3NlcTo6b3R1X3RhYmxlKHBzMl9yYXJlKSwgIm1hdHJpeCIpLCAgcGh5bG9zZXE6OnBoeV90cmVlKHBzMl9yYXJlKSwgbnVsbC5tb2RlbCA9ICMidGF4YS5sYWJlbHMiLCBpbmNsdWRlLnJvb3QgPSBGLCBydW5zID0gOTkpDQoNCnBkX3JhcmUgPC0gcGljYW50ZTo6cGQoYXMocGh5bG9zZXE6Om90dV90YWJsZShwczJfcmFyZSksICJtYXRyaXgiKSwgIHBoeWxvc2VxOjpwaHlfdHJlZShwczJfcmFyZSksIGluY2x1ZGUucm9vdCA9IEZBTFNFKQ0KDQojIEpvaW4gdG9nZXRoZXINCmRpdl90YWJsZV9yYXJlIDwtIHBkX3JhcmUgJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigiU2FtcGxlX05hbWUiKSAlPiUNCiAgZHBseXI6OnNlbGVjdChTYW1wbGVfTmFtZSwgYWxwaGEgPSBTUiwgcGQgPSBQRCkgJT4lDQogIGxlZnRfam9pbihyaWNobmVzcywgYnk9IlNhbXBsZV9OYW1lIikgJT4lDQogIGxlZnRfam9pbihzYW1wbGVfZGF0YShwczJfcmFyZSkgJT4lIA0KICAgICAgICAgICAgICBhcygibWF0cml4IikgJT4lDQogICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgICAgICAgICAgICAgZmlsdGVyKCFkdXBsaWNhdGVkKFNhbXBsZV9OYW1lKSkgJT4lDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoU2FtcGxlX05hbWUsIHBzeWxsaWRfc3BwLCBwc3lsbGlkX2dlbnVzLCBnZW51c19nZW8pLA0KICAgICAgICAgICAgYnkgPSAiU2FtcGxlX05hbWUiKSANCg0KIyBTdW1tYXJpc2UgbWVhbnMNCmRpdl90YWJsZV9yYXJlICU+JQ0KICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBEaWZmZXJlbmNlIGJldHdlZW4gYWxsIG1ham9yIGdlbmVyYSBmb3IgYWxwaGEgZGl2ZXJzaXR5IEFOT1ZBDQpkaXZfdGFibGVfcmFyZTIgPC0gZGl2X3RhYmxlX3JhcmUgJT4lDQogIGRwbHlyOjpmaWx0ZXIocHN5bGxpZF9nZW51cyAlaW4lIGMoIlBvd2VsbGlhIiwgIkN0ZW5hcnl0YWluYSIsICJQc3lsbGEiKSkNCg0KbWdfZGl2X3JhcmUgPC0gYmluZF9yb3dzKA0KICBicm9vbTo6dGlkeShUdWtleUhTRChhb3YoYWxwaGEgfnNlcXJ1bitwc3lsbGlkX2ZhbWlseStwc3lsbGlkX2dlbnVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YT1kaXZfdGFibGVfcmFyZTIpKSkgJT4lIG11dGF0ZSh0eXBlPSJSaWNobmVzcyIpLA0KICBicm9vbTo6dGlkeShUdWtleUhTRChhb3YoU2hhbm5vbiB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K3BzeWxsaWRfZ2VudXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWRpdl90YWJsZV9yYXJlMikpKSAlPiUgbXV0YXRlKHR5cGU9IlNoYW5ub24iKSwNCiAgYnJvb206OnRpZHkoVHVrZXlIU0QoYW92KHBkIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZGl2X3RhYmxlX3JhcmUyKSkpICU+JSBtdXRhdGUodHlwZT0iUGh5bG9nZW5ldGljIiksDQogIGJyb29tOjp0aWR5KFR1a2V5SFNEKGFvdihhbHBoYSB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K2dlbnVzX2dlbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZGl2X3RhYmxlX3JhcmUyKSkpICU+JSBtdXRhdGUodHlwZT0iUmljaG5lc3MiKSwNCiAgYnJvb206OnRpZHkoVHVrZXlIU0QoYW92KGFscGhhIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrZ2VudXNfZ2VvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YT1kaXZfdGFibGVfcmFyZTIpKSkgJT4lIG11dGF0ZSh0eXBlPSJTaGFubm9uIiksDQogIGJyb29tOjp0aWR5KFR1a2V5SFNEKGFvdihwZCB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K2dlbnVzX2dlbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9ZGl2X3RhYmxlX3JhcmUyKSkpICU+JSBtdXRhdGUodHlwZT0iUGh5bG9nZW5ldGljIikNCiAgICAgICAgICApDQp3cml0ZV9jc3YobWdfZGl2X3JhcmUsICJvdXRwdXQvYWxwaGEvbWFqb3JfZ2VuZXJhX2FscGhhX3JhcmVmaWVkLmNzdiIpDQoNCiMgRGlmZmVyZW5jZSBiZXR3ZWVuIGdlbnVzIGZhY3RvcnMgQU5PVkENCnJlcG9ydDo6cmVwb3J0KGFvdihhbHBoYSB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K3BzeWxsaWRfZ2VudXMsIGRhdGE9ZGl2X3RhYmxlX3JhcmUyKSkNCnJlcG9ydDo6cmVwb3J0KGFvdihTaGFubm9uIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGVfcmFyZTIpKQ0KcmVwb3J0OjpyZXBvcnQoYW92KHBkIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGVfcmFyZTIpKQ0KDQojIERpZmZlcmVuY2UgYmV0d2VlbiBnZW51cy9nZW9ncmFwaHkgZmFjdG9ycyBBTk9WQQ0KcmVwb3J0OjpyZXBvcnQoYW92KGFscGhhIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrZ2VudXNfZ2VvLCBkYXRhPWRpdl90YWJsZV9yYXJlMikpDQpyZXBvcnQ6OnJlcG9ydChhb3YoU2hhbm5vbiB+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K2dlbnVzX2dlbywgZGF0YT1kaXZfdGFibGVfcmFyZTIpKQ0KcmVwb3J0OjpyZXBvcnQoYW92KGFscGhhIH5zZXFydW4rcHN5bGxpZF9mYW1pbHkrZ2VudXNfZ2VvLCBkYXRhPWRpdl90YWJsZV9yYXJlMikpDQoNCg0KZGF0IDwtIGRpdl90YWJsZV9yYXJlICAlPiUNCiAgZmlsdGVyKHBzeWxsaWRfc3BwICVpbiUgcHJ1bmVkLnRyZWUkdGlwLmxhYmVsKSAlPiUgI1N1YnNldCB0byBjb21tb24gDQogIGdyb3VwX2J5KHBzeWxsaWRfc3BwKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtd2hlcmUoaXMuY2hhcmFjdGVyKSkgJT4lDQogIHN1bW1hcmlzZV9hbGwobWVhbikgJT4lDQogIGFycmFuZ2UobWF0Y2gocHN5bGxpZF9zcHAsIHBydW5lZC50cmVlJHRpcC5sYWJlbCkpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIG1hZ3JpdHRyOjpzZXRfcm93bmFtZXMoLiRwc3lsbGlkX3NwcCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoLXBzeWxsaWRfc3BwKQ0KDQojIEFkZCBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgY29udHJvbHMNCmRhdCRyYW5kb20gPC0gcm5vcm0obGVuZ3RoKGRhdCRhbHBoYSksIHNkID0gMTApICNSYW5kb20gYXNzb2NpYXRpb24NCmRhdCRibSA8LSByVHJhaXRDb250KHBydW5lZC50cmVlKSAjQnJvd25pYW4gbW90aW9uDQoNCiMgTWFrZSBwaHlsb3NpZ25hbCBvYmplY3QgYW5kIG1lYXN1cmUgc2lnbmFsIGJldHdlZW4gdW5pdmFyaWF0ZSB0cmFpdHMuDQpwNGQgPC0gcGh5bG9iYXNlOjpwaHlsbzRkKHBydW5lZC50cmVlLCBkYXQpCQ0Kc2lnbmFsX3JhcmUgPC0gcGh5bG9zaWduYWw6OnBoeWxvU2lnbmFsKHA0ZCA9IHA0ZCwgbWV0aG9kcyA9IGMoIkkiLCAiTGFtYmRhIiwgIksiKSwgcmVwcyA9IDk5OSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJtZWFzdXJlIikNCg0KIyBwcmludCBwaHlsb2dlbmV0aWMgc2lnbmFsDQpzaWduYWxfcmFyZQ0Kd3JpdGVfY3N2KHNpZ25hbF9yYXJlLCAib3V0cHV0L2FscGhhL3BoeWxvc2lnbmFsX3JhcmVmaWVkLmNzdiIpDQoNCiMgTG9jYXRlIHNpZ25hbA0KbGlwYSA8LSBsaXBhTW9yYW4ocDRkLCByZXBzPTk5OSkNCmxpcGEucDRkIDwtIGxpcGFNb3JhbihwNGQsIGFzLnA0ZCA9IFRSVUUsIHJlcHM9OTk5KQ0KYmFycGxvdC5waHlsbzRkKGxpcGEucDRkLCBiYXIuY29sID0gKGxpcGEkcC52YWx1ZSA8IDAuMDUpICsgMSwgY2VudGVyID0gRkFMU0UsIHNjYWxlID0gRkFMU0UpICsgdGl0bGUoIlJhcmVmaWVkIikNCg0KI3dyaXRlIG91dCBsaXBhDQpsaXBhX291dF9yYXJlIDwtIGNiaW5kKGxpcGEkbGlwYSAlPiUNCiAgICAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSAlPiUNCiAgICAgICAgICAgICAgICAgICAgcmVuYW1lX2FsbChmdW5zKHBhc3RlMCguLCAiX3N0YXQiKSkpLA0KICAgICAgICAgICAgICAgICAgbGlwYSRwLnZhbHVlICU+JQ0KICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lICU+JQ0KICAgICAgICAgICAgICAgICAgICByZW5hbWVfYWxsKGZ1bnMocGFzdGUwKC4sICJfcHZhbCIpKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigicHN5bGxpZF9zcHAiKQ0Kd3JpdGVfY3N2KGxpcGFfb3V0X3JhcmUsICJvdXRwdXQvYWxwaGEvbGlwYV9yYXJlZmllZC5jc3YiKSAgICANCmBgYAkNCg0KIyMgQWxwaGEgbm8gR2FtbWFwcm90ZW9iYWN0ZXJpYQ0KDQpgYGB7ciBObyBnYW1tYSByaWNobmVzc30NCiMgUmFyZWZpZWQgcmljaG5lc3MNCnBzMl9zdWJzZXQgPC0gcHMyICU+JQ0KIHN1YnNldF90YXhhKGNsYXNzICE9ICJHYW1tYXByb3Rlb2JhY3RlcmlhIikgJT4lICNpcyB0aGlzIHdvcmtpbmc/DQogZmlsdGVyX3RheGEoZnVuY3Rpb24oeCkgbWVhbih4KSA+IDAsIFRSVUUpI0Ryb3AgbWlzc2luZyB0YXhhIGZyb20gdGFibGUNCnBzMl9zdWJzZXQgPC0gcHJ1bmVfc2FtcGxlcyhzYW1wbGVfc3VtcyhwczJfc3Vic2V0KSA+MCAsIHBzMl9zdWJzZXQpDQptZXNzYWdlKG5zYW1wbGVzKHBzMikgLSBuc2FtcGxlcyhwczJfc3Vic2V0KSwgIiBTYW1wbGVzIGFuZCAiLCBudGF4YShwczIpIC0gbnRheGEocHMyX3N1YnNldCksICIgdGF4YSBEcm9wcGVkIikNCg0KIyBHZXQgcmljaG5lc3MgbWVhc3VyZXMNCnJpY2huZXNzX3N1YnNldCA8LSBwaHlsb3NlcTo6ZXN0aW1hdGVfcmljaG5lc3MocHMyX3N1YnNldCwgbWVhc3VyZXM9YygiU2hhbm5vbiIpKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJTYW1wbGVfTmFtZSIpICU+JQ0KICBtdXRhdGUoU2FtcGxlX05hbWUgPSBTYW1wbGVfTmFtZSAlPiUgDQogICAgICAgICAgIHN0cl9yZW1vdmUoIl5YIikgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiXFwuIiwgIiAiKSkNCg0KI1NldCBudW1iZXIgb2YgcmFuZG9taXNhdGlvbnMgZm9yIGNhbGN1bGF0aW5nIHNpZ25pZmljYW5jZQ0KIyBDYWxjdWxhdGUgRmFpdGgncyBQRC1pbmRleCAmIFNwZWNpZXMgcmljaG5lc3MgLSB3aXRoIFN0YW5kYXJkIGVycm9ycw0KI3Nlc3BkX3N1YnNldCA8LSBwaWNhbnRlOjpzZXMucGQoYXMocGh5bG9zZXE6Om90dV90YWJsZShwczJfc3Vic2V0KSwgIm1hdHJpeCIpLCAgcGh5bG9zZXE6OnBoeV90cmVlKHBzMl9zdWJzZXQpLCBudWxsLm1vZGVsID0gInRheGEubGFiZWxzIiwgaW5jbHVkZS5yb290ID0gRiwgcnVucyA9IDk5KQ0KDQpwZF9zdWJzZXQgPC0gcGljYW50ZTo6cGQoYXMocGh5bG9zZXE6Om90dV90YWJsZShwczJfc3Vic2V0KSwgIm1hdHJpeCIpLCAgcGh5bG9zZXE6OnBoeV90cmVlKHBzMl9zdWJzZXQpLCBpbmNsdWRlLnJvb3QgPSBGQUxTRSkNCg0KIyBKb2luIHRvZ2V0aGVyDQpkaXZfdGFibGVfc3Vic2V0IDwtIHBkX3N1YnNldCAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJTYW1wbGVfTmFtZSIpICU+JQ0KICBkcGx5cjo6c2VsZWN0KFNhbXBsZV9OYW1lLCBhbHBoYSA9IFNSLCBwZCA9IFBEKSAlPiUNCiAgbGVmdF9qb2luKHJpY2huZXNzLCBieT0iU2FtcGxlX05hbWUiKSAlPiUNCiAgbGVmdF9qb2luKHNhbXBsZV9kYXRhKHBzMl9zdWJzZXQpICU+JSANCiAgICAgICAgICAgICAgYXMoIm1hdHJpeCIpICU+JQ0KICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogICAgICAgICAgICAgIGZpbHRlcighZHVwbGljYXRlZChTYW1wbGVfTmFtZSkpICU+JQ0KICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KFNhbXBsZV9OYW1lLCBwc3lsbGlkX3NwcCwgcHN5bGxpZF9nZW51cywgZ2VudXNfZ2VvKSwNCiAgICAgICAgICAgIGJ5ID0gIlNhbXBsZV9OYW1lIikgDQoNCiMgU3VtbWFyaXNlIG1lYW5zDQpkaXZfdGFibGVfc3Vic2V0ICU+JQ0KICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgfm1lYW4oLngsIG5hLnJtPVRSVUUpKQ0KDQojIERpZmZlcmVuY2UgYmV0d2VlbiBhbGwgbWFqb3IgZ2VuZXJhIGZvciBhbHBoYSBkaXZlcnNpdHkgQU5PVkENCmRpdl90YWJsZV9zdWJzZXQyIDwtIGRpdl90YWJsZV9zdWJzZXQgJT4lDQogIGRwbHlyOjpmaWx0ZXIocHN5bGxpZF9nZW51cyAlaW4lIGMoIlBvd2VsbGlhIiwgIkN0ZW5hcnl0YWluYSIsICJQc3lsbGEiKSkNCg0KbWdfZGl2X3N1YnNldCA8LSBiaW5kX3Jvd3MoDQogIGJyb29tOjp0aWR5KFR1a2V5SFNEKGFvdihhbHBoYSB+cHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGVfc3Vic2V0MikpKSAlPiUgbXV0YXRlKHR5cGU9IlJpY2huZXNzIiksDQogIGJyb29tOjp0aWR5KFR1a2V5SFNEKGFvdihTaGFubm9uIH5wc3lsbGlkX2dlbnVzLCBkYXRhPWRpdl90YWJsZV9zdWJzZXQyKSkpICU+JSBtdXRhdGUodHlwZT0iU2hhbm5vbiIpLA0KICBicm9vbTo6dGlkeShUdWtleUhTRChhb3YocGQgfnBzeWxsaWRfZ2VudXMsIGRhdGE9ZGl2X3RhYmxlX3N1YnNldDIpKSkgJT4lIG11dGF0ZSh0eXBlPSJQaHlsb2dlbmV0aWMiKSwNCiAgYnJvb206OnRpZHkoVHVrZXlIU0QoYW92KGFscGhhIH5nZW51c19nZW8sIGRhdGE9ZGl2X3RhYmxlX3N1YnNldDIpKSkgJT4lIG11dGF0ZSh0eXBlPSJSaWNobmVzcyIpLA0KICBicm9vbTo6dGlkeShUdWtleUhTRChhb3YoYWxwaGEgfmdlbnVzX2dlbywgZGF0YT1kaXZfdGFibGVfc3Vic2V0MikpKSAlPiUgbXV0YXRlKHR5cGU9IlNoYW5ub24iKSwNCiAgYnJvb206OnRpZHkoVHVrZXlIU0QoYW92KHBkIH5nZW51c19nZW8sIGRhdGE9ZGl2X3RhYmxlX3N1YnNldDIpKSkgJT4lIG11dGF0ZSh0eXBlPSJQaHlsb2dlbmV0aWMiKQ0KICAgICAgICAgICkNCndyaXRlX2NzdihtZ19kaXZfc3Vic2V0LCAib3V0cHV0L2FscGhhL21ham9yX2dlbmVyYV9hbHBoYV9ub2dhbW1hLmNzdiIpDQoNCiMgRGlmZmVyZW5jZSBiZXR3ZWVuIGdlbnVzIGZhY3RvcnMgQU5PVkENCnJlcG9ydDo6cmVwb3J0KGFvdihhbHBoYSB+cHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGVfc3Vic2V0MikpDQpyZXBvcnQ6OnJlcG9ydChhb3YoU2hhbm5vbiB+cHN5bGxpZF9nZW51cywgZGF0YT1kaXZfdGFibGVfc3Vic2V0MikpDQpyZXBvcnQ6OnJlcG9ydChhb3YocGQgfnBzeWxsaWRfZ2VudXMsIGRhdGE9ZGl2X3RhYmxlX3N1YnNldDIpKQ0KDQoNCiMgRGlmZmVyZW5jZSBiZXR3ZWVuIGdlbnVzL2dlb2dyYXBoeSBmYWN0b3JzIEFOT1ZBDQpyZXBvcnQ6OnJlcG9ydChhb3YoYWxwaGEgfmdlbnVzX2dlbywgZGF0YT1kaXZfdGFibGVfc3Vic2V0MikpDQpyZXBvcnQ6OnJlcG9ydChhb3YoU2hhbm5vbiB+Z2VudXNfZ2VvLCBkYXRhPWRpdl90YWJsZV9zdWJzZXQyKSkNCnJlcG9ydDo6cmVwb3J0KGFvdihhbHBoYSB+Z2VudXNfZ2VvLCBkYXRhPWRpdl90YWJsZV9zdWJzZXQyKSkNCg0KYGBgCQ0KDQojIEJldGEgZGl2ZXJzaXR5DQoNCiMjIE1pY3JvYmUgZGlzdGFuY2VzDQpgYGB7ciBkaXN0bGlzdH0NCnBzMl9kaXN0IDwtIHBzMg0KI3BzMl9kaXN0IDwtIHBzMl9maWx0DQoNCiMgR2V0IE9UVSB0YWJsZXMNCm90dXRhYiA8LSBvdHVfdGFibGUocHMyX2Rpc3QpDQojSW1wdXRlIHplcm9lcyBmb3IgY29tcG9zaXRpb25hbCBkaXN0YW5jZXMNCm90dXRhYl9uMCA8LSBhcy5tYXRyaXgoekNvbXBvc2l0aW9uczo6Y211bHRSZXBsKG90dXRhYiwgbWV0aG9kPSJCTCIsIG91dHB1dD0icC1jb3VudHMiKSkNCg0KI1Jvb3QgJiBsYWJlbCBwaHlsb2dlbmV0aWMgdHJlZQ0KcGh5X3RyZWUocHMyX2Rpc3QpIDwtIG11bHRpMmRpKHBoeV90cmVlKHBzMl9kaXN0KSkNCnBoeV90cmVlKHBzMl9kaXN0KSA8LSBtYWtlTm9kZUxhYmVsKHBoeV90cmVlKHBzMl9kaXN0KSwgbWV0aG9kPSJudW1iZXIiLCBwcmVmaXg9J24nKQ0KbmFtZS5iYWxhbmNlKHBoeV90cmVlKHBzMl9kaXN0KSwgdGF4X3RhYmxlKHBzMl9kaXN0KSwgJ24xJykNCg0KI0NhbGN1bGF0ZSBkaWZmZXJlbnQgZGlzdGFuY2UgbWV0cmljcw0KbWV0cmljcyA8LSBjKCJCcmF5IiwgIkphY2NhcmQiLCAiQWl0Y2hpc29uIiwiUGhpbHIiLCAiVW5pZnJhYyIsICJXVW5pZnJhYyIpICANCmRpc3RsaXN0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aD1sZW5ndGgobWV0cmljcykpDQpuYW1lcyhkaXN0bGlzdCkgPC0gbWV0cmljcw0KDQpkaXN0bGlzdCRKYWNjYXJkIDwtIGFzLm1hdHJpeCh2ZWdkaXN0KG90dXRhYiwgbWV0aG9kPSJqYWMiLGJpbmFyeSA9IFQpKQ0KZGlzdGxpc3QkQnJheSA8LSBhcy5tYXRyaXgodmVnZGlzdChvdHV0YWIsIG1ldGhvZD0iYnJheSIpKQ0KZGlzdGxpc3QkQWl0Y2hpc29uIDwtIGFzLm1hdHJpeCh2ZWdkaXN0KENvRGFTZXE6OmNvZGFTZXEuY2xyKG90dXRhYl9uMCksIG1ldGhvZD0iZXVjbGlkZWFuIikpDQpkaXN0bGlzdCRQaGlsciA8LSBhcy5tYXRyaXgodmVnZGlzdChwaGlscjo6cGhpbHIob3R1dGFiX24wLCBwaHlfdHJlZShwczJfZGlzdCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJ0LndlaWdodHM9J2Vub3JtLnguZ20uY291bnRzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlsci53ZWlnaHRzPSdibHcuc3FydCcpLCBtZXRob2Q9ImV1Y2xpZGVhbiIsIG5hLnJtPVRSVUUpKQ0KZGlzdGxpc3QkVW5pZnJhYyA8LSBhcy5tYXRyaXgocGh5bG9zZXE6OlVuaUZyYWMocHMyX2Rpc3QsIHdlaWdodGVkPUZBTFNFLCBwYXJhbGxlbCA9IFRSVUUpKQ0KZGlzdGxpc3QkV1VuaWZyYWMgPC0gYXMubWF0cml4KHBoeWxvc2VxOjpVbmlGcmFjKHBzMl9kaXN0LCB3ZWlnaHRlZD1UUlVFLCBwYXJhbGxlbCA9IFRSVUUpKQ0KDQojIENyZWF0ZSBsb3cgYWJ1bmRhbmNlIGZpbHRlcmVkIGRhdGFzZXQNCmZpbHRlcmZ1bjEgPC0gZnVuY3Rpb24oeCl7DQogIHhbKHggLyBzdW0oeCkpIDwgKDFlLTQpXSA8LSAwDQogIHJldHVybih4KQ0KfQ0KcHMyX2ZpbHQgIDwtIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKHBzMiwgZnVuID0gZmlsdGVyZnVuMSkgJT4lDQogIGZpbHRlcl90YXhhKGZ1bmN0aW9uKHgpIG1lYW4oeCkgPiAwLCBUUlVFKSAjRHJvcCBtaXNzaW5nIHRheGEgZnJvbSB0YWJsZQ0KDQpwcmludChwYXN0ZTAoKG50YXhhKHBzMiktbnRheGEocHMyX2ZpbHQpKSwgIiB0YXhhIHVuZGVyIHRocmVzaG9sZCByZW1vdmVkIikpDQoNCiMgR2V0IE9UVSB0YWJsZXMNCm90dXRhYiA8LSBvdHVfdGFibGUocHMyX2ZpbHQpDQojSW1wdXRlIHplcm9lcyBmb3IgY29tcG9zaXRpb25hbCBkaXN0YW5jZXMNCm90dXRhYl9uMCA8LSBhcy5tYXRyaXgoekNvbXBvc2l0aW9uczo6Y211bHRSZXBsKG90dXRhYiwgbWV0aG9kPSJCTCIsIG91dHB1dD0icC1jb3VudHMiKSkNCiNSb290ICYgbGFiZWwgcGh5bG9nZW5ldGljIHRyZWUNCnBoeV90cmVlKHBzMl9maWx0KSA8LSBtdWx0aTJkaShwaHlfdHJlZShwczJfZmlsdCkpDQpwaHlfdHJlZShwczJfZmlsdCkgPC0gbWFrZU5vZGVMYWJlbChwaHlfdHJlZShwczJfZmlsdCksIG1ldGhvZD0ibnVtYmVyIiwgcHJlZml4PSduJykNCm5hbWUuYmFsYW5jZShwaHlfdHJlZShwczJfZmlsdCksIHRheF90YWJsZShwczJfZmlsdCksICduMScpDQoNCiNDYWxjdWxhdGUgZGlmZmVyZW50IGRpc3RhbmNlIG1ldHJpY3MNCm1ldHJpY3MgPC0gYygiQnJheSIsICJKYWNjYXJkIiwgIkFpdGNoaXNvbiIsIlBoaWxyIiwgIlVuaWZyYWMiLCAiV1VuaWZyYWMiKSAgDQpkaXN0bGlzdF9maWx0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aD1sZW5ndGgobWV0cmljcykpDQpuYW1lcyhkaXN0bGlzdF9maWx0KSA8LSBtZXRyaWNzDQoNCmRpc3RsaXN0X2ZpbHQkSmFjY2FyZCA8LSBhcy5tYXRyaXgodmVnZGlzdChvdHV0YWIsIG1ldGhvZD0iamFjIixiaW5hcnkgPSBUKSkNCmRpc3RsaXN0X2ZpbHQkQnJheSA8LSBhcy5tYXRyaXgodmVnZGlzdChvdHV0YWIsIG1ldGhvZD0iYnJheSIpKQ0KZGlzdGxpc3RfZmlsdCRBaXRjaGlzb24gPC0gYXMubWF0cml4KHZlZ2Rpc3QoQ29EYVNlcTo6Y29kYVNlcS5jbHIob3R1dGFiX24wKSwgbWV0aG9kPSJldWNsaWRlYW4iKSkNCmRpc3RsaXN0X2ZpbHQkUGhpbHIgPC0gYXMubWF0cml4KHZlZ2Rpc3QocGhpbHI6OnBoaWxyKG90dXRhYl9uMCwgcGh5X3RyZWUocHMyX2ZpbHQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFydC53ZWlnaHRzPSdlbm9ybS54LmdtLmNvdW50cycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbHIud2VpZ2h0cz0nYmx3LnNxcnQnKSwgbWV0aG9kPSJldWNsaWRlYW4iLCBuYS5ybT1UUlVFKSkNCmRpc3RsaXN0X2ZpbHQkVW5pZnJhYyA8LSBhcy5tYXRyaXgocGh5bG9zZXE6OlVuaUZyYWMocHMyX2ZpbHQsIHdlaWdodGVkPUZBTFNFLCBwYXJhbGxlbCA9IFRSVUUpKQ0KZGlzdGxpc3RfZmlsdCRXVW5pZnJhYyA8LSBhcy5tYXRyaXgocGh5bG9zZXE6OlVuaUZyYWMocHMyX2ZpbHQsIHdlaWdodGVkPVRSVUUsIHBhcmFsbGVsID0gVFJVRSkpDQoNCiMgQ3JlYXRlIGRhdGFzZXQgd2l0aG91dCBnYW1tYXByb3Rlb2JhDQpwczJfc3Vic2V0IDwtIHBzMiAlPiUNCiBzdWJzZXRfdGF4YShjbGFzcyAhPSAiR2FtbWFwcm90ZW9iYWN0ZXJpYSIpICU+JSAjaXMgdGhpcyB3b3JraW5nPw0KIGZpbHRlcl90YXhhKGZ1bmN0aW9uKHgpIG1lYW4oeCkgPiAwLCBUUlVFKSNEcm9wIG1pc3NpbmcgdGF4YSBmcm9tIHRhYmxlDQpwczJfc3Vic2V0IDwtIHBydW5lX3NhbXBsZXMoc2FtcGxlX3N1bXMocHMyX3N1YnNldCkgPjAgLCBwczJfc3Vic2V0KQ0KbWVzc2FnZShuc2FtcGxlcyhwczIpIC0gbnNhbXBsZXMocHMyX3N1YnNldCksICIgU2FtcGxlcyBhbmQgIiwgbnRheGEocHMyKSAtIG50YXhhKHBzMl9zdWJzZXQpLCAiIHRheGEgRHJvcHBlZCIpDQoNCiMgR2V0IE9UVSB0YWJsZXMNCm90dXRhYl9zdWJzZXQgPC0gb3R1X3RhYmxlKHBzMl9zdWJzZXQpDQojSW1wdXRlIHplcm9lcyBmb3IgY29tcG9zaXRpb25hbCBkaXN0YW5jZXMNCm90dXRhYl9zdWJzZXRfbjAgPC0gYXMubWF0cml4KHpDb21wb3NpdGlvbnM6OmNtdWx0UmVwbChvdHV0YWJfc3Vic2V0LCBtZXRob2Q9IkJMIiwgb3V0cHV0PSJwLWNvdW50cyIpKQ0KI1Jvb3QgcGh5bG9nZW5ldGljIHRyZWUNCnBoeV90cmVlKHBzMl9zdWJzZXQpIDwtIG11bHRpMmRpKHBoeV90cmVlKHBzMl9zdWJzZXQpKQ0KcGh5X3RyZWUocHMyX3N1YnNldCkgPC0gbWFrZU5vZGVMYWJlbChwaHlfdHJlZShwczJfc3Vic2V0KSwgbWV0aG9kPSJudW1iZXIiLCBwcmVmaXg9J24nKQ0KbmFtZS5iYWxhbmNlKHBoeV90cmVlKHBzMl9zdWJzZXQpLCB0YXhfdGFibGUocHMyX3N1YnNldCksICduMScpDQoNCiNDYWxjdWxhdGUgZGlmZmVyZW50IGRpc3RhbmNlIG1ldHJpY3MNCm1ldHJpY3MgPC0gYygiQnJheSIsICJKYWNjYXJkIiwgIkFpdGNoaXNvbiIsIlBoaWxyIiwgIlVuaWZyYWMiLCAiV1VuaWZyYWMiKSAgDQpkaXN0bGlzdF9zdWJzZXQgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoPWxlbmd0aChtZXRyaWNzKSkNCm5hbWVzKGRpc3RsaXN0X3N1YnNldCkgPC0gbWV0cmljcw0KDQpkaXN0bGlzdF9zdWJzZXQkSmFjY2FyZCA8LSBhcy5tYXRyaXgodmVnZGlzdChvdHV0YWJfc3Vic2V0LCBtZXRob2Q9ImphYyIsYmluYXJ5ID0gVCkpDQpkaXN0bGlzdF9zdWJzZXQkQnJheSA8LSBhcy5tYXRyaXgodmVnZGlzdChvdHV0YWJfc3Vic2V0LCBtZXRob2Q9ImJyYXkiKSkNCmRpc3RsaXN0X3N1YnNldCRBaXRjaGlzb24gPC0gYXMubWF0cml4KHZlZ2Rpc3QoQ29EYVNlcTo6Y29kYVNlcS5jbHIob3R1dGFiX3N1YnNldF9uMCksIG1ldGhvZD0iZXVjbGlkZWFuIikpDQpkaXN0bGlzdF9zdWJzZXQkUGhpbHIgPC0gYXMubWF0cml4KHZlZ2Rpc3QocGhpbHI6OnBoaWxyKG90dXRhYl9zdWJzZXRfbjAsIHBoeV90cmVlKHBzMl9zdWJzZXQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFydC53ZWlnaHRzPSdlbm9ybS54LmdtLmNvdW50cycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbHIud2VpZ2h0cz0nYmx3LnNxcnQnKSwgbWV0aG9kPSJldWNsaWRlYW4iLCBuYS5ybT1UUlVFKSkNCmRpc3RsaXN0X3N1YnNldCRVbmlmcmFjIDwtIGFzLm1hdHJpeChwaHlsb3NlcTo6VW5pRnJhYyhwczJfc3Vic2V0LCB3ZWlnaHRlZD1GQUxTRSwgcGFyYWxsZWwgPSBUUlVFKSkNCmRpc3RsaXN0X3N1YnNldCRXVW5pZnJhYyA8LSBhcy5tYXRyaXgocGh5bG9zZXE6OlVuaUZyYWMocHMyX3N1YnNldCwgd2VpZ2h0ZWQ9VFJVRSwgcGFyYWxsZWwgPSBUUlVFKSkNCg0KIyBNYW50ZWwgdGVzdCB0byBjaGVjayBjb25jb3JkYW5jZSBvZiBiZXRhIGRpdmVyc2l0eSBwcmUgYW5kIHBvc3QgZmlsdGVyaW5nDQoNCnB1cnJyOjptYXAyKGRpc3RsaXN0LCBkaXN0bGlzdF9maWx0LH57DQogIHN1YnNhbXBsZSA8LSBpbnRlcnNlY3QoY29sbmFtZXMoLngpLCBjb2xuYW1lcygueSkpDQogIGFzLmRhdGEuZnJhbWUodmVnYW46Om1hbnRlbCgueFtzdWJzYW1wbGUsIHN1YnNhbXBsZV0sIC55W3N1YnNhbXBsZSwgc3Vic2FtcGxlXSlbYygic3RhdGlzdGljIiwic2lnbmlmIiwicGVybXV0YXRpb25zIildKQ0KfSkgJT4lDQogIGJpbmRfcm93cyguaWQ9ImRpc3QiKQ0KDQojIE1hbnRlbCB0ZXN0IHRvIGNoZWNrIGNvbmNvcmRhbmNlIG9mIGJldGEgZGl2ZXJzaXR5IHByZSBhbmQgcG9zdCBzdWJzZXQNCg0KcHVycnI6Om1hcDIoZGlzdGxpc3QsIGRpc3RsaXN0X3N1YnNldCx+ew0KICBzdWJzYW1wbGUgPC0gaW50ZXJzZWN0KGNvbG5hbWVzKC54KSwgY29sbmFtZXMoLnkpKQ0KICBhcy5kYXRhLmZyYW1lKHZlZ2FuOjptYW50ZWwoLnhbc3Vic2FtcGxlLCBzdWJzYW1wbGVdLCAueVtzdWJzYW1wbGUsIHN1YnNhbXBsZV0pW2MoInN0YXRpc3RpYyIsInNpZ25pZiIsInBlcm11dGF0aW9ucyIpXSkNCn0pICU+JQ0KICBiaW5kX3Jvd3MoLmlkPSJkaXN0IikNCmBgYA0KDQoNCiMjIEFkb25pcyAmIEJldGFkaXNwZXINCg0KYGBge3IgQWRvbmlzfQ0KIyBBRE9OSVMgaXMgY29uc3RydWN0ZWQgaGVpcmFyY2hpYWxseSB0byBtYXJnaW5hbGlzZSB0ZWNoaWNhbCB2YXJpYW5jZSB0aGVuIG1vdmluZyBkb3duIHRoZSB0YXhvbm9taWMgcmFua3MgDQoNCiMgQWRvbmlzIHRlc3QNCm1ldGFkYXRhIDwtIHNhbXBsZV9kYXRhKHBzMikgJT4lDQogIGFzKCJkYXRhLmZyYW1lIikNCmFkb25pc19yZXN1bHRzIDwtIGRpc3RsaXN0ICU+JQ0KICBwdXJycjo6bWFwKGZ1bmN0aW9uKHgpIHsNCiAgICB5IDwtIGFzLmRpc3QoeFttZXRhZGF0YSRTYW1wbGVfTmFtZSwgbWV0YWRhdGEkU2FtcGxlX05hbWVdKQ0KICAgIGJpbmRfcm93cygNCiAgICBicm9vbTo6dGlkeShhZG9uaXMyKHl+c2VxcnVuK3BzeWxsaWRfZmFtaWx5K3BzeWxsaWRfZ2VudXMrcHN5bGxpZF9zcHAsIG1ldGhvZD0iZXVjbGlkZWFuIiwgZGF0YT1tZXRhZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgIHBlcm11dGF0aW9ucz05OTksIGJ5PSJ0ZXJtcyIpKSAlPiUgDQogICAgICBtdXRhdGUodGVzdCA9IHBhc3RlKHRlcm1bIXRlcm0gJWluJSBjKCJSZXNpZHVhbCIsICJUb3RhbCIpXSwgY29sbGFwc2U9Ii0iKSksDQogICAgI2Jyb29tOjp0aWR5KGFkb25pczIoeX5zZXFydW4raG9zdHBsYW50X3NwcCtwc3lsbGlkX3NwcCwgbWV0aG9kPSJldWNsaWRlYW4iLCBkYXRhPW1ldGFkYXRhLCANCiAgICAgIyAgICAgICAgICAgICAgICAgIHBlcm11dGF0aW9ucz05OTksIGJ5PSJtYXJnaW4iKSkgJT4lIA0KICAgICMgIG11dGF0ZSh0ZXN0ID0gcGFzdGUodGVybVshdGVybSAlaW4lIGMoIlJlc2lkdWFsIiwgIlRvdGFsIildLCBjb2xsYXBzZT0iLSIpKSwNCiAgICBicm9vbTo6dGlkeShhZG9uaXMyKHl+c2VxcnVuK2hvc3RwbGFudF9zcHAsIG1ldGhvZD0iZXVjbGlkZWFuIiwgZGF0YT1tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgcGVybXV0YXRpb25zPTk5OSwgYnk9InRlcm1zIikpICU+JSANCiAgICAgIG11dGF0ZSh0ZXN0ID0gcGFzdGUodGVybVshdGVybSAlaW4lIGMoIlJlc2lkdWFsIiwgIlRvdGFsIildLCBjb2xsYXBzZT0iLSIpKQ0KICAgICkNCn0pICAlPiUNCiAgYmluZF9yb3dzKC5pZD0iZGlzdCIpDQoNCiMgQ2hlY2sgaG9tb2dlbmVpdHkNCmJldGFkaXNwZXJfcmVzdWx0cyA8LSBkaXN0bGlzdCAlPiUNCiAgcHVycnI6Om1hcChmdW5jdGlvbih4KSB7DQogICAgeSA8LSBhcy5kaXN0KHhbbWV0YWRhdGEkU2FtcGxlX05hbWUsIG1ldGFkYXRhJFNhbXBsZV9OYW1lXSkNCiAgYmluZF9yb3dzKA0KICAgIGFzX3RpYmJsZShwZXJtdXRlc3QodmVnYW46OmJldGFkaXNwZXIoeSwgbWV0YWRhdGEkcHN5bGxpZF9zcHApKSR0YWIsIHJvd25hbWVzPSJ0ZXJtIikgJT4lDQogICAgICBtdXRhdGUodGVzdD0icHN5bGxpZF9zcHAiKSwNCiAgICBhc190aWJibGUocGVybXV0ZXN0KHZlZ2FuOjpiZXRhZGlzcGVyKHksIG1ldGFkYXRhJGhvc3RwbGFudF9zcHApKSR0YWIsIHJvd25hbWVzPSJ0ZXJtIikgICU+JQ0KICAgICAgbXV0YXRlKHRlc3Q9Imhvc3RwbGFudF9zcHAiKSwNCiAgKQ0KfSkgICU+JQ0KICBiaW5kX3Jvd3MoLmlkPSJkaXN0IikNCg0KZGlyLmNyZWF0ZSgib3V0cHV0L2JldGEiKQ0Kd3JpdGVfY3N2KGFkb25pc19yZXN1bHRzLCAib3V0cHV0L2JldGEvYWRvbmlzX2Z1bGxkYXRhLmNzdiIpDQp3cml0ZV9jc3YoYmV0YWRpc3Blcl9yZXN1bHRzLCAib3V0cHV0L2JldGEvYmV0YWRpc3Blcl9mdWxsZGF0YS5jc3YiKQ0KYGBgDQoNCiMjIFNhbWUgdHJlZSBkaXNzaW1pbGFyaXRpZXMNCg0KTG9vayBhdCB0aGUgc2ltaWxhcml0aWVzIGluIHRoZSBtaWNyb2Jpb21lIG9mIHRoZSBwc3lsbGlkIHNwZWNpbWVucyBjb2xsZWN0ZWQgZnJvbSB0aGUgc2FtZSBob3N0IHBsYW50DQpgYGB7ciBBdmcgRGlzc2ltaWFyaXR5fQ0KaG9zdHBsYW50X21ldGFkYXRhIDwtIG1ldGFkYXRhICU+JSBtdXRhdGUoaW5ncm91cCA9IGNhc2Vfd2hlbigNCiAgU2FtcGxlX05hbWUgJWluJSBjKCI5NCIsIjEwNyIsICIxMTMiLCI5MyIsIjEwNiIsICIxMTIiKSB+ICJmcmF4aW5pLWZyYXhpbmljb2xhIiwNCiAgU2FtcGxlX05hbWUgJWluJSBjKCIyMDBiaWciLCAiMjAxYmlnIiwgIjIwMHNtYWxsIiwgIjIwMXNtYWxsIikgfiAiYXBpY2FsaXMtZnJvZG9iYWdnaW5zaSIsDQogIFRSVUUgfiAib3RoZXIiDQogICkpDQoNCmJyb29tOjp0aWR5KGFkb25pczIoZGlzdGxpc3QkQWl0Y2hpc29ufmluZ3JvdXAgKyBwc3lsbGlkX3NwcCwgbWV0aG9kPSJldWNsaWRlYW4iLA0KICAgICAgICAgICAgICAgICAgIGRhdGE9aG9zdHBsYW50X21ldGFkYXRhKSkNCg0KcGFpcndpc2UuYWRvbmlzMihkaXN0bGlzdCRBaXRjaGlzb25+aW5ncm91cCArIHBzeWxsaWRfc3BwLCBtZXRob2Q9ImV1Y2xpZGVhbiIsDQogICAgICAgICAgICAgICAgICAgZGF0YT1ob3N0cGxhbnRfbWV0YWRhdGEpDQpgYGANCg0KIyMgQmFycGxvdA0KDQpgYGB7ciBiYXJwbG90fQ0KIyBQbG90IHRyZWUNCnAgPC0gZ2d0cmVlKHBydW5lZC50cmVlKSArIGdlb21fdGlwbGFiKGFsaWduPVRSVUUpICsgZ2VvbV9ub2RlbGFiKGdlb209J2xhYmVsJykgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQ9YygwLCAwLjEpKSANCg0KIyBQbG90IGJhcg0KcHMzX2JhciA8LSBwczMgJT4lDQogIHNwZWVkeXNlcTo6dGF4X2dsb20odGF4cmFuayA9ICJvcmRlciIpICU+JSAgICAgICAgICAgIyBhZ2dsb21lcmF0ZSBhdCBPcmRlciBsZXZlbA0KICB0cmFuc2Zvcm1fc2FtcGxlX2NvdW50cyhmdW5jdGlvbih4KSB7eC9zdW0oeCl9ICkgJT4lICMgVHJhbnNmb3JtIHRvIHJlbC4gYWJ1bmRhbmNlDQogIHNwZWVkeXNlcTo6cHNtZWx0KCkgJT4lDQogIG11dGF0ZShwbG90bGFiZWwgPSBwaHlsdW0pICU+JQ0KICBtdXRhdGUocGxvdGxhYmVsID0gY2FzZV93aGVuKA0KICAgIEFidW5kYW5jZSA+PSAwLjAxICYgcGh5bHVtPT0iUHJvdGVvYmFjdGVyaWEiIH4gcGFzdGUwKCJQIC0gIiwgb3JkZXIpLCAjIENoYW5nZSB0aGlzIHRvIHdoYXRldmVyIHRheHJhbmsgd2Ugd2FudA0KICAgIEFidW5kYW5jZSA+PSAwLjAxICYgIXBoeWx1bT09IlByb3Rlb2JhY3RlcmlhIn4gcGh5bHVtICwNCiAgICBBYnVuZGFuY2UgPCAwLjAxIH4gIk5BIg0KICAgICkpICU+JQ0KICBkcGx5cjo6bmFfaWYoIk5BIikgJT4lDQogIGRwbHlyOjpzZWxlY3QocHN5bGxpZF9zcHAsIHBsb3RsYWJlbCwgcGh5bHVtLCBvcmRlciwgQWJ1bmRhbmNlKSAlPiUNCiAgbGVmdF9qb2luKHAkZGF0YSAlPiUNCiAgICAgICAgICAgICAgYXNfZGF0YV9mcmFtZSAlPiUNCiAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihpc1RpcCkgJT4lDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoeSwgbGFiZWwpICU+JQ0KICAgICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHBzeWxsaWRfc3BwPWxhYmVsKSkgDQoNCmdnLmJhciA8LSBnZ3Bsb3QocHMzX2JhciwgYWVzKHg9eSwgeT1BYnVuZGFuY2UsIGZpbGw9cGxvdGxhYmVsKSkgKw0KICBnZW9tX2NvbCgpICArIA0KICBjb29yZF9mbGlwKCkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoOSwgIlNldDEiKSkobGVuZ3RoKHVuaXF1ZShwczNfYmFyJHBsb3RsYWJlbCkpLTEpLCBuYS52YWx1ZT0iZ3JleSIpICsgDQogICAgYmFzZV90aGVtZSAgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgI3BhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfbGluZShjb2xvdXI9ImdyZXk5MiIsIHNpemU9MC41LCBsaW5ldHlwZT0iZGFzaGVkIiksDQogICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTkyIiwgDQogICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxKSwNCiAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgYXhpcy50aWNrcy55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAsMCksIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkrDQogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQ9YygwLDApKSArDQogIGxhYnMoeCA9IE5VTEwgLA0KICAgICAgIHkgPSAiUmVsYXRpdmUgQWJ1bmRhbmNlIiwNCiAgICAgICBmaWxsID0gTlVMTCkNCg0KIyBNYWtlIHJpY2huZXNzIHBsb3RzDQpnZy5yaWNoIDwtICBkaXZfdGFibGUgJT4lDQogIGdyb3VwX2J5KHBzeWxsaWRfc3BwKSAlPiUNCiAgc3VtbWFyaXNlKGFscGhhPW1lYW4oYWxwaGEpLCBwZD1tZWFuKHBkKS8xMGUrNywgU2hhbm5vbj1tZWFuKFNoYW5ub24pKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBwaXZvdF9sb25nZXIoLXBzeWxsaWRfc3BwLCBuYW1lc190bz0ibWVhc3VyZSIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAgJT4lDQogIGxlZnRfam9pbihsaXBhX291dCAlPiUgDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QocHN5bGxpZF9zcHAsIGFscGhhX3B2YWwsIHBkX3B2YWwsIFNoYW5ub25fcHZhbCkgJT4lDQogICAgICAgICAgICAgIHBpdm90X2xvbmdlcigtcHN5bGxpZF9zcHAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc190bz0ibWVhc3VyZSIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicHZhbCIpICU+JQ0KICAgICAgICAgICAgICBtdXRhdGUobWVhc3VyZSA9IHN0cl9yZW1vdmUobWVhc3VyZSwgIl9wdmFsIikpKSAlPiUNCiAgbGVmdF9qb2luKHAkZGF0YSAlPiUNCiAgICAgICAgICAgICAgYXNfZGF0YV9mcmFtZSAlPiUNCiAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihpc1RpcCkgJT4lDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoeSwgbGFiZWwpICU+JQ0KICAgICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHBzeWxsaWRfc3BwPWxhYmVsKSkgJT4lDQogIG11dGF0ZV9hdCh2YXJzKG1lYXN1cmUpLCBmdW5zKGZhY3RvciguLCBsZXZlbHM9YygiYWxwaGEiLCJTaGFubm9uIiwicGQiKSkpKSAlPiUNCiAgZ2dwbG90KGFlcyh4PXksIHk9dmFsdWUsIGZpbGw9cHZhbDwwLjA1KSkgKyANCiAgICBnZW9tX2NvbCgpICsNCiAgICBmYWNldF9ncmlkKH5tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArIA0KICAgIGNvb3JkX2ZsaXAoKSsNCiAgICBiYXNlX3RoZW1lICArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9saW5lKGNvbG91cj0iZ3JleTkyIiwgc2l6ZT0wLjUsIGxpbmV0eXBlPSJkYXNoZWQiKSwNCiAgICMgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXk5MiIsIA0KICAgICMgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAxKSwNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImRhcmtncmF5IiwgImRhcmtyZWQiKSkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2NhbGVzOjpwcmV0dHlfYnJlYWtzKG49MSkpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kPWMoMCwwKSkrDQogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQ9YygwLDApKQ0KDQojIyBDb2xsZWN0aW9uX2hpc3QNCmdnLnNwcCA8LSBzYW1wbGVfZGF0YShwczIpICU+JQ0KICBhcygibWF0cml4IikgJT4lDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgZHBseXI6OnJlbmFtZShsYWJlbCA9IHBzeWxsaWRfc3BwKSAlPiUNCiAgZ3JvdXBfYnkobGFiZWwpICU+JQ0KICBzdW1tYXJpc2Uobl9zcGVjaWVzID0gbigpKSAlPiUNCiAgbGVmdF9qb2luKHAkZGF0YSAlPiUNCiAgZmlsdGVyKGlzVGlwKSAlPiUgZHBseXI6OnNlbGVjdChjKGxhYmVsLCB5KSkpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHkpKSAlPiUNCiAgZ2dwbG90KGFlcyh4PXksIHk9MSwgZmlsbD1uX3NwZWNpZXMpKSArDQogICAgZ2VvbV90aWxlKCkgKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9bl9zcGVjaWVzKSkrDQogICAgY29vcmRfZmxpcCgpICsNCiAgICB0aGVtZV92b2lkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJSZWRzIiwgZGlyZWN0aW9uPTEpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kPWMoMCwwKSkrDQogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQ9YygwLDApKQ0KDQojIE1ha2UgdHJlZSB3aXRoIG5vIHVuZGVyc2NvcmVzIGluIG5hbWUNCnAyIDwtIHANCnAyJGRhdGEkbGFiZWwgPC0gc3RyX3JlcGxhY2UocDIkZGF0YSRsYWJlbCwgIl8iLCAiICIpDQoNCiNBcnJhbmdlDQpGaWcxIDwtIHAyICsgZ2cuc3BwICsgZ2cuYmFyICsgZ2cucmljaCArIHBsb3RfbGF5b3V0KG5yb3c9MSwgd2lkdGhzPWMoMSwwLjA4LDIsMC42KSkgIA0KDQpwZGYoZmlsZT0iZmlncy9CZXRhLnBkZiIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTEgLCBwYXBlcj0iYTQiKQ0KICBwbG90KEZpZzEpDQp0cnkoZGV2Lm9mZigpLCBzaWxlbnQ9VFJVRSkNCmBgYA0KDQojIyBQaHlsb3N5bWJpb3Npcw0KDQojIyMgUHJlcGFyZSBkaXN0YW5jZSBtYXRyaWNlcw0KYGBge3IgcHJlcGFyZSBkaXN0YW5jZXN9DQojIyBQc3lsbGlkIHBoeWxvZ2VueSBjb3BoZW5ldGljIGRpc3RhbmNlIG1hdHJpeA0KcGh5bG8uZGlzdCA8LSBjb3BoZW5ldGljKHBydW5lZC50cmVlKSAlPiUNCiAgIHNxcnQoKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oInBzeWxsaWRfc3BwLngiKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9LXBzeWxsaWRfc3BwLngsDQogICAgICAgICAgICAgICBuYW1lc190bz0icHN5bGxpZF9zcHAueSIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiZGlzdCIpICU+JQ0KICByaWdodF9qb2luKHNhbXBsZV9kYXRhKHBzMikgJT4lDQogICAgICAgICAgICAgICBhcygibWF0cml4IikgJT4lDQogICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoU2FtcGxlX05hbWUsIHBzeWxsaWRfc3BwKSAlPiUNCiAgICAgICAgICAgICAgZHBseXI6OnJlbmFtZShwc3lsbGlkX3NwcC54ID0gcHN5bGxpZF9zcHAsIFNhbXBsZV9OYW1lLnggPSBTYW1wbGVfTmFtZSksDQogICAgICAgICAgICBieT0icHN5bGxpZF9zcHAueCIpJT4lDQogIHJpZ2h0X2pvaW4oc2FtcGxlX2RhdGEocHMyKSAlPiUNCiAgICAgICAgICAgICAgIGFzKCJtYXRyaXgiKSAlPiUNCiAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChTYW1wbGVfTmFtZSwgcHN5bGxpZF9zcHApICU+JQ0KICAgICAgICAgICAgICBkcGx5cjo6cmVuYW1lKHBzeWxsaWRfc3BwLnkgPSBwc3lsbGlkX3NwcCwgU2FtcGxlX05hbWUueSA9IFNhbXBsZV9OYW1lKSwNCiAgICAgICAgICAgIGJ5PSJwc3lsbGlkX3NwcC55IikgJT4lDQogIGZpbHRlcighaXMubmEoZGlzdCkpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1wc3lsbGlkX3NwcC55LCAtcHN5bGxpZF9zcHAueCkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBTYW1wbGVfTmFtZS55LCB2YWx1ZXNfZnJvbSA9IGRpc3QpICAlPiUNCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJTYW1wbGVfTmFtZS54IikgJT4lDQogIGFzLm1hdHJpeCgpDQoNCnBsYW50LnRyZWUgPC0gcmVhZC50cmVlKCJzYW1wbGVfZGF0YS9wbGFudF90cmVlLm53ayIpDQpwbGFudC5kaXN0IDwtIGNvcGhlbmV0aWMocGxhbnQudHJlZSkgJT4lDQogIHNxcnQoKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oImhvc3RwbGFudF9zcHAueCIpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scz0taG9zdHBsYW50X3NwcC54LA0KICAgICAgICAgICAgICAgbmFtZXNfdG89Imhvc3RwbGFudF9zcHAueSIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiZGlzdCIpICU+JQ0KICByaWdodF9qb2luKHNhbXBsZV9kYXRhKHBzMikgJT4lDQogICAgICAgICAgICAgICBhcygibWF0cml4IikgJT4lDQogICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoU2FtcGxlX05hbWUsIGhvc3RwbGFudF9zcHApICU+JQ0KICAgICAgICAgICAgICBkcGx5cjo6cmVuYW1lKGhvc3RwbGFudF9zcHAueCA9IGhvc3RwbGFudF9zcHAsIFNhbXBsZV9OYW1lLnggPSBTYW1wbGVfTmFtZSksDQogICAgICAgICAgICBieT0iaG9zdHBsYW50X3NwcC54IiklPiUNCiAgcmlnaHRfam9pbihzYW1wbGVfZGF0YShwczIpICU+JQ0KICAgICAgICAgICAgICAgYXMoIm1hdHJpeCIpICU+JQ0KICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KFNhbXBsZV9OYW1lLCBob3N0cGxhbnRfc3BwKSAlPiUNCiAgICAgICAgICAgICAgZHBseXI6OnJlbmFtZShob3N0cGxhbnRfc3BwLnkgPSBob3N0cGxhbnRfc3BwLCBTYW1wbGVfTmFtZS55ID0gU2FtcGxlX05hbWUpLA0KICAgICAgICAgICAgYnk9Imhvc3RwbGFudF9zcHAueSIpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGRpc3QpKSU+JQ0KICBkcGx5cjo6c2VsZWN0KC1ob3N0cGxhbnRfc3BwLnksIC1ob3N0cGxhbnRfc3BwLngpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gU2FtcGxlX05hbWUueSwgdmFsdWVzX2Zyb20gPSBkaXN0KSAlPiUNCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJTYW1wbGVfTmFtZS54IikgJT4lDQogIGFzLm1hdHJpeCgpDQoNCiMgU3BhdGlhbCBkaXN0YW5jZSBtYXRyaXgNCmVudkRhdGEgPC0gc2FtcGxlX2RhdGEocHMyKSAlPiUNCiAgYXMoIm1hdHJpeCIpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIGRwbHlyOjpzZWxlY3QobG9uZywgbGF0KSAlPiUNCiAgbXV0YXRlKGxvbmcgPSBhcy5udW1lcmljKGxvbmcpLCBsYXQ9YXMubnVtZXJpYyhsYXQpKSU+JQ0KICBkcm9wX25hKCkgDQogIA0Kc3BhdC5kaXN0IDwtIHNwRGlzdHMoYXMubWF0cml4KGVudkRhdGEpLCBsb25nbGF0PVRSVUUpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIG1hZ3JpdHRyOjpzZXRfcm93bmFtZXMocm93bmFtZXMoZW52RGF0YSkgJT4lIHN0cl9yZXBsYWNlKHBhdHRlcm49IlxcX1MoLiopJCIscmVwbGFjZW1lbnQ9IiIpICU+JSBtYWtlLnVuaXF1ZSgpKSAlPiUNCiAgbWFncml0dHI6OnNldF9jb2xuYW1lcyhyb3duYW1lcyhlbnZEYXRhKSAlPiUgc3RyX3JlcGxhY2UocGF0dGVybj0iXFxfUyguKikkIixyZXBsYWNlbWVudD0iIikgJT4lIG1ha2UudW5pcXVlKCkpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZUlEIikgJT4lDQogIG11dGF0ZShTYW1wbGVJRCA9IFNhbXBsZUlEICU+JSBzdHJfcmVwbGFjZShwYXR0ZXJuPSJcXF9TKC4qKSQiLHJlcGxhY2VtZW50PSIiKSAlPiUgbWFrZS51bmlxdWUoKSkgJT4lDQogIGNvbHVtbl90b19yb3duYW1lcygiU2FtcGxlSUQiKSAlPiUNCiAgYXMubWF0cml4KCkNCg0KYGBgDQoNCiMjIyBXaG9sZSBkYXRhc2V0DQoNCmBgYHtyIHBoeWxvc3ltYmlvc2lzfQ0Kc2V0LnNlZWQoOTA5KQ0KZGlyLmNyZWF0ZSgib3V0cHV0L3BoeWxvc3ltYmlvc2lzIikNCg0KIyBNYXRyaXggY29ycmVsYXRpb25zDQojb25seSB1c2Ugc2FtcGxlcyBwcmVzZW50IGluIGFsbA0Kc3Vic2FtcGxlIDwtIFJlZHVjZShpbnRlcnNlY3QsIGxpc3Qocm93bmFtZXMob3R1X3RhYmxlKHBzMikpLCBjb2xuYW1lcyhwaHlsby5kaXN0KSwgY29sbmFtZXMocGxhbnQuZGlzdCksIGNvbG5hbWVzKHNwYXQuZGlzdCkpKQ0KDQojIE1hbnRlbCB0ZXN0DQptYW50ZWxfcmVzdWx0cyA8LSBkaXN0bGlzdCAlPiUNCiAgcHVycnI6Om1hcChmdW5jdGlvbih4KXsNCiAgICBydW5fbWFudGVsKHgsIGRpc3RzID0gYygicGh5bG8uZGlzdCIsICJwbGFudC5kaXN0IiwgInNwYXQuZGlzdCIpLA0KICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gc3Vic2FtcGxlLCB0eXBlICA9Im1hbnRlbCIsIG5ib290PTEwMDApDQogIH0pICU+JQ0KICBiaW5kX3Jvd3MoLmlkPSJkaXN0IikNCg0Kd3JpdGVfY3N2KG1hbnRlbF9yZXN1bHRzICU+JQ0KICAgICAgICAgICAgZHBseXI6OnNlbGVjdCgtb25lX29mKCJwdmFsMSIsInB2YWwyIikpLCNvbmx5IGtlZXAgdHdvIHNpZGVkIFAgdmFsdWVzDQogICAgICAgICAgIm91dHB1dC9waHlsb3N5bWJpb3Npcy9tYW50ZWxfZnVsbGRhdGEuY3N2IikNCg0KDQojIFBhcnRpYWwgTWFudGVsIFRlc3QNCnBtYW50ZWxfcmVzdWx0cyA8LSBkaXN0bGlzdCAlPiUNCiAgcHVycnI6Om1hcChmdW5jdGlvbih4KXsNCiAgICBydW5fbWFudGVsKHgsIGRpc3RzID0gYygicGh5bG8uZGlzdCIsICJwbGFudC5kaXN0IiwgInNwYXQuZGlzdCIpLA0KICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gc3Vic2FtcGxlLCB0eXBlID0gInBhcnRpYWwiLCBuYm9vdD0xMDAwKQ0KICB9KSAlPiUNCiAgYmluZF9yb3dzKC5pZD0iZGlzdCIpIA0KDQp3cml0ZV9jc3YocG1hbnRlbF9yZXN1bHRzICU+JSBkcGx5cjo6c2VsZWN0KC1vbmVfb2YoInB2YWwxIiwicHZhbDIiKSksDQogICAgICAgICAgIm91dHB1dC9waHlsb3N5bWJpb3Npcy9wbWFudGVsX2Z1bGxkYXRhLmNzdiIpDQoNCiMjIFBsb3QgbWFudGVscw0KZ2cubWFudGVscyA8LSBiaW5kX3Jvd3MobWFudGVsX3Jlc3VsdHMsIHBtYW50ZWxfcmVzdWx0cykgJT4lDQogICAgICAgICAgICAgIGRwbHlyOjptdXRhdGUoZGlzdDEgPSBjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgc3RyX2RldGVjdChkaXN0MSwgInBsYW50LmRpc3QiKSB+ICJIb3N0cGxhbnQgcGh5bG9nZW55IiwNCiAgICAgICAgICAgICAgICBzdHJfZGV0ZWN0KGRpc3QxLCAicGh5bG8uZGlzdCIpIH4gIlBzeWxsaWQgcGh5bG9nZW55IiwgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgc3RyX2RldGVjdChkaXN0MSwgInNwYXQuZGlzdCIpIH4gIlNwYXRpYWwgZGlzdGFuY2UiICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICApKSAlPiUNCiAgZmlsdGVyKGRpc3QgPT0gIkFpdGNoaXNvbiIpICU+JQ0KICBtdXRhdGUoZGlzdDEgPSBmYWN0b3IoZGlzdDEsIGxldmVscz0gYygiU3BhdGlhbCBkaXN0YW5jZSIsIkhvc3RwbGFudCBwaHlsb2dlbnkiLCAgIlBzeWxsaWQgcGh5bG9nZW55IikpKSAlPiUNCiAgbXV0YXRlKHR5cGUgPSB0eXBlICU+JSAgDQogICAgICAgICAgIHN0cl9yZXBsYWNlKCJtYW50ZWwiLCAiTWFudGVsIikgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlKCJwYXJ0aWFsX01hbnRlbCIsICJQYXJ0aWFsIE1hbnRlbCIpKSAlPiUNCiAgICAgICAgICAgICAgZ2dwbG90KGFlcyh4PW1hbnRlbHIsIHk9ZGlzdDEsIGNvbG91cj1kaXN0MSkpICsgDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3VyPSJibGFjayIsIGxpbmV0eXBlPTIpICsNCiAgZ2VvbV9wb2ludHJhbmdlKGFlcyh4bWluPWBsbGltLjIuNSVgLCB4bWF4PWB1bGltLjk3LjUlYCksIHNpemU9MSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIkhvc3RwbGFudCBwaHlsb2dlbnkiPSIjYjJkZjhhIiwiU3BhdGlhbCBkaXN0YW5jZSI9IiNhNmNlZTMiLCAiUHN5bGxpZCBwaHlsb2dlbnkiPSIjMWY3OGI0IikpKw0KICBmYWNldF93cmFwKH50eXBlLCBuY29sPTIpICsNCiAgbGFicyggeCA9ICJNYW50ZWwgUiIsIHkgPSBOVUxMLCBjb2xvdXI9TlVMTCkgKw0KICBiYXNlX3RoZW1lICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKCkpDQoNCmdnLm1hbnRlbHMNCg0KcGRmKGZpbGU9ImZpZ3MvZmlnM19tYW50ZWxzLnBkZiIsICB3aWR0aCA9IDgsIGhlaWdodCA9IDQsIHBhcGVyPSJhNHIiKQ0KICBwbG90KGdnLm1hbnRlbHMpDQp0cnkoZGV2Lm9mZigpLCBzaWxlbnQ9VFJVRSkNCiAgDQpgYGANCg0KIyMjIFdpdGhvdXQgR2FtbWFwcm90ZW9iYWN0ZXJpYQ0KDQpgYGB7ciBwaHlsb3N5bWJpb3NpcyBubyBlbnRlcm99DQojIEFkb25pcyB0ZXN0DQptZXRhZGF0YSA8LSBzYW1wbGVfZGF0YShwczJfc3Vic2V0KSAlPiUNCiAgYXMoImRhdGEuZnJhbWUiKQ0KDQphZG9uaXNfcmVzdWx0cyA8LSBkaXN0bGlzdF9zdWJzZXQgJT4lDQogIHB1cnJyOjptYXAoZnVuY3Rpb24oeCkgew0KICAgIHkgPC0gYXMuZGlzdCh4W21ldGFkYXRhJFNhbXBsZV9OYW1lLCBtZXRhZGF0YSRTYW1wbGVfTmFtZV0pDQogICAgYmluZF9yb3dzKA0KICAgIGJyb29tOjp0aWR5KGFkb25pczIoeX5zZXFydW4rcHN5bGxpZF9mYW1pbHkrcHN5bGxpZF9nZW51cytwc3lsbGlkX3NwcCwgbWV0aG9kPSJldWNsaWRlYW4iLCBkYXRhPW1ldGFkYXRhLCANCiAgICAgICAgICAgICAgICAgICAgICAgcGVybXV0YXRpb25zPTk5OSkpICU+JSANCiAgICAgIG11dGF0ZSh0ZXN0ID0gcGFzdGUodGVybVshdGVybSAlaW4lIGMoIlJlc2lkdWFsIiwgIlRvdGFsIildLCBjb2xsYXBzZT0iLSIpKSwNCiAgICBicm9vbTo6dGlkeShhZG9uaXMyKHl+c2VxcnVuK2hvc3RwbGFudF9zcHAsIG1ldGhvZD0iZXVjbGlkZWFuIiwgZGF0YT1tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgcGVybXV0YXRpb25zPTk5OSkpICU+JSANCiAgICAgIG11dGF0ZSh0ZXN0ID0gcGFzdGUodGVybVshdGVybSAlaW4lIGMoIlJlc2lkdWFsIiwgIlRvdGFsIildLCBjb2xsYXBzZT0iLSIpKQ0KICAgICkNCn0pICAlPiUNCiAgYmluZF9yb3dzKC5pZD0iZGlzdCIpDQoNCndyaXRlX2NzdihhZG9uaXNfcmVzdWx0cywgIm91dHB1dC9iZXRhL2Fkb25pc19zdWJzZXQuY3N2IikNCg0KIyBNYXRyaXggY29ycmVsYXRpb25zDQojb25seSB1c2Ugc2FtcGxlcyBwcmVzZW50IGluIGFsbA0Kc3Vic2FtcGxlIDwtIFJlZHVjZShpbnRlcnNlY3QsIGxpc3Qocm93bmFtZXMob3R1X3RhYmxlKHBzMl9zdWJzZXQpKSwgY29sbmFtZXMocGh5bG8uZGlzdCksIGNvbG5hbWVzKHBsYW50LmRpc3QpLCBjb2xuYW1lcyhzcGF0LmRpc3QpKSkNCg0KIyBNYW50ZWwgdGVzdA0KbWFudGVsX3Jlc3VsdHMgPC0gZGlzdGxpc3Rfc3Vic2V0ICU+JQ0KICBwdXJycjo6bWFwKGZ1bmN0aW9uKHgpew0KICAgIHJ1bl9tYW50ZWwoeCwgZGlzdHM9YygicGh5bG8uZGlzdCIsICJwbGFudC5kaXN0IiwgInNwYXQuZGlzdCIpLA0KICAgICAgICAgICAgICAgc3Vic2FtcGxlPXN1YnNhbXBsZSwgdHlwZT0ibWFudGVsIikNCiAgfSkgJT4lDQogIGJpbmRfcm93cyguaWQ9ImRpc3QiKSANCg0Kd3JpdGVfY3N2KG1hbnRlbF9yZXN1bHRzICU+JSBkcGx5cjo6c2VsZWN0KC1wdmFsMSwgLXB2YWwyKSwgIm91dHB1dC9waHlsb3N5bWJpb3Npcy9tYW50ZWxfc3Vic2V0LmNzdiIpDQoNCiMgUGFydGlhbCBNYW50ZWwgVGVzdA0KcG1hbnRlbF9yZXN1bHRzIDwtIGRpc3RsaXN0X3N1YnNldCAlPiUNCiAgcHVycnI6Om1hcChmdW5jdGlvbih4KXsNCiAgICBydW5fbWFudGVsKHgsIGRpc3RzPWMoInBoeWxvLmRpc3QiLCAicGxhbnQuZGlzdCIsICJzcGF0LmRpc3QiKSwNCiAgICAgICAgICAgICAgIHN1YnNhbXBsZT1zdWJzYW1wbGUsIHR5cGU9InBhcnRpYWwiKQ0KICB9KSAlPiUNCiAgYmluZF9yb3dzKC5pZD0iZGlzdCIpIA0KDQp3cml0ZV9jc3YocG1hbnRlbF9yZXN1bHRzICU+JSBkcGx5cjo6c2VsZWN0KC1wdmFsMSwtcHZhbDIpLCAib3V0cHV0L3BoeWxvc3ltYmlvc2lzL3BtYW50ZWxfc3Vic2V0LmNzdiIpDQpgYGANCg0KIyMgUENBIHBsb3RzDQpgYGB7ciBQQ0F9DQojc2V0IGRpc3RhbmNlDQpwY2FfZGlzdCA8LSBkaXN0bGlzdCRBaXRjaGlzb24NCg0KI1BDQSANCnIucGN4IDwtIHByY29tcChwY2FfZGlzdCkNCnBjX3NhbXAgPC0gZGF0YS5mcmFtZShTYW1wbGVfTmFtZSA9IHJvd25hbWVzKHIucGN4JHgpLCByLnBjeCR4WywgMToyXSklPiUNCiAgbGVmdF9qb2luKHNhbXBsZV9kYXRhKHBzMikgJT4lDQogICAgICAgICAgICAgIGFzKCJtYXRyaXgiKSAlPiUNCiAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpLCBieT0iU2FtcGxlX05hbWUiKQ0KIyBjYWxjdWxhdGUgcGVyY2VudCB2YXJpYW5jZSBleHBsYWluZWQgZm9yIHRoZSBheGlzIGxhYmVscw0KcGMxIDwtIHJvdW5kKHIucGN4JHNkZXZbMV1eMi9zdW0oci5wY3gkc2Rldl4yKSwyKQ0KcGMyIDwtIHJvdW5kKHIucGN4JHNkZXZbMl1eMi9zdW0oci5wY3gkc2Rldl4yKSwyKQ0KcGNfeWxhYiA8LSBwYXN0ZSgiUEMxOiAiLCBwYzEsIHNlcD0iIikNCnBjX3hsYWIgPC0gcGFzdGUoIlBDMjogIiwgcGMyLCBzZXA9IiIpDQoNCiMjIGNvbG91ciBieSBwc3lsbGlkIHBoeWxvZ2VueQ0KZGVuZCA8LSBhcy5kZW5kcm9ncmFtKGZvcmNlLnVsdHJhbWV0cmljKHBydW5lZC50cmVlKSkNCm1lbWJlcnNoaXAgPC0gYXMuZGF0YS5mcmFtZShjdXRyZWUoZGVuZCwgaz0xMSkpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oInBzeWxsaWRfc3BwIikgJT4lDQogIG1hZ3JpdHRyOjpzZXRfY29sbmFtZXMoYygicHN5bGxpZF9zcHAiLCAiY2x1c3RlciIpKQ0KDQpwY2FfcGh5bG8gPC0gcGNfc2FtcCAlPiUNCiAgbGVmdF9qb2luKG1lbWJlcnNoaXApDQpnZy5wY2EgPC0gZ2dwbG90KGRhdGE9cGNhX3BoeWxvLCBhZXMoeD1QQzIsIHk9UEMxLCBjb2xvdXI9YXMuZmFjdG9yKGNsdXN0ZXIpKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUsIHNpemU9MykgKyAjLCBzaGFwZT0yMSwgY29sb3VyPSJibGFjayINCiAgI2dlb21fcG9pbnQoZGF0YT1wY19vdHUsYWVzKFBDMSwgUEMyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGU9IlBhaXJlZCIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9MiwgYWxwaGE9MC41KSArICANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9MiwgYWxwaGE9MC41KSArDQogIHhsYWIocGNfeGxhYikgKyANCiAgeWxhYihwY195bGFiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBzY2FsZV95X3JldmVyc2UocG9zaXRpb24gPSAicmlnaHQiKSANCiAgI2Nvb3JkX2ZpeGVkKHJhdGlvPXBjMi9wYzEpICMgU2NhbGUgcGxvdCBieSB2YXJpYW5jZSBleHBsYWluZWQNCg0KcDEgPC0gZ2d0cmVlKHBydW5lZC50cmVlKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZD1jKDAsIDAuMikpICsgDQogIHRoZW1lX3RyZWUyKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpIA0KDQpjb2xvdXJzX3AxIDwtIHAxJGRhdGEgJT4lDQogIGxlZnRfam9pbihtZW1iZXJzaGlwICU+JQ0KICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gcHN5bGxpZF9zcHApDQogICAgKSAlPiUNCiAgbXV0YXRlKG5ld19sYWJlbCA9IGxhYmVsICU+JSBzdHJfcmVwbGFjZV9hbGwoIl8iLCAiICIpKQ0KDQpwMSA8LSBwMSAlPCslIGNvbG91cnNfcDEgICsgDQogIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91cj1hcy5mYWN0b3IoY2x1c3RlcikpKSAgKyANCiAgZ2VvbV90aXBsYWIoYWVzKGNvbG91cj1hcy5mYWN0b3IoY2x1c3RlciksIGxhYmVsPW5ld19sYWJlbCkpKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGU9IlBhaXJlZCIpIA0KDQpnZy5wc3lsbGlkX3BjYSA8LSBwMSArIGdnLnBjYSArIHBsb3RfYW5ub3RhdGlvbih0aXRsZT0iUHN5bGxpZCBwaHlsb2dlbmV0aWMgZGlzdGFuY2UiKQ0KZ2cucHN5bGxpZF9wY2ENCg0KcGRmKGZpbGU9ImZpZ3MvcHN5bGxpZF9wY2EucGRmIiwgIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTEsIHBhcGVyPSJhNCIpDQogIHBsb3QoZ2cucHN5bGxpZF9wY2EpDQp0cnkoZGV2Lm9mZigpLCBzaWxlbnQ9VFJVRSkNCiAgDQojIENvbG91ciBieSBwbGFudCBwaHlsb2dlbnkNCnBsYW50LnRyZWUgPC0gcmVhZC50cmVlKCJzYW1wbGVfZGF0YS9wbGFudF90cmVlLm53ayIpDQpwbGFudC50cmVlICA8LSBkcm9wLnRpcChwbGFudC50cmVlLCAiU29waG9yYV9taWNyb3BoeWxsYV8ta293aGFpIiApDQpwbGFudC50cmVlIDwtIG11bHRpMmRpKHBsYW50LnRyZWUpDQoNCmRlbmQgPC0gYXMuZGVuZHJvZ3JhbShwbGFudC50cmVlICkNCiN0ZXN0IDwtIGNvbG9yX2JyYW5jaGVzKGRlbmQsIGs9MTIpDQojcGxvdCh0ZXN0KQ0KbWVtYmVyc2hpcCA8LSBhcy5kYXRhLmZyYW1lKGN1dHJlZShkZW5kLCBrPTEyKSkgJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigiaG9zdHBsYW50X3NwcCIpICU+JQ0KICBtYWdyaXR0cjo6c2V0X2NvbG5hbWVzKGMoImhvc3RwbGFudF9zcHAiLCAiY2x1c3RlciIpKQ0KDQpwY2FfcGh5bG8gPC0gcGNfc2FtcCAlPiUNCiAgbGVmdF9qb2luKG1lbWJlcnNoaXApDQpnZy5wY2EgPC0gZ2dwbG90KGRhdGE9cGNhX3BoeWxvLCBhZXMoeD1QQzIsIHk9UEMxLCBjb2xvdXI9YXMuZmFjdG9yKGNsdXN0ZXIpKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUsIHNpemU9MykgKyAjLCBzaGFwZT0yMSwgY29sb3VyPSJibGFjayINCiAgI2dlb21fcG9pbnQoZGF0YT1wY19vdHUsYWVzKFBDMSwgUEMyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGU9IlBhaXJlZCIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9MiwgYWxwaGE9MC41KSArICANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9MiwgYWxwaGE9MC41KSArDQogIHhsYWIocGNfeGxhYikgKyANCiAgeWxhYihwY195bGFiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBzY2FsZV95X3JldmVyc2UocG9zaXRpb24gPSAicmlnaHQiKQ0KICAjY29vcmRfZml4ZWQocmF0aW89cGMyL3BjMSkgIyBTY2FsZSBwbG90IGJ5IHZhcmlhbmNlIGV4cGxhaW5lZA0KDQpwMiA8LSBnZ3RyZWUocGxhbnQudHJlZSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kPWMoMCwgNzApKSArIA0KICB0aGVtZV90cmVlMigpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSANCg0KY29sb3Vyc19wMiA8LSBwMiRkYXRhICU+JQ0KICBsZWZ0X2pvaW4obWVtYmVyc2hpcCAlPiUNCiAgZHBseXI6OnJlbmFtZShsYWJlbCA9IGhvc3RwbGFudF9zcHApDQogICAgKSU+JQ0KICBtdXRhdGUobmV3X2xhYmVsID0gbGFiZWwgJT4lIHN0cl9yZXBsYWNlX2FsbCgiXyIsICIgIikpDQoNCnAyIDwtIHAyICU8KyUgY29sb3Vyc19wMiAgKyANCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3VyPWFzLmZhY3RvcihjbHVzdGVyKSkpICArIA0KICBnZW9tX3RpcGxhYihhZXMoY29sb3VyPWFzLmZhY3RvcihjbHVzdGVyKSwgbGFiZWw9bmV3X2xhYmVsKSkrDQogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgDQoNCmdnLnBsYW50X3BjYSA8LSBwMiArIGdnLnBjYSArIHBsb3RfYW5ub3RhdGlvbih0aXRsZT0iUGxhbnQgcGh5bG9nZW5ldGljIGRpc3RhbmNlIikNCmdnLnBsYW50X3BjYQ0KDQpwZGYoZmlsZT0iZmlncy9wbGFudF9wY2EucGRmIiwgIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTEsIHBhcGVyPSJhNCIpDQogIHBsb3QoZ2cucGxhbnRfcGNhKQ0KdHJ5KGRldi5vZmYoKSwgc2lsZW50PVRSVUUpDQoNCiMgSEMgb2Ygc3BhdGlhbCBkaXN0YW5jZQ0KZGVuZCA8LSBoY2x1c3QoYXMuZGlzdChzcGF0LmRpc3QpLCBtZXRob2Q9ImF2ZXJhZ2UiKQ0KI3Rlc3QgPC0gY29sb3JfYnJhbmNoZXMoZGVuZCwgaz0xMikNCiNwbG90KHRlc3QpDQoNCm1lbWJlcnNoaXAgPC0gYXMuZGF0YS5mcmFtZShjdXRyZWUoZGVuZCwgaz0xMikpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZV9OYW1lIikgJT4lDQogIG1hZ3JpdHRyOjpzZXRfY29sbmFtZXMoYygiU2FtcGxlX05hbWUiLCAiY2x1c3RlciIpKQ0KDQpwY2FfcGh5bG8gPC0gcGNfc2FtcCAlPiUNCiAgbGVmdF9qb2luKG1lbWJlcnNoaXApDQpnZy5wY2EgPC0gZ2dwbG90KGRhdGE9cGNhX3BoeWxvLCBhZXMoeD1QQzIsIHk9UEMxLCBjb2xvdXI9YXMuZmFjdG9yKGNsdXN0ZXIpKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYT0wLjUsIHNpemU9MykgKyAjLCBzaGFwZT0yMSwgY29sb3VyPSJibGFjayINCiAgI2dlb21fcG9pbnQoZGF0YT1wY19vdHUsYWVzKFBDMSwgUEMyKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGU9IlBhaXJlZCIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9MiwgYWxwaGE9MC41KSArICANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGU9MiwgYWxwaGE9MC41KSArDQogIHhsYWIocGNfeGxhYikgKyANCiAgeWxhYihwY195bGFiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBzY2FsZV95X3JldmVyc2UocG9zaXRpb24gPSAicmlnaHQiKQ0KICAjY29vcmRfZml4ZWQocmF0aW89cGMyL3BjMSkgIyBTY2FsZSBwbG90IGJ5IHZhcmlhbmNlIGV4cGxhaW5lZA0KDQpwMyA8LSBnZ3RyZWUoYXMucGh5bG8oZGVuZCkgKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZD1jKDAsIDQwMCkpICsNCiAgdGhlbWVfdHJlZTIoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgDQoNCmNvbG91cnNfcDMgPC0gcDMkZGF0YSAlPiUNCiAgbGVmdF9qb2luKG1lbWJlcnNoaXAgJT4lDQogIGRwbHlyOjpyZW5hbWUobGFiZWwgPSBTYW1wbGVfTmFtZSkNCiAgICApJT4lDQogIG11dGF0ZShuZXdfbGFiZWwgPSBsYWJlbCAlPiUgc3RyX3JlcGxhY2VfYWxsKCJfIiwgIiAiKSkNCg0KcDMgPC0gcDMgJTwrJSBjb2xvdXJzX3AzICArIA0KICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvdXI9YXMuZmFjdG9yKGNsdXN0ZXIpKSkgICsgDQogICNnZW9tX3RpcGxhYihhZXMoY29sb3VyPWFzLmZhY3RvcihjbHVzdGVyKSwgbGFiZWw9bmV3X2xhYmVsKSkrDQogIHNjYWxlX2NvbG91cl9icmV3ZXIocGFsZXR0ZT0iUGFpcmVkIikgDQoNCmdnLnNwYXRfcGNhIDwtIHAzICsgZ2cucGNhICsgcGxvdF9hbm5vdGF0aW9uKHRpdGxlPSJTcGF0aWFsIERpc3RhbmNlIikNCmdnLnNwYXRfcGNhDQoNCnBkZihmaWxlPSJmaWdzL3NwYXRpYWxfcGNhLnBkZiIsICB3aWR0aCA9IDgsIGhlaWdodCA9IDExLCBwYXBlcj0iYTQiKQ0KICBwbG90KGdnLnNwYXRfcGNhKQ0KdHJ5KGRldi5vZmYoKSwgc2lsZW50PVRSVUUpDQpgYGANCg0KIyBDb3BoeWxvZ2VueQ0KDQpSdW4gdGhlIHNjcmlwdHMgaW4gdGhlIFIgc3ViZGlyZWN0b3J5DQoNCmBgYHtiYXNoIHN1Ym1pdCBwYWNvIGpvYnN9DQpjZCAvZ3JvdXAvcGF0aG9nZW5zL0FsZXhwL01ldGFiYXJjb2RpbmcvUHN5bGxpZF9taWNyb2Jpb21lL3BhY29fcGFyYQ0KZG9zMnVuaXggYmF0Y2hfc3VibWl0X3BhY28uc2gNCmZpbmQgJCgvdXNyL2Jpbi9scyAtZCAkUFdELyopIC1uYW1lICdwYWNvXyonIC10eXBlIGYgfCBncmVwIC12ICcucmRzJyA+IHNlcXVlbmNlX2luZGV4LnR4dA0Kam9ibGVuZ3RoPSQoY2F0IHNlcXVlbmNlX2luZGV4LnR4dCB8IHdjIC1sKQ0Kc2JhdGNoIC0tYXJyYXk9MS0kam9ibGVuZ3RoIGJhdGNoX3N1Ym1pdF9wYWNvLnNoDQpgYGANCg0KIyBNaWNyb2Jpb21lIDMgZ2VuZXJhIGhlYXRtYXANCg0KYGBge3IgYWxsIG1pY3JvYmV9DQpmaWx0ZXJmdW4xIDwtIGZ1bmN0aW9uKHgpew0KICB4Wyh4IC8gc3VtKHgpKSA8ICgxZS00KV0gPC0gMCAjMWUtNCBpcyAwLjAxJSB0aHJlc2hvbGQNCiAgcmV0dXJuKHgpDQp9DQpwczNfZmlsdCAgPC0gdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMocHMzLCBmdW4gPSBmaWx0ZXJmdW4xKSU+JQ0KICBmaWx0ZXJfdGF4YShmdW5jdGlvbih4KSBtZWFuKHgpID4gMCwgVFJVRSkgI0Ryb3AgbWlzc2luZyB0YXhhIGZyb20gdGFibGUNCg0KcHJpbnQocGFzdGUwKChudGF4YShwczMpLW50YXhhKHBzM19maWx0KSksICIgdGF4YSB1bmRlciB0aHJlc2hvbGQgcmVtb3ZlZCIpKQ0KDQojR2V0IGNvLW9jY3VyYW5jZSBtYXRyaXgNCmNvb2N1ciA8LSBwczNfZmlsdCAlPiUNCiAgICBzdWJzZXRfc2FtcGxlcyhwc3lsbGlkX2dlbnVzICVpbiUgYygiUG93ZWxsaWEiLCAiQ3RlbmFyeXRhaW5hIiwgIlBzeWxsYSIpKSAlPiUNCiAgICBmaWx0ZXJfdGF4YShmdW5jdGlvbih4KSBtZWFuKHgpID4gMCwgVFJVRSkgJT4lDQogICAgb3R1X3RhYmxlICU+JQ0KICAgIGFzLm1hdHJpeCgpICU+JSANCiAgYXBwbHkoMiwgZnVuY3Rpb24oeCkgaWZlbHNlKHggPiAwLCAxLCAwKSkNCg0KY29sbmFtZXMoY29vY3VyKSA8LSBjb2xuYW1lcyhjb29jdXIpICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikNCnJvd25hbWVzKGNvb2N1cikgPC0gcm93bmFtZXMoY29vY3VyKSAlPiUgDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKQ0KDQojIEggY29waGVuZXRpYyBkaXN0YW5jZQ0KaF90cmVlIDwtIHBydW5lZC50cmVlDQpoX3RyZWUkdGlwLmxhYmVsIDwtIGhfdHJlZSR0aXAubGFiZWwgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKQ0KaF90cmVlIDwtIGRyb3AudGlwKGhfdHJlZSwgc2V0ZGlmZihoX3RyZWUkdGlwLmxhYmVsLCByb3duYW1lcyhjb29jdXIpKSkNCg0KIyBQIGNvcGhlbmV0aWMgZGlzdGFuY2UNCnNfdHJlZSA8LSBwaHlfdHJlZShwczMpDQpzX3RyZWUkdGlwLmxhYmVsIDwtIHNfdHJlZSR0aXAubGFiZWwgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKQ0Kc190cmVlIDwtIGRyb3AudGlwKHNfdHJlZSwgc2V0ZGlmZihzX3RyZWUkdGlwLmxhYmVsLCBjb2xuYW1lcyhjb29jdXIpKSkNCg0KY29vY3VyIDwtIGNvb2N1cltoX3RyZWUkdGlwLmxhYmVsLCBzX3RyZWUkdGlwLmxhYmVsXQ0KDQojIFJlYWQgaW4gUGFjbyBydW5zIA0KcGFjb19ydW5fcG93ZWxsaWEgPC0gcmVhZFJEUygib3V0cHV0L2NvcGh5bG9nZW55L3BhY29fcG93ZWxsaWFfbWljcm9iZV9maWx0ZXJlZF9hc3ltLnJkcyIpDQpwYWNvX3J1bl9jdGVuIDwtIHJlYWRSRFMoIm91dHB1dC9jb3BoeWxvZ2VueS9wYWNvX2N0ZW5hcnl0YWluYV9taWNyb2JlX2ZpbHRlcmVkX2FzeW0ucmRzIikNCnBhY29fcnVuX3BzeWxsYSA8LSByZWFkUkRTKCJvdXRwdXQvY29waHlsb2dlbnkvcGFjb19wc3lsbGFfbWljcm9iZV9maWx0ZXJlZF9hc3ltLnJkcyIpDQoNCnBhY29fcnVuX3Bvd2VsbGlhJGdvZg0KcGFjb19ydW5fY3RlbiRnb2YNCnBhY29fcnVuX3BzeWxsYSRnb2YNCg0Kel90cmFucyA8LSBmdW5jdGlvbih4KXsNCiAgKHggLSBtZWFuKHgsIG5hLnJtPVRSVUUpKSAvIHNkKHgsIG5hLnJtPVRSVUUpDQp9DQoNCiMgR2V0IGxpbmsgaW1wb3J0YW5jZQ0KbGlua3MgPC0gZG8uY2FsbCgibGlzdCIsIG1nZXQoZ3JlcCgicGFjb19ydW5fIixuYW1lcyguR2xvYmFsRW52KSx2YWx1ZT1UUlVFKSkpICU+JQ0KICBwdXJycjo6bWFwKGZ1bmN0aW9uKHgpew0KICAgIGdlbnVzX25hbWUgPC0gcm93bmFtZXMoeCRIKVsxXSAlPiUgc3RyX3JlbW92ZSgiXy4qJCIpDQogICAgZGF0YS5mcmFtZSgNCiAgICAgIGpvaW50PW5hbWVzKHgkamFja2tuaWZlKSwNCiAgICAgIHZhbHVlcz11bm5hbWUoeCRqYWNra25pZmUpLA0KICAgICAgI3VwcGVyPXVubmFtZSh4JGphY2trbmlmZSR1cHBlciksDQogICAgICBnZW51cyA9IGdlbnVzX25hbWUNCiAgICApDQogIH0pICU+JQ0KICBiaW5kX3Jvd3MoKSAlPiUNCiBzZXBhcmF0ZShqb2ludCwgaW50bz1jKCJTYW1wbGVfTmFtZSIsICJPVFUiKSwgc2VwPSItIiwgZXh0cmE9Im1lcmdlIiwgcmVtb3ZlPUZBTFNFKSAlPiUNCiAgZ3JvdXBfYnkoZ2VudXMpICU+JQ0KICBtdXRhdGUodmFsdWVzID0gdmFsdWVzXjIpJT4lDQogIG11dGF0ZShtZWFuX3ZhbCA9IG1lYW4odmFsdWVzKSkgJT4lDQogIG11dGF0ZShzaWduaWYgPSBjYXNlX3doZW4oDQogICAgdmFsdWVzIDwgbWVhbl92YWwgfiAxLA0KICAgIHZhbHVlcyA+IG1lYW5fdmFsIH4gMA0KICApKSU+JQ0KICBncm91cF9ieShnZW51cykgJT4lDQogIG11dGF0ZSgNCiAgICAjc3RfZGV2ID0gc2QodmFsdWVzKSwNCiAgICAjIHpfdmFsdWVzID0gIHZhbHVlcy9zdF9kZXYNCiAgICB6X3ZhbHVlcyA9ICB6X3RyYW5zKHZhbHVlcykNCiAgICApICU+JQ0KICB1bmdyb3VwKCkNCg0KIyBQbG90IGxpbmtzDQpsaW5rcyAlPiUNCiAgZHBseXI6OnJlbmFtZShsYWJlbCA9IGpvaW50KSAlPiUNCiAgbXV0YXRlKGxhYmVsID0gYXMuZmFjdG9yKGxhYmVsKSwNCiAgICAgICAgIGxhYmVsPXJlb3JkZXJfd2l0aGluKGxhYmVsLCB2YWx1ZXMsIGdlbnVzKSklPiUNCiAgI2FycmFuZ2UoLXZhbHVlcykgJT4lDQogIGdncGxvdChhZXMoeD1sYWJlbCwgeT12YWx1ZXMsIGNvbG91cj1hcy5mYWN0b3Ioc2lnbmlmKSkpICsgIw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+Z2VudXMsIHNjYWxlcyA9ICJmcmVlIikrDQogICNnZW9tX2Vycm9yYmFyKGFlcyh5bWluPXZhbHVlcywgeW1heD11cHBlcikpICsNCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lYW5fdmFsKSwgbHR5PTMpICsNCiAgc2NhbGVfeF9yZW9yZGVyZWQoKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJzdGVlbGJsdWUiLCAiZGFya29yYW5nZTEiKSwgbmFtZSA9ICJTaWduaWZpY2FudCIpICsNCiAgdGhlbWVfY2xhc3NpYygpKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpDQoNCiNHZXQgb2JzZXJ2ZWQgcmVzaWR1YWxzIG9mIFByb2NydXN0ZWFuIHN1cGVyaW1wb3NpdGlvbiANCnBhY29fcmVzaWR1YWxzIDwtIGRvLmNhbGwoImxpc3QiLCBtZ2V0KGdyZXAoInBhY29fcnVuXyIsbmFtZXMoLkdsb2JhbEVudiksdmFsdWU9VFJVRSkpKSAlPiUNCiAgcHVycnI6Om1hcChmdW5jdGlvbih4KXsNCiAgICByZXMgPC0gcmVzaWR1YWxzX3BhY28oeCRwcm9jLCB0eXBlID0gImludGVyYWN0aW9uIikNCiAgICBnZW51c19uYW1lIDwtIHJvd25hbWVzKHgkSClbMV0gJT4lIHN0cl9yZW1vdmUoIl8uKiQiKQ0KICAgIGRhdGEuZnJhbWUoDQogICAgICBPVFU9bmFtZXMocmVzKSwNCiAgICAgIHZhbHVlcz11bm5hbWUocmVzKSwNCiAgICAgIGdlbnVzPWdlbnVzX25hbWUNCiAgICApIA0KICB9KSU+JQ0KICBiaW5kX3Jvd3MoKSANCg0KIyBQbG90IHJlc2lkdWFscw0KZ2dwbG90KHBhY29fcmVzaWR1YWxzLCBhZXMoeD12YWx1ZXMpKSsNCiAgZ2VvbV9kZW5zaXR5KGZpbGw9J2dyZXk3MCcpKw0KICB0aGVtZV9idygpKw0KICBmYWNldF9ncmlkKGdlbnVzfi4pICsNCiAgeGxhYignUHJvY3J1c3RlcyByZXNpZHVhbHMnKSsNCiAgeWxhYignRnJlcXVlbmN5JykNCg0KIyBQYXJhZml0IHJ1bg0KUEZfcnVuX3Bvd2VsbGlhIDwtIHJlYWRSRFMoIm91dHB1dC9jb3BoeWxvZ2VueS9wYXJhZml0X3Bvd2VsbGlhX21pY3JvYmVfZmlsdGVyZWQucmRzIikNClBGX3J1bl9jdGVuIDwtIHJlYWRSRFMoIm91dHB1dC9jb3BoeWxvZ2VueS9wYXJhZml0X2N0ZW5hcnl0YWluYV9taWNyb2JlX2ZpbHRlcmVkLnJkcyIpDQpQRl9ydW5fcHN5bGxhIDwtIHJlYWRSRFMoIm91dHB1dC9jb3BoeWxvZ2VueS9wYXJhZml0X3BzeWxsYV9taWNyb2JlX2ZpbHRlcmVkLnJkcyIpDQoNClBGX3J1bl9wb3dlbGxpYSRQYXJhRml0R2xvYmFsDQpQRl9ydW5fcG93ZWxsaWEkcC5nbG9iYWwNCg0KUEZfcnVuX2N0ZW4kUGFyYUZpdEdsb2JhbA0KUEZfcnVuX2N0ZW4kcC5nbG9iYWwNCg0KUEZfcnVuX3BzeWxsYSRQYXJhRml0R2xvYmFsDQpQRl9ydW5fcHN5bGxhJHAuZ2xvYmFsDQoNCiMgSm9pbmluZyBpc250IHdvcmtpbmcgaGVyZSBiZWNhdXNlIHRoZXkgd2VyZSBtYWRlIHNlcGFyYXRlbHkgLSBzbyBnZXR0aW5nIHRoZSByb3duYW1lcyBmcm9tIGNvb2NjdXIgbm8gbG9nbmVyIHdvcmtzDQpQRl9saW5rcyA8LSBkby5jYWxsKCJsaXN0IiwgbWdldChncmVwKCJQRl9ydW5fIixuYW1lcyguR2xvYmFsRW52KSx2YWx1ZT1UUlVFKSkpICU+JQ0KICBwdXJycjo6bWFwKGZ1bmN0aW9uKHgpew0KICAgIGdlbnVzX25hbWUgPC0gbmFtZXMoeCRwYXJhLnBlci5ob3N0WzFdKSAlPiUgc3RyX3JlbW92ZSgiXy4qJCIpDQogICAgYXMuZGF0YS5mcmFtZSh4JGxpbmsudGFibGUpICU+JQ0KICAgICAgbGVmdF9qb2luKGVuZnJhbWUobmFtZXMoeCRwYXJhLnBlci5ob3N0KSwgbmFtZSA9ICJIb3N0IiwgdmFsdWU9InBzeWxsaWRfc3BwIikgJT4lDQogICAgICAgICAgICAgICAgICBtdXRhdGUoSG9zdCA9IGFzLm51bWVyaWMoSG9zdCkpKSAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKHgkaG9zdC5wZXIucGFyYSksIG5hbWUgPSAiUGFyYXNpdGUiLCB2YWx1ZT0iT1RVIikgJT4lDQogICAgICAgICAgICAgICAgICBtdXRhdGUoUGFyYXNpdGUgPSBhcy5udW1lcmljKFBhcmFzaXRlKSkpICU+JQ0KICAgICAgbXV0YXRlKGdlbnVzID0gZ2VudXNfbmFtZSkNCiAgfSkgJT4lDQogIGJpbmRfcm93cygpICU+JQ0KICBtdXRhdGUoc2lnbmlmID0gY2FzZV93aGVuKA0KICAgIHAuRjEgPCAwLjA1IH4gMSwNCiAgICBwLkYxID4gMC4wNSB+IDANCiAgKSkgJT4lDQogIGdyb3VwX2J5KGdlbnVzKSAlPiUNCiAgbXV0YXRlKHpfdmFsdWVzID0gIHpfdHJhbnMoRjEuc3RhdCkpICU+JQ0KICB1bmdyb3VwKCkNCg0KIyBQcm9wb3J0aW9uIG9mIHNpZ25pZmljYW50IGxpbmtzDQpQRl9saW5rcyAlPiUNCiAgZ3JvdXBfYnkoZ2VudXMpICU+JQ0KICBzdW1tYXJpc2UocyA9IGNvdW50KHNpZ25pZiA+IDApLCBucyA9IGNvdW50KHNpZ25pZiA9PSAwKSkgDQoNCiMgQ29waHlsb3Bsb3QNCmNvb2N1ci5sdXQgPC0gd2hpY2goY29vY3VyID09MSwgYXJyLmluZD1UUlVFKQ0KYXNzb2MgPC0gY2JpbmQocm93bmFtZXMoY29vY3VyKVtjb29jdXIubHV0WywxXV0sIGNvbG5hbWVzKGNvb2N1cilbY29vY3VyLmx1dFssMl1dKQ0KDQojIFJvdGF0ZSB0aGUgbm9kZXMgdXNpbmcgcGh5dG9vbHMNCmxpYnJhcnkocGh5dG9vbHMpDQpvYmogPC0gY29waHlsbyh0cjE9aF90cmVlLCB0cjI9c190cmVlLCBhc3NvYz1hc3NvYywgcm90YXRlPUZBTFNFKSANCg0KdHJlZTEgPC0gb2JqW1sidHJlZXMiXV1bWzFdXQ0KcDEgPC0gZ2d0cmVlKHRyZWUxLCBsYWRkZXJpemU9RkFMU0UsIGFlcyhjb2xvdXI9bGlua3MpKQ0Kd2VpZ2h0c19wMSA8LSBwMSRkYXRhICU+JQ0KICBsZWZ0X2pvaW4obGlua3MgJT4lDQogICAgZ3JvdXBfYnkoU2FtcGxlX05hbWUpICU+JQ0KICAgIHN1bW1hcmlzZSh2YWx1ZXMgPSBtZWFuKHNpZ25pZikpICU+JQ0KICAgICNzdW1tYXJpc2UodmFsdWVzID0gbWVhbih6X3ZhbHVlcykpICU+JQ0KICAgIGRwbHlyOjpyZW5hbWUobGFiZWwgPSBTYW1wbGVfTmFtZSkNCiAgICApICANCg0KIyMgR2V0IHZhbHVlcyBmb3IgaGlnaGVyIG5vZGVzDQp3ZWlnaHRzX3AxIDwtIHdlaWdodHNfcDEgJT4lDQogIGxlZnRfam9pbihkYXRhLmZyYW1lKG5vZGU9d2VpZ2h0c19wMSRub2RlLCBsaW5rcyA9IHdlaWdodHNfcDEkbm9kZSAlPiUgcHVycnI6Om1hcF9kYmwoYXZlcmFnZV9kZXNjZW5kYW50cywgdHJlZT10cmVlMSwgZGY9d2VpZ2h0c19wMSkpKQ0KDQpwMSA8LSBwMSAlPCslIHdlaWdodHNfcDEgKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgKw0KICAjICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQoaGlnaD0ic3RlZWxibHVlIiwgbG93PSJkYXJrb3JhbmdlMSIsIHRyYW5zPSJsb2cxMCIpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgT1RVIHRyZWUNCnRyZWUyIDwtIG9ialtbInRyZWVzIl1dW1syXV0NCg0KIyBtYWtlIGEgY2xhZGUgbGFiZWwgbGlzdA0KdGF4X2dyb3VwcyA8LSBhcy5kYXRhLmZyYW1lKHRheF90YWJsZShwczMpKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJsYWJlbCIpICU+JQ0KICBkcGx5cjo6bXV0YXRlKGxhYmVsID0gbGFiZWwgJT4lIHN0cl9yZXBsYWNlX2FsbCgiLSIsICJfIikgJT4lDQogICAgICAgICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiAiLCAiXyIpKSAlPiUNCiAgZHBseXI6OnNlbGVjdChsYWJlbCwgZ2VudXMpICU+JQ0KICBmaWx0ZXIobGFiZWwgJWluJSB0cmVlMiR0aXAubGFiZWwpICU+JQ0KICBncm91cF9ieShnZW51cykgDQoNCmdyb3VwX25hbWUgPC0gZ3JvdXBfa2V5cyh0YXhfZ3JvdXBzKSAgJT4lDQogIG11dGF0ZShncm91cF9uYW1lID0gZ2VudXMgJT4lIHN0cl9yZW1vdmVfYWxsKCJcXFt8XFxdIikpDQoNCmNscyA8LSB0YXhfZ3JvdXBzICU+JSANCiAgZ3JvdXBfc3BsaXQoKSAlPiUgDQogIHB1cnJyOjptYXAocHVsbCwgbGFiZWwpICU+JQ0KICBzZXRfbmFtZXMoZ3JvdXBfbmFtZSRncm91cF9uYW1lKQ0KDQpuZXd0cmVlIDwtIGdyb3VwT1RVKHRyZWUyLCBjbHMpDQpwMiA8LSBnZ3RyZWUobmV3dHJlZSwgbGFkZGVyaXplPVRSVUUsIGFlcyhjb2xvdXI9bGlua3MpKSANCndlaWdodHNfcDIgPC0gcDIkZGF0YSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICBncm91cF9ieShPVFUpICU+JQ0KICAgIHN1bW1hcmlzZSh2YWx1ZXMgPSBtZWFuKHNpZ25pZikpICU+JQ0KICAgICNzdW1tYXJpc2UodmFsdWVzID0gbWVhbih6X3ZhbHVlcykpICU+JQ0KICAgIGRwbHlyOjpyZW5hbWUobGFiZWwgPSBPVFUpDQogICAgKSANCg0KI1Nob3VsZCBiZSBhYmxlIHRvIHVzZSBjYXN0b3IgdG8gbWFrZSB0aGlzIGZhc3Rlcg0Kd2VpZ2h0c19wMiA8LSB3ZWlnaHRzX3AyICU+JQ0KICBsZWZ0X2pvaW4oZGF0YS5mcmFtZShub2RlPXdlaWdodHNfcDIkbm9kZSwgbGlua3MgPSB3ZWlnaHRzX3AyJG5vZGUgJT4lIHB1cnJyOjptYXBfZGJsKGF2ZXJhZ2VfZGVzY2VuZGFudHMsIHRyZWU9dHJlZTIsIGRmPXdlaWdodHNfcDIpKSkNCg0KcDIgPC0gcDIgJTwrJSB3ZWlnaHRzX3AyICArICANCiAgI3NjYWxlX2NvbG91cl9ncmFkaWVudChoaWdoPSJzdGVlbGJsdWUiLCBsb3c9ImRhcmtvcmFuZ2UxIiwgdHJhbnM9ImxvZzEwIikgKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgUGxvdCBoZWF0bWFwcw0KaGVhdG1hcF9kYXQgPC0gb2JqJGFzc29jICU+JQ0KICBhc19kYXRhX2ZyYW1lKCkgJT4lDQogIG1hZ3JpdHRyOjpzZXRfY29sbmFtZXMoYygibGFiZWwueCIsICJsYWJlbC55IikpICU+JQ0KICBtdXRhdGUoZ2VudXMgPSBsYWJlbC54ICU+JSBzdHJfcmVtb3ZlKCJfLiokIikpICU+JQ0KICBsZWZ0X2pvaW4obGlua3MgJT4lIA0KICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KGxhYmVsLnggPSBTYW1wbGVfTmFtZSwgbGFiZWwueSA9IE9UVSwgc2lnbmlmX3BhY289c2lnbmlmLCB2YWxfcGFjbyA9IHpfdmFsdWVzKSkgJT4lDQogIGxlZnRfam9pbihQRl9saW5rcyAlPiUgDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QobGFiZWwueCA9IHBzeWxsaWRfc3BwLCBsYWJlbC55ID0gT1RVLCBzaWduaWZfcGFyYT1zaWduaWYsIHZhbF9wYXJhID0gel92YWx1ZXMpKSAlPiUNCiAgbGVmdF9qb2luKHAxJGRhdGEgJT4lIGRwbHlyOjpzZWxlY3QobGFiZWwsIHkpICU+JSBkcGx5cjo6cmVuYW1lKGxhYmVsLnggPSBsYWJlbCksIGJ5PSJsYWJlbC54IikgJT4lDQogIGxlZnRfam9pbihwMiRkYXRhICU+JSBkcGx5cjo6c2VsZWN0KGxhYmVsLCB5KSAlPiUgZHBseXI6OnJlbmFtZShsYWJlbC55ID0gbGFiZWwpLCBieT0ibGFiZWwueSIpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oImFzc29jIikgJT4lDQogIGRwbHlyOjpyZW5hbWUoU2FtcGxlX05hbWUgPSBsYWJlbC54LA0KICAgICAgICAgICAgICAgIE9UVSA9IGxhYmVsLnksDQogICAgICAgICAgICAgICAgcG9zX3ggPSB5LngsDQogICAgICAgICAgICAgICAgcG9zX3kgPSB5LnkpICU+JQ0KICBtdXRhdGUoc2lnbmlmX3BhY28gPSByZXBsYWNlX25hKHNpZ25pZl9wYWNvLCAwKSwNCiAgICAgICAgIHNpZ25pZl9wYXJhID0gcmVwbGFjZV9uYShzaWduaWZfcGFyYSwgMCkpICU+JQ0KICBtdXRhdGUoaGlnaGxpZ2h0ID0gY2FzZV93aGVuKA0KICAgIHNpZ25pZl9wYWNvPT0wICYgc2lnbmlmX3BhcmE9PTAgIH4gIk5TIiwNCiAgICBzaWduaWZfcGFjbz09MCAmIHNpZ25pZl9wYXJhPT0xIH4gInBhcmEiLA0KICAgIHNpZ25pZl9wYWNvPT0xICYgc2lnbmlmX3BhcmE9PTAgfiAicGFjbyIsDQogICAgc2lnbmlmX3BhY289PTEgJiBzaWduaWZfcGFyYT09MSB+ICJib3RoIg0KICApKSANCg0KZ2cuaGVhdG1hcCA8LSBoZWF0bWFwX2RhdCAlPiUNCiAgICBtdXRhdGUoT1RVID0gT1RVICU+JSBzdHJfcmVwbGFjZV9hbGwoIl8iLCAiICIpLA0KICAgICAgICAgU2FtcGxlX05hbWUgPSAgU2FtcGxlX05hbWUgJT4lIHN0cl9yZXBsYWNlX2FsbCgiXyIsIiAiKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoU2FtcGxlX05hbWUsIE9UVSwgZ2VudXMsIGhpZ2hsaWdodCwgcG9zX3gsIHBvc195LCB2YWxfcGFyYSwgdmFsX3BhY28pICU+JQ0KICBkcGx5cjo6bXV0YXRlKFNhbXBsZV9OYW1lID0gZmFjdG9yKFNhbXBsZV9OYW1lKSwNCiAgICAgICAgICAgICAgICBPVFUgPSBmYWN0b3IoT1RVKSwNCiAgICAgICAgICAgICAgICBnZW51cz1mYWN0b3IoZ2VudXMsIGxldmVscz1jKCJQc3lsbGEiICwiUG93ZWxsaWEiLCAiQ3RlbmFyeXRhaW5hIikpKSAgJT4lDQogICBnZ3Bsb3QoYWVzKHg9IGZjdF9yZW9yZGVyKFNhbXBsZV9OYW1lLCBwb3NfeCksIHk9ZmN0X3Jlb3JkZXIoT1RVLCBwb3NfeSksIGZpbGw9aGlnaGxpZ2h0ICkpICsNCiAgZ2VvbV90aWxlKCkgKw0KICB0aGVtZV9idygpICsNCiAgZmFjZXRfZ3JpZCh+Z2VudXMsIGRyb3AgPSBUUlVFLCBzY2FsZXM9ImZyZWUiLCBzcGFjZSA9ImZyZWUiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kPWMoMCwwKSkrDQogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kPWMoMCwwKSkgKw0KICAjc2NhbGVfZmlsbF9ncmFkaWVudChoaWdoPSJzdGVlbGJsdWUiLCBsb3c9ImRhcmtvcmFuZ2UxIikgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJOUyI9InN0ZWVsYmx1ZSIsInBhY28iPSJkYXJrb3JhbmdlMSIsInBhcmEiPSIjZGEyYjkxIiwgImJvdGgiPSIjOTFkYTJiIiksIG5hLnRyYW5zbGF0ZT1GQUxTRSkgDQoNCmdnLmhlYXRtYXANCg0KIyBEZW5zaXR5IHBsb3Qgb2YgbWlzbWF0Y2gNCmRlbnNpdHlfZGF0IDwtICBoZWF0bWFwX2RhdCAlPiUNCiAgbGVmdF9qb2luKHAyJGRhdGEgJT4lIGRwbHlyOjpzZWxlY3QoT1RVID0gbGFiZWwsIHkpKSAlPiUNCiAgZ3JvdXBfYnkoT1RVLCB5KSAlPiUNCiAgc3VtbWFyaXNlKHN1bV9wYWNvID0gc3VtKHNpZ25pZl9wYWNvLCBuYS5ybT1UUlVFKSwgc3VtX3BhcmEgPSBzdW0oc2lnbmlmX3BhcmEsIG5hLnJtPVRSVUUpKSAlPiUNCiAgbXV0YXRlKHRvdGFsX3N1bSA9IHN1bShzdW1fcGFjbywgc3VtX3BhcmEsIG5hLnJtPVRSVUUpKSAlPiUNCiAgI3N1bW1hcmlzZSh0b3RhbF9zdW0gPSBtZWFuKHZhbF9wYWNvLCBuYS5ybT1UUlVFKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpkZW5zaXR5X2xhYmVscyA8LSBkZW5zaXR5X2RhdCAlPiUNCiAgbGVmdF9qb2luKHRheF90YWJsZShwczMpICU+JQ0KICAgICAgICAgICAgICBhcygibWF0cml4IikgJT4lDQogICAgICAgICAgICAgIGFzX3RpYmJsZShyb3duYW1lcz0iT1RVIikgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUoT1RVID0gT1RVICU+JSBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikpKSU+JQ0KICBhcnJhbmdlKHkpIA0KICANCmNodW5rID0gNTANCm4gPC0gbnJvdyhkZW5zaXR5X2xhYmVscykNCnIgIDwtIHJlcCgxOmNlaWxpbmcobi9jaHVuayksZWFjaD1jaHVuaylbMTpuXQ0KZGVuc2l0eV9sYWJlbHNfY2h1bmtlZCA8LSBzcGxpdChkZW5zaXR5X2xhYmVscyxyKQ0KDQpkZW5zaXR5X2xhYmVscyA8LSBkZW5zaXR5X2xhYmVsc19jaHVua2VkICU+JSANCiAgcHVycnI6Om1hcChmdW5jdGlvbih4KXsNCiAgICBkZiA8LSB4ICU+JQ0KICAgICAgbXV0YXRlKHpzY29yZSA9ICh0b3RhbF9zdW0gLSBtZWFuKHRvdGFsX3N1bSwgbmEucm09VFJVRSkpL3NkKHRvdGFsX3N1bSwgbmEucm09VFJVRSkpICU+JQ0KICAgICAgZmlsdGVyKCB0b3RhbF9zdW0gPiAzLCB6c2NvcmUgID4gMywpICMsIA0KICAgIGlmKG5yb3coZGYpID4gMCl7DQogICAgICAgIG91dCA8LSBkZiAlPiUNCiAgICAgICAgc3VtbWFyaXNlKHkgPSBtZWFuKHkpLCB0b3RhbF9zdW0gID0gbWF4KHRvdGFsX3N1bSksIGFubm90ID0gIGNhc2Vfd2hlbigNCiAgICAgICAgbGVuZ3RoKHVuaXF1ZShnZW51cykpID09IDEgfiBuYW1lcyh3aGljaC5tYXgodGFibGUoZ2VudXMpKSksDQogICAgICAgIGxlbmd0aCh1bmlxdWUoZ2VudXMpKSA+IDF+ICBuYW1lcyh3aGljaC5tYXgodGFibGUoZmFtaWx5KSkpKSkNCiAgICAgICAgcmV0dXJuKG91dCkNCiAgICB9DQogIH0pICU+JQ0KICBiaW5kX3Jvd3MoKSANCg0KZ2cuZGVuc2l0eSA8LSBkZW5zaXR5X2RhdCAlPiUNCiAgZmlsdGVyKHRvdGFsX3N1bSA+IDApICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5LCB5PXRvdGFsX3N1bSwgY29sb3VyPXRvdGFsX3N1bSkpICsNCiAgZ2VvbV9wb2ludChzaXplPTAuMDEsIGFscGhhPTEpKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgKw0KICAgIGdlb21fdGV4dChkYXRhPWRlbnNpdHlfbGFiZWxzLCBhZXMobGFiZWwgPSBhbm5vdCksIGhqdXN0PTApICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZD1jKDAsMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAsMCkpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsNCiAgY29vcmRfZmxpcCgpDQoNCmdnLmRlbnNpdHkNCg0KIyBJbnN0ZWFkIG9mIGRlbnNpdHkgY291bGQgaSBsYWJlbCBsaW5lYWdlcyB3aXRoIHRoZSBncmVhdGVzdCB2YWx1ZT8NCg0KdG9wIDwtIHdyYXBfZWxlbWVudHMoZ3JpZDo6dGV4dEdyb2IoJycpKSArKHAxKyBjb29yZF9mbGlwKCkgKyBzY2FsZV94X3JldmVyc2UoZXhwYW5kPWMoMCwwKSkrIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9YygwLDApKSkgKyB3cmFwX2VsZW1lbnRzKGdyaWQ6OnRleHRHcm9iKCcnKSkgK3Bsb3RfbGF5b3V0KHdpZHRocz1jKDAuNSwzLCAwLjEpKSANCg0KYm90dG9tIDwtIHAyKyBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kPWMoMCwwKSkgKyBnZy5oZWF0bWFwICsgZ2cuZGVuc2l0eSArIHBsb3RfbGF5b3V0KHdpZHRocz1jKDAuNSwzLCAwLjEpKQ0KDQpnZy50cmVlbWFwIDwtIHRvcCAvIGJvdHRvbSArIHBsb3RfbGF5b3V0KGhlaWdodHM9YygwLjUsMykpICANCg0KZ2cudHJlZW1hcA0KDQpwZGYoZmlsZT0iZmlncy8zZ2VudXNfaGVhdG1hcHMucGRmIiwgIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTEsIHBhcGVyPSJhNCIpDQogIHBsb3QoZ2cudHJlZW1hcCkNCnRyeShkZXYub2ZmKCksIHNpbGVudD1UUlVFKQ0KICANCiMgTWFrZSBwbG90IHJhbmtpbmcgbGlua3MsIGFuZCBub2Rlcw0KDQpnZy5ob3N0X2xpbmtzIDwtIGhlYXRtYXBfZGF0ICU+JQ0KICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gU2FtcGxlX05hbWUpICU+JQ0KICBkcm9wX25hKCkgJT4lDQogIGdyb3VwX2J5KGxhYmVsKSAlPiUNCiAgc3VtbWFyaXNlKHNpZ25pZl9wYWNvID0gc3VtKHNpZ25pZl9wYWNvKSwgc2lnbmlmX3BhcmE9IHN1bShzaWduaWZfcGFyYSkpICU+JQ0KICBwaXZvdF9sb25nZXIoc3RhcnRzX3dpdGgoInNpZ25pZl8iKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvPSJ0eXBlIiwNCiAgICAgICAgICAgICAgIHZhbHVlc190bz0idmFsdWVzIikgJT4lDQogIG11dGF0ZSh0eXBlID0gdHlwZSAlPiUgc3RyX3JlbW92ZSgic2lnbmlmXyIpKSAlPiUNCiAgICBtdXRhdGUobGFiZWwgPSBsYWJlbCAlPiUgDQogICAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKCJfIiwgIiAiKSAlPiUNCiAgICAgICAgICAgICBzdHJfcmVwbGFjZSgiIHNwIiwgIiBzcC4iKSklPiUNCiAgbXV0YXRlKGxhYmVsID0gYXMuZmFjdG9yKGxhYmVsKSklPiUNCiAgZmlsdGVyKHZhbHVlcyA+IDApICU+JQ0KICBhcnJhbmdlKC12YWx1ZXMpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZmN0X3Jlb3JkZXIobGFiZWwsIHZhbHVlcywgc3VtICksIHk9dmFsdWVzLCBmaWxsPXR5cGUpKSArDQogIGdlb21fY29sKCkgKyAgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJOUyI9InN0ZWVsYmx1ZSIsInBhY28iPSJkYXJrb3JhbmdlMSIsInBhcmEiPSIjZGEyYjkxIiwgImJvdGgiPSIjOTFkYTJiIiksIG5hLnRyYW5zbGF0ZT1GQUxTRSkrDQogIHRoZW1lX2NsYXNzaWMoKSsgDQogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhY2U9Iml0YWxpYyIpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikrDQogIGxhYnMoeSA9ICJOdW1iZXIgb2YgU2lnbmZpY2FudCBsaW5rcyIsIHg9TlVMTCwgdGl0bGUgPSJQc3lsbGlkIHRheGEiKSArIA0KICBjb29yZF9mbGlwKCkNCg0KZ2cuc3ltX2xpbmtzIDwtIGhlYXRtYXBfZGF0ICU+JQ0KICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gT1RVKSAlPiUNCiAgbGVmdF9qb2luKHRheF9ncm91cHMgJT4lIGRwbHlyOjpyZW5hbWUob3R1X2dlbnVzID0gZ2VudXMpKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBncm91cF9ieShvdHVfZ2VudXMpICU+JQ0KICBzdW1tYXJpc2Uoc2lnbmlmX3BhY28gPSBzdW0oc2lnbmlmX3BhY28pLCBzaWduaWZfcGFyYT0gc3VtKHNpZ25pZl9wYXJhKSkgJT4lDQogIHBpdm90X2xvbmdlcihzdGFydHNfd2l0aCgic2lnbmlmXyIpLA0KICAgICAgICAgICAgICAgbmFtZXNfdG89InR5cGUiLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvPSJ2YWx1ZXMiKSAlPiUNCiAgZHBseXI6OnJlbmFtZShsYWJlbCA9IG90dV9nZW51cykgJT4lDQogIGRwbHlyOjptdXRhdGUobGFiZWwgPSBsYWJlbCAlPiUgDQogICAgICAgICAgICAgICAgICBzdHJfcmVtb3ZlKCJcXC8uKiQiKSAlPiUNCiAgICAgICAgICAgICAgICAgIHN0cl9yZXBsYWNlKCJOQV9jYW5hcmllbnNlIiwgIkJyYWR5cmhpem9iaXVtX2NhbmFyaWVuc2UiKSAlPiUNCiAgICAgICAgICAgICAgICAgIHN0cl9yZW1vdmUoIl5zX18iKQ0KICAgICAgICAgICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKHR5cGUgPSB0eXBlICU+JSBzdHJfcmVtb3ZlKCJzaWduaWZfIikpICU+JQ0KICBtdXRhdGUobGFiZWwgPSBhcy5mYWN0b3IobGFiZWwpLA0KICAgICAgICAgbGFiZWw9ZmN0X3Jlb3JkZXIobGFiZWwsIHZhbHVlcywgc3VtICkpJT4lDQogIGdyb3VwX2J5KGxhYmVsKSAlPiUNCiAgbXV0YXRlKHRvdGFsID0gc3VtKHZhbHVlcykpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogICBmaWx0ZXIodG90YWwgPiAwKSAlPiUNCiAgdG9wX24oODAsIHRvdGFsKSAlPiUNCiAgYXJyYW5nZSgtdmFsdWVzKSAlPiUNCiAgZ2dwbG90KGFlcyh4PWxhYmVsLCB5PXZhbHVlcywgZmlsbD10eXBlKSkgKw0KICBnZW9tX2NvbCgpICsgIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiTlMiPSJzdGVlbGJsdWUiLCJwYWNvIj0iZGFya29yYW5nZTEiLCJwYXJhIj0iI2RhMmI5MSIsICJib3RoIj0iIzkxZGEyYiIpLCBuYS50cmFuc2xhdGU9RkFMU0UpKw0KICB0aGVtZV9jbGFzc2ljKCkrIA0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlPSJpdGFsaWMiKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKw0KICBsYWJzKHkgPSAiTnVtYmVyIG9mIFNpZ25maWNhbnQgbGlua3MiLCB4PU5VTEwsIHRpdGxlID0iTWljcm9iaWFsIGdlbmVyYSIpICsgDQogIGNvb3JkX2ZsaXAoKQ0KDQpnZy5ob3N0X2xpbmtzICsgZ2cuc3ltX2xpbmtzDQoNCnBkZihmaWxlPSJmaWdzLzNnZW51c19maXRfc3VtbWFyeS5wZGYiLCAgd2lkdGggPSA4LCBoZWlnaHQgPSAxMSwgcGFwZXI9ImE0IikNCiAgcGxvdChnZy5ob3N0X2xpbmtzICsgZ2cuc3ltX2xpbmtzKQ0KdHJ5KGRldi5vZmYoKSwgc2lsZW50PVRSVUUpDQoNCmBgYA0KDQojIFRhbmdsZWdyYW1zDQoNCiMjIFBzeWxsaWQgfiBDYXJzb25lbGxhIA0KDQpgYGB7ciBjYXJzb25lbGxhIGNvcGh5bG99DQojRmxhZyB0b3AgYWJ1bmRhbmNlIGNhcnNvbmVsbGEgYnkgc2FtcGxlDQp0b3BfY2Fyc29uIDwtIHBzMiAlPiUNCiAgdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoZnVuY3Rpb24gKHgpIHgvc3VtKHgpKSAlPiUNCiAgc3BlZWR5c2VxOjpwc21lbHQoKSAlPiUNCiAgZmlsdGVyKHBzeWxsaWRfc3BwICVpbiUgcHJ1bmVkLnRyZWUkdGlwLmxhYmVsKSAlPiUNCiAgZmlsdGVyKGdlbnVzPT0iQ2FuZGlkYXR1cyBDYXJzb25lbGxhIikgJT4lDQogIGdyb3VwX2J5KFNhbXBsZSkgJT4lICANCiAgZmlsdGVyKEFidW5kYW5jZSA+IDApICU+JQ0KICB0b3BfbigxLCB3dD1BYnVuZGFuY2UpICU+JQ0KICBtdXRhdGUodG9wID0gVFJVRSkgDQoNCmNvb2N1ciA8LSBwczIgJT4lDQogIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKGZ1bmN0aW9uICh4KSB4L3N1bSh4KSkgJT4lDQogIHNwZWVkeXNlcTo6cHNtZWx0KCkgJT4lDQogICAgZmlsdGVyKHBzeWxsaWRfc3BwICVpbiUgcHJ1bmVkLnRyZWUkdGlwLmxhYmVsKSAlPiUNCiAgbGVmdF9qb2luKHRvcF9jYXJzb24pICU+JQ0KICBmaWx0ZXIoZ2VudXM9PSJDYW5kaWRhdHVzIENhcnNvbmVsbGEiLCB0b3A9PVRSVUUpICU+JQ0KICBkcGx5cjo6c2VsZWN0KE9UVSwgcHN5bGxpZF9zcHAsIFNhbXBsZUlELCBBYnVuZGFuY2UpICU+JQ0KICBtdXRhdGUoT1RVID0gT1RVICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikpICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoT1RVLCBwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKEFidW5kYW5jZSA9IHN1bShBYnVuZGFuY2UpKSAlPiUNCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IHBzeWxsaWRfc3BwLA0KICAgICAgICAgICAgICBuYW1lc19mcm9tID0gT1RVLA0KICAgICAgICAgICAgICB2YWx1ZXNfZnJvbT1BYnVuZGFuY2UsDQogICAgICAgICAgICAgIHZhbHVlc19maWxsID0gbGlzdChBYnVuZGFuY2UgPSAwKSkgICU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoInBzeWxsaWRfc3BwIikgJT4lDQogICAgYXMubWF0cml4KCkgJT4lIA0KICBhcHBseSgyLCBmdW5jdGlvbih4KSBpZmVsc2UoeCA+IDAsIDEsIDApKSANCg0KIyBIIGNvcGhlbmV0aWMgZGlzdGFuY2UNCmhfdHJlZSA8LSBwcnVuZWQudHJlZQ0KaF90cmVlJHRpcC5sYWJlbCA8LSBoX3RyZWUkdGlwLmxhYmVsICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikNCmhfdHJlZSA8LSBkcm9wLnRpcChoX3RyZWUsIHNldGRpZmYoaF90cmVlJHRpcC5sYWJlbCwgcm93bmFtZXMoY29vY3VyKSkpDQpoX2Rpc3QgPC0gc3FydChjb3BoZW5ldGljKGhfdHJlZSkpDQoNCiMgUCBjb3BoZW5ldGljIGRpc3RhbmNlDQpzX3RyZWUgPC0gcGh5X3RyZWUocHMzKQ0Kc190cmVlJHRpcC5sYWJlbCA8LSBzX3RyZWUkdGlwLmxhYmVsICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikNCnNfdHJlZSA8LSBkcm9wLnRpcChzX3RyZWUsIHNldGRpZmYoc190cmVlJHRpcC5sYWJlbCwgY29sbmFtZXMoY29vY3VyKSkpDQpzX2Rpc3QgPC0gc3FydChjb3BoZW5ldGljKHNfdHJlZSkvMWUrNiApICMjY29udmVydCB0byBNeWEgc28gaW50ZWdlcnMgYXJlIHNtYWxsIGVub3VnaCBmb3IgUEFDTw0KDQpjb29jdXIgPC0gY29vY3VyW2hfdHJlZSR0aXAubGFiZWwsIHNfdHJlZSR0aXAubGFiZWxdDQoNCiMgcHJlcGFyZSBwYWNvIGRhdGENCkQgPC0gcHJlcGFyZV9wYWNvX2RhdGEoSD1oX2Rpc3QsIFA9c19kaXN0LCBIUD1jb29jdXIpDQoNCiMgQWRkIHBjb3JkDQpEIDwtIGFkZF9wY29vcmQoRCwgY29ycmVjdGlvbj0nbm9uZScpIA0KcF9ob3N0IDwtIGdncGxvdChEJEhfUENvWyxjKCdBeGlzLjEnLCAnQXhpcy4yJyldICU+JSBhcy5kYXRhLmZyYW1lLCBhZXMoQXhpcy4xLCBBeGlzLjIpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICB0aGVtZV9idygpICsNCiAgZ2d0aXRsZSgiSCBQQ0EiKQ0KcF9wYXJhIDwtIGdncGxvdChEJFBfUENvWyxjKCdBeGlzLjEnLCAnQXhpcy4yJyldICU+JSBhcy5kYXRhLmZyYW1lLCBhZXMoQXhpcy4xLCBBeGlzLjIpKSAgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgdGhlbWVfYncoKSsNCiAgZ2d0aXRsZSgiUCBQQ0EiKQ0KICAgIA0KcGxvdChwX2hvc3QgKyBwX3BhcmEpDQoNCiMgcnVuIHBhY28NCnBhY29fcnVuIDwtIFBBQ28oRCwgbnBlcm09OTk5LCBzZWVkPTkwOSwgbWV0aG9kPSdxdWFzaXN3YXAnLCBzeW1tZXRyaWM9RkFMU0UpDQoNCiNwcmludCBvdmVyYWxsIHNpZ25pZmljYW5jZQ0KcHJpbnQocGFjb19ydW4kZ29mKQ0KDQojIEdldCBpbnRlcmFjdGlvbi1zcGVjaWZpYyBjb3BoeWxvZ2VuZXRpYyBjb250cmlidXRpb25zIHVzaW5nIGphY2tuaWZpbmcNCnBhY29fcnVuIDwtIHBhY29fbGlua3MocGFjb19ydW4pDQpsaW5rcyA8LSBkYXRhLmZyYW1lKGpvaW50PW5hbWVzKHBhY29fcnVuJGphY2trbmlmZSksICNsb3Npbmcgc3Y1MCBoZXJlPw0KICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9dW5uYW1lKHBhY29fcnVuJGphY2trbmlmZSkjLA0KICAgICAgICAgICAgICAgICAgICAjdXBwZXI9dW5uYW1lKHBhY29fcnVuJGphY2trbmlmZSkNCiAgICAgICAgICAgICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKE9UVSA9IGpvaW50KSAlPiUNCiBzZXBhcmF0ZShPVFUsIGludG89YygiU2FtcGxlX05hbWUiLCAiT1RVIiksIHNlcD0iLSIsIGV4dHJhPSJtZXJnZSIpICU+JQ0KICBtdXRhdGUoc2lnbmlmID0gY2FzZV93aGVuKA0KICAgIHZhbHVlcyA8IG1lYW4oLiR2YWx1ZXMpIH4gMSwNCiAgICB2YWx1ZXMgPiBtZWFuKC4kdmFsdWVzKSB+IDANCiAgKSkNCg0KIyBQbG90IGxpbmtzDQpsaW5rcyAlPiUNCiAgZHBseXI6OnJlbmFtZShsYWJlbCA9IGpvaW50KSAlPiUNCiAgbXV0YXRlKGxhYmVsID0gYXMuZmFjdG9yKGxhYmVsKSwNCiAgICAgICAgIGxhYmVsPWZjdF9yZW9yZGVyKGxhYmVsLCB2YWx1ZXMsIHN1bSkpJT4lDQogIGFycmFuZ2UoLXZhbHVlcykgJT4lDQogIGdncGxvdChhZXMoeD1sYWJlbCwgeT12YWx1ZXMsIGNvbG91cj1hcy5mYWN0b3Ioc2lnbmlmKSkpICsNCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICNnZW9tX2Vycm9yYmFyKGFlcyh5bWluPXZhbHVlcywgeW1heD11cHBlcikpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihsaW5rcyR2YWx1ZXMpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygic3RlZWxibHVlIiwgImRhcmtvcmFuZ2UxIikpICsNCiAgdGhlbWVfY2xhc3NpYygpKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpDQoNCmRpci5jcmVhdGUoIm91dHB1dC9jb3BoeWxvZ2VueS9wc3lsbGlkX2NhcnNvbmVsbGEiKQ0Kd3JpdGVfY3N2KGxpbmtzLCAib3V0cHV0L2NvcGh5bG9nZW55L3BzeWxsaWRfY2Fyc29uZWxsYS9wc3lsbGlkX2NhcnNvbmVsbGFfbGlua3MuY3N2IikNCndyaXRlX2NzdihsaW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoT1RVKSAlPiUNCiAgICBzdW1tYXJpc2UodmFsdWVzID0gbWVhbih2YWx1ZXMpKSwgIm91dHB1dC9jb3BoeWxvZ2VueS9wc3lsbGlkX2NhcnNvbmVsbGEvY2Fyc29uZWxsYV93ZWlnaHRzLmNzdiIpDQp3cml0ZV9jc3YobGlua3MgJT4lIA0KICAgIGdyb3VwX2J5KFNhbXBsZV9OYW1lKSAlPiUNCiAgICBzdW1tYXJpc2UodmFsdWVzID0gbWVhbih2YWx1ZXMpKSwgIm91dHB1dC9jb3BoeWxvZ2VueS9wc3lsbGlkX2NhcnNvbmVsbGEvcHN5bGxpZF93ZWlnaHRzLmNzdiIpDQoNCiNHZXQgb2JzZXJ2ZWQgcmVzaWR1YWxzIG9mIFByb2NydXN0ZWFuIHN1cGVyaW1wb3NpdGlvbiANCnBhY29fcmVzaWR1YWxzIDwtIHJlc2lkdWFsc19wYWNvKHBhY29fcnVuJHByb2MsIHR5cGUgPSAiaW50ZXJhY3Rpb24iKQ0KDQojIFZpc3VhbGlzZSByZXNpZHVhbHMNCnJlcyA8LSBkYXRhLmZyYW1lKE9UVT1uYW1lcyhwYWNvX3Jlc2lkdWFscyksIHZhbHVlcz11bm5hbWUocGFjb19yZXNpZHVhbHMpKSAlPiUNCiAgc2VwYXJhdGUoT1RVLCBpbnRvPWMoIlNhbXBsZV9OYW1lIiwgIk9UVSIpLCBzZXA9Ii0iLCBleHRyYT0ibWVyZ2UiKSANCmdncGxvdChyZXMsIGFlcyh4PXZhbHVlcykpKw0KICBnZW9tX2RlbnNpdHkoZmlsbD0nZ3JleTcwJykrDQogIHRoZW1lX2J3KCkrDQogIHhsYWIoJ1Byb2NydXN0ZXMgcmVzaWR1YWxzJykrDQogIHlsYWIoJ0ZyZXF1ZW5jeScpDQoNCiMgUGFyYWZpdCBydW4NClBGX3J1biA8LSBwYXJhZml0KGhfZGlzdCwgc19kaXN0LGNvb2N1ciwgbnBlcm09OTk5LCB0ZXN0LmxpbmtzPVRSVUUpDQpQRl9ydW4kUGFyYUZpdEdsb2JhbA0KUEZfcnVuJHAuZ2xvYmFsDQoNClBGX2xpbmtzIDwtIGFzLmRhdGEuZnJhbWUoUEZfcnVuJGxpbmsudGFibGUpICAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKFBGX3J1biRwYXJhLnBlci5ob3N0KSwgbmFtZSA9ICJIb3N0IiwgdmFsdWU9InBzeWxsaWRfc3BwIikgJT4lDQogICAgICAgICAgICAgICAgICBtdXRhdGUoSG9zdCA9IGFzLm51bWVyaWMoSG9zdCkpKSAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKFBGX3J1biRob3N0LnBlci5wYXJhKSwgbmFtZSA9ICJQYXJhc2l0ZSIsIHZhbHVlPSJPVFUiKSAlPiUNCiAgICAgICAgICAgICAgICAgIG11dGF0ZShQYXJhc2l0ZSA9IGFzLm51bWVyaWMoUGFyYXNpdGUpKSklPiUNCiAgbXV0YXRlKHNpZ25pZiA9IGNhc2Vfd2hlbigNCiAgICBwLkYxIDwgMC4wNSB+IDEsDQogICAgcC5GMSA+IDAuMDUgfiAwDQogICkpIA0KDQojIENvcGh5bG9wbG90DQpjb29jdXIubHV0IDwtIHdoaWNoKGNvb2N1ciA9PTEsIGFyci5pbmQ9VFJVRSkNCmFzc29jIDwtIGNiaW5kKHJvd25hbWVzKGNvb2N1cilbY29vY3VyLmx1dFssMV1dLCBjb2xuYW1lcyhjb29jdXIpW2Nvb2N1ci5sdXRbLDJdXSkNCg0KIyBSb3RhdGUgdGhlIG5vZGVzIHVzaW5nIHBoeXRvb2xzDQpvYmogPC0gY29waHlsbyh0cjE9aF90cmVlLCB0cjI9c190cmVlLCBhc3NvYz1hc3NvYywgcm90YXRlPVRSVUUpIA0KDQojIHBzeWxsaWRfdHJlZQ0KdHJlZTEgPC0gb2JqW1sidHJlZXMiXV1bWzFdXQ0KDQpwMSA8LSBnZ3RyZWUodHJlZTEpDQphdG1ldG9fbm9kZSA8LSBwMSRkYXRhICU+JSANCiAgZmlsdGVyKHN0cl9kZXRlY3QobGFiZWwsICJBdG1ldG8iKSkgJT4lDQogIHB1bGwobm9kZSkNCnJvb3Rfbm9kZSA8LSBwMSRkYXRhICU+JSANCiAgZmlsdGVyKHN0cl9kZXRlY3QobGFiZWwsICJBdG1ldG8iKSkgJT4lDQogIHB1bGwocGFyZW50KQ0KDQp0cmVlMSA8LSBncm91cE9UVSh0cmVlMSwgLm5vZGU9YXRtZXRvX25vZGUpICMgTWFrZSB0aGUgYXRtZXRvIGRvdHRlZCB0byBpbmRpY2F0ZSBvdXRncm91cCB3YXMgcmVzY2FsZWQNCg0KcDEgPC0gZ2d0cmVlKHRyZWUxLCBsYWRkZXJpemU9RkFMU0UsIGFlcyhjb2xvdXI9bGlua3MsbGluZXR5cGU9Z3JvdXApKQ0Kd2VpZ2h0c19wMSA8LSBwMSRkYXRhICU+JQ0KICBsZWZ0X2pvaW4obGlua3MgJT4lIA0KICAgIGdyb3VwX2J5KFNhbXBsZV9OYW1lKSAlPiUNCiAgICBzdW1tYXJpc2UoUEFfdmFsdWVzID0gbWVhbihzaWduaWYpKSAlPiUNCiAgICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gU2FtcGxlX05hbWUpDQogICAgKSU+JQ0KICBsZWZ0X2pvaW4oUEZfbGlua3MgJT4lIA0KICAgIGdyb3VwX2J5KHBzeWxsaWRfc3BwKSAlPiUNCiAgICBzdW1tYXJpc2UoUEZfdmFsdWVzID0gbWVhbihzaWduaWYpKSAlPiUNCiAgICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gcHN5bGxpZF9zcHApDQogICAgKSAlPiUNCiAgbXV0YXRlKHZhbHVlcyA9IFBBX3ZhbHVlcyArIFBGX3ZhbHVlcykNCg0KI1NjYWxlIHRoZSBhdG1ldG9jcmFuaXVtIGFuZCByb290IG5vZGVzIHRvIGJlIHNob3J0ZXINCnAxJGRhdGFbcDEkZGF0YSRub2RlICVpbiUgYXRtZXRvX25vZGUsICJ4Il0gPC0gbWF4KHAxJGRhdGEkeCkNCnAxJGRhdGFbcDEkZGF0YSRub2RlICVpbiUgcm9vdF9ub2RlLCAieCJdIDwtIDAuMiAjcm9vdA0KDQpwMSRkYXRhJG5vZGVbcDEkZGF0YSRub2RlXQ0KDQojIyBHZXQgdmFsdWVzIGZvciBoaWdoZXIgbm9kZXMNCndlaWdodHNfcDEgPC0gd2VpZ2h0c19wMSAlPiUNCiAgbGVmdF9qb2luKGRhdGEuZnJhbWUobm9kZT13ZWlnaHRzX3AxJG5vZGUsIGxpbmtzID0gd2VpZ2h0c19wMSRub2RlICU+JSBwdXJycjo6bWFwX2RibChhdmVyYWdlX2Rlc2NlbmRhbnRzLCB0cmVlPXRyZWUxLCBkZj13ZWlnaHRzX3AxKSkpDQpwMSA8LSBwMSAlPCslIHdlaWdodHNfcDEgKyBnZW9tX3RpcHBvaW50KGFlcyhjb2xvdXI9bGlua3MpKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0ic3RlZWxibHVlIiwgaGlnaD0iZGFya29yYW5nZTEiKSAgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgT1RVIHRyZWUNCnRyZWUyIDwtIG9ialtbInRyZWVzIl1dW1syXV0NCnNfdHJlZSA8LSBkcm9wLnRpcCh0cmVlMiwgc2V0ZGlmZih0cmVlMiR0aXAubGFiZWwsIG9iaiRhc3NvY1ssMl0pKQ0KcDIgPC0gZ2d0cmVlKHRyZWUyICwgbGFkZGVyaXplPUZBTFNFLCBhZXMoY29sb3VyPWxpbmtzKSkgDQp3ZWlnaHRzX3AyIDwtIHAyJGRhdGEgJT4lDQogIGxlZnRfam9pbihsaW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoT1RVKSAlPiUNCiAgICBzdW1tYXJpc2UoUEFfdmFsdWVzID0gbWVhbihzaWduaWYpKSAlPiUNCiAgICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gT1RVKQ0KICAgICklPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICBncm91cF9ieShPVFUpICU+JQ0KICAgIHN1bW1hcmlzZShQRl92YWx1ZXMgPSBtZWFuKHNpZ25pZikpICU+JQ0KICAgIGRwbHlyOjpyZW5hbWUobGFiZWwgPSBPVFUpDQogICAgKSAlPiUNCiAgbXV0YXRlKHZhbHVlcyA9IFBBX3ZhbHVlcyArIFBGX3ZhbHVlcykNCg0Kd2VpZ2h0c19wMiA8LSB3ZWlnaHRzX3AyICU+JQ0KICBsZWZ0X2pvaW4oZGF0YS5mcmFtZShub2RlPXdlaWdodHNfcDIkbm9kZSwgbGlua3MgPSB3ZWlnaHRzX3AyJG5vZGUgJT4lIHB1cnJyOjptYXBfZGJsKGF2ZXJhZ2VfZGVzY2VuZGFudHMsIHRyZWU9dHJlZTIsIGRmPXdlaWdodHNfcDIpKSkNCg0KcDIgPC0gcDIgJTwrJSB3ZWlnaHRzX3AyICArIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91cj1saW5rcykpICsgDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0ic3RlZWxibHVlIiwgaGlnaD0iZGFya29yYW5nZTEiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KIyBUYW5nbGVncmFtIA0KdGFuZ2xlIDwtIG9iaiRhc3NvYyAlPiUNCiAgYXNfZGF0YV9mcmFtZSgpICU+JQ0KICBtYWdyaXR0cjo6c2V0X2NvbG5hbWVzKGMoImxhYmVsLngiLCAibGFiZWwueSIpKSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChsYWJlbC54ID0gU2FtcGxlX05hbWUsIGxhYmVsLnkgPSBPVFUsIHNpZ25pZl9wYWNvPXNpZ25pZikpICU+JQ0KICBsZWZ0X2pvaW4oUEZfbGlua3MgJT4lIA0KICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KGxhYmVsLnggPSBwc3lsbGlkX3NwcCwgbGFiZWwueSA9IE9UVSwgc2lnbmlmX3BhcmE9c2lnbmlmKSkgJT4lDQogIGxlZnRfam9pbihwMSRkYXRhICU+JSBkcGx5cjo6c2VsZWN0KGxhYmVsLCB5KSAlPiUgZHBseXI6OnJlbmFtZShsYWJlbC54ID0gbGFiZWwpLCBieT0ibGFiZWwueCIpICU+JQ0KICBsZWZ0X2pvaW4ocDIkZGF0YSAlPiUgZHBseXI6OnNlbGVjdChsYWJlbCwgeSkgJT4lIGRwbHlyOjpyZW5hbWUobGFiZWwueSA9IGxhYmVsKSwgYnk9ImxhYmVsLnkiKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJhc3NvYyIpICU+JQ0KICByZW5hbWVfYWxsKH5zdHJfcmVwbGFjZSgueCxwYXR0ZXJuPSJcXC4iLCByZXBsYWNlbWVudD0iXyIpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGVuZHNfd2l0aChjKCJfeCIsICJfeSIpKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvPWMoIi52YWx1ZSIsICJ0cmVlIiksIA0KICAgICAgICAgICAgICAgbmFtZXNfc2VwID0gIl8iDQogICAgICAgICAgICAgICkgICU+JQ0KICBtdXRhdGUoc2lnbmlmX3BhY28gPSByZXBsYWNlX25hKHNpZ25pZl9wYWNvLCAwKSwNCiAgICAgICAgIHNpZ25pZl9wYXJhID0gcmVwbGFjZV9uYShzaWduaWZfcGFyYSwgMCkpICU+JQ0KICBtdXRhdGUoc2lnbmlmID0gY2FzZV93aGVuKA0KICAgIHNpZ25pZl9wYWNvPT0wICYgc2lnbmlmX3BhcmE9PTAgIH4gIk5TIiwNCiAgICBzaWduaWZfcGFjbz09MCAmIHNpZ25pZl9wYXJhPT0xIH4gInBhcmEiLA0KICAgIHNpZ25pZl9wYWNvPT0xICYgc2lnbmlmX3BhcmE9PTAgfiAicGFjbyIsDQogICAgc2lnbmlmX3BhY289PTEgJiBzaWduaWZfcGFyYT09MSB+ICJib3RoIg0KICApKSAlPiUNCiAgbXV0YXRlKHRyZWUgPSB0cmVlICU+JSANCiAgICAgICAgICAgc3RyX3JlcGxhY2UoIngiLCAiaG9zdCIpJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlKCJ5IiwgIm1pY3JvYmUiKSkgJT4lDQogIGZpbHRlcighaXMubmEobGFiZWwpKSU+JSANCiAgZ3JvdXBfYnkodHJlZSkgJT4lDQogIG11dGF0ZSh5ID0geSAvIG1heCh5KSkgJT4lDQogIG11dGF0ZShsYWJlbCA9IGxhYmVsICU+JSBzdHJfcmVwbGFjZV9hbGwoIl8iLCAiICIpKSANCg0KDQpnZy50YW5nbGUgPC0gZ2dwbG90KHRhbmdsZSwgYWVzKHg9dHJlZSwgeT15LCBncm91cD1hc3NvYywgY29sb3VyPWFzLmZhY3RvcihzaWduaWYpKSkgKw0KICAgIGdlb21fbGluZShhbHBoYT0wLjgpICsgIA0KICBnZW9tX3RleHQoZGF0YSA9IHRhbmdsZSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcih0cmVlPT0iaG9zdCIpLA0KICAgICAgICAgICAgYWVzKGxhYmVsPWxhYmVsKSxzdGF0ID0gJ3VuaXF1ZScsIGhqdXN0PTEsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSB0YW5nbGUgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIodHJlZT09Im1pY3JvYmUiKSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1sYWJlbCksc3RhdCA9ICd1bmlxdWUnLCBoanVzdD0wLCBjaGVja19vdmVybGFwID0gVFJVRSkrDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoIk5TIj0ic3RlZWxibHVlIiwgInBhY28iPSJkYXJrb3JhbmdlMSIsICJwYXJhIj0iI2RhMmI5MSIsICJib3RoIj0iIzkxZGEyYiIpLCBuYS50cmFuc2xhdGU9RkFMU0UpKw0KICAgIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKGFkZD1jKDAuOCwwLjgpKSkgKyANCiAgICB0aGVtZV92b2lkKCkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9YygwLjAwNSwwLjAwNSkpKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIGxhYnMoY29sb3VyPSJTaWduaWZpY2FuY2U6IikNCg0KZ2cuY2Fyc29uX3RhbmdsZSA8LSBwMSArIGdnLnRhbmdsZSArIChwMiArIHNjYWxlX3hfcmV2ZXJzZSgpKSAjKyBwbG90X2xheW91dCh3aWR0aHMgPSBjKDIsIDEsIDIpKQ0KZ2cuY2Fyc29uX3RhbmdsZSANCg0KcGRmKGZpbGU9ImZpZ3MvY2Fyc29uZWxsYV90YW5nbGVncmFtLnBkZiIsICB3aWR0aCA9IDgsIGhlaWdodCA9IDExLCBwYXBlcj0iYTQiKQ0KICBwbG90KGdnLmNhcnNvbl90YW5nbGUpDQp0cnkoZGV2Lm9mZigpLCBzaWxlbnQ9VFJVRSkNCmBgYA0KDQojIyBQc3lsbGlkIH4gU29kYWxpcw0KDQpgYGB7ciBTb2RhbGlzIGNvcGh5bG99DQojRmxhZyB0b3AgYWJ1bmRhbmNlIHNvZGFsaXMgYnkgc2FtcGxlDQp0b3Bfc29kYWxpcyA8LSBwczIgJT4lDQogIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKGZ1bmN0aW9uICh4KSB4L3N1bSh4KSkgJT4lDQogIHNwZWVkeXNlcTo6cHNtZWx0KCkgJT4lDQogIGZpbHRlcihwc3lsbGlkX3NwcCAlaW4lIHBydW5lZC50cmVlJHRpcC5sYWJlbCkgJT4lDQogIGZpbHRlcihnZW51cz09IlNvZGFsaXMiKSAlPiUNCiAgZ3JvdXBfYnkoU2FtcGxlKSAlPiUgIA0KICBmaWx0ZXIoQWJ1bmRhbmNlID4gMCkgJT4lDQogIHRvcF9uKDEsIHd0PUFidW5kYW5jZSkgJT4lDQogIG11dGF0ZSh0b3AgPSBUUlVFKSANCg0KY29vY3VyIDwtIHBzMiAlPiUNCiAgdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoZnVuY3Rpb24gKHgpIHgvc3VtKHgpKSAlPiUNCiAgc3BlZWR5c2VxOjpwc21lbHQoKSAlPiUNCiAgZmlsdGVyKHBzeWxsaWRfc3BwICVpbiUgcHJ1bmVkLnRyZWUkdGlwLmxhYmVsKSAlPiUNCiAgbGVmdF9qb2luKHRvcF9zb2RhbGlzKSAlPiUNCiAgZmlsdGVyKGdlbnVzPT0iU29kYWxpcyIsIHRvcD09VFJVRSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoT1RVLCBwc3lsbGlkX3NwcCwgU2FtcGxlSUQsIEFidW5kYW5jZSkgJT4lDQogIG11dGF0ZShPVFUgPSBPVFUgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKSkgJT4lDQogIGRwbHlyOjpncm91cF9ieShPVFUsIHBzeWxsaWRfc3BwKSAlPiUNCiAgICBzdW1tYXJpc2UoQWJ1bmRhbmNlID0gc3VtKEFidW5kYW5jZSkpICU+JQ0KICBwaXZvdF93aWRlcihpZF9jb2xzID0gcHN5bGxpZF9zcHAsDQogICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBPVFUsDQogICAgICAgICAgICAgIHZhbHVlc19mcm9tPUFidW5kYW5jZSwNCiAgICAgICAgICAgICAgdmFsdWVzX2ZpbGwgPSBsaXN0KEFidW5kYW5jZSA9IDApKSAgJT4lDQogIGNvbHVtbl90b19yb3duYW1lcygicHN5bGxpZF9zcHAiKSAlPiUNCiAgICBhcy5tYXRyaXgoKSAlPiUgDQogIGFwcGx5KDIsIGZ1bmN0aW9uKHgpIGlmZWxzZSh4ID4gMCwgMSwgMCkpIA0KDQpjb29jdXIgPC0gY29vY3VyWyByb3dTdW1zKGNvb2N1cikgPiAwLGNvbFN1bXMoY29vY3VyKSA+IDBdDQoNCiMgSCBjb3BoZW5ldGljIGRpc3RhbmNlDQpoX3RyZWUgPC0gcHJ1bmVkLnRyZWUNCmhfdHJlZSR0aXAubGFiZWwgPC0gaF90cmVlJHRpcC5sYWJlbCAlPiUNCiAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKCIgfC0iLCAiXyIpDQpoX3RyZWUgPC0gZHJvcC50aXAoaF90cmVlLCBzZXRkaWZmKGhfdHJlZSR0aXAubGFiZWwsIHJvd25hbWVzKGNvb2N1cikpKQ0KaF9kaXN0IDwtIHNxcnQoY29waGVuZXRpYyhoX3RyZWUpKQ0KDQojIFAgY29waGVuZXRpYyBkaXN0YW5jZQ0Kc190cmVlIDwtIHBoeV90cmVlKHBzMykNCnNfdHJlZSR0aXAubGFiZWwgPC0gc190cmVlJHRpcC5sYWJlbCAlPiUNCiAgICAgICAgICAgc3RyX3JlcGxhY2VfYWxsKCIgfC0iLCAiXyIpDQpzX3RyZWUgPC0gZHJvcC50aXAoc190cmVlLCBzZXRkaWZmKHNfdHJlZSR0aXAubGFiZWwsIGNvbG5hbWVzKGNvb2N1cikpKQ0Kc19kaXN0IDwtIHNxcnQoY29waGVuZXRpYyhzX3RyZWUpLzFlKzYgKSAjI2NvbnZlcnQgdG8gTXlhIHNvIGludGVnZXJzIGFyZSBzbWFsbCBlbm91Z2ggZm9yIFBBQ08NCg0KI2FsdGVybmF0aXZlbHkgLSBzcXJ0KGNvcGhlbmV0aWMoc190cmVlKSkNCmNvb2N1ciA8LSBjb29jdXJbaF90cmVlJHRpcC5sYWJlbCwgc190cmVlJHRpcC5sYWJlbF0NCg0KIyBwcmVwYXJlIHBhY28gZGF0YQ0KRCA8LSBwcmVwYXJlX3BhY29fZGF0YShIPWhfZGlzdCwgUD1zX2Rpc3QsIEhQPWNvb2N1cikNCiMgQWRkIHBjb3JkDQpEIDwtIGFkZF9wY29vcmQoRCwgY29ycmVjdGlvbj0nbm9uZScpIA0KDQpwX2hvc3QgPC0gZ2dwbG90KEQkSF9QQ29bLGMoJ0F4aXMuMScsICdBeGlzLjInKV0gJT4lIGFzLmRhdGEuZnJhbWUsIGFlcyhBeGlzLjEsIEF4aXMuMikpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIHRoZW1lX2J3KCkgKw0KICBnZ3RpdGxlKCJIIFBDQSIpDQoNCnBfcGFyYSA8LSBnZ3Bsb3QoRCRQX1BDb1ssYygnQXhpcy4xJywgJ0F4aXMuMicpXSAlPiUgYXMuZGF0YS5mcmFtZSwgYWVzKEF4aXMuMSwgQXhpcy4yKSkgICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIHRoZW1lX2J3KCkrDQogIGdndGl0bGUoIlAgUENBIikNCiAgICANCnBsb3QocF9ob3N0ICsgcF9wYXJhKQ0KDQojIHJ1biBwYWNvDQpwYWNvX3J1biA8LSBQQUNvKEQsIG5wZXJtPTk5OSwgc2VlZD05MDksIG1ldGhvZD0ncXVhc2lzd2FwJywgc3ltbWV0cmljPVRSVUUpDQoNCiNwcmludCBvdmVyYWxsIHNpZ25pZmljYW5jZQ0KcHJpbnQocGFjb19ydW4kZ29mKQ0KDQojIEdldCBpbnRlcmFjdGlvbi1zcGVjaWZpYyBjb3BoeWxvZ2VuZXRpYyBjb250cmlidXRpb25zIHVzaW5nIGphY2tuaWZpbmcNCnBhY29fcnVuIDwtIHBhY29fbGlua3MocGFjb19ydW4pDQpsaW5rcyA8LSBkYXRhLmZyYW1lKGpvaW50PW5hbWVzKHBhY29fcnVuJGphY2trbmlmZSksICNsb3Npbmcgc3Y1MCBoZXJlPw0KICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9dW5uYW1lKHBhY29fcnVuJGphY2trbmlmZSkjLA0KICAgICAgICAgICAgICAgICAgICAjdXBwZXI9dW5uYW1lKHBhY29fcnVuJGphY2trbmlmZSR1cHBlcikNCiAgICAgICAgICAgICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKE9UVSA9IGpvaW50KSAlPiUNCiBzZXBhcmF0ZShPVFUsIGludG89YygiU2FtcGxlX05hbWUiLCAiT1RVIiksIHNlcD0iLSIsIGV4dHJhPSJtZXJnZSIpICU+JQ0KICBtdXRhdGUoc2lnbmlmID0gY2FzZV93aGVuKA0KICAgIHZhbHVlcyA8IG1lYW4oLiR2YWx1ZXMpIH4gMSwNCiAgICB2YWx1ZXMgPiBtZWFuKC4kdmFsdWVzKSB+IDANCiAgKSkNCg0KIyBQbG90IGxpbmtzDQpsaW5rcyAlPiUNCiAgZHBseXI6OnJlbmFtZShsYWJlbCA9IGpvaW50KSAlPiUNCiAgbXV0YXRlKGxhYmVsID0gYXMuZmFjdG9yKGxhYmVsKSwNCiAgICAgICAgIGxhYmVsPWZjdF9yZW9yZGVyKGxhYmVsLCB2YWx1ZXMsIHN1bSkpJT4lDQogIGFycmFuZ2UoLXZhbHVlcykgJT4lDQogIGdncGxvdChhZXMoeD1sYWJlbCwgeT12YWx1ZXMsIGNvbG91cj1hcy5mYWN0b3Ioc2lnbmlmKSkpICsNCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIyBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPXZhbHVlcywgeW1heD11cHBlcikpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihsaW5rcyR2YWx1ZXMpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygic3RlZWxibHVlIiwgImRhcmtvcmFuZ2UxIikpICsNCiAgdGhlbWVfY2xhc3NpYygpKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpDQoNCmRpci5jcmVhdGUoIm91dHB1dC9jb3BoeWxvZ2VueS9wc3lsbGlkX3NlY29uZGFyeSIpDQp3cml0ZV9jc3YobGlua3MsICJvdXRwdXQvY29waHlsb2dlbnkvcHN5bGxpZF9zZWNvbmRhcnkvcHN5bGxpZF9zZWNvbmRhcnlfbGlua3MuY3N2IikNCndyaXRlX2NzdihsaW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoT1RVKSAlPiUNCiAgICBzdW1tYXJpc2UodmFsdWVzID0gbWVhbih2YWx1ZXMpKSwgIm91dHB1dC9jb3BoeWxvZ2VueS9wc3lsbGlkX3NlY29uZGFyeS9zZWNvbmRhcnlfc3ltYmlvbnRfd2VpZ2h0cy5jc3YiKQ0Kd3JpdGVfY3N2KGxpbmtzICU+JSANCiAgICBncm91cF9ieShTYW1wbGVfTmFtZSkgJT4lDQogICAgc3VtbWFyaXNlKHZhbHVlcyA9IG1lYW4odmFsdWVzKSksICJvdXRwdXQvY29waHlsb2dlbnkvcHN5bGxpZF9zZWNvbmRhcnkvcHN5bGxpZF93ZWlnaHRzLmNzdiIpDQoNCiNHZXQgb2JzZXJ2ZWQgcmVzaWR1YWxzIG9mIFByb2NydXN0ZWFuIHN1cGVyaW1wb3NpdGlvbiANCnBhY29fcmVzaWR1YWxzIDwtIHJlc2lkdWFsc19wYWNvKHBhY29fcnVuJHByb2MsIHR5cGUgPSAiaW50ZXJhY3Rpb24iKQ0KIyBWaXN1YWxpc2UgcmVzaWR1YWxzDQpyZXMgPC0gZGF0YS5mcmFtZShPVFU9bmFtZXMocGFjb19yZXNpZHVhbHMpLCB2YWx1ZXM9dW5uYW1lKHBhY29fcmVzaWR1YWxzKSkgJT4lDQogIHNlcGFyYXRlKE9UVSwgaW50bz1jKCJTYW1wbGVfTmFtZSIsICJPVFUiKSwgc2VwPSItIiwgZXh0cmE9Im1lcmdlIikgDQoNCmdncGxvdChyZXMsIGFlcyh4PXZhbHVlcykpKw0KICBnZW9tX2RlbnNpdHkoZmlsbD0nZ3JleTcwJykrDQogIHRoZW1lX2J3KCkrDQogIHhsYWIoJ1Byb2NydXN0ZXMgcmVzaWR1YWxzJykrDQogIHlsYWIoJ0ZyZXF1ZW5jeScpDQoNCiMgUGFyYWZpdCBydW4NClBGX3J1biA8LSBwYXJhZml0KGhfZGlzdCwgc19kaXN0LGNvb2N1ciwgbnBlcm09OTk5LCB0ZXN0LmxpbmtzPVRSVUUpDQpQRl9ydW4kUGFyYUZpdEdsb2JhbA0KUEZfcnVuJHAuZ2xvYmFsDQoNClBGX2xpbmtzIDwtIGFzLmRhdGEuZnJhbWUoUEZfcnVuJGxpbmsudGFibGUpICAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKFBGX3J1biRwYXJhLnBlci5ob3N0KSwgbmFtZSA9ICJIb3N0IiwgdmFsdWU9InBzeWxsaWRfc3BwIikgJT4lDQogICAgICAgICAgICAgICAgICBtdXRhdGUoSG9zdCA9IGFzLm51bWVyaWMoSG9zdCkpKSAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKFBGX3J1biRob3N0LnBlci5wYXJhKSwgbmFtZSA9ICJQYXJhc2l0ZSIsIHZhbHVlPSJPVFUiKSAlPiUNCiAgICAgICAgICAgICAgICAgIG11dGF0ZShQYXJhc2l0ZSA9IGFzLm51bWVyaWMoUGFyYXNpdGUpKSklPiUNCiAgbXV0YXRlKHNpZ25pZiA9IGNhc2Vfd2hlbigNCiAgICBwLkYxIDwgMC4wNSB+IDEsDQogICAgcC5GMSA+IDAuMDUgfiAwDQogICkpIA0KDQojIENvcGh5bG9wbG90DQpjb29jdXIubHV0IDwtIHdoaWNoKGNvb2N1ciA9PTEsIGFyci5pbmQ9VFJVRSkNCmFzc29jIDwtIGNiaW5kKHJvd25hbWVzKGNvb2N1cilbY29vY3VyLmx1dFssMV1dLCBjb2xuYW1lcyhjb29jdXIpW2Nvb2N1ci5sdXRbLDJdXSkNCg0KIyBSb3RhdGUgdGhlIG5vZGVzIHVzaW5nIHBoeXRvb2xzDQpsaWJyYXJ5KHBoeXRvb2xzKQ0Kb2JqIDwtIGNvcGh5bG8odHIxPWhfdHJlZSwgdHIyPXNfdHJlZSwgYXNzb2M9YXNzb2MsIHJvdGF0ZT1UUlVFKSANCg0KIyBwc3lsbGlkX3RyZWUNCnRyZWUxIDwtIG9ialtbInRyZWVzIl1dW1sxXV0NCg0KcDEgPC0gZ2d0cmVlKHRyZWUxKQ0KYXRtZXRvX25vZGUgPC0gcDEkZGF0YSAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KGxhYmVsLCAiQXRtZXRvIikpICU+JQ0KICBwdWxsKG5vZGUpDQpyb290X25vZGUgPC0gcDEkZGF0YSAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KGxhYmVsLCAiQXRtZXRvIikpICU+JQ0KICBwdWxsKHBhcmVudCkNCg0KdHJlZTEgPC0gZ3JvdXBPVFUodHJlZTEsIC5ub2RlPWF0bWV0b19ub2RlKSAjIE1ha2UgdGhlIGF0bWV0byBkb3R0ZWQgdG8gaW5kaWNhdGUgb3V0Z3JvdXAgd2FzIHJlc2NhbGVkDQoNCnAxIDwtIGdndHJlZSh0cmVlMSwgbGFkZGVyaXplPUZBTFNFLCBhZXMoY29sb3VyPWxpbmtzLGxpbmV0eXBlPWdyb3VwKSkNCndlaWdodHNfcDEgPC0gcDEkZGF0YSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICBncm91cF9ieShTYW1wbGVfTmFtZSkgJT4lDQogICAgc3VtbWFyaXNlKFBBX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IFNhbXBsZV9OYW1lKQ0KICAgICklPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICBncm91cF9ieShwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKFBGX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IHBzeWxsaWRfc3BwKQ0KICAgICkgJT4lDQogIG11dGF0ZSh2YWx1ZXMgPSBQQV92YWx1ZXMgKyBQRl92YWx1ZXMpDQoNCiNTY2FsZSB0aGUgYXRtZXRvY3Jhbml1bSBhbmQgcm9vdCBub2RlcyB0byBiZSBzaG9ydGVyDQpwMSRkYXRhW3AxJGRhdGEkbm9kZSAlaW4lIGF0bWV0b19ub2RlLCAieCJdIDwtIG1heChwMSRkYXRhJHgpDQpwMSRkYXRhW3AxJGRhdGEkbm9kZSAlaW4lIHJvb3Rfbm9kZSwgIngiXSA8LSAwLjIgI3Jvb3QNCg0KcDEkZGF0YSRub2RlW3AxJGRhdGEkbm9kZV0NCg0KIyMgR2V0IHZhbHVlcyBmb3IgaGlnaGVyIG5vZGVzDQp3ZWlnaHRzX3AxIDwtIHdlaWdodHNfcDEgJT4lDQogIGxlZnRfam9pbihkYXRhLmZyYW1lKG5vZGU9d2VpZ2h0c19wMSRub2RlLCBsaW5rcyA9IHdlaWdodHNfcDEkbm9kZSAlPiUgcHVycnI6Om1hcF9kYmwoYXZlcmFnZV9kZXNjZW5kYW50cywgdHJlZT10cmVlMSwgZGY9d2VpZ2h0c19wMSkpKQ0KcDEgPC0gcDEgJTwrJSB3ZWlnaHRzX3AxICsgZ2VvbV90aXBwb2ludChhZXMoY29sb3VyPWxpbmtzKSkgKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQojIE9UVSB0cmVlDQp0cmVlMiA8LSBvYmpbWyJ0cmVlcyJdXVtbMl1dDQpzX3RyZWUgPC0gZHJvcC50aXAodHJlZTIsIHNldGRpZmYodHJlZTIkdGlwLmxhYmVsLCBvYmokYXNzb2NbLDJdKSkNCnAyIDwtIGdndHJlZSh0cmVlMiAsIGxhZGRlcml6ZT1GQUxTRSwgYWVzKGNvbG91cj1saW5rcykpIA0Kd2VpZ2h0c19wMiA8LSBwMiRkYXRhICU+JQ0KICBsZWZ0X2pvaW4obGlua3MgJT4lIA0KICAgIGdyb3VwX2J5KE9UVSkgJT4lDQogICAgc3VtbWFyaXNlKFBBX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IE9UVSkNCiAgICApJT4lDQogIGxlZnRfam9pbihQRl9saW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoT1RVKSAlPiUNCiAgICBzdW1tYXJpc2UoUEZfdmFsdWVzID0gbWVhbihzaWduaWYpKSAlPiUNCiAgICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gT1RVKQ0KICAgICkgJT4lDQogIG11dGF0ZSh2YWx1ZXMgPSBQQV92YWx1ZXMgKyBQRl92YWx1ZXMpDQoNCndlaWdodHNfcDIgPC0gd2VpZ2h0c19wMiAlPiUNCiAgbGVmdF9qb2luKGRhdGEuZnJhbWUobm9kZT13ZWlnaHRzX3AyJG5vZGUsIGxpbmtzID0gd2VpZ2h0c19wMiRub2RlICU+JSBwdXJycjo6bWFwX2RibChhdmVyYWdlX2Rlc2NlbmRhbnRzLCB0cmVlPXRyZWUyLCBkZj13ZWlnaHRzX3AyKSkpDQoNCnAyIDwtIHAyICU8KyUgd2VpZ2h0c19wMiAgKyBnZW9tX3RpcHBvaW50KGFlcyhjb2xvdXI9bGlua3MpKSArIA0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgVGFuZ2xlZ3JhbSANCnRhbmdsZSA8LSBvYmokYXNzb2MgJT4lDQogIGFzX2RhdGFfZnJhbWUoKSAlPiUNCiAgbWFncml0dHI6OnNldF9jb2xuYW1lcyhjKCJsYWJlbC54IiwgImxhYmVsLnkiKSkgJT4lDQogIGxlZnRfam9pbihsaW5rcyAlPiUgDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QobGFiZWwueCA9IFNhbXBsZV9OYW1lLCBsYWJlbC55ID0gT1RVLCBzaWduaWZfcGFjbz1zaWduaWYpKSAlPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChsYWJlbC54ID0gcHN5bGxpZF9zcHAsIGxhYmVsLnkgPSBPVFUsIHNpZ25pZl9wYXJhPXNpZ25pZikpICU+JQ0KICBsZWZ0X2pvaW4ocDEkZGF0YSAlPiUgZHBseXI6OnNlbGVjdChsYWJlbCwgeSkgJT4lIGRwbHlyOjpyZW5hbWUobGFiZWwueCA9IGxhYmVsKSwgYnk9ImxhYmVsLngiKSAlPiUNCiAgbGVmdF9qb2luKHAyJGRhdGEgJT4lIGRwbHlyOjpzZWxlY3QobGFiZWwsIHkpICU+JSBkcGx5cjo6cmVuYW1lKGxhYmVsLnkgPSBsYWJlbCksIGJ5PSJsYWJlbC55IikgJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigiYXNzb2MiKSAlPiUNCiAgcmVuYW1lX2FsbCh+c3RyX3JlcGxhY2UoLngscGF0dGVybj0iXFwuIiwgcmVwbGFjZW1lbnQ9Il8iKSkgJT4lDQogIHBpdm90X2xvbmdlcihlbmRzX3dpdGgoYygiX3giLCAiX3kiKSksDQogICAgICAgICAgICAgICBuYW1lc190bz1jKCIudmFsdWUiLCAidHJlZSIpLCANCiAgICAgICAgICAgICAgIG5hbWVzX3NlcCA9ICJfIg0KICAgICAgICAgICAgICApICAlPiUNCiAgbXV0YXRlKHNpZ25pZl9wYWNvID0gcmVwbGFjZV9uYShzaWduaWZfcGFjbywgMCksDQogICAgICAgICBzaWduaWZfcGFyYSA9IHJlcGxhY2VfbmEoc2lnbmlmX3BhcmEsIDApKSAlPiUNCiAgbXV0YXRlKHNpZ25pZiA9IGNhc2Vfd2hlbigNCiAgICBzaWduaWZfcGFjbz09MCAmIHNpZ25pZl9wYXJhPT0wICB+ICJOUyIsDQogICAgc2lnbmlmX3BhY289PTAgJiBzaWduaWZfcGFyYT09MSB+ICJwYXJhIiwNCiAgICBzaWduaWZfcGFjbz09MSAmIHNpZ25pZl9wYXJhPT0wIH4gInBhY28iLA0KICAgIHNpZ25pZl9wYWNvPT0xICYgc2lnbmlmX3BhcmE9PTEgfiAiYm90aCINCiAgKSkgJT4lDQogIG11dGF0ZSh0cmVlID0gdHJlZSAlPiUgDQogICAgICAgICAgIHN0cl9yZXBsYWNlKCJ4IiwgImhvc3QiKSU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZSgieSIsICJtaWNyb2JlIikpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGxhYmVsKSklPiUgDQogIGdyb3VwX2J5KHRyZWUpICU+JQ0KICBtdXRhdGUoeSA9IHkgLyBtYXgoeSkpJT4lDQogIG11dGF0ZShsYWJlbCA9IGxhYmVsICU+JSBzdHJfcmVwbGFjZV9hbGwoIl8iLCAiICIpKSANCg0KDQpnZy50YW5nbGUgPC0gZ2dwbG90KHRhbmdsZSwgYWVzKHg9dHJlZSwgeT15LCBncm91cD1hc3NvYywgY29sb3VyPWFzLmZhY3RvcihzaWduaWYpKSkgKw0KICAgIGdlb21fbGluZShhbHBoYT0wLjgpICsgIA0KICBnZW9tX3RleHQoZGF0YSA9IHRhbmdsZSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcih0cmVlPT0iaG9zdCIpLA0KICAgICAgICAgICAgYWVzKGxhYmVsPWxhYmVsKSxzdGF0ID0gJ3VuaXF1ZScsIGhqdXN0PTEsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSB0YW5nbGUgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIodHJlZT09Im1pY3JvYmUiKSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1sYWJlbCksc3RhdCA9ICd1bmlxdWUnLCBoanVzdD0wLCBjaGVja19vdmVybGFwID0gVFJVRSkrDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoIk5TIj0ic3RlZWxibHVlIiwgInBhY28iPSJkYXJrb3JhbmdlMSIsICJwYXJhIj0iI2RhMmI5MSIsICJib3RoIj0iIzkxZGEyYiIpLCBuYS50cmFuc2xhdGU9RkFMU0UpKw0KICAgIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKGFkZD1jKDAuOCwwLjgpKSkgKyANCiAgICB0aGVtZV92b2lkKCkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9YygwLjAxLDAuMDEpKSsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICBsYWJzKGNvbG91cj0iU2lnbmlmaWNhbmNlOiIpDQoNCmdnLnNvZGFsaXNfdGFuZ2xlIDwtIHAxICsgZ2cudGFuZ2xlICsgKHAyICsgc2NhbGVfeF9yZXZlcnNlKCkpICMrIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMiwgMSwgMikpDQpnZy5zb2RhbGlzX3RhbmdsZQ0KDQpwZGYoZmlsZT0iZmlncy9zb2RhbGlzX3RhbmdsZWdyYW0ucGRmIiwgIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTEsIHBhcGVyPSJhNCIpDQogIHBsb3QoZ2cuc29kYWxpc190YW5nbGUpDQp0cnkoZGV2Lm9mZigpLCBzaWxlbnQ9VFJVRSkNCmBgYA0KDQoNCiMjIFBzeWxsaWQgfiBBcnNlbm9waG9udXMNCg0KYGBge3IgQXJzZW5vcGhvbnVzIGNvcGh5bG99DQojIFN1YnNldCB0byB0b3AgYXJzZW5vcGhvbnVzDQp0b3BfYXJzZSA8LSBwczIgJT4lDQogIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKGZ1bmN0aW9uICh4KSB4L3N1bSh4KSkgJT4lDQogIHNwZWVkeXNlcTo6cHNtZWx0KCkgJT4lDQogIGZpbHRlcihwc3lsbGlkX3NwcCAlaW4lIHBydW5lZC50cmVlJHRpcC5sYWJlbCkgJT4lDQogIGZpbHRlcihnZW51cz09IkFyc2Vub3Bob251cyIpICU+JQ0KICBncm91cF9ieShTYW1wbGUpICU+JSAgDQogIGZpbHRlcihBYnVuZGFuY2UgPiAwKSAlPiUNCiAgdG9wX24oMSwgd3Q9QWJ1bmRhbmNlKSAlPiUNCiAgbXV0YXRlKHRvcCA9IFRSVUUpIA0KDQpjb29jdXIgPC0gcHMyICU+JQ0KICB0cmFuc2Zvcm1fc2FtcGxlX2NvdW50cyhmdW5jdGlvbiAoeCkgeC9zdW0oeCkpICU+JQ0KICBzcGVlZHlzZXE6OnBzbWVsdCgpICU+JQ0KICBmaWx0ZXIocHN5bGxpZF9zcHAgJWluJSBwcnVuZWQudHJlZSR0aXAubGFiZWwpICU+JQ0KICBsZWZ0X2pvaW4odG9wX2Fyc2UpICU+JQ0KICBmaWx0ZXIoZ2VudXM9PSJBcnNlbm9waG9udXMiLCB0b3A9PVRSVUUpICU+JQ0KICBkcGx5cjo6c2VsZWN0KE9UVSwgcHN5bGxpZF9zcHAsIFNhbXBsZUlELCBBYnVuZGFuY2UpICU+JQ0KICBtdXRhdGUoT1RVID0gT1RVICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikpICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoT1RVLCBwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKEFidW5kYW5jZSA9IHN1bShBYnVuZGFuY2UpKSAlPiUNCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IHBzeWxsaWRfc3BwLA0KICAgICAgICAgICAgICBuYW1lc19mcm9tID0gT1RVLA0KICAgICAgICAgICAgICB2YWx1ZXNfZnJvbT1BYnVuZGFuY2UsDQogICAgICAgICAgICAgIHZhbHVlc19maWxsID0gbGlzdChBYnVuZGFuY2UgPSAwKSkgICU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoInBzeWxsaWRfc3BwIikgJT4lDQogICAgYXMubWF0cml4KCkgJT4lIA0KICBhcHBseSgyLCBmdW5jdGlvbih4KSBpZmVsc2UoeCA+IDAsIDEsIDApKSANCg0KY29vY3VyIDwtIGNvb2N1clsgcm93U3Vtcyhjb29jdXIpID4gMCxjb2xTdW1zKGNvb2N1cikgPiAwXQ0KDQojIEggY29waGVuZXRpYyBkaXN0YW5jZQ0KaF90cmVlIDwtIHBydW5lZC50cmVlDQpoX3RyZWUkdGlwLmxhYmVsIDwtIGhfdHJlZSR0aXAubGFiZWwgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKQ0KaF90cmVlIDwtIGRyb3AudGlwKGhfdHJlZSwgc2V0ZGlmZihoX3RyZWUkdGlwLmxhYmVsLCByb3duYW1lcyhjb29jdXIpKSkNCmhfZGlzdCA8LSBzcXJ0KGNvcGhlbmV0aWMoaF90cmVlKSkNCg0KIyBQIGNvcGhlbmV0aWMgZGlzdGFuY2UNCnNfdHJlZSA8LSBwaHlfdHJlZShwczMpDQpzX3RyZWUkdGlwLmxhYmVsIDwtIHNfdHJlZSR0aXAubGFiZWwgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKQ0Kc190cmVlIDwtIGRyb3AudGlwKHNfdHJlZSwgc2V0ZGlmZihzX3RyZWUkdGlwLmxhYmVsLCBjb2xuYW1lcyhjb29jdXIpKSkNCnNfZGlzdCA8LSBzcXJ0KGNvcGhlbmV0aWMoc190cmVlKS8xZSs2ICkgIyNjb252ZXJ0IHRvIE15YSBzbyBpbnRlZ2VycyBhcmUgc21hbGwgZW5vdWdoIGZvciBQQUNPDQoNCiNhbHRlcm5hdGl2ZWx5IC0gc3FydChjb3BoZW5ldGljKHNfdHJlZSkpDQpjb29jdXIgPC0gY29vY3VyW2hfdHJlZSR0aXAubGFiZWwsIHNfdHJlZSR0aXAubGFiZWxdDQoNCiMgcHJlcGFyZSBwYWNvIGRhdGENCkQgPC0gcHJlcGFyZV9wYWNvX2RhdGEoSD1oX2Rpc3QsIFA9c19kaXN0LCBIUD1jb29jdXIpDQojIEFkZCBwY29yZA0KRCA8LSBhZGRfcGNvb3JkKEQsIGNvcnJlY3Rpb249J25vbmUnKSANCg0KcF9ob3N0IDwtIGdncGxvdChEJEhfUENvWyxjKCdBeGlzLjEnLCAnQXhpcy4yJyldICU+JSBhcy5kYXRhLmZyYW1lLCBhZXMoQXhpcy4xLCBBeGlzLjIpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICB0aGVtZV9idygpICsNCiAgZ2d0aXRsZSgiSCBQQ0EiKQ0KDQpwX3BhcmEgPC0gZ2dwbG90KEQkUF9QQ29bLGMoJ0F4aXMuMScsICdBeGlzLjInKV0gJT4lIGFzLmRhdGEuZnJhbWUsIGFlcyhBeGlzLjEsIEF4aXMuMikpICArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICB0aGVtZV9idygpKw0KICBnZ3RpdGxlKCJQIFBDQSIpDQogICAgDQpwbG90KHBfaG9zdCArIHBfcGFyYSkNCg0KIyBydW4gcGFjbw0KcGFjb19ydW4gPC0gUEFDbyhELCBucGVybT05OTksIHNlZWQ9OTA5LCBtZXRob2Q9J3F1YXNpc3dhcCcsIHN5bW1ldHJpYz1GQUxTRSkNCg0KI3ByaW50IG92ZXJhbGwgc2lnbmlmaWNhbmNlDQpwcmludChwYWNvX3J1biRnb2YpDQoNCiMgR2V0IGludGVyYWN0aW9uLXNwZWNpZmljIGNvcGh5bG9nZW5ldGljIGNvbnRyaWJ1dGlvbnMgdXNpbmcgamFja25pZmluZw0KcGFjb19ydW4gPC0gcGFjb19saW5rcyhwYWNvX3J1bikNCmxpbmtzIDwtIGRhdGEuZnJhbWUoam9pbnQ9bmFtZXMocGFjb19ydW4kamFja2tuaWZlKSwNCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzPXVubmFtZShwYWNvX3J1biRqYWNra25pZmUpIywNCiAgICAgICAgICAgICAgICAgICAgI3VwcGVyPXVubmFtZShwYWNvX3J1biRqYWNra25pZmUkdXBwZXIpDQogICAgICAgICAgICAgICAgICAgICkgJT4lDQogIG11dGF0ZShPVFUgPSBqb2ludCkgJT4lDQogc2VwYXJhdGUoT1RVLCBpbnRvPWMoIlNhbXBsZV9OYW1lIiwgIk9UVSIpLCBzZXA9Ii0iLCBleHRyYT0ibWVyZ2UiKSAlPiUNCiAgbXV0YXRlKHNpZ25pZiA9IGNhc2Vfd2hlbigNCiAgICB2YWx1ZXMgPCBtZWFuKC4kdmFsdWVzKSB+IDEsDQogICAgdmFsdWVzID4gbWVhbiguJHZhbHVlcykgfiAwDQogICkpDQoNCiMgUGxvdCBsaW5rcw0KbGlua3MgJT4lDQogIGRwbHlyOjpyZW5hbWUobGFiZWwgPSBqb2ludCkgJT4lDQogIG11dGF0ZShsYWJlbCA9IGFzLmZhY3RvcihsYWJlbCksDQogICAgICAgICBsYWJlbD1mY3RfcmVvcmRlcihsYWJlbCwgdmFsdWVzLCBzdW0pKSU+JQ0KICBhcnJhbmdlKC12YWx1ZXMpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9bGFiZWwsIHk9dmFsdWVzLCBjb2xvdXI9YXMuZmFjdG9yKHNpZ25pZikpKSArDQogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPXZhbHVlcywgeW1heD11cHBlcikpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihsaW5rcyR2YWx1ZXMpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygic3RlZWxibHVlIiwgImRhcmtvcmFuZ2UxIikpICsNCiAgdGhlbWVfY2xhc3NpYygpKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpDQoNCmRpci5jcmVhdGUoIm91dHB1dC9jb3BoeWxvZ2VueS9wc3lsbGlkX3NlY29uZGFyeSIpDQp3cml0ZV9jc3YobGlua3MsICJvdXRwdXQvY29waHlsb2dlbnkvcHN5bGxpZF9zZWNvbmRhcnkvcHN5bGxpZF9zZWNvbmRhcnlfbGlua3MuY3N2IikNCndyaXRlX2NzdihsaW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoT1RVKSAlPiUNCiAgICBzdW1tYXJpc2UodmFsdWVzID0gbWVhbih2YWx1ZXMpKSwgIm91dHB1dC9jb3BoeWxvZ2VueS9wc3lsbGlkX3NlY29uZGFyeS9zZWNvbmRhcnlfc3ltYmlvbnRfd2VpZ2h0cy5jc3YiKQ0Kd3JpdGVfY3N2KGxpbmtzICU+JSANCiAgICBncm91cF9ieShTYW1wbGVfTmFtZSkgJT4lDQogICAgc3VtbWFyaXNlKHZhbHVlcyA9IG1lYW4odmFsdWVzKSksICJvdXRwdXQvY29waHlsb2dlbnkvcHN5bGxpZF9zZWNvbmRhcnkvcHN5bGxpZF93ZWlnaHRzLmNzdiIpDQoNCiNHZXQgb2JzZXJ2ZWQgcmVzaWR1YWxzIG9mIFByb2NydXN0ZWFuIHN1cGVyaW1wb3NpdGlvbiANCnBhY29fcmVzaWR1YWxzIDwtIHJlc2lkdWFsc19wYWNvKHBhY29fcnVuJHByb2MsIHR5cGUgPSAiaW50ZXJhY3Rpb24iKQ0KIyBWaXN1YWxpc2UgcmVzaWR1YWxzDQpyZXMgPC0gZGF0YS5mcmFtZShPVFU9bmFtZXMocGFjb19yZXNpZHVhbHMpLCB2YWx1ZXM9dW5uYW1lKHBhY29fcmVzaWR1YWxzKSkgJT4lDQogIHNlcGFyYXRlKE9UVSwgaW50bz1jKCJTYW1wbGVfTmFtZSIsICJPVFUiKSwgc2VwPSItIiwgZXh0cmE9Im1lcmdlIikgDQoNCmdncGxvdChyZXMsIGFlcyh4PXZhbHVlcykpKw0KICBnZW9tX2RlbnNpdHkoZmlsbD0nZ3JleTcwJykrDQogIHRoZW1lX2J3KCkrDQogIHhsYWIoJ1Byb2NydXN0ZXMgcmVzaWR1YWxzJykrDQogIHlsYWIoJ0ZyZXF1ZW5jeScpDQoNCiMgUGFyYWZpdCBydW4NClBGX3J1biA8LSBwYXJhZml0KGhfZGlzdCwgc19kaXN0LGNvb2N1ciwgbnBlcm09OTk5LCB0ZXN0LmxpbmtzPVRSVUUpDQpQRl9ydW4kUGFyYUZpdEdsb2JhbA0KUEZfcnVuJHAuZ2xvYmFsDQoNClBGX2xpbmtzIDwtIGFzLmRhdGEuZnJhbWUoUEZfcnVuJGxpbmsudGFibGUpICAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKFBGX3J1biRwYXJhLnBlci5ob3N0KSwgbmFtZSA9ICJIb3N0IiwgdmFsdWU9InBzeWxsaWRfc3BwIikgJT4lDQogICAgICAgICAgICAgICAgICBtdXRhdGUoSG9zdCA9IGFzLm51bWVyaWMoSG9zdCkpKSAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKFBGX3J1biRob3N0LnBlci5wYXJhKSwgbmFtZSA9ICJQYXJhc2l0ZSIsIHZhbHVlPSJPVFUiKSAlPiUNCiAgICAgICAgICAgICAgICAgIG11dGF0ZShQYXJhc2l0ZSA9IGFzLm51bWVyaWMoUGFyYXNpdGUpKSklPiUNCiAgbXV0YXRlKHNpZ25pZiA9IGNhc2Vfd2hlbigNCiAgICBwLkYxIDwgMC4wNSB+IDEsDQogICAgcC5GMSA+IDAuMDUgfiAwDQogICkpIA0KDQojIENvcGh5bG9wbG90DQpjb29jdXIubHV0IDwtIHdoaWNoKGNvb2N1ciA9PTEsIGFyci5pbmQ9VFJVRSkNCmFzc29jIDwtIGNiaW5kKHJvd25hbWVzKGNvb2N1cilbY29vY3VyLmx1dFssMV1dLCBjb2xuYW1lcyhjb29jdXIpW2Nvb2N1ci5sdXRbLDJdXSkNCg0KIyBSb3RhdGUgdGhlIG5vZGVzIHVzaW5nIHBoeXRvb2xzDQpsaWJyYXJ5KHBoeXRvb2xzKQ0Kb2JqIDwtIGNvcGh5bG8odHIxPWhfdHJlZSwgdHIyPXNfdHJlZSwgYXNzb2M9YXNzb2MsIHJvdGF0ZT1UUlVFKSANCg0KIyBwc3lsbGlkX3RyZWUNCnRyZWUxIDwtIG9ialtbInRyZWVzIl1dW1sxXV0NCg0KcDEgPC0gZ2d0cmVlKHRyZWUxKQ0KYXRtZXRvX25vZGUgPC0gcDEkZGF0YSAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KGxhYmVsLCAiQXRtZXRvIikpICU+JQ0KICBwdWxsKG5vZGUpDQpyb290X25vZGUgPC0gcDEkZGF0YSAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KGxhYmVsLCAiQXRtZXRvIikpICU+JQ0KICBwdWxsKHBhcmVudCkNCg0KdHJlZTEgPC0gZ3JvdXBPVFUodHJlZTEsIC5ub2RlPWF0bWV0b19ub2RlKSAjIE1ha2UgdGhlIGF0bWV0byBkb3R0ZWQgdG8gaW5kaWNhdGUgb3V0Z3JvdXAgd2FzIHJlc2NhbGVkDQoNCnAxIDwtIGdndHJlZSh0cmVlMSwgbGFkZGVyaXplPUZBTFNFLCBhZXMoY29sb3VyPWxpbmtzLGxpbmV0eXBlPWdyb3VwKSkNCndlaWdodHNfcDEgPC0gcDEkZGF0YSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICBncm91cF9ieShTYW1wbGVfTmFtZSkgJT4lDQogICAgc3VtbWFyaXNlKFBBX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IFNhbXBsZV9OYW1lKQ0KICAgICklPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICBncm91cF9ieShwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKFBGX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IHBzeWxsaWRfc3BwKQ0KICAgICkgJT4lDQogIG11dGF0ZSh2YWx1ZXMgPSBQQV92YWx1ZXMgKyBQRl92YWx1ZXMpDQoNCiNTY2FsZSB0aGUgYXRtZXRvY3Jhbml1bSBhbmQgcm9vdCBub2RlcyB0byBiZSBzaG9ydGVyDQpwMSRkYXRhW3AxJGRhdGEkbm9kZSAlaW4lIGF0bWV0b19ub2RlLCAieCJdIDwtIG1heChwMSRkYXRhJHgpDQpwMSRkYXRhW3AxJGRhdGEkbm9kZSAlaW4lIHJvb3Rfbm9kZSwgIngiXSA8LSAwLjIgI3Jvb3QNCg0KcDEkZGF0YSRub2RlW3AxJGRhdGEkbm9kZV0NCg0KIyMgR2V0IHZhbHVlcyBmb3IgaGlnaGVyIG5vZGVzDQp3ZWlnaHRzX3AxIDwtIHdlaWdodHNfcDEgJT4lDQogIGxlZnRfam9pbihkYXRhLmZyYW1lKG5vZGU9d2VpZ2h0c19wMSRub2RlLCBsaW5rcyA9IHdlaWdodHNfcDEkbm9kZSAlPiUgcHVycnI6Om1hcF9kYmwoYXZlcmFnZV9kZXNjZW5kYW50cywgdHJlZT10cmVlMSwgZGY9d2VpZ2h0c19wMSkpKQ0KcDEgPC0gcDEgJTwrJSB3ZWlnaHRzX3AxICsgZ2VvbV90aXBwb2ludChhZXMoY29sb3VyPWxpbmtzKSkgKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQojIE9UVSB0cmVlDQp0cmVlMiA8LSBvYmpbWyJ0cmVlcyJdXVtbMl1dDQpzX3RyZWUgPC0gZHJvcC50aXAodHJlZTIsIHNldGRpZmYodHJlZTIkdGlwLmxhYmVsLCBvYmokYXNzb2NbLDJdKSkNCnAyIDwtIGdndHJlZSh0cmVlMiAsIGxhZGRlcml6ZT1GQUxTRSwgYWVzKGNvbG91cj1saW5rcykpIA0Kd2VpZ2h0c19wMiA8LSBwMiRkYXRhICU+JQ0KICBsZWZ0X2pvaW4obGlua3MgJT4lIA0KICAgIGdyb3VwX2J5KE9UVSkgJT4lDQogICAgc3VtbWFyaXNlKFBBX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IE9UVSkNCiAgICApJT4lDQogIGxlZnRfam9pbihQRl9saW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoT1RVKSAlPiUNCiAgICBzdW1tYXJpc2UoUEZfdmFsdWVzID0gbWVhbihzaWduaWYpKSAlPiUNCiAgICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gT1RVKQ0KICAgICkgJT4lDQogIG11dGF0ZSh2YWx1ZXMgPSBQQV92YWx1ZXMgKyBQRl92YWx1ZXMpDQoNCndlaWdodHNfcDIgPC0gd2VpZ2h0c19wMiAlPiUNCiAgbGVmdF9qb2luKGRhdGEuZnJhbWUobm9kZT13ZWlnaHRzX3AyJG5vZGUsIGxpbmtzID0gd2VpZ2h0c19wMiRub2RlICU+JSBwdXJycjo6bWFwX2RibChhdmVyYWdlX2Rlc2NlbmRhbnRzLCB0cmVlPXRyZWUyLCBkZj13ZWlnaHRzX3AyKSkpDQoNCnAyIDwtIHAyICU8KyUgd2VpZ2h0c19wMiAgKyBnZW9tX3RpcHBvaW50KGFlcyhjb2xvdXI9bGlua3MpKSArIA0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgVGFuZ2xlZ3JhbSANCnRhbmdsZSA8LSBvYmokYXNzb2MgJT4lDQogIGFzX2RhdGFfZnJhbWUoKSAlPiUNCiAgbWFncml0dHI6OnNldF9jb2xuYW1lcyhjKCJsYWJlbC54IiwgImxhYmVsLnkiKSkgJT4lDQogIGxlZnRfam9pbihsaW5rcyAlPiUgDQogICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QobGFiZWwueCA9IFNhbXBsZV9OYW1lLCBsYWJlbC55ID0gT1RVLCBzaWduaWZfcGFjbz1zaWduaWYpKSAlPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChsYWJlbC54ID0gcHN5bGxpZF9zcHAsIGxhYmVsLnkgPSBPVFUsIHNpZ25pZl9wYXJhPXNpZ25pZikpICU+JQ0KICBsZWZ0X2pvaW4ocDEkZGF0YSAlPiUgZHBseXI6OnNlbGVjdChsYWJlbCwgeSkgJT4lIGRwbHlyOjpyZW5hbWUobGFiZWwueCA9IGxhYmVsKSwgYnk9ImxhYmVsLngiKSAlPiUNCiAgbGVmdF9qb2luKHAyJGRhdGEgJT4lIGRwbHlyOjpzZWxlY3QobGFiZWwsIHkpICU+JSBkcGx5cjo6cmVuYW1lKGxhYmVsLnkgPSBsYWJlbCksIGJ5PSJsYWJlbC55IikgJT4lDQogIHJvd25hbWVzX3RvX2NvbHVtbigiYXNzb2MiKSAlPiUNCiAgcmVuYW1lX2FsbCh+c3RyX3JlcGxhY2UoLngscGF0dGVybj0iXFwuIiwgcmVwbGFjZW1lbnQ9Il8iKSkgJT4lDQogIHBpdm90X2xvbmdlcihlbmRzX3dpdGgoYygiX3giLCAiX3kiKSksDQogICAgICAgICAgICAgICBuYW1lc190bz1jKCIudmFsdWUiLCAidHJlZSIpLCANCiAgICAgICAgICAgICAgIG5hbWVzX3NlcCA9ICJfIg0KICAgICAgICAgICAgICApICAlPiUNCiAgbXV0YXRlKHNpZ25pZl9wYWNvID0gcmVwbGFjZV9uYShzaWduaWZfcGFjbywgMCksDQogICAgICAgICBzaWduaWZfcGFyYSA9IHJlcGxhY2VfbmEoc2lnbmlmX3BhcmEsIDApKSAlPiUNCiAgbXV0YXRlKHNpZ25pZiA9IGNhc2Vfd2hlbigNCiAgICBzaWduaWZfcGFjbz09MCAmIHNpZ25pZl9wYXJhPT0wICB+ICJOUyIsDQogICAgc2lnbmlmX3BhY289PTAgJiBzaWduaWZfcGFyYT09MSB+ICJwYXJhIiwNCiAgICBzaWduaWZfcGFjbz09MSAmIHNpZ25pZl9wYXJhPT0wIH4gInBhY28iLA0KICAgIHNpZ25pZl9wYWNvPT0xICYgc2lnbmlmX3BhcmE9PTEgfiAiYm90aCINCiAgKSkgJT4lDQogIG11dGF0ZSh0cmVlID0gdHJlZSAlPiUgDQogICAgICAgICAgIHN0cl9yZXBsYWNlKCJ4IiwgImhvc3QiKSU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZSgieSIsICJtaWNyb2JlIikpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGxhYmVsKSklPiUgDQogIGdyb3VwX2J5KHRyZWUpICU+JQ0KICBtdXRhdGUoeSA9IHkgLyBtYXgoeSkpJT4lDQogIG11dGF0ZShsYWJlbCA9IGxhYmVsICU+JSBzdHJfcmVwbGFjZV9hbGwoIl8iLCAiICIpKSANCg0KDQpnZy50YW5nbGUgPC0gZ2dwbG90KHRhbmdsZSwgYWVzKHg9dHJlZSwgeT15LCBncm91cD1hc3NvYywgY29sb3VyPWFzLmZhY3RvcihzaWduaWYpKSkgKw0KICAgIGdlb21fbGluZShhbHBoYT0wLjgpICsgIA0KICBnZW9tX3RleHQoZGF0YSA9IHRhbmdsZSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcih0cmVlPT0iaG9zdCIpLA0KICAgICAgICAgICAgYWVzKGxhYmVsPWxhYmVsKSxzdGF0ID0gJ3VuaXF1ZScsIGhqdXN0PTEsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSB0YW5nbGUgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIodHJlZT09Im1pY3JvYmUiKSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1sYWJlbCksc3RhdCA9ICd1bmlxdWUnLCBoanVzdD0wLCBjaGVja19vdmVybGFwID0gVFJVRSkrDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoIk5TIj0ic3RlZWxibHVlIiwgInBhY28iPSJkYXJrb3JhbmdlMSIsICJwYXJhIj0iI2RhMmI5MSIsICJib3RoIj0iIzkxZGEyYiIpLCBuYS50cmFuc2xhdGU9RkFMU0UpKw0KICAgIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKGFkZD1jKDAuOCwwLjgpKSkgKyANCiAgICB0aGVtZV92b2lkKCkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9YygwLjAwNSwwLjAwNSkpKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIGxhYnMoY29sb3VyPSJTaWduaWZpY2FuY2U6IikNCmdnLmFyc2VfdGFuZ2xlIDwtIHAxICsgZ2cudGFuZ2xlICsgKHAyICsgc2NhbGVfeF9yZXZlcnNlKCkpICMrIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMiwgMSwgMikpDQpnZy5hcnNlX3RhbmdsZQ0KDQpwZGYoZmlsZT0iZmlncy9hcnNlbm9waG9udXNfdGFuZ2xlZ3JhbS5wZGYiLCAgd2lkdGggPSA4LCBoZWlnaHQgPSAxMSwgcGFwZXI9ImE0IikNCiAgcGxvdChnZy5hcnNlX3RhbmdsZSkNCnRyeShkZXYub2ZmKCksIHNpbGVudD1UUlVFKQ0KYGBgDQoNCiMjIFBzeWxsaWQgfiAgaG9zdHBsYW50DQoNClRhbmdsZWdyYW0gb2YgYWxsIHBsYW50cyBhbmQgIHBzeWxsaWRzIQ0KYGBge3IgaG9zdHBsYW50LXBzeWxsaWR9DQpwbGFudC50cmVlIDwtIHJlYWQudHJlZSgic2FtcGxlX2RhdGEvcGxhbnRfdHJlZS5ud2siKQ0KcGxhbnQudHJlZSAgPC0gZHJvcC50aXAocGxhbnQudHJlZSAsICJTb3Bob3JhX21pY3JvcGh5bGxhXy1rb3doYWkiICkNCg0KI1ByZXBhcmUgY28tb2NjdXJhbmNlIG1hdHJpeA0KY29vY3VyIDwtIHNhbXBsZV9kYXRhKHBzMikgJT4lDQogIGFzX3RpYmJsZSgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KHBzeWxsaWRfc3BwLCBob3N0cGxhbnRfc3BwKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShwc3lsbGlkX3NwcCkpICU+JQ0KICB1bmlxdWUoKSAlPiUNCiAgbXV0YXRlKHBzeWxsaWRfc3BwID0gcHN5bGxpZF9zcHAgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKSwNCiAgICAgICAgIGhvc3RwbGFudF9zcHAgPSBob3N0cGxhbnRfc3BwICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIiksDQogICAgICAgICBwcmVzZW5jZSA9IDENCiAgICAgICAgICkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiaG9zdHBsYW50X3NwcCIsDQogICAgICAgICAgICAgIHZhbHVlc19mcm9tPSJwcmVzZW5jZSIsDQogICAgICAgICAgICAgIHZhbHVlc19maWxsID0gbGlzdChwcmVzZW5jZSA9IDApKSAlPiUNCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJwc3lsbGlkX3NwcCIpICU+JQ0KICB0KCkNCg0KIyBIIGNvcGhlbmV0aWMgZGlzdGFuY2UNCmhfdHJlZSA8LSBwbGFudC50cmVlDQpoX3RyZWUkdGlwLmxhYmVsIDwtIGhfdHJlZSR0aXAubGFiZWwgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKQ0KaF90cmVlIDwtIGRyb3AudGlwKGhfdHJlZSwgc2V0ZGlmZihoX3RyZWUkdGlwLmxhYmVsLCByb3duYW1lcyhjb29jdXIpKSkNCmhfZGlzdCA8LSBzcXJ0KGNvcGhlbmV0aWMoaF90cmVlKSkNCg0KIyBQIGNvcGhlbmV0aWMgZGlzdGFuY2UNCnNfdHJlZSA8LSBwcnVuZWQudHJlZQ0Kc190cmVlJHRpcC5sYWJlbCA8LSBzX3RyZWUkdGlwLmxhYmVsICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikNCnNfdHJlZSA8LSBkcm9wLnRpcChzX3RyZWUsIHNldGRpZmYoc190cmVlJHRpcC5sYWJlbCwgY29sbmFtZXMoY29vY3VyKSkpDQpzX2Rpc3QgPC0gc3FydChjb3BoZW5ldGljKHNfdHJlZSkpDQoNCmNvb2N1ciA8LSBjb29jdXJbaF90cmVlJHRpcC5sYWJlbCwgc190cmVlJHRpcC5sYWJlbF0NCg0KIyBwcmVwYXJlIHBhY28gZGF0YQ0KRCA8LSBwcmVwYXJlX3BhY29fZGF0YShIPWhfZGlzdCwgUD1zX2Rpc3QsIEhQPWNvb2N1cikNCiMgQWRkIHBjb3JkDQpEIDwtIGFkZF9wY29vcmQoRCwgY29ycmVjdGlvbj0nbm9uZScpIA0KDQpwX2hvc3QgPC0gZ2dwbG90KEQkSF9QQ29bLGMoJ0F4aXMuMScsICdBeGlzLjInKV0gJT4lIGFzLmRhdGEuZnJhbWUsIGFlcyhBeGlzLjEsIEF4aXMuMikpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIHRoZW1lX2J3KCkgKw0KICBnZ3RpdGxlKCJIIFBDQSIpDQoNCnBfcGFyYSA8LSBnZ3Bsb3QoRCRQX1BDb1ssYygnQXhpcy4xJywgJ0F4aXMuMicpXSAlPiUgYXMuZGF0YS5mcmFtZSwgYWVzKEF4aXMuMSwgQXhpcy4yKSkgICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIHRoZW1lX2J3KCkrDQogIGdndGl0bGUoIlAgUENBIikNCiAgICANCnBsb3QocF9ob3N0ICsgcF9wYXJhKQ0KDQojIHJ1biBwYWNvDQpwYWNvX3J1biA8LSBQQUNvKEQsIG5wZXJtPTk5OSwgc2VlZD05MDksIG1ldGhvZD0ncXVhc2lzd2FwJywgc3ltbWV0cmljPUZBTFNFKQ0KDQojcHJpbnQgb3ZlcmFsbCBzaWduaWZpY2FuY2UNCnByaW50KHBhY29fcnVuJGdvZikNCg0KIyBHZXQgaW50ZXJhY3Rpb24tc3BlY2lmaWMgY29waHlsb2dlbmV0aWMgY29udHJpYnV0aW9ucyB1c2luZyBsZWF2ZS1vbmUtb3V0IGphY2tuaWZpbmcNCnBhY29fcnVuIDwtIHBhY29fbGlua3MocGFjb19ydW4sIC5wYXJhbGxlbCA9IFRSVUUpDQoNCiMgZ2V0IGxpbmtzDQpsaW5rcyA8LSBkYXRhLmZyYW1lKA0KICBqb2ludD1uYW1lcyhwYWNvX3J1biRqYWNra25pZmUpLA0KICB2YWx1ZXM9dW5uYW1lKHBhY29fcnVuJGphY2trbmlmZSkjLA0KICAjdXBwZXI9dW5uYW1lKHBhY29fcnVuJGphY2trbmlmZSR1cHBlcikNCiAgKSAlPiUNCiBtdXRhdGUoT1RVID0gam9pbnQpICU+JQ0KIHNlcGFyYXRlKE9UVSwgaW50bz1jKCJob3N0cGxhbnRfc3BwIiwgInBzeWxsaWRfc3BwIiksIHNlcD0iLSIsIGV4dHJhPSJtZXJnZSIpICU+JQ0KICBtdXRhdGUoc2lnbmlmID0gY2FzZV93aGVuKA0KICAgIHZhbHVlcyA8IG1lYW4oLiR2YWx1ZXMpIH4gMSwNCiAgICB2YWx1ZXMgPiBtZWFuKC4kdmFsdWVzKSB+IDANCiAgKSkNCiMgUGxvdCBsaW5rcw0KbGlua3MgJT4lDQogIGRwbHlyOjpyZW5hbWUobGFiZWwgPSBqb2ludCkgJT4lDQogIG11dGF0ZShsYWJlbCA9IGFzLmZhY3RvcihsYWJlbCksDQogICAgICAgICBsYWJlbD1mY3RfcmVvcmRlcihsYWJlbCwgdmFsdWVzLCBzdW0pKSU+JQ0KICBhcnJhbmdlKC12YWx1ZXMpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9bGFiZWwsIHk9dmFsdWVzLCBjb2xvdXI9YXMuZmFjdG9yKHNpZ25pZikpKSArDQogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICMgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj12YWx1ZXMsIHltYXg9dXBwZXIpKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4obGlua3MkdmFsdWVzKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInN0ZWVsYmx1ZSIsICJkYXJrb3JhbmdlMSIpKSArDQogIHRoZW1lX2NsYXNzaWMoKSsgDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQ0KDQpkaXIuY3JlYXRlKCJvdXRwdXQvY29waHlsb2dlbnkvcHN5bGxpZF9ob3N0cGxhbnQiKQ0Kd3JpdGVfY3N2KGxpbmtzLCAib3V0cHV0L2NvcGh5bG9nZW55L3BzeWxsaWRfaG9zdHBsYW50L3BzeWxsaWRfaG9zdHBsYW50X2xpbmtzLmNzdiIpDQp3cml0ZV9jc3YobGlua3MgJT4lIA0KICAgIGdyb3VwX2J5KHBzeWxsaWRfc3BwKSAlPiUNCiAgICBzdW1tYXJpc2UodmFsdWVzID0gbWVhbih2YWx1ZXMpKSwgIm91dHB1dC9jb3BoeWxvZ2VueS9wc3lsbGlkX2hvc3RwbGFudC9wc3lsbGlkX3dlaWdodHMuY3N2IikNCndyaXRlX2NzdihsaW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoaG9zdHBsYW50X3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKHZhbHVlcyA9IG1lYW4odmFsdWVzKSksICJvdXRwdXQvY29waHlsb2dlbnkvcHN5bGxpZF9ob3N0cGxhbnQvaG9zdHBsYW50X3dlaWdodHMuY3N2IikNCg0KI0dldCBvYnNlcnZlZCByZXNpZHVhbHMgb2YgUHJvY3J1c3RlYW4gc3VwZXJpbXBvc2l0aW9uIA0KcGFjb19yZXNpZHVhbHMgPC0gcmVzaWR1YWxzX3BhY28ocGFjb19ydW4kcHJvYywgdHlwZSA9ICJpbnRlcmFjdGlvbiIpDQoNCiMgVmlzdWFsaXNlIHJlc2lkdWFscw0KcmVzIDwtIGRhdGEuZnJhbWUoT1RVPW5hbWVzKHBhY29fcmVzaWR1YWxzKSwgdmFsdWVzPXVubmFtZShwYWNvX3Jlc2lkdWFscykpICU+JQ0KICBzZXBhcmF0ZShPVFUsIGludG89YygiaG9zdHBsYW50X3NwcCIsICJwc3lsbGlkX3NwcCIpLCBzZXA9Ii0iLCBleHRyYT0ibWVyZ2UiKQ0KDQpnZ3Bsb3QocmVzLCBhZXMoeD12YWx1ZXMpKSsNCiAgZ2VvbV9kZW5zaXR5KGZpbGw9J2dyZXk3MCcpKw0KICAjZmFjZXRfd3JhcCh+cHN5bGxpZF9nZW51cykgKw0KICB0aGVtZV9idygpKw0KICB4bGFiKCdQcm9jcnVzdGVzIHJlc2lkdWFscycpKw0KICB5bGFiKCdGcmVxdWVuY3knKQ0KDQojIFBhcmFmaXQgcnVuDQpQRl9ydW4gPC0gcGFyYWZpdChoX2Rpc3QsIHNfZGlzdCwgdChjb29jdXIpLCBucGVybT05OTksIHRlc3QubGlua3M9VFJVRSwgc2lsZW50PUZBTFNFKQ0KUEZfcnVuJFBhcmFGaXRHbG9iYWwNClBGX3J1biRwLmdsb2JhbA0KDQpQRl9saW5rcyA8LSBhcy5kYXRhLmZyYW1lKFBGX3J1biRsaW5rLnRhYmxlKSAgJT4lDQogICAgICBsZWZ0X2pvaW4oZW5mcmFtZShuYW1lcyhQRl9ydW4kcGFyYS5wZXIuaG9zdCksIG5hbWUgPSAiSG9zdCIsIHZhbHVlPSJob3N0cGxhbnRfc3BwIikgJT4lDQogICAgICAgICAgICAgICAgICBtdXRhdGUoSG9zdCA9IGFzLm51bWVyaWMoSG9zdCkpKSAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKFBGX3J1biRob3N0LnBlci5wYXJhKSwgbmFtZSA9ICJQYXJhc2l0ZSIsIHZhbHVlPSJwc3lsbGlkX3NwcCIpICU+JQ0KICAgICAgICAgICAgICAgICAgbXV0YXRlKFBhcmFzaXRlID0gYXMubnVtZXJpYyhQYXJhc2l0ZSkpKSU+JQ0KICBtdXRhdGUoc2lnbmlmID0gY2FzZV93aGVuKA0KICAgIHAuRjEgPCAwLjA1IH4gMSwNCiAgICBwLkYxID4gMC4wNSB+IDANCiAgKSkgDQoNCiMgQ29waHlsb3Bsb3QNCmNvb2N1ci5sdXQgPC0gd2hpY2goY29vY3VyID09MSwgYXJyLmluZD1UUlVFKQ0KYXNzb2MgPC0gY2JpbmQocm93bmFtZXMoY29vY3VyKVtjb29jdXIubHV0WywxXV0sIGNvbG5hbWVzKGNvb2N1cilbY29vY3VyLmx1dFssMl1dKQ0KDQojIFJvdGF0ZSB0aGUgbm9kZXMgdXNpbmcgcGh5dG9vbHMNCm9iaiA8LSBjb3BoeWxvKHRyMT1oX3RyZWUsIHRyMj1zX3RyZWUsIGFzc29jPWFzc29jLCByb3RhdGU9VFJVRSkgDQoNCiMgRXh0cmFjdCB0aGUgZ29vZHMgZm9yIGdndHJlZQ0KIyBwbGFudCB0cmVlDQp0cmVlMSA8LSBvYmpbWyJ0cmVlcyJdXVtbMV1dDQoNCnAxIDwtIGdndHJlZSh0cmVlMSwgbGFkZGVyaXplPUZBTFNFLCBhZXMoY29sb3VyPWxpbmtzKSkNCndlaWdodHNfcDEgPC0gcDEkZGF0YSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICBncm91cF9ieShob3N0cGxhbnRfc3BwKSAlPiUNCiAgICBzdW1tYXJpc2UoUEFfdmFsdWVzID0gbWVhbihzaWduaWYpKSAlPiUNCiAgICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gaG9zdHBsYW50X3NwcCkNCiAgICApJT4lDQogIGxlZnRfam9pbihQRl9saW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoaG9zdHBsYW50X3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKFBGX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IGhvc3RwbGFudF9zcHApDQogICAgKSAlPiUNCiAgbXV0YXRlKHZhbHVlcyA9IFBBX3ZhbHVlcyArIFBGX3ZhbHVlcykNCg0KIyMgR2V0IHZhbHVlcyBmb3IgaGlnaGVyIG5vZGVzDQp3ZWlnaHRzX3AxIDwtIHdlaWdodHNfcDEgJT4lDQogIGxlZnRfam9pbihkYXRhLmZyYW1lKG5vZGU9d2VpZ2h0c19wMSRub2RlLCBsaW5rcyA9IHdlaWdodHNfcDEkbm9kZSAlPiUgcHVycnI6Om1hcF9kYmwoYXZlcmFnZV9kZXNjZW5kYW50cywgdHJlZT10cmVlMSwgZGY9d2VpZ2h0c19wMSkpKQ0KcDEgPC0gcDEgJTwrJSB3ZWlnaHRzX3AxICsgZ2VvbV90aXBwb2ludChhZXMoY29sb3VyPWxpbmtzKSkgKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQojIHBzeWxsaWRfdHJlZQ0KdHJlZTIgPC0gb2JqW1sidHJlZXMiXV1bWzJdXQ0Kc190cmVlIDwtIGRyb3AudGlwKHRyZWUyLCBzZXRkaWZmKHRyZWUyJHRpcC5sYWJlbCwgb2JqJGFzc29jWywyXSkpDQoNCnAyIDwtIGdndHJlZSh0cmVlMikNCmF0bWV0b19ub2RlIDwtIHAyJGRhdGEgJT4lIA0KICBmaWx0ZXIoc3RyX2RldGVjdChsYWJlbCwgIkF0bWV0byIpKSAlPiUNCiAgcHVsbChub2RlKQ0Kcm9vdF9ub2RlIDwtIHAyJGRhdGEgJT4lIA0KICBmaWx0ZXIoc3RyX2RldGVjdChsYWJlbCwgIkF0bWV0byIpKSAlPiUNCiAgcHVsbChwYXJlbnQpDQoNCnRyZWUyIDwtIGdyb3VwT1RVKHRyZWUyLCAubm9kZT1hdG1ldG9fbm9kZSkgIyBNYWtlIHRoZSBhdG1ldG8gZG90dGVkIHRvIGluZGljYXRlIG91dGdyb3VwIHdhcyByZXNjYWxlZA0KDQpwMiA8LSBnZ3RyZWUodHJlZTIgLCBsYWRkZXJpemU9VFJVRSwgYWVzKGNvbG91cj1saW5rcywgbGluZXR5cGU9Z3JvdXApKSANCndlaWdodHNfcDIgPC0gcDIkZGF0YSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICBncm91cF9ieShwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKFBBX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IHBzeWxsaWRfc3BwKQ0KICAgICklPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICBncm91cF9ieShwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKFBGX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IHBzeWxsaWRfc3BwKQ0KICAgICkgJT4lDQogIG11dGF0ZSh2YWx1ZXMgPSBQQV92YWx1ZXMgKyBQRl92YWx1ZXMpDQoNCiNTY2FsZSB0aGUgYXRtZXRvY3Jhbml1bSBhbmQgcm9vdCBub2RlcyB0byBiZSBzaG9ydGVyDQpwMiRkYXRhW3AyJGRhdGEkbm9kZSAlaW4lIGF0bWV0b19ub2RlLCAieCJdIDwtIG1heChwMiRkYXRhJHgpDQpwMiRkYXRhW3AyJGRhdGEkbm9kZSAlaW4lIHJvb3Rfbm9kZSwgIngiXSA8LSAwLjIgI3Jvb3QNCg0KcDIkZGF0YSRub2RlW3AyJGRhdGEkbm9kZV0NCg0Kd2VpZ2h0c19wMiA8LSB3ZWlnaHRzX3AyICU+JQ0KICBsZWZ0X2pvaW4oZGF0YS5mcmFtZShub2RlPXdlaWdodHNfcDIkbm9kZSwgbGlua3MgPSB3ZWlnaHRzX3AyJG5vZGUgJT4lIHB1cnJyOjptYXBfZGJsKGF2ZXJhZ2VfZGVzY2VuZGFudHMsIHRyZWU9dHJlZTIsIGRmPXdlaWdodHNfcDIpKSkNCg0KcDIgPC0gcDIgJTwrJSB3ZWlnaHRzX3AyICArIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91cj1saW5rcykpICsgDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0ic3RlZWxibHVlIiwgaGlnaD0iZGFya29yYW5nZTEiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KIyBUYW5nbGVncmFtIA0KdGFuZ2xlIDwtIG9iaiRhc3NvYyAlPiUNCiAgYXNfZGF0YV9mcmFtZSgpICU+JQ0KICBtYWdyaXR0cjo6c2V0X2NvbG5hbWVzKGMoImxhYmVsLngiLCAibGFiZWwueSIpKSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChsYWJlbC54ID0gaG9zdHBsYW50X3NwcCwgbGFiZWwueSA9IHBzeWxsaWRfc3BwLCBzaWduaWZfcGFjbz1zaWduaWYpKSAlPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChsYWJlbC54ID0gaG9zdHBsYW50X3NwcCwgbGFiZWwueSA9IHBzeWxsaWRfc3BwLCBzaWduaWZfcGFyYT1zaWduaWYpKSAlPiUNCiAgbGVmdF9qb2luKHAxJGRhdGEgJT4lIGRwbHlyOjpzZWxlY3QobGFiZWwsIHkpICU+JSBkcGx5cjo6cmVuYW1lKGxhYmVsLnggPSBsYWJlbCksIGJ5PSJsYWJlbC54IikgJT4lDQogIGxlZnRfam9pbihwMiRkYXRhICU+JSBkcGx5cjo6c2VsZWN0KGxhYmVsLCB5KSAlPiUgZHBseXI6OnJlbmFtZShsYWJlbC55ID0gbGFiZWwpLCBieT0ibGFiZWwueSIpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oImFzc29jIikgJT4lDQogIHJlbmFtZV9hbGwofnN0cl9yZXBsYWNlKC54LHBhdHRlcm49IlxcLiIsIHJlcGxhY2VtZW50PSJfIikpICU+JQ0KICBwaXZvdF9sb25nZXIoZW5kc193aXRoKGMoIl94IiwgIl95IikpLA0KICAgICAgICAgICAgICAgbmFtZXNfdG89YygiLnZhbHVlIiwgInRyZWUiKSwgDQogICAgICAgICAgICAgICBuYW1lc19zZXAgPSAiXyINCiAgICAgICAgICAgICAgKSAgJT4lDQogIG11dGF0ZShzaWduaWZfcGFjbyA9IHJlcGxhY2VfbmEoc2lnbmlmX3BhY28sIDApLA0KICAgICAgICAgc2lnbmlmX3BhcmEgPSByZXBsYWNlX25hKHNpZ25pZl9wYXJhLCAwKSkgJT4lDQogIG11dGF0ZShzaWduaWYgPSBjYXNlX3doZW4oDQogICAgc2lnbmlmX3BhY289PTAgJiBzaWduaWZfcGFyYT09MCAgfiAiTlMiLA0KICAgIHNpZ25pZl9wYWNvPT0wICYgc2lnbmlmX3BhcmE9PTEgfiAicGFyYSIsDQogICAgc2lnbmlmX3BhY289PTEgJiBzaWduaWZfcGFyYT09MCB+ICJwYWNvIiwNCiAgICBzaWduaWZfcGFjbz09MSAmIHNpZ25pZl9wYXJhPT0xIH4gImJvdGgiDQogICkpICU+JQ0KICBtdXRhdGUodHJlZSA9IHRyZWUgJT4lIA0KICAgICAgICAgICBzdHJfcmVwbGFjZSgieCIsICJob3N0IiklPiUNCiAgICAgICAgICAgc3RyX3JlcGxhY2UoInkiLCAibWljcm9iZSIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShsYWJlbCkpJT4lIA0KICBncm91cF9ieSh0cmVlKSAlPiUNCiAgbXV0YXRlKHkgPSB5IC8gbWF4KHkpKSU+JQ0KICBtdXRhdGUobGFiZWwgPSBsYWJlbCAlPiUgc3RyX3JlcGxhY2VfYWxsKCJfIiwgIiAiKSkgDQoNCmdnLnRhbmdsZSA8LSBnZ3Bsb3QodGFuZ2xlLCBhZXMoeD1mYWN0b3IodHJlZSwgbGV2ZWxzPWMoIm1pY3JvYmUiLCAiaG9zdCIpKSwgeT15LCBncm91cD1hc3NvYywgY29sb3VyPWFzLmZhY3RvcihzaWduaWYpKSkgKw0KICAgIGdlb21fbGluZShhbHBoYT0wLjgpICsgIA0KICBnZW9tX3RleHQoZGF0YSA9IHRhbmdsZSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcih0cmVlPT0iaG9zdCIpLA0KICAgICAgICAgICAgYWVzKGxhYmVsPWxhYmVsKSxzdGF0ID0gJ3VuaXF1ZScsIGhqdXN0PTAsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSB0YW5nbGUgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIodHJlZT09Im1pY3JvYmUiKSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1sYWJlbCksc3RhdCA9ICd1bmlxdWUnLCBoanVzdD0xLCBjaGVja19vdmVybGFwID0gVFJVRSkrDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoIk5TIj0ic3RlZWxibHVlIiwgInBhY28iPSJkYXJrb3JhbmdlMSIsICJwYXJhIj0iI2RhMmI5MSIsICJib3RoIj0iIzkxZGEyYiIpLCBuYS50cmFuc2xhdGU9RkFMU0UpKw0KICAgIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKGFkZD1jKDAuOCwwLjgpKSkgKyANCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kPWMoMC4wMDUsMC4wMDUpKSsNCiAgICB0aGVtZV92b2lkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIGxhYnMoY29sb3VyPSJTaWduaWZpY2FuY2UiKQ0KDQpnZy5wbGFudF90YW5nbGUgPC0gcDIgKyBnZy50YW5nbGUgKyAocDEgKyBzY2FsZV94X3JldmVyc2UoKSkgIysgcGxvdF9sYXlvdXQod2lkdGhzID0gYygyLCAxLCAyKSkNCmdnLnBsYW50X3RhbmdsZQ0KDQpwZGYoZmlsZT0iZmlncy9wbGFudF90YW5nbGVncmFtLnBkZiIsICB3aWR0aCA9IDgsIGhlaWdodCA9IDExLCBwYXBlcj0iYTQiKQ0KICBwbG90KGdnLnBsYW50X3RhbmdsZSkNCnRyeShkZXYub2ZmKCksIHNpbGVudD1UUlVFKQ0KYGBgDQoNCiMgUG93ZWxsaWENCg0KIyMgUG93ZWxsaWEgfiBDYXJzb25lbGxhDQoNCmBgYHtyIFBvd2VsbGlhIGNhcnNvbmVsbGF9DQojRmxhZyB0b3AgYWJ1bmRhbmNlIGNhcnNvbmVsbGEgYnkgc2FtcGxlDQp0b3BfY2Fyc29uIDwtIHBzMiAlPiUNCiAgdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoZnVuY3Rpb24gKHgpIHgvc3VtKHgpKSAlPiUNCiAgc3BlZWR5c2VxOjpwc21lbHQoKSAlPiUNCiAgZmlsdGVyKHBzeWxsaWRfc3BwICVpbiUgcHJ1bmVkLnRyZWUkdGlwLmxhYmVsKSAlPiUNCiAgZmlsdGVyKGdlbnVzPT0iQ2FuZGlkYXR1cyBDYXJzb25lbGxhIikgJT4lDQogIGdyb3VwX2J5KFNhbXBsZSkgJT4lICANCiAgZmlsdGVyKEFidW5kYW5jZSA+IDApICU+JQ0KICB0b3BfbigxLCB3dD1BYnVuZGFuY2UpICU+JQ0KICBtdXRhdGUodG9wID0gVFJVRSkgDQoNCmNvb2N1ciA8LSBwczIgJT4lDQogIHN1YnNldF9zYW1wbGVzKHBzeWxsaWRfZ2VudXMgPT0gIlBvd2VsbGlhIikgJT4lDQogIGZpbHRlcl90YXhhKGZ1bmN0aW9uKHgpIG1lYW4oeCkgPiAwLCBUUlVFKSAlPiUNCiAgdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMoZnVuY3Rpb24gKHgpIHgvc3VtKHgpKSAlPiUNCiAgc3BlZWR5c2VxOjpwc21lbHQoKSAlPiUNCiAgZmlsdGVyKHBzeWxsaWRfc3BwICVpbiUgcHJ1bmVkLnRyZWUkdGlwLmxhYmVsKSAlPiUNCiAgbGVmdF9qb2luKHRvcF9jYXJzb24pICU+JQ0KICBmaWx0ZXIoZ2VudXM9PSJDYW5kaWRhdHVzIENhcnNvbmVsbGEiLCB0b3A9PVRSVUUpICU+JQ0KICBkcGx5cjo6c2VsZWN0KE9UVSwgcHN5bGxpZF9zcHAsIFNhbXBsZUlELCBBYnVuZGFuY2UpICU+JQ0KICBtdXRhdGUoT1RVID0gT1RVICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikpICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoT1RVLCBwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKEFidW5kYW5jZSA9IHN1bShBYnVuZGFuY2UpKSAlPiUNCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IHBzeWxsaWRfc3BwLA0KICAgICAgICAgICAgICBuYW1lc19mcm9tID0gT1RVLA0KICAgICAgICAgICAgICB2YWx1ZXNfZnJvbT1BYnVuZGFuY2UsDQogICAgICAgICAgICAgIHZhbHVlc19maWxsID0gbGlzdChBYnVuZGFuY2UgPSAwKSkgICU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoInBzeWxsaWRfc3BwIikgJT4lDQogICAgYXMubWF0cml4KCkgJT4lIA0KICBhcHBseSgyLCBmdW5jdGlvbih4KSBpZmVsc2UoeCA+IDAsIDEsIDApKSANCg0KIyBIIGNvcGhlbmV0aWMgZGlzdGFuY2UNCmhfdHJlZSA8LSBwcnVuZWQudHJlZQ0KaF90cmVlJHRpcC5sYWJlbCA8LSBoX3RyZWUkdGlwLmxhYmVsICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikNCmhfdHJlZSA8LSBkcm9wLnRpcChoX3RyZWUsIHNldGRpZmYoaF90cmVlJHRpcC5sYWJlbCwgcm93bmFtZXMoY29vY3VyKSkpDQpoX2Rpc3QgPC0gc3FydChjb3BoZW5ldGljKGhfdHJlZSkpDQoNCiMgUCBjb3BoZW5ldGljIGRpc3RhbmNlDQpzX3RyZWUgPC0gcGh5X3RyZWUocHMzKQ0Kc190cmVlJHRpcC5sYWJlbCA8LSBzX3RyZWUkdGlwLmxhYmVsICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikNCnNfdHJlZSA8LSBkcm9wLnRpcChzX3RyZWUsIHNldGRpZmYoc190cmVlJHRpcC5sYWJlbCwgY29sbmFtZXMoY29vY3VyKSkpDQpzX2Rpc3QgPC0gc3FydChjb3BoZW5ldGljKHNfdHJlZSkvMWUrNiApICMjY29udmVydCB0byBNeWEgc28gaW50ZWdlcnMgYXJlIHNtYWxsIGVub3VnaCBmb3IgUEFDTw0KDQpjb29jdXIgPC0gY29vY3VyW2hfdHJlZSR0aXAubGFiZWwsIHNfdHJlZSR0aXAubGFiZWxdDQoNCiMgcHJlcGFyZSBwYWNvIGRhdGENCkQgPC0gcHJlcGFyZV9wYWNvX2RhdGEoSD1oX2Rpc3QsIFA9c19kaXN0LCBIUD1jb29jdXIpDQojIEFkZCBwY29yZA0KRCA8LSBhZGRfcGNvb3JkKEQsIGNvcnJlY3Rpb249J25vbmUnKSANCg0KcF9ob3N0IDwtIGdncGxvdChEJEhfUENvWyxjKCdBeGlzLjEnLCAnQXhpcy4yJyldICU+JSBhcy5kYXRhLmZyYW1lLCBhZXMoQXhpcy4xLCBBeGlzLjIpKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICB0aGVtZV9idygpICsNCiAgZ2d0aXRsZSgiSCBQQ0EiKQ0KDQpwX3BhcmEgPC0gZ2dwbG90KEQkUF9QQ29bLGMoJ0F4aXMuMScsICdBeGlzLjInKV0gJT4lIGFzLmRhdGEuZnJhbWUsIGFlcyhBeGlzLjEsIEF4aXMuMikpICArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICB0aGVtZV9idygpKw0KICBnZ3RpdGxlKCJQIFBDQSIpDQogICAgDQpwbG90KHBfaG9zdCArIHBfcGFyYSkNCg0KIyBydW4gcGFjbw0KcGFjb19ydW4gPC0gUEFDbyhELCBucGVybT05OTksIHNlZWQ9OTA5LCBtZXRob2Q9J3F1YXNpc3dhcCcsIHN5bW1ldHJpYz1GQUxTRSkNCg0KI3ByaW50IG92ZXJhbGwgc2lnbmlmaWNhbmNlDQpwcmludChwYWNvX3J1biRnb2YpDQoNCiMgR2V0IGludGVyYWN0aW9uLXNwZWNpZmljIGNvcGh5bG9nZW5ldGljIGNvbnRyaWJ1dGlvbnMgdXNpbmcgamFja25pZmluZw0KcGFjb19ydW4gPC0gcGFjb19saW5rcyhwYWNvX3J1bikNCmxpbmtzIDwtIGRhdGEuZnJhbWUoam9pbnQ9bmFtZXMocGFjb19ydW4kamFja2tuaWZlKSwgDQogICAgICAgICAgICAgICAgICAgIHZhbHVlcz11bm5hbWUocGFjb19ydW4kamFja2tuaWZlKSMsIA0KICAgICAgICAgICAgICAgICAgICAjdXBwZXI9dW5uYW1lKHBhY29fcnVuJGphY2trbmlmZSR1cHBlcikNCiAgICAgICAgICAgICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKE9UVSA9IGpvaW50KSAlPiUNCiBzZXBhcmF0ZShPVFUsIGludG89YygiU2FtcGxlX05hbWUiLCAiT1RVIiksIHNlcD0iLSIsIGV4dHJhPSJtZXJnZSIpICU+JQ0KICBtdXRhdGUoc2lnbmlmID0gY2FzZV93aGVuKA0KICAgIHZhbHVlcyA8IG1lYW4oLiR2YWx1ZXMpIH4gMSwNCiAgICB2YWx1ZXMgPiBtZWFuKC4kdmFsdWVzKSB+IDANCiAgKSkNCg0KIyBQbG90IGxpbmtzDQpsaW5rcyAlPiUNCiAgZHBseXI6OnJlbmFtZShsYWJlbCA9IGpvaW50KSAlPiUNCiAgbXV0YXRlKGxhYmVsID0gYXMuZmFjdG9yKGxhYmVsKSwNCiAgICAgICAgIGxhYmVsPWZjdF9yZW9yZGVyKGxhYmVsLCB2YWx1ZXMsIHN1bSkpJT4lDQogIGFycmFuZ2UoLXZhbHVlcykgJT4lDQogIGdncGxvdChhZXMoeD1sYWJlbCwgeT12YWx1ZXMsIGNvbG91cj1hcy5mYWN0b3Ioc2lnbmlmKSkpICsNCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICNnZW9tX2Vycm9yYmFyKGFlcyh5bWluPXZhbHVlcywgeW1heD11cHBlcikpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbihsaW5rcyR2YWx1ZXMpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Yygic3RlZWxibHVlIiwgImRhcmtvcmFuZ2UxIikpICsNCiAgdGhlbWVfY2xhc3NpYygpKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpDQoNCmRpci5jcmVhdGUoIm91dHB1dC9jb3BoeWxvZ2VueS90cmlvemFfY2Fyc29uZWxsYSIpDQp3cml0ZV9jc3YobGlua3MsICJvdXRwdXQvY29waHlsb2dlbnkvdHJpb3phX2NhcnNvbmVsbGEvdHJpb3phX2NhcnNvbmVsbGFfbGlua3MuY3N2IikNCndyaXRlX2NzdihsaW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoT1RVKSAlPiUNCiAgICBzdW1tYXJpc2UodmFsdWVzID0gbWVhbih2YWx1ZXMpKSwgIm91dHB1dC9jb3BoeWxvZ2VueS90cmlvemFfY2Fyc29uZWxsYS9jYXJzb25lbGxhX3dlaWdodHMuY3N2IikNCndyaXRlX2NzdihsaW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoU2FtcGxlX05hbWUpICU+JQ0KICAgIHN1bW1hcmlzZSh2YWx1ZXMgPSBtZWFuKHZhbHVlcykpLCAib3V0cHV0L2NvcGh5bG9nZW55L3RyaW96YV9jYXJzb25lbGxhL3BzeWxsaWRfd2VpZ2h0cy5jc3YiKQ0KDQojR2V0IG9ic2VydmVkIHJlc2lkdWFscyBvZiBQcm9jcnVzdGVhbiBzdXBlcmltcG9zaXRpb24gDQpwYWNvX3Jlc2lkdWFscyA8LSByZXNpZHVhbHNfcGFjbyhwYWNvX3J1biRwcm9jLCB0eXBlID0gImludGVyYWN0aW9uIikNCiMgVmlzdWFsaXNlIHJlc2lkdWFscw0KcmVzIDwtIGRhdGEuZnJhbWUoT1RVPW5hbWVzKHBhY29fcmVzaWR1YWxzKSwgdmFsdWVzPXVubmFtZShwYWNvX3Jlc2lkdWFscykpICU+JQ0KICBzZXBhcmF0ZShPVFUsIGludG89YygiU2FtcGxlX05hbWUiLCAiT1RVIiksIHNlcD0iLSIsIGV4dHJhPSJtZXJnZSIpIA0KDQpnZ3Bsb3QocmVzLCBhZXMoeD12YWx1ZXMpKSsNCiAgZ2VvbV9kZW5zaXR5KGZpbGw9J2dyZXk3MCcpKw0KICB0aGVtZV9idygpKw0KICB4bGFiKCdQcm9jcnVzdGVzIHJlc2lkdWFscycpKw0KICB5bGFiKCdGcmVxdWVuY3knKQ0KDQojIFBhcmFmaXQgcnVuDQpQRl9ydW4gPC0gcGFyYWZpdChoX2Rpc3QsIHNfZGlzdCwgdChjb29jdXIpLCBucGVybT05OTksIHRlc3QubGlua3M9VFJVRSwgc2lsZW50PUZBTFNFKQ0KUEZfcnVuJFBhcmFGaXRHbG9iYWwNClBGX3J1biRwLmdsb2JhbA0KDQpQRl9saW5rcyA8LSBhcy5kYXRhLmZyYW1lKFBGX3J1biRsaW5rLnRhYmxlKSAgJT4lDQogICAgICBsZWZ0X2pvaW4oZW5mcmFtZShuYW1lcyhQRl9ydW4kcGFyYS5wZXIuaG9zdCksIG5hbWUgPSAiSG9zdCIsIHZhbHVlPSJwc3lsbGlkX3NwcCIpICU+JQ0KICAgICAgICAgICAgICAgICAgbXV0YXRlKEhvc3QgPSBhcy5udW1lcmljKEhvc3QpKSkgJT4lDQogICAgICBsZWZ0X2pvaW4oZW5mcmFtZShuYW1lcyhQRl9ydW4kaG9zdC5wZXIucGFyYSksIG5hbWUgPSAiUGFyYXNpdGUiLCB2YWx1ZT0iT1RVIikgJT4lDQogICAgICAgICAgICAgICAgICBtdXRhdGUoUGFyYXNpdGUgPSBhcy5udW1lcmljKFBhcmFzaXRlKSkpJT4lDQogIG11dGF0ZShzaWduaWYgPSBjYXNlX3doZW4oDQogICAgcC5GMSA8IDAuMDUgfiAxLA0KICAgIHAuRjEgPiAwLjA1IH4gMA0KICApKSANCg0KIyBDb3BoeWxvcGxvdA0KY29vY3VyLmx1dCA8LSB3aGljaChjb29jdXIgPT0xLCBhcnIuaW5kPVRSVUUpDQphc3NvYyA8LSBjYmluZChyb3duYW1lcyhjb29jdXIpW2Nvb2N1ci5sdXRbLDFdXSwgY29sbmFtZXMoY29vY3VyKVtjb29jdXIubHV0WywyXV0pDQoNCiMgUm90YXRlIHRoZSBub2RlcyB1c2luZyBwaHl0b29scw0Kb2JqIDwtIGNvcGh5bG8odHIxPWhfdHJlZSwgdHIyPXNfdHJlZSwgYXNzb2M9YXNzb2MsIHJvdGF0ZT1UUlVFKSANCg0KIyBwc3lsbGlkX3RyZWUNCnRyZWUxIDwtIG9ialtbInRyZWVzIl1dW1sxXV0NCg0KcDEgPC0gZ2d0cmVlKHRyZWUxKQ0KYXRtZXRvX25vZGUgPC0gcDEkZGF0YSAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KGxhYmVsLCAiQXRtZXRvIikpICU+JQ0KICBwdWxsKG5vZGUpDQpyb290X25vZGUgPC0gcDEkZGF0YSAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KGxhYmVsLCAiQXRtZXRvIikpICU+JQ0KICBwdWxsKHBhcmVudCkNCg0KdHJlZTEgPC0gZ3JvdXBPVFUodHJlZTEsIC5ub2RlPWF0bWV0b19ub2RlKSAjIE1ha2UgdGhlIGF0bWV0byBkb3R0ZWQgdG8gaW5kaWNhdGUgb3V0Z3JvdXAgd2FzIHJlc2NhbGVkDQoNCnAxIDwtIGdndHJlZSh0cmVlMSwgbGFkZGVyaXplPUZBTFNFLCBhZXMoY29sb3VyPWxpbmtzLGxpbmV0eXBlPWdyb3VwKSkNCndlaWdodHNfcDEgPC0gcDEkZGF0YSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICBncm91cF9ieShTYW1wbGVfTmFtZSkgJT4lDQogICAgc3VtbWFyaXNlKFBBX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IFNhbXBsZV9OYW1lKQ0KICAgICklPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICBncm91cF9ieShwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKFBGX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IHBzeWxsaWRfc3BwKQ0KICAgICkgJT4lDQogIG11dGF0ZSh2YWx1ZXMgPSBQQV92YWx1ZXMgKyBQRl92YWx1ZXMpDQoNCiNTY2FsZSB0aGUgYXRtZXRvY3Jhbml1bSBhbmQgcm9vdCBub2RlcyB0byBiZSBzaG9ydGVyDQpwMSRkYXRhW3AxJGRhdGEkbm9kZSAlaW4lIGF0bWV0b19ub2RlLCAieCJdIDwtIG1heChwMSRkYXRhJHgpDQpwMSRkYXRhW3AxJGRhdGEkbm9kZSAlaW4lIHJvb3Rfbm9kZSwgIngiXSA8LSAwLjIgI3Jvb3QNCg0KcDEkZGF0YSRub2RlW3AxJGRhdGEkbm9kZV0NCg0KIyMgR2V0IHZhbHVlcyBmb3IgaGlnaGVyIG5vZGVzDQp3ZWlnaHRzX3AxIDwtIHdlaWdodHNfcDEgJT4lDQogIGxlZnRfam9pbihkYXRhLmZyYW1lKG5vZGU9d2VpZ2h0c19wMSRub2RlLCBsaW5rcyA9IHdlaWdodHNfcDEkbm9kZSAlPiUgcHVycnI6Om1hcF9kYmwoYXZlcmFnZV9kZXNjZW5kYW50cywgdHJlZT10cmVlMSwgZGY9d2VpZ2h0c19wMSkpKQ0KcDEgPC0gcDEgJTwrJSB3ZWlnaHRzX3AxICsgZ2VvbV90aXBwb2ludChhZXMoY29sb3VyPWxpbmtzKSkgKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsIGhpZ2g9ImRhcmtvcmFuZ2UxIikgICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQojIE9UVSB0cmVlDQp0cmVlMiA8LSBvYmpbWyJ0cmVlcyJdXVtbMl1dDQpzX3RyZWUgPC0gZHJvcC50aXAodHJlZTIsIHNldGRpZmYodHJlZTIkdGlwLmxhYmVsLCBvYmokYXNzb2NbLDJdKSkNCnAyIDwtIGdndHJlZSh0cmVlMiAsIGxhZGRlcml6ZT1UUlVFLCBhZXMoY29sb3VyPWxpbmtzKSkgDQp3ZWlnaHRzX3AyIDwtIHAyJGRhdGEgJT4lDQogIGxlZnRfam9pbihsaW5rcyAlPiUgDQogICAgZ3JvdXBfYnkoT1RVKSAlPiUNCiAgICBzdW1tYXJpc2UoUEFfdmFsdWVzID0gbWVhbihzaWduaWYpKSAlPiUNCiAgICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gT1RVKQ0KICAgICklPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICBncm91cF9ieShPVFUpICU+JQ0KICAgIHN1bW1hcmlzZShQRl92YWx1ZXMgPSBtZWFuKHNpZ25pZikpICU+JQ0KICAgIGRwbHlyOjpyZW5hbWUobGFiZWwgPSBPVFUpDQogICAgKSAlPiUNCiAgbXV0YXRlKHZhbHVlcyA9IFBBX3ZhbHVlcyArIFBGX3ZhbHVlcykNCg0Kd2VpZ2h0c19wMiA8LSB3ZWlnaHRzX3AyICU+JQ0KICBsZWZ0X2pvaW4oZGF0YS5mcmFtZShub2RlPXdlaWdodHNfcDIkbm9kZSwgbGlua3MgPSB3ZWlnaHRzX3AyJG5vZGUgJT4lIHB1cnJyOjptYXBfZGJsKGF2ZXJhZ2VfZGVzY2VuZGFudHMsIHRyZWU9dHJlZTIsIGRmPXdlaWdodHNfcDIpKSkNCg0KcDIgPC0gcDIgJTwrJSB3ZWlnaHRzX3AyICArIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91cj1saW5rcykpICsgDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0ic3RlZWxibHVlIiwgaGlnaD0iZGFya29yYW5nZTEiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KIyBUYW5nbGVncmFtIA0KdGFuZ2xlIDwtIG9iaiRhc3NvYyAlPiUNCiAgYXNfZGF0YV9mcmFtZSgpICU+JQ0KICBtYWdyaXR0cjo6c2V0X2NvbG5hbWVzKGMoImxhYmVsLngiLCAibGFiZWwueSIpKSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChsYWJlbC54ID0gU2FtcGxlX05hbWUsIGxhYmVsLnkgPSBPVFUsIHNpZ25pZl9wYWNvPXNpZ25pZikpICU+JQ0KICBsZWZ0X2pvaW4oUEZfbGlua3MgJT4lIA0KICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KGxhYmVsLnggPSBwc3lsbGlkX3NwcCwgbGFiZWwueSA9IE9UVSwgc2lnbmlmX3BhcmE9c2lnbmlmKSkgJT4lDQogIGxlZnRfam9pbihwMSRkYXRhICU+JSBkcGx5cjo6c2VsZWN0KGxhYmVsLCB5KSAlPiUgZHBseXI6OnJlbmFtZShsYWJlbC54ID0gbGFiZWwpLCBieT0ibGFiZWwueCIpICU+JQ0KICBsZWZ0X2pvaW4ocDIkZGF0YSAlPiUgZHBseXI6OnNlbGVjdChsYWJlbCwgeSkgJT4lIGRwbHlyOjpyZW5hbWUobGFiZWwueSA9IGxhYmVsKSwgYnk9ImxhYmVsLnkiKSAlPiUNCiAgcm93bmFtZXNfdG9fY29sdW1uKCJhc3NvYyIpICU+JQ0KICByZW5hbWVfYWxsKH5zdHJfcmVwbGFjZSgueCxwYXR0ZXJuPSJcXC4iLCByZXBsYWNlbWVudD0iXyIpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGVuZHNfd2l0aChjKCJfeCIsICJfeSIpKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvPWMoIi52YWx1ZSIsICJ0cmVlIiksIA0KICAgICAgICAgICAgICAgbmFtZXNfc2VwID0gIl8iDQogICAgICAgICAgICAgICkgICU+JQ0KICBtdXRhdGUoc2lnbmlmX3BhY28gPSByZXBsYWNlX25hKHNpZ25pZl9wYWNvLCAwKSwNCiAgICAgICAgIHNpZ25pZl9wYXJhID0gcmVwbGFjZV9uYShzaWduaWZfcGFyYSwgMCkpICU+JQ0KICBtdXRhdGUoc2lnbmlmID0gY2FzZV93aGVuKA0KICAgIHNpZ25pZl9wYWNvPT0wICYgc2lnbmlmX3BhcmE9PTAgIH4gIk5TIiwNCiAgICBzaWduaWZfcGFjbz09MCAmIHNpZ25pZl9wYXJhPT0xIH4gInBhcmEiLA0KICAgIHNpZ25pZl9wYWNvPT0xICYgc2lnbmlmX3BhcmE9PTAgfiAicGFjbyIsDQogICAgc2lnbmlmX3BhY289PTEgJiBzaWduaWZfcGFyYT09MSB+ICJib3RoIg0KICApKSAlPiUNCiAgbXV0YXRlKHRyZWUgPSB0cmVlICU+JSANCiAgICAgICAgICAgc3RyX3JlcGxhY2UoIngiLCAiaG9zdCIpJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlKCJ5IiwgIm1pY3JvYmUiKSkgJT4lDQogIGZpbHRlcighaXMubmEobGFiZWwpKSU+JSANCiAgZ3JvdXBfYnkodHJlZSkgJT4lDQogIG11dGF0ZSh5ID0geSAvIG1heCh5KSklPiUNCiAgbXV0YXRlKGxhYmVsID0gbGFiZWwgJT4lIHN0cl9yZXBsYWNlX2FsbCgiXyIsICIgIikpIA0KDQoNCmdnLnRhbmdsZSA8LSBnZ3Bsb3QodGFuZ2xlLCBhZXMoeD10cmVlLCB5PXksIGdyb3VwPWFzc29jLCBjb2xvdXI9YXMuZmFjdG9yKHNpZ25pZikpKSArDQogICAgZ2VvbV9saW5lKGFscGhhPTAuOCkgKyAgDQogIGdlb21fdGV4dChkYXRhID0gdGFuZ2xlICU+JSANCiAgICAgICAgICAgICAgZmlsdGVyKHRyZWU9PSJob3N0IiksDQogICAgICAgICAgICBhZXMobGFiZWw9bGFiZWwpLHN0YXQgPSAndW5pcXVlJywgaGp1c3Q9MSwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUpKw0KICBnZW9tX3RleHQoZGF0YSA9IHRhbmdsZSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcih0cmVlPT0ibWljcm9iZSIpLA0KICAgICAgICAgICAgYWVzKGxhYmVsPWxhYmVsKSxzdGF0ID0gJ3VuaXF1ZScsIGhqdXN0PTAsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXM9YygiTlMiPSJzdGVlbGJsdWUiLCAicGFjbyI9ImRhcmtvcmFuZ2UxIiwgInBhcmEiPSIjZGEyYjkxIiwgImJvdGgiPSIjOTFkYTJiIiksIG5hLnRyYW5zbGF0ZT1GQUxTRSkrDQogICAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQgPSBleHBhbnNpb24oYWRkPWMoMC44LDAuOCkpKSArIA0KICAgIHRoZW1lX3ZvaWQoKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAuMDA1LDAuMDA1KSkrDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgbGFicyhjb2xvdXI9IlNpZ25pZmljYW5jZToiKQ0KDQpnZy50cmlvemFfY2Fyc29uX3RhbmdsZSA8LSBwMSArIGdnLnRhbmdsZSArIChwMiArIHNjYWxlX3hfcmV2ZXJzZSgpKSANCmdnLnRyaW96YV9jYXJzb25fdGFuZ2xlDQoNCnBkZihmaWxlPSJmaWdzL3Bvd2VsbGlhX2NhcnNvbmVsbGFfdGFuZ2xlZ3JhbS5wZGYiLCAgd2lkdGggPSA4LCBoZWlnaHQgPSAxMSwgcGFwZXI9ImE0IikNCiAgcGxvdChnZy50cmlvemFfY2Fyc29uX3RhbmdsZSkNCnRyeShkZXYub2ZmKCksIHNpbGVudD1UUlVFKQ0KYGBgDQoNCiMjIFBvd2VsbGlhIH4gaG9zdHBsYW50DQoNCmBgYHtyIHRyaW96YSBob3N0cGxhbnQgcHN5bGxpZH0NCnBsYW50LnRyZWUgPC0gcmVhZC50cmVlKCJzYW1wbGVfZGF0YS9wbGFudF90cmVlLm53ayIpDQpwbGFudC50cmVlICA8LSBkcm9wLnRpcChwbGFudC50cmVlICwgIlNvcGhvcmFfbWljcm9waHlsbGFfLWtvd2hhaSIgKQ0KDQojUHJlcGFyZSBjby1vY2N1cmFuY2UgbWF0cml4DQpjb29jdXIgPC0gc2FtcGxlX2RhdGEocHMyKSAlPiUNCiAgYXNfdGliYmxlKCkgJT4lDQogIGZpbHRlcihwc3lsbGlkX2dlbnVzID09ICJQb3dlbGxpYSIpICU+JQ0KICBkcGx5cjo6c2VsZWN0KHBzeWxsaWRfc3BwLCBob3N0cGxhbnRfc3BwKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShwc3lsbGlkX3NwcCkpICU+JQ0KICB1bmlxdWUoKSAlPiUNCiAgbXV0YXRlKHBzeWxsaWRfc3BwID0gcHN5bGxpZF9zcHAgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKSwNCiAgICAgICAgIGhvc3RwbGFudF9zcHAgPSBob3N0cGxhbnRfc3BwICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIiksDQogICAgICAgICBwcmVzZW5jZSA9IDENCiAgICAgICAgICkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiaG9zdHBsYW50X3NwcCIsDQogICAgICAgICAgICAgIHZhbHVlc19mcm9tPSJwcmVzZW5jZSIsDQogICAgICAgICAgICAgIHZhbHVlc19maWxsID0gbGlzdChwcmVzZW5jZSA9IDApKSAlPiUNCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJwc3lsbGlkX3NwcCIpICU+JQ0KICB0KCkNCg0KIyBIIGNvcGhlbmV0aWMgZGlzdGFuY2UNCmhfdHJlZSA8LSBwbGFudC50cmVlDQpoX3RyZWUkdGlwLmxhYmVsIDwtIGhfdHJlZSR0aXAubGFiZWwgJT4lDQogICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiIHwtIiwgIl8iKQ0KaF90cmVlIDwtIGRyb3AudGlwKGhfdHJlZSwgc2V0ZGlmZihoX3RyZWUkdGlwLmxhYmVsLCByb3duYW1lcyhjb29jdXIpKSkNCmhfZGlzdCA8LSBzcXJ0KGNvcGhlbmV0aWMoaF90cmVlKSkNCg0KIyBQIGNvcGhlbmV0aWMgZGlzdGFuY2UNCnNfdHJlZSA8LSBwcnVuZWQudHJlZQ0Kc190cmVlJHRpcC5sYWJlbCA8LSBzX3RyZWUkdGlwLmxhYmVsICU+JQ0KICAgICAgICAgICBzdHJfcmVwbGFjZV9hbGwoIiB8LSIsICJfIikNCnNfdHJlZSA8LSBkcm9wLnRpcChzX3RyZWUsIHNldGRpZmYoc190cmVlJHRpcC5sYWJlbCwgY29sbmFtZXMoY29vY3VyKSkpDQpzX2Rpc3QgPC0gc3FydChjb3BoZW5ldGljKHNfdHJlZSkpDQoNCmNvb2N1ciA8LSBjb29jdXJbaF90cmVlJHRpcC5sYWJlbCwgc190cmVlJHRpcC5sYWJlbF0NCiMgcHJlcGFyZSBwYWNvIGRhdGENCkQgPC0gcHJlcGFyZV9wYWNvX2RhdGEoSD1oX2Rpc3QsIFA9c19kaXN0LCBIUD1jb29jdXIpDQojIEFkZCBwY29yZA0KRCA8LSBhZGRfcGNvb3JkKEQsIGNvcnJlY3Rpb249J25vbmUnKSAgDQoNCnBfaG9zdCA8LSBnZ3Bsb3QoRCRIX1BDb1ssYygnQXhpcy4xJywgJ0F4aXMuMicpXSAlPiUgYXMuZGF0YS5mcmFtZSwgYWVzKEF4aXMuMSwgQXhpcy4yKSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgdGhlbWVfYncoKSArDQogIGdndGl0bGUoIkggUENBIikNCg0KcF9wYXJhIDwtIGdncGxvdChEJFBfUENvWyxjKCdBeGlzLjEnLCAnQXhpcy4yJyldICU+JSBhcy5kYXRhLmZyYW1lLCBhZXMoQXhpcy4xLCBBeGlzLjIpKSAgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgdGhlbWVfYncoKSsNCiAgZ2d0aXRsZSgiUCBQQ0EiKQ0KICAgIA0KcGxvdChwX2hvc3QgKyBwX3BhcmEpDQoNCiMgcnVuIHBhY28NCnBhY29fcnVuIDwtIFBBQ28oRCwgbnBlcm09OTk5LCBzZWVkPTkwOSwgbWV0aG9kPSdxdWFzaXN3YXAnLCBzeW1tZXRyaWM9RkFMU0UpICNTeW1ldHJpYyAtIGlzIG9uZSBtZWFudCB0byB0cmFjayB0aGUgZXZvbHV0aW9uIG9mIGFub3RoZXI/DQoNCiNwcmludCBvdmVyYWxsIHNpZ25pZmljYW5jZQ0KcHJpbnQocGFjb19ydW4kZ29mKQ0KDQojIFByb2NydXN0ZXMgZGlhZ25vc3RpYyBwbG90cw0KcGxvdChwYWNvX3J1biRwcm9jKQ0KcGxvdChwYWNvX3J1biRwcm9jLCBraW5kPTIpDQoNCiMgR2V0IGludGVyYWN0aW9uLXNwZWNpZmljIGNvcGh5bG9nZW5ldGljIGNvbnRyaWJ1dGlvbnMgdXNpbmcgamFja25pZmluZw0KcGFjb19ydW4gPC0gcGFjb19saW5rcyhwYWNvX3J1bikNCmxpbmtzIDwtIGRhdGEuZnJhbWUoDQogIGpvaW50PW5hbWVzKHBhY29fcnVuJGphY2trbmlmZSksDQogIHZhbHVlcz11bm5hbWUocGFjb19ydW4kamFja2tuaWZlKSMsDQogICN1cHBlcj11bm5hbWUocGFjb19ydW4kamFja2tuaWZlJHVwcGVyKQ0KICApICU+JQ0KIG11dGF0ZShPVFUgPSBqb2ludCkgJT4lDQogc2VwYXJhdGUoT1RVLCBpbnRvPWMoImhvc3RwbGFudF9zcHAiLCAicHN5bGxpZF9zcHAiKSwgc2VwPSItIiwgZXh0cmE9Im1lcmdlIikgJT4lDQogIG11dGF0ZShzaWduaWYgPSBjYXNlX3doZW4oDQogICAgdmFsdWVzIDwgbWVhbiguJHZhbHVlcykgfiAxLA0KICAgIHZhbHVlcyA+IG1lYW4oLiR2YWx1ZXMpIH4gMA0KICApKQ0KDQojIFBsb3QgbGlua3MNCmxpbmtzICU+JQ0KICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gam9pbnQpICU+JQ0KICBtdXRhdGUobGFiZWwgPSBhcy5mYWN0b3IobGFiZWwpLA0KICAgICAgICAgbGFiZWw9ZmN0X3Jlb3JkZXIobGFiZWwsIHZhbHVlcywgc3VtKSklPiUNCiAgYXJyYW5nZSgtdmFsdWVzKSAlPiUNCiAgZ2dwbG90KGFlcyh4PWxhYmVsLCB5PXZhbHVlcywgY29sb3VyPWFzLmZhY3RvcihzaWduaWYpKSkgKw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgI2dlb21fZXJyb3JiYXIoYWVzKHltaW49dmFsdWVzLCB5bWF4PXVwcGVyKSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuKGxpbmtzJHZhbHVlcykpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJzdGVlbGJsdWUiLCAiZGFya29yYW5nZTEiKSkgKw0KICB0aGVtZV9jbGFzc2ljKCkrIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQoNCmRpci5jcmVhdGUoIm91dHB1dC9jb3BoeWxvZ2VueS90cmlvemFfaG9zdHBsYW50IikNCndyaXRlX2NzdihsaW5rcywgIm91dHB1dC9jb3BoeWxvZ2VueS90cmlvemFfaG9zdHBsYW50L3BzeWxsaWRfaG9zdHBsYW50X2xpbmtzLmNzdiIpDQp3cml0ZV9jc3YobGlua3MgJT4lIA0KICAgIGdyb3VwX2J5KHBzeWxsaWRfc3BwKSAlPiUNCiAgICBzdW1tYXJpc2UodmFsdWVzID0gbWVhbih2YWx1ZXMpKSwgIm91dHB1dC9jb3BoeWxvZ2VueS90cmlvemFfaG9zdHBsYW50L3BzeWxsaWRfd2VpZ2h0cy5jc3YiKQ0Kd3JpdGVfY3N2KGxpbmtzICU+JSANCiAgICBncm91cF9ieShob3N0cGxhbnRfc3BwKSAlPiUNCiAgICBzdW1tYXJpc2UodmFsdWVzID0gbWVhbih2YWx1ZXMpKSwgIm91dHB1dC9jb3BoeWxvZ2VueS90cmlvemFfaG9zdHBsYW50L2hvc3RwbGFudF93ZWlnaHRzLmNzdiIpDQoNCiNHZXQgb2JzZXJ2ZWQgcmVzaWR1YWxzIG9mIFByb2NydXN0ZWFuIHN1cGVyaW1wb3NpdGlvbiANCnBhY29fcmVzaWR1YWxzIDwtIHJlc2lkdWFsc19wYWNvKHBhY29fcnVuJHByb2MsIHR5cGUgPSAiaW50ZXJhY3Rpb24iKQ0KDQojIFZpc3VhbGlzZSByZXNpZHVhbHMNCnJlcyA8LSBkYXRhLmZyYW1lKE9UVT1uYW1lcyhwYWNvX3Jlc2lkdWFscyksIHZhbHVlcz11bm5hbWUocGFjb19yZXNpZHVhbHMpKSAlPiUNCiAgc2VwYXJhdGUoT1RVLCBpbnRvPWMoImhvc3RwbGFudF9zcHAiLCAicHN5bGxpZF9zcHAiKSwgc2VwPSItIiwgZXh0cmE9Im1lcmdlIikNCg0KZ2dwbG90KHJlcywgYWVzKHg9dmFsdWVzKSkrDQogIGdlb21fZGVuc2l0eShmaWxsPSdncmV5NzAnKSsNCiAgdGhlbWVfYncoKSsNCiAgeGxhYignUHJvY3J1c3RlcyByZXNpZHVhbHMnKSsNCiAgeWxhYignRnJlcXVlbmN5JykNCg0KIyBQYXJhZml0IHJ1bg0KUEZfcnVuIDwtIHBhcmFmaXQoaF9kaXN0LCBzX2Rpc3QsIHQoY29vY3VyKSwgbnBlcm09OTk5LCB0ZXN0LmxpbmtzPVRSVUUsIHNpbGVudD1UUlVFKQ0KUEZfcnVuJFBhcmFGaXRHbG9iYWwNClBGX3J1biRwLmdsb2JhbA0KDQoNClBGX2xpbmtzIDwtIGFzLmRhdGEuZnJhbWUoUEZfcnVuJGxpbmsudGFibGUpICAlPiUNCiAgICAgIGxlZnRfam9pbihlbmZyYW1lKG5hbWVzKFBGX3J1biRwYXJhLnBlci5ob3N0KSwgbmFtZSA9ICJIb3N0IiwgdmFsdWU9Imhvc3RwbGFudF9zcHAiKSAlPiUNCiAgICAgICAgICAgICAgICAgIG11dGF0ZShIb3N0ID0gYXMubnVtZXJpYyhIb3N0KSkpICU+JQ0KICAgICAgbGVmdF9qb2luKGVuZnJhbWUobmFtZXMoUEZfcnVuJGhvc3QucGVyLnBhcmEpLCBuYW1lID0gIlBhcmFzaXRlIiwgdmFsdWU9InBzeWxsaWRfc3BwIikgJT4lDQogICAgICAgICAgICAgICAgICBtdXRhdGUoUGFyYXNpdGUgPSBhcy5udW1lcmljKFBhcmFzaXRlKSkpJT4lDQogIG11dGF0ZShzaWduaWYgPSBjYXNlX3doZW4oDQogICAgcC5GMSA8IDAuMDUgfiAxLA0KICAgIHAuRjEgPiAwLjA1IH4gMA0KICApKSANCg0KIyBDb3BoeWxvcGxvdA0KY29vY3VyLmx1dCA8LSB3aGljaChjb29jdXIgPT0xLCBhcnIuaW5kPVRSVUUpDQphc3NvYyA8LSBjYmluZChyb3duYW1lcyhjb29jdXIpW2Nvb2N1ci5sdXRbLDFdXSwgY29sbmFtZXMoY29vY3VyKVtjb29jdXIubHV0WywyXV0pDQoNCiMgUm90YXRlIHRoZSBub2RlcyB1c2luZyBwaHl0b29scw0Kb2JqIDwtIGNvcGh5bG8odHIxPWhfdHJlZSwgdHIyPXNfdHJlZSwgYXNzb2M9YXNzb2MsIHJvdGF0ZT1UUlVFKSANCg0KIyBFeHRyYWN0IHRoZSBnb29kcyBmb3IgZ2d0cmVlDQojIHBsYW50IHRyZWUNCnRyZWUxIDwtIG9ialtbInRyZWVzIl1dW1sxXV0NCg0KcDEgPC0gZ2d0cmVlKHRyZWUxLCBsYWRkZXJpemU9RkFMU0UsIGFlcyhjb2xvdXI9bGlua3MpKQ0Kd2VpZ2h0c19wMSA8LSBwMSRkYXRhICU+JQ0KICBsZWZ0X2pvaW4obGlua3MgJT4lIA0KICAgIGdyb3VwX2J5KGhvc3RwbGFudF9zcHApICU+JQ0KICAgIHN1bW1hcmlzZShQQV92YWx1ZXMgPSBtZWFuKHNpZ25pZikpICU+JQ0KICAgIGRwbHlyOjpyZW5hbWUobGFiZWwgPSBob3N0cGxhbnRfc3BwKQ0KICAgICklPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICBncm91cF9ieShob3N0cGxhbnRfc3BwKSAlPiUNCiAgICBzdW1tYXJpc2UoUEZfdmFsdWVzID0gbWVhbihzaWduaWYpKSAlPiUNCiAgICBkcGx5cjo6cmVuYW1lKGxhYmVsID0gaG9zdHBsYW50X3NwcCkNCiAgICApICU+JQ0KICBtdXRhdGUodmFsdWVzID0gUEFfdmFsdWVzICsgUEZfdmFsdWVzKQ0KDQojIyBHZXQgdmFsdWVzIGZvciBoaWdoZXIgbm9kZXMNCndlaWdodHNfcDEgPC0gd2VpZ2h0c19wMSAlPiUNCiAgbGVmdF9qb2luKGRhdGEuZnJhbWUobm9kZT13ZWlnaHRzX3AxJG5vZGUsIGxpbmtzID0gd2VpZ2h0c19wMSRub2RlICU+JSBwdXJycjo6bWFwX2RibChhdmVyYWdlX2Rlc2NlbmRhbnRzLCB0cmVlPXRyZWUxLCBkZj13ZWlnaHRzX3AxKSkpDQpwMSA8LSBwMSAlPCslIHdlaWdodHNfcDEgKyBnZW9tX3RpcHBvaW50KGFlcyhjb2xvdXI9bGlua3MpKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0ic3RlZWxibHVlIiwgaGlnaD0iZGFya29yYW5nZTEiKSAgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgcHN5bGxpZF90cmVlDQp0cmVlMiA8LSBvYmpbWyJ0cmVlcyJdXVtbMl1dDQpzX3RyZWUgPC0gZHJvcC50aXAodHJlZTIsIHNldGRpZmYodHJlZTIkdGlwLmxhYmVsLCBvYmokYXNzb2NbLDJdKSkNCg0KcDIgPC0gZ2d0cmVlKHRyZWUyICwgbGFkZGVyaXplPVRSVUUsIGFlcyhjb2xvdXI9bGlua3MpKSANCndlaWdodHNfcDIgPC0gcDIkZGF0YSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICBncm91cF9ieShwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKFBBX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IHBzeWxsaWRfc3BwKQ0KICAgICklPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICBncm91cF9ieShwc3lsbGlkX3NwcCkgJT4lDQogICAgc3VtbWFyaXNlKFBGX3ZhbHVlcyA9IG1lYW4oc2lnbmlmKSkgJT4lDQogICAgZHBseXI6OnJlbmFtZShsYWJlbCA9IHBzeWxsaWRfc3BwKQ0KICAgICkgJT4lDQogIG11dGF0ZSh2YWx1ZXMgPSBQQV92YWx1ZXMgKyBQRl92YWx1ZXMpDQoNCiNTY2FsZSB0aGUgYXRtZXRvY3Jhbml1bSBhbmQgcm9vdCBub2RlcyB0byBiZSBzaG9ydGVyDQpwMiRkYXRhW3AyJGRhdGEkbm9kZSAlaW4lIGF0bWV0b19ub2RlLCAieCJdIDwtIG1heChwMiRkYXRhJHgpDQpwMiRkYXRhW3AyJGRhdGEkbm9kZSAlaW4lIHJvb3Rfbm9kZSwgIngiXSA8LSAwLjIgI3Jvb3QNCg0KcDIkZGF0YSRub2RlW3AyJGRhdGEkbm9kZV0NCg0Kd2VpZ2h0c19wMiA8LSB3ZWlnaHRzX3AyICU+JQ0KICBsZWZ0X2pvaW4oZGF0YS5mcmFtZShub2RlPXdlaWdodHNfcDIkbm9kZSwgbGlua3MgPSB3ZWlnaHRzX3AyJG5vZGUgJT4lIHB1cnJyOjptYXBfZGJsKGF2ZXJhZ2VfZGVzY2VuZGFudHMsIHRyZWU9dHJlZTIsIGRmPXdlaWdodHNfcDIpKSkNCg0KcDIgPC0gcDIgJTwrJSB3ZWlnaHRzX3AyICArIGdlb21fdGlwcG9pbnQoYWVzKGNvbG91cj1saW5rcykpICsgDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdz0ic3RlZWxibHVlIiwgaGlnaD0iZGFya29yYW5nZTEiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KIyBUYW5nbGVncmFtIA0KdGFuZ2xlIDwtIG9iaiRhc3NvYyAlPiUNCiAgYXNfZGF0YV9mcmFtZSgpICU+JQ0KICBtYWdyaXR0cjo6c2V0X2NvbG5hbWVzKGMoImxhYmVsLngiLCAibGFiZWwueSIpKSAlPiUNCiAgbGVmdF9qb2luKGxpbmtzICU+JSANCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChsYWJlbC54ID0gaG9zdHBsYW50X3NwcCwgbGFiZWwueSA9IHBzeWxsaWRfc3BwLCBzaWduaWZfcGFjbz1zaWduaWYpKSAlPiUNCiAgbGVmdF9qb2luKFBGX2xpbmtzICU+JSANCiAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChsYWJlbC54ID0gaG9zdHBsYW50X3NwcCwgbGFiZWwueSA9IHBzeWxsaWRfc3BwLCBzaWduaWZfcGFyYT1zaWduaWYpKSAlPiUNCiAgbGVmdF9qb2luKHAxJGRhdGEgJT4lIGRwbHlyOjpzZWxlY3QobGFiZWwsIHkpICU+JSBkcGx5cjo6cmVuYW1lKGxhYmVsLnggPSBsYWJlbCksIGJ5PSJsYWJlbC54IikgJT4lDQogIGxlZnRfam9pbihwMiRkYXRhICU+JSBkcGx5cjo6c2VsZWN0KGxhYmVsLCB5KSAlPiUgZHBseXI6OnJlbmFtZShsYWJlbC55ID0gbGFiZWwpLCBieT0ibGFiZWwueSIpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oImFzc29jIikgJT4lDQogIHJlbmFtZV9hbGwofnN0cl9yZXBsYWNlKC54LHBhdHRlcm49IlxcLiIsIHJlcGxhY2VtZW50PSJfIikpICU+JQ0KICBwaXZvdF9sb25nZXIoZW5kc193aXRoKGMoIl94IiwgIl95IikpLA0KICAgICAgICAgICAgICAgbmFtZXNfdG89YygiLnZhbHVlIiwgInRyZWUiKSwgDQogICAgICAgICAgICAgICBuYW1lc19zZXAgPSAiXyINCiAgICAgICAgICAgICAgKSAgJT4lDQogIG11dGF0ZShzaWduaWZfcGFjbyA9IHJlcGxhY2VfbmEoc2lnbmlmX3BhY28sIDApLA0KICAgICAgICAgc2lnbmlmX3BhcmEgPSByZXBsYWNlX25hKHNpZ25pZl9wYXJhLCAwKSkgJT4lDQogIG11dGF0ZShzaWduaWYgPSBjYXNlX3doZW4oDQogICAgc2lnbmlmX3BhY289PTAgJiBzaWduaWZfcGFyYT09MCAgfiAiTlMiLA0KICAgIHNpZ25pZl9wYWNvPT0wICYgc2lnbmlmX3BhcmE9PTEgfiAicGFyYSIsDQogICAgc2lnbmlmX3BhY289PTEgJiBzaWduaWZfcGFyYT09MCB+ICJwYWNvIiwNCiAgICBzaWduaWZfcGFjbz09MSAmIHNpZ25pZl9wYXJhPT0xIH4gImJvdGgiDQogICkpICU+JQ0KICBtdXRhdGUodHJlZSA9IHRyZWUgJT4lIA0KICAgICAgICAgICBzdHJfcmVwbGFjZSgieCIsICJob3N0IiklPiUNCiAgICAgICAgICAgc3RyX3JlcGxhY2UoInkiLCAibWljcm9iZSIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShsYWJlbCkpJT4lIA0KICBncm91cF9ieSh0cmVlKSAlPiUNCiAgbXV0YXRlKHkgPSB5IC8gbWF4KHkpKSU+JQ0KICBtdXRhdGUobGFiZWwgPSBsYWJlbCAlPiUgc3RyX3JlcGxhY2VfYWxsKCJfIiwgIiAiKSkgDQoNCmdnLnRhbmdsZSA8LSBnZ3Bsb3QodGFuZ2xlLCBhZXMoeD1mYWN0b3IodHJlZSwgbGV2ZWxzPWMoIm1pY3JvYmUiLCAiaG9zdCIpKSwgeT15LCBncm91cD1hc3NvYywgY29sb3VyPWFzLmZhY3RvcihzaWduaWYpKSkgKw0KICAgIGdlb21fbGluZShhbHBoYT0wLjgpICsgIA0KICBnZW9tX3RleHQoZGF0YSA9IHRhbmdsZSAlPiUgDQogICAgICAgICAgICAgIGZpbHRlcih0cmVlPT0iaG9zdCIpLA0KICAgICAgICAgICAgYWVzKGxhYmVsPWxhYmVsKSxzdGF0ID0gJ3VuaXF1ZScsIGhqdXN0PTAsIGNoZWNrX292ZXJsYXAgPSBUUlVFKSsNCiAgZ2VvbV90ZXh0KGRhdGEgPSB0YW5nbGUgJT4lIA0KICAgICAgICAgICAgICBmaWx0ZXIodHJlZT09Im1pY3JvYmUiKSwNCiAgICAgICAgICAgIGFlcyhsYWJlbD1sYWJlbCksc3RhdCA9ICd1bmlxdWUnLCBoanVzdD0xLCBjaGVja19vdmVybGFwID0gVFJVRSkrDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWMoIk5TIj0ic3RlZWxibHVlIiwgInBhY28iPSJkYXJrb3JhbmdlMSIsICJwYXJhIj0iI2RhMmI5MSIsICJib3RoIj0iIzkxZGEyYiIpLCBuYS50cmFuc2xhdGU9RkFMU0UpKw0KICAgIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gZXhwYW5zaW9uKGFkZD1jKDAuOCwwLjgpKSkgKyANCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kPWMoMC4wMDUsMC4wMDUpKSsNCiAgICB0aGVtZV92b2lkKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIGxhYnMoY29sb3VyPSJTaWduaWZpY2FuY2UiKQ0KZ2cucG93ZWxsaWFfdGFuZ2xlIDwtIHAyICsgZ2cudGFuZ2xlICsgKHAxICsgc2NhbGVfeF9yZXZlcnNlKCkpIA0KZ2cucG93ZWxsaWFfdGFuZ2xlDQoNCnBkZihmaWxlPSJmaWdzL3Bvd2VsbGlhX3BsYW50X3RhbmdsZWdyYW0ucGRmIiwgIHdpZHRoID0gOCwgaGVpZ2h0ID0gMTEsIHBhcGVyPSJhNCIpDQogIHBsb3QoZ2cucG93ZWxsaWFfdGFuZ2xlKQ0KdHJ5KGRldi5vZmYoKSwgc2lsZW50PVRSVUUpDQpgYGANCg0KDQojIE1hcCBvZiBjb2xsZWNpdG9uIGxvY2F0aW9ucw0KDQpgYGB7ciBDb2xsZWN0aW9uIG1hcH0NCiNQbG90IG9uIG1hcCB0byBjb25maXJtIHBvaW50cw0KZW52RGF0YSA8LSBzYW1wbGVfZGF0YShwczIpICU+JQ0KICBhc19kYXRhX2ZyYW1lKCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoU2FtcGxlX05hbWUscHN5bGxpZF9zcHAsIGxhdCwgbG9uZykgJT4lDQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJTYW1wbGVfTmFtZSIpICU+JQ0KICBkcm9wX25hKCkNCg0KeGxpbSA8LSBjKDE2NSwxODApDQp5bGltIDwtIGMoLTUwLC0zMCkNCg0KbnogPC0gbWFwKGRhdGFiYXNlPSAid29ybGQiLCByZWdpb249ICJOZXcgWmVhbGFuZCIsIGZpbGw9VFJVRSwgeGxpbT14bGltLA0KICB5bGltPXlsaW0pICMsIG1hcj1jKDAsMCwwLDApDQoNCnAxIDwtIGdndHJlZShwcnVuZWQudHJlZSwgbGFkZGVyaXplPVRSVUUpDQptYXBfZGF0YSA8LSBwMSRkYXRhJT4lDQogIGxlZnRfam9pbihlbnZEYXRhICU+JSBkcGx5cjo6bXV0YXRlKGxhYmVsID1wc3lsbGlkX3NwcCApKQ0KDQoNCmNvbCA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoMTIsICJQYWlyZWQiKSkobGVuZ3RoKHVuaXF1ZShtYXBfZGF0YSRwc3lsbGlkX3NwcCkpKQ0KDQpnZy5uem1hcCA8LSBnZ3Bsb3QoZm9ydGlmeShueiksIGFlcyh5PWxhdCwgeD1sb25nLCBncm91cD1ncm91cCkpICsgDQogIGdlb21fcG9seWdvbihmaWxsPSJsaWdodGdyZXkiLCBjb2xvcj0iIzdmN2Y3ZiIpICsNCiAgZ2VvbV9wb2ludChkYXRhPW1hcF9kYXRhLCBhZXMoeD1sb25nLCB5PWxhdCwgY29sb3I9cHN5bGxpZF9zcHApLCBhbHBoYT0uNSwgc2l6ZT0zLCBpbmhlcml0LmFlcyA9IEZBTFNFKSArIA0KICAgIGdlb21fc2VnbWVudChkYXRhPW1hcF9kYXRhICU+JQ0KICBtdXRhdGUoeSA9IHNjYWxlczo6cmVzY2FsZSh5LCB0byA9IHlsaW0gKSksIGFlcyh4PW1pbih4bGltKSwgeT15LCB4ZW5kPSBsb25nLCB5ZW5kPWxhdCwgY29sb3I9cHN5bGxpZF9zcHApLCBhbHBoYT0uMiwgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKw0KICAgIHRoZW1lX2NsYXNzaWMoKSArDQogICAgY29vcmRfZml4ZWQoeWxpbSA9eWxpbSwgeGxpbT14bGltKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKyANCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jb2wpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMocG9zaXRpb24gPSAicmlnaHQiKSArDQogIHhsYWIoIkxvbmdpdHVkZSIpICsgDQogIHlsYWIoIkxhdHRpdHVkZSIpDQoNCmdnLm56bWFwIA0KDQojcHJpbnQoZ2cubnptYXAsIHZwID0gdmlld3BvcnQod2lkdGggPSAuNywgaGVpZ2h0ID0gLjcsIGFuZ2xlID0gMzUpKQ0KcDEgPC0gcDEgJTwrJSBtYXBfZGF0YSArDQogIGdlb21fdGlwbGFiKGFsaWduPVRSVUUsIGFlcyhjb2xvcj1wc3lsbGlkX3NwcCksIG9mZnNldD0wLjEsIGhqdXN0PTEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZD1jKDAsIDApKSsgDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPWNvbCkgDQoNCmdnLnBoeWxvbWFwIDwtIHAxICsgZ2cubnptYXANCg0KcGRmKGZpbGU9ImZpZ3MvcGh5bG9tYXAucGRmIiwgIHdpZHRoID0gMTUsIGhlaWdodCA9IDE1LCBwYXBlcj0iYTRyIikNCiAgcGxvdChnZy5waHlsb21hcCkNCnRyeShkZXYub2ZmKCksIHNpbGVudD1UUlVFKQ0KYGBgDQoNCiMgU2Vzc2lvbmluZm8NCg0KYGBge3Igc2Vzc2lvbmluZm8sIGV2YWw9VFJVRX0NCmRldnRvb2xzOjpzZXNzaW9uX2luZm8oKQ0KYGBgDQo=
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/