Load necessary packages.
Upload the data and metadata.
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)

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