ALL IMPORTED FILES MUST BE IN THE SAME DIRECTORY AS THIS SCRIPT

Load tidyverse package

library("tidyverse")

Import individual tumour exome somatic variants (‘f’) and tumour-only variants (‘g’) files

max.callers = 3
f <- read.table("tumour_sample_germline_exome_somatic_caller_file.tsv", header=TRUE, stringsAsFactors=FALSE, sep="\t", comment.char="", quote="") %>%
  dplyr::rename("Tumour_Sample"="X.Tumour_Sample") %>% 
  mutate(n.callers = setNames(sapply(strsplit(unique(Identified), "-"), 
                                     function(callers){
                                       ifelse("Intersection" %in% callers, max.callers, sum(!grepl("^filter", callers)))
                                       }), unique(Identified))[Identified])
g <- read.delim("tumour_sample_germline_paired_exomes_HAP.tsv", header=TRUE, stringsAsFactors=FALSE, sep="\t", comment.char="") %>% 
  dplyr::rename("Tumour_Sample"="X.Tumour_Sample")
names(f)
names(g)
sort(unique(f$Tumour_Sample))
sort(unique(g$Tumour_Sample))
ViP_list <- read.delim("ViP Samples & Candidate Genes.txt", header=TRUE, stringsAsFactors=FALSE, sep="\t", comment.char="") %>% 
  arrange(Sample)
tumour_type <- select(ViP_list,Sample,Tumour.Type) %>% distinct() %>% rename("Sample"="Normal_Sample")
f$Normal_Sample <- as.character(f$Normal_Sample)
g$Normal_Sample <- as.character(g$Normal_Sample)
f <- left_join(f,tumour_type,by="Normal_Sample",copy=FALSE)
g0 <- left_join(g,tumour_type,by="Normal_Sample",copy=FALSE) %>% filter(CANONICAL%in%"YES")
gene_list <- select(ViP_list,Sample,SYMBOL) %>% filter(Sample%in%(f$Normal_Sample))
genes <- gene_list$SYMBOL
tumour_purity <- read.delim("Tumour Purity.txt", header=TRUE, stringsAsFactors=FALSE, sep="\t", comment.char="") %>%
  select(Tumour_Sample,Purity,No_Mutations)
f <- left_join(f,tumour_purity,by="Tumour_Sample",copy=FALSE)
g0 <- left_join(g0,tumour_purity,by="Tumour_Sample",copy=FALSE)

Convert all relevant strings to integers/doubles, and remove variants that fail QC in one or more somatic pipeline callers or are Consequence 7 (IMPACT==MODIFIER)

f$QUAL <- as.numeric(f$QUAL) %>% replace_na(0)
f$TUMOUR.PMCAD <- as.numeric(f$TUMOUR.PMCAD) %>% replace_na(0)
f$TUMOUR.PMCDP <- as.numeric(f$TUMOUR.PMCDP) %>% replace_na(0)
f$TUMOUR.PMCFREQ <- as.numeric(f$TUMOUR.PMCFREQ) %>% replace_na(0)
f$NORMAL.PMCAD <- as.numeric(f$NORMAL.PMCAD) %>% replace_na(0)
f$NORMAL.PMCDP <- as.numeric(f$NORMAL.PMCDP) %>% replace_na(0)
f$NORMAL.PMCFREQ <- as.numeric(f$NORMAL.PMCFREQ) %>% replace_na(0)
f$GnomAD_v3_AF <- as.numeric(f$GnomAD_v3_AF) %>% replace_na(0)

g$QUAL <- as.numeric(g$QUAL) %>% replace_na(0)
g$TUMOUR.PMCAD <- as.numeric(g$TUMOUR.PMCAD) %>% replace_na(0)
g$TUMOUR.PMCDP <- as.numeric(g$TUMOUR.PMCDP) %>% replace_na(0)
g$TUMOUR.PMCFREQ <- as.numeric(g$TUMOUR.PMCFREQ) %>% replace_na(0)
g$NORMAL.PMCAD <- as.numeric(g$NORMAL.PMCAD) %>% replace_na(0)
g$NORMAL.PMCDP <- as.numeric(g$NORMAL.PMCDP) %>% replace_na(0)
g$NORMAL.PMCFREQ <- as.numeric(g$NORMAL.PMCFREQ) %>% replace_na(0)
g$GnomAD_v3_AF <- as.numeric(g$GnomAD_v3_AF) %>% replace_na(0)

f0 <- filter(f,!Identified%in%c("FilteredInAll")) %>% 
    filter(n.callers>=2) %>%
    filter(Consequence_Rank<7)

List unique, sorted elements in Identified (Somatic Caller QC results), Consequence Rank, NORMAL.PMCAD (germline alt allele base depth), NORMAL.PMCADP (germline total base depth) NORMAL.PMCFREQ (germline alt allele read freq), GnomAD v2.1/3 AF and QUAL score

sort(unique(f0$Identified))
sort(unique(f0$Consequence_Rank))
sort(unique(f0$NORMAL.PMCAD))
sort(unique(f0$NORMAL.PMCDP))
sort(unique(f0$NORMAL.PMCFREQ))
sort(unique(f0$GnomAD_v2.1_non_cancer_AF))
sort(unique(f0$GnomAD_v3_AF))
sort(unique(f0$QUAL))

Plot pre-filtering distribution of tumour alt allele read number(TUMOUR.PMCAD), depth (TUMOUR.PMCDP) and frequency (TUMOUR.PMCFREQ)

sort(unique(f0$TUMOUR.PMCAD))
hist(f0$TUMOUR.PMCAD, breaks=2000, main="Distribution of TUMOUR.PMCAD Values", xlab="TUMOUR.PMCAD",xlim=c(0,20))
sort(unique(f0$TUMOUR.PMCDP))
hist(f0$TUMOUR.PMCDP, breaks=4000, main="Distribution of TUMOUR.PMCDP Values", xlab="TUMOUR.PMCDP",xlim=c(0,100))
sort(unique(f0$TUMOUR.PMCFREQ))
hist(f0$TUMOUR.PMCFREQ, breaks=200, main="Distribution of TUMOUR.PMCFREQ Values", xlab="TUMOUR.PMCFREQ",xlim=c(0,1))

Filter out and remove elements/rows with high number alt allele reads in germline (NORMAL.PMCAD), high germline alt allele read freq (NORMAL.PMCFREQ), low germline and tumour read depths (NORMAL/TUMOUR.PMCDP), low number alt allele reads in tumour (TUMOUR.PMCAD), low tumour alt allele read freq (TUMOUR.PMCFREQ) and common variants (using GnomAD v2.1/3 AFs); list remaining unique elements from those fields; and save output (for ENSEMBL CANONICAL transcripts only)

f1<-filter(f0,(NORMAL.PMCAD <= 2) & (NORMAL.PMCDP >= 10) & (NORMAL.PMCFREQ < 0.01))
f2<-filter(f1,TUMOUR.PMCAD >= 5)
f3<-filter(f2,TUMOUR.PMCDP>=20)
f4<-filter(f3,TUMOUR.PMCFREQ >= (f3$Purity*0.5*(2/3)))
f5<-filter(f4,GnomAD_v2.1_non_cancer_AF <= 0.0001)
f6<-filter(f5,GnomAD_v3_AF <= 0.0001)

sort(unique(f1$QUAL))
sort(unique(f2$TUMOUR.PMCAD))
sort(unique(f2$QUAL))
sort(unique(f3$TUMOUR.PMCDP))
sort(unique(f3$QUAL))
sort(unique(f4$TUMOUR.PMCFREQ))
sort(unique(f4$QUAL))
sort(unique(f5$GnomAD_v2.1_non_cancer_AF))
sort(unique(f6$GnomAD_v3_AF))
samples<-unique(f$Tumour_Sample)
for (sample in samples){
  dir.create(sample)
  setwd(sample)
f6 %>% 
  filter(CANONICAL%in%"YES") %>% 
  write_excel_csv(path=paste(samples,"tumour_germline_exome_somatic_caller_file_filtered.csv",sep="_"), na=".",append=FALSE,col_names=TRUE)
}

Plot post-filtering distribution of tumour alt allele read number(TUMOUR.PMCAD), depth (TUMOUR.PMCDP) and frequency (TUMOUR.PMCFREQ)

sort(unique(f6$TUMOUR.PMCAD))
hist(f6$TUMOUR.PMCAD, breaks=2000, main="Distribution of TUMOUR.PMCAD Values", xlab="TUMOUR.PMCAD",xlim=c(0,20))
sort(unique(f6$TUMOUR.PMCDP))
hist(f6$TUMOUR.PMCDP, breaks=4000, main="Distribution of TUMOUR.PMCDP Values", xlab="TUMOUR.PMCDP",xlim=c(0,100))
sort(unique(f6$TUMOUR.PMCFREQ))
hist(f6$TUMOUR.PMCFREQ, breaks=200, main="Distribution of TUMOUR.PMCFREQ Values", xlab="TUMOUR.PMCFREQ",xlim=c(0,1))

Extract rows with variants in gene(s) of interest pre- and post-filtering (e.g. TP53) from somatic variants file, and save output

HGS_tumour_genes <- c("TP53","BRCA1","BRCA2","PTEN","NF1","RB1","CDK12","CDKN2A")
END_tumour_genes <- c("ARID1A","PIK3CA","PTEN","CTNNB1","KRAS","BRAF","TP53","MET")
setwd(sample)
f0_genes <- if (f0$Tumour.Type%in%"High-grade endometrioid")
{
  filter(f0,(SYMBOL%in%END_tumour_genes)|
                     (SYMBOL%in%c(genes))) %>% 
  write_excel_csv(path=paste(samples,"tumour_germline_exome_somatic_caller_genes_unfiltered.csv",sep="_"), na=".",append=FALSE,col_names=TRUE)
}else{
  filter(f0,(SYMBOL%in%HGS_tumour_genes)|
                     (SYMBOL%in%c(genes))) %>% 
  write_excel_csv(path=paste(samples,"tumour_germline_exome_somatic_caller_genes_unfiltered.csv",sep="_"), na=".",append=FALSE,col_names=TRUE)
}
f6_genes <- if (f6$Tumour.Type%in%"High-grade endometrioid")
{
  filter(f6,(SYMBOL%in%END_tumour_genes)|
                     (SYMBOL%in%c(genes))) %>% 
  write_excel_csv(path=paste(samples,"tumour_germline_exome_somatic_caller_genes_filtered.csv",sep="_"), na=".",append=FALSE,col_names=TRUE)
}else{
  filter(f6,(SYMBOL%in%HGS_tumour_genes)|
                     (SYMBOL%in%c(genes))) %>% 
  write_excel_csv(sample,path=paste(samples,"tumour_germline_exome_somatic_caller_genes_filtered.csv",sep="_"), na=".",append=FALSE,col_names=TRUE)
}

Extract equivalent rows with variants in gene(s) of interest (one gene transcript per variant) from tumour-only variants file, and save output

setwd(sample)
g_genes <-
  if (g0$Tumour.Type%in%"High-grade endometrioid")
{
  filter(g0,(SYMBOL%in%END_tumour_genes)|
                     (SYMBOL%in%c(genes)))
}else{
  filter(g0,(SYMBOL%in%HGS_tumour_genes)|
                     (SYMBOL%in%c(genes)))
} %>% 
  arrange(Transcript_Index) %>% 
  distinct(CHROM,POS,REF,ALT,.keep_all=TRUE)
  write_excel_csv(g_genes,path=paste(sample,"tumour_germline_paired_exomes_HAP_genes.csv",sep="_"), na=".",append=FALSE,col_names=TRUE)
LS0tCnRpdGxlOiAiVGhlc2lzIFR1bW91ciBFeG9tZSBWYXJpYW50cyBGaWx0ZXJpbmcgU2NyaXB0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpBTEwgSU1QT1JURUQgRklMRVMgTVVTVCBCRSBJTiBUSEUgU0FNRSBESVJFQ1RPUlkgQVMgVEhJUyBTQ1JJUFQKCkxvYWQgdGlkeXZlcnNlIHBhY2thZ2UKYGBge3J9CmxpYnJhcnkoInRpZHl2ZXJzZSIpCmBgYAoKSW1wb3J0IGluZGl2aWR1YWwgdHVtb3VyIGV4b21lIHNvbWF0aWMgdmFyaWFudHMgKCdmJykgYW5kIHR1bW91ci1vbmx5IHZhcmlhbnRzICgnZycpIGZpbGVzCmBgYHtyfQptYXguY2FsbGVycyA9IDMKZiA8LSByZWFkLnRhYmxlKCJ0dW1vdXJfc2FtcGxlX2dlcm1saW5lX2V4b21lX3NvbWF0aWNfY2FsbGVyX2ZpbGUudHN2IiwgaGVhZGVyPVRSVUUsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsIHNlcD0iXHQiLCBjb21tZW50LmNoYXI9IiIsIHF1b3RlPSIiKSAlPiUKICBkcGx5cjo6cmVuYW1lKCJUdW1vdXJfU2FtcGxlIj0iWC5UdW1vdXJfU2FtcGxlIikgJT4lIAogIG11dGF0ZShuLmNhbGxlcnMgPSBzZXROYW1lcyhzYXBwbHkoc3Ryc3BsaXQodW5pcXVlKElkZW50aWZpZWQpLCAiLSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGNhbGxlcnMpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoIkludGVyc2VjdGlvbiIgJWluJSBjYWxsZXJzLCBtYXguY2FsbGVycywgc3VtKCFncmVwbCgiXmZpbHRlciIsIGNhbGxlcnMpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSksIHVuaXF1ZShJZGVudGlmaWVkKSlbSWRlbnRpZmllZF0pCmcgPC0gcmVhZC5kZWxpbSgidHVtb3VyX3NhbXBsZV9nZXJtbGluZV9wYWlyZWRfZXhvbWVzX0hBUC50c3YiLCBoZWFkZXI9VFJVRSwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSwgc2VwPSJcdCIsIGNvbW1lbnQuY2hhcj0iIikgJT4lIAogIGRwbHlyOjpyZW5hbWUoIlR1bW91cl9TYW1wbGUiPSJYLlR1bW91cl9TYW1wbGUiKQpuYW1lcyhmKQpuYW1lcyhnKQpzb3J0KHVuaXF1ZShmJFR1bW91cl9TYW1wbGUpKQpzb3J0KHVuaXF1ZShnJFR1bW91cl9TYW1wbGUpKQpWaVBfbGlzdCA8LSByZWFkLmRlbGltKCJWaVAgU2FtcGxlcyAmIENhbmRpZGF0ZSBHZW5lcy50eHQiLCBoZWFkZXI9VFJVRSwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSwgc2VwPSJcdCIsIGNvbW1lbnQuY2hhcj0iIikgJT4lIAogIGFycmFuZ2UoU2FtcGxlKQp0dW1vdXJfdHlwZSA8LSBzZWxlY3QoVmlQX2xpc3QsU2FtcGxlLFR1bW91ci5UeXBlKSAlPiUgZGlzdGluY3QoKSAlPiUgcmVuYW1lKCJTYW1wbGUiPSJOb3JtYWxfU2FtcGxlIikKZiROb3JtYWxfU2FtcGxlIDwtIGFzLmNoYXJhY3RlcihmJE5vcm1hbF9TYW1wbGUpCmckTm9ybWFsX1NhbXBsZSA8LSBhcy5jaGFyYWN0ZXIoZyROb3JtYWxfU2FtcGxlKQpmIDwtIGxlZnRfam9pbihmLHR1bW91cl90eXBlLGJ5PSJOb3JtYWxfU2FtcGxlIixjb3B5PUZBTFNFKQpnMCA8LSBsZWZ0X2pvaW4oZyx0dW1vdXJfdHlwZSxieT0iTm9ybWFsX1NhbXBsZSIsY29weT1GQUxTRSkgJT4lIGZpbHRlcihDQU5PTklDQUwlaW4lIllFUyIpCmdlbmVfbGlzdCA8LSBzZWxlY3QoVmlQX2xpc3QsU2FtcGxlLFNZTUJPTCkgJT4lIGZpbHRlcihTYW1wbGUlaW4lKGYkTm9ybWFsX1NhbXBsZSkpCmdlbmVzIDwtIGdlbmVfbGlzdCRTWU1CT0wKdHVtb3VyX3B1cml0eSA8LSByZWFkLmRlbGltKCJUdW1vdXIgUHVyaXR5LnR4dCIsIGhlYWRlcj1UUlVFLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFLCBzZXA9Ilx0IiwgY29tbWVudC5jaGFyPSIiKSAlPiUKICBzZWxlY3QoVHVtb3VyX1NhbXBsZSxQdXJpdHksTm9fTXV0YXRpb25zKQpmIDwtIGxlZnRfam9pbihmLHR1bW91cl9wdXJpdHksYnk9IlR1bW91cl9TYW1wbGUiLGNvcHk9RkFMU0UpCmcwIDwtIGxlZnRfam9pbihnMCx0dW1vdXJfcHVyaXR5LGJ5PSJUdW1vdXJfU2FtcGxlIixjb3B5PUZBTFNFKQpgYGAKCkNvbnZlcnQgYWxsIHJlbGV2YW50IHN0cmluZ3MgdG8gaW50ZWdlcnMvZG91YmxlcywgYW5kIHJlbW92ZSB2YXJpYW50cyB0aGF0IGZhaWwgUUMgaW4gb25lIG9yIG1vcmUgc29tYXRpYyBwaXBlbGluZSBjYWxsZXJzIG9yIGFyZSBDb25zZXF1ZW5jZSA3IChJTVBBQ1Q9PU1PRElGSUVSKQpgYGB7cn0KZiRRVUFMIDwtIGFzLm51bWVyaWMoZiRRVUFMKSAlPiUgcmVwbGFjZV9uYSgwKQpmJFRVTU9VUi5QTUNBRCA8LSBhcy5udW1lcmljKGYkVFVNT1VSLlBNQ0FEKSAlPiUgcmVwbGFjZV9uYSgwKQpmJFRVTU9VUi5QTUNEUCA8LSBhcy5udW1lcmljKGYkVFVNT1VSLlBNQ0RQKSAlPiUgcmVwbGFjZV9uYSgwKQpmJFRVTU9VUi5QTUNGUkVRIDwtIGFzLm51bWVyaWMoZiRUVU1PVVIuUE1DRlJFUSkgJT4lIHJlcGxhY2VfbmEoMCkKZiROT1JNQUwuUE1DQUQgPC0gYXMubnVtZXJpYyhmJE5PUk1BTC5QTUNBRCkgJT4lIHJlcGxhY2VfbmEoMCkKZiROT1JNQUwuUE1DRFAgPC0gYXMubnVtZXJpYyhmJE5PUk1BTC5QTUNEUCkgJT4lIHJlcGxhY2VfbmEoMCkKZiROT1JNQUwuUE1DRlJFUSA8LSBhcy5udW1lcmljKGYkTk9STUFMLlBNQ0ZSRVEpICU+JSByZXBsYWNlX25hKDApCmYkR25vbUFEX3YzX0FGIDwtIGFzLm51bWVyaWMoZiRHbm9tQURfdjNfQUYpICU+JSByZXBsYWNlX25hKDApCgpnJFFVQUwgPC0gYXMubnVtZXJpYyhnJFFVQUwpICU+JSByZXBsYWNlX25hKDApCmckVFVNT1VSLlBNQ0FEIDwtIGFzLm51bWVyaWMoZyRUVU1PVVIuUE1DQUQpICU+JSByZXBsYWNlX25hKDApCmckVFVNT1VSLlBNQ0RQIDwtIGFzLm51bWVyaWMoZyRUVU1PVVIuUE1DRFApICU+JSByZXBsYWNlX25hKDApCmckVFVNT1VSLlBNQ0ZSRVEgPC0gYXMubnVtZXJpYyhnJFRVTU9VUi5QTUNGUkVRKSAlPiUgcmVwbGFjZV9uYSgwKQpnJE5PUk1BTC5QTUNBRCA8LSBhcy5udW1lcmljKGckTk9STUFMLlBNQ0FEKSAlPiUgcmVwbGFjZV9uYSgwKQpnJE5PUk1BTC5QTUNEUCA8LSBhcy5udW1lcmljKGckTk9STUFMLlBNQ0RQKSAlPiUgcmVwbGFjZV9uYSgwKQpnJE5PUk1BTC5QTUNGUkVRIDwtIGFzLm51bWVyaWMoZyROT1JNQUwuUE1DRlJFUSkgJT4lIHJlcGxhY2VfbmEoMCkKZyRHbm9tQURfdjNfQUYgPC0gYXMubnVtZXJpYyhnJEdub21BRF92M19BRikgJT4lIHJlcGxhY2VfbmEoMCkKCmYwIDwtIGZpbHRlcihmLCFJZGVudGlmaWVkJWluJWMoIkZpbHRlcmVkSW5BbGwiKSkgJT4lIAogICAgZmlsdGVyKG4uY2FsbGVycz49MikgJT4lCiAgICBmaWx0ZXIoQ29uc2VxdWVuY2VfUmFuazw3KQpgYGAKCkxpc3QgdW5pcXVlLCBzb3J0ZWQgZWxlbWVudHMgaW4gSWRlbnRpZmllZCAoU29tYXRpYyBDYWxsZXIgUUMgcmVzdWx0cyksIENvbnNlcXVlbmNlIFJhbmssIE5PUk1BTC5QTUNBRCAoZ2VybWxpbmUgYWx0IGFsbGVsZSBiYXNlIGRlcHRoKSwgTk9STUFMLlBNQ0FEUCAoZ2VybWxpbmUgdG90YWwgYmFzZSBkZXB0aCkgTk9STUFMLlBNQ0ZSRVEgKGdlcm1saW5lIGFsdCBhbGxlbGUgcmVhZCBmcmVxKSwgR25vbUFEIHYyLjEvMyBBRiBhbmQgUVVBTCBzY29yZQpgYGB7cn0Kc29ydCh1bmlxdWUoZjAkSWRlbnRpZmllZCkpCmBgYApgYGB7cn0Kc29ydCh1bmlxdWUoZjAkQ29uc2VxdWVuY2VfUmFuaykpCmBgYApgYGB7cn0Kc29ydCh1bmlxdWUoZjAkTk9STUFMLlBNQ0FEKSkKYGBgCmBgYHtyfQpzb3J0KHVuaXF1ZShmMCROT1JNQUwuUE1DRFApKQpgYGAKYGBge3J9CnNvcnQodW5pcXVlKGYwJE5PUk1BTC5QTUNGUkVRKSkKYGBgCmBgYHtyfQpzb3J0KHVuaXF1ZShmMCRHbm9tQURfdjIuMV9ub25fY2FuY2VyX0FGKSkKYGBgCmBgYHtyfQpzb3J0KHVuaXF1ZShmMCRHbm9tQURfdjNfQUYpKQpgYGAKYGBge3J9CnNvcnQodW5pcXVlKGYwJFFVQUwpKQpgYGAKClBsb3QgcHJlLWZpbHRlcmluZyBkaXN0cmlidXRpb24gb2YgdHVtb3VyIGFsdCBhbGxlbGUgcmVhZCBudW1iZXIoVFVNT1VSLlBNQ0FEKSwgZGVwdGggKFRVTU9VUi5QTUNEUCkgYW5kIGZyZXF1ZW5jeSAoVFVNT1VSLlBNQ0ZSRVEpCmBgYHtyfQpzb3J0KHVuaXF1ZShmMCRUVU1PVVIuUE1DQUQpKQpoaXN0KGYwJFRVTU9VUi5QTUNBRCwgYnJlYWtzPTIwMDAsIG1haW49IkRpc3RyaWJ1dGlvbiBvZiBUVU1PVVIuUE1DQUQgVmFsdWVzIiwgeGxhYj0iVFVNT1VSLlBNQ0FEIix4bGltPWMoMCwyMCkpCnNvcnQodW5pcXVlKGYwJFRVTU9VUi5QTUNEUCkpCmhpc3QoZjAkVFVNT1VSLlBNQ0RQLCBicmVha3M9NDAwMCwgbWFpbj0iRGlzdHJpYnV0aW9uIG9mIFRVTU9VUi5QTUNEUCBWYWx1ZXMiLCB4bGFiPSJUVU1PVVIuUE1DRFAiLHhsaW09YygwLDEwMCkpCnNvcnQodW5pcXVlKGYwJFRVTU9VUi5QTUNGUkVRKSkKaGlzdChmMCRUVU1PVVIuUE1DRlJFUSwgYnJlYWtzPTIwMCwgbWFpbj0iRGlzdHJpYnV0aW9uIG9mIFRVTU9VUi5QTUNGUkVRIFZhbHVlcyIsIHhsYWI9IlRVTU9VUi5QTUNGUkVRIix4bGltPWMoMCwxKSkKYGBgCgpGaWx0ZXIgb3V0IGFuZCByZW1vdmUgZWxlbWVudHMvcm93cyB3aXRoIGhpZ2ggbnVtYmVyIGFsdCBhbGxlbGUgcmVhZHMgaW4gZ2VybWxpbmUgKE5PUk1BTC5QTUNBRCksIGhpZ2ggZ2VybWxpbmUgYWx0IGFsbGVsZSByZWFkIGZyZXEgKE5PUk1BTC5QTUNGUkVRKSwgbG93IGdlcm1saW5lIGFuZCB0dW1vdXIgcmVhZCBkZXB0aHMgKE5PUk1BTC9UVU1PVVIuUE1DRFApLCBsb3cgbnVtYmVyIGFsdCBhbGxlbGUgcmVhZHMgaW4gdHVtb3VyIChUVU1PVVIuUE1DQUQpLCBsb3cgdHVtb3VyIGFsdCBhbGxlbGUgcmVhZCBmcmVxIChUVU1PVVIuUE1DRlJFUSkgYW5kIGNvbW1vbiB2YXJpYW50cyAodXNpbmcgR25vbUFEIHYyLjEvMyBBRnMpOyBsaXN0IHJlbWFpbmluZyB1bmlxdWUgZWxlbWVudHMgZnJvbSB0aG9zZSBmaWVsZHM7IGFuZCBzYXZlIG91dHB1dCAoZm9yIEVOU0VNQkwgQ0FOT05JQ0FMIHRyYW5zY3JpcHRzIG9ubHkpCmBgYHtyfQpmMTwtZmlsdGVyKGYwLChOT1JNQUwuUE1DQUQgPD0gMikgJiAoTk9STUFMLlBNQ0RQID49IDEwKSAmIChOT1JNQUwuUE1DRlJFUSA8IDAuMDEpKQpmMjwtZmlsdGVyKGYxLFRVTU9VUi5QTUNBRCA+PSA1KQpmMzwtZmlsdGVyKGYyLFRVTU9VUi5QTUNEUD49MjApCmY0PC1maWx0ZXIoZjMsVFVNT1VSLlBNQ0ZSRVEgPj0gKGYzJFB1cml0eSowLjUqKDIvMykpKQpmNTwtZmlsdGVyKGY0LEdub21BRF92Mi4xX25vbl9jYW5jZXJfQUYgPD0gMC4wMDAxKQpmNjwtZmlsdGVyKGY1LEdub21BRF92M19BRiA8PSAwLjAwMDEpCgpzb3J0KHVuaXF1ZShmMSRRVUFMKSkKYGBgCmBgYHtyfQpzb3J0KHVuaXF1ZShmMiRUVU1PVVIuUE1DQUQpKQpzb3J0KHVuaXF1ZShmMiRRVUFMKSkKYGBgCmBgYHtyfQpzb3J0KHVuaXF1ZShmMyRUVU1PVVIuUE1DRFApKQpzb3J0KHVuaXF1ZShmMyRRVUFMKSkKYGBgCmBgYHtyfQpzb3J0KHVuaXF1ZShmNCRUVU1PVVIuUE1DRlJFUSkpCnNvcnQodW5pcXVlKGY0JFFVQUwpKQpgYGAKYGBge3J9CnNvcnQodW5pcXVlKGY1JEdub21BRF92Mi4xX25vbl9jYW5jZXJfQUYpKQpgYGAKYGBge3J9CnNvcnQodW5pcXVlKGY2JEdub21BRF92M19BRikpCmBgYApgYGB7cn0Kc2FtcGxlczwtdW5pcXVlKGYkVHVtb3VyX1NhbXBsZSkKZm9yIChzYW1wbGUgaW4gc2FtcGxlcyl7CiAgZGlyLmNyZWF0ZShzYW1wbGUpCiAgc2V0d2Qoc2FtcGxlKQpmNiAlPiUgCiAgZmlsdGVyKENBTk9OSUNBTCVpbiUiWUVTIikgJT4lIAogIHdyaXRlX2V4Y2VsX2NzdihwYXRoPXBhc3RlKHNhbXBsZXMsInR1bW91cl9nZXJtbGluZV9leG9tZV9zb21hdGljX2NhbGxlcl9maWxlX2ZpbHRlcmVkLmNzdiIsc2VwPSJfIiksIG5hPSIuIixhcHBlbmQ9RkFMU0UsY29sX25hbWVzPVRSVUUpCn0KYGBgCgpQbG90IHBvc3QtZmlsdGVyaW5nIGRpc3RyaWJ1dGlvbiBvZiB0dW1vdXIgYWx0IGFsbGVsZSByZWFkIG51bWJlcihUVU1PVVIuUE1DQUQpLCBkZXB0aCAoVFVNT1VSLlBNQ0RQKSBhbmQgZnJlcXVlbmN5IChUVU1PVVIuUE1DRlJFUSkKYGBge3J9CnNvcnQodW5pcXVlKGY2JFRVTU9VUi5QTUNBRCkpCmhpc3QoZjYkVFVNT1VSLlBNQ0FELCBicmVha3M9MjAwMCwgbWFpbj0iRGlzdHJpYnV0aW9uIG9mIFRVTU9VUi5QTUNBRCBWYWx1ZXMiLCB4bGFiPSJUVU1PVVIuUE1DQUQiLHhsaW09YygwLDIwKSkKc29ydCh1bmlxdWUoZjYkVFVNT1VSLlBNQ0RQKSkKaGlzdChmNiRUVU1PVVIuUE1DRFAsIGJyZWFrcz00MDAwLCBtYWluPSJEaXN0cmlidXRpb24gb2YgVFVNT1VSLlBNQ0RQIFZhbHVlcyIsIHhsYWI9IlRVTU9VUi5QTUNEUCIseGxpbT1jKDAsMTAwKSkKc29ydCh1bmlxdWUoZjYkVFVNT1VSLlBNQ0ZSRVEpKQpoaXN0KGY2JFRVTU9VUi5QTUNGUkVRLCBicmVha3M9MjAwLCBtYWluPSJEaXN0cmlidXRpb24gb2YgVFVNT1VSLlBNQ0ZSRVEgVmFsdWVzIiwgeGxhYj0iVFVNT1VSLlBNQ0ZSRVEiLHhsaW09YygwLDEpKQpgYGAKCkV4dHJhY3Qgcm93cyB3aXRoIHZhcmlhbnRzIGluIGdlbmUocykgb2YgaW50ZXJlc3QgcHJlLSBhbmQgcG9zdC1maWx0ZXJpbmcgKGUuZy4gVFA1MykgZnJvbSBzb21hdGljIHZhcmlhbnRzIGZpbGUsIGFuZCBzYXZlIG91dHB1dApgYGB7cn0KSEdTX3R1bW91cl9nZW5lcyA8LSBjKCJUUDUzIiwiQlJDQTEiLCJCUkNBMiIsIlBURU4iLCJORjEiLCJSQjEiLCJDREsxMiIsIkNES04yQSIpCkVORF90dW1vdXJfZ2VuZXMgPC0gYygiQVJJRDFBIiwiUElLM0NBIiwiUFRFTiIsIkNUTk5CMSIsIktSQVMiLCJCUkFGIiwiVFA1MyIsIk1FVCIpCnNldHdkKHNhbXBsZSkKZjBfZ2VuZXMgPC0gaWYgKGYwJFR1bW91ci5UeXBlJWluJSJIaWdoLWdyYWRlIGVuZG9tZXRyaW9pZCIpCnsKICBmaWx0ZXIoZjAsKFNZTUJPTCVpbiVFTkRfdHVtb3VyX2dlbmVzKXwKICAgICAgICAgICAgICAgICAgICAgKFNZTUJPTCVpbiVjKGdlbmVzKSkpICU+JSAKICB3cml0ZV9leGNlbF9jc3YocGF0aD1wYXN0ZShzYW1wbGVzLCJ0dW1vdXJfZ2VybWxpbmVfZXhvbWVfc29tYXRpY19jYWxsZXJfZ2VuZXNfdW5maWx0ZXJlZC5jc3YiLHNlcD0iXyIpLCBuYT0iLiIsYXBwZW5kPUZBTFNFLGNvbF9uYW1lcz1UUlVFKQp9ZWxzZXsKICBmaWx0ZXIoZjAsKFNZTUJPTCVpbiVIR1NfdHVtb3VyX2dlbmVzKXwKICAgICAgICAgICAgICAgICAgICAgKFNZTUJPTCVpbiVjKGdlbmVzKSkpICU+JSAKICB3cml0ZV9leGNlbF9jc3YocGF0aD1wYXN0ZShzYW1wbGVzLCJ0dW1vdXJfZ2VybWxpbmVfZXhvbWVfc29tYXRpY19jYWxsZXJfZ2VuZXNfdW5maWx0ZXJlZC5jc3YiLHNlcD0iXyIpLCBuYT0iLiIsYXBwZW5kPUZBTFNFLGNvbF9uYW1lcz1UUlVFKQp9CmY2X2dlbmVzIDwtIGlmIChmNiRUdW1vdXIuVHlwZSVpbiUiSGlnaC1ncmFkZSBlbmRvbWV0cmlvaWQiKQp7CiAgZmlsdGVyKGY2LChTWU1CT0wlaW4lRU5EX3R1bW91cl9nZW5lcyl8CiAgICAgICAgICAgICAgICAgICAgIChTWU1CT0wlaW4lYyhnZW5lcykpKSAlPiUgCiAgd3JpdGVfZXhjZWxfY3N2KHBhdGg9cGFzdGUoc2FtcGxlcywidHVtb3VyX2dlcm1saW5lX2V4b21lX3NvbWF0aWNfY2FsbGVyX2dlbmVzX2ZpbHRlcmVkLmNzdiIsc2VwPSJfIiksIG5hPSIuIixhcHBlbmQ9RkFMU0UsY29sX25hbWVzPVRSVUUpCn1lbHNlewogIGZpbHRlcihmNiwoU1lNQk9MJWluJUhHU190dW1vdXJfZ2VuZXMpfAogICAgICAgICAgICAgICAgICAgICAoU1lNQk9MJWluJWMoZ2VuZXMpKSkgJT4lIAogIHdyaXRlX2V4Y2VsX2NzdihzYW1wbGUscGF0aD1wYXN0ZShzYW1wbGVzLCJ0dW1vdXJfZ2VybWxpbmVfZXhvbWVfc29tYXRpY19jYWxsZXJfZ2VuZXNfZmlsdGVyZWQuY3N2IixzZXA9Il8iKSwgbmE9Ii4iLGFwcGVuZD1GQUxTRSxjb2xfbmFtZXM9VFJVRSkKfQpgYGAKCkV4dHJhY3QgZXF1aXZhbGVudCByb3dzIHdpdGggdmFyaWFudHMgaW4gZ2VuZShzKSBvZiBpbnRlcmVzdCAob25lIGdlbmUgdHJhbnNjcmlwdCBwZXIgdmFyaWFudCkgZnJvbSB0dW1vdXItb25seSB2YXJpYW50cyBmaWxlLCBhbmQgc2F2ZSBvdXRwdXQKYGBge3J9CnNldHdkKHNhbXBsZSkKZ19nZW5lcyA8LQogIGlmIChnMCRUdW1vdXIuVHlwZSVpbiUiSGlnaC1ncmFkZSBlbmRvbWV0cmlvaWQiKQp7CiAgZmlsdGVyKGcwLChTWU1CT0wlaW4lRU5EX3R1bW91cl9nZW5lcyl8CiAgICAgICAgICAgICAgICAgICAgIChTWU1CT0wlaW4lYyhnZW5lcykpKQp9ZWxzZXsKICBmaWx0ZXIoZzAsKFNZTUJPTCVpbiVIR1NfdHVtb3VyX2dlbmVzKXwKICAgICAgICAgICAgICAgICAgICAgKFNZTUJPTCVpbiVjKGdlbmVzKSkpCn0gJT4lIAogIGFycmFuZ2UoVHJhbnNjcmlwdF9JbmRleCkgJT4lIAogIGRpc3RpbmN0KENIUk9NLFBPUyxSRUYsQUxULC5rZWVwX2FsbD1UUlVFKQogIHdyaXRlX2V4Y2VsX2NzdihnX2dlbmVzLHBhdGg9cGFzdGUoc2FtcGxlLCJ0dW1vdXJfZ2VybWxpbmVfcGFpcmVkX2V4b21lc19IQVBfZ2VuZXMuY3N2IixzZXA9Il8iKSwgbmE9Ii4iLGFwcGVuZD1GQUxTRSxjb2xfbmFtZXM9VFJVRSkKYGBgCg==