1 Paths

dir.data <- "./DATASETS/"

2 Single CpG meta analysis with bacon adjusted results

library(dplyr)
library(meta)

2.1 Import datasets and pre-process for each cohort

results.files <- dir(
    dir.data,
    pattern = "single_cpg_pVal_df.csv",
    recursive = TRUE,
    full.names = TRUE,
    ignore.case = TRUE
)

for(i in results.files){
    data <- readr::read_csv(i)
    dataset <- unlist(stringr::str_split(i,"//|/"))[3]  %>% as.character()
    aux <- paste0(dataset, "_", colnames(data)[-1])
    colnames(data) <- c("cpg", aux)
    assign(dataset,data)
}

2.2 Create a merged final dataset

cohort_ls <- list(
    Gasparoni = GASPARONI,
    London_PFC = LONDON,
    MtSinai = MTSINAI,
    ROSMAP = ROSMAP
)

### outer join input region
multi_cohorts <- Reduce(
    function(x,y, ...) merge(x, y, by = "cpg", all = TRUE, ...),
    cohort_ls
)
dim(multi_cohorts)

2.3 Meta analysis

### calculate meta analysis z scores and p values
doParallel::registerDoParallel(cores = parallel::detectCores()/2)
meta_df <- plyr::adply(
    .data = multi_cohorts, 
    .margins = 1, 
    .fun =  function(rowOne_df){
        
        est <- rowOne_df[grep("Estimate.bacon",colnames(rowOne_df))] %>% as.numeric
        
        direction <-  paste(
            ifelse(
                is.na(est), ".",
                ifelse(est > 0, "+", "-")
            ), collapse = "")
        
        se <- rowOne_df[grep("StdErr.bacon",colnames(rowOne_df))] %>% as.numeric
        cohort <- gsub("_StdErr.bacon","",grep("StdErr.bacon",colnames(rowOne_df),value = T))
        rowOne_reform_df <- data.frame(
            cohort = cohort,
            est = est,
            se = se,
            stringsAsFactors = FALSE
        )
        
        f <- metagen(
            TE = est,
            seTE = se,
            data = rowOne_reform_df
        )
        
        tibble::tibble(
            cpg = rowOne_df$cpg,
            estimate.bacon = f$TE.fixed,
            se.bacon = f$seTE.fixed,
            pVal.fixed.bacon = f$pval.fixed,
            pVal.random.bacon = f$pval.random,
            pValQ.bacon = f$pval.Q,
            direction.bacon = direction
        )
    }  , .progress = "time",
    .parallel = TRUE,
    .id = NULL
)

### create final pVal
meta_df$pVal.final.bacon <- ifelse(
    meta_df$pValQ.bacon > 0.05, meta_df$pVal.fixed.bacon, meta_df$pVal.random.bacon
)

### calculate fdr
meta_df$fdr.bacon <- p.adjust(meta_df$pVal.final.bacon, method = "fdr")

### order meta_df
meta_final_df <- meta_df[, c(grep("_",colnames(meta_df),invert = T),
                           grep("_",colnames(meta_df),invert = F))
                         ]

meta_final_ordered_df <- meta_final_df[order(meta_final_df$pVal.final.bacon),]

2.4 Add annotation to input cpgs

library(sesame)
probes.info <- sesameDataGet("HM450.hg19.manifest")
probes.info <- probes.info[meta_final_ordered_df$cpg %>% as.character()] %>% 
    as.data.frame %>% 
    dplyr::select(c("seqnames","start","end"))

result_annot_df <- merge(
    y = meta_final_ordered_df,
    x = probes.info,
    by.y = "cpg",
    by.x = "row.names",
    all.y = TRUE,
    sort = FALSE
)

colnames(result_annot_df)[1] <- "cpg"
### final raw data
write.csv(
    result_annot_df %>% as.data.frame(),
    file.path(dir.data, "meta_analysis_single_cpg_bacon_df.csv"),
    row.names = FALSE
)

2.5 Delete cross-hybridizing and smoking probes from sig probes

### Exclude non-significant probes
result_annot_sig_df <- result_annot_df %>% filter(fdr.bacon < 0.05) #dim:3979 24

write.csv(
    result_annot_sig_df %>% as.data.frame(),
    paste0(dir.data, "meta_analysis_single_cpg_sig_df_bacon.csv"),
    row.names = FALSE
)

### Get crosshybrdizing probes
library(ExperimentHub)

eh = ExperimentHub()
query(eh, "DMRcate")
crosshyb <- eh[["EH3129"]]


### Get significant smoking probes
smoking.file <- "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5267325/bin/NIHMS817273-supplement-001506_-_Supplemental_Tables.xlsx"

if(!file.exists(basename(smoking.file))) downloader::download(smoking.file,basename(smoking.file))

smoking <- readxl::read_xlsx(
    basename(smoking.file),
    sheet = "02",
    skip = 2
)
smoking.sig.probes <- smoking %>% dplyr::filter(`P-value` < 1*10^(-7)) %>% pull("Probe ID")


### Exclude cross-hybridizing and smoking probes
result_annot_sig_df$cpg <- as.character(result_annot_sig_df$cpg)

result_annot_sig_no_crossHyb_smoking_df <- result_annot_sig_df[
    !(result_annot_sig_df$cpg %in% c(crosshyb, smoking.sig.probes)),
] #dim: 3751 28

### Add annotation
library(coMethDMR)

result_annot_sig_no_crossHyb_smoking_df$chrom <- as.character(
    result_annot_sig_no_crossHyb_smoking_df$seqnames
)

result_final <- AnnotateResults(result_annot_sig_no_crossHyb_smoking_df)

result_final_ordered <- result_final[
    ,c(
        1:4, 46:49, 5:12,
        grep("GASPARONI", colnames(result_final), ignore.case = TRUE, invert = FALSE),
        grep("LONDON", colnames(result_final), ignore.case = TRUE, invert = FALSE),
        grep("MTSINAI", colnames(result_final), ignore.case = TRUE, invert = FALSE),
        grep("ROSMAP", colnames(result_final), ignore.case = TRUE, invert = FALSE)
    )
]

write.csv(
    result_final_ordered %>% as.data.frame(),
    paste0(dir.data, "meta_analysis_single_cpg_sig_no_crossHyb_smoking_df_bacon.csv"),
    row.names = FALSE
)


result_annot_sig_df_2_4_minus_7 <- result_annot_df %>% filter(pVal.final.bacon <  2.4e-07) 

write.csv(
    result_annot_sig_df_2_4_minus_7 %>% as.data.frame(),
    paste0(dir.data, "meta_analysis_single_cpg_sig_df_bacon_2_4_minus_7.csv"),
    row.names = FALSE
)


### Exclude cross-hybridizing and smoking probes
result_annot_sig_df_2_4_minus_7$cpg <- as.character(result_annot_sig_df_2_4_minus_7$cpg)

result_annot_sig_no_crossHyb_smoking_df_2_4_minus_7 <- result_annot_sig_df_2_4_minus_7[
    !(result_annot_sig_df_2_4_minus_7$cpg %in% c(crosshyb, smoking.sig.probes)),
] #dim: 3751 28

### Add annotation
library(coMethDMR)

result_annot_sig_no_crossHyb_smoking_df_2_4_minus_7$chrom <- as.character(
    result_annot_sig_no_crossHyb_smoking_df_2_4_minus_7$seqnames
)

result_final_2_4_minus_7 <- AnnotateResults(result_annot_sig_no_crossHyb_smoking_df_2_4_minus_7)

result_final_ordered_2_4_minus_7<- result_final_2_4_minus_7[
    ,c(
        1:4, 46:49, 5:12,
        grep("GASPARONI", colnames(result_final_2_4_minus_7), ignore.case = TRUE, invert = FALSE),
        grep("LONDON", colnames(result_final_2_4_minus_7), ignore.case = TRUE, invert = FALSE),
        grep("MTSINAI", colnames(result_final_2_4_minus_7), ignore.case = TRUE, invert = FALSE),
        grep("ROSMAP", colnames(result_final_2_4_minus_7), ignore.case = TRUE, invert = FALSE)
    )
]

write.csv(
    result_final_ordered_2_4_minus_7 %>% as.data.frame(),
    paste0(dir.data, "meta_analysis_single_cpg_sig_no_crossHyb_smoking_df_bacon_2_4_minus_7.csv"),
    row.names = FALSE
)
readr::read_csv(
  paste0(dir.data, "meta_analysis_single_cpg_sig_no_crossHyb_smoking_df_bacon.csv"),
  col_types = readr::cols()
)

3 Session information

devtools::session_info()
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value                       
##  version  R version 4.0.2 (2020-06-22)
##  os       macOS Catalina 10.15.6      
##  system   x86_64, darwin17.0          
##  ui       X11                         
##  language (EN)                        
##  collate  en_US.UTF-8                 
##  ctype    en_US.UTF-8                 
##  tz       America/New_York            
##  date     2020-08-11                  
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package      * version  date       lib source        
##  assertthat     0.2.1    2019-03-21 [1] CRAN (R 4.0.2)
##  backports      1.1.8    2020-06-17 [1] CRAN (R 4.0.2)
##  base64enc      0.1-3    2015-07-28 [1] CRAN (R 4.0.2)
##  boot           1.3-25   2020-04-26 [1] CRAN (R 4.0.2)
##  callr          3.4.3    2020-03-28 [1] CRAN (R 4.0.2)
##  cli            2.0.2    2020-02-28 [1] CRAN (R 4.0.2)
##  CompQuadForm   1.4.3    2017-04-12 [1] CRAN (R 4.0.2)
##  crayon         1.3.4    2017-09-16 [1] CRAN (R 4.0.2)
##  desc           1.2.0    2018-05-01 [1] CRAN (R 4.0.2)
##  devtools       2.3.1    2020-07-21 [1] CRAN (R 4.0.2)
##  digest         0.6.25   2020-02-23 [1] CRAN (R 4.0.2)
##  dplyr        * 1.0.1    2020-07-31 [1] CRAN (R 4.0.2)
##  ellipsis       0.3.1    2020-05-15 [1] CRAN (R 4.0.2)
##  evaluate       0.14     2019-05-28 [1] CRAN (R 4.0.1)
##  fansi          0.4.1    2020-01-08 [1] CRAN (R 4.0.2)
##  fs             1.5.0    2020-07-31 [1] CRAN (R 4.0.2)
##  generics       0.0.2    2018-11-29 [1] CRAN (R 4.0.2)
##  glue           1.4.1    2020-05-13 [1] CRAN (R 4.0.2)
##  hms            0.5.3    2020-01-08 [1] CRAN (R 4.0.2)
##  htmltools      0.5.0    2020-06-16 [1] CRAN (R 4.0.2)
##  jsonlite       1.7.0    2020-06-25 [1] CRAN (R 4.0.2)
##  knitr          1.29     2020-06-23 [1] CRAN (R 4.0.2)
##  lattice        0.20-41  2020-04-02 [1] CRAN (R 4.0.2)
##  lifecycle      0.2.0    2020-03-06 [1] CRAN (R 4.0.2)
##  lme4           1.1-23   2020-04-07 [1] CRAN (R 4.0.1)
##  magrittr       1.5      2014-11-22 [1] CRAN (R 4.0.2)
##  MASS           7.3-51.6 2020-04-26 [1] CRAN (R 4.0.2)
##  Matrix         1.2-18   2019-11-27 [1] CRAN (R 4.0.2)
##  memoise        1.1.0    2017-04-21 [1] CRAN (R 4.0.2)
##  meta         * 4.13-0   2020-07-03 [1] CRAN (R 4.0.2)
##  metafor        2.4-0    2020-03-19 [1] CRAN (R 4.0.2)
##  minqa          1.2.4    2014-10-09 [1] CRAN (R 4.0.2)
##  nlme           3.1-148  2020-05-24 [1] CRAN (R 4.0.2)
##  nloptr         1.2.2.2  2020-07-02 [1] CRAN (R 4.0.2)
##  pillar         1.4.6    2020-07-10 [1] CRAN (R 4.0.2)
##  pkgbuild       1.1.0    2020-07-13 [1] CRAN (R 4.0.2)
##  pkgconfig      2.0.3    2019-09-22 [1] CRAN (R 4.0.2)
##  pkgload        1.1.0    2020-05-29 [1] CRAN (R 4.0.2)
##  prettyunits    1.1.1    2020-01-24 [1] CRAN (R 4.0.2)
##  processx       3.4.3    2020-07-05 [1] CRAN (R 4.0.2)
##  ps             1.3.3    2020-05-08 [1] CRAN (R 4.0.2)
##  purrr          0.3.4    2020-04-17 [1] CRAN (R 4.0.2)
##  R6             2.4.1    2019-11-12 [1] CRAN (R 4.0.2)
##  Rcpp           1.0.5    2020-07-06 [1] CRAN (R 4.0.2)
##  readr          1.3.1    2018-12-21 [1] CRAN (R 4.0.2)
##  remotes        2.2.0    2020-07-21 [1] CRAN (R 4.0.2)
##  rlang          0.4.7    2020-07-09 [1] CRAN (R 4.0.2)
##  rmarkdown      2.3      2020-06-18 [1] CRAN (R 4.0.2)
##  rprojroot      1.3-2    2018-01-03 [1] CRAN (R 4.0.2)
##  sessioninfo    1.1.1    2018-11-05 [1] CRAN (R 4.0.2)
##  statmod        1.4.34   2020-02-17 [1] CRAN (R 4.0.2)
##  stringi        1.4.6    2020-02-17 [1] CRAN (R 4.0.2)
##  stringr        1.4.0    2019-02-10 [1] CRAN (R 4.0.2)
##  testthat       2.3.2    2020-03-02 [1] CRAN (R 4.0.2)
##  tibble         3.0.3    2020-07-10 [1] CRAN (R 4.0.2)
##  tidyselect     1.1.0    2020-05-11 [1] CRAN (R 4.0.2)
##  usethis        1.6.1    2020-04-29 [1] CRAN (R 4.0.2)
##  vctrs          0.3.2    2020-07-15 [1] CRAN (R 4.0.2)
##  withr          2.2.0    2020-04-20 [1] CRAN (R 4.0.2)
##  xfun           0.16     2020-07-24 [1] CRAN (R 4.0.2)
##  yaml           2.2.1    2020-02-01 [1] CRAN (R 4.0.2)
## 
## [1] /Library/Frameworks/R.framework/Versions/4.0/Resources/library
LS0tDQp0aXRsZTogIk1ldGEgYW5hbHlzaXMgdXNpbmcgYmFjb24gaW5mbGF0aW9uLSBhbmQgYmlhcy1jb3JyZWN0ZWQgUC12YWx1ZXMiDQphdXRob3I6ICJMYW55dSBaaGFuZywgVGlhZ28gQy4gU2lsdmEsIExpbHkgV2FuZyINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgcm1hcmtkb3duOjpodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBsdW1lbg0KICAgIHRvYzogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IHllcw0KICAgIHRvY19kZXB0aDogMw0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUgICAgDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSkNCmBgYA0KDQojIFBhdGhzDQoNCmBgYHtSfQ0KZGlyLmRhdGEgPC0gIi4vREFUQVNFVFMvIg0KYGBgDQoNCiMgU2luZ2xlIENwRyBtZXRhIGFuYWx5c2lzIHdpdGggYmFjb24gYWRqdXN0ZWQgcmVzdWx0cw0KDQpgYGB7UiwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIHJlc3VsdCA9ICJoaWRlIn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KG1ldGEpDQpgYGANCg0KIyMgSW1wb3J0IGRhdGFzZXRzIGFuZCBwcmUtcHJvY2VzcyBmb3IgZWFjaCBjb2hvcnQgDQoNCmBgYHtSLCBldmFsID0gRkFMU0V9DQpyZXN1bHRzLmZpbGVzIDwtIGRpcigNCiAgICBkaXIuZGF0YSwNCiAgICBwYXR0ZXJuID0gInNpbmdsZV9jcGdfcFZhbF9kZi5jc3YiLA0KICAgIHJlY3Vyc2l2ZSA9IFRSVUUsDQogICAgZnVsbC5uYW1lcyA9IFRSVUUsDQogICAgaWdub3JlLmNhc2UgPSBUUlVFDQopDQoNCmZvcihpIGluIHJlc3VsdHMuZmlsZXMpew0KICAgIGRhdGEgPC0gcmVhZHI6OnJlYWRfY3N2KGkpDQogICAgZGF0YXNldCA8LSB1bmxpc3Qoc3RyaW5ncjo6c3RyX3NwbGl0KGksIi8vfC8iKSlbM10gICU+JSBhcy5jaGFyYWN0ZXIoKQ0KICAgIGF1eCA8LSBwYXN0ZTAoZGF0YXNldCwgIl8iLCBjb2xuYW1lcyhkYXRhKVstMV0pDQogICAgY29sbmFtZXMoZGF0YSkgPC0gYygiY3BnIiwgYXV4KQ0KICAgIGFzc2lnbihkYXRhc2V0LGRhdGEpDQp9DQpgYGANCg0KIyMgQ3JlYXRlIGEgbWVyZ2VkIGZpbmFsIGRhdGFzZXQNCg0KYGBge1IsIGV2YWwgPSBGQUxTRX0NCmNvaG9ydF9scyA8LSBsaXN0KA0KICAgIEdhc3Bhcm9uaSA9IEdBU1BBUk9OSSwNCiAgICBMb25kb25fUEZDID0gTE9ORE9OLA0KICAgIE10U2luYWkgPSBNVFNJTkFJLA0KICAgIFJPU01BUCA9IFJPU01BUA0KKQ0KDQojIyMgb3V0ZXIgam9pbiBpbnB1dCByZWdpb24NCm11bHRpX2NvaG9ydHMgPC0gUmVkdWNlKA0KICAgIGZ1bmN0aW9uKHgseSwgLi4uKSBtZXJnZSh4LCB5LCBieSA9ICJjcGciLCBhbGwgPSBUUlVFLCAuLi4pLA0KICAgIGNvaG9ydF9scw0KKQ0KZGltKG11bHRpX2NvaG9ydHMpDQpgYGANCg0KIyMgTWV0YSBhbmFseXNpcyANCg0KYGBge1IsIGV2YWwgPSBGQUxTRX0NCiMjIyBjYWxjdWxhdGUgbWV0YSBhbmFseXNpcyB6IHNjb3JlcyBhbmQgcCB2YWx1ZXMNCmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbChjb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLzIpDQptZXRhX2RmIDwtIHBseXI6OmFkcGx5KA0KICAgIC5kYXRhID0gbXVsdGlfY29ob3J0cywgDQogICAgLm1hcmdpbnMgPSAxLCANCiAgICAuZnVuID0gIGZ1bmN0aW9uKHJvd09uZV9kZil7DQogICAgICAgIA0KICAgICAgICBlc3QgPC0gcm93T25lX2RmW2dyZXAoIkVzdGltYXRlLmJhY29uIixjb2xuYW1lcyhyb3dPbmVfZGYpKV0gJT4lIGFzLm51bWVyaWMNCiAgICAgICAgDQogICAgICAgIGRpcmVjdGlvbiA8LSAgcGFzdGUoDQogICAgICAgICAgICBpZmVsc2UoDQogICAgICAgICAgICAgICAgaXMubmEoZXN0KSwgIi4iLA0KICAgICAgICAgICAgICAgIGlmZWxzZShlc3QgPiAwLCAiKyIsICItIikNCiAgICAgICAgICAgICksIGNvbGxhcHNlID0gIiIpDQogICAgICAgIA0KICAgICAgICBzZSA8LSByb3dPbmVfZGZbZ3JlcCgiU3RkRXJyLmJhY29uIixjb2xuYW1lcyhyb3dPbmVfZGYpKV0gJT4lIGFzLm51bWVyaWMNCiAgICAgICAgY29ob3J0IDwtIGdzdWIoIl9TdGRFcnIuYmFjb24iLCIiLGdyZXAoIlN0ZEVyci5iYWNvbiIsY29sbmFtZXMocm93T25lX2RmKSx2YWx1ZSA9IFQpKQ0KICAgICAgICByb3dPbmVfcmVmb3JtX2RmIDwtIGRhdGEuZnJhbWUoDQogICAgICAgICAgICBjb2hvcnQgPSBjb2hvcnQsDQogICAgICAgICAgICBlc3QgPSBlc3QsDQogICAgICAgICAgICBzZSA9IHNlLA0KICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQogICAgICAgICkNCiAgICAgICAgDQogICAgICAgIGYgPC0gbWV0YWdlbigNCiAgICAgICAgICAgIFRFID0gZXN0LA0KICAgICAgICAgICAgc2VURSA9IHNlLA0KICAgICAgICAgICAgZGF0YSA9IHJvd09uZV9yZWZvcm1fZGYNCiAgICAgICAgKQ0KICAgICAgICANCiAgICAgICAgdGliYmxlOjp0aWJibGUoDQogICAgICAgICAgICBjcGcgPSByb3dPbmVfZGYkY3BnLA0KICAgICAgICAgICAgZXN0aW1hdGUuYmFjb24gPSBmJFRFLmZpeGVkLA0KICAgICAgICAgICAgc2UuYmFjb24gPSBmJHNlVEUuZml4ZWQsDQogICAgICAgICAgICBwVmFsLmZpeGVkLmJhY29uID0gZiRwdmFsLmZpeGVkLA0KICAgICAgICAgICAgcFZhbC5yYW5kb20uYmFjb24gPSBmJHB2YWwucmFuZG9tLA0KICAgICAgICAgICAgcFZhbFEuYmFjb24gPSBmJHB2YWwuUSwNCiAgICAgICAgICAgIGRpcmVjdGlvbi5iYWNvbiA9IGRpcmVjdGlvbg0KICAgICAgICApDQogICAgfSAgLCAucHJvZ3Jlc3MgPSAidGltZSIsDQogICAgLnBhcmFsbGVsID0gVFJVRSwNCiAgICAuaWQgPSBOVUxMDQopDQoNCiMjIyBjcmVhdGUgZmluYWwgcFZhbA0KbWV0YV9kZiRwVmFsLmZpbmFsLmJhY29uIDwtIGlmZWxzZSgNCiAgICBtZXRhX2RmJHBWYWxRLmJhY29uID4gMC4wNSwgbWV0YV9kZiRwVmFsLmZpeGVkLmJhY29uLCBtZXRhX2RmJHBWYWwucmFuZG9tLmJhY29uDQopDQoNCiMjIyBjYWxjdWxhdGUgZmRyDQptZXRhX2RmJGZkci5iYWNvbiA8LSBwLmFkanVzdChtZXRhX2RmJHBWYWwuZmluYWwuYmFjb24sIG1ldGhvZCA9ICJmZHIiKQ0KDQojIyMgb3JkZXIgbWV0YV9kZg0KbWV0YV9maW5hbF9kZiA8LSBtZXRhX2RmWywgYyhncmVwKCJfIixjb2xuYW1lcyhtZXRhX2RmKSxpbnZlcnQgPSBUKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXAoIl8iLGNvbG5hbWVzKG1ldGFfZGYpLGludmVydCA9IEYpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgIF0NCg0KbWV0YV9maW5hbF9vcmRlcmVkX2RmIDwtIG1ldGFfZmluYWxfZGZbb3JkZXIobWV0YV9maW5hbF9kZiRwVmFsLmZpbmFsLmJhY29uKSxdDQpgYGANCg0KIyMgQWRkIGFubm90YXRpb24gdG8gaW5wdXQgY3Bncw0KDQpgYGB7UiwgZXZhbCA9IEZBTFNFfQ0KbGlicmFyeShzZXNhbWUpDQpwcm9iZXMuaW5mbyA8LSBzZXNhbWVEYXRhR2V0KCJITTQ1MC5oZzE5Lm1hbmlmZXN0IikNCnByb2Jlcy5pbmZvIDwtIHByb2Jlcy5pbmZvW21ldGFfZmluYWxfb3JkZXJlZF9kZiRjcGcgJT4lIGFzLmNoYXJhY3RlcigpXSAlPiUgDQogICAgYXMuZGF0YS5mcmFtZSAlPiUgDQogICAgZHBseXI6OnNlbGVjdChjKCJzZXFuYW1lcyIsInN0YXJ0IiwiZW5kIikpDQoNCnJlc3VsdF9hbm5vdF9kZiA8LSBtZXJnZSgNCiAgICB5ID0gbWV0YV9maW5hbF9vcmRlcmVkX2RmLA0KICAgIHggPSBwcm9iZXMuaW5mbywNCiAgICBieS55ID0gImNwZyIsDQogICAgYnkueCA9ICJyb3cubmFtZXMiLA0KICAgIGFsbC55ID0gVFJVRSwNCiAgICBzb3J0ID0gRkFMU0UNCikNCg0KY29sbmFtZXMocmVzdWx0X2Fubm90X2RmKVsxXSA8LSAiY3BnIg0KIyMjIGZpbmFsIHJhdyBkYXRhDQp3cml0ZS5jc3YoDQogICAgcmVzdWx0X2Fubm90X2RmICU+JSBhcy5kYXRhLmZyYW1lKCksDQogICAgZmlsZS5wYXRoKGRpci5kYXRhLCAibWV0YV9hbmFseXNpc19zaW5nbGVfY3BnX2JhY29uX2RmLmNzdiIpLA0KICAgIHJvdy5uYW1lcyA9IEZBTFNFDQopDQpgYGANCg0KYGBge1IsIGV2YWwgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQ0KDQojIyBQcmVwYXJlIGRhdGFzZXQgZm9yIGNvbWItcA0KcmVzdWx0X2Zvcl9jb21icF9kZiA8LSByZXN1bHRfYW5ub3RfZGZbDQogICwgYygic2VxbmFtZXMiLCAic3RhcnQiLCAiZW5kIiwgInBWYWwuZmluYWwuYmFjb24iKQ0KXQ0KY29sbmFtZXMocmVzdWx0X2Zvcl9jb21icF9kZilbNF0gPC0gInBWYWx1ZSINCmNvbG5hbWVzKHJlc3VsdF9mb3JfY29tYnBfZGYpWzFdIDwtICJjaHIiDQoNCg0Kd3JpdGUuY3N2KA0KICByZXN1bHRfZm9yX2NvbWJwX2RmICU+JSBhcy5kYXRhLmZyYW1lKCksDQogIHBhc3RlMChkaXIuZGF0YSwgIm1ldGFfYW5hbHlzaXNfc2luZ2xlX2NwZ19kZl9mb3JfY29tYnBfYmFjb24uY3N2IiksDQogIHJvdy5uYW1lcyA9IEZBTFNFDQopDQpgYGANCg0KDQoNCmBgYHtSLCBldmFsID0gRkFMU0UsIGluY2x1ZGUgPSBGQUxTRX0NCg0KIyMgQ29tcGFyZSByZXN1bHRzIHdpdGggc21pdGggdG9wIDEwDQpzbWl0aFRvcDEwIDwtIGMoDQogICJjZzIyODY3ODE2IiwgImNnMDY5NzcyODUiLCAiY2cwNTc4MzM4NCIsICJjZzA3MzQ5ODE1IiwgImNnMjE4MDYyNDIiLA0KICAiY2cwMzgzNDc2NyIsICJjZzEzOTM1NTc3IiwgImNnMjcwNzg4OTAiLCAiY2cyMjk2MjEyMyIsICJjZzI2MTk5ODU3Ig0KKQ0KDQpjb2xuYW1lcyhyZXN1bHRfYW5ub3RfZGYpWzFdIDwtICJjcGciDQoNCnJlc3VsdF9zbWl0aFRvcDEwIDwtIHJlc3VsdF9hbm5vdF9kZlttYXRjaChzbWl0aFRvcDEwLCByZXN1bHRfYW5ub3RfZGYkY3BnKSxdDQpyZXN1bHRfc21pdGhUb3AxMCA8LSByZXN1bHRfc21pdGhUb3AxMFtjb21wbGV0ZS5jYXNlcyhyZXN1bHRfc21pdGhUb3AxMCRjcGcpLF0NCg0Kd3JpdGUuY3N2KA0KICByZXN1bHRfc21pdGhUb3AxMCAlPiUgYXMuZGF0YS5mcmFtZSgpLA0KICBwYXN0ZTAoZGlyLmRhdGEsICJtZXRhX2FuYWx5c2lzX3NpbmdsZV9jcGdfZGZfc21pdGhUb3AxMF9iYWNvbi5jc3YiKSwNCiAgcm93Lm5hbWVzID0gRkFMU0UNCikNCmBgYA0KDQojIyBEZWxldGUgY3Jvc3MtaHlicmlkaXppbmcgYW5kIHNtb2tpbmcgcHJvYmVzIGZyb20gc2lnIHByb2Jlcw0KDQpgYGB7UiwgZXZhbCA9IEZBTFNFfQ0KIyMjIEV4Y2x1ZGUgbm9uLXNpZ25pZmljYW50IHByb2Jlcw0KcmVzdWx0X2Fubm90X3NpZ19kZiA8LSByZXN1bHRfYW5ub3RfZGYgJT4lIGZpbHRlcihmZHIuYmFjb24gPCAwLjA1KSAjZGltOjM5NzkgMjQNCg0Kd3JpdGUuY3N2KA0KICAgIHJlc3VsdF9hbm5vdF9zaWdfZGYgJT4lIGFzLmRhdGEuZnJhbWUoKSwNCiAgICBwYXN0ZTAoZGlyLmRhdGEsICJtZXRhX2FuYWx5c2lzX3NpbmdsZV9jcGdfc2lnX2RmX2JhY29uLmNzdiIpLA0KICAgIHJvdy5uYW1lcyA9IEZBTFNFDQopDQoNCiMjIyBHZXQgY3Jvc3NoeWJyZGl6aW5nIHByb2Jlcw0KbGlicmFyeShFeHBlcmltZW50SHViKQ0KDQplaCA9IEV4cGVyaW1lbnRIdWIoKQ0KcXVlcnkoZWgsICJETVJjYXRlIikNCmNyb3NzaHliIDwtIGVoW1siRUgzMTI5Il1dDQoNCg0KIyMjIEdldCBzaWduaWZpY2FudCBzbW9raW5nIHByb2Jlcw0Kc21va2luZy5maWxlIDwtICJodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM1MjY3MzI1L2Jpbi9OSUhNUzgxNzI3My1zdXBwbGVtZW50LTAwMTUwNl8tX1N1cHBsZW1lbnRhbF9UYWJsZXMueGxzeCINCg0KaWYoIWZpbGUuZXhpc3RzKGJhc2VuYW1lKHNtb2tpbmcuZmlsZSkpKSBkb3dubG9hZGVyOjpkb3dubG9hZChzbW9raW5nLmZpbGUsYmFzZW5hbWUoc21va2luZy5maWxlKSkNCg0Kc21va2luZyA8LSByZWFkeGw6OnJlYWRfeGxzeCgNCiAgICBiYXNlbmFtZShzbW9raW5nLmZpbGUpLA0KICAgIHNoZWV0ID0gIjAyIiwNCiAgICBza2lwID0gMg0KKQ0Kc21va2luZy5zaWcucHJvYmVzIDwtIHNtb2tpbmcgJT4lIGRwbHlyOjpmaWx0ZXIoYFAtdmFsdWVgIDwgMSoxMF4oLTcpKSAlPiUgcHVsbCgiUHJvYmUgSUQiKQ0KDQoNCiMjIyBFeGNsdWRlIGNyb3NzLWh5YnJpZGl6aW5nIGFuZCBzbW9raW5nIHByb2Jlcw0KcmVzdWx0X2Fubm90X3NpZ19kZiRjcGcgPC0gYXMuY2hhcmFjdGVyKHJlc3VsdF9hbm5vdF9zaWdfZGYkY3BnKQ0KDQpyZXN1bHRfYW5ub3Rfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYgPC0gcmVzdWx0X2Fubm90X3NpZ19kZlsNCiAgICAhKHJlc3VsdF9hbm5vdF9zaWdfZGYkY3BnICVpbiUgYyhjcm9zc2h5Yiwgc21va2luZy5zaWcucHJvYmVzKSksDQpdICNkaW06IDM3NTEgMjgNCg0KIyMjIEFkZCBhbm5vdGF0aW9uDQpsaWJyYXJ5KGNvTWV0aERNUikNCg0KcmVzdWx0X2Fubm90X3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmJGNocm9tIDwtIGFzLmNoYXJhY3RlcigNCiAgICByZXN1bHRfYW5ub3Rfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYkc2VxbmFtZXMNCikNCg0KcmVzdWx0X2ZpbmFsIDwtIEFubm90YXRlUmVzdWx0cyhyZXN1bHRfYW5ub3Rfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYpDQoNCnJlc3VsdF9maW5hbF9vcmRlcmVkIDwtIHJlc3VsdF9maW5hbFsNCiAgICAsYygNCiAgICAgICAgMTo0LCA0Njo0OSwgNToxMiwNCiAgICAgICAgZ3JlcCgiR0FTUEFST05JIiwgY29sbmFtZXMocmVzdWx0X2ZpbmFsKSwgaWdub3JlLmNhc2UgPSBUUlVFLCBpbnZlcnQgPSBGQUxTRSksDQogICAgICAgIGdyZXAoIkxPTkRPTiIsIGNvbG5hbWVzKHJlc3VsdF9maW5hbCksIGlnbm9yZS5jYXNlID0gVFJVRSwgaW52ZXJ0ID0gRkFMU0UpLA0KICAgICAgICBncmVwKCJNVFNJTkFJIiwgY29sbmFtZXMocmVzdWx0X2ZpbmFsKSwgaWdub3JlLmNhc2UgPSBUUlVFLCBpbnZlcnQgPSBGQUxTRSksDQogICAgICAgIGdyZXAoIlJPU01BUCIsIGNvbG5hbWVzKHJlc3VsdF9maW5hbCksIGlnbm9yZS5jYXNlID0gVFJVRSwgaW52ZXJ0ID0gRkFMU0UpDQogICAgKQ0KXQ0KDQp3cml0ZS5jc3YoDQogICAgcmVzdWx0X2ZpbmFsX29yZGVyZWQgJT4lIGFzLmRhdGEuZnJhbWUoKSwNCiAgICBwYXN0ZTAoZGlyLmRhdGEsICJtZXRhX2FuYWx5c2lzX3NpbmdsZV9jcGdfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGZfYmFjb24uY3N2IiksDQogICAgcm93Lm5hbWVzID0gRkFMU0UNCikNCg0KDQpyZXN1bHRfYW5ub3Rfc2lnX2RmXzJfNF9taW51c183IDwtIHJlc3VsdF9hbm5vdF9kZiAlPiUgZmlsdGVyKHBWYWwuZmluYWwuYmFjb24gPCAgMi40ZS0wNykgDQoNCndyaXRlLmNzdigNCiAgICByZXN1bHRfYW5ub3Rfc2lnX2RmXzJfNF9taW51c183ICU+JSBhcy5kYXRhLmZyYW1lKCksDQogICAgcGFzdGUwKGRpci5kYXRhLCAibWV0YV9hbmFseXNpc19zaW5nbGVfY3BnX3NpZ19kZl9iYWNvbl8yXzRfbWludXNfNy5jc3YiKSwNCiAgICByb3cubmFtZXMgPSBGQUxTRQ0KKQ0KDQoNCiMjIyBFeGNsdWRlIGNyb3NzLWh5YnJpZGl6aW5nIGFuZCBzbW9raW5nIHByb2Jlcw0KcmVzdWx0X2Fubm90X3NpZ19kZl8yXzRfbWludXNfNyRjcGcgPC0gYXMuY2hhcmFjdGVyKHJlc3VsdF9hbm5vdF9zaWdfZGZfMl80X21pbnVzXzckY3BnKQ0KDQpyZXN1bHRfYW5ub3Rfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGZfMl80X21pbnVzXzcgPC0gcmVzdWx0X2Fubm90X3NpZ19kZl8yXzRfbWludXNfN1sNCiAgICAhKHJlc3VsdF9hbm5vdF9zaWdfZGZfMl80X21pbnVzXzckY3BnICVpbiUgYyhjcm9zc2h5Yiwgc21va2luZy5zaWcucHJvYmVzKSksDQpdICNkaW06IDM3NTEgMjgNCg0KIyMjIEFkZCBhbm5vdGF0aW9uDQpsaWJyYXJ5KGNvTWV0aERNUikNCg0KcmVzdWx0X2Fubm90X3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmXzJfNF9taW51c183JGNocm9tIDwtIGFzLmNoYXJhY3RlcigNCiAgICByZXN1bHRfYW5ub3Rfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGZfMl80X21pbnVzXzckc2VxbmFtZXMNCikNCg0KcmVzdWx0X2ZpbmFsXzJfNF9taW51c183IDwtIEFubm90YXRlUmVzdWx0cyhyZXN1bHRfYW5ub3Rfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGZfMl80X21pbnVzXzcpDQoNCnJlc3VsdF9maW5hbF9vcmRlcmVkXzJfNF9taW51c183PC0gcmVzdWx0X2ZpbmFsXzJfNF9taW51c183Ww0KICAgICxjKA0KICAgICAgICAxOjQsIDQ2OjQ5LCA1OjEyLA0KICAgICAgICBncmVwKCJHQVNQQVJPTkkiLCBjb2xuYW1lcyhyZXN1bHRfZmluYWxfMl80X21pbnVzXzcpLCBpZ25vcmUuY2FzZSA9IFRSVUUsIGludmVydCA9IEZBTFNFKSwNCiAgICAgICAgZ3JlcCgiTE9ORE9OIiwgY29sbmFtZXMocmVzdWx0X2ZpbmFsXzJfNF9taW51c183KSwgaWdub3JlLmNhc2UgPSBUUlVFLCBpbnZlcnQgPSBGQUxTRSksDQogICAgICAgIGdyZXAoIk1UU0lOQUkiLCBjb2xuYW1lcyhyZXN1bHRfZmluYWxfMl80X21pbnVzXzcpLCBpZ25vcmUuY2FzZSA9IFRSVUUsIGludmVydCA9IEZBTFNFKSwNCiAgICAgICAgZ3JlcCgiUk9TTUFQIiwgY29sbmFtZXMocmVzdWx0X2ZpbmFsXzJfNF9taW51c183KSwgaWdub3JlLmNhc2UgPSBUUlVFLCBpbnZlcnQgPSBGQUxTRSkNCiAgICApDQpdDQoNCndyaXRlLmNzdigNCiAgICByZXN1bHRfZmluYWxfb3JkZXJlZF8yXzRfbWludXNfNyAlPiUgYXMuZGF0YS5mcmFtZSgpLA0KICAgIHBhc3RlMChkaXIuZGF0YSwgIm1ldGFfYW5hbHlzaXNfc2luZ2xlX2NwZ19zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZl9iYWNvbl8yXzRfbWludXNfNy5jc3YiKSwNCiAgICByb3cubmFtZXMgPSBGQUxTRQ0KKQ0KYGBgDQoNCmBgYHtSfQ0KcmVhZHI6OnJlYWRfY3N2KA0KICBwYXN0ZTAoZGlyLmRhdGEsICJtZXRhX2FuYWx5c2lzX3NpbmdsZV9jcGdfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGZfYmFjb24uY3N2IiksDQogIGNvbF90eXBlcyA9IHJlYWRyOjpjb2xzKCkNCikNCmBgYA0KDQoNCmBgYHtSLCBpbmNsdWRlID0gRkFMU0UsIGV2YWwgPSBGQUxTRX0NCmRpci5yZXN1bHQgPC0gIi4uL21ldGFfYW5hbHlzaXNfcmVnaW9uX3Jlc3VsdHMvIg0KZGlyLnJlc3VsdC5tZXRhLmFuYWx5c2lzIDwtIGZpbGUucGF0aChkaXIucmVzdWx0LCAic3RlcDFfbWV0YV9hbmFseXNpcy8iKQ0KZGlyLnJlc3VsdC5zbW9raW5nX2Nyb3NzaHliIDwtIGZpbGUucGF0aChkaXIucmVzdWx0LCAic3RlcDJfc21va2luZ19jcm9zc19hbm90YXRpb24vIikNCmRpci5yZXN1bHQuY29tcCA8LSBmaWxlLnBhdGgoZGlyLnJlc3VsdCwgInN0ZXAzX2NvbXAvIikNCmRpci5yZXN1bHQuY3BnLnZzLmRtciA8LSBmaWxlLnBhdGgoZGlyLnJlc3VsdCwgInN0ZXA0X2Rtcl92c19jcGdzLyIpDQpkaXIucmVzdWx0LmxvbGEgPC0gZmlsZS5wYXRoKGRpci5yZXN1bHQsICJzdGVwNV9sb2xhLyIpDQpkaXIucmVzdWx0LmVucmljaG1lbnQgPC0gZmlsZS5wYXRoKGRpci5yZXN1bHQsICJzdGVwNl9lbnJpY2htZW50LyIpDQpkaXIucmVzdWx0LnBhdGh3YXkgPC0gZmlsZS5wYXRoKGRpci5yZXN1bHQsICJzdGVwN19wYXRod2F5LyIpDQpkYXRhLmZpbmFsIDwtIGRpci5kYXRhDQpgYGANCg0KYGBge1IsIGluY2x1ZGUgPSBGQUxTRSwgZXZhbCA9IEZBTFNFfQ0KIyMgQWRkaW5nIHNpZ25pZmljYW50IHByb2JlcyBmcm9tIHNpbmdsZSBjcGcgYW5hbHlzaXMgdG8gRE1SIGFuYWx5c2lzDQoNCm92ZXJsYXBwaW5nLnJlc3VsdHMgPC0gIHJlYWRyOjpyZWFkX2NzdigNCiAgcGFzdGUwKGRpci5yZXN1bHQuY29tcCwgIm1ldGFfYW5hbHlzaXNfb3ZfY29tYl9wLmNzdiIpLA0KICBjb2xfdHlwZXMgPSByZWFkcjo6Y29scygpDQopDQpwcm9iZXMuaW4uYWxsLnJlZ2lvbnMgPC0gY29NZXRoRE1SOjpHZXRDcEdzSW5BbGxSZWdpb24oDQogIG92ZXJsYXBwaW5nLnJlc3VsdHMkaW5wdXRSZWdpb24NCikNCg0Kc2luZ2xlLmNwZy5yZXN1bHRzIDwtIHJlYWRyOjpyZWFkX2NzdigNCiAgcGFzdGUwKGRpci5kYXRhLCAibWV0YV9hbmFseXNpc19zaW5nbGVfY3BnX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmX2JhY29uLmNzdiIpLA0KICBjb2xfdHlwZXMgPSByZWFkcjo6Y29scygpDQogICkNCnNpbmdsZS5jcGcucmVzdWx0cy5wcm9iZXMgPC0gc2luZ2xlLmNwZy5yZXN1bHRzICU+JSBwdWxsKGNwZykgJT4lIGFzLmNoYXJhY3Rlcg0KDQpvdmVybGFwcGluZy5yZXN1bHRzJFByb2Jlc19zaW5nbGVfY3BnX2FuYWx5c2lzX2Zkcl8wXzA1IDwtIGxhcHBseShwcm9iZXMuaW4uYWxsLnJlZ2lvbnMsIGZ1bmN0aW9uKHgpew0KICBwYXN0ZSh4W3ggJWluJSBzaW5nbGUuY3BnLnJlc3VsdHMucHJvYmVzXSxjb2xsYXBzZSA9ICI7IikNCn0pICU+JSB1bmxpc3QNCg0Kd3JpdGUuY3N2KA0KICBvdmVybGFwcGluZy5yZXN1bHRzLA0KICBwYXN0ZTAoZGlyLmRhdGEsICJtZXRhX2FuYWx5c2lzX3NpZ19hZGRfY3Jvc3NIeWJfb3ZfY29tYl9wX3dpdGhfc2lnX3NpbmdsZV9jcGdzX2JhY29uLmNzdiIpLA0KICByb3cubmFtZXMgPSBGQUxTRQ0KKQ0KDQp3cml0ZS5jc3YoDQogIG92ZXJsYXBwaW5nLnJlc3VsdHMgJT4lIGRwbHlyOjpmaWx0ZXIoc21va2VfYmkgPT0gMCAmIGNyb3NzSHliX2JpID09IDApLA0KICBwYXN0ZTAoZGlyLmRhdGEsICJtZXRhX2FuYWx5c2lzX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX292X2NvbWJfcF93aXRoX3NpZ19zaW5nbGVfY3Bnc19iYWNvbi5jc3YiKSwNCiAgcm93Lm5hbWVzID0gRkFMU0UNCikNCmBgYA0KDQpgYGB7UiwgaW5jbHVkZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9DQpyZWFkcjo6cmVhZF9jc3YoDQogIHBhc3RlMChkaXIucmVzdWx0LmNwZy52cy5kbXIsICJtZXRhX2FuYWx5c2lzX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX292X2NvbWJfcF93aXRoX3NpZ19zaW5nbGVfY3Bnc19iYWNvbi5jc3YiKSwNCiAgY29sX3R5cGVzID0gcmVhZHI6OmNvbHMoKQ0KKQ0KYGBgDQoNCg0KYGBge1IsIGluY2x1ZGUgPSBGQUxTRSwgZXZhbCA9IEZBTFNFfQ0Kb3ZlcmxhcHBpbmcucmVzdWx0cyA8LSAgcmVhZHI6OnJlYWRfY3N2KA0KICAgIHBhc3RlMChkaXIucmVzdWx0LmNvbXAsICJtZXRhX2FuYWx5c2lzX25vX2Nyb3NzSHliX3Ntb2tpbmdfb3ZfY29tYl9wLmNzdiIpLA0KICAgIGNvbF90eXBlcyA9IHJlYWRyOjpjb2xzKCkNCikNCnByb2Jlcy5pbi5hbGwucmVnaW9ucyA8LSBjb01ldGhETVI6OkdldENwR3NJbkFsbFJlZ2lvbihvdmVybGFwcGluZy5yZXN1bHRzJGlucHV0UmVnaW9uKQ0KcHJvYmVzLmluLmFsbC5yZWdpb25zIDwtIHByb2Jlcy5pbi5hbGwucmVnaW9ucyAlPiUgdW5pcXVlICU+JSB1bmxpc3QgDQoNCnNpbmdsZS5jcGcucmVzdWx0cyA8LSByZWFkcjo6cmVhZF9jc3YoDQogIHBhc3RlMChkaXIuZGF0YSwibWV0YV9hbmFseXNpc19zaW5nbGVfY3BnX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmX2JhY29uLmNzdiIpLA0KICBjb2xfdHlwZXMgPSByZWFkcjo6Y29scygpDQopIA0KDQpzaW5nbGUuY3BnLnJlc3VsdHMucHJvYmVzIDwtIHNpbmdsZS5jcGcucmVzdWx0cyAlPiUgcHVsbChjcGcpICU+JSBhcy5jaGFyYWN0ZXIgDQoNCiMgbGlicmFyeQ0KbGlicmFyeShWZW5uRGlhZ3JhbSkNCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBNYWtlIHRoZSBwbG90DQp2ZW5uIDwtIHZlbm4uZGlhZ3JhbSgNCiAgeCA9IGxpc3QoDQogICAgcHJvYmVzLmluLmFsbC5yZWdpb25zICU+JSB1bmlxdWUgLCAgICANCiAgICBzaW5nbGUuY3BnLnJlc3VsdHMucHJvYmVzICU+JSB1bmlxdWUgIA0KICApLCANCiAgY2F0ZWdvcnkubmFtZXMgPSBjKCJETVIgYW5hbHlzaXMgcHJvYmVzIiAsICJTaW5nbGUgY3BnIGFuYWx5c2lzIHByb2JlcyIgKSwNCiAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoZGlyLmRhdGEsICcvdmVubl9ETVJfY3BnLnBuZycpLA0KICBvdXRwdXQgPSBUUlVFICwNCiAgaW1hZ2V0eXBlID0gInBuZyIgLA0KICBoZWlnaHQgPSA3MDAgLA0KICB3aWR0aCA9IDcwMCAsDQogIHJlc29sdXRpb24gPSAzMDAsDQogIGNvbXByZXNzaW9uID0gImx6dyIsDQogIGx3ZCA9IDEsDQogIGNvbCA9IGMoIiM0NDAxNTRmZiIsICcjMjE5MDhkZmYnKSwNCiAgZmlsbCA9IGMoYWxwaGEoIiM0NDAxNTRmZiIsMC4zKSwgYWxwaGEoJyMyMTkwOGRmZicsMC4zKSksDQogIGNleCA9IDAuNSwNCiAgY2F0LmNleCA9IDAuMywNCiAgY2F0LmRlZmF1bHQucG9zID0gIm91dGVyIiwNCiAgY2F0LnBvcyA9IGMoMCwgMTgwKSwNCiAgY2F0LmRpc3QgPSBjKDAuMDEsIDAuMDEpDQopDQpgYGANCg0KYGBge1Isc2hvdyA9IEZBTFNFLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0PSA1LCBpbmNsdWRlID0gRkFMU0UsIGV2YWwgPSBGQUxTRX0NCiB2ZW5uLmRpYWdyYW0oDQogIHggPSBsaXN0KA0KICAgIHByb2Jlcy5pbi5hbGwucmVnaW9ucyAlPiUgdW5pcXVlICwgICAgDQogICAgc2luZ2xlLmNwZy5yZXN1bHRzLnByb2JlcyAlPiUgdW5pcXVlICANCiAgKSwgDQogIGNhdGVnb3J5Lm5hbWVzID0gYygiRE1SIGFuYWx5c2lzIHByb2JlcyIgLCAiU2luZ2xlIGNwZyBhbmFseXNpcyBwcm9iZXMiICksDQogIGZpbGVuYW1lID0gTlVMTCwNCiAgb3V0cHV0ID0gVFJVRSAsDQogIGltYWdldHlwZSA9ICJwbmciICwNCiAgaGVpZ2h0ID0gNzAwICwNCiAgd2lkdGggPSA3MDAgLA0KICByZXNvbHV0aW9uID0gMzAwLA0KICBjb21wcmVzc2lvbiA9ICJsenciLA0KICBsd2QgPSAxLA0KICBjb2wgPSBjKCIjNDQwMTU0ZmYiLCAnIzIxOTA4ZGZmJyksDQogIGZpbGwgPSBjKGFscGhhKCIjNDQwMTU0ZmYiLDAuMyksIGFscGhhKCcjMjE5MDhkZmYnLDAuMykpLA0KICBjZXggPSAxLA0KICBjYXQuY2V4ID0gMSwNCiAgY2F0LmRlZmF1bHQucG9zID0gIm91dGVyIiwNCiAgY2F0LnBvcyA9IGMoMCwgMTgwKSwNCiAgY2F0LmRpc3QgPSBjKDAuMDEsIDAuMDEpDQopICU+JSBncmlkLmRyYXcoKQ0KYGBgDQoNCg0KIyBTZXNzaW9uIGluZm9ybWF0aW9uDQpgYGB7Un0NCmRldnRvb2xzOjpzZXNzaW9uX2luZm8oKQ0KYGBgDQo=