Data are converted to phyloseq format. Experiment samples are first run on their own, and then artificial calculus samples are run together with calculus and plaque reference samples.

calculus_otu <- taxatable %>%
  column_to_rownames(var = "#OTU ID") %>% 
  dplyr::select(which(
    colnames(.) %in% filter(
      analysis_metadata, str_detect(Env, "calculus|plaque"))$`#SampleID`
  )) %>%
  otu_table(taxa_are_rows = T)

experiment_otu <- taxatable %>%
  column_to_rownames(var = "#OTU ID") %>% 
  dplyr::select(which(colnames(.) %in% experiment_metadata$`#SampleID`)) %>%
  otu_table(taxa_are_rows = T)

experiment_sample <- experiment_metadata %>%
  filter(`#SampleID` %in% analysis_metadata$`#SampleID`) %>%
  column_to_rownames(var = "#SampleID") %>% 
  sample_data()

experiment_phyloseq <- phyloseq(experiment_otu, experiment_sample)

calculus_sample <- analysis_metadata %>%
  filter(str_detect(Env, "calculus|plaque")) %>%
  column_to_rownames(var = "#SampleID") %>%
  sample_data()

calculus_phyloseq <- phyloseq(calculus_otu, calculus_sample)

ANCOM-BC

Differential abundance is calculated using the ANCOM-BC method from the ANCOMBC R package. P-values adjusted using the false discovery rate (FDR) method. Samples grouped by sample type (i.e. saliva, plaque, modern calculus, artificial calculus).

First, differential abundance of experimental samples only, comparing abundance between donated saliva, medium, and the final calculus product.

Across experiment

exp_da <- ANCOMBC::ancombc(
  experiment_phyloseq, 
  formula = "Env",
  group = "Env",
  global = F,
  p_adj_method = "fdr")
Warning: Small sample size detected for the following group(s): 
saliva
ANCOM-BC results would be unstable when the sample size is < 5 per group

Species with the highest log-fold change mainly see a reduction from saliva to medium, and then a slight further reduction from medium to calculus. The largest reduction of species occurs in Rothia dentocariosa (LFC = 12.9250215).

Artificial vs. modern calculus

calculus_da <- ANCOMBC::ancombc(
  calculus_phyloseq, 
  formula = "Env",
  group = "Env",
  global = F,
  p_adj_method = "fdr")

# table with log-fold change statistics between artificial calculus and others
  # negative lfc means higher abundance in artificial calculus
  # positive lfc means lower abundance in art calculus
calc_logf_change <- as_tibble(calculus_da$res$lfc, rownames = "species") #%>%
    #select(species, Envmedium) %>%
calc_logf_se <- as_tibble(calculus_da$res$se, rownames = "species") #%>%
    #select(!c(Envsaliva.x, Envsaliva.y)) %>%
calc_logf_q <- as_tibble(calculus_da$res$q_val, rownames = "species")

calc_logf_full <- calc_logf_change %>%
  pivot_longer(-species, values_to = "lfc") %>%
  full_join(
    calc_logf_se %>%
      pivot_longer(-species, values_to = "se"),
    by = c("species", "name")
  ) %>%
  full_join(
    calc_logf_q %>%
      pivot_longer(-species, values_to = "q_value"),
    by = c("species", "name")
  ) %>%
  mutate(lower = lfc - se,
         upper = lfc + se,
         name = str_remove(name, "^Env"),
         abn = case_when(sign(lfc) == -1 ~ "byoc_calculus",
         TRUE ~ name)) %>%
  rename(env = name)

head(calc_logf_full)

Plots of log-fold change between samples

# Plot of largest absolute log-fold changes
calc_logf_full %>%
  group_by(env) %>% 
  arrange(desc(abs(lfc))) %>%
    slice_head(n = 20) %>% # causes loss of some env values
    ungroup() %>%
  slice_head(n = 20) %>% # extract top 20 lfc from modern_calculus
  bind_rows(filter( # recombine with other env values so all are included in plot
    calc_logf_full, 
    env != "modern_calculus",
    species %in% .$species
    )
  ) %>% 
   ggplot(aes(x = lfc, y = reorder(species, lfc), col = abn)) +
    geom_point(size = 2) +
    geom_linerange(aes(xmin = lower, xmax = upper), size = 1) +
    geom_vline(xintercept = 0, linetype = "dashed") +
    facet_wrap(~ env) +
    theme_bw() +
    theme(
      legend.position = "none",
      axis.title.y = element_blank()
          )


# Top 30 loadings from PC1

calc_logf_full %>%
  filter(species %in% pca_loadings$species[1:20]) %>% 
  slice_head(n = 60) %>% # pca_loadings already arranged by PC1
  ggplot(aes(x = lfc, y = reorder(species, lfc), col = abn)) +
    geom_point(size = 2) +
    geom_linerange(aes(xmin = lower, xmax = upper), size = 1) +
    geom_vline(xintercept = 0, linetype = "dashed") +
    facet_wrap(~ env) +
    theme_bw() +
    theme(
      legend.position = "none",
      axis.title.y = element_blank()
          )


calc_logf_full %>%
  filter(species %in% arrange(pca_loadings, desc(abs(PC2)))$species[1:30]) %>%
  slice_head(n = 30) #%>% # pca_loadings already arranged by PC1

# Top 30 loadings from PC2
calc_logf_full %>%
  filter(species %in% arrange(pca_loadings, desc(abs(PC2)))$species[1:20]) %>%
  slice_head(n = 60) %>% # pca_loadings already arranged by PC1
  ggplot(aes(x = lfc, y = reorder(species, lfc), col = abn)) +
    geom_point(size = 2) +
    geom_linerange(aes(xmin = lower, xmax = upper), size = 1) +
    geom_vline(xintercept = 0, linetype = "dashed") +
    facet_wrap(~ env) +
    theme_bw() +
    theme(
      legend.position = "none",
      axis.title.y = element_blank()
          )

Bias-corrected log observed abundances

Bias-corrected log observed abundances were calculated by following the example in vignette("ANCOMBC").

# from vignette("ANCOMBC")
calc_samp_frac <- calculus_da$samp_frac
# Replace NA with 0
calc_samp_frac[is.na(calc_samp_frac)] = 0 
# add 1 to counts for log-transformation
calc_log_exp_otu <- log(microbiome::abundances(calculus_otu) + 1)

# Adjust the log observed abundances
# Bias-corrected (log) observed abundances
calc_log_abn <- t(t(calc_log_exp_otu) - calc_samp_frac) %>%
  as_tibble(rownames = "species")

# need to convert counts back to zero (counts that are = samp_frac)

calc_log_abn

Discussion

Enterococcus faecalis has the highest log-fold change, with a higher abundance in the artificial calculus samples compared to the reference samples and may represent one of the main differences between artificial calculus and other reference samples, especially modern calculus, consistent with the results of the sPCA analysis.

LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsIGFidW5kYW5jZSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CiN8IGxhYmVsOiBzZXR1cAojfCBpbmNsdWRlOiBmYWxzZQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBoeWxvc2VxKQpsaWJyYXJ5KG1pY3JvYmlvbWUpCmxpYnJhcnkoQU5DT01CQykKdGF4YXRhYmxlIDwtIHJlYWRyOjpyZWFkX3RzdihoZXJlKCIwNS1yZXN1bHRzL3Bvc3QtZGVjb250YW1fdGF4YXRhYmxlLnRzdiIpKQphbmFseXNpc19tZXRhZGF0YSA8LSByZWFkcjo6cmVhZF90c3YoaGVyZSgiMDEtZG9jdW1lbnRhdGlvbi9hbmFseXNpcy1tZXRhZGF0YS50c3YiKSkKZXhwZXJpbWVudF9tZXRhZGF0YSA8LSByZWFkcjo6cmVhZF90c3YoaGVyZSgiMDEtZG9jdW1lbnRhdGlvbi9leHBlcmltZW50LW1ldGFkYXRhLnRzdiIpKSAlPiUKICBmaWx0ZXIoYCNTYW1wbGVJRGAgJWluJSBhbmFseXNpc19tZXRhZGF0YSRgI1NhbXBsZUlEYCkKcGNhX2xvYWRpbmdzIDwtIHJlYWRyOjpyZWFkX3RzdihoZXJlKCIwNS1yZXN1bHRzL2FsbC1wY2EtbG9hZGluZ3MudHN2IikpCmV4cF9wY2FfbG9hZGluZ3MgPC0gcmVhZHI6OnJlYWRfdHN2KGhlcmUoIjA1LXJlc3VsdHMvZXhwZXJpbWVudC1wY2EtbG9hZGluZ3MudHN2IikpCmJhY19wcm9wZXJ0aWVzIDwtIHJlYWRyOjpyZWFkX3RzdihoZXJlKCIwMS1kb2N1bWVudGF0aW9uL3NwZWNpZXMtcHJvcGVydGllcy50c3YiKSkKYGBgCgpEYXRhIGFyZSBjb252ZXJ0ZWQgdG8gYHBoeWxvc2VxYCBmb3JtYXQuIEV4cGVyaW1lbnQgc2FtcGxlcyBhcmUgZmlyc3QgcnVuIG9uCnRoZWlyIG93biwgYW5kIHRoZW4gYXJ0aWZpY2lhbCBjYWxjdWx1cyBzYW1wbGVzIGFyZSBydW4gdG9nZXRoZXIgd2l0aCBjYWxjdWx1cwphbmQgcGxhcXVlIHJlZmVyZW5jZSBzYW1wbGVzLgoKYGBge3J9CmNhbGN1bHVzX290dSA8LSB0YXhhdGFibGUgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICIjT1RVIElEIikgJT4lIAogIGRwbHlyOjpzZWxlY3Qod2hpY2goCiAgICBjb2xuYW1lcyguKSAlaW4lIGZpbHRlcigKICAgICAgYW5hbHlzaXNfbWV0YWRhdGEsIHN0cl9kZXRlY3QoRW52LCAiY2FsY3VsdXN8cGxhcXVlIikpJGAjU2FtcGxlSURgCiAgKSkgJT4lCiAgb3R1X3RhYmxlKHRheGFfYXJlX3Jvd3MgPSBUKQoKZXhwZXJpbWVudF9vdHUgPC0gdGF4YXRhYmxlICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiI09UVSBJRCIpICU+JSAKICBkcGx5cjo6c2VsZWN0KHdoaWNoKGNvbG5hbWVzKC4pICVpbiUgZXhwZXJpbWVudF9tZXRhZGF0YSRgI1NhbXBsZUlEYCkpICU+JQogIG90dV90YWJsZSh0YXhhX2FyZV9yb3dzID0gVCkKCmV4cGVyaW1lbnRfc2FtcGxlIDwtIGV4cGVyaW1lbnRfbWV0YWRhdGEgJT4lCiAgZmlsdGVyKGAjU2FtcGxlSURgICVpbiUgYW5hbHlzaXNfbWV0YWRhdGEkYCNTYW1wbGVJRGApICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiI1NhbXBsZUlEIikgJT4lIAogIHNhbXBsZV9kYXRhKCkKCmV4cGVyaW1lbnRfcGh5bG9zZXEgPC0gcGh5bG9zZXEoZXhwZXJpbWVudF9vdHUsIGV4cGVyaW1lbnRfc2FtcGxlKQoKY2FsY3VsdXNfc2FtcGxlIDwtIGFuYWx5c2lzX21ldGFkYXRhICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KEVudiwgImNhbGN1bHVzfHBsYXF1ZSIpKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gIiNTYW1wbGVJRCIpICU+JQogIHNhbXBsZV9kYXRhKCkKCmNhbGN1bHVzX3BoeWxvc2VxIDwtIHBoeWxvc2VxKGNhbGN1bHVzX290dSwgY2FsY3VsdXNfc2FtcGxlKQpgYGAKCiMjIEFOQ09NLUJDCgpEaWZmZXJlbnRpYWwgYWJ1bmRhbmNlIGlzIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIEFOQ09NLUJDIG1ldGhvZCBmcm9tIHRoZSBgQU5DT01CQ2AKUiBwYWNrYWdlLiBQLXZhbHVlcyBhZGp1c3RlZCB1c2luZyB0aGUgZmFsc2UgZGlzY292ZXJ5IHJhdGUgKEZEUikgbWV0aG9kLiBTYW1wbGVzCmdyb3VwZWQgYnkgc2FtcGxlIHR5cGUgKGkuZS4gc2FsaXZhLCBwbGFxdWUsIG1vZGVybiBjYWxjdWx1cywgYXJ0aWZpY2lhbCBjYWxjdWx1cykuCgpGaXJzdCwgZGlmZmVyZW50aWFsIGFidW5kYW5jZSBvZiBleHBlcmltZW50YWwgc2FtcGxlcyBvbmx5LCBjb21wYXJpbmcgYWJ1bmRhbmNlCmJldHdlZW4gZG9uYXRlZCBzYWxpdmEsIG1lZGl1bSwgYW5kIHRoZSBmaW5hbCBjYWxjdWx1cyBwcm9kdWN0LgoKIyMjIEFjcm9zcyBleHBlcmltZW50CgpgYGB7cn0KZXhwX2RhIDwtIEFOQ09NQkM6OmFuY29tYmMoCiAgZXhwZXJpbWVudF9waHlsb3NlcSwgCiAgZm9ybXVsYSA9ICJFbnYiLAogIGdyb3VwID0gIkVudiIsCiAgZ2xvYmFsID0gRiwKICBwX2Fkal9tZXRob2QgPSAiZmRyIikKYGBgCgpgYGB7cn0KIyBmcm9tIHZpZ25ldHRlKCJBTkNPTUJDIikKc2FtcF9mcmFjIDwtIGV4cF9kYSRzYW1wX2ZyYWMKIyBSZXBsYWNlIE5BIHdpdGggMApzYW1wX2ZyYWNbaXMubmEoc2FtcF9mcmFjKV0gPSAwIAojIGFkZCAxIHRvIGNvdW50cyBmb3IgbG9nLXRyYW5zZm9ybWF0aW9uCmxvZ19leHBfb3R1IDwtIGxvZyhtaWNyb2Jpb21lOjphYnVuZGFuY2VzKGV4cGVyaW1lbnRfb3R1KSArIDEpCgojIEFkanVzdCB0aGUgbG9nIG9ic2VydmVkIGFidW5kYW5jZXMKIyBCaWFzLWNvcnJlY3RlZCAobG9nKSBvYnNlcnZlZCBhYnVuZGFuY2VzCmxvZ19leHBfb3R1X2FkaiA8LSBleHAodCh0KGxvZ19leHBfb3R1KSAtIHNhbXBfZnJhYykpICU+JQogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJzcGVjaWVzIikKCmV4cF9sb2dmX2Z1bGwgPC0gYXNfdGliYmxlKGV4cF9kYSRyZXMkbGZjLCByb3duYW1lcyA9ICJzcGVjaWVzIikgJT4lCiAgcGl2b3RfbG9uZ2VyKC1zcGVjaWVzLCB2YWx1ZXNfdG8gPSAibGZjIikgJT4lCiAgZnVsbF9qb2luKAogICAgYXNfdGliYmxlKGV4cF9kYSRyZXMkc2UsIHJvd25hbWVzID0gInNwZWNpZXMiKSAlPiUKICAgICAgcGl2b3RfbG9uZ2VyKC1zcGVjaWVzLCB2YWx1ZXNfdG8gPSAic2UiKSwKICAgIGJ5ID0gYygic3BlY2llcyIsICJuYW1lIikKICApICU+JQogIGZ1bGxfam9pbigKICAgIGFzX3RpYmJsZShleHBfZGEkcmVzJHEsIHJvd25hbWVzID0gInNwZWNpZXMiKSAlPiUKICAgICAgcGl2b3RfbG9uZ2VyKC1zcGVjaWVzLCB2YWx1ZXNfdG8gPSAicV92YWx1ZSIpLAogICAgYnkgPSBjKCJzcGVjaWVzIiwgIm5hbWUiKQogICkgJT4lCiAgbXV0YXRlKHVwcGVyID0gbGZjICsgc2UsCiAgICAgICAgIGxvd2VyID0gbGZjIC0gc2UsCiAgICAgICAgIG5hbWUgPSBzdHJfcmVtb3ZlKG5hbWUsICJeRW52IiksCiAgICAgICAgIGFibiA9IGNhc2Vfd2hlbihzaWduKGxmYykgPT0gLTEgfiAiYnlvY19jYWxjdWx1cyIsCiAgICAgICAgIFRSVUUgfiBuYW1lKSkgJT4lCiAgcmVuYW1lKGVudiA9IG5hbWUpICU+JQogIGxlZnRfam9pbihiYWNfcHJvcGVydGllcywgYnkgPSAic3BlY2llcyIpCgpleHBfbG9nZl9mdWxsICU+JQogIGFycmFuZ2UoZGVzYyhhYnMobGZjKSkpICU+JQogIHNsaWNlX2hlYWQobiA9IDIwKSAlPiUKICBkcGx5cjo6c2VsZWN0KHNwZWNpZXMsIGxmYywgYE94eWdlbiB0b2xlcmFuY2VgLCBhYnMpCgojIGhpZ2hlc3QgYWJzb2x1dGUgbG9nLWZvbGQgY2hhbmdlcwpleHBfbG9nZl9mdWxsICU+JSAKICBncm91cF9ieShlbnYpICU+JSAKICBhcnJhbmdlKGRlc2MoYWJzKGxmYykpKSAlPiUKICBzbGljZV9oZWFkKG4gPSAyMCkgJT4lICMgY2F1c2VzIGxvc3Mgb2Ygc29tZSBlbnYgdmFsdWVzCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZGVzYyhhYnMobGZjKSkpICU+JQogIHNsaWNlX2hlYWQobiA9IDIwKSAlPiUgIyBleHRyYWN0IHRvcCAyMCBsZmMgZnJvbSBtb2Rlcm5fY2FsY3VsdXMKICBiaW5kX3Jvd3MoZmlsdGVyKCAjIHJlY29tYmluZSB3aXRoIG90aGVyIGVudiB2YWx1ZXMgc28gYWxsIGFyZSBpbmNsdWRlZCBpbiBwbG90CiAgICBleHBfbG9nZl9mdWxsLCAKICAgIGVudiAhPSAic2FsaXZhIiwKICAgIHNwZWNpZXMgJWluJSAuJHNwZWNpZXMKICAgICkKICApICU+JQogIGdncGxvdChhZXMoeCA9IGxmYywgeSA9IHJlb3JkZXIoc3BlY2llcywgbGZjKSwgY29sID0gYWJuKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fbGluZXJhbmdlKGFlcyh4bWluID0gbG93ZXIsIHhtYXggPSB1cHBlcikpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICAgIGZhY2V0X3dyYXAofiBlbnYpCgojIHRvcCAzMCBzcGVjaWVzIGZyb20gUEMxCmV4cF9sb2dmX2Z1bGwgJT4lCiAgZmlsdGVyKHNwZWNpZXMgJWluJSBleHBfcGNhX2xvYWRpbmdzJHNwZWNpZXNbMToyMF0pICU+JSAKICBzbGljZV9oZWFkKG4gPSA2MCkgJT4lIAogIGdncGxvdChhZXMoeCA9IGxmYywgeSA9IHJlb3JkZXIoc3BlY2llcywgbGZjKSwgY29sID0gYWJuKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fbGluZXJhbmdlKGFlcyh4bWluID0gbG93ZXIsIHhtYXggPSB1cHBlcikpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICAgIGZhY2V0X3dyYXAofiBlbnYpICsgIAogICAgdGhlbWVfYncoKQoKIyB0b3AgMzAgc3BlY2llcyBmcm9tIFBDMgpleHBfbG9nZl9mdWxsICU+JQogIGZpbHRlcihzcGVjaWVzICVpbiUgZXhwX3BjYV9sb2FkaW5ncyRzcGVjaWVzWzE6MjBdKSAlPiUgCiAgc2xpY2VfaGVhZChuID0gNjApICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBsZmMsIHkgPSByZW9yZGVyKHNwZWNpZXMsIGxmYyksIGNvbCA9IGFibikpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBnZW9tX2xpbmVyYW5nZShhZXMoeG1pbiA9IGxvd2VyLCB4bWF4ID0gdXBwZXIpKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgICB0aGVtZV9idygpICsKICAgIGZhY2V0X3dyYXAofiBlbnYpCgojIyMgUFJPVklERSBPWFlHRU4gVE9MRVJBTkNFIE9GIFNQRUNJRVMgIyMjCmBgYAoKU3BlY2llcyB3aXRoIHRoZSBoaWdoZXN0IGxvZy1mb2xkIGNoYW5nZSBtYWlubHkgc2VlIGEgcmVkdWN0aW9uIGZyb20gc2FsaXZhIHRvCm1lZGl1bSwgYW5kIHRoZW4gYSBzbGlnaHQgZnVydGhlciByZWR1Y3Rpb24gZnJvbSBtZWRpdW0gdG8gY2FsY3VsdXMuIFRoZSBsYXJnZXN0CnJlZHVjdGlvbiBvZiBzcGVjaWVzIG9jY3VycyBpbgpgciBmaWx0ZXIoZXhwX2xvZ2ZfZnVsbCwgbGZjID09IG1heChsZmMpKSRzcGVjaWVzYAooTEZDID0gYHIgZmlsdGVyKGV4cF9sb2dmX2Z1bGwsIGxmYyA9PSBtYXgobGZjKSkkbGZjYCkuCgojIyMgQXJ0aWZpY2lhbCB2cy4gbW9kZXJuIGNhbGN1bHVzCgpgYGB7cn0KY2FsY3VsdXNfZGEgPC0gQU5DT01CQzo6YW5jb21iYygKICBjYWxjdWx1c19waHlsb3NlcSwgCiAgZm9ybXVsYSA9ICJFbnYiLAogIGdyb3VwID0gIkVudiIsCiAgZ2xvYmFsID0gRiwKICBwX2Fkal9tZXRob2QgPSAiZmRyIikKCiMgdGFibGUgd2l0aCBsb2ctZm9sZCBjaGFuZ2Ugc3RhdGlzdGljcyBiZXR3ZWVuIGFydGlmaWNpYWwgY2FsY3VsdXMgYW5kIG90aGVycwogICMgbmVnYXRpdmUgbGZjIG1lYW5zIGhpZ2hlciBhYnVuZGFuY2UgaW4gYXJ0aWZpY2lhbCBjYWxjdWx1cwogICMgcG9zaXRpdmUgbGZjIG1lYW5zIGxvd2VyIGFidW5kYW5jZSBpbiBhcnQgY2FsY3VsdXMKY2FsY19sb2dmX2NoYW5nZSA8LSBhc190aWJibGUoY2FsY3VsdXNfZGEkcmVzJGxmYywgcm93bmFtZXMgPSAic3BlY2llcyIpICMlPiUKICAgICNzZWxlY3Qoc3BlY2llcywgRW52bWVkaXVtKSAlPiUKY2FsY19sb2dmX3NlIDwtIGFzX3RpYmJsZShjYWxjdWx1c19kYSRyZXMkc2UsIHJvd25hbWVzID0gInNwZWNpZXMiKSAjJT4lCiAgICAjc2VsZWN0KCFjKEVudnNhbGl2YS54LCBFbnZzYWxpdmEueSkpICU+JQpjYWxjX2xvZ2ZfcSA8LSBhc190aWJibGUoY2FsY3VsdXNfZGEkcmVzJHFfdmFsLCByb3duYW1lcyA9ICJzcGVjaWVzIikKCmNhbGNfbG9nZl9mdWxsIDwtIGNhbGNfbG9nZl9jaGFuZ2UgJT4lCiAgcGl2b3RfbG9uZ2VyKC1zcGVjaWVzLCB2YWx1ZXNfdG8gPSAibGZjIikgJT4lCiAgZnVsbF9qb2luKAogICAgY2FsY19sb2dmX3NlICU+JQogICAgICBwaXZvdF9sb25nZXIoLXNwZWNpZXMsIHZhbHVlc190byA9ICJzZSIpLAogICAgYnkgPSBjKCJzcGVjaWVzIiwgIm5hbWUiKQogICkgJT4lCiAgZnVsbF9qb2luKAogICAgY2FsY19sb2dmX3EgJT4lCiAgICAgIHBpdm90X2xvbmdlcigtc3BlY2llcywgdmFsdWVzX3RvID0gInFfdmFsdWUiKSwKICAgIGJ5ID0gYygic3BlY2llcyIsICJuYW1lIikKICApICU+JQogIG11dGF0ZShsb3dlciA9IGxmYyAtIHNlLAogICAgICAgICB1cHBlciA9IGxmYyArIHNlLAogICAgICAgICBuYW1lID0gc3RyX3JlbW92ZShuYW1lLCAiXkVudiIpLAogICAgICAgICBhYm4gPSBjYXNlX3doZW4oc2lnbihsZmMpID09IC0xIH4gImJ5b2NfY2FsY3VsdXMiLAogICAgICAgICBUUlVFIH4gbmFtZSkpICU+JQogIHJlbmFtZShlbnYgPSBuYW1lKQoKaGVhZChjYWxjX2xvZ2ZfZnVsbCkKYGBgCgpQbG90cyBvZiBsb2ctZm9sZCBjaGFuZ2UgYmV0d2VlbiBzYW1wbGVzCgpgYGB7cn0KIyBQbG90IG9mIGxhcmdlc3QgYWJzb2x1dGUgbG9nLWZvbGQgY2hhbmdlcwpjYWxjX2xvZ2ZfZnVsbCAlPiUKICBncm91cF9ieShlbnYpICU+JSAKICBhcnJhbmdlKGRlc2MoYWJzKGxmYykpKSAlPiUKICAgIHNsaWNlX2hlYWQobiA9IDIwKSAlPiUgIyBjYXVzZXMgbG9zcyBvZiBzb21lIGVudiB2YWx1ZXMKICAgIHVuZ3JvdXAoKSAlPiUKICBzbGljZV9oZWFkKG4gPSAyMCkgJT4lICMgZXh0cmFjdCB0b3AgMjAgbGZjIGZyb20gbW9kZXJuX2NhbGN1bHVzCiAgYmluZF9yb3dzKGZpbHRlciggIyByZWNvbWJpbmUgd2l0aCBvdGhlciBlbnYgdmFsdWVzIHNvIGFsbCBhcmUgaW5jbHVkZWQgaW4gcGxvdAogICAgY2FsY19sb2dmX2Z1bGwsIAogICAgZW52ICE9ICJtb2Rlcm5fY2FsY3VsdXMiLAogICAgc3BlY2llcyAlaW4lIC4kc3BlY2llcwogICAgKQogICkgJT4lIAogICBnZ3Bsb3QoYWVzKHggPSBsZmMsIHkgPSByZW9yZGVyKHNwZWNpZXMsIGxmYyksIGNvbCA9IGFibikpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICAgIGdlb21fbGluZXJhbmdlKGFlcyh4bWluID0gbG93ZXIsIHhtYXggPSB1cHBlciksIHNpemUgPSAxKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgICBmYWNldF93cmFwKH4gZW52KSArCiAgICB0aGVtZV9idygpICsKICAgIHRoZW1lKAogICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKQogICAgICAgICAgKQoKIyBUb3AgMzAgbG9hZGluZ3MgZnJvbSBQQzEKCmNhbGNfbG9nZl9mdWxsICU+JQogIGZpbHRlcihzcGVjaWVzICVpbiUgcGNhX2xvYWRpbmdzJHNwZWNpZXNbMToyMF0pICU+JSAKICBzbGljZV9oZWFkKG4gPSA2MCkgJT4lICMgcGNhX2xvYWRpbmdzIGFscmVhZHkgYXJyYW5nZWQgYnkgUEMxCiAgZ2dwbG90KGFlcyh4ID0gbGZjLCB5ID0gcmVvcmRlcihzcGVjaWVzLCBsZmMpLCBjb2wgPSBhYm4pKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgICBnZW9tX2xpbmVyYW5nZShhZXMoeG1pbiA9IGxvd2VyLCB4bWF4ID0gdXBwZXIpLCBzaXplID0gMSkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogICAgZmFjZXRfd3JhcCh+IGVudikgKwogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZSgKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkKICAgICAgICAgICkKCiMgVG9wIDMwIGxvYWRpbmdzIGZyb20gUEMyCmNhbGNfbG9nZl9mdWxsICU+JQogIGZpbHRlcihzcGVjaWVzICVpbiUgYXJyYW5nZShwY2FfbG9hZGluZ3MsIGRlc2MoYWJzKFBDMikpKSRzcGVjaWVzWzE6MjBdKSAlPiUKICBzbGljZV9oZWFkKG4gPSA2MCkgJT4lICMgcGNhX2xvYWRpbmdzIGFscmVhZHkgYXJyYW5nZWQgYnkgUEMxCiAgZ2dwbG90KGFlcyh4ID0gbGZjLCB5ID0gcmVvcmRlcihzcGVjaWVzLCBsZmMpLCBjb2wgPSBhYm4pKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgICBnZW9tX2xpbmVyYW5nZShhZXMoeG1pbiA9IGxvd2VyLCB4bWF4ID0gdXBwZXIpLCBzaXplID0gMSkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogICAgZmFjZXRfd3JhcCh+IGVudikgKwogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZSgKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkKICAgICAgICAgICkKYGBgCgojIyMgQmlhcy1jb3JyZWN0ZWQgbG9nIG9ic2VydmVkIGFidW5kYW5jZXMKCkJpYXMtY29ycmVjdGVkIGxvZyBvYnNlcnZlZCBhYnVuZGFuY2VzIHdlcmUgY2FsY3VsYXRlZCBieSBmb2xsb3dpbmcgdGhlIGV4YW1wbGUKaW4gYHZpZ25ldHRlKCJBTkNPTUJDIilgLgoKYGBge3J9CiMgZnJvbSB2aWduZXR0ZSgiQU5DT01CQyIpCmNhbGNfc2FtcF9mcmFjIDwtIGNhbGN1bHVzX2RhJHNhbXBfZnJhYwojIFJlcGxhY2UgTkEgd2l0aCAwCmNhbGNfc2FtcF9mcmFjW2lzLm5hKGNhbGNfc2FtcF9mcmFjKV0gPSAwIAojIGFkZCAxIHRvIGNvdW50cyBmb3IgbG9nLXRyYW5zZm9ybWF0aW9uCmNhbGNfbG9nX2V4cF9vdHUgPC0gbG9nKG1pY3JvYmlvbWU6OmFidW5kYW5jZXMoY2FsY3VsdXNfb3R1KSArIDEpCgojIEFkanVzdCB0aGUgbG9nIG9ic2VydmVkIGFidW5kYW5jZXMKIyBCaWFzLWNvcnJlY3RlZCAobG9nKSBvYnNlcnZlZCBhYnVuZGFuY2VzCmNhbGNfbG9nX2FibiA8LSB0KHQoY2FsY19sb2dfZXhwX290dSkgLSBjYWxjX3NhbXBfZnJhYykgJT4lCiAgYXNfdGliYmxlKHJvd25hbWVzID0gInNwZWNpZXMiKQoKIyBuZWVkIHRvIGNvbnZlcnQgY291bnRzIGJhY2sgdG8gemVybyAoY291bnRzIHRoYXQgYXJlID0gc2FtcF9mcmFjKQoKY2FsY19sb2dfYWJuCmBgYAoKIyBEaXNjdXNzaW9uCgoqRW50ZXJvY29jY3VzIGZhZWNhbGlzKiBoYXMgdGhlIGhpZ2hlc3QgbG9nLWZvbGQgY2hhbmdlLCB3aXRoIGEgaGlnaGVyIGFidW5kYW5jZQppbiB0aGUgYXJ0aWZpY2lhbCBjYWxjdWx1cyBzYW1wbGVzIGNvbXBhcmVkIHRvIHRoZSByZWZlcmVuY2Ugc2FtcGxlcyBhbmQKbWF5IHJlcHJlc2VudCBvbmUgb2YgdGhlIG1haW4gZGlmZmVyZW5jZXMgYmV0d2VlbiBhcnRpZmljaWFsIGNhbGN1bHVzIGFuZCBvdGhlcgpyZWZlcmVuY2Ugc2FtcGxlcywgZXNwZWNpYWxseSBtb2Rlcm4gY2FsY3VsdXMsIGNvbnNpc3RlbnQgd2l0aCB0aGUgcmVzdWx0cyBvZiB0aGUKc1BDQSBhbmFseXNpcy4K