This notebook takes the output for the MAF Pipeline (mycsvfile.csv) and does:

  1. Calculates true pos, false pos, and false neg and makes a precision recall curve for all tools
  2. Finds total numbers of variants found by tools at different allele frequencies and sequencing depths
  3. Looks at correlation between SNPS in the golden vcf and worflow vcf For viral variant callers!

Loading Libraries

library('ggplot2')
library('tidyverse')
-- Attaching packages --------------------------------------- tidyverse 1.3.0 --
v tibble  3.0.4     v dplyr   1.0.2
v tidyr   1.0.0     v stringr 1.4.0
v readr   1.3.1     v forcats 0.4.0
v purrr   0.3.3     
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library('plyr')
------------------------------------------------------------------------------
You have loaded plyr after dplyr - this is likely to cause problems.
If you need functions from both plyr and dplyr, please load plyr first, then dplyr:
library(plyr); library(dplyr)
------------------------------------------------------------------------------

Attaching package: 'plyr'
The following objects are masked from 'package:dplyr':

    arrange, count, desc, failwith, id, mutate, rename, summarise,
    summarize
The following object is masked from 'package:purrr':

    compact
library('ggpubr')

Attaching package: 'ggpubr'
The following object is masked from 'package:plyr':

    mutate
library("MLmetrics")

Attaching package: 'MLmetrics'
The following object is masked from 'package:base':

    Recall
MyColors = c("#E76F51", "#E9C369", "#2A9D8F","#2B4FA2", "#119DAC", "#672D7B", "#262366", "#BCBCBC", "#000000", "#CE0019", "#BBBCCD", "#E56D54", "#113CBD")
ggplot2::theme_set(theme_minimal())
detach(package:plyr, unload=TRUE)
library("plotrix") 

Making Transition/Transversion Function

TsTv = function(dataframe) {
  transitions = 0
  for (row in 1:nrow(dataframe)) {
  
   REF <- dataframe[row, "ref"] #requires dataframe to have column name "ref"
   ALT <- dataframe[row, "alt"] # requires dataframe to have column name "alt"

    if(REF == "A" & ALT == "G")
      transitions = transitions + 1
    if(REF == "G" & ALT == "A")
      transitions = transitions + 1
    if(REF == "C" & ALT == "T")
      transitions = transitions + 1
    if(REF == "T" & ALT == "C")
      transitions = transitions + 1
  }

  transversions = 0
  for (row in 1:nrow(dataframe)) {
  
    REF <- dataframe[row, "ref"]
    ALT <- dataframe[row, "alt"]
  
    if(REF == "A" & ALT == "T")
      transversions = transversions + 1
    if(REF == "T" & ALT == "A")
      transversions = transversions + 1
    if(REF == "C" & ALT == "A")
      transversions = transversions + 1
    if(REF == "A" & ALT == "C")
      transversions = transversions + 1
    if(REF == "G" & ALT == "T")
      transversions = transversions + 1
    if(REF == "T" & ALT == "G")
      transversions = transversions + 1
    if(REF == "C" & ALT == "G")
      transversions = transversions + 1
    if(REF == "G" & ALT == "C")
      transversions = transversions + 1
  }

  TsTv_df = data.frame(transitions,transversions)
  print(TsTv_df)
  TsTv_ratio = (transitions/transversions)
  print(TsTv_ratio)
  
}

Reading in Variant Data and Evaluating

AV = read.csv(af_report,header = T)
AV$sample_id = as.character(AV$sample_id)
AV$dp = as.numeric(AV$dp)
AV = separate(AV, sample_id, sep = "_", 
               into = c(NA,NA,"allele_freq",NA,"seq_depth","tool","parameter"))%>% 
  filter(ref == "A" | ref == "C" | ref == "T" | ref == "G") %>% 
  filter(alt == "A" | alt == "C" | alt == "T" | alt == "G") %>% droplevels()
Warning: Expected 7 pieces. Additional pieces discarded in 19576 rows [137, 138,
139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
155, 156, ...].
Warning: Expected 7 pieces. Missing pieces filled with `NA` in 73089 rows [407,
408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423,
424, 425, 426, ...].
AV$seq_depth = as.numeric(AV$seq_depth)
AV$coverage = (AV$seq_depth * 100000)

AV$parameter = NULL
head(AV)
Dp_Variation = ggplot(AV, aes(x = log10(coverage), y = log10(dp), color = tool)) +
  geom_point() + 
  facet_wrap(~tool, scales = "free_y") +
  scale_color_manual(values = MyColors)
Warning: namespace 'plyr' is not available and has been replaced
by .GlobalEnv when processing object ''
print(Dp_Variation)
Warning: Removed 4599 rows containing missing values (geom_point).

ggsave(Dp_Variation, file = "AV_Dp_Coverage.png")
Saving 7 x 5 in image
Warning: Removed 4599 rows containing missing values (geom_point).

Reading in Golden Data and Evaluating

viral_golden = read.table(golden_vcf, stringsAsFactors = F)
colnames(viral_golden) = c("chrom","pos","ID","ref","alt","qual","filter","info","codes","genotype")
viral_golden = separate(viral_golden, info, sep = "=", into = c("label","AF"))
viral_golden$label = NULL
head(viral_golden)
dim(viral_golden)
[1] 133  10
viral_golden_pos = ggplot(viral_golden, aes(x = pos, y = ref, color = alt)) +
  geom_point()
print(viral_golden_pos)

TsTv(viral_golden)
  transitions transversions
1         111            22
[1] 5.045455

SetAF - Separating Data into TP, FP, FN

AV_setAF = filter(AV, allele_freq != "random") %>% droplevels()

AV_setAF_falsepos = filter(AV_setAF, af_golden == 0 & af_workflow != 0) %>% droplevels()
  AV_setAF_falsepos$category = c("FP")
AV_setAF_falseneg = filter(AV_setAF, af_workflow == 0 & af_golden != 0) %>% droplevels()
  AV_setAF_falseneg$category = c("FN")
AV_setAF_truepos = filter(AV_setAF, af_golden != 0 & af_workflow != 0)
  AV_setAF_truepos$category = c("TP")
  
AV_setAF = rbind(AV_setAF_truepos, AV_setAF_falseneg, AV_setAF_falsepos)

FP_All = group_by(AV_setAF_falsepos, allele_freq,coverage,tool) %>% tally()
  colnames(FP_All) = c("allele_freq","coverage","tool","FP")
FN_All = group_by(AV_setAF_falseneg, allele_freq,coverage,tool) %>% tally()
  colnames(FN_All) = c("allele_freq","coverage","tool","FN")
TP_All = group_by(AV_setAF_truepos, allele_freq,coverage,tool) %>% tally()
  colnames(TP_All) = c("allele_freq","coverage","tool","TP")
    
AllVar = merge(FP_All,FN_All, by=c("allele_freq","coverage","tool"),all=T)
AllVar = merge(AllVar,TP_All, by=c("allele_freq","coverage","tool"), all=T)
head(AllVar)
AllVar[is.na(AllVar)] = 0
head(AllVar)
AllVar$prec = (AllVar$TP)/(AllVar$TP + AllVar$FP)
AllVar$recall = (AllVar$TP)/(AllVar$TP + AllVar$FN)
AllVar$area = AllVar$prec * AllVar$recall
head(AllVar)

RandomAF - Separating Data into TP, FP, FN

AV_randomAF = filter(AV, allele_freq == "random") %>% droplevels()

AV_randomAF_falsepos = filter(AV_randomAF, af_golden == 0 & af_workflow != 0) %>% droplevels()
  AV_randomAF_falsepos$category = c("FP")
AV_randomAF_falseneg = filter(AV_randomAF, af_workflow == 0 & af_golden != 0) %>% droplevels()
  AV_randomAF_falseneg$category = c("FN")
AV_randomAF_truepos = filter(AV_randomAF, af_golden != 0 & af_workflow != 0)
  AV_randomAF_truepos$category = c("TP")
  
AV_randomAF = rbind(AV_randomAF_truepos, AV_randomAF_falseneg, AV_randomAF_falsepos)

rFP_All = group_by(AV_randomAF_falsepos, allele_freq,coverage,tool) %>% tally()
  colnames(rFP_All) = c("allele_freq","coverage","tool","FP")
rFN_All = group_by(AV_randomAF_falseneg, allele_freq,coverage,tool) %>% tally()
  colnames(rFN_All) = c("allele_freq","coverage","tool","FN")
rTP_All = group_by(AV_randomAF_truepos, allele_freq,coverage,tool) %>% tally()
  colnames(rTP_All) = c("allele_freq","coverage","tool","TP")

rAllVar = merge(rFP_All,rFN_All, by=c("allele_freq","coverage","tool"),all=T)
rAllVar = merge(rAllVar, rTP_All, by=c("allele_freq","coverage","tool"), all=T)
head(rAllVar)
rAllVar[is.na(rAllVar)] = 0

rAllVar$prec = (rAllVar$TP)/(rAllVar$TP + rAllVar$FP)
rAllVar$recall = (rAllVar$TP)/(rAllVar$TP + rAllVar$FN)
rAllVar$area = rAllVar$prec * rAllVar$recall
head(rAllVar)

1. Precision_Recall Curves

PR_AllVarSet_freq = ggplot(AllVar,aes(x=recall, y=prec, group = tool, color = tool)) +
  geom_point() + 
  geom_line() +
  facet_wrap(~allele_freq) +
  scale_color_manual(values = MyColors)
print(PR_AllVarSet_freq)
Warning: Removed 5 rows containing missing values (geom_point).
Warning: Removed 4 row(s) containing missing values (geom_path).

ggsave("PR_AllVarSet_byAF.png",PR_AllVarSet_freq)
Saving 7 x 5 in image
Warning: Removed 5 rows containing missing values (geom_point).

Warning: Removed 4 row(s) containing missing values (geom_path).
PR_AllVarSet_cover = ggplot(AllVar,aes(x=recall, y=prec, group = tool, color=tool)) +
  geom_point() + 
  geom_line() +
  facet_wrap(~coverage) +
  scale_color_manual(values = MyColors)
print(PR_AllVarSet_cover)
Warning: Removed 5 rows containing missing values (geom_point).
Warning: Removed 3 row(s) containing missing values (geom_path).

ggsave("PR_AllVarSet_byCov.png",PR_AllVarSet_cover)
Saving 7 x 5 in image
Warning: Removed 5 rows containing missing values (geom_point).

Warning: Removed 3 row(s) containing missing values (geom_path).
PR_AllVarRandom = ggplot(rAllVar,aes(x=recall, y=prec, group = tool, color=tool)) +
  geom_point() + 
  geom_line() +
  scale_color_manual(values = MyColors)
print(PR_AllVarRandom)

ggsave("PR_AllVarRandom.png",PR_AllVarRandom)
Saving 7 x 5 in image
# Examining Data

AllVar_PR_zeros = filter(AllVar, prec == 0 & recall == 0)
AllVar_PR_high = filter(AllVar, prec > 0.9 & recall > 0.9)

AllVar_PR_highest = group_by(AllVar, tool, allele_freq, coverage) %>%
  arrange(area, decreasing = TRUE)
write.csv(AllVar_PR_highest, file = "AllVar_PR_Thresholds.csv")

2.1 SetAF - Proportion of Variants Found

AV_setAF_counts = group_by(AV_setAF_truepos, tool, allele_freq, coverage) %>% tally()
AV_setAF_counts$proportion = (AV_setAF_counts$n / nrow(viral_golden))

AV_setAF_proportion = ggplot(AV_setAF_counts,aes(x = log10(coverage), y = proportion,
                                                group = tool, color = tool)) +
  geom_point() +
  geom_line() +
  facet_grid(~allele_freq) +
  scale_color_manual(values = MyColors)
print(AV_setAF_proportion)

ggsave("AllVarSet_Proportion.png", AV_setAF_proportion)
Saving 7 x 5 in image

2.2 RandomAF - Proportion of Variants Found

AV_randomAF_counts = group_by(AV_randomAF_truepos, tool, allele_freq, coverage) %>% tally()
AV_randomAF_counts$proportion = (AV_randomAF_counts$n / nrow(viral_golden))

AV_randomAF_proportion = ggplot(AV_randomAF_counts, aes(x = log10(coverage), y = proportion, 
                                                        group = tool, color = tool)) +
  geom_point() +
  geom_line() +
  scale_color_manual(values = MyColors)
print(AV_randomAF_proportion)

ggsave("AllVarRandom_Proportion.png", AV_randomAF_proportion)
Saving 7 x 5 in image
## Only looking at coverage between 100-1000
AV_randomAF_truepos_f = filter(AV_randomAF_truepos, coverage > 99 & coverage < 1001)
AV_randomAF_counts_f = group_by(AV_randomAF_truepos_f, tool, allele_freq, coverage) %>% tally()
AV_randomAF_counts_f$proportion = (AV_randomAF_counts_f$n / nrow(viral_golden))
 
AV_randomAF_proportion_f = ggplot(AV_randomAF_counts_f, aes(x = log10(coverage), y = proportion, 
                                                             group = tool, color = tool)) +
  geom_point() +
  geom_line() +
  scale_color_manual(values = MyColors)
print(AV_randomAF_proportion_f)

ggsave("AllVarRandom_Proportion_Filtered.png", AV_randomAF_proportion_f)
Saving 7 x 5 in image

3.1 SetAF - Correlation Between Golden and Workflow

## Only looking at true positives
AV_Corr_setAF = ggplot(AV_setAF, aes(x = af_golden, y = af_workflow,
                                                color = tool, shape = category)) +
  geom_point() +
  scale_color_manual(values = MyColors) +
  geom_boxplot(aes(group = allele_freq)) + 
  facet_grid(tool~coverage) +
  ylim(-0.001,1.0) + xlim(-0.001,1.0)
print(AV_Corr_setAF)

ggsave("AllVarSet_Correlation.png",AV_Corr_setAF)
Saving 7 x 5 in image
##Looking at all SetAF Data
AV_Corr_setAF = ggplot(AV_setAF, aes(x = af_golden, y = af_workflow, 
                                       color = tool, group = tool, shape = category)) +
  geom_point(size = 3, alpha = 0.4) +
  scale_color_manual(values = MyColors) +
  facet_grid(~coverage) +
  geom_smooth(method='lm', size = 1.5) +
  ylim(-0.001,1.0) + xlim(-0.001,1.0)
print(AV_Corr_setAF)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 39 rows containing missing values (geom_smooth).

ggsave("AllVarSet_Correlation_Filtered.png",AV_Corr_setAF)
Saving 7 x 5 in image
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 39 rows containing missing values (geom_smooth).
AV_Corr_setAF_Zoom = ggplot(AV_setAF, aes(x = af_golden, y = af_workflow,
                                            color = tool, group = tool, shape = category)) +
  geom_point(size = 3, alpha = 0.4) +
  scale_color_manual(values = MyColors) +
  facet_grid(~coverage) +
  ylim(-0.01,0.15) + xlim(-0.01,0.15)
print(AV_Corr_setAF_Zoom)
Warning: Removed 12395 rows containing missing values (geom_point).

ggsave("AllVarSet_CorrelationF_Filtered_Zoom.png", AV_Corr_setAF_Zoom)
Saving 7 x 5 in image
Warning: Removed 12395 rows containing missing values (geom_point).

3.2 RandomAF - Correlation Between Golden and Workflow

## Only looking at true positives
AV_Corr_randomAF = ggplot(AV_randomAF, aes(x = af_golden, y = af_workflow,
                                                      color = tool, shape = category)) +
  geom_point() +
  scale_color_manual(values = MyColors) +
  facet_grid(tool~coverage) +
  ylim(-0.001,1.0) + xlim(-0.001,1.0)
print(AV_Corr_randomAF)

ggsave("AllVarRandom_Correlation.png",AV_Corr_randomAF)
Saving 7 x 5 in image
##Looking at all RandomAF Data
AV_Corr_randomAF = ggplot(AV_randomAF, aes(x = af_golden, y = af_workflow, 
                                             color = tool, group = tool, shape = category)) +
  geom_point(size = 3, alpha = 0.4) +
  scale_color_manual(values = MyColors) +
  facet_grid(~coverage) +
  geom_smooth(method='lm', size = 1.5) +
  ylim(-0.01,1) + xlim(-0.01,1)
print(AV_Corr_randomAF)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 105 rows containing missing values (geom_smooth).

ggsave("AllVarRandom_Correlation_Filtered.png",AV_Corr_randomAF)
Saving 7 x 5 in image
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 105 rows containing missing values (geom_smooth).
AV_Corr_randomAF_Zoom = ggplot(AV_randomAF, aes(x = af_golden, y = af_workflow,
                                                  color = tool, group = tool, shape = category)) +
  geom_point(size = 3, alpha = 0.4) +
  scale_color_manual(values = MyColors) +
  facet_grid(~coverage) +
  geom_smooth(method='lm', size = 1.5) +
  ylim(-0.01,0.15) + xlim(-0.01,0.15)
print(AV_Corr_randomAF_Zoom)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 10113 rows containing non-finite values (stat_smooth).
Warning: Removed 10113 rows containing missing values (geom_point).

ggsave("AllVarRandom_Correlation_Filtered_Zoom.png", AV_Corr_randomAF_Zoom)
Saving 7 x 5 in image
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 10113 rows containing non-finite values (stat_smooth).

Warning: Removed 10113 rows containing missing values (geom_point).

Stats for SetAF Data

coeff_var <- function(x) {
  CV <- sd(x) / mean(x) * 100
  return(CV)
}

AV_setAF_split = group_by(AV_setAF_truepos, tool, allele_freq, coverage) %>%
  summarize(mean(af_workflow), sd(af_workflow), coeff_var(af_workflow)) %>% droplevels()
`summarise()` regrouping output by 'tool', 'allele_freq' (override with `.groups` argument)
colnames(AV_setAF_split) = c("tool","AF","cov","mean","sd","variance")

Mean_setAF = ggplot(AV_setAF_split, aes(x = tool, y = mean, color = cov)) + 
  geom_point() +
  facet_grid(rows = vars(tool), cols = vars(AF)) +
  theme(text = element_text(size = 15),
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
print(Mean_setAF)

ggsave("SetAF_Mean_AlleleFrequency.png",Mean_setAF)
Saving 7 x 5 in image
CoeffVar_setAF = ggplot(AV_setAF_split, aes(x = tool, y = variance, color = cov)) + 
  geom_point() +
  facet_grid(rows = vars(tool), cols = vars(AF)) +
  theme(text = element_text(size = 15),
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
print(CoeffVar_setAF)
Warning: Removed 4 rows containing missing values (geom_point).

ggsave("SetAF_CoeffVar_AlleleFrequency.png",CoeffVar_setAF)
Saving 7 x 5 in image
Warning: Removed 4 rows containing missing values (geom_point).

Set Up for Stats for RandomAF Data

AV_randomAF_split = group_by(AV_randomAF_truepos, tool, coverage) %>%
  summarize(mean(af_workflow), sd(af_workflow), coeff_var(af_workflow)) %>% droplevels()
`summarise()` regrouping output by 'tool' (override with `.groups` argument)
colnames(AV_randomAF_split) = c("tool","cov","mean","sd","variance")

Mean_randomAF = ggplot(AV_randomAF_split, aes(x = tool, y = mean, color = cov)) + 
  geom_point()
  theme(text = element_text(size = 15),
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
List of 2
 $ text       :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : num 15
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi FALSE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.x:List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 1
  ..$ vjust        : num 0.5
  ..$ angle        : num 90
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi FALSE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE
print(Mean_randomAF)

ggsave("RandomAF_Mean_AlleleFrequency.png",Mean_randomAF)
Saving 7 x 5 in image
CoeffVar_randomAF = ggplot(AV_randomAF_split, aes(x = tool, y = variance, color = cov)) + 
  geom_point() +
  theme(text = element_text(size = 15),
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
print(CoeffVar_randomAF)

ggsave("RandomAF_CoeffVar_AlleleFrequency.png",CoeffVar_randomAF)
Saving 7 x 5 in image

4. Look at how each tool compares at each AF and seq-depth

AVSet_Corr = ggplot(AV_setAF_truepos, aes(x = tool, y = af_workflow)) + 
  geom_point() +
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  facet_grid(allele_freq~coverage, scales = "free_y")
print(AVSet_Corr)

ggsave("AllVarSet_Tool_Comparison.pdf", AVSet_Corr, width = 15, height = 20)
# y axes are set per row at the moment (scales = "free_y")

AVRandom_Corr = ggplot(AV_randomAF_truepos, aes(x = tool, y = af_workflow)) + 
  geom_point() +
  geom_violin() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) +
  facet_grid(allele_freq~coverage)
print(AVRandom_Corr)

ggsave("AllVarRandom_Tool_Comparison.pdf", AVRandom_Corr, width = 15, height = 20)

5.1. Looks at where the SNPS are located throughout the genome

# Set AF Data
AV_Pos = ggplot(AV_setAF_truepos, aes(x = pos, y = af_workflow)) +
  geom_point()
print(AV_Pos)

AV_Pos_falsepos = ggplot(AV_setAF_falsepos, aes(x = pos, y = af_workflow)) +
  geom_point()
print(AV_Pos_falsepos)

AV_Pos_falseneg = ggplot(AV_setAF_falseneg, aes(x = pos, y = af_workflow)) +
  geom_point()
print(AV_Pos_falseneg)

# Random AF Data
AV_Pos_r = ggplot(AV_randomAF_truepos, aes(x = pos, y = af_workflow)) +
  geom_point()
print(AV_Pos_r)

AV_Pos_falsepos_r = ggplot(AV_randomAF_falsepos, aes(x = pos, y = af_workflow)) +
  geom_point()
print(AV_Pos_falsepos_r)

AV_Pos_falseneg_r = ggplot(AV_randomAF_falseneg, aes(x = pos, y = af_workflow)) +
  geom_point()
print(AV_Pos_falseneg_r)

5.2.Transitions/Transversions

setAF_TP_pos = ggplot(AV_setAF_truepos, aes(x = pos, y = ref, color = tool, shape = alt)) +
  geom_point()
print(setAF_TP_pos)

TsTv(AV_setAF_truepos)
  transitions transversions
1       38806          7821
[1] 4.96177
#counts variants from all tools/seq_depth/AF trials

randomAF_TP_pos = ggplot(AV_randomAF_truepos, aes(x = pos, y = ref, color = tool, shape = alt)) +
  geom_point()
print(randomAF_TP_pos)

TsTv(AV_randomAF_truepos)
  transitions transversions
1        7612          1543
[1] 4.933247
LS0tCnRpdGxlOiAiVmFyaWFudHNfQWxsQ2FsbGVycyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyNUaGlzIG5vdGVib29rIHRha2VzIHRoZSBvdXRwdXQgZm9yIHRoZSBNQUYgUGlwZWxpbmUgKG15Y3N2ZmlsZS5jc3YpIGFuZCBkb2VzOgoxLiBDYWxjdWxhdGVzIHRydWUgcG9zLCBmYWxzZSBwb3MsIGFuZCBmYWxzZSBuZWcgYW5kIG1ha2VzIGEgcHJlY2lzaW9uIHJlY2FsbCBjdXJ2ZSBmb3IgYWxsIHRvb2xzCjIuIEZpbmRzIHRvdGFsIG51bWJlcnMgb2YgdmFyaWFudHMgZm91bmQgYnkgdG9vbHMgYXQgZGlmZmVyZW50IGFsbGVsZSBmcmVxdWVuY2llcyBhbmQgc2VxdWVuY2luZyBkZXB0aHMKMy4gTG9va3MgYXQgY29ycmVsYXRpb24gYmV0d2VlbiBTTlBTIGluIHRoZSBnb2xkZW4gdmNmIGFuZCB3b3JmbG93IHZjZgpGb3IgdmlyYWwgdmFyaWFudCBjYWxsZXJzIQoKCiMjTG9hZGluZyBMaWJyYXJpZXMKYGBge3J9CmxpYnJhcnkoJ2dncGxvdDInKQpsaWJyYXJ5KCd0aWR5dmVyc2UnKQpsaWJyYXJ5KCdwbHlyJykKbGlicmFyeSgnZ2dwdWJyJykKbGlicmFyeSgiTUxtZXRyaWNzIikKCk15Q29sb3JzID0gYygiI0U3NkY1MSIsICIjRTlDMzY5IiwgIiMyQTlEOEYiLCIjMkI0RkEyIiwgIiMxMTlEQUMiLCAiIzY3MkQ3QiIsICIjMjYyMzY2IiwgIiNCQ0JDQkMiLCAiIzAwMDAwMCIsICIjQ0UwMDE5IiwgIiNCQkJDQ0QiLCAiI0U1NkQ1NCIsICIjMTEzQ0JEIikKZ2dwbG90Mjo6dGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkKCmBgYAoKYGBge3J9CmRldGFjaChwYWNrYWdlOnBseXIsIHVubG9hZD1UUlVFKQpsaWJyYXJ5KCJwbG90cml4IikgCmBgYAoKIyNNYWtpbmcgVHJhbnNpdGlvbi9UcmFuc3ZlcnNpb24gRnVuY3Rpb24KYGBge3J9ClRzVHYgPSBmdW5jdGlvbihkYXRhZnJhbWUpIHsKICB0cmFuc2l0aW9ucyA9IDAKICBmb3IgKHJvdyBpbiAxOm5yb3coZGF0YWZyYW1lKSkgewogIAogICBSRUYgPC0gZGF0YWZyYW1lW3JvdywgInJlZiJdICNyZXF1aXJlcyBkYXRhZnJhbWUgdG8gaGF2ZSBjb2x1bW4gbmFtZSAicmVmIgogICBBTFQgPC0gZGF0YWZyYW1lW3JvdywgImFsdCJdICMgcmVxdWlyZXMgZGF0YWZyYW1lIHRvIGhhdmUgY29sdW1uIG5hbWUgImFsdCIKCiAgICBpZihSRUYgPT0gIkEiICYgQUxUID09ICJHIikKICAgICAgdHJhbnNpdGlvbnMgPSB0cmFuc2l0aW9ucyArIDEKICAgIGlmKFJFRiA9PSAiRyIgJiBBTFQgPT0gIkEiKQogICAgICB0cmFuc2l0aW9ucyA9IHRyYW5zaXRpb25zICsgMQogICAgaWYoUkVGID09ICJDIiAmIEFMVCA9PSAiVCIpCiAgICAgIHRyYW5zaXRpb25zID0gdHJhbnNpdGlvbnMgKyAxCiAgICBpZihSRUYgPT0gIlQiICYgQUxUID09ICJDIikKICAgICAgdHJhbnNpdGlvbnMgPSB0cmFuc2l0aW9ucyArIDEKICB9CgogIHRyYW5zdmVyc2lvbnMgPSAwCiAgZm9yIChyb3cgaW4gMTpucm93KGRhdGFmcmFtZSkpIHsKICAKICAgIFJFRiA8LSBkYXRhZnJhbWVbcm93LCAicmVmIl0KICAgIEFMVCA8LSBkYXRhZnJhbWVbcm93LCAiYWx0Il0KICAKICAgIGlmKFJFRiA9PSAiQSIgJiBBTFQgPT0gIlQiKQogICAgICB0cmFuc3ZlcnNpb25zID0gdHJhbnN2ZXJzaW9ucyArIDEKICAgIGlmKFJFRiA9PSAiVCIgJiBBTFQgPT0gIkEiKQogICAgICB0cmFuc3ZlcnNpb25zID0gdHJhbnN2ZXJzaW9ucyArIDEKICAgIGlmKFJFRiA9PSAiQyIgJiBBTFQgPT0gIkEiKQogICAgICB0cmFuc3ZlcnNpb25zID0gdHJhbnN2ZXJzaW9ucyArIDEKICAgIGlmKFJFRiA9PSAiQSIgJiBBTFQgPT0gIkMiKQogICAgICB0cmFuc3ZlcnNpb25zID0gdHJhbnN2ZXJzaW9ucyArIDEKICAgIGlmKFJFRiA9PSAiRyIgJiBBTFQgPT0gIlQiKQogICAgICB0cmFuc3ZlcnNpb25zID0gdHJhbnN2ZXJzaW9ucyArIDEKICAgIGlmKFJFRiA9PSAiVCIgJiBBTFQgPT0gIkciKQogICAgICB0cmFuc3ZlcnNpb25zID0gdHJhbnN2ZXJzaW9ucyArIDEKICAgIGlmKFJFRiA9PSAiQyIgJiBBTFQgPT0gIkciKQogICAgICB0cmFuc3ZlcnNpb25zID0gdHJhbnN2ZXJzaW9ucyArIDEKICAgIGlmKFJFRiA9PSAiRyIgJiBBTFQgPT0gIkMiKQogICAgICB0cmFuc3ZlcnNpb25zID0gdHJhbnN2ZXJzaW9ucyArIDEKICB9CgogIFRzVHZfZGYgPSBkYXRhLmZyYW1lKHRyYW5zaXRpb25zLHRyYW5zdmVyc2lvbnMpCiAgcHJpbnQoVHNUdl9kZikKICBUc1R2X3JhdGlvID0gKHRyYW5zaXRpb25zL3RyYW5zdmVyc2lvbnMpCiAgcHJpbnQoVHNUdl9yYXRpbykKICAKfQpgYGAKCiMjUmVhZGluZyBpbiBWYXJpYW50IERhdGEgYW5kIEV2YWx1YXRpbmcKYGBge3J9CkFWID0gcmVhZC5jc3YoYWZfcmVwb3J0LGhlYWRlciA9IFQpCkFWJHNhbXBsZV9pZCA9IGFzLmNoYXJhY3RlcihBViRzYW1wbGVfaWQpCkFWJGRwID0gYXMubnVtZXJpYyhBViRkcCkKQVYgPSBzZXBhcmF0ZShBViwgc2FtcGxlX2lkLCBzZXAgPSAiXyIsIAogICAgICAgICAgICAgICBpbnRvID0gYyhOQSxOQSwiYWxsZWxlX2ZyZXEiLE5BLCJzZXFfZGVwdGgiLCJ0b29sIiwicGFyYW1ldGVyIikpJT4lIAogIGZpbHRlcihyZWYgPT0gIkEiIHwgcmVmID09ICJDIiB8IHJlZiA9PSAiVCIgfCByZWYgPT0gIkciKSAlPiUgCiAgZmlsdGVyKGFsdCA9PSAiQSIgfCBhbHQgPT0gIkMiIHwgYWx0ID09ICJUIiB8IGFsdCA9PSAiRyIpICU+JSBkcm9wbGV2ZWxzKCkKQVYkc2VxX2RlcHRoID0gYXMubnVtZXJpYyhBViRzZXFfZGVwdGgpCkFWJGNvdmVyYWdlID0gKEFWJHNlcV9kZXB0aCAqIDEwMDAwMCkKCkFWJHBhcmFtZXRlciA9IE5VTEwKaGVhZChBVikKCkRwX1ZhcmlhdGlvbiA9IGdncGxvdChBViwgYWVzKHggPSBsb2cxMChjb3ZlcmFnZSksIHkgPSBsb2cxMChkcCksIGNvbG9yID0gdG9vbCkpICsKICBnZW9tX3BvaW50KCkgKyAKICBmYWNldF93cmFwKH50b29sLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBNeUNvbG9ycykKcHJpbnQoRHBfVmFyaWF0aW9uKQpnZ3NhdmUoRHBfVmFyaWF0aW9uLCBmaWxlID0gIkFWX0RwX0NvdmVyYWdlLnBuZyIpCmBgYAoKIyNSZWFkaW5nIGluIEdvbGRlbiBEYXRhIGFuZCBFdmFsdWF0aW5nCmBgYHtyfQp2aXJhbF9nb2xkZW4gPSByZWFkLnRhYmxlKGdvbGRlbl92Y2YsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpjb2xuYW1lcyh2aXJhbF9nb2xkZW4pID0gYygiY2hyb20iLCJwb3MiLCJJRCIsInJlZiIsImFsdCIsInF1YWwiLCJmaWx0ZXIiLCJpbmZvIiwiY29kZXMiLCJnZW5vdHlwZSIpCnZpcmFsX2dvbGRlbiA9IHNlcGFyYXRlKHZpcmFsX2dvbGRlbiwgaW5mbywgc2VwID0gIj0iLCBpbnRvID0gYygibGFiZWwiLCJBRiIpKQp2aXJhbF9nb2xkZW4kbGFiZWwgPSBOVUxMCmhlYWQodmlyYWxfZ29sZGVuKQpkaW0odmlyYWxfZ29sZGVuKQoKdmlyYWxfZ29sZGVuX3BvcyA9IGdncGxvdCh2aXJhbF9nb2xkZW4sIGFlcyh4ID0gcG9zLCB5ID0gcmVmLCBjb2xvciA9IGFsdCkpICsKICBnZW9tX3BvaW50KCkKcHJpbnQodmlyYWxfZ29sZGVuX3BvcykKClRzVHYodmlyYWxfZ29sZGVuKQpgYGAKCiMjU2V0QUYgLSBTZXBhcmF0aW5nIERhdGEgaW50byBUUCwgRlAsIEZOCmBgYHtyfQpBVl9zZXRBRiA9IGZpbHRlcihBViwgYWxsZWxlX2ZyZXEgIT0gInJhbmRvbSIpICU+JSBkcm9wbGV2ZWxzKCkKCkFWX3NldEFGX2ZhbHNlcG9zID0gZmlsdGVyKEFWX3NldEFGLCBhZl9nb2xkZW4gPT0gMCAmIGFmX3dvcmtmbG93ICE9IDApICU+JSBkcm9wbGV2ZWxzKCkKICBBVl9zZXRBRl9mYWxzZXBvcyRjYXRlZ29yeSA9IGMoIkZQIikKQVZfc2V0QUZfZmFsc2VuZWcgPSBmaWx0ZXIoQVZfc2V0QUYsIGFmX3dvcmtmbG93ID09IDAgJiBhZl9nb2xkZW4gIT0gMCkgJT4lIGRyb3BsZXZlbHMoKQogIEFWX3NldEFGX2ZhbHNlbmVnJGNhdGVnb3J5ID0gYygiRk4iKQpBVl9zZXRBRl90cnVlcG9zID0gZmlsdGVyKEFWX3NldEFGLCBhZl9nb2xkZW4gIT0gMCAmIGFmX3dvcmtmbG93ICE9IDApCiAgQVZfc2V0QUZfdHJ1ZXBvcyRjYXRlZ29yeSA9IGMoIlRQIikKICAKQVZfc2V0QUYgPSByYmluZChBVl9zZXRBRl90cnVlcG9zLCBBVl9zZXRBRl9mYWxzZW5lZywgQVZfc2V0QUZfZmFsc2Vwb3MpCgpGUF9BbGwgPSBncm91cF9ieShBVl9zZXRBRl9mYWxzZXBvcywgYWxsZWxlX2ZyZXEsY292ZXJhZ2UsdG9vbCkgJT4lIHRhbGx5KCkKICBjb2xuYW1lcyhGUF9BbGwpID0gYygiYWxsZWxlX2ZyZXEiLCJjb3ZlcmFnZSIsInRvb2wiLCJGUCIpCkZOX0FsbCA9IGdyb3VwX2J5KEFWX3NldEFGX2ZhbHNlbmVnLCBhbGxlbGVfZnJlcSxjb3ZlcmFnZSx0b29sKSAlPiUgdGFsbHkoKQogIGNvbG5hbWVzKEZOX0FsbCkgPSBjKCJhbGxlbGVfZnJlcSIsImNvdmVyYWdlIiwidG9vbCIsIkZOIikKVFBfQWxsID0gZ3JvdXBfYnkoQVZfc2V0QUZfdHJ1ZXBvcywgYWxsZWxlX2ZyZXEsY292ZXJhZ2UsdG9vbCkgJT4lIHRhbGx5KCkKICBjb2xuYW1lcyhUUF9BbGwpID0gYygiYWxsZWxlX2ZyZXEiLCJjb3ZlcmFnZSIsInRvb2wiLCJUUCIpCiAgICAKQWxsVmFyID0gbWVyZ2UoRlBfQWxsLEZOX0FsbCwgYnk9YygiYWxsZWxlX2ZyZXEiLCJjb3ZlcmFnZSIsInRvb2wiKSxhbGw9VCkKQWxsVmFyID0gbWVyZ2UoQWxsVmFyLFRQX0FsbCwgYnk9YygiYWxsZWxlX2ZyZXEiLCJjb3ZlcmFnZSIsInRvb2wiKSwgYWxsPVQpCmhlYWQoQWxsVmFyKQoKQWxsVmFyW2lzLm5hKEFsbFZhcildID0gMApoZWFkKEFsbFZhcikKCkFsbFZhciRwcmVjID0gKEFsbFZhciRUUCkvKEFsbFZhciRUUCArIEFsbFZhciRGUCkKQWxsVmFyJHJlY2FsbCA9IChBbGxWYXIkVFApLyhBbGxWYXIkVFAgKyBBbGxWYXIkRk4pCkFsbFZhciRhcmVhID0gQWxsVmFyJHByZWMgKiBBbGxWYXIkcmVjYWxsCmhlYWQoQWxsVmFyKQpgYGAKCiMjUmFuZG9tQUYgLSBTZXBhcmF0aW5nIERhdGEgaW50byBUUCwgRlAsIEZOCmBgYHtyfQpBVl9yYW5kb21BRiA9IGZpbHRlcihBViwgYWxsZWxlX2ZyZXEgPT0gInJhbmRvbSIpICU+JSBkcm9wbGV2ZWxzKCkKCkFWX3JhbmRvbUFGX2ZhbHNlcG9zID0gZmlsdGVyKEFWX3JhbmRvbUFGLCBhZl9nb2xkZW4gPT0gMCAmIGFmX3dvcmtmbG93ICE9IDApICU+JSBkcm9wbGV2ZWxzKCkKICBBVl9yYW5kb21BRl9mYWxzZXBvcyRjYXRlZ29yeSA9IGMoIkZQIikKQVZfcmFuZG9tQUZfZmFsc2VuZWcgPSBmaWx0ZXIoQVZfcmFuZG9tQUYsIGFmX3dvcmtmbG93ID09IDAgJiBhZl9nb2xkZW4gIT0gMCkgJT4lIGRyb3BsZXZlbHMoKQogIEFWX3JhbmRvbUFGX2ZhbHNlbmVnJGNhdGVnb3J5ID0gYygiRk4iKQpBVl9yYW5kb21BRl90cnVlcG9zID0gZmlsdGVyKEFWX3JhbmRvbUFGLCBhZl9nb2xkZW4gIT0gMCAmIGFmX3dvcmtmbG93ICE9IDApCiAgQVZfcmFuZG9tQUZfdHJ1ZXBvcyRjYXRlZ29yeSA9IGMoIlRQIikKICAKQVZfcmFuZG9tQUYgPSByYmluZChBVl9yYW5kb21BRl90cnVlcG9zLCBBVl9yYW5kb21BRl9mYWxzZW5lZywgQVZfcmFuZG9tQUZfZmFsc2Vwb3MpCgpyRlBfQWxsID0gZ3JvdXBfYnkoQVZfcmFuZG9tQUZfZmFsc2Vwb3MsIGFsbGVsZV9mcmVxLGNvdmVyYWdlLHRvb2wpICU+JSB0YWxseSgpCiAgY29sbmFtZXMockZQX0FsbCkgPSBjKCJhbGxlbGVfZnJlcSIsImNvdmVyYWdlIiwidG9vbCIsIkZQIikKckZOX0FsbCA9IGdyb3VwX2J5KEFWX3JhbmRvbUFGX2ZhbHNlbmVnLCBhbGxlbGVfZnJlcSxjb3ZlcmFnZSx0b29sKSAlPiUgdGFsbHkoKQogIGNvbG5hbWVzKHJGTl9BbGwpID0gYygiYWxsZWxlX2ZyZXEiLCJjb3ZlcmFnZSIsInRvb2wiLCJGTiIpCnJUUF9BbGwgPSBncm91cF9ieShBVl9yYW5kb21BRl90cnVlcG9zLCBhbGxlbGVfZnJlcSxjb3ZlcmFnZSx0b29sKSAlPiUgdGFsbHkoKQogIGNvbG5hbWVzKHJUUF9BbGwpID0gYygiYWxsZWxlX2ZyZXEiLCJjb3ZlcmFnZSIsInRvb2wiLCJUUCIpCgpyQWxsVmFyID0gbWVyZ2UockZQX0FsbCxyRk5fQWxsLCBieT1jKCJhbGxlbGVfZnJlcSIsImNvdmVyYWdlIiwidG9vbCIpLGFsbD1UKQpyQWxsVmFyID0gbWVyZ2UockFsbFZhciwgclRQX0FsbCwgYnk9YygiYWxsZWxlX2ZyZXEiLCJjb3ZlcmFnZSIsInRvb2wiKSwgYWxsPVQpCmhlYWQockFsbFZhcikKCnJBbGxWYXJbaXMubmEockFsbFZhcildID0gMAoKckFsbFZhciRwcmVjID0gKHJBbGxWYXIkVFApLyhyQWxsVmFyJFRQICsgckFsbFZhciRGUCkKckFsbFZhciRyZWNhbGwgPSAockFsbFZhciRUUCkvKHJBbGxWYXIkVFAgKyByQWxsVmFyJEZOKQpyQWxsVmFyJGFyZWEgPSByQWxsVmFyJHByZWMgKiByQWxsVmFyJHJlY2FsbApoZWFkKHJBbGxWYXIpCmBgYAoKIzEuIFByZWNpc2lvbl9SZWNhbGwgQ3VydmVzCmBgYHtyfQpQUl9BbGxWYXJTZXRfZnJlcSA9IGdncGxvdChBbGxWYXIsYWVzKHg9cmVjYWxsLCB5PXByZWMsIGdyb3VwID0gdG9vbCwgY29sb3IgPSB0b29sKSkgKwogIGdlb21fcG9pbnQoKSArIAogIGdlb21fbGluZSgpICsKICBmYWNldF93cmFwKH5hbGxlbGVfZnJlcSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBNeUNvbG9ycykKcHJpbnQoUFJfQWxsVmFyU2V0X2ZyZXEpCmdnc2F2ZSgiUFJfQWxsVmFyU2V0X2J5QUYucG5nIixQUl9BbGxWYXJTZXRfZnJlcSkKClBSX0FsbFZhclNldF9jb3ZlciA9IGdncGxvdChBbGxWYXIsYWVzKHg9cmVjYWxsLCB5PXByZWMsIGdyb3VwID0gdG9vbCwgY29sb3I9dG9vbCkpICsKICBnZW9tX3BvaW50KCkgKyAKICBnZW9tX2xpbmUoKSArCiAgZmFjZXRfd3JhcCh+Y292ZXJhZ2UpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gTXlDb2xvcnMpCnByaW50KFBSX0FsbFZhclNldF9jb3ZlcikKZ2dzYXZlKCJQUl9BbGxWYXJTZXRfYnlDb3YucG5nIixQUl9BbGxWYXJTZXRfY292ZXIpCgpQUl9BbGxWYXJSYW5kb20gPSBnZ3Bsb3QockFsbFZhcixhZXMoeD1yZWNhbGwsIHk9cHJlYywgZ3JvdXAgPSB0b29sLCBjb2xvcj10b29sKSkgKwogIGdlb21fcG9pbnQoKSArIAogIGdlb21fbGluZSgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gTXlDb2xvcnMpCnByaW50KFBSX0FsbFZhclJhbmRvbSkKZ2dzYXZlKCJQUl9BbGxWYXJSYW5kb20ucG5nIixQUl9BbGxWYXJSYW5kb20pCgojIEV4YW1pbmluZyBEYXRhCgpBbGxWYXJfUFJfemVyb3MgPSBmaWx0ZXIoQWxsVmFyLCBwcmVjID09IDAgJiByZWNhbGwgPT0gMCkKQWxsVmFyX1BSX2hpZ2ggPSBmaWx0ZXIoQWxsVmFyLCBwcmVjID4gMC45ICYgcmVjYWxsID4gMC45KQoKQWxsVmFyX1BSX2hpZ2hlc3QgPSBncm91cF9ieShBbGxWYXIsIHRvb2wsIGFsbGVsZV9mcmVxLCBjb3ZlcmFnZSkgJT4lCiAgYXJyYW5nZShhcmVhLCBkZWNyZWFzaW5nID0gVFJVRSkKd3JpdGUuY3N2KEFsbFZhcl9QUl9oaWdoZXN0LCBmaWxlID0gIkFsbFZhcl9QUl9UaHJlc2hvbGRzLmNzdiIpCmBgYAoKIzIuMSBTZXRBRiAtIFByb3BvcnRpb24gb2YgVmFyaWFudHMgRm91bmQKYGBge3J9CkFWX3NldEFGX2NvdW50cyA9IGdyb3VwX2J5KEFWX3NldEFGX3RydWVwb3MsIHRvb2wsIGFsbGVsZV9mcmVxLCBjb3ZlcmFnZSkgJT4lIHRhbGx5KCkKQVZfc2V0QUZfY291bnRzJHByb3BvcnRpb24gPSAoQVZfc2V0QUZfY291bnRzJG4gLyBucm93KHZpcmFsX2dvbGRlbikpCgpBVl9zZXRBRl9wcm9wb3J0aW9uID0gZ2dwbG90KEFWX3NldEFGX2NvdW50cyxhZXMoeCA9IGxvZzEwKGNvdmVyYWdlKSwgeSA9IHByb3BvcnRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gdG9vbCwgY29sb3IgPSB0b29sKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIGZhY2V0X2dyaWQofmFsbGVsZV9mcmVxKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IE15Q29sb3JzKQpwcmludChBVl9zZXRBRl9wcm9wb3J0aW9uKQpnZ3NhdmUoIkFsbFZhclNldF9Qcm9wb3J0aW9uLnBuZyIsIEFWX3NldEFGX3Byb3BvcnRpb24pCmBgYAoKIzIuMiBSYW5kb21BRiAtIFByb3BvcnRpb24gb2YgVmFyaWFudHMgRm91bmQKYGBge3J9CkFWX3JhbmRvbUFGX2NvdW50cyA9IGdyb3VwX2J5KEFWX3JhbmRvbUFGX3RydWVwb3MsIHRvb2wsIGFsbGVsZV9mcmVxLCBjb3ZlcmFnZSkgJT4lIHRhbGx5KCkKQVZfcmFuZG9tQUZfY291bnRzJHByb3BvcnRpb24gPSAoQVZfcmFuZG9tQUZfY291bnRzJG4gLyBucm93KHZpcmFsX2dvbGRlbikpCgpBVl9yYW5kb21BRl9wcm9wb3J0aW9uID0gZ2dwbG90KEFWX3JhbmRvbUFGX2NvdW50cywgYWVzKHggPSBsb2cxMChjb3ZlcmFnZSksIHkgPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IHRvb2wsIGNvbG9yID0gdG9vbCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gTXlDb2xvcnMpCnByaW50KEFWX3JhbmRvbUFGX3Byb3BvcnRpb24pCmdnc2F2ZSgiQWxsVmFyUmFuZG9tX1Byb3BvcnRpb24ucG5nIiwgQVZfcmFuZG9tQUZfcHJvcG9ydGlvbikKIAojIyBPbmx5IGxvb2tpbmcgYXQgY292ZXJhZ2UgYmV0d2VlbiAxMDAtMTAwMApBVl9yYW5kb21BRl90cnVlcG9zX2YgPSBmaWx0ZXIoQVZfcmFuZG9tQUZfdHJ1ZXBvcywgY292ZXJhZ2UgPiA5OSAmIGNvdmVyYWdlIDwgMTAwMSkKQVZfcmFuZG9tQUZfY291bnRzX2YgPSBncm91cF9ieShBVl9yYW5kb21BRl90cnVlcG9zX2YsIHRvb2wsIGFsbGVsZV9mcmVxLCBjb3ZlcmFnZSkgJT4lIHRhbGx5KCkKQVZfcmFuZG9tQUZfY291bnRzX2YkcHJvcG9ydGlvbiA9IChBVl9yYW5kb21BRl9jb3VudHNfZiRuIC8gbnJvdyh2aXJhbF9nb2xkZW4pKQogCkFWX3JhbmRvbUFGX3Byb3BvcnRpb25fZiA9IGdncGxvdChBVl9yYW5kb21BRl9jb3VudHNfZiwgYWVzKHggPSBsb2cxMChjb3ZlcmFnZSksIHkgPSBwcm9wb3J0aW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gdG9vbCwgY29sb3IgPSB0b29sKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBNeUNvbG9ycykKcHJpbnQoQVZfcmFuZG9tQUZfcHJvcG9ydGlvbl9mKQpnZ3NhdmUoIkFsbFZhclJhbmRvbV9Qcm9wb3J0aW9uX0ZpbHRlcmVkLnBuZyIsIEFWX3JhbmRvbUFGX3Byb3BvcnRpb25fZikKYGBgCgojMy4xIFNldEFGIC0gQ29ycmVsYXRpb24gQmV0d2VlbiBHb2xkZW4gYW5kIFdvcmtmbG93CmBgYHtyfQojIyBPbmx5IGxvb2tpbmcgYXQgdHJ1ZSBwb3NpdGl2ZXMKQVZfQ29ycl9zZXRBRiA9IGdncGxvdChBVl9zZXRBRiwgYWVzKHggPSBhZl9nb2xkZW4sIHkgPSBhZl93b3JrZmxvdywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB0b29sLCBzaGFwZSA9IGNhdGVnb3J5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IE15Q29sb3JzKSArCiAgZ2VvbV9ib3hwbG90KGFlcyhncm91cCA9IGFsbGVsZV9mcmVxKSkgKyAKICBmYWNldF9ncmlkKHRvb2x+Y292ZXJhZ2UpICsKICB5bGltKC0wLjAwMSwxLjApICsgeGxpbSgtMC4wMDEsMS4wKQpwcmludChBVl9Db3JyX3NldEFGKQpnZ3NhdmUoIkFsbFZhclNldF9Db3JyZWxhdGlvbi5wbmciLEFWX0NvcnJfc2V0QUYpCgojI0xvb2tpbmcgYXQgYWxsIFNldEFGIERhdGEKQVZfQ29ycl9zZXRBRiA9IGdncGxvdChBVl9zZXRBRiwgYWVzKHggPSBhZl9nb2xkZW4sIHkgPSBhZl93b3JrZmxvdywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdG9vbCwgZ3JvdXAgPSB0b29sLCBzaGFwZSA9IGNhdGVnb3J5KSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC40KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IE15Q29sb3JzKSArCiAgZmFjZXRfZ3JpZCh+Y292ZXJhZ2UpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgc2l6ZSA9IDEuNSkgKwogIHlsaW0oLTAuMDAxLDEuMCkgKyB4bGltKC0wLjAwMSwxLjApCnByaW50KEFWX0NvcnJfc2V0QUYpCmdnc2F2ZSgiQWxsVmFyU2V0X0NvcnJlbGF0aW9uX0ZpbHRlcmVkLnBuZyIsQVZfQ29ycl9zZXRBRikKCkFWX0NvcnJfc2V0QUZfWm9vbSA9IGdncGxvdChBVl9zZXRBRiwgYWVzKHggPSBhZl9nb2xkZW4sIHkgPSBhZl93b3JrZmxvdywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHRvb2wsIGdyb3VwID0gdG9vbCwgc2hhcGUgPSBjYXRlZ29yeSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBNeUNvbG9ycykgKwogIGZhY2V0X2dyaWQofmNvdmVyYWdlKSArCiAgeWxpbSgtMC4wMSwwLjE1KSArIHhsaW0oLTAuMDEsMC4xNSkKcHJpbnQoQVZfQ29ycl9zZXRBRl9ab29tKQpnZ3NhdmUoIkFsbFZhclNldF9Db3JyZWxhdGlvbkZfRmlsdGVyZWRfWm9vbS5wbmciLCBBVl9Db3JyX3NldEFGX1pvb20pCmBgYAoKIzMuMiBSYW5kb21BRiAtIENvcnJlbGF0aW9uIEJldHdlZW4gR29sZGVuIGFuZCBXb3JrZmxvdwpgYGB7cn0KIyMgT25seSBsb29raW5nIGF0IHRydWUgcG9zaXRpdmVzCkFWX0NvcnJfcmFuZG9tQUYgPSBnZ3Bsb3QoQVZfcmFuZG9tQUYsIGFlcyh4ID0gYWZfZ29sZGVuLCB5ID0gYWZfd29ya2Zsb3csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdG9vbCwgc2hhcGUgPSBjYXRlZ29yeSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBNeUNvbG9ycykgKwogIGZhY2V0X2dyaWQodG9vbH5jb3ZlcmFnZSkgKwogIHlsaW0oLTAuMDAxLDEuMCkgKyB4bGltKC0wLjAwMSwxLjApCnByaW50KEFWX0NvcnJfcmFuZG9tQUYpCmdnc2F2ZSgiQWxsVmFyUmFuZG9tX0NvcnJlbGF0aW9uLnBuZyIsQVZfQ29ycl9yYW5kb21BRikKCiMjTG9va2luZyBhdCBhbGwgUmFuZG9tQUYgRGF0YQpBVl9Db3JyX3JhbmRvbUFGID0gZ2dwbG90KEFWX3JhbmRvbUFGLCBhZXMoeCA9IGFmX2dvbGRlbiwgeSA9IGFmX3dvcmtmbG93LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB0b29sLCBncm91cCA9IHRvb2wsIHNoYXBlID0gY2F0ZWdvcnkpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjQpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gTXlDb2xvcnMpICsKICBmYWNldF9ncmlkKH5jb3ZlcmFnZSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0nbG0nLCBzaXplID0gMS41KSArCiAgeWxpbSgtMC4wMSwxKSArIHhsaW0oLTAuMDEsMSkKcHJpbnQoQVZfQ29ycl9yYW5kb21BRikKZ2dzYXZlKCJBbGxWYXJSYW5kb21fQ29ycmVsYXRpb25fRmlsdGVyZWQucG5nIixBVl9Db3JyX3JhbmRvbUFGKQoKQVZfQ29ycl9yYW5kb21BRl9ab29tID0gZ2dwbG90KEFWX3JhbmRvbUFGLCBhZXMoeCA9IGFmX2dvbGRlbiwgeSA9IGFmX3dvcmtmbG93LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gdG9vbCwgZ3JvdXAgPSB0b29sLCBzaGFwZSA9IGNhdGVnb3J5KSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC40KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IE15Q29sb3JzKSArCiAgZmFjZXRfZ3JpZCh+Y292ZXJhZ2UpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgc2l6ZSA9IDEuNSkgKwogIHlsaW0oLTAuMDEsMC4xNSkgKyB4bGltKC0wLjAxLDAuMTUpCnByaW50KEFWX0NvcnJfcmFuZG9tQUZfWm9vbSkKZ2dzYXZlKCJBbGxWYXJSYW5kb21fQ29ycmVsYXRpb25fRmlsdGVyZWRfWm9vbS5wbmciLCBBVl9Db3JyX3JhbmRvbUFGX1pvb20pCmBgYAoKIyMgU3RhdHMgZm9yIFNldEFGIERhdGEKYGBge3J9CmNvZWZmX3ZhciA8LSBmdW5jdGlvbih4KSB7CiAgQ1YgPC0gc2QoeCkgLyBtZWFuKHgpICogMTAwCiAgcmV0dXJuKENWKQp9CgpBVl9zZXRBRl9zcGxpdCA9IGdyb3VwX2J5KEFWX3NldEFGX3RydWVwb3MsIHRvb2wsIGFsbGVsZV9mcmVxLCBjb3ZlcmFnZSkgJT4lCiAgc3VtbWFyaXplKG1lYW4oYWZfd29ya2Zsb3cpLCBzZChhZl93b3JrZmxvdyksIGNvZWZmX3ZhcihhZl93b3JrZmxvdykpICU+JSBkcm9wbGV2ZWxzKCkKY29sbmFtZXMoQVZfc2V0QUZfc3BsaXQpID0gYygidG9vbCIsIkFGIiwiY292IiwibWVhbiIsInNkIiwidmFyaWFuY2UiKQoKTWVhbl9zZXRBRiA9IGdncGxvdChBVl9zZXRBRl9zcGxpdCwgYWVzKHggPSB0b29sLCB5ID0gbWVhbiwgY29sb3IgPSBjb3YpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyh0b29sKSwgY29scyA9IHZhcnMoQUYpKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKcHJpbnQoTWVhbl9zZXRBRikKZ2dzYXZlKCJTZXRBRl9NZWFuX0FsbGVsZUZyZXF1ZW5jeS5wbmciLE1lYW5fc2V0QUYpCgpDb2VmZlZhcl9zZXRBRiA9IGdncGxvdChBVl9zZXRBRl9zcGxpdCwgYWVzKHggPSB0b29sLCB5ID0gdmFyaWFuY2UsIGNvbG9yID0gY292KSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQocm93cyA9IHZhcnModG9vbCksIGNvbHMgPSB2YXJzKEFGKSkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCnByaW50KENvZWZmVmFyX3NldEFGKQpnZ3NhdmUoIlNldEFGX0NvZWZmVmFyX0FsbGVsZUZyZXF1ZW5jeS5wbmciLENvZWZmVmFyX3NldEFGKQpgYGAKCgojIyBTZXQgVXAgZm9yIFN0YXRzIGZvciBSYW5kb21BRiBEYXRhCmBgYHtyfQpBVl9yYW5kb21BRl9zcGxpdCA9IGdyb3VwX2J5KEFWX3JhbmRvbUFGX3RydWVwb3MsIHRvb2wsIGNvdmVyYWdlKSAlPiUKICBzdW1tYXJpemUobWVhbihhZl93b3JrZmxvdyksIHNkKGFmX3dvcmtmbG93KSwgY29lZmZfdmFyKGFmX3dvcmtmbG93KSkgJT4lIGRyb3BsZXZlbHMoKQpjb2xuYW1lcyhBVl9yYW5kb21BRl9zcGxpdCkgPSBjKCJ0b29sIiwiY292IiwibWVhbiIsInNkIiwidmFyaWFuY2UiKQoKTWVhbl9yYW5kb21BRiA9IGdncGxvdChBVl9yYW5kb21BRl9zcGxpdCwgYWVzKHggPSB0b29sLCB5ID0gbWVhbiwgY29sb3IgPSBjb3YpKSArIAogIGdlb21fcG9pbnQoKQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCnByaW50KE1lYW5fcmFuZG9tQUYpCmdnc2F2ZSgiUmFuZG9tQUZfTWVhbl9BbGxlbGVGcmVxdWVuY3kucG5nIixNZWFuX3JhbmRvbUFGKQoKQ29lZmZWYXJfcmFuZG9tQUYgPSBnZ3Bsb3QoQVZfcmFuZG9tQUZfc3BsaXQsIGFlcyh4ID0gdG9vbCwgeSA9IHZhcmlhbmNlLCBjb2xvciA9IGNvdikpICsgCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQpwcmludChDb2VmZlZhcl9yYW5kb21BRikKZ2dzYXZlKCJSYW5kb21BRl9Db2VmZlZhcl9BbGxlbGVGcmVxdWVuY3kucG5nIixDb2VmZlZhcl9yYW5kb21BRikKYGBgCgojNC4gTG9vayBhdCBob3cgZWFjaCB0b29sIGNvbXBhcmVzIGF0IGVhY2ggQUYgYW5kIHNlcS1kZXB0aApgYGB7cn0KQVZTZXRfQ29yciA9IGdncGxvdChBVl9zZXRBRl90cnVlcG9zLCBhZXMoeCA9IHRvb2wsIHkgPSBhZl93b3JrZmxvdykpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2JveHBsb3QoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpICsKICBmYWNldF9ncmlkKGFsbGVsZV9mcmVxfmNvdmVyYWdlLCBzY2FsZXMgPSAiZnJlZV95IikKcHJpbnQoQVZTZXRfQ29ycikKZ2dzYXZlKCJBbGxWYXJTZXRfVG9vbF9Db21wYXJpc29uLnBkZiIsIEFWU2V0X0NvcnIsIHdpZHRoID0gMTUsIGhlaWdodCA9IDIwKQojIHkgYXhlcyBhcmUgc2V0IHBlciByb3cgYXQgdGhlIG1vbWVudCAoc2NhbGVzID0gImZyZWVfeSIpCgpBVlJhbmRvbV9Db3JyID0gZ2dwbG90KEFWX3JhbmRvbUFGX3RydWVwb3MsIGFlcyh4ID0gdG9vbCwgeSA9IGFmX3dvcmtmbG93KSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdmlvbGluKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKSArCiAgZmFjZXRfZ3JpZChhbGxlbGVfZnJlcX5jb3ZlcmFnZSkKcHJpbnQoQVZSYW5kb21fQ29ycikKZ2dzYXZlKCJBbGxWYXJSYW5kb21fVG9vbF9Db21wYXJpc29uLnBkZiIsIEFWUmFuZG9tX0NvcnIsIHdpZHRoID0gMTUsIGhlaWdodCA9IDIwKQpgYGAKCiM1LjEuIExvb2tzIGF0IHdoZXJlIHRoZSBTTlBTIGFyZSBsb2NhdGVkIHRocm91Z2hvdXQgdGhlIGdlbm9tZQpgYGB7cn0KIyBTZXQgQUYgRGF0YQpBVl9Qb3MgPSBnZ3Bsb3QoQVZfc2V0QUZfdHJ1ZXBvcywgYWVzKHggPSBwb3MsIHkgPSBhZl93b3JrZmxvdykpICsKICBnZW9tX3BvaW50KCkKcHJpbnQoQVZfUG9zKQpBVl9Qb3NfZmFsc2Vwb3MgPSBnZ3Bsb3QoQVZfc2V0QUZfZmFsc2Vwb3MsIGFlcyh4ID0gcG9zLCB5ID0gYWZfd29ya2Zsb3cpKSArCiAgZ2VvbV9wb2ludCgpCnByaW50KEFWX1Bvc19mYWxzZXBvcykKQVZfUG9zX2ZhbHNlbmVnID0gZ2dwbG90KEFWX3NldEFGX2ZhbHNlbmVnLCBhZXMoeCA9IHBvcywgeSA9IGFmX3dvcmtmbG93KSkgKwogIGdlb21fcG9pbnQoKQpwcmludChBVl9Qb3NfZmFsc2VuZWcpCgojIFJhbmRvbSBBRiBEYXRhCkFWX1Bvc19yID0gZ2dwbG90KEFWX3JhbmRvbUFGX3RydWVwb3MsIGFlcyh4ID0gcG9zLCB5ID0gYWZfd29ya2Zsb3cpKSArCiAgZ2VvbV9wb2ludCgpCnByaW50KEFWX1Bvc19yKQpBVl9Qb3NfZmFsc2Vwb3NfciA9IGdncGxvdChBVl9yYW5kb21BRl9mYWxzZXBvcywgYWVzKHggPSBwb3MsIHkgPSBhZl93b3JrZmxvdykpICsKICBnZW9tX3BvaW50KCkKcHJpbnQoQVZfUG9zX2ZhbHNlcG9zX3IpCkFWX1Bvc19mYWxzZW5lZ19yID0gZ2dwbG90KEFWX3JhbmRvbUFGX2ZhbHNlbmVnLCBhZXMoeCA9IHBvcywgeSA9IGFmX3dvcmtmbG93KSkgKwogIGdlb21fcG9pbnQoKQpwcmludChBVl9Qb3NfZmFsc2VuZWdfcikKYGBgCgojNS4yLlRyYW5zaXRpb25zL1RyYW5zdmVyc2lvbnMKYGBge3J9CnNldEFGX1RQX3BvcyA9IGdncGxvdChBVl9zZXRBRl90cnVlcG9zLCBhZXMoeCA9IHBvcywgeSA9IHJlZiwgY29sb3IgPSB0b29sLCBzaGFwZSA9IGFsdCkpICsKICBnZW9tX3BvaW50KCkKcHJpbnQoc2V0QUZfVFBfcG9zKQoKVHNUdihBVl9zZXRBRl90cnVlcG9zKQojY291bnRzIHZhcmlhbnRzIGZyb20gYWxsIHRvb2xzL3NlcV9kZXB0aC9BRiB0cmlhbHMKCnJhbmRvbUFGX1RQX3BvcyA9IGdncGxvdChBVl9yYW5kb21BRl90cnVlcG9zLCBhZXMoeCA9IHBvcywgeSA9IHJlZiwgY29sb3IgPSB0b29sLCBzaGFwZSA9IGFsdCkpICsKICBnZW9tX3BvaW50KCkKcHJpbnQocmFuZG9tQUZfVFBfcG9zKQoKVHNUdihBVl9yYW5kb21BRl90cnVlcG9zKQpgYGAK