Overview on substrate uptake, growth, and yield
After running a set of simulations in RBApy that simulate increasing substrate limitation, we can plot the substrate uptake rate q
, yield Y
in gram biomass per gram substrate, and growth rate µ
. Unlike genome scale models, growth becomes limited by the maximum amount of proteins that a cell can synthesize. If cells would not be protein-limited, or proteins would catalyze reactions infinitely fast, no such limitation would take place and growth rate would scale linearly with substrate concentration. This is the situation in FBA simulation.
# rearrange some rows (mu, qS) to columns
df_macr <- df_macr %>% filter(!grepl("test_process", key)) %>%
spread(key, value) %>%
# add type of simulation
mutate(substrate = case_when(
carbon_source == "for" ~ "formate",
carbon_source == "succ" ~ "succinate",
carbon_source == "fru" & nitrogen_conc == 18.7 ~ "fructose",
carbon_source == "fru" & nitrogen_conc != 18.7 ~ "ammonium"
)) %>%
# add uptake rate in g/gDCW*h instead of mmol
mutate(qS_g_gDCW_h = case_when(
substrate == "formate" ~ qS*0.04603,
substrate == "succinate" ~ qS*0.11809,
substrate == "fructose" ~ qS*0.18016,
substrate == "ammonium" ~ qS*0.05349
))
First we can have a look at how growth rate levels off with increasing substrate concentration in mmol/L. Note: this is not equal to substrate uptake rate. Substrate uptake rate and growth rate should have an almost linear relationship. We can log-transform the carbon concentration and fit a linear model to predict the substrate concentration required to obtain a certain substrate uptake rate and growth rate.
# copy nitrogen to carbon concentration for ammonium limitation,
# just for plotting purposes
df_macr_viz <- df_macr %>%
mutate(carbon_conc = case_when(
substrate == "ammonium" ~ nitrogen_conc,
TRUE ~ carbon_conc
))
plot_mu_qs_lin <- xyplot(mu ~ carbon_conc | substrate, df_macr_viz,
par.settings = custom.colorblind(),
between = list(x = 0.5, y = 0.5),
layout = c(4,1), lwd = 1.5, pch = 19,
xlab = expression("S [mM]"),
ylab = expression('µ [h'^'-1'*']'),
scales = list(alternating = FALSE),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.xyplot(x, y, cex = 0.9, ...)
#panel.lmlineq(x[1:4], y[1:4], fontfamily = "FreeSans", ...)
}
)
plot_mu_qs_log <- xyplot(log10(carbon_conc) ~ qS | substrate,
df_macr_viz,
par.settings = custom.colorblind(),
between = list(x = 0.5, y = 0.5), #xlim = c(0, 5),
layout = c(4,1), lwd = 1.5, pch = 19,
xlab = expression('q'[S]*' mmol g DCW'^-1*'h'^-1),
ylab = expression('log'[10]*' S [mM]'),
scales = list(alternating = FALSE),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.xyplot(x, y, cex = 0.9, ...)
panel.lmlineq(x[1:6], y[1:6], fontfamily = "FreeSans", ...)
}
)
print(plot_mu_qs_lin, split = c(1,1,1,2), more = TRUE)
print(plot_mu_qs_log, split = c(1,2,1,2))
df_macr_viz %>%
group_by(substrate) %>%
# fit linear model to substrate uptake rate vs concentration
summarize(
slope = lm(x ~ y, data = list(x = log10(carbon_conc), y = qS))$coeff[2],
offset = lm(x ~ y, data = list(x = log10(carbon_conc), y = qS))$coeff[1]
) %>% mutate(model = paste0("c = 10^(", round(offset, 3), " + ", round(slope, 3), "*qS)"))
Create a Herbert-Pirt plot for each condition (growth rate versus substrate uptake rate). This plot would show a change in yield by a ‘kink’ of the data points.
xyplot(qS_g_gDCW_h ~ mu | substrate, df_macr,
par.settings = custom.colorblind(),
between = list(x = 0.5, y = 0.5),
layout = c(4,1), lwd = 1.5, pch = 19,
ylab = expression("q"[S]*" [g h"^-1*" gDCW"^-1*"]"),
xlab = expression('µ [h'^'-1'*']'),
scales = list(alternating = FALSE),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.xyplot(x, y, cex = 0.9, ...)
# displaying maintenance and yield coefficients
coef <- lm(y ~ x, data.frame(x, y))$coeff
panel.text(median(x), 2.7,
paste("ms =", round(coef[[1]], 3), "g h-1 g_DCW-1"),
col = grey(0.3), cex = 0.7)
panel.text(median(x), 2.4, paste(expression("Yx/S ="),
round(1/coef[[2]], 3), "g_DCW g_S-1"),
col = grey(0.3), cex = 0.7)
}
)
Resource allocation in terms of protein mass
To determine the true allocation of protein resources per compartment, but also the true cost of protein per process, we need to convert the predicted concentration of proteins in mmol per gDCW to g per gDCW, simply by multiplying protein concentration with the molecular weight of a protein (g/mol, converted to g/mmol). We can then also easily transform g/gDCW to mass fraction by dividing individual protein concentrations by the sum of all protein concentrations. The protein mass fraction is dimensionless. The only parameter required for this transformation is the molecular weight per protein which is available from uniprot. We can for example take the protein annotation table that is automatically downloaded during RBApy
model generation.
# import downloaded Ralstonia protein annotation from uniprot
df_uniprot <- read_tsv("../data/input/uniprot.csv", col_types = cols()) %>%
mutate(locus_tag = stri_extract_first(`Gene names`, regex = "H16_[AB][0-9]{4}|PHG[0-9]{3}"))
# merge predicted protein allocation with molecular weight info from uniprot
df_prot <- left_join(df_prot, select(df_uniprot, locus_tag, Length, Mass),
by = c("key" = "locus_tag")) %>%
# calculate predicted protein mass in g/gDCW using MW in g/mmol, and mass fraction
group_by(simulation) %>% mutate(
predicted_mass_g_gDCW = value * Mass / 1000)
# test if mass fractions sum to reasonable value
df_prot %>% summarize(
predicted_mass_g_gDCW = sum(predicted_mass_g_gDCW, na.rm = TRUE))
The simulated protein mass per gDCW changes with growth rate, and it is considerably lower then the estimated ~0.65-0.68 g total protein/gDCW that was previously estimated for bacteria (see e.g. Park et al., biomass composition for Ralstonia eutropha model, or Touloupakis et al., biomass composition for cyanobacteria). One reason is that above numbers only include enzymatic proteins (the ones represented by the GSM). For machinery such as ribosomal proteins, a separate calculation needs to be performed. The model returns estimated concentration of machineries for replication, transcription, translation, and protein folding. The associated proteins can be imported from the model folder and the total mass estimated using molecular weight and subunit stoichiometry as done before for enzymatic proteins.
machinery_names <- c("replication", "transcription", "ribosome", "chaperones")
machinery_tables <- paste0("../data/simulation/macro_machines/", machinery_names, ".tsv")
df_macr <- df_macr %>%
# remove unused columns and rename important ones
select(-P_ENZ, -P_RNADEG) %>%
rename(replication = P_REP, transcription = P_TSC,
ribosome = P_TA, chaperones = P_CHP, substrate_uptake_rate = qS,
predicted_growth_rate = mu
) %>%
# gather individual machineries in one column
gather(key = machine, value = predicted_mass_mmol_gDCW,
chaperones:transcription) %>%
# round uptake rates for merging
mutate(substrate_uptake_rate = round(substrate_uptake_rate, 3))
df_machinery <- lapply(machinery_tables, read_tsv, col_type = cols()) %>%
bind_rows(.id = "machine") %>%
mutate(machine = recode(machine, !!!setNames(machinery_names, 1:4))) %>%
select(-`Entry name`, -Sequence, -Cofactor, -`EC number`, -`Organism ID`, `Organism`,
-`Catalytic activity`, -Status) %>%
# join with prediction of molecular machine concentration (mmol/gDCW)
left_join(df_macr, by = "machine") %>%
# calculate predicted protein mass in g/gDCW using MW in g/mmol, and mass fraction
group_by(simulation) %>% mutate(
predicted_mass_g_gDCW = predicted_mass_mmol_gDCW * Stoichiometry * Mass / 1000
)
# test if mass fractions sum to reasonable value
df_machinery %>%
summarize(sum_g_gDCW = sum(predicted_mass_g_gDCW, na.rm = TRUE)) %>%
ungroup %>% head(10)
If the utilized protein for enzymes and machinery is summed up, it does not exceed ~0.25 g/gDCW. The reason for this is that up to 60% of the protein mass is not modeled (non enzymatic, NE proteome, 57.7% at µ = 0, see R notebook Ralstonia-model-constraints
), and only around 68% of the total cell mass is protein. If we consider this, than total protein mass can be estimated as m = 0.25/(1-0.6) = 0.625 g/gDCW. This is much closer to the estimated 0.68 g protein/gDCW. The difference can be attributed to default values of amino acid concentration in RBApy
that determine the size of the ‘protein pool’. It is important to note that the sum of utilizable proteins is not a constant value but a linear function of growth rate. The total proteome pool including non-enzymatic proteins is, however, constant.
Correlation between predicted and experimentally determined proteome
To compare the predicted and experimental proteome composition, we load the required proteomics data. Proteomics data are mass spectrometry measurements with label-free quantification of peptides. Protein quantification was performed by summing up all peptide intensities per annotated protein. The proteomic measurement unit, mass fraction, can be easily transformed to g/gDCW by multiplying mass fraction with the total protein mass (0.68 g/gDCW) used in RBApy simulations. Or vice versa by converting RBApy protein concentration (mmol/gDCW) to mass fraction.
To allow a fair comparison between measured and predicted data, it is necessary to aggregate (e.g. sum up) all protein abundances allocated to one reaction. The reason is that the model will only predict protein abundance of the first of a range of iso-enzymes for a particular reaction, while in reality another iso-enzyme might be more abundant (carry the majority of flux). This would lead to lower correlation between measured and predicted protein concentrations. This is not necessary for machinery proteins, they have no iso-enzymes and are used under all conditions.
Combine simulations and experimental data
The proteomics data must be merged with RBApy
simulation results using matching conditions. First, proteomics data are loaded and prepared for merging.
Step 1: load proteomics data
load(Reutropha_proteomics)
# pick a condition matching simulations
Ralstonia_eutropha <- Ralstonia_eutropha %>%
# round substrate uptake rate
mutate(substrate_uptake_rate = round(substrate_uptake_rate, 3)) %>%
# select only required columns
rename(growth_rate = growthrate) %>%
select(uniprot, locus_tag, protein, condition, substrate, substrate_uptake_rate,
growth_rate, COG_Process, R1:R4) %>%
# turn raw intensity measurements into mass in g per gDCW (assuming a
# total protein concentration of 0.68 g/gDCW)
group_by(condition) %>%
mutate(across(matches("R[1234]"), function(x) x/sum(x, na.rm = TRUE)*0.68)) %>%
gather(key = "replicate", value = "mass_g_gDCW", R1:R4)
Step 2: Load gene reaction associations obtained from genome scale model
df_model_reactions <- read_csv(model_reactions, col_types = cols()) %>%
# filter for reactions with gene associations
select(reaction_id, reaction_name, genes, groups) %>% separate_rows(genes, sep = ", ") %>%
filter(!is.na(genes)) %>%
rename(model_group = groups) %>%
# add a more general groups description
mutate(model_group_basic = case_when(
grepl("Phenylala|Valine|Tyrosine|Glutamate|Glycine|Tryptophan|Methionine|
Cysteine|Alanine|Histidine|Arginine|Lysine", model_group) ~ "Amino acid",
grepl("Pentose|Calvin", model_group) ~ "PPP + Calvin cycle",
model_group == "Citric Acid Cycle" ~ "Citric Acid Cycle",
model_group == "Glycolysis/Gluconeogenesis" ~ "Glycolysis/Gluconeogenesis",
model_group == "Glyoxylate and Dicarboxylate metabolism" ~ "Autotrophic energy",
model_group == "Oxidative Phosphorylation" ~ "Oxidative Phosphorylation",
TRUE ~ "Other"
))
Step 3: Select and rename conditions from RBA simulation
# add type of substrate limitation
add_cond <- function(df) {
df %>% mutate(substrate = case_when(
carbon_source == "succ" ~ "succinate",
carbon_source == "for" ~ "formate",
carbon_source == "fru" & nitrogen_conc == 18.7 ~ "fructose",
TRUE ~ "ammonium",
))
}
# add substrate uptake rate
df_substrate_uptake <- df_macr %>%
select(simulation, substrate_uptake_rate) %>% distinct
df_prot <- df_prot %>% add_cond %>% left_join(df_substrate_uptake, by = "simulation")
df_flux <- df_flux %>% add_cond %>% left_join(df_substrate_uptake, by = "simulation")
Step 4: Merge protein measurements and predictions into master tables
The first step is to merge the tables for machinery proteins, that means proteins related to replication, transcription, translation, and protein folding. These don’t require allocation of protein mass to reactions, and merging becomes simply an operation on enzyme IDs and conditions.
# join with proteomics data
df_machinery <- df_machinery %>%
left_join(Ralstonia_eutropha,
by = c("Entry" = "uniprot", "substrate", "substrate_uptake_rate"))
The second table for all enzymatic proteins requires the allocation of estimated protein mass to enzymes. One option for the future is to retrieve these values directly from RBApy, but this is not implemented yet.
df_prot_comp <- df_model_reactions %>%
# join with proteomics data
left_join(Ralstonia_eutropha, by = c("genes" = "locus_tag")) %>%
complete(nesting(genes, reaction_id, reaction_name, model_group, model_group_basic),
nesting(condition, substrate, substrate_uptake_rate, growth_rate, replicate)) %>%
filter(!is.na(substrate)) %>%
# join with simulation data
left_join(df_prot, by = c("genes" = "key", "substrate", "substrate_uptake_rate")) %>%
# determine number of reactions per protein
group_by(condition, genes, replicate) %>%
mutate(n_reactions = length(reaction_id)) %>%
# calculate protein mass in g/gDCW
ungroup %>% mutate(
predicted_mass_g_gDCW = predicted_mass_g_gDCW/n_reactions,
mass_g_gDCW = mass_g_gDCW/n_reactions
) %>%
# summarize by summing up protein abundance per reaction (NA treated as zero)
group_by(condition, reaction_id, reaction_name, model_group,
model_group_basic, substrate, substrate_uptake_rate, growth_rate, replicate) %>%
summarize(
predicted_mass_g_gDCW = sum(predicted_mass_g_gDCW, na.rm = TRUE),
mass_g_gDCW = sum(mass_g_gDCW, na.rm = TRUE)
) %>%
filter(mass_g_gDCW != 0) %>%
# add predicted growth rate to experimental
left_join(
df_machinery %>% ungroup %>%
select(substrate, substrate_uptake_rate, predicted_growth_rate) %>%
filter(!duplicated(substrate_uptake_rate))
) %>%
# add predicted fluxes per reaction and condition
left_join(
df_flux %>% ungroup %>%
select(key, value, substrate, substrate_uptake_rate) %>%
rename(reaction_id = key, flux_mmol_gDCW_h = value)
)
`summarise()` has grouped output by 'condition', 'reaction_id', 'reaction_name', 'model_group', 'model_group_basic', 'substrate', 'substrate_uptake_rate', 'growth_rate'. You can override using the `.groups` argument.
Joining, by = c("substrate", "substrate_uptake_rate")
Joining, by = c("reaction_id", "substrate", "substrate_uptake_rate")
Now we perform a test. We check if all protein abundances allocated to reactions sum to a reasonable value as we would expect. This value would be the total enzymatic protein mass in g/gDCW, per condition and replicate, and could reach up to 0.2 g/gDCW for the simulations, and higher for the actual data (includes all additional proteins quantified in experiment, but not carrying flux in model simulations).
df_prot_comp %>% group_by(condition, replicate) %>%
filter(predicted_mass_g_gDCW != 0) %>%
summarize(sum(mass_g_gDCW), sum(predicted_mass_g_gDCW))
`summarise()` has grouped output by 'condition'. You can override using the `.groups` argument.
The total predicted protein mass is lower than the measured protein mass. Therefore the following section quantifies discrepancies between model predicted and actually measured abundances. First we can inspect the top N reactions with highest average predicted protein abundance. The ratio of predicted divided by measured mass indicates that a handful of proteins are predicted to be more than 10 fold abundant compared to the measured abundance. This points towards fluxes being erroneously predicted too high for particular reactions, or k_app values being estimated too low for the estimated flux.
However, the largest discrepancies arise from under-estimation of proteins, the main cause being that the model predicts the optimal abundance for each enzyme to carry a certain flux. If fluxes are drastically reduced due to strong substrate limitation, the minimal required protein abundance to optimize growth will be much lower than the measured abundance. A bacterial cell on the other hand can not fully reduce its proteome but instead ‘suspends’ inactive enzymes.
Change of machinery proteins with growth rate
The simulation and measurement data was prepared and merged by condition in the previous sections. Now it can be plotted to e.g. compare protein allocation over growth rate. Interestingly, we see that model predictions are quite accurately reflecting the range of protein allocation for the four different machineries, see following paragraphs. This is a good confirmation of the model’s predictive power, given that the rates of these machineries were not fitted from data but taken purely from literature. There are however some deviations from the predicted ‘optimal’ proteome:
- the most important machine is the ribosome. Prediction and experiment show a very similar increase of ribosome abundance with growth rate (slope of linear model), but the intersection (amount of unused ribosomes at zero growth) is much higher in experiment
- chaperones show an inverse proportional relationship with growth rate contrary to model prediction. Do (some of?) these proteins have another role than just folding, like stress response?
- transcription sector is quite stable, however, predicted enzyme mass is much lower than measured (underestimated by 1 order of magnitude , 5x10^-3 vs 5x10^-4 g/gDCW)
- replication sector is heavily underestimated by 3 orders of magnitude (10^-3 vs 10^-6 g/gDCW)
The protein allocation for the two low-abundant machines, replication and transcription is very hard to see. We can update the Y-axis limits for this plot and re-plot it with 10-fold ‘zoom’. Abundance of replication machinery increases with growth rate from 0.0010 to a maximum of 0.0015 g/gDCW, an increase by 20-50%. For transcription, abundance increases with growth rate from 0.006 to 0.008 g/gDCW, an increase of 33%.
df_machinery_util <- df_machinery %>%
# remove 1 non-quantified replicate
filter(!(substrate == "fructose" & growth_rate == 0.1 & replicate == "R2")) %>%
# summing up protein mass over all conditions
group_by(machine, substrate, growth_rate, replicate) %>%
summarize(
`mass [g/gDCW]` = sum(mass_g_gDCW, na.rm = TRUE),
`predicted mass [g/gDCW]` = sum(predicted_mass_g_gDCW, na.rm = TRUE),
utilization = `predicted mass [g/gDCW]`/`mass [g/gDCW]`
) %>% ungroup %>%
# replace 0 with NA and reorder factors
mutate(across(matches("mass"), ~ na_if(.x, 0))) %>%
mutate(machine = machine %>% factor(., unique(.)[c(2,4,3,1)]))
`summarise()` has grouped output by 'machine', 'substrate', 'growth_rate'. You can override using the `.groups` argument.
# plot mass and utilization
plot_machinery <-
xyplot(`mass [g/gDCW]` + `predicted mass [g/gDCW]` ~ factor(growth_rate) |
machine * substrate, df_machinery_util,
par.settings = custom.colorblind(), auto.key = list(columns = 2, cex = 0.7),
xlab = expression("µ [h"^-1*"]"),
ylab = expression("m"[protein]*" [g gDCW"^-1*"]"),
scales = list(alternating = FALSE, x = list(at = c(2,4))),
as.table = TRUE, ylim = c(-0.01, 0.12),
between = list(x = 0.5, y = 0.5), pch = 19, lwd = 2,
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.errbars(x, y, ewidth = 0, ...)
panel.superpose(x, y, ...)
}, panel.groups = function(x, y, ...) {
panel.lmline(x, y, ...)
}
)
Under-utilization of machinery proteins
Here, we determine the underutilized machinery fraction by taking the ratio of simulated optimal enzyme abundance and experimentally measured abundance, over all four central dogma machines.
plot_machinery_util <- df_machinery %>%
# remove 1 non-quantified replicate
filter(!(substrate == "fructose" & growth_rate == 0.1 & replicate == "R2")) %>%
# summarizing protein utilization for all machines
mutate(machine = "machines") %>%
group_by(machine, substrate, growth_rate, replicate) %>%
summarize(
mass = sum(mass_g_gDCW, na.rm = TRUE),
predicted_mass = sum(predicted_mass_g_gDCW, na.rm = TRUE),
utilization = predicted_mass/(mass)
) %>%
# plot. use alternating = 2 to switch axis to right side
xyplot(utilization*100 ~ factor(growth_rate) | machine * substrate, .,
par.settings = custom.colorblind(),
scales = list(x = list(at = c(2,4)), y = list(alternating = FALSE)),
xlab = expression("µ [h"^-1*"]"), ylab = "",
key = simpleKey("% utilization", cex = 0.7),
as.table = TRUE, between = list(x = 0.5, y = 0.5),
pch = 19, lwd = 2, layout = c(1, 4), ylim = c(-10, 110),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
x_mean = unique(x); y_mean = tapply(y, x, mean)
panel.xyarea(c(0, x_mean, 6), c(0, y_mean, tail(y_mean, 1)),
lty = 0, col = grey(0.6, alpha = 0.5), ...)
panel.errbars(x, y, ewidth = 0, col = grey(0.5), ...)
}
) %>% useOuterStrips
`summarise()` has grouped output by 'machine', 'substrate', 'growth_rate'. You can override using the `.groups` argument.
print(useOuterStrips(plot_machinery), position = c(0, 0, 0.77, 1.027), more = TRUE)
print(plot_machinery_util, position = c(0.71, 0, 1.02, 1.027))
grid::grid.text(c("B", "C"), x = c(0.02, 0.75), y = c(0.97,0.97))
Under-utilization of enzymes
Global trends in utilization
We can subdivide enzymes by utilization and plot average mass, average variability, and average essentiality. Average essentiality here means that every reaction that is associated with at least one probably essential genes is probably essential, and every reactions with at least one essential gene is counted as essential (taking precedence over ‘probably essential’).
df_util <- df_prot_comp %>%
# filter reactions without protein measurement or substrate
filter(!(is.na(mass_g_gDCW) | is.na(substrate)), growth_rate == 0.25) %>%
# first calculate utilization per condition
group_by(model_group_basic, reaction_id, reaction_name, substrate) %>%
summarize(.groups = "drop_last",
mass_g_gDCW = mean(mass_g_gDCW, na.rm = TRUE),
predicted_mass_g_gDCW = mean(predicted_mass_g_gDCW, na.rm = TRUE),
utilization = predicted_mass_g_gDCW/mass_g_gDCW
) %>%
# determine average mass, sd and utilization per reaction
summarize(.groups = "drop_last",
mean_mass = mean(mass_g_gDCW, na.rm = TRUE),
sd_mass = sd(mass_g_gDCW, na.rm = TRUE),
cv_mass = sd_mass/mean_mass,
utilization = mean(utilization, na.rm = TRUE)
) %>%
# manual 'clustering' based on protein vs growth rate slope
mutate(utilization_group = case_when(
utilization <= 0.33 ~ "low",
between(utilization, 0.33, 0.66) ~ "moderate",
utilization > 0.66 ~ "high",
) %>% factor(., c("low", "moderate", "high")))
# import essentiality from TnSeq/BarSeq data
df_util <- read_csv("../data/output/essentiality_escher.csv", col_types = cols()) %>%
mutate(essentiality = essentiality %>% recode(
`0` = "not essential", `1` = "probably essential", `2` = "essential")) %>%
mutate(essentiality = case_when(
locus_tag %in% read_csv("../data/output/essentiality_fructose.csv", col_types = cols())$locus_tag ~ "essential",
TRUE ~ essentiality
)) %>%
# merge with utilization
inner_join(df_model_reactions %>% rename(locus_tag = genes)) %>%
group_by(reaction_id) %>% summarize(essentiality = max(essentiality, na.rm = TRUE) %>%
recode(`0` = "not essential", `1` = "probably essential", `2` = "essential")) %>%
right_join(df_util)
Joining, by = "locus_tag"
Joining, by = "reaction_id"
Test if mean enzyme mass and enzyme variability (CV) are normal-distributed. This is important for choosing the statistical test for significance. We use the Shapiro-Wilks test for normality.
df_util %>% group_by(utilization_group) %>%
summarize(
Shapiro_Wilk_pvalue_mass = shapiro.test(log10(mean_mass))$p.value,
Shapiro_Wilk_pvalue_CV = shapiro.test(log10(cv_mass))$p.value
)
Now plot average mass in g/gDCW, CV of mass in g/gDCW and essentiality (score) from TnSeq experiments. As some of the data is not normally distributed, we apply a non-parametric significance test instead - here, for comparison of two samples, the best alternative to Student’s t-test is the Mann-Whitney U-test. For one sample comparisons, the non-parametric alternative to t-test would be the Wilcoxon Rank Sum test.
boxplot_colorblind <- custom.colorblind()
boxplot_colorblind$box.rectangle$lwd = 1.5
boxplot_colorblind$box.umbrella$lwd = 1.5
plot_underutil_mass <- xyplot(log10(mean_mass) ~ utilization_group,
df_util,
par.settings = boxplot_colorblind,
horizontal = FALSE, do.out = FALSE, ylim = c(-8, -1),
xlab = "utilization", ylab = expression("m"[protein]*" [log"[10]*" g gDCW"^-1*"]"),
scales = list(x = list(cex = 0.7)),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.violin(x, y, lwd = 0, col = grey(0.6, 0.5), box.width = 0.7, ...)
panel.bwplot(x, y, pch = "|", box.width = 0.3, ...)
panel.pvalue(x, y, cex = 0.7, fixed_pos = -0.9, verbose = TRUE, method = "wilcox.test", ...)
panel.text(unique(x), -7.5, labels = paste0("n=", table(x)), cex = 0.6)
}
)
plot_underutil_cv <- xyplot(log10(cv_mass) ~ utilization_group,
df_util,
par.settings = boxplot_colorblind,
horizontal = FALSE, do.out = FALSE, ylim = c(-1.6,0.6),
xlab = "utilization", ylab = expression("CV m"[protein]*" [log"[10]*"]"),
scales = list(x = list(cex = 0.7)),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.violin(x, y, lwd = 0, col = grey(0.6, 0.5), box.width = 0.7, ...)
panel.bwplot(x, y, pch = "|", box.width = 0.3, ...)
panel.pvalue(x, y, cex = 0.7, fixed_pos = 0.65, verbose = TRUE, method = "wilcox.test", ...)
panel.text(unique(x), -1.45, labels = paste0("n=", table(x)), cex = 0.6)
}
)
plot_underutil_es <- xyplot(n_reactions ~ utilization_group,
df_util %>% group_by(utilization_group, essentiality) %>% summarize(n_reactions = n()),
par.settings = boxplot_colorblind,
col = custom.colorblind()$superpose.polygon$col[c(5,4,2)],
groups = factor(essentiality, c("not essential", "probably essential", "essential")),
xlab = "utilization", ylab = "number of reactions", ylim = c(-20, 620),
ewidth = 0.1, lwd = 2, scales = list(x = list(cex = 0.7)),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.barplot(x, y, beside = TRUE, ...)
panel.key(..., points = FALSE, corner = c(0.95, 0.95), cex = 0.7)
}
)
`summarise()` has grouped output by 'utilization_group'. You can override using the `.groups` argument.
print(plot_underutil_mass, position = c(0,0,0.35,1.05), more = TRUE)
print(plot_underutil_cv, position = c(0.32,0,0.68,1.05), more = TRUE)
print(plot_underutil_es, position = c(0.64,0,1,1.05))
grid::grid.text(c("A", "B", "C"), x = c(0.02,0.35,0.65), y = c(0.94,0.94,0.94))
We can try to combine several types of information for each reaction in concise mini-figures:
- the essentiality (simply by color around reaction name)
- the average protein mass associated with the reaction
- the change over conditions (CV)
- the utilization/saturation
# enzymes grouped by pathway
list_ccm_enzymes <- list(
cbb = c("PGK", "TPI", "GAPD", "FBA", "FBP", "TKT1", "TKT2", "TAh", "RPE", "RPI", "PRUK", "RBPC", "FDH"),
ed = c("PGI", "G6PDH2r", "PGL", "EDD", "EDA"),
pyr = c("PGM", "ENO", "PYK", "PDH1", "PDH2", "PDH3", "PC", "PPC", "ME1", "ME2", "CS"),
tca = c("ACONT1", "ACONT2", "ICDHx", "ICDHyr", "AKGDH", "SUCOAS", "SUCDi", "SUCD1", "FUM", "MDH", "MALS")
)
plot_minifigs <- df_prot_comp %>%
filter(reaction_id %in% unlist(list_ccm_enzymes),
growth_rate == 0.25) %>%
group_by(reaction_id, substrate) %>%
# calculate utilization
mutate(utilization = predicted_mass_g_gDCW/mass_g_gDCW) %>%
summarize(utilization = mean(utilization), mean_mass = mean(mass_g_gDCW)) %>%
# rescale all to new range between 0 and 1
ungroup %>%
mutate(across(matches("mass|utilization$"), scales::rescale)) %>%
pivot_longer(all_of(c("mean_mass", "utilization"))) %>%
mutate(name = factor(name, c("mean_mass", "utilization"))) %>%
xyplot(value ~ name | reaction_id, .,
groups = substrate, par.settings = custom.colorblind(),
xlab = "", ylab = "", fill_alpha = 1,
ewidth = 0.09, lty = 1.5, as.table = TRUE,
scales = list(draw = FALSE), ylim = c(-0.1, 1.0),
between = list(x = 0.5, y = 0.5),
panel = function(x, y, ...) {
panel.rect(0.5,-0.1,2.5,1.1, col = "white", border = "transparent")
panel.rect(0.5,-0.1,1.5,1.0, col = grey(0.9), border = "transparent")
panel.barplot(x, y, beside = TRUE, ...)
}
)
`summarise()` has grouped output by 'reaction_id'. You can override using the `.groups` argument.
print(plot_minifigs)
Central carbon metabolism, detailed utilization per enzyme
Analogously to the above analysis, we can plot protein abundance and utilization for all enzymes of central carbon metabolism (from Figure 3 D).
plot_enz_ccm <- lapply(list_ccm_enzymes, function(lst) {
df_prot_comp %>%
filter(reaction_id %in% lst, !is.na(condition)) %>%
mutate(reaction_id = factor(reaction_id, lst)) %>%
group_by(reaction_id) %>% mutate(mass_g_gDCW = scales::rescale(mass_g_gDCW)) %>%
xyplot(mass_g_gDCW ~ factor(growth_rate) | reaction_id, .,
par.settings = custom.colorblind(),
groups = substrate, xlab = "", #expression("µ [h"^-1*"]"),
#ylab = expression("m"[protein]*" [g gDCW"^-1*"]"),
ylab = "relative abundance",
layout = c(8, ifelse(length(lst) > 8, 2, 1)),
ewidth = 0, lwd = 2, as.table = TRUE,
between = list(x = 0.5, y = 0.5),
scales = list(alternating = FALSE, x = list(at = c(2, 4))),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.errbars(x, y, ...)
}
)
})
print(plot_enz_ccm[[1]], position = c(0.0,0.67,1,1), more = TRUE)
print(plot_enz_ccm[[2]], position = c(0,0.51,1,0.75), more = TRUE)
print(plot_enz_ccm[[3]], position = c(0,0.26,1,0.59), more = TRUE)
print(plot_enz_ccm[[4]], position = c(0,0,1,0.34))
grid::grid.text(c("CBB", "ED", "PYR", "TCA"), x = c(0.03, 0.03, 0.03, 0.03), y = c(0.97, 0.71, 0.53, 0.3))
And we can add utilization for the different enzymes and conditions too.
plot_util_ccm <- lapply(list_ccm_enzymes, function(lst) {
df_prot_comp %>%
filter(reaction_id %in% lst, !is.na(condition)) %>%
mutate(reaction_id = factor(reaction_id, lst)) %>%
# calculate utilization
mutate(percent_utilization = predicted_mass_g_gDCW/mass_g_gDCW*100) %>%
xyplot(percent_utilization ~ factor(growth_rate) | reaction_id, .,
par.settings = custom.colorblind(),
groups = substrate,
xlab = "", ylab = "% utilization", ylim = c(-20, 200),
layout = c(8, ifelse(length(lst) > 8, 2, 1)),
ewidth = 0, lwd = 2, as.table = TRUE,
between = list(x = 0.5, y = 0.5),
scales = list(alternating = FALSE, x = list(at = c(2, 4))),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.errbars(x, y, ...)
}
)
})
print(plot_util_ccm[[1]], position = c(0.0,0.67,1,1), more = TRUE)
print(plot_util_ccm[[2]], position = c(0,0.51,1,0.75), more = TRUE)
print(plot_util_ccm[[3]], position = c(0,0.26,1,0.59), more = TRUE)
print(plot_util_ccm[[4]], position = c(0,0,1,0.34))
grid::grid.text(c("CBB", "ED", "PYR", "TCA"), x = c(0.03, 0.03, 0.03, 0.03), y = c(0.97, 0.71, 0.53, 0.3))
Finally we can take a look on trends with growth rate, by fitting a linear model for each enzyme and condition over growth rate, and plotting the R or R squared per condition. The R (correlation coefficient) should indicate the trend, i.e. increasing or decreasing with growth rate for a certain substrate.
plot_lm_ccm <- lapply(list_ccm_enzymes, function(lst) {
df_prot_comp %>%
filter(reaction_id %in% lst, !is.na(condition)) %>%
mutate(reaction_id = factor(reaction_id, lst)) %>%
group_by(reaction_id) %>% mutate(mass_g_gDCW = scales::rescale(mass_g_gDCW)) %>%
# fit linear model
group_by(reaction_id, substrate) %>%
summarize(
lin_reg_slope = summary(lm(mass_g_gDCW ~ growth_rate))$coef[2],
lin_reg_pvalue = summary(lm(mass_g_gDCW ~ growth_rate))$coef[8]
) %>%
mutate(lin_reg_slope = lin_reg_slope %>%
if_else(. > 2.5, 2.5, .) %>% if_else(. < -2.5, -2.5, .)) %>%
levelplot(lin_reg_slope ~ reaction_id * factor(substrate, unique(substrate)[c(3,2,1,4)]), .,
par.settings = custom.colorblind(), at = seq(-2.5, 2.5, by = 0.25),
col.regions = colorspace::diverge_hcl(20), ylab = "",
as.table = TRUE, xlab = "",
scales = list(x = list(cex = 0.65))
)
})
`summarise()` has grouped output by 'reaction_id'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'reaction_id'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'reaction_id'. You can override using the `.groups` argument.
`summarise()` has grouped output by 'reaction_id'. You can override using the `.groups` argument.
print(plot_lm_ccm[[1]], position = c(0.0,0.68,1,1), more = TRUE)
print(plot_lm_ccm[[2]], position = c(0,0.46,1,0.77), more = TRUE)
print(plot_lm_ccm[[3]], position = c(0,0.24,1,0.55), more = TRUE)
print(plot_lm_ccm[[4]], position = c(0,0,1,0.32))
grid::grid.text(c("CBB", "ED", "PYR", "TCA"), x = c(0.03, 0.03, 0.03, 0.03), y = c(0.97, 0.73, 0.5, 0.28))
Example of underutilization of enzymes from Cbb operon
Similar to machinery proteins we can also follow the simulation and actual protein abundance of all (detected) enzymes. We can see that some protein abundances nicely follow a growth-rate dependent manner, in line with predictions of higher flux. One such example are Calvin cycle enzymes for formatotrophic growth. However, for the same enzymes we see that no abundance is predicted under heterotrophic conditions because of missing flux through the pathway, but in fact the proteins are expressed in high abundance, often in a growth-rate dependent manner.
cbb_selected <- c("PGK", "GAPD", "FBA", "FBP", "TKT1", "PRUK", "RBPC")
# "TKT2", "TAh", "RPE", "RPI"
# summary table of selected Calvin cycle genes
df_calvin <- df_prot_comp %>%
# remove 1 non-quantified replicate
filter(!(substrate == "fructose" & growth_rate == 0.1 & replicate == "R2")) %>%
# select only subset of reactions
filter(reaction_id %in% cbb_selected) %>%
mutate(reaction_id = factor(reaction_id, cbb_selected)) %>%
rename(`mass [g/gDCW]` = mass_g_gDCW, `predicted mass [g/gDCW]` = predicted_mass_g_gDCW) %>%
mutate(utilization = `predicted mass [g/gDCW]`/`mass [g/gDCW]`)
# plot mass and utilization
plot_calvin <- #doubleYScale(use.style = FALSE, under = TRUE,
xyplot(`mass [g/gDCW]` + `predicted mass [g/gDCW]` ~ factor(growth_rate) |
reaction_id * substrate, df_calvin,
par.settings = custom.colorblind(), auto.key = list(columns = 2, cex = 0.7),
xlab = expression("µ [h"^-1*"]"), ylim = c(-0.003, 0.033), #c(-0.002, 0.032),
ylab = expression("m"[protein]*" [g gDCW"^-1*"]"),
scales = list(alternating = FALSE, x = list(at = c(2,4)),
y = list(at = c(0, 0.01, 0.02, 0.03))), as.table = TRUE,
between = list(x = 0.5, y = 0.5), lwd = 2,
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.errbars(x, y, ewidth = 0, ...)
panel.superpose(x, y, ...)
}, panel.groups = function(x, y, ...) {
panel.lmline(x, y, ...)
}
)
print(useOuterStrips(plot_calvin))
It is clear from the previous analysis that the cells maintain proteins even if they are not or only marginally utilized. The actually utilized proteome is minimal under strong substrate limitation. The next section will try to quantify the under-utilization of enzymes by comparing the minimal protein requirement (simulation) versus experimentally determined protein abundance. It is sufficient to determine the underutilized protein fraction (of all utilized proteins).
plot_calvin_util <- df_prot_comp %>%
# filter set of enzymes
filter(reaction_id %in% c("PGK", "GAPD", "FBA", "FBP", "TKT1", "PRUK", "RBPC")) %>%
# remove 1 non-quantified replicate
filter(!(substrate == "fructose" & growth_rate == 0.1 & replicate == "R2")) %>%
# calculate protein utilization
group_by(substrate, growth_rate, replicate) %>%
summarize(
mass = sum(mass_g_gDCW, na.rm = TRUE),
predicted_mass = sum(predicted_mass_g_gDCW, na.rm = TRUE),
percent_utilization = predicted_mass/(mass)
) %>%
# add pseudo enzyme group
mutate(enzyme = "CBB cycle") %>%
# optionally show some summary statistics
#summarize(across(matches("mass|util"), mean)) %>%
#mutate(unutil_mass = mass-predicted_mass) %>%
#summarize(max(unutil_mass)/0.68*100) #for percent: /0.68*100
# plot
xyplot(percent_utilization*100 ~ factor(growth_rate) | enzyme * substrate, .,
par.settings = custom.colorblind(), ylim = c(-10, 110),
scales = list(alternating = FALSE, x = list(at = c(2,4))),
xlab = expression("µ [h"^-1*"]"), ylab = "",
as.table = TRUE, between = list(x = 0.5, y = 0.5),
lwd = 2, key = simpleKey("% utilization", cex = 0.7),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
x_mean = unique(x); y_mean = tapply(y, x, mean)
panel.xyarea(c(0, x_mean, 6), c(0, y_mean, tail(y_mean, 1)),
lty = 0, col = grey(0.6, alpha = 0.5), ...)
panel.errbars(x, y, ewidth = 0, col = grey(0.5), ...)
}
) %>% useOuterStrips
`summarise()` has grouped output by 'substrate', 'growth_rate'. You can override using the `.groups` argument.
print(plot_calvin_util)
A final control is to check which copies of the same Cbb cycle enzymes are more abundant, or undergo stronger changes. For example, PRUK and Rubisco (RBPC) are only present on the two copies of the Calvin cycle operon, on pHG1 and Chromosome 2. But other glycolysis related enzymes have one canonical copy on chromosome 1, which seems to encode mostly “housekeeping” functions. We can look at protein abundance of the three single gene loci for each enzyme (chromosome 1, 2, and pHG1 megaplasmid). It’s important to keep in mind that most peptides for cbb genes can not be distinguished between chromosome 2 and pHG1, therefore the similar expression pattern.
plot_calvin_loci <- df_model_reactions %>% filter(reaction_id %in% c("PGK", "GAPD", "FBA", "FBP", "TKT1")) %>%
select(reaction_id, genes) %>% rename(locus_tag = genes) %>%
filter(locus_tag != "H16_B0278") %>%
inner_join(Ralstonia_eutropha) %>%
mutate(chromosome = case_when(
grepl("H16_A", locus_tag) ~ "chromosome 1",
grepl("H16_B", locus_tag) ~ "chromosome 2",
grepl("PHG", locus_tag) ~ "pHG1",
)) %>%
xyplot(mass_g_gDCW ~ factor(growth_rate) | reaction_id * chromosome, .,
groups = substrate, layout = c(5, 3),
par.settings = custom.colorblind(), lwd = 2,
scales = list(alternating = FALSE),
xlab = expression("µ [h"^-1*"]"),
ylab = expression("m"[protein]*" [g gDCW"^-1*"]"),
as.table = TRUE, between = list(x = 0.5, y = 0.5),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.errbars(x, y, ewidth = 0, type = c("p", "l"), ...)
panel.key(..., cex = 0.7)
}
)
Joining, by = "locus_tag"
print(plot_calvin_loci)
Expression and utilization of PHB synthesis enzymes
The pathway for PHB synthesis consists mainly of three enzymes, each one with multiple genes annotated. The first is Acetyl-CoA C-acetyltransferase (phaA), making acetoacetyl-CoA, the second one is Acetoacetyl-CoA reductase (phaB), making 3-hydroxy-butyryl-CoA, and the third is the PHB-synthase condensing 3HB monomers to PHB polymer (phaC). The mass for each enzyme is the sum of the individual protein masses allocated to this reaction.
plot_phb_mass <- df_prot_comp %>%
filter(reaction_id %in% c("PHAS", "ACACT1r", "AACOAR")) %>%
mutate(reaction_id = recode(reaction_id,
PHAS = "PHAS (phaC)", ACACT1r = "ACACT1r (phaA)", AACOAR = "AACOAR (phaB)")) %>%
xyplot(mass_g_gDCW ~ factor(growth_rate) |
factor(reaction_id, unique(reaction_id)[c(2,1,3)]), .,
par.settings = custom.colorblind(),
groups = substrate, xlab = "",
ylab = expression("m"[protein]*" [g gDCW"^-1*"]"),
layout = c(3, 1), ewidth = 0, lwd = 2, as.table = TRUE,
between = list(x = 0.5, y = 0.5), auto.key = list(columns = 2),
scales = list(alternating = FALSE, x = list(at = c(2, 4))),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.errbars(x, y, ...)
}
)
plot_phb_util <- df_prot_comp %>%
filter(reaction_id %in% c("PHAS", "ACACT1r", "AACOAR")) %>%
mutate(percent_utilization = predicted_mass_g_gDCW/mass_g_gDCW*100) %>%
mutate(reaction_id = recode(reaction_id,
PHAS = "PHAS (phaC)", ACACT1r = "ACACT1r (phaA)", AACOAR = "AACOAR (phaB)")) %>%
xyplot(percent_utilization ~ factor(growth_rate) |
factor(reaction_id, unique(reaction_id)[c(2,1,3)]), .,
par.settings = custom.colorblind(),
groups = substrate,
xlab = expression("µ [h"^-1*"]"),
ylab = "% utilization", ylim = c(-20, 320),
layout = c(3, 1),
ewidth = 0, lwd = 2, as.table = TRUE,
between = list(x = 0.5, y = 0.5),
scales = list(alternating = FALSE, x = list(at = c(2, 4))),
panel = function(x, y, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.errbars(x, y, ...)
}
)
print(plot_phb_mass, position = c(0,0.42,1,1), more = TRUE)
print(plot_phb_util, position = c(0.04,0,1,0.55))
Yield-growth rate tradeoff when Cupriavidus re-assimilates CO2?
It was experimentally observed before that R. eutropha expresses Rubisco and other cbb-operon located genes even on growth on fructose or other heterotrophic substrates. Bowien et al., 1990, show that Rubisco activity can be found on growth on fructose, but not on pyruvate. Dangel & Tabita, 2015, review the regulation by CbbR type regulators among others also in R. eutropha and mention that citrate also leads to activation of cbb expression. They hypothesize that ribulose bisphosphate (RuBP) is an activating effector while other central metabolism intermediates such as phosphoenolpyruvate (PEP) are repressing effector molecules. It was speculated in Bowien et al. that regulation by cbbR might actually be a repression-derepression rather than activation, which means that the default state would be a bound cbbR repressing the cbb operon. However it became clear that this is not the case. Shimizu et al., 2015, knocked the cbbR gene out and the result was reduced expression of cbb genes by 100 fold. This proves that cbbR is a required activator and not a repressor of cbb, otherwise cbbR deletion would lead to constitutive activation of cbb expression.
The same group speculated in their study that the additional Rubisco expression could have a benefit for carbon yield, specifically for product yield of PHB. They show that PHB from the WT contains slightly more 13C labeled mass (and total mass) than the cbbR and cbbS/L knockouts. This means that the cell would have a (product or biomass) yield advantage by Rubisco expression on fructose.
The following section tests the hypothesis of a yield-growth rate tradeoff with the resource allocation model. First simulation data from the RBA model is imported
#adjust path
mixotroph_dir <- gsub("substrate_limitation", "mixotrophy", simulation_dir)
# read simulation results
df_mixflux <- read_rba_result(list.files(mixotroph_dir, pattern = "fluxes_.*.tsv$", full.names = TRUE))
df_mixmacr <- read_rba_result(list.files(mixotroph_dir, pattern = "macroprocesses_.*.tsv", full.names = TRUE))
# rename column
df_mixflux <- df_mixflux %>% rename(CO2_refixation = sim_run) %>% filter(CO2_refixation <= 5)
df_mixmacr <- df_mixmacr %>% rename(CO2_refixation = sim_run) %>% filter(CO2_refixation <= 5)
The next task is to compare yield, growth rate, CO2 emission and other fluxes for a range of simulations where Rubisco was forced to re-fix emitted CO2 from growth on fructose. Simulations were performed for increasing flux through Rubisco from 0 to 5 mmol gDCW-1 h-1.
plot_mixo_mu <- lapply(c("mu", "yield"), function(keys) {
if (keys == "mu") {
ylabel <- expression("µ [h"^-1*"]")
ylim <- c(0.001, 0.3)
} else {
ylabel <- expression("Y [gDCW gS"^-1*"]")
ylim <- c(0.001, 0.399)
}
xyplot(value ~ factor(CO2_refixation),
filter(df_mixmacr, key == keys, carbon_conc == 1.25),
type = "b", lwd = 2, par.settings = custom.colorblind(),
xlab = expression("CO"[2]*" uptake [mmol h"^-1*" gDCW"^-1*"]"),
ylab = ylabel, ylim = ylim,
panel = function(x, y, z, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.xyplot(x, y, ...)
}
)
})
print(plot_mixo_mu[[1]], position = c(0,0,0.53,1), more = TRUE)
print(plot_mixo_mu[[2]], position = c(0.47,0,1,1))
There is no increase in growth rate or yield with additional CO2 fixation according to the RBA model. The yield is in fact calculated based on the growth rate µ and the substrate uptake rate qS. Yield and growth rate depend on each other in the relation Y [gDCW/gS] = µ [h-1] / qS [gS gDCW-1 h-1].
Since only the initial substrate concentration is constrained, the model could predict a lower substrate uptake rate or lower CO2 emission per biomass in order to reach a yield increase. However this was not the case under any simulation, see below for details of specific metabolites.
The model seems to predict a high-yield phenotype already. If CO2 re-fixation would have had an advantage for growth, it might have been detected earlier depending on the protein costs for the respective pathway. Before a final verdict, we can follow the fate of the fixed CO2 through the metabolism.
plot_mixo_enz <- xyplot(abs(value) ~ as.factor(CO2_refixation) |
factor(key, unique(key)), layout = c(2, 2),
filter(df_mixflux, key %in% c("CO2t", "EDD", "CS", "O2t"), carbon_conc == 1.25),
par.settings = custom.colorblind(),
type = "b", lwd = 2, between = list(x = 0.5, y = 0.5), as.table = TRUE,
xlab = expression("CO"[2]*" uptake [mmol h"^-1*" gDCW"^-1*"]"),
ylab = expression("flux [mmol h"^-1*"gDCW"^-1*"]"),
scales = list(alternating = FALSE),
panel = function(x, y, z, ...) {
panel.grid(h = -1, v = -1, col = grey(0.9))
panel.xyplot(x, y, ...)
}
)
print(plot_mixo_enz)
Conclusion
It becomes clear that yield and growth rate decrease, and not increase, with additional CO2 fixation, because:
- energy requirement for CO2 fixation leads to lower flux through ED pathway, but higher flux through TCA
- this is in order to generate the required NADH/ATP for CO2 fixation
- respiration and O2 consumption also increases with forced mixotrophic growth
- there is no net reduction of CO2 emission. In fact cells emit more CO2 even when they fix some of it due to increased energy requirement
- storing some of the fixed CO2 as PHB would not increase biomass yield as additional energy requirement still persists
- cells should not be able to make more PHB using this strategy, regardless of biomass yield. If acetyl-CoA is drained for PHB, even less energy is made available through TCA and OXPHOS.
Miniature RBA flux maps
Install my small R package fluctuator to overlay fluxes on SVG metabolic maps. The template map is a simplified metabolic map of C. necator’s central metabolism.
First we plot a map of the mixotrophic simulation. Then we use the same work-flow and template to plot metabolic flux maps for growth on the three main substrates, fructose, succinate and formate (maximum tested growth rate, 0.25 h^-1).
library(fluctuator)
# import map
SVG_map <- read_svg("../data/simulation/central_metabolism.svg")
#get_attributes(SVG_map, node = "key_01") %>% pull(style)
# prepare data: mixotrophic growth
df_mixflux <- df_mixflux %>% filter(CO2_refixation == 3) %>%
# add fluxes for legend entries
add_row(key = c("key_01", "key_12", "key_24", "key_48"), value = c(1,2,4,8)) %>%
# add stroke width to simulation
mutate(stroke_width = 0.2 + 0.2*sqrt(abs(value))) %>%
inner_join(select(SVG_map@summary, label), by = c("key" = "label")) %>%
distinct
# prepare data: carbon-limited growth
df_carbflux <- df_flux %>%
filter(simulation %in% c(
"for_0.487_nh4_18.7_10", "fru_1.223_nh4_18.7_0", "succ_0.662_nh4_18.7_5")) %>%
# add fluxes for legend entries
add_row(key = rep(c("key_01", "key_12", "key_24", "key_48"), each = 3),
value = rep(c(1,2,4,8), each = 3), carbon_source = rep(c("for", "fru", "succ"), 4)) %>%
# add stroke width to simulation
mutate(stroke_width = 0.2 + 0.2*sqrt(abs(value))) %>%
inner_join(select(SVG_map@summary, label), by = c("key" = "label")) %>%
distinct
Plot all four conditions in a loop.
lapply(
list(
mutate(df_mixflux, carbon_source = "mix"),
filter(df_carbflux, carbon_source == "fru"),
filter(df_carbflux, carbon_source == "for"),
filter(df_carbflux, carbon_source == "succ")
), FUN = function(df) {
# reload base map for every instance, as the SVG is altered
# even without assignment (python style, untypical in R)
SVG_map <- read_svg("../data/simulation/central_metabolism.svg")
# modify map
SVG2 <- set_attributes(SVG_map,
node = df$key, attr = "style",
pattern = "stroke-width:[0-9]+\\.[0-9]+",
replacement = paste0("stroke-width:", df$stroke_width))
# turn non-zero fluxes into darker color
SVG2 <- set_attributes(SVG2,
node = filter(df, value != 0) %>% pull(key), attr = "style",
pattern = "stroke:#b3b3b3",
replacement = "stroke:#808080")
# rescale arrow heads
SVG2 <- set_attributes(SVG2,
node = grep("marker", SVG2@summary$id, value = TRUE),
node_attr = "id",
attr = "transform",
pattern = "scale\\(0.2\\)",
replacement = "scale(0.15)")
SVG2 <- set_attributes(SVG2,
node = grep("marker", SVG2@summary$id, value = TRUE),
node_attr = "id",
attr = "transform",
pattern = "scale\\(-0.2\\)",
replacement = "scale(-0.15)")
# export map as SVG again
#write_svg(SVG2, file = paste0("../data/simulation/central_metabolism_", df$carbon_source[1], ".svg"))
}
) %>% invisible()
|
|
|
|
LS0tCnRpdGxlOiAiRW56eW1lIHV0aWxpemF0aW9uIGFuZCBzYXR1cmF0aW9uIGZvciAqUi4gZXV0cm9waGEqIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOiAKICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KCgojIERlc2NyaXB0aW9uCgpUaGlzIFIgbm90ZWJvb2sgaXMgYSBiaW9pbmZvcm1hdGljcyBwaXBlbGluZSB0byAqKmFuYWx5emUgcHJvdGVpbiBzYXR1cmF0aW9uL3VuZGVyLXV0aWxpemF0aW9uIHdpdGggYSByZXNvdXJjZSBhbGxvY2F0aW9uIG1vZGVsKiogZm9yIHRoZSBjaGVtb2xpdGhvYXV0b3Ryb3BoICpSYWxzdG9uaWEgZXV0cm9waGEqIChhLmsuYS4gKkN1cHJpYXZpZHVzIG5lY2F0b3IqKS4KCgojIExpYnJhcmllcwoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KGxhdHRpY2UpCiAgbGlicmFyeShsYXR0aWNlRXh0cmEpCiAgbGlicmFyeShsYXR0aWNldG9vbHMpCiAgbGlicmFyeSh0aWR5dmVyc2UpCiAgbGlicmFyeShzdHJpbmdpKQp9KQpgYGAKCiMgRGF0YSBpbXBvcnQKCkRlZmluZSB0aGUgZGF0YSBzb3VyY2UgZGlyZWN0b3JpZXMuIFNvbWUgb2YgdGhlbSBhcmUgZXh0ZXJuYWwgaW4gdGhlIHNlbnNlIG9mIG5vdCBpbmNsdWRlZCBpbiB0aGUgYWNjb21wYW55aW5nIGRhdGEgZm9sZGVyIG9mIHRoaXMgUiBub3RlYm9vay4gVGhlc2UgYXJlIGxvY2F0ZWQgaW4gdGhlIGFjY29tcGFueWluZyBnaXRodWIgcmVwb3NpdG9yeSBmb3IgdGhlIHJlc291cmNlIGFsbG9jYXRpb24gbW9kZWwgdGhhdCB3YXMgdXNlZCBoZXJlLiBUaGUgcmVzb3VyY2UgYWxsb2NhdGlvbiBtb2RlbCBjYW4gYmUgZm91bmQgYXQgbXkgZm9yayBvZiBbQmFjdGVyaWFsLVJCQS1tb2RlbHNdKGh0dHBzOi8vZ2l0aHViLmNvbS9tLWphaG4vQmFjdGVyaWFsLVJCQS1tb2RlbHMpLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KUmV1dHJvcGhhX3Byb3Rlb21pY3MgPC0gIi4uL2RhdGEvaW5wdXQvUmFsc3RvbmlhX2V1dHJvcGhhLlJkYXRhIgptb2RlbF9yZWFjdGlvbnMgPC0gIi4uL2RhdGEvaW5wdXQvbW9kZWxfcmVhY3Rpb25zLmNzdiIKc2ltdWxhdGlvbl9kaXIgPC0gIi4uL2RhdGEvc2ltdWxhdGlvbi9zdWJzdHJhdGVfbGltaXRhdGlvbi8iCnNvdXJjZSgicmVhZF9yYmFfcmVzdWx0LlIiKQpgYGAKCgpSZWFkIHNpbXVsYXRpb24gZGF0YS4KCmBgYHtyfQojIHJlYWQgc2ltdWxhdGlvbiByZXN1bHRzCmRmX2ZsdXggPC0gcmVhZF9yYmFfcmVzdWx0KGxpc3QuZmlsZXMoc2ltdWxhdGlvbl9kaXIsIHBhdHRlcm4gPSAiZmx1eGVzXy4qLnRzdiQiLCBmdWxsLm5hbWVzID0gVFJVRSkpCmRmX3Byb3QgPC0gcmVhZF9yYmFfcmVzdWx0KGxpc3QuZmlsZXMoc2ltdWxhdGlvbl9kaXIsIHBhdHRlcm4gPSAicHJvdGVpbnNfLioudHN2IiwgZnVsbC5uYW1lcyA9IFRSVUUpKQpkZl9tYWNyIDwtIHJlYWRfcmJhX3Jlc3VsdChsaXN0LmZpbGVzKHNpbXVsYXRpb25fZGlyLCBwYXR0ZXJuID0gIm1hY3JvcHJvY2Vzc2VzXy4qLnRzdiIsIGZ1bGwubmFtZXMgPSBUUlVFKSkKYGBgCgoKIyBPdmVydmlldyBvbiBzdWJzdHJhdGUgdXB0YWtlLCBncm93dGgsIGFuZCB5aWVsZAoKQWZ0ZXIgcnVubmluZyBhIHNldCBvZiBzaW11bGF0aW9ucyBpbiBSQkFweSB0aGF0IHNpbXVsYXRlIGluY3JlYXNpbmcgc3Vic3RyYXRlIGxpbWl0YXRpb24sIHdlIGNhbiBwbG90IHRoZSBzdWJzdHJhdGUgdXB0YWtlIHJhdGUgYHFgLCB5aWVsZCBgWWAgaW4gZ3JhbSBiaW9tYXNzIHBlciBncmFtIHN1YnN0cmF0ZSwgYW5kIGdyb3d0aCByYXRlIGDCtWAuIFVubGlrZSBnZW5vbWUgc2NhbGUgbW9kZWxzLCBncm93dGggYmVjb21lcyBsaW1pdGVkIGJ5IHRoZSBtYXhpbXVtIGFtb3VudCBvZiBwcm90ZWlucyB0aGF0IGEgY2VsbCBjYW4gc3ludGhlc2l6ZS4gSWYgY2VsbHMgd291bGQgbm90IGJlIHByb3RlaW4tbGltaXRlZCwgKm9yKiBwcm90ZWlucyB3b3VsZCBjYXRhbHl6ZSByZWFjdGlvbnMgaW5maW5pdGVseSBmYXN0LCBubyBzdWNoIGxpbWl0YXRpb24gd291bGQgdGFrZSBwbGFjZSBhbmQgZ3Jvd3RoIHJhdGUgd291bGQgc2NhbGUgbGluZWFybHkgd2l0aCBzdWJzdHJhdGUgY29uY2VudHJhdGlvbi4gVGhpcyBpcyB0aGUgc2l0dWF0aW9uIGluIEZCQSBzaW11bGF0aW9uLgoKYGBge3IsIGZpZy53aWR0aCA9IDYuNywgZmlnLmhlaWdodCA9IDYuNywgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgcmVhcnJhbmdlIHNvbWUgcm93cyAobXUsIHFTKSB0byBjb2x1bW5zCmRmX21hY3IgPC0gZGZfbWFjciAlPiUgZmlsdGVyKCFncmVwbCgidGVzdF9wcm9jZXNzIiwga2V5KSkgJT4lIAogIHNwcmVhZChrZXksIHZhbHVlKSAlPiUKICAKICAjIGFkZCB0eXBlIG9mIHNpbXVsYXRpb24KICBtdXRhdGUoc3Vic3RyYXRlID0gY2FzZV93aGVuKAogICAgY2FyYm9uX3NvdXJjZSA9PSAiZm9yIiB+ICJmb3JtYXRlIiwKICAgIGNhcmJvbl9zb3VyY2UgPT0gInN1Y2MiIH4gInN1Y2NpbmF0ZSIsCiAgICBjYXJib25fc291cmNlID09ICJmcnUiICYgbml0cm9nZW5fY29uYyA9PSAxOC43IH4gImZydWN0b3NlIiwKICAgIGNhcmJvbl9zb3VyY2UgPT0gImZydSIgJiBuaXRyb2dlbl9jb25jICE9IDE4LjcgfiAiYW1tb25pdW0iCiAgKSkgJT4lCiAgCiAgIyBhZGQgdXB0YWtlIHJhdGUgaW4gZy9nRENXKmggaW5zdGVhZCBvZiBtbW9sCiAgbXV0YXRlKHFTX2dfZ0RDV19oID0gY2FzZV93aGVuKAogICAgc3Vic3RyYXRlID09ICJmb3JtYXRlIiB+IHFTKjAuMDQ2MDMsCiAgICBzdWJzdHJhdGUgPT0gInN1Y2NpbmF0ZSIgfiBxUyowLjExODA5LAogICAgc3Vic3RyYXRlID09ICJmcnVjdG9zZSIgfiBxUyowLjE4MDE2LAogICAgc3Vic3RyYXRlID09ICJhbW1vbml1bSIgfiBxUyowLjA1MzQ5CiAgKSkKYGBgCgpGaXJzdCB3ZSBjYW4gaGF2ZSBhIGxvb2sgYXQgaG93IGdyb3d0aCByYXRlIGxldmVscyBvZmYgd2l0aCBpbmNyZWFzaW5nIHN1YnN0cmF0ZSAqY29uY2VudHJhdGlvbiogaW4gbW1vbC9MLiBOb3RlOiB0aGlzIGlzIG5vdCBlcXVhbCB0byAqc3Vic3RyYXRlIHVwdGFrZSByYXRlKi4gU3Vic3RyYXRlIHVwdGFrZSByYXRlIGFuZCBncm93dGggcmF0ZSBzaG91bGQgaGF2ZSBhbiBhbG1vc3QgbGluZWFyIHJlbGF0aW9uc2hpcC4gV2UgY2FuIGxvZy10cmFuc2Zvcm0gdGhlIGNhcmJvbiBjb25jZW50cmF0aW9uIGFuZCBmaXQgYSBsaW5lYXIgbW9kZWwgdG8gcHJlZGljdCB0aGUgc3Vic3RyYXRlIGNvbmNlbnRyYXRpb24gcmVxdWlyZWQgdG8gb2J0YWluIGEgY2VydGFpbiBzdWJzdHJhdGUgdXB0YWtlIHJhdGUgYW5kIGdyb3d0aCByYXRlLgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2fQojIGNvcHkgbml0cm9nZW4gdG8gY2FyYm9uIGNvbmNlbnRyYXRpb24gZm9yIGFtbW9uaXVtIGxpbWl0YXRpb24sCiMganVzdCBmb3IgcGxvdHRpbmcgcHVycG9zZXMKZGZfbWFjcl92aXogPC0gZGZfbWFjciAlPiUKICBtdXRhdGUoY2FyYm9uX2NvbmMgPSBjYXNlX3doZW4oCiAgICBzdWJzdHJhdGUgPT0gImFtbW9uaXVtIiB+IG5pdHJvZ2VuX2NvbmMsCiAgICBUUlVFIH4gY2FyYm9uX2NvbmMKICApKQoKcGxvdF9tdV9xc19saW4gPC0geHlwbG90KG11IH4gY2FyYm9uX2NvbmMgfCBzdWJzdHJhdGUsIGRmX21hY3Jfdml6LAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgbGF5b3V0ID0gYyg0LDEpLCBsd2QgPSAxLjUsIHBjaCA9IDE5LAogICAgeGxhYiA9IGV4cHJlc3Npb24oIlMgW21NXSIpLAogICAgeWxhYiA9IGV4cHJlc3Npb24oJ8K1IFtoJ14nLTEnKiddJyksCiAgICBzY2FsZXMgPSBsaXN0KGFsdGVybmF0aW5nID0gRkFMU0UpLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC54eXBsb3QoeCwgeSwgY2V4ID0gMC45LCAuLi4pCiAgICAgICNwYW5lbC5sbWxpbmVxKHhbMTo0XSwgeVsxOjRdLCBmb250ZmFtaWx5ID0gIkZyZWVTYW5zIiwgLi4uKQogICAgfQogICkKCnBsb3RfbXVfcXNfbG9nIDwtIHh5cGxvdChsb2cxMChjYXJib25fY29uYykgfiBxUyB8IHN1YnN0cmF0ZSwKICAgIGRmX21hY3Jfdml6LAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLCAjeGxpbSA9IGMoMCwgNSksCiAgICBsYXlvdXQgPSBjKDQsMSksIGx3ZCA9IDEuNSwgcGNoID0gMTksCiAgICB4bGFiID0gZXhwcmVzc2lvbigncSdbU10qJyBtbW9sIGcgRENXJ14tMSonaCdeLTEpLAogICAgeWxhYiA9ICBleHByZXNzaW9uKCdsb2cnWzEwXSonIFMgW21NXScpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIGNleCA9IDAuOSwgLi4uKQogICAgICBwYW5lbC5sbWxpbmVxKHhbMTo2XSwgeVsxOjZdLCBmb250ZmFtaWx5ID0gIkZyZWVTYW5zIiwgLi4uKQogICAgfQogICkKCnByaW50KHBsb3RfbXVfcXNfbGluLCBzcGxpdCA9IGMoMSwxLDEsMiksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X211X3FzX2xvZywgc3BsaXQgPSBjKDEsMiwxLDIpKQoKZGZfbWFjcl92aXogJT4lCiAgZ3JvdXBfYnkoc3Vic3RyYXRlKSAlPiUKICAjIGZpdCBsaW5lYXIgbW9kZWwgdG8gc3Vic3RyYXRlIHVwdGFrZSByYXRlIHZzIGNvbmNlbnRyYXRpb24KICBzdW1tYXJpemUoCiAgICBzbG9wZSA9IGxtKHggfiB5LCBkYXRhID0gbGlzdCh4ID0gbG9nMTAoY2FyYm9uX2NvbmMpLCB5ID0gcVMpKSRjb2VmZlsyXSwKICAgIG9mZnNldCA9IGxtKHggfiB5LCBkYXRhID0gbGlzdCh4ID0gbG9nMTAoY2FyYm9uX2NvbmMpLCB5ID0gcVMpKSRjb2VmZlsxXQogICkgJT4lIG11dGF0ZShtb2RlbCA9IHBhc3RlMCgiYyA9IDEwXigiLCByb3VuZChvZmZzZXQsIDMpLCAiICsgIiwgcm91bmQoc2xvcGUsIDMpLCAiKnFTKSIpKQpgYGAKCkNyZWF0ZSBhIEhlcmJlcnQtUGlydCBwbG90IGZvciBlYWNoIGNvbmRpdGlvbiAoZ3Jvd3RoIHJhdGUgdmVyc3VzIHN1YnN0cmF0ZSB1cHRha2UgcmF0ZSkuIFRoaXMgcGxvdCB3b3VsZCBzaG93IGEgY2hhbmdlIGluIHlpZWxkIGJ5IGEgJ2tpbmsnIG9mIHRoZSBkYXRhIHBvaW50cy4KCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gM30KeHlwbG90KHFTX2dfZ0RDV19oIH4gbXUgfCBzdWJzdHJhdGUsIGRmX21hY3IsCiAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICBiZXR3ZWVuID0gbGlzdCh4ID0gMC41LCB5ID0gMC41KSwKICBsYXlvdXQgPSBjKDQsMSksIGx3ZCA9IDEuNSwgcGNoID0gMTksCiAgeWxhYiA9IGV4cHJlc3Npb24oInEiW1NdKiIgW2cgaCJeLTEqIiBnRENXIl4tMSoiXSIpLAogIHhsYWIgPSBleHByZXNzaW9uKCfCtSBbaCdeJy0xJyonXScpLAogIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSksCiAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgIHBhbmVsLnh5cGxvdCh4LCB5LCBjZXggPSAwLjksIC4uLikKICAgICMgZGlzcGxheWluZyBtYWludGVuYW5jZSBhbmQgeWllbGQgY29lZmZpY2llbnRzCiAgICBjb2VmIDwtIGxtKHkgfiB4LCBkYXRhLmZyYW1lKHgsIHkpKSRjb2VmZgogICAgcGFuZWwudGV4dChtZWRpYW4oeCksIDIuNywgCiAgICAgIHBhc3RlKCJtcyA9Iiwgcm91bmQoY29lZltbMV1dLCAzKSwgImcgaC0xIGdfRENXLTEiKSwgCiAgICAgIGNvbCA9IGdyZXkoMC4zKSwgY2V4ID0gMC43KQogICAgcGFuZWwudGV4dChtZWRpYW4oeCksIDIuNCwgcGFzdGUoZXhwcmVzc2lvbigiWXgvUyA9IiksIAogICAgICAgIHJvdW5kKDEvY29lZltbMl1dLCAzKSwgImdfRENXIGdfUy0xIiksIAogICAgICBjb2wgPSBncmV5KDAuMyksIGNleCA9IDAuNykKICB9CikKYGBgCgoKIyBSZXNvdXJjZSBhbGxvY2F0aW9uIGluIHRlcm1zIG9mIHByb3RlaW4gbWFzcwoKVG8gZGV0ZXJtaW5lIHRoZSB0cnVlIGFsbG9jYXRpb24gb2YgcHJvdGVpbiByZXNvdXJjZXMgcGVyIGNvbXBhcnRtZW50LCBidXQgYWxzbyB0aGUgdHJ1ZSBjb3N0IG9mIHByb3RlaW4gcGVyIHByb2Nlc3MsIHdlIG5lZWQgdG8gKipjb252ZXJ0IHRoZSBwcmVkaWN0ZWQgY29uY2VudHJhdGlvbiBvZiBwcm90ZWlucyBpbiBtbW9sIHBlciBnRENXIHRvIGcgcGVyIGdEQ1cqKiwgc2ltcGx5IGJ5IG11bHRpcGx5aW5nIHByb3RlaW4gY29uY2VudHJhdGlvbiB3aXRoIHRoZSBtb2xlY3VsYXIgd2VpZ2h0IG9mIGEgcHJvdGVpbiAoZy9tb2wsIGNvbnZlcnRlZCB0byBnL21tb2wpLiBXZSBjYW4gdGhlbiBhbHNvIGVhc2lseSB0cmFuc2Zvcm0gZy9nRENXIHRvIG1hc3MgZnJhY3Rpb24gYnkgZGl2aWRpbmcgaW5kaXZpZHVhbCBwcm90ZWluIGNvbmNlbnRyYXRpb25zIGJ5IHRoZSBzdW0gb2YgYWxsIHByb3RlaW4gY29uY2VudHJhdGlvbnMuIFRoZSBwcm90ZWluIG1hc3MgZnJhY3Rpb24gaXMgZGltZW5zaW9ubGVzcy4gVGhlIG9ubHkgcGFyYW1ldGVyIHJlcXVpcmVkIGZvciB0aGlzIHRyYW5zZm9ybWF0aW9uIGlzIHRoZSBtb2xlY3VsYXIgd2VpZ2h0IHBlciBwcm90ZWluIHdoaWNoIGlzIGF2YWlsYWJsZSBmcm9tIHVuaXByb3QuIFdlIGNhbiBmb3IgZXhhbXBsZSB0YWtlIHRoZSBwcm90ZWluIGFubm90YXRpb24gdGFibGUgdGhhdCBpcyBhdXRvbWF0aWNhbGx5IGRvd25sb2FkZWQgZHVyaW5nIGBSQkFweWAgbW9kZWwgZ2VuZXJhdGlvbi4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CiMgaW1wb3J0IGRvd25sb2FkZWQgUmFsc3RvbmlhIHByb3RlaW4gYW5ub3RhdGlvbiBmcm9tIHVuaXByb3QKZGZfdW5pcHJvdCA8LSByZWFkX3RzdigiLi4vZGF0YS9pbnB1dC91bmlwcm90LmNzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lCiAgbXV0YXRlKGxvY3VzX3RhZyA9IHN0cmlfZXh0cmFjdF9maXJzdChgR2VuZSBuYW1lc2AsIHJlZ2V4ID0gIkgxNl9bQUJdWzAtOV17NH18UEhHWzAtOV17M30iKSkKCiMgbWVyZ2UgcHJlZGljdGVkIHByb3RlaW4gYWxsb2NhdGlvbiB3aXRoIG1vbGVjdWxhciB3ZWlnaHQgaW5mbyBmcm9tIHVuaXByb3QKZGZfcHJvdCA8LSBsZWZ0X2pvaW4oZGZfcHJvdCwgc2VsZWN0KGRmX3VuaXByb3QsIGxvY3VzX3RhZywgTGVuZ3RoLCBNYXNzKSwKICBieSA9IGMoImtleSIgPSAibG9jdXNfdGFnIikpICU+JQogIAogICMgY2FsY3VsYXRlIHByZWRpY3RlZCBwcm90ZWluIG1hc3MgaW4gZy9nRENXIHVzaW5nIE1XIGluIGcvbW1vbCwgYW5kIG1hc3MgZnJhY3Rpb24KICBncm91cF9ieShzaW11bGF0aW9uKSAlPiUgbXV0YXRlKAogICAgcHJlZGljdGVkX21hc3NfZ19nRENXID0gdmFsdWUgKiBNYXNzIC8gMTAwMCkKCiMgdGVzdCBpZiBtYXNzIGZyYWN0aW9ucyBzdW0gdG8gcmVhc29uYWJsZSB2YWx1ZQpkZl9wcm90ICU+JSBzdW1tYXJpemUoCiAgcHJlZGljdGVkX21hc3NfZ19nRENXID0gc3VtKHByZWRpY3RlZF9tYXNzX2dfZ0RDVywgbmEucm0gPSBUUlVFKSkKYGBgCgotLS0tLS0tLS0tCgpUaGUgc2ltdWxhdGVkIHByb3RlaW4gbWFzcyBwZXIgZ0RDVyBjaGFuZ2VzIHdpdGggZ3Jvd3RoIHJhdGUsIGFuZCBpdCBpcyBjb25zaWRlcmFibHkgbG93ZXIgdGhlbiB0aGUgZXN0aW1hdGVkIH4wLjY1LTAuNjggZyB0b3RhbCBwcm90ZWluL2dEQ1cgdGhhdCB3YXMgcHJldmlvdXNseSBlc3RpbWF0ZWQgZm9yIGJhY3RlcmlhIChzZWUgZS5nLiBQYXJrIGV0IGFsLiwgYmlvbWFzcyBjb21wb3NpdGlvbiBmb3IgKlJhbHN0b25pYSBldXRyb3BoYSogbW9kZWwsIG9yIFRvdWxvdXBha2lzIGV0IGFsLiwgYmlvbWFzcyBjb21wb3NpdGlvbiBmb3IgY3lhbm9iYWN0ZXJpYSkuIE9uZSByZWFzb24gaXMgdGhhdCBhYm92ZSBudW1iZXJzIG9ubHkgaW5jbHVkZSBlbnp5bWF0aWMgcHJvdGVpbnMgKHRoZSBvbmVzIHJlcHJlc2VudGVkIGJ5IHRoZSBHU00pLiBGb3IgbWFjaGluZXJ5IHN1Y2ggYXMgcmlib3NvbWFsIHByb3RlaW5zLCBhIHNlcGFyYXRlIGNhbGN1bGF0aW9uIG5lZWRzIHRvIGJlIHBlcmZvcm1lZC4gVGhlIG1vZGVsIHJldHVybnMgZXN0aW1hdGVkIGNvbmNlbnRyYXRpb24gb2YgbWFjaGluZXJpZXMgZm9yIHJlcGxpY2F0aW9uLCB0cmFuc2NyaXB0aW9uLCB0cmFuc2xhdGlvbiwgYW5kIHByb3RlaW4gZm9sZGluZy4gVGhlIGFzc29jaWF0ZWQgcHJvdGVpbnMgY2FuIGJlIGltcG9ydGVkIGZyb20gdGhlIG1vZGVsIGZvbGRlciBhbmQgdGhlIHRvdGFsIG1hc3MgZXN0aW1hdGVkIHVzaW5nIG1vbGVjdWxhciB3ZWlnaHQgYW5kIHN1YnVuaXQgc3RvaWNoaW9tZXRyeSBhcyBkb25lIGJlZm9yZSBmb3IgZW56eW1hdGljIHByb3RlaW5zLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KbWFjaGluZXJ5X25hbWVzIDwtIGMoInJlcGxpY2F0aW9uIiwgInRyYW5zY3JpcHRpb24iLCAicmlib3NvbWUiLCAiY2hhcGVyb25lcyIpCm1hY2hpbmVyeV90YWJsZXMgPC0gcGFzdGUwKCIuLi9kYXRhL3NpbXVsYXRpb24vbWFjcm9fbWFjaGluZXMvIiwgbWFjaGluZXJ5X25hbWVzLCAiLnRzdiIpCgpkZl9tYWNyIDwtIGRmX21hY3IgJT4lIAogIAogICMgcmVtb3ZlIHVudXNlZCBjb2x1bW5zIGFuZCByZW5hbWUgaW1wb3J0YW50IG9uZXMKICBzZWxlY3QoLVBfRU5aLCAtUF9STkFERUcpICU+JQogIHJlbmFtZShyZXBsaWNhdGlvbiA9IFBfUkVQLCB0cmFuc2NyaXB0aW9uID0gUF9UU0MsIAogICAgcmlib3NvbWUgPSBQX1RBLCBjaGFwZXJvbmVzID0gUF9DSFAsIHN1YnN0cmF0ZV91cHRha2VfcmF0ZSA9IHFTLAogICAgcHJlZGljdGVkX2dyb3d0aF9yYXRlID0gbXUKICApICU+JQogIAogICMgZ2F0aGVyIGluZGl2aWR1YWwgbWFjaGluZXJpZXMgaW4gb25lIGNvbHVtbgogIGdhdGhlcihrZXkgPSBtYWNoaW5lLCB2YWx1ZSA9IHByZWRpY3RlZF9tYXNzX21tb2xfZ0RDVywgCiAgICBjaGFwZXJvbmVzOnRyYW5zY3JpcHRpb24pICU+JQogIAogICMgcm91bmQgdXB0YWtlIHJhdGVzIGZvciBtZXJnaW5nCiAgbXV0YXRlKHN1YnN0cmF0ZV91cHRha2VfcmF0ZSA9IHJvdW5kKHN1YnN0cmF0ZV91cHRha2VfcmF0ZSwgMykpCiAgCgpkZl9tYWNoaW5lcnkgPC0gbGFwcGx5KG1hY2hpbmVyeV90YWJsZXMsIHJlYWRfdHN2LCBjb2xfdHlwZSA9IGNvbHMoKSkgJT4lIAogIGJpbmRfcm93cyguaWQgPSAibWFjaGluZSIpICU+JQogIG11dGF0ZShtYWNoaW5lID0gcmVjb2RlKG1hY2hpbmUsICEhIXNldE5hbWVzKG1hY2hpbmVyeV9uYW1lcywgMTo0KSkpICU+JQogIHNlbGVjdCgtYEVudHJ5IG5hbWVgLCAtU2VxdWVuY2UsIC1Db2ZhY3RvciwgLWBFQyBudW1iZXJgLCAtYE9yZ2FuaXNtIElEYCwgYE9yZ2FuaXNtYCwKICAgIC1gQ2F0YWx5dGljIGFjdGl2aXR5YCwgLVN0YXR1cykgJT4lCiAgCiAgIyBqb2luIHdpdGggcHJlZGljdGlvbiBvZiBtb2xlY3VsYXIgbWFjaGluZSBjb25jZW50cmF0aW9uIChtbW9sL2dEQ1cpCiAgbGVmdF9qb2luKGRmX21hY3IsIGJ5ID0gIm1hY2hpbmUiKSAlPiUKICAKICAjIGNhbGN1bGF0ZSBwcmVkaWN0ZWQgcHJvdGVpbiBtYXNzIGluIGcvZ0RDVyB1c2luZyBNVyBpbiBnL21tb2wsIGFuZCBtYXNzIGZyYWN0aW9uCiAgZ3JvdXBfYnkoc2ltdWxhdGlvbikgJT4lIG11dGF0ZSgKICAgIHByZWRpY3RlZF9tYXNzX2dfZ0RDVyA9IHByZWRpY3RlZF9tYXNzX21tb2xfZ0RDVyAqIFN0b2ljaGlvbWV0cnkgKiBNYXNzIC8gMTAwMAogICkKCiMgdGVzdCBpZiBtYXNzIGZyYWN0aW9ucyBzdW0gdG8gcmVhc29uYWJsZSB2YWx1ZQpkZl9tYWNoaW5lcnkgJT4lIAogIHN1bW1hcml6ZShzdW1fZ19nRENXID0gc3VtKHByZWRpY3RlZF9tYXNzX2dfZ0RDVywgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCAlPiUgaGVhZCgxMCkKYGBgCgpJZiB0aGUgdXRpbGl6ZWQgcHJvdGVpbiBmb3IgZW56eW1lcyBhbmQgbWFjaGluZXJ5IGlzIHN1bW1lZCB1cCwgaXQgZG9lcyBub3QgZXhjZWVkIH4wLjI1IGcvZ0RDVy4gVGhlIHJlYXNvbiBmb3IgdGhpcyBpcyB0aGF0IHVwIHRvIDYwJSBvZiB0aGUgcHJvdGVpbiBtYXNzIGlzIG5vdCBtb2RlbGVkIChub24gZW56eW1hdGljLCBORSBwcm90ZW9tZSwgNTcuNyUgYXQgwrUgPSAwLCBzZWUgUiBub3RlYm9vayBgUmFsc3RvbmlhLW1vZGVsLWNvbnN0cmFpbnRzYCksIGFuZCBvbmx5IGFyb3VuZCA2OCUgb2YgdGhlIHRvdGFsIGNlbGwgbWFzcyBpcyBwcm90ZWluLiBJZiB3ZSBjb25zaWRlciB0aGlzLCB0aGFuIHRvdGFsIHByb3RlaW4gbWFzcyBjYW4gYmUgZXN0aW1hdGVkIGFzIG0gPSAwLjI1LygxLTAuNikgPSBgciByb3VuZCgwLjI1LygxLTAuNiksIDMpYCBnL2dEQ1cuIFRoaXMgaXMgbXVjaCBjbG9zZXIgdG8gdGhlIGVzdGltYXRlZCAwLjY4IGcgcHJvdGVpbi9nRENXLiBUaGUgZGlmZmVyZW5jZSBjYW4gYmUgYXR0cmlidXRlZCB0byBkZWZhdWx0IHZhbHVlcyBvZiBhbWlubyBhY2lkIGNvbmNlbnRyYXRpb24gaW4gYFJCQXB5YCB0aGF0IGRldGVybWluZSB0aGUgc2l6ZSBvZiB0aGUgJ3Byb3RlaW4gcG9vbCcuIEl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIHN1bSBvZiB1dGlsaXphYmxlIHByb3RlaW5zIGlzIG5vdCBhIGNvbnN0YW50IHZhbHVlIGJ1dCBhIGxpbmVhciBmdW5jdGlvbiBvZiBncm93dGggcmF0ZS4gVGhlIHRvdGFsIHByb3Rlb21lIHBvb2wgKmluY2x1ZGluZyBub24tZW56eW1hdGljIHByb3RlaW5zKiBpcywgaG93ZXZlciwgY29uc3RhbnQuCgoKIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIHByZWRpY3RlZCBhbmQgZXhwZXJpbWVudGFsbHkgZGV0ZXJtaW5lZCBwcm90ZW9tZQoKVG8gY29tcGFyZSB0aGUgcHJlZGljdGVkIGFuZCBleHBlcmltZW50YWwgcHJvdGVvbWUgY29tcG9zaXRpb24sIHdlIGxvYWQgdGhlIHJlcXVpcmVkIHByb3Rlb21pY3MgZGF0YS4gUHJvdGVvbWljcyBkYXRhIGFyZSBtYXNzIHNwZWN0cm9tZXRyeSBtZWFzdXJlbWVudHMgd2l0aCBsYWJlbC1mcmVlIHF1YW50aWZpY2F0aW9uIG9mIHBlcHRpZGVzLiBQcm90ZWluIHF1YW50aWZpY2F0aW9uIHdhcyBwZXJmb3JtZWQgYnkgc3VtbWluZyB1cCBhbGwgcGVwdGlkZSBpbnRlbnNpdGllcyBwZXIgYW5ub3RhdGVkIHByb3RlaW4uIFRoZSBwcm90ZW9taWMgbWVhc3VyZW1lbnQgdW5pdCwgbWFzcyBmcmFjdGlvbiwgY2FuIGJlIGVhc2lseSB0cmFuc2Zvcm1lZCB0byBnL2dEQ1cgYnkgbXVsdGlwbHlpbmcgbWFzcyBmcmFjdGlvbiB3aXRoIHRoZSB0b3RhbCBwcm90ZWluIG1hc3MgKDAuNjggZy9nRENXKSB1c2VkIGluIFJCQXB5IHNpbXVsYXRpb25zLiBPciAqdmljZSB2ZXJzYSogYnkgY29udmVydGluZyBSQkFweSBwcm90ZWluIGNvbmNlbnRyYXRpb24gKG1tb2wvZ0RDVykgdG8gbWFzcyBmcmFjdGlvbi4KClRvIGFsbG93IGEgZmFpciBjb21wYXJpc29uIGJldHdlZW4gbWVhc3VyZWQgYW5kIHByZWRpY3RlZCBkYXRhLCBpdCBpcyBuZWNlc3NhcnkgdG8gYWdncmVnYXRlIChlLmcuIHN1bSB1cCkgYWxsIHByb3RlaW4gYWJ1bmRhbmNlcyBhbGxvY2F0ZWQgdG8gb25lIHJlYWN0aW9uLiBUaGUgcmVhc29uIGlzIHRoYXQgdGhlIG1vZGVsIHdpbGwgb25seSBwcmVkaWN0ICoqcHJvdGVpbiBhYnVuZGFuY2Ugb2YgdGhlIGZpcnN0IG9mIGEgcmFuZ2Ugb2YgaXNvLWVuenltZXMqKiBmb3IgYSBwYXJ0aWN1bGFyIHJlYWN0aW9uLCB3aGlsZSBpbiByZWFsaXR5IGFub3RoZXIgaXNvLWVuenltZSBtaWdodCBiZSBtb3JlIGFidW5kYW50IChjYXJyeSB0aGUgbWFqb3JpdHkgb2YgZmx1eCkuIFRoaXMgd291bGQgbGVhZCB0byBsb3dlciBjb3JyZWxhdGlvbiBiZXR3ZWVuIG1lYXN1cmVkIGFuZCBwcmVkaWN0ZWQgcHJvdGVpbiBjb25jZW50cmF0aW9ucy4gVGhpcyBpcyBub3QgbmVjZXNzYXJ5IGZvciBtYWNoaW5lcnkgcHJvdGVpbnMsIHRoZXkgaGF2ZSBubyBpc28tZW56eW1lcyBhbmQgYXJlIHVzZWQgdW5kZXIgYWxsIGNvbmRpdGlvbnMuCgoKIyMgQ29tYmluZSBzaW11bGF0aW9ucyBhbmQgZXhwZXJpbWVudGFsIGRhdGEKClRoZSBwcm90ZW9taWNzIGRhdGEgbXVzdCBiZSBtZXJnZWQgd2l0aCBgUkJBcHlgIHNpbXVsYXRpb24gcmVzdWx0cyB1c2luZyBtYXRjaGluZyBjb25kaXRpb25zLiBGaXJzdCwgcHJvdGVvbWljcyBkYXRhIGFyZSBsb2FkZWQgYW5kIHByZXBhcmVkIGZvciBtZXJnaW5nLgoKCioqU3RlcCAxOiBsb2FkIHByb3Rlb21pY3MgZGF0YSoqCgpgYGB7cn0KbG9hZChSZXV0cm9waGFfcHJvdGVvbWljcykKCiMgcGljayBhIGNvbmRpdGlvbiBtYXRjaGluZyBzaW11bGF0aW9ucwpSYWxzdG9uaWFfZXV0cm9waGEgPC0gUmFsc3RvbmlhX2V1dHJvcGhhICU+JQogIAogICMgcm91bmQgc3Vic3RyYXRlIHVwdGFrZSByYXRlCiAgbXV0YXRlKHN1YnN0cmF0ZV91cHRha2VfcmF0ZSA9IHJvdW5kKHN1YnN0cmF0ZV91cHRha2VfcmF0ZSwgMykpICU+JQogIAogICMgc2VsZWN0IG9ubHkgcmVxdWlyZWQgY29sdW1ucwogIHJlbmFtZShncm93dGhfcmF0ZSA9IGdyb3d0aHJhdGUpICU+JQogIHNlbGVjdCh1bmlwcm90LCBsb2N1c190YWcsIHByb3RlaW4sIGNvbmRpdGlvbiwgc3Vic3RyYXRlLCBzdWJzdHJhdGVfdXB0YWtlX3JhdGUsCiAgICBncm93dGhfcmF0ZSwgQ09HX1Byb2Nlc3MsIFIxOlI0KSAlPiUKICAKICAjIHR1cm4gcmF3IGludGVuc2l0eSBtZWFzdXJlbWVudHMgaW50byBtYXNzIGluIGcgcGVyIGdEQ1cgKGFzc3VtaW5nIGEgCiAgIyB0b3RhbCBwcm90ZWluIGNvbmNlbnRyYXRpb24gb2YgMC42OCBnL2dEQ1cpCiAgZ3JvdXBfYnkoY29uZGl0aW9uKSAlPiUKICBtdXRhdGUoYWNyb3NzKG1hdGNoZXMoIlJbMTIzNF0iKSwgZnVuY3Rpb24oeCkgeC9zdW0oeCwgbmEucm0gPSBUUlVFKSowLjY4KSkgJT4lCiAgZ2F0aGVyKGtleSA9ICJyZXBsaWNhdGUiLCB2YWx1ZSA9ICJtYXNzX2dfZ0RDVyIsIFIxOlI0KQpgYGAKCgoqKlN0ZXAgMjogTG9hZCBnZW5lIHJlYWN0aW9uIGFzc29jaWF0aW9ucyBvYnRhaW5lZCBmcm9tIGdlbm9tZSBzY2FsZSBtb2RlbCoqCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQpkZl9tb2RlbF9yZWFjdGlvbnMgPC0gcmVhZF9jc3YobW9kZWxfcmVhY3Rpb25zLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JQogIAogICMgZmlsdGVyIGZvciByZWFjdGlvbnMgd2l0aCBnZW5lIGFzc29jaWF0aW9ucwogIHNlbGVjdChyZWFjdGlvbl9pZCwgcmVhY3Rpb25fbmFtZSwgZ2VuZXMsIGdyb3VwcykgJT4lIHNlcGFyYXRlX3Jvd3MoZ2VuZXMsIHNlcCA9ICIsICIpICU+JQogIGZpbHRlcighaXMubmEoZ2VuZXMpKSAlPiUgCiAgcmVuYW1lKG1vZGVsX2dyb3VwID0gZ3JvdXBzKSAlPiUKICAKICAjIGFkZCBhIG1vcmUgZ2VuZXJhbCBncm91cHMgZGVzY3JpcHRpb24KICBtdXRhdGUobW9kZWxfZ3JvdXBfYmFzaWMgPSBjYXNlX3doZW4oCiAgICBncmVwbCgiUGhlbnlsYWxhfFZhbGluZXxUeXJvc2luZXxHbHV0YW1hdGV8R2x5Y2luZXxUcnlwdG9waGFufE1ldGhpb25pbmV8CiAgICAgICAgICBDeXN0ZWluZXxBbGFuaW5lfEhpc3RpZGluZXxBcmdpbmluZXxMeXNpbmUiLCBtb2RlbF9ncm91cCkgfiAiQW1pbm8gYWNpZCIsCiAgICBncmVwbCgiUGVudG9zZXxDYWx2aW4iLCBtb2RlbF9ncm91cCkgfiAiUFBQICsgQ2FsdmluIGN5Y2xlIiwKICAgIG1vZGVsX2dyb3VwID09ICJDaXRyaWMgQWNpZCBDeWNsZSIgfiAiQ2l0cmljIEFjaWQgQ3ljbGUiLAogICAgbW9kZWxfZ3JvdXAgPT0gIkdseWNvbHlzaXMvR2x1Y29uZW9nZW5lc2lzIiB+ICJHbHljb2x5c2lzL0dsdWNvbmVvZ2VuZXNpcyIsCiAgICBtb2RlbF9ncm91cCA9PSAiR2x5b3h5bGF0ZSBhbmQgRGljYXJib3h5bGF0ZSBtZXRhYm9saXNtIiB+ICJBdXRvdHJvcGhpYyBlbmVyZ3kiLAogICAgbW9kZWxfZ3JvdXAgPT0gIk94aWRhdGl2ZSBQaG9zcGhvcnlsYXRpb24iIH4gIk94aWRhdGl2ZSBQaG9zcGhvcnlsYXRpb24iLAogICAgVFJVRSB+ICJPdGhlciIKICApKQpgYGAKCgoqKlN0ZXAgMzogU2VsZWN0IGFuZCByZW5hbWUgY29uZGl0aW9ucyBmcm9tIFJCQSBzaW11bGF0aW9uKioKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CiMgYWRkIHR5cGUgb2Ygc3Vic3RyYXRlIGxpbWl0YXRpb24KYWRkX2NvbmQgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiAlPiUgbXV0YXRlKHN1YnN0cmF0ZSA9IGNhc2Vfd2hlbigKICAgIGNhcmJvbl9zb3VyY2UgPT0gInN1Y2MiIH4gInN1Y2NpbmF0ZSIsCiAgICBjYXJib25fc291cmNlID09ICJmb3IiIH4gImZvcm1hdGUiLAogICAgY2FyYm9uX3NvdXJjZSA9PSAiZnJ1IiAmIG5pdHJvZ2VuX2NvbmMgPT0gMTguNyB+ICJmcnVjdG9zZSIsCiAgICBUUlVFIH4gImFtbW9uaXVtIiwKICApKQp9CgojIGFkZCBzdWJzdHJhdGUgdXB0YWtlIHJhdGUKZGZfc3Vic3RyYXRlX3VwdGFrZSA8LSBkZl9tYWNyICU+JSAKICBzZWxlY3Qoc2ltdWxhdGlvbiwgc3Vic3RyYXRlX3VwdGFrZV9yYXRlKSAlPiUgZGlzdGluY3QKZGZfcHJvdCA8LSBkZl9wcm90ICU+JSBhZGRfY29uZCAlPiUgbGVmdF9qb2luKGRmX3N1YnN0cmF0ZV91cHRha2UsIGJ5ID0gInNpbXVsYXRpb24iKQpkZl9mbHV4IDwtIGRmX2ZsdXggJT4lIGFkZF9jb25kICU+JSBsZWZ0X2pvaW4oZGZfc3Vic3RyYXRlX3VwdGFrZSwgYnkgPSAic2ltdWxhdGlvbiIpCmBgYAoKCioqU3RlcCA0OiBNZXJnZSBwcm90ZWluIG1lYXN1cmVtZW50cyBhbmQgcHJlZGljdGlvbnMgaW50byBtYXN0ZXIgdGFibGVzKioKClRoZSBmaXJzdCBzdGVwIGlzIHRvIG1lcmdlIHRoZSB0YWJsZXMgZm9yIG1hY2hpbmVyeSBwcm90ZWlucywgdGhhdCBtZWFucyBwcm90ZWlucyByZWxhdGVkIHRvIHJlcGxpY2F0aW9uLCB0cmFuc2NyaXB0aW9uLCB0cmFuc2xhdGlvbiwgYW5kIHByb3RlaW4gZm9sZGluZy4gVGhlc2UgZG9uJ3QgcmVxdWlyZSBhbGxvY2F0aW9uIG9mIHByb3RlaW4gbWFzcyB0byByZWFjdGlvbnMsIGFuZCBtZXJnaW5nIGJlY29tZXMgc2ltcGx5IGFuIG9wZXJhdGlvbiBvbiBlbnp5bWUgSURzIGFuZCBjb25kaXRpb25zLgoKYGBge3J9CiMgam9pbiB3aXRoIHByb3Rlb21pY3MgZGF0YQpkZl9tYWNoaW5lcnkgPC0gZGZfbWFjaGluZXJ5ICU+JQogIGxlZnRfam9pbihSYWxzdG9uaWFfZXV0cm9waGEsCiAgICBieSA9IGMoIkVudHJ5IiA9ICJ1bmlwcm90IiwgInN1YnN0cmF0ZSIsICJzdWJzdHJhdGVfdXB0YWtlX3JhdGUiKSkKYGBgCgpUaGUgc2Vjb25kIHRhYmxlIGZvciBhbGwgZW56eW1hdGljIHByb3RlaW5zIHJlcXVpcmVzIHRoZSBhbGxvY2F0aW9uIG9mIGVzdGltYXRlZCBwcm90ZWluIG1hc3MgdG8gZW56eW1lcy4KT25lIG9wdGlvbiBmb3IgdGhlIGZ1dHVyZSBpcyB0byByZXRyaWV2ZSB0aGVzZSB2YWx1ZXMgZGlyZWN0bHkgZnJvbSBSQkFweSwgYnV0IHRoaXMgaXMgbm90IGltcGxlbWVudGVkIHlldC4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CmRmX3Byb3RfY29tcCA8LSBkZl9tb2RlbF9yZWFjdGlvbnMgJT4lCiAgCiAgIyBqb2luIHdpdGggcHJvdGVvbWljcyBkYXRhCiAgbGVmdF9qb2luKFJhbHN0b25pYV9ldXRyb3BoYSwgYnkgPSBjKCJnZW5lcyIgPSAibG9jdXNfdGFnIikpICU+JQogIGNvbXBsZXRlKG5lc3RpbmcoZ2VuZXMsIHJlYWN0aW9uX2lkLCByZWFjdGlvbl9uYW1lLCBtb2RlbF9ncm91cCwgbW9kZWxfZ3JvdXBfYmFzaWMpLAogICAgbmVzdGluZyhjb25kaXRpb24sIHN1YnN0cmF0ZSwgc3Vic3RyYXRlX3VwdGFrZV9yYXRlLCBncm93dGhfcmF0ZSwgcmVwbGljYXRlKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShzdWJzdHJhdGUpKSAlPiUKICAKICAjIGpvaW4gd2l0aCBzaW11bGF0aW9uIGRhdGEKICBsZWZ0X2pvaW4oZGZfcHJvdCwgYnkgPSBjKCJnZW5lcyIgPSAia2V5IiwgInN1YnN0cmF0ZSIsICJzdWJzdHJhdGVfdXB0YWtlX3JhdGUiKSkgJT4lCiAgCiAgIyBkZXRlcm1pbmUgbnVtYmVyIG9mIHJlYWN0aW9ucyBwZXIgcHJvdGVpbgogIGdyb3VwX2J5KGNvbmRpdGlvbiwgZ2VuZXMsIHJlcGxpY2F0ZSkgJT4lIAogIG11dGF0ZShuX3JlYWN0aW9ucyA9IGxlbmd0aChyZWFjdGlvbl9pZCkpICU+JQogIAogICMgY2FsY3VsYXRlIHByb3RlaW4gbWFzcyBpbiBnL2dEQ1cKICB1bmdyb3VwICU+JSBtdXRhdGUoCiAgICBwcmVkaWN0ZWRfbWFzc19nX2dEQ1cgPSBwcmVkaWN0ZWRfbWFzc19nX2dEQ1cvbl9yZWFjdGlvbnMsCiAgICBtYXNzX2dfZ0RDVyA9IG1hc3NfZ19nRENXL25fcmVhY3Rpb25zCiAgKSAlPiUKICAKICAjIHN1bW1hcml6ZSBieSBzdW1taW5nIHVwIHByb3RlaW4gYWJ1bmRhbmNlIHBlciByZWFjdGlvbiAoTkEgdHJlYXRlZCBhcyB6ZXJvKQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgcmVhY3Rpb25faWQsIHJlYWN0aW9uX25hbWUsIG1vZGVsX2dyb3VwLAogICAgbW9kZWxfZ3JvdXBfYmFzaWMsIHN1YnN0cmF0ZSwgc3Vic3RyYXRlX3VwdGFrZV9yYXRlLCBncm93dGhfcmF0ZSwgcmVwbGljYXRlKSAlPiUgCiAgc3VtbWFyaXplKAogICAgcHJlZGljdGVkX21hc3NfZ19nRENXID0gc3VtKHByZWRpY3RlZF9tYXNzX2dfZ0RDVywgbmEucm0gPSBUUlVFKSwKICAgIG1hc3NfZ19nRENXID0gc3VtKG1hc3NfZ19nRENXLCBuYS5ybSA9IFRSVUUpCiAgKSAlPiUKICBmaWx0ZXIobWFzc19nX2dEQ1cgIT0gMCkgJT4lCiAgCiAgIyBhZGQgcHJlZGljdGVkIGdyb3d0aCByYXRlIHRvIGV4cGVyaW1lbnRhbAogIGxlZnRfam9pbigKICAgIGRmX21hY2hpbmVyeSAlPiUgdW5ncm91cCAlPiUKICAgIHNlbGVjdChzdWJzdHJhdGUsIHN1YnN0cmF0ZV91cHRha2VfcmF0ZSwgcHJlZGljdGVkX2dyb3d0aF9yYXRlKSAlPiUKICAgIGZpbHRlcighZHVwbGljYXRlZChzdWJzdHJhdGVfdXB0YWtlX3JhdGUpKQogICkgJT4lCiAgCiAgIyBhZGQgcHJlZGljdGVkIGZsdXhlcyBwZXIgcmVhY3Rpb24gYW5kIGNvbmRpdGlvbgogIGxlZnRfam9pbigKICAgIGRmX2ZsdXggJT4lIHVuZ3JvdXAgJT4lCiAgICBzZWxlY3Qoa2V5LCB2YWx1ZSwgc3Vic3RyYXRlLCBzdWJzdHJhdGVfdXB0YWtlX3JhdGUpICU+JQogICAgcmVuYW1lKHJlYWN0aW9uX2lkID0ga2V5LCBmbHV4X21tb2xfZ0RDV19oID0gdmFsdWUpCiAgKQpgYGAKCk5vdyB3ZSBwZXJmb3JtIGEgdGVzdC4gV2UgY2hlY2sgaWYgYWxsIHByb3RlaW4gYWJ1bmRhbmNlcyBhbGxvY2F0ZWQgdG8gcmVhY3Rpb25zIHN1bSB0byBhIHJlYXNvbmFibGUgdmFsdWUgYXMgd2Ugd291bGQgZXhwZWN0LiBUaGlzIHZhbHVlIHdvdWxkIGJlIHRoZSB0b3RhbCAqZW56eW1hdGljKiBwcm90ZWluIG1hc3MgaW4gZy9nRENXLCBwZXIgY29uZGl0aW9uIGFuZCByZXBsaWNhdGUsIGFuZCBjb3VsZCByZWFjaCB1cCB0byAwLjIgZy9nRENXIGZvciB0aGUgc2ltdWxhdGlvbnMsIGFuZCBoaWdoZXIgZm9yIHRoZSBhY3R1YWwgZGF0YSAoaW5jbHVkZXMgYWxsIGFkZGl0aW9uYWwgcHJvdGVpbnMgcXVhbnRpZmllZCBpbiBleHBlcmltZW50LCBidXQgbm90IGNhcnJ5aW5nIGZsdXggaW4gbW9kZWwgc2ltdWxhdGlvbnMpLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KZGZfcHJvdF9jb21wICU+JSBncm91cF9ieShjb25kaXRpb24sIHJlcGxpY2F0ZSkgJT4lIAogIGZpbHRlcihwcmVkaWN0ZWRfbWFzc19nX2dEQ1cgIT0gMCkgJT4lCiAgc3VtbWFyaXplKHN1bShtYXNzX2dfZ0RDVyksIHN1bShwcmVkaWN0ZWRfbWFzc19nX2dEQ1cpKQpgYGAKCi0tLS0tLS0tLS0KClRoZSB0b3RhbCBwcmVkaWN0ZWQgcHJvdGVpbiBtYXNzIGlzIGxvd2VyIHRoYW4gdGhlIG1lYXN1cmVkIHByb3RlaW4gbWFzcy4gVGhlcmVmb3JlIHRoZSBmb2xsb3dpbmcgc2VjdGlvbiBxdWFudGlmaWVzIGRpc2NyZXBhbmNpZXMgYmV0d2VlbiBtb2RlbCBwcmVkaWN0ZWQgYW5kIGFjdHVhbGx5IG1lYXN1cmVkIGFidW5kYW5jZXMuIEZpcnN0IHdlIGNhbiBpbnNwZWN0IHRoZSB0b3AgTiByZWFjdGlvbnMgd2l0aCBoaWdoZXN0ICoqYXZlcmFnZSBwcmVkaWN0ZWQgcHJvdGVpbiBhYnVuZGFuY2UqKi4gVGhlIHJhdGlvIG9mIHByZWRpY3RlZCBkaXZpZGVkIGJ5IG1lYXN1cmVkIG1hc3MgaW5kaWNhdGVzIHRoYXQgYSBoYW5kZnVsIG9mIHByb3RlaW5zIGFyZSBwcmVkaWN0ZWQgdG8gYmUgbW9yZSB0aGFuIDEwIGZvbGQgYWJ1bmRhbnQgY29tcGFyZWQgdG8gdGhlIG1lYXN1cmVkIGFidW5kYW5jZS4gVGhpcyBwb2ludHMgdG93YXJkcyBmbHV4ZXMgYmVpbmcgZXJyb25lb3VzbHkgcHJlZGljdGVkIHRvbyBoaWdoIGZvciBwYXJ0aWN1bGFyIHJlYWN0aW9ucywgb3IgKmtfYXBwKiB2YWx1ZXMgYmVpbmcgZXN0aW1hdGVkIHRvbyBsb3cgZm9yIHRoZSBlc3RpbWF0ZWQgZmx1eC4KCkhvd2V2ZXIsIHRoZSBsYXJnZXN0IGRpc2NyZXBhbmNpZXMgYXJpc2UgZnJvbSAqKnVuZGVyLWVzdGltYXRpb24gb2YgcHJvdGVpbnMqKiwgdGhlIG1haW4gY2F1c2UgYmVpbmcgdGhhdCB0aGUgbW9kZWwgcHJlZGljdHMgdGhlICoqb3B0aW1hbCBhYnVuZGFuY2UgZm9yIGVhY2ggZW56eW1lIHRvIGNhcnJ5IGEgY2VydGFpbiBmbHV4KiouIElmIGZsdXhlcyBhcmUgZHJhc3RpY2FsbHkgcmVkdWNlZCBkdWUgdG8gc3Ryb25nIHN1YnN0cmF0ZSBsaW1pdGF0aW9uLCB0aGUgbWluaW1hbCByZXF1aXJlZCBwcm90ZWluIGFidW5kYW5jZSB0byBvcHRpbWl6ZSBncm93dGggd2lsbCBiZSBtdWNoIGxvd2VyIHRoYW4gdGhlIG1lYXN1cmVkIGFidW5kYW5jZS4gQSBiYWN0ZXJpYWwgY2VsbCBvbiB0aGUgb3RoZXIgaGFuZCBjYW4gbm90IGZ1bGx5IHJlZHVjZSBpdHMgcHJvdGVvbWUgYnV0IGluc3RlYWQgJ3N1c3BlbmRzJyBpbmFjdGl2ZSBlbnp5bWVzLgoKCiMjIENoYW5nZSBvZiBtYWNoaW5lcnkgcHJvdGVpbnMgd2l0aCBncm93dGggcmF0ZQoKVGhlIHNpbXVsYXRpb24gYW5kIG1lYXN1cmVtZW50IGRhdGEgd2FzIHByZXBhcmVkIGFuZCBtZXJnZWQgYnkgY29uZGl0aW9uIGluIHRoZSBwcmV2aW91cyBzZWN0aW9ucy4gTm93IGl0IGNhbiBiZSBwbG90dGVkIHRvIGUuZy4gY29tcGFyZSBwcm90ZWluIGFsbG9jYXRpb24gb3ZlciBncm93dGggcmF0ZS4gSW50ZXJlc3RpbmdseSwgd2Ugc2VlIHRoYXQgbW9kZWwgcHJlZGljdGlvbnMgYXJlIHF1aXRlIGFjY3VyYXRlbHkgcmVmbGVjdGluZyB0aGUgcmFuZ2Ugb2YgcHJvdGVpbiBhbGxvY2F0aW9uIGZvciB0aGUgZm91ciBkaWZmZXJlbnQgbWFjaGluZXJpZXMsIHNlZSBmb2xsb3dpbmcgcGFyYWdyYXBocy4gVGhpcyBpcyBhIGdvb2QgY29uZmlybWF0aW9uIG9mIHRoZSBtb2RlbCdzIHByZWRpY3RpdmUgcG93ZXIsIGdpdmVuIHRoYXQgdGhlIHJhdGVzIG9mIHRoZXNlIG1hY2hpbmVyaWVzIHdlcmUgKm5vdCBmaXR0ZWQqIGZyb20gZGF0YSBidXQgdGFrZW4gcHVyZWx5IGZyb20gbGl0ZXJhdHVyZS4gVGhlcmUgYXJlIGhvd2V2ZXIgc29tZSBkZXZpYXRpb25zIGZyb20gdGhlIHByZWRpY3RlZCAnb3B0aW1hbCcgcHJvdGVvbWU6CgotIHRoZSBtb3N0IGltcG9ydGFudCBtYWNoaW5lIGlzIHRoZSByaWJvc29tZS4gUHJlZGljdGlvbiBhbmQgZXhwZXJpbWVudCBzaG93IGEgdmVyeSBzaW1pbGFyIGluY3JlYXNlIG9mIHJpYm9zb21lIGFidW5kYW5jZSB3aXRoIGdyb3d0aCByYXRlIChzbG9wZSBvZiBsaW5lYXIgbW9kZWwpLCBidXQgdGhlIGludGVyc2VjdGlvbiAoYW1vdW50IG9mIHVudXNlZCByaWJvc29tZXMgYXQgemVybyBncm93dGgpIGlzIG11Y2ggaGlnaGVyIGluIGV4cGVyaW1lbnQKLSBjaGFwZXJvbmVzIHNob3cgYW4gaW52ZXJzZSBwcm9wb3J0aW9uYWwgcmVsYXRpb25zaGlwIHdpdGggZ3Jvd3RoIHJhdGUgY29udHJhcnkgdG8gbW9kZWwgcHJlZGljdGlvbi4gRG8gKHNvbWUgb2Y/KSB0aGVzZSBwcm90ZWlucyBoYXZlIGFub3RoZXIgcm9sZSB0aGFuIGp1c3QgZm9sZGluZywgbGlrZSBzdHJlc3MgcmVzcG9uc2U/Ci0gdHJhbnNjcmlwdGlvbiBzZWN0b3IgaXMgcXVpdGUgc3RhYmxlLCBob3dldmVyLCBwcmVkaWN0ZWQgZW56eW1lIG1hc3MgaXMgbXVjaCBsb3dlciB0aGFuIG1lYXN1cmVkICh1bmRlcmVzdGltYXRlZCBieSAxIG9yZGVyIG9mIG1hZ25pdHVkZSAsIDV4MTBeLTMgdnMgNXgxMF4tNCBnL2dEQ1cpCi0gcmVwbGljYXRpb24gc2VjdG9yIGlzIGhlYXZpbHkgdW5kZXJlc3RpbWF0ZWQgYnkgMyBvcmRlcnMgb2YgbWFnbml0dWRlICgxMF4tMyB2cyAxMF4tNiBnL2dEQ1cpCgpUaGUgcHJvdGVpbiBhbGxvY2F0aW9uIGZvciB0aGUgdHdvIGxvdy1hYnVuZGFudCBtYWNoaW5lcywgcmVwbGljYXRpb24gYW5kIHRyYW5zY3JpcHRpb24gaXMgdmVyeSBoYXJkIHRvIHNlZS4gV2UgY2FuIHVwZGF0ZSB0aGUgWS1heGlzIGxpbWl0cyBmb3IgdGhpcyBwbG90IGFuZCByZS1wbG90IGl0IHdpdGggMTAtZm9sZCAnem9vbScuIEFidW5kYW5jZSBvZiByZXBsaWNhdGlvbiBtYWNoaW5lcnkgaW5jcmVhc2VzIHdpdGggZ3Jvd3RoIHJhdGUgZnJvbSAwLjAwMTAgdG8gYSBtYXhpbXVtIG9mIDAuMDAxNSBnL2dEQ1csIGFuIGluY3JlYXNlIGJ5IDIwLTUwJS4gRm9yIHRyYW5zY3JpcHRpb24sIGFidW5kYW5jZSBpbmNyZWFzZXMgd2l0aCBncm93dGggcmF0ZSBmcm9tIDAuMDA2IHRvIDAuMDA4IGcvZ0RDVywgYW4gaW5jcmVhc2Ugb2YgMzMlLiAKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0fQpkZl9tYWNoaW5lcnlfdXRpbCA8LSBkZl9tYWNoaW5lcnkgJT4lCiAgCiAgIyByZW1vdmUgMSBub24tcXVhbnRpZmllZCByZXBsaWNhdGUKICBmaWx0ZXIoIShzdWJzdHJhdGUgPT0gImZydWN0b3NlIiAmIGdyb3d0aF9yYXRlID09IDAuMSAmIHJlcGxpY2F0ZSA9PSAiUjIiKSkgJT4lCiAgCiAgIyBzdW1taW5nIHVwIHByb3RlaW4gbWFzcyBvdmVyIGFsbCBjb25kaXRpb25zCiAgZ3JvdXBfYnkobWFjaGluZSwgc3Vic3RyYXRlLCBncm93dGhfcmF0ZSwgcmVwbGljYXRlKSAlPiUKICBzdW1tYXJpemUoCiAgICBgbWFzcyBbZy9nRENXXWAgPSBzdW0obWFzc19nX2dEQ1csIG5hLnJtID0gVFJVRSksCiAgICBgcHJlZGljdGVkIG1hc3MgW2cvZ0RDV11gID0gc3VtKHByZWRpY3RlZF9tYXNzX2dfZ0RDVywgbmEucm0gPSBUUlVFKSwKICAgIHV0aWxpemF0aW9uID0gYHByZWRpY3RlZCBtYXNzIFtnL2dEQ1ddYC9gbWFzcyBbZy9nRENXXWAKICApICU+JSB1bmdyb3VwICU+JQogIAogICMgcmVwbGFjZSAwIHdpdGggTkEgYW5kIHJlb3JkZXIgZmFjdG9ycwogIG11dGF0ZShhY3Jvc3MobWF0Y2hlcygibWFzcyIpLCB+IG5hX2lmKC54LCAwKSkpICU+JQogIG11dGF0ZShtYWNoaW5lID0gbWFjaGluZSAlPiUgZmFjdG9yKC4sIHVuaXF1ZSguKVtjKDIsNCwzLDEpXSkpCgojIHBsb3QgbWFzcyBhbmQgdXRpbGl6YXRpb24KcGxvdF9tYWNoaW5lcnkgPC0KICAKICB4eXBsb3QoYG1hc3MgW2cvZ0RDV11gICsgYHByZWRpY3RlZCBtYXNzIFtnL2dEQ1ddYCB+IGZhY3Rvcihncm93dGhfcmF0ZSkgfCAKICAgIG1hY2hpbmUgKiBzdWJzdHJhdGUsIGRmX21hY2hpbmVyeV91dGlsLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgYXV0by5rZXkgPSBsaXN0KGNvbHVtbnMgPSAyLCBjZXggPSAwLjcpLAogICAgeGxhYiA9IGV4cHJlc3Npb24oIsK1IFtoIl4tMSoiXSIpLAogICAgeWxhYiA9IGV4cHJlc3Npb24oIm0iW3Byb3RlaW5dKiIgW2cgZ0RDVyJeLTEqIl0iKSwKICAgIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSwgeCA9IGxpc3QoYXQgPSBjKDIsNCkpKSwKICAgIGFzLnRhYmxlID0gVFJVRSwgeWxpbSA9IGMoLTAuMDEsIDAuMTIpLAogICAgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksIHBjaCA9IDE5LCBsd2QgPSAyLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC5lcnJiYXJzKHgsIHksIGV3aWR0aCA9IDAsIC4uLikKICAgICAgcGFuZWwuc3VwZXJwb3NlKHgsIHksIC4uLikKICAgIH0sIHBhbmVsLmdyb3VwcyA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5sbWxpbmUoeCwgeSwgLi4uKQogICAgfQogICkKYGBgCgojIyBVbmRlci11dGlsaXphdGlvbiBvZiBtYWNoaW5lcnkgcHJvdGVpbnMKCkhlcmUsIHdlIGRldGVybWluZSB0aGUgKnVuZGVydXRpbGl6ZWQgbWFjaGluZXJ5IGZyYWN0aW9uKiBieSB0YWtpbmcgdGhlIHJhdGlvIG9mIHNpbXVsYXRlZCBvcHRpbWFsIGVuenltZSBhYnVuZGFuY2UgYW5kIGV4cGVyaW1lbnRhbGx5IG1lYXN1cmVkIGFidW5kYW5jZSwgb3ZlciBhbGwgZm91ciBjZW50cmFsIGRvZ21hIG1hY2hpbmVzLgoKYGBge3IsIGZpZy53aWR0aCA9IDYuNSwgZmlnLmhlaWdodCA9IDQuOCwgbWVzc2FnZSA9IEZBTFNFfQpwbG90X21hY2hpbmVyeV91dGlsIDwtIGRmX21hY2hpbmVyeSAlPiUKICAKICAjIHJlbW92ZSAxIG5vbi1xdWFudGlmaWVkIHJlcGxpY2F0ZQogIGZpbHRlcighKHN1YnN0cmF0ZSA9PSAiZnJ1Y3Rvc2UiICYgZ3Jvd3RoX3JhdGUgPT0gMC4xICYgcmVwbGljYXRlID09ICJSMiIpKSAlPiUKICAKICAjIHN1bW1hcml6aW5nIHByb3RlaW4gdXRpbGl6YXRpb24gZm9yIGFsbCBtYWNoaW5lcwogIG11dGF0ZShtYWNoaW5lID0gIm1hY2hpbmVzIikgJT4lCiAgZ3JvdXBfYnkobWFjaGluZSwgc3Vic3RyYXRlLCBncm93dGhfcmF0ZSwgcmVwbGljYXRlKSAlPiUKICBzdW1tYXJpemUoCiAgICBtYXNzID0gc3VtKG1hc3NfZ19nRENXLCBuYS5ybSA9IFRSVUUpLAogICAgcHJlZGljdGVkX21hc3MgPSBzdW0ocHJlZGljdGVkX21hc3NfZ19nRENXLCBuYS5ybSA9IFRSVUUpLAogICAgdXRpbGl6YXRpb24gPSBwcmVkaWN0ZWRfbWFzcy8obWFzcykKICApICU+JQogIAogICMgcGxvdC4gdXNlIGFsdGVybmF0aW5nID0gMiB0byBzd2l0Y2ggYXhpcyB0byByaWdodCBzaWRlCiAgeHlwbG90KHV0aWxpemF0aW9uKjEwMCB+IGZhY3Rvcihncm93dGhfcmF0ZSkgfCBtYWNoaW5lICogc3Vic3RyYXRlLCAuLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgCiAgICBzY2FsZXMgPSBsaXN0KHggPSBsaXN0KGF0ID0gYygyLDQpKSwgeSA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSkpLAogICAgeGxhYiA9IGV4cHJlc3Npb24oIsK1IFtoIl4tMSoiXSIpLCB5bGFiID0gIiIsCiAgICBrZXkgPSBzaW1wbGVLZXkoIiUgdXRpbGl6YXRpb24iLCBjZXggPSAwLjcpLAogICAgYXMudGFibGUgPSBUUlVFLCBiZXR3ZWVuID0gbGlzdCh4ID0gMC41LCB5ID0gMC41KSwKICAgIHBjaCA9IDE5LCBsd2QgPSAyLCBsYXlvdXQgPSBjKDEsIDQpLCB5bGltID0gYygtMTAsIDExMCksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHhfbWVhbiA9IHVuaXF1ZSh4KTsgeV9tZWFuID0gdGFwcGx5KHksIHgsIG1lYW4pCiAgICAgIHBhbmVsLnh5YXJlYShjKDAsIHhfbWVhbiwgNiksIGMoMCwgeV9tZWFuLCB0YWlsKHlfbWVhbiwgMSkpLCAKICAgICAgICBsdHkgPSAwLCBjb2wgPSBncmV5KDAuNiwgYWxwaGEgPSAwLjUpLCAuLi4pCiAgICAgIHBhbmVsLmVycmJhcnMoeCwgeSwgZXdpZHRoID0gMCwgY29sID0gZ3JleSgwLjUpLCAuLi4pCiAgICB9CiAgKSAlPiUgdXNlT3V0ZXJTdHJpcHMKCnByaW50KHVzZU91dGVyU3RyaXBzKHBsb3RfbWFjaGluZXJ5KSwgcG9zaXRpb24gPSBjKDAsIDAsIDAuNzcsIDEuMDI3KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbWFjaGluZXJ5X3V0aWwsIHBvc2l0aW9uID0gYygwLjcxLCAwLCAxLjAyLCAxLjAyNykpCmdyaWQ6OmdyaWQudGV4dChjKCJCIiwgIkMiKSwgeCA9IGMoMC4wMiwgMC43NSksIHkgPSBjKDAuOTcsMC45NykpCmBgYAoKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZygiLi4vZmlndXJlcy9maWd1cmVfbWFjaGluZV91dGlsLnN2ZyIsIHdpZHRoID0gNi41LCBoZWlnaHQgPSA0LjgpCnByaW50KHVzZU91dGVyU3RyaXBzKHBsb3RfbWFjaGluZXJ5KSwgcG9zaXRpb24gPSBjKDAsIDAsIDAuNzcsIDEuMDI3KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbWFjaGluZXJ5X3V0aWwsIHBvc2l0aW9uID0gYygwLjcxLCAwLCAxLjAyLCAxLjAyNykpCmdyaWQ6OmdyaWQudGV4dChjKCJCIiwgIkMiKSwgeCA9IGMoMC4wMiwgMC43NSksIHkgPSBjKDAuOTcsMC45NykpCmRldi5vZmYoKQpgYGAKCgojIFVuZGVyLXV0aWxpemF0aW9uIG9mIGVuenltZXMKCiMjIEdsb2JhbCB0cmVuZHMgaW4gdXRpbGl6YXRpb24KCldlIGNhbiBzdWJkaXZpZGUgZW56eW1lcyBieSB1dGlsaXphdGlvbiBhbmQgcGxvdCBhdmVyYWdlIG1hc3MsIGF2ZXJhZ2UgdmFyaWFiaWxpdHksIGFuZCBhdmVyYWdlIGVzc2VudGlhbGl0eS4KQXZlcmFnZSBlc3NlbnRpYWxpdHkgaGVyZSBtZWFucyB0aGF0IGV2ZXJ5IHJlYWN0aW9uIHRoYXQgaXMgYXNzb2NpYXRlZCB3aXRoIGF0IGxlYXN0IG9uZSBwcm9iYWJseSBlc3NlbnRpYWwgZ2VuZXMgaXMgcHJvYmFibHkgZXNzZW50aWFsLCBhbmQgZXZlcnkgcmVhY3Rpb25zIHdpdGggYXQgbGVhc3Qgb25lIGVzc2VudGlhbCBnZW5lIGlzIGNvdW50ZWQgYXMgZXNzZW50aWFsICh0YWtpbmcgcHJlY2VkZW5jZSBvdmVyICdwcm9iYWJseSBlc3NlbnRpYWwnKS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CmRmX3V0aWwgPC0gZGZfcHJvdF9jb21wICU+JQogIAogICMgZmlsdGVyIHJlYWN0aW9ucyB3aXRob3V0IHByb3RlaW4gbWVhc3VyZW1lbnQgb3Igc3Vic3RyYXRlCiAgZmlsdGVyKCEoaXMubmEobWFzc19nX2dEQ1cpIHwgaXMubmEoc3Vic3RyYXRlKSksIGdyb3d0aF9yYXRlID09IDAuMjUpICU+JQogIAogICMgZmlyc3QgY2FsY3VsYXRlIHV0aWxpemF0aW9uIHBlciBjb25kaXRpb24KICBncm91cF9ieShtb2RlbF9ncm91cF9iYXNpYywgcmVhY3Rpb25faWQsIHJlYWN0aW9uX25hbWUsIHN1YnN0cmF0ZSkgJT4lCiAgc3VtbWFyaXplKC5ncm91cHMgPSAiZHJvcF9sYXN0IiwKICAgIG1hc3NfZ19nRENXID0gbWVhbihtYXNzX2dfZ0RDVywgbmEucm0gPSBUUlVFKSwKICAgIHByZWRpY3RlZF9tYXNzX2dfZ0RDVyA9IG1lYW4ocHJlZGljdGVkX21hc3NfZ19nRENXLCBuYS5ybSA9IFRSVUUpLAogICAgdXRpbGl6YXRpb24gPSBwcmVkaWN0ZWRfbWFzc19nX2dEQ1cvbWFzc19nX2dEQ1cKICApICU+JQogIAogICMgZGV0ZXJtaW5lIGF2ZXJhZ2UgbWFzcywgc2QgYW5kIHV0aWxpemF0aW9uIHBlciByZWFjdGlvbgogIHN1bW1hcml6ZSguZ3JvdXBzID0gImRyb3BfbGFzdCIsCiAgICBtZWFuX21hc3MgPSBtZWFuKG1hc3NfZ19nRENXLCBuYS5ybSA9IFRSVUUpLAogICAgc2RfbWFzcyA9IHNkKG1hc3NfZ19nRENXLCBuYS5ybSA9IFRSVUUpLAogICAgY3ZfbWFzcyA9IHNkX21hc3MvbWVhbl9tYXNzLAogICAgdXRpbGl6YXRpb24gPSBtZWFuKHV0aWxpemF0aW9uLCBuYS5ybSA9IFRSVUUpCiAgKSAlPiUKICAKICAjIG1hbnVhbCAnY2x1c3RlcmluZycgYmFzZWQgb24gcHJvdGVpbiB2cyBncm93dGggcmF0ZSBzbG9wZQogIG11dGF0ZSh1dGlsaXphdGlvbl9ncm91cCA9IGNhc2Vfd2hlbigKICAgIHV0aWxpemF0aW9uIDw9IDAuMzMgfiAibG93IiwKICAgIGJldHdlZW4odXRpbGl6YXRpb24sIDAuMzMsIDAuNjYpIH4gIm1vZGVyYXRlIiwKICAgIHV0aWxpemF0aW9uID4gMC42NiB+ICJoaWdoIiwKICApICU+JSBmYWN0b3IoLiwgYygibG93IiwgIm1vZGVyYXRlIiwgImhpZ2giKSkpCgoKIyBpbXBvcnQgZXNzZW50aWFsaXR5IGZyb20gVG5TZXEvQmFyU2VxIGRhdGEKZGZfdXRpbCA8LSByZWFkX2NzdigiLi4vZGF0YS9vdXRwdXQvZXNzZW50aWFsaXR5X2VzY2hlci5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JQogIG11dGF0ZShlc3NlbnRpYWxpdHkgPSBlc3NlbnRpYWxpdHkgJT4lIHJlY29kZSgKICAgIGAwYCA9ICJub3QgZXNzZW50aWFsIiwgYDFgID0gInByb2JhYmx5IGVzc2VudGlhbCIsIGAyYCA9ICJlc3NlbnRpYWwiKSkgJT4lCiAgbXV0YXRlKGVzc2VudGlhbGl0eSA9IGNhc2Vfd2hlbigKICAgIGxvY3VzX3RhZyAlaW4lIHJlYWRfY3N2KCIuLi9kYXRhL291dHB1dC9lc3NlbnRpYWxpdHlfZnJ1Y3Rvc2UuY3N2IiwgY29sX3R5cGVzID0gY29scygpKSRsb2N1c190YWcgfiAiZXNzZW50aWFsIiwKICAgIFRSVUUgfiBlc3NlbnRpYWxpdHkKICApKSAlPiUKICAjIG1lcmdlIHdpdGggdXRpbGl6YXRpb24KICBpbm5lcl9qb2luKGRmX21vZGVsX3JlYWN0aW9ucyAlPiUgcmVuYW1lKGxvY3VzX3RhZyA9IGdlbmVzKSkgJT4lCiAgICBncm91cF9ieShyZWFjdGlvbl9pZCkgJT4lIHN1bW1hcml6ZShlc3NlbnRpYWxpdHkgPSBtYXgoZXNzZW50aWFsaXR5LCBuYS5ybSA9IFRSVUUpICU+JQogICAgcmVjb2RlKGAwYCA9ICJub3QgZXNzZW50aWFsIiwgYDFgID0gInByb2JhYmx5IGVzc2VudGlhbCIsIGAyYCA9ICJlc3NlbnRpYWwiKSkgJT4lCiAgcmlnaHRfam9pbihkZl91dGlsKQpgYGAKClRlc3QgaWYgbWVhbiBlbnp5bWUgbWFzcyBhbmQgZW56eW1lIHZhcmlhYmlsaXR5IChDVikgYXJlIG5vcm1hbC1kaXN0cmlidXRlZC4gVGhpcyBpcyBpbXBvcnRhbnQgZm9yIGNob29zaW5nIHRoZSAgc3RhdGlzdGljYWwgdGVzdCBmb3Igc2lnbmlmaWNhbmNlLiBXZSB1c2UgdGhlIFNoYXBpcm8tV2lsa3MgdGVzdCBmb3Igbm9ybWFsaXR5LgoKYGBge3J9CmRmX3V0aWwgJT4lIGdyb3VwX2J5KHV0aWxpemF0aW9uX2dyb3VwKSAlPiUKICBzdW1tYXJpemUoCiAgICBTaGFwaXJvX1dpbGtfcHZhbHVlX21hc3MgPSBzaGFwaXJvLnRlc3QobG9nMTAobWVhbl9tYXNzKSkkcC52YWx1ZSwKICAgIFNoYXBpcm9fV2lsa19wdmFsdWVfQ1YgPSBzaGFwaXJvLnRlc3QobG9nMTAoY3ZfbWFzcykpJHAudmFsdWUKICApCmBgYAoKTm93IHBsb3QgYXZlcmFnZSBtYXNzIGluIGcvZ0RDVywgQ1Ygb2YgbWFzcyBpbiBnL2dEQ1cgYW5kIGVzc2VudGlhbGl0eSAoc2NvcmUpIGZyb20gVG5TZXEgZXhwZXJpbWVudHMuCkFzIHNvbWUgb2YgdGhlIGRhdGEgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCB3ZSBhcHBseSBhIG5vbi1wYXJhbWV0cmljIHNpZ25pZmljYW5jZSB0ZXN0IGluc3RlYWQgLSBoZXJlLCBmb3IgY29tcGFyaXNvbiBvZiB0d28gc2FtcGxlcywgdGhlIGJlc3QgYWx0ZXJuYXRpdmUgdG8gU3R1ZGVudCdzIHQtdGVzdCBpcyB0aGUgTWFubi1XaGl0bmV5IFUtdGVzdC4gRm9yIG9uZSBzYW1wbGUgY29tcGFyaXNvbnMsIHRoZSBub24tcGFyYW1ldHJpYyBhbHRlcm5hdGl2ZSB0byB0LXRlc3Qgd291bGQgYmUgdGhlIFdpbGNveG9uIFJhbmsgU3VtIHRlc3QuCgpgYGB7ciwgZmlnLndpZHRoID0gNy41LCBmaWcuaGVpZ2h0ID0gMi41LCBtZXNzYWdlID0gRkFMU0V9CmJveHBsb3RfY29sb3JibGluZCA8LSBjdXN0b20uY29sb3JibGluZCgpCmJveHBsb3RfY29sb3JibGluZCRib3gucmVjdGFuZ2xlJGx3ZCA9IDEuNQpib3hwbG90X2NvbG9yYmxpbmQkYm94LnVtYnJlbGxhJGx3ZCA9IDEuNQoKCnBsb3RfdW5kZXJ1dGlsX21hc3MgPC0geHlwbG90KGxvZzEwKG1lYW5fbWFzcykgfiB1dGlsaXphdGlvbl9ncm91cCwKICAgIGRmX3V0aWwsCiAgICBwYXIuc2V0dGluZ3MgPSBib3hwbG90X2NvbG9yYmxpbmQsCiAgICBob3Jpem9udGFsID0gRkFMU0UsIGRvLm91dCA9IEZBTFNFLCB5bGltID0gYygtOCwgLTEpLAogICAgeGxhYiA9ICJ1dGlsaXphdGlvbiIsIHlsYWIgPSBleHByZXNzaW9uKCJtIltwcm90ZWluXSoiIFtsb2ciWzEwXSoiIGcgZ0RDVyJeLTEqIl0iKSwKICAgIHNjYWxlcyA9IGxpc3QoeCA9IGxpc3QoY2V4ID0gMC43KSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLnZpb2xpbih4LCB5LCBsd2QgPSAwLCBjb2wgPSBncmV5KDAuNiwgMC41KSwgYm94LndpZHRoID0gMC43LCAuLi4pCiAgICAgIHBhbmVsLmJ3cGxvdCh4LCB5LCBwY2ggPSAifCIsIGJveC53aWR0aCA9IDAuMywgLi4uKQogICAgICBwYW5lbC5wdmFsdWUoeCwgeSwgY2V4ID0gMC43LCBmaXhlZF9wb3MgPSAtMC45LCB2ZXJib3NlID0gVFJVRSwgbWV0aG9kID0gIndpbGNveC50ZXN0IiwgLi4uKQogICAgICBwYW5lbC50ZXh0KHVuaXF1ZSh4KSwgLTcuNSwgbGFiZWxzID0gcGFzdGUwKCJuPSIsIHRhYmxlKHgpKSwgY2V4ID0gMC42KQogICAgfQogICkKCnBsb3RfdW5kZXJ1dGlsX2N2IDwtIHh5cGxvdChsb2cxMChjdl9tYXNzKSB+IHV0aWxpemF0aW9uX2dyb3VwLAogICAgZGZfdXRpbCwKICAgIHBhci5zZXR0aW5ncyA9IGJveHBsb3RfY29sb3JibGluZCwKICAgIGhvcml6b250YWwgPSBGQUxTRSwgZG8ub3V0ID0gRkFMU0UsIHlsaW0gPSBjKC0xLjYsMC42KSwKICAgIHhsYWIgPSAidXRpbGl6YXRpb24iLCB5bGFiID0gZXhwcmVzc2lvbigiQ1YgbSJbcHJvdGVpbl0qIiBbbG9nIlsxMF0qIl0iKSwKICAgIHNjYWxlcyA9IGxpc3QoeCA9IGxpc3QoY2V4ID0gMC43KSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLnZpb2xpbih4LCB5LCBsd2QgPSAwLCBjb2wgPSBncmV5KDAuNiwgMC41KSwgYm94LndpZHRoID0gMC43LCAuLi4pCiAgICAgIHBhbmVsLmJ3cGxvdCh4LCB5LCBwY2ggPSAifCIsIGJveC53aWR0aCA9IDAuMywgLi4uKQogICAgICBwYW5lbC5wdmFsdWUoeCwgeSwgY2V4ID0gMC43LCBmaXhlZF9wb3MgPSAwLjY1LCB2ZXJib3NlID0gVFJVRSwgbWV0aG9kID0gIndpbGNveC50ZXN0IiwgLi4uKQogICAgICBwYW5lbC50ZXh0KHVuaXF1ZSh4KSwgLTEuNDUsIGxhYmVscyA9IHBhc3RlMCgibj0iLCB0YWJsZSh4KSksIGNleCA9IDAuNikKICAgIH0KICApCgpwbG90X3VuZGVydXRpbF9lcyA8LSB4eXBsb3Qobl9yZWFjdGlvbnMgfiB1dGlsaXphdGlvbl9ncm91cCwKICAgIGRmX3V0aWwgJT4lIGdyb3VwX2J5KHV0aWxpemF0aW9uX2dyb3VwLCBlc3NlbnRpYWxpdHkpICU+JSBzdW1tYXJpemUobl9yZWFjdGlvbnMgPSBuKCkpLAogICAgcGFyLnNldHRpbmdzID0gYm94cGxvdF9jb2xvcmJsaW5kLAogICAgY29sID0gY3VzdG9tLmNvbG9yYmxpbmQoKSRzdXBlcnBvc2UucG9seWdvbiRjb2xbYyg1LDQsMildLAogICAgZ3JvdXBzID0gZmFjdG9yKGVzc2VudGlhbGl0eSwgYygibm90IGVzc2VudGlhbCIsICJwcm9iYWJseSBlc3NlbnRpYWwiLCAiZXNzZW50aWFsIikpLAogICAgeGxhYiA9ICJ1dGlsaXphdGlvbiIsIHlsYWIgPSAibnVtYmVyIG9mIHJlYWN0aW9ucyIsIHlsaW0gPSBjKC0yMCwgNjIwKSwKICAgIGV3aWR0aCA9IDAuMSwgbHdkID0gMiwgc2NhbGVzID0gbGlzdCh4ID0gbGlzdChjZXggPSAwLjcpKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwuYmFycGxvdCh4LCB5LCBiZXNpZGUgPSBUUlVFLCAuLi4pCiAgICAgIHBhbmVsLmtleSguLi4sIHBvaW50cyA9IEZBTFNFLCBjb3JuZXIgPSBjKDAuOTUsIDAuOTUpLCBjZXggPSAwLjcpCiAgICB9CiAgKQoKcHJpbnQocGxvdF91bmRlcnV0aWxfbWFzcywgcG9zaXRpb24gPSBjKDAsMCwwLjM1LDEuMDUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF91bmRlcnV0aWxfY3YsIHBvc2l0aW9uID0gYygwLjMyLDAsMC42OCwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfdW5kZXJ1dGlsX2VzLCBwb3NpdGlvbiA9IGMoMC42NCwwLDEsMS4wNSkpCmdyaWQ6OmdyaWQudGV4dChjKCJBIiwgIkIiLCAiQyIpLCB4ID0gYygwLjAyLDAuMzUsMC42NSksIHkgPSBjKDAuOTQsMC45NCwwLjk0KSkKYGBgCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpzdmcoIi4uL2ZpZ3VyZXMvZmlndXJlX2VuenltZV91dGlsLnN2ZyIsIHdpZHRoID0gNy41LCBoZWlnaHQgPSAyLjUpCnByaW50KHBsb3RfdW5kZXJ1dGlsX21hc3MsIHBvc2l0aW9uID0gYygwLDAsMC4zNSwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfdW5kZXJ1dGlsX2N2LCBwb3NpdGlvbiA9IGMoMC4zMiwwLDAuNjgsMS4wNSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X3VuZGVydXRpbF9lcywgcG9zaXRpb24gPSBjKDAuNjQsMCwxLDEuMDUpKQpncmlkOjpncmlkLnRleHQoYygiQSIsICJCIiwgIkMiKSwgeCA9IGMoMC4wMiwwLjM1LDAuNjUpLCB5ID0gYygwLjk0LDAuOTQsMC45NCkpCmRldi5vZmYoKQpgYGAKCgpXZSBjYW4gdHJ5IHRvIGNvbWJpbmUgc2V2ZXJhbCB0eXBlcyBvZiBpbmZvcm1hdGlvbiBmb3IgZWFjaCByZWFjdGlvbiBpbiBjb25jaXNlIG1pbmktZmlndXJlczoKCi0gdGhlIGVzc2VudGlhbGl0eSAoc2ltcGx5IGJ5IGNvbG9yIGFyb3VuZCByZWFjdGlvbiBuYW1lKQotIHRoZSBhdmVyYWdlIHByb3RlaW4gbWFzcyBhc3NvY2lhdGVkIHdpdGggdGhlIHJlYWN0aW9uCi0gdGhlIGNoYW5nZSBvdmVyIGNvbmRpdGlvbnMgKENWKQotIHRoZSB1dGlsaXphdGlvbi9zYXR1cmF0aW9uCgpgYGB7ciwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDMuNX0KIyBlbnp5bWVzIGdyb3VwZWQgYnkgcGF0aHdheQpsaXN0X2NjbV9lbnp5bWVzIDwtIGxpc3QoCiAgY2JiID0gYygiUEdLIiwgIlRQSSIsICJHQVBEIiwgIkZCQSIsICJGQlAiLCAiVEtUMSIsICJUS1QyIiwgIlRBaCIsICJSUEUiLCAiUlBJIiwgIlBSVUsiLCAiUkJQQyIsICJGREgiKSwKICBlZCA9IGMoIlBHSSIsICJHNlBESDJyIiwgIlBHTCIsICJFREQiLCAiRURBIiksCiAgcHlyID0gYygiUEdNIiwgIkVOTyIsICJQWUsiLCAiUERIMSIsICJQREgyIiwgIlBESDMiLCAiUEMiLCAiUFBDIiwgIk1FMSIsICJNRTIiLCAiQ1MiKSwKICB0Y2EgPSBjKCJBQ09OVDEiLCAiQUNPTlQyIiwgIklDREh4IiwgIklDREh5ciIsICJBS0dESCIsICJTVUNPQVMiLCAiU1VDRGkiLCAiU1VDRDEiLCAiRlVNIiwgIk1ESCIsICJNQUxTIikKKQoKcGxvdF9taW5pZmlncyA8LSBkZl9wcm90X2NvbXAgJT4lCiAgZmlsdGVyKHJlYWN0aW9uX2lkICVpbiUgdW5saXN0KGxpc3RfY2NtX2VuenltZXMpLAogICAgZ3Jvd3RoX3JhdGUgPT0gMC4yNSkgJT4lCiAgZ3JvdXBfYnkocmVhY3Rpb25faWQsIHN1YnN0cmF0ZSkgJT4lCiAgIyBjYWxjdWxhdGUgdXRpbGl6YXRpb24KICBtdXRhdGUodXRpbGl6YXRpb24gPSBwcmVkaWN0ZWRfbWFzc19nX2dEQ1cvbWFzc19nX2dEQ1cpICU+JQogIHN1bW1hcml6ZSh1dGlsaXphdGlvbiA9IG1lYW4odXRpbGl6YXRpb24pLCBtZWFuX21hc3MgPSBtZWFuKG1hc3NfZ19nRENXKSkgJT4lCiAgIyByZXNjYWxlIGFsbCB0byBuZXcgcmFuZ2UgYmV0d2VlbiAwIGFuZCAxCiAgdW5ncm91cCAlPiUKICBtdXRhdGUoYWNyb3NzKG1hdGNoZXMoIm1hc3N8dXRpbGl6YXRpb24kIiksIHNjYWxlczo6cmVzY2FsZSkpICU+JQogIHBpdm90X2xvbmdlcihhbGxfb2YoYygibWVhbl9tYXNzIiwgInV0aWxpemF0aW9uIikpKSAlPiUKICBtdXRhdGUobmFtZSA9IGZhY3RvcihuYW1lLCBjKCJtZWFuX21hc3MiLCAidXRpbGl6YXRpb24iKSkpICU+JQogIAogIHh5cGxvdCh2YWx1ZSB+IG5hbWUgfCByZWFjdGlvbl9pZCwgLiwKICAgIGdyb3VwcyA9IHN1YnN0cmF0ZSwgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICAgIHhsYWIgPSAiIiwgeWxhYiA9ICIiLCBmaWxsX2FscGhhID0gMSwKICAgIGV3aWR0aCA9IDAuMDksIGx0eSA9IDEuNSwgYXMudGFibGUgPSBUUlVFLAogICAgc2NhbGVzID0gbGlzdChkcmF3ID0gRkFMU0UpLCB5bGltID0gYygtMC4xLCAxLjApLAogICAgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5yZWN0KDAuNSwtMC4xLDIuNSwxLjEsIGNvbCA9ICJ3aGl0ZSIsIGJvcmRlciA9ICJ0cmFuc3BhcmVudCIpCiAgICAgIHBhbmVsLnJlY3QoMC41LC0wLjEsMS41LDEuMCwgY29sID0gZ3JleSgwLjkpLCBib3JkZXIgPSAidHJhbnNwYXJlbnQiKQogICAgICBwYW5lbC5iYXJwbG90KHgsIHksIGJlc2lkZSA9IFRSVUUsIC4uLikKICAgIH0KICApCgpwcmludChwbG90X21pbmlmaWdzKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZygiLi4vZmlndXJlcy9maWd1cmVfbWluaWZpZ3NfdXRpbC5zdmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMuNSkKcHJpbnQocGxvdF9taW5pZmlncykKZGV2Lm9mZigpCmBgYAoKCiMjIENlbnRyYWwgY2FyYm9uIG1ldGFib2xpc20sIGRldGFpbGVkIHV0aWxpemF0aW9uIHBlciBlbnp5bWUKCkFuYWxvZ291c2x5IHRvIHRoZSBhYm92ZSBhbmFseXNpcywgd2UgY2FuIHBsb3QgcHJvdGVpbiBhYnVuZGFuY2UgYW5kIHV0aWxpemF0aW9uIGZvciBhbGwgZW56eW1lcyBvZiBjZW50cmFsIGNhcmJvbiBtZXRhYm9saXNtIChmcm9tIEZpZ3VyZSAzIEQpLiAKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOH0KcGxvdF9lbnpfY2NtIDwtIGxhcHBseShsaXN0X2NjbV9lbnp5bWVzLCBmdW5jdGlvbihsc3QpIHsKICBkZl9wcm90X2NvbXAgJT4lCiAgZmlsdGVyKHJlYWN0aW9uX2lkICVpbiUgbHN0LCAhaXMubmEoY29uZGl0aW9uKSkgJT4lCiAgbXV0YXRlKHJlYWN0aW9uX2lkID0gZmFjdG9yKHJlYWN0aW9uX2lkLCBsc3QpKSAlPiUKICBncm91cF9ieShyZWFjdGlvbl9pZCkgJT4lIG11dGF0ZShtYXNzX2dfZ0RDVyA9IHNjYWxlczo6cmVzY2FsZShtYXNzX2dfZ0RDVykpICU+JQogIAogIHh5cGxvdChtYXNzX2dfZ0RDVyB+IGZhY3Rvcihncm93dGhfcmF0ZSkgfCByZWFjdGlvbl9pZCwgLiwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgICBncm91cHMgPSBzdWJzdHJhdGUsIHhsYWIgPSAiIiwgI2V4cHJlc3Npb24oIsK1IFtoIl4tMSoiXSIpLAogICAgI3lsYWIgPSBleHByZXNzaW9uKCJtIltwcm90ZWluXSoiIFtnIGdEQ1ciXi0xKiJdIiksCiAgICB5bGFiID0gInJlbGF0aXZlIGFidW5kYW5jZSIsCiAgICBsYXlvdXQgPSBjKDgsIGlmZWxzZShsZW5ndGgobHN0KSA+IDgsIDIsIDEpKSwKICAgIGV3aWR0aCA9IDAsIGx3ZCA9IDIsIGFzLnRhYmxlID0gVFJVRSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFLCB4ID0gbGlzdChhdCA9IGMoMiwgNCkpKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwuZXJyYmFycyh4LCB5LCAuLi4pCiAgICB9CiAgKQp9KQoKcHJpbnQocGxvdF9lbnpfY2NtW1sxXV0sIHBvc2l0aW9uID0gYygwLjAsMC42NywxLDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9lbnpfY2NtW1syXV0sIHBvc2l0aW9uID0gYygwLDAuNTEsMSwwLjc1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZW56X2NjbVtbM11dLCBwb3NpdGlvbiA9IGMoMCwwLjI2LDEsMC41OSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2Vuel9jY21bWzRdXSwgcG9zaXRpb24gPSBjKDAsMCwxLDAuMzQpKQpncmlkOjpncmlkLnRleHQoYygiQ0JCIiwgIkVEIiwgIlBZUiIsICJUQ0EiKSwgeCA9IGMoMC4wMywgMC4wMywgMC4wMywgMC4wMyksIHkgPSBjKDAuOTcsIDAuNzEsIDAuNTMsIDAuMykpCmBgYAoKQW5kIHdlIGNhbiBhZGQgdXRpbGl6YXRpb24gZm9yIHRoZSBkaWZmZXJlbnQgZW56eW1lcyBhbmQgY29uZGl0aW9ucyB0b28uCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDh9CnBsb3RfdXRpbF9jY20gPC0gbGFwcGx5KGxpc3RfY2NtX2VuenltZXMsIGZ1bmN0aW9uKGxzdCkgewogIGRmX3Byb3RfY29tcCAlPiUKICBmaWx0ZXIocmVhY3Rpb25faWQgJWluJSBsc3QsICFpcy5uYShjb25kaXRpb24pKSAlPiUKICBtdXRhdGUocmVhY3Rpb25faWQgPSBmYWN0b3IocmVhY3Rpb25faWQsIGxzdCkpICU+JQogICMgY2FsY3VsYXRlIHV0aWxpemF0aW9uCiAgbXV0YXRlKHBlcmNlbnRfdXRpbGl6YXRpb24gPSBwcmVkaWN0ZWRfbWFzc19nX2dEQ1cvbWFzc19nX2dEQ1cqMTAwKSAlPiUKICAKICB4eXBsb3QocGVyY2VudF91dGlsaXphdGlvbiB+IGZhY3Rvcihncm93dGhfcmF0ZSkgfCByZWFjdGlvbl9pZCwgLiwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgICBncm91cHMgPSBzdWJzdHJhdGUsCiAgICB4bGFiID0gIiIsIHlsYWIgPSAiJSB1dGlsaXphdGlvbiIsIHlsaW0gPSBjKC0yMCwgMjAwKSwKICAgIGxheW91dCA9IGMoOCwgaWZlbHNlKGxlbmd0aChsc3QpID4gOCwgMiwgMSkpLAogICAgZXdpZHRoID0gMCwgbHdkID0gMiwgYXMudGFibGUgPSBUUlVFLAogICAgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksCiAgICBzY2FsZXMgPSBsaXN0KGFsdGVybmF0aW5nID0gRkFMU0UsIHggPSBsaXN0KGF0ID0gYygyLCA0KSkpLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC5lcnJiYXJzKHgsIHksIC4uLikKICAgIH0KICApCn0pCgpwcmludChwbG90X3V0aWxfY2NtW1sxXV0sIHBvc2l0aW9uID0gYygwLjAsMC42NywxLDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF91dGlsX2NjbVtbMl1dLCBwb3NpdGlvbiA9IGMoMCwwLjUxLDEsMC43NSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X3V0aWxfY2NtW1szXV0sIHBvc2l0aW9uID0gYygwLDAuMjYsMSwwLjU5KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfdXRpbF9jY21bWzRdXSwgcG9zaXRpb24gPSBjKDAsMCwxLDAuMzQpKQpncmlkOjpncmlkLnRleHQoYygiQ0JCIiwgIkVEIiwgIlBZUiIsICJUQ0EiKSwgeCA9IGMoMC4wMywgMC4wMywgMC4wMywgMC4wMyksIHkgPSBjKDAuOTcsIDAuNzEsIDAuNTMsIDAuMykpCmBgYAoKRmluYWxseSB3ZSBjYW4gdGFrZSBhIGxvb2sgb24gdHJlbmRzIHdpdGggZ3Jvd3RoIHJhdGUsIGJ5IGZpdHRpbmcgYSBsaW5lYXIgbW9kZWwgZm9yIGVhY2ggZW56eW1lIGFuZCBjb25kaXRpb24gb3ZlciBncm93dGggcmF0ZSwgYW5kIHBsb3R0aW5nIHRoZSBSIG9yIFIgc3F1YXJlZCBwZXIgY29uZGl0aW9uLiBUaGUgUiAoY29ycmVsYXRpb24gY29lZmZpY2llbnQpIHNob3VsZCBpbmRpY2F0ZSB0aGUgdHJlbmQsIGkuZS4gaW5jcmVhc2luZyBvciBkZWNyZWFzaW5nIHdpdGggZ3Jvd3RoIHJhdGUgZm9yIGEgY2VydGFpbiBzdWJzdHJhdGUuCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDYsIG1lc3NhZ2UgPSBGQUxTRX0KcGxvdF9sbV9jY20gPC0gbGFwcGx5KGxpc3RfY2NtX2VuenltZXMsIGZ1bmN0aW9uKGxzdCkgewogIAogIGRmX3Byb3RfY29tcCAlPiUKICBmaWx0ZXIocmVhY3Rpb25faWQgJWluJSBsc3QsICFpcy5uYShjb25kaXRpb24pKSAlPiUKICBtdXRhdGUocmVhY3Rpb25faWQgPSBmYWN0b3IocmVhY3Rpb25faWQsIGxzdCkpICU+JQogIGdyb3VwX2J5KHJlYWN0aW9uX2lkKSAlPiUgbXV0YXRlKG1hc3NfZ19nRENXID0gc2NhbGVzOjpyZXNjYWxlKG1hc3NfZ19nRENXKSkgJT4lCiAgCiAgIyBmaXQgbGluZWFyIG1vZGVsCiAgZ3JvdXBfYnkocmVhY3Rpb25faWQsIHN1YnN0cmF0ZSkgJT4lCiAgc3VtbWFyaXplKAogICAgbGluX3JlZ19zbG9wZSA9IHN1bW1hcnkobG0obWFzc19nX2dEQ1cgfiBncm93dGhfcmF0ZSkpJGNvZWZbMl0sCiAgICBsaW5fcmVnX3B2YWx1ZSA9IHN1bW1hcnkobG0obWFzc19nX2dEQ1cgfiBncm93dGhfcmF0ZSkpJGNvZWZbOF0KICApICU+JQogIG11dGF0ZShsaW5fcmVnX3Nsb3BlID0gbGluX3JlZ19zbG9wZSAlPiUgCiAgICBpZl9lbHNlKC4gPiAyLjUsIDIuNSwgLikgJT4lIGlmX2Vsc2UoLiA8IC0yLjUsIC0yLjUsIC4pKSAlPiUKICAKICBsZXZlbHBsb3QobGluX3JlZ19zbG9wZSB+IHJlYWN0aW9uX2lkICogZmFjdG9yKHN1YnN0cmF0ZSwgdW5pcXVlKHN1YnN0cmF0ZSlbYygzLDIsMSw0KV0pLCAuLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgYXQgPSBzZXEoLTIuNSwgMi41LCBieSA9IDAuMjUpLAogICAgY29sLnJlZ2lvbnMgPSBjb2xvcnNwYWNlOjpkaXZlcmdlX2hjbCgyMCksIHlsYWIgPSAiIiwKICAgIGFzLnRhYmxlID0gVFJVRSwgeGxhYiA9ICIiLAogICAgc2NhbGVzID0gbGlzdCh4ID0gbGlzdChjZXggPSAwLjY1KSkKICApCn0pCgoKcHJpbnQocGxvdF9sbV9jY21bWzFdXSwgcG9zaXRpb24gPSBjKDAuMCwwLjY4LDEsMSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2xtX2NjbVtbMl1dLCBwb3NpdGlvbiA9IGMoMCwwLjQ2LDEsMC43NyksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2xtX2NjbVtbM11dLCBwb3NpdGlvbiA9IGMoMCwwLjI0LDEsMC41NSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2xtX2NjbVtbNF1dLCBwb3NpdGlvbiA9IGMoMCwwLDEsMC4zMikpCmdyaWQ6OmdyaWQudGV4dChjKCJDQkIiLCAiRUQiLCAiUFlSIiwgIlRDQSIpLCB4ID0gYygwLjAzLCAwLjAzLCAwLjAzLCAwLjAzKSwgeSA9IGMoMC45NywgMC43MywgMC41LCAwLjI4KSkKYGBgCgojIyBFeGFtcGxlIG9mIHVuZGVydXRpbGl6YXRpb24gb2YgZW56eW1lcyBmcm9tIENiYiBvcGVyb24gCgpTaW1pbGFyIHRvIG1hY2hpbmVyeSBwcm90ZWlucyB3ZSBjYW4gYWxzbyBmb2xsb3cgdGhlIHNpbXVsYXRpb24gYW5kIGFjdHVhbCBwcm90ZWluIGFidW5kYW5jZSBvZiBhbGwgKGRldGVjdGVkKSBlbnp5bWVzLiBXZSBjYW4gc2VlIHRoYXQgc29tZSBwcm90ZWluIGFidW5kYW5jZXMgbmljZWx5IGZvbGxvdyBhIGdyb3d0aC1yYXRlIGRlcGVuZGVudCBtYW5uZXIsIGluIGxpbmUgd2l0aCBwcmVkaWN0aW9ucyBvZiBoaWdoZXIgZmx1eC4gT25lIHN1Y2ggZXhhbXBsZSBhcmUgQ2FsdmluIGN5Y2xlIGVuenltZXMgZm9yIGZvcm1hdG90cm9waGljIGdyb3d0aC4gSG93ZXZlciwgZm9yIHRoZSBzYW1lIGVuenltZXMgd2Ugc2VlIHRoYXQgbm8gYWJ1bmRhbmNlIGlzIHByZWRpY3RlZCB1bmRlciBoZXRlcm90cm9waGljIGNvbmRpdGlvbnMgYmVjYXVzZSBvZiBtaXNzaW5nIGZsdXggdGhyb3VnaCB0aGUgcGF0aHdheSwgYnV0IGluIGZhY3QgdGhlIHByb3RlaW5zIGFyZSBleHByZXNzZWQgaW4gaGlnaCBhYnVuZGFuY2UsIG9mdGVuIGluIGEgZ3Jvd3RoLXJhdGUgZGVwZW5kZW50IG1hbm5lci4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0Ljh9CmNiYl9zZWxlY3RlZCA8LSBjKCJQR0siLCAiR0FQRCIsICJGQkEiLCAiRkJQIiwgIlRLVDEiLCAiUFJVSyIsICJSQlBDIikKIyAiVEtUMiIsICJUQWgiLCAiUlBFIiwgIlJQSSIKCiMgc3VtbWFyeSB0YWJsZSBvZiBzZWxlY3RlZCBDYWx2aW4gY3ljbGUgZ2VuZXMKZGZfY2FsdmluIDwtIGRmX3Byb3RfY29tcCAlPiUKICAKICAjIHJlbW92ZSAxIG5vbi1xdWFudGlmaWVkIHJlcGxpY2F0ZQogIGZpbHRlcighKHN1YnN0cmF0ZSA9PSAiZnJ1Y3Rvc2UiICYgZ3Jvd3RoX3JhdGUgPT0gMC4xICYgcmVwbGljYXRlID09ICJSMiIpKSAlPiUKICAKICAjIHNlbGVjdCBvbmx5IHN1YnNldCBvZiByZWFjdGlvbnMKICBmaWx0ZXIocmVhY3Rpb25faWQgJWluJSBjYmJfc2VsZWN0ZWQpICU+JQogIG11dGF0ZShyZWFjdGlvbl9pZCA9IGZhY3RvcihyZWFjdGlvbl9pZCwgY2JiX3NlbGVjdGVkKSkgJT4lCiAgcmVuYW1lKGBtYXNzIFtnL2dEQ1ddYCA9IG1hc3NfZ19nRENXLCBgcHJlZGljdGVkIG1hc3MgW2cvZ0RDV11gID0gcHJlZGljdGVkX21hc3NfZ19nRENXKSAlPiUKICBtdXRhdGUodXRpbGl6YXRpb24gPSBgcHJlZGljdGVkIG1hc3MgW2cvZ0RDV11gL2BtYXNzIFtnL2dEQ1ddYCkKCgojIHBsb3QgbWFzcyBhbmQgdXRpbGl6YXRpb24KcGxvdF9jYWx2aW4gPC0gI2RvdWJsZVlTY2FsZSh1c2Uuc3R5bGUgPSBGQUxTRSwgdW5kZXIgPSBUUlVFLAogIAogIHh5cGxvdChgbWFzcyBbZy9nRENXXWAgKyBgcHJlZGljdGVkIG1hc3MgW2cvZ0RDV11gIH4gZmFjdG9yKGdyb3d0aF9yYXRlKSB8IAogICAgcmVhY3Rpb25faWQgKiBzdWJzdHJhdGUsIGRmX2NhbHZpbiwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksIGF1dG8ua2V5ID0gbGlzdChjb2x1bW5zID0gMiwgY2V4ID0gMC43KSwKICAgIHhsYWIgPSBleHByZXNzaW9uKCLCtSBbaCJeLTEqIl0iKSwgeWxpbSA9IGMoLTAuMDAzLCAwLjAzMyksICNjKC0wLjAwMiwgMC4wMzIpLAogICAgeWxhYiA9IGV4cHJlc3Npb24oIm0iW3Byb3RlaW5dKiIgW2cgZ0RDVyJeLTEqIl0iKSwKICAgIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSwgeCA9IGxpc3QoYXQgPSBjKDIsNCkpLAogICAgICB5ID0gbGlzdChhdCA9IGMoMCwgMC4wMSwgMC4wMiwgMC4wMykpKSwgYXMudGFibGUgPSBUUlVFLAogICAgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksIGx3ZCA9IDIsIAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC5lcnJiYXJzKHgsIHksIGV3aWR0aCA9IDAsIC4uLikKICAgICAgcGFuZWwuc3VwZXJwb3NlKHgsIHksIC4uLikKICAgIH0sIHBhbmVsLmdyb3VwcyA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5sbWxpbmUoeCwgeSwgLi4uKQogICAgfQogICkKCnByaW50KHVzZU91dGVyU3RyaXBzKHBsb3RfY2FsdmluKSkKYGBgCgpJdCBpcyBjbGVhciBmcm9tIHRoZSBwcmV2aW91cyBhbmFseXNpcyB0aGF0IHRoZSBjZWxscyBtYWludGFpbiBwcm90ZWlucyBldmVuIGlmIHRoZXkgYXJlIG5vdCBvciBvbmx5IG1hcmdpbmFsbHkgdXRpbGl6ZWQuIFRoZSBhY3R1YWxseSB1dGlsaXplZCBwcm90ZW9tZSBpcyBtaW5pbWFsIHVuZGVyIHN0cm9uZyBzdWJzdHJhdGUgbGltaXRhdGlvbi4gVGhlIG5leHQgc2VjdGlvbiB3aWxsIHRyeSB0byBxdWFudGlmeSB0aGUgKip1bmRlci11dGlsaXphdGlvbiBvZiBlbnp5bWVzKiogYnkgY29tcGFyaW5nIHRoZSBtaW5pbWFsIHByb3RlaW4gcmVxdWlyZW1lbnQgKHNpbXVsYXRpb24pIHZlcnN1cyBleHBlcmltZW50YWxseSBkZXRlcm1pbmVkIHByb3RlaW4gYWJ1bmRhbmNlLiBJdCBpcyBzdWZmaWNpZW50IHRvIGRldGVybWluZSB0aGUgdW5kZXJ1dGlsaXplZCBwcm90ZWluIGZyYWN0aW9uIChvZiBhbGwgdXRpbGl6ZWQgcHJvdGVpbnMpLgoKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQgPSA0fQpwbG90X2NhbHZpbl91dGlsIDwtIGRmX3Byb3RfY29tcCAlPiUKICAKICAjIGZpbHRlciBzZXQgb2YgZW56eW1lcwogIGZpbHRlcihyZWFjdGlvbl9pZCAlaW4lIGMoIlBHSyIsICJHQVBEIiwgIkZCQSIsICJGQlAiLCAiVEtUMSIsICJQUlVLIiwgIlJCUEMiKSkgJT4lCiAgCiAgIyByZW1vdmUgMSBub24tcXVhbnRpZmllZCByZXBsaWNhdGUKICBmaWx0ZXIoIShzdWJzdHJhdGUgPT0gImZydWN0b3NlIiAmIGdyb3d0aF9yYXRlID09IDAuMSAmIHJlcGxpY2F0ZSA9PSAiUjIiKSkgJT4lCiAgCiAgIyBjYWxjdWxhdGUgcHJvdGVpbiB1dGlsaXphdGlvbgogIGdyb3VwX2J5KHN1YnN0cmF0ZSwgZ3Jvd3RoX3JhdGUsIHJlcGxpY2F0ZSkgJT4lCiAgc3VtbWFyaXplKAogICAgbWFzcyA9IHN1bShtYXNzX2dfZ0RDVywgbmEucm0gPSBUUlVFKSwKICAgIHByZWRpY3RlZF9tYXNzID0gc3VtKHByZWRpY3RlZF9tYXNzX2dfZ0RDVywgbmEucm0gPSBUUlVFKSwKICAgIHBlcmNlbnRfdXRpbGl6YXRpb24gPSBwcmVkaWN0ZWRfbWFzcy8obWFzcykKICApICU+JQogIAogICMgYWRkIHBzZXVkbyBlbnp5bWUgZ3JvdXAKICBtdXRhdGUoZW56eW1lID0gIkNCQiBjeWNsZSIpICU+JQogIAogICMgb3B0aW9uYWxseSBzaG93IHNvbWUgc3VtbWFyeSBzdGF0aXN0aWNzCiAgI3N1bW1hcml6ZShhY3Jvc3MobWF0Y2hlcygibWFzc3x1dGlsIiksIG1lYW4pKSAlPiUKICAjbXV0YXRlKHVudXRpbF9tYXNzID0gbWFzcy1wcmVkaWN0ZWRfbWFzcykgJT4lCiAgI3N1bW1hcml6ZShtYXgodW51dGlsX21hc3MpLzAuNjgqMTAwKSAjZm9yIHBlcmNlbnQ6IC8wLjY4KjEwMAogIAogICMgcGxvdAogIHh5cGxvdChwZXJjZW50X3V0aWxpemF0aW9uKjEwMCB+IGZhY3Rvcihncm93dGhfcmF0ZSkgfCBlbnp5bWUgKiBzdWJzdHJhdGUsIC4sCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLCB5bGltID0gYygtMTAsIDExMCksCiAgICBzY2FsZXMgPSBsaXN0KGFsdGVybmF0aW5nID0gRkFMU0UsIHggPSBsaXN0KGF0ID0gYygyLDQpKSksCiAgICB4bGFiID0gZXhwcmVzc2lvbigiwrUgW2giXi0xKiJdIiksIHlsYWIgPSAiIiwKICAgIGFzLnRhYmxlID0gVFJVRSwgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksCiAgICBsd2QgPSAyLCBrZXkgPSBzaW1wbGVLZXkoIiUgdXRpbGl6YXRpb24iLCBjZXggPSAwLjcpLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICB4X21lYW4gPSB1bmlxdWUoeCk7IHlfbWVhbiA9IHRhcHBseSh5LCB4LCBtZWFuKQogICAgICBwYW5lbC54eWFyZWEoYygwLCB4X21lYW4sIDYpLCBjKDAsIHlfbWVhbiwgdGFpbCh5X21lYW4sIDEpKSwgCiAgICAgICAgbHR5ID0gMCwgY29sID0gZ3JleSgwLjYsIGFscGhhID0gMC41KSwgLi4uKQogICAgICBwYW5lbC5lcnJiYXJzKHgsIHksIGV3aWR0aCA9IDAsIGNvbCA9IGdyZXkoMC41KSwgLi4uKQogICAgfQogICkgJT4lIHVzZU91dGVyU3RyaXBzCgpwcmludChwbG90X2NhbHZpbl91dGlsKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZygiLi4vZmlndXJlcy9maWd1cmVfY2FsdmluX3V0aWwuc3ZnIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDQuOCkKcHJpbnQodXNlT3V0ZXJTdHJpcHMocGxvdF9jYWx2aW4pLCBwb3NpdGlvbiA9IGMoMCwgMCwgMC44MywgMS4wMjcpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9jYWx2aW5fdXRpbCwgcG9zaXRpb24gPSBjKDAuNzcsIDAsIDEuMDMsIDEuMDI3KSkKZ3JpZDo6Z3JpZC50ZXh0KGMoIkEiLCAiQiIpLCB4ID0gYygwLjAzLCAwLjgxKSwgeSA9IGMoMC45NywgMC45NykpCmRldi5vZmYoKQpgYGAKCi0tLS0tLS0tLS0KCkEgZmluYWwgY29udHJvbCBpcyB0byBjaGVjayB3aGljaCBjb3BpZXMgb2YgdGhlIHNhbWUgQ2JiIGN5Y2xlIGVuenltZXMgYXJlIG1vcmUgYWJ1bmRhbnQsIG9yIHVuZGVyZ28gc3Ryb25nZXIgY2hhbmdlcy4gRm9yIGV4YW1wbGUsIFBSVUsgYW5kIFJ1YmlzY28gKFJCUEMpIGFyZSBvbmx5IHByZXNlbnQgb24gdGhlIHR3byBjb3BpZXMgb2YgdGhlIENhbHZpbiBjeWNsZSBvcGVyb24sIG9uIHBIRzEgYW5kIENocm9tb3NvbWUgMi4gQnV0IG90aGVyIGdseWNvbHlzaXMgcmVsYXRlZCBlbnp5bWVzIGhhdmUgb25lIGNhbm9uaWNhbCBjb3B5IG9uIGNocm9tb3NvbWUgMSwgd2hpY2ggc2VlbXMgdG8gZW5jb2RlIG1vc3RseSAiaG91c2VrZWVwaW5nIiBmdW5jdGlvbnMuIFdlIGNhbiBsb29rIGF0IHByb3RlaW4gYWJ1bmRhbmNlIG9mIHRoZSB0aHJlZSBzaW5nbGUgZ2VuZSBsb2NpIGZvciBlYWNoIGVuenltZSAoY2hyb21vc29tZSAxLCAyLCBhbmQgcEhHMSBtZWdhcGxhc21pZCkuIEl0J3MgaW1wb3J0YW50IHRvIGtlZXAgaW4gbWluZCB0aGF0IG1vc3QgcGVwdGlkZXMgZm9yIGNiYiBnZW5lcyBjYW4gbm90IGJlIGRpc3Rpbmd1aXNoZWQgYmV0d2VlbiBjaHJvbW9zb21lIDIgYW5kIHBIRzEsIHRoZXJlZm9yZSB0aGUgc2ltaWxhciBleHByZXNzaW9uIHBhdHRlcm4uCgpgYGB7ciwgZmlnLndpZHRoID0gNy41LCBmaWcuaGVpZ2h0ID0gNS41LCBtZXNzYWdlID0gRkFMU0V9CnBsb3RfY2FsdmluX2xvY2kgPC0gZGZfbW9kZWxfcmVhY3Rpb25zICU+JSBmaWx0ZXIocmVhY3Rpb25faWQgJWluJSBjKCJQR0siLCAiR0FQRCIsICJGQkEiLCAiRkJQIiwgIlRLVDEiKSkgJT4lCiAgc2VsZWN0KHJlYWN0aW9uX2lkLCBnZW5lcykgJT4lIHJlbmFtZShsb2N1c190YWcgPSBnZW5lcykgJT4lCiAgZmlsdGVyKGxvY3VzX3RhZyAhPSAiSDE2X0IwMjc4IikgJT4lCiAgaW5uZXJfam9pbihSYWxzdG9uaWFfZXV0cm9waGEpICU+JQogIG11dGF0ZShjaHJvbW9zb21lID0gY2FzZV93aGVuKAogICAgZ3JlcGwoIkgxNl9BIiwgbG9jdXNfdGFnKSB+ICJjaHJvbW9zb21lIDEiLAogICAgZ3JlcGwoIkgxNl9CIiwgbG9jdXNfdGFnKSB+ICJjaHJvbW9zb21lIDIiLAogICAgZ3JlcGwoIlBIRyIsIGxvY3VzX3RhZykgfiAicEhHMSIsCiAgKSkgJT4lCiAgCiAgeHlwbG90KG1hc3NfZ19nRENXIH4gZmFjdG9yKGdyb3d0aF9yYXRlKSB8IHJlYWN0aW9uX2lkICogY2hyb21vc29tZSwgLiwKICAgIGdyb3VwcyA9IHN1YnN0cmF0ZSwgbGF5b3V0ID0gYyg1LCAzKSwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksIGx3ZCA9IDIsCiAgICBzY2FsZXMgPSBsaXN0KGFsdGVybmF0aW5nID0gRkFMU0UpLAogICAgeGxhYiA9IGV4cHJlc3Npb24oIsK1IFtoIl4tMSoiXSIpLAogICAgeWxhYiA9IGV4cHJlc3Npb24oIm0iW3Byb3RlaW5dKiIgW2cgZ0RDVyJeLTEqIl0iKSwKICAgIGFzLnRhYmxlID0gVFJVRSwgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmVycmJhcnMoeCwgeSwgZXdpZHRoID0gMCwgdHlwZSA9IGMoInAiLCAibCIpLCAuLi4pCiAgICAgIHBhbmVsLmtleSguLi4sIGNleCA9IDAuNykKICAgIH0KICApCgpwcmludChwbG90X2NhbHZpbl9sb2NpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZygiLi4vZmlndXJlcy9maWd1cmVfY2FsdmluX2xvY2kuc3ZnIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDUuNSkKcHJpbnQocGxvdF9jYWx2aW5fbG9jaSkKZGV2Lm9mZigpCmBgYAoKIyBFeHByZXNzaW9uIGFuZCB1dGlsaXphdGlvbiBvZiBQSEIgc3ludGhlc2lzIGVuenltZXMKClRoZSBwYXRod2F5IGZvciBQSEIgc3ludGhlc2lzIGNvbnNpc3RzIG1haW5seSBvZiB0aHJlZSBlbnp5bWVzLCBlYWNoIG9uZSB3aXRoIG11bHRpcGxlIGdlbmVzIGFubm90YXRlZC4KVGhlIGZpcnN0IGlzIEFjZXR5bC1Db0EgQy1hY2V0eWx0cmFuc2ZlcmFzZSAocGhhQSksIG1ha2luZyBhY2V0b2FjZXR5bC1Db0EsIHRoZSBzZWNvbmQgb25lIGlzIEFjZXRvYWNldHlsLUNvQSByZWR1Y3Rhc2UgKHBoYUIpLCBtYWtpbmcgMy1oeWRyb3h5LWJ1dHlyeWwtQ29BLCBhbmQgdGhlIHRoaXJkIGlzIHRoZSBQSEItc3ludGhhc2UgY29uZGVuc2luZyAzSEIgbW9ub21lcnMgdG8gUEhCIHBvbHltZXIgKHBoYUMpLiBUaGUgbWFzcyBmb3IgZWFjaCBlbnp5bWUgaXMgdGhlIHN1bSBvZiB0aGUgaW5kaXZpZHVhbCBwcm90ZWluIG1hc3NlcyBhbGxvY2F0ZWQgdG8gdGhpcyByZWFjdGlvbi4KCmBgYHtyLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNC41fQpwbG90X3BoYl9tYXNzIDwtIGRmX3Byb3RfY29tcCAlPiUKICBmaWx0ZXIocmVhY3Rpb25faWQgJWluJSBjKCJQSEFTIiwgIkFDQUNUMXIiLCAiQUFDT0FSIikpICU+JQogIG11dGF0ZShyZWFjdGlvbl9pZCA9IHJlY29kZShyZWFjdGlvbl9pZCwKICAgIFBIQVMgPSAiUEhBUyAocGhhQykiLCBBQ0FDVDFyID0gIkFDQUNUMXIgKHBoYUEpIiwgQUFDT0FSID0gIkFBQ09BUiAocGhhQikiKSkgJT4lCiAgCiAgeHlwbG90KG1hc3NfZ19nRENXIH4gZmFjdG9yKGdyb3d0aF9yYXRlKSB8IAogICAgICBmYWN0b3IocmVhY3Rpb25faWQsIHVuaXF1ZShyZWFjdGlvbl9pZClbYygyLDEsMyldKSwgLiwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgICBncm91cHMgPSBzdWJzdHJhdGUsIHhsYWIgPSAiIiwKICAgIHlsYWIgPSBleHByZXNzaW9uKCJtIltwcm90ZWluXSoiIFtnIGdEQ1ciXi0xKiJdIiksCiAgICBsYXlvdXQgPSBjKDMsIDEpLCBld2lkdGggPSAwLCBsd2QgPSAyLCBhcy50YWJsZSA9IFRSVUUsCiAgICBiZXR3ZWVuID0gbGlzdCh4ID0gMC41LCB5ID0gMC41KSwgYXV0by5rZXkgPSBsaXN0KGNvbHVtbnMgPSAyKSwKICAgIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSwgeCA9IGxpc3QoYXQgPSBjKDIsIDQpKSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmVycmJhcnMoeCwgeSwgLi4uKQogICAgfQogICkKCnBsb3RfcGhiX3V0aWwgPC0gZGZfcHJvdF9jb21wICU+JQogIGZpbHRlcihyZWFjdGlvbl9pZCAlaW4lIGMoIlBIQVMiLCAiQUNBQ1QxciIsICJBQUNPQVIiKSkgJT4lCiAgbXV0YXRlKHBlcmNlbnRfdXRpbGl6YXRpb24gPSBwcmVkaWN0ZWRfbWFzc19nX2dEQ1cvbWFzc19nX2dEQ1cqMTAwKSAlPiUKICBtdXRhdGUocmVhY3Rpb25faWQgPSByZWNvZGUocmVhY3Rpb25faWQsIAogICAgUEhBUyA9ICJQSEFTIChwaGFDKSIsIEFDQUNUMXIgPSAiQUNBQ1QxciAocGhhQSkiLCBBQUNPQVIgPSAiQUFDT0FSIChwaGFCKSIpKSAlPiUKICAKICB4eXBsb3QocGVyY2VudF91dGlsaXphdGlvbiB+IGZhY3Rvcihncm93dGhfcmF0ZSkgfCAKICAgICAgZmFjdG9yKHJlYWN0aW9uX2lkLCB1bmlxdWUocmVhY3Rpb25faWQpW2MoMiwxLDMpXSksIC4sCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLAogICAgZ3JvdXBzID0gc3Vic3RyYXRlLAogICAgeGxhYiA9IGV4cHJlc3Npb24oIsK1IFtoIl4tMSoiXSIpLAogICAgeWxhYiA9ICIlIHV0aWxpemF0aW9uIiwgeWxpbSA9IGMoLTIwLCAzMjApLAogICAgbGF5b3V0ID0gYygzLCAxKSwKICAgIGV3aWR0aCA9IDAsIGx3ZCA9IDIsIGFzLnRhYmxlID0gVFJVRSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFLCB4ID0gbGlzdChhdCA9IGMoMiwgNCkpKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwuZXJyYmFycyh4LCB5LCAuLi4pCiAgICB9CiAgKQoKcHJpbnQocGxvdF9waGJfbWFzcywgcG9zaXRpb24gPSBjKDAsMC40MiwxLDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9waGJfdXRpbCwgcG9zaXRpb24gPSBjKDAuMDQsMCwxLDAuNTUpKQpgYGAKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc3ZnKCIuLi9maWd1cmVzL2ZpZ3VyZV9QSEJfcGF0aHdheS5zdmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDQuNSkKcHJpbnQocGxvdF9waGJfbWFzcywgcG9zaXRpb24gPSBjKDAsMC40MiwxLDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9waGJfdXRpbCwgcG9zaXRpb24gPSBjKDAuMDQsMCwxLDAuNTUpKQpkZXYub2ZmKCkKYGBgCgoKIyBZaWVsZC1ncm93dGggcmF0ZSB0cmFkZW9mZiB3aGVuIEN1cHJpYXZpZHVzIHJlLWFzc2ltaWxhdGVzIENPMj8KCkl0IHdhcyBleHBlcmltZW50YWxseSBvYnNlcnZlZCBiZWZvcmUgdGhhdCAqUi4gZXV0cm9waGEqIGV4cHJlc3NlcyBSdWJpc2NvIGFuZCBvdGhlciAqY2JiKi1vcGVyb24gbG9jYXRlZCBnZW5lcyBldmVuIG9uIGdyb3d0aCBvbiBmcnVjdG9zZSBvciBvdGhlciBoZXRlcm90cm9waGljIHN1YnN0cmF0ZXMuIFtCb3dpZW4gZXQgYWwuLCAxOTkwXShodHRwOi8vZG9pLndpbGV5LmNvbS8xMC4xMDE2LzAzNzgtMTA5Nyg5MCk5MDQ5My1BKSwgc2hvdyB0aGF0IFJ1YmlzY28gYWN0aXZpdHkgY2FuIGJlIGZvdW5kIG9uIGdyb3d0aCBvbiBmcnVjdG9zZSwgYnV0IG5vdCBvbiBweXJ1dmF0ZS4gW0RhbmdlbCAmIFRhYml0YSwgMjAxNV0oaHR0cHM6Ly9qYi5hc20ub3JnL2NvbnRlbnQvMTk3LzIyLzM0ODgpLCByZXZpZXcgdGhlIHJlZ3VsYXRpb24gYnkgQ2JiUiB0eXBlIHJlZ3VsYXRvcnMgYW1vbmcgb3RoZXJzIGFsc28gaW4gKlIuIGV1dHJvcGhhKiBhbmQgbWVudGlvbiB0aGF0IGNpdHJhdGUgYWxzbyBsZWFkcyB0byBhY3RpdmF0aW9uIG9mICpjYmIqIGV4cHJlc3Npb24uIFRoZXkgaHlwb3RoZXNpemUgdGhhdCByaWJ1bG9zZSBiaXNwaG9zcGhhdGUgKFJ1QlApIGlzIGFuIGFjdGl2YXRpbmcgZWZmZWN0b3Igd2hpbGUgb3RoZXIgY2VudHJhbCBtZXRhYm9saXNtIGludGVybWVkaWF0ZXMgc3VjaCBhcyBwaG9zcGhvZW5vbHB5cnV2YXRlIChQRVApIGFyZSByZXByZXNzaW5nIGVmZmVjdG9yIG1vbGVjdWxlcy4gSXQgd2FzIHNwZWN1bGF0ZWQgaW4gQm93aWVuIGV0IGFsLiB0aGF0IHJlZ3VsYXRpb24gYnkgY2JiUiBtaWdodCBhY3R1YWxseSBiZSBhIHJlcHJlc3Npb24tZGVyZXByZXNzaW9uIHJhdGhlciB0aGFuIGFjdGl2YXRpb24sIHdoaWNoIG1lYW5zIHRoYXQgdGhlIGRlZmF1bHQgc3RhdGUgd291bGQgYmUgYSBib3VuZCBjYmJSIHJlcHJlc3NpbmcgdGhlICpjYmIqIG9wZXJvbi4gSG93ZXZlciBpdCBiZWNhbWUgY2xlYXIgdGhhdCB0aGlzIGlzIG5vdCB0aGUgY2FzZS4gW1NoaW1penUgZXQgYWwuLCAyMDE1XShodHRwOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvc3JlcDExNjE3KSwga25vY2tlZCB0aGUgY2JiUiBnZW5lIG91dCBhbmQgdGhlIHJlc3VsdCB3YXMgcmVkdWNlZCBleHByZXNzaW9uIG9mICpjYmIqIGdlbmVzIGJ5IDEwMCBmb2xkLiBUaGlzIHByb3ZlcyB0aGF0IGNiYlIgaXMgYSByZXF1aXJlZCBhY3RpdmF0b3IgYW5kIG5vdCBhIHJlcHJlc3NvciBvZiBjYmIsIG90aGVyd2lzZSBjYmJSIGRlbGV0aW9uIHdvdWxkIGxlYWQgdG8gY29uc3RpdHV0aXZlIGFjdGl2YXRpb24gb2YgY2JiIGV4cHJlc3Npb24uCgpUaGUgc2FtZSBncm91cCBzcGVjdWxhdGVkIGluIHRoZWlyIHN0dWR5IHRoYXQgdGhlIGFkZGl0aW9uYWwgUnViaXNjbyBleHByZXNzaW9uIGNvdWxkIGhhdmUgYSBiZW5lZml0IGZvciBjYXJib24geWllbGQsIHNwZWNpZmljYWxseSBmb3IgcHJvZHVjdCB5aWVsZCBvZiBQSEIuIFRoZXkgc2hvdyB0aGF0IFBIQiBmcm9tIHRoZSBXVCBjb250YWlucyBzbGlnaHRseSBtb3JlIDEzQyBsYWJlbGVkIG1hc3MgKGFuZCB0b3RhbCBtYXNzKSB0aGFuIHRoZSBjYmJSIGFuZCBjYmJTL0wga25vY2tvdXRzLiBUaGlzIG1lYW5zIHRoYXQgdGhlIGNlbGwgd291bGQgaGF2ZSBhIChwcm9kdWN0IG9yIGJpb21hc3MpIHlpZWxkIGFkdmFudGFnZSBieSBSdWJpc2NvIGV4cHJlc3Npb24gb24gZnJ1Y3Rvc2UuCgpUaGUgZm9sbG93aW5nIHNlY3Rpb24gdGVzdHMgdGhlIGh5cG90aGVzaXMgb2YgYSB5aWVsZC1ncm93dGggcmF0ZSB0cmFkZW9mZiB3aXRoIHRoZSByZXNvdXJjZSBhbGxvY2F0aW9uIG1vZGVsLiBGaXJzdCBzaW11bGF0aW9uIGRhdGEgZnJvbSB0aGUgUkJBIG1vZGVsIGlzIGltcG9ydGVkCgpgYGB7cn0KI2FkanVzdCBwYXRoCm1peG90cm9waF9kaXIgPC0gZ3N1Yigic3Vic3RyYXRlX2xpbWl0YXRpb24iLCAibWl4b3Ryb3BoeSIsIHNpbXVsYXRpb25fZGlyKQoKIyByZWFkIHNpbXVsYXRpb24gcmVzdWx0cwpkZl9taXhmbHV4IDwtIHJlYWRfcmJhX3Jlc3VsdChsaXN0LmZpbGVzKG1peG90cm9waF9kaXIsIHBhdHRlcm4gPSAiZmx1eGVzXy4qLnRzdiQiLCBmdWxsLm5hbWVzID0gVFJVRSkpCmRmX21peG1hY3IgPC0gcmVhZF9yYmFfcmVzdWx0KGxpc3QuZmlsZXMobWl4b3Ryb3BoX2RpciwgcGF0dGVybiA9ICJtYWNyb3Byb2Nlc3Nlc18uKi50c3YiLCBmdWxsLm5hbWVzID0gVFJVRSkpCgojIHJlbmFtZSBjb2x1bW4KZGZfbWl4Zmx1eCA8LSBkZl9taXhmbHV4ICU+JSByZW5hbWUoQ08yX3JlZml4YXRpb24gPSBzaW1fcnVuKSAlPiUgZmlsdGVyKENPMl9yZWZpeGF0aW9uIDw9IDUpCmRmX21peG1hY3IgPC0gZGZfbWl4bWFjciAlPiUgcmVuYW1lKENPMl9yZWZpeGF0aW9uID0gc2ltX3J1bikgJT4lIGZpbHRlcihDTzJfcmVmaXhhdGlvbiA8PSA1KQpgYGAKClRoZSBuZXh0IHRhc2sgaXMgdG8gY29tcGFyZSB5aWVsZCwgZ3Jvd3RoIHJhdGUsIENPPHN1Yj4yPC9zdWI+IGVtaXNzaW9uIGFuZCBvdGhlciBmbHV4ZXMgZm9yIGEgcmFuZ2Ugb2Ygc2ltdWxhdGlvbnMgd2hlcmUgUnViaXNjbyB3YXMgZm9yY2VkIHRvIHJlLWZpeCBlbWl0dGVkIENPPHN1Yj4yPC9zdWI+IGZyb20gZ3Jvd3RoIG9uIGZydWN0b3NlLiBTaW11bGF0aW9ucyB3ZXJlIHBlcmZvcm1lZCBmb3IgaW5jcmVhc2luZyBmbHV4IHRocm91Z2ggUnViaXNjbyBmcm9tIDAgdG8gNSBtbW9sIGdEQ1c8c3VwPi0xPC9zdXA+IGg8c3VwPi0xPC9zdXA+LgoKYGBge3IsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSAyLjd9CnBsb3RfbWl4b19tdSA8LSBsYXBwbHkoYygibXUiLCAieWllbGQiKSwgZnVuY3Rpb24oa2V5cykgewogIAogIGlmIChrZXlzID09ICJtdSIpIHsKICAgIHlsYWJlbCA8LSBleHByZXNzaW9uKCLCtSBbaCJeLTEqIl0iKQogICAgeWxpbSA8LSBjKDAuMDAxLCAwLjMpCiAgfSBlbHNlIHsKICAgIHlsYWJlbCA8LSBleHByZXNzaW9uKCJZIFtnRENXIGdTIl4tMSoiXSIpCiAgICB5bGltIDwtIGMoMC4wMDEsIDAuMzk5KQogIH0KICAKICAKICB4eXBsb3QodmFsdWUgfiBmYWN0b3IoQ08yX3JlZml4YXRpb24pLAogICAgZmlsdGVyKGRmX21peG1hY3IsIGtleSA9PSBrZXlzLCBjYXJib25fY29uYyA9PSAxLjI1KSwKICAgIHR5cGUgPSAiYiIsIGx3ZCA9IDIsIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksIAogICAgeGxhYiA9IGV4cHJlc3Npb24oIkNPIlsyXSoiIHVwdGFrZSBbbW1vbCBoIl4tMSoiIGdEQ1ciXi0xKiJdIiksCiAgICB5bGFiID0geWxhYmVsLCB5bGltID0geWxpbSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgeiwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIC4uLikKICAgIH0KICApCn0pCgpwcmludChwbG90X21peG9fbXVbWzFdXSwgcG9zaXRpb24gPSBjKDAsMCwwLjUzLDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9taXhvX211W1syXV0sIHBvc2l0aW9uID0gYygwLjQ3LDAsMSwxKSkKYGBgClRoZXJlIGlzIG5vIGluY3JlYXNlIGluIGdyb3d0aCByYXRlIG9yIHlpZWxkIHdpdGggYWRkaXRpb25hbCBDTzxzdWI+Mjwvc3ViPiBmaXhhdGlvbiBhY2NvcmRpbmcgdG8gdGhlIFJCQSBtb2RlbC4gVGhlIHlpZWxkIGlzIGluIGZhY3QgY2FsY3VsYXRlZCBiYXNlZCBvbiB0aGUgZ3Jvd3RoIHJhdGUgwrUgYW5kIHRoZSBzdWJzdHJhdGUgdXB0YWtlIHJhdGUgcVMuIFlpZWxkIGFuZCBncm93dGggcmF0ZSBkZXBlbmQgb24gZWFjaCBvdGhlciBpbiB0aGUgcmVsYXRpb24gWSBbZ0RDVy9nU10gPSDCtSBbaC0xXSAvIHFTIFtnUyBnRENXLTEgaC0xXS4gCgpTaW5jZSBvbmx5IHRoZSBpbml0aWFsIHN1YnN0cmF0ZSBjb25jZW50cmF0aW9uIGlzIGNvbnN0cmFpbmVkLCB0aGUgbW9kZWwgY291bGQgcHJlZGljdCBhIGxvd2VyIHN1YnN0cmF0ZSB1cHRha2UgcmF0ZSBvciBsb3dlciBDTzxzdWI+Mjwvc3ViPiBlbWlzc2lvbiBwZXIgYmlvbWFzcyBpbiBvcmRlciB0byByZWFjaCBhIHlpZWxkIGluY3JlYXNlLiBIb3dldmVyIHRoaXMgd2FzIG5vdCB0aGUgY2FzZSB1bmRlciBhbnkgc2ltdWxhdGlvbiwgc2VlIGJlbG93IGZvciBkZXRhaWxzIG9mIHNwZWNpZmljIG1ldGFib2xpdGVzLiAKClRoZSBtb2RlbCBzZWVtcyB0byBwcmVkaWN0IGEgaGlnaC15aWVsZCBwaGVub3R5cGUgYWxyZWFkeS4gSWYgQ088c3ViPjI8L3N1Yj4gcmUtZml4YXRpb24gd291bGQgaGF2ZSBoYWQgYW4gYWR2YW50YWdlIGZvciBncm93dGgsIGl0IG1pZ2h0IGhhdmUgYmVlbiBkZXRlY3RlZCBlYXJsaWVyIGRlcGVuZGluZyBvbiB0aGUgcHJvdGVpbiBjb3N0cyBmb3IgdGhlIHJlc3BlY3RpdmUgcGF0aHdheS4gQmVmb3JlIGEgZmluYWwgdmVyZGljdCwgd2UgY2FuIGZvbGxvdyB0aGUgZmF0ZSBvZiB0aGUgZml4ZWQgQ088c3ViPjI8L3N1Yj4gdGhyb3VnaCB0aGUgbWV0YWJvbGlzbS4KCgpgYGB7ciwgZmlnLndpZHRoID0gMy44LCBmaWcuaGVpZ2h0ID0gMy44fQpwbG90X21peG9fZW56IDwtIHh5cGxvdChhYnModmFsdWUpIH4gYXMuZmFjdG9yKENPMl9yZWZpeGF0aW9uKSB8IAogICAgZmFjdG9yKGtleSwgdW5pcXVlKGtleSkpLCBsYXlvdXQgPSBjKDIsIDIpLAogIGZpbHRlcihkZl9taXhmbHV4LCBrZXkgJWluJSBjKCJDTzJ0IiwgIkVERCIsICJDUyIsICJPMnQiKSwgY2FyYm9uX2NvbmMgPT0gMS4yNSksCiAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICB0eXBlID0gImIiLCBsd2QgPSAyLCBiZXR3ZWVuID0gbGlzdCh4ID0gMC41LCB5ID0gMC41KSwgYXMudGFibGUgPSBUUlVFLAogIHhsYWIgPSBleHByZXNzaW9uKCJDTyJbMl0qIiB1cHRha2UgW21tb2wgaCJeLTEqIiBnRENXIl4tMSoiXSIpLAogIHlsYWIgPSBleHByZXNzaW9uKCJmbHV4IFttbW9sIGgiXi0xKiJnRENXIl4tMSoiXSIpLAogIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSksCiAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCB6LCAuLi4pIHsKICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgIHBhbmVsLnh5cGxvdCh4LCB5LCAuLi4pCiAgfQopCgpwcmludChwbG90X21peG9fZW56KQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA3LjUsIGZpZy5oZWlnaHQgPSAzLjcsIGluY2x1ZGUgPSBGQUxTRX0Kc3ZnKCIuLi9maWd1cmVzL2ZpZ3VyZV9yZWZpeGF0aW9uLnN2ZyIsIHdpZHRoID0gNy41LCBoZWlnaHQgPSAzLjcpCnByaW50KHBsb3RfbWl4b19tdVtbMV1dLCBwb3NpdGlvbiA9IGMoMC4yOSwwLjQyLDAuNiwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbWl4b19tdVtbMl1dLCBwb3NpdGlvbiA9IGMoMC4zMCwwLDAuNiwwLjYzKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbWl4b19lbnosIHBvc2l0aW9uID0gYygwLjU2LDAsMS4wMywxLjA1KSkKZGV2Lm9mZigpCmBgYAoKIyMgQ29uY2x1c2lvbgoKSXQgYmVjb21lcyBjbGVhciB0aGF0IHlpZWxkIGFuZCBncm93dGggcmF0ZSBkZWNyZWFzZSwgYW5kIG5vdCBpbmNyZWFzZSwgd2l0aCBhZGRpdGlvbmFsIENPMiBmaXhhdGlvbiwgYmVjYXVzZToKCi0gZW5lcmd5IHJlcXVpcmVtZW50IGZvciBDTzIgZml4YXRpb24gbGVhZHMgdG8gbG93ZXIgZmx1eCB0aHJvdWdoIEVEIHBhdGh3YXksIGJ1dCBoaWdoZXIgZmx1eCB0aHJvdWdoIFRDQQotIHRoaXMgaXMgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgdGhlIHJlcXVpcmVkIE5BREgvQVRQIGZvciBDTzIgZml4YXRpb24KLSByZXNwaXJhdGlvbiBhbmQgTzIgY29uc3VtcHRpb24gYWxzbyBpbmNyZWFzZXMgd2l0aCBmb3JjZWQgbWl4b3Ryb3BoaWMgZ3Jvd3RoCi0gdGhlcmUgaXMgbm8gbmV0IHJlZHVjdGlvbiBvZiBDTzIgZW1pc3Npb24uIEluIGZhY3QgY2VsbHMgZW1pdCBtb3JlIENPMiBldmVuIHdoZW4gdGhleSBmaXggc29tZSBvZiBpdCBkdWUgdG8gaW5jcmVhc2VkIGVuZXJneSByZXF1aXJlbWVudAotIHN0b3Jpbmcgc29tZSBvZiB0aGUgZml4ZWQgQ08yIGFzIFBIQiB3b3VsZCBub3QgaW5jcmVhc2UgYmlvbWFzcyB5aWVsZCBhcyBhZGRpdGlvbmFsIGVuZXJneSByZXF1aXJlbWVudCBzdGlsbCBwZXJzaXN0cwotIGNlbGxzIHNob3VsZCBub3QgYmUgYWJsZSB0byBtYWtlIG1vcmUgUEhCIHVzaW5nIHRoaXMgc3RyYXRlZ3ksIHJlZ2FyZGxlc3Mgb2YgYmlvbWFzcyB5aWVsZC4gSWYgYWNldHlsLUNvQSBpcyBkcmFpbmVkIGZvciBQSEIsIGV2ZW4gbGVzcyBlbmVyZ3kgaXMgbWFkZSBhdmFpbGFibGUgdGhyb3VnaCBUQ0EgYW5kIE9YUEhPUy4KCiMgTWluaWF0dXJlIFJCQSBmbHV4IG1hcHMKCkluc3RhbGwgbXkgc21hbGwgUiBwYWNrYWdlIFtmbHVjdHVhdG9yXShodHRwczovL2dpdGh1Yi5jb20vbS1qYWhuL2ZsdWN0dWF0b3IpIHRvIG92ZXJsYXkgZmx1eGVzIG9uIFNWRyBtZXRhYm9saWMgbWFwcy4KVGhlIHRlbXBsYXRlIG1hcCBpcyBhIHNpbXBsaWZpZWQgbWV0YWJvbGljIG1hcCBvZiBDLiBuZWNhdG9yJ3MgY2VudHJhbCBtZXRhYm9saXNtLgoKRmlyc3Qgd2UgcGxvdCBhIG1hcCBvZiB0aGUgbWl4b3Ryb3BoaWMgc2ltdWxhdGlvbi4gVGhlbiB3ZSB1c2UgdGhlIHNhbWUgd29yay1mbG93IGFuZCB0ZW1wbGF0ZSB0byBwbG90IG1ldGFib2xpYyBmbHV4IG1hcHMgZm9yIGdyb3d0aCBvbiB0aGUgdGhyZWUgbWFpbiBzdWJzdHJhdGVzLCBmcnVjdG9zZSwgc3VjY2luYXRlIGFuZCBmb3JtYXRlIChtYXhpbXVtIHRlc3RlZCBncm93dGggcmF0ZSwgMC4yNSBoXi0xKS4KCmBgYHtyfQpsaWJyYXJ5KGZsdWN0dWF0b3IpCgojIGltcG9ydCBtYXAKU1ZHX21hcCA8LSByZWFkX3N2ZygiLi4vZGF0YS9zaW11bGF0aW9uL2NlbnRyYWxfbWV0YWJvbGlzbS5zdmciKQojZ2V0X2F0dHJpYnV0ZXMoU1ZHX21hcCwgbm9kZSA9ICJrZXlfMDEiKSAlPiUgcHVsbChzdHlsZSkKCiMgcHJlcGFyZSBkYXRhOiBtaXhvdHJvcGhpYyBncm93dGgKZGZfbWl4Zmx1eCA8LSBkZl9taXhmbHV4ICU+JSBmaWx0ZXIoQ08yX3JlZml4YXRpb24gPT0gMykgJT4lCiAgIyBhZGQgZmx1eGVzIGZvciBsZWdlbmQgZW50cmllcwogIGFkZF9yb3coa2V5ID0gYygia2V5XzAxIiwgImtleV8xMiIsICJrZXlfMjQiLCAia2V5XzQ4IiksIHZhbHVlID0gYygxLDIsNCw4KSkgJT4lCiAgIyBhZGQgc3Ryb2tlIHdpZHRoIHRvIHNpbXVsYXRpb24KICBtdXRhdGUoc3Ryb2tlX3dpZHRoID0gMC4yICsgMC4yKnNxcnQoYWJzKHZhbHVlKSkpICU+JQogIGlubmVyX2pvaW4oc2VsZWN0KFNWR19tYXBAc3VtbWFyeSwgbGFiZWwpLCBieSA9IGMoImtleSIgPSAibGFiZWwiKSkgJT4lCiAgZGlzdGluY3QKCiMgcHJlcGFyZSBkYXRhOiBjYXJib24tbGltaXRlZCBncm93dGgKZGZfY2FyYmZsdXggPC0gZGZfZmx1eCAlPiUKICBmaWx0ZXIoc2ltdWxhdGlvbiAlaW4lIGMoCiAgICAiZm9yXzAuNDg3X25oNF8xOC43XzEwIiwgImZydV8xLjIyM19uaDRfMTguN18wIiwgInN1Y2NfMC42NjJfbmg0XzE4LjdfNSIpKSAlPiUKICAjIGFkZCBmbHV4ZXMgZm9yIGxlZ2VuZCBlbnRyaWVzCiAgYWRkX3JvdyhrZXkgPSByZXAoYygia2V5XzAxIiwgImtleV8xMiIsICJrZXlfMjQiLCAia2V5XzQ4IiksIGVhY2ggPSAzKSwKICAgIHZhbHVlID0gcmVwKGMoMSwyLDQsOCksIGVhY2ggPSAzKSwgY2FyYm9uX3NvdXJjZSA9IHJlcChjKCJmb3IiLCAiZnJ1IiwgInN1Y2MiKSwgNCkpICU+JQogICMgYWRkIHN0cm9rZSB3aWR0aCB0byBzaW11bGF0aW9uCiAgbXV0YXRlKHN0cm9rZV93aWR0aCA9IDAuMiArIDAuMipzcXJ0KGFicyh2YWx1ZSkpKSAlPiUKICBpbm5lcl9qb2luKHNlbGVjdChTVkdfbWFwQHN1bW1hcnksIGxhYmVsKSwgYnkgPSBjKCJrZXkiID0gImxhYmVsIikpICU+JQogIGRpc3RpbmN0CmBgYAoKUGxvdCBhbGwgZm91ciBjb25kaXRpb25zIGluIGEgbG9vcC4KCmBgYHtyfQpsYXBwbHkoCiAgbGlzdCgKICAgIG11dGF0ZShkZl9taXhmbHV4LCBjYXJib25fc291cmNlID0gIm1peCIpLAogICAgZmlsdGVyKGRmX2NhcmJmbHV4LCBjYXJib25fc291cmNlID09ICJmcnUiKSwKICAgIGZpbHRlcihkZl9jYXJiZmx1eCwgY2FyYm9uX3NvdXJjZSA9PSAiZm9yIiksCiAgICBmaWx0ZXIoZGZfY2FyYmZsdXgsIGNhcmJvbl9zb3VyY2UgPT0gInN1Y2MiKQogICksIEZVTiA9IGZ1bmN0aW9uKGRmKSB7CiAgICAKICAgICMgcmVsb2FkIGJhc2UgbWFwIGZvciBldmVyeSBpbnN0YW5jZSwgYXMgdGhlIFNWRyBpcyBhbHRlcmVkCiAgICAjIGV2ZW4gd2l0aG91dCBhc3NpZ25tZW50IChweXRob24gc3R5bGUsIHVudHlwaWNhbCBpbiBSKQogICAgU1ZHX21hcCA8LSByZWFkX3N2ZygiLi4vZGF0YS9zaW11bGF0aW9uL2NlbnRyYWxfbWV0YWJvbGlzbS5zdmciKQogICAgCiAgICAjIG1vZGlmeSBtYXAKICAgIFNWRzIgPC0gc2V0X2F0dHJpYnV0ZXMoU1ZHX21hcCwKICAgICAgbm9kZSA9IGRmJGtleSwgYXR0ciA9ICJzdHlsZSIsCiAgICAgIHBhdHRlcm4gPSAic3Ryb2tlLXdpZHRoOlswLTldK1xcLlswLTldKyIsCiAgICAgIHJlcGxhY2VtZW50ID0gcGFzdGUwKCJzdHJva2Utd2lkdGg6IiwgZGYkc3Ryb2tlX3dpZHRoKSkKICAgIAogICAgIyB0dXJuIG5vbi16ZXJvIGZsdXhlcyBpbnRvIGRhcmtlciBjb2xvcgogICAgU1ZHMiA8LSBzZXRfYXR0cmlidXRlcyhTVkcyLAogICAgICBub2RlID0gZmlsdGVyKGRmLCB2YWx1ZSAhPSAwKSAlPiUgcHVsbChrZXkpLCBhdHRyID0gInN0eWxlIiwKICAgICAgcGF0dGVybiA9ICJzdHJva2U6I2IzYjNiMyIsCiAgICAgIHJlcGxhY2VtZW50ID0gInN0cm9rZTojODA4MDgwIikKICAgIAogICAgIyByZXNjYWxlIGFycm93IGhlYWRzCiAgICBTVkcyIDwtIHNldF9hdHRyaWJ1dGVzKFNWRzIsIAogICAgICBub2RlID0gZ3JlcCgibWFya2VyIiwgU1ZHMkBzdW1tYXJ5JGlkLCB2YWx1ZSA9IFRSVUUpLAogICAgICBub2RlX2F0dHIgPSAiaWQiLAogICAgICBhdHRyID0gInRyYW5zZm9ybSIsCiAgICAgIHBhdHRlcm4gPSAic2NhbGVcXCgwLjJcXCkiLAogICAgICByZXBsYWNlbWVudCA9ICJzY2FsZSgwLjE1KSIpCiAgICAKICAgIFNWRzIgPC0gc2V0X2F0dHJpYnV0ZXMoU1ZHMiwgCiAgICAgIG5vZGUgPSBncmVwKCJtYXJrZXIiLCBTVkcyQHN1bW1hcnkkaWQsIHZhbHVlID0gVFJVRSksCiAgICAgIG5vZGVfYXR0ciA9ICJpZCIsCiAgICAgIGF0dHIgPSAidHJhbnNmb3JtIiwKICAgICAgcGF0dGVybiA9ICJzY2FsZVxcKC0wLjJcXCkiLAogICAgICByZXBsYWNlbWVudCA9ICJzY2FsZSgtMC4xNSkiKQogICAgCiAgICAjIGV4cG9ydCBtYXAgYXMgU1ZHIGFnYWluCiAgICAjd3JpdGVfc3ZnKFNWRzIsIGZpbGUgPSBwYXN0ZTAoIi4uL2RhdGEvc2ltdWxhdGlvbi9jZW50cmFsX21ldGFib2xpc21fIiwgZGYkY2FyYm9uX3NvdXJjZVsxXSwgIi5zdmciKSkKICB9CikgJT4lIGludmlzaWJsZSgpCgpgYGAKCk1ldGFib2xpYyBmbHV4IHdpdGggQ08yIHJlZml4YXRpb24gfCAgTWV0YWJvbGljIGZsdXggb24gZnJ1Y3Rvc2UKOi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tOgohW10oLi4vZGF0YS9zaW11bGF0aW9uL2NlbnRyYWxfbWV0YWJvbGlzbV9taXguc3ZnKSAgfCAgIVtdKC4uL2RhdGEvc2ltdWxhdGlvbi9jZW50cmFsX21ldGFib2xpc21fZnJ1LnN2ZykKCk1ldGFib2xpYyBmbHV4IG9uIHN1Y2NpbmF0ZSB8ICBNZXRhYm9saWMgZmx1eCBvbiBmb3JtaWMgYWNpZAo6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS06CiFbXSguLi9kYXRhL3NpbXVsYXRpb24vY2VudHJhbF9tZXRhYm9saXNtX3N1Y2Muc3ZnKSAgfCAgIVtdKC4uL2RhdGEvc2ltdWxhdGlvbi9jZW50cmFsX21ldGFib2xpc21fZm9yLnN2ZykKCg==