Intro

The purpose of this analysis is to find synergy biomarkers using the emba R package in the cascade boolean model datasets (Cascade is the name of the topology used). Particularly, we will investigate \(4\) synergies: AK-PD, PD-PI, PD-G2, PI-D1 and try to find causal mechanisms that might explain why and where (pathways) these synergies manifest.

Note that AK is an AKT inhibitor and PI is a PI3K inhibitor and they both target the PI3K/AKT/mTOR pathway. The combination of such an inhibitor with a MEK inhibitor - PD - that targets the MAPK/ERK pathway, has proven to be more effective than the single drug treatment during clinical trials with patients that had advanced colorectal carcinoma (source). The G2 drug is a PDPK1 inhibitor and the D1 drug is an inhibitor of RSK isoforms (RPS6KA1, RPS6KA3, RPS6KA2, RPS6KA6).

The boolean model datasets are in total \(9\): one for each cell line of interest (8 cell lines) where the models were fitted to a specific steady state in each case and one for the so-called random models which were generated randomly in the sense that were fitted only to a proliferation state (simulations were done using the DrugLogics software modules Gitsbe and Drabme).

Summary of results

The results will focus on the activity state biomarkers and not the link operator ones (since we didn’t know how to best interpret them - though an effort has been made in the AK-PD analysis).

Input

Loading libraries:

library(emba)
library(usefun)
library(ComplexHeatmap)
library(circlize)
library(dplyr)
library(tibble)
library(DT)
library(ggpubr)
library(Ckmeans.1d.dp)

We load the cell-specific input data:

# Cell Lines
cell.lines = c("A498", "AGS", "DU145", "colo205", "SW620", "SF295", "UACC62", "MDA-MB-468")

cell.line.dirs = sapply(cell.lines, function(cell.line) {
  paste0(getwd(), "/", cell.line)
})

# Model predictions
model.predictions.files = sapply(cell.line.dirs, function(cell.line.dir) {
  paste0(cell.line.dir, "/model_predictions")
})

model.predictions.per.cell.line = lapply(model.predictions.files, function(file) {   
  get_model_predictions(file) 
})

# Observed synergies
observed.synergies.files = sapply(cell.line.dirs, function(cell.line.dir) {
  paste0(cell.line.dir, "/observed_synergies")
})

observed.synergies.per.cell.line = lapply(observed.synergies.files, function(file) {
  get_observed_synergies(file)
})

# Models Stable State (1 per model)
models.stable.state.files = sapply(cell.line.dirs, function(cell.line.dir) {
  paste0(cell.line.dir, "/models_stable_state")
})

models.stable.state.per.cell.line = lapply(models.stable.state.files, function(file) { 
  as.matrix(read.table(file, check.names = FALSE))
})

# Models Link Operators
models.link.operator.files = sapply(cell.line.dirs, function(cell.line.dir) {
  paste0(cell.line.dir, "/models_link_operator")
})

models.link.operators.per.cell.line = lapply(models.link.operator.files, function(file) {
  as.matrix(read.table(file, check.names = FALSE))
})

The random model input data:

random.dir = paste0(getwd(), "/random")
random.model.predictions = get_model_predictions(paste0(random.dir, "/model_predictions"))

random.models.stable.state = as.matrix(
  read.table(file = paste0(random.dir, "/models_stable_state"), check.names = FALSE)
)

random.models.link.operator =
  as.matrix(read.table(file = paste0(random.dir, "/models_link_operator"), check.names = FALSE))

# the node names used in our analysis
node.names = colnames(random.models.stable.state)
# the tested drug combinations
drug.combos = colnames(random.model.predictions)

Synergy Biomarker Analysis

Using the generic function biomarker_synergy_analysis from the emba R package, we can find synergy biomarkers i.e. nodes whose activity and boolean equation parameterization (link operator) affect the manifestation of synergies in the boolean models. Models are classified based on whether they predict or not each of the predicted synergies found in each boolean dataset.

First we run the analysis on the cell-specific boolean model datasets (note that every input to the biomarker_synergy_analysis function changes per cell line):

cell.specific.synergy.analysis.res = list()

for (cell.line in cell.lines) {
  cell.specific.synergy.analysis.res[[cell.line]] =
    biomarker_synergy_analysis(model.predictions.per.cell.line[[cell.line]],
      models.stable.state.per.cell.line[[cell.line]], 
      models.link.operators.per.cell.line[[cell.line]],
      observed.synergies.per.cell.line[[cell.line]], threshold = 0.7)
}

Next we run the analysis on the random boolean model datasets (note that the only input to the biomarker_synergy_analysis function that changes is the observed synergies per cell line - the rest is the same data from the random model dataset):

# Synergy Biomarkers for cell proliferation models
random.synergy.analysis.res = list()

for (cell.line in cell.lines) {
  random.synergy.analysis.res[[cell.line]] =
    biomarker_synergy_analysis(random.model.predictions, 
      random.models.stable.state, random.models.link.operator, 
      observed.synergies.per.cell.line[[cell.line]], threshold = 0.7)
}

The results from all the previous code chunks have already been executed and the result data is saved in the cascade_synergy_biomarkers.RData file. We load all the objects:

#save.image(file = "cascade_synergy_biomarkers.RData")
load(file = "cascade_synergy_biomarkers.RData")

Observed synergies

Each of the cell lines studied has a different set of observed synergies (drug combinations that were found synergistic across all the 153 tested ones). In this section, we will visualize the cell lines’ observed synergies and mark the synergies that were also predicted by the cell-specific models and the random-generated ones (in one cell line at least). First, we get the biomarkers for these synergies from each cell line:

total.predicted.synergies.cell.specific =
  unique(unlist(sapply(cell.specific.synergy.analysis.res, function(x) { x$predicted.synergies})))
total.predicted.synergies.cell.specific.num = length(total.predicted.synergies.cell.specific)

The same for the random models:

total.predicted.synergies.random = 
  unique(unlist(sapply(random.synergy.analysis.res, function(x) { x$predicted.synergies})))
total.predicted.synergies.random.num = length(total.predicted.synergies.random)

Then, we get the observed synergies from each cell line in a data.frame:

observed.synergies.res = get_observed_synergies_per_cell_line(cell.line.dirs, drug.combos)

# remove drug combinations which are not observed in any of the cell lines
observed.synergies.res = prune_columns_from_df(observed.synergies.res, value = 0)

total.observed.synergies = colnames(observed.synergies.res)
total.observed.synergies.num = length(total.observed.synergies)

Lastly, we visualize the observed and predicted synergies for all cell lines in one heatmap:

# color the cell-specific predicted synergies
predicted.synergies.colors = rep("black", total.observed.synergies.num)
names(predicted.synergies.colors) = total.observed.synergies
common.predicted.synergies = intersect(total.predicted.synergies.cell.specific,
                                       total.predicted.synergies.random)
cell.specific.only.predicted.synergies = 
  total.predicted.synergies.cell.specific[!total.predicted.synergies.cell.specific %in% total.predicted.synergies.random]
random.only.predicted.synergies = 
  total.predicted.synergies.random[!total.predicted.synergies.random %in% total.predicted.synergies.cell.specific]

predicted.synergies.colors[total.observed.synergies %in% 
                           common.predicted.synergies] = "blue"
predicted.synergies.colors[total.observed.synergies %in% 
                           cell.specific.only.predicted.synergies] = "orange"
predicted.synergies.colors[total.observed.synergies %in% 
                           random.only.predicted.synergies] = "purple"

# define a coloring
obs.synergies.col.fun = colorRamp2(c(0, 1), c("red", "green"))

observed.synergies.heatmap = 
  Heatmap(matrix = as.matrix(observed.synergies.res), 
          col = obs.synergies.col.fun,
          column_title = "Observed synergies per cell line",
          column_title_gp = gpar(fontsize = 20),
          column_names_gp = gpar(col = predicted.synergies.colors),
          row_title = "Cell Lines", row_title_side = "left",
          row_dend_side = "right", row_names_side = "left",
          rect_gp = gpar(col = "black", lwd = 0.3),
          heatmap_legend_param = list(at = c(1, 0), labels = c("YES", "NO"), 
            color_bar = "discrete", title = "Observed", direction = "vertical"))

lgd = Legend(at = c("Cell-specific", "Random", "Both"), title = "Predicted", 
             legend_gp = gpar(fill = c("orange", "purple", "blue")))

draw(observed.synergies.heatmap,  heatmap_legend_list = list(lgd), 
     heatmap_legend_side = "right")

  • The cell-specific models predicted 27 of the 40 observed synergies found across the 8 cell lines, whereas the random models predicted 27 of the them. Thus, the total true positive coverage for all the models across all cell lines is 72.5%
  • Note that there exist synergies which were observed in all cell lines (AK-BI, PI-D1)
  • AK-G4 and 5Z-D1 are observed synergies that only the cell-specific models could predict, whereas the G2-P5 and PI-D4 are observed synergies that only the random models could predict. This shows us that a complimentary approach is needed when searching for biomarkers as the two different kind of models (trained to a specific activity state profile vs trained to proliferation) although they share common true positives regarding the synergies they predict, there are also synergies only a specific class of models could predict.
  • The \(3\) synergies of interest, AK-PD, PD-PI and PD-G2 were observed in the A498 cell line and predicted by both the cell-specific and random models.

AK-PD biomarkers

As observed above, the AK-PD synergy was predicted by both the cell specific and random models in the A498 cell line.

Cell-specific (A498)

We get the average state and link operator differences per network node for the A498 cell line from the cell-specific models:

AK.PD.avg.state.diff.cell.specific = cell.specific.synergy.analysis.res$A498$diff.state.synergies.mat["AK-PD", ]
AK.PD.avg.link.diff.cell.specific  = cell.specific.synergy.analysis.res$A498$diff.link.synergies.mat["AK-PD", ]

We build the network from the topology file:

topology.file = paste0(getwd(), "/topology")
net = construct_network(topology.file = topology.file, models.dir =  paste0(getwd(), "/AGS/models"))

# a static layout for plotting the same network always
coordinates.file = paste0(getwd(), "/network_xy_coordinates")
nice.layout = as.matrix(read.table(coordinates.file))

Activity state biomarkers

We will now visualize the nodes average state differences in a network graph. Note that the good models are those that predicted the AK-PD drug combination to be synergistic and were contrasted to those that predicted it to be antagonistic (bad models). The number of models in each category were:

model.predictions = model.predictions.per.cell.line[["A498"]]
models.stable.state = models.stable.state.per.cell.line[["A498"]]
drug.comb = "AK-PD"

good.models.num = sum(model.predictions[, drug.comb] == 1 & !is.na(model.predictions[, drug.comb]))
# unique good models
# models.link.operator = models.link.operators.per.cell.line$A498
# nrow(unique(models.link.operator[model.predictions[, drug.comb] == 1 & !is.na(model.predictions[, drug.comb]), ]))
bad.models.num  = sum(model.predictions[, drug.comb] == 0 & !is.na(model.predictions[, drug.comb]))

pretty_print_string(paste0("Number of 'good' models (AK-PD synergistic) in the A498 cell line: ", good.models.num))

Number of ‘good’ models (AK-PD synergistic) in the A498 cell line: 26

pretty_print_string(paste0("Number of 'bad' models (AK-PD antagonistic) in the A498 cell line: ", bad.models.num))

Number of ‘bad’ models (AK-PD antagonistic) in the A498 cell line: 2672

plot_avg_state_diff_graph(net, diff = AK.PD.avg.state.diff.cell.specific, 
  layout = nice.layout, title = "AK-PD activity state biomarkers (Cell specific models - A498)")

Thus, we can identify the active state biomarkers:

AK.PD.active.biomarkers = AK.PD.avg.state.diff.cell.specific[AK.PD.avg.state.diff.cell.specific > 0.7]
pretty_print_vector_names(AK.PD.active.biomarkers)

1 node: ERK_f

So, the AK-PD synergy manifests in cancer cell models that have the ERK_f family logical node in an active state. The MAPK-ERK signaling pathway has been studied a lot and has been found to be overexpressed/have increased activity in cancers and as such cancer treatments that include the inhibition of that pathway are found to be most beneficial.

Paper evidence for ERK overexpression in cancer (there are many):

The inhibited state biomarkers are:

AK.PD.inhibited.biomarkers = AK.PD.avg.state.diff.cell.specific[AK.PD.avg.state.diff.cell.specific < -0.7]
pretty_print_vector_names(AK.PD.inhibited.biomarkers)

2 nodes: PTPN11, GAB_f

We will demonstrate that the PTPN11 and GAB_f inhibited biomarkers are a direct consequence of the overexpression of ERK_f:


If we check the logical equations related to the above biomarkers we see that:

pretty_print_string("ERK_f *=  (  MEK_f ) AND/OR NOT  (  ( DUSP6 )  or PPP1CA )")

ERK_f *= ( MEK_f ) AND/OR NOT ( ( DUSP6 ) or PPP1CA )

pretty_print_string("GAB_f *=  (  GRB2 ) AND/OR NOT ( ERK_f )")

GAB_f *= ( GRB2 ) AND/OR NOT ( ERK_f )

pretty_print_string("PTPN11 *=  (  GAB_f )")

PTPN11 *= ( GAB_f )

So, pretty much if the GAB_f node is more inhibited in the models that predicted the AK-PD synergy (good models), then PTPN11 is also as well. Also the average activity difference of the GRB2 node is -0.3434189, which makes the GAB_f node more inhibited in the good models since it’s activity is mostly dependent on the ERK_f node, which is mostly overexpressed in the good models. All in all, the overexpression of ERK_f is what causes the two other inhibited biomarkers.


Synergy subsets analysis

It will be interesting to find all the possible synergy sets and subsets that include the AK-PD as the extra synergy. Using these synergy sets, models that predict a set of synergies will be contrasted to models that predicted the same set with the addition of the extra AK-PD synergy. Thus we could find synergy biomarkers that allow already good predicting models to predict the additional synergy of interest. This investigation will allow us thus to refine the activity state and link operator biomarkers we found above.

We first construct two matrices: in the first, each row is a set vs subset average activity difference vector of the network nodes, while on the second each row is a set vs subset average link operator difference vector of the network nodes:

model.predictions = model.predictions.per.cell.line$A498
models.stable.state = models.stable.state.per.cell.line$A498
models.link.operator = models.link.operators.per.cell.line$A498
  
res = get_synergy_comparison_sets(cell.specific.synergy.analysis.res$A498$synergy.subset.stats)
AK.PD.res = res %>% filter(synergies == "AK-PD")

diff.state.list = list()
diff.link.list = list()
for (i in 1:nrow(AK.PD.res)) {
  synergy.set    = AK.PD.res[i, 2]
  synergy.subset = AK.PD.res[i, 3]
  
  synergy.set.str    = unlist(strsplit(x = synergy.set, split = ","))
  synergy.subset.str = unlist(strsplit(x = synergy.subset, split = ","))
  
  # count models
  synergy.set.models.num = count_models_that_predict_synergies(synergy.set.str, model.predictions)
  synergy.subset.models.num = count_models_that_predict_synergies(synergy.subset.str, model.predictions)
  
  # if too small number of models, skip the diff vector
  if ((synergy.set.models.num <= 3) | (synergy.set.models.num <= 3)) 
    next
  
  # get the diff
  diff.state.ak.pd = get_avg_activity_diff_based_on_synergy_set_cmp(synergy.set.str, synergy.subset.str, model.predictions, models.stable.state)
  diff.link.ak.pd = get_avg_link_operator_diff_based_on_synergy_set_cmp(synergy.set.str, synergy.subset.str, model.predictions, models.link.operator)
  diff.state.list[[paste0(synergy.set, " vs ", synergy.subset)]] = diff.state.ak.pd
  diff.link.list[[paste0(synergy.set, " vs ", synergy.subset)]] = diff.link.ak.pd
}

diff.state.mat = do.call(rbind, diff.state.list)
diff.link.mat = do.call(rbind, diff.link.list)
caption.title.1 = "Table 1: Average activity difference values across all synergy set comparisons (AK-PD)"
datatable(data = diff.state.mat[, c("SRC", "CSK", "MEK_f", "STAT1", "PTPN6")], options = list(
    searching = FALSE, pageLength = 5, lengthMenu = c(5, 10)),
  caption = htmltools::tags$caption(caption.title.1, style="color:#dd4814; font-size: 18px")) %>% 
  formatRound(1:ncol(diff.state.mat), digits = 3)
caption.title.2 = "Table 2: Average link operator difference values across all synergy set comparisons (AK-PD)"
datatable(data = diff.link.mat[, c("SRC", "RAC_f", "MEK_f", "STAT1", "PTEN")], options = list(
    searching = FALSE, pageLength = 5, lengthMenu = c(5, 10)),
  caption = htmltools::tags$caption(caption.title.2, style="color:#dd4814; font-size: 18px")) %>% 
  formatRound(1:ncol(diff.link.mat), digits = 3)

Using the matrix from Table 1 (where we show just \(5\) nodes), we count per network node the number of times that the node’s average activity difference value has surpassed a specified threshold - i.e. the number of times it has been found as important (a biomarker) across all the synergy set comparisons (so the more the better):

threshold = 0.8
biomarker.state.mat = binarize_to_thres(mat = diff.state.mat, threshold)
biomarker.state.counts = colSums(biomarker.state.mat)
pretty_print_vector_names_and_values(table(biomarker.state.counts))

0: 141, 1: 1, 11: 2

The above means that there were \(141\) nodes that were zero times found as activity state biomarkers across all synergy set comparisons, one that was found once and \(2\) that were found \(11\) times (out of a total of 19). These nodes are:

pretty_print_vector_names(biomarker.state.counts[biomarker.state.counts == 11])

2 nodes: SRC, PTPN6

If we visualize the average activity state difference across all synergy comparison sets from Table 1 we can see that the previous \(2\) nodes are more pronounced:

plot_avg_state_diff_graph(net, diff = colMeans(diff.state.mat), layout = nice.layout, 
  title = "Average activity state diff across all synergy subsets (AK-PD)")

We also visualize the median activity difference per node from Table 1 using a dotchart, where we have excluded the nodes that have an absolute median activity difference of \(0.05\) or less:

df = as.data.frame(apply(diff.state.mat, 2, median))
colnames(df) = "median.state.diff"
df = df %>% 
  rownames_to_column(var = "nodes") %>%
  filter(median.state.diff > 0.05 | median.state.diff < -0.05)

ggdotchart(df, x = "nodes", y = "median.state.diff",
  title = "Median activity state difference across all synergy comparison sets (AK-PD, A498 cell line)", 
  label = "nodes", font.label = list(size = 11, color = "blue"),
  label.select = list(criteria = "`y` >= 0.7 | `y` <= -0.7"), 
  repel = TRUE, label.rectangle = TRUE,
  xlab = "Nodes", ylab = "Median Activity State", 
  ylim = c(-1,1), add = "segments") + 
    font("x.text", size = 10) +
    font("title", size = 11) + 
    geom_hline(yintercept = -0.7, linetype = "dashed", color = "red") + 
    geom_hline(aes(yintercept = 0.7, linetype = "0.7"), color = "red") +
    scale_linetype_manual(name = "Threshold", values = 2, 
      guide = guide_legend(override.aes = list(color = "red"))) + 
    theme(legend.position = c(0.2,0.7))

Note the boolean equation: PTPN6 *= SRC. This means that SRC is the only inhibited node of interest


We will now check if the above observations regarding the activity of the nodes are true using the MCLP dataset as a reference:

mclp.data = read.table(file = "MCLP-v1.1-Level4.tsv", header = TRUE, stringsAsFactors = FALSE)
cell.lines.in.mclp = c("A498", "AGS", "DU145", "COLO205", "SW620", "SF295", "UACC62", "MDAMB468")

We check the phosphorylation value of the SRC_pY527 across all cell lines:

src.data = mclp.data %>% select(Sample_Name, SRC_pY527) %>% na.omit()

# find the activity classes (3 classes: low activity, no activity, high activity)
res = Ckmeans.1d.dp(x = src.data$SRC, k = 3)
activity = as.factor(res$cluster)
levels(activity) = c("low", "medium", "high")
src.data = cbind(src.data, activity)

ggdotchart(data = src.data, x = "Sample_Name", y = "SRC_pY527", 
  title = "SRC_pY527 signaling across all cell lines in MCLP Dataset",
  color = "activity", palette = c("red", "grey", "green"), 
  label = "Sample_Name", label.select = cell.lines.in.mclp, repel = TRUE,
  add = "segments", label.rectangle = TRUE,
  xlab = "Cell Lines", ylab = "SRC_pY527 Signaling",
  add.params = list(color = "activity", palette = c("red", "grey", "green"))) + 
    theme(axis.text.x = element_blank(), axis.ticks = element_blank())

It has been shown in various studies (e.g. in this paper) that the phosphorylation sites of SRC include the inhibiting phosphotyrosine 527 site, which overrides the Y416 phosphorylation (which is also tested in the MCLP dataset). Since for A498 cell line we observe one of the largest signaling measurements compared to all the cell lines for the SRC_pY527 site, this means that SRC should be in an inhibiting state, which is exactly what we found from our analysis above.


We also check for the phosphorylation value of the MAPK_pT202Y204 (for the ERK_f activation) across all cell lines:

erk.data = mclp.data %>% select(Sample_Name, MAPK_pT202Y204) %>% na.omit()

# find the activity classes (3 classes: low expression, no expression, high expression)
res = Ckmeans.1d.dp(x = erk.data$MAPK_pT202Y204, k = 3)
activity = as.factor(res$cluster)
levels(activity) = c("low", "medium", "high")
erk.data = cbind(erk.data, activity)

ggdotchart(data = erk.data, x = "Sample_Name", y = "MAPK_pT202Y204", 
  title = "MAPK_pT202Y204 signaling across all cell lines in MCLP Dataset",
  color = "activity", palette = c("red", "grey", "green"), 
  label = "Sample_Name", label.select = cell.lines.in.mclp, repel = TRUE, xlab = "Cell Lines",
  add = "segments", label.rectangle = TRUE, ylab = "MAPK_pT202Y204 Signaling",
  add.params = list(color = "activity", palette = c("red", "grey", "green"))) + 
    theme(axis.text.x = element_blank(), axis.ticks = element_blank())

One of the activating phosphorylation sites of ERK is the ERK1 T202/Y204. From the above figure we see a weak signaling of this phoshosite for the A498 cell line which does not fully correlate well with the results from our analysis above (ERK_f overexpression). Though, in a later section we show that ERK_f is found active in all models from all cell lines that predict the AK-PD synergy (for example even in UACC62 cell line which has a high phosphorylation signal in the above figure).


Using the matrix from Table 2, we count per network node the number of times that the node’s average link operator difference value has surpassed a specified threshold - i.e. the number of times it has been found as important (a biomarker) across all the synergy set comparisons (so the more the better):

biomarker.link.mat = binarize_to_thres(mat = diff.link.mat, thres = 0.8)

biomarker.link.counts = colSums(biomarker.link.mat)
pretty_print_vector_names_and_values(table(biomarker.link.counts))

0: 44, 1: 1, 2: 1, 6: 5, 11: 1

The above means that there were \(44\) nodes that were zero times found as link operator biomarkers across all synergy set comparisons, one that was found once, one that was found twice, \(5\) that were found 6 times and \(1\) that was found 11 times. The last two categories of nodes are:

pretty_print_vector_names(biomarker.link.counts[biomarker.link.counts == 6])

5 nodes: TGFBR2, mTORC1_c, TSC_f, CDC42, RHOA

pretty_print_vector_names(biomarker.link.counts[biomarker.link.counts == 11])

1 node: SRC

If we visualize the average link operator difference across all of the synergy comparison sets from Table 2, we can see the nodes mentioned above:

plot_avg_link_operator_diff_graph(net, diff = colMeans(diff.link.mat), layout = nice.layout, 
  title = "Average link operator diff across all synergy subsets (AK-PD)")

Some notes/observations on the structure of the boolean equations of the nodes found above:


The boolean equation of the SRC node is: SRC *= ( RTPK_f ) AND/OR NOT ( CSK ) and it’s mean link operator difference value across all synergy set comparisons is -0.5721936 which means that on average the logic operator that binds its two regulators is the AND NOT and thus it’s more difficult to have it as activated (1 case out of 4 in boolean logic). This correlates with the fact that it was found as mostly inhibited in the analysis above. Also note the equations:

  • CSK *= ( PRKACA )
  • PRKACA *= ( ( NFKB_f ) or FOS )
  • FOS *= ( ( ( ERK_f ) or RSK_f ) or SRF )

So, when ERK_f is overexpressed, CSK becomes active, which means that the prevalence of the AND NOT link operator makes the activity of the SRC node dependent only on it’s inhibitor CSK (since it’s active) and not on the activity of RTPK_f node.

  • mTORC1_c *= ( ( RHEB ) or RSK_f ) AND/OR NOT ( AKT1S1 )
  • AKT1S1 *= not ( AKT_f )

mTORC1_c’s mean link operator difference value across all synergy set comparisons is 0.699427 which means that on average the logic operator that binds its two regulators is the OR NOT. This gives the node the structural flexibility to become active when the AK drug is used: AK inhibits AKT_f node, making thus AKT1S1 active, which in turn makes the mTORC1_c equation like this: mTORC1_c *= ( ( RHEB ) or RSK_f ) AND/OR 0. So, an AND link operator would result always in an inhibited mTORC1_c whereas an OR link operator would give the possibility for the mTORC1_c to be active in case one of its activators are active.

We conclude that cancer models in which the AK-PD drug combination is synergistic, tend to also have the nodes SRC and PTPN6 in an inhibited state and the SRC node depends on both it’s regulators, RTPK_f and CSK. The link operators of the mTORC1_c and TSC_f equations also seem to have an important role in the manifestation of this synergy.


Random

We get the average state and link operator differences per network node for the A498 cell line from the random models:

AK.PD.avg.state.diff.random = random.synergy.analysis.res$A498$diff.state.synergies.mat["AK-PD", ]
AK.PD.avg.link.diff.random  = random.synergy.analysis.res$A498$diff.link.synergies.mat["AK-PD", ]

Activity state biomarkers

We will now visualize the nodes average state differences in a network graph. Note that the good models are those that predicted the AK-PD drug combination to be synergistic and were contrasted to those that predicted it to be antagonistic (bad models). The number of models in each category were:

drug.comb = "AK-PD"

good.models.num = sum(random.model.predictions[, drug.comb] == 1 & !is.na(random.model.predictions[, drug.comb]))
# unique good models
# nrow(unique(random.models.link.operator[random.model.predictions[, drug.comb] == 1 & !is.na(random.model.predictions[, drug.comb]),]))
bad.models.num  = sum(random.model.predictions[, drug.comb] == 0 & !is.na(random.model.predictions[, drug.comb]))

pretty_print_string(paste0("Number of 'good' random models (AK-PD synergistic) in the A498 cell line: ", good.models.num))

Number of ‘good’ random models (AK-PD synergistic) in the A498 cell line: 107

pretty_print_string(paste0("Number of 'bad' random models (AK-PD antagonistic) in the A498 cell line: ", bad.models.num))

Number of ‘bad’ random models (AK-PD antagonistic) in the A498 cell line: 3632

plot_avg_state_diff_graph(net, diff = AK.PD.avg.state.diff.random, 
  layout = nice.layout, title = "AK-PD activity state biomarkers (Random models - A498)")

Thus, we can identify the active state biomarkers (note that no inhibited biomarkers at the specified threshold difference level were found):

AK.PD.active.biomarkers = AK.PD.avg.state.diff.random[AK.PD.avg.state.diff.random > 0.8]
pretty_print_vector_names(AK.PD.active.biomarkers)

1 node: ERK_f

The random models that predicted the AK-PD synergy also show an overexpression of the ERK_f node.


Synergy subsets analysis

We perform the same kind of analysis as with the cell-specific models: models that predict a set of synergies will be contrasted to models that predicted the same set with the addition of the extra AK-PD synergy, allowing us thus to refine the activity state and link operator biomarkers we found above from the random models.

We first construct two matrices: in the first, each row is a set vs subset average activity difference vector of the network nodes, while on the second each row is a set vs subset average link operator difference vector of the network nodes:

res = get_synergy_comparison_sets(random.synergy.analysis.res$A498$synergy.subset.stats)
AK.PD.res = res %>% filter(synergies == "AK-PD")

diff.state.list.random = list()
diff.link.list.random = list()
for (i in 1:nrow(AK.PD.res)) {
  synergy.set    = AK.PD.res[i, 2]
  synergy.subset = AK.PD.res[i, 3]
  
  synergy.set.str    = unlist(strsplit(x = synergy.set, split = ","))
  synergy.subset.str = unlist(strsplit(x = synergy.subset, split = ","))
  
  # count models
  synergy.set.models.num = count_models_that_predict_synergies(synergy.set.str, random.model.predictions)
  synergy.subset.models.num = count_models_that_predict_synergies(synergy.subset.str, random.model.predictions)
  # print(paste0(synergy.set.models.num, " ", synergy.subset.models.num))
  
  # if too small number of models, skip the diff vector
  if ((synergy.set.models.num <= 3) | (synergy.set.models.num <= 3)) 
    next
  
  # get the diff
  diff.state.ak.pd = get_avg_activity_diff_based_on_synergy_set_cmp(synergy.set.str, synergy.subset.str, random.model.predictions, random.models.stable.state)
  diff.link.ak.pd = get_avg_link_operator_diff_based_on_synergy_set_cmp(synergy.set.str, synergy.subset.str, random.model.predictions, random.models.link.operator)
  
  diff.state.list.random[[paste0(synergy.set, " vs ", synergy.subset)]] = diff.state.ak.pd
  diff.link.list.random[[paste0(synergy.set, " vs ", synergy.subset)]] = diff.link.ak.pd
}

diff.state.mat.random = do.call(rbind, diff.state.list.random)
diff.link.mat.random = do.call(rbind, diff.link.list.random)
caption.title.3 = "Table 3: Average activity difference values across all synergy set comparisons (AK-PD)"
datatable(data = diff.state.mat.random[, c("SRC", "CSK", "MEK_f", "STAT1", "PTPN6")], options = list(
    searching = FALSE, pageLength = 5, lengthMenu = c(5, 10)),
    caption = htmltools::tags$caption(caption.title.3, style="color:#dd4814; font-size: 18px")) %>% 
      formatRound(1:ncol(diff.state.mat.random), digits = 3)
caption.title.4 = "Table 4: Average link operator difference values across all synergy set comparisons (AK-PD)"
datatable(data = diff.link.mat.random[, c("SRC", "RAC_f", "MEK_f", "STAT1", "PTEN")], options = list(
    searching = FALSE, pageLength = 5, lengthMenu = c(5, 10)),
    caption = htmltools::tags$caption(caption.title.4, style="color:#dd4814; font-size: 18px")) %>% 
      formatRound(1:ncol(diff.link.mat.random), digits = 3)

Using the matrix from Table 3 (where we show just \(5\) nodes), we count per network node the number of times that the node’s average activity difference value has surpassed a specified threshold - i.e. the number of times it has been found as important (a biomarker) across all the synergy set comparisons (so the more the better):

biomarker.state.mat.random = binarize_to_thres(mat = diff.state.mat.random, thres = 0.7)

biomarker.state.counts.random = colSums(biomarker.state.mat.random)
pretty_print_vector_names_and_values(table(biomarker.state.counts.random))

0: 139, 1: 1, 2: 1, 4: 1, 6: 2

So, there are \(2\) nodes that were found as activity state biomarkers \(6\) times across all synergy set comparisons. These nodes are:

pretty_print_vector_names(biomarker.state.counts.random[biomarker.state.counts.random == 6])

2 nodes: PDPK1, PRKCD

But the total number of comparisons was 49 so we could argue that this is not a statistically significant result, which can be seen clearly in the next graph where we visualize the average activity state difference across all synergy comparison sets from Table 3:

plot_avg_state_diff_graph(net, diff = colMeans(diff.state.mat.random), layout = nice.layout, 
  title = "Average activity state diff across all synergy subsets (AK-PD)")

Using the matrix from Table 4, we count per network node the number of times that the node’s average link operator difference value has surpassed a specified threshold - i.e. the number of times it has been found as important (a biomarker) across all the synergy set comparisons (so the more the better):

biomarker.link.mat.random = binarize_to_thres(mat = diff.link.mat.random, thres = 0.7)

biomarker.link.counts.random = colSums(biomarker.link.mat.random)
pretty_print_vector_names_and_values(table(biomarker.link.counts.random))

0: 50, 1: 1, 7: 1

So, there was a node that was found \(7\) times (out of a total of 49, so not statistically significant) as a link operator biomarkers across all synergy set comparisons:

pretty_print_vector_names(biomarker.link.counts.random[biomarker.link.counts.random == 7])

1 node: mTORC1_c

We visualize the average link operator difference across all synergy comparison sets from Table 4:

plot_avg_link_operator_diff_graph(net, diff = colMeans(diff.link.mat.random), layout = nice.layout, 
  title = "Average link operator diff across all synergy subsets (AK-PD)")

AK-PD in other cell lines

Though the AK-PD synergy was observed and predicted in the A498 model dataset, we investigate its biomarkers in the other cell lines where it was predicted as a False Positive (FP) synergy (predicted by the models but not observed in the experiments). Thus, we can still see if there are any common (activity state and link operator) biomarkers for AK-PD across the all the cell-specific models by contrasting in each cell line the models that predicted AK-PD (if there were any) vs the models that did not:

drug.comb = "AK-PD"

ak.pd.diff.state.list = list()
ak.pd.diff.link.list = list()

for (cell.line in cell.lines) {
  if (cell.line == "A498") 
    next
  model.predictions = model.predictions.per.cell.line[[cell.line]]
  
  ak.pd.diff.state.list[[cell.line]] = get_avg_activity_diff_based_on_specific_synergy_prediction(
    model.predictions, models.stable.state = models.stable.state.per.cell.line[[cell.line]], drug.comb)
  ak.pd.diff.link.list[[cell.line]] = get_avg_link_operator_diff_based_on_specific_synergy_prediction(
    model.predictions, models.link.operator = models.link.operators.per.cell.line[[cell.line]], drug.comb)
  
  # See the #models that predicted vs #models those that did not
  # print(paste0(sum(model.predictions[,drug.comb], na.rm = T), " vs ", sum(!model.predictions[,drug.comb], na.rm = T)))
}

ak.pd.diff.state.mat = do.call(rbind, ak.pd.diff.state.list)
ak.pd.diff.link.mat = do.call(rbind, ak.pd.diff.link.list)

ERK_f was the one node that was found as an activity state & link operator AK-PD biomarker in all cell lines

ak.pd.biomarker.state.mat = binarize_to_thres(mat = ak.pd.diff.state.mat, thres = 0.7)
ak.pd.biomarker.link.mat  = binarize_to_thres(mat = ak.pd.diff.link.mat, thres = 0.7)

ak.pd.biomarker.state.mat.counts = colSums(ak.pd.biomarker.state.mat)
ak.pd.biomarker.link.mat.counts = colSums(ak.pd.biomarker.link.mat)

#pretty_print_vector_names_and_values(table(ak.pd.biomarker.state.mat.counts))
#pretty_print_vector_names_and_values(table(ak.pd.biomarker.link.mat.counts))

pretty_print_vector_names(ak.pd.biomarker.state.mat.counts[ak.pd.biomarker.state.mat.counts == 7])

1 node: ERK_f

pretty_print_vector_names(ak.pd.biomarker.link.mat.counts[ak.pd.biomarker.link.mat.counts == 6])

1 node: ERK_f

PD-PI activity biomarkers

As observed in the heatmap above, the PD-PI synergy was predicted by both the cell specific and random models in the A498 cell line.

Cell-specific (A498)

PD.PI.avg.state.diff.cell.specific = cell.specific.synergy.analysis.res$A498$diff.state.synergies.mat["PD-PI",]
#PD.PI.avg.link.diff.cell.specific  = cell.specific.synergy.analysis.res$A498$diff.link.synergies.mat["PD-PI", ]

We will now visualize the nodes average state differences in a network graph. Note that the good models are those that predicted the PD-PI drug combination to be synergistic and were contrasted to those that predicted it to be antagonistic (bad models). The number of models in each category were:

model.predictions = model.predictions.per.cell.line[["A498"]]
models.stable.state = models.stable.state.per.cell.line[["A498"]]
drug.comb = "PD-PI"

good.models.num = sum(model.predictions[, drug.comb] == 1 & !is.na(model.predictions[, drug.comb]))
# unique good models
# models.link.operator = models.link.operators.per.cell.line$A498
# nrow(unique(models.link.operator[model.predictions[, drug.comb] == 1 & !is.na(model.predictions[, drug.comb]), ]))
bad.models.num  = sum(model.predictions[, drug.comb] == 0 & !is.na(model.predictions[, drug.comb]))

pretty_print_string(paste0("Number of 'good' models (PD-PI synergistic) in the A498 cell line: ", good.models.num))

Number of ‘good’ models (PD-PI synergistic) in the A498 cell line: 107

pretty_print_string(paste0("Number of 'bad' models (PD-PI antagonistic) in the A498 cell line: ", bad.models.num))

Number of ‘bad’ models (PD-PI antagonistic) in the A498 cell line: 6873

plot_avg_state_diff_graph(net, diff = PD.PI.avg.state.diff.cell.specific, 
  layout = nice.layout, title = "PD-PI activity state biomarkers (Cell specific models - A498)")

We now visualize the nodes average state differences in a dotchart where we can easily identify the active and inhibited state biomarkers with the defined thresholds (we have excluded nodes whose absolute average difference value was less than \(0.05\)):

df = as.data.frame(PD.PI.avg.state.diff.cell.specific)
colnames(df) = "avg.state.diff"
df = df %>% 
  rownames_to_column(var = "nodes") %>%
  filter(avg.state.diff > 0.05 | avg.state.diff < -0.05)

ggdotchart(df, x = "nodes", y = "avg.state.diff",
  title = "Average activity state difference (Good - bad) - A498 cell line", 
  label = "nodes", font.label = list(size = 11, color = "blue"),
  label.select = list(criteria = "`y` >= 0.7 | `y` <= -0.5"), 
  repel = TRUE, label.rectangle = TRUE,
  xlab = "Nodes", ylab = "Average Activity State", 
  ylim = c(-1,1), add = "segments") + 
    font("x.text", size = 8) +
    font("title", size = 14) + 
    geom_hline(yintercept = -0.7, linetype = "dashed", color = "red") + 
    geom_hline(aes(yintercept = 0.7, linetype = "0.7"), color = "red") +
    scale_linetype_manual(name = "Threshold", values = 2, 
      guide = guide_legend(override.aes = list(color = "red"))) + 
    theme(legend.position = c(0.2,0.7)) + 
    annotate("text", x = c(18, 35), y = -0.9, label = c("Good models: PD-PI synergy,", "Bad Models: PD-PI antagonism"))

Note the boolean equation: PPM1A *= ( PTEN ). This means that PTEN is the only inhibited node of interest


We now check the PTEN in the MCLP dataset:

pten.data = mclp.data %>% select(Sample_Name, PTEN) %>% na.omit()

# find the activity classes (3 classes: low activity, no activity, high activity)
res = Ckmeans.1d.dp(x = pten.data$PTEN, k = 3)
activity = as.factor(res$cluster)
levels(activity) = c("low", "medium", "high")
pten.data = cbind(pten.data, activity)

ggdotchart(data = pten.data, x = "Sample_Name", y = "PTEN", 
  title = "PTEN signaling across all cell lines in MCLP Dataset",
  color = "activity", palette = c("red", "grey", "green"), 
  label = "Sample_Name", label.select = cell.lines.in.mclp, repel = TRUE,
  add = "segments", label.rectangle = TRUE,
  xlab = "Cell Lines", ylab = "PTEN Signaling",
  add.params = list(color = "activity", palette = c("red", "grey", "green"))) + 
    theme(axis.text.x = element_blank(), axis.ticks = element_blank())

The PTEN node is found highly expressed in the A498 cell line in the MCLP dataset, but for the models to predict the PD-PI synergy we found that the node needs to be in an inhibited state (see dotchart here). This correlates though with the fact that PTEN is found inhibited in many cancer types (article). The overexpression of the ERK_f node is also a result from this analysis.


Synergy Subsets Analysis

It will be interesting to find all the possible synergy sets and subsets that include the PD-PI as the extra synergy. Using these synergy sets, models that predict a set of synergies will be contrasted to models that predicted the same set with the addition of the extra PD-PI synergy. Thus we could find synergy biomarkers that allow already good predicting models to predict the additional synergy of interest. This investigation will allow us thus to refine the activity state biomarkers we found above.

We first construct a matrix where each row is a set vs subset average activity difference vector of the network nodes:
model.predictions = model.predictions.per.cell.line$A498
models.stable.state = models.stable.state.per.cell.line$A498
models.link.operator = models.link.operators.per.cell.line$A498

res = get_synergy_comparison_sets(cell.specific.synergy.analysis.res$A498$synergy.subset.stats)
PD.PI.res = res %>% filter(synergies == "PD-PI")

diff.state.list = list()

for (i in 1:nrow(PD.PI.res)) {
  synergy.set    = PD.PI.res[i, 2]
  synergy.subset = PD.PI.res[i, 3]
  
  synergy.set.str    = unlist(strsplit(x = synergy.set, split = ","))
  synergy.subset.str = unlist(strsplit(x = synergy.subset, split = ","))
  
  # count models
  synergy.set.models.num = count_models_that_predict_synergies(synergy.set.str, model.predictions)
  synergy.subset.models.num = count_models_that_predict_synergies(synergy.subset.str, model.predictions)
  
  # if too small number of models, skip the diff vector
  # if ((synergy.set.models.num <= 3) | (synergy.set.models.num <= 3)) 
  #   next
  
  # get the diff
  diff.state.PD.PI = get_avg_activity_diff_based_on_synergy_set_cmp(synergy.set.str, synergy.subset.str, model.predictions, models.stable.state)
  diff.state.list[[paste0(synergy.set, " vs ", synergy.subset)]] = diff.state.PD.PI
}

diff.state.mat = do.call(rbind, diff.state.list)

caption.title.5 = "Table 5: Average activity difference values across all synergy set comparisons (PD-PI)"
datatable(data = diff.state.mat[, c("SRC", "CSK", "MEK_f", "STAT1", "PTPN6")], options = list(
    searching = FALSE, pageLength = 5, lengthMenu = c(5, 10)),
  caption = htmltools::tags$caption(caption.title.5, style="color:#dd4814; font-size: 18px")) %>% 
  formatRound(1:ncol(diff.state.mat), digits = 3)

If we visualize the average activity state difference across all synergy comparison sets from Table 5 in a network graph, we see that there are no nodes with significant average state difference:

plot_avg_state_diff_graph(net, diff = colMeans(diff.state.mat), layout = nice.layout, 
  title = "Average activity state diff across all synergy subsets (PD-PI)")

We also visualize the median activity difference per node from Table 5 using a dotchart, where we have excluded the nodes that have an absolute median activity difference of \(0.05\) or less:

df = as.data.frame(apply(diff.state.mat, 2, median))
colnames(df) = "median.state.diff"
df = df %>% 
  rownames_to_column(var = "nodes") %>%
  filter(median.state.diff > 0.05 | median.state.diff < -0.05)

ggdotchart(df, x = "nodes", y = "median.state.diff",
  title = "Median activity state difference across all synergy comparison sets (PD-PI, A498 cell line)", 
  label = "nodes", font.label = list(size = 11, color = "blue"),
  label.select = list(criteria = "`y` >= 0.7 | `y` <= -0.7"), 
  repel = TRUE, label.rectangle = TRUE,
  xlab = "Nodes", ylab = "Median Activity State", 
  ylim = c(-1,1), add = "segments") + 
    font("x.text", size = 10) +
    font("title", size = 11) + 
    geom_hline(yintercept = -0.7, linetype = "dashed", color = "red") + 
    geom_hline(aes(yintercept = 0.7, linetype = "0.7"), color = "red") +
    scale_linetype_manual(name = "Threshold", values = 2, 
      guide = guide_legend(override.aes = list(color = "red"))) + 
    theme(legend.position = c(0.2,0.7))

So no significant nodes whose activity plays important role in the manifestation of the PD-PI synergy were found with the synergy subset analysis method.


Random

We get the average state differences per network node for the A498 cell line from the random models:

PD.PI.avg.state.diff.random = random.synergy.analysis.res$A498$diff.state.synergies.mat["PD-PI",]

We will now visualize the nodes average state differences in a network graph. Note that the good models are those that predicted the PD-PI drug combination to be synergistic and were contrasted to those that predicted it to be antagonistic (bad models). The number of models in each category were:

drug.comb = "PD-PI"

good.models.num = sum(random.model.predictions[, drug.comb] == 1 & !is.na(random.model.predictions[, drug.comb]))
# unique good models
# nrow(unique(random.models.link.operator[random.model.predictions[, drug.comb] == 1 & !is.na(random.model.predictions[, drug.comb]),]))
bad.models.num  = sum(random.model.predictions[, drug.comb] == 0 & !is.na(random.model.predictions[, drug.comb]))

pretty_print_string(paste0("Number of 'good' random models (PD-PI synergistic) in the A498 cell line: ", good.models.num))

Number of ‘good’ random models (PD-PI synergistic) in the A498 cell line: 719

pretty_print_string(paste0("Number of 'bad' random models (PD-PI antagonistic) in the A498 cell line: ", bad.models.num))

Number of ‘bad’ random models (PD-PI antagonistic) in the A498 cell line: 6066

plot_avg_state_diff_graph(net, diff = PD.PI.avg.state.diff.random, 
  layout = nice.layout, title = "PD-PI activity state biomarkers (Random models - A498)")

We now visualize the nodes average state differences in a dotchart where we can easily identify the active and inhibited state biomarkers with the defined thresholds (we have excluded nodes whose absolute average difference value was less than \(0.05\)):

df = as.data.frame(PD.PI.avg.state.diff.random)
colnames(df) = "avg.state.diff"
df = df %>% 
  rownames_to_column(var = "nodes") %>%
  filter(avg.state.diff > 0.05 | avg.state.diff < -0.05)

ggdotchart(df, x = "nodes", y = "avg.state.diff",
  title = "Average activity state difference for Random Models (Good - bad) - A498 cell line", 
  label = "nodes", font.label = list(size = 10, color = "blue"),
  label.select = list(criteria = "`y` >= 0.7 | `y` <= -0.5"), 
  repel = TRUE, label.rectangle = TRUE,
  xlab = "Nodes", ylab = "Average Activity State", 
  ylim = c(-1,1), add = "segments") + 
    font("x.text", size = 8) +
    font("title", size = 14) + 
    geom_hline(yintercept = -0.7, linetype = "dashed", color = "red") + 
    geom_hline(aes(yintercept = 0.7, linetype = "0.7"), color = "red") +
    scale_linetype_manual(name = "Threshold", values = 2, 
      guide = guide_legend(override.aes = list(color = "red"))) + 
    theme(legend.position = c(0.2,0.7)) + 
    annotate("text", x = c(8, 17), y = -0.9, label = c("Good models: PD-PI synergy,", "Bad Models: PD-PI antagonism"))

The overexpression of ERK_f is also a characteristic of the random models that predict the PD-PI drug combination to be synergistic.


Synergy subsets analysis

We perform the same kind of analysis as with the cell-specific models: models that predict a set of synergies will be contrasted to models that predicted the same set with the addition of the extra PD-PI synergy, allowing us thus to refine the activity state biomarkers we found above from the random models.

We first construct a matrix where each row is a set vs subset average activity difference vector of the network nodes:
res = get_synergy_comparison_sets(random.synergy.analysis.res$A498$synergy.subset.stats)
PD.PI.res = res %>% filter(synergies == "PD-PI")

diff.state.list.random = list()
for (i in 1:nrow(PD.PI.res)) {
  synergy.set    = PD.PI.res[i, 2]
  synergy.subset = PD.PI.res[i, 3]
  
  synergy.set.str    = unlist(strsplit(x = synergy.set, split = ","))
  synergy.subset.str = unlist(strsplit(x = synergy.subset, split = ","))
  
  # count models
  synergy.set.models.num = count_models_that_predict_synergies(synergy.set.str, random.model.predictions)
  synergy.subset.models.num = count_models_that_predict_synergies(synergy.subset.str, random.model.predictions)
  # print(paste0(synergy.set.models.num, " ", synergy.subset.models.num))
  
  # if too small number of drug combinations in the subset, skip the diff vector
  #if (length(synergy.subset.str) <= 3) next
  # if too small number of models compared
  #if ((synergy.set.models.num <= 3) | (synergy.set.models.num <= 3)) next
  
  # get the diff
  diff.state.PD.PI = get_avg_activity_diff_based_on_synergy_set_cmp(synergy.set.str, synergy.subset.str, random.model.predictions, random.models.stable.state)
  
  diff.state.list.random[[paste0(synergy.set, " vs ", synergy.subset)]] = diff.state.PD.PI
}

diff.state.mat.random = do.call(rbind, diff.state.list.random)

caption.title.6 = "Table 6: Average link operator difference values across all synergy set comparisons (PD-PI)"
datatable(data = diff.state.mat.random[, c("SRC", "RAC_f", "MEK_f", "STAT1", "PTEN")], options = list(
    searching = FALSE, pageLength = 5, lengthMenu = c(5, 10)),
    caption = htmltools::tags$caption(caption.title.6, style="color:#dd4814; font-size: 18px")) %>% 
      formatRound(1:ncol(diff.state.mat.random), digits = 3)

If we visualize the average activity state difference across all synergy comparison sets from Table 6 we see that there are no nodes with significant average state difference:

plot_avg_state_diff_graph(net, diff = colMeans(diff.state.mat.random), layout = nice.layout, 
  title = "Average activity state diff across all synergy subsets (PD-PI)")

We also visualize the median activity difference per node from Table 6 using a dotchart, where we have excluded the nodes that have an absolute median activity difference of \(0.05\) or less:

df = as.data.frame(apply(diff.state.mat.random, 2, median))
colnames(df) = "median.state.diff"
df = df %>% 
  rownames_to_column(var = "nodes") %>%
  filter(median.state.diff > 0.05 | median.state.diff < -0.05)

ggdotchart(df, x = "nodes", y = "median.state.diff",
  title = "Median activity state difference across all synergy comparison sets (PD-PI, Random Models, A498)", 
  label = "nodes", font.label = list(size = 11, color = "blue"),
  label.select = list(criteria = "`y` >= 0.7 | `y` <= -0.7"), 
  repel = TRUE, label.rectangle = TRUE,
  xlab = "Nodes", ylab = "Median Activity State", 
  ylim = c(-1,1), add = "segments") + 
    font("x.text", size = 10) +
    font("title", size = 11) + 
    geom_hline(yintercept = -0.7, linetype = "dashed", color = "red") + 
    geom_hline(aes(yintercept = 0.7, linetype = "0.7"), color = "red") +
    scale_linetype_manual(name = "Threshold", values = 2, 
      guide = guide_legend(override.aes = list(color = "red"))) + 
    theme(legend.position = c(0.2,0.7))

So no significant nodes whose activity plays important role in the manifestation of the PD-PI synergy were found with the synergy subset analysis method for the random models.


PD-PI in other cell lines

Though the PD-PI synergy was observed and predicted in the A498 model dataset, we investigate its biomarkers in the other cell lines where it was predicted as a False Positive (FP) synergy (predicted by the models but not observed in the experiments). Thus, we can still see if there are any common (activity state and link operator) biomarkers for this synergy across the all the cell-specific models by contrasting in each cell line the models that predicted PD-PI (if there were any) vs the models that did not:

drug.comb = "PD-PI"

diff.state.list = list()

for (cell.line in cell.lines) {
  if (cell.line == "A498") next
  
  model.predictions = model.predictions.per.cell.line[[cell.line]]
  models.stable.state = models.stable.state.per.cell.line[[cell.line]]
  
  models.predicted.num = sum(model.predictions[,drug.comb], na.rm = T)
  models.not.predicted.num = sum(!model.predictions[,drug.comb], na.rm = T)
  
  # See the #models that predicted vs #models those that did not
  # print(paste0(models.predicted.num, " vs ", models.not.predicted.num))
  
  if (models.predicted.num != 0 & models.not.predicted.num != 0)
    diff.state.list[[cell.line]] = get_avg_activity_diff_based_on_specific_synergy_prediction(model.predictions, models.stable.state, drug.comb)
}

# combine the data to a single matrix
diff.state.mat = do.call(rbind, diff.state.list)

Now, we identify the biomarkers of interest as those nodes that have surpassed a user-defined threshold (\(0.6\)) across as many as possible cell lines - counting thus the frequency that this has occured. We show those nodes that surpassed the threshold in at least half of the cell lines:

biomarker.state.mat = binarize_to_thres(mat = diff.state.mat, thres = 0.6)
biomarkers.freq = colSums(biomarker.state.mat)/nrow(biomarker.state.mat)

pretty_print_vector_names_and_values(biomarkers.freq[biomarkers.freq > 0.5])

ERK_f: 1, SOS1: 0.714285714285714

So we identified two biomarkers, ERK_f (known from before) and SOS1. The average activity difference values for the SOS1 per cell line model datasets were:
breaks.red = quantile(c(-1,0), probs = seq(.05, .95, .05), na.rm = TRUE)
colors.red = sort(round(seq(255, 40, length.out = length(breaks.red) + 1), digits = 0)) %>%
  {paste0("rgb(255,", ., ",", ., ")")} # red

datatable(data = as.data.frame(diff.state.mat[,'SOS1']), colnames = "SOS1", 
  options = list(dom = 't', order = list(list(1, 'asc'))), width = "470px",
  caption = htmltools::tags$caption("Avg activity diff values (all cell lines except A498, PD-PI)", style="color:#dd4814; font-size: 18px")) %>% 
  formatRound(columns = 1, digits = 3) %>%
  formatStyle(columns = 1, backgroundColor = styleInterval(breaks.red, colors.red))

Some article evidence against the above (so SOS1 is somewhat expressed in these cancer cell lines):

PD-G2 activity biomarkers

As observed in the heatmap above, the PD-G2 synergy was predicted by both the cell specific and random models in the A498 cell line.

Cell-specific (A498)

PD.G2.avg.state.diff.cell.specific = cell.specific.synergy.analysis.res$A498$diff.state.synergies.mat["PD-G2", ]

We will now visualize the nodes average state differences in a network graph. Note that the good models are those that predicted the PD-G2 drug combination to be synergistic and were contrasted to those that predicted it to be antagonistic (bad models). The number of models in each category were:

model.predictions = model.predictions.per.cell.line[["A498"]]
models.stable.state = models.stable.state.per.cell.line[["A498"]]
drug.comb = "PD-G2"

good.models.num = sum(model.predictions[, drug.comb] == 1 & !is.na(model.predictions[, drug.comb]))
# unique good models
#models.link.operator = models.link.operators.per.cell.line$A498
#nrow(unique(models.link.operator[model.predictions[, drug.comb] == 1 & !is.na(model.predictions[, drug.comb]), ]))
bad.models.num  = sum(model.predictions[, drug.comb] == 0 & !is.na(model.predictions[, drug.comb]))

pretty_print_string(paste0("Number of 'good' models (PD-G2 synergistic) in the A498 cell line: ", good.models.num))

Number of ‘good’ models (PD-G2 synergistic) in the A498 cell line: 238

pretty_print_string(paste0("Number of 'bad' models (PD-G2 antagonistic) in the A498 cell line: ", bad.models.num))

Number of ‘bad’ models (PD-G2 antagonistic) in the A498 cell line: 6812

plot_avg_state_diff_graph(net, diff = PD.G2.avg.state.diff.cell.specific, 
  layout = nice.layout, title = "PD-G2 activity state biomarkers (Cell specific models - A498)")

We now visualize the nodes average state differences in a dotchart where we can easily identify the active and inhibited state biomarkers with the defined thresholds (we have excluded nodes whose absolute average difference value was less than \(0.05\)):

df = as.data.frame(PD.G2.avg.state.diff.cell.specific)
colnames(df) = "avg.state.diff"
df = df %>% 
  rownames_to_column(var = "nodes") %>%
  filter(avg.state.diff > 0.05 | avg.state.diff < -0.05)

ggdotchart(df, x = "nodes", y = "avg.state.diff",
  title = "Average activity state difference (Good - bad) - A498 cell line", 
  label = "nodes", font.label = list(size = 11, color = "blue"),
  label.select = list(criteria = "`y` >= 0.9 | `y` <= -0.5"), 
  repel = TRUE, label.rectangle = TRUE,
  xlab = "Nodes", ylab = "Average Activity State", 
  ylim = c(-1,1), add = "segments") + 
    font("x.text", size = 8) +
    font("title", size = 14) + 
    geom_hline(yintercept = -0.7, linetype = "dashed", color = "red") + 
    geom_hline(aes(yintercept = 0.7, linetype = "0.7"), color = "red") +
    scale_linetype_manual(name = "Threshold", values = 2, 
      guide = guide_legend(override.aes = list(color = "red"))) + 
    theme(legend.position = c(0.2,0.7)) + 
    annotate("text", x = c(18, 32), y = -0.9, label = c("Good models: PD-G2 synergy,", "Bad Models: PD-G2 antagonism"))

Note again the boolean equation: PPM1A *= ( PTEN ). This means that PTEN is the only inhibited node of interest


As in the case of the PD-PI synergy, inhibition of the PTEN node and overexpression of the ERK_f node are the main biomarkers (see dotchart above) that make the models predict the PD-G2 synergy.

Synergy Subsets Analysis

It will be interesting to find all the possible synergy sets and subsets that include the PD-G2 as the extra synergy. Using these synergy sets, models that predict a set of synergies will be contrasted to models that predicted the same set with the addition of the extra PD-G2 synergy. Thus we could find synergy biomarkers that allow already good predicting models to predict the additional synergy of interest. This investigation will allow us thus to refine the activity state biomarkers we found above.

We first construct a matrix where each row is a set vs subset average activity difference vector of the network nodes:
model.predictions = model.predictions.per.cell.line$A498
models.stable.state = models.stable.state.per.cell.line$A498
models.link.operator = models.link.operators.per.cell.line$A498

res = get_synergy_comparison_sets(cell.specific.synergy.analysis.res$A498$synergy.subset.stats)
PD.G2.res = res %>% filter(synergies == "PD-G2")

diff.state.list = list()

for (i in 1:nrow(PD.G2.res)) {
  synergy.set    = PD.G2.res[i, 2]
  synergy.subset = PD.G2.res[i, 3]
  
  synergy.set.str    = unlist(strsplit(x = synergy.set, split = ","))
  synergy.subset.str = unlist(strsplit(x = synergy.subset, split = ","))
  
  # count models
  synergy.set.models.num = count_models_that_predict_synergies(synergy.set.str, model.predictions)
  synergy.subset.models.num = count_models_that_predict_synergies(synergy.subset.str, model.predictions)
  
  # print(paste0(synergy.set.models.num, " ", synergy.subset.models.num))
  
  # if too small number of models, skip the diff vector
  if ((synergy.set.models.num <= 3) | (synergy.set.models.num <= 3)) 
     next
  
  # get the diff
  diff.state.PD.G2 = get_avg_activity_diff_based_on_synergy_set_cmp(synergy.set.str, synergy.subset.str, model.predictions, models.stable.state)
  diff.state.list[[paste0(synergy.set, " vs ", synergy.subset)]] = diff.state.PD.G2
}

diff.state.mat = do.call(rbind, diff.state.list)

caption.title.7 = "Table 7: Average activity difference values across all synergy set comparisons (PD-G2)"
datatable(data = diff.state.mat[, c("SRC", "CSK", "PTEN", "STAT1", "PTPN6")], options = list(
    searching = FALSE, pageLength = 5, lengthMenu = c(5, 10)),
  caption = htmltools::tags$caption(caption.title.7, style="color:#dd4814; font-size: 18px")) %>% 
  formatRound(1:ncol(diff.state.mat), digits = 3)

If we visualize the average activity state difference across all synergy comparison sets from Table 7 in a network graph, we see that there are no nodes with significant average state difference:

plot_avg_state_diff_graph(net, diff = colMeans(diff.state.mat), layout = nice.layout, 
  title = "Average activity state diff across all synergy subsets (PD-G2)")

We also visualize the median activity difference per node from Table 7 using a dotchart, where we have excluded the nodes that have an absolute median activity difference of \(0.05\) or less:

df = as.data.frame(apply(diff.state.mat, 2, median))
colnames(df) = "median.state.diff"
df = df %>% 
  rownames_to_column(var = "nodes") %>%
  filter(median.state.diff > 0.05 | median.state.diff < -0.05)

ggdotchart(df, x = "nodes", y = "median.state.diff",
  title = "Median activity state difference across all synergy comparison sets (PD-G2, A498 cell line)", 
  label = "nodes", font.label = list(size = 11, color = "blue"),
  label.select = list(criteria = "`y` >= 0.5 | `y` <= -0.5"), 
  repel = TRUE, label.rectangle = TRUE,
  xlab = "Nodes", ylab = "Median Activity State", 
  ylim = c(-1,1), add = "segments") + 
    font("x.text", size = 10) +
    font("title", size = 11) + 
    geom_hline(yintercept = -0.7, linetype = "dashed", color = "red") + 
    geom_hline(aes(yintercept = 0.7, linetype = "0.7"), color = "red") +
    scale_linetype_manual(name = "Threshold", values = 2, 
      guide = guide_legend(override.aes = list(color = "red"))) + 
    theme(legend.position = c(0.2,0.7))

The above results show the same thing we found also before with the simple model comparison method: ERK_f overexpression, PTEN inhibition.


Random

We get the average state differences per network node for the A498 cell line from the random models:

PD.G2.avg.state.diff.random = random.synergy.analysis.res$A498$diff.state.synergies.mat["PD-G2",]

We will now visualize the nodes average state differences in a network graph. Note that the good models are those that predicted the PD-G2 drug combination to be synergistic and were contrasted to those that predicted it to be antagonistic (bad models). The number of models in each category were:

drug.comb = "PD-G2"

good.models.num = sum(random.model.predictions[, drug.comb] == 1 & !is.na(random.model.predictions[, drug.comb]))
# unique good models
# nrow(unique(random.models.link.operator[random.model.predictions[, drug.comb] == 1 & !is.na(random.model.predictions[, drug.comb]),]))
bad.models.num  = sum(random.model.predictions[, drug.comb] == 0 & !is.na(random.model.predictions[, drug.comb]))

pretty_print_string(paste0("Number of 'good' random models (PD-G2 synergistic) in the A498 cell line: ", good.models.num))

Number of ‘good’ random models (PD-G2 synergistic) in the A498 cell line: 1272

pretty_print_string(paste0("Number of 'bad' random models (PD-G2 antagonistic) in the A498 cell line: ", bad.models.num))

Number of ‘bad’ random models (PD-G2 antagonistic) in the A498 cell line: 5756

plot_avg_state_diff_graph(net, diff = PD.G2.avg.state.diff.random, 
  layout = nice.layout, title = "PD-G2 activity state biomarkers (Random models - A498)")

The overexpression of ERK_f is also a characteristic of the random models that predict the PD-G2 drug combination to be synergistic.


Synergy subsets analysis

We perform the same kind of analysis as with the cell-specific models: models that predict a set of synergies will be contrasted to models that predicted the same set with the addition of the extra PD-G2 synergy, allowing us thus to refine the activity state biomarkers we found above from the random models.

We first construct a matrix where each row is a set vs subset average activity difference vector of the network nodes:
res = get_synergy_comparison_sets(random.synergy.analysis.res$A498$synergy.subset.stats)
PD.G2.res = res %>% filter(synergies == "PD-G2")

diff.state.list.random = list()
for (i in 1:nrow(PD.G2.res)) {
  synergy.set    = PD.G2.res[i, 2]
  synergy.subset = PD.G2.res[i, 3]
  
  synergy.set.str    = unlist(strsplit(x = synergy.set, split = ","))
  synergy.subset.str = unlist(strsplit(x = synergy.subset, split = ","))
  
  # count models
  synergy.set.models.num = count_models_that_predict_synergies(synergy.set.str, random.model.predictions)
  synergy.subset.models.num = count_models_that_predict_synergies(synergy.subset.str, random.model.predictions)
  # print(paste0(synergy.set.models.num, " ", synergy.subset.models.num))
  
  # if too small number of drug combinations in the subset, skip the diff vector
  #if (length(synergy.subset.str) <= 3) next
  # if too small number of models compared
  if ((synergy.set.models.num <= 3) | (synergy.set.models.num <= 3)) next
  
  # get the diff
  diff.state.PD.G2 = get_avg_activity_diff_based_on_synergy_set_cmp(synergy.set.str, synergy.subset.str, random.model.predictions, random.models.stable.state)
  
  diff.state.list.random[[paste0(synergy.set, " vs ", synergy.subset)]] = diff.state.PD.G2
}

diff.state.mat.random = do.call(rbind, diff.state.list.random)

caption.title.8 = "Table 8: Average link operator difference values across all synergy set comparisons (PD-G2)"
datatable(data = diff.state.mat.random[, c("SRC", "RAC_f", "MEK_f", "STAT1", "PTEN")], options = list(
    searching = FALSE, pageLength = 5, lengthMenu = c(5, 10)),
    caption = htmltools::tags$caption(caption.title.8, style="color:#dd4814; font-size: 18px")) %>% 
      formatRound(1:ncol(diff.state.mat.random), digits = 3)

If we visualize the average activity state difference across all synergy comparison sets from Table 8 we have some nodes that show significantly different activity in the random models that predict the extra PD-G2 synergy:

plot_avg_state_diff_graph(net, diff = colMeans(diff.state.mat.random), layout = nice.layout, 
  title = "Average activity state diff across all synergy subsets (PD-G2)")

We also visualize the median activity difference per node from Table 8 using a dotchart, where we have excluded the nodes that have an absolute median activity difference of \(0.05\) or less:

df = as.data.frame(apply(diff.state.mat.random, 2, median))
colnames(df) = "median.state.diff"
df = df %>% 
  rownames_to_column(var = "nodes") %>%
  filter(median.state.diff > 0.05 | median.state.diff < -0.05)

ggdotchart(df, x = "nodes", y = "median.state.diff",
  title = "Median activity state difference across all synergy comparison sets (PD-G2, Random Models, A498)", 
  label = "nodes", font.label = list(size = 11, color = "blue"),
  label.select = list(criteria = "`y` >= 0.7 | `y` <= -0.7"), 
  repel = TRUE, label.rectangle = TRUE,
  xlab = "Nodes", ylab = "Median Activity State", 
  ylim = c(-1,1), add = "segments") + 
    font("x.text", size = 10) +
    font("title", size = 11) + 
    geom_hline(yintercept = -0.7, linetype = "dashed", color = "red") + 
    geom_hline(aes(yintercept = 0.7, linetype = "0.7"), color = "red") +
    scale_linetype_manual(name = "Threshold", values = 2, 
      guide = guide_legend(override.aes = list(color = "red"))) + 
    theme(legend.position = c(0.2,0.7))

From the above figure we see that the nodes PTEN, PPM1A and LIMK2 are more found to be more inhibited in the models that predicted the PD-G2 synergy and the nodes PRKCD and PDPK1 more activated.

From the boolean equation: LIMK2 *= ROCK1 and/or not PRKCD, since PRKCD == TRUE (overexpressed) we reduced it to: LIMK2 *= ROCK1 and/or FALSE where 3 out of 4 cases here correspond to LIMK2 == FALSE (inhibited). So, we could argue that most of the times the LIMK2 inhibition is due to the PRKCD overexpression in the end!

Note the equation: PRKCD *= PDPK1 or CASP3. This means that the activation of PRKCD is a direct consequence from PDPK1’s activation. But PDPK1 is also the target of the G2 drug. This means that models that responded to the PD-G2 combination are the ones that showed PDPK1 overexpression!

Also again from the equation: PPM1A *= PTEN, we have that PTEN is the only inhibited node of interest

So, according to this analysis, PTEN inhibition and PDPK1 overexpression are the main biomarkers that cause the random models to respond synergistically to the PD-G2 drug combination.


We will again use the MCLP-dataset to check for the activity/signaling values of PDPK1 and PIK3CA (the later because it’s strongly connected with the others based on the following boolean equation in our model: PDPK1 *= PIK3CA and/or not PTEN).

We check the phosphorylation value of the PDK1_pS241 across all cell lines:

PDPK1.data = mclp.data %>% select(Sample_Name, PDK1_pS241) %>% na.omit()

# find the activity classes (3 classes: low activity, no activity, high activity)
res = Ckmeans.1d.dp(x = PDPK1.data$PDK1_pS241, k = 3)
activity = as.factor(res$cluster)
levels(activity) = c("low", "medium", "high")
PDPK1.data = cbind(PDPK1.data, activity)

ggdotchart(data = PDPK1.data, x = "Sample_Name", y = "PDK1_pS241", 
  title = "PDK1_pS241 signaling across all cell lines in MCLP Dataset",
  color = "activity", palette = c("red", "grey", "green"), 
  label = "Sample_Name", label.select = cell.lines.in.mclp, repel = TRUE,
  add = "segments", label.rectangle = TRUE,
  xlab = "Cell Lines", ylab = "PDK1_pS241 Signaling",
  add.params = list(color = "activity", palette = c("red", "grey", "green"))) + 
    theme(axis.text.x = element_blank(), axis.ticks = element_blank())

Since for A498 cell line we observe one of the largest signaling measurements compared to all the cell lines for the PDK1_pS241 site, this means that PDPK1 is overexpressed, which is exactly what we found from our analysis above.


Also, we check the phosphorylation value of the PI3K-p110-alpha across all cell lines:

PIK3CA.data = mclp.data %>% select(Sample_Name, PI3KP110ALPHA) %>% na.omit()

# find the activity classes (3 classes: low activity, no activity, high activity)
res = Ckmeans.1d.dp(x = PIK3CA.data$PI3KP110ALPHA, k = 3)
activity = as.factor(res$cluster)
levels(activity) = c("low", "medium", "high")
PIK3CA.data = cbind(PIK3CA.data, activity)

ggdotchart(data = PIK3CA.data, x = "Sample_Name", y = "PI3KP110ALPHA", 
  title = "PI3K-p110-alpha signaling across all cell lines in MCLP Dataset",
  color = "activity", palette = c("red", "grey", "green"), 
  label = "Sample_Name", label.select = cell.lines.in.mclp, repel = TRUE,
  add = "segments", label.rectangle = TRUE,
  xlab = "Cell Lines", ylab = "PI3K-p110-alpha Signaling",
  add.params = list(color = "activity", palette = c("red", "grey", "green"))) + 
    theme(axis.text.x = element_blank(), axis.ticks = element_blank())

So, the results above hint that PIK3CA might be inhibited in the A498 cell line, which considering the PTEN inhibition, PDPK1 overexpression and their boolean equation (which connects all of them together): PDPK1 *= PIK3CA and/or not PTEN (PDPK1 = TRUE and PIK3CA = PTEN = FALSE), we conclude that there must be an or not link operator between the 2 regulators.


PTEN inhibition and PDPK1 overexpression play both an important role in the manifestation of the PD-G2 synergy as was found with the synergy subset analysis method for the random models. Also, PIK3CA has an or not (link operator) relationship with PTEN (according to our model’s equation) as regulators of PDPK1.


PD-G2 in other cell lines

Though the PD-G2 synergy was observed and predicted in the A498 model dataset, we investigate its biomarkers in the other cell lines where it was predicted as a False Positive (FP) synergy (predicted by the models but not observed in the experiments). Thus, we can still see if there are any common (activity state and link operator) biomarkers for this synergy across the all the cell-specific models by contrasting in each cell line the models that predicted PD-G2 (if there were any) vs the models that did not:

drug.comb = "PD-G2"

diff.state.list = list()

for (cell.line in cell.lines) {
  if (cell.line == "A498") next
  
  model.predictions = model.predictions.per.cell.line[[cell.line]]
  models.stable.state = models.stable.state.per.cell.line[[cell.line]]
  
  models.predicted.num = sum(model.predictions[,drug.comb], na.rm = T)
  models.not.predicted.num = sum(!model.predictions[,drug.comb], na.rm = T)
  
  # See the #models that predicted vs #models those that did not
  # print(paste0(models.predicted.num, " vs ", models.not.predicted.num))
  
  if (models.predicted.num != 0 & models.not.predicted.num != 0)
    diff.state.list[[cell.line]] = get_avg_activity_diff_based_on_specific_synergy_prediction(model.predictions, models.stable.state, drug.comb)
}

# combine the data to a single matrix
diff.state.mat = do.call(rbind, diff.state.list)

Now, we identify the biomarkers of interest as those nodes that have surpassed a user-defined threshold (\(0.6\)) across as many as possible cell lines - counting thus the frequency that this has occured. We show those nodes that surpassed the threshold in at least half of the cell lines:

biomarker.state.mat = binarize_to_thres(mat = diff.state.mat, thres = 0.6)
biomarkers.freq = colSums(biomarker.state.mat)/nrow(biomarker.state.mat)

pretty_print_vector_names_and_values(biomarkers.freq[biomarkers.freq > 0.5])

ERK_f: 1, SOS1: 0.571428571428571

So we identified two biomarkers, ERK_f (known from before) and SOS1. The average activity difference values for the SOS1 per cell line model datasets were:
datatable(data = as.data.frame(diff.state.mat[,'SOS1']), colnames = "SOS1", 
  options = list(dom = 't', order = list(list(1, 'asc'))), width = "470px",
  caption = htmltools::tags$caption("Avg activity diff values (all cell lines except A498, PD-G2)", style="color:#dd4814; font-size: 18px")) %>%
  formatRound(columns = 1, digits = 3) %>%
  formatStyle(columns = 1, backgroundColor = styleInterval(breaks.red, colors.red))

I found some articles that I believe show that SOS1 is expressed in some of the above cancer cell lines (in contrast to the results above).

PI-D1 activity biomarkers

As observed in the heatmap above, the PI-D1 synergy was predicted by both the cell specific and random models in some of the cell lines tested. So, for each seperate model dataset, we are going to contrast the models that predicted the PI-D1 synergy vs the models that did not (if these two sets of models do exist for each cell line) and find the average activity difference per node in each case:

drug.comb = "PI-D1"

diff.state.list = list()

# cell-specific data
for (cell.line in cell.lines) {
  model.predictions = model.predictions.per.cell.line[[cell.line]]
  models.stable.state = models.stable.state.per.cell.line[[cell.line]]
  
  models.predicted.num = sum(model.predictions[,drug.comb], na.rm = T)
  models.not.predicted.num = sum(!model.predictions[,drug.comb], na.rm = T)
  
  # See the #models that predicted vs #models those that did not
  # print(paste0(models.predicted.num, " vs ", models.not.predicted.num))
  
  if (models.predicted.num != 0 & models.not.predicted.num != 0)
    diff.state.list[[cell.line]] = get_avg_activity_diff_based_on_specific_synergy_prediction(model.predictions, models.stable.state, drug.comb)
}

# random model data
#random.models.predicted.num = sum(random.model.predictions[,drug.comb], na.rm = T)
#random.models.not.predicted.num = sum(!random.model.predictions[,drug.comb], na.rm = T)
diff.state.list[["random"]] = get_avg_activity_diff_based_on_specific_synergy_prediction(random.model.predictions, random.models.stable.state, drug.comb)

# combine the data to a single matrix
diff.state.mat = do.call(rbind, diff.state.list)

Now, we identify the biomarkers of interest as those nodes that have surpassed a user-defined threshold (\(0.6\)) across as many as possible cell lines - counting thus the frequency that this has occured. We show those nodes that surpassed the threshold in at least half of the cell lines:

biomarker.state.mat = binarize_to_thres(mat = diff.state.mat, thres = 0.6)
biomarkers.freq = colSums(biomarker.state.mat)/nrow(biomarker.state.mat)

pretty_print_vector_names_and_values(biomarkers.freq[biomarkers.freq > 0.5])

ERK_f: 1, ILK: 0.666666666666667, CDC42: 0.666666666666667, PAK1: 0.666666666666667

So, ERK_f was a biomarker for all the cell lines that predicted the PI-D1 synergy and the nodes ILK, CDC42 and PAK1 in more than half of them. If we see the actual numbers we can clarify their state as well:

breaks.green = quantile(c(0,1), probs = seq(.05, .95, .05), na.rm = TRUE)
colors.green = round(seq(255, 40, length.out = length(breaks.red) + 1), digits = 0) %>%
  {paste0("rgb(", ., ",255,", ., ")")} # green

caption.title.9 = "Table 9: Average activity state difference values across all model datasets that predicted PI-D1"
datatable(data = diff.state.mat[,c("ILK","PAK1","CDC42","ERK_f","RAC_f","ARHGAP24","SRC")], options = list(dom = 't'),
  caption = htmltools::tags$caption(caption.title.9, style="color:#dd4814; font-size: 18px")) %>% 
  formatRound(1:11, digits = 3) %>%
  formatStyle(columns = c(1:3), backgroundColor = styleInterval(breaks.red, colors.red)) %>%
  formatStyle(columns = 4, backgroundColor = styleInterval(breaks.green, colors.green))

If we examine the boolean equations: ILK *= PAK1, PAK1 *= CDC42 or RAC_f and CDC42 *= SRC and/or not ARHGAP24 as well as the information from the table 9, then we can clearly see that the only inhibited node of interest is CDC42 among the 3 most frequent inhibited biomarkers.


The overexpression of ERK_f and the inhibition of CDC42 are the two most common biomarkers that characterize the models that are able to predict the PI-D1 observed synergy.

R session info

xfun::session_info()
R version 3.6.2 (2019-12-12)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.4 LTS

Locale:
  LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
  LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
  LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
  LC_PAPER=en_US.UTF-8       LC_NAME=C                 
  LC_ADDRESS=C               LC_TELEPHONE=C            
  LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

Package version:
  assertthat_0.2.1     backports_1.1.5      base64enc_0.1-3     
  BH_1.72.0.3          bibtex_0.4.2.2       circlize_0.4.8      
  Ckmeans.1d.dp_4.3.0  cli_2.0.1            clue_0.3-57         
  cluster_2.1.0        colorspace_1.4-1     compiler_3.6.2      
  ComplexHeatmap_2.0.0 cowplot_1.0.0        crayon_1.3.4        
  crosstalk_1.0.0      digest_0.6.23        dplyr_0.8.3         
  DT_0.11              ellipsis_0.3.0       emba_0.1.2          
  evaluate_0.14        fansi_0.4.1          farver_2.0.3        
  fastmap_1.0.1        gbRd_0.4-11          GetoptLong_0.1.8    
  ggplot2_3.2.1        ggpubr_0.2.4         ggrepel_0.8.1       
  ggsci_2.9            ggsignif_0.6.0       GlobalOptions_0.1.1 
  glue_1.3.1           graphics_3.6.2       grDevices_3.6.2     
  grid_3.6.2           gridExtra_2.3        gtable_0.3.0        
  highr_0.8            htmltools_0.4.0      htmlwidgets_1.5.1   
  httpuv_1.5.2         igraph_1.2.4.2       jsonlite_1.6        
  knitr_1.27           labeling_0.3         later_1.0.0         
  lattice_0.20.38      lazyeval_0.2.2       lifecycle_0.1.0     
  magrittr_1.5         markdown_1.1         MASS_7.3.51.5       
  Matrix_1.2.18        methods_3.6.2        mgcv_1.8.31         
  mime_0.8             munsell_0.5.0        nlme_3.1.143        
  parallel_3.6.2       pillar_1.4.3         pkgconfig_2.0.3     
  plogr_0.2.0          plyr_1.8.5           png_0.1-7           
  polynom_1.4.0        promises_1.1.0       purrr_0.3.3         
  R6_2.4.1             RColorBrewer_1.1-2   Rcpp_1.0.3          
  Rdpack_0.11-1        reshape2_1.4.3       rje_1.10.13         
  rjson_0.2.20         rlang_0.4.4          rmarkdown_2.1       
  scales_1.1.0         shape_1.4.4          shiny_1.4.0         
  sourcetools_0.1.7    splines_3.6.2        stats_3.6.2         
  stringi_1.4.5        stringr_1.4.0        tibble_2.1.3        
  tidyr_1.0.2          tidyselect_1.0.0     tinytex_0.19        
  tools_3.6.2          usefun_0.4.3         utf8_1.1.4          
  utils_3.6.2          vctrs_0.2.2          viridisLite_0.3.0   
  visNetwork_2.0.9     withr_2.1.2          xfun_0.12           
  xtable_1.8-4         yaml_2.2.0           zeallot_0.1.0       
LS0tCnRpdGxlOiAiQ2FzY2FkZSBTeW5lcmd5IEJpb21hcmtlcnMgZm9yIEFLLVBELCBQRC1QSSwgUEQtRzIsIFBJLUQxIHN5bmVyZ2llcyIKYXV0aG9yOiAiW0pvaG4gWm9ib2xhc10oaHR0cHM6Ly9naXRodWIuY29tL2JibG9kZm9uKSIKZGF0ZTogIkxhc3QgdXBkYXRlZDogYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY3NzOiBzdHlsZS5jc3MKICAgIHRoZW1lOiB1bml0ZWQKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKIyMgSW50cm8gey19CgpUaGUgcHVycG9zZSBvZiB0aGlzIGFuYWx5c2lzIGlzIHRvIGZpbmQgc3luZXJneSBiaW9tYXJrZXJzIHVzaW5nIHRoZSBbZW1iYSBSIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9iYmxvZGZvbi9lbWJhKSBpbiB0aGUgY2FzY2FkZSBib29sZWFuIG1vZGVsIGRhdGFzZXRzICgqQ2FzY2FkZSogaXMgdGhlIG5hbWUgb2YgdGhlIHRvcG9sb2d5IHVzZWQpLiAKUGFydGljdWxhcmx5LCB3ZSB3aWxsIGludmVzdGlnYXRlICQ0JCBzeW5lcmdpZXM6IGBBSy1QRGAsIGBQRC1QSWAsIGBQRC1HMmAsIGBQSS1EMWAgYW5kIHRyeSB0byBmaW5kIGNhdXNhbCBtZWNoYW5pc21zIHRoYXQgbWlnaHQgZXhwbGFpbiB3aHkgYW5kIHdoZXJlIChwYXRod2F5cykgdGhlc2Ugc3luZXJnaWVzIG1hbmlmZXN0LiAKCk5vdGUgdGhhdCBgQUtgIGlzIGFuICoqQUtUIGluaGliaXRvcioqIGFuZCBgUElgIGlzIGEgKipQSTNLIGluaGliaXRvcioqIGFuZCB0aGV5IGJvdGggdGFyZ2V0IHRoZSAqKlBJM0svQUtUL21UT1IgcGF0aHdheSoqLiBUaGUgY29tYmluYXRpb24gb2Ygc3VjaCBhbiBpbmhpYml0b3Igd2l0aCBhICoqTUVLIGluaGliaXRvcioqIC0gYFBEYCAtIHRoYXQgdGFyZ2V0cyB0aGUgKipNQVBLL0VSSyBwYXRod2F5KiosIGhhcyBwcm92ZW4gdG8gYmUgbW9yZSBlZmZlY3RpdmUgdGhhbiB0aGUgc2luZ2xlIGRydWcgdHJlYXRtZW50IGR1cmluZyBjbGluaWNhbCB0cmlhbHMgd2l0aCBwYXRpZW50cyB0aGF0IGhhZCBhZHZhbmNlZCBjb2xvcmVjdGFsIGNhcmNpbm9tYSAoW3NvdXJjZV0oaHR0cHM6Ly9jbGluaWNhbHRyaWFscy5nb3YvY3QyL3Nob3cvTkNUMDEzMzM0NzUpKS4gClRoZSBgRzJgIGRydWcgaXMgYSAqKlBEUEsxIGluaGliaXRvcioqIGFuZCB0aGUgYEQxYCBkcnVnIGlzICoqYW4gaW5oaWJpdG9yIG9mIGBSU0tgIGlzb2Zvcm1zKiogKFJQUzZLQTEsIFJQUzZLQTMsIFJQUzZLQTIsIFJQUzZLQTYpLgoKVGhlIGJvb2xlYW4gbW9kZWwgZGF0YXNldHMgYXJlIGluIHRvdGFsICQ5JDogb25lIGZvciBlYWNoIGNlbGwgbGluZSAKb2YgaW50ZXJlc3QgKDggY2VsbCBsaW5lcykgd2hlcmUgdGhlIG1vZGVscyB3ZXJlICoqZml0dGVkIHRvIGEgc3BlY2lmaWMgc3RlYWR5IHN0YXRlKiogaW4gZWFjaCAKY2FzZSBhbmQgb25lIGZvciB0aGUgc28tY2FsbGVkICoqcmFuZG9tIG1vZGVscyoqIHdoaWNoIHdlcmUgZ2VuZXJhdGVkICpyYW5kb21seSogaW4gCnRoZSBzZW5zZSB0aGF0IHdlcmUgZml0dGVkIG9ubHkgdG8gYSBwcm9saWZlcmF0aW9uIHN0YXRlIChzaW11bGF0aW9ucyB3ZXJlIGRvbmUgdXNpbmcgCnRoZSBEcnVnTG9naWNzIHNvZnR3YXJlIG1vZHVsZXMgYEdpdHNiZWAgYW5kIGBEcmFibWVgKS4KCiMjIFN1bW1hcnkgb2YgcmVzdWx0cyB7LX0KClRoZSByZXN1bHRzIHdpbGwgZm9jdXMgb24gdGhlICoqYWN0aXZpdHkgc3RhdGUgYmlvbWFya2VycyoqIGFuZCBub3QgdGhlICpsaW5rIG9wZXJhdG9yKiBvbmVzIChzaW5jZSB3ZSBkaWRuJ3Qga25vdyBob3cgdG8gYmVzdCBpbnRlcnByZXQgdGhlbSAtIHRob3VnaCBhbiBlZmZvcnQgaGFzIGJlZW4gbWFkZSBpbiB0aGUgW0FLLVBEIGFuYWx5c2lzXSgjYWstcGQtYmlvbWFya2VycykpLgoKPGRpdiBjbGFzcz0iZ3JlZW4tYm94Ij4KLSAqKkFLLVBEKiogKEE0OTggY2VsbCBsaW5lKQogIC0gYEVSS19mYCBvdmVyZXhwcmVzc2lvbiAoW2NlbGwtc3BlY2lmaWMgbW9kZWxzXSgjVGV4dDApIGFuZCBbcmFuZG9tIG1vZGVsc10oI1RleHQwMikpCiAgLSBgU1JDYCBbaW5oaWJpdGlvbl0oI1RleHQwMSkKLSAqKlBELVBJKiogKEE0OTggY2VsbCBsaW5lKQogIC0gYEVSS19mYCBvdmVyZXhwcmVzc2lvbiAoW3JhbmRvbSBtb2RlbHNdKCNUZXh0MikpCiAgLSBgUFRFTmAgaW5oaWJpdGlvbiAtIGluIFtjZWxsLXNwZWNpZmljIG1vZGVsc10oI1RleHQxKSAod2l0aCBgRVJLX2ZgIG92ZXJleHByZXNzaW9uKQogIC0gYFNPUzFgIFtpbmhpYml0aW9uXSgjc29zMSkgaW4gYWxsIGNlbGwgbGluZXMgZXhjZXB0IGBBNDk4YCBhbmQgYFNXNjIwYAotICoqUEQtRzIqKiAoQTQ5OCBjZWxsIGxpbmUpCiAgLSBgRVJLX2ZgIG92ZXJleHByZXNzaW9uIChbcmFuZG9tIG1vZGVsc10oI1RleHQ1KSkKICAtIGBQRFBLMWAgW292ZXJleHByZXNzaW9uXSgjVGV4dDYpCiAgLSBgUFRFTmAgaW5oaWJpdGlvbiAoc2VlIFtoZXJlXSgjVGV4dDMpIGFuZCBbaGVyZV0oI1RleHQ0KSkKICAtIGBQSUszQ0FgIGFuZCBgUFRFTmAgc2hhcmUgYW4gW2BPUiBOT1RgIHJlbGF0aW9uc2hpcCBhcyByZWd1bGF0b3JzIG9mIGBQRFBLMWBdKCNwZHBrMS1lcSkKICAtIGBTT1MxYCBbaW5oaWJpdGlvbl0oI3NvczIpIGluIGFsbCBjZWxsIGxpbmVzIGV4Y2VwdCBgQTQ5OGAgYW5kIGBTVzYyMGAKLSAqKlBJLUQxKiogKGFsbCA5IG1vZGVsIGRhdGFzZXRzKQogIC0gYEVSS19mYCBbb3ZlcmV4cHJlc3Npb25dKCNUYWJsZTkpCiAgLSBgQ0RDNDJgIFtpbmhpYml0aW9uXSgjVGFibGU5KQo8L2Rpdj4KCiMjIElucHV0IHstfQoKTG9hZGluZyBsaWJyYXJpZXM6CmBgYHtyIExvYWQgbGlicmFyaWVzLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoZW1iYSkKbGlicmFyeSh1c2VmdW4pCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShDa21lYW5zLjFkLmRwKQpgYGAKCldlIGxvYWQgdGhlIGNlbGwtc3BlY2lmaWMgaW5wdXQgZGF0YToKYGBge3IgQ2VsbC1zcGVjaWZpYyBJbnB1dCwgZXZhbD1GQUxTRX0KIyBDZWxsIExpbmVzCmNlbGwubGluZXMgPSBjKCJBNDk4IiwgIkFHUyIsICJEVTE0NSIsICJjb2xvMjA1IiwgIlNXNjIwIiwgIlNGMjk1IiwgIlVBQ0M2MiIsICJNREEtTUItNDY4IikKCmNlbGwubGluZS5kaXJzID0gc2FwcGx5KGNlbGwubGluZXMsIGZ1bmN0aW9uKGNlbGwubGluZSkgewogIHBhc3RlMChnZXR3ZCgpLCAiLyIsIGNlbGwubGluZSkKfSkKCiMgTW9kZWwgcHJlZGljdGlvbnMKbW9kZWwucHJlZGljdGlvbnMuZmlsZXMgPSBzYXBwbHkoY2VsbC5saW5lLmRpcnMsIGZ1bmN0aW9uKGNlbGwubGluZS5kaXIpIHsKICBwYXN0ZTAoY2VsbC5saW5lLmRpciwgIi9tb2RlbF9wcmVkaWN0aW9ucyIpCn0pCgptb2RlbC5wcmVkaWN0aW9ucy5wZXIuY2VsbC5saW5lID0gbGFwcGx5KG1vZGVsLnByZWRpY3Rpb25zLmZpbGVzLCBmdW5jdGlvbihmaWxlKSB7ICAgCiAgZ2V0X21vZGVsX3ByZWRpY3Rpb25zKGZpbGUpIAp9KQoKIyBPYnNlcnZlZCBzeW5lcmdpZXMKb2JzZXJ2ZWQuc3luZXJnaWVzLmZpbGVzID0gc2FwcGx5KGNlbGwubGluZS5kaXJzLCBmdW5jdGlvbihjZWxsLmxpbmUuZGlyKSB7CiAgcGFzdGUwKGNlbGwubGluZS5kaXIsICIvb2JzZXJ2ZWRfc3luZXJnaWVzIikKfSkKCm9ic2VydmVkLnN5bmVyZ2llcy5wZXIuY2VsbC5saW5lID0gbGFwcGx5KG9ic2VydmVkLnN5bmVyZ2llcy5maWxlcywgZnVuY3Rpb24oZmlsZSkgewogIGdldF9vYnNlcnZlZF9zeW5lcmdpZXMoZmlsZSkKfSkKCiMgTW9kZWxzIFN0YWJsZSBTdGF0ZSAoMSBwZXIgbW9kZWwpCm1vZGVscy5zdGFibGUuc3RhdGUuZmlsZXMgPSBzYXBwbHkoY2VsbC5saW5lLmRpcnMsIGZ1bmN0aW9uKGNlbGwubGluZS5kaXIpIHsKICBwYXN0ZTAoY2VsbC5saW5lLmRpciwgIi9tb2RlbHNfc3RhYmxlX3N0YXRlIikKfSkKCm1vZGVscy5zdGFibGUuc3RhdGUucGVyLmNlbGwubGluZSA9IGxhcHBseShtb2RlbHMuc3RhYmxlLnN0YXRlLmZpbGVzLCBmdW5jdGlvbihmaWxlKSB7IAogIGFzLm1hdHJpeChyZWFkLnRhYmxlKGZpbGUsIGNoZWNrLm5hbWVzID0gRkFMU0UpKQp9KQoKIyBNb2RlbHMgTGluayBPcGVyYXRvcnMKbW9kZWxzLmxpbmsub3BlcmF0b3IuZmlsZXMgPSBzYXBwbHkoY2VsbC5saW5lLmRpcnMsIGZ1bmN0aW9uKGNlbGwubGluZS5kaXIpIHsKICBwYXN0ZTAoY2VsbC5saW5lLmRpciwgIi9tb2RlbHNfbGlua19vcGVyYXRvciIpCn0pCgptb2RlbHMubGluay5vcGVyYXRvcnMucGVyLmNlbGwubGluZSA9IGxhcHBseShtb2RlbHMubGluay5vcGVyYXRvci5maWxlcywgZnVuY3Rpb24oZmlsZSkgewogIGFzLm1hdHJpeChyZWFkLnRhYmxlKGZpbGUsIGNoZWNrLm5hbWVzID0gRkFMU0UpKQp9KQpgYGAKClRoZSByYW5kb20gbW9kZWwgaW5wdXQgZGF0YToKYGBge3IgUmFuZG9tIG1vZGVsIElucHV0LCBldmFsPUZBTFNFfQpyYW5kb20uZGlyID0gcGFzdGUwKGdldHdkKCksICIvcmFuZG9tIikKcmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zID0gZ2V0X21vZGVsX3ByZWRpY3Rpb25zKHBhc3RlMChyYW5kb20uZGlyLCAiL21vZGVsX3ByZWRpY3Rpb25zIikpCgpyYW5kb20ubW9kZWxzLnN0YWJsZS5zdGF0ZSA9IGFzLm1hdHJpeCgKICByZWFkLnRhYmxlKGZpbGUgPSBwYXN0ZTAocmFuZG9tLmRpciwgIi9tb2RlbHNfc3RhYmxlX3N0YXRlIiksIGNoZWNrLm5hbWVzID0gRkFMU0UpCikKCnJhbmRvbS5tb2RlbHMubGluay5vcGVyYXRvciA9CiAgYXMubWF0cml4KHJlYWQudGFibGUoZmlsZSA9IHBhc3RlMChyYW5kb20uZGlyLCAiL21vZGVsc19saW5rX29wZXJhdG9yIiksIGNoZWNrLm5hbWVzID0gRkFMU0UpKQoKIyB0aGUgbm9kZSBuYW1lcyB1c2VkIGluIG91ciBhbmFseXNpcwpub2RlLm5hbWVzID0gY29sbmFtZXMocmFuZG9tLm1vZGVscy5zdGFibGUuc3RhdGUpCiMgdGhlIHRlc3RlZCBkcnVnIGNvbWJpbmF0aW9ucwpkcnVnLmNvbWJvcyA9IGNvbG5hbWVzKHJhbmRvbS5tb2RlbC5wcmVkaWN0aW9ucykKYGBgCgojIyBTeW5lcmd5IEJpb21hcmtlciBBbmFseXNpcyB7LX0KClVzaW5nIHRoZSBnZW5lcmljIGZ1bmN0aW9uIGBiaW9tYXJrZXJfc3luZXJneV9hbmFseXNpc2AgZnJvbSB0aGUgW2VtYmEgUiBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vYmJsb2Rmb24vZW1iYSksIHdlIGNhbiBmaW5kCipzeW5lcmd5IGJpb21hcmtlcnMqIGkuZS4gbm9kZXMgd2hvc2UgYWN0aXZpdHkgYW5kIGJvb2xlYW4gZXF1YXRpb24gcGFyYW1ldGVyaXphdGlvbiAobGluayBvcGVyYXRvcikgYWZmZWN0IHRoZSBtYW5pZmVzdGF0aW9uIG9mIHN5bmVyZ2llcyBpbiB0aGUgYm9vbGVhbiBtb2RlbHMuIE1vZGVscyBhcmUgY2xhc3NpZmllZCBiYXNlZCBvbiB3aGV0aGVyIHRoZXkgcHJlZGljdCBvciBub3QgZWFjaCBvZiB0aGUgcHJlZGljdGVkIHN5bmVyZ2llcyBmb3VuZCBpbiBlYWNoIGJvb2xlYW4gZGF0YXNldC4KCkZpcnN0IHdlIHJ1biB0aGUgYW5hbHlzaXMgb24gdGhlICoqY2VsbC1zcGVjaWZpYyBib29sZWFuIG1vZGVsIGRhdGFzZXRzKiogKG5vdGUgCnRoYXQgZXZlcnkgaW5wdXQgdG8gdGhlIGBiaW9tYXJrZXJfc3luZXJneV9hbmFseXNpc2AgZnVuY3Rpb24gY2hhbmdlcyBwZXIgY2VsbCBsaW5lKToKYGBge3IgQ2VsbC1zcGVjaWZpYyBNb2RlbHMgLSBTeW5lcmd5IEJpb21hcmtlciBBbmFseXNpcywgZXZhbD1GQUxTRX0KY2VsbC5zcGVjaWZpYy5zeW5lcmd5LmFuYWx5c2lzLnJlcyA9IGxpc3QoKQoKZm9yIChjZWxsLmxpbmUgaW4gY2VsbC5saW5lcykgewogIGNlbGwuc3BlY2lmaWMuc3luZXJneS5hbmFseXNpcy5yZXNbW2NlbGwubGluZV1dID0KICAgIGJpb21hcmtlcl9zeW5lcmd5X2FuYWx5c2lzKG1vZGVsLnByZWRpY3Rpb25zLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dLAogICAgICBtb2RlbHMuc3RhYmxlLnN0YXRlLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dLCAKICAgICAgbW9kZWxzLmxpbmsub3BlcmF0b3JzLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dLAogICAgICBvYnNlcnZlZC5zeW5lcmdpZXMucGVyLmNlbGwubGluZVtbY2VsbC5saW5lXV0sIHRocmVzaG9sZCA9IDAuNykKfQpgYGAKCk5leHQgd2UgcnVuIHRoZSBhbmFseXNpcyBvbiB0aGUgKipyYW5kb20gYm9vbGVhbiBtb2RlbCBkYXRhc2V0cyoqIChub3RlIAp0aGF0IHRoZSBvbmx5IGlucHV0IHRvIHRoZSBgYmlvbWFya2VyX3N5bmVyZ3lfYW5hbHlzaXNgIGZ1bmN0aW9uIHRoYXQgY2hhbmdlcwppcyB0aGUgb2JzZXJ2ZWQgc3luZXJnaWVzIHBlciBjZWxsIGxpbmUgLSB0aGUgcmVzdCBpcyB0aGUgc2FtZSBkYXRhIGZyb20gdGhlIApyYW5kb20gbW9kZWwgZGF0YXNldCk6CmBgYHtyIFJhbmRvbSBNb2RlbHMgLSBTeW5lcmd5IEJpb21hcmtlciBBbmFseXNpcywgZXZhbD1GQUxTRX0KIyBTeW5lcmd5IEJpb21hcmtlcnMgZm9yIGNlbGwgcHJvbGlmZXJhdGlvbiBtb2RlbHMKcmFuZG9tLnN5bmVyZ3kuYW5hbHlzaXMucmVzID0gbGlzdCgpCgpmb3IgKGNlbGwubGluZSBpbiBjZWxsLmxpbmVzKSB7CiAgcmFuZG9tLnN5bmVyZ3kuYW5hbHlzaXMucmVzW1tjZWxsLmxpbmVdXSA9CiAgICBiaW9tYXJrZXJfc3luZXJneV9hbmFseXNpcyhyYW5kb20ubW9kZWwucHJlZGljdGlvbnMsIAogICAgICByYW5kb20ubW9kZWxzLnN0YWJsZS5zdGF0ZSwgcmFuZG9tLm1vZGVscy5saW5rLm9wZXJhdG9yLCAKICAgICAgb2JzZXJ2ZWQuc3luZXJnaWVzLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dLCB0aHJlc2hvbGQgPSAwLjcpCn0KYGBgCgpUaGUgcmVzdWx0cyBmcm9tIGFsbCB0aGUgcHJldmlvdXMgY29kZSBjaHVua3MgaGF2ZSBhbHJlYWR5IGJlZW4gZXhlY3V0ZWQgYW5kIHRoZSByZXN1bHQgZGF0YSBpcyBzYXZlZCBpbiB0aGUgYGNhc2NhZGVfc3luZXJneV9iaW9tYXJrZXJzLlJEYXRhYCBmaWxlLiBXZSBsb2FkIGFsbCB0aGUgb2JqZWN0czoKYGBge3IgTG9hZCBzYXZlZCBkYXRhIGZyb20gcHJldmlvdXMgY2h1bmtzfQojc2F2ZS5pbWFnZShmaWxlID0gImNhc2NhZGVfc3luZXJneV9iaW9tYXJrZXJzLlJEYXRhIikKbG9hZChmaWxlID0gImNhc2NhZGVfc3luZXJneV9iaW9tYXJrZXJzLlJEYXRhIikKYGBgCgojIyBPYnNlcnZlZCBzeW5lcmdpZXMgey19CgpFYWNoIG9mIHRoZSBjZWxsIGxpbmVzIHN0dWRpZWQgaGFzIGEgZGlmZmVyZW50IHNldCBvZiBvYnNlcnZlZCBzeW5lcmdpZXMgKGRydWcgY29tYmluYXRpb25zIHRoYXQgd2VyZSBmb3VuZCBzeW5lcmdpc3RpYyBhY3Jvc3MgYWxsIHRoZSBgciBsZW5ndGgoZHJ1Zy5jb21ib3MpYCB0ZXN0ZWQgb25lcykuIApJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgKip2aXN1YWxpemUgdGhlIGNlbGwgbGluZXMnIG9ic2VydmVkIHN5bmVyZ2llcyBhbmQgbWFyayB0aGUgc3luZXJnaWVzIHRoYXQgd2VyZSBhbHNvIHByZWRpY3RlZCBieSB0aGUgY2VsbC1zcGVjaWZpYyBtb2RlbHMgYW5kIHRoZSByYW5kb20tZ2VuZXJhdGVkIG9uZXMgKGluIG9uZSBjZWxsIGxpbmUgYXQgbGVhc3QpKiouIApGaXJzdCwgd2UgZ2V0IHRoZSBiaW9tYXJrZXJzIGZvciB0aGVzZSBzeW5lcmdpZXMgZnJvbSBlYWNoIGNlbGwgbGluZToKYGBge3IgVG90YWwgcHJlZGljdGVkIHN5bmVyZ2llcyBieSB0aGUgY2VsbC1zcGVjaWZpYyBtb2RlbHN9CnRvdGFsLnByZWRpY3RlZC5zeW5lcmdpZXMuY2VsbC5zcGVjaWZpYyA9CiAgdW5pcXVlKHVubGlzdChzYXBwbHkoY2VsbC5zcGVjaWZpYy5zeW5lcmd5LmFuYWx5c2lzLnJlcywgZnVuY3Rpb24oeCkgeyB4JHByZWRpY3RlZC5zeW5lcmdpZXN9KSkpCnRvdGFsLnByZWRpY3RlZC5zeW5lcmdpZXMuY2VsbC5zcGVjaWZpYy5udW0gPSBsZW5ndGgodG90YWwucHJlZGljdGVkLnN5bmVyZ2llcy5jZWxsLnNwZWNpZmljKQpgYGAKClRoZSBzYW1lIGZvciB0aGUgcmFuZG9tIG1vZGVsczoKYGBge3IgVG90YWwgcHJlZGljdGVkIHN5bmVyZ2llcyBieSB0aGUgcmFuZG9tIG1vZGVsc30KdG90YWwucHJlZGljdGVkLnN5bmVyZ2llcy5yYW5kb20gPSAKICB1bmlxdWUodW5saXN0KHNhcHBseShyYW5kb20uc3luZXJneS5hbmFseXNpcy5yZXMsIGZ1bmN0aW9uKHgpIHsgeCRwcmVkaWN0ZWQuc3luZXJnaWVzfSkpKQp0b3RhbC5wcmVkaWN0ZWQuc3luZXJnaWVzLnJhbmRvbS5udW0gPSBsZW5ndGgodG90YWwucHJlZGljdGVkLnN5bmVyZ2llcy5yYW5kb20pCmBgYAoKVGhlbiwgd2UgZ2V0IHRoZSBvYnNlcnZlZCBzeW5lcmdpZXMgZnJvbSBlYWNoIGNlbGwgbGluZSBpbiBhIGBkYXRhLmZyYW1lYDoKYGBge3IgT2JzZXJ2ZWQgc3luZXJnaWVzIHBlciBjZWxsIGxpbmV9Cm9ic2VydmVkLnN5bmVyZ2llcy5yZXMgPSBnZXRfb2JzZXJ2ZWRfc3luZXJnaWVzX3Blcl9jZWxsX2xpbmUoY2VsbC5saW5lLmRpcnMsIGRydWcuY29tYm9zKQoKIyByZW1vdmUgZHJ1ZyBjb21iaW5hdGlvbnMgd2hpY2ggYXJlIG5vdCBvYnNlcnZlZCBpbiBhbnkgb2YgdGhlIGNlbGwgbGluZXMKb2JzZXJ2ZWQuc3luZXJnaWVzLnJlcyA9IHBydW5lX2NvbHVtbnNfZnJvbV9kZihvYnNlcnZlZC5zeW5lcmdpZXMucmVzLCB2YWx1ZSA9IDApCgp0b3RhbC5vYnNlcnZlZC5zeW5lcmdpZXMgPSBjb2xuYW1lcyhvYnNlcnZlZC5zeW5lcmdpZXMucmVzKQp0b3RhbC5vYnNlcnZlZC5zeW5lcmdpZXMubnVtID0gbGVuZ3RoKHRvdGFsLm9ic2VydmVkLnN5bmVyZ2llcykKYGBgCgpMYXN0bHksIHdlIHZpc3VhbGl6ZSB0aGUgb2JzZXJ2ZWQgYW5kIHByZWRpY3RlZCBzeW5lcmdpZXMgZm9yIGFsbCBjZWxsIGxpbmVzIAppbiBvbmUgaGVhdG1hcDoKYGBge3IgT2JzZXJ2ZWQgc3luZXJnaWVzIGhlYXRtYXAsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA5LCBkcGkgPSAzMDAsIGNhY2hlID0gVFJVRX0KIyBjb2xvciB0aGUgY2VsbC1zcGVjaWZpYyBwcmVkaWN0ZWQgc3luZXJnaWVzCnByZWRpY3RlZC5zeW5lcmdpZXMuY29sb3JzID0gcmVwKCJibGFjayIsIHRvdGFsLm9ic2VydmVkLnN5bmVyZ2llcy5udW0pCm5hbWVzKHByZWRpY3RlZC5zeW5lcmdpZXMuY29sb3JzKSA9IHRvdGFsLm9ic2VydmVkLnN5bmVyZ2llcwpjb21tb24ucHJlZGljdGVkLnN5bmVyZ2llcyA9IGludGVyc2VjdCh0b3RhbC5wcmVkaWN0ZWQuc3luZXJnaWVzLmNlbGwuc3BlY2lmaWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsLnByZWRpY3RlZC5zeW5lcmdpZXMucmFuZG9tKQpjZWxsLnNwZWNpZmljLm9ubHkucHJlZGljdGVkLnN5bmVyZ2llcyA9IAogIHRvdGFsLnByZWRpY3RlZC5zeW5lcmdpZXMuY2VsbC5zcGVjaWZpY1shdG90YWwucHJlZGljdGVkLnN5bmVyZ2llcy5jZWxsLnNwZWNpZmljICVpbiUgdG90YWwucHJlZGljdGVkLnN5bmVyZ2llcy5yYW5kb21dCnJhbmRvbS5vbmx5LnByZWRpY3RlZC5zeW5lcmdpZXMgPSAKICB0b3RhbC5wcmVkaWN0ZWQuc3luZXJnaWVzLnJhbmRvbVshdG90YWwucHJlZGljdGVkLnN5bmVyZ2llcy5yYW5kb20gJWluJSB0b3RhbC5wcmVkaWN0ZWQuc3luZXJnaWVzLmNlbGwuc3BlY2lmaWNdCgpwcmVkaWN0ZWQuc3luZXJnaWVzLmNvbG9yc1t0b3RhbC5vYnNlcnZlZC5zeW5lcmdpZXMgJWluJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbW9uLnByZWRpY3RlZC5zeW5lcmdpZXNdID0gImJsdWUiCnByZWRpY3RlZC5zeW5lcmdpZXMuY29sb3JzW3RvdGFsLm9ic2VydmVkLnN5bmVyZ2llcyAlaW4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsLnNwZWNpZmljLm9ubHkucHJlZGljdGVkLnN5bmVyZ2llc10gPSAib3JhbmdlIgpwcmVkaWN0ZWQuc3luZXJnaWVzLmNvbG9yc1t0b3RhbC5vYnNlcnZlZC5zeW5lcmdpZXMgJWluJSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tLm9ubHkucHJlZGljdGVkLnN5bmVyZ2llc10gPSAicHVycGxlIgoKIyBkZWZpbmUgYSBjb2xvcmluZwpvYnMuc3luZXJnaWVzLmNvbC5mdW4gPSBjb2xvclJhbXAyKGMoMCwgMSksIGMoInJlZCIsICJncmVlbiIpKQoKb2JzZXJ2ZWQuc3luZXJnaWVzLmhlYXRtYXAgPSAKICBIZWF0bWFwKG1hdHJpeCA9IGFzLm1hdHJpeChvYnNlcnZlZC5zeW5lcmdpZXMucmVzKSwgCiAgICAgICAgICBjb2wgPSBvYnMuc3luZXJnaWVzLmNvbC5mdW4sCiAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiT2JzZXJ2ZWQgc3luZXJnaWVzIHBlciBjZWxsIGxpbmUiLAogICAgICAgICAgY29sdW1uX3RpdGxlX2dwID0gZ3Bhcihmb250c2l6ZSA9IDIwKSwKICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoY29sID0gcHJlZGljdGVkLnN5bmVyZ2llcy5jb2xvcnMpLAogICAgICAgICAgcm93X3RpdGxlID0gIkNlbGwgTGluZXMiLCByb3dfdGl0bGVfc2lkZSA9ICJsZWZ0IiwKICAgICAgICAgIHJvd19kZW5kX3NpZGUgPSAicmlnaHQiLCByb3dfbmFtZXNfc2lkZSA9ICJsZWZ0IiwKICAgICAgICAgIHJlY3RfZ3AgPSBncGFyKGNvbCA9ICJibGFjayIsIGx3ZCA9IDAuMyksCiAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QoYXQgPSBjKDEsIDApLCBsYWJlbHMgPSBjKCJZRVMiLCAiTk8iKSwgCiAgICAgICAgICAgIGNvbG9yX2JhciA9ICJkaXNjcmV0ZSIsIHRpdGxlID0gIk9ic2VydmVkIiwgZGlyZWN0aW9uID0gInZlcnRpY2FsIikpCgpsZ2QgPSBMZWdlbmQoYXQgPSBjKCJDZWxsLXNwZWNpZmljIiwgIlJhbmRvbSIsICJCb3RoIiksIHRpdGxlID0gIlByZWRpY3RlZCIsIAogICAgICAgICAgICAgbGVnZW5kX2dwID0gZ3BhcihmaWxsID0gYygib3JhbmdlIiwgInB1cnBsZSIsICJibHVlIikpKQoKZHJhdyhvYnNlcnZlZC5zeW5lcmdpZXMuaGVhdG1hcCwgIGhlYXRtYXBfbGVnZW5kX2xpc3QgPSBsaXN0KGxnZCksIAogICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAicmlnaHQiKQpgYGAKCjxkaXYgY2xhc3MgPSAiYmx1ZS1ib3giPgotIFRoZSAqY2VsbC1zcGVjaWZpYyogbW9kZWxzIHByZWRpY3RlZCBgciB0b3RhbC5wcmVkaWN0ZWQuc3luZXJnaWVzLmNlbGwuc3BlY2lmaWMubnVtYCAKb2YgdGhlIGByIHRvdGFsLm9ic2VydmVkLnN5bmVyZ2llcy5udW1gIG9ic2VydmVkIHN5bmVyZ2llcyBmb3VuZCBhY3Jvc3MgdGhlIApgciBsZW5ndGgoY2VsbC5saW5lcylgIGNlbGwgbGluZXMsIHdoZXJlYXMgdGhlICpyYW5kb20qIG1vZGVscyBwcmVkaWN0ZWQgCmByIHRvdGFsLnByZWRpY3RlZC5zeW5lcmdpZXMucmFuZG9tLm51bWAgb2YgdGhlIHRoZW0uClRodXMsIHRoZSAqKnRvdGFsIHRydWUgcG9zaXRpdmUgY292ZXJhZ2UgZm9yIGFsbCB0aGUgbW9kZWxzIGFjcm9zcyBhbGwgY2VsbCBsaW5lcyBpcyAKYHIgKGxlbmd0aCh1bmlvbih0b3RhbC5wcmVkaWN0ZWQuc3luZXJnaWVzLmNlbGwuc3BlY2lmaWMsIHRvdGFsLnByZWRpY3RlZC5zeW5lcmdpZXMucmFuZG9tKSkvdG90YWwub2JzZXJ2ZWQuc3luZXJnaWVzLm51bSkqMTAwYCUqKgotIE5vdGUgdGhhdCB0aGVyZSBleGlzdCBzeW5lcmdpZXMgd2hpY2ggd2VyZSBvYnNlcnZlZCBpbiBhbGwgY2VsbCBsaW5lcyAoYEFLLUJJYCwKYFBJLUQxYCkKLSBgQUstRzRgIGFuZCBgNVotRDFgIGFyZSBvYnNlcnZlZCBzeW5lcmdpZXMgdGhhdCBvbmx5IHRoZSBjZWxsLXNwZWNpZmljIG1vZGVscyBjb3VsZCBwcmVkaWN0LCB3aGVyZWFzIHRoZSBgRzItUDVgIGFuZCBgUEktRDRgIGFyZSBvYnNlcnZlZCBzeW5lcmdpZXMgdGhhdCBvbmx5IHRoZSByYW5kb20gbW9kZWxzIGNvdWxkIHByZWRpY3QuIFRoaXMgc2hvd3MgdXMgdGhhdCBhICoqY29tcGxpbWVudGFyeQphcHByb2FjaCoqIGlzIG5lZWRlZCB3aGVuIHNlYXJjaGluZyBmb3IgYmlvbWFya2VycyBhcyB0aGUgdHdvIGRpZmZlcmVudCBraW5kIG9mIAptb2RlbHMgKHRyYWluZWQgdG8gYSBzcGVjaWZpYyBhY3Rpdml0eSBzdGF0ZSBwcm9maWxlIHZzIHRyYWluZWQgdG8gcHJvbGlmZXJhdGlvbikKYWx0aG91Z2ggdGhleSBzaGFyZSBjb21tb24gdHJ1ZSBwb3NpdGl2ZXMgcmVnYXJkaW5nIHRoZSBzeW5lcmdpZXMgdGhleSBwcmVkaWN0LAp0aGVyZSBhcmUgYWxzbyBzeW5lcmdpZXMgb25seSBhIHNwZWNpZmljIGNsYXNzIG9mIG1vZGVscyBjb3VsZCBwcmVkaWN0LgotIFRoZSAkMyQgc3luZXJnaWVzIG9mIGludGVyZXN0LCBgQUstUERgLCBgUEQtUElgIGFuZCBgUEQtRzJgIHdlcmUgb2JzZXJ2ZWQgaW4gCnRoZSBgQTQ5OGAgY2VsbCBsaW5lIGFuZCBwcmVkaWN0ZWQgYnkgYm90aCB0aGUgY2VsbC1zcGVjaWZpYyBhbmQgcmFuZG9tIG1vZGVscy4KPC9kaXY+CgojIyBBSy1QRCBiaW9tYXJrZXJzIHstI2FrLXBkLWJpb21hcmtlcnN9CgpbQXMgb2JzZXJ2ZWQgYWJvdmVdKCNvYnNlcnZlZC1zeW5lcmdpZXMpLCB0aGUgYEFLLVBEYCBzeW5lcmd5IHdhcyBwcmVkaWN0ZWQgYnkgYm90aCB0aGUgKipjZWxsIHNwZWNpZmljKiogYW5kICoqcmFuZG9tIG1vZGVscyoqIGluIHRoZSBgQTQ5OGAgY2VsbCBsaW5lLgoKIyMjIENlbGwtc3BlY2lmaWMgKEE0OTgpIHstfQoKV2UgZ2V0IHRoZSBhdmVyYWdlIHN0YXRlIGFuZCBsaW5rIG9wZXJhdG9yIGRpZmZlcmVuY2VzIHBlciBuZXR3b3JrIG5vZGUgZm9yIHRoZSBgQTQ5OGAgY2VsbCBsaW5lIGZyb20gdGhlIGNlbGwtc3BlY2lmaWMgbW9kZWxzOgoKYGBge3IgQUstUEQgZGlmZiAoQ2VsbCBzcGVjaWZpYyBtb2RlbHMgLSBBNDk4KX0KQUsuUEQuYXZnLnN0YXRlLmRpZmYuY2VsbC5zcGVjaWZpYyA9IGNlbGwuc3BlY2lmaWMuc3luZXJneS5hbmFseXNpcy5yZXMkQTQ5OCRkaWZmLnN0YXRlLnN5bmVyZ2llcy5tYXRbIkFLLVBEIiwgXQpBSy5QRC5hdmcubGluay5kaWZmLmNlbGwuc3BlY2lmaWMgID0gY2VsbC5zcGVjaWZpYy5zeW5lcmd5LmFuYWx5c2lzLnJlcyRBNDk4JGRpZmYubGluay5zeW5lcmdpZXMubWF0WyJBSy1QRCIsIF0KYGBgCgpXZSBidWlsZCB0aGUgbmV0d29yayBmcm9tIHRoZSB0b3BvbG9neSBmaWxlOgoKYGBge3IgTmV0d29yayBidWlsZGluZ30KdG9wb2xvZ3kuZmlsZSA9IHBhc3RlMChnZXR3ZCgpLCAiL3RvcG9sb2d5IikKbmV0ID0gY29uc3RydWN0X25ldHdvcmsodG9wb2xvZ3kuZmlsZSA9IHRvcG9sb2d5LmZpbGUsIG1vZGVscy5kaXIgPSAgcGFzdGUwKGdldHdkKCksICIvQUdTL21vZGVscyIpKQoKIyBhIHN0YXRpYyBsYXlvdXQgZm9yIHBsb3R0aW5nIHRoZSBzYW1lIG5ldHdvcmsgYWx3YXlzCmNvb3JkaW5hdGVzLmZpbGUgPSBwYXN0ZTAoZ2V0d2QoKSwgIi9uZXR3b3JrX3h5X2Nvb3JkaW5hdGVzIikKbmljZS5sYXlvdXQgPSBhcy5tYXRyaXgocmVhZC50YWJsZShjb29yZGluYXRlcy5maWxlKSkKYGBgCgojIyMjIEFjdGl2aXR5IHN0YXRlIGJpb21hcmtlcnMgey19CgpXZSB3aWxsIG5vdyB2aXN1YWxpemUgdGhlIG5vZGVzIGF2ZXJhZ2Ugc3RhdGUgZGlmZmVyZW5jZXMgaW4gYSBuZXR3b3JrIGdyYXBoLiBOb3RlIHRoYXQgdGhlICoqZ29vZCBtb2RlbHMqKiBhcmUgdGhvc2UgdGhhdCBwcmVkaWN0ZWQgdGhlIGBBSy1QRGAgZHJ1ZyBjb21iaW5hdGlvbiB0byBiZSAqc3luZXJnaXN0aWMqIGFuZCB3ZXJlIGNvbnRyYXN0ZWQgdG8gdGhvc2UgdGhhdCBwcmVkaWN0ZWQgaXQgdG8gYmUgKmFudGFnb25pc3RpYyogKCoqYmFkIG1vZGVscyoqKS4gVGhlIG51bWJlciBvZiBtb2RlbHMgaW4gZWFjaCBjYXRlZ29yeSB3ZXJlOgoKYGBge3IgQ291bnQgZ29vZCB2cyBiYWQgbW9kZWxzIGZvciBBSy1QRCBzeW5lcmd5IChjZWxsLXNwZWNpZmljIG1vZGVscyksIHJlc3VsdHM9J2FzaXMnfQptb2RlbC5wcmVkaWN0aW9ucyA9IG1vZGVsLnByZWRpY3Rpb25zLnBlci5jZWxsLmxpbmVbWyJBNDk4Il1dCm1vZGVscy5zdGFibGUuc3RhdGUgPSBtb2RlbHMuc3RhYmxlLnN0YXRlLnBlci5jZWxsLmxpbmVbWyJBNDk4Il1dCmRydWcuY29tYiA9ICJBSy1QRCIKCmdvb2QubW9kZWxzLm51bSA9IHN1bShtb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0gPT0gMSAmICFpcy5uYShtb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0pKQojIHVuaXF1ZSBnb29kIG1vZGVscwojIG1vZGVscy5saW5rLm9wZXJhdG9yID0gbW9kZWxzLmxpbmsub3BlcmF0b3JzLnBlci5jZWxsLmxpbmUkQTQ5OAojIG5yb3codW5pcXVlKG1vZGVscy5saW5rLm9wZXJhdG9yW21vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSA9PSAxICYgIWlzLm5hKG1vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSksIF0pKQpiYWQubW9kZWxzLm51bSAgPSBzdW0obW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdID09IDAgJiAhaXMubmEobW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdKSkKCnByZXR0eV9wcmludF9zdHJpbmcocGFzdGUwKCJOdW1iZXIgb2YgJ2dvb2QnIG1vZGVscyAoQUstUEQgc3luZXJnaXN0aWMpIGluIHRoZSBBNDk4IGNlbGwgbGluZTogIiwgZ29vZC5tb2RlbHMubnVtKSkKcHJldHR5X3ByaW50X3N0cmluZyhwYXN0ZTAoIk51bWJlciBvZiAnYmFkJyBtb2RlbHMgKEFLLVBEIGFudGFnb25pc3RpYykgaW4gdGhlIEE0OTggY2VsbCBsaW5lOiAiLCBiYWQubW9kZWxzLm51bSkpCmBgYAoKYGBge3IgQUstUEQgYWN0aXZpdHkgc3RhdGUgYmlvbWFya2VycyAoQ2VsbCBzcGVjaWZpYyBtb2RlbHMgLSBBNDk4KSwgY2FjaGUgPSBUUlVFLCBmaWcuYWxpZ249J2NlbnRlcid9CnBsb3RfYXZnX3N0YXRlX2RpZmZfZ3JhcGgobmV0LCBkaWZmID0gQUsuUEQuYXZnLnN0YXRlLmRpZmYuY2VsbC5zcGVjaWZpYywgCiAgbGF5b3V0ID0gbmljZS5sYXlvdXQsIHRpdGxlID0gIkFLLVBEIGFjdGl2aXR5IHN0YXRlIGJpb21hcmtlcnMgKENlbGwgc3BlY2lmaWMgbW9kZWxzIC0gQTQ5OCkiKQpgYGAKClRodXMsIHdlIGNhbiBpZGVudGlmeSB0aGUgYWN0aXZlIHN0YXRlIGJpb21hcmtlcnM6CgpgYGB7ciBBSy1QRCBhY3RpdmUgc3RhdGUgYmlvbWFya2VycyAoQ2VsbCBzcGVjaWZpYyBtb2RlbHMgLSBBNDk4KSwgcmVzdWx0cyA9ICdhc2lzJ30KQUsuUEQuYWN0aXZlLmJpb21hcmtlcnMgPSBBSy5QRC5hdmcuc3RhdGUuZGlmZi5jZWxsLnNwZWNpZmljW0FLLlBELmF2Zy5zdGF0ZS5kaWZmLmNlbGwuc3BlY2lmaWMgPiAwLjddCnByZXR0eV9wcmludF92ZWN0b3JfbmFtZXMoQUsuUEQuYWN0aXZlLmJpb21hcmtlcnMpCmBgYAoKU28sIHRoZSBgQUstUERgIHN5bmVyZ3kgbWFuaWZlc3RzIGluIGNhbmNlciBjZWxsIG1vZGVscyB0aGF0IGhhdmUgdGhlIGBFUktfZmAgZmFtaWx5IGxvZ2ljYWwgbm9kZSBpbiBhbiAqKmFjdGl2ZSoqIHN0YXRlLgpUaGUgKipNQVBLLUVSSyBzaWduYWxpbmcgcGF0aHdheSoqIGhhcyBiZWVuIHN0dWRpZWQgYSBsb3QgYW5kIGhhcyBiZWVuIGZvdW5kIHRvIGJlICoqb3ZlcmV4cHJlc3NlZC9oYXZlIGluY3JlYXNlZCBhY3Rpdml0eSoqIGluIGNhbmNlcnMgYW5kIGFzIHN1Y2ggY2FuY2VyIHRyZWF0bWVudHMgdGhhdCBpbmNsdWRlIHRoZSBpbmhpYml0aW9uIG9mIHRoYXQgcGF0aHdheSBhcmUgZm91bmQgdG8gYmUgbW9zdCBiZW5lZmljaWFsLgoKUGFwZXIgZXZpZGVuY2UgZm9yICoqYEVSS2Agb3ZlcmV4cHJlc3Npb24qKiBpbiBjYW5jZXIgKHRoZXJlIGFyZSBtYW55KTogCgotIFtSb2xlcyBvZiB0aGUgUmFmL01FSy9FUksgcGF0aHdheSBpbiBjZWxsIGdyb3d0aCwgbWFsaWduYW50IHRyYW5zZm9ybWF0aW9uIGFuZCBkcnVnIHJlc2lzdGFuY2VdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouYmJhbWNyLjIwMDYuMTAuMDAxKQotIFtUaGUgUmFzL1JhZi9NRUsvRVJLIHNpZ25hbGluZyBwYXRod2F5IGFuZCBpdHMgcm9sZSBpbiB0aGUgb2NjdXJyZW5jZSBhbmQgZGV2ZWxvcG1lbnQgb2YgSENDIChSZXZpZXcpXShodHRwczovL2RvaS5vcmcvMTAuMzg5Mi9vbC4yMDE2LjUxMTApCi0gW1RhcmdldGluZyBFUkssIGFuIEFjaGlsbGVzJyBIZWVsIG9mIHRoZSBNQVBLIHBhdGh3YXksIGluIGNhbmNlciB0aGVyYXB5XShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmFwc2IuMjAxOC4wMS4wMDgpCi0gW0VSSyBpcyBhIFBpdm90YWwgUGxheWVyIG9mIENoZW1vLUltbXVuZS1SZXNpc3RhbmNlIGluIENhbmNlcl0oaHR0cHM6Ly9kb2kub3JnLzEwLjMzOTAvaWptczIwMTAyNTA1KQoKVGhlIGluaGliaXRlZCBzdGF0ZSBiaW9tYXJrZXJzIGFyZToKCmBgYHtyIEFLLVBEIGluaGliaXRlZCBzdGF0ZSBiaW9tYXJrZXJzIChDZWxsIHNwZWNpZmljIG1vZGVscyAtIEE0OTgpLCByZXN1bHRzID0gJ2FzaXMnfQpBSy5QRC5pbmhpYml0ZWQuYmlvbWFya2VycyA9IEFLLlBELmF2Zy5zdGF0ZS5kaWZmLmNlbGwuc3BlY2lmaWNbQUsuUEQuYXZnLnN0YXRlLmRpZmYuY2VsbC5zcGVjaWZpYyA8IC0wLjddCnByZXR0eV9wcmludF92ZWN0b3JfbmFtZXMoQUsuUEQuaW5oaWJpdGVkLmJpb21hcmtlcnMpCmBgYAoKPGRpdiBjbGFzcz0iZ3JlZW4tYm94Ij4KV2Ugd2lsbCBkZW1vbnN0cmF0ZSB0aGF0IHRoZSBgUFRQTjExYCBhbmQgYEdBQl9mYCBpbmhpYml0ZWQgYmlvbWFya2VycyBhcmUgYSBkaXJlY3QgY29uc2VxdWVuY2Ugb2YgdGhlIG92ZXJleHByZXNzaW9uIG9mIGBFUktfZmA6CjwvZGl2Pgo8L2JyPgoKSWYgd2UgY2hlY2sgdGhlIGxvZ2ljYWwgZXF1YXRpb25zIHJlbGF0ZWQgdG8gdGhlIGFib3ZlIGJpb21hcmtlcnMgd2Ugc2VlIHRoYXQ6CgpgYGB7ciBwcmludCBlcXVhdGlvbnMsIHJlc3VsdHM9J2FzaXMnfQpwcmV0dHlfcHJpbnRfc3RyaW5nKCJFUktfZiAqPSAgKCAgTUVLX2YgKSBBTkQvT1IgTk9UICAoICAoIERVU1A2ICkgIG9yIFBQUDFDQSApIikKcHJldHR5X3ByaW50X3N0cmluZygiR0FCX2YgKj0gICggIEdSQjIgKSBBTkQvT1IgTk9UICggRVJLX2YgKSIpCnByZXR0eV9wcmludF9zdHJpbmcoIlBUUE4xMSAqPSAgKCAgR0FCX2YgKSIpCmBgYAoKU28sIHByZXR0eSBtdWNoIGlmIHRoZSBgR0FCX2ZgIG5vZGUgaXMgbW9yZSBpbmhpYml0ZWQgaW4gdGhlIG1vZGVscyB0aGF0IHByZWRpY3RlZCB0aGUgYEFLLVBEYCBzeW5lcmd5IChnb29kIG1vZGVscyksIHRoZW4gYFBUUE4xMWAgaXMgYWxzbyBhcyB3ZWxsLiAKQWxzbyB0aGUgYXZlcmFnZSBhY3Rpdml0eSBkaWZmZXJlbmNlIG9mIHRoZSBgR1JCMmAgbm9kZSBpcyBgciBBSy5QRC5hdmcuc3RhdGUuZGlmZi5jZWxsLnNwZWNpZmljWyJHUkIyIl1gLCB3aGljaCBtYWtlcyB0aGUgYEdBQl9mYCBub2RlIG1vcmUgaW5oaWJpdGVkIGluIHRoZSBnb29kIG1vZGVscyBzaW5jZSBpdCdzIGFjdGl2aXR5IGlzIG1vc3RseSBkZXBlbmRlbnQgb24gdGhlIGBFUktfZmAgbm9kZSwgd2hpY2ggaXMgbW9zdGx5IG92ZXJleHByZXNzZWQgaW4gdGhlIGdvb2QgbW9kZWxzLgpBbGwgaW4gYWxsLCB0aGUgb3ZlcmV4cHJlc3Npb24gb2YgYEVSS19mYCBpcyB3aGF0IGNhdXNlcyB0aGUgdHdvIG90aGVyIGluaGliaXRlZCBiaW9tYXJrZXJzLgoKPC9icj4KCiMjIyMgTGluayBvcGVyYXRvciBiaW9tYXJrZXJzIHstfQoKV2UgdmlzdWFsaXplIHRoZSBub2RlcyBhdmVyYWdlIGxpbmsgb3BlcmF0b3IgZGlmZmVyZW5jZXMgaW4gYSBuZXR3b3JrIGdyYXBoOgoKYGBge3IgQUstUEQgbGluayBvcGVyYXRvciBiaW9tYXJrZXJzIChDZWxsIHNwZWNpZmljIG1vZGVscyAtIEE0OTgpLCBjYWNoZSA9IFRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGxvdF9hdmdfbGlua19vcGVyYXRvcl9kaWZmX2dyYXBoKG5ldCwgZGlmZiA9IEFLLlBELmF2Zy5saW5rLmRpZmYuY2VsbC5zcGVjaWZpYywgCiAgbGF5b3V0ID0gbmljZS5sYXlvdXQsIHRpdGxlID0gIkFLLVBEIGxpbmsgb3BlcmF0b3IgYmlvbWFya2VycyAoQ2VsbCBzcGVjaWZpYyBtb2RlbHMgLSBBNDk4KSIpCmBgYAoKU28sIHRoZSBtb2RlbHMgdGhhdCBwcmVkaWN0ZWQgdGhlIGBBSy1QRGAgc3luZXJneSwgaGFkIHRoZSAqKk9SIE5PVCoqIGFzIGEgbGluayBvcGVyYXRvciBpbiB0aGUgYm9vbGVhbiBlcXVhdGlvbiB0aGF0IGhhcyB0aGUgYEVSS19mYCBub2RlIGFzIHRhcmdldCwgaS5lLiBgRVJLX2YgKj0gICggIE1FS19mICkgT1IgTk9UICAoICAoIERVU1A2ICkgIG9yIFBQUDFDQSApYCBpbnN0ZWFkIG9mIGBFUktfZiAqPSAgKCAgTUVLX2YgKSBBTkQgTk9UICggKCBEVVNQNiApICBvciBQUFAxQ0EgKWAuIApUaGUgZGlmZmVyZW5jZSBpbiB0aGUgcmVzdWx0IG9mIHRoZSBsb2dpY2FsIGVxdWF0aW9uIGNhbiBiZSBzZWVuIGluIHRoZSBuZXh0IHR3byB0cnV0aCB0YWJsZXMgd2hlcmUgdGhlIHVzZSBvZiB0aGUgKipPUiBOT1QgbGluayBvcGVyYXRvcioqIG1ha2VzIHRoZSBlbmQgdHJ1dGggdmFsdWUgbW9yZSAqZmxleGlibGUqIGluIHRoZSBzZW5zZSB0aGF0IGEgbG90IG1vcmUgbW9yZSBUUlVFIHZhbHVlcyBhcmUgcG9zc2libGUsIHNpbmNlIHRoZSBhY3RpdmF0aW5nIHJlZ3VsYXRvciBgTUVLX2ZgICh3aGljaCBpcyB0aGUgYFBEYCBkcnVnJ3MgdGFyZ2V0KSBoYXMgbW9yZSB3ZWlnaHQgaW4gdGhlIG91dGNvbWU6CgohW10oaW1nL3RydXRoX3RhYmxlX2FuZC5zdmcpIVtdKGltZy90cnV0aF90YWJsZV9vci5zdmcpCgo8ZGl2IGNsYXNzPSJvcmFuZ2UtYm94Ij4KSWYgeW91IHRoaW5rIGl0IGluIHJldmVyc2UgdGVybXMsIGluIGNhbmNlciBtb2RlbHMgaW4gd2hpY2ggdGhlIGBFUktfZmAgZXF1YXRpb24gaXMgbW9zdGx5IGJhc2VkIG9uIGFuIGBBTkQgTk9UYCBsaW5rIG9wZXJhdG9yIGxvZ2ljIGFuZCBoYXZlIHRoZSBgRVJLX0ZgIG5vZGUgYXMgKmluYWN0aXZlKiAoYWxzbyBmcm9tIHRoZSBlcXVhdGlvbiBgRFVTUDYgKj0gICggICggIG1UT1JDMV9jICkgIG9yIEVSS19mIClgLCBgRFVTUDZgIHdpbGwgYWxzbyBiZSBpbmFjdGl2ZSksIHRoZSBgTUVLX2ZgIG5vZGUgd2lsbCBhbHNvIGJlIG1vc3RseSAqKmluYWN0aXZlKiogKCQyJCBvdXQgb2YgJDMkIGNhc2VzIGluIHRoZSB0YWJsZSBhYm92ZTogMXN0LCAybmQgYW5kIDR0aCByb3cpIC0gaXQgY2FuIGJlIHNlZW4gYXMgYSBub2RlIHRoYXQgZG9lcyBub3QgcGxheSBhbiBpbXBvcnRhbnQgcm9sZSBpbiB0aGUgYWN0aXZpdHkgb3V0Y29tZSBvZiAqKkVSS19mKiogLSBhbmQgc28gYnkgaW5oaWJpdGluZyBpdCBmdXJ0aGVyIHdpdGggdGhlIGBQRGAgZHJ1ZyB3aWxsIG5vdCBicmluZyBhbnkgc2lnbmlmaWNhbnQgYmVuZWZpdCBmb3IgY2FuY2VyIHRyZWF0bWVudC4KPC9kaXY+CjwvYnI+Cgo8ZGl2IGNsYXNzID0gImJsdWUtYm94IiBpZD0iVGV4dDAiPgpUaHVzLCBpbiBjYW5jZXIgYm9vbGVhbiBtb2RlbHMgd2hlcmUgdGhlIGBFUktfZmAgbm9kZSBpcyBvdmVyZXhwcmVzc2VkIGFuZCB0aGUgYE1FS19mYCBsb2dpY2FsIG5vZGUgaXMgaXQncyBtb3N0IGNydWNpYWwgcmVndWxhdG9yLCBpbmhpYml0aW5nIGJvdGggdGhlICoqTUFQSy9FUksgcGF0aHdheSoqIChkcnVnIGBQRGApIGFuZCB0aGUgKipBS1QgcGF0aHdheSoqIChkcnVnIGBBS2ApIGlzIGEgc3luZXJnaXN0aWMgY29tYmluYXRpb24gZm9yIGNhbmNlciB0cmVhdG1lbnQuCjwvZGl2Pgo8L2JyPgoKIyMjIyBTeW5lcmd5IHN1YnNldHMgYW5hbHlzaXMgey19CgpJdCB3aWxsIGJlIGludGVyZXN0aW5nIHRvIGZpbmQgKiphbGwgdGhlIHBvc3NpYmxlIHN5bmVyZ3kgc2V0cyBhbmQgc3Vic2V0cyB0aGF0IGluY2x1ZGUgdGhlIGBBSy1QRGAgYXMgdGhlIGV4dHJhIHN5bmVyZ3kqKi4gClVzaW5nIHRoZXNlIHN5bmVyZ3kgc2V0cywgbW9kZWxzIHRoYXQgcHJlZGljdCBhIHNldCBvZiBzeW5lcmdpZXMgd2lsbCBiZSBjb250cmFzdGVkIHRvIG1vZGVscyB0aGF0IHByZWRpY3RlZCB0aGUgc2FtZSBzZXQgd2l0aCB0aGUgYWRkaXRpb24gb2YgdGhlIGV4dHJhIGBBSy1QRGAgc3luZXJneS4gVGh1cyB3ZSBjb3VsZCBmaW5kICoqc3luZXJneSBiaW9tYXJrZXJzIHRoYXQgYWxsb3cgYWxyZWFkeSBnb29kIHByZWRpY3RpbmcgbW9kZWxzIHRvIHByZWRpY3QgdGhlIGFkZGl0aW9uYWwgc3luZXJneSBvZiBpbnRlcmVzdCoqLgpUaGlzIGludmVzdGlnYXRpb24gd2lsbCBhbGxvdyB1cyB0aHVzIHRvIHJlZmluZSB0aGUgYWN0aXZpdHkgc3RhdGUgYW5kIGxpbmsgb3BlcmF0b3IgYmlvbWFya2VycyB3ZSBmb3VuZCBhYm92ZS4KCldlIGZpcnN0IGNvbnN0cnVjdCB0d28gbWF0cmljZXM6IGluIHRoZSBmaXJzdCwgKiplYWNoIHJvdyBpcyBhIHNldCB2cyBzdWJzZXQgYXZlcmFnZSBhY3Rpdml0eSBkaWZmZXJlbmNlIHZlY3RvciBvZiB0aGUgbmV0d29yayBub2RlcyoqLCB3aGlsZSBvbiB0aGUgc2Vjb25kICoqZWFjaCByb3cgaXMgYSBzZXQgdnMgc3Vic2V0IGF2ZXJhZ2UgbGluayBvcGVyYXRvciBkaWZmZXJlbmNlIHZlY3RvciBvZiB0aGUgbmV0d29yayBub2RlcyoqOgpgYGB7ciBTeW5lcmd5IFN1YnNldHMgQ29tcGFyaXNvbiAoQ2VsbC1zcGVjaWZpYyAtIEE0OTgpLCBjYWNoZT1UUlVFfQptb2RlbC5wcmVkaWN0aW9ucyA9IG1vZGVsLnByZWRpY3Rpb25zLnBlci5jZWxsLmxpbmUkQTQ5OAptb2RlbHMuc3RhYmxlLnN0YXRlID0gbW9kZWxzLnN0YWJsZS5zdGF0ZS5wZXIuY2VsbC5saW5lJEE0OTgKbW9kZWxzLmxpbmsub3BlcmF0b3IgPSBtb2RlbHMubGluay5vcGVyYXRvcnMucGVyLmNlbGwubGluZSRBNDk4CiAgCnJlcyA9IGdldF9zeW5lcmd5X2NvbXBhcmlzb25fc2V0cyhjZWxsLnNwZWNpZmljLnN5bmVyZ3kuYW5hbHlzaXMucmVzJEE0OTgkc3luZXJneS5zdWJzZXQuc3RhdHMpCkFLLlBELnJlcyA9IHJlcyAlPiUgZmlsdGVyKHN5bmVyZ2llcyA9PSAiQUstUEQiKQoKZGlmZi5zdGF0ZS5saXN0ID0gbGlzdCgpCmRpZmYubGluay5saXN0ID0gbGlzdCgpCmZvciAoaSBpbiAxOm5yb3coQUsuUEQucmVzKSkgewogIHN5bmVyZ3kuc2V0ICAgID0gQUsuUEQucmVzW2ksIDJdCiAgc3luZXJneS5zdWJzZXQgPSBBSy5QRC5yZXNbaSwgM10KICAKICBzeW5lcmd5LnNldC5zdHIgICAgPSB1bmxpc3Qoc3Ryc3BsaXQoeCA9IHN5bmVyZ3kuc2V0LCBzcGxpdCA9ICIsIikpCiAgc3luZXJneS5zdWJzZXQuc3RyID0gdW5saXN0KHN0cnNwbGl0KHggPSBzeW5lcmd5LnN1YnNldCwgc3BsaXQgPSAiLCIpKQogIAogICMgY291bnQgbW9kZWxzCiAgc3luZXJneS5zZXQubW9kZWxzLm51bSA9IGNvdW50X21vZGVsc190aGF0X3ByZWRpY3Rfc3luZXJnaWVzKHN5bmVyZ3kuc2V0LnN0ciwgbW9kZWwucHJlZGljdGlvbnMpCiAgc3luZXJneS5zdWJzZXQubW9kZWxzLm51bSA9IGNvdW50X21vZGVsc190aGF0X3ByZWRpY3Rfc3luZXJnaWVzKHN5bmVyZ3kuc3Vic2V0LnN0ciwgbW9kZWwucHJlZGljdGlvbnMpCiAgCiAgIyBpZiB0b28gc21hbGwgbnVtYmVyIG9mIG1vZGVscywgc2tpcCB0aGUgZGlmZiB2ZWN0b3IKICBpZiAoKHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0gPD0gMykgfCAoc3luZXJneS5zZXQubW9kZWxzLm51bSA8PSAzKSkgCiAgICBuZXh0CiAgCiAgIyBnZXQgdGhlIGRpZmYKICBkaWZmLnN0YXRlLmFrLnBkID0gZ2V0X2F2Z19hY3Rpdml0eV9kaWZmX2Jhc2VkX29uX3N5bmVyZ3lfc2V0X2NtcChzeW5lcmd5LnNldC5zdHIsIHN5bmVyZ3kuc3Vic2V0LnN0ciwgbW9kZWwucHJlZGljdGlvbnMsIG1vZGVscy5zdGFibGUuc3RhdGUpCiAgZGlmZi5saW5rLmFrLnBkID0gZ2V0X2F2Z19saW5rX29wZXJhdG9yX2RpZmZfYmFzZWRfb25fc3luZXJneV9zZXRfY21wKHN5bmVyZ3kuc2V0LnN0ciwgc3luZXJneS5zdWJzZXQuc3RyLCBtb2RlbC5wcmVkaWN0aW9ucywgbW9kZWxzLmxpbmsub3BlcmF0b3IpCiAgZGlmZi5zdGF0ZS5saXN0W1twYXN0ZTAoc3luZXJneS5zZXQsICIgdnMgIiwgc3luZXJneS5zdWJzZXQpXV0gPSBkaWZmLnN0YXRlLmFrLnBkCiAgZGlmZi5saW5rLmxpc3RbW3Bhc3RlMChzeW5lcmd5LnNldCwgIiB2cyAiLCBzeW5lcmd5LnN1YnNldCldXSA9IGRpZmYubGluay5hay5wZAp9CgpkaWZmLnN0YXRlLm1hdCA9IGRvLmNhbGwocmJpbmQsIGRpZmYuc3RhdGUubGlzdCkKZGlmZi5saW5rLm1hdCA9IGRvLmNhbGwocmJpbmQsIGRpZmYubGluay5saXN0KQpgYGAKCjxkaXYgaWQ9IlRhYmxlMSI+PC9kaXY+CmBgYHtyIFRhYmxlMSwgY2FjaGU9VFJVRX0KY2FwdGlvbi50aXRsZS4xID0gIlRhYmxlIDE6IEF2ZXJhZ2UgYWN0aXZpdHkgZGlmZmVyZW5jZSB2YWx1ZXMgYWNyb3NzIGFsbCBzeW5lcmd5IHNldCBjb21wYXJpc29ucyAoQUstUEQpIgpkYXRhdGFibGUoZGF0YSA9IGRpZmYuc3RhdGUubWF0WywgYygiU1JDIiwgIkNTSyIsICJNRUtfZiIsICJTVEFUMSIsICJQVFBONiIpXSwgb3B0aW9ucyA9IGxpc3QoCiAgICBzZWFyY2hpbmcgPSBGQUxTRSwgcGFnZUxlbmd0aCA9IDUsIGxlbmd0aE1lbnUgPSBjKDUsIDEwKSksCiAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKGNhcHRpb24udGl0bGUuMSwgc3R5bGU9ImNvbG9yOiNkZDQ4MTQ7IGZvbnQtc2l6ZTogMThweCIpKSAlPiUgCiAgZm9ybWF0Um91bmQoMTpuY29sKGRpZmYuc3RhdGUubWF0KSwgZGlnaXRzID0gMykKYGBgCgo8ZGl2IGlkPSJUYWJsZTIiPjwvZGl2PgpgYGB7ciBUYWJsZTIsIGNhY2hlPVRSVUV9CmNhcHRpb24udGl0bGUuMiA9ICJUYWJsZSAyOiBBdmVyYWdlIGxpbmsgb3BlcmF0b3IgZGlmZmVyZW5jZSB2YWx1ZXMgYWNyb3NzIGFsbCBzeW5lcmd5IHNldCBjb21wYXJpc29ucyAoQUstUEQpIgpkYXRhdGFibGUoZGF0YSA9IGRpZmYubGluay5tYXRbLCBjKCJTUkMiLCAiUkFDX2YiLCAiTUVLX2YiLCAiU1RBVDEiLCAiUFRFTiIpXSwgb3B0aW9ucyA9IGxpc3QoCiAgICBzZWFyY2hpbmcgPSBGQUxTRSwgcGFnZUxlbmd0aCA9IDUsIGxlbmd0aE1lbnUgPSBjKDUsIDEwKSksCiAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKGNhcHRpb24udGl0bGUuMiwgc3R5bGU9ImNvbG9yOiNkZDQ4MTQ7IGZvbnQtc2l6ZTogMThweCIpKSAlPiUgCiAgZm9ybWF0Um91bmQoMTpuY29sKGRpZmYubGluay5tYXQpLCBkaWdpdHMgPSAzKQpgYGAKClVzaW5nIHRoZSBtYXRyaXggZnJvbSBbVGFibGUgMV0oI1RhYmxlMSkgKHdoZXJlIHdlIHNob3cganVzdCAkNSQgbm9kZXMpLCB3ZSBjb3VudCBwZXIgbmV0d29yayBub2RlICoqdGhlIG51bWJlciBvZiB0aW1lcyB0aGF0IHRoZSBub2RlJ3MgYXZlcmFnZSBhY3Rpdml0eSBkaWZmZXJlbmNlIHZhbHVlIGhhcyBzdXJwYXNzZWQgYSBzcGVjaWZpZWQgdGhyZXNob2xkKiogLSBpLmUuIHRoZSBudW1iZXIgb2YgdGltZXMgaXQgaGFzIGJlZW4gZm91bmQgYXMgaW1wb3J0YW50IChhIGJpb21hcmtlcikgYWNyb3NzIGFsbCB0aGUgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgKHNvIHRoZSBtb3JlIHRoZSBiZXR0ZXIpOgpgYGB7ciBBY3Rpdml0eSBTdGF0ZSBCaW9tYXJrZXIgY29udGluZ2VuY3kgdGFibGUgYWNyb3NzIGFsbCBjb21wYXJpc29uIHNldHMgKEFLLVBEIC0gQ2VsbC1zcGVjaWZpYyksIHJlc3VsdHM9J2FzaXMnfQp0aHJlc2hvbGQgPSAwLjgKYmlvbWFya2VyLnN0YXRlLm1hdCA9IGJpbmFyaXplX3RvX3RocmVzKG1hdCA9IGRpZmYuc3RhdGUubWF0LCB0aHJlc2hvbGQpCmJpb21hcmtlci5zdGF0ZS5jb3VudHMgPSBjb2xTdW1zKGJpb21hcmtlci5zdGF0ZS5tYXQpCnByZXR0eV9wcmludF92ZWN0b3JfbmFtZXNfYW5kX3ZhbHVlcyh0YWJsZShiaW9tYXJrZXIuc3RhdGUuY291bnRzKSkKYGBgCgpUaGUgYWJvdmUgbWVhbnMgdGhhdCB0aGVyZSB3ZXJlICQxNDEkIG5vZGVzIHRoYXQgd2VyZSAqemVybyogdGltZXMgZm91bmQgYXMgKiphY3Rpdml0eSBzdGF0ZSBiaW9tYXJrZXJzKiogYWNyb3NzIGFsbCBzeW5lcmd5IHNldCBjb21wYXJpc29ucywgKipvbmUgdGhhdCB3YXMgZm91bmQgb25jZSBhbmQgJDIkIHRoYXQgd2VyZSBmb3VuZCAkMTEkIHRpbWVzIChvdXQgb2YgYSB0b3RhbCBvZiBgciBucm93KGJpb21hcmtlci5zdGF0ZS5tYXQpYCkqKi4gVGhlc2Ugbm9kZXMgYXJlOgoKYGBge3IgRXh0cmEgYWN0aXZpdHkgc3RhdGUgYmlvbWFya2VycyAoQ2VsbC1zcGVjaWZpYyAtIEFLLVBEKSwgcmVzdWx0cz0nYXNpcyd9CnByZXR0eV9wcmludF92ZWN0b3JfbmFtZXMoYmlvbWFya2VyLnN0YXRlLmNvdW50c1tiaW9tYXJrZXIuc3RhdGUuY291bnRzID09IDExXSkKYGBgCgpJZiB3ZSB2aXN1YWxpemUgdGhlICoqYXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmZXJlbmNlIGFjcm9zcyBhbGwgc3luZXJneSBjb21wYXJpc29uIHNldHMqKiBmcm9tIFtUYWJsZSAxXSgjVGFibGUxKSB3ZSBjYW4gc2VlIHRoYXQgdGhlIHByZXZpb3VzICQyJCBub2RlcyBhcmUgbW9yZSBwcm9ub3VuY2VkOgoKYGBge3IgQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChBSy1QRCksIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGxvdF9hdmdfc3RhdGVfZGlmZl9ncmFwaChuZXQsIGRpZmYgPSBjb2xNZWFucyhkaWZmLnN0YXRlLm1hdCksIGxheW91dCA9IG5pY2UubGF5b3V0LCAKICB0aXRsZSA9ICJBdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmYgYWNyb3NzIGFsbCBzeW5lcmd5IHN1YnNldHMgKEFLLVBEKSIpCmBgYAoKV2UgYWxzbyB2aXN1YWxpemUgdGhlICoqbWVkaWFuIGFjdGl2aXR5IGRpZmZlcmVuY2UgcGVyIG5vZGUqKiBmcm9tIFtUYWJsZSAxXSgjVGFibGUxKSB1c2luZyBhIGBkb3RjaGFydGAsIHdoZXJlIHdlIGhhdmUgZXhjbHVkZWQgdGhlIG5vZGVzIHRoYXQgaGF2ZSBhbiBhYnNvbHV0ZSBtZWRpYW4gYWN0aXZpdHkgZGlmZmVyZW5jZSBvZiAkMC4wNSQgb3IgbGVzczoKYGBge3IgTWVkaWFuIGFjdGl2aXR5IHN0YXRlIGRpZmYgYWNyb3NzIGFsbCBzeW5lcmd5IHN1YnNldHMgKEFLLVBEKSAtIGRvdGNoYXJ0LCBjYWNoZT1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcid9CmRmID0gYXMuZGF0YS5mcmFtZShhcHBseShkaWZmLnN0YXRlLm1hdCwgMiwgbWVkaWFuKSkKY29sbmFtZXMoZGYpID0gIm1lZGlhbi5zdGF0ZS5kaWZmIgpkZiA9IGRmICU+JSAKICByb3duYW1lc190b19jb2x1bW4odmFyID0gIm5vZGVzIikgJT4lCiAgZmlsdGVyKG1lZGlhbi5zdGF0ZS5kaWZmID4gMC4wNSB8IG1lZGlhbi5zdGF0ZS5kaWZmIDwgLTAuMDUpCgpnZ2RvdGNoYXJ0KGRmLCB4ID0gIm5vZGVzIiwgeSA9ICJtZWRpYW4uc3RhdGUuZGlmZiIsCiAgdGl0bGUgPSAiTWVkaWFuIGFjdGl2aXR5IHN0YXRlIGRpZmZlcmVuY2UgYWNyb3NzIGFsbCBzeW5lcmd5IGNvbXBhcmlzb24gc2V0cyAoQUstUEQsIEE0OTggY2VsbCBsaW5lKSIsIAogIGxhYmVsID0gIm5vZGVzIiwgZm9udC5sYWJlbCA9IGxpc3Qoc2l6ZSA9IDExLCBjb2xvciA9ICJibHVlIiksCiAgbGFiZWwuc2VsZWN0ID0gbGlzdChjcml0ZXJpYSA9ICJgeWAgPj0gMC43IHwgYHlgIDw9IC0wLjciKSwgCiAgcmVwZWwgPSBUUlVFLCBsYWJlbC5yZWN0YW5nbGUgPSBUUlVFLAogIHhsYWIgPSAiTm9kZXMiLCB5bGFiID0gIk1lZGlhbiBBY3Rpdml0eSBTdGF0ZSIsIAogIHlsaW0gPSBjKC0xLDEpLCBhZGQgPSAic2VnbWVudHMiKSArIAogICAgZm9udCgieC50ZXh0Iiwgc2l6ZSA9IDEwKSArCiAgICBmb250KCJ0aXRsZSIsIHNpemUgPSAxMSkgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC0wLjcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsgCiAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC43LCBsaW5ldHlwZSA9ICIwLjciKSwgY29sb3IgPSAicmVkIikgKwogICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKG5hbWUgPSAiVGhyZXNob2xkIiwgdmFsdWVzID0gMiwgCiAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoY29sb3IgPSAicmVkIikpKSArIAogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsMC43KSkKYGBgCgo8ZGl2IGNsYXNzPSJvcmFuZ2UtYm94Ij4KTm90ZSB0aGUgYm9vbGVhbiBlcXVhdGlvbjogYFBUUE42ICo9IFNSQ2AuIFRoaXMgbWVhbnMgdGhhdCBgU1JDYCBpcyAqKnRoZSBvbmx5IGluaGliaXRlZCBub2RlIG9mIGludGVyZXN0KioKPC9kaXY+CjwvYnI+CgpXZSB3aWxsIG5vdyBjaGVjayBpZiB0aGUgYWJvdmUgb2JzZXJ2YXRpb25zIHJlZ2FyZGluZyB0aGUgYWN0aXZpdHkgb2YgdGhlIG5vZGVzIGFyZSB0cnVlIHVzaW5nIHRoZSBbTUNMUCBkYXRhc2V0XShodHRwczovL3RjcGFwb3J0YWwub3JnL21jbHAvIy9kb3dubG9hZCkgYXMgYSByZWZlcmVuY2U6CgpgYGB7ciBNQ0xQIERhdGFzZXQsIGNhY2hlPVRSVUV9Cm1jbHAuZGF0YSA9IHJlYWQudGFibGUoZmlsZSA9ICJNQ0xQLXYxLjEtTGV2ZWw0LnRzdiIsIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKY2VsbC5saW5lcy5pbi5tY2xwID0gYygiQTQ5OCIsICJBR1MiLCAiRFUxNDUiLCAiQ09MTzIwNSIsICJTVzYyMCIsICJTRjI5NSIsICJVQUNDNjIiLCAiTURBTUI0NjgiKQpgYGAKCldlIGNoZWNrIHRoZSBwaG9zcGhvcnlsYXRpb24gdmFsdWUgb2YgdGhlIGBTUkNfcFk1MjdgIGFjcm9zcyBhbGwgY2VsbCBsaW5lczoKYGBge3IgTUNMUCBEYXRhIFZpc3VhbGl6YXRpb24gKFNSQyksIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30Kc3JjLmRhdGEgPSBtY2xwLmRhdGEgJT4lIHNlbGVjdChTYW1wbGVfTmFtZSwgU1JDX3BZNTI3KSAlPiUgbmEub21pdCgpCgojIGZpbmQgdGhlIGFjdGl2aXR5IGNsYXNzZXMgKDMgY2xhc3NlczogbG93IGFjdGl2aXR5LCBubyBhY3Rpdml0eSwgaGlnaCBhY3Rpdml0eSkKcmVzID0gQ2ttZWFucy4xZC5kcCh4ID0gc3JjLmRhdGEkU1JDLCBrID0gMykKYWN0aXZpdHkgPSBhcy5mYWN0b3IocmVzJGNsdXN0ZXIpCmxldmVscyhhY3Rpdml0eSkgPSBjKCJsb3ciLCAibWVkaXVtIiwgImhpZ2giKQpzcmMuZGF0YSA9IGNiaW5kKHNyYy5kYXRhLCBhY3Rpdml0eSkKCmdnZG90Y2hhcnQoZGF0YSA9IHNyYy5kYXRhLCB4ID0gIlNhbXBsZV9OYW1lIiwgeSA9ICJTUkNfcFk1MjciLCAKICB0aXRsZSA9ICJTUkNfcFk1Mjcgc2lnbmFsaW5nIGFjcm9zcyBhbGwgY2VsbCBsaW5lcyBpbiBNQ0xQIERhdGFzZXQiLAogIGNvbG9yID0gImFjdGl2aXR5IiwgcGFsZXR0ZSA9IGMoInJlZCIsICJncmV5IiwgImdyZWVuIiksIAogIGxhYmVsID0gIlNhbXBsZV9OYW1lIiwgbGFiZWwuc2VsZWN0ID0gY2VsbC5saW5lcy5pbi5tY2xwLCByZXBlbCA9IFRSVUUsCiAgYWRkID0gInNlZ21lbnRzIiwgbGFiZWwucmVjdGFuZ2xlID0gVFJVRSwKICB4bGFiID0gIkNlbGwgTGluZXMiLCB5bGFiID0gIlNSQ19wWTUyNyBTaWduYWxpbmciLAogIGFkZC5wYXJhbXMgPSBsaXN0KGNvbG9yID0gImFjdGl2aXR5IiwgcGFsZXR0ZSA9IGMoInJlZCIsICJncmV5IiwgImdyZWVuIikpKSArIAogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKPGRpdiBjbGFzcz0iYmx1ZS1ib3giIGlkPSJUZXh0MDEiPgpJdCBoYXMgYmVlbiBzaG93biBpbiB2YXJpb3VzIHN0dWRpZXMgKGUuZy4gaW4gW3RoaXMgcGFwZXJdKGh0dHBzOi8vam91cm5hbHMucGxvcy5vcmcvcGxvc29uZS9hcnRpY2xlP2lkPTEwLjEzNzEvam91cm5hbC5wb25lLjAwNzEwMzUpKSB0aGF0IHRoZSBwaG9zcGhvcnlsYXRpb24gc2l0ZXMgb2YgYFNSQ2AgaW5jbHVkZSB0aGUgKippbmhpYml0aW5nIHBob3NwaG90eXJvc2luZSA1Mjcgc2l0ZSoqLCB3aGljaCBvdmVycmlkZXMgdGhlIGBZNDE2YCBwaG9zcGhvcnlsYXRpb24gKHdoaWNoIGlzIGFsc28gdGVzdGVkIGluIHRoZSBNQ0xQIGRhdGFzZXQpLiAKU2luY2UgZm9yIGBBNDk4YCBjZWxsIGxpbmUgd2Ugb2JzZXJ2ZSBvbmUgb2YgdGhlIGxhcmdlc3Qgc2lnbmFsaW5nIG1lYXN1cmVtZW50cyBjb21wYXJlZCB0byBhbGwgdGhlIGNlbGwgbGluZXMgZm9yIHRoZSBgU1JDX3BZNTI3YCBzaXRlLCB0aGlzIG1lYW5zIHRoYXQgYFNSQ2Agc2hvdWxkIGJlIGluIGFuICoqaW5oaWJpdGluZyBzdGF0ZSoqLCB3aGljaCBpcyBleGFjdGx5IHdoYXQgd2UgZm91bmQgZnJvbSBvdXIgYW5hbHlzaXMgYWJvdmUuCjwvZGl2Pgo8L2JyPgoKV2UgYWxzbyBjaGVjayBmb3IgdGhlIHBob3NwaG9yeWxhdGlvbiB2YWx1ZSBvZiB0aGUgYE1BUEtfcFQyMDJZMjA0YCAoZm9yIHRoZSBgRVJLX2ZgIGFjdGl2YXRpb24pIGFjcm9zcyBhbGwgY2VsbCBsaW5lczoKYGBge3IgTUNMUCBEYXRhIFZpc3VhbGl6YXRpb24gKEVSS19mKSwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQplcmsuZGF0YSA9IG1jbHAuZGF0YSAlPiUgc2VsZWN0KFNhbXBsZV9OYW1lLCBNQVBLX3BUMjAyWTIwNCkgJT4lIG5hLm9taXQoKQoKIyBmaW5kIHRoZSBhY3Rpdml0eSBjbGFzc2VzICgzIGNsYXNzZXM6IGxvdyBleHByZXNzaW9uLCBubyBleHByZXNzaW9uLCBoaWdoIGV4cHJlc3Npb24pCnJlcyA9IENrbWVhbnMuMWQuZHAoeCA9IGVyay5kYXRhJE1BUEtfcFQyMDJZMjA0LCBrID0gMykKYWN0aXZpdHkgPSBhcy5mYWN0b3IocmVzJGNsdXN0ZXIpCmxldmVscyhhY3Rpdml0eSkgPSBjKCJsb3ciLCAibWVkaXVtIiwgImhpZ2giKQplcmsuZGF0YSA9IGNiaW5kKGVyay5kYXRhLCBhY3Rpdml0eSkKCmdnZG90Y2hhcnQoZGF0YSA9IGVyay5kYXRhLCB4ID0gIlNhbXBsZV9OYW1lIiwgeSA9ICJNQVBLX3BUMjAyWTIwNCIsIAogIHRpdGxlID0gIk1BUEtfcFQyMDJZMjA0IHNpZ25hbGluZyBhY3Jvc3MgYWxsIGNlbGwgbGluZXMgaW4gTUNMUCBEYXRhc2V0IiwKICBjb2xvciA9ICJhY3Rpdml0eSIsIHBhbGV0dGUgPSBjKCJyZWQiLCAiZ3JleSIsICJncmVlbiIpLCAKICBsYWJlbCA9ICJTYW1wbGVfTmFtZSIsIGxhYmVsLnNlbGVjdCA9IGNlbGwubGluZXMuaW4ubWNscCwgcmVwZWwgPSBUUlVFLCB4bGFiID0gIkNlbGwgTGluZXMiLAogIGFkZCA9ICJzZWdtZW50cyIsIGxhYmVsLnJlY3RhbmdsZSA9IFRSVUUsIHlsYWIgPSAiTUFQS19wVDIwMlkyMDQgU2lnbmFsaW5nIiwKICBhZGQucGFyYW1zID0gbGlzdChjb2xvciA9ICJhY3Rpdml0eSIsIHBhbGV0dGUgPSBjKCJyZWQiLCAiZ3JleSIsICJncmVlbiIpKSkgKyAKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCjxkaXYgY2xhc3M9ImJsdWUtYm94Ij4KT25lIG9mIHRoZSAqKmFjdGl2YXRpbmcgcGhvc3Bob3J5bGF0aW9uIHNpdGVzIG9mIGBFUktgKiogaXMgdGhlICoqRVJLMSBUMjAyL1kyMDQqKi4gCkZyb20gdGhlIGFib3ZlIGZpZ3VyZSB3ZSBzZWUgYSB3ZWFrIHNpZ25hbGluZyBvZiB0aGlzIHBob3Nob3NpdGUgZm9yIHRoZSBgQTQ5OGAgY2VsbCBsaW5lIHdoaWNoIGRvZXMgbm90IGZ1bGx5IGNvcnJlbGF0ZSB3ZWxsIHdpdGggdGhlIHJlc3VsdHMgZnJvbSBvdXIgYW5hbHlzaXMgYWJvdmUgKGBFUktfZmAgb3ZlcmV4cHJlc3Npb24pLiBUaG91Z2gsIGluIGEgW2xhdGVyIHNlY3Rpb25dKCNhay1wZC1pbi1vdGhlci1jZWxsLWxpbmVzKSB3ZSBzaG93IHRoYXQgYEVSS19mYCBpcyBmb3VuZCBhY3RpdmUgaW4gYWxsIG1vZGVscyBmcm9tIGFsbCBjZWxsIGxpbmVzIHRoYXQgcHJlZGljdCB0aGUgYEFLLVBEYCBzeW5lcmd5IChmb3IgZXhhbXBsZSBldmVuIGluIGBVQUNDNjJgIGNlbGwgbGluZSB3aGljaCBoYXMgYSBoaWdoIHBob3NwaG9yeWxhdGlvbiBzaWduYWwgaW4gdGhlIGFib3ZlIGZpZ3VyZSkuCjwvZGl2Pgo8L2JyPgoKVXNpbmcgdGhlIG1hdHJpeCBmcm9tIFtUYWJsZSAyXSgjVGFibGUyKSwgd2UgY291bnQgcGVyIG5ldHdvcmsgbm9kZSAqKnRoZSBudW1iZXIgb2YgdGltZXMgdGhhdCB0aGUgbm9kZSdzIGF2ZXJhZ2UgbGluayBvcGVyYXRvciBkaWZmZXJlbmNlIHZhbHVlIGhhcyBzdXJwYXNzZWQgYSBzcGVjaWZpZWQgdGhyZXNob2xkKiogLSBpLmUuIHRoZSBudW1iZXIgb2YgdGltZXMgaXQgaGFzIGJlZW4gZm91bmQgYXMgaW1wb3J0YW50IChhIGJpb21hcmtlcikgYWNyb3NzIGFsbCB0aGUgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgKHNvIHRoZSBtb3JlIHRoZSBiZXR0ZXIpOgoKYGBge3IgTGluayBPcGVyYXRvciBCaW9tYXJrZXIgY29udGluZ2VuY3kgdGFibGUgYWNyb3NzIGFsbCBjb21wYXJpc29uIHNldHMgKEFLLVBLIC0gQ2VsbC1zcGVjaWZpYyAtIEE0OTgpLCByZXN1bHRzPSdhc2lzJ30KYmlvbWFya2VyLmxpbmsubWF0ID0gYmluYXJpemVfdG9fdGhyZXMobWF0ID0gZGlmZi5saW5rLm1hdCwgdGhyZXMgPSAwLjgpCgpiaW9tYXJrZXIubGluay5jb3VudHMgPSBjb2xTdW1zKGJpb21hcmtlci5saW5rLm1hdCkKcHJldHR5X3ByaW50X3ZlY3Rvcl9uYW1lc19hbmRfdmFsdWVzKHRhYmxlKGJpb21hcmtlci5saW5rLmNvdW50cykpCmBgYAoKVGhlIGFib3ZlIG1lYW5zIHRoYXQgdGhlcmUgd2VyZSAkNDQkIG5vZGVzIHRoYXQgd2VyZSAqemVybyogdGltZXMgZm91bmQgYXMgbGluayBvcGVyYXRvciBiaW9tYXJrZXJzIGFjcm9zcyBhbGwgc3luZXJneSBzZXQgY29tcGFyaXNvbnMsIG9uZSB0aGF0IHdhcyBmb3VuZCBvbmNlLCBvbmUgdGhhdCB3YXMgZm91bmQgdHdpY2UsICoqJDUkIHRoYXQgd2VyZSBmb3VuZCA2IHRpbWVzIGFuZCAkMSQgdGhhdCB3YXMgZm91bmQgMTEgdGltZXMqKi4gVGhlIGxhc3QgdHdvIGNhdGVnb3JpZXMgb2Ygbm9kZXMgYXJlOgoKYGBge3IgRXh0cmEgbGluayBvcGVyYXRvciBiaW9tYXJrZXJzIChDZWxsLXNwZWNpZmljIC0gQUstUEQpLCByZXN1bHRzPSdhc2lzJ30KcHJldHR5X3ByaW50X3ZlY3Rvcl9uYW1lcyhiaW9tYXJrZXIubGluay5jb3VudHNbYmlvbWFya2VyLmxpbmsuY291bnRzID09IDZdKQpwcmV0dHlfcHJpbnRfdmVjdG9yX25hbWVzKGJpb21hcmtlci5saW5rLmNvdW50c1tiaW9tYXJrZXIubGluay5jb3VudHMgPT0gMTFdKQpgYGAKCklmIHdlIHZpc3VhbGl6ZSB0aGUgKiphdmVyYWdlIGxpbmsgb3BlcmF0b3IgZGlmZmVyZW5jZSBhY3Jvc3MgYWxsIG9mIHRoZSBzeW5lcmd5IGNvbXBhcmlzb24gc2V0cyoqIGZyb20gW1RhYmxlIDJdKCNUYWJsZTIpLCB3ZSBjYW4gc2VlIHRoZSBub2RlcyBtZW50aW9uZWQgYWJvdmU6CgpgYGB7ciBBdmVyYWdlIGxpbmsgb3BlcmF0b3IgZGlmZiBhY3Jvc3MgYWxsIHN5bmVyZ3kgc3Vic2V0cyAoQUstUEQpLCBjYWNoZT1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcid9CnBsb3RfYXZnX2xpbmtfb3BlcmF0b3JfZGlmZl9ncmFwaChuZXQsIGRpZmYgPSBjb2xNZWFucyhkaWZmLmxpbmsubWF0KSwgbGF5b3V0ID0gbmljZS5sYXlvdXQsIAogIHRpdGxlID0gIkF2ZXJhZ2UgbGluayBvcGVyYXRvciBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChBSy1QRCkiKQpgYGAKCjxkaXYgY2xhc3M9Im9yYW5nZS1ib3giPgpTb21lIG5vdGVzL29ic2VydmF0aW9ucyBvbiB0aGUgc3RydWN0dXJlIG9mIHRoZSBib29sZWFuIGVxdWF0aW9ucyBvZiB0aGUgbm9kZXMgZm91bmQgYWJvdmU6CjwvZGl2Pgo8L2JyPgoKVGhlIGJvb2xlYW4gZXF1YXRpb24gb2YgdGhlIGBTUkNgIG5vZGUgaXM6IGBTUkMgKj0gICggIFJUUEtfZiApIEFORC9PUiBOT1QgICggQ1NLIClgIGFuZCBpdCdzIG1lYW4gbGluayBvcGVyYXRvciBkaWZmZXJlbmNlIHZhbHVlIGFjcm9zcyBhbGwgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgaXMgYHIgbWVhbihkaWZmLmxpbmsubWF0WywgIlNSQyJdKWAgd2hpY2ggbWVhbnMgdGhhdCBvbiBhdmVyYWdlIHRoZSBsb2dpYyBvcGVyYXRvciB0aGF0IGJpbmRzIGl0cyB0d28gcmVndWxhdG9ycyBpcyB0aGUgYEFORCBOT1RgIGFuZCB0aHVzIGl0J3MgbW9yZSBkaWZmaWN1bHQgdG8gaGF2ZSBpdCBhcyBhY3RpdmF0ZWQgKDEgY2FzZSBvdXQgb2YgNCBpbiBib29sZWFuIGxvZ2ljKS4gKipUaGlzIGNvcnJlbGF0ZXMgd2l0aCB0aGUgZmFjdCB0aGF0IGl0IHdhcyBmb3VuZCBhcyBtb3N0bHkgaW5oaWJpdGVkIGluIHRoZSBhbmFseXNpcyBhYm92ZSoqLiBBbHNvIG5vdGUgdGhlIGVxdWF0aW9uczogCgotIGBDU0sgKj0gICggIFBSS0FDQSApYAotIGBQUktBQ0EgKj0gICggICggIE5GS0JfZiApICBvciBGT1MgKWAKLSBgRk9TICo9ICAoICAoICAoICBFUktfZiApICBvciBSU0tfZiApICBvciBTUkYgKWAKClNvLCB3aGVuIGBFUktfZmAgaXMgb3ZlcmV4cHJlc3NlZCwgYENTS2AgYmVjb21lcyBhY3RpdmUsIHdoaWNoIG1lYW5zIHRoYXQgdGhlIHByZXZhbGVuY2Ugb2YgdGhlIGBBTkQgTk9UYCBsaW5rIG9wZXJhdG9yIG1ha2VzIHRoZSBhY3Rpdml0eSBvZiB0aGUgYFNSQ2Agbm9kZSBkZXBlbmRlbnQgb25seSBvbiBpdCdzIGluaGliaXRvciBgQ1NLYCAoc2luY2UgaXQncyBhY3RpdmUpIGFuZCBub3Qgb24gdGhlIGFjdGl2aXR5IG9mIGBSVFBLX2ZgIG5vZGUuCgotIGBtVE9SQzFfYyAqPSAgKCAgKCAgUkhFQiApICBvciBSU0tfZiApIEFORC9PUiBOT1QgICggQUtUMVMxIClgCi0gYEFLVDFTMSAqPSAgbm90ICAoIEFLVF9mIClgCgpgbVRPUkMxX2NgJ3MgbWVhbiBsaW5rIG9wZXJhdG9yIGRpZmZlcmVuY2UgdmFsdWUgYWNyb3NzIGFsbCBzeW5lcmd5IHNldCBjb21wYXJpc29ucyBpcyBgciBtZWFuKGRpZmYubGluay5tYXRbLCAibVRPUkMxX2MiXSlgIHdoaWNoIG1lYW5zIHRoYXQgb24gYXZlcmFnZSB0aGUgbG9naWMgb3BlcmF0b3IgdGhhdCBiaW5kcyBpdHMgdHdvIHJlZ3VsYXRvcnMgaXMgdGhlIGBPUiBOT1RgLiAKKipUaGlzIGdpdmVzIHRoZSBub2RlIHRoZSBzdHJ1Y3R1cmFsIGZsZXhpYmlsaXR5IHRvIGJlY29tZSBhY3RpdmUgd2hlbiB0aGUgYEFLYCBkcnVnIGlzIHVzZWQqKjogYEFLYCBpbmhpYml0cyBgQUtUX2ZgIG5vZGUsIG1ha2luZyB0aHVzIGBBS1QxUzFgIGFjdGl2ZSwgd2hpY2ggaW4gdHVybiBtYWtlcyB0aGUgYG1UT1JDMV9jYCBlcXVhdGlvbiBsaWtlIHRoaXM6IGBtVE9SQzFfYyAqPSAgKCAgKCAgUkhFQiApICBvciBSU0tfZiApIEFORC9PUiAwYC4gU28sIGFuIGBBTkRgIGxpbmsgb3BlcmF0b3Igd291bGQgcmVzdWx0IGFsd2F5cyBpbiBhbiBpbmhpYml0ZWQgYG1UT1JDMV9jYCB3aGVyZWFzIGFuIGBPUmAgbGluayBvcGVyYXRvciB3b3VsZCBnaXZlIHRoZSBwb3NzaWJpbGl0eSBmb3IgdGhlIGBtVE9SQzFfY2AgdG8gYmUgYWN0aXZlIGluIGNhc2Ugb25lIG9mIGl0cyBhY3RpdmF0b3JzIGFyZSBhY3RpdmUuCgoKYGBge3IgSW5kdWNlZCBncmFwaHMgaW52ZXN0aWdhdGlvbiwgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0Kbm9kZXMgPSBjKCJtVE9SQzFfYyIsICJUU0NfZiIsICJTUkMiLCAiQUtUX2YiLCAiTUVLX2YiKQpzdWIubmV0ID0gZmlsdGVyX25ldHdvcmsobmV0LCBub2RlcywgbGV2ZWwgPSAxKQpzdWIubm9kZXMgPSBWKHN1Yi5uZXQpJG5hbWUKc2V0LnNlZWQoMCkKc3ViLmxheW91dCA9IGxheW91dF9uaWNlbHkoZ3JhcGggPSBzdWIubmV0KQoKZGlmZiA9IGNvbE1lYW5zKGRpZmYubGluay5tYXQpCgpwbG90X2F2Z19saW5rX29wZXJhdG9yX2RpZmZfZ3JhcGgoc3ViLm5ldCwgZGlmZiA9IGRpZmZbc3ViLm5vZGVzXVshaXMubmEoZGlmZltzdWIubm9kZXNdKV0sIAogIGxheW91dCA9IHN1Yi5sYXlvdXQsIHRpdGxlID0gIkF2ZXJhZ2UgbGluayBvcGVyYXRvciBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChBSy1QRCkiKQpgYGAKCjxkaXYgY2xhc3M9ImJsdWUtYm94Ij4KV2UgY29uY2x1ZGUgdGhhdCBjYW5jZXIgbW9kZWxzIGluIHdoaWNoIHRoZSBgQUstUERgIGRydWcgY29tYmluYXRpb24gaXMgc3luZXJnaXN0aWMsIHRlbmQgdG8gYWxzbyBoYXZlIHRoZSBub2RlcyBgU1JDYCBhbmQgYFBUUE42YCBpbiBhbiAqKmluaGliaXRlZCBzdGF0ZSoqIGFuZCB0aGUgYFNSQ2Agbm9kZSBkZXBlbmRzIG9uIGJvdGggaXQncyByZWd1bGF0b3JzLCBgUlRQS19mYCBhbmQgYENTS2AuIApUaGUgbGluayBvcGVyYXRvcnMgb2YgdGhlIGBtVE9SQzFfY2AgYW5kIGBUU0NfZmAgZXF1YXRpb25zIGFsc28gc2VlbSB0byBoYXZlIGFuIGltcG9ydGFudCByb2xlIGluIHRoZSBtYW5pZmVzdGF0aW9uIG9mIHRoaXMgc3luZXJneS4KPC9kaXY+CjwvYnI+CgojIyMgUmFuZG9tIHstfQoKV2UgZ2V0IHRoZSBhdmVyYWdlIHN0YXRlIGFuZCBsaW5rIG9wZXJhdG9yIGRpZmZlcmVuY2VzIHBlciBuZXR3b3JrIG5vZGUgZm9yIHRoZSBgQTQ5OGAgY2VsbCBsaW5lIGZyb20gdGhlIHJhbmRvbSBtb2RlbHM6CgpgYGB7ciBBSy1QRCBkaWZmIChSYW5kb20gbW9kZWxzIC0gQTQ5OCl9CkFLLlBELmF2Zy5zdGF0ZS5kaWZmLnJhbmRvbSA9IHJhbmRvbS5zeW5lcmd5LmFuYWx5c2lzLnJlcyRBNDk4JGRpZmYuc3RhdGUuc3luZXJnaWVzLm1hdFsiQUstUEQiLCBdCkFLLlBELmF2Zy5saW5rLmRpZmYucmFuZG9tICA9IHJhbmRvbS5zeW5lcmd5LmFuYWx5c2lzLnJlcyRBNDk4JGRpZmYubGluay5zeW5lcmdpZXMubWF0WyJBSy1QRCIsIF0KYGBgCgojIyMjIEFjdGl2aXR5IHN0YXRlIGJpb21hcmtlcnMgey19CgpXZSB3aWxsIG5vdyB2aXN1YWxpemUgdGhlIG5vZGVzIGF2ZXJhZ2Ugc3RhdGUgZGlmZmVyZW5jZXMgaW4gYSBuZXR3b3JrIGdyYXBoLiBOb3RlIHRoYXQgdGhlICoqZ29vZCBtb2RlbHMqKiBhcmUgdGhvc2UgdGhhdCBwcmVkaWN0ZWQgdGhlIGBBSy1QRGAgZHJ1ZyBjb21iaW5hdGlvbiB0byBiZSAqc3luZXJnaXN0aWMqIGFuZCB3ZXJlIGNvbnRyYXN0ZWQgdG8gdGhvc2UgdGhhdCBwcmVkaWN0ZWQgaXQgdG8gYmUgKmFudGFnb25pc3RpYyogKCoqYmFkIG1vZGVscyoqKS4gVGhlIG51bWJlciBvZiBtb2RlbHMgaW4gZWFjaCBjYXRlZ29yeSB3ZXJlOgoKYGBge3IgQ291bnQgZ29vZCB2cyBiYWQgbW9kZWxzIGZvciBBSy1QRCBzeW5lcmd5IChyYW5kb20gbW9kZWxzKSwgcmVzdWx0cz0nYXNpcyd9CmRydWcuY29tYiA9ICJBSy1QRCIKCmdvb2QubW9kZWxzLm51bSA9IHN1bShyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdID09IDEgJiAhaXMubmEocmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSkpCiMgdW5pcXVlIGdvb2QgbW9kZWxzCiMgbnJvdyh1bmlxdWUocmFuZG9tLm1vZGVscy5saW5rLm9wZXJhdG9yW3JhbmRvbS5tb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0gPT0gMSAmICFpcy5uYShyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdKSxdKSkKYmFkLm1vZGVscy5udW0gID0gc3VtKHJhbmRvbS5tb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0gPT0gMCAmICFpcy5uYShyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdKSkKCnByZXR0eV9wcmludF9zdHJpbmcocGFzdGUwKCJOdW1iZXIgb2YgJ2dvb2QnIHJhbmRvbSBtb2RlbHMgKEFLLVBEIHN5bmVyZ2lzdGljKSBpbiB0aGUgQTQ5OCBjZWxsIGxpbmU6ICIsIGdvb2QubW9kZWxzLm51bSkpCnByZXR0eV9wcmludF9zdHJpbmcocGFzdGUwKCJOdW1iZXIgb2YgJ2JhZCcgcmFuZG9tIG1vZGVscyAoQUstUEQgYW50YWdvbmlzdGljKSBpbiB0aGUgQTQ5OCBjZWxsIGxpbmU6ICIsIGJhZC5tb2RlbHMubnVtKSkKYGBgCgpgYGB7ciBBSy1QRCBhY3Rpdml0eSBzdGF0ZSBiaW9tYXJrZXJzIChSYW5kb20gbW9kZWxzIC0gQTQ5OCksIGNhY2hlID0gVFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpwbG90X2F2Z19zdGF0ZV9kaWZmX2dyYXBoKG5ldCwgZGlmZiA9IEFLLlBELmF2Zy5zdGF0ZS5kaWZmLnJhbmRvbSwgCiAgbGF5b3V0ID0gbmljZS5sYXlvdXQsIHRpdGxlID0gIkFLLVBEIGFjdGl2aXR5IHN0YXRlIGJpb21hcmtlcnMgKFJhbmRvbSBtb2RlbHMgLSBBNDk4KSIpCmBgYAoKVGh1cywgd2UgY2FuIGlkZW50aWZ5IHRoZSBhY3RpdmUgc3RhdGUgYmlvbWFya2VycyAobm90ZSB0aGF0IG5vIGluaGliaXRlZCBiaW9tYXJrZXJzIGF0IHRoZSBzcGVjaWZpZWQgdGhyZXNob2xkIGRpZmZlcmVuY2UgbGV2ZWwgd2VyZSBmb3VuZCk6CgpgYGB7ciBBSy1QRCBhY3RpdmUgc3RhdGUgYmlvbWFya2VycyAoUmFuZG9tIG1vZGVscyAtIEE0OTgpLCByZXN1bHRzID0gJ2FzaXMnfQpBSy5QRC5hY3RpdmUuYmlvbWFya2VycyA9IEFLLlBELmF2Zy5zdGF0ZS5kaWZmLnJhbmRvbVtBSy5QRC5hdmcuc3RhdGUuZGlmZi5yYW5kb20gPiAwLjhdCnByZXR0eV9wcmludF92ZWN0b3JfbmFtZXMoQUsuUEQuYWN0aXZlLmJpb21hcmtlcnMpCmBgYAoKYGBge3IgQUstUEQgaW5oaWJpdGVkIHN0YXRlIGJpb21hcmtlcnMgKFJhbmRvbSBtb2RlbHMgLSBBNDk4KSwgcmVzdWx0cyA9ICdhc2lzJywgaW5jbHVkZT1GQUxTRX0KQUsuUEQuaW5oaWJpdGVkLmJpb21hcmtlcnMgPSBBSy5QRC5hdmcuc3RhdGUuZGlmZi5yYW5kb21bQUsuUEQuYXZnLnN0YXRlLmRpZmYucmFuZG9tIDwgLTAuOF0KcHJldHR5X3ByaW50X3ZlY3Rvcl9uYW1lcyhBSy5QRC5pbmhpYml0ZWQuYmlvbWFya2VycykKYGBgCgo8ZGl2IGNsYXNzPSJibHVlLWJveCIgaWQ9IlRleHQwMiI+ClRoZSByYW5kb20gbW9kZWxzIHRoYXQgcHJlZGljdGVkIHRoZSBgQUstUERgIHN5bmVyZ3kgYWxzbyBzaG93IGFuIG92ZXJleHByZXNzaW9uIG9mIHRoZSBgRVJLX2ZgIG5vZGUuCjwvZGl2Pgo8L2JyPgoKIyMjIyBMaW5rIG9wZXJhdG9yIGJpb21hcmtlcnMgey19CgpXZSB2aXN1YWxpemUgdGhlIG5vZGVzIGF2ZXJhZ2UgbGluayBvcGVyYXRvciBkaWZmZXJlbmNlcyBpbiBhIG5ldHdvcmsgZ3JhcGg6CgpgYGB7ciBBSy1QRCBsaW5rIG9wZXJhdG9yIGJpb21hcmtlcnMgKFJhbmRvbSBtb2RlbHMgLSBBNDk4KSwgY2FjaGUgPSBUUlVFLCBmaWcuYWxpZ249J2NlbnRlcid9CnBsb3RfYXZnX2xpbmtfb3BlcmF0b3JfZGlmZl9ncmFwaChuZXQsIGRpZmYgPSBBSy5QRC5hdmcubGluay5kaWZmLnJhbmRvbSwgCiAgbGF5b3V0ID0gbmljZS5sYXlvdXQsIHRpdGxlID0gIkFLLVBEIGxpbmsgb3BlcmF0b3IgYmlvbWFya2VycyAoUmFuZG9tIG1vZGVscyAtIEE0OTgpIikKYGBgCgo8ZGl2IGNsYXNzPSdibHVlLWJveCc+ClRoZSBpbXBvcnRhbmNlIG9mIHRoZSAqKk9SIE5PVCoqIGxpbmsgb3BlcmF0b3IgaW4gdGhlIGJvb2xlYW4gZXF1YXRpb24gb2YgYEVSS19mYCBpcyBhZ2FpbiBwcm92ZW4gdG8gYmUgY3J1Y2lhbCBmb3IgdGhlIG1hbmlmZXN0YXRpb24gb2YgdGhlIGBBSy1QRGAgc3luZXJneSwgYWxvbmcgd2l0aCB0aGUgbGluayBvcGVyYXRvcnMgb2YgdGhlIGVxdWF0aW9ucyBvZiB0aGUgYE1FS19mYCwgYFBURU5gIGFuZCBgUERQSzFgIG5vZGVzLgo8L2Rpdj4KPC9icj4KCiMjIyMgU3luZXJneSBzdWJzZXRzIGFuYWx5c2lzIHstfQoKV2UgcGVyZm9ybSB0aGUgc2FtZSBraW5kIG9mIGFuYWx5c2lzIGFzIHdpdGggdGhlIGNlbGwtc3BlY2lmaWMgbW9kZWxzOiBtb2RlbHMgdGhhdCBwcmVkaWN0IGEgc2V0IG9mIHN5bmVyZ2llcyB3aWxsIGJlIGNvbnRyYXN0ZWQgdG8gbW9kZWxzIHRoYXQgcHJlZGljdGVkIHRoZSBzYW1lIHNldCB3aXRoIHRoZSBhZGRpdGlvbiBvZiB0aGUgZXh0cmEgYEFLLVBEYCBzeW5lcmd5LCBhbGxvd2luZyB1cyB0aHVzIHRvIHJlZmluZSB0aGUgYWN0aXZpdHkgc3RhdGUgYW5kIGxpbmsgb3BlcmF0b3IgYmlvbWFya2VycyB3ZSBmb3VuZCBhYm92ZSBmcm9tIHRoZSByYW5kb20gbW9kZWxzLgoKV2UgZmlyc3QgY29uc3RydWN0IHR3byBtYXRyaWNlczogaW4gdGhlIGZpcnN0LCAqKmVhY2ggcm93IGlzIGEgc2V0IHZzIHN1YnNldCBhdmVyYWdlIGFjdGl2aXR5IGRpZmZlcmVuY2UgdmVjdG9yIG9mIHRoZSBuZXR3b3JrIG5vZGVzKiosIHdoaWxlIG9uIHRoZSBzZWNvbmQgKiplYWNoIHJvdyBpcyBhIHNldCB2cyBzdWJzZXQgYXZlcmFnZSBsaW5rIG9wZXJhdG9yIGRpZmZlcmVuY2UgdmVjdG9yIG9mIHRoZSBuZXR3b3JrIG5vZGVzKio6CmBgYHtyIFN5bmVyZ3kgU3Vic2V0cyBDb21wYXJpc29uIGZvciBBSy1QRCAoUmFuZG9tIC0gQTQ5OCksIGNhY2hlPVRSVUV9CnJlcyA9IGdldF9zeW5lcmd5X2NvbXBhcmlzb25fc2V0cyhyYW5kb20uc3luZXJneS5hbmFseXNpcy5yZXMkQTQ5OCRzeW5lcmd5LnN1YnNldC5zdGF0cykKQUsuUEQucmVzID0gcmVzICU+JSBmaWx0ZXIoc3luZXJnaWVzID09ICJBSy1QRCIpCgpkaWZmLnN0YXRlLmxpc3QucmFuZG9tID0gbGlzdCgpCmRpZmYubGluay5saXN0LnJhbmRvbSA9IGxpc3QoKQpmb3IgKGkgaW4gMTpucm93KEFLLlBELnJlcykpIHsKICBzeW5lcmd5LnNldCAgICA9IEFLLlBELnJlc1tpLCAyXQogIHN5bmVyZ3kuc3Vic2V0ID0gQUsuUEQucmVzW2ksIDNdCiAgCiAgc3luZXJneS5zZXQuc3RyICAgID0gdW5saXN0KHN0cnNwbGl0KHggPSBzeW5lcmd5LnNldCwgc3BsaXQgPSAiLCIpKQogIHN5bmVyZ3kuc3Vic2V0LnN0ciA9IHVubGlzdChzdHJzcGxpdCh4ID0gc3luZXJneS5zdWJzZXQsIHNwbGl0ID0gIiwiKSkKICAKICAjIGNvdW50IG1vZGVscwogIHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0gPSBjb3VudF9tb2RlbHNfdGhhdF9wcmVkaWN0X3N5bmVyZ2llcyhzeW5lcmd5LnNldC5zdHIsIHJhbmRvbS5tb2RlbC5wcmVkaWN0aW9ucykKICBzeW5lcmd5LnN1YnNldC5tb2RlbHMubnVtID0gY291bnRfbW9kZWxzX3RoYXRfcHJlZGljdF9zeW5lcmdpZXMoc3luZXJneS5zdWJzZXQuc3RyLCByYW5kb20ubW9kZWwucHJlZGljdGlvbnMpCiAgIyBwcmludChwYXN0ZTAoc3luZXJneS5zZXQubW9kZWxzLm51bSwgIiAiLCBzeW5lcmd5LnN1YnNldC5tb2RlbHMubnVtKSkKICAKICAjIGlmIHRvbyBzbWFsbCBudW1iZXIgb2YgbW9kZWxzLCBza2lwIHRoZSBkaWZmIHZlY3RvcgogIGlmICgoc3luZXJneS5zZXQubW9kZWxzLm51bSA8PSAzKSB8IChzeW5lcmd5LnNldC5tb2RlbHMubnVtIDw9IDMpKSAKICAgIG5leHQKICAKICAjIGdldCB0aGUgZGlmZgogIGRpZmYuc3RhdGUuYWsucGQgPSBnZXRfYXZnX2FjdGl2aXR5X2RpZmZfYmFzZWRfb25fc3luZXJneV9zZXRfY21wKHN5bmVyZ3kuc2V0LnN0ciwgc3luZXJneS5zdWJzZXQuc3RyLCByYW5kb20ubW9kZWwucHJlZGljdGlvbnMsIHJhbmRvbS5tb2RlbHMuc3RhYmxlLnN0YXRlKQogIGRpZmYubGluay5hay5wZCA9IGdldF9hdmdfbGlua19vcGVyYXRvcl9kaWZmX2Jhc2VkX29uX3N5bmVyZ3lfc2V0X2NtcChzeW5lcmd5LnNldC5zdHIsIHN5bmVyZ3kuc3Vic2V0LnN0ciwgcmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zLCByYW5kb20ubW9kZWxzLmxpbmsub3BlcmF0b3IpCiAgCiAgZGlmZi5zdGF0ZS5saXN0LnJhbmRvbVtbcGFzdGUwKHN5bmVyZ3kuc2V0LCAiIHZzICIsIHN5bmVyZ3kuc3Vic2V0KV1dID0gZGlmZi5zdGF0ZS5hay5wZAogIGRpZmYubGluay5saXN0LnJhbmRvbVtbcGFzdGUwKHN5bmVyZ3kuc2V0LCAiIHZzICIsIHN5bmVyZ3kuc3Vic2V0KV1dID0gZGlmZi5saW5rLmFrLnBkCn0KCmRpZmYuc3RhdGUubWF0LnJhbmRvbSA9IGRvLmNhbGwocmJpbmQsIGRpZmYuc3RhdGUubGlzdC5yYW5kb20pCmRpZmYubGluay5tYXQucmFuZG9tID0gZG8uY2FsbChyYmluZCwgZGlmZi5saW5rLmxpc3QucmFuZG9tKQpgYGAKCjxkaXYgaWQ9IlRhYmxlMyI+PC9kaXY+CmBgYHtyIFRhYmxlIDMsIGNhY2hlPVRSVUV9CmNhcHRpb24udGl0bGUuMyA9ICJUYWJsZSAzOiBBdmVyYWdlIGFjdGl2aXR5IGRpZmZlcmVuY2UgdmFsdWVzIGFjcm9zcyBhbGwgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgKEFLLVBEKSIKZGF0YXRhYmxlKGRhdGEgPSBkaWZmLnN0YXRlLm1hdC5yYW5kb21bLCBjKCJTUkMiLCAiQ1NLIiwgIk1FS19mIiwgIlNUQVQxIiwgIlBUUE42IildLCBvcHRpb25zID0gbGlzdCgKICAgIHNlYXJjaGluZyA9IEZBTFNFLCBwYWdlTGVuZ3RoID0gNSwgbGVuZ3RoTWVudSA9IGMoNSwgMTApKSwKICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbihjYXB0aW9uLnRpdGxlLjMsIHN0eWxlPSJjb2xvcjojZGQ0ODE0OyBmb250LXNpemU6IDE4cHgiKSkgJT4lIAogICAgICBmb3JtYXRSb3VuZCgxOm5jb2woZGlmZi5zdGF0ZS5tYXQucmFuZG9tKSwgZGlnaXRzID0gMykKYGBgCgo8ZGl2IGlkPSJUYWJsZTQiPjwvZGl2PgpgYGB7ciBUYWJsZSA0LCBjYWNoZT1UUlVFfQpjYXB0aW9uLnRpdGxlLjQgPSAiVGFibGUgNDogQXZlcmFnZSBsaW5rIG9wZXJhdG9yIGRpZmZlcmVuY2UgdmFsdWVzIGFjcm9zcyBhbGwgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgKEFLLVBEKSIKZGF0YXRhYmxlKGRhdGEgPSBkaWZmLmxpbmsubWF0LnJhbmRvbVssIGMoIlNSQyIsICJSQUNfZiIsICJNRUtfZiIsICJTVEFUMSIsICJQVEVOIildLCBvcHRpb25zID0gbGlzdCgKICAgIHNlYXJjaGluZyA9IEZBTFNFLCBwYWdlTGVuZ3RoID0gNSwgbGVuZ3RoTWVudSA9IGMoNSwgMTApKSwKICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbihjYXB0aW9uLnRpdGxlLjQsIHN0eWxlPSJjb2xvcjojZGQ0ODE0OyBmb250LXNpemU6IDE4cHgiKSkgJT4lIAogICAgICBmb3JtYXRSb3VuZCgxOm5jb2woZGlmZi5saW5rLm1hdC5yYW5kb20pLCBkaWdpdHMgPSAzKQpgYGAKClVzaW5nIHRoZSBtYXRyaXggZnJvbSBbVGFibGUgM10oI1RhYmxlMykgKHdoZXJlIHdlIHNob3cganVzdCAkNSQgbm9kZXMpLCB3ZSBjb3VudCBwZXIgbmV0d29yayBub2RlICoqdGhlIG51bWJlciBvZiB0aW1lcyB0aGF0IHRoZSBub2RlJ3MgYXZlcmFnZSBhY3Rpdml0eSBkaWZmZXJlbmNlIHZhbHVlIGhhcyBzdXJwYXNzZWQgYSBzcGVjaWZpZWQgdGhyZXNob2xkKiogLSBpLmUuIHRoZSBudW1iZXIgb2YgdGltZXMgaXQgaGFzIGJlZW4gZm91bmQgYXMgaW1wb3J0YW50IChhIGJpb21hcmtlcikgYWNyb3NzIGFsbCB0aGUgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgKHNvIHRoZSBtb3JlIHRoZSBiZXR0ZXIpOgpgYGB7ciBBY3Rpdml0eSBTdGF0ZSBCaW9tYXJrZXIgY29udGluZ2VuY3kgdGFibGUgYWNyb3NzIGFsbCBjb21wYXJpc29uIHNldHMgKEFLLVBEIC0gUmFuZG9tKSwgcmVzdWx0cz0nYXNpcyd9CmJpb21hcmtlci5zdGF0ZS5tYXQucmFuZG9tID0gYmluYXJpemVfdG9fdGhyZXMobWF0ID0gZGlmZi5zdGF0ZS5tYXQucmFuZG9tLCB0aHJlcyA9IDAuNykKCmJpb21hcmtlci5zdGF0ZS5jb3VudHMucmFuZG9tID0gY29sU3VtcyhiaW9tYXJrZXIuc3RhdGUubWF0LnJhbmRvbSkKcHJldHR5X3ByaW50X3ZlY3Rvcl9uYW1lc19hbmRfdmFsdWVzKHRhYmxlKGJpb21hcmtlci5zdGF0ZS5jb3VudHMucmFuZG9tKSkKYGBgCgpTbywgdGhlcmUgYXJlICQyJCBub2RlcyB0aGF0IHdlcmUgZm91bmQgYXMgKiphY3Rpdml0eSBzdGF0ZSBiaW9tYXJrZXJzKiogJDYkIHRpbWVzIGFjcm9zcyBhbGwgc3luZXJneSBzZXQgY29tcGFyaXNvbnMuIFRoZXNlIG5vZGVzIGFyZToKCmBgYHtyIEV4dHJhIGFjdGl2aXR5IHN0YXRlIGJpb21hcmtlcnMgKEFLLVBEIC0gUmFuZG9tKSwgcmVzdWx0cz0nYXNpcyd9CnByZXR0eV9wcmludF92ZWN0b3JfbmFtZXMoYmlvbWFya2VyLnN0YXRlLmNvdW50cy5yYW5kb21bYmlvbWFya2VyLnN0YXRlLmNvdW50cy5yYW5kb20gPT0gNl0pCmBgYAoKQnV0IHRoZSB0b3RhbCBudW1iZXIgb2YgY29tcGFyaXNvbnMgd2FzIGByIG5yb3coYmlvbWFya2VyLnN0YXRlLm1hdC5yYW5kb20pYCBzbyB3ZSBjb3VsZCBhcmd1ZSB0aGF0ICoqdGhpcyBpcyBub3QgYSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHJlc3VsdCoqLCB3aGljaCBjYW4gYmUgc2VlbiBjbGVhcmx5IGluIHRoZSBuZXh0IGdyYXBoIHdoZXJlIHdlIHZpc3VhbGl6ZSB0aGUgKiphdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmZlcmVuY2UgYWNyb3NzIGFsbCBzeW5lcmd5IGNvbXBhcmlzb24gc2V0cyoqIGZyb20gW1RhYmxlIDNdKCNUYWJsZTMpOgoKYGBge3IgQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChBSy1QRCwgcmFuZG9tIG1vZGVscyksIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGxvdF9hdmdfc3RhdGVfZGlmZl9ncmFwaChuZXQsIGRpZmYgPSBjb2xNZWFucyhkaWZmLnN0YXRlLm1hdC5yYW5kb20pLCBsYXlvdXQgPSBuaWNlLmxheW91dCwgCiAgdGl0bGUgPSAiQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChBSy1QRCkiKQpgYGAKClVzaW5nIHRoZSBtYXRyaXggZnJvbSBbVGFibGUgNF0oI1RhYmxlNCksIHdlIGNvdW50IHBlciBuZXR3b3JrIG5vZGUgKip0aGUgbnVtYmVyIG9mIHRpbWVzIHRoYXQgdGhlIG5vZGUncyBhdmVyYWdlIGxpbmsgb3BlcmF0b3IgZGlmZmVyZW5jZSB2YWx1ZSBoYXMgc3VycGFzc2VkIGEgc3BlY2lmaWVkIHRocmVzaG9sZCoqIC0gaS5lLiB0aGUgbnVtYmVyIG9mIHRpbWVzIGl0IGhhcyBiZWVuIGZvdW5kIGFzIGltcG9ydGFudCAoYSBiaW9tYXJrZXIpIGFjcm9zcyBhbGwgdGhlIHN5bmVyZ3kgc2V0IGNvbXBhcmlzb25zIChzbyB0aGUgbW9yZSB0aGUgYmV0dGVyKToKCmBgYHtyIExpbmsgT3BlcmF0b3IgQmlvbWFya2VyIGNvbnRpbmdlbmN5IHRhYmxlIGFjcm9zcyBhbGwgY29tcGFyaXNvbiBzZXRzIChBSy1QSyAtIFJhbmRvbSksIHJlc3VsdHM9J2FzaXMnfQpiaW9tYXJrZXIubGluay5tYXQucmFuZG9tID0gYmluYXJpemVfdG9fdGhyZXMobWF0ID0gZGlmZi5saW5rLm1hdC5yYW5kb20sIHRocmVzID0gMC43KQoKYmlvbWFya2VyLmxpbmsuY291bnRzLnJhbmRvbSA9IGNvbFN1bXMoYmlvbWFya2VyLmxpbmsubWF0LnJhbmRvbSkKcHJldHR5X3ByaW50X3ZlY3Rvcl9uYW1lc19hbmRfdmFsdWVzKHRhYmxlKGJpb21hcmtlci5saW5rLmNvdW50cy5yYW5kb20pKQpgYGAKClNvLCB0aGVyZSB3YXMgYSBub2RlIHRoYXQgd2FzIGZvdW5kICQ3JCB0aW1lcyAob3V0IG9mIGEgdG90YWwgb2YgYHIgbnJvdyhiaW9tYXJrZXIubGluay5tYXQucmFuZG9tKWAsICoqc28gbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQqKikgYXMgYSBsaW5rIG9wZXJhdG9yIGJpb21hcmtlcnMgYWNyb3NzIGFsbCBzeW5lcmd5IHNldCBjb21wYXJpc29uczoKIApgYGB7ciBFeHRyYSBsaW5rIG9wZXJhdG9yIGJpb21hcmtlcnMgKEFLLVBEIC0gUmFuZG9tKSwgcmVzdWx0cz0nYXNpcyd9CnByZXR0eV9wcmludF92ZWN0b3JfbmFtZXMoYmlvbWFya2VyLmxpbmsuY291bnRzLnJhbmRvbVtiaW9tYXJrZXIubGluay5jb3VudHMucmFuZG9tID09IDddKQpgYGAKCldlIHZpc3VhbGl6ZSB0aGUgKiphdmVyYWdlIGxpbmsgb3BlcmF0b3IgZGlmZmVyZW5jZSBhY3Jvc3MgYWxsIHN5bmVyZ3kgY29tcGFyaXNvbiBzZXRzKiogZnJvbSBbVGFibGUgNF0oI1RhYmxlNCk6CmBgYHtyIEF2ZXJhZ2UgbGluayBvcGVyYXRvciBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChBSy1QRCwgcmFuZG9tKSwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpwbG90X2F2Z19saW5rX29wZXJhdG9yX2RpZmZfZ3JhcGgobmV0LCBkaWZmID0gY29sTWVhbnMoZGlmZi5saW5rLm1hdC5yYW5kb20pLCBsYXlvdXQgPSBuaWNlLmxheW91dCwgCiAgdGl0bGUgPSAiQXZlcmFnZSBsaW5rIG9wZXJhdG9yIGRpZmYgYWNyb3NzIGFsbCBzeW5lcmd5IHN1YnNldHMgKEFLLVBEKSIpCmBgYAoKIyMjIEFLLVBEIGluIG90aGVyIGNlbGwgbGluZXMgey19CgpUaG91Z2ggdGhlIGBBSy1QRGAgc3luZXJneSB3YXMgb2JzZXJ2ZWQgYW5kIHByZWRpY3RlZCBpbiB0aGUgQTQ5OCBtb2RlbCBkYXRhc2V0LCB3ZSBpbnZlc3RpZ2F0ZSBpdHMgYmlvbWFya2VycyBpbiB0aGUgb3RoZXIgY2VsbCBsaW5lcyB3aGVyZSAqKml0IHdhcyBwcmVkaWN0ZWQgYXMgYSBGYWxzZSBQb3NpdGl2ZSAoRlApIHN5bmVyZ3kgKHByZWRpY3RlZCBieSB0aGUgbW9kZWxzIGJ1dCBub3Qgb2JzZXJ2ZWQgaW4gdGhlIGV4cGVyaW1lbnRzKSoqLiAKVGh1cywgd2UgY2FuIHN0aWxsIHNlZSBpZiB0aGVyZSBhcmUgYW55IGNvbW1vbiAoYWN0aXZpdHkgc3RhdGUgYW5kIGxpbmsgb3BlcmF0b3IpIGJpb21hcmtlcnMgZm9yIGBBSy1QRGAgYWNyb3NzIHRoZSBhbGwgdGhlIGNlbGwtc3BlY2lmaWMgbW9kZWxzIGJ5IGNvbnRyYXN0aW5nIGluIGVhY2ggY2VsbCBsaW5lIHRoZSBtb2RlbHMgdGhhdCBwcmVkaWN0ZWQgYEFLLVBEYCAoaWYgdGhlcmUgd2VyZSBhbnkpIHZzIHRoZSBtb2RlbHMgdGhhdCBkaWQgbm90OgoKYGBge3IgQUstUEQgYXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmZXJlbmNlIGluIGFsbCBjZWxsIGxpbmVzIGV4Y2VwdCBBNDk4fQpkcnVnLmNvbWIgPSAiQUstUEQiCgphay5wZC5kaWZmLnN0YXRlLmxpc3QgPSBsaXN0KCkKYWsucGQuZGlmZi5saW5rLmxpc3QgPSBsaXN0KCkKCmZvciAoY2VsbC5saW5lIGluIGNlbGwubGluZXMpIHsKICBpZiAoY2VsbC5saW5lID09ICJBNDk4IikgCiAgICBuZXh0CiAgbW9kZWwucHJlZGljdGlvbnMgPSBtb2RlbC5wcmVkaWN0aW9ucy5wZXIuY2VsbC5saW5lW1tjZWxsLmxpbmVdXQogIAogIGFrLnBkLmRpZmYuc3RhdGUubGlzdFtbY2VsbC5saW5lXV0gPSBnZXRfYXZnX2FjdGl2aXR5X2RpZmZfYmFzZWRfb25fc3BlY2lmaWNfc3luZXJneV9wcmVkaWN0aW9uKAogICAgbW9kZWwucHJlZGljdGlvbnMsIG1vZGVscy5zdGFibGUuc3RhdGUgPSBtb2RlbHMuc3RhYmxlLnN0YXRlLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dLCBkcnVnLmNvbWIpCiAgYWsucGQuZGlmZi5saW5rLmxpc3RbW2NlbGwubGluZV1dID0gZ2V0X2F2Z19saW5rX29wZXJhdG9yX2RpZmZfYmFzZWRfb25fc3BlY2lmaWNfc3luZXJneV9wcmVkaWN0aW9uKAogICAgbW9kZWwucHJlZGljdGlvbnMsIG1vZGVscy5saW5rLm9wZXJhdG9yID0gbW9kZWxzLmxpbmsub3BlcmF0b3JzLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dLCBkcnVnLmNvbWIpCiAgCiAgIyBTZWUgdGhlICNtb2RlbHMgdGhhdCBwcmVkaWN0ZWQgdnMgI21vZGVscyB0aG9zZSB0aGF0IGRpZCBub3QKICAjIHByaW50KHBhc3RlMChzdW0obW9kZWwucHJlZGljdGlvbnNbLGRydWcuY29tYl0sIG5hLnJtID0gVCksICIgdnMgIiwgc3VtKCFtb2RlbC5wcmVkaWN0aW9uc1ssZHJ1Zy5jb21iXSwgbmEucm0gPSBUKSkpCn0KCmFrLnBkLmRpZmYuc3RhdGUubWF0ID0gZG8uY2FsbChyYmluZCwgYWsucGQuZGlmZi5zdGF0ZS5saXN0KQphay5wZC5kaWZmLmxpbmsubWF0ID0gZG8uY2FsbChyYmluZCwgYWsucGQuZGlmZi5saW5rLmxpc3QpCmBgYAoKPGRpdiBjbGFzcz0iYmx1ZS1ib3giPgpgRVJLX2ZgIHdhcyB0aGUgb25lIG5vZGUgdGhhdCB3YXMgZm91bmQgYXMgYW4gKiphY3Rpdml0eSBzdGF0ZSAmIGxpbmsgb3BlcmF0b3IgYEFLLVBEYCBiaW9tYXJrZXIqKiBpbiBhbGwgY2VsbCBsaW5lcwo8L2Rpdj4KCmBgYHtyIEFjdGl2aXR5IHN0YXRlIGFuZCBsaW5rIG9wZXJhdG9yIGJpb21hcmtlcnMgKEFLLVBELCBhbGwgY2VsbCBsaW5lcyBleGNlcHQgQTQ5OCksIHJlc3VsdHM9J2FzaXMnfQphay5wZC5iaW9tYXJrZXIuc3RhdGUubWF0ID0gYmluYXJpemVfdG9fdGhyZXMobWF0ID0gYWsucGQuZGlmZi5zdGF0ZS5tYXQsIHRocmVzID0gMC43KQphay5wZC5iaW9tYXJrZXIubGluay5tYXQgID0gYmluYXJpemVfdG9fdGhyZXMobWF0ID0gYWsucGQuZGlmZi5saW5rLm1hdCwgdGhyZXMgPSAwLjcpCgphay5wZC5iaW9tYXJrZXIuc3RhdGUubWF0LmNvdW50cyA9IGNvbFN1bXMoYWsucGQuYmlvbWFya2VyLnN0YXRlLm1hdCkKYWsucGQuYmlvbWFya2VyLmxpbmsubWF0LmNvdW50cyA9IGNvbFN1bXMoYWsucGQuYmlvbWFya2VyLmxpbmsubWF0KQoKI3ByZXR0eV9wcmludF92ZWN0b3JfbmFtZXNfYW5kX3ZhbHVlcyh0YWJsZShhay5wZC5iaW9tYXJrZXIuc3RhdGUubWF0LmNvdW50cykpCiNwcmV0dHlfcHJpbnRfdmVjdG9yX25hbWVzX2FuZF92YWx1ZXModGFibGUoYWsucGQuYmlvbWFya2VyLmxpbmsubWF0LmNvdW50cykpCgpwcmV0dHlfcHJpbnRfdmVjdG9yX25hbWVzKGFrLnBkLmJpb21hcmtlci5zdGF0ZS5tYXQuY291bnRzW2FrLnBkLmJpb21hcmtlci5zdGF0ZS5tYXQuY291bnRzID09IDddKQpwcmV0dHlfcHJpbnRfdmVjdG9yX25hbWVzKGFrLnBkLmJpb21hcmtlci5saW5rLm1hdC5jb3VudHNbYWsucGQuYmlvbWFya2VyLmxpbmsubWF0LmNvdW50cyA9PSA2XSkKYGBgCgojIyBQRC1QSSBhY3Rpdml0eSBiaW9tYXJrZXJzIHstfQoKQXMgb2JzZXJ2ZWQgaW4gdGhlIFtoZWF0bWFwIGFib3ZlXSgjb2JzZXJ2ZWQtc3luZXJnaWVzKSwgdGhlIGBQRC1QSWAgc3luZXJneSB3YXMgcHJlZGljdGVkIGJ5IGJvdGggdGhlICoqY2VsbCBzcGVjaWZpYyoqIGFuZCAqKnJhbmRvbSBtb2RlbHMqKiBpbiB0aGUgYEE0OThgIGNlbGwgbGluZS4KCiMjIyBDZWxsLXNwZWNpZmljIChBNDk4KSB7LX0KCmBgYHtyIFBELVBJIGRpZmYgKENlbGwgc3BlY2lmaWMgbW9kZWxzIC0gQTQ5OCl9ClBELlBJLmF2Zy5zdGF0ZS5kaWZmLmNlbGwuc3BlY2lmaWMgPSBjZWxsLnNwZWNpZmljLnN5bmVyZ3kuYW5hbHlzaXMucmVzJEE0OTgkZGlmZi5zdGF0ZS5zeW5lcmdpZXMubWF0WyJQRC1QSSIsXQojUEQuUEkuYXZnLmxpbmsuZGlmZi5jZWxsLnNwZWNpZmljICA9IGNlbGwuc3BlY2lmaWMuc3luZXJneS5hbmFseXNpcy5yZXMkQTQ5OCRkaWZmLmxpbmsuc3luZXJnaWVzLm1hdFsiUEQtUEkiLCBdCmBgYAoKV2Ugd2lsbCBub3cgdmlzdWFsaXplIHRoZSBub2RlcyBhdmVyYWdlIHN0YXRlIGRpZmZlcmVuY2VzIGluIGEgbmV0d29yayBncmFwaC4gTm90ZSB0aGF0IHRoZSAqKmdvb2QgbW9kZWxzKiogYXJlIHRob3NlIHRoYXQgcHJlZGljdGVkIHRoZSBgUEQtUElgIGRydWcgY29tYmluYXRpb24gdG8gYmUgKnN5bmVyZ2lzdGljKiBhbmQgd2VyZSBjb250cmFzdGVkIHRvIHRob3NlIHRoYXQgcHJlZGljdGVkIGl0IHRvIGJlICphbnRhZ29uaXN0aWMqICgqKmJhZCBtb2RlbHMqKikuIFRoZSBudW1iZXIgb2YgbW9kZWxzIGluIGVhY2ggY2F0ZWdvcnkgd2VyZToKCmBgYHtyIENvdW50IGdvb2QgdnMgYmFkIG1vZGVscyBmb3IgUEQtUEkgc3luZXJneSAoY2VsbCBzcGVjaWZpYyBtb2RlbHMpLCByZXN1bHRzPSdhc2lzJ30KbW9kZWwucHJlZGljdGlvbnMgPSBtb2RlbC5wcmVkaWN0aW9ucy5wZXIuY2VsbC5saW5lW1siQTQ5OCJdXQptb2RlbHMuc3RhYmxlLnN0YXRlID0gbW9kZWxzLnN0YWJsZS5zdGF0ZS5wZXIuY2VsbC5saW5lW1siQTQ5OCJdXQpkcnVnLmNvbWIgPSAiUEQtUEkiCgpnb29kLm1vZGVscy5udW0gPSBzdW0obW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdID09IDEgJiAhaXMubmEobW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdKSkKIyB1bmlxdWUgZ29vZCBtb2RlbHMKIyBtb2RlbHMubGluay5vcGVyYXRvciA9IG1vZGVscy5saW5rLm9wZXJhdG9ycy5wZXIuY2VsbC5saW5lJEE0OTgKIyBucm93KHVuaXF1ZShtb2RlbHMubGluay5vcGVyYXRvclttb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0gPT0gMSAmICFpcy5uYShtb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0pLCBdKSkKYmFkLm1vZGVscy5udW0gID0gc3VtKG1vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSA9PSAwICYgIWlzLm5hKG1vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSkpCgpwcmV0dHlfcHJpbnRfc3RyaW5nKHBhc3RlMCgiTnVtYmVyIG9mICdnb29kJyBtb2RlbHMgKFBELVBJIHN5bmVyZ2lzdGljKSBpbiB0aGUgQTQ5OCBjZWxsIGxpbmU6ICIsIGdvb2QubW9kZWxzLm51bSkpCnByZXR0eV9wcmludF9zdHJpbmcocGFzdGUwKCJOdW1iZXIgb2YgJ2JhZCcgbW9kZWxzIChQRC1QSSBhbnRhZ29uaXN0aWMpIGluIHRoZSBBNDk4IGNlbGwgbGluZTogIiwgYmFkLm1vZGVscy5udW0pKQpgYGAKCmBgYHtyIFBELVBJIGFjdGl2aXR5IHN0YXRlIGJpb21hcmtlcnMgKENlbGwgc3BlY2lmaWMgbW9kZWxzIC0gQTQ5OCksIGNhY2hlID0gVFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpwbG90X2F2Z19zdGF0ZV9kaWZmX2dyYXBoKG5ldCwgZGlmZiA9IFBELlBJLmF2Zy5zdGF0ZS5kaWZmLmNlbGwuc3BlY2lmaWMsIAogIGxheW91dCA9IG5pY2UubGF5b3V0LCB0aXRsZSA9ICJQRC1QSSBhY3Rpdml0eSBzdGF0ZSBiaW9tYXJrZXJzIChDZWxsIHNwZWNpZmljIG1vZGVscyAtIEE0OTgpIikKYGBgCgo8ZGl2IGlkPSJUZXh0MSI+IFdlIG5vdyB2aXN1YWxpemUgdGhlIG5vZGVzIGF2ZXJhZ2Ugc3RhdGUgZGlmZmVyZW5jZXMgaW4gYSBgZG90Y2hhcnRgIHdoZXJlIHdlIGNhbiBlYXNpbHkgaWRlbnRpZnkgdGhlICoqYWN0aXZlIGFuZCBpbmhpYml0ZWQgc3RhdGUgYmlvbWFya2VycyoqIHdpdGggdGhlIGRlZmluZWQgdGhyZXNob2xkcyAod2UgaGF2ZSBleGNsdWRlZCBub2RlcyB3aG9zZSBhYnNvbHV0ZSBhdmVyYWdlIGRpZmZlcmVuY2UgdmFsdWUgd2FzIGxlc3MgdGhhbiAkMC4wNSQpOgo8L2Rpdj4KYGBge3IgUEQtUEkgYWN0aXZlIHN0YXRlIGJpb21hcmtlcnMgKENlbGwgc3BlY2lmaWMgbW9kZWxzIC0gQTQ5OCksIGZpZy5hbGlnbj0nY2VudGVyJ30KZGYgPSBhcy5kYXRhLmZyYW1lKFBELlBJLmF2Zy5zdGF0ZS5kaWZmLmNlbGwuc3BlY2lmaWMpCmNvbG5hbWVzKGRmKSA9ICJhdmcuc3RhdGUuZGlmZiIKZGYgPSBkZiAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJub2RlcyIpICU+JQogIGZpbHRlcihhdmcuc3RhdGUuZGlmZiA+IDAuMDUgfCBhdmcuc3RhdGUuZGlmZiA8IC0wLjA1KQoKZ2dkb3RjaGFydChkZiwgeCA9ICJub2RlcyIsIHkgPSAiYXZnLnN0YXRlLmRpZmYiLAogIHRpdGxlID0gIkF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZmVyZW5jZSAoR29vZCAtIGJhZCkgLSBBNDk4IGNlbGwgbGluZSIsIAogIGxhYmVsID0gIm5vZGVzIiwgZm9udC5sYWJlbCA9IGxpc3Qoc2l6ZSA9IDExLCBjb2xvciA9ICJibHVlIiksCiAgbGFiZWwuc2VsZWN0ID0gbGlzdChjcml0ZXJpYSA9ICJgeWAgPj0gMC43IHwgYHlgIDw9IC0wLjUiKSwgCiAgcmVwZWwgPSBUUlVFLCBsYWJlbC5yZWN0YW5nbGUgPSBUUlVFLAogIHhsYWIgPSAiTm9kZXMiLCB5bGFiID0gIkF2ZXJhZ2UgQWN0aXZpdHkgU3RhdGUiLCAKICB5bGltID0gYygtMSwxKSwgYWRkID0gInNlZ21lbnRzIikgKyAKICAgIGZvbnQoIngudGV4dCIsIHNpemUgPSA4KSArCiAgICBmb250KCJ0aXRsZSIsIHNpemUgPSAxNCkgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC0wLjcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsgCiAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC43LCBsaW5ldHlwZSA9ICIwLjciKSwgY29sb3IgPSAicmVkIikgKwogICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKG5hbWUgPSAiVGhyZXNob2xkIiwgdmFsdWVzID0gMiwgCiAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoY29sb3IgPSAicmVkIikpKSArIAogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsMC43KSkgKyAKICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IGMoMTgsIDM1KSwgeSA9IC0wLjksIGxhYmVsID0gYygiR29vZCBtb2RlbHM6IFBELVBJIHN5bmVyZ3ksIiwgIkJhZCBNb2RlbHM6IFBELVBJIGFudGFnb25pc20iKSkKYGBgCgo8ZGl2IGNsYXNzPSJvcmFuZ2UtYm94Ij4KTm90ZSB0aGUgYm9vbGVhbiBlcXVhdGlvbjogYFBQTTFBICo9ICggUFRFTiApYC4gVGhpcyBtZWFucyB0aGF0IGBQVEVOYCBpcyAqKnRoZSBvbmx5IGluaGliaXRlZCBub2RlIG9mIGludGVyZXN0KioKPC9kaXY+CjwvYnI+CgpXZSBub3cgY2hlY2sgdGhlIGBQVEVOYCBpbiB0aGUgYE1DTFBgIGRhdGFzZXQ6CmBgYHtyIE1DTFAgRGF0YSBWaXN1YWxpemF0aW9uIChQVEVOKSwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpwdGVuLmRhdGEgPSBtY2xwLmRhdGEgJT4lIHNlbGVjdChTYW1wbGVfTmFtZSwgUFRFTikgJT4lIG5hLm9taXQoKQoKIyBmaW5kIHRoZSBhY3Rpdml0eSBjbGFzc2VzICgzIGNsYXNzZXM6IGxvdyBhY3Rpdml0eSwgbm8gYWN0aXZpdHksIGhpZ2ggYWN0aXZpdHkpCnJlcyA9IENrbWVhbnMuMWQuZHAoeCA9IHB0ZW4uZGF0YSRQVEVOLCBrID0gMykKYWN0aXZpdHkgPSBhcy5mYWN0b3IocmVzJGNsdXN0ZXIpCmxldmVscyhhY3Rpdml0eSkgPSBjKCJsb3ciLCAibWVkaXVtIiwgImhpZ2giKQpwdGVuLmRhdGEgPSBjYmluZChwdGVuLmRhdGEsIGFjdGl2aXR5KQoKZ2dkb3RjaGFydChkYXRhID0gcHRlbi5kYXRhLCB4ID0gIlNhbXBsZV9OYW1lIiwgeSA9ICJQVEVOIiwgCiAgdGl0bGUgPSAiUFRFTiBzaWduYWxpbmcgYWNyb3NzIGFsbCBjZWxsIGxpbmVzIGluIE1DTFAgRGF0YXNldCIsCiAgY29sb3IgPSAiYWN0aXZpdHkiLCBwYWxldHRlID0gYygicmVkIiwgImdyZXkiLCAiZ3JlZW4iKSwgCiAgbGFiZWwgPSAiU2FtcGxlX05hbWUiLCBsYWJlbC5zZWxlY3QgPSBjZWxsLmxpbmVzLmluLm1jbHAsIHJlcGVsID0gVFJVRSwKICBhZGQgPSAic2VnbWVudHMiLCBsYWJlbC5yZWN0YW5nbGUgPSBUUlVFLAogIHhsYWIgPSAiQ2VsbCBMaW5lcyIsIHlsYWIgPSAiUFRFTiBTaWduYWxpbmciLAogIGFkZC5wYXJhbXMgPSBsaXN0KGNvbG9yID0gImFjdGl2aXR5IiwgcGFsZXR0ZSA9IGMoInJlZCIsICJncmV5IiwgImdyZWVuIikpKSArIAogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKPGRpdiBjbGFzcz0iYmx1ZS1ib3giPgpUaGUgYFBURU5gIG5vZGUgaXMgZm91bmQgaGlnaGx5IGV4cHJlc3NlZCBpbiB0aGUgYEE0OThgIGNlbGwgbGluZSBpbiB0aGUgYE1DTFBgIGRhdGFzZXQsIGJ1dCBmb3IgdGhlIG1vZGVscyB0byBwcmVkaWN0IHRoZSBgUEQtUElgIHN5bmVyZ3kgd2UgZm91bmQgdGhhdCB0aGUgbm9kZSBuZWVkcyB0byBiZSBpbiBhbiAqKmluaGliaXRlZCBzdGF0ZSoqIChbc2VlIGRvdGNoYXJ0IGhlcmVdKCNUZXh0MSkpLiBUaGlzIGNvcnJlbGF0ZXMgdGhvdWdoIHdpdGggdGhlIGZhY3QgdGhhdCBgUFRFTmAgaXMgZm91bmQgaW5oaWJpdGVkIGluIG1hbnkgY2FuY2VyIHR5cGVzIChbYXJ0aWNsZV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNjk0NjE4MS8pKS4gVGhlIG92ZXJleHByZXNzaW9uIG9mIHRoZSBgRVJLX2ZgIG5vZGUgaXMgYWxzbyBhIHJlc3VsdCBmcm9tIHRoaXMgYW5hbHlzaXMuCjwvZGl2Pgo8YnI+CgojIyMjIFN5bmVyZ3kgU3Vic2V0cyBBbmFseXNpcyB7I3NzMn0KCkl0IHdpbGwgYmUgaW50ZXJlc3RpbmcgdG8gZmluZCAqKmFsbCB0aGUgcG9zc2libGUgc3luZXJneSBzZXRzIGFuZCBzdWJzZXRzIHRoYXQgaW5jbHVkZSB0aGUgYFBELVBJYCBhcyB0aGUgZXh0cmEgc3luZXJneSoqLiAKVXNpbmcgdGhlc2Ugc3luZXJneSBzZXRzLCBtb2RlbHMgdGhhdCBwcmVkaWN0IGEgc2V0IG9mIHN5bmVyZ2llcyB3aWxsIGJlIGNvbnRyYXN0ZWQgdG8gbW9kZWxzIHRoYXQgcHJlZGljdGVkIHRoZSBzYW1lIHNldCB3aXRoIHRoZSBhZGRpdGlvbiBvZiB0aGUgZXh0cmEgYFBELVBJYCBzeW5lcmd5LiAKVGh1cyB3ZSBjb3VsZCBmaW5kICoqc3luZXJneSBiaW9tYXJrZXJzIHRoYXQgYWxsb3cgYWxyZWFkeSBnb29kIHByZWRpY3RpbmcgbW9kZWxzIHRvIHByZWRpY3QgdGhlIGFkZGl0aW9uYWwgc3luZXJneSBvZiBpbnRlcmVzdCoqLgpUaGlzIGludmVzdGlnYXRpb24gd2lsbCBhbGxvdyB1cyB0aHVzIHRvIHJlZmluZSB0aGUgYWN0aXZpdHkgc3RhdGUgYmlvbWFya2VycyB3ZSBmb3VuZCBhYm92ZS4KCldlIGZpcnN0IGNvbnN0cnVjdCBhIG1hdHJpeCB3aGVyZSAqKmVhY2ggcm93IGlzIGEgc2V0IHZzIHN1YnNldCBhdmVyYWdlIGFjdGl2aXR5IGRpZmZlcmVuY2UgdmVjdG9yIG9mIHRoZSBuZXR3b3JrIG5vZGVzKio6CjxkaXYgaWQ9IlRhYmxlNSI+PC9kaXY+CmBgYHtyIFN5bmVyZ3kgU3Vic2V0cyBDb21wYXJpc29uIFBELVBJIChDZWxsLXNwZWNpZmljIC0gQTQ5OCksIGNhY2hlPVRSVUV9Cm1vZGVsLnByZWRpY3Rpb25zID0gbW9kZWwucHJlZGljdGlvbnMucGVyLmNlbGwubGluZSRBNDk4Cm1vZGVscy5zdGFibGUuc3RhdGUgPSBtb2RlbHMuc3RhYmxlLnN0YXRlLnBlci5jZWxsLmxpbmUkQTQ5OAptb2RlbHMubGluay5vcGVyYXRvciA9IG1vZGVscy5saW5rLm9wZXJhdG9ycy5wZXIuY2VsbC5saW5lJEE0OTgKCnJlcyA9IGdldF9zeW5lcmd5X2NvbXBhcmlzb25fc2V0cyhjZWxsLnNwZWNpZmljLnN5bmVyZ3kuYW5hbHlzaXMucmVzJEE0OTgkc3luZXJneS5zdWJzZXQuc3RhdHMpClBELlBJLnJlcyA9IHJlcyAlPiUgZmlsdGVyKHN5bmVyZ2llcyA9PSAiUEQtUEkiKQoKZGlmZi5zdGF0ZS5saXN0ID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpucm93KFBELlBJLnJlcykpIHsKICBzeW5lcmd5LnNldCAgICA9IFBELlBJLnJlc1tpLCAyXQogIHN5bmVyZ3kuc3Vic2V0ID0gUEQuUEkucmVzW2ksIDNdCiAgCiAgc3luZXJneS5zZXQuc3RyICAgID0gdW5saXN0KHN0cnNwbGl0KHggPSBzeW5lcmd5LnNldCwgc3BsaXQgPSAiLCIpKQogIHN5bmVyZ3kuc3Vic2V0LnN0ciA9IHVubGlzdChzdHJzcGxpdCh4ID0gc3luZXJneS5zdWJzZXQsIHNwbGl0ID0gIiwiKSkKICAKICAjIGNvdW50IG1vZGVscwogIHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0gPSBjb3VudF9tb2RlbHNfdGhhdF9wcmVkaWN0X3N5bmVyZ2llcyhzeW5lcmd5LnNldC5zdHIsIG1vZGVsLnByZWRpY3Rpb25zKQogIHN5bmVyZ3kuc3Vic2V0Lm1vZGVscy5udW0gPSBjb3VudF9tb2RlbHNfdGhhdF9wcmVkaWN0X3N5bmVyZ2llcyhzeW5lcmd5LnN1YnNldC5zdHIsIG1vZGVsLnByZWRpY3Rpb25zKQogIAogICMgaWYgdG9vIHNtYWxsIG51bWJlciBvZiBtb2RlbHMsIHNraXAgdGhlIGRpZmYgdmVjdG9yCiAgIyBpZiAoKHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0gPD0gMykgfCAoc3luZXJneS5zZXQubW9kZWxzLm51bSA8PSAzKSkgCiAgIyAgIG5leHQKICAKICAjIGdldCB0aGUgZGlmZgogIGRpZmYuc3RhdGUuUEQuUEkgPSBnZXRfYXZnX2FjdGl2aXR5X2RpZmZfYmFzZWRfb25fc3luZXJneV9zZXRfY21wKHN5bmVyZ3kuc2V0LnN0ciwgc3luZXJneS5zdWJzZXQuc3RyLCBtb2RlbC5wcmVkaWN0aW9ucywgbW9kZWxzLnN0YWJsZS5zdGF0ZSkKICBkaWZmLnN0YXRlLmxpc3RbW3Bhc3RlMChzeW5lcmd5LnNldCwgIiB2cyAiLCBzeW5lcmd5LnN1YnNldCldXSA9IGRpZmYuc3RhdGUuUEQuUEkKfQoKZGlmZi5zdGF0ZS5tYXQgPSBkby5jYWxsKHJiaW5kLCBkaWZmLnN0YXRlLmxpc3QpCgpjYXB0aW9uLnRpdGxlLjUgPSAiVGFibGUgNTogQXZlcmFnZSBhY3Rpdml0eSBkaWZmZXJlbmNlIHZhbHVlcyBhY3Jvc3MgYWxsIHN5bmVyZ3kgc2V0IGNvbXBhcmlzb25zIChQRC1QSSkiCmRhdGF0YWJsZShkYXRhID0gZGlmZi5zdGF0ZS5tYXRbLCBjKCJTUkMiLCAiQ1NLIiwgIk1FS19mIiwgIlNUQVQxIiwgIlBUUE42IildLCBvcHRpb25zID0gbGlzdCgKICAgIHNlYXJjaGluZyA9IEZBTFNFLCBwYWdlTGVuZ3RoID0gNSwgbGVuZ3RoTWVudSA9IGMoNSwgMTApKSwKICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oY2FwdGlvbi50aXRsZS41LCBzdHlsZT0iY29sb3I6I2RkNDgxNDsgZm9udC1zaXplOiAxOHB4IikpICU+JSAKICBmb3JtYXRSb3VuZCgxOm5jb2woZGlmZi5zdGF0ZS5tYXQpLCBkaWdpdHMgPSAzKQpgYGAKCklmIHdlIHZpc3VhbGl6ZSB0aGUgKiphdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmZlcmVuY2UgYWNyb3NzIGFsbCBzeW5lcmd5IGNvbXBhcmlzb24gc2V0cyoqIGZyb20gW1RhYmxlIDVdKCNUYWJsZTUpIGluIGEgbmV0d29yayBncmFwaCwgd2Ugc2VlIHRoYXQgdGhlcmUgYXJlIG5vIG5vZGVzIHdpdGggc2lnbmlmaWNhbnQgYXZlcmFnZSBzdGF0ZSBkaWZmZXJlbmNlOgoKYGBge3IgQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChQRC1QSSksIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGxvdF9hdmdfc3RhdGVfZGlmZl9ncmFwaChuZXQsIGRpZmYgPSBjb2xNZWFucyhkaWZmLnN0YXRlLm1hdCksIGxheW91dCA9IG5pY2UubGF5b3V0LCAKICB0aXRsZSA9ICJBdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmYgYWNyb3NzIGFsbCBzeW5lcmd5IHN1YnNldHMgKFBELVBJKSIpCmBgYAoKV2UgYWxzbyB2aXN1YWxpemUgdGhlICoqbWVkaWFuIGFjdGl2aXR5IGRpZmZlcmVuY2UgcGVyIG5vZGUqKiBmcm9tIFtUYWJsZSA1XSgjVGFibGU1KSB1c2luZyBhIGBkb3RjaGFydGAsIHdoZXJlIHdlIGhhdmUgZXhjbHVkZWQgdGhlIG5vZGVzIHRoYXQgaGF2ZSBhbiBhYnNvbHV0ZSBtZWRpYW4gYWN0aXZpdHkgZGlmZmVyZW5jZSBvZiAkMC4wNSQgb3IgbGVzczoKYGBge3IgQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChQRC1QSSkgLSBkb3RjaGFydCwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpkZiA9IGFzLmRhdGEuZnJhbWUoYXBwbHkoZGlmZi5zdGF0ZS5tYXQsIDIsIG1lZGlhbikpCmNvbG5hbWVzKGRmKSA9ICJtZWRpYW4uc3RhdGUuZGlmZiIKZGYgPSBkZiAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJub2RlcyIpICU+JQogIGZpbHRlcihtZWRpYW4uc3RhdGUuZGlmZiA+IDAuMDUgfCBtZWRpYW4uc3RhdGUuZGlmZiA8IC0wLjA1KQoKZ2dkb3RjaGFydChkZiwgeCA9ICJub2RlcyIsIHkgPSAibWVkaWFuLnN0YXRlLmRpZmYiLAogIHRpdGxlID0gIk1lZGlhbiBhY3Rpdml0eSBzdGF0ZSBkaWZmZXJlbmNlIGFjcm9zcyBhbGwgc3luZXJneSBjb21wYXJpc29uIHNldHMgKFBELVBJLCBBNDk4IGNlbGwgbGluZSkiLCAKICBsYWJlbCA9ICJub2RlcyIsIGZvbnQubGFiZWwgPSBsaXN0KHNpemUgPSAxMSwgY29sb3IgPSAiYmx1ZSIpLAogIGxhYmVsLnNlbGVjdCA9IGxpc3QoY3JpdGVyaWEgPSAiYHlgID49IDAuNyB8IGB5YCA8PSAtMC43IiksIAogIHJlcGVsID0gVFJVRSwgbGFiZWwucmVjdGFuZ2xlID0gVFJVRSwKICB4bGFiID0gIk5vZGVzIiwgeWxhYiA9ICJNZWRpYW4gQWN0aXZpdHkgU3RhdGUiLCAKICB5bGltID0gYygtMSwxKSwgYWRkID0gInNlZ21lbnRzIikgKyAKICAgIGZvbnQoIngudGV4dCIsIHNpemUgPSAxMCkgKwogICAgZm9udCgidGl0bGUiLCBzaXplID0gMTEpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMC43LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArIAogICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDAuNywgbGluZXR5cGUgPSAiMC43IiksIGNvbG9yID0gInJlZCIpICsKICAgIHNjYWxlX2xpbmV0eXBlX21hbnVhbChuYW1lID0gIlRocmVzaG9sZCIsIHZhbHVlcyA9IDIsIAogICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGNvbG9yID0gInJlZCIpKSkgKyAKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLDAuNykpCmBgYAoKPGRpdiBjbGFzcz0ib3JhbmdlLWJveCI+ClNvICoqbm8gc2lnbmlmaWNhbnQgbm9kZXMqKiB3aG9zZSBhY3Rpdml0eSBwbGF5cyBpbXBvcnRhbnQgcm9sZSBpbiB0aGUgbWFuaWZlc3RhdGlvbiBvZiB0aGUgYFBELVBJYCBzeW5lcmd5IHdlcmUgZm91bmQgd2l0aCB0aGUgW3N5bmVyZ3kgc3Vic2V0IGFuYWx5c2lzXSgjc3MyKSBtZXRob2QuCjwvZGl2Pgo8L2JyPgoKIyMjIFJhbmRvbSB7LX0KCldlIGdldCB0aGUgYXZlcmFnZSBzdGF0ZSBkaWZmZXJlbmNlcyBwZXIgbmV0d29yayBub2RlIGZvciB0aGUgYEE0OThgIGNlbGwgbGluZSBmcm9tIHRoZSByYW5kb20gbW9kZWxzOgoKYGBge3IgUEQtUEkgZGlmZiAoUmFuZG9tIG1vZGVscyAtIEE0OTgpfQpQRC5QSS5hdmcuc3RhdGUuZGlmZi5yYW5kb20gPSByYW5kb20uc3luZXJneS5hbmFseXNpcy5yZXMkQTQ5OCRkaWZmLnN0YXRlLnN5bmVyZ2llcy5tYXRbIlBELVBJIixdCmBgYAoKV2Ugd2lsbCBub3cgdmlzdWFsaXplIHRoZSBub2RlcyBhdmVyYWdlIHN0YXRlIGRpZmZlcmVuY2VzIGluIGEgbmV0d29yayBncmFwaC4gTm90ZSB0aGF0IHRoZSAqKmdvb2QgbW9kZWxzKiogYXJlIHRob3NlIHRoYXQgcHJlZGljdGVkIHRoZSBgUEQtUElgIGRydWcgY29tYmluYXRpb24gdG8gYmUgKnN5bmVyZ2lzdGljKiBhbmQgd2VyZSBjb250cmFzdGVkIHRvIHRob3NlIHRoYXQgcHJlZGljdGVkIGl0IHRvIGJlICphbnRhZ29uaXN0aWMqICgqKmJhZCBtb2RlbHMqKikuIFRoZSBudW1iZXIgb2YgbW9kZWxzIGluIGVhY2ggY2F0ZWdvcnkgd2VyZToKCmBgYHtyIENvdW50IGdvb2QgdnMgYmFkIG1vZGVscyBmb3IgUEQtUEkgc3luZXJneSAoUmFuZG9tIG1vZGVscyksIHJlc3VsdHM9J2FzaXMnfQpkcnVnLmNvbWIgPSAiUEQtUEkiCgpnb29kLm1vZGVscy5udW0gPSBzdW0ocmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSA9PSAxICYgIWlzLm5hKHJhbmRvbS5tb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0pKQojIHVuaXF1ZSBnb29kIG1vZGVscwojIG5yb3codW5pcXVlKHJhbmRvbS5tb2RlbHMubGluay5vcGVyYXRvcltyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdID09IDEgJiAhaXMubmEocmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSksXSkpCmJhZC5tb2RlbHMubnVtICA9IHN1bShyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdID09IDAgJiAhaXMubmEocmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSkpCgpwcmV0dHlfcHJpbnRfc3RyaW5nKHBhc3RlMCgiTnVtYmVyIG9mICdnb29kJyByYW5kb20gbW9kZWxzIChQRC1QSSBzeW5lcmdpc3RpYykgaW4gdGhlIEE0OTggY2VsbCBsaW5lOiAiLCBnb29kLm1vZGVscy5udW0pKQpwcmV0dHlfcHJpbnRfc3RyaW5nKHBhc3RlMCgiTnVtYmVyIG9mICdiYWQnIHJhbmRvbSBtb2RlbHMgKFBELVBJIGFudGFnb25pc3RpYykgaW4gdGhlIEE0OTggY2VsbCBsaW5lOiAiLCBiYWQubW9kZWxzLm51bSkpCmBgYAoKYGBge3IgUEQtUEkgYWN0aXZpdHkgc3RhdGUgYmlvbWFya2VycyAoUmFuZG9tIG1vZGVscyAtIEE0OTgpLCBjYWNoZSA9IFRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGxvdF9hdmdfc3RhdGVfZGlmZl9ncmFwaChuZXQsIGRpZmYgPSBQRC5QSS5hdmcuc3RhdGUuZGlmZi5yYW5kb20sIAogIGxheW91dCA9IG5pY2UubGF5b3V0LCB0aXRsZSA9ICJQRC1QSSBhY3Rpdml0eSBzdGF0ZSBiaW9tYXJrZXJzIChSYW5kb20gbW9kZWxzIC0gQTQ5OCkiKQpgYGAKCjxkaXYgaWQ9IlRleHQyIj4gV2Ugbm93IHZpc3VhbGl6ZSB0aGUgbm9kZXMgYXZlcmFnZSBzdGF0ZSBkaWZmZXJlbmNlcyBpbiBhIGBkb3RjaGFydGAgd2hlcmUgd2UgY2FuIGVhc2lseSBpZGVudGlmeSB0aGUgKiphY3RpdmUgYW5kIGluaGliaXRlZCBzdGF0ZSBiaW9tYXJrZXJzKiogd2l0aCB0aGUgZGVmaW5lZCB0aHJlc2hvbGRzICh3ZSBoYXZlIGV4Y2x1ZGVkIG5vZGVzIHdob3NlIGFic29sdXRlIGF2ZXJhZ2UgZGlmZmVyZW5jZSB2YWx1ZSB3YXMgbGVzcyB0aGFuICQwLjA1JCk6CjwvZGl2PgpgYGB7ciBQRC1QSSBhY3RpdmUgc3RhdGUgYmlvbWFya2VycyAoUmFuZG9tIG1vZGVscyAtIEE0OTgpLCBmaWcuYWxpZ249J2NlbnRlcid9CmRmID0gYXMuZGF0YS5mcmFtZShQRC5QSS5hdmcuc3RhdGUuZGlmZi5yYW5kb20pCmNvbG5hbWVzKGRmKSA9ICJhdmcuc3RhdGUuZGlmZiIKZGYgPSBkZiAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJub2RlcyIpICU+JQogIGZpbHRlcihhdmcuc3RhdGUuZGlmZiA+IDAuMDUgfCBhdmcuc3RhdGUuZGlmZiA8IC0wLjA1KQoKZ2dkb3RjaGFydChkZiwgeCA9ICJub2RlcyIsIHkgPSAiYXZnLnN0YXRlLmRpZmYiLAogIHRpdGxlID0gIkF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZmVyZW5jZSBmb3IgUmFuZG9tIE1vZGVscyAoR29vZCAtIGJhZCkgLSBBNDk4IGNlbGwgbGluZSIsIAogIGxhYmVsID0gIm5vZGVzIiwgZm9udC5sYWJlbCA9IGxpc3Qoc2l6ZSA9IDEwLCBjb2xvciA9ICJibHVlIiksCiAgbGFiZWwuc2VsZWN0ID0gbGlzdChjcml0ZXJpYSA9ICJgeWAgPj0gMC43IHwgYHlgIDw9IC0wLjUiKSwgCiAgcmVwZWwgPSBUUlVFLCBsYWJlbC5yZWN0YW5nbGUgPSBUUlVFLAogIHhsYWIgPSAiTm9kZXMiLCB5bGFiID0gIkF2ZXJhZ2UgQWN0aXZpdHkgU3RhdGUiLCAKICB5bGltID0gYygtMSwxKSwgYWRkID0gInNlZ21lbnRzIikgKyAKICAgIGZvbnQoIngudGV4dCIsIHNpemUgPSA4KSArCiAgICBmb250KCJ0aXRsZSIsIHNpemUgPSAxNCkgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC0wLjcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsgCiAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC43LCBsaW5ldHlwZSA9ICIwLjciKSwgY29sb3IgPSAicmVkIikgKwogICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKG5hbWUgPSAiVGhyZXNob2xkIiwgdmFsdWVzID0gMiwgCiAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoY29sb3IgPSAicmVkIikpKSArIAogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsMC43KSkgKyAKICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IGMoOCwgMTcpLCB5ID0gLTAuOSwgbGFiZWwgPSBjKCJHb29kIG1vZGVsczogUEQtUEkgc3luZXJneSwiLCAiQmFkIE1vZGVsczogUEQtUEkgYW50YWdvbmlzbSIpKQpgYGAKCjxkaXYgY2xhc3M9ImJsdWUtYm94Ij4KVGhlIG92ZXJleHByZXNzaW9uIG9mICoqRVJLX2YqKiBpcyBhbHNvIGEgY2hhcmFjdGVyaXN0aWMgb2YgdGhlIHJhbmRvbSBtb2RlbHMgdGhhdCBwcmVkaWN0IHRoZSBgUEQtUElgIGRydWcgY29tYmluYXRpb24gdG8gYmUgc3luZXJnaXN0aWMuCjwvZGl2Pgo8L2JyPgoKIyMjIyBTeW5lcmd5IHN1YnNldHMgYW5hbHlzaXMgeyNzczN9CgpXZSBwZXJmb3JtIHRoZSBzYW1lIGtpbmQgb2YgYW5hbHlzaXMgYXMgd2l0aCB0aGUgY2VsbC1zcGVjaWZpYyBtb2RlbHM6IG1vZGVscyB0aGF0IHByZWRpY3QgYSBzZXQgb2Ygc3luZXJnaWVzIHdpbGwgYmUgY29udHJhc3RlZCB0byBtb2RlbHMgdGhhdCBwcmVkaWN0ZWQgdGhlIHNhbWUgc2V0IHdpdGggdGhlIGFkZGl0aW9uIG9mIHRoZSBleHRyYSBgUEQtUElgIHN5bmVyZ3ksIGFsbG93aW5nIHVzIHRodXMgdG8gcmVmaW5lIHRoZSBhY3Rpdml0eSBzdGF0ZSBiaW9tYXJrZXJzIHdlIGZvdW5kIGFib3ZlIGZyb20gdGhlIHJhbmRvbSBtb2RlbHMuCgpXZSBmaXJzdCBjb25zdHJ1Y3QgYSBtYXRyaXggd2hlcmUgKiplYWNoIHJvdyBpcyBhIHNldCB2cyBzdWJzZXQgYXZlcmFnZSBhY3Rpdml0eSBkaWZmZXJlbmNlIHZlY3RvciBvZiB0aGUgbmV0d29yayBub2RlcyoqOgo8ZGl2IGlkPSJUYWJsZTYiPjwvZGl2PgpgYGB7ciBTeW5lcmd5IFN1YnNldHMgQ29tcGFyaXNvbiBmb3IgUEQtUEkgKFJhbmRvbSAtIEE0OTgpLCBjYWNoZT1UUlVFfQpyZXMgPSBnZXRfc3luZXJneV9jb21wYXJpc29uX3NldHMocmFuZG9tLnN5bmVyZ3kuYW5hbHlzaXMucmVzJEE0OTgkc3luZXJneS5zdWJzZXQuc3RhdHMpClBELlBJLnJlcyA9IHJlcyAlPiUgZmlsdGVyKHN5bmVyZ2llcyA9PSAiUEQtUEkiKQoKZGlmZi5zdGF0ZS5saXN0LnJhbmRvbSA9IGxpc3QoKQpmb3IgKGkgaW4gMTpucm93KFBELlBJLnJlcykpIHsKICBzeW5lcmd5LnNldCAgICA9IFBELlBJLnJlc1tpLCAyXQogIHN5bmVyZ3kuc3Vic2V0ID0gUEQuUEkucmVzW2ksIDNdCiAgCiAgc3luZXJneS5zZXQuc3RyICAgID0gdW5saXN0KHN0cnNwbGl0KHggPSBzeW5lcmd5LnNldCwgc3BsaXQgPSAiLCIpKQogIHN5bmVyZ3kuc3Vic2V0LnN0ciA9IHVubGlzdChzdHJzcGxpdCh4ID0gc3luZXJneS5zdWJzZXQsIHNwbGl0ID0gIiwiKSkKICAKICAjIGNvdW50IG1vZGVscwogIHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0gPSBjb3VudF9tb2RlbHNfdGhhdF9wcmVkaWN0X3N5bmVyZ2llcyhzeW5lcmd5LnNldC5zdHIsIHJhbmRvbS5tb2RlbC5wcmVkaWN0aW9ucykKICBzeW5lcmd5LnN1YnNldC5tb2RlbHMubnVtID0gY291bnRfbW9kZWxzX3RoYXRfcHJlZGljdF9zeW5lcmdpZXMoc3luZXJneS5zdWJzZXQuc3RyLCByYW5kb20ubW9kZWwucHJlZGljdGlvbnMpCiAgIyBwcmludChwYXN0ZTAoc3luZXJneS5zZXQubW9kZWxzLm51bSwgIiAiLCBzeW5lcmd5LnN1YnNldC5tb2RlbHMubnVtKSkKICAKICAjIGlmIHRvbyBzbWFsbCBudW1iZXIgb2YgZHJ1ZyBjb21iaW5hdGlvbnMgaW4gdGhlIHN1YnNldCwgc2tpcCB0aGUgZGlmZiB2ZWN0b3IKICAjaWYgKGxlbmd0aChzeW5lcmd5LnN1YnNldC5zdHIpIDw9IDMpIG5leHQKICAjIGlmIHRvbyBzbWFsbCBudW1iZXIgb2YgbW9kZWxzIGNvbXBhcmVkCiAgI2lmICgoc3luZXJneS5zZXQubW9kZWxzLm51bSA8PSAzKSB8IChzeW5lcmd5LnNldC5tb2RlbHMubnVtIDw9IDMpKSBuZXh0CiAgCiAgIyBnZXQgdGhlIGRpZmYKICBkaWZmLnN0YXRlLlBELlBJID0gZ2V0X2F2Z19hY3Rpdml0eV9kaWZmX2Jhc2VkX29uX3N5bmVyZ3lfc2V0X2NtcChzeW5lcmd5LnNldC5zdHIsIHN5bmVyZ3kuc3Vic2V0LnN0ciwgcmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zLCByYW5kb20ubW9kZWxzLnN0YWJsZS5zdGF0ZSkKICAKICBkaWZmLnN0YXRlLmxpc3QucmFuZG9tW1twYXN0ZTAoc3luZXJneS5zZXQsICIgdnMgIiwgc3luZXJneS5zdWJzZXQpXV0gPSBkaWZmLnN0YXRlLlBELlBJCn0KCmRpZmYuc3RhdGUubWF0LnJhbmRvbSA9IGRvLmNhbGwocmJpbmQsIGRpZmYuc3RhdGUubGlzdC5yYW5kb20pCgpjYXB0aW9uLnRpdGxlLjYgPSAiVGFibGUgNjogQXZlcmFnZSBsaW5rIG9wZXJhdG9yIGRpZmZlcmVuY2UgdmFsdWVzIGFjcm9zcyBhbGwgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgKFBELVBJKSIKZGF0YXRhYmxlKGRhdGEgPSBkaWZmLnN0YXRlLm1hdC5yYW5kb21bLCBjKCJTUkMiLCAiUkFDX2YiLCAiTUVLX2YiLCAiU1RBVDEiLCAiUFRFTiIpXSwgb3B0aW9ucyA9IGxpc3QoCiAgICBzZWFyY2hpbmcgPSBGQUxTRSwgcGFnZUxlbmd0aCA9IDUsIGxlbmd0aE1lbnUgPSBjKDUsIDEwKSksCiAgICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oY2FwdGlvbi50aXRsZS42LCBzdHlsZT0iY29sb3I6I2RkNDgxNDsgZm9udC1zaXplOiAxOHB4IikpICU+JSAKICAgICAgZm9ybWF0Um91bmQoMTpuY29sKGRpZmYuc3RhdGUubWF0LnJhbmRvbSksIGRpZ2l0cyA9IDMpCmBgYAoKSWYgd2UgdmlzdWFsaXplIHRoZSAqKmF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZmVyZW5jZSBhY3Jvc3MgYWxsIHN5bmVyZ3kgY29tcGFyaXNvbiBzZXRzKiogZnJvbSBbVGFibGUgNl0oI1RhYmxlNikgd2Ugc2VlIHRoYXQgdGhlcmUgYXJlIG5vIG5vZGVzIHdpdGggc2lnbmlmaWNhbnQgYXZlcmFnZSBzdGF0ZSBkaWZmZXJlbmNlOgoKYGBge3IgQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIFBELVBJLCByYW5kb20gbW9kZWxzKSwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpwbG90X2F2Z19zdGF0ZV9kaWZmX2dyYXBoKG5ldCwgZGlmZiA9IGNvbE1lYW5zKGRpZmYuc3RhdGUubWF0LnJhbmRvbSksIGxheW91dCA9IG5pY2UubGF5b3V0LCAKICB0aXRsZSA9ICJBdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmYgYWNyb3NzIGFsbCBzeW5lcmd5IHN1YnNldHMgKFBELVBJKSIpCmBgYAoKV2UgYWxzbyB2aXN1YWxpemUgdGhlICoqbWVkaWFuIGFjdGl2aXR5IGRpZmZlcmVuY2UgcGVyIG5vZGUqKiBmcm9tIFtUYWJsZSA2XSgjVGFibGU2KSB1c2luZyBhIGBkb3RjaGFydGAsIHdoZXJlIHdlIGhhdmUgZXhjbHVkZWQgdGhlIG5vZGVzIHRoYXQgaGF2ZSBhbiBhYnNvbHV0ZSBtZWRpYW4gYWN0aXZpdHkgZGlmZmVyZW5jZSBvZiAkMC4wNSQgb3IgbGVzczoKYGBge3IgQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIGZvciByYW5kb20gbW9kZWxzIChQRC1QSSkgLSBkb3RjaGFydCwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpkZiA9IGFzLmRhdGEuZnJhbWUoYXBwbHkoZGlmZi5zdGF0ZS5tYXQucmFuZG9tLCAyLCBtZWRpYW4pKQpjb2xuYW1lcyhkZikgPSAibWVkaWFuLnN0YXRlLmRpZmYiCmRmID0gZGYgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAibm9kZXMiKSAlPiUKICBmaWx0ZXIobWVkaWFuLnN0YXRlLmRpZmYgPiAwLjA1IHwgbWVkaWFuLnN0YXRlLmRpZmYgPCAtMC4wNSkKCmdnZG90Y2hhcnQoZGYsIHggPSAibm9kZXMiLCB5ID0gIm1lZGlhbi5zdGF0ZS5kaWZmIiwKICB0aXRsZSA9ICJNZWRpYW4gYWN0aXZpdHkgc3RhdGUgZGlmZmVyZW5jZSBhY3Jvc3MgYWxsIHN5bmVyZ3kgY29tcGFyaXNvbiBzZXRzIChQRC1QSSwgUmFuZG9tIE1vZGVscywgQTQ5OCkiLCAKICBsYWJlbCA9ICJub2RlcyIsIGZvbnQubGFiZWwgPSBsaXN0KHNpemUgPSAxMSwgY29sb3IgPSAiYmx1ZSIpLAogIGxhYmVsLnNlbGVjdCA9IGxpc3QoY3JpdGVyaWEgPSAiYHlgID49IDAuNyB8IGB5YCA8PSAtMC43IiksIAogIHJlcGVsID0gVFJVRSwgbGFiZWwucmVjdGFuZ2xlID0gVFJVRSwKICB4bGFiID0gIk5vZGVzIiwgeWxhYiA9ICJNZWRpYW4gQWN0aXZpdHkgU3RhdGUiLCAKICB5bGltID0gYygtMSwxKSwgYWRkID0gInNlZ21lbnRzIikgKyAKICAgIGZvbnQoIngudGV4dCIsIHNpemUgPSAxMCkgKwogICAgZm9udCgidGl0bGUiLCBzaXplID0gMTEpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMC43LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArIAogICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDAuNywgbGluZXR5cGUgPSAiMC43IiksIGNvbG9yID0gInJlZCIpICsKICAgIHNjYWxlX2xpbmV0eXBlX21hbnVhbChuYW1lID0gIlRocmVzaG9sZCIsIHZhbHVlcyA9IDIsIAogICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGNvbG9yID0gInJlZCIpKSkgKyAKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLDAuNykpCmBgYAoKPGRpdiBjbGFzcz0ib3JhbmdlLWJveCI+ClNvICoqbm8gc2lnbmlmaWNhbnQgbm9kZXMqKiB3aG9zZSBhY3Rpdml0eSBwbGF5cyBpbXBvcnRhbnQgcm9sZSBpbiB0aGUgbWFuaWZlc3RhdGlvbiBvZiB0aGUgYFBELVBJYCBzeW5lcmd5IHdlcmUgZm91bmQgd2l0aCB0aGUgW3N5bmVyZ3kgc3Vic2V0IGFuYWx5c2lzXSgjc3MzKSBtZXRob2QgZm9yIHRoZSByYW5kb20gbW9kZWxzLgo8L2Rpdj4KPC9icj4KCgojIyMgUEQtUEkgaW4gb3RoZXIgY2VsbCBsaW5lcyAKClRob3VnaCB0aGUgYFBELVBJYCBzeW5lcmd5IHdhcyBvYnNlcnZlZCBhbmQgcHJlZGljdGVkIGluIHRoZSAqKkE0OTggbW9kZWwgZGF0YXNldCoqLCB3ZSBpbnZlc3RpZ2F0ZSBpdHMgYmlvbWFya2VycyBpbiB0aGUgb3RoZXIgY2VsbCBsaW5lcyB3aGVyZSAqKml0IHdhcyBwcmVkaWN0ZWQgYXMgYSBGYWxzZSBQb3NpdGl2ZSAoRlApIHN5bmVyZ3kgKHByZWRpY3RlZCBieSB0aGUgbW9kZWxzIGJ1dCBub3Qgb2JzZXJ2ZWQgaW4gdGhlIGV4cGVyaW1lbnRzKSoqLiAKVGh1cywgd2UgY2FuIHN0aWxsIHNlZSBpZiB0aGVyZSBhcmUgYW55IGNvbW1vbiAoYWN0aXZpdHkgc3RhdGUgYW5kIGxpbmsgb3BlcmF0b3IpIGJpb21hcmtlcnMgZm9yIHRoaXMgc3luZXJneSBhY3Jvc3MgdGhlIGFsbCB0aGUgY2VsbC1zcGVjaWZpYyBtb2RlbHMgYnkgY29udHJhc3RpbmcgaW4gZWFjaCBjZWxsIGxpbmUgdGhlIG1vZGVscyB0aGF0IHByZWRpY3RlZCBgUEQtUElgIChpZiB0aGVyZSB3ZXJlIGFueSkgdnMgdGhlIG1vZGVscyB0aGF0IGRpZCBub3Q6CgpgYGB7ciBQRC1QSSBhdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmZlcmVuY2UgaW4gYWxsIGNlbGwgbGluZXN9CmRydWcuY29tYiA9ICJQRC1QSSIKCmRpZmYuc3RhdGUubGlzdCA9IGxpc3QoKQoKZm9yIChjZWxsLmxpbmUgaW4gY2VsbC5saW5lcykgewogIGlmIChjZWxsLmxpbmUgPT0gIkE0OTgiKSBuZXh0CiAgCiAgbW9kZWwucHJlZGljdGlvbnMgPSBtb2RlbC5wcmVkaWN0aW9ucy5wZXIuY2VsbC5saW5lW1tjZWxsLmxpbmVdXQogIG1vZGVscy5zdGFibGUuc3RhdGUgPSBtb2RlbHMuc3RhYmxlLnN0YXRlLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dCiAgCiAgbW9kZWxzLnByZWRpY3RlZC5udW0gPSBzdW0obW9kZWwucHJlZGljdGlvbnNbLGRydWcuY29tYl0sIG5hLnJtID0gVCkKICBtb2RlbHMubm90LnByZWRpY3RlZC5udW0gPSBzdW0oIW1vZGVsLnByZWRpY3Rpb25zWyxkcnVnLmNvbWJdLCBuYS5ybSA9IFQpCiAgCiAgIyBTZWUgdGhlICNtb2RlbHMgdGhhdCBwcmVkaWN0ZWQgdnMgI21vZGVscyB0aG9zZSB0aGF0IGRpZCBub3QKICAjIHByaW50KHBhc3RlMChtb2RlbHMucHJlZGljdGVkLm51bSwgIiB2cyAiLCBtb2RlbHMubm90LnByZWRpY3RlZC5udW0pKQogIAogIGlmIChtb2RlbHMucHJlZGljdGVkLm51bSAhPSAwICYgbW9kZWxzLm5vdC5wcmVkaWN0ZWQubnVtICE9IDApCiAgICBkaWZmLnN0YXRlLmxpc3RbW2NlbGwubGluZV1dID0gZ2V0X2F2Z19hY3Rpdml0eV9kaWZmX2Jhc2VkX29uX3NwZWNpZmljX3N5bmVyZ3lfcHJlZGljdGlvbihtb2RlbC5wcmVkaWN0aW9ucywgbW9kZWxzLnN0YWJsZS5zdGF0ZSwgZHJ1Zy5jb21iKQp9CgojIGNvbWJpbmUgdGhlIGRhdGEgdG8gYSBzaW5nbGUgbWF0cml4CmRpZmYuc3RhdGUubWF0ID0gZG8uY2FsbChyYmluZCwgZGlmZi5zdGF0ZS5saXN0KQpgYGAKCk5vdywgd2UgKippZGVudGlmeSB0aGUgYmlvbWFya2VycyBvZiBpbnRlcmVzdCoqIGFzIHRob3NlIG5vZGVzIHRoYXQgaGF2ZSBzdXJwYXNzZWQgYSB1c2VyLWRlZmluZWQgdGhyZXNob2xkICgkMC42JCkgYWNyb3NzIGFzIG1hbnkgYXMgcG9zc2libGUgY2VsbCBsaW5lcyAtIGNvdW50aW5nIHRodXMgdGhlICpmcmVxdWVuY3kqIHRoYXQgdGhpcyBoYXMgb2NjdXJlZC4gCldlIHNob3cgdGhvc2Ugbm9kZXMgdGhhdCBzdXJwYXNzZWQgdGhlIHRocmVzaG9sZCBpbiBhdCBsZWFzdCBoYWxmIG9mIHRoZSBjZWxsIGxpbmVzOgpgYGB7ciBQRC1QSSBhY3Rpdml0eSBiaW9tYXJrZXJzIGZyZXF1ZW5jeSBhY3Jvc3MgY2VsbCBsaW5lcywgcmVzdWx0cyA9ICdhc2lzJ30KYmlvbWFya2VyLnN0YXRlLm1hdCA9IGJpbmFyaXplX3RvX3RocmVzKG1hdCA9IGRpZmYuc3RhdGUubWF0LCB0aHJlcyA9IDAuNikKYmlvbWFya2Vycy5mcmVxID0gY29sU3VtcyhiaW9tYXJrZXIuc3RhdGUubWF0KS9ucm93KGJpb21hcmtlci5zdGF0ZS5tYXQpCgpwcmV0dHlfcHJpbnRfdmVjdG9yX25hbWVzX2FuZF92YWx1ZXMoYmlvbWFya2Vycy5mcmVxW2Jpb21hcmtlcnMuZnJlcSA+IDAuNV0pCmBgYAoKU28gd2UgaWRlbnRpZmllZCB0d28gYmlvbWFya2VycywgYEVSS19mYCAoa25vd24gZnJvbSBiZWZvcmUpIGFuZCBgU09TMWAuClRoZSBhdmVyYWdlIGFjdGl2aXR5IGRpZmZlcmVuY2UgdmFsdWVzIGZvciB0aGUgYFNPUzFgIHBlciBjZWxsIGxpbmUgbW9kZWwgZGF0YXNldHMgd2VyZToKPGRpdiBpZD0ic29zMSI+PC9kaXY+CmBgYHtyIFNPUzEgYXZlcmFnZSBkaWZmIHZhbHVlcyAoUEQtUEkpfQpicmVha3MucmVkID0gcXVhbnRpbGUoYygtMSwwKSwgcHJvYnMgPSBzZXEoLjA1LCAuOTUsIC4wNSksIG5hLnJtID0gVFJVRSkKY29sb3JzLnJlZCA9IHNvcnQocm91bmQoc2VxKDI1NSwgNDAsIGxlbmd0aC5vdXQgPSBsZW5ndGgoYnJlYWtzLnJlZCkgKyAxKSwgZGlnaXRzID0gMCkpICU+JQogIHtwYXN0ZTAoInJnYigyNTUsIiwgLiwgIiwiLCAuLCAiKSIpfSAjIHJlZAoKZGF0YXRhYmxlKGRhdGEgPSBhcy5kYXRhLmZyYW1lKGRpZmYuc3RhdGUubWF0WywnU09TMSddKSwgY29sbmFtZXMgPSAiU09TMSIsIAogIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICd0Jywgb3JkZXIgPSBsaXN0KGxpc3QoMSwgJ2FzYycpKSksIHdpZHRoID0gIjQ3MHB4IiwKICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oIkF2ZyBhY3Rpdml0eSBkaWZmIHZhbHVlcyAoYWxsIGNlbGwgbGluZXMgZXhjZXB0IEE0OTgsIFBELVBJKSIsIHN0eWxlPSJjb2xvcjojZGQ0ODE0OyBmb250LXNpemU6IDE4cHgiKSkgJT4lIAogIGZvcm1hdFJvdW5kKGNvbHVtbnMgPSAxLCBkaWdpdHMgPSAzKSAlPiUKICBmb3JtYXRTdHlsZShjb2x1bW5zID0gMSwgYmFja2dyb3VuZENvbG9yID0gc3R5bGVJbnRlcnZhbChicmVha3MucmVkLCBjb2xvcnMucmVkKSkKYGBgCgpTb21lIGFydGljbGUgZXZpZGVuY2UgKmFnYWluc3QqIHRoZSBhYm92ZSAoc28gYFNPUzFgIGlzIHNvbWV3aGF0IGV4cHJlc3NlZCBpbiB0aGVzZSBjYW5jZXIgY2VsbCBsaW5lcyk6CgotIFtEVTE0NV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DMzcyNzYzMy8pCi0gW0FHUyBhbmQgb3RoZXIgZ2FzdHJpYyBjZWxsIGxpbmVzXShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE1OTgtMDE4LTI0OTY5LXcpCi0gW1NXNjIwXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM1OTIxMTgxLykKCiMjIFBELUcyIGFjdGl2aXR5IGJpb21hcmtlcnMgey19CgpBcyBvYnNlcnZlZCBpbiB0aGUgW2hlYXRtYXAgYWJvdmVdKCNvYnNlcnZlZC1zeW5lcmdpZXMpLCB0aGUgYFBELUcyYCBzeW5lcmd5IHdhcyBwcmVkaWN0ZWQgYnkgYm90aCB0aGUgKipjZWxsIHNwZWNpZmljKiogYW5kICoqcmFuZG9tIG1vZGVscyoqIGluIHRoZSBgQTQ5OGAgY2VsbCBsaW5lLgoKIyMjIENlbGwtc3BlY2lmaWMgKEE0OTgpIHstfQoKYGBge3IgUEQtRzIgZGlmZiAoQ2VsbCBzcGVjaWZpYyBtb2RlbHMgLSBBNDk4KX0KUEQuRzIuYXZnLnN0YXRlLmRpZmYuY2VsbC5zcGVjaWZpYyA9IGNlbGwuc3BlY2lmaWMuc3luZXJneS5hbmFseXNpcy5yZXMkQTQ5OCRkaWZmLnN0YXRlLnN5bmVyZ2llcy5tYXRbIlBELUcyIiwgXQpgYGAKCldlIHdpbGwgbm93IHZpc3VhbGl6ZSB0aGUgbm9kZXMgYXZlcmFnZSBzdGF0ZSBkaWZmZXJlbmNlcyBpbiBhIG5ldHdvcmsgZ3JhcGguIApOb3RlIHRoYXQgdGhlICoqZ29vZCBtb2RlbHMqKiBhcmUgdGhvc2UgdGhhdCBwcmVkaWN0ZWQgdGhlIGBQRC1HMmAgZHJ1ZyBjb21iaW5hdGlvbiB0byBiZSAqc3luZXJnaXN0aWMqIGFuZCB3ZXJlIGNvbnRyYXN0ZWQgdG8gdGhvc2UgdGhhdCBwcmVkaWN0ZWQgaXQgdG8gYmUgKmFudGFnb25pc3RpYyogKCoqYmFkIG1vZGVscyoqKS4gClRoZSBudW1iZXIgb2YgbW9kZWxzIGluIGVhY2ggY2F0ZWdvcnkgd2VyZToKCmBgYHtyIENvdW50IGdvb2QgdnMgYmFkIG1vZGVscyBmb3IgUEQtRzIgc3luZXJneSAoY2VsbC1zcGVjaWZpYyBtb2RlbHMpLCByZXN1bHRzPSdhc2lzJ30KbW9kZWwucHJlZGljdGlvbnMgPSBtb2RlbC5wcmVkaWN0aW9ucy5wZXIuY2VsbC5saW5lW1siQTQ5OCJdXQptb2RlbHMuc3RhYmxlLnN0YXRlID0gbW9kZWxzLnN0YWJsZS5zdGF0ZS5wZXIuY2VsbC5saW5lW1siQTQ5OCJdXQpkcnVnLmNvbWIgPSAiUEQtRzIiCgpnb29kLm1vZGVscy5udW0gPSBzdW0obW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdID09IDEgJiAhaXMubmEobW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdKSkKIyB1bmlxdWUgZ29vZCBtb2RlbHMKI21vZGVscy5saW5rLm9wZXJhdG9yID0gbW9kZWxzLmxpbmsub3BlcmF0b3JzLnBlci5jZWxsLmxpbmUkQTQ5OAojbnJvdyh1bmlxdWUobW9kZWxzLmxpbmsub3BlcmF0b3JbbW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdID09IDEgJiAhaXMubmEobW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdKSwgXSkpCmJhZC5tb2RlbHMubnVtICA9IHN1bShtb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0gPT0gMCAmICFpcy5uYShtb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0pKQoKcHJldHR5X3ByaW50X3N0cmluZyhwYXN0ZTAoIk51bWJlciBvZiAnZ29vZCcgbW9kZWxzIChQRC1HMiBzeW5lcmdpc3RpYykgaW4gdGhlIEE0OTggY2VsbCBsaW5lOiAiLCBnb29kLm1vZGVscy5udW0pKQpwcmV0dHlfcHJpbnRfc3RyaW5nKHBhc3RlMCgiTnVtYmVyIG9mICdiYWQnIG1vZGVscyAoUEQtRzIgYW50YWdvbmlzdGljKSBpbiB0aGUgQTQ5OCBjZWxsIGxpbmU6ICIsIGJhZC5tb2RlbHMubnVtKSkKYGBgCgpgYGB7ciBQRC1HMiBhY3Rpdml0eSBzdGF0ZSBiaW9tYXJrZXJzIChDZWxsIHNwZWNpZmljIG1vZGVscyAtIEE0OTgpLCBjYWNoZSA9IFRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGxvdF9hdmdfc3RhdGVfZGlmZl9ncmFwaChuZXQsIGRpZmYgPSBQRC5HMi5hdmcuc3RhdGUuZGlmZi5jZWxsLnNwZWNpZmljLCAKICBsYXlvdXQgPSBuaWNlLmxheW91dCwgdGl0bGUgPSAiUEQtRzIgYWN0aXZpdHkgc3RhdGUgYmlvbWFya2VycyAoQ2VsbCBzcGVjaWZpYyBtb2RlbHMgLSBBNDk4KSIpCmBgYAoKPGRpdiBpZD0iVGV4dDMiPiBXZSBub3cgdmlzdWFsaXplIHRoZSBub2RlcyBhdmVyYWdlIHN0YXRlIGRpZmZlcmVuY2VzIGluIGEgYGRvdGNoYXJ0YCB3aGVyZSB3ZSBjYW4gZWFzaWx5IGlkZW50aWZ5IHRoZSAqKmFjdGl2ZSBhbmQgaW5oaWJpdGVkIHN0YXRlIGJpb21hcmtlcnMqKiB3aXRoIHRoZSBkZWZpbmVkIHRocmVzaG9sZHMgKHdlIGhhdmUgZXhjbHVkZWQgbm9kZXMgd2hvc2UgYWJzb2x1dGUgYXZlcmFnZSBkaWZmZXJlbmNlIHZhbHVlIHdhcyBsZXNzIHRoYW4gJDAuMDUkKToKPC9kaXY+CmBgYHtyIFBELUcyIGFjdGl2ZSBzdGF0ZSBiaW9tYXJrZXJzIChDZWxsIHNwZWNpZmljIG1vZGVscyAtIEE0OTgpLCBmaWcuYWxpZ249J2NlbnRlcid9CmRmID0gYXMuZGF0YS5mcmFtZShQRC5HMi5hdmcuc3RhdGUuZGlmZi5jZWxsLnNwZWNpZmljKQpjb2xuYW1lcyhkZikgPSAiYXZnLnN0YXRlLmRpZmYiCmRmID0gZGYgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAibm9kZXMiKSAlPiUKICBmaWx0ZXIoYXZnLnN0YXRlLmRpZmYgPiAwLjA1IHwgYXZnLnN0YXRlLmRpZmYgPCAtMC4wNSkKCmdnZG90Y2hhcnQoZGYsIHggPSAibm9kZXMiLCB5ID0gImF2Zy5zdGF0ZS5kaWZmIiwKICB0aXRsZSA9ICJBdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmZlcmVuY2UgKEdvb2QgLSBiYWQpIC0gQTQ5OCBjZWxsIGxpbmUiLCAKICBsYWJlbCA9ICJub2RlcyIsIGZvbnQubGFiZWwgPSBsaXN0KHNpemUgPSAxMSwgY29sb3IgPSAiYmx1ZSIpLAogIGxhYmVsLnNlbGVjdCA9IGxpc3QoY3JpdGVyaWEgPSAiYHlgID49IDAuOSB8IGB5YCA8PSAtMC41IiksIAogIHJlcGVsID0gVFJVRSwgbGFiZWwucmVjdGFuZ2xlID0gVFJVRSwKICB4bGFiID0gIk5vZGVzIiwgeWxhYiA9ICJBdmVyYWdlIEFjdGl2aXR5IFN0YXRlIiwgCiAgeWxpbSA9IGMoLTEsMSksIGFkZCA9ICJzZWdtZW50cyIpICsgCiAgICBmb250KCJ4LnRleHQiLCBzaXplID0gOCkgKwogICAgZm9udCgidGl0bGUiLCBzaXplID0gMTQpICsgCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtMC43LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArIAogICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDAuNywgbGluZXR5cGUgPSAiMC43IiksIGNvbG9yID0gInJlZCIpICsKICAgIHNjYWxlX2xpbmV0eXBlX21hbnVhbChuYW1lID0gIlRocmVzaG9sZCIsIHZhbHVlcyA9IDIsIAogICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGNvbG9yID0gInJlZCIpKSkgKyAKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4yLDAuNykpICsgCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSBjKDE4LCAzMiksIHkgPSAtMC45LCBsYWJlbCA9IGMoIkdvb2QgbW9kZWxzOiBQRC1HMiBzeW5lcmd5LCIsICJCYWQgTW9kZWxzOiBQRC1HMiBhbnRhZ29uaXNtIikpCmBgYAoKPGRpdiBjbGFzcz0ib3JhbmdlLWJveCI+Ck5vdGUgYWdhaW4gdGhlIGJvb2xlYW4gZXF1YXRpb246IGBQUE0xQSAqPSAoIFBURU4gKWAuIFRoaXMgbWVhbnMgdGhhdCBgUFRFTmAgaXMgKip0aGUgb25seSBpbmhpYml0ZWQgbm9kZSBvZiBpbnRlcmVzdCoqCjwvZGl2Pgo8YnI+Cgo8ZGl2IGNsYXNzPSJibHVlLWJveCI+CkFzIGluIHRoZSBjYXNlIG9mIHRoZSBgUEQtUElgIHN5bmVyZ3ksICoqaW5oaWJpdGlvbioqIG9mIHRoZSBgUFRFTmAgbm9kZSBhbmQgKipvdmVyZXhwcmVzc2lvbioqIG9mIHRoZSBgRVJLX2ZgIG5vZGUgYXJlIHRoZSBtYWluIGJpb21hcmtlcnMgKFtzZWUgZG90Y2hhcnQgYWJvdmVdKCNUZXh0MykpIHRoYXQgbWFrZSB0aGUgbW9kZWxzIHByZWRpY3QgdGhlIGBQRC1HMmAgc3luZXJneS4KPC9kaXY+CgojIyMjIFN5bmVyZ3kgU3Vic2V0cyBBbmFseXNpcyB7I3NzNH0KCkl0IHdpbGwgYmUgaW50ZXJlc3RpbmcgdG8gZmluZCAqKmFsbCB0aGUgcG9zc2libGUgc3luZXJneSBzZXRzIGFuZCBzdWJzZXRzIHRoYXQgaW5jbHVkZSB0aGUgYFBELUcyYCBhcyB0aGUgZXh0cmEgc3luZXJneSoqLiAKVXNpbmcgdGhlc2Ugc3luZXJneSBzZXRzLCBtb2RlbHMgdGhhdCBwcmVkaWN0IGEgc2V0IG9mIHN5bmVyZ2llcyB3aWxsIGJlIGNvbnRyYXN0ZWQgdG8gbW9kZWxzIHRoYXQgcHJlZGljdGVkIHRoZSBzYW1lIHNldCB3aXRoIHRoZSBhZGRpdGlvbiBvZiB0aGUgZXh0cmEgYFBELUcyYCBzeW5lcmd5LiAKVGh1cyB3ZSBjb3VsZCBmaW5kICoqc3luZXJneSBiaW9tYXJrZXJzIHRoYXQgYWxsb3cgYWxyZWFkeSBnb29kIHByZWRpY3RpbmcgbW9kZWxzIHRvIHByZWRpY3QgdGhlIGFkZGl0aW9uYWwgc3luZXJneSBvZiBpbnRlcmVzdCoqLgpUaGlzIGludmVzdGlnYXRpb24gd2lsbCBhbGxvdyB1cyB0aHVzIHRvIHJlZmluZSB0aGUgYWN0aXZpdHkgc3RhdGUgYmlvbWFya2VycyB3ZSBmb3VuZCBhYm92ZS4KCldlIGZpcnN0IGNvbnN0cnVjdCBhIG1hdHJpeCB3aGVyZSAqKmVhY2ggcm93IGlzIGEgc2V0IHZzIHN1YnNldCBhdmVyYWdlIGFjdGl2aXR5IGRpZmZlcmVuY2UgdmVjdG9yIG9mIHRoZSBuZXR3b3JrIG5vZGVzKio6CjxkaXYgaWQ9IlRhYmxlNyI+PC9kaXY+CmBgYHtyIFN5bmVyZ3kgU3Vic2V0cyBDb21wYXJpc29uIFBELUcyIChDZWxsLXNwZWNpZmljIC0gQTQ5OCksIGNhY2hlPVRSVUV9Cm1vZGVsLnByZWRpY3Rpb25zID0gbW9kZWwucHJlZGljdGlvbnMucGVyLmNlbGwubGluZSRBNDk4Cm1vZGVscy5zdGFibGUuc3RhdGUgPSBtb2RlbHMuc3RhYmxlLnN0YXRlLnBlci5jZWxsLmxpbmUkQTQ5OAptb2RlbHMubGluay5vcGVyYXRvciA9IG1vZGVscy5saW5rLm9wZXJhdG9ycy5wZXIuY2VsbC5saW5lJEE0OTgKCnJlcyA9IGdldF9zeW5lcmd5X2NvbXBhcmlzb25fc2V0cyhjZWxsLnNwZWNpZmljLnN5bmVyZ3kuYW5hbHlzaXMucmVzJEE0OTgkc3luZXJneS5zdWJzZXQuc3RhdHMpClBELkcyLnJlcyA9IHJlcyAlPiUgZmlsdGVyKHN5bmVyZ2llcyA9PSAiUEQtRzIiKQoKZGlmZi5zdGF0ZS5saXN0ID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpucm93KFBELkcyLnJlcykpIHsKICBzeW5lcmd5LnNldCAgICA9IFBELkcyLnJlc1tpLCAyXQogIHN5bmVyZ3kuc3Vic2V0ID0gUEQuRzIucmVzW2ksIDNdCiAgCiAgc3luZXJneS5zZXQuc3RyICAgID0gdW5saXN0KHN0cnNwbGl0KHggPSBzeW5lcmd5LnNldCwgc3BsaXQgPSAiLCIpKQogIHN5bmVyZ3kuc3Vic2V0LnN0ciA9IHVubGlzdChzdHJzcGxpdCh4ID0gc3luZXJneS5zdWJzZXQsIHNwbGl0ID0gIiwiKSkKICAKICAjIGNvdW50IG1vZGVscwogIHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0gPSBjb3VudF9tb2RlbHNfdGhhdF9wcmVkaWN0X3N5bmVyZ2llcyhzeW5lcmd5LnNldC5zdHIsIG1vZGVsLnByZWRpY3Rpb25zKQogIHN5bmVyZ3kuc3Vic2V0Lm1vZGVscy5udW0gPSBjb3VudF9tb2RlbHNfdGhhdF9wcmVkaWN0X3N5bmVyZ2llcyhzeW5lcmd5LnN1YnNldC5zdHIsIG1vZGVsLnByZWRpY3Rpb25zKQogIAogICMgcHJpbnQocGFzdGUwKHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0sICIgIiwgc3luZXJneS5zdWJzZXQubW9kZWxzLm51bSkpCiAgCiAgIyBpZiB0b28gc21hbGwgbnVtYmVyIG9mIG1vZGVscywgc2tpcCB0aGUgZGlmZiB2ZWN0b3IKICBpZiAoKHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0gPD0gMykgfCAoc3luZXJneS5zZXQubW9kZWxzLm51bSA8PSAzKSkgCiAgICAgbmV4dAogIAogICMgZ2V0IHRoZSBkaWZmCiAgZGlmZi5zdGF0ZS5QRC5HMiA9IGdldF9hdmdfYWN0aXZpdHlfZGlmZl9iYXNlZF9vbl9zeW5lcmd5X3NldF9jbXAoc3luZXJneS5zZXQuc3RyLCBzeW5lcmd5LnN1YnNldC5zdHIsIG1vZGVsLnByZWRpY3Rpb25zLCBtb2RlbHMuc3RhYmxlLnN0YXRlKQogIGRpZmYuc3RhdGUubGlzdFtbcGFzdGUwKHN5bmVyZ3kuc2V0LCAiIHZzICIsIHN5bmVyZ3kuc3Vic2V0KV1dID0gZGlmZi5zdGF0ZS5QRC5HMgp9CgpkaWZmLnN0YXRlLm1hdCA9IGRvLmNhbGwocmJpbmQsIGRpZmYuc3RhdGUubGlzdCkKCmNhcHRpb24udGl0bGUuNyA9ICJUYWJsZSA3OiBBdmVyYWdlIGFjdGl2aXR5IGRpZmZlcmVuY2UgdmFsdWVzIGFjcm9zcyBhbGwgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgKFBELUcyKSIKZGF0YXRhYmxlKGRhdGEgPSBkaWZmLnN0YXRlLm1hdFssIGMoIlNSQyIsICJDU0siLCAiUFRFTiIsICJTVEFUMSIsICJQVFBONiIpXSwgb3B0aW9ucyA9IGxpc3QoCiAgICBzZWFyY2hpbmcgPSBGQUxTRSwgcGFnZUxlbmd0aCA9IDUsIGxlbmd0aE1lbnUgPSBjKDUsIDEwKSksCiAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKGNhcHRpb24udGl0bGUuNywgc3R5bGU9ImNvbG9yOiNkZDQ4MTQ7IGZvbnQtc2l6ZTogMThweCIpKSAlPiUgCiAgZm9ybWF0Um91bmQoMTpuY29sKGRpZmYuc3RhdGUubWF0KSwgZGlnaXRzID0gMykKYGBgCgpJZiB3ZSB2aXN1YWxpemUgdGhlICoqYXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmZXJlbmNlIGFjcm9zcyBhbGwgc3luZXJneSBjb21wYXJpc29uIHNldHMqKiBmcm9tIFtUYWJsZSA3XSgjVGFibGU3KSBpbiBhIG5ldHdvcmsgZ3JhcGgsIHdlIHNlZSB0aGF0IHRoZXJlIGFyZSBubyBub2RlcyB3aXRoIHNpZ25pZmljYW50IGF2ZXJhZ2Ugc3RhdGUgZGlmZmVyZW5jZToKCmBgYHtyIEF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZiBhY3Jvc3MgYWxsIHN5bmVyZ3kgc3Vic2V0cyAoUEQtRzIpLCBjYWNoZT1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcid9CnBsb3RfYXZnX3N0YXRlX2RpZmZfZ3JhcGgobmV0LCBkaWZmID0gY29sTWVhbnMoZGlmZi5zdGF0ZS5tYXQpLCBsYXlvdXQgPSBuaWNlLmxheW91dCwgCiAgdGl0bGUgPSAiQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChQRC1HMikiKQpgYGAKCldlIGFsc28gdmlzdWFsaXplIHRoZSAqKm1lZGlhbiBhY3Rpdml0eSBkaWZmZXJlbmNlIHBlciBub2RlKiogZnJvbSBbVGFibGUgN10oI1RhYmxlNykgdXNpbmcgYSBgZG90Y2hhcnRgLCB3aGVyZSB3ZSBoYXZlIGV4Y2x1ZGVkIHRoZSBub2RlcyB0aGF0IGhhdmUgYW4gYWJzb2x1dGUgbWVkaWFuIGFjdGl2aXR5IGRpZmZlcmVuY2Ugb2YgJDAuMDUkIG9yIGxlc3M6CmBgYHtyIEF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZiBhY3Jvc3MgYWxsIHN5bmVyZ3kgc3Vic2V0cyAoUEQtRzIpIC0gZG90Y2hhcnQsIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KZGYgPSBhcy5kYXRhLmZyYW1lKGFwcGx5KGRpZmYuc3RhdGUubWF0LCAyLCBtZWRpYW4pKQpjb2xuYW1lcyhkZikgPSAibWVkaWFuLnN0YXRlLmRpZmYiCmRmID0gZGYgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAibm9kZXMiKSAlPiUKICBmaWx0ZXIobWVkaWFuLnN0YXRlLmRpZmYgPiAwLjA1IHwgbWVkaWFuLnN0YXRlLmRpZmYgPCAtMC4wNSkKCmdnZG90Y2hhcnQoZGYsIHggPSAibm9kZXMiLCB5ID0gIm1lZGlhbi5zdGF0ZS5kaWZmIiwKICB0aXRsZSA9ICJNZWRpYW4gYWN0aXZpdHkgc3RhdGUgZGlmZmVyZW5jZSBhY3Jvc3MgYWxsIHN5bmVyZ3kgY29tcGFyaXNvbiBzZXRzIChQRC1HMiwgQTQ5OCBjZWxsIGxpbmUpIiwgCiAgbGFiZWwgPSAibm9kZXMiLCBmb250LmxhYmVsID0gbGlzdChzaXplID0gMTEsIGNvbG9yID0gImJsdWUiKSwKICBsYWJlbC5zZWxlY3QgPSBsaXN0KGNyaXRlcmlhID0gImB5YCA+PSAwLjUgfCBgeWAgPD0gLTAuNSIpLCAKICByZXBlbCA9IFRSVUUsIGxhYmVsLnJlY3RhbmdsZSA9IFRSVUUsCiAgeGxhYiA9ICJOb2RlcyIsIHlsYWIgPSAiTWVkaWFuIEFjdGl2aXR5IFN0YXRlIiwgCiAgeWxpbSA9IGMoLTEsMSksIGFkZCA9ICJzZWdtZW50cyIpICsgCiAgICBmb250KCJ4LnRleHQiLCBzaXplID0gMTApICsKICAgIGZvbnQoInRpdGxlIiwgc2l6ZSA9IDExKSArIAogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLTAuNywgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKyAKICAgIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAwLjcsIGxpbmV0eXBlID0gIjAuNyIpLCBjb2xvciA9ICJyZWQiKSArCiAgICBzY2FsZV9saW5ldHlwZV9tYW51YWwobmFtZSA9ICJUaHJlc2hvbGQiLCB2YWx1ZXMgPSAyLCAKICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChjb2xvciA9ICJyZWQiKSkpICsgCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwwLjcpKQpgYGAKCjxkaXYgY2xhc3M9Im9yYW5nZS1ib3giIGlkPSJUZXh0NCI+ClRoZSBhYm92ZSByZXN1bHRzIHNob3cgdGhlIHNhbWUgdGhpbmcgd2UgZm91bmQgYWxzbyBiZWZvcmUgd2l0aCB0aGUgc2ltcGxlIG1vZGVsIGNvbXBhcmlzb24gbWV0aG9kOiBgRVJLX2ZgIG92ZXJleHByZXNzaW9uLCBgUFRFTmAgaW5oaWJpdGlvbi4KPC9kaXY+CjwvYnI+CgojIyMgUmFuZG9tIHstfQoKV2UgZ2V0IHRoZSBhdmVyYWdlIHN0YXRlIGRpZmZlcmVuY2VzIHBlciBuZXR3b3JrIG5vZGUgZm9yIHRoZSBgQTQ5OGAgY2VsbCBsaW5lIGZyb20gdGhlIHJhbmRvbSBtb2RlbHM6CgpgYGB7ciBQRC1HMiBkaWZmIChSYW5kb20gbW9kZWxzIC0gQTQ5OCl9ClBELkcyLmF2Zy5zdGF0ZS5kaWZmLnJhbmRvbSA9IHJhbmRvbS5zeW5lcmd5LmFuYWx5c2lzLnJlcyRBNDk4JGRpZmYuc3RhdGUuc3luZXJnaWVzLm1hdFsiUEQtRzIiLF0KYGBgCgpXZSB3aWxsIG5vdyB2aXN1YWxpemUgdGhlIG5vZGVzIGF2ZXJhZ2Ugc3RhdGUgZGlmZmVyZW5jZXMgaW4gYSBuZXR3b3JrIGdyYXBoLiBOb3RlIHRoYXQgdGhlICoqZ29vZCBtb2RlbHMqKiBhcmUgdGhvc2UgdGhhdCBwcmVkaWN0ZWQgdGhlIGBQRC1HMmAgZHJ1ZyBjb21iaW5hdGlvbiB0byBiZSAqc3luZXJnaXN0aWMqIGFuZCB3ZXJlIGNvbnRyYXN0ZWQgdG8gdGhvc2UgdGhhdCBwcmVkaWN0ZWQgaXQgdG8gYmUgKmFudGFnb25pc3RpYyogKCoqYmFkIG1vZGVscyoqKS4gVGhlIG51bWJlciBvZiBtb2RlbHMgaW4gZWFjaCBjYXRlZ29yeSB3ZXJlOgoKYGBge3IgQ291bnQgZ29vZCB2cyBiYWQgbW9kZWxzIGZvciBQRC1HMiBzeW5lcmd5IChSYW5kb20gbW9kZWxzKSwgcmVzdWx0cz0nYXNpcyd9CmRydWcuY29tYiA9ICJQRC1HMiIKCmdvb2QubW9kZWxzLm51bSA9IHN1bShyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdID09IDEgJiAhaXMubmEocmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zWywgZHJ1Zy5jb21iXSkpCiMgdW5pcXVlIGdvb2QgbW9kZWxzCiMgbnJvdyh1bmlxdWUocmFuZG9tLm1vZGVscy5saW5rLm9wZXJhdG9yW3JhbmRvbS5tb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0gPT0gMSAmICFpcy5uYShyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdKSxdKSkKYmFkLm1vZGVscy5udW0gID0gc3VtKHJhbmRvbS5tb2RlbC5wcmVkaWN0aW9uc1ssIGRydWcuY29tYl0gPT0gMCAmICFpcy5uYShyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLCBkcnVnLmNvbWJdKSkKCnByZXR0eV9wcmludF9zdHJpbmcocGFzdGUwKCJOdW1iZXIgb2YgJ2dvb2QnIHJhbmRvbSBtb2RlbHMgKFBELUcyIHN5bmVyZ2lzdGljKSBpbiB0aGUgQTQ5OCBjZWxsIGxpbmU6ICIsIGdvb2QubW9kZWxzLm51bSkpCnByZXR0eV9wcmludF9zdHJpbmcocGFzdGUwKCJOdW1iZXIgb2YgJ2JhZCcgcmFuZG9tIG1vZGVscyAoUEQtRzIgYW50YWdvbmlzdGljKSBpbiB0aGUgQTQ5OCBjZWxsIGxpbmU6ICIsIGJhZC5tb2RlbHMubnVtKSkKYGBgCgpgYGB7ciBQRC1HMiBhY3Rpdml0eSBzdGF0ZSBiaW9tYXJrZXJzIChSYW5kb20gbW9kZWxzIC0gQTQ5OCksIGNhY2hlID0gVFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpwbG90X2F2Z19zdGF0ZV9kaWZmX2dyYXBoKG5ldCwgZGlmZiA9IFBELkcyLmF2Zy5zdGF0ZS5kaWZmLnJhbmRvbSwgCiAgbGF5b3V0ID0gbmljZS5sYXlvdXQsIHRpdGxlID0gIlBELUcyIGFjdGl2aXR5IHN0YXRlIGJpb21hcmtlcnMgKFJhbmRvbSBtb2RlbHMgLSBBNDk4KSIpCmBgYAoKPGRpdiBjbGFzcz0iYmx1ZS1ib3giIGlkPSJUZXh0NSI+ClRoZSBvdmVyZXhwcmVzc2lvbiBvZiAqKkVSS19mKiogaXMgYWxzbyBhIGNoYXJhY3RlcmlzdGljIG9mIHRoZSByYW5kb20gbW9kZWxzIHRoYXQgcHJlZGljdCB0aGUgYFBELUcyYCBkcnVnIGNvbWJpbmF0aW9uIHRvIGJlIHN5bmVyZ2lzdGljLgo8L2Rpdj4KPC9icj4KCiMjIyMgU3luZXJneSBzdWJzZXRzIGFuYWx5c2lzIHsjc3M1fQoKV2UgcGVyZm9ybSB0aGUgc2FtZSBraW5kIG9mIGFuYWx5c2lzIGFzIHdpdGggdGhlIGNlbGwtc3BlY2lmaWMgbW9kZWxzOiBtb2RlbHMgdGhhdCBwcmVkaWN0IGEgc2V0IG9mIHN5bmVyZ2llcyB3aWxsIGJlIGNvbnRyYXN0ZWQgdG8gbW9kZWxzIHRoYXQgcHJlZGljdGVkIHRoZSBzYW1lIHNldCB3aXRoIHRoZSBhZGRpdGlvbiBvZiB0aGUgZXh0cmEgYFBELUcyYCBzeW5lcmd5LCBhbGxvd2luZyB1cyB0aHVzIHRvIHJlZmluZSB0aGUgYWN0aXZpdHkgc3RhdGUgYmlvbWFya2VycyB3ZSBmb3VuZCBhYm92ZSBmcm9tIHRoZSByYW5kb20gbW9kZWxzLgoKV2UgZmlyc3QgY29uc3RydWN0IGEgbWF0cml4IHdoZXJlICoqZWFjaCByb3cgaXMgYSBzZXQgdnMgc3Vic2V0IGF2ZXJhZ2UgYWN0aXZpdHkgZGlmZmVyZW5jZSB2ZWN0b3Igb2YgdGhlIG5ldHdvcmsgbm9kZXMqKjoKPGRpdiBpZD0iVGFibGU4Ij48L2Rpdj4KYGBge3IgU3luZXJneSBTdWJzZXRzIENvbXBhcmlzb24gZm9yIFBELUcyIChSYW5kb20gLSBBNDk4KSwgY2FjaGU9VFJVRX0KcmVzID0gZ2V0X3N5bmVyZ3lfY29tcGFyaXNvbl9zZXRzKHJhbmRvbS5zeW5lcmd5LmFuYWx5c2lzLnJlcyRBNDk4JHN5bmVyZ3kuc3Vic2V0LnN0YXRzKQpQRC5HMi5yZXMgPSByZXMgJT4lIGZpbHRlcihzeW5lcmdpZXMgPT0gIlBELUcyIikKCmRpZmYuc3RhdGUubGlzdC5yYW5kb20gPSBsaXN0KCkKZm9yIChpIGluIDE6bnJvdyhQRC5HMi5yZXMpKSB7CiAgc3luZXJneS5zZXQgICAgPSBQRC5HMi5yZXNbaSwgMl0KICBzeW5lcmd5LnN1YnNldCA9IFBELkcyLnJlc1tpLCAzXQogIAogIHN5bmVyZ3kuc2V0LnN0ciAgICA9IHVubGlzdChzdHJzcGxpdCh4ID0gc3luZXJneS5zZXQsIHNwbGl0ID0gIiwiKSkKICBzeW5lcmd5LnN1YnNldC5zdHIgPSB1bmxpc3Qoc3Ryc3BsaXQoeCA9IHN5bmVyZ3kuc3Vic2V0LCBzcGxpdCA9ICIsIikpCiAgCiAgIyBjb3VudCBtb2RlbHMKICBzeW5lcmd5LnNldC5tb2RlbHMubnVtID0gY291bnRfbW9kZWxzX3RoYXRfcHJlZGljdF9zeW5lcmdpZXMoc3luZXJneS5zZXQuc3RyLCByYW5kb20ubW9kZWwucHJlZGljdGlvbnMpCiAgc3luZXJneS5zdWJzZXQubW9kZWxzLm51bSA9IGNvdW50X21vZGVsc190aGF0X3ByZWRpY3Rfc3luZXJnaWVzKHN5bmVyZ3kuc3Vic2V0LnN0ciwgcmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zKQogICMgcHJpbnQocGFzdGUwKHN5bmVyZ3kuc2V0Lm1vZGVscy5udW0sICIgIiwgc3luZXJneS5zdWJzZXQubW9kZWxzLm51bSkpCiAgCiAgIyBpZiB0b28gc21hbGwgbnVtYmVyIG9mIGRydWcgY29tYmluYXRpb25zIGluIHRoZSBzdWJzZXQsIHNraXAgdGhlIGRpZmYgdmVjdG9yCiAgI2lmIChsZW5ndGgoc3luZXJneS5zdWJzZXQuc3RyKSA8PSAzKSBuZXh0CiAgIyBpZiB0b28gc21hbGwgbnVtYmVyIG9mIG1vZGVscyBjb21wYXJlZAogIGlmICgoc3luZXJneS5zZXQubW9kZWxzLm51bSA8PSAzKSB8IChzeW5lcmd5LnNldC5tb2RlbHMubnVtIDw9IDMpKSBuZXh0CiAgCiAgIyBnZXQgdGhlIGRpZmYKICBkaWZmLnN0YXRlLlBELkcyID0gZ2V0X2F2Z19hY3Rpdml0eV9kaWZmX2Jhc2VkX29uX3N5bmVyZ3lfc2V0X2NtcChzeW5lcmd5LnNldC5zdHIsIHN5bmVyZ3kuc3Vic2V0LnN0ciwgcmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zLCByYW5kb20ubW9kZWxzLnN0YWJsZS5zdGF0ZSkKICAKICBkaWZmLnN0YXRlLmxpc3QucmFuZG9tW1twYXN0ZTAoc3luZXJneS5zZXQsICIgdnMgIiwgc3luZXJneS5zdWJzZXQpXV0gPSBkaWZmLnN0YXRlLlBELkcyCn0KCmRpZmYuc3RhdGUubWF0LnJhbmRvbSA9IGRvLmNhbGwocmJpbmQsIGRpZmYuc3RhdGUubGlzdC5yYW5kb20pCgpjYXB0aW9uLnRpdGxlLjggPSAiVGFibGUgODogQXZlcmFnZSBsaW5rIG9wZXJhdG9yIGRpZmZlcmVuY2UgdmFsdWVzIGFjcm9zcyBhbGwgc3luZXJneSBzZXQgY29tcGFyaXNvbnMgKFBELUcyKSIKZGF0YXRhYmxlKGRhdGEgPSBkaWZmLnN0YXRlLm1hdC5yYW5kb21bLCBjKCJTUkMiLCAiUkFDX2YiLCAiTUVLX2YiLCAiU1RBVDEiLCAiUFRFTiIpXSwgb3B0aW9ucyA9IGxpc3QoCiAgICBzZWFyY2hpbmcgPSBGQUxTRSwgcGFnZUxlbmd0aCA9IDUsIGxlbmd0aE1lbnUgPSBjKDUsIDEwKSksCiAgICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oY2FwdGlvbi50aXRsZS44LCBzdHlsZT0iY29sb3I6I2RkNDgxNDsgZm9udC1zaXplOiAxOHB4IikpICU+JSAKICAgICAgZm9ybWF0Um91bmQoMTpuY29sKGRpZmYuc3RhdGUubWF0LnJhbmRvbSksIGRpZ2l0cyA9IDMpCmBgYAoKSWYgd2UgdmlzdWFsaXplIHRoZSAqKmF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZmVyZW5jZSBhY3Jvc3MgYWxsIHN5bmVyZ3kgY29tcGFyaXNvbiBzZXRzKiogZnJvbSBbVGFibGUgOF0oI1RhYmxlOCkgd2UgaGF2ZSBzb21lIG5vZGVzIHRoYXQgc2hvdyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBhY3Rpdml0eSBpbiB0aGUgcmFuZG9tIG1vZGVscyB0aGF0IHByZWRpY3QgdGhlIGV4dHJhIGBQRC1HMmAgc3luZXJneToKCmBgYHtyIEF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZiBhY3Jvc3MgYWxsIHN5bmVyZ3kgc3Vic2V0cyBQRC1HMiwgcmFuZG9tIG1vZGVscyksIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGxvdF9hdmdfc3RhdGVfZGlmZl9ncmFwaChuZXQsIGRpZmYgPSBjb2xNZWFucyhkaWZmLnN0YXRlLm1hdC5yYW5kb20pLCBsYXlvdXQgPSBuaWNlLmxheW91dCwgCiAgdGl0bGUgPSAiQXZlcmFnZSBhY3Rpdml0eSBzdGF0ZSBkaWZmIGFjcm9zcyBhbGwgc3luZXJneSBzdWJzZXRzIChQRC1HMikiKQpgYGAKCldlIGFsc28gdmlzdWFsaXplIHRoZSAqKm1lZGlhbiBhY3Rpdml0eSBkaWZmZXJlbmNlIHBlciBub2RlKiogZnJvbSBbVGFibGUgOF0oI1RhYmxlOCkgdXNpbmcgYSBgZG90Y2hhcnRgLCB3aGVyZSB3ZSBoYXZlIGV4Y2x1ZGVkIHRoZSBub2RlcyB0aGF0IGhhdmUgYW4gYWJzb2x1dGUgbWVkaWFuIGFjdGl2aXR5IGRpZmZlcmVuY2Ugb2YgJDAuMDUkIG9yIGxlc3M6CmBgYHtyIEF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZiBhY3Jvc3MgYWxsIHN5bmVyZ3kgc3Vic2V0cyBmb3IgcmFuZG9tIG1vZGVscyAoUEQtKSAtIGRvdGNoYXJ0LCBjYWNoZT1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcid9CmRmID0gYXMuZGF0YS5mcmFtZShhcHBseShkaWZmLnN0YXRlLm1hdC5yYW5kb20sIDIsIG1lZGlhbikpCmNvbG5hbWVzKGRmKSA9ICJtZWRpYW4uc3RhdGUuZGlmZiIKZGYgPSBkZiAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJub2RlcyIpICU+JQogIGZpbHRlcihtZWRpYW4uc3RhdGUuZGlmZiA+IDAuMDUgfCBtZWRpYW4uc3RhdGUuZGlmZiA8IC0wLjA1KQoKZ2dkb3RjaGFydChkZiwgeCA9ICJub2RlcyIsIHkgPSAibWVkaWFuLnN0YXRlLmRpZmYiLAogIHRpdGxlID0gIk1lZGlhbiBhY3Rpdml0eSBzdGF0ZSBkaWZmZXJlbmNlIGFjcm9zcyBhbGwgc3luZXJneSBjb21wYXJpc29uIHNldHMgKFBELUcyLCBSYW5kb20gTW9kZWxzLCBBNDk4KSIsIAogIGxhYmVsID0gIm5vZGVzIiwgZm9udC5sYWJlbCA9IGxpc3Qoc2l6ZSA9IDExLCBjb2xvciA9ICJibHVlIiksCiAgbGFiZWwuc2VsZWN0ID0gbGlzdChjcml0ZXJpYSA9ICJgeWAgPj0gMC43IHwgYHlgIDw9IC0wLjciKSwgCiAgcmVwZWwgPSBUUlVFLCBsYWJlbC5yZWN0YW5nbGUgPSBUUlVFLAogIHhsYWIgPSAiTm9kZXMiLCB5bGFiID0gIk1lZGlhbiBBY3Rpdml0eSBTdGF0ZSIsIAogIHlsaW0gPSBjKC0xLDEpLCBhZGQgPSAic2VnbWVudHMiKSArIAogICAgZm9udCgieC50ZXh0Iiwgc2l6ZSA9IDEwKSArCiAgICBmb250KCJ0aXRsZSIsIHNpemUgPSAxMSkgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC0wLjcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsgCiAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMC43LCBsaW5ldHlwZSA9ICIwLjciKSwgY29sb3IgPSAicmVkIikgKwogICAgc2NhbGVfbGluZXR5cGVfbWFudWFsKG5hbWUgPSAiVGhyZXNob2xkIiwgdmFsdWVzID0gMiwgCiAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoY29sb3IgPSAicmVkIikpKSArIAogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsMC43KSkKYGBgCgpGcm9tIHRoZSBhYm92ZSBmaWd1cmUgd2Ugc2VlIHRoYXQgdGhlIG5vZGVzIGBQVEVOYCwgYFBQTTFBYCBhbmQgYExJTUsyYCBhcmUgbW9yZSBmb3VuZCB0byBiZSBtb3JlICoqaW5oaWJpdGVkKiogaW4gdGhlIG1vZGVscyB0aGF0IHByZWRpY3RlZCB0aGUgYFBELUcyYCBzeW5lcmd5IGFuZCB0aGUgbm9kZXMgYFBSS0NEYCBhbmQgYFBEUEsxYCBtb3JlICoqYWN0aXZhdGVkKiouCgo8ZGl2IGNsYXNzPSJvcmFuZ2UtYm94IiBpZD0iVGV4dDYiPgpGcm9tIHRoZSBib29sZWFuIGVxdWF0aW9uOiBgTElNSzIgKj0gUk9DSzEgYW5kL29yIG5vdCBQUktDRGAsIHNpbmNlIGBQUktDRCA9PSBUUlVFYCAob3ZlcmV4cHJlc3NlZCkgd2UgcmVkdWNlZCBpdCB0bzogYExJTUsyICo9IFJPQ0sxIGFuZC9vciBGQUxTRWAgd2hlcmUgMyBvdXQgb2YgNCBjYXNlcyBoZXJlIGNvcnJlc3BvbmQgdG8gYExJTUsyID09IEZBTFNFYCAoaW5oaWJpdGVkKS4gClNvLCB3ZSBjb3VsZCBhcmd1ZSB0aGF0IG1vc3Qgb2YgdGhlIHRpbWVzIHRoZSBgTElNSzJgIGluaGliaXRpb24gaXMgZHVlIHRvIHRoZSBgUFJLQ0RgICoqb3ZlcmV4cHJlc3Npb24qKiBpbiB0aGUgZW5kIQo8YnI+PGJyPgpOb3RlIHRoZSBlcXVhdGlvbjogYFBSS0NEICo9IFBEUEsxIG9yIENBU1AzYC4KVGhpcyBtZWFucyB0aGF0IHRoZSBhY3RpdmF0aW9uIG9mIGBQUktDRGAgaXMgKiphIGRpcmVjdCBjb25zZXF1ZW5jZSoqIGZyb20gYFBEUEsxYCdzIGFjdGl2YXRpb24uCkJ1dCBgUERQSzFgIGlzIGFsc28gdGhlIHRhcmdldCBvZiB0aGUgYEcyYCBkcnVnLgpUaGlzIG1lYW5zIHRoYXQgbW9kZWxzIHRoYXQgcmVzcG9uZGVkIHRvIHRoZSBgUEQtRzJgIGNvbWJpbmF0aW9uIGFyZSB0aGUgb25lcyB0aGF0IHNob3dlZCBgUERQSzFgICoqb3ZlcmV4cHJlc3Npb24qKiEKPGJyPjxicj4KQWxzbyBhZ2FpbiBmcm9tIHRoZSBlcXVhdGlvbjogYFBQTTFBICo9IFBURU5gLCB3ZSBoYXZlIHRoYXQgYFBURU5gIGlzIHRoZSBvbmx5ICoqaW5oaWJpdGVkKiogbm9kZSBvZiBpbnRlcmVzdAo8YnI+PGJyPgpTbywgYWNjb3JkaW5nIHRvIHRoaXMgYW5hbHlzaXMsIGBQVEVOYCAqKmluaGliaXRpb24qKiBhbmQgYFBEUEsxYCAqKm92ZXJleHByZXNzaW9uKiogYXJlIHRoZSBtYWluIGJpb21hcmtlcnMgdGhhdCBjYXVzZSB0aGUgcmFuZG9tIG1vZGVscyB0byByZXNwb25kIHN5bmVyZ2lzdGljYWxseSB0byB0aGUgYFBELUcyYCBkcnVnIGNvbWJpbmF0aW9uLiAKPC9kaXY+Cjxicj4KCldlIHdpbGwgYWdhaW4gdXNlIHRoZSBNQ0xQLWRhdGFzZXQgdG8gY2hlY2sgZm9yIHRoZSBhY3Rpdml0eS9zaWduYWxpbmcgdmFsdWVzIG9mIGBQRFBLMWAgYW5kIGBQSUszQ0FgICh0aGUgbGF0ZXIgYmVjYXVzZSBpdCdzIHN0cm9uZ2x5IGNvbm5lY3RlZCB3aXRoIHRoZSBvdGhlcnMgYmFzZWQgb24gdGhlIGZvbGxvd2luZyBib29sZWFuIGVxdWF0aW9uIGluIG91ciBtb2RlbDogYFBEUEsxICo9IFBJSzNDQSBhbmQvb3Igbm90IFBURU5gKS4KCldlIGNoZWNrIHRoZSBwaG9zcGhvcnlsYXRpb24gdmFsdWUgb2YgdGhlIGBQREsxX3BTMjQxYCBhY3Jvc3MgYWxsIGNlbGwgbGluZXM6CmBgYHtyIE1DTFAgRGF0YSBWaXN1YWxpemF0aW9uIChQRFBLMSksIGNhY2hlPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJ30KUERQSzEuZGF0YSA9IG1jbHAuZGF0YSAlPiUgc2VsZWN0KFNhbXBsZV9OYW1lLCBQREsxX3BTMjQxKSAlPiUgbmEub21pdCgpCgojIGZpbmQgdGhlIGFjdGl2aXR5IGNsYXNzZXMgKDMgY2xhc3NlczogbG93IGFjdGl2aXR5LCBubyBhY3Rpdml0eSwgaGlnaCBhY3Rpdml0eSkKcmVzID0gQ2ttZWFucy4xZC5kcCh4ID0gUERQSzEuZGF0YSRQREsxX3BTMjQxLCBrID0gMykKYWN0aXZpdHkgPSBhcy5mYWN0b3IocmVzJGNsdXN0ZXIpCmxldmVscyhhY3Rpdml0eSkgPSBjKCJsb3ciLCAibWVkaXVtIiwgImhpZ2giKQpQRFBLMS5kYXRhID0gY2JpbmQoUERQSzEuZGF0YSwgYWN0aXZpdHkpCgpnZ2RvdGNoYXJ0KGRhdGEgPSBQRFBLMS5kYXRhLCB4ID0gIlNhbXBsZV9OYW1lIiwgeSA9ICJQREsxX3BTMjQxIiwgCiAgdGl0bGUgPSAiUERLMV9wUzI0MSBzaWduYWxpbmcgYWNyb3NzIGFsbCBjZWxsIGxpbmVzIGluIE1DTFAgRGF0YXNldCIsCiAgY29sb3IgPSAiYWN0aXZpdHkiLCBwYWxldHRlID0gYygicmVkIiwgImdyZXkiLCAiZ3JlZW4iKSwgCiAgbGFiZWwgPSAiU2FtcGxlX05hbWUiLCBsYWJlbC5zZWxlY3QgPSBjZWxsLmxpbmVzLmluLm1jbHAsIHJlcGVsID0gVFJVRSwKICBhZGQgPSAic2VnbWVudHMiLCBsYWJlbC5yZWN0YW5nbGUgPSBUUlVFLAogIHhsYWIgPSAiQ2VsbCBMaW5lcyIsIHlsYWIgPSAiUERLMV9wUzI0MSBTaWduYWxpbmciLAogIGFkZC5wYXJhbXMgPSBsaXN0KGNvbG9yID0gImFjdGl2aXR5IiwgcGFsZXR0ZSA9IGMoInJlZCIsICJncmV5IiwgImdyZWVuIikpKSArIAogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKPGRpdiBjbGFzcz0iYmx1ZS1ib3giPgpTaW5jZSBmb3IgYEE0OThgIGNlbGwgbGluZSB3ZSBvYnNlcnZlIG9uZSBvZiB0aGUgbGFyZ2VzdCBzaWduYWxpbmcgbWVhc3VyZW1lbnRzIGNvbXBhcmVkIHRvIGFsbCB0aGUgY2VsbCBsaW5lcyBmb3IgdGhlIGBQREsxX3BTMjQxYCBzaXRlLCB0aGlzIG1lYW5zIHRoYXQgYFBEUEsxYCBpcyAqKm92ZXJleHByZXNzZWQqKiwgd2hpY2ggaXMgZXhhY3RseSB3aGF0IHdlIGZvdW5kIGZyb20gb3VyIGFuYWx5c2lzIGFib3ZlLgo8L2Rpdj4KPGJyPgoKQWxzbywgd2UgY2hlY2sgdGhlIHBob3NwaG9yeWxhdGlvbiB2YWx1ZSBvZiB0aGUgYFBJM0stcDExMC1hbHBoYWAgYWNyb3NzIGFsbCBjZWxsIGxpbmVzOgpgYGB7ciBNQ0xQIERhdGEgVmlzdWFsaXphdGlvbiAoUElLM0NBKSwgY2FjaGU9VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInfQpQSUszQ0EuZGF0YSA9IG1jbHAuZGF0YSAlPiUgc2VsZWN0KFNhbXBsZV9OYW1lLCBQSTNLUDExMEFMUEhBKSAlPiUgbmEub21pdCgpCgojIGZpbmQgdGhlIGFjdGl2aXR5IGNsYXNzZXMgKDMgY2xhc3NlczogbG93IGFjdGl2aXR5LCBubyBhY3Rpdml0eSwgaGlnaCBhY3Rpdml0eSkKcmVzID0gQ2ttZWFucy4xZC5kcCh4ID0gUElLM0NBLmRhdGEkUEkzS1AxMTBBTFBIQSwgayA9IDMpCmFjdGl2aXR5ID0gYXMuZmFjdG9yKHJlcyRjbHVzdGVyKQpsZXZlbHMoYWN0aXZpdHkpID0gYygibG93IiwgIm1lZGl1bSIsICJoaWdoIikKUElLM0NBLmRhdGEgPSBjYmluZChQSUszQ0EuZGF0YSwgYWN0aXZpdHkpCgpnZ2RvdGNoYXJ0KGRhdGEgPSBQSUszQ0EuZGF0YSwgeCA9ICJTYW1wbGVfTmFtZSIsIHkgPSAiUEkzS1AxMTBBTFBIQSIsIAogIHRpdGxlID0gIlBJM0stcDExMC1hbHBoYSBzaWduYWxpbmcgYWNyb3NzIGFsbCBjZWxsIGxpbmVzIGluIE1DTFAgRGF0YXNldCIsCiAgY29sb3IgPSAiYWN0aXZpdHkiLCBwYWxldHRlID0gYygicmVkIiwgImdyZXkiLCAiZ3JlZW4iKSwgCiAgbGFiZWwgPSAiU2FtcGxlX05hbWUiLCBsYWJlbC5zZWxlY3QgPSBjZWxsLmxpbmVzLmluLm1jbHAsIHJlcGVsID0gVFJVRSwKICBhZGQgPSAic2VnbWVudHMiLCBsYWJlbC5yZWN0YW5nbGUgPSBUUlVFLAogIHhsYWIgPSAiQ2VsbCBMaW5lcyIsIHlsYWIgPSAiUEkzSy1wMTEwLWFscGhhIFNpZ25hbGluZyIsCiAgYWRkLnBhcmFtcyA9IGxpc3QoY29sb3IgPSAiYWN0aXZpdHkiLCBwYWxldHRlID0gYygicmVkIiwgImdyZXkiLCAiZ3JlZW4iKSkpICsgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgo8ZGl2IGNsYXNzPSJvcmFuZ2UtYm94IiBpZD0icGRwazEtZXEiPgpTbywgdGhlIHJlc3VsdHMgYWJvdmUgaGludCB0aGF0IGBQSUszQ0FgIG1pZ2h0IGJlICoqaW5oaWJpdGVkKiogaW4gdGhlIGBBNDk4YCBjZWxsIGxpbmUsIHdoaWNoIGNvbnNpZGVyaW5nIHRoZSBgUFRFTmAgKippbmhpYml0aW9uKiosIGBQRFBLMWAgKipvdmVyZXhwcmVzc2lvbioqIGFuZCB0aGVpciBib29sZWFuIGVxdWF0aW9uICh3aGljaCBjb25uZWN0cyBhbGwgb2YgdGhlbSB0b2dldGhlcik6IGBQRFBLMSAqPSBQSUszQ0EgYW5kL29yIG5vdCBQVEVOYCAoYFBEUEsxID0gVFJVRWAgYW5kIGBQSUszQ0EgPSBQVEVOID0gRkFMU0VgKSwgd2UgY29uY2x1ZGUgdGhhdCB0aGVyZSBtdXN0IGJlIGFuIGBvciBub3RgIGxpbmsgb3BlcmF0b3IgYmV0d2VlbiB0aGUgMiByZWd1bGF0b3JzLgo8L2Rpdj4KPGJyPgo8ZGl2IGNsYXNzPSJibHVlLWJveCI+CmBQVEVOYCAqKmluaGliaXRpb24qKiBhbmQgYFBEUEsxYCAqKm92ZXJleHByZXNzaW9uKiogcGxheSBib3RoIGFuIGltcG9ydGFudCByb2xlIGluIHRoZSBtYW5pZmVzdGF0aW9uIG9mIHRoZSBgUEQtRzJgIHN5bmVyZ3kgYXMgd2FzIGZvdW5kIHdpdGggdGhlIFtzeW5lcmd5IHN1YnNldCBhbmFseXNpc10oI3NzNSkgbWV0aG9kIGZvciB0aGUgcmFuZG9tIG1vZGVscy4KQWxzbywgYFBJSzNDQWAgaGFzIGFuIGBvciBub3RgIChsaW5rIG9wZXJhdG9yKSByZWxhdGlvbnNoaXAgd2l0aCBgUFRFTmAgKGFjY29yZGluZyB0byBvdXIgbW9kZWwncyBlcXVhdGlvbikgYXMgcmVndWxhdG9ycyBvZiBgUERQSzFgLgo8L2Rpdj4KPC9icj4KCiMjIyBQRC1HMiBpbiBvdGhlciBjZWxsIGxpbmVzCgpUaG91Z2ggdGhlIGBQRC1HMmAgc3luZXJneSB3YXMgb2JzZXJ2ZWQgYW5kIHByZWRpY3RlZCBpbiB0aGUgKipBNDk4IG1vZGVsIGRhdGFzZXQqKiwgd2UgaW52ZXN0aWdhdGUgaXRzIGJpb21hcmtlcnMgaW4gdGhlIG90aGVyIGNlbGwgbGluZXMgd2hlcmUgKippdCB3YXMgcHJlZGljdGVkIGFzIGEgRmFsc2UgUG9zaXRpdmUgKEZQKSBzeW5lcmd5IChwcmVkaWN0ZWQgYnkgdGhlIG1vZGVscyBidXQgbm90IG9ic2VydmVkIGluIHRoZSBleHBlcmltZW50cykqKi4KVGh1cywgd2UgY2FuIHN0aWxsIHNlZSBpZiB0aGVyZSBhcmUgYW55IGNvbW1vbiAoYWN0aXZpdHkgc3RhdGUgYW5kIGxpbmsgb3BlcmF0b3IpIGJpb21hcmtlcnMgZm9yIHRoaXMgc3luZXJneSBhY3Jvc3MgdGhlIGFsbCB0aGUgY2VsbC1zcGVjaWZpYyBtb2RlbHMgYnkgY29udHJhc3RpbmcgaW4gZWFjaCBjZWxsIGxpbmUgdGhlIG1vZGVscyB0aGF0IHByZWRpY3RlZCBgUEQtRzJgIChpZiB0aGVyZSB3ZXJlIGFueSkgdnMgdGhlIG1vZGVscyB0aGF0IGRpZCBub3Q6CgpgYGB7ciBQRC1HMiBhdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmZlcmVuY2UgaW4gYWxsIGNlbGwgbGluZXN9CmRydWcuY29tYiA9ICJQRC1HMiIKCmRpZmYuc3RhdGUubGlzdCA9IGxpc3QoKQoKZm9yIChjZWxsLmxpbmUgaW4gY2VsbC5saW5lcykgewogIGlmIChjZWxsLmxpbmUgPT0gIkE0OTgiKSBuZXh0CiAgCiAgbW9kZWwucHJlZGljdGlvbnMgPSBtb2RlbC5wcmVkaWN0aW9ucy5wZXIuY2VsbC5saW5lW1tjZWxsLmxpbmVdXQogIG1vZGVscy5zdGFibGUuc3RhdGUgPSBtb2RlbHMuc3RhYmxlLnN0YXRlLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dCiAgCiAgbW9kZWxzLnByZWRpY3RlZC5udW0gPSBzdW0obW9kZWwucHJlZGljdGlvbnNbLGRydWcuY29tYl0sIG5hLnJtID0gVCkKICBtb2RlbHMubm90LnByZWRpY3RlZC5udW0gPSBzdW0oIW1vZGVsLnByZWRpY3Rpb25zWyxkcnVnLmNvbWJdLCBuYS5ybSA9IFQpCiAgCiAgIyBTZWUgdGhlICNtb2RlbHMgdGhhdCBwcmVkaWN0ZWQgdnMgI21vZGVscyB0aG9zZSB0aGF0IGRpZCBub3QKICAjIHByaW50KHBhc3RlMChtb2RlbHMucHJlZGljdGVkLm51bSwgIiB2cyAiLCBtb2RlbHMubm90LnByZWRpY3RlZC5udW0pKQogIAogIGlmIChtb2RlbHMucHJlZGljdGVkLm51bSAhPSAwICYgbW9kZWxzLm5vdC5wcmVkaWN0ZWQubnVtICE9IDApCiAgICBkaWZmLnN0YXRlLmxpc3RbW2NlbGwubGluZV1dID0gZ2V0X2F2Z19hY3Rpdml0eV9kaWZmX2Jhc2VkX29uX3NwZWNpZmljX3N5bmVyZ3lfcHJlZGljdGlvbihtb2RlbC5wcmVkaWN0aW9ucywgbW9kZWxzLnN0YWJsZS5zdGF0ZSwgZHJ1Zy5jb21iKQp9CgojIGNvbWJpbmUgdGhlIGRhdGEgdG8gYSBzaW5nbGUgbWF0cml4CmRpZmYuc3RhdGUubWF0ID0gZG8uY2FsbChyYmluZCwgZGlmZi5zdGF0ZS5saXN0KQpgYGAKCk5vdywgd2UgKippZGVudGlmeSB0aGUgYmlvbWFya2VycyBvZiBpbnRlcmVzdCoqIGFzIHRob3NlIG5vZGVzIHRoYXQgaGF2ZSBzdXJwYXNzZWQgYSB1c2VyLWRlZmluZWQgdGhyZXNob2xkICgkMC42JCkgYWNyb3NzIGFzIG1hbnkgYXMgcG9zc2libGUgY2VsbCBsaW5lcyAtIGNvdW50aW5nIHRodXMgdGhlICpmcmVxdWVuY3kqIHRoYXQgdGhpcyBoYXMgb2NjdXJlZC4gCldlIHNob3cgdGhvc2Ugbm9kZXMgdGhhdCBzdXJwYXNzZWQgdGhlIHRocmVzaG9sZCBpbiBhdCBsZWFzdCBoYWxmIG9mIHRoZSBjZWxsIGxpbmVzOgpgYGB7ciBQRC1HMiBhY3Rpdml0eSBiaW9tYXJrZXJzIGZyZXF1ZW5jeSBhY3Jvc3MgY2VsbCBsaW5lcywgcmVzdWx0cyA9ICdhc2lzJ30KYmlvbWFya2VyLnN0YXRlLm1hdCA9IGJpbmFyaXplX3RvX3RocmVzKG1hdCA9IGRpZmYuc3RhdGUubWF0LCB0aHJlcyA9IDAuNikKYmlvbWFya2Vycy5mcmVxID0gY29sU3VtcyhiaW9tYXJrZXIuc3RhdGUubWF0KS9ucm93KGJpb21hcmtlci5zdGF0ZS5tYXQpCgpwcmV0dHlfcHJpbnRfdmVjdG9yX25hbWVzX2FuZF92YWx1ZXMoYmlvbWFya2Vycy5mcmVxW2Jpb21hcmtlcnMuZnJlcSA+IDAuNV0pCmBgYAoKU28gd2UgaWRlbnRpZmllZCB0d28gYmlvbWFya2VycywgYEVSS19mYCAoa25vd24gZnJvbSBiZWZvcmUpIGFuZCBgU09TMWAuClRoZSBhdmVyYWdlIGFjdGl2aXR5IGRpZmZlcmVuY2UgdmFsdWVzIGZvciB0aGUgYFNPUzFgIHBlciBjZWxsIGxpbmUgbW9kZWwgZGF0YXNldHMgd2VyZToKPGRpdiBpZD0ic29zMiI+PC9kaXY+CmBgYHtyIFNPUzEgYXZlcmFnZSBkaWZmIHZhbHVlcyAoUEQtRzIpfQpkYXRhdGFibGUoZGF0YSA9IGFzLmRhdGEuZnJhbWUoZGlmZi5zdGF0ZS5tYXRbLCdTT1MxJ10pLCBjb2xuYW1lcyA9ICJTT1MxIiwgCiAgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ3QnLCBvcmRlciA9IGxpc3QobGlzdCgxLCAnYXNjJykpKSwgd2lkdGggPSAiNDcwcHgiLAogIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbigiQXZnIGFjdGl2aXR5IGRpZmYgdmFsdWVzIChhbGwgY2VsbCBsaW5lcyBleGNlcHQgQTQ5OCwgUEQtRzIpIiwgc3R5bGU9ImNvbG9yOiNkZDQ4MTQ7IGZvbnQtc2l6ZTogMThweCIpKSAlPiUKICBmb3JtYXRSb3VuZChjb2x1bW5zID0gMSwgZGlnaXRzID0gMykgJT4lCiAgZm9ybWF0U3R5bGUoY29sdW1ucyA9IDEsIGJhY2tncm91bmRDb2xvciA9IHN0eWxlSW50ZXJ2YWwoYnJlYWtzLnJlZCwgY29sb3JzLnJlZCkpCmBgYAoKSSBmb3VuZCBzb21lIFthcnRpY2xlc10oI3NvczEpIHRoYXQgSSBiZWxpZXZlIHNob3cgdGhhdCBgU09TMWAgaXMgZXhwcmVzc2VkIGluIHNvbWUgb2YgdGhlIGFib3ZlIGNhbmNlciBjZWxsIGxpbmVzIChpbiBjb250cmFzdCB0byB0aGUgcmVzdWx0cyBbYWJvdmVdKCNzb3MyKSkuCgojIyBQSS1EMSBhY3Rpdml0eSBiaW9tYXJrZXJzIHstfQoKQXMgb2JzZXJ2ZWQgaW4gdGhlIFtoZWF0bWFwIGFib3ZlXSgjb2JzZXJ2ZWQtc3luZXJnaWVzKSwgdGhlIGBQSS1EMWAgc3luZXJneSB3YXMgcHJlZGljdGVkIGJ5IGJvdGggdGhlICoqY2VsbCBzcGVjaWZpYyoqIGFuZCAqKnJhbmRvbSBtb2RlbHMqKiBpbiAqKnNvbWUgb2YgdGhlIGNlbGwgbGluZXMgdGVzdGVkKiouClNvLCBmb3IgZWFjaCBzZXBlcmF0ZSBtb2RlbCBkYXRhc2V0LCB3ZSBhcmUgZ29pbmcgdG8gY29udHJhc3QgdGhlIG1vZGVscyB0aGF0IHByZWRpY3RlZCB0aGUgYFBJLUQxYCBzeW5lcmd5IHZzIHRoZSBtb2RlbHMgdGhhdCBkaWQgbm90IChpZiB0aGVzZSB0d28gc2V0cyBvZiBtb2RlbHMgZG8gZXhpc3QgZm9yIGVhY2ggY2VsbCBsaW5lKSBhbmQgZmluZCB0aGUgYXZlcmFnZSBhY3Rpdml0eSBkaWZmZXJlbmNlIHBlciBub2RlIGluIGVhY2ggY2FzZToKCmBgYHtyIFBJLUQxIGF2ZXJhZ2UgYWN0aXZpdHkgc3RhdGUgZGlmZmVyZW5jZSBpbiBhbGwgY2VsbCBsaW5lc30KZHJ1Zy5jb21iID0gIlBJLUQxIgoKZGlmZi5zdGF0ZS5saXN0ID0gbGlzdCgpCgojIGNlbGwtc3BlY2lmaWMgZGF0YQpmb3IgKGNlbGwubGluZSBpbiBjZWxsLmxpbmVzKSB7CiAgbW9kZWwucHJlZGljdGlvbnMgPSBtb2RlbC5wcmVkaWN0aW9ucy5wZXIuY2VsbC5saW5lW1tjZWxsLmxpbmVdXQogIG1vZGVscy5zdGFibGUuc3RhdGUgPSBtb2RlbHMuc3RhYmxlLnN0YXRlLnBlci5jZWxsLmxpbmVbW2NlbGwubGluZV1dCiAgCiAgbW9kZWxzLnByZWRpY3RlZC5udW0gPSBzdW0obW9kZWwucHJlZGljdGlvbnNbLGRydWcuY29tYl0sIG5hLnJtID0gVCkKICBtb2RlbHMubm90LnByZWRpY3RlZC5udW0gPSBzdW0oIW1vZGVsLnByZWRpY3Rpb25zWyxkcnVnLmNvbWJdLCBuYS5ybSA9IFQpCiAgCiAgIyBTZWUgdGhlICNtb2RlbHMgdGhhdCBwcmVkaWN0ZWQgdnMgI21vZGVscyB0aG9zZSB0aGF0IGRpZCBub3QKICAjIHByaW50KHBhc3RlMChtb2RlbHMucHJlZGljdGVkLm51bSwgIiB2cyAiLCBtb2RlbHMubm90LnByZWRpY3RlZC5udW0pKQogIAogIGlmIChtb2RlbHMucHJlZGljdGVkLm51bSAhPSAwICYgbW9kZWxzLm5vdC5wcmVkaWN0ZWQubnVtICE9IDApCiAgICBkaWZmLnN0YXRlLmxpc3RbW2NlbGwubGluZV1dID0gZ2V0X2F2Z19hY3Rpdml0eV9kaWZmX2Jhc2VkX29uX3NwZWNpZmljX3N5bmVyZ3lfcHJlZGljdGlvbihtb2RlbC5wcmVkaWN0aW9ucywgbW9kZWxzLnN0YWJsZS5zdGF0ZSwgZHJ1Zy5jb21iKQp9CgojIHJhbmRvbSBtb2RlbCBkYXRhCiNyYW5kb20ubW9kZWxzLnByZWRpY3RlZC5udW0gPSBzdW0ocmFuZG9tLm1vZGVsLnByZWRpY3Rpb25zWyxkcnVnLmNvbWJdLCBuYS5ybSA9IFQpCiNyYW5kb20ubW9kZWxzLm5vdC5wcmVkaWN0ZWQubnVtID0gc3VtKCFyYW5kb20ubW9kZWwucHJlZGljdGlvbnNbLGRydWcuY29tYl0sIG5hLnJtID0gVCkKZGlmZi5zdGF0ZS5saXN0W1sicmFuZG9tIl1dID0gZ2V0X2F2Z19hY3Rpdml0eV9kaWZmX2Jhc2VkX29uX3NwZWNpZmljX3N5bmVyZ3lfcHJlZGljdGlvbihyYW5kb20ubW9kZWwucHJlZGljdGlvbnMsIHJhbmRvbS5tb2RlbHMuc3RhYmxlLnN0YXRlLCBkcnVnLmNvbWIpCgojIGNvbWJpbmUgdGhlIGRhdGEgdG8gYSBzaW5nbGUgbWF0cml4CmRpZmYuc3RhdGUubWF0ID0gZG8uY2FsbChyYmluZCwgZGlmZi5zdGF0ZS5saXN0KQpgYGAKCk5vdywgd2UgKippZGVudGlmeSB0aGUgYmlvbWFya2VycyBvZiBpbnRlcmVzdCoqIGFzIHRob3NlIG5vZGVzIHRoYXQgaGF2ZSBzdXJwYXNzZWQgYSB1c2VyLWRlZmluZWQgdGhyZXNob2xkICgkMC42JCkgYWNyb3NzIGFzIG1hbnkgYXMgcG9zc2libGUgY2VsbCBsaW5lcyAtIGNvdW50aW5nIHRodXMgdGhlICpmcmVxdWVuY3kqIHRoYXQgdGhpcyBoYXMgb2NjdXJlZC4gCldlIHNob3cgdGhvc2Ugbm9kZXMgdGhhdCBzdXJwYXNzZWQgdGhlIHRocmVzaG9sZCBpbiBhdCBsZWFzdCBoYWxmIG9mIHRoZSBjZWxsIGxpbmVzOgpgYGB7ciBQSS1EMSBhY3Rpdml0eSBiaW9tYXJrZXJzIGZyZXF1ZW5jeSBhY3Jvc3MgY2VsbCBsaW5lcywgcmVzdWx0cyA9ICdhc2lzJ30KYmlvbWFya2VyLnN0YXRlLm1hdCA9IGJpbmFyaXplX3RvX3RocmVzKG1hdCA9IGRpZmYuc3RhdGUubWF0LCB0aHJlcyA9IDAuNikKYmlvbWFya2Vycy5mcmVxID0gY29sU3VtcyhiaW9tYXJrZXIuc3RhdGUubWF0KS9ucm93KGJpb21hcmtlci5zdGF0ZS5tYXQpCgpwcmV0dHlfcHJpbnRfdmVjdG9yX25hbWVzX2FuZF92YWx1ZXMoYmlvbWFya2Vycy5mcmVxW2Jpb21hcmtlcnMuZnJlcSA+IDAuNV0pCmBgYAoKU28sIGBFUktfZmAgd2FzIGEgYmlvbWFya2VyIGZvciBhbGwgdGhlIGNlbGwgbGluZXMgdGhhdCBwcmVkaWN0ZWQgdGhlIGBQSS1EMWAgc3luZXJneSBhbmQgdGhlIG5vZGVzIGBJTEtgLCBgQ0RDNDJgIGFuZCBgUEFLMWAgaW4gbW9yZSB0aGFuIGhhbGYgb2YgdGhlbS4KSWYgd2Ugc2VlIHRoZSBhY3R1YWwgbnVtYmVycyB3ZSBjYW4gY2xhcmlmeSB0aGVpciBzdGF0ZSBhcyB3ZWxsOgoKPGRpdiBpZD0iVGFibGU5Ij48L2Rpdj4KYGBge3IgUEktRDEgYmlvbWFya2VycyAodGFibGUgOSl9CmJyZWFrcy5ncmVlbiA9IHF1YW50aWxlKGMoMCwxKSwgcHJvYnMgPSBzZXEoLjA1LCAuOTUsIC4wNSksIG5hLnJtID0gVFJVRSkKY29sb3JzLmdyZWVuID0gcm91bmQoc2VxKDI1NSwgNDAsIGxlbmd0aC5vdXQgPSBsZW5ndGgoYnJlYWtzLnJlZCkgKyAxKSwgZGlnaXRzID0gMCkgJT4lCiAge3Bhc3RlMCgicmdiKCIsIC4sICIsMjU1LCIsIC4sICIpIil9ICMgZ3JlZW4KCmNhcHRpb24udGl0bGUuOSA9ICJUYWJsZSA5OiBBdmVyYWdlIGFjdGl2aXR5IHN0YXRlIGRpZmZlcmVuY2UgdmFsdWVzIGFjcm9zcyBhbGwgbW9kZWwgZGF0YXNldHMgdGhhdCBwcmVkaWN0ZWQgUEktRDEiCmRhdGF0YWJsZShkYXRhID0gZGlmZi5zdGF0ZS5tYXRbLGMoIklMSyIsIlBBSzEiLCJDREM0MiIsIkVSS19mIiwiUkFDX2YiLCJBUkhHQVAyNCIsIlNSQyIpXSwgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ3QnKSwKICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oY2FwdGlvbi50aXRsZS45LCBzdHlsZT0iY29sb3I6I2RkNDgxNDsgZm9udC1zaXplOiAxOHB4IikpICU+JSAKICBmb3JtYXRSb3VuZCgxOjExLCBkaWdpdHMgPSAzKSAlPiUKICBmb3JtYXRTdHlsZShjb2x1bW5zID0gYygxOjMpLCBiYWNrZ3JvdW5kQ29sb3IgPSBzdHlsZUludGVydmFsKGJyZWFrcy5yZWQsIGNvbG9ycy5yZWQpKSAlPiUKICBmb3JtYXRTdHlsZShjb2x1bW5zID0gNCwgYmFja2dyb3VuZENvbG9yID0gc3R5bGVJbnRlcnZhbChicmVha3MuZ3JlZW4sIGNvbG9ycy5ncmVlbikpCmBgYAoKPGRpdiBjbGFzcz0ib3JhbmdlLWJveCI+CklmIHdlIGV4YW1pbmUgdGhlIGJvb2xlYW4gZXF1YXRpb25zOiBgSUxLICo9IFBBSzFgLCBgUEFLMSAqPSBDREM0MiBvciBSQUNfZmAgYW5kIGBDREM0MiAqPSBTUkMgYW5kL29yIG5vdCBBUkhHQVAyNGAgYXMgd2VsbCBhcyB0aGUgaW5mb3JtYXRpb24gZnJvbSB0aGUgW3RhYmxlIDldKCNUYWJsZTkpLCB0aGVuIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGF0ICoqdGhlIG9ubHkgaW5oaWJpdGVkIG5vZGUgb2YgaW50ZXJlc3QgaXMgYENEQzQyYCBhbW9uZyB0aGUgMyBtb3N0IGZyZXF1ZW50IGluaGliaXRlZCBiaW9tYXJrZXJzKiouCjwvZGl2Pgo8YnI+Cgo8ZGl2IGNsYXNzPSJibHVlLWJveCI+ClRoZSAqKm92ZXJleHByZXNzaW9uKiogb2YgYEVSS19mYCBhbmQgdGhlICoqaW5oaWJpdGlvbioqIG9mIGBDREM0MmAgYXJlIHRoZSB0d28gbW9zdCBjb21tb24gYmlvbWFya2VycyB0aGF0IGNoYXJhY3Rlcml6ZSB0aGUgbW9kZWxzIHRoYXQgYXJlIGFibGUgdG8gcHJlZGljdCB0aGUgYFBJLUQxYCBvYnNlcnZlZCBzeW5lcmd5Lgo8L2Rpdj4KCiMjIFIgc2Vzc2lvbiBpbmZvIHstfQoKYGBge3Igc2Vzc2lvbiBpbmZvLCBjb21tZW50PSIifQp4ZnVuOjpzZXNzaW9uX2luZm8oKQpgYGAK