Load packages
suppressMessages(library(dplyr))
suppressMessages(library(umap))
suppressMessages(library(ggplot2))
suppressMessages(library(devtools))
suppressMessages(library(gdata))
Directories and Files
Directories
# Data directory
data_dir <- file.path("~/Documents/GitHub/jharenza.github.io/openPBTA-notebooks/mb-subtypes/data/")
pbta_data_dir <- file.path("~/OpenPBTA-analysis/data/release-v16-20200320/")
# Create a results directory
results_dir <- "results"
if (!dir.exists(results_dir)) {
dir.create(results_dir, recursive = TRUE)
}
Read in files
# PBTA
pbta_fpkm_collapsed_mat <- readRDS(pbta_rnaseq_file)
pbta_fpkm_full_mat <- readRDS(pbta_rnaseq_file_full)
pbta_histologies_df <- read.delim(pbta_hist_file, sep = "\t", header = T)
# PPTC
load(file.path(data_dir, "2019-02-14-pptc-STAR_cufflinks_hg19_matrix_244.rda"), verbose = T)
Loading objects:
rna.mat
pptc_subtypes_df <- read.delim(pptc_annot_file, sep = "\t", header = T)
# Rename PPTC df, remove gene name column, subset MB samples
mv(from = "rna.mat", to = "pptc_fpkm_df")
pptc_fpkm_df$gene_short_name <- NULL
pptc_mb_df <- pptc_fpkm_df %>%
select(pptc_subtypes_df$Sample)
# Michael Taylor (MB classifier training set), rename
load(file.path(data_dir, "mt_rnaseq.RData"), verbose = T)
Loading objects:
mt_mat
mt_annot
mv(from = "mt_mat", to = "mt_fpkm_df")
head(mt_fpkm_df)
head(mt_annot)
# MM2S PBTA classifier results, MB classifier results, pathology data
# MM2S Paper: https://scfbm.biomedcentral.com/articles/10.1186/s13029-016-0053-y
pbta_combined_subtypes <- read.delim(classifier_path_file, sep = "\t", header = T)
# How many of each subtype identified by pathology?
table(pbta_combined_subtypes$pathology_subtype)
Group 3 or 4 Group 4 non-WNT SHH WNT
11 2 1 12 6
PBTA wrangling
# Only pull out sample identifiers (KidsFirst biospecimen identifiers) that correspond to medulloblastoma samples
medulloblastoma_samples <- pbta_histologies_df %>%
filter(short_histology == "Medulloblastoma") %>%
filter(experimental_strategy == "RNA-Seq") %>%
filter(RNA_library == "stranded") %>%
pull(Kids_First_Biospecimen_ID)
# Calculate N
length(medulloblastoma_samples)
[1] 121
# Collect subtypes and count
mb_molecular_subtype_df <- pbta_histologies_df %>%
filter(short_histology == "Medulloblastoma") %>%
filter(experimental_strategy == "RNA-Seq") %>%
filter(RNA_library == "stranded") %>%
select(Kids_First_Biospecimen_ID, molecular_subtype, seq_center)
# Calculate N
nrow(mb_molecular_subtype_df)
[1] 121
# How many per subtype?
table(droplevels(mb_molecular_subtype_df$molecular_subtype))
Group3 Group4 SHH WNT
15 67 28 11
Create matrices of only MB samples
# select PBTA MB samples only
pbta_mb_coll_df <- pbta_fpkm_collapsed_mat %>%
select(medulloblastoma_samples)
pbta_mb_full_df <- pbta_fpkm_full_mat %>%
select(medulloblastoma_samples)
# select PPTC MB samples only
pptc_mb_df <- pptc_fpkm_df %>%
select(pptc_subtypes_df$Sample)
length(pptc_subtypes_df$Sample)
[1] 18
# How many per subtype?
table(pptc_subtypes_df$class_subtype)
Group3 Group4 SHH WNT
7 2 7 2
table(pptc_subtypes_df$path_subtype)
Group 3 Group 4 SHH WNT
5 1 6 1
pptc_subtypes_df
# 12/13 classified correctly - too few samples for clustering
12/13*100
[1] 92.30769
# How many in MT dataset?
length(mt_annot$Sample)
[1] 97
table(mt_annot$Subgroup)
Group3 Group4 SHH WNT
19 25 50 3
# convert to matrix
pbta_mb_coll_mat <- as.matrix(pbta_mb_coll_df)
pbta_mb_full_mat <- as.matrix(pbta_mb_full_df)
mt_fpkm_mat <- as.matrix(mt_fpkm_df)
Calculate variance for OpenPBTA collapsed data
gene_variance <- matrixStats::rowVars(pbta_coll_log2)
# Find the value that we'll use as a threshold to filter the top 5%
variance_threshold <- quantile(gene_variance, 0.95, na.rm = T)
# Row indices of high variance genes
high_variance_index <- which(gene_variance > variance_threshold)
Set seed for reproducible UMAP results
set.seed(2020)
OpenPBTA stranded RNA-Seq (N = 121) UMAP clustering
Using collapsed matrix
# expects features (genes) to be columns, so we have to use t()
umap_results <- umap::umap(t(pbta_coll_log2[high_variance_index, ]))
# Make a data frame of the layout results and join with molecular subtype
umap_plot_df <- data.frame(umap_results$layout) %>%
tibble::rownames_to_column("Kids_First_Biospecimen_ID") %>%
inner_join(mb_molecular_subtype_df)
Joining, by = "Kids_First_Biospecimen_ID"
Column `Kids_First_Biospecimen_ID` joining character vector and factor, coercing into character vector
# Plot by subtype
umap_plot_df %>%
ggplot(aes(x = X1,
y = X2,
color = molecular_subtype)) +
geom_point(size = 3, alpha = 0.7) +
theme_bw() +
xlab("UMAP1") +
ylab("UMAP2")

# Plot by sequencing center - is this having any effect? (Not really)
umap_plot_df %>%
ggplot(aes(x = X1,
y = X2,
color = seq_center)) +
geom_point(size = 3, alpha = 0.7) +
theme_bw() +
xlab("UMAP1") +
ylab("UMAP2")

Merge MM2S classifier UMAP
Will use this later
pbta_results_mer <- merge(umap_plot_df, pbta_combined_subtypes, by = "Kids_First_Biospecimen_ID")
head(pbta_results_mer)
Calculate variance for OpenPBTA non-collapsed data
Does this give similar results?
gene_variance <- matrixStats::rowVars(pbta_full_log2)
variance_threshold <- quantile(gene_variance, 0.95, na.rm = T)
high_variance_index <- which(gene_variance > variance_threshold)
OpenPBTA stranded RNA-Seq (N = 121) UMAP clustering - full matrix
umap_results <- umap::umap(t(pbta_full_log2[high_variance_index, ]))
umap_plot_df <- data.frame(umap_results$layout) %>%
tibble::rownames_to_column("Kids_First_Biospecimen_ID") %>%
inner_join(mb_molecular_subtype_df)
Joining, by = "Kids_First_Biospecimen_ID"
Column `Kids_First_Biospecimen_ID` joining character vector and factor, coercing into character vector
umap_plot_df %>%
ggplot(aes(x = X1,
y = X2,
color = molecular_subtype)) +
geom_point(size = 3, alpha = 0.7) +
theme_bw() +
xlab("UMAP1") +
ylab("UMAP2")

- Yes, will proceed only with the collapsed matrix.
Exploration
Are the subtypes that don’t classify by unsupervised clustering the same subtypes that did not match pathology subtypes?
pbta_results_mer %>%
ggplot(aes(x = X1,
y = X2,
color = pathology_subtype,
shape = mb_classifier_prediction)) +
geom_point(size = 3, alpha = 0.7) +
theme_bw() +
xlab("UMAP1") +
ylab("UMAP2")

Misclassified samples:
- For the 3 samples within the WNT cluster not classified as WNT, two were WNT by pathology and the other was not noted by pathology.
- For 1 sample classified as WNT that clusterd with SHH, pathology was not noted.
- For the 6 samples within the SHH cluster that were classified as Group 4, 3 were noted as SHH by pathology.
- 1 was SHH by pathology.
- Within this cluster, one sample was classified as SHH but pathology noted as WNT.
- The other 3 samples were not noted by pathology.
- Two samples classified as WNT present in the Group 3/4 cluster were not noted by pathology.
- 6 samples were classified as SHH but are in the Group 3/4 cluster. 2 of these were SHH by pathology, and the rest were not noted. # Examine accuracy for this dataset
# Subset for only samples that had pathology results
subtypes_calc <- subset(pbta_combined_subtypes, !is.na(pathology_subtype))
# convert to character to perform ifelse on factors with different levels
subtypes_calc$pathology_subtype <- as.character(subtypes_calc$pathology_subtype)
subtypes_calc$mb_classifier_prediction <- as.character(subtypes_calc$mb_classifier_prediction)
# Create column to calculate matches
subtypes_calc$mbclass_v_path <- ifelse(subtypes_calc$pathology_subtype == subtypes_calc$mb_classifier_prediction, "true",
ifelse(subtypes_calc$pathology_subtype == "Group 3 or 4" & subtypes_calc$mb_classifier_prediction == "Group3", "true",
ifelse(subtypes_calc$pathology_subtype == "Group 3 or 4" & subtypes_calc$mb_classifier_prediction == "Group4", "true", "false")))
# How many were predicted correctly (true)?
table(subtypes_calc$mbclass_v_path)
false true
6 26
# Percent of classifications that match MB classifier calls
(26/(6+26))*100
[1] 81.25
# Create column to calculate matches for MM2S classifier
subtypes_calc$MM2S_v_path <- ifelse(subtypes_calc$pathology_subtype == subtypes_calc$MM2S_prediction, "true",
ifelse(subtypes_calc$pathology_subtype == "Group 3 or 4" & subtypes_calc$MM2S_prediction == "Group3", "true",
ifelse(subtypes_calc$pathology_subtype == "Group 3 or 4" & subtypes_calc$MM2S_prediction == "Group4", "true", "false")))
table(subtypes_calc$MM2S_v_path)
false true
7 25
# Percent of classifications that match MM2S classifier calls
(25/(7+25))*100
[1] 78.125
plot MM2S predictions
First, combine matrix with subtypes
gene_variance <- matrixStats::rowVars(pbta_coll_log2)
variance_threshold <- quantile(gene_variance, 0.95, na.rm = T)
high_variance_index <- which(gene_variance > variance_threshold)
umap_results <- umap::umap(t(pbta_coll_log2[high_variance_index, ]))
umap_plot_df <- data.frame(umap_results$layout) %>%
tibble::rownames_to_column("Kids_First_Biospecimen_ID") %>%
inner_join(pbta_combined_subtypes)
Joining, by = "Kids_First_Biospecimen_ID"
Column `Kids_First_Biospecimen_ID` joining character vector and factor, coercing into character vector
Does this data look better?
umap_plot_df %>%
ggplot(aes(x = X1,
y = X2,
color = MM2S_prediction,
shape = mb_classifier_prediction)) +
geom_point(size = 3, alpha = 0.7) +
theme_bw() +
xlab("UMAP1") +
ylab("UMAP2")

Calculate variance for Michael Taylor RNA-Seq data
gene_variance <- matrixStats::rowVars(mt_mat_log2)
variance_threshold <- quantile(gene_variance, 0.95, na.rm = T)
high_variance_index <- which(gene_variance > variance_threshold)
Michael Taylor (N = 97) UMAP clustering
umap_results <- umap::umap(t(mt_mat_log2[high_variance_index, ]))
umap_plot_df <- data.frame(umap_results$layout) %>%
tibble::rownames_to_column("Sample") %>%
inner_join(mt_annot)
Joining, by = "Sample"
# Plot
umap_plot_df %>%
ggplot(aes(x = X1,
y = X2,
color = Subgroup)) +
geom_point(size = 3, alpha = 0.7) +
theme_bw() +
xlab("UMAP1") +
ylab("UMAP2")

Observation:
- It looks like there are too few WNT samples (N = 3) to enable unsupervised clustering of those samples in this dataset.
Rerun of OpenPBTA stranded RNA-Seq (N = 121) UMAP clustering
- Turns out the initial FPKM values were not log2 transformed, thus MB subtypes in pbta-histologies.tsv from MB classifier are not accurate.
# Replot UMAP with new subtype labels
gene_variance <- matrixStats::rowVars(pbta_coll_log2)
variance_threshold <- quantile(gene_variance, 0.95, na.rm = T)
high_variance_index <- which(gene_variance > variance_threshold)
umap_results <- umap::umap(t(pbta_coll_log2[high_variance_index, ]))
# Make a data frame of the layout results and join with molecular subtype
umap_plot_df <- data.frame(umap_results$layout) %>%
tibble::rownames_to_column("Kids_First_Biospecimen_ID") %>%
inner_join(pbta_combined_subtypes)
Joining, by = "Kids_First_Biospecimen_ID"
Column `Kids_First_Biospecimen_ID` joining character vector and factor, coercing into character vector
# Plot by subtype
umap_plot_df %>%
ggplot(aes(x = X1,
y = X2,
color = mb_classifier_prediction)) +
geom_point(size = 3, alpha = 0.7) +
theme_bw() +
xlab("UMAP1") +
ylab("UMAP2")

session info
session_info()
─ Session info ───────────────────────────────────────────────────────────────────────────────────────────────────────
─ Packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────
package * version date lib source
askpass 1.1 2019-01-13 [1] CRAN (R 3.6.0)
assertthat 0.2.1 2019-03-21 [1] CRAN (R 3.6.0)
backports 1.1.5 2019-10-02 [1] CRAN (R 3.6.0)
base64enc 0.1-3 2015-07-28 [1] CRAN (R 3.6.0)
callr 3.3.2 2019-09-22 [1] CRAN (R 3.6.0)
cli 1.1.0 2019-03-19 [1] CRAN (R 3.6.0)
colorspace 1.4-1 2019-03-18 [1] CRAN (R 3.6.0)
crayon 1.3.4 2017-09-16 [1] CRAN (R 3.6.0)
desc 1.2.0 2018-05-01 [1] CRAN (R 3.6.0)
devtools * 2.2.1 2019-09-24 [1] CRAN (R 3.6.0)
digest 0.6.22 2019-10-21 [1] CRAN (R 3.6.1)
dplyr * 0.8.3 2019-07-04 [1] CRAN (R 3.6.0)
ellipsis 0.3.0 2019-09-20 [1] CRAN (R 3.6.0)
evaluate 0.14 2019-05-28 [1] CRAN (R 3.6.0)
fs 1.3.1 2019-05-06 [1] CRAN (R 3.6.0)
gdata * 2.18.0 2017-06-06 [1] CRAN (R 3.6.0)
ggplot2 * 3.2.1 2019-08-10 [1] CRAN (R 3.6.0)
glue 1.3.1 2019-03-12 [1] CRAN (R 3.6.0)
gtable 0.3.0 2019-03-25 [1] CRAN (R 3.6.0)
gtools 3.8.1 2018-06-26 [1] CRAN (R 3.6.0)
htmltools 0.4.0 2019-10-04 [1] CRAN (R 3.6.0)
jsonlite 1.6 2018-12-07 [1] CRAN (R 3.6.0)
knitr 1.25 2019-09-18 [1] CRAN (R 3.6.0)
labeling 0.3 2014-08-23 [1] CRAN (R 3.6.0)
lattice 0.20-38 2018-11-04 [1] CRAN (R 3.6.1)
lazyeval 0.2.2 2019-03-15 [1] CRAN (R 3.6.0)
lifecycle 0.2.0 2020-03-06 [1] CRAN (R 3.6.0)
magrittr 1.5 2014-11-22 [1] CRAN (R 3.6.0)
Matrix 1.2-17 2019-03-22 [1] CRAN (R 3.6.1)
matrixStats 0.55.0 2019-09-07 [1] CRAN (R 3.6.0)
memoise 1.1.0 2017-04-21 [1] CRAN (R 3.6.0)
munsell 0.5.0 2018-06-12 [1] CRAN (R 3.6.0)
openssl 1.4.1 2019-07-18 [1] CRAN (R 3.6.0)
pillar 1.4.6 2020-07-10 [1] CRAN (R 3.6.2)
pkgbuild 1.0.6 2019-10-09 [1] CRAN (R 3.6.0)
pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 3.6.0)
pkgload 1.0.2 2018-10-29 [1] CRAN (R 3.6.0)
prettyunits 1.0.2 2015-07-13 [1] CRAN (R 3.6.0)
processx 3.4.1 2019-07-18 [1] CRAN (R 3.6.0)
ps 1.3.0 2018-12-21 [1] CRAN (R 3.6.0)
purrr 0.3.3 2019-10-18 [1] CRAN (R 3.6.0)
R6 2.4.0 2019-02-14 [1] CRAN (R 3.6.0)
Rcpp 1.0.5 2020-07-06 [1] CRAN (R 3.6.2)
remotes 2.1.0 2019-06-24 [1] CRAN (R 3.6.0)
reticulate 1.16 2020-05-27 [1] CRAN (R 3.6.2)
rlang 0.4.7 2020-07-09 [1] CRAN (R 3.6.2)
rmarkdown 1.16 2019-10-01 [1] CRAN (R 3.6.0)
rprojroot 1.3-2 2018-01-03 [1] CRAN (R 3.6.0)
RSpectra 0.16-0 2019-12-01 [1] CRAN (R 3.6.0)
rstudioapi 0.10 2019-03-19 [1] CRAN (R 3.6.0)
scales 1.0.0 2018-08-09 [1] CRAN (R 3.6.0)
sessioninfo 1.1.1 2018-11-05 [1] CRAN (R 3.6.0)
stringi 1.4.3 2019-03-12 [1] CRAN (R 3.6.0)
stringr 1.4.0 2019-02-10 [1] CRAN (R 3.6.0)
testthat 2.2.1 2019-07-25 [1] CRAN (R 3.6.0)
tibble 3.0.3 2020-07-10 [1] CRAN (R 3.6.2)
tidyselect 1.1.0 2020-05-11 [1] CRAN (R 3.6.2)
umap * 0.2.6.0 2020-06-16 [1] CRAN (R 3.6.2)
usethis * 1.5.1 2019-07-04 [1] CRAN (R 3.6.0)
vctrs 0.3.2 2020-07-15 [1] CRAN (R 3.6.2)
withr 2.1.2 2018-03-15 [1] CRAN (R 3.6.0)
xfun 0.10 2019-10-01 [1] CRAN (R 3.6.0)
yaml 2.2.0 2018-07-25 [1] CRAN (R 3.6.0)
[1] /Library/Frameworks/R.framework/Versions/3.6/Resources/library
LS0tCnRpdGxlOiAiRXhwbG9yZSBtZWR1bGxvYmxhc3RvbWEgc3VidHlwZSBjYWxscyBpbiB0aGUgY29udGV4dCBvZiB1bnN1cGVydmlzZWQgY2x1c3RlcmluZyIKb3V0cHV0OiBodG1sX25vdGVib29rICAgIAp0b2M6IFRSVUUKdG9jX2Zsb2F0OiBUUlVFCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQpgYGB7ciBnbG9iYWxfb3B0aW9ucywgaW5jbHVkZT1GfQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3IgPSBGLCBlY2hvID0gVCwgd2FybmluZyA9IFQsIG1lc3NhZ2UgPSBUKQpgYGAKCgojIExvYWQgcGFja2FnZXMKYGBge3J9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkcGx5cikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh1bWFwKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGdncGxvdDIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZGV2dG9vbHMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZ2RhdGEpKQpgYGAKCiMgRGlyZWN0b3JpZXMgYW5kIEZpbGVzCiMjIERpcmVjdG9yaWVzCmBgYHtyfQojIERhdGEgZGlyZWN0b3J5CmRhdGFfZGlyIDwtIGZpbGUucGF0aCgifi9Eb2N1bWVudHMvR2l0SHViL2poYXJlbnphLmdpdGh1Yi5pby9vcGVuUEJUQS1ub3RlYm9va3MvbWItc3VidHlwZXMvZGF0YS8iKQpwYnRhX2RhdGFfZGlyIDwtIGZpbGUucGF0aCgifi9PcGVuUEJUQS1hbmFseXNpcy9kYXRhL3JlbGVhc2UtdjE2LTIwMjAwMzIwLyIpCiMgQ3JlYXRlIGEgcmVzdWx0cyBkaXJlY3RvcnkKcmVzdWx0c19kaXIgPC0gInJlc3VsdHMiCmlmICghZGlyLmV4aXN0cyhyZXN1bHRzX2RpcikpIHsKICBkaXIuY3JlYXRlKHJlc3VsdHNfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQp9CmBgYAoKIyMgSW5wdXQgRmlsZXBhdGhzCmBgYHtyfQpwYnRhX3JuYXNlcV9maWxlIDwtIGZpbGUucGF0aChwYnRhX2RhdGFfZGlyLCAicGJ0YS1nZW5lLWV4cHJlc3Npb24tcnNlbS1mcGttLWNvbGxhcHNlZC5zdHJhbmRlZC5yZHMiKQpwYnRhX3JuYXNlcV9maWxlX2Z1bGwgPC0gZmlsZS5wYXRoKHBidGFfZGF0YV9kaXIsICJwYnRhLWdlbmUtZXhwcmVzc2lvbi1yc2VtLWZwa20uc3RyYW5kZWQucmRzIikKcGJ0YV9oaXN0X2ZpbGUgPC0gZmlsZS5wYXRoKHBidGFfZGF0YV9kaXIsICJwYnRhLWhpc3RvbG9naWVzLnRzdiIpCnBwdGNfYW5ub3RfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJwcHRjX2Fubm90LnRzdiIpCnBwdGNfaGlzdF9maWxlIDwtIGZpbGUucGF0aChkYXRhX2RpciwgInBwdGMtcGR4LWNsaW5pY2FsLXdlYi50eHQiKQpjbGFzc2lmaWVyX3BhdGhfZmlsZSA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJyYXRoaV9yZXN1bHRzLnRzdiIpCmBgYAojIyBSZWFkIGluIGZpbGVzCmBgYHtyfQojIFBCVEEKcGJ0YV9mcGttX2NvbGxhcHNlZF9tYXQgPC0gcmVhZFJEUyhwYnRhX3JuYXNlcV9maWxlKQpwYnRhX2Zwa21fZnVsbF9tYXQgPC0gcmVhZFJEUyhwYnRhX3JuYXNlcV9maWxlX2Z1bGwpCnBidGFfaGlzdG9sb2dpZXNfZGYgPC0gcmVhZC5kZWxpbShwYnRhX2hpc3RfZmlsZSwgc2VwID0gIlx0IiwgaGVhZGVyID0gVCkKIyBQUFRDCmxvYWQoZmlsZS5wYXRoKGRhdGFfZGlyLCAiMjAxOS0wMi0xNC1wcHRjLVNUQVJfY3VmZmxpbmtzX2hnMTlfbWF0cml4XzI0NC5yZGEiKSwgdmVyYm9zZSA9IFQpCnBwdGNfc3VidHlwZXNfZGYgPC0gcmVhZC5kZWxpbShwcHRjX2Fubm90X2ZpbGUsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFQpCiMgUmVuYW1lIFBQVEMgZGYsIHJlbW92ZSBnZW5lIG5hbWUgY29sdW1uLCBzdWJzZXQgTUIgc2FtcGxlcwptdihmcm9tID0gInJuYS5tYXQiLCB0byA9ICJwcHRjX2Zwa21fZGYiKQpwcHRjX2Zwa21fZGYkZ2VuZV9zaG9ydF9uYW1lIDwtIE5VTEwKcHB0Y19tYl9kZiA8LSBwcHRjX2Zwa21fZGYgJT4lCiAgc2VsZWN0KHBwdGNfc3VidHlwZXNfZGYkU2FtcGxlKQoKIyBNaWNoYWVsIFRheWxvciAoTUIgY2xhc3NpZmllciB0cmFpbmluZyBzZXQpLCByZW5hbWUKbG9hZChmaWxlLnBhdGgoZGF0YV9kaXIsICJtdF9ybmFzZXEuUkRhdGEiKSwgdmVyYm9zZSA9IFQpCm12KGZyb20gPSAibXRfbWF0IiwgdG8gPSAibXRfZnBrbV9kZiIpCmhlYWQobXRfZnBrbV9kZikKaGVhZChtdF9hbm5vdCkKCiMgTU0yUyBQQlRBIGNsYXNzaWZpZXIgcmVzdWx0cywgTUIgY2xhc3NpZmllciByZXN1bHRzLCBwYXRob2xvZ3kgZGF0YQojIE1NMlMgUGFwZXI6IGh0dHBzOi8vc2NmYm0uYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwMjktMDE2LTAwNTMteQpwYnRhX2NvbWJpbmVkX3N1YnR5cGVzIDwtIHJlYWQuZGVsaW0oY2xhc3NpZmllcl9wYXRoX2ZpbGUsIHNlcCA9ICJcdCIsIGhlYWRlciA9IFQpCiMgSG93IG1hbnkgb2YgZWFjaCBzdWJ0eXBlIGlkZW50aWZpZWQgYnkgcGF0aG9sb2d5Pwp0YWJsZShwYnRhX2NvbWJpbmVkX3N1YnR5cGVzJHBhdGhvbG9neV9zdWJ0eXBlKQpgYGAKCiMgUEJUQSB3cmFuZ2xpbmcKYGBge3J9CiMgT25seSBwdWxsIG91dCBzYW1wbGUgaWRlbnRpZmllcnMgKEtpZHNGaXJzdCBiaW9zcGVjaW1lbiBpZGVudGlmaWVycykgdGhhdCBjb3JyZXNwb25kIHRvIG1lZHVsbG9ibGFzdG9tYSBzYW1wbGVzCm1lZHVsbG9ibGFzdG9tYV9zYW1wbGVzIDwtIHBidGFfaGlzdG9sb2dpZXNfZGYgJT4lCiAgZmlsdGVyKHNob3J0X2hpc3RvbG9neSA9PSAiTWVkdWxsb2JsYXN0b21hIikgJT4lCiAgZmlsdGVyKGV4cGVyaW1lbnRhbF9zdHJhdGVneSA9PSAiUk5BLVNlcSIpICU+JQogIGZpbHRlcihSTkFfbGlicmFyeSA9PSAic3RyYW5kZWQiKSAlPiUKICBwdWxsKEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpCiMgQ2FsY3VsYXRlIE4KbGVuZ3RoKG1lZHVsbG9ibGFzdG9tYV9zYW1wbGVzKQojIENvbGxlY3Qgc3VidHlwZXMgYW5kIGNvdW50Cm1iX21vbGVjdWxhcl9zdWJ0eXBlX2RmIDwtIHBidGFfaGlzdG9sb2dpZXNfZGYgJT4lCiAgZmlsdGVyKHNob3J0X2hpc3RvbG9neSA9PSAiTWVkdWxsb2JsYXN0b21hIikgJT4lCiAgZmlsdGVyKGV4cGVyaW1lbnRhbF9zdHJhdGVneSA9PSAiUk5BLVNlcSIpICU+JQogIGZpbHRlcihSTkFfbGlicmFyeSA9PSAic3RyYW5kZWQiKSAlPiUKICBzZWxlY3QoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgbW9sZWN1bGFyX3N1YnR5cGUsIHNlcV9jZW50ZXIpCiMgQ2FsY3VsYXRlIE4KbnJvdyhtYl9tb2xlY3VsYXJfc3VidHlwZV9kZikKIyBIb3cgbWFueSBwZXIgc3VidHlwZT8KdGFibGUoZHJvcGxldmVscyhtYl9tb2xlY3VsYXJfc3VidHlwZV9kZiRtb2xlY3VsYXJfc3VidHlwZSkpCmBgYAojIENyZWF0ZSBtYXRyaWNlcyBvZiBvbmx5IE1CIHNhbXBsZXMKYGBge3J9CiMgc2VsZWN0IFBCVEEgTUIgc2FtcGxlcyBvbmx5CnBidGFfbWJfY29sbF9kZiA8LSBwYnRhX2Zwa21fY29sbGFwc2VkX21hdCAlPiUgCiAgc2VsZWN0KG1lZHVsbG9ibGFzdG9tYV9zYW1wbGVzKQpwYnRhX21iX2Z1bGxfZGYgPC0gcGJ0YV9mcGttX2Z1bGxfbWF0ICU+JSAKICBzZWxlY3QobWVkdWxsb2JsYXN0b21hX3NhbXBsZXMpCiMgc2VsZWN0IFBQVEMgTUIgc2FtcGxlcyBvbmx5CnBwdGNfbWJfZGYgPC0gcHB0Y19mcGttX2RmICU+JSAKICBzZWxlY3QocHB0Y19zdWJ0eXBlc19kZiRTYW1wbGUpCmxlbmd0aChwcHRjX3N1YnR5cGVzX2RmJFNhbXBsZSkKIyBIb3cgbWFueSBwZXIgc3VidHlwZT8KdGFibGUocHB0Y19zdWJ0eXBlc19kZiRjbGFzc19zdWJ0eXBlKQp0YWJsZShwcHRjX3N1YnR5cGVzX2RmJHBhdGhfc3VidHlwZSkKcHB0Y19zdWJ0eXBlc19kZgojIDEyLzEzIGNsYXNzaWZpZWQgY29ycmVjdGx5IC0gdG9vIGZldyBzYW1wbGVzIGZvciBjbHVzdGVyaW5nCjEyLzEzKjEwMAoKIyBIb3cgbWFueSBpbiBNVCBkYXRhc2V0PwpsZW5ndGgobXRfYW5ub3QkU2FtcGxlKQp0YWJsZShtdF9hbm5vdCRTdWJncm91cCkKCiMgY29udmVydCB0byBtYXRyaXgKcGJ0YV9tYl9jb2xsX21hdCA8LSBhcy5tYXRyaXgocGJ0YV9tYl9jb2xsX2RmKQpwYnRhX21iX2Z1bGxfbWF0IDwtIGFzLm1hdHJpeChwYnRhX21iX2Z1bGxfZGYpCm10X2Zwa21fbWF0IDwtIGFzLm1hdHJpeChtdF9mcGttX2RmKQpgYGAKIyBUcmFuc2Zvcm0gbWF0cmljZXMKIyMgbG9nMiBtYXRyaXgKYGBge3J9CnBidGFfY29sbF9sb2cyIDwtIGxvZzIocGJ0YV9tYl9jb2xsX21hdCkKcGJ0YV9mdWxsX2xvZzIgPC0gbG9nMihwYnRhX21iX2Z1bGxfbWF0KQptdF9tYXRfbG9nMiA8LSBsb2cyKG10X2Zwa21fbWF0KQpgYGAKIyBDYWxjdWxhdGUgdmFyaWFuY2UgZm9yIE9wZW5QQlRBIGNvbGxhcHNlZCBkYXRhCmBgYHtyfQpnZW5lX3ZhcmlhbmNlIDwtIG1hdHJpeFN0YXRzOjpyb3dWYXJzKHBidGFfY29sbF9sb2cyKQojIEZpbmQgdGhlIHZhbHVlIHRoYXQgd2UnbGwgdXNlIGFzIGEgdGhyZXNob2xkIHRvIGZpbHRlciB0aGUgdG9wIDUlCnZhcmlhbmNlX3RocmVzaG9sZCA8LSBxdWFudGlsZShnZW5lX3ZhcmlhbmNlLCAwLjk1LCBuYS5ybSA9IFQpCiMgUm93IGluZGljZXMgb2YgaGlnaCB2YXJpYW5jZSBnZW5lcwpoaWdoX3ZhcmlhbmNlX2luZGV4IDwtIHdoaWNoKGdlbmVfdmFyaWFuY2UgPiB2YXJpYW5jZV90aHJlc2hvbGQpCmBgYAojIFNldCBzZWVkIGZvciByZXByb2R1Y2libGUgVU1BUCByZXN1bHRzCmBgYHtyfQpzZXQuc2VlZCgyMDIwKQpgYGAKCiMgT3BlblBCVEEgc3RyYW5kZWQgUk5BLVNlcSAoTiA9IDEyMSkgVU1BUCBjbHVzdGVyaW5nCiMjIFVzaW5nIGNvbGxhcHNlZCBtYXRyaXgKYGBge3J9CiMgZXhwZWN0cyBmZWF0dXJlcyAoZ2VuZXMpIHRvIGJlIGNvbHVtbnMsIHNvIHdlIGhhdmUgdG8gdXNlIHQoKQp1bWFwX3Jlc3VsdHMgPC0gdW1hcDo6dW1hcCh0KHBidGFfY29sbF9sb2cyW2hpZ2hfdmFyaWFuY2VfaW5kZXgsIF0pKQojIE1ha2UgYSBkYXRhIGZyYW1lIG9mIHRoZSBsYXlvdXQgcmVzdWx0cyBhbmQgam9pbiB3aXRoIG1vbGVjdWxhciBzdWJ0eXBlIAp1bWFwX3Bsb3RfZGYgPC0gZGF0YS5mcmFtZSh1bWFwX3Jlc3VsdHMkbGF5b3V0KSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIpICU+JQogIGlubmVyX2pvaW4obWJfbW9sZWN1bGFyX3N1YnR5cGVfZGYpCiMgUGxvdCBieSBzdWJ0eXBlCnVtYXBfcGxvdF9kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBYMSwgCiAgICAgICAgICAgICB5ID0gWDIsCiAgICAgICAgICAgICBjb2xvciA9IG1vbGVjdWxhcl9zdWJ0eXBlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC43KSArCiAgdGhlbWVfYncoKSArCiAgeGxhYigiVU1BUDEiKSArCiAgeWxhYigiVU1BUDIiKQpgYGAKYGBge3J9CiMgUGxvdCBieSBzZXF1ZW5jaW5nIGNlbnRlciAtIGlzIHRoaXMgaGF2aW5nIGFueSBlZmZlY3Q/IChOb3QgcmVhbGx5KQp1bWFwX3Bsb3RfZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gWDEsIAogICAgICAgICAgICAgeSA9IFgyLAogICAgICAgICAgICAgY29sb3IgPSBzZXFfY2VudGVyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC43KSArCiAgdGhlbWVfYncoKSArCiAgeGxhYigiVU1BUDEiKSArCiAgeWxhYigiVU1BUDIiKQpgYGAKIyBNZXJnZSBNTTJTIGNsYXNzaWZpZXIgVU1BUAojIyBXaWxsIHVzZSB0aGlzIGxhdGVyCmBgYHtyfQpwYnRhX3Jlc3VsdHNfbWVyIDwtIG1lcmdlKHVtYXBfcGxvdF9kZiwgcGJ0YV9jb21iaW5lZF9zdWJ0eXBlcywgYnkgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIpCmhlYWQocGJ0YV9yZXN1bHRzX21lcikKYGBgCiMgQ2FsY3VsYXRlIHZhcmlhbmNlIGZvciBPcGVuUEJUQSBub24tY29sbGFwc2VkIGRhdGEKIyMgRG9lcyB0aGlzIGdpdmUgc2ltaWxhciByZXN1bHRzPwoKYGBge3J9CmdlbmVfdmFyaWFuY2UgPC0gbWF0cml4U3RhdHM6OnJvd1ZhcnMocGJ0YV9mdWxsX2xvZzIpCnZhcmlhbmNlX3RocmVzaG9sZCA8LSBxdWFudGlsZShnZW5lX3ZhcmlhbmNlLCAwLjk1LCBuYS5ybSA9IFQpCmhpZ2hfdmFyaWFuY2VfaW5kZXggPC0gd2hpY2goZ2VuZV92YXJpYW5jZSA+IHZhcmlhbmNlX3RocmVzaG9sZCkKYGBgCiMgT3BlblBCVEEgc3RyYW5kZWQgUk5BLVNlcSAoTiA9IDEyMSkgVU1BUCBjbHVzdGVyaW5nIC0gZnVsbCBtYXRyaXgKCmBgYHtyfQp1bWFwX3Jlc3VsdHMgPC0gdW1hcDo6dW1hcCh0KHBidGFfZnVsbF9sb2cyW2hpZ2hfdmFyaWFuY2VfaW5kZXgsIF0pKQp1bWFwX3Bsb3RfZGYgPC0gZGF0YS5mcmFtZSh1bWFwX3Jlc3VsdHMkbGF5b3V0KSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIpICU+JQogIGlubmVyX2pvaW4obWJfbW9sZWN1bGFyX3N1YnR5cGVfZGYpCnVtYXBfcGxvdF9kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBYMSwgCiAgICAgICAgICAgICB5ID0gWDIsCiAgICAgICAgICAgICBjb2xvciA9IG1vbGVjdWxhcl9zdWJ0eXBlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC43KSArCiAgdGhlbWVfYncoKSArCiAgeGxhYigiVU1BUDEiKSArCiAgeWxhYigiVU1BUDIiKQpgYGAKLSBZZXMsIHdpbGwgcHJvY2VlZCBvbmx5IHdpdGggdGhlIGNvbGxhcHNlZCBtYXRyaXguCgojIEV4cGxvcmF0aW9uCiMjIEFyZSB0aGUgc3VidHlwZXMgdGhhdCBkb24ndCBjbGFzc2lmeSBieSB1bnN1cGVydmlzZWQgY2x1c3RlcmluZyB0aGUgc2FtZSBzdWJ0eXBlcyB0aGF0IGRpZCBub3QgbWF0Y2ggcGF0aG9sb2d5IHN1YnR5cGVzPwpgYGB7cn0KcGJ0YV9yZXN1bHRzX21lciAlPiUKICBnZ3Bsb3QoYWVzKHggPSBYMSwgCiAgICAgICAgICAgICB5ID0gWDIsCiAgICAgICAgICAgICBjb2xvciA9IHBhdGhvbG9neV9zdWJ0eXBlLAogICAgICAgICAgICAgc2hhcGUgPSBtYl9jbGFzc2lmaWVyX3ByZWRpY3Rpb24pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjcpICsKICB0aGVtZV9idygpICsKICB4bGFiKCJVTUFQMSIpICsKICB5bGFiKCJVTUFQMiIpCmBgYAojIyBNaXNjbGFzc2lmaWVkIHNhbXBsZXM6Ci0gRm9yIHRoZSAzIHNhbXBsZXMgd2l0aGluIHRoZSBXTlQgY2x1c3RlciBub3QgY2xhc3NpZmllZCBhcyBXTlQsIHR3byB3ZXJlIFdOVCBieSBwYXRob2xvZ3kgYW5kIHRoZSBvdGhlciB3YXMgbm90IG5vdGVkIGJ5IHBhdGhvbG9neS4gCi0gRm9yIDEgc2FtcGxlIGNsYXNzaWZpZWQgYXMgV05UIHRoYXQgY2x1c3RlcmQgd2l0aCBTSEgsIHBhdGhvbG9neSB3YXMgbm90IG5vdGVkLiAKLSBGb3IgdGhlIDYgc2FtcGxlcyB3aXRoaW4gdGhlIFNISCBjbHVzdGVyIHRoYXQgd2VyZSBjbGFzc2lmaWVkIGFzIEdyb3VwIDQsIDMgd2VyZSBub3RlZCBhcyBTSEggYnkgcGF0aG9sb2d5LiAKLSAxIHdhcyBTSEggYnkgcGF0aG9sb2d5LiAKLSBXaXRoaW4gdGhpcyBjbHVzdGVyLCBvbmUgc2FtcGxlIHdhcyBjbGFzc2lmaWVkIGFzIFNISCBidXQgcGF0aG9sb2d5IG5vdGVkIGFzIFdOVC4gCi0gVGhlIG90aGVyIDMgc2FtcGxlcyB3ZXJlIG5vdCBub3RlZCBieSBwYXRob2xvZ3kuIAotIFR3byBzYW1wbGVzIGNsYXNzaWZpZWQgYXMgV05UIHByZXNlbnQgaW4gdGhlIEdyb3VwIDMvNCBjbHVzdGVyIHdlcmUgbm90IG5vdGVkIGJ5IHBhdGhvbG9neS4gCi0gNiBzYW1wbGVzIHdlcmUgY2xhc3NpZmllZCBhcyBTSEggYnV0IGFyZSBpbiB0aGUgR3JvdXAgMy80IGNsdXN0ZXIuIDIgb2YgdGhlc2Ugd2VyZSBTSEggYnkgcGF0aG9sb2d5LCBhbmQgdGhlIHJlc3Qgd2VyZSBub3Qgbm90ZWQuCiMgRXhhbWluZSBhY2N1cmFjeSBmb3IgdGhpcyBkYXRhc2V0CmBgYHtyfQojIFN1YnNldCBmb3Igb25seSBzYW1wbGVzIHRoYXQgaGFkIHBhdGhvbG9neSByZXN1bHRzCnN1YnR5cGVzX2NhbGMgPC0gc3Vic2V0KHBidGFfY29tYmluZWRfc3VidHlwZXMsICFpcy5uYShwYXRob2xvZ3lfc3VidHlwZSkpIAojIGNvbnZlcnQgdG8gY2hhcmFjdGVyIHRvIHBlcmZvcm0gaWZlbHNlIG9uIGZhY3RvcnMgd2l0aCBkaWZmZXJlbnQgbGV2ZWxzCnN1YnR5cGVzX2NhbGMkcGF0aG9sb2d5X3N1YnR5cGUgPC0gYXMuY2hhcmFjdGVyKHN1YnR5cGVzX2NhbGMkcGF0aG9sb2d5X3N1YnR5cGUpCnN1YnR5cGVzX2NhbGMkbWJfY2xhc3NpZmllcl9wcmVkaWN0aW9uIDwtIGFzLmNoYXJhY3RlcihzdWJ0eXBlc19jYWxjJG1iX2NsYXNzaWZpZXJfcHJlZGljdGlvbikKIyBDcmVhdGUgY29sdW1uIHRvIGNhbGN1bGF0ZSBtYXRjaGVzCnN1YnR5cGVzX2NhbGMkbWJjbGFzc192X3BhdGggPC0gaWZlbHNlKHN1YnR5cGVzX2NhbGMkcGF0aG9sb2d5X3N1YnR5cGUgPT0gc3VidHlwZXNfY2FsYyRtYl9jbGFzc2lmaWVyX3ByZWRpY3Rpb24sICJ0cnVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzdWJ0eXBlc19jYWxjJHBhdGhvbG9neV9zdWJ0eXBlID09ICJHcm91cCAzIG9yIDQiICYgc3VidHlwZXNfY2FsYyRtYl9jbGFzc2lmaWVyX3ByZWRpY3Rpb24gPT0gIkdyb3VwMyIsICJ0cnVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc3VidHlwZXNfY2FsYyRwYXRob2xvZ3lfc3VidHlwZSA9PSAiR3JvdXAgMyBvciA0IiAmIHN1YnR5cGVzX2NhbGMkbWJfY2xhc3NpZmllcl9wcmVkaWN0aW9uID09ICJHcm91cDQiLCAidHJ1ZSIsICJmYWxzZSIpKSkKIyBIb3cgbWFueSB3ZXJlIHByZWRpY3RlZCBjb3JyZWN0bHkgKHRydWUpPwp0YWJsZShzdWJ0eXBlc19jYWxjJG1iY2xhc3Nfdl9wYXRoKQojIFBlcmNlbnQgb2YgY2xhc3NpZmljYXRpb25zIHRoYXQgbWF0Y2ggTUIgY2xhc3NpZmllciBjYWxscwooMjYvKDYrMjYpKSoxMDAKIyBDcmVhdGUgY29sdW1uIHRvIGNhbGN1bGF0ZSBtYXRjaGVzIGZvciBNTTJTIGNsYXNzaWZpZXIKc3VidHlwZXNfY2FsYyRNTTJTX3ZfcGF0aCA8LSBpZmVsc2Uoc3VidHlwZXNfY2FsYyRwYXRob2xvZ3lfc3VidHlwZSA9PSBzdWJ0eXBlc19jYWxjJE1NMlNfcHJlZGljdGlvbiwgInRydWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN1YnR5cGVzX2NhbGMkcGF0aG9sb2d5X3N1YnR5cGUgPT0gIkdyb3VwIDMgb3IgNCIgJiBzdWJ0eXBlc19jYWxjJE1NMlNfcHJlZGljdGlvbiA9PSAiR3JvdXAzIiwgInRydWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzdWJ0eXBlc19jYWxjJHBhdGhvbG9neV9zdWJ0eXBlID09ICJHcm91cCAzIG9yIDQiICYgc3VidHlwZXNfY2FsYyRNTTJTX3ByZWRpY3Rpb24gPT0gIkdyb3VwNCIsICJ0cnVlIiwgImZhbHNlIikpKQp0YWJsZShzdWJ0eXBlc19jYWxjJE1NMlNfdl9wYXRoKQojIFBlcmNlbnQgb2YgY2xhc3NpZmljYXRpb25zIHRoYXQgbWF0Y2ggTU0yUyBjbGFzc2lmaWVyIGNhbGxzCigyNS8oNysyNSkpKjEwMApgYGAKCiMgcGxvdCBNTTJTIHByZWRpY3Rpb25zCiMjIEZpcnN0LCBjb21iaW5lIG1hdHJpeCB3aXRoIHN1YnR5cGVzCgpgYGB7cn0KZ2VuZV92YXJpYW5jZSA8LSBtYXRyaXhTdGF0czo6cm93VmFycyhwYnRhX2NvbGxfbG9nMikKdmFyaWFuY2VfdGhyZXNob2xkIDwtIHF1YW50aWxlKGdlbmVfdmFyaWFuY2UsIDAuOTUsIG5hLnJtID0gVCkKaGlnaF92YXJpYW5jZV9pbmRleCA8LSB3aGljaChnZW5lX3ZhcmlhbmNlID4gdmFyaWFuY2VfdGhyZXNob2xkKQp1bWFwX3Jlc3VsdHMgPC0gdW1hcDo6dW1hcCh0KHBidGFfY29sbF9sb2cyW2hpZ2hfdmFyaWFuY2VfaW5kZXgsIF0pKQp1bWFwX3Bsb3RfZGYgPC0gZGF0YS5mcmFtZSh1bWFwX3Jlc3VsdHMkbGF5b3V0KSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIpICU+JQogIGlubmVyX2pvaW4ocGJ0YV9jb21iaW5lZF9zdWJ0eXBlcykKYGBgICAKIyMgRG9lcyB0aGlzIGRhdGEgbG9vayBiZXR0ZXI/CmBgYHtyfQp1bWFwX3Bsb3RfZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gWDEsIAogICAgICAgICAgICAgeSA9IFgyLAogICAgICAgICAgICAgY29sb3IgPSBNTTJTX3ByZWRpY3Rpb24sCiAgICAgICAgICAgICBzaGFwZSA9IG1iX2NsYXNzaWZpZXJfcHJlZGljdGlvbikpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNykgKwogIHRoZW1lX2J3KCkgKwogIHhsYWIoIlVNQVAxIikgKwogIHlsYWIoIlVNQVAyIikKYGBgCgoKIyBDYWxjdWxhdGUgdmFyaWFuY2UgZm9yIE1pY2hhZWwgVGF5bG9yIFJOQS1TZXEgZGF0YQpgYGB7cn0KZ2VuZV92YXJpYW5jZSA8LSBtYXRyaXhTdGF0czo6cm93VmFycyhtdF9tYXRfbG9nMikKdmFyaWFuY2VfdGhyZXNob2xkIDwtIHF1YW50aWxlKGdlbmVfdmFyaWFuY2UsIDAuOTUsIG5hLnJtID0gVCkKaGlnaF92YXJpYW5jZV9pbmRleCA8LSB3aGljaChnZW5lX3ZhcmlhbmNlID4gdmFyaWFuY2VfdGhyZXNob2xkKQpgYGAKCiMgTWljaGFlbCBUYXlsb3IgKE4gPSA5NykgVU1BUCBjbHVzdGVyaW5nIApgYGB7cn0KdW1hcF9yZXN1bHRzIDwtIHVtYXA6OnVtYXAodChtdF9tYXRfbG9nMltoaWdoX3ZhcmlhbmNlX2luZGV4LCBdKSkKdW1hcF9wbG90X2RmIDwtIGRhdGEuZnJhbWUodW1hcF9yZXN1bHRzJGxheW91dCkgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oIlNhbXBsZSIpICU+JQogIGlubmVyX2pvaW4obXRfYW5ub3QpCiMgUGxvdAp1bWFwX3Bsb3RfZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gWDEsIAogICAgICAgICAgICAgeSA9IFgyLAogICAgICAgICAgICAgY29sb3IgPSBTdWJncm91cCkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBhbHBoYSA9IDAuNykgKwogIHRoZW1lX2J3KCkgKwogIHhsYWIoIlVNQVAxIikgKwogIHlsYWIoIlVNQVAyIikKYGBgCiMjIE9ic2VydmF0aW9uOgotIEl0IGxvb2tzIGxpa2UgdGhlcmUgYXJlIHRvbyBmZXcgV05UIHNhbXBsZXMgKE4gPSAzKSB0byBlbmFibGUgdW5zdXBlcnZpc2VkIGNsdXN0ZXJpbmcgb2YgdGhvc2Ugc2FtcGxlcyBpbiB0aGlzIGRhdGFzZXQuCgojIFJlcnVuIG9mIE9wZW5QQlRBIHN0cmFuZGVkIFJOQS1TZXEgKE4gPSAxMjEpIFVNQVAgY2x1c3RlcmluZwotIFR1cm5zIG91dCB0aGUgaW5pdGlhbCBGUEtNIHZhbHVlcyB3ZXJlIG5vdCBsb2cyIHRyYW5zZm9ybWVkLCB0aHVzIE1CIHN1YnR5cGVzIGluIHBidGEtaGlzdG9sb2dpZXMudHN2IGZyb20gTUIgY2xhc3NpZmllciBhcmUgbm90IGFjY3VyYXRlLgpgYGB7cn0KIyBSZXBsb3QgVU1BUCB3aXRoIG5ldyBzdWJ0eXBlIGxhYmVscwpnZW5lX3ZhcmlhbmNlIDwtIG1hdHJpeFN0YXRzOjpyb3dWYXJzKHBidGFfY29sbF9sb2cyKQp2YXJpYW5jZV90aHJlc2hvbGQgPC0gcXVhbnRpbGUoZ2VuZV92YXJpYW5jZSwgMC45NSwgbmEucm0gPSBUKQpoaWdoX3ZhcmlhbmNlX2luZGV4IDwtIHdoaWNoKGdlbmVfdmFyaWFuY2UgPiB2YXJpYW5jZV90aHJlc2hvbGQpCnVtYXBfcmVzdWx0cyA8LSB1bWFwOjp1bWFwKHQocGJ0YV9jb2xsX2xvZzJbaGlnaF92YXJpYW5jZV9pbmRleCwgXSkpCiMgTWFrZSBhIGRhdGEgZnJhbWUgb2YgdGhlIGxheW91dCByZXN1bHRzIGFuZCBqb2luIHdpdGggbW9sZWN1bGFyIHN1YnR5cGUgCnVtYXBfcGxvdF9kZiA8LSBkYXRhLmZyYW1lKHVtYXBfcmVzdWx0cyRsYXlvdXQpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIikgJT4lCiAgaW5uZXJfam9pbihwYnRhX2NvbWJpbmVkX3N1YnR5cGVzKQojIFBsb3QgYnkgc3VidHlwZQp1bWFwX3Bsb3RfZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gWDEsIAogICAgICAgICAgICAgeSA9IFgyLAogICAgICAgICAgICAgY29sb3IgPSBtYl9jbGFzc2lmaWVyX3ByZWRpY3Rpb24pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjcpICsKICB0aGVtZV9idygpICsKICB4bGFiKCJVTUFQMSIpICsKICB5bGFiKCJVTUFQMiIpCmBgYAotICBUaGlzIGxvb2tzIEEgTE9UIGJldHRlciEKCiMgc2Vzc2lvbiBpbmZvCmBgYHtyfQpzZXNzaW9uX2luZm8oKQpgYGAK