1 Description

This R notebook is a bioinformatics pipeline to analyze fitness data obtained from a barcoded transposon library in Ralstonia eutropha a.k.a. Cupriavidus necator. For background and details regarding the method, see Wetmore at al., mBio, 2015 and Price et al., Nature, 2018).

2 Libraries

# optionally install repos from github
# devtools::install_github("m-jahn/lattice-tools")
# devtools::install_github("m-jahn/R-tools")
library(lattice)
library(latticeExtra)
library(latticetools)
library(tidyverse)
library(dendextend)
library(Rtools)
library(colorspace)
library(stringi)

3 Overview of barcode/transposon read counts

3.1 Data import and processing

Read in the main data tables with A) reads per barcode and sample (‘pool counts’), and B) the fitness tables. Tables were obtained by processing sequencing data with a custom BarSeq pipeline. The 32 generation sequencing samples are removed due to the low read count in the continuous samples.

# import barseq counts data in wide format and reshape to long format
df_counts_frc <- read_tsv("../../../rebar/data/20201222_barseq_frc/results/result.poolcount") %>%
  select(!matches("32gen|_32_")) %>%
  pivot_longer(
    cols = !all_of(c("barcode", "rcbarcode", "scaffold", "strand", "pos")), 
    names_to = "sample", values_to = "n_reads")

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  barcode = col_character(),
  rcbarcode = col_character(),
  scaffold = col_character(),
  strand = col_character()
)
ℹ Use `spec()` for the full column specifications.
df_counts_suc <- read_tsv("../../../rebar/data/20210407_barseq_suc_for/results/result.poolcount") %>%
  pivot_longer(
    cols = !all_of(c("barcode", "rcbarcode", "scaffold", "strand", "pos")), 
    names_to = "sample", values_to = "n_reads")

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  barcode = col_character(),
  rcbarcode = col_character(),
  scaffold = col_character(),
  strand = col_character()
)
ℹ Use `spec()` for the full column specifications.
# merge barcode counts tables
df_counts <- bind_rows(df_counts_frc, df_counts_suc)

# import fitness data, the final output of the BarSeq pipeline
load("../../../rebar/data/20201222_barseq_frc/results/fitness_gene.Rdata")
df_fitness_frc <- fitness_gene %>%
  filter(condition != "long pulse", time != 32) %>%
  mutate(ID = as.numeric(ID), substrate = "fructose", 
    condition = str_remove(condition, "short "))

load("../../../rebar/data/20210407_barseq_suc_for/results/fitness_gene.Rdata")
df_fitness_suc <- fitness_gene %>%
  separate(condition, sep = "_", into = c("substrate", "condition"))

# merge fitness tables
df_fitness <- bind_rows(df_fitness_frc, df_fitness_suc) %>%
  rename(locus_tag = locusId)
rm("df_fitness_frc", "df_fitness_suc", "df_counts_frc", "df_counts_suc")

# import genome annotation
df_ref <- read_csv("../data/ref/Ralstonia_H16_genome_annotation.csv") %>%
  filter(!duplicated(locus_tag)) %>%
  mutate(eggNOG_name = if_else(is.na(eggNOG_name), gene_name, eggNOG_name))

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  length = col_double(),
  start = col_double(),
  end = col_double(),
  feature_interval_length = col_double(),
  Psortb_score = col_double()
)
ℹ Use `spec()` for the full column specifications.
# define standard colors
stdcol <- custom.colorblind()$superpose.line$col

3.2 Summary statistics

Overview about the number of reads per barcode, barcodes per gene and so on. Around 8-10 M reads were mapped on average, per sample.

# Number of total mapped reads
df_counts %>% group_by(sample) %>%
  summarize(n_million_reads = sum(n_reads)/10^6) %>%
  barchart(factor(sample) ~ n_million_reads, .,
    par.settings = custom.colorblind(),
    horizontal = TRUE, border = NULL,
    scales = list(y = list(cex = 0.7)),
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.barchart(x, y, ...)
    }
  )

Distribution of number of reads per barcode. There is a sufficient number of reads for quantification, on average log2(n) = 5 = 32 reads per barcode.

plot_reads_per_bc <- histogram(~ log2(n_reads) | sample,
  df_counts, as.table = TRUE, layout = c(8,6),
  par.settings = custom.colorblind(),
  between = list(x = 0.5, y = 0.5),
  xlab = expression("log"[2]*" reads per barcode"),
  scales = list(alternating = FALSE),
  panel = function(x, ...) {
    panel.grid(h = -1, v = -1, col = grey(0.9))
    panel.histogram(x, border = "white", ...)
  }
)

print(plot_reads_per_bc)

Similarly to the above, this is an overview about the number of barcodes per gene as a histogram. This distribution is the same for all conditions and replicates. The second plot is the number of reads per gene, averaged as median over all conditions (excluding 0 time point where counts were averaged by BarSeq pipeline). The average reads per gene are log2(n) = 8, which translates to n = 2^8 or around 256 reads per gene.

plot_reads_per_bc <- histogram(~ strains_per_gene,
  df_fitness %>% select(locus_tag, strains_per_gene) %>% 
    distinct %>% filter(strains_per_gene < 40), 
  par.settings = custom.colorblind(), breaks = 20, xlim=c(-2, 42),
  xlab = expression("mutants per gene"),
  panel = function(x, ...) {
    panel.grid(h = -1, v = -1, col = grey(0.9))
    panel.histogram(x, border = "white", ...)
    panel.ablineq(v = mean(x, na.rm = TRUE),
      fontfamily = "FreeSans", col = grey(0.5), lwd = 2, lty = 2)
  }
)
Adding missing grouping variables: `scaffold`, `date`, `time`, `ID`, `condition`, `replicate`
plot_reads_per_gene <- histogram(~ log2(reads_per_gene_median),
  df_fitness %>% filter(time != 0) %>% group_by(locus_tag) %>%
    summarize(reads_per_gene_median = median(counts)),
  par.settings = custom.colorblind(), breaks = 20,
  xlab = expression("log"[2]*" reads per gene (med)"),
  panel = function(x, ...) {
    panel.grid(h = -1, v = -1, col = grey(0.9))
    panel.histogram(x, border = "white", ...)
    panel.ablineq(v = mean(x[!is.infinite(x)]),
      fontfamily = "FreeSans", col = grey(0.5), lwd = 2, lty = 2)
  }
)

print(plot_reads_per_bc, split = c(1,1,2,1), more = TRUE)
print(plot_reads_per_gene, split = c(2,1,2,1))

4 Gene fitness analysis

4.1 Depletion over time (generations)

We can plot log2 FC or normalized gene fitness over generations. For this type of overview it is best to summarize individual replicates (4x) to the mean or median, per time point and condition. We also add genome annotation to the summary table. The plots shows that depletion of some strains is so strong already at 8 generations that fitness/log2 FC could not be quantified for 16 generations due to missing read counts. It is best to focus on 8 generations as because it provides a more complete picture.

df_fitness_summary <- df_fitness %>%
  group_by(locus_tag, scaffold, time, condition, substrate, strains_per_gene) %>%
  summarize(
    norm_gene_fitness_median = median(norm_gene_fitness, na.rm = TRUE),
    log2FC_median = median(log2FC, na.rm = TRUE),
    tstat_median = median(t, na.rm = TRUE)
  ) %>%
  left_join(df_ref)
`summarise()` has grouped output by 'locus_tag', 'scaffold', 'time', 'condition', 'substrate'. You can override using the `.groups` argument.
Joining, by = "locus_tag"
plot_hist_log2FC <- df_fitness_summary %>%
  filter(all(!is.infinite(log2FC_median))) %>%
  group_by(condition, substrate) %>%
  slice(1:2000) %>%
  
  xyplot(log2FC_median ~ time | substrate * condition, .,
    groups = locus_tag, as.table = TRUE,
    col = stdcol[1], alpha = 0.2, layout = c(3, 2),
    xlab = "", ylab = expression("log"[2]*" FC"),
    par.settings = custom.colorblind(), type = c("l"),
    between = list(x = 0.5, y = 0.5),
    scales = list(alternating = FALSE), lwd = 2,
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.xyplot(x, y, ...)
    }
  )

plot_hist_normfg <- df_fitness_summary %>%
  filter(all(!is.infinite(log2FC_median))) %>%
  group_by(condition, substrate) %>%
  slice(1:2000) %>%
  
  xyplot(norm_gene_fitness_median ~ time | substrate * condition, .,
    groups = locus_tag, as.table = TRUE,
    col = stdcol[2], alpha = 0.2, layout = c(3, 2),
    xlab = "generations", ylab = "fitness",
    par.settings = custom.colorblind(), type = c("l"),
    between = list(x = 0.5, y = 0.5),
    scales = list(alternating = FALSE), lwd = 2,
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.xyplot(x, y, ...)
    }
  )

print(plot_hist_log2FC, position = c(0,0.47,1,1), more = TRUE)
print(plot_hist_normfg, position = c(0,0,1,0.53))

As sort of an internal control, we compare the gene fitness obtained by a complex procedure to the log2 FC of read counts, which is a very simple measure of ‘fitness’. The two variables correlate well all tested conditions and substrates.

df_fitness_summary %>%
  filter(time == 8, all(!is.infinite(log2FC_median))) %>%
  
  xyplot(norm_gene_fitness_median ~ log2FC_median |  substrate * condition, .,
    as.table = TRUE, col = stdcol[5], pch = 19,
    alpha = 0.4, cex = 0.6,
    layout = c(3, 2), xlim = c(-8,3), ylim = c(-6,5),
    xlab = expression("log"[2]*" FC"), ylab = "fitness",
    par.settings = custom.colorblind(),
    between = list(x = 0.5, y = 0.5),
    scales = list(alternating = FALSE),
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.xyplot(x, y, ...)
      panel.lmlineq(x, y, fontfamily = "FreeSans", r.squared = TRUE, lwd = 1.5, 
        col.text = 1, pos = 3, offset = 5, ...)
      panel.abline(a = 2, b = 1, col = grey(0.5), lty = 2, lwd = 1.5)
    }
  )

4.2 Comparing gene fitness between conditions

Similarly to Figure 2 in Wetmore et al, mBIO, 2015, we can investigate condition-dependent gene fitness by comparing conditions and substrates one-on-one. The correlation between substrates and growth regimes (continuous, pulsed) is quite different. The strongest correlation does exist for the pulsed vs continuous regime for each substrate (R = 0.64 to 0.87). And for the pulsed conditions, comparison between substrates (R = 0.62 to 0.72).

df_fitness_comp <- df_fitness_summary %>% filter(time == 8) %>%
  group_by(locus_tag) %>% mutate(tstat_median = min(tstat_median)) %>%
  select(locus_tag, condition, substrate, norm_gene_fitness_median, gene_name, COG_Process, tstat_median) %>%
  unite(condition, condition, substrate) %>%
  pivot_wider(names_from = condition, values_from = norm_gene_fitness_median) %>%
  filter(!is.na(locus_tag))

custom_splom(df_fitness_comp %>% ungroup %>% 
  select(matches("conti|pulse")), col = grey(0.4, 0.4))

We can also compare selected conditions directly to identify genes enriched or depleted in several conditions.

# generalized plotting function
plot_fitness_comp <- function(data, vars){
  xyplot(get(vars[2]) ~ get(vars[1]), data,
    groups = abs(get(vars[1])-get(vars[2])) > 2, #tstat_median < -3
    par.settings = custom.colorblind(), col = stdcol[c(5,1)],
    pch = 19, alpha = 0.5, cex = 0.7, aspect = 1,
    xlim = c(-7, 4), ylim = c(-7, 4),
    xlab = vars[1], ylab = vars[2],
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.abline(v = 0, h = 0, col = grey(0.5), lty = 2, lwd = 1.5)
      panel.abline(a = 0, b = 1, col = grey(0.5), lty = 2, lwd = 1.5)
      panel.abline(a = 2, b = 1, col = grey(0.5), lty = 2, lwd = 1.5)
      panel.abline(a = -2, b = 1, col = grey(0.5), lty = 2, lwd = 1.5)
      panel.xyplot(x, y, ...)
      panel.key(...)
    }
  )
}

plot_fitness_comp(df_fitness_comp, c("continuous_formate", "pulse_formate")) %>%
  print(position = c(0,0.5,0.5,1), more = TRUE)
plot_fitness_comp(df_fitness_comp, c("continuous_fructose", "pulse_fructose")) %>%
  print(position = c(0.5,0.5,1,1), more = TRUE)
plot_fitness_comp(df_fitness_comp, c("continuous_succinate", "pulse_succinate")) %>%
  print(position = c(0,0,0.5,0.5), more = TRUE)
plot_fitness_comp(df_fitness_comp, c("continuous_formate", "continuous_fructose")) %>%
  print(position = c(0.5,0,1,0.5), more = TRUE)

5 Differential fitness of selected gene sets

Most genes correlate in fitness value between conditions. That means, only some genes have a condition-specific fitness effect, i.e. increase or decrease fitness of the respective strain in one substrate or growth regime specifically. This section investigates specific genes and their functions that show such differential fitness between conditions. A simple comparison of one condition versus another will not be helpful as there two many possible combinations. To identify interesting sets of genes, cluster analysis can be performed.

# generate colorpalette for heatmap
heat_cols <- diverging_hcl(n = 7, h = c(255, 12), c = c(50, 80), l = c(20, 97), power = c(1, 1.3))
#heat_cols <- diverging_hcl(n = 7, h = c(340, 128), c = c(60, 80), l = c(30, 97), power = c(0.8, 1.5))
#heat_cols <- diverging_hcl(n = 7, h = c(300, 128), c = c(30, 65), l = c(20, 95), power = c(1, 1.4))

# create a matrix from wide fitness data for plotting heatmap
mat_heatmap <- df_fitness_comp %>% ungroup %>%
  filter(if_any(.cols = matches("conti|pulse"), ~ !between(., -2, 2))) %>%
  select(matches("locus_tag|conti|pulse")) %>%
  # filter out NA rows, and replace extreme values
  drop_na %>% mutate(across(matches("conti|pulse"), 
    function(x) {y = replace(x, x > 6, 6); replace(y, y < -6, -6)})) %>%
  column_to_rownames(var = "locus_tag") %>%
  as.matrix

# create cluster for reordering
mat_cluster <- mat_heatmap %>% dist %>% hclust(method = "ward.D")
mat_heatmap <- mat_heatmap[order.dendrogram(as.dendrogram(mat_cluster)), c(1,4,2,5,3,6)]

# plot heatmap
plot_heatmap <- levelplot(mat_heatmap,
  par.settings = custom.colorblind(),
  col.regions = colorRampPalette(heat_cols)(16),
  at = seq(-6, 6, 1), aspect = "fill",
  xlab = "", ylab = "", scales = list(x = list(draw = FALSE)),
  panel = function(x, y, z, ...) {
    panel.levelplot(x, y, z, ...)
    panel.abline(h = 1:5+0.5, col = "white", lwd = 1.5)
  }
)

print(plot_heatmap)

Clustering and silhouette analysis revealed that we have 4 to 7 clusters with acceptable separation, see the plot below. Of these, several clusters stick out:

  1. mutants specifically depleted in formate conditions = essential for formate
  2. mutants depleted in all conditions = essential in minimal medium
  3. mutants enriched in fructose growth, neutral on other substrates
  4. mutants enriched in continuous regime, mostly neutral in pulsed regime
  5. mutants slightly depleted in continuous succinate - low priority
  6. mutants slightly depleted in continuous fructose - low priority
  7. mutants slightly depleted in various conditions - low priority
silhouetteResult <- Rtools::silhouette_analysis(mat_heatmap, mat_cluster, 2:20)

Call:
hclust(d = ., method = "ward.D")

Cluster method   : ward.D 
Distance         : euclidean 
Number of objects: 310 

Silhouette analysis finished for clusters 2 to 20
silhouetteResult$plot.summary


# plot colored dendrogram
plot(color_branches(
  mat_cluster, k = 7,
  groupLabels = TRUE,
  col = stdcol[1:7]
))


# collect names of enriched/depleted genes
list_enriched <- vegan::cutreeord(mat_cluster, k = 7)
Registered S3 method overwritten by 'vegan':
  method     from      
  rev.hclust dendextend
list_enriched %>% table
.
 1  2  3  4  5  6  7 
34 40 14 27 80 67 48 

5.1 Enrichment of mutants over time (increased fitness)

One of the most puzzling results is the appearance of (very) fast growing mutants in some conditions. These mutants quickly enrich over only 16 generations to a volume of up to 20% of all library mutants, they “take over the culture”. This phenomenon was not observed previously in experiments with the Synechocystis sp. PCC6803 CRISPRi repression library (Yao et al., Nature Communications, 2020). The CRISPRi library is based on the same principle of depletion and enrichment of mutants depending on the associated fitness.

5.1.0.1 Strains enriched on fructose

What is the identity of the enriched genes? Which ones are enriched in several conditions? First we identify the mutants highly and specifically enriched on fructose, predominantly in the continuous growth regime.

df_fitness_summary %>%
  filter(locus_tag %in% names(subset(list_enriched, list_enriched == 3))) %>%
  filter(time == 8) %>%
  barchart_fitness() %>%
  print(position = c(0,0,0.6,1), more = TRUE)

df_fitness_summary %>%
  filter(locus_tag %in% names(subset(list_enriched, list_enriched == 3))) %>%
  linechart_fitness() %>%
  print(position = c(0.58,0,1,1))

Mechanisms enabling faster growth on fructose

The mechanism for increased growth on fructose seems clearly different from the mechanism for faster growth in all other (mainly continuous) conditions. For the fructose set, several genes are functionally linked suggesting a similar mechanism of action,

  1. Genes H16_B0517 (alcohol dehydrogenase) and H16_B1917 (aldehyde dehydrogenase): According to STRING DB, the genes are co-located i.e. either in direct proximity or in the same operon in alpha-, beta- and gamma-proteobacteria, and even cyanobcateria. BioCyc has 17 possible iso-enzymes in Cupriavidus for the 2-enzyme pathway “ethanol degradation 1”: ethanol –> acetaldehyde –> acetyl-CoA (reverse EtOH fermentation, obtain 1 NADH per reaction) catalyzed by both genes. What could be the mechanism for improved growth/yield on fructose? Chemostats are more yield- than growth selective, this pathway is probably functional and secretes EtOH as a fermentation product; KO of this pathway probably increases yield.

  2. Two genes are involved in nitrate respiration/reduction, PHG269 or narK, and H16_B2087, a NarL-family response regulator. NarK is a nitrate transporter, and NarL senses nitrate and then activates transcription of its targets by binding the DNA. According to this paper on NarL by Ruanto et al., 2020, “the Regulon DB database [11] for transcription regulation in E. coli lists 26 gene regulatory regions where NarL has a direct effect on transcript initiation, and it functions as an activator at 11 of these”. It is not clear what effect the KO of nitrate transport could have on growth or yield, nitrate is not part of the medium.

  3. Genes involved in translation/protein folding/sulphur metabolism:

    • H16_B2521, Alpha-ketoglutarate-dependent taurine dioxygenase; taurine is a sulphur carrier
    • H16_A2861, Glutathione S-transferase; Posttranslational modification, protein turnover, chaperones
    • H16_A1138, Thioredoxin; Posttranslational modification, protein turnover, chaperones
    • H16_A1336, yogA/tdcF, putative translation initiation inhibitor,yjg Ffamily; Translation, ribosomal structure and biogenesis
    • H16_A2553, cO, DNA repair protein RecO; Involved in DNA repair and RecF pathway recombination
  4. Several other gene products are presumably regulatory proteins:

    • 16_A0310, transcriptional regulator, GntR-family; Transcription
    • H16_B2215, HTH lysR-type domain-containing protein; helix turn helix DNA binding motif

5.1.0.2 Strains enriched on formate

Here, the obvious question with biotechnological importance is to find mutants with higher (specific) tolerance to formate, regardless if it’s pulsed or continuously supplied. We explicitly exclude mutants beneficial for other growth conditions too, which are dealt with in the next section. The clustering results of the previous section could not identify formate-specific mutants enriched after 8 generations. We need to lower the filter threshold to detect genes that have a more subtle enrichment (fitness >= 2 for formate, and <= 1 for all other conditions) after 16 generations.

list_fitness_formate <- df_fitness_summary %>%
  group_by(locus_tag, substrate, time) %>%
  mutate(formate_hit = case_when(
    substrate == "formate" & max(norm_gene_fitness_median) >= 2 ~ TRUE,
    substrate != "formate" & max(norm_gene_fitness_median) <= 1 ~ TRUE,
    TRUE ~ FALSE)
  ) %>% group_by(locus_tag, time) %>%
  filter(all(formate_hit), n() == 6) %>% 
  pull(locus_tag) %>% unique

df_fitness_summary %>%
  filter(time == 8, locus_tag %in% list_fitness_formate) %>%
  barchart_fitness() %>%
  print(position = c(0,0,0.6,1), more = TRUE)

df_fitness_summary %>%
  filter(locus_tag %in% list_fitness_formate) %>%
  linechart_fitness() %>%
  print(position = c(0.58,0,1,1))

Mechanisms enabling faster growth on formate

According to STRING DB there are no obvious relationships between these genes except for the pair PHG388 (pcaI, Oxoadipate CoA transferase alpha subunit) and H16_B1583 (pcaD, 3-Oxoadipate enol-lactone hydrolase). Both genes are part of the catechol degradation pathway that is itself a larger part of benzene degradation. The gene products catalyze two sequential reactions 3-oxoadipate-enol-lactone –> 3-oxoadipate (pcaD) –> 3-oxoadipyl-CoA (pcaI). The next reaction catalyzed by four isoenzymes makes succinyl-CoA which ends up in the Citrate cycle. How the KO of this pathway would improve tolerance to formate is unclear.

5.1.0.3 Strains enriched on succinate

This is similar to the analysis above for formate, with the exception that many mutants associated with higher fitness in succinate conditions enrich only after 16 generations, and only in the continuous regime. This seems be an artifact of read compression. We therefore use the same thresholds as for formate (fitness >= 2 for succinate, and <= 1 for all other conditions) but after 8 instead of 16 generations.

list_fitness_succinate <- df_fitness_summary %>%
  filter(time == 8) %>%
  group_by(locus_tag, substrate) %>%
  mutate(succinate_hit = case_when(
    substrate == "succinate" & max(norm_gene_fitness_median) >= 2 ~ TRUE,
    substrate != "succinate" & max(norm_gene_fitness_median) <= 1 ~ TRUE,
    TRUE ~ FALSE)
  ) %>% group_by(locus_tag) %>%
  filter(all(succinate_hit), n() == 6, locus_tag != "H16_A2912") %>% 
  pull(locus_tag) %>% unique

df_fitness_summary %>%
  filter(time == 8, locus_tag %in% list_fitness_succinate) %>%
  barchart_fitness() %>%
  print(position = c(0,0,0.6,1), more = TRUE)

df_fitness_summary %>%
  filter(locus_tag %in% list_fitness_succinate) %>%
  linechart_fitness() %>%
  print(position = c(0.58,0,1,1))

Mechanisms enabling faster growth on succinate

A group of five highly related genes shows significant enrichment in (continuous) succinate: H16_2096 (dppA1), H16_A2406 (dppD2), H16_A2407 (dppC2), H16_A2408 (dppB2), and H16_A2409, dppA2. Alternative names are yejABCDE. All of these proteins are part of an ABC transporter of the PepT family. Quote from PFAM about this type of protein/domain: “All characterised members appear to be involved in the transport of oligopeptides or dipeptides. Some are important for sporulation or antibiotic resistance. Some dipeptide transporters also act on the heme precursor delta-aminolevulinic acid. The enrichment seems to be highly reproducible: each of these genes has 7 to 15 different insertion mutants, all showing the same average pattern. At least two more genes are also involved in (amino acid?) transport, H16_A3284 and H16_A2521. The latter one is a”D-amino acid transferase (D-AAT), required by bacteria to catalyse the synthesis of D-glutamic acid and D-alanine" (PFAM). Supposed mechanism of enrichment: Growth on succinate could lead to a shortage/imbalance of certain amino acids, and investment in uptake might give a growth advantage. The growth advantage was only visible in the continuous condition, and is probably not universal for succinate. No gene/mutant was enriched in both succinate growth regimes.

5.1.0.4 Strains enriched in several conditions

A wealth of genes/mutants are enriched in multiple mainly pulsed growth conditions (cluster 4).

df_fitness_summary %>%
  filter(locus_tag %in% names(subset(list_enriched, list_enriched == 4))) %>%
  filter(time == 8) %>%
  barchart_fitness() %>%
  print(position = c(0,0,0.6,1), more = TRUE)

df_fitness_summary %>%
  filter(locus_tag %in% names(subset(list_enriched, list_enriched == 4))) %>%
  linechart_fitness() %>%
  print(position = c(0.58,0,1,1))

To learn more about functional relationships between enriched genes/mutants, we can submit the gene list to the STRING interaction database and retrieve a network of probable interactions.

library(ggraph)
library(tidygraph)

Attaching package: ‘tidygraph’

The following object is masked from ‘package:stats’:

    filter
# function retrieve network interaction data from STRING DB
# separate gene IDs by "%0d"; species/taxon ID for Cupriavidus necator H16: 381666
# (see https://string-db.org/cgi/organisms)
retrieve_STRING <- function(gene_ID, taxon_ID, min_score = 0000, ref = NULL) {
  gene_list <- paste(gene_ID, collapse = "%0d")
  string_graph <- paste0(
    "https://string-db.org/api/tsv/network?identifiers=", 
    gene_list, "&species=", taxon_ID, "&required_score=", min_score) %>%
  read_tsv(col_types = cols()) %>%
  mutate(across(matches("stringId"), function(x) gsub(paste0(taxon_ID, "."), "", x))) %>%
  as_tbl_graph()
  if (!is.null(ref)) {
    left_join(string_graph, ref, by = "name")
  } else {
    string_graph
  }
}

# function to space labels in certain distance to circle
nudge_circle <- function(n, size_x = 0.15, size_y = 0.1) {
  nudge_x = size_x * -cos(seq(0.5*pi, 2.5*pi, length.out = n))
  nudge_y = size_y * sin(seq(0.5*pi, 2.5*pi, length.out = n))
  list(x = nudge_x, y = nudge_y)
}
graph_all_enrich <- retrieve_STRING(
  gene_ID = names(subset(list_enriched, list_enriched == 4)),
  taxon_ID = 381666,
  ref = rename(df_ref, name = locus_tag)
)

graph_all_enrich %>% arrange(COG_Process) %>% activate(edges) %>% 
  filter(score >= 0.4) %>%
  ggraph(layout = 'linear', circular = TRUE) +
  geom_edge_arc(colour = grey(0.6, 0.5), aes(width = score)) + 
  geom_node_point(aes(colour = COG_Process), size = 5) +
  geom_node_text(nudge_x = nudge_circle(22)$x, nudge_y = nudge_circle(22)$y, 
    size = 3.2, aes(label = eggNOG_name, colour = COG_Process)) +
  scale_edge_width(range = c(0.2, 2)) +
  theme_graph(background = "white", foreground = grey(0.5),
    plot_margin = margin(10, 10, 10, 10))

Mechanisms enabling faster growth in several conditions

The picture for these strains is more clear then for the fructose-enriched strains. 13 out of 27 enriched genes (48%), among them some of the most enriched, are directly involved in hydrogenase activity, i.e. hydrogen oxygenation (hoxA, hoxF, hoxU, hoxX, hypA, hypB, hypC, hypD hypE, hypF, hyaA, hyaB, hupH). The mechanism of enabling faster growth by KO of hydrogenases is unknown.

The second theme is enrichment of ptsI (H16_A0326) and ptsH (H16_A0325), together the entire functional unit of the PTS system, the PEP-dependent sugar phosphotransferase system (sugar PTS). “This major carbohydrate active-transport system catalyzes the phosphorylation of incoming sugar substrates concomitantly with their translocation across the cell membrane. Enzyme I transfers the phosphoryl group from (PEP) to the phosphoryl carrier protein (HPr).” Why does the KO of PTS system provides a growth benefit in in all conditions with almost equal contribution? Must be related to energy metabolism, because neither fructose (ABC transporter), succinate and def not formate are transported via a PTS.

Other interesting mechanisms: KO of RNA polymerase sigma factor RpoS (H16_A2373) seems to have a beneficial effect on steady state growth, as well as other stress related transcription factors like cold shock protein capB (H16_B2205), and scoF (H16_B0002).

The gene H16_B2043 is by far most abundant (by reads) and most enriched (by fitness) single mutant in almost all conditions. What is the mechanism? There is almost nothing known about this gene except that it probably has cyclic-guanylate-specific phosphodiesterase activity. This enzyme has, according to PFAM, two domains, the diguanylate cyclase (GGDEF) and the EAL domain. The first one synthesizes cyclic di-GMP Ryjenkov et al., J Bact, 2005. The second domain can act as phosphodiesterase and cleave cyclic cAMP or cGMP. Both compounds play a role in signaling, cAMP for example as the hormone in catabolite repression. According to the discussion in the linked paper, “Mutations in the GGDEF domain proteins or overexpression of such proteins affect exopolysaccharide synthesis […] and formation of biofilms. In C. crescentus, flagellum ejection, which is required for the switch from motile to sessile lifestyle, is impaired in a mutated GGDEF domain”. The underlying mechanism could be disrupted biofilm formation, higher flagellum activity, and thus non-settling phenotype in the bioreactor. Alternatively the mutation affects cAMP formation and disrupts or enhances catabolite repression (more likely that disrupted catabolite repression is beneficial).

5.1.0.5 Sanity checks

All above results are based on fitness score, which is not much more than a normalized log2 fold change of read count over time. We can compare if the mutants with extremely high fitness scores are also the ones that are super-abundant at the final time point, ie.e. have an average read count of >= 500,000 after 16 generations. In fact, only 3 genes have such an extreme read count, but 9 have more than 100,000 reads.

df_fitness %>% filter(time == 16, locus_tag %in% (subset(list_enriched, list_enriched %in% c(3,4)) %>% names)) %>%
  group_by(locus_tag) %>% summarize(max_counts = max(counts), max_fitness = max(norm_gene_fitness)) %>%
  arrange(desc(max_counts))

On the other hand, we can check which genes have an extremely high read count (>= 500,000 in any condition) and see if this correlates with high fitness score. It does partly. 3 out of 7 highly abundant gene mutants enrich extremely over time. The other enrich too, but only 2^1 = 2 to 2^3.5 = 11 times. These non-enriching super-abundant mutants are:

  • H16_A0774 - cphA, Cyanophycin synthase. Supposed mechanism: enriches in LB medium as it does not turn Asp into cyanophycin. No benefit on minimal medium.
  • H16_B2570 - fecA, Outer membrane receptor for Fe(III). Supposed mechanism: regulator activity together with fecR, but also transmembrane transporter for siderophores. KO reduces sensitivty to excess iron??
  • H16_A3145 - Conserved protein/domain typically associated with flavoprotein oxygenases, DIM6/NTAB family. Supposed mechanism: unknown.
df_fitness %>% filter(time == 16, counts > 5*10^5) %>%
  group_by(locus_tag) %>% summarize(max_counts = max(counts), max_fitness = max(norm_gene_fitness)) %>%
  arrange(desc(max_counts))

5.2 Depletion of mutants over time (reduced fitness)

5.2.0.1 Depletion on formate

Several genes/mutants are depleted over time depending on growth condition. Most interesting in this context are cluster 1 and 2, depletion only on formate, or in all conditions, respectively. Not surprisingly, formate-specific genes are highly depleted during growth on formate, but not cbb (Calvin cycle) genes.

df_fitness_summary %>%
  filter(locus_tag %in% names(subset(list_enriched, list_enriched == 1))) %>%
  filter(time == 16) %>%
  barchart_fitness() %>%
  print(position = c(0,0,0.6,1), more = TRUE)

df_fitness_summary %>%
  filter(locus_tag %in% names(subset(list_enriched, list_enriched == 1))) %>%
  linechart_fitness() %>%
  print(position = c(0.58,0,1,1))

What is the role of formate-essential genes? Are there patterns that emerge? We can submit the depleted gene list to STRING DB and retrieve a possible network of interactions. The network shows immediately that there are many high-scoring interactions between the genes, particularly the soluble FDH genes are sticking out as one cluster (fdsABDG) and their transcriptional regulator fdsR. Then the molybdenum cofactor processing proteins moaCDE, moeA, mobA, mog. And a range of cytochrome genes responsible for acceptance and transport of of electrons from formate to the ETC in cytoplasmic membrane (petABC, ccoGNOP, cyc). It is also intersting what is NOT essential on formate, such as no cbb genes except the master transcriptional regulator cbbR. The reason for this must be the redundancy of all cbb genes (two operons).

graph_formate_depl <- retrieve_STRING(
  gene_ID = names(subset(list_enriched, list_enriched == 1)),
  taxon_ID = 381666,
  ref = rename(df_ref, name = locus_tag)
)

graph_formate_depl %>% arrange(COG_Process) %>% activate(edges) %>% 
  filter(score >= 0.4) %>%
  ggraph(layout = 'linear', circular = TRUE) +
  geom_edge_arc(colour = grey(0.6, 0.5), aes(width = score)) + 
  geom_node_point(aes(colour = COG_Process), size = 5) +
  geom_node_text(nudge_x = nudge_circle(34)$x, nudge_y = nudge_circle(34)$y,
    size = 3.2, aes(label = eggNOG_name, colour = COG_Process)) +
  scale_edge_width(range = c(0.2, 2)) +
  theme_graph(background = "white", foreground = grey(0.5),
    plot_margin = margin(10, 10, 10, 10))

Let’s create a pathway/operon based overview about the formate-essential genes. We group the genes into three categories, and also include the genes/proteins that belong to each group but were not depleted or enriched. The three groups are:

  • molybdenum cofactor biosynthesis
    • modAB = molybdenum transporter
    • moaACDE = molybdopterin backbone biosynthesis
    • moeA = molybdopterin molybdenum transferase
    • mobAB = molybdenum cofactor guanylyltransferase,
    • mog = molybdopterin biosynthesis protein (role?)
  • formate dehydrogenase
    • soluble (fds, fwd)
    • membrane-bound (fdh, fdo)
  • electron transport chain
    • petABC (aka qcrABC, H16_A3398, H16_A3397, H16_A3396) = cycochrome bc1 (reductase)
    • cyc (H16_A3451), cytochrome c553. Other cytochrome c553 genes to include according to uniprot: H16_A0830, H16_A0846, H16_B1452, H16_A3576, H16_A1121, H16_A1120
    • ccoGNOP (H16_A2316, H16_A2317, H16_A2318, H16_A2319), cytochrome C oxidase (cbb3 type)
    • VP2641 (H16_A1031) = Membrane spanning protein, RDD family. PFAM: unknown function, potential transporter? Interacts with cytochrome c553 and qcrC, cytochrome reductase. It’s depletion implicates this protein in formate-specific e- transport in the membrane.

We can prove that these complexes are mostly or only required during growth on formate. Can we exclude other cytochrome reductase and cytochrome oxidase complexes, or other cytochrome c553 proteins that act as e- acceptors from FDH? This type of information can be used to carve out the specific path of e- from formate through the ETC. If other cytochrome oxidase/reductase proteins are essential in all conditions, it can not be excluded that they are not required for growth on formate. We therefore also include alternative cytochrome oxidases, like ctaABCDEG and coxMNOP, cyo and cyd operons.

To summarize, according to the essentiality data on formate, electrons from formate are transported through the membrane the following way:

S-FDH --> NADH --|
                 |
M-FDH --> Cytochrome C reductase (bc1 complex III) --> Cytochrome c553 () --> Cytochrome C oxidase (cbb3 complex IV) --> O2

What is known in literature about electron transport from formate to membrane?

  • main FDH is S-FDH both in terms of protein abundance, inducibility, and essentiality
  • cytochrome reductase bc1 complex (qcrABC) is particularly important during growth on formate (this study)
  • it is also beneficial during heterotrophic growth (slightly reduced fitness)
  • inhibition of this complex reduces growth in heterotrophy and lithotrophy (see Cramm 2006)
  • qcrC seems to be a cytochrome C that is mobile (?) and transports e- in membrane
  • bc1 complex functions in oxygen-limited respiration, therefore maybe no growth on formate and nitrate respiration?
  • bc1 complex deletion mutant was completely deficient in oxygen-limited growth on nitrate (Garg et al., 2018).
  • bc1 complex accepts reduced quinols as e- donor, pumps 4 H+ /2 e− via proton-motive Q cycle (quinol:cyt c oxidoreduction), and releases reduced cytochrome C e- carriers
  • Cytochrome c553 proteins are e- shuttles in membrane, between bc1 and respiration complexes (?)
  • cbb3-type cytochrome oxidase special member of the heme-copper oxidase family
  • “in Bradyrhizobium japonicum, this enzyme operates at extremely low partial pressures of oxygen” [Preisig et al., 1996]
# generalized function to plot (small) heatmaps
heatmap_fitness <- function(data, key = TRUE) {
  
  # make some small adjustments to optical appeaarance
  mutate(data, gene_name = factor(gene_name, unique(gene_name))) %>%
  mutate(condition = if_else(condition == "pulse", "P", "C")) %>%
  mutate(cond = paste0(substrate, " - ", condition)) %>%
  group_by(gene_name) %>% arrange(cond) %>%
  mutate(norm_gene_fitness_median = replace(norm_gene_fitness_median, norm_gene_fitness_median < -6, -6)) %>%

  # plot heatmap
  levelplot(norm_gene_fitness_median ~ gene_name * factor(cond), .,
    par.settings = custom.colorblind(), colorkey = key,
    col.regions = colorRampPalette(heat_cols)(16),
    at = seq(-6, 6, 1), aspect = "fill",
    xlab = "", ylab = "", scales = list(cex = 0.7, x = list(rot = 90)),
    panel = function(x, y, z, ...) {
      panel.levelplot(x, y, z, ...)
      panel.abline(v = seq_along(unique(x))+0.5, 
        h = seq_along(unique(y))+0.5, col = "white", lwd = 2)
    }
  )
}
plot_formate_fit_1 <- df_fitness_summary %>% filter(time == 8, grepl("mo[aedbg]", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "mo[aedbg]([A-Z0-9]+)?")) %>%
  ungroup %>% arrange(gene_name) %>%
  heatmap_fitness(key = FALSE)

plot_formate_fit_2 <- df_fitness_summary %>% filter(time == 8, grepl("fd[swho]", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "fd[swho][A-Z0-9]+")) %>%
  mutate(gene_name = stri_replace_first_regex(gene_name, "fdhD$", "fdsC")) %>%
  ungroup %>% arrange(gene_name) %>%
  heatmap_fitness(key = FALSE)

plot_formate_fit_3 <- df_fitness_summary %>% filter(time == 8, locus_tag %in% c("H16_A3451", "H16_A0830",
    "H16_A0846", "16_B1452", "H16_A3576", "H16_A1121", "H16_A1120", "H16_A1031") | 
    grepl("qcr|cco|cta|cox[MNOPQ] |cyo|cyd", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "H16_[AB][0-9]+|(qcr|cco|cta|cox|cyo|cyd)([A-Z0-9]+)?")) %>%
  ungroup %>% arrange(
    factor(str_sub(gene_name, 1, 3), c("cyo", "cyd", "qcr", "H16", "cox", "cta", "cco")), 
    str_sub(gene_name, 4, 5)) %>%
  heatmap_fitness()

print(plot_formate_fit_1, position = c(0, 0.475, 0.5, 1), more = TRUE)
print(plot_formate_fit_2, position = c(0.475, 0.49, 0.95, 1), more = TRUE)
print(plot_formate_fit_3, position = c(0, 0, 1, 0.57))

5.2.0.2 Depletion in all conditions

Which genes are depleted in all conditions (cluster 2)? Here we see the effect of the read compression, such that many genes seem to “recover” from initial depletion and enrich from 8 to 16 generation time point. This effect is only apparent for the continuous conditions and with very high probability related to the distortion of read counts by super-enriching mutants. The 8 generation time point should be considered as more reliable in those cases.

df_fitness_summary %>%
  filter(locus_tag %in% names(subset(list_enriched, list_enriched == 2))) %>%
  filter(time == 16) %>%
  barchart_fitness() %>%
  print(position = c(0,0,0.6,1), more = TRUE)

df_fitness_summary %>%
  filter(locus_tag %in% names(subset(list_enriched, list_enriched == 2))) %>%
  linechart_fitness() %>%
  print(position = c(0.58,0,1,1))

graph_all_depl <- retrieve_STRING(
  gene_ID = names(subset(list_enriched, list_enriched == 2)),
  taxon_ID = 381666,
  ref = rename(df_ref, name = locus_tag)
)

graph_all_depl %>% arrange(COG_Process) %>% activate(edges) %>% 
  filter(score >= 0.4) %>%
  ggraph(layout = 'linear', circular = TRUE) +
  geom_edge_arc(colour = grey(0.6, 0.5), aes(width = score)) + 
  geom_node_point(aes(colour = COG_Process), size = 5) +
  geom_node_text(nudge_x = nudge_circle(38)$x, nudge_y = nudge_circle(38)$y,
    size = 3.2, aes(label = eggNOG_name, colour = COG_Process)) +
  scale_edge_width(range = c(0.2, 2)) +
  theme_graph(background = "white", foreground = grey(0.5),
    plot_margin = margin(10, 10, 10, 10))

5.3 Fitness of cbb genes

One of the most interesting features of C. necator is the cbb operon encoding enzymes for PPP and Calvin cycle (e.g. Rubisco). Two almost identical copies of the operon are present in the genome, one on the pHG1 megaplasmid and one one chromosome 2. The latter one has two additional genes, a putative cbbB formate dehydrogenase, and cbbR, the transcriptional regulator. To visualize general trends for all genes of a pathway, we can use a beeswarm plot broken down by condition.

plot_cbb_fitness_bee <- df_fitness_summary %>% ungroup %>%
  filter(time == 8, str_detect(gene_name, "cbb.|cfx.|cbx."), condition != "pulse") %>%
  mutate(gene_name = str_replace(gene_name, "cfx|cbx", "cbb")) %>%
  
  xyplot(norm_gene_fitness_median ~ factor(substrate), .,
    groups = factor(substrate), par.settings = custom.colorblind(col = subs_col[c(1,3,5)]),
    labels = stri_extract_first(.[["gene_name"]], regex = "cbb.|cfx.|cbx."),
    xlab = "", ylab = "fitness (8 generations)", ylim = c(-7.5, 2.5),
    scales = list(alternating = FALSE),
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.abline(h = 0, lwd = 1.5, col = grey(0.7), lty = 2)
      panel.abline(h = -3, lwd = 1.5, col = grey(0.7), lty = 3)
      panel.text(3.1, -3.5, labels = "significant", col = grey(0.6), cex = 0.65)
      coords <- panel.beeswarm(x, y, bin_y = TRUE, breaks_y = seq(-10, 4, 0.4), 
        return_coords = TRUE, ...)
      panel.directlabel(x = coords$x, y = coords$y, y_boundary = c(-10, -4), 
        cex = 0.65, draw_box = TRUE, box_line = TRUE, ...)
      panel.key(labels = c("formate", "fructose", "succinate"), cex = 0.8, 
        corner = c(0.98, 0.05))
    }
  )

print(plot_cbb_fitness_bee)

Only one gene is really depleted and therefore essential on the two formate conditions, cbbR. At least two more genes for Rubisco and PRUK should be essential on formate as they have no counterpart on chromosome 1. They show no significant depletion. This proves that the two cbb operons can fully complement each other, except for cbbR, the one gene present in only copy (chromosome 2).

Note: Some cbb mutants for chromosome 2 seem to have a more positive fitness score compared to their counter parts on the pHG1 megaplasmid. Some genes with higher fitness show sudden leaps from 8 to 16 hours. A look up in the summary table revealed that they have only 1 or 2 mutants quantified per gene, which makes the results less reliable, particularly for the continuous conditions on fructose with low read count at 16 generationa. Results for these mutants have to be interpreted with care. The folowing plot focuses on the pHG1 cbb copies which have on average higher coverage with individual mutants.

# single time series per gene
plot_cbb_fitness_select <- df_fitness %>% ungroup %>%
  left_join(select(df_ref, locus_tag, gene_name), by = "locus_tag") %>%
  filter(str_detect(gene_name, "(cbb|cfx|cbx)[PSLKGAT]P|cbbR"), condition != "pulse") %>%
  mutate(
    gene_name = case_when(
      gene_name == "cfxR cbbR H16_B1396" ~ "cbbR (REG)",
      gene_name == "cfxP cbbPP PHG421" ~ "cbbP (PRUK)",
      gene_name == "cbxSP cbbS cbbSP cfxSP rbcS PHG426" ~ "cbbS (RBPC)",
      gene_name == "cbbL2 cbbL cbxLP cfxLP PHG427" ~ "cbbL (RBPC)",
      gene_name == "cbbKP PHG417" ~ "cbbK (PGK)",
      gene_name == "cbbGP PHG418" ~ "cbbG (GAPDH)",
      gene_name == "cbbAP PHG416" ~ "cbbA (FBA)",
      gene_name == "cbbTP PHG420" ~ "cbbT (TKT)",
      #gene_name == "cbbZP PHG419" ~ "cbbZ (PGP)",
      #gene_name == "cbxXP cfxXP PHG425" ~ "cbbX (RBCA)",
      #gene_name == "cbbEP cfxE PHG423" ~ "cbbE (RPE)"
    ) %>% factor(., unique(.)[c(1,6,7,8,3,4,2,5)])
  ) %>% ungroup() %>%
  
  xyplot(norm_gene_fitness ~ factor(time) | gene_name, .,
    groups = substrate, par.settings = custom.lattice(col = subs_col[c(1,3,5)]),
    type = c("p", "l"), xlab = "generations", ylab = "fitness",
    ylim = c(-7.9, 2.5), layout = c(4, 2), between = list(x = 0.5, y = 0.5),
    scales = list(alternating = FALSE), lwd = 2.5, as.table = TRUE,
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.errbars(x, y, ewidth = 0, ...)
    }
  )

print(plot_cbb_fitness_select)

5.4 Fitness of phosphoglycolate salvage mutants

Phosphoglycolate salvage, also known as photorespiration, is the process of recycling the product of Rubisco’s oxygenation reaction, 2-phoshoglycolate (2-PGly). Depending on the local CO2 concentration around Rubisco, the flux towards 2-PGly becomes higher with decreasing CO2 concentration. Typically, flux towards 2-PGly increases signifcantly between … and … ppm CO2 [Reference?]. The following plot shows fitness of the most important 2-PGly salvage KO mutants.

The key enzymes for the universal ‘upper part’ of 2-PGly salvage are 2-PGly-phosphatase (cbbZ2, cbbZP) and glycolate dehydrogenase GDH (GlcDEF, ). The glycerate pathway is the dominant route for the ‘lower part’ of 2-PGly salvage with four enzymes being strongly upregulated in low CO2: glyoxylate carboligase GLXCL (H16_A3598), hydroxypyruvate isomerase HPYRI (H16_A3599), tartronate semialdehyde reductase TRSARr (H16_A3600), and glycerate kinase GLYCK (H16_B0612). The first three of these enzymes are encoded in one operon.

df_fitness %>% ungroup %>%
  left_join(select(df_ref, locus_tag, gene_name), by = "locus_tag") %>%
  filter(
    locus_tag %in% c("H16_B1387", "PHG419", "H16_A3094", "H16_A3096", "H16_A3097",
      "H16_A3598", "H16_A3599", "H16_A3600", "H16_B0612", "H16_A0640", "H16_A0641",
      "H16_A0642", "H16_A0644"),
    condition == "pulse") %>%
  mutate(gene_name = str_replace(gene_name, " cbbZ2", "")) %>%
  
  xyplot(norm_gene_fitness ~ factor(time) | factor(gene_name, rev(unique(gene_name))), .,
    groups = substrate, par.settings = custom.lattice(col = subs_col[c(1,3,5)]),
    type = c("p", "l"), xlab = "generations", ylab = "fitness",
    ylim = c(-7.9, 2.5), layout = c(5, 3), between = list(x = 0.5, y = 0.5),
    scales = list(alternating = FALSE), lwd = 2.5, as.table = TRUE,
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.errbars(x, y, ewidth = 0, ...)
      panel.key(..., corner = c(0.05, 0.05))
    }
  )

Possible explanation: local CO2 concentration is high during formatotrophic growth

Question: What is steady-state CO2/HCO3-concentration in medium?

Known cultivation parameters for highest growth rate:

  • medium concentration: 1.5 g/L formate
  • growth rate/dilution rate: 0.25 h^-1
  • residual formate at µ = 0.25 h^-1: 0.25 g L^-1
  • 25% of CO2 from formate is fixed, 75% is emitted (C. necator GEM)

Answer:

  • formate consumption rate: Q_HCOO3 = (1.5 g L^-1 - 0.25 g L^-1) * 0.25 h^-1 = 0.31 g formate L^-1 h^-1
  • estimation of CO2 production rate: P_CO2 = Q_HCOO3 * MW_HCOO3 = 0.31 g L^-1 h^-1 / 48 g mol^-1 = 6.5 mmol L^-1 h^-1
  • estimation of CO2 consumption rate: Q_CO2 = P_CO2 * 0.25 = 1.625 mmol L^-1 h^-1
  • final CO2 production rate: P_CO2 - Q_CO2 = 4.875 mmol L^-1 h^-1

Unfortunately it is not possible to determine the steady-state concentration from the CO2 emission rate. However we can compare this rate with the growth-limiting CO2 supply for autotrophic growth (ambient air, Claassens et al., 2020). The authors used:

  • 0.04% CO2 or 10% CO2, 6.25 L/min = 375 L/h
  • 0.7L culture volume
  • density of CO2 = 1.836 g/L at 25*C
  • CO2 supply rate = 375 L h^-1 * 0.0004 / 0.7 L culture * 1.836 g L^-1 = 0.39 g L^-1 h^-1 = 8.5 mmol L^-1 h^-1

5.5 Fitness of central metabolism genes

In order to obtain an overview about fitness contribution of central carbon metabolism enzymes, we can select the same list of enzymes (reactions) that was used for Figure 3 of the manuscript. In this figure, absolute protein abundance, relative change in protein abundance, utilization was compared. The names of enzymatic reactions are taken from the genome scale modeland have to be mapped to gene IDs.

df_ccm_enzymes <- list(
  `CBB cycle` = c("PGK", "TPI", "GAPD", "FBA", "FBP", "TKT1", "TKT2", "TAh", "RPE", "RPI", "PRUK", "RBPC", "FDH"),
  `Entner-Doudoroff pathway` = c("PGI", "G6PDH2r", "PGL", "EDD", "EDA"),
  `Pyruvate metabolism` = c("PGM", "ENO", "PYK", "PDH1", "PDH2", "PDH3", "PC", "PPC", "ME1", "ME2", "CS"),
  `TCA cycle` = c("ACONT1", "ACONT2", "ICDHx", "ICDHyr", "AKGDH", "SUCOAS", "SUCDi", "SUCD1", "FUM", "MDH", "MALS")
) %>% enframe("model_group_short", "reaction_id") %>% 
unnest(reaction_id)

df_model_reactions <- read_csv("../data/input/model_reactions.csv", col_types = cols()) %>%
  select(reaction_id, reaction_name, genes) %>% separate_rows(genes, sep = ", ") %>%
  rename(locus_tag = genes) %>%
  right_join(df_ccm_enzymes, by = "reaction_id") %>%
  # remove 1 gene from TCA which is also listed in PYR (pdh3 / dihydrolipoamide dehydrogenase)
  filter(!(locus_tag == "H16_A1377" & model_group_short == "TCA"))
Warning: Missing column names filled in: 'X1' [1]

The beeswarm plot shows that some genes are conditionally essential, albeit not many. Trends that are consistent between time points and growth regimes are: - depletion of fdsABDG aka the soluble FDH in both formate conditions (described previously in detail) - depletion of eda, edd and pgl in fructose (all consecutive reactions of the ED pw), to a lower extent in formate too? - ppc (PEP carbooxylase) in formate, also predicted by FBA/RBA to carry major flux in formate) - pdhA (pyruvate dehydrogenase) on fructose and succinate - maeA malic enzyme - nothing from TCA cycle

plot_ccm_bee <- df_fitness_summary %>%
  inner_join(df_model_reactions, by = "locus_tag") %>%
  ungroup %>% filter(time == 8, condition != "pulse") %>%
  mutate(gene_name = stri_extract_first(gene_name, regex = "[a-zA-Z0-9]+")) %>%

  xyplot(norm_gene_fitness_median ~ factor(substrate) | model_group_short, .,
    groups = substrate, par.settings = custom.colorblind(col = subs_col[c(1,3,5)]),
    labels = .[["gene_name"]], xlab = "", ylab = "fitness (8 generations)", 
    ylim = c(-7.2, 1.8), as.table = TRUE, between = list(x = 0.5, y = 0.5),
    scales = list(alternating = FALSE), layout = c(4, 1),
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.abline(h = 0, lwd = 1.5, col = grey(0.7), lty = 2)
      panel.abline(h = -3, lwd = 1.5, col = grey(0.7), lty = 3)
      panel.text(3, -3.5, labels = "significant", col = grey(0.6), cex = 0.65)
      coords <- panel.beeswarm(x, y, bin_y = TRUE, breaks_y = seq(-10, 4, 0.4),
        return_coords = TRUE, ...)
      panel.directlabel(coords$x, coords$y, y_boundary = c(-10, -3), cex = 0.65,
        draw_box = TRUE, box_line = TRUE, box_scale = -0.01, ...)
    }
  )

print(plot_ccm_bee)

Looking at the single genes as a time series shows the same picture. Some genes however show a sudden “leap” in fitness from 8 to 16 generations even after previous depletion. This is an artifact caused by normalization of fitness by the overall read distribution. These genes have 0 reads quantified but the algorithm still determines if 0 reads can be expected for genes with low fitness and attributes this fitness to them. However if the read distribution becomes extremely skewed towards few high abundant mutants, the fitness calculation becomes impossible for low-abundant mutants. We therefore focus on the 8 generation time point.

# single time series per gene
plot_ccm_fitness_select <- df_fitness %>% ungroup %>%
  left_join(select(df_ref, locus_tag, gene_name), by = "locus_tag") %>%
  inner_join(df_model_reactions, by = "locus_tag") %>%
  filter(str_detect(gene_name, "fds|pgl|pgi|edd1|eda|ppc|pdh|mae")) %>%
  mutate(condition = paste0(substrate, " - ", toupper(substr(condition, 1, 1)))) %>%
  mutate(gene_name = stri_extract_first(gene_name, regex = "[a-zA-Z0-9]+")) %>%
  
  xyplot(norm_gene_fitness ~ factor(time) | gene_name, .,
    groups = condition, as.table = TRUE,
    par.settings = custom.colorblind(col = subs_col), type = c("p", "l"),
    xlab = "generations", ylab = "fitness",
    ylim = c(-8.5, 4), layout = c(7, 2), 
    between = list(x = 0.5, y = 0.5),
    auto.key = list(columns = 3, cex = 0.7),
    scales = list(alternating = FALSE), lwd = 2,
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.errbars(x, y, ewidth = 0, ...)
    }
  )

print(plot_ccm_fitness_select)

5.6 Table with conditionally essential reactions in central metabolism

df_ccm_essential <- df_model_reactions %>%
  filter(reaction_id %in% c("FDH", "PPC", "PGL", "EDD", "EDA", "PDH1", "ME1", "ME2")) %>%
  left_join(select(df_ref, locus_tag, gene_name), by = "locus_tag") %>%
  left_join(select(df_fitness_comp, matches("locus|continuous")), by = "locus_tag") %>%
  arrange(model_group_short, reaction_id, locus_tag) %>%
  mutate(across(matches("continuous_"), round, 1)) %>%
  mutate(gene_name = str_remove(gene_name, " ?H16\\_[AB0-9]+")) %>%
  rename_with(function(x) gsub("continuous_", "", x), matches("continuous_")) %>%
  rename(Pathway = model_group_short, Reaction = reaction_id,
    `Reaction name` = reaction_name, `Gene ID` = locus_tag, `Gene name` = gene_name) %>%
  select(c(4,1,2,3,5,6,7,8))

print(df_ccm_essential)
write_csv(df_ccm_essential, "../data/output/essentiality_ccm.csv")

6 Export gene fitness data

Save an Rdata file with fitness data for all conditions that finally were used in the manuscript by Jahn et al., 2021. This is used in the ShinyLib web app.

Cupriavidus_BarSeq_2021 <- df_fitness %>%
  ungroup %>% select(-ID) %>%
  unite(condition, substrate, condition, sep = " - ") %>%
  mutate(fold_change = 2^log2FC) %>%
  rename(fitness_score = norm_gene_fitness, t_stat = t, timepoint = time) %>%
  left_join(select(df_ref, locus_tag, new_locus_tag, uniprot, gene_name, protein_name, System, 
    Process, Pathway), by = "locus_tag")

save(Cupriavidus_BarSeq_2021, file = "../../ShinyLib/data/Cupriavidus_BarSeq_2021.Rdata")
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgY29tcGV0aXRpb24gZXhwZXJpbWVudHMgd2l0aCBhIGJhcmNvZGVkIHRyYW5zcG9zb24gbGlicmFyeSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgphdXRob3I6ICJNaWNoYWVsIEphaG4iCm91dHB1dDoKICBodG1sX25vdGVib29rOiAKICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KCiMgRGVzY3JpcHRpb24KClRoaXMgUiBub3RlYm9vayBpcyBhIGJpb2luZm9ybWF0aWNzIHBpcGVsaW5lIHRvIGFuYWx5emUgZml0bmVzcyBkYXRhIG9idGFpbmVkIGZyb20gYSBiYXJjb2RlZCB0cmFuc3Bvc29uIGxpYnJhcnkgaW4gKlJhbHN0b25pYSBldXRyb3BoYSogYS5rLmEuICpDdXByaWF2aWR1cyBuZWNhdG9yKi4gRm9yIGJhY2tncm91bmQgYW5kIGRldGFpbHMgcmVnYXJkaW5nIHRoZSBtZXRob2QsIHNlZSBbV2V0bW9yZSBhdCBhbC4sIG1CaW8sIDIwMTVdKGh0dHBzOi8vbWJpby5hc20ub3JnL2NvbnRlbnQvNi8zL2UwMDMwNi0xNSkgYW5kIFtQcmljZSBldCBhbC4sIE5hdHVyZSwgMjAxOF0oaHR0cDovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU4Ni0wMTgtMDEyNC0wKSkuCgoKIyBMaWJyYXJpZXMKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CiMgb3B0aW9uYWxseSBpbnN0YWxsIHJlcG9zIGZyb20gZ2l0aHViCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJtLWphaG4vbGF0dGljZS10b29scyIpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJtLWphaG4vUi10b29scyIpCmxpYnJhcnkobGF0dGljZSkKbGlicmFyeShsYXR0aWNlRXh0cmEpCmxpYnJhcnkobGF0dGljZXRvb2xzKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkZW5kZXh0ZW5kKQpsaWJyYXJ5KFJ0b29scykKbGlicmFyeShjb2xvcnNwYWNlKQpsaWJyYXJ5KHN0cmluZ2kpCmBgYAoKCiMgT3ZlcnZpZXcgb2YgYmFyY29kZS90cmFuc3Bvc29uIHJlYWQgY291bnRzCgojIyBEYXRhIGltcG9ydCBhbmQgcHJvY2Vzc2luZwoKUmVhZCBpbiB0aGUgbWFpbiBkYXRhIHRhYmxlcyB3aXRoIEEpIHJlYWRzIHBlciBiYXJjb2RlIGFuZCBzYW1wbGUgKCdwb29sIGNvdW50cycpLCBhbmQgQikgdGhlIGZpdG5lc3MgdGFibGVzLiBUYWJsZXMgd2VyZSBvYnRhaW5lZCBieSBwcm9jZXNzaW5nIHNlcXVlbmNpbmcgZGF0YSB3aXRoIGEgY3VzdG9tIFtCYXJTZXEgcGlwZWxpbmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9tLWphaG4vcmViYXIpLiBUaGUgMzIgZ2VuZXJhdGlvbiBzZXF1ZW5jaW5nIHNhbXBsZXMgYXJlIHJlbW92ZWQgZHVlIHRvIHRoZSBsb3cgcmVhZCBjb3VudCBpbiB0aGUgYGNvbnRpbnVvdXNgIHNhbXBsZXMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQojIGltcG9ydCBiYXJzZXEgY291bnRzIGRhdGEgaW4gd2lkZSBmb3JtYXQgYW5kIHJlc2hhcGUgdG8gbG9uZyBmb3JtYXQKZGZfY291bnRzX2ZyYyA8LSByZWFkX3RzdigiLi4vLi4vLi4vcmViYXIvZGF0YS8yMDIwMTIyMl9iYXJzZXFfZnJjL3Jlc3VsdHMvcmVzdWx0LnBvb2xjb3VudCIpICU+JQogIHNlbGVjdCghbWF0Y2hlcygiMzJnZW58XzMyXyIpKSAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gIWFsbF9vZihjKCJiYXJjb2RlIiwgInJjYmFyY29kZSIsICJzY2FmZm9sZCIsICJzdHJhbmQiLCAicG9zIikpLCAKICAgIG5hbWVzX3RvID0gInNhbXBsZSIsIHZhbHVlc190byA9ICJuX3JlYWRzIikKCmRmX2NvdW50c19zdWMgPC0gcmVhZF90c3YoIi4uLy4uLy4uL3JlYmFyL2RhdGEvMjAyMTA0MDdfYmFyc2VxX3N1Y19mb3IvcmVzdWx0cy9yZXN1bHQucG9vbGNvdW50IikgJT4lCiAgcGl2b3RfbG9uZ2VyKAogICAgY29scyA9ICFhbGxfb2YoYygiYmFyY29kZSIsICJyY2JhcmNvZGUiLCAic2NhZmZvbGQiLCAic3RyYW5kIiwgInBvcyIpKSwgCiAgICBuYW1lc190byA9ICJzYW1wbGUiLCB2YWx1ZXNfdG8gPSAibl9yZWFkcyIpCgojIG1lcmdlIGJhcmNvZGUgY291bnRzIHRhYmxlcwpkZl9jb3VudHMgPC0gYmluZF9yb3dzKGRmX2NvdW50c19mcmMsIGRmX2NvdW50c19zdWMpCgojIGltcG9ydCBmaXRuZXNzIGRhdGEsIHRoZSBmaW5hbCBvdXRwdXQgb2YgdGhlIEJhclNlcSBwaXBlbGluZQpsb2FkKCIuLi8uLi8uLi9yZWJhci9kYXRhLzIwMjAxMjIyX2JhcnNlcV9mcmMvcmVzdWx0cy9maXRuZXNzX2dlbmUuUmRhdGEiKQpkZl9maXRuZXNzX2ZyYyA8LSBmaXRuZXNzX2dlbmUgJT4lCiAgZmlsdGVyKGNvbmRpdGlvbiAhPSAibG9uZyBwdWxzZSIsIHRpbWUgIT0gMzIpICU+JQogIG11dGF0ZShJRCA9IGFzLm51bWVyaWMoSUQpLCBzdWJzdHJhdGUgPSAiZnJ1Y3Rvc2UiLCAKICAgIGNvbmRpdGlvbiA9IHN0cl9yZW1vdmUoY29uZGl0aW9uLCAic2hvcnQgIikpCgpsb2FkKCIuLi8uLi8uLi9yZWJhci9kYXRhLzIwMjEwNDA3X2JhcnNlcV9zdWNfZm9yL3Jlc3VsdHMvZml0bmVzc19nZW5lLlJkYXRhIikKZGZfZml0bmVzc19zdWMgPC0gZml0bmVzc19nZW5lICU+JQogIHNlcGFyYXRlKGNvbmRpdGlvbiwgc2VwID0gIl8iLCBpbnRvID0gYygic3Vic3RyYXRlIiwgImNvbmRpdGlvbiIpKQoKIyBtZXJnZSBmaXRuZXNzIHRhYmxlcwpkZl9maXRuZXNzIDwtIGJpbmRfcm93cyhkZl9maXRuZXNzX2ZyYywgZGZfZml0bmVzc19zdWMpICU+JQogIHJlbmFtZShsb2N1c190YWcgPSBsb2N1c0lkKQpybSgiZGZfZml0bmVzc19mcmMiLCAiZGZfZml0bmVzc19zdWMiLCAiZGZfY291bnRzX2ZyYyIsICJkZl9jb3VudHNfc3VjIikKCiMgaW1wb3J0IGdlbm9tZSBhbm5vdGF0aW9uCmRmX3JlZiA8LSByZWFkX2NzdigiLi4vZGF0YS9yZWYvUmFsc3RvbmlhX0gxNl9nZW5vbWVfYW5ub3RhdGlvbi5jc3YiKSAlPiUKICBmaWx0ZXIoIWR1cGxpY2F0ZWQobG9jdXNfdGFnKSkgJT4lCiAgbXV0YXRlKGVnZ05PR19uYW1lID0gaWZfZWxzZShpcy5uYShlZ2dOT0dfbmFtZSksIGdlbmVfbmFtZSwgZWdnTk9HX25hbWUpKQoKIyBkZWZpbmUgc3RhbmRhcmQgY29sb3JzCnN0ZGNvbCA8LSBjdXN0b20uY29sb3JibGluZCgpJHN1cGVycG9zZS5saW5lJGNvbApgYGAKCiMjIFN1bW1hcnkgc3RhdGlzdGljcwoKT3ZlcnZpZXcgYWJvdXQgdGhlIG51bWJlciBvZiByZWFkcyBwZXIgYmFyY29kZSwgYmFyY29kZXMgcGVyIGdlbmUgYW5kIHNvIG9uLiBBcm91bmQgOC0xMCBNIHJlYWRzIHdlcmUgbWFwcGVkIG9uIGF2ZXJhZ2UsIHBlciBzYW1wbGUuIAoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4LCBtZXNzYWdlID0gRkFMU0V9CiMgTnVtYmVyIG9mIHRvdGFsIG1hcHBlZCByZWFkcwpkZl9jb3VudHMgJT4lIGdyb3VwX2J5KHNhbXBsZSkgJT4lCiAgc3VtbWFyaXplKG5fbWlsbGlvbl9yZWFkcyA9IHN1bShuX3JlYWRzKS8xMF42KSAlPiUKICBiYXJjaGFydChmYWN0b3Ioc2FtcGxlKSB+IG5fbWlsbGlvbl9yZWFkcywgLiwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgICBob3Jpem9udGFsID0gVFJVRSwgYm9yZGVyID0gTlVMTCwKICAgIHNjYWxlcyA9IGxpc3QoeSA9IGxpc3QoY2V4ID0gMC43KSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmJhcmNoYXJ0KHgsIHksIC4uLikKICAgIH0KICApCmBgYAoKRGlzdHJpYnV0aW9uIG9mICoqbnVtYmVyIG9mIHJlYWRzIHBlciBiYXJjb2RlKiouIFRoZXJlIGlzIGEgc3VmZmljaWVudCBudW1iZXIgb2YgcmVhZHMgZm9yIHF1YW50aWZpY2F0aW9uLCBvbiBhdmVyYWdlIGxvZzIobikgPSA1ID0gMzIgcmVhZHMgcGVyIGJhcmNvZGUuCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDYuMn0KcGxvdF9yZWFkc19wZXJfYmMgPC0gaGlzdG9ncmFtKH4gbG9nMihuX3JlYWRzKSB8IHNhbXBsZSwKICBkZl9jb3VudHMsIGFzLnRhYmxlID0gVFJVRSwgbGF5b3V0ID0gYyg4LDYpLAogIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksCiAgeGxhYiA9IGV4cHJlc3Npb24oImxvZyJbMl0qIiByZWFkcyBwZXIgYmFyY29kZSIpLAogIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSksCiAgcGFuZWwgPSBmdW5jdGlvbih4LCAuLi4pIHsKICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgIHBhbmVsLmhpc3RvZ3JhbSh4LCBib3JkZXIgPSAid2hpdGUiLCAuLi4pCiAgfQopCgpwcmludChwbG90X3JlYWRzX3Blcl9iYykKYGBgCgpTaW1pbGFybHkgdG8gdGhlIGFib3ZlLCB0aGlzIGlzIGFuIG92ZXJ2aWV3IGFib3V0IHRoZSAqKm51bWJlciBvZiBiYXJjb2RlcyBwZXIgZ2VuZSoqIGFzIGEgaGlzdG9ncmFtLiBUaGlzIGRpc3RyaWJ1dGlvbiBpcyB0aGUgc2FtZSBmb3IgYWxsIGNvbmRpdGlvbnMgYW5kIHJlcGxpY2F0ZXMuIFRoZSBzZWNvbmQgcGxvdCBpcyB0aGUgKipudW1iZXIgb2YgcmVhZHMgcGVyIGdlbmUqKiwgYXZlcmFnZWQgYXMgbWVkaWFuIG92ZXIgYWxsIGNvbmRpdGlvbnMgKGV4Y2x1ZGluZyAwIHRpbWUgcG9pbnQgd2hlcmUgY291bnRzIHdlcmUgYXZlcmFnZWQgYnkgQmFyU2VxIHBpcGVsaW5lKS4gVGhlIGF2ZXJhZ2UgcmVhZHMgcGVyIGdlbmUgYXJlIGxvZzIobikgPSA4LCB3aGljaCB0cmFuc2xhdGVzIHRvIG4gPSAyXjggb3IgYXJvdW5kIDI1NiByZWFkcyBwZXIgZ2VuZS4KCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDQuMiwgbWVzc2FnZSA9IEZBTFNFfQpwbG90X3JlYWRzX3Blcl9iYyA8LSBoaXN0b2dyYW0ofiBzdHJhaW5zX3Blcl9nZW5lLAogIGRmX2ZpdG5lc3MgJT4lIHNlbGVjdChsb2N1c190YWcsIHN0cmFpbnNfcGVyX2dlbmUpICU+JSAKICAgIGRpc3RpbmN0ICU+JSBmaWx0ZXIoc3RyYWluc19wZXJfZ2VuZSA8IDQwKSwgCiAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgYnJlYWtzID0gMjAsIHhsaW09YygtMiwgNDIpLAogIHhsYWIgPSBleHByZXNzaW9uKCJtdXRhbnRzIHBlciBnZW5lIiksCiAgcGFuZWwgPSBmdW5jdGlvbih4LCAuLi4pIHsKICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgIHBhbmVsLmhpc3RvZ3JhbSh4LCBib3JkZXIgPSAid2hpdGUiLCAuLi4pCiAgICBwYW5lbC5hYmxpbmVxKHYgPSBtZWFuKHgsIG5hLnJtID0gVFJVRSksCiAgICAgIGZvbnRmYW1pbHkgPSAiRnJlZVNhbnMiLCBjb2wgPSBncmV5KDAuNSksIGx3ZCA9IDIsIGx0eSA9IDIpCiAgfQopCgpwbG90X3JlYWRzX3Blcl9nZW5lIDwtIGhpc3RvZ3JhbSh+IGxvZzIocmVhZHNfcGVyX2dlbmVfbWVkaWFuKSwKICBkZl9maXRuZXNzICU+JSBmaWx0ZXIodGltZSAhPSAwKSAlPiUgZ3JvdXBfYnkobG9jdXNfdGFnKSAlPiUKICAgIHN1bW1hcml6ZShyZWFkc19wZXJfZ2VuZV9tZWRpYW4gPSBtZWRpYW4oY291bnRzKSksCiAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgYnJlYWtzID0gMjAsCiAgeGxhYiA9IGV4cHJlc3Npb24oImxvZyJbMl0qIiByZWFkcyBwZXIgZ2VuZSAobWVkKSIpLAogIHBhbmVsID0gZnVuY3Rpb24oeCwgLi4uKSB7CiAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICBwYW5lbC5oaXN0b2dyYW0oeCwgYm9yZGVyID0gIndoaXRlIiwgLi4uKQogICAgcGFuZWwuYWJsaW5lcSh2ID0gbWVhbih4WyFpcy5pbmZpbml0ZSh4KV0pLAogICAgICBmb250ZmFtaWx5ID0gIkZyZWVTYW5zIiwgY29sID0gZ3JleSgwLjUpLCBsd2QgPSAyLCBsdHkgPSAyKQogIH0KKQoKcHJpbnQocGxvdF9yZWFkc19wZXJfYmMsIHNwbGl0ID0gYygxLDEsMiwxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfcmVhZHNfcGVyX2dlbmUsIHNwbGl0ID0gYygyLDEsMiwxKSkKYGBgCgojIEdlbmUgZml0bmVzcyBhbmFseXNpcwoKIyMgRGVwbGV0aW9uIG92ZXIgdGltZSAoZ2VuZXJhdGlvbnMpCgpXZSBjYW4gcGxvdCBsb2cyIEZDIG9yIG5vcm1hbGl6ZWQgZ2VuZSBmaXRuZXNzIG92ZXIgZ2VuZXJhdGlvbnMuIEZvciB0aGlzIHR5cGUgb2Ygb3ZlcnZpZXcgaXQgaXMgYmVzdCB0byBzdW1tYXJpemUgaW5kaXZpZHVhbCByZXBsaWNhdGVzICg0eCkgdG8gdGhlIG1lYW4gb3IgbWVkaWFuLCBwZXIgdGltZSBwb2ludCBhbmQgY29uZGl0aW9uLiBXZSBhbHNvIGFkZCBnZW5vbWUgYW5ub3RhdGlvbiB0byB0aGUgc3VtbWFyeSB0YWJsZS4gVGhlIHBsb3RzIHNob3dzIHRoYXQgZGVwbGV0aW9uIG9mIHNvbWUgc3RyYWlucyBpcyBzbyBzdHJvbmcgYWxyZWFkeSBhdCA4IGdlbmVyYXRpb25zIHRoYXQgZml0bmVzcy9sb2cyIEZDIGNvdWxkIG5vdCBiZSBxdWFudGlmaWVkIGZvciAxNiBnZW5lcmF0aW9ucyBkdWUgdG8gbWlzc2luZyByZWFkIGNvdW50cy4gSXQgaXMgYmVzdCB0byBmb2N1cyBvbiA4IGdlbmVyYXRpb25zIGFzIGJlY2F1c2UgaXQgcHJvdmlkZXMgYSBtb3JlIGNvbXBsZXRlIHBpY3R1cmUuCgpgYGB7ciwgbWVzc2FnZXMgPSBGQUxTRX0KZGZfZml0bmVzc19zdW1tYXJ5IDwtIGRmX2ZpdG5lc3MgJT4lCiAgZ3JvdXBfYnkobG9jdXNfdGFnLCBzY2FmZm9sZCwgdGltZSwgY29uZGl0aW9uLCBzdWJzdHJhdGUsIHN0cmFpbnNfcGVyX2dlbmUpICU+JQogIHN1bW1hcml6ZSgKICAgIG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiA9IG1lZGlhbihub3JtX2dlbmVfZml0bmVzcywgbmEucm0gPSBUUlVFKSwKICAgIGxvZzJGQ19tZWRpYW4gPSBtZWRpYW4obG9nMkZDLCBuYS5ybSA9IFRSVUUpLAogICAgdHN0YXRfbWVkaWFuID0gbWVkaWFuKHQsIG5hLnJtID0gVFJVRSkKICApICU+JQogIGxlZnRfam9pbihkZl9yZWYpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSA2LjUsIGZpZy5oZWlnaHQgPSA4fQpwbG90X2hpc3RfbG9nMkZDIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBmaWx0ZXIoYWxsKCFpcy5pbmZpbml0ZShsb2cyRkNfbWVkaWFuKSkpICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgc3Vic3RyYXRlKSAlPiUKICBzbGljZSgxOjIwMDApICU+JQogIAogIHh5cGxvdChsb2cyRkNfbWVkaWFuIH4gdGltZSB8IHN1YnN0cmF0ZSAqIGNvbmRpdGlvbiwgLiwKICAgIGdyb3VwcyA9IGxvY3VzX3RhZywgYXMudGFibGUgPSBUUlVFLAogICAgY29sID0gc3RkY29sWzFdLCBhbHBoYSA9IDAuMiwgbGF5b3V0ID0gYygzLCAyKSwKICAgIHhsYWIgPSAiIiwgeWxhYiA9IGV4cHJlc3Npb24oImxvZyJbMl0qIiBGQyIpLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgdHlwZSA9IGMoImwiKSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwgbHdkID0gMiwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIC4uLikKICAgIH0KICApCgpwbG90X2hpc3Rfbm9ybWZnIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBmaWx0ZXIoYWxsKCFpcy5pbmZpbml0ZShsb2cyRkNfbWVkaWFuKSkpICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgc3Vic3RyYXRlKSAlPiUKICBzbGljZSgxOjIwMDApICU+JQogIAogIHh5cGxvdChub3JtX2dlbmVfZml0bmVzc19tZWRpYW4gfiB0aW1lIHwgc3Vic3RyYXRlICogY29uZGl0aW9uLCAuLAogICAgZ3JvdXBzID0gbG9jdXNfdGFnLCBhcy50YWJsZSA9IFRSVUUsCiAgICBjb2wgPSBzdGRjb2xbMl0sIGFscGhhID0gMC4yLCBsYXlvdXQgPSBjKDMsIDIpLAogICAgeGxhYiA9ICJnZW5lcmF0aW9ucyIsIHlsYWIgPSAiZml0bmVzcyIsCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLCB0eXBlID0gYygibCIpLAogICAgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksCiAgICBzY2FsZXMgPSBsaXN0KGFsdGVybmF0aW5nID0gRkFMU0UpLCBsd2QgPSAyLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC54eXBsb3QoeCwgeSwgLi4uKQogICAgfQogICkKCnByaW50KHBsb3RfaGlzdF9sb2cyRkMsIHBvc2l0aW9uID0gYygwLDAuNDcsMSwxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfaGlzdF9ub3JtZmcsIHBvc2l0aW9uID0gYygwLDAsMSwwLjUzKSkKYGBgCgpBcyBzb3J0IG9mIGFuIGludGVybmFsIGNvbnRyb2wsIHdlIGNvbXBhcmUgdGhlIGdlbmUgZml0bmVzcyBvYnRhaW5lZCBieSBhIGNvbXBsZXggcHJvY2VkdXJlIHRvIHRoZSBsb2cyIEZDIG9mIHJlYWQgY291bnRzLCB3aGljaCBpcyBhIHZlcnkgc2ltcGxlIG1lYXN1cmUgb2YgJ2ZpdG5lc3MnLiBUaGUgdHdvIHZhcmlhYmxlcyBjb3JyZWxhdGUgd2VsbCBhbGwgdGVzdGVkIGNvbmRpdGlvbnMgYW5kIHN1YnN0cmF0ZXMuCgpgYGB7ciwgZmlnLndpZHRoID0gNi41LCBmaWcuaGVpZ2h0ID0gNS4yfQpkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgZmlsdGVyKHRpbWUgPT0gOCwgYWxsKCFpcy5pbmZpbml0ZShsb2cyRkNfbWVkaWFuKSkpICU+JQogIAogIHh5cGxvdChub3JtX2dlbmVfZml0bmVzc19tZWRpYW4gfiBsb2cyRkNfbWVkaWFuIHwgIHN1YnN0cmF0ZSAqIGNvbmRpdGlvbiwgLiwKICAgIGFzLnRhYmxlID0gVFJVRSwgY29sID0gc3RkY29sWzVdLCBwY2ggPSAxOSwKICAgIGFscGhhID0gMC40LCBjZXggPSAwLjYsCiAgICBsYXlvdXQgPSBjKDMsIDIpLCB4bGltID0gYygtOCwzKSwgeWxpbSA9IGMoLTYsNSksCiAgICB4bGFiID0gZXhwcmVzc2lvbigibG9nIlsyXSoiIEZDIiksIHlsYWIgPSAiZml0bmVzcyIsCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLAogICAgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksCiAgICBzY2FsZXMgPSBsaXN0KGFsdGVybmF0aW5nID0gRkFMU0UpLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC54eXBsb3QoeCwgeSwgLi4uKQogICAgICBwYW5lbC5sbWxpbmVxKHgsIHksIGZvbnRmYW1pbHkgPSAiRnJlZVNhbnMiLCByLnNxdWFyZWQgPSBUUlVFLCBsd2QgPSAxLjUsIAogICAgICAgIGNvbC50ZXh0ID0gMSwgcG9zID0gMywgb2Zmc2V0ID0gNSwgLi4uKQogICAgICBwYW5lbC5hYmxpbmUoYSA9IDIsIGIgPSAxLCBjb2wgPSBncmV5KDAuNSksIGx0eSA9IDIsIGx3ZCA9IDEuNSkKICAgIH0KICApCmBgYAoKIyMgQ29tcGFyaW5nIGdlbmUgZml0bmVzcyBiZXR3ZWVuIGNvbmRpdGlvbnMKClNpbWlsYXJseSB0byBGaWd1cmUgMiBpbiBbV2V0bW9yZSBldCBhbCwgbUJJTywgMjAxNV0oaHR0cHM6Ly9tYmlvLmFzbS5vcmcvY29udGVudC82LzMvZTAwMzA2LTE1KSwgd2UgY2FuIGludmVzdGlnYXRlIGNvbmRpdGlvbi1kZXBlbmRlbnQgZ2VuZSBmaXRuZXNzIGJ5IGNvbXBhcmluZyBjb25kaXRpb25zIGFuZCBzdWJzdHJhdGVzIG9uZS1vbi1vbmUuIFRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHN1YnN0cmF0ZXMgYW5kIGdyb3d0aCByZWdpbWVzIChjb250aW51b3VzLCBwdWxzZWQpIGlzIHF1aXRlIGRpZmZlcmVudC4gVGhlIHN0cm9uZ2VzdCBjb3JyZWxhdGlvbiBkb2VzIGV4aXN0IGZvciB0aGUgcHVsc2VkIHZzIGNvbnRpbnVvdXMgcmVnaW1lIGZvciBlYWNoIHN1YnN0cmF0ZSAoUiA9IDAuNjQgdG8gMC44NykuIEFuZCBmb3IgdGhlIHB1bHNlZCBjb25kaXRpb25zLCBjb21wYXJpc29uIGJldHdlZW4gc3Vic3RyYXRlcyAoUiA9IDAuNjIgdG8gMC43MikuCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDgsIG1lc3NhZ2UgPSBGQUxTRX0KZGZfZml0bmVzc19jb21wIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUgZmlsdGVyKHRpbWUgPT0gOCkgJT4lCiAgZ3JvdXBfYnkobG9jdXNfdGFnKSAlPiUgbXV0YXRlKHRzdGF0X21lZGlhbiA9IG1pbih0c3RhdF9tZWRpYW4pKSAlPiUKICBzZWxlY3QobG9jdXNfdGFnLCBjb25kaXRpb24sIHN1YnN0cmF0ZSwgbm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuLCBnZW5lX25hbWUsIENPR19Qcm9jZXNzLCB0c3RhdF9tZWRpYW4pICU+JQogIHVuaXRlKGNvbmRpdGlvbiwgY29uZGl0aW9uLCBzdWJzdHJhdGUpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjb25kaXRpb24sIHZhbHVlc19mcm9tID0gbm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGxvY3VzX3RhZykpCgpjdXN0b21fc3Bsb20oZGZfZml0bmVzc19jb21wICU+JSB1bmdyb3VwICU+JSAKICBzZWxlY3QobWF0Y2hlcygiY29udGl8cHVsc2UiKSksIGNvbCA9IGdyZXkoMC40LCAwLjQpKQpgYGAKCldlIGNhbiBhbHNvIGNvbXBhcmUgc2VsZWN0ZWQgY29uZGl0aW9ucyBkaXJlY3RseSB0byBpZGVudGlmeSBnZW5lcyBlbnJpY2hlZCBvciBkZXBsZXRlZCBpbiBzZXZlcmFsIGNvbmRpdGlvbnMuCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDgsIG1lc3NhZ2UgPSBGQUxTRX0KIyBnZW5lcmFsaXplZCBwbG90dGluZyBmdW5jdGlvbgpwbG90X2ZpdG5lc3NfY29tcCA8LSBmdW5jdGlvbihkYXRhLCB2YXJzKXsKICB4eXBsb3QoZ2V0KHZhcnNbMl0pIH4gZ2V0KHZhcnNbMV0pLCBkYXRhLAogICAgZ3JvdXBzID0gYWJzKGdldCh2YXJzWzFdKS1nZXQodmFyc1syXSkpID4gMiwgI3RzdGF0X21lZGlhbiA8IC0zCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLCBjb2wgPSBzdGRjb2xbYyg1LDEpXSwKICAgIHBjaCA9IDE5LCBhbHBoYSA9IDAuNSwgY2V4ID0gMC43LCBhc3BlY3QgPSAxLAogICAgeGxpbSA9IGMoLTcsIDQpLCB5bGltID0gYygtNywgNCksCiAgICB4bGFiID0gdmFyc1sxXSwgeWxhYiA9IHZhcnNbMl0sCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmFibGluZSh2ID0gMCwgaCA9IDAsIGNvbCA9IGdyZXkoMC41KSwgbHR5ID0gMiwgbHdkID0gMS41KQogICAgICBwYW5lbC5hYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSBncmV5KDAuNSksIGx0eSA9IDIsIGx3ZCA9IDEuNSkKICAgICAgcGFuZWwuYWJsaW5lKGEgPSAyLCBiID0gMSwgY29sID0gZ3JleSgwLjUpLCBsdHkgPSAyLCBsd2QgPSAxLjUpCiAgICAgIHBhbmVsLmFibGluZShhID0gLTIsIGIgPSAxLCBjb2wgPSBncmV5KDAuNSksIGx0eSA9IDIsIGx3ZCA9IDEuNSkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIC4uLikKICAgICAgcGFuZWwua2V5KC4uLikKICAgIH0KICApCn0KCnBsb3RfZml0bmVzc19jb21wKGRmX2ZpdG5lc3NfY29tcCwgYygiY29udGludW91c19mb3JtYXRlIiwgInB1bHNlX2Zvcm1hdGUiKSkgJT4lCiAgcHJpbnQocG9zaXRpb24gPSBjKDAsMC41LDAuNSwxKSwgbW9yZSA9IFRSVUUpCnBsb3RfZml0bmVzc19jb21wKGRmX2ZpdG5lc3NfY29tcCwgYygiY29udGludW91c19mcnVjdG9zZSIsICJwdWxzZV9mcnVjdG9zZSIpKSAlPiUKICBwcmludChwb3NpdGlvbiA9IGMoMC41LDAuNSwxLDEpLCBtb3JlID0gVFJVRSkKcGxvdF9maXRuZXNzX2NvbXAoZGZfZml0bmVzc19jb21wLCBjKCJjb250aW51b3VzX3N1Y2NpbmF0ZSIsICJwdWxzZV9zdWNjaW5hdGUiKSkgJT4lCiAgcHJpbnQocG9zaXRpb24gPSBjKDAsMCwwLjUsMC41KSwgbW9yZSA9IFRSVUUpCnBsb3RfZml0bmVzc19jb21wKGRmX2ZpdG5lc3NfY29tcCwgYygiY29udGludW91c19mb3JtYXRlIiwgImNvbnRpbnVvdXNfZnJ1Y3Rvc2UiKSkgJT4lCiAgcHJpbnQocG9zaXRpb24gPSBjKDAuNSwwLDEsMC41KSwgbW9yZSA9IFRSVUUpCmBgYAoKIyBEaWZmZXJlbnRpYWwgZml0bmVzcyBvZiBzZWxlY3RlZCBnZW5lIHNldHMKCk1vc3QgZ2VuZXMgY29ycmVsYXRlIGluIGZpdG5lc3MgdmFsdWUgYmV0d2VlbiBjb25kaXRpb25zLiBUaGF0IG1lYW5zLCBvbmx5IHNvbWUgZ2VuZXMgaGF2ZSBhICoqY29uZGl0aW9uLXNwZWNpZmljIGZpdG5lc3MgZWZmZWN0KiosIGkuZS4gaW5jcmVhc2Ugb3IgZGVjcmVhc2UgZml0bmVzcyBvZiB0aGUgcmVzcGVjdGl2ZSBzdHJhaW4gaW4gb25lIHN1YnN0cmF0ZSBvciBncm93dGggcmVnaW1lIHNwZWNpZmljYWxseS4gVGhpcyBzZWN0aW9uIGludmVzdGlnYXRlcyBzcGVjaWZpYyBnZW5lcyBhbmQgdGhlaXIgZnVuY3Rpb25zIHRoYXQgc2hvdyBzdWNoIGRpZmZlcmVudGlhbCBmaXRuZXNzIGJldHdlZW4gY29uZGl0aW9ucy4gQSBzaW1wbGUgY29tcGFyaXNvbiBvZiBvbmUgY29uZGl0aW9uIHZlcnN1cyBhbm90aGVyIHdpbGwgbm90IGJlIGhlbHBmdWwgYXMgdGhlcmUgdHdvIG1hbnkgcG9zc2libGUgY29tYmluYXRpb25zLiBUbyBpZGVudGlmeSBpbnRlcmVzdGluZyBzZXRzIG9mIGdlbmVzLCBjbHVzdGVyIGFuYWx5c2lzIGNhbiBiZSBwZXJmb3JtZWQuCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxLjV9CiMgZ2VuZXJhdGUgY29sb3JwYWxldHRlIGZvciBoZWF0bWFwCmhlYXRfY29scyA8LSBkaXZlcmdpbmdfaGNsKG4gPSA3LCBoID0gYygyNTUsIDEyKSwgYyA9IGMoNTAsIDgwKSwgbCA9IGMoMjAsIDk3KSwgcG93ZXIgPSBjKDEsIDEuMykpCiNoZWF0X2NvbHMgPC0gZGl2ZXJnaW5nX2hjbChuID0gNywgaCA9IGMoMzQwLCAxMjgpLCBjID0gYyg2MCwgODApLCBsID0gYygzMCwgOTcpLCBwb3dlciA9IGMoMC44LCAxLjUpKQojaGVhdF9jb2xzIDwtIGRpdmVyZ2luZ19oY2wobiA9IDcsIGggPSBjKDMwMCwgMTI4KSwgYyA9IGMoMzAsIDY1KSwgbCA9IGMoMjAsIDk1KSwgcG93ZXIgPSBjKDEsIDEuNCkpCgojIGNyZWF0ZSBhIG1hdHJpeCBmcm9tIHdpZGUgZml0bmVzcyBkYXRhIGZvciBwbG90dGluZyBoZWF0bWFwCm1hdF9oZWF0bWFwIDwtIGRmX2ZpdG5lc3NfY29tcCAlPiUgdW5ncm91cCAlPiUKICBmaWx0ZXIoaWZfYW55KC5jb2xzID0gbWF0Y2hlcygiY29udGl8cHVsc2UiKSwgfiAhYmV0d2VlbiguLCAtMiwgMikpKSAlPiUKICBzZWxlY3QobWF0Y2hlcygibG9jdXNfdGFnfGNvbnRpfHB1bHNlIikpICU+JQogICMgZmlsdGVyIG91dCBOQSByb3dzLCBhbmQgcmVwbGFjZSBleHRyZW1lIHZhbHVlcwogIGRyb3BfbmEgJT4lIG11dGF0ZShhY3Jvc3MobWF0Y2hlcygiY29udGl8cHVsc2UiKSwgCiAgICBmdW5jdGlvbih4KSB7eSA9IHJlcGxhY2UoeCwgeCA+IDYsIDYpOyByZXBsYWNlKHksIHkgPCAtNiwgLTYpfSkpICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAibG9jdXNfdGFnIikgJT4lCiAgYXMubWF0cml4CgojIGNyZWF0ZSBjbHVzdGVyIGZvciByZW9yZGVyaW5nCm1hdF9jbHVzdGVyIDwtIG1hdF9oZWF0bWFwICU+JSBkaXN0ICU+JSBoY2x1c3QobWV0aG9kID0gIndhcmQuRCIpCm1hdF9oZWF0bWFwIDwtIG1hdF9oZWF0bWFwW29yZGVyLmRlbmRyb2dyYW0oYXMuZGVuZHJvZ3JhbShtYXRfY2x1c3RlcikpLCBjKDEsNCwyLDUsMyw2KV0KCiMgcGxvdCBoZWF0bWFwCnBsb3RfaGVhdG1hcCA8LSBsZXZlbHBsb3QobWF0X2hlYXRtYXAsCiAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICBjb2wucmVnaW9ucyA9IGNvbG9yUmFtcFBhbGV0dGUoaGVhdF9jb2xzKSgxNiksCiAgYXQgPSBzZXEoLTYsIDYsIDEpLCBhc3BlY3QgPSAiZmlsbCIsCiAgeGxhYiA9ICIiLCB5bGFiID0gIiIsIHNjYWxlcyA9IGxpc3QoeCA9IGxpc3QoZHJhdyA9IEZBTFNFKSksCiAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCB6LCAuLi4pIHsKICAgIHBhbmVsLmxldmVscGxvdCh4LCB5LCB6LCAuLi4pCiAgICBwYW5lbC5hYmxpbmUoaCA9IDE6NSswLjUsIGNvbCA9ICJ3aGl0ZSIsIGx3ZCA9IDEuNSkKICB9CikKCnByaW50KHBsb3RfaGVhdG1hcCkKYGBgCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQojIHNpbGVudGx5IGV4cG9ydCBkZW5kcm9ncmFtCnBuZygiLi4vZmlndXJlcy9maWd1cmVfYmFyc2VxX2hlYXQucG5nIiwgd2lkdGggPSAxNjkwLCBoZWlnaHQgPSAyNTAsIHJlcyA9IDE1MCkKcHJpbnQocGxvdF9oZWF0bWFwKQpkZXYub2ZmKCkKYGBgCgpDbHVzdGVyaW5nIGFuZCBzaWxob3VldHRlIGFuYWx5c2lzIHJldmVhbGVkIHRoYXQgd2UgaGF2ZSA0IHRvIDcgY2x1c3RlcnMgd2l0aCBhY2NlcHRhYmxlIHNlcGFyYXRpb24sIHNlZSB0aGUgcGxvdCBiZWxvdy4gT2YgdGhlc2UsIHNldmVyYWwgY2x1c3RlcnMgc3RpY2sgb3V0OgoKMS4gbXV0YW50cyBzcGVjaWZpY2FsbHkgZGVwbGV0ZWQgaW4gZm9ybWF0ZSBjb25kaXRpb25zID0gZXNzZW50aWFsIGZvciBmb3JtYXRlCjIuIG11dGFudHMgZGVwbGV0ZWQgaW4gYWxsIGNvbmRpdGlvbnMgPSBlc3NlbnRpYWwgaW4gbWluaW1hbCBtZWRpdW0KMy4gbXV0YW50cyBlbnJpY2hlZCBpbiBmcnVjdG9zZSBncm93dGgsIG5ldXRyYWwgb24gb3RoZXIgc3Vic3RyYXRlcwo0LiBtdXRhbnRzIGVucmljaGVkIGluIGNvbnRpbnVvdXMgcmVnaW1lLCBtb3N0bHkgbmV1dHJhbCBpbiBwdWxzZWQgcmVnaW1lCjUuIG11dGFudHMgX3NsaWdodGx5XyBkZXBsZXRlZCBpbiBjb250aW51b3VzIHN1Y2NpbmF0ZSAtIGxvdyBwcmlvcml0eQo2LiBtdXRhbnRzIF9zbGlnaHRseV8gZGVwbGV0ZWQgaW4gY29udGludW91cyBmcnVjdG9zZSAtIGxvdyBwcmlvcml0eQo3LiBtdXRhbnRzIF9zbGlnaHRseV8gZGVwbGV0ZWQgaW4gdmFyaW91cyBjb25kaXRpb25zIC0gbG93IHByaW9yaXR5CgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA0fQpzaWxob3VldHRlUmVzdWx0IDwtIFJ0b29sczo6c2lsaG91ZXR0ZV9hbmFseXNpcyhtYXRfaGVhdG1hcCwgbWF0X2NsdXN0ZXIsIDI6MjApCnNpbGhvdWV0dGVSZXN1bHQkcGxvdC5zdW1tYXJ5CgojIHBsb3QgY29sb3JlZCBkZW5kcm9ncmFtCnBsb3QoY29sb3JfYnJhbmNoZXMoCiAgbWF0X2NsdXN0ZXIsIGsgPSA3LAogIGdyb3VwTGFiZWxzID0gVFJVRSwKICBjb2wgPSBzdGRjb2xbMTo3XQopKQoKIyBjb2xsZWN0IG5hbWVzIG9mIGVucmljaGVkL2RlcGxldGVkIGdlbmVzCmxpc3RfZW5yaWNoZWQgPC0gdmVnYW46OmN1dHJlZW9yZChtYXRfY2x1c3RlciwgayA9IDcpCmxpc3RfZW5yaWNoZWQgJT4lIHRhYmxlCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KIyBzaWxlbnRseSBleHBvcnQgZGVuZHJvZ3JhbQpwbmcoIi4uL2ZpZ3VyZXMvZmlndXJlX2JhcnNlcV9kZW5kcm8ucG5nIiwgd2lkdGggPSAxNjAwLCBoZWlnaHQgPSA3MDAsIHJlcyA9IDE1MCkKcGxvdChjb2xvcl9icmFuY2hlcygKICBtYXRfY2x1c3RlciwgayA9IDcsCiAgZ3JvdXBMYWJlbHMgPSBUUlVFLAogIGNvbCA9IHN0ZGNvbFsxOjddCikpCmRldi5vZmYoKQpgYGAKCgojIyBFbnJpY2htZW50IG9mIG11dGFudHMgb3ZlciB0aW1lIChpbmNyZWFzZWQgZml0bmVzcykKCk9uZSBvZiB0aGUgbW9zdCBwdXp6bGluZyByZXN1bHRzIGlzIHRoZSBhcHBlYXJhbmNlIG9mICh2ZXJ5KSBmYXN0IGdyb3dpbmcgbXV0YW50cyBpbiBzb21lIGNvbmRpdGlvbnMuIFRoZXNlIG11dGFudHMgcXVpY2tseSBlbnJpY2ggb3ZlciBvbmx5IDE2IGdlbmVyYXRpb25zIHRvIGEgdm9sdW1lIG9mIHVwIHRvIDIwJSBvZiBhbGwgbGlicmFyeSBtdXRhbnRzLCB0aGV5ICJ0YWtlIG92ZXIgdGhlIGN1bHR1cmUiLiBUaGlzIHBoZW5vbWVub24gd2FzIG5vdCBvYnNlcnZlZCBwcmV2aW91c2x5IGluIGV4cGVyaW1lbnRzIHdpdGggdGhlICpTeW5lY2hvY3lzdGlzKiBzcC4gUENDNjgwMyBDUklTUFJpIHJlcHJlc3Npb24gbGlicmFyeSAoWWFvIGV0IGFsLiwgTmF0dXJlIENvbW11bmljYXRpb25zLCAyMDIwKS4gVGhlIENSSVNQUmkgbGlicmFyeSBpcyBiYXNlZCBvbiB0aGUgc2FtZSBwcmluY2lwbGUgb2YgZGVwbGV0aW9uIGFuZCBlbnJpY2htZW50IG9mIG11dGFudHMgZGVwZW5kaW5nIG9uIHRoZSBhc3NvY2lhdGVkIGZpdG5lc3MuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQojIGNvbG9yIHZlY3RvciBmb3IgY29uZGl0aW9ucwpzdWJzX2NvbCA8LSBjKHN0ZGNvbFsyXSwgbGlnaHRlbihzdGRjb2xbMl0sIDAuMyksIHN0ZGNvbFszXSwKICBsaWdodGVuKHN0ZGNvbFszXSwgMC4zKSwgc3RkY29sWzRdLCBsaWdodGVuKHN0ZGNvbFs0XSwgMC4zKSkKCiMgZ2VuZXJhbGl6ZWQgZnVuY3Rpb24gZm9yIGJhcnBsb3RzCmJhcmNoYXJ0X2ZpdG5lc3MgPC0gZnVuY3Rpb24oZGF0YSkgewogICMgc29ydCBieSBjdW11bGF0aXZlIGZpdG5lc3MKICBkYXRhICU+JSBncm91cF9ieShsb2N1c190YWcpICU+JSAKICBtdXRhdGUoc3VtX2ZpdG5lc3MgPSBzdW0obm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuKSkgJT4lCiAgYXJyYW5nZShzdW1fZml0bmVzcykgJT4lCiAgbXV0YXRlKG5hbWUgPSBwYXN0ZTAobG9jdXNfdGFnLCAiIHwgIiwgZWdnTk9HX25hbWUsICIgfCAiLCAKICAgIHN1YnN0cihlZ2dOT0dfZGVzY3JpcHRpb24sIDEsIDQwKSkpICU+JQogIAogIGJhcmNoYXJ0KGZhY3RvcihuYW1lLCB1bmlxdWUobmFtZSkpIH4gbm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuLCAuLAogICAgZ3JvdXAgPSBwYXN0ZShzdWJzdHJhdGUsIGNvbmRpdGlvbiksCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLAogICAgY29sID0gc3Vic19jb2wsIGJvcmRlciA9ICJ3aGl0ZSIsIHN0YWNrID0gVFJVRSwKICAgIHhsYWIgPSAiY3VtdWxhdGl2ZSBmaXRuZXNzIiwKICAgIHNjYWxlcyA9IGxpc3QoeSA9IGxpc3QoY2V4ID0gMC43KSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmJhcmNoYXJ0KHgsIHksIC4uLikKICAgICAgcGFuZWwua2V5KC4uLiwgcG9pbnRzID0gRkFMU0UsIGNleCA9IDAuNiwgY29ybmVyID0gYygwLjk3LCAwLjAzKSkKICAgIH0KICApCn0KCmxpbmVjaGFydF9maXRuZXNzIDwtIGZ1bmN0aW9uKGRhdGEpIHsKICB4eXBsb3Qobm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuIH4gdGltZSB8IGxvY3VzX3RhZywgZGF0YSwKICAgIGdyb3VwcyA9IHBhc3RlKHN1YnN0cmF0ZSwgY29uZGl0aW9uKSwgYXMudGFibGUgPSBUUlVFLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgdHlwZSA9IGMoImwiKSwKICAgIGNvbCA9IHN1YnNfY29sLCBsd2QgPSAyLjUsCiAgICBsYXlvdXQgPSBjKDUsIGNlaWxpbmcobGVuZ3RoKHVuaXF1ZShkYXRhJGxvY3VzX3RhZykpLzUpKSwKICAgIHhsYWIgPSAiZ2VuZXJhdGlvbnMiLCB5bGFiID0gImZpdG5lc3MiLAogICAgYmV0d2VlbiA9IGxpc3QoeCA9IDAuNSwgeSA9IDAuNSksCiAgICBzY2FsZXMgPSBsaXN0KGFsdGVybmF0aW5nID0gRkFMU0UpLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC54eXBsb3QoeCwgeSwgLi4uKQogICAgfQogICkKfQpgYGAKCiMjIyMgU3RyYWlucyBlbnJpY2hlZCBvbiBmcnVjdG9zZQoKV2hhdCBpcyB0aGUgaWRlbnRpdHkgb2YgdGhlIGVucmljaGVkIGdlbmVzPyBXaGljaCBvbmVzIGFyZSBlbnJpY2hlZCBpbiBzZXZlcmFsIGNvbmRpdGlvbnM/IEZpcnN0IHdlIGlkZW50aWZ5IHRoZSBtdXRhbnRzIGhpZ2hseSBhbmQgc3BlY2lmaWNhbGx5IGVucmljaGVkIG9uIGZydWN0b3NlLCBwcmVkb21pbmFudGx5IGluIHRoZSBjb250aW51b3VzIGdyb3d0aCByZWdpbWUuCgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNC41fQpkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgZmlsdGVyKGxvY3VzX3RhZyAlaW4lIG5hbWVzKHN1YnNldChsaXN0X2VucmljaGVkLCBsaXN0X2VucmljaGVkID09IDMpKSkgJT4lCiAgZmlsdGVyKHRpbWUgPT0gOCkgJT4lCiAgYmFyY2hhcnRfZml0bmVzcygpICU+JQogIHByaW50KHBvc2l0aW9uID0gYygwLDAsMC42LDEpLCBtb3JlID0gVFJVRSkKCmRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBmaWx0ZXIobG9jdXNfdGFnICVpbiUgbmFtZXMoc3Vic2V0KGxpc3RfZW5yaWNoZWQsIGxpc3RfZW5yaWNoZWQgPT0gMykpKSAlPiUKICBsaW5lY2hhcnRfZml0bmVzcygpICU+JQogIHByaW50KHBvc2l0aW9uID0gYygwLjU4LDAsMSwxKSkKYGBgCgoqKk1lY2hhbmlzbXMgZW5hYmxpbmcgZmFzdGVyIGdyb3d0aCBvbiBmcnVjdG9zZSoqCgpUaGUgbWVjaGFuaXNtIGZvciBpbmNyZWFzZWQgZ3Jvd3RoIG9uIGZydWN0b3NlIHNlZW1zIGNsZWFybHkgZGlmZmVyZW50IGZyb20gdGhlIG1lY2hhbmlzbSBmb3IgZmFzdGVyIGdyb3d0aCBpbiBhbGwgb3RoZXIgKG1haW5seSBjb250aW51b3VzKSAgY29uZGl0aW9ucy4gRm9yIHRoZSBmcnVjdG9zZSBzZXQsIHNldmVyYWwgZ2VuZXMgYXJlIGZ1bmN0aW9uYWxseSBsaW5rZWQgc3VnZ2VzdGluZyBhIHNpbWlsYXIgbWVjaGFuaXNtIG9mIGFjdGlvbiwgCgoxLiBHZW5lcyBgSDE2X0IwNTE3YCAoYWxjb2hvbCBkZWh5ZHJvZ2VuYXNlKSBhbmQgYEgxNl9CMTkxN2AgKGFsZGVoeWRlIGRlaHlkcm9nZW5hc2UpOiBBY2NvcmRpbmcgdG8gU1RSSU5HIERCLCB0aGUgZ2VuZXMgYXJlIGNvLWxvY2F0ZWQgaS5lLiBlaXRoZXIgaW4gZGlyZWN0IHByb3hpbWl0eSBvciBpbiB0aGUgc2FtZSBvcGVyb24gaW4gYWxwaGEtLCBiZXRhLSBhbmQgZ2FtbWEtcHJvdGVvYmFjdGVyaWEsIGFuZCBldmVuIGN5YW5vYmNhdGVyaWEuIEJpb0N5YyBoYXMgMTcgcG9zc2libGUgaXNvLWVuenltZXMgaW4gKkN1cHJpYXZpZHVzKiBmb3IgdGhlIDItZW56eW1lIHBhdGh3YXkgImV0aGFub2wgZGVncmFkYXRpb24gMSI6IGV0aGFub2wgLS0+IGFjZXRhbGRlaHlkZSAtLT4gYWNldHlsLUNvQSAocmV2ZXJzZSBFdE9IIGZlcm1lbnRhdGlvbiwgb2J0YWluIDEgTkFESCBwZXIgcmVhY3Rpb24pIGNhdGFseXplZCBieSBib3RoIGdlbmVzLiBXaGF0IGNvdWxkIGJlIHRoZSBtZWNoYW5pc20gZm9yIGltcHJvdmVkIGdyb3d0aC95aWVsZCBvbiBmcnVjdG9zZT8gQ2hlbW9zdGF0cyBhcmUgbW9yZSB5aWVsZC0gdGhhbiBncm93dGggc2VsZWN0aXZlLCB0aGlzIHBhdGh3YXkgaXMgcHJvYmFibHkgZnVuY3Rpb25hbCBhbmQgc2VjcmV0ZXMgRXRPSCBhcyBhIGZlcm1lbnRhdGlvbiBwcm9kdWN0OyBLTyBvZiB0aGlzIHBhdGh3YXkgcHJvYmFibHkgaW5jcmVhc2VzIHlpZWxkLgoKMi4gVHdvIGdlbmVzIGFyZSBpbnZvbHZlZCBpbiBuaXRyYXRlIHJlc3BpcmF0aW9uL3JlZHVjdGlvbiwgYFBIRzI2OWAgb3IgbmFySywgYW5kIGBIMTZfQjIwODdgLCBhIE5hckwtZmFtaWx5IHJlc3BvbnNlIHJlZ3VsYXRvci4gTmFySyBpcyBhIG5pdHJhdGUgdHJhbnNwb3J0ZXIsIGFuZCBOYXJMIHNlbnNlcyBuaXRyYXRlIGFuZCB0aGVuIGFjdGl2YXRlcyB0cmFuc2NyaXB0aW9uIG9mIGl0cyB0YXJnZXRzIGJ5IGJpbmRpbmcgdGhlIEROQS4gQWNjb3JkaW5nIHRvIFt0aGlzIHBhcGVyIG9uIE5hckwgYnkgUnVhbnRvIGV0IGFsLiwgMjAyMF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNzQxOTA3OS8pLCAidGhlIFJlZ3Vsb24gREIgZGF0YWJhc2UgWzExXSBmb3IgdHJhbnNjcmlwdGlvbiByZWd1bGF0aW9uIGluIEUuIGNvbGkgbGlzdHMgMjYgZ2VuZSByZWd1bGF0b3J5IHJlZ2lvbnMgd2hlcmUgTmFyTCBoYXMgYSBkaXJlY3QgZWZmZWN0IG9uIHRyYW5zY3JpcHQgaW5pdGlhdGlvbiwgYW5kIGl0IGZ1bmN0aW9ucyBhcyBhbiBhY3RpdmF0b3IgYXQgMTEgb2YgdGhlc2UiLiBJdCBpcyBub3QgY2xlYXIgd2hhdCBlZmZlY3QgdGhlIEtPIG9mIG5pdHJhdGUgdHJhbnNwb3J0IGNvdWxkIGhhdmUgb24gZ3Jvd3RoIG9yIHlpZWxkLCBuaXRyYXRlIGlzIG5vdCBwYXJ0IG9mIHRoZSBtZWRpdW0uCgozLiBHZW5lcyBpbnZvbHZlZCBpbiB0cmFuc2xhdGlvbi9wcm90ZWluIGZvbGRpbmcvc3VscGh1ciBtZXRhYm9saXNtOgogICAgLSBgSDE2X0IyNTIxYCwgQWxwaGEta2V0b2dsdXRhcmF0ZS1kZXBlbmRlbnQgdGF1cmluZSBkaW94eWdlbmFzZTsgdGF1cmluZSBpcyBhIHN1bHBodXIgY2FycmllcgogICAgLSBgSDE2X0EyODYxYCwgR2x1dGF0aGlvbmUgUy10cmFuc2ZlcmFzZTsgUG9zdHRyYW5zbGF0aW9uYWwgbW9kaWZpY2F0aW9uLCBwcm90ZWluIHR1cm5vdmVyLCBjaGFwZXJvbmVzCiAgICAtIGBIMTZfQTExMzhgLCBUaGlvcmVkb3hpbjsgUG9zdHRyYW5zbGF0aW9uYWwgbW9kaWZpY2F0aW9uLCBwcm90ZWluIHR1cm5vdmVyLCBjaGFwZXJvbmVzCiAgICAtIGBIMTZfQTEzMzZgLCB5b2dBL3RkY0YsIHB1dGF0aXZlIHRyYW5zbGF0aW9uIGluaXRpYXRpb24gaW5oaWJpdG9yLHlqZyBGZmFtaWx5OyBUcmFuc2xhdGlvbiwgcmlib3NvbWFsIHN0cnVjdHVyZSBhbmQgYmlvZ2VuZXNpcwogICAgLSBgSDE2X0EyNTUzYCwgY08sIEROQSByZXBhaXIgcHJvdGVpbiBSZWNPOyBJbnZvbHZlZCBpbiBETkEgcmVwYWlyIGFuZCBSZWNGIHBhdGh3YXkgcmVjb21iaW5hdGlvbgoKNC4gU2V2ZXJhbCBvdGhlciBnZW5lIHByb2R1Y3RzIGFyZSBwcmVzdW1hYmx5IHJlZ3VsYXRvcnkgcHJvdGVpbnM6CiAgICAtIGAxNl9BMDMxMGAsIHRyYW5zY3JpcHRpb25hbCByZWd1bGF0b3IsIEdudFItZmFtaWx5OyBUcmFuc2NyaXB0aW9uCiAgICAtIGBIMTZfQjIyMTVgLCBIVEggbHlzUi10eXBlIGRvbWFpbi1jb250YWluaW5nIHByb3RlaW47IGhlbGl4IHR1cm4gaGVsaXggRE5BIGJpbmRpbmcgbW90aWYKCiMjIyMgU3RyYWlucyBlbnJpY2hlZCBvbiBmb3JtYXRlCgpIZXJlLCB0aGUgb2J2aW91cyBxdWVzdGlvbiB3aXRoIGJpb3RlY2hub2xvZ2ljYWwgaW1wb3J0YW5jZSBpcyB0byBmaW5kIG11dGFudHMgd2l0aCBoaWdoZXIgKHNwZWNpZmljKSB0b2xlcmFuY2UgdG8gZm9ybWF0ZSwgcmVnYXJkbGVzcyBpZiBpdCdzIHB1bHNlZCBvciBjb250aW51b3VzbHkgc3VwcGxpZWQuIFdlIGV4cGxpY2l0bHkgZXhjbHVkZSBtdXRhbnRzIGJlbmVmaWNpYWwgZm9yIG90aGVyIGdyb3d0aCBjb25kaXRpb25zIHRvbywgd2hpY2ggYXJlIGRlYWx0IHdpdGggaW4gdGhlIG5leHQgc2VjdGlvbi4gVGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBvZiB0aGUgcHJldmlvdXMgc2VjdGlvbiBjb3VsZCBub3QgaWRlbnRpZnkgZm9ybWF0ZS1zcGVjaWZpYyBtdXRhbnRzIGVucmljaGVkIGFmdGVyIDggZ2VuZXJhdGlvbnMuIFdlIG5lZWQgdG8gbG93ZXIgdGhlIGZpbHRlciB0aHJlc2hvbGQgdG8gZGV0ZWN0IGdlbmVzIHRoYXQgaGF2ZSBhIG1vcmUgc3VidGxlIGVucmljaG1lbnQgKGZpdG5lc3MgPj0gMiBmb3IgZm9ybWF0ZSwgYW5kIDw9IDEgZm9yIGFsbCBvdGhlciBjb25kaXRpb25zKSBhZnRlciAxNiBnZW5lcmF0aW9ucy4KCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDQuNX0KbGlzdF9maXRuZXNzX2Zvcm1hdGUgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGdyb3VwX2J5KGxvY3VzX3RhZywgc3Vic3RyYXRlLCB0aW1lKSAlPiUKICBtdXRhdGUoZm9ybWF0ZV9oaXQgPSBjYXNlX3doZW4oCiAgICBzdWJzdHJhdGUgPT0gImZvcm1hdGUiICYgbWF4KG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbikgPj0gMiB+IFRSVUUsCiAgICBzdWJzdHJhdGUgIT0gImZvcm1hdGUiICYgbWF4KG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbikgPD0gMSB+IFRSVUUsCiAgICBUUlVFIH4gRkFMU0UpCiAgKSAlPiUgZ3JvdXBfYnkobG9jdXNfdGFnLCB0aW1lKSAlPiUKICBmaWx0ZXIoYWxsKGZvcm1hdGVfaGl0KSwgbigpID09IDYpICU+JSAKICBwdWxsKGxvY3VzX3RhZykgJT4lIHVuaXF1ZQoKZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcih0aW1lID09IDgsIGxvY3VzX3RhZyAlaW4lIGxpc3RfZml0bmVzc19mb3JtYXRlKSAlPiUKICBiYXJjaGFydF9maXRuZXNzKCkgJT4lCiAgcHJpbnQocG9zaXRpb24gPSBjKDAsMCwwLjYsMSksIG1vcmUgPSBUUlVFKQoKZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcihsb2N1c190YWcgJWluJSBsaXN0X2ZpdG5lc3NfZm9ybWF0ZSkgJT4lCiAgbGluZWNoYXJ0X2ZpdG5lc3MoKSAlPiUKICBwcmludChwb3NpdGlvbiA9IGMoMC41OCwwLDEsMSkpCmBgYAoKKipNZWNoYW5pc21zIGVuYWJsaW5nIGZhc3RlciBncm93dGggb24gZm9ybWF0ZSoqCgpBY2NvcmRpbmcgdG8gU1RSSU5HIERCIHRoZXJlIGFyZSBubyBvYnZpb3VzIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGVzZSBnZW5lcyBleGNlcHQgZm9yIHRoZSBwYWlyIGBQSEczODhgIChwY2FJLCBPeG9hZGlwYXRlIENvQSB0cmFuc2ZlcmFzZSBhbHBoYSBzdWJ1bml0KSBhbmQgYEgxNl9CMTU4M2AgKHBjYUQsIDMtT3hvYWRpcGF0ZSBlbm9sLWxhY3RvbmUgaHlkcm9sYXNlKS4gQm90aCBnZW5lcyBhcmUgcGFydCBvZiB0aGUgY2F0ZWNob2wgZGVncmFkYXRpb24gcGF0aHdheSB0aGF0IGlzIGl0c2VsZiBhIGxhcmdlciBwYXJ0IG9mIGJlbnplbmUgZGVncmFkYXRpb24uIFRoZSBnZW5lIHByb2R1Y3RzIGNhdGFseXplIHR3byBzZXF1ZW50aWFsIHJlYWN0aW9ucyAzLW94b2FkaXBhdGUtZW5vbC1sYWN0b25lIC0tPiAzLW94b2FkaXBhdGUgKHBjYUQpIC0tPiAzLW94b2FkaXB5bC1Db0EgKHBjYUkpLiBUaGUgbmV4dCByZWFjdGlvbiBjYXRhbHl6ZWQgYnkgZm91ciBpc29lbnp5bWVzIG1ha2VzIHN1Y2NpbnlsLUNvQSB3aGljaCBlbmRzIHVwIGluIHRoZSBDaXRyYXRlIGN5Y2xlLiBIb3cgdGhlIEtPIG9mIHRoaXMgcGF0aHdheSB3b3VsZCBpbXByb3ZlIHRvbGVyYW5jZSB0byBmb3JtYXRlIGlzIHVuY2xlYXIuCgojIyMjIFN0cmFpbnMgZW5yaWNoZWQgb24gc3VjY2luYXRlCgpUaGlzIGlzIHNpbWlsYXIgdG8gdGhlIGFuYWx5c2lzIGFib3ZlIGZvciBmb3JtYXRlLCB3aXRoIHRoZSBleGNlcHRpb24gdGhhdCBtYW55IG11dGFudHMgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciBmaXRuZXNzIGluIHN1Y2NpbmF0ZSBjb25kaXRpb25zIGVucmljaCBvbmx5IGFmdGVyIDE2IGdlbmVyYXRpb25zLCBhbmQgb25seSBpbiB0aGUgY29udGludW91cyByZWdpbWUuIFRoaXMgc2VlbXMgYmUgYW4gYXJ0aWZhY3Qgb2YgcmVhZCBjb21wcmVzc2lvbi4gV2UgdGhlcmVmb3JlIHVzZSB0aGUgc2FtZSB0aHJlc2hvbGRzIGFzIGZvciBmb3JtYXRlIChmaXRuZXNzID49IDIgZm9yIHN1Y2NpbmF0ZSwgYW5kIDw9IDEgZm9yIGFsbCBvdGhlciBjb25kaXRpb25zKSBidXQgYWZ0ZXIgOCBpbnN0ZWFkIG9mIDE2IGdlbmVyYXRpb25zLgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNC41fQpsaXN0X2ZpdG5lc3Nfc3VjY2luYXRlIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBmaWx0ZXIodGltZSA9PSA4KSAlPiUKICBncm91cF9ieShsb2N1c190YWcsIHN1YnN0cmF0ZSkgJT4lCiAgbXV0YXRlKHN1Y2NpbmF0ZV9oaXQgPSBjYXNlX3doZW4oCiAgICBzdWJzdHJhdGUgPT0gInN1Y2NpbmF0ZSIgJiBtYXgobm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuKSA+PSAyIH4gVFJVRSwKICAgIHN1YnN0cmF0ZSAhPSAic3VjY2luYXRlIiAmIG1heChub3JtX2dlbmVfZml0bmVzc19tZWRpYW4pIDw9IDEgfiBUUlVFLAogICAgVFJVRSB+IEZBTFNFKQogICkgJT4lIGdyb3VwX2J5KGxvY3VzX3RhZykgJT4lCiAgZmlsdGVyKGFsbChzdWNjaW5hdGVfaGl0KSwgbigpID09IDYsIGxvY3VzX3RhZyAhPSAiSDE2X0EyOTEyIikgJT4lIAogIHB1bGwobG9jdXNfdGFnKSAlPiUgdW5pcXVlCgpkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgZmlsdGVyKHRpbWUgPT0gOCwgbG9jdXNfdGFnICVpbiUgbGlzdF9maXRuZXNzX3N1Y2NpbmF0ZSkgJT4lCiAgYmFyY2hhcnRfZml0bmVzcygpICU+JQogIHByaW50KHBvc2l0aW9uID0gYygwLDAsMC42LDEpLCBtb3JlID0gVFJVRSkKCmRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBmaWx0ZXIobG9jdXNfdGFnICVpbiUgbGlzdF9maXRuZXNzX3N1Y2NpbmF0ZSkgJT4lCiAgbGluZWNoYXJ0X2ZpdG5lc3MoKSAlPiUKICBwcmludChwb3NpdGlvbiA9IGMoMC41OCwwLDEsMSkpCmBgYAoKKipNZWNoYW5pc21zIGVuYWJsaW5nIGZhc3RlciBncm93dGggb24gc3VjY2luYXRlKioKCkEgZ3JvdXAgb2YgZml2ZSBoaWdobHkgcmVsYXRlZCBnZW5lcyBzaG93cyBzaWduaWZpY2FudCBlbnJpY2htZW50IGluIChjb250aW51b3VzKSBzdWNjaW5hdGU6IGBIMTZfMjA5NmAgKGRwcEExKSwgYEgxNl9BMjQwNmAgKGRwcEQyKSwgYEgxNl9BMjQwN2AgKGRwcEMyKSwgYEgxNl9BMjQwOGAgKGRwcEIyKSwgYW5kIGBIMTZfQTI0MDlgLCBkcHBBMi4gQWx0ZXJuYXRpdmUgbmFtZXMgYXJlIHllakFCQ0RFLiBBbGwgb2YgdGhlc2UgcHJvdGVpbnMgYXJlIHBhcnQgb2YgYW4gQUJDIHRyYW5zcG9ydGVyIG9mIHRoZSBQZXBUIGZhbWlseS4gUXVvdGUgZnJvbSBQRkFNIGFib3V0IHRoaXMgdHlwZSBvZiBwcm90ZWluL2RvbWFpbjogIkFsbCBjaGFyYWN0ZXJpc2VkIG1lbWJlcnMgYXBwZWFyIHRvIGJlIGludm9sdmVkIGluIHRoZSB0cmFuc3BvcnQgb2Ygb2xpZ29wZXB0aWRlcyBvciBkaXBlcHRpZGVzLiBTb21lIGFyZSBpbXBvcnRhbnQgZm9yIHNwb3J1bGF0aW9uIG9yIGFudGliaW90aWMgcmVzaXN0YW5jZS4gU29tZSBkaXBlcHRpZGUgdHJhbnNwb3J0ZXJzIGFsc28gYWN0IG9uIHRoZSBoZW1lIHByZWN1cnNvciBkZWx0YS1hbWlub2xldnVsaW5pYyBhY2lkLgpUaGUgZW5yaWNobWVudCBzZWVtcyB0byBiZSBoaWdobHkgcmVwcm9kdWNpYmxlOiBlYWNoIG9mIHRoZXNlIGdlbmVzIGhhcyA3IHRvIDE1IGRpZmZlcmVudCBpbnNlcnRpb24gbXV0YW50cywgYWxsIHNob3dpbmcgdGhlIHNhbWUgYXZlcmFnZSBwYXR0ZXJuLiBBdCBsZWFzdCB0d28gbW9yZSBnZW5lcyBhcmUgYWxzbyBpbnZvbHZlZCBpbiAoYW1pbm8gYWNpZD8pIHRyYW5zcG9ydCwgYEgxNl9BMzI4NGAgYW5kIGBIMTZfQTI1MjFgLiBUaGUgbGF0dGVyIG9uZSBpcyBhICJELWFtaW5vIGFjaWQgdHJhbnNmZXJhc2UgKEQtQUFUKSwgcmVxdWlyZWQgYnkgYmFjdGVyaWEgdG8gY2F0YWx5c2UgdGhlIHN5bnRoZXNpcyBvZiBELWdsdXRhbWljIGFjaWQgYW5kIEQtYWxhbmluZSIgKFBGQU0pLgpTdXBwb3NlZCBtZWNoYW5pc20gb2YgZW5yaWNobWVudDogR3Jvd3RoIG9uIHN1Y2NpbmF0ZSBjb3VsZCBsZWFkIHRvIGEgc2hvcnRhZ2UvaW1iYWxhbmNlIG9mIGNlcnRhaW4gYW1pbm8gYWNpZHMsIGFuZCBpbnZlc3RtZW50IGluIHVwdGFrZSBtaWdodCBnaXZlIGEgZ3Jvd3RoIGFkdmFudGFnZS4gVGhlIGdyb3d0aCBhZHZhbnRhZ2Ugd2FzIG9ubHkgdmlzaWJsZSBpbiB0aGUgY29udGludW91cyBjb25kaXRpb24sIGFuZCBpcyBwcm9iYWJseSBub3QgdW5pdmVyc2FsIGZvciBzdWNjaW5hdGUuIE5vIGdlbmUvbXV0YW50IHdhcyBlbnJpY2hlZCBpbiBib3RoIHN1Y2NpbmF0ZSBncm93dGggcmVnaW1lcy4KCiMjIyMgU3RyYWlucyBlbnJpY2hlZCBpbiBzZXZlcmFsIGNvbmRpdGlvbnMKCkEgd2VhbHRoIG9mIGdlbmVzL211dGFudHMgYXJlIGVucmljaGVkIGluIG11bHRpcGxlIG1haW5seSBwdWxzZWQgZ3Jvd3RoIGNvbmRpdGlvbnMgKGNsdXN0ZXIgNCkuCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2LjV9CmRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBmaWx0ZXIobG9jdXNfdGFnICVpbiUgbmFtZXMoc3Vic2V0KGxpc3RfZW5yaWNoZWQsIGxpc3RfZW5yaWNoZWQgPT0gNCkpKSAlPiUKICBmaWx0ZXIodGltZSA9PSA4KSAlPiUKICBiYXJjaGFydF9maXRuZXNzKCkgJT4lCiAgcHJpbnQocG9zaXRpb24gPSBjKDAsMCwwLjYsMSksIG1vcmUgPSBUUlVFKQoKZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcihsb2N1c190YWcgJWluJSBuYW1lcyhzdWJzZXQobGlzdF9lbnJpY2hlZCwgbGlzdF9lbnJpY2hlZCA9PSA0KSkpICU+JQogIGxpbmVjaGFydF9maXRuZXNzKCkgJT4lCiAgcHJpbnQocG9zaXRpb24gPSBjKDAuNTgsMCwxLDEpKQpgYGAKClRvIGxlYXJuIG1vcmUgYWJvdXQgZnVuY3Rpb25hbCByZWxhdGlvbnNoaXBzIGJldHdlZW4gZW5yaWNoZWQgZ2VuZXMvbXV0YW50cywgd2UgY2FuIHN1Ym1pdCB0aGUgZ2VuZSBsaXN0IHRvIHRoZSBTVFJJTkcgaW50ZXJhY3Rpb24gZGF0YWJhc2UgYW5kIHJldHJpZXZlIGEgbmV0d29yayBvZiBwcm9iYWJsZSBpbnRlcmFjdGlvbnMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KGdncmFwaCkKbGlicmFyeSh0aWR5Z3JhcGgpCgojIGZ1bmN0aW9uIHJldHJpZXZlIG5ldHdvcmsgaW50ZXJhY3Rpb24gZGF0YSBmcm9tIFNUUklORyBEQgojIHNlcGFyYXRlIGdlbmUgSURzIGJ5ICIlMGQiOyBzcGVjaWVzL3RheG9uIElEIGZvciBDdXByaWF2aWR1cyBuZWNhdG9yIEgxNjogMzgxNjY2CiMgKHNlZSBodHRwczovL3N0cmluZy1kYi5vcmcvY2dpL29yZ2FuaXNtcykKcmV0cmlldmVfU1RSSU5HIDwtIGZ1bmN0aW9uKGdlbmVfSUQsIHRheG9uX0lELCBtaW5fc2NvcmUgPSAwMDAwLCByZWYgPSBOVUxMKSB7CiAgZ2VuZV9saXN0IDwtIHBhc3RlKGdlbmVfSUQsIGNvbGxhcHNlID0gIiUwZCIpCiAgc3RyaW5nX2dyYXBoIDwtIHBhc3RlMCgKICAgICJodHRwczovL3N0cmluZy1kYi5vcmcvYXBpL3Rzdi9uZXR3b3JrP2lkZW50aWZpZXJzPSIsIAogICAgZ2VuZV9saXN0LCAiJnNwZWNpZXM9IiwgdGF4b25fSUQsICImcmVxdWlyZWRfc2NvcmU9IiwgbWluX3Njb3JlKSAlPiUKICByZWFkX3Rzdihjb2xfdHlwZXMgPSBjb2xzKCkpICU+JQogIG11dGF0ZShhY3Jvc3MobWF0Y2hlcygic3RyaW5nSWQiKSwgZnVuY3Rpb24oeCkgZ3N1YihwYXN0ZTAodGF4b25fSUQsICIuIiksICIiLCB4KSkpICU+JQogIGFzX3RibF9ncmFwaCgpCiAgaWYgKCFpcy5udWxsKHJlZikpIHsKICAgIGxlZnRfam9pbihzdHJpbmdfZ3JhcGgsIHJlZiwgYnkgPSAibmFtZSIpCiAgfSBlbHNlIHsKICAgIHN0cmluZ19ncmFwaAogIH0KfQoKIyBmdW5jdGlvbiB0byBzcGFjZSBsYWJlbHMgaW4gY2VydGFpbiBkaXN0YW5jZSB0byBjaXJjbGUKbnVkZ2VfY2lyY2xlIDwtIGZ1bmN0aW9uKG4sIHNpemVfeCA9IDAuMTUsIHNpemVfeSA9IDAuMSkgewogIG51ZGdlX3ggPSBzaXplX3ggKiAtY29zKHNlcSgwLjUqcGksIDIuNSpwaSwgbGVuZ3RoLm91dCA9IG4pKQogIG51ZGdlX3kgPSBzaXplX3kgKiBzaW4oc2VxKDAuNSpwaSwgMi41KnBpLCBsZW5ndGgub3V0ID0gbikpCiAgbGlzdCh4ID0gbnVkZ2VfeCwgeSA9IG51ZGdlX3kpCn0KYGBgCgoKYGBge3IsIGZpZy53aWR0aCA9IDguNSwgZmlnLmhlaWdodCA9IDQuOH0KZ3JhcGhfYWxsX2VucmljaCA8LSByZXRyaWV2ZV9TVFJJTkcoCiAgZ2VuZV9JRCA9IG5hbWVzKHN1YnNldChsaXN0X2VucmljaGVkLCBsaXN0X2VucmljaGVkID09IDQpKSwKICB0YXhvbl9JRCA9IDM4MTY2NiwKICByZWYgPSByZW5hbWUoZGZfcmVmLCBuYW1lID0gbG9jdXNfdGFnKQopCgpncmFwaF9hbGxfZW5yaWNoICU+JSBhcnJhbmdlKENPR19Qcm9jZXNzKSAlPiUgYWN0aXZhdGUoZWRnZXMpICU+JSAKICBmaWx0ZXIoc2NvcmUgPj0gMC40KSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gJ2xpbmVhcicsIGNpcmN1bGFyID0gVFJVRSkgKwogIGdlb21fZWRnZV9hcmMoY29sb3VyID0gZ3JleSgwLjYsIDAuNSksIGFlcyh3aWR0aCA9IHNjb3JlKSkgKyAKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG91ciA9IENPR19Qcm9jZXNzKSwgc2l6ZSA9IDUpICsKICBnZW9tX25vZGVfdGV4dChudWRnZV94ID0gbnVkZ2VfY2lyY2xlKDIyKSR4LCBudWRnZV95ID0gbnVkZ2VfY2lyY2xlKDIyKSR5LCAKICAgIHNpemUgPSAzLjIsIGFlcyhsYWJlbCA9IGVnZ05PR19uYW1lLCBjb2xvdXIgPSBDT0dfUHJvY2VzcykpICsKICBzY2FsZV9lZGdlX3dpZHRoKHJhbmdlID0gYygwLjIsIDIpKSArCiAgdGhlbWVfZ3JhcGgoYmFja2dyb3VuZCA9ICJ3aGl0ZSIsIGZvcmVncm91bmQgPSBncmV5KDAuNSksCiAgICBwbG90X21hcmdpbiA9IG1hcmdpbigxMCwgMTAsIDEwLCAxMCkpCmBgYAoKKipNZWNoYW5pc21zIGVuYWJsaW5nIGZhc3RlciBncm93dGggaW4gc2V2ZXJhbCBjb25kaXRpb25zKioKClRoZSBwaWN0dXJlIGZvciB0aGVzZSBzdHJhaW5zIGlzIG1vcmUgY2xlYXIgdGhlbiBmb3IgdGhlIGZydWN0b3NlLWVucmljaGVkIHN0cmFpbnMuIDEzIG91dCBvZiAyNyBlbnJpY2hlZCBnZW5lcyAoNDglKSwgYW1vbmcgdGhlbSBzb21lIG9mIHRoZSBtb3N0IGVucmljaGVkLCBhcmUgZGlyZWN0bHkgaW52b2x2ZWQgaW4gaHlkcm9nZW5hc2UgYWN0aXZpdHksIGkuZS4gaHlkcm9nZW4gb3h5Z2VuYXRpb24gKGhveEEsIGhveEYsIGhveFUsIGhveFgsIGh5cEEsIGh5cEIsIGh5cEMsIGh5cEQgaHlwRSwgaHlwRiwgaHlhQSwgaHlhQiwgaHVwSCkuIFRoZSBtZWNoYW5pc20gb2YgZW5hYmxpbmcgZmFzdGVyIGdyb3d0aCBieSBLTyBvZiBoeWRyb2dlbmFzZXMgaXMgdW5rbm93bi4KClRoZSBzZWNvbmQgdGhlbWUgaXMgZW5yaWNobWVudCBvZiBwdHNJIChgSDE2X0EwMzI2YCkgYW5kIHB0c0ggKGBIMTZfQTAzMjVgKSwgdG9nZXRoZXIgdGhlIGVudGlyZSBmdW5jdGlvbmFsIHVuaXQgb2YgdGhlIFBUUyBzeXN0ZW0sIHRoZSBQRVAtZGVwZW5kZW50IHN1Z2FyIHBob3NwaG90cmFuc2ZlcmFzZSBzeXN0ZW0gKHN1Z2FyIFBUUykuICJUaGlzIG1ham9yIGNhcmJvaHlkcmF0ZSBhY3RpdmUtdHJhbnNwb3J0IHN5c3RlbSBjYXRhbHl6ZXMgdGhlIHBob3NwaG9yeWxhdGlvbiBvZiBpbmNvbWluZyBzdWdhciBzdWJzdHJhdGVzIGNvbmNvbWl0YW50bHkgd2l0aCB0aGVpciB0cmFuc2xvY2F0aW9uIGFjcm9zcyB0aGUgY2VsbCBtZW1icmFuZS4gRW56eW1lIEkgdHJhbnNmZXJzIHRoZSBwaG9zcGhvcnlsIGdyb3VwIGZyb20gKFBFUCkgdG8gdGhlIHBob3NwaG9yeWwgY2FycmllciBwcm90ZWluIChIUHIpLiIKV2h5IGRvZXMgdGhlIEtPIG9mIFBUUyBzeXN0ZW0gcHJvdmlkZXMgYSBncm93dGggYmVuZWZpdCBpbiAqaW4gYWxsIGNvbmRpdGlvbnMqIHdpdGggYWxtb3N0IGVxdWFsIGNvbnRyaWJ1dGlvbj8gTXVzdCBiZSByZWxhdGVkIHRvIGVuZXJneSBtZXRhYm9saXNtLCBiZWNhdXNlIG5laXRoZXIgZnJ1Y3Rvc2UgKEFCQyB0cmFuc3BvcnRlciksIHN1Y2NpbmF0ZSBhbmQgZGVmIG5vdCBmb3JtYXRlIGFyZSB0cmFuc3BvcnRlZCB2aWEgYSBQVFMuCgpPdGhlciBpbnRlcmVzdGluZyBtZWNoYW5pc21zOiBLTyBvZiBSTkEgcG9seW1lcmFzZSBzaWdtYSBmYWN0b3IgUnBvUyAoYEgxNl9BMjM3M2ApIHNlZW1zIHRvIGhhdmUgYSBiZW5lZmljaWFsIGVmZmVjdCBvbiBzdGVhZHkgc3RhdGUgZ3Jvd3RoLCBhcyB3ZWxsIGFzIG90aGVyIHN0cmVzcyByZWxhdGVkIHRyYW5zY3JpcHRpb24gZmFjdG9ycyBsaWtlIGNvbGQgc2hvY2sgcHJvdGVpbiBjYXBCIChgSDE2X0IyMjA1YCksIGFuZCBzY29GIChgSDE2X0IwMDAyYCkuCgpUaGUgZ2VuZSBgSDE2X0IyMDQzYCBpcyBieSBmYXIgbW9zdCBhYnVuZGFudCAoYnkgcmVhZHMpIGFuZCBtb3N0IGVucmljaGVkIChieSBmaXRuZXNzKSBzaW5nbGUgbXV0YW50IGluIGFsbW9zdCBhbGwgY29uZGl0aW9ucy4gV2hhdCBpcyB0aGUgbWVjaGFuaXNtPyBUaGVyZSBpcyBhbG1vc3Qgbm90aGluZyBrbm93biBhYm91dCB0aGlzIGdlbmUgZXhjZXB0IHRoYXQgaXQgcHJvYmFibHkgaGFzIGN5Y2xpYy1ndWFueWxhdGUtc3BlY2lmaWMgcGhvc3Bob2RpZXN0ZXJhc2UgYWN0aXZpdHkuIFRoaXMgZW56eW1lIGhhcywgYWNjb3JkaW5nIHRvIFBGQU0sIHR3byBkb21haW5zLCB0aGUgZGlndWFueWxhdGUgY3ljbGFzZSAoR0dERUYpIGFuZCB0aGUgRUFMIGRvbWFpbi4gVGhlIGZpcnN0IG9uZSBzeW50aGVzaXplcyBjeWNsaWMgZGktR01QIFtSeWplbmtvdiBldCBhbC4sIEogQmFjdCwgMjAwNV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DMTA2NDAxNi8pLiBUaGUgc2Vjb25kIGRvbWFpbiBjYW4gYWN0IGFzIHBob3NwaG9kaWVzdGVyYXNlIGFuZCBjbGVhdmUgY3ljbGljIGNBTVAgb3IgY0dNUC4gQm90aCBjb21wb3VuZHMgcGxheSBhIHJvbGUgaW4gc2lnbmFsaW5nLCBjQU1QIGZvciBleGFtcGxlIGFzIHRoZSBob3Jtb25lIGluIGNhdGFib2xpdGUgcmVwcmVzc2lvbi4gQWNjb3JkaW5nIHRvIHRoZSBkaXNjdXNzaW9uIGluIHRoZSBsaW5rZWQgcGFwZXIsICJNdXRhdGlvbnMgaW4gdGhlIEdHREVGIGRvbWFpbiBwcm90ZWlucyBvciBvdmVyZXhwcmVzc2lvbiBvZiBzdWNoIHByb3RlaW5zIGFmZmVjdCBleG9wb2x5c2FjY2hhcmlkZSBzeW50aGVzaXMgWy4uLl0gYW5kIGZvcm1hdGlvbiBvZiBiaW9maWxtcy4gSW4gQy4gY3Jlc2NlbnR1cywgZmxhZ2VsbHVtIGVqZWN0aW9uLCB3aGljaCBpcyByZXF1aXJlZCBmb3IgdGhlIHN3aXRjaCBmcm9tIG1vdGlsZSB0byBzZXNzaWxlIGxpZmVzdHlsZSwgaXMgaW1wYWlyZWQgaW4gYSBtdXRhdGVkIEdHREVGIGRvbWFpbiIuIFRoZSB1bmRlcmx5aW5nIG1lY2hhbmlzbSBjb3VsZCBiZSBkaXNydXB0ZWQgYmlvZmlsbSBmb3JtYXRpb24sIGhpZ2hlciBmbGFnZWxsdW0gYWN0aXZpdHksIGFuZCB0aHVzIG5vbi1zZXR0bGluZyBwaGVub3R5cGUgaW4gdGhlIGJpb3JlYWN0b3IuIEFsdGVybmF0aXZlbHkgdGhlIG11dGF0aW9uIGFmZmVjdHMgY0FNUCBmb3JtYXRpb24gYW5kIGRpc3J1cHRzIG9yIGVuaGFuY2VzIGNhdGFib2xpdGUgcmVwcmVzc2lvbiAobW9yZSBsaWtlbHkgdGhhdCBkaXNydXB0ZWQgY2F0YWJvbGl0ZSByZXByZXNzaW9uIGlzIGJlbmVmaWNpYWwpLgoKCiMjIyMgU2FuaXR5IGNoZWNrcwoKQWxsIGFib3ZlIHJlc3VsdHMgYXJlIGJhc2VkIG9uIGZpdG5lc3Mgc2NvcmUsIHdoaWNoIGlzIG5vdCBtdWNoIG1vcmUgdGhhbiBhIG5vcm1hbGl6ZWQgbG9nMiBmb2xkIGNoYW5nZSBvZiByZWFkIGNvdW50IG92ZXIgdGltZS4KV2UgY2FuIGNvbXBhcmUgaWYgdGhlIG11dGFudHMgd2l0aCBleHRyZW1lbHkgaGlnaCBmaXRuZXNzIHNjb3JlcyBhcmUgYWxzbyB0aGUgb25lcyB0aGF0IGFyZSBzdXBlci1hYnVuZGFudCBhdCB0aGUgZmluYWwgdGltZSBwb2ludCwgaWUuZS4gaGF2ZSBhbiBhdmVyYWdlIHJlYWQgY291bnQgb2YgPj0gNTAwLDAwMCBhZnRlciAxNiBnZW5lcmF0aW9ucy4gSW4gZmFjdCwgb25seSAzIGdlbmVzIGhhdmUgc3VjaCBhbiBleHRyZW1lIHJlYWQgY291bnQsIGJ1dCA5IGhhdmUgbW9yZSB0aGFuIDEwMCwwMDAgcmVhZHMuCgpgYGB7cn0KZGZfZml0bmVzcyAlPiUgZmlsdGVyKHRpbWUgPT0gMTYsIGxvY3VzX3RhZyAlaW4lIChzdWJzZXQobGlzdF9lbnJpY2hlZCwgbGlzdF9lbnJpY2hlZCAlaW4lIGMoMyw0KSkgJT4lIG5hbWVzKSkgJT4lCiAgZ3JvdXBfYnkobG9jdXNfdGFnKSAlPiUgc3VtbWFyaXplKG1heF9jb3VudHMgPSBtYXgoY291bnRzKSwgbWF4X2ZpdG5lc3MgPSBtYXgobm9ybV9nZW5lX2ZpdG5lc3MpKSAlPiUKICBhcnJhbmdlKGRlc2MobWF4X2NvdW50cykpCmBgYAoKT24gdGhlIG90aGVyIGhhbmQsIHdlIGNhbiBjaGVjayB3aGljaCBnZW5lcyBoYXZlIGFuIGV4dHJlbWVseSBoaWdoIHJlYWQgY291bnQgKD49IDUwMCwwMDAgaW4gKmFueSogY29uZGl0aW9uKSBhbmQgc2VlIGlmIHRoaXMgY29ycmVsYXRlcyB3aXRoIGhpZ2ggZml0bmVzcyBzY29yZS4gSXQgZG9lcyBwYXJ0bHkuIDMgb3V0IG9mIDcgaGlnaGx5IGFidW5kYW50IGdlbmUgbXV0YW50cyBlbnJpY2ggZXh0cmVtZWx5IG92ZXIgdGltZS4gVGhlIG90aGVyIGVucmljaCB0b28sIGJ1dCBvbmx5IDJeMSA9IDIgdG8gMl4zLjUgPSAxMSB0aW1lcy4gVGhlc2Ugbm9uLWVucmljaGluZyBzdXBlci1hYnVuZGFudCBtdXRhbnRzIGFyZToKCiAgLSBgSDE2X0EwNzc0YCAtIGNwaEEsIEN5YW5vcGh5Y2luIHN5bnRoYXNlLiBTdXBwb3NlZCBtZWNoYW5pc206IGVucmljaGVzIGluIExCIG1lZGl1bSBhcyBpdCBkb2VzIG5vdCB0dXJuIEFzcAogICAgaW50byBjeWFub3BoeWNpbi4gTm8gYmVuZWZpdCBvbiBtaW5pbWFsIG1lZGl1bS4KICAtIGBIMTZfQjI1NzBgIC0gZmVjQSwgT3V0ZXIgbWVtYnJhbmUgcmVjZXB0b3IgZm9yIEZlKElJSSkuIFN1cHBvc2VkIG1lY2hhbmlzbTogcmVndWxhdG9yIGFjdGl2aXR5IHRvZ2V0aGVyIHdpdGggZmVjUiwgYnV0IGFsc28gCiAgICB0cmFuc21lbWJyYW5lIHRyYW5zcG9ydGVyIGZvciBzaWRlcm9waG9yZXMuIEtPIHJlZHVjZXMgc2Vuc2l0aXZ0eSB0byBleGNlc3MgaXJvbj8/CiAgLSBgSDE2X0EzMTQ1YCAtIENvbnNlcnZlZCBwcm90ZWluL2RvbWFpbiB0eXBpY2FsbHkgYXNzb2NpYXRlZCB3aXRoIGZsYXZvcHJvdGVpbiBveHlnZW5hc2VzLCBESU02L05UQUIgZmFtaWx5LiAKICAgIFN1cHBvc2VkIG1lY2hhbmlzbTogdW5rbm93bi4KCmBgYHtyfQpkZl9maXRuZXNzICU+JSBmaWx0ZXIodGltZSA9PSAxNiwgY291bnRzID4gNSoxMF41KSAlPiUKICBncm91cF9ieShsb2N1c190YWcpICU+JSBzdW1tYXJpemUobWF4X2NvdW50cyA9IG1heChjb3VudHMpLCBtYXhfZml0bmVzcyA9IG1heChub3JtX2dlbmVfZml0bmVzcykpICU+JQogIGFycmFuZ2UoZGVzYyhtYXhfY291bnRzKSkKYGBgCgoKIyMgRGVwbGV0aW9uIG9mIG11dGFudHMgb3ZlciB0aW1lIChyZWR1Y2VkIGZpdG5lc3MpCgojIyMjIERlcGxldGlvbiBvbiBmb3JtYXRlCgpTZXZlcmFsIGdlbmVzL211dGFudHMgYXJlIGRlcGxldGVkIG92ZXIgdGltZSBkZXBlbmRpbmcgb24gZ3Jvd3RoIGNvbmRpdGlvbi4gTW9zdCBpbnRlcmVzdGluZyBpbiB0aGlzIGNvbnRleHQgYXJlIGNsdXN0ZXIgMSBhbmQgMiwgZGVwbGV0aW9uIG9ubHkgb24gZm9ybWF0ZSwgb3IgaW4gYWxsIGNvbmRpdGlvbnMsIHJlc3BlY3RpdmVseS4gTm90IHN1cnByaXNpbmdseSwgZm9ybWF0ZS1zcGVjaWZpYyBnZW5lcyBhcmUgaGlnaGx5IGRlcGxldGVkIGR1cmluZyBncm93dGggb24gZm9ybWF0ZSwgYnV0IG5vdCBjYmIgKENhbHZpbiBjeWNsZSkgZ2VuZXMuCgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNi41fQpkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgZmlsdGVyKGxvY3VzX3RhZyAlaW4lIG5hbWVzKHN1YnNldChsaXN0X2VucmljaGVkLCBsaXN0X2VucmljaGVkID09IDEpKSkgJT4lCiAgZmlsdGVyKHRpbWUgPT0gMTYpICU+JQogIGJhcmNoYXJ0X2ZpdG5lc3MoKSAlPiUKICBwcmludChwb3NpdGlvbiA9IGMoMCwwLDAuNiwxKSwgbW9yZSA9IFRSVUUpCgpkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgZmlsdGVyKGxvY3VzX3RhZyAlaW4lIG5hbWVzKHN1YnNldChsaXN0X2VucmljaGVkLCBsaXN0X2VucmljaGVkID09IDEpKSkgJT4lCiAgbGluZWNoYXJ0X2ZpdG5lc3MoKSAlPiUKICBwcmludChwb3NpdGlvbiA9IGMoMC41OCwwLDEsMSkpCmBgYAoKV2hhdCBpcyB0aGUgcm9sZSBvZiBmb3JtYXRlLWVzc2VudGlhbCBnZW5lcz8gQXJlIHRoZXJlIHBhdHRlcm5zIHRoYXQgZW1lcmdlPyBXZSBjYW4gc3VibWl0IHRoZSBkZXBsZXRlZCBnZW5lIGxpc3QgdG8gU1RSSU5HIERCIGFuZCByZXRyaWV2ZSBhIHBvc3NpYmxlIG5ldHdvcmsgb2YgaW50ZXJhY3Rpb25zLiBUaGUgbmV0d29yayBzaG93cyBpbW1lZGlhdGVseSB0aGF0IHRoZXJlIGFyZSBtYW55IGhpZ2gtc2NvcmluZyBpbnRlcmFjdGlvbnMgYmV0d2VlbiB0aGUgZ2VuZXMsIHBhcnRpY3VsYXJseSB0aGUgc29sdWJsZSBGREggZ2VuZXMgYXJlIHN0aWNraW5nIG91dCBhcyBvbmUgY2x1c3RlciAoZmRzQUJERykgYW5kIHRoZWlyIHRyYW5zY3JpcHRpb25hbCByZWd1bGF0b3IgZmRzUi4gVGhlbiB0aGUgbW9seWJkZW51bSBjb2ZhY3RvciBwcm9jZXNzaW5nIHByb3RlaW5zIG1vYUNERSwgbW9lQSwgbW9iQSwgbW9nLiBBbmQgYSByYW5nZSBvZiBjeXRvY2hyb21lIGdlbmVzIHJlc3BvbnNpYmxlIGZvciBhY2NlcHRhbmNlIGFuZCB0cmFuc3BvcnQgb2Ygb2YgZWxlY3Ryb25zIGZyb20gZm9ybWF0ZSB0byB0aGUgRVRDIGluIGN5dG9wbGFzbWljIG1lbWJyYW5lIChwZXRBQkMsIGNjb0dOT1AsIGN5YykuIEl0IGlzIGFsc28gaW50ZXJzdGluZyB3aGF0IGlzIE5PVCBlc3NlbnRpYWwgb24gZm9ybWF0ZSwgc3VjaCBhcyBubyBjYmIgZ2VuZXMgZXhjZXB0IHRoZSBtYXN0ZXIgdHJhbnNjcmlwdGlvbmFsIHJlZ3VsYXRvciBjYmJSLiBUaGUgcmVhc29uIGZvciB0aGlzIG11c3QgYmUgdGhlIHJlZHVuZGFuY3kgb2YgYWxsIGNiYiBnZW5lcyAodHdvIG9wZXJvbnMpLgoKCmBgYHtyLCBmaWcud2lkdGggPSA4LjUsIGZpZy5oZWlnaHQgPSA0Ljh9CmdyYXBoX2Zvcm1hdGVfZGVwbCA8LSByZXRyaWV2ZV9TVFJJTkcoCiAgZ2VuZV9JRCA9IG5hbWVzKHN1YnNldChsaXN0X2VucmljaGVkLCBsaXN0X2VucmljaGVkID09IDEpKSwKICB0YXhvbl9JRCA9IDM4MTY2NiwKICByZWYgPSByZW5hbWUoZGZfcmVmLCBuYW1lID0gbG9jdXNfdGFnKQopCgpncmFwaF9mb3JtYXRlX2RlcGwgJT4lIGFycmFuZ2UoQ09HX1Byb2Nlc3MpICU+JSBhY3RpdmF0ZShlZGdlcykgJT4lIAogIGZpbHRlcihzY29yZSA+PSAwLjQpICU+JQogIGdncmFwaChsYXlvdXQgPSAnbGluZWFyJywgY2lyY3VsYXIgPSBUUlVFKSArCiAgZ2VvbV9lZGdlX2FyYyhjb2xvdXIgPSBncmV5KDAuNiwgMC41KSwgYWVzKHdpZHRoID0gc2NvcmUpKSArIAogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3VyID0gQ09HX1Byb2Nlc3MpLCBzaXplID0gNSkgKwogIGdlb21fbm9kZV90ZXh0KG51ZGdlX3ggPSBudWRnZV9jaXJjbGUoMzQpJHgsIG51ZGdlX3kgPSBudWRnZV9jaXJjbGUoMzQpJHksCiAgICBzaXplID0gMy4yLCBhZXMobGFiZWwgPSBlZ2dOT0dfbmFtZSwgY29sb3VyID0gQ09HX1Byb2Nlc3MpKSArCiAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IGMoMC4yLCAyKSkgKwogIHRoZW1lX2dyYXBoKGJhY2tncm91bmQgPSAid2hpdGUiLCBmb3JlZ3JvdW5kID0gZ3JleSgwLjUpLAogICAgcGxvdF9tYXJnaW4gPSBtYXJnaW4oMTAsIDEwLCAxMCwgMTApKQpgYGAKCkxldCdzIGNyZWF0ZSBhIHBhdGh3YXkvb3Blcm9uIGJhc2VkIG92ZXJ2aWV3IGFib3V0IHRoZSBmb3JtYXRlLWVzc2VudGlhbCBnZW5lcy4gV2UgZ3JvdXAgdGhlIGdlbmVzIGludG8gdGhyZWUgY2F0ZWdvcmllcywgYW5kIGFsc28gaW5jbHVkZSB0aGUgZ2VuZXMvcHJvdGVpbnMgdGhhdCBiZWxvbmcgdG8gZWFjaCBncm91cCBidXQgd2VyZSBub3QgZGVwbGV0ZWQgb3IgZW5yaWNoZWQuIFRoZSB0aHJlZSBncm91cHMgYXJlOgoKLSAqKm1vbHliZGVudW0gY29mYWN0b3IgYmlvc3ludGhlc2lzKioKICAtIG1vZEFCID0gbW9seWJkZW51bSB0cmFuc3BvcnRlcgogIC0gbW9hQUNERSA9IG1vbHliZG9wdGVyaW4gYmFja2JvbmUgYmlvc3ludGhlc2lzCiAgLSBtb2VBID0gbW9seWJkb3B0ZXJpbiBtb2x5YmRlbnVtIHRyYW5zZmVyYXNlCiAgLSBtb2JBQiA9IG1vbHliZGVudW0gY29mYWN0b3IgZ3VhbnlseWx0cmFuc2ZlcmFzZSwgCiAgLSBtb2cgPSBtb2x5YmRvcHRlcmluIGJpb3N5bnRoZXNpcyBwcm90ZWluIChyb2xlPykKLSAqKmZvcm1hdGUgZGVoeWRyb2dlbmFzZSoqCiAgLSBzb2x1YmxlIChmZHMsIGZ3ZCkKICAtIG1lbWJyYW5lLWJvdW5kIChmZGgsIGZkbykKLSAqKmVsZWN0cm9uIHRyYW5zcG9ydCBjaGFpbioqCiAgLSBwZXRBQkMgKGFrYSBxY3JBQkMsIGBIMTZfQTMzOThgLCBgSDE2X0EzMzk3YCwgYEgxNl9BMzM5NmApID0gY3ljb2Nocm9tZSBiYzEgKHJlZHVjdGFzZSkKICAtIGN5YyAoYEgxNl9BMzQ1MWApLCBjeXRvY2hyb21lIGM1NTMuIE90aGVyIGN5dG9jaHJvbWUgYzU1MyBnZW5lcyB0byBpbmNsdWRlIGFjY29yZGluZyB0byB1bmlwcm90OgogICAgYEgxNl9BMDgzMGAsIGBIMTZfQTA4NDZgLCBgSDE2X0IxNDUyYCwgYEgxNl9BMzU3NmAsIGBIMTZfQTExMjFgLCBgSDE2X0ExMTIwYAogIC0gY2NvR05PUCAoYEgxNl9BMjMxNmAsIGBIMTZfQTIzMTdgLCBgSDE2X0EyMzE4YCwgYEgxNl9BMjMxOWApLCBjeXRvY2hyb21lIEMgb3hpZGFzZSAoY2JiMyB0eXBlKQogIC0gVlAyNjQxIChgSDE2X0ExMDMxYCkgPSBNZW1icmFuZSBzcGFubmluZyBwcm90ZWluLCBSREQgZmFtaWx5LiBQRkFNOiB1bmtub3duIGZ1bmN0aW9uLCBwb3RlbnRpYWwgdHJhbnNwb3J0ZXI/IEludGVyYWN0cyB3aXRoIGN5dG9jaHJvbWUgYzU1MyBhbmQgcWNyQywgY3l0b2Nocm9tZSByZWR1Y3Rhc2UuIEl0J3MgZGVwbGV0aW9uIGltcGxpY2F0ZXMgdGhpcyBwcm90ZWluIGluIGZvcm1hdGUtc3BlY2lmaWMgZS0gdHJhbnNwb3J0IGluIHRoZSBtZW1icmFuZS4KCldlIGNhbiBwcm92ZSB0aGF0IHRoZXNlIGNvbXBsZXhlcyBhcmUgbW9zdGx5IG9yIG9ubHkgcmVxdWlyZWQgZHVyaW5nIGdyb3d0aCBvbiBmb3JtYXRlLiBDYW4gd2UgZXhjbHVkZSBvdGhlciBjeXRvY2hyb21lIHJlZHVjdGFzZSBhbmQgY3l0b2Nocm9tZSBveGlkYXNlIGNvbXBsZXhlcywgb3Igb3RoZXIgY3l0b2Nocm9tZSBjNTUzIHByb3RlaW5zIHRoYXQgYWN0IGFzIGUtIGFjY2VwdG9ycyBmcm9tIEZESD8gVGhpcyB0eXBlIG9mIGluZm9ybWF0aW9uIGNhbiBiZSB1c2VkIHRvIGNhcnZlIG91dCB0aGUgc3BlY2lmaWMgcGF0aCBvZiBlLSBmcm9tIGZvcm1hdGUgdGhyb3VnaCB0aGUgRVRDLiBJZiBvdGhlciBjeXRvY2hyb21lIG94aWRhc2UvcmVkdWN0YXNlIHByb3RlaW5zIGFyZSBlc3NlbnRpYWwgaW4gYWxsIGNvbmRpdGlvbnMsIGl0IGNhbiBub3QgYmUgZXhjbHVkZWQgdGhhdCB0aGV5IGFyZSBub3QgcmVxdWlyZWQgZm9yIGdyb3d0aCBvbiBmb3JtYXRlLiBXZSB0aGVyZWZvcmUgYWxzbyBpbmNsdWRlIGFsdGVybmF0aXZlIGN5dG9jaHJvbWUgb3hpZGFzZXMsIGxpa2UgY3RhQUJDREVHIGFuZCBjb3hNTk9QLCBjeW8gYW5kIGN5ZCBvcGVyb25zLgoKVG8gc3VtbWFyaXplLCBhY2NvcmRpbmcgdG8gdGhlIGVzc2VudGlhbGl0eSBkYXRhIG9uIGZvcm1hdGUsIGVsZWN0cm9ucyBmcm9tIGZvcm1hdGUgYXJlIHRyYW5zcG9ydGVkIHRocm91Z2ggdGhlIG1lbWJyYW5lIHRoZSBmb2xsb3dpbmcgd2F5OgoKYGBgClMtRkRIIC0tPiBOQURIIC0tfAogICAgICAgICAgICAgICAgIHwKTS1GREggLS0+IEN5dG9jaHJvbWUgQyByZWR1Y3Rhc2UgKGJjMSBjb21wbGV4IElJSSkgLS0+IEN5dG9jaHJvbWUgYzU1MyAoKSAtLT4gQ3l0b2Nocm9tZSBDIG94aWRhc2UgKGNiYjMgY29tcGxleCBJVikgLS0+IE8yCmBgYAoKV2hhdCBpcyBrbm93biBpbiBsaXRlcmF0dXJlIGFib3V0IGVsZWN0cm9uIHRyYW5zcG9ydCBmcm9tIGZvcm1hdGUgdG8gbWVtYnJhbmU/CgotIG1haW4gRkRIIGlzIFMtRkRIIGJvdGggaW4gdGVybXMgb2YgcHJvdGVpbiBhYnVuZGFuY2UsIGluZHVjaWJpbGl0eSwgYW5kIGVzc2VudGlhbGl0eQotIGN5dG9jaHJvbWUgcmVkdWN0YXNlIGJjMSBjb21wbGV4IChxY3JBQkMpIGlzICoqcGFydGljdWxhcmx5IGltcG9ydGFudCoqIGR1cmluZyBncm93dGggb24gZm9ybWF0ZSAodGhpcyBzdHVkeSkKLSBpdCBpcyBhbHNvICoqYmVuZWZpY2lhbCoqIGR1cmluZyBoZXRlcm90cm9waGljIGdyb3d0aCAoc2xpZ2h0bHkgcmVkdWNlZCBmaXRuZXNzKQotIGluaGliaXRpb24gb2YgdGhpcyBjb21wbGV4IHJlZHVjZXMgZ3Jvd3RoIGluIGhldGVyb3Ryb3BoeSBhbmQgbGl0aG90cm9waHkgKHNlZSBDcmFtbSAyMDA2KQotIHFjckMgc2VlbXMgdG8gYmUgYSBjeXRvY2hyb21lIEMgdGhhdCBpcyBtb2JpbGUgKD8pIGFuZCB0cmFuc3BvcnRzIGUtIGluIG1lbWJyYW5lCi0gYmMxIGNvbXBsZXggZnVuY3Rpb25zIGluIG94eWdlbi1saW1pdGVkIHJlc3BpcmF0aW9uLCB0aGVyZWZvcmUgbWF5YmUgbm8gZ3Jvd3RoIG9uIGZvcm1hdGUgYW5kIG5pdHJhdGUgcmVzcGlyYXRpb24/Ci0gYmMxIGNvbXBsZXggZGVsZXRpb24gbXV0YW50IHdhcyBjb21wbGV0ZWx5IGRlZmljaWVudCBpbiBveHlnZW4tbGltaXRlZCBncm93dGggb24gbml0cmF0ZSAoR2FyZyBldCBhbC4sIDIwMTgpLgotIGJjMSBjb21wbGV4IGFjY2VwdHMgcmVkdWNlZCBxdWlub2xzIGFzIGUtIGRvbm9yLCBwdW1wcyA0IEgrIC8yIGXiiJIgdmlhIHByb3Rvbi1tb3RpdmUgUSBjeWNsZSAocXVpbm9sOmN5dCBjIG94aWRvcmVkdWN0aW9uKSwgYW5kIHJlbGVhc2VzIHJlZHVjZWQgY3l0b2Nocm9tZSBDIGUtIGNhcnJpZXJzCi0gQ3l0b2Nocm9tZSBjNTUzIHByb3RlaW5zIGFyZSBlLSBzaHV0dGxlcyBpbiBtZW1icmFuZSwgYmV0d2VlbiBiYzEgYW5kIHJlc3BpcmF0aW9uIGNvbXBsZXhlcyAoPykKLSBjYmIzLXR5cGUgY3l0b2Nocm9tZSBveGlkYXNlIHNwZWNpYWwgbWVtYmVyIG9mIHRoZSBoZW1lLWNvcHBlciBveGlkYXNlIGZhbWlseQotICJpbiBCcmFkeXJoaXpvYml1bSBqYXBvbmljdW0sIHRoaXMgZW56eW1lIG9wZXJhdGVzIGF0IGV4dHJlbWVseSBsb3cgcGFydGlhbCBwcmVzc3VyZXMgb2Ygb3h5Z2VuIiBbUHJlaXNpZyBldCBhbC4sIDE5OTZdCgoKYGBge3J9CiMgZ2VuZXJhbGl6ZWQgZnVuY3Rpb24gdG8gcGxvdCAoc21hbGwpIGhlYXRtYXBzCmhlYXRtYXBfZml0bmVzcyA8LSBmdW5jdGlvbihkYXRhLCBrZXkgPSBUUlVFKSB7CiAgCiAgIyBtYWtlIHNvbWUgc21hbGwgYWRqdXN0bWVudHMgdG8gb3B0aWNhbCBhcHBlYWFyYW5jZQogIG11dGF0ZShkYXRhLCBnZW5lX25hbWUgPSBmYWN0b3IoZ2VuZV9uYW1lLCB1bmlxdWUoZ2VuZV9uYW1lKSkpICU+JQogIG11dGF0ZShjb25kaXRpb24gPSBpZl9lbHNlKGNvbmRpdGlvbiA9PSAicHVsc2UiLCAiUCIsICJDIikpICU+JQogIG11dGF0ZShjb25kID0gcGFzdGUwKHN1YnN0cmF0ZSwgIiAtICIsIGNvbmRpdGlvbikpICU+JQogIGdyb3VwX2J5KGdlbmVfbmFtZSkgJT4lIGFycmFuZ2UoY29uZCkgJT4lCiAgbXV0YXRlKG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiA9IHJlcGxhY2Uobm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuLCBub3JtX2dlbmVfZml0bmVzc19tZWRpYW4gPCAtNiwgLTYpKSAlPiUKCiAgIyBwbG90IGhlYXRtYXAKICBsZXZlbHBsb3Qobm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuIH4gZ2VuZV9uYW1lICogZmFjdG9yKGNvbmQpLCAuLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgY29sb3JrZXkgPSBrZXksCiAgICBjb2wucmVnaW9ucyA9IGNvbG9yUmFtcFBhbGV0dGUoaGVhdF9jb2xzKSgxNiksCiAgICBhdCA9IHNlcSgtNiwgNiwgMSksIGFzcGVjdCA9ICJmaWxsIiwKICAgIHhsYWIgPSAiIiwgeWxhYiA9ICIiLCBzY2FsZXMgPSBsaXN0KGNleCA9IDAuNywgeCA9IGxpc3Qocm90ID0gOTApKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgeiwgLi4uKSB7CiAgICAgIHBhbmVsLmxldmVscGxvdCh4LCB5LCB6LCAuLi4pCiAgICAgIHBhbmVsLmFibGluZSh2ID0gc2VxX2Fsb25nKHVuaXF1ZSh4KSkrMC41LCAKICAgICAgICBoID0gc2VxX2Fsb25nKHVuaXF1ZSh5KSkrMC41LCBjb2wgPSAid2hpdGUiLCBsd2QgPSAyKQogICAgfQogICkKfQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA1LjB9CnBsb3RfZm9ybWF0ZV9maXRfMSA8LSBkZl9maXRuZXNzX3N1bW1hcnkgJT4lIGZpbHRlcih0aW1lID09IDgsIGdyZXBsKCJtb1thZWRiZ10iLCBnZW5lX25hbWUpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyaV9leHRyYWN0X2ZpcnN0X3JlZ2V4KGdlbmVfbmFtZSwgIm1vW2FlZGJnXShbQS1aMC05XSspPyIpKSAlPiUKICB1bmdyb3VwICU+JSBhcnJhbmdlKGdlbmVfbmFtZSkgJT4lCiAgaGVhdG1hcF9maXRuZXNzKGtleSA9IEZBTFNFKQoKcGxvdF9mb3JtYXRlX2ZpdF8yIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUgZmlsdGVyKHRpbWUgPT0gOCwgZ3JlcGwoImZkW3N3aG9dIiwgZ2VuZV9uYW1lKSkgJT4lCiAgbXV0YXRlKGdlbmVfbmFtZSA9IHN0cmlfZXh0cmFjdF9maXJzdF9yZWdleChnZW5lX25hbWUsICJmZFtzd2hvXVtBLVowLTldKyIpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyaV9yZXBsYWNlX2ZpcnN0X3JlZ2V4KGdlbmVfbmFtZSwgImZkaEQkIiwgImZkc0MiKSkgJT4lCiAgdW5ncm91cCAlPiUgYXJyYW5nZShnZW5lX25hbWUpICU+JQogIGhlYXRtYXBfZml0bmVzcyhrZXkgPSBGQUxTRSkKCnBsb3RfZm9ybWF0ZV9maXRfMyA8LSBkZl9maXRuZXNzX3N1bW1hcnkgJT4lIGZpbHRlcih0aW1lID09IDgsIGxvY3VzX3RhZyAlaW4lIGMoIkgxNl9BMzQ1MSIsICJIMTZfQTA4MzAiLAogICAgIkgxNl9BMDg0NiIsICIxNl9CMTQ1MiIsICJIMTZfQTM1NzYiLCAiSDE2X0ExMTIxIiwgIkgxNl9BMTEyMCIsICJIMTZfQTEwMzEiKSB8IAogICAgZ3JlcGwoInFjcnxjY298Y3RhfGNveFtNTk9QUV0gfGN5b3xjeWQiLCBnZW5lX25hbWUpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyaV9leHRyYWN0X2ZpcnN0X3JlZ2V4KGdlbmVfbmFtZSwgIkgxNl9bQUJdWzAtOV0rfChxY3J8Y2NvfGN0YXxjb3h8Y3lvfGN5ZCkoW0EtWjAtOV0rKT8iKSkgJT4lCiAgdW5ncm91cCAlPiUgYXJyYW5nZSgKICAgIGZhY3RvcihzdHJfc3ViKGdlbmVfbmFtZSwgMSwgMyksIGMoImN5byIsICJjeWQiLCAicWNyIiwgIkgxNiIsICJjb3giLCAiY3RhIiwgImNjbyIpKSwgCiAgICBzdHJfc3ViKGdlbmVfbmFtZSwgNCwgNSkpICU+JQogIGhlYXRtYXBfZml0bmVzcygpCgpwcmludChwbG90X2Zvcm1hdGVfZml0XzEsIHBvc2l0aW9uID0gYygwLCAwLjQ3NSwgMC41LCAxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZm9ybWF0ZV9maXRfMiwgcG9zaXRpb24gPSBjKDAuNDc1LCAwLjQ5LCAwLjk1LCAxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZm9ybWF0ZV9maXRfMywgcG9zaXRpb24gPSBjKDAsIDAsIDEsIDAuNTcpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnN2ZygiLi4vZmlndXJlcy9maWd1cmVfYmFyc2VxX2Zvcm1hdGUuc3ZnIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNS4wKQpwcmludChwbG90X2Zvcm1hdGVfZml0XzEsIHBvc2l0aW9uID0gYygwLCAwLjQ4LCAwLjQ5LCAxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZm9ybWF0ZV9maXRfMiwgcG9zaXRpb24gPSBjKDAuNDg1LCAwLjQ5LCAwLjk1LCAxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZm9ybWF0ZV9maXRfMywgcG9zaXRpb24gPSBjKDAsIDAsIDEsIDAuNTcpKQpkZXYub2ZmKCkKYGBgCgoKIyMjIyBEZXBsZXRpb24gaW4gYWxsIGNvbmRpdGlvbnMKCldoaWNoIGdlbmVzIGFyZSBkZXBsZXRlZCBpbiBhbGwgY29uZGl0aW9ucyAoY2x1c3RlciAyKT8gSGVyZSB3ZSBzZWUgdGhlIGVmZmVjdCBvZiB0aGUgcmVhZCBjb21wcmVzc2lvbiwgc3VjaCB0aGF0IG1hbnkgZ2VuZXMgc2VlbSB0byAicmVjb3ZlciIgZnJvbSBpbml0aWFsIGRlcGxldGlvbiBhbmQgZW5yaWNoIGZyb20gOCB0byAxNiBnZW5lcmF0aW9uIHRpbWUgcG9pbnQuIFRoaXMgZWZmZWN0IGlzIG9ubHkgYXBwYXJlbnQgZm9yIHRoZSBjb250aW51b3VzIGNvbmRpdGlvbnMgYW5kIHdpdGggdmVyeSBoaWdoIHByb2JhYmlsaXR5IHJlbGF0ZWQgdG8gdGhlIGRpc3RvcnRpb24gb2YgcmVhZCBjb3VudHMgYnkgc3VwZXItZW5yaWNoaW5nIG11dGFudHMuIFRoZSA4IGdlbmVyYXRpb24gdGltZSBwb2ludCBzaG91bGQgYmUgY29uc2lkZXJlZCBhcyBtb3JlIHJlbGlhYmxlIGluIHRob3NlIGNhc2VzLgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNi41fQpkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgZmlsdGVyKGxvY3VzX3RhZyAlaW4lIG5hbWVzKHN1YnNldChsaXN0X2VucmljaGVkLCBsaXN0X2VucmljaGVkID09IDIpKSkgJT4lCiAgZmlsdGVyKHRpbWUgPT0gMTYpICU+JQogIGJhcmNoYXJ0X2ZpdG5lc3MoKSAlPiUKICBwcmludChwb3NpdGlvbiA9IGMoMCwwLDAuNiwxKSwgbW9yZSA9IFRSVUUpCgpkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgZmlsdGVyKGxvY3VzX3RhZyAlaW4lIG5hbWVzKHN1YnNldChsaXN0X2VucmljaGVkLCBsaXN0X2VucmljaGVkID09IDIpKSkgJT4lCiAgbGluZWNoYXJ0X2ZpdG5lc3MoKSAlPiUKICBwcmludChwb3NpdGlvbiA9IGMoMC41OCwwLDEsMSkpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1LjJ9CmdyYXBoX2FsbF9kZXBsIDwtIHJldHJpZXZlX1NUUklORygKICBnZW5lX0lEID0gbmFtZXMoc3Vic2V0KGxpc3RfZW5yaWNoZWQsIGxpc3RfZW5yaWNoZWQgPT0gMikpLAogIHRheG9uX0lEID0gMzgxNjY2LAogIHJlZiA9IHJlbmFtZShkZl9yZWYsIG5hbWUgPSBsb2N1c190YWcpCikKCmdyYXBoX2FsbF9kZXBsICU+JSBhcnJhbmdlKENPR19Qcm9jZXNzKSAlPiUgYWN0aXZhdGUoZWRnZXMpICU+JSAKICBmaWx0ZXIoc2NvcmUgPj0gMC40KSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gJ2xpbmVhcicsIGNpcmN1bGFyID0gVFJVRSkgKwogIGdlb21fZWRnZV9hcmMoY29sb3VyID0gZ3JleSgwLjYsIDAuNSksIGFlcyh3aWR0aCA9IHNjb3JlKSkgKyAKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG91ciA9IENPR19Qcm9jZXNzKSwgc2l6ZSA9IDUpICsKICBnZW9tX25vZGVfdGV4dChudWRnZV94ID0gbnVkZ2VfY2lyY2xlKDM4KSR4LCBudWRnZV95ID0gbnVkZ2VfY2lyY2xlKDM4KSR5LAogICAgc2l6ZSA9IDMuMiwgYWVzKGxhYmVsID0gZWdnTk9HX25hbWUsIGNvbG91ciA9IENPR19Qcm9jZXNzKSkgKwogIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAuMiwgMikpICsKICB0aGVtZV9ncmFwaChiYWNrZ3JvdW5kID0gIndoaXRlIiwgZm9yZWdyb3VuZCA9IGdyZXkoMC41KSwKICAgIHBsb3RfbWFyZ2luID0gbWFyZ2luKDEwLCAxMCwgMTAsIDEwKSkKYGBgCgojIyBGaXRuZXNzIG9mICpjYmIqIGdlbmVzCgpPbmUgb2YgdGhlIG1vc3QgaW50ZXJlc3RpbmcgZmVhdHVyZXMgb2YgKkMuIG5lY2F0b3IqIGlzIHRoZSAqY2JiKiBvcGVyb24gZW5jb2RpbmcgZW56eW1lcyBmb3IgUFBQIGFuZCBDYWx2aW4gY3ljbGUgKGUuZy4gUnViaXNjbykuIFR3byBhbG1vc3QgaWRlbnRpY2FsIGNvcGllcyBvZiB0aGUgb3Blcm9uIGFyZSBwcmVzZW50IGluIHRoZSBnZW5vbWUsIG9uZSBvbiB0aGUgcEhHMSBtZWdhcGxhc21pZCBhbmQgb25lIG9uZSBjaHJvbW9zb21lIDIuIFRoZSBsYXR0ZXIgb25lIGhhcyB0d28gYWRkaXRpb25hbCBnZW5lcywgYSBwdXRhdGl2ZSAqY2JiQiogZm9ybWF0ZSBkZWh5ZHJvZ2VuYXNlLCBhbmQgKmNiYlIqLCB0aGUgdHJhbnNjcmlwdGlvbmFsIHJlZ3VsYXRvci4KVG8gdmlzdWFsaXplIGdlbmVyYWwgdHJlbmRzIGZvciBhbGwgZ2VuZXMgb2YgYSBwYXRod2F5LCB3ZSBjYW4gdXNlIGEgYmVlc3dhcm0gcGxvdCBicm9rZW4gZG93biBieSBjb25kaXRpb24uCgpgYGB7ciwgZmlnLndpZHRoID0gMy41LCBmaWcuaGVpZ2h0ID0gM30KcGxvdF9jYmJfZml0bmVzc19iZWUgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JSB1bmdyb3VwICU+JQogIGZpbHRlcih0aW1lID09IDgsIHN0cl9kZXRlY3QoZ2VuZV9uYW1lLCAiY2JiLnxjZngufGNieC4iKSwgY29uZGl0aW9uICE9ICJwdWxzZSIpICU+JQogIG11dGF0ZShnZW5lX25hbWUgPSBzdHJfcmVwbGFjZShnZW5lX25hbWUsICJjZnh8Y2J4IiwgImNiYiIpKSAlPiUKICAKICB4eXBsb3Qobm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuIH4gZmFjdG9yKHN1YnN0cmF0ZSksIC4sCiAgICBncm91cHMgPSBmYWN0b3Ioc3Vic3RyYXRlKSwgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoY29sID0gc3Vic19jb2xbYygxLDMsNSldKSwKICAgIGxhYmVscyA9IHN0cmlfZXh0cmFjdF9maXJzdCguW1siZ2VuZV9uYW1lIl1dLCByZWdleCA9ICJjYmIufGNmeC58Y2J4LiIpLAogICAgeGxhYiA9ICIiLCB5bGFiID0gImZpdG5lc3MgKDggZ2VuZXJhdGlvbnMpIiwgeWxpbSA9IGMoLTcuNSwgMi41KSwKICAgIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmFibGluZShoID0gMCwgbHdkID0gMS41LCBjb2wgPSBncmV5KDAuNyksIGx0eSA9IDIpCiAgICAgIHBhbmVsLmFibGluZShoID0gLTMsIGx3ZCA9IDEuNSwgY29sID0gZ3JleSgwLjcpLCBsdHkgPSAzKQogICAgICBwYW5lbC50ZXh0KDMuMSwgLTMuNSwgbGFiZWxzID0gInNpZ25pZmljYW50IiwgY29sID0gZ3JleSgwLjYpLCBjZXggPSAwLjY1KQogICAgICBjb29yZHMgPC0gcGFuZWwuYmVlc3dhcm0oeCwgeSwgYmluX3kgPSBUUlVFLCBicmVha3NfeSA9IHNlcSgtMTAsIDQsIDAuNCksIAogICAgICAgIHJldHVybl9jb29yZHMgPSBUUlVFLCAuLi4pCiAgICAgIHBhbmVsLmRpcmVjdGxhYmVsKHggPSBjb29yZHMkeCwgeSA9IGNvb3JkcyR5LCB5X2JvdW5kYXJ5ID0gYygtMTAsIC00KSwgCiAgICAgICAgY2V4ID0gMC42NSwgZHJhd19ib3ggPSBUUlVFLCBib3hfbGluZSA9IFRSVUUsIC4uLikKICAgICAgcGFuZWwua2V5KGxhYmVscyA9IGMoImZvcm1hdGUiLCAiZnJ1Y3Rvc2UiLCAic3VjY2luYXRlIiksIGNleCA9IDAuOCwgCiAgICAgICAgY29ybmVyID0gYygwLjk4LCAwLjA1KSkKICAgIH0KICApCgpwcmludChwbG90X2NiYl9maXRuZXNzX2JlZSkKYGBgCk9ubHkgb25lIGdlbmUgaXMgcmVhbGx5IGRlcGxldGVkIGFuZCB0aGVyZWZvcmUgZXNzZW50aWFsIG9uIHRoZSB0d28gZm9ybWF0ZSBjb25kaXRpb25zLCAqY2JiUiouIEF0IGxlYXN0IHR3byBtb3JlIGdlbmVzIGZvciBSdWJpc2NvIGFuZCBQUlVLIHNob3VsZCBiZSBlc3NlbnRpYWwgb24gZm9ybWF0ZSBhcyB0aGV5IGhhdmUgbm8gY291bnRlcnBhcnQgb24gY2hyb21vc29tZSAxLiBUaGV5IHNob3cgbm8gc2lnbmlmaWNhbnQgZGVwbGV0aW9uLiBUaGlzIHByb3ZlcyB0aGF0IHRoZSB0d28gKmNiYiogb3Blcm9ucyBjYW4gZnVsbHkgY29tcGxlbWVudCBlYWNoIG90aGVyLCBleGNlcHQgZm9yICpjYmJSKiwgdGhlIG9uZSBnZW5lIHByZXNlbnQgaW4gb25seSBjb3B5IChjaHJvbW9zb21lIDIpLgoKTm90ZTogU29tZSAqY2JiKiBtdXRhbnRzIGZvciBjaHJvbW9zb21lIDIgc2VlbSB0byBoYXZlIGEgbW9yZSBwb3NpdGl2ZSBmaXRuZXNzIHNjb3JlIGNvbXBhcmVkIHRvIHRoZWlyIGNvdW50ZXIgcGFydHMgb24gdGhlIHBIRzEgbWVnYXBsYXNtaWQuIFNvbWUgZ2VuZXMgd2l0aCBoaWdoZXIgZml0bmVzcyBzaG93IHN1ZGRlbiBsZWFwcyBmcm9tIDggdG8gMTYgaG91cnMuIEEgbG9vayB1cCBpbiB0aGUgc3VtbWFyeSB0YWJsZSByZXZlYWxlZCB0aGF0IHRoZXkgaGF2ZSBvbmx5IDEgb3IgMiBtdXRhbnRzIHF1YW50aWZpZWQgcGVyIGdlbmUsIHdoaWNoIG1ha2VzIHRoZSByZXN1bHRzIGxlc3MgcmVsaWFibGUsIHBhcnRpY3VsYXJseSBmb3IgdGhlIGNvbnRpbnVvdXMgY29uZGl0aW9ucyBvbiBmcnVjdG9zZSB3aXRoIGxvdyByZWFkIGNvdW50IGF0IDE2IGdlbmVyYXRpb25hLiBSZXN1bHRzIGZvciB0aGVzZSBtdXRhbnRzIGhhdmUgdG8gYmUgaW50ZXJwcmV0ZWQgd2l0aCBjYXJlLiBUaGUgZm9sb3dpbmcgcGxvdCBmb2N1c2VzIG9uIHRoZSBwSEcxICpjYmIqIGNvcGllcyB3aGljaCBoYXZlIG9uIGF2ZXJhZ2UgaGlnaGVyIGNvdmVyYWdlIHdpdGggaW5kaXZpZHVhbCBtdXRhbnRzLgoKCmBgYHtyLCBmaWcud2lkdGggPSA1LjUsIGZpZy5oZWlnaHQgPSAzLjV9CiMgc2luZ2xlIHRpbWUgc2VyaWVzIHBlciBnZW5lCnBsb3RfY2JiX2ZpdG5lc3Nfc2VsZWN0IDwtIGRmX2ZpdG5lc3MgJT4lIHVuZ3JvdXAgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZl9yZWYsIGxvY3VzX3RhZywgZ2VuZV9uYW1lKSwgYnkgPSAibG9jdXNfdGFnIikgJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3QoZ2VuZV9uYW1lLCAiKGNiYnxjZnh8Y2J4KVtQU0xLR0FUXVB8Y2JiUiIpLCBjb25kaXRpb24gIT0gInB1bHNlIikgJT4lCiAgbXV0YXRlKAogICAgZ2VuZV9uYW1lID0gY2FzZV93aGVuKAogICAgICBnZW5lX25hbWUgPT0gImNmeFIgY2JiUiBIMTZfQjEzOTYiIH4gImNiYlIgKFJFRykiLAogICAgICBnZW5lX25hbWUgPT0gImNmeFAgY2JiUFAgUEhHNDIxIiB+ICJjYmJQIChQUlVLKSIsCiAgICAgIGdlbmVfbmFtZSA9PSAiY2J4U1AgY2JiUyBjYmJTUCBjZnhTUCByYmNTIFBIRzQyNiIgfiAiY2JiUyAoUkJQQykiLAogICAgICBnZW5lX25hbWUgPT0gImNiYkwyIGNiYkwgY2J4TFAgY2Z4TFAgUEhHNDI3IiB+ICJjYmJMIChSQlBDKSIsCiAgICAgIGdlbmVfbmFtZSA9PSAiY2JiS1AgUEhHNDE3IiB+ICJjYmJLIChQR0spIiwKICAgICAgZ2VuZV9uYW1lID09ICJjYmJHUCBQSEc0MTgiIH4gImNiYkcgKEdBUERIKSIsCiAgICAgIGdlbmVfbmFtZSA9PSAiY2JiQVAgUEhHNDE2IiB+ICJjYmJBIChGQkEpIiwKICAgICAgZ2VuZV9uYW1lID09ICJjYmJUUCBQSEc0MjAiIH4gImNiYlQgKFRLVCkiLAogICAgICAjZ2VuZV9uYW1lID09ICJjYmJaUCBQSEc0MTkiIH4gImNiYlogKFBHUCkiLAogICAgICAjZ2VuZV9uYW1lID09ICJjYnhYUCBjZnhYUCBQSEc0MjUiIH4gImNiYlggKFJCQ0EpIiwKICAgICAgI2dlbmVfbmFtZSA9PSAiY2JiRVAgY2Z4RSBQSEc0MjMiIH4gImNiYkUgKFJQRSkiCiAgICApICU+JSBmYWN0b3IoLiwgdW5pcXVlKC4pW2MoMSw2LDcsOCwzLDQsMiw1KV0pCiAgKSAlPiUgdW5ncm91cCgpICU+JQogIAogIHh5cGxvdChub3JtX2dlbmVfZml0bmVzcyB+IGZhY3Rvcih0aW1lKSB8IGdlbmVfbmFtZSwgLiwKICAgIGdyb3VwcyA9IHN1YnN0cmF0ZSwgcGFyLnNldHRpbmdzID0gY3VzdG9tLmxhdHRpY2UoY29sID0gc3Vic19jb2xbYygxLDMsNSldKSwKICAgIHR5cGUgPSBjKCJwIiwgImwiKSwgeGxhYiA9ICJnZW5lcmF0aW9ucyIsIHlsYWIgPSAiZml0bmVzcyIsCiAgICB5bGltID0gYygtNy45LCAyLjUpLCBsYXlvdXQgPSBjKDQsIDIpLCBiZXR3ZWVuID0gbGlzdCh4ID0gMC41LCB5ID0gMC41KSwKICAgIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSksIGx3ZCA9IDIuNSwgYXMudGFibGUgPSBUUlVFLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC5lcnJiYXJzKHgsIHksIGV3aWR0aCA9IDAsIC4uLikKICAgIH0KICApCgpwcmludChwbG90X2NiYl9maXRuZXNzX3NlbGVjdCkKYGBgCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFLCBmaWcud2lkdGggPSA3LjcsIGZpZy5oZWlnaHQgPSAzLjJ9CnN2ZygiLi4vZmlndXJlcy9maWd1cmVfZml0bmVzc19jYmIuc3ZnIiwgd2lkdGggPSA3LjcsIGhlaWdodCA9IDMuMikKcHJpbnQocGxvdF9jYmJfZml0bmVzc19iZWUsIHBvc2l0aW9uID0gYygwLCAwLjAzLCAwLjQsIDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9jYmJfZml0bmVzc19zZWxlY3QsIHBvc2l0aW9uID0gYygwLjM2LCAwLCAxLCAxKSkKZGV2Lm9mZigpCmBgYAoKIyMgRml0bmVzcyBvZiBwaG9zcGhvZ2x5Y29sYXRlIHNhbHZhZ2UgbXV0YW50cwoKUGhvc3Bob2dseWNvbGF0ZSBzYWx2YWdlLCBhbHNvIGtub3duIGFzIHBob3RvcmVzcGlyYXRpb24sIGlzIHRoZSBwcm9jZXNzIG9mIHJlY3ljbGluZyB0aGUgcHJvZHVjdCBvZiBSdWJpc2NvJ3Mgb3h5Z2VuYXRpb24gcmVhY3Rpb24sIDItcGhvc2hvZ2x5Y29sYXRlICgyLVBHbHkpLiBEZXBlbmRpbmcgb24gdGhlIGxvY2FsIENPMiBjb25jZW50cmF0aW9uIGFyb3VuZCBSdWJpc2NvLCB0aGUgZmx1eCB0b3dhcmRzIDItUEdseSBiZWNvbWVzIGhpZ2hlciB3aXRoIGRlY3JlYXNpbmcgQ08yIGNvbmNlbnRyYXRpb24uIFR5cGljYWxseSwgZmx1eCB0b3dhcmRzIDItUEdseSBpbmNyZWFzZXMgc2lnbmlmY2FudGx5IGJldHdlZW4gLi4uIGFuZCAuLi4gcHBtIENPMiBbUmVmZXJlbmNlP10uIFRoZSBmb2xsb3dpbmcgcGxvdCBzaG93cyBmaXRuZXNzIG9mIHRoZSBtb3N0IGltcG9ydGFudCAyLVBHbHkgc2FsdmFnZSBLTyBtdXRhbnRzLgoKVGhlIGtleSBlbnp5bWVzIGZvciB0aGUgdW5pdmVyc2FsICd1cHBlciBwYXJ0JyBvZiAyLVBHbHkgc2FsdmFnZSBhcmUgMi1QR2x5LXBob3NwaGF0YXNlIChjYmJaMiwgY2JiWlApIGFuZCBnbHljb2xhdGUgZGVoeWRyb2dlbmFzZSBHREggKEdsY0RFRiwgKS4gVGhlIGdseWNlcmF0ZSBwYXRod2F5IGlzIHRoZSBkb21pbmFudCByb3V0ZSBmb3IgdGhlICdsb3dlciBwYXJ0JyBvZiAyLVBHbHkgc2FsdmFnZSB3aXRoIGZvdXIgZW56eW1lcyBiZWluZyBzdHJvbmdseSB1cHJlZ3VsYXRlZCBpbiBsb3cgQ08yOiBnbHlveHlsYXRlIGNhcmJvbGlnYXNlIEdMWENMIChIMTZfQTM1OTgpLCBoeWRyb3h5cHlydXZhdGUgaXNvbWVyYXNlIEhQWVJJIChIMTZfQTM1OTkpLCB0YXJ0cm9uYXRlIHNlbWlhbGRlaHlkZSByZWR1Y3Rhc2UgVFJTQVJyIChIMTZfQTM2MDApLCBhbmQgZ2x5Y2VyYXRlIGtpbmFzZSBHTFlDSyAoSDE2X0IwNjEyKS4gVGhlIGZpcnN0IHRocmVlIG9mIHRoZXNlIGVuenltZXMgYXJlIGVuY29kZWQgaW4gb25lIG9wZXJvbi4KCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNS4wfQpkZl9maXRuZXNzICU+JSB1bmdyb3VwICU+JQogIGxlZnRfam9pbihzZWxlY3QoZGZfcmVmLCBsb2N1c190YWcsIGdlbmVfbmFtZSksIGJ5ID0gImxvY3VzX3RhZyIpICU+JQogIGZpbHRlcigKICAgIGxvY3VzX3RhZyAlaW4lIGMoIkgxNl9CMTM4NyIsICJQSEc0MTkiLCAiSDE2X0EzMDk0IiwgIkgxNl9BMzA5NiIsICJIMTZfQTMwOTciLAogICAgICAiSDE2X0EzNTk4IiwgIkgxNl9BMzU5OSIsICJIMTZfQTM2MDAiLCAiSDE2X0IwNjEyIiwgIkgxNl9BMDY0MCIsICJIMTZfQTA2NDEiLAogICAgICAiSDE2X0EwNjQyIiwgIkgxNl9BMDY0NCIpLAogICAgY29uZGl0aW9uID09ICJwdWxzZSIpICU+JQogIG11dGF0ZShnZW5lX25hbWUgPSBzdHJfcmVwbGFjZShnZW5lX25hbWUsICIgY2JiWjIiLCAiIikpICU+JQogIAogIHh5cGxvdChub3JtX2dlbmVfZml0bmVzcyB+IGZhY3Rvcih0aW1lKSB8IGZhY3RvcihnZW5lX25hbWUsIHJldih1bmlxdWUoZ2VuZV9uYW1lKSkpLCAuLAogICAgZ3JvdXBzID0gc3Vic3RyYXRlLCBwYXIuc2V0dGluZ3MgPSBjdXN0b20ubGF0dGljZShjb2wgPSBzdWJzX2NvbFtjKDEsMyw1KV0pLAogICAgdHlwZSA9IGMoInAiLCAibCIpLCB4bGFiID0gImdlbmVyYXRpb25zIiwgeWxhYiA9ICJmaXRuZXNzIiwKICAgIHlsaW0gPSBjKC03LjksIDIuNSksIGxheW91dCA9IGMoNSwgMyksIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwgbHdkID0gMi41LCBhcy50YWJsZSA9IFRSVUUsCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmVycmJhcnMoeCwgeSwgZXdpZHRoID0gMCwgLi4uKQogICAgICBwYW5lbC5rZXkoLi4uLCBjb3JuZXIgPSBjKDAuMDUsIDAuMDUpKQogICAgfQogICkKYGBgCgoKKipQb3NzaWJsZSBleHBsYW5hdGlvbjogbG9jYWwgQ08yIGNvbmNlbnRyYXRpb24gaXMgaGlnaCBkdXJpbmcgZm9ybWF0b3Ryb3BoaWMgZ3Jvd3RoKioKClF1ZXN0aW9uOiBXaGF0IGlzIHN0ZWFkeS1zdGF0ZSBDTzIvSENPMy1jb25jZW50cmF0aW9uIGluIG1lZGl1bT8KCktub3duIGN1bHRpdmF0aW9uIHBhcmFtZXRlcnMgZm9yIGhpZ2hlc3QgZ3Jvd3RoIHJhdGU6CgotIG1lZGl1bSBjb25jZW50cmF0aW9uOiAxLjUgZy9MIGZvcm1hdGUKLSBncm93dGggcmF0ZS9kaWx1dGlvbiByYXRlOiAwLjI1IGheLTEKLSByZXNpZHVhbCBmb3JtYXRlIGF0IMK1ID0gMC4yNSBoXi0xOiAwLjI1IGcgTF4tMQotIDI1JSBvZiBDTzIgZnJvbSBmb3JtYXRlIGlzIGZpeGVkLCA3NSUgaXMgZW1pdHRlZCAoKkMuIG5lY2F0b3IqIEdFTSkKCkFuc3dlcjoKCi0gZm9ybWF0ZSBjb25zdW1wdGlvbiByYXRlOiBRX0hDT08zID0gKDEuNSBnIExeLTEgLSAwLjI1IGcgTF4tMSkgKiAwLjI1IGheLTEgPSAwLjMxIGcgZm9ybWF0ZSBMXi0xIGheLTEKLSBlc3RpbWF0aW9uIG9mIENPMiBwcm9kdWN0aW9uIHJhdGU6IFBfQ08yID0gUV9IQ09PMyAqIE1XX0hDT08zID0gMC4zMSBnIExeLTEgaF4tMSAvIDQ4IGcgbW9sXi0xID0gNi41IG1tb2wgTF4tMSBoXi0xCi0gZXN0aW1hdGlvbiBvZiBDTzIgY29uc3VtcHRpb24gcmF0ZTogUV9DTzIgPSBQX0NPMiAqIDAuMjUgPSAgMS42MjUgbW1vbCBMXi0xIGheLTEKLSBmaW5hbCBDTzIgcHJvZHVjdGlvbiByYXRlOiBQX0NPMiAtIFFfQ08yID0gNC44NzUgbW1vbCBMXi0xIGheLTEKClVuZm9ydHVuYXRlbHkgaXQgaXMgbm90IHBvc3NpYmxlIHRvIGRldGVybWluZSB0aGUgc3RlYWR5LXN0YXRlIGNvbmNlbnRyYXRpb24gZnJvbSB0aGUgQ08yIGVtaXNzaW9uIHJhdGUuIEhvd2V2ZXIgd2UgY2FuIGNvbXBhcmUgdGhpcyByYXRlIHdpdGggdGhlIGdyb3d0aC1saW1pdGluZyBDTzIgc3VwcGx5IGZvciBhdXRvdHJvcGhpYyBncm93dGggKGFtYmllbnQgYWlyLCBDbGFhc3NlbnMgZXQgYWwuLCAyMDIwKS4KVGhlIGF1dGhvcnMgdXNlZDoKCi0gMC4wNCUgQ08yIG9yIDEwJSBDTzIsIDYuMjUgTC9taW4gPSAzNzUgTC9oCi0gMC43TCBjdWx0dXJlIHZvbHVtZQotIGRlbnNpdHkgb2YgQ08yID0gMS44MzYgZy9MIGF0IDI1KkMKLSBDTzIgc3VwcGx5IHJhdGUgPSAzNzUgTCBoXi0xICogMC4wMDA0IC8gMC43IEwgY3VsdHVyZSAqIDEuODM2IGcgTF4tMSA9IDAuMzkgZyBMXi0xIGheLTEgPSA4LjUgbW1vbCBMXi0xIGheLTEKCgojIyBGaXRuZXNzIG9mIGNlbnRyYWwgbWV0YWJvbGlzbSBnZW5lcwoKSW4gb3JkZXIgdG8gb2J0YWluIGFuIG92ZXJ2aWV3IGFib3V0IGZpdG5lc3MgY29udHJpYnV0aW9uIG9mIGNlbnRyYWwgY2FyYm9uIG1ldGFib2xpc20gZW56eW1lcywgd2UgY2FuIHNlbGVjdCB0aGUgc2FtZSBsaXN0IG9mIGVuenltZXMgKHJlYWN0aW9ucykgdGhhdCB3YXMgdXNlZCBmb3IgRmlndXJlIDMgb2YgdGhlIG1hbnVzY3JpcHQuIEluIHRoaXMgZmlndXJlLCBhYnNvbHV0ZSBwcm90ZWluIGFidW5kYW5jZSwgcmVsYXRpdmUgY2hhbmdlIGluIHByb3RlaW4gYWJ1bmRhbmNlLCB1dGlsaXphdGlvbiB3YXMgY29tcGFyZWQuIFRoZSBuYW1lcyBvZiBlbnp5bWF0aWMgcmVhY3Rpb25zIGFyZSB0YWtlbiBmcm9tIHRoZSBnZW5vbWUgc2NhbGUgbW9kZWxhbmQgaGF2ZSB0byBiZSBtYXBwZWQgdG8gZ2VuZSBJRHMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpkZl9jY21fZW56eW1lcyA8LSBsaXN0KAogIGBDQkIgY3ljbGVgID0gYygiUEdLIiwgIlRQSSIsICJHQVBEIiwgIkZCQSIsICJGQlAiLCAiVEtUMSIsICJUS1QyIiwgIlRBaCIsICJSUEUiLCAiUlBJIiwgIlBSVUsiLCAiUkJQQyIsICJGREgiKSwKICBgRW50bmVyLURvdWRvcm9mZiBwYXRod2F5YCA9IGMoIlBHSSIsICJHNlBESDJyIiwgIlBHTCIsICJFREQiLCAiRURBIiksCiAgYFB5cnV2YXRlIG1ldGFib2xpc21gID0gYygiUEdNIiwgIkVOTyIsICJQWUsiLCAiUERIMSIsICJQREgyIiwgIlBESDMiLCAiUEMiLCAiUFBDIiwgIk1FMSIsICJNRTIiLCAiQ1MiKSwKICBgVENBIGN5Y2xlYCA9IGMoIkFDT05UMSIsICJBQ09OVDIiLCAiSUNESHgiLCAiSUNESHlyIiwgIkFLR0RIIiwgIlNVQ09BUyIsICJTVUNEaSIsICJTVUNEMSIsICJGVU0iLCAiTURIIiwgIk1BTFMiKQopICU+JSBlbmZyYW1lKCJtb2RlbF9ncm91cF9zaG9ydCIsICJyZWFjdGlvbl9pZCIpICU+JSAKdW5uZXN0KHJlYWN0aW9uX2lkKQoKZGZfbW9kZWxfcmVhY3Rpb25zIDwtIHJlYWRfY3N2KCIuLi9kYXRhL2lucHV0L21vZGVsX3JlYWN0aW9ucy5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpICU+JQogIHNlbGVjdChyZWFjdGlvbl9pZCwgcmVhY3Rpb25fbmFtZSwgZ2VuZXMpICU+JSBzZXBhcmF0ZV9yb3dzKGdlbmVzLCBzZXAgPSAiLCAiKSAlPiUKICByZW5hbWUobG9jdXNfdGFnID0gZ2VuZXMpICU+JQogIHJpZ2h0X2pvaW4oZGZfY2NtX2VuenltZXMsIGJ5ID0gInJlYWN0aW9uX2lkIikgJT4lCiAgIyByZW1vdmUgMSBnZW5lIGZyb20gVENBIHdoaWNoIGlzIGFsc28gbGlzdGVkIGluIFBZUiAocGRoMyAvIGRpaHlkcm9saXBvYW1pZGUgZGVoeWRyb2dlbmFzZSkKICBmaWx0ZXIoIShsb2N1c190YWcgPT0gIkgxNl9BMTM3NyIgJiBtb2RlbF9ncm91cF9zaG9ydCA9PSAiVENBIikpCmBgYAoKVGhlIGJlZXN3YXJtIHBsb3Qgc2hvd3MgdGhhdCBzb21lIGdlbmVzIGFyZSBjb25kaXRpb25hbGx5IGVzc2VudGlhbCwgYWxiZWl0IG5vdCBtYW55LiBUcmVuZHMgdGhhdCBhcmUgY29uc2lzdGVudCBiZXR3ZWVuIHRpbWUgcG9pbnRzIGFuZCBncm93dGggcmVnaW1lcyBhcmU6Ci0gZGVwbGV0aW9uIG9mIGBmZHNBQkRHYCBha2EgdGhlIHNvbHVibGUgRkRIIGluIGJvdGggZm9ybWF0ZSBjb25kaXRpb25zIChkZXNjcmliZWQgcHJldmlvdXNseSBpbiBkZXRhaWwpCi0gZGVwbGV0aW9uIG9mIGBlZGFgLCBgZWRkYCBhbmQgYHBnbGAgaW4gZnJ1Y3Rvc2UgKGFsbCBjb25zZWN1dGl2ZSByZWFjdGlvbnMgb2YgdGhlIEVEIHB3KSwgdG8gYSBsb3dlciBleHRlbnQgaW4gZm9ybWF0ZSB0b28/Ci0gYHBwY2AgKFBFUCBjYXJib294eWxhc2UpIGluIGZvcm1hdGUsIGFsc28gcHJlZGljdGVkIGJ5IEZCQS9SQkEgdG8gY2FycnkgbWFqb3IgZmx1eCBpbiBmb3JtYXRlKQotIGBwZGhBYCAocHlydXZhdGUgZGVoeWRyb2dlbmFzZSkgb24gZnJ1Y3Rvc2UgYW5kIHN1Y2NpbmF0ZQotIGBtYWVBYCBtYWxpYyBlbnp5bWUKLSBub3RoaW5nIGZyb20gVENBIGN5Y2xlCgoKYGBge3IsIGZpZy53aWR0aCA9IDguMiwgZmlnLmhlaWdodCA9IDMuMH0KcGxvdF9jY21fYmVlIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBpbm5lcl9qb2luKGRmX21vZGVsX3JlYWN0aW9ucywgYnkgPSAibG9jdXNfdGFnIikgJT4lCiAgdW5ncm91cCAlPiUgZmlsdGVyKHRpbWUgPT0gOCwgY29uZGl0aW9uICE9ICJwdWxzZSIpICU+JQogIG11dGF0ZShnZW5lX25hbWUgPSBzdHJpX2V4dHJhY3RfZmlyc3QoZ2VuZV9uYW1lLCByZWdleCA9ICJbYS16QS1aMC05XSsiKSkgJT4lCgogIHh5cGxvdChub3JtX2dlbmVfZml0bmVzc19tZWRpYW4gfiBmYWN0b3Ioc3Vic3RyYXRlKSB8IG1vZGVsX2dyb3VwX3Nob3J0LCAuLAogICAgZ3JvdXBzID0gc3Vic3RyYXRlLCBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZChjb2wgPSBzdWJzX2NvbFtjKDEsMyw1KV0pLAogICAgbGFiZWxzID0gLltbImdlbmVfbmFtZSJdXSwgeGxhYiA9ICIiLCB5bGFiID0gImZpdG5lc3MgKDggZ2VuZXJhdGlvbnMpIiwgCiAgICB5bGltID0gYygtNy4yLCAxLjgpLCBhcy50YWJsZSA9IFRSVUUsIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwgbGF5b3V0ID0gYyg0LCAxKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwuYWJsaW5lKGggPSAwLCBsd2QgPSAxLjUsIGNvbCA9IGdyZXkoMC43KSwgbHR5ID0gMikKICAgICAgcGFuZWwuYWJsaW5lKGggPSAtMywgbHdkID0gMS41LCBjb2wgPSBncmV5KDAuNyksIGx0eSA9IDMpCiAgICAgIHBhbmVsLnRleHQoMywgLTMuNSwgbGFiZWxzID0gInNpZ25pZmljYW50IiwgY29sID0gZ3JleSgwLjYpLCBjZXggPSAwLjY1KQogICAgICBjb29yZHMgPC0gcGFuZWwuYmVlc3dhcm0oeCwgeSwgYmluX3kgPSBUUlVFLCBicmVha3NfeSA9IHNlcSgtMTAsIDQsIDAuNCksCiAgICAgICAgcmV0dXJuX2Nvb3JkcyA9IFRSVUUsIC4uLikKICAgICAgcGFuZWwuZGlyZWN0bGFiZWwoY29vcmRzJHgsIGNvb3JkcyR5LCB5X2JvdW5kYXJ5ID0gYygtMTAsIC0zKSwgY2V4ID0gMC42NSwKICAgICAgICBkcmF3X2JveCA9IFRSVUUsIGJveF9saW5lID0gVFJVRSwgYm94X3NjYWxlID0gLTAuMDEsIC4uLikKICAgIH0KICApCgpwcmludChwbG90X2NjbV9iZWUpCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc3ZnKCIuLi9maWd1cmVzL2ZpZ3VyZV9maXRuZXNzX2NjbS5zdmciLCB3aWR0aCA9IDguMiwgaGVpZ2h0ID0gMy4wKQpwcmludChwbG90X2NjbV9iZWUpCmRldi5vZmYoKQpgYGAKCkxvb2tpbmcgYXQgdGhlIHNpbmdsZSBnZW5lcyBhcyBhIHRpbWUgc2VyaWVzIHNob3dzIHRoZSBzYW1lIHBpY3R1cmUuIFNvbWUgZ2VuZXMgaG93ZXZlciBzaG93IGEgc3VkZGVuICJsZWFwIiBpbiBmaXRuZXNzIGZyb20gOCB0byAxNiBnZW5lcmF0aW9ucyBldmVuIGFmdGVyIHByZXZpb3VzIGRlcGxldGlvbi4gVGhpcyBpcyBhbiBhcnRpZmFjdCBjYXVzZWQgYnkgbm9ybWFsaXphdGlvbiBvZiBmaXRuZXNzIGJ5IHRoZSBvdmVyYWxsIHJlYWQgZGlzdHJpYnV0aW9uLiBUaGVzZSBnZW5lcyBoYXZlIDAgcmVhZHMgcXVhbnRpZmllZCBidXQgdGhlIGFsZ29yaXRobSBzdGlsbCBkZXRlcm1pbmVzIGlmIDAgcmVhZHMgY2FuIGJlIGV4cGVjdGVkIGZvciBnZW5lcyB3aXRoIGxvdyBmaXRuZXNzIGFuZCBhdHRyaWJ1dGVzIHRoaXMgZml0bmVzcyB0byB0aGVtLiBIb3dldmVyIGlmIHRoZSByZWFkIGRpc3RyaWJ1dGlvbiBiZWNvbWVzIGV4dHJlbWVseSBza2V3ZWQgdG93YXJkcyBmZXcgaGlnaCBhYnVuZGFudCBtdXRhbnRzLCB0aGUgZml0bmVzcyBjYWxjdWxhdGlvbiBiZWNvbWVzIGltcG9zc2libGUgZm9yIGxvdy1hYnVuZGFudCBtdXRhbnRzLiBXZSB0aGVyZWZvcmUgZm9jdXMgb24gdGhlIDggZ2VuZXJhdGlvbiB0aW1lIHBvaW50LgoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAzLjV9CiMgc2luZ2xlIHRpbWUgc2VyaWVzIHBlciBnZW5lCnBsb3RfY2NtX2ZpdG5lc3Nfc2VsZWN0IDwtIGRmX2ZpdG5lc3MgJT4lIHVuZ3JvdXAgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZl9yZWYsIGxvY3VzX3RhZywgZ2VuZV9uYW1lKSwgYnkgPSAibG9jdXNfdGFnIikgJT4lCiAgaW5uZXJfam9pbihkZl9tb2RlbF9yZWFjdGlvbnMsIGJ5ID0gImxvY3VzX3RhZyIpICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KGdlbmVfbmFtZSwgImZkc3xwZ2x8cGdpfGVkZDF8ZWRhfHBwY3xwZGh8bWFlIikpICU+JQogIG11dGF0ZShjb25kaXRpb24gPSBwYXN0ZTAoc3Vic3RyYXRlLCAiIC0gIiwgdG91cHBlcihzdWJzdHIoY29uZGl0aW9uLCAxLCAxKSkpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyaV9leHRyYWN0X2ZpcnN0KGdlbmVfbmFtZSwgcmVnZXggPSAiW2EtekEtWjAtOV0rIikpICU+JQogIAogIHh5cGxvdChub3JtX2dlbmVfZml0bmVzcyB+IGZhY3Rvcih0aW1lKSB8IGdlbmVfbmFtZSwgLiwKICAgIGdyb3VwcyA9IGNvbmRpdGlvbiwgYXMudGFibGUgPSBUUlVFLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoY29sID0gc3Vic19jb2wpLCB0eXBlID0gYygicCIsICJsIiksCiAgICB4bGFiID0gImdlbmVyYXRpb25zIiwgeWxhYiA9ICJmaXRuZXNzIiwKICAgIHlsaW0gPSBjKC04LjUsIDQpLCBsYXlvdXQgPSBjKDcsIDIpLCAKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgYXV0by5rZXkgPSBsaXN0KGNvbHVtbnMgPSAzLCBjZXggPSAwLjcpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwgbHdkID0gMiwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwuZXJyYmFycyh4LCB5LCBld2lkdGggPSAwLCAuLi4pCiAgICB9CiAgKQoKcHJpbnQocGxvdF9jY21fZml0bmVzc19zZWxlY3QpCmBgYAoKIyMgVGFibGUgd2l0aCBjb25kaXRpb25hbGx5IGVzc2VudGlhbCByZWFjdGlvbnMgaW4gY2VudHJhbCBtZXRhYm9saXNtCgpgYGB7cn0KZGZfY2NtX2Vzc2VudGlhbCA8LSBkZl9tb2RlbF9yZWFjdGlvbnMgJT4lCiAgZmlsdGVyKHJlYWN0aW9uX2lkICVpbiUgYygiRkRIIiwgIlBQQyIsICJQR0wiLCAiRUREIiwgIkVEQSIsICJQREgxIiwgIk1FMSIsICJNRTIiKSkgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZl9yZWYsIGxvY3VzX3RhZywgZ2VuZV9uYW1lKSwgYnkgPSAibG9jdXNfdGFnIikgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZl9maXRuZXNzX2NvbXAsIG1hdGNoZXMoImxvY3VzfGNvbnRpbnVvdXMiKSksIGJ5ID0gImxvY3VzX3RhZyIpICU+JQogIGFycmFuZ2UobW9kZWxfZ3JvdXBfc2hvcnQsIHJlYWN0aW9uX2lkLCBsb2N1c190YWcpICU+JQogIG11dGF0ZShhY3Jvc3MobWF0Y2hlcygiY29udGludW91c18iKSwgcm91bmQsIDEpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyX3JlbW92ZShnZW5lX25hbWUsICIgP0gxNlxcX1tBQjAtOV0rIikpICU+JQogIHJlbmFtZV93aXRoKGZ1bmN0aW9uKHgpIGdzdWIoImNvbnRpbnVvdXNfIiwgIiIsIHgpLCBtYXRjaGVzKCJjb250aW51b3VzXyIpKSAlPiUKICByZW5hbWUoUGF0aHdheSA9IG1vZGVsX2dyb3VwX3Nob3J0LCBSZWFjdGlvbiA9IHJlYWN0aW9uX2lkLAogICAgYFJlYWN0aW9uIG5hbWVgID0gcmVhY3Rpb25fbmFtZSwgYEdlbmUgSURgID0gbG9jdXNfdGFnLCBgR2VuZSBuYW1lYCA9IGdlbmVfbmFtZSkgJT4lCiAgc2VsZWN0KGMoNCwxLDIsMyw1LDYsNyw4KSkKCnByaW50KGRmX2NjbV9lc3NlbnRpYWwpCndyaXRlX2NzdihkZl9jY21fZXNzZW50aWFsLCAiLi4vZGF0YS9vdXRwdXQvZXNzZW50aWFsaXR5X2NjbS5jc3YiKQpgYGAKCiMgRXhwb3J0IGdlbmUgZml0bmVzcyBkYXRhCgpTYXZlIGFuIGBSZGF0YWAgZmlsZSB3aXRoIGZpdG5lc3MgZGF0YSBmb3IgYWxsIGNvbmRpdGlvbnMgdGhhdCBmaW5hbGx5IHdlcmUgdXNlZCBpbiB0aGUgbWFudXNjcmlwdCBieSBKYWhuIGV0IGFsLiwgMjAyMS4KVGhpcyBpcyB1c2VkIGluIHRoZSBbU2hpbnlMaWJdKGh0dHBzOi8vbS1qYWhuLnNoaW55YXBwcy5pby9TaGlueUxpYi8pIHdlYiBhcHAuCgpgYGB7cn0KQ3VwcmlhdmlkdXNfQmFyU2VxXzIwMjEgPC0gZGZfZml0bmVzcyAlPiUKICB1bmdyb3VwICU+JSBzZWxlY3QoLUlEKSAlPiUKICB1bml0ZShjb25kaXRpb24sIHN1YnN0cmF0ZSwgY29uZGl0aW9uLCBzZXAgPSAiIC0gIikgJT4lCiAgbXV0YXRlKGZvbGRfY2hhbmdlID0gMl5sb2cyRkMpICU+JQogIHJlbmFtZShmaXRuZXNzX3Njb3JlID0gbm9ybV9nZW5lX2ZpdG5lc3MsIHRfc3RhdCA9IHQsIHRpbWVwb2ludCA9IHRpbWUpICU+JQogIGxlZnRfam9pbihzZWxlY3QoZGZfcmVmLCBsb2N1c190YWcsIG5ld19sb2N1c190YWcsIHVuaXByb3QsIGdlbmVfbmFtZSwgcHJvdGVpbl9uYW1lLCBTeXN0ZW0sIAogICAgUHJvY2VzcywgUGF0aHdheSksIGJ5ID0gImxvY3VzX3RhZyIpCgpzYXZlKEN1cHJpYXZpZHVzX0JhclNlcV8yMDIxLCBmaWxlID0gIi4uLy4uL1NoaW55TGliL2RhdGEvQ3VwcmlhdmlkdXNfQmFyU2VxXzIwMjEuUmRhdGEiKQpgYGAKCg==