11_Speed Map


To evaluate the differences in replication fork speed between the SY and BY strain, we proceed similarly as in Theulot et al, 2022.
Forks were reduced to their center. We choose to remove forks mapping on the problematic Ty loci from chrIII and chrXVI and the ura3∆0 locus. The SY genome was parted into non overlapping 1kb bins on which forks were affected with respect to their center.
When we computed the forks speed distribution for genomic bins above 1kb, we choose to proceed by merging consecutive 1kb bins, keeping a 1kb shift.
As an illustration, the bin 73 at the 3kb scale contains the forks mapping on the bins 72,73 and 74 from the 1kb scale. For all this procedure, data were pooled by strain.

suppressMessages(library(GenomicRanges))
suppressMessages(library(tidyverse))
suppressMessages(library(rtracklayer))
library(future.apply)
library(parallel)
library(furrr)
library(patchwork)
library(ggprism)
library(DescTools)
`%+%`<-paste0
seqinfSY <- readRDS("Data/seqinfSY.rds")
rDNA_SY <- GRanges("CP029160.1",IRanges(3879940,3934000),strand="*",seqinfo=seqinfSY)
maskedTy_SY <- readRDS("Data/maskedTy_SY.rds")
masked_ura3d0 <- GRanges("CP029160.1",IRanges(1051200,1051500),strand="*",seqinfo=seqinfSY,type="ura3d0")
masked_SY <- c(maskedTy_SY,masked_ura3d0) 

source("Helper_function.r")
theme_set(theme_bw())
mypal <- c(paletteer::paletteer_d("ggthemes::Classic_20"),"grey40","black")

Importing forks

forksBY <- readRDS("Data/forks_BYBYSY.rds")
forksSY <- readRDS("Data/forks_SYSY.rds")

fBY <- forksBY %>%
    select(chrom=chromSY,direc,X0=X0nSY,X1=X1nSY,exp,speed,type) %>%
    mutate(Rep=map_chr(exp,function(x) x %>% str_remove("BY_"))) %>%
    mutate(strain="BY")
fSY <- forksSY %>%
    select(chrom,direc,X0,X1,exp,speed,type) %>%
    mutate(Rep=map_chr(exp,function(x) x %>% str_remove("SY_"))) %>%
    mutate(strain="SY")


forkBY.gr<- with(fBY, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="L"~"+",T~"-"),seqinfo=seqinfSY,speed=speed,direc=direc,type=type,Rep=Rep))
## exclude TychrIII, TychrXVI and ura3d0 forks
forkBY.gr <- forkBY.gr[!overlapsAny(forkBY.gr,masked_SY)]
speedmedgenBY <- median(forkBY.gr$speed,na.rm=T)
# 2402
forkSY.gr<- with(fSY, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="L"~"+",T~"-"),seqinfo=seqinfSY,speed=speed,direc=direc,type=type,Rep=Rep))
forkSY.gr <- forkSY.gr[!overlapsAny(forkSY.gr,masked_SY)]
speedmedgenSY <- median(forkSY.gr$speed,na.rm=T)
# 2432
forkBY.grc <- resize(forkBY.gr,fix="center",width=1)
forkSY.grc <- resize(forkSY.gr,fix="center",width=1)

Affecting speeds to genomic bins

bs <- 1000 
bingen <- tileGenome(seqinfSY,tilewidth=bs, cut.last.tile.in.chrom=T)
nc <- 8L

ol1 <- findOverlaps(forkBY.grc,bingen)
bingen$BY1k <- mclapply(1:length(bingen), function(x) tibble(speed=forkBY.grc[queryHits(ol1)[subjectHits(ol1)==x]]$speed,direc=forkBY.grc[queryHits(ol1)[subjectHits(ol1)==x]]$direc,type=forkBY.grc[queryHits(ol1)[subjectHits(ol1)==x]]$type,Rep=forkBY.grc[queryHits(ol1)[subjectHits(ol1)==x]]$Rep),mc.cores=nc)

ol1 <- findOverlaps(forkSY.grc,bingen)
bingen$SY1k <- mclapply(1:length(bingen), function(x) tibble(speed=forkSY.grc[queryHits(ol1)[subjectHits(ol1)==x]]$speed,direc=forkSY.grc[queryHits(ol1)[subjectHits(ol1)==x]]$direc,type=forkSY.grc[queryHits(ol1)[subjectHits(ol1)==x]]$type,Rep=forkSY.grc[queryHits(ol1)[subjectHits(ol1)==x]]$Rep),mc.cores=nc)

Generating other scales binning

roll_c_mc <- function(x,win=3,nc=4L)
{
# x is a list
    require(future.apply)
    plan(multicore, workers = nc)

    res0 <- future_lapply(1:(length(x)-win+1), function(i) {
        purrr::reduce(x[i:(i+win-1)], dplyr::full_join, by = c("speed","direc","type","Rep"))
        })
    if (win%%2==0) 
    {res <- c(rep(list(NA),win%/%2-1),res0,rep(list(NA),win%/%2))
    }else{res <- c(rep(list(NA),win%/%2),res0,rep(list(NA),win%/%2))
    }
return(res)
}

bingen$BY3k <- roll_c_mc(bingen$BY1k,win=3)
bingen$SY3k <- roll_c_mc(bingen$SY1k,win=3)
#saveRDS(bingen,"centerfullpooledSpeed_" %+% data_suff %+% ".rds")
bingen$BY5k <- roll_c_mc(bingen$BY1k,win=5)
bingen$SY5k <- roll_c_mc(bingen$SY1k,win=5)
#saveRDS(bingen,"centerfullpooledSpeed_" %+% data_suff %+% ".rds")
bingen$BY11k <- roll_c_mc(bingen$BY1k,win=11)
bingen$SY11k <- roll_c_mc(bingen$SY1k,win=11)
#saveRDS(bingen,"centerfullpooledSpeed_" %+% data_suff %+% ".rds")
bingen$BY21k <- roll_c_mc(bingen$BY1k,win=21)
bingen$SY21k <- roll_c_mc(bingen$SY1k,win=21)
#saveRDS(bingen,"centerfullpooledSpeed_" %+% data_suff %+% ".rds")
bingen$BY41k <- roll_c_mc(bingen$BY1k,win=41)
bingen$SY41k <- roll_c_mc(bingen$SY1k,win=41)
saveRDS(bingen,"Data/centerfullpooledSpeed.rds")

Exporting 21k speed and coverage

bingen <- readRDS("Data/centerfullpooledSpeed.rds")
speedBY21k <- lapply(bingen$BY21k, function(x) {
    if (!is.na(x)[1])
    {res <- summarize(x,speedmed=MedianCI(speed)[1],speedmedCIinf=MedianCI(speed)[2],speedmedCIsup=MedianCI(speed)[3],n=n())} else {res <- tibble(speedmed=NA,speedmedCIinf=NA,speedmedCIsup=NA,n=NA)}
    return(res)
    }) 
speedBY21k2 <- do.call(bind_rows,speedBY21k)
speedSY21k <- lapply(bingen$SY21k, function(x) {
    if (!is.na(x)[1])
    {res <- summarize(x,speedmed=MedianCI(speed)[1],speedmedCIinf=MedianCI(speed)[2],speedmedCIsup=MedianCI(speed)[3],n=n())} else {res <- tibble(speedmed=NA,speedmedCIinf=NA,speedmedCIsup=NA,n=NA)}
    return(res)
    }) 
speedSY21k2 <- do.call(bind_rows,speedSY21k)

export(coverage(bingen,weight=speedBY21k2$n),con="BigWig/BYforkcovC21k.bw")
export(coverage(bingen,weight=speedBY21k2$speedmed),con="BigWig/BYforkspeedmedC21k.bw")
export(coverage(bingen,weight=speedSY21k2$n),con="BigWig/SYforkcovC21k.bw")
export(coverage(bingen,weight=speedSY21k2$speedmed),con="BigWig/SYforkspeedmedC21k.bw")

By strain comparison

We choose to test for each strain and for each bin if the speed distribution in this bin is different from the whole genome speed distribution. rDNA speeds were excluded from the whole genome distribution.

    require(future.apply)
    plan(multicore, workers = 10L)

bingenNoR <- bingen[!overlapsAny(bingen,rDNA_SY)]
# BY
speed.binBY <- do.call(c,lapply(bingenNoR$BY1k,function(x) x%>%pull(speed)))
BYtest <- bingen
BYtest$wtl1k <- future_sapply(BYtest$BY1k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binBY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
BYtest$wtg1k <- 1-BYtest$wtl1k
BYtest$wtl3k <- future_sapply(BYtest$BY3k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binBY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
BYtest$wtg3k <- 1-BYtest$wtl3k
BYtest$wtl5k <- future_sapply(BYtest$BY5k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binBY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
BYtest$wtg5k <- 1-BYtest$wtl5k
BYtest$wtl11k <- future_sapply(BYtest$BY11k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binBY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
BYtest$wtg11k <- 1-BYtest$wtl11k
BYtest$wtl21k <- future_sapply(BYtest$BY21k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binBY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
BYtest$wtg21k <- 1-BYtest$wtl21k
BYtest$wtl41k <- future_sapply(BYtest$BY41k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binBY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
BYtest$wtg41k <- 1-BYtest$wtl41k
saveRDS(BYtest,file="Data/BYspeedtest_centered.rds")
# SY
speed.binSY <- do.call(c,lapply(bingenNoR$SY1k,function(x) x%>%pull(speed)))

SYtest <- bingen
SYtest$wtl1k <- future_sapply(SYtest$SY1k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binSY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
SYtest$wtg1k <- 1-SYtest$wtl1k
SYtest$wtl3k <- future_sapply(SYtest$SY3k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binSY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
SYtest$wtg3k <- 1-SYtest$wtl3k
SYtest$wtl5k <- future_sapply(SYtest$SY5k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binSY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
SYtest$wtg5k <- 1-SYtest$wtl5k
SYtest$wtl11k <- future_sapply(SYtest$SY11k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binSY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
SYtest$wtg11k <- 1-SYtest$wtl11k
SYtest$wtl21k <- future_sapply(SYtest$SY21k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binSY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
SYtest$wtg21k <- 1-SYtest$wtl21k
SYtest$wtl41k <- future_sapply(SYtest$SY41k, function(x) {
        test <- nrow(x)!=0
        if (length(test)==0) {test=F}
        if (test) {     
            res <- wilcox.test(x$speed,speed.binSY,alternative="l")$p.value} else {res<- NA}
    return(res)
    })
SYtest$wtg41k <- 1-SYtest$wtl41k
saveRDS(SYtest,file="Data/SYspeedtest_centered.rds")

### holm correction for the pvalues
BYwtl_adj <- as_tibble(BYtest) %>% 
    select(seqnames,start,end,strand,contains("wtl")) %>% 
    pivot_longer(contains("wtl")) %>%
    mutate(padj=p.adjust(value,"holm")) %>% 
    select(-"value") %>%
    pivot_wider(names_from = name,values_from=c(padj),names_prefix="adj_")
BYwtg_adj <- as_tibble(BYtest) %>% 
    select(seqnames,start,end,strand,contains("wtg")) %>% 
    pivot_longer(contains("wtg")) %>%
    mutate(padj=p.adjust(value,"holm")) %>% 
    select(-"value") %>%
    pivot_wider(names_from = name,values_from=c(padj),names_prefix="adj_")

BYwt_adj <- left_join(BYwtl_adj,BYwtg_adj)
saveRDS(BYwt_adj,file="Data/BYspeedtestadjHolm_centered.rds")

SYwtl_adj <- as_tibble(SYtest) %>% 
    select(seqnames,start,end,strand,contains("wtl")) %>% 
    pivot_longer(contains("wtl")) %>%
    mutate(padj=p.adjust(value,"holm")) %>% 
    select(-"value") %>%
    pivot_wider(names_from = name,values_from=c(padj),names_prefix="adj_")
SYwtg_adj <- as_tibble(SYtest) %>% 
    select(seqnames,start,end,strand,contains("wtg")) %>% 
    pivot_longer(contains("wtg")) %>%
    mutate(padj=p.adjust(value,"holm")) %>% 
    select(-"value") %>%
    pivot_wider(names_from = name,values_from=c(padj),names_prefix="adj_")

SYwt_adj <- left_join(SYwtl_adj,SYwtg_adj)
saveRDS(SYwt_adj,file="Data/SYspeedtestadjHolm_centered.rds")

SY vs BY stat test

bg <- as_tibble(bingen)

bg2 <- bg %>%
    mutate(wtl1k=pmap_dbl(.,function(SY1k,BY1k,...) {
        if (nrow(SY1k)!=0 & nrow(BY1k)!=0) {
            x <- SY1k %>% pull(speed)
            y <- BY1k %>% pull(speed)
            res <- wilcox.test(x,y,alternative="l")$p.value
            }else{
            res<- NA}
        return(res)
        })) %>%
    mutate(wtg1k=1-wtl1k)
    
bg3 <- bg2 %>%
    mutate(wtl3k=future_pmap_dbl(.,function(SY3k,BY3k,...) {
        test <- (nrow(SY3k)!=0 & nrow(BY3k)!=0)
        if (length(test)==0) {test=F}
        if (test) {
            x <- SY3k %>% pull(speed)
            y <- BY3k %>% pull(speed)
            res <- wilcox.test(x,y,alternative="l")$p.value
            }else{
            res<- NA}
        return(res)
        })) %>%
    mutate(wtg3k=1-wtl3k)%>%
    mutate(wtl5k=future_pmap_dbl(.,function(SY5k,BY5k,...) {
        test <- (nrow(SY5k)!=0 & nrow(BY5k)!=0)
        if (length(test)==0) {test=F}
        if (test) {
            x <- SY5k %>% pull(speed)
            y <- BY5k %>% pull(speed)
            res <- wilcox.test(x,y,alternative="l")$p.value
            }else{
            res<- NA}
        return(res)
        })) %>%
    mutate(wtg5k=1-wtl5k)%>%
    mutate(wtl11k=future_pmap_dbl(.,function(SY11k,BY11k,...) {
        test <- (nrow(SY11k)!=0 & nrow(BY11k)!=0)
        if (length(test)==0) {test=F}
        if (test) {
            x <- SY11k %>% pull(speed)
            y <- BY11k %>% pull(speed)
            res <- wilcox.test(x,y,alternative="l")$p.value
            }else{
            res<- NA}
        return(res)
        })) %>%
    mutate(wtg11k=1-wtl11k)%>%
    mutate(wtl21k=future_pmap_dbl(.,function(SY21k,BY21k,...) {
        test <- (nrow(SY21k)!=0 & nrow(BY21k)!=0)
        if (length(test)==0) {test=F}
        if (test) {
            x <- SY21k %>% pull(speed)
            y <- BY21k %>% pull(speed)
            res <- wilcox.test(x,y,alternative="l")$p.value
            }else{
            res<- NA}
        return(res)
        })) %>%
    mutate(wtg21k=1-wtl21k)%>%
    mutate(wtl41k=future_pmap_dbl(.,function(SY41k,BY41k,...) {
        test <- (nrow(SY41k)!=0 & nrow(BY41k)!=0)
        if (length(test)==0) {test=F}
        if (test) {
            x <- SY41k %>% pull(speed)
            y <- BY41k %>% pull(speed)
            res <- wilcox.test(x,y,alternative="l")$p.value
            }else{
            res<- NA}
        return(res)
        })) %>%
    mutate(wtg41k=1-wtl41k)
    
saveRDS(bg3,file="Data/centerfullpooledSpeed_Wtest.rds")


### Holm correction for the pvalues
wtl_adj <- bg3 %>% 
    select(seqnames,start,end,strand,contains("wtl")) %>% 
    pivot_longer(contains("wtl")) %>%
    mutate(padj=p.adjust(value,"holm")) %>% 
    select(-"value") %>%
    pivot_wider(names_from = name,values_from=c(padj),names_prefix="adj_")
wtg_adj <- bg3 %>% 
    select(seqnames,start,end,strand,contains("wtg")) %>% 
    pivot_longer(contains("wtg")) %>%
    mutate(padj=p.adjust(value,"holm")) %>% 
    select(-"value") %>%
    pivot_wider(names_from = name,values_from=c(padj),names_prefix="adj_")

res_tot <- left_join(bg3,wtl_adj) %>% left_join(wtg_adj)

saveRDS(res_tot,file="Data/centerfullpooledSpeed_Wtest_Holmpadj.rds")
LS0tCnRpdGxlOiAiQllTWSBwcm9qZWN0IE5vdGVib29rIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tICAKIyAxMV9TcGVlZCBNYXAgIAoKKioqICAKClRvIGV2YWx1YXRlIHRoZSBkaWZmZXJlbmNlcyBpbiByZXBsaWNhdGlvbiBmb3JrIHNwZWVkIGJldHdlZW4gdGhlIFNZIGFuZCBCWSBzdHJhaW4sIHdlIHByb2NlZWQgc2ltaWxhcmx5IGFzIGluIFtUaGV1bG90IGV0IGFsLCAyMDIyXShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE0NjctMDIyLTMxMDEyLTApLiAgCkZvcmtzIHdlcmUgcmVkdWNlZCB0byB0aGVpciBjZW50ZXIuIFdlIGNob29zZSB0byByZW1vdmUgZm9ya3MgbWFwcGluZyBvbiB0aGUgIHByb2JsZW1hdGljIFR5IGxvY2kgZnJvbSBjaHJJSUkgYW5kIGNoclhWSSBhbmQgdGhlIHVyYTPiiIYwIGxvY3VzLiAKVGhlIFNZIGdlbm9tZSB3YXMgcGFydGVkIGludG8gbm9uIG92ZXJsYXBwaW5nIDFrYiBiaW5zIG9uIHdoaWNoIGZvcmtzIHdlcmUgYWZmZWN0ZWQgd2l0aCByZXNwZWN0IHRvIHRoZWlyIGNlbnRlci4gIApXaGVuIHdlIGNvbXB1dGVkIHRoZSBmb3JrcyBzcGVlZCBkaXN0cmlidXRpb24gZm9yIGdlbm9taWMgYmlucyBhYm92ZSAxa2IsIHdlIGNob29zZSB0byBwcm9jZWVkIGJ5IG1lcmdpbmcgY29uc2VjdXRpdmUgMWtiIGJpbnMsIGtlZXBpbmcgYSAxa2Igc2hpZnQuICAKQXMgYW4gaWxsdXN0cmF0aW9uLCB0aGUgYmluIDczIGF0IHRoZSAza2Igc2NhbGUgY29udGFpbnMgdGhlIGZvcmtzIG1hcHBpbmcgb24gdGhlIGJpbnMgNzIsNzMgYW5kIDc0IGZyb20gdGhlIDFrYiBzY2FsZS4KRm9yIGFsbCB0aGlzIHByb2NlZHVyZSwgZGF0YSB3ZXJlIHBvb2xlZCBieSBzdHJhaW4uICAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShHZW5vbWljUmFuZ2VzKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHRpZHl2ZXJzZSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShydHJhY2tsYXllcikpCmxpYnJhcnkoZnV0dXJlLmFwcGx5KQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KGZ1cnJyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3ByaXNtKQpsaWJyYXJ5KERlc2NUb29scykKYCUrJWA8LXBhc3RlMApzZXFpbmZTWSA8LSByZWFkUkRTKCJEYXRhL3NlcWluZlNZLnJkcyIpCnJETkFfU1kgPC0gR1JhbmdlcygiQ1AwMjkxNjAuMSIsSVJhbmdlcygzODc5OTQwLDM5MzQwMDApLHN0cmFuZD0iKiIsc2VxaW5mbz1zZXFpbmZTWSkKbWFza2VkVHlfU1kgPC0gcmVhZFJEUygiRGF0YS9tYXNrZWRUeV9TWS5yZHMiKQptYXNrZWRfdXJhM2QwIDwtIEdSYW5nZXMoIkNQMDI5MTYwLjEiLElSYW5nZXMoMTA1MTIwMCwxMDUxNTAwKSxzdHJhbmQ9IioiLHNlcWluZm89c2VxaW5mU1ksdHlwZT0idXJhM2QwIikKbWFza2VkX1NZIDwtIGMobWFza2VkVHlfU1ksbWFza2VkX3VyYTNkMCkgCgpzb3VyY2UoIkhlbHBlcl9mdW5jdGlvbi5yIikKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCm15cGFsIDwtIGMocGFsZXR0ZWVyOjpwYWxldHRlZXJfZCgiZ2d0aGVtZXM6OkNsYXNzaWNfMjAiKSwiZ3JleTQwIiwiYmxhY2siKQpgYGAKCiMjIyBJbXBvcnRpbmcgZm9ya3MKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRX0KZm9ya3NCWSA8LSByZWFkUkRTKCJEYXRhL2ZvcmtzX0JZQllTWS5yZHMiKQpmb3Jrc1NZIDwtIHJlYWRSRFMoIkRhdGEvZm9ya3NfU1lTWS5yZHMiKQoKZkJZIDwtIGZvcmtzQlkgJT4lCglzZWxlY3QoY2hyb209Y2hyb21TWSxkaXJlYyxYMD1YMG5TWSxYMT1YMW5TWSxleHAsc3BlZWQsdHlwZSkgJT4lCgltdXRhdGUoUmVwPW1hcF9jaHIoZXhwLGZ1bmN0aW9uKHgpIHggJT4lIHN0cl9yZW1vdmUoIkJZXyIpKSkgJT4lCgltdXRhdGUoc3RyYWluPSJCWSIpCmZTWSA8LSBmb3Jrc1NZICU+JQoJc2VsZWN0KGNocm9tLGRpcmVjLFgwLFgxLGV4cCxzcGVlZCx0eXBlKSAlPiUKCW11dGF0ZShSZXA9bWFwX2NocihleHAsZnVuY3Rpb24oeCkgeCAlPiUgc3RyX3JlbW92ZSgiU1lfIikpKSAlPiUKCW11dGF0ZShzdHJhaW49IlNZIikKCgpmb3JrQlkuZ3I8LSB3aXRoKGZCWSwgR1JhbmdlcyhzZXFuYW1lcz1jaHJvbSxyYW5nZXM9SVJhbmdlcyhwbWluKFgwLFgxKSxwbWF4KFgwLFgxKSksc3RyYW5kPWNhc2Vfd2hlbihkaXJlYz09IkwifiIrIixUfiItIiksc2VxaW5mbz1zZXFpbmZTWSxzcGVlZD1zcGVlZCxkaXJlYz1kaXJlYyx0eXBlPXR5cGUsUmVwPVJlcCkpCiMjIGV4Y2x1ZGUgVHljaHJJSUksIFR5Y2hyWFZJIGFuZCB1cmEzZDAgZm9ya3MKZm9ya0JZLmdyIDwtIGZvcmtCWS5nclshb3ZlcmxhcHNBbnkoZm9ya0JZLmdyLG1hc2tlZF9TWSldCnNwZWVkbWVkZ2VuQlkgPC0gbWVkaWFuKGZvcmtCWS5nciRzcGVlZCxuYS5ybT1UKQojIDI0MDIKZm9ya1NZLmdyPC0gd2l0aChmU1ksIEdSYW5nZXMoc2VxbmFtZXM9Y2hyb20scmFuZ2VzPUlSYW5nZXMocG1pbihYMCxYMSkscG1heChYMCxYMSkpLHN0cmFuZD1jYXNlX3doZW4oZGlyZWM9PSJMIn4iKyIsVH4iLSIpLHNlcWluZm89c2VxaW5mU1ksc3BlZWQ9c3BlZWQsZGlyZWM9ZGlyZWMsdHlwZT10eXBlLFJlcD1SZXApKQpmb3JrU1kuZ3IgPC0gZm9ya1NZLmdyWyFvdmVybGFwc0FueShmb3JrU1kuZ3IsbWFza2VkX1NZKV0Kc3BlZWRtZWRnZW5TWSA8LSBtZWRpYW4oZm9ya1NZLmdyJHNwZWVkLG5hLnJtPVQpCiMgMjQzMgpmb3JrQlkuZ3JjIDwtIHJlc2l6ZShmb3JrQlkuZ3IsZml4PSJjZW50ZXIiLHdpZHRoPTEpCmZvcmtTWS5ncmMgPC0gcmVzaXplKGZvcmtTWS5ncixmaXg9ImNlbnRlciIsd2lkdGg9MSkKYGBgCgojIyMgQWZmZWN0aW5nIHNwZWVkcyB0byBnZW5vbWljIGJpbnMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGNhY2hlPVRSVUV9CmJzIDwtIDEwMDAgCmJpbmdlbiA8LSB0aWxlR2Vub21lKHNlcWluZlNZLHRpbGV3aWR0aD1icywgY3V0Lmxhc3QudGlsZS5pbi5jaHJvbT1UKQpuYyA8LSA4TAoKb2wxIDwtIGZpbmRPdmVybGFwcyhmb3JrQlkuZ3JjLGJpbmdlbikKYmluZ2VuJEJZMWsgPC0gbWNsYXBwbHkoMTpsZW5ndGgoYmluZ2VuKSwgZnVuY3Rpb24oeCkgdGliYmxlKHNwZWVkPWZvcmtCWS5ncmNbcXVlcnlIaXRzKG9sMSlbc3ViamVjdEhpdHMob2wxKT09eF1dJHNwZWVkLGRpcmVjPWZvcmtCWS5ncmNbcXVlcnlIaXRzKG9sMSlbc3ViamVjdEhpdHMob2wxKT09eF1dJGRpcmVjLHR5cGU9Zm9ya0JZLmdyY1txdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kdHlwZSxSZXA9Zm9ya0JZLmdyY1txdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kUmVwKSxtYy5jb3Jlcz1uYykKCm9sMSA8LSBmaW5kT3ZlcmxhcHMoZm9ya1NZLmdyYyxiaW5nZW4pCmJpbmdlbiRTWTFrIDwtIG1jbGFwcGx5KDE6bGVuZ3RoKGJpbmdlbiksIGZ1bmN0aW9uKHgpIHRpYmJsZShzcGVlZD1mb3JrU1kuZ3JjW3F1ZXJ5SGl0cyhvbDEpW3N1YmplY3RIaXRzKG9sMSk9PXhdXSRzcGVlZCxkaXJlYz1mb3JrU1kuZ3JjW3F1ZXJ5SGl0cyhvbDEpW3N1YmplY3RIaXRzKG9sMSk9PXhdXSRkaXJlYyx0eXBlPWZvcmtTWS5ncmNbcXVlcnlIaXRzKG9sMSlbc3ViamVjdEhpdHMob2wxKT09eF1dJHR5cGUsUmVwPWZvcmtTWS5ncmNbcXVlcnlIaXRzKG9sMSlbc3ViamVjdEhpdHMob2wxKT09eF1dJFJlcCksbWMuY29yZXM9bmMpCmBgYAoKIyMjIEdlbmVyYXRpbmcgb3RoZXIgc2NhbGVzIGJpbm5pbmcKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRX0Kcm9sbF9jX21jIDwtIGZ1bmN0aW9uKHgsd2luPTMsbmM9NEwpCnsKIyB4IGlzIGEgbGlzdAoJcmVxdWlyZShmdXR1cmUuYXBwbHkpCglwbGFuKG11bHRpY29yZSwgd29ya2VycyA9IG5jKQoKCXJlczAgPC0gZnV0dXJlX2xhcHBseSgxOihsZW5ndGgoeCktd2luKzEpLCBmdW5jdGlvbihpKSB7CgkJcHVycnI6OnJlZHVjZSh4W2k6KGkrd2luLTEpXSwgZHBseXI6OmZ1bGxfam9pbiwgYnkgPSBjKCJzcGVlZCIsImRpcmVjIiwidHlwZSIsIlJlcCIpKQoJCX0pCglpZiAod2luJSUyPT0wKSAKCXtyZXMgPC0gYyhyZXAobGlzdChOQSksd2luJS8lMi0xKSxyZXMwLHJlcChsaXN0KE5BKSx3aW4lLyUyKSkKCX1lbHNle3JlcyA8LSBjKHJlcChsaXN0KE5BKSx3aW4lLyUyKSxyZXMwLHJlcChsaXN0KE5BKSx3aW4lLyUyKSkKCX0KcmV0dXJuKHJlcykKfQoKYmluZ2VuJEJZM2sgPC0gcm9sbF9jX21jKGJpbmdlbiRCWTFrLHdpbj0zKQpiaW5nZW4kU1kzayA8LSByb2xsX2NfbWMoYmluZ2VuJFNZMWssd2luPTMpCiNzYXZlUkRTKGJpbmdlbiwiY2VudGVyZnVsbHBvb2xlZFNwZWVkXyIgJSslIGRhdGFfc3VmZiAlKyUgIi5yZHMiKQpiaW5nZW4kQlk1ayA8LSByb2xsX2NfbWMoYmluZ2VuJEJZMWssd2luPTUpCmJpbmdlbiRTWTVrIDwtIHJvbGxfY19tYyhiaW5nZW4kU1kxayx3aW49NSkKI3NhdmVSRFMoYmluZ2VuLCJjZW50ZXJmdWxscG9vbGVkU3BlZWRfIiAlKyUgZGF0YV9zdWZmICUrJSAiLnJkcyIpCmJpbmdlbiRCWTExayA8LSByb2xsX2NfbWMoYmluZ2VuJEJZMWssd2luPTExKQpiaW5nZW4kU1kxMWsgPC0gcm9sbF9jX21jKGJpbmdlbiRTWTFrLHdpbj0xMSkKI3NhdmVSRFMoYmluZ2VuLCJjZW50ZXJmdWxscG9vbGVkU3BlZWRfIiAlKyUgZGF0YV9zdWZmICUrJSAiLnJkcyIpCmJpbmdlbiRCWTIxayA8LSByb2xsX2NfbWMoYmluZ2VuJEJZMWssd2luPTIxKQpiaW5nZW4kU1kyMWsgPC0gcm9sbF9jX21jKGJpbmdlbiRTWTFrLHdpbj0yMSkKI3NhdmVSRFMoYmluZ2VuLCJjZW50ZXJmdWxscG9vbGVkU3BlZWRfIiAlKyUgZGF0YV9zdWZmICUrJSAiLnJkcyIpCmJpbmdlbiRCWTQxayA8LSByb2xsX2NfbWMoYmluZ2VuJEJZMWssd2luPTQxKQpiaW5nZW4kU1k0MWsgPC0gcm9sbF9jX21jKGJpbmdlbiRTWTFrLHdpbj00MSkKc2F2ZVJEUyhiaW5nZW4sIkRhdGEvY2VudGVyZnVsbHBvb2xlZFNwZWVkLnJkcyIpCmBgYAoKIyMjIEV4cG9ydGluZyAyMWsgc3BlZWQgYW5kIGNvdmVyYWdlCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFfQpiaW5nZW4gPC0gcmVhZFJEUygiRGF0YS9jZW50ZXJmdWxscG9vbGVkU3BlZWQucmRzIikKc3BlZWRCWTIxayA8LSBsYXBwbHkoYmluZ2VuJEJZMjFrLCBmdW5jdGlvbih4KSB7CglpZiAoIWlzLm5hKHgpWzFdKQoJe3JlcyA8LSBzdW1tYXJpemUoeCxzcGVlZG1lZD1NZWRpYW5DSShzcGVlZClbMV0sc3BlZWRtZWRDSWluZj1NZWRpYW5DSShzcGVlZClbMl0sc3BlZWRtZWRDSXN1cD1NZWRpYW5DSShzcGVlZClbM10sbj1uKCkpfSBlbHNlIHtyZXMgPC0gdGliYmxlKHNwZWVkbWVkPU5BLHNwZWVkbWVkQ0lpbmY9TkEsc3BlZWRtZWRDSXN1cD1OQSxuPU5BKX0KCXJldHVybihyZXMpCgl9KSAKc3BlZWRCWTIxazIgPC0gZG8uY2FsbChiaW5kX3Jvd3Msc3BlZWRCWTIxaykKc3BlZWRTWTIxayA8LSBsYXBwbHkoYmluZ2VuJFNZMjFrLCBmdW5jdGlvbih4KSB7CglpZiAoIWlzLm5hKHgpWzFdKQoJe3JlcyA8LSBzdW1tYXJpemUoeCxzcGVlZG1lZD1NZWRpYW5DSShzcGVlZClbMV0sc3BlZWRtZWRDSWluZj1NZWRpYW5DSShzcGVlZClbMl0sc3BlZWRtZWRDSXN1cD1NZWRpYW5DSShzcGVlZClbM10sbj1uKCkpfSBlbHNlIHtyZXMgPC0gdGliYmxlKHNwZWVkbWVkPU5BLHNwZWVkbWVkQ0lpbmY9TkEsc3BlZWRtZWRDSXN1cD1OQSxuPU5BKX0KCXJldHVybihyZXMpCgl9KSAKc3BlZWRTWTIxazIgPC0gZG8uY2FsbChiaW5kX3Jvd3Msc3BlZWRTWTIxaykKCmV4cG9ydChjb3ZlcmFnZShiaW5nZW4sd2VpZ2h0PXNwZWVkQlkyMWsyJG4pLGNvbj0iQmlnV2lnL0JZZm9ya2NvdkMyMWsuYnciKQpleHBvcnQoY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1zcGVlZEJZMjFrMiRzcGVlZG1lZCksY29uPSJCaWdXaWcvQllmb3Jrc3BlZWRtZWRDMjFrLmJ3IikKZXhwb3J0KGNvdmVyYWdlKGJpbmdlbix3ZWlnaHQ9c3BlZWRTWTIxazIkbiksY29uPSJCaWdXaWcvU1lmb3JrY292QzIxay5idyIpCmV4cG9ydChjb3ZlcmFnZShiaW5nZW4sd2VpZ2h0PXNwZWVkU1kyMWsyJHNwZWVkbWVkKSxjb249IkJpZ1dpZy9TWWZvcmtzcGVlZG1lZEMyMWsuYnciKQpgYGAKCiMjIyBCeSBzdHJhaW4gY29tcGFyaXNvbgoKV2UgY2hvb3NlIHRvIHRlc3QgZm9yIGVhY2ggc3RyYWluIGFuZCBmb3IgZWFjaCBiaW4gaWYgdGhlIHNwZWVkIGRpc3RyaWJ1dGlvbiBpbiB0aGlzIGJpbiBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgd2hvbGUgZ2Vub21lIHNwZWVkIGRpc3RyaWJ1dGlvbi4gckROQSBzcGVlZHMgd2VyZSBleGNsdWRlZCBmcm9tIHRoZSB3aG9sZSBnZW5vbWUgZGlzdHJpYnV0aW9uLgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFfQoJcmVxdWlyZShmdXR1cmUuYXBwbHkpCglwbGFuKG11bHRpY29yZSwgd29ya2VycyA9IDEwTCkKCmJpbmdlbk5vUiA8LSBiaW5nZW5bIW92ZXJsYXBzQW55KGJpbmdlbixyRE5BX1NZKV0KIyBCWQpzcGVlZC5iaW5CWSA8LSBkby5jYWxsKGMsbGFwcGx5KGJpbmdlbk5vUiRCWTFrLGZ1bmN0aW9uKHgpIHglPiVwdWxsKHNwZWVkKSkpCkJZdGVzdCA8LSBiaW5nZW4KQll0ZXN0JHd0bDFrIDwtIGZ1dHVyZV9zYXBwbHkoQll0ZXN0JEJZMWssIGZ1bmN0aW9uKHgpIHsKCQl0ZXN0IDwtIG5yb3coeCkhPTAKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CQkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgkc3BlZWQsc3BlZWQuYmluQlksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlfSBlbHNlIHtyZXM8LSBOQX0KCXJldHVybihyZXMpCgl9KQpCWXRlc3Qkd3RnMWsgPC0gMS1CWXRlc3Qkd3RsMWsKQll0ZXN0JHd0bDNrIDwtIGZ1dHVyZV9zYXBwbHkoQll0ZXN0JEJZM2ssIGZ1bmN0aW9uKHgpIHsKCQl0ZXN0IDwtIG5yb3coeCkhPTAKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CQkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgkc3BlZWQsc3BlZWQuYmluQlksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlfSBlbHNlIHtyZXM8LSBOQX0KCXJldHVybihyZXMpCgl9KQpCWXRlc3Qkd3RnM2sgPC0gMS1CWXRlc3Qkd3RsM2sKQll0ZXN0JHd0bDVrIDwtIGZ1dHVyZV9zYXBwbHkoQll0ZXN0JEJZNWssIGZ1bmN0aW9uKHgpIHsKCQl0ZXN0IDwtIG5yb3coeCkhPTAKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CQkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgkc3BlZWQsc3BlZWQuYmluQlksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlfSBlbHNlIHtyZXM8LSBOQX0KCXJldHVybihyZXMpCgl9KQpCWXRlc3Qkd3RnNWsgPC0gMS1CWXRlc3Qkd3RsNWsKQll0ZXN0JHd0bDExayA8LSBmdXR1cmVfc2FwcGx5KEJZdGVzdCRCWTExaywgZnVuY3Rpb24oeCkgewoJCXRlc3QgPC0gbnJvdyh4KSE9MAoJCWlmIChsZW5ndGgodGVzdCk9PTApIHt0ZXN0PUZ9CgkJaWYgKHRlc3QpIHsJCQoJCQlyZXMgPC0gd2lsY294LnRlc3QoeCRzcGVlZCxzcGVlZC5iaW5CWSxhbHRlcm5hdGl2ZT0ibCIpJHAudmFsdWV9IGVsc2Uge3JlczwtIE5BfQoJcmV0dXJuKHJlcykKCX0pCkJZdGVzdCR3dGcxMWsgPC0gMS1CWXRlc3Qkd3RsMTFrCkJZdGVzdCR3dGwyMWsgPC0gZnV0dXJlX3NhcHBseShCWXRlc3QkQlkyMWssIGZ1bmN0aW9uKHgpIHsKCQl0ZXN0IDwtIG5yb3coeCkhPTAKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CQkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgkc3BlZWQsc3BlZWQuYmluQlksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlfSBlbHNlIHtyZXM8LSBOQX0KCXJldHVybihyZXMpCgl9KQpCWXRlc3Qkd3RnMjFrIDwtIDEtQll0ZXN0JHd0bDIxawpCWXRlc3Qkd3RsNDFrIDwtIGZ1dHVyZV9zYXBwbHkoQll0ZXN0JEJZNDFrLCBmdW5jdGlvbih4KSB7CgkJdGVzdCA8LSBucm93KHgpIT0wCgkJaWYgKGxlbmd0aCh0ZXN0KT09MCkge3Rlc3Q9Rn0KCQlpZiAodGVzdCkgewkJCgkJCXJlcyA8LSB3aWxjb3gudGVzdCh4JHNwZWVkLHNwZWVkLmJpbkJZLGFsdGVybmF0aXZlPSJsIikkcC52YWx1ZX0gZWxzZSB7cmVzPC0gTkF9CglyZXR1cm4ocmVzKQoJfSkKQll0ZXN0JHd0ZzQxayA8LSAxLUJZdGVzdCR3dGw0MWsKc2F2ZVJEUyhCWXRlc3QsZmlsZT0iRGF0YS9CWXNwZWVkdGVzdF9jZW50ZXJlZC5yZHMiKQojIFNZCnNwZWVkLmJpblNZIDwtIGRvLmNhbGwoYyxsYXBwbHkoYmluZ2VuTm9SJFNZMWssZnVuY3Rpb24oeCkgeCU+JXB1bGwoc3BlZWQpKSkKClNZdGVzdCA8LSBiaW5nZW4KU1l0ZXN0JHd0bDFrIDwtIGZ1dHVyZV9zYXBwbHkoU1l0ZXN0JFNZMWssIGZ1bmN0aW9uKHgpIHsKCQl0ZXN0IDwtIG5yb3coeCkhPTAKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CQkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgkc3BlZWQsc3BlZWQuYmluU1ksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlfSBlbHNlIHtyZXM8LSBOQX0KCXJldHVybihyZXMpCgl9KQpTWXRlc3Qkd3RnMWsgPC0gMS1TWXRlc3Qkd3RsMWsKU1l0ZXN0JHd0bDNrIDwtIGZ1dHVyZV9zYXBwbHkoU1l0ZXN0JFNZM2ssIGZ1bmN0aW9uKHgpIHsKCQl0ZXN0IDwtIG5yb3coeCkhPTAKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CQkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgkc3BlZWQsc3BlZWQuYmluU1ksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlfSBlbHNlIHtyZXM8LSBOQX0KCXJldHVybihyZXMpCgl9KQpTWXRlc3Qkd3RnM2sgPC0gMS1TWXRlc3Qkd3RsM2sKU1l0ZXN0JHd0bDVrIDwtIGZ1dHVyZV9zYXBwbHkoU1l0ZXN0JFNZNWssIGZ1bmN0aW9uKHgpIHsKCQl0ZXN0IDwtIG5yb3coeCkhPTAKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CQkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgkc3BlZWQsc3BlZWQuYmluU1ksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlfSBlbHNlIHtyZXM8LSBOQX0KCXJldHVybihyZXMpCgl9KQpTWXRlc3Qkd3RnNWsgPC0gMS1TWXRlc3Qkd3RsNWsKU1l0ZXN0JHd0bDExayA8LSBmdXR1cmVfc2FwcGx5KFNZdGVzdCRTWTExaywgZnVuY3Rpb24oeCkgewoJCXRlc3QgPC0gbnJvdyh4KSE9MAoJCWlmIChsZW5ndGgodGVzdCk9PTApIHt0ZXN0PUZ9CgkJaWYgKHRlc3QpIHsJCQoJCQlyZXMgPC0gd2lsY294LnRlc3QoeCRzcGVlZCxzcGVlZC5iaW5TWSxhbHRlcm5hdGl2ZT0ibCIpJHAudmFsdWV9IGVsc2Uge3JlczwtIE5BfQoJcmV0dXJuKHJlcykKCX0pClNZdGVzdCR3dGcxMWsgPC0gMS1TWXRlc3Qkd3RsMTFrClNZdGVzdCR3dGwyMWsgPC0gZnV0dXJlX3NhcHBseShTWXRlc3QkU1kyMWssIGZ1bmN0aW9uKHgpIHsKCQl0ZXN0IDwtIG5yb3coeCkhPTAKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CQkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgkc3BlZWQsc3BlZWQuYmluU1ksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlfSBlbHNlIHtyZXM8LSBOQX0KCXJldHVybihyZXMpCgl9KQpTWXRlc3Qkd3RnMjFrIDwtIDEtU1l0ZXN0JHd0bDIxawpTWXRlc3Qkd3RsNDFrIDwtIGZ1dHVyZV9zYXBwbHkoU1l0ZXN0JFNZNDFrLCBmdW5jdGlvbih4KSB7CgkJdGVzdCA8LSBucm93KHgpIT0wCgkJaWYgKGxlbmd0aCh0ZXN0KT09MCkge3Rlc3Q9Rn0KCQlpZiAodGVzdCkgewkJCgkJCXJlcyA8LSB3aWxjb3gudGVzdCh4JHNwZWVkLHNwZWVkLmJpblNZLGFsdGVybmF0aXZlPSJsIikkcC52YWx1ZX0gZWxzZSB7cmVzPC0gTkF9CglyZXR1cm4ocmVzKQoJfSkKU1l0ZXN0JHd0ZzQxayA8LSAxLVNZdGVzdCR3dGw0MWsKc2F2ZVJEUyhTWXRlc3QsZmlsZT0iRGF0YS9TWXNwZWVkdGVzdF9jZW50ZXJlZC5yZHMiKQoKIyMjIGhvbG0gY29ycmVjdGlvbiBmb3IgdGhlIHB2YWx1ZXMKQll3dGxfYWRqIDwtIGFzX3RpYmJsZShCWXRlc3QpICU+JSAKCXNlbGVjdChzZXFuYW1lcyxzdGFydCxlbmQsc3RyYW5kLGNvbnRhaW5zKCJ3dGwiKSkgJT4lIAoJcGl2b3RfbG9uZ2VyKGNvbnRhaW5zKCJ3dGwiKSkgJT4lCgltdXRhdGUocGFkaj1wLmFkanVzdCh2YWx1ZSwiaG9sbSIpKSAlPiUgCglzZWxlY3QoLSJ2YWx1ZSIpICU+JQoJcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG5hbWUsdmFsdWVzX2Zyb209YyhwYWRqKSxuYW1lc19wcmVmaXg9ImFkal8iKQpCWXd0Z19hZGogPC0gYXNfdGliYmxlKEJZdGVzdCkgJT4lIAoJc2VsZWN0KHNlcW5hbWVzLHN0YXJ0LGVuZCxzdHJhbmQsY29udGFpbnMoInd0ZyIpKSAlPiUgCglwaXZvdF9sb25nZXIoY29udGFpbnMoInd0ZyIpKSAlPiUKCW11dGF0ZShwYWRqPXAuYWRqdXN0KHZhbHVlLCJob2xtIikpICU+JSAKCXNlbGVjdCgtInZhbHVlIikgJT4lCglwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbmFtZSx2YWx1ZXNfZnJvbT1jKHBhZGopLG5hbWVzX3ByZWZpeD0iYWRqXyIpCgpCWXd0X2FkaiA8LSBsZWZ0X2pvaW4oQll3dGxfYWRqLEJZd3RnX2FkaikKc2F2ZVJEUyhCWXd0X2FkaixmaWxlPSJEYXRhL0JZc3BlZWR0ZXN0YWRqSG9sbV9jZW50ZXJlZC5yZHMiKQoKU1l3dGxfYWRqIDwtIGFzX3RpYmJsZShTWXRlc3QpICU+JSAKCXNlbGVjdChzZXFuYW1lcyxzdGFydCxlbmQsc3RyYW5kLGNvbnRhaW5zKCJ3dGwiKSkgJT4lIAoJcGl2b3RfbG9uZ2VyKGNvbnRhaW5zKCJ3dGwiKSkgJT4lCgltdXRhdGUocGFkaj1wLmFkanVzdCh2YWx1ZSwiaG9sbSIpKSAlPiUgCglzZWxlY3QoLSJ2YWx1ZSIpICU+JQoJcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG5hbWUsdmFsdWVzX2Zyb209YyhwYWRqKSxuYW1lc19wcmVmaXg9ImFkal8iKQpTWXd0Z19hZGogPC0gYXNfdGliYmxlKFNZdGVzdCkgJT4lIAoJc2VsZWN0KHNlcW5hbWVzLHN0YXJ0LGVuZCxzdHJhbmQsY29udGFpbnMoInd0ZyIpKSAlPiUgCglwaXZvdF9sb25nZXIoY29udGFpbnMoInd0ZyIpKSAlPiUKCW11dGF0ZShwYWRqPXAuYWRqdXN0KHZhbHVlLCJob2xtIikpICU+JSAKCXNlbGVjdCgtInZhbHVlIikgJT4lCglwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbmFtZSx2YWx1ZXNfZnJvbT1jKHBhZGopLG5hbWVzX3ByZWZpeD0iYWRqXyIpCgpTWXd0X2FkaiA8LSBsZWZ0X2pvaW4oU1l3dGxfYWRqLFNZd3RnX2FkaikKc2F2ZVJEUyhTWXd0X2FkaixmaWxlPSJEYXRhL1NZc3BlZWR0ZXN0YWRqSG9sbV9jZW50ZXJlZC5yZHMiKQoKYGBgCgojIyMjIFNZIHZzIEJZIHN0YXQgdGVzdAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2FjaGU9VFJVRX0KYmcgPC0gYXNfdGliYmxlKGJpbmdlbikKCmJnMiA8LSBiZyAlPiUKCW11dGF0ZSh3dGwxaz1wbWFwX2RibCguLGZ1bmN0aW9uKFNZMWssQlkxaywuLi4pIHsKCQlpZiAobnJvdyhTWTFrKSE9MCAmIG5yb3coQlkxaykhPTApIHsKCQkJeCA8LSBTWTFrICU+JSBwdWxsKHNwZWVkKQoJCQl5IDwtIEJZMWsgJT4lIHB1bGwoc3BlZWQpCgkJCXJlcyA8LSB3aWxjb3gudGVzdCh4LHksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlCgkJCX1lbHNlewoJCQlyZXM8LSBOQX0KCQlyZXR1cm4ocmVzKQoJCX0pKSAlPiUKCW11dGF0ZSh3dGcxaz0xLXd0bDFrKQoJCmJnMyA8LSBiZzIgJT4lCgltdXRhdGUod3RsM2s9ZnV0dXJlX3BtYXBfZGJsKC4sZnVuY3Rpb24oU1kzayxCWTNrLC4uLikgewoJCXRlc3QgPC0gKG5yb3coU1kzaykhPTAgJiBucm93KEJZM2spIT0wKQoJCWlmIChsZW5ndGgodGVzdCk9PTApIHt0ZXN0PUZ9CgkJaWYgKHRlc3QpIHsKCQkJeCA8LSBTWTNrICU+JSBwdWxsKHNwZWVkKQoJCQl5IDwtIEJZM2sgJT4lIHB1bGwoc3BlZWQpCgkJCXJlcyA8LSB3aWxjb3gudGVzdCh4LHksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlCgkJCX1lbHNlewoJCQlyZXM8LSBOQX0KCQlyZXR1cm4ocmVzKQoJCX0pKSAlPiUKCW11dGF0ZSh3dGczaz0xLXd0bDNrKSU+JQoJbXV0YXRlKHd0bDVrPWZ1dHVyZV9wbWFwX2RibCguLGZ1bmN0aW9uKFNZNWssQlk1aywuLi4pIHsKCQl0ZXN0IDwtIChucm93KFNZNWspIT0wICYgbnJvdyhCWTVrKSE9MCkKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CgkJCXggPC0gU1k1ayAlPiUgcHVsbChzcGVlZCkKCQkJeSA8LSBCWTVrICU+JSBwdWxsKHNwZWVkKQoJCQlyZXMgPC0gd2lsY294LnRlc3QoeCx5LGFsdGVybmF0aXZlPSJsIikkcC52YWx1ZQoJCQl9ZWxzZXsKCQkJcmVzPC0gTkF9CgkJcmV0dXJuKHJlcykKCQl9KSkgJT4lCgltdXRhdGUod3RnNWs9MS13dGw1ayklPiUKCW11dGF0ZSh3dGwxMWs9ZnV0dXJlX3BtYXBfZGJsKC4sZnVuY3Rpb24oU1kxMWssQlkxMWssLi4uKSB7CgkJdGVzdCA8LSAobnJvdyhTWTExaykhPTAgJiBucm93KEJZMTFrKSE9MCkKCQlpZiAobGVuZ3RoKHRlc3QpPT0wKSB7dGVzdD1GfQoJCWlmICh0ZXN0KSB7CgkJCXggPC0gU1kxMWsgJT4lIHB1bGwoc3BlZWQpCgkJCXkgPC0gQlkxMWsgJT4lIHB1bGwoc3BlZWQpCgkJCXJlcyA8LSB3aWxjb3gudGVzdCh4LHksYWx0ZXJuYXRpdmU9ImwiKSRwLnZhbHVlCgkJCX1lbHNlewoJCQlyZXM8LSBOQX0KCQlyZXR1cm4ocmVzKQoJCX0pKSAlPiUKCW11dGF0ZSh3dGcxMWs9MS13dGwxMWspJT4lCgltdXRhdGUod3RsMjFrPWZ1dHVyZV9wbWFwX2RibCguLGZ1bmN0aW9uKFNZMjFrLEJZMjFrLC4uLikgewoJCXRlc3QgPC0gKG5yb3coU1kyMWspIT0wICYgbnJvdyhCWTIxaykhPTApCgkJaWYgKGxlbmd0aCh0ZXN0KT09MCkge3Rlc3Q9Rn0KCQlpZiAodGVzdCkgewoJCQl4IDwtIFNZMjFrICU+JSBwdWxsKHNwZWVkKQoJCQl5IDwtIEJZMjFrICU+JSBwdWxsKHNwZWVkKQoJCQlyZXMgPC0gd2lsY294LnRlc3QoeCx5LGFsdGVybmF0aXZlPSJsIikkcC52YWx1ZQoJCQl9ZWxzZXsKCQkJcmVzPC0gTkF9CgkJcmV0dXJuKHJlcykKCQl9KSkgJT4lCgltdXRhdGUod3RnMjFrPTEtd3RsMjFrKSU+JQoJbXV0YXRlKHd0bDQxaz1mdXR1cmVfcG1hcF9kYmwoLixmdW5jdGlvbihTWTQxayxCWTQxaywuLi4pIHsKCQl0ZXN0IDwtIChucm93KFNZNDFrKSE9MCAmIG5yb3coQlk0MWspIT0wKQoJCWlmIChsZW5ndGgodGVzdCk9PTApIHt0ZXN0PUZ9CgkJaWYgKHRlc3QpIHsKCQkJeCA8LSBTWTQxayAlPiUgcHVsbChzcGVlZCkKCQkJeSA8LSBCWTQxayAlPiUgcHVsbChzcGVlZCkKCQkJcmVzIDwtIHdpbGNveC50ZXN0KHgseSxhbHRlcm5hdGl2ZT0ibCIpJHAudmFsdWUKCQkJfWVsc2V7CgkJCXJlczwtIE5BfQoJCXJldHVybihyZXMpCgkJfSkpICU+JQoJbXV0YXRlKHd0ZzQxaz0xLXd0bDQxaykKCQpzYXZlUkRTKGJnMyxmaWxlPSJEYXRhL2NlbnRlcmZ1bGxwb29sZWRTcGVlZF9XdGVzdC5yZHMiKQoKCiMjIyBIb2xtIGNvcnJlY3Rpb24gZm9yIHRoZSBwdmFsdWVzCnd0bF9hZGogPC0gYmczICU+JSAKCXNlbGVjdChzZXFuYW1lcyxzdGFydCxlbmQsc3RyYW5kLGNvbnRhaW5zKCJ3dGwiKSkgJT4lIAoJcGl2b3RfbG9uZ2VyKGNvbnRhaW5zKCJ3dGwiKSkgJT4lCgltdXRhdGUocGFkaj1wLmFkanVzdCh2YWx1ZSwiaG9sbSIpKSAlPiUgCglzZWxlY3QoLSJ2YWx1ZSIpICU+JQoJcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG5hbWUsdmFsdWVzX2Zyb209YyhwYWRqKSxuYW1lc19wcmVmaXg9ImFkal8iKQp3dGdfYWRqIDwtIGJnMyAlPiUgCglzZWxlY3Qoc2VxbmFtZXMsc3RhcnQsZW5kLHN0cmFuZCxjb250YWlucygid3RnIikpICU+JSAKCXBpdm90X2xvbmdlcihjb250YWlucygid3RnIikpICU+JQoJbXV0YXRlKHBhZGo9cC5hZGp1c3QodmFsdWUsImhvbG0iKSkgJT4lIAoJc2VsZWN0KC0idmFsdWUiKSAlPiUKCXBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBuYW1lLHZhbHVlc19mcm9tPWMocGFkaiksbmFtZXNfcHJlZml4PSJhZGpfIikKCnJlc190b3QgPC0gbGVmdF9qb2luKGJnMyx3dGxfYWRqKSAlPiUgbGVmdF9qb2luKHd0Z19hZGopCgpzYXZlUkRTKHJlc190b3QsZmlsZT0iRGF0YS9jZW50ZXJmdWxscG9vbGVkU3BlZWRfV3Rlc3RfSG9sbXBhZGoucmRzIikKYGBgCgo=