Load necessary packages.

library(cuperdec)
library(decontam)
library(here)
library(tidyverse)

Upload the data and metadata.

# upload data
lib_conc <- readr::read_tsv(here("01-documentation/SYN_DNA_concentrations.tsv")) # library concentrations
metadata <- readr::read_tsv(here("01-documentation/metadata.tsv"))
experiment_metadata <- readr::read_tsv(here("01-documentation/experiment-metadata.tsv"))
analysis_metadata <- readr::read_tsv(here("01-documentation/analysis-metadata.tsv"))
kraken_taxatab <- readr::read_tsv(here("04-analysis/OTUfilter_table.tsv"))
sourcetracker2 <- readr::read_tsv(here("04-analysis/sourcetracker/sourcetracker2_output/mixing_proportions.txt"))
sourcetracker2_stdevs <- readr::read_tsv(here("04-analysis/sourcetracker/sourcetracker2_output/mixing_proportions_stds.txt")) 
otu_decontam <- read_tsv(here("05-results/post-decontam_taxatable.tsv"))
all_data_long <- read_tsv(here("04-analysis/sourcetracker/source-comb_long.tsv"))

SourceTracker

Steps taken for SourceTracker analysis.

OTU table was filtered for relative abundance. Percent abundance of each taxon across all samples was calculated and then taxa with lower than 0.001% abundance were filtered out. See data prep script.

Installation of Qiime2 and dev version of SourceTracker via conda:

wget https://data.qiime2.org/distro/core/qiime2-2022.2-py38-linux-conda.yml
conda env create -n qiime2-2022.2 --file qiime2-2022.2-py38-linux-conda.yml
rm qiime2-2022.2-py38-linux-conda.yml # cleanup
conda activate qiime2-2022.2
pip install https://github.com/biota/sourcetracker2/archive/master.zip

Installation of Qiime1 to access filter_samples_from_otu_table.py

conda create -n qiime1 python=2.7 qiime -c bioconda

Convert OTU table from .tsv to .biom.

biom convert -i 04-analysis/OTUfilter_table.tsv -o 04-analysis/sourcetracker/OTUfilter-table-from-tsv_json.biom --table-type="OTU table" --to-json

Filter OTU table

filter_samples_from_otu_table.py \
-i 04-analysis/sourcetracker/OTUfilter-table-from-tsv_json.biom \
-o 04-analysis/sourcetracker/OTUs1000filter_table.biom \
-n 1000

Table summary

biom summarize-table -i 04-analysis/sourcetracker/OTUs1000filter_table.biom > 04-analysis/sourcetracker/summary_OTUs1000filter-table.txt

Convert to TSV for use with the decontam package.

biom convert -i 04-analysis/sourcetracker/OTUs1000filter_table.biom -o 04-analysis/decontam/pre-decontam_OTUfiltered-table_from-biom.tsv --to-tsv

Run SourceTracker2 on the filtered OTU table with rarefaction depth of 1000 for both source and samples. Samples and sources were mapped in the ST_comb-plaque-map.txt. The plaque source is a combination of supragingival and subgingival plaque.

conda activate qiime2-2022.2

Another run with indoor_air sources included and sediment removed

sourcetracker2 \
    -i 04-analysis/sourcetracker/OTUs1000filter_table.biom \
    -m 04-analysis/sourcetracker/ST_comb-plaque-map.txt \
    --source_sink_column SourceSink  \
    --source_column_value source \
    --source_rarefaction_depth 1000 \
    --sink_rarefaction_depth 1000 \
    --sink_column_value sink \
    --source_category_column Env \
    -o 04-analysis/sourcetracker/sourcetracker2_output \
    --jobs 2 \
    --per_sink_feature_assignments

The full results can be found in 04-analysis/sourcetracker/sourcetracker2_output

# convert to long format
sourcetracker2_long <- sourcetracker2 %>%
  pivot_longer(cols = where(is.numeric), 
               values_to = "proportion", 
               names_to = "SampleID") %>%
  rename(source = ...1)

Visualisation of sources

The samples were organised by day they were sampled,

day_order <- experiment_metadata %>%
  mutate(Env = factor(
    Env, 
    levels = c("saliva", "medium", "calculus") # force level order so it doesn't order alphabetically
    ) 
  ) %>% 
  group_by(Env) %>%
  arrange(day, .by_group = T) %>%
  filter(`#SampleID` %in% colnames(sourcetracker2)) %>%
  mutate(rm = if_else(`#SampleID` %in% analysis_metadata$`#SampleID`, F, T),
         col = case_when(rm == T ~ "red",
                         rm == F ~ "black"))

removed_samples <- experiment_metadata %>%
  mutate(rm = if_else(`#SampleID` %in% analysis_metadata$`#SampleID`, F, T),
         col = case_when(rm == T ~ "red",
                         rm == F ~ "black")) %>% 
  mutate(sample = `#SampleID`) %>%
  arrange(day)

then visualised.

sourcetracker2_long %>% 
  ggplot(aes(y = SampleID, x= proportion, fill = source)) +
    geom_col() +
    theme_minimal() +
    scale_y_discrete(limits = day_order$`#SampleID`) +
    scale_fill_viridis_d(option = "C") +
    theme(axis.title = element_blank(),
          axis.text.y = element_text(vjust = 0.5, hjust = 0.5, colour = day_order$col))

Many of the later medium samples were assigned to the ‘Unknown’ and ‘indoor_air’ categories. To see whether this was the result of external contamination or the presence of oral taxa with an unknown origin (which could be due to the taxa matching multiple sources), the taxa from ‘Unknown’ were compared to the oral reference database from the cuperdec R package. Only samples with a large proportion of oral taxa and a low proportion of indoor_air (indoor_air + Unknown < oral and oral > 70%) were included. Included sample names are indicated with black, and removed samples with red.

oral_taxa <- cuperdec_database_ex %>%
  filter(isolation_source == "oral")
all_data_long %>% 
  filter(count > 0) %>% 
  mutate(oral_source = if_else(taxon %in% oral_taxa$species, "oral", "other")) %>%
  ggplot(aes(y = SampleID, x = count, fill = oral_source)) +
    geom_col(position = "fill") +
    scale_fill_viridis_d(option = "C") +
    theme_minimal() +
    theme(axis.text.y = element_text(colour = day_order$col),
          axis.title.y = element_blank()) +
    scale_y_discrete(limits = day_order$`#SampleID`)

A large proportion of the species assigned to the various sources are known to be oral taxa. The samples do also seem to contain some external contamination.

Based on the results from SourceTracker, samples c(“SYN015.F0101”, “SYN017.F0101”, “SYN015.G0101”, “SYN017.G0101”, “SYN015.H0101”, “SYN018.H0101”, “SYN013.I0101”, “SYN016.I0101”) were removed from the analysis.

cuperdec

taxa_table <- load_taxa_table(kraken_taxatab)
iso_database <- load_database(cuperdec_database_ex, target = "oral")
metadata_table <- load_map(metadata,
                           sample_col = "#SampleID",
                           source_col = "Env"
                           )

curves <- calculate_curve(taxa_table, iso_database)
filter_result <- simple_filter(curves, 60)
plot_cuperdec(curves, metadata_table, filter_result)

decontam

species_table_long <- otu_decontam %>%
  pivot_longer(cols = where(is.numeric), names_to = "sample", values_to = "count")

byoc_table_long <- species_table_long %>% 
  filter(str_detect(sample, "SYN"),
         sample %in% analysis_metadata$`#SampleID`,
         count > 0)

species_summ <- byoc_table_long %>%
  group_by(sample) %>%
  count(`#OTU ID`) %>%
  summarise(count = sum(n))

After running the decontam package, the samples contained between 87 and 269 species with a mean of 172.89.

LS0tCnRpdGxlOiAiQXV0aGVudGljYXRpb24iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19rbml0JHNldChtZXNzYWdlID0gRkFMU0UpCmBgYAoKTG9hZCBuZWNlc3NhcnkgcGFja2FnZXMuCgpgYGB7cn0KI3wgbGFiZWw6IGRlcGVuZGVuY2llcwpsaWJyYXJ5KGN1cGVyZGVjKQpsaWJyYXJ5KGRlY29udGFtKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKClVwbG9hZCB0aGUgZGF0YSBhbmQgbWV0YWRhdGEuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojfCBsYWJlbDogZGF0YS11cGxvYWQKIyB1cGxvYWQgZGF0YQpsaWJfY29uYyA8LSByZWFkcjo6cmVhZF90c3YoaGVyZSgiMDEtZG9jdW1lbnRhdGlvbi9TWU5fRE5BX2NvbmNlbnRyYXRpb25zLnRzdiIpKSAjIGxpYnJhcnkgY29uY2VudHJhdGlvbnMKbWV0YWRhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KGhlcmUoIjAxLWRvY3VtZW50YXRpb24vbWV0YWRhdGEudHN2IikpCmV4cGVyaW1lbnRfbWV0YWRhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KGhlcmUoIjAxLWRvY3VtZW50YXRpb24vZXhwZXJpbWVudC1tZXRhZGF0YS50c3YiKSkKYW5hbHlzaXNfbWV0YWRhdGEgPC0gcmVhZHI6OnJlYWRfdHN2KGhlcmUoIjAxLWRvY3VtZW50YXRpb24vYW5hbHlzaXMtbWV0YWRhdGEudHN2IikpCmtyYWtlbl90YXhhdGFiIDwtIHJlYWRyOjpyZWFkX3RzdihoZXJlKCIwNC1hbmFseXNpcy9PVFVmaWx0ZXJfdGFibGUudHN2IikpCnNvdXJjZXRyYWNrZXIyIDwtIHJlYWRyOjpyZWFkX3RzdihoZXJlKCIwNC1hbmFseXNpcy9zb3VyY2V0cmFja2VyL3NvdXJjZXRyYWNrZXIyX291dHB1dC9taXhpbmdfcHJvcG9ydGlvbnMudHh0IikpCnNvdXJjZXRyYWNrZXIyX3N0ZGV2cyA8LSByZWFkcjo6cmVhZF90c3YoaGVyZSgiMDQtYW5hbHlzaXMvc291cmNldHJhY2tlci9zb3VyY2V0cmFja2VyMl9vdXRwdXQvbWl4aW5nX3Byb3BvcnRpb25zX3N0ZHMudHh0IikpIApvdHVfZGVjb250YW0gPC0gcmVhZF90c3YoaGVyZSgiMDUtcmVzdWx0cy9wb3N0LWRlY29udGFtX3RheGF0YWJsZS50c3YiKSkKYWxsX2RhdGFfbG9uZyA8LSByZWFkX3RzdihoZXJlKCIwNC1hbmFseXNpcy9zb3VyY2V0cmFja2VyL3NvdXJjZS1jb21iX2xvbmcudHN2IikpCmBgYAoKIyMgU291cmNlVHJhY2tlcgoKU3RlcHMgdGFrZW4gZm9yIFNvdXJjZVRyYWNrZXIgYW5hbHlzaXMuCgpPVFUgdGFibGUgd2FzIGZpbHRlcmVkIGZvciByZWxhdGl2ZSBhYnVuZGFuY2UuIFBlcmNlbnQgYWJ1bmRhbmNlIG9mIGVhY2ggdGF4b24KYWNyb3NzIGFsbCBzYW1wbGVzIHdhcyBjYWxjdWxhdGVkIGFuZCB0aGVuIHRheGEgd2l0aCBsb3dlciB0aGFuIDAuMDAxJSBhYnVuZGFuY2UKd2VyZSBmaWx0ZXJlZCBvdXQuIFNlZSBbZGF0YSBwcmVwIHNjcmlwdF0oLi4vMDItc2NyaXB0cy8wMS1kYXRhcHJlcC5SKS4KCkluc3RhbGxhdGlvbiBvZiBRaWltZTIgYW5kIGRldiB2ZXJzaW9uIG9mIFNvdXJjZVRyYWNrZXIgdmlhIGNvbmRhOgoKYGBgc2gKd2dldCBodHRwczovL2RhdGEucWlpbWUyLm9yZy9kaXN0cm8vY29yZS9xaWltZTItMjAyMi4yLXB5MzgtbGludXgtY29uZGEueW1sCmNvbmRhIGVudiBjcmVhdGUgLW4gcWlpbWUyLTIwMjIuMiAtLWZpbGUgcWlpbWUyLTIwMjIuMi1weTM4LWxpbnV4LWNvbmRhLnltbApybSBxaWltZTItMjAyMi4yLXB5MzgtbGludXgtY29uZGEueW1sICMgY2xlYW51cApjb25kYSBhY3RpdmF0ZSBxaWltZTItMjAyMi4yCnBpcCBpbnN0YWxsIGh0dHBzOi8vZ2l0aHViLmNvbS9iaW90YS9zb3VyY2V0cmFja2VyMi9hcmNoaXZlL21hc3Rlci56aXAKYGBgCgpJbnN0YWxsYXRpb24gb2YgUWlpbWUxIHRvIGFjY2VzcyBmaWx0ZXJfc2FtcGxlc19mcm9tX290dV90YWJsZS5weQoKYGBgc2gKY29uZGEgY3JlYXRlIC1uIHFpaW1lMSBweXRob249Mi43IHFpaW1lIC1jIGJpb2NvbmRhCmBgYAoKQ29udmVydCBPVFUgdGFibGUgZnJvbSAqLnRzdiogdG8gKi5iaW9tKi4KCmBgYHNoCmJpb20gY29udmVydCAtaSAwNC1hbmFseXNpcy9PVFVmaWx0ZXJfdGFibGUudHN2IC1vIDA0LWFuYWx5c2lzL3NvdXJjZXRyYWNrZXIvT1RVZmlsdGVyLXRhYmxlLWZyb20tdHN2X2pzb24uYmlvbSAtLXRhYmxlLXR5cGU9Ik9UVSB0YWJsZSIgLS10by1qc29uCmBgYAoKRmlsdGVyIE9UVSB0YWJsZQoKYGBgc2gKZmlsdGVyX3NhbXBsZXNfZnJvbV9vdHVfdGFibGUucHkgXAotaSAwNC1hbmFseXNpcy9zb3VyY2V0cmFja2VyL09UVWZpbHRlci10YWJsZS1mcm9tLXRzdl9qc29uLmJpb20gXAotbyAwNC1hbmFseXNpcy9zb3VyY2V0cmFja2VyL09UVXMxMDAwZmlsdGVyX3RhYmxlLmJpb20gXAotbiAxMDAwCmBgYAoKVGFibGUgc3VtbWFyeQoKYGBgc2gKYmlvbSBzdW1tYXJpemUtdGFibGUgLWkgMDQtYW5hbHlzaXMvc291cmNldHJhY2tlci9PVFVzMTAwMGZpbHRlcl90YWJsZS5iaW9tID4gMDQtYW5hbHlzaXMvc291cmNldHJhY2tlci9zdW1tYXJ5X09UVXMxMDAwZmlsdGVyLXRhYmxlLnR4dApgYGAKCkNvbnZlcnQgdG8gVFNWIGZvciB1c2Ugd2l0aCB0aGUgKipkZWNvbnRhbSoqIHBhY2thZ2UuCgpgYGBzaApiaW9tIGNvbnZlcnQgLWkgMDQtYW5hbHlzaXMvc291cmNldHJhY2tlci9PVFVzMTAwMGZpbHRlcl90YWJsZS5iaW9tIC1vIDA0LWFuYWx5c2lzL2RlY29udGFtL3ByZS1kZWNvbnRhbV9PVFVmaWx0ZXJlZC10YWJsZV9mcm9tLWJpb20udHN2IC0tdG8tdHN2CmBgYAoKUnVuIFNvdXJjZVRyYWNrZXIyIG9uIHRoZSBmaWx0ZXJlZCBPVFUgdGFibGUgd2l0aCByYXJlZmFjdGlvbiBkZXB0aCBvZiAxMDAwCmZvciBib3RoIHNvdXJjZSBhbmQgc2FtcGxlcy4gU2FtcGxlcyBhbmQgc291cmNlcyB3ZXJlIG1hcHBlZCBpbiB0aGUKW1NUX2NvbWItcGxhcXVlLW1hcC50eHRdKC4uLzA0LWFuYWx5c2lzL3NvdXJjZXRyYWNrZXIvU1RfY29tYi1wbGFxdWUtbWFwLnR4dCkuClRoZSBwbGFxdWUgc291cmNlIGlzIGEgY29tYmluYXRpb24gb2Ygc3VwcmFnaW5naXZhbCBhbmQgc3ViZ2luZ2l2YWwgcGxhcXVlLgoKYGBgc2gKY29uZGEgYWN0aXZhdGUgcWlpbWUyLTIwMjIuMgpgYGAKCkFub3RoZXIgcnVuIHdpdGggaW5kb29yX2FpciBzb3VyY2VzIGluY2x1ZGVkIGFuZCBzZWRpbWVudCByZW1vdmVkCgpgYGBzaApzb3VyY2V0cmFja2VyMiBcCiAgICAtaSAwNC1hbmFseXNpcy9zb3VyY2V0cmFja2VyL09UVXMxMDAwZmlsdGVyX3RhYmxlLmJpb20gXAogICAgLW0gMDQtYW5hbHlzaXMvc291cmNldHJhY2tlci9TVF9jb21iLXBsYXF1ZS1tYXAudHh0IFwKICAgIC0tc291cmNlX3NpbmtfY29sdW1uIFNvdXJjZVNpbmsgIFwKICAgIC0tc291cmNlX2NvbHVtbl92YWx1ZSBzb3VyY2UgXAogICAgLS1zb3VyY2VfcmFyZWZhY3Rpb25fZGVwdGggMTAwMCBcCiAgICAtLXNpbmtfcmFyZWZhY3Rpb25fZGVwdGggMTAwMCBcCiAgICAtLXNpbmtfY29sdW1uX3ZhbHVlIHNpbmsgXAogICAgLS1zb3VyY2VfY2F0ZWdvcnlfY29sdW1uIEVudiBcCiAgICAtbyAwNC1hbmFseXNpcy9zb3VyY2V0cmFja2VyL3NvdXJjZXRyYWNrZXIyX291dHB1dCBcCiAgICAtLWpvYnMgMiBcCiAgICAtLXBlcl9zaW5rX2ZlYXR1cmVfYXNzaWdubWVudHMKYGBgCgpUaGUgZnVsbCByZXN1bHRzIGNhbiBiZSBmb3VuZCBpbiBbMDQtYW5hbHlzaXMvc291cmNldHJhY2tlci9zb3VyY2V0cmFja2VyMl9vdXRwdXRdKC4uLzA0LWFuYWx5c2lzL3NvdXJjZXRyYWNrZXIvc291cmNldHJhY2tlcjJfb3V0cHV0LykKCmBgYHtyfQojIGNvbnZlcnQgdG8gbG9uZyBmb3JtYXQKc291cmNldHJhY2tlcjJfbG9uZyA8LSBzb3VyY2V0cmFja2VyMiAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHdoZXJlKGlzLm51bWVyaWMpLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInByb3BvcnRpb24iLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiU2FtcGxlSUQiKSAlPiUKICByZW5hbWUoc291cmNlID0gLi4uMSkKYGBgCgoKIyMjIFZpc3VhbGlzYXRpb24gb2Ygc291cmNlcwoKVGhlIHNhbXBsZXMgd2VyZSBvcmdhbmlzZWQgYnkgZGF5IHRoZXkgd2VyZSBzYW1wbGVkLAoKYGBge3J9CmRheV9vcmRlciA8LSBleHBlcmltZW50X21ldGFkYXRhICU+JQogIG11dGF0ZShFbnYgPSBmYWN0b3IoCiAgICBFbnYsIAogICAgbGV2ZWxzID0gYygic2FsaXZhIiwgIm1lZGl1bSIsICJjYWxjdWx1cyIpICMgZm9yY2UgbGV2ZWwgb3JkZXIgc28gaXQgZG9lc24ndCBvcmRlciBhbHBoYWJldGljYWxseQogICAgKSAKICApICU+JSAKICBncm91cF9ieShFbnYpICU+JQogIGFycmFuZ2UoZGF5LCAuYnlfZ3JvdXAgPSBUKSAlPiUKICBmaWx0ZXIoYCNTYW1wbGVJRGAgJWluJSBjb2xuYW1lcyhzb3VyY2V0cmFja2VyMikpICU+JQogIG11dGF0ZShybSA9IGlmX2Vsc2UoYCNTYW1wbGVJRGAgJWluJSBhbmFseXNpc19tZXRhZGF0YSRgI1NhbXBsZUlEYCwgRiwgVCksCiAgICAgICAgIGNvbCA9IGNhc2Vfd2hlbihybSA9PSBUIH4gInJlZCIsICMgZXhjbHVkZWQgc2FtcGxlcyBjb2xvdXJlZCByZWQKICAgICAgICAgICAgICAgICAgICAgICAgIHJtID09IEYgfiAiYmxhY2siKSkKCiMgcmVtb3ZlZF9zYW1wbGVzIDwtIGV4cGVyaW1lbnRfbWV0YWRhdGEgJT4lCiMgICBtdXRhdGUocm0gPSBpZl9lbHNlKGAjU2FtcGxlSURgICVpbiUgYW5hbHlzaXNfbWV0YWRhdGEkYCNTYW1wbGVJRGAsIEYsIFQpLAojICAgICAgICAgIGNvbCA9IGNhc2Vfd2hlbihybSA9PSBUIH4gInJlZCIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHJtID09IEYgfiAiYmxhY2siKSkgJT4lIAojICAgbXV0YXRlKHNhbXBsZSA9IGAjU2FtcGxlSURgKSAlPiUKIyAgIGFycmFuZ2UoZGF5KQpgYGAKCnRoZW4gdmlzdWFsaXNlZC4KCmBgYHtyfQojfCBsYWJlbDogc3QtcGxvdAojfCBmaWctY2FwOiAiUGxvdCBvZiBlc3RpbWF0ZWQgY29udHJpYnV0aW9ucyBvZiB2YXJpb3VzIHNvdXJjZXMgdG8gdGhlIGFydGlmaWNpYWwgY2FsY3VsdXMgYW5kIGFydGlmaWNpYWwgc2FsaXZhIHNhbXBsZXMuIFNhbXBsZXMgYXJlIGFycmFuZ2VkIGZyb20gbGVmdCB0byByaWdodCBieSBob3cgbGF0ZSBpbiB0aGUgZXhwZXJpbWVudCB0aGV5IHdlcmUgc2FtcGxlZCwgd2l0aCBsZWZ0IGJlaW5nIHRoZSBlYXJsaWVzdCBzYW1wbGVzLiIKc291cmNldHJhY2tlcjJfbG9uZyAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gU2FtcGxlSUQsIHg9IHByb3BvcnRpb24sIGZpbGwgPSBzb3VyY2UpKSArCiAgICBnZW9tX2NvbCgpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IGRheV9vcmRlciRgI1NhbXBsZUlEYCkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gIkMiKSArCiAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQodmp1c3QgPSAwLjUsIGhqdXN0ID0gMC41LCBjb2xvdXIgPSBkYXlfb3JkZXIkY29sKSkKYGBgCgpNYW55IG9mIHRoZSBsYXRlciBtZWRpdW0gc2FtcGxlcyB3ZXJlIGFzc2lnbmVkIHRvIHRoZSAnVW5rbm93bicgYW5kICdpbmRvb3JfYWlyJwpjYXRlZ29yaWVzLiBUbyBzZWUgd2hldGhlciB0aGlzIHdhcyB0aGUgcmVzdWx0IG9mIGV4dGVybmFsCmNvbnRhbWluYXRpb24gb3IgdGhlIHByZXNlbmNlIG9mIG9yYWwgdGF4YSB3aXRoIGFuIHVua25vd24gb3JpZ2luICh3aGljaCBjb3VsZCBiZQpkdWUgdG8gdGhlIHRheGEgbWF0Y2hpbmcgbXVsdGlwbGUgc291cmNlcyksIHRoZSB0YXhhIGZyb20gJ1Vua25vd24nIHdlcmUgY29tcGFyZWQKdG8gdGhlIG9yYWwgcmVmZXJlbmNlIGRhdGFiYXNlIGZyb20gdGhlICoqY3VwZXJkZWMqKiBSIHBhY2thZ2UuIE9ubHkgc2FtcGxlcyB3aXRoCmEgbGFyZ2UgcHJvcG9ydGlvbiBvZiBvcmFsIHRheGEgYW5kIGEgbG93IHByb3BvcnRpb24gb2YgaW5kb29yX2FpcgooYGluZG9vcl9haXIgKyBVbmtub3duIDwgb3JhbGAgYW5kIGBvcmFsID4gNzAlYCkgd2VyZSBpbmNsdWRlZC4gSW5jbHVkZWQgc2FtcGxlCm5hbWVzIGFyZSBpbmRpY2F0ZWQgd2l0aCBibGFjaywgYW5kIHJlbW92ZWQgc2FtcGxlcyB3aXRoIHJlZC4KCmBgYHtyfQojfCBsYWJlbDogdW5rbm93bnMtcGxvdAojfCBmaWctY2FwOiAiUGxvdCBvZiBTb3VyY2VUcmFja2VyIHNhbXBsZXMsIHdoZXRoZXIgYXJlIG9yYWwgb3Igb3RoZXIuIgpvcmFsX3RheGEgPC0gY3VwZXJkZWNfZGF0YWJhc2VfZXggJT4lCiAgZmlsdGVyKGlzb2xhdGlvbl9zb3VyY2UgPT0gIm9yYWwiKQphbGxfZGF0YV9sb25nICU+JSAKICBmaWx0ZXIoY291bnQgPiAwKSAlPiUgCiAgbXV0YXRlKG9yYWxfc291cmNlID0gaWZfZWxzZSh0YXhvbiAlaW4lIG9yYWxfdGF4YSRzcGVjaWVzLCAib3JhbCIsICJvdGhlciIpKSAlPiUKICBnZ3Bsb3QoYWVzKHkgPSBTYW1wbGVJRCwgeCA9IGNvdW50LCBmaWxsID0gb3JhbF9zb3VyY2UpKSArCiAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJmaWxsIikgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gIkMiKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3VyID0gZGF5X29yZGVyJGNvbCksCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gZGF5X29yZGVyJGAjU2FtcGxlSURgKQpgYGAKCkEgbGFyZ2UgcHJvcG9ydGlvbiBvZiB0aGUgc3BlY2llcyBhc3NpZ25lZCB0byB0aGUgdmFyaW91cyBzb3VyY2VzIGFyZSBrbm93biB0bwpiZSBvcmFsIHRheGEuIFRoZSBzYW1wbGVzIGRvIGFsc28gc2VlbSB0byBjb250YWluIHNvbWUgZXh0ZXJuYWwgY29udGFtaW5hdGlvbi4KCkJhc2VkIG9uIHRoZSByZXN1bHRzIGZyb20gU291cmNlVHJhY2tlciwgc2FtcGxlcwpgciBwYXN0ZShhcy52ZWN0b3IoZmlsdGVyKHJlbW92ZWRfc2FtcGxlcywgcm0gPT0gVClbLDFdKSwgc2VwID0gIiwiKWAKd2VyZSByZW1vdmVkIGZyb20gdGhlIGFuYWx5c2lzLgoKIyMgY3VwZXJkZWMKCmBgYHtyfQp0YXhhX3RhYmxlIDwtIGxvYWRfdGF4YV90YWJsZShrcmFrZW5fdGF4YXRhYikKaXNvX2RhdGFiYXNlIDwtIGxvYWRfZGF0YWJhc2UoY3VwZXJkZWNfZGF0YWJhc2VfZXgsIHRhcmdldCA9ICJvcmFsIikKbWV0YWRhdGFfdGFibGUgPC0gbG9hZF9tYXAobWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9jb2wgPSAiI1NhbXBsZUlEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc291cmNlX2NvbCA9ICJFbnYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCmN1cnZlcyA8LSBjYWxjdWxhdGVfY3VydmUodGF4YV90YWJsZSwgaXNvX2RhdGFiYXNlKQpmaWx0ZXJfcmVzdWx0IDwtIHNpbXBsZV9maWx0ZXIoY3VydmVzLCA2MCkKYGBgCgpgYGB7cn0KcGxvdF9jdXBlcmRlYyhjdXJ2ZXMsIG1ldGFkYXRhX3RhYmxlLCBmaWx0ZXJfcmVzdWx0KQpgYGAKCiMjIGRlY29udGFtCgpgYGB7cn0Kc3BlY2llc190YWJsZV9sb25nIDwtIG90dV9kZWNvbnRhbSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHdoZXJlKGlzLm51bWVyaWMpLCBuYW1lc190byA9ICJzYW1wbGUiLCB2YWx1ZXNfdG8gPSAiY291bnQiKQoKYnlvY190YWJsZV9sb25nIDwtIHNwZWNpZXNfdGFibGVfbG9uZyAlPiUgCiAgZmlsdGVyKHN0cl9kZXRlY3Qoc2FtcGxlLCAiU1lOIiksCiAgICAgICAgIHNhbXBsZSAlaW4lIGFuYWx5c2lzX21ldGFkYXRhJGAjU2FtcGxlSURgLAogICAgICAgICBjb3VudCA+IDApCgpzcGVjaWVzX3N1bW0gPC0gYnlvY190YWJsZV9sb25nICU+JQogIGdyb3VwX2J5KHNhbXBsZSkgJT4lCiAgY291bnQoYCNPVFUgSURgKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBzdW0obikpCmBgYAoKQWZ0ZXIgcnVubmluZyB0aGUgKipkZWNvbnRhbSoqIHBhY2thZ2UsIHRoZSBzYW1wbGVzIGNvbnRhaW5lZCBiZXR3ZWVuCmByIHBhc3RlKHJhbmdlKHNwZWNpZXNfc3VtbSRjb3VudCksIGNvbGxhcHNlID0gIiBhbmQgIilgCnNwZWNpZXMgd2l0aCBhIG1lYW4gb2YKYHIgcGFzdGUocm91bmQobWVhbihzcGVjaWVzX3N1bW0kY291bnQpLCAyKSlgLgoKYGBge3J9CiMgcmVtYWluaW5nIHNwZWNpZXMgY291bnQgaW4gZWFjaCBzYW1wbGUKYnlvY190YWJsZV9sb25nICU+JSAKICBncm91cF9ieShzYW1wbGUpICU+JQogIGNvdW50KGAjT1RVIElEYCkgJT4lIAogIGdncGxvdChhZXMoeSA9IHNhbXBsZSwgeCA9IG4pKSArCiAgICBnZW9tX2NvbCgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoCiAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKCksCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoK