library(limma)
library(ggplot2)
library(RColorBrewer)
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.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 <- 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.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]
  return(name)
}
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')
new_name <- sapply(as.vector(x.protoarray$genes$Name), clean_name)
x.protoarray$genes$Name <- new_name
x.protoarray <- x.protoarray[x.protoarray$genes$Name %in% c(x.huprot$genes$ID, controls.protoarray),]
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.01)
  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.01)
  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(huprot.results$ID, protoarray.results$Name))
}
summary.df
LS0tCnRpdGxlOiAiUHJvdG9hcnJheS9IdXByb3RhcnJheSBjb21tb24gQW5hbHlzaXMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZmlnX2hlaWdodDogMzAKICAgIGZpZ193aWR0aDogMzAKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CmRhdGU6ICIwMS8yMC8yMDE3IgotLS0KCgpgYGB7cn0KbGlicmFyeShsaW1tYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKCnRhcmdldHMucHJvdG9hcnJheSA8LSByZWFkVGFyZ2V0cygnLi4vcmF3ZGF0YS9wcm90b2FycmF5X2NvbW1vbi5jc3YnLCBzZXA9JywnKQp0YXJnZXRzLmh1cHJvdCA8LSByZWFkVGFyZ2V0cygnLi4vcmF3ZGF0YV9odXByb3QvaHVwcm90X2NvbW1vbi5jc3YnLCBzZXA9JywnKQoKdGFyZ2V0cy5odXByb3QkQ29uZGl0aW9uIDwtIGFzLmZhY3Rvcih0YXJnZXRzLmh1cHJvdCRDb25kaXRpb24pCnRhcmdldHMucHJvdG9hcnJheSRDb25kaXRpb24gPC0gYXMuZmFjdG9yKHRhcmdldHMucHJvdG9hcnJheSRDb25kaXRpb24pCgp0YXJnZXRDb2xvcnMuaHVwcm90IDwtCiAgICB3aXRoKHRhcmdldHMuaHVwcm90LAogICAgICAgICBkYXRhLmZyYW1lKENvbmRpdGlvbiA9IGxldmVscyhDb25kaXRpb24pLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gSShicmV3ZXIucGFsKG5sZXZlbHMoQ29uZGl0aW9uKSwgbmFtZSA9ICdEYXJrMicpKSkpCgp0YXJnZXRDb2xvcnMucHJvdG9hcnJheSA8LQogICAgd2l0aCh0YXJnZXRzLnByb3RvYXJyYXksCiAgICAgICAgIGRhdGEuZnJhbWUoQ29uZGl0aW9uID0gbGV2ZWxzKENvbmRpdGlvbiksCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBJKGJyZXdlci5wYWwobmxldmVscyhDb25kaXRpb24pLCBuYW1lID0gJ0RhcmsyJykpKSkKCgoKeC5wcm90b2FycmF5IDwtIHJlYWQubWFpbWFnZXModGFyZ2V0cy5wcm90b2FycmF5JEZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgcGF0aD1maWxlLnBhdGgoJy4uJywgJ3Jhd2RhdGEnKSwKICAgICAgICAgICAgICAgICAgIHNvdXJjZT0nZ2VuZXBpeCcsCiAgICAgICAgICAgICAgICAgICBjb2x1bW5zPWxpc3QoRz0nRjYzNSBNZWFuJywgR2I9J0I2MzUgTWVkaWFuJyksCiAgICAgICAgICAgICAgICAgICBncmVlbi5vbmx5PVRSVUUpCgp4Lmh1cHJvdCA8LSByZWFkLm1haW1hZ2VzKHRhcmdldHMuaHVwcm90JEZpbGVOYW1lLAogICAgICAgICAgICAgICAgICAgcGF0aD1maWxlLnBhdGgoJy4uJywgJ3Jhd2RhdGFfaHVwcm90JyksCiAgICAgICAgICAgICAgICAgICBzb3VyY2U9J2dlbmVwaXgnLAogICAgICAgICAgICAgICAgICAgY29sdW1ucz1saXN0KEc9J0Y2MzUgTWVhbicsIEdiPSdCNjM1IE1lZGlhbicpLAogICAgICAgICAgICAgICAgICAgZ3JlZW4ub25seT1UUlVFKQoKeC5odXByb3QkZ2VuZXMkTmFtZSA8LSBnc3ViKCdcXG4nLCAnJywgeC5odXByb3QkZ2VuZXMkTmFtZSkKeC5wcm90b2FycmF5JGdlbmVzJE5hbWUgPC0gZ3N1YignXFxuJywgJycsIHgucHJvdG9hcnJheSRnZW5lcyROYW1lKQoKYGBgCgoKCiMjRXh0cmFjdCBjb21tb24gdGFyZ2V0cwoKYGBge3J9CmNvbnRyb2xzLnByb3RvYXJyYXkgPC0gYygnQnVmZmVyJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAnQlNBfk4vQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgJ0dTVDF+UkZVOjMyMi43NCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUMn5SRlU6NTc4LjU0JywgCiAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUM35SRlU6MTc3MC4yJywKICAgICAgICAgICAgICAgICAgICAgICAgICdHU1Q0flJGVTo0MjMwLjE4JywKICAgICAgICAgICAgICAgICAgICAgICAgICdBbGV4YUFudGlNb3VzZUFifk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAnSHVtYW5JZ0cxfk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAnSHVtYW5JZ0cyfk4vQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgJ0h1bWFuSWdHM35OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgJ0h1bWFuSWdHNH5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICdBbnRpLUh1bWFuSWdHMX5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgJ0FudGktSHVtYW5JZ0cyfk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAnQW50aS1IdW1hbklnRzN+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICdBbnRpLUh1bWFuSWdHNH5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICdWNWNvbnRyb2w1fk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAnVjVjb250cm9sNH5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICdWNWNvbnRyb2wzfk4vQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgJ1Y1Y29udHJvbDJ+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICdWNWNvbnRyb2wxfk4vQScKICAgICAgICAgICAgICAgICAgICAgICAgICkKY2xlYW5fbmFtZSA8LSBmdW5jdGlvbihuYW1lKXsKICBpZiAobmFtZSAlaW4lIGNvbnRyb2xzLnByb3RvYXJyYXkpewogICAgcmV0dXJuKG5hbWUpCiAgfQogIG5hbWUgPC0gc3Ryc3BsaXQobmFtZSwgIjoiKVtbMV1dWzJdCiAgbmFtZSA8LSBzdHJzcGxpdChuYW1lLCAifiIpW1sxXV1bMV0KICByZXR1cm4obmFtZSkKfQpgYGAKCmBgYHtyfQoKCgoKbmVnYXRpdmUuY29udHJvbHMuaHVwcm90IDwtIHguaHVwcm90JGdlbmVzJE5hbWUgJWluJSBjKCdCU0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSGVMYSBjZWxsIGx5c2F0ZXMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdwMzAwLUJIQycpCgpwb3NpdGl2ZS5jb250cm9scy5odXByb3QgPC0geC5odXByb3QkZ2VuZXMkTmFtZSAlaW4lIGMoJ0gyQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdIMkInLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0gzJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdINCcsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUMTBuJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0dTVDUwbicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdHU1QxMDBuJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdHU1Q1MDBuJykKCgpgYGAKCmBgYHtyfQpuZXdfbmFtZSA8LSBzYXBwbHkoYXMudmVjdG9yKHgucHJvdG9hcnJheSRnZW5lcyROYW1lKSwgY2xlYW5fbmFtZSkKeC5wcm90b2FycmF5JGdlbmVzJE5hbWUgPC0gbmV3X25hbWUKeC5wcm90b2FycmF5IDwtIHgucHJvdG9hcnJheVt4LnByb3RvYXJyYXkkZ2VuZXMkTmFtZSAlaW4lIGMoeC5odXByb3QkZ2VuZXMkSUQsIGNvbnRyb2xzLnByb3RvYXJyYXkpLF0KCm5lZ2F0aXZlLmNvbnRyb2xzLnByb3RvYXJyYXkgPC0geC5wcm90b2FycmF5JGdlbmVzJE5hbWUgJWluJSBjKCdCdWZmZXInLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnQlNBfk4vQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdHU1QxflJGVTozMjIuNzQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdHU1QyflJGVTo1NzguNTQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUM35SRlU6MTc3MC4yJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnR1NUNH5SRlU6NDIzMC4xOCcpCgpwb3NpdGl2ZS5jb250cm9scy5wcm90b2FycmF5IDwtIHgucHJvdG9hcnJheSRnZW5lcyROYW1lICVpbiUgYygnQWxleGFBbnRpTW91c2VBYn5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdIdW1hbklnRzF+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSHVtYW5JZ0cyfk4vQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdIdW1hbklnRzN+Ti9BJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnSHVtYW5JZ0c0fk4vQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdBbnRpLUh1bWFuSWdHMX5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdBbnRpLUh1bWFuSWdHMn5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdBbnRpLUh1bWFuSWdHM35OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdBbnRpLUh1bWFuSWdHNH5OL0EnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVjVjb250cm9sNX5OL0EnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdWNWNvbnRyb2w0fk4vQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdWNWNvbnRyb2wzfk4vQScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdWNWNvbnRyb2wyfk4vQScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1Y1Y29udHJvbDF+Ti9BJykKCgpgYGAKCmBgYHtyfQpwYXIobWFyPWMoOCw0LDQsNCkrLjEpCgpib3hwbG90KGxvZzIoeC5odXByb3QkRSksbGFzPTIsIG1haW49IlByZS1ub3JtYWxpemF0aW9uIEh1cHJvdCIseWxhYj0ibG9nMi1pbnRlbnNpdHkiLCBjb2w9dGFyZ2V0Q29sb3JzLmh1cHJvdCRjb2xvclttYXRjaCh0YXJnZXRzLmh1cHJvdFtjb2xuYW1lcyh4Lmh1cHJvdCRFKSxdJENvbmRpdGlvbiwgdGFyZ2V0Q29sb3JzLmh1cHJvdCRDb25kaXRpb24pXSwgcGNoPTE5LCBjZXguYXhpcz0wLjcsIGNleD0wLjcpCgpwYXIobWFyPWMoOCw0LDQsNCkrLjEpCmJveHBsb3QobG9nMih4LnByb3RvYXJyYXkkRSksbGFzPTIsIG1haW49IlByZS1ub3JtYWxpemF0aW9uIFByb3RvYXJyYXkiLHlsYWI9ImxvZzItaW50ZW5zaXR5IiwgY29sPXRhcmdldENvbG9ycy5wcm90b2FycmF5JGNvbG9yW21hdGNoKHRhcmdldHMucHJvdG9hcnJheVtjb2xuYW1lcyh4LnByb3RvYXJyYXkkRSksXSRDb25kaXRpb24sIHRhcmdldENvbG9ycy5wcm90b2FycmF5JENvbmRpdGlvbildLCBwY2g9MTksIGNleC5heGlzPTAuNywgY2V4PTAuNykKCmBgYAoKCmBgYHtyfQp4Lmh1cHJvdCRnZW5lcyRTdGF0dXMgIDwtIHJlcCgncmVndWxhcicsIG5yb3coeC5odXByb3QpKQp4Lmh1cHJvdCRnZW5lcyRTdGF0dXNbcG9zaXRpdmUuY29udHJvbHMuaHVwcm90XSA8LSAnUE9TSVRJVkUnCnguaHVwcm90JGdlbmVzJFN0YXR1c1tuZWdhdGl2ZS5jb250cm9scy5odXByb3RdIDwtICdORUdBVElWRScKeS5odXByb3QgPC0gbmVxYyh4Lmh1cHJvdCkKCngucHJvdG9hcnJheSRnZW5lcyRTdGF0dXMgIDwtIHJlcCgncmVndWxhcicsIG5yb3coeC5wcm90b2FycmF5KSkKeC5wcm90b2FycmF5JGdlbmVzJFN0YXR1c1twb3NpdGl2ZS5jb250cm9scy5wcm90b2FycmF5XSA8LSAnUE9TSVRJVkUnCngucHJvdG9hcnJheSRnZW5lcyRTdGF0dXNbbmVnYXRpdmUuY29udHJvbHMucHJvdG9hcnJheV0gPC0gJ05FR0FUSVZFJwp5LnByb3RvYXJyYXkgPC0gbmVxYyh4LnByb3RvYXJyYXkpCgpgYGAKCgpgYGB7cn0KcGFyKG1hcj1jKDgsNCw0LDQpKy4xKQoKYm94cGxvdChsb2cyKHkuaHVwcm90JEUpLGxhcz0yLCBtYWluPSJQb3N0LW5vcm1hbGl6YXRpb24gSHVwcm90Iix5bGFiPSJsb2cyLWludGVuc2l0eSIsIGNvbD10YXJnZXRDb2xvcnMuaHVwcm90JGNvbG9yW21hdGNoKHRhcmdldHMuaHVwcm90W2NvbG5hbWVzKHkuaHVwcm90JEUpLF0kQ29uZGl0aW9uLCB0YXJnZXRDb2xvcnMuaHVwcm90JENvbmRpdGlvbildLCBwY2g9MTksIGNleC5heGlzPTAuNywgY2V4PTAuNykKCnBhcihtYXI9Yyg4LDQsNCw0KSsuMSkKYm94cGxvdChsb2cyKHkucHJvdG9hcnJheSRFKSxsYXM9MiwgbWFpbj0iUG9zdC1ub3JtYWxpemF0aW9uIFByb3RvYXJyYXkiLHlsYWI9ImxvZzItaW50ZW5zaXR5IiwgY29sPXRhcmdldENvbG9ycy5wcm90b2FycmF5JGNvbG9yW21hdGNoKHRhcmdldHMucHJvdG9hcnJheVtjb2xuYW1lcyh5LnByb3RvYXJyYXkkRSksXSRDb25kaXRpb24sIHRhcmdldENvbG9ycy5wcm90b2FycmF5JENvbmRpdGlvbildLCBwY2g9MTksIGNleC5heGlzPTAuNywgY2V4PTAuNykKYGBgCgoKYGBge3J9Cm9wYXIgPC0gcGFyKHBjaCA9IDE5KQoKcGMgPSBwcmNvbXAoIHQoIHkuaHVwcm90JEUgICksIHNjYWxlPVRSVUUgKQpwYy5zdW1tYXJ5IDwtIHN1bW1hcnkocGMpCnBjMS52YXIgPC0gcGMuc3VtbWFyeSRpbXBvcnRhbmNlWzIsMV0KcGMyLnZhciA8LSBwYy5zdW1tYXJ5JGltcG9ydGFuY2VbMiwyXQoKcGxvdChwYyR4WywgMToyXSwgY29sPXRhcmdldENvbG9ycy5odXByb3QkY29sb3JbbWF0Y2godGFyZ2V0cy5odXByb3QkQ29uZGl0aW9uLCB0YXJnZXRDb2xvcnMuaHVwcm90JENvbmRpdGlvbildLCBjZXg9MS41LCB4bGFiID0gcGFzdGUoJ1BDMTogJywgMTAwKnBjMS52YXIsICclJywgc2VwPScnKSwgeWxhYiA9ICBwYXN0ZSgnUEMyOiAnLCAxMDAqcGMyLnZhciwgJyUnLCBzZXA9JycpLCAgICAgbWFpbj0gIlBDQSBub3JtYWxpemVkIGludGVuc2l0aWVzIGh1cHJvdCIpCmxlZ2VuZCh4ID0gJ3RvcHJpZ2h0JywgCiAgICAgICBsZWdlbmQgPSBhcy5jaGFyYWN0ZXIodGFyZ2V0Q29sb3JzLmh1cHJvdCRDb25kaXRpb24pLAogICAgICAgY29sID0gdGFyZ2V0Q29sb3JzLmh1cHJvdCRjb2xvciwgcGNoID0gcGFyKCJwY2giKSwgYnR5ID0gJ24nLCB4anVzdCA9IDEpCmBgYAoKYGBge3J9Cm9wYXIgPC0gcGFyKHBjaCA9IDE5KQoKcGMgPSBwcmNvbXAoIHQoIHkucHJvdG9hcnJheSRFICApLCBzY2FsZT1UUlVFICkKcGMuc3VtbWFyeSA8LSBzdW1tYXJ5KHBjKQpwYzEudmFyIDwtIHBjLnN1bW1hcnkkaW1wb3J0YW5jZVsyLDFdCnBjMi52YXIgPC0gcGMuc3VtbWFyeSRpbXBvcnRhbmNlWzIsMl0KCnBsb3QocGMkeFssIDE6Ml0sIGNvbD10YXJnZXRDb2xvcnMucHJvdG9hcnJheSRjb2xvclttYXRjaCh0YXJnZXRzLnByb3RvYXJyYXkkQ29uZGl0aW9uLCB0YXJnZXRDb2xvcnMucHJvdG9hcnJheSRDb25kaXRpb24pXSwgY2V4PTEuNSwgeGxhYiA9IHBhc3RlKCdQQzE6ICcsIDEwMCpwYzEudmFyLCAnJScsIHNlcD0nJyksIHlsYWIgPSAgcGFzdGUoJ1BDMjogJywgMTAwKnBjMi52YXIsICclJywgc2VwPScnKSwgICAgIG1haW49ICJQQ0Egbm9ybWFsaXplZCBpbnRlbnNpdGllcyBwcm90b2FycmF5IikKbGVnZW5kKHggPSAndG9wcmlnaHQnLCAKICAgICAgIGxlZ2VuZCA9IGFzLmNoYXJhY3Rlcih0YXJnZXRDb2xvcnMucHJvdG9hcnJheSRDb25kaXRpb24pLAogICAgICAgY29sID0gdGFyZ2V0Q29sb3JzLnByb3RvYXJyYXkkY29sb3IsIHBjaCA9IHBhcigicGNoIiksIGJ0eSA9ICduJywgeGp1c3QgPSAxKQpgYGAKCgojIyBNRFMgcGxvdAoKCmBgYHtyfQpvcGFyIDwtIHBhcihwY2ggPSAxOSkKcGxvdE1EUyh5Lmh1cHJvdCwgIGNvbD10YXJnZXRDb2xvcnMuaHVwcm90JGNvbG9yW21hdGNoKHRhcmdldHMuaHVwcm90JENvbmRpdGlvbiwgdGFyZ2V0Q29sb3JzLmh1cHJvdCRDb25kaXRpb24pXSwgY2V4PTEuMiwgcGNoPTE5LCBtYWluPSdNRFMgSHVwcm90JykKbGVnZW5kKHggPSAndG9wcmlnaHQnLCAKICAgICAgIGxlZ2VuZCA9IGFzLmNoYXJhY3Rlcih0YXJnZXRDb2xvcnMuaHVwcm90JENvbmRpdGlvbiksCiAgICAgICBjb2wgPSB0YXJnZXRDb2xvcnMuaHVwcm90JGNvbG9yLCBwY2ggPSBwYXIoInBjaCIpLCBidHkgPSAnbicsIHhqdXN0ID0gMSkKCmBgYAoKYGBge3J9Cm9wYXIgPC0gcGFyKHBjaCA9IDE5KQpwbG90TURTKHkucHJvdG9hcnJheSwgIGNvbD10YXJnZXRDb2xvcnMucHJvdG9hcnJheSRjb2xvclttYXRjaCh0YXJnZXRzLnByb3RvYXJyYXkkQ29uZGl0aW9uLCB0YXJnZXRDb2xvcnMucHJvdG9hcnJheSRDb25kaXRpb24pXSwgY2V4PTEuMiwgcGNoPTE5LCBtYWluPSdNRFMgUHJvdG9hcnJheScpCmxlZ2VuZCh4ID0gJ3RvcHJpZ2h0JywgCiAgICAgICBsZWdlbmQgPSBhcy5jaGFyYWN0ZXIodGFyZ2V0Q29sb3JzLnByb3RvYXJyYXkkQ29uZGl0aW9uKSwKICAgICAgIGNvbCA9IHRhcmdldENvbG9ycy5wcm90b2FycmF5JGNvbG9yLCBwY2ggPSBwYXIoInBjaCIpLCBidHkgPSAnbicsIHhqdXN0ID0gMSkKCmBgYAoKYGBge3J9CgpmLmh1cHJvdCA8LSBmYWN0b3IodGFyZ2V0cy5odXByb3QkQ29uZGl0aW9uLCBsZXZlbHMgPSB1bmlxdWUodGFyZ2V0cy5odXByb3QkQ29uZGl0aW9uKSkKZGVzaWduLmh1cHJvdCA8LSBtb2RlbC5tYXRyaXgofjAgKyBmLmh1cHJvdCkKY29sbmFtZXMoZGVzaWduLmh1cHJvdCkgPC0gbGV2ZWxzKGYuaHVwcm90KQoKCmYucHJvdG9hcnJheSA8LSBmYWN0b3IodGFyZ2V0cy5wcm90b2FycmF5JENvbmRpdGlvbiwgbGV2ZWxzID0gdW5pcXVlKHRhcmdldHMucHJvdG9hcnJheSRDb25kaXRpb24pKQpkZXNpZ24ucHJvdG9hcnJheSA8LSBtb2RlbC5tYXRyaXgofjAgKyBmLnByb3RvYXJyYXkpCmNvbG5hbWVzKGRlc2lnbi5wcm90b2FycmF5KSA8LSBsZXZlbHMoZi5wcm90b2FycmF5KQoKCgpjb3JmaXQucHJvdG9hcnJheSA8LSBkdXBsaWNhdGVDb3JyZWxhdGlvbih5LnByb3RvYXJyYXksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24ucHJvdG9hcnJheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5kdXBzPTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFjaW5nPTEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCmZpdC5wcm90b2FycmF5IDwtIGxtRml0KHkucHJvdG9hcnJheSwKICAgICAgICAgICAgICAgICAgICAgICAgZGVzaWduLnByb3RvYXJyYXksCiAgICAgICAgICAgICAgICAgICAgICAgIG5kdXBzPTIsCiAgICAgICAgICAgICAgICAgICAgICAgY29ycmVsYXRpb249Y29yZml0LnByb3RvYXJyYXkkY29uc2Vuc3VzLmNvcnJlbGF0aW9uKQoKY29udHJhc3QubWF0cml4LnByb3RvYXJyYXkgPC0gbWFrZUNvbnRyYXN0cyhHcmFkZTJ2c0dyYWRlMT1HcmFkZTItR3JhZGUxLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGUzdnNHcmFkZTI9R3JhZGUzLUdyYWRlMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlNHZzR3JhZGUzPUdyYWRlNC1HcmFkZTMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlMXZzQ29udHJvbD1HcmFkZTEtQ29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGUydnNDb250cm9sPUdyYWRlMi1Db250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTN2c0NvbnRyb2w9R3JhZGUzLUNvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlNHZzQ29udHJvbD1HcmFkZTQtQ29udHJvbCwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZXZzQ29udHJvbD0oR3JhZGUxK0dyYWRlMitHcmFkZTMrR3JhZGU0KS80LUNvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhpZ2hHcmFkZXZzQ29udHJvbD0oR3JhZGUzK0dyYWRlNCkvMi1Db250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMb3dHcmFkZXZzQ29udHJvbD0oR3JhZGUxK0dyYWRlMikvMi1Db250cm9sLAogICAgICAgICAgICAgICAgICBIaWdoR3JhZGV2c0xvd0dyYWRlPShHcmFkZTMrR3JhZGU0KS8yLShHcmFkZTErR3JhZGUyKS8yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTN2c0xvd0dyYWRlPUdyYWRlMy0oR3JhZGUxK0dyYWRlMikvMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGU0dnNMb3dHcmFkZT1HcmFkZTQtKEdyYWRlMStHcmFkZTIpLzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1kZXNpZ24ucHJvdG9hcnJheSkKCmZpdDIucHJvdG9hcnJheSA8LSBjb250cmFzdHMuZml0KGZpdC5wcm90b2FycmF5LCBjb250cmFzdC5tYXRyaXgucHJvdG9hcnJheSkKZml0Mi5wcm90b2FycmF5IDwtIGVCYXllcyhmaXQyLnByb3RvYXJyYXkpCgoKY29yZml0Lmh1cHJvdCA8LSBkdXBsaWNhdGVDb3JyZWxhdGlvbih5Lmh1cHJvdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbi5odXByb3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZHVwcz0yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BhY2luZz0xCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpmaXQuaHVwcm90IDwtIGxtRml0KHkuaHVwcm90LAogICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24uaHVwcm90LAogICAgICAgICAgICAgICAgICAgICAgICBuZHVwcz0yLAogICAgICAgICAgICAgICAgICAgICAgIGNvcnJlbGF0aW9uPWNvcmZpdC5odXByb3QkY29uc2Vuc3VzLmNvcnJlbGF0aW9uKQoKY29udHJhc3QubWF0cml4Lmh1cHJvdCA8LSBtYWtlQ29udHJhc3RzKEdyYWRlMnZzR3JhZGUxPUdyYWRlMi1HcmFkZTEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTN2c0dyYWRlMj1HcmFkZTMtR3JhZGUyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGU0dnNHcmFkZTM9R3JhZGU0LUdyYWRlMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGUxdnNDb250cm9sPUdyYWRlMS1Db250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTJ2c0NvbnRyb2w9R3JhZGUyLUNvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlM3ZzQ29udHJvbD1HcmFkZTMtQ29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3JhZGU0dnNDb250cm9sPUdyYWRlNC1Db250cm9sLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRldnNDb250cm9sPShHcmFkZTErR3JhZGUyK0dyYWRlMytHcmFkZTQpLzQtQ29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSGlnaEdyYWRldnNDb250cm9sPShHcmFkZTMrR3JhZGU0KS8yLUNvbnRyb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvd0dyYWRldnNDb250cm9sPShHcmFkZTErR3JhZGUyKS8yLUNvbnRyb2wsCiAgICAgICAgICAgICAgICAgIEhpZ2hHcmFkZXZzTG93R3JhZGU9KEdyYWRlMytHcmFkZTQpLzItKEdyYWRlMStHcmFkZTIpLzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyYWRlM3ZzTG93R3JhZGU9R3JhZGUzLShHcmFkZTErR3JhZGUyKS8yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHcmFkZTR2c0xvd0dyYWRlPUdyYWRlNC0oR3JhZGUxK0dyYWRlMikvMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWRlc2lnbi5odXByb3QpCgpmaXQyLmh1cHJvdCA8LSBjb250cmFzdHMuZml0KGZpdC5odXByb3QsIGNvbnRyYXN0Lm1hdHJpeC5odXByb3QpCmZpdDIuaHVwcm90IDwtIGVCYXllcyhmaXQyLmh1cHJvdCkKYGBgCgoKYGBge3J9CmNvZWZzIDwtIGMoJ0dyYWRlMnZzR3JhZGUxJywKICAgICAgICAgICAnR3JhZGUzdnNHcmFkZTInLAogICAgICAgICAgICdHcmFkZTR2c0dyYWRlMycsCiAgICAgICAgICAgJ0dyYWRlMXZzQ29udHJvbCcsCiAgICAgICAgICAgJ0dyYWRlMnZzQ29udHJvbCcsCiAgICAgICAgICAgJ0dyYWRlM3ZzQ29udHJvbCcsCiAgICAgICAgICAgJ0dyYWRlNHZzQ29udHJvbCcsCiAgICAgICAgICAgJ0dyYWRldnNDb250cm9sJywKICAgICAgICAgICAnSGlnaEdyYWRldnNDb250cm9sJywKICAgICAgICAgICAnTG93R3JhZGV2c0NvbnRyb2wnLAogICAgICAgICAgICdIaWdoR3JhZGV2c0xvd0dyYWRlJywKICAgICAgICAgICAnR3JhZGUzdnNMb3dHcmFkZScsCiAgICAgICAgICAgJ0dyYWRlNHZzTG93R3JhZGUnKQoKZm9yIChjb2VmIGluIGNvZWZzKXsKICB0dCA8LSB0b3BUYWJsZShmaXQyLmh1cHJvdCwgY29lZj1jb2VmLCBudW1iZXIgPSBJbmYpCiAgdHQuc2lnIDwtIHN1YnNldCh0dCwgdHQkYWRqLlAuVmFsPDAuMDEpCiAgd3JpdGUudGFibGUodHQsIGZpbGUucGF0aCgnLi4nLCAncmVzdWx0c19jb21tb24vaHVwcm90JywgcGFzdGUoY29lZiwnY3N2Jywgc2VwPScuJykgKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgd3JpdGUudGFibGUodHQuc2lnLCBmaWxlLnBhdGgoJy4uJywgJ3Jlc3VsdHNfY29tbW9uL2h1cHJvdCcsICdzaWduaWZpY2FudF9maWx0ZXJlZCcsIHBhc3RlKGNvZWYsJ2NzdicsIHNlcD0nLicpICksIHJvdy5uYW1lcyA9IEZBTFNFKQp9CmBgYAoKYGBge3J9CmZvciAoY29lZiBpbiBjb2Vmcyl7CiAgdHQgPC0gdG9wVGFibGUoZml0Mi5wcm90b2FycmF5LCBjb2VmPWNvZWYsIG51bWJlciA9IEluZikKICB0dC5zaWcgPC0gc3Vic2V0KHR0LCB0dCRhZGouUC5WYWw8MC4wMSkKICB3cml0ZS50YWJsZSh0dCwgZmlsZS5wYXRoKCcuLicsICdyZXN1bHRzX2NvbW1vbi9wcm90b2FycmF5JywgcGFzdGUoY29lZiwnY3N2Jywgc2VwPScuJykgKSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgd3JpdGUudGFibGUodHQuc2lnLCBmaWxlLnBhdGgoJy4uJywgJ3Jlc3VsdHNfY29tbW9uL3Byb3RvYXJyYXknLCAnc2lnbmlmaWNhbnRfZmlsdGVyZWQnLCBwYXN0ZShjb2VmLCdjc3YnLCBzZXA9Jy4nKSApLCByb3cubmFtZXMgPSBGQUxTRSkKfQoKYGBgCgoKIyMgU2ltaWxhcml0eSBiL3cgUHJvdG9hcnJheSAmIEh1cHJvdCBhcnJheSByZXN1bHRzCgpgYGB7cn0Kc3VtbWFyeS5kZiA8LSBkYXRhLmZyYW1lKGNvZWZmaWNpZW50PWNvZWZzLCBodXByb3Q9MCwgcHJvdG9hcnJheT0wLCBjb21tb249MCwgamFjY2FyZD0wLCBuYW1lcz0wLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCnJvdy5uYW1lcyhzdW1tYXJ5LmRmKSA8LSBjb2Vmcwpmb3IgKGNvZWYgaW4gY29lZnMpewogIGh1cHJvdC5yZXN1bHRzIDwtIHJlYWQudGFibGUoZmlsZS5wYXRoKCcuLicsICdyZXN1bHRzX2NvbW1vbi9odXByb3QnLCAnc2lnbmlmaWNhbnRfZmlsdGVyZWQnLCBwYXN0ZShjb2VmLCdjc3YnLCBzZXA9Jy4nKSApLCBoZWFkZXIgPSBUKQogIHByb3RvYXJyYXkucmVzdWx0cyA8LSByZWFkLnRhYmxlKGZpbGUucGF0aCgnLi4nLCAncmVzdWx0c19jb21tb24vcHJvdG9hcnJheScsICdzaWduaWZpY2FudF9maWx0ZXJlZCcsIHBhc3RlKGNvZWYsJ2NzdicsIHNlcD0nLicpICksIGhlYWRlciA9IFQpCiAgI25ld19uYW1lIDwtIHNhcHBseShhcy52ZWN0b3IocHJvdG9hcnJheS5yZXN1bHRzJE5hbWUpLCBjbGVhbl9uYW1lKQogICNwcm90b2FycmF5LnJlc3VsdHMkTmFtZSA8LSBuZXdfbmFtZQogIHRvdGFsIDwtIGxlbmd0aChodXByb3QucmVzdWx0cyRJRCkrIGxlbmd0aChwcm90b2FycmF5LnJlc3VsdHMkTmFtZSkKICBjb21tb24gPC0gbGVuZ3RoKGludGVyc2VjdChodXByb3QucmVzdWx0cyRJRCwgcHJvdG9hcnJheS5yZXN1bHRzJE5hbWUpKQogIHN1bW1hcnkuZGZbY29lZixdJGh1cHJvdCA8LSAgIGxlbmd0aChodXByb3QucmVzdWx0cyRJRCkKICBzdW1tYXJ5LmRmW2NvZWYsXSRwcm90b2FycmF5IDwtICAgbGVuZ3RoKHByb3RvYXJyYXkucmVzdWx0cyROYW1lKQogIHN1bW1hcnkuZGZbY29lZixdJGNvbW1vbiA8LSBjb21tb24gIAogIHN1bW1hcnkuZGZbY29lZixdJGphY2NhcmQgPC0gY29tbW9uL3RvdGFsICAKICBzdW1tYXJ5LmRmW2NvZWYsXSRuYW1lcyA8LSB0b1N0cmluZyhpbnRlcnNlY3QoaHVwcm90LnJlc3VsdHMkSUQsIHByb3RvYXJyYXkucmVzdWx0cyROYW1lKSkKfQpzdW1tYXJ5LmRmCmBgYAo=