Data preparation

The otu table with contaminants removed is convert to long format and a column for genus names is created by extracting the first word of the species designation.

# convert decontaminated taxatable to long format
otu_table_long <- otu_table_decontam %>%
  pivot_longer(
    cols = where(is.numeric),
    names_to = "sample",
    values_to = "count"
    ) %>%
  rename(species = `#OTU ID`) %>%
  mutate(genus = str_extract(species, "\\w+"))

Counts are converted to relative abundance per sample. Separate data frames are generated for species and genus levels.

species_abund_long <- otu_table_long %>%
  group_by(sample) %>%
  mutate(rel_abund = count / sum(count))

genus_abund_long <- species_abund_long %>%
  group_by(sample, genus) %>%
  summarise(
    count = sum(count),
    rel_abund = sum(rel_abund)
    )
`summarise()` has grouped output by 'sample'. You can override using the `.groups` argument.
# top 20 common genera in experiment
top_genus <- genus_abund_long %>%
  filter(str_detect(sample, "SYN")) %>% 
  ungroup() %>%
  arrange(desc(rel_abund)) %>%
  distinct(genus) %>%
  slice_head(n = 30) %>%
  left_join(genus_abund_long, by = "genus")

# top 20 species in experiment
top_species <- species_abund_long %>%
  filter(str_detect(sample, "SYN")) %>% 
  ungroup() %>%
  arrange(desc(rel_abund)) %>%
  distinct(species) %>%
  slice_head(n = 30) %>%
  left_join(species_abund_long, by = "species")

Information on the oxygen tolerance of bacterial species was downloaded from BacDive on . A list of amylase-binding streptococci (ABS) was created based on Nikitkova et al. 2013.

The data frame of bacterial properties was combined with the list of species from the analysis.

species_properties <- bac_properties %>%
  right_join(species_abund_long, by = "species") %>%
  mutate(abs = if_else(is.na(abs), FALSE, abs))

Distribution of oxygen tolerance across all experimental and reference samples based on information from BacDive.

species_properties %>%
  distinct(species, .keep_all = T) %>% 
  count(`Oxygen tolerance`) %>%
  mutate(`Oxygen tolerance` = replace_na(`Oxygen tolerance`, "unknown"),
         perc = scales::percent(n / sum(n), accuracy = 0.1)) %>%
  ggplot(aes(x = "", y = n, fill = `Oxygen tolerance`)) +
    geom_col(colour = "white") + 
    coord_polar(theta = "y") +
    geom_text(aes(label = perc), position = position_stack(vjust = 0.5), col = "white") +
    theme_void() +
    scale_fill_manual(
      values = c(viridisLite::turbo(n = length(levels(as.factor(species_properties$`Oxygen tolerance`)))), "darkgrey"))

Colour legends

Legend for text-colour based on sample type.

col_seq <- 1:length(levels(as.factor(analysis_colours$env_col)))
env_col <- na.omit(unique(analysis_colours$env_col))
env_label <- filter(
  analysis_colours,
  Env == "saliva" |
  Env == "byoc_calculus" |
  Env == "medium" |
  Env == "buccal_mucosa" |
  Env == "subgingival_plaque" |
  Env == "supragingival_plaque" |
  Env == "modern_calculus" |
    Env == "vitro_biofilm"
  ) %>%
  .$Env %>%
  unique()
qplot(y= col_seq, x = 1, fill=factor(col_seq), geom="tile") +
  scale_fill_manual(values = env_col) +
  geom_label(aes(label = env_label), col = "white") +
  theme_void() +
  theme(legend.position="none")
Warning: `qplot()` was deprecated in ggplot2 3.4.0.

Legend for text-colour based on treatment groups from the experiment.

col_seq <- 1:length(levels(as.factor(experiment_colours$treat_col)))
qplot(y= col_seq, x = 1, fill=factor(col_seq), geom="tile") +
  scale_fill_manual(values = na.omit(unique(experiment_colours$treat_col))) +
  geom_label(aes(label = unique(experiment_colours$treatment)), col = "white") +
  theme_void() +
  theme(legend.position="none")

Within the experiment

Plotting relative species and genus abundances across experimental samples only. Samples are ordered from left to right by sampling day.

Genus composition

#genus_abund_long
top_genus %>% 
  filter(sample %in% experiment_metadata$`#SampleID`) %>%
  ggplot(aes(x = sample, y = genus, fill = log(rel_abund + 0.001))) +
    geom_tile() +
    theme_void() +
    scale_x_discrete(limits = rev(day_order)) + # reverse day_order vector
    #my_scale2 +
    theme(
      legend.position = "top",
      axis.text = element_text(),
      axis.text.x = element_text(
        angle = 60,
        hjust = 1,
        vjust = 1,
        colour = rev(experiment_colours$env_col)
        )
      ) +
    scale_fill_viridis_c(option = "B")
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

Species composition

Only including the name of the top 20 species (relative abundance) within each sample.

top_species %>% 
  filter(sample %in% experiment_metadata$`#SampleID`) %>%
  ggplot(aes(x = sample, y = species, fill = log(rel_abund + 0.001))) +
    geom_tile() +
    theme_void() +
    scale_x_discrete(limits = rev(day_order)) + # reverse day_order vector
    #my_scale2 +
    theme(
      legend.position = "top",
      axis.text = element_text(),
      axis.text.x = element_text(
        angle = 60,
        hjust = 1,
        vjust = 1,
        colour = rev(experiment_colours$env_col)
        )
      ) +
    scale_fill_viridis_c(option = "B")
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

clr_byoc_long %>% 
  filter(#sample %in% experiment_metadata$`#SampleID`,
         str_detect(species, "Streptococcus|Actinomyces|Fusobacterium|Haemo")) %>% # early colonisers
  ggplot(aes(x = sample, y = species, fill = clr_count)) +
    geom_tile() +
    theme_void() +
    scale_x_discrete(limits = rev(day_order)) + # reverse day_order vector
    #my_scale2 +
    theme(
      legend.position = "top",
      axis.text = element_text(),
      axis.text.x = element_text(
        angle = 60,
        hjust = 1,
        vjust = 1,
        colour = rev(experiment_colours$env_col)
        )
      ) +
    scale_fill_viridis_c(option = "B")
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

clr_byoc_long %>% 
  filter(#sample %in% experiment_metadata$`#SampleID`,
         str_detect(species, "Fusobacterium|Eubacterium|Treponema|Tannerella|Prevotella")) %>% # Late colonisers
  ggplot(aes(x = sample, y = species, fill = clr_count)) +
    geom_tile() +
    theme_void() +
    scale_x_discrete(limits = rev(day_order)) + # reverse day_order vector
    #my_scale2 +
    theme(
      legend.position = "top",
      axis.text = element_text(),
      axis.text.x = element_text(
        angle = 60,
        hjust = 1,
        vjust = 1,
        colour = rev(experiment_colours$env_col)
        )
      ) +
    scale_fill_viridis_c(option = "B")
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

Decrease in oral streptococci over the course of the experiment is expected in biofilm growth. Actinomyces spp. were more abundant in saliva and in the final calculus product than early biofilm samples.

Oxygen tolerance

no_match <- unique(filter(species_properties, is.na(`Oxygen tolerance`))$species) # which species had no match?

Proportional distribution of oxygen tolerances across experimental samples. Data on oxygen tolerance obtained from BacDive. A total of 629 out of 1129 (55.7%) species did not have a match on BacDive.

species_properties %>% 
  filter(sample %in% experiment_metadata$`#SampleID`) %>% 
  ggplot(aes(y = sample, x = rel_abund, fill = `Oxygen tolerance`)) +
    geom_col() +
    theme_minimal() +
    #theme(axis.text.x = element_text(angle = 90)) +
    scale_y_discrete(limits = day_order) +
    scale_fill_manual(
      values = viridisLite::turbo(n = length(levels(as.factor(species_properties$`Oxygen tolerance`))))) +
  theme(
    axis.text.y = element_text(colour = experiment_colours$env_col)
  )
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

Generally an increase of anaerobes over the course of the experiment, at the expense of facultative anaerobes. Low proportion of aerobes throughout the experiment.

Amylase-binding species

Relative abundance of ABS per sample. Different treatments indicated by text colour.

species_properties %>% 
  filter(str_detect(sample, "SYN")) %>%#,
         #str_detect(sample, "SYN00[1-3]", negate = T)) %>% 
  ggplot(aes(y = sample, x = rel_abund, fill = abs)) +
    geom_col() +
    theme_minimal() +
    #theme(axis.text.x = element_text(angle = 90)) +
    scale_y_discrete(limits = day_order) +
    scale_fill_viridis_d(option = "E") +
    theme(
      axis.text.y = element_text(
        colour = experiment_colours %>%
          arrange(desc(day)) %>%
          .$treat_col))
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

experiment_metadata %>%
  dplyr::select(`#SampleID`, treatment)

The presence of ABS is unrelated to the presence of starch. The overall proportion of ABS decreases over the course of the experiment. The absence of amylase in the model may reduce the need for species capable of starch hydrolysis, especially when sucrose was available in all samples.

Comparative samples

Looking at relative abundances at species and genus levels of calculus and plaque samples. Top 30 taxa (relative abundance) were plotted.

Genus composition

genus_abund_long %>%
  filter(sample %in% filter(analysis_metadata, str_detect(Env, "calculus|plaque"))$`#SampleID`) %>% 
  ungroup() %>%
  arrange(desc(rel_abund)) %>%
  distinct(genus) %>%
  slice_head(n = 30) %>% # top 30 species across all samples
  left_join(species_abund_long, by = "genus") %>% # recombine with samples
  filter(sample %in% filter(analysis_metadata, str_detect(Env, "calculus|plaque"))$`#SampleID`) %>% #need to filter out samples again
  ggplot(aes(x = sample, y = genus, fill = log(rel_abund + 0.0001))) +
    geom_tile() +
    theme_void() +
    #my_scale2 +
    theme(
      legend.position = "top",
      axis.text = element_text(),
      axis.text.x = element_text(
        angle = 60,
        hjust = 1,
        vjust = 1,
        colour = filter(
            analysis_colours, str_detect(Env, "calculus|plaque")
            )$env_col %>%
          rev()
        )
      ) +
    scale_fill_viridis_c(option = "B")
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

Species composition

Relative abundance

# relative abundance
species_abund_long %>%
  filter(sample %in% filter(analysis_metadata, str_detect(Env, "calculus|plaque"))$`#SampleID`) %>% 
  ungroup() %>%
  arrange(desc(rel_abund)) %>%
  distinct(species) %>%
  slice_head(n = 30) %>% # top 30 species across all samples
  left_join(species_abund_long, by = "species") %>% # recombine with samples
  filter(sample %in% filter(analysis_metadata, str_detect(Env, "calculus|plaque"))$`#SampleID`) %>% #need to filter out samples again
  ggplot(aes(x = sample, y = species, fill = log(rel_abund + 0.001))) +
    geom_tile() +
    theme_void() +
    #my_scale2 +
    theme(
      legend.position = "top",
      axis.text = element_text(),
      axis.text.x = element_text(
        angle = 60,
        hjust = 1,
        vjust = 1,
        colour = filter(
            analysis_colours, str_detect(Env, "calculus|plaque")
            )$env_col
        )
      ) +
    scale_fill_viridis_c(option = "B")
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

Centered ratio log-transform abundance ordered by PC1 loadings.

# Top positive loadings on PC1
species_pos_pc1 +
    scale_fill_viridis_c() +
    theme(
      axis.title.y = element_blank(),
      axis.title.x = element_blank(),
      axis.text.x = element_text(
        angle = 90, size = 8, vjust = 0.5, hjust = 1,
        colour = analysis_colours %>%
          right_join(
            as_tibble(spca_species$x, rownames = "#SampleID"), 
            by = "#SampleID") %>%
          arrange(PC1) %>% 
          .$env_col
      )
      )
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

Enterococcus faecalis, Enterococcus casseliflavus, and Enterococcus durans are more abundant in the in vitro biofilm samples than in vivo samples of plaque and calculus. Conversely, Neisseria spp. and Actinomyces spp. are deficient in the in vitro samples.

Early colonisers

clr_compar_long %>%
  filter(str_detect(species, "Streptococcus|Actinomyces|Fusobacterium")) %>% 
  ggplot(aes(x = sample, y = species, fill = clr_count)) +
    geom_tile() +
    scale_fill_viridis_c(option = "C") +
    theme(axis.text.x = element_text(angle = 90, size = 6))

Late colonisers

clr_compar_long %>%
  filter(str_detect(species, "Fusobacterium|Eubacterium|Treponema|Tannerella|Prevotella")) %>% 
  ggplot(aes(x = sample, y = species, fill = clr_count)) +
    geom_tile() +
    scale_fill_viridis_c(option = "C") +
    theme(axis.text.x = element_text(angle = 90, size = 6))

Oxygen tolerance

Proportional distribution of oxygen tolerance across artificial calculus and oral reference samples.

species_properties %>% 
  filter(sample %in% filter(analysis_metadata, str_detect(Env, "calculus|plaque"))$`#SampleID`) %>% 
  #mutate(`Oxygen tolerance` = forcats::fct_explicit_na(`Oxygen tolerance`)) %>%
  ggplot(aes(y = sample, x = rel_abund, fill = `Oxygen tolerance`)) +
    geom_col() +
    theme_minimal() +
    #scale_fill_viridis_d(option = "H") +
    scale_fill_manual(
      values = viridisLite::turbo(n = length(levels(as.factor(species_properties$`Oxygen tolerance`))))) +
  theme(
    axis.text.y = element_text(
        colour = analysis_colours %>%
          filter(str_detect(Env, "calculus|plaque")) %>%
          #arrange(desc(day)) %>%
          .$env_col %>%
      rev()
    )
  )
Warning: Vectorized input to `element_text()` is not officially supported.
ℹ Results may be unexpected or may change in future versions of ggplot2.

Most notable difference is the absence of aerobes in the artificial calculus samples. Modern calculus contains a large proportion of species where oxygen tolerance is unknown (NA).

Amylase-binding species

species_properties %>% 
  filter(sample %in% filter(analysis_metadata, str_detect(Env, "calculus|plaque"))$`#SampleID`) %>% 
  ggplot(aes(y = sample, x = rel_abund, fill = abs)) +
    geom_col() +
    theme_minimal() +
    scale_fill_viridis_d(option = "E")

    #theme(axis.text.x = element_text(angle = 90)) +

Amylase-binding species are seemingly underrepresented in the artificial calculus samples compared to the modern calculus and plaque samples.

Discussion

Very few aerobes made it into the model. Apart from the donated saliva, very few aerobes were detected in any of the experimental samples. Rothia spp. disappeared between saliva and medium samples. We may need to introduce more oxygen into the protocol, or increase the frequency of medium replacement (e.g. every two days instead of three).

ABS are also underrepresented in the experimental samples compared to the modern calculus and plaque reference samples. The high relative abundance of E. faecalis may represent contamination despite it being commonly found in the oral cavity. There was a relatively low frequency of ABS in the artificial samples compared to the reference samples, which may be attributed to the presence of sucrose in the treatment solutions, potentially eliminating the need for ABS.

LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGhlcmUpCm1ldGFkYXRhIDwtIHJlYWRyOjpyZWFkX3RzdihoZXJlKCIwMS1kb2N1bWVudGF0aW9uL21ldGFkYXRhLnRzdiIpKQphbmFseXNpc19tZXRhZGF0YSA8LSByZWFkcjo6cmVhZF90c3YoaGVyZSgiMDEtZG9jdW1lbnRhdGlvbi9hbmFseXNpcy1tZXRhZGF0YS50c3YiKSkKZXhwZXJpbWVudF9tZXRhZGF0YSA8LSByZWFkcjo6cmVhZF90c3YoaGVyZSgiMDEtZG9jdW1lbnRhdGlvbi9leHBlcmltZW50LW1ldGFkYXRhLnRzdiIpKSAlPiUKICBmaWx0ZXIoYCNTYW1wbGVJRGAgJWluJSBhbmFseXNpc19tZXRhZGF0YSRgI1NhbXBsZUlEYCkKYmFjX3Byb3BlcnRpZXMgPC0gcmVhZHI6OnJlYWRfdHN2KGhlcmUoIjAxLWRvY3VtZW50YXRpb24vc3BlY2llcy1wcm9wZXJ0aWVzLnRzdiIpKQpvdHVfdGFibGVfZGVjb250YW0gPC0gcmVhZHI6OnJlYWRfdHN2KGhlcmUoIjA1LXJlc3VsdHMvcG9zdC1kZWNvbnRhbV90YXhhdGFibGUudHN2IikpIApwY2FfbG9hZGluZ3MgPC0gcmVhZHI6OnJlYWRfdHN2KGhlcmUoIjA1LXJlc3VsdHMvYWxsLXBjYS1sb2FkaW5ncy50c3YiKSkKbG9hZChoZXJlKCIwNS1yZXN1bHRzL3NwY2Ffc3BlY2llcy5yZGEiKSkKCmNscl9jb21wYXJfbG9uZyA8LSByZWFkcjo6cmVhZF90c3YoaGVyZSgiMDUtcmVzdWx0cy9jbHItY29tcGFyLnRzdiIpKSAlPiUKICBwaXZvdF9sb25nZXIoLXNhbXBsZSwgdmFsdWVzX3RvID0gImNscl9jb3VudCIsIG5hbWVzX3RvID0gInNwZWNpZXMiKQoKY2xyX2J5b2NfbG9uZyA8LSByZWFkcjo6cmVhZF90c3YoaGVyZSgiMDUtcmVzdWx0cy9jbHItYnlvYy50c3YiKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKC1zYW1wbGUsIHZhbHVlc190byA9ICJjbHJfY291bnQiLCBuYW1lc190byA9ICJzcGVjaWVzIikKCiMgb2JqZWN0IHRvIG9yZGVyIHBsb3RzIGJ5IHNhbXBsaW5nIGRheQpkYXlfb3JkZXIgPC0gZXhwZXJpbWVudF9tZXRhZGF0YSAlPiUKICAjZmlsdGVyKGAjU2FtcGxlSURgICVpbiUgYW5hbHlzaXNfbWV0YWRhdGEkYCNTYW1wbGVJRGApICU+JQogICNmaWx0ZXIoc3RyX2RldGVjdChgI1NhbXBsZUlEYCwgIlNZTiIpKSAlPiUgIyBvbmx5IGluY2x1ZGUgc2FtcGxlcyBmcm9tIHRoaXMgc3R1ZHkKICBhcnJhbmdlKGRlc2MoZGF5KSkgJT4lICMgb3JkZXIgYnkgc2FtcGxpbmcgZGF5IChhc2NlbmRpbmcpCiAgLiRgI1NhbXBsZUlEYCAjIGlzb2xhdGUgc2FtcGxlIG5hbWVzCgojIG15X3BhbGxldHRlMiA8LSBjKHZpcmlkaXNMaXRlOjptYWdtYShuID0gbGVuZ3RoKHRvcF9nZW51c19uYW1lcykpLCAiZ3JleSIpIAojIG5hbWVzKG15X3BhbGxldHRlMikgPC0gbGV2ZWxzKHRvcF9hYnVuZGFuY2VfZ2VudXMkdG9wX2dlbnVzKQojIG15X3NjYWxlMiA8LSBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gInRvcF9nZW51cyIsIHZhbHVlcyA9IG15X3BhbGxldHRlMikKCnRyZWF0bWVudF9jb2xvdXJzIDwtIHZpcmlkaXNMaXRlOjppbmZlcm5vKG4gPSBsZW5ndGgodW5pcXVlKGV4cGVyaW1lbnRfbWV0YWRhdGEkdHJlYXRtZW50KSkpICMgZGVmaW5lIHBhbGxldHRlIGFuZCBudW1iZXIgb2YgY29sb3VycwplbnZfY29sb3VycyA8LSB2aXJpZGlzTGl0ZTo6bWFnbWEobiA9IGxlbmd0aCh1bmlxdWUoYW5hbHlzaXNfbWV0YWRhdGEkRW52KSkpICMgZGVmaW5lIHBhbGxldHRlIGFuZCBudW1iZXIgb2YgY29sb3VycwoKZXhwZXJpbWVudF9jb2xvdXJzIDwtIGV4cGVyaW1lbnRfbWV0YWRhdGEgJT4lCiAgYXJyYW5nZShkZXNjKGRheSkpICU+JSAKICBtdXRhdGUodHJlYXRfY29sID0gY2FzZV93aGVuKAogICAgICAgICAgIHRyZWF0bWVudCA9PSAicG90YXRvIiB+IHRyZWF0bWVudF9jb2xvdXJzWzFdLAogICAgICAgICAgIHRyZWF0bWVudCA9PSAid2hlYXQiIH4gdHJlYXRtZW50X2NvbG91cnNbMl0sCiAgICAgICAgICAgdHJlYXRtZW50ID09ICJtaXgiIH4gdHJlYXRtZW50X2NvbG91cnNbM10sCiAgICAgICAgICAgdHJlYXRtZW50ID09ICJjb250cm9sIiB+IHRyZWF0bWVudF9jb2xvdXJzWzRdLAogICAgICAgICAgIGlzLm5hKHRyZWF0bWVudCkgfiAiZ3JleSIpLAogICAgICAgICBlbnZfY29sID0gY2FzZV93aGVuKAogICAgICAgICAgIEVudiA9PSAic2FsaXZhIiB+IGVudl9jb2xvdXJzWzVdLAogICAgICAgICAgIEVudiA9PSAiYnlvY19jYWxjdWx1cyIgfiBlbnZfY29sb3Vyc1sxXSwKICAgICAgICAgICBFbnYgPT0gIm1lZGl1bSIgfiBlbnZfY29sb3Vyc1s4XQogICAgICAgICApKQphbmFseXNpc19jb2xvdXJzIDwtIGFuYWx5c2lzX21ldGFkYXRhICU+JQogIG11dGF0ZShlbnZfY29sID0gY2FzZV93aGVuKAogICAgRW52ID09ICJzYWxpdmEiIH4gZW52X2NvbG91cnNbNV0sCiAgICBFbnYgPT0gImJ5b2NfY2FsY3VsdXMiIH4gZW52X2NvbG91cnNbMV0sCiAgICBFbnYgPT0gIm1lZGl1bSIgfiBlbnZfY29sb3Vyc1s4XSwKICAgIEVudiA9PSAiYnVjY2FsX211Y29zYSIgfiBlbnZfY29sb3Vyc1s2XSwKICAgIEVudiA9PSAic3ViZ2luZ2l2YWxfcGxhcXVlIiB+IGVudl9jb2xvdXJzWzJdLAogICAgRW52ID09ICJzdXByYWdpbmdpdmFsX3BsYXF1ZSIgfiBlbnZfY29sb3Vyc1szXSwKICAgIEVudiA9PSAibW9kZXJuX2NhbGN1bHVzIiB+IGVudl9jb2xvdXJzWzRdLAogICAgRW52ID09ICJ2aXRyb19iaW9maWxtIiB+IGVudl9jb2xvdXJzWzddCiAgICAgICAgICkpCmBgYAoKIyBEYXRhIHByZXBhcmF0aW9uCgpUaGUgb3R1IHRhYmxlIHdpdGggY29udGFtaW5hbnRzIHJlbW92ZWQgaXMgY29udmVydCB0byBsb25nIGZvcm1hdCBhbmQgYSBjb2x1bW4gZm9yCmdlbnVzIG5hbWVzIGlzIGNyZWF0ZWQgYnkgZXh0cmFjdGluZyB0aGUgZmlyc3Qgd29yZCBvZiB0aGUgc3BlY2llcyBkZXNpZ25hdGlvbi4KCmBgYHtyfQojIGNvbnZlcnQgZGVjb250YW1pbmF0ZWQgdGF4YXRhYmxlIHRvIGxvbmcgZm9ybWF0Cm90dV90YWJsZV9sb25nIDwtIG90dV90YWJsZV9kZWNvbnRhbSAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gd2hlcmUoaXMubnVtZXJpYyksCiAgICBuYW1lc190byA9ICJzYW1wbGUiLAogICAgdmFsdWVzX3RvID0gImNvdW50IgogICAgKSAlPiUKICByZW5hbWUoc3BlY2llcyA9IGAjT1RVIElEYCkgJT4lCiAgbXV0YXRlKGdlbnVzID0gc3RyX2V4dHJhY3Qoc3BlY2llcywgIlxcdysiKSkKYGBgCgpDb3VudHMgYXJlIGNvbnZlcnRlZCB0byByZWxhdGl2ZSBhYnVuZGFuY2UgcGVyIHNhbXBsZS4gU2VwYXJhdGUgZGF0YSBmcmFtZXMgYXJlCmdlbmVyYXRlZCBmb3Igc3BlY2llcyBhbmQgZ2VudXMgbGV2ZWxzLgoKYGBge3J9CnNwZWNpZXNfYWJ1bmRfbG9uZyA8LSBvdHVfdGFibGVfbG9uZyAlPiUKICBncm91cF9ieShzYW1wbGUpICU+JQogIG11dGF0ZShyZWxfYWJ1bmQgPSBjb3VudCAvIHN1bShjb3VudCkpCgpnZW51c19hYnVuZF9sb25nIDwtIHNwZWNpZXNfYWJ1bmRfbG9uZyAlPiUKICBncm91cF9ieShzYW1wbGUsIGdlbnVzKSAlPiUKICBzdW1tYXJpc2UoCiAgICBjb3VudCA9IHN1bShjb3VudCksCiAgICByZWxfYWJ1bmQgPSBzdW0ocmVsX2FidW5kKQogICAgKQoKIyB0b3AgMjAgY29tbW9uIGdlbmVyYSBpbiBleHBlcmltZW50CnRvcF9nZW51cyA8LSBnZW51c19hYnVuZF9sb25nICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHNhbXBsZSwgIlNZTiIpKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZGVzYyhyZWxfYWJ1bmQpKSAlPiUKICBkaXN0aW5jdChnZW51cykgJT4lCiAgc2xpY2VfaGVhZChuID0gMzApICU+JQogIGxlZnRfam9pbihnZW51c19hYnVuZF9sb25nLCBieSA9ICJnZW51cyIpCgojIHRvcCAyMCBzcGVjaWVzIGluIGV4cGVyaW1lbnQKdG9wX3NwZWNpZXMgPC0gc3BlY2llc19hYnVuZF9sb25nICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHNhbXBsZSwgIlNZTiIpKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZGVzYyhyZWxfYWJ1bmQpKSAlPiUKICBkaXN0aW5jdChzcGVjaWVzKSAlPiUKICBzbGljZV9oZWFkKG4gPSAzMCkgJT4lCiAgbGVmdF9qb2luKHNwZWNpZXNfYWJ1bmRfbG9uZywgYnkgPSAic3BlY2llcyIpCmBgYAoKSW5mb3JtYXRpb24gb24gdGhlIG94eWdlbiB0b2xlcmFuY2Ugb2YgYmFjdGVyaWFsIHNwZWNpZXMgd2FzIGRvd25sb2FkZWQgZnJvbSAKW0JhY0RpdmVdKGh0dHBzOi8vYmFjZGl2ZS5kc216LmRlKSBvbgpgciBzdHJfZXh0cmFjdChsaXN0LmZpbGVzKCIuLi8wMy1kYXRhLyIsIHBhdHRlcm4gPSAiYmFjZGl2ZSIpLCAiXFxkezR9LVxcZHsyfS1cXGR7Mn0iKWAuCkEgbGlzdCBvZiBhbXlsYXNlLWJpbmRpbmcgc3RyZXB0b2NvY2NpIChBQlMpIHdhcyBjcmVhdGVkIGJhc2VkIG9uIE5pa2l0a292YSBldCBhbC4KMjAxMy4KClRoZSBkYXRhIGZyYW1lIG9mIGJhY3RlcmlhbCBwcm9wZXJ0aWVzIHdhcyBjb21iaW5lZCB3aXRoIHRoZSBsaXN0IG9mIHNwZWNpZXMKZnJvbSB0aGUgYW5hbHlzaXMuCgpgYGB7cn0Kc3BlY2llc19wcm9wZXJ0aWVzIDwtIGJhY19wcm9wZXJ0aWVzICU+JQogIHJpZ2h0X2pvaW4oc3BlY2llc19hYnVuZF9sb25nLCBieSA9ICJzcGVjaWVzIikgJT4lCiAgbXV0YXRlKGFicyA9IGlmX2Vsc2UoaXMubmEoYWJzKSwgRkFMU0UsIGFicykpCmBgYAoKRGlzdHJpYnV0aW9uIG9mIG94eWdlbiB0b2xlcmFuY2UgYWNyb3NzIGFsbCBleHBlcmltZW50YWwgYW5kIHJlZmVyZW5jZSBzYW1wbGVzCmJhc2VkIG9uIGluZm9ybWF0aW9uIGZyb20gQmFjRGl2ZS4KCmBgYHtyfQpzcGVjaWVzX3Byb3BlcnRpZXMgJT4lCiAgZGlzdGluY3Qoc3BlY2llcywgLmtlZXBfYWxsID0gVCkgJT4lIAogIGNvdW50KGBPeHlnZW4gdG9sZXJhbmNlYCkgJT4lCiAgbXV0YXRlKGBPeHlnZW4gdG9sZXJhbmNlYCA9IHJlcGxhY2VfbmEoYE94eWdlbiB0b2xlcmFuY2VgLCAidW5rbm93biIpLAogICAgICAgICBwZXJjID0gc2NhbGVzOjpwZXJjZW50KG4gLyBzdW0obiksIGFjY3VyYWN5ID0gMC4xKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBuLCBmaWxsID0gYE94eWdlbiB0b2xlcmFuY2VgKSkgKwogICAgZ2VvbV9jb2woY29sb3VyID0gIndoaXRlIikgKyAKICAgIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGVyYyksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2wgPSAid2hpdGUiKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwoCiAgICAgIHZhbHVlcyA9IGModmlyaWRpc0xpdGU6OnR1cmJvKG4gPSBsZW5ndGgobGV2ZWxzKGFzLmZhY3RvcihzcGVjaWVzX3Byb3BlcnRpZXMkYE94eWdlbiB0b2xlcmFuY2VgKSkpKSwgImRhcmtncmV5IikpCmBgYAoKCiMjIENvbG91ciBsZWdlbmRzCgpMZWdlbmQgZm9yIHRleHQtY29sb3VyIGJhc2VkIG9uIHNhbXBsZSB0eXBlLgoKYGBge3J9CmNvbF9zZXEgPC0gMTpsZW5ndGgobGV2ZWxzKGFzLmZhY3RvcihhbmFseXNpc19jb2xvdXJzJGVudl9jb2wpKSkKZW52X2NvbCA8LSBuYS5vbWl0KHVuaXF1ZShhbmFseXNpc19jb2xvdXJzJGVudl9jb2wpKQplbnZfbGFiZWwgPC0gZmlsdGVyKAogIGFuYWx5c2lzX2NvbG91cnMsCiAgRW52ID09ICJzYWxpdmEiIHwKICBFbnYgPT0gImJ5b2NfY2FsY3VsdXMiIHwKICBFbnYgPT0gIm1lZGl1bSIgfAogIEVudiA9PSAiYnVjY2FsX211Y29zYSIgfAogIEVudiA9PSAic3ViZ2luZ2l2YWxfcGxhcXVlIiB8CiAgRW52ID09ICJzdXByYWdpbmdpdmFsX3BsYXF1ZSIgfAogIEVudiA9PSAibW9kZXJuX2NhbGN1bHVzIiB8CiAgICBFbnYgPT0gInZpdHJvX2Jpb2ZpbG0iCiAgKSAlPiUKICAuJEVudiAlPiUKICB1bmlxdWUoKQpxcGxvdCh5PSBjb2xfc2VxLCB4ID0gMSwgZmlsbD1mYWN0b3IoY29sX3NlcSksIGdlb209InRpbGUiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZW52X2NvbCkgKwogIGdlb21fbGFiZWwoYWVzKGxhYmVsID0gZW52X2xhYmVsKSwgY29sID0gIndoaXRlIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKYGBgCgpMZWdlbmQgZm9yIHRleHQtY29sb3VyIGJhc2VkIG9uIHRyZWF0bWVudCBncm91cHMgZnJvbSB0aGUgZXhwZXJpbWVudC4KCmBgYHtyfQpjb2xfc2VxIDwtIDE6bGVuZ3RoKGxldmVscyhhcy5mYWN0b3IoZXhwZXJpbWVudF9jb2xvdXJzJHRyZWF0X2NvbCkpKQpxcGxvdCh5PSBjb2xfc2VxLCB4ID0gMSwgZmlsbD1mYWN0b3IoY29sX3NlcSksIGdlb209InRpbGUiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbmEub21pdCh1bmlxdWUoZXhwZXJpbWVudF9jb2xvdXJzJHRyZWF0X2NvbCkpKSArCiAgZ2VvbV9sYWJlbChhZXMobGFiZWwgPSB1bmlxdWUoZXhwZXJpbWVudF9jb2xvdXJzJHRyZWF0bWVudCkpLCBjb2wgPSAid2hpdGUiKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpgYGAKCgojIFdpdGhpbiB0aGUgZXhwZXJpbWVudAoKUGxvdHRpbmcgcmVsYXRpdmUgc3BlY2llcyBhbmQgZ2VudXMgYWJ1bmRhbmNlcyBhY3Jvc3MgZXhwZXJpbWVudGFsIHNhbXBsZXMgb25seS4KU2FtcGxlcyBhcmUgb3JkZXJlZCBmcm9tIGxlZnQgdG8gcmlnaHQgYnkgc2FtcGxpbmcgZGF5LgoKIyMgR2VudXMgY29tcG9zaXRpb24KCmBgYHtyfQojZ2VudXNfYWJ1bmRfbG9uZwp0b3BfZ2VudXMgJT4lIAogIGZpbHRlcihzYW1wbGUgJWluJSBleHBlcmltZW50X21ldGFkYXRhJGAjU2FtcGxlSURgKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBzYW1wbGUsIHkgPSBnZW51cywgZmlsbCA9IGxvZyhyZWxfYWJ1bmQgKyAwLjAwMSkpKSArCiAgICBnZW9tX3RpbGUoKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSByZXYoZGF5X29yZGVyKSkgKyAjIHJldmVyc2UgZGF5X29yZGVyIHZlY3RvcgogICAgI215X3NjYWxlMiArCiAgICB0aGVtZSgKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dCgpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCgKICAgICAgICBhbmdsZSA9IDYwLAogICAgICAgIGhqdXN0ID0gMSwKICAgICAgICB2anVzdCA9IDEsCiAgICAgICAgY29sb3VyID0gcmV2KGV4cGVyaW1lbnRfY29sb3VycyRlbnZfY29sKQogICAgICAgICkKICAgICAgKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiQiIpCmBgYAoKCgojIyBTcGVjaWVzIGNvbXBvc2l0aW9uCgpPbmx5IGluY2x1ZGluZyB0aGUgbmFtZSBvZiB0aGUgdG9wIDIwIHNwZWNpZXMgKHJlbGF0aXZlIGFidW5kYW5jZSkgd2l0aGluIGVhY2gKc2FtcGxlLgoKYGBge3J9CnRvcF9zcGVjaWVzICU+JSAKICBmaWx0ZXIoc2FtcGxlICVpbiUgZXhwZXJpbWVudF9tZXRhZGF0YSRgI1NhbXBsZUlEYCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc2FtcGxlLCB5ID0gc3BlY2llcywgZmlsbCA9IGxvZyhyZWxfYWJ1bmQgKyAwLjAwMSkpKSArCiAgICBnZW9tX3RpbGUoKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSByZXYoZGF5X29yZGVyKSkgKyAjIHJldmVyc2UgZGF5X29yZGVyIHZlY3RvcgogICAgI215X3NjYWxlMiArCiAgICB0aGVtZSgKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dCgpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCgKICAgICAgICBhbmdsZSA9IDYwLAogICAgICAgIGhqdXN0ID0gMSwKICAgICAgICB2anVzdCA9IDEsCiAgICAgICAgY29sb3VyID0gcmV2KGV4cGVyaW1lbnRfY29sb3VycyRlbnZfY29sKQogICAgICAgICkKICAgICAgKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiQiIpCmBgYAoKCmBgYHtyfQpjbHJfYnlvY19sb25nICU+JSAKICBmaWx0ZXIoI3NhbXBsZSAlaW4lIGV4cGVyaW1lbnRfbWV0YWRhdGEkYCNTYW1wbGVJRGAsCiAgICAgICAgIHN0cl9kZXRlY3Qoc3BlY2llcywgIlN0cmVwdG9jb2NjdXN8QWN0aW5vbXljZXN8RnVzb2JhY3Rlcml1bXxIYWVtbyIpKSAlPiUgIyBlYXJseSBjb2xvbmlzZXJzCiAgZ2dwbG90KGFlcyh4ID0gc2FtcGxlLCB5ID0gc3BlY2llcywgZmlsbCA9IGNscl9jb3VudCkpICsKICAgIGdlb21fdGlsZSgpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihkYXlfb3JkZXIpKSArICMgcmV2ZXJzZSBkYXlfb3JkZXIgdmVjdG9yCiAgICAjbXlfc2NhbGUyICsKICAgIHRoZW1lKAogICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KCksCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KAogICAgICAgIGFuZ2xlID0gNjAsCiAgICAgICAgaGp1c3QgPSAxLAogICAgICAgIHZqdXN0ID0gMSwKICAgICAgICBjb2xvdXIgPSByZXYoZXhwZXJpbWVudF9jb2xvdXJzJGVudl9jb2wpCiAgICAgICAgKQogICAgICApICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJCIikKCmNscl9ieW9jX2xvbmcgJT4lIAogIGZpbHRlcigjc2FtcGxlICVpbiUgZXhwZXJpbWVudF9tZXRhZGF0YSRgI1NhbXBsZUlEYCwKICAgICAgICAgc3RyX2RldGVjdChzcGVjaWVzLCAiRnVzb2JhY3Rlcml1bXxFdWJhY3Rlcml1bXxUcmVwb25lbWF8VGFubmVyZWxsYXxQcmV2b3RlbGxhIikpICU+JSAjIExhdGUgY29sb25pc2VycwogIGdncGxvdChhZXMoeCA9IHNhbXBsZSwgeSA9IHNwZWNpZXMsIGZpbGwgPSBjbHJfY291bnQpKSArCiAgICBnZW9tX3RpbGUoKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSByZXYoZGF5X29yZGVyKSkgKyAjIHJldmVyc2UgZGF5X29yZGVyIHZlY3RvcgogICAgI215X3NjYWxlMiArCiAgICB0aGVtZSgKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dCgpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCgKICAgICAgICBhbmdsZSA9IDYwLAogICAgICAgIGhqdXN0ID0gMSwKICAgICAgICB2anVzdCA9IDEsCiAgICAgICAgY29sb3VyID0gcmV2KGV4cGVyaW1lbnRfY29sb3VycyRlbnZfY29sKQogICAgICAgICkKICAgICAgKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiQiIpCmBgYAoKRGVjcmVhc2UgaW4gb3JhbCBzdHJlcHRvY29jY2kgb3ZlciB0aGUgY291cnNlIG9mIHRoZSBleHBlcmltZW50IGlzIGV4cGVjdGVkIGluCmJpb2ZpbG0gZ3Jvd3RoLiAqQWN0aW5vbXljZXMqIHNwcC4gd2VyZSBtb3JlIGFidW5kYW50IGluIHNhbGl2YSBhbmQgaW4gdGhlIGZpbmFsCmNhbGN1bHVzIHByb2R1Y3QgdGhhbiBlYXJseSBiaW9maWxtIHNhbXBsZXMuCgojIyMgT3h5Z2VuIHRvbGVyYW5jZQoKYGBge3J9Cm5vX21hdGNoIDwtIHVuaXF1ZShmaWx0ZXIoc3BlY2llc19wcm9wZXJ0aWVzLCBpcy5uYShgT3h5Z2VuIHRvbGVyYW5jZWApKSRzcGVjaWVzKSAjIHdoaWNoIHNwZWNpZXMgaGFkIG5vIG1hdGNoPwpgYGAKClByb3BvcnRpb25hbCBkaXN0cmlidXRpb24gb2Ygb3h5Z2VuIHRvbGVyYW5jZXMgYWNyb3NzIGV4cGVyaW1lbnRhbCBzYW1wbGVzLgpEYXRhIG9uIG94eWdlbiB0b2xlcmFuY2Ugb2J0YWluZWQgZnJvbSBbQmFjRGl2ZV0oaHR0cHM6Ly9iYWNkaXZlLmRzbXouZGUvKS4KQSB0b3RhbCBvZiAqKmByIGxlbmd0aChub19tYXRjaClgKiogb3V0IG9mCioqYHIgbGVuZ3RoKHVuaXF1ZShzcGVjaWVzX2FidW5kX2xvbmckc3BlY2llcykpYCoqCihgciBzY2FsZXM6OnBlcmNlbnQobGVuZ3RoKG5vX21hdGNoKSAvIGxlbmd0aCh1bmlxdWUoc3BlY2llc19hYnVuZF9sb25nJHNwZWNpZXMpKSwgYWNjdXJhY3kgPSAwLjEpYCkKc3BlY2llcyBkaWQgbm90IGhhdmUgYSBtYXRjaCBvbiBCYWNEaXZlLgoKYGBge3J9CnNwZWNpZXNfcHJvcGVydGllcyAlPiUgCiAgZmlsdGVyKHNhbXBsZSAlaW4lIGV4cGVyaW1lbnRfbWV0YWRhdGEkYCNTYW1wbGVJRGApICU+JSAKICBnZ3Bsb3QoYWVzKHkgPSBzYW1wbGUsIHggPSByZWxfYWJ1bmQsIGZpbGwgPSBgT3h5Z2VuIHRvbGVyYW5jZWApKSArCiAgICBnZW9tX2NvbCgpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAjdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKICAgIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gZGF5X29yZGVyKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCgKICAgICAgdmFsdWVzID0gdmlyaWRpc0xpdGU6OnR1cmJvKG4gPSBsZW5ndGgobGV2ZWxzKGFzLmZhY3RvcihzcGVjaWVzX3Byb3BlcnRpZXMkYE94eWdlbiB0b2xlcmFuY2VgKSkpKSkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3VyID0gZXhwZXJpbWVudF9jb2xvdXJzJGVudl9jb2wpCiAgKQpgYGAKCkdlbmVyYWxseSBhbiBpbmNyZWFzZSBvZiBhbmFlcm9iZXMgb3ZlciB0aGUgY291cnNlIG9mIHRoZSBleHBlcmltZW50LCBhdCB0aGUKZXhwZW5zZSBvZiBmYWN1bHRhdGl2ZSBhbmFlcm9iZXMuIExvdyBwcm9wb3J0aW9uIG9mIGFlcm9iZXMgdGhyb3VnaG91dCB0aGUKZXhwZXJpbWVudC4KCiMjIyBBbXlsYXNlLWJpbmRpbmcgc3BlY2llcwoKUmVsYXRpdmUgYWJ1bmRhbmNlIG9mIEFCUyBwZXIgc2FtcGxlLiBEaWZmZXJlbnQgdHJlYXRtZW50cyBpbmRpY2F0ZWQgYnkgdGV4dCBjb2xvdXIuCgpgYGB7cn0Kc3BlY2llc19wcm9wZXJ0aWVzICU+JSAKICBmaWx0ZXIoc3RyX2RldGVjdChzYW1wbGUsICJTWU4iKSkgJT4lIywKICAgICAgICAgI3N0cl9kZXRlY3Qoc2FtcGxlLCAiU1lOMDBbMS0zXSIsIG5lZ2F0ZSA9IFQpKSAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gc2FtcGxlLCB4ID0gcmVsX2FidW5kLCBmaWxsID0gYWJzKSkgKwogICAgZ2VvbV9jb2woKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgI3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSArCiAgICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IGRheV9vcmRlcikgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gIkUiKSArCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoCiAgICAgICAgY29sb3VyID0gZXhwZXJpbWVudF9jb2xvdXJzICU+JQogICAgICAgICAgYXJyYW5nZShkZXNjKGRheSkpICU+JQogICAgICAgICAgLiR0cmVhdF9jb2wpKQpgYGAKCmBgYHtyfQpleHBlcmltZW50X21ldGFkYXRhICU+JQogIGRwbHlyOjpzZWxlY3QoYCNTYW1wbGVJRGAsIHRyZWF0bWVudCkKYGBgCgpUaGUgcHJlc2VuY2Ugb2YgQUJTIGlzIHVucmVsYXRlZCB0byB0aGUgcHJlc2VuY2Ugb2Ygc3RhcmNoLiBUaGUgb3ZlcmFsbCBwcm9wb3J0aW9uCm9mIEFCUyBkZWNyZWFzZXMgb3ZlciB0aGUgY291cnNlIG9mIHRoZSBleHBlcmltZW50LiBUaGUgYWJzZW5jZSBvZiBhbXlsYXNlIGluCnRoZSBtb2RlbCBtYXkgcmVkdWNlIHRoZSBuZWVkIGZvciBzcGVjaWVzIGNhcGFibGUgb2Ygc3RhcmNoIGh5ZHJvbHlzaXMsIGVzcGVjaWFsbHkKd2hlbiBzdWNyb3NlIHdhcyBhdmFpbGFibGUgaW4gYWxsIHNhbXBsZXMuCgojIENvbXBhcmF0aXZlIHNhbXBsZXMKCkxvb2tpbmcgYXQgcmVsYXRpdmUgYWJ1bmRhbmNlcyBhdCBzcGVjaWVzIGFuZCBnZW51cyBsZXZlbHMgb2YgY2FsY3VsdXMgYW5kIHBsYXF1ZQpzYW1wbGVzLiBUb3AgMzAgdGF4YSAocmVsYXRpdmUgYWJ1bmRhbmNlKSB3ZXJlIHBsb3R0ZWQuCgojIyBHZW51cyBjb21wb3NpdGlvbgoKYGBge3J9CmdlbnVzX2FidW5kX2xvbmcgJT4lCiAgZmlsdGVyKHNhbXBsZSAlaW4lIGZpbHRlcihhbmFseXNpc19tZXRhZGF0YSwgc3RyX2RldGVjdChFbnYsICJjYWxjdWx1c3xwbGFxdWUiKSkkYCNTYW1wbGVJRGApICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZShkZXNjKHJlbF9hYnVuZCkpICU+JQogIGRpc3RpbmN0KGdlbnVzKSAlPiUKICBzbGljZV9oZWFkKG4gPSAzMCkgJT4lICMgdG9wIDMwIHNwZWNpZXMgYWNyb3NzIGFsbCBzYW1wbGVzCiAgbGVmdF9qb2luKHNwZWNpZXNfYWJ1bmRfbG9uZywgYnkgPSAiZ2VudXMiKSAlPiUgIyByZWNvbWJpbmUgd2l0aCBzYW1wbGVzCiAgZmlsdGVyKHNhbXBsZSAlaW4lIGZpbHRlcihhbmFseXNpc19tZXRhZGF0YSwgc3RyX2RldGVjdChFbnYsICJjYWxjdWx1c3xwbGFxdWUiKSkkYCNTYW1wbGVJRGApICU+JSAjbmVlZCB0byBmaWx0ZXIgb3V0IHNhbXBsZXMgYWdhaW4KICBnZ3Bsb3QoYWVzKHggPSBzYW1wbGUsIHkgPSBnZW51cywgZmlsbCA9IGxvZyhyZWxfYWJ1bmQgKyAwLjAwMDEpKSkgKwogICAgZ2VvbV90aWxlKCkgKwogICAgdGhlbWVfdm9pZCgpICsKICAgICNteV9zY2FsZTIgKwogICAgdGhlbWUoCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoKSwKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoCiAgICAgICAgYW5nbGUgPSA2MCwKICAgICAgICBoanVzdCA9IDEsCiAgICAgICAgdmp1c3QgPSAxLAogICAgICAgIGNvbG91ciA9IGZpbHRlcigKICAgICAgICAgICAgYW5hbHlzaXNfY29sb3Vycywgc3RyX2RldGVjdChFbnYsICJjYWxjdWx1c3xwbGFxdWUiKQogICAgICAgICAgICApJGVudl9jb2wgJT4lCiAgICAgICAgICByZXYoKQogICAgICAgICkKICAgICAgKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiQiIpCmBgYAoKCiMjIFNwZWNpZXMgY29tcG9zaXRpb24KClJlbGF0aXZlIGFidW5kYW5jZQoKYGBge3J9CiMgcmVsYXRpdmUgYWJ1bmRhbmNlCnNwZWNpZXNfYWJ1bmRfbG9uZyAlPiUKICBmaWx0ZXIoc2FtcGxlICVpbiUgZmlsdGVyKGFuYWx5c2lzX21ldGFkYXRhLCBzdHJfZGV0ZWN0KEVudiwgImNhbGN1bHVzfHBsYXF1ZSIpKSRgI1NhbXBsZUlEYCkgJT4lIAogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGRlc2MocmVsX2FidW5kKSkgJT4lCiAgZGlzdGluY3Qoc3BlY2llcykgJT4lCiAgc2xpY2VfaGVhZChuID0gMzApICU+JSAjIHRvcCAzMCBzcGVjaWVzIGFjcm9zcyBhbGwgc2FtcGxlcwogIGxlZnRfam9pbihzcGVjaWVzX2FidW5kX2xvbmcsIGJ5ID0gInNwZWNpZXMiKSAlPiUgIyByZWNvbWJpbmUgd2l0aCBzYW1wbGVzCiAgZmlsdGVyKHNhbXBsZSAlaW4lIGZpbHRlcihhbmFseXNpc19tZXRhZGF0YSwgc3RyX2RldGVjdChFbnYsICJjYWxjdWx1c3xwbGFxdWUiKSkkYCNTYW1wbGVJRGApICU+JSAjbmVlZCB0byBmaWx0ZXIgb3V0IHNhbXBsZXMgYWdhaW4KICBnZ3Bsb3QoYWVzKHggPSBzYW1wbGUsIHkgPSBzcGVjaWVzLCBmaWxsID0gbG9nKHJlbF9hYnVuZCArIDAuMDAxKSkpICsKICAgIGdlb21fdGlsZSgpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICAjbXlfc2NhbGUyICsKICAgIHRoZW1lKAogICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KCksCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KAogICAgICAgIGFuZ2xlID0gNjAsCiAgICAgICAgaGp1c3QgPSAxLAogICAgICAgIHZqdXN0ID0gMSwKICAgICAgICBjb2xvdXIgPSBmaWx0ZXIoCiAgICAgICAgICAgIGFuYWx5c2lzX2NvbG91cnMsIHN0cl9kZXRlY3QoRW52LCAiY2FsY3VsdXN8cGxhcXVlIikKICAgICAgICAgICAgKSRlbnZfY29sCiAgICAgICAgKQogICAgICApICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJCIikKYGBgCgpDZW50ZXJlZCByYXRpbyBsb2ctdHJhbnNmb3JtIGFidW5kYW5jZSBvcmRlcmVkIGJ5IFBDMSBsb2FkaW5ncy4KCmBgYHtyfQojIFRvcCBwb3NpdGl2ZSBsb2FkaW5ncyBvbiBQQzEKc3BlY2llc19wb3NfcGMxICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKwogICAgdGhlbWUoCiAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCgKICAgICAgICBhbmdsZSA9IDkwLCBzaXplID0gOCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSwKICAgICAgICBjb2xvdXIgPSBhbmFseXNpc19jb2xvdXJzICU+JQogICAgICAgICAgcmlnaHRfam9pbigKICAgICAgICAgICAgYXNfdGliYmxlKHNwY2Ffc3BlY2llcyR4LCByb3duYW1lcyA9ICIjU2FtcGxlSUQiKSwgCiAgICAgICAgICAgIGJ5ID0gIiNTYW1wbGVJRCIpICU+JQogICAgICAgICAgYXJyYW5nZShQQzEpICU+JSAKICAgICAgICAgIC4kZW52X2NvbAogICAgICApCiAgICAgICkKCiMgVG9wIG5lZ2F0aXZlIGxvYWRpbmdzIG9uIFBDMQpzcGVjaWVzX25lZ19wYzEgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoCiAgICAgIGFuZ2xlID0gOTAsIHNpemUgPSA4LCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxLAogICAgICBjb2xvdXIgPSBhbmFseXNpc19jb2xvdXJzICU+JQogICAgICAgICAgcmlnaHRfam9pbigKICAgICAgICAgICAgYXNfdGliYmxlKHNwY2Ffc3BlY2llcyR4LCByb3duYW1lcyA9ICIjU2FtcGxlSUQiKSwgCiAgICAgICAgICAgIGJ5ID0gIiNTYW1wbGVJRCIpICU+JQogICAgICAgICAgYXJyYW5nZShQQzEpICU+JSAKICAgICAgICAgIC4kZW52X2NvbAogICAgICApCiAgICAgICkKYGBgCgoqRW50ZXJvY29jY3VzIGZhZWNhbGlzKiwgKkVudGVyb2NvY2N1cyBjYXNzZWxpZmxhdnVzKiwgYW5kICpFbnRlcm9jb2NjdXMgZHVyYW5zKgphcmUgbW9yZSBhYnVuZGFudCBpbiB0aGUgKmluIHZpdHJvKiBiaW9maWxtIHNhbXBsZXMgdGhhbgoqaW4gdml2byogc2FtcGxlcyBvZiBwbGFxdWUgYW5kIGNhbGN1bHVzLiBDb252ZXJzZWx5LCAqTmVpc3NlcmlhKiBzcHAuIGFuZAoqQWN0aW5vbXljZXMqIHNwcC4gYXJlIGRlZmljaWVudCBpbiB0aGUgKmluIHZpdHJvKiBzYW1wbGVzLgoKIyMjIEVhcmx5IGNvbG9uaXNlcnMKCmBgYHtyfQpjbHJfY29tcGFyX2xvbmcgJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3Qoc3BlY2llcywgIlN0cmVwdG9jb2NjdXN8QWN0aW5vbXljZXN8RnVzb2JhY3Rlcml1bSIpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gc2FtcGxlLCB5ID0gc3BlY2llcywgZmlsbCA9IGNscl9jb3VudCkpICsKICAgIGdlb21fdGlsZSgpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJDIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IDYpKQoKYGBgCgojIyMgTGF0ZSBjb2xvbmlzZXJzCgpgYGB7cn0KY2xyX2NvbXBhcl9sb25nICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHNwZWNpZXMsICJGdXNvYmFjdGVyaXVtfEV1YmFjdGVyaXVtfFRyZXBvbmVtYXxUYW5uZXJlbGxhfFByZXZvdGVsbGEiKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHNhbXBsZSwgeSA9IHNwZWNpZXMsIGZpbGwgPSBjbHJfY291bnQpKSArCiAgICBnZW9tX3RpbGUoKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiQyIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2KSkKCmBgYAoKCiMjIyBPeHlnZW4gdG9sZXJhbmNlCgpQcm9wb3J0aW9uYWwgZGlzdHJpYnV0aW9uIG9mIG94eWdlbiB0b2xlcmFuY2UgYWNyb3NzIGFydGlmaWNpYWwgY2FsY3VsdXMgYW5kCm9yYWwgcmVmZXJlbmNlIHNhbXBsZXMuCgpgYGB7cn0Kc3BlY2llc19wcm9wZXJ0aWVzICU+JSAKICBmaWx0ZXIoc2FtcGxlICVpbiUgZmlsdGVyKGFuYWx5c2lzX21ldGFkYXRhLCBzdHJfZGV0ZWN0KEVudiwgImNhbGN1bHVzfHBsYXF1ZSIpKSRgI1NhbXBsZUlEYCkgJT4lIAogICNtdXRhdGUoYE94eWdlbiB0b2xlcmFuY2VgID0gZm9yY2F0czo6ZmN0X2V4cGxpY2l0X25hKGBPeHlnZW4gdG9sZXJhbmNlYCkpICU+JQogIGdncGxvdChhZXMoeSA9IHNhbXBsZSwgeCA9IHJlbF9hYnVuZCwgZmlsbCA9IGBPeHlnZW4gdG9sZXJhbmNlYCkpICsKICAgIGdlb21fY29sKCkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgICNzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb24gPSAiSCIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKAogICAgICB2YWx1ZXMgPSB2aXJpZGlzTGl0ZTo6dHVyYm8obiA9IGxlbmd0aChsZXZlbHMoYXMuZmFjdG9yKHNwZWNpZXNfcHJvcGVydGllcyRgT3h5Z2VuIHRvbGVyYW5jZWApKSkpKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dCgKICAgICAgICBjb2xvdXIgPSBhbmFseXNpc19jb2xvdXJzICU+JQogICAgICAgICAgZmlsdGVyKHN0cl9kZXRlY3QoRW52LCAiY2FsY3VsdXN8cGxhcXVlIikpICU+JQogICAgICAgICAgI2FycmFuZ2UoZGVzYyhkYXkpKSAlPiUKICAgICAgICAgIC4kZW52X2NvbCAlPiUKICAgICAgcmV2KCkKICAgICkKICApCmBgYAoKTW9zdCBub3RhYmxlIGRpZmZlcmVuY2UgaXMgdGhlIGFic2VuY2Ugb2YgYWVyb2JlcyBpbiB0aGUgYXJ0aWZpY2lhbApjYWxjdWx1cyBzYW1wbGVzLiBNb2Rlcm4gY2FsY3VsdXMgY29udGFpbnMgYSBsYXJnZSBwcm9wb3J0aW9uIG9mIHNwZWNpZXMgd2hlcmUKb3h5Z2VuIHRvbGVyYW5jZSBpcyB1bmtub3duIChgTkFgKS4KCiMjIyBBbXlsYXNlLWJpbmRpbmcgc3BlY2llcwoKYGBge3J9CnNwZWNpZXNfcHJvcGVydGllcyAlPiUgCiAgZmlsdGVyKHNhbXBsZSAlaW4lIGZpbHRlcihhbmFseXNpc19tZXRhZGF0YSwgc3RyX2RldGVjdChFbnYsICJjYWxjdWx1c3xwbGFxdWUiKSkkYCNTYW1wbGVJRGApICU+JSAKICBnZ3Bsb3QoYWVzKHkgPSBzYW1wbGUsIHggPSByZWxfYWJ1bmQsIGZpbGwgPSBhYnMpKSArCiAgICBnZW9tX2NvbCgpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb24gPSAiRSIpCiAgICAjdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKYGBgCgpBbXlsYXNlLWJpbmRpbmcgc3BlY2llcyBhcmUgc2VlbWluZ2x5IHVuZGVycmVwcmVzZW50ZWQgaW4gdGhlIGFydGlmaWNpYWwgY2FsY3VsdXMKc2FtcGxlcyBjb21wYXJlZCB0byB0aGUgbW9kZXJuIGNhbGN1bHVzIGFuZCBwbGFxdWUgc2FtcGxlcy4KCiMgRGlzY3Vzc2lvbgoKVmVyeSBmZXcgYWVyb2JlcyBtYWRlIGl0IGludG8gdGhlIG1vZGVsLiBBcGFydCBmcm9tIHRoZSBkb25hdGVkIHNhbGl2YSwgdmVyeSBmZXcKYWVyb2JlcyB3ZXJlIGRldGVjdGVkIGluIGFueSBvZiB0aGUgZXhwZXJpbWVudGFsIHNhbXBsZXMuICpSb3RoaWEqIHNwcC4gZGlzYXBwZWFyZWQKYmV0d2VlbiBzYWxpdmEgYW5kIG1lZGl1bSBzYW1wbGVzLiBXZSBtYXkgbmVlZCB0byBpbnRyb2R1Y2UgbW9yZSBveHlnZW4gaW50bwp0aGUgcHJvdG9jb2wsIG9yIGluY3JlYXNlIHRoZSBmcmVxdWVuY3kgb2YgbWVkaXVtIHJlcGxhY2VtZW50IChlLmcuIGV2ZXJ5IHR3bwpkYXlzIGluc3RlYWQgb2YgdGhyZWUpLgoKQUJTIGFyZSBhbHNvIHVuZGVycmVwcmVzZW50ZWQKaW4gdGhlIGV4cGVyaW1lbnRhbCBzYW1wbGVzIGNvbXBhcmVkIHRvIHRoZSBtb2Rlcm4gY2FsY3VsdXMgYW5kIHBsYXF1ZSByZWZlcmVuY2UKc2FtcGxlcy4gVGhlIGhpZ2ggcmVsYXRpdmUgYWJ1bmRhbmNlIG9mICpFLiBmYWVjYWxpcyogbWF5IHJlcHJlc2VudCBjb250YW1pbmF0aW9uCmRlc3BpdGUgaXQgYmVpbmcgY29tbW9ubHkgZm91bmQgaW4gdGhlIG9yYWwgY2F2aXR5LiBUaGVyZSB3YXMgYSByZWxhdGl2ZWx5IGxvdwpmcmVxdWVuY3kgb2YgQUJTIGluIHRoZSBhcnRpZmljaWFsIHNhbXBsZXMgY29tcGFyZWQgdG8gdGhlIHJlZmVyZW5jZSBzYW1wbGVzLAp3aGljaCBtYXkgYmUgYXR0cmlidXRlZCB0byB0aGUgcHJlc2VuY2Ugb2Ygc3Vjcm9zZSBpbiB0aGUgdHJlYXRtZW50IHNvbHV0aW9ucywKcG90ZW50aWFsbHkgZWxpbWluYXRpbmcgdGhlIG5lZWQgZm9yIEFCUy4KCg==