library(limma)
library(ggplot2)
library(RColorBrewer)
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:ggplot2’:
ggsave
targets.protoarray <- readTargets('../rawdata/protoarray_common.csv', sep=',')
targets.huprot <- readTargets('../rawdata_huprot/huprot_common.csv', sep=',')
targets.huprot$Condition <- as.factor(targets.huprot$Condition)
targets.protoarray$Condition <- as.factor(targets.protoarray$Condition)
targetColors.huprot <-
with(targets.huprot,
data.frame(Condition = levels(Condition),
color = I(brewer.pal(nlevels(Condition), name = 'Dark2'))))
targetColors.protoarray <-
with(targets.protoarray,
data.frame(Condition = levels(Condition),
color = I(brewer.pal(nlevels(Condition), name = 'Dark2'))))
x.huprot <- read.maimages(targets.huprot$FileName,
path=file.path('..', 'rawdata_huprot'),
source='genepix',
columns=list(G='F635 Mean', Gb='B635 Median'),
green.only=TRUE)
Read ../rawdata_huprot/Control_1256587_2000143978.gpr
Read ../rawdata_huprot/Control_1311449_2000143966.gpr
Read ../rawdata_huprot/Control_1312497_2000143964.gpr
Read ../rawdata_huprot/Control_1313058_2000143976.gpr
Read ../rawdata_huprot/Grade_L_CJ_14434_2000143958.gpr
Read ../rawdata_huprot/Grade_L_CH_27096_2000155729.gpr
Read ../rawdata_huprot/Grade_L_CJ_3659_2000143955.gpr
Read ../rawdata_huprot/Grade_III_CH_15796_2000143692.gpr
Read ../rawdata_huprot/Grade_III_CJ_11032_2000143957.gpr
Read ../rawdata_huprot/Grade_III_CJ_27572_2000144442.gpr
Read ../rawdata_huprot/Grade_III_CJ_9761_2000144443.gpr
Read ../rawdata_huprot/Grade_III_CK_5121_2000144441.gpr
Read ../rawdata_huprot/Grade_III_CK_6451_2000144440.gpr
Read ../rawdata_huprot/Grade_IV_CH_12724_2000143977.gpr
Read ../rawdata_huprot/Grade_IV_CH_29738_2000143975.gpr
Read ../rawdata_huprot/Grade_IV_CH_31148_2000143961.gpr
Read ../rawdata_huprot/Grade_IV_CH_32705_2000155727.gpr
Read ../rawdata_huprot/Grade_IV_CJ_11291_2000143969.gpr
Read ../rawdata_huprot/Grade_IV_CJ_12441_2000143979.gpr
Read ../rawdata_huprot/Grade_IV_CK_6570_2000143967.gpr
x.protoarray <- read.maimages(targets.protoarray$FileName,
path=file.path('..', 'rawdata'),
source='genepix',
columns=list(G='F635 Mean', Gb='B635 Median'),
green.only=TRUE)#,
Read ../rawdata/control_67336_18052013.gpr
Read ../rawdata/control_68051_21052013.gpr
Read ../rawdata/control_68213_11052013.gpr
Read ../rawdata/control_68473_05062013.gpr
Read ../rawdata/Grade_L_68204_11052013.gpr
Read ../rawdata/Grade_L_67341_18052013.gpr
Read ../rawdata/Grade_L_68210_13052013.gpr
Read ../rawdata/Grade_III_67346_18052013.gpr
Read ../rawdata/Grade_III_68001_21052013.gpr
Read ../rawdata/Grade_III_68052_21052013.gpr
Read ../rawdata/Grade_III_68207_11052013.gpr
Read ../rawdata/Grade_III_68211_11052013.gpr
Read ../rawdata/Grade_III_68476_18052013_2.gpr
Read ../rawdata/Grade_IV_68037_21052013.gpr
Read ../rawdata/Grade_IV_68056_21052013_2.gpr
Read ../rawdata/Grade_IV_68209_11052013.gpr
Read ../rawdata/Grade_IV_68212_11052013.gpr
Read ../rawdata/Grade_IV_68481_18052013.gpr
Read ../rawdata/Grade_IV_68055_21052013_2.gpr
Read ../rawdata/Grade_IV_67347_18092013_2.gpr
x.huprot$genes$Name <- gsub('\\n', '', x.huprot$genes$Name)
x.protoarray$genes$Name <- gsub('\\n', '', x.protoarray$genes$Name)
MDS plot
opar <- par(pch = 19)
plotMDS(y.huprot, col=targetColors.huprot$color[match(targets.huprot$Condition, targetColors.huprot$Condition)], cex=1.2, pch=19, main='MDS Huprot')
legend(x = 'topright',
legend = as.character(targetColors.huprot$Condition),
col = targetColors.huprot$color, pch = par("pch"), bty = 'n', xjust = 1)

opar <- par(pch = 19)
plotMDS(y.protoarray, col=targetColors.protoarray$color[match(targets.protoarray$Condition, targetColors.protoarray$Condition)], cex=1.2, pch=19, main='MDS Protoarray')
legend(x = 'topright',
legend = as.character(targetColors.protoarray$Condition),
col = targetColors.protoarray$color, pch = par("pch"), bty = 'n', xjust = 1)

f.huprot <- factor(targets.huprot$Condition, levels = unique(targets.huprot$Condition))
design.huprot <- model.matrix(~0 + f.huprot)
colnames(design.huprot) <- levels(f.huprot)
f.protoarray <- factor(targets.protoarray$Condition, levels = unique(targets.protoarray$Condition))
design.protoarray <- model.matrix(~0 + f.protoarray)
colnames(design.protoarray) <- levels(f.protoarray)
corfit.protoarray <- duplicateCorrelation(y.protoarray,
design.protoarray,
ndups=2,
spacing=1
)
fit.protoarray <- lmFit(y.protoarray,
design.protoarray,
ndups=2,
correlation=corfit.protoarray$consensus.correlation)
contrast.matrix.protoarray <- makeContrasts(Grade2vsGrade1=Grade2-Grade1,
Grade3vsGrade2=Grade3-Grade2,
Grade4vsGrade3=Grade4-Grade3,
Grade1vsControl=Grade1-Control,
Grade2vsControl=Grade2-Control,
Grade3vsControl=Grade3-Control,
Grade4vsControl=Grade4-Control, GradevsControl=(Grade1+Grade2+Grade3+Grade4)/4-Control,
HighGradevsControl=(Grade3+Grade4)/2-Control,
LowGradevsControl=(Grade1+Grade2)/2-Control,
HighGradevsLowGrade=(Grade3+Grade4)/2-(Grade1+Grade2)/2,
Grade3vsLowGrade=Grade3-(Grade1+Grade2)/2,
Grade4vsLowGrade=Grade4-(Grade1+Grade2)/2,
levels=design.protoarray)
fit2.protoarray <- contrasts.fit(fit.protoarray, contrast.matrix.protoarray)
fit2.protoarray <- eBayes(fit2.protoarray)
corfit.huprot <- duplicateCorrelation(y.huprot,
design.huprot,
ndups=2,
spacing=1
)
fit.huprot <- lmFit(y.huprot,
design.huprot,
ndups=2,
correlation=corfit.huprot$consensus.correlation)
contrast.matrix.huprot <- makeContrasts(Grade2vsGrade1=Grade2-Grade1,
Grade3vsGrade2=Grade3-Grade2,
Grade4vsGrade3=Grade4-Grade3,
Grade1vsControl=Grade1-Control,
Grade2vsControl=Grade2-Control,
Grade3vsControl=Grade3-Control,
Grade4vsControl=Grade4-Control,
GradevsControl=(Grade1+Grade2+Grade3+Grade4)/4-Control,
HighGradevsControl=(Grade3+Grade4)/2-Control,
LowGradevsControl=(Grade1+Grade2)/2-Control,
HighGradevsLowGrade=(Grade3+Grade4)/2-(Grade1+Grade2)/2,
Grade3vsLowGrade=Grade3-(Grade1+Grade2)/2,
Grade4vsLowGrade=Grade4-(Grade1+Grade2)/2,
levels=design.huprot)
fit2.huprot <- contrasts.fit(fit.huprot, contrast.matrix.huprot)
fit2.huprot <- eBayes(fit2.huprot)
coefs <- c('Grade2vsGrade1',
'Grade3vsGrade2',
'Grade4vsGrade3',
'Grade1vsControl',
'Grade2vsControl',
'Grade3vsControl',
'Grade4vsControl',
'GradevsControl',
'HighGradevsControl',
'LowGradevsControl',
'HighGradevsLowGrade',
'Grade3vsLowGrade',
'Grade4vsLowGrade')
for (coef in coefs){
tt <- topTable(fit2.huprot, coef=coef, number = Inf)
tt.sig <- subset(tt, tt$adj.P.Val<0.1)
write.table(tt, file.path('..', 'results_common/huprot', paste(coef,'csv', sep='.') ), row.names = FALSE)
write.table(tt.sig, file.path('..', 'results_common/huprot', 'significant_filtered', paste(coef,'csv', sep='.') ), row.names = FALSE)
}
for (coef in coefs){
tt <- topTable(fit2.protoarray, coef=coef, number = Inf)
tt.sig <- subset(tt, tt$adj.P.Val<0.1)
write.table(tt, file.path('..', 'results_common/protoarray', paste(coef,'csv', sep='.') ), row.names = FALSE)
write.table(tt.sig, file.path('..', 'results_common/protoarray', 'significant_filtered', paste(coef,'csv', sep='.') ), row.names = FALSE)
}
Similarity b/w Protoarray & Huprot array results
summary.df <- data.frame(coefficient=coefs, huprot=0, protoarray=0, common=0, jaccard=0, names=0, stringsAsFactors = FALSE)
row.names(summary.df) <- coefs
for (coef in coefs){
huprot.results <- read.table(file.path('..', 'results_common/huprot', 'significant_filtered', paste(coef,'csv', sep='.') ), header = T)
protoarray.results <- read.table(file.path('..', 'results_common/protoarray', 'significant_filtered', paste(coef,'csv', sep='.') ), header = T)
#new_name <- sapply(as.vector(protoarray.results$Name), clean_name)
#protoarray.results$Name <- new_name
total <- length(huprot.results$ID)+ length(protoarray.results$Name)
common <- length(intersect(huprot.results$ID, protoarray.results$Name))
summary.df[coef,]$huprot <- length(huprot.results$ID)
summary.df[coef,]$protoarray <- length(protoarray.results$Name)
summary.df[coef,]$common <- common
summary.df[coef,]$jaccard <- common/total
summary.df[coef,]$names <- toString(intersect( gsub('\\..*', '', huprot.results$ID), gsub('\\..*', '', protoarray.results$Name) ))
}
summary.df
LS0tCnRpdGxlOiAiUHJvdG9hcnJheS9IdXByb3RhcnJheSBjb21tb24gQW5hbHlzaXMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZmlnX2hlaWdodDogMzAKICAgIGZpZ193aWR0aDogMzAKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CmRhdGU6ICIwNC8xMS8yMDE3IgotLS0KCgpgYGB7cn0KbGlicmFyeShsaW1tYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShjb3dwbG90KQoKdGFyZ2V0cy5wcm90b2FycmF5IDwtIHJlYWRUYXJnZXRzKCcuLi9yYXdkYXRhL3Byb3RvYXJyYXlfY29tbW9uLmNzdicsIHNlcD0nLCcpCnRhcmdldHMuaHVwcm90IDwtIHJlYWRUYXJnZXRzKCcuLi9yYXdkYXRhX2h1cHJvdC9odXByb3RfY29tbW9uLmNzdicsIHNlcD0nLCcpCgp0YXJnZXRzLmh1cHJvdCRDb25kaXRpb24gPC0gYXMuZmFjdG9yKHRhcmdldHMuaHVwcm90JENvbmRpdGlvbikKdGFyZ2V0cy5wcm90b2FycmF5JENvbmRpdGlvbiA8LSBhcy5mYWN0b3IodGFyZ2V0cy5wcm90b2FycmF5JENvbmRpdGlvbikKCnRhcmdldENvbG9ycy5odXByb3QgPC0KICAgIHdpdGgodGFyZ2V0cy5odXByb3QsCiAgICAgICAgIGRhdGEuZnJhbWUoQ29uZGl0aW9uID0gbGV2ZWxzKENvbmRpdGlvbiksCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBJKGJyZXdlci5wYWwobmxldmVscyhDb25kaXRpb24pLCBuYW1lID0gJ0RhcmsyJykpKSkKCnRhcmdldENvbG9ycy5wcm90b2FycmF5IDwtCiAgICB3aXRoKHRhcmdldHMucHJvdG9hcnJheSwKICAgICAgICAgZGF0YS5mcmFtZShDb25kaXRpb24gPSBsZXZlbHMoQ29uZGl0aW9uKSwKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IEkoYnJld2VyLnBhbChubGV2ZWxzKENvbmRpdGlvbiksIG5hbWUgPSAnRGFyazInKSkpKQoKCgoKCnguaHVwcm90IDwtIHJlYWQubWFpbWFnZXModGFyZ2V0cy5odXByb3QkRmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICBwYXRoPWZpbGUucGF0aCgnLi4nLCAncmF3ZGF0YV9odXByb3QnKSwKICAgICAgICAgICAgICAgICAgIHNvdXJjZT0nZ2VuZXBpeCcsCiAgICAgICAgICAgICAgICAgICBjb2x1bW5zPWxpc3QoRz0nRjYzNSBNZWFuJywgR2I9J0I2MzUgTWVkaWFuJyksCiAgICAgICAgICAgICAgICAgICBncmVlbi5vbmx5PVRSVUUpCngucHJvdG9hcnJheSA8LSByZWFkLm1haW1hZ2VzKHRhcmdldHMucHJvdG9hcnJheSRGaWxlTmFtZSwKICAgICAgICAgICAgICAgICAgIHBhdGg9ZmlsZS5wYXRoKCcuLicsICdyYXdkYXRhJyksCiAgICAgICAgICAgICAgICAgICBzb3VyY2U9J2dlbmVwaXgnLAogICAgICAgICAgICAgICAgICAgY29sdW1ucz1saXN0KEc9J0Y2MzUgTWVhbicsIEdiPSdCNjM1IE1lZGlhbicpLAogICAgICAgICAgICAgICAgICAgZ3JlZW4ub25seT1UUlVFKSMsCnguaHVwcm90JGdlbmVzJE5hbWUgPC0gZ3N1YignXFxuJywgJycsIHguaHVwcm90JGdlbmVzJE5hbWUpCngucHJvdG9hcnJheSRnZW5lcyROYW1lIDwtIGdzdWIoJ1xcbicsICcnLCB4LnByb3RvYXJyYXkkZ2VuZXMkTmFtZSkKCmBgYAoKCgojI0V4dHJhY3QgY29tbW9uIHRhcmdldHMKCmBgYHtyfQpjb250cm9scy5wcm90b2FycmF5IDwtIGMoJ0J1ZmZlcicsIAogICAgICAgICAgICAgICAgICAgICAgICAgJ0JTQX5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICdHU1QxflJGVTozMjIuNzQnLAogICAgICAgICAgICAgICAgICAgICAgICAgJ0dTVDJ+UkZVOjU3OC41NCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgJ0dTVDN+UkZVOjE3NzAuMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUNH5SRlU6NDIzMC4xOCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAnQWxleGFBbnRpTW91c2VBYn5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgJ0h1bWFuSWdHMX5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgJ0h1bWFuSWdHMn5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICdIdW1hbklnRzN+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICdIdW1hbklnRzR+Ti9BJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAnQW50aS1IdW1hbklnRzF+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICdBbnRpLUh1bWFuSWdHMn5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgJ0FudGktSHVtYW5JZ0czfk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAnQW50aS1IdW1hbklnRzR+Ti9BJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAnVjVjb250cm9sNX5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgJ1Y1Y29udHJvbDR+Ti9BJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAnVjVjb250cm9sM35OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICdWNWNvbnRyb2wyfk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAnVjVjb250cm9sMX5OL0EnCiAgICAgICAgICAgICAgICAgICAgICAgICApCmNsZWFuX25hbWUgPC0gZnVuY3Rpb24obmFtZSl7CiAgaWYgKG5hbWUgJWluJSBjb250cm9scy5wcm90b2FycmF5KXsKICAgIHJldHVybihuYW1lKQogIH0KICBuYW1lIDwtIHN0cnNwbGl0KG5hbWUsICI6IilbWzFdXVsyXQogIG5hbWUgPC0gc3Ryc3BsaXQobmFtZSwgIn4iKVtbMV1dWzFdCiAgI25hbWUgPC0gIGdzdWIoJ1xcLi4qJywgJycsIG5hbWUpCiAgcmV0dXJuKG5hbWUpCn0KYGBgCgpgYGB7cn0KY29udHJvbHMuaHVwcm90IDwtIGMoJ0JTQScsICdIZUxhIGNlbGwgbHlzYXRlcycsICdwMzAwLUJIQycsICdIMkEnLCAnSDJCJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSDMnLCAnSDQnLCAnR1NUMTBuJywgJ0dTVDUwbicsICdHU1QxMDBuJywgJ0dTVDUwMG4nKQpgYGAKCmBgYHtyfQp4Lmh1cHJvdCRnZW5lcyRJRCA8LSBnc3ViKCdcXC4uKicsJycsIHguaHVwcm90JGdlbmVzJElEKQojeC5odXByb3QkZ2VuZXMkTmFtZSA8LSBnc3ViKCdcXC4uKicsJycsIHguaHVwcm90JGdlbmVzJElEKQoKbmV3X25hbWUgPC0gc2FwcGx5KGFzLnZlY3Rvcih4LnByb3RvYXJyYXkkZ2VuZXMkTmFtZSksIGNsZWFuX25hbWUpCgp4LnByb3RvYXJyYXkkZ2VuZXMkTmFtZSA8LSBuZXdfbmFtZQp4LnByb3RvYXJyYXkkZ2VuZXMkTmFtZSA8LSAgZ3N1YignXFwuLionLCAnJywgeC5wcm90b2FycmF5JGdlbmVzJE5hbWUpCngucHJvdG9hcnJheSA8LSB4LnByb3RvYXJyYXlbeC5wcm90b2FycmF5JGdlbmVzJE5hbWUgJWluJSBjKHguaHVwcm90JGdlbmVzJElELCBjb250cm9scy5wcm90b2FycmF5KSxdCgojeC5odXByb3QgPC0gc3Vic2V0KHguaHVwcm90LCB4Lmh1cHJvdCRnZW5lcyRJRCAlaW4lIGMoeC5wcm90b2FycmF5JGdlbmVzJE5hbWUpIHwgeC5odXByb3QkZ2VuZXMkTmFtZSAlaW4lIGNvbnRyb2xzLmh1cHJvdCApCnguaHVwcm90IDwtIHguaHVwcm90W3guaHVwcm90JGdlbmVzJElEICVpbiUgeC5wcm90b2FycmF5JGdlbmVzJE5hbWUgfCB4Lmh1cHJvdCRnZW5lcyROYW1lICVpbiUgY29udHJvbHMuaHVwcm90LF0gIyMgQWRkZWQgMTF0aCBBcHJpbCAyMDE3CgoKbmVnYXRpdmUuY29udHJvbHMuaHVwcm90IDwtIHguaHVwcm90JGdlbmVzJE5hbWUgJWluJSBjKCdCU0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSGVMYSBjZWxsIGx5c2F0ZXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdwMzAwLUJIQycpCgpwb3NpdGl2ZS5jb250cm9scy5odXByb3QgPC0geC5odXByb3QkZ2VuZXMkTmFtZSAlaW4lIGMoJ0gyQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdIMkInLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0gzJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdINCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUMTBuJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0dTVDUwbicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdHU1QxMDBuJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdHU1Q1MDBuJykKCgpuZWdhdGl2ZS5jb250cm9scy5wcm90b2FycmF5IDwtIHgucHJvdG9hcnJheSRnZW5lcyROYW1lICVpbiUgYygnQnVmZmVyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0JTQX5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUMX5SRlU6MzIyLjc0JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUMn5SRlU6NTc4LjU0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0dTVDN+UkZVOjE3NzAuMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0dTVDR+UkZVOjQyMzAuMTgnKQoKcG9zaXRpdmUuY29udHJvbHMucHJvdG9hcnJheSA8LSB4LnByb3RvYXJyYXkkZ2VuZXMkTmFtZSAlaW4lIGMoJ0FsZXhhQW50aU1vdXNlQWJ+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSHVtYW5JZ0cxfk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0h1bWFuSWdHMn5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSHVtYW5JZ0czfk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0h1bWFuSWdHNH5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQW50aS1IdW1hbklnRzF+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQW50aS1IdW1hbklnRzJ+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQW50aS1IdW1hbklnRzN+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQW50aS1IdW1hbklnRzR+Ti9BJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1Y1Y29udHJvbDV+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVjVjb250cm9sNH5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVjVjb250cm9sM35OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVjVjb250cm9sMn5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdWNWNvbnRyb2wxfk4vQScpCgoKYGBgCgpgYGB7cn0KcGFyKG1hcj1jKDgsNCw0LDQpKy4xKQoKYm94cGxvdChsb2cyKHguaHVwcm90JEUpLGxhcz0yLCBtYWluPSJQcmUtbm9ybWFsaXphdGlvbiBIdXByb3QiLHlsYWI9ImxvZzItaW50ZW5zaXR5IiwgY29sPXRhcmdldENvbG9ycy5odXByb3QkY29sb3JbbWF0Y2godGFyZ2V0cy5odXByb3RbY29sbmFtZXMoeC5odXByb3QkRSksXSRDb25kaXRpb24sIHRhcmdldENvbG9ycy5odXByb3QkQ29uZGl0aW9uKV0sIHBjaD0xOSwgY2V4LmF4aXM9MC43LCBjZXg9MC43KQoKcGFyKG1hcj1jKDgsNCw0LDQpKy4xKQpib3hwbG90KGxvZzIoeC5wcm90b2FycmF5JEUpLGxhcz0yLCBtYWluPSJQcmUtbm9ybWFsaXphdGlvbiBQcm90b2FycmF5Iix5bGFiPSJsb2cyLWludGVuc2l0eSIsIGNvbD10YXJnZXRDb2xvcnMucHJvdG9hcnJheSRjb2xvclttYXRjaCh0YXJnZXRzLnByb3RvYXJyYXlbY29sbmFtZXMoeC5wcm90b2FycmF5JEUpLF0kQ29uZGl0aW9uLCB0YXJnZXRDb2xvcnMucHJvdG9hcnJheSRDb25kaXRpb24pXSwgcGNoPTE5LCBjZXguYXhpcz0wLjcsIGNleD0wLjcpCgpgYGAKCgpgYGB7cn0KeC5odXByb3QkZ2VuZXMkU3RhdHVzICA8LSByZXAoJ3JlZ3VsYXInLCBucm93KHguaHVwcm90KSkKeC5odXByb3QkZ2VuZXMkU3RhdHVzW3Bvc2l0aXZlLmNvbnRyb2xzLmh1cHJvdF0gPC0gJ1BPU0lUSVZFJwp4Lmh1cHJvdCRnZW5lcyRTdGF0dXNbbmVnYXRpdmUuY29udHJvbHMuaHVwcm90XSA8LSAnTkVHQVRJVkUnCnkuaHVwcm90IDwtIG5lcWMoeC5odXByb3QpCgp4LnByb3RvYXJyYXkkZ2VuZXMkU3RhdHVzICA8LSByZXAoJ3JlZ3VsYXInLCBucm93KHgucHJvdG9hcnJheSkpCngucHJvdG9hcnJheSRnZW5lcyRTdGF0dXNbcG9zaXRpdmUuY29udHJvbHMucHJvdG9hcnJheV0gPC0gJ1BPU0lUSVZFJwp4LnByb3RvYXJyYXkkZ2VuZXMkU3RhdHVzW25lZ2F0aXZlLmNvbnRyb2xzLnByb3RvYXJyYXldIDwtICdORUdBVElWRScKeS5wcm90b2FycmF5IDwtIG5lcWMoeC5wcm90b2FycmF5KQoKYGBgCgoKYGBge3J9CnBhcihtYXI9Yyg4LDQsNCw0KSsuMSkKCmJveHBsb3QobG9nMih5Lmh1cHJvdCRFKSxsYXM9MiwgbWFpbj0iUG9zdC1ub3JtYWxpemF0aW9uIEh1cHJvdCIseWxhYj0ibG9nMi1pbnRlbnNpdHkiLCBjb2w9dGFyZ2V0Q29sb3JzLmh1cHJvdCRjb2xvclttYXRjaCh0YXJnZXRzLmh1cHJvdFtjb2xuYW1lcyh5Lmh1cHJvdCRFKSxdJENvbmRpdGlvbiwgdGFyZ2V0Q29sb3JzLmh1cHJvdCRDb25kaXRpb24pXSwgcGNoPTE5LCBjZXguYXhpcz0wLjcsIGNleD0wLjcpCgpwYXIobWFyPWMoOCw0LDQsNCkrLjEpCmJveHBsb3QobG9nMih5LnByb3RvYXJyYXkkRSksbGFzPTIsIG1haW49IlBvc3Qtbm9ybWFsaXphdGlvbiBQcm90b2FycmF5Iix5bGFiPSJsb2cyLWludGVuc2l0eSIsIGNvbD10YXJnZXRDb2xvcnMucHJvdG9hcnJheSRjb2xvclttYXRjaCh0YXJnZXRzLnByb3RvYXJyYXlbY29sbmFtZXMoeS5wcm90b2FycmF5JEUpLF0kQ29uZGl0aW9uLCB0YXJnZXRDb2xvcnMucHJvdG9hcnJheSRDb25kaXRpb24pXSwgcGNoPTE5LCBjZXguYXhpcz0wLjcsIGNleD0wLjcpCmBgYAoKCmBgYHtyfQpvcGFyIDwtIHBhcihwY2ggPSAxOSkKCnBjID0gcHJjb21wKCB0KCB5Lmh1cHJvdCRFICApLCBzY2FsZT1UUlVFICkKcGMuc3VtbWFyeSA8LSBzdW1tYXJ5KHBjKQpwYzEudmFyIDwtIHBjLnN1bW1hcnkkaW1wb3J0YW5jZVsyLDFdCnBjMi52YXIgPC0gcGMuc3VtbWFyeSRpbXBvcnRhbmNlWzIsMl0KCnBsb3QocGMkeFssIDE6Ml0sIGNvbD10YXJnZXRDb2xvcnMuaHVwcm90JGNvbG9yW21hdGNoKHRhcmdldHMuaHVwcm90JENvbmRpdGlvbiwgdGFyZ2V0Q29sb3JzLmh1cHJvdCRDb25kaXRpb24pXSwgY2V4PTEuNSwgeGxhYiA9IHBhc3RlKCdQQzE6ICcsIDEwMCpwYzEudmFyLCAnJScsIHNlcD0nJyksIHlsYWIgPSAgcGFzdGUoJ1BDMjogJywgMTAwKnBjMi52YXIsICclJywgc2VwPScnKSwgICAgIG1haW49ICJQQ0Egbm9ybWFsaXplZCBpbnRlbnNpdGllcyBodXByb3QiKQpsZWdlbmQoeCA9ICd0b3ByaWdodCcsIAogICAgICAgbGVnZW5kID0gYXMuY2hhcmFjdGVyKHRhcmdldENvbG9ycy5odXByb3QkQ29uZGl0aW9uKSwKICAgICAgIGNvbCA9IHRhcmdldENvbG9ycy5odXByb3QkY29sb3IsIHBjaCA9IHBhcigicGNoIiksIGJ0eSA9ICduJywgeGp1c3QgPSAxKQpgYGAKCmBgYHtyfQpvcGFyIDwtIHBhcihwY2ggPSAxOSkKCnBjID0gcHJjb21wKCB0KCB5LnByb3RvYXJyYXkkRSAgKSwgc2NhbGU9VFJVRSApCnBjLnN1bW1hcnkgPC0gc3VtbWFyeShwYykKcGMxLnZhciA8LSBwYy5zdW1tYXJ5JGltcG9ydGFuY2VbMiwxXQpwYzIudmFyIDwtIHBjLnN1bW1hcnkkaW1wb3J0YW5jZVsyLDJdCgpwbG90KHBjJHhbLCAxOjJdLCBjb2w9dGFyZ2V0Q29sb3JzLnByb3RvYXJyYXkkY29sb3JbbWF0Y2godGFyZ2V0cy5wcm90b2FycmF5JENvbmRpdGlvbiwgdGFyZ2V0Q29sb3JzLnByb3RvYXJyYXkkQ29uZGl0aW9uKV0sIGNleD0xLjUsIHhsYWIgPSBwYXN0ZSgnUEMxOiAnLCAxMDAqcGMxLnZhciwgJyUnLCBzZXA9JycpLCB5bGFiID0gIHBhc3RlKCdQQzI6ICcsIDEwMCpwYzIudmFyLCAnJScsIHNlcD0nJyksICAgICBtYWluPSAiUENBIG5vcm1hbGl6ZWQgaW50ZW5zaXRpZXMgcHJvdG9hcnJheSIpCmxlZ2VuZCh4ID0gJ3RvcHJpZ2h0JywgCiAgICAgICBsZWdlbmQgPSBhcy5jaGFyYWN0ZXIodGFyZ2V0Q29sb3JzLnByb3RvYXJyYXkkQ29uZGl0aW9uKSwKICAgICAgIGNvbCA9IHRhcmdldENvbG9ycy5wcm90b2FycmF5JGNvbG9yLCBwY2ggPSBwYXIoInBjaCIpLCBidHkgPSAnbicsIHhqdXN0ID0gMSkKYGBgCgoKIyMgTURTIHBsb3QKCgpgYGB7cn0Kb3BhciA8LSBwYXIocGNoID0gMTkpCnBsb3RNRFMoeS5odXByb3QsICBjb2w9dGFyZ2V0Q29sb3JzLmh1cHJvdCRjb2xvclttYXRjaCh0YXJnZXRzLmh1cHJvdCRDb25kaXRpb24sIHRhcmdldENvbG9ycy5odXByb3QkQ29uZGl0aW9uKV0sIGNleD0xLjIsIHBjaD0xOSwgbWFpbj0nTURTIEh1cHJvdCcpCmxlZ2VuZCh4ID0gJ3RvcHJpZ2h0JywgCiAgICAgICBsZWdlbmQgPSBhcy5jaGFyYWN0ZXIodGFyZ2V0Q29sb3JzLmh1cHJvdCRDb25kaXRpb24pLAogICAgICAgY29sID0gdGFyZ2V0Q29sb3JzLmh1cHJvdCRjb2xvciwgcGNoID0gcGFyKCJwY2giKSwgYnR5ID0gJ24nLCB4anVzdCA9IDEpCgpgYGAKCmBgYHtyfQpvcGFyIDwtIHBhcihwY2ggPSAxOSkKcGxvdE1EUyh5LnByb3RvYXJyYXksICBjb2w9dGFyZ2V0Q29sb3JzLnByb3RvYXJyYXkkY29sb3JbbWF0Y2godGFyZ2V0cy5wcm90b2FycmF5JENvbmRpdGlvbiwgdGFyZ2V0Q29sb3JzLnByb3RvYXJyYXkkQ29uZGl0aW9uKV0sIGNleD0xLjIsIHBjaD0xOSwgbWFpbj0nTURTIFByb3RvYXJyYXknKQpsZWdlbmQoeCA9ICd0b3ByaWdodCcsIAogICAgICAgbGVnZW5kID0gYXMuY2hhcmFjdGVyKHRhcmdldENvbG9ycy5wcm90b2FycmF5JENvbmRpdGlvbiksCiAgICAgICBjb2wgPSB0YXJnZXRDb2xvcnMucHJvdG9hcnJheSRjb2xvciwgcGNoID0gcGFyKCJwY2giKSwgYnR5ID0gJ24nLCB4anVzdCA9IDEpCgpgYGAKCmBgYHtyfQoKZi5odXByb3QgPC0gZmFjdG9yKHRhcmdldHMuaHVwcm90JENvbmRpdGlvbiwgbGV2ZWxzID0gdW5pcXVlKHRhcmdldHMuaHVwcm90JENvbmRpdGlvbikpCmRlc2lnbi5odXByb3QgPC0gbW9kZWwubWF0cml4KH4wICsgZi5odXByb3QpCmNvbG5hbWVzKGRlc2lnbi5odXByb3QpIDwtIGxldmVscyhmLmh1cHJvdCkKCgpmLnByb3RvYXJyYXkgPC0gZmFjdG9yKHRhcmdldHMucHJvdG9hcnJheSRDb25kaXRpb24sIGxldmVscyA9IHVuaXF1ZSh0YXJnZXRzLnByb3RvYXJyYXkkQ29uZGl0aW9uKSkKZGVzaWduLnByb3RvYXJyYXkgPC0gbW9kZWwubWF0cml4KH4wICsgZi5wcm90b2FycmF5KQpjb2xuYW1lcyhkZXNpZ24ucHJvdG9hcnJheSkgPC0gbGV2ZWxzKGYucHJvdG9hcnJheSkKCgoKY29yZml0LnByb3RvYXJyYXkgPC0gZHVwbGljYXRlQ29ycmVsYXRpb24oeS5wcm90b2FycmF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduLnByb3RvYXJyYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZHVwcz0yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BhY2luZz0xCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpmaXQucHJvdG9hcnJheSA8LSBsbUZpdCh5LnByb3RvYXJyYXksCiAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbi5wcm90b2FycmF5LAogICAgICAgICAgICAgICAgICAgICAgICBuZHVwcz0yLAogICAgICAgICAgICAgICAgICAgICAgIGNvcnJlbGF0aW9uPWNvcmZpdC5wcm90b2FycmF5JGNvbnNlbnN1cy5jb3JyZWxhdGlvbikKCmNvbnRyYXN0Lm1hdHJpeC5wcm90b2FycmF5IDwtIG1ha2VDb250cmFzdHMoR3JhZGUydnNHcmFkZTE9R3JhZGUyLUdyYWRlMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlM3ZzR3JhZGUyPUdyYWRlMy1HcmFkZTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTR2c0dyYWRlMz1HcmFkZTQtR3JhZGUzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTF2c0NvbnRyb2w9R3JhZGUxLUNvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlMnZzQ29udHJvbD1HcmFkZTItQ29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGUzdnNDb250cm9sPUdyYWRlMy1Db250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTR2c0NvbnRyb2w9R3JhZGU0LUNvbnRyb2wsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGV2c0NvbnRyb2w9KEdyYWRlMStHcmFkZTIrR3JhZGUzK0dyYWRlNCkvNC1Db250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIaWdoR3JhZGV2c0NvbnRyb2w9KEdyYWRlMytHcmFkZTQpLzItQ29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG93R3JhZGV2c0NvbnRyb2w9KEdyYWRlMStHcmFkZTIpLzItQ29udHJvbCwKICAgICAgICAgICAgICAgICAgSGlnaEdyYWRldnNMb3dHcmFkZT0oR3JhZGUzK0dyYWRlNCkvMi0oR3JhZGUxK0dyYWRlMikvMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGUzdnNMb3dHcmFkZT1HcmFkZTMtKEdyYWRlMStHcmFkZTIpLzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlNHZzTG93R3JhZGU9R3JhZGU0LShHcmFkZTErR3JhZGUyKS8yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9ZGVzaWduLnByb3RvYXJyYXkpCgpmaXQyLnByb3RvYXJyYXkgPC0gY29udHJhc3RzLmZpdChmaXQucHJvdG9hcnJheSwgY29udHJhc3QubWF0cml4LnByb3RvYXJyYXkpCmZpdDIucHJvdG9hcnJheSA8LSBlQmF5ZXMoZml0Mi5wcm90b2FycmF5KQoKCmNvcmZpdC5odXByb3QgPC0gZHVwbGljYXRlQ29ycmVsYXRpb24oeS5odXByb3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24uaHVwcm90LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmR1cHM9MiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwYWNpbmc9MQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKZml0Lmh1cHJvdCA8LSBsbUZpdCh5Lmh1cHJvdCwKICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduLmh1cHJvdCwKICAgICAgICAgICAgICAgICAgICAgICAgbmR1cHM9MiwKICAgICAgICAgICAgICAgICAgICAgICBjb3JyZWxhdGlvbj1jb3JmaXQuaHVwcm90JGNvbnNlbnN1cy5jb3JyZWxhdGlvbikKCmNvbnRyYXN0Lm1hdHJpeC5odXByb3QgPC0gbWFrZUNvbnRyYXN0cyhHcmFkZTJ2c0dyYWRlMT1HcmFkZTItR3JhZGUxLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGUzdnNHcmFkZTI9R3JhZGUzLUdyYWRlMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlNHZzR3JhZGUzPUdyYWRlNC1HcmFkZTMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlMXZzQ29udHJvbD1HcmFkZTEtQ29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGUydnNDb250cm9sPUdyYWRlMi1Db250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTN2c0NvbnRyb2w9R3JhZGUzLUNvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlNHZzQ29udHJvbD1HcmFkZTQtQ29udHJvbCwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGV2c0NvbnRyb2w9KEdyYWRlMStHcmFkZTIrR3JhZGUzK0dyYWRlNCkvNC1Db250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIaWdoR3JhZGV2c0NvbnRyb2w9KEdyYWRlMytHcmFkZTQpLzItQ29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG93R3JhZGV2c0NvbnRyb2w9KEdyYWRlMStHcmFkZTIpLzItQ29udHJvbCwKICAgICAgICAgICAgICAgICAgSGlnaEdyYWRldnNMb3dHcmFkZT0oR3JhZGUzK0dyYWRlNCkvMi0oR3JhZGUxK0dyYWRlMikvMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGUzdnNMb3dHcmFkZT1HcmFkZTMtKEdyYWRlMStHcmFkZTIpLzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlNHZzTG93R3JhZGU9R3JhZGU0LShHcmFkZTErR3JhZGUyKS8yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9ZGVzaWduLmh1cHJvdCkKCmZpdDIuaHVwcm90IDwtIGNvbnRyYXN0cy5maXQoZml0Lmh1cHJvdCwgY29udHJhc3QubWF0cml4Lmh1cHJvdCkKZml0Mi5odXByb3QgPC0gZUJheWVzKGZpdDIuaHVwcm90KQpgYGAKCgpgYGB7cn0KY29lZnMgPC0gYygnR3JhZGUydnNHcmFkZTEnLAogICAgICAgICAgICdHcmFkZTN2c0dyYWRlMicsCiAgICAgICAgICAgJ0dyYWRlNHZzR3JhZGUzJywKICAgICAgICAgICAnR3JhZGUxdnNDb250cm9sJywKICAgICAgICAgICAnR3JhZGUydnNDb250cm9sJywKICAgICAgICAgICAnR3JhZGUzdnNDb250cm9sJywKICAgICAgICAgICAnR3JhZGU0dnNDb250cm9sJywKICAgICAgICAgICAnR3JhZGV2c0NvbnRyb2wnLAogICAgICAgICAgICdIaWdoR3JhZGV2c0NvbnRyb2wnLAogICAgICAgICAgICdMb3dHcmFkZXZzQ29udHJvbCcsCiAgICAgICAgICAgJ0hpZ2hHcmFkZXZzTG93R3JhZGUnLAogICAgICAgICAgICdHcmFkZTN2c0xvd0dyYWRlJywKICAgICAgICAgICAnR3JhZGU0dnNMb3dHcmFkZScpCgpmb3IgKGNvZWYgaW4gY29lZnMpewogIHR0IDwtIHRvcFRhYmxlKGZpdDIuaHVwcm90LCBjb2VmPWNvZWYsIG51bWJlciA9IEluZikKICB0dC5zaWcgPC0gc3Vic2V0KHR0LCB0dCRhZGouUC5WYWw8MC4xKQogIHdyaXRlLnRhYmxlKHR0LCBmaWxlLnBhdGgoJy4uJywgJ3Jlc3VsdHNfY29tbW9uL2h1cHJvdCcsIHBhc3RlKGNvZWYsJ2NzdicsIHNlcD0nLicpICksIHJvdy5uYW1lcyA9IEZBTFNFKQogIHdyaXRlLnRhYmxlKHR0LnNpZywgZmlsZS5wYXRoKCcuLicsICdyZXN1bHRzX2NvbW1vbi9odXByb3QnLCAnc2lnbmlmaWNhbnRfZmlsdGVyZWQnLCBwYXN0ZShjb2VmLCdjc3YnLCBzZXA9Jy4nKSApLCByb3cubmFtZXMgPSBGQUxTRSkKfQpgYGAKCmBgYHtyfQpmb3IgKGNvZWYgaW4gY29lZnMpewogIHR0IDwtIHRvcFRhYmxlKGZpdDIucHJvdG9hcnJheSwgY29lZj1jb2VmLCBudW1iZXIgPSBJbmYpCiAgdHQuc2lnIDwtIHN1YnNldCh0dCwgdHQkYWRqLlAuVmFsPDAuMSkKICB3cml0ZS50YWJsZSh0dCwgZmlsZS5wYXRoKCcuLicsICdyZXN1bHRzX2NvbW1vbi9wcm90b2FycmF5JywgcGFzdGUoY29lZiwnY3N2Jywgc2VwPScuJykgKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgd3JpdGUudGFibGUodHQuc2lnLCBmaWxlLnBhdGgoJy4uJywgJ3Jlc3VsdHNfY29tbW9uL3Byb3RvYXJyYXknLCAnc2lnbmlmaWNhbnRfZmlsdGVyZWQnLCBwYXN0ZShjb2VmLCdjc3YnLCBzZXA9Jy4nKSApLCByb3cubmFtZXMgPSBGQUxTRSkKfQoKYGBgCgoKIyMgU2ltaWxhcml0eSBiL3cgUHJvdG9hcnJheSAmIEh1cHJvdCBhcnJheSByZXN1bHRzCgpgYGB7cn0Kc3VtbWFyeS5kZiA8LSBkYXRhLmZyYW1lKGNvZWZmaWNpZW50PWNvZWZzLCBodXByb3Q9MCwgcHJvdG9hcnJheT0wLCBjb21tb249MCwgamFjY2FyZD0wLCBuYW1lcz0wLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnJvdy5uYW1lcyhzdW1tYXJ5LmRmKSA8LSBjb2Vmcwpmb3IgKGNvZWYgaW4gY29lZnMpewogIGh1cHJvdC5yZXN1bHRzIDwtIHJlYWQudGFibGUoZmlsZS5wYXRoKCcuLicsICdyZXN1bHRzX2NvbW1vbi9odXByb3QnLCAnc2lnbmlmaWNhbnRfZmlsdGVyZWQnLCBwYXN0ZShjb2VmLCdjc3YnLCBzZXA9Jy4nKSApLCBoZWFkZXIgPSBUKQogIHByb3RvYXJyYXkucmVzdWx0cyA8LSByZWFkLnRhYmxlKGZpbGUucGF0aCgnLi4nLCAncmVzdWx0c19jb21tb24vcHJvdG9hcnJheScsICdzaWduaWZpY2FudF9maWx0ZXJlZCcsIHBhc3RlKGNvZWYsJ2NzdicsIHNlcD0nLicpICksIGhlYWRlciA9IFQpCiAgI25ld19uYW1lIDwtIHNhcHBseShhcy52ZWN0b3IocHJvdG9hcnJheS5yZXN1bHRzJE5hbWUpLCBjbGVhbl9uYW1lKQogICNwcm90b2FycmF5LnJlc3VsdHMkTmFtZSA8LSBuZXdfbmFtZQogIHRvdGFsIDwtIGxlbmd0aChodXByb3QucmVzdWx0cyRJRCkrIGxlbmd0aChwcm90b2FycmF5LnJlc3VsdHMkTmFtZSkKICBjb21tb24gPC0gbGVuZ3RoKGludGVyc2VjdChodXByb3QucmVzdWx0cyRJRCwgcHJvdG9hcnJheS5yZXN1bHRzJE5hbWUpKQogIHN1bW1hcnkuZGZbY29lZixdJGh1cHJvdCA8LSAgIGxlbmd0aChodXByb3QucmVzdWx0cyRJRCkKICBzdW1tYXJ5LmRmW2NvZWYsXSRwcm90b2FycmF5IDwtICAgbGVuZ3RoKHByb3RvYXJyYXkucmVzdWx0cyROYW1lKQogIHN1bW1hcnkuZGZbY29lZixdJGNvbW1vbiA8LSBjb21tb24gIAogIHN1bW1hcnkuZGZbY29lZixdJGphY2NhcmQgPC0gY29tbW9uL3RvdGFsICAKICBzdW1tYXJ5LmRmW2NvZWYsXSRuYW1lcyA8LSB0b1N0cmluZyhpbnRlcnNlY3QoIGdzdWIoJ1xcLi4qJywgJycsIGh1cHJvdC5yZXN1bHRzJElEKSwgIGdzdWIoJ1xcLi4qJywgJycsIHByb3RvYXJyYXkucmVzdWx0cyROYW1lKSApKQp9CnN1bW1hcnkuZGYKYGBg