ALL IMPORTED FILES MUST BE IN THE SAME DIRECTORY AS THIS SCRIPT
Load tidyverse package
library(tidyverse)
Import raw vcf data from “masterfile” and exclude variants with GnomAD non-cancer AF > 0.005
masterfile <- read.delim("masterfile_181009_exomes_with_missing_regions_gnomad2.1_rare_variants_loftee_GnomAD3_canonical_refseq_or_LRG_consequence_4-6.tsv", header=TRUE, row.names=NULL, na.strings = ".", stringsAsFactors=FALSE) %>%
dplyr::rename("Sample"="X.Sample")
masterfile_rare_non_cancer <- filter(masterfile, GnomAD_v2.1_non_cancer_AF_nfe<=0.005)
Exclude non-epithelial, uterine-only and BRCA +ve samples + non-NFE samples
sample_ethnicities <- read.delim("exomes_pca_with_gnomad_info.tsv", stringsAsFactors=FALSE)
NFE_samples <- filter(sample_ethnicities, GnomAD_class%in%"European")
ViP_Complete_Cohort <- read.delim("ViP_LoF_Complete_Cohort.txt", stringsAsFactors=FALSE)
ViP_Complete_Cohort_list_NFE <- filter(ViP_Complete_Cohort,Exome.ID%in%c(NFE_samples$Sample))
masterfile_eoc <- filter(masterfile_rare_non_cancer,Sample%in%c(ViP_Complete_Cohort_list_NFE$Exome.ID)) %>%
filter(Sample%in%(NFE_samples$Sample)) %>%
filter(GnomAD_v2.1_non_cancer_AF_nfe<=0.005)
rm(masterfile)
Append patient path data
ViP_OvCa_Path_Data <- read.delim("ViP_OvCa_Path_Data.txt", header=TRUE, row.names=NULL, stringsAsFactors=FALSE) %>%
dplyr::rename("Sample"=Exome.ID)
masterfile_eoc_withPath <- left_join(masterfile_eoc,ViP_OvCa_Path_Data,by="Sample",copy=FALSE)
rm(ViP_OvCa_Path_Data)
rm(masterfile_eoc)
Exclude low-quality variants and GnomAD RF/InbreedingCoeff-flagged variants
masterfile_eoc_goodQ <- filter(masterfile_eoc_withPath, (QUAL>=30)&
(Identified!="FilteredInAll")&
(Sample.PMCDP>=10)&
(Sample.PMCFREQ>=0.25)) %>%
filter((!str_detect(GnomAD_v2.1_FILTER_exome,"InbreedingCoeff")&
!str_detect(GnomAD_v2.1_FILTER_exome,"RF"))|
is.na(GnomAD_v2.1_FILTER_exome)) %>%
filter((!str_detect(GnomAD_v2.1_FILTER_genome,"InbreedingCoeff")&
!str_detect(GnomAD_v2.1_FILTER_genome,"RF"))|
is.na(GnomAD_v2.1_FILTER_genome))
rm(masterfile_eoc_withPath)
Exclude other mutation +ve samples (MLH1/MSH2/MSH6/PMS2, TP53, RAD51C/D, BRIP1)
ViP_Discovery_Cohort <- read.delim("ViP_LoF_Discovery_Cohort.txt", stringsAsFactors=FALSE)
ViP_Discovery_Cohort_list <- ViP_Discovery_Cohort[,1]
masterfile_eoc_goodQ2 <- filter(masterfile_eoc_goodQ,Sample%in%c(ViP_Discovery_Cohort_list))
Exclude HIGH and MODERATE impact variants
masterfile_eoc_goodQ2_LOW <- filter(masterfile_eoc_goodQ2, IMPACT%in%"LOW")
rm(masterfile_eoc_goodQ2)
Exclude non-synonymous variants
masterfile_eoc_goodQ2_LOW_synonymous_NoLOFTEE <- filter(masterfile_eoc_goodQ2_LOW,Consequence%in%c("synonymous_variant","synonymous_variant,NMD_transcript_variant")) %>%
filter(!LoF%in%c("HC","LC"))
rm(masterfile_eoc_goodQ2_LOW)
Separate Ensembl canonical and RefSeq canonical variants
masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical <- filter(masterfile_eoc_goodQ2_LOW_synonymous_NoLOFTEE,CANONICAL%in%"YES")
Sample variant counts and AFs
variants_heterozygous <- masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical %>%
select(HGVSc,Sample.GT) %>%
group_by(HGVSc,Sample.GT) %>%
summarise(n()) %>%
filter((Sample.GT%in%c("'0/1","'1/0"))) %>%
rename(alleles = "n()") %>%
group_by(HGVSc) %>%
summarise(sum(alleles))
variants_homozygous <- masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical %>%
select(HGVSc,Sample.GT) %>%
group_by(HGVSc,Sample.GT) %>%
summarise(n()) %>%
filter((Sample.GT!="'0/1")&(Sample.GT!="'1/0")) %>%
mutate(alleles = `n()`*2) %>%
select(-`n()`) %>%
group_by(HGVSc) %>%
summarise(sum(alleles))
variants <- full_join(variants_heterozygous,variants_homozygous,by="HGVSc",copy=FALSE,suffix=c(".x",".y")) %>%
mutate_all(funs(replace(., is.na(.), 0))) %>%
mutate(Total_Allele_Count=`sum(alleles).x`+`sum(alleles).y`) %>%
mutate(Sample_AF=Total_Allele_Count/(n_distinct(masterfile_eoc_goodQ2_LOW_synonymous_NoLOFTEE$Sample)*2))
masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_2 <- left_join(masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical,variants[,c(1,4:5)],by="HGVSc",copy=FALSE)
Exclude variants with sample AF >0.01
masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01 <- filter(masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_2,Sample_AF < 0.01)
Output list of genes and variants with sample AF >0.01
masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_genesandvariants0.01 <- filter(masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_2,Sample_AF > 0.01)
masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_genesandvariants0.01[,c(2:6,25:54)] %>%
distinct(.keep_all = FALSE) %>%
write_excel_csv(path="masterfile_eoc_goodQ_0.001_LOW_synonymous_NoLOFTEE_ENSTcanonical_genesandvariants0.01.csv",na=".",append=FALSE,col_names=TRUE)
Sample gene counts and frequencies
genes_oneVarAllele <- masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01 %>% select(Gene,Sample.GT) %>%
select(Gene,Sample.GT) %>%
group_by(Gene,Sample.GT) %>%
summarise(n()) %>%
filter((Sample.GT%in%c("'0/1","'1/0"))) %>%
rename(gene_Var = "n()") %>%
group_by(Gene) %>%
summarise(sum(gene_Var))
genes_twoVarAllele <- masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01 %>% select(Gene,Sample.GT) %>%
select(Gene,Sample.GT) %>%
group_by(Gene,Sample.GT) %>%
summarise(n()) %>%
filter((Sample.GT!="'0/1")&(Sample.GT!="'1/0")) %>%
mutate(gene_Var = `n()`*2) %>%
select(-`n()`) %>%
group_by(Gene) %>%
summarise(sum(gene_Var))
genes <- full_join(genes_oneVarAllele,genes_twoVarAllele,by="Gene",copy=FALSE,suffix=c(".x",".y")) %>%
mutate_all(funs(replace(., is.na(.), 0))) %>%
mutate(Total_Gene_Count=`sum(gene_Var).x`+`sum(gene_Var).y`) %>%
mutate(Sample_Gene_Freq=Total_Gene_Count/(n_distinct(masterfile_eoc_goodQ2_LOW_synonymous_NoLOFTEE$Sample)*2))
masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_2 <- left_join(masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01, genes[,c(1,4:5)],by="Gene",copy=FALSE)
Add GnomAD gene-level data
ensembl_biotypes <- read.delim("ensembl_biotypes.tsv", stringsAsFactors=FALSE) %>%
select(ensembl_transcript_id,transcript_biotype) %>%
dplyr::rename("Feature"="ensembl_transcript_id","BIOTYPE"="transcript_biotype")
all_features_exons_only_fraction_covered_agilent_v6_base_figures <- read.delim("~/all_features_exons_only_fraction_covered_agilent_v6_150bp_extended.tsv")
gnomad_coverage_coding_features <- read.delim("~/gnomad_coverage_coding_features.tsv") %>%
mutate("total_10x_non_cancer_count"=((exome_10x)*56885)+((genome_10x)*7718)) %>%
mutate("total_10x_non_cancer_NFE_count"=((exome_10x)*51377)+((genome_10x)*7718))
gnomad_coverage_coding_features$total_10x_non_cancer_count=round(gnomad_coverage_coding_features$total_10x_non_cancer_count)
gnomad_coverage_coding_features$total_10x_non_cancer_NFE_count=round(gnomad_coverage_coding_features$total_10x_non_cancer_NFE_count)
GnomAD_stats_synonymous_VEP_NO_RF <- read.delim("agilent_v6_gnomad_v2.1.1_genestats_canonical_SYN_noLOFTEE.tsv", header=TRUE, row.names=NULL, stringsAsFactors=FALSE) %>%
left_join(ensembl_biotypes,by="Feature",copy=FALSE) %>%
filter(BIOTYPE%in%c("protein_coding")) %>%
distinct(.keep_all = FALSE) %>%
left_join(all_features_exons_only_fraction_covered_agilent_v6_base_figures[,c(1,4)],by="Feature",copy=FALSE) %>%
left_join(gnomad_coverage_coding_features[,c(1,6,7)],by="Feature",copy=FALSE)
masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_withGnomADstats <- left_join(masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_2, GnomAD_stats_synonymous_VEP_NO_RF[,c(2,20:25,27:29)],by="Feature",copy=FALSE)
masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_withGnomADstats2 <- mutate_at(masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_withGnomADstats,vars(starts_with("FILTER_")),funs(replace(., is.na(.), 0))) %>%
mutate_if(grepl("popmax$", names(.)),funs(ifelse(. == "NA", 0, as.numeric(.)))) %>%
mutate_at(vars(ends_with("popmax")),funs(replace(., is.na(.), 0)))
Create data frames with Agilent SureSelect whole exome genes (all ENST, protein-coding only and non-protein-coding)
GnomAD_stats_AgilentSSv6_list <- read.delim("agilent_v6_gnomad_v2.1.1_genestats_canonical_SYN_noLOFTEE.tsv", header=TRUE, row.names=NULL, stringsAsFactors=FALSE) %>%
filter(CANONICAL=="YES") %>%
distinct(.keep_all = FALSE) %>%
select(SYMBOL:ENSP)
GnomAD_stats_AgilentSSv6_SYN_NO_LOFTEE_VEP_ENSTcanonical_protein_coding <- filter(GnomAD_stats_synonymous_VEP_NO_RF,Feature%in%c(GnomAD_stats_AgilentSSv6_list$Feature)) %>%
dplyr::rename("Gene"="ENSG") %>%
filter(BIOTYPE%in%c("protein_coding")) %>%
distinct(.keep_all = FALSE)
allGenes_AgilentSSv6_list_protein_coding_only <- tibble("SYMBOL"=GnomAD_stats_AgilentSSv6_SYN_NO_LOFTEE_VEP_ENSTcanonical_protein_coding$SYMBOL,"Gene"=GnomAD_stats_AgilentSSv6_SYN_NO_LOFTEE_VEP_ENSTcanonical_protein_coding$Gene,"Feature"=GnomAD_stats_AgilentSSv6_SYN_NO_LOFTEE_VEP_ENSTcanonical_protein_coding$Feature) %>%
distinct(.keep_all=TRUE) %>%
arrange(SYMBOL)
allGenes_masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_biotype_list <- tibble("SYMBOL"=masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_withGnomADstats2$SYMBOL,"Gene"=masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_withGnomADstats2$Gene,"BIOTYPE"=masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_withGnomADstats2$BIOTYPE) %>%
distinct(.keep_all=TRUE) %>%
filter(BIOTYPE%in%c("protein_coding")) %>%
arrange(SYMBOL)
comparison_sampleAF0.01_biotype_allGenes <- masterfile_eoc_goodQ_0.005_LOW_synonymous_NoLOFTEE_ENSTcanonical_sampleAF0.01_withGnomADstats2 %>%
mutate(Total_Case_Bases=(n_distinct(masterfile_eoc_goodQ2_LOW_synonymous_NoLOFTEE$Sample)*2*Total_Bases_Covered)) %>%
filter(BIOTYPE%in%c("protein_coding")) %>%
select(SYMBOL,Gene,Total_Gene_Count,Total_Case_Bases) %>%
distinct(SYMBOL,Gene,Total_Gene_Count,Total_Case_Bases)
comparison_sampleAF0.01_biotype_allGenes2 <- bind_rows(comparison_sampleAF0.01_biotype_allGenes, anti_join(allGenes_AgilentSSv6_list_protein_coding_only,comparison_sampleAF0.01_biotype_allGenes,by="Gene")) %>%
mutate_at(vars("Total_Gene_Count"),funs(replace(., is.na(.), 0)))
comparison_sampleAF0.01_biotype_allGenes3 <- right_join(
comparison_sampleAF0.01_biotype_allGenes2,
GnomAD_stats_AgilentSSv6_SYN_NO_LOFTEE_VEP_ENSTcanonical_protein_coding,
by="Gene",copy=FALSE) %>%
mutate_at(vars("Total_Case_Bases"),funs(replace(., is.na(.), n_distinct(masterfile_eoc_goodQ2_LOW_synonymous_NoLOFTEE$Sample)*2*Total_Bases_Covered))) %>%
select("SYMBOL.x","Gene","Total_Gene_Count","Total_Case_Bases","FILTER_RF_SYN_NO_LOFTEE_AC_0.005","FILTER_RF_SYN_NO_LOFTEE_AC_0.005_NFE","Total_Bases_Covered","total_10x_non_cancer_count","total_10x_non_cancer_NFE_count") %>%
dplyr::rename("SYMBOL"="SYMBOL.x") %>%
mutate("Total_GnomAD_Bases"=total_10x_non_cancer_count*2*Total_Bases_Covered) %>%
mutate("Total_GnomAD_NFE_Bases"=total_10x_non_cancer_NFE_count*2*Total_Bases_Covered) %>%
filter(Total_Bases_Covered!=0) %>%
distinct(.keep_all=TRUE)
Calculate total LoF variants in sample vs total LoF variants in GnomAD non-cancer NFE and use for chi-squared test
oddsratio <- function (a, b = NULL, c = NULL, d = NULL, conf.level = 0.95,
p.calc.by.independence = TRUE)
{
if (is.matrix(a)) {
if ((dim(a)[1] != 2L) | (dim(a)[2] != 2L)) {
stop("Input matrix must be a 2x2 table.")
}
.a <- a[1, 1]
.b <- a[1, 2]
.c <- a[2, 1]
.d <- a[2, 2]
.data.name <- deparse(substitute(a))
}
else {
.a <- a
.b <- b
.c <- c
.d <- d
.data.name <- paste(deparse(substitute(a)), deparse(substitute(b)),
deparse(substitute(c)), deparse(substitute(d)))
}
.MAT <- matrix(c(.a, .b, M1 <- .a + .b, .c, .d, M0 <- .c +
.d, N1 <- .a + .c, N0 <- .b + .d, Total <- .a + .b +
.c + .d), 3, 3)
colnames(.MAT) <- c("Sample", "GnomAD", "Total") #("Disease", "Nondisease", "Total")
rownames(.MAT) <- c("Syn", "No Syn", "Total") #("Exposed", "Nonexposed", "Total")
class(.MAT) <- "table"
print(.MAT)
ESTIMATE <- (.a /.b)/(.c/.d)
norm.pp <- qnorm(1 - (1 - conf.level)/2)
if (p.calc.by.independence) {
p.v <- 2 * (1 - pnorm(abs((.a - N1 * M1/Total)/sqrt(N1 *
N0 * M1 * M0/Total/Total/(Total - 1)))))
}
else {
p.v <- 2 * (1 - pnorm(log(ifelse(ESTIMATE > 1, ESTIMATE,
1/ESTIMATE))/sqrt(1/.a + 1/.b + 1/.c + 1/.d)))
}
ORL <- ESTIMATE * exp(-norm.pp * sqrt(1/.a + 1/.b + 1/.c +
1/.d))
ORU <- ESTIMATE * exp(norm.pp * sqrt(1/.a + 1/.b + 1/.c +
1/.d)) %>% signif(digits=7)
CINT <- paste(signif(ORL,digits = 7),signif(ORU,digits = 7),sep="~")
attr(CINT, "conf.level") <- conf.level
RVAL <- list(p.value = p.v, conf.int = CINT, estimate = ESTIMATE,
method = "Odds ratio estimate and its significance probability",
data.name = .data.name)
class(RVAL) <- "htest"
return(RVAL)
}
a <- sum(comparison_sampleAF0.01_biotype_allGenes3$Total_Gene_Count) %>% as.numeric()
b <- n_distinct(masterfile_eoc_goodQ2_LOW_synonymous_NoLOFTEE$Sample)*2*33218304 %>% as.numeric()
b1 <- b-a %>% as.numeric()
c <- sum(comparison_sampleAF0.01_biotype_allGenes3$FILTER_RF_SYN_NO_LOFTEE_AC_0.005) %>% as.numeric()
d <- (30651075*2*118479)+(32482680*2*15708) %>% as.numeric()
d1 <- d-c %>% as.numeric()
e <- sum(comparison_sampleAF0.01_biotype_allGenes3$FILTER_RF_SYN_NO_LOFTEE_AC_0.005_NFE) %>% as.numeric()
f <- (30651075*2*51377)+(32482680*2*7718) %>% as.numeric()
f1 <- f-e %>% as.numeric()
chiX2_0.005_biotype <- matrix(c(a,b1,c,d1),nrow=2,byrow=TRUE)
chiX2_0.005_NFE_biotype <- matrix(c(a,b1,e,f1),nrow=2,byrow=TRUE)
sink(file="masterfile_eoc_SYN_NO_LOFTEE_comparison_chiX2_results.txt",append=FALSE)
chisq.test(chiX2_0.005_biotype,correct=FALSE)
oddsratio(chiX2_0.005_biotype)
chisq.test(chiX2_0.005_NFE_biotype,correct=FALSE)
oddsratio(chiX2_0.005_NFE_biotype)
sink()
LS0tCnRpdGxlOiAiVGhlc2lzIFN5bm9ueW1vdXMgVmFyaWFudHMgQW5hbHlzaXMgU2NyaXB0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpBTEwgSU1QT1JURUQgRklMRVMgTVVTVCBCRSBJTiBUSEUgU0FNRSBESVJFQ1RPUlkgQVMgVEhJUyBTQ1JJUFQKCkxvYWQgdGlkeXZlcnNlIHBhY2thZ2UKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCkltcG9ydCByYXcgdmNmIGRhdGEgZnJvbSAibWFzdGVyZmlsZSIgYW5kIGV4Y2x1ZGUgdmFyaWFudHMgd2l0aCBHbm9tQUQgbm9uLWNhbmNlciBBRiA+IDAuMDA1CmBgYHtyfQptYXN0ZXJmaWxlIDwtIHJlYWQuZGVsaW0oIm1hc3RlcmZpbGVfMTgxMDA5X2V4b21lc193aXRoX21pc3NpbmdfcmVnaW9uc19nbm9tYWQyLjFfcmFyZV92YXJpYW50c19sb2Z0ZWVfR25vbUFEM19jYW5vbmljYWxfcmVmc2VxX29yX0xSR19jb25zZXF1ZW5jZV80LTYudHN2IiwgaGVhZGVyPVRSVUUsIHJvdy5uYW1lcz1OVUxMLCBuYS5zdHJpbmdzID0gIi4iLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSAlPiUgCiAgZHBseXI6OnJlbmFtZSgiU2FtcGxlIj0iWC5TYW1wbGUiKSAKICAKbWFzdGVyZmlsZV9yYXJlX25vbl9jYW5jZXIgPC0gZmlsdGVyKG1hc3RlcmZpbGUsIEdub21BRF92Mi4xX25vbl9jYW5jZXJfQUZfbmZlPD0wLjAwNSkKYGBgCgpFeGNsdWRlIG5vbi1lcGl0aGVsaWFsLCB1dGVyaW5lLW9ubHkgYW5kIEJSQ0EgK3ZlIHNhbXBsZXMgKyBub24tTkZFIHNhbXBsZXMKYGBge3J9CnNhbXBsZV9ldGhuaWNpdGllcyA8LSByZWFkLmRlbGltKCJleG9tZXNfcGNhX3dpdGhfZ25vbWFkX2luZm8udHN2Iiwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKTkZFX3NhbXBsZXMgPC0gZmlsdGVyKHNhbXBsZV9ldGhuaWNpdGllcywgR25vbUFEX2NsYXNzJWluJSJFdXJvcGVhbiIpCgpWaVBfQ29tcGxldGVfQ29ob3J0IDwtIHJlYWQuZGVsaW0oIlZpUF9Mb0ZfQ29tcGxldGVfQ29ob3J0LnR4dCIsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpClZpUF9Db21wbGV0ZV9Db2hvcnRfbGlzdF9ORkUgPC0gZmlsdGVyKFZpUF9Db21wbGV0ZV9Db2hvcnQsRXhvbWUuSUQlaW4lYyhORkVfc2FtcGxlcyRTYW1wbGUpKQoKbWFzdGVyZmlsZV9lb2MgPC0gZmlsdGVyKG1hc3RlcmZpbGVfcmFyZV9ub25fY2FuY2VyLFNhbXBsZSVpbiVjKFZpUF9Db21wbGV0ZV9Db2hvcnRfbGlzdF9ORkUkRXhvbWUuSUQpKSAlPiUgCiAgZmlsdGVyKFNhbXBsZSVpbiUoTkZFX3NhbXBsZXMkU2FtcGxlKSkgJT4lCiAgZmlsdGVyKEdub21BRF92Mi4xX25vbl9jYW5jZXJfQUZfbmZlPD0wLjAwNSkKCnJtKG1hc3RlcmZpbGUpCmBgYAoKQXBwZW5kIHBhdGllbnQgcGF0aCBkYXRhCmBgYHtyfQpWaVBfT3ZDYV9QYXRoX0RhdGEgPC0gcmVhZC5kZWxpbSgiVmlQX092Q2FfUGF0aF9EYXRhLnR4dCIsIGhlYWRlcj1UUlVFLCByb3cubmFtZXM9TlVMTCwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkgJT4lIAogIGRwbHlyOjpyZW5hbWUoIlNhbXBsZSI9RXhvbWUuSUQpCm1hc3RlcmZpbGVfZW9jX3dpdGhQYXRoIDwtIGxlZnRfam9pbihtYXN0ZXJmaWxlX2VvYyxWaVBfT3ZDYV9QYXRoX0RhdGEsYnk9IlNhbXBsZSIsY29weT1GQUxTRSkKCnJtKFZpUF9PdkNhX1BhdGhfRGF0YSkKcm0obWFzdGVyZmlsZV9lb2MpCmBgYAoKRXhjbHVkZSBsb3ctcXVhbGl0eSB2YXJpYW50cyBhbmQgR25vbUFEIFJGL0luYnJlZWRpbmdDb2VmZi1mbGFnZ2VkIHZhcmlhbnRzCmBgYHtyfQptYXN0ZXJmaWxlX2VvY19nb29kUSA8LSBmaWx0ZXIobWFzdGVyZmlsZV9lb2Nfd2l0aFBhdGgsIChRVUFMPj0zMCkmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChJZGVudGlmaWVkIT0iRmlsdGVyZWRJbkFsbCIpJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoU2FtcGxlLlBNQ0RQPj0xMCkmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChTYW1wbGUuUE1DRlJFUT49MC4yNSkpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCghc3RyX2RldGVjdChHbm9tQURfdjIuMV9GSUxURVJfZXhvbWUsIkluYnJlZWRpbmdDb2VmZiIpJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXN0cl9kZXRlY3QoR25vbUFEX3YyLjFfRklMVEVSX2V4b21lLCJSRiIpKXwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXMubmEoR25vbUFEX3YyLjFfRklMVEVSX2V4b21lKSkgJT4lIAogIGZpbHRlcigoIXN0cl9kZXRlY3QoR25vbUFEX3YyLjFfRklMVEVSX2dlbm9tZSwiSW5icmVlZGluZ0NvZWZmIikmCiAgICAgICAgICAgICFzdHJfZGV0ZWN0KEdub21BRF92Mi4xX0ZJTFRFUl9nZW5vbWUsIlJGIikpfAogICAgICAgICAgIGlzLm5hKEdub21BRF92Mi4xX0ZJTFRFUl9nZW5vbWUpKQoKcm0obWFzdGVyZmlsZV9lb2Nfd2l0aFBhdGgpCmBgYAoKRXhjbHVkZSBvdGhlciBtdXRhdGlvbiArdmUgc2FtcGxlcyAoTUxIMS9NU0gyL01TSDYvUE1TMiwgVFA1MywgUkFENTFDL0QsIEJSSVAxKQpgYGB7cn0KVmlQX0Rpc2NvdmVyeV9Db2hvcnQgPC0gcmVhZC5kZWxpbSgiVmlQX0xvRl9EaXNjb3ZlcnlfQ29ob3J0LnR4dCIsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpClZpUF9EaXNjb3ZlcnlfQ29ob3J0X2xpc3QgPC0gVmlQX0Rpc2NvdmVyeV9Db2hvcnRbLDFdCgptYXN0ZXJmaWxlX2VvY19nb29kUTIgPC0gZmlsdGVyKG1hc3RlcmZpbGVfZW9jX2dvb2RRLFNhbXBsZSVpbiVjKFZpUF9EaXNjb3ZlcnlfQ29ob3J0X2xpc3QpKQpgYGAKCkV4Y2x1ZGUgSElHSCBhbmQgTU9ERVJBVEUgaW1wYWN0IHZhcmlhbnRzCmBgYHtyfQptYXN0ZXJmaWxlX2VvY19nb29kUTJfTE9XIDwtIGZpbHRlcihtYXN0ZXJmaWxlX2VvY19nb29kUTIsIElNUEFDVCVpbiUiTE9XIikKCnJtKG1hc3RlcmZpbGVfZW9jX2dvb2RRMikKYGBgCgpFeGNsdWRlIG5vbi1zeW5vbnltb3VzIHZhcmlhbnRzCmBgYHtyfQptYXN0ZXJmaWxlX2VvY19nb29kUTJfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUUgPC0gZmlsdGVyKG1hc3RlcmZpbGVfZW9jX2dvb2RRMl9MT1csQ29uc2VxdWVuY2UlaW4lYygic3lub255bW91c192YXJpYW50Iiwic3lub255bW91c192YXJpYW50LE5NRF90cmFuc2NyaXB0X3ZhcmlhbnQiKSkgJT4lIAogIGZpbHRlcighTG9GJWluJWMoIkhDIiwiTEMiKSkKCnJtKG1hc3RlcmZpbGVfZW9jX2dvb2RRMl9MT1cpCmBgYAoKU2VwYXJhdGUgRW5zZW1ibCBjYW5vbmljYWwgYW5kIFJlZlNlcSBjYW5vbmljYWwgdmFyaWFudHMKYGBge3J9Cm1hc3RlcmZpbGVfZW9jX2dvb2RRXzAuMDA1X0xPV19zeW5vbnltb3VzX05vTE9GVEVFX0VOU1RjYW5vbmljYWwgPC0gZmlsdGVyKG1hc3RlcmZpbGVfZW9jX2dvb2RRMl9MT1dfc3lub255bW91c19Ob0xPRlRFRSxDQU5PTklDQUwlaW4lIllFUyIpIApgYGAKClNhbXBsZSB2YXJpYW50IGNvdW50cyBhbmQgQUZzCmBgYHtyfQp2YXJpYW50c19oZXRlcm96eWdvdXMgPC0gbWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbCAlPiUgCiAgc2VsZWN0KEhHVlNjLFNhbXBsZS5HVCkgJT4lIAogIGdyb3VwX2J5KEhHVlNjLFNhbXBsZS5HVCkgJT4lIAogIHN1bW1hcmlzZShuKCkpICU+JSAKICBmaWx0ZXIoKFNhbXBsZS5HVCVpbiVjKCInMC8xIiwiJzEvMCIpKSkgJT4lIAogIHJlbmFtZShhbGxlbGVzID0gIm4oKSIpICU+JSAKICBncm91cF9ieShIR1ZTYykgJT4lIAogIHN1bW1hcmlzZShzdW0oYWxsZWxlcykpCnZhcmlhbnRzX2hvbW96eWdvdXMgPC0gbWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbCAlPiUgCiAgc2VsZWN0KEhHVlNjLFNhbXBsZS5HVCkgJT4lIAogIGdyb3VwX2J5KEhHVlNjLFNhbXBsZS5HVCkgJT4lIAogIHN1bW1hcmlzZShuKCkpICU+JSAKICBmaWx0ZXIoKFNhbXBsZS5HVCE9IicwLzEiKSYoU2FtcGxlLkdUIT0iJzEvMCIpKSAlPiUgCiAgbXV0YXRlKGFsbGVsZXMgPSBgbigpYCoyKSAlPiUgCiAgc2VsZWN0KC1gbigpYCkgJT4lIAogIGdyb3VwX2J5KEhHVlNjKSAlPiUgCiAgc3VtbWFyaXNlKHN1bShhbGxlbGVzKSkKdmFyaWFudHMgPC0gZnVsbF9qb2luKHZhcmlhbnRzX2hldGVyb3p5Z291cyx2YXJpYW50c19ob21venlnb3VzLGJ5PSJIR1ZTYyIsY29weT1GQUxTRSxzdWZmaXg9YygiLngiLCIueSIpKSAlPiUKICBtdXRhdGVfYWxsKGZ1bnMocmVwbGFjZSguLCBpcy5uYSguKSwgMCkpKSAlPiUgCiAgbXV0YXRlKFRvdGFsX0FsbGVsZV9Db3VudD1gc3VtKGFsbGVsZXMpLnhgK2BzdW0oYWxsZWxlcykueWApICU+JSAKICBtdXRhdGUoU2FtcGxlX0FGPVRvdGFsX0FsbGVsZV9Db3VudC8obl9kaXN0aW5jdChtYXN0ZXJmaWxlX2VvY19nb29kUTJfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUUkU2FtcGxlKSoyKSkKbWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF8yIDwtIGxlZnRfam9pbihtYXN0ZXJmaWxlX2VvY19nb29kUV8wLjAwNV9MT1dfc3lub255bW91c19Ob0xPRlRFRV9FTlNUY2Fub25pY2FsLHZhcmlhbnRzWyxjKDEsNDo1KV0sYnk9IkhHVlNjIixjb3B5PUZBTFNFKQpgYGAKCkV4Y2x1ZGUgdmFyaWFudHMgd2l0aCBzYW1wbGUgQUYgPjAuMDEKYGBge3J9Cm1hc3RlcmZpbGVfZW9jX2dvb2RRXzAuMDA1X0xPV19zeW5vbnltb3VzX05vTE9GVEVFX0VOU1RjYW5vbmljYWxfc2FtcGxlQUYwLjAxIDwtIGZpbHRlcihtYXN0ZXJmaWxlX2VvY19nb29kUV8wLjAwNV9MT1dfc3lub255bW91c19Ob0xPRlRFRV9FTlNUY2Fub25pY2FsXzIsU2FtcGxlX0FGIDwgMC4wMSkKYGBgCgpPdXRwdXQgbGlzdCBvZiBnZW5lcyBhbmQgdmFyaWFudHMgd2l0aCBzYW1wbGUgQUYgPjAuMDEKYGBge3J9Cm1hc3RlcmZpbGVfZW9jX2dvb2RRXzAuMDA1X0xPV19zeW5vbnltb3VzX05vTE9GVEVFX0VOU1RjYW5vbmljYWxfZ2VuZXNhbmR2YXJpYW50czAuMDEgPC0gZmlsdGVyKG1hc3RlcmZpbGVfZW9jX2dvb2RRXzAuMDA1X0xPV19zeW5vbnltb3VzX05vTE9GVEVFX0VOU1RjYW5vbmljYWxfMixTYW1wbGVfQUYgPiAwLjAxKQptYXN0ZXJmaWxlX2VvY19nb29kUV8wLjAwNV9MT1dfc3lub255bW91c19Ob0xPRlRFRV9FTlNUY2Fub25pY2FsX2dlbmVzYW5kdmFyaWFudHMwLjAxWyxjKDI6NiwyNTo1NCldICU+JSAKICBkaXN0aW5jdCgua2VlcF9hbGwgPSBGQUxTRSkgJT4lIAogIHdyaXRlX2V4Y2VsX2NzdihwYXRoPSJtYXN0ZXJmaWxlX2VvY19nb29kUV8wLjAwMV9MT1dfc3lub255bW91c19Ob0xPRlRFRV9FTlNUY2Fub25pY2FsX2dlbmVzYW5kdmFyaWFudHMwLjAxLmNzdiIsbmE9Ii4iLGFwcGVuZD1GQUxTRSxjb2xfbmFtZXM9VFJVRSkKYGBgCgpTYW1wbGUgZ2VuZSBjb3VudHMgYW5kIGZyZXF1ZW5jaWVzCmBgYHtyfQpnZW5lc19vbmVWYXJBbGxlbGUgPC0gbWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF9zYW1wbGVBRjAuMDEgJT4lIHNlbGVjdChHZW5lLFNhbXBsZS5HVCkgJT4lIAogIHNlbGVjdChHZW5lLFNhbXBsZS5HVCkgJT4lIAogIGdyb3VwX2J5KEdlbmUsU2FtcGxlLkdUKSAlPiUgCiAgc3VtbWFyaXNlKG4oKSkgJT4lIAogIGZpbHRlcigoU2FtcGxlLkdUJWluJWMoIicwLzEiLCInMS8wIikpKSAlPiUgCiAgcmVuYW1lKGdlbmVfVmFyID0gIm4oKSIpICU+JSAKICBncm91cF9ieShHZW5lKSAlPiUgCiAgc3VtbWFyaXNlKHN1bShnZW5lX1ZhcikpCmdlbmVzX3R3b1ZhckFsbGVsZSA8LSBtYXN0ZXJmaWxlX2VvY19nb29kUV8wLjAwNV9MT1dfc3lub255bW91c19Ob0xPRlRFRV9FTlNUY2Fub25pY2FsX3NhbXBsZUFGMC4wMSAlPiUgc2VsZWN0KEdlbmUsU2FtcGxlLkdUKSAlPiUgCiAgc2VsZWN0KEdlbmUsU2FtcGxlLkdUKSAlPiUgCiAgZ3JvdXBfYnkoR2VuZSxTYW1wbGUuR1QpICU+JSAKICBzdW1tYXJpc2UobigpKSAlPiUgCiAgZmlsdGVyKChTYW1wbGUuR1QhPSInMC8xIikmKFNhbXBsZS5HVCE9IicxLzAiKSkgJT4lIAogIG11dGF0ZShnZW5lX1ZhciA9IGBuKClgKjIpICU+JSAKICBzZWxlY3QoLWBuKClgKSAlPiUgCiAgZ3JvdXBfYnkoR2VuZSkgJT4lIAogIHN1bW1hcmlzZShzdW0oZ2VuZV9WYXIpKQpnZW5lcyA8LSBmdWxsX2pvaW4oZ2VuZXNfb25lVmFyQWxsZWxlLGdlbmVzX3R3b1ZhckFsbGVsZSxieT0iR2VuZSIsY29weT1GQUxTRSxzdWZmaXg9YygiLngiLCIueSIpKSAlPiUgCiAgbXV0YXRlX2FsbChmdW5zKHJlcGxhY2UoLiwgaXMubmEoLiksIDApKSkgJT4lIAogIG11dGF0ZShUb3RhbF9HZW5lX0NvdW50PWBzdW0oZ2VuZV9WYXIpLnhgK2BzdW0oZ2VuZV9WYXIpLnlgKSAlPiUgCiAgbXV0YXRlKFNhbXBsZV9HZW5lX0ZyZXE9VG90YWxfR2VuZV9Db3VudC8obl9kaXN0aW5jdChtYXN0ZXJmaWxlX2VvY19nb29kUTJfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUUkU2FtcGxlKSoyKSkKbWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF9zYW1wbGVBRjAuMDFfMiA8LSBsZWZ0X2pvaW4obWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF9zYW1wbGVBRjAuMDEsIGdlbmVzWyxjKDEsNDo1KV0sYnk9IkdlbmUiLGNvcHk9RkFMU0UpCmBgYAoKQWRkIEdub21BRCBnZW5lLWxldmVsIGRhdGEKYGBge3J9CmVuc2VtYmxfYmlvdHlwZXMgPC0gcmVhZC5kZWxpbSgiZW5zZW1ibF9iaW90eXBlcy50c3YiLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSAlPiUgCiAgc2VsZWN0KGVuc2VtYmxfdHJhbnNjcmlwdF9pZCx0cmFuc2NyaXB0X2Jpb3R5cGUpICU+JSAKICBkcGx5cjo6cmVuYW1lKCJGZWF0dXJlIj0iZW5zZW1ibF90cmFuc2NyaXB0X2lkIiwiQklPVFlQRSI9InRyYW5zY3JpcHRfYmlvdHlwZSIpCgphbGxfZmVhdHVyZXNfZXhvbnNfb25seV9mcmFjdGlvbl9jb3ZlcmVkX2FnaWxlbnRfdjZfYmFzZV9maWd1cmVzIDwtIHJlYWQuZGVsaW0oIn4vYWxsX2ZlYXR1cmVzX2V4b25zX29ubHlfZnJhY3Rpb25fY292ZXJlZF9hZ2lsZW50X3Y2XzE1MGJwX2V4dGVuZGVkLnRzdiIpCgpnbm9tYWRfY292ZXJhZ2VfY29kaW5nX2ZlYXR1cmVzIDwtIHJlYWQuZGVsaW0oIn4vZ25vbWFkX2NvdmVyYWdlX2NvZGluZ19mZWF0dXJlcy50c3YiKSAlPiUgCiAgbXV0YXRlKCJ0b3RhbF8xMHhfbm9uX2NhbmNlcl9jb3VudCI9KChleG9tZV8xMHgpKjU2ODg1KSsoKGdlbm9tZV8xMHgpKjc3MTgpKSAlPiUgCiAgbXV0YXRlKCJ0b3RhbF8xMHhfbm9uX2NhbmNlcl9ORkVfY291bnQiPSgoZXhvbWVfMTB4KSo1MTM3NykrKChnZW5vbWVfMTB4KSo3NzE4KSkKZ25vbWFkX2NvdmVyYWdlX2NvZGluZ19mZWF0dXJlcyR0b3RhbF8xMHhfbm9uX2NhbmNlcl9jb3VudD1yb3VuZChnbm9tYWRfY292ZXJhZ2VfY29kaW5nX2ZlYXR1cmVzJHRvdGFsXzEweF9ub25fY2FuY2VyX2NvdW50KQpnbm9tYWRfY292ZXJhZ2VfY29kaW5nX2ZlYXR1cmVzJHRvdGFsXzEweF9ub25fY2FuY2VyX05GRV9jb3VudD1yb3VuZChnbm9tYWRfY292ZXJhZ2VfY29kaW5nX2ZlYXR1cmVzJHRvdGFsXzEweF9ub25fY2FuY2VyX05GRV9jb3VudCkKCkdub21BRF9zdGF0c19zeW5vbnltb3VzX1ZFUF9OT19SRiA8LSByZWFkLmRlbGltKCJhZ2lsZW50X3Y2X2dub21hZF92Mi4xLjFfZ2VuZXN0YXRzX2Nhbm9uaWNhbF9TWU5fbm9MT0ZURUUudHN2IiwgaGVhZGVyPVRSVUUsIHJvdy5uYW1lcz1OVUxMLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSAlPiUgCiAgbGVmdF9qb2luKGVuc2VtYmxfYmlvdHlwZXMsYnk9IkZlYXR1cmUiLGNvcHk9RkFMU0UpICU+JSAKICBmaWx0ZXIoQklPVFlQRSVpbiVjKCJwcm90ZWluX2NvZGluZyIpKSAlPiUgCiAgZGlzdGluY3QoLmtlZXBfYWxsID0gRkFMU0UpICU+JSAKICBsZWZ0X2pvaW4oYWxsX2ZlYXR1cmVzX2V4b25zX29ubHlfZnJhY3Rpb25fY292ZXJlZF9hZ2lsZW50X3Y2X2Jhc2VfZmlndXJlc1ssYygxLDQpXSxieT0iRmVhdHVyZSIsY29weT1GQUxTRSkgJT4lIAogIGxlZnRfam9pbihnbm9tYWRfY292ZXJhZ2VfY29kaW5nX2ZlYXR1cmVzWyxjKDEsNiw3KV0sYnk9IkZlYXR1cmUiLGNvcHk9RkFMU0UpCiAgCm1hc3RlcmZpbGVfZW9jX2dvb2RRXzAuMDA1X0xPV19zeW5vbnltb3VzX05vTE9GVEVFX0VOU1RjYW5vbmljYWxfc2FtcGxlQUYwLjAxX3dpdGhHbm9tQURzdGF0cyA8LSBsZWZ0X2pvaW4obWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF9zYW1wbGVBRjAuMDFfMiwgR25vbUFEX3N0YXRzX3N5bm9ueW1vdXNfVkVQX05PX1JGWyxjKDIsMjA6MjUsMjc6MjkpXSxieT0iRmVhdHVyZSIsY29weT1GQUxTRSkKbWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF9zYW1wbGVBRjAuMDFfd2l0aEdub21BRHN0YXRzMiA8LSBtdXRhdGVfYXQobWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF9zYW1wbGVBRjAuMDFfd2l0aEdub21BRHN0YXRzLHZhcnMoc3RhcnRzX3dpdGgoIkZJTFRFUl8iKSksZnVucyhyZXBsYWNlKC4sIGlzLm5hKC4pLCAwKSkpICU+JSAKICBtdXRhdGVfaWYoZ3JlcGwoInBvcG1heCQiLCBuYW1lcyguKSksZnVucyhpZmVsc2UoLiA9PSAiTkEiLCAwLCBhcy5udW1lcmljKC4pKSkpICU+JSAKICBtdXRhdGVfYXQodmFycyhlbmRzX3dpdGgoInBvcG1heCIpKSxmdW5zKHJlcGxhY2UoLiwgaXMubmEoLiksIDApKSkKYGBgCgpDcmVhdGUgZGF0YSBmcmFtZXMgd2l0aCBBZ2lsZW50IFN1cmVTZWxlY3Qgd2hvbGUgZXhvbWUgZ2VuZXMgKGFsbCBFTlNULCBwcm90ZWluLWNvZGluZyBvbmx5IGFuZCBub24tcHJvdGVpbi1jb2RpbmcpCmBgYHtyfQpHbm9tQURfc3RhdHNfQWdpbGVudFNTdjZfbGlzdCA8LSAgcmVhZC5kZWxpbSgiYWdpbGVudF92Nl9nbm9tYWRfdjIuMS4xX2dlbmVzdGF0c19jYW5vbmljYWxfU1lOX25vTE9GVEVFLnRzdiIsIGhlYWRlcj1UUlVFLCByb3cubmFtZXM9TlVMTCwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkgJT4lIAogIGZpbHRlcihDQU5PTklDQUw9PSJZRVMiKSAlPiUgCiAgZGlzdGluY3QoLmtlZXBfYWxsID0gRkFMU0UpICU+JSAKICBzZWxlY3QoU1lNQk9MOkVOU1ApCgpHbm9tQURfc3RhdHNfQWdpbGVudFNTdjZfU1lOX05PX0xPRlRFRV9WRVBfRU5TVGNhbm9uaWNhbF9wcm90ZWluX2NvZGluZyA8LSBmaWx0ZXIoR25vbUFEX3N0YXRzX3N5bm9ueW1vdXNfVkVQX05PX1JGLEZlYXR1cmUlaW4lYyhHbm9tQURfc3RhdHNfQWdpbGVudFNTdjZfbGlzdCRGZWF0dXJlKSkgJT4lIAogIGRwbHlyOjpyZW5hbWUoIkdlbmUiPSJFTlNHIikgJT4lIAogIGZpbHRlcihCSU9UWVBFJWluJWMoInByb3RlaW5fY29kaW5nIikpICU+JSAKICBkaXN0aW5jdCgua2VlcF9hbGwgPSBGQUxTRSkKCmFsbEdlbmVzX0FnaWxlbnRTU3Y2X2xpc3RfcHJvdGVpbl9jb2Rpbmdfb25seSA8LSB0aWJibGUoIlNZTUJPTCI9R25vbUFEX3N0YXRzX0FnaWxlbnRTU3Y2X1NZTl9OT19MT0ZURUVfVkVQX0VOU1RjYW5vbmljYWxfcHJvdGVpbl9jb2RpbmckU1lNQk9MLCJHZW5lIj1Hbm9tQURfc3RhdHNfQWdpbGVudFNTdjZfU1lOX05PX0xPRlRFRV9WRVBfRU5TVGNhbm9uaWNhbF9wcm90ZWluX2NvZGluZyRHZW5lLCJGZWF0dXJlIj1Hbm9tQURfc3RhdHNfQWdpbGVudFNTdjZfU1lOX05PX0xPRlRFRV9WRVBfRU5TVGNhbm9uaWNhbF9wcm90ZWluX2NvZGluZyRGZWF0dXJlKSAlPiUgCiAgZGlzdGluY3QoLmtlZXBfYWxsPVRSVUUpICU+JSAKICBhcnJhbmdlKFNZTUJPTCkKCmFsbEdlbmVzX21hc3RlcmZpbGVfZW9jX2dvb2RRXzAuMDA1X0xPV19zeW5vbnltb3VzX05vTE9GVEVFX0VOU1RjYW5vbmljYWxfc2FtcGxlQUYwLjAxX2Jpb3R5cGVfbGlzdCA8LSB0aWJibGUoIlNZTUJPTCI9bWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF9zYW1wbGVBRjAuMDFfd2l0aEdub21BRHN0YXRzMiRTWU1CT0wsIkdlbmUiPW1hc3RlcmZpbGVfZW9jX2dvb2RRXzAuMDA1X0xPV19zeW5vbnltb3VzX05vTE9GVEVFX0VOU1RjYW5vbmljYWxfc2FtcGxlQUYwLjAxX3dpdGhHbm9tQURzdGF0czIkR2VuZSwiQklPVFlQRSI9bWFzdGVyZmlsZV9lb2NfZ29vZFFfMC4wMDVfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUVfRU5TVGNhbm9uaWNhbF9zYW1wbGVBRjAuMDFfd2l0aEdub21BRHN0YXRzMiRCSU9UWVBFKSAlPiUgCiAgZGlzdGluY3QoLmtlZXBfYWxsPVRSVUUpICU+JSAKICBmaWx0ZXIoQklPVFlQRSVpbiVjKCJwcm90ZWluX2NvZGluZyIpKSAlPiUgCiAgYXJyYW5nZShTWU1CT0wpCgpjb21wYXJpc29uX3NhbXBsZUFGMC4wMV9iaW90eXBlX2FsbEdlbmVzIDwtIG1hc3RlcmZpbGVfZW9jX2dvb2RRXzAuMDA1X0xPV19zeW5vbnltb3VzX05vTE9GVEVFX0VOU1RjYW5vbmljYWxfc2FtcGxlQUYwLjAxX3dpdGhHbm9tQURzdGF0czIgJT4lIAogIG11dGF0ZShUb3RhbF9DYXNlX0Jhc2VzPShuX2Rpc3RpbmN0KG1hc3RlcmZpbGVfZW9jX2dvb2RRMl9MT1dfc3lub255bW91c19Ob0xPRlRFRSRTYW1wbGUpKjIqVG90YWxfQmFzZXNfQ292ZXJlZCkpICU+JSAKICBmaWx0ZXIoQklPVFlQRSVpbiVjKCJwcm90ZWluX2NvZGluZyIpKSAlPiUgCiAgc2VsZWN0KFNZTUJPTCxHZW5lLFRvdGFsX0dlbmVfQ291bnQsVG90YWxfQ2FzZV9CYXNlcykgJT4lIAogIGRpc3RpbmN0KFNZTUJPTCxHZW5lLFRvdGFsX0dlbmVfQ291bnQsVG90YWxfQ2FzZV9CYXNlcykgCmNvbXBhcmlzb25fc2FtcGxlQUYwLjAxX2Jpb3R5cGVfYWxsR2VuZXMyIDwtIGJpbmRfcm93cyhjb21wYXJpc29uX3NhbXBsZUFGMC4wMV9iaW90eXBlX2FsbEdlbmVzLCBhbnRpX2pvaW4oYWxsR2VuZXNfQWdpbGVudFNTdjZfbGlzdF9wcm90ZWluX2NvZGluZ19vbmx5LGNvbXBhcmlzb25fc2FtcGxlQUYwLjAxX2Jpb3R5cGVfYWxsR2VuZXMsYnk9IkdlbmUiKSkgJT4lIAogIG11dGF0ZV9hdCh2YXJzKCJUb3RhbF9HZW5lX0NvdW50IiksZnVucyhyZXBsYWNlKC4sIGlzLm5hKC4pLCAwKSkpCmNvbXBhcmlzb25fc2FtcGxlQUYwLjAxX2Jpb3R5cGVfYWxsR2VuZXMzIDwtIHJpZ2h0X2pvaW4oCiAgY29tcGFyaXNvbl9zYW1wbGVBRjAuMDFfYmlvdHlwZV9hbGxHZW5lczIsCiAgR25vbUFEX3N0YXRzX0FnaWxlbnRTU3Y2X1NZTl9OT19MT0ZURUVfVkVQX0VOU1RjYW5vbmljYWxfcHJvdGVpbl9jb2RpbmcsCiAgYnk9IkdlbmUiLGNvcHk9RkFMU0UpICU+JSAKICBtdXRhdGVfYXQodmFycygiVG90YWxfQ2FzZV9CYXNlcyIpLGZ1bnMocmVwbGFjZSguLCBpcy5uYSguKSwgbl9kaXN0aW5jdChtYXN0ZXJmaWxlX2VvY19nb29kUTJfTE9XX3N5bm9ueW1vdXNfTm9MT0ZURUUkU2FtcGxlKSoyKlRvdGFsX0Jhc2VzX0NvdmVyZWQpKSkgJT4lIAogIHNlbGVjdCgiU1lNQk9MLngiLCJHZW5lIiwiVG90YWxfR2VuZV9Db3VudCIsIlRvdGFsX0Nhc2VfQmFzZXMiLCJGSUxURVJfUkZfU1lOX05PX0xPRlRFRV9BQ18wLjAwNSIsIkZJTFRFUl9SRl9TWU5fTk9fTE9GVEVFX0FDXzAuMDA1X05GRSIsIlRvdGFsX0Jhc2VzX0NvdmVyZWQiLCJ0b3RhbF8xMHhfbm9uX2NhbmNlcl9jb3VudCIsInRvdGFsXzEweF9ub25fY2FuY2VyX05GRV9jb3VudCIpICU+JSAKICBkcGx5cjo6cmVuYW1lKCJTWU1CT0wiPSJTWU1CT0wueCIpICU+JSAKICBtdXRhdGUoIlRvdGFsX0dub21BRF9CYXNlcyI9dG90YWxfMTB4X25vbl9jYW5jZXJfY291bnQqMipUb3RhbF9CYXNlc19Db3ZlcmVkKSAlPiUgCiAgbXV0YXRlKCJUb3RhbF9Hbm9tQURfTkZFX0Jhc2VzIj10b3RhbF8xMHhfbm9uX2NhbmNlcl9ORkVfY291bnQqMipUb3RhbF9CYXNlc19Db3ZlcmVkKSAlPiUgCiAgZmlsdGVyKFRvdGFsX0Jhc2VzX0NvdmVyZWQhPTApICU+JSAKICBkaXN0aW5jdCgua2VlcF9hbGw9VFJVRSkKYGBgCgpDYWxjdWxhdGUgdG90YWwgTG9GIHZhcmlhbnRzIGluIHNhbXBsZSB2cyB0b3RhbCBMb0YgdmFyaWFudHMgaW4gR25vbUFEIG5vbi1jYW5jZXIgTkZFIGFuZCB1c2UgZm9yIGNoaS1zcXVhcmVkIHRlc3QKYGBge3J9Cm9kZHNyYXRpbyA8LSBmdW5jdGlvbiAoYSwgYiA9IE5VTEwsIGMgPSBOVUxMLCBkID0gTlVMTCwgY29uZi5sZXZlbCA9IDAuOTUsIAogICAgcC5jYWxjLmJ5LmluZGVwZW5kZW5jZSA9IFRSVUUpIAp7CiAgICBpZiAoaXMubWF0cml4KGEpKSB7CiAgICAgICAgaWYgKChkaW0oYSlbMV0gIT0gMkwpIHwgKGRpbShhKVsyXSAhPSAyTCkpIHsKICAgICAgICAgICAgc3RvcCgiSW5wdXQgbWF0cml4IG11c3QgYmUgYSAyeDIgdGFibGUuIikKICAgICAgICB9CiAgICAgICAgLmEgPC0gYVsxLCAxXQogICAgICAgIC5iIDwtIGFbMSwgMl0KICAgICAgICAuYyA8LSBhWzIsIDFdCiAgICAgICAgLmQgPC0gYVsyLCAyXQogICAgICAgIC5kYXRhLm5hbWUgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGEpKQogICAgfQogICAgZWxzZSB7CiAgICAgICAgLmEgPC0gYQogICAgICAgIC5iIDwtIGIKICAgICAgICAuYyA8LSBjCiAgICAgICAgLmQgPC0gZAogICAgICAgIC5kYXRhLm5hbWUgPC0gcGFzdGUoZGVwYXJzZShzdWJzdGl0dXRlKGEpKSwgZGVwYXJzZShzdWJzdGl0dXRlKGIpKSwgCiAgICAgICAgICAgIGRlcGFyc2Uoc3Vic3RpdHV0ZShjKSksIGRlcGFyc2Uoc3Vic3RpdHV0ZShkKSkpCiAgICB9CiAgICAuTUFUIDwtIG1hdHJpeChjKC5hLCAuYiwgTTEgPC0gLmEgKyAuYiwgLmMsIC5kLCBNMCA8LSAuYyArIAogICAgICAgIC5kLCBOMSA8LSAuYSArIC5jLCBOMCA8LSAuYiArIC5kLCBUb3RhbCA8LSAuYSArIC5iICsgCiAgICAgICAgLmMgKyAuZCksIDMsIDMpCiAgICBjb2xuYW1lcyguTUFUKSA8LSBjKCJTYW1wbGUiLCAiR25vbUFEIiwgIlRvdGFsIikgIygiRGlzZWFzZSIsICJOb25kaXNlYXNlIiwgIlRvdGFsIikKICAgIHJvd25hbWVzKC5NQVQpIDwtIGMoIlN5biIsICJObyBTeW4iLCAiVG90YWwiKSAjKCJFeHBvc2VkIiwgIk5vbmV4cG9zZWQiLCAiVG90YWwiKQogICAgY2xhc3MoLk1BVCkgPC0gInRhYmxlIgogICAgcHJpbnQoLk1BVCkKICAgIEVTVElNQVRFIDwtICguYSAvLmIpLyguYy8uZCkKICAgIG5vcm0ucHAgPC0gcW5vcm0oMSAtICgxIC0gY29uZi5sZXZlbCkvMikKICAgIGlmIChwLmNhbGMuYnkuaW5kZXBlbmRlbmNlKSB7CiAgICAgICAgcC52IDwtIDIgKiAoMSAtIHBub3JtKGFicygoLmEgLSBOMSAqIE0xL1RvdGFsKS9zcXJ0KE4xICogCiAgICAgICAgICAgIE4wICogTTEgKiBNMC9Ub3RhbC9Ub3RhbC8oVG90YWwgLSAxKSkpKSkKICAgIH0KICAgIGVsc2UgewogICAgICAgIHAudiA8LSAyICogKDEgLSBwbm9ybShsb2coaWZlbHNlKEVTVElNQVRFID4gMSwgRVNUSU1BVEUsIAogICAgICAgICAgICAxL0VTVElNQVRFKSkvc3FydCgxLy5hICsgMS8uYiArIDEvLmMgKyAxLy5kKSkpCiAgICB9CiAgICBPUkwgPC0gRVNUSU1BVEUgKiBleHAoLW5vcm0ucHAgKiBzcXJ0KDEvLmEgKyAxLy5iICsgMS8uYyArIAogICAgICAgIDEvLmQpKQogICAgT1JVIDwtIEVTVElNQVRFICogZXhwKG5vcm0ucHAgKiBzcXJ0KDEvLmEgKyAxLy5iICsgMS8uYyArIAogICAgICAgIDEvLmQpKSAlPiUgc2lnbmlmKGRpZ2l0cz03KQogICAgQ0lOVCA8LSBwYXN0ZShzaWduaWYoT1JMLGRpZ2l0cyA9IDcpLHNpZ25pZihPUlUsZGlnaXRzID0gNyksc2VwPSJ+IikKICAgIGF0dHIoQ0lOVCwgImNvbmYubGV2ZWwiKSA8LSBjb25mLmxldmVsCiAgICBSVkFMIDwtIGxpc3QocC52YWx1ZSA9IHAudiwgY29uZi5pbnQgPSBDSU5ULCBlc3RpbWF0ZSA9IEVTVElNQVRFLCAKICAgICAgICBtZXRob2QgPSAiT2RkcyByYXRpbyBlc3RpbWF0ZSBhbmQgaXRzIHNpZ25pZmljYW5jZSBwcm9iYWJpbGl0eSIsIAogICAgICAgIGRhdGEubmFtZSA9IC5kYXRhLm5hbWUpCiAgICBjbGFzcyhSVkFMKSA8LSAiaHRlc3QiCiAgICByZXR1cm4oUlZBTCkKfQoKYSA8LSBzdW0oY29tcGFyaXNvbl9zYW1wbGVBRjAuMDFfYmlvdHlwZV9hbGxHZW5lczMkVG90YWxfR2VuZV9Db3VudCkgJT4lIGFzLm51bWVyaWMoKQpiIDwtIG5fZGlzdGluY3QobWFzdGVyZmlsZV9lb2NfZ29vZFEyX0xPV19zeW5vbnltb3VzX05vTE9GVEVFJFNhbXBsZSkqMiozMzIxODMwNCAlPiUgYXMubnVtZXJpYygpCmIxIDwtIGItYSAlPiUgYXMubnVtZXJpYygpCmMgPC0gc3VtKGNvbXBhcmlzb25fc2FtcGxlQUYwLjAxX2Jpb3R5cGVfYWxsR2VuZXMzJEZJTFRFUl9SRl9TWU5fTk9fTE9GVEVFX0FDXzAuMDA1KSAlPiUgYXMubnVtZXJpYygpCmQgPC0gKDMwNjUxMDc1KjIqMTE4NDc5KSsoMzI0ODI2ODAqMioxNTcwOCkgJT4lIGFzLm51bWVyaWMoKQpkMSA8LSBkLWMgJT4lIGFzLm51bWVyaWMoKQplIDwtIHN1bShjb21wYXJpc29uX3NhbXBsZUFGMC4wMV9iaW90eXBlX2FsbEdlbmVzMyRGSUxURVJfUkZfU1lOX05PX0xPRlRFRV9BQ18wLjAwNV9ORkUpICU+JSBhcy5udW1lcmljKCkKZiA8LSAoMzA2NTEwNzUqMio1MTM3NykrKDMyNDgyNjgwKjIqNzcxOCkgJT4lIGFzLm51bWVyaWMoKQpmMSA8LSBmLWUgJT4lIGFzLm51bWVyaWMoKQoKY2hpWDJfMC4wMDVfYmlvdHlwZSA8LSBtYXRyaXgoYyhhLGIxLGMsZDEpLG5yb3c9MixieXJvdz1UUlVFKQpjaGlYMl8wLjAwNV9ORkVfYmlvdHlwZSA8LSBtYXRyaXgoYyhhLGIxLGUsZjEpLG5yb3c9MixieXJvdz1UUlVFKQoKc2luayhmaWxlPSJtYXN0ZXJmaWxlX2VvY19TWU5fTk9fTE9GVEVFX2NvbXBhcmlzb25fY2hpWDJfcmVzdWx0cy50eHQiLGFwcGVuZD1GQUxTRSkKCmNoaXNxLnRlc3QoY2hpWDJfMC4wMDVfYmlvdHlwZSxjb3JyZWN0PUZBTFNFKQpvZGRzcmF0aW8oY2hpWDJfMC4wMDVfYmlvdHlwZSkKY2hpc3EudGVzdChjaGlYMl8wLjAwNV9ORkVfYmlvdHlwZSxjb3JyZWN0PUZBTFNFKQpvZGRzcmF0aW8oY2hpWDJfMC4wMDVfTkZFX2Jpb3R5cGUpCgpzaW5rKCkKYGBgCg==