02_Genome Annotation


Using the published genome sequences for SY14 and BY4742, genomic feature annotation was performed using LRSDAY. This allowed to map genes, mobile elements, tRNA, X and Y’ elements. The gff file resulting from the LRSDAY pipeline was reformated in R using the following procedure.

suppressMessages(library(GenomicRanges))
suppressMessages(library(GenomicFeatures))
suppressMessages(library(tidyverse))
suppressMessages(library(rtracklayer))
`%+%`<-paste0
seqinfSY <- readRDS("Data/seqinfSY.rds")
seqinfBY <- readRDS("Data/seqinfBY.rds")

The GFF file from S288C downloaded from SGD was used to import the gene names to add this information to the SY GFF. The S288C GFF was manually curated for small error that prevent its importation in R

S288Cgff <- import.gff("Data_raw/saccharomyces_cerevisiae_R64-3-1_20210421_mod.gff")
gene_name <- as_tibble(S288Cgff) %>% filter(type=="gene") %>% select(gene,Name) %>% unique

Gene names were then added to the SY GFF file.

### add gene names
SYgff2 <- SYgff %>% 
    mutate(asYname=map_lgl(Name,function(z) substr(z,1,1)=="Y" & substr(z,2,2)!="_" )) %>%
    mutate(name_split=map2(Name,asYname, function(x,y) if (y) {strsplit(x,"/")[[1]]})) %>%
#   mutate(mapchr=map2(name_split,asYname, function(x,y) {
#       if (y)
#           {
#           sapply(x, function(z) {
#               ch <- grep(substr(z,2,2),LETTERS) %>% as.roman
#               paste0("chr",ch)
#               })
#           }
#   })) %>%
#   mutate(mapstr=map2(name_split,asYname, function(x,y) {
#       if (y)
#           {
#           sapply(x, function(z) {
#               ifelse(substr(z,7,7)=="W","+","-")
#               })
#           }
#   })) %>%
#   mutate(genename=pmap_chr(., function(chrom,strand,asYname,mapchr,mapstr,Name,...){
#       if (asYname)
#           {
#           newname <- mapchr[mapchr==chrom & mapstr==strand] %>% names(.)
#           }else{
#           newname <- Name 
#           }
#       if (length(newname)==0 & asYname) {newname=NA}
#       if (length(newname)==1 & asYname) {
#           newstr <- case_when(substr(newname,7,7)=="W"~"+",substr(newname,7,7)=="C"~"-",T~"ambiguous")
#           newchr <- ifelse(!is.na(newname),paste0("chr",as.roman(grep(substr(newname,2,2),LETTERS))),"ambiguous")
#           if (newstr!=strand | newchr!=chrom) {newname=NA}
#           }
#       if (length(newname)!=1 & asYname) {newname=paste0(Name,"_ambiguous")}
#       return(newname)
#       }))
mutate(genename=pmap_chr(., function(name_split,asYname,Name,...){
    len <- length(name_split)
    #if (length(name_split)==0 & asYname) {newname=NA}
    if (len==1) {newname=Name}
#   if (length(name_split)>1 & asYname) {newname=paste0(Name,"_ambiguous")}
#   newname=length(name_split)
    return(newname)
    }))
Error in `mutate()`:
ℹ In argument: `genename = pmap_chr(...)`.
Caused by error in `pmap_chr()`:
ℹ In index: 1.
Caused by error in `.f()`:
! object 'newname' not found
Run `]8;;x-r-run:rlang::last_trace()rlang::last_trace()]8;;` to see where the error occurred.

The table was then split by features type

gene <- SYgff3 %>% filter(type=="gene")
trna <- SYgff2 %>% filter(type=="tRNA")
tel_elmnt <- SYgff2 %>% filter(type %in% c("X_element","Y_prime_element"))
Transposon <- SYgff2 %>% filter(type %in% c("mobile_element"))

Ribosomal RNA gene were mapped manually by positioning the neighbouring gene on chrXII.

rDNA_SY <- GRanges("CP029160.1",IRanges(3879940,3934000),"*",seqinfSY) %>% as_tibble %>% mutate(type="rRNA") %>% mutate(name="rDNA locus")

Telomeric repeat were map using Telofinder.

tel <- read_tsv("Data_raw/SY.tel.bed",col_names=c("seqnames","start","end","name"),show_col_types = FALSE) %>% mutate(type="Tel_repeat")

Please note that the bed file from Telofinder appears to be 1-based and not 0-based as expected for bed files.

Centromere were mapped also with LRSDAY but only for BY4742 and CEN15 for SY14 as all of the other were deleted in the SY14 strain. The positions of the deleted centromeres in SY14 were manually mapped by inspecting the position of the sequences the closest to the former centromere in SY14 genome. Thus, all CEN positions, with the exception of CEN15 in the SY14 annotations are not the positions of the centromeres but rather the position of the deletion point. Features were then exported in GFF3 file format.

cenSY <- import("Data_raw/CEN_SY_LL.bed") %>% as_tibble %>% mutate(type="centromere") %>% mutate(Name=name)
export(cenSY, con="Genome_annotations/SY14_CEN.gff3", format = "GFF3")
export(gene, con="Genome_annotations/SY14_gene.gff3", format = "GFF3")
export(rDNA_SY, con="Genome_annotations/SY14_rRNA.gff3", format = "GFF3")
export(trna, con="Genome_annotations/SY14_tRNA.gff3", format = "GFF3")
export(tel, con="Genome_annotations/SY14_TEL.gff3", format = "GFF3")
export(tel_elmnt, con="Genome_annotations/SY14_TEL_ELMNT.gff3", format = "GFF3")
export(Transposon, con="Genome_annotations/SY14_TE_LTR.gff3", format = "GFF3")
SYgff4 <- c(
    import("Genome_annotations/SY14_TEL.gff3"),
    import("Genome_annotations/SY14_gene.gff3"),
    import("Genome_annotations/SY14_CEN.gff3"),
    import("Genome_annotations/SY14_rRNA.gff3"),
    import("Genome_annotations/SY14_tRNA.gff3"),
    import("Genome_annotations/SY14_TEL_ELMNT.gff3"),
    import("Genome_annotations/SY14_TE_LTR.gff3")
    )
export(SYgff4,con="Genome_annotations/SY14.gff3", format = "GFF3")

BY4742 annotation were processed similarly.

BYgff <- readGFF("Data_raw/BY.tidy.gff3") %>% 
    as_tibble()%>% 
    select(chrom=seqid,type,start,end,strand,ID,Name) %>%
    mutate(strand=case_when(strand=="0"~"*",T~strand)) %>%
    filter(chrom!="chrM")
BYgff2 <- BYgff %>% 
    mutate(asYname=map_lgl(Name,function(z) substr(z,1,1)=="Y" & substr(z,2,2)!="_" )) %>%
    mutate(name_split=map2(Name,asYname, function(x,y) if (y) {strsplit(x,"/")[[1]]})) %>%
    # mutate(mapchr=map2(name_split,asYname, function(x,y) {
    #   if (y)
    #       {
    #       sapply(x, function(z) {
    #           ch <- grep(substr(z,2,2),LETTERS) %>% as.roman
    #           paste0("chr",ch)
    #           })
    #       }
    # })) %>%
    # mutate(mapstr=map2(name_split,asYname, function(x,y) {
    #   if (y)
    #       {
    #       sapply(x, function(z) {
    #           ifelse(substr(z,7,7)=="W","+","-")
    #           })
    #       }
    # })) %>%
    # mutate(genename=pmap_chr(., function(chrom,strand,asYname,mapchr,mapstr,Name,...){
    #   if (asYname)
    #       {
    #       newname <- mapchr[mapchr==chrom & mapstr==strand] %>% names(.)
    #       }else{
    #       newname <- Name 
    #       }
    #   if (length(newname)==0 & asYname) {newname=NA}
    #   if (length(newname)==1 & asYname) {
    #       newstr <- case_when(substr(newname,7,7)=="W"~"+",substr(newname,7,7)=="C"~"-",T~"ambiguous")
    #       newchr <- ifelse(!is.na(newname),paste0("chr",as.roman(grep(substr(newname,2,2),LETTERS))),"ambiguous")
    #       if (newstr!=strand | newchr!=chrom) {newname=NA}
    #       }
    #   if (length(newname)!=1 & asYname) {newname=paste0(Name,"_ambiguous")}
    #   return(newname)
    #   }))
    mutate(genename=pmap_chr(., function(name_split,asYname,Name,...){
    newname <- NA
    if (length(name_split)==0 & asYname) {newname=NA}
    if (length(name_split)==1 & asYname) {newname=Name}
    if (length(name_split)>1 & asYname) {newname=paste0(Name,"_ambiguous")}
    return(newname)
    }))
BYgff3 <- left_join(BYgff2,gene_name,by=join_by(genename==Name)) %>% mutate(gene=map2_chr(gene,genename, function(x,y) ifelse(is.na(x),y,x)))%>% filter(!is.na(gene))
## looks better


gene <- BYgff3 %>% filter(type=="gene")
trna <- BYgff2 %>% filter(type=="tRNA")
tel <- read_tsv("Data_raw/BY.tel.bed",col_names=c("seqnames","start","end","name"),show_col_types = FALSE) %>% mutate(type="Tel_repeat")
tel_elmnt <- BYgff2 %>% filter(type %in% c("X_element","Y_prime_element"))
Transposon <- BYgff2 %>% filter(type %in% c("mobile_element"))
cen <- BYgff2 %>% filter(type=="centromere")
rDNA_BY <- GRanges(seqnames="CP026300.1",ranges=IRanges(441284,495332),strand="*",seqinfo=seqinfBY) %>% as_tibble %>% mutate(type="rRNA") %>% mutate(name="rDNA locus")
# based on the coverage of reads
export(gene, con="Genome_annotations/BY4742_gene.gff3", format = "GFF3")
export(cen, con="Genome_annotations/BY4742_CEN.gff3", format = "GFF3")
export(rDNA_BY, con="Genome_annotations/BY4742_rRNA.gff3", format = "GFF3")
export(trna, con="Genome_annotations/BY4742_tRNA.gff3", format = "GFF3")
export(tel, con="Genome_annotations/BY4742_TEL.gff3", format = "GFF3")
export(tel_elmnt, con="Genome_annotations/BY4742_TEL_ELMNT.gff3", format = "GFF3")
export(Transposon, con="Genome_annotations/BY4742_TE_LTR.gff3", format = "GFF3")

BYgff4 <- c(
    import("Genome_annotations/BY4742_TEL.gff3"),
    import("Genome_annotations/BY4742_gene.gff3"),
    import("Genome_annotations/BY4742_CEN.gff3"),
    import("Genome_annotations/BY4742_rRNA.gff3"),
    import("Genome_annotations/BY4742_tRNA.gff3"),
    import("Genome_annotations/BY4742_TEL_ELMNT.gff3"),
    import("Genome_annotations/BY4742_TE_LTR.gff3")
    )
export(BYgff4,con="Genome_annotations/BY4742.gff3", format = "GFF3")

In order to map known ARS on the BY and SY genome, ARS position were download from OriDB (20231204). A unique naming scheme was designed for the Likely (or respectively Dubious) ARS by adding the chromosome name and the order of the likely (or respectively dubious) ARS in this chromosome. There were two ARS names ARS302 which were then names ARS302_1 and ARS302_2. The DNA sequence corresponding to these ARS was then extracted from the reference genome used in OriDB (sacCer1). The resulting fasta file (AllARS_sacCer1_20231218.fa) was then mapped either in the BY4742 or the SY14 genome using bwa mem and the bam file was then converted to bed using bedtools bamtobed.
Warning ARS1311.7 is mapped on chrXIV on OriDB despite the name.

### 1- Extract Fasta from Genome
suppressMessages(library("BSgenome.Scerevisiae.UCSC.sacCer1"))
suppressMessages(library(rtracklayer))
suppressMessages(library(tidyverse))
`%+%`<-paste0

genome_sacCer1 <- BSgenome.Scerevisiae.UCSC.sacCer1

ARS_oriDB <- read_tsv("Data_raw/ARSfromOriDB20231204.txt",show_col_types = FALSE) %>%
    mutate(chrRom="chr" %+% as.roman(chr))%>% 
    mutate(chr="chr" %+% chr)
### create a naming for ARS oriDB
ARS_oriDB2 <- ARS_oriDB %>%
    mutate(newname=pmap_chr(., function(name,chrRom,status,...) {
        if (status %in% c("Likely","Dubious"))
            {res=paste(status,chrRom,sep="_")}
            else
            {res=name}
            return(res)
        }))%>%
    group_by(newname) %>%
    mutate(newname2=ifelse((!status %in% c("Likely","Dubious") & name!="ARS302"),name,paste(newname,1:n(),sep="_"))) %>% 
    ungroup %>%
    mutate(name=ifelse(is.na(name),"putative_ARS",name))
ARS_oriDB2_GR <- makeGRangesFromDataFrame(ARS_oriDB2,starts.in.df.are.0based=T)

ARS_oriDB2_seq <- getSeq(genome_sacCer1,ARS_oriDB2_GR)
names(ARS_oriDB2_seq) <- ARS_oriDB2$newname2
writeXStringSet(ARS_oriDB2_seq, "Data/AllARS_sacCer1_20250312.fa")

These sequence from the fasta file were then mapped on SY and BY genome using bwa (Version: 0.7.17-r1198-dirty), samtools (Version: 1.17) and bedtools (Version: 2.26.0-0)

I need to add the test for ARS mapped on the wrong chromosome.
ARS1311.7 is on chrXIV in oriDB and ARS131a and 131n generate NA

ARS_BY <- import("Data/AllARS_sacCer1_20250312BY4742.bed")
toto <- as_tibble(ARS_BY) %>%
     mutate(chromROM=factor(seqnames) %>%
    fct_recode("chrI"="CP026301.1","chrII"="CP026296.1","chrIII"="CP026297.1","chrIV"="CP026298.1","chrV"="CP026299.1","chrVI"="CP026302.1","chrVII"="CP026294.1","chrVIII"="CP026287.1","chrIX"="CP026295.1","chrX"="CP026288.1","chrXI"="CP026289.1","chrXII"="CP026300.1","chrXIII"="CP026291.1","chrXIV"="CP026293.1","chrXV"="CP026303.1","chrXVI"="CP026290.1")) %>%
    mutate(chrARS=map_chr(name, function(x) {
        y <- strsplit(x,"_")[[1]]
        if (y[1] %in% c("Likely","Dubious")) 
            {res <- y[2]}
        else
            {res1 <- y[1] %>%
                str_remove("ARS") %>%
                as.numeric()
            res <- paste0("chr",floor(res1/100) %>% as.roman)
            }
        return(res)
    }))
## ARS131n and 131a generate NA --> add an exception for those

ARS_OriDB_cleaned <- toto %>% filter(chromROM==chrARS | name %in% c("ARS131a","ARS131n","ARS1311.7"))

Then I add the othernames from OriDB using a previous mapping by Jade Pellet

ARS_BY_JPGR <- import("Data_raw/ARS_BYmappedJP_20231214.bed")
ARS_BY_cleaned <- left_join(ARS_OriDB_cleaned %>% select(-c(chrARS,score)),as_tibble(ARS_BY_JPGR) %>% rename(other_name=name) %>% select(-c(strand,score)),by = join_by(seqnames, start, end, width))
saveRDS(ARS_BY_cleaned,file="Data/ARS_BY_cleaned.rds")
export(makeGRangesFromDataFrame(ARS_BY_cleaned,keep.extra.columns=T),con="Genome_annotations/ARS_BY_cleaned.bed")

Then I need to transpose them to SY

BY2SY <- read_tsv("BigFiles/Data/BY2SYcon.tsv.gz",show_col_types = FALSE)
arsBY <- ARS_BY_cleaned
arsBY$fid <- 1:nrow(arsBY)
arsBY2 <- arsBY %>% mutate(BYpos=map2(start,end,function(x,y) x:y)) %>% select(chromBY=seqnames,start,end,BYpos,fid,name)
res <- inner_join(unnest(arsBY2,cols=c(BYpos)),BY2SY,by = c("chromBY", "BYpos")) %>%
    group_by(fid,chromBY) %>%
    nest() %>%
    ungroup %>%
    mutate(startn=map_int(data,function(x) dplyr::slice(x,1) %>% pull(BYpos))) %>%
    mutate(endn=map_dbl(data,function(x) dplyr::slice(x,nrow(x)) %>% pull(BYpos))) %>%
    mutate(startnSY=map_int(data,function(x) dplyr::slice(x,1) %>% pull(SYpos))) %>%
    mutate(endnSY=map_dbl(data,function(x) dplyr::slice(x,nrow(x)) %>% pull(SYpos))) %>%
    select(-data)
arsBYSY <- inner_join(arsBY,res,by = join_by(fid)) %>% mutate(chromSY="CP029160.1") %>% arrange(startnSY)
saveRDS(arsBYSY,file="Data/ARS_BYonSY.rds")
ARS_BYSY_GR <- with(arsBYSY,GRanges(seqnames=chromSY,ranges=IRanges(start=startnSY,end=endnSY),seqinfo=seqinfSY,name=name)) %>% sort
export(ARS_BYSY_GR,con="Genome_annotations/ARS_BYonSY.bed")
LS0tCnRpdGxlOiAiQllTWSBwcm9qZWN0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tICAKIyAwMl9HZW5vbWUgQW5ub3RhdGlvbgoKKioqICAKClVzaW5nIHRoZSBwdWJsaXNoZWQgZ2Vub21lIHNlcXVlbmNlcyBmb3IgU1kxNCBhbmQgQlk0NzQyLCBnZW5vbWljIGZlYXR1cmUgIGFubm90YXRpb24gd2FzIHBlcmZvcm1lZCB1c2luZyBMUlNEQVkuIFRoaXMgYWxsb3dlZCB0byBtYXAgZ2VuZXMsIG1vYmlsZSBlbGVtZW50cywgdFJOQSwgWCBhbmQgWeKAmSBlbGVtZW50cy4gVGhlIGdmZiBmaWxlIHJlc3VsdGluZyBmcm9tIHRoZSBMUlNEQVkgcGlwZWxpbmUgd2FzIHJlZm9ybWF0ZWQgaW4gUiB1c2luZyB0aGUgZm9sbG93aW5nIHByb2NlZHVyZS4gIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoR2Vub21pY1JhbmdlcykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShHZW5vbWljRmVhdHVyZXMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkodGlkeXZlcnNlKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHJ0cmFja2xheWVyKSkKYCUrJWA8LXBhc3RlMApzZXFpbmZTWSA8LSByZWFkUkRTKCJEYXRhL3NlcWluZlNZLnJkcyIpCnNlcWluZkJZIDwtIHJlYWRSRFMoIkRhdGEvc2VxaW5mQlkucmRzIikKYGBgCgpUaGUgR0ZGIGZpbGUgZnJvbSBTMjg4QyBkb3dubG9hZGVkIGZyb20gW1NHRF0oaHR0cDovL3NnZC1hcmNoaXZlLnllYXN0Z2Vub21lLm9yZy9zZXF1ZW5jZS9TMjg4Q19yZWZlcmVuY2UvZ2Vub21lX3JlbGVhc2VzL1MyODhDX3JlZmVyZW5jZV9nZW5vbWVfUjY0LTMtMV8yMDIxMDQyMS50Z3opIHdhcyB1c2VkIHRvIGltcG9ydCB0aGUgZ2VuZSBuYW1lcyB0byBhZGQgdGhpcyBpbmZvcm1hdGlvbiB0byB0aGUgU1kgR0ZGLiBUaGUgUzI4OEMgR0ZGIHdhcyBtYW51YWxseSBjdXJhdGVkIGZvciBzbWFsbCBlcnJvciB0aGF0IHByZXZlbnQgaXRzIGltcG9ydGF0aW9uIGluIFIgIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KUzI4OENnZmYgPC0gaW1wb3J0LmdmZigiRGF0YV9yYXcvc2FjY2hhcm9teWNlc19jZXJldmlzaWFlX1I2NC0zLTFfMjAyMTA0MjFfbW9kLmdmZiIpCmdlbmVfbmFtZSA8LSBhc190aWJibGUoUzI4OENnZmYpICU+JSBmaWx0ZXIodHlwZT09ImdlbmUiKSAlPiUgc2VsZWN0KGdlbmUsTmFtZSkgJT4lIHVuaXF1ZQpgYGAKR2VuZSBuYW1lcyB3ZXJlIHRoZW4gYWRkZWQgdG8gdGhlIFNZIEdGRiBmaWxlLiAgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9ClNZZ2ZmIDwtIHJlYWRHRkYoIkRhdGFfcmF3L1NZLnRpZHkuZ2ZmMyIpICU+JSAKCWFzX3RpYmJsZSgpJT4lIAoJc2VsZWN0KGNocm9tPXNlcWlkLHR5cGUsc3RhcnQsZW5kLHN0cmFuZCxJRCxOYW1lKSAlPiUKCW11dGF0ZShzdHJhbmQ9Y2FzZV93aGVuKHN0cmFuZD09IjAifiIqIixUfnN0cmFuZCkpICU+JQoJZmlsdGVyKGNocm9tIT0iY2hyTSIpCiMjIyBhZGQgZ2VuZSBuYW1lcwpTWWdmZjIgPC0gU1lnZmYgJT4lIAoJbXV0YXRlKGFzWW5hbWU9bWFwX2xnbChOYW1lLGZ1bmN0aW9uKHopIHN1YnN0cih6LDEsMSk9PSJZIiAmIHN1YnN0cih6LDIsMikhPSJfIiApKSAlPiUKCW11dGF0ZShuYW1lX3NwbGl0PW1hcDIoTmFtZSxhc1luYW1lLCBmdW5jdGlvbih4LHkpIGlmICh5KSB7c3Ryc3BsaXQoeCwiLyIpW1sxXV19KSkgJT4lCiMJbXV0YXRlKG1hcGNocj1tYXAyKG5hbWVfc3BsaXQsYXNZbmFtZSwgZnVuY3Rpb24oeCx5KSB7CiMJCWlmICh5KQojCQkJewojCQkJc2FwcGx5KHgsIGZ1bmN0aW9uKHopIHsKIwkJCQljaCA8LSBncmVwKHN1YnN0cih6LDIsMiksTEVUVEVSUykgJT4lIGFzLnJvbWFuCiMJCQkJcGFzdGUwKCJjaHIiLGNoKQojCQkJCX0pCiMJCQl9CiMJfSkpICU+JQojCW11dGF0ZShtYXBzdHI9bWFwMihuYW1lX3NwbGl0LGFzWW5hbWUsIGZ1bmN0aW9uKHgseSkgewojCQlpZiAoeSkKIwkJCXsKIwkJCXNhcHBseSh4LCBmdW5jdGlvbih6KSB7CiMJCQkJaWZlbHNlKHN1YnN0cih6LDcsNyk9PSJXIiwiKyIsIi0iKQojCQkJCX0pCiMJCQl9CiMJfSkpICU+JQojCW11dGF0ZShnZW5lbmFtZT1wbWFwX2NociguLCBmdW5jdGlvbihjaHJvbSxzdHJhbmQsYXNZbmFtZSxtYXBjaHIsbWFwc3RyLE5hbWUsLi4uKXsKIwkJaWYgKGFzWW5hbWUpCiMJCQl7CiMJCQluZXduYW1lIDwtIG1hcGNoclttYXBjaHI9PWNocm9tICYgbWFwc3RyPT1zdHJhbmRdICU+JSBuYW1lcyguKQojCQkJfWVsc2V7CiMJCQluZXduYW1lIDwtIE5hbWUJCiMJCQl9CiMJCWlmIChsZW5ndGgobmV3bmFtZSk9PTAgJiBhc1luYW1lKSB7bmV3bmFtZT1OQX0KIwkJaWYgKGxlbmd0aChuZXduYW1lKT09MSAmIGFzWW5hbWUpIHsKIwkJCW5ld3N0ciA8LSBjYXNlX3doZW4oc3Vic3RyKG5ld25hbWUsNyw3KT09IlcifiIrIixzdWJzdHIobmV3bmFtZSw3LDcpPT0iQyJ+Ii0iLFR+ImFtYmlndW91cyIpCiMJCQluZXdjaHIgPC0gaWZlbHNlKCFpcy5uYShuZXduYW1lKSxwYXN0ZTAoImNociIsYXMucm9tYW4oZ3JlcChzdWJzdHIobmV3bmFtZSwyLDIpLExFVFRFUlMpKSksImFtYmlndW91cyIpCiMJCQlpZiAobmV3c3RyIT1zdHJhbmQgfCBuZXdjaHIhPWNocm9tKSB7bmV3bmFtZT1OQX0KIwkJCX0KIwkJaWYgKGxlbmd0aChuZXduYW1lKSE9MSAmIGFzWW5hbWUpIHtuZXduYW1lPXBhc3RlMChOYW1lLCJfYW1iaWd1b3VzIil9CiMJCXJldHVybihuZXduYW1lKQojCQl9KSkKbXV0YXRlKGdlbmVuYW1lPXBtYXBfY2hyKC4sIGZ1bmN0aW9uKG5hbWVfc3BsaXQsYXNZbmFtZSxOYW1lLC4uLil7CgluZXduYW1lIDwtIE5BCglpZiAobGVuZ3RoKG5hbWVfc3BsaXQpPT0wICYgYXNZbmFtZSkge25ld25hbWU9TkF9CglpZiAobGVuZ3RoKG5hbWVfc3BsaXQpPT0xICYgYXNZbmFtZSkge25ld25hbWU9TmFtZX0KCWlmIChsZW5ndGgobmFtZV9zcGxpdCk+MSAmIGFzWW5hbWUpIHtuZXduYW1lPXBhc3RlMChOYW1lLCJfYW1iaWd1b3VzIil9CglyZXR1cm4obmV3bmFtZSkKCX0pKQpTWWdmZjMgPC0gbGVmdF9qb2luKFNZZ2ZmMixnZW5lX25hbWUsYnk9am9pbl9ieShnZW5lbmFtZT09TmFtZSkpICU+JSBtdXRhdGUoZ2VuZT1tYXAyX2NocihnZW5lLGdlbmVuYW1lLCBmdW5jdGlvbih4LHkpIGlmZWxzZShpcy5uYSh4KSx5LHgpKSklPiUgZmlsdGVyKCFpcy5uYShnZW5lKSkKIyMgbG9va3MgYmV0dGVyCgpgYGAKVGhlIHRhYmxlIHdhcyB0aGVuIHNwbGl0IGJ5IGZlYXR1cmVzIHR5cGUKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2VuZSA8LSBTWWdmZjMgJT4lIGZpbHRlcih0eXBlPT0iZ2VuZSIpCnRybmEgPC0gU1lnZmYyICU+JSBmaWx0ZXIodHlwZT09InRSTkEiKQp0ZWxfZWxtbnQgPC0gU1lnZmYyICU+JSBmaWx0ZXIodHlwZSAlaW4lIGMoIlhfZWxlbWVudCIsIllfcHJpbWVfZWxlbWVudCIpKQpUcmFuc3Bvc29uIDwtIFNZZ2ZmMiAlPiUgZmlsdGVyKHR5cGUgJWluJSBjKCJtb2JpbGVfZWxlbWVudCIpKQpgYGAKClJpYm9zb21hbCBSTkEgZ2VuZSB3ZXJlIG1hcHBlZCBtYW51YWxseSBieSBwb3NpdGlvbmluZyB0aGUgbmVpZ2hib3VyaW5nIGdlbmUgb24gY2hyWElJLiAgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpyRE5BX1NZIDwtIEdSYW5nZXMoIkNQMDI5MTYwLjEiLElSYW5nZXMoMzg3OTk0MCwzOTM0MDAwKSwiKiIsc2VxaW5mU1kpICU+JSBhc190aWJibGUgJT4lIG11dGF0ZSh0eXBlPSJyUk5BIikgJT4lIG11dGF0ZShuYW1lPSJyRE5BIGxvY3VzIikKYGBgCgpUZWxvbWVyaWMgcmVwZWF0IHdlcmUgbWFwIHVzaW5nIFRlbG9maW5kZXIuICAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KdGVsIDwtIHJlYWRfdHN2KCJEYXRhX3Jhdy9TWS50ZWwuYmVkIixjb2xfbmFtZXM9Yygic2VxbmFtZXMiLCJzdGFydCIsImVuZCIsIm5hbWUiKSxzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUgbXV0YXRlKHR5cGU9IlRlbF9yZXBlYXQiKQpgYGAKUGxlYXNlIG5vdGUgdGhhdCB0aGUgYmVkIGZpbGUgZnJvbSBUZWxvZmluZGVyIGFwcGVhcnMgdG8gYmUgMS1iYXNlZCBhbmQgbm90IDAtYmFzZWQgYXMgZXhwZWN0ZWQgZm9yIGJlZCBmaWxlcy4gIAoKQ2VudHJvbWVyZSB3ZXJlIG1hcHBlZCBhbHNvIHdpdGggTFJTREFZIGJ1dCBvbmx5IGZvciBCWTQ3NDIgYW5kIENFTjE1IGZvciBTWTE0IGFzIGFsbCBvZiB0aGUgb3RoZXIgd2VyZSBkZWxldGVkIGluIHRoZSBTWTE0IHN0cmFpbi4gVGhlIHBvc2l0aW9ucyBvZiB0aGUgZGVsZXRlZCBjZW50cm9tZXJlcyBpbiBTWTE0IHdlcmUgbWFudWFsbHkgbWFwcGVkIGJ5IGluc3BlY3RpbmcgdGhlIHBvc2l0aW9uIG9mIHRoZSBzZXF1ZW5jZXMgdGhlIGNsb3Nlc3QgdG8gdGhlIGZvcm1lciBjZW50cm9tZXJlIGluIFNZMTQgZ2Vub21lLiBUaHVzLCBhbGwgQ0VOIHBvc2l0aW9ucywgd2l0aCB0aGUgZXhjZXB0aW9uIG9mIENFTjE1IGluIHRoZSBTWTE0IGFubm90YXRpb25zIGFyZSBub3QgdGhlIHBvc2l0aW9ucyBvZiB0aGUgY2VudHJvbWVyZXMgYnV0IHJhdGhlciB0aGUgcG9zaXRpb24gb2YgdGhlIGRlbGV0aW9uIHBvaW50LiAKRmVhdHVyZXMgd2VyZSB0aGVuIGV4cG9ydGVkIGluIEdGRjMgZmlsZSBmb3JtYXQuICAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmNlblNZIDwtIGltcG9ydCgiRGF0YV9yYXcvQ0VOX1NZX0xMLmJlZCIpICU+JSBhc190aWJibGUgJT4lIG11dGF0ZSh0eXBlPSJjZW50cm9tZXJlIikgJT4lIG11dGF0ZShOYW1lPW5hbWUpCmV4cG9ydChjZW5TWSwgY29uPSJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9DRU4uZ2ZmMyIsIGZvcm1hdCA9ICJHRkYzIikKZXhwb3J0KGdlbmUsIGNvbj0iR2Vub21lX2Fubm90YXRpb25zL1NZMTRfZ2VuZS5nZmYzIiwgZm9ybWF0ID0gIkdGRjMiKQpleHBvcnQockROQV9TWSwgY29uPSJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9yUk5BLmdmZjMiLCBmb3JtYXQgPSAiR0ZGMyIpCmV4cG9ydCh0cm5hLCBjb249Ikdlbm9tZV9hbm5vdGF0aW9ucy9TWTE0X3RSTkEuZ2ZmMyIsIGZvcm1hdCA9ICJHRkYzIikKZXhwb3J0KHRlbCwgY29uPSJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9URUwuZ2ZmMyIsIGZvcm1hdCA9ICJHRkYzIikKZXhwb3J0KHRlbF9lbG1udCwgY29uPSJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9URUxfRUxNTlQuZ2ZmMyIsIGZvcm1hdCA9ICJHRkYzIikKZXhwb3J0KFRyYW5zcG9zb24sIGNvbj0iR2Vub21lX2Fubm90YXRpb25zL1NZMTRfVEVfTFRSLmdmZjMiLCBmb3JtYXQgPSAiR0ZGMyIpClNZZ2ZmNCA8LSBjKAoJaW1wb3J0KCJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9URUwuZ2ZmMyIpLAoJaW1wb3J0KCJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9nZW5lLmdmZjMiKSwKCWltcG9ydCgiR2Vub21lX2Fubm90YXRpb25zL1NZMTRfQ0VOLmdmZjMiKSwKCWltcG9ydCgiR2Vub21lX2Fubm90YXRpb25zL1NZMTRfclJOQS5nZmYzIiksCglpbXBvcnQoIkdlbm9tZV9hbm5vdGF0aW9ucy9TWTE0X3RSTkEuZ2ZmMyIpLAoJaW1wb3J0KCJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9URUxfRUxNTlQuZ2ZmMyIpLAoJaW1wb3J0KCJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9URV9MVFIuZ2ZmMyIpCgkpCmV4cG9ydChTWWdmZjQsY29uPSJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNC5nZmYzIiwgZm9ybWF0ID0gIkdGRjMiKQpgYGAKCkJZNDc0MiBhbm5vdGF0aW9uIHdlcmUgcHJvY2Vzc2VkIHNpbWlsYXJseS4gIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KQllnZmYgPC0gcmVhZEdGRigiRGF0YV9yYXcvQlkudGlkeS5nZmYzIikgJT4lIAoJYXNfdGliYmxlKCklPiUgCglzZWxlY3QoY2hyb209c2VxaWQsdHlwZSxzdGFydCxlbmQsc3RyYW5kLElELE5hbWUpICU+JQoJbXV0YXRlKHN0cmFuZD1jYXNlX3doZW4oc3RyYW5kPT0iMCJ+IioiLFR+c3RyYW5kKSkgJT4lCglmaWx0ZXIoY2hyb20hPSJjaHJNIikKQllnZmYyIDwtIEJZZ2ZmICU+JSAKCW11dGF0ZShhc1luYW1lPW1hcF9sZ2woTmFtZSxmdW5jdGlvbih6KSBzdWJzdHIoeiwxLDEpPT0iWSIgJiBzdWJzdHIoeiwyLDIpIT0iXyIgKSkgJT4lCgltdXRhdGUobmFtZV9zcGxpdD1tYXAyKE5hbWUsYXNZbmFtZSwgZnVuY3Rpb24oeCx5KSBpZiAoeSkge3N0cnNwbGl0KHgsIi8iKVtbMV1dfSkpICU+JQoJIyBtdXRhdGUobWFwY2hyPW1hcDIobmFtZV9zcGxpdCxhc1luYW1lLCBmdW5jdGlvbih4LHkpIHsKCSMgCWlmICh5KQoJIyAJCXsKCSMgCQlzYXBwbHkoeCwgZnVuY3Rpb24oeikgewoJIyAJCQljaCA8LSBncmVwKHN1YnN0cih6LDIsMiksTEVUVEVSUykgJT4lIGFzLnJvbWFuCgkjIAkJCXBhc3RlMCgiY2hyIixjaCkKCSMgCQkJfSkKCSMgCQl9CgkjIH0pKSAlPiUKCSMgbXV0YXRlKG1hcHN0cj1tYXAyKG5hbWVfc3BsaXQsYXNZbmFtZSwgZnVuY3Rpb24oeCx5KSB7CgkjIAlpZiAoeSkKCSMgCQl7CgkjIAkJc2FwcGx5KHgsIGZ1bmN0aW9uKHopIHsKCSMgCQkJaWZlbHNlKHN1YnN0cih6LDcsNyk9PSJXIiwiKyIsIi0iKQoJIyAJCQl9KQoJIyAJCX0KCSMgfSkpICU+JQoJIyBtdXRhdGUoZ2VuZW5hbWU9cG1hcF9jaHIoLiwgZnVuY3Rpb24oY2hyb20sc3RyYW5kLGFzWW5hbWUsbWFwY2hyLG1hcHN0cixOYW1lLC4uLil7CgkjIAlpZiAoYXNZbmFtZSkKCSMgCQl7CgkjIAkJbmV3bmFtZSA8LSBtYXBjaHJbbWFwY2hyPT1jaHJvbSAmIG1hcHN0cj09c3RyYW5kXSAlPiUgbmFtZXMoLikKCSMgCQl9ZWxzZXsKCSMgCQluZXduYW1lIDwtIE5hbWUJCgkjIAkJfQoJIyAJaWYgKGxlbmd0aChuZXduYW1lKT09MCAmIGFzWW5hbWUpIHtuZXduYW1lPU5BfQoJIyAJaWYgKGxlbmd0aChuZXduYW1lKT09MSAmIGFzWW5hbWUpIHsKCSMgCQluZXdzdHIgPC0gY2FzZV93aGVuKHN1YnN0cihuZXduYW1lLDcsNyk9PSJXIn4iKyIsc3Vic3RyKG5ld25hbWUsNyw3KT09IkMifiItIixUfiJhbWJpZ3VvdXMiKQoJIyAJCW5ld2NociA8LSBpZmVsc2UoIWlzLm5hKG5ld25hbWUpLHBhc3RlMCgiY2hyIixhcy5yb21hbihncmVwKHN1YnN0cihuZXduYW1lLDIsMiksTEVUVEVSUykpKSwiYW1iaWd1b3VzIikKCSMgCQlpZiAobmV3c3RyIT1zdHJhbmQgfCBuZXdjaHIhPWNocm9tKSB7bmV3bmFtZT1OQX0KCSMgCQl9CgkjIAlpZiAobGVuZ3RoKG5ld25hbWUpIT0xICYgYXNZbmFtZSkge25ld25hbWU9cGFzdGUwKE5hbWUsIl9hbWJpZ3VvdXMiKX0KCSMgCXJldHVybihuZXduYW1lKQoJIyAJfSkpCgltdXRhdGUoZ2VuZW5hbWU9cG1hcF9jaHIoLiwgZnVuY3Rpb24obmFtZV9zcGxpdCxhc1luYW1lLE5hbWUsLi4uKXsKCW5ld25hbWUgPC0gTkEKCWlmIChsZW5ndGgobmFtZV9zcGxpdCk9PTAgJiBhc1luYW1lKSB7bmV3bmFtZT1OQX0KCWlmIChsZW5ndGgobmFtZV9zcGxpdCk9PTEgJiBhc1luYW1lKSB7bmV3bmFtZT1OYW1lfQoJaWYgKGxlbmd0aChuYW1lX3NwbGl0KT4xICYgYXNZbmFtZSkge25ld25hbWU9cGFzdGUwKE5hbWUsIl9hbWJpZ3VvdXMiKX0KCXJldHVybihuZXduYW1lKQoJfSkpCkJZZ2ZmMyA8LSBsZWZ0X2pvaW4oQllnZmYyLGdlbmVfbmFtZSxieT1qb2luX2J5KGdlbmVuYW1lPT1OYW1lKSkgJT4lIG11dGF0ZShnZW5lPW1hcDJfY2hyKGdlbmUsZ2VuZW5hbWUsIGZ1bmN0aW9uKHgseSkgaWZlbHNlKGlzLm5hKHgpLHkseCkpKSU+JSBmaWx0ZXIoIWlzLm5hKGdlbmUpKQojIyBsb29rcyBiZXR0ZXIKCgpnZW5lIDwtIEJZZ2ZmMyAlPiUgZmlsdGVyKHR5cGU9PSJnZW5lIikKdHJuYSA8LSBCWWdmZjIgJT4lIGZpbHRlcih0eXBlPT0idFJOQSIpCnRlbCA8LSByZWFkX3RzdigiRGF0YV9yYXcvQlkudGVsLmJlZCIsY29sX25hbWVzPWMoInNlcW5hbWVzIiwic3RhcnQiLCJlbmQiLCJuYW1lIiksc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lIG11dGF0ZSh0eXBlPSJUZWxfcmVwZWF0IikKdGVsX2VsbW50IDwtIEJZZ2ZmMiAlPiUgZmlsdGVyKHR5cGUgJWluJSBjKCJYX2VsZW1lbnQiLCJZX3ByaW1lX2VsZW1lbnQiKSkKVHJhbnNwb3NvbiA8LSBCWWdmZjIgJT4lIGZpbHRlcih0eXBlICVpbiUgYygibW9iaWxlX2VsZW1lbnQiKSkKY2VuIDwtIEJZZ2ZmMiAlPiUgZmlsdGVyKHR5cGU9PSJjZW50cm9tZXJlIikKckROQV9CWSA8LSBHUmFuZ2VzKHNlcW5hbWVzPSJDUDAyNjMwMC4xIixyYW5nZXM9SVJhbmdlcyg0NDEyODQsNDk1MzMyKSxzdHJhbmQ9IioiLHNlcWluZm89c2VxaW5mQlkpICU+JSBhc190aWJibGUgJT4lIG11dGF0ZSh0eXBlPSJyUk5BIikgJT4lIG11dGF0ZShuYW1lPSJyRE5BIGxvY3VzIikKIyBiYXNlZCBvbiB0aGUgY292ZXJhZ2Ugb2YgcmVhZHMKZXhwb3J0KGdlbmUsIGNvbj0iR2Vub21lX2Fubm90YXRpb25zL0JZNDc0Ml9nZW5lLmdmZjMiLCBmb3JtYXQgPSAiR0ZGMyIpCmV4cG9ydChjZW4sIGNvbj0iR2Vub21lX2Fubm90YXRpb25zL0JZNDc0Ml9DRU4uZ2ZmMyIsIGZvcm1hdCA9ICJHRkYzIikKZXhwb3J0KHJETkFfQlksIGNvbj0iR2Vub21lX2Fubm90YXRpb25zL0JZNDc0Ml9yUk5BLmdmZjMiLCBmb3JtYXQgPSAiR0ZGMyIpCmV4cG9ydCh0cm5hLCBjb249Ikdlbm9tZV9hbm5vdGF0aW9ucy9CWTQ3NDJfdFJOQS5nZmYzIiwgZm9ybWF0ID0gIkdGRjMiKQpleHBvcnQodGVsLCBjb249Ikdlbm9tZV9hbm5vdGF0aW9ucy9CWTQ3NDJfVEVMLmdmZjMiLCBmb3JtYXQgPSAiR0ZGMyIpCmV4cG9ydCh0ZWxfZWxtbnQsIGNvbj0iR2Vub21lX2Fubm90YXRpb25zL0JZNDc0Ml9URUxfRUxNTlQuZ2ZmMyIsIGZvcm1hdCA9ICJHRkYzIikKZXhwb3J0KFRyYW5zcG9zb24sIGNvbj0iR2Vub21lX2Fubm90YXRpb25zL0JZNDc0Ml9URV9MVFIuZ2ZmMyIsIGZvcm1hdCA9ICJHRkYzIikKCkJZZ2ZmNCA8LSBjKAoJaW1wb3J0KCJHZW5vbWVfYW5ub3RhdGlvbnMvQlk0NzQyX1RFTC5nZmYzIiksCglpbXBvcnQoIkdlbm9tZV9hbm5vdGF0aW9ucy9CWTQ3NDJfZ2VuZS5nZmYzIiksCglpbXBvcnQoIkdlbm9tZV9hbm5vdGF0aW9ucy9CWTQ3NDJfQ0VOLmdmZjMiKSwKCWltcG9ydCgiR2Vub21lX2Fubm90YXRpb25zL0JZNDc0Ml9yUk5BLmdmZjMiKSwKCWltcG9ydCgiR2Vub21lX2Fubm90YXRpb25zL0JZNDc0Ml90Uk5BLmdmZjMiKSwKCWltcG9ydCgiR2Vub21lX2Fubm90YXRpb25zL0JZNDc0Ml9URUxfRUxNTlQuZ2ZmMyIpLAoJaW1wb3J0KCJHZW5vbWVfYW5ub3RhdGlvbnMvQlk0NzQyX1RFX0xUUi5nZmYzIikKCSkKZXhwb3J0KEJZZ2ZmNCxjb249Ikdlbm9tZV9hbm5vdGF0aW9ucy9CWTQ3NDIuZ2ZmMyIsIGZvcm1hdCA9ICJHRkYzIikKYGBgCgpJbiBvcmRlciB0byBtYXAga25vd24gQVJTIG9uIHRoZSBCWSBhbmQgU1kgZ2Vub21lLCBBUlMgcG9zaXRpb24gd2VyZSBkb3dubG9hZCBmcm9tIFtPcmlEQl0oaHR0cDovL2NlcmV2aXNpYWUub3JpZGIub3JnLykgKDIwMjMxMjA0KS4gQSB1bmlxdWUgbmFtaW5nIHNjaGVtZSB3YXMgZGVzaWduZWQgZm9yIHRoZSBMaWtlbHkgKG9yIHJlc3BlY3RpdmVseSBEdWJpb3VzKSBBUlMgYnkgYWRkaW5nIHRoZSBjaHJvbW9zb21lIG5hbWUgYW5kIHRoZSBvcmRlciBvZiB0aGUgbGlrZWx5IChvciByZXNwZWN0aXZlbHkgZHViaW91cykgQVJTIGluIHRoaXMgY2hyb21vc29tZS4gVGhlcmUgd2VyZSB0d28gQVJTIG5hbWVzIEFSUzMwMiB3aGljaCB3ZXJlIHRoZW4gbmFtZXMgQVJTMzAyXzEgYW5kIEFSUzMwMl8yLiBUaGUgRE5BIHNlcXVlbmNlIGNvcnJlc3BvbmRpbmcgdG8gdGhlc2UgQVJTIHdhcyB0aGVuIGV4dHJhY3RlZCBmcm9tIHRoZSByZWZlcmVuY2UgZ2Vub21lIHVzZWQgaW4gT3JpREIgKHNhY0NlcjEpLiBUaGUgcmVzdWx0aW5nIGZhc3RhIGZpbGUgKEFsbEFSU19zYWNDZXIxXzIwMjMxMjE4LmZhKSB3YXMgdGhlbiBtYXBwZWQgZWl0aGVyIGluIHRoZSBCWTQ3NDIgb3IgdGhlIFNZMTQgZ2Vub21lIHVzaW5nIGJ3YSBtZW0gYW5kIHRoZSBiYW0gZmlsZSB3YXMgdGhlbiBjb252ZXJ0ZWQgdG8gYmVkIHVzaW5nIGJlZHRvb2xzIGJhbXRvYmVkLiAgCldhcm5pbmcgQVJTMTMxMS43IGlzIG1hcHBlZCBvbiBjaHJYSVYgb24gT3JpREIgZGVzcGl0ZSB0aGUgbmFtZS4gIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyMgMS0gRXh0cmFjdCBGYXN0YSBmcm9tIEdlbm9tZQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoIkJTZ2Vub21lLlNjZXJldmlzaWFlLlVDU0Muc2FjQ2VyMSIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkocnRyYWNrbGF5ZXIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkodGlkeXZlcnNlKSkKYCUrJWA8LXBhc3RlMAoKZ2Vub21lX3NhY0NlcjEgPC0gQlNnZW5vbWUuU2NlcmV2aXNpYWUuVUNTQy5zYWNDZXIxCgpBUlNfb3JpREIgPC0gcmVhZF90c3YoIkRhdGFfcmF3L0FSU2Zyb21PcmlEQjIwMjMxMjA0LnR4dCIsc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lCgltdXRhdGUoY2hyUm9tPSJjaHIiICUrJSBhcy5yb21hbihjaHIpKSU+JSAKCW11dGF0ZShjaHI9ImNociIgJSslIGNocikKIyMjIGNyZWF0ZSBhIG5hbWluZyBmb3IgQVJTIG9yaURCCkFSU19vcmlEQjIgPC0gQVJTX29yaURCICU+JQoJbXV0YXRlKG5ld25hbWU9cG1hcF9jaHIoLiwgZnVuY3Rpb24obmFtZSxjaHJSb20sc3RhdHVzLC4uLikgewoJCWlmIChzdGF0dXMgJWluJSBjKCJMaWtlbHkiLCJEdWJpb3VzIikpCgkJCXtyZXM9cGFzdGUoc3RhdHVzLGNoclJvbSxzZXA9Il8iKX0KCQkJZWxzZQoJCQl7cmVzPW5hbWV9CgkJCXJldHVybihyZXMpCgkJfSkpJT4lCglncm91cF9ieShuZXduYW1lKSAlPiUKCW11dGF0ZShuZXduYW1lMj1pZmVsc2UoKCFzdGF0dXMgJWluJSBjKCJMaWtlbHkiLCJEdWJpb3VzIikgJiBuYW1lIT0iQVJTMzAyIiksbmFtZSxwYXN0ZShuZXduYW1lLDE6bigpLHNlcD0iXyIpKSkgJT4lIAoJdW5ncm91cCAlPiUKCW11dGF0ZShuYW1lPWlmZWxzZShpcy5uYShuYW1lKSwicHV0YXRpdmVfQVJTIixuYW1lKSkKQVJTX29yaURCMl9HUiA8LSBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUoQVJTX29yaURCMixzdGFydHMuaW4uZGYuYXJlLjBiYXNlZD1UKQoKQVJTX29yaURCMl9zZXEgPC0gZ2V0U2VxKGdlbm9tZV9zYWNDZXIxLEFSU19vcmlEQjJfR1IpCm5hbWVzKEFSU19vcmlEQjJfc2VxKSA8LSBBUlNfb3JpREIyJG5ld25hbWUyCndyaXRlWFN0cmluZ1NldChBUlNfb3JpREIyX3NlcSwgIkRhdGEvQWxsQVJTX3NhY0NlcjFfMjAyNTAzMTIuZmEiKQpgYGAKVGhlc2Ugc2VxdWVuY2UgZnJvbSB0aGUgZmFzdGEgZmlsZSB3ZXJlIHRoZW4gbWFwcGVkIG9uIFNZIGFuZCBCWSBnZW5vbWUgdXNpbmcgYndhIChWZXJzaW9uOiAwLjcuMTctcjExOTgtZGlydHkpLCBzYW10b29scyAoVmVyc2lvbjogMS4xNykgYW5kIGJlZHRvb2xzIChWZXJzaW9uOiAyLjI2LjAtMCkKCmBgYHtzaCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpmYWZpbGU9QWxsQVJTX3NhY0NlcjFfMjAyNTAzMTIKCiMgQlk0NzQyCmdlbm9tZT0uLi9EYXRhX3Jhdy9CWTQ3NDIuZmEKZ2Vub3N1ZmY9Qlk0NzQyCgp+L3dvcmsvYmluL2J3YS9id2EgaW5kZXggJGdlbm9tZQp+L3dvcmsvYmluL2J3YS9id2EgbWVtIC10IDEwICRnZW5vbWUgJGZhZmlsZS5mYSB8IHNhbXRvb2xzIHNvcnQgLUAgNSAtbyAkZmFmaWxlJGdlbm9zdWZmLnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggJGZhZmlsZSRnZW5vc3VmZi5zb3J0ZWQuYmFtCmJlZHRvb2xzIGJhbXRvYmVkIC1pICRmYWZpbGUkZ2Vub3N1ZmYuc29ydGVkLmJhbSA+ICRmYWZpbGUkZ2Vub3N1ZmYuYmVkCgojIFNZMTQKZ2Vub21lPS4uL0RhdGFfcmF3L1NZMTQuZmEKZ2Vub3N1ZmY9U1kxNAp+L3dvcmsvYmluL2J3YS9id2EgaW5kZXggJGdlbm9tZQp+L3dvcmsvYmluL2J3YS9id2EgbWVtIC10IDEwICRnZW5vbWUgJGZhZmlsZS5mYSB8IHNhbXRvb2xzIHNvcnQgLUAgNSAtbyAkZmFmaWxlJGdlbm9zdWZmLnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggJGZhZmlsZSRnZW5vc3VmZi5zb3J0ZWQuYmFtCmJlZHRvb2xzIGJhbXRvYmVkIC1pICRmYWZpbGUkZ2Vub3N1ZmYuc29ydGVkLmJhbSA+ICRmYWZpbGUkZ2Vub3N1ZmYuYmVkCmBgYApJIG5lZWQgdG8gYWRkIHRoZSB0ZXN0IGZvciBBUlMgbWFwcGVkIG9uIHRoZSB3cm9uZyBjaHJvbW9zb21lLiAgCkFSUzEzMTEuNyBpcyBvbiBjaHJYSVYgaW4gb3JpREIgYW5kIEFSUzEzMWEgYW5kIDEzMW4gZ2VuZXJhdGUgTkEKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpBUlNfQlkgPC0gaW1wb3J0KCJEYXRhL0FsbEFSU19zYWNDZXIxXzIwMjUwMzEyQlk0NzQyLmJlZCIpCnRvdG8gPC0gYXNfdGliYmxlKEFSU19CWSkgJT4lCgkgbXV0YXRlKGNocm9tUk9NPWZhY3RvcihzZXFuYW1lcykgJT4lCglmY3RfcmVjb2RlKCJjaHJJIj0iQ1AwMjYzMDEuMSIsImNocklJIj0iQ1AwMjYyOTYuMSIsImNocklJSSI9IkNQMDI2Mjk3LjEiLCJjaHJJViI9IkNQMDI2Mjk4LjEiLCJjaHJWIj0iQ1AwMjYyOTkuMSIsImNoclZJIj0iQ1AwMjYzMDIuMSIsImNoclZJSSI9IkNQMDI2Mjk0LjEiLCJjaHJWSUlJIj0iQ1AwMjYyODcuMSIsImNocklYIj0iQ1AwMjYyOTUuMSIsImNoclgiPSJDUDAyNjI4OC4xIiwiY2hyWEkiPSJDUDAyNjI4OS4xIiwiY2hyWElJIj0iQ1AwMjYzMDAuMSIsImNoclhJSUkiPSJDUDAyNjI5MS4xIiwiY2hyWElWIj0iQ1AwMjYyOTMuMSIsImNoclhWIj0iQ1AwMjYzMDMuMSIsImNoclhWSSI9IkNQMDI2MjkwLjEiKSkgJT4lCgltdXRhdGUoY2hyQVJTPW1hcF9jaHIobmFtZSwgZnVuY3Rpb24oeCkgewoJCXkgPC0gc3Ryc3BsaXQoeCwiXyIpW1sxXV0KCQlpZiAoeVsxXSAlaW4lIGMoIkxpa2VseSIsIkR1YmlvdXMiKSkgCgkJCXtyZXMgPC0geVsyXX0KCQllbHNlCgkJCXtyZXMxIDwtIHlbMV0gJT4lCgkJCQlzdHJfcmVtb3ZlKCJBUlMiKSAlPiUKCQkJCWFzLm51bWVyaWMoKQoJCQlyZXMgPC0gcGFzdGUwKCJjaHIiLGZsb29yKHJlczEvMTAwKSAlPiUgYXMucm9tYW4pCgkJCX0KCQlyZXR1cm4ocmVzKQoJfSkpCiMjIEFSUzEzMW4gYW5kIDEzMWEgZ2VuZXJhdGUgTkEgLS0+IGFkZCBhbiBleGNlcHRpb24gZm9yIHRob3NlCgpBUlNfT3JpREJfY2xlYW5lZCA8LSB0b3RvICU+JSBmaWx0ZXIoY2hyb21ST009PWNockFSUyB8IG5hbWUgJWluJSBjKCJBUlMxMzFhIiwiQVJTMTMxbiIsIkFSUzEzMTEuNyIpKQpgYGAKVGhlbiBJIGFkZCB0aGUgb3RoZXJuYW1lcyBmcm9tIE9yaURCIHVzaW5nIGEgcHJldmlvdXMgbWFwcGluZyBieSBKYWRlIFBlbGxldApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpBUlNfQllfSlBHUiA8LSBpbXBvcnQoIkRhdGFfcmF3L0FSU19CWW1hcHBlZEpQXzIwMjMxMjE0LmJlZCIpCkFSU19CWV9jbGVhbmVkIDwtIGxlZnRfam9pbihBUlNfT3JpREJfY2xlYW5lZCAlPiUgc2VsZWN0KC1jKGNockFSUyxzY29yZSkpLGFzX3RpYmJsZShBUlNfQllfSlBHUikgJT4lIHJlbmFtZShvdGhlcl9uYW1lPW5hbWUpICU+JSBzZWxlY3QoLWMoc3RyYW5kLHNjb3JlKSksYnkgPSBqb2luX2J5KHNlcW5hbWVzLCBzdGFydCwgZW5kLCB3aWR0aCkpCnNhdmVSRFMoQVJTX0JZX2NsZWFuZWQsZmlsZT0iRGF0YS9BUlNfQllfY2xlYW5lZC5yZHMiKQpleHBvcnQobWFrZUdSYW5nZXNGcm9tRGF0YUZyYW1lKEFSU19CWV9jbGVhbmVkLGtlZXAuZXh0cmEuY29sdW1ucz1UKSxjb249Ikdlbm9tZV9hbm5vdGF0aW9ucy9BUlNfQllfY2xlYW5lZC5iZWQiKQoKYGBgCgpUaGVuIEkgbmVlZCB0byB0cmFuc3Bvc2UgdGhlbSB0byBTWQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KQlkyU1kgPC0gcmVhZF90c3YoIkJpZ0ZpbGVzL0RhdGEvQlkyU1ljb24udHN2Lmd6IixzaG93X2NvbF90eXBlcyA9IEZBTFNFKQphcnNCWSA8LSBBUlNfQllfY2xlYW5lZAphcnNCWSRmaWQgPC0gMTpucm93KGFyc0JZKQphcnNCWTIgPC0gYXJzQlkgJT4lIG11dGF0ZShCWXBvcz1tYXAyKHN0YXJ0LGVuZCxmdW5jdGlvbih4LHkpIHg6eSkpICU+JSBzZWxlY3QoY2hyb21CWT1zZXFuYW1lcyxzdGFydCxlbmQsQllwb3MsZmlkLG5hbWUpCnJlcyA8LSBpbm5lcl9qb2luKHVubmVzdChhcnNCWTIsY29scz1jKEJZcG9zKSksQlkyU1ksYnkgPSBjKCJjaHJvbUJZIiwgIkJZcG9zIikpICU+JQoJZ3JvdXBfYnkoZmlkLGNocm9tQlkpICU+JQoJbmVzdCgpICU+JQoJdW5ncm91cCAlPiUKCW11dGF0ZShzdGFydG49bWFwX2ludChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LDEpICU+JSBwdWxsKEJZcG9zKSkpICU+JQoJbXV0YXRlKGVuZG49bWFwX2RibChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LG5yb3coeCkpICU+JSBwdWxsKEJZcG9zKSkpICU+JQoJbXV0YXRlKHN0YXJ0blNZPW1hcF9pbnQoZGF0YSxmdW5jdGlvbih4KSBkcGx5cjo6c2xpY2UoeCwxKSAlPiUgcHVsbChTWXBvcykpKSAlPiUKCW11dGF0ZShlbmRuU1k9bWFwX2RibChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LG5yb3coeCkpICU+JSBwdWxsKFNZcG9zKSkpICU+JQoJc2VsZWN0KC1kYXRhKQphcnNCWVNZIDwtIGlubmVyX2pvaW4oYXJzQlkscmVzLGJ5ID0gam9pbl9ieShmaWQpKSAlPiUgbXV0YXRlKGNocm9tU1k9IkNQMDI5MTYwLjEiKSAlPiUgYXJyYW5nZShzdGFydG5TWSkKc2F2ZVJEUyhhcnNCWVNZLGZpbGU9IkRhdGEvQVJTX0JZb25TWS5yZHMiKQpBUlNfQllTWV9HUiA8LSB3aXRoKGFyc0JZU1ksR1JhbmdlcyhzZXFuYW1lcz1jaHJvbVNZLHJhbmdlcz1JUmFuZ2VzKHN0YXJ0PXN0YXJ0blNZLGVuZD1lbmRuU1kpLHNlcWluZm89c2VxaW5mU1ksbmFtZT1uYW1lKSkgJT4lIHNvcnQKZXhwb3J0KEFSU19CWVNZX0dSLGNvbj0iR2Vub21lX2Fubm90YXRpb25zL0FSU19CWW9uU1kuYmVkIikKYGBgCg==