Prepare analysis workflow
Set parameters
knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file(),
fig.width=15,
digit=5,
scipen=8)
options(readr.show_progress = FALSE,
digits=5,
scipen=8,
future.globals.maxSize = +Inf)
Set filepaths and parameters
project_dir <- rprojroot::find_rstudio_root_file()
if(is.null(project_dir)){
project_dir <- getwd()
warning(sprintf("No rstudio project root file found.
Setting project directory to current workflow.Rmd file location: %s.
Override if needed.",
project_dir))
}
message(sprintf("Project directory: %s",
project_dir))
Project directory: /home/rfarouni/Documents/index_hopping
Load libraries
library(rhdf5)
#library(DropletUtils) # install but not load
library(tidyverse)
library(matrixStats)
library(broom)
library(furrr)
library(tictoc)
library(data.table)
library(cowplot)
plan(multiprocess)
Load functions
code_dir <- file.path(project_dir, "code")
source(file.path(code_dir, "1_create_joined_counts_table.R"))
source(file.path(code_dir, "2_create_counts_by_outcome_table.R"))
source(file.path(code_dir, "3_estimate_sample_index_hopping_rate.R"))
source(file.path(code_dir, "4_compute_summary_statistics.R"))
source(file.path(code_dir, "5_reassign_hopped_reads.R"))
source(file.path(code_dir, "6_purge_phantom_molecules.R"))
source(file.path(code_dir, "7_call_cells.R"))
source(file.path(code_dir, "8_summarize_purge.R"))
source(file.path(code_dir, "9_plotting_functions.R"))
Load data
validation_output_dir <- file.path(project_dir, "data", "hiseq4000_validation")
data <- read_tsv(file.path(validation_output_dir,
"hiseq4000_inner_joined_with_labels.txt"))
Parsed with column specification:
cols(
cell = [31mcol_character()[39m,
gene = [32mcol_double()[39m,
umi = [32mcol_double()[39m,
s1_nonplexed = [32mcol_double()[39m,
s2_nonplexed = [32mcol_double()[39m,
s1_plexed = [32mcol_double()[39m,
s2_plexed = [32mcol_double()[39m,
outcome = [31mcol_character()[39m,
label = [31mcol_character()[39m
)
data
Compute Index hopping rate
Estimates conditional on duplication level and label
summary_counts <-
data %>%
mutate(r=as.integer(rowSums(.[c(6,7)]))) %>%
arrange(r) %>%
group_by(r, label) %>%
summarize_at(vars(matches("^s1_pl|s2_pl")),
list(~ sum(.))) %>%
mutate(s1_hopped=if_else(label %in% c("0,f", "r,f"), s2_plexed,0),
s2_hopped=if_else(label %in% c("f,0", "f,r"), s1_plexed,0),
s1_nonhopped=if_else(label %in% c("r,0", "r,f"), s1_plexed,0),
s2_nonhopped=if_else(label %in% c("0,r", "f,r"), s2_plexed,0)) %>%
select(-s1_plexed,- s2_plexed)
summary_counts
Estimates conditional on duplication level
summary_counts_conditional <-
summary_counts %>%
group_by(r) %>%
summarize_at(vars(matches("^s")),
list(~ sum(.)))%>%
mutate(SIHR_12 = s1_hopped/(s1_hopped+s1_nonhopped),
SIHR_21 = s2_hopped/(s2_hopped+s2_nonhopped),
frac_s1= (s1_hopped+s1_nonhopped)/ (s1_hopped+s1_nonhopped+s2_hopped+s2_nonhopped))
summary_counts_conditional
Marginal estimates
summary_counts_marginal <-
summary_counts %>%
ungroup() %>%
summarize_at(vars(matches("^s")),
list(~ sum(.)))%>%
mutate(SIHR_12 = s1_hopped/(s1_hopped+s1_nonhopped),
SIHR_21 = s2_hopped/(s2_hopped+s2_nonhopped),
SIHR=1-(s1_nonhopped+s2_nonhopped)/(s1_hopped+s1_nonhopped +s2_hopped+s2_nonhopped),
frac_s1= (s1_hopped+s1_nonhopped)/ (s1_hopped+s1_nonhopped+s2_hopped+s2_nonhopped))
summary_counts_marginal
p1 <-
ggplot(summary_counts_conditional) +
geom_line(aes(x = r,
y = SIHR_12*100,
colour="SIHR_12"))+
geom_line(aes(x = r,
y = SIHR_21*100,
colour="SIHR_21")) +
geom_line(aes(x = r,
y = frac_s1,
colour="frac_s1")) +
geom_hline(yintercept = unlist(summary_counts_marginal[5:7])* c(100, 100, 100),
linetype="dashed") +
xlim(0,90) +
ylim(0,1)
p1

NA
Molecules
summary_mol_counts <-
data %>%
mutate(r=as.integer(rowSums(.[c(6,7)]))) %>%
arrange(r) %>%
mutate_at(vars(matches("^s")),
list(~ as.integer(.!=0))) %>%
group_by(r, label) %>%
summarize_at(vars(matches("^s1_pl|^s2_pl")),
list(~ sum(.))) %>%
mutate(s1_phantom=if_else(label %in% c("0,f", "r,f"), s2_plexed,0L),
s2_phantom=if_else(label %in% c("f,0", "f,r"), s1_plexed,0L),
s1_real=if_else(label %in% c("r,0", "r,f"), s1_plexed,0L),
s2_real=if_else(label %in% c("0,r", "f,r"), s2_plexed,0L)) %>%
select(-s1_plexed,- s2_plexed)
summary_mol_counts
summary_mol_counts_marginal <-
summary_mol_counts%>%
ungroup() %>%
summarize_at(vars(matches("^s")),
list(~ sum(.))) %>%
mutate(ppm_12 = s1_phantom/(s1_phantom+s1_real),# prop hopped phantom molec
ppm_21 = s2_phantom/(s2_phantom+s2_real),
ppm_1 = s2_phantom/(s2_phantom+s1_real), # prop phantom molec
ppm_2 = s1_phantom/(s1_phantom+s2_real),
ppm=(s1_phantom+s2_phantom)/(s1_phantom+s1_real+s2_phantom+s2_real),
frac_mol_s1= (s1_phantom+s1_real)/ (s1_phantom+s1_real+s2_phantom+s2_real))
summary_mol_counts_marginal
summary_mol_counts_conditional <-
summary_mol_counts%>%
group_by(r) %>%
summarize_at(vars(matches("^s")),
list(~ sum(.))) %>%
mutate(ppm_12 = s1_phantom/(s1_phantom+s1_real),
ppm_21 = s2_phantom/(s2_phantom+s2_real),
ppm=(s1_phantom+s2_phantom)/(s1_phantom+s1_real+s2_phantom+s2_real),
frac_mol_s1= (s1_phantom+s1_real)/ (s1_phantom+s1_real+s2_phantom+s2_real))
summary_mol_counts_conditional
p4 <- ggplot(summary_mol_counts_conditional) +
geom_line(aes(x = r,
y = ppm_12,
colour="ppm_12"))+
geom_line(aes(x = r,
y = ppm_21,
colour=" ppm_21")) +
#geom_hline(yintercept = unlist(summary_mol_counts_marginal[5:7]), linetype="dashed") +
xlim(0,300)
p4

#ggsave("phantom_molecules_validation.pdf", p4, width =8, height = 5)
Examine extent of contamination in cells
summary_mol_counts_cell<-
data %>%
filter(label!="NA") %>%
mutate_at(vars(matches("^s")),
list(~ as.integer(.!=0))) %>%
group_by(cell, label) %>%
summarize_at(vars(matches("^s1_pl|^s2_pl")),
list(~ sum(.))) %>%
mutate(s1_phantom=if_else(label %in% c("f,0", "f,r"), s1_plexed,0L),
s2_phantom=if_else(label %in% c("0,f", "r,f"), s2_plexed,0L),
s1_real=if_else(label %in% c("r,0", "r,f"), s1_plexed,0L),
s2_real=if_else(label %in% c("0,r", "f,r"), s2_plexed,0L)) %>%
select(-s1_plexed,- s2_plexed) %>%
group_by(cell) %>%
summarize_at(vars(matches("^s")),
list(~ sum(.))) %>%
mutate_at(vars(matches("^s")),
list(nonempty= ~ as.integer(.!=0))) %>%
unite(label,matches("nonempty"), sep=",") %>%
mutate(cell_status=
case_when(label %in% c("0,0,0,1","0,0,1,0") ~ "real",
label %in% c("0,0,1,1") ~ "real",
label %in% c("1,0,0,0","0,1,0,0", "1,1,0,0") ~ "phantom",
label %in% c("1,0,0,1","0,1,1,0") ~ "phantom",
TRUE ~ "contaminated")) %>%
mutate(s1_total =(s1_phantom+s1_real),
s2_total =(s2_phantom+s2_real),
s1_ppm = s1_phantom/(s1_total),
s2_ppm = s2_phantom/(s2_total))
summary_mol_counts_cell
cell_status_tally <-
summary_mol_counts_cell %>%
group_by(cell_status,label) %>%
tally(sort=TRUE)
cell_status_tally
n_affected_cells <-
cell_status_tally %>%
ungroup() %>%
filter(cell_status!="real") %>%
summarise(n=sum(n)) %>%
pull(n)
n_affected_cells <- n_affected_cells + 1502 +6
n_affected_cells
[1] 64579
n_total_cells <-
summary_mol_counts_cell %>%
mutate_at(vars(matches("_total")),
list(~ as.integer(.!=0))) %>%
ungroup() %>%
summarise_at(vars(matches("_total")), list(~sum(.))) %>%
mutate(cells_total= s1_total+s2_total)%>%
pull(cells_total)
n_total_cells
[1] 322321
Proportion of affected cells
p_affected_cells <- n_affected_cells/n_total_cells
p_affected_cells
[1] 0.20036
For each cell-barcode, plot the number of phantom molecules against the number of total molecules associated with it.
Sample 1 plot
p2 <- ggplot(summary_mol_counts_cell %>%
filter(cell_status!="real" & s1_phantom >0 )) +
geom_point(aes(x = s1_total,
y = s1_phantom,
colour=cell_status)) +
scale_x_log10() +
scale_y_log10()
p2

#ggsave("phantom_molecules_validation.pdf", p2, width =8, height = 5)
Sample 2 plot
p3 <- ggplot(summary_mol_counts_cell %>%
filter(cell_status!="real" & s2_phantom >0 )) +
geom_point(aes(x = s2_total,
y = s2_phantom,
colour=cell_status)) +
scale_x_log10() +
scale_y_log10()
p3

#ggsave("phantom_cells_validation_s2.pdf", p3, width =8, height = 5)
Run workflow on multiplexed data
read_counts <-
data %>%
filter(label!="NA")%>%
select(-ends_with("nonplexed")) %>%
set_names(c("cell", "gene", "umi", "s1", "s2", "outcome", "label"))
read_counts
S <- 2
sample_names <- colnames(read_counts)[4:(S+3)]
sample_names
[1] "s1" "s2"
tic("Step 2: creating outcome counts datatable with grouping vars")
outcome_counts <- create_outcome_counts(read_counts%>%
select(-label),
sample_names,
min_frac=0.8)
toc()
Step 2: creating outcome counts datatable with grouping vars: 1.591 sec elapsed
outcome_counts
tic("Step 3: creating a chimera counts datatable and estimating hopping rate")
fit_out <-
estimate_hopping_rate(
outcome_counts,
S
)
toc()
Step 3: creating a chimera counts datatable and estimating hopping rate: 0.083 sec elapsed
fit_out
$glm_estimates
$chimera_counts
NA
# compute_molecular_complexity_profile
tic("Step 4: compute molecular complexity profile and other summary statistics")
summary_stats <-
compute_summary_stats(
outcome_counts,
fit_out$glm_estimates$phat,
sample_names
)
toc()
Step 4: compute molecular complexity profile and other summary statistics: 0.16 sec elapsed
summary_stats
$summary_estimates
$marginal
$conditional
$pi_r_hat
NA
Set the trade-off ratio cost cutoff (torc). The parameter torc represents the number of real molecules one is willing to incorrectly discard in order to correctly purge one phantom molecule. Since discarding a large proportion of the data is undesirable, reasonable values of torc are expected to be within the range of 1-5.
torc <- 3
tic("Step 5: reassign read counts, determine cutoff, and mark retained observations")
outcome_counts <-
reassign_reads_and_mark_retained_observations(
outcome_counts,
summary_stats,
sample_names,
fit_out,
torc
)
# get the tradoff ratio cutoff
summary_stats <- get_threshold(outcome_counts, summary_stats)
toc()
Step 5: reassign read counts, determine cutoff, and mark retained observations: 1.176 sec elapsed
tic("Step 6: Purge and save read counts datatable to disk")
read_counts <-
left_join(read_counts %>%
select(outcome, cell, umi, gene, sample_names, label),
outcome_counts,
by = c("outcome")
) %>%
select(-outcome)
toc()
Step 6: Purge and save read counts datatable to disk: 2.166 sec elapsed
Compare the SIHR estimates with ground truth estimates
p5 <-
ggplot(summary_counts_conditional) +
geom_line(aes(x = r,
y = SIHR_12,
colour="12"))+
geom_line(aes(x = r,
y = SIHR_21,
colour="21")) +
geom_hline(aes(yintercept = summary_counts_marginal$SIHR,
colour="true mean"),
linetype="solid",
size=.5) +
geom_hline(aes(yintercept = fit_out$glm_estimates$SIHR,
colour="estimate"),
linetype="solid",
size=.1) +
geom_linerange(data=summary_counts_conditional,
aes(x=r,
ymax=1-fit_out$glm_estimates$phat_low,
ymin=1-fit_out$glm_estimates$phat_high,
colour="estimate"),
size=.5)+
xlim(1,210) +
ylim(0.002,0.005)
#ggsave("index_hopping_rate_200.pdf", p5, width=9, height=6)
p5

Determine the number of false positives and false negatives
read_counts <-
read_counts %>%
ungroup() %>%
arrange(-qr) %>%
mutate(t= case_when(
label %in% c("f,r","0,r") ~ 2,
label %in% c("r,f","r,0") ~ 1 ),
f= case_when(
label %in% c("f,r","f,0") ~ 1,
label %in% c("r,f","0,f") ~ 2 )) %>%
mutate(tp= if_else( t == s, 1L, 0L, missing =0L),
fp= if_else( f == s, 1L, 0L, missing =0L),
tn= if_else( f != s, 1L, 0L, missing =0L),
fn= if_else( t != s, 1L, 0L, missing =0L),
tp0= if_else( t == 0, 1L, 0L, missing =0L), #0 if predict all molecules to be phantom
fn0= if_else( t != 0, 1L, 0L, missing =0L),
tn0= if_else( f != 0, 1L, 0L, missing =0L),
fp0= if_else( f == 0, 1L, 0L, missing =0L),
tp_max= if_else( t == s_maxprop, 1L, 0L, missing =0L),
fp_max= if_else( f == s_maxprop, 1L, 0L, missing =0L),
tn_max= if_else( f != s_maxprop, 1L, 0L, missing =0L),
fn_max= if_else( t != s_maxprop, 1L, 0L, missing =0L))
The maximum read fraction method
false_counts_maxprop <-
read_counts %>%
summarize_at(vars(c("tp_max", "fp_max", "tn_max", "fn_max")),
list( ~ sum(.))) %>%
set_names(c("tp", "fp", "tn", "fn"))
false_counts_maxprop
The tor method
false_counts_min_cutoff <-
read_counts %>%
summarize_at(vars(c("tp", "fp", "tn", "fn")),
list( ~ sum(.)))
false_counts_min_cutoff
No purging
false_counts_nopurging <-
read_counts%>%
summarize(n_cugs = n(),
n_real= sum(t>0, na.rm = TRUE),
n_fantom = sum(f>0, na.rm = TRUE),
n_mol=n_real+n_fantom,
g =n_cugs- n_real,
u = n_mol-n_cugs,
tp=n_mol-g,
fp=u+g,
tn=0,
fn=0)
false_counts_nopurging
TOR cutoff
read_counts <-
read_counts %>%
mutate_at(vars(c("tp", "fp", "tn", "fn","tp0", "fp0", "tn0", "fn0")),
list(cum= ~ cumsum(.))) %>%
mutate_at(vars(c("tp_cum", "fp_cum", "tn_cum", "fn_cum")),
list( ~ (last(.)-lag(., default =0)))) %>%
mutate(tp_t=tp_cum + tp0_cum,
fp_t=fp_cum + fp0_cum,
tn_t=tn_cum + tn0_cum,
fn_t=fn_cum + fn0_cum,
fpm= first(fp_t)- fp_t,
fnm= fn_t-first(fn_t),
tor_true= fnm/fpm)
false_counts_tor_cutoff <-
read_counts%>%
filter(retain) %>%
slice(1)%>%
select(c("s1", "s2", "qr", "tor", "tp_t", "fp_t", "tn_t", "fn_t", "fpm", "fnm", "tor_true"))
false_counts_tor_cutoff
Create comparison datatable
false_counts_dt <-
bind_rows(
list(no_purging=false_counts_nopurging %>%
select(c("tp", "fp", "tn", "fn")),
no_cutoff=false_counts_min_cutoff,
tor_cutoff=
false_counts_tor_cutoff %>%
select(c("tp_t", "fp_t", "tn_t", "fn_t")) %>%
set_names(c("tp", "fp", "tn", "fn")),
max_frac=false_counts_maxprop),
.id="approach") %>%
select(approach, fp,fn, tp, tn) %>%
mutate(fpr=fp/false_counts_nopurging$n_fantom,
fnr=fn/false_counts_nopurging$n_real)
false_counts_dt
Plots
Datatable for plotting
classification_curves <-
read_counts %>%
group_by(qr) %>%
slice(1L) %>%
ungroup() %>%
select( qr, qs, tor, retain, fp_t, FP, fn_t, FN, tp_t, tn_t, TP, TN, FPm, FNm, fpm, fnm, tor_true,o,r) %>%
mutate(fpr=fp_t/false_counts_nopurging$n_fantom,
fnr=fn_t/false_counts_nopurging$n_real)
classification_curves
Preformance Plots
p_tradeoff <-
ggplot(classification_curves) +
geom_point(
aes(x = FPm,
y = FNm),
size=.5)+
geom_line(
aes(x = FPm,
y = FPm,
colour="1")
) +
geom_line(
aes(x = FPm,
y = 2*FPm,
colour="2"))+
geom_line(
aes(x = FPm,
y = 3*FPm,
colour="3"))+
geom_line(
aes(x = FPm,
y = 4*FPm,
colour="4"))+
geom_line(
aes(x = FPm,
y = 5*FPm,
colour="5"))+
geom_line(
aes(x = FPm,
y = 9*FPm,
colour="9"))+
scale_y_log10() +
theme_bw() +
theme(
legend.title = element_text(face = "bold")) +
scale_color_discrete(name = "TORC") +
labs(x="Marginal Decrease in False Positives (reduce phantom molecs) ",
y="Marginal Increase in False Negatives (discard real molecs)")
#ggsave(file.path(figures_dir, "validation_tradeoff.pdf"), p_tradeoff, width=9, height=6)
p_tradeoff

p6 <-
ggplot(classification_curves) +
geom_point(
aes(x = fp_t,
y = fn_t,
colour="true")) +
geom_line(
aes(x = fp_t,
y = fn_t,
colour="true")) +
geom_line(
aes(x = FP,
y = FN,
colour="predicted"))+
geom_point(
aes(x = FP,
y = FN,
colour="predicted"))+
geom_point(data=false_counts_dt ,
aes(x = fp,
y = fn,
shape=approach),
size=2) +
labs(x="False Positive Count",
y="False Negative Count") +
scale_y_sqrt() +
scale_x_sqrt()
#ggsave("peformance_groundtruth.pdf", p6, width=9, height=6)
p6

p7 <- ggplot(false_counts_dt
%>% filter(approach %in% c("no_cutoff", "tor_cutoff", "max_frac"))) +
geom_point(
aes(x = fp ,
y = fn,
color=approach),
size=2)
#ggsave("peformance_zoom.pdf", p7, width=9, height=6)
p7

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.2 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8
[6] LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] cowplot_0.9.4 data.table_1.12.2 tictoc_1.0 furrr_0.1.0 future_1.13.0 broom_0.5.2 matrixStats_0.54.0 forcats_0.4.0
[9] stringr_1.4.0 dplyr_0.8.1 purrr_0.3.2 readr_1.3.1 tidyr_0.8.3 tibble_2.1.2 ggplot2_3.1.1 tidyverse_1.2.1
[17] rhdf5_2.28.0
loaded via a namespace (and not attached):
[1] tidyselect_0.2.5 xfun_0.7 listenv_0.7.0 haven_2.1.0 lattice_0.20-38 colorspace_1.4-1 generics_0.0.2 yaml_2.2.0
[9] rlang_0.3.4 pillar_1.4.1 glue_1.3.1 withr_2.1.2 modelr_0.1.4 readxl_1.3.1 plyr_1.8.4 munsell_0.5.0
[17] gtable_0.3.0 cellranger_1.1.0 rvest_0.3.4 codetools_0.2-16 labeling_0.3 knitr_1.23 parallel_3.6.0 Rcpp_1.0.1
[25] scales_1.0.0 backports_1.1.4 jsonlite_1.6 hms_0.4.2 digest_0.6.19 stringi_1.4.3 grid_3.6.0 rprojroot_1.3-2
[33] cli_1.1.0 tools_3.6.0 magrittr_1.5 lazyeval_0.2.2 crayon_1.3.4 pkgconfig_2.0.2 MASS_7.3-51.1 xml2_1.2.0
[41] lubridate_1.7.4 assertthat_0.2.1 httr_1.4.0 rstudioapi_0.10 Rhdf5lib_1.6.0 R6_2.4.0 globals_0.12.4 nlme_3.1-140
[49] compiler_3.6.0
LS0tCnRpdGxlOiAiUGhhbnRvbSBQdXJnZSIKc3VidGl0bGU6ICJWYWxpZGF0aW9uIEFuYWx5c2lzOiBQYXJ0IElJIgphdXRob3I6IAotIG5hbWU6IFJpY2sgRmFyb3VuaQogIGFmZmlsaWF0aW9uOgogIC0gJmNydWsgR8Opbm9tZSBRdcOpYmVjIElubm92YXRpb24gQ2VudHJlLCBNY0dpbGwgVW5pdmVyc2l0eSwgTW9udHJlYWwsIENhbmFkYQpkYXRlOiAnYHIgZm9ybWF0KFN5cy5EYXRlKCksICIlWS0lQi0lZCIpYCcKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdG9jOiBubwogICAgdG9jX2Zsb2F0OiAKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCiMgUHJlcGFyZSBhbmFseXNpcyB3b3JrZmxvdwoKIyMjIFNldCBwYXJhbWV0ZXJzCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBycHJvanJvb3Q6OmZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSwKICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoPTE1LAogICAgICAgICAgICAgICAgICAgICBkaWdpdD01LAogICAgICAgICAgICAgICAgICAgICBzY2lwZW49OCkKb3B0aW9ucyhyZWFkci5zaG93X3Byb2dyZXNzID0gRkFMU0UsCiAgICAgICAgZGlnaXRzPTUsIAogICAgICAgIHNjaXBlbj04LAogICAgICAgIGZ1dHVyZS5nbG9iYWxzLm1heFNpemUgPSArSW5mKQpgYGAKCgojIyMgU2V0IGZpbGVwYXRocyBhbmQgcGFyYW1ldGVycwoKYGBge3J9CnByb2plY3RfZGlyIDwtIHJwcm9qcm9vdDo6ZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpCgppZihpcy5udWxsKHByb2plY3RfZGlyKSl7CiAgcHJvamVjdF9kaXIgPC0gZ2V0d2QoKQogIHdhcm5pbmcoc3ByaW50ZigiTm8gcnN0dWRpbyBwcm9qZWN0IHJvb3QgZmlsZSAgZm91bmQuIAogICAgICAgICAgICAgICAgICBTZXR0aW5nIHByb2plY3QgZGlyZWN0b3J5IHRvIGN1cnJlbnQgd29ya2Zsb3cuUm1kIGZpbGUgbG9jYXRpb246ICVzLiAKICAgICAgICAgICAgICAgICAgT3ZlcnJpZGUgaWYgbmVlZGVkLiIsCiAgICAgICAgICAgICAgICAgIHByb2plY3RfZGlyKSkKIAp9Cm1lc3NhZ2Uoc3ByaW50ZigiUHJvamVjdCBkaXJlY3Rvcnk6ICVzIiwKICAgICAgICAgICAgICAgIHByb2plY3RfZGlyKSkKYGBgCgojIyMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkocmhkZjUpCiNsaWJyYXJ5KERyb3BsZXRVdGlscykgIyBpbnN0YWxsIGJ1dCBub3QgbG9hZApsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShtYXRyaXhTdGF0cykKbGlicmFyeShicm9vbSkKbGlicmFyeShmdXJycikKbGlicmFyeSh0aWN0b2MpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShjb3dwbG90KQpwbGFuKG11bHRpcHJvY2VzcykKYGBgCgoKIyMjIExvYWQgZnVuY3Rpb25zCgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KY29kZV9kaXIgPC0gZmlsZS5wYXRoKHByb2plY3RfZGlyLCAiY29kZSIpCnNvdXJjZShmaWxlLnBhdGgoY29kZV9kaXIsICIxX2NyZWF0ZV9qb2luZWRfY291bnRzX3RhYmxlLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjJfY3JlYXRlX2NvdW50c19ieV9vdXRjb21lX3RhYmxlLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjNfZXN0aW1hdGVfc2FtcGxlX2luZGV4X2hvcHBpbmdfcmF0ZS5SIikpCnNvdXJjZShmaWxlLnBhdGgoY29kZV9kaXIsICI0X2NvbXB1dGVfc3VtbWFyeV9zdGF0aXN0aWNzLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjVfcmVhc3NpZ25faG9wcGVkX3JlYWRzLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjZfcHVyZ2VfcGhhbnRvbV9tb2xlY3VsZXMuUiIpKQpzb3VyY2UoZmlsZS5wYXRoKGNvZGVfZGlyLCAiN19jYWxsX2NlbGxzLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjhfc3VtbWFyaXplX3B1cmdlLlIiKSkKc291cmNlKGZpbGUucGF0aChjb2RlX2RpciwgIjlfcGxvdHRpbmdfZnVuY3Rpb25zLlIiKSkKYGBgCgojIyMgTG9hZCBkYXRhCmBgYHtyfQp2YWxpZGF0aW9uX291dHB1dF9kaXIgPC0gZmlsZS5wYXRoKHByb2plY3RfZGlyLCAiZGF0YSIsICJoaXNlcTQwMDBfdmFsaWRhdGlvbiIpCmBgYAoKYGBge3J9CmRhdGEgPC0gcmVhZF90c3YoZmlsZS5wYXRoKHZhbGlkYXRpb25fb3V0cHV0X2RpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpc2VxNDAwMF9pbm5lcl9qb2luZWRfd2l0aF9sYWJlbHMudHh0IikpCmRhdGEKYGBgCgoKIyBDb21wdXRlIEluZGV4IGhvcHBpbmcgcmF0ZQoKIyMgIEVzdGltYXRlcyBjb25kaXRpb25hbCBvbiBkdXBsaWNhdGlvbiBsZXZlbCBhbmQgbGFiZWwKCmBgYHtyfQpzdW1tYXJ5X2NvdW50cyA8LQogICAgZGF0YSAlPiUKICBtdXRhdGUocj1hcy5pbnRlZ2VyKHJvd1N1bXMoLltjKDYsNyldKSkpICU+JQogIGFycmFuZ2UocikgJT4lCiAgZ3JvdXBfYnkociwgbGFiZWwpICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zMV9wbHxzMl9wbCIpKSwgCiAgICAgICAgICAgICAgIGxpc3QofiBzdW0oLikpKSAgJT4lCiAgbXV0YXRlKHMxX2hvcHBlZD1pZl9lbHNlKGxhYmVsICVpbiUgYygiMCxmIiwgInIsZiIpLCBzMl9wbGV4ZWQsMCksCiAgICAgICAgIHMyX2hvcHBlZD1pZl9lbHNlKGxhYmVsICVpbiUgYygiZiwwIiwgImYsciIpLCBzMV9wbGV4ZWQsMCksCiAgICAgICAgIHMxX25vbmhvcHBlZD1pZl9lbHNlKGxhYmVsICVpbiUgYygiciwwIiwgInIsZiIpLCBzMV9wbGV4ZWQsMCksCiAgICAgICAgIHMyX25vbmhvcHBlZD1pZl9lbHNlKGxhYmVsICVpbiUgYygiMCxyIiwgImYsciIpLCBzMl9wbGV4ZWQsMCkpICU+JQogIHNlbGVjdCgtczFfcGxleGVkLC0gczJfcGxleGVkKQoKc3VtbWFyeV9jb3VudHMKYGBgCgojIyBFc3RpbWF0ZXMgY29uZGl0aW9uYWwgb24gZHVwbGljYXRpb24gbGV2ZWwKCmBgYHtyfQpzdW1tYXJ5X2NvdW50c19jb25kaXRpb25hbCA8LQpzdW1tYXJ5X2NvdW50cyAgJT4lCiAgZ3JvdXBfYnkocikgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIAogICAgICAgICAgICAgICBsaXN0KH4gc3VtKC4pKSklPiUKICBtdXRhdGUoU0lIUl8xMiA9IHMxX2hvcHBlZC8oczFfaG9wcGVkK3MxX25vbmhvcHBlZCksCiAgICAgICAgIFNJSFJfMjEgPSBzMl9ob3BwZWQvKHMyX2hvcHBlZCtzMl9ub25ob3BwZWQpLAogICAgICAgICBmcmFjX3MxPSAoczFfaG9wcGVkK3MxX25vbmhvcHBlZCkvIChzMV9ob3BwZWQrczFfbm9uaG9wcGVkK3MyX2hvcHBlZCtzMl9ub25ob3BwZWQpKQpzdW1tYXJ5X2NvdW50c19jb25kaXRpb25hbApgYGAKCgoKIyMgTWFyZ2luYWwgZXN0aW1hdGVzCgpgYGB7cn0Kc3VtbWFyeV9jb3VudHNfbWFyZ2luYWwgPC0gCiAgc3VtbWFyeV9jb3VudHMgJT4lCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLCAKICAgICAgICAgICAgICAgbGlzdCh+IHN1bSguKSkpJT4lCiAgbXV0YXRlKFNJSFJfMTIgPSBzMV9ob3BwZWQvKHMxX2hvcHBlZCtzMV9ub25ob3BwZWQpLAogICAgICAgICBTSUhSXzIxID0gczJfaG9wcGVkLyhzMl9ob3BwZWQrczJfbm9uaG9wcGVkKSwKICAgICAgICAgU0lIUj0xLShzMV9ub25ob3BwZWQrczJfbm9uaG9wcGVkKS8oczFfaG9wcGVkK3MxX25vbmhvcHBlZCArczJfaG9wcGVkK3MyX25vbmhvcHBlZCksCiAgICAgICAgIGZyYWNfczE9IChzMV9ob3BwZWQrczFfbm9uaG9wcGVkKS8gKHMxX2hvcHBlZCtzMV9ub25ob3BwZWQrczJfaG9wcGVkK3MyX25vbmhvcHBlZCkpCnN1bW1hcnlfY291bnRzX21hcmdpbmFsCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpwMSA8LSAKICBnZ3Bsb3Qoc3VtbWFyeV9jb3VudHNfY29uZGl0aW9uYWwpICsKICAgIGdlb21fbGluZShhZXMoeCA9IHIsCiAgICAgICAgICAgICAgICAgICAgeSA9IFNJSFJfMTIqMTAwLAogICAgICAgICAgICAgICBjb2xvdXI9IlNJSFJfMTIiKSkrCiAgICBnZW9tX2xpbmUoYWVzKHggPSByLAogICAgICAgICAgICAgICAgICAgIHkgPSBTSUhSXzIxKjEwMCwKICAgICAgICAgICAgICAgY29sb3VyPSJTSUhSXzIxIikpICsKICAgICAgZ2VvbV9saW5lKGFlcyh4ID0gciwKICAgICAgICAgICAgICAgICAgICB5ID0gZnJhY19zMSwKICAgICAgICAgICAgICAgY29sb3VyPSJmcmFjX3MxIikpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHVubGlzdChzdW1tYXJ5X2NvdW50c19tYXJnaW5hbFs1OjddKSogYygxMDAsIDEwMCwgMTAwKSwKICAgICAgICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIpICsKICAgIHhsaW0oMCw5MCkgKyAKICB5bGltKDAsMSkgCnAxCiAKYGBgCgoKIyMgTW9sZWN1bGVzCgpgYGB7cn0Kc3VtbWFyeV9tb2xfY291bnRzIDwtCiAgZGF0YSAlPiUKICBtdXRhdGUocj1hcy5pbnRlZ2VyKHJvd1N1bXMoLltjKDYsNyldKSkpICU+JQogIGFycmFuZ2UocikgJT4lICAKICBtdXRhdGVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgCiAgICAgICAgICAgIGxpc3QofiBhcy5pbnRlZ2VyKC4hPTApKSkgICU+JQogIGdyb3VwX2J5KHIsIGxhYmVsKSAlPiUKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJeczFfcGx8XnMyX3BsIikpLCAKICAgICAgICAgICAgICAgbGlzdCh+IHN1bSguKSkpICAlPiUKICAgIG11dGF0ZShzMV9waGFudG9tPWlmX2Vsc2UobGFiZWwgJWluJSBjKCIwLGYiLCAicixmIiksIHMyX3BsZXhlZCwwTCksCiAgICAgICAgIHMyX3BoYW50b209aWZfZWxzZShsYWJlbCAlaW4lIGMoImYsMCIsICJmLHIiKSwgczFfcGxleGVkLDBMKSwKICAgICAgICAgczFfcmVhbD1pZl9lbHNlKGxhYmVsICVpbiUgYygiciwwIiwgInIsZiIpLCBzMV9wbGV4ZWQsMEwpLAogICAgICAgICBzMl9yZWFsPWlmX2Vsc2UobGFiZWwgJWluJSBjKCIwLHIiLCAiZixyIiksIHMyX3BsZXhlZCwwTCkpICU+JQogIHNlbGVjdCgtczFfcGxleGVkLC0gczJfcGxleGVkKSAKc3VtbWFyeV9tb2xfY291bnRzIApgYGAKYGBge3J9CnN1bW1hcnlfbW9sX2NvdW50c19tYXJnaW5hbCA8LQogIHN1bW1hcnlfbW9sX2NvdW50cyU+JQogIHVuZ3JvdXAoKSAlPiUKICBzdW1tYXJpemVfYXQodmFycyhtYXRjaGVzKCJecyIpKSwgCiAgICAgICAgICAgICAgIGxpc3QofiBzdW0oLikpKSAlPiUKICBtdXRhdGUocHBtXzEyID0gczFfcGhhbnRvbS8oczFfcGhhbnRvbStzMV9yZWFsKSwjIHByb3AgaG9wcGVkIHBoYW50b20gbW9sZWMKICAgICAgICAgcHBtXzIxID0gczJfcGhhbnRvbS8oczJfcGhhbnRvbStzMl9yZWFsKSwKICAgICAgICAgcHBtXzEgPSBzMl9waGFudG9tLyhzMl9waGFudG9tK3MxX3JlYWwpLCAjIHByb3AgcGhhbnRvbSBtb2xlYwogICAgICAgICBwcG1fMiA9IHMxX3BoYW50b20vKHMxX3BoYW50b20rczJfcmVhbCksCiAgICAgICAgIHBwbT0oczFfcGhhbnRvbStzMl9waGFudG9tKS8oczFfcGhhbnRvbStzMV9yZWFsK3MyX3BoYW50b20rczJfcmVhbCksCiAgICAgICAgIGZyYWNfbW9sX3MxPSAoczFfcGhhbnRvbStzMV9yZWFsKS8gKHMxX3BoYW50b20rczFfcmVhbCtzMl9waGFudG9tK3MyX3JlYWwpKQpzdW1tYXJ5X21vbF9jb3VudHNfbWFyZ2luYWwKYGBgCgoKYGBge3J9CnN1bW1hcnlfbW9sX2NvdW50c19jb25kaXRpb25hbCA8LQogIHN1bW1hcnlfbW9sX2NvdW50cyU+JQogIGdyb3VwX2J5KHIpICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKG1hdGNoZXMoIl5zIikpLCAKICAgICAgICAgICAgICAgbGlzdCh+IHN1bSguKSkpICU+JQogIG11dGF0ZShwcG1fMTIgPSBzMV9waGFudG9tLyhzMV9waGFudG9tK3MxX3JlYWwpLAogICAgICAgICBwcG1fMjEgPSBzMl9waGFudG9tLyhzMl9waGFudG9tK3MyX3JlYWwpLAogICAgICAgICBwcG09KHMxX3BoYW50b20rczJfcGhhbnRvbSkvKHMxX3BoYW50b20rczFfcmVhbCtzMl9waGFudG9tK3MyX3JlYWwpLAogICAgICAgICBmcmFjX21vbF9zMT0gKHMxX3BoYW50b20rczFfcmVhbCkvIChzMV9waGFudG9tK3MxX3JlYWwrczJfcGhhbnRvbStzMl9yZWFsKSkKc3VtbWFyeV9tb2xfY291bnRzX2NvbmRpdGlvbmFsCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MTB9CnA0IDwtIGdncGxvdChzdW1tYXJ5X21vbF9jb3VudHNfY29uZGl0aW9uYWwpICsKICBnZW9tX2xpbmUoYWVzKHggPSByLAogICAgICAgICAgICAgICAgICB5ID0gcHBtXzEyLAogICAgICAgICAgICAgY29sb3VyPSJwcG1fMTIiKSkrCiAgZ2VvbV9saW5lKGFlcyh4ID0gciwKICAgICAgICAgICAgICAgICAgeSA9ICBwcG1fMjEsCiAgICAgICAgICAgICBjb2xvdXI9IiBwcG1fMjEiKSkgKwogICNnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB1bmxpc3Qoc3VtbWFyeV9tb2xfY291bnRzX21hcmdpbmFsWzU6N10pLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIHhsaW0oMCwzMDApIApwNAogI2dnc2F2ZSgicGhhbnRvbV9tb2xlY3VsZXNfdmFsaWRhdGlvbi5wZGYiLCBwNCwgIHdpZHRoID04LCBoZWlnaHQgPSA1KQpgYGAKCgoKIyMgRXhhbWluZSBleHRlbnQgb2YgY29udGFtaW5hdGlvbiBpbiBjZWxscwoKYGBge3J9CnN1bW1hcnlfbW9sX2NvdW50c19jZWxsPC0KICBkYXRhICU+JQogIGZpbHRlcihsYWJlbCE9Ik5BIikgJT4lCiAgbXV0YXRlX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIAogICAgICAgICAgICBsaXN0KH4gYXMuaW50ZWdlciguIT0wKSkpICAlPiUKICBncm91cF9ieShjZWxsLCBsYWJlbCkgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMxX3BsfF5zMl9wbCIpKSwgCiAgICAgICAgICAgICAgIGxpc3QofiBzdW0oLikpKSAgJT4lCiAgICBtdXRhdGUoczFfcGhhbnRvbT1pZl9lbHNlKGxhYmVsICVpbiUgYygiZiwwIiwgImYsciIpLCBzMV9wbGV4ZWQsMEwpLAogICAgICAgICBzMl9waGFudG9tPWlmX2Vsc2UobGFiZWwgJWluJSBjKCIwLGYiLCAicixmIiksIHMyX3BsZXhlZCwwTCksCiAgICAgICAgIHMxX3JlYWw9aWZfZWxzZShsYWJlbCAlaW4lIGMoInIsMCIsICJyLGYiKSwgczFfcGxleGVkLDBMKSwKICAgICAgICAgczJfcmVhbD1pZl9lbHNlKGxhYmVsICVpbiUgYygiMCxyIiwgImYsciIpLCBzMl9wbGV4ZWQsMEwpKSAlPiUKICBzZWxlY3QoLXMxX3BsZXhlZCwtIHMyX3BsZXhlZCkgJT4lCiAgZ3JvdXBfYnkoY2VsbCkgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIAogICAgICAgICAgICAgICBsaXN0KH4gc3VtKC4pKSkgJT4lIAogICAgbXV0YXRlX2F0KHZhcnMobWF0Y2hlcygiXnMiKSksIAogICAgICAgICAgICBsaXN0KG5vbmVtcHR5PSB+IGFzLmludGVnZXIoLiE9MCkpKSAgJT4lCiAgdW5pdGUobGFiZWwsbWF0Y2hlcygibm9uZW1wdHkiKSwgc2VwPSIsIikgJT4lCiAgbXV0YXRlKGNlbGxfc3RhdHVzPSAKICAgICAgICAgICBjYXNlX3doZW4obGFiZWwgJWluJSBjKCIwLDAsMCwxIiwiMCwwLDEsMCIpIH4gInJlYWwiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbCAlaW4lIGMoIjAsMCwxLDEiKSB+ICJyZWFsIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwgJWluJSBjKCIxLDAsMCwwIiwiMCwxLDAsMCIsICIxLDEsMCwwIikgfiAgInBoYW50b20iLAogICAgICAgICAgICAgICAgICAgICBsYWJlbCAlaW4lIGMoIjEsMCwwLDEiLCIwLDEsMSwwIikgfiAgInBoYW50b20iLAogICAgICAgICAgICAgICAgICAgICBUUlVFIH4gICJjb250YW1pbmF0ZWQiKSkgICU+JQogIG11dGF0ZShzMV90b3RhbCA9KHMxX3BoYW50b20rczFfcmVhbCksCiAgICAgICAgIHMyX3RvdGFsID0oczJfcGhhbnRvbStzMl9yZWFsKSwKICAgICAgICAgczFfcHBtID0gczFfcGhhbnRvbS8oczFfdG90YWwpLAogICAgICAgICBzMl9wcG0gPSBzMl9waGFudG9tLyhzMl90b3RhbCkpCiAgCnN1bW1hcnlfbW9sX2NvdW50c19jZWxsCmBgYApgYGB7cn0KY2VsbF9zdGF0dXNfdGFsbHkgPC0KICBzdW1tYXJ5X21vbF9jb3VudHNfY2VsbCAlPiUKICBncm91cF9ieShjZWxsX3N0YXR1cyxsYWJlbCkgJT4lCiAgdGFsbHkoc29ydD1UUlVFKQpjZWxsX3N0YXR1c190YWxseQpgYGAKCmBgYHtyfQpuX2FmZmVjdGVkX2NlbGxzIDwtIAogIGNlbGxfc3RhdHVzX3RhbGx5ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIoY2VsbF9zdGF0dXMhPSJyZWFsIikgJT4lIAogIHN1bW1hcmlzZShuPXN1bShuKSkgJT4lIAogIHB1bGwobikKbl9hZmZlY3RlZF9jZWxscyA8LSBuX2FmZmVjdGVkX2NlbGxzICsgMTUwMiArNgpuX2FmZmVjdGVkX2NlbGxzCmBgYAoKCmBgYHtyfQpuX3RvdGFsX2NlbGxzIDwtCiAgc3VtbWFyeV9tb2xfY291bnRzX2NlbGwgJT4lCiAgbXV0YXRlX2F0KHZhcnMobWF0Y2hlcygiX3RvdGFsIikpLCAKICAgICAgICAgICAgbGlzdCh+IGFzLmludGVnZXIoLiE9MCkpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3VtbWFyaXNlX2F0KHZhcnMobWF0Y2hlcygiX3RvdGFsIikpLCBsaXN0KH5zdW0oLikpKSAlPiUKICBtdXRhdGUoY2VsbHNfdG90YWw9IHMxX3RvdGFsK3MyX3RvdGFsKSU+JQogIHB1bGwoY2VsbHNfdG90YWwpCm5fdG90YWxfY2VsbHMgCmBgYAoKUHJvcG9ydGlvbiBvZiBhZmZlY3RlZCBjZWxscwoKYGBge3J9CnBfYWZmZWN0ZWRfY2VsbHMgPC0gIG5fYWZmZWN0ZWRfY2VsbHMvbl90b3RhbF9jZWxscwpwX2FmZmVjdGVkX2NlbGxzCmBgYAoKRm9yIGVhY2ggY2VsbC1iYXJjb2RlLCBwbG90IHRoZSBudW1iZXIgb2YgcGhhbnRvbSBtb2xlY3VsZXMgYWdhaW5zdCB0aGUgbnVtYmVyIG9mIHRvdGFsIG1vbGVjdWxlcyBhc3NvY2lhdGVkIHdpdGggaXQuCgpTYW1wbGUgMSBwbG90CgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KcDIgPC0gZ2dwbG90KHN1bW1hcnlfbW9sX2NvdW50c19jZWxsICU+JQogICAgICAgICAgICAgICBmaWx0ZXIoY2VsbF9zdGF0dXMhPSJyZWFsIiAmIHMxX3BoYW50b20gPjAgKSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBzMV90b3RhbCwKICAgICAgICAgICAgICAgICAgeSA9IHMxX3BoYW50b20sCiAgICAgICAgICAgICAgICAgY29sb3VyPWNlbGxfc3RhdHVzKSkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpCgpwMgogI2dnc2F2ZSgicGhhbnRvbV9tb2xlY3VsZXNfdmFsaWRhdGlvbi5wZGYiLCBwMiwgIHdpZHRoID04LCBoZWlnaHQgPSA1KQpgYGAKClNhbXBsZSAyIHBsb3QKCgpgYGB7ciwgZmlnLmhlaWdodD0xMH0KcDMgPC0gZ2dwbG90KHN1bW1hcnlfbW9sX2NvdW50c19jZWxsICU+JQogICAgICAgICAgICAgICBmaWx0ZXIoY2VsbF9zdGF0dXMhPSJyZWFsIiAmIHMyX3BoYW50b20gPjAgKSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBzMl90b3RhbCwKICAgICAgICAgICAgICAgICAgeSA9IHMyX3BoYW50b20sCiAgICAgICAgICAgICAgICAgY29sb3VyPWNlbGxfc3RhdHVzKSkgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgc2NhbGVfeV9sb2cxMCgpCgpwMwojZ2dzYXZlKCJwaGFudG9tX2NlbGxzX3ZhbGlkYXRpb25fczIucGRmIiwgcDMsICB3aWR0aCA9OCwgaGVpZ2h0ID0gNSkKYGBgCgojIFJ1biB3b3JrZmxvdyBvbiBtdWx0aXBsZXhlZCBkYXRhCgpgYGB7cn0KcmVhZF9jb3VudHMgPC0gCiAgZGF0YSAlPiUgCiAgZmlsdGVyKGxhYmVsIT0iTkEiKSU+JQogIHNlbGVjdCgtZW5kc193aXRoKCJub25wbGV4ZWQiKSkgJT4lCiAgc2V0X25hbWVzKGMoImNlbGwiLCAiZ2VuZSIsICJ1bWkiLCAiczEiLCAiczIiLCAib3V0Y29tZSIsICJsYWJlbCIpKQpyZWFkX2NvdW50cwpgYGAKCgpgYGB7cn0KUyA8LSAyCnNhbXBsZV9uYW1lcyA8LSBjb2xuYW1lcyhyZWFkX2NvdW50cylbNDooUyszKV0Kc2FtcGxlX25hbWVzCmBgYAoKCgpgYGB7cn0KdGljKCJTdGVwIDI6IGNyZWF0aW5nIG91dGNvbWUgY291bnRzIGRhdGF0YWJsZSB3aXRoIGdyb3VwaW5nIHZhcnMiKQoKb3V0Y29tZV9jb3VudHMgPC0gY3JlYXRlX291dGNvbWVfY291bnRzKHJlYWRfY291bnRzJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtbGFiZWwpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9uYW1lcywgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX2ZyYWM9MC44KQp0b2MoKQoKCm91dGNvbWVfY291bnRzCmBgYAoKYGBge3J9CnRpYygiU3RlcCAzOiBjcmVhdGluZyBhIGNoaW1lcmEgY291bnRzIGRhdGF0YWJsZSBhbmQgZXN0aW1hdGluZyBob3BwaW5nIHJhdGUiKQogIGZpdF9vdXQgPC0KICAgIGVzdGltYXRlX2hvcHBpbmdfcmF0ZSgKICAgICAgb3V0Y29tZV9jb3VudHMsCiAgICAgIFMKICAgICkKICB0b2MoKQpmaXRfb3V0IApgYGAKCgoKYGBge3J9CiAgIyBjb21wdXRlX21vbGVjdWxhcl9jb21wbGV4aXR5X3Byb2ZpbGUKICB0aWMoIlN0ZXAgNDogY29tcHV0ZSBtb2xlY3VsYXIgY29tcGxleGl0eSBwcm9maWxlIGFuZCBvdGhlciBzdW1tYXJ5IHN0YXRpc3RpY3MiKQogIHN1bW1hcnlfc3RhdHMgPC0KICAgIGNvbXB1dGVfc3VtbWFyeV9zdGF0cygKICAgICAgb3V0Y29tZV9jb3VudHMsCiAgICAgIGZpdF9vdXQkZ2xtX2VzdGltYXRlcyRwaGF0LAogICAgICBzYW1wbGVfbmFtZXMKICAgICkKICB0b2MoKQpzdW1tYXJ5X3N0YXRzCmBgYAoKU2V0IHRoZSB0cmFkZS1vZmYgcmF0aW8gY29zdCBjdXRvZmYgKCp0b3JjKikuIFRoZSBwYXJhbWV0ZXIgKnRvcmMqIHJlcHJlc2VudHMgdGhlIG51bWJlciBvZiByZWFsIG1vbGVjdWxlcyBvbmUgaXMgd2lsbGluZyB0byBpbmNvcnJlY3RseSBkaXNjYXJkIGluIG9yZGVyIHRvIGNvcnJlY3RseSBwdXJnZSBvbmUgcGhhbnRvbSBtb2xlY3VsZS4gU2luY2UgZGlzY2FyZGluZyBhIGxhcmdlIHByb3BvcnRpb24gb2YgdGhlIGRhdGEgaXMgdW5kZXNpcmFibGUsIHJlYXNvbmFibGUgdmFsdWVzIG9mICp0b3JjKiBhcmUgZXhwZWN0ZWQgdG8gYmUgd2l0aGluIHRoZSByYW5nZSBvZiAxLTUuCgpgYGB7cn0KdG9yYyA8LSAzIApgYGAKCgpgYGB7cn0KdGljKCJTdGVwIDU6IHJlYXNzaWduIHJlYWQgY291bnRzLCBkZXRlcm1pbmUgY3V0b2ZmLCBhbmQgbWFyayByZXRhaW5lZCBvYnNlcnZhdGlvbnMiKQoKICBvdXRjb21lX2NvdW50cyA8LQogICAgcmVhc3NpZ25fcmVhZHNfYW5kX21hcmtfcmV0YWluZWRfb2JzZXJ2YXRpb25zKAogICAgICBvdXRjb21lX2NvdW50cywKICAgICAgc3VtbWFyeV9zdGF0cywKICAgICAgc2FtcGxlX25hbWVzLAogICAgICBmaXRfb3V0LAogICAgICB0b3JjCiAgICApCiAgIyBnZXQgdGhlIHRyYWRvZmYgcmF0aW8gY3V0b2ZmCiAgc3VtbWFyeV9zdGF0cyA8LSBnZXRfdGhyZXNob2xkKG91dGNvbWVfY291bnRzLCBzdW1tYXJ5X3N0YXRzKQoKICB0b2MoKQpgYGAKCgoKYGBge3J9CnRpYygiU3RlcCA2OiBQdXJnZSBhbmQgc2F2ZSByZWFkIGNvdW50cyBkYXRhdGFibGUgdG8gZGlzayIpCgpyZWFkX2NvdW50cyA8LQogIGxlZnRfam9pbihyZWFkX2NvdW50cyAlPiUKICAgIHNlbGVjdChvdXRjb21lLCBjZWxsLCB1bWksIGdlbmUsIHNhbXBsZV9uYW1lcywgbGFiZWwpLAogIG91dGNvbWVfY291bnRzLAogIGJ5ID0gYygib3V0Y29tZSIpCiAgKSAlPiUKICBzZWxlY3QoLW91dGNvbWUpCgp0b2MoKQoKYGBgCgoKIyMjIENvbXBhcmUgdGhlIFNJSFIgZXN0aW1hdGVzIHdpdGggZ3JvdW5kIHRydXRoIGVzdGltYXRlcwoKYGBge3IsIGZpZy53aWR0aD0xMH0KcDUgPC0gCiAgZ2dwbG90KHN1bW1hcnlfY291bnRzX2NvbmRpdGlvbmFsKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gciwKICAgICAgICAgICAgICAgICAgeSA9IFNJSFJfMTIsCiAgICAgICAgICAgICBjb2xvdXI9IjEyIikpKwogIGdlb21fbGluZShhZXMoeCA9IHIsCiAgICAgICAgICAgICAgICAgIHkgPSBTSUhSXzIxLAogICAgICAgICAgICAgY29sb3VyPSIyMSIpKSAgKwpnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gIHN1bW1hcnlfY291bnRzX21hcmdpbmFsJFNJSFIsIAogICAgICAgICAgICAgICBjb2xvdXI9InRydWUgbWVhbiIpLCAKICAgICAgICAgICBsaW5ldHlwZT0ic29saWQiLAogICAgICAgICAgIHNpemU9LjUpICAgKwpnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gIGZpdF9vdXQkZ2xtX2VzdGltYXRlcyRTSUhSLCAKICAgICAgICAgICAgICAgY29sb3VyPSJlc3RpbWF0ZSIpLAogICAgICAgICAgIGxpbmV0eXBlPSJzb2xpZCIsCiAgICAgICAgICAgc2l6ZT0uMSkgICArCiAgICBnZW9tX2xpbmVyYW5nZShkYXRhPXN1bW1hcnlfY291bnRzX2NvbmRpdGlvbmFsLCAKICAgICAgICAgICAgICAgICAgYWVzKHg9ciwKICAgICAgICAgICAgICAgICAgICAgIHltYXg9MS1maXRfb3V0JGdsbV9lc3RpbWF0ZXMkcGhhdF9sb3csCiAgICAgICAgICAgICAgICAgICAgICB5bWluPTEtZml0X291dCRnbG1fZXN0aW1hdGVzJHBoYXRfaGlnaCwKICAgICAgICAgICAgICAgICAgICAgIGNvbG91cj0iZXN0aW1hdGUiKSwgCiAgICAgICAgICAgICAgICAgIHNpemU9LjUpKyAKICB4bGltKDEsMjEwKSArCiAgeWxpbSgwLjAwMiwwLjAwNSkKI2dnc2F2ZSgiaW5kZXhfaG9wcGluZ19yYXRlXzIwMC5wZGYiLCBwNSwgd2lkdGg9OSwgaGVpZ2h0PTYpCnA1CmBgYAoKCgojIERldGVybWluZSB0aGUgbnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzCgpgYGB7cn0KcmVhZF9jb3VudHMgPC0KICAgIHJlYWRfY291bnRzICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKC1xcikgJT4lCiAgbXV0YXRlKHQ9IGNhc2Vfd2hlbigKICAgICAgbGFiZWwgJWluJSBjKCJmLHIiLCIwLHIiKSB+IDIsCiAgICAgIGxhYmVsICVpbiUgYygicixmIiwiciwwIikgfiAxICAgICksCiAgICBmPSBjYXNlX3doZW4oCiAgICAgIGxhYmVsICVpbiUgYygiZixyIiwiZiwwIikgfiAxLAogICAgICBsYWJlbCAlaW4lIGMoInIsZiIsIjAsZiIpIH4gMiAgICApKSAlPiUKICBtdXRhdGUodHA9IGlmX2Vsc2UoIHQgPT0gcywgMUwsIDBMLCBtaXNzaW5nID0wTCksCiAgICAgICAgIGZwPSBpZl9lbHNlKCBmID09IHMsIDFMLCAwTCwgbWlzc2luZyA9MEwpLAogICAgICAgICB0bj0gaWZfZWxzZSggZiAhPSBzLCAxTCwgMEwsIG1pc3NpbmcgPTBMKSwKICAgICAgICAgZm49IGlmX2Vsc2UoIHQgIT0gcywgMUwsIDBMLCBtaXNzaW5nID0wTCksCiAgICAgICAgIHRwMD0gaWZfZWxzZSggdCA9PSAwLCAxTCwgMEwsIG1pc3NpbmcgPTBMKSwgIzAgaWYgIHByZWRpY3QgYWxsIG1vbGVjdWxlcyB0byBiZSBwaGFudG9tCiAgICAgICAgIGZuMD0gaWZfZWxzZSggdCAhPSAwLCAxTCwgMEwsIG1pc3NpbmcgPTBMKSwKICAgICAgICAgdG4wPSBpZl9lbHNlKCBmICE9IDAsIDFMLCAwTCwgbWlzc2luZyA9MEwpLAogICAgICAgICBmcDA9IGlmX2Vsc2UoIGYgPT0gMCwgMUwsIDBMLCBtaXNzaW5nID0wTCksCiAgICAgICAgIHRwX21heD0gaWZfZWxzZSggdCA9PSBzX21heHByb3AsIDFMLCAwTCwgbWlzc2luZyA9MEwpLAogICAgICAgICBmcF9tYXg9IGlmX2Vsc2UoIGYgPT0gc19tYXhwcm9wLCAxTCwgMEwsIG1pc3NpbmcgPTBMKSwKICAgICAgICAgdG5fbWF4PSBpZl9lbHNlKCBmICE9IHNfbWF4cHJvcCwgMUwsIDBMLCBtaXNzaW5nID0wTCksCiAgICAgICAgIGZuX21heD0gaWZfZWxzZSggdCAhPSBzX21heHByb3AsIDFMLCAwTCwgbWlzc2luZyA9MEwpKSAKYGBgCgoKIyMjIFRoZSBtYXhpbXVtIHJlYWQgZnJhY3Rpb24gbWV0aG9kCgoKYGBge3J9CmZhbHNlX2NvdW50c19tYXhwcm9wIDwtCiAgcmVhZF9jb3VudHMgJT4lCiAgc3VtbWFyaXplX2F0KHZhcnMoYygidHBfbWF4IiwgImZwX21heCIsICJ0bl9tYXgiLCAiZm5fbWF4IikpLAogICAgICAgICAgICBsaXN0KCB+IHN1bSguKSkpICU+JQogIHNldF9uYW1lcyhjKCJ0cCIsICJmcCIsICJ0biIsICJmbiIpKQpmYWxzZV9jb3VudHNfbWF4cHJvcApgYGAKCiMjIyBUaGUgdG9yIG1ldGhvZAoKCmBgYHtyfQpmYWxzZV9jb3VudHNfbWluX2N1dG9mZiA8LQogIHJlYWRfY291bnRzICU+JQogIHN1bW1hcml6ZV9hdCh2YXJzKGMoInRwIiwgImZwIiwgInRuIiwgImZuIikpLAogICAgICAgICAgICBsaXN0KCB+IHN1bSguKSkpIApmYWxzZV9jb3VudHNfbWluX2N1dG9mZgpgYGAKCiMjIyBObyBwdXJnaW5nCgpgYGB7cn0KZmFsc2VfY291bnRzX25vcHVyZ2luZyA8LSAKICByZWFkX2NvdW50cyU+JQogIHN1bW1hcml6ZShuX2N1Z3MgPSBuKCksCiAgICAgICAgICAgIG5fcmVhbD0gc3VtKHQ+MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbl9mYW50b20gPSBzdW0oZj4wLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBuX21vbD1uX3JlYWwrbl9mYW50b20sCiAgICAgICAgICAgIGcgPW5fY3Vncy0gbl9yZWFsLAogICAgICAgICAgICB1ID0gbl9tb2wtbl9jdWdzLAogICAgICAgICAgICB0cD1uX21vbC1nLAogICAgICAgICAgICBmcD11K2csCiAgICAgICAgICAgIHRuPTAsCiAgICAgICAgICAgIGZuPTApCgpmYWxzZV9jb3VudHNfbm9wdXJnaW5nCmBgYAoKCiMjIyAgVE9SIGN1dG9mZgoKYGBge3J9CnJlYWRfY291bnRzIDwtCiAgcmVhZF9jb3VudHMgJT4lCiAgbXV0YXRlX2F0KHZhcnMoYygidHAiLCAiZnAiLCAidG4iLCAiZm4iLCJ0cDAiLCAiZnAwIiwgInRuMCIsICJmbjAiKSksCiAgICAgICAgICAgIGxpc3QoY3VtPSB+IGN1bXN1bSguKSkpICAlPiUKICBtdXRhdGVfYXQodmFycyhjKCJ0cF9jdW0iLCAiZnBfY3VtIiwgInRuX2N1bSIsICJmbl9jdW0iKSksIAogICAgICAgICAgICBsaXN0KCB+IChsYXN0KC4pLWxhZyguLCBkZWZhdWx0ID0wKSkpKSAlPiUKICBtdXRhdGUodHBfdD10cF9jdW0gKyB0cDBfY3VtLAogICAgICAgICBmcF90PWZwX2N1bSArIGZwMF9jdW0sCiAgICAgICAgIHRuX3Q9dG5fY3VtICsgdG4wX2N1bSwKICAgICAgICAgZm5fdD1mbl9jdW0gKyBmbjBfY3VtLAogICAgICAgICBmcG09IGZpcnN0KGZwX3QpLSBmcF90LCAKICAgICAgICAgZm5tPSBmbl90LWZpcnN0KGZuX3QpLAogICAgICAgICB0b3JfdHJ1ZT0gZm5tL2ZwbSkKYGBgCgoKCmBgYHtyfQpmYWxzZV9jb3VudHNfdG9yX2N1dG9mZiA8LQogIHJlYWRfY291bnRzJT4lCiAgZmlsdGVyKHJldGFpbikgJT4lCiAgc2xpY2UoMSklPiUKICBzZWxlY3QoYygiczEiLCAiczIiLCAicXIiLCAidG9yIiwgInRwX3QiLCAiZnBfdCIsICJ0bl90IiwgImZuX3QiLCAiZnBtIiwgImZubSIsICJ0b3JfdHJ1ZSIpKSAgCmZhbHNlX2NvdW50c190b3JfY3V0b2ZmCmBgYAojIyBDcmVhdGUgY29tcGFyaXNvbiBkYXRhdGFibGUKCmBgYHtyfQpmYWxzZV9jb3VudHNfZHQgPC0KICBiaW5kX3Jvd3MoCiAgICBsaXN0KG5vX3B1cmdpbmc9ZmFsc2VfY291bnRzX25vcHVyZ2luZyAlPiUKICAgICAgICAgICBzZWxlY3QoYygidHAiLCAiZnAiLCAidG4iLCAiZm4iKSksCiAgICAgICAgIG5vX2N1dG9mZj1mYWxzZV9jb3VudHNfbWluX2N1dG9mZiwKICAgICAgICAgdG9yX2N1dG9mZj0KICAgICAgICAgICBmYWxzZV9jb3VudHNfdG9yX2N1dG9mZiAlPiUKICAgICAgICAgICBzZWxlY3QoYygidHBfdCIsICJmcF90IiwgInRuX3QiLCAiZm5fdCIpKSAlPiUKICAgICAgICAgICBzZXRfbmFtZXMoYygidHAiLCAiZnAiLCAidG4iLCAiZm4iKSksCiAgICAgICAgIG1heF9mcmFjPWZhbHNlX2NvdW50c19tYXhwcm9wKSwKICAgIC5pZD0iYXBwcm9hY2giKSAlPiUKICBzZWxlY3QoYXBwcm9hY2gsIGZwLGZuLCB0cCwgdG4pICU+JQogIG11dGF0ZShmcHI9ZnAvZmFsc2VfY291bnRzX25vcHVyZ2luZyRuX2ZhbnRvbSwKICAgICAgICAgZm5yPWZuL2ZhbHNlX2NvdW50c19ub3B1cmdpbmckbl9yZWFsKQpmYWxzZV9jb3VudHNfZHQKYGBgCgoKCiMjIFBsb3RzCgojIyMgRGF0YXRhYmxlIGZvciBwbG90dGluZwoKCmBgYHtyfQpjbGFzc2lmaWNhdGlvbl9jdXJ2ZXMgPC0KICByZWFkX2NvdW50cyAlPiUgCiAgZ3JvdXBfYnkocXIpICU+JQogIHNsaWNlKDFMKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KCBxciwgcXMsIHRvciwgcmV0YWluLCBmcF90LCBGUCwgZm5fdCwgRk4sIHRwX3QsIHRuX3QsIFRQLCBUTiwgRlBtLCBGTm0sIGZwbSwgZm5tLCB0b3JfdHJ1ZSxvLHIpICU+JQogIG11dGF0ZShmcHI9ZnBfdC9mYWxzZV9jb3VudHNfbm9wdXJnaW5nJG5fZmFudG9tLAogICAgICAgICBmbnI9Zm5fdC9mYWxzZV9jb3VudHNfbm9wdXJnaW5nJG5fcmVhbCkKY2xhc3NpZmljYXRpb25fY3VydmVzCmBgYAoKIyMjIFByZWZvcm1hbmNlIFBsb3RzCgoKCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwX3RyYWRlb2ZmIDwtICAKICAgIGdncGxvdChjbGFzc2lmaWNhdGlvbl9jdXJ2ZXMpICsgCiAgICBnZW9tX3BvaW50KAogICAgICBhZXMoeCA9IEZQbSwKICAgICAgICAgIHkgPSBGTm0pLAogICAgICBzaXplPS41KSsKICAgIGdlb21fbGluZSgKICAgICAgYWVzKHggPSBGUG0sCiAgICAgICAgICB5ID0gRlBtLAogICAgICAgICAgY29sb3VyPSIxIikKICAgICkgKwogICAgZ2VvbV9saW5lKAogICAgICBhZXMoeCA9IEZQbSwKICAgICAgICAgIHkgPSAyKkZQbSwKICAgICAgICAgIGNvbG91cj0iMiIpKSsKICAgIGdlb21fbGluZSgKICAgICAgYWVzKHggPSBGUG0sCiAgICAgICAgICB5ID0gMypGUG0sCiAgICAgICAgICBjb2xvdXI9IjMiKSkrCiAgICBnZW9tX2xpbmUoCiAgICAgIGFlcyh4ID0gRlBtLAogICAgICAgICAgeSA9IDQqRlBtLAogICAgICAgICAgY29sb3VyPSI0IikpKwogICAgZ2VvbV9saW5lKAogICAgICBhZXMoeCA9IEZQbSwKICAgICAgICAgIHkgPSA1KkZQbSwKICAgICAgICAgIGNvbG91cj0iNSIpKSsKICAgIGdlb21fbGluZSgKICAgICAgYWVzKHggPSBGUG0sCiAgICAgICAgICB5ID0gOSpGUG0sCiAgICAgICAgICBjb2xvdXI9IjkiKSkrCiAgICBzY2FsZV95X2xvZzEwKCkgKwogICAgdGhlbWVfYncoKSAgKwogICAgICB0aGVtZSgKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpICsgCiAgICAgIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiVE9SQyIpICsKICAgIGxhYnMoeD0iTWFyZ2luYWwgRGVjcmVhc2UgaW4gRmFsc2UgUG9zaXRpdmVzIChyZWR1Y2UgcGhhbnRvbSBtb2xlY3MpICIsCiAgICAgICAgIHk9Ik1hcmdpbmFsIEluY3JlYXNlIGluIEZhbHNlIE5lZ2F0aXZlcyAoZGlzY2FyZCByZWFsIG1vbGVjcykiKSAKICAgIAoKI2dnc2F2ZShmaWxlLnBhdGgoZmlndXJlc19kaXIsICJ2YWxpZGF0aW9uX3RyYWRlb2ZmLnBkZiIpLCBwX3RyYWRlb2ZmLCB3aWR0aD05LCBoZWlnaHQ9NikKCnBfdHJhZGVvZmYKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTZ9CnA2IDwtCiAgZ2dwbG90KGNsYXNzaWZpY2F0aW9uX2N1cnZlcykgICsgCiAgICBnZW9tX3BvaW50KAogICAgICAgICAgICAgYWVzKHggPSBmcF90LAogICAgICAgICAgICAgICAgICB5ID0gZm5fdCwKICAgICAgICAgICAgICAgICBjb2xvdXI9InRydWUiKSkgKwoKICBnZW9tX2xpbmUoCiAgICAgICAgICAgICBhZXMoeCA9IGZwX3QsCiAgICAgICAgICAgICAgICAgIHkgPSBmbl90LAogICAgICAgICAgICAgICAgIGNvbG91cj0idHJ1ZSIpKSArCiAgICBnZW9tX2xpbmUoCiAgICAgICAgICAgICBhZXMoeCA9IEZQLAogICAgICAgICAgICAgICAgICB5ID0gRk4sCiAgICAgICAgICAgICAgICAgY29sb3VyPSJwcmVkaWN0ZWQiKSkrCiAgICAgIGdlb21fcG9pbnQoCiAgICAgICAgICAgICBhZXMoeCA9IEZQLAogICAgICAgICAgICAgICAgICB5ID0gRk4sCiAgICAgICAgICAgICAgICAgY29sb3VyPSJwcmVkaWN0ZWQiKSkrIAoKICBnZW9tX3BvaW50KGRhdGE9ZmFsc2VfY291bnRzX2R0ICwKICAgICAgICAgICAgIGFlcyh4ID0gZnAsCiAgICAgICAgICAgICAgICAgIHkgPSBmbiwKICAgICAgICAgICAgICAgICAgc2hhcGU9YXBwcm9hY2gpLAogICAgICAgICAgICAgc2l6ZT0yKSArCiAgICBsYWJzKHg9IkZhbHNlIFBvc2l0aXZlIENvdW50IiwKICAgICAgIHk9IkZhbHNlIE5lZ2F0aXZlIENvdW50IikgICsgCiAgICBzY2FsZV95X3NxcnQoKSArIAogIHNjYWxlX3hfc3FydCgpIAojZ2dzYXZlKCJwZWZvcm1hbmNlX2dyb3VuZHRydXRoLnBkZiIsIHA2LCB3aWR0aD05LCBoZWlnaHQ9NikKIHA2CmBgYAoKCgpgYGB7ciBmaWcuaGVpZ2h0PTh9CnA3IDwtIGdncGxvdChmYWxzZV9jb3VudHNfZHQKICAgICAgICAgICAgICAlPiUgZmlsdGVyKGFwcHJvYWNoICVpbiUgYygibm9fY3V0b2ZmIiwgInRvcl9jdXRvZmYiLCAibWF4X2ZyYWMiKSkpICArIAogIGdlb21fcG9pbnQoCiAgICAgICAgICAgICBhZXMoeCA9IGZwICwKICAgICAgICAgICAgICAgICAgeSA9IGZuLAogICAgICAgICAgICAgICAgICBjb2xvcj1hcHByb2FjaCksCiAgICAgICAgICAgICBzaXplPTIpIAoKCiNnZ3NhdmUoInBlZm9ybWFuY2Vfem9vbS5wZGYiLCBwNywgd2lkdGg9OSwgaGVpZ2h0PTYpCnA3CmBgYApgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==