I. Initialization

Import necessary libraries

# Libraries
library(tidyverse)
library(pROC)
library(cvAUC)
library(ggpubr)
library(ROCR)
library(pracma)
library(tidyverse)
library(MASS)
library(Hmisc)
library(reshape2)
library(caret)
library(Amelia)
library(mice)
library(bestNormalize)
library(gridExtra)
library(latex2exp)
library(UBL)
library(GGally)
library(ggpubr)
library(knitr)
library(brant)
library(car)
library(mlr)
library(rms)
library(viridis)
library(shadowtext)
library(VIM)

II. Figure 1: Distributions of numerical and ordinal predictors stratified by GOSE

(A) Distribution plots (violin and boxplot) of numerical variables

# Formatted GOSE labels
gose.labels <- c("1", "2 or 3", "4", "5", "6", "7", "8")

# Load untransformed, cleaned dataset
impact.dataframe <- read.csv('../impact_dataframe.csv')

# Pivot IMPACT dataframe into longer dataframe with noncategorical numerical features (including age)
impact.dataframe.long.noncat <- impact.dataframe %>% filter(is.na(glu) | glu <= 20) %>% pivot_longer(cols = c(age,Hb,glu)) %>% drop_na(value) %>% mutate(GOSE = factor(GOSE, order = TRUE)) %>% mutate(GOSE = plyr::mapvalues(GOSE,from = c("1","3","4","5","6","7","8"), to = gose.labels))

# Violin plots of each noncategorical numeric IMPACT predictor against GOSE outcomes
violPlots <- ggplot(impact.dataframe.long.noncat, aes(x = GOSE, y = value, fill = GOSE)) +
  geom_violin(size = .75, 
              alpha = 0.75,
              trim = TRUE) +
  geom_boxplot(width=0.15, 
               fill="white",
               outlier.shape = NA) +
  stat_compare_means(label = "p.signif", 
                     ref.group = ".all.",
                     hide.ns = TRUE) +
  geom_jitter(alpha = 0.15,
              size = .15) +
  facet_wrap(~name, 
             scales = "free_y",
             strip.position = "left",
             labeller = as_labeller(c(age = "Age (y)", glu = "Glucose (mmol/L)", Hb = "Hb (g/dL)") ) ) +
  ylab(NULL) +
  xlab('GOSE at 6 months post-injury') +
  theme_classic() +
  theme(strip.text = element_text(size=20), 
        axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1, size = 16, color = "black"),
        axis.text.y = element_text(size = 16, color = "black"),
        axis.title.x = element_text(size = 20),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        strip.background = element_blank(),
        strip.placement = "outside",
        legend.position = "none",
        aspect.ratio = 1)

print(violPlots)

(B) Segmented bar plots of categorical variables

# Formatted GOSE labels
gose.labels <- c("1", "2 or 3", "4", "5", "6", "7", "8")

# Load untransformed, cleaned dataset
impact.dataframe <- read.csv('../impact_dataframe.csv')

# Load and fix IMPACT variable dataframe
impact.dataframe.long.cat <- read.csv('../impact_dataframe.csv') %>% pivot_longer(cols = c(GCSm,marshall,unreactive_pupils)) %>% drop_na(value) %>% mutate(GOSE = factor(GOSE, order = TRUE)) %>% mutate(GOSE = plyr::mapvalues(GOSE,from = c("1","3","4","5","6","7","8"), to = gose.labels)) %>% group_by(GOSE, name, value) %>% summarise(count = n())
impact.dataframe.long.cat[impact.dataframe.long.cat$name == 'unreactive_pupils','value']<-impact.dataframe.long.cat[impact.dataframe.long.cat$name == 'unreactive_pupils','value']+1

barPlots <- ggplot(impact.dataframe.long.cat, aes(x = GOSE, y = count, fill = forcats::fct_rev(as.factor(value)))) +
  geom_bar(stat = "identity",
           position = "fill") +
  scale_fill_brewer(palette = "Set2") + 
    facet_wrap(~name, 
             scales = "free_y",
             strip.position = "left",
             labeller = as_labeller(c(GCSm = "Pr(GCSm)",marshall = "Pr(Marshall CT)", unreactive_pupils = "Pr(Unreactive Pupils)") )) +
  ylab('Proportion') +
  xlab('GOSE at 6 months post-injury') +
  theme_classic() +
  theme(strip.text = element_text(size=20), 
        axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1, size = 16, color = "black"),
        axis.text.y = element_text(size = 16, color = "black"),
        axis.title.x = element_text(size = 20),
        axis.title.y = element_blank(), 
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        strip.background = element_blank(),
        strip.placement = "outside",
        legend.position = "none",
        aspect.ratio = 1)

print(barPlots)

III. Figure 2: True predictor means vs. expected means under the proportional odds assumption

# Source function to fix IMPACT dataframe
source('./functions/fix_impact_dataframe.R')

# Source function to calculate proportional odds expectations
source('./functions/prop_odds_expectations.R')

# Reload IMPACT dataframe
impact.dataframe <- read.csv('../impact_dataframe.csv') %>% fix.impact.dataframe()

# Formatted GOSE labels
gose.labels <- c("1", "2 or 3", "4", "5", "6", "7", "8")

# `%notin%` <- Negate(`%in%`)
# predictor.set <- names(impact.dataframe)[names(impact.dataframe) %notin% c("entity_id","PatientType","GCS","GOSE")]
# 
# expect.table <- data.frame(matrix(nrow= 0, ncol = 5))
# for (i in 1:length(predictor.set)){
#   curr.pred.name <- predictor.set[i]
#   curr.x <- impact.dataframe[curr.pred.name]
#   not.missing.idx <- which(!is.na(curr.x))
#   non.missing.x <- curr.x[not.missing.idx,1]
#   non.missing.y <- impact.dataframe$GOSE[not.missing.idx]
#   if (is.factor(non.missing.x)){
#     f <- table(non.missing.x)
#     ncat <- length(f)
#     if (ncat < 2) {
#       warning(paste("predictor", curr.pred.name, "only has one level and is ignored"))
#       next
#     }
#     nc <- ncat - 1
#     cats <- (names(f)[order(-f)])[(ncat-nc+1):ncat]
#     for (wcat in cats) {
#       xx <- 1 * (non.missing.x == wcat)
#       curr.expect <- prop.odds.expectation(xx, non.missing.y)
#     }
#   } else {
#     curr.expect <- prop.odds.expectation(non.missing.x, non.missing.y)
#   }
#   curr.expect$predictor <- curr.pred.name
#   expect.table <- rbind(expect.table, curr.expect)
#   names(expect.table) <- names(curr.expect) 
# }
# expect.table <- expect.table %>% 
#   mutate(GOSE = factor(GOSE, order = TRUE)) %>% 
#   mutate(GOSE = plyr::mapvalues(GOSE,from = c("1","3","4","5","6","7","8"), to = gose.labels)) %>%
#   rowwise() %>% 
#   mutate(errorMin = max(xmean.y-xsd.y,0), errorMax = xmean.y+xsd.y)
# 
# write.csv(expect.table,'../prop_odds_expectations.csv',row.names = FALSE)

expect.table <- read.csv('../prop_odds_expectations.csv') %>%
    mutate(GOSE = factor(GOSE, order = TRUE)) 

propOddsPlots <- ggplot(expect.table, aes(x = GOSE, group=1)) +
  geom_line(aes(y = xmean.y), size=1) + 
  geom_point(aes(y = xmean.y), size=3) +
  geom_line(aes(y=xmean.y.po),linetype = "dashed", size=1.20) + 
  facet_wrap(~predictor, 
             ncol = 3,
             scales = "free_y",
             strip.position = "left",
             labeller = as_labeller(c(age = "Age (y)",
                                      unreactive_pupils = "Unreactive Pupils",
                                      hypoxia = "Pr(Hypoxia)",
                                      hypotension = "Pr(Hypotension)",
                                      marshall = "Marshall CT",
                                      tsah = "Pr(tSAH)",
                                      EDH = "Pr(EDH)",
                                      hypotension = "Pr(Hypotension)",
                                      glu = "Glucose (mmol/L)", 
                                      Hb = "Hb (g/dL)",
                                      GCSm = "GCSm")))  +
  ylab(NULL) +
  xlab('GOSE at 6 months post-injury') +
  theme_classic()+
  theme(strip.text = element_text(size=20), 
        axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1, size = 16, color = "black"),
        axis.text.y = element_text(size = 16, color = "black"),
        axis.title.x = element_text(size = 20),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        strip.background = element_blank(),
        panel.border = element_rect(colour = "black", fill=NA, size = 2),
        strip.placement = "outside",
        aspect.ratio = 1)

plot(propOddsPlots)

IV. Figure 3: ROC and PRC per each GOSE score at 6 months post-injury

Load compiled ROC and PRC axes information (calculated during model evaluation)


# Load IMPACT dataframe and calculate class frequencies for PRC reference
impact.dataframe <- read.csv('../impact_dataframe.csv')
class.freqs <- as.data.frame(table(impact.dataframe$GOSE)/nrow(impact.dataframe)) %>% rename(class = Var1, class.freq = Freq)

# Load and compile axes for each model type
mnlr.plot.roc.pcr.axes <- read.csv('../metrics/mnlr_compiled_plot_roc_prc_axes.csv') %>%
  mutate(Model = 'MNLR')

polr.plot.roc.pcr.axes <- read.csv('../metrics/polr_compiled_plot_roc_prc_axes.csv') %>%
  mutate(Model = 'POLR')

deepMN.plot.roc.pcr.axes <- read.csv('../metrics/deepMN_compiled_plot_roc_prc_axes.csv') %>%
  mutate(Model = 'DeepMN')

deepOR.plot.roc.pcr.axes <- read.csv('../metrics/deepOR_compiled_plot_roc_prc_axes.csv') %>%
  mutate(Model = 'DeepOR')

compiled.plot.roc.pcr.axes <- rbind(mnlr.plot.roc.pcr.axes,polr.plot.roc.pcr.axes,
                                    deepMN.plot.roc.pcr.axes,deepOR.plot.roc.pcr.axes)

# Repair plot endpoints
roc.left.endpoint.idx <- which(compiled.plot.roc.pcr.axes$type == 'roc' & compiled.plot.roc.pcr.axes$x == 0)
roc.right.endpoint.idx <- which(compiled.plot.roc.pcr.axes$type == 'roc' & compiled.plot.roc.pcr.axes$x == 1)
prc.left.endpoint.idx <- which(compiled.plot.roc.pcr.axes$type == 'prc' & compiled.plot.roc.pcr.axes$x == 0)

compiled.plot.roc.pcr.axes[roc.left.endpoint.idx,c("mean.y","lower.ci.y","upper.ci.y")] <- 0
compiled.plot.roc.pcr.axes[roc.right.endpoint.idx,c("mean.y","lower.ci.y","upper.ci.y")] <- 1
compiled.plot.roc.pcr.axes[prc.left.endpoint.idx,c("mean.y","lower.ci.y","upper.ci.y")] <- 1

# Change class integers to GOSE labels
compiled.plot.roc.pcr.axes <- compiled.plot.roc.pcr.axes %>%
  mutate(class = as.factor(class)) %>%
  left_join(class.freqs,by = 'class') %>%
  mutate(class = plyr::mapvalues(class, 
                                 from = c(1,3,4,5,6,7,8),
                                 to = c("GOSE: 1", "GOSE: 2 or 3", "GOSE: 4", "GOSE: 5", "GOSE: 6", "GOSE: 7", "GOSE: 8")))

# Change order of models
compiled.plot.roc.pcr.axes$Model <- factor(compiled.plot.roc.pcr.axes$Model, levels = c('MNLR','POLR','DeepMN','DeepOR'))

(A) Receiver operating characteristic (ROC) curves per each GOSE score at 6-mo post-injury

roc.curves <- compiled.plot.roc.pcr.axes %>% 
  filter(type == 'roc') %>%
  ggplot(aes(x = x)) +
  facet_wrap( ~ class,
              scales = 'free',
              ncol = 4) +
  xlab("False Positive Rate") +
  ylab("True Positive Rate") +
  coord_cartesian(ylim = c(0,1),xlim = c(0,1))+
  geom_ribbon(aes(ymin = lower.ci.y, ymax = upper.ci.y, fill = Model), alpha = 0.3) +
  geom_line(aes(y = mean.y, color = Model), alpha = 0.75, size=1.20) +
  guides(linetype = FALSE, color = guide_legend(nrow = 2)) +
  geom_segment(x = 0, y = 0, xend = 1, yend = 1,alpha = 0.5,linetype = "dashed",size=1, color = 'gray70')+
  theme_classic()+
  theme(
    strip.text = element_text(size=20), 
    panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(size = 12, color = "black"),
    axis.text.y = element_text(size = 12, color = "black"),
    axis.title.x = element_text(size = 22),
    axis.title.y = element_text(size = 22),
    strip.background = element_blank(),
    panel.border = element_rect(colour = "black", fill=NA, size = 2),
    legend.position = c(0.85, .25),
    legend.title = element_text(size=20),
    legend.text=element_text(size=16),
    plot.title = element_text(hjust = 0.5),
    aspect.ratio = 1
  )

plot(roc.curves)

(B) Precision-recall (PRC) curves per each GOSE score at 6-mo post-injury

prc.curves <- compiled.plot.roc.pcr.axes %>% 
  filter(type == 'prc') %>%
  ggplot(aes(x = x)) +
  facet_wrap( ~ class,
              scales = 'free',
              ncol = 4) +
  xlab("Recall") +
  ylab("Precision") +
  coord_cartesian(ylim = c(0,1),xlim = c(0,1))+
  geom_ribbon(aes(ymin = lower.ci.y, ymax = upper.ci.y, fill = Model), alpha = 0.3) +
  geom_line(aes(y = mean.y, color = Model), alpha = 0.75, size=1.20) +
  guides(linetype = FALSE, color = guide_legend(nrow = 2)) +
  geom_segment(aes(y = class.freq, yend = class.freq),x = 0, xend = 1, alpha = 0.5,linetype = "dashed",size=1, color = 'gray70')+
  theme_classic()+
  theme(
    strip.text = element_text(size=20), 
    panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(size = 12, color = "black"),
    axis.text.y = element_text(size = 12, color = "black"),
    axis.title.x = element_text(size = 22),
    axis.title.y = element_text(size = 22),
    strip.background = element_blank(),
    panel.border = element_rect(colour = "black", fill=NA, size = 2),
    legend.position = c(0.85, .25),
    legend.title = element_text(size=20),
    legend.text=element_text(size=16),
    plot.title = element_text(hjust = 0.5),
    aspect.ratio = 1
  )

plot(prc.curves)

V. Figure 4: Probability calibration curves per each GOSE score at 6 months post-injury

# Set GOSE panel labels
gose.labels <- c("GOSE: 1", "GOSE: 2 or 3", "GOSE: 4", "GOSE: 5", "GOSE: 6", "GOSE: 7", "GOSE: 8")
# 
# # Load compiled calibration curves
# compiled.cal.curves.df <- read.csv('../metrics/compiled_calib_curves.csv') %>%
#   mutate(class = factor(class)) %>%
#   mutate(class = plyr::mapvalues(class, from = c(1,3,4,5,6,7,8), to = gose.labels))
# 
# # Interpolate calibration curves into common x-axes values
# interp.cal.curves.df <- data.frame(matrix(ncol = ncol(compiled.cal.curves.df),nrow=0))
# xq <- seq(from = 0, to = 1, length.out = 21)
# for (curr.label in unique(compiled.cal.curves.df$class)){
#   for (curr.bs.idx in unique(compiled.cal.curves.df$bs_idx)){
#     for (curr.model in unique(compiled.cal.curves.df$Model)){
#       curr.idx <- which(compiled.cal.curves.df$class == curr.label & compiled.cal.curves.df$bs_idx == curr.bs.idx & compiled.cal.curves.df$Model == curr.model)
#       if (length(curr.idx) == 0){
#         next
#       } else if (length(curr.idx) == 1){
#         interp.cal.curves.df <- rbind(interp.cal.curves.df, data.frame(prob_pred = xq[which.min(abs(compiled.cal.curves.df$prob_pred[curr.idx] - xq))], prob_true = compiled.cal.curves.df$prob_true[curr.idx], Model = curr.model, bs.idx = curr.bs.idx, class = curr.label))
#         next
#       }
#       interpol.object <- approx(x = compiled.cal.curves.df$prob_pred[curr.idx],y = compiled.cal.curves.df$prob_true[curr.idx],xout = xq)
#       interp.cal.curves.df <- rbind(interp.cal.curves.df, data.frame(prob_pred = interpol.object$x, prob_true = interpol.object$y, Model = curr.model, bs.idx = curr.bs.idx, class = curr.label))
#     }
#   }
# }
# # Save interpolated calibration dataframe
# write.csv(interp.cal.curves.df,'../metrics/interp_compiled_calib_curves.csv')
# 
# plot.interp.cal.curves.df <- interp.cal.curves.df %>%
#   group_by(class, Model, prob_pred) %>%
#   summarise(mean.y = mean(prob_true,na.rm = TRUE),
#             lower.ci.y = quantile(prob_true,.025,na.rm = TRUE),
#             upper.ci.y = quantile(prob_true,.975,na.rm = TRUE))
# 
# # Save summarized, interpolated calibration dataframe
# write.csv(plot.interp.cal.curves.df,'../metrics/plot_interp_compiled_calib_curves.csv')

# Load summary dataframes of interpolated ROC axes
plot.interp.cal.curves.df <- read.csv('../metrics/plot_interp_compiled_calib_curves.csv')

# Change order of models
plot.interp.cal.curves.df$Model <- factor(plot.interp.cal.curves.df$Model, levels = c('MNLR','POLR','DeepMN','DeepOR'))

calib.plot <-
  plot.interp.cal.curves.df %>%
  ggplot(aes(x = prob_pred)) +
  facet_wrap( ~ class,
              scales = 'free',
              ncol = 4) +
  xlab("Mean Predicted Probability") +
  ylab("Fraction of Positives") +
  geom_ribbon(aes(ymin = lower.ci.y, ymax = upper.ci.y, fill = Model),
              alpha = 0.3) +
  guides(linetype = FALSE, color = guide_legend(nrow = 2)) +
  geom_segment(x = 0, y = 0, xend = 1, yend = 1,alpha = 0.5,linetype = "dashed",size=1, color = 'gray70')+
  geom_line(aes(y = mean.y, color = Model), alpha = 0.8, size=1.20) +
  coord_cartesian(ylim = c(0,1),xlim = c(0,1))+
  theme_classic()+
  theme(
    strip.text = element_text(size=22), 
    panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(size = 12, color = "black"),
    axis.text.y = element_text(size = 12, color = "black"),
    axis.title.x = element_text(size = 22),
    axis.title.y = element_text(size = 22),
    strip.background = element_blank(),
    panel.border = element_rect(colour = "black", fill=NA, size = 2),
    legend.position = c(0.85, .25),
    legend.title = element_text(size=20),
    legend.text=element_text(size=16),
    plot.title = element_text(hjust = 0.5),
    aspect.ratio = 1
  )

plot(calib.plot)

VI. Figure 5: Normalised confusion matrices of each model type

# Load deepMN confusion matrix axes and average across repeats
deepMN.cm.values <- read.csv('../metrics/deepMN_confusionMatrices.csv') %>%
  group_by(true_labels,predicted_labels) %>%
  summarise(mean_cm_prob = mean(cm_prob), sd_prob = sd(cm_prob)) %>%
  mutate(Model = 'DeepMN')

# Load deepOR confusion matrix axes and average across repeats
deepOR.cm.values <- read.csv('../metrics/deepOR_confusionMatrices.csv') %>%
  group_by(true_labels,predicted_labels) %>%
  summarise(mean_cm_prob = mean(cm_prob), sd_prob = sd(cm_prob)) %>%
  mutate(Model = 'DeepOR')

# Load mnlr confusion matrix axes and average across repeats
mnlr.cm.values <- read.csv('../metrics/mnlr_confusionMatrices.csv') %>%
  group_by(true_labels,predicted_labels) %>%
  summarise(mean_cm_prob = mean(cm_prob), sd_prob = sd(cm_prob)) %>%
  mutate(Model = 'MNLR')

# Load polr confusion matrix axes and average across repeats
polr.cm.values <- read.csv('../metrics/polr_confusionMatrices.csv') %>%
  group_by(true_labels,predicted_labels) %>%
  summarise(mean_cm_prob = mean(cm_prob), sd_prob = sd(cm_prob)) %>%
  mutate(Model = 'POLR')

# Compile all model types into one dataframe
compiled.confusion.matrix <- rbind(deepMN.cm.values, deepOR.cm.values, mnlr.cm.values, polr.cm.values)
compiled.confusion.matrix <- compiled.confusion.matrix %>% mutate(Model = factor(Model, levels = c("MNLR","POLR","DeepMN","DeepOR")),
                                                                  true_labels = factor(true_labels),
                                                                  predicted_labels = factor(predicted_labels))

# Plot confusion matrices
confusionMatrixPlots <- ggplot(compiled.confusion.matrix, aes(x = predicted_labels,y = true_labels,fill = mean_cm_prob))+
  geom_tile() +
  geom_shadowtext(aes(label= sprintf("%0.2f",mean_cm_prob)),color="white", size = 6)+
  scale_fill_viridis(discrete=FALSE,limits=c(0, 1), breaks=seq(0,1,by=0.25)) +
  guides(fill = guide_colourbar(barwidth = 40,
                                title = 'Mean proportion of predicted label given true label',
                                title.position = 'top',
                                title.hjust = .5)) +
  ylab(label = "True Labels") +
  xlab(label = 'Predicted Labels')+
  scale_y_discrete(limits = rev(levels(compiled.confusion.matrix$true_labels)))+
  facet_wrap(~Model, nrow = 2, ncol = 2)+
  theme_classic()+
  theme(
    strip.text = element_text(size=22, color = 'black'), 
    panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(size = 18, color = "black",angle = 45, hjust = 1, vjust = 1),
    axis.text.y = element_text(size = 18, color = "black",angle = 45, hjust = 1, vjust = 0),
    axis.title.x = element_text(size = 22),
    axis.title.y = element_text(size = 22),
    strip.background = element_blank(),
    panel.border = element_rect(colour = "black", fill=NA, size = 2),
    legend.position = 'bottom',
    legend.title = element_text(size = 18, color = "black"),
    legend.text=element_text(size=16),
    aspect.ratio = 1
  )

plot(confusionMatrixPlots)

VII. Supplementary Figure 1: Missingness pattern of IMPACT predictor variables in CENTER-TBI

# Load function to fix IMPACT variable types in the dataframe
source('functions/fix_impact_dataframe.R')

# Load cleaned sample set labels for stratified cross-validation sampling
impact.dataframe <- read.csv('../impact_dataframe.csv') %>% fix.impact.dataframe()

# Convert pupillary reactivity variable type to integer for imputation
impact.dataframe$unreactive_pupils <- as.integer(impact.dataframe$unreactive_pupils) - 1

# Remove non-IMPACT predictors
plot.impact.dataframe <- impact.dataframe %>% dplyr::select(-c(entity_id,PatientType,GCS,GOSE))

# Shorten plot labels to fit
plt.labels <- names(plot.impact.dataframe)
plt.labels[1] <- "Age"
plt.labels[2] <- "P.R."
plt.labels[5] <- "Glu."
plt.labels[6] <- "Hypoxia"
plt.labels[7] <- "HoTN"
plt.labels[8] <- "Marshall"
plt.labels[9] <- "tSAH"

# Produce both barplots of missing variables and combinations plot
miss.aggr <- aggr(plot.impact.dataframe,numbers=TRUE,labels = plt.labels)

VII. Supplementary Figure 2: Effect of optimized Box-Cox transformation and standardization on distributions of numerical and ordinal variables

# Load function to fix IMPACT variable types in the dataframe
source('functions/fix_impact_dataframe.R')

# Load IMPACT dataframe and fix variable types
impact.dataframe <- read.csv('../impact_dataframe.csv') %>% fix.impact.dataframe()

# Identify columns to undergo Box-Cox normalization
bc.columns <- c('age','GCSm','Hb','glu','marshall')

# Initialize empty lists to store Box-Cox models and make modifiable copy of dataset
bc <- vector(mode = 'list')
bc.impact.dataframe <- impact.dataframe
copy.impact.dataframe <- impact.dataframe

# Initialize empty list to store ggplot objects
bc.plots <- list()
# Loop through to-normalize columns
for (curr.col in bc.columns){
  if (curr.col %in% c('GCSm','marshall')){
    copy.impact.dataframe[[curr.col]] <- as.factor(copy.impact.dataframe[[curr.col]])
    if (curr.col == 'marshall'){
      copy.impact.dataframe[[curr.col]] <- plyr::mapvalues(copy.impact.dataframe[[curr.col]], from = c('1','2','3','4','5','6'), to = c('I','II','III','IV','V','VI'))
    }
    bc.plots[[paste0(curr.col, '.og.density')]] <- copy.impact.dataframe %>%
      drop_na(curr.col) %>%
      ggplot(aes_string(x = curr.col)) +
      geom_bar(aes(y = ..prop.., group = 1)) +
      theme_classic() +
      theme(axis.text.x = element_text(size = 16, color = "black"),
            axis.text.y = element_text(size = 16, color = "black"),
            axis.title.x = element_blank(),
            axis.title.y = element_blank(),
            panel.border = element_rect(colour = "black", fill=NA, size = 2),
            legend.position = "none")
  } else {
    bc.plots[[paste0(curr.col, '.og.density')]] <- copy.impact.dataframe %>%
      drop_na(curr.col) %>%
      ggplot(aes_string(x = curr.col)) +
      geom_histogram(aes(y =..density..), bins = min(100,length(unique(copy.impact.dataframe[[curr.col]]))) ) +
      theme_classic() +
      theme(axis.text.x = element_text(size = 16, color = "black"),
            axis.text.y = element_text(size = 16, color = "black"),
            axis.title.x = element_blank(),
            axis.title.y = element_blank(),
            panel.border = element_rect(colour = "black", fill=NA, size = 2),
            legend.position = "none")
  }
  # First, plot original density of current numeric variable

  # Transform current numeric variable and store transformed variable
  bc[[curr.col]] <- boxcox(impact.dataframe[[curr.col]],
                           standardize = TRUE)
  bc.impact.dataframe[, curr.col] <- bc[[curr.col]]$x.t
  curr.shapiro.test <- shapiro.test(bc.impact.dataframe[[curr.col]])

  # Second, plot transformed density of current numeric variable
  bc.plots[[paste0(curr.col, '.tf.density')]] <- ggplot(bc.impact.dataframe, aes_string(x = curr.col)) +
    geom_density() +
    theme_classic() +
    theme(axis.text.x = element_text(size = 16, color = "black"),
          axis.text.y = element_text(size = 16, color = "black"),
          axis.title.x = element_blank(),
          axis.title.y = element_blank(),
          panel.border = element_rect(colour = "black", fill=NA, size = 2),
          legend.position = "none")
  # Third, plot normal q-q plot of transformed numeric variable
  bc.plots[[paste0(curr.col, '.tf.normqq')]] <- ggplot(bc.impact.dataframe, aes_string(sample = curr.col)) +
    stat_qq() +
    stat_qq_line() +
    theme_classic() +
    theme(axis.text.x = element_text(size = 16, color = "black"),
          axis.text.y = element_text(size = 16, color = "black"),
          axis.title.x = element_blank(),
          axis.title.y = element_blank(),
          panel.border = element_rect(colour = "black", fill=NA, size = 2),
          legend.position = "none")
  # Fourth, plot the transformation (Box-Cox) function of the current numeric variable
  xx <- seq(min(impact.dataframe[[curr.col]],na.rm = TRUE), max(impact.dataframe[[curr.col]],na.rm = TRUE), length = 500)
  yy <- predict(bc[[curr.col]],newdata = xx)
  xrng <- range(xx)
  yrng <- range(yy)
  curr.tf.df <- data.frame(xx,yy)
  bc.plots[[paste0(curr.col,'.tf.function')]] <- ggplot(curr.tf.df,aes(xx,yy)) +
    geom_line() +
    theme_classic() +
    theme(axis.text.x = element_text(size = 16, color = "black"),
          axis.text.y = element_text(size = 16, color = "black"),
          axis.title.x = element_blank(),
          axis.title.y = element_blank(),
          panel.border = element_rect(colour = "black", fill=NA, size = 2),
          legend.position = "none") 
    # annotate("text", x=xrng[2], y=yrng[1], label=TeX(sprintf("$\\lambda = %0.2f$", bc[[curr.col]]$lambda)),hjust = 1, vjust = 0, size = 8)
}
# Save Box-Cox plots object:
saveRDS(bc.plots,'../box_cox_plots.rds')

# Load Box-Cox plots object:
bc.plots <-readRDS('../box_cox_plots.rds')
do.call("grid.arrange",c(bc.plots,nrow=length(bc.columns),ncol=4))


print('Y-axis labels (Top-to-bottom): Age, GCSm, Hb, Glucose, Marshall CT')
[1] "Y-axis labels (Top-to-bottom): Age, GCSm, Hb, Glucose, Marshall CT"
print('X-axis labels (Left-to-Right): Original Density, Transformed Density, Transformed Density, Transformed Q-Q, Transformation Function')
[1] "X-axis labels (Left-to-Right): Original Density, Transformed Density, Transformed Density, Transformed Q-Q, Transformation Function"
LS0tCnRpdGxlOiAiUGxvdCBmaWd1cmVzIGZvciB0aGUgbWFudXNjcmlwdCIKYXV0aG9yOgotIFNodWJoYXl1IEJoYXR0YWNoYXJ5YXkKLSBBcmkgRXJjb2xlCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Ci5tYWluLWNvbnRhaW5lciB7Cm1heC13aWR0aDogMTgwMHB4OwptYXJnaW4tbGVmdDogYXV0bzsKbWFyZ2luLXJpZ2h0OiBhdXRvOwp9Cjwvc3R5bGU+CgojIyBJLiBJbml0aWFsaXphdGlvbgoKIyMjIEltcG9ydCBuZWNlc3NhcnkgbGlicmFyaWVzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoY3ZBVUMpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KFJPQ1IpCmxpYnJhcnkocHJhY21hKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShNQVNTKQpsaWJyYXJ5KEhtaXNjKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KEFtZWxpYSkKbGlicmFyeShtaWNlKQpsaWJyYXJ5KGJlc3ROb3JtYWxpemUpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGxhdGV4MmV4cCkKbGlicmFyeShVQkwpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShrbml0cikKbGlicmFyeShicmFudCkKbGlicmFyeShjYXIpCmxpYnJhcnkobWxyKQpsaWJyYXJ5KHJtcykKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KHNoYWRvd3RleHQpCmxpYnJhcnkoVklNKQpgYGAKCiMjIElJLiBGaWd1cmUgMTogRGlzdHJpYnV0aW9ucyBvZiBudW1lcmljYWwgYW5kIG9yZGluYWwgcHJlZGljdG9ycyBzdHJhdGlmaWVkIGJ5IEdPU0UKCiMjIyAoQSkgRGlzdHJpYnV0aW9uIHBsb3RzICh2aW9saW4gYW5kIGJveHBsb3QpIG9mIG51bWVyaWNhbCB2YXJpYWJsZXMKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTYuNSwgZmlnLmhlaWdodD0yLjE3fQojIEZvcm1hdHRlZCBHT1NFIGxhYmVscwpnb3NlLmxhYmVscyA8LSBjKCIxIiwgIjIgb3IgMyIsICI0IiwgIjUiLCAiNiIsICI3IiwgIjgiKQoKIyBMb2FkIHVudHJhbnNmb3JtZWQsIGNsZWFuZWQgZGF0YXNldAppbXBhY3QuZGF0YWZyYW1lIDwtIHJlYWQuY3N2KCcuLi9pbXBhY3RfZGF0YWZyYW1lLmNzdicpCgojIFBpdm90IElNUEFDVCBkYXRhZnJhbWUgaW50byBsb25nZXIgZGF0YWZyYW1lIHdpdGggbm9uY2F0ZWdvcmljYWwgbnVtZXJpY2FsIGZlYXR1cmVzIChpbmNsdWRpbmcgYWdlKQppbXBhY3QuZGF0YWZyYW1lLmxvbmcubm9uY2F0IDwtIGltcGFjdC5kYXRhZnJhbWUgJT4lIGZpbHRlcihpcy5uYShnbHUpIHwgZ2x1IDw9IDIwKSAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGFnZSxIYixnbHUpKSAlPiUgZHJvcF9uYSh2YWx1ZSkgJT4lIG11dGF0ZShHT1NFID0gZmFjdG9yKEdPU0UsIG9yZGVyID0gVFJVRSkpICU+JSBtdXRhdGUoR09TRSA9IHBseXI6Om1hcHZhbHVlcyhHT1NFLGZyb20gPSBjKCIxIiwiMyIsIjQiLCI1IiwiNiIsIjciLCI4IiksIHRvID0gZ29zZS5sYWJlbHMpKQoKIyBWaW9saW4gcGxvdHMgb2YgZWFjaCBub25jYXRlZ29yaWNhbCBudW1lcmljIElNUEFDVCBwcmVkaWN0b3IgYWdhaW5zdCBHT1NFIG91dGNvbWVzCnZpb2xQbG90cyA8LSBnZ3Bsb3QoaW1wYWN0LmRhdGFmcmFtZS5sb25nLm5vbmNhdCwgYWVzKHggPSBHT1NFLCB5ID0gdmFsdWUsIGZpbGwgPSBHT1NFKSkgKwogIGdlb21fdmlvbGluKHNpemUgPSAuNzUsIAogICAgICAgICAgICAgIGFscGhhID0gMC43NSwKICAgICAgICAgICAgICB0cmltID0gVFJVRSkgKwogIGdlb21fYm94cGxvdCh3aWR0aD0wLjE1LCAKICAgICAgICAgICAgICAgZmlsbD0id2hpdGUiLAogICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlID0gTkEpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMobGFiZWwgPSAicC5zaWduaWYiLCAKICAgICAgICAgICAgICAgICAgICAgcmVmLmdyb3VwID0gIi5hbGwuIiwKICAgICAgICAgICAgICAgICAgICAgaGlkZS5ucyA9IFRSVUUpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMTUsCiAgICAgICAgICAgICAgc2l6ZSA9IC4xNSkgKwogIGZhY2V0X3dyYXAofm5hbWUsIAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeSIsCiAgICAgICAgICAgICBzdHJpcC5wb3NpdGlvbiA9ICJsZWZ0IiwKICAgICAgICAgICAgIGxhYmVsbGVyID0gYXNfbGFiZWxsZXIoYyhhZ2UgPSAiQWdlICh5KSIsIGdsdSA9ICJHbHVjb3NlIChtbW9sL0wpIiwgSGIgPSAiSGIgKGcvZEwpIikgKSApICsKICB5bGFiKE5VTEwpICsKICB4bGFiKCdHT1NFIGF0IDYgbW9udGhzIHBvc3QtaW5qdXJ5JykgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCB2anVzdCA9IDEsIHNpemUgPSAxNiwgY29sb3IgPSAiYmxhY2siKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXNwZWN0LnJhdGlvID0gMSkKCnByaW50KHZpb2xQbG90cykKYGBgCgojIyMgKEIpIFNlZ21lbnRlZCBiYXIgcGxvdHMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD02LjUsIGZpZy5oZWlnaHQ9Mi4xN30KIyBGb3JtYXR0ZWQgR09TRSBsYWJlbHMKZ29zZS5sYWJlbHMgPC0gYygiMSIsICIyIG9yIDMiLCAiNCIsICI1IiwgIjYiLCAiNyIsICI4IikKCiMgTG9hZCB1bnRyYW5zZm9ybWVkLCBjbGVhbmVkIGRhdGFzZXQKaW1wYWN0LmRhdGFmcmFtZSA8LSByZWFkLmNzdignLi4vaW1wYWN0X2RhdGFmcmFtZS5jc3YnKQoKIyBMb2FkIGFuZCBmaXggSU1QQUNUIHZhcmlhYmxlIGRhdGFmcmFtZQppbXBhY3QuZGF0YWZyYW1lLmxvbmcuY2F0IDwtIHJlYWQuY3N2KCcuLi9pbXBhY3RfZGF0YWZyYW1lLmNzdicpICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoR0NTbSxtYXJzaGFsbCx1bnJlYWN0aXZlX3B1cGlscykpICU+JSBkcm9wX25hKHZhbHVlKSAlPiUgbXV0YXRlKEdPU0UgPSBmYWN0b3IoR09TRSwgb3JkZXIgPSBUUlVFKSkgJT4lIG11dGF0ZShHT1NFID0gcGx5cjo6bWFwdmFsdWVzKEdPU0UsZnJvbSA9IGMoIjEiLCIzIiwiNCIsIjUiLCI2IiwiNyIsIjgiKSwgdG8gPSBnb3NlLmxhYmVscykpICU+JSBncm91cF9ieShHT1NFLCBuYW1lLCB2YWx1ZSkgJT4lIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKaW1wYWN0LmRhdGFmcmFtZS5sb25nLmNhdFtpbXBhY3QuZGF0YWZyYW1lLmxvbmcuY2F0JG5hbWUgPT0gJ3VucmVhY3RpdmVfcHVwaWxzJywndmFsdWUnXTwtaW1wYWN0LmRhdGFmcmFtZS5sb25nLmNhdFtpbXBhY3QuZGF0YWZyYW1lLmxvbmcuY2F0JG5hbWUgPT0gJ3VucmVhY3RpdmVfcHVwaWxzJywndmFsdWUnXSsxCgpiYXJQbG90cyA8LSBnZ3Bsb3QoaW1wYWN0LmRhdGFmcmFtZS5sb25nLmNhdCwgYWVzKHggPSBHT1NFLCB5ID0gY291bnQsIGZpbGwgPSBmb3JjYXRzOjpmY3RfcmV2KGFzLmZhY3Rvcih2YWx1ZSkpKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLAogICAgICAgICAgIHBvc2l0aW9uID0gImZpbGwiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKyAKICAgIGZhY2V0X3dyYXAofm5hbWUsIAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeSIsCiAgICAgICAgICAgICBzdHJpcC5wb3NpdGlvbiA9ICJsZWZ0IiwKICAgICAgICAgICAgIGxhYmVsbGVyID0gYXNfbGFiZWxsZXIoYyhHQ1NtID0gIlByKEdDU20pIixtYXJzaGFsbCA9ICJQcihNYXJzaGFsbCBDVCkiLCB1bnJlYWN0aXZlX3B1cGlscyA9ICJQcihVbnJlYWN0aXZlIFB1cGlscykiKSApKSArCiAgeWxhYignUHJvcG9ydGlvbicpICsKICB4bGFiKCdHT1NFIGF0IDYgbW9udGhzIHBvc3QtaW5qdXJ5JykgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCB2anVzdCA9IDEsIHNpemUgPSAxNiwgY29sb3IgPSAiYmxhY2siKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBhc3BlY3QucmF0aW8gPSAxKQoKcHJpbnQoYmFyUGxvdHMpCmBgYAoKIyMgSUlJLiBGaWd1cmUgMjogVHJ1ZSBwcmVkaWN0b3IgbWVhbnMgdnMuIGV4cGVjdGVkIG1lYW5zIHVuZGVyIHRoZSBwcm9wb3J0aW9uYWwgb2RkcyBhc3N1bXB0aW9uCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9Ni41LCBmaWcuaGVpZ2h0PTcuMTJ9CiMgU291cmNlIGZ1bmN0aW9uIHRvIGZpeCBJTVBBQ1QgZGF0YWZyYW1lCnNvdXJjZSgnLi9mdW5jdGlvbnMvZml4X2ltcGFjdF9kYXRhZnJhbWUuUicpCgojIFNvdXJjZSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgcHJvcG9ydGlvbmFsIG9kZHMgZXhwZWN0YXRpb25zCnNvdXJjZSgnLi9mdW5jdGlvbnMvcHJvcF9vZGRzX2V4cGVjdGF0aW9ucy5SJykKCiMgUmVsb2FkIElNUEFDVCBkYXRhZnJhbWUKaW1wYWN0LmRhdGFmcmFtZSA8LSByZWFkLmNzdignLi4vaW1wYWN0X2RhdGFmcmFtZS5jc3YnKSAlPiUgZml4LmltcGFjdC5kYXRhZnJhbWUoKQoKIyBGb3JtYXR0ZWQgR09TRSBsYWJlbHMKZ29zZS5sYWJlbHMgPC0gYygiMSIsICIyIG9yIDMiLCAiNCIsICI1IiwgIjYiLCAiNyIsICI4IikKCiMgYCVub3RpbiVgIDwtIE5lZ2F0ZShgJWluJWApCiMgcHJlZGljdG9yLnNldCA8LSBuYW1lcyhpbXBhY3QuZGF0YWZyYW1lKVtuYW1lcyhpbXBhY3QuZGF0YWZyYW1lKSAlbm90aW4lIGMoImVudGl0eV9pZCIsIlBhdGllbnRUeXBlIiwiR0NTIiwiR09TRSIpXQojIAojIGV4cGVjdC50YWJsZSA8LSBkYXRhLmZyYW1lKG1hdHJpeChucm93PSAwLCBuY29sID0gNSkpCiMgZm9yIChpIGluIDE6bGVuZ3RoKHByZWRpY3Rvci5zZXQpKXsKIyAgIGN1cnIucHJlZC5uYW1lIDwtIHByZWRpY3Rvci5zZXRbaV0KIyAgIGN1cnIueCA8LSBpbXBhY3QuZGF0YWZyYW1lW2N1cnIucHJlZC5uYW1lXQojICAgbm90Lm1pc3NpbmcuaWR4IDwtIHdoaWNoKCFpcy5uYShjdXJyLngpKQojICAgbm9uLm1pc3NpbmcueCA8LSBjdXJyLnhbbm90Lm1pc3NpbmcuaWR4LDFdCiMgICBub24ubWlzc2luZy55IDwtIGltcGFjdC5kYXRhZnJhbWUkR09TRVtub3QubWlzc2luZy5pZHhdCiMgICBpZiAoaXMuZmFjdG9yKG5vbi5taXNzaW5nLngpKXsKIyAgICAgZiA8LSB0YWJsZShub24ubWlzc2luZy54KQojICAgICBuY2F0IDwtIGxlbmd0aChmKQojICAgICBpZiAobmNhdCA8IDIpIHsKIyAgICAgICB3YXJuaW5nKHBhc3RlKCJwcmVkaWN0b3IiLCBjdXJyLnByZWQubmFtZSwgIm9ubHkgaGFzIG9uZSBsZXZlbCBhbmQgaXMgaWdub3JlZCIpKQojICAgICAgIG5leHQKIyAgICAgfQojICAgICBuYyA8LSBuY2F0IC0gMQojICAgICBjYXRzIDwtIChuYW1lcyhmKVtvcmRlcigtZildKVsobmNhdC1uYysxKTpuY2F0XQojICAgICBmb3IgKHdjYXQgaW4gY2F0cykgewojICAgICAgIHh4IDwtIDEgKiAobm9uLm1pc3NpbmcueCA9PSB3Y2F0KQojICAgICAgIGN1cnIuZXhwZWN0IDwtIHByb3Aub2Rkcy5leHBlY3RhdGlvbih4eCwgbm9uLm1pc3NpbmcueSkKIyAgICAgfQojICAgfSBlbHNlIHsKIyAgICAgY3Vyci5leHBlY3QgPC0gcHJvcC5vZGRzLmV4cGVjdGF0aW9uKG5vbi5taXNzaW5nLngsIG5vbi5taXNzaW5nLnkpCiMgICB9CiMgICBjdXJyLmV4cGVjdCRwcmVkaWN0b3IgPC0gY3Vyci5wcmVkLm5hbWUKIyAgIGV4cGVjdC50YWJsZSA8LSByYmluZChleHBlY3QudGFibGUsIGN1cnIuZXhwZWN0KQojICAgbmFtZXMoZXhwZWN0LnRhYmxlKSA8LSBuYW1lcyhjdXJyLmV4cGVjdCkgCiMgfQojIGV4cGVjdC50YWJsZSA8LSBleHBlY3QudGFibGUgJT4lIAojICAgbXV0YXRlKEdPU0UgPSBmYWN0b3IoR09TRSwgb3JkZXIgPSBUUlVFKSkgJT4lIAojICAgbXV0YXRlKEdPU0UgPSBwbHlyOjptYXB2YWx1ZXMoR09TRSxmcm9tID0gYygiMSIsIjMiLCI0IiwiNSIsIjYiLCI3IiwiOCIpLCB0byA9IGdvc2UubGFiZWxzKSkgJT4lCiMgICByb3d3aXNlKCkgJT4lIAojICAgbXV0YXRlKGVycm9yTWluID0gbWF4KHhtZWFuLnkteHNkLnksMCksIGVycm9yTWF4ID0geG1lYW4ueSt4c2QueSkKIyAKIyB3cml0ZS5jc3YoZXhwZWN0LnRhYmxlLCcuLi9wcm9wX29kZHNfZXhwZWN0YXRpb25zLmNzdicscm93Lm5hbWVzID0gRkFMU0UpCgpleHBlY3QudGFibGUgPC0gcmVhZC5jc3YoJy4uL3Byb3Bfb2Rkc19leHBlY3RhdGlvbnMuY3N2JykgJT4lCiAgICBtdXRhdGUoR09TRSA9IGZhY3RvcihHT1NFLCBvcmRlciA9IFRSVUUpKSAKCnByb3BPZGRzUGxvdHMgPC0gZ2dwbG90KGV4cGVjdC50YWJsZSwgYWVzKHggPSBHT1NFLCBncm91cD0xKSkgKwogIGdlb21fbGluZShhZXMoeSA9IHhtZWFuLnkpLCBzaXplPTEpICsgCiAgZ2VvbV9wb2ludChhZXMoeSA9IHhtZWFuLnkpLCBzaXplPTMpICsKICBnZW9tX2xpbmUoYWVzKHk9eG1lYW4ueS5wbyksbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZT0xLjIwKSArIAogIGZhY2V0X3dyYXAofnByZWRpY3RvciwgCiAgICAgICAgICAgICBuY29sID0gMywKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiLAogICAgICAgICAgICAgc3RyaXAucG9zaXRpb24gPSAibGVmdCIsCiAgICAgICAgICAgICBsYWJlbGxlciA9IGFzX2xhYmVsbGVyKGMoYWdlID0gIkFnZSAoeSkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVucmVhY3RpdmVfcHVwaWxzID0gIlVucmVhY3RpdmUgUHVwaWxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoeXBveGlhID0gIlByKEh5cG94aWEpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoeXBvdGVuc2lvbiA9ICJQcihIeXBvdGVuc2lvbikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcnNoYWxsID0gIk1hcnNoYWxsIENUIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0c2FoID0gIlByKHRTQUgpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFREggPSAiUHIoRURIKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHlwb3RlbnNpb24gPSAiUHIoSHlwb3RlbnNpb24pIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnbHUgPSAiR2x1Y29zZSAobW1vbC9MKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhiID0gIkhiIChnL2RMKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR0NTbSA9ICJHQ1NtIikpKSAgKwogIHlsYWIoTlVMTCkgKwogIHhsYWIoJ0dPU0UgYXQgNiBtb250aHMgcG9zdC1pbmp1cnknKSArCiAgdGhlbWVfY2xhc3NpYygpKwogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgdmp1c3QgPSAxLCBzaXplID0gMTYsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZSA9IDIpLAogICAgICAgIHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgICAgICBhc3BlY3QucmF0aW8gPSAxKQoKcGxvdChwcm9wT2Rkc1Bsb3RzKQpgYGAKIyMgSVYuIEZpZ3VyZSAzOiBST0MgYW5kIFBSQyBwZXIgZWFjaCBHT1NFIHNjb3JlIGF0IDYgbW9udGhzIHBvc3QtaW5qdXJ5CgojIyMgTG9hZCBjb21waWxlZCBST0MgYW5kIFBSQyBheGVzIGluZm9ybWF0aW9uIChjYWxjdWxhdGVkIGR1cmluZyBtb2RlbCBldmFsdWF0aW9uKQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKIyBMb2FkIElNUEFDVCBkYXRhZnJhbWUgYW5kIGNhbGN1bGF0ZSBjbGFzcyBmcmVxdWVuY2llcyBmb3IgUFJDIHJlZmVyZW5jZQppbXBhY3QuZGF0YWZyYW1lIDwtIHJlYWQuY3N2KCcuLi9pbXBhY3RfZGF0YWZyYW1lLmNzdicpCmNsYXNzLmZyZXFzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoaW1wYWN0LmRhdGFmcmFtZSRHT1NFKS9ucm93KGltcGFjdC5kYXRhZnJhbWUpKSAlPiUgcmVuYW1lKGNsYXNzID0gVmFyMSwgY2xhc3MuZnJlcSA9IEZyZXEpCgojIExvYWQgYW5kIGNvbXBpbGUgYXhlcyBmb3IgZWFjaCBtb2RlbCB0eXBlCm1ubHIucGxvdC5yb2MucGNyLmF4ZXMgPC0gcmVhZC5jc3YoJy4uL21ldHJpY3MvbW5scl9jb21waWxlZF9wbG90X3JvY19wcmNfYXhlcy5jc3YnKSAlPiUKICBtdXRhdGUoTW9kZWwgPSAnTU5MUicpCgpwb2xyLnBsb3Qucm9jLnBjci5heGVzIDwtIHJlYWQuY3N2KCcuLi9tZXRyaWNzL3BvbHJfY29tcGlsZWRfcGxvdF9yb2NfcHJjX2F4ZXMuY3N2JykgJT4lCiAgbXV0YXRlKE1vZGVsID0gJ1BPTFInKQoKZGVlcE1OLnBsb3Qucm9jLnBjci5heGVzIDwtIHJlYWQuY3N2KCcuLi9tZXRyaWNzL2RlZXBNTl9jb21waWxlZF9wbG90X3JvY19wcmNfYXhlcy5jc3YnKSAlPiUKICBtdXRhdGUoTW9kZWwgPSAnRGVlcE1OJykKCmRlZXBPUi5wbG90LnJvYy5wY3IuYXhlcyA8LSByZWFkLmNzdignLi4vbWV0cmljcy9kZWVwT1JfY29tcGlsZWRfcGxvdF9yb2NfcHJjX2F4ZXMuY3N2JykgJT4lCiAgbXV0YXRlKE1vZGVsID0gJ0RlZXBPUicpCgpjb21waWxlZC5wbG90LnJvYy5wY3IuYXhlcyA8LSByYmluZChtbmxyLnBsb3Qucm9jLnBjci5heGVzLHBvbHIucGxvdC5yb2MucGNyLmF4ZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZXBNTi5wbG90LnJvYy5wY3IuYXhlcyxkZWVwT1IucGxvdC5yb2MucGNyLmF4ZXMpCgojIFJlcGFpciBwbG90IGVuZHBvaW50cwpyb2MubGVmdC5lbmRwb2ludC5pZHggPC0gd2hpY2goY29tcGlsZWQucGxvdC5yb2MucGNyLmF4ZXMkdHlwZSA9PSAncm9jJyAmIGNvbXBpbGVkLnBsb3Qucm9jLnBjci5heGVzJHggPT0gMCkKcm9jLnJpZ2h0LmVuZHBvaW50LmlkeCA8LSB3aGljaChjb21waWxlZC5wbG90LnJvYy5wY3IuYXhlcyR0eXBlID09ICdyb2MnICYgY29tcGlsZWQucGxvdC5yb2MucGNyLmF4ZXMkeCA9PSAxKQpwcmMubGVmdC5lbmRwb2ludC5pZHggPC0gd2hpY2goY29tcGlsZWQucGxvdC5yb2MucGNyLmF4ZXMkdHlwZSA9PSAncHJjJyAmIGNvbXBpbGVkLnBsb3Qucm9jLnBjci5heGVzJHggPT0gMCkKCmNvbXBpbGVkLnBsb3Qucm9jLnBjci5heGVzW3JvYy5sZWZ0LmVuZHBvaW50LmlkeCxjKCJtZWFuLnkiLCJsb3dlci5jaS55IiwidXBwZXIuY2kueSIpXSA8LSAwCmNvbXBpbGVkLnBsb3Qucm9jLnBjci5heGVzW3JvYy5yaWdodC5lbmRwb2ludC5pZHgsYygibWVhbi55IiwibG93ZXIuY2kueSIsInVwcGVyLmNpLnkiKV0gPC0gMQpjb21waWxlZC5wbG90LnJvYy5wY3IuYXhlc1twcmMubGVmdC5lbmRwb2ludC5pZHgsYygibWVhbi55IiwibG93ZXIuY2kueSIsInVwcGVyLmNpLnkiKV0gPC0gMQoKIyBDaGFuZ2UgY2xhc3MgaW50ZWdlcnMgdG8gR09TRSBsYWJlbHMKY29tcGlsZWQucGxvdC5yb2MucGNyLmF4ZXMgPC0gY29tcGlsZWQucGxvdC5yb2MucGNyLmF4ZXMgJT4lCiAgbXV0YXRlKGNsYXNzID0gYXMuZmFjdG9yKGNsYXNzKSkgJT4lCiAgbGVmdF9qb2luKGNsYXNzLmZyZXFzLGJ5ID0gJ2NsYXNzJykgJT4lCiAgbXV0YXRlKGNsYXNzID0gcGx5cjo6bWFwdmFsdWVzKGNsYXNzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbSA9IGMoMSwzLDQsNSw2LDcsOCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gYygiR09TRTogMSIsICJHT1NFOiAyIG9yIDMiLCAiR09TRTogNCIsICJHT1NFOiA1IiwgIkdPU0U6IDYiLCAiR09TRTogNyIsICJHT1NFOiA4IikpKQoKIyBDaGFuZ2Ugb3JkZXIgb2YgbW9kZWxzCmNvbXBpbGVkLnBsb3Qucm9jLnBjci5heGVzJE1vZGVsIDwtIGZhY3Rvcihjb21waWxlZC5wbG90LnJvYy5wY3IuYXhlcyRNb2RlbCwgbGV2ZWxzID0gYygnTU5MUicsJ1BPTFInLCdEZWVwTU4nLCdEZWVwT1InKSkKYGBgCgojIyMgKEEpIFJlY2VpdmVyIG9wZXJhdGluZyBjaGFyYWN0ZXJpc3RpYyAoUk9DKSBjdXJ2ZXMgcGVyIGVhY2ggR09TRSBzY29yZSBhdCA2LW1vIHBvc3QtaW5qdXJ5CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD02LjUsIGZpZy5oZWlnaHQ9My40fQpyb2MuY3VydmVzIDwtIGNvbXBpbGVkLnBsb3Qucm9jLnBjci5heGVzICU+JSAKICBmaWx0ZXIodHlwZSA9PSAncm9jJykgJT4lCiAgZ2dwbG90KGFlcyh4ID0geCkpICsKICBmYWNldF93cmFwKCB+IGNsYXNzLAogICAgICAgICAgICAgIHNjYWxlcyA9ICdmcmVlJywKICAgICAgICAgICAgICBuY29sID0gNCkgKwogIHhsYWIoIkZhbHNlIFBvc2l0aXZlIFJhdGUiKSArCiAgeWxhYigiVHJ1ZSBQb3NpdGl2ZSBSYXRlIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDEpLHhsaW0gPSBjKDAsMSkpKwogIGdlb21fcmliYm9uKGFlcyh5bWluID0gbG93ZXIuY2kueSwgeW1heCA9IHVwcGVyLmNpLnksIGZpbGwgPSBNb2RlbCksIGFscGhhID0gMC4zKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gbWVhbi55LCBjb2xvciA9IE1vZGVsKSwgYWxwaGEgPSAwLjc1LCBzaXplPTEuMjApICsKICBndWlkZXMobGluZXR5cGUgPSBGQUxTRSwgY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDIpKSArCiAgZ2VvbV9zZWdtZW50KHggPSAwLCB5ID0gMCwgeGVuZCA9IDEsIHllbmQgPSAxLGFscGhhID0gMC41LGxpbmV0eXBlID0gImRhc2hlZCIsc2l6ZT0xLCBjb2xvciA9ICdncmF5NzAnKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgdGhlbWUoCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAiYmxhY2siKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMiksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemUgPSAyKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44NSwgLjI1KSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwKICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE2KSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgYXNwZWN0LnJhdGlvID0gMQogICkKCnBsb3Qocm9jLmN1cnZlcykKYGBgCgojIyMgKEIpIFByZWNpc2lvbi1yZWNhbGwgKFBSQykgY3VydmVzIHBlciBlYWNoIEdPU0Ugc2NvcmUgYXQgNi1tbyBwb3N0LWluanVyeQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9Ni41LCBmaWcuaGVpZ2h0PTMuNH0KcHJjLmN1cnZlcyA8LSBjb21waWxlZC5wbG90LnJvYy5wY3IuYXhlcyAlPiUgCiAgZmlsdGVyKHR5cGUgPT0gJ3ByYycpICU+JQogIGdncGxvdChhZXMoeCA9IHgpKSArCiAgZmFjZXRfd3JhcCggfiBjbGFzcywKICAgICAgICAgICAgICBzY2FsZXMgPSAnZnJlZScsCiAgICAgICAgICAgICAgbmNvbCA9IDQpICsKICB4bGFiKCJSZWNhbGwiKSArCiAgeWxhYigiUHJlY2lzaW9uIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDEpLHhsaW0gPSBjKDAsMSkpKwogIGdlb21fcmliYm9uKGFlcyh5bWluID0gbG93ZXIuY2kueSwgeW1heCA9IHVwcGVyLmNpLnksIGZpbGwgPSBNb2RlbCksIGFscGhhID0gMC4zKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gbWVhbi55LCBjb2xvciA9IE1vZGVsKSwgYWxwaGEgPSAwLjc1LCBzaXplPTEuMjApICsKICBndWlkZXMobGluZXR5cGUgPSBGQUxTRSwgY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDIpKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh5ID0gY2xhc3MuZnJlcSwgeWVuZCA9IGNsYXNzLmZyZXEpLHggPSAwLCB4ZW5kID0gMSwgYWxwaGEgPSAwLjUsbGluZXR5cGUgPSAiZGFzaGVkIixzaXplPTEsIGNvbG9yID0gJ2dyYXk3MCcpKwogIHRoZW1lX2NsYXNzaWMoKSsKICB0aGVtZSgKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMiksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZSA9IDIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjg1LCAuMjUpLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjApLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICBhc3BlY3QucmF0aW8gPSAxCiAgKQoKcGxvdChwcmMuY3VydmVzKQpgYGAKCiMjIFYuIEZpZ3VyZSA0OiBQcm9iYWJpbGl0eSBjYWxpYnJhdGlvbiBjdXJ2ZXMgcGVyIGVhY2ggR09TRSBzY29yZSBhdCA2IG1vbnRocyBwb3N0LWluanVyeQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTYuNSwgZmlnLmhlaWdodD0zLjR9CiMgU2V0IEdPU0UgcGFuZWwgbGFiZWxzCmdvc2UubGFiZWxzIDwtIGMoIkdPU0U6IDEiLCAiR09TRTogMiBvciAzIiwgIkdPU0U6IDQiLCAiR09TRTogNSIsICJHT1NFOiA2IiwgIkdPU0U6IDciLCAiR09TRTogOCIpCiMgCiMgIyBMb2FkIGNvbXBpbGVkIGNhbGlicmF0aW9uIGN1cnZlcwojIGNvbXBpbGVkLmNhbC5jdXJ2ZXMuZGYgPC0gcmVhZC5jc3YoJy4uL21ldHJpY3MvY29tcGlsZWRfY2FsaWJfY3VydmVzLmNzdicpICU+JQojICAgbXV0YXRlKGNsYXNzID0gZmFjdG9yKGNsYXNzKSkgJT4lCiMgICBtdXRhdGUoY2xhc3MgPSBwbHlyOjptYXB2YWx1ZXMoY2xhc3MsIGZyb20gPSBjKDEsMyw0LDUsNiw3LDgpLCB0byA9IGdvc2UubGFiZWxzKSkKIyAKIyAjIEludGVycG9sYXRlIGNhbGlicmF0aW9uIGN1cnZlcyBpbnRvIGNvbW1vbiB4LWF4ZXMgdmFsdWVzCiMgaW50ZXJwLmNhbC5jdXJ2ZXMuZGYgPC0gZGF0YS5mcmFtZShtYXRyaXgobmNvbCA9IG5jb2woY29tcGlsZWQuY2FsLmN1cnZlcy5kZiksbnJvdz0wKSkKIyB4cSA8LSBzZXEoZnJvbSA9IDAsIHRvID0gMSwgbGVuZ3RoLm91dCA9IDIxKQojIGZvciAoY3Vyci5sYWJlbCBpbiB1bmlxdWUoY29tcGlsZWQuY2FsLmN1cnZlcy5kZiRjbGFzcykpewojICAgZm9yIChjdXJyLmJzLmlkeCBpbiB1bmlxdWUoY29tcGlsZWQuY2FsLmN1cnZlcy5kZiRic19pZHgpKXsKIyAgICAgZm9yIChjdXJyLm1vZGVsIGluIHVuaXF1ZShjb21waWxlZC5jYWwuY3VydmVzLmRmJE1vZGVsKSl7CiMgICAgICAgY3Vyci5pZHggPC0gd2hpY2goY29tcGlsZWQuY2FsLmN1cnZlcy5kZiRjbGFzcyA9PSBjdXJyLmxhYmVsICYgY29tcGlsZWQuY2FsLmN1cnZlcy5kZiRic19pZHggPT0gY3Vyci5icy5pZHggJiBjb21waWxlZC5jYWwuY3VydmVzLmRmJE1vZGVsID09IGN1cnIubW9kZWwpCiMgICAgICAgaWYgKGxlbmd0aChjdXJyLmlkeCkgPT0gMCl7CiMgICAgICAgICBuZXh0CiMgICAgICAgfSBlbHNlIGlmIChsZW5ndGgoY3Vyci5pZHgpID09IDEpewojICAgICAgICAgaW50ZXJwLmNhbC5jdXJ2ZXMuZGYgPC0gcmJpbmQoaW50ZXJwLmNhbC5jdXJ2ZXMuZGYsIGRhdGEuZnJhbWUocHJvYl9wcmVkID0geHFbd2hpY2gubWluKGFicyhjb21waWxlZC5jYWwuY3VydmVzLmRmJHByb2JfcHJlZFtjdXJyLmlkeF0gLSB4cSkpXSwgcHJvYl90cnVlID0gY29tcGlsZWQuY2FsLmN1cnZlcy5kZiRwcm9iX3RydWVbY3Vyci5pZHhdLCBNb2RlbCA9IGN1cnIubW9kZWwsIGJzLmlkeCA9IGN1cnIuYnMuaWR4LCBjbGFzcyA9IGN1cnIubGFiZWwpKQojICAgICAgICAgbmV4dAojICAgICAgIH0KIyAgICAgICBpbnRlcnBvbC5vYmplY3QgPC0gYXBwcm94KHggPSBjb21waWxlZC5jYWwuY3VydmVzLmRmJHByb2JfcHJlZFtjdXJyLmlkeF0seSA9IGNvbXBpbGVkLmNhbC5jdXJ2ZXMuZGYkcHJvYl90cnVlW2N1cnIuaWR4XSx4b3V0ID0geHEpCiMgICAgICAgaW50ZXJwLmNhbC5jdXJ2ZXMuZGYgPC0gcmJpbmQoaW50ZXJwLmNhbC5jdXJ2ZXMuZGYsIGRhdGEuZnJhbWUocHJvYl9wcmVkID0gaW50ZXJwb2wub2JqZWN0JHgsIHByb2JfdHJ1ZSA9IGludGVycG9sLm9iamVjdCR5LCBNb2RlbCA9IGN1cnIubW9kZWwsIGJzLmlkeCA9IGN1cnIuYnMuaWR4LCBjbGFzcyA9IGN1cnIubGFiZWwpKQojICAgICB9CiMgICB9CiMgfQojICMgU2F2ZSBpbnRlcnBvbGF0ZWQgY2FsaWJyYXRpb24gZGF0YWZyYW1lCiMgd3JpdGUuY3N2KGludGVycC5jYWwuY3VydmVzLmRmLCcuLi9tZXRyaWNzL2ludGVycF9jb21waWxlZF9jYWxpYl9jdXJ2ZXMuY3N2JykKIyAKIyBwbG90LmludGVycC5jYWwuY3VydmVzLmRmIDwtIGludGVycC5jYWwuY3VydmVzLmRmICU+JQojICAgZ3JvdXBfYnkoY2xhc3MsIE1vZGVsLCBwcm9iX3ByZWQpICU+JQojICAgc3VtbWFyaXNlKG1lYW4ueSA9IG1lYW4ocHJvYl90cnVlLG5hLnJtID0gVFJVRSksCiMgICAgICAgICAgICAgbG93ZXIuY2kueSA9IHF1YW50aWxlKHByb2JfdHJ1ZSwuMDI1LG5hLnJtID0gVFJVRSksCiMgICAgICAgICAgICAgdXBwZXIuY2kueSA9IHF1YW50aWxlKHByb2JfdHJ1ZSwuOTc1LG5hLnJtID0gVFJVRSkpCiMgCiMgIyBTYXZlIHN1bW1hcml6ZWQsIGludGVycG9sYXRlZCBjYWxpYnJhdGlvbiBkYXRhZnJhbWUKIyB3cml0ZS5jc3YocGxvdC5pbnRlcnAuY2FsLmN1cnZlcy5kZiwnLi4vbWV0cmljcy9wbG90X2ludGVycF9jb21waWxlZF9jYWxpYl9jdXJ2ZXMuY3N2JykKCiMgTG9hZCBzdW1tYXJ5IGRhdGFmcmFtZXMgb2YgaW50ZXJwb2xhdGVkIFJPQyBheGVzCnBsb3QuaW50ZXJwLmNhbC5jdXJ2ZXMuZGYgPC0gcmVhZC5jc3YoJy4uL21ldHJpY3MvcGxvdF9pbnRlcnBfY29tcGlsZWRfY2FsaWJfY3VydmVzLmNzdicpCgojIENoYW5nZSBvcmRlciBvZiBtb2RlbHMKcGxvdC5pbnRlcnAuY2FsLmN1cnZlcy5kZiRNb2RlbCA8LSBmYWN0b3IocGxvdC5pbnRlcnAuY2FsLmN1cnZlcy5kZiRNb2RlbCwgbGV2ZWxzID0gYygnTU5MUicsJ1BPTFInLCdEZWVwTU4nLCdEZWVwT1InKSkKCmNhbGliLnBsb3QgPC0KICBwbG90LmludGVycC5jYWwuY3VydmVzLmRmICU+JQogIGdncGxvdChhZXMoeCA9IHByb2JfcHJlZCkpICsKICBmYWNldF93cmFwKCB+IGNsYXNzLAogICAgICAgICAgICAgIHNjYWxlcyA9ICdmcmVlJywKICAgICAgICAgICAgICBuY29sID0gNCkgKwogIHhsYWIoIk1lYW4gUHJlZGljdGVkIFByb2JhYmlsaXR5IikgKwogIHlsYWIoIkZyYWN0aW9uIG9mIFBvc2l0aXZlcyIpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGxvd2VyLmNpLnksIHltYXggPSB1cHBlci5jaS55LCBmaWxsID0gTW9kZWwpLAogICAgICAgICAgICAgIGFscGhhID0gMC4zKSArCiAgZ3VpZGVzKGxpbmV0eXBlID0gRkFMU0UsIGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAyKSkgKwogIGdlb21fc2VnbWVudCh4ID0gMCwgeSA9IDAsIHhlbmQgPSAxLCB5ZW5kID0gMSxhbHBoYSA9IDAuNSxsaW5ldHlwZSA9ICJkYXNoZWQiLHNpemU9MSwgY29sb3IgPSAnZ3JheTcwJykrCiAgZ2VvbV9saW5lKGFlcyh5ID0gbWVhbi55LCBjb2xvciA9IE1vZGVsKSwgYWxwaGEgPSAwLjgsIHNpemU9MS4yMCkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDEpLHhsaW0gPSBjKDAsMSkpKwogIHRoZW1lX2NsYXNzaWMoKSsKICB0aGVtZSgKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMiksIAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMiksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZSA9IDIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjg1LCAuMjUpLAogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjApLAogICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTYpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICBhc3BlY3QucmF0aW8gPSAxCiAgKQoKcGxvdChjYWxpYi5wbG90KQpgYGAKCiMjIFZJLiBGaWd1cmUgNTogTm9ybWFsaXNlZCBjb25mdXNpb24gbWF0cmljZXMgb2YgZWFjaCBtb2RlbCB0eXBlCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9Ni41LCBmaWcuaGVpZ2h0PTcuNjZ9CiMgTG9hZCBkZWVwTU4gY29uZnVzaW9uIG1hdHJpeCBheGVzIGFuZCBhdmVyYWdlIGFjcm9zcyByZXBlYXRzCmRlZXBNTi5jbS52YWx1ZXMgPC0gcmVhZC5jc3YoJy4uL21ldHJpY3MvZGVlcE1OX2NvbmZ1c2lvbk1hdHJpY2VzLmNzdicpICU+JQogIGdyb3VwX2J5KHRydWVfbGFiZWxzLHByZWRpY3RlZF9sYWJlbHMpICU+JQogIHN1bW1hcmlzZShtZWFuX2NtX3Byb2IgPSBtZWFuKGNtX3Byb2IpLCBzZF9wcm9iID0gc2QoY21fcHJvYikpICU+JQogIG11dGF0ZShNb2RlbCA9ICdEZWVwTU4nKQoKIyBMb2FkIGRlZXBPUiBjb25mdXNpb24gbWF0cml4IGF4ZXMgYW5kIGF2ZXJhZ2UgYWNyb3NzIHJlcGVhdHMKZGVlcE9SLmNtLnZhbHVlcyA8LSByZWFkLmNzdignLi4vbWV0cmljcy9kZWVwT1JfY29uZnVzaW9uTWF0cmljZXMuY3N2JykgJT4lCiAgZ3JvdXBfYnkodHJ1ZV9sYWJlbHMscHJlZGljdGVkX2xhYmVscykgJT4lCiAgc3VtbWFyaXNlKG1lYW5fY21fcHJvYiA9IG1lYW4oY21fcHJvYiksIHNkX3Byb2IgPSBzZChjbV9wcm9iKSkgJT4lCiAgbXV0YXRlKE1vZGVsID0gJ0RlZXBPUicpCgojIExvYWQgbW5sciBjb25mdXNpb24gbWF0cml4IGF4ZXMgYW5kIGF2ZXJhZ2UgYWNyb3NzIHJlcGVhdHMKbW5sci5jbS52YWx1ZXMgPC0gcmVhZC5jc3YoJy4uL21ldHJpY3MvbW5scl9jb25mdXNpb25NYXRyaWNlcy5jc3YnKSAlPiUKICBncm91cF9ieSh0cnVlX2xhYmVscyxwcmVkaWN0ZWRfbGFiZWxzKSAlPiUKICBzdW1tYXJpc2UobWVhbl9jbV9wcm9iID0gbWVhbihjbV9wcm9iKSwgc2RfcHJvYiA9IHNkKGNtX3Byb2IpKSAlPiUKICBtdXRhdGUoTW9kZWwgPSAnTU5MUicpCgojIExvYWQgcG9sciBjb25mdXNpb24gbWF0cml4IGF4ZXMgYW5kIGF2ZXJhZ2UgYWNyb3NzIHJlcGVhdHMKcG9sci5jbS52YWx1ZXMgPC0gcmVhZC5jc3YoJy4uL21ldHJpY3MvcG9scl9jb25mdXNpb25NYXRyaWNlcy5jc3YnKSAlPiUKICBncm91cF9ieSh0cnVlX2xhYmVscyxwcmVkaWN0ZWRfbGFiZWxzKSAlPiUKICBzdW1tYXJpc2UobWVhbl9jbV9wcm9iID0gbWVhbihjbV9wcm9iKSwgc2RfcHJvYiA9IHNkKGNtX3Byb2IpKSAlPiUKICBtdXRhdGUoTW9kZWwgPSAnUE9MUicpCgojIENvbXBpbGUgYWxsIG1vZGVsIHR5cGVzIGludG8gb25lIGRhdGFmcmFtZQpjb21waWxlZC5jb25mdXNpb24ubWF0cml4IDwtIHJiaW5kKGRlZXBNTi5jbS52YWx1ZXMsIGRlZXBPUi5jbS52YWx1ZXMsIG1ubHIuY20udmFsdWVzLCBwb2xyLmNtLnZhbHVlcykKY29tcGlsZWQuY29uZnVzaW9uLm1hdHJpeCA8LSBjb21waWxlZC5jb25mdXNpb24ubWF0cml4ICU+JSBtdXRhdGUoTW9kZWwgPSBmYWN0b3IoTW9kZWwsIGxldmVscyA9IGMoIk1OTFIiLCJQT0xSIiwiRGVlcE1OIiwiRGVlcE9SIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlX2xhYmVscyA9IGZhY3Rvcih0cnVlX2xhYmVscyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWRpY3RlZF9sYWJlbHMgPSBmYWN0b3IocHJlZGljdGVkX2xhYmVscykpCgojIFBsb3QgY29uZnVzaW9uIG1hdHJpY2VzCmNvbmZ1c2lvbk1hdHJpeFBsb3RzIDwtIGdncGxvdChjb21waWxlZC5jb25mdXNpb24ubWF0cml4LCBhZXMoeCA9IHByZWRpY3RlZF9sYWJlbHMseSA9IHRydWVfbGFiZWxzLGZpbGwgPSBtZWFuX2NtX3Byb2IpKSsKICBnZW9tX3RpbGUoKSArCiAgZ2VvbV9zaGFkb3d0ZXh0KGFlcyhsYWJlbD0gc3ByaW50ZigiJTAuMmYiLG1lYW5fY21fcHJvYikpLGNvbG9yPSJ3aGl0ZSIsIHNpemUgPSA2KSsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9RkFMU0UsbGltaXRzPWMoMCwgMSksIGJyZWFrcz1zZXEoMCwxLGJ5PTAuMjUpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9jb2xvdXJiYXIoYmFyd2lkdGggPSA0MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICdNZWFuIHByb3BvcnRpb24gb2YgcHJlZGljdGVkIGxhYmVsIGdpdmVuIHRydWUgbGFiZWwnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gJ3RvcCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUuaGp1c3QgPSAuNSkpICsKICB5bGFiKGxhYmVsID0gIlRydWUgTGFiZWxzIikgKwogIHhsYWIobGFiZWwgPSAnUHJlZGljdGVkIExhYmVscycpKwogIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gcmV2KGxldmVscyhjb21waWxlZC5jb25mdXNpb24ubWF0cml4JHRydWVfbGFiZWxzKSkpKwogIGZhY2V0X3dyYXAofk1vZGVsLCBucm93ID0gMiwgbmNvbCA9IDIpKwogIHRoZW1lX2NsYXNzaWMoKSsKICB0aGVtZSgKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMiwgY29sb3IgPSAnYmxhY2snKSwgCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGNvbG9yID0gImJsYWNrIixhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMSksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGNvbG9yID0gImJsYWNrIixhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BLCBzaXplID0gMiksCiAgICBsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGNvbG9yID0gImJsYWNrIiksCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgICBhc3BlY3QucmF0aW8gPSAxCiAgKQoKcGxvdChjb25mdXNpb25NYXRyaXhQbG90cykKYGBgCgojIyBWSUkuIFN1cHBsZW1lbnRhcnkgRmlndXJlIDE6IE1pc3NpbmduZXNzIHBhdHRlcm4gb2YgSU1QQUNUIHByZWRpY3RvciB2YXJpYWJsZXMgaW4gQ0VOVEVSLVRCSQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTV9CiMgTG9hZCBmdW5jdGlvbiB0byBmaXggSU1QQUNUIHZhcmlhYmxlIHR5cGVzIGluIHRoZSBkYXRhZnJhbWUKc291cmNlKCdmdW5jdGlvbnMvZml4X2ltcGFjdF9kYXRhZnJhbWUuUicpCgojIExvYWQgY2xlYW5lZCBzYW1wbGUgc2V0IGxhYmVscyBmb3Igc3RyYXRpZmllZCBjcm9zcy12YWxpZGF0aW9uIHNhbXBsaW5nCmltcGFjdC5kYXRhZnJhbWUgPC0gcmVhZC5jc3YoJy4uL2ltcGFjdF9kYXRhZnJhbWUuY3N2JykgJT4lIGZpeC5pbXBhY3QuZGF0YWZyYW1lKCkKCiMgQ29udmVydCBwdXBpbGxhcnkgcmVhY3Rpdml0eSB2YXJpYWJsZSB0eXBlIHRvIGludGVnZXIgZm9yIGltcHV0YXRpb24KaW1wYWN0LmRhdGFmcmFtZSR1bnJlYWN0aXZlX3B1cGlscyA8LSBhcy5pbnRlZ2VyKGltcGFjdC5kYXRhZnJhbWUkdW5yZWFjdGl2ZV9wdXBpbHMpIC0gMQoKIyBSZW1vdmUgbm9uLUlNUEFDVCBwcmVkaWN0b3JzCnBsb3QuaW1wYWN0LmRhdGFmcmFtZSA8LSBpbXBhY3QuZGF0YWZyYW1lICU+JSBkcGx5cjo6c2VsZWN0KC1jKGVudGl0eV9pZCxQYXRpZW50VHlwZSxHQ1MsR09TRSkpCgojIFNob3J0ZW4gcGxvdCBsYWJlbHMgdG8gZml0CnBsdC5sYWJlbHMgPC0gbmFtZXMocGxvdC5pbXBhY3QuZGF0YWZyYW1lKQpwbHQubGFiZWxzWzFdIDwtICJBZ2UiCnBsdC5sYWJlbHNbMl0gPC0gIlAuUi4iCnBsdC5sYWJlbHNbNV0gPC0gIkdsdS4iCnBsdC5sYWJlbHNbNl0gPC0gIkh5cG94aWEiCnBsdC5sYWJlbHNbN10gPC0gIkhvVE4iCnBsdC5sYWJlbHNbOF0gPC0gIk1hcnNoYWxsIgpwbHQubGFiZWxzWzldIDwtICJ0U0FIIgoKIyBQcm9kdWNlIGJvdGggYmFycGxvdHMgb2YgbWlzc2luZyB2YXJpYWJsZXMgYW5kIGNvbWJpbmF0aW9ucyBwbG90Cm1pc3MuYWdnciA8LSBhZ2dyKHBsb3QuaW1wYWN0LmRhdGFmcmFtZSxudW1iZXJzPVRSVUUsbGFiZWxzID0gcGx0LmxhYmVscykKYGBgCiMjIFZJSS4gU3VwcGxlbWVudGFyeSBGaWd1cmUgMjogRWZmZWN0IG9mIG9wdGltaXplZCBCb3gtQ294IHRyYW5zZm9ybWF0aW9uIGFuZCBzdGFuZGFyZGl6YXRpb24gb24gZGlzdHJpYnV0aW9ucyBvZiBudW1lcmljYWwgYW5kIG9yZGluYWwgdmFyaWFibGVzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD02LjUsIGZpZy5oZWlnaHQ9NC41fQojIExvYWQgZnVuY3Rpb24gdG8gZml4IElNUEFDVCB2YXJpYWJsZSB0eXBlcyBpbiB0aGUgZGF0YWZyYW1lCnNvdXJjZSgnZnVuY3Rpb25zL2ZpeF9pbXBhY3RfZGF0YWZyYW1lLlInKQoKIyBMb2FkIElNUEFDVCBkYXRhZnJhbWUgYW5kIGZpeCB2YXJpYWJsZSB0eXBlcwppbXBhY3QuZGF0YWZyYW1lIDwtIHJlYWQuY3N2KCcuLi9pbXBhY3RfZGF0YWZyYW1lLmNzdicpICU+JSBmaXguaW1wYWN0LmRhdGFmcmFtZSgpCgojIElkZW50aWZ5IGNvbHVtbnMgdG8gdW5kZXJnbyBCb3gtQ294IG5vcm1hbGl6YXRpb24KYmMuY29sdW1ucyA8LSBjKCdhZ2UnLCdHQ1NtJywnSGInLCdnbHUnLCdtYXJzaGFsbCcpCgojICMgSW5pdGlhbGl6ZSBlbXB0eSBsaXN0cyB0byBzdG9yZSBCb3gtQ294IG1vZGVscyBhbmQgbWFrZSBtb2RpZmlhYmxlIGNvcHkgb2YgZGF0YXNldAojIGJjIDwtIHZlY3Rvcihtb2RlID0gJ2xpc3QnKQojIGJjLmltcGFjdC5kYXRhZnJhbWUgPC0gaW1wYWN0LmRhdGFmcmFtZQojIGNvcHkuaW1wYWN0LmRhdGFmcmFtZSA8LSBpbXBhY3QuZGF0YWZyYW1lCiMgCiMgIyBJbml0aWFsaXplIGVtcHR5IGxpc3QgdG8gc3RvcmUgZ2dwbG90IG9iamVjdHMKIyBiYy5wbG90cyA8LSBsaXN0KCkKIyAjIExvb3AgdGhyb3VnaCB0by1ub3JtYWxpemUgY29sdW1ucwojIGZvciAoY3Vyci5jb2wgaW4gYmMuY29sdW1ucyl7CiMgICBpZiAoY3Vyci5jb2wgJWluJSBjKCdHQ1NtJywnbWFyc2hhbGwnKSl7CiMgICAgIGNvcHkuaW1wYWN0LmRhdGFmcmFtZVtbY3Vyci5jb2xdXSA8LSBhcy5mYWN0b3IoY29weS5pbXBhY3QuZGF0YWZyYW1lW1tjdXJyLmNvbF1dKQojICAgICBpZiAoY3Vyci5jb2wgPT0gJ21hcnNoYWxsJyl7CiMgICAgICAgY29weS5pbXBhY3QuZGF0YWZyYW1lW1tjdXJyLmNvbF1dIDwtIHBseXI6Om1hcHZhbHVlcyhjb3B5LmltcGFjdC5kYXRhZnJhbWVbW2N1cnIuY29sXV0sIGZyb20gPSBjKCcxJywnMicsJzMnLCc0JywnNScsJzYnKSwgdG8gPSBjKCdJJywnSUknLCdJSUknLCdJVicsJ1YnLCdWSScpKQojICAgICB9CiMgICAgIGJjLnBsb3RzW1twYXN0ZTAoY3Vyci5jb2wsICcub2cuZGVuc2l0eScpXV0gPC0gY29weS5pbXBhY3QuZGF0YWZyYW1lICU+JQojICAgICAgIGRyb3BfbmEoY3Vyci5jb2wpICU+JQojICAgICAgIGdncGxvdChhZXNfc3RyaW5nKHggPSBjdXJyLmNvbCkpICsKIyAgICAgICBnZW9tX2JhcihhZXMoeSA9IC4ucHJvcC4uLCBncm91cCA9IDEpKSArCiMgICAgICAgdGhlbWVfY2xhc3NpYygpICsKIyAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImJsYWNrIiksCiMgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJibGFjayIpLAojICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiMgICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemUgPSAyKSwKIyAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiMgICB9IGVsc2UgewojICAgICBiYy5wbG90c1tbcGFzdGUwKGN1cnIuY29sLCAnLm9nLmRlbnNpdHknKV1dIDwtIGNvcHkuaW1wYWN0LmRhdGFmcmFtZSAlPiUKIyAgICAgICBkcm9wX25hKGN1cnIuY29sKSAlPiUKIyAgICAgICBnZ3Bsb3QoYWVzX3N0cmluZyh4ID0gY3Vyci5jb2wpKSArCiMgICAgICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPS4uZGVuc2l0eS4uKSwgYmlucyA9IG1pbigxMDAsbGVuZ3RoKHVuaXF1ZShjb3B5LmltcGFjdC5kYXRhZnJhbWVbW2N1cnIuY29sXV0pKSkgKSArCiMgICAgICAgdGhlbWVfY2xhc3NpYygpICsKIyAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImJsYWNrIiksCiMgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJibGFjayIpLAojICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiMgICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemUgPSAyKSwKIyAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiMgICB9CiMgICAjIEZpcnN0LCBwbG90IG9yaWdpbmFsIGRlbnNpdHkgb2YgY3VycmVudCBudW1lcmljIHZhcmlhYmxlCiMgCiMgICAjIFRyYW5zZm9ybSBjdXJyZW50IG51bWVyaWMgdmFyaWFibGUgYW5kIHN0b3JlIHRyYW5zZm9ybWVkIHZhcmlhYmxlCiMgICBiY1tbY3Vyci5jb2xdXSA8LSBib3hjb3goaW1wYWN0LmRhdGFmcmFtZVtbY3Vyci5jb2xdXSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFuZGFyZGl6ZSA9IFRSVUUpCiMgICBiYy5pbXBhY3QuZGF0YWZyYW1lWywgY3Vyci5jb2xdIDwtIGJjW1tjdXJyLmNvbF1dJHgudAojICAgY3Vyci5zaGFwaXJvLnRlc3QgPC0gc2hhcGlyby50ZXN0KGJjLmltcGFjdC5kYXRhZnJhbWVbW2N1cnIuY29sXV0pCiMgCiMgICAjIFNlY29uZCwgcGxvdCB0cmFuc2Zvcm1lZCBkZW5zaXR5IG9mIGN1cnJlbnQgbnVtZXJpYyB2YXJpYWJsZQojICAgYmMucGxvdHNbW3Bhc3RlMChjdXJyLmNvbCwgJy50Zi5kZW5zaXR5JyldXSA8LSBnZ3Bsb3QoYmMuaW1wYWN0LmRhdGFmcmFtZSwgYWVzX3N0cmluZyh4ID0gY3Vyci5jb2wpKSArCiMgICAgIGdlb21fZGVuc2l0eSgpICsKIyAgICAgdGhlbWVfY2xhc3NpYygpICsKIyAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJibGFjayIpLAojICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImJsYWNrIiksCiMgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAojICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZSA9IDIpLAojICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiMgICAjIFRoaXJkLCBwbG90IG5vcm1hbCBxLXEgcGxvdCBvZiB0cmFuc2Zvcm1lZCBudW1lcmljIHZhcmlhYmxlCiMgICBiYy5wbG90c1tbcGFzdGUwKGN1cnIuY29sLCAnLnRmLm5vcm1xcScpXV0gPC0gZ2dwbG90KGJjLmltcGFjdC5kYXRhZnJhbWUsIGFlc19zdHJpbmcoc2FtcGxlID0gY3Vyci5jb2wpKSArCiMgICAgIHN0YXRfcXEoKSArCiMgICAgIHN0YXRfcXFfbGluZSgpICsKIyAgICAgdGhlbWVfY2xhc3NpYygpICsKIyAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJibGFjayIpLAojICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImJsYWNrIiksCiMgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAojICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZSA9IDIpLAojICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiMgICAjIEZvdXJ0aCwgcGxvdCB0aGUgdHJhbnNmb3JtYXRpb24gKEJveC1Db3gpIGZ1bmN0aW9uIG9mIHRoZSBjdXJyZW50IG51bWVyaWMgdmFyaWFibGUKIyAgIHh4IDwtIHNlcShtaW4oaW1wYWN0LmRhdGFmcmFtZVtbY3Vyci5jb2xdXSxuYS5ybSA9IFRSVUUpLCBtYXgoaW1wYWN0LmRhdGFmcmFtZVtbY3Vyci5jb2xdXSxuYS5ybSA9IFRSVUUpLCBsZW5ndGggPSA1MDApCiMgICB5eSA8LSBwcmVkaWN0KGJjW1tjdXJyLmNvbF1dLG5ld2RhdGEgPSB4eCkKIyAgIHhybmcgPC0gcmFuZ2UoeHgpCiMgICB5cm5nIDwtIHJhbmdlKHl5KQojICAgY3Vyci50Zi5kZiA8LSBkYXRhLmZyYW1lKHh4LHl5KQojICAgYmMucGxvdHNbW3Bhc3RlMChjdXJyLmNvbCwnLnRmLmZ1bmN0aW9uJyldXSA8LSBnZ3Bsb3QoY3Vyci50Zi5kZixhZXMoeHgseXkpKSArCiMgICAgIGdlb21fbGluZSgpICsKIyAgICAgdGhlbWVfY2xhc3NpYygpICsKIyAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBjb2xvciA9ICJibGFjayIpLAojICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gImJsYWNrIiksCiMgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAojICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSwgc2l6ZSA9IDIpLAojICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpIAojICAgICAjIGFubm90YXRlKCJ0ZXh0IiwgeD14cm5nWzJdLCB5PXlybmdbMV0sIGxhYmVsPVRlWChzcHJpbnRmKCIkXFxsYW1iZGEgPSAlMC4yZiQiLCBiY1tbY3Vyci5jb2xdXSRsYW1iZGEpKSxoanVzdCA9IDEsIHZqdXN0ID0gMCwgc2l6ZSA9IDgpCiMgfQojICMgU2F2ZSBCb3gtQ294IHBsb3RzIG9iamVjdDoKIyBzYXZlUkRTKGJjLnBsb3RzLCcuLi9ib3hfY294X3Bsb3RzLnJkcycpCgojIExvYWQgQm94LUNveCBwbG90cyBvYmplY3Q6CmJjLnBsb3RzIDwtcmVhZFJEUygnLi4vYm94X2NveF9wbG90cy5yZHMnKQpkby5jYWxsKCJncmlkLmFycmFuZ2UiLGMoYmMucGxvdHMsbnJvdz1sZW5ndGgoYmMuY29sdW1ucyksbmNvbD00KSkKCnByaW50KCdZLWF4aXMgbGFiZWxzIChUb3AtdG8tYm90dG9tKTogQWdlLCBHQ1NtLCBIYiwgR2x1Y29zZSwgTWFyc2hhbGwgQ1QnKQpwcmludCgnWC1heGlzIGxhYmVscyAoTGVmdC10by1SaWdodCk6IE9yaWdpbmFsIERlbnNpdHksIFRyYW5zZm9ybWVkIERlbnNpdHksIFRyYW5zZm9ybWVkIERlbnNpdHksIFRyYW5zZm9ybWVkIFEtUSwgVHJhbnNmb3JtYXRpb24gRnVuY3Rpb24nKQpgYGA=