07_RFD test


In order to test difference in forks directionality between BY and SY, experiment were either pooled by strain or treated as paired replicates. The genome was cut into non overlapping bins of 1kb and for each bin and each conditions, we compared the number of L and R forks using either the exact Boschloo test for the pooled approach or the Mantel_Haenszel test in the paired approach. the tests were repeated using 3, 5, 11, 21 and 41kb scales by merging the counts for successive bins but counting each fork only once for eack bi at each scale. Results were corrected for multiplicity testing using the Benjamini and Yekuteli correction.

suppressMessages(library(GenomicRanges))
suppressMessages(library(tidyverse))
suppressMessages(library(rtracklayer))
`%+%`<-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")

Importing forks

forksBY <- readRDS("Data/forks_BYBYSY.rds") %>%
    select(chrom=chromSY,direc,X0=X0nSY,X1=X1nSY,exp) %>%
    mutate(Rep=map_chr(exp,function(x) x %>% str_remove("BY_"))) %>%
    mutate(strain="BY")
forks_BYSY.gr<- with(forksBY, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),seqinfo=seqinfSY))
fBY <- forksBY %>% 
    mutate(filt=!overlapsAny(forks_BYSY.gr,c(masked_SY))) %>%
    filter(filt)

forksSY <- readRDS("Data/forks_SYSY.rds") %>%
    select(chrom,direc,X0,X1,exp) %>%
    mutate(Rep=map_chr(exp,function(x) x %>% str_remove("SY_"))) %>%
    mutate(strain="SY")
forks_SYSY.gr<- with(forksSY, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),seqinfo=seqinfSY))
fSY <- forksSY %>% 
    mutate(filt=!overlapsAny(forks_SYSY.gr,c(masked_SY))) %>%
    filter(filt)

Create GenomicRanges as in the OEM script.

forkBY1 <- fBY %>% filter(Rep=="rep1")
forkBY1$fid <- 1:nrow(forkBY1)
forkBY1_R <- forkBY1 %>% filter(direc=="R")
forkBY1_L <- forkBY1 %>% filter(direc=="L")
forkBY1_R.gr<- with(forkBY1_R, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))
forkBY1_L.gr<- with(forkBY1_L, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))

forkBY2 <- fBY %>% filter(Rep=="rep2")
forkBY2$fid <- 1:nrow(forkBY2)
forkBY2_R <- forkBY2 %>% filter(direc=="R")
forkBY2_L <- forkBY2 %>% filter(direc=="L")
forkBY2_R.gr<- with(forkBY2_R, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))
forkBY2_L.gr<- with(forkBY2_L, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))

forkSY1 <- fSY %>% filter(Rep=="rep1")
forkSY1$fid <- 1:nrow(forkSY1)
forkSY1_R <- forkSY1 %>% filter(direc=="R")
forkSY1_L <- forkSY1 %>% filter(direc=="L")
forkSY1_R.gr<- with(forkSY1_R, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))
forkSY1_L.gr<- with(forkSY1_L, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))

forkSY2 <- fSY %>% filter(Rep=="rep2")
forkSY2$fid <- 1:nrow(forkSY2)
forkSY2_R <- forkSY2 %>% filter(direc=="R")
forkSY2_L <- forkSY2 %>% filter(direc=="L")
forkSY2_R.gr<- with(forkSY2_R, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))
forkSY2_L.gr<- with(forkSY2_L, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))

forkBY3 <- fBY %>% filter(Rep=="rep3")
forkBY3$fid <- 1:nrow(forkBY3)
forkBY3_R <- forkBY3 %>% filter(direc=="R")
forkBY3_L <- forkBY3 %>% filter(direc=="L")
forkBY3_R.gr<- with(forkBY3_R, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))
forkBY3_L.gr<- with(forkBY3_L, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))

forkSY3 <- fSY %>% filter(Rep=="rep3")
forkSY3$fid <- 1:nrow(forkSY3)
forkSY3_R <- forkSY3 %>% filter(direc=="R")
forkSY3_L <- forkSY3 %>% filter(direc=="L")
forkSY3_R.gr<- with(forkSY3_R, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))
forkSY3_L.gr<- with(forkSY3_L, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))

1kb Data binning

bs <- 1000 
bingen <- tileGenome(seqinfSY,tilewidth=bs, cut.last.tile.in.chrom=T)
nc <- 8L
min_ovl <- 501L
ol1 <- findOverlaps(forkBY1_R.gr,bingen,minoverlap=min_ovl)
bingen$BY1_1kR <- smclapply(1:length(bingen), function(x) forkBY1_R.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkBY1_L.gr,bingen,minoverlap=min_ovl)
bingen$BY1_1kL <- smclapply(1:length(bingen), function(x) forkBY1_L.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)

ol1 <- findOverlaps(forkBY2_R.gr,bingen,minoverlap=min_ovl)
bingen$BY2_1kR <- smclapply(1:length(bingen), function(x) forkBY2_R.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkBY2_L.gr,bingen,minoverlap=min_ovl)
bingen$BY2_1kL <- smclapply(1:length(bingen), function(x) forkBY2_L.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)

ol1 <- findOverlaps(forkSY1_R.gr,bingen,minoverlap=min_ovl)
bingen$SY1_1kR <- smclapply(1:length(bingen), function(x) forkSY1_R.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkSY1_L.gr,bingen,minoverlap=min_ovl)
bingen$SY1_1kL <- smclapply(1:length(bingen), function(x) forkSY1_L.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)

ol1 <- findOverlaps(forkSY2_R.gr,bingen,minoverlap=min_ovl)
bingen$SY2_1kR <- smclapply(1:length(bingen), function(x) forkSY2_R.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkSY2_L.gr,bingen,minoverlap=min_ovl)
bingen$SY2_1kL <- smclapply(1:length(bingen), function(x) forkSY2_L.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)

ol1 <- findOverlaps(forkBY3_R.gr,bingen,minoverlap=min_ovl)
bingen$BY3_1kR <- smclapply(1:length(bingen), function(x) forkBY3_R.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkBY3_L.gr,bingen,minoverlap=min_ovl)
bingen$BY3_1kL <- smclapply(1:length(bingen), function(x) forkBY3_L.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)

ol1 <- findOverlaps(forkSY3_R.gr,bingen,minoverlap=min_ovl)
bingen$SY3_1kR <- smclapply(1:length(bingen), function(x) forkSY3_R.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkSY3_L.gr,bingen,minoverlap=min_ovl)
bingen$SY3_1kL <- smclapply(1:length(bingen), function(x) forkSY3_L.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)

Other scales binning

roll_c <- function(x,win=3)
{
# x is a list
    res0 <- lapply(1:(length(x)-win+1), function(i) do.call(c,x[i:(i+win-1)]))
    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$BY1_01kR <- sapply(roll_c(bingen$BY1_1kR,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$BY1_01kL <- sapply(roll_c(bingen$BY1_1kL,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_01kR <- sapply(roll_c(bingen$BY2_1kR,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_01kL <- sapply(roll_c(bingen$BY2_1kL,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_01kR <- sapply(roll_c(bingen$SY1_1kR,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_01kL <- sapply(roll_c(bingen$SY1_1kL,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_01kR <- sapply(roll_c(bingen$SY2_1kR,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_01kL <- sapply(roll_c(bingen$SY2_1kL,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_01kR <- sapply(roll_c(bingen$BY3_1kR,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_01kL <- sapply(roll_c(bingen$BY3_1kL,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_01kR <- sapply(roll_c(bingen$SY3_1kR,win=1),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_01kL <- sapply(roll_c(bingen$SY3_1kL,win=1),function(x) length(unique(x[!is.na(x)])))


bingen$BY1_3kR <- sapply(roll_c(bingen$BY1_1kR,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$BY1_3kL <- sapply(roll_c(bingen$BY1_1kL,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_3kR <- sapply(roll_c(bingen$BY2_1kR,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_3kL <- sapply(roll_c(bingen$BY2_1kL,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_3kR <- sapply(roll_c(bingen$SY1_1kR,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_3kL <- sapply(roll_c(bingen$SY1_1kL,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_3kR <- sapply(roll_c(bingen$SY2_1kR,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_3kL <- sapply(roll_c(bingen$SY2_1kL,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_3kR <- sapply(roll_c(bingen$BY3_1kR,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_3kL <- sapply(roll_c(bingen$BY3_1kL,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_3kR <- sapply(roll_c(bingen$SY3_1kR,win=3),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_3kL <- sapply(roll_c(bingen$SY3_1kL,win=3),function(x) length(unique(x[!is.na(x)])))

bingen$BY1_5kR <- sapply(roll_c(bingen$BY1_1kR,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$BY1_5kL <- sapply(roll_c(bingen$BY1_1kL,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_5kR <- sapply(roll_c(bingen$BY2_1kR,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_5kL <- sapply(roll_c(bingen$BY2_1kL,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_5kR <- sapply(roll_c(bingen$SY1_1kR,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_5kL <- sapply(roll_c(bingen$SY1_1kL,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_5kR <- sapply(roll_c(bingen$SY2_1kR,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_5kL <- sapply(roll_c(bingen$SY2_1kL,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_5kR <- sapply(roll_c(bingen$BY3_1kR,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_5kL <- sapply(roll_c(bingen$BY3_1kL,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_5kR <- sapply(roll_c(bingen$SY3_1kR,win=5),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_5kL <- sapply(roll_c(bingen$SY3_1kL,win=5),function(x) length(unique(x[!is.na(x)])))

bingen$BY1_11kR <- sapply(roll_c(bingen$BY1_1kR,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$BY1_11kL <- sapply(roll_c(bingen$BY1_1kL,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_11kR <- sapply(roll_c(bingen$BY2_1kR,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_11kL <- sapply(roll_c(bingen$BY2_1kL,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_11kR <- sapply(roll_c(bingen$SY1_1kR,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_11kL <- sapply(roll_c(bingen$SY1_1kL,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_11kR <- sapply(roll_c(bingen$SY2_1kR,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_11kL <- sapply(roll_c(bingen$SY2_1kL,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_11kR <- sapply(roll_c(bingen$BY3_1kR,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_11kL <- sapply(roll_c(bingen$BY3_1kL,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_11kR <- sapply(roll_c(bingen$SY3_1kR,win=11),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_11kL <- sapply(roll_c(bingen$SY3_1kL,win=11),function(x) length(unique(x[!is.na(x)])))

bingen$BY1_21kR <- sapply(roll_c(bingen$BY1_1kR,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$BY1_21kL <- sapply(roll_c(bingen$BY1_1kL,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_21kR <- sapply(roll_c(bingen$BY2_1kR,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_21kL <- sapply(roll_c(bingen$BY2_1kL,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_21kR <- sapply(roll_c(bingen$SY1_1kR,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_21kL <- sapply(roll_c(bingen$SY1_1kL,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_21kR <- sapply(roll_c(bingen$SY2_1kR,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_21kL <- sapply(roll_c(bingen$SY2_1kL,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_21kR <- sapply(roll_c(bingen$BY3_1kR,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_21kL <- sapply(roll_c(bingen$BY3_1kL,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_21kR <- sapply(roll_c(bingen$SY3_1kR,win=21),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_21kL <- sapply(roll_c(bingen$SY3_1kL,win=21),function(x) length(unique(x[!is.na(x)])))

bingen$BY1_41kR <- sapply(roll_c(bingen$BY1_1kR,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$BY1_41kL <- sapply(roll_c(bingen$BY1_1kL,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_41kR <- sapply(roll_c(bingen$BY2_1kR,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$BY2_41kL <- sapply(roll_c(bingen$BY2_1kL,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_41kR <- sapply(roll_c(bingen$SY1_1kR,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$SY1_41kL <- sapply(roll_c(bingen$SY1_1kL,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_41kR <- sapply(roll_c(bingen$SY2_1kR,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$SY2_41kL <- sapply(roll_c(bingen$SY2_1kL,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_41kR <- sapply(roll_c(bingen$BY3_1kR,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$BY3_41kL <- sapply(roll_c(bingen$BY3_1kL,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_41kR <- sapply(roll_c(bingen$SY3_1kR,win=41),function(x) length(unique(x[!is.na(x)])))
bingen$SY3_41kL <- sapply(roll_c(bingen$SY3_1kL,win=41),function(x) length(unique(x[!is.na(x)])))

Paired test after transforming the data into 3D data frame.

bingen2 <- bingen

bg3 <- as_tibble(bingen) %>% select(contains("3k")) %>%
    mutate(test_mat=pmap(., function(BY1_3kR,BY1_3kL,BY2_3kR,BY2_3kL,SY1_3kR,SY1_3kL,SY2_3kR,SY2_3kL,BY3_3kR,BY3_3kL,SY3_3kR,SY3_3kL,...) {
        x111=BY1_3kR
        x211=BY1_3kL
        x121=SY1_3kR
        x221=SY1_3kL
        x112=BY2_3kR
        x212=BY2_3kL
        x122=SY2_3kR
        x222=SY2_3kL
        x113=BY3_3kR
        x213=BY3_3kL
        x123=SY3_3kR
        x223=SY3_3kL
        df= c(x111,x211,x121,x221,x112,x212,x122,x222,x113,x213,x123,x223)
        df2 <- array(df,dim=c(2,2,3))
        tempo <- list(c("R","L"),c("BY","SY"),c("rep1","rep2","rep3"))
        names(tempo) <- c("Dir","Strain","Rep")
        dimnames(df2) <- tempo
        return(df2)
        }))
bg4 <- bg3 %>% mutate(mh_test=map_dbl(test_mat, function(x) {
    x <- apply(x,c(1,2,3),as.numeric)
    n1.z <- margin.table(x[1,,],2)
    n.1z <- margin.table(x[,1,],2)
    n.2z<- margin.table(x[,2,],2)
    nz <- margin.table(x,3)
    t11z <- n1.z*n.1z/nz
    if (sum((apply(x,3,sum,na.rm=T)>1))<3 |
    (sum(t11z,na.rm=T) - sum(pmax(0, (n1.z-n.2z),na.rm=T))) < 5 | (sum(pmin(n1.z, n.1z,na.rm=T)) - sum(t11z,na.rm=T)) < 5)
    {res=NA}else{res=mantelhaen.test(x,correct=T,exact=F)$p.value}
    }))
    
bingen2$mh_test3k <- bg4$mh_test

bg3 <- as_tibble(bingen) %>% select(contains("5k")) %>%
    mutate(test_mat=pmap(., function(BY1_5kR,BY1_5kL,BY2_5kR,BY2_5kL,SY1_5kR,SY1_5kL,SY2_5kR,SY2_5kL,BY3_5kR,BY3_5kL,SY3_5kR,SY3_5kL,...) {
        x111=BY1_5kR
        x211=BY1_5kL
        x121=SY1_5kR
        x221=SY1_5kL
        x112=BY2_5kR
        x212=BY2_5kL
        x122=SY2_5kR
        x222=SY2_5kL
        x113=BY3_5kR
        x213=BY3_5kL
        x123=SY3_5kR
        x223=SY3_5kL
        df= c(x111,x211,x121,x221,x112,x212,x122,x222,x113,x213,x123,x223)
        df2 <- array(df,dim=c(2,2,3))
        tempo <- list(c("R","L"),c("BY","SY"),c("rep1","rep2","rep3"))
        names(tempo) <- c("Dir","Strain","Rep")
        dimnames(df2) <- tempo
        return(df2)
        }))

bg4 <- bg3 %>% mutate(mh_test=map_dbl(test_mat, function(x) {
    x <- apply(x,c(1,2,3),as.numeric)
    n1.z <- margin.table(x[1,,],2)
    n.1z <- margin.table(x[,1,],2)
    n.2z<- margin.table(x[,2,],2)
    nz <- margin.table(x,3)
    t11z <- n1.z*n.1z/nz
    if (sum((apply(x,3,sum,na.rm=T)>1))<3 |
    (sum(t11z,na.rm=T) - sum(pmax(0, (n1.z-n.2z),na.rm=T))) < 5 | (sum(pmin(n1.z, n.1z,na.rm=T)) - sum(t11z,na.rm=T)) < 5)
    {res=NA}else{res=mantelhaen.test(x,correct=T,exact=F)$p.value}
    }))
    
bingen2$mh_test5k <- bg4$mh_test

bg3 <- as_tibble(bingen) %>% select(contains("11k")) %>%
    mutate(test_mat=pmap(., function(BY1_11kR,BY1_11kL,BY2_11kR,BY2_11kL,SY1_11kR,SY1_11kL,SY2_11kR,SY2_11kL,BY3_11kR,BY3_11kL,SY3_11kR,SY3_11kL,...) {
        x111=BY1_11kR
        x211=BY1_11kL
        x121=SY1_11kR
        x221=SY1_11kL
        x112=BY2_11kR
        x212=BY2_11kL
        x122=SY2_11kR
        x222=SY2_11kL
        x113=BY3_11kR
        x213=BY3_11kL
        x123=SY3_11kR
        x223=SY3_11kL
        df= c(x111,x211,x121,x221,x112,x212,x122,x222,x113,x213,x123,x223)
        df2 <- array(df,dim=c(2,2,3))
        tempo <- list(c("R","L"),c("BY","SY"),c("rep1","rep2","rep3"))
        names(tempo) <- c("Dir","Strain","Rep")
        dimnames(df2) <- tempo
        return(df2)
        }))

bg4 <- bg3 %>% mutate(mh_test=map_dbl(test_mat, function(x) {
    x <- apply(x,c(1,2,3),as.numeric)
    n1.z <- margin.table(x[1,,],2)
    n.1z <- margin.table(x[,1,],2)
    n.2z<- margin.table(x[,2,],2)
    nz <- margin.table(x,3)
    t11z <- n1.z*n.1z/nz
    if (sum((apply(x,3,sum,na.rm=T)>1))<3 |
    (sum(t11z,na.rm=T) - sum(pmax(0, (n1.z-n.2z),na.rm=T))) < 5 | (sum(pmin(n1.z, n.1z,na.rm=T)) - sum(t11z,na.rm=T)) < 5)
    {res=NA}else{res=mantelhaen.test(x,correct=T,exact=F)$p.value}
    }))
    
bingen2$mh_test11k <- bg4$mh_test

bg3 <- as_tibble(bingen) %>% select(contains("21k")) %>%
    mutate(test_mat=pmap(., function(BY1_21kR,BY1_21kL,BY2_21kR,BY2_21kL,SY1_21kR,SY1_21kL,SY2_21kR,SY2_21kL,BY3_21kR,BY3_21kL,SY3_21kR,SY3_21kL,...) {
        x111=BY1_21kR
        x211=BY1_21kL
        x121=SY1_21kR
        x221=SY1_21kL
        x112=BY2_21kR
        x212=BY2_21kL
        x122=SY2_21kR
        x222=SY2_21kL
        x113=BY3_21kR
        x213=BY3_21kL
        x123=SY3_21kR
        x223=SY3_21kL
        df= c(x111,x211,x121,x221,x112,x212,x122,x222,x113,x213,x123,x223)
        df2 <- array(df,dim=c(2,2,3))
        tempo <- list(c("R","L"),c("BY","SY"),c("rep1","rep2","rep3"))
        names(tempo) <- c("Dir","Strain","Rep")
        dimnames(df2) <- tempo
        return(df2)
        }))

bg4 <- bg3 %>% mutate(mh_test=map_dbl(test_mat, function(x) {
    x <- apply(x,c(1,2,3),as.numeric)
    n1.z <- margin.table(x[1,,],2)
    n.1z <- margin.table(x[,1,],2)
    n.2z<- margin.table(x[,2,],2)
    nz <- margin.table(x,3)
    t11z <- n1.z*n.1z/nz
    if (sum((apply(x,3,sum,na.rm=T)>1))<3 |
    (sum(t11z,na.rm=T) - sum(pmax(0, (n1.z-n.2z),na.rm=T))) < 5 | (sum(pmin(n1.z, n.1z,na.rm=T)) - sum(t11z,na.rm=T)) < 5)
    {res=NA}else{res=mantelhaen.test(x,correct=T,exact=F)$p.value}
    }))
    
bingen2$mh_test21k <- bg4$mh_test

bg3 <- as_tibble(bingen) %>% select(contains("41k")) %>%
    mutate(test_mat=pmap(., function(BY1_41kR,BY1_41kL,BY2_41kR,BY2_41kL,SY1_41kR,SY1_41kL,SY2_41kR,SY2_41kL,BY3_41kR,BY3_41kL,SY3_41kR,SY3_41kL,...) {
        x111=BY1_41kR
        x211=BY1_41kL
        x121=SY1_41kR
        x221=SY1_41kL
        x112=BY2_41kR
        x212=BY2_41kL
        x122=SY2_41kR
        x222=SY2_41kL
        x113=BY3_41kR
        x213=BY3_41kL
        x123=SY3_41kR
        x223=SY3_41kL
        df= c(x111,x211,x121,x221,x112,x212,x122,x222,x113,x213,x123,x223)
        df2 <- array(df,dim=c(2,2,3))
        tempo <- list(c("R","L"),c("BY","SY"),c("rep1","rep2","rep3"))
        names(tempo) <- c("Dir","Strain","Rep")
        dimnames(df2) <- tempo
        return(df2)
        }))

bg4 <- bg3 %>% mutate(mh_test=map_dbl(test_mat, function(x) {
    x <- apply(x,c(1,2,3),as.numeric)
    n1.z <- margin.table(x[1,,],2)
    n.1z <- margin.table(x[,1,],2)
    n.2z<- margin.table(x[,2,],2)
    nz <- margin.table(x,3)
    t11z <- n1.z*n.1z/nz
    if (sum((apply(x,3,sum,na.rm=T)>1))<3 |
    (sum(t11z,na.rm=T) - sum(pmax(0, (n1.z-n.2z),na.rm=T))) < 5 | (sum(pmin(n1.z, n.1z,na.rm=T)) - sum(t11z,na.rm=T)) < 5)
    {res=NA}else{res=mantelhaen.test(x,correct=T,exact=F)$p.value}
    }))
    
bingen2$mh_test41k <- bg4$mh_test

multiple test correction and results export.

ptib3 <- as_tibble(mcols(bingen2)) %>% select(contains("mh_test")) %>% 
pivot_longer(contains("mh_test")) %>%
mutate(padj=p.adjust(value,"BY")) %>%
select(-"value") %>%
pivot_wider(names_from = name,values_from=c(padj),values_fn = list)

bingen2$mh_test3kcor <- ptib3$mh_test3k[[1]]
bingen2$mh_test5kcor <- ptib3$mh_test5k[[1]]
bingen2$mh_test11kcor <- ptib3$mh_test11k[[1]]
bingen2$mh_test21kcor <- ptib3$mh_test21k[[1]]
bingen2$mh_test41kcor <- ptib3$mh_test41k[[1]]

saveRDS(bingen2,file="Data/RFD_bg_MHtest_adjBY.rds")

export(coverage(bingen2,weight=bingen2$mh_test3kcor),con="BigWig/RFD_MHtest3k.bw")
export(coverage(bingen2,weight=bingen2$mh_test5kcor),con="BigWig/RFD_MHtest5k.bw")
export(coverage(bingen2,weight=bingen2$mh_test11kcor),con="BigWig/RFD_MHtest11k.bw")
export(coverage(bingen2,weight=bingen2$mh_test21kcor),con="BigWig/RFD_MHtest21k.bw")
export(coverage(bingen2,weight=bingen2$mh_test41kcor),con="BigWig/RFD_MHtest41k.bw")

Non pooled RFD export.

cvLBY1 <- coverage(forkBY1_L.gr)
cvRBY1 <- coverage(forkBY1_R.gr)
rfdBY1 <- (cvRBY1-cvLBY1)/(cvRBY1+cvLBY1)
export(rfdBY1,con="BigWig/rfdnt_BY1.bw")
export((cvLBY1+cvRBY1),con="BigWig/covTnt_BY1.bw")
cvLBY2 <- coverage(forkBY2_L.gr)
cvRBY2 <- coverage(forkBY2_R.gr)
rfdBY2 <- (cvRBY2-cvLBY2)/(cvRBY2+cvLBY2)
export(rfdBY2,con="BigWig/rfdnt_BY2.bw")
export((cvLBY2+cvRBY2),con="BigWig/covTnt_BY2.bw")
cvLSY1 <- coverage(forkSY1_L.gr)
cvRSY1 <- coverage(forkSY1_R.gr)
rfdSY1 <- (cvRSY1-cvLSY1)/(cvRSY1+cvLSY1)
export(rfdSY1,con="BigWig/rfdnt_SY1.bw")
export((cvLSY1+cvRSY1),con="BigWig/covTnt_SY1.bw")
cvLSY2 <- coverage(forkSY2_L.gr)
cvRSY2 <- coverage(forkSY2_R.gr)
rfdSY2 <- (cvRSY2-cvLSY2)/(cvRSY2+cvLSY2)
export(rfdSY2,con="BigWig/rfdnt_SY2.bw")
export((cvLSY2+cvRSY2),con="BigWig/covTnt_SY2.bw")
cvLBY3 <- coverage(forkBY3_L.gr)
cvRBY3 <- coverage(forkBY3_R.gr)
rfdBY3 <- (cvRBY3-cvLBY3)/(cvRBY3+cvLBY3)
export(rfdBY3,con="BigWig/rfdnt_BY3.bw")
export((cvLBY3+cvRBY3),con="BigWig/covTnt_BY3.bw")
cvLSY3 <- coverage(forkSY3_L.gr)
cvRSY3 <- coverage(forkSY3_R.gr)
rfdSY3 <- (cvRSY3-cvLSY3)/(cvRSY3+cvLSY3)
export(rfdSY3,con="BigWig/rfdnt_SY3.bw")
export((cvLSY3+cvRSY3),con="BigWig/covTnt_SY3.bw")

### I need to export the binned rfd for each experiment (1k)
cvLBY1 <- coverage(bingen,weight=bingen$BY1_01kL)
cvRBY1 <- coverage(bingen,weight=bingen$BY1_01kR)
rfdBY1 <- (cvRBY1-cvLBY1)/(cvRBY1+cvLBY1)
export(rfdBY1,con="BigWig/rfd1k_BY1.bw")
export((cvLBY1+cvRBY1),con="BigWig/covT1k_BY1.bw")
cvLBY2 <- coverage(bingen,weight=bingen$BY2_01kL)
cvRBY2 <- coverage(bingen,weight=bingen$BY2_01kR)
rfdBY2 <- (cvRBY2-cvLBY2)/(cvRBY2+cvLBY2)
export(rfdBY2,con="BigWig/rfd1k_BY2.bw")
export((cvLBY2+cvRBY2),con="BigWig/covT1k_BY2.bw")
cvLSY1 <- coverage(bingen,weight=bingen$SY1_01kL)
cvRSY1 <- coverage(bingen,weight=bingen$SY1_01kR)
rfdSY1 <- (cvRSY1-cvLSY1)/(cvRSY1+cvLSY1)
export(rfdSY1,con="BigWig/rfd1k_SY1.bw")
export((cvLSY1+cvRSY1),con="BigWig/covT1k_SY1.bw")
cvLSY2 <- coverage(bingen,weight=bingen$SY2_01kL)
cvRSY2 <- coverage(bingen,weight=bingen$SY2_01kR)
rfdSY2 <- (cvRSY2-cvLSY2)/(cvRSY2+cvLSY2)
export(rfdSY2,con="BigWig/rfd1k_SY2.bw")
export((cvLSY2+cvRSY2),con="BigWig/covT1k_SY2.bw")
cvLBY3 <- coverage(bingen,weight=bingen$BY3_01kL)
cvRBY3 <- coverage(bingen,weight=bingen$BY3_01kR)
rfdBY3 <- (cvRBY3-cvLBY3)/(cvRBY3+cvLBY3)
export(rfdBY3,con="BigWig/rfd1k_BY3.bw")
export((cvLBY3+cvRBY3),con="BigWig/covT1k_BY3.bw")
cvLSY3 <- coverage(bingen,weight=bingen$SY3_01kL)
cvRSY3 <- coverage(bingen,weight=bingen$SY3_01kR)
rfdSY3 <- (cvRSY3-cvLSY3)/(cvRSY3+cvLSY3)
export(rfdSY3,con="BigWig/rfd1k_SY3.bw")
export((cvLSY3+cvRSY3),con="BigWig/covT1k_SY3.bw")

pooling forks.

fBY$fid <- 1:nrow(fBY)
forkBY.gr <- with(fBY, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))

forkBY_R.gr <- forkBY.gr[strand(forkBY.gr)=="+"]
forkBY_L.gr <- forkBY.gr[strand(forkBY.gr)=="-"]

fSY$fid <- 1:nrow(fSY)
forkSY.gr<- with(fSY, GRanges(seqnames=chrom,ranges=IRanges(pmin(X0,X1),pmax(X0,X1)),strand=case_when(direc=="R"~"+",T~"-"),fid=fid,seqinfo=seqinfSY))

forkSY_R.gr <- forkSY.gr[strand(forkSY.gr)=="+"]
forkSY_L.gr <- forkSY.gr[strand(forkSY.gr)=="-"]

1kb binning.

bs <- 1000 
bingen <- tileGenome(seqinfSY,tilewidth=bs, cut.last.tile.in.chrom=T)
nc <- 4L
min_ovl <- 501L
ol1 <- findOverlaps(forkBY_R.gr,bingen,minoverlap=min_ovl)
bingen$forksBY1kR <- smclapply(1:length(bingen), function(x) forkBY_R.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkBY_L.gr,bingen,minoverlap=min_ovl)
bingen$forksBY1kL <- smclapply(1:length(bingen), function(x) forkBY_L.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkSY_R.gr,bingen,minoverlap=min_ovl)
bingen$forksSY1kR <- smclapply(1:length(bingen), function(x) forkSY_R.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)
ol1 <- findOverlaps(forkSY_L.gr,bingen,minoverlap=min_ovl)
bingen$forksSY1kL <- smclapply(1:length(bingen), function(x) forkSY_L.gr[queryHits(ol1)[subjectHits(ol1)==x]]$fid,mc.cores=nc)

### count unique forks
bingen$BY1kR <- sapply(bingen$forksBY1kR,function(x) length(unique(x[!is.na(x)])))
bingen$BY1kL <- sapply(bingen$forksBY1kL,function(x) length(unique(x[!is.na(x)])))
bingen$SY1kR <- sapply(bingen$forksSY1kR,function(x) length(unique(x[!is.na(x)])))
bingen$SY1kL <- sapply(bingen$forksSY1kL,function(x) length(unique(x[!is.na(x)])))

pooled 1kb binned RFD.

# covL_BY <- coverage(forkBY_L.gr)
# covR_BY <- coverage(forkBY_R.gr)
# rfdBY <- (covR_BY-covL_BY)/(covR_BY+covL_BY)
# export(rfdBY,con="BigWig/rfd_BYnt.bw")
# export(covR_BY+covL_BY,con="BigWig/covT_BYnt.bw")
# 
# covL_SY <- coverage(forkSY_L.gr)
# covR_SY <- coverage(forkSY_R.gr)
# rfdSY <- (covR_SY-covL_SY)/(covR_SY+covL_SY)
# export(rfdSY,con="BigWig/rfd_SYnt.bw")
# export(covR_SY+covL_SY,con="BigWig/covT_SYnt.bw")

cvLBY1k <- coverage(bingen,weight=bingen$BY1kL)
cvRBY1k <- coverage(bingen,weight=bingen$BY1kR)
rfdBY1k <- (cvRBY1k-cvLBY1k)/(cvRBY1k+cvLBY1k)
export(rfdBY1k,con="BigWig/rfd1k_BY.bw")
export((cvLBY1k+cvRBY1k),con="BigWig/covT1k_BY.bw")
cvLSY1k <- coverage(bingen,weight=bingen$SY1kL)
cvRSY1k <- coverage(bingen,weight=bingen$SY1kR)
rfdSY1k <- (cvRSY1k-cvLSY1k)/(cvRSY1k+cvLSY1k)
export(rfdSY1k,con="BigWig/rfd1k_SY.bw")
export((cvLSY1k+cvRSY1k),con="BigWig/covT1k_SY.bw")

Selecting significative RFD changes

In order to recapitulate these multiscales testing procedures, we choose to select the bin that were significant for at least 3 out of the 5 tested scales. And we decide to set the threshold for the p-value at 1e-2.


bg_MH <- readRDS("Data/RFD_bg_MHtest_adjBY.rds")
minp <- 1e-2

bg_MH2 <- as_tibble(bg_MH) %>% select(seqnames,start,end,contains("cor"))
bg_MH2 <- bg_MH2 %>% mutate(nbsigMH=pmap_int(., function(mh_test3kcor,mh_test5kcor,mh_test11kcor,mh_test21kcor,mh_test41kcor,...) {
        sum(mh_test3kcor<=minp,mh_test5kcor<=minp,mh_test11kcor<=minp,mh_test21kcor<=minp,mh_test41kcor<=minp)
        }))
        
bgsigMH <- with(bg_MH2 %>% filter(nbsigMH>=3),GRanges(seqnames=seqnames,range=IRanges(start,end),seqinfo=seqinfSY))
bgsigMH2 <- GenomicRanges::reduce(bgsigMH)

export(bgsigMH2,con="Data/bgsigMH.bed")

compute distance to CEN/Junction/Tel

CEN <- import("Genome_annotations/SY14_CEN.gff3") 
chromSY <- import("Genome_annotations/chromBYonSYgr.bed") %>% sort
seqinfo(chromSY) <- seqinfSY

chromJunction <- gaps(chromSY-10)
chromTrueJunction <- chromJunction[4:18]
TEL_SY <- chromJunction[c(3,19)]

bgsigMH2 <- import("Data/bgsigMH.bed")
bgsigMH2$score <- NULL
bgsigMH2$name <- NULL
bgsigMH2$dt_CEN <- data.frame(distanceToNearest(bgsigMH2,CEN))[,3]

bgsigMH2$dt_JUNC <- data.frame(distanceToNearest(bgsigMH2,chromTrueJunction))[,3]
bgsigMH2$dt_TEL <- data.frame(distanceToNearest(bgsigMH2,TEL_SY))[,3]

bgsigMH2$dt_nearest <- data.frame(distanceToNearest(bgsigMH2,c(TEL_SY,CEN,chromTrueJunction)))[,3]

write_tsv(as_tibble(bgsigMH2), file="Data/RFDsig.tsv")
write_tsv(as_tibble(bgsigMH2), file="Data/TableS3.tsv")
LS0tCnRpdGxlOiAiQllTWSBwcm9qZWN0IE5vdGVib29rIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tICAKIyAwN19SRkQgdGVzdCAgCioqKiAgCgpJbiBvcmRlciB0byB0ZXN0IGRpZmZlcmVuY2UgaW4gZm9ya3MgZGlyZWN0aW9uYWxpdHkgYmV0d2VlbiBCWSBhbmQgU1ksIGV4cGVyaW1lbnQgd2VyZSBlaXRoZXIgcG9vbGVkIGJ5IHN0cmFpbiBvciB0cmVhdGVkIGFzIHBhaXJlZCByZXBsaWNhdGVzLgpUaGUgZ2Vub21lIHdhcyBjdXQgaW50byBub24gb3ZlcmxhcHBpbmcgYmlucyBvZiAxa2IgYW5kIGZvciBlYWNoIGJpbiBhbmQgZWFjaCBjb25kaXRpb25zLCB3ZSBjb21wYXJlZCB0aGUgbnVtYmVyIG9mIEwgYW5kIFIgZm9ya3MgdXNpbmcgZWl0aGVyIHRoZSBleGFjdCBCb3NjaGxvbyB0ZXN0IGZvciB0aGUgcG9vbGVkIGFwcHJvYWNoIG9yIHRoZSBNYW50ZWxfSGFlbnN6ZWwgdGVzdCBpbiB0aGUgcGFpcmVkIGFwcHJvYWNoLiB0aGUgdGVzdHMgd2VyZSByZXBlYXRlZCB1c2luZyAzLCA1LCAxMSwgMjEgYW5kIDQxa2Igc2NhbGVzIGJ5IG1lcmdpbmcgdGhlIGNvdW50cyBmb3Igc3VjY2Vzc2l2ZSBiaW5zIGJ1dCBjb3VudGluZyBlYWNoIGZvcmsgb25seSBvbmNlIGZvciBlYWNrIGJpIGF0IGVhY2ggc2NhbGUuIFJlc3VsdHMgd2VyZSBjb3JyZWN0ZWQgZm9yIG11bHRpcGxpY2l0eSB0ZXN0aW5nIHVzaW5nIHRoZSBCZW5qYW1pbmkgYW5kIFlla3V0ZWxpIGNvcnJlY3Rpb24uCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoR2Vub21pY1JhbmdlcykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkocnRyYWNrbGF5ZXIpKQpgJSslYDwtcGFzdGUwCnNlcWluZlNZIDwtIHJlYWRSRFMoIkRhdGEvc2VxaW5mU1kucmRzIikKckROQV9TWSA8LSBHUmFuZ2VzKCJDUDAyOTE2MC4xIixJUmFuZ2VzKDM4Nzk5NDAsMzkzNDAwMCksc3RyYW5kPSIqIixzZXFpbmZvPXNlcWluZlNZKQptYXNrZWRUeV9TWSA8LSByZWFkUkRTKCJEYXRhL21hc2tlZFR5X1NZLnJkcyIpCm1hc2tlZF91cmEzZDAgPC0gR1JhbmdlcygiQ1AwMjkxNjAuMSIsSVJhbmdlcygxMDUxMjAwLDEwNTE1MDApLHN0cmFuZD0iKiIsc2VxaW5mbz1zZXFpbmZTWSx0eXBlPSJ1cmEzZDAiKQptYXNrZWRfU1kgPC0gYyhtYXNrZWRUeV9TWSxtYXNrZWRfdXJhM2QwKSAKc291cmNlKCJIZWxwZXJfZnVuY3Rpb24uciIpCmBgYAoKIyMjIEltcG9ydGluZyBmb3JrcwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpmb3Jrc0JZIDwtIHJlYWRSRFMoIkRhdGEvZm9ya3NfQllCWVNZLnJkcyIpICU+JQoJc2VsZWN0KGNocm9tPWNocm9tU1ksZGlyZWMsWDA9WDBuU1ksWDE9WDFuU1ksZXhwKSAlPiUKCW11dGF0ZShSZXA9bWFwX2NocihleHAsZnVuY3Rpb24oeCkgeCAlPiUgc3RyX3JlbW92ZSgiQllfIikpKSAlPiUKCW11dGF0ZShzdHJhaW49IkJZIikKZm9ya3NfQllTWS5ncjwtIHdpdGgoZm9ya3NCWSwgR1JhbmdlcyhzZXFuYW1lcz1jaHJvbSxyYW5nZXM9SVJhbmdlcyhwbWluKFgwLFgxKSxwbWF4KFgwLFgxKSksc3RyYW5kPWNhc2Vfd2hlbihkaXJlYz09IlIifiIrIixUfiItIiksc2VxaW5mbz1zZXFpbmZTWSkpCmZCWSA8LSBmb3Jrc0JZICU+JSAKCW11dGF0ZShmaWx0PSFvdmVybGFwc0FueShmb3Jrc19CWVNZLmdyLGMobWFza2VkX1NZKSkpICU+JQoJZmlsdGVyKGZpbHQpCgpmb3Jrc1NZIDwtIHJlYWRSRFMoIkRhdGEvZm9ya3NfU1lTWS5yZHMiKSAlPiUKCXNlbGVjdChjaHJvbSxkaXJlYyxYMCxYMSxleHApICU+JQoJbXV0YXRlKFJlcD1tYXBfY2hyKGV4cCxmdW5jdGlvbih4KSB4ICU+JSBzdHJfcmVtb3ZlKCJTWV8iKSkpICU+JQoJbXV0YXRlKHN0cmFpbj0iU1kiKQpmb3Jrc19TWVNZLmdyPC0gd2l0aChmb3Jrc1NZLCBHUmFuZ2VzKHNlcW5hbWVzPWNocm9tLHJhbmdlcz1JUmFuZ2VzKHBtaW4oWDAsWDEpLHBtYXgoWDAsWDEpKSxzdHJhbmQ9Y2FzZV93aGVuKGRpcmVjPT0iUiJ+IisiLFR+Ii0iKSxzZXFpbmZvPXNlcWluZlNZKSkKZlNZIDwtIGZvcmtzU1kgJT4lIAoJbXV0YXRlKGZpbHQ9IW92ZXJsYXBzQW55KGZvcmtzX1NZU1kuZ3IsYyhtYXNrZWRfU1kpKSkgJT4lCglmaWx0ZXIoZmlsdCkKYGBgCgojIyMgQ3JlYXRlIEdlbm9taWNSYW5nZXMgYXMgaW4gdGhlIE9FTSBzY3JpcHQuICAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZvcmtCWTEgPC0gZkJZICU+JSBmaWx0ZXIoUmVwPT0icmVwMSIpCmZvcmtCWTEkZmlkIDwtIDE6bnJvdyhmb3JrQlkxKQpmb3JrQlkxX1IgPC0gZm9ya0JZMSAlPiUgZmlsdGVyKGRpcmVjPT0iUiIpCmZvcmtCWTFfTCA8LSBmb3JrQlkxICU+JSBmaWx0ZXIoZGlyZWM9PSJMIikKZm9ya0JZMV9SLmdyPC0gd2l0aChmb3JrQlkxX1IsIEdSYW5nZXMoc2VxbmFtZXM9Y2hyb20scmFuZ2VzPUlSYW5nZXMocG1pbihYMCxYMSkscG1heChYMCxYMSkpLHN0cmFuZD1jYXNlX3doZW4oZGlyZWM9PSJSIn4iKyIsVH4iLSIpLGZpZD1maWQsc2VxaW5mbz1zZXFpbmZTWSkpCmZvcmtCWTFfTC5ncjwtIHdpdGgoZm9ya0JZMV9MLCBHUmFuZ2VzKHNlcW5hbWVzPWNocm9tLHJhbmdlcz1JUmFuZ2VzKHBtaW4oWDAsWDEpLHBtYXgoWDAsWDEpKSxzdHJhbmQ9Y2FzZV93aGVuKGRpcmVjPT0iUiJ+IisiLFR+Ii0iKSxmaWQ9ZmlkLHNlcWluZm89c2VxaW5mU1kpKQoKZm9ya0JZMiA8LSBmQlkgJT4lIGZpbHRlcihSZXA9PSJyZXAyIikKZm9ya0JZMiRmaWQgPC0gMTpucm93KGZvcmtCWTIpCmZvcmtCWTJfUiA8LSBmb3JrQlkyICU+JSBmaWx0ZXIoZGlyZWM9PSJSIikKZm9ya0JZMl9MIDwtIGZvcmtCWTIgJT4lIGZpbHRlcihkaXJlYz09IkwiKQpmb3JrQlkyX1IuZ3I8LSB3aXRoKGZvcmtCWTJfUiwgR1JhbmdlcyhzZXFuYW1lcz1jaHJvbSxyYW5nZXM9SVJhbmdlcyhwbWluKFgwLFgxKSxwbWF4KFgwLFgxKSksc3RyYW5kPWNhc2Vfd2hlbihkaXJlYz09IlIifiIrIixUfiItIiksZmlkPWZpZCxzZXFpbmZvPXNlcWluZlNZKSkKZm9ya0JZMl9MLmdyPC0gd2l0aChmb3JrQlkyX0wsIEdSYW5nZXMoc2VxbmFtZXM9Y2hyb20scmFuZ2VzPUlSYW5nZXMocG1pbihYMCxYMSkscG1heChYMCxYMSkpLHN0cmFuZD1jYXNlX3doZW4oZGlyZWM9PSJSIn4iKyIsVH4iLSIpLGZpZD1maWQsc2VxaW5mbz1zZXFpbmZTWSkpCgpmb3JrU1kxIDwtIGZTWSAlPiUgZmlsdGVyKFJlcD09InJlcDEiKQpmb3JrU1kxJGZpZCA8LSAxOm5yb3coZm9ya1NZMSkKZm9ya1NZMV9SIDwtIGZvcmtTWTEgJT4lIGZpbHRlcihkaXJlYz09IlIiKQpmb3JrU1kxX0wgPC0gZm9ya1NZMSAlPiUgZmlsdGVyKGRpcmVjPT0iTCIpCmZvcmtTWTFfUi5ncjwtIHdpdGgoZm9ya1NZMV9SLCBHUmFuZ2VzKHNlcW5hbWVzPWNocm9tLHJhbmdlcz1JUmFuZ2VzKHBtaW4oWDAsWDEpLHBtYXgoWDAsWDEpKSxzdHJhbmQ9Y2FzZV93aGVuKGRpcmVjPT0iUiJ+IisiLFR+Ii0iKSxmaWQ9ZmlkLHNlcWluZm89c2VxaW5mU1kpKQpmb3JrU1kxX0wuZ3I8LSB3aXRoKGZvcmtTWTFfTCwgR1JhbmdlcyhzZXFuYW1lcz1jaHJvbSxyYW5nZXM9SVJhbmdlcyhwbWluKFgwLFgxKSxwbWF4KFgwLFgxKSksc3RyYW5kPWNhc2Vfd2hlbihkaXJlYz09IlIifiIrIixUfiItIiksZmlkPWZpZCxzZXFpbmZvPXNlcWluZlNZKSkKCmZvcmtTWTIgPC0gZlNZICU+JSBmaWx0ZXIoUmVwPT0icmVwMiIpCmZvcmtTWTIkZmlkIDwtIDE6bnJvdyhmb3JrU1kyKQpmb3JrU1kyX1IgPC0gZm9ya1NZMiAlPiUgZmlsdGVyKGRpcmVjPT0iUiIpCmZvcmtTWTJfTCA8LSBmb3JrU1kyICU+JSBmaWx0ZXIoZGlyZWM9PSJMIikKZm9ya1NZMl9SLmdyPC0gd2l0aChmb3JrU1kyX1IsIEdSYW5nZXMoc2VxbmFtZXM9Y2hyb20scmFuZ2VzPUlSYW5nZXMocG1pbihYMCxYMSkscG1heChYMCxYMSkpLHN0cmFuZD1jYXNlX3doZW4oZGlyZWM9PSJSIn4iKyIsVH4iLSIpLGZpZD1maWQsc2VxaW5mbz1zZXFpbmZTWSkpCmZvcmtTWTJfTC5ncjwtIHdpdGgoZm9ya1NZMl9MLCBHUmFuZ2VzKHNlcW5hbWVzPWNocm9tLHJhbmdlcz1JUmFuZ2VzKHBtaW4oWDAsWDEpLHBtYXgoWDAsWDEpKSxzdHJhbmQ9Y2FzZV93aGVuKGRpcmVjPT0iUiJ+IisiLFR+Ii0iKSxmaWQ9ZmlkLHNlcWluZm89c2VxaW5mU1kpKQoKZm9ya0JZMyA8LSBmQlkgJT4lIGZpbHRlcihSZXA9PSJyZXAzIikKZm9ya0JZMyRmaWQgPC0gMTpucm93KGZvcmtCWTMpCmZvcmtCWTNfUiA8LSBmb3JrQlkzICU+JSBmaWx0ZXIoZGlyZWM9PSJSIikKZm9ya0JZM19MIDwtIGZvcmtCWTMgJT4lIGZpbHRlcihkaXJlYz09IkwiKQpmb3JrQlkzX1IuZ3I8LSB3aXRoKGZvcmtCWTNfUiwgR1JhbmdlcyhzZXFuYW1lcz1jaHJvbSxyYW5nZXM9SVJhbmdlcyhwbWluKFgwLFgxKSxwbWF4KFgwLFgxKSksc3RyYW5kPWNhc2Vfd2hlbihkaXJlYz09IlIifiIrIixUfiItIiksZmlkPWZpZCxzZXFpbmZvPXNlcWluZlNZKSkKZm9ya0JZM19MLmdyPC0gd2l0aChmb3JrQlkzX0wsIEdSYW5nZXMoc2VxbmFtZXM9Y2hyb20scmFuZ2VzPUlSYW5nZXMocG1pbihYMCxYMSkscG1heChYMCxYMSkpLHN0cmFuZD1jYXNlX3doZW4oZGlyZWM9PSJSIn4iKyIsVH4iLSIpLGZpZD1maWQsc2VxaW5mbz1zZXFpbmZTWSkpCgpmb3JrU1kzIDwtIGZTWSAlPiUgZmlsdGVyKFJlcD09InJlcDMiKQpmb3JrU1kzJGZpZCA8LSAxOm5yb3coZm9ya1NZMykKZm9ya1NZM19SIDwtIGZvcmtTWTMgJT4lIGZpbHRlcihkaXJlYz09IlIiKQpmb3JrU1kzX0wgPC0gZm9ya1NZMyAlPiUgZmlsdGVyKGRpcmVjPT0iTCIpCmZvcmtTWTNfUi5ncjwtIHdpdGgoZm9ya1NZM19SLCBHUmFuZ2VzKHNlcW5hbWVzPWNocm9tLHJhbmdlcz1JUmFuZ2VzKHBtaW4oWDAsWDEpLHBtYXgoWDAsWDEpKSxzdHJhbmQ9Y2FzZV93aGVuKGRpcmVjPT0iUiJ+IisiLFR+Ii0iKSxmaWQ9ZmlkLHNlcWluZm89c2VxaW5mU1kpKQpmb3JrU1kzX0wuZ3I8LSB3aXRoKGZvcmtTWTNfTCwgR1JhbmdlcyhzZXFuYW1lcz1jaHJvbSxyYW5nZXM9SVJhbmdlcyhwbWluKFgwLFgxKSxwbWF4KFgwLFgxKSksc3RyYW5kPWNhc2Vfd2hlbihkaXJlYz09IlIifiIrIixUfiItIiksZmlkPWZpZCxzZXFpbmZvPXNlcWluZlNZKSkKYGBgCgojIyMgMWtiIERhdGEgYmlubmluZwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpicyA8LSAxMDAwIApiaW5nZW4gPC0gdGlsZUdlbm9tZShzZXFpbmZTWSx0aWxld2lkdGg9YnMsIGN1dC5sYXN0LnRpbGUuaW4uY2hyb209VCkKbmMgPC0gOEwKbWluX292bCA8LSA1MDFMCm9sMSA8LSBmaW5kT3ZlcmxhcHMoZm9ya0JZMV9SLmdyLGJpbmdlbixtaW5vdmVybGFwPW1pbl9vdmwpCmJpbmdlbiRCWTFfMWtSIDwtIHNtY2xhcHBseSgxOmxlbmd0aChiaW5nZW4pLCBmdW5jdGlvbih4KSBmb3JrQlkxX1IuZ3JbcXVlcnlIaXRzKG9sMSlbc3ViamVjdEhpdHMob2wxKT09eF1dJGZpZCxtYy5jb3Jlcz1uYykKb2wxIDwtIGZpbmRPdmVybGFwcyhmb3JrQlkxX0wuZ3IsYmluZ2VuLG1pbm92ZXJsYXA9bWluX292bCkKYmluZ2VuJEJZMV8xa0wgPC0gc21jbGFwcGx5KDE6bGVuZ3RoKGJpbmdlbiksIGZ1bmN0aW9uKHgpIGZvcmtCWTFfTC5ncltxdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kZmlkLG1jLmNvcmVzPW5jKQoKb2wxIDwtIGZpbmRPdmVybGFwcyhmb3JrQlkyX1IuZ3IsYmluZ2VuLG1pbm92ZXJsYXA9bWluX292bCkKYmluZ2VuJEJZMl8xa1IgPC0gc21jbGFwcGx5KDE6bGVuZ3RoKGJpbmdlbiksIGZ1bmN0aW9uKHgpIGZvcmtCWTJfUi5ncltxdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kZmlkLG1jLmNvcmVzPW5jKQpvbDEgPC0gZmluZE92ZXJsYXBzKGZvcmtCWTJfTC5ncixiaW5nZW4sbWlub3ZlcmxhcD1taW5fb3ZsKQpiaW5nZW4kQlkyXzFrTCA8LSBzbWNsYXBwbHkoMTpsZW5ndGgoYmluZ2VuKSwgZnVuY3Rpb24oeCkgZm9ya0JZMl9MLmdyW3F1ZXJ5SGl0cyhvbDEpW3N1YmplY3RIaXRzKG9sMSk9PXhdXSRmaWQsbWMuY29yZXM9bmMpCgpvbDEgPC0gZmluZE92ZXJsYXBzKGZvcmtTWTFfUi5ncixiaW5nZW4sbWlub3ZlcmxhcD1taW5fb3ZsKQpiaW5nZW4kU1kxXzFrUiA8LSBzbWNsYXBwbHkoMTpsZW5ndGgoYmluZ2VuKSwgZnVuY3Rpb24oeCkgZm9ya1NZMV9SLmdyW3F1ZXJ5SGl0cyhvbDEpW3N1YmplY3RIaXRzKG9sMSk9PXhdXSRmaWQsbWMuY29yZXM9bmMpCm9sMSA8LSBmaW5kT3ZlcmxhcHMoZm9ya1NZMV9MLmdyLGJpbmdlbixtaW5vdmVybGFwPW1pbl9vdmwpCmJpbmdlbiRTWTFfMWtMIDwtIHNtY2xhcHBseSgxOmxlbmd0aChiaW5nZW4pLCBmdW5jdGlvbih4KSBmb3JrU1kxX0wuZ3JbcXVlcnlIaXRzKG9sMSlbc3ViamVjdEhpdHMob2wxKT09eF1dJGZpZCxtYy5jb3Jlcz1uYykKCm9sMSA8LSBmaW5kT3ZlcmxhcHMoZm9ya1NZMl9SLmdyLGJpbmdlbixtaW5vdmVybGFwPW1pbl9vdmwpCmJpbmdlbiRTWTJfMWtSIDwtIHNtY2xhcHBseSgxOmxlbmd0aChiaW5nZW4pLCBmdW5jdGlvbih4KSBmb3JrU1kyX1IuZ3JbcXVlcnlIaXRzKG9sMSlbc3ViamVjdEhpdHMob2wxKT09eF1dJGZpZCxtYy5jb3Jlcz1uYykKb2wxIDwtIGZpbmRPdmVybGFwcyhmb3JrU1kyX0wuZ3IsYmluZ2VuLG1pbm92ZXJsYXA9bWluX292bCkKYmluZ2VuJFNZMl8xa0wgPC0gc21jbGFwcGx5KDE6bGVuZ3RoKGJpbmdlbiksIGZ1bmN0aW9uKHgpIGZvcmtTWTJfTC5ncltxdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kZmlkLG1jLmNvcmVzPW5jKQoKb2wxIDwtIGZpbmRPdmVybGFwcyhmb3JrQlkzX1IuZ3IsYmluZ2VuLG1pbm92ZXJsYXA9bWluX292bCkKYmluZ2VuJEJZM18xa1IgPC0gc21jbGFwcGx5KDE6bGVuZ3RoKGJpbmdlbiksIGZ1bmN0aW9uKHgpIGZvcmtCWTNfUi5ncltxdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kZmlkLG1jLmNvcmVzPW5jKQpvbDEgPC0gZmluZE92ZXJsYXBzKGZvcmtCWTNfTC5ncixiaW5nZW4sbWlub3ZlcmxhcD1taW5fb3ZsKQpiaW5nZW4kQlkzXzFrTCA8LSBzbWNsYXBwbHkoMTpsZW5ndGgoYmluZ2VuKSwgZnVuY3Rpb24oeCkgZm9ya0JZM19MLmdyW3F1ZXJ5SGl0cyhvbDEpW3N1YmplY3RIaXRzKG9sMSk9PXhdXSRmaWQsbWMuY29yZXM9bmMpCgpvbDEgPC0gZmluZE92ZXJsYXBzKGZvcmtTWTNfUi5ncixiaW5nZW4sbWlub3ZlcmxhcD1taW5fb3ZsKQpiaW5nZW4kU1kzXzFrUiA8LSBzbWNsYXBwbHkoMTpsZW5ndGgoYmluZ2VuKSwgZnVuY3Rpb24oeCkgZm9ya1NZM19SLmdyW3F1ZXJ5SGl0cyhvbDEpW3N1YmplY3RIaXRzKG9sMSk9PXhdXSRmaWQsbWMuY29yZXM9bmMpCm9sMSA8LSBmaW5kT3ZlcmxhcHMoZm9ya1NZM19MLmdyLGJpbmdlbixtaW5vdmVybGFwPW1pbl9vdmwpCmJpbmdlbiRTWTNfMWtMIDwtIHNtY2xhcHBseSgxOmxlbmd0aChiaW5nZW4pLCBmdW5jdGlvbih4KSBmb3JrU1kzX0wuZ3JbcXVlcnlIaXRzKG9sMSlbc3ViamVjdEhpdHMob2wxKT09eF1dJGZpZCxtYy5jb3Jlcz1uYykKYGBgCgojIyMgT3RoZXIgc2NhbGVzIGJpbm5pbmcKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kcm9sbF9jIDwtIGZ1bmN0aW9uKHgsd2luPTMpCnsKIyB4IGlzIGEgbGlzdAoJcmVzMCA8LSBsYXBwbHkoMToobGVuZ3RoKHgpLXdpbisxKSwgZnVuY3Rpb24oaSkgZG8uY2FsbChjLHhbaTooaSt3aW4tMSldKSkKCWlmICh3aW4lJTI9PTApIAoJe3JlcyA8LSBjKHJlcChsaXN0KE5BKSx3aW4lLyUyLTEpLHJlczAscmVwKGxpc3QoTkEpLHdpbiUvJTIpKQoJfWVsc2V7cmVzIDwtIGMocmVwKGxpc3QoTkEpLHdpbiUvJTIpLHJlczAscmVwKGxpc3QoTkEpLHdpbiUvJTIpKQoJfQpyZXR1cm4ocmVzKQp9CgpiaW5nZW4kQlkxXzAxa1IgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkxXzFrUix3aW49MSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJEJZMV8wMWtMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMV8xa0wsd2luPTEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTJfMDFrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTJfMWtSLHdpbj0xKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkyXzAxa0wgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkyXzFrTCx3aW49MSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZMV8wMWtSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMV8xa1Isd2luPTEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTFfMDFrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTFfMWtMLHdpbj0xKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kyXzAxa1IgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kU1kyXzFrUix3aW49MSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZMl8wMWtMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMl8xa0wsd2luPTEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTNfMDFrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTNfMWtSLHdpbj0xKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkzXzAxa0wgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkzXzFrTCx3aW49MSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZM18wMWtSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZM18xa1Isd2luPTEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTNfMDFrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTNfMWtMLHdpbj0xKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQoKCmJpbmdlbiRCWTFfM2tSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMV8xa1Isd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTFfM2tMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMV8xa0wsd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTJfM2tSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMl8xa1Isd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTJfM2tMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMl8xa0wsd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTFfM2tSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMV8xa1Isd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTFfM2tMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMV8xa0wsd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTJfM2tSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMl8xa1Isd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTJfM2tMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMl8xa0wsd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTNfM2tSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZM18xa1Isd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTNfM2tMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZM18xa0wsd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTNfM2tSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZM18xa1Isd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTNfM2tMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZM18xa0wsd2luPTMpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCgpiaW5nZW4kQlkxXzVrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTFfMWtSLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkxXzVrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTFfMWtMLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkyXzVrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTJfMWtSLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkyXzVrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTJfMWtMLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kxXzVrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTFfMWtSLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kxXzVrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTFfMWtMLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kyXzVrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTJfMWtSLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kyXzVrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTJfMWtMLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkzXzVrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTNfMWtSLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkzXzVrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTNfMWtMLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kzXzVrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTNfMWtSLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kzXzVrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTNfMWtMLHdpbj01KSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQoKYmluZ2VuJEJZMV8xMWtSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMV8xa1Isd2luPTExKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkxXzExa0wgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkxXzFrTCx3aW49MTEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTJfMTFrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTJfMWtSLHdpbj0xMSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJEJZMl8xMWtMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMl8xa0wsd2luPTExKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kxXzExa1IgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kU1kxXzFrUix3aW49MTEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTFfMTFrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTFfMWtMLHdpbj0xMSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZMl8xMWtSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMl8xa1Isd2luPTExKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kyXzExa0wgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kU1kyXzFrTCx3aW49MTEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTNfMTFrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTNfMWtSLHdpbj0xMSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJEJZM18xMWtMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZM18xa0wsd2luPTExKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kzXzExa1IgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kU1kzXzFrUix3aW49MTEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTNfMTFrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTNfMWtMLHdpbj0xMSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKCmJpbmdlbiRCWTFfMjFrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTFfMWtSLHdpbj0yMSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJEJZMV8yMWtMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMV8xa0wsd2luPTIxKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkyXzIxa1IgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkyXzFrUix3aW49MjEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTJfMjFrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTJfMWtMLHdpbj0yMSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZMV8yMWtSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMV8xa1Isd2luPTIxKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kxXzIxa0wgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kU1kxXzFrTCx3aW49MjEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTJfMjFrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTJfMWtSLHdpbj0yMSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZMl8yMWtMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMl8xa0wsd2luPTIxKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkzXzIxa1IgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkzXzFrUix3aW49MjEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTNfMjFrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTNfMWtMLHdpbj0yMSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZM18yMWtSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZM18xa1Isd2luPTIxKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kzXzIxa0wgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kU1kzXzFrTCx3aW49MjEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCgpiaW5nZW4kQlkxXzQxa1IgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkxXzFrUix3aW49NDEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTFfNDFrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRCWTFfMWtMLHdpbj00MSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJEJZMl80MWtSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZMl8xa1Isd2luPTQxKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkyXzQxa0wgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkyXzFrTCx3aW49NDEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTFfNDFrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTFfMWtSLHdpbj00MSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZMV80MWtMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZMV8xa0wsd2luPTQxKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kyXzQxa1IgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kU1kyXzFrUix3aW49NDEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTJfNDFrTCA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTJfMWtMLHdpbj00MSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJEJZM180MWtSIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJEJZM18xa1Isd2luPTQxKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kQlkzXzQxa0wgPC0gc2FwcGx5KHJvbGxfYyhiaW5nZW4kQlkzXzFrTCx3aW49NDEpLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRTWTNfNDFrUiA8LSBzYXBwbHkocm9sbF9jKGJpbmdlbiRTWTNfMWtSLHdpbj00MSksZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZM180MWtMIDwtIHNhcHBseShyb2xsX2MoYmluZ2VuJFNZM18xa0wsd2luPTQxKSxmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpgYGAKCiMjIyBQYWlyZWQgdGVzdCBhZnRlciB0cmFuc2Zvcm1pbmcgdGhlIGRhdGEgaW50byAzRCBkYXRhIGZyYW1lLiAgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmJpbmdlbjIgPC0gYmluZ2VuCgpiZzMgPC0gYXNfdGliYmxlKGJpbmdlbikgJT4lIHNlbGVjdChjb250YWlucygiM2siKSkgJT4lCgltdXRhdGUodGVzdF9tYXQ9cG1hcCguLCBmdW5jdGlvbihCWTFfM2tSLEJZMV8za0wsQlkyXzNrUixCWTJfM2tMLFNZMV8za1IsU1kxXzNrTCxTWTJfM2tSLFNZMl8za0wsQlkzXzNrUixCWTNfM2tMLFNZM18za1IsU1kzXzNrTCwuLi4pIHsKCQl4MTExPUJZMV8za1IKCQl4MjExPUJZMV8za0wKCQl4MTIxPVNZMV8za1IKCQl4MjIxPVNZMV8za0wKCQl4MTEyPUJZMl8za1IKCQl4MjEyPUJZMl8za0wKCQl4MTIyPVNZMl8za1IKCQl4MjIyPVNZMl8za0wKCQl4MTEzPUJZM18za1IKCQl4MjEzPUJZM18za0wKCQl4MTIzPVNZM18za1IKCQl4MjIzPVNZM18za0wKCQlkZj0gYyh4MTExLHgyMTEseDEyMSx4MjIxLHgxMTIseDIxMix4MTIyLHgyMjIseDExMyx4MjEzLHgxMjMseDIyMykKCQlkZjIgPC0gYXJyYXkoZGYsZGltPWMoMiwyLDMpKQoJCXRlbXBvIDwtIGxpc3QoYygiUiIsIkwiKSxjKCJCWSIsIlNZIiksYygicmVwMSIsInJlcDIiLCJyZXAzIikpCgkJbmFtZXModGVtcG8pIDwtIGMoIkRpciIsIlN0cmFpbiIsIlJlcCIpCgkJZGltbmFtZXMoZGYyKSA8LSB0ZW1wbwoJCXJldHVybihkZjIpCgkJfSkpCmJnNCA8LSBiZzMgJT4lIG11dGF0ZShtaF90ZXN0PW1hcF9kYmwodGVzdF9tYXQsIGZ1bmN0aW9uKHgpIHsKCXggPC0gYXBwbHkoeCxjKDEsMiwzKSxhcy5udW1lcmljKQoJbjEueiA8LSBtYXJnaW4udGFibGUoeFsxLCxdLDIpCgluLjF6IDwtIG1hcmdpbi50YWJsZSh4WywxLF0sMikKCW4uMno8LSBtYXJnaW4udGFibGUoeFssMixdLDIpCglueiA8LSBtYXJnaW4udGFibGUoeCwzKQoJdDExeiA8LSBuMS56Km4uMXovbnoKCWlmIChzdW0oKGFwcGx5KHgsMyxzdW0sbmEucm09VCk+MSkpPDMgfAoJKHN1bSh0MTF6LG5hLnJtPVQpIC0gc3VtKHBtYXgoMCwgKG4xLnotbi4yeiksbmEucm09VCkpKSA8IDUgfCAoc3VtKHBtaW4objEueiwgbi4xeixuYS5ybT1UKSkgLSBzdW0odDExeixuYS5ybT1UKSkgPCA1KQoJe3Jlcz1OQX1lbHNle3Jlcz1tYW50ZWxoYWVuLnRlc3QoeCxjb3JyZWN0PVQsZXhhY3Q9RikkcC52YWx1ZX0KCX0pKQoJCmJpbmdlbjIkbWhfdGVzdDNrIDwtIGJnNCRtaF90ZXN0CgpiZzMgPC0gYXNfdGliYmxlKGJpbmdlbikgJT4lIHNlbGVjdChjb250YWlucygiNWsiKSkgJT4lCgltdXRhdGUodGVzdF9tYXQ9cG1hcCguLCBmdW5jdGlvbihCWTFfNWtSLEJZMV81a0wsQlkyXzVrUixCWTJfNWtMLFNZMV81a1IsU1kxXzVrTCxTWTJfNWtSLFNZMl81a0wsQlkzXzVrUixCWTNfNWtMLFNZM181a1IsU1kzXzVrTCwuLi4pIHsKCQl4MTExPUJZMV81a1IKCQl4MjExPUJZMV81a0wKCQl4MTIxPVNZMV81a1IKCQl4MjIxPVNZMV81a0wKCQl4MTEyPUJZMl81a1IKCQl4MjEyPUJZMl81a0wKCQl4MTIyPVNZMl81a1IKCQl4MjIyPVNZMl81a0wKCQl4MTEzPUJZM181a1IKCQl4MjEzPUJZM181a0wKCQl4MTIzPVNZM181a1IKCQl4MjIzPVNZM181a0wKCQlkZj0gYyh4MTExLHgyMTEseDEyMSx4MjIxLHgxMTIseDIxMix4MTIyLHgyMjIseDExMyx4MjEzLHgxMjMseDIyMykKCQlkZjIgPC0gYXJyYXkoZGYsZGltPWMoMiwyLDMpKQoJCXRlbXBvIDwtIGxpc3QoYygiUiIsIkwiKSxjKCJCWSIsIlNZIiksYygicmVwMSIsInJlcDIiLCJyZXAzIikpCgkJbmFtZXModGVtcG8pIDwtIGMoIkRpciIsIlN0cmFpbiIsIlJlcCIpCgkJZGltbmFtZXMoZGYyKSA8LSB0ZW1wbwoJCXJldHVybihkZjIpCgkJfSkpCgpiZzQgPC0gYmczICU+JSBtdXRhdGUobWhfdGVzdD1tYXBfZGJsKHRlc3RfbWF0LCBmdW5jdGlvbih4KSB7Cgl4IDwtIGFwcGx5KHgsYygxLDIsMyksYXMubnVtZXJpYykKCW4xLnogPC0gbWFyZ2luLnRhYmxlKHhbMSwsXSwyKQoJbi4xeiA8LSBtYXJnaW4udGFibGUoeFssMSxdLDIpCgluLjJ6PC0gbWFyZ2luLnRhYmxlKHhbLDIsXSwyKQoJbnogPC0gbWFyZ2luLnRhYmxlKHgsMykKCXQxMXogPC0gbjEueipuLjF6L256CglpZiAoc3VtKChhcHBseSh4LDMsc3VtLG5hLnJtPVQpPjEpKTwzIHwKCShzdW0odDExeixuYS5ybT1UKSAtIHN1bShwbWF4KDAsIChuMS56LW4uMnopLG5hLnJtPVQpKSkgPCA1IHwgKHN1bShwbWluKG4xLnosIG4uMXosbmEucm09VCkpIC0gc3VtKHQxMXosbmEucm09VCkpIDwgNSkKCXtyZXM9TkF9ZWxzZXtyZXM9bWFudGVsaGFlbi50ZXN0KHgsY29ycmVjdD1ULGV4YWN0PUYpJHAudmFsdWV9Cgl9KSkKCQpiaW5nZW4yJG1oX3Rlc3Q1ayA8LSBiZzQkbWhfdGVzdAoKYmczIDwtIGFzX3RpYmJsZShiaW5nZW4pICU+JSBzZWxlY3QoY29udGFpbnMoIjExayIpKSAlPiUKCW11dGF0ZSh0ZXN0X21hdD1wbWFwKC4sIGZ1bmN0aW9uKEJZMV8xMWtSLEJZMV8xMWtMLEJZMl8xMWtSLEJZMl8xMWtMLFNZMV8xMWtSLFNZMV8xMWtMLFNZMl8xMWtSLFNZMl8xMWtMLEJZM18xMWtSLEJZM18xMWtMLFNZM18xMWtSLFNZM18xMWtMLC4uLikgewoJCXgxMTE9QlkxXzExa1IKCQl4MjExPUJZMV8xMWtMCgkJeDEyMT1TWTFfMTFrUgoJCXgyMjE9U1kxXzExa0wKCQl4MTEyPUJZMl8xMWtSCgkJeDIxMj1CWTJfMTFrTAoJCXgxMjI9U1kyXzExa1IKCQl4MjIyPVNZMl8xMWtMCgkJeDExMz1CWTNfMTFrUgoJCXgyMTM9QlkzXzExa0wKCQl4MTIzPVNZM18xMWtSCgkJeDIyMz1TWTNfMTFrTAoJCWRmPSBjKHgxMTEseDIxMSx4MTIxLHgyMjEseDExMix4MjEyLHgxMjIseDIyMix4MTEzLHgyMTMseDEyMyx4MjIzKQoJCWRmMiA8LSBhcnJheShkZixkaW09YygyLDIsMykpCgkJdGVtcG8gPC0gbGlzdChjKCJSIiwiTCIpLGMoIkJZIiwiU1kiKSxjKCJyZXAxIiwicmVwMiIsInJlcDMiKSkKCQluYW1lcyh0ZW1wbykgPC0gYygiRGlyIiwiU3RyYWluIiwiUmVwIikKCQlkaW1uYW1lcyhkZjIpIDwtIHRlbXBvCgkJcmV0dXJuKGRmMikKCQl9KSkKCmJnNCA8LSBiZzMgJT4lIG11dGF0ZShtaF90ZXN0PW1hcF9kYmwodGVzdF9tYXQsIGZ1bmN0aW9uKHgpIHsKCXggPC0gYXBwbHkoeCxjKDEsMiwzKSxhcy5udW1lcmljKQoJbjEueiA8LSBtYXJnaW4udGFibGUoeFsxLCxdLDIpCgluLjF6IDwtIG1hcmdpbi50YWJsZSh4WywxLF0sMikKCW4uMno8LSBtYXJnaW4udGFibGUoeFssMixdLDIpCglueiA8LSBtYXJnaW4udGFibGUoeCwzKQoJdDExeiA8LSBuMS56Km4uMXovbnoKCWlmIChzdW0oKGFwcGx5KHgsMyxzdW0sbmEucm09VCk+MSkpPDMgfAoJKHN1bSh0MTF6LG5hLnJtPVQpIC0gc3VtKHBtYXgoMCwgKG4xLnotbi4yeiksbmEucm09VCkpKSA8IDUgfCAoc3VtKHBtaW4objEueiwgbi4xeixuYS5ybT1UKSkgLSBzdW0odDExeixuYS5ybT1UKSkgPCA1KQoJe3Jlcz1OQX1lbHNle3Jlcz1tYW50ZWxoYWVuLnRlc3QoeCxjb3JyZWN0PVQsZXhhY3Q9RikkcC52YWx1ZX0KCX0pKQoJCmJpbmdlbjIkbWhfdGVzdDExayA8LSBiZzQkbWhfdGVzdAoKYmczIDwtIGFzX3RpYmJsZShiaW5nZW4pICU+JSBzZWxlY3QoY29udGFpbnMoIjIxayIpKSAlPiUKCW11dGF0ZSh0ZXN0X21hdD1wbWFwKC4sIGZ1bmN0aW9uKEJZMV8yMWtSLEJZMV8yMWtMLEJZMl8yMWtSLEJZMl8yMWtMLFNZMV8yMWtSLFNZMV8yMWtMLFNZMl8yMWtSLFNZMl8yMWtMLEJZM18yMWtSLEJZM18yMWtMLFNZM18yMWtSLFNZM18yMWtMLC4uLikgewoJCXgxMTE9QlkxXzIxa1IKCQl4MjExPUJZMV8yMWtMCgkJeDEyMT1TWTFfMjFrUgoJCXgyMjE9U1kxXzIxa0wKCQl4MTEyPUJZMl8yMWtSCgkJeDIxMj1CWTJfMjFrTAoJCXgxMjI9U1kyXzIxa1IKCQl4MjIyPVNZMl8yMWtMCgkJeDExMz1CWTNfMjFrUgoJCXgyMTM9QlkzXzIxa0wKCQl4MTIzPVNZM18yMWtSCgkJeDIyMz1TWTNfMjFrTAoJCWRmPSBjKHgxMTEseDIxMSx4MTIxLHgyMjEseDExMix4MjEyLHgxMjIseDIyMix4MTEzLHgyMTMseDEyMyx4MjIzKQoJCWRmMiA8LSBhcnJheShkZixkaW09YygyLDIsMykpCgkJdGVtcG8gPC0gbGlzdChjKCJSIiwiTCIpLGMoIkJZIiwiU1kiKSxjKCJyZXAxIiwicmVwMiIsInJlcDMiKSkKCQluYW1lcyh0ZW1wbykgPC0gYygiRGlyIiwiU3RyYWluIiwiUmVwIikKCQlkaW1uYW1lcyhkZjIpIDwtIHRlbXBvCgkJcmV0dXJuKGRmMikKCQl9KSkKCmJnNCA8LSBiZzMgJT4lIG11dGF0ZShtaF90ZXN0PW1hcF9kYmwodGVzdF9tYXQsIGZ1bmN0aW9uKHgpIHsKCXggPC0gYXBwbHkoeCxjKDEsMiwzKSxhcy5udW1lcmljKQoJbjEueiA8LSBtYXJnaW4udGFibGUoeFsxLCxdLDIpCgluLjF6IDwtIG1hcmdpbi50YWJsZSh4WywxLF0sMikKCW4uMno8LSBtYXJnaW4udGFibGUoeFssMixdLDIpCglueiA8LSBtYXJnaW4udGFibGUoeCwzKQoJdDExeiA8LSBuMS56Km4uMXovbnoKCWlmIChzdW0oKGFwcGx5KHgsMyxzdW0sbmEucm09VCk+MSkpPDMgfAoJKHN1bSh0MTF6LG5hLnJtPVQpIC0gc3VtKHBtYXgoMCwgKG4xLnotbi4yeiksbmEucm09VCkpKSA8IDUgfCAoc3VtKHBtaW4objEueiwgbi4xeixuYS5ybT1UKSkgLSBzdW0odDExeixuYS5ybT1UKSkgPCA1KQoJe3Jlcz1OQX1lbHNle3Jlcz1tYW50ZWxoYWVuLnRlc3QoeCxjb3JyZWN0PVQsZXhhY3Q9RikkcC52YWx1ZX0KCX0pKQoJCmJpbmdlbjIkbWhfdGVzdDIxayA8LSBiZzQkbWhfdGVzdAoKYmczIDwtIGFzX3RpYmJsZShiaW5nZW4pICU+JSBzZWxlY3QoY29udGFpbnMoIjQxayIpKSAlPiUKCW11dGF0ZSh0ZXN0X21hdD1wbWFwKC4sIGZ1bmN0aW9uKEJZMV80MWtSLEJZMV80MWtMLEJZMl80MWtSLEJZMl80MWtMLFNZMV80MWtSLFNZMV80MWtMLFNZMl80MWtSLFNZMl80MWtMLEJZM180MWtSLEJZM180MWtMLFNZM180MWtSLFNZM180MWtMLC4uLikgewoJCXgxMTE9QlkxXzQxa1IKCQl4MjExPUJZMV80MWtMCgkJeDEyMT1TWTFfNDFrUgoJCXgyMjE9U1kxXzQxa0wKCQl4MTEyPUJZMl80MWtSCgkJeDIxMj1CWTJfNDFrTAoJCXgxMjI9U1kyXzQxa1IKCQl4MjIyPVNZMl80MWtMCgkJeDExMz1CWTNfNDFrUgoJCXgyMTM9QlkzXzQxa0wKCQl4MTIzPVNZM180MWtSCgkJeDIyMz1TWTNfNDFrTAoJCWRmPSBjKHgxMTEseDIxMSx4MTIxLHgyMjEseDExMix4MjEyLHgxMjIseDIyMix4MTEzLHgyMTMseDEyMyx4MjIzKQoJCWRmMiA8LSBhcnJheShkZixkaW09YygyLDIsMykpCgkJdGVtcG8gPC0gbGlzdChjKCJSIiwiTCIpLGMoIkJZIiwiU1kiKSxjKCJyZXAxIiwicmVwMiIsInJlcDMiKSkKCQluYW1lcyh0ZW1wbykgPC0gYygiRGlyIiwiU3RyYWluIiwiUmVwIikKCQlkaW1uYW1lcyhkZjIpIDwtIHRlbXBvCgkJcmV0dXJuKGRmMikKCQl9KSkKCmJnNCA8LSBiZzMgJT4lIG11dGF0ZShtaF90ZXN0PW1hcF9kYmwodGVzdF9tYXQsIGZ1bmN0aW9uKHgpIHsKCXggPC0gYXBwbHkoeCxjKDEsMiwzKSxhcy5udW1lcmljKQoJbjEueiA8LSBtYXJnaW4udGFibGUoeFsxLCxdLDIpCgluLjF6IDwtIG1hcmdpbi50YWJsZSh4WywxLF0sMikKCW4uMno8LSBtYXJnaW4udGFibGUoeFssMixdLDIpCglueiA8LSBtYXJnaW4udGFibGUoeCwzKQoJdDExeiA8LSBuMS56Km4uMXovbnoKCWlmIChzdW0oKGFwcGx5KHgsMyxzdW0sbmEucm09VCk+MSkpPDMgfAoJKHN1bSh0MTF6LG5hLnJtPVQpIC0gc3VtKHBtYXgoMCwgKG4xLnotbi4yeiksbmEucm09VCkpKSA8IDUgfCAoc3VtKHBtaW4objEueiwgbi4xeixuYS5ybT1UKSkgLSBzdW0odDExeixuYS5ybT1UKSkgPCA1KQoJe3Jlcz1OQX1lbHNle3Jlcz1tYW50ZWxoYWVuLnRlc3QoeCxjb3JyZWN0PVQsZXhhY3Q9RikkcC52YWx1ZX0KCX0pKQoJCmJpbmdlbjIkbWhfdGVzdDQxayA8LSBiZzQkbWhfdGVzdApgYGAKCiMjIyBtdWx0aXBsZSB0ZXN0IGNvcnJlY3Rpb24gYW5kIHJlc3VsdHMgZXhwb3J0LiAgCmBgYHtyfQpwdGliMyA8LSBhc190aWJibGUobWNvbHMoYmluZ2VuMikpICU+JSBzZWxlY3QoY29udGFpbnMoIm1oX3Rlc3QiKSkgJT4lIApwaXZvdF9sb25nZXIoY29udGFpbnMoIm1oX3Rlc3QiKSkgJT4lCm11dGF0ZShwYWRqPXAuYWRqdXN0KHZhbHVlLCJCWSIpKSAlPiUKc2VsZWN0KC0idmFsdWUiKSAlPiUKcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG5hbWUsdmFsdWVzX2Zyb209YyhwYWRqKSx2YWx1ZXNfZm4gPSBsaXN0KQoKYmluZ2VuMiRtaF90ZXN0M2tjb3IgPC0gcHRpYjMkbWhfdGVzdDNrW1sxXV0KYmluZ2VuMiRtaF90ZXN0NWtjb3IgPC0gcHRpYjMkbWhfdGVzdDVrW1sxXV0KYmluZ2VuMiRtaF90ZXN0MTFrY29yIDwtIHB0aWIzJG1oX3Rlc3QxMWtbWzFdXQpiaW5nZW4yJG1oX3Rlc3QyMWtjb3IgPC0gcHRpYjMkbWhfdGVzdDIxa1tbMV1dCmJpbmdlbjIkbWhfdGVzdDQxa2NvciA8LSBwdGliMyRtaF90ZXN0NDFrW1sxXV0KCnNhdmVSRFMoYmluZ2VuMixmaWxlPSJEYXRhL1JGRF9iZ19NSHRlc3RfYWRqQlkucmRzIikKCmV4cG9ydChjb3ZlcmFnZShiaW5nZW4yLHdlaWdodD1iaW5nZW4yJG1oX3Rlc3Qza2NvciksY29uPSJCaWdXaWcvUkZEX01IdGVzdDNrLmJ3IikKZXhwb3J0KGNvdmVyYWdlKGJpbmdlbjIsd2VpZ2h0PWJpbmdlbjIkbWhfdGVzdDVrY29yKSxjb249IkJpZ1dpZy9SRkRfTUh0ZXN0NWsuYnciKQpleHBvcnQoY292ZXJhZ2UoYmluZ2VuMix3ZWlnaHQ9YmluZ2VuMiRtaF90ZXN0MTFrY29yKSxjb249IkJpZ1dpZy9SRkRfTUh0ZXN0MTFrLmJ3IikKZXhwb3J0KGNvdmVyYWdlKGJpbmdlbjIsd2VpZ2h0PWJpbmdlbjIkbWhfdGVzdDIxa2NvciksY29uPSJCaWdXaWcvUkZEX01IdGVzdDIxay5idyIpCmV4cG9ydChjb3ZlcmFnZShiaW5nZW4yLHdlaWdodD1iaW5nZW4yJG1oX3Rlc3Q0MWtjb3IpLGNvbj0iQmlnV2lnL1JGRF9NSHRlc3Q0MWsuYnciKQpgYGAKCiMjIyBOb24gcG9vbGVkIFJGRCBleHBvcnQuICAKYGBge3J9CmN2TEJZMSA8LSBjb3ZlcmFnZShmb3JrQlkxX0wuZ3IpCmN2UkJZMSA8LSBjb3ZlcmFnZShmb3JrQlkxX1IuZ3IpCnJmZEJZMSA8LSAoY3ZSQlkxLWN2TEJZMSkvKGN2UkJZMStjdkxCWTEpCmV4cG9ydChyZmRCWTEsY29uPSJCaWdXaWcvcmZkbnRfQlkxLmJ3IikKZXhwb3J0KChjdkxCWTErY3ZSQlkxKSxjb249IkJpZ1dpZy9jb3ZUbnRfQlkxLmJ3IikKY3ZMQlkyIDwtIGNvdmVyYWdlKGZvcmtCWTJfTC5ncikKY3ZSQlkyIDwtIGNvdmVyYWdlKGZvcmtCWTJfUi5ncikKcmZkQlkyIDwtIChjdlJCWTItY3ZMQlkyKS8oY3ZSQlkyK2N2TEJZMikKZXhwb3J0KHJmZEJZMixjb249IkJpZ1dpZy9yZmRudF9CWTIuYnciKQpleHBvcnQoKGN2TEJZMitjdlJCWTIpLGNvbj0iQmlnV2lnL2NvdlRudF9CWTIuYnciKQpjdkxTWTEgPC0gY292ZXJhZ2UoZm9ya1NZMV9MLmdyKQpjdlJTWTEgPC0gY292ZXJhZ2UoZm9ya1NZMV9SLmdyKQpyZmRTWTEgPC0gKGN2UlNZMS1jdkxTWTEpLyhjdlJTWTErY3ZMU1kxKQpleHBvcnQocmZkU1kxLGNvbj0iQmlnV2lnL3JmZG50X1NZMS5idyIpCmV4cG9ydCgoY3ZMU1kxK2N2UlNZMSksY29uPSJCaWdXaWcvY292VG50X1NZMS5idyIpCmN2TFNZMiA8LSBjb3ZlcmFnZShmb3JrU1kyX0wuZ3IpCmN2UlNZMiA8LSBjb3ZlcmFnZShmb3JrU1kyX1IuZ3IpCnJmZFNZMiA8LSAoY3ZSU1kyLWN2TFNZMikvKGN2UlNZMitjdkxTWTIpCmV4cG9ydChyZmRTWTIsY29uPSJCaWdXaWcvcmZkbnRfU1kyLmJ3IikKZXhwb3J0KChjdkxTWTIrY3ZSU1kyKSxjb249IkJpZ1dpZy9jb3ZUbnRfU1kyLmJ3IikKY3ZMQlkzIDwtIGNvdmVyYWdlKGZvcmtCWTNfTC5ncikKY3ZSQlkzIDwtIGNvdmVyYWdlKGZvcmtCWTNfUi5ncikKcmZkQlkzIDwtIChjdlJCWTMtY3ZMQlkzKS8oY3ZSQlkzK2N2TEJZMykKZXhwb3J0KHJmZEJZMyxjb249IkJpZ1dpZy9yZmRudF9CWTMuYnciKQpleHBvcnQoKGN2TEJZMytjdlJCWTMpLGNvbj0iQmlnV2lnL2NvdlRudF9CWTMuYnciKQpjdkxTWTMgPC0gY292ZXJhZ2UoZm9ya1NZM19MLmdyKQpjdlJTWTMgPC0gY292ZXJhZ2UoZm9ya1NZM19SLmdyKQpyZmRTWTMgPC0gKGN2UlNZMy1jdkxTWTMpLyhjdlJTWTMrY3ZMU1kzKQpleHBvcnQocmZkU1kzLGNvbj0iQmlnV2lnL3JmZG50X1NZMy5idyIpCmV4cG9ydCgoY3ZMU1kzK2N2UlNZMyksY29uPSJCaWdXaWcvY292VG50X1NZMy5idyIpCgojIyMgSSBuZWVkIHRvIGV4cG9ydCB0aGUgYmlubmVkIHJmZCBmb3IgZWFjaCBleHBlcmltZW50ICgxaykKY3ZMQlkxIDwtIGNvdmVyYWdlKGJpbmdlbix3ZWlnaHQ9YmluZ2VuJEJZMV8wMWtMKQpjdlJCWTEgPC0gY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1iaW5nZW4kQlkxXzAxa1IpCnJmZEJZMSA8LSAoY3ZSQlkxLWN2TEJZMSkvKGN2UkJZMStjdkxCWTEpCmV4cG9ydChyZmRCWTEsY29uPSJCaWdXaWcvcmZkMWtfQlkxLmJ3IikKZXhwb3J0KChjdkxCWTErY3ZSQlkxKSxjb249IkJpZ1dpZy9jb3ZUMWtfQlkxLmJ3IikKY3ZMQlkyIDwtIGNvdmVyYWdlKGJpbmdlbix3ZWlnaHQ9YmluZ2VuJEJZMl8wMWtMKQpjdlJCWTIgPC0gY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1iaW5nZW4kQlkyXzAxa1IpCnJmZEJZMiA8LSAoY3ZSQlkyLWN2TEJZMikvKGN2UkJZMitjdkxCWTIpCmV4cG9ydChyZmRCWTIsY29uPSJCaWdXaWcvcmZkMWtfQlkyLmJ3IikKZXhwb3J0KChjdkxCWTIrY3ZSQlkyKSxjb249IkJpZ1dpZy9jb3ZUMWtfQlkyLmJ3IikKY3ZMU1kxIDwtIGNvdmVyYWdlKGJpbmdlbix3ZWlnaHQ9YmluZ2VuJFNZMV8wMWtMKQpjdlJTWTEgPC0gY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1iaW5nZW4kU1kxXzAxa1IpCnJmZFNZMSA8LSAoY3ZSU1kxLWN2TFNZMSkvKGN2UlNZMStjdkxTWTEpCmV4cG9ydChyZmRTWTEsY29uPSJCaWdXaWcvcmZkMWtfU1kxLmJ3IikKZXhwb3J0KChjdkxTWTErY3ZSU1kxKSxjb249IkJpZ1dpZy9jb3ZUMWtfU1kxLmJ3IikKY3ZMU1kyIDwtIGNvdmVyYWdlKGJpbmdlbix3ZWlnaHQ9YmluZ2VuJFNZMl8wMWtMKQpjdlJTWTIgPC0gY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1iaW5nZW4kU1kyXzAxa1IpCnJmZFNZMiA8LSAoY3ZSU1kyLWN2TFNZMikvKGN2UlNZMitjdkxTWTIpCmV4cG9ydChyZmRTWTIsY29uPSJCaWdXaWcvcmZkMWtfU1kyLmJ3IikKZXhwb3J0KChjdkxTWTIrY3ZSU1kyKSxjb249IkJpZ1dpZy9jb3ZUMWtfU1kyLmJ3IikKY3ZMQlkzIDwtIGNvdmVyYWdlKGJpbmdlbix3ZWlnaHQ9YmluZ2VuJEJZM18wMWtMKQpjdlJCWTMgPC0gY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1iaW5nZW4kQlkzXzAxa1IpCnJmZEJZMyA8LSAoY3ZSQlkzLWN2TEJZMykvKGN2UkJZMytjdkxCWTMpCmV4cG9ydChyZmRCWTMsY29uPSJCaWdXaWcvcmZkMWtfQlkzLmJ3IikKZXhwb3J0KChjdkxCWTMrY3ZSQlkzKSxjb249IkJpZ1dpZy9jb3ZUMWtfQlkzLmJ3IikKY3ZMU1kzIDwtIGNvdmVyYWdlKGJpbmdlbix3ZWlnaHQ9YmluZ2VuJFNZM18wMWtMKQpjdlJTWTMgPC0gY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1iaW5nZW4kU1kzXzAxa1IpCnJmZFNZMyA8LSAoY3ZSU1kzLWN2TFNZMykvKGN2UlNZMytjdkxTWTMpCmV4cG9ydChyZmRTWTMsY29uPSJCaWdXaWcvcmZkMWtfU1kzLmJ3IikKZXhwb3J0KChjdkxTWTMrY3ZSU1kzKSxjb249IkJpZ1dpZy9jb3ZUMWtfU1kzLmJ3IikKYGBgCgoKIyMjIHBvb2xpbmcgZm9ya3MuICAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZkJZJGZpZCA8LSAxOm5yb3coZkJZKQpmb3JrQlkuZ3IgPC0gd2l0aChmQlksIEdSYW5nZXMoc2VxbmFtZXM9Y2hyb20scmFuZ2VzPUlSYW5nZXMocG1pbihYMCxYMSkscG1heChYMCxYMSkpLHN0cmFuZD1jYXNlX3doZW4oZGlyZWM9PSJSIn4iKyIsVH4iLSIpLGZpZD1maWQsc2VxaW5mbz1zZXFpbmZTWSkpCgpmb3JrQllfUi5nciA8LSBmb3JrQlkuZ3Jbc3RyYW5kKGZvcmtCWS5ncik9PSIrIl0KZm9ya0JZX0wuZ3IgPC0gZm9ya0JZLmdyW3N0cmFuZChmb3JrQlkuZ3IpPT0iLSJdCgpmU1kkZmlkIDwtIDE6bnJvdyhmU1kpCmZvcmtTWS5ncjwtIHdpdGgoZlNZLCBHUmFuZ2VzKHNlcW5hbWVzPWNocm9tLHJhbmdlcz1JUmFuZ2VzKHBtaW4oWDAsWDEpLHBtYXgoWDAsWDEpKSxzdHJhbmQ9Y2FzZV93aGVuKGRpcmVjPT0iUiJ+IisiLFR+Ii0iKSxmaWQ9ZmlkLHNlcWluZm89c2VxaW5mU1kpKQoKZm9ya1NZX1IuZ3IgPC0gZm9ya1NZLmdyW3N0cmFuZChmb3JrU1kuZ3IpPT0iKyJdCmZvcmtTWV9MLmdyIDwtIGZvcmtTWS5ncltzdHJhbmQoZm9ya1NZLmdyKT09Ii0iXQoKYGBgCgojIyMgMWtiIGJpbm5pbmcuICAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYnMgPC0gMTAwMCAKYmluZ2VuIDwtIHRpbGVHZW5vbWUoc2VxaW5mU1ksdGlsZXdpZHRoPWJzLCBjdXQubGFzdC50aWxlLmluLmNocm9tPVQpCm5jIDwtIDRMCm1pbl9vdmwgPC0gNTAxTApvbDEgPC0gZmluZE92ZXJsYXBzKGZvcmtCWV9SLmdyLGJpbmdlbixtaW5vdmVybGFwPW1pbl9vdmwpCmJpbmdlbiRmb3Jrc0JZMWtSIDwtIHNtY2xhcHBseSgxOmxlbmd0aChiaW5nZW4pLCBmdW5jdGlvbih4KSBmb3JrQllfUi5ncltxdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kZmlkLG1jLmNvcmVzPW5jKQpvbDEgPC0gZmluZE92ZXJsYXBzKGZvcmtCWV9MLmdyLGJpbmdlbixtaW5vdmVybGFwPW1pbl9vdmwpCmJpbmdlbiRmb3Jrc0JZMWtMIDwtIHNtY2xhcHBseSgxOmxlbmd0aChiaW5nZW4pLCBmdW5jdGlvbih4KSBmb3JrQllfTC5ncltxdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kZmlkLG1jLmNvcmVzPW5jKQpvbDEgPC0gZmluZE92ZXJsYXBzKGZvcmtTWV9SLmdyLGJpbmdlbixtaW5vdmVybGFwPW1pbl9vdmwpCmJpbmdlbiRmb3Jrc1NZMWtSIDwtIHNtY2xhcHBseSgxOmxlbmd0aChiaW5nZW4pLCBmdW5jdGlvbih4KSBmb3JrU1lfUi5ncltxdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kZmlkLG1jLmNvcmVzPW5jKQpvbDEgPC0gZmluZE92ZXJsYXBzKGZvcmtTWV9MLmdyLGJpbmdlbixtaW5vdmVybGFwPW1pbl9vdmwpCmJpbmdlbiRmb3Jrc1NZMWtMIDwtIHNtY2xhcHBseSgxOmxlbmd0aChiaW5nZW4pLCBmdW5jdGlvbih4KSBmb3JrU1lfTC5ncltxdWVyeUhpdHMob2wxKVtzdWJqZWN0SGl0cyhvbDEpPT14XV0kZmlkLG1jLmNvcmVzPW5jKQoKIyMjIGNvdW50IHVuaXF1ZSBmb3JrcwpiaW5nZW4kQlkxa1IgPC0gc2FwcGx5KGJpbmdlbiRmb3Jrc0JZMWtSLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmJpbmdlbiRCWTFrTCA8LSBzYXBwbHkoYmluZ2VuJGZvcmtzQlkxa0wsZnVuY3Rpb24oeCkgbGVuZ3RoKHVuaXF1ZSh4WyFpcy5uYSh4KV0pKSkKYmluZ2VuJFNZMWtSIDwtIHNhcHBseShiaW5nZW4kZm9ya3NTWTFrUixmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHhbIWlzLm5hKHgpXSkpKQpiaW5nZW4kU1kxa0wgPC0gc2FwcGx5KGJpbmdlbiRmb3Jrc1NZMWtMLGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeFshaXMubmEoeCldKSkpCmBgYAoKCiMjIyBwb29sZWQgMWtiIGJpbm5lZCBSRkQuICAKYGBge3IgbWVzc2FnZT1GQUxTRX0KIyBjb3ZMX0JZIDwtIGNvdmVyYWdlKGZvcmtCWV9MLmdyKQojIGNvdlJfQlkgPC0gY292ZXJhZ2UoZm9ya0JZX1IuZ3IpCiMgcmZkQlkgPC0gKGNvdlJfQlktY292TF9CWSkvKGNvdlJfQlkrY292TF9CWSkKIyBleHBvcnQocmZkQlksY29uPSJCaWdXaWcvcmZkX0JZbnQuYnciKQojIGV4cG9ydChjb3ZSX0JZK2NvdkxfQlksY29uPSJCaWdXaWcvY292VF9CWW50LmJ3IikKIyAKIyBjb3ZMX1NZIDwtIGNvdmVyYWdlKGZvcmtTWV9MLmdyKQojIGNvdlJfU1kgPC0gY292ZXJhZ2UoZm9ya1NZX1IuZ3IpCiMgcmZkU1kgPC0gKGNvdlJfU1ktY292TF9TWSkvKGNvdlJfU1krY292TF9TWSkKIyBleHBvcnQocmZkU1ksY29uPSJCaWdXaWcvcmZkX1NZbnQuYnciKQojIGV4cG9ydChjb3ZSX1NZK2NvdkxfU1ksY29uPSJCaWdXaWcvY292VF9TWW50LmJ3IikKCmN2TEJZMWsgPC0gY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1iaW5nZW4kQlkxa0wpCmN2UkJZMWsgPC0gY292ZXJhZ2UoYmluZ2VuLHdlaWdodD1iaW5nZW4kQlkxa1IpCnJmZEJZMWsgPC0gKGN2UkJZMWstY3ZMQlkxaykvKGN2UkJZMWsrY3ZMQlkxaykKZXhwb3J0KHJmZEJZMWssY29uPSJCaWdXaWcvcmZkMWtfQlkuYnciKQpleHBvcnQoKGN2TEJZMWsrY3ZSQlkxayksY29uPSJCaWdXaWcvY292VDFrX0JZLmJ3IikKY3ZMU1kxayA8LSBjb3ZlcmFnZShiaW5nZW4sd2VpZ2h0PWJpbmdlbiRTWTFrTCkKY3ZSU1kxayA8LSBjb3ZlcmFnZShiaW5nZW4sd2VpZ2h0PWJpbmdlbiRTWTFrUikKcmZkU1kxayA8LSAoY3ZSU1kxay1jdkxTWTFrKS8oY3ZSU1kxaytjdkxTWTFrKQpleHBvcnQocmZkU1kxayxjb249IkJpZ1dpZy9yZmQxa19TWS5idyIpCmV4cG9ydCgoY3ZMU1kxaytjdlJTWTFrKSxjb249IkJpZ1dpZy9jb3ZUMWtfU1kuYnciKQoKYGBgCgoKIyMjIFNlbGVjdGluZyBzaWduaWZpY2F0aXZlIFJGRCBjaGFuZ2VzCgpJbiBvcmRlciB0byByZWNhcGl0dWxhdGUgdGhlc2UgbXVsdGlzY2FsZXMgdGVzdGluZyBwcm9jZWR1cmVzLCB3ZSBjaG9vc2UgdG8gc2VsZWN0IHRoZSBiaW4gdGhhdCB3ZXJlIHNpZ25pZmljYW50IGZvciBhdCBsZWFzdCAzIG91dCBvZiB0aGUgNSB0ZXN0ZWQgc2NhbGVzLiBBbmQgd2UgZGVjaWRlIHRvIHNldCB0aGUgdGhyZXNob2xkIGZvciB0aGUgcC12YWx1ZSBhdCAxZS0yLiAgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKYmdfTUggPC0gcmVhZFJEUygiRGF0YS9SRkRfYmdfTUh0ZXN0X2FkakJZLnJkcyIpCm1pbnAgPC0gMWUtMgoKYmdfTUgyIDwtIGFzX3RpYmJsZShiZ19NSCkgJT4lIHNlbGVjdChzZXFuYW1lcyxzdGFydCxlbmQsY29udGFpbnMoImNvciIpKQpiZ19NSDIgPC0gYmdfTUgyICU+JSBtdXRhdGUobmJzaWdNSD1wbWFwX2ludCguLCBmdW5jdGlvbihtaF90ZXN0M2tjb3IsbWhfdGVzdDVrY29yLG1oX3Rlc3QxMWtjb3IsbWhfdGVzdDIxa2NvcixtaF90ZXN0NDFrY29yLC4uLikgewoJCXN1bShtaF90ZXN0M2tjb3I8PW1pbnAsbWhfdGVzdDVrY29yPD1taW5wLG1oX3Rlc3QxMWtjb3I8PW1pbnAsbWhfdGVzdDIxa2Nvcjw9bWlucCxtaF90ZXN0NDFrY29yPD1taW5wKQoJCX0pKQoJCQpiZ3NpZ01IIDwtIHdpdGgoYmdfTUgyICU+JSBmaWx0ZXIobmJzaWdNSD49MyksR1JhbmdlcyhzZXFuYW1lcz1zZXFuYW1lcyxyYW5nZT1JUmFuZ2VzKHN0YXJ0LGVuZCksc2VxaW5mbz1zZXFpbmZTWSkpCmJnc2lnTUgyIDwtIEdlbm9taWNSYW5nZXM6OnJlZHVjZShiZ3NpZ01IKQoKZXhwb3J0KGJnc2lnTUgyLGNvbj0iRGF0YS9iZ3NpZ01ILmJlZCIpCmBgYAoKY29tcHV0ZSBkaXN0YW5jZSB0byBDRU4vSnVuY3Rpb24vVGVsCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpDRU4gPC0gaW1wb3J0KCJHZW5vbWVfYW5ub3RhdGlvbnMvU1kxNF9DRU4uZ2ZmMyIpIApjaHJvbVNZIDwtIGltcG9ydCgiR2Vub21lX2Fubm90YXRpb25zL2Nocm9tQllvblNZZ3IuYmVkIikgJT4lIHNvcnQKc2VxaW5mbyhjaHJvbVNZKSA8LSBzZXFpbmZTWQoKY2hyb21KdW5jdGlvbiA8LSBnYXBzKGNocm9tU1ktMTApCmNocm9tVHJ1ZUp1bmN0aW9uIDwtIGNocm9tSnVuY3Rpb25bNDoxOF0KVEVMX1NZIDwtIGNocm9tSnVuY3Rpb25bYygzLDE5KV0KCmJnc2lnTUgyIDwtIGltcG9ydCgiRGF0YS9iZ3NpZ01ILmJlZCIpCmJnc2lnTUgyJHNjb3JlIDwtIE5VTEwKYmdzaWdNSDIkbmFtZSA8LSBOVUxMCmJnc2lnTUgyJGR0X0NFTiA8LSBkYXRhLmZyYW1lKGRpc3RhbmNlVG9OZWFyZXN0KGJnc2lnTUgyLENFTikpWywzXQoKYmdzaWdNSDIkZHRfSlVOQyA8LSBkYXRhLmZyYW1lKGRpc3RhbmNlVG9OZWFyZXN0KGJnc2lnTUgyLGNocm9tVHJ1ZUp1bmN0aW9uKSlbLDNdCmJnc2lnTUgyJGR0X1RFTCA8LSBkYXRhLmZyYW1lKGRpc3RhbmNlVG9OZWFyZXN0KGJnc2lnTUgyLFRFTF9TWSkpWywzXQoKYmdzaWdNSDIkZHRfbmVhcmVzdCA8LSBkYXRhLmZyYW1lKGRpc3RhbmNlVG9OZWFyZXN0KGJnc2lnTUgyLGMoVEVMX1NZLENFTixjaHJvbVRydWVKdW5jdGlvbikpKVssM10KCndyaXRlX3Rzdihhc190aWJibGUoYmdzaWdNSDIpLCBmaWxlPSJEYXRhL1JGRHNpZy50c3YiKQp3cml0ZV90c3YoYXNfdGliYmxlKGJnc2lnTUgyKSAlPiUgc2VsZWN0KC1zdHJhbmQpLCBmaWxlPSJEYXRhL1RhYmxlUzMudHN2IikKCmBgYAo=