1 Differential Expression, Macrophage: 20180822

2 Differential expression analyses

It appears that it is possible though somewhat difficult to apply batch estimations generated by sva to the model given to DESeq/EdgeR/limma. In the case of limma it is fairly simple, but in the other two it is a bit more difficult. There is a nice discussion of this at: https://www.biostars.org/p/156186/ I am attempting to apply that logic to this data with limited success.

3 Table S2 and Figure 1b, Table S3

  • Table S2 is taking only the DESeq2 results.
  • Figure 1c is intended to be a volcano plot of the DESeq2 results.
##                 ensembltranscriptid   ensemblgeneid hgncsymbol
## ENSG00000000003     ENST00000373020 ENSG00000000003     TSPAN6
## ENSG00000000005     ENST00000373031 ENSG00000000005       TNMD
## ENSG00000000419     ENST00000371582 ENSG00000000419       DPM1
## ENSG00000000457     ENST00000367770 ENSG00000000457      SCYL3
## ENSG00000000460     ENST00000286031 ENSG00000000460   C1orf112
## ENSG00000000938     ENST00000374003 ENSG00000000938        FGR
##                                                                                                    description
## ENSG00000000003                                              tetraspanin 6 [Source:HGNC Symbol;Acc:HGNC:11858]
## ENSG00000000005                                                tenomodulin [Source:HGNC Symbol;Acc:HGNC:17757]
## ENSG00000000419 dolichyl-phosphate mannosyltransferase subunit 1, catalytic [Source:HGNC Symbol;Acc:HGNC:3005]
## ENSG00000000457                                   SCY1 like pseudokinase 3 [Source:HGNC Symbol;Acc:HGNC:19285]
## ENSG00000000460                        chromosome 1 open reading frame 112 [Source:HGNC Symbol;Acc:HGNC:25565]
## ENSG00000000938              FGR proto-oncogene, Src family tyrosine kinase [Source:HGNC Symbol;Acc:HGNC:3697]
##                    genebiotype deseq_logfc deseq_adjp deseq_basemean
## ENSG00000000003 protein_coding    -0.98470     1.0000          1.444
## ENSG00000000005 protein_coding     0.00000     1.0000          0.000
## ENSG00000000419 protein_coding     0.08404     0.5659        417.900
## ENSG00000000457 protein_coding    -0.11690     0.6087        205.300
## ENSG00000000460 protein_coding    -0.20640     0.3532        176.700
## ENSG00000000938 protein_coding     0.43850     0.2213       5293.000
##                 deseq_lfcse deseq_stat deseq_p deseq_adjp_fdr
## ENSG00000000003     1.24100    -0.7937 0.42730      1.000e+00
## ENSG00000000005     0.00000     0.0000 1.00000      1.000e+00
## ENSG00000000419     0.08866     0.9479 0.34320      1.000e+00
## ENSG00000000457     0.13560    -0.8624 0.38850      1.000e+00
## ENSG00000000460     0.14610    -1.4130 0.15760      1.000e+00
## ENSG00000000938     0.24590     1.7830 0.07457      7.749e-01
## The color list must have 4, setting it to the default.
## Going to write the image to: images/Figure_1c.pdf when dev.off() is called.
## png 
##   2

3.2 Batch correction via ruv residuals

3.2.1 Set up ruvresiduals

## The be method chose 1 surrogate variable(s).
## Attempting ruvseq residual surrogate estimation with 1 surrogates.

## The be method chose 1 surrogate variable(s).
## Attempting ruvseq residual surrogate estimation with 1 surrogates.

3.5 Batch correction with combat

Then repeat with the batch-corrected data and see the differences.

3.5.1 Setup combat

## Error in normalize_expt(input, filter = TRUE, batch = FALSE, transform = "log2", : object 'hs_macr_combat_norm' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_combat' not found
## Error in combine_de_tables(hs_macr_combat, excel = excel_file, keepers = hs_contrasts, : object 'hs_macr_combat' not found
## Error in extract_significant_genes(hs_macr_combat_tables, excel = excel_file): object 'hs_macr_combat_tables' not found
## Error in extract_de_plots(pairwise = hs_macr_combat, type = "limma", table = "sh_vs_chr"): object 'hs_macr_combat' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_combat_ma_limma' not found
## Error in extract_de_plots(pairwise = hs_macr_combat, type = "edger", table = "sh_vs_chr"): object 'hs_macr_combat' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_combat_ma_edger' not found
## Error in extract_de_plots(pairwise = hs_macr_combat, type = "deseq", table = "sh_vs_chr"): object 'hs_macr_combat' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_combat_ma_deseq' not found

4 Figure out how to compare these results

I have 4 methods of performing this differential expression analysis. Each one comes with a set of metrics defining ‘significant’. Perhaps I can make a table of the # of genes defined as significant by contrast for each. In addition it may be worth while to do a scatter plots of the logFCs between these comparisons and see how well they agree?

5 Look first at the de counts

##               change_counts_up change_counts_down
## macro_chr-sh              1032                261
## macro_chr-nil              711                989
## macro_sh-nil               271               1222

5.1 Compare DeSeq / Basic without batch in model

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 280, df = 13000, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.9242 0.9290
## sample estimates:
##    cor 
## 0.9266

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 100, df = 13000, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.6618 0.6806
## sample estimates:
##    cor 
## 0.6713

5.2 Compare SVA to batch in model, DESeq

## Error in merge(hs_macr_sva$deseq$all_tables$sh_vs_chr, hs_macr_batch$deseq$all_tables$sh_vs_chr, : object 'hs_macr_sva' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_sva_batch' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_sva_batch' not found
## Error in colnames(hs_macr_sva_logfc) <- c("sva", "batch"): object 'hs_macr_sva_logfc' not found
## Error in data.frame(df[, c(1, 2)]): object 'hs_macr_sva_logfc' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_lfc_b_s' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_lfc_b_s' not found

5.2.1 Include p-value estimations

Try putting some information of the p-values with the comparative log2fc

## Error in eval(expr, envir, enclos): object 'hs_macr_sva_batch' not found
## Error in colnames(lfc_b_s) <- c("l2fcsva", "l2fcbatch", "psva", "pbatch"): object 'lfc_b_s' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_lfc_b_s' not found
## Error in ifelse(lfc_b_s$psva > cutoff & lfc_b_s$pbatch > cutoff, "bothinsig", : object 'lfc_b_s' not found
## Error in eval(expr, envir, enclos): object 'lfc_b_s' not found
## Error in eval(expr, envir, enclos): object 'lfc_b_s' not found
## Error in eval(expr, envir, enclos): object 'lfc_b_s' not found
## Error in eval(expr, envir, enclos): object 'lfc_b_s' not found
## Error in ggplot2::ggplot(lfc_b_s, aes_string(x = "l2fcsva", y = "l2fcbatch")): object 'lfc_b_s' not found
## Error in eval(expr, envir, enclos): object 'plt' not found

5.3 Compare ruvresid to batch in model, DESeq

## Warning in plot_multihistogram(df): NAs introduced by coercion
## Used Bon Ferroni corrected t test(s) between columns.

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 440, df = 13000, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.9663 0.9685
## sample estimates:
##    cor 
## 0.9674

5.4 Compare no batch to batch in model, limma

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0

## Warning in lmrob.S(x, y, control = control): find_scale() did not converge
## in 'maxit.scale' (= 200) iterations with tol=1e-10, last rel.diff=0
## Warning in outlierStats(ret, x, control): Detected possible local breakdown of SMDM-estimate in 2 coefficients 'Overall', ''.
## Use lmrob argument 'setting="KS2014"' to avoid this problem.
## Warning in plot_multihistogram(df): NAs introduced by coercion
## Used Bon Ferroni corrected t test(s) between columns.

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 130, df = 51000, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4918 0.5048
## sample estimates:
##    cor 
## 0.4983

5.5 Batch in model vs. SVA, limma

## Error in as.data.frame(y): object 'hs_macr_sva' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_batch_sva' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_batch_sva' not found
## Error in colnames(hs_macr_batch_sva) <- c("batch", "sva"): object 'hs_macr_batch_sva' not found
## Error in data.frame(df[, c(1, 2)]): object 'hs_macr_batch_sva' not found
## Error in eval(expr, envir, enclos): object 'b_s' not found
## Error in eval(expr, envir, enclos): object 'b_s' not found

5.6 Nobatch vs. batch in model, edger

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 320, df = 51000, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.8135 0.8193
## sample estimates:
##    cor 
## 0.8164

5.7 Batch in model vs. SVA, edger

## Error in as.data.frame(y): object 'hs_macr_sva' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_batch_sva' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_batch_sva' not found
## Error in colnames(hs_macr_batch_sva) <- c("batch", "sva"): object 'hs_macr_batch_sva' not found
## Error in data.frame(df[, c(1, 2)]): object 'hs_macr_batch_sva' not found
## Error in eval(expr, envir, enclos): object 'b_s' not found
## Error in eval(expr, envir, enclos): object 'b_s' not found

5.8 Compare nobatch vs. batch, deseq

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 190, df = 51000, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.6286 0.6390
## sample estimates:
##    cor 
## 0.6338

5.9 Compare batch vs. SVA, deseq

## Error in as.data.frame(y): object 'hs_macr_sva' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_batch_sva' not found
## Error in eval(expr, envir, enclos): object 'hs_macr_batch_sva' not found
## Error in colnames(hs_macr_batch_sva) <- c("batch", "sva"): object 'hs_macr_batch_sva' not found
## Error in data.frame(df[, c(1, 2)]): object 'hs_macr_batch_sva' not found
## Error in eval(expr, envir, enclos): object 'b_s' not found
## Error in eval(expr, envir, enclos): object 'b_s' not found

6 Repeat using the parasite data

In ‘macrophage_estimation’, we did a series of analyses to try to pick out some of the surrogate variables in the data. Now we will perform a set of differential expression analyses using the results from that. Since the ‘batch’ element of the data is reasonably well explained, we will not abuse the data with sva/combat, but instead include batch in the experimental model.

It appears that it is possible though somewhat difficult to apply batch estimations generated by sva to the model given to DESeq/EdgeR/limma. In the case of limma it is fairly simple, but in the other two it is a bit more difficult. There is a nice discussion of this at: https://www.biostars.org/p/156186/ I am attempting to apply that logic to this data with limited success.

7 Figure out how to compare these results

I have 4 methods of performing this differential expression analysis. Each one comes with a set of metrics defining ‘significant’. Perhaps I can make a table of the # of genes defined as significant by contrast for each. In addition it may be worth while to do a scatter plots of the logFCs between these comparisons and see how well they agree?

7.1 Compare DeSeq / Basic without batch in model

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 89, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.6805 0.7025
## sample estimates:
##    cor 
## 0.6917

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 120, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.7760 0.7922
## sample estimates:
##    cor 
## 0.7842

7.2 Compare SVA to batch in model, DESeq

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 55, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4939 0.5251
## sample estimates:
##    cor 
## 0.5097

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 25, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.2381 0.2774
## sample estimates:
##    cor 
## 0.2579

7.2.1 Include p-value estimations

Try putting some information of the p-values with the comparative log2fc

cutoff <- 0.1
lfcp_b_s$state <- ifelse(lfcp_b_s$psva > cutoff & lfcp_b_s$pbatch > cutoff, "bothinsig",
                  ifelse(lfcp_b_s$psva <= cutoff & lfcp_b_s$pbatch <= cutoff, "bothsig",
                  ifelse(lfcp_b_s$psva <= cutoff, "svasig", "batchsig")))
##lfcp_b_s$lfcstate <- ifelse(lfcp_b_s$l2fcsva >= 0.75 & lfcp_b_s$l2fcbatch, "", "")
num_bothinsig <- sum(lfcp_b_s$state == "bothinsig")
num_bothsig <- sum(lfcp_b_s$state == "bothsig")
num_svasig <- sum(lfcp_b_s$state == "svasig")
num_batchsig <- sum(lfcp_b_s$state == "batchsig")

library(ggplot2)
aes_color = "(l2fcsva >= 0.75 | l2fcsva <= -0.75 | l2fcbatch >= 0.75 | l2fcbatch <= -0.75)"
plt <- ggplot2::ggplot(lfcp_b_s, aes_string(x="l2fcsva", y="l2fcbatch")) +
    ## ggplot2::geom_point(stat="identity", size=2, alpha=0.2, aes_string(shape="as.factor(aes_color)", colour="as.factor(state)", fill="as.factor(state)")) +
    ggplot2::geom_abline(colour="blue", slope=1, intercept=0, size=0.5) +
    ggplot2::geom_hline(yintercept=c(-0.75, 0.75), color="red", size=0.5) +
    ggplot2::geom_vline(xintercept=c(-0.75, 0.75), color="red", size=0.5) +
    ggplot2::geom_point(stat="identity", size=2, alpha=0.2, aes_string(colour="as.factor(state)", fill="as.factor(state)")) +
    ggplot2::scale_color_manual(name="state", values=c("bothinsig"="grey", "bothsig"="forestgreen", "svasig"="darkred", "batchsig"="darkblue")) +
    ggplot2::scale_fill_manual(name="state", values=c("bothinsig"="grey", "bothsig"="forestgreen", "svasig"="darkred", "batchsig"="darkblue"),
                               labels=c(
                                   paste0("Both InSig.: ", num_bothinsig),
                                   paste0("Both Sig.: ", num_bothsig),
                                   paste0("Sva Sig.: ", num_svasig),
                                   paste0("Batch Sig.: ", num_batchsig)),
                               guide=ggplot2::guide_legend(override.aes=aes(size=3, fill="grey"))) +
    ggplot2::guides(fill=ggplot2::guide_legend(override.aes=list(size=3))) +
    ggplot2::theme_bw()
plt

7.3 Compare no batch to batch in model, limma

## Warning in plot_multihistogram(df): NAs introduced by coercion
## Used Bon Ferroni corrected t test(s) between columns.

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 150, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.8441 0.8558
## sample estimates:
##    cor 
## 0.8501

7.4 Batch in model vs. SVA, limma

## Warning in plot_multihistogram(df): NAs introduced by coercion
## Used Bon Ferroni corrected t test(s) between columns.

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 45, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4216 0.4556
## sample estimates:
##    cor 
## 0.4387

7.5 Nobatch vs. batch in model, edger

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 100, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.7371 0.7558
## sample estimates:
##    cor 
## 0.7466

7.6 Batch in model vs. SVA, edger

## Warning in plot_multihistogram(df): NAs introduced by coercion
## Used Bon Ferroni corrected t test(s) between columns.

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 56, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4966 0.5277
## sample estimates:
##    cor 
## 0.5123

7.7 Compare nobatch vs. batch, deseq

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 110, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.7400 0.7585
## sample estimates:
##    cor 
## 0.7494

7.8 Compare batch vs. SVA, deseq

## 
##  Pearson's product-moment correlation
## 
## data:  df[, 1] and df[, 2]
## t = 55, df = 8700, p-value <2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4939 0.5251
## sample estimates:
##    cor 
## 0.5097

R version 3.5.1 (2018-07-02)

Platform: x86_64-pc-linux-gnu (64-bit)

locale: LC_CTYPE=en_US.utf8, LC_NUMERIC=C, LC_TIME=en_US.utf8, LC_COLLATE=en_US.utf8, LC_MONETARY=en_US.utf8, LC_MESSAGES=en_US.utf8, LC_PAPER=en_US.utf8, LC_NAME=C, LC_ADDRESS=C, LC_TELEPHONE=C, LC_MEASUREMENT=en_US.utf8 and LC_IDENTIFICATION=C

attached base packages: parallel, stats, graphics, grDevices, utils, datasets, methods and base

other attached packages: ggplot2(v.3.1.0), ruv(v.0.9.7), foreach(v.1.4.4), edgeR(v.3.22.5), bindrcpp(v.0.2.2), hpgltools(v.2018.11), Biobase(v.2.40.0) and BiocGenerics(v.0.26.0)

loaded via a namespace (and not attached): R.utils(v.2.7.0), tidyselect(v.0.2.5), lme4(v.1.1-19), RSQLite(v.2.1.1), AnnotationDbi(v.1.42.1), htmlwidgets(v.1.3), grid(v.3.5.1), BiocParallel(v.1.14.2), DESeq(v.1.32.0), devtools(v.2.0.1), munsell(v.0.5.0), codetools(v.0.2-15), preprocessCore(v.1.42.0), units(v.0.6-1), statmod(v.1.4.30), withr(v.2.1.2), colorspace(v.1.3-2), GOSemSim(v.2.6.2), BiocInstaller(v.1.30.0), OrganismDbi(v.1.22.0), knitr(v.1.20), rstudioapi(v.0.8), stats4(v.3.5.1), Vennerable(v.3.1.0.9000), robustbase(v.0.93-3), DOSE(v.3.6.1), labeling(v.0.3), GenomeInfoDbData(v.1.1.0), hwriter(v.1.3.2), bit64(v.0.9-7), farver(v.1.0), rprojroot(v.1.3-2), EDASeq(v.2.14.1), R6(v.2.3.0), doParallel(v.1.0.14), GenomeInfoDb(v.1.16.0), locfit(v.1.5-9.1), bitops(v.1.0-6), fgsea(v.1.6.0), DelayedArray(v.0.6.6), assertthat(v.0.2.0), scales(v.1.0.0), ggraph(v.1.0.2), nnet(v.7.3-12), enrichplot(v.1.0.2), gtable(v.0.2.0), RUVSeq(v.1.14.0), sva(v.3.28.0), processx(v.3.2.0), rlang(v.0.3.0.1), genefilter(v.1.62.0), splines(v.3.5.1), rtracklayer(v.1.40.6), lazyeval(v.0.2.1), acepack(v.1.4.1), checkmate(v.1.8.5), yaml(v.2.2.0), reshape2(v.1.4.3), GenomicFeatures(v.1.32.3), backports(v.1.1.2), qvalue(v.2.12.0), Hmisc(v.4.1-1), clusterProfiler(v.3.8.1), RBGL(v.1.56.0), tools(v.3.5.1), usethis(v.1.4.0), gplots(v.3.0.1), RColorBrewer(v.1.1-2), blockmodeling(v.0.3.1), sessioninfo(v.1.1.1), ggridges(v.0.5.1), Rcpp(v.1.0.0), plyr(v.1.8.4), base64enc(v.0.1-3), progress(v.1.2.0), zlibbioc(v.1.26.0), purrr(v.0.2.5), RCurl(v.1.95-4.11), ps(v.1.2.1), prettyunits(v.1.0.2), rpart(v.4.1-13), viridis(v.0.5.1), cowplot(v.0.9.3), S4Vectors(v.0.18.3), SummarizedExperiment(v.1.10.1), ggrepel(v.0.8.0), cluster(v.2.0.7-1), colorRamps(v.2.3), fs(v.1.2.6), variancePartition(v.1.10.4), magrittr(v.1.5), data.table(v.1.11.8), openxlsx(v.4.1.0), DO.db(v.2.9), packrat(v.0.4.9-3), matrixStats(v.0.54.0), aroma.light(v.3.10.0), pkgload(v.1.0.2), hms(v.0.4.2), evaluate(v.0.12), xtable(v.1.8-3), pbkrtest(v.0.4-7), XML(v.3.98-1.16), IRanges(v.2.14.12), gridExtra(v.2.3), testthat(v.2.0.1), compiler(v.3.5.1), biomaRt(v.2.36.1), tibble(v.1.4.2), KernSmooth(v.2.23-15), crayon(v.1.3.4), R.oo(v.1.22.0), minqa(v.1.2.4), htmltools(v.0.3.6), mgcv(v.1.8-25), corpcor(v.1.6.9), snow(v.0.4-3), Formula(v.1.2-3), tidyr(v.0.8.2), geneplotter(v.1.58.0), DBI(v.1.0.0), tweenr(v.1.0.0), MASS(v.7.3-51.1), ShortRead(v.1.38.0), Matrix(v.1.2-15), cli(v.1.0.1), R.methodsS3(v.1.7.1), quadprog(v.1.5-5), gdata(v.2.18.0), bindr(v.0.1.1), igraph(v.1.2.2), GenomicRanges(v.1.32.7), pkgconfig(v.2.0.2), registry(v.0.5), rvcheck(v.0.1.1), GenomicAlignments(v.1.16.0), foreign(v.0.8-71), annotate(v.1.58.0), rngtools(v.1.3.1), pkgmaker(v.0.27), XVector(v.0.20.0), bibtex(v.0.4.2), doRNG(v.1.7.1), EBSeq(v.1.20.0), stringr(v.1.3.1), callr(v.3.0.0), digest(v.0.6.18), graph(v.1.58.2), Biostrings(v.2.48.0), rmarkdown(v.1.10), fastmatch(v.1.1-0), htmlTable(v.1.12), directlabels(v.2018.05.22), Rsamtools(v.1.32.3), gtools(v.3.8.1), nloptr(v.1.2.1), nlme(v.3.1-137), desc(v.1.2.0), viridisLite(v.0.3.0), limma(v.3.36.5), pillar(v.1.3.0), lattice(v.0.20-38), DEoptimR(v.1.0-8), httr(v.1.3.1), pkgbuild(v.1.0.2), survival(v.2.43-1), GO.db(v.3.6.0), glue(v.1.3.0), remotes(v.2.0.2), zip(v.1.0.0), UpSetR(v.1.3.3), iterators(v.1.0.10), pander(v.0.6.3), bit(v.1.1-14), ggforce(v.0.1.3), stringi(v.1.2.4), blob(v.1.1.1), DESeq2(v.1.20.0), doSNOW(v.1.0.16), latticeExtra(v.0.6-28), caTools(v.1.17.1.1), memoise(v.1.1.0) and dplyr(v.0.7.8)

## If you wish to reproduce this exact build of hpgltools, invoke the following:
## > git clone http://github.com/abelew/hpgltools.git
## > git reset 355f1065fb30b65f999b2e2a1aee18fdf8e6ebce
## This is hpgltools commit: Sun Nov 25 19:27:18 2018 -0500: 355f1065fb30b65f999b2e2a1aee18fdf8e6ebce
## Saving to 03_expression_macrophage_20180822-v20180822.rda.xz
LS0tCnRpdGxlOiAiTC5wYW5hbWVuc2lzIDIwMTY6IERpZmZlcmVudGlhbCBFeHByZXNzaW9uIGluIGh1bWFuIG1hY3JvcGhhZ2VzLiIKYXV0aG9yOiAiYXRiIGFiZWxld0BnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIHdpZHRoOiAzMDAKICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICB0b2NfZmxvYXQ6IHRydWUKICBCaW9jU3R5bGU6Omh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICB0b2NfZmxvYXQ6IHRydWUKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgZmlnX2hlaWdodDogNwogICAgZmlnX3dpZHRoOiA3CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogICAgdGhlbWU6IHJlYWRhYmxlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHksIHRkIHsKICBmb250LXNpemU6IDE2cHg7Cn0KY29kZS5yewogIGZvbnQtc2l6ZTogMTZweDsKfQpwcmUgewogZm9udC1zaXplOiAxNnB4Cn0KPC9zdHlsZT4KCmBgYHtyIG9wdGlvbnMsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoImhwZ2x0b29scyIpCnR0IDwtIGRldnRvb2xzOjpsb2FkX2FsbCgifi9ocGdsdG9vbHMiKQprbml0cjo6b3B0c19rbml0JHNldChwcm9ncmVzcz1UUlVFLAogICAgICAgICAgICAgICAgICAgICB2ZXJib3NlPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgIHdpZHRoPTkwLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoPTgsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0PTgsCiAgICAgICAgICAgICAgICAgICAgICBkcGk9OTYpCm9sZF9vcHRpb25zIDwtIG9wdGlvbnMoZGlnaXRzPTQsCiAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWw9ImFsbG93IikKZ2dwbG90Mjo6dGhlbWVfc2V0KGdncGxvdDI6OnRoZW1lX2J3KGJhc2Vfc2l6ZT0xMCkpCnJ1bmRhdGUgPC0gZm9ybWF0KFN5cy5EYXRlKCksIGZvcm1hdD0iJVklbSVkIikKcHJldmlvdXNfZmlsZSA8LSAiMDJfZXN0aW1hdGlvbl9tYWNyb3BoYWdlXzIwMTgwODIyLlJtZCIKdmVyIDwtICIyMDE4MDgyMiIKCnRtcCA8LSBzbShsb2FkbWUoZmlsZW5hbWU9cGFzdGUwKGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iIiwgeD1wcmV2aW91c19maWxlKSwgIi12IiwgdmVyLCAiLnJkYS54eiIpKSkKcm1kX2ZpbGUgPC0gIjAzX2V4cHJlc3Npb25fbWFjcm9waGFnZV8yMDE4MDgyMi5SbWQiCmBgYAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiwgTWFjcm9waGFnZTogYHIgdmVyYAoKIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlcwoKSXQgYXBwZWFycyB0aGF0IGl0IGlzIHBvc3NpYmxlIHRob3VnaCBzb21ld2hhdCBkaWZmaWN1bHQgdG8gYXBwbHkgYmF0Y2ggZXN0aW1hdGlvbnMgZ2VuZXJhdGVkIGJ5IHN2YQp0byB0aGUgbW9kZWwgZ2l2ZW4gdG8gREVTZXEvRWRnZVIvbGltbWEuICBJbiB0aGUgY2FzZSBvZiBsaW1tYSBpdCBpcyBmYWlybHkgc2ltcGxlLCBidXQgaW4gdGhlIG90aGVyCnR3byBpdCBpcyBhIGJpdCBtb3JlIGRpZmZpY3VsdC4gIFRoZXJlIGlzIGEgbmljZSBkaXNjdXNzaW9uIG9mIHRoaXMgYXQ6IGh0dHBzOi8vd3d3LmJpb3N0YXJzLm9yZy9wLzE1NjE4Ni8KSSBhbSBhdHRlbXB0aW5nIHRvIGFwcGx5IHRoYXQgbG9naWMgdG8gdGhpcyBkYXRhIHdpdGggbGltaXRlZCBzdWNjZXNzLgoKYGBge3Igc2V0dXBfZGVfbm9ybSwgZmlnLnNob3c9ImhpZGUifQpoc19jb250cmFzdHMgPC0gbGlzdCgKICAgICJtYWNyb19jaHItc2giID0gYygiY2hyIiwic2giKSwKICAgICJtYWNyb19jaHItbmlsIiA9IGMoImNociIsInVuaW5mIiksCiAgICAibWFjcm9fc2gtbmlsIiA9IGMoInNoIiwgInVuaW5mIikpCiMjIFNldCB1cCB0aGUgZGF0YSB1c2VkIGluIHRoZSBjb21wYXJhdGl2ZSBjb250cmFzdCBzZXRzLgpgYGAKCiMjIE5vIGJhdGNoIGluIHRoZSBtb2RlbAoKIyMjIFNldCB1cCBubyBiYXRjaAoKUHJpbnQgYSByZW1pbmRlciBvZiB3aGF0IHdlIGNhbiBleHBlY3Qgd2hlbiBkb2luZyB0aGlzIHdpdGggbm8gYmF0Y2ggaW5mb3JtYXRpb24uCgpgYGB7ciBub2JhdGNoX3NldHVwfQpoc19tYWNyX2xvd2ZpbHQgPC0gc20obm9ybWFsaXplX2V4cHQoaHNfY2RzX21hY3IsIGZpbHRlcj1UUlVFKSkKaHNfbG93ZmlsdF9wY2EgPC0gc20ocGxvdF9wY2EoaHNfY2RzX21hY3IsIHRyYW5zZm9ybT0ibG9nMiIpKQpoc19sb3dmaWx0X3BjYSRwbG90CmBgYAoKYGBge3IgbWFjcm9fbm9iYXRjaDEsIGZpZy5zaG93PSJoaWRlIn0KaHNfbWFjcl9ub2JhdGNoIDwtIHNtKGFsbF9wYWlyd2lzZShpbnB1dD1oc19jZHNfbWFjciwgbW9kZWxfYmF0Y2g9RkFMU0UsIHBhcmFsbGVsPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbW1hX21ldGhvZD0icm9idXN0IikpCiMjIHdvdywgYWxsIHRvb2xzIGluY2x1ZGluZyBiYXNpYyBhZ3JlZSBhbG1vc3QgY29tcGxldGVseQptZWRpYW5zX2J5X2NvbmRpdGlvbiA8LSBoc19tYWNyX25vYmF0Y2gkYmFzaWMkbWVkaWFucwpleGNlbF9maWxlIDwtIGdsdWU6OmdsdWUoImV4Y2VsL3tydW5kYXRlfV9oc19tYWNyX25vYmF0Y2hfY29udHItdnt2ZXJ9Lnhsc3giKQpoc19tYWNyX25vYmF0Y2hfdGFibGVzIDwtIHNtKGNvbWJpbmVfZGVfdGFibGVzKGhzX21hY3Jfbm9iYXRjaCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleGNlbD1leGNlbF9maWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtlZXBlcnM9aHNfY29udHJhc3RzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dHJhX2Fubm90PW1lZGlhbnNfYnlfY29uZGl0aW9uKSkKZXhjZWxfZmlsZSA8LSBnbHVlOjpnbHVlKCJleGNlbC97cnVuZGF0ZX1faHNfbWFjcl9ub2JhdGNoX3NpZy12e3Zlcn0ueGxzeCIpCmhzX21hY3Jfbm9iYXRjaF9zaWcgPC0gc20oZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcyhoc19tYWNyX25vYmF0Y2hfdGFibGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjZWw9ZXhjZWxfZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY29yZGluZ190bz0iYWxsIikpCmBgYAoKIyMgQmF0Y2ggaW4gdGhlIG1vZGVsCgojIyMgQmF0Y2ggc2V0dXAKCmBgYHtyIGJhdGNoX3NldHVwfQpoc19sb3dmaWx0X2JhdGNoX3BjYSA8LSBzbShwbG90X3BjYShoc19jZHNfbWFjciwgdHJhbnNmb3JtPSJsb2cyIiwgYmF0Y2g9ImxpbW1hIikpCmhzX2xvd2ZpbHRfYmF0Y2hfcGNhJHBsb3QKYGBgCgpJbiB0aGlzICBhdHRlbXB0LCB3ZSBhZGQgYSBiYXRjaCBmYWN0b3IgaW4gdGhlIGV4cGVyaW1lbnRhbCBtb2RlbCBhbmQgc2VlIGhvdyBpdCBkb2VzLgoKYGBge3IgbWFjcm9fYmF0Y2gxLCBmaWcuc2hvdz0iaGlkZSJ9CiMjIEhlcmUganVzdCBsZXQgYWxsX3BhaXJ3aXNlIHJ1biBvbiBmaWx0ZXJlZCBkYXRhIGFuZCBkbyBpdHMgbm9ybWFsIH4gMCArIGNvbmRpdGlvbiArIGJhdGNoIGFuYWx5c2VzCmhzX21hY3JfYmF0Y2ggPC0gc20oYWxsX3BhaXJ3aXNlKGlucHV0PWhzX2Nkc19tYWNyLCBsaW1tYV9tZXRob2Q9InJvYnVzdCIsIHBhcmFsbGVsPUZBTFNFKSkKbWVkaWFuc19ieV9jb25kaXRpb24gPC0gaHNfbWFjcl9iYXRjaCRiYXNpYyRtZWRpYW5zCmV4Y2VsX2ZpbGUgPC0gZ2x1ZTo6Z2x1ZSgiZXhjZWwve3J1bmRhdGV9X2hzX21hY3JfYmF0Y2htb2RlbF9jb250ci12e3Zlcn0ueGxzeCIpCmhzX21hY3JfYmF0Y2hfdGFibGVzIDwtIHNtKGNvbWJpbmVfZGVfdGFibGVzKAogIGhzX21hY3JfYmF0Y2gsCiAga2VlcGVycz1oc19jb250cmFzdHMsCiAgZXh0cmFfYW5ub3Q9bWVkaWFuc19ieV9jb25kaXRpb24sCiAgaW5jbHVkZV9saW1tYT1GQUxTRSwgaW5jbHVkZV9lZGdlcj1GQUxTRSwgaW5jbHVkZV9iYXNpYz1GQUxTRSwgaW5jbHVkZV9lYnNlcT1GQUxTRSwKICBleGNlbD1leGNlbF9maWxlKSkKZXhjZWxfZmlsZSA8LSBnbHVlOjpnbHVlKCJleGNlbC97cnVuZGF0ZX1faHNfbWFjcl9iYXRjaG1vZGVsX3NpZy12e3Zlcn0ueGxzeCIpCmhzX21hY3JfYmF0Y2hfc2lnIDwtIHNtKGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgaHNfbWFjcl9iYXRjaF90YWJsZXMsIGV4Y2VsPWV4Y2VsX2ZpbGUsCiAgYWNjb3JkaW5nX3RvPSJkZXNlcSIpKQpleGNlbF9maWxlIDwtIGdsdWU6OmdsdWUoImV4Y2VsL3tydW5kYXRlfV9oc19tYWNyX2JhdGNobW9kZWxfYWJ1bmQtdnt2ZXJ9Lnhsc3giKQpoc19tYWNyX2JhdGNoX2FidW4gPC0gc20oZXh0cmFjdF9hYnVuZGFudF9nZW5lcygKICBoc19tYWNyX2JhdGNoX3RhYmxlcywgZXhjZWw9ZXhjZWxfZmlsZSwKICBhY2NvcmRpbmdfdG89ImRlc2VxIikpCmBgYAoKIyBUYWJsZSBTMiBhbmQgRmlndXJlIDFiLCBUYWJsZSBTMwoKICogVGFibGUgUzIgaXMgdGFraW5nIG9ubHkgdGhlIERFU2VxMiByZXN1bHRzLgogKiBGaWd1cmUgMWMgaXMgaW50ZW5kZWQgdG8gYmUgYSB2b2xjYW5vIHBsb3Qgb2YgdGhlIERFU2VxMiByZXN1bHRzLgoKYGBge3IgdGFibGVfczJ9CnMyX2NvbnRyYXN0cyA8LSBsaXN0KAogICJtYWNyb19jaHItc2giID0gYygiY2hyIiwic2giKSkKZXhjZWxfZmlsZSA8LSBnbHVlOjpnbHVlKCJleGNlbC97cnVuZGF0ZX1fdGFibGUtczJfaHNfbWFjcl9iYXRjaG1vZGVsX2NvbnRyLXZ7dmVyfS54bHN4IikKdGFibGVfczIgPC0gc20oY29tYmluZV9kZV90YWJsZXMoCiAgaHNfbWFjcl9iYXRjaCwKICBleGNlbD1leGNlbF9maWxlLAogIGtlZXBlcnM9czJfY29udHJhc3RzLAogIGluY2x1ZGVfYmFzaWM9RkFMU0UsIGluY2x1ZGVfbGltbWE9RkFMU0UsCiAgaW5jbHVkZV9lYnNlcT1GQUxTRSwgaW5jbHVkZV9lZGdlcj1GQUxTRSkpCmV4Y2VsX2ZpbGUgPC0gZ2x1ZTo6Z2x1ZSgiZXhjZWwve3J1bmRhdGV9X3RhYmxlLXMzX2hzX21hY3JfYmF0Y2htb2RlbF9zaWctdnt2ZXJ9Lnhsc3giKQp0YWJsZV9zMyA8LSBzbShleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIHRhYmxlX3MyLAogIGV4Y2VsPWV4Y2VsX2ZpbGUsCiAgYWNjb3JkaW5nX3RvPSJkZXNlcSIpKQoKY2hvc2VuX3RhYmxlIDwtIHRhYmxlX3MyW1siZGF0YSJdXVtbMV1dCmhlYWQoY2hvc2VuX3RhYmxlKQp2b2wgPC0gcGxvdF92b2xjYW5vX2RlKHRhYmxlPWNob3Nlbl90YWJsZSwKICAgICAgICAgICAgICAgICAgICAgICBjb2xvcl9ieT0ic3RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgIGZjX2NvbD0iZGVzZXFfbG9nZmMiLAogICAgICAgICAgICAgICAgICAgICAgIHBfY29sPSJkZXNlcV9hZGpwIiwKICAgICAgICAgICAgICAgICAgICAgICBzaGFwZXNfYnlfc3RhdGU9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgbGluZV9wb3NpdGlvbj0idG9wIikKcHAoZmlsZT0iaW1hZ2VzL0ZpZ3VyZV8xYy5wZGYiKQp2b2wkcGxvdApkZXYub2ZmKCkKYGBgCgojIyBCYXRjaCBlc3RpbWF0ZWQgd2l0aCBTVkEKCiMjIyBTZXQgdXAgc3ZhCgpgYGB7ciBzZXR1cF9zdmF9CmhzX2xvd2ZpbHRfc3Zhc2VxX3BjYSA8LSBzbShwbG90X3BjYShoc19jZHNfbWFjciwgdHJhbnNmb3JtPSJsb2cyIiwgYmF0Y2g9InN2YXNlcSIsIGZpbHRlcj1UUlVFKSkKaHNfbG93ZmlsdF9zdmFzZXFfcGNhJHBsb3QKYGBgCgpgYGB7ciBtYWNyb19zdmExLCBmaWcuc2hvdz0iaGlkZSJ9CmhzX2Nkc19tYWNyX2xvd2ZpbHQgPC0gc20obm9ybWFsaXplX2V4cHQoaHNfY2RzX21hY3IsIGZpbHRlcj1UUlVFKSkKIyMgSGVyZSBqdXN0IGxldCBhbGxfcGFpcndpc2UgcnVuIG9uIGZpbHRlcmVkIGRhdGEgYW5kIGRvIGl0cyBub3JtYWwgfiAwICsgY29uZGl0aW9uICsgYmF0Y2ggYW5hbHlzZXMKaHNfbWFjcl9zdmEgPC0gc20oYWxsX3BhaXJ3aXNlKAogIGlucHV0PWhzX2Nkc19tYWNyX2xvd2ZpbHQsCiAgbW9kZWxfYmF0Y2g9InN2YXNlcSIsCiAgbGltbWFfbWV0aG9kPSJyb2J1c3QiKSkKbWVkaWFuc19ieV9jb25kaXRpb24gPC0gaHNfbWFjcl9zdmEkYmFzaWMkbWVkaWFucwpleGNlbF9maWxlIDwtIGdsdWU6OmdsdWUoImV4Y2VsL3tydW5kYXRlfV9oc19tYWNyX3N2YV9jb250ci12e3Zlcn0ueGxzeCIpCmhzX21hY3Jfc3ZhX3RhYmxlcyA8LSBzbShjb21iaW5lX2RlX3RhYmxlcygKICBoc19tYWNyX3N2YSwKICBleGNlbD1leGNlbF9maWxlLAogIGtlZXBlcnM9aHNfY29udHJhc3RzLAogIGV4dHJhX2Fubm90PW1lZGlhbnNfYnlfY29uZGl0aW9uKSkKZXhjZWxfZmlsZSA8LSBnbHVlOjpnbHVlKCJleGNlbC97cnVuZGF0ZX1faHNfbWFjcl9zdmFfc2lnLXZ7dmVyfS54bHN4IikKaHNfbWFjcl9zdmFfc2lnIDwtIHNtKGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgaHNfbWFjcl9zdmFfdGFibGVzLAogIGV4Y2VsPWV4Y2VsX2ZpbGUsCmhzX21hY3Jfc3ZhX21hX2xpbW1hIDwtIGV4dHJhY3RfZGVfcGxvdHMoCiAgcGFpcndpc2U9aHNfbWFjcl9zdmEsCiAgdHlwZT0ibGltbWEiLAogIHRhYmxlPSJzaF92c19jaHIiKQpgYGAKCmBgYHtyIHN2YV9tYV9wbG90fQpoc19tYWNyX3N2YV9tYV9saW1tYSRtYSRwbG90CmBgYAoKIyMgQmF0Y2ggY29ycmVjdGlvbiB2aWEgcnV2IHJlc2lkdWFscwoKIyMjIFNldCB1cCBydXZyZXNpZHVhbHMKCmBgYHtyIHNldHVwX3J1dnJlc2lkfQojIyBobW0gSSBnb3QgdGhlIFJVVnIgZXJyb3IgYWdhaW4sIGJ1dCB3aGVuIEkgcmFuIGl0IG1hbnVhbGx5IGRpZCBub3QuCiMjIEV2ZW4gbW9yZSBzdHJhbmdlbHksIGlmIEkganVzdCBydW4gdGhlIHNhbWUgdGhpbmcgYWdhaW4sIG5vIGVycm9yLi4uCnRlc3RtZSA8LSB0cnkoZ2V0X21vZGVsX2FkanVzdChpbnB1dD1oc19tYWNyX2xvd2ZpbHQsIGVzdGltYXRlX3R5cGU9InJ1dl9yZXNpZHVhbHMiKSwgc2lsZW50PVRSVUUpCgpoc19sb3dmaWx0X3J1dnJlc2lkX3BjYSA8LSBzbShwbG90X3BjYShoc19tYWNyX2xvd2ZpbHQsIHRyYW5zZm9ybT0ibG9nMiIsIGJhdGNoPSJydXZfcmVzaWR1YWxzIikpCmhzX2xvd2ZpbHRfcnV2cmVzaWRfcGNhJHBsb3QKYGBgCgpgYGB7ciBtYWNyb19ydXZyZXNpZDEsIGZpZy5zaG93PSJoaWRlIn0KIyMgSGVyZSBqdXN0IGxldCBhbGxfcGFpcndpc2UgcnVuIG9uIGZpbHRlcmVkIGRhdGEgYW5kIGRvIGl0cyBub3JtYWwgfiAwICsgY29uZGl0aW9uICsgYmF0Y2ggYW5hbHlzZXMKIyMgQml6YXJyZWx5LCBzb21ldGltZXMgaWYgb25lIHJ1bnMgdGhpcywgaXQgZ2l2ZXMgYW4gZXJyb3IgIkVycm9yIGluIChmdW5jdGlvbiAoY2xhc3NlcywgZmRlZiwgbXRhYmxlKSA6IHVuYWJsZSB0byBmaW5kIGFuIGluaGVyaXRlZCBtZXRob2QgZm9yIGZ1bmN0aW9uICdSVVZyJyBmb3Igc2lnbmF0dXJlICcibWF0cml4IiwgImxvZ2ljYWwiLCAibnVtZXJpYyIsICJOVUxMIiciICAtLSBob3dldmVyLCBpZiBvbmUgdGhlbiBzaW1wbHkgcnVucyBpdCBhZ2FpbiBpdCB3b3JrcyBmaW5lLgojIyBJIGFtIGdvaW5nIHRvIGFzc3VtZSB0aGF0IGl0IGlzIGJlY2F1c2UgSSBkbyBub3QgZXhwbGljaXRseSBpbnZva2UgdGhlIGxpYnJhcnkuCiMjIGxpYnJhcnkocnV2KSAgIyMgaG9wZWZ1bGx5IGEgc21hbGwgY29kZSBjaGFuZ2UgbWFkZSB0aGlzIG5vdCBuZWVkZWQuCnRlc3RtZSA8LSBnZXRfbW9kZWxfYWRqdXN0KGlucHV0PWhzX21hY3JfbG93ZmlsdCwgZXN0aW1hdGVfdHlwZT0icnV2X3Jlc2lkdWFscyIpCmhzX21hY3JfcnV2cmVzIDwtIHNtKGFsbF9wYWlyd2lzZSgKICBpbnB1dD1oc19tYWNyX2xvd2ZpbHQsCiAgbW9kZWxfYmF0Y2g9InJ1dl9yZXNpZHVhbHMiLAogIGxpbW1hX21ldGhvZD0icm9idXN0IikpCm1lZGlhbnNfYnlfY29uZGl0aW9uIDwtIGhzX21hY3JfcnV2cmVzJGJhc2ljJG1lZGlhbnMKZXhjZWxfZmlsZSA8LSBnbHVlOjpnbHVlKCJleGNlbC97cnVuZGF0ZX1faHNfbWFjcl9ydXZyZXNfY29udHItdnt2ZXJ9Lnhsc3giKQpoc19tYWNyX3J1dnJlc190YWJsZXMgPC0gc20oY29tYmluZV9kZV90YWJsZXMoCiAgaHNfbWFjcl9ydXZyZXMsCiAgZXhjZWw9ZXhjZWxfZmlsZSwKICBleHRyYV9hbm5vdD1tZWRpYW5zX2J5X2NvbmRpdGlvbiwKICBrZWVwZXJzPWhzX2NvbnRyYXN0cykpCmV4Y2VsX2ZpbGUgPC0gZ2x1ZTo6Z2x1ZSgiZXhjZWwve3J1bmRhdGV9X2hzX21hY3JfcnV2cmVzX3NpZy12e3Zlcn0ueGxzeCIpCmhzX21hY3JfcnV2cmVzX3NpZyA8LSBzbShleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3JfcnV2cmVzX3RhYmxlcywKICBleGNlbD1leGNlbF9maWxlKSkKYGBgCgojIyBCYXRjaCBjb3JyZWN0aW9uIHdpdGggcGNhCgojIyMgU2V0dXAgcGNhCgpgYGB7ciBzZXR1cF9wY2EwMX0KaHNfbG93ZmlsdF9wY2FfcGNhIDwtIHNtKHBsb3RfcGNhKGhzX21hY3JfbG93ZmlsdCwgdHJhbnNmb3JtPSJsb2cyIiwgYmF0Y2g9InBjYSIpKQpoc19sb3dmaWx0X3BjYV9wY2EkcGxvdApgYGAKCmBgYHtyIG1hY3JvX3BjYTEsIGZpZy5zaG93PSJoaWRlIn0KIyMgSGVyZSBqdXN0IGxldCBhbGxfcGFpcndpc2UgcnVuIG9uIGZpbHRlcmVkIGRhdGEgYW5kIGRvIGl0cyBub3JtYWwgfiAwICsgY29uZGl0aW9uICsgYmF0Y2ggYW5hbHlzZXMKaHNfbWFjcl9wY2EgPC0gc20oYWxsX3BhaXJ3aXNlKAogIGlucHV0PWhzX21hY3JfbG93ZmlsdCwKICBtb2RlbF9iYXRjaD0icGNhIiwKICBsaW1tYV9tZXRob2Q9InJvYnVzdCIpKQptZWRpYW5zX2J5X2NvbmRpdGlvbiA8LSBoc19tYWNyX3BjYSRiYXNpYyRtZWRpYW5zCmV4Y2VsX2ZpbGUgPC0gZ2x1ZTo6Z2x1ZSgiZXhjZWwve3J1bmRhdGV9X2hzX21hY3JfcGNhX2NvbnRyLXZ7dmVyfS54bHN4IikKaHNfbWFjcl9wY2FfdGFibGVzIDwtIHNtKGNvbWJpbmVfZGVfdGFibGVzKAogIGhzX21hY3JfcGNhLAogIGV4Y2VsPWV4Y2VsX2ZpbGUKICBrZWVwZXJzPWhzX2NvbnRyYXN0cywKICBleHRyYV9hbm5vdD1tZWRpYW5zX2J5X2NvbmRpdGlvbikpCmV4Y2VsX2ZpbGUgPC0gZ2x1ZTo6Z2x1ZSgiZXhjZWwve3J1bmRhdGV9X2hzX21hY3JfcGNhX3NpZy12e3Zlcn0ueGxzeCIpCmhzX21hY3JfcGNhX3NpZyA8LSBzbShleHRyYWN0X3NpZ25pZmljYW50X2dlbmVzKAogIGhzX21hY3JfcGNhX3RhYmxlcywKICBleGNlbD1leGNlbF9maWxlKSkKYGBgCgojIyBCYXRjaCBjb3JyZWN0aW9uIHdpdGggcnV2IGVtcGlyaWNhbAoKIyMjIFNldHVwIHJ1diBlbXBpcmljYWwKCmBgYHtyIHNldHVwX3BjYTAyfQpoc19sb3dmaWx0X3J1dmVtcF9wY2EgPC0gc20ocGxvdF9wY2EoaHNfbWFjcl9sb3dmaWx0LCB0cmFuc2Zvcm09ImxvZzIiLCBiYXRjaD0icnV2X2VtcGlyaWNhbCIpKQpoc19sb3dmaWx0X3J1dmVtcF9wY2EkcGxvdApgYGAKCmBgYHtyIG1hY3JvX3J1dmVtcDEsIGZpZy5zaG93PSJoaWRlIn0KaHNfbWFjcl9ydXZlbXAgPC0gc20oYWxsX3BhaXJ3aXNlKAogIGlucHV0PWhzX21hY3JfbG93ZmlsdCwKICBtb2RlbF9iYXRjaD0icnV2X2VtcGlyaWNhbCIsCiAgbGltbWFfbWV0aG9kPSJyb2J1c3QiKSkKbWVkaWFuc19ieV9jb25kaXRpb24gPC0gaHNfbWFjcl9ydXZlbXAkYmFzaWMkbWVkaWFucwpleGNlbF9maWxlIDwtIGdsdWU6OmdsdWUoImV4Y2VsL3tydW5kYXRlfV9oc19tYWNyX3J1dmVtcF9jb250ci12e3Zlcn0ueGxzeCIpCmhzX21hY3JfcnV2ZW1wX3RhYmxlcyA8LSBzbShjb21iaW5lX2RlX3RhYmxlcygKICBoc19tYWNyX3J1dmVtcCwKICBleGNlbD1leGNlbF9maWxlLAogIGtlZXBlcnM9aHNfY29udHJhc3RzLAogIGV4dHJhX2Fubm90PW1lZGlhbnNfYnlfY29uZGl0aW9uKSkKZXhjZWxfZmlsZSA8LSBnbHVlOjpnbHVlKCJleGNlbC97cnVuZGF0ZX1faHNfbWFjcl9ydXZlbXBfc2lnLXZ7dmVyfS54bHN4IikKaHNfbWFjcl9ydXZlbXBfc2lnIDwtIHNtKGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgaHNfbWFjcl9ydXZlbXBfdGFibGVzLAogIGV4Y2VsPWV4Y2VsX2ZpbGUpKQpgYGAKCiMjIEJhdGNoIGNvcnJlY3Rpb24gd2l0aCBjb21iYXQKClRoZW4gcmVwZWF0IHdpdGggdGhlIGJhdGNoLWNvcnJlY3RlZCBkYXRhIGFuZCBzZWUgdGhlIGRpZmZlcmVuY2VzLgoKIyMjIFNldHVwIGNvbWJhdAoKYGBge3Igc2V0dXBfY29tYmF0fQpoc19sb3dmaWx0X2NvbWJhdF9wY2EgPC0gc20ocGxvdF9wY2EoaHNfbWFjcl9sb3dmaWx0LCB0cmFuc2Zvcm09ImxvZzIiLCBiYXRjaD0iY29tYmF0X25vcHJpb3IiKSkKaHNfbG93ZmlsdF9jb21iYXRfcGNhJHBsb3QKYGBgCgpgYGB7ciByZXBlYXRfcGFpcndpc2VfYmF0Y2gxLCBmaWcuc2hvdz0iaGlkZSJ9CmhzX21hY3JfY29tYmF0IDwtIHNtKGFsbF9wYWlyd2lzZSgKICBpbnB1dD1oc19tYWNyX2NvbWJhdF9ub3JtLAogIGZvcmNlPVRSVUUsCiAgbGltbWFfbWV0aG9kPSJyb2J1c3QiKSkKbWVkaWFuc19ieV9jb25kaXRpb24gPC0gaHNfbWFjcl9jb21iYXQkYmFzaWMkbWVkaWFucwpleGNlbF9maWxlIDwtIGdsdWU6OmdsdWUoImV4Y2VsL3tydW5kYXRlfV9oc19tYWNyX2NvbWJhdF9jb250ci12e3Zlcn0ueGxzeCIpCmhzX21hY3JfY29tYmF0X3RhYmxlcyA8LSBzbShjb21iaW5lX2RlX3RhYmxlcygKICBoc19tYWNyX2NvbWJhdCwKICBleGNlbD1leGNlbF9maWxlLAogIGtlZXBlcnM9aHNfY29udHJhc3RzLAogIGV4dHJhX2Fubm90PW1lZGlhbnNfYnlfY29uZGl0aW9uKSkKZXhjZWxfZmlsZSA8LSBnbHVlOjpnbHVlKCJleGNlbC97cnVuZGF0ZX1faHNfbWFjcl9jb21iYXRfY29udHItdnt2ZXJ9Lnhsc3giKQpoc19tYWNyX2NvbWJhdF9zaWcgPC0gc20oZXh0cmFjdF9zaWduaWZpY2FudF9nZW5lcygKICBoc19tYWNyX2NvbWJhdF90YWJsZXMsCiAgZXhjZWw9ZXhjZWxfZmlsZSkpCgpgYGAKCmBgYHtyIGZpbmlzaGVkX21hX3Bsb3RzfQpoc19tYWNyX2NvbWJhdF9tYV9saW1tYSA8LSBleHRyYWN0X2RlX3Bsb3RzKAogIHBhaXJ3aXNlPWhzX21hY3JfY29tYmF0LAogIHR5cGU9ImxpbW1hIiwKICB0YWJsZT0ic2hfdnNfY2hyIikKaHNfbWFjcl9jb21iYXRfbWFfbGltbWEkbWEkcGxvdAoKaHNfbWFjcl9jb21iYXRfbWFfZWRnZXIgPC0gZXh0cmFjdF9kZV9wbG90cygKICBwYWlyd2lzZT1oc19tYWNyX2NvbWJhdCwKICB0eXBlPSJlZGdlciIsCiAgdGFibGU9InNoX3ZzX2NociIpCmhzX21hY3JfY29tYmF0X21hX2VkZ2VyJG1hJHBsb3QKCmhzX21hY3JfY29tYmF0X21hX2Rlc2VxIDwtIGV4dHJhY3RfZGVfcGxvdHMoCiAgcGFpcndpc2U9aHNfbWFjcl9jb21iYXQsCiAgdHlwZT0iZGVzZXEiLAogIHRhYmxlPSJzaF92c19jaHIiKQpoc19tYWNyX2NvbWJhdF9tYV9kZXNlcSRtYSRwbG90CmBgYAoKIyBGaWd1cmUgb3V0IGhvdyB0byBjb21wYXJlIHRoZXNlIHJlc3VsdHMKCkkgaGF2ZSA0IG1ldGhvZHMgb2YgcGVyZm9ybWluZyB0aGlzIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLiAgRWFjaCBvbmUgY29tZXMgd2l0aCBhIHNldCBvZgptZXRyaWNzIGRlZmluaW5nICdzaWduaWZpY2FudCcuICBQZXJoYXBzIEkgY2FuIG1ha2UgYSB0YWJsZSBvZiB0aGUgIyBvZiBnZW5lcyBkZWZpbmVkIGFzIHNpZ25pZmljYW50CmJ5IGNvbnRyYXN0IGZvciBlYWNoLiAgSW4gYWRkaXRpb24gaXQgbWF5IGJlIHdvcnRoIHdoaWxlIHRvIGRvIGEgc2NhdHRlciBwbG90cyBvZiB0aGUgbG9nRkNzIGJldHdlZW4KdGhlc2UgY29tcGFyaXNvbnMgYW5kIHNlZSBob3cgd2VsbCB0aGV5IGFncmVlPwoKIyBMb29rIGZpcnN0IGF0IHRoZSBkZSBjb3VudHMKCmBgYHtyIGNvbXBhcmVfZGVfc2V0dXAxfQpoc19tYWNyX25vYmF0Y2hfc2lnJGxpbW1hJGNvdW50cwojI2hzX21hY3JfYmF0Y2hfdGFibGVzJHNpZ25pZmljYW50JGxpbW1hJGNvdW50cwojI2hzX21hY3Jfc3ZhX3RhYmxlcyRzaWduaWZpY2FudCRsaW1tYSRjb3VudHMKIyNoc19tYWNyX3J1dnJlc190YWJsZXMkc2lnbmlmaWNhbnQkbGltbWEkY291bnRzCiMjaHNfbWFjcl9wY2FfdGFibGVzJHNpZ25pZmljYW50JGxpbW1hJGNvdW50cwojI2hzX21hY3JfcnV2ZW1wX3RhYmxlcyRzaWduaWZpY2FudCRsaW1tYSRjb3VudHMKIyNoc19tYWNyX2NvbWJhdF90YWJsZXMkc2lnbmlmaWNhbnQkbGltbWEkY291bnRzCmBgYAoKIyMgQ29tcGFyZSBEZVNlcSAvIEJhc2ljIHdpdGhvdXQgYmF0Y2ggaW4gbW9kZWwKCmBgYHtyIGJhc2ljX2Rlc2VxX25vYmF0Y2gxfQpoc19tYWNyX25vYmF0Y2hfYmFzaWMgPC0gbWVyZ2UoCiAgaHNfbWFjcl9ub2JhdGNoJGRlc2VxJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGhzX21hY3JfYmF0Y2gkYmFzaWMkYWxsX3RhYmxlcyRzaF92c19jaHIsCiAgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKGhzX21hY3Jfbm9iYXRjaF9iYXNpYykgPC0gaHNfbWFjcl9ub2JhdGNoX2Jhc2ljW1siUm93Lm5hbWVzIl1dCmhzX21hY3Jfbm9iYXRjaF9sb2dmYyA8LSBoc19tYWNyX25vYmF0Y2hfYmFzaWNbLCBjKCJsb2dGQy54IiwgImxvZ0ZDLnkiKV0KY29sbmFtZXMoaHNfbWFjcl9ub2JhdGNoX2xvZ2ZjKSA8LSBjKCJub2JhdGNoIiwgImJhc2ljIikKbGZjX25iX2IgPC0gc20ocGxvdF9saW5lYXJfc2NhdHRlcihoc19tYWNyX25vYmF0Y2hfbG9nZmMsIHByZXR0eV9jb2xvcnM9RkFMU0UpKQpsZmNfbmJfYiRzY2F0dGVyCmxmY19uYl9iJGNvcnJlbGF0aW9uCmhzX21hY3Jfbm9iYXRjaF9wIDwtIGhzX21hY3Jfbm9iYXRjaF9iYXNpY1ssIGMoIlAuVmFsdWUiLCJwIildCmhzX21hY3Jfbm9iYXRjaF9wW1syXV0gPC0gYXMubnVtZXJpYyhoc19tYWNyX25vYmF0Y2hfcFtbMl1dKQpjb2xuYW1lcyhoc19tYWNyX25vYmF0Y2hfcCkgPC0gYygibm9iYXRjaCIsImJhc2ljIikKaHNfbWFjcl9ub2JhdGNoX3AgPC0gLTEgKiBsb2coaHNfbWFjcl9ub2JhdGNoX3ApCmhzX21hY3JfcF9uYl9iIDwtIHNtKHBsb3RfbGluZWFyX3NjYXR0ZXIoaHNfbWFjcl9ub2JhdGNoX3AsIHByZXR0eV9jb2xvcnM9RkFMU0UpKQpoc19tYWNyX3BfbmJfYiRzY2F0dGVyCmhzX21hY3JfcF9uYl9iJGNvcnJlbGF0aW9uCmBgYAoKIyMgQ29tcGFyZSBTVkEgdG8gYmF0Y2ggaW4gbW9kZWwsIERFU2VxCgpgYGB7ciBkZXNlcV9zdmFfYmF0Y2gxfQpoc19tYWNyX3N2YV9iYXRjaCA8LSBtZXJnZSgKICBoc19tYWNyX3N2YSRkZXNlcSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBoc19tYWNyX2JhdGNoJGRlc2VxJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhoc19tYWNyX3N2YV9iYXRjaCkgPC0gaHNfbWFjcl9zdmFfYmF0Y2hbWyJSb3cubmFtZXMiXV0KaHNfbWFjcl9zdmFfbG9nZmMgPC0gaHNfbWFjcl9zdmFfYmF0Y2hbLCBjKCJsb2dGQy54IiwibG9nRkMueSIpXQpjb2xuYW1lcyhoc19tYWNyX3N2YV9sb2dmYykgPC0gYygic3ZhIiwiYmF0Y2giKQpoc19tYWNyX2xmY19iX3MgPC0gc20ocGxvdF9saW5lYXJfc2NhdHRlcihoc19tYWNyX3N2YV9sb2dmYywgcHJldHR5X2NvbG9ycz1GQUxTRSkpCmhzX21hY3JfbGZjX2JfcyRzY2F0dGVyCmhzX21hY3JfbGZjX2JfcyRjb3JyZWxhdGlvbgpgYGAKCiMjIyBJbmNsdWRlIHAtdmFsdWUgZXN0aW1hdGlvbnMKClRyeSBwdXR0aW5nIHNvbWUgaW5mb3JtYXRpb24gb2YgdGhlIHAtdmFsdWVzIHdpdGggdGhlIGNvbXBhcmF0aXZlIGxvZzJmYwoKYGBge3IgbDJmc19wdmFsczF9CmxmY19iX3MgPC0gaHNfbWFjcl9zdmFfYmF0Y2hbLCBjKCJsb2dGQy54IiwgImxvZ0ZDLnkiLCAiUC5WYWx1ZS54IiwgIlAuVmFsdWUueSIpXQpjb2xuYW1lcyhsZmNfYl9zKSA8LSBjKCJsMmZjc3ZhIiwgImwyZmNiYXRjaCIsICJwc3ZhIiwgInBiYXRjaCIpCmhzX21hY3JfbGZjX2JfcyRzY2F0dGVyCmN1dG9mZiA8LSAwLjEKbGZjX2JfcyRzdGF0ZSA8LSBpZmVsc2UobGZjX2JfcyRwc3ZhID4gY3V0b2ZmICYgbGZjX2JfcyRwYmF0Y2ggPiBjdXRvZmYsICJib3RoaW5zaWciLAogICAgICAgICAgICAgICAgIGlmZWxzZShsZmNfYl9zJHBzdmEgPD0gY3V0b2ZmICYgbGZjX2JfcyRwYmF0Y2ggPD0gY3V0b2ZmLCAiYm90aHNpZyIsCiAgICAgICAgICAgICAgICAgaWZlbHNlKGxmY19iX3MkcHN2YSA8PSBjdXRvZmYsICJzdmFzaWciLCAiYmF0Y2hzaWciKSkpCiMjbGZjcF9iX3MkbGZjc3RhdGUgPC0gaWZlbHNlKGxmY3BfYl9zJGwyZmNzdmEgPj0gMC43NSAmIGxmY3BfYl9zJGwyZmNiYXRjaCwgIiIsICIiKQpudW1fYm90aGluc2lnIDwtIHN1bShsZmNfYl9zJHN0YXRlID09ICJib3RoaW5zaWciKQpudW1fYm90aHNpZyA8LSBzdW0obGZjX2JfcyRzdGF0ZSA9PSAiYm90aHNpZyIpCm51bV9zdmFzaWcgPC0gc3VtKGxmY19iX3Mkc3RhdGUgPT0gInN2YXNpZyIpCm51bV9iYXRjaHNpZyA8LSBzdW0obGZjX2JfcyRzdGF0ZSA9PSAiYmF0Y2hzaWciKQoKbGlicmFyeShnZ3Bsb3QyKQphZXNfY29sb3IgPSAiKGwyZmNzdmEgPj0gMC43NSB8IGwyZmNzdmEgPD0gLTAuNzUgfCBsMmZjYmF0Y2ggPj0gMC43NSB8IGwyZmNiYXRjaCA8PSAtMC43NSkiCnBsdCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QobGZjX2JfcywgYWVzX3N0cmluZyh4PSJsMmZjc3ZhIiwgeT0ibDJmY2JhdGNoIikpICsKICAgICMjIGdncGxvdDI6Omdlb21fcG9pbnQoc3RhdD0iaWRlbnRpdHkiLCBzaXplPTIsIGFscGhhPTAuMiwgYWVzX3N0cmluZyhzaGFwZT0iYXMuZmFjdG9yKGFlc19jb2xvcikiLCBjb2xvdXI9ImFzLmZhY3RvcihzdGF0ZSkiLCBmaWxsPSJhcy5mYWN0b3Ioc3RhdGUpIikpICsKICAgIGdncGxvdDI6Omdlb21fYWJsaW5lKGNvbG91cj0iYmx1ZSIsIHNsb3BlPTEsIGludGVyY2VwdD0wLCBzaXplPTAuNSkgKwogICAgZ2dwbG90Mjo6Z2VvbV9obGluZSh5aW50ZXJjZXB0PWMoLTAuNzUsIDAuNzUpLCBjb2xvcj0icmVkIiwgc2l6ZT0wLjUpICsKICAgIGdncGxvdDI6Omdlb21fdmxpbmUoeGludGVyY2VwdD1jKC0wLjc1LCAwLjc1KSwgY29sb3I9InJlZCIsIHNpemU9MC41KSArCiAgICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHN0YXQ9ImlkZW50aXR5Iiwgc2l6ZT0yLCBhbHBoYT0wLjIsIGFlc19zdHJpbmcoY29sb3VyPSJhcy5mYWN0b3Ioc3RhdGUpIiwgZmlsbD0iYXMuZmFjdG9yKHN0YXRlKSIpKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0ic3RhdGUiLCB2YWx1ZXM9YygiYm90aGluc2lnIj0iZ3JleSIsICJib3Roc2lnIj0iZm9yZXN0Z3JlZW4iLCAic3Zhc2lnIj0iZGFya3JlZCIsICJiYXRjaHNpZyI9ImRhcmtibHVlIikpICsKICAgIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9InN0YXRlIiwgdmFsdWVzPWMoImJvdGhpbnNpZyI9ImdyZXkiLCAiYm90aHNpZyI9ImZvcmVzdGdyZWVuIiwgInN2YXNpZyI9ImRhcmtyZWQiLCAiYmF0Y2hzaWciPSJkYXJrYmx1ZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJCb3RoIEluU2lnLjogIiwgbnVtX2JvdGhpbnNpZyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJCb3RoIFNpZy46ICIsIG51bV9ib3Roc2lnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIlN2YSBTaWcuOiAiLCBudW1fc3Zhc2lnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoIkJhdGNoIFNpZy46ICIsIG51bV9iYXRjaHNpZykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGU9Z2dwbG90Mjo6Z3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcz1hZXMoc2l6ZT0zLCBmaWxsPSJncmV5IikpKSArCiAgICBnZ3Bsb3QyOjpndWlkZXMoZmlsbD1nZ3Bsb3QyOjpndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzPWxpc3Qoc2l6ZT0zKSkpICsKICAgIGdncGxvdDI6OnRoZW1lX2J3KCkKcGx0CmBgYAoKIyMgQ29tcGFyZSBydXZyZXNpZCB0byBiYXRjaCBpbiBtb2RlbCwgREVTZXEKCmBgYHtyIGJhdGNoX3J1dnJlc2lkX2Rlc2VxMX0KaHNfbWFjcl9iYXRjaF9ydXZyZXNpZF9kZXNlcSA8LSBtZXJnZSgKICBoc19tYWNyX3J1dnJlcyRkZXNlcSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBoc19tYWNyX2JhdGNoJGJhc2ljJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhoc19tYWNyX2JhdGNoX3J1dnJlc2lkX2Rlc2VxKSA8LSBoc19tYWNyX2JhdGNoX3J1dnJlc2lkX2Rlc2VxW1siUm93Lm5hbWVzIl1dCmhzX21hY3JfYmF0Y2hfcnV2cmVzaWRfbG9nZmMgPC0gaHNfbWFjcl9iYXRjaF9ydXZyZXNpZF9kZXNlcVssIGMoImxvZ0ZDLngiLCJsb2dGQy55IildCmNvbG5hbWVzKGhzX21hY3JfYmF0Y2hfcnV2cmVzaWRfbG9nZmMpIDwtIGMoIm5vYmF0Y2giLCJiYXNpYyIpCmxmY19ydXZfYmF0IDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIoaHNfbWFjcl9iYXRjaF9ydXZyZXNpZF9sb2dmYywgcHJldHR5X2NvbG9ycz1GQUxTRSkKbGZjX3J1dl9iYXQkc2NhdHRlcgpsZmNfcnV2X2JhdCRjb3JyZWxhdGlvbgpgYGAKCiMjIENvbXBhcmUgbm8gYmF0Y2ggdG8gYmF0Y2ggaW4gbW9kZWwsIGxpbW1hCgpgYGB7ciBjb21wYXJlX2JhdGNoX25vYmF0Y2hfbGltbWExfQpoc19tYWNyX25vYmF0Y2hfYmF0Y2ggPC0gbWVyZ2UoCiAgaHNfbWFjcl9ub2JhdGNoJGxpbW1hJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGhzX21hY3JfYmF0Y2gkbGltbWEkYWxsX3RhYmxlcyRzaF92c19jaHIsCiAgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKGhzX21hY3Jfbm9iYXRjaF9iYXRjaCkgPC0gaHNfbWFjcl9ub2JhdGNoX2JhdGNoW1siUm93Lm5hbWVzIl1dCmhzX21hY3Jfbm9iYXRjaF9iYXRjaCA8LSBoc19tYWNyX25vYmF0Y2hfYmF0Y2hbLCBjKCJsb2dGQy54IiwibG9nRkMueSIpXQpjb2xuYW1lcyhoc19tYWNyX25vYmF0Y2hfYmF0Y2gpIDwtIGMoIm5vYmF0Y2giLCJiYXRjaCIpCm5iX2IgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihoc19tYWNyX25vYmF0Y2hfYmF0Y2gsIHByZXR0eV9jb2xvcnM9RkFMU0UpCm5iX2Ikc2NhdHRlcgpuYl9iJGNvcnJlbGF0aW9uCmBgYAoKIyMgQmF0Y2ggaW4gbW9kZWwgdnMuIFNWQSwgbGltbWEKCmBgYHtyIGNvbXBhcmVfYmF0Y2hfc3ZhX2xpbW1hMX0KaHNfbWFjcl9iYXRjaF9zdmEgPC0gbWVyZ2UoCiAgaHNfbWFjcl9iYXRjaCRsaW1tYSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBoc19tYWNyX3N2YSRsaW1tYSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBieT0icm93Lm5hbWVzIikKcm93bmFtZXMoaHNfbWFjcl9iYXRjaF9zdmEpIDwtIGhzX21hY3JfYmF0Y2hfc3ZhW1siUm93Lm5hbWVzIl1dCmhzX21hY3JfYmF0Y2hfc3ZhIDwtIGhzX21hY3JfYmF0Y2hfc3ZhWywgYygibG9nRkMueCIsImxvZ0ZDLnkiKV0KY29sbmFtZXMoaHNfbWFjcl9iYXRjaF9zdmEpIDwtIGMoImJhdGNoIiwic3ZhIikKYl9zIDwtIHBsb3RfbGluZWFyX3NjYXR0ZXIoaHNfbWFjcl9iYXRjaF9zdmEsIHByZXR0eV9jb2xvcnM9RkFMU0UpCmJfcyRzY2F0dGVyCmJfcyRjb3JyZWxhdGlvbgpgYGAKCiMjIE5vYmF0Y2ggdnMuIGJhdGNoIGluIG1vZGVsLCBlZGdlcgoKYGBge3IgY29tcGFyZV9ub2JhdGNoX2JhdGNoX2VkZ2VyMX0KaHNfbWFjcl9ub2JhdGNoX2JhdGNoIDwtIG1lcmdlKAogIGhzX21hY3Jfbm9iYXRjaCRlZGdlciRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBoc19tYWNyX2JhdGNoJGVkZ2VyJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhoc19tYWNyX25vYmF0Y2hfYmF0Y2gpIDwtIGhzX21hY3Jfbm9iYXRjaF9iYXRjaFtbIlJvdy5uYW1lcyJdXQpoc19tYWNyX25vYmF0Y2hfYmF0Y2ggPC0gaHNfbWFjcl9ub2JhdGNoX2JhdGNoWywgYygibG9nRkMueCIsImxvZ0ZDLnkiKV0KY29sbmFtZXMoaHNfbWFjcl9ub2JhdGNoX2JhdGNoKSA8LSBjKCJub2JhdGNoIiwiYmF0Y2giKQpuYl9iIDwtIHNtKHBsb3RfbGluZWFyX3NjYXR0ZXIoaHNfbWFjcl9ub2JhdGNoX2JhdGNoLCBwcmV0dHlfY29sb3JzPUZBTFNFKSkKbmJfYiRzY2F0dGVyCm5iX2IkY29ycmVsYXRpb24KYGBgCgojIyBCYXRjaCBpbiBtb2RlbCB2cy4gU1ZBLCBlZGdlcgoKYGBge3IgY29tcGFyZV9iYXRjaF9zdmFfZWRnZXIxfQpoc19tYWNyX2JhdGNoX3N2YSA8LSBtZXJnZSgKICBoc19tYWNyX2JhdGNoJGVkZ2VyJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGhzX21hY3Jfc3ZhJGVkZ2VyJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhoc19tYWNyX2JhdGNoX3N2YSkgPC0gaHNfbWFjcl9iYXRjaF9zdmFbWyJSb3cubmFtZXMiXV0KaHNfbWFjcl9iYXRjaF9zdmEgPC0gaHNfbWFjcl9iYXRjaF9zdmFbLCBjKCJsb2dGQy54IiwibG9nRkMueSIpXQpjb2xuYW1lcyhoc19tYWNyX2JhdGNoX3N2YSkgPC0gYygiYmF0Y2giLCJzdmEiKQpiX3MgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihoc19tYWNyX2JhdGNoX3N2YSwgcHJldHR5X2NvbG9ycz1GQUxTRSkKYl9zJHNjYXR0ZXIKYl9zJGNvcnJlbGF0aW9uCmBgYAoKIyMgQ29tcGFyZSBub2JhdGNoIHZzLiBiYXRjaCwgZGVzZXEKCmBgYHtyIGNvbXBhcmVfbm9iYXRjaF9iYXRjaF9kZXNlcTF9CmhzX21hY3Jfbm9iYXRjaF9iYXRjaCA8LSBtZXJnZSgKICBoc19tYWNyX25vYmF0Y2gkZGVzZXEkYWxsX3RhYmxlcyRzaF92c19jaHIsCiAgaHNfbWFjcl9iYXRjaCRkZXNlcSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBieT0icm93Lm5hbWVzIikKcm93bmFtZXMoaHNfbWFjcl9ub2JhdGNoX2JhdGNoKSA8LSBoc19tYWNyX25vYmF0Y2hfYmF0Y2hbWyJSb3cubmFtZXMiXV0KaHNfbWFjcl9ub2JhdGNoX2JhdGNoIDwtIGhzX21hY3Jfbm9iYXRjaF9iYXRjaFssIGMoImxvZ0ZDLngiLCJsb2dGQy55IildCmNvbG5hbWVzKGhzX21hY3Jfbm9iYXRjaF9iYXRjaCkgPC0gYygibm9iYXRjaCIsImJhdGNoIikKbmJfYiA8LSBzbShwbG90X2xpbmVhcl9zY2F0dGVyKGhzX21hY3Jfbm9iYXRjaF9iYXRjaCwgcHJldHR5X2NvbG9ycz1GQUxTRSkpCm5iX2Ikc2NhdHRlcgpuYl9iJGNvcnJlbGF0aW9uCmBgYAoKIyMgQ29tcGFyZSBiYXRjaCB2cy4gU1ZBLCBkZXNlcQoKYGBge3IgY29tcGFyZV9iYXRjaF9zdmFfZGVzZXExfQpoc19tYWNyX2JhdGNoX3N2YSA8LSBtZXJnZSgKICBoc19tYWNyX2JhdGNoJGRlc2VxJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGhzX21hY3Jfc3ZhJGRlc2VxJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhoc19tYWNyX2JhdGNoX3N2YSkgPC0gaHNfbWFjcl9iYXRjaF9zdmFbWyJSb3cubmFtZXMiXV0KaHNfbWFjcl9iYXRjaF9zdmEgPC0gaHNfbWFjcl9iYXRjaF9zdmFbLCBjKCJsb2dGQy54IiwgImxvZ0ZDLnkiKV0KY29sbmFtZXMoaHNfbWFjcl9iYXRjaF9zdmEpIDwtIGMoImJhdGNoIiwgInN2YSIpCmJfcyA8LSBzbShwbG90X2xpbmVhcl9zY2F0dGVyKGhzX21hY3JfYmF0Y2hfc3ZhLCBwcmV0dHlfY29sb3JzPUZBTFNFKSkKYl9zJHNjYXR0ZXIKYl9zJGNvcnJlbGF0aW9uCmBgYAoKIyBSZXBlYXQgdXNpbmcgdGhlIHBhcmFzaXRlIGRhdGEKCkluICdtYWNyb3BoYWdlX2VzdGltYXRpb24nLCB3ZSBkaWQgYSBzZXJpZXMgb2YgYW5hbHlzZXMgdG8gdHJ5IHRvIHBpY2sgb3V0IHNvbWUgb2YgdGhlIHN1cnJvZ2F0ZQp2YXJpYWJsZXMgaW4gdGhlIGRhdGEuICBOb3cgd2Ugd2lsbCBwZXJmb3JtIGEgc2V0IG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2VzIHVzaW5nIHRoZQpyZXN1bHRzIGZyb20gdGhhdC4gIFNpbmNlIHRoZSAnYmF0Y2gnIGVsZW1lbnQgb2YgdGhlIGRhdGEgaXMgcmVhc29uYWJseSB3ZWxsIGV4cGxhaW5lZCwgd2Ugd2lsbCBub3QKYWJ1c2UgdGhlIGRhdGEgd2l0aCBzdmEvY29tYmF0LCBidXQgaW5zdGVhZCBpbmNsdWRlIGJhdGNoIGluIHRoZSBleHBlcmltZW50YWwgbW9kZWwuCgpJdCBhcHBlYXJzIHRoYXQgaXQgaXMgcG9zc2libGUgdGhvdWdoIHNvbWV3aGF0IGRpZmZpY3VsdCB0byBhcHBseSBiYXRjaCBlc3RpbWF0aW9ucyBnZW5lcmF0ZWQgYnkgc3ZhCnRvIHRoZSBtb2RlbCBnaXZlbiB0byBERVNlcS9FZGdlUi9saW1tYS4gIEluIHRoZSBjYXNlIG9mIGxpbW1hIGl0IGlzIGZhaXJseSBzaW1wbGUsIGJ1dCBpbiB0aGUgb3RoZXIKdHdvIGl0IGlzIGEgYml0IG1vcmUgZGlmZmljdWx0LiAgVGhlcmUgaXMgYSBuaWNlIGRpc2N1c3Npb24gb2YgdGhpcyBhdDogaHR0cHM6Ly93d3cuYmlvc3RhcnMub3JnL3AvMTU2MTg2LwpJIGFtIGF0dGVtcHRpbmcgdG8gYXBwbHkgdGhhdCBsb2dpYyB0byB0aGlzIGRhdGEgd2l0aCBsaW1pdGVkIHN1Y2Nlc3MuCgpgYGB7ciBzZXR1cF9kZSwgZmlnLnNob3c9ImhpZGUifQpscF9jb250cmFzdHMgPC0gbGlzdCgKICAgICJtYWNyb19jaHItc2giID0gYygiY2hyIiwgInNoIikpCmxwX21hY3Jfbm9ybSA8LSBzbShub3JtYWxpemVfZXhwdChscF9tYWNyLCBmaWx0ZXI9VFJVRSwgY29udmVydD0iY3BtIiwgbm9ybT0icXVhbnQiKSkKbHBfbWFjcl9jb21iYXRfbm9ybSA8LSBzbShub3JtYWxpemVfZXhwdChscF9tYWNyLCBmaWx0ZXI9VFJVRSwgbm9ybT0icXVhbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvd190b196ZXJvPVRSVUUsIGJhdGNoPSJjb21iYXQiKSkKbHBfbWFjcl9sb3dmaWx0IDwtIHNtKG5vcm1hbGl6ZV9leHB0KGxwX21hY3IsIGZpbHRlcj1UUlVFKSkKIyMgU2V0IHVwIHRoZSBkYXRhIHVzZWQgaW4gdGhlIDMgY29tcGFyYXRpdmUgY29udHJhc3Qgc2V0cy4KYGBgCgojIyBObyBiYXRjaCBpbiB0aGUgbW9kZWwKCmBgYHtyIG1hY3JvX25vYmF0Y2gsIGZpZy5zaG93PSJoaWRlIn0KbHBfbWFjcl9ub2JhdGNoIDwtIHNtKGFsbF9wYWlyd2lzZShscF9tYWNyX2xvd2ZpbHQsIGxpbW1hX21ldGhvZD0icm9idXN0IiwgbW9kZWxfYmF0Y2g9RkFMU0UpKQojIyB3b3csIGFsbCB0b29scyBpbmNsdWRpbmcgYmFzaWMgYWdyZWUgYWxtb3N0IGNvbXBsZXRlbHkKbWVkaWFuc19ieV9jb25kaXRpb24gPC0gbHBfbWFjcl9ub2JhdGNoJGJhc2ljJG1lZGlhbnMKbHBfbWFjcl9ub2JhdGNoX3RhYmxlcyA8LSBzbShjb21iaW5lX2RlX3RhYmxlcygKICBscF9tYWNyX25vYmF0Y2gsCiAgZXhjZWw9cGFzdGUwKCJleGNlbC9scF9tYWNyX25vYmF0Y2gtdiIsIHZlciwgIi54bHN4IiksCiAga2VlcGVycz1scF9jb250cmFzdHMsCiAgZXh0cmFfYW5ub3Q9bWVkaWFuc19ieV9jb25kaXRpb24pKQpscF9tYWNyX25vYmF0Y2hfc2lnIDwtIHNtKGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgbHBfbWFjcl9ub2JhdGNoX3RhYmxlcywKICBleGNlbD1wYXN0ZTAoImV4Y2VsL2xwX21hY3Jfbm9iYXRjaF9zaWduaWZpY2FudC12IiwgdmVyLCAiLnhsc3giKSkpCmBgYAoKIyMgQmF0Y2ggaW4gdGhlIG1vZGVsCgpJbiB0aGlzICBhdHRlbXB0LCB3ZSBhZGQgYSBiYXRjaCBmYWN0b3IgaW4gdGhlIGV4cGVyaW1lbnRhbCBtb2RlbCBhbmQgc2VlIGhvdyBpdCBkb2VzLgoKYGBge3IgbWFjcm9fYmF0Y2gsIGZpZy5zaG93PSJoaWRlIn0KIyMgSGVyZSBqdXN0IGxldCBhbGxfcGFpcndpc2UgcnVuIG9uIGZpbHRlcmVkIGRhdGEgYW5kIGRvIGl0cyBub3JtYWwgfiAwICsgY29uZGl0aW9uICsgYmF0Y2ggYW5hbHlzZXMKbHBfbWFjcl9iYXRjaCA8LSBzbShhbGxfcGFpcndpc2UobHBfbWFjcl9sb3dmaWx0LCBsaW1tYV9tZXRob2Q9InJvYnVzdCIpKQptZWRpYW5zX2J5X2NvbmRpdGlvbiA8LSBscF9tYWNyX2JhdGNoJGJhc2ljJG1lZGlhbnMKbHBfbWFjcl9iYXRjaF90YWJsZXMgPC0gc20oY29tYmluZV9kZV90YWJsZXMoCiAgbHBfbWFjcl9iYXRjaCwKICBleGNlbD1wYXN0ZTAoImV4Y2VsL2xwX21hY3JfYmF0Y2htb2RlbC12IiwgdmVyLCAiLnhsc3giKSwKICBrZWVwZXJzPWxwX2NvbnRyYXN0cywKICBleHRyYV9hbm5vdD1tZWRpYW5zX2J5X2NvbmRpdGlvbikpCmxwX21hY3JfYmF0Y2hfc2lnIDwtIHNtKGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgbHBfbWFjcl9iYXRjaF90YWJsZXMsCiAgZXhjZWw9cGFzdGUwKCJleGNlbC9scF9tYWNyX2JhdGNobW9kZWxfc2lnbmlmaWNhbnQtdiIsIHZlciwgIi54bHN4IikpKQpgYGAKCiMjIEJhdGNoIGVzdGltYXRlZCB3aXRoIFNWQQoKYGBge3IgbWFjcm9fc3ZhLCBmaWcuc2hvdz0iaGlkZSJ9CiMjIEhlcmUganVzdCBsZXQgYWxsX3BhaXJ3aXNlIHJ1biBvbiBmaWx0ZXJlZCBkYXRhIGFuZCBkbyBpdHMgbm9ybWFsIH4gMCArIGNvbmRpdGlvbiArIGJhdGNoIGFuYWx5c2VzCmxwX21hY3Jfc3ZhIDwtIHNtKGFsbF9wYWlyd2lzZShscF9tYWNyX2xvd2ZpbHQsIGxpbW1hX21ldGhvZD0icm9idXN0IiwgbW9kZWxfYmF0Y2g9InN2YSIpKQptZWRpYW5zX2J5X2NvbmRpdGlvbiA8LSBscF9tYWNyX3N2YSRiYXNpYyRtZWRpYW5zCmxwX21hY3Jfc3ZhX3RhYmxlcyA8LSBzbShjb21iaW5lX2RlX3RhYmxlcygKICBscF9tYWNyX3N2YSwKICBleGNlbD1wYXN0ZTAoImV4Y2VsL2xwX21hY3Jfc3ZhLXYiLCB2ZXIsICIueGxzeCIpLAogIGtlZXBlcnM9bHBfY29udHJhc3RzLAogIGV4dHJhX2Fubm90PW1lZGlhbnNfYnlfY29uZGl0aW9uKSkKbHBfbWFjcl9zdmFfc2lnIDwtIHNtKGV4dHJhY3Rfc2lnbmlmaWNhbnRfZ2VuZXMoCiAgbHBfbWFjcl9zdmFfdGFibGVzLAogIGV4Y2VsPXBhc3RlMCgiZXhjZWwvbHBfbWFjcl9zdmFfc2lnbmlmaWNhbnQtdiIsIHZlciwgIi54bHN4IikpKQpgYGAKCiMgRmlndXJlIG91dCBob3cgdG8gY29tcGFyZSB0aGVzZSByZXN1bHRzCgpJIGhhdmUgNCBtZXRob2RzIG9mIHBlcmZvcm1pbmcgdGhpcyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4gIEVhY2ggb25lIGNvbWVzIHdpdGggYSBzZXQgb2YKbWV0cmljcyBkZWZpbmluZyAnc2lnbmlmaWNhbnQnLiAgUGVyaGFwcyBJIGNhbiBtYWtlIGEgdGFibGUgb2YgdGhlICMgb2YgZ2VuZXMgZGVmaW5lZCBhcyBzaWduaWZpY2FudApieSBjb250cmFzdCBmb3IgZWFjaC4gIEluIGFkZGl0aW9uIGl0IG1heSBiZSB3b3J0aCB3aGlsZSB0byBkbyBhIHNjYXR0ZXIgcGxvdHMgb2YgdGhlIGxvZ0ZDcyBiZXR3ZWVuCnRoZXNlIGNvbXBhcmlzb25zIGFuZCBzZWUgaG93IHdlbGwgdGhleSBhZ3JlZT8KCiMjIENvbXBhcmUgRGVTZXEgLyBCYXNpYyB3aXRob3V0IGJhdGNoIGluIG1vZGVsCgpgYGB7ciBiYXNpY19kZXNlcV9ub2JhdGNofQpscF9tYWNyX25vYmF0Y2hfYmFzaWMgPC0gbWVyZ2UoCiAgbHBfbWFjcl9ub2JhdGNoJGRlc2VxJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGxwX21hY3JfYmF0Y2gkYmFzaWMkYWxsX3RhYmxlcyRzaF92c19jaHIsCiAgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKGxwX21hY3Jfbm9iYXRjaF9iYXNpYykgPC0gbHBfbWFjcl9ub2JhdGNoX2Jhc2ljW1siUm93Lm5hbWVzIl1dCmxwX21hY3Jfbm9iYXRjaF9sb2dmYyA8LSBscF9tYWNyX25vYmF0Y2hfYmFzaWNbLCBjKCJsb2dGQy54IiwgImxvZ0ZDLnkiKV0KY29sbmFtZXMobHBfbWFjcl9ub2JhdGNoX2xvZ2ZjKSA8LSBjKCJub2JhdGNoIiwiYmFzaWMiKQpsZmNfbmJfYiA8LSBzbShwbG90X2xpbmVhcl9zY2F0dGVyKGxwX21hY3Jfbm9iYXRjaF9sb2dmYywgcHJldHR5X2NvbG9ycz1GQUxTRSkpCmxmY19uYl9iJHNjYXR0ZXIKbGZjX25iX2IkY29ycmVsYXRpb24KCmxwX21hY3Jfbm9iYXRjaF9wIDwtIGxwX21hY3Jfbm9iYXRjaF9iYXNpY1ssIGMoIlAuVmFsdWUiLCJwIildCmxwX21hY3Jfbm9iYXRjaF9wW1syXV0gPC0gYXMubnVtZXJpYyhscF9tYWNyX25vYmF0Y2hfcFtbMl1dKQpjb2xuYW1lcyhscF9tYWNyX25vYmF0Y2hfcCkgPC0gYygibm9iYXRjaCIsImJhc2ljIikKbHBfbWFjcl9ub2JhdGNoX3AgPC0gLTEgKiBsb2cobHBfbWFjcl9ub2JhdGNoX3ApCnBfbmJfYiA8LSBzbShwbG90X2xpbmVhcl9zY2F0dGVyKGxwX21hY3Jfbm9iYXRjaF9wLCBwcmV0dHlfY29sb3JzPUZBTFNFKSkKcF9uYl9iJHNjYXR0ZXIKcF9uYl9iJGNvcnJlbGF0aW9uCmBgYAoKIyMgQ29tcGFyZSBTVkEgdG8gYmF0Y2ggaW4gbW9kZWwsIERFU2VxCgpgYGB7ciBkZXNlcV9zdmFfYmF0Y2h9CmxwX21hY3Jfc3ZhX2JhdGNoIDwtIG1lcmdlKAogIGxwX21hY3Jfc3ZhJGRlc2VxJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGxwX21hY3JfYmF0Y2gkZGVzZXEkYWxsX3RhYmxlcyRzaF92c19jaHIsCiAgYnk9InJvdy5uYW1lcyIpCnJvd25hbWVzKGxwX21hY3Jfc3ZhX2JhdGNoKSA8LSBscF9tYWNyX3N2YV9iYXRjaFtbIlJvdy5uYW1lcyJdXQpscF9tYWNyX3N2YV9sb2dmYyA8LSBscF9tYWNyX3N2YV9iYXRjaFssIGMoImxvZ0ZDLngiLCJsb2dGQy55IildCmNvbG5hbWVzKGxwX21hY3Jfc3ZhX2xvZ2ZjKSA8LSBjKCJzdmEiLCJiYXRjaCIpCmxmY19iX3MgPC0gc20ocGxvdF9saW5lYXJfc2NhdHRlcihscF9tYWNyX3N2YV9sb2dmYywgcHJldHR5X2NvbG9ycz1GQUxTRSkpCmxmY19iX3Mkc2NhdHRlcgpsZmNfYl9zJGNvcnJlbGF0aW9uCgpscF9tYWNyX3N2YV9wIDwtIGxwX21hY3Jfc3ZhX2JhdGNoWywgYygiUC5WYWx1ZS54IiwiUC5WYWx1ZS55IildCmxwX21hY3Jfc3ZhX3BbWzJdXSA8LSBhcy5udW1lcmljKGxwX21hY3Jfc3ZhX3BbWzJdXSkKY29sbmFtZXMobHBfbWFjcl9zdmFfcCkgPC0gYygic3ZhIiwiYmF0Y2giKQpscF9tYWNyX3N2YV9wIDwtIC0xICogbG9nKGxwX21hY3Jfc3ZhX3ApCnBfYl9zIDwtIHNtKHBsb3RfbGluZWFyX3NjYXR0ZXIobHBfbWFjcl9zdmFfcCwgcHJldHR5X2NvbG9ycz1GQUxTRSkpCnBfYl9zJHNjYXR0ZXIKcF9iX3MkY29ycmVsYXRpb24KYGBgCgojIyMgSW5jbHVkZSBwLXZhbHVlIGVzdGltYXRpb25zCgpUcnkgcHV0dGluZyBzb21lIGluZm9ybWF0aW9uIG9mIHRoZSBwLXZhbHVlcyB3aXRoIHRoZSBjb21wYXJhdGl2ZSBsb2cyZmMKCmBgYHtyIGwyZnNfcHZhbHN9CmxmY3BfYl9zIDwtIGxwX21hY3Jfc3ZhX2JhdGNoWywgYygibG9nRkMueCIsICJsb2dGQy55IiwgIlAuVmFsdWUueCIsICJQLlZhbHVlLnkiKV0KY29sbmFtZXMobGZjcF9iX3MpIDwtIGMoImwyZmNzdmEiLCAibDJmY2JhdGNoIiwgInBzdmEiLCAicGJhdGNoIikKbGZjX2JfcyRzY2F0dGVyCmN1dG9mZiA8LSAwLjEKbGZjcF9iX3Mkc3RhdGUgPC0gaWZlbHNlKGxmY3BfYl9zJHBzdmEgPiBjdXRvZmYgJiBsZmNwX2JfcyRwYmF0Y2ggPiBjdXRvZmYsICJib3RoaW5zaWciLAogICAgICAgICAgICAgICAgICBpZmVsc2UobGZjcF9iX3MkcHN2YSA8PSBjdXRvZmYgJiBsZmNwX2JfcyRwYmF0Y2ggPD0gY3V0b2ZmLCAiYm90aHNpZyIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShsZmNwX2JfcyRwc3ZhIDw9IGN1dG9mZiwgInN2YXNpZyIsICJiYXRjaHNpZyIpKSkKIyNsZmNwX2JfcyRsZmNzdGF0ZSA8LSBpZmVsc2UobGZjcF9iX3MkbDJmY3N2YSA+PSAwLjc1ICYgbGZjcF9iX3MkbDJmY2JhdGNoLCAiIiwgIiIpCm51bV9ib3RoaW5zaWcgPC0gc3VtKGxmY3BfYl9zJHN0YXRlID09ICJib3RoaW5zaWciKQpudW1fYm90aHNpZyA8LSBzdW0obGZjcF9iX3Mkc3RhdGUgPT0gImJvdGhzaWciKQpudW1fc3Zhc2lnIDwtIHN1bShsZmNwX2JfcyRzdGF0ZSA9PSAic3Zhc2lnIikKbnVtX2JhdGNoc2lnIDwtIHN1bShsZmNwX2JfcyRzdGF0ZSA9PSAiYmF0Y2hzaWciKQoKbGlicmFyeShnZ3Bsb3QyKQphZXNfY29sb3IgPSAiKGwyZmNzdmEgPj0gMC43NSB8IGwyZmNzdmEgPD0gLTAuNzUgfCBsMmZjYmF0Y2ggPj0gMC43NSB8IGwyZmNiYXRjaCA8PSAtMC43NSkiCnBsdCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QobGZjcF9iX3MsIGFlc19zdHJpbmcoeD0ibDJmY3N2YSIsIHk9ImwyZmNiYXRjaCIpKSArCiAgICAjIyBnZ3Bsb3QyOjpnZW9tX3BvaW50KHN0YXQ9ImlkZW50aXR5Iiwgc2l6ZT0yLCBhbHBoYT0wLjIsIGFlc19zdHJpbmcoc2hhcGU9ImFzLmZhY3RvcihhZXNfY29sb3IpIiwgY29sb3VyPSJhcy5mYWN0b3Ioc3RhdGUpIiwgZmlsbD0iYXMuZmFjdG9yKHN0YXRlKSIpKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2FibGluZShjb2xvdXI9ImJsdWUiLCBzbG9wZT0xLCBpbnRlcmNlcHQ9MCwgc2l6ZT0wLjUpICsKICAgIGdncGxvdDI6Omdlb21faGxpbmUoeWludGVyY2VwdD1jKC0wLjc1LCAwLjc1KSwgY29sb3I9InJlZCIsIHNpemU9MC41KSArCiAgICBnZ3Bsb3QyOjpnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9YygtMC43NSwgMC43NSksIGNvbG9yPSJyZWQiLCBzaXplPTAuNSkgKwogICAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzdGF0PSJpZGVudGl0eSIsIHNpemU9MiwgYWxwaGE9MC4yLCBhZXNfc3RyaW5nKGNvbG91cj0iYXMuZmFjdG9yKHN0YXRlKSIsIGZpbGw9ImFzLmZhY3RvcihzdGF0ZSkiKSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKG5hbWU9InN0YXRlIiwgdmFsdWVzPWMoImJvdGhpbnNpZyI9ImdyZXkiLCAiYm90aHNpZyI9ImZvcmVzdGdyZWVuIiwgInN2YXNpZyI9ImRhcmtyZWQiLCAiYmF0Y2hzaWciPSJkYXJrYmx1ZSIpKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbChuYW1lPSJzdGF0ZSIsIHZhbHVlcz1jKCJib3RoaW5zaWciPSJncmV5IiwgImJvdGhzaWciPSJmb3Jlc3RncmVlbiIsICJzdmFzaWciPSJkYXJrcmVkIiwgImJhdGNoc2lnIj0iZGFya2JsdWUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQm90aCBJblNpZy46ICIsIG51bV9ib3RoaW5zaWcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQm90aCBTaWcuOiAiLCBudW1fYm90aHNpZyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJTdmEgU2lnLjogIiwgbnVtX3N2YXNpZyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJCYXRjaCBTaWcuOiAiLCBudW1fYmF0Y2hzaWcpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlPWdncGxvdDI6Omd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXM9YWVzKHNpemU9MywgZmlsbD0iZ3JleSIpKSkgKwogICAgZ2dwbG90Mjo6Z3VpZGVzKGZpbGw9Z2dwbG90Mjo6Z3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcz1saXN0KHNpemU9MykpKSArCiAgICBnZ3Bsb3QyOjp0aGVtZV9idygpCnBsdApgYGAKCiMjIENvbXBhcmUgbm8gYmF0Y2ggdG8gYmF0Y2ggaW4gbW9kZWwsIGxpbW1hCgpgYGB7ciBjb21wYXJlX2JhdGNoX25vYmF0Y2hfbGltbWF9CmxwX21hY3Jfbm9iYXRjaF9iYXRjaCA8LSBtZXJnZSgKICBscF9tYWNyX25vYmF0Y2gkbGltbWEkYWxsX3RhYmxlcyRzaF92c19jaHIsCiAgbHBfbWFjcl9iYXRjaCRsaW1tYSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBieT0icm93Lm5hbWVzIikKcm93bmFtZXMobHBfbWFjcl9ub2JhdGNoX2JhdGNoKSA8LSBscF9tYWNyX25vYmF0Y2hfYmF0Y2hbWyJSb3cubmFtZXMiXV0KbHBfbWFjcl9ub2JhdGNoX2JhdGNoIDwtIGxwX21hY3Jfbm9iYXRjaF9iYXRjaFssIGMoImxvZ0ZDLngiLCJsb2dGQy55IildCmNvbG5hbWVzKGxwX21hY3Jfbm9iYXRjaF9iYXRjaCkgPC0gYygibm9iYXRjaCIsImJhdGNoIikKbmJfYiA8LSBwbG90X2xpbmVhcl9zY2F0dGVyKGxwX21hY3Jfbm9iYXRjaF9iYXRjaCwgcHJldHR5X2NvbG9ycz1GQUxTRSkKbmJfYiRzY2F0dGVyCm5iX2IkY29ycmVsYXRpb24KYGBgCgojIyBCYXRjaCBpbiBtb2RlbCB2cy4gU1ZBLCBsaW1tYQoKYGBge3IgY29tcGFyZV9iYXRjaF9zdmFfbGltbWEyfQpscF9tYWNyX2JhdGNoX3N2YSA8LSBtZXJnZSgKICBscF9tYWNyX2JhdGNoJGxpbW1hJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGxwX21hY3Jfc3ZhJGxpbW1hJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhscF9tYWNyX2JhdGNoX3N2YSkgPC0gbHBfbWFjcl9iYXRjaF9zdmFbWyJSb3cubmFtZXMiXV0KbHBfbWFjcl9iYXRjaF9zdmEgPC0gbHBfbWFjcl9iYXRjaF9zdmFbLCBjKCJsb2dGQy54IiwibG9nRkMueSIpXQpjb2xuYW1lcyhscF9tYWNyX2JhdGNoX3N2YSkgPC0gYygiYmF0Y2giLCJzdmEiKQpiX3MgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihscF9tYWNyX2JhdGNoX3N2YSwgcHJldHR5X2NvbG9ycz1GQUxTRSkKYl9zJHNjYXR0ZXIKYl9zJGNvcnJlbGF0aW9uCmBgYAoKIyMgTm9iYXRjaCB2cy4gYmF0Y2ggaW4gbW9kZWwsIGVkZ2VyCgpgYGB7ciBjb21wYXJlX25vYmF0Y2hfYmF0Y2hfZWRnZXJ9CmxwX21hY3Jfbm9iYXRjaF9iYXRjaCA8LSBtZXJnZSgKICBscF9tYWNyX25vYmF0Y2gkZWRnZXIkYWxsX3RhYmxlcyRzaF92c19jaHIsCiAgbHBfbWFjcl9iYXRjaCRlZGdlciRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBieT0icm93Lm5hbWVzIikKcm93bmFtZXMobHBfbWFjcl9ub2JhdGNoX2JhdGNoKSA8LSBscF9tYWNyX25vYmF0Y2hfYmF0Y2hbWyJSb3cubmFtZXMiXV0KbHBfbWFjcl9ub2JhdGNoX2JhdGNoIDwtIGxwX21hY3Jfbm9iYXRjaF9iYXRjaFssIGMoImxvZ0ZDLngiLCJsb2dGQy55IildCmNvbG5hbWVzKGxwX21hY3Jfbm9iYXRjaF9iYXRjaCkgPC0gYygibm9iYXRjaCIsImJhdGNoIikKbmJfYiA8LSBzbShwbG90X2xpbmVhcl9zY2F0dGVyKGxwX21hY3Jfbm9iYXRjaF9iYXRjaCwgcHJldHR5X2NvbG9ycz1GQUxTRSkpCm5iX2Ikc2NhdHRlcgpuYl9iJGNvcnJlbGF0aW9uCmBgYAoKIyMgQmF0Y2ggaW4gbW9kZWwgdnMuIFNWQSwgZWRnZXIKCmBgYHtyIGNvbXBhcmVfYmF0Y2hfc3ZhX2VkZ2VyfQpscF9tYWNyX2JhdGNoX3N2YSA8LSBtZXJnZSgKICBscF9tYWNyX2JhdGNoJGVkZ2VyJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGxwX21hY3Jfc3ZhJGVkZ2VyJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhscF9tYWNyX2JhdGNoX3N2YSkgPC0gbHBfbWFjcl9iYXRjaF9zdmFbWyJSb3cubmFtZXMiXV0KbHBfbWFjcl9iYXRjaF9zdmEgPC0gbHBfbWFjcl9iYXRjaF9zdmFbLCBjKCJsb2dGQy54IiwibG9nRkMueSIpXQpjb2xuYW1lcyhscF9tYWNyX2JhdGNoX3N2YSkgPC0gYygiYmF0Y2giLCJzdmEiKQpiX3MgPC0gcGxvdF9saW5lYXJfc2NhdHRlcihscF9tYWNyX2JhdGNoX3N2YSwgcHJldHR5X2NvbG9ycz1GQUxTRSkKYl9zJHNjYXR0ZXIKYl9zJGNvcnJlbGF0aW9uCmBgYAoKIyMgQ29tcGFyZSBub2JhdGNoIHZzLiBiYXRjaCwgZGVzZXEKCmBgYHtyIGNvbXBhcmVfbm9iYXRjaF9iYXRjaF9kZXNlcX0KbHBfbWFjcl9ub2JhdGNoX2JhdGNoIDwtIG1lcmdlKAogIGxwX21hY3Jfbm9iYXRjaCRkZXNlcSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBscF9tYWNyX2JhdGNoJGRlc2VxJGFsbF90YWJsZXMkc2hfdnNfY2hyLAogIGJ5PSJyb3cubmFtZXMiKQpyb3duYW1lcyhscF9tYWNyX25vYmF0Y2hfYmF0Y2gpIDwtIGxwX21hY3Jfbm9iYXRjaF9iYXRjaFtbIlJvdy5uYW1lcyJdXQpscF9tYWNyX25vYmF0Y2hfYmF0Y2ggPC0gbHBfbWFjcl9ub2JhdGNoX2JhdGNoWywgYygibG9nRkMueCIsImxvZ0ZDLnkiKV0KY29sbmFtZXMobHBfbWFjcl9ub2JhdGNoX2JhdGNoKSA8LSBjKCJub2JhdGNoIiwiYmF0Y2giKQpuYl9iIDwtIHNtKHBsb3RfbGluZWFyX3NjYXR0ZXIobHBfbWFjcl9ub2JhdGNoX2JhdGNoLCBwcmV0dHlfY29sb3JzPUZBTFNFKSkKbmJfYiRzY2F0dGVyCm5iX2IkY29ycmVsYXRpb24KYGBgCgojIyBDb21wYXJlIGJhdGNoIHZzLiBTVkEsIGRlc2VxCgpgYGB7ciBjb21wYXJlX2JhdGNoX3N2YV9kZXNlcX0KbHBfbWFjcl9iYXRjaF9zdmEgPC0gbWVyZ2UoCiAgbHBfbWFjcl9iYXRjaCRkZXNlcSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBscF9tYWNyX3N2YSRkZXNlcSRhbGxfdGFibGVzJHNoX3ZzX2NociwKICBieT0icm93Lm5hbWVzIikKcm93bmFtZXMobHBfbWFjcl9iYXRjaF9zdmEpIDwtIGxwX21hY3JfYmF0Y2hfc3ZhW1siUm93Lm5hbWVzIl1dCmxwX21hY3JfYmF0Y2hfc3ZhIDwtIGxwX21hY3JfYmF0Y2hfc3ZhWywgYygibG9nRkMueCIsImxvZ0ZDLnkiKV0KY29sbmFtZXMobHBfbWFjcl9iYXRjaF9zdmEpIDwtIGMoImJhdGNoIiwic3ZhIikKYl9zIDwtIHNtKHBsb3RfbGluZWFyX3NjYXR0ZXIobHBfbWFjcl9iYXRjaF9zdmEsIHByZXR0eV9jb2xvcnM9RkFMU0UpKQpiX3Mkc2NhdHRlcgpiX3MkY29ycmVsYXRpb24KYGBgCgpgYGB7ciBzYXZlbWV9CnBhbmRlcjo6cGFuZGVyKHNlc3Npb25JbmZvKCkpCm1lc3NhZ2UocGFzdGUwKCJUaGlzIGlzIGhwZ2x0b29scyBjb21taXQ6ICIsIGdldF9naXRfY29tbWl0KCkpKQp0aGlzX3NhdmUgPC0gcGFzdGUwKGdzdWIocGF0dGVybj0iXFwuUm1kIiwgcmVwbGFjZT0iIiwgeD1ybWRfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKQptZXNzYWdlKHBhc3RlMCgiU2F2aW5nIHRvICIsIHRoaXNfc2F2ZSkpCnRtcCA8LSBzbShzYXZlbWUoZmlsZW5hbWU9dGhpc19zYXZlKSkKYGBgCg==