In our analysis of the SLE WB compendium, we were able to do evaluations structured around cell types because we had neutrophil counts in one of the datasets under consideration. For NARES, we do not have any information about the cell type composition of the samples (e.g., histological data, which can be semi-quantitative in nature).
MCPcounter (Becht, et al. Genome Biology. 2016.) is a method for estimating cell type abundance in solid tissues. The original paper also explicitly tested the approach in non-cancerous tissues. (Many methods for immune infiltrate estimation are developed in the context of the tumor microenvironment. Note that in the PLIER preprint, PLIER compared favorably to another method, CIBERSORT (Newman, et al. Nature Methods. 2015.), when using CyTOF measurements as a gold standard.)
In our analysis of the NARES PLIER model, we noted that the neutrophil and ECM signals appeared to be among the strongest in the NARES gene expression data. In this notebook, we’re interested if PLIER neutrophil-associated LVs, both from the NARES and recount2 models, are well-correlated with the neutrophil estimates from MCPcounter.
PLIER has appealing features over MCPcounter: it is explicitly designed to capture biological signal outside cell types (e.g., canonical pathways) and, because it doesn’t only learn geneset-associated LVs, can model technical variance. If we get similar estimates with the two methods (particularly with the multi-PLIER approach), it supports the notion that a single method/model can be useful for a broad set of biological questions & contexts.
Install MCPcounter
This is not currently in the Docker image we’re using for this project.
devtools::install_github("ebecht/MCPcounter",
ref = "a79614eee002c88c64725d69140c7653e7c379b4",
subdir = "Source",
dependencies = TRUE)
Downloading GitHub repo ebecht/MCPcounter@a79614eee002c88c64725d69140c7653e7c379b4
from URL https://api.github.com/repos/ebecht/MCPcounter/zipball/a79614eee002c88c64725d69140c7653e7c379b4
Installing MCPcounter
'/usr/local/lib/R/bin/R' --no-site-file --no-environ --no-save --no-restore \
--quiet CMD INSTALL \
'/tmp/RtmpodPVj8/devtoolsaac2090ee08/ebecht-MCPcounter-a79614e/Source' \
--library='/usr/local/lib/R/site-library' --install-tests
Functions and directory setup
`%>%` <- dplyr::`%>%`
# plot and result directory setup for this notebook
plot.dir <- file.path("plots", "14")
dir.create(plot.dir, recursive = TRUE, showWarnings = FALSE)
results.dir <- file.path("results", "14")
dir.create(results.dir, recursive = TRUE, showWarnings = FALSE)
Read in data
NARES expression
# get NARES expression matrix
exprs.file <- file.path("data", "expression_data",
"NARES_SCANfast_ComBat_with_GeneSymbol.pcl")
exprs.df <- data.table::fread(exprs.file, data.table = FALSE)
exprs.mat <- dplyr::select(exprs.df, -(EntrezID:GeneSymbol))
rownames(exprs.mat) <- exprs.df$GeneSymbol
rm(exprs.df)
NARES PLIER model
nares.plier.file <- file.path("results", "12", "NARES_PLIER_model.RDS")
nares.plier <- readRDS(nares.plier.file)
nares.b <- nares.plier$B
recount2 NARES B
recount.b.file <- file.path("results", "13", "NARES_recount2_B.RDS")
recount.b <- readRDS(file = recount.b.file)
Run MCPcounter
mcp.results <-
MCPcounter::MCPcounter.estimate(exprs.mat, featuresType = "HUGO_symbols")
mcp.melt <- reshape2::melt(mcp.results, varnames = c("Cell_type", "Sample"),
value.name = "MCP_estimate")
readr::write_tsv(mcp.melt,
file.path(results.dir,
"NARES_ComBat_MCPCounter_results_tidy.tsv"))
Compare to PLIER LVs
NARES LV3
was the neutrophil-associated LV in the NARES PLIER model; its best match is recount LV603
on the basis of the Z
matrices (13-compare_NARES_B
). recount LV603
also appears to be pretty neutrophil-specific (not significantly associated with other myeloid cell types; 07-sle_cell_type_recount2_model
). These are the LVs we’ll compare to the MCPcounter estimates.
Data wrangling
# tidy neutrophil-associated LVs
neutro.lv.df <- as.data.frame(nares.b["3,IRIS_Neutrophil-Resting", ])
neutro.lv.df <- tibble::rownames_to_column(neutro.lv.df)
colnames(neutro.lv.df) <- c("Sample", "NARES_LV3")
recount.lv.df <- as.data.frame(recount.b["603,SVM Neutrophils", ])
recount.lv.df <- tibble::rownames_to_column(recount.lv.df)
colnames(recount.lv.df) <- c("Sample", "recount_LV603")
neutro.lv.df <- dplyr::inner_join(neutro.lv.df, recount.lv.df,
by = "Sample")
head(neutro.lv.df)
# join with MCPcounter neutrophil estimates
neutro.df <- dplyr::filter(mcp.melt, Cell_type == "Neutrophils") %>%
dplyr::inner_join(y = neutro.lv.df, by = "Sample")
Column `Sample` joining factor and character vector, coercing into character vector
neutro.file <- file.path(results.dir, "NARES_neutrophil_LV_mcp_all.tsv")
readr::write_tsv(neutro.df, path = neutro.file)
Plotting
summary(lm(neutro.df$MCP_estimate ~ neutro.df$NARES_LV3))
Call:
lm(formula = neutro.df$MCP_estimate ~ neutro.df$NARES_LV3)
Residuals:
Min 1Q Median 3Q Max
-0.11900 -0.02890 -0.00306 0.03302 0.13345
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.903203 0.005758 156.87 <2e-16 ***
neutro.df$NARES_LV3 0.307055 0.004794 64.05 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.0502 on 74 degrees of freedom
Multiple R-squared: 0.9823, Adjusted R-squared: 0.982
F-statistic: 4103 on 1 and 74 DF, p-value: < 2.2e-16
nares.p <- neutro.df %>%
ggplot2::ggplot(ggplot2::aes(x = NARES_LV3, y = MCP_estimate)) +
ggplot2::geom_point(alpha = 0.7) +
ggplot2::geom_smooth(method = "lm") +
ggplot2::theme_bw() +
ggplot2::labs(title = "NARES PLIER model",
y = "MCPcounter Neutrophil Estimate",
x = "NARES LV3") +
ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, face = "bold"),
text = ggplot2::element_text(size = 15)) +
ggplot2::annotate(geom = "text", x = -0.1, y = 2.25,
label = "r-squared = 0.98", size = 5)
nares.p

summary(lm(neutro.df$MCP_estimate ~ neutro.df$recount_LV603))
Call:
lm(formula = neutro.df$MCP_estimate ~ neutro.df$recount_LV603)
Residuals:
Min 1Q Median 3Q Max
-0.26979 -0.08089 0.00521 0.07853 0.32115
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.90320 0.01348 66.98 <2e-16 ***
neutro.df$recount_LV603 1.57195 0.05995 26.22 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.1176 on 74 degrees of freedom
Multiple R-squared: 0.9028, Adjusted R-squared: 0.9015
F-statistic: 687.5 on 1 and 74 DF, p-value: < 2.2e-16
recount.p <- neutro.df %>%
ggplot2::ggplot(ggplot2::aes(x = recount_LV603, y = MCP_estimate)) +
ggplot2::geom_point(alpha = 0.7) +
ggplot2::geom_smooth(method = "lm") +
ggplot2::theme_bw() +
ggplot2::labs(title = "recount2 PLIER model",
y = "MCPcounter Neutrophil Estimate",
x = "recount LV603") +
ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, face = "bold"),
text = ggplot2::element_text(size = 15)) +
ggplot2::annotate(geom = "text", x = -0.1, y = 2.5,
label = "r-squared = 0.90", size = 5)
recount.p

pdf(file.path(plot.dir, "NARES_MCPcounter_model_comparison.pdf"), width = 14,
height = 7)
gridExtra::grid.arrange(nares.p, recount.p, ncol = 2)
dev.off()
null device
1
LS0tCnRpdGxlOiAiTkFSRVMgTUNQY291bnRlciIKb3V0cHV0OiAgIAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpJbiBvdXIgYW5hbHlzaXMgb2YgdGhlIFNMRSBXQiBjb21wZW5kaXVtLCB3ZSB3ZXJlIGFibGUgdG8gZG8gZXZhbHVhdGlvbnMgCnN0cnVjdHVyZWQgYXJvdW5kIGNlbGwgdHlwZXMgYmVjYXVzZSB3ZSBoYWQgbmV1dHJvcGhpbCBjb3VudHMgaW4gb25lIG9mIHRoZSAKZGF0YXNldHMgdW5kZXIgY29uc2lkZXJhdGlvbi4KRm9yIE5BUkVTLCB3ZSBkbyBub3QgaGF2ZSBhbnkgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGNlbGwgdHlwZSBjb21wb3NpdGlvbiBvZgp0aGUgc2FtcGxlcyAoZS5nLiwgaGlzdG9sb2dpY2FsIGRhdGEsIHdoaWNoIGNhbiBiZSBzZW1pLXF1YW50aXRhdGl2ZSBpbiBuYXR1cmUpLgoKW01DUGNvdW50ZXJdKGh0dHBzOi8vZ2l0aHViLmNvbS9lYmVjaHQvTUNQY291bnRlci90cmVlL2E3OTYxNGVlZTAwMmM4OGM2NDcyNWQ2OTE0MGM3NjUzZTdjMzc5YjQpIAooW0JlY2h0LCBldCBhbC4gX0dlbm9tZSBCaW9sb2d5Xy4gMjAxNi5dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMzA1OS0wMTYtMTA3MC01KSkKaXMgYSBtZXRob2QgZm9yIGVzdGltYXRpbmcgY2VsbCB0eXBlIGFidW5kYW5jZSBpbiBzb2xpZCB0aXNzdWVzLgpUaGUgb3JpZ2luYWwgcGFwZXIgYWxzbyBleHBsaWNpdGx5IHRlc3RlZCB0aGUgYXBwcm9hY2ggaW4gbm9uLWNhbmNlcm91cyAKdGlzc3Vlcy4KKE1hbnkgbWV0aG9kcyBmb3IgaW1tdW5lIGluZmlsdHJhdGUgZXN0aW1hdGlvbiBhcmUgZGV2ZWxvcGVkIGluIHRoZSBjb250ZXh0Cm9mIHRoZSB0dW1vciBtaWNyb2Vudmlyb25tZW50LiBOb3RlIHRoYXQgaW4gdGhlIApbUExJRVIgcHJlcHJpbnRdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTAxLzExNjA2MSksIFBMSUVSIGNvbXBhcmVkIGZhdm9yYWJseSB0byAKYW5vdGhlciBtZXRob2QsIFtDSUJFUlNPUlRdKGh0dHBzOi8vY2liZXJzb3J0LnN0YW5mb3JkLmVkdSkgCihbTmV3bWFuLCBldCBhbC4gX05hdHVyZSBNZXRob2RzLl8gMjAxNS5dKGh0dHBzOi8vZG9pLm9yZzEwLjEwMzgvbm1ldGguMzMzNykpLCAKd2hlbiB1c2luZyBDeVRPRiBtZWFzdXJlbWVudHMgYXMgYSBnb2xkIHN0YW5kYXJkLikgCgpJbiBvdXIgYW5hbHlzaXMgb2YgdGhlIE5BUkVTIFBMSUVSIG1vZGVsLCB3ZSBub3RlZCB0aGF0IHRoZSBuZXV0cm9waGlsIGFuZApFQ00gc2lnbmFscyBhcHBlYXJlZCB0byBiZSBhbW9uZyB0aGUgc3Ryb25nZXN0IGluIHRoZSBOQVJFUyBnZW5lIGV4cHJlc3Npb24gCmRhdGEuIEluIHRoaXMgbm90ZWJvb2ssIHdlJ3JlIGludGVyZXN0ZWQgaWYgUExJRVIgbmV1dHJvcGhpbC1hc3NvY2lhdGVkIExWcywgCmJvdGggZnJvbSB0aGUgTkFSRVMgYW5kIHJlY291bnQyIG1vZGVscywgYXJlIHdlbGwtY29ycmVsYXRlZCB3aXRoIHRoZSBuZXV0cm9waGlsIAplc3RpbWF0ZXMgZnJvbSBNQ1Bjb3VudGVyLgoKUExJRVIgaGFzIGFwcGVhbGluZyBmZWF0dXJlcyBvdmVyIE1DUGNvdW50ZXI6IGl0IGlzIGV4cGxpY2l0bHkgZGVzaWduZWQgdG8gCmNhcHR1cmUgYmlvbG9naWNhbCBzaWduYWwgX291dHNpZGVfIGNlbGwgdHlwZXMgKGUuZy4sIGNhbm9uaWNhbCBwYXRod2F5cykgYW5kLCAKYmVjYXVzZSBpdCBkb2Vzbid0IG9ubHkgbGVhcm4gZ2VuZXNldC1hc3NvY2lhdGVkIExWcywgY2FuIG1vZGVsIHRlY2huaWNhbCAKdmFyaWFuY2UuIElmIHdlIGdldCBzaW1pbGFyIGVzdGltYXRlcyB3aXRoIHRoZSB0d28gbWV0aG9kcyAocGFydGljdWxhcmx5IHdpdGgKdGhlIG11bHRpLVBMSUVSIGFwcHJvYWNoKSwgaXQgc3VwcG9ydHMgdGhlIG5vdGlvbiB0aGF0IGEgc2luZ2xlIG1ldGhvZC9tb2RlbCBjYW4KYmUgdXNlZnVsIGZvciBhIGJyb2FkIHNldCBvZiBiaW9sb2dpY2FsIHF1ZXN0aW9ucyAmIGNvbnRleHRzLgoKIyMgSW5zdGFsbCBNQ1Bjb3VudGVyCgpUaGlzIGlzIG5vdCBjdXJyZW50bHkgaW4gdGhlIERvY2tlciBpbWFnZSB3ZSdyZSB1c2luZyBmb3IgdGhpcyBwcm9qZWN0LgoKYGBge3J9CmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZWJlY2h0L01DUGNvdW50ZXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHJlZiA9ICJhNzk2MTRlZWUwMDJjODhjNjQ3MjVkNjkxNDBjNzY1M2U3YzM3OWI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIHN1YmRpciA9ICJTb3VyY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgZGVwZW5kZW5jaWVzID0gVFJVRSkKYGBgCgojIyBGdW5jdGlvbnMgYW5kIGRpcmVjdG9yeSBzZXR1cAoKYGBge3J9CmAlPiVgIDwtIGRwbHlyOjpgJT4lYApgYGAKCmBgYHtyfQojIHBsb3QgYW5kIHJlc3VsdCBkaXJlY3Rvcnkgc2V0dXAgZm9yIHRoaXMgbm90ZWJvb2sKcGxvdC5kaXIgPC0gZmlsZS5wYXRoKCJwbG90cyIsICIxNCIpCmRpci5jcmVhdGUocGxvdC5kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpyZXN1bHRzLmRpciA8LSBmaWxlLnBhdGgoInJlc3VsdHMiLCAiMTQiKQpkaXIuY3JlYXRlKHJlc3VsdHMuZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKYGBgCgojIyBSZWFkIGluIGRhdGEKCiMjIyBOQVJFUyBleHByZXNzaW9uCgpgYGB7cn0KIyBnZXQgTkFSRVMgZXhwcmVzc2lvbiBtYXRyaXgKZXhwcnMuZmlsZSA8LSBmaWxlLnBhdGgoImRhdGEiLCAiZXhwcmVzc2lvbl9kYXRhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJOQVJFU19TQ0FOZmFzdF9Db21CYXRfd2l0aF9HZW5lU3ltYm9sLnBjbCIpCmV4cHJzLmRmIDwtIGRhdGEudGFibGU6OmZyZWFkKGV4cHJzLmZpbGUsIGRhdGEudGFibGUgPSBGQUxTRSkKZXhwcnMubWF0IDwtIGRwbHlyOjpzZWxlY3QoZXhwcnMuZGYsIC0oRW50cmV6SUQ6R2VuZVN5bWJvbCkpCnJvd25hbWVzKGV4cHJzLm1hdCkgPC0gZXhwcnMuZGYkR2VuZVN5bWJvbApybShleHBycy5kZikKYGBgCgojIyMgTkFSRVMgUExJRVIgbW9kZWwKCmBgYHtyfQpuYXJlcy5wbGllci5maWxlIDwtIGZpbGUucGF0aCgicmVzdWx0cyIsICIxMiIsICJOQVJFU19QTElFUl9tb2RlbC5SRFMiKQpuYXJlcy5wbGllciA8LSByZWFkUkRTKG5hcmVzLnBsaWVyLmZpbGUpCm5hcmVzLmIgPC0gbmFyZXMucGxpZXIkQgpgYGAKCiMjIyByZWNvdW50MiBOQVJFUyBCCgpgYGB7cn0KcmVjb3VudC5iLmZpbGUgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIjEzIiwgIk5BUkVTX3JlY291bnQyX0IuUkRTIikKcmVjb3VudC5iIDwtIHJlYWRSRFMoZmlsZSA9IHJlY291bnQuYi5maWxlKQpgYGAKCiMjIFJ1biBNQ1Bjb3VudGVyCgpgYGB7cn0KbWNwLnJlc3VsdHMgPC0gCiAgTUNQY291bnRlcjo6TUNQY291bnRlci5lc3RpbWF0ZShleHBycy5tYXQsIGZlYXR1cmVzVHlwZSA9ICJIVUdPX3N5bWJvbHMiKQptY3AubWVsdCA8LSByZXNoYXBlMjo6bWVsdChtY3AucmVzdWx0cywgdmFybmFtZXMgPSBjKCJDZWxsX3R5cGUiLCAiU2FtcGxlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLm5hbWUgPSAiTUNQX2VzdGltYXRlIikKcmVhZHI6OndyaXRlX3RzdihtY3AubWVsdCwgCiAgICAgICAgICAgICAgICAgZmlsZS5wYXRoKHJlc3VsdHMuZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5BUkVTX0NvbUJhdF9NQ1BDb3VudGVyX3Jlc3VsdHNfdGlkeS50c3YiKSkKYGBgCgojIyBDb21wYXJlIHRvIFBMSUVSIExWcwoKYE5BUkVTIExWM2Agd2FzIHRoZSBuZXV0cm9waGlsLWFzc29jaWF0ZWQgTFYgaW4gdGhlIE5BUkVTIFBMSUVSIG1vZGVsOyBpdHMKYmVzdCBtYXRjaCBpcyBgcmVjb3VudCBMVjYwM2Agb24gdGhlIGJhc2lzIG9mIHRoZSBgWmAgbWF0cmljZXMgCihgMTMtY29tcGFyZV9OQVJFU19CYCkuIApgcmVjb3VudCBMVjYwM2AgYWxzbyBhcHBlYXJzIHRvIGJlIHByZXR0eSBuZXV0cm9waGlsLXNwZWNpZmljIChub3Qgc2lnbmlmaWNhbnRseQphc3NvY2lhdGVkIHdpdGggb3RoZXIgbXllbG9pZCBjZWxsIHR5cGVzOyBgMDctc2xlX2NlbGxfdHlwZV9yZWNvdW50Ml9tb2RlbGApLiAKVGhlc2UgYXJlIHRoZSBMVnMgd2UnbGwgY29tcGFyZSB0byB0aGUgTUNQY291bnRlciBlc3RpbWF0ZXMuCgojIyMgRGF0YSB3cmFuZ2xpbmcKCmBgYHtyfQojIHRpZHkgbmV1dHJvcGhpbC1hc3NvY2lhdGVkIExWcwpuZXV0cm8ubHYuZGYgPC0gYXMuZGF0YS5mcmFtZShuYXJlcy5iWyIzLElSSVNfTmV1dHJvcGhpbC1SZXN0aW5nIiwgXSkKbmV1dHJvLmx2LmRmIDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKG5ldXRyby5sdi5kZikKY29sbmFtZXMobmV1dHJvLmx2LmRmKSA8LSBjKCJTYW1wbGUiLCAiTkFSRVNfTFYzIikKcmVjb3VudC5sdi5kZiA8LSBhcy5kYXRhLmZyYW1lKHJlY291bnQuYlsiNjAzLFNWTSBOZXV0cm9waGlscyIsIF0pCnJlY291bnQubHYuZGYgPC0gdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4ocmVjb3VudC5sdi5kZikKY29sbmFtZXMocmVjb3VudC5sdi5kZikgPC0gYygiU2FtcGxlIiwgInJlY291bnRfTFY2MDMiKQpuZXV0cm8ubHYuZGYgPC0gZHBseXI6OmlubmVyX2pvaW4obmV1dHJvLmx2LmRmLCByZWNvdW50Lmx2LmRmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gIlNhbXBsZSIpCmBgYApgYGB7cn0KaGVhZChuZXV0cm8ubHYuZGYpCmBgYAoKYGBge3J9CiMgam9pbiB3aXRoIE1DUGNvdW50ZXIgbmV1dHJvcGhpbCBlc3RpbWF0ZXMKbmV1dHJvLmRmIDwtIGRwbHlyOjpmaWx0ZXIobWNwLm1lbHQsIENlbGxfdHlwZSA9PSAiTmV1dHJvcGhpbHMiKSAlPiUKICAgICAgICAgICAgICAgIGRwbHlyOjppbm5lcl9qb2luKHkgPSBuZXV0cm8ubHYuZGYsIGJ5ID0gIlNhbXBsZSIpCm5ldXRyby5maWxlIDwtIGZpbGUucGF0aChyZXN1bHRzLmRpciwgIk5BUkVTX25ldXRyb3BoaWxfTFZfbWNwX2FsbC50c3YiKQpyZWFkcjo6d3JpdGVfdHN2KG5ldXRyby5kZiwgcGF0aCA9IG5ldXRyby5maWxlKQpgYGAKCiMjIyBQbG90dGluZwoKYGBge3J9CnN1bW1hcnkobG0obmV1dHJvLmRmJE1DUF9lc3RpbWF0ZSB+IG5ldXRyby5kZiROQVJFU19MVjMpKQpgYGAKCmBgYHtyfQpuYXJlcy5wIDwtIG5ldXRyby5kZiAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHggPSBOQVJFU19MVjMsIHkgPSBNQ1BfZXN0aW1hdGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChhbHBoYSA9IDAuNykgKwogIGdncGxvdDI6Omdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICBnZ3Bsb3QyOjp0aGVtZV9idygpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIk5BUkVTIFBMSUVSIG1vZGVsIiwKICAgICAgICAgICAgICAgIHkgPSAiTUNQY291bnRlciBOZXV0cm9waGlsIEVzdGltYXRlIiwKICAgICAgICAgICAgICAgIHggPSAiTkFSRVMgTFYzIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgIHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogIGdncGxvdDI6OmFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSAtMC4xLCB5ID0gMi4yNSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAici1zcXVhcmVkID0gMC45OCIsIHNpemUgPSA1KQpuYXJlcy5wCmBgYAoKYGBge3J9CnN1bW1hcnkobG0obmV1dHJvLmRmJE1DUF9lc3RpbWF0ZSB+IG5ldXRyby5kZiRyZWNvdW50X0xWNjAzKSkKYGBgCgpgYGB7cn0KcmVjb3VudC5wIDwtIG5ldXRyby5kZiAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHggPSByZWNvdW50X0xWNjAzLCB5ID0gTUNQX2VzdGltYXRlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoYWxwaGEgPSAwLjcpICsKICBnZ3Bsb3QyOjpnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9ICJyZWNvdW50MiBQTElFUiBtb2RlbCIsCiAgICAgICAgICAgICAgICB5ID0gIk1DUGNvdW50ZXIgTmV1dHJvcGhpbCBFc3RpbWF0ZSIsCiAgICAgICAgICAgICAgICB4ID0gInJlY291bnQgTFY2MDMiKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgdGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgZ2dwbG90Mjo6YW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IC0wLjEsIHkgPSAyLjUsIAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gInItc3F1YXJlZCA9IDAuOTAiLCBzaXplID0gNSkKcmVjb3VudC5wCmBgYAoKYGBge3J9CnBkZihmaWxlLnBhdGgocGxvdC5kaXIsICJOQVJFU19NQ1Bjb3VudGVyX21vZGVsX2NvbXBhcmlzb24ucGRmIiksIHdpZHRoID0gMTQsCiAgICBoZWlnaHQgPSA3KQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShuYXJlcy5wLCByZWNvdW50LnAsIG5jb2wgPSAyKQpkZXYub2ZmKCkKYGBgCg==