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)

Extract common targets

controls.protoarray <- c('Buffer', 
                         'BSA~N/A', 
                         'GST1~RFU:322.74',
                         'GST2~RFU:578.54', 
                         'GST3~RFU:1770.2',
                         'GST4~RFU:4230.18',
                         'AlexaAntiMouseAb~N/A',
                         'HumanIgG1~N/A',
                         'HumanIgG2~N/A', 
                         'HumanIgG3~N/A',
                         'HumanIgG4~N/A', 
                         'Anti-HumanIgG1~N/A',
                         'Anti-HumanIgG2~N/A',
                         'Anti-HumanIgG3~N/A',
                         'Anti-HumanIgG4~N/A', 
                         'V5control5~N/A',
                         'V5control4~N/A', 
                         'V5control3~N/A', 
                         'V5control2~N/A',
                         'V5control1~N/A'
                         )
clean_name <- function(name){
  if (name %in% controls.protoarray){
    return(name)
  }
  name <- strsplit(name, ":")[[1]][2]
  name <- strsplit(name, "~")[[1]][1]
  #name <-  gsub('\\..*', '', name)
  return(name)
}
controls.huprot <- c('BSA', 'HeLa cell lysates', 'p300-BHC', 'H2A', 'H2B', 
                                        'H3', 'H4', 'GST10n', 'GST50n', 'GST100n', 'GST500n')
x.huprot$genes$ID <- gsub('\\..*','', x.huprot$genes$ID)
#x.huprot$genes$Name <- gsub('\\..*','', x.huprot$genes$ID)
new_name <- sapply(as.vector(x.protoarray$genes$Name), clean_name)
x.protoarray$genes$Name <- new_name
x.protoarray$genes$Name <-  gsub('\\..*', '', x.protoarray$genes$Name)
x.protoarray <- x.protoarray[x.protoarray$genes$Name %in% c(x.huprot$genes$ID, controls.protoarray),]
#x.huprot <- subset(x.huprot, x.huprot$genes$ID %in% c(x.protoarray$genes$Name) | x.huprot$genes$Name %in% controls.huprot )
x.huprot <- x.huprot[x.huprot$genes$ID %in% x.protoarray$genes$Name | x.huprot$genes$Name %in% controls.huprot,] ## Added 11th April 2017
negative.controls.huprot <- x.huprot$genes$Name %in% c('BSA', 
                                         'HeLa cell lysates',
                                         'p300-BHC')
positive.controls.huprot <- x.huprot$genes$Name %in% c('H2A', 
                                         'H2B', 
                                          'H3', 
                                          'H4', 
                                          'GST10n',
                                          'GST50n',
                                          'GST100n', 
                                          'GST500n')
negative.controls.protoarray <- x.protoarray$genes$Name %in% c('Buffer', 
                                         'BSA~N/A', 
                                         'GST1~RFU:322.74',
                                         'GST2~RFU:578.54', 
                                         'GST3~RFU:1770.2',
                                         'GST4~RFU:4230.18')
positive.controls.protoarray <- x.protoarray$genes$Name %in% c('AlexaAntiMouseAb~N/A',
                                         'HumanIgG1~N/A',
                                         'HumanIgG2~N/A', 
                                         'HumanIgG3~N/A',
                                         'HumanIgG4~N/A', 
                                         'Anti-HumanIgG1~N/A',
                                         'Anti-HumanIgG2~N/A',
                                         'Anti-HumanIgG3~N/A',
                                         'Anti-HumanIgG4~N/A', 
                                         'V5control5~N/A',
                                         'V5control4~N/A', 
                                         'V5control3~N/A', 
                                         'V5control2~N/A',
                                         'V5control1~N/A')
par(mar=c(8,4,4,4)+.1)
boxplot(log2(x.huprot$E),las=2, main="Pre-normalization Huprot",ylab="log2-intensity", col=targetColors.huprot$color[match(targets.huprot[colnames(x.huprot$E),]$Condition, targetColors.huprot$Condition)], pch=19, cex.axis=0.7, cex=0.7)
par(mar=c(8,4,4,4)+.1)

boxplot(log2(x.protoarray$E),las=2, main="Pre-normalization Protoarray",ylab="log2-intensity", col=targetColors.protoarray$color[match(targets.protoarray[colnames(x.protoarray$E),]$Condition, targetColors.protoarray$Condition)], pch=19, cex.axis=0.7, cex=0.7)

x.huprot$genes$Status  <- rep('regular', nrow(x.huprot))
x.huprot$genes$Status[positive.controls.huprot] <- 'POSITIVE'
x.huprot$genes$Status[negative.controls.huprot] <- 'NEGATIVE'
y.huprot <- neqc(x.huprot)
x.protoarray$genes$Status  <- rep('regular', nrow(x.protoarray))
x.protoarray$genes$Status[positive.controls.protoarray] <- 'POSITIVE'
x.protoarray$genes$Status[negative.controls.protoarray] <- 'NEGATIVE'
y.protoarray <- neqc(x.protoarray)
par(mar=c(8,4,4,4)+.1)
boxplot(log2(y.huprot$E),las=2, main="Post-normalization Huprot",ylab="log2-intensity", col=targetColors.huprot$color[match(targets.huprot[colnames(y.huprot$E),]$Condition, targetColors.huprot$Condition)], pch=19, cex.axis=0.7, cex=0.7)
par(mar=c(8,4,4,4)+.1)

boxplot(log2(y.protoarray$E),las=2, main="Post-normalization Protoarray",ylab="log2-intensity", col=targetColors.protoarray$color[match(targets.protoarray[colnames(y.protoarray$E),]$Condition, targetColors.protoarray$Condition)], pch=19, cex.axis=0.7, cex=0.7)

opar <- par(pch = 19)
pc = prcomp( t( y.huprot$E  ), scale=TRUE )
pc.summary <- summary(pc)
pc1.var <- pc.summary$importance[2,1]
pc2.var <- pc.summary$importance[2,2]
plot(pc$x[, 1:2], col=targetColors.huprot$color[match(targets.huprot$Condition, targetColors.huprot$Condition)], cex=1.5, xlab = paste('PC1: ', 100*pc1.var, '%', sep=''), ylab =  paste('PC2: ', 100*pc2.var, '%', sep=''),     main= "PCA normalized intensities 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)
pc = prcomp( t( y.protoarray$E  ), scale=TRUE )
pc.summary <- summary(pc)
pc1.var <- pc.summary$importance[2,1]
pc2.var <- pc.summary$importance[2,2]
plot(pc$x[, 1:2], col=targetColors.protoarray$color[match(targets.protoarray$Condition, targetColors.protoarray$Condition)], cex=1.5, xlab = paste('PC1: ', 100*pc1.var, '%', sep=''), ylab =  paste('PC2: ', 100*pc2.var, '%', sep=''),     main= "PCA normalized intensities protoarray")
legend(x = 'topright', 
       legend = as.character(targetColors.protoarray$Condition),
       col = targetColors.protoarray$color, pch = par("pch"), bty = 'n', xjust = 1)

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