01_Data Transposition


Generating the nucleotide to nucleotide conversion table

Original fasta files for the genomes (SY14.fa and BY4742.fa) were downloaded from the accession identifier PRJNA429985 Chromosomes from the BY strain were used a “reads” and mapped on the SY genome using minimap2 (version=2.22-r1101) and samtools (version=1.17):

Then the conversion table was build in R based on the CIGAR information from the resulting bam-file.

suppressMessages(library(rtracklayer))
suppressMessages(library(tidyverse))
suppressMessages(library(GenomicAlignments))
`%+%` <- paste0

First two SeqInfo object were generated:

seqinfSY <- Seqinfo(seqnames=c("CP029160.1"),seqlengths=c(11848804),isCircular=F,genome="SY14")
saveRDS(seqinfSY, file="Data/seqinfSY.rds")
BYgenome <- Biostrings::readDNAStringSet("Data_raw/BY4742.fa")
names(BYgenome) <- sapply(names(BYgenome), function(x) strsplit(x," ")[[1]][1])
seqinfBY <- Seqinfo(seqnames=names(BYgenome), seqlengths=lengths(BYgenome), genome="BY4742", isCircular=c(rep(F,16),T))
saveRDS(seqinfBY, file="Data/seqinfBY.rds")

We built two functions to shift the coordinates according the the CIGAR information:

cigar2genome <- function(x) {
### x is a char vector from CIGAR
### I and S sequence in read but not in genome
    xres <- x
    xres[x %in% c("I","S","H")] <- 0
    xres[x %in% c("M","D")] <- 1
    return(xres)
    }
    
cigar2signal <- function(x) {
### x is a char vector from CIGAR
### I and S sequence in read but not in genome and D are not in the read_signal
    xres <- x
    xres[x %in% c("D")] <- 0
    xres[x %in% c("M","I","S","H")] <- 1
    return(xres)
    }

Then the data from the bam-file were processed to generate a coordinate to coordinate conversion table masterconvtable that can convert coordinates from BY to SY and reciprocally.

bam.in <- "Data/BYonSY.sorted.bam"

data0 <- try(system(paste0("samtools view -F 2047 ",bam.in," | cut -f 1,2,3,4,6"),intern=T))
# keep only primary and supplementary mappings
data1 <- lapply(data0, function(x) 
    {
        input1 <- strsplit(x,"\t")[[1]]
        flag <- input1[2]
        startBYSY <- as.numeric(input1[4])
        chromBY <- input1[1]
        chromSY <- input1[3]
        cigar <- GenomicAlignments::cigarToRleList(input1[5])[[1]]
        cig <- as.vector(cigar)
        BYpos <- cumsum(cigar2signal(cig))
        SYpos <- startBYSY-1+cumsum(as.numeric(cigar2genome(cig)))
        transtib <- tibble(cig,chromBY,BYpos,chromSY,SYpos)
        leng <- sum(as.numeric(cigar2genome(cig)))
        endBYSY <- startBYSY+leng-1
        res <- tibble(flag,chromBY,chromSY,startBYSY,endBYSY,leng,BYpos=list(BYpos),SYpos=list(SYpos),cig=list(cig),cigar=list(input1[5]),convtable=list(transtib))
    return(res)
    })
    
data_out2 <- do.call(bind_rows,data1) %>%
# filter for the primary aligned and for long supplementary mapping to recover the big chunk of chrVIII that minimap2 generates a supplementary (CUP1-1 locus)
     filter(flag==0 | leng>10000) %>%
     mutate(chromBYrom=factor(chromBY) %>%
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")) %>%
    select(chromBY,chromBYrom,chromSY,startBYSY,endBYSY,convtable)

data4 <- data_out2 %>% 
    select(chromBYrom,convtable) %>%
    unnest(col=c(convtable)) %>%
    filter(cig=="M") %>%
    filter(!duplicated(SYpos,fromLast=T)) %>%
    group_by(chromSY,chromBY,chromBYrom) %>%
    summarise(startSY=min(SYpos),endSY=max(SYpos),startBY=min(BYpos),endBY=max(BYpos)) %>%
    ungroup %>%
    arrange(startSY)

masterconvtable <- data_out2 %>% 
    select(chromBYrom,convtable) %>%
    unnest(col=c(convtable)) %>%
    filter(cig=="M") %>%
    filter(!duplicated(SYpos,fromLast=T)) %>%
    select(-cig)

chromBYonSYgr <- with(data4,GRanges(seqnames=chromSY,range=IRanges(startSY,endSY),strand="*",name=chromBYrom,seqinfo=seqinfSY))

chromSYonBYgr <- with(data4,GRanges(seqnames=chromBY,range=IRanges(startBY,endBY),strand="*",name=chromSY,seqinfo=seqinfBY))
export(chromBYonSYgr,con="Genome_annotations/chromBYonSYgr.bed")
export(chromSYonBYgr,con="Genome_annotations/chromSYonBYgr.bed")
write_tsv(masterconvtable,file="BigFiles/Data/BY2SYcon.tsv.gz")

As control, genomic coordinates from the 16 BY chromosomes were “transposed” this way.

chromBY <- as(seqinfBY,"GRanges")

chromBY2 <- chromBY %>% as_tibble %>% mutate(coord=map2(start,end, function(x,y) x:y)) %>% unnest(cols=c(coord)) %>% select(chromBY=seqnames,BYpos=coord)
chromBY2SY <- left_join(masterconvtable,chromBY2) %>% 
    group_by(chromSY,chromBY,chromBYrom) %>%
    summarise(startSY=min(SYpos),endSY=max(SYpos),startBY=min(BYpos),endBY=max(BYpos)) %>%
    ungroup %>%
    arrange(startSY)

chromBY2SYgr <- with(chromBY2SY,GRanges(seqnames=chromSY,range=IRanges(startSY,endSY),strand="*",name=chromBYrom,seqinfo=seqinfSY))

chromBY2SY2 <- left_join(masterconvtable,chromBY2)
chromBY2SY3 <- split(chromBY2SY2,chromBY2SY2$chromBYrom)

chromBY2SY3grl <- lapply(seq_along(chromBY2SY3), function(i)
    {
        x <- chromBY2SY3[[i]]
        res <- with(x,GRanges(seqnames=chromSY,ranges=IRanges(start=SYpos,width=1),strand="*",seqinfo=seqinfSY,name=chromBYrom)) %>% GenomicRanges::reduce()
        res$name <- names(chromBY2SY3)[[i]]
        return(res)
        })
chromBY2SY3gr <- do.call(c,chromBY2SY3grl)
## correspond to all the partial matched segment

export(chromBY2SYgr,con="Genome_annotations/chromBY2SYgr.bed")
export(chromBY2SY3gr,con="Genome_annotations/chromBY2SY3gr.bed")

identical(chromBY2SYgr,chromBYonSYgr)
#[1] TRUE

The chromBY2SY3gr.bed illustrates that this conversion is not always a perfect match and might conduct to some information lost but it appears really acceptable for our purpose.

Converting NFS forks from BY to SY

First we need to load all the forks coordinates from the BY experiment

suppressMessages(library(rtracklayer))
suppressMessages(library(tidyverse))
`%+%` <- paste0

forks_BYBY <- readRDS("Data_raw/forks_BYBY.rds")
saveRDS(forks_BYBY,file="Data/forks_BYBY.rds")

Then we import the conversion table if not in memory

BY2SY <- read_tsv("BigFiles/Data/BY2SYcon.tsv.gz",show_col_types = FALSE)

Then we assign at each fork a unique identifier (fid) and proceed to the conversion

forks_BYBY$fid <- 1:nrow(forks_BYBY)
forksBY <- forks_BYBY %>% mutate(BYpos=map2(X0,X1,function(x,y) x:y)) %>% select(chromBY=chrom,direc,X0,X1,BYpos,fid,speed,read_id)

forksBYlist <- split(forksBY,forksBY$chromBY)

forksBY2SYlist <- lapply(forksBYlist, function(x)
{
res <- inner_join(unnest(x,cols=c(BYpos)),BY2SY,by = c("chromBY", "BYpos")) %>%
    group_by(fid,chromBY,direc) %>%
    nest() %>%
    ungroup %>%
    mutate(X0n=map_int(data,function(x) dplyr::slice(x,1) %>% pull(BYpos))) %>%
    mutate(X1n=map_dbl(data,function(x) dplyr::slice(x,nrow(x)) %>% pull(BYpos))) %>%
    mutate(X0nSY=map_int(data,function(x) dplyr::slice(x,1) %>% pull(SYpos))) %>%
    mutate(X1nSY=map_dbl(data,function(x) dplyr::slice(x,nrow(x)) %>% pull(SYpos))) %>%
    select(-data)
    return(res)
})
forksBY2SY <- do.call(bind_rows,forksBY2SYlist)

forksBY2SY2 <- inner_join(forksBY2SY,forks_BYBY,by = join_by(direc, fid)) %>% mutate(chromSY="CP029160.1")
saveRDS(forksBY2SY2,file="Data/forks_BYBYSY.rds")

forks_SYSY <- readRDS("Data_raw/forks_SYSY.rds")
saveRDS(forks_SYSY,file="Data/forks_SYSY.rds")

Conversion could result in change of the width of the object, that could affect the new speed determination. Therefore, the speed measured on the mapping genome was kept during the genomic coordinates conversion process. As control, SY data can be transposed to BY and then back to SY and BY data can be transposed to SY and then back to BY.

Converting initiations and terminations from BY to SY

Similarly, Initiations and terminations can be transposed:

initer_BYBY <- readRDS("Data_raw/initer_BYBY.rds")
saveRDS(initer_BYBY,file="Data/initer_BYBY.rds")

initer_BYBY$fid <- 1:nrow(initer_BYBY)

initerBY <- initer_BYBY %>% 
    mutate(BYpos=map2(x0,x1,function(x,y) x:y)) %>% 
    mutate(wid=x1-x0+1) %>%
    select(chromBY=chrom,x0,x1,BYpos,fid,type,wid)

initerBYlist <- split(initerBY,initerBY$chromBY)

initerBY2SYlist <- lapply(initerBYlist, function(x)
{
res <- inner_join(unnest(x,cols=c(BYpos)),BY2SY,by = c("chromBY", "BYpos")) %>%
    group_by(fid,chromBY,type,wid) %>%
    nest() %>%
    ungroup %>%
    mutate(x0n=map_int(data,function(y) dplyr::slice(y,1) %>% pull(BYpos))) %>%
    mutate(x1n=map_dbl(data,function(y) dplyr::slice(y,nrow(y)) %>% pull(BYpos))) %>%
    mutate(x0nSY=map_int(data,function(y) dplyr::slice(y,1) %>% pull(SYpos))) %>%
    mutate(x1nSY=map_dbl(data,function(y) dplyr::slice(y,nrow(y)) %>% pull(SYpos))) %>%
    select(-data)
    return(res)
})
initerBY2SY <- do.call(bind_rows,initerBY2SYlist)

initerBY2SY2 <- inner_join(initerBY2SY,initer_BYBY,by = join_by(fid, type)) %>% mutate(chromSY="CP029160.1") %>% mutate(centerSY=floor((x0nSY+x1nSY)/2))
# WARNING, the new "center" might by wrong for partially deleted segment but it was the only way to deal with partially deleted segment that loose the center!

saveRDS(initerBY2SY2,file="Data/initer_BYBYSY.rds")

initer_SYSY <- readRDS("Data_raw/initer_SYSY.rds")
saveRDS(initer_SYSY,file="Data/initer_SYSY.rds")

Similarly to the forks transposition process, width of initiation or termination segment could also be changed during the conversion process, affecting the initiation (or termination) probability affected to the segment. Therefore, the width from the original mapping was also kept in the conversion process. Unfortunately, it was not possible to transfer unambiguously the center of the initiation and termination segment as its precise coordinate did often fall within microdeletion present in the CIGAR code used to build the conversion table.

Converting nanotiming data from BY to SY

source("Helper_function.r")

BYnano_rep1 <- readRDS("Data_raw/nanoT_BY_rep1.rds") %>% 
    mutate(nanoTsc99=1+myscaling0(mean_br_bin,infq=0.005,supq=0.995))
BYnano_rep1$fid <- 1:nrow(BYnano_rep1)
BYnanoBY2 <- BYnano_rep1 %>% mutate(BYpos=map2(positions,positions+999,function(x,y) x:y)) %>% select(chromBY=chrom,positions,BYpos,fid,mean_br_bin)
res <- inner_join(unnest(BYnanoBY2,cols=c(BYpos)),BY2SY,by = c("chromBY", "BYpos")) %>%
    group_by(fid,chromBY) %>%
    nest() %>%
    ungroup %>%
    mutate(startn=map_dbl(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_dbl(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)
BYnano_rep1SY <- inner_join(BYnano_rep1,res,by = join_by(fid)) %>%
    mutate(chromSY="CP029160.1")
BY_rep1 <- BYnano_rep1SY %>% 
    mutate(Rep="rep1") %>%
    mutate(strain="BY")
saveRDS(BY_rep1,file="Data/nanoT_BYBYSY_rep1.rds")
BYrep1.gr <- with(BY_rep1,GRanges(seqnames=chromSY,ranges=IRanges(start=startnSY,end=endnSY),strand="*",seqinfo=seqinfSY,nanoT=nanoTsc99))
cov2exp <- coverage(BYrep1.gr,weight=BYrep1.gr$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_BYBYSY_rep1.bw")
BYrep1.grBY <- with(BYnano_rep1,suppressWarnings(GRanges(seqnames=chrom,ranges=IRanges(start=positions,end=positions+999),strand="*",seqinfo=seqinfBY,nanoT=nanoTsc99)))
cov2exp <- coverage(BYrep1.grBY,weight=BYrep1.grBY$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_BYBY_rep1.bw")

BYnano_rep2 <- readRDS("Data_raw/nanoT_BY_rep2.rds") %>%
    mutate(nanoTsc99=1+myscaling0(mean_br_bin,infq=0.005,supq=0.995))
BYnano_rep2$fid <- 1:nrow(BYnano_rep2)
BYnanoBY2 <- BYnano_rep2 %>% mutate(BYpos=map2(positions,positions+999,function(x,y) x:y)) %>% select(chromBY=chrom,positions,BYpos,fid,mean_br_bin)
res <- inner_join(unnest(BYnanoBY2,cols=c(BYpos)),BY2SY,by = c("chromBY", "BYpos")) %>%
    group_by(fid,chromBY) %>%
    nest() %>%
    ungroup %>%
    mutate(startn=map_dbl(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_dbl(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)
BYnano_rep2SY <- inner_join(BYnano_rep2,res,by = join_by(fid)) %>%
    mutate(chromSY="CP029160.1") 
BY_rep2 <- BYnano_rep2SY  %>% 
    mutate(Rep="rep2") %>%
    mutate(strain="BY")
saveRDS(BY_rep2,file="Data/nanoT_BYBYSY_rep2.rds")
BYrep2.gr <- with(BY_rep2,GRanges(seqnames=chromSY,ranges=IRanges(start=startnSY,end=endnSY),strand="*",seqinfo=seqinfSY,nanoT=nanoTsc99))
cov2exp <- coverage(BYrep2.gr,weight=BYrep2.gr$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_BYBYSY_rep2.bw")
BYrep2.grBY <- with(BYnano_rep2,suppressWarnings(GRanges(seqnames=chrom,ranges=IRanges(start=positions,end=positions+999),strand="*",seqinfo=seqinfBY,nanoT=nanoTsc99)))
cov2exp <- coverage(BYrep2.grBY,weight=BYrep2.grBY$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_BYBY_rep2.bw")

BYnano_rep3 <- readRDS("Data_raw/nanoT_BY_rep3.rds") %>% 
    mutate(nanoTsc99=1+myscaling0(mean_br_bin,infq=0.005,supq=0.995))
BYnano_rep3$fid <- 1:nrow(BYnano_rep3)
BYnanoBY2 <- BYnano_rep3 %>% mutate(BYpos=map2(positions,positions+999,function(x,y) x:y)) %>% select(chromBY=chrom,positions,BYpos,fid,mean_br_bin)
res <- inner_join(unnest(BYnanoBY2,cols=c(BYpos)),BY2SY,by = c("chromBY", "BYpos")) %>%
    group_by(fid,chromBY) %>%
    nest() %>%
    ungroup %>%
    mutate(startn=map_dbl(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_dbl(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)
BYnano_rep3SY <- inner_join(BYnano_rep3,res,by = join_by(fid)) %>%
    mutate(chromSY="CP029160.1")
BY_rep3 <- BYnano_rep3SY %>% 
    mutate(Rep="rep3") %>%
    mutate(strain="BY")
saveRDS(BY_rep3,file="Data/nanoT_BYBYSY_rep3.rds")
BYrep3.gr <- with(BY_rep3,GRanges(seqnames=chromSY,ranges=IRanges(start=startnSY,end=endnSY),strand="*",seqinfo=seqinfSY,nanoT=nanoTsc99))
cov2exp <- coverage(BYrep3.gr,weight=BYrep3.gr$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_BYBYSY_rep3.bw")
BYrep3.grBY <- with(BYnano_rep3,suppressWarnings(GRanges(seqnames=chrom,ranges=IRanges(start=positions,end=positions+999),strand="*",seqinfo=seqinfBY,nanoT=nanoTsc99)))
cov2exp <- coverage(BYrep3.grBY,weight=BYrep3.grBY$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_BYBY_rep3.bw")

SYnano_rep1 <- readRDS("Data_raw/nanoT_SY_rep1.rds") %>%
    rename(chromSY=chrom)
SY_rep1 <- SYnano_rep1 %>% 
    mutate(nanoTsc99=1+myscaling0(mean_br_bin,infq=0.005,supq=0.995)) %>%
    mutate(Rep="rep1") %>%
    mutate(strain="SY")
saveRDS(SY_rep1,file="Data/nanoT_SYSY_rep1.rds")
SYrep1.gr <- with(SY_rep1,GRanges(seqnames=chromSY,ranges=IRanges(start=positions,end=positions+999),strand="*",seqinfo=seqinfSY,nanoT=nanoTsc99))
cov2exp <- coverage(SYrep1.gr,weight=SYrep1.gr$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_SYSY_rep1.bw")

SYnano_rep2 <- readRDS("Data_raw/nanoT_SY_rep2.rds") %>%
    rename(chromSY=chrom)
SY_rep2 <- SYnano_rep2 %>% 
    mutate(nanoTsc99=1+myscaling0(mean_br_bin,infq=0.005,supq=0.995)) %>%
    mutate(Rep="rep2") %>%
    mutate(strain="SY")
saveRDS(SY_rep2,file="Data/nanoT_SYSY_rep2.rds")
SYrep2.gr <- with(SY_rep2,GRanges(seqnames=chromSY,ranges=IRanges(start=positions,end=positions+999),strand="*",seqinfo=seqinfSY,nanoT=nanoTsc99))
cov2exp <- coverage(SYrep2.gr,weight=SYrep2.gr$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_SYSY_rep2.bw")

SYnano_rep3 <- readRDS("Data_raw/nanoT_SY_rep3.rds")%>%
    rename(chromSY=chrom)
SY_rep3 <- SYnano_rep3 %>% 
    mutate(nanoTsc99=1+myscaling0(mean_br_bin,infq=0.005,supq=0.995)) %>%
    mutate(Rep="rep3") %>%
    mutate(strain="SY")
saveRDS(SY_rep3,file="Data/nanoT_SYSY_rep3.rds")
SYrep3.gr <- with(SY_rep3,GRanges(seqnames=chromSY,ranges=IRanges(start=positions,end=positions+999),strand="*",seqinfo=seqinfSY,nanoT=nanoTsc99))
cov2exp <- coverage(SYrep3.gr,weight=SYrep3.gr$nanoT)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_SYSY_rep3.bw")

nanoT_SY <- bind_rows(SY_rep1,SY_rep2,SY_rep3)
nanoT_SY.gr <- with(nanoT_SY,GRanges(seqnames=chromSY,ranges=IRanges(start=positions,end=positions+999),strand="*",seqinfo=seqinfSY,nanoT=nanoTsc99))
cov2exp <- coverage(nanoT_SY.gr,weight=nanoT_SY.gr$nanoT/3)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_SYSY.bw")

nanoT_BY <- bind_rows(BY_rep1,BY_rep2,BY_rep3)
nanoT_BY.gr <- with(nanoT_BY,GRanges(seqnames=chromSY,ranges=IRanges(start=startnSY,end=endnSY),strand="*",seqinfo=seqinfSY,nanoT=nanoTsc99))
cov2exp <- coverage(nanoT_BY.gr,weight=nanoT_BY.gr$nanoT/3)
cov2exp[cov2exp<0.1] <- NA
export(cov2exp,con="BigWig/nanoT_BYBYSY.bw")
LS0tCnRpdGxlOiAiQllTWSBwcm9qZWN0IE5vdGVib29rIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tICAKIyAwMV9EYXRhIFRyYW5zcG9zaXRpb24KCioqKiAgCiMjIEdlbmVyYXRpbmcgdGhlIG51Y2xlb3RpZGUgdG8gbnVjbGVvdGlkZSBjb252ZXJzaW9uIHRhYmxlICAKT3JpZ2luYWwgZmFzdGEgZmlsZXMgZm9yIHRoZSBnZW5vbWVzIChTWTE0LmZhIGFuZCBCWTQ3NDIuZmEpIHdlcmUgZG93bmxvYWRlZCBmcm9tIHRoZSBhY2Nlc3Npb24gaWRlbnRpZmllciBbUFJKTkE0Mjk5ODVdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvYmlvcHJvamVjdC8/dGVybT1QUkpOQTQyOTk4NSkgCkNocm9tb3NvbWVzIGZyb20gdGhlIEJZIHN0cmFpbiB3ZXJlIHVzZWQgYSAicmVhZHMiIGFuZCBtYXBwZWQgb24gdGhlIFNZIGdlbm9tZSB1c2luZyBtaW5pbWFwMiAodmVyc2lvbj0yLjIyLXIxMTAxKSBhbmQgc2FtdG9vbHMgKHZlcnNpb249MS4xNyk6ICAKYGBge2Jhc2ggZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KbWluaW1hcDIgLWF4IGFzbTUgRGF0YV9yYXcvU1kxNC5mYSBEYXRhX3Jhdy9CWTQ3NDIuZmEgfCBzYW10b29scyBzb3J0IC1vIERhdGEvQllvblNZLnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggRGF0YS9CWW9uU1kuc29ydGVkLmJhbQpgYGAKVGhlbiB0aGUgY29udmVyc2lvbiB0YWJsZSB3YXMgYnVpbGQgaW4gUiBiYXNlZCBvbiB0aGUgQ0lHQVIgaW5mb3JtYXRpb24gZnJvbSB0aGUgcmVzdWx0aW5nIGJhbS1maWxlLiAgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShydHJhY2tsYXllcikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoR2Vub21pY0FsaWdubWVudHMpKQpgJSslYCA8LSBwYXN0ZTAKYGBgCkZpcnN0IHR3byBTZXFJbmZvIG9iamVjdCB3ZXJlIGdlbmVyYXRlZDogIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzZXFpbmZTWSA8LSBTZXFpbmZvKHNlcW5hbWVzPWMoIkNQMDI5MTYwLjEiKSxzZXFsZW5ndGhzPWMoMTE4NDg4MDQpLGlzQ2lyY3VsYXI9RixnZW5vbWU9IlNZMTQiKQpzYXZlUkRTKHNlcWluZlNZLCBmaWxlPSJEYXRhL3NlcWluZlNZLnJkcyIpCkJZZ2Vub21lIDwtIEJpb3N0cmluZ3M6OnJlYWRETkFTdHJpbmdTZXQoIkRhdGFfcmF3L0JZNDc0Mi5mYSIpCm5hbWVzKEJZZ2Vub21lKSA8LSBzYXBwbHkobmFtZXMoQllnZW5vbWUpLCBmdW5jdGlvbih4KSBzdHJzcGxpdCh4LCIgIilbWzFdXVsxXSkKc2VxaW5mQlkgPC0gU2VxaW5mbyhzZXFuYW1lcz1uYW1lcyhCWWdlbm9tZSksIHNlcWxlbmd0aHM9bGVuZ3RocyhCWWdlbm9tZSksIGdlbm9tZT0iQlk0NzQyIiwgaXNDaXJjdWxhcj1jKHJlcChGLDE2KSxUKSkKc2F2ZVJEUyhzZXFpbmZCWSwgZmlsZT0iRGF0YS9zZXFpbmZCWS5yZHMiKQpgYGAKCldlIGJ1aWx0IHR3byBmdW5jdGlvbnMgdG8gc2hpZnQgdGhlIGNvb3JkaW5hdGVzIGFjY29yZGluZyB0aGUgdGhlIENJR0FSIGluZm9ybWF0aW9uOiAgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmNpZ2FyMmdlbm9tZSA8LSBmdW5jdGlvbih4KSB7CiMjIyB4IGlzIGEgY2hhciB2ZWN0b3IgZnJvbSBDSUdBUgojIyMgSSBhbmQgUyBzZXF1ZW5jZSBpbiByZWFkIGJ1dCBub3QgaW4gZ2Vub21lCgl4cmVzIDwtIHgKCXhyZXNbeCAlaW4lIGMoIkkiLCJTIiwiSCIpXSA8LSAwCgl4cmVzW3ggJWluJSBjKCJNIiwiRCIpXSA8LSAxCglyZXR1cm4oeHJlcykKCX0KCQpjaWdhcjJzaWduYWwgPC0gZnVuY3Rpb24oeCkgewojIyMgeCBpcyBhIGNoYXIgdmVjdG9yIGZyb20gQ0lHQVIKIyMjIEkgYW5kIFMgc2VxdWVuY2UgaW4gcmVhZCBidXQgbm90IGluIGdlbm9tZSBhbmQgRCBhcmUgbm90IGluIHRoZSByZWFkX3NpZ25hbAoJeHJlcyA8LSB4Cgl4cmVzW3ggJWluJSBjKCJEIildIDwtIDAKCXhyZXNbeCAlaW4lIGMoIk0iLCJJIiwiUyIsIkgiKV0gPC0gMQoJcmV0dXJuKHhyZXMpCgl9CmBgYApUaGVuIHRoZSBkYXRhIGZyb20gdGhlIGJhbS1maWxlIHdlcmUgcHJvY2Vzc2VkIHRvIGdlbmVyYXRlIGEgY29vcmRpbmF0ZSB0byBjb29yZGluYXRlIGNvbnZlcnNpb24gdGFibGUgKiptYXN0ZXJjb252dGFibGUqKiB0aGF0IGNhbiBjb252ZXJ0IGNvb3JkaW5hdGVzIGZyb20gQlkgdG8gU1kgYW5kIHJlY2lwcm9jYWxseS4gIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpiYW0uaW4gPC0gIkRhdGEvQllvblNZLnNvcnRlZC5iYW0iCgpkYXRhMCA8LSB0cnkoc3lzdGVtKHBhc3RlMCgic2FtdG9vbHMgdmlldyAtRiAyMDQ3ICIsYmFtLmluLCIgfCBjdXQgLWYgMSwyLDMsNCw2IiksaW50ZXJuPVQpKQojIGtlZXAgb25seSBwcmltYXJ5IGFuZCBzdXBwbGVtZW50YXJ5IG1hcHBpbmdzCmRhdGExIDwtIGxhcHBseShkYXRhMCwgZnVuY3Rpb24oeCkgCgl7CgkJaW5wdXQxIDwtIHN0cnNwbGl0KHgsIlx0IilbWzFdXQoJCWZsYWcgPC0gaW5wdXQxWzJdCgkJc3RhcnRCWVNZIDwtIGFzLm51bWVyaWMoaW5wdXQxWzRdKQoJCWNocm9tQlkgPC0gaW5wdXQxWzFdCgkJY2hyb21TWSA8LSBpbnB1dDFbM10KCQljaWdhciA8LSBHZW5vbWljQWxpZ25tZW50czo6Y2lnYXJUb1JsZUxpc3QoaW5wdXQxWzVdKVtbMV1dCgkJY2lnIDwtIGFzLnZlY3RvcihjaWdhcikKCQlCWXBvcyA8LSBjdW1zdW0oY2lnYXIyc2lnbmFsKGNpZykpCgkJU1lwb3MgPC0gc3RhcnRCWVNZLTErY3Vtc3VtKGFzLm51bWVyaWMoY2lnYXIyZ2Vub21lKGNpZykpKQoJCXRyYW5zdGliIDwtIHRpYmJsZShjaWcsY2hyb21CWSxCWXBvcyxjaHJvbVNZLFNZcG9zKQoJCWxlbmcgPC0gc3VtKGFzLm51bWVyaWMoY2lnYXIyZ2Vub21lKGNpZykpKQoJCWVuZEJZU1kgPC0gc3RhcnRCWVNZK2xlbmctMQoJCXJlcyA8LSB0aWJibGUoZmxhZyxjaHJvbUJZLGNocm9tU1ksc3RhcnRCWVNZLGVuZEJZU1ksbGVuZyxCWXBvcz1saXN0KEJZcG9zKSxTWXBvcz1saXN0KFNZcG9zKSxjaWc9bGlzdChjaWcpLGNpZ2FyPWxpc3QoaW5wdXQxWzVdKSxjb252dGFibGU9bGlzdCh0cmFuc3RpYikpCglyZXR1cm4ocmVzKQoJfSkKCQpkYXRhX291dDIgPC0gZG8uY2FsbChiaW5kX3Jvd3MsZGF0YTEpICU+JQojIGZpbHRlciBmb3IgdGhlIHByaW1hcnkgYWxpZ25lZCBhbmQgZm9yIGxvbmcgc3VwcGxlbWVudGFyeSBtYXBwaW5nIHRvIHJlY292ZXIgdGhlIGJpZyBjaHVuayBvZiBjaHJWSUlJIHRoYXQgbWluaW1hcDIgZ2VuZXJhdGVzIGEgc3VwcGxlbWVudGFyeSAoQ1VQMS0xIGxvY3VzKQoJIGZpbHRlcihmbGFnPT0wIHwgbGVuZz4xMDAwMCkgJT4lCgkgbXV0YXRlKGNocm9tQllyb209ZmFjdG9yKGNocm9tQlkpICU+JQpmY3RfcmVjb2RlKCJjaHJJIj0iQ1AwMjYzMDEuMSIsImNocklJIj0iQ1AwMjYyOTYuMSIsImNocklJSSI9IkNQMDI2Mjk3LjEiLCJjaHJJViI9IkNQMDI2Mjk4LjEiLCJjaHJWIj0iQ1AwMjYyOTkuMSIsImNoclZJIj0iQ1AwMjYzMDIuMSIsImNoclZJSSI9IkNQMDI2Mjk0LjEiLCJjaHJWSUlJIj0iQ1AwMjYyODcuMSIsImNocklYIj0iQ1AwMjYyOTUuMSIsImNoclgiPSJDUDAyNjI4OC4xIiwiY2hyWEkiPSJDUDAyNjI4OS4xIiwiY2hyWElJIj0iQ1AwMjYzMDAuMSIsImNoclhJSUkiPSJDUDAyNjI5MS4xIiwiY2hyWElWIj0iQ1AwMjYyOTMuMSIsImNoclhWIj0iQ1AwMjYzMDMuMSIsImNoclhWSSI9IkNQMDI2MjkwLjEiKSkgJT4lCglzZWxlY3QoY2hyb21CWSxjaHJvbUJZcm9tLGNocm9tU1ksc3RhcnRCWVNZLGVuZEJZU1ksY29udnRhYmxlKQoKZGF0YTQgPC0gZGF0YV9vdXQyICU+JSAKCXNlbGVjdChjaHJvbUJZcm9tLGNvbnZ0YWJsZSkgJT4lCgl1bm5lc3QoY29sPWMoY29udnRhYmxlKSkgJT4lCglmaWx0ZXIoY2lnPT0iTSIpICU+JQoJZmlsdGVyKCFkdXBsaWNhdGVkKFNZcG9zLGZyb21MYXN0PVQpKSAlPiUKCWdyb3VwX2J5KGNocm9tU1ksY2hyb21CWSxjaHJvbUJZcm9tKSAlPiUKCXN1bW1hcmlzZShzdGFydFNZPW1pbihTWXBvcyksZW5kU1k9bWF4KFNZcG9zKSxzdGFydEJZPW1pbihCWXBvcyksZW5kQlk9bWF4KEJZcG9zKSkgJT4lCgl1bmdyb3VwICU+JQoJYXJyYW5nZShzdGFydFNZKQoKbWFzdGVyY29udnRhYmxlIDwtIGRhdGFfb3V0MiAlPiUgCglzZWxlY3QoY2hyb21CWXJvbSxjb252dGFibGUpICU+JQoJdW5uZXN0KGNvbD1jKGNvbnZ0YWJsZSkpICU+JQoJZmlsdGVyKGNpZz09Ik0iKSAlPiUKCWZpbHRlcighZHVwbGljYXRlZChTWXBvcyxmcm9tTGFzdD1UKSkgJT4lCglzZWxlY3QoLWNpZykKCmNocm9tQllvblNZZ3IgPC0gd2l0aChkYXRhNCxHUmFuZ2VzKHNlcW5hbWVzPWNocm9tU1kscmFuZ2U9SVJhbmdlcyhzdGFydFNZLGVuZFNZKSxzdHJhbmQ9IioiLG5hbWU9Y2hyb21CWXJvbSxzZXFpbmZvPXNlcWluZlNZKSkKCmNocm9tU1lvbkJZZ3IgPC0gd2l0aChkYXRhNCxHUmFuZ2VzKHNlcW5hbWVzPWNocm9tQlkscmFuZ2U9SVJhbmdlcyhzdGFydEJZLGVuZEJZKSxzdHJhbmQ9IioiLG5hbWU9Y2hyb21TWSxzZXFpbmZvPXNlcWluZkJZKSkKZXhwb3J0KGNocm9tQllvblNZZ3IsY29uPSJHZW5vbWVfYW5ub3RhdGlvbnMvY2hyb21CWW9uU1lnci5iZWQiKQpleHBvcnQoY2hyb21TWW9uQllncixjb249Ikdlbm9tZV9hbm5vdGF0aW9ucy9jaHJvbVNZb25CWWdyLmJlZCIpCndyaXRlX3RzdihtYXN0ZXJjb252dGFibGUsZmlsZT0iQmlnRmlsZXMvRGF0YS9CWTJTWWNvbi50c3YuZ3oiKQpgYGAKQXMgY29udHJvbCwgZ2Vub21pYyBjb29yZGluYXRlcyBmcm9tIHRoZSAxNiBCWSBjaHJvbW9zb21lcyB3ZXJlICJ0cmFuc3Bvc2VkIiB0aGlzIHdheS4gIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjaHJvbUJZIDwtIGFzKHNlcWluZkJZLCJHUmFuZ2VzIikKCmNocm9tQlkyIDwtIGNocm9tQlkgJT4lIGFzX3RpYmJsZSAlPiUgbXV0YXRlKGNvb3JkPW1hcDIoc3RhcnQsZW5kLCBmdW5jdGlvbih4LHkpIHg6eSkpICU+JSB1bm5lc3QoY29scz1jKGNvb3JkKSkgJT4lIHNlbGVjdChjaHJvbUJZPXNlcW5hbWVzLEJZcG9zPWNvb3JkKQpjaHJvbUJZMlNZIDwtIGxlZnRfam9pbihtYXN0ZXJjb252dGFibGUsY2hyb21CWTIpICU+JSAKCWdyb3VwX2J5KGNocm9tU1ksY2hyb21CWSxjaHJvbUJZcm9tKSAlPiUKCXN1bW1hcmlzZShzdGFydFNZPW1pbihTWXBvcyksZW5kU1k9bWF4KFNZcG9zKSxzdGFydEJZPW1pbihCWXBvcyksZW5kQlk9bWF4KEJZcG9zKSkgJT4lCgl1bmdyb3VwICU+JQoJYXJyYW5nZShzdGFydFNZKQoKY2hyb21CWTJTWWdyIDwtIHdpdGgoY2hyb21CWTJTWSxHUmFuZ2VzKHNlcW5hbWVzPWNocm9tU1kscmFuZ2U9SVJhbmdlcyhzdGFydFNZLGVuZFNZKSxzdHJhbmQ9IioiLG5hbWU9Y2hyb21CWXJvbSxzZXFpbmZvPXNlcWluZlNZKSkKCmNocm9tQlkyU1kyIDwtIGxlZnRfam9pbihtYXN0ZXJjb252dGFibGUsY2hyb21CWTIpCmNocm9tQlkyU1kzIDwtIHNwbGl0KGNocm9tQlkyU1kyLGNocm9tQlkyU1kyJGNocm9tQllyb20pCgpjaHJvbUJZMlNZM2dybCA8LSBsYXBwbHkoc2VxX2Fsb25nKGNocm9tQlkyU1kzKSwgZnVuY3Rpb24oaSkKCXsKCQl4IDwtIGNocm9tQlkyU1kzW1tpXV0KCQlyZXMgPC0gd2l0aCh4LEdSYW5nZXMoc2VxbmFtZXM9Y2hyb21TWSxyYW5nZXM9SVJhbmdlcyhzdGFydD1TWXBvcyx3aWR0aD0xKSxzdHJhbmQ9IioiLHNlcWluZm89c2VxaW5mU1ksbmFtZT1jaHJvbUJZcm9tKSkgJT4lIEdlbm9taWNSYW5nZXM6OnJlZHVjZSgpCgkJcmVzJG5hbWUgPC0gbmFtZXMoY2hyb21CWTJTWTMpW1tpXV0KCQlyZXR1cm4ocmVzKQoJCX0pCmNocm9tQlkyU1kzZ3IgPC0gZG8uY2FsbChjLGNocm9tQlkyU1kzZ3JsKQojIyBjb3JyZXNwb25kIHRvIGFsbCB0aGUgcGFydGlhbCBtYXRjaGVkIHNlZ21lbnQKCmV4cG9ydChjaHJvbUJZMlNZZ3IsY29uPSJHZW5vbWVfYW5ub3RhdGlvbnMvY2hyb21CWTJTWWdyLmJlZCIpCmV4cG9ydChjaHJvbUJZMlNZM2dyLGNvbj0iR2Vub21lX2Fubm90YXRpb25zL2Nocm9tQlkyU1kzZ3IuYmVkIikKCmlkZW50aWNhbChjaHJvbUJZMlNZZ3IsY2hyb21CWW9uU1lncikKI1sxXSBUUlVFCmBgYAoKVGhlICpjaHJvbUJZMlNZM2dyLmJlZCogaWxsdXN0cmF0ZXMgdGhhdCB0aGlzIGNvbnZlcnNpb24gaXMgbm90IGFsd2F5cyBhIHBlcmZlY3QgbWF0Y2ggYW5kIG1pZ2h0IGNvbmR1Y3QgdG8gc29tZSBpbmZvcm1hdGlvbiBsb3N0IGJ1dCBpdCBhcHBlYXJzIHJlYWxseSBhY2NlcHRhYmxlIGZvciBvdXIgcHVycG9zZS4gIAoKIyMgQ29udmVydGluZyBORlMgZm9ya3MgZnJvbSBCWSB0byBTWSAgCgpGaXJzdCB3ZSBuZWVkIHRvIGxvYWQgYWxsIHRoZSBmb3JrcyBjb29yZGluYXRlcyBmcm9tIHRoZSBCWSBleHBlcmltZW50CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShydHJhY2tsYXllcikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQpgJSslYCA8LSBwYXN0ZTAKCmZvcmtzX0JZQlkgPC0gcmVhZFJEUygiRGF0YV9yYXcvZm9ya3NfQllCWS5yZHMiKQpzYXZlUkRTKGZvcmtzX0JZQlksZmlsZT0iRGF0YS9mb3Jrc19CWUJZLnJkcyIpCmBgYApUaGVuIHdlIGltcG9ydCB0aGUgY29udmVyc2lvbiB0YWJsZSBpZiBub3QgaW4gbWVtb3J5CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkJZMlNZIDwtIHJlYWRfdHN2KCJCaWdGaWxlcy9EYXRhL0JZMlNZY29uLnRzdi5neiIsc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKYGBgClRoZW4gd2UgYXNzaWduIGF0IGVhY2ggZm9yayBhIHVuaXF1ZSBpZGVudGlmaWVyIChmaWQpIGFuZCBwcm9jZWVkIHRvIHRoZSBjb252ZXJzaW9uCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZvcmtzX0JZQlkkZmlkIDwtIDE6bnJvdyhmb3Jrc19CWUJZKQpmb3Jrc0JZIDwtIGZvcmtzX0JZQlkgJT4lIG11dGF0ZShCWXBvcz1tYXAyKFgwLFgxLGZ1bmN0aW9uKHgseSkgeDp5KSkgJT4lIHNlbGVjdChjaHJvbUJZPWNocm9tLGRpcmVjLFgwLFgxLEJZcG9zLGZpZCxzcGVlZCxyZWFkX2lkKQoKZm9ya3NCWWxpc3QgPC0gc3BsaXQoZm9ya3NCWSxmb3Jrc0JZJGNocm9tQlkpCgpmb3Jrc0JZMlNZbGlzdCA8LSBsYXBwbHkoZm9ya3NCWWxpc3QsIGZ1bmN0aW9uKHgpCnsKcmVzIDwtIGlubmVyX2pvaW4odW5uZXN0KHgsY29scz1jKEJZcG9zKSksQlkyU1ksYnkgPSBjKCJjaHJvbUJZIiwgIkJZcG9zIikpICU+JQoJZ3JvdXBfYnkoZmlkLGNocm9tQlksZGlyZWMpICU+JQoJbmVzdCgpICU+JQoJdW5ncm91cCAlPiUKCW11dGF0ZShYMG49bWFwX2ludChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LDEpICU+JSBwdWxsKEJZcG9zKSkpICU+JQoJbXV0YXRlKFgxbj1tYXBfZGJsKGRhdGEsZnVuY3Rpb24oeCkgZHBseXI6OnNsaWNlKHgsbnJvdyh4KSkgJT4lIHB1bGwoQllwb3MpKSkgJT4lCgltdXRhdGUoWDBuU1k9bWFwX2ludChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LDEpICU+JSBwdWxsKFNZcG9zKSkpICU+JQoJbXV0YXRlKFgxblNZPW1hcF9kYmwoZGF0YSxmdW5jdGlvbih4KSBkcGx5cjo6c2xpY2UoeCxucm93KHgpKSAlPiUgcHVsbChTWXBvcykpKSAlPiUKCXNlbGVjdCgtZGF0YSkKCXJldHVybihyZXMpCn0pCmZvcmtzQlkyU1kgPC0gZG8uY2FsbChiaW5kX3Jvd3MsZm9ya3NCWTJTWWxpc3QpCgpmb3Jrc0JZMlNZMiA8LSBpbm5lcl9qb2luKGZvcmtzQlkyU1ksZm9ya3NfQllCWSxieSA9IGpvaW5fYnkoZGlyZWMsIGZpZCkpICU+JSBtdXRhdGUoY2hyb21TWT0iQ1AwMjkxNjAuMSIpCnNhdmVSRFMoZm9ya3NCWTJTWTIsZmlsZT0iRGF0YS9mb3Jrc19CWUJZU1kucmRzIikKCmZvcmtzX1NZU1kgPC0gcmVhZFJEUygiRGF0YV9yYXcvZm9ya3NfU1lTWS5yZHMiKQpzYXZlUkRTKGZvcmtzX1NZU1ksZmlsZT0iRGF0YS9mb3Jrc19TWVNZLnJkcyIpCmBgYApDb252ZXJzaW9uIGNvdWxkIHJlc3VsdCBpbiBjaGFuZ2Ugb2YgdGhlIHdpZHRoIG9mIHRoZSBvYmplY3QsIHRoYXQgY291bGQgYWZmZWN0IHRoZSBuZXcgc3BlZWQgZGV0ZXJtaW5hdGlvbi4gVGhlcmVmb3JlLCB0aGUgc3BlZWQgbWVhc3VyZWQgb24gdGhlIG1hcHBpbmcgZ2Vub21lIHdhcyBrZXB0IGR1cmluZyB0aGUgZ2Vub21pYyBjb29yZGluYXRlcyBjb252ZXJzaW9uIHByb2Nlc3MuCkFzIGNvbnRyb2wsIFNZIGRhdGEgY2FuIGJlIHRyYW5zcG9zZWQgdG8gQlkgYW5kIHRoZW4gYmFjayB0byBTWSBhbmQgQlkgZGF0YSBjYW4gYmUgdHJhbnNwb3NlZCB0byBTWSBhbmQgdGhlbiBiYWNrIHRvIEJZLiAgCgojIyBDb252ZXJ0aW5nIGluaXRpYXRpb25zIGFuZCB0ZXJtaW5hdGlvbnMgZnJvbSBCWSB0byBTWSAgCgpTaW1pbGFybHksIEluaXRpYXRpb25zIGFuZCB0ZXJtaW5hdGlvbnMgY2FuIGJlIHRyYW5zcG9zZWQ6ICAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KaW5pdGVyX0JZQlkgPC0gcmVhZFJEUygiRGF0YV9yYXcvaW5pdGVyX0JZQlkucmRzIikKc2F2ZVJEUyhpbml0ZXJfQllCWSxmaWxlPSJEYXRhL2luaXRlcl9CWUJZLnJkcyIpCgppbml0ZXJfQllCWSRmaWQgPC0gMTpucm93KGluaXRlcl9CWUJZKQoKaW5pdGVyQlkgPC0gaW5pdGVyX0JZQlkgJT4lIAoJbXV0YXRlKEJZcG9zPW1hcDIoeDAseDEsZnVuY3Rpb24oeCx5KSB4OnkpKSAlPiUgCgltdXRhdGUod2lkPXgxLXgwKzEpICU+JQoJc2VsZWN0KGNocm9tQlk9Y2hyb20seDAseDEsQllwb3MsZmlkLHR5cGUsd2lkKQoKaW5pdGVyQllsaXN0IDwtIHNwbGl0KGluaXRlckJZLGluaXRlckJZJGNocm9tQlkpCgppbml0ZXJCWTJTWWxpc3QgPC0gbGFwcGx5KGluaXRlckJZbGlzdCwgZnVuY3Rpb24oeCkKewpyZXMgPC0gaW5uZXJfam9pbih1bm5lc3QoeCxjb2xzPWMoQllwb3MpKSxCWTJTWSxieSA9IGMoImNocm9tQlkiLCAiQllwb3MiKSkgJT4lCglncm91cF9ieShmaWQsY2hyb21CWSx0eXBlLHdpZCkgJT4lCgluZXN0KCkgJT4lCgl1bmdyb3VwICU+JQoJbXV0YXRlKHgwbj1tYXBfaW50KGRhdGEsZnVuY3Rpb24oeSkgZHBseXI6OnNsaWNlKHksMSkgJT4lIHB1bGwoQllwb3MpKSkgJT4lCgltdXRhdGUoeDFuPW1hcF9kYmwoZGF0YSxmdW5jdGlvbih5KSBkcGx5cjo6c2xpY2UoeSxucm93KHkpKSAlPiUgcHVsbChCWXBvcykpKSAlPiUKCW11dGF0ZSh4MG5TWT1tYXBfaW50KGRhdGEsZnVuY3Rpb24oeSkgZHBseXI6OnNsaWNlKHksMSkgJT4lIHB1bGwoU1lwb3MpKSkgJT4lCgltdXRhdGUoeDFuU1k9bWFwX2RibChkYXRhLGZ1bmN0aW9uKHkpIGRwbHlyOjpzbGljZSh5LG5yb3coeSkpICU+JSBwdWxsKFNZcG9zKSkpICU+JQoJc2VsZWN0KC1kYXRhKQoJcmV0dXJuKHJlcykKfSkKaW5pdGVyQlkyU1kgPC0gZG8uY2FsbChiaW5kX3Jvd3MsaW5pdGVyQlkyU1lsaXN0KQoKaW5pdGVyQlkyU1kyIDwtIGlubmVyX2pvaW4oaW5pdGVyQlkyU1ksaW5pdGVyX0JZQlksYnkgPSBqb2luX2J5KGZpZCwgdHlwZSkpICU+JSBtdXRhdGUoY2hyb21TWT0iQ1AwMjkxNjAuMSIpICU+JSBtdXRhdGUoY2VudGVyU1k9Zmxvb3IoKHgwblNZK3gxblNZKS8yKSkKIyBXQVJOSU5HLCB0aGUgbmV3ICJjZW50ZXIiIG1pZ2h0IGJ5IHdyb25nIGZvciBwYXJ0aWFsbHkgZGVsZXRlZCBzZWdtZW50IGJ1dCBpdCB3YXMgdGhlIG9ubHkgd2F5IHRvIGRlYWwgd2l0aCBwYXJ0aWFsbHkgZGVsZXRlZCBzZWdtZW50IHRoYXQgbG9vc2UgdGhlIGNlbnRlciEKCnNhdmVSRFMoaW5pdGVyQlkyU1kyLGZpbGU9IkRhdGEvaW5pdGVyX0JZQllTWS5yZHMiKQoKaW5pdGVyX1NZU1kgPC0gcmVhZFJEUygiRGF0YV9yYXcvaW5pdGVyX1NZU1kucmRzIikKc2F2ZVJEUyhpbml0ZXJfU1lTWSxmaWxlPSJEYXRhL2luaXRlcl9TWVNZLnJkcyIpCmBgYAoKU2ltaWxhcmx5IHRvIHRoZSBmb3JrcyB0cmFuc3Bvc2l0aW9uIHByb2Nlc3MsIHdpZHRoIG9mIGluaXRpYXRpb24gb3IgdGVybWluYXRpb24gc2VnbWVudCBjb3VsZCBhbHNvIGJlIGNoYW5nZWQgZHVyaW5nIHRoZSBjb252ZXJzaW9uIHByb2Nlc3MsIGFmZmVjdGluZyB0aGUgaW5pdGlhdGlvbiAob3IgdGVybWluYXRpb24pIHByb2JhYmlsaXR5IGFmZmVjdGVkIHRvIHRoZSBzZWdtZW50LiBUaGVyZWZvcmUsIHRoZSB3aWR0aCBmcm9tIHRoZSBvcmlnaW5hbCBtYXBwaW5nIHdhcyBhbHNvIGtlcHQgaW4gdGhlIGNvbnZlcnNpb24gcHJvY2Vzcy4gVW5mb3J0dW5hdGVseSwgaXQgd2FzIG5vdCBwb3NzaWJsZSB0byB0cmFuc2ZlciB1bmFtYmlndW91c2x5IHRoZSBjZW50ZXIgb2YgdGhlIGluaXRpYXRpb24gYW5kIHRlcm1pbmF0aW9uIHNlZ21lbnQgYXMgaXRzIHByZWNpc2UgY29vcmRpbmF0ZSBkaWQgb2Z0ZW4gZmFsbCB3aXRoaW4gbWljcm9kZWxldGlvbiBwcmVzZW50IGluIHRoZSBDSUdBUiBjb2RlIHVzZWQgdG8gYnVpbGQgdGhlIGNvbnZlcnNpb24gdGFibGUuICAKCiMjIENvbnZlcnRpbmcgbmFub3RpbWluZyBkYXRhIGZyb20gQlkgdG8gU1kgIAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc291cmNlKCJIZWxwZXJfZnVuY3Rpb24uciIpCgpCWW5hbm9fcmVwMSA8LSByZWFkUkRTKCJEYXRhX3Jhdy9uYW5vVF9CWV9yZXAxLnJkcyIpICU+JSAKCW11dGF0ZShuYW5vVHNjOTk9MStteXNjYWxpbmcwKG1lYW5fYnJfYmluLGluZnE9MC4wMDUsc3VwcT0wLjk5NSkpCkJZbmFub19yZXAxJGZpZCA8LSAxOm5yb3coQlluYW5vX3JlcDEpCkJZbmFub0JZMiA8LSBCWW5hbm9fcmVwMSAlPiUgbXV0YXRlKEJZcG9zPW1hcDIocG9zaXRpb25zLHBvc2l0aW9ucys5OTksZnVuY3Rpb24oeCx5KSB4OnkpKSAlPiUgc2VsZWN0KGNocm9tQlk9Y2hyb20scG9zaXRpb25zLEJZcG9zLGZpZCxtZWFuX2JyX2JpbikKcmVzIDwtIGlubmVyX2pvaW4odW5uZXN0KEJZbmFub0JZMixjb2xzPWMoQllwb3MpKSxCWTJTWSxieSA9IGMoImNocm9tQlkiLCAiQllwb3MiKSkgJT4lCglncm91cF9ieShmaWQsY2hyb21CWSkgJT4lCgluZXN0KCkgJT4lCgl1bmdyb3VwICU+JQoJbXV0YXRlKHN0YXJ0bj1tYXBfZGJsKGRhdGEsZnVuY3Rpb24oeCkgZHBseXI6OnNsaWNlKHgsMSkgJT4lIHB1bGwoQllwb3MpKSkgJT4lCgltdXRhdGUoZW5kbj1tYXBfZGJsKGRhdGEsZnVuY3Rpb24oeCkgZHBseXI6OnNsaWNlKHgsbnJvdyh4KSkgJT4lIHB1bGwoQllwb3MpKSkgJT4lCgltdXRhdGUoc3RhcnRuU1k9bWFwX2RibChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LDEpICU+JSBwdWxsKFNZcG9zKSkpICU+JQoJbXV0YXRlKGVuZG5TWT1tYXBfZGJsKGRhdGEsZnVuY3Rpb24oeCkgZHBseXI6OnNsaWNlKHgsbnJvdyh4KSkgJT4lIHB1bGwoU1lwb3MpKSkgJT4lCglzZWxlY3QoLWRhdGEpCkJZbmFub19yZXAxU1kgPC0gaW5uZXJfam9pbihCWW5hbm9fcmVwMSxyZXMsYnkgPSBqb2luX2J5KGZpZCkpICU+JQoJbXV0YXRlKGNocm9tU1k9IkNQMDI5MTYwLjEiKQpCWV9yZXAxIDwtIEJZbmFub19yZXAxU1kgJT4lIAoJbXV0YXRlKFJlcD0icmVwMSIpICU+JQoJbXV0YXRlKHN0cmFpbj0iQlkiKQpzYXZlUkRTKEJZX3JlcDEsZmlsZT0iRGF0YS9uYW5vVF9CWUJZU1lfcmVwMS5yZHMiKQpCWXJlcDEuZ3IgPC0gd2l0aChCWV9yZXAxLEdSYW5nZXMoc2VxbmFtZXM9Y2hyb21TWSxyYW5nZXM9SVJhbmdlcyhzdGFydD1zdGFydG5TWSxlbmQ9ZW5kblNZKSxzdHJhbmQ9IioiLHNlcWluZm89c2VxaW5mU1ksbmFub1Q9bmFub1RzYzk5KSkKY292MmV4cCA8LSBjb3ZlcmFnZShCWXJlcDEuZ3Isd2VpZ2h0PUJZcmVwMS5nciRuYW5vVCkKY292MmV4cFtjb3YyZXhwPDAuMV0gPC0gTkEKZXhwb3J0KGNvdjJleHAsY29uPSJCaWdXaWcvbmFub1RfQllCWVNZX3JlcDEuYnciKQpCWXJlcDEuZ3JCWSA8LSB3aXRoKEJZbmFub19yZXAxLHN1cHByZXNzV2FybmluZ3MoR1JhbmdlcyhzZXFuYW1lcz1jaHJvbSxyYW5nZXM9SVJhbmdlcyhzdGFydD1wb3NpdGlvbnMsZW5kPXBvc2l0aW9ucys5OTkpLHN0cmFuZD0iKiIsc2VxaW5mbz1zZXFpbmZCWSxuYW5vVD1uYW5vVHNjOTkpKSkKY292MmV4cCA8LSBjb3ZlcmFnZShCWXJlcDEuZ3JCWSx3ZWlnaHQ9QllyZXAxLmdyQlkkbmFub1QpCmNvdjJleHBbY292MmV4cDwwLjFdIDwtIE5BCmV4cG9ydChjb3YyZXhwLGNvbj0iQmlnV2lnL25hbm9UX0JZQllfcmVwMS5idyIpCgpCWW5hbm9fcmVwMiA8LSByZWFkUkRTKCJEYXRhX3Jhdy9uYW5vVF9CWV9yZXAyLnJkcyIpICU+JQoJbXV0YXRlKG5hbm9Uc2M5OT0xK215c2NhbGluZzAobWVhbl9icl9iaW4saW5mcT0wLjAwNSxzdXBxPTAuOTk1KSkKQlluYW5vX3JlcDIkZmlkIDwtIDE6bnJvdyhCWW5hbm9fcmVwMikKQlluYW5vQlkyIDwtIEJZbmFub19yZXAyICU+JSBtdXRhdGUoQllwb3M9bWFwMihwb3NpdGlvbnMscG9zaXRpb25zKzk5OSxmdW5jdGlvbih4LHkpIHg6eSkpICU+JSBzZWxlY3QoY2hyb21CWT1jaHJvbSxwb3NpdGlvbnMsQllwb3MsZmlkLG1lYW5fYnJfYmluKQpyZXMgPC0gaW5uZXJfam9pbih1bm5lc3QoQlluYW5vQlkyLGNvbHM9YyhCWXBvcykpLEJZMlNZLGJ5ID0gYygiY2hyb21CWSIsICJCWXBvcyIpKSAlPiUKCWdyb3VwX2J5KGZpZCxjaHJvbUJZKSAlPiUKCW5lc3QoKSAlPiUKCXVuZ3JvdXAgJT4lCgltdXRhdGUoc3RhcnRuPW1hcF9kYmwoZGF0YSxmdW5jdGlvbih4KSBkcGx5cjo6c2xpY2UoeCwxKSAlPiUgcHVsbChCWXBvcykpKSAlPiUKCW11dGF0ZShlbmRuPW1hcF9kYmwoZGF0YSxmdW5jdGlvbih4KSBkcGx5cjo6c2xpY2UoeCxucm93KHgpKSAlPiUgcHVsbChCWXBvcykpKSAlPiUKCW11dGF0ZShzdGFydG5TWT1tYXBfZGJsKGRhdGEsZnVuY3Rpb24oeCkgZHBseXI6OnNsaWNlKHgsMSkgJT4lIHB1bGwoU1lwb3MpKSkgJT4lCgltdXRhdGUoZW5kblNZPW1hcF9kYmwoZGF0YSxmdW5jdGlvbih4KSBkcGx5cjo6c2xpY2UoeCxucm93KHgpKSAlPiUgcHVsbChTWXBvcykpKSAlPiUKCXNlbGVjdCgtZGF0YSkKQlluYW5vX3JlcDJTWSA8LSBpbm5lcl9qb2luKEJZbmFub19yZXAyLHJlcyxieSA9IGpvaW5fYnkoZmlkKSkgJT4lCgltdXRhdGUoY2hyb21TWT0iQ1AwMjkxNjAuMSIpIApCWV9yZXAyIDwtIEJZbmFub19yZXAyU1kgICU+JSAKCW11dGF0ZShSZXA9InJlcDIiKSAlPiUKCW11dGF0ZShzdHJhaW49IkJZIikKc2F2ZVJEUyhCWV9yZXAyLGZpbGU9IkRhdGEvbmFub1RfQllCWVNZX3JlcDIucmRzIikKQllyZXAyLmdyIDwtIHdpdGgoQllfcmVwMixHUmFuZ2VzKHNlcW5hbWVzPWNocm9tU1kscmFuZ2VzPUlSYW5nZXMoc3RhcnQ9c3RhcnRuU1ksZW5kPWVuZG5TWSksc3RyYW5kPSIqIixzZXFpbmZvPXNlcWluZlNZLG5hbm9UPW5hbm9Uc2M5OSkpCmNvdjJleHAgPC0gY292ZXJhZ2UoQllyZXAyLmdyLHdlaWdodD1CWXJlcDIuZ3IkbmFub1QpCmNvdjJleHBbY292MmV4cDwwLjFdIDwtIE5BCmV4cG9ydChjb3YyZXhwLGNvbj0iQmlnV2lnL25hbm9UX0JZQllTWV9yZXAyLmJ3IikKQllyZXAyLmdyQlkgPC0gd2l0aChCWW5hbm9fcmVwMixzdXBwcmVzc1dhcm5pbmdzKEdSYW5nZXMoc2VxbmFtZXM9Y2hyb20scmFuZ2VzPUlSYW5nZXMoc3RhcnQ9cG9zaXRpb25zLGVuZD1wb3NpdGlvbnMrOTk5KSxzdHJhbmQ9IioiLHNlcWluZm89c2VxaW5mQlksbmFub1Q9bmFub1RzYzk5KSkpCmNvdjJleHAgPC0gY292ZXJhZ2UoQllyZXAyLmdyQlksd2VpZ2h0PUJZcmVwMi5nckJZJG5hbm9UKQpjb3YyZXhwW2NvdjJleHA8MC4xXSA8LSBOQQpleHBvcnQoY292MmV4cCxjb249IkJpZ1dpZy9uYW5vVF9CWUJZX3JlcDIuYnciKQoKQlluYW5vX3JlcDMgPC0gcmVhZFJEUygiRGF0YV9yYXcvbmFub1RfQllfcmVwMy5yZHMiKSAlPiUgCgltdXRhdGUobmFub1RzYzk5PTErbXlzY2FsaW5nMChtZWFuX2JyX2JpbixpbmZxPTAuMDA1LHN1cHE9MC45OTUpKQpCWW5hbm9fcmVwMyRmaWQgPC0gMTpucm93KEJZbmFub19yZXAzKQpCWW5hbm9CWTIgPC0gQlluYW5vX3JlcDMgJT4lIG11dGF0ZShCWXBvcz1tYXAyKHBvc2l0aW9ucyxwb3NpdGlvbnMrOTk5LGZ1bmN0aW9uKHgseSkgeDp5KSkgJT4lIHNlbGVjdChjaHJvbUJZPWNocm9tLHBvc2l0aW9ucyxCWXBvcyxmaWQsbWVhbl9icl9iaW4pCnJlcyA8LSBpbm5lcl9qb2luKHVubmVzdChCWW5hbm9CWTIsY29scz1jKEJZcG9zKSksQlkyU1ksYnkgPSBjKCJjaHJvbUJZIiwgIkJZcG9zIikpICU+JQoJZ3JvdXBfYnkoZmlkLGNocm9tQlkpICU+JQoJbmVzdCgpICU+JQoJdW5ncm91cCAlPiUKCW11dGF0ZShzdGFydG49bWFwX2RibChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LDEpICU+JSBwdWxsKEJZcG9zKSkpICU+JQoJbXV0YXRlKGVuZG49bWFwX2RibChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LG5yb3coeCkpICU+JSBwdWxsKEJZcG9zKSkpICU+JQoJbXV0YXRlKHN0YXJ0blNZPW1hcF9kYmwoZGF0YSxmdW5jdGlvbih4KSBkcGx5cjo6c2xpY2UoeCwxKSAlPiUgcHVsbChTWXBvcykpKSAlPiUKCW11dGF0ZShlbmRuU1k9bWFwX2RibChkYXRhLGZ1bmN0aW9uKHgpIGRwbHlyOjpzbGljZSh4LG5yb3coeCkpICU+JSBwdWxsKFNZcG9zKSkpICU+JQoJc2VsZWN0KC1kYXRhKQpCWW5hbm9fcmVwM1NZIDwtIGlubmVyX2pvaW4oQlluYW5vX3JlcDMscmVzLGJ5ID0gam9pbl9ieShmaWQpKSAlPiUKCW11dGF0ZShjaHJvbVNZPSJDUDAyOTE2MC4xIikKQllfcmVwMyA8LSBCWW5hbm9fcmVwM1NZICU+JSAKCW11dGF0ZShSZXA9InJlcDMiKSAlPiUKCW11dGF0ZShzdHJhaW49IkJZIikKc2F2ZVJEUyhCWV9yZXAzLGZpbGU9IkRhdGEvbmFub1RfQllCWVNZX3JlcDMucmRzIikKQllyZXAzLmdyIDwtIHdpdGgoQllfcmVwMyxHUmFuZ2VzKHNlcW5hbWVzPWNocm9tU1kscmFuZ2VzPUlSYW5nZXMoc3RhcnQ9c3RhcnRuU1ksZW5kPWVuZG5TWSksc3RyYW5kPSIqIixzZXFpbmZvPXNlcWluZlNZLG5hbm9UPW5hbm9Uc2M5OSkpCmNvdjJleHAgPC0gY292ZXJhZ2UoQllyZXAzLmdyLHdlaWdodD1CWXJlcDMuZ3IkbmFub1QpCmNvdjJleHBbY292MmV4cDwwLjFdIDwtIE5BCmV4cG9ydChjb3YyZXhwLGNvbj0iQmlnV2lnL25hbm9UX0JZQllTWV9yZXAzLmJ3IikKQllyZXAzLmdyQlkgPC0gd2l0aChCWW5hbm9fcmVwMyxzdXBwcmVzc1dhcm5pbmdzKEdSYW5nZXMoc2VxbmFtZXM9Y2hyb20scmFuZ2VzPUlSYW5nZXMoc3RhcnQ9cG9zaXRpb25zLGVuZD1wb3NpdGlvbnMrOTk5KSxzdHJhbmQ9IioiLHNlcWluZm89c2VxaW5mQlksbmFub1Q9bmFub1RzYzk5KSkpCmNvdjJleHAgPC0gY292ZXJhZ2UoQllyZXAzLmdyQlksd2VpZ2h0PUJZcmVwMy5nckJZJG5hbm9UKQpjb3YyZXhwW2NvdjJleHA8MC4xXSA8LSBOQQpleHBvcnQoY292MmV4cCxjb249IkJpZ1dpZy9uYW5vVF9CWUJZX3JlcDMuYnciKQoKU1luYW5vX3JlcDEgPC0gcmVhZFJEUygiRGF0YV9yYXcvbmFub1RfU1lfcmVwMS5yZHMiKSAlPiUKCXJlbmFtZShjaHJvbVNZPWNocm9tKQpTWV9yZXAxIDwtIFNZbmFub19yZXAxICU+JSAKCW11dGF0ZShuYW5vVHNjOTk9MStteXNjYWxpbmcwKG1lYW5fYnJfYmluLGluZnE9MC4wMDUsc3VwcT0wLjk5NSkpICU+JQoJbXV0YXRlKFJlcD0icmVwMSIpICU+JQoJbXV0YXRlKHN0cmFpbj0iU1kiKQpzYXZlUkRTKFNZX3JlcDEsZmlsZT0iRGF0YS9uYW5vVF9TWVNZX3JlcDEucmRzIikKU1lyZXAxLmdyIDwtIHdpdGgoU1lfcmVwMSxHUmFuZ2VzKHNlcW5hbWVzPWNocm9tU1kscmFuZ2VzPUlSYW5nZXMoc3RhcnQ9cG9zaXRpb25zLGVuZD1wb3NpdGlvbnMrOTk5KSxzdHJhbmQ9IioiLHNlcWluZm89c2VxaW5mU1ksbmFub1Q9bmFub1RzYzk5KSkKY292MmV4cCA8LSBjb3ZlcmFnZShTWXJlcDEuZ3Isd2VpZ2h0PVNZcmVwMS5nciRuYW5vVCkKY292MmV4cFtjb3YyZXhwPDAuMV0gPC0gTkEKZXhwb3J0KGNvdjJleHAsY29uPSJCaWdXaWcvbmFub1RfU1lTWV9yZXAxLmJ3IikKClNZbmFub19yZXAyIDwtIHJlYWRSRFMoIkRhdGFfcmF3L25hbm9UX1NZX3JlcDIucmRzIikgJT4lCglyZW5hbWUoY2hyb21TWT1jaHJvbSkKU1lfcmVwMiA8LSBTWW5hbm9fcmVwMiAlPiUgCgltdXRhdGUobmFub1RzYzk5PTErbXlzY2FsaW5nMChtZWFuX2JyX2JpbixpbmZxPTAuMDA1LHN1cHE9MC45OTUpKSAlPiUKCW11dGF0ZShSZXA9InJlcDIiKSAlPiUKCW11dGF0ZShzdHJhaW49IlNZIikKc2F2ZVJEUyhTWV9yZXAyLGZpbGU9IkRhdGEvbmFub1RfU1lTWV9yZXAyLnJkcyIpClNZcmVwMi5nciA8LSB3aXRoKFNZX3JlcDIsR1JhbmdlcyhzZXFuYW1lcz1jaHJvbVNZLHJhbmdlcz1JUmFuZ2VzKHN0YXJ0PXBvc2l0aW9ucyxlbmQ9cG9zaXRpb25zKzk5OSksc3RyYW5kPSIqIixzZXFpbmZvPXNlcWluZlNZLG5hbm9UPW5hbm9Uc2M5OSkpCmNvdjJleHAgPC0gY292ZXJhZ2UoU1lyZXAyLmdyLHdlaWdodD1TWXJlcDIuZ3IkbmFub1QpCmNvdjJleHBbY292MmV4cDwwLjFdIDwtIE5BCmV4cG9ydChjb3YyZXhwLGNvbj0iQmlnV2lnL25hbm9UX1NZU1lfcmVwMi5idyIpCgpTWW5hbm9fcmVwMyA8LSByZWFkUkRTKCJEYXRhX3Jhdy9uYW5vVF9TWV9yZXAzLnJkcyIpJT4lCglyZW5hbWUoY2hyb21TWT1jaHJvbSkKU1lfcmVwMyA8LSBTWW5hbm9fcmVwMyAlPiUgCgltdXRhdGUobmFub1RzYzk5PTErbXlzY2FsaW5nMChtZWFuX2JyX2JpbixpbmZxPTAuMDA1LHN1cHE9MC45OTUpKSAlPiUKCW11dGF0ZShSZXA9InJlcDMiKSAlPiUKCW11dGF0ZShzdHJhaW49IlNZIikKc2F2ZVJEUyhTWV9yZXAzLGZpbGU9IkRhdGEvbmFub1RfU1lTWV9yZXAzLnJkcyIpClNZcmVwMy5nciA8LSB3aXRoKFNZX3JlcDMsR1JhbmdlcyhzZXFuYW1lcz1jaHJvbVNZLHJhbmdlcz1JUmFuZ2VzKHN0YXJ0PXBvc2l0aW9ucyxlbmQ9cG9zaXRpb25zKzk5OSksc3RyYW5kPSIqIixzZXFpbmZvPXNlcWluZlNZLG5hbm9UPW5hbm9Uc2M5OSkpCmNvdjJleHAgPC0gY292ZXJhZ2UoU1lyZXAzLmdyLHdlaWdodD1TWXJlcDMuZ3IkbmFub1QpCmNvdjJleHBbY292MmV4cDwwLjFdIDwtIE5BCmV4cG9ydChjb3YyZXhwLGNvbj0iQmlnV2lnL25hbm9UX1NZU1lfcmVwMy5idyIpCgpuYW5vVF9TWSA8LSBiaW5kX3Jvd3MoU1lfcmVwMSxTWV9yZXAyLFNZX3JlcDMpCm5hbm9UX1NZLmdyIDwtIHdpdGgobmFub1RfU1ksR1JhbmdlcyhzZXFuYW1lcz1jaHJvbVNZLHJhbmdlcz1JUmFuZ2VzKHN0YXJ0PXBvc2l0aW9ucyxlbmQ9cG9zaXRpb25zKzk5OSksc3RyYW5kPSIqIixzZXFpbmZvPXNlcWluZlNZLG5hbm9UPW5hbm9Uc2M5OSkpCmNvdjJleHAgPC0gY292ZXJhZ2UobmFub1RfU1kuZ3Isd2VpZ2h0PW5hbm9UX1NZLmdyJG5hbm9ULzMpCmNvdjJleHBbY292MmV4cDwwLjFdIDwtIE5BCmV4cG9ydChjb3YyZXhwLGNvbj0iQmlnV2lnL25hbm9UX1NZU1kuYnciKQoKbmFub1RfQlkgPC0gYmluZF9yb3dzKEJZX3JlcDEsQllfcmVwMixCWV9yZXAzKQpuYW5vVF9CWS5nciA8LSB3aXRoKG5hbm9UX0JZLEdSYW5nZXMoc2VxbmFtZXM9Y2hyb21TWSxyYW5nZXM9SVJhbmdlcyhzdGFydD1zdGFydG5TWSxlbmQ9ZW5kblNZKSxzdHJhbmQ9IioiLHNlcWluZm89c2VxaW5mU1ksbmFub1Q9bmFub1RzYzk5KSkKY292MmV4cCA8LSBjb3ZlcmFnZShuYW5vVF9CWS5ncix3ZWlnaHQ9bmFub1RfQlkuZ3IkbmFub1QvMykKY292MmV4cFtjb3YyZXhwPDAuMV0gPC0gTkEKZXhwb3J0KGNvdjJleHAsY29uPSJCaWdXaWcvbmFub1RfQllCWVNZLmJ3IikKCmBgYAo=