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).

One part of the data used here (growth on fructose, succinate, formate) was already published in a previous research article with focus on C. necator’s central carbon metabolism. The preprint is available on BioRxiv. The second part (autotrophic growth on hydrogen, anoxic growth with nitrate respiration) is new data that complements previous results. The focus of this pipeline is to study the energy metabolism of C. necator in diverse conditions, each of it challenging a different aspect/machinery of metabolism. This investigation tries to answer the following key questions:

  1. Which genes are constituting the essential parts of the hydrogenases and formate dehydrogenases, enabling autotrophic lifestyle? Many things about the structure and function of these enzymes is already known, where do these results show differences?
  2. Which genes are mandatory as accessory proteins, for example cofactor synthesis, insertion, and enzyme maturation?
  3. Why does KO of hydrogenases provides a growth benefit in non-autotrophic conditions?
  4. What are the essential parts of the electron transport chain? What complexes are used specifically for some growth conditions?
  5. Is essentiality/fitness correlated with protein abundance? Is there a dosage effect, such that KO of a high abundant iso-enzyme has stronger fitness effect compared to a low-abundant?

2 Libraries

Optionally install required libraries, particularly custom packages from github.

devtools::install_github("m-jahn/lattice-tools")
suppressPackageStartupMessages({
  library(lattice)
  library(latticeExtra)
  library(latticetools)
  library(tidyverse)
  library(forcats)
  library(dendextend)
  library(colorspace)
  library(stringi)
  library(vegan)
  library(tsne)
  library(directlabels)
  library(limma)
})

3 Data import and processing

Import the main data tables with gene fitness data for all conditions. Tables were obtained by processing sequencing data with a custom BarSeq pipeline. The 32 generation sequencing samples are removed due to low diversity, leading to overall lower average read count per gene.

load("../data/barseq/20201222_fru_fitness.Rdata")
df_fitness_frc <- fitness_gene %>% ungroup %>%
  filter(condition != "long pulse", time != 32) %>%
  mutate(ID = as.numeric(ID), substrate = "fructose",
    condition = str_remove(condition, "short "))

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

load("../data/barseq/20210624_H2_NO3_fitness.Rdata")
df_fitness_no3 <- fitness_gene %>% ungroup %>%
  filter(condition %in% c("NO3")) %>%
  mutate(substrate = "nitrate", condition = "continuous")

load("../data/barseq/20210928_H2_fitness.Rdata")
df_fitness_h2 <- fitness_gene %>% ungroup %>%
  mutate(substrate = "hydrogen", condition = "pulse")

load("../data/barseq/20220420_lowCO2_NO3.Rdata")
df_fitness_lowco2 <- fitness_gene %>% ungroup %>%
  filter(condition == "NO3_batch") %>%
  mutate(substrate = "nitrate", condition = "pulse")

# merge fitness tables
df_fitness <- bind_rows(df_fitness_frc, df_fitness_suc, df_fitness_no3,
  df_fitness_h2, df_fitness_lowco2) %>%
  rename(locus_tag = locusId)
rm("df_fitness_frc", "df_fitness_suc", "df_fitness_h2", "df_fitness_no3",
   "df_fitness_lowco2", "fitness_gene")

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

# define standard colors
stdcol <- custom.colorblind()$superpose.line$col

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.

nest_cols = c("time", "condition", "substrate")
df_ref_long <- bind_rows(df_ref, distinct(df_fitness %>% select(!!!nest_cols))) %>%
  complete(locus_tag, nesting(!!!lapply(nest_cols, sym))) %>%
  filter(!is.na(time)) %>% select(locus_tag, !!!nest_cols) %>%
  left_join(df_ref, by = "locus_tag")

df_fitness_summary <- df_fitness %>%
  group_by(locus_tag, scaffold, time, condition, substrate, strains_per_gene) %>%
  summarize(
    .groups = "drop",
    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)
  ) %>%
  full_join(df_ref_long) %>%
  mutate(cond_short = if_else(condition == "pulse", "P", "C")) %>%
  mutate(cond_short = paste0(substrate, " - ", cond_short) %>%
    factor(levels = paste0(
      rep(c("fructose", "succinate", "formate", "hydrogen", "nitrate"), each = 2),
      rep(c(" - P", " - C"), 5)
    )[-8])
  )
Joining with `by = join_by(locus_tag, time, condition, substrate)`
plot_hist_log2FC <- df_fitness_summary %>%
  group_by(condition, substrate) %>%
  slice(1:1000) %>%
  
  xyplot(log2FC_median ~ time | substrate * condition, .,
    groups = locus_tag, as.table = TRUE,
    col = stdcol[1], alpha = 0.2, layout = c(5, 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 %>%
  group_by(condition, substrate) %>%
  slice(1:1000) %>%
  
  xyplot(norm_gene_fitness_median ~ time | substrate * condition, .,
    groups = locus_tag, as.table = TRUE,
    col = stdcol[2], alpha = 0.2, layout = c(5, 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))

null device 
          1 

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 is present in the pulsed vs continuous regime for each substrate (R = 0.64 to 0.87), and within the pulsed conditions the 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, cond_short, norm_gene_fitness_median, gene_name, COG_Process, tstat_median) %>%
  pivot_wider(names_from = cond_short, values_from = norm_gene_fitness_median) %>%
  filter(!is.na(locus_tag)) %>% ungroup

custom_splom(df_fitness_comp %>%
  select(matches(" - [CP]")), pch = 19, cex = 0.3, col = grey(0.4, 0.4))

null device 
          1 

5 Unsupervised clustering of genes by fitness

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. The first step is to generate a matrix from reformatted fitness data, and cluster genes by similarity using a generic clustering algorithm such as hclust(method = "ward.D").

# generate color palette 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))

# create a matrix from wide fitness data for plotting heatmap
mat_heatmap <- df_fitness_comp %>% ungroup %>%
  filter(if_any(.cols = matches(" - [CP]"), ~ !between(., -2, 2))) %>%
  select(matches("locus_tag| - [CP]")) %>%
  # filter out NA rows, and replace extreme values
  drop_na %>% mutate(across(matches(" - [CP]"), 
    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.D2")
mat_heatmap <- mat_heatmap[
  order.dendrogram(as.dendrogram(mat_cluster)),
  levels(df_fitness_summary$cond_short)
]

The second step is to find the optimal number of clusters for subdividing the data set. Silhouette analysis can be used for this purpose. However, the result is not entirely clear, but suggests a number of 5 to 10 clusters would give good separation. A wrapper for more or less fully automated silhouette analysis is available in my Rtools package (random collection of helper functions).

devtools::install_github("m-jahn/r-tools")
silhouette_result <- Rtools::silhouette_analysis(mat = mat_heatmap, n_clusters = 2:20, n_repeats = 10)
silhouette_result$plot_summary$par.settings = custom.colorblind()
silhouette_result$plot_summary
n_clusters = 7

svg(filename = "../figures/figure_silhouette.svg", width = 4, height = 4)
print(silhouette_result$plot_summary)
dev.off()
png 
  2 

# define a custom set of colors for clusters
custom_colors <- colorRampPalette(custom.colorblind()$superpose.line$col[c(2,3,1,5)])(n_clusters)

# plot heatmap
plot_heatmap <- levelplot(mat_heatmap[ ,rev(colnames(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:9+0.5, col = "white", lwd = 1.5)
  }
)

plot_cluster_dend <- mat_cluster %>% as.dendrogram %>%
  set("branches_k_col", custom_colors, k = n_clusters) %>%
  set("branches_lwd", 0.5) %>%
  as.ggdend %>%
  ggplot(labels = FALSE)

gridExtra::grid.arrange(
  plot_cluster_dend + 
    theme(plot.margin = unit(c(0.1, 0.05, -0.26, 0.085),"npc")),
  plot_heatmap,
  nrow = 2
)

null device 
          1 

5.1 Gene similarity, by cluster

The heat map clusters genes together that show similar expression over different conditions. Alternatively, we can use dimensionality reduction to identify clusters of genes that are similar, e.g. through PCA, NMDS, or t-SNE.

# set a seed to obtain same pattern for stochastic methods
set.seed(123)
regex_moa <- "mo[aedbg]([A-Z0-9]+)?"
regex_for <- "fd[swho][A-Z0-9]+"
regex_hyd <- "hox[A-Z0-9]+|hyp[A-Z0-9]+"
regex_etc <- "(qcr|cco|cta|cox|cyo|cyd)([A-Z0-9]+)?"
regex_nit <- "na[rsp]([A-Z0-9]+)?|nir([A-Z0-9]+)?|nor([A-Z0-9]+)?|nos([A-Z0-9]+)?"
regex_all <- paste(c(regex_moa, regex_for, regex_hyd, regex_etc, regex_nit), collapse = "|")

# run t-SNE analysis
SNE <- tsne(mat_heatmap %>% dist, max_iter = 500)
sigma summary: Min. : 0.443752156712937 |1st Qu. : 0.631509019280422 |Median : 0.707827611279302 |Mean : 0.768252158651903 |3rd Qu. : 0.880353013632023 |Max. : 1.38975561028632 |
Epoch: Iteration #100 error is: 14.0419952880744
Epoch: Iteration #200 error is: 0.473145722307766
Epoch: Iteration #300 error is: 0.411810046962138
Epoch: Iteration #400 error is: 0.391638658174712
Epoch: Iteration #500 error is: 0.390500309862624
df_tsne <- SNE %>% setNames(c("x", "y")) %>% as_tibble %>%
  mutate(locus = rownames(mat_heatmap)) %>%
  left_join(enframe(cutreeord(mat_cluster, k = n_clusters), "locus", "cluster")) %>%
  rename(locus_tag = locus) %>%
  left_join(select(df_fitness_comp, locus_tag, gene_name)) %>%
  mutate(
    gene_name = stri_extract_first_regex(gene_name, regex_all) %>%
    str_replace("mog", "mogA")
  )
Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if `.name_repair` is omitted as of
tibble 2.0.0.
ℹ Using compatibility `.name_repair`.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Joining with `by = join_by(locus)`
Joining with `by = join_by(locus_tag)`
plot_tsne <- df_tsne %>%
  xyplot(V2 ~ V1, .,
    groups = cluster, col = custom_colors,
    xlab = "tSNE 1", ylab = "tSNE 2",
    par.settings = custom.colorblind(),
    labels = .[["gene_name"]], selected = {!is.na(.[["gene_name"]])},
    panel = function(x, y, selected, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.repellabels(x, y, selected = selected, cex = 0.65,
        draw_box = TRUE, box_fill = grey(0.95, 0.5), ...)
      directlabels::panel.superpose.dl(x, y, ...)
    }
  )

print(plot_tsne)

5.2 Functions enriched in different clusters

Extract cluster number for each gene and perform GO or KEGG enrichment per cluster. The goal is to identify the most prevalent biological functions per cluster.

df_cluster <- cutreeord(mat_cluster, k = n_clusters) %>%
  enframe(name = "locus_tag", value = "cluster") %>%
  arrange(cluster)

We use the functions kegga for KEGG enrichment analysis and goana for GO term enrichment from the limma package. Both functions test for over or under-representation of genes associated with certain pathways or GO terms. The functions don’t take the strength of differential fitness into account (DF; the depletion/enrichment over time).

tryCatch(
  df_kegg_enrichment <- lapply(1:n_clusters, function(clust) {
    filter(df_cluster, cluster == clust) %>% pull(locus_tag) %>%
    kegga(species.KEGG = "reh") %>%
    mutate(cluster = clust)
  }) %>% bind_rows,
  error = function(e) print(e)
)

Visualize the pathways that are most enriched for the different gene clusters.

plot_keggenrich <- df_kegg_enrichment %>%
  mutate(
    kegg_pathway = tolower(Pathway),
    kegg_pathway = str_remove_all(kegg_pathway, ".?biosynthesis| of| metabolism"),
    kegg_pathway = str_sub(kegg_pathway, 1, 32)
  ) %>%
  filter(N >= 5, N <= 200) %>%
  mutate(log10_p_value = log10(P.DE), .keep = "unused") %>%
  filter(log10_p_value < 0) %>%
  group_by(cluster) %>%
  arrange(log10_p_value) %>%
  slice(1:3) %>% rowwise() %>%
  mutate(
    kegg_pathway = str_remove(kegg_pathway, " - Cupriavidus necator H16"),
    kegg_pathway = str_glue("{kegg_pathway} ({cluster})")
  ) %>% ungroup %>%
  
  xyplot(factor(kegg_pathway, rev(unique(kegg_pathway))) ~ log10_p_value, .,
    groups = cluster,
    par.settings = custom.colorblind(),
    col = custom_colors,
    xlab = expression("log"[10]*" p-value"), ylab = "",
    panel = function(x, y, labels, ...) {
      panel.grid(h = -1, col = grey(0.9))
      panel.xyplot(x, y, ...)
    }
  )

print(plot_keggenrich)

null device 
          1 

6 Genes essential in all conditions

6.1 Cluster 1

  • before looking at condition-specific essentiality, can also look at genes essential in all conditions
  • these genes are mostly present in cluster 1
  • we assume that these are amino acid biosynthesis genes
  • they become essential when transferring C. necator from rich to minimal/defined medium
  • in total 33 genes with depletion

Generalized function to plot (small) heatmaps.

heatmap_fitness <- function(data, key = TRUE) {
  
  # make some small adjustments to optical appearance
  data %>%
  mutate(norm_gene_fitness_median = norm_gene_fitness_median %>%
    replace(., . < -6, -6) %>% replace(., . > 6, 6)) %>%

  # plot heatmap
  levelplot(norm_gene_fitness_median ~ factor(gene_name) * fct_rev(cond_short), .,
    par.settings = custom.colorblind(), colorkey = key,
    col.regions = colorRampPalette(heat_cols)(25),
    at = seq(-6, 6, 0.5), aspect = "iso",
    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)
    }
  )
}

Generalized function to plot genes for different regions of the genome.

genome_plot <- function(df, xlim = NULL, title = "", rot_labels = 0) {
  df <-  replace_na(df, list(strains_per_gene = 0))
  theme = custom.colorblind(col = c(grey(0.7), grey(0.85)))
  theme$axis.line$col = grey(0.3)
  theme$axis.text$col = grey(0.3)
  theme$axis.text$cex = 0.7
  
  if (!is.null(xlim))
    xscale = list(limits = xlim)
  else 
    xscale = list()
  xyplot(end/1000 ~ start/1000, df,
    groups = strand, cex = 0.7, lwd = 1,
    par.settings = theme, strains = df$strains_per_gene,
    scales = list(y = list(draw = FALSE), x = xscale),
    ylim = c(-3,2), xlab = "", ylab = "",
    gene_strand = df[["strand"]],
    gene_name = df[["gene_name"]],
    panel = function(x, y, strains, ...) {
      panel.geneplot(x, y, arrows = TRUE, 
        tip = 0.1, rot_labels = rot_labels, ...)
      panel.text((x+y)/2, rep(0, length(x)), labels = strains, cex = 0.7)
      panel.text(mean(xlim), 1.5, labels = title, col = 1)
    }
  )
}

Plot heat map of cluster 1 gene fitness.

plot_cl1 <- df_cluster %>%
  filter(cluster == 1) %>%
  left_join(df_fitness_summary, by = "locus_tag") %>%
  filter(time == 8) %>%
  group_by(COG_Process) %>%
  mutate(COG_n = n()) %>%
  ungroup() %>%
  arrange(desc(COG_n), COG_Process, gene_name) %>%
  mutate(gene_name = fct_inorder(gene_name)) %>%
  heatmap_fitness()

print(plot_cl1)

  • Which functional categories (COG) do genes belong to?
plot_cl1_function <- df_cluster %>%
  filter(cluster == 1) %>%
  left_join(df_fitness_comp, by = "locus_tag") %>%
  count(COG_Process) %>%
  arrange(desc(n)) %>%
  mutate(COG_Process = fct_inorder(COG_Process)) %>%
  mutate(vars = factor("COG process")) %>%
  xyplot(vars ~ n, .,
    stack = TRUE, horizontal = TRUE,
    auto.key = list(columns = 2),
    par.settings = custom.colorblind(),
    xlab = "count", ylab = "", lty = 0,
    xlim = c(0, 33),
    groups = COG_Process,
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.barchart(x, y, ...)
    }
  )

print(plot_cl1_function)

7 Supervised analysis of energy metabolism

7.1 Molybdenum cofactor biosynthesis

  • modAB = molybdenum transporter
  • moaACDE = molybdopterin backbone biosynthesis
  • moeA = molybdopterin molybdenum transferase
  • mobAB = molybdenum cofactor guanylyltransferase,
  • mog = molybdopterin biosynthesis protein (role unclear?)
# plot heatmap
df_moco <- df_fitness_summary %>% filter(time == 8, grepl("mo[aedbg]", gene_name)) %>%
  filter(!str_detect(gene_name, "moaA2")) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "mo[aedbg]([A-Z0-9]+)?") %>%
    str_replace("mog", "mogA")) %>%
  mutate(gene_name = factor(gene_name, c("moaA", "moaC", "moaD", "moaE", "moaF",
    "modA", "modB", "modC", "mogA", "moeA1", "moeA2", "moeA3", "mobA", "mobB", "mobB2", "mobB3")))
plot_moco_fit <- heatmap_fitness(df_moco)

# plot genomic locations
df_moco <- df_moco %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_moco_g1 <- genome_plot(df_moco, xlim = c(2455.9, 2458.6), title = "chr 1")
plot_moco_g2 <- genome_plot(df_moco, xlim = c(2787.8, 2791.2), title = "chr 1")

print(plot_moco_fit, position = c(0,0.21,1,1.05), more = TRUE)
print(plot_moco_g1, position = c(0.17,-0.1,0.57,0.4), more = TRUE)
print(plot_moco_g2, position = c(0.53,-0.1,0.83,0.4))

Interesting results to double-check:

  • Why is moaA essential for nitrate respiration but not formate? Is there another iso-enzyme for first step of MoCo synthesis?
  • mogA, moaA, moaB, moaE, modB, or modC deletion mutants are not viable in E.coli. Except for moaA, also modA,B,C are not essential for growth on formate.
  • modABC is the 3-subunit membrane transport system; it should be essential, however one explanation for it not being essential is that another low-affinity transporter might exist that compensates for the loss in medium with high Mo-concentration (Xia et al., 2018). The authors write in the discussion: “Molybdate can be taken up by the sulfate-transport system in the absence of a functional high-affinity molybdate-transport system in E. coli and B. japonicum (Rosentel et al., 1995; Delgado et al., 2006).”
  • moeA (molybdenum transferase) is present with 3 isoenzymes. MoeA1 and A2 both seem to be important for fitness as a KO of each one of them reduces fitness partially but not fully on formate/nitrate.

7.2 Formate dehydrogenase

  • fdsABCDG = primary soluble FDH
  • fdwAB = secondary soluble FDH
  • fdhABCD = primary membrane-bound FDH, 2 copies A1B1… and A2B2…
  • fdoGHI = secondary membrane-bound FDH
# plot heatmap
df_fdh <- 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 = recode(gene_name, `fdhD` = "fdsC", `fdhC` = "fdhC1", `fdhE` = "fdhE1")) %>%
  mutate(gene_name = factor(gene_name, c(paste0("fds", c("A", "B", "C", "D", "G", "R")),
    "fdwA", "fdwB", paste0("fdh", c("A1", "B1", "C1", "D1", "E1", "A2")), "fdoG")))
plot_fdh_fit <- heatmap_fitness(df_fdh)

# plot genomic locations
df_fdh <- df_fdh %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_fdh_g1 <- genome_plot(df_fdh, xlim = c(677.7, 685.6), title = "chr 1")
plot_fdh_g2 <- genome_plot(df_fdh, xlim = c(3167.9, 3174.9), title = "chr 1")

print(plot_fdh_fit, position = c(0,0.23,1,1.05), more = TRUE)
print(plot_fdh_g1, position = c(0.17,-0.1,0.57,0.4), more = TRUE)
print(plot_fdh_g2, position = c(0.52,-0.1,0.83,0.4))

7.3 Hydrogenases

  • hoxA = master regulator, 2-component system. HoxA is constantly active in its non- phosphorylated state below a temperature of 33*C. Originally, hoxA activity is regulated by the kinase HoxJ (together with HoxBC, the ‘regulatory’ hydrogenase). This function is inactivated due to a mutation in hoxJ, Friedrich & Friedrich, J Bac, 1983, Lenz et al., JMMB, 2004.
  • hoxKGZ = membrane-bound hydrogenase
  • hoxMLOQRTV = MBH accessory genes (cofactor incorporation, subunit assembly, and twin-arginine-dependent membrane translocation - TAT), Schubert et al., Mol Microbio, 2007, Fritsch et al., J Bac, 2011 Schwartz et al., J Mol Bio, 2003
  • hoxN1, hoxN2 = Nickel/Cobalt transporters. HoxN2 51% identical on AA level but not functional, Schwartz et al., J Mol Bio, 2003
  • hoxBC = regulatory hydrogenase RH, oxygen-resistant non-energy generating hydrogen sensor. HoxC is large active-site-containing subunit, hoxB small subunit HoxB carrying Fe-S clusters
  • HoxJ = RH binding signal transducer, histidine protein kinase Löscher et al., Chem Phys Chem, 2010 Buerstel et al., PNAS, 2016
  • hoxFUYH = soluble hydrogenase
  • hoxW = soluble hydrogenase (HoxH-specific) C-terminal protease (maturation)
  • hoxI = optional subunit of SH (hoxFUYH); specific reaction site for NADPH, Burgdorf et al., J Bac, 2005
  • a 4th hydrogenase exist on pHG1, made from 2 subunits (hofK, hofG, pHG064/065), see Schäfer et al., 2015.
  • these subunits have no effect on fitness (high coverage of 9-11 mutants per gene), and are very lowly expressed (PHG064 n.d., PHG065 low abundance)
# plot heatmap
df_hyd <- df_fitness_summary %>%
  filter(time == 8, grepl("hox", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "hox[A-Z0-9]+")) %>%
  ungroup %>% mutate(gene_name = factor(gene_name, paste0("hox", c("K","G","Z","M",
   "L","O","Q","R","T","V","A","B","C","J","N","N2","F","U","Y","H","W","I"))))
plot_hyd_fit <- heatmap_fitness(df_hyd)

# plot genomic locations
df_hyd <- df_hyd %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_hyd_g1 <- genome_plot(df_hyd, xlim = c(0, 4.1), title = "pHG1")
plot_hyd_g2 <- genome_plot(df_hyd, xlim = c(15.1, 22.8), title = "pHG1")
plot_hyd_g3 <- genome_plot(df_hyd, xlim = c(79.2, 85.5), title = "pHG1")

print(plot_hyd_fit, position = c(0,0.23,1,1.05), more = TRUE)
print(plot_hyd_g1, position = c(0.15,-0.1,0.39,0.4), more = TRUE)
print(plot_hyd_g2, position = c(0.35,-0.1,0.65,0.4), more = TRUE)
print(plot_hyd_g3, position = c(0.61,-0.1,0.90,0.4))

7.4 Hydrogenase pleiotropy genes (maturation)

  • hypA1,B1,F1,C1,D1,E1 = Ni-Fe metallocenter formation proteins for both MBH and SH. “HypC1, hypD1, and hypE1 are essential for SH and MBH maturation, while only one intact copy each of hypA, hypB, and hypF is needed”, Schwartz et al., J Mol Bio, 2003
  • hypA and B
  • hypCDEF all form a complex to assemble the Ni-Fe-CO-CN cofactor
  • hypX = CO cofactor formation for NiFe-hydrogenase. HypX converts formyl-THF into water and CO in aerobic conditions, Schulz et al., JACS, 2020
  • hypA2,B2,F2 = alternative genes for hypA1,B1,F1, not sufficient alone for hydrogenase formation
  • hypF3,hypC2,hypD2,hypE2,hypA3,hypB3. The physiological function of these extra hyp genes is unclear
df_hyp <- df_fitness_summary %>% filter(time == 8, grepl("hyp", gene_name)) %>%
  # correct names of several hyp genes to make it more systematic
  mutate(gene_name = gsub("hypA PHG094", "hypA2 PHG094", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "hyp[A-Z0-9]+") %>%
    recode(hypA = "hypA1", hypB = "hypB1", hypC = "hypC1", hypD = "hypD1", hypE = "hypE1")) %>%
  ungroup %>% mutate(gene_name = factor(gene_name, paste0("hyp", c("A1","B1","C1","D1","E1",
    "F1","X","A2","B2","C2","D2","E2","F2","A3","B3","F3"))))
plot_hyp_fit <- heatmap_fitness(df_hyp)

# plot genomic locations
df_hyp <- df_hyp %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_hyp_g1 <- genome_plot(df_hyp, xlim = c(8.3, 15.6), title = "pHG1")
plot_hyp_g2 <- genome_plot(df_hyp, xlim = c(66.3, 73.5), title = "pHG1")
plot_hyp_g3 <- genome_plot(df_hyp, xlim = c(85.0, 89.7), title = "pHG1")

print(plot_hyp_fit, position = c(0,0.23,1,1.05), more = TRUE)
print(plot_hyp_g1, position = c(0.26,-0.1,0.78,0.4), more = TRUE)

7.5 Other complexes important in energy metabolism

  • succinate dehydrogenase
    • sdhABCD = membrane integral succinate dehydrogenase, reducing UQ to UQH2: all of its genes are strictly essential, no KO mutants available
  • NADH dehydrogenase
    • nuoABCDEFGHIJKLMN = membrane integral NADH dehydrogenase, oxidizes NADH, pumps 4 H+, reduces UQ to UQH2: all of its genes are strictly essential, no KO mutants available

7.6 Electron transport chain

Cytochrome e- transport proteins, cytochrome reductases, and terminal oxidases.

  • quinol oxidases
    • cyoA1B1C1D1, … (3 copies) = cytochrome bo3 complex, quinole oxidase, low O2 affinity
    • cydA1B1, … (2 copies) = cytochrome bd complex, quinole oxidase, high O2 affinity
  • cytochrome reductases
    • qcrABC (aka petABC, H16_A3398, H16_A3397, H16_A3396) = cytochrome bc1 reductase
  • cytochrome C e- carriers
    • cytochrome c553 according to uniprot: H16_A3451, H16_A0830, H16_A0846, H16_B1452, H16_A3576, H16_A1121, H16_A1120. According to String DB, H16_A3576 is in same operon as thiosulphate oxidation pathway (SoxXYZABCD), where thiosulfate is used as electron source Zhang et al., 2020
  • terminal cytochrome C oxidases
    • coxMNOPQ = bb3 complex, affinity unknown, not essential for growth or N-fixation in B. japonycum where it was discovered.
    • ctaABCDEG = aa3 complex, low O2 affinity
    • ccoGNOP = cbb3 complex, high affinity, high similarity to NO3 reductase
    • 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.
df_etc <- 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]+)?") %>%
    factor(c(
      paste0("cyo", c("A1","A2","A3","B1","B2","B3","C1","C2","D1","D2")),
      paste0("cyd", c("A1","B1","B2")), paste0("qcr", c("A","B","C")),
      paste0("H16_", c("A3576","A0846","A1120","A1121","A1031","A3451")),
      paste0("cox", c("M","N","O","P","Q")), paste0("cta", c("A","B","C","D","E","G")),
      paste0("cco", c("G","N","O","P"))
    ))
  )
plot_etc_fit <- heatmap_fitness(df_etc)

# plot genomic locations
df_etc <- df_etc %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_etc_g1 <- genome_plot(df_etc, xlim = c(3675.2, 3679.0), title = "chr 1")
plot_etc_g2 <- genome_plot(df_etc, xlim = c(361.2, 371.2), title = "chr 1")
plot_etc_g3 <- genome_plot(df_etc, xlim = c(2509.5, 2515.2), title = "chr 1")

print(plot_etc_fit, position = c(0,0.17,1,1.05), more = TRUE)
print(plot_etc_g1, position = c(0.1,-0.1,0.31,0.4), more = TRUE)
print(plot_etc_g2, position = c(0.28,-0.1,0.65,0.4), more = TRUE)
print(plot_etc_g3, position = c(0.62,-0.1,0.9,0.4))

7.7 Nitrate respiration

Nitrate reductase (NAR)

Reaction: HNO3- + QH2 –> NO2- + H2O + Q.

Two copies of the nar operon, one on the megaplasmid and one on chromosome 2. Several other enzymes catalyze similar reactions as NAR. NAS is cytoplasmic and NADH dependent assimilatory nitrate reductase Cramm R, 2008. NAP is periplasmic membrane protein complex accepting QH2, involved in nitrate respiration but probably also dissipation of excess redox power. Here we just focus on the main NAR enzyme.

df_nar <- df_fitness_summary %>%
  filter(time == 8, grepl("na[rsp]", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "na[rsp]([A-Z0-9]+)?") %>%
    recode(narG = "narG1", narH = "narH1", narI = "narI1", narL = "narL1", narX = "narX1"))
plot_nar_fit <- heatmap_fitness(df_nar)

# plot genomic locations
df_nar_genes <- df_nar %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_nar_g1 <- genome_plot(df_nar_genes, xlim = c(279.5, 294.5), title = "pHG1")

print(plot_nar_fit, position = c(0,0.26,1,1.05), more = TRUE)
print(plot_nar_g1, position = c(0.2,-0.1,0.85,0.4))

Nitrite reductase (NIR)

Reaction: NO2- + QH2 –> NO + H2O + Q

NIR is a cd1 type copper containing terminal oxidase, homodimer of 2 nirS subunits. The genes for nitrite reductase are assembled in a cluster (nirS, -C, -F, -D, -G, -H, -J, -N, -E) spanning 8.6 kb on chromosome 2. The first gene of this cluster, nirS, is the structural gene encoding cd1 -NIR. The re-maining genes are presumed to be accessory genes essential for maturation of active nitrite reductase (?).

df_nir <- df_fitness_summary %>%
  filter(time == 8, grepl("nir", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "nir([A-Z0-9]+)?"))
plot_nir_fit <- heatmap_fitness(df_nir)

# plot genomic locations
df_nir_genes <- df_nir %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_nir_g1 <- genome_plot(df_nir_genes, xlim = c(2576.3, 2585.9), title = "chr 2")

print(plot_nir_fit, position = c(0,0.26,1,1.05), more = TRUE)
print(plot_nir_g1, position = c(0.3,-0.1,0.75,0.4))

NO reductase (NOR)

Reaction: NO + QH2 –> N2O + H2O + Q

From Cramm R, 2008: “The catalytic sub-unit (NorB) of all NORs that have been characterized to date contains one high-spin heme b and a binuclear catalytic center of a low-spin heme b and a non-heme iron. The best investigated NORs are heterodimers that contain, in addition to NorB, a heme c-containing subunit NorC which channels lectrons from the physiological electron donor cytochrome c to NorB.”

df_nor <- df_fitness_summary %>%
  filter(time == 8, grepl("nor", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "nor([A-Z0-9]+)?"))
plot_nor_fit <- heatmap_fitness(df_nor)

# plot genomic locations
df_nor_genes <- df_nor %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_nor_g1 <- genome_plot(df_nor_genes, xlim = c(256.4, 262.1), title = "pHG1")

print(plot_nor_fit, position = c(0,0.25,1,1.05), more = TRUE)
print(plot_nor_g1, position = c(0.3,-0.1,0.75,0.4))

NOS (nitrous oxide) reductase

Reaction: N2O + QH2 –> N2 + H2O + Q

The NOS of R. eutropha H16 has not been investigated on the biochemical level. Genes for NOS are located adjacent to the nor genes on pHG1. NosC (PHG253) might be the cyt c that is the e- donor for NOS. The nosC KO is enriched on formate, i.e. grows better than WT. Is nosC taking e- away from formate-fed ETC?

df_nos <- df_fitness_summary %>%
  filter(time == 8, grepl("nos", gene_name)) %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "nos([A-Z0-9]+)?"))
plot_nos_fit <- heatmap_fitness(df_nos)

# plot genomic locations
df_nos_genes <- df_nos %>% group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop")
plot_nos_g1 <- genome_plot(df_nos_genes, xlim = c(261.1, 271.9), title = "pHG1")

print(plot_nos_fit, position = c(0,0.25,1,1.05), more = TRUE)
print(plot_nos_g1, position = c(0.22,-0.1,0.85,0.4))

The following section simply plots all nitrate respiration modules on one canvas.

plot_nitrate_all <- bind_rows(df_nar, df_nor, df_nos, df_nir) %>%
  mutate(gene_name = factor(gene_name, c(
    paste0("nar", c("X1", "L1", "K1", "K2", "G1", "H1", "I1", "X2", "L2", "G2", "H2", "I2", "K3", "K5")),
    paste0("nap", c("A", "B", "C", "D")), paste0("nas", c("D", "E")),
    paste0("nir", c("S", "C", "F", "D", "J", "N", "E")),
    paste0("nor", c("A", "B", "R1", "A2", "B2", "R2")),
    paste0("nos", c("L", "Y", "F", "D", "R", "Z", "C", "X"))
  ))) %>%
  heatmap_fitness()

print(plot_nitrate_all, position = c(0,0.23,1,1.05), more = TRUE)
print(plot_nar_g1, position = c(0,-0.1,0.35,0.4), more = TRUE)
print(plot_nir_g1, position = c(0.33,-0.1,0.58,0.4), more = TRUE)
print(plot_nor_g1, position = c(0.56,-0.1,0.72,0.4), more = TRUE)
print(plot_nos_g1, position = c(0.70,-0.1,0.96,0.4), more = TRUE)

7.8 Comparison of nitrate respiration conditions

  • we have two different nitrate respiration conditions, continuous and pulsed
  • low depletion of nitrate/nitrite/nitrous respiration mutants probably due to constant NO3 supply
df_fitness_summary %>%
  filter(substrate == "nitrate", time == 8, !is.na(gene_name)) %>%
  select(gene_name, condition, norm_gene_fitness_median) %>%
  pivot_wider(names_from = condition, values_from = norm_gene_fitness_median) %>%
  mutate(
    dfit = pulse - continuous,
    significant = !between(dfit, quantile(dfit, probs = c(0.003), na.rm = TRUE),
      quantile(dfit, probs = c(0.997), na.rm = TRUE))) %>%
  
  # plot
  xyplot(continuous ~ pulse, data = ., groups = significant,
    labels = .[["gene_name"]],
    par.settings = custom.colorblind(), col = custom_colors,
    xlim = c(-9, 3), ylim = c(-9, 3),
    xlab = "Nitrate respiration (P)", ylab = "Nitrate respiration (C)",
    panel = function(x, y, labels, ...) {
      panel.grid(h = -1, col = grey(0.9))
      panel.abline(a = 0, b = 1, col = grey(0.5), lty = 2, size = 0.8)
      panel.abline(a = 4, b = 1, col = grey(0.5), lty = 2, size = 0.8)
      panel.abline(a = -4, b = 1, col = grey(0.5), lty = 2, size = 0.8)
      panel.xyplot(x, y, ...)
      panel.repellabels(x, y, labels, cex = 0.6,
        selected = .[["significant"]],
        draw_box = TRUE, box_fill = "white", box_line = TRUE, ...)
    }
  )

7.9 Non-hydrogenase genes important for H2 fitness

Different gene clusters appear based on the fitness pattern in different conditions. Clusters specific for lithoautotrophic metabolism are particularly interesting. We can add the cluster IDs to the main data frame, and investigate fitness. Or simply select genes that show depletion only for the hydrogen metabolizing condition.

df_fitness_summary <- df_fitness_summary %>%
  left_join(by = "locus_tag",
    enframe(cutreeord(mat_cluster, k = n_clusters), "locus_tag", "cluster")
  )
df_fitness_summary %>%
  filter(time == 8, !str_detect(gene_name, "hox|hyp")) %>%
  mutate(hyd_essential = case_when(
    substrate == "hydrogen" & norm_gene_fitness_median <= -2 ~ TRUE,
    substrate != "hydrogen" & norm_gene_fitness_median >= -2 ~ TRUE,
    TRUE ~ FALSE
  )) %>% 
  group_by(locus_tag) %>%
  filter(sum(hyd_essential) == 9 |
    locus_tag %in% c("H16_A0325", "H16_A0326")) %>%
  ungroup %>% arrange(locus_tag) %>%
  mutate(gene_name = factor(gene_name, unique(gene_name))) %>%
  mutate(norm_gene_fitness_median = norm_gene_fitness_median %>%
    replace(., . < -6, -6) %>% replace(., . > 6, 6)) %>%
  heatmap_fitness

  • ptsI + ptsH - according to databases, a clear hit for a PEP-dependent sugar PTS system. This can’t be true for C. necator otherwise it would not show the familiar pattern of depletion on hydrogen and growth benefit on other substrates. This PTS system must be implicated in hydrogen metabolism, probably a transporter? The genes downstream of ptsIH do not seem to be affected (H16_A0327 to H16_A0329).
  • hldD (H16_A0804) - ADP-L-glycero-beta-D-manno-heptose-6-epimerase, involved in LPS sugar phosphate synthesis. No obvious connection to hydrogen metabolism.
  • glnE (H16_A1127) - Bifunctional glutamine synthetase adenylyltransferase. Involved in the regulation of glutamine synthetase GlnA. Transfers or removes adenylyl-residues to and from GlnA, thereby regulating N uptake through GlnA. No obvious connection to hydrogen metabolism. Only 1 mutant in library.
  • H16_A2692 - uncharacterized membrane protein. Is neutral in all other conditions, just as are all surrounding genes (H16_A269[0-9]). Small, 141 AA, potential carbonic anhydrase or transporter?
  • bolA (H16_A3419) - Predicted transcriptional regulator, no known domains or published functions. Only 1 mutant in library.
  • yadG (H16_A3421) Separated by only 1 gene (yadH, H16_A3420) from the previous hit, shows very specific depletion for hydrogen. 7-8 mutants quantified, annotated as ABC transporter, ATPase function. YadH is the permease partner of the 2 subunits, however does not show depletion. COG process ‘defense’ because it supposedly is active as anti-drug efflux pump.
  • H16_B1603 - ABC-type transporter, ATPase coupled transmembrane transporter. Strong depletion on hydrogen only. All surrounding genes are neutral to fitness. Only 1 mutant in library. A candidate for a CO2/HCO3- transporter? Aligns best with a lot of different ABC transporters, with spermidine/putrescine transporters best hits.

7.10 CO2 transporter/carbonic anhydrase homologs

All CAs with quantified fitness are neutral during lithoautotrophic growth (H16_A1192 - cag, H16_B2403 - caa, H16_B2270 - can2). Only H16_A0169 (can) is strictly essential, no mutants are available. This is different to the results from Gai et al., AMB Express, 2014 that found two essential CAs, can and caa:

“All four purified CAs were capable of performing the interconversion of CO2 and HCO3– […] Deletion of can affected the growth of R. eutropha; however the growth defect could be compensated by adding CO2 to the culture. Deletion of caa, encoding an alpha-CA, had the strongest deleterious influence on cell growth.”.

Also Kusian et al., J Bac, 2002 report the essentiality of can for lithoautotrophic growth. Caa fitness was quantified in the library with 11 mutants, yet does not show any phenotype on any substrate. The results in Gai et al. are somewhat contradictory, as the caa completion test in a quadruple-CA mutant had no beneficial effect on growth.

Altogether, no specific bicarbonate transporters are annotated in C. necator. However, important bicarbonate (HCO3+) or CO2 transporters known from e.g. cyanobacteria (Synechocystis sp. PCC 6803) could be used to search for homologous genes in C. necator. The following CO2/HCO3+ transporters are known in Synechocystis:

  • BicA (sll0834): constitutive high-flux, low-affinity Na+/HCO3+ symport
  • SbtA (slr1512): inducible low-flux, high-affinity Na+/HCO3+ symport
  • BCT1 (cmpABCD operon/slr0040-44): inducible high-affinity, ATP-dependent uniport
  • NDH (nuo operon/): all genes of this operon are essential in C. necator, no cond. fitness

The first three of these genes were blasted against the genome of C. necator. The figure shows alignment scores for the top 20 hits.

df_co2_transport_align <- read_tsv("../data/alignments/CO2_transporters.tsv", col_types = cols()) %>%
  group_by(target_name) %>%
  mutate(rank = seq_along(uniprot))
  
  xyplot(score + -log10(e_value) ~ rank | target_name, df_co2_transport_align,
    par.settings = custom.colorblind(), xlim = c(-1, 21),
    xlab = "rank", ylab = expression("-log"[10]*" e value, score"),
    between = list(x = 0.5, y = 0.5), lwd = 0,
    scales = list(alternating = FALSE),
    panel = function(x, y, z, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.barplot(x, y, ewidth = 0.5, fill_alpha = 0.7, ...)
      panel.key(..., points = FALSE, corner = c(0.97, 0.97))
    }
  )

BicA and cmpB seem to have some very similar homologs in the C. necator genome. However, none of these homologs has a clear phenotype related to hydrogen metabolism.

df_co2_transport_align %>%
  filter(rank <= 20) %>% 
  select(uniprot, target_name, rank) %>%
  inner_join(df_fitness_summary, by = "uniprot") %>% ungroup %>%
  mutate(norm_gene_fitness_median = norm_gene_fitness_median %>%
    replace(., . < -6, -6) %>% replace(., . > 6, 6)) %>%
  heatmap_fitness
Warning in inner_join(., df_fitness_summary, by = "uniprot") :
  Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 1 of `x` matches multiple rows in `y`.
ℹ Row 100034 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship = "many-to-many"` to silence this
  warning.

7.11 Main conclusions

  • MoCo cofactor is important for growth on formate and nitrate respiration, but not for growth on hydrogen
  • Main FDH is S-FDH both in terms of protein abundance, inducibility, and essentiality
  • Alternative S-FDH operon fdw does not play a roll
  • Both hydrogenases, S-H and m_H have similar fitness penalty on hydrogen when KOed
  • Hydrogenase genes, but even more so master regulator hoxA and Hydrogenase pleiotropy (hyp) genes confer fitness advantage when KOed on all non-loithoautotrophic substrates
  • Cytochrome reductase bc1 complex (qcrABC) is particularly important for growth on formate
  • Bc1 complex plays no role on nitrate, therefore no cross talk (while a bc1 KO mutant was growth-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. Main cytochrome complex for most substrates including hydrogen
  • Cbb3-type cytochrome oxidase (cco operon) also important for growth on formate, while cta operon (aa3 complex) more important on fructose and succinate

8 Central carbon metabolism

8.1 gene-reaction relationship

To show (conditional) essentiality of genes which are related to central metabolic reactions, we need to obtain a table of gene-reaction relationships. Such a table was used in our eLife paper, Jahn et al., 2021, and was exported from the curated genome scale model of C. necator.

  • download gene-reaction table
  • add some genes manually that are not metabolic reactions but interesting regulators
  • we add:
    • cbbR (H16_B1396) cbb operon transcriptional activator
    • regA (H16_A0202) response regulator, 2-comp. system
    • regB (H16_A0203) kinase, 2-comp. system
    • rpoN (H16_A0387) Sigma 54 RNA-polymerase sigma factor
    • hoxA (PHG019) hoxA master regulator
url_reactions <- url("https://github.com/m-jahn/R-notebook-ralstonia-proteome/raw/main/data/input/model_reactions.csv")
df_reactions <- read_csv(url_reactions, show_col_types = FALSE) %>%
  rename(locus_tag = genes) %>%
  add_row(reaction_id = "Regulators", locus_tag = "H16_B1396, H16_A0202, H16_A0203, H16_A0387, PHG019")
New names:
• `` -> `...1`
df_reactions %>%
  filter(!str_detect(reaction_id, "^EX_")) %>%
  head()

8.2 Plot conditional fitness broken down by CCM pathway

  • use the same set of enzymes as in Jahn et al., 2021
  • using these sets, extract fitness from results table and plot minifigures
  • these can be arranged in/around a map of the central carbon metabolism (CCM)
  • reactions with slightly different substrates/products but that are gene-wise identical have been removed
  • these are SUCDi, ACONT2, TKT2, and PDH3 which is identical to AKGDH
list_ccm_enzymes <- list(
  CBB = c("PGK", "TPI", "GAPD", "FBA", "FBP", "TKT1", "TAh", "RPE", "RPI", "PRUK", "RBPC"),
  ED = c("PGI", "G6PDH2r", "PGL", "EDD", "EDA"),
  PYR = c("PGM", "ENO", "PYK", "PDH1", "PDH2", "PC", "PPC", "ME1", "ME2", "CS"),
  TCA = c("ACONT1", "ICDHx", "ICDHyr", "AKGDH", "SUCOAS", "SUCD1", "FUM", "MDH", "MALS"),
  REG = c("Regulators")
)
# find genes for metabolic reactions
list_reaction <- list_ccm_enzymes %>%
  enframe(name = "pathway", value = "reaction_id") %>%
  tidyr::unnest(reaction_id)

list_genes <- df_reactions %>%
  right_join(list_reaction, by = "reaction_id") %>%
  separate_longer_delim(locus_tag, ", ") %>%
  select(pathway, reaction_id, locus_tag)

# get fitness for these genes
df_fitness_ccm <- df_fitness_summary %>%
  filter(time == 8) %>%
  inner_join(list_genes, by = "locus_tag")

# plot heatmaps
plots_ccm <- df_fitness_ccm %>%
  arrange(pathway, reaction_id, locus_tag) %>%
  mutate(
    gene_name = locus_tag,
    # gene_name = eggNOG_name %>%
    # str_extract("[a-zA-Z0-9_]+") %>%
    # fct_inorder(),
    cond_short = case_match(cond_short,
      "formate - C" ~ "FoC",
      "fructose - C" ~ "FrC",
      "nitrate - C" ~ "NiC",
      "succinate - C"   ~ "SuC",
      "formate - P" ~ "FoP",
      "fructose - P" ~ "FrP",
      "hydrogen - P" ~ "HyP",
      "nitrate - P" ~ "NiP",
      "succinate - P"~ "SuP") %>%
      factor(., unique(.)[c(3,8,7,1,5,4,9,2,6)]),
    norm_gene_fitness_median = norm_gene_fitness_median %>%
           replace(., . < -6, -6) %>% replace(., . > 6, 6)
  ) %>%
  group_by(pathway, reaction_id) %>%
  dplyr::group_split() %>%
  lapply(function(df) {
    levelplot(
      norm_gene_fitness_median ~ fct_rev(cond_short) * fct_rev(gene_name) |
        reaction_id, df,
      par.settings = custom.colorblind(), colorkey = FALSE,
      col.regions = colorRampPalette(heat_cols)(25),
      at = seq(-6, 6, 0.5), aspect = "iso", xlab = "", ylab = "",
      scales = list(alternating = FALSE, cex = 0.7, tck = 0,
        x = list(rot = 90)),
      between = list(x = 0.5, y = 0.5), lwd = 0,
      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
        )
      }
    )
  })

cowplot::plot_grid(plotlist = plots_ccm, ncol = 6)

null device 
          1 

9 Growth advantage of Hydrogenase mutants

9.1 Determine mutant growth rate from competition experiments

The main measurement that was obtained from transposon library competition experiments is the fold change, and the fitness score as a summary statistic thereof. The fold change of a mutant sub-population over time can also be used to calculate the relative growth advantage over other strains. For such a growth model, the only required input parameters are the FC, the average population growth rate, and the time in hours.

\[ FC = (1 - (\mu_{pop} - \mu_{mut}))^{t} \] Re-arranged to obtain the mutant growth rate.

\[ \mu_{mut} = -(1 - FC^{1/t}) + \mu_{pop}\]

All input variables are available:

  • µ_pop = 0.1 h^-1, known from chemostats
  • FC for each mutant/gene from library competition experiments (‘fitness’ is the normalized log2 FC)
  • t = n_gen x (ln 2)/0.1 - number of generations (0, 8, 16) multiplied with generation time of 6.93 h

We make a generalized function that calculates growth rate (dis-) advantage.

mutant_mu <- function(fold_change, pop_mu, time, log2_fc = FALSE) {
  if (log2_fc) {fold_change = 2^fold_change}
  result = -(1 - (fold_change^(1/time))) + pop_mu
  return(result)
}

Then select the hydrogenase mutants where an enrichment was observed in heterotrophic conditions.

df_increased_mu <- df_fitness_summary %>%
  filter(
    grepl("hox[KGZMLOQRTVABCJNFUYHWI]|hyp[BCDEFX]1? |hypA PHG012", gene_name), condition == "continuous",
    substrate %in% c("formate", "fructose", "succinate")) %>%
  filter(locus_tag != "PHG063") %>%
  mutate(gene_name = stri_extract_first_regex(gene_name, "hox[KGZMLOQRTVABCJNFUYHWI]|hyp[ABCDEFX]")) %>%
  mutate(gene_name = fct_inorder(gene_name))

plot_increased_mu <- xyplot(norm_gene_fitness_median ~ time | gene_name, df_increased_mu,
    groups = substrate, as.table = TRUE, layout = c(6, 5),
    col = stdcol[2:4], xlab = "generations", ylab = "fitness",
    par.settings = custom.colorblind(), type = c("l"),
    auto.key = list(columns = 3),
    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.abline(h = 0, col = grey(0.5), lty = 3, lwd = 2)
      panel.xyplot(x, y, ...)
    }
  )

df_mutant_mu <- df_increased_mu %>%
  filter(time != 0) %>%
  group_by(gene_name, substrate) %>%
  mutate(mutant_mu = mutant_mu(
    fold_change = norm_gene_fitness_median, 
    pop_mu = 0.1, time = time*(log(2)/0.1),
    log2_fc = TRUE)) %>%
  select(locus_tag, time, condition, substrate,
    norm_gene_fitness_median, gene_name, cond_short, mutant_mu)

print(plot_increased_mu)

9.2 Growth advantage versus protein cost

The main question is if the observed growth advantage can be purely explained by the saving of gene expression cost. Hydrogenase and some accessory proteins are highly abundant and therefore cause a significant cost to the cell. It was shown in many other studies that reducing excess/unused proteins can increase growth rate. This is based on the observation that the protein pool is limited and cells can optimize the utilization of this limited resource by tuning gene expression towards highly utilized genes/proteins.

In the next section, global protein abundance data is imported that was obtained from quantitative MS experiments. MS data is available for the three conditions used in the previous section, continuous growth on fructose, succinate, and formate in chemostats at defined growth rates.

Note: The data is imported directly from the respective github repository, requiring internet connection.

load(url("https://github.com/m-jahn/R-notebook-ralstonia-proteome/blob/main/data/input/Ralstonia_eutropha.Rdata?raw=true"))
head(Ralstonia_eutropha[1:10])

To assess real protein cost of a knock-out, it is important to consider that KO by transposon integration will have an effect on the downstream gene expression too, effectively disrupting transcription/translation of the entire operon downstream of the integration site.

If we assume that each KO knocks out the gene expression of all consecutive genes of the operon, we need to sum up protein cost for these affected gene sets; we generate a binary matrix of associations for each KO with a number of affected genes (0 = FALSE, 1 = TRUE), and multiply with protein mass. Here, we need to test different assumptions. The canonical regulation of the hox/hyp operon happens via hoxA transcriptional regulator, hoxBC H2 sensing ‘regulatory hydrogenase’ (RH), and hoxJ, the defect kinase that transmits the output from hoxBC to hoxA?. This regulation controls two promoters:

  • the P_MBH promoter that starts transcription of the huge hoxKGZMLOQRTV + hypABFCDEX + hoxABCJ operon
  • the P_SH promoter that starts transcription of the smaller hoxFUYHWI + hypA2B2F2 operon

Accordingly, hoxA controls its own transcription in a feed-forward autoregulatory fashion. Increased hoxA protein abundance leads to stronger hoxA expression, etc. However, if a transposon integrates into any given site of one of the operons, transcription of following genes should be terminated or otherwise negatively affected. Surprisingly, this effect is visible for hyp mutants that might negatively affect hoxA expression, but not for any preceding genes (hoxKHZMLOQRTV). This suggests that:

  • there is another, secondary promoter that controls hyp + hoxABCJN gene expression
  • in fact this has been shown in …
gene_list_mh <- c("hoxK", "hoxG", "hoxZ", "hoxM", "hoxL", "hoxO", "hoxQ", "hoxR", "hoxT", "hoxV")
gene_list_sh <- c("hoxA", "hoxB", "hoxC", "hoxJ", "hoxN", "hoxF", "hoxU", "hoxY", "hoxH", "hoxW", "hoxI")
gene_list_hyp <- c("hypA", "hypB", "hypF", "hypC", "hypD", "hypE", "hypX")

list_mutant_genomic <- list(
  hoxK = gene_list_mh,
  hoxG = gene_list_mh[2:10],
  hoxZ = gene_list_mh[3:10],
  hoxM = gene_list_mh[4:10],
  hoxL = gene_list_mh[5:10],
  hoxO = gene_list_mh[6:10],
  hoxQ = gene_list_mh[7:10],
  hoxR = gene_list_mh[8:10],
  hoxT = gene_list_mh[9:10],
  hoxV = gene_list_mh[10],
  hypA = c(gene_list_mh, gene_list_hyp, gene_list_sh),
  hypB = c(gene_list_mh, gene_list_hyp[2:7], gene_list_sh),
  hypF = c(gene_list_mh, gene_list_hyp[3:7], gene_list_sh),
  hypC = c(gene_list_mh, gene_list_hyp[4:7], gene_list_sh),
  hypD = c(gene_list_mh, gene_list_hyp[5:7], gene_list_sh),
  hypE = c(gene_list_mh, gene_list_hyp[6:7], gene_list_sh),
  hypX = c(gene_list_mh, gene_list_hyp[7], gene_list_sh),
  hoxA = c(gene_list_mh, gene_list_hyp, gene_list_sh),
  hoxB = gene_list_sh[2:5],
  hoxC = gene_list_sh[3:5],
  hoxJ = gene_list_sh[4:5],
  hoxN = gene_list_sh[5],
  hoxF = gene_list_sh[c(6:11)],
  hoxU = gene_list_sh[c(7:11)],
  hoxY = gene_list_sh[c(8:11)],
  hoxH = gene_list_sh[c(9:11)],
  hoxW = gene_list_sh[c(10:11)],
  hoxI = gene_list_sh[c(11)]
)

mat_mutant_genomic <- lapply(list_mutant_genomic, function(x) {
    as.numeric(names(list_mutant_genomic) %in% x)
  }) %>% unlist %>% matrix(ncol = length(list_mutant_genomic), byrow = TRUE) %>% t
rownames(mat_mutant_genomic) <- names(list_mutant_genomic)
colnames(mat_mutant_genomic) <- names(list_mutant_genomic)
  • Add individual and accumulated protein cost for each KO mutant.
# add individual protein cost from MS data
df_mutant_cost <- df_mutant_mu %>%
  group_by(locus_tag, gene_name, substrate) %>%
  summarize(.groups = "drop",
    mean_mutant_mu = mean(mutant_mu),
    sd_mutant_mu = sd(mutant_mu)) %>%
  left_join(by = c("locus_tag", "substrate"),
    Ralstonia_eutropha %>% ungroup %>%
      filter(growthrate == 0.1) %>%
      select(locus_tag, substrate, mean_mass_fraction_norm, sd_massfraction)) %>%
  arrange(locus_tag) %>%
  tidyr::complete(substrate, nesting(locus_tag, gene_name)) %>%
  
  # add estimated total cost for each knockout
  group_by(substrate) %>%
  mutate(
    corrected_cost = mean_mass_fraction_norm %>% {colSums(na.rm = TRUE, .*100*mat_mutant_genomic)},
    corrected_sd = sd_massfraction %>% {colSums(na.rm = TRUE, .*100*mat_mutant_genomic)}
  )
  • Plot mutant growth rate, individual cost, and total protein cost that is saved in KO mutants.
plot_mutant_mu <- xyplot(mutant_mu ~ gene_name, df_mutant_mu,
    groups = substrate, as.table = TRUE,
    col = stdcol[2:4], xlab = "", ylab = expression("estimated µ [h"^-1*"]"),
    par.settings = custom.colorblind(), lwd = 2,
    scales = list(x = list(rot = 90)),
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.abline(h = 0.1, col = grey(0.5), lty = 3, lwd = 2)
      panel.errbars(x, y, beside = TRUE, ...)
    }
  )

plot_mutant_cost <- xyplot(mean_mass_fraction_norm*100 ~ gene_name, df_mutant_cost,
    groups = substrate, par.settings = custom.colorblind(),
    error_margin = df_mutant_cost[["sd_massfraction"]]*100,
    lwd = 2, col = stdcol[2:4], ylim = c(-0.2, 6.5), pch = 1,
    xlab = "", ylab = "reduction protein cost [%]",
    scales = list(x = list(rot = 90)),
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.errbars(x, y, beside = TRUE, ...)
      panel.key(corner = c(0.98, 0.95), ...)
      panel.key(corner = c(0.98, 0.60),
        labels = c("individual cost", "total cost (est.)"),
        col = grey(0.5), pch = c(1, 19))
    }
  ) + as.layer(
    xyplot(corrected_cost ~ gene_name, df_mutant_cost,
      groups = substrate,
      error_margin = df_mutant_cost[["corrected_sd"]],
      lwd = 2, col = stdcol[2:4],
      panel = function(x, y, ...) {
        panel.errbars(x, y, beside = TRUE, ...)
      }
    )
  )

plot_mbh_operon <- df_increased_mu %>%
  filter(time == 8) %>%
  group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop") %>%
  genome_plot(xlim = c(0.015, 22.490), title = "pHG1", rot_labels = 45)

plot_sh_operon <- df_increased_mu %>%
  filter(time == 8) %>%
  group_by(locus_tag, gene_name, scaffold, strand, start, end) %>%
  summarize(strains_per_gene = min(unique(strains_per_gene)), .groups = "drop") %>%
  genome_plot(xlim = c(79.612, 85.438), title = "pHG1", rot_labels = 45)

print(plot_mutant_mu, position = c(-0.017, 0.60, 1, 1), more = TRUE)
print(plot_mutant_cost, position = c(0.02, 0.30, 1, 0.70), more = TRUE)
print(plot_mbh_operon, position = c(0.075, 0.14, 0.97, 0.39), more = TRUE)
print(plot_sh_operon, position = c(0.075, 0, 0.4, 0.25))
grid::grid.text(c("A","B","C"), x = c(0.02, 0.02, 0.02),
  y = c(0.98, 0.66, 0.34), gp = grid::gpar(cex = 1.2))

  • plot correlation of estimated protein cost and growth rate, per mutant
  • correlation is visible when plotting both parameters separately, but will it also hold in direct comparison?
plot_cost_vs_mu <- xyplot(mean_mutant_mu ~ corrected_cost | substrate,
    df_mutant_cost, groups = substrate,
    pointlabels = as.character(df_mutant_cost$gene_name),
    par.settings = custom.colorblind(), col = stdcol[2:4],
    xlab = "reduction protein cost [%]",
    ylab = expression("estimated µ [h"^-1*"]"),
    aspect = 1, scales = list(alternating = FALSE),
    between = list(x = 0.5, y = 0.5),
    panel = function(x, y, pointlabels, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.superpose(x, y, ...)},
    panel.groups = function(x, y, ...) {
      panel.xyplot(x, y, ...)
      panel.lmlineq(x, y, r.squared = TRUE, label = "", ...)
    }
  )

plot_cost_vs_mu

svg("../figures/figure_mutant_cost_vs_growth.svg", width = 6.5, height = 3.5)
print(plot_cost_vs_mu)
dev.off()
png 
  2 

9.3 Prediction of growth advantage using cell economy model

The constrained Resource Balance Analysis (RBA) model described in Jahn et al., 2021 can be used to make predictions on the effect of hydrogenase knockout on cell growth rate. The RBA model is a genome scale metabolic model of R. eutropha that takes protein costs and global protein pool limitations into account. In constrast to simple FBA (flux balance analysis), growth rate is ultimately capped by rate limitation of enzymes and space limitation for enzyme mass within the cell, or the cytoplasmic membrane. The model was constrained using experimentally determined steady-state growth rates and protein abundances.

Here, we can ask the question if, and to which degree, growth rate would increase by expanding the total protein pool size (shrinking protein cost) by the fraction indicated above (e.g. hoxA KO freeing up to 4% protein mass). The first step is to import RBA simulation results.

source(url("https://github.com/m-jahn/R-notebook-ralstonia-proteome/raw/main/pipeline/read_rba_result.R"))
dir_sim <- "../../../Models/Bacterial-RBA-models/Ralstonia-eutropha-H16/simulation/hydrogenase/"

df_flux <- read_rba_result(list.files(dir_sim, pattern = "fluxes_.*.tsv$", full.names = TRUE))
df_prot <- read_rba_result(list.files(dir_sim, pattern = "proteins_.*.tsv", full.names = TRUE))
df_macr <- read_rba_result(list.files(dir_sim, pattern = "macroprocesses_.*.tsv", full.names = TRUE))

The next step is to plot the increase in growth rate as a function of protein resources that have been saved. The prediction shows smaller increases than the growth rates estimated from the library data.

  • roughly 8% increase in µ for 10% reduction of protein cost
  • library data suggest roughly 40% increase for 4-5% reduction of protein cost
  • this is not realistic, most likely an artifact of calculation from NGS data
plot_cost_predict <- df_macr %>%
  mutate(carbon_source = recode(carbon_source,
    `for` = "formate", `succ` = "succinate", `fru` = "fructose")) %>%
  mutate(sim_run = as.numeric(sim_run)) %>%
  arrange(carbon_source, sim_run) %>%
  group_by(carbon_source, key) %>%
  mutate(reduced_protein_cost = 0:10) %>%
  filter(key == "mu") %>%
  
  xyplot(value ~ reduced_protein_cost, .,
    groups = carbon_source, aspect = 1,
    par.settings = custom.colorblind(),
    type = "b", lwd = 2, lty = 2, pch = 1,
    ylim = c(0.095, 0.125), col = stdcol[2:4],
    xlab = "freed up protein resources [% total]",
    ylab = expression("µ [h"^-1*"]"),
    panel = function(x, y, z, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.xyplot(x, y, ...)
      panel.key(..., cex = 0.7, corner = c(0.96, 0.96))
      panel.key(c("data", "model"), lines = TRUE, lty = c(1,2),
        pch = c(19, 1), col = grey(0.4),
        cex = 0.7, corner = c(0.96, 0.72))
    }
  )

print(plot_cost_predict)

9.4 Growth advantage of clean, in-frame hydrogenase deletion mutants

  • a selection of hydrogenase mutants was grown in bioreactors, batch mode
  • total volume was 50 mL minimal medium supplemented with 2 g/L fructose + 1 g/L NH4Cl
  • aeration: 100 mL/min for all tubes
  • temperature 30*C
  • OD 720nm was constantly measured every 15 min
  • biomass yield was determined by collecting all biomass at the end of the experiment and determining DCW
df_growth_assay <- read_csv("../data/growth_assays/df_mu_hox.csv", show_col_types = FALSE)
df_dcw_assay <- read_csv("../data/growth_assays/df_dcw_hox.csv", show_col_types = FALSE)
plot_order <- c("WT", "hoxG", "hypB", "hypX", "hoxA", "hoxH")

plot_growth_dcw <- df_dcw_assay %>%
  mutate(sample = factor(sample, plot_order)) %>%
  xyplot(yield_gDCW_gFRC ~ sample, .,
    par.settings = custom.colorblind(),
    col = stdcol[3], lwd = 2, ylim = c(-0.0, 0.6),
    xlab = "", ylab = expression("yield [g DCW g Frc"^-1*"]"),
    scales = list(x = list(rot = 45)),
    panel = function(x, y, z, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.xyplot(x, y, pch = 19, cex = 0.5, col = grey(0.6))
      panel.barplot(x, y, ...)
      panel.pvalue(x, y, offset = 0.12, ...)
    }
  )

plot_growth_mu <- df_growth_assay %>%
  mutate(sample = factor(sample, plot_order)) %>%
  xyplot(mu_max ~ sample, .,
    par.settings = custom.colorblind(),
    col = stdcol[3], lwd = 2, ylim = c(-0.0, 0.39),
    xlab = "", ylab = expression("µ [h"^-1*"]"),
    scales = list(x = list(rot = 45)),
    panel = function(x, y, z, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.xyplot(x, y, pch = 19, cex = 0.5, col = grey(0.6))
      panel.barplot(x, y, ...)
      panel.pvalue(x, y, offset = 0.08, ...)
    }
  )

print(plot_growth_dcw, position = c(0, 0, 0.5, 1), more = TRUE)
print(plot_growth_mu, position = c(0.5, 0, 1, 1), more = TRUE)
grid::grid.text(c("A","B"), x = c(0.03, 0.5), y = c(0.94, 0.94), gp = grid::gpar(cex = 1.2))

9.5 Hydrogen evolution of WT and hydrogenase mutant

  • an alternative hypothesis to protein cost by hydrogenases is energy cost
  • this hypothesis basically states that the enzymatic action of hydrogenases “running reverse” catalyzes the formation of H2 from NADH and protons (as known from cyanobacteria)
  • this reaction would drain the reductant pool (NADH(P)H) and lead to reduced growth
  • this reaction plays a role when the terminal electron acceptor, oxygen or one of the nitrogen oxides, are limiting
  • in order to determine if C. necator spills reductant in heterotrophic conditions through the action of hydrogenases, WT and a mutant defective in all hydrogenases were cultivated with fructose and glycerol to induce hydrogenase expression
  • without strong limitation of oxygen (which was not present in our bioreactor experiments), no hydrogen evolution was detectable
  • this result strengthens the protein cost hypothesis
df_h2_evolution <- read_csv("../data/growth_assays/h2_evolution.csv", show_col_types = FALSE) %>%
  mutate(time = ifelse(time == 20.5, 12.5, time))

plot_h2_evolution <- xyplot(mean ~ time,
    filter(df_h2_evolution, gas == "O2"),
    groups = strain,
    par.settings = custom.colorblind(),
    error_margin = filter(df_h2_evolution, gas == "O2")$sd,
    lwd = 2, pch = 1,
    xlab = "time [h]", ylab = expression("O"[2]*" [%]"),
    ylim = c(-2, 22),
    scales = list(x = list(at = seq(0, 10, 2))),
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = 0, col = grey(0.9))
      panel.abline(v = seq(0, 10, 2), col = grey(0.9))
      panel.xyplot(x, y, type = "l", lty = 2, ...)
      panel.errbars(x, y, ewidth = 0, lty = 1, ...)
      panel.key(
        corner = c(0.9, 0.9),
        labels = c("∆hoxGHC,∆hofG", "WT", "O2", "H2"),
        col = c("#E7298A", "#66A61E", grey(0.5), grey(0.5)),
        pch = c(19, 19, 19, 1)
      )
    }
  ) + as.layer(
    xyplot(mean/100 ~ time,
      filter(df_h2_evolution, gas == "H2"),
      groups = strain, pch = 19, lty = 1,
      error_margin = filter(df_h2_evolution, gas == "H2")$sd/100,
      panel = function(x, y, ...) {
        panel.xyplot(x, y, type = "l", ...)
        panel.errbars(x, y, ewidth = 0, ...)
      }
    )
  )

print(plot_h2_evolution)

svg("../figures/figure_mutant_mu.svg", width = 7.5, height = 2.85)
print(plot_growth_dcw, position = c(-0.02, -0.034, 0.38, 1), more = TRUE)
print(plot_growth_mu, position = c(0.29, -0.034, 0.69, 1), more = TRUE)
print(plot_h2_evolution, position = c(0.61, 0, 1, 1), more = TRUE)
grid::grid.text(c("D","E", "F"), x = c(0.03, 0.35, 0.65),
  y = c(0.94, 0.94, 0.94), gp = grid::gpar(cex = 1.2))
dev.off()
null device 
          1 

9.6 Protein cost of secondary hydrogenases

This figure shows protein cost of the hox/hof genes that were not included in the protein cost analysis:

  • the 4th ‘actinobacterial’ hydrogenase, 1 subunit (hofG, PHG065)
Ralstonia_eutropha %>% ungroup %>%
  filter(grepl("PHG065", protein), substrate != "ammonium") %>%
  
  xyplot(mean_mass_fraction_norm*100 ~ factor(growthrate) | protein, .,
    groups = substrate, par.settings = custom.colorblind(),
    error_margin = .[["sd_massfraction"]]*100,
    lwd = 2, col = stdcol[2:4], ylim = c(-0.0005, 0.0045), pch = 19,
    xlab = "", ylab = "protein mass fraction [%]",
    panel = function(x, y, ...) {
      panel.grid(h = -1, v = -1, col = grey(0.9))
      panel.errbars(x, y, beside = TRUE, ...)
      panel.key(corner = c(0.02, 0.95), ...)
    }
  )

9.7 Protein cost of ETC complexes

  • as a complimentary information, we can determine the mass fraction of ETC complexes using MS data
  • these are membrane proteins and hence likely underestimated in comparison to soluble/cytoplasmic proteins
  • the reason is the lower efficiency when extracting membrane proteins
  • a relative comparison is nevertheless possible
  • should allow us to see the rough order of magnitude of ETC complexes
plots_etc_mass <- Ralstonia_eutropha %>%
  ungroup %>%
  select(locus_tag, mean_mass_fraction_norm, growthrate, protein, substrate, sd_massfraction) %>%
  inner_join(
    select(df_etc, locus_tag, gene_name) %>%
      distinct %>%
      mutate(gene_name = as.character(gene_name)),
    by = join_by(locus_tag)) %>%
  filter(substrate != "ammonium", growthrate == 0.1, !is.na(gene_name), !str_detect(gene_name, "H16_")) %>%
  mutate(operon = str_sub(gene_name, 1, 3)) %>%
  group_by(operon) %>%
  group_split() %>%
  lapply(function(df) {
    xyplot(mean_mass_fraction_norm*100 ~ factor(gene_name) | operon, df,
      groups = substrate, par.settings = custom.colorblind(),
      error_margin = df$sd_massfraction * 100,
      lwd = 2, col = stdcol[2:4], ylim = c(-0.005, 0.085), pch = 19,
      between = list(x = 0.5, y = 0.5), as.table = TRUE,
      scales = list(alternating = FALSE, x = list(rot = 90)),
      xlab = "", ylab = "protein mass fraction [%]",
      panel = function(x, y, ...) {
        panel.grid(h = -1, v = -1, col = grey(0.9))
        panel.errbars(x, y, beside = TRUE, ...)
        panel.key(corner = c(0.02, 0.95), ...)
      }
    )
  })

gridExtra::grid.arrange(
  ncol = 3, nrow = 2,
  plots_etc_mass[[5]],
  plots_etc_mass[[4]],
  plots_etc_mass[[6]],
  plots_etc_mass[[2]],
  plots_etc_mass[[3]],
  plots_etc_mass[[1]]
)

null device 
          1 

10 Export processed data

Cupriavidus_BarSeq_2023 <- df_fitness %>%
  rename(timepoint = time, fitness_score = norm_gene_fitness, t_stat = t) %>%
  mutate(
    induction = "not applicable",
    log2FC = replace(log2FC, is.infinite(log2FC), NA),
    fold_change = 2^log2FC,
    condition = paste0(substrate, " - ", condition)
  ) %>%
  select(-ID) %>%
  arrange(locus_tag, condition, timepoint) %>%
  left_join(by = "locus_tag",
    df_ref %>%
      select(
        locus_tag, new_locus_tag,
        uniprot, protein_name, gene_name,
        System, Process, Pathway
      )
  )

# export to RData
save(Cupriavidus_BarSeq_2023, file = "../data/barseq/Cupriavidus_BarSeq_2023.Rdata")

11 Session Info

sessionInfo()
R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 22.04.4 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.10.0 
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=de_DE.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=de_DE.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=de_DE.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Berlin
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] limma_3.60.3           directlabels_2024.1.21 tsne_0.1-3.1           vegan_2.6-6.1         
 [5] permute_0.9-7          stringi_1.8.4          colorspace_2.1-0       dendextend_1.17.1     
 [9] lubridate_1.9.3        forcats_1.0.0          stringr_1.5.1          dplyr_1.1.4           
[13] purrr_1.0.2            readr_2.1.5            tidyr_1.3.1            tibble_3.2.1          
[17] ggplot2_3.5.1          tidyverse_2.0.0        latticetools_0.1.0     latticeExtra_0.6-30   
[21] lattice_0.22-6        

loaded via a namespace (and not attached):
 [1] gtable_0.3.5       xfun_0.45          tzdb_0.4.0         quadprog_1.5-8     vctrs_0.6.5       
 [6] tools_4.4.1        generics_0.1.3     parallel_4.4.1     fansi_1.0.6        cluster_2.1.6     
[11] pkgconfig_2.0.3    Matrix_1.6-5       RColorBrewer_1.1-3 lifecycle_1.0.4    farver_2.1.2      
[16] compiler_4.4.1     deldir_2.0-4       statmod_1.5.0      munsell_0.5.1      nlstools_2.1-0    
[21] pillar_1.9.0       crayon_1.5.3       MASS_7.3-60        Rtools_0.0.1       viridis_0.6.5     
[26] nlme_3.1-165       gtools_3.9.5       tidyselect_1.2.1   labeling_0.4.3     splines_4.4.1     
[31] cowplot_1.1.3      grid_4.4.1         cli_3.6.3          magrittr_2.0.3     utf8_1.2.4        
[36] withr_3.0.0        scales_1.3.0       bit64_4.0.5        timechange_0.3.0   jpeg_0.1-10       
[41] bit_4.0.5          interp_1.1-6       gridExtra_2.3      png_0.1-8          hms_1.1.3         
[46] knitr_1.47         viridisLite_0.4.2  mgcv_1.9-1         rlang_1.1.4        Rcpp_1.0.12       
[51] glue_1.7.0         rstudioapi_0.16.0  vroom_1.6.5        R6_2.5.1          
LS0tCnRpdGxlOiBJbnZlc3RpZ2F0aW5nIGVuZXJneSBtZXRhYm9saXNtIGluICpDLiBuZWNhdG9yKiB1c2luZyBhIGJhcmNvZGVkIHRyYW5zcG9zb24KICBsaWJyYXJ5CmF1dGhvcjogIk1pY2hhZWwgSmFobiIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKLS0tCgojIERlc2NyaXB0aW9uCgpUaGlzIFIgbm90ZWJvb2sgaXMgYSBiaW9pbmZvcm1hdGljcyBwaXBlbGluZSB0byBhbmFseXplIGZpdG5lc3MgZGF0YSBvYnRhaW5lZCBmcm9tIGEgYmFyY29kZWQgdHJhbnNwb3NvbiBsaWJyYXJ5IGluICpSYWxzdG9uaWEgZXV0cm9waGEqIGEuay5hLiAqQ3VwcmlhdmlkdXMgbmVjYXRvciouIEZvciBiYWNrZ3JvdW5kIGFuZCBkZXRhaWxzIHJlZ2FyZGluZyB0aGUgbWV0aG9kLCBzZWUgW1dldG1vcmUgYXQgYWwuLCBtQmlvLCAyMDE1XShodHRwczovL21iaW8uYXNtLm9yZy9jb250ZW50LzYvMy9lMDAzMDYtMTUpIGFuZCBbUHJpY2UgZXQgYWwuLCBOYXR1cmUsIDIwMThdKGh0dHA6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1ODYtMDE4LTAxMjQtMCkpLgoKT25lIHBhcnQgb2YgdGhlIGRhdGEgdXNlZCBoZXJlIChncm93dGggb24gKipmcnVjdG9zZSoqLCAqKnN1Y2NpbmF0ZSoqLCAqKmZvcm1hdGUqKikgd2FzIGFscmVhZHkgcHVibGlzaGVkIGluIGEgcHJldmlvdXMgcmVzZWFyY2ggYXJ0aWNsZSB3aXRoIGZvY3VzIG9uICpDLiBuZWNhdG9yKidzIGNlbnRyYWwgY2FyYm9uIG1ldGFib2xpc20uIFRoZSBbcHJlcHJpbnQgaXMgYXZhaWxhYmxlIG9uIEJpb1J4aXZdKGh0dHBzOi8vd3d3LmJpb3J4aXYub3JnL2NvbnRlbnQvMTAuMTEwMS8yMDIxLjAzLjIxLjQzNjMwNHYyKS4gVGhlIHNlY29uZCBwYXJ0IChhdXRvdHJvcGhpYyBncm93dGggb24gKipoeWRyb2dlbioqLCBhbm94aWMgZ3Jvd3RoIHdpdGggKipuaXRyYXRlIHJlc3BpcmF0aW9uKiopIGlzIG5ldyBkYXRhIHRoYXQgY29tcGxlbWVudHMgcHJldmlvdXMgcmVzdWx0cy4gVGhlIGZvY3VzIG9mIHRoaXMgcGlwZWxpbmUgaXMgdG8gc3R1ZHkgdGhlIGVuZXJneSBtZXRhYm9saXNtIG9mICpDLiBuZWNhdG9yKiBpbiBkaXZlcnNlIGNvbmRpdGlvbnMsIGVhY2ggb2YgaXQgY2hhbGxlbmdpbmcgYSBkaWZmZXJlbnQgYXNwZWN0L21hY2hpbmVyeSBvZiBtZXRhYm9saXNtLiBUaGlzIGludmVzdGlnYXRpb24gdHJpZXMgdG8gYW5zd2VyIHRoZSBmb2xsb3dpbmcga2V5IHF1ZXN0aW9uczoKCjEuICBXaGljaCBnZW5lcyBhcmUgY29uc3RpdHV0aW5nIHRoZSBlc3NlbnRpYWwgcGFydHMgb2YgdGhlICoqaHlkcm9nZW5hc2VzIGFuZCBmb3JtYXRlIGRlaHlkcm9nZW5hc2VzKiosIGVuYWJsaW5nIGF1dG90cm9waGljIGxpZmVzdHlsZT8gTWFueSB0aGluZ3MgYWJvdXQgdGhlIHN0cnVjdHVyZSBhbmQgZnVuY3Rpb24gb2YgdGhlc2UgZW56eW1lcyBpcyBhbHJlYWR5IGtub3duLCB3aGVyZSBkbyB0aGVzZSByZXN1bHRzIHNob3cgZGlmZmVyZW5jZXM/CjIuICBXaGljaCBnZW5lcyBhcmUgKiptYW5kYXRvcnkgYXMgYWNjZXNzb3J5IHByb3RlaW5zKiosIGZvciBleGFtcGxlIGNvZmFjdG9yIHN5bnRoZXNpcywgaW5zZXJ0aW9uLCBhbmQgZW56eW1lIG1hdHVyYXRpb24/CjMuICBXaHkgZG9lcyAqKktPIG9mIGh5ZHJvZ2VuYXNlcyBwcm92aWRlcyBhIGdyb3d0aCBiZW5lZml0KiogaW4gbm9uLWF1dG90cm9waGljIGNvbmRpdGlvbnM/CjQuICBXaGF0IGFyZSB0aGUgZXNzZW50aWFsIHBhcnRzIG9mIHRoZSAqKmVsZWN0cm9uIHRyYW5zcG9ydCBjaGFpbioqPyBXaGF0IGNvbXBsZXhlcyBhcmUgdXNlZCBzcGVjaWZpY2FsbHkgZm9yIHNvbWUgZ3Jvd3RoIGNvbmRpdGlvbnM/CjUuICBJcyBlc3NlbnRpYWxpdHkvZml0bmVzcyBjb3JyZWxhdGVkIHdpdGggcHJvdGVpbiBhYnVuZGFuY2U/IElzIHRoZXJlIGEgZG9zYWdlIGVmZmVjdCwgc3VjaCB0aGF0IEtPIG9mIGEgaGlnaCBhYnVuZGFudCBpc28tZW56eW1lIGhhcyBzdHJvbmdlciBmaXRuZXNzIGVmZmVjdCBjb21wYXJlZCB0byBhIGxvdy1hYnVuZGFudD8KCiMgTGlicmFyaWVzCgpPcHRpb25hbGx5IGluc3RhbGwgcmVxdWlyZWQgbGlicmFyaWVzLCBwYXJ0aWN1bGFybHkgY3VzdG9tIHBhY2thZ2VzIGZyb20gZ2l0aHViLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJtLWphaG4vbGF0dGljZS10b29scyIpCmBgYAoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShsYXR0aWNlKQogIGxpYnJhcnkobGF0dGljZUV4dHJhKQogIGxpYnJhcnkobGF0dGljZXRvb2xzKQogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkoZm9yY2F0cykKICBsaWJyYXJ5KGRlbmRleHRlbmQpCiAgbGlicmFyeShjb2xvcnNwYWNlKQogIGxpYnJhcnkoc3RyaW5naSkKICBsaWJyYXJ5KHZlZ2FuKQogIGxpYnJhcnkodHNuZSkKICBsaWJyYXJ5KGRpcmVjdGxhYmVscykKICBsaWJyYXJ5KGxpbW1hKQp9KQpgYGAKCiMgRGF0YSBpbXBvcnQgYW5kIHByb2Nlc3NpbmcKCkltcG9ydCB0aGUgbWFpbiBkYXRhIHRhYmxlcyB3aXRoIGdlbmUgZml0bmVzcyBkYXRhIGZvciBhbGwgY29uZGl0aW9ucy4gVGFibGVzIHdlcmUgb2J0YWluZWQgYnkgcHJvY2Vzc2luZyBzZXF1ZW5jaW5nIGRhdGEgd2l0aCBhIGN1c3RvbSBbQmFyU2VxIHBpcGVsaW5lXShodHRwczovL2dpdGh1Yi5jb20vbS1qYWhuL3JlYmFyKS4gVGhlIDMyIGdlbmVyYXRpb24gc2VxdWVuY2luZyBzYW1wbGVzIGFyZSByZW1vdmVkIGR1ZSB0byBsb3cgZGl2ZXJzaXR5LCBsZWFkaW5nIHRvIG92ZXJhbGwgbG93ZXIgYXZlcmFnZSByZWFkIGNvdW50IHBlciBnZW5lLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KbG9hZCgiLi4vZGF0YS9iYXJzZXEvMjAyMDEyMjJfZnJ1X2ZpdG5lc3MuUmRhdGEiKQpkZl9maXRuZXNzX2ZyYyA8LSBmaXRuZXNzX2dlbmUgJT4lIHVuZ3JvdXAgJT4lCiAgZmlsdGVyKGNvbmRpdGlvbiAhPSAibG9uZyBwdWxzZSIsIHRpbWUgIT0gMzIpICU+JQogIG11dGF0ZShJRCA9IGFzLm51bWVyaWMoSUQpLCBzdWJzdHJhdGUgPSAiZnJ1Y3Rvc2UiLAogICAgY29uZGl0aW9uID0gc3RyX3JlbW92ZShjb25kaXRpb24sICJzaG9ydCAiKSkKCmxvYWQoIi4uL2RhdGEvYmFyc2VxLzIwMjEwNDA3X3N1Y19mb3JfZml0bmVzcy5SZGF0YSIpCmRmX2ZpdG5lc3Nfc3VjIDwtIGZpdG5lc3NfZ2VuZSAlPiUgdW5ncm91cCAlPiUKICBzZXBhcmF0ZShjb25kaXRpb24sIHNlcCA9ICJfIiwgaW50byA9IGMoInN1YnN0cmF0ZSIsICJjb25kaXRpb24iKSkKCmxvYWQoIi4uL2RhdGEvYmFyc2VxLzIwMjEwNjI0X0gyX05PM19maXRuZXNzLlJkYXRhIikKZGZfZml0bmVzc19ubzMgPC0gZml0bmVzc19nZW5lICU+JSB1bmdyb3VwICU+JQogIGZpbHRlcihjb25kaXRpb24gJWluJSBjKCJOTzMiKSkgJT4lCiAgbXV0YXRlKHN1YnN0cmF0ZSA9ICJuaXRyYXRlIiwgY29uZGl0aW9uID0gImNvbnRpbnVvdXMiKQoKbG9hZCgiLi4vZGF0YS9iYXJzZXEvMjAyMTA5MjhfSDJfZml0bmVzcy5SZGF0YSIpCmRmX2ZpdG5lc3NfaDIgPC0gZml0bmVzc19nZW5lICU+JSB1bmdyb3VwICU+JQogIG11dGF0ZShzdWJzdHJhdGUgPSAiaHlkcm9nZW4iLCBjb25kaXRpb24gPSAicHVsc2UiKQoKbG9hZCgiLi4vZGF0YS9iYXJzZXEvMjAyMjA0MjBfbG93Q08yX05PMy5SZGF0YSIpCmRmX2ZpdG5lc3NfbG93Y28yIDwtIGZpdG5lc3NfZ2VuZSAlPiUgdW5ncm91cCAlPiUKICBmaWx0ZXIoY29uZGl0aW9uID09ICJOTzNfYmF0Y2giKSAlPiUKICBtdXRhdGUoc3Vic3RyYXRlID0gIm5pdHJhdGUiLCBjb25kaXRpb24gPSAicHVsc2UiKQoKIyBtZXJnZSBmaXRuZXNzIHRhYmxlcwpkZl9maXRuZXNzIDwtIGJpbmRfcm93cyhkZl9maXRuZXNzX2ZyYywgZGZfZml0bmVzc19zdWMsIGRmX2ZpdG5lc3Nfbm8zLAogIGRmX2ZpdG5lc3NfaDIsIGRmX2ZpdG5lc3NfbG93Y28yKSAlPiUKICByZW5hbWUobG9jdXNfdGFnID0gbG9jdXNJZCkKcm0oImRmX2ZpdG5lc3NfZnJjIiwgImRmX2ZpdG5lc3Nfc3VjIiwgImRmX2ZpdG5lc3NfaDIiLCAiZGZfZml0bmVzc19ubzMiLAogICAiZGZfZml0bmVzc19sb3djbzIiLCAiZml0bmVzc19nZW5lIikKCiMgaW1wb3J0IGdlbm9tZSBhbm5vdGF0aW9uCmRmX3JlZiA8LSByZWFkX2NzdigiLi4vZGF0YS9yZWYvUmFsc3RvbmlhX0gxNl9nZW5vbWVfYW5ub3RhdGlvbi5jc3YiLAogIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lCiAgZmlsdGVyKCFkdXBsaWNhdGVkKGxvY3VzX3RhZykpICU+JQogIG11dGF0ZShlZ2dOT0dfbmFtZSA9IGlmX2Vsc2UoaXMubmEoZWdnTk9HX25hbWUpLCBnZW5lX25hbWUsIGVnZ05PR19uYW1lKSkKCiMgZGVmaW5lIHN0YW5kYXJkIGNvbG9ycwpzdGRjb2wgPC0gY3VzdG9tLmNvbG9yYmxpbmQoKSRzdXBlcnBvc2UubGluZSRjb2wKYGBgCgojIEdlbmUgZml0bmVzcyBhbmFseXNpcwoKIyMgRGVwbGV0aW9uIG92ZXIgdGltZSAoZ2VuZXJhdGlvbnMpCgpXZSBjYW4gcGxvdCBsb2cyIEZDIG9yIG5vcm1hbGl6ZWQgZ2VuZSBmaXRuZXNzIG92ZXIgZ2VuZXJhdGlvbnMuIEZvciB0aGlzIHR5cGUgb2Ygb3ZlcnZpZXcgaXQgaXMgYmVzdCB0byBzdW1tYXJpemUgaW5kaXZpZHVhbCByZXBsaWNhdGVzICg0eCkgdG8gdGhlIG1lYW4gb3IgbWVkaWFuLCBwZXIgdGltZSBwb2ludCBhbmQgY29uZGl0aW9uLiBXZSBhbHNvIGFkZCBnZW5vbWUgYW5ub3RhdGlvbiB0byB0aGUgc3VtbWFyeSB0YWJsZS4gVGhlIHBsb3RzIHNob3dzIHRoYXQgZGVwbGV0aW9uIG9mIHNvbWUgc3RyYWlucyBpcyBzbyBzdHJvbmcgYWxyZWFkeSBhdCA4IGdlbmVyYXRpb25zIHRoYXQgZml0bmVzcy9sb2cyIEZDIGNvdWxkIG5vdCBiZSBxdWFudGlmaWVkIGZvciAxNiBnZW5lcmF0aW9ucyBkdWUgdG8gbWlzc2luZyByZWFkIGNvdW50cy4gSXQgaXMgYmVzdCB0byBmb2N1cyBvbiA4IGdlbmVyYXRpb25zIGFzIGJlY2F1c2UgaXQgcHJvdmlkZXMgYSBtb3JlIGNvbXBsZXRlIHBpY3R1cmUuCgpgYGB7ciwgbWVzc2FnZXMgPSBGQUxTRX0KbmVzdF9jb2xzID0gYygidGltZSIsICJjb25kaXRpb24iLCAic3Vic3RyYXRlIikKZGZfcmVmX2xvbmcgPC0gYmluZF9yb3dzKGRmX3JlZiwgZGlzdGluY3QoZGZfZml0bmVzcyAlPiUgc2VsZWN0KCEhIW5lc3RfY29scykpKSAlPiUKICBjb21wbGV0ZShsb2N1c190YWcsIG5lc3RpbmcoISEhbGFwcGx5KG5lc3RfY29scywgc3ltKSkpICU+JQogIGZpbHRlcighaXMubmEodGltZSkpICU+JSBzZWxlY3QobG9jdXNfdGFnLCAhISFuZXN0X2NvbHMpICU+JQogIGxlZnRfam9pbihkZl9yZWYsIGJ5ID0gImxvY3VzX3RhZyIpCgpkZl9maXRuZXNzX3N1bW1hcnkgPC0gZGZfZml0bmVzcyAlPiUKICBncm91cF9ieShsb2N1c190YWcsIHNjYWZmb2xkLCB0aW1lLCBjb25kaXRpb24sIHN1YnN0cmF0ZSwgc3RyYWluc19wZXJfZ2VuZSkgJT4lCiAgc3VtbWFyaXplKAogICAgLmdyb3VwcyA9ICJkcm9wIiwKICAgIG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiA9IG1lZGlhbihub3JtX2dlbmVfZml0bmVzcywgbmEucm0gPSBUUlVFKSwKICAgIGxvZzJGQ19tZWRpYW4gPSBtZWRpYW4obG9nMkZDLCBuYS5ybSA9IFRSVUUpLAogICAgdHN0YXRfbWVkaWFuID0gbWVkaWFuKHQsIG5hLnJtID0gVFJVRSkKICApICU+JQogIGZ1bGxfam9pbihkZl9yZWZfbG9uZykgJT4lCiAgbXV0YXRlKGNvbmRfc2hvcnQgPSBpZl9lbHNlKGNvbmRpdGlvbiA9PSAicHVsc2UiLCAiUCIsICJDIikpICU+JQogIG11dGF0ZShjb25kX3Nob3J0ID0gcGFzdGUwKHN1YnN0cmF0ZSwgIiAtICIsIGNvbmRfc2hvcnQpICU+JQogICAgZmFjdG9yKGxldmVscyA9IHBhc3RlMCgKICAgICAgcmVwKGMoImZydWN0b3NlIiwgInN1Y2NpbmF0ZSIsICJmb3JtYXRlIiwgImh5ZHJvZ2VuIiwgIm5pdHJhdGUiKSwgZWFjaCA9IDIpLAogICAgICByZXAoYygiIC0gUCIsICIgLSBDIiksIDUpCiAgICApWy04XSkKICApCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDYuNSwgZmlnLmhlaWdodCA9IDh9CnBsb3RfaGlzdF9sb2cyRkMgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgc3Vic3RyYXRlKSAlPiUKICBzbGljZSgxOjEwMDApICU+JQogIAogIHh5cGxvdChsb2cyRkNfbWVkaWFuIH4gdGltZSB8IHN1YnN0cmF0ZSAqIGNvbmRpdGlvbiwgLiwKICAgIGdyb3VwcyA9IGxvY3VzX3RhZywgYXMudGFibGUgPSBUUlVFLAogICAgY29sID0gc3RkY29sWzFdLCBhbHBoYSA9IDAuMiwgbGF5b3V0ID0gYyg1LCAyKSwKICAgIHhsYWIgPSAiIiwgeWxhYiA9IGV4cHJlc3Npb24oImxvZyJbMl0qIiBGQyIpLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgdHlwZSA9IGMoImwiKSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwgbHdkID0gMiwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIC4uLikKICAgIH0KICApCgpwbG90X2hpc3Rfbm9ybWZnIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBncm91cF9ieShjb25kaXRpb24sIHN1YnN0cmF0ZSkgJT4lCiAgc2xpY2UoMToxMDAwKSAlPiUKICAKICB4eXBsb3Qobm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuIH4gdGltZSB8IHN1YnN0cmF0ZSAqIGNvbmRpdGlvbiwgLiwKICAgIGdyb3VwcyA9IGxvY3VzX3RhZywgYXMudGFibGUgPSBUUlVFLAogICAgY29sID0gc3RkY29sWzJdLCBhbHBoYSA9IDAuMiwgbGF5b3V0ID0gYyg1LCAyKSwKICAgIHhsYWIgPSAiZ2VuZXJhdGlvbnMiLCB5bGFiID0gImZpdG5lc3MiLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgdHlwZSA9IGMoImwiKSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwgbHdkID0gMiwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIC4uLikKICAgIH0KICApCgpwcmludChwbG90X2hpc3RfbG9nMkZDLCBwb3NpdGlvbiA9IGMoMCwwLjQ3LDEsMSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2hpc3Rfbm9ybWZnLCBwb3NpdGlvbiA9IGMoMCwwLDEsMC41MykpCmBgYAoKYGBge3IsIGVjaG8gPSBGQUxTRX0KcG5nKCIuLi9maWd1cmVzL2ZpZ3VyZV9kZXBsZXRpb25fb3Zlcl90aW1lLnBuZyIsIHdpZHRoID0gOTAwLCBoZWlnaHQgPSAxMjAwLCByZXMgPSAxNTApCnByaW50KHBsb3RfaGlzdF9sb2cyRkMsIHBvc2l0aW9uID0gYygwLDAuNDcsMSwxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfaGlzdF9ub3JtZmcsIHBvc2l0aW9uID0gYygwLDAsMSwwLjUzKSkKZGV2Lm9mZigpCmBgYAoKIyMgQ29tcGFyaW5nIGdlbmUgZml0bmVzcyBiZXR3ZWVuIGNvbmRpdGlvbnMKClNpbWlsYXJseSB0byBGaWd1cmUgMiBpbiBbV2V0bW9yZSBldCBhbCwgbUJJTywgMjAxNV0oaHR0cHM6Ly9tYmlvLmFzbS5vcmcvY29udGVudC82LzMvZTAwMzA2LTE1KSwgd2UgY2FuIGludmVzdGlnYXRlIGNvbmRpdGlvbi1kZXBlbmRlbnQgZ2VuZSBmaXRuZXNzIGJ5IGNvbXBhcmluZyBjb25kaXRpb25zIGFuZCBzdWJzdHJhdGVzIG9uZS1vbi1vbmUuIFRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHN1YnN0cmF0ZXMgYW5kIGdyb3d0aCByZWdpbWVzIChjb250aW51b3VzLCBwdWxzZWQpIGlzIHF1aXRlIGRpZmZlcmVudC4gVGhlIHN0cm9uZ2VzdCBjb3JyZWxhdGlvbiBpcyBwcmVzZW50IGluIHRoZSBwdWxzZWQgdnMgY29udGludW91cyByZWdpbWUgZm9yIGVhY2ggc3Vic3RyYXRlIChSID0gMC42NCB0byAwLjg3KSwgYW5kIHdpdGhpbiB0aGUgcHVsc2VkIGNvbmRpdGlvbnMgdGhlIGNvbXBhcmlzb24gYmV0d2VlbiBzdWJzdHJhdGVzIChSID0gMC42MiB0byAwLjcyKS4KCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOCwgbWVzc2FnZSA9IEZBTFNFfQpkZl9maXRuZXNzX2NvbXAgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JSBmaWx0ZXIodGltZSA9PSA4KSAlPiUKICBncm91cF9ieShsb2N1c190YWcpICU+JSBtdXRhdGUodHN0YXRfbWVkaWFuID0gbWluKHRzdGF0X21lZGlhbikpICU+JQogIHNlbGVjdChsb2N1c190YWcsIGNvbmRfc2hvcnQsIG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiwgZ2VuZV9uYW1lLCBDT0dfUHJvY2VzcywgdHN0YXRfbWVkaWFuKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY29uZF9zaG9ydCwgdmFsdWVzX2Zyb20gPSBub3JtX2dlbmVfZml0bmVzc19tZWRpYW4pICU+JQogIGZpbHRlcighaXMubmEobG9jdXNfdGFnKSkgJT4lIHVuZ3JvdXAKCmN1c3RvbV9zcGxvbShkZl9maXRuZXNzX2NvbXAgJT4lCiAgc2VsZWN0KG1hdGNoZXMoIiAtIFtDUF0iKSksIHBjaCA9IDE5LCBjZXggPSAwLjMsIGNvbCA9IGdyZXkoMC40LCAwLjQpKQpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0V9CnBuZygiLi4vZmlndXJlcy9maWd1cmVfc3Bsb21fYWxsX2NvbmRpdGlvbnMucG5nIiwgd2lkdGggPSAxMjAwLCBoZWlnaHQgPSAxMjAwLCByZXMgPSAxNTApCmN1c3RvbV9zcGxvbShkZl9maXRuZXNzX2NvbXAgJT4lCiAgc2VsZWN0KG1hdGNoZXMoIiAtIFtDUF0iKSksIHBjaCA9IDE5LCBjZXggPSAwLjMsIGNvbCA9IGdyZXkoMC40LCAwLjQpKSAlPiUgcHJpbnQKZGV2Lm9mZigpCmBgYAoKIyBVbnN1cGVydmlzZWQgY2x1c3RlcmluZyBvZiBnZW5lcyBieSBmaXRuZXNzCgpNb3N0IGdlbmVzIGNvcnJlbGF0ZSBpbiBmaXRuZXNzIHZhbHVlIGJldHdlZW4gY29uZGl0aW9ucy4gVGhhdCBtZWFucywgb25seSBzb21lIGdlbmVzIGhhdmUgYSAqKmNvbmRpdGlvbi1zcGVjaWZpYyBmaXRuZXNzIGVmZmVjdCoqLCBpLmUuIGluY3JlYXNlIG9yIGRlY3JlYXNlIGZpdG5lc3Mgb2YgdGhlIHJlc3BlY3RpdmUgc3RyYWluIGluIG9uZSBzdWJzdHJhdGUgb3IgZ3Jvd3RoIHJlZ2ltZSBzcGVjaWZpY2FsbHkuIFRoaXMgc2VjdGlvbiBpbnZlc3RpZ2F0ZXMgc3BlY2lmaWMgZ2VuZXMgYW5kIHRoZWlyIGZ1bmN0aW9ucyB0aGF0IHNob3cgc3VjaCBkaWZmZXJlbnRpYWwgZml0bmVzcyBiZXR3ZWVuIGNvbmRpdGlvbnMuIEEgc2ltcGxlIGNvbXBhcmlzb24gb2Ygb25lIGNvbmRpdGlvbiB2ZXJzdXMgYW5vdGhlciB3aWxsIG5vdCBiZSBoZWxwZnVsIGFzIHRoZXJlIHR3byBtYW55IHBvc3NpYmxlIGNvbWJpbmF0aW9ucy4gVG8gaWRlbnRpZnkgaW50ZXJlc3Rpbmcgc2V0cyBvZiBnZW5lcywgY2x1c3RlciBhbmFseXNpcyBjYW4gYmUgcGVyZm9ybWVkLiBUaGUgZmlyc3Qgc3RlcCBpcyB0byBnZW5lcmF0ZSBhIG1hdHJpeCBmcm9tIHJlZm9ybWF0dGVkIGZpdG5lc3MgZGF0YSwgYW5kIGNsdXN0ZXIgZ2VuZXMgYnkgc2ltaWxhcml0eSB1c2luZyBhIGdlbmVyaWMgY2x1c3RlcmluZyBhbGdvcml0aG0gc3VjaCBhcyBgaGNsdXN0KG1ldGhvZCA9ICJ3YXJkLkQiKWAuCgpgYGB7cn0KIyBnZW5lcmF0ZSBjb2xvciBwYWxldHRlIGZvciBoZWF0bWFwCmhlYXRfY29scyA8LSBkaXZlcmdpbmdfaGNsKG4gPSA3LCBoID0gYygyNTUsIDEyKSwgYyA9IGMoNTAsIDgwKSwgbCA9IGMoMjAsIDk3KSwgcG93ZXIgPSBjKDEsIDEuMykpCgojIGNyZWF0ZSBhIG1hdHJpeCBmcm9tIHdpZGUgZml0bmVzcyBkYXRhIGZvciBwbG90dGluZyBoZWF0bWFwCm1hdF9oZWF0bWFwIDwtIGRmX2ZpdG5lc3NfY29tcCAlPiUgdW5ncm91cCAlPiUKICBmaWx0ZXIoaWZfYW55KC5jb2xzID0gbWF0Y2hlcygiIC0gW0NQXSIpLCB+ICFiZXR3ZWVuKC4sIC0yLCAyKSkpICU+JQogIHNlbGVjdChtYXRjaGVzKCJsb2N1c190YWd8IC0gW0NQXSIpKSAlPiUKICAjIGZpbHRlciBvdXQgTkEgcm93cywgYW5kIHJlcGxhY2UgZXh0cmVtZSB2YWx1ZXMKICBkcm9wX25hICU+JSBtdXRhdGUoYWNyb3NzKG1hdGNoZXMoIiAtIFtDUF0iKSwgCiAgICBmdW5jdGlvbih4KSB7eSA9IHJlcGxhY2UoeCwgeCA+IDYsIDYpOyByZXBsYWNlKHksIHkgPCAtNiwgLTYpfSkpICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAibG9jdXNfdGFnIikgJT4lCiAgYXMubWF0cml4CgojIGNyZWF0ZSBjbHVzdGVyIGZvciByZW9yZGVyaW5nCm1hdF9jbHVzdGVyIDwtIG1hdF9oZWF0bWFwICU+JSBkaXN0ICU+JSBoY2x1c3QobWV0aG9kID0gIndhcmQuRDIiKQptYXRfaGVhdG1hcCA8LSBtYXRfaGVhdG1hcFsKICBvcmRlci5kZW5kcm9ncmFtKGFzLmRlbmRyb2dyYW0obWF0X2NsdXN0ZXIpKSwKICBsZXZlbHMoZGZfZml0bmVzc19zdW1tYXJ5JGNvbmRfc2hvcnQpCl0KYGBgCgpUaGUgc2Vjb25kIHN0ZXAgaXMgdG8gZmluZCB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgZm9yIHN1YmRpdmlkaW5nIHRoZSBkYXRhIHNldC4gU2lsaG91ZXR0ZSBhbmFseXNpcyBjYW4gYmUgdXNlZCBmb3IgdGhpcyBwdXJwb3NlLiBIb3dldmVyLCB0aGUgcmVzdWx0IGlzIG5vdCBlbnRpcmVseSBjbGVhciwgYnV0IHN1Z2dlc3RzIGEgbnVtYmVyIG9mIDUgdG8gMTAgY2x1c3RlcnMgd291bGQgZ2l2ZSBnb29kIHNlcGFyYXRpb24uIEEgd3JhcHBlciBmb3IgbW9yZSBvciBsZXNzIGZ1bGx5IGF1dG9tYXRlZCBzaWxob3VldHRlIGFuYWx5c2lzIGlzIGF2YWlsYWJsZSBpbiBteSBgUnRvb2xzYCBwYWNrYWdlIChyYW5kb20gY29sbGVjdGlvbiBvZiBoZWxwZXIgZnVuY3Rpb25zKS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigibS1qYWhuL3ItdG9vbHMiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0Kc2lsaG91ZXR0ZV9yZXN1bHQgPC0gUnRvb2xzOjpzaWxob3VldHRlX2FuYWx5c2lzKG1hdCA9IG1hdF9oZWF0bWFwLCBuX2NsdXN0ZXJzID0gMjoyMCwgbl9yZXBlYXRzID0gMTApCnNpbGhvdWV0dGVfcmVzdWx0JHBsb3Rfc3VtbWFyeSRwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpCnNpbGhvdWV0dGVfcmVzdWx0JHBsb3Rfc3VtbWFyeQpuX2NsdXN0ZXJzID0gNwoKc3ZnKGZpbGVuYW1lID0gIi4uL2ZpZ3VyZXMvZmlndXJlX3NpbGhvdWV0dGUuc3ZnIiwgd2lkdGggPSA0LCBoZWlnaHQgPSA0KQpwcmludChzaWxob3VldHRlX3Jlc3VsdCRwbG90X3N1bW1hcnkpCmRldi5vZmYoKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDQuMCwgd2FybmluZyA9IEZBTFNFfQojIGRlZmluZSBhIGN1c3RvbSBzZXQgb2YgY29sb3JzIGZvciBjbHVzdGVycwpjdXN0b21fY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoY3VzdG9tLmNvbG9yYmxpbmQoKSRzdXBlcnBvc2UubGluZSRjb2xbYygyLDMsMSw1KV0pKG5fY2x1c3RlcnMpCgojIHBsb3QgaGVhdG1hcApwbG90X2hlYXRtYXAgPC0gbGV2ZWxwbG90KG1hdF9oZWF0bWFwWyAscmV2KGNvbG5hbWVzKG1hdF9oZWF0bWFwKSldLAogIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgY29sLnJlZ2lvbnMgPSBjb2xvclJhbXBQYWxldHRlKGhlYXRfY29scykoMTYpLAogIGF0ID0gc2VxKC02LCA2LCAxKSwgYXNwZWN0ID0gImZpbGwiLAogIHhsYWIgPSAiIiwgeWxhYiA9ICIiLCBzY2FsZXMgPSBsaXN0KHggPSBsaXN0KGRyYXcgPSBGQUxTRSkpLAogIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgeiwgLi4uKSB7CiAgICBwYW5lbC5sZXZlbHBsb3QoeCwgeSwgeiwgLi4uKQogICAgcGFuZWwuYWJsaW5lKGggPSAxOjkrMC41LCBjb2wgPSAid2hpdGUiLCBsd2QgPSAxLjUpCiAgfQopCgpwbG90X2NsdXN0ZXJfZGVuZCA8LSBtYXRfY2x1c3RlciAlPiUgYXMuZGVuZHJvZ3JhbSAlPiUKICBzZXQoImJyYW5jaGVzX2tfY29sIiwgY3VzdG9tX2NvbG9ycywgayA9IG5fY2x1c3RlcnMpICU+JQogIHNldCgiYnJhbmNoZXNfbHdkIiwgMC41KSAlPiUKICBhcy5nZ2RlbmQgJT4lCiAgZ2dwbG90KGxhYmVscyA9IEZBTFNFKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoCiAgcGxvdF9jbHVzdGVyX2RlbmQgKyAKICAgIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuMSwgMC4wNSwgLTAuMjYsIDAuMDg1KSwibnBjIikpLAogIHBsb3RfaGVhdG1hcCwKICBucm93ID0gMgopCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gMy41fQpzdmcoIi4uL2ZpZ3VyZXMvZmlndXJlX2hlYXRtYXAuc3ZnIiwgd2lkdGggPSA4LCBoZWlnaHQgPSAzLjUpCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKAogIHBsb3RfY2x1c3Rlcl9kZW5kICsgCiAgICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjEyLCAwLjA3NSwgLTAuMjksIDAuMTE3KSwibnBjIikpLAogIHBsb3RfaGVhdG1hcCwKICBucm93ID0gMgopCmRldi5vZmYoKQpgYGAKCiMjIEdlbmUgc2ltaWxhcml0eSwgYnkgY2x1c3RlcgoKVGhlIGhlYXQgbWFwIGNsdXN0ZXJzIGdlbmVzIHRvZ2V0aGVyIHRoYXQgc2hvdyBzaW1pbGFyIGV4cHJlc3Npb24gb3ZlciBkaWZmZXJlbnQgY29uZGl0aW9ucy4gQWx0ZXJuYXRpdmVseSwgd2UgY2FuIHVzZSBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gdG8gaWRlbnRpZnkgY2x1c3RlcnMgb2YgZ2VuZXMgdGhhdCBhcmUgc2ltaWxhciwgZS5nLiB0aHJvdWdoICoqUENBKiosICoqTk1EUyoqLCBvciAqKnQtU05FKiouCgpgYGB7ciwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDV9CiMgc2V0IGEgc2VlZCB0byBvYnRhaW4gc2FtZSBwYXR0ZXJuIGZvciBzdG9jaGFzdGljIG1ldGhvZHMKc2V0LnNlZWQoMTIzKQpyZWdleF9tb2EgPC0gIm1vW2FlZGJnXShbQS1aMC05XSspPyIKcmVnZXhfZm9yIDwtICJmZFtzd2hvXVtBLVowLTldKyIKcmVnZXhfaHlkIDwtICJob3hbQS1aMC05XSt8aHlwW0EtWjAtOV0rIgpyZWdleF9ldGMgPC0gIihxY3J8Y2NvfGN0YXxjb3h8Y3lvfGN5ZCkoW0EtWjAtOV0rKT8iCnJlZ2V4X25pdCA8LSAibmFbcnNwXShbQS1aMC05XSspP3xuaXIoW0EtWjAtOV0rKT98bm9yKFtBLVowLTldKyk/fG5vcyhbQS1aMC05XSspPyIKcmVnZXhfYWxsIDwtIHBhc3RlKGMocmVnZXhfbW9hLCByZWdleF9mb3IsIHJlZ2V4X2h5ZCwgcmVnZXhfZXRjLCByZWdleF9uaXQpLCBjb2xsYXBzZSA9ICJ8IikKCiMgcnVuIHQtU05FIGFuYWx5c2lzClNORSA8LSB0c25lKG1hdF9oZWF0bWFwICU+JSBkaXN0LCBtYXhfaXRlciA9IDUwMCkKZGZfdHNuZSA8LSBTTkUgJT4lIHNldE5hbWVzKGMoIngiLCAieSIpKSAlPiUgYXNfdGliYmxlICU+JQogIG11dGF0ZShsb2N1cyA9IHJvd25hbWVzKG1hdF9oZWF0bWFwKSkgJT4lCiAgbGVmdF9qb2luKGVuZnJhbWUoY3V0cmVlb3JkKG1hdF9jbHVzdGVyLCBrID0gbl9jbHVzdGVycyksICJsb2N1cyIsICJjbHVzdGVyIikpICU+JQogIHJlbmFtZShsb2N1c190YWcgPSBsb2N1cykgJT4lCiAgbGVmdF9qb2luKHNlbGVjdChkZl9maXRuZXNzX2NvbXAsIGxvY3VzX3RhZywgZ2VuZV9uYW1lKSkgJT4lCiAgbXV0YXRlKAogICAgZ2VuZV9uYW1lID0gc3RyaV9leHRyYWN0X2ZpcnN0X3JlZ2V4KGdlbmVfbmFtZSwgcmVnZXhfYWxsKSAlPiUKICAgIHN0cl9yZXBsYWNlKCJtb2ciLCAibW9nQSIpCiAgKQoKcGxvdF90c25lIDwtIGRmX3RzbmUgJT4lCiAgeHlwbG90KFYyIH4gVjEsIC4sCiAgICBncm91cHMgPSBjbHVzdGVyLCBjb2wgPSBjdXN0b21fY29sb3JzLAogICAgeGxhYiA9ICJ0U05FIDEiLCB5bGFiID0gInRTTkUgMiIsCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLAogICAgbGFiZWxzID0gLltbImdlbmVfbmFtZSJdXSwgc2VsZWN0ZWQgPSB7IWlzLm5hKC5bWyJnZW5lX25hbWUiXV0pfSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgc2VsZWN0ZWQsIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLnJlcGVsbGFiZWxzKHgsIHksIHNlbGVjdGVkID0gc2VsZWN0ZWQsIGNleCA9IDAuNjUsCiAgICAgICAgZHJhd19ib3ggPSBUUlVFLCBib3hfZmlsbCA9IGdyZXkoMC45NSwgMC41KSwgLi4uKQogICAgICBkaXJlY3RsYWJlbHM6OnBhbmVsLnN1cGVycG9zZS5kbCh4LCB5LCAuLi4pCiAgICB9CiAgKQoKcHJpbnQocGxvdF90c25lKQpgYGAKCiMjIEZ1bmN0aW9ucyBlbnJpY2hlZCBpbiBkaWZmZXJlbnQgY2x1c3RlcnMKCkV4dHJhY3QgY2x1c3RlciBudW1iZXIgZm9yIGVhY2ggZ2VuZSBhbmQgcGVyZm9ybSBHTyBvciBLRUdHIGVucmljaG1lbnQgcGVyIGNsdXN0ZXIuIFRoZSBnb2FsIGlzIHRvIGlkZW50aWZ5IHRoZSBtb3N0IHByZXZhbGVudCBiaW9sb2dpY2FsIGZ1bmN0aW9ucyBwZXIgY2x1c3Rlci4KCmBgYHtyfQpkZl9jbHVzdGVyIDwtIGN1dHJlZW9yZChtYXRfY2x1c3RlciwgayA9IG5fY2x1c3RlcnMpICU+JQogIGVuZnJhbWUobmFtZSA9ICJsb2N1c190YWciLCB2YWx1ZSA9ICJjbHVzdGVyIikgJT4lCiAgYXJyYW5nZShjbHVzdGVyKQpgYGAKCldlIHVzZSB0aGUgZnVuY3Rpb25zIGBrZWdnYWAgZm9yIEtFR0cgZW5yaWNobWVudCBhbmFseXNpcyBhbmQgYGdvYW5hYCBmb3IgR08gdGVybSBlbnJpY2htZW50IGZyb20gdGhlIGBsaW1tYWAgcGFja2FnZS4gQm90aCBmdW5jdGlvbnMgdGVzdCBmb3Igb3ZlciBvciB1bmRlci1yZXByZXNlbnRhdGlvbiBvZiBnZW5lcyBhc3NvY2lhdGVkIHdpdGggY2VydGFpbiBwYXRod2F5cyBvciBHTyB0ZXJtcy4gVGhlIGZ1bmN0aW9ucyBkb24ndCB0YWtlIHRoZSBzdHJlbmd0aCBvZiBkaWZmZXJlbnRpYWwgZml0bmVzcyBpbnRvIGFjY291bnQgKERGOyB0aGUgZGVwbGV0aW9uL2VucmljaG1lbnQgb3ZlciB0aW1lKS4KCmBgYHtyfQp0cnlDYXRjaCgKICBkZl9rZWdnX2VucmljaG1lbnQgPC0gbGFwcGx5KDE6bl9jbHVzdGVycywgZnVuY3Rpb24oY2x1c3QpIHsKICAgIGZpbHRlcihkZl9jbHVzdGVyLCBjbHVzdGVyID09IGNsdXN0KSAlPiUgcHVsbChsb2N1c190YWcpICU+JQogICAga2VnZ2Eoc3BlY2llcy5LRUdHID0gInJlaCIpICU+JQogICAgbXV0YXRlKGNsdXN0ZXIgPSBjbHVzdCkKICB9KSAlPiUgYmluZF9yb3dzLAogIGVycm9yID0gZnVuY3Rpb24oZSkgcHJpbnQoZSkKKQpgYGAKClZpc3VhbGl6ZSB0aGUgcGF0aHdheXMgdGhhdCBhcmUgbW9zdCBlbnJpY2hlZCBmb3IgdGhlIGRpZmZlcmVudCBnZW5lIGNsdXN0ZXJzLgoKYGBge3IsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQpwbG90X2tlZ2dlbnJpY2ggPC0gZGZfa2VnZ19lbnJpY2htZW50ICU+JQogIG11dGF0ZSgKICAgIGtlZ2dfcGF0aHdheSA9IHRvbG93ZXIoUGF0aHdheSksCiAgICBrZWdnX3BhdGh3YXkgPSBzdHJfcmVtb3ZlX2FsbChrZWdnX3BhdGh3YXksICIuP2Jpb3N5bnRoZXNpc3wgb2Z8IG1ldGFib2xpc20iKSwKICAgIGtlZ2dfcGF0aHdheSA9IHN0cl9zdWIoa2VnZ19wYXRod2F5LCAxLCAzMikKICApICU+JQogIGZpbHRlcihOID49IDUsIE4gPD0gMjAwKSAlPiUKICBtdXRhdGUobG9nMTBfcF92YWx1ZSA9IGxvZzEwKFAuREUpLCAua2VlcCA9ICJ1bnVzZWQiKSAlPiUKICBmaWx0ZXIobG9nMTBfcF92YWx1ZSA8IDApICU+JQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogIGFycmFuZ2UobG9nMTBfcF92YWx1ZSkgJT4lCiAgc2xpY2UoMTozKSAlPiUgcm93d2lzZSgpICU+JQogIG11dGF0ZSgKICAgIGtlZ2dfcGF0aHdheSA9IHN0cl9yZW1vdmUoa2VnZ19wYXRod2F5LCAiIC0gQ3VwcmlhdmlkdXMgbmVjYXRvciBIMTYiKSwKICAgIGtlZ2dfcGF0aHdheSA9IHN0cl9nbHVlKCJ7a2VnZ19wYXRod2F5fSAoe2NsdXN0ZXJ9KSIpCiAgKSAlPiUgdW5ncm91cCAlPiUKICAKICB4eXBsb3QoZmFjdG9yKGtlZ2dfcGF0aHdheSwgcmV2KHVuaXF1ZShrZWdnX3BhdGh3YXkpKSkgfiBsb2cxMF9wX3ZhbHVlLCAuLAogICAgZ3JvdXBzID0gY2x1c3RlciwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgICBjb2wgPSBjdXN0b21fY29sb3JzLAogICAgeGxhYiA9IGV4cHJlc3Npb24oImxvZyJbMTBdKiIgcC12YWx1ZSIpLCB5bGFiID0gIiIsCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIGxhYmVscywgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLnh5cGxvdCh4LCB5LCAuLi4pCiAgICB9CiAgKQoKcHJpbnQocGxvdF9rZWdnZW5yaWNoKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNC41LCBlY2hvID0gRkFMU0V9CnN2ZyhmaWxlbmFtZSA9ICIuLi9maWd1cmVzL2ZpZ3VyZV90c25lLnN2ZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNC41KQpwcmludChwbG90X3RzbmUsIHBvc2l0aW9uID0gYygtMC4wMiwgMCAsIDAuNTIsIDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9rZWdnZW5yaWNoLCBwb3NpdGlvbiA9IGMoMC40NiwgMCAsIDEsIDEpLCBtb3JlID0gVFJVRSkKZGV2Lm9mZigpCmBgYAojIEdlbmVzIGVzc2VudGlhbCBpbiBhbGwgY29uZGl0aW9ucwoKIyMgQ2x1c3RlciAxCgotIGJlZm9yZSBsb29raW5nIGF0IGNvbmRpdGlvbi1zcGVjaWZpYyBlc3NlbnRpYWxpdHksIGNhbiBhbHNvIGxvb2sgYXQgZ2VuZXMgZXNzZW50aWFsIGluIGFsbCBjb25kaXRpb25zCi0gdGhlc2UgZ2VuZXMgYXJlIG1vc3RseSBwcmVzZW50IGluIGNsdXN0ZXIgMQotIHdlIGFzc3VtZSB0aGF0IHRoZXNlIGFyZSBhbWlubyBhY2lkIGJpb3N5bnRoZXNpcyBnZW5lcwotIHRoZXkgYmVjb21lIGVzc2VudGlhbCB3aGVuIHRyYW5zZmVycmluZyAqQy4gbmVjYXRvciogZnJvbSByaWNoIHRvIG1pbmltYWwvZGVmaW5lZCBtZWRpdW0KLSBpbiB0b3RhbCAzMyBnZW5lcyB3aXRoIGRlcGxldGlvbiAKCkdlbmVyYWxpemVkIGZ1bmN0aW9uIHRvIHBsb3QgKHNtYWxsKSBoZWF0bWFwcy4KCmBgYHtyfQpoZWF0bWFwX2ZpdG5lc3MgPC0gZnVuY3Rpb24oZGF0YSwga2V5ID0gVFJVRSkgewogIAogICMgbWFrZSBzb21lIHNtYWxsIGFkanVzdG1lbnRzIHRvIG9wdGljYWwgYXBwZWFyYW5jZQogIGRhdGEgJT4lCiAgbXV0YXRlKG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiA9IG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiAlPiUKICAgIHJlcGxhY2UoLiwgLiA8IC02LCAtNikgJT4lIHJlcGxhY2UoLiwgLiA+IDYsIDYpKSAlPiUKCiAgIyBwbG90IGhlYXRtYXAKICBsZXZlbHBsb3Qobm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuIH4gZmFjdG9yKGdlbmVfbmFtZSkgKiBmY3RfcmV2KGNvbmRfc2hvcnQpLCAuLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgY29sb3JrZXkgPSBrZXksCiAgICBjb2wucmVnaW9ucyA9IGNvbG9yUmFtcFBhbGV0dGUoaGVhdF9jb2xzKSgyNSksCiAgICBhdCA9IHNlcSgtNiwgNiwgMC41KSwgYXNwZWN0ID0gImlzbyIsCiAgICB4bGFiID0gIiIsIHlsYWIgPSAiIiwgc2NhbGVzID0gbGlzdChjZXggPSAwLjcsIHggPSBsaXN0KHJvdCA9IDkwKSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIHosIC4uLikgewogICAgICBwYW5lbC5sZXZlbHBsb3QoeCwgeSwgeiwgLi4uKQogICAgICBwYW5lbC5hYmxpbmUodiA9IHNlcV9hbG9uZyh1bmlxdWUoeCkpKzAuNSwgCiAgICAgICAgaCA9IHNlcV9hbG9uZyh1bmlxdWUoeSkpKzAuNSwgY29sID0gIndoaXRlIiwgbHdkID0gMikKICAgIH0KICApCn0KYGBgCgpHZW5lcmFsaXplZCBmdW5jdGlvbiB0byBwbG90IGdlbmVzIGZvciBkaWZmZXJlbnQgcmVnaW9ucyBvZiB0aGUgZ2Vub21lLgoKYGBge3J9Cmdlbm9tZV9wbG90IDwtIGZ1bmN0aW9uKGRmLCB4bGltID0gTlVMTCwgdGl0bGUgPSAiIiwgcm90X2xhYmVscyA9IDApIHsKICBkZiA8LSAgcmVwbGFjZV9uYShkZiwgbGlzdChzdHJhaW5zX3Blcl9nZW5lID0gMCkpCiAgdGhlbWUgPSBjdXN0b20uY29sb3JibGluZChjb2wgPSBjKGdyZXkoMC43KSwgZ3JleSgwLjg1KSkpCiAgdGhlbWUkYXhpcy5saW5lJGNvbCA9IGdyZXkoMC4zKQogIHRoZW1lJGF4aXMudGV4dCRjb2wgPSBncmV5KDAuMykKICB0aGVtZSRheGlzLnRleHQkY2V4ID0gMC43CiAgCiAgaWYgKCFpcy5udWxsKHhsaW0pKQogICAgeHNjYWxlID0gbGlzdChsaW1pdHMgPSB4bGltKQogIGVsc2UgCiAgICB4c2NhbGUgPSBsaXN0KCkKICB4eXBsb3QoZW5kLzEwMDAgfiBzdGFydC8xMDAwLCBkZiwKICAgIGdyb3VwcyA9IHN0cmFuZCwgY2V4ID0gMC43LCBsd2QgPSAxLAogICAgcGFyLnNldHRpbmdzID0gdGhlbWUsIHN0cmFpbnMgPSBkZiRzdHJhaW5zX3Blcl9nZW5lLAogICAgc2NhbGVzID0gbGlzdCh5ID0gbGlzdChkcmF3ID0gRkFMU0UpLCB4ID0geHNjYWxlKSwKICAgIHlsaW0gPSBjKC0zLDIpLCB4bGFiID0gIiIsIHlsYWIgPSAiIiwKICAgIGdlbmVfc3RyYW5kID0gZGZbWyJzdHJhbmQiXV0sCiAgICBnZW5lX25hbWUgPSBkZltbImdlbmVfbmFtZSJdXSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgc3RyYWlucywgLi4uKSB7CiAgICAgIHBhbmVsLmdlbmVwbG90KHgsIHksIGFycm93cyA9IFRSVUUsIAogICAgICAgIHRpcCA9IDAuMSwgcm90X2xhYmVscyA9IHJvdF9sYWJlbHMsIC4uLikKICAgICAgcGFuZWwudGV4dCgoeCt5KS8yLCByZXAoMCwgbGVuZ3RoKHgpKSwgbGFiZWxzID0gc3RyYWlucywgY2V4ID0gMC43KQogICAgICBwYW5lbC50ZXh0KG1lYW4oeGxpbSksIDEuNSwgbGFiZWxzID0gdGl0bGUsIGNvbCA9IDEpCiAgICB9CiAgKQp9CmBgYAoKUGxvdCBoZWF0IG1hcCBvZiBjbHVzdGVyIDEgZ2VuZSBmaXRuZXNzLgoKYGBge3IsIGZpZy53aWR0aCA9IDcuMCwgZmlnLmhlaWdodCA9IDQuMH0KcGxvdF9jbDEgPC0gZGZfY2x1c3RlciAlPiUKICBmaWx0ZXIoY2x1c3RlciA9PSAxKSAlPiUKICBsZWZ0X2pvaW4oZGZfZml0bmVzc19zdW1tYXJ5LCBieSA9ICJsb2N1c190YWciKSAlPiUKICBmaWx0ZXIodGltZSA9PSA4KSAlPiUKICBncm91cF9ieShDT0dfUHJvY2VzcykgJT4lCiAgbXV0YXRlKENPR19uID0gbigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZShkZXNjKENPR19uKSwgQ09HX1Byb2Nlc3MsIGdlbmVfbmFtZSkgJT4lCiAgbXV0YXRlKGdlbmVfbmFtZSA9IGZjdF9pbm9yZGVyKGdlbmVfbmFtZSkpICU+JQogIGhlYXRtYXBfZml0bmVzcygpCgpwcmludChwbG90X2NsMSkKYGBgCi0gV2hpY2ggZnVuY3Rpb25hbCBjYXRlZ29yaWVzIChDT0cpIGRvIGdlbmVzIGJlbG9uZyB0bz8KCmBgYHtyLCBmaWcud2lkdGggPSA3LjAsIGZpZy5oZWlnaHQgPSAyLjV9CnBsb3RfY2wxX2Z1bmN0aW9uIDwtIGRmX2NsdXN0ZXIgJT4lCiAgZmlsdGVyKGNsdXN0ZXIgPT0gMSkgJT4lCiAgbGVmdF9qb2luKGRmX2ZpdG5lc3NfY29tcCwgYnkgPSAibG9jdXNfdGFnIikgJT4lCiAgY291bnQoQ09HX1Byb2Nlc3MpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAgbXV0YXRlKENPR19Qcm9jZXNzID0gZmN0X2lub3JkZXIoQ09HX1Byb2Nlc3MpKSAlPiUKICBtdXRhdGUodmFycyA9IGZhY3RvcigiQ09HIHByb2Nlc3MiKSkgJT4lCiAgeHlwbG90KHZhcnMgfiBuLCAuLAogICAgc3RhY2sgPSBUUlVFLCBob3Jpem9udGFsID0gVFJVRSwKICAgIGF1dG8ua2V5ID0gbGlzdChjb2x1bW5zID0gMiksCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLAogICAgeGxhYiA9ICJjb3VudCIsIHlsYWIgPSAiIiwgbHR5ID0gMCwKICAgIHhsaW0gPSBjKDAsIDMzKSwKICAgIGdyb3VwcyA9IENPR19Qcm9jZXNzLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC5iYXJjaGFydCh4LCB5LCAuLi4pCiAgICB9CiAgKQoKcHJpbnQocGxvdF9jbDFfZnVuY3Rpb24pCmBgYAoKYGBge3IsIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDZ9CnN2ZyhmaWxlbmFtZSA9ICIuLi9maWd1cmVzL2ZpZ3VyZV9jbDEuc3ZnIiwgd2lkdGggPSA3LCBoZWlnaHQgPSA2KQpwcmludChwbG90X2NsMSwgcG9zaXRpb24gPSBjKDAsIDAsIDEsIDAuNzUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9jbDFfZnVuY3Rpb24sIHBvc2l0aW9uID0gYygtMC4wMTgsIDAuNjIsIDAuOTMsIDEpKQpkZXYub2ZmKCkKYGBgCgoKCiMgU3VwZXJ2aXNlZCBhbmFseXNpcyBvZiBlbmVyZ3kgbWV0YWJvbGlzbQoKIyMgTW9seWJkZW51bSBjb2ZhY3RvciBiaW9zeW50aGVzaXMKCi0gICBtb2RBQiA9IG1vbHliZGVudW0gdHJhbnNwb3J0ZXIKLSAgIG1vYUFDREUgPSBtb2x5YmRvcHRlcmluIGJhY2tib25lIGJpb3N5bnRoZXNpcwotICAgbW9lQSA9IG1vbHliZG9wdGVyaW4gbW9seWJkZW51bSB0cmFuc2ZlcmFzZQotICAgbW9iQUIgPSBtb2x5YmRlbnVtIGNvZmFjdG9yIGd1YW55bHlsdHJhbnNmZXJhc2UsCi0gICBtb2cgPSBtb2x5YmRvcHRlcmluIGJpb3N5bnRoZXNpcyBwcm90ZWluIChyb2xlIHVuY2xlYXI/KQoKYGBge3IsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA0fQojIHBsb3QgaGVhdG1hcApkZl9tb2NvIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUgZmlsdGVyKHRpbWUgPT0gOCwgZ3JlcGwoIm1vW2FlZGJnXSIsIGdlbmVfbmFtZSkpICU+JQogIGZpbHRlcighc3RyX2RldGVjdChnZW5lX25hbWUsICJtb2FBMiIpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyaV9leHRyYWN0X2ZpcnN0X3JlZ2V4KGdlbmVfbmFtZSwgIm1vW2FlZGJnXShbQS1aMC05XSspPyIpICU+JQogICAgc3RyX3JlcGxhY2UoIm1vZyIsICJtb2dBIikpICU+JQogIG11dGF0ZShnZW5lX25hbWUgPSBmYWN0b3IoZ2VuZV9uYW1lLCBjKCJtb2FBIiwgIm1vYUMiLCAibW9hRCIsICJtb2FFIiwgIm1vYUYiLAogICAgIm1vZEEiLCAibW9kQiIsICJtb2RDIiwgIm1vZ0EiLCAibW9lQTEiLCAibW9lQTIiLCAibW9lQTMiLCAibW9iQSIsICJtb2JCIiwgIm1vYkIyIiwgIm1vYkIzIikpKQpwbG90X21vY29fZml0IDwtIGhlYXRtYXBfZml0bmVzcyhkZl9tb2NvKQoKIyBwbG90IGdlbm9taWMgbG9jYXRpb25zCmRmX21vY28gPC0gZGZfbW9jbyAlPiUgZ3JvdXBfYnkobG9jdXNfdGFnLCBnZW5lX25hbWUsIHNjYWZmb2xkLCBzdHJhbmQsIHN0YXJ0LCBlbmQpICU+JQogIHN1bW1hcml6ZShzdHJhaW5zX3Blcl9nZW5lID0gbWluKHVuaXF1ZShzdHJhaW5zX3Blcl9nZW5lKSksIC5ncm91cHMgPSAiZHJvcCIpCnBsb3RfbW9jb19nMSA8LSBnZW5vbWVfcGxvdChkZl9tb2NvLCB4bGltID0gYygyNDU1LjksIDI0NTguNiksIHRpdGxlID0gImNociAxIikKcGxvdF9tb2NvX2cyIDwtIGdlbm9tZV9wbG90KGRmX21vY28sIHhsaW0gPSBjKDI3ODcuOCwgMjc5MS4yKSwgdGl0bGUgPSAiY2hyIDEiKQoKcHJpbnQocGxvdF9tb2NvX2ZpdCwgcG9zaXRpb24gPSBjKDAsMC4yMSwxLDEuMDUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9tb2NvX2cxLCBwb3NpdGlvbiA9IGMoMC4xNywtMC4xLDAuNTcsMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbW9jb19nMiwgcG9zaXRpb24gPSBjKDAuNTMsLTAuMSwwLjgzLDAuNCkpCmBgYAoKSW50ZXJlc3RpbmcgcmVzdWx0cyB0byBkb3VibGUtY2hlY2s6CgotICAgV2h5IGlzIG1vYUEgZXNzZW50aWFsIGZvciBuaXRyYXRlIHJlc3BpcmF0aW9uIGJ1dCBub3QgZm9ybWF0ZT8gSXMgdGhlcmUgYW5vdGhlciBpc28tZW56eW1lIGZvciBmaXJzdCBzdGVwIG9mIE1vQ28gc3ludGhlc2lzPwotICAgbW9nQSwgbW9hQSwgbW9hQiwgbW9hRSwgbW9kQiwgb3IgbW9kQyBkZWxldGlvbiBtdXRhbnRzIGFyZSBub3QgdmlhYmxlIGluIEUuY29saS4gRXhjZXB0IGZvciBtb2FBLCBhbHNvIG1vZEEsQixDIGFyZSBub3QgZXNzZW50aWFsIGZvciBncm93dGggb24gZm9ybWF0ZS4KLSAgIG1vZEFCQyBpcyB0aGUgMy1zdWJ1bml0IG1lbWJyYW5lIHRyYW5zcG9ydCBzeXN0ZW07IGl0IHNob3VsZCBiZSBlc3NlbnRpYWwsIGhvd2V2ZXIgb25lIGV4cGxhbmF0aW9uIGZvciBpdCBub3QgYmVpbmcgZXNzZW50aWFsIGlzIHRoYXQgYW5vdGhlciBsb3ctYWZmaW5pdHkgdHJhbnNwb3J0ZXIgbWlnaHQgZXhpc3QgdGhhdCBjb21wZW5zYXRlcyBmb3IgdGhlIGxvc3MgaW4gbWVkaXVtIHdpdGggaGlnaCBNby1jb25jZW50cmF0aW9uIChbWGlhIGV0IGFsLiwgMjAxOF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNjI5NTQ1NS8pKS4gVGhlIGF1dGhvcnMgd3JpdGUgaW4gdGhlIGRpc2N1c3Npb246ICJNb2x5YmRhdGUgY2FuIGJlIHRha2VuIHVwIGJ5IHRoZSBzdWxmYXRlLXRyYW5zcG9ydCBzeXN0ZW0gaW4gdGhlIGFic2VuY2Ugb2YgYSBmdW5jdGlvbmFsIGhpZ2gtYWZmaW5pdHkgbW9seWJkYXRlLXRyYW5zcG9ydCBzeXN0ZW0gaW4gRS4gY29saSBhbmQgQi4gamFwb25pY3VtIChSb3NlbnRlbCBldCBhbC4sIDE5OTU7IERlbGdhZG8gZXQgYWwuLCAyMDA2KS4iCi0gICBtb2VBIChtb2x5YmRlbnVtIHRyYW5zZmVyYXNlKSBpcyBwcmVzZW50IHdpdGggMyBpc29lbnp5bWVzLiBNb2VBMSBhbmQgQTIgYm90aCBzZWVtIHRvIGJlIGltcG9ydGFudCBmb3IgZml0bmVzcyBhcyBhIEtPIG9mIGVhY2ggb25lIG9mIHRoZW0gcmVkdWNlcyBmaXRuZXNzIHBhcnRpYWxseSBidXQgbm90IGZ1bGx5IG9uIGZvcm1hdGUvbml0cmF0ZS4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CnN2ZyhmaWxlbmFtZSA9ICIuLi9maWd1cmVzL2ZpZ3VyZV9maXRfbW9jby5zdmciLCB3aWR0aCA9IDcsIGhlaWdodCA9IDQpCnByaW50KHBsb3RfbW9jb19maXQsIHBvc2l0aW9uID0gYygwLDAuMjEsMSwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbW9jb19nMSwgcG9zaXRpb24gPSBjKDAuMTcsLTAuMSwwLjU3LDAuNCksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X21vY29fZzIsIHBvc2l0aW9uID0gYygwLjUzLC0wLjEsMC44MywwLjQpKQpkZXYub2ZmKCkKYGBgCgojIyBGb3JtYXRlIGRlaHlkcm9nZW5hc2UKCi0gICBmZHNBQkNERyA9IHByaW1hcnkgc29sdWJsZSBGREgKLSAgIGZkd0FCID0gc2Vjb25kYXJ5IHNvbHVibGUgRkRICi0gICBmZGhBQkNEID0gcHJpbWFyeSBtZW1icmFuZS1ib3VuZCBGREgsIDIgY29waWVzIEExQjEuLi4gYW5kIEEyQjIuLi4KLSAgIGZkb0dISSA9IHNlY29uZGFyeSBtZW1icmFuZS1ib3VuZCBGREgKCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNH0KIyBwbG90IGhlYXRtYXAKZGZfZmRoIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUgZmlsdGVyKHRpbWUgPT0gOCwgZ3JlcGwoImZkW3N3aG9dIiwgZ2VuZV9uYW1lKSkgJT4lCiAgbXV0YXRlKGdlbmVfbmFtZSA9IHN0cmlfZXh0cmFjdF9maXJzdF9yZWdleChnZW5lX25hbWUsICJmZFtzd2hvXVtBLVowLTldKyIpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gcmVjb2RlKGdlbmVfbmFtZSwgYGZkaERgID0gImZkc0MiLCBgZmRoQ2AgPSAiZmRoQzEiLCBgZmRoRWAgPSAiZmRoRTEiKSkgJT4lCiAgbXV0YXRlKGdlbmVfbmFtZSA9IGZhY3RvcihnZW5lX25hbWUsIGMocGFzdGUwKCJmZHMiLCBjKCJBIiwgIkIiLCAiQyIsICJEIiwgIkciLCAiUiIpKSwKICAgICJmZHdBIiwgImZkd0IiLCBwYXN0ZTAoImZkaCIsIGMoIkExIiwgIkIxIiwgIkMxIiwgIkQxIiwgIkUxIiwgIkEyIikpLCAiZmRvRyIpKSkKcGxvdF9mZGhfZml0IDwtIGhlYXRtYXBfZml0bmVzcyhkZl9mZGgpCgojIHBsb3QgZ2Vub21pYyBsb2NhdGlvbnMKZGZfZmRoIDwtIGRmX2ZkaCAlPiUgZ3JvdXBfYnkobG9jdXNfdGFnLCBnZW5lX25hbWUsIHNjYWZmb2xkLCBzdHJhbmQsIHN0YXJ0LCBlbmQpICU+JQogIHN1bW1hcml6ZShzdHJhaW5zX3Blcl9nZW5lID0gbWluKHVuaXF1ZShzdHJhaW5zX3Blcl9nZW5lKSksIC5ncm91cHMgPSAiZHJvcCIpCnBsb3RfZmRoX2cxIDwtIGdlbm9tZV9wbG90KGRmX2ZkaCwgeGxpbSA9IGMoNjc3LjcsIDY4NS42KSwgdGl0bGUgPSAiY2hyIDEiKQpwbG90X2ZkaF9nMiA8LSBnZW5vbWVfcGxvdChkZl9mZGgsIHhsaW0gPSBjKDMxNjcuOSwgMzE3NC45KSwgdGl0bGUgPSAiY2hyIDEiKQoKcHJpbnQocGxvdF9mZGhfZml0LCBwb3NpdGlvbiA9IGMoMCwwLjIzLDEsMS4wNSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2ZkaF9nMSwgcG9zaXRpb24gPSBjKDAuMTcsLTAuMSwwLjU3LDAuNCksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2ZkaF9nMiwgcG9zaXRpb24gPSBjKDAuNTIsLTAuMSwwLjgzLDAuNCkpCmBgYAoKYGBge3IsIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30Kc3ZnKGZpbGVuYW1lID0gIi4uL2ZpZ3VyZXMvZmlndXJlX2ZpdF9mZGguc3ZnIiwgd2lkdGggPSA3LCBoZWlnaHQgPSA0KQpwcmludChwbG90X2ZkaF9maXQsIHBvc2l0aW9uID0gYygwLDAuMjMsMSwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZmRoX2cxLCBwb3NpdGlvbiA9IGMoMC4xNywtMC4xLDAuNTcsMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZmRoX2cyLCBwb3NpdGlvbiA9IGMoMC41MiwtMC4xLDAuODMsMC40KSkKZGV2Lm9mZigpCmBgYAoKIyMgSHlkcm9nZW5hc2VzCgotICAgaG94QSA9IG1hc3RlciByZWd1bGF0b3IsIDItY29tcG9uZW50IHN5c3RlbS4gSG94QSBpcyBjb25zdGFudGx5IGFjdGl2ZSBpbiBpdHMgbm9uLSBwaG9zcGhvcnlsYXRlZCBzdGF0ZSBiZWxvdyBhIHRlbXBlcmF0dXJlIG9mIDMzXCpDLiBPcmlnaW5hbGx5LCBob3hBIGFjdGl2aXR5IGlzIHJlZ3VsYXRlZCBieSB0aGUga2luYXNlIEhveEogKHRvZ2V0aGVyIHdpdGggSG94QkMsIHRoZSAncmVndWxhdG9yeScgaHlkcm9nZW5hc2UpLiBUaGlzIGZ1bmN0aW9uIGlzIGluYWN0aXZhdGVkIGR1ZSB0byBhIG11dGF0aW9uIGluIGhveEosIFtGcmllZHJpY2ggJiBGcmllZHJpY2gsIEogQmFjLCAxOTgzXShodHRwczovL2pvdXJuYWxzLmFzbS5vcmcvZG9pLzEwLjExMjgvamIuMTUzLjEuMTc2LTE4MS4xOTgzKSwgW0xlbnogZXQgYWwuLCBKTU1CLCAyMDA0XShodHRwczovL2NpdGVzZWVyeC5pc3QucHN1LmVkdS92aWV3ZG9jL2Rvd25sb2FkP2RvaT0xMC4xLjEuMzM0LjUzMzAmcmVwPXJlcDEmdHlwZT1wZGYpLgotICAgaG94S0daID0gbWVtYnJhbmUtYm91bmQgaHlkcm9nZW5hc2UKLSAgIGhveE1MT1FSVFYgPSBNQkggYWNjZXNzb3J5IGdlbmVzIChjb2ZhY3RvciBpbmNvcnBvcmF0aW9uLCBzdWJ1bml0IGFzc2VtYmx5LCBhbmQgdHdpbi1hcmdpbmluZS1kZXBlbmRlbnQgbWVtYnJhbmUgdHJhbnNsb2NhdGlvbiAtIFRBVCksIFtTY2h1YmVydCBldCBhbC4sIE1vbCBNaWNyb2JpbywgMjAwN10oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8xNzg1MDI1OS8pLCBbRnJpdHNjaCBldCBhbC4sIEogQmFjLCAyMDExXShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzIxNDQxNTE0LykgW1NjaHdhcnR6IGV0IGFsLiwgSiBNb2wgQmlvLCAyMDAzXShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzEyOTQ4NDg4KQotICAgaG94TjEsIGhveE4yID0gTmlja2VsL0NvYmFsdCB0cmFuc3BvcnRlcnMuIEhveE4yIDUxJSBpZGVudGljYWwgb24gQUEgbGV2ZWwgYnV0IG5vdCBmdW5jdGlvbmFsLCBbU2Nod2FydHogZXQgYWwuLCBKIE1vbCBCaW8sIDIwMDNdKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvMTI5NDg0ODgpCi0gICBob3hCQyA9IHJlZ3VsYXRvcnkgaHlkcm9nZW5hc2UgUkgsIG94eWdlbi1yZXNpc3RhbnQgbm9uLWVuZXJneSBnZW5lcmF0aW5nIGh5ZHJvZ2VuIHNlbnNvci4gSG94QyBpcyBsYXJnZSBhY3RpdmUtc2l0ZS1jb250YWluaW5nIHN1YnVuaXQsIGhveEIgc21hbGwgc3VidW5pdCBIb3hCIGNhcnJ5aW5nIEZlLVMgY2x1c3RlcnMKLSAgIEhveEogPSBSSCBiaW5kaW5nIHNpZ25hbCB0cmFuc2R1Y2VyLCBoaXN0aWRpbmUgcHJvdGVpbiBraW5hc2UgW0zDtnNjaGVyIGV0IGFsLiwgQ2hlbSBQaHlzIENoZW0sIDIwMTBdKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvMjAzNDAxMjQvKSBbQnVlcnN0ZWwgZXQgYWwuLCBQTkFTLCAyMDE2XShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzI3OTMwMzE5LykKLSAgIGhveEZVWUggPSBzb2x1YmxlIGh5ZHJvZ2VuYXNlCi0gICBob3hXID0gc29sdWJsZSBoeWRyb2dlbmFzZSAoSG94SC1zcGVjaWZpYykgQy10ZXJtaW5hbCBwcm90ZWFzZSAobWF0dXJhdGlvbikKLSAgIGhveEkgPSBvcHRpb25hbCBzdWJ1bml0IG9mIFNIIChob3hGVVlIKTsgc3BlY2lmaWMgcmVhY3Rpb24gc2l0ZSBmb3IgTkFEUEgsIFtCdXJnZG9yZiBldCBhbC4sIEogQmFjLCAyMDA1XShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzE1ODM4MDM5LykKLSAgIGEgNHRoIGh5ZHJvZ2VuYXNlIGV4aXN0IG9uIHBIRzEsIG1hZGUgZnJvbSAyIHN1YnVuaXRzICgqaG9mSyosICpob2ZHKiwgcEhHMDY0LzA2NSksIHNlZSBbU2Now6RmZXIgZXQgYWwuLCAyMDE1XShodHRwczovL3d3dy5jZWxsLmNvbS9zdHJ1Y3R1cmUvcGRmRXh0ZW5kZWQvUzA5NjktMjEyNigxNSkwMDQ5OS0yKS4KLSAgIHRoZXNlIHN1YnVuaXRzIGhhdmUgbm8gZWZmZWN0IG9uIGZpdG5lc3MgKGhpZ2ggY292ZXJhZ2Ugb2YgOS0xMSBtdXRhbnRzIHBlciBnZW5lKSwgYW5kIGFyZSB2ZXJ5IGxvd2x5IGV4cHJlc3NlZCAoUEhHMDY0IG4uZC4sIFBIRzA2NSBsb3cgYWJ1bmRhbmNlKQoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0fQojIHBsb3QgaGVhdG1hcApkZl9oeWQgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcih0aW1lID09IDgsIGdyZXBsKCJob3giLCBnZW5lX25hbWUpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyaV9leHRyYWN0X2ZpcnN0X3JlZ2V4KGdlbmVfbmFtZSwgImhveFtBLVowLTldKyIpKSAlPiUKICB1bmdyb3VwICU+JSBtdXRhdGUoZ2VuZV9uYW1lID0gZmFjdG9yKGdlbmVfbmFtZSwgcGFzdGUwKCJob3giLCBjKCJLIiwiRyIsIloiLCJNIiwKICAgIkwiLCJPIiwiUSIsIlIiLCJUIiwiViIsIkEiLCJCIiwiQyIsIkoiLCJOIiwiTjIiLCJGIiwiVSIsIlkiLCJIIiwiVyIsIkkiKSkpKQpwbG90X2h5ZF9maXQgPC0gaGVhdG1hcF9maXRuZXNzKGRmX2h5ZCkKCiMgcGxvdCBnZW5vbWljIGxvY2F0aW9ucwpkZl9oeWQgPC0gZGZfaHlkICU+JSBncm91cF9ieShsb2N1c190YWcsIGdlbmVfbmFtZSwgc2NhZmZvbGQsIHN0cmFuZCwgc3RhcnQsIGVuZCkgJT4lCiAgc3VtbWFyaXplKHN0cmFpbnNfcGVyX2dlbmUgPSBtaW4odW5pcXVlKHN0cmFpbnNfcGVyX2dlbmUpKSwgLmdyb3VwcyA9ICJkcm9wIikKcGxvdF9oeWRfZzEgPC0gZ2Vub21lX3Bsb3QoZGZfaHlkLCB4bGltID0gYygwLCA0LjEpLCB0aXRsZSA9ICJwSEcxIikKcGxvdF9oeWRfZzIgPC0gZ2Vub21lX3Bsb3QoZGZfaHlkLCB4bGltID0gYygxNS4xLCAyMi44KSwgdGl0bGUgPSAicEhHMSIpCnBsb3RfaHlkX2czIDwtIGdlbm9tZV9wbG90KGRmX2h5ZCwgeGxpbSA9IGMoNzkuMiwgODUuNSksIHRpdGxlID0gInBIRzEiKQoKcHJpbnQocGxvdF9oeWRfZml0LCBwb3NpdGlvbiA9IGMoMCwwLjIzLDEsMS4wNSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2h5ZF9nMSwgcG9zaXRpb24gPSBjKDAuMTUsLTAuMSwwLjM5LDAuNCksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2h5ZF9nMiwgcG9zaXRpb24gPSBjKDAuMzUsLTAuMSwwLjY1LDAuNCksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2h5ZF9nMywgcG9zaXRpb24gPSBjKDAuNjEsLTAuMSwwLjkwLDAuNCkpCmBgYAoKYGBge3IsIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30Kc3ZnKGZpbGVuYW1lID0gIi4uL2ZpZ3VyZXMvZmlndXJlX2ZpdF9oeWQuc3ZnIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA0KQpwcmludChwbG90X2h5ZF9maXQsIHBvc2l0aW9uID0gYygwLDAuMjMsMSwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfaHlkX2cxLCBwb3NpdGlvbiA9IGMoMC4xNSwtMC4xLDAuMzksMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfaHlkX2cyLCBwb3NpdGlvbiA9IGMoMC4zNSwtMC4xLDAuNjUsMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfaHlkX2czLCBwb3NpdGlvbiA9IGMoMC42MSwtMC4xLDAuOTAsMC40KSkKZGV2Lm9mZigpCmBgYAoKIyMgSHlkcm9nZW5hc2UgcGxlaW90cm9weSBnZW5lcyAobWF0dXJhdGlvbikKCi0gICBoeXBBMSxCMSxGMSxDMSxEMSxFMSA9IE5pLUZlIG1ldGFsbG9jZW50ZXIgZm9ybWF0aW9uIHByb3RlaW5zIGZvciBib3RoIE1CSCBhbmQgU0guICJIeXBDMSwgaHlwRDEsIGFuZCBoeXBFMSBhcmUgZXNzZW50aWFsIGZvciBTSCBhbmQgTUJIIG1hdHVyYXRpb24sIHdoaWxlIG9ubHkgb25lIGludGFjdCBjb3B5IGVhY2ggb2YgaHlwQSwgaHlwQiwgYW5kIGh5cEYgaXMgbmVlZGVkIiwgW1NjaHdhcnR6IGV0IGFsLiwgSiBNb2wgQmlvLCAyMDAzXShodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzEyOTQ4NDg4KQotICAgaHlwQSBhbmQgQgotICAgaHlwQ0RFRiBhbGwgZm9ybSBhIGNvbXBsZXggdG8gYXNzZW1ibGUgdGhlIE5pLUZlLUNPLUNOIGNvZmFjdG9yCi0gICBoeXBYID0gQ08gY29mYWN0b3IgZm9ybWF0aW9uIGZvciBOaUZlLWh5ZHJvZ2VuYXNlLiBIeXBYIGNvbnZlcnRzIGZvcm15bC1USEYgaW50byB3YXRlciBhbmQgQ08gaW4gYWVyb2JpYyBjb25kaXRpb25zLCBbU2NodWx6IGV0IGFsLiwgSkFDUywgMjAyMF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMjEvamFjcy45YjExNTA2KQotICAgaHlwQTIsQjIsRjIgPSBhbHRlcm5hdGl2ZSBnZW5lcyBmb3IgaHlwQTEsQjEsRjEsIG5vdCBzdWZmaWNpZW50IGFsb25lIGZvciBoeWRyb2dlbmFzZSBmb3JtYXRpb24KLSAgIGh5cEYzLGh5cEMyLGh5cEQyLGh5cEUyLGh5cEEzLGh5cEIzLiBUaGUgcGh5c2lvbG9naWNhbCBmdW5jdGlvbiBvZiB0aGVzZSBleHRyYSBoeXAgZ2VuZXMgaXMgdW5jbGVhcgoKYGBge3IsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA0fQpkZl9oeXAgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JSBmaWx0ZXIodGltZSA9PSA4LCBncmVwbCgiaHlwIiwgZ2VuZV9uYW1lKSkgJT4lCiAgIyBjb3JyZWN0IG5hbWVzIG9mIHNldmVyYWwgaHlwIGdlbmVzIHRvIG1ha2UgaXQgbW9yZSBzeXN0ZW1hdGljCiAgbXV0YXRlKGdlbmVfbmFtZSA9IGdzdWIoImh5cEEgUEhHMDk0IiwgImh5cEEyIFBIRzA5NCIsIGdlbmVfbmFtZSkpICU+JQogIG11dGF0ZShnZW5lX25hbWUgPSBzdHJpX2V4dHJhY3RfZmlyc3RfcmVnZXgoZ2VuZV9uYW1lLCAiaHlwW0EtWjAtOV0rIikgJT4lCiAgICByZWNvZGUoaHlwQSA9ICJoeXBBMSIsIGh5cEIgPSAiaHlwQjEiLCBoeXBDID0gImh5cEMxIiwgaHlwRCA9ICJoeXBEMSIsIGh5cEUgPSAiaHlwRTEiKSkgJT4lCiAgdW5ncm91cCAlPiUgbXV0YXRlKGdlbmVfbmFtZSA9IGZhY3RvcihnZW5lX25hbWUsIHBhc3RlMCgiaHlwIiwgYygiQTEiLCJCMSIsIkMxIiwiRDEiLCJFMSIsCiAgICAiRjEiLCJYIiwiQTIiLCJCMiIsIkMyIiwiRDIiLCJFMiIsIkYyIiwiQTMiLCJCMyIsIkYzIikpKSkKcGxvdF9oeXBfZml0IDwtIGhlYXRtYXBfZml0bmVzcyhkZl9oeXApCgojIHBsb3QgZ2Vub21pYyBsb2NhdGlvbnMKZGZfaHlwIDwtIGRmX2h5cCAlPiUgZ3JvdXBfYnkobG9jdXNfdGFnLCBnZW5lX25hbWUsIHNjYWZmb2xkLCBzdHJhbmQsIHN0YXJ0LCBlbmQpICU+JQogIHN1bW1hcml6ZShzdHJhaW5zX3Blcl9nZW5lID0gbWluKHVuaXF1ZShzdHJhaW5zX3Blcl9nZW5lKSksIC5ncm91cHMgPSAiZHJvcCIpCnBsb3RfaHlwX2cxIDwtIGdlbm9tZV9wbG90KGRmX2h5cCwgeGxpbSA9IGMoOC4zLCAxNS42KSwgdGl0bGUgPSAicEhHMSIpCnBsb3RfaHlwX2cyIDwtIGdlbm9tZV9wbG90KGRmX2h5cCwgeGxpbSA9IGMoNjYuMywgNzMuNSksIHRpdGxlID0gInBIRzEiKQpwbG90X2h5cF9nMyA8LSBnZW5vbWVfcGxvdChkZl9oeXAsIHhsaW0gPSBjKDg1LjAsIDg5LjcpLCB0aXRsZSA9ICJwSEcxIikKCnByaW50KHBsb3RfaHlwX2ZpdCwgcG9zaXRpb24gPSBjKDAsMC4yMywxLDEuMDUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9oeXBfZzEsIHBvc2l0aW9uID0gYygwLjI2LC0wLjEsMC43OCwwLjQpLCBtb3JlID0gVFJVRSkKYGBgCgpgYGB7ciwgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gJ2hpZGUnfQpzdmcoZmlsZW5hbWUgPSAiLi4vZmlndXJlcy9maWd1cmVfZml0X2h5cC5zdmciLCB3aWR0aCA9IDcsIGhlaWdodCA9IDQpCnByaW50KHBsb3RfaHlwX2ZpdCwgcG9zaXRpb24gPSBjKDAsMC4yMywxLDEuMDUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9oeXBfZzEsIHBvc2l0aW9uID0gYygwLjI2LC0wLjEsMC43OCwwLjQpLCBtb3JlID0gVFJVRSkKZGV2Lm9mZigpCmBgYAoKIyMgT3RoZXIgY29tcGxleGVzIGltcG9ydGFudCBpbiBlbmVyZ3kgbWV0YWJvbGlzbQoKLSAgICoqc3VjY2luYXRlIGRlaHlkcm9nZW5hc2UqKgogICAgLSAgIHNkaEFCQ0QgPSBtZW1icmFuZSBpbnRlZ3JhbCBzdWNjaW5hdGUgZGVoeWRyb2dlbmFzZSwgcmVkdWNpbmcgVVEgdG8gVVFIMjogKiphbGwgb2YgaXRzIGdlbmVzIGFyZSBzdHJpY3RseSBlc3NlbnRpYWwqKiwgbm8gS08gbXV0YW50cyBhdmFpbGFibGUKLSAgICoqTkFESCBkZWh5ZHJvZ2VuYXNlKioKICAgIC0gICBudW9BQkNERUZHSElKS0xNTiA9IG1lbWJyYW5lIGludGVncmFsIE5BREggZGVoeWRyb2dlbmFzZSwgb3hpZGl6ZXMgTkFESCwgcHVtcHMgNCBIKywgcmVkdWNlcyBVUSB0byBVUUgyOiAqKmFsbCBvZiBpdHMgZ2VuZXMgYXJlIHN0cmljdGx5IGVzc2VudGlhbCoqLCBubyBLTyBtdXRhbnRzIGF2YWlsYWJsZQoKIyMgRWxlY3Ryb24gdHJhbnNwb3J0IGNoYWluCgpDeXRvY2hyb21lIGUtIHRyYW5zcG9ydCBwcm90ZWlucywgY3l0b2Nocm9tZSByZWR1Y3Rhc2VzLCBhbmQgdGVybWluYWwgb3hpZGFzZXMuCgotICAgKipxdWlub2wgb3hpZGFzZXMqKgogICAgLSAgIGN5b0ExQjFDMUQxLCAuLi4gKDMgY29waWVzKSA9IGN5dG9jaHJvbWUgYm8zIGNvbXBsZXgsIHF1aW5vbGUgb3hpZGFzZSwgbG93IE8yIGFmZmluaXR5CiAgICAtICAgY3lkQTFCMSwgLi4uICgyIGNvcGllcykgPSBjeXRvY2hyb21lIGJkIGNvbXBsZXgsIHF1aW5vbGUgb3hpZGFzZSwgaGlnaCBPMiBhZmZpbml0eQotICAgKipjeXRvY2hyb21lIHJlZHVjdGFzZXMqKgogICAgLSAgIHFjckFCQyAoYWthIHBldEFCQywgYEgxNl9BMzM5OGAsIGBIMTZfQTMzOTdgLCBgSDE2X0EzMzk2YCkgPSBjeXRvY2hyb21lIGJjMSByZWR1Y3Rhc2UKLSAgICoqY3l0b2Nocm9tZSBDIGUtIGNhcnJpZXJzKioKICAgIC0gICBjeXRvY2hyb21lIGM1NTMgYWNjb3JkaW5nIHRvIHVuaXByb3Q6IGBIMTZfQTM0NTFgLCBgSDE2X0EwODMwYCwgYEgxNl9BMDg0NmAsIGBIMTZfQjE0NTJgLCBgSDE2X0EzNTc2YCwgYEgxNl9BMTEyMWAsIGBIMTZfQTExMjBgLiBBY2NvcmRpbmcgdG8gU3RyaW5nIERCLCBgSDE2X0EzNTc2YCBpcyBpbiBzYW1lIG9wZXJvbiBhcyB0aGlvc3VscGhhdGUgb3hpZGF0aW9uIHBhdGh3YXkgKFNveFhZWkFCQ0QpLCB3aGVyZSB0aGlvc3VsZmF0ZSBpcyB1c2VkIGFzIGVsZWN0cm9uIHNvdXJjZSBbWmhhbmcgZXQgYWwuLCAyMDIwXShodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTM5Ni0wMjAtMDY4NC01KQotICAgKip0ZXJtaW5hbCBjeXRvY2hyb21lIEMgb3hpZGFzZXMqKgogICAgLSAgIGNveE1OT1BRID0gYmIzIGNvbXBsZXgsIGFmZmluaXR5IHVua25vd24sIG5vdCBlc3NlbnRpYWwgZm9yIGdyb3d0aCBvciBOLWZpeGF0aW9uIGluIEIuIGphcG9ueWN1bSB3aGVyZSBpdCB3YXMgZGlzY292ZXJlZC4KICAgIC0gICBjdGFBQkNERUcgPSBhYTMgY29tcGxleCwgbG93IE8yIGFmZmluaXR5CiAgICAtICAgY2NvR05PUCA9IGNiYjMgY29tcGxleCwgaGlnaCBhZmZpbml0eSwgaGlnaCBzaW1pbGFyaXR5IHRvIE5PMyByZWR1Y3Rhc2UKICAgIC0gICBWUDI2NDEgKGBIMTZfQTEwMzFgKSA9IE1lbWJyYW5lIHNwYW5uaW5nIHByb3RlaW4sIFJERCBmYW1pbHkuIFBGQU06IHVua25vd24gZnVuY3Rpb24sIHBvdGVudGlhbCB0cmFuc3BvcnRlcj8gSW50ZXJhY3RzIHdpdGggY3l0b2Nocm9tZSBjNTUzIGFuZCBxY3JDLCBjeXRvY2hyb21lIHJlZHVjdGFzZS4gSXQncyBkZXBsZXRpb24gaW1wbGljYXRlcyB0aGlzIHByb3RlaW4gaW4gZm9ybWF0ZS1zcGVjaWZpYyBlLSB0cmFuc3BvcnQgaW4gdGhlIG1lbWJyYW5lLgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNH0KZGZfZXRjIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUgZmlsdGVyKHRpbWUgPT0gOCwgbG9jdXNfdGFnICVpbiUgYygiSDE2X0EzNDUxIiwgIkgxNl9BMDgzMCIsCiAgICAiSDE2X0EwODQ2IiwgIjE2X0IxNDUyIiwgIkgxNl9BMzU3NiIsICJIMTZfQTExMjEiLCAiSDE2X0ExMTIwIiwgIkgxNl9BMTAzMSIpIHwgCiAgICBncmVwbCgicWNyfGNjb3xjdGF8Y294W01OT1BRXSB8Y3lvfGN5ZCIsIGdlbmVfbmFtZSkpICU+JQogIG11dGF0ZShnZW5lX25hbWUgPSBzdHJpX2V4dHJhY3RfZmlyc3RfcmVnZXgoZ2VuZV9uYW1lLCAiSDE2X1tBQl1bMC05XSt8KHFjcnxjY298Y3RhfGNveHxjeW98Y3lkKShbQS1aMC05XSspPyIpICU+JQogICAgZmFjdG9yKGMoCiAgICAgIHBhc3RlMCgiY3lvIiwgYygiQTEiLCJBMiIsIkEzIiwiQjEiLCJCMiIsIkIzIiwiQzEiLCJDMiIsIkQxIiwiRDIiKSksCiAgICAgIHBhc3RlMCgiY3lkIiwgYygiQTEiLCJCMSIsIkIyIikpLCBwYXN0ZTAoInFjciIsIGMoIkEiLCJCIiwiQyIpKSwKICAgICAgcGFzdGUwKCJIMTZfIiwgYygiQTM1NzYiLCJBMDg0NiIsIkExMTIwIiwiQTExMjEiLCJBMTAzMSIsIkEzNDUxIikpLAogICAgICBwYXN0ZTAoImNveCIsIGMoIk0iLCJOIiwiTyIsIlAiLCJRIikpLCBwYXN0ZTAoImN0YSIsIGMoIkEiLCJCIiwiQyIsIkQiLCJFIiwiRyIpKSwKICAgICAgcGFzdGUwKCJjY28iLCBjKCJHIiwiTiIsIk8iLCJQIikpCiAgICApKQogICkKcGxvdF9ldGNfZml0IDwtIGhlYXRtYXBfZml0bmVzcyhkZl9ldGMpCgojIHBsb3QgZ2Vub21pYyBsb2NhdGlvbnMKZGZfZXRjIDwtIGRmX2V0YyAlPiUgZ3JvdXBfYnkobG9jdXNfdGFnLCBnZW5lX25hbWUsIHNjYWZmb2xkLCBzdHJhbmQsIHN0YXJ0LCBlbmQpICU+JQogIHN1bW1hcml6ZShzdHJhaW5zX3Blcl9nZW5lID0gbWluKHVuaXF1ZShzdHJhaW5zX3Blcl9nZW5lKSksIC5ncm91cHMgPSAiZHJvcCIpCnBsb3RfZXRjX2cxIDwtIGdlbm9tZV9wbG90KGRmX2V0YywgeGxpbSA9IGMoMzY3NS4yLCAzNjc5LjApLCB0aXRsZSA9ICJjaHIgMSIpCnBsb3RfZXRjX2cyIDwtIGdlbm9tZV9wbG90KGRmX2V0YywgeGxpbSA9IGMoMzYxLjIsIDM3MS4yKSwgdGl0bGUgPSAiY2hyIDEiKQpwbG90X2V0Y19nMyA8LSBnZW5vbWVfcGxvdChkZl9ldGMsIHhsaW0gPSBjKDI1MDkuNSwgMjUxNS4yKSwgdGl0bGUgPSAiY2hyIDEiKQoKcHJpbnQocGxvdF9ldGNfZml0LCBwb3NpdGlvbiA9IGMoMCwwLjE3LDEsMS4wNSksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2V0Y19nMSwgcG9zaXRpb24gPSBjKDAuMSwtMC4xLDAuMzEsMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZXRjX2cyLCBwb3NpdGlvbiA9IGMoMC4yOCwtMC4xLDAuNjUsMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZXRjX2czLCBwb3NpdGlvbiA9IGMoMC42MiwtMC4xLDAuOSwwLjQpKQpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CnN2ZyhmaWxlbmFtZSA9ICIuLi9maWd1cmVzL2ZpZ3VyZV9maXRfZXRjLnN2ZyIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDQpCnByaW50KHBsb3RfZXRjX2ZpdCwgcG9zaXRpb24gPSBjKDAsMC4xNywxLDEuMDUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9ldGNfZzEsIHBvc2l0aW9uID0gYygwLjEsLTAuMSwwLjMxLDAuNCksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2V0Y19nMiwgcG9zaXRpb24gPSBjKDAuMjgsLTAuMSwwLjY1LDAuNCksIG1vcmUgPSBUUlVFKQpwcmludChwbG90X2V0Y19nMywgcG9zaXRpb24gPSBjKDAuNjIsLTAuMSwwLjksMC40KSkKZGV2Lm9mZigpCmBgYAoKIyMgTml0cmF0ZSByZXNwaXJhdGlvbgoKKipOaXRyYXRlIHJlZHVjdGFzZSAoTkFSKSoqCgpSZWFjdGlvbjogSE5PMy0gKyBRSDIgLS1cPiBOTzItICsgSDJPICsgUS4KClR3byBjb3BpZXMgb2YgdGhlICpuYXIqIG9wZXJvbiwgb25lIG9uIHRoZSBtZWdhcGxhc21pZCBhbmQgb25lIG9uIGNocm9tb3NvbWUgMi4gU2V2ZXJhbCBvdGhlciBlbnp5bWVzIGNhdGFseXplIHNpbWlsYXIgcmVhY3Rpb25zIGFzIE5BUi4gTkFTIGlzIGN5dG9wbGFzbWljIGFuZCBOQURIIGRlcGVuZGVudCAqYXNzaW1pbGF0b3J5KiBuaXRyYXRlIHJlZHVjdGFzZSBbQ3JhbW0gUiwgMjAwOF0oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8xODk1Nzg2MS8pLiBOQVAgaXMgKnBlcmlwbGFzbWljKiBtZW1icmFuZSBwcm90ZWluIGNvbXBsZXggYWNjZXB0aW5nIFFIMiwgaW52b2x2ZWQgaW4gbml0cmF0ZSByZXNwaXJhdGlvbiBidXQgcHJvYmFibHkgYWxzbyBkaXNzaXBhdGlvbiBvZiBleGNlc3MgcmVkb3ggcG93ZXIuIEhlcmUgd2UganVzdCBmb2N1cyBvbiB0aGUgbWFpbiBOQVIgZW56eW1lLgoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpkZl9uYXIgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcih0aW1lID09IDgsIGdyZXBsKCJuYVtyc3BdIiwgZ2VuZV9uYW1lKSkgJT4lCiAgbXV0YXRlKGdlbmVfbmFtZSA9IHN0cmlfZXh0cmFjdF9maXJzdF9yZWdleChnZW5lX25hbWUsICJuYVtyc3BdKFtBLVowLTldKyk/IikgJT4lCiAgICByZWNvZGUobmFyRyA9ICJuYXJHMSIsIG5hckggPSAibmFySDEiLCBuYXJJID0gIm5hckkxIiwgbmFyTCA9ICJuYXJMMSIsIG5hclggPSAibmFyWDEiKSkKcGxvdF9uYXJfZml0IDwtIGhlYXRtYXBfZml0bmVzcyhkZl9uYXIpCgojIHBsb3QgZ2Vub21pYyBsb2NhdGlvbnMKZGZfbmFyX2dlbmVzIDwtIGRmX25hciAlPiUgZ3JvdXBfYnkobG9jdXNfdGFnLCBnZW5lX25hbWUsIHNjYWZmb2xkLCBzdHJhbmQsIHN0YXJ0LCBlbmQpICU+JQogIHN1bW1hcml6ZShzdHJhaW5zX3Blcl9nZW5lID0gbWluKHVuaXF1ZShzdHJhaW5zX3Blcl9nZW5lKSksIC5ncm91cHMgPSAiZHJvcCIpCnBsb3RfbmFyX2cxIDwtIGdlbm9tZV9wbG90KGRmX25hcl9nZW5lcywgeGxpbSA9IGMoMjc5LjUsIDI5NC41KSwgdGl0bGUgPSAicEhHMSIpCgpwcmludChwbG90X25hcl9maXQsIHBvc2l0aW9uID0gYygwLDAuMjYsMSwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbmFyX2cxLCBwb3NpdGlvbiA9IGMoMC4yLC0wLjEsMC44NSwwLjQpKQpgYGAKCioqTml0cml0ZSByZWR1Y3Rhc2UgKE5JUikqKgoKUmVhY3Rpb246IE5PMi0gKyBRSDIgLS1cPiBOTyArIEgyTyArIFEKCk5JUiBpcyBhIGNkMSB0eXBlIGNvcHBlciBjb250YWluaW5nIHRlcm1pbmFsIG94aWRhc2UsIGhvbW9kaW1lciBvZiAyIG5pclMgc3VidW5pdHMuIFRoZSBnZW5lcyBmb3Igbml0cml0ZSByZWR1Y3Rhc2UgYXJlIGFzc2VtYmxlZCBpbiBhIGNsdXN0ZXIgKG5pclMsIC1DLCAtRiwgLUQsIC1HLCAtSCwgLUosIC1OLCAtRSkgc3Bhbm5pbmcgOC42IGtiIG9uIGNocm9tb3NvbWUgMi4gVGhlIGZpcnN0IGdlbmUgb2YgdGhpcyBjbHVzdGVyLCBuaXJTLCBpcyB0aGUgc3RydWN0dXJhbCBnZW5lIGVuY29kaW5nIGNkMSAtTklSLiBUaGUgcmUtbWFpbmluZyBnZW5lcyBhcmUgcHJlc3VtZWQgdG8gYmUgYWNjZXNzb3J5IGdlbmVzIGVzc2VudGlhbCBmb3IgbWF0dXJhdGlvbiBvZiBhY3RpdmUgbml0cml0ZSByZWR1Y3Rhc2UgKD8pLgoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpkZl9uaXIgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcih0aW1lID09IDgsIGdyZXBsKCJuaXIiLCBnZW5lX25hbWUpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyaV9leHRyYWN0X2ZpcnN0X3JlZ2V4KGdlbmVfbmFtZSwgIm5pcihbQS1aMC05XSspPyIpKQpwbG90X25pcl9maXQgPC0gaGVhdG1hcF9maXRuZXNzKGRmX25pcikKCiMgcGxvdCBnZW5vbWljIGxvY2F0aW9ucwpkZl9uaXJfZ2VuZXMgPC0gZGZfbmlyICU+JSBncm91cF9ieShsb2N1c190YWcsIGdlbmVfbmFtZSwgc2NhZmZvbGQsIHN0cmFuZCwgc3RhcnQsIGVuZCkgJT4lCiAgc3VtbWFyaXplKHN0cmFpbnNfcGVyX2dlbmUgPSBtaW4odW5pcXVlKHN0cmFpbnNfcGVyX2dlbmUpKSwgLmdyb3VwcyA9ICJkcm9wIikKcGxvdF9uaXJfZzEgPC0gZ2Vub21lX3Bsb3QoZGZfbmlyX2dlbmVzLCB4bGltID0gYygyNTc2LjMsIDI1ODUuOSksIHRpdGxlID0gImNociAyIikKCnByaW50KHBsb3RfbmlyX2ZpdCwgcG9zaXRpb24gPSBjKDAsMC4yNiwxLDEuMDUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9uaXJfZzEsIHBvc2l0aW9uID0gYygwLjMsLTAuMSwwLjc1LDAuNCkpCmBgYAoKKipOTyByZWR1Y3Rhc2UgKE5PUikqKgoKUmVhY3Rpb246IE5PICsgUUgyIC0tXD4gTjJPICsgSDJPICsgUQoKRnJvbSBbQ3JhbW0gUiwgMjAwOF0oaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8xODk1Nzg2MS8pOiAiVGhlIGNhdGFseXRpYyBzdWItdW5pdCAoTm9yQikgb2YgYWxsIE5PUnMgdGhhdCBoYXZlIGJlZW4gY2hhcmFjdGVyaXplZCB0byBkYXRlIGNvbnRhaW5zIG9uZSBoaWdoLXNwaW4gaGVtZSBiIGFuZCBhIGJpbnVjbGVhciBjYXRhbHl0aWMgY2VudGVyIG9mIGEgbG93LXNwaW4gaGVtZSBiIGFuZCBhIG5vbi1oZW1lIGlyb24uIFRoZSBiZXN0IGludmVzdGlnYXRlZCBOT1JzIGFyZSBoZXRlcm9kaW1lcnMgdGhhdCBjb250YWluLCBpbiBhZGRpdGlvbiB0byBOb3JCLCBhIGhlbWUgYy1jb250YWluaW5nIHN1YnVuaXQgTm9yQyB3aGljaCBjaGFubmVscyBsZWN0cm9ucyBmcm9tIHRoZSBwaHlzaW9sb2dpY2FsIGVsZWN0cm9uIGRvbm9yIGN5dG9jaHJvbWUgYyB0byBOb3JCLiIKCmBgYHtyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KZGZfbm9yIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBmaWx0ZXIodGltZSA9PSA4LCBncmVwbCgibm9yIiwgZ2VuZV9uYW1lKSkgJT4lCiAgbXV0YXRlKGdlbmVfbmFtZSA9IHN0cmlfZXh0cmFjdF9maXJzdF9yZWdleChnZW5lX25hbWUsICJub3IoW0EtWjAtOV0rKT8iKSkKcGxvdF9ub3JfZml0IDwtIGhlYXRtYXBfZml0bmVzcyhkZl9ub3IpCgojIHBsb3QgZ2Vub21pYyBsb2NhdGlvbnMKZGZfbm9yX2dlbmVzIDwtIGRmX25vciAlPiUgZ3JvdXBfYnkobG9jdXNfdGFnLCBnZW5lX25hbWUsIHNjYWZmb2xkLCBzdHJhbmQsIHN0YXJ0LCBlbmQpICU+JQogIHN1bW1hcml6ZShzdHJhaW5zX3Blcl9nZW5lID0gbWluKHVuaXF1ZShzdHJhaW5zX3Blcl9nZW5lKSksIC5ncm91cHMgPSAiZHJvcCIpCnBsb3Rfbm9yX2cxIDwtIGdlbm9tZV9wbG90KGRmX25vcl9nZW5lcywgeGxpbSA9IGMoMjU2LjQsIDI2Mi4xKSwgdGl0bGUgPSAicEhHMSIpCgpwcmludChwbG90X25vcl9maXQsIHBvc2l0aW9uID0gYygwLDAuMjUsMSwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3Rfbm9yX2cxLCBwb3NpdGlvbiA9IGMoMC4zLC0wLjEsMC43NSwwLjQpKQpgYGAKCioqTk9TIChuaXRyb3VzIG94aWRlKSByZWR1Y3Rhc2UqKgoKUmVhY3Rpb246IE4yTyArIFFIMiAtLVw+IE4yICsgSDJPICsgUQoKVGhlIE5PUyBvZiBSLiBldXRyb3BoYSBIMTYgaGFzIG5vdCBiZWVuIGludmVzdGlnYXRlZCBvbiB0aGUgYmlvY2hlbWljYWwgbGV2ZWwuIEdlbmVzIGZvciBOT1MgYXJlIGxvY2F0ZWQgYWRqYWNlbnQgdG8gdGhlIG5vciBnZW5lcyBvbiBwSEcxLiBOb3NDIChQSEcyNTMpIG1pZ2h0IGJlIHRoZSBjeXQgYyB0aGF0IGlzIHRoZSBlLSBkb25vciBmb3IgTk9TLiBUaGUgbm9zQyBLTyBpcyBlbnJpY2hlZCBvbiBmb3JtYXRlLCBpLmUuIGdyb3dzIGJldHRlciB0aGFuIFdULiBJcyBub3NDIHRha2luZyBlLSBhd2F5IGZyb20gZm9ybWF0ZS1mZWQgRVRDPwoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQpkZl9ub3MgPC0gZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcih0aW1lID09IDgsIGdyZXBsKCJub3MiLCBnZW5lX25hbWUpKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gc3RyaV9leHRyYWN0X2ZpcnN0X3JlZ2V4KGdlbmVfbmFtZSwgIm5vcyhbQS1aMC05XSspPyIpKQpwbG90X25vc19maXQgPC0gaGVhdG1hcF9maXRuZXNzKGRmX25vcykKCiMgcGxvdCBnZW5vbWljIGxvY2F0aW9ucwpkZl9ub3NfZ2VuZXMgPC0gZGZfbm9zICU+JSBncm91cF9ieShsb2N1c190YWcsIGdlbmVfbmFtZSwgc2NhZmZvbGQsIHN0cmFuZCwgc3RhcnQsIGVuZCkgJT4lCiAgc3VtbWFyaXplKHN0cmFpbnNfcGVyX2dlbmUgPSBtaW4odW5pcXVlKHN0cmFpbnNfcGVyX2dlbmUpKSwgLmdyb3VwcyA9ICJkcm9wIikKcGxvdF9ub3NfZzEgPC0gZ2Vub21lX3Bsb3QoZGZfbm9zX2dlbmVzLCB4bGltID0gYygyNjEuMSwgMjcxLjkpLCB0aXRsZSA9ICJwSEcxIikKCnByaW50KHBsb3Rfbm9zX2ZpdCwgcG9zaXRpb24gPSBjKDAsMC4yNSwxLDEuMDUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9ub3NfZzEsIHBvc2l0aW9uID0gYygwLjIyLC0wLjEsMC44NSwwLjQpKQpgYGAKClRoZSBmb2xsb3dpbmcgc2VjdGlvbiBzaW1wbHkgcGxvdHMgYWxsIG5pdHJhdGUgcmVzcGlyYXRpb24gbW9kdWxlcyBvbiBvbmUgY2FudmFzLgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNH0KcGxvdF9uaXRyYXRlX2FsbCA8LSBiaW5kX3Jvd3MoZGZfbmFyLCBkZl9ub3IsIGRmX25vcywgZGZfbmlyKSAlPiUKICBtdXRhdGUoZ2VuZV9uYW1lID0gZmFjdG9yKGdlbmVfbmFtZSwgYygKICAgIHBhc3RlMCgibmFyIiwgYygiWDEiLCAiTDEiLCAiSzEiLCAiSzIiLCAiRzEiLCAiSDEiLCAiSTEiLCAiWDIiLCAiTDIiLCAiRzIiLCAiSDIiLCAiSTIiLCAiSzMiLCAiSzUiKSksCiAgICBwYXN0ZTAoIm5hcCIsIGMoIkEiLCAiQiIsICJDIiwgIkQiKSksIHBhc3RlMCgibmFzIiwgYygiRCIsICJFIikpLAogICAgcGFzdGUwKCJuaXIiLCBjKCJTIiwgIkMiLCAiRiIsICJEIiwgIkoiLCAiTiIsICJFIikpLAogICAgcGFzdGUwKCJub3IiLCBjKCJBIiwgIkIiLCAiUjEiLCAiQTIiLCAiQjIiLCAiUjIiKSksCiAgICBwYXN0ZTAoIm5vcyIsIGMoIkwiLCAiWSIsICJGIiwgIkQiLCAiUiIsICJaIiwgIkMiLCAiWCIpKQogICkpKSAlPiUKICBoZWF0bWFwX2ZpdG5lc3MoKQoKcHJpbnQocGxvdF9uaXRyYXRlX2FsbCwgcG9zaXRpb24gPSBjKDAsMC4yMywxLDEuMDUpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9uYXJfZzEsIHBvc2l0aW9uID0gYygwLC0wLjEsMC4zNSwwLjQpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9uaXJfZzEsIHBvc2l0aW9uID0gYygwLjMzLC0wLjEsMC41OCwwLjQpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9ub3JfZzEsIHBvc2l0aW9uID0gYygwLjU2LC0wLjEsMC43MiwwLjQpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9ub3NfZzEsIHBvc2l0aW9uID0gYygwLjcwLC0wLjEsMC45NiwwLjQpLCBtb3JlID0gVFJVRSkKYGBgCgpgYGB7ciwgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gJ2hpZGUnfQpzdmcoZmlsZW5hbWUgPSAiLi4vZmlndXJlcy9maWd1cmVfbml0cmF0ZV9yZXNwLnN2ZyIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDQpCnByaW50KHBsb3Rfbml0cmF0ZV9hbGwsIHBvc2l0aW9uID0gYygwLDAuMjMsMSwxLjA1KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbmFyX2cxLCBwb3NpdGlvbiA9IGMoMCwtMC4xLDAuMzUsMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbmlyX2cxLCBwb3NpdGlvbiA9IGMoMC4zMywtMC4xLDAuNTgsMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3Rfbm9yX2cxLCBwb3NpdGlvbiA9IGMoMC41NiwtMC4xLDAuNzIsMC40KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3Rfbm9zX2cxLCBwb3NpdGlvbiA9IGMoMC43MCwtMC4xLDAuOTYsMC40KSwgbW9yZSA9IFRSVUUpCmRldi5vZmYoKQpgYGAKCiMjIENvbXBhcmlzb24gb2Ygbml0cmF0ZSByZXNwaXJhdGlvbiBjb25kaXRpb25zCgotICAgd2UgaGF2ZSB0d28gZGlmZmVyZW50IG5pdHJhdGUgcmVzcGlyYXRpb24gY29uZGl0aW9ucywgY29udGludW91cyBhbmQgcHVsc2VkCi0gICBsb3cgZGVwbGV0aW9uIG9mIG5pdHJhdGUvbml0cml0ZS9uaXRyb3VzIHJlc3BpcmF0aW9uIG11dGFudHMgcHJvYmFibHkgZHVlIHRvIGNvbnN0YW50IE5PMyBzdXBwbHkKCmBgYHtyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0KZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcihzdWJzdHJhdGUgPT0gIm5pdHJhdGUiLCB0aW1lID09IDgsICFpcy5uYShnZW5lX25hbWUpKSAlPiUKICBzZWxlY3QoZ2VuZV9uYW1lLCBjb25kaXRpb24sIG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbikgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGNvbmRpdGlvbiwgdmFsdWVzX2Zyb20gPSBub3JtX2dlbmVfZml0bmVzc19tZWRpYW4pICU+JQogIG11dGF0ZSgKICAgIGRmaXQgPSBwdWxzZSAtIGNvbnRpbnVvdXMsCiAgICBzaWduaWZpY2FudCA9ICFiZXR3ZWVuKGRmaXQsIHF1YW50aWxlKGRmaXQsIHByb2JzID0gYygwLjAwMyksIG5hLnJtID0gVFJVRSksCiAgICAgIHF1YW50aWxlKGRmaXQsIHByb2JzID0gYygwLjk5NyksIG5hLnJtID0gVFJVRSkpKSAlPiUKICAKICAjIHBsb3QKICB4eXBsb3QoY29udGludW91cyB+IHB1bHNlLCBkYXRhID0gLiwgZ3JvdXBzID0gc2lnbmlmaWNhbnQsCiAgICBsYWJlbHMgPSAuW1siZ2VuZV9uYW1lIl1dLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgY29sID0gY3VzdG9tX2NvbG9ycywKICAgIHhsaW0gPSBjKC05LCAzKSwgeWxpbSA9IGMoLTksIDMpLAogICAgeGxhYiA9ICJOaXRyYXRlIHJlc3BpcmF0aW9uIChQKSIsIHlsYWIgPSAiTml0cmF0ZSByZXNwaXJhdGlvbiAoQykiLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCBsYWJlbHMsIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC5hYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSBncmV5KDAuNSksIGx0eSA9IDIsIHNpemUgPSAwLjgpCiAgICAgIHBhbmVsLmFibGluZShhID0gNCwgYiA9IDEsIGNvbCA9IGdyZXkoMC41KSwgbHR5ID0gMiwgc2l6ZSA9IDAuOCkKICAgICAgcGFuZWwuYWJsaW5lKGEgPSAtNCwgYiA9IDEsIGNvbCA9IGdyZXkoMC41KSwgbHR5ID0gMiwgc2l6ZSA9IDAuOCkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIC4uLikKICAgICAgcGFuZWwucmVwZWxsYWJlbHMoeCwgeSwgbGFiZWxzLCBjZXggPSAwLjYsCiAgICAgICAgc2VsZWN0ZWQgPSAuW1sic2lnbmlmaWNhbnQiXV0sCiAgICAgICAgZHJhd19ib3ggPSBUUlVFLCBib3hfZmlsbCA9ICJ3aGl0ZSIsIGJveF9saW5lID0gVFJVRSwgLi4uKQogICAgfQogICkKYGBgCgojIyBOb24taHlkcm9nZW5hc2UgZ2VuZXMgaW1wb3J0YW50IGZvciBIMiBmaXRuZXNzCgpEaWZmZXJlbnQgZ2VuZSBjbHVzdGVycyBhcHBlYXIgYmFzZWQgb24gdGhlIGZpdG5lc3MgcGF0dGVybiBpbiBkaWZmZXJlbnQgY29uZGl0aW9ucy4gQ2x1c3RlcnMgc3BlY2lmaWMgZm9yIGxpdGhvYXV0b3Ryb3BoaWMgbWV0YWJvbGlzbSBhcmUgcGFydGljdWxhcmx5IGludGVyZXN0aW5nLiBXZSBjYW4gYWRkIHRoZSBjbHVzdGVyIElEcyB0byB0aGUgbWFpbiBkYXRhIGZyYW1lLCBhbmQgaW52ZXN0aWdhdGUgZml0bmVzcy4gT3Igc2ltcGx5IHNlbGVjdCBnZW5lcyB0aGF0IHNob3cgZGVwbGV0aW9uIG9ubHkgZm9yIHRoZSBoeWRyb2dlbiBtZXRhYm9saXppbmcgY29uZGl0aW9uLgoKYGBge3J9CmRmX2ZpdG5lc3Nfc3VtbWFyeSA8LSBkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgbGVmdF9qb2luKGJ5ID0gImxvY3VzX3RhZyIsCiAgICBlbmZyYW1lKGN1dHJlZW9yZChtYXRfY2x1c3RlciwgayA9IG5fY2x1c3RlcnMpLCAibG9jdXNfdGFnIiwgImNsdXN0ZXIiKQogICkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDQuNX0KZGZfZml0bmVzc19zdW1tYXJ5ICU+JQogIGZpbHRlcih0aW1lID09IDgsICFzdHJfZGV0ZWN0KGdlbmVfbmFtZSwgImhveHxoeXAiKSkgJT4lCiAgbXV0YXRlKGh5ZF9lc3NlbnRpYWwgPSBjYXNlX3doZW4oCiAgICBzdWJzdHJhdGUgPT0gImh5ZHJvZ2VuIiAmIG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiA8PSAtMiB+IFRSVUUsCiAgICBzdWJzdHJhdGUgIT0gImh5ZHJvZ2VuIiAmIG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiA+PSAtMiB+IFRSVUUsCiAgICBUUlVFIH4gRkFMU0UKICApKSAlPiUgCiAgZ3JvdXBfYnkobG9jdXNfdGFnKSAlPiUKICBmaWx0ZXIoc3VtKGh5ZF9lc3NlbnRpYWwpID09IDkgfAogICAgbG9jdXNfdGFnICVpbiUgYygiSDE2X0EwMzI1IiwgIkgxNl9BMDMyNiIpKSAlPiUKICB1bmdyb3VwICU+JSBhcnJhbmdlKGxvY3VzX3RhZykgJT4lCiAgbXV0YXRlKGdlbmVfbmFtZSA9IGZhY3RvcihnZW5lX25hbWUsIHVuaXF1ZShnZW5lX25hbWUpKSkgJT4lCiAgbXV0YXRlKG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiA9IG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiAlPiUKICAgIHJlcGxhY2UoLiwgLiA8IC02LCAtNikgJT4lIHJlcGxhY2UoLiwgLiA+IDYsIDYpKSAlPiUKICBoZWF0bWFwX2ZpdG5lc3MKYGBgCgotICAgKipwdHNJICsgcHRzSCoqIC0gYWNjb3JkaW5nIHRvIGRhdGFiYXNlcywgYSBjbGVhciBoaXQgZm9yIGEgUEVQLWRlcGVuZGVudCBzdWdhciBQVFMgc3lzdGVtLiBUaGlzIGNhbid0IGJlIHRydWUgZm9yICpDLiBuZWNhdG9yKiBvdGhlcndpc2UgaXQgd291bGQgbm90IHNob3cgdGhlIGZhbWlsaWFyIHBhdHRlcm4gb2YgZGVwbGV0aW9uIG9uIGh5ZHJvZ2VuIGFuZCBncm93dGggYmVuZWZpdCBvbiBvdGhlciBzdWJzdHJhdGVzLiBUaGlzIFBUUyBzeXN0ZW0gbXVzdCBiZSBpbXBsaWNhdGVkIGluIGh5ZHJvZ2VuIG1ldGFib2xpc20sIHByb2JhYmx5IGEgdHJhbnNwb3J0ZXI/IFRoZSBnZW5lcyBkb3duc3RyZWFtIG9mIHB0c0lIIGRvIG5vdCBzZWVtIHRvIGJlIGFmZmVjdGVkIChIMTZfQTAzMjcgdG8gSDE2X0EwMzI5KS4KLSAgICoqaGxkRCoqIChIMTZfQTA4MDQpIC0gQURQLUwtZ2x5Y2Vyby1iZXRhLUQtbWFubm8taGVwdG9zZS02LWVwaW1lcmFzZSwgaW52b2x2ZWQgaW4gTFBTIHN1Z2FyIHBob3NwaGF0ZSBzeW50aGVzaXMuIE5vIG9idmlvdXMgY29ubmVjdGlvbiB0byBoeWRyb2dlbiBtZXRhYm9saXNtLgotICAgKipnbG5FKiogKEgxNl9BMTEyNykgLSBCaWZ1bmN0aW9uYWwgZ2x1dGFtaW5lIHN5bnRoZXRhc2UgYWRlbnlseWx0cmFuc2ZlcmFzZS4gSW52b2x2ZWQgaW4gdGhlIHJlZ3VsYXRpb24gb2YgZ2x1dGFtaW5lIHN5bnRoZXRhc2UgR2xuQS4gVHJhbnNmZXJzIG9yIHJlbW92ZXMgYWRlbnlseWwtcmVzaWR1ZXMgdG8gYW5kIGZyb20gR2xuQSwgdGhlcmVieSByZWd1bGF0aW5nIE4gdXB0YWtlIHRocm91Z2ggR2xuQS4gTm8gb2J2aW91cyBjb25uZWN0aW9uIHRvIGh5ZHJvZ2VuIG1ldGFib2xpc20uIE9ubHkgMSBtdXRhbnQgaW4gbGlicmFyeS4KLSAgICoqSDE2X0EyNjkyKiogLSB1bmNoYXJhY3Rlcml6ZWQgbWVtYnJhbmUgcHJvdGVpbi4gSXMgbmV1dHJhbCBpbiBhbGwgb3RoZXIgY29uZGl0aW9ucywganVzdCBhcyBhcmUgYWxsIHN1cnJvdW5kaW5nIGdlbmVzIChIMTZfQTI2OVswLTldKS4gU21hbGwsIDE0MSBBQSwgcG90ZW50aWFsIGNhcmJvbmljIGFuaHlkcmFzZSBvciB0cmFuc3BvcnRlcj8KLSAgICoqYm9sQSoqIChIMTZfQTM0MTkpIC0gUHJlZGljdGVkIHRyYW5zY3JpcHRpb25hbCByZWd1bGF0b3IsIG5vIGtub3duIGRvbWFpbnMgb3IgcHVibGlzaGVkIGZ1bmN0aW9ucy4gT25seSAxIG11dGFudCBpbiBsaWJyYXJ5LgotICAgKip5YWRHKiogKEgxNl9BMzQyMSkgU2VwYXJhdGVkIGJ5IG9ubHkgMSBnZW5lICh5YWRILCBIMTZfQTM0MjApIGZyb20gdGhlIHByZXZpb3VzIGhpdCwgc2hvd3MgdmVyeSBzcGVjaWZpYyBkZXBsZXRpb24gZm9yIGh5ZHJvZ2VuLiA3LTggbXV0YW50cyBxdWFudGlmaWVkLCBhbm5vdGF0ZWQgYXMgQUJDIHRyYW5zcG9ydGVyLCBBVFBhc2UgZnVuY3Rpb24uIFlhZEggaXMgdGhlIHBlcm1lYXNlIHBhcnRuZXIgb2YgdGhlIDIgc3VidW5pdHMsIGhvd2V2ZXIgZG9lcyBub3Qgc2hvdyBkZXBsZXRpb24uIENPRyBwcm9jZXNzICdkZWZlbnNlJyBiZWNhdXNlIGl0IHN1cHBvc2VkbHkgaXMgYWN0aXZlIGFzIGFudGktZHJ1ZyBlZmZsdXggcHVtcC4KLSAgICoqSDE2X0IxNjAzKiogLSBBQkMtdHlwZSB0cmFuc3BvcnRlciwgQVRQYXNlIGNvdXBsZWQgdHJhbnNtZW1icmFuZSB0cmFuc3BvcnRlci4gU3Ryb25nIGRlcGxldGlvbiBvbiBoeWRyb2dlbiBvbmx5LiBBbGwgc3Vycm91bmRpbmcgZ2VuZXMgYXJlIG5ldXRyYWwgdG8gZml0bmVzcy4gT25seSAxIG11dGFudCBpbiBsaWJyYXJ5LiBBIGNhbmRpZGF0ZSBmb3IgYSBDTzIvSENPMy0gdHJhbnNwb3J0ZXI/IEFsaWducyBiZXN0IHdpdGggYSBsb3Qgb2YgZGlmZmVyZW50IEFCQyB0cmFuc3BvcnRlcnMsIHdpdGggc3Blcm1pZGluZS9wdXRyZXNjaW5lIHRyYW5zcG9ydGVycyBiZXN0IGhpdHMuCgojIyBDTzIgdHJhbnNwb3J0ZXIvY2FyYm9uaWMgYW5oeWRyYXNlIGhvbW9sb2dzCgpBbGwgQ0FzIHdpdGggcXVhbnRpZmllZCBmaXRuZXNzIGFyZSBuZXV0cmFsIGR1cmluZyBsaXRob2F1dG90cm9waGljIGdyb3d0aCAoSDE2X0ExMTkyIC0gKmNhZyosIEgxNl9CMjQwMyAtICpjYWEqLCBIMTZfQjIyNzAgLSAqY2FuMiopLiBPbmx5IEgxNl9BMDE2OSAoKmNhbiopIGlzIHN0cmljdGx5IGVzc2VudGlhbCwgbm8gbXV0YW50cyBhcmUgYXZhaWxhYmxlLiBUaGlzIGlzIGRpZmZlcmVudCB0byB0aGUgcmVzdWx0cyBmcm9tIFtHYWkgZXQgYWwuLCBBTUIgRXhwcmVzcywgMjAxNF0oaHR0cHM6Ly9saW5rLnNwcmluZ2VyLmNvbS9hcnRpY2xlLzEwLjExODYvMjE5MS0wODU1LTQtMikgdGhhdCBmb3VuZCB0d28gZXNzZW50aWFsIENBcywgKmNhbiogYW5kICpjYWEqOgoKIkFsbCBmb3VyIHB1cmlmaWVkIENBcyB3ZXJlIGNhcGFibGUgb2YgcGVyZm9ybWluZyB0aGUgaW50ZXJjb252ZXJzaW9uIG9mIENPMiBhbmQgSENPMy0tIFsuLi5dIERlbGV0aW9uIG9mICpjYW4qIGFmZmVjdGVkIHRoZSBncm93dGggb2YgUi4gZXV0cm9waGE7IGhvd2V2ZXIgdGhlIGdyb3d0aCBkZWZlY3QgY291bGQgYmUgY29tcGVuc2F0ZWQgYnkgYWRkaW5nIENPMiB0byB0aGUgY3VsdHVyZS4gRGVsZXRpb24gb2YgKmNhYSosIGVuY29kaW5nIGFuIGFscGhhLUNBLCBoYWQgdGhlIHN0cm9uZ2VzdCBkZWxldGVyaW91cyBpbmZsdWVuY2Ugb24gY2VsbCBncm93dGguIi4KCkFsc28gW0t1c2lhbiBldCBhbC4sIEogQmFjLCAyMDAyXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUMxMzUzMTQvKSByZXBvcnQgdGhlIGVzc2VudGlhbGl0eSBvZiAqY2FuKiBmb3IgbGl0aG9hdXRvdHJvcGhpYyBncm93dGguICpDYWEqIGZpdG5lc3Mgd2FzIHF1YW50aWZpZWQgaW4gdGhlIGxpYnJhcnkgd2l0aCAxMSBtdXRhbnRzLCB5ZXQgZG9lcyBub3Qgc2hvdyBhbnkgcGhlbm90eXBlIG9uIGFueSBzdWJzdHJhdGUuIFRoZSByZXN1bHRzIGluIEdhaSBldCBhbC4gYXJlIHNvbWV3aGF0IGNvbnRyYWRpY3RvcnksIGFzIHRoZSAqY2FhKiBjb21wbGV0aW9uIHRlc3QgaW4gYSBxdWFkcnVwbGUtQ0EgbXV0YW50IGhhZCBubyBiZW5lZmljaWFsIGVmZmVjdCBvbiBncm93dGguCgpBbHRvZ2V0aGVyLCBubyBzcGVjaWZpYyBiaWNhcmJvbmF0ZSB0cmFuc3BvcnRlcnMgYXJlIGFubm90YXRlZCBpbiAqQy4gbmVjYXRvciouIEhvd2V2ZXIsIGltcG9ydGFudCBiaWNhcmJvbmF0ZSAoSENPMyspIG9yIENPMiB0cmFuc3BvcnRlcnMga25vd24gZnJvbSBlLmcuIGN5YW5vYmFjdGVyaWEgKCpTeW5lY2hvY3lzdGlzKiBzcC4gUENDIDY4MDMpIGNvdWxkIGJlIHVzZWQgdG8gc2VhcmNoIGZvciBob21vbG9nb3VzIGdlbmVzIGluICpDLiBuZWNhdG9yKi4gVGhlIGZvbGxvd2luZyBDTzIvSENPMysgdHJhbnNwb3J0ZXJzIGFyZSBrbm93biBpbiAqU3luZWNob2N5c3Rpcyo6CgotICAgKipCaWNBKiogKHNsbDA4MzQpOiBjb25zdGl0dXRpdmUgaGlnaC1mbHV4LCBsb3ctYWZmaW5pdHkgTmErL0hDTzMrIHN5bXBvcnQKLSAgICoqU2J0QSoqIChzbHIxNTEyKTogaW5kdWNpYmxlIGxvdy1mbHV4LCBoaWdoLWFmZmluaXR5IE5hKy9IQ08zKyBzeW1wb3J0Ci0gICAqKkJDVDEqKiAoY21wQUJDRCBvcGVyb24vc2xyMDA0MC00NCk6IGluZHVjaWJsZSBoaWdoLWFmZmluaXR5LCBBVFAtZGVwZW5kZW50IHVuaXBvcnQKLSAgICoqTkRIKiogKG51byBvcGVyb24vKTogYWxsIGdlbmVzIG9mIHRoaXMgb3Blcm9uIGFyZSBlc3NlbnRpYWwgaW4gKkMuIG5lY2F0b3IqLCBubyBjb25kLiBmaXRuZXNzCgpUaGUgZmlyc3QgdGhyZWUgb2YgdGhlc2UgZ2VuZXMgd2VyZSBibGFzdGVkIGFnYWluc3QgdGhlIGdlbm9tZSBvZiAqQy4gbmVjYXRvciouIFRoZSBmaWd1cmUgc2hvd3MgYWxpZ25tZW50IHNjb3JlcyBmb3IgdGhlIHRvcCAyMCBoaXRzLgoKYGBge3IsIGZpZy53aWR0aCA9IDYuNSwgZmlnLmhlaWdodCA9IDN9CmRmX2NvMl90cmFuc3BvcnRfYWxpZ24gPC0gcmVhZF90c3YoIi4uL2RhdGEvYWxpZ25tZW50cy9DTzJfdHJhbnNwb3J0ZXJzLnRzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lCiAgZ3JvdXBfYnkodGFyZ2V0X25hbWUpICU+JQogIG11dGF0ZShyYW5rID0gc2VxX2Fsb25nKHVuaXByb3QpKQogIAogIHh5cGxvdChzY29yZSArIC1sb2cxMChlX3ZhbHVlKSB+IHJhbmsgfCB0YXJnZXRfbmFtZSwgZGZfY28yX3RyYW5zcG9ydF9hbGlnbiwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksIHhsaW0gPSBjKC0xLCAyMSksCiAgICB4bGFiID0gInJhbmsiLCB5bGFiID0gZXhwcmVzc2lvbigiLWxvZyJbMTBdKiIgZSB2YWx1ZSwgc2NvcmUiKSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLCBsd2QgPSAwLAogICAgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgeiwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwuYmFycGxvdCh4LCB5LCBld2lkdGggPSAwLjUsIGZpbGxfYWxwaGEgPSAwLjcsIC4uLikKICAgICAgcGFuZWwua2V5KC4uLiwgcG9pbnRzID0gRkFMU0UsIGNvcm5lciA9IGMoMC45NywgMC45NykpCiAgICB9CiAgKQpgYGAKCkJpY0EgYW5kIGNtcEIgc2VlbSB0byBoYXZlIHNvbWUgdmVyeSBzaW1pbGFyIGhvbW9sb2dzIGluIHRoZSAqQy4gbmVjYXRvciogZ2Vub21lLiBIb3dldmVyLCBub25lIG9mIHRoZXNlIGhvbW9sb2dzIGhhcyBhIGNsZWFyIHBoZW5vdHlwZSByZWxhdGVkIHRvIGh5ZHJvZ2VuIG1ldGFib2xpc20uCgpgYGB7ciwgZmlnLndpZHRoID0gOS41LCBmaWcuaGVpZ2h0ID0gMy41fQpkZl9jbzJfdHJhbnNwb3J0X2FsaWduICU+JQogIGZpbHRlcihyYW5rIDw9IDIwKSAlPiUgCiAgc2VsZWN0KHVuaXByb3QsIHRhcmdldF9uYW1lLCByYW5rKSAlPiUKICBpbm5lcl9qb2luKGRmX2ZpdG5lc3Nfc3VtbWFyeSwgYnkgPSAidW5pcHJvdCIpICU+JSB1bmdyb3VwICU+JQogIG11dGF0ZShub3JtX2dlbmVfZml0bmVzc19tZWRpYW4gPSBub3JtX2dlbmVfZml0bmVzc19tZWRpYW4gJT4lCiAgICByZXBsYWNlKC4sIC4gPCAtNiwgLTYpICU+JSByZXBsYWNlKC4sIC4gPiA2LCA2KSkgJT4lCiAgaGVhdG1hcF9maXRuZXNzCmBgYAoKIyMgTWFpbiBjb25jbHVzaW9ucwoKLSAgIE1vQ28gY29mYWN0b3IgaXMgaW1wb3J0YW50IGZvciBncm93dGggb24gZm9ybWF0ZSAqYW5kKiBuaXRyYXRlIHJlc3BpcmF0aW9uLCBidXQgbm90IGZvciBncm93dGggb24gaHlkcm9nZW4KLSAgIE1haW4gRkRIIGlzIFMtRkRIIGJvdGggaW4gdGVybXMgb2YgcHJvdGVpbiBhYnVuZGFuY2UsIGluZHVjaWJpbGl0eSwgYW5kIGVzc2VudGlhbGl0eQotICAgQWx0ZXJuYXRpdmUgUy1GREggb3Blcm9uICpmZHcqIGRvZXMgbm90IHBsYXkgYSByb2xsCi0gICBCb3RoIGh5ZHJvZ2VuYXNlcywgUy1IIGFuZCBtX0ggaGF2ZSBzaW1pbGFyIGZpdG5lc3MgcGVuYWx0eSBvbiBoeWRyb2dlbiB3aGVuIEtPZWQKLSAgIEh5ZHJvZ2VuYXNlIGdlbmVzLCBidXQgZXZlbiBtb3JlIHNvIG1hc3RlciByZWd1bGF0b3IgaG94QSBhbmQgSHlkcm9nZW5hc2UgcGxlaW90cm9weSAoaHlwKSBnZW5lcyBjb25mZXIgZml0bmVzcyBhZHZhbnRhZ2Ugd2hlbiBLT2VkIG9uIGFsbCBub24tbG9pdGhvYXV0b3Ryb3BoaWMgc3Vic3RyYXRlcwotICAgQ3l0b2Nocm9tZSByZWR1Y3Rhc2UgYmMxIGNvbXBsZXggKHFjckFCQykgaXMgcGFydGljdWxhcmx5IGltcG9ydGFudCBmb3IgZ3Jvd3RoIG9uIGZvcm1hdGUKLSAgIEJjMSBjb21wbGV4IHBsYXlzIG5vIHJvbGUgb24gbml0cmF0ZSwgdGhlcmVmb3JlIG5vIGNyb3NzIHRhbGsgKHdoaWxlIGEgYmMxIEtPIG11dGFudCB3YXMgZ3Jvd3RoLWRlZmljaWVudCBpbiBveHlnZW4tbGltaXRlZCBncm93dGggb24gbml0cmF0ZSAoR2FyZyBldCBhbC4sIDIwMTgpLgotICAgQmMxIGNvbXBsZXggYWNjZXB0cyByZWR1Y2VkIHF1aW5vbHMgYXMgZS0gZG9ub3IsIHB1bXBzIDQgSCsgLzIgZeKIkiB2aWEgcHJvdG9uLW1vdGl2ZSBRIGN5Y2xlIChxdWlub2w6Y3l0IGMgb3hpZG9yZWR1Y3Rpb24pLCBhbmQgcmVsZWFzZXMgcmVkdWNlZCBjeXRvY2hyb21lIEMgZS0gY2FycmllcnMuIE1haW4gY3l0b2Nocm9tZSBjb21wbGV4IGZvciBtb3N0IHN1YnN0cmF0ZXMgaW5jbHVkaW5nIGh5ZHJvZ2VuCi0gICBDYmIzLXR5cGUgY3l0b2Nocm9tZSBveGlkYXNlICgqY2NvKiBvcGVyb24pIGFsc28gaW1wb3J0YW50IGZvciBncm93dGggb24gZm9ybWF0ZSwgd2hpbGUgKmN0YSogb3Blcm9uIChhYTMgY29tcGxleCkgbW9yZSBpbXBvcnRhbnQgb24gZnJ1Y3Rvc2UgYW5kIHN1Y2NpbmF0ZQoKIyBDZW50cmFsIGNhcmJvbiBtZXRhYm9saXNtCgojIyBnZW5lLXJlYWN0aW9uIHJlbGF0aW9uc2hpcAoKVG8gc2hvdyAoY29uZGl0aW9uYWwpIGVzc2VudGlhbGl0eSBvZiBnZW5lcyB3aGljaCBhcmUgcmVsYXRlZCB0byBjZW50cmFsIG1ldGFib2xpYyByZWFjdGlvbnMsIHdlIG5lZWQgdG8gb2J0YWluIGEgdGFibGUgb2YgZ2VuZS1yZWFjdGlvbiByZWxhdGlvbnNoaXBzLgpTdWNoIGEgdGFibGUgd2FzIHVzZWQgaW4gb3VyIGVMaWZlIHBhcGVyLCBbSmFobiBldCBhbC4sIDIwMjFdKGh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvNjkwMTkpLCBhbmQgd2FzIGV4cG9ydGVkIGZyb20gdGhlIGN1cmF0ZWQgZ2Vub21lIHNjYWxlIG1vZGVsIG9mICpDLiBuZWNhdG9yKi4KCi0gZG93bmxvYWQgZ2VuZS1yZWFjdGlvbiB0YWJsZQotIGFkZCBzb21lIGdlbmVzIG1hbnVhbGx5IHRoYXQgYXJlIG5vdCBtZXRhYm9saWMgcmVhY3Rpb25zIGJ1dCBpbnRlcmVzdGluZyByZWd1bGF0b3JzCi0gd2UgYWRkOiAKICAtIGBjYmJSYCAoYEgxNl9CMTM5NmApIGNiYiBvcGVyb24gdHJhbnNjcmlwdGlvbmFsIGFjdGl2YXRvcgogIC0gYHJlZ0FgIChgSDE2X0EwMjAyYCkgcmVzcG9uc2UgcmVndWxhdG9yLCAyLWNvbXAuIHN5c3RlbQogIC0gYHJlZ0JgIChgSDE2X0EwMjAzYCkga2luYXNlLCAyLWNvbXAuIHN5c3RlbQogIC0gYHJwb05gIChgSDE2X0EwMzg3YCkgU2lnbWEgNTQgUk5BLXBvbHltZXJhc2Ugc2lnbWEgZmFjdG9yCiAgLSBgaG94QWAgKGBQSEcwMTlgKSBob3hBIG1hc3RlciByZWd1bGF0b3IKCmBgYHtyfQp1cmxfcmVhY3Rpb25zIDwtIHVybCgiaHR0cHM6Ly9naXRodWIuY29tL20tamFobi9SLW5vdGVib29rLXJhbHN0b25pYS1wcm90ZW9tZS9yYXcvbWFpbi9kYXRhL2lucHV0L21vZGVsX3JlYWN0aW9ucy5jc3YiKQpkZl9yZWFjdGlvbnMgPC0gcmVhZF9jc3YodXJsX3JlYWN0aW9ucywgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lCiAgcmVuYW1lKGxvY3VzX3RhZyA9IGdlbmVzKSAlPiUKICBhZGRfcm93KHJlYWN0aW9uX2lkID0gIlJlZ3VsYXRvcnMiLCBsb2N1c190YWcgPSAiSDE2X0IxMzk2LCBIMTZfQTAyMDIsIEgxNl9BMDIwMywgSDE2X0EwMzg3LCBQSEcwMTkiKQoKZGZfcmVhY3Rpb25zICU+JQogIGZpbHRlcighc3RyX2RldGVjdChyZWFjdGlvbl9pZCwgIl5FWF8iKSkgJT4lCiAgaGVhZCgpCmBgYAoKIyMgUGxvdCBjb25kaXRpb25hbCBmaXRuZXNzIGJyb2tlbiBkb3duIGJ5IENDTSBwYXRod2F5CgotIHVzZSB0aGUgc2FtZSBzZXQgb2YgZW56eW1lcyBhcyBpbiBbSmFobiBldCBhbC4sIDIwMjFdKGh0dHBzOi8vZWxpZmVzY2llbmNlcy5vcmcvYXJ0aWNsZXMvNjkwMTkpCi0gdXNpbmcgdGhlc2Ugc2V0cywgZXh0cmFjdCBmaXRuZXNzIGZyb20gcmVzdWx0cyB0YWJsZSBhbmQgcGxvdCBtaW5pZmlndXJlcwotIHRoZXNlIGNhbiBiZSBhcnJhbmdlZCBpbi9hcm91bmQgYSBtYXAgb2YgdGhlIGNlbnRyYWwgY2FyYm9uIG1ldGFib2xpc20gKENDTSkKLSByZWFjdGlvbnMgd2l0aCBzbGlnaHRseSBkaWZmZXJlbnQgc3Vic3RyYXRlcy9wcm9kdWN0cyBidXQgdGhhdCBhcmUgZ2VuZS13aXNlIGlkZW50aWNhbCBoYXZlIGJlZW4gcmVtb3ZlZAotIHRoZXNlIGFyZSBTVUNEaSwgQUNPTlQyLCBUS1QyLCBhbmQgUERIMyB3aGljaCBpcyBpZGVudGljYWwgdG8gQUtHREgKCmBgYHtyfQpsaXN0X2NjbV9lbnp5bWVzIDwtIGxpc3QoCiAgQ0JCID0gYygiUEdLIiwgIlRQSSIsICJHQVBEIiwgIkZCQSIsICJGQlAiLCAiVEtUMSIsICJUQWgiLCAiUlBFIiwgIlJQSSIsICJQUlVLIiwgIlJCUEMiKSwKICBFRCA9IGMoIlBHSSIsICJHNlBESDJyIiwgIlBHTCIsICJFREQiLCAiRURBIiksCiAgUFlSID0gYygiUEdNIiwgIkVOTyIsICJQWUsiLCAiUERIMSIsICJQREgyIiwgIlBDIiwgIlBQQyIsICJNRTEiLCAiTUUyIiwgIkNTIiksCiAgVENBID0gYygiQUNPTlQxIiwgIklDREh4IiwgIklDREh5ciIsICJBS0dESCIsICJTVUNPQVMiLCAiU1VDRDEiLCAiRlVNIiwgIk1ESCIsICJNQUxTIiksCiAgUkVHID0gYygiUmVndWxhdG9ycyIpCikKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQgPSAxMiwgZmlnLndpZHRoID0gMTQsIHdhcm5pbmcgPSBGQUxTRX0KIyBmaW5kIGdlbmVzIGZvciBtZXRhYm9saWMgcmVhY3Rpb25zCmxpc3RfcmVhY3Rpb24gPC0gbGlzdF9jY21fZW56eW1lcyAlPiUKICBlbmZyYW1lKG5hbWUgPSAicGF0aHdheSIsIHZhbHVlID0gInJlYWN0aW9uX2lkIikgJT4lCiAgdGlkeXI6OnVubmVzdChyZWFjdGlvbl9pZCkKCmxpc3RfZ2VuZXMgPC0gZGZfcmVhY3Rpb25zICU+JQogIHJpZ2h0X2pvaW4obGlzdF9yZWFjdGlvbiwgYnkgPSAicmVhY3Rpb25faWQiKSAlPiUKICBzZXBhcmF0ZV9sb25nZXJfZGVsaW0obG9jdXNfdGFnLCAiLCAiKSAlPiUKICBzZWxlY3QocGF0aHdheSwgcmVhY3Rpb25faWQsIGxvY3VzX3RhZykKCiMgZ2V0IGZpdG5lc3MgZm9yIHRoZXNlIGdlbmVzCmRmX2ZpdG5lc3NfY2NtIDwtIGRmX2ZpdG5lc3Nfc3VtbWFyeSAlPiUKICBmaWx0ZXIodGltZSA9PSA4KSAlPiUKICBpbm5lcl9qb2luKGxpc3RfZ2VuZXMsIGJ5ID0gImxvY3VzX3RhZyIpCgojIHBsb3QgaGVhdG1hcHMKcGxvdHNfY2NtIDwtIGRmX2ZpdG5lc3NfY2NtICU+JQogIGFycmFuZ2UocGF0aHdheSwgcmVhY3Rpb25faWQsIGxvY3VzX3RhZykgJT4lCiAgbXV0YXRlKAogICAgZ2VuZV9uYW1lID0gbG9jdXNfdGFnLAogICAgIyBnZW5lX25hbWUgPSBlZ2dOT0dfbmFtZSAlPiUKICAgICMgc3RyX2V4dHJhY3QoIlthLXpBLVowLTlfXSsiKSAlPiUKICAgICMgZmN0X2lub3JkZXIoKSwKICAgIGNvbmRfc2hvcnQgPSBjYXNlX21hdGNoKGNvbmRfc2hvcnQsCiAgICAgICJmb3JtYXRlIC0gQyIJfiAiRm9DIiwKICAgICAgImZydWN0b3NlIC0gQyIgfiAiRnJDIiwKICAgICAgIm5pdHJhdGUgLSBDIiB+ICJOaUMiLAogICAgICAic3VjY2luYXRlIC0gQyIJfiAiU3VDIiwKICAgICAgImZvcm1hdGUgLSBQIiB+ICJGb1AiLAogICAgICAiZnJ1Y3Rvc2UgLSBQIiB+ICJGclAiLAogICAgICAiaHlkcm9nZW4gLSBQIiB+ICJIeVAiLAogICAgICAibml0cmF0ZSAtIFAiCX4gIk5pUCIsCiAgICAgICJzdWNjaW5hdGUgLSBQIn4gIlN1UCIpICU+JQogICAgICBmYWN0b3IoLiwgdW5pcXVlKC4pW2MoMyw4LDcsMSw1LDQsOSwyLDYpXSksCiAgICBub3JtX2dlbmVfZml0bmVzc19tZWRpYW4gPSBub3JtX2dlbmVfZml0bmVzc19tZWRpYW4gJT4lCiAgICAgICAgICAgcmVwbGFjZSguLCAuIDwgLTYsIC02KSAlPiUgcmVwbGFjZSguLCAuID4gNiwgNikKICApICU+JQogIGdyb3VwX2J5KHBhdGh3YXksIHJlYWN0aW9uX2lkKSAlPiUKICBkcGx5cjo6Z3JvdXBfc3BsaXQoKSAlPiUKICBsYXBwbHkoZnVuY3Rpb24oZGYpIHsKICAgIGxldmVscGxvdCgKICAgICAgbm9ybV9nZW5lX2ZpdG5lc3NfbWVkaWFuIH4gZmN0X3Jldihjb25kX3Nob3J0KSAqIGZjdF9yZXYoZ2VuZV9uYW1lKSB8CiAgICAgICAgcmVhY3Rpb25faWQsIGRmLAogICAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLCBjb2xvcmtleSA9IEZBTFNFLAogICAgICBjb2wucmVnaW9ucyA9IGNvbG9yUmFtcFBhbGV0dGUoaGVhdF9jb2xzKSgyNSksCiAgICAgIGF0ID0gc2VxKC02LCA2LCAwLjUpLCBhc3BlY3QgPSAiaXNvIiwgeGxhYiA9ICIiLCB5bGFiID0gIiIsCiAgICAgIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSwgY2V4ID0gMC43LCB0Y2sgPSAwLAogICAgICAgIHggPSBsaXN0KHJvdCA9IDkwKSksCiAgICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLCBsd2QgPSAwLAogICAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIHosIC4uLikgewogICAgICAgIHBhbmVsLmxldmVscGxvdCh4LCB5LCB6LCAuLi4pCiAgICAgICAgcGFuZWwuYWJsaW5lKAogICAgICAgICAgdiA9IHNlcV9hbG9uZyh1bmlxdWUoeCkpICsgMC41LAogICAgICAgICAgaCA9IHNlcV9hbG9uZyh1bmlxdWUoeSkpICsgMC41LAogICAgICAgICAgY29sID0gIndoaXRlIiwKICAgICAgICAgIGx3ZCA9IDIKICAgICAgICApCiAgICAgIH0KICAgICkKICB9KQoKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdHNfY2NtLCBuY29sID0gNikKYGBgCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnN2ZygiLi4vZmlndXJlcy9maWd1cmVfY2VudHJhbG1ldC5zdmciLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMikKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdHNfY2NtLCBuY29sID0gNikKZGV2Lm9mZigpCmBgYAoKIyBHcm93dGggYWR2YW50YWdlIG9mIEh5ZHJvZ2VuYXNlIG11dGFudHMKCiMjIERldGVybWluZSBtdXRhbnQgZ3Jvd3RoIHJhdGUgZnJvbSBjb21wZXRpdGlvbiBleHBlcmltZW50cwoKVGhlIG1haW4gbWVhc3VyZW1lbnQgdGhhdCB3YXMgb2J0YWluZWQgZnJvbSB0cmFuc3Bvc29uIGxpYnJhcnkgY29tcGV0aXRpb24gZXhwZXJpbWVudHMgaXMgdGhlIGZvbGQgY2hhbmdlLCBhbmQgdGhlIGZpdG5lc3Mgc2NvcmUgYXMgYSBzdW1tYXJ5IHN0YXRpc3RpYyB0aGVyZW9mLiBUaGUgZm9sZCBjaGFuZ2Ugb2YgYSBtdXRhbnQgc3ViLXBvcHVsYXRpb24gb3ZlciB0aW1lIGNhbiBhbHNvIGJlIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSByZWxhdGl2ZSBncm93dGggYWR2YW50YWdlIG92ZXIgb3RoZXIgc3RyYWlucy4gRm9yIHN1Y2ggYSBncm93dGggbW9kZWwsIHRoZSBvbmx5IHJlcXVpcmVkIGlucHV0IHBhcmFtZXRlcnMgYXJlIHRoZSBGQywgdGhlIGF2ZXJhZ2UgcG9wdWxhdGlvbiBncm93dGggcmF0ZSwgYW5kIHRoZSB0aW1lIGluIGhvdXJzLgoKJCQgRkMgPSAoMSAtIChcbXVfe3BvcH0gLSBcbXVfe211dH0pKV57dH0gJCQgUmUtYXJyYW5nZWQgdG8gb2J0YWluIHRoZSBtdXRhbnQgZ3Jvd3RoIHJhdGUuCgokJCBcbXVfe211dH0gPSAtKDEgLSBGQ157MS90fSkgKyBcbXVfe3BvcH0kJAoKQWxsIGlucHV0IHZhcmlhYmxlcyBhcmUgYXZhaWxhYmxlOgoKLSAgIMK1X3BvcCA9IDAuMSBoXF4tMSwga25vd24gZnJvbSBjaGVtb3N0YXRzCi0gICBGQyBmb3IgZWFjaCBtdXRhbnQvZ2VuZSBmcm9tIGxpYnJhcnkgY29tcGV0aXRpb24gZXhwZXJpbWVudHMgKCdmaXRuZXNzJyBpcyB0aGUgbm9ybWFsaXplZCBsb2cyIEZDKQotICAgdCA9IG5fZ2VuIHggKGxuIDIpLzAuMSAtIG51bWJlciBvZiBnZW5lcmF0aW9ucyAoMCwgOCwgMTYpIG11bHRpcGxpZWQgd2l0aCBnZW5lcmF0aW9uIHRpbWUgb2YgNi45MyBoCgpXZSBtYWtlIGEgZ2VuZXJhbGl6ZWQgZnVuY3Rpb24gdGhhdCBjYWxjdWxhdGVzIGdyb3d0aCByYXRlIChkaXMtKSBhZHZhbnRhZ2UuCgpgYGB7cn0KbXV0YW50X211IDwtIGZ1bmN0aW9uKGZvbGRfY2hhbmdlLCBwb3BfbXUsIHRpbWUsIGxvZzJfZmMgPSBGQUxTRSkgewogIGlmIChsb2cyX2ZjKSB7Zm9sZF9jaGFuZ2UgPSAyXmZvbGRfY2hhbmdlfQogIHJlc3VsdCA9IC0oMSAtIChmb2xkX2NoYW5nZV4oMS90aW1lKSkpICsgcG9wX211CiAgcmV0dXJuKHJlc3VsdCkKfQpgYGAKClRoZW4gc2VsZWN0IHRoZSBoeWRyb2dlbmFzZSBtdXRhbnRzIHdoZXJlIGFuIGVucmljaG1lbnQgd2FzIG9ic2VydmVkIGluIGhldGVyb3Ryb3BoaWMgY29uZGl0aW9ucy4KCmBgYHtyLCBmaWcud2lkdGggPSA2LjUsIGZpZy5oZWlnaHQgPSA2LjB9CmRmX2luY3JlYXNlZF9tdSA8LSBkZl9maXRuZXNzX3N1bW1hcnkgJT4lCiAgZmlsdGVyKAogICAgZ3JlcGwoImhveFtLR1pNTE9RUlRWQUJDSk5GVVlIV0ldfGh5cFtCQ0RFRlhdMT8gfGh5cEEgUEhHMDEyIiwgZ2VuZV9uYW1lKSwgY29uZGl0aW9uID09ICJjb250aW51b3VzIiwKICAgIHN1YnN0cmF0ZSAlaW4lIGMoImZvcm1hdGUiLCAiZnJ1Y3Rvc2UiLCAic3VjY2luYXRlIikpICU+JQogIGZpbHRlcihsb2N1c190YWcgIT0gIlBIRzA2MyIpICU+JQogIG11dGF0ZShnZW5lX25hbWUgPSBzdHJpX2V4dHJhY3RfZmlyc3RfcmVnZXgoZ2VuZV9uYW1lLCAiaG94W0tHWk1MT1FSVFZBQkNKTkZVWUhXSV18aHlwW0FCQ0RFRlhdIikpICU+JQogIG11dGF0ZShnZW5lX25hbWUgPSBmY3RfaW5vcmRlcihnZW5lX25hbWUpKQoKcGxvdF9pbmNyZWFzZWRfbXUgPC0geHlwbG90KG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiB+IHRpbWUgfCBnZW5lX25hbWUsIGRmX2luY3JlYXNlZF9tdSwKICAgIGdyb3VwcyA9IHN1YnN0cmF0ZSwgYXMudGFibGUgPSBUUlVFLCBsYXlvdXQgPSBjKDYsIDUpLAogICAgY29sID0gc3RkY29sWzI6NF0sIHhsYWIgPSAiZ2VuZXJhdGlvbnMiLCB5bGFiID0gImZpdG5lc3MiLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwgdHlwZSA9IGMoImwiKSwKICAgIGF1dG8ua2V5ID0gbGlzdChjb2x1bW5zID0gMyksCiAgICBiZXR3ZWVuID0gbGlzdCh4ID0gMC41LCB5ID0gMC41KSwKICAgIHNjYWxlcyA9IGxpc3QoYWx0ZXJuYXRpbmcgPSBGQUxTRSksIGx3ZCA9IDIsCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmFibGluZShoID0gMCwgY29sID0gZ3JleSgwLjUpLCBsdHkgPSAzLCBsd2QgPSAyKQogICAgICBwYW5lbC54eXBsb3QoeCwgeSwgLi4uKQogICAgfQogICkKCmRmX211dGFudF9tdSA8LSBkZl9pbmNyZWFzZWRfbXUgJT4lCiAgZmlsdGVyKHRpbWUgIT0gMCkgJT4lCiAgZ3JvdXBfYnkoZ2VuZV9uYW1lLCBzdWJzdHJhdGUpICU+JQogIG11dGF0ZShtdXRhbnRfbXUgPSBtdXRhbnRfbXUoCiAgICBmb2xkX2NoYW5nZSA9IG5vcm1fZ2VuZV9maXRuZXNzX21lZGlhbiwgCiAgICBwb3BfbXUgPSAwLjEsIHRpbWUgPSB0aW1lKihsb2coMikvMC4xKSwKICAgIGxvZzJfZmMgPSBUUlVFKSkgJT4lCiAgc2VsZWN0KGxvY3VzX3RhZywgdGltZSwgY29uZGl0aW9uLCBzdWJzdHJhdGUsCiAgICBub3JtX2dlbmVfZml0bmVzc19tZWRpYW4sIGdlbmVfbmFtZSwgY29uZF9zaG9ydCwgbXV0YW50X211KQoKcHJpbnQocGxvdF9pbmNyZWFzZWRfbXUpCmBgYAoKIyMgR3Jvd3RoIGFkdmFudGFnZSAqdmVyc3VzKiBwcm90ZWluIGNvc3QKClRoZSBtYWluIHF1ZXN0aW9uIGlzIGlmIHRoZSBvYnNlcnZlZCBncm93dGggYWR2YW50YWdlIGNhbiBiZSBwdXJlbHkgZXhwbGFpbmVkIGJ5IHRoZSBzYXZpbmcgb2YgZ2VuZSBleHByZXNzaW9uIGNvc3QuIEh5ZHJvZ2VuYXNlIGFuZCBzb21lIGFjY2Vzc29yeSBwcm90ZWlucyBhcmUgaGlnaGx5IGFidW5kYW50IGFuZCB0aGVyZWZvcmUgY2F1c2UgYSBzaWduaWZpY2FudCBjb3N0IHRvIHRoZSBjZWxsLiBJdCB3YXMgc2hvd24gaW4gbWFueSBvdGhlciBzdHVkaWVzIHRoYXQgcmVkdWNpbmcgZXhjZXNzL3VudXNlZCBwcm90ZWlucyBjYW4gaW5jcmVhc2UgZ3Jvd3RoIHJhdGUuIFRoaXMgaXMgYmFzZWQgb24gdGhlIG9ic2VydmF0aW9uIHRoYXQgdGhlIHByb3RlaW4gcG9vbCBpcyBsaW1pdGVkIGFuZCBjZWxscyBjYW4gb3B0aW1pemUgdGhlIHV0aWxpemF0aW9uIG9mIHRoaXMgbGltaXRlZCByZXNvdXJjZSBieSB0dW5pbmcgZ2VuZSBleHByZXNzaW9uIHRvd2FyZHMgaGlnaGx5IHV0aWxpemVkIGdlbmVzL3Byb3RlaW5zLgoKSW4gdGhlIG5leHQgc2VjdGlvbiwgZ2xvYmFsIHByb3RlaW4gYWJ1bmRhbmNlIGRhdGEgaXMgaW1wb3J0ZWQgdGhhdCB3YXMgb2J0YWluZWQgZnJvbSBxdWFudGl0YXRpdmUgTVMgZXhwZXJpbWVudHMuIE1TIGRhdGEgaXMgYXZhaWxhYmxlIGZvciB0aGUgdGhyZWUgY29uZGl0aW9ucyB1c2VkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uLCBjb250aW51b3VzIGdyb3d0aCBvbiBmcnVjdG9zZSwgc3VjY2luYXRlLCBhbmQgZm9ybWF0ZSBpbiBjaGVtb3N0YXRzIGF0IGRlZmluZWQgZ3Jvd3RoIHJhdGVzLgoKKipOb3RlOiBUaGUgZGF0YSBpcyBpbXBvcnRlZCBkaXJlY3RseSBmcm9tIHRoZSByZXNwZWN0aXZlIGdpdGh1YiByZXBvc2l0b3J5LCByZXF1aXJpbmcgaW50ZXJuZXQgY29ubmVjdGlvbioqLgoKYGBge3J9CmxvYWQodXJsKCJodHRwczovL2dpdGh1Yi5jb20vbS1qYWhuL1Itbm90ZWJvb2stcmFsc3RvbmlhLXByb3Rlb21lL2Jsb2IvbWFpbi9kYXRhL2lucHV0L1JhbHN0b25pYV9ldXRyb3BoYS5SZGF0YT9yYXc9dHJ1ZSIpKQpoZWFkKFJhbHN0b25pYV9ldXRyb3BoYVsxOjEwXSkKYGBgCgpUbyBhc3Nlc3MgcmVhbCBwcm90ZWluIGNvc3Qgb2YgYSBrbm9jay1vdXQsIGl0IGlzIGltcG9ydGFudCB0byBjb25zaWRlciB0aGF0IEtPIGJ5IHRyYW5zcG9zb24gaW50ZWdyYXRpb24gd2lsbCBoYXZlIGFuIGVmZmVjdCBvbiB0aGUgZG93bnN0cmVhbSBnZW5lIGV4cHJlc3Npb24gdG9vLCBlZmZlY3RpdmVseSBkaXNydXB0aW5nIHRyYW5zY3JpcHRpb24vdHJhbnNsYXRpb24gb2YgdGhlIGVudGlyZSBvcGVyb24gZG93bnN0cmVhbSBvZiB0aGUgaW50ZWdyYXRpb24gc2l0ZS4KCklmIHdlIGFzc3VtZSB0aGF0IGVhY2ggS08ga25vY2tzIG91dCB0aGUgZ2VuZSBleHByZXNzaW9uIG9mIGFsbCBjb25zZWN1dGl2ZSBnZW5lcyBvZiB0aGUgb3Blcm9uLCB3ZSBuZWVkIHRvIHN1bSB1cCBwcm90ZWluIGNvc3QgZm9yIHRoZXNlIGFmZmVjdGVkIGdlbmUgc2V0czsgd2UgZ2VuZXJhdGUgYSAqKmJpbmFyeSBtYXRyaXgqKiBvZiBhc3NvY2lhdGlvbnMgZm9yIGVhY2ggS08gd2l0aCBhIG51bWJlciBvZiBhZmZlY3RlZCBnZW5lcyAoMCA9IEZBTFNFLCAxID0gVFJVRSksIGFuZCBtdWx0aXBseSB3aXRoIHByb3RlaW4gbWFzcy4gSGVyZSwgd2UgbmVlZCB0byB0ZXN0IGRpZmZlcmVudCBhc3N1bXB0aW9ucy4gVGhlIGNhbm9uaWNhbCByZWd1bGF0aW9uIG9mIHRoZSBob3gvaHlwIG9wZXJvbiBoYXBwZW5zIHZpYSBgaG94QWAgdHJhbnNjcmlwdGlvbmFsIHJlZ3VsYXRvciwgYGhveEJDYCBIMiBzZW5zaW5nICdyZWd1bGF0b3J5IGh5ZHJvZ2VuYXNlJyAoUkgpLCBhbmQgYGhveEpgLCB0aGUgZGVmZWN0IGtpbmFzZSB0aGF0IHRyYW5zbWl0cyB0aGUgb3V0cHV0IGZyb20gYGhveEJDYCB0byBgaG94QT9gLiBUaGlzIHJlZ3VsYXRpb24gY29udHJvbHMgdHdvIHByb21vdGVyczoKCi0gICB0aGUgUF9NQkggcHJvbW90ZXIgdGhhdCBzdGFydHMgdHJhbnNjcmlwdGlvbiBvZiB0aGUgaHVnZSBgaG94S0daTUxPUVJUVmAgKyBgaHlwQUJGQ0RFWGAgKyBgaG94QUJDSiBvcGVyb25gCi0gICB0aGUgUF9TSCBwcm9tb3RlciB0aGF0IHN0YXJ0cyB0cmFuc2NyaXB0aW9uIG9mIHRoZSBzbWFsbGVyIGBob3hGVVlIV0lgICsgYGh5cEEyQjJGMmAgb3Blcm9uCgpBY2NvcmRpbmdseSwgYGhveEFgIGNvbnRyb2xzIGl0cyBvd24gdHJhbnNjcmlwdGlvbiBpbiBhIGZlZWQtZm9yd2FyZCBhdXRvcmVndWxhdG9yeSBmYXNoaW9uLiBJbmNyZWFzZWQgYGhveEFgIHByb3RlaW4gYWJ1bmRhbmNlIGxlYWRzIHRvIHN0cm9uZ2VyIGBob3hBYCBleHByZXNzaW9uLCBldGMuIEhvd2V2ZXIsIGlmIGEgdHJhbnNwb3NvbiBpbnRlZ3JhdGVzIGludG8gYW55IGdpdmVuIHNpdGUgb2Ygb25lIG9mIHRoZSBvcGVyb25zLCB0cmFuc2NyaXB0aW9uIG9mIGZvbGxvd2luZyBnZW5lcyBzaG91bGQgYmUgdGVybWluYXRlZCBvciBvdGhlcndpc2UgbmVnYXRpdmVseSBhZmZlY3RlZC4gU3VycHJpc2luZ2x5LCB0aGlzIGVmZmVjdCBpcyB2aXNpYmxlIGZvciBgaHlwYCBtdXRhbnRzIHRoYXQgbWlnaHQgbmVnYXRpdmVseSBhZmZlY3QgYGhveEFgIGV4cHJlc3Npb24sIGJ1dCBub3QgZm9yIGFueSBwcmVjZWRpbmcgZ2VuZXMgKGBob3hLSFpNTE9RUlRWYCkuIFRoaXMgc3VnZ2VzdHMgdGhhdDoKCi0gICB0aGVyZSBpcyBhbm90aGVyLCBzZWNvbmRhcnkgcHJvbW90ZXIgdGhhdCBjb250cm9scyBgaHlwYCArIGBob3hBQkNKTmAgZ2VuZSBleHByZXNzaW9uCi0gICBpbiBmYWN0IHRoaXMgaGFzIGJlZW4gc2hvd24gaW4gLi4uCgpgYGB7cn0KZ2VuZV9saXN0X21oIDwtIGMoImhveEsiLCAiaG94RyIsICJob3haIiwgImhveE0iLCAiaG94TCIsICJob3hPIiwgImhveFEiLCAiaG94UiIsICJob3hUIiwgImhveFYiKQpnZW5lX2xpc3Rfc2ggPC0gYygiaG94QSIsICJob3hCIiwgImhveEMiLCAiaG94SiIsICJob3hOIiwgImhveEYiLCAiaG94VSIsICJob3hZIiwgImhveEgiLCAiaG94VyIsICJob3hJIikKZ2VuZV9saXN0X2h5cCA8LSBjKCJoeXBBIiwgImh5cEIiLCAiaHlwRiIsICJoeXBDIiwgImh5cEQiLCAiaHlwRSIsICJoeXBYIikKCmxpc3RfbXV0YW50X2dlbm9taWMgPC0gbGlzdCgKICBob3hLID0gZ2VuZV9saXN0X21oLAogIGhveEcgPSBnZW5lX2xpc3RfbWhbMjoxMF0sCiAgaG94WiA9IGdlbmVfbGlzdF9taFszOjEwXSwKICBob3hNID0gZ2VuZV9saXN0X21oWzQ6MTBdLAogIGhveEwgPSBnZW5lX2xpc3RfbWhbNToxMF0sCiAgaG94TyA9IGdlbmVfbGlzdF9taFs2OjEwXSwKICBob3hRID0gZ2VuZV9saXN0X21oWzc6MTBdLAogIGhveFIgPSBnZW5lX2xpc3RfbWhbODoxMF0sCiAgaG94VCA9IGdlbmVfbGlzdF9taFs5OjEwXSwKICBob3hWID0gZ2VuZV9saXN0X21oWzEwXSwKICBoeXBBID0gYyhnZW5lX2xpc3RfbWgsIGdlbmVfbGlzdF9oeXAsIGdlbmVfbGlzdF9zaCksCiAgaHlwQiA9IGMoZ2VuZV9saXN0X21oLCBnZW5lX2xpc3RfaHlwWzI6N10sIGdlbmVfbGlzdF9zaCksCiAgaHlwRiA9IGMoZ2VuZV9saXN0X21oLCBnZW5lX2xpc3RfaHlwWzM6N10sIGdlbmVfbGlzdF9zaCksCiAgaHlwQyA9IGMoZ2VuZV9saXN0X21oLCBnZW5lX2xpc3RfaHlwWzQ6N10sIGdlbmVfbGlzdF9zaCksCiAgaHlwRCA9IGMoZ2VuZV9saXN0X21oLCBnZW5lX2xpc3RfaHlwWzU6N10sIGdlbmVfbGlzdF9zaCksCiAgaHlwRSA9IGMoZ2VuZV9saXN0X21oLCBnZW5lX2xpc3RfaHlwWzY6N10sIGdlbmVfbGlzdF9zaCksCiAgaHlwWCA9IGMoZ2VuZV9saXN0X21oLCBnZW5lX2xpc3RfaHlwWzddLCBnZW5lX2xpc3Rfc2gpLAogIGhveEEgPSBjKGdlbmVfbGlzdF9taCwgZ2VuZV9saXN0X2h5cCwgZ2VuZV9saXN0X3NoKSwKICBob3hCID0gZ2VuZV9saXN0X3NoWzI6NV0sCiAgaG94QyA9IGdlbmVfbGlzdF9zaFszOjVdLAogIGhveEogPSBnZW5lX2xpc3Rfc2hbNDo1XSwKICBob3hOID0gZ2VuZV9saXN0X3NoWzVdLAogIGhveEYgPSBnZW5lX2xpc3Rfc2hbYyg2OjExKV0sCiAgaG94VSA9IGdlbmVfbGlzdF9zaFtjKDc6MTEpXSwKICBob3hZID0gZ2VuZV9saXN0X3NoW2MoODoxMSldLAogIGhveEggPSBnZW5lX2xpc3Rfc2hbYyg5OjExKV0sCiAgaG94VyA9IGdlbmVfbGlzdF9zaFtjKDEwOjExKV0sCiAgaG94SSA9IGdlbmVfbGlzdF9zaFtjKDExKV0KKQoKbWF0X211dGFudF9nZW5vbWljIDwtIGxhcHBseShsaXN0X211dGFudF9nZW5vbWljLCBmdW5jdGlvbih4KSB7CiAgICBhcy5udW1lcmljKG5hbWVzKGxpc3RfbXV0YW50X2dlbm9taWMpICVpbiUgeCkKICB9KSAlPiUgdW5saXN0ICU+JSBtYXRyaXgobmNvbCA9IGxlbmd0aChsaXN0X211dGFudF9nZW5vbWljKSwgYnlyb3cgPSBUUlVFKSAlPiUgdApyb3duYW1lcyhtYXRfbXV0YW50X2dlbm9taWMpIDwtIG5hbWVzKGxpc3RfbXV0YW50X2dlbm9taWMpCmNvbG5hbWVzKG1hdF9tdXRhbnRfZ2Vub21pYykgPC0gbmFtZXMobGlzdF9tdXRhbnRfZ2Vub21pYykKYGBgCgotICAgQWRkIGluZGl2aWR1YWwgYW5kIGFjY3VtdWxhdGVkIHByb3RlaW4gY29zdCBmb3IgZWFjaCBLTyBtdXRhbnQuCgpgYGB7cn0KIyBhZGQgaW5kaXZpZHVhbCBwcm90ZWluIGNvc3QgZnJvbSBNUyBkYXRhCmRmX211dGFudF9jb3N0IDwtIGRmX211dGFudF9tdSAlPiUKICBncm91cF9ieShsb2N1c190YWcsIGdlbmVfbmFtZSwgc3Vic3RyYXRlKSAlPiUKICBzdW1tYXJpemUoLmdyb3VwcyA9ICJkcm9wIiwKICAgIG1lYW5fbXV0YW50X211ID0gbWVhbihtdXRhbnRfbXUpLAogICAgc2RfbXV0YW50X211ID0gc2QobXV0YW50X211KSkgJT4lCiAgbGVmdF9qb2luKGJ5ID0gYygibG9jdXNfdGFnIiwgInN1YnN0cmF0ZSIpLAogICAgUmFsc3RvbmlhX2V1dHJvcGhhICU+JSB1bmdyb3VwICU+JQogICAgICBmaWx0ZXIoZ3Jvd3RocmF0ZSA9PSAwLjEpICU+JQogICAgICBzZWxlY3QobG9jdXNfdGFnLCBzdWJzdHJhdGUsIG1lYW5fbWFzc19mcmFjdGlvbl9ub3JtLCBzZF9tYXNzZnJhY3Rpb24pKSAlPiUKICBhcnJhbmdlKGxvY3VzX3RhZykgJT4lCiAgdGlkeXI6OmNvbXBsZXRlKHN1YnN0cmF0ZSwgbmVzdGluZyhsb2N1c190YWcsIGdlbmVfbmFtZSkpICU+JQogIAogICMgYWRkIGVzdGltYXRlZCB0b3RhbCBjb3N0IGZvciBlYWNoIGtub2Nrb3V0CiAgZ3JvdXBfYnkoc3Vic3RyYXRlKSAlPiUKICBtdXRhdGUoCiAgICBjb3JyZWN0ZWRfY29zdCA9IG1lYW5fbWFzc19mcmFjdGlvbl9ub3JtICU+JSB7Y29sU3VtcyhuYS5ybSA9IFRSVUUsIC4qMTAwKm1hdF9tdXRhbnRfZ2Vub21pYyl9LAogICAgY29ycmVjdGVkX3NkID0gc2RfbWFzc2ZyYWN0aW9uICU+JSB7Y29sU3VtcyhuYS5ybSA9IFRSVUUsIC4qMTAwKm1hdF9tdXRhbnRfZ2Vub21pYyl9CiAgKQpgYGAKCi0gICBQbG90IG11dGFudCBncm93dGggcmF0ZSwgaW5kaXZpZHVhbCBjb3N0LCBhbmQgdG90YWwgcHJvdGVpbiBjb3N0IHRoYXQgaXMgc2F2ZWQgaW4gS08gbXV0YW50cy4KCmBgYHtyLCBmaWcud2lkdGggPSA3LjUsIGZpZy5oZWlnaHQgPSA3LjV9CnBsb3RfbXV0YW50X211IDwtIHh5cGxvdChtdXRhbnRfbXUgfiBnZW5lX25hbWUsIGRmX211dGFudF9tdSwKICAgIGdyb3VwcyA9IHN1YnN0cmF0ZSwgYXMudGFibGUgPSBUUlVFLAogICAgY29sID0gc3RkY29sWzI6NF0sIHhsYWIgPSAiIiwgeWxhYiA9IGV4cHJlc3Npb24oImVzdGltYXRlZCDCtSBbaCJeLTEqIl0iKSwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksIGx3ZCA9IDIsCiAgICBzY2FsZXMgPSBsaXN0KHggPSBsaXN0KHJvdCA9IDkwKSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmFibGluZShoID0gMC4xLCBjb2wgPSBncmV5KDAuNSksIGx0eSA9IDMsIGx3ZCA9IDIpCiAgICAgIHBhbmVsLmVycmJhcnMoeCwgeSwgYmVzaWRlID0gVFJVRSwgLi4uKQogICAgfQogICkKCnBsb3RfbXV0YW50X2Nvc3QgPC0geHlwbG90KG1lYW5fbWFzc19mcmFjdGlvbl9ub3JtKjEwMCB+IGdlbmVfbmFtZSwgZGZfbXV0YW50X2Nvc3QsCiAgICBncm91cHMgPSBzdWJzdHJhdGUsIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgICBlcnJvcl9tYXJnaW4gPSBkZl9tdXRhbnRfY29zdFtbInNkX21hc3NmcmFjdGlvbiJdXSoxMDAsCiAgICBsd2QgPSAyLCBjb2wgPSBzdGRjb2xbMjo0XSwgeWxpbSA9IGMoLTAuMiwgNi41KSwgcGNoID0gMSwKICAgIHhsYWIgPSAiIiwgeWxhYiA9ICJyZWR1Y3Rpb24gcHJvdGVpbiBjb3N0IFslXSIsCiAgICBzY2FsZXMgPSBsaXN0KHggPSBsaXN0KHJvdCA9IDkwKSksCiAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgIHBhbmVsLmVycmJhcnMoeCwgeSwgYmVzaWRlID0gVFJVRSwgLi4uKQogICAgICBwYW5lbC5rZXkoY29ybmVyID0gYygwLjk4LCAwLjk1KSwgLi4uKQogICAgICBwYW5lbC5rZXkoY29ybmVyID0gYygwLjk4LCAwLjYwKSwKICAgICAgICBsYWJlbHMgPSBjKCJpbmRpdmlkdWFsIGNvc3QiLCAidG90YWwgY29zdCAoZXN0LikiKSwKICAgICAgICBjb2wgPSBncmV5KDAuNSksIHBjaCA9IGMoMSwgMTkpKQogICAgfQogICkgKyBhcy5sYXllcigKICAgIHh5cGxvdChjb3JyZWN0ZWRfY29zdCB+IGdlbmVfbmFtZSwgZGZfbXV0YW50X2Nvc3QsCiAgICAgIGdyb3VwcyA9IHN1YnN0cmF0ZSwKICAgICAgZXJyb3JfbWFyZ2luID0gZGZfbXV0YW50X2Nvc3RbWyJjb3JyZWN0ZWRfc2QiXV0sCiAgICAgIGx3ZCA9IDIsIGNvbCA9IHN0ZGNvbFsyOjRdLAogICAgICBwYW5lbCA9IGZ1bmN0aW9uKHgsIHksIC4uLikgewogICAgICAgIHBhbmVsLmVycmJhcnMoeCwgeSwgYmVzaWRlID0gVFJVRSwgLi4uKQogICAgICB9CiAgICApCiAgKQoKcGxvdF9tYmhfb3Blcm9uIDwtIGRmX2luY3JlYXNlZF9tdSAlPiUKICBmaWx0ZXIodGltZSA9PSA4KSAlPiUKICBncm91cF9ieShsb2N1c190YWcsIGdlbmVfbmFtZSwgc2NhZmZvbGQsIHN0cmFuZCwgc3RhcnQsIGVuZCkgJT4lCiAgc3VtbWFyaXplKHN0cmFpbnNfcGVyX2dlbmUgPSBtaW4odW5pcXVlKHN0cmFpbnNfcGVyX2dlbmUpKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgZ2Vub21lX3Bsb3QoeGxpbSA9IGMoMC4wMTUsIDIyLjQ5MCksIHRpdGxlID0gInBIRzEiLCByb3RfbGFiZWxzID0gNDUpCgpwbG90X3NoX29wZXJvbiA8LSBkZl9pbmNyZWFzZWRfbXUgJT4lCiAgZmlsdGVyKHRpbWUgPT0gOCkgJT4lCiAgZ3JvdXBfYnkobG9jdXNfdGFnLCBnZW5lX25hbWUsIHNjYWZmb2xkLCBzdHJhbmQsIHN0YXJ0LCBlbmQpICU+JQogIHN1bW1hcml6ZShzdHJhaW5zX3Blcl9nZW5lID0gbWluKHVuaXF1ZShzdHJhaW5zX3Blcl9nZW5lKSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIGdlbm9tZV9wbG90KHhsaW0gPSBjKDc5LjYxMiwgODUuNDM4KSwgdGl0bGUgPSAicEhHMSIsIHJvdF9sYWJlbHMgPSA0NSkKCnByaW50KHBsb3RfbXV0YW50X211LCBwb3NpdGlvbiA9IGMoLTAuMDE3LCAwLjYwLCAxLCAxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbXV0YW50X2Nvc3QsIHBvc2l0aW9uID0gYygwLjAyLCAwLjMwLCAxLCAwLjcwKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfbWJoX29wZXJvbiwgcG9zaXRpb24gPSBjKDAuMDc1LCAwLjE0LCAwLjk3LCAwLjM5KSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3Rfc2hfb3Blcm9uLCBwb3NpdGlvbiA9IGMoMC4wNzUsIDAsIDAuNCwgMC4yNSkpCmdyaWQ6OmdyaWQudGV4dChjKCJBIiwiQiIsIkMiKSwgeCA9IGMoMC4wMiwgMC4wMiwgMC4wMiksCiAgeSA9IGMoMC45OCwgMC42NiwgMC4zNCksIGdwID0gZ3JpZDo6Z3BhcihjZXggPSAxLjIpKQpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CnN2ZygiLi4vZmlndXJlcy9maWd1cmVfbXV0YW50X2Nvc3Quc3ZnIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDcuNSkKcHJpbnQocGxvdF9tdXRhbnRfbXUsIHBvc2l0aW9uID0gYygtMC4wMTcsIDAuNjAsIDEsIDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9tdXRhbnRfY29zdCwgcG9zaXRpb24gPSBjKDAuMDIsIDAuMzAsIDEsIDAuNzApLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9tYmhfb3Blcm9uLCBwb3NpdGlvbiA9IGMoMC4wNzUsIDAuMTQsIDAuOTcsIDAuMzkpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9zaF9vcGVyb24sIHBvc2l0aW9uID0gYygwLjA3NSwgMCwgMC40LCAwLjI1KSkKZ3JpZDo6Z3JpZC50ZXh0KGMoIkEiLCJCIiwiQyIpLCB4ID0gYygwLjAyLCAwLjAyLCAwLjAyKSwKICB5ID0gYygwLjk4LCAwLjY2LCAwLjM0KSwgZ3AgPSBncmlkOjpncGFyKGNleCA9IDEuMikpCmRldi5vZmYoKQpgYGAKCi0gcGxvdCBjb3JyZWxhdGlvbiBvZiBlc3RpbWF0ZWQgcHJvdGVpbiBjb3N0IGFuZCBncm93dGggcmF0ZSwgcGVyIG11dGFudAotIGNvcnJlbGF0aW9uIGlzIHZpc2libGUgd2hlbiBwbG90dGluZyBib3RoIHBhcmFtZXRlcnMgc2VwYXJhdGVseSwgYnV0IHdpbGwgaXQgYWxzbyBob2xkIGluIGRpcmVjdCBjb21wYXJpc29uPwoKYGBge3IsIGZpZy53aWR0aCA9IDYuNSwgZmlnLmhlaWdodCA9My41fQpwbG90X2Nvc3RfdnNfbXUgPC0geHlwbG90KG1lYW5fbXV0YW50X211IH4gY29ycmVjdGVkX2Nvc3QgfCBzdWJzdHJhdGUsCiAgICBkZl9tdXRhbnRfY29zdCwgZ3JvdXBzID0gc3Vic3RyYXRlLAogICAgcG9pbnRsYWJlbHMgPSBhcy5jaGFyYWN0ZXIoZGZfbXV0YW50X2Nvc3QkZ2VuZV9uYW1lKSwKICAgIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksIGNvbCA9IHN0ZGNvbFsyOjRdLAogICAgeGxhYiA9ICJyZWR1Y3Rpb24gcHJvdGVpbiBjb3N0IFslXSIsCiAgICB5bGFiID0gZXhwcmVzc2lvbigiZXN0aW1hdGVkIMK1IFtoIl4tMSoiXSIpLAogICAgYXNwZWN0ID0gMSwgc2NhbGVzID0gbGlzdChhbHRlcm5hdGluZyA9IEZBTFNFKSwKICAgIGJldHdlZW4gPSBsaXN0KHggPSAwLjUsIHkgPSAwLjUpLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCBwb2ludGxhYmVscywgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwuc3VwZXJwb3NlKHgsIHksIC4uLil9LAogICAgcGFuZWwuZ3JvdXBzID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLnh5cGxvdCh4LCB5LCAuLi4pCiAgICAgIHBhbmVsLmxtbGluZXEoeCwgeSwgci5zcXVhcmVkID0gVFJVRSwgbGFiZWwgPSAiIiwgLi4uKQogICAgfQogICkKCnBsb3RfY29zdF92c19tdQoKc3ZnKCIuLi9maWd1cmVzL2ZpZ3VyZV9tdXRhbnRfY29zdF92c19ncm93dGguc3ZnIiwgd2lkdGggPSA2LjUsIGhlaWdodCA9IDMuNSkKcHJpbnQocGxvdF9jb3N0X3ZzX211KQpkZXYub2ZmKCkKYGBgCgoKIyMgUHJlZGljdGlvbiBvZiBncm93dGggYWR2YW50YWdlIHVzaW5nIGNlbGwgZWNvbm9teSBtb2RlbAoKVGhlIGNvbnN0cmFpbmVkIFJlc291cmNlIEJhbGFuY2UgQW5hbHlzaXMgKFJCQSkgbW9kZWwgZGVzY3JpYmVkIGluIFtKYWhuIGV0IGFsLiwgMjAyMV0oaHR0cHM6Ly9lbGlmZXNjaWVuY2VzLm9yZy9hcnRpY2xlcy82OTAxOSkgY2FuIGJlIHVzZWQgdG8gbWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgZWZmZWN0IG9mIGh5ZHJvZ2VuYXNlIGtub2Nrb3V0IG9uIGNlbGwgZ3Jvd3RoIHJhdGUuIFRoZSBSQkEgbW9kZWwgaXMgYSBnZW5vbWUgc2NhbGUgbWV0YWJvbGljIG1vZGVsIG9mICpSLiBldXRyb3BoYSogdGhhdCB0YWtlcyBwcm90ZWluIGNvc3RzIGFuZCBnbG9iYWwgcHJvdGVpbiBwb29sIGxpbWl0YXRpb25zIGludG8gYWNjb3VudC4gSW4gY29uc3RyYXN0IHRvIHNpbXBsZSBGQkEgKGZsdXggYmFsYW5jZSBhbmFseXNpcyksIGdyb3d0aCByYXRlIGlzIHVsdGltYXRlbHkgY2FwcGVkIGJ5IHJhdGUgbGltaXRhdGlvbiBvZiBlbnp5bWVzIGFuZCBzcGFjZSBsaW1pdGF0aW9uIGZvciBlbnp5bWUgbWFzcyB3aXRoaW4gdGhlIGNlbGwsIG9yIHRoZSBjeXRvcGxhc21pYyBtZW1icmFuZS4gVGhlIG1vZGVsIHdhcyBjb25zdHJhaW5lZCB1c2luZyBleHBlcmltZW50YWxseSBkZXRlcm1pbmVkIHN0ZWFkeS1zdGF0ZSBncm93dGggcmF0ZXMgYW5kIHByb3RlaW4gYWJ1bmRhbmNlcy4KCkhlcmUsIHdlIGNhbiBhc2sgdGhlIHF1ZXN0aW9uIGlmLCBhbmQgdG8gd2hpY2ggZGVncmVlLCBncm93dGggcmF0ZSB3b3VsZCBpbmNyZWFzZSBieSBleHBhbmRpbmcgdGhlIHRvdGFsIHByb3RlaW4gcG9vbCBzaXplIChzaHJpbmtpbmcgcHJvdGVpbiBjb3N0KSBieSB0aGUgZnJhY3Rpb24gaW5kaWNhdGVkIGFib3ZlIChlLmcuICpob3hBKiBLTyBmcmVlaW5nIHVwIHRvIDQlIHByb3RlaW4gbWFzcykuIFRoZSBmaXJzdCBzdGVwIGlzIHRvIGltcG9ydCBSQkEgc2ltdWxhdGlvbiByZXN1bHRzLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30Kc291cmNlKHVybCgiaHR0cHM6Ly9naXRodWIuY29tL20tamFobi9SLW5vdGVib29rLXJhbHN0b25pYS1wcm90ZW9tZS9yYXcvbWFpbi9waXBlbGluZS9yZWFkX3JiYV9yZXN1bHQuUiIpKQpkaXJfc2ltIDwtICIuLi8uLi8uLi9Nb2RlbHMvQmFjdGVyaWFsLVJCQS1tb2RlbHMvUmFsc3RvbmlhLWV1dHJvcGhhLUgxNi9zaW11bGF0aW9uL2h5ZHJvZ2VuYXNlLyIKCmRmX2ZsdXggPC0gcmVhZF9yYmFfcmVzdWx0KGxpc3QuZmlsZXMoZGlyX3NpbSwgcGF0dGVybiA9ICJmbHV4ZXNfLioudHN2JCIsIGZ1bGwubmFtZXMgPSBUUlVFKSkKZGZfcHJvdCA8LSByZWFkX3JiYV9yZXN1bHQobGlzdC5maWxlcyhkaXJfc2ltLCBwYXR0ZXJuID0gInByb3RlaW5zXy4qLnRzdiIsIGZ1bGwubmFtZXMgPSBUUlVFKSkKZGZfbWFjciA8LSByZWFkX3JiYV9yZXN1bHQobGlzdC5maWxlcyhkaXJfc2ltLCBwYXR0ZXJuID0gIm1hY3JvcHJvY2Vzc2VzXy4qLnRzdiIsIGZ1bGwubmFtZXMgPSBUUlVFKSkKYGBgCgpUaGUgbmV4dCBzdGVwIGlzIHRvIHBsb3QgdGhlIGluY3JlYXNlIGluIGdyb3d0aCByYXRlIGFzIGEgZnVuY3Rpb24gb2YgcHJvdGVpbiByZXNvdXJjZXMgdGhhdCBoYXZlIGJlZW4gc2F2ZWQuIFRoZSBwcmVkaWN0aW9uIHNob3dzIHNtYWxsZXIgaW5jcmVhc2VzIHRoYW4gdGhlIGdyb3d0aCByYXRlcyBlc3RpbWF0ZWQgZnJvbSB0aGUgbGlicmFyeSBkYXRhLgoKLSAgIHJvdWdobHkgOCUgaW5jcmVhc2UgaW4gwrUgZm9yIDEwJSByZWR1Y3Rpb24gb2YgcHJvdGVpbiBjb3N0Ci0gICBsaWJyYXJ5IGRhdGEgc3VnZ2VzdCByb3VnaGx5IDQwJSBpbmNyZWFzZSBmb3IgNC01JSByZWR1Y3Rpb24gb2YgcHJvdGVpbiBjb3N0Ci0gICB0aGlzIGlzIG5vdCByZWFsaXN0aWMsIG1vc3QgbGlrZWx5IGFuIGFydGlmYWN0IG9mIGNhbGN1bGF0aW9uIGZyb20gTkdTIGRhdGEKCmBgYHtyLCBmaWcud2lkdGggPSAzLjcsIGZpZy5oZWlnaHQgPSAzLjd9CnBsb3RfY29zdF9wcmVkaWN0IDwtIGRmX21hY3IgJT4lCiAgbXV0YXRlKGNhcmJvbl9zb3VyY2UgPSByZWNvZGUoY2FyYm9uX3NvdXJjZSwKICAgIGBmb3JgID0gImZvcm1hdGUiLCBgc3VjY2AgPSAic3VjY2luYXRlIiwgYGZydWAgPSAiZnJ1Y3Rvc2UiKSkgJT4lCiAgbXV0YXRlKHNpbV9ydW4gPSBhcy5udW1lcmljKHNpbV9ydW4pKSAlPiUKICBhcnJhbmdlKGNhcmJvbl9zb3VyY2UsIHNpbV9ydW4pICU+JQogIGdyb3VwX2J5KGNhcmJvbl9zb3VyY2UsIGtleSkgJT4lCiAgbXV0YXRlKHJlZHVjZWRfcHJvdGVpbl9jb3N0ID0gMDoxMCkgJT4lCiAgZmlsdGVyKGtleSA9PSAibXUiKSAlPiUKICAKICB4eXBsb3QodmFsdWUgfiByZWR1Y2VkX3Byb3RlaW5fY29zdCwgLiwKICAgIGdyb3VwcyA9IGNhcmJvbl9zb3VyY2UsIGFzcGVjdCA9IDEsCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLAogICAgdHlwZSA9ICJiIiwgbHdkID0gMiwgbHR5ID0gMiwgcGNoID0gMSwKICAgIHlsaW0gPSBjKDAuMDk1LCAwLjEyNSksIGNvbCA9IHN0ZGNvbFsyOjRdLAogICAgeGxhYiA9ICJmcmVlZCB1cCBwcm90ZWluIHJlc291cmNlcyBbJSB0b3RhbF0iLAogICAgeWxhYiA9IGV4cHJlc3Npb24oIsK1IFtoIl4tMSoiXSIpLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCB6LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC54eXBsb3QoeCwgeSwgLi4uKQogICAgICBwYW5lbC5rZXkoLi4uLCBjZXggPSAwLjcsIGNvcm5lciA9IGMoMC45NiwgMC45NikpCiAgICAgIHBhbmVsLmtleShjKCJkYXRhIiwgIm1vZGVsIiksIGxpbmVzID0gVFJVRSwgbHR5ID0gYygxLDIpLAogICAgICAgIHBjaCA9IGMoMTksIDEpLCBjb2wgPSBncmV5KDAuNCksCiAgICAgICAgY2V4ID0gMC43LCBjb3JuZXIgPSBjKDAuOTYsIDAuNzIpKQogICAgfQogICkKCnByaW50KHBsb3RfY29zdF9wcmVkaWN0KQpgYGAKCiMjIEdyb3d0aCBhZHZhbnRhZ2Ugb2YgY2xlYW4sIGluLWZyYW1lIGh5ZHJvZ2VuYXNlIGRlbGV0aW9uIG11dGFudHMKCi0gYSBzZWxlY3Rpb24gb2YgaHlkcm9nZW5hc2UgbXV0YW50cyB3YXMgZ3Jvd24gaW4gYmlvcmVhY3RvcnMsIGJhdGNoIG1vZGUKLSB0b3RhbCB2b2x1bWUgd2FzIDUwIG1MIG1pbmltYWwgbWVkaXVtIHN1cHBsZW1lbnRlZCB3aXRoIDIgZy9MIGZydWN0b3NlICsgMSBnL0wgTkg0Q2wKLSBhZXJhdGlvbjogMTAwIG1ML21pbiBmb3IgYWxsIHR1YmVzCi0gdGVtcGVyYXR1cmUgMzBcKkMKLSBPRCA3MjBubSB3YXMgY29uc3RhbnRseSBtZWFzdXJlZCBldmVyeSAxNSBtaW4KLSBiaW9tYXNzIHlpZWxkIHdhcyBkZXRlcm1pbmVkIGJ5IGNvbGxlY3RpbmcgYWxsIGJpb21hc3MgYXQgdGhlIGVuZCBvZiB0aGUgZXhwZXJpbWVudCBhbmQgZGV0ZXJtaW5pbmcgRENXCgpgYGB7ciwgZmlnLndpZHRoID0gNi41LCBmaWcuaGVpZ2h0ID0gMi42NX0KZGZfZ3Jvd3RoX2Fzc2F5IDwtIHJlYWRfY3N2KCIuLi9kYXRhL2dyb3d0aF9hc3NheXMvZGZfbXVfaG94LmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCmRmX2Rjd19hc3NheSA8LSByZWFkX2NzdigiLi4vZGF0YS9ncm93dGhfYXNzYXlzL2RmX2Rjd19ob3guY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKcGxvdF9vcmRlciA8LSBjKCJXVCIsICJob3hHIiwgImh5cEIiLCAiaHlwWCIsICJob3hBIiwgImhveEgiKQoKcGxvdF9ncm93dGhfZGN3IDwtIGRmX2Rjd19hc3NheSAlPiUKICBtdXRhdGUoc2FtcGxlID0gZmFjdG9yKHNhbXBsZSwgcGxvdF9vcmRlcikpICU+JQogIHh5cGxvdCh5aWVsZF9nRENXX2dGUkMgfiBzYW1wbGUsIC4sCiAgICBwYXIuc2V0dGluZ3MgPSBjdXN0b20uY29sb3JibGluZCgpLAogICAgY29sID0gc3RkY29sWzNdLCBsd2QgPSAyLCB5bGltID0gYygtMC4wLCAwLjYpLAogICAgeGxhYiA9ICIiLCB5bGFiID0gZXhwcmVzc2lvbigieWllbGQgW2cgRENXIGcgRnJjIl4tMSoiXSIpLAogICAgc2NhbGVzID0gbGlzdCh4ID0gbGlzdChyb3QgPSA0NSkpLAogICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCB6LCAuLi4pIHsKICAgICAgcGFuZWwuZ3JpZChoID0gLTEsIHYgPSAtMSwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC54eXBsb3QoeCwgeSwgcGNoID0gMTksIGNleCA9IDAuNSwgY29sID0gZ3JleSgwLjYpKQogICAgICBwYW5lbC5iYXJwbG90KHgsIHksIC4uLikKICAgICAgcGFuZWwucHZhbHVlKHgsIHksIG9mZnNldCA9IDAuMTIsIC4uLikKICAgIH0KICApCgpwbG90X2dyb3d0aF9tdSA8LSBkZl9ncm93dGhfYXNzYXkgJT4lCiAgbXV0YXRlKHNhbXBsZSA9IGZhY3RvcihzYW1wbGUsIHBsb3Rfb3JkZXIpKSAlPiUKICB4eXBsb3QobXVfbWF4IH4gc2FtcGxlLCAuLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICAgIGNvbCA9IHN0ZGNvbFszXSwgbHdkID0gMiwgeWxpbSA9IGMoLTAuMCwgMC4zOSksCiAgICB4bGFiID0gIiIsIHlsYWIgPSBleHByZXNzaW9uKCLCtSBbaCJeLTEqIl0iKSwKICAgIHNjYWxlcyA9IGxpc3QoeCA9IGxpc3Qocm90ID0gNDUpKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgeiwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIHBjaCA9IDE5LCBjZXggPSAwLjUsIGNvbCA9IGdyZXkoMC42KSkKICAgICAgcGFuZWwuYmFycGxvdCh4LCB5LCAuLi4pCiAgICAgIHBhbmVsLnB2YWx1ZSh4LCB5LCBvZmZzZXQgPSAwLjA4LCAuLi4pCiAgICB9CiAgKQoKcHJpbnQocGxvdF9ncm93dGhfZGN3LCBwb3NpdGlvbiA9IGMoMCwgMCwgMC41LCAxKSwgbW9yZSA9IFRSVUUpCnByaW50KHBsb3RfZ3Jvd3RoX211LCBwb3NpdGlvbiA9IGMoMC41LCAwLCAxLCAxKSwgbW9yZSA9IFRSVUUpCmdyaWQ6OmdyaWQudGV4dChjKCJBIiwiQiIpLCB4ID0gYygwLjAzLCAwLjUpLCB5ID0gYygwLjk0LCAwLjk0KSwgZ3AgPSBncmlkOjpncGFyKGNleCA9IDEuMikpCmBgYAoKIyMgSHlkcm9nZW4gZXZvbHV0aW9uIG9mIFdUIGFuZCBoeWRyb2dlbmFzZSBtdXRhbnQKCi0gYW4gYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyB0byBwcm90ZWluIGNvc3QgYnkgaHlkcm9nZW5hc2VzIGlzIGVuZXJneSBjb3N0Ci0gdGhpcyBoeXBvdGhlc2lzIGJhc2ljYWxseSBzdGF0ZXMgdGhhdCB0aGUgZW56eW1hdGljIGFjdGlvbiBvZiBoeWRyb2dlbmFzZXMgInJ1bm5pbmcgcmV2ZXJzZSIgY2F0YWx5emVzIHRoZSBmb3JtYXRpb24gb2YgSDIgZnJvbSBOQURIIGFuZCBwcm90b25zIChhcyBrbm93biBmcm9tIGN5YW5vYmFjdGVyaWEpCi0gdGhpcyByZWFjdGlvbiB3b3VsZCBkcmFpbiB0aGUgcmVkdWN0YW50IHBvb2wgKE5BREgoUClIKSBhbmQgbGVhZCB0byByZWR1Y2VkIGdyb3d0aAotIHRoaXMgcmVhY3Rpb24gcGxheXMgYSByb2xlIHdoZW4gdGhlIHRlcm1pbmFsIGVsZWN0cm9uIGFjY2VwdG9yLCBveHlnZW4gb3Igb25lIG9mIHRoZSBuaXRyb2dlbiBveGlkZXMsIGFyZSBsaW1pdGluZwotIGluIG9yZGVyIHRvIGRldGVybWluZSBpZiBDLiBuZWNhdG9yIHNwaWxscyByZWR1Y3RhbnQgaW4gaGV0ZXJvdHJvcGhpYyBjb25kaXRpb25zIHRocm91Z2ggdGhlIGFjdGlvbiBvZiBoeWRyb2dlbmFzZXMsIFdUIGFuZCBhIG11dGFudCBkZWZlY3RpdmUgaW4gYWxsIGh5ZHJvZ2VuYXNlcyB3ZXJlIGN1bHRpdmF0ZWQgd2l0aCBmcnVjdG9zZSBhbmQgZ2x5Y2Vyb2wgdG8gaW5kdWNlIGh5ZHJvZ2VuYXNlIGV4cHJlc3Npb24KLSB3aXRob3V0IHN0cm9uZyBsaW1pdGF0aW9uIG9mIG94eWdlbiAod2hpY2ggd2FzIG5vdCBwcmVzZW50IGluIG91ciBiaW9yZWFjdG9yIGV4cGVyaW1lbnRzKSwgbm8gaHlkcm9nZW4gZXZvbHV0aW9uIHdhcyBkZXRlY3RhYmxlCi0gdGhpcyByZXN1bHQgc3RyZW5ndGhlbnMgdGhlIHByb3RlaW4gY29zdCBoeXBvdGhlc2lzCgoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSAzLjJ9CmRmX2gyX2V2b2x1dGlvbiA8LSByZWFkX2NzdigiLi4vZGF0YS9ncm93dGhfYXNzYXlzL2gyX2V2b2x1dGlvbi5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUKICBtdXRhdGUodGltZSA9IGlmZWxzZSh0aW1lID09IDIwLjUsIDEyLjUsIHRpbWUpKQoKcGxvdF9oMl9ldm9sdXRpb24gPC0geHlwbG90KG1lYW4gfiB0aW1lLAogICAgZmlsdGVyKGRmX2gyX2V2b2x1dGlvbiwgZ2FzID09ICJPMiIpLAogICAgZ3JvdXBzID0gc3RyYWluLAogICAgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICAgIGVycm9yX21hcmdpbiA9IGZpbHRlcihkZl9oMl9ldm9sdXRpb24sIGdhcyA9PSAiTzIiKSRzZCwKICAgIGx3ZCA9IDIsIHBjaCA9IDEsCiAgICB4bGFiID0gInRpbWUgW2hdIiwgeWxhYiA9IGV4cHJlc3Npb24oIk8iWzJdKiIgWyVdIiksCiAgICB5bGltID0gYygtMiwgMjIpLAogICAgc2NhbGVzID0gbGlzdCh4ID0gbGlzdChhdCA9IHNlcSgwLCAxMCwgMikpKSwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gMCwgY29sID0gZ3JleSgwLjkpKQogICAgICBwYW5lbC5hYmxpbmUodiA9IHNlcSgwLCAxMCwgMiksIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwueHlwbG90KHgsIHksIHR5cGUgPSAibCIsIGx0eSA9IDIsIC4uLikKICAgICAgcGFuZWwuZXJyYmFycyh4LCB5LCBld2lkdGggPSAwLCBsdHkgPSAxLCAuLi4pCiAgICAgIHBhbmVsLmtleSgKICAgICAgICBjb3JuZXIgPSBjKDAuOSwgMC45KSwKICAgICAgICBsYWJlbHMgPSBjKCLiiIZob3hHSEMs4oiGaG9mRyIsICJXVCIsICJPMiIsICJIMiIpLAogICAgICAgIGNvbCA9IGMoIiNFNzI5OEEiLCAiIzY2QTYxRSIsIGdyZXkoMC41KSwgZ3JleSgwLjUpKSwKICAgICAgICBwY2ggPSBjKDE5LCAxOSwgMTksIDEpCiAgICAgICkKICAgIH0KICApICsgYXMubGF5ZXIoCiAgICB4eXBsb3QobWVhbi8xMDAgfiB0aW1lLAogICAgICBmaWx0ZXIoZGZfaDJfZXZvbHV0aW9uLCBnYXMgPT0gIkgyIiksCiAgICAgIGdyb3VwcyA9IHN0cmFpbiwgcGNoID0gMTksIGx0eSA9IDEsCiAgICAgIGVycm9yX21hcmdpbiA9IGZpbHRlcihkZl9oMl9ldm9sdXRpb24sIGdhcyA9PSAiSDIiKSRzZC8xMDAsCiAgICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgICAgcGFuZWwueHlwbG90KHgsIHksIHR5cGUgPSAibCIsIC4uLikKICAgICAgICBwYW5lbC5lcnJiYXJzKHgsIHksIGV3aWR0aCA9IDAsIC4uLikKICAgICAgfQogICAgKQogICkKCnByaW50KHBsb3RfaDJfZXZvbHV0aW9uKQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoID0gNy41LCBmaWcuaGVpZ2h0ID0gMi44NX0Kc3ZnKCIuLi9maWd1cmVzL2ZpZ3VyZV9tdXRhbnRfbXUuc3ZnIiwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDIuODUpCnByaW50KHBsb3RfZ3Jvd3RoX2RjdywgcG9zaXRpb24gPSBjKC0wLjAyLCAtMC4wMzQsIDAuMzgsIDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9ncm93dGhfbXUsIHBvc2l0aW9uID0gYygwLjI5LCAtMC4wMzQsIDAuNjksIDEpLCBtb3JlID0gVFJVRSkKcHJpbnQocGxvdF9oMl9ldm9sdXRpb24sIHBvc2l0aW9uID0gYygwLjYxLCAwLCAxLCAxKSwgbW9yZSA9IFRSVUUpCmdyaWQ6OmdyaWQudGV4dChjKCJEIiwiRSIsICJGIiksIHggPSBjKDAuMDMsIDAuMzUsIDAuNjUpLAogIHkgPSBjKDAuOTQsIDAuOTQsIDAuOTQpLCBncCA9IGdyaWQ6OmdwYXIoY2V4ID0gMS4yKSkKZGV2Lm9mZigpCmBgYAoKIyMgUHJvdGVpbiBjb3N0IG9mIHNlY29uZGFyeSBoeWRyb2dlbmFzZXMKClRoaXMgZmlndXJlIHNob3dzIHByb3RlaW4gY29zdCBvZiB0aGUgaG94L2hvZiBnZW5lcyB0aGF0IHdlcmUgbm90IGluY2x1ZGVkIGluIHRoZSBwcm90ZWluIGNvc3QgYW5hbHlzaXM6CgotICAgdGhlIDR0aCAnYWN0aW5vYmFjdGVyaWFsJyBoeWRyb2dlbmFzZSwgMSBzdWJ1bml0IChob2ZHLCBQSEcwNjUpCgpgYGB7ciwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDN9ClJhbHN0b25pYV9ldXRyb3BoYSAlPiUgdW5ncm91cCAlPiUKICBmaWx0ZXIoZ3JlcGwoIlBIRzA2NSIsIHByb3RlaW4pLCBzdWJzdHJhdGUgIT0gImFtbW9uaXVtIikgJT4lCiAgCiAgeHlwbG90KG1lYW5fbWFzc19mcmFjdGlvbl9ub3JtKjEwMCB+IGZhY3Rvcihncm93dGhyYXRlKSB8IHByb3RlaW4sIC4sCiAgICBncm91cHMgPSBzdWJzdHJhdGUsIHBhci5zZXR0aW5ncyA9IGN1c3RvbS5jb2xvcmJsaW5kKCksCiAgICBlcnJvcl9tYXJnaW4gPSAuW1sic2RfbWFzc2ZyYWN0aW9uIl1dKjEwMCwKICAgIGx3ZCA9IDIsIGNvbCA9IHN0ZGNvbFsyOjRdLCB5bGltID0gYygtMC4wMDA1LCAwLjAwNDUpLCBwY2ggPSAxOSwKICAgIHhsYWIgPSAiIiwgeWxhYiA9ICJwcm90ZWluIG1hc3MgZnJhY3Rpb24gWyVdIiwKICAgIHBhbmVsID0gZnVuY3Rpb24oeCwgeSwgLi4uKSB7CiAgICAgIHBhbmVsLmdyaWQoaCA9IC0xLCB2ID0gLTEsIGNvbCA9IGdyZXkoMC45KSkKICAgICAgcGFuZWwuZXJyYmFycyh4LCB5LCBiZXNpZGUgPSBUUlVFLCAuLi4pCiAgICAgIHBhbmVsLmtleShjb3JuZXIgPSBjKDAuMDIsIDAuOTUpLCAuLi4pCiAgICB9CiAgKQpgYGAKCiMjIFByb3RlaW4gY29zdCBvZiBFVEMgY29tcGxleGVzCgotIGFzIGEgY29tcGxpbWVudGFyeSBpbmZvcm1hdGlvbiwgd2UgY2FuIGRldGVybWluZSB0aGUgbWFzcyBmcmFjdGlvbiBvZiBFVEMgY29tcGxleGVzIHVzaW5nIE1TIGRhdGEKLSB0aGVzZSBhcmUgbWVtYnJhbmUgcHJvdGVpbnMgYW5kIGhlbmNlIGxpa2VseSB1bmRlcmVzdGltYXRlZCBpbiBjb21wYXJpc29uIHRvIHNvbHVibGUvY3l0b3BsYXNtaWMgcHJvdGVpbnMKLSB0aGUgcmVhc29uIGlzIHRoZSBsb3dlciBlZmZpY2llbmN5IHdoZW4gZXh0cmFjdGluZyBtZW1icmFuZSBwcm90ZWlucwotIGEgcmVsYXRpdmUgY29tcGFyaXNvbiBpcyBuZXZlcnRoZWxlc3MgcG9zc2libGUKLSBzaG91bGQgYWxsb3cgdXMgdG8gc2VlIHRoZSByb3VnaCBvcmRlciBvZiBtYWduaXR1ZGUgb2YgRVRDIGNvbXBsZXhlcwoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA2fQpwbG90c19ldGNfbWFzcyA8LSBSYWxzdG9uaWFfZXV0cm9waGEgJT4lCiAgdW5ncm91cCAlPiUKICBzZWxlY3QobG9jdXNfdGFnLCBtZWFuX21hc3NfZnJhY3Rpb25fbm9ybSwgZ3Jvd3RocmF0ZSwgcHJvdGVpbiwgc3Vic3RyYXRlLCBzZF9tYXNzZnJhY3Rpb24pICU+JQogIGlubmVyX2pvaW4oCiAgICBzZWxlY3QoZGZfZXRjLCBsb2N1c190YWcsIGdlbmVfbmFtZSkgJT4lCiAgICAgIGRpc3RpbmN0ICU+JQogICAgICBtdXRhdGUoZ2VuZV9uYW1lID0gYXMuY2hhcmFjdGVyKGdlbmVfbmFtZSkpLAogICAgYnkgPSBqb2luX2J5KGxvY3VzX3RhZykpICU+JQogIGZpbHRlcihzdWJzdHJhdGUgIT0gImFtbW9uaXVtIiwgZ3Jvd3RocmF0ZSA9PSAwLjEsICFpcy5uYShnZW5lX25hbWUpLCAhc3RyX2RldGVjdChnZW5lX25hbWUsICJIMTZfIikpICU+JQogIG11dGF0ZShvcGVyb24gPSBzdHJfc3ViKGdlbmVfbmFtZSwgMSwgMykpICU+JQogIGdyb3VwX2J5KG9wZXJvbikgJT4lCiAgZ3JvdXBfc3BsaXQoKSAlPiUKICBsYXBwbHkoZnVuY3Rpb24oZGYpIHsKICAgIHh5cGxvdChtZWFuX21hc3NfZnJhY3Rpb25fbm9ybSoxMDAgfiBmYWN0b3IoZ2VuZV9uYW1lKSB8IG9wZXJvbiwgZGYsCiAgICAgIGdyb3VwcyA9IHN1YnN0cmF0ZSwgcGFyLnNldHRpbmdzID0gY3VzdG9tLmNvbG9yYmxpbmQoKSwKICAgICAgZXJyb3JfbWFyZ2luID0gZGYkc2RfbWFzc2ZyYWN0aW9uICogMTAwLAogICAgICBsd2QgPSAyLCBjb2wgPSBzdGRjb2xbMjo0XSwgeWxpbSA9IGMoLTAuMDA1LCAwLjA4NSksIHBjaCA9IDE5LAogICAgICBiZXR3ZWVuID0gbGlzdCh4ID0gMC41LCB5ID0gMC41KSwgYXMudGFibGUgPSBUUlVFLAogICAgICBzY2FsZXMgPSBsaXN0KGFsdGVybmF0aW5nID0gRkFMU0UsIHggPSBsaXN0KHJvdCA9IDkwKSksCiAgICAgIHhsYWIgPSAiIiwgeWxhYiA9ICJwcm90ZWluIG1hc3MgZnJhY3Rpb24gWyVdIiwKICAgICAgcGFuZWwgPSBmdW5jdGlvbih4LCB5LCAuLi4pIHsKICAgICAgICBwYW5lbC5ncmlkKGggPSAtMSwgdiA9IC0xLCBjb2wgPSBncmV5KDAuOSkpCiAgICAgICAgcGFuZWwuZXJyYmFycyh4LCB5LCBiZXNpZGUgPSBUUlVFLCAuLi4pCiAgICAgICAgcGFuZWwua2V5KGNvcm5lciA9IGMoMC4wMiwgMC45NSksIC4uLikKICAgICAgfQogICAgKQogIH0pCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZSgKICBuY29sID0gMywgbnJvdyA9IDIsCiAgcGxvdHNfZXRjX21hc3NbWzVdXSwKICBwbG90c19ldGNfbWFzc1tbNF1dLAogIHBsb3RzX2V0Y19tYXNzW1s2XV0sCiAgcGxvdHNfZXRjX21hc3NbWzJdXSwKICBwbG90c19ldGNfbWFzc1tbM11dLAogIHBsb3RzX2V0Y19tYXNzW1sxXV0KKQpgYGAKCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpzdmcoIi4uL2ZpZ3VyZXMvZmlndXJlX2V0Y19tYXNzLnN2ZyIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoCiAgbmNvbCA9IDMsIG5yb3cgPSAyLAogIHBsb3RzX2V0Y19tYXNzW1s1XV0sCiAgcGxvdHNfZXRjX21hc3NbWzRdXSwKICBwbG90c19ldGNfbWFzc1tbNl1dLAogIHBsb3RzX2V0Y19tYXNzW1syXV0sCiAgcGxvdHNfZXRjX21hc3NbWzNdXSwKICBwbG90c19ldGNfbWFzc1tbMV1dCikKZGV2Lm9mZigpCmBgYAoKIyBFeHBvcnQgcHJvY2Vzc2VkIGRhdGEKCi0gZXhwb3J0IGZpdG5lc3MgZGF0YSBpbiBhIGZvcm1hdCB0aGF0IGlzIHN1aXRhYmxlIGZvciBbU2hpbnlMaWJdKGh0dHBzOi8vbS1qYWhuLnNoaW55YXBwcy5pby9TaGlueUxpYi8pCi0gd2UgZXhwb3J0IGVudGlyZSBkYXRhIHNldCBmb3IgYmVzdCBjb21wYXJpc29uIG9wdGlvbnMsIGV2ZW4gdGhvdWdoIGZpcnN0IHBhcnQgKGNhcmJvbiBzb3VyY2VzKSB3YXMgcHVibGlzaGVkIGVhbGllciBhcyBKYWhuIGV0IGFsLiwgZUxpZmUsIDIwMjEKCmBgYHtyfQpDdXByaWF2aWR1c19CYXJTZXFfMjAyMyA8LSBkZl9maXRuZXNzICU+JQogIHJlbmFtZSh0aW1lcG9pbnQgPSB0aW1lLCBmaXRuZXNzX3Njb3JlID0gbm9ybV9nZW5lX2ZpdG5lc3MsIHRfc3RhdCA9IHQpICU+JQogIG11dGF0ZSgKICAgIGluZHVjdGlvbiA9ICJub3QgYXBwbGljYWJsZSIsCiAgICBsb2cyRkMgPSByZXBsYWNlKGxvZzJGQywgaXMuaW5maW5pdGUobG9nMkZDKSwgTkEpLAogICAgZm9sZF9jaGFuZ2UgPSAyXmxvZzJGQywKICAgIGNvbmRpdGlvbiA9IHBhc3RlMChzdWJzdHJhdGUsICIgLSAiLCBjb25kaXRpb24pCiAgKSAlPiUKICBzZWxlY3QoLUlEKSAlPiUKICBhcnJhbmdlKGxvY3VzX3RhZywgY29uZGl0aW9uLCB0aW1lcG9pbnQpICU+JQogIGxlZnRfam9pbihieSA9ICJsb2N1c190YWciLAogICAgZGZfcmVmICU+JQogICAgICBzZWxlY3QoCiAgICAgICAgbG9jdXNfdGFnLCBuZXdfbG9jdXNfdGFnLAogICAgICAgIHVuaXByb3QsIHByb3RlaW5fbmFtZSwgZ2VuZV9uYW1lLAogICAgICAgIFN5c3RlbSwgUHJvY2VzcywgUGF0aHdheQogICAgICApCiAgKQoKIyBleHBvcnQgdG8gUkRhdGEKc2F2ZShDdXByaWF2aWR1c19CYXJTZXFfMjAyMywgZmlsZSA9ICIuLi9kYXRhL2JhcnNlcS9DdXByaWF2aWR1c19CYXJTZXFfMjAyMy5SZGF0YSIpCmBgYAoKCiMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK