1 Main libraries and configuration

dir.result <- "./DATASETS/matched_analysis_results/"
dir.dmr <- "../meta_analysis_region_results/step4_dmr_vs_cpgs/" 
dir.single.cpg <- "../meta_analysis_single_cpg_results/"        
dir.combp <- "../../../Michael/matchedAnalysis_revision_7-13-2020/comb-p_7_15_2020/"
dir.revision <- "../../../DRAFT_REVISION_NatComm_6-2020/Individual files/"
dir.fig <- "./FIGURES"
library(dplyr)
library(tidyr)
library(ExperimentHub)
library(GenomicRanges)
library(e1071)
library(meta)

2 Matched analysis

devtools::source_gist("https://gist.github.com/tiagochst/d3a7b1639acf603916c315d23b1efb3e")
## Sourcing https://gist.githubusercontent.com/tiagochst/d3a7b1639acf603916c315d23b1efb3e/raw/a14424662da343c1301b7b2f03210d28d16ae05c/functions.R
## SHA-1 hash of file is ef6f39dc4e5eddb5ca1c6e5af321e75ff06e9362
files <- dir(
  path = "../DATASETS",
  pattern = paste0(".*neuro"),
  recursive = TRUE,
  full.names = TRUE,
  ignore.case = TRUE
)
files
## [1] "../DATASETS/GASPARONI/step8_neuron_comp/pheno_withNeuronProp_df.RDS"    
## [2] "../DATASETS/LONDON/step6_neuron_comp/pheno107_PFC_withNeuronProp_df.RDS"
## [3] "../DATASETS/MtSinai/step6_neuron_comp/pheno141_withNeuronProp_df.RDS"   
## [4] "../DATASETS/ROSMAP/step8_neuron_comp/pheno_PFC_withNeuronProp_df.RDS"
pheno.ls <- lapply(files, function (f){
  readRDS(f)
})
names(pheno.ls) <- files %>% dirname %>% dirname %>% basename
lapply(pheno.ls,dim)
## $GASPARONI
## [1] 56  8
## 
## $LONDON
## [1] 107   9
## 
## $MtSinai
## [1] 141   8
## 
## $ROSMAP
## [1] 726  28

2.1 Rosmap dataset

rosmap <- pheno.ls$ROSMAP[, c("Sample", "msex", "braaksc", "age_death")]
rosmap$age <- rosmap$age_death
rosmap$status <- ifelse(rosmap$braaksc < 3, "Control", "Case")
rosmap$status <- as.factor(rosmap$status)
rosmap$sex <- ifelse(rosmap$msex == 1, "Male", "Female")
rosmap$sex <- as.factor(rosmap$sex)
str(rosmap)
## 'data.frame':    726 obs. of  7 variables:
##  $ Sample   : chr  "PT-313T" "PT-3142" "PT-318X" "PT-35N6" ...
##  $ msex     : num  1 0 1 1 0 0 0 0 1 1 ...
##  $ braaksc  : num  1 5 4 6 3 3 3 4 4 3 ...
##  $ age_death: num  84.4 90 87.9 89 90 ...
##  $ age      : num  84.4 90 87.9 89 90 ...
##  $ status   : Factor w/ 2 levels "Case","Control": 2 1 1 1 1 1 1 1 1 1 ...
##  $ sex      : Factor w/ 2 levels "Female","Male": 2 1 2 2 1 1 1 1 2 2 ...
{
  set.seed(5) # matchControl uses a distance calculation, if ties, it uses sample function
  m_ros <- matchControls(
    formula = status ~ age + sex,
    data = rosmap,
    contlabel = "Case",
    caselabel = "Control"
  )
}

outFile <- data.frame(
  "matchedControls" = rosmap[m_ros$cases, "Sample"], 
  "matchedCases" = rosmap[m_ros$controls, "Sample"]
)

outFileControl <- merge(
  x = outFile, 
  y = rosmap[, c("Sample", "status", "age", "sex")],
  by.x = "matchedControls",
  by.y = "Sample"
)

colnames(outFileControl)[3:5] <- c("status_control", "age_control", "sex_control")

outFileControlCases <- merge(
  x = outFileControl, 
  y = rosmap[, c("Sample", "status", "age", "sex")],
  by.x = "matchedCases",
  by.y = "Sample"
)

colnames(outFileControlCases)[6:8] <- c("status_case", "age_case", "sex_case")

outFileControlCases <- outFileControlCases[, c(2, 3, 4, 5, 1, 6, 7, 8)]
write.csv(
  outFileControlCases,
  paste0(dir.result, "Rosmap_matchedCaseControls.csv"),
  row.names = FALSE
)
ROSMAP_matchAgeSex <-  outFileControlCases %>% 
  dplyr::filter(abs(age_control - age_case) <= 1)
dim(ROSMAP_matchAgeSex)
## [1] 122   8
table(ROSMAP_matchAgeSex$status_control, ROSMAP_matchAgeSex$sex_control)
##          
##           Female Male
##   Case         0    0
##   Control     71   51
table(ROSMAP_matchAgeSex$status_case, ROSMAP_matchAgeSex$sex_case)
##          
##           Female Male
##   Case        71   51
##   Control      0    0
ROSMAP_samples <- c(
  as.character(ROSMAP_matchAgeSex$matchedControls),
  as.character(ROSMAP_matchAgeSex$matchedCases)
)
saveRDS(
  ROSMAP_samples,
  paste0(dir.result, "ROSMAP_samples.RDS")
)

2.1.1 Samples

length(readRDS(paste0(dir.result, "ROSMAP_samples.RDS")))
## [1] 244

2.1.2 Linear model

### Import datasets
cohort <- "ROSMAP"
pheno <- "pheno"

info_df <- readRDS(
  dir(path = "../DATASETS/ROSMAP////", pattern = "info", recursive = TRUE, full.names = TRUE)
)
mediansMval_df <- readRDS(
  dir(path = "../DATASETS/ROSMAP/", pattern = "medians", recursive = TRUE, full.names = TRUE)
)
pheno_df <- readRDS(
  dir(path = "../DATASETS/ROSMAP/", pattern = "Neuron", recursive = TRUE, full.names = TRUE)
)
samples <- readRDS(
  paste0(dir.result, "ROSMAP_samples.RDS")
)

### Limit samples
mediansMval_df <- mediansMval_df[, samples]
pheno_df <- pheno_df[pheno_df$Sample %in% samples, ]

mediansMval_df <- mediansMval_df[, pheno_df$Sample]

identical(pheno_df$Sample, colnames(mediansMval_df))

str(pheno_df)

pheno_df$age_death <- as.numeric(as.character(pheno_df$age_death))
pheno_df$msex <- as.factor(as.character(pheno_df$msex))
pheno_df$Slide <- as.factor(as.character(pheno_df$Slide))
pheno_df$batch <- as.factor(as.character(pheno_df$batch))

predictors_char <- "braaksc"
covariates_char <- c("prop.neuron", "batch")

res_df <- TestAllRegions_noInfo(
  predictors_char = predictors_char,
  covariates_char = covariates_char,
  pheno_df = pheno_df,
  summarizedRegions_df = mediansMval_df,
  cores = 4
)

colnames(res_df) <- c(
  paste0(cohort, "_estimate"),
  paste0(cohort, "_se"),
  paste0(cohort, "_pVal"),
  paste0(cohort, "_fdr")
)

res_withInfo_df <- cbind(info_df, res_df)
saveRDS(
  res_withInfo_df,
  paste0(dir.result, cohort, "_matched_linear_df.rds")
)

2.1.3 Single cpg linear model

### Import datasets
beta_mat <- readRDS(
  "../DATASETS/ROSMAP/step7_pca_filtering/ROSMAP_QNBMIQ_PCfiltered.RDS"
) 
pheno_df <- readRDS(
  dir(path = "../DATASETS/ROSMAP/", pattern = "Neuron", recursive = TRUE, full.names = TRUE)
)
samples <- readRDS(
  paste0(dir.result, "ROSMAP_samples.RDS")
)

### Limit samples
beta_mat <- beta_mat[, samples]
pheno_df <- pheno_df[pheno_df$Sample %in% samples, ]

beta_mat <- beta_mat[, pheno_df$Sample]

identical(pheno_df$Sample, colnames(beta_mat))
## [1] TRUE
### Compute M values
mval_mat <- log2(beta_mat / (1 - beta_mat))

pheno_df$sex <- as.factor(pheno_df$sex)
pheno_df$slide <- as.factor(pheno_df$Sentrix_ID)
pheno_df$batch <- as.factor(pheno_df$batch)

str(pheno_df)
## 'data.frame':    244 obs. of  29 variables:
##  $ Sample                : chr  "PT-313T" "PT-318X" "PT-35PM" "PT-BY89" ...
##  $ Sentrix_ID            : num  5.82e+09 6.04e+09 5.82e+09 5.82e+09 5.82e+09 ...
##  $ Sentrix_Position      : chr  "R04C01" "R02C02" "R05C01" "R04C02" ...
##  $ Sample_Plate          : chr  "WG0001091-MSA4" "WG0003259-MSA4" "WG0001091-MSA4" "WG0001338-MSA4" ...
##  $ Sample_Well           : chr  "H08" "H01" "A03" "F12" ...
##  $ Sample_Group          : chr  "PT-313T" "PT-318X" "PT-35PM" "PT-BY89" ...
##  $ batch                 : Factor w/ 2 levels "0","1": 2 2 2 2 2 2 1 1 1 2 ...
##  $ Sentrix               : chr  "5822020006_R04C01" "6042324057_R02C02" "5822020007_R05C01" "5822020002_R04C02" ...
##  $ Slide                 : num  5.82e+09 6.04e+09 5.82e+09 5.82e+09 5.82e+09 ...
##  $ projid                : num  11464261 3713990 22209673 75990666 32705437 ...
##  $ Study                 : chr  "ROS" "MAP" "ROS" "MAP" ...
##  $ msex                  : num  1 1 0 1 1 1 0 0 1 1 ...
##  $ educ                  : num  16 12 18 12 16 13 16 16 14 13 ...
##  $ race                  : Factor w/ 4 levels "1","2","3","7": 1 1 2 1 1 1 1 1 1 1 ...
##  $ spanish               : Factor w/ 2 levels "1","2": 2 2 2 2 2 2 2 2 2 2 ...
##  $ apoe_genotype         : Factor w/ 6 levels "22","23","24",..: 2 4 5 5 4 4 4 4 2 4 ...
##  $ age_at_visit_max      : chr  "83.507186858316217" "87.668720054757017" "68.668035592060235" "87.463381245722104" ...
##  $ age_first_ad_dx       : chr  NA NA NA NA ...
##  $ age_death             : num  84.4 87.9 69.2 88 90 ...
##  $ cts_mmse30_first_ad_dx: num  NA NA NA NA NA NA NA NA NA NA ...
##  $ cts_mmse30_lv         : num  24 30 26 12 24 27 29 28 29 27 ...
##  $ pmi                   : num  4 4.33 8.5 7.58 13.47 ...
##  $ braaksc               : num  1 4 3 4 4 1 2 2 1 2 ...
##  $ ceradsc               : num  4 2 2 2 1 3 4 2 4 4 ...
##  $ cogdx                 : num  4 1 6 5 2 3 1 1 1 1 ...
##  $ stage3                : chr  "0-2" "3-4" "3-4" "3-4" ...
##  $ sex                   : Factor w/ 2 levels "Female","Male": 2 2 1 2 2 2 1 1 2 2 ...
##  $ prop.neuron           : num  0.458 0.395 0.41 0.401 0.569 ...
##  $ slide                 : Factor w/ 65 levels "5772325072","5772325089",..: 30 65 31 26 44 28 17 10 3 38 ...
is(pheno_df$braaksc,"numeric")
## [1] TRUE
is(pheno_df$prop.neuron,"numeric")
## [1] TRUE
predictors_char <- "braaksc"
covariates_char <- c("prop.neuron", "batch")

doParallel::registerDoParallel(cores = parallel::detectCores()/2)
devtools::source_gist("https://gist.github.com/tiagochst/d3a7b1639acf603916c315d23b1efb3e")

results_ordered_df <- plyr::adply(mval_mat,1, function(row){
  
  sumOneRegion_df <- as.data.frame(t(row))
  
  result <- TestSingleRegion(
    predictors_char = predictors_char,
    covariates_char = covariates_char,
    pheno_df = pheno_df,
    sumOneRegion_df = sumOneRegion_df
  )
  result
}, .progress = "time",.parallel = TRUE,.id = "cpg")
colnames(results_ordered_df)[1] <- "cpg"

identical(row.names(mval_mat), results_ordered_df$cpg %>% as.character())

write.csv(
  results_ordered_df,
  paste0(dir.result, "ROSMAP_matched_single_cpg_linear_df.csv"),
  row.names = FALSE
)
results_ordered_df <- readr::read_csv(
  paste0(dir.result, "ROSMAP_matched_single_cpg_linear_df.csv"),
  col_types = readr::cols()
)
dim(results_ordered_df)
## [1] 431803      4
results_ordered_df %>% head

2.2 London dataset

london <- pheno.ls$LONDON[, c("sample", "sex", "stage", "age.brain")]
london$status <- ifelse(london$stage < 3, "Control", "Case")
london$status <- as.factor(london$status)
london$sex <- as.factor(london$sex)
str(london)

{
  set.seed(5)
  m_london <- matchControls(
    formula = status ~ age.brain + sex,
    data = london,
    contlabel = "Case",
    caselabel = "Control"
  )
}
outFile <-  data.frame(
  "matchedControls" = london[m_london$cases, "sample"],
  "matchedCases" = london[m_london$controls, "sample"]
)

outFileControl <- merge(
  x = outFile,
  y = london[, c("sample", "status", "age.brain", "sex")],
  by.x = "matchedControls",
  by.y = "sample"
)
colnames(outFileControl)[3:5] <- c("status_control", "age_control", "sex_control")

outFileControlCases <- merge(
  x = outFileControl,
  y = london[, c("sample", "status", "age.brain", "sex")],
  by.x = "matchedCases",
  by.y = "sample"
)
colnames(outFileControlCases)[6:8] <- c("status_case", "age_case", "sex_case")
outFileControlCases <- outFileControlCases[, c(2, 3, 4, 5, 1, 6, 7, 8)]

write.csv(
  outFileControlCases,
  paste0(dir.result, "London_matchedCaseControls.csv"),
  row.names = FALSE
)

London_matchAgeSex <- outFileControlCases %>% 
  dplyr::filter(abs(age_control - age_case) <= 1)

dim(London_matchAgeSex)

table(London_matchAgeSex$status_control, London_matchAgeSex$sex_control)
table(London_matchAgeSex$status_case, London_matchAgeSex$sex_case)
London_samples <- c(
  as.character(London_matchAgeSex$matchedControls),
  as.character(London_matchAgeSex$matchedCases)
)
saveRDS(
  London_samples,
  paste0(dir.result, "London_samples.RDS")
)

2.2.1 Samples

length(readRDS(paste0(dir.result, "London_samples.RDS")))
## [1] 46

2.2.2 Linear model

### Import datasets
cohort <- "London"
pheno <- "pheno"

info_df <- readRDS(
  dir(path = "../DATASETS/LONDON///", pattern = "info", recursive = TRUE, full.names = TRUE)
)
mediansMval_df <- readRDS(
  dir(path = "../DATASETS/LONDON/", pattern = "medians", recursive = TRUE, full.names = TRUE)
)
pheno_df <- readRDS(
  dir(path = "../DATASETS/LONDON/", pattern = "NeuronProp_df", recursive = TRUE, full.names = TRUE)
)
samples <- readRDS(
  paste0(dir.result, "London_samples.RDS")
)

### Limit samples
mediansMval_df <- mediansMval_df[, samples]
pheno_df <- pheno_df[pheno_df$sample %in% samples, ]

mediansMval_df <- mediansMval_df[, pheno_df$sample]

### Check variables before fitting model
pheno_df$Sample <- pheno_df$sample

identical(pheno_df$Sample, colnames(mediansMval_df))
## [1] TRUE
str(pheno_df)
## 'data.frame':    46 obs. of  10 variables:
##  $ sample     : chr  "GSM1443251" "GSM1443256" "GSM1443263" "GSM1443269" ...
##  $ subject.id : num  1 2 4 5 6 7 9 10 13 18 ...
##  $ sentrix_id : chr  "6042316048_R05C01" "6042316066_R05C01" "7786923107_R02C01" "6042316121_R04C02" ...
##  $ slide      : chr  "6042316048" "6042316066" "7786923107" "6042316121" ...
##  $ age.brain  : num  82 82 81 92 78 86 90 82 80 82 ...
##  $ sex        : chr  "FEMALE" "FEMALE" "FEMALE" "FEMALE" ...
##  $ stage      : num  0 2 1 2 1 3 6 6 5 6 ...
##  $ stage3     : chr  "0-2" "0-2" "0-2" "0-2" ...
##  $ prop.neuron: num  0.377 0.226 0.206 0.456 0.424 ...
##  $ Sample     : chr  "GSM1443251" "GSM1443256" "GSM1443263" "GSM1443269" ...
pheno_df$sex <- as.factor(pheno_df$sex)
pheno_df$slide <- as.factor(pheno_df$slide)
# If rosmap cohort, don't forget batch effect
devtools::source_gist("https://gist.github.com/tiagochst/d3a7b1639acf603916c315d23b1efb3e")
predictors_char <- "stage"
covariates_char <- "prop.neuron"

res_df <- TestAllRegions_noInfo(
  predictors_char = predictors_char,
  covariates_char = covariates_char,
  pheno_df = pheno_df,
  summarizedRegions_df = mediansMval_df,
  cores = 4
)

colnames(res_df) <- c(
  paste0(cohort, "_estimate"),
  paste0(cohort, "_se"),
  paste0(cohort, "_pVal"),
  paste0(cohort, "_fdr")
)

res_withInfo_df <- cbind(info_df, res_df)
saveRDS(
  res_withInfo_df,
  paste0(dir.result, cohort, "_matched_linear_df.rds")
)

2.2.3 Single cpg linear model

### Import datasets
beta_mat <- readRDS(
  "../DATASETS/LONDON/step5_pca_filtering/London_PFC_QNBMIQ_PCfiltered.RDS"
) 
pheno_df <- readRDS(
  dir("../DATASETS/LONDON/",pattern = "NeuronProp_df",recursive = TRUE,full.names = TRUE)
)
samples <- readRDS(
  paste0(dir.result, "London_samples.RDS")
)

### Limit samples
pheno_df$Sample <- pheno_df$sample

beta_mat <- beta_mat[, samples]
pheno_df <- pheno_df[pheno_df$Sample %in% samples, ]

beta_mat <- beta_mat[, pheno_df$Sample]

identical(pheno_df$Sample, colnames(beta_mat))
## [1] TRUE
### Compute M values
mval_mat <- log2(beta_mat / (1 - beta_mat))

pheno_df$sex <- as.factor(pheno_df$sex)
pheno_df$slide <- as.factor(pheno_df$slide)

str(pheno_df)
## 'data.frame':    46 obs. of  10 variables:
##  $ sample     : chr  "GSM1443251" "GSM1443256" "GSM1443263" "GSM1443269" ...
##  $ subject.id : num  1 2 4 5 6 7 9 10 13 18 ...
##  $ sentrix_id : chr  "6042316048_R05C01" "6042316066_R05C01" "7786923107_R02C01" "6042316121_R04C02" ...
##  $ slide      : Factor w/ 12 levels "6042316035","6042316048",..: 2 3 9 8 6 1 5 11 7 2 ...
##  $ age.brain  : num  82 82 81 92 78 86 90 82 80 82 ...
##  $ sex        : Factor w/ 2 levels "FEMALE","MALE": 1 1 1 1 2 2 1 2 1 1 ...
##  $ stage      : num  0 2 1 2 1 3 6 6 5 6 ...
##  $ stage3     : chr  "0-2" "0-2" "0-2" "0-2" ...
##  $ prop.neuron: num  0.377 0.226 0.206 0.456 0.424 ...
##  $ Sample     : chr  "GSM1443251" "GSM1443256" "GSM1443263" "GSM1443269" ...
is(pheno_df$stage,"numeric")
## [1] TRUE
is(pheno_df$prop.neuron,"numeric")
## [1] TRUE
predictors_char <- "stage"
covariates_char <- "prop.neuron"

doParallel::registerDoParallel(cores = parallel::detectCores()/2)
devtools::source_gist("https://gist.github.com/tiagochst/d3a7b1639acf603916c315d23b1efb3e")

results_ordered_df <- plyr::adply(mval_mat,1, function(row){
  
  sumOneRegion_df <- as.data.frame(t(row))
  
  result <- TestSingleRegion(
    predictors_char = predictors_char,
    covariates_char = covariates_char,
    pheno_df = pheno_df,
    sumOneRegion_df = sumOneRegion_df
  )
  result
}, .progress = "time", .parallel = TRUE, .id = "cpg")
colnames(results_ordered_df)[1] <- "cpg"

identical(row.names(mval_mat), results_ordered_df$cpg %>% as.character())
write.csv(
  results_ordered_df,
  paste0(dir.result, "London_matched_single_cpg_linear_df.csv"),
  row.names = FALSE
)
results_ordered_df <- readr::read_csv(
  paste0(dir.result, "London_matched_single_cpg_linear_df.csv"),
  col_types = readr::cols()
)
dim(results_ordered_df)
## [1] 450793      4
results_ordered_df %>% head

2.3 Gasparoni dataset

write.csv(
  outFileControlCases,
  paste0(dir.result, "Gasparoni_matchedCaseControls.csv"),
  row.names = FALSE
)
Gasparoni_matchAgeSex <-  outFileControlCases %>% 
  dplyr::filter(abs(age_control - age_case) <= 1)
dim(Gasparoni_matchAgeSex)

table(Gasparoni_matchAgeSex$status_control, Gasparoni_matchAgeSex$sex_control)
table(Gasparoni_matchAgeSex$status_case, Gasparoni_matchAgeSex$sex_case)

Gasparoni_samples <- c(
  as.character(Gasparoni_matchAgeSex$matchedControls),
  as.character(Gasparoni_matchAgeSex$matchedCases)
)
saveRDS(
  Gasparoni_samples,
  paste0(dir.result, "Gasparoni_samples.RDS")
)

2.4 Mt.Sinai dataset

mtsinai <- pheno.ls$MtSinai[, c("sample", "sex", "stage", "age.brain")]
mtsinai$status <- ifelse(mtsinai$stage < 3, "Control", "Case")
mtsinai$status <- as.factor(mtsinai$status)
mtsinai$sex <- as.factor(mtsinai$sex)
str(mtsinai)

{
  set.seed(5)
  m_mtsinai <- matchControls(
    formula = status ~ age.brain + sex,
    data = mtsinai,
    contlabel = "Case",
    caselabel = "Control"
  )
}
outFile <- data.frame(
  "matchedControls" = mtsinai[m_mtsinai$cases, "sample"], 
  "matchedCases" = mtsinai[m_mtsinai$controls, "sample"]
)

outFileControl <- merge(
  outFile, 
  mtsinai[, c("sample", "status", "age.brain", "sex")], 
  by.x = "matchedControls", 
  by.y = "sample"
)

colnames(outFileControl)[3:5] <- c("status_control", "age_control", "sex_control")
outFileControlCases <-  merge(
  outFileControl, 
  mtsinai[, c("sample", "status", "age.brain", "sex")], 
  by.x = "matchedCases", 
  by.y = "sample"
)
colnames(outFileControlCases)[6:8] <- c("status_case", "age_case", "sex_case")
outFileControlCases <- outFileControlCases[, c(2, 3, 4, 5, 1, 6, 7, 8)]
write.csv(
  outFileControlCases,
  paste0(dir.result, "MtSinai_matchedCaseControls.csv"),
  row.names = FALSE
)
MtSinai_matchAgeSex <-  outFileControlCases %>% 
  dplyr::filter(abs(age_control - age_case) <= 1)
dim(MtSinai_matchAgeSex)

table(MtSinai_matchAgeSex$status_control, MtSinai_matchAgeSex$sex_control)
table(MtSinai_matchAgeSex$status_case, MtSinai_matchAgeSex$sex_case)

MtSinai_samples <- c(
  as.character(MtSinai_matchAgeSex$matchedControls),
  as.character(MtSinai_matchAgeSex$matchedCases)
)
saveRDS(
  MtSinai_samples,
  paste0(dir.result, "MtSinai_samples.RDS")
)

2.4.1 Samples

length(readRDS(paste0(dir.result, "MtSinai_samples.RDS")))
## [1] 56

2.4.2 Linear model

### Import datasets
cohort <- "MtSinai"
pheno <- "pheno"

info_df <- readRDS(
  dir("../DATASETS/MtSinai//",pattern = "info",recursive = TRUE,full.names = TRUE)
)
mediansMval_df <- readRDS(
  dir("../DATASETS/MtSinai/",pattern = "medians",recursive = TRUE,full.names = TRUE)
)
pheno_df <- readRDS(
  dir("../DATASETS/MtSinai/",pattern = "NeuronProp",recursive = TRUE,full.names = TRUE)
)
samples <- readRDS(
  paste0(dir.result, "MtSinai_samples.RDS")
)

### Limit samples
mediansMval_df <- mediansMval_df[, samples]
pheno_df <- pheno_df[pheno_df$sample %in% samples, ]

mediansMval_df <- mediansMval_df[, pheno_df$sample]

### Check variables before fitting model
pheno_df$Sample <- pheno_df$sample

identical(pheno_df$Sample, colnames(mediansMval_df))
## [1] TRUE
str(pheno_df)
## 'data.frame':    56 obs. of  9 variables:
##  $ sample     : chr  "GSM2139163" "GSM2139164" "GSM2139166" "GSM2139170" ...
##  $ subject.id : chr  "MS66" "MS546" "MS1549" "MS842" ...
##  $ sentrix_id : chr  "7796806144_R01C01" "7796806144_R01C02" "7796806144_R03C01" "7796806144_R05C01" ...
##  $ slide      : chr  "7796806144" "7796806144" "7796806144" "7796806144" ...
##  $ age.brain  : num  89 80 78 83 78 73 81 93 93 73 ...
##  $ sex        : chr  "male" "female" "male" "female" ...
##  $ stage      : num  5 2 4 1 2 6 2 6 2 6 ...
##  $ prop.neuron: num  0.243 0.202 0.304 0.329 0.235 ...
##  $ Sample     : chr  "GSM2139163" "GSM2139164" "GSM2139166" "GSM2139170" ...
pheno_df$sex <- as.factor(pheno_df$sex)
pheno_df$slide <- as.factor(pheno_df$slide)
# If rosmap cohort, don't forget batch effect
devtools::source_gist("https://gist.github.com/tiagochst/d3a7b1639acf603916c315d23b1efb3e")
predictors_char <- "stage"
covariates_char <- "prop.neuron"

res_df <- TestAllRegions_noInfo(
  predictors_char = predictors_char,
  covariates_char = covariates_char,
  pheno_df = pheno_df,
  summarizedRegions_df = mediansMval_df,
  cores = 4
)

colnames(res_df) <- c(
  paste0(cohort, "_estimate"),
  paste0(cohort, "_se"),
  paste0(cohort, "_pVal"),
  paste0(cohort, "_fdr")
)

res_withInfo_df <- cbind(info_df, res_df)
saveRDS(
  res_withInfo_df,
  paste0(dir.result, cohort, "_matched_linear_df.rds")
)

2.4.3 Single cpg linear model

### Import datasets
beta_mat <- readRDS(
  "../DATASETS/MtSinai/step5_pca_filtering/MtSinai_QNBMIQ_PCfiltered.RDS"
) 
pheno_df <- readRDS(
  dir("../DATASETS/MtSinai/",pattern = "Neuron",recursive = TRUE,full.names = TRUE)
)
samples <- readRDS(
  paste0(dir.result, "MtSinai_samples.RDS")
)

### Limit samples
pheno_df$Sample <- pheno_df$sample

beta_mat <- beta_mat[, samples]
pheno_df <- pheno_df[pheno_df$Sample %in% samples, ]

beta_mat <- beta_mat[, pheno_df$Sample]

identical(pheno_df$Sample, colnames(beta_mat))
## [1] TRUE
### Compute M values
mval_mat <- log2(beta_mat / (1 - beta_mat))

pheno_df$sex <- as.factor(pheno_df$sex)
pheno_df$slide <- as.factor(pheno_df$slide)

str(pheno_df)
## 'data.frame':    56 obs. of  9 variables:
##  $ sample     : chr  "GSM2139163" "GSM2139164" "GSM2139166" "GSM2139170" ...
##  $ subject.id : chr  "MS66" "MS546" "MS1549" "MS842" ...
##  $ sentrix_id : chr  "7796806144_R01C01" "7796806144_R01C02" "7796806144_R03C01" "7796806144_R05C01" ...
##  $ slide      : Factor w/ 12 levels "7796806144","7796814008",..: 1 1 1 1 1 2 2 2 2 2 ...
##  $ age.brain  : num  89 80 78 83 78 73 81 93 93 73 ...
##  $ sex        : Factor w/ 2 levels "female","male": 2 1 2 1 2 2 2 1 1 1 ...
##  $ stage      : num  5 2 4 1 2 6 2 6 2 6 ...
##  $ prop.neuron: num  0.243 0.202 0.304 0.329 0.235 ...
##  $ Sample     : chr  "GSM2139163" "GSM2139164" "GSM2139166" "GSM2139170" ...
is(pheno_df$stage,"numeric")
## [1] TRUE
is(pheno_df$prop.neuron,"numeric")
## [1] TRUE
predictors_char <- "stage"
covariates_char <- "prop.neuron"

doParallel::registerDoParallel(cores = parallel::detectCores()/2)
devtools::source_gist("https://gist.github.com/tiagochst/d3a7b1639acf603916c315d23b1efb3e")

results_ordered_df <- plyr::adply(mval_mat,1, function(row){
  
  sumOneRegion_df <- as.data.frame(t(row))
  
  result <- TestSingleRegion(
    predictors_char = predictors_char,
    covariates_char = covariates_char,
    pheno_df = pheno_df,
    sumOneRegion_df = sumOneRegion_df
  )
  result
}, .progress = "time",.parallel = TRUE,.id = "cpg")
colnames(results_ordered_df)[1] <- "cpg"

identical(row.names(mval_mat), results_ordered_df$cpg %>% as.character())
write.csv(
  results_ordered_df,
  paste0(dir.result, "MtSinai_matched_single_cpg_linear_df.csv"),
  row.names = FALSE
)
results_ordered_df <- readr::read_csv(
  paste0(dir.result, "MtSinai_matched_single_cpg_linear_df.csv"),
  col_types = readr::cols()
)
dim(results_ordered_df)
## [1] 434610      4
results_ordered_df %>% head

3 Meta-analysis of Genomic Regions

3.1 Import datasets and pre-process for each cohort

preMeta <- function(cohort){
  
  ### Load data
  file <- dir(
    path = "./DATASETS/matched_analysis_results",
    pattern = paste0(cohort,".*matched_linear_df"),
    recursive = T,
    full.names = TRUE,
    ignore.case = T
  )
  linear.results.final <- readRDS(file)
  
  ### select the most sig cometh region for each input region
  pva.col <- grep("_pVal",colnames(linear.results.final),value = TRUE)
  colnames(linear.results.final)[grep("inputRegion",colnames(linear.results.final))] <- "inputRegion"
  
  # !! is used to unquote an input 
  # https://dplyr.tidyverse.org/articles/programming.html
  result_sig <- linear.results.final %>%
    group_by(inputRegion) %>%
    filter((!!as.symbol(pva.col)) == min(!!as.symbol(pva.col)))
  
  data.frame(result_sig, stringsAsFactors = FALSE)
}

London_PFC <- preMeta(cohort = "London")
dim(London_PFC)

MtSinai <- preMeta(cohort = "MtSinai") 
dim(MtSinai)

ROSMAP <- preMeta(cohort = "ROSMAP")
dim(ROSMAP)

3.2 Merge cohorts

### merge datasets
cohort_ls <- list(
  London_PFC = London_PFC,
  MtSinai = MtSinai,
  ROSMAP = ROSMAP
)

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

3.3 Meta analysis

doParallel::registerDoParallel(cores = parallel::detectCores()/2)
meta_df <- plyr::adply(
  .data = multi_cohorts, 
  .margins = 1, 
  .fun =  function(rowOne_df){
    
    est <- rowOne_df[grep("estimate",colnames(rowOne_df))] %>% as.numeric
    
    direction <-  paste(ifelse(
      is.na(est), ".",
      ifelse(est > 0, "+", "-")
    ),collapse = "")
    
    se <- rowOne_df[grep("se",colnames(rowOne_df))] %>% as.numeric
    cohort <- gsub("_se","",grep("se",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(
      inputRegion = rowOne_df$inputRegion,
      estimate = f$TE.fixed,
      se = f$seTE.fixed,
      pVal.fixed = f$pval.fixed,
      pVal.random = f$pval.random,
      pValQ = f$pval.Q,
      direction = direction
    )
  }  , .progress = "time",
  .parallel = TRUE,
  .id = NULL
)

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

### calculate fdr
meta_df$fdr <- p.adjust(meta_df$pVal.final, 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),]

3.4 Add annotation to input regions

### find annotations
library(coMethDMR)

splited_input <- meta_final_ordered_df %>% 
  tidyr::separate(col = inputRegion,into =  c("chrom", "start", "end"),remove = FALSE)
splited_input_annot <- AnnotateResults(splited_input[,c("chrom", "start", "end")],nCores_int = 10) 

### merge annotation with meta analysis data
meta_ordered_withAnnot_df <- cbind(
  meta_final_ordered_df, splited_input_annot[, 4:7]
)

### order columns
meta_ordered_withAnnot_df <- meta_ordered_withAnnot_df %>% 
  dplyr::select(c(1,
                  (ncol(meta_ordered_withAnnot_df) - 3):ncol(meta_ordered_withAnnot_df),
                  2:(ncol(meta_ordered_withAnnot_df) - 4))
  )
### save dataset
write.csv(
  meta_ordered_withAnnot_df,
  paste0(dir.result, "meta_analysis_matched_df.csv"),
  row.names = FALSE
)

meta_all_sig <- meta_ordered_withAnnot_df[
  !is.na(meta_ordered_withAnnot_df$fdr) &
    (meta_ordered_withAnnot_df$fdr < 0.05),  
  ] #dim: 36 34
row.names(meta_all_sig) <- NULL
write.csv(
  meta_all_sig,
  paste0(dir.result, "meta_analysis_matched_sig_df.csv"),
  row.names = FALSE
)

3.5 Delete sig DMRs with cross-hybridizing or smoking probes

### call in all cross hybridizing probes
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") 
length(smoking.sig.probes)

### Call in meta analysis final results
meta_all <- read.csv(
  paste0(dir.result, "meta_analysis_matched_sig_df.csv")
) #dim: 36 34

### Find files with regions and probes
files <- dir(
  path = "../DATASETS",
  pattern = paste0(".*_residuals_cometh_input_ls.rds"),
  recursive = T,
  full.names = TRUE,
  ignore.case = TRUE
)
files <- grep("LONDON_PFC|MtSinai|ROSMAP",files,value = TRUE,ignore.case = TRUE)

### Read files and Limit the cohort_ls to cohort_coMethRegion in meta_all 
cometh.probes.list <- lapply(files, function(f){
  print(f)
  all.clusters <- readRDS(f)$coMeth_ls # Read files
  cohort <- f %>% dirname %>% dirname %>% basename # get cohort from folder name
  
  col <- grep(paste0(cohort,"_coMethRegion"),
              colnames(meta_all),
              ignore.case = TRUE,
              value = TRUE) # get column with cohort sig regions
  
  # keep sig regions only
  all.clusters[names(all.clusters) %in% meta_all[[col]]]
})
names(cometh.probes.list) <-  files %>% dirname %>% dirname %>% basename

lapply(cometh.probes.list,length)

### Map probes in each region to smoking and crosshybrdizing
extractCrosHybridization <- function(probes.list){
  crosshyb.extracted <- plyr::laply(probes.list,function(probes){
    paste(intersect(probes, crosshyb), 
          collapse = ", "
    )
  })
  smoking.extracted <- plyr::laply(probes.list,function(probes){
    paste(intersect(probes, smoking.sig.probes), 
          collapse = ", "
    )
  })
  tibble::tibble(
    "coMethRegion" = names(probes.list),
    "crossHyb" = crosshyb.extracted, 
    "crossHyb_bi" = ifelse(crosshyb.extracted == "",0,1),
    "smoke" = smoking.extracted,
    "smoke_bi" = ifelse(smoking.extracted == "",0,1)
  )
}

London_PFC_crossHyb_df <- extractCrosHybridization(cometh.probes.list$LONDON)
colnames(London_PFC_crossHyb_df) <- paste0("London_",colnames(London_PFC_crossHyb_df))
plyr::count(
  London_PFC_crossHyb_df, 
  vars = grep("_bi",colnames(London_PFC_crossHyb_df),value = TRUE)
)

MtSinai_crossHyb_df <- extractCrosHybridization(cometh.probes.list$MtSinai)
colnames(MtSinai_crossHyb_df) <- paste0("MtSinai_",colnames(MtSinai_crossHyb_df))
plyr::count(
  MtSinai_crossHyb_df, 
  vars = grep("_bi",colnames(MtSinai_crossHyb_df),value = TRUE)
)

ROSMAP_crossHyb_df <- extractCrosHybridization(cometh.probes.list$ROSMAP)
colnames(ROSMAP_crossHyb_df) <- paste0("ROSMAP_",colnames(ROSMAP_crossHyb_df))
plyr::count(ROSMAP_crossHyb_df, vars = grep("_bi",colnames(ROSMAP_crossHyb_df),value = TRUE))

### Merge smoking and crossHyb probes information with meta analysis results 
meta_all_final <- meta_all %>%
  left_join(London_PFC_crossHyb_df) %>% 
  left_join(MtSinai_crossHyb_df) %>% 
  left_join(ROSMAP_crossHyb_df)

### Add information with input regions with any cross hybridization in cohorts 
meta_all_final$crossHyb_bi <- rowSums(meta_all_final[,grep("crossHyb_bi",colnames(meta_all_final))],na.rm = TRUE) > 0
meta_all_final$smoke_bi <- rowSums(meta_all_final[,grep("smoke_bi",colnames(meta_all_final))],na.rm = TRUE) > 0

# Sort by region meta analysis FDR
# Cluster columns of the projects together
meta_all_final <- meta_all_final[
  order(meta_all_final$fdr),
  c(grep("MtSinai|London|ROSMAP",colnames(meta_all_final),ignore.case = TRUE,invert = TRUE),
    grep("Gasparoni",colnames(meta_all_final),ignore.case = TRUE),
    grep("MtSinai",colnames(meta_all_final),ignore.case = TRUE),
    grep("London",colnames(meta_all_final),ignore.case = TRUE),
    grep("ROSMAP",colnames(meta_all_final),ignore.case = TRUE)
  )
 ]
str(meta_all_final)

meta_sig_final <- meta_all_final[
  meta_all_final$crossHyb_bi == 0 &
    meta_all_final$smoke_bi == 0,
] #dim: 32 48
### Save
write.csv(
  meta_sig_final %>% as.data.frame,
  paste0(dir.result, "meta_analysis_matched_sig_no_crossHyb_smoking_df.csv"),
  row.names = FALSE
)

3.6 Results

4 Meta-analysis Single CpGs

  1. merge cohorts
  2. meta analysis
  3. add annotation
  4. Delete cross-hybridizing and smoking probes from sig probes

4.1 Import datasets

results.files <- dir(
  path = "./DATASETS/matched_analysis_results/",
  pattern = "single_cpg_linear_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(basename(i),"\\/|\\_"))[1]  %>% as.character()
  aux <- paste0(dataset,c("_estimate", "_se", "_pValue"))
  colnames(data) <- c("cpg", aux)
  assign(dataset,data)
}

4.2 Merge cohorts

cohort_ls <- list(
  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: 450793 10
dim(multi_cohorts)

4.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",colnames(rowOne_df))] %>% as.numeric
    
    direction <-  paste(ifelse(
      is.na(est), ".",
      ifelse(est > 0, "+", "-")
    ),collapse = "")
    
    se <- rowOne_df[grep("se",colnames(rowOne_df))] %>% as.numeric
    cohort <- gsub("_se","",grep("se",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 = f$TE.fixed,
      se = f$seTE.fixed,
      pVal.fixed = f$pval.fixed,
      pVal.random = f$pval.random,
      pValQ = f$pval.Q,
      direction = direction
    )
  }  , .progress = "time",
  .parallel = TRUE,
  .id = NULL
)

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

### calculate fdr
meta_df$fdr <- p.adjust(meta_df$pVal.final, 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),]

4.4 Add annotation

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
)
### final raw data
write.csv(
  result_annot_df  %>% as.data.frame(),
  paste0(dir.result, "meta_analysis_matched_single_cpg_df.csv"),
  row.names = FALSE
)
### prepare data for comb-p
result_for_combp_df <- result_annot_df[
  , c("seqnames", "start", "end", "pVal.final")
  ]
colnames(result_for_combp_df)[c(1,4)] <- c("chr", "pValue")
result_for_combp_df$chr <- as.character(result_for_combp_df$chr)
write.csv(
  result_for_combp_df,
  paste0(dir.result, "meta_analysis_matched_single_cpg_df_for_combp.csv"),
  row.names = FALSE
)

4.5 Delete cross-hybridizing and smoking probes from sig probes

### Exclude non-significant probes
result_annot_sig_df <- result_annot_df %>% filter(fdr < 0.05) 
write.csv(
  result_annot_sig_df %>% as.data.frame(),
  paste0(dir.result, "meta_analysis_matched_single_cpg_sig_df.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$Row.names <- as.character(result_annot_sig_df$Row.names)

result_annot_sig_no_crossHyb_smoking_df <- result_annot_sig_df[
  !((result_annot_sig_df$Row.names %in% crosshyb) |
      (result_annot_sig_df$Row.names %in% smoking.sig.probes)),
  ] #dim: 642 24

### 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, 23:26, 5:21)
]
write.csv(
  result_final_ordered %>% as.data.frame(),
  paste0(dir.result, "meta_analysis_matched_single_cpg_sig_no_crossHyb_smoking_df.csv"),
  row.names = FALSE
)           

4.6 Results

5 Compare matched-analysis results with main meta-analysis results

sig_cpgs <- readxl::read_xlsx(
    path = dir(dir.revision,pattern = "ALL",full.names = TRUE),
    sheet = "Supp Table 1",
    skip = 3
)

sig_dmrs <- readxl::read_xlsx(
    path = dir(dir.revision,pattern = "ALL",full.names = TRUE),
    sheet = "Supp Table 2",
    skip = 3
)

matched_cpgs <- read.csv(
  paste0(dir.result, "meta_analysis_matched_single_cpg_sig_no_crossHyb_smoking_df.csv")
)

matched_dmrs <- read.csv(
  paste0(dir.result, "meta_analysis_matched_sig_no_crossHyb_smoking_df.csv")
)
sig_cpgs <- sig_cpgs[, 1:17]
matched_cpgs <- matched_cpgs[, c(1, 9:16)]
colnames(matched_cpgs) <- c(
    "cpg", paste0("matched_", colnames(matched_cpgs)[2:ncol(matched_cpgs)])
)
sig_matched_cpgs <- merge(
    sig_cpgs, 
    matched_cpgs,
    by = "cpg",
    sort = FALSE
)
nrow(sig_matched_cpgs)
## [1] 129
meta_cpg_direction <- ifelse(
    sig_matched_cpgs$estimate > 0, "+", "-"
)
matched_cpg_direction <- ifelse(
    sig_matched_cpgs$matched_estimate > 0, "+", "-"
)
table(meta_cpg_direction == matched_cpg_direction)
## 
## TRUE 
##  129
sig_dmrs <- sig_dmrs[, 1:15]
matched_dmrs <- matched_dmrs[, c(1, 6:13)]
colnames(matched_dmrs) <- c(
    "DMR", paste0("matched_", colnames(matched_dmrs)[2:ncol(matched_dmrs)])
)
sig_matched_dmrs <- merge(
    sig_dmrs, matched_dmrs,
    by = "DMR",
    sort = FALSE
)
nrow(sig_matched_dmrs)
## [1] 16
meta_dmr_direction <- ifelse(
    sig_matched_dmrs$estimate > 0, "+", "-"
)
matched_dmr_direction <- ifelse(
    sig_matched_dmrs$matched_estimate > 0, "+", "-"
)
table(meta_dmr_direction == matched_dmr_direction)
## 
## TRUE 
##   16
write.csv(
   sig_matched_cpgs,
   paste0(dir.result, "meta_matched_common_cpgs.csv"),
   row.names = FALSE
)

write.csv(
   sig_matched_dmrs,
   paste0(dir.result, "meta_matched_common_dmrs.csv"),
   row.names = FALSE
)

6 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        
##  AnnotationDbi            1.50.3   2020-07-25 [1] Bioconductor  
##  AnnotationHub          * 2.20.0   2020-04-27 [1] Bioconductor  
##  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)
##  Biobase                  2.48.0   2020-04-27 [1] Bioconductor  
##  BiocFileCache          * 1.12.0   2020-04-27 [1] Bioconductor  
##  BiocGenerics           * 0.34.0   2020-04-27 [1] Bioconductor  
##  BiocManager              1.30.10  2019-11-16 [1] CRAN (R 4.0.2)
##  BiocVersion              3.11.1   2020-04-07 [1] Bioconductor  
##  bit                      4.0.3    2020-07-30 [1] CRAN (R 4.0.2)
##  bit64                    4.0.2    2020-07-30 [1] CRAN (R 4.0.2)
##  bitops                   1.0-6    2013-08-17 [1] CRAN (R 4.0.2)
##  blob                     1.2.1    2020-01-20 [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)
##  cellranger               1.1.0    2016-07-27 [1] CRAN (R 4.0.2)
##  class                    7.3-17   2020-04-26 [1] CRAN (R 4.0.2)
##  cli                      2.0.2    2020-02-28 [1] CRAN (R 4.0.2)
##  cluster                  2.1.0    2019-06-19 [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)
##  curl                     4.3      2019-12-02 [1] CRAN (R 4.0.1)
##  DBI                      1.1.0    2019-12-15 [1] CRAN (R 4.0.2)
##  dbplyr                 * 1.4.4    2020-05-27 [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)
##  e1071                  * 1.7-3    2019-11-26 [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)
##  ExperimentHub          * 1.14.0   2020-04-27 [1] Bioconductor  
##  fansi                    0.4.1    2020-01-08 [1] CRAN (R 4.0.2)
##  fastmap                  1.0.1    2019-10-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)
##  GenomeInfoDb           * 1.24.2   2020-06-15 [1] Bioconductor  
##  GenomeInfoDbData         1.2.3    2020-07-23 [1] Bioconductor  
##  GenomicRanges          * 1.40.0   2020-04-27 [1] Bioconductor  
##  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)
##  httpuv                   1.5.4    2020-06-06 [1] CRAN (R 4.0.2)
##  httr                     1.4.2    2020-07-20 [1] CRAN (R 4.0.2)
##  interactiveDisplayBase   1.26.3   2020-06-02 [1] Bioconductor  
##  IRanges                * 2.22.2   2020-05-21 [1] Bioconductor  
##  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)
##  later                    1.1.0.1  2020-06-05 [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)
##  mime                     0.9      2020-02-04 [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)
##  promises                 1.1.1    2020-06-09 [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)
##  rappdirs                 0.3.1    2016-03-28 [1] CRAN (R 4.0.2)
##  Rcpp                     1.0.5    2020-07-06 [1] CRAN (R 4.0.2)
##  RCurl                    1.98-1.2 2020-04-18 [1] CRAN (R 4.0.2)
##  readr                    1.3.1    2018-12-21 [1] CRAN (R 4.0.2)
##  readxl                   1.3.1    2019-03-13 [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)
##  RSQLite                  2.2.0    2020-01-07 [1] CRAN (R 4.0.2)
##  S4Vectors              * 0.26.1   2020-05-16 [1] Bioconductor  
##  sessioninfo              1.1.1    2018-11-05 [1] CRAN (R 4.0.2)
##  shiny                    1.5.0    2020-06-23 [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)
##  tidyr                  * 1.1.1    2020-07-31 [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)
##  xtable                   1.8-4    2019-04-21 [1] CRAN (R 4.0.2)
##  XVector                  0.28.0   2020-04-27 [1] Bioconductor  
##  yaml                     2.2.1    2020-02-01 [1] CRAN (R 4.0.2)
##  zlibbioc                 1.34.0   2020-04-27 [1] Bioconductor  
## 
## [1] /Library/Frameworks/R.framework/Versions/4.0/Resources/library
LS0tCnRpdGxlOiAiTWV0YS1hbmFseXNpcyB3aXRoIG1hdGNoZWQgc2FtcGxlcyAoYnkgYWdlIGF0IGRlYXRoIGFuZCBzZXgpIgphdXRob3I6ICJMYW55dSBaaGFuZywgVGlhZ28gQy4gU2lsdmEsIExpbHkgV2FuZyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgcm1hcmtkb3duOjpodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGx1bWVuCiAgICB0b2M6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogeWVzCiAgICB0b2NfZGVwdGg6IDMKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZSAgICAKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpCmBgYAoKIyBNYWluIGxpYnJhcmllcyBhbmQgY29uZmlndXJhdGlvbgoKYGBge1J9CmRpci5yZXN1bHQgPC0gIi4vREFUQVNFVFMvbWF0Y2hlZF9hbmFseXNpc19yZXN1bHRzLyIKZGlyLmRtciA8LSAiLi4vbWV0YV9hbmFseXNpc19yZWdpb25fcmVzdWx0cy9zdGVwNF9kbXJfdnNfY3Bncy8iIApkaXIuc2luZ2xlLmNwZyA8LSAiLi4vbWV0YV9hbmFseXNpc19zaW5nbGVfY3BnX3Jlc3VsdHMvIiAgICAgICAgCmRpci5jb21icCA8LSAiLi4vLi4vLi4vTWljaGFlbC9tYXRjaGVkQW5hbHlzaXNfcmV2aXNpb25fNy0xMy0yMDIwL2NvbWItcF83XzE1XzIwMjAvIgpkaXIucmV2aXNpb24gPC0gIi4uLy4uLy4uL0RSQUZUX1JFVklTSU9OX05hdENvbW1fNi0yMDIwL0luZGl2aWR1YWwgZmlsZXMvIgpkaXIuZmlnIDwtICIuL0ZJR1VSRVMiCmBgYAoKYGBge1IsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShFeHBlcmltZW50SHViKQpsaWJyYXJ5KEdlbm9taWNSYW5nZXMpCmxpYnJhcnkoZTEwNzEpCmxpYnJhcnkobWV0YSkKYGBgCgojIE1hdGNoZWQgYW5hbHlzaXMKCmBgYHtSfQpkZXZ0b29sczo6c291cmNlX2dpc3QoImh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3RpYWdvY2hzdC9kM2E3YjE2MzlhY2Y2MDM5MTZjMzE1ZDIzYjFlZmIzZSIpCmBgYAoKYGBge1J9CmZpbGVzIDwtIGRpcigKICBwYXRoID0gIi4uL0RBVEFTRVRTIiwKICBwYXR0ZXJuID0gcGFzdGUwKCIuKm5ldXJvIiksCiAgcmVjdXJzaXZlID0gVFJVRSwKICBmdWxsLm5hbWVzID0gVFJVRSwKICBpZ25vcmUuY2FzZSA9IFRSVUUKKQpgYGAKCmBgYHtSLCBlY2hvID0gRkFMU0V9CmZpbGVzIDwtIGZpbGVzW2MoMSwyLDQsNSldCmBgYAoKYGBge1J9CmZpbGVzCnBoZW5vLmxzIDwtIGxhcHBseShmaWxlcywgZnVuY3Rpb24gKGYpewogIHJlYWRSRFMoZikKfSkKbmFtZXMocGhlbm8ubHMpIDwtIGZpbGVzICU+JSBkaXJuYW1lICU+JSBkaXJuYW1lICU+JSBiYXNlbmFtZQpsYXBwbHkocGhlbm8ubHMsZGltKQpgYGAKCiMjIFJvc21hcCBkYXRhc2V0CgpgYGB7UiwgZXZhbCA9IFRSVUV9CnJvc21hcCA8LSBwaGVuby5scyRST1NNQVBbLCBjKCJTYW1wbGUiLCAibXNleCIsICJicmFha3NjIiwgImFnZV9kZWF0aCIpXQpyb3NtYXAkYWdlIDwtIHJvc21hcCRhZ2VfZGVhdGgKcm9zbWFwJHN0YXR1cyA8LSBpZmVsc2Uocm9zbWFwJGJyYWFrc2MgPCAzLCAiQ29udHJvbCIsICJDYXNlIikKcm9zbWFwJHN0YXR1cyA8LSBhcy5mYWN0b3Iocm9zbWFwJHN0YXR1cykKcm9zbWFwJHNleCA8LSBpZmVsc2Uocm9zbWFwJG1zZXggPT0gMSwgIk1hbGUiLCAiRmVtYWxlIikKcm9zbWFwJHNleCA8LSBhcy5mYWN0b3Iocm9zbWFwJHNleCkKc3RyKHJvc21hcCkKCgp7CiAgc2V0LnNlZWQoNSkgIyBtYXRjaENvbnRyb2wgdXNlcyBhIGRpc3RhbmNlIGNhbGN1bGF0aW9uLCBpZiB0aWVzLCBpdCB1c2VzIHNhbXBsZSBmdW5jdGlvbgogIG1fcm9zIDwtIG1hdGNoQ29udHJvbHMoCiAgICBmb3JtdWxhID0gc3RhdHVzIH4gYWdlICsgc2V4LAogICAgZGF0YSA9IHJvc21hcCwKICAgIGNvbnRsYWJlbCA9ICJDYXNlIiwKICAgIGNhc2VsYWJlbCA9ICJDb250cm9sIgogICkKfQoKb3V0RmlsZSA8LSBkYXRhLmZyYW1lKAogICJtYXRjaGVkQ29udHJvbHMiID0gcm9zbWFwW21fcm9zJGNhc2VzLCAiU2FtcGxlIl0sIAogICJtYXRjaGVkQ2FzZXMiID0gcm9zbWFwW21fcm9zJGNvbnRyb2xzLCAiU2FtcGxlIl0KKQoKb3V0RmlsZUNvbnRyb2wgPC0gbWVyZ2UoCiAgeCA9IG91dEZpbGUsIAogIHkgPSByb3NtYXBbLCBjKCJTYW1wbGUiLCAic3RhdHVzIiwgImFnZSIsICJzZXgiKV0sCiAgYnkueCA9ICJtYXRjaGVkQ29udHJvbHMiLAogIGJ5LnkgPSAiU2FtcGxlIgopCgpjb2xuYW1lcyhvdXRGaWxlQ29udHJvbClbMzo1XSA8LSBjKCJzdGF0dXNfY29udHJvbCIsICJhZ2VfY29udHJvbCIsICJzZXhfY29udHJvbCIpCgpvdXRGaWxlQ29udHJvbENhc2VzIDwtIG1lcmdlKAogIHggPSBvdXRGaWxlQ29udHJvbCwgCiAgeSA9IHJvc21hcFssIGMoIlNhbXBsZSIsICJzdGF0dXMiLCAiYWdlIiwgInNleCIpXSwKICBieS54ID0gIm1hdGNoZWRDYXNlcyIsCiAgYnkueSA9ICJTYW1wbGUiCikKCmNvbG5hbWVzKG91dEZpbGVDb250cm9sQ2FzZXMpWzY6OF0gPC0gYygic3RhdHVzX2Nhc2UiLCAiYWdlX2Nhc2UiLCAic2V4X2Nhc2UiKQoKb3V0RmlsZUNvbnRyb2xDYXNlcyA8LSBvdXRGaWxlQ29udHJvbENhc2VzWywgYygyLCAzLCA0LCA1LCAxLCA2LCA3LCA4KV0KYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQp3cml0ZS5jc3YoCiAgb3V0RmlsZUNvbnRyb2xDYXNlcywKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIlJvc21hcF9tYXRjaGVkQ2FzZUNvbnRyb2xzLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKYGBgCgpgYGB7Un0KUk9TTUFQX21hdGNoQWdlU2V4IDwtICBvdXRGaWxlQ29udHJvbENhc2VzICU+JSAKICBkcGx5cjo6ZmlsdGVyKGFicyhhZ2VfY29udHJvbCAtIGFnZV9jYXNlKSA8PSAxKQpkaW0oUk9TTUFQX21hdGNoQWdlU2V4KQoKdGFibGUoUk9TTUFQX21hdGNoQWdlU2V4JHN0YXR1c19jb250cm9sLCBST1NNQVBfbWF0Y2hBZ2VTZXgkc2V4X2NvbnRyb2wpCnRhYmxlKFJPU01BUF9tYXRjaEFnZVNleCRzdGF0dXNfY2FzZSwgUk9TTUFQX21hdGNoQWdlU2V4JHNleF9jYXNlKQoKUk9TTUFQX3NhbXBsZXMgPC0gYygKICBhcy5jaGFyYWN0ZXIoUk9TTUFQX21hdGNoQWdlU2V4JG1hdGNoZWRDb250cm9scyksCiAgYXMuY2hhcmFjdGVyKFJPU01BUF9tYXRjaEFnZVNleCRtYXRjaGVkQ2FzZXMpCikKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpzYXZlUkRTKAogIFJPU01BUF9zYW1wbGVzLAogIHBhc3RlMChkaXIucmVzdWx0LCAiUk9TTUFQX3NhbXBsZXMuUkRTIikKKQpgYGAKCiMjIyBTYW1wbGVzCgpgYGB7Un0KbGVuZ3RoKHJlYWRSRFMocGFzdGUwKGRpci5yZXN1bHQsICJST1NNQVBfc2FtcGxlcy5SRFMiKSkpCmBgYAoKIyMjIExpbmVhciBtb2RlbAoKYGBge1IgUk9TTUFQX0RNUiwgZXZhbCA9IEZBTFNFfQojIyMgSW1wb3J0IGRhdGFzZXRzCmNvaG9ydCA8LSAiUk9TTUFQIgpwaGVubyA8LSAicGhlbm8iCgppbmZvX2RmIDwtIHJlYWRSRFMoCiAgZGlyKHBhdGggPSAiLi4vREFUQVNFVFMvUk9TTUFQLy8vLyIsIHBhdHRlcm4gPSAiaW5mbyIsIHJlY3Vyc2l2ZSA9IFRSVUUsIGZ1bGwubmFtZXMgPSBUUlVFKQopCm1lZGlhbnNNdmFsX2RmIDwtIHJlYWRSRFMoCiAgZGlyKHBhdGggPSAiLi4vREFUQVNFVFMvUk9TTUFQLyIsIHBhdHRlcm4gPSAibWVkaWFucyIsIHJlY3Vyc2l2ZSA9IFRSVUUsIGZ1bGwubmFtZXMgPSBUUlVFKQopCnBoZW5vX2RmIDwtIHJlYWRSRFMoCiAgZGlyKHBhdGggPSAiLi4vREFUQVNFVFMvUk9TTUFQLyIsIHBhdHRlcm4gPSAiTmV1cm9uIiwgcmVjdXJzaXZlID0gVFJVRSwgZnVsbC5uYW1lcyA9IFRSVUUpCikKc2FtcGxlcyA8LSByZWFkUkRTKAogIHBhc3RlMChkaXIucmVzdWx0LCAiUk9TTUFQX3NhbXBsZXMuUkRTIikKKQoKIyMjIExpbWl0IHNhbXBsZXMKbWVkaWFuc012YWxfZGYgPC0gbWVkaWFuc012YWxfZGZbLCBzYW1wbGVzXQpwaGVub19kZiA8LSBwaGVub19kZltwaGVub19kZiRTYW1wbGUgJWluJSBzYW1wbGVzLCBdCgptZWRpYW5zTXZhbF9kZiA8LSBtZWRpYW5zTXZhbF9kZlssIHBoZW5vX2RmJFNhbXBsZV0KCmlkZW50aWNhbChwaGVub19kZiRTYW1wbGUsIGNvbG5hbWVzKG1lZGlhbnNNdmFsX2RmKSkKCnN0cihwaGVub19kZikKCnBoZW5vX2RmJGFnZV9kZWF0aCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihwaGVub19kZiRhZ2VfZGVhdGgpKQpwaGVub19kZiRtc2V4IDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIocGhlbm9fZGYkbXNleCkpCnBoZW5vX2RmJFNsaWRlIDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIocGhlbm9fZGYkU2xpZGUpKQpwaGVub19kZiRiYXRjaCA8LSBhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKHBoZW5vX2RmJGJhdGNoKSkKCnByZWRpY3RvcnNfY2hhciA8LSAiYnJhYWtzYyIKY292YXJpYXRlc19jaGFyIDwtIGMoInByb3AubmV1cm9uIiwgImJhdGNoIikKCnJlc19kZiA8LSBUZXN0QWxsUmVnaW9uc19ub0luZm8oCiAgcHJlZGljdG9yc19jaGFyID0gcHJlZGljdG9yc19jaGFyLAogIGNvdmFyaWF0ZXNfY2hhciA9IGNvdmFyaWF0ZXNfY2hhciwKICBwaGVub19kZiA9IHBoZW5vX2RmLAogIHN1bW1hcml6ZWRSZWdpb25zX2RmID0gbWVkaWFuc012YWxfZGYsCiAgY29yZXMgPSA0CikKCmNvbG5hbWVzKHJlc19kZikgPC0gYygKICBwYXN0ZTAoY29ob3J0LCAiX2VzdGltYXRlIiksCiAgcGFzdGUwKGNvaG9ydCwgIl9zZSIpLAogIHBhc3RlMChjb2hvcnQsICJfcFZhbCIpLAogIHBhc3RlMChjb2hvcnQsICJfZmRyIikKKQoKcmVzX3dpdGhJbmZvX2RmIDwtIGNiaW5kKGluZm9fZGYsIHJlc19kZikKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpzYXZlUkRTKAogIHJlc193aXRoSW5mb19kZiwKICBwYXN0ZTAoZGlyLnJlc3VsdCwgY29ob3J0LCAiX21hdGNoZWRfbGluZWFyX2RmLnJkcyIpCikKYGBgCgojIyMgU2luZ2xlIGNwZyBsaW5lYXIgbW9kZWwKCmBgYHtSIFJPU01BUF9zaW5nbGVDcEcsIGV2YWwgPSBUUlVFfQojIyMgSW1wb3J0IGRhdGFzZXRzCmJldGFfbWF0IDwtIHJlYWRSRFMoCiAgIi4uL0RBVEFTRVRTL1JPU01BUC9zdGVwN19wY2FfZmlsdGVyaW5nL1JPU01BUF9RTkJNSVFfUENmaWx0ZXJlZC5SRFMiCikgCnBoZW5vX2RmIDwtIHJlYWRSRFMoCiAgZGlyKHBhdGggPSAiLi4vREFUQVNFVFMvUk9TTUFQLyIsIHBhdHRlcm4gPSAiTmV1cm9uIiwgcmVjdXJzaXZlID0gVFJVRSwgZnVsbC5uYW1lcyA9IFRSVUUpCikKc2FtcGxlcyA8LSByZWFkUkRTKAogIHBhc3RlMChkaXIucmVzdWx0LCAiUk9TTUFQX3NhbXBsZXMuUkRTIikKKQoKIyMjIExpbWl0IHNhbXBsZXMKYmV0YV9tYXQgPC0gYmV0YV9tYXRbLCBzYW1wbGVzXQpwaGVub19kZiA8LSBwaGVub19kZltwaGVub19kZiRTYW1wbGUgJWluJSBzYW1wbGVzLCBdCgpiZXRhX21hdCA8LSBiZXRhX21hdFssIHBoZW5vX2RmJFNhbXBsZV0KCmlkZW50aWNhbChwaGVub19kZiRTYW1wbGUsIGNvbG5hbWVzKGJldGFfbWF0KSkKCiMjIyBDb21wdXRlIE0gdmFsdWVzCm12YWxfbWF0IDwtIGxvZzIoYmV0YV9tYXQgLyAoMSAtIGJldGFfbWF0KSkKCnBoZW5vX2RmJHNleCA8LSBhcy5mYWN0b3IocGhlbm9fZGYkc2V4KQpwaGVub19kZiRzbGlkZSA8LSBhcy5mYWN0b3IocGhlbm9fZGYkU2VudHJpeF9JRCkKcGhlbm9fZGYkYmF0Y2ggPC0gYXMuZmFjdG9yKHBoZW5vX2RmJGJhdGNoKQoKc3RyKHBoZW5vX2RmKQoKaXMocGhlbm9fZGYkYnJhYWtzYywibnVtZXJpYyIpCmlzKHBoZW5vX2RmJHByb3AubmV1cm9uLCJudW1lcmljIikKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpwcmVkaWN0b3JzX2NoYXIgPC0gImJyYWFrc2MiCmNvdmFyaWF0ZXNfY2hhciA8LSBjKCJwcm9wLm5ldXJvbiIsICJiYXRjaCIpCgpkb1BhcmFsbGVsOjpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKS8yKQpkZXZ0b29sczo6c291cmNlX2dpc3QoImh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3RpYWdvY2hzdC9kM2E3YjE2MzlhY2Y2MDM5MTZjMzE1ZDIzYjFlZmIzZSIpCgpyZXN1bHRzX29yZGVyZWRfZGYgPC0gcGx5cjo6YWRwbHkobXZhbF9tYXQsMSwgZnVuY3Rpb24ocm93KXsKICAKICBzdW1PbmVSZWdpb25fZGYgPC0gYXMuZGF0YS5mcmFtZSh0KHJvdykpCiAgCiAgcmVzdWx0IDwtIFRlc3RTaW5nbGVSZWdpb24oCiAgICBwcmVkaWN0b3JzX2NoYXIgPSBwcmVkaWN0b3JzX2NoYXIsCiAgICBjb3ZhcmlhdGVzX2NoYXIgPSBjb3ZhcmlhdGVzX2NoYXIsCiAgICBwaGVub19kZiA9IHBoZW5vX2RmLAogICAgc3VtT25lUmVnaW9uX2RmID0gc3VtT25lUmVnaW9uX2RmCiAgKQogIHJlc3VsdAp9LCAucHJvZ3Jlc3MgPSAidGltZSIsLnBhcmFsbGVsID0gVFJVRSwuaWQgPSAiY3BnIikKY29sbmFtZXMocmVzdWx0c19vcmRlcmVkX2RmKVsxXSA8LSAiY3BnIgoKaWRlbnRpY2FsKHJvdy5uYW1lcyhtdmFsX21hdCksIHJlc3VsdHNfb3JkZXJlZF9kZiRjcGcgJT4lIGFzLmNoYXJhY3RlcigpKQoKd3JpdGUuY3N2KAogIHJlc3VsdHNfb3JkZXJlZF9kZiwKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIlJPU01BUF9tYXRjaGVkX3NpbmdsZV9jcGdfbGluZWFyX2RmLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKYGBgCgpgYGB7Un0KcmVzdWx0c19vcmRlcmVkX2RmIDwtIHJlYWRyOjpyZWFkX2NzdigKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIlJPU01BUF9tYXRjaGVkX3NpbmdsZV9jcGdfbGluZWFyX2RmLmNzdiIpLAogIGNvbF90eXBlcyA9IHJlYWRyOjpjb2xzKCkKKQpkaW0ocmVzdWx0c19vcmRlcmVkX2RmKQpyZXN1bHRzX29yZGVyZWRfZGYgJT4lIGhlYWQKYGBgCgojIyBMb25kb24gZGF0YXNldAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KbG9uZG9uIDwtIHBoZW5vLmxzJExPTkRPTlssIGMoInNhbXBsZSIsICJzZXgiLCAic3RhZ2UiLCAiYWdlLmJyYWluIildCmxvbmRvbiRzdGF0dXMgPC0gaWZlbHNlKGxvbmRvbiRzdGFnZSA8IDMsICJDb250cm9sIiwgIkNhc2UiKQpsb25kb24kc3RhdHVzIDwtIGFzLmZhY3Rvcihsb25kb24kc3RhdHVzKQpsb25kb24kc2V4IDwtIGFzLmZhY3Rvcihsb25kb24kc2V4KQpzdHIobG9uZG9uKQoKewogIHNldC5zZWVkKDUpCiAgbV9sb25kb24gPC0gbWF0Y2hDb250cm9scygKICAgIGZvcm11bGEgPSBzdGF0dXMgfiBhZ2UuYnJhaW4gKyBzZXgsCiAgICBkYXRhID0gbG9uZG9uLAogICAgY29udGxhYmVsID0gIkNhc2UiLAogICAgY2FzZWxhYmVsID0gIkNvbnRyb2wiCiAgKQp9Cm91dEZpbGUgPC0gIGRhdGEuZnJhbWUoCiAgIm1hdGNoZWRDb250cm9scyIgPSBsb25kb25bbV9sb25kb24kY2FzZXMsICJzYW1wbGUiXSwKICAibWF0Y2hlZENhc2VzIiA9IGxvbmRvblttX2xvbmRvbiRjb250cm9scywgInNhbXBsZSJdCikKCm91dEZpbGVDb250cm9sIDwtIG1lcmdlKAogIHggPSBvdXRGaWxlLAogIHkgPSBsb25kb25bLCBjKCJzYW1wbGUiLCAic3RhdHVzIiwgImFnZS5icmFpbiIsICJzZXgiKV0sCiAgYnkueCA9ICJtYXRjaGVkQ29udHJvbHMiLAogIGJ5LnkgPSAic2FtcGxlIgopCmNvbG5hbWVzKG91dEZpbGVDb250cm9sKVszOjVdIDwtIGMoInN0YXR1c19jb250cm9sIiwgImFnZV9jb250cm9sIiwgInNleF9jb250cm9sIikKCm91dEZpbGVDb250cm9sQ2FzZXMgPC0gbWVyZ2UoCiAgeCA9IG91dEZpbGVDb250cm9sLAogIHkgPSBsb25kb25bLCBjKCJzYW1wbGUiLCAic3RhdHVzIiwgImFnZS5icmFpbiIsICJzZXgiKV0sCiAgYnkueCA9ICJtYXRjaGVkQ2FzZXMiLAogIGJ5LnkgPSAic2FtcGxlIgopCmNvbG5hbWVzKG91dEZpbGVDb250cm9sQ2FzZXMpWzY6OF0gPC0gYygic3RhdHVzX2Nhc2UiLCAiYWdlX2Nhc2UiLCAic2V4X2Nhc2UiKQpvdXRGaWxlQ29udHJvbENhc2VzIDwtIG91dEZpbGVDb250cm9sQ2FzZXNbLCBjKDIsIDMsIDQsIDUsIDEsIDYsIDcsIDgpXQoKd3JpdGUuY3N2KAogIG91dEZpbGVDb250cm9sQ2FzZXMsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJMb25kb25fbWF0Y2hlZENhc2VDb250cm9scy5jc3YiKSwKICByb3cubmFtZXMgPSBGQUxTRQopCgpMb25kb25fbWF0Y2hBZ2VTZXggPC0gb3V0RmlsZUNvbnRyb2xDYXNlcyAlPiUgCiAgZHBseXI6OmZpbHRlcihhYnMoYWdlX2NvbnRyb2wgLSBhZ2VfY2FzZSkgPD0gMSkKCmRpbShMb25kb25fbWF0Y2hBZ2VTZXgpCgp0YWJsZShMb25kb25fbWF0Y2hBZ2VTZXgkc3RhdHVzX2NvbnRyb2wsIExvbmRvbl9tYXRjaEFnZVNleCRzZXhfY29udHJvbCkKdGFibGUoTG9uZG9uX21hdGNoQWdlU2V4JHN0YXR1c19jYXNlLCBMb25kb25fbWF0Y2hBZ2VTZXgkc2V4X2Nhc2UpCkxvbmRvbl9zYW1wbGVzIDwtIGMoCiAgYXMuY2hhcmFjdGVyKExvbmRvbl9tYXRjaEFnZVNleCRtYXRjaGVkQ29udHJvbHMpLAogIGFzLmNoYXJhY3RlcihMb25kb25fbWF0Y2hBZ2VTZXgkbWF0Y2hlZENhc2VzKQopCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0Kc2F2ZVJEUygKICBMb25kb25fc2FtcGxlcywKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIkxvbmRvbl9zYW1wbGVzLlJEUyIpCikKCmBgYAoKIyMjIFNhbXBsZXMKCmBgYHtSfQpsZW5ndGgocmVhZFJEUyhwYXN0ZTAoZGlyLnJlc3VsdCwgIkxvbmRvbl9zYW1wbGVzLlJEUyIpKSkKYGBgCgojIyMgTGluZWFyIG1vZGVsCgpgYGB7UiBMb25kb25fRE1SLCBldmFsID0gVFJVRX0KIyMjIEltcG9ydCBkYXRhc2V0cwpjb2hvcnQgPC0gIkxvbmRvbiIKcGhlbm8gPC0gInBoZW5vIgoKaW5mb19kZiA8LSByZWFkUkRTKAogIGRpcihwYXRoID0gIi4uL0RBVEFTRVRTL0xPTkRPTi8vLyIsIHBhdHRlcm4gPSAiaW5mbyIsIHJlY3Vyc2l2ZSA9IFRSVUUsIGZ1bGwubmFtZXMgPSBUUlVFKQopCm1lZGlhbnNNdmFsX2RmIDwtIHJlYWRSRFMoCiAgZGlyKHBhdGggPSAiLi4vREFUQVNFVFMvTE9ORE9OLyIsIHBhdHRlcm4gPSAibWVkaWFucyIsIHJlY3Vyc2l2ZSA9IFRSVUUsIGZ1bGwubmFtZXMgPSBUUlVFKQopCnBoZW5vX2RmIDwtIHJlYWRSRFMoCiAgZGlyKHBhdGggPSAiLi4vREFUQVNFVFMvTE9ORE9OLyIsIHBhdHRlcm4gPSAiTmV1cm9uUHJvcF9kZiIsIHJlY3Vyc2l2ZSA9IFRSVUUsIGZ1bGwubmFtZXMgPSBUUlVFKQopCnNhbXBsZXMgPC0gcmVhZFJEUygKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIkxvbmRvbl9zYW1wbGVzLlJEUyIpCikKCiMjIyBMaW1pdCBzYW1wbGVzCm1lZGlhbnNNdmFsX2RmIDwtIG1lZGlhbnNNdmFsX2RmWywgc2FtcGxlc10KcGhlbm9fZGYgPC0gcGhlbm9fZGZbcGhlbm9fZGYkc2FtcGxlICVpbiUgc2FtcGxlcywgXQoKbWVkaWFuc012YWxfZGYgPC0gbWVkaWFuc012YWxfZGZbLCBwaGVub19kZiRzYW1wbGVdCgojIyMgQ2hlY2sgdmFyaWFibGVzIGJlZm9yZSBmaXR0aW5nIG1vZGVsCnBoZW5vX2RmJFNhbXBsZSA8LSBwaGVub19kZiRzYW1wbGUKCmlkZW50aWNhbChwaGVub19kZiRTYW1wbGUsIGNvbG5hbWVzKG1lZGlhbnNNdmFsX2RmKSkKCnN0cihwaGVub19kZikKCnBoZW5vX2RmJHNleCA8LSBhcy5mYWN0b3IocGhlbm9fZGYkc2V4KQpwaGVub19kZiRzbGlkZSA8LSBhcy5mYWN0b3IocGhlbm9fZGYkc2xpZGUpCiMgSWYgcm9zbWFwIGNvaG9ydCwgZG9uJ3QgZm9yZ2V0IGJhdGNoIGVmZmVjdApgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CmRldnRvb2xzOjpzb3VyY2VfZ2lzdCgiaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdGlhZ29jaHN0L2QzYTdiMTYzOWFjZjYwMzkxNmMzMTVkMjNiMWVmYjNlIikKcHJlZGljdG9yc19jaGFyIDwtICJzdGFnZSIKY292YXJpYXRlc19jaGFyIDwtICJwcm9wLm5ldXJvbiIKCnJlc19kZiA8LSBUZXN0QWxsUmVnaW9uc19ub0luZm8oCiAgcHJlZGljdG9yc19jaGFyID0gcHJlZGljdG9yc19jaGFyLAogIGNvdmFyaWF0ZXNfY2hhciA9IGNvdmFyaWF0ZXNfY2hhciwKICBwaGVub19kZiA9IHBoZW5vX2RmLAogIHN1bW1hcml6ZWRSZWdpb25zX2RmID0gbWVkaWFuc012YWxfZGYsCiAgY29yZXMgPSA0CikKCmNvbG5hbWVzKHJlc19kZikgPC0gYygKICBwYXN0ZTAoY29ob3J0LCAiX2VzdGltYXRlIiksCiAgcGFzdGUwKGNvaG9ydCwgIl9zZSIpLAogIHBhc3RlMChjb2hvcnQsICJfcFZhbCIpLAogIHBhc3RlMChjb2hvcnQsICJfZmRyIikKKQoKcmVzX3dpdGhJbmZvX2RmIDwtIGNiaW5kKGluZm9fZGYsIHJlc19kZikKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpzYXZlUkRTKAogIHJlc193aXRoSW5mb19kZiwKICBwYXN0ZTAoZGlyLnJlc3VsdCwgY29ob3J0LCAiX21hdGNoZWRfbGluZWFyX2RmLnJkcyIpCikKYGBgCgojIyMgU2luZ2xlIGNwZyBsaW5lYXIgbW9kZWwKCmBgYHtSIExvbmRvbl9zaW5nbGVDcEcsIGV2YWwgPSBUUlVFfQojIyMgSW1wb3J0IGRhdGFzZXRzCmJldGFfbWF0IDwtIHJlYWRSRFMoCiAgIi4uL0RBVEFTRVRTL0xPTkRPTi9zdGVwNV9wY2FfZmlsdGVyaW5nL0xvbmRvbl9QRkNfUU5CTUlRX1BDZmlsdGVyZWQuUkRTIgopIApwaGVub19kZiA8LSByZWFkUkRTKAogIGRpcigiLi4vREFUQVNFVFMvTE9ORE9OLyIscGF0dGVybiA9ICJOZXVyb25Qcm9wX2RmIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnNhbXBsZXMgPC0gcmVhZFJEUygKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIkxvbmRvbl9zYW1wbGVzLlJEUyIpCikKCiMjIyBMaW1pdCBzYW1wbGVzCnBoZW5vX2RmJFNhbXBsZSA8LSBwaGVub19kZiRzYW1wbGUKCmJldGFfbWF0IDwtIGJldGFfbWF0Wywgc2FtcGxlc10KcGhlbm9fZGYgPC0gcGhlbm9fZGZbcGhlbm9fZGYkU2FtcGxlICVpbiUgc2FtcGxlcywgXQoKYmV0YV9tYXQgPC0gYmV0YV9tYXRbLCBwaGVub19kZiRTYW1wbGVdCgppZGVudGljYWwocGhlbm9fZGYkU2FtcGxlLCBjb2xuYW1lcyhiZXRhX21hdCkpCgojIyMgQ29tcHV0ZSBNIHZhbHVlcwptdmFsX21hdCA8LSBsb2cyKGJldGFfbWF0IC8gKDEgLSBiZXRhX21hdCkpCgpwaGVub19kZiRzZXggPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNleCkKcGhlbm9fZGYkc2xpZGUgPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNsaWRlKQoKc3RyKHBoZW5vX2RmKQoKaXMocGhlbm9fZGYkc3RhZ2UsIm51bWVyaWMiKQppcyhwaGVub19kZiRwcm9wLm5ldXJvbiwibnVtZXJpYyIpCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KcHJlZGljdG9yc19jaGFyIDwtICJzdGFnZSIKY292YXJpYXRlc19jaGFyIDwtICJwcm9wLm5ldXJvbiIKCmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbChjb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLzIpCmRldnRvb2xzOjpzb3VyY2VfZ2lzdCgiaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdGlhZ29jaHN0L2QzYTdiMTYzOWFjZjYwMzkxNmMzMTVkMjNiMWVmYjNlIikKCnJlc3VsdHNfb3JkZXJlZF9kZiA8LSBwbHlyOjphZHBseShtdmFsX21hdCwxLCBmdW5jdGlvbihyb3cpewogIAogIHN1bU9uZVJlZ2lvbl9kZiA8LSBhcy5kYXRhLmZyYW1lKHQocm93KSkKICAKICByZXN1bHQgPC0gVGVzdFNpbmdsZVJlZ2lvbigKICAgIHByZWRpY3RvcnNfY2hhciA9IHByZWRpY3RvcnNfY2hhciwKICAgIGNvdmFyaWF0ZXNfY2hhciA9IGNvdmFyaWF0ZXNfY2hhciwKICAgIHBoZW5vX2RmID0gcGhlbm9fZGYsCiAgICBzdW1PbmVSZWdpb25fZGYgPSBzdW1PbmVSZWdpb25fZGYKICApCiAgcmVzdWx0Cn0sIC5wcm9ncmVzcyA9ICJ0aW1lIiwgLnBhcmFsbGVsID0gVFJVRSwgLmlkID0gImNwZyIpCmNvbG5hbWVzKHJlc3VsdHNfb3JkZXJlZF9kZilbMV0gPC0gImNwZyIKCmlkZW50aWNhbChyb3cubmFtZXMobXZhbF9tYXQpLCByZXN1bHRzX29yZGVyZWRfZGYkY3BnICU+JSBhcy5jaGFyYWN0ZXIoKSkKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQp3cml0ZS5jc3YoCiAgcmVzdWx0c19vcmRlcmVkX2RmLAogIHBhc3RlMChkaXIucmVzdWx0LCAiTG9uZG9uX21hdGNoZWRfc2luZ2xlX2NwZ19saW5lYXJfZGYuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQpgYGAKCmBgYHtSfQpyZXN1bHRzX29yZGVyZWRfZGYgPC0gcmVhZHI6OnJlYWRfY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAiTG9uZG9uX21hdGNoZWRfc2luZ2xlX2NwZ19saW5lYXJfZGYuY3N2IiksCiAgY29sX3R5cGVzID0gcmVhZHI6OmNvbHMoKQopCmRpbShyZXN1bHRzX29yZGVyZWRfZGYpCnJlc3VsdHNfb3JkZXJlZF9kZiAlPiUgaGVhZApgYGAKCiMjIEdhc3Bhcm9uaSBkYXRhc2V0CgpgYGB7UiwgZXZhbCA9IEZBTFNFLCBpbmNsdWRlID0gRkFMU0V9Cmdhc3Bhcm9uaSA8LSBwaGVuby5scyRHQVNQQVJPTklbLCBjKCJzYW1wbGUiLCAic2V4IiwgInN0YWdlIiwgImFnZS5icmFpbiIpXQpnYXNwYXJvbmkgPC0gZ2FzcGFyb25pWywgYygic2FtcGxlIiwgInNleCIsICJzdGFnZSIsICJhZ2UuYnJhaW4iKV0KZ2FzcGFyb25pJHN0YXR1cyA8LSBpZmVsc2UoZ2FzcGFyb25pJHN0YWdlIDwgMywgIkNvbnRyb2wiLCAiQ2FzZSIpCmdhc3Bhcm9uaSRzdGF0dXMgPC0gYXMuZmFjdG9yKGdhc3Bhcm9uaSRzdGF0dXMpCmdhc3Bhcm9uaSRzZXggPC0gYXMuZmFjdG9yKGdhc3Bhcm9uaSRzZXgpCnN0cihnYXNwYXJvbmkpCgp7CiAgc2V0LnNlZWQoNSkKICBtX2dhc3Bhcm9uaSA8LSBtYXRjaENvbnRyb2xzKAogICAgZm9ybXVsYSA9IHN0YXR1cyB+IGFnZS5icmFpbiArIHNleCwKICAgIGRhdGEgPSBnYXNwYXJvbmksCiAgICBjb250bGFiZWwgPSAiQ2FzZSIsCiAgICBjYXNlbGFiZWwgPSAiQ29udHJvbCIKICApCn0KCm91dEZpbGUgPC0gZGF0YS5mcmFtZSgKICAibWF0Y2hlZENvbnRyb2xzIiA9IGdhc3Bhcm9uaVttX2dhc3Bhcm9uaSRjYXNlcywgInNhbXBsZSJdLCAKICAibWF0Y2hlZENhc2VzIiA9IGdhc3Bhcm9uaVttX2dhc3Bhcm9uaSRjb250cm9scywgInNhbXBsZSJdCikKCm91dEZpbGVDb250cm9sIDwtIG1lcmdlKAogIHggPSBvdXRGaWxlLCAKICB5ID0gZ2FzcGFyb25pWywgYygic2FtcGxlIiwgInN0YXR1cyIsICJhZ2UuYnJhaW4iLCAic2V4IildLAogIGJ5LnggPSAibWF0Y2hlZENvbnRyb2xzIiwKICBieS55ID0gInNhbXBsZSIKKQpjb2xuYW1lcyhvdXRGaWxlQ29udHJvbClbMzo1XSA8LSBjKCJzdGF0dXNfY29udHJvbCIsICJhZ2VfY29udHJvbCIsICJzZXhfY29udHJvbCIpCgpvdXRGaWxlQ29udHJvbENhc2VzIDwtIG1lcmdlKAogIHggPSBvdXRGaWxlQ29udHJvbCwKICB5ID0gZ2FzcGFyb25pWywgYygic2FtcGxlIiwgInN0YXR1cyIsICJhZ2UuYnJhaW4iLCAic2V4IildLAogIGJ5LnggPSAibWF0Y2hlZENhc2VzIiwKICBieS55ID0gInNhbXBsZSIKKQpjb2xuYW1lcyhvdXRGaWxlQ29udHJvbENhc2VzKVs2OjhdIDwtIGMoInN0YXR1c19jYXNlIiwgImFnZV9jYXNlIiwgInNleF9jYXNlIikKCm91dEZpbGVDb250cm9sQ2FzZXMgPC0gb3V0RmlsZUNvbnRyb2xDYXNlc1ssIGMoMiwgMywgNCwgNSwgMSwgNiwgNywgOCldCgp0YWJsZShvdXRGaWxlQ29udHJvbENhc2VzJHN0YXR1c19jb250cm9sLAogICAgICBvdXRGaWxlQ29udHJvbENhc2VzJHNleF9jb250cm9sKQp0YWJsZShvdXRGaWxlQ29udHJvbENhc2VzJHN0YXR1c19jYXNlLAogICAgICBvdXRGaWxlQ29udHJvbENhc2VzJHNleF9jYXNlKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CndyaXRlLmNzdigKICBvdXRGaWxlQ29udHJvbENhc2VzLAogIHBhc3RlMChkaXIucmVzdWx0LCAiR2FzcGFyb25pX21hdGNoZWRDYXNlQ29udHJvbHMuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9Ckdhc3Bhcm9uaV9tYXRjaEFnZVNleCA8LSAgb3V0RmlsZUNvbnRyb2xDYXNlcyAlPiUgCiAgZHBseXI6OmZpbHRlcihhYnMoYWdlX2NvbnRyb2wgLSBhZ2VfY2FzZSkgPD0gMSkKZGltKEdhc3Bhcm9uaV9tYXRjaEFnZVNleCkKCnRhYmxlKEdhc3Bhcm9uaV9tYXRjaEFnZVNleCRzdGF0dXNfY29udHJvbCwgR2FzcGFyb25pX21hdGNoQWdlU2V4JHNleF9jb250cm9sKQp0YWJsZShHYXNwYXJvbmlfbWF0Y2hBZ2VTZXgkc3RhdHVzX2Nhc2UsIEdhc3Bhcm9uaV9tYXRjaEFnZVNleCRzZXhfY2FzZSkKCkdhc3Bhcm9uaV9zYW1wbGVzIDwtIGMoCiAgYXMuY2hhcmFjdGVyKEdhc3Bhcm9uaV9tYXRjaEFnZVNleCRtYXRjaGVkQ29udHJvbHMpLAogIGFzLmNoYXJhY3RlcihHYXNwYXJvbmlfbWF0Y2hBZ2VTZXgkbWF0Y2hlZENhc2VzKQopCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0Kc2F2ZVJEUygKICBHYXNwYXJvbmlfc2FtcGxlcywKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIkdhc3Bhcm9uaV9zYW1wbGVzLlJEUyIpCikKCmBgYAoKIyMgTXQuU2luYWkgZGF0YXNldAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KbXRzaW5haSA8LSBwaGVuby5scyRNdFNpbmFpWywgYygic2FtcGxlIiwgInNleCIsICJzdGFnZSIsICJhZ2UuYnJhaW4iKV0KbXRzaW5haSRzdGF0dXMgPC0gaWZlbHNlKG10c2luYWkkc3RhZ2UgPCAzLCAiQ29udHJvbCIsICJDYXNlIikKbXRzaW5haSRzdGF0dXMgPC0gYXMuZmFjdG9yKG10c2luYWkkc3RhdHVzKQptdHNpbmFpJHNleCA8LSBhcy5mYWN0b3IobXRzaW5haSRzZXgpCnN0cihtdHNpbmFpKQoKewogIHNldC5zZWVkKDUpCiAgbV9tdHNpbmFpIDwtIG1hdGNoQ29udHJvbHMoCiAgICBmb3JtdWxhID0gc3RhdHVzIH4gYWdlLmJyYWluICsgc2V4LAogICAgZGF0YSA9IG10c2luYWksCiAgICBjb250bGFiZWwgPSAiQ2FzZSIsCiAgICBjYXNlbGFiZWwgPSAiQ29udHJvbCIKICApCn0Kb3V0RmlsZSA8LSBkYXRhLmZyYW1lKAogICJtYXRjaGVkQ29udHJvbHMiID0gbXRzaW5haVttX210c2luYWkkY2FzZXMsICJzYW1wbGUiXSwgCiAgIm1hdGNoZWRDYXNlcyIgPSBtdHNpbmFpW21fbXRzaW5haSRjb250cm9scywgInNhbXBsZSJdCikKCm91dEZpbGVDb250cm9sIDwtIG1lcmdlKAogIG91dEZpbGUsIAogIG10c2luYWlbLCBjKCJzYW1wbGUiLCAic3RhdHVzIiwgImFnZS5icmFpbiIsICJzZXgiKV0sIAogIGJ5LnggPSAibWF0Y2hlZENvbnRyb2xzIiwgCiAgYnkueSA9ICJzYW1wbGUiCikKCmNvbG5hbWVzKG91dEZpbGVDb250cm9sKVszOjVdIDwtIGMoInN0YXR1c19jb250cm9sIiwgImFnZV9jb250cm9sIiwgInNleF9jb250cm9sIikKb3V0RmlsZUNvbnRyb2xDYXNlcyA8LSAgbWVyZ2UoCiAgb3V0RmlsZUNvbnRyb2wsIAogIG10c2luYWlbLCBjKCJzYW1wbGUiLCAic3RhdHVzIiwgImFnZS5icmFpbiIsICJzZXgiKV0sIAogIGJ5LnggPSAibWF0Y2hlZENhc2VzIiwgCiAgYnkueSA9ICJzYW1wbGUiCikKY29sbmFtZXMob3V0RmlsZUNvbnRyb2xDYXNlcylbNjo4XSA8LSBjKCJzdGF0dXNfY2FzZSIsICJhZ2VfY2FzZSIsICJzZXhfY2FzZSIpCm91dEZpbGVDb250cm9sQ2FzZXMgPC0gb3V0RmlsZUNvbnRyb2xDYXNlc1ssIGMoMiwgMywgNCwgNSwgMSwgNiwgNywgOCldCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0Kd3JpdGUuY3N2KAogIG91dEZpbGVDb250cm9sQ2FzZXMsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJNdFNpbmFpX21hdGNoZWRDYXNlQ29udHJvbHMuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9Ck10U2luYWlfbWF0Y2hBZ2VTZXggPC0gIG91dEZpbGVDb250cm9sQ2FzZXMgJT4lIAogIGRwbHlyOjpmaWx0ZXIoYWJzKGFnZV9jb250cm9sIC0gYWdlX2Nhc2UpIDw9IDEpCmRpbShNdFNpbmFpX21hdGNoQWdlU2V4KQoKdGFibGUoTXRTaW5haV9tYXRjaEFnZVNleCRzdGF0dXNfY29udHJvbCwgTXRTaW5haV9tYXRjaEFnZVNleCRzZXhfY29udHJvbCkKdGFibGUoTXRTaW5haV9tYXRjaEFnZVNleCRzdGF0dXNfY2FzZSwgTXRTaW5haV9tYXRjaEFnZVNleCRzZXhfY2FzZSkKCk10U2luYWlfc2FtcGxlcyA8LSBjKAogIGFzLmNoYXJhY3RlcihNdFNpbmFpX21hdGNoQWdlU2V4JG1hdGNoZWRDb250cm9scyksCiAgYXMuY2hhcmFjdGVyKE10U2luYWlfbWF0Y2hBZ2VTZXgkbWF0Y2hlZENhc2VzKQopCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0Kc2F2ZVJEUygKICBNdFNpbmFpX3NhbXBsZXMsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJNdFNpbmFpX3NhbXBsZXMuUkRTIikKKQpgYGAKCiMjIyBTYW1wbGVzCgpgYGB7Un0KbGVuZ3RoKHJlYWRSRFMocGFzdGUwKGRpci5yZXN1bHQsICJNdFNpbmFpX3NhbXBsZXMuUkRTIikpKQpgYGAKCiMjIyBMaW5lYXIgbW9kZWwKCmBgYHtSIE10U2luYWlfRE1SLCBldmFsID0gVFJVRX0KIyMjIEltcG9ydCBkYXRhc2V0cwpjb2hvcnQgPC0gIk10U2luYWkiCnBoZW5vIDwtICJwaGVubyIKCmluZm9fZGYgPC0gcmVhZFJEUygKICBkaXIoIi4uL0RBVEFTRVRTL010U2luYWkvLyIscGF0dGVybiA9ICJpbmZvIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCm1lZGlhbnNNdmFsX2RmIDwtIHJlYWRSRFMoCiAgZGlyKCIuLi9EQVRBU0VUUy9NdFNpbmFpLyIscGF0dGVybiA9ICJtZWRpYW5zIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnBoZW5vX2RmIDwtIHJlYWRSRFMoCiAgZGlyKCIuLi9EQVRBU0VUUy9NdFNpbmFpLyIscGF0dGVybiA9ICJOZXVyb25Qcm9wIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnNhbXBsZXMgPC0gcmVhZFJEUygKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIk10U2luYWlfc2FtcGxlcy5SRFMiKQopCgojIyMgTGltaXQgc2FtcGxlcwptZWRpYW5zTXZhbF9kZiA8LSBtZWRpYW5zTXZhbF9kZlssIHNhbXBsZXNdCnBoZW5vX2RmIDwtIHBoZW5vX2RmW3BoZW5vX2RmJHNhbXBsZSAlaW4lIHNhbXBsZXMsIF0KCm1lZGlhbnNNdmFsX2RmIDwtIG1lZGlhbnNNdmFsX2RmWywgcGhlbm9fZGYkc2FtcGxlXQoKIyMjIENoZWNrIHZhcmlhYmxlcyBiZWZvcmUgZml0dGluZyBtb2RlbApwaGVub19kZiRTYW1wbGUgPC0gcGhlbm9fZGYkc2FtcGxlCgppZGVudGljYWwocGhlbm9fZGYkU2FtcGxlLCBjb2xuYW1lcyhtZWRpYW5zTXZhbF9kZikpCgpzdHIocGhlbm9fZGYpCgpwaGVub19kZiRzZXggPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNleCkKcGhlbm9fZGYkc2xpZGUgPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNsaWRlKQojIElmIHJvc21hcCBjb2hvcnQsIGRvbid0IGZvcmdldCBiYXRjaCBlZmZlY3QKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpkZXZ0b29sczo6c291cmNlX2dpc3QoImh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3RpYWdvY2hzdC9kM2E3YjE2MzlhY2Y2MDM5MTZjMzE1ZDIzYjFlZmIzZSIpCnByZWRpY3RvcnNfY2hhciA8LSAic3RhZ2UiCmNvdmFyaWF0ZXNfY2hhciA8LSAicHJvcC5uZXVyb24iCgpyZXNfZGYgPC0gVGVzdEFsbFJlZ2lvbnNfbm9JbmZvKAogIHByZWRpY3RvcnNfY2hhciA9IHByZWRpY3RvcnNfY2hhciwKICBjb3ZhcmlhdGVzX2NoYXIgPSBjb3ZhcmlhdGVzX2NoYXIsCiAgcGhlbm9fZGYgPSBwaGVub19kZiwKICBzdW1tYXJpemVkUmVnaW9uc19kZiA9IG1lZGlhbnNNdmFsX2RmLAogIGNvcmVzID0gNAopCgpjb2xuYW1lcyhyZXNfZGYpIDwtIGMoCiAgcGFzdGUwKGNvaG9ydCwgIl9lc3RpbWF0ZSIpLAogIHBhc3RlMChjb2hvcnQsICJfc2UiKSwKICBwYXN0ZTAoY29ob3J0LCAiX3BWYWwiKSwKICBwYXN0ZTAoY29ob3J0LCAiX2ZkciIpCikKCnJlc193aXRoSW5mb19kZiA8LSBjYmluZChpbmZvX2RmLCByZXNfZGYpCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0Kc2F2ZVJEUygKICByZXNfd2l0aEluZm9fZGYsCiAgcGFzdGUwKGRpci5yZXN1bHQsIGNvaG9ydCwgIl9tYXRjaGVkX2xpbmVhcl9kZi5yZHMiKQopCmBgYAoKIyMjIFNpbmdsZSBjcGcgbGluZWFyIG1vZGVsCgpgYGB7UiBNdFNpbmFpX3NpbmdsZUNwRywgZXZhbCA9IFRSVUV9CiMjIyBJbXBvcnQgZGF0YXNldHMKYmV0YV9tYXQgPC0gcmVhZFJEUygKICAiLi4vREFUQVNFVFMvTXRTaW5haS9zdGVwNV9wY2FfZmlsdGVyaW5nL010U2luYWlfUU5CTUlRX1BDZmlsdGVyZWQuUkRTIgopIApwaGVub19kZiA8LSByZWFkUkRTKAogIGRpcigiLi4vREFUQVNFVFMvTXRTaW5haS8iLHBhdHRlcm4gPSAiTmV1cm9uIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnNhbXBsZXMgPC0gcmVhZFJEUygKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIk10U2luYWlfc2FtcGxlcy5SRFMiKQopCgojIyMgTGltaXQgc2FtcGxlcwpwaGVub19kZiRTYW1wbGUgPC0gcGhlbm9fZGYkc2FtcGxlCgpiZXRhX21hdCA8LSBiZXRhX21hdFssIHNhbXBsZXNdCnBoZW5vX2RmIDwtIHBoZW5vX2RmW3BoZW5vX2RmJFNhbXBsZSAlaW4lIHNhbXBsZXMsIF0KCmJldGFfbWF0IDwtIGJldGFfbWF0WywgcGhlbm9fZGYkU2FtcGxlXQoKaWRlbnRpY2FsKHBoZW5vX2RmJFNhbXBsZSwgY29sbmFtZXMoYmV0YV9tYXQpKQoKIyMjIENvbXB1dGUgTSB2YWx1ZXMKbXZhbF9tYXQgPC0gbG9nMihiZXRhX21hdCAvICgxIC0gYmV0YV9tYXQpKQoKcGhlbm9fZGYkc2V4IDwtIGFzLmZhY3RvcihwaGVub19kZiRzZXgpCnBoZW5vX2RmJHNsaWRlIDwtIGFzLmZhY3RvcihwaGVub19kZiRzbGlkZSkKCnN0cihwaGVub19kZikKCmlzKHBoZW5vX2RmJHN0YWdlLCJudW1lcmljIikKaXMocGhlbm9fZGYkcHJvcC5uZXVyb24sIm51bWVyaWMiKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CnByZWRpY3RvcnNfY2hhciA8LSAic3RhZ2UiCmNvdmFyaWF0ZXNfY2hhciA8LSAicHJvcC5uZXVyb24iCgpkb1BhcmFsbGVsOjpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKS8yKQpkZXZ0b29sczo6c291cmNlX2dpc3QoImh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3RpYWdvY2hzdC9kM2E3YjE2MzlhY2Y2MDM5MTZjMzE1ZDIzYjFlZmIzZSIpCgpyZXN1bHRzX29yZGVyZWRfZGYgPC0gcGx5cjo6YWRwbHkobXZhbF9tYXQsMSwgZnVuY3Rpb24ocm93KXsKICAKICBzdW1PbmVSZWdpb25fZGYgPC0gYXMuZGF0YS5mcmFtZSh0KHJvdykpCiAgCiAgcmVzdWx0IDwtIFRlc3RTaW5nbGVSZWdpb24oCiAgICBwcmVkaWN0b3JzX2NoYXIgPSBwcmVkaWN0b3JzX2NoYXIsCiAgICBjb3ZhcmlhdGVzX2NoYXIgPSBjb3ZhcmlhdGVzX2NoYXIsCiAgICBwaGVub19kZiA9IHBoZW5vX2RmLAogICAgc3VtT25lUmVnaW9uX2RmID0gc3VtT25lUmVnaW9uX2RmCiAgKQogIHJlc3VsdAp9LCAucHJvZ3Jlc3MgPSAidGltZSIsLnBhcmFsbGVsID0gVFJVRSwuaWQgPSAiY3BnIikKY29sbmFtZXMocmVzdWx0c19vcmRlcmVkX2RmKVsxXSA8LSAiY3BnIgoKaWRlbnRpY2FsKHJvdy5uYW1lcyhtdmFsX21hdCksIHJlc3VsdHNfb3JkZXJlZF9kZiRjcGcgJT4lIGFzLmNoYXJhY3RlcigpKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CndyaXRlLmNzdigKICByZXN1bHRzX29yZGVyZWRfZGYsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJNdFNpbmFpX21hdGNoZWRfc2luZ2xlX2NwZ19saW5lYXJfZGYuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQpgYGAKCmBgYHtSfQpyZXN1bHRzX29yZGVyZWRfZGYgPC0gcmVhZHI6OnJlYWRfY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAiTXRTaW5haV9tYXRjaGVkX3NpbmdsZV9jcGdfbGluZWFyX2RmLmNzdiIpLAogIGNvbF90eXBlcyA9IHJlYWRyOjpjb2xzKCkKKQpkaW0ocmVzdWx0c19vcmRlcmVkX2RmKQpyZXN1bHRzX29yZGVyZWRfZGYgJT4lIGhlYWQKYGBgCgojIE1ldGEtYW5hbHlzaXMgb2YgR2Vub21pYyBSZWdpb25zCgojIyBJbXBvcnQgZGF0YXNldHMgYW5kIHByZS1wcm9jZXNzIGZvciBlYWNoIGNvaG9ydCAKCmBgYHtSLCBldmFsID0gRkFMU0V9CnByZU1ldGEgPC0gZnVuY3Rpb24oY29ob3J0KXsKICAKICAjIyMgTG9hZCBkYXRhCiAgZmlsZSA8LSBkaXIoCiAgICBwYXRoID0gIi4vREFUQVNFVFMvbWF0Y2hlZF9hbmFseXNpc19yZXN1bHRzIiwKICAgIHBhdHRlcm4gPSBwYXN0ZTAoY29ob3J0LCIuKm1hdGNoZWRfbGluZWFyX2RmIiksCiAgICByZWN1cnNpdmUgPSBULAogICAgZnVsbC5uYW1lcyA9IFRSVUUsCiAgICBpZ25vcmUuY2FzZSA9IFQKICApCiAgbGluZWFyLnJlc3VsdHMuZmluYWwgPC0gcmVhZFJEUyhmaWxlKQogIAogICMjIyBzZWxlY3QgdGhlIG1vc3Qgc2lnIGNvbWV0aCByZWdpb24gZm9yIGVhY2ggaW5wdXQgcmVnaW9uCiAgcHZhLmNvbCA8LSBncmVwKCJfcFZhbCIsY29sbmFtZXMobGluZWFyLnJlc3VsdHMuZmluYWwpLHZhbHVlID0gVFJVRSkKICBjb2xuYW1lcyhsaW5lYXIucmVzdWx0cy5maW5hbClbZ3JlcCgiaW5wdXRSZWdpb24iLGNvbG5hbWVzKGxpbmVhci5yZXN1bHRzLmZpbmFsKSldIDwtICJpbnB1dFJlZ2lvbiIKICAKICAjICEhIGlzIHVzZWQgdG8gdW5xdW90ZSBhbiBpbnB1dCAKICAjIGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9wcm9ncmFtbWluZy5odG1sCiAgcmVzdWx0X3NpZyA8LSBsaW5lYXIucmVzdWx0cy5maW5hbCAlPiUKICAgIGdyb3VwX2J5KGlucHV0UmVnaW9uKSAlPiUKICAgIGZpbHRlcigoISFhcy5zeW1ib2wocHZhLmNvbCkpID09IG1pbighIWFzLnN5bWJvbChwdmEuY29sKSkpCiAgCiAgZGF0YS5mcmFtZShyZXN1bHRfc2lnLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCn0KCkxvbmRvbl9QRkMgPC0gcHJlTWV0YShjb2hvcnQgPSAiTG9uZG9uIikKZGltKExvbmRvbl9QRkMpCgpNdFNpbmFpIDwtIHByZU1ldGEoY29ob3J0ID0gIk10U2luYWkiKSAKZGltKE10U2luYWkpCgpST1NNQVAgPC0gcHJlTWV0YShjb2hvcnQgPSAiUk9TTUFQIikKZGltKFJPU01BUCkKYGBgCgojIyBNZXJnZSBjb2hvcnRzIAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KIyMjIG1lcmdlIGRhdGFzZXRzCmNvaG9ydF9scyA8LSBsaXN0KAogIExvbmRvbl9QRkMgPSBMb25kb25fUEZDLAogIE10U2luYWkgPSBNdFNpbmFpLAogIFJPU01BUCA9IFJPU01BUAopCgojIyMgb3V0ZXIgam9pbiBpbnB1dCByZWdpb24KbXVsdGlfY29ob3J0cyA8LSBSZWR1Y2UoCiAgZnVuY3Rpb24oeCx5LCAuLi4pIG1lcmdlKHgsIHksIGJ5ID0gImlucHV0UmVnaW9uIiwgYWxsID0gVFJVRSwgLi4uKSwKICBjb2hvcnRfbHMKKQpgYGAKCiMjIE1ldGEgYW5hbHlzaXMKCmBgYHtSLCBldmFsID0gRkFMU0V9CmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbChjb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLzIpCm1ldGFfZGYgPC0gcGx5cjo6YWRwbHkoCiAgLmRhdGEgPSBtdWx0aV9jb2hvcnRzLCAKICAubWFyZ2lucyA9IDEsIAogIC5mdW4gPSAgZnVuY3Rpb24ocm93T25lX2RmKXsKICAgIAogICAgZXN0IDwtIHJvd09uZV9kZltncmVwKCJlc3RpbWF0ZSIsY29sbmFtZXMocm93T25lX2RmKSldICU+JSBhcy5udW1lcmljCiAgICAKICAgIGRpcmVjdGlvbiA8LSAgcGFzdGUoaWZlbHNlKAogICAgICBpcy5uYShlc3QpLCAiLiIsCiAgICAgIGlmZWxzZShlc3QgPiAwLCAiKyIsICItIikKICAgICksY29sbGFwc2UgPSAiIikKICAgIAogICAgc2UgPC0gcm93T25lX2RmW2dyZXAoInNlIixjb2xuYW1lcyhyb3dPbmVfZGYpKV0gJT4lIGFzLm51bWVyaWMKICAgIGNvaG9ydCA8LSBnc3ViKCJfc2UiLCIiLGdyZXAoInNlIixjb2xuYW1lcyhyb3dPbmVfZGYpLHZhbHVlID0gVCkpCiAgICByb3dPbmVfcmVmb3JtX2RmIDwtIGRhdGEuZnJhbWUoCiAgICAgIGNvaG9ydCA9IGNvaG9ydCwKICAgICAgZXN0ID0gZXN0LAogICAgICBzZSA9IHNlLAogICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICkKICAgIAogICAgZiA8LSBtZXRhZ2VuKAogICAgICBURSA9IGVzdCwKICAgICAgc2VURSA9IHNlLAogICAgICBkYXRhID0gcm93T25lX3JlZm9ybV9kZgogICAgKQogICAgCiAgICB0aWJibGU6OnRpYmJsZSgKICAgICAgaW5wdXRSZWdpb24gPSByb3dPbmVfZGYkaW5wdXRSZWdpb24sCiAgICAgIGVzdGltYXRlID0gZiRURS5maXhlZCwKICAgICAgc2UgPSBmJHNlVEUuZml4ZWQsCiAgICAgIHBWYWwuZml4ZWQgPSBmJHB2YWwuZml4ZWQsCiAgICAgIHBWYWwucmFuZG9tID0gZiRwdmFsLnJhbmRvbSwKICAgICAgcFZhbFEgPSBmJHB2YWwuUSwKICAgICAgZGlyZWN0aW9uID0gZGlyZWN0aW9uCiAgICApCiAgfSAgLCAucHJvZ3Jlc3MgPSAidGltZSIsCiAgLnBhcmFsbGVsID0gVFJVRSwKICAuaWQgPSBOVUxMCikKCiMjIyBjcmVhdGUgZmluYWwgcFZhbAptZXRhX2RmJHBWYWwuZmluYWwgPC0gaWZlbHNlKAogIG1ldGFfZGYkcFZhbFEgPiAwLjA1LCBtZXRhX2RmJHBWYWwuZml4ZWQsIG1ldGFfZGYkcFZhbC5yYW5kb20KKQoKIyMjIGNhbGN1bGF0ZSBmZHIKbWV0YV9kZiRmZHIgPC0gcC5hZGp1c3QobWV0YV9kZiRwVmFsLmZpbmFsLCBtZXRob2QgPSAiZmRyIikKCiMjIyBvcmRlciBtZXRhX2RmCm1ldGFfZmluYWxfZGYgPC0gbWV0YV9kZlssIGMoZ3JlcCgiXyIsY29sbmFtZXMobWV0YV9kZiksaW52ZXJ0ID0gVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcCgiXyIsY29sbmFtZXMobWV0YV9kZiksaW52ZXJ0ID0gRikpXQptZXRhX2ZpbmFsX29yZGVyZWRfZGYgPC0gbWV0YV9maW5hbF9kZltvcmRlcihtZXRhX2ZpbmFsX2RmJHBWYWwuZmluYWwpLF0KYGBgCgojIyBBZGQgYW5ub3RhdGlvbiB0byBpbnB1dCByZWdpb25zCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQojIyMgZmluZCBhbm5vdGF0aW9ucwpsaWJyYXJ5KGNvTWV0aERNUikKCnNwbGl0ZWRfaW5wdXQgPC0gbWV0YV9maW5hbF9vcmRlcmVkX2RmICU+JSAKICB0aWR5cjo6c2VwYXJhdGUoY29sID0gaW5wdXRSZWdpb24saW50byA9ICBjKCJjaHJvbSIsICJzdGFydCIsICJlbmQiKSxyZW1vdmUgPSBGQUxTRSkKc3BsaXRlZF9pbnB1dF9hbm5vdCA8LSBBbm5vdGF0ZVJlc3VsdHMoc3BsaXRlZF9pbnB1dFssYygiY2hyb20iLCAic3RhcnQiLCAiZW5kIildLG5Db3Jlc19pbnQgPSAxMCkgCgojIyMgbWVyZ2UgYW5ub3RhdGlvbiB3aXRoIG1ldGEgYW5hbHlzaXMgZGF0YQptZXRhX29yZGVyZWRfd2l0aEFubm90X2RmIDwtIGNiaW5kKAogIG1ldGFfZmluYWxfb3JkZXJlZF9kZiwgc3BsaXRlZF9pbnB1dF9hbm5vdFssIDQ6N10KKQoKIyMjIG9yZGVyIGNvbHVtbnMKbWV0YV9vcmRlcmVkX3dpdGhBbm5vdF9kZiA8LSBtZXRhX29yZGVyZWRfd2l0aEFubm90X2RmICU+JSAKICBkcGx5cjo6c2VsZWN0KGMoMSwKICAgICAgICAgICAgICAgICAgKG5jb2wobWV0YV9vcmRlcmVkX3dpdGhBbm5vdF9kZikgLSAzKTpuY29sKG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGYpLAogICAgICAgICAgICAgICAgICAyOihuY29sKG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGYpIC0gNCkpCiAgKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CiMjIyBzYXZlIGRhdGFzZXQKd3JpdGUuY3N2KAogIG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGYsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfZGYuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQoKbWV0YV9hbGxfc2lnIDwtIG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGZbCiAgIWlzLm5hKG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGYkZmRyKSAmCiAgICAobWV0YV9vcmRlcmVkX3dpdGhBbm5vdF9kZiRmZHIgPCAwLjA1KSwgIAogIF0gI2RpbTogMzYgMzQKcm93Lm5hbWVzKG1ldGFfYWxsX3NpZykgPC0gTlVMTApgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CndyaXRlLmNzdigKICBtZXRhX2FsbF9zaWcsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2lnX2RmLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQojIyBPdmVybGFwIHdpdGggY29tYi1wIERNUnMKbWF0Y2hlZF9kbXJzIDwtIHJlYWQuY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpZ19kZi5jc3YiKQopCm1hdGNoZWRfZG1yc19nciA8LSBtYXRjaGVkX2RtcnMgJT4lCiAgdGlkeXI6OnNlcGFyYXRlKCJpbnB1dFJlZ2lvbiIsIGMoImNocm9tIiwgInN0YXJ0IiwgImVuZCIpKSAlPiUKICBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUoKQoKY29tYnBfZG1ycyA8LSByZWFkLnRhYmxlKAogIHBhc3RlMChkaXIuY29tYnAsICJjbmV3LnJlZ2lvbnMtcC5iZWQiKSwKICBoZWFkZXIgPSBUUlVFCikKY29sbmFtZXMoY29tYnBfZG1ycykgPC0gYygKICAiY2hyb20iLCAic3RhcnQiLCAiZW5kIiwgIm1pbl9wIiwgIm5fcHJvYmVzIiwgInpfcCIsICJ6X3NpZGFrX3AiCikKY29tYnBfc2lnIDwtIGNvbWJwX2RtcnMgJT4lIGZpbHRlcih6X3NpZGFrX3AgPCAwLjA1ICYgbl9wcm9iZXMgPiAyKQpjb21icF9zaWdfZ3IgPC0gbWFrZUdSYW5nZXNGcm9tRGF0YUZyYW1lKGNvbWJwX3NpZykKCm92ZXJsYXBwaW5nLnJlc3VsdHMgPC0gbWF0Y2hlZF9kbXJzWwogIHF1ZXJ5SGl0cyhmaW5kT3ZlcmxhcHMobWF0Y2hlZF9kbXJzX2dyLGNvbWJwX3NpZ19ncikpLApdCiNObyBvdmVybGFwIHdpdGggY29tYnAKCiMgb3ZlcmxhcHBpbmcucmVzdWx0cy51bmlxdWUgPC0gdW5pcXVlKG92ZXJsYXBwaW5nLnJlc3VsdHMpCiMgCiMgd3JpdGUuY3N2KAojICAgICBvdmVybGFwcGluZy5yZXN1bHRzLnVuaXF1ZSwKIyAgICAgcGFzdGUwKGRpci5jb21icCwgIm1hdGNoZWRfYW5hbHlzaXNfb3ZfY29tYl9wLmNzdiIpLAojICAgICByb3cubmFtZXMgPSBGQUxTRQojICkKYGBgCgojIyBEZWxldGUgc2lnIERNUnMgd2l0aCBjcm9zcy1oeWJyaWRpemluZyBvciBzbW9raW5nIHByb2JlcyAKCmBgYHtSLCBldmFsID0gRkFMU0V9CiMjIyBjYWxsIGluIGFsbCBjcm9zcyBoeWJyaWRpemluZyBwcm9iZXMKZWggPSBFeHBlcmltZW50SHViKCkKcXVlcnkoZWgsICJETVJjYXRlIikKY3Jvc3NoeWIgPC0gZWhbWyJFSDMxMjkiXV0KCiMjIyBHZXQgc2lnbmlmaWNhbnQgc21va2luZyBwcm9iZXMKc21va2luZy5maWxlIDwtICJodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM1MjY3MzI1L2Jpbi9OSUhNUzgxNzI3My1zdXBwbGVtZW50LTAwMTUwNl8tX1N1cHBsZW1lbnRhbF9UYWJsZXMueGxzeCIKCmlmKCFmaWxlLmV4aXN0cyhiYXNlbmFtZShzbW9raW5nLmZpbGUpKSkgZG93bmxvYWRlcjo6ZG93bmxvYWQoc21va2luZy5maWxlLGJhc2VuYW1lKHNtb2tpbmcuZmlsZSkpCgpzbW9raW5nIDwtIHJlYWR4bDo6cmVhZF94bHN4KAogIGJhc2VuYW1lKHNtb2tpbmcuZmlsZSksCiAgc2hlZXQgPSAiMDIiLAogIHNraXAgPSAyCikKc21va2luZy5zaWcucHJvYmVzIDwtIHNtb2tpbmcgJT4lIGRwbHlyOjpmaWx0ZXIoYFAtdmFsdWVgIDwgMSoxMF4oLTcpKSAlPiUgcHVsbCgiUHJvYmUgSUQiKSAKbGVuZ3RoKHNtb2tpbmcuc2lnLnByb2JlcykKCiMjIyBDYWxsIGluIG1ldGEgYW5hbHlzaXMgZmluYWwgcmVzdWx0cwptZXRhX2FsbCA8LSByZWFkLmNzdigKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIm1ldGFfYW5hbHlzaXNfbWF0Y2hlZF9zaWdfZGYuY3N2IikKKSAjZGltOiAzNiAzNAoKIyMjIEZpbmQgZmlsZXMgd2l0aCByZWdpb25zIGFuZCBwcm9iZXMKZmlsZXMgPC0gZGlyKAogIHBhdGggPSAiLi4vREFUQVNFVFMiLAogIHBhdHRlcm4gPSBwYXN0ZTAoIi4qX3Jlc2lkdWFsc19jb21ldGhfaW5wdXRfbHMucmRzIiksCiAgcmVjdXJzaXZlID0gVCwKICBmdWxsLm5hbWVzID0gVFJVRSwKICBpZ25vcmUuY2FzZSA9IFRSVUUKKQpmaWxlcyA8LSBncmVwKCJMT05ET05fUEZDfE10U2luYWl8Uk9TTUFQIixmaWxlcyx2YWx1ZSA9IFRSVUUsaWdub3JlLmNhc2UgPSBUUlVFKQoKIyMjIFJlYWQgZmlsZXMgYW5kIExpbWl0IHRoZSBjb2hvcnRfbHMgdG8gY29ob3J0X2NvTWV0aFJlZ2lvbiBpbiBtZXRhX2FsbCAKY29tZXRoLnByb2Jlcy5saXN0IDwtIGxhcHBseShmaWxlcywgZnVuY3Rpb24oZil7CiAgcHJpbnQoZikKICBhbGwuY2x1c3RlcnMgPC0gcmVhZFJEUyhmKSRjb01ldGhfbHMgIyBSZWFkIGZpbGVzCiAgY29ob3J0IDwtIGYgJT4lIGRpcm5hbWUgJT4lIGRpcm5hbWUgJT4lIGJhc2VuYW1lICMgZ2V0IGNvaG9ydCBmcm9tIGZvbGRlciBuYW1lCiAgCiAgY29sIDwtIGdyZXAocGFzdGUwKGNvaG9ydCwiX2NvTWV0aFJlZ2lvbiIpLAogICAgICAgICAgICAgIGNvbG5hbWVzKG1ldGFfYWxsKSwKICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IFRSVUUsCiAgICAgICAgICAgICAgdmFsdWUgPSBUUlVFKSAjIGdldCBjb2x1bW4gd2l0aCBjb2hvcnQgc2lnIHJlZ2lvbnMKICAKICAjIGtlZXAgc2lnIHJlZ2lvbnMgb25seQogIGFsbC5jbHVzdGVyc1tuYW1lcyhhbGwuY2x1c3RlcnMpICVpbiUgbWV0YV9hbGxbW2NvbF1dXQp9KQpuYW1lcyhjb21ldGgucHJvYmVzLmxpc3QpIDwtICBmaWxlcyAlPiUgZGlybmFtZSAlPiUgZGlybmFtZSAlPiUgYmFzZW5hbWUKCmxhcHBseShjb21ldGgucHJvYmVzLmxpc3QsbGVuZ3RoKQoKIyMjIE1hcCBwcm9iZXMgaW4gZWFjaCByZWdpb24gdG8gc21va2luZyBhbmQgY3Jvc3NoeWJyZGl6aW5nCmV4dHJhY3RDcm9zSHlicmlkaXphdGlvbiA8LSBmdW5jdGlvbihwcm9iZXMubGlzdCl7CiAgY3Jvc3NoeWIuZXh0cmFjdGVkIDwtIHBseXI6OmxhcGx5KHByb2Jlcy5saXN0LGZ1bmN0aW9uKHByb2Jlcyl7CiAgICBwYXN0ZShpbnRlcnNlY3QocHJvYmVzLCBjcm9zc2h5YiksIAogICAgICAgICAgY29sbGFwc2UgPSAiLCAiCiAgICApCiAgfSkKICBzbW9raW5nLmV4dHJhY3RlZCA8LSBwbHlyOjpsYXBseShwcm9iZXMubGlzdCxmdW5jdGlvbihwcm9iZXMpewogICAgcGFzdGUoaW50ZXJzZWN0KHByb2Jlcywgc21va2luZy5zaWcucHJvYmVzKSwgCiAgICAgICAgICBjb2xsYXBzZSA9ICIsICIKICAgICkKICB9KQogIHRpYmJsZTo6dGliYmxlKAogICAgImNvTWV0aFJlZ2lvbiIgPSBuYW1lcyhwcm9iZXMubGlzdCksCiAgICAiY3Jvc3NIeWIiID0gY3Jvc3NoeWIuZXh0cmFjdGVkLCAKICAgICJjcm9zc0h5Yl9iaSIgPSBpZmVsc2UoY3Jvc3NoeWIuZXh0cmFjdGVkID09ICIiLDAsMSksCiAgICAic21va2UiID0gc21va2luZy5leHRyYWN0ZWQsCiAgICAic21va2VfYmkiID0gaWZlbHNlKHNtb2tpbmcuZXh0cmFjdGVkID09ICIiLDAsMSkKICApCn0KCkxvbmRvbl9QRkNfY3Jvc3NIeWJfZGYgPC0gZXh0cmFjdENyb3NIeWJyaWRpemF0aW9uKGNvbWV0aC5wcm9iZXMubGlzdCRMT05ET04pCmNvbG5hbWVzKExvbmRvbl9QRkNfY3Jvc3NIeWJfZGYpIDwtIHBhc3RlMCgiTG9uZG9uXyIsY29sbmFtZXMoTG9uZG9uX1BGQ19jcm9zc0h5Yl9kZikpCnBseXI6OmNvdW50KAogIExvbmRvbl9QRkNfY3Jvc3NIeWJfZGYsIAogIHZhcnMgPSBncmVwKCJfYmkiLGNvbG5hbWVzKExvbmRvbl9QRkNfY3Jvc3NIeWJfZGYpLHZhbHVlID0gVFJVRSkKKQoKTXRTaW5haV9jcm9zc0h5Yl9kZiA8LSBleHRyYWN0Q3Jvc0h5YnJpZGl6YXRpb24oY29tZXRoLnByb2Jlcy5saXN0JE10U2luYWkpCmNvbG5hbWVzKE10U2luYWlfY3Jvc3NIeWJfZGYpIDwtIHBhc3RlMCgiTXRTaW5haV8iLGNvbG5hbWVzKE10U2luYWlfY3Jvc3NIeWJfZGYpKQpwbHlyOjpjb3VudCgKICBNdFNpbmFpX2Nyb3NzSHliX2RmLCAKICB2YXJzID0gZ3JlcCgiX2JpIixjb2xuYW1lcyhNdFNpbmFpX2Nyb3NzSHliX2RmKSx2YWx1ZSA9IFRSVUUpCikKClJPU01BUF9jcm9zc0h5Yl9kZiA8LSBleHRyYWN0Q3Jvc0h5YnJpZGl6YXRpb24oY29tZXRoLnByb2Jlcy5saXN0JFJPU01BUCkKY29sbmFtZXMoUk9TTUFQX2Nyb3NzSHliX2RmKSA8LSBwYXN0ZTAoIlJPU01BUF8iLGNvbG5hbWVzKFJPU01BUF9jcm9zc0h5Yl9kZikpCnBseXI6OmNvdW50KFJPU01BUF9jcm9zc0h5Yl9kZiwgdmFycyA9IGdyZXAoIl9iaSIsY29sbmFtZXMoUk9TTUFQX2Nyb3NzSHliX2RmKSx2YWx1ZSA9IFRSVUUpKQoKIyMjIE1lcmdlIHNtb2tpbmcgYW5kIGNyb3NzSHliIHByb2JlcyBpbmZvcm1hdGlvbiB3aXRoIG1ldGEgYW5hbHlzaXMgcmVzdWx0cyAKbWV0YV9hbGxfZmluYWwgPC0gbWV0YV9hbGwgJT4lCiAgbGVmdF9qb2luKExvbmRvbl9QRkNfY3Jvc3NIeWJfZGYpICU+JSAKICBsZWZ0X2pvaW4oTXRTaW5haV9jcm9zc0h5Yl9kZikgJT4lIAogIGxlZnRfam9pbihST1NNQVBfY3Jvc3NIeWJfZGYpCgojIyMgQWRkIGluZm9ybWF0aW9uIHdpdGggaW5wdXQgcmVnaW9ucyB3aXRoIGFueSBjcm9zcyBoeWJyaWRpemF0aW9uIGluIGNvaG9ydHMgCm1ldGFfYWxsX2ZpbmFsJGNyb3NzSHliX2JpIDwtIHJvd1N1bXMobWV0YV9hbGxfZmluYWxbLGdyZXAoImNyb3NzSHliX2JpIixjb2xuYW1lcyhtZXRhX2FsbF9maW5hbCkpXSxuYS5ybSA9IFRSVUUpID4gMAptZXRhX2FsbF9maW5hbCRzbW9rZV9iaSA8LSByb3dTdW1zKG1ldGFfYWxsX2ZpbmFsWyxncmVwKCJzbW9rZV9iaSIsY29sbmFtZXMobWV0YV9hbGxfZmluYWwpKV0sbmEucm0gPSBUUlVFKSA+IDAKCiMgU29ydCBieSByZWdpb24gbWV0YSBhbmFseXNpcyBGRFIKIyBDbHVzdGVyIGNvbHVtbnMgb2YgdGhlIHByb2plY3RzIHRvZ2V0aGVyCm1ldGFfYWxsX2ZpbmFsIDwtIG1ldGFfYWxsX2ZpbmFsWwogIG9yZGVyKG1ldGFfYWxsX2ZpbmFsJGZkciksCiAgYyhncmVwKCJNdFNpbmFpfExvbmRvbnxST1NNQVAiLGNvbG5hbWVzKG1ldGFfYWxsX2ZpbmFsKSxpZ25vcmUuY2FzZSA9IFRSVUUsaW52ZXJ0ID0gVFJVRSksCiAgICBncmVwKCJHYXNwYXJvbmkiLGNvbG5hbWVzKG1ldGFfYWxsX2ZpbmFsKSxpZ25vcmUuY2FzZSA9IFRSVUUpLAogICAgZ3JlcCgiTXRTaW5haSIsY29sbmFtZXMobWV0YV9hbGxfZmluYWwpLGlnbm9yZS5jYXNlID0gVFJVRSksCiAgICBncmVwKCJMb25kb24iLGNvbG5hbWVzKG1ldGFfYWxsX2ZpbmFsKSxpZ25vcmUuY2FzZSA9IFRSVUUpLAogICAgZ3JlcCgiUk9TTUFQIixjb2xuYW1lcyhtZXRhX2FsbF9maW5hbCksaWdub3JlLmNhc2UgPSBUUlVFKQogICkKIF0Kc3RyKG1ldGFfYWxsX2ZpbmFsKQoKbWV0YV9zaWdfZmluYWwgPC0gbWV0YV9hbGxfZmluYWxbCiAgbWV0YV9hbGxfZmluYWwkY3Jvc3NIeWJfYmkgPT0gMCAmCiAgICBtZXRhX2FsbF9maW5hbCRzbW9rZV9iaSA9PSAwLApdICNkaW06IDMyIDQ4CmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KIyMjIFNhdmUKd3JpdGUuY3N2KAogIG1ldGFfc2lnX2ZpbmFsICU+JSBhcy5kYXRhLmZyYW1lLAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKYGBgCgojIyBSZXN1bHRzCgpgYGB7UiwgZWNobyA9IEZBTFNFfQpyZXMgPC0gcmVhZHI6OnJlYWRfY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmLmNzdiIpLAogIGNvbF90eXBlcyA9IHJlYWRyOjpjb2xzKCkKKSAKcmVzIDwtIHJlc1tvcmRlcihyZXMkZmRyKSxdCnJlcwpgYGAKCgpgYGB7UiwgZXZhbCA9IEZBTFNFLCBpbmNsdWRlID0gRkFMU0V9CiMjIENvbXBhcmUgd2l0aCBzaWcgRE1ScyBmcm9tIG1haW4gYW5hbHlzaXMKCiMjIyBDYWxsIGluIGRhdGFzZXRzCm1haW5fZG1ycyA8LSByZWFkLmNzdigKICBwYXN0ZTAoZGlyLmRtciwgIm1ldGFfYW5hbHlzaXNfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfb3ZfY29tYl9wX3dpdGhfc2lnX3NpbmdsZV9jcGdzLmNzdiIpCikgI2RpbTogMTE5IDYwCgptYXRjaGVkX2RtcnMgPC0gcmVhZC5jc3YoCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYuY3N2IikKKSAjZGltOiA4MyA1OQoKIyMjIFR1cm4gaW5wdXQgcmVnaW9ucyBpbnRvIGdyYW5nZXMKbWFpbl9kbXJzX2dyIDwtIG1haW5fZG1ycyAlPiUKICB0aWR5cjo6c2VwYXJhdGUoImlucHV0UmVnaW9uIixjKCJjaHJvbSIsInN0YXJ0IiwiZW5kIikpICU+JQogIG1ha2VHUmFuZ2VzRnJvbURhdGFGcmFtZSgpICU+JQogIHVuaXF1ZQoKbWF0Y2hlZF9kbXJzX2dyIDwtIG1hdGNoZWRfZG1ycyAlPiUKICB0aWR5cjo6c2VwYXJhdGUoImlucHV0UmVnaW9uIiwgYygiY2hyb20iLCAic3RhcnQiLCAiZW5kIikpICU+JQogIG1ha2VHUmFuZ2VzRnJvbURhdGFGcmFtZSgpICU+JQogIHVuaXF1ZQoKIyMjIENyZWF0ZSBWZW5uIERpYWdyYW0KbGlicmFyeShDaElQcGVha0Fubm8pCmxpYnJhcnkoZ2dwbG90MikKCm1ldGhvZHMgICAgICA8LSBjKCJtYWluIGFuYWx5c2lzIiwgIm1hdGNoZWQgYW5hbHlzaXMiKQptZXRob2RzTGFiZWwgPC0gYygibWFpbiBhbmFseXNpcyBzaWcgRE1ScyIsICJtYXRjaGVkIGFuYWx5c2lzIHNpZyBETVJzIikKCm4gPC0gbGVuZ3RoKG1ldGhvZHMpCgpnZ19jb2xvcl9odWUgPC0gZnVuY3Rpb24obil7CiAgaHVlcyA9IHNlcSgxNSwgMzc1LCBsZW5ndGggPSBuICsgMSkKICBoY2woaCA9IGh1ZXMsIGwgPSA2NSwgYyA9IDEwMClbMTpuXQp9Cgpjb2xzID0gZ2dfY29sb3JfaHVlKG4pCgpyYW5nZXMubGlzdCA8LSBsaXN0KG1haW5fZG1yc19nciwgbWF0Y2hlZF9kbXJzX2dyKQoKIyBwZGYoCiMgIGZpbGUgPSBwYXN0ZTAoZGlyLnJlc3VsdCwidmVubl9tYWluX21hdGNoZWRfc2lnX0RNUnMucGRmIiksCiMgIHdpZHRoID0gNSwgaGVpZ2h0ID0gNQojKQptYWtlVmVubkRpYWdyYW0oCiAgcmFuZ2VzLmxpc3QsCiAgTmFtZU9mUGVha3MgPSBtZXRob2RzTGFiZWxbMTpuXSwKICB0b3RhbFRlc3QgPSAzNzE1OSwgCiAgYnkgPSAicmVnaW9uIiwKICBtYWluID0gcGFzdGUwKCIgIiksCiAgY29sID0gYygiIzQ0MDE1NGZmIiwgJyMyMTkwOGRmZicpLAogIGZpbGwgPSBjKGFscGhhKCIjNDQwMTU0ZmYiLDAuMyksIGFscGhhKCcjMjE5MDhkZmYnLDAuMykpLAogIGNleCA9IDEsCiAgZm9udGZhbWlseSA9ICJzYW5zIiwKICBjYXQuY2V4ID0gMSwKICBjYXQuZGVmYXVsdC5wb3MgPSAib3V0ZXIiLAogIGNhdC5wb3MgPSBjKDE4MCwgMTgwKSwKICBjYXQuZGlzdCA9IGMoLTAuMDU1LCAtMC4wNTUpLAogIGNhdC5mb250ZmFtaWx5ID0gInNhbnMiLAogIGNhdC5jb2wgPSBjKCIjNDQwMTU0ZmYiLCAnIzIxOTA4ZGZmJykpCiMgZGV2Lm9mZigpCmBgYAoKIyBNZXRhLWFuYWx5c2lzIFNpbmdsZSBDcEdzCgoxLiBtZXJnZSBjb2hvcnRzCjIuIG1ldGEgYW5hbHlzaXMKMy4gYWRkIGFubm90YXRpb24KNC4gRGVsZXRlIGNyb3NzLWh5YnJpZGl6aW5nIGFuZCBzbW9raW5nIHByb2JlcyBmcm9tIHNpZyBwcm9iZXMKCiMjIEltcG9ydCBkYXRhc2V0cyAgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpyZXN1bHRzLmZpbGVzIDwtIGRpcigKICBwYXRoID0gIi4vREFUQVNFVFMvbWF0Y2hlZF9hbmFseXNpc19yZXN1bHRzLyIsCiAgcGF0dGVybiA9ICJzaW5nbGVfY3BnX2xpbmVhcl9kZi5jc3YiLAogIHJlY3Vyc2l2ZSA9IFRSVUUsCiAgZnVsbC5uYW1lcyA9IFRSVUUsCiAgaWdub3JlLmNhc2UgPSBUUlVFCikKCmZvcihpIGluIHJlc3VsdHMuZmlsZXMpewogIGRhdGEgPC0gcmVhZHI6OnJlYWRfY3N2KGkpCiAgZGF0YXNldCA8LSB1bmxpc3Qoc3RyaW5ncjo6c3RyX3NwbGl0KGJhc2VuYW1lKGkpLCJcXC98XFxfIikpWzFdICAlPiUgYXMuY2hhcmFjdGVyKCkKICBhdXggPC0gcGFzdGUwKGRhdGFzZXQsYygiX2VzdGltYXRlIiwgIl9zZSIsICJfcFZhbHVlIikpCiAgY29sbmFtZXMoZGF0YSkgPC0gYygiY3BnIiwgYXV4KQogIGFzc2lnbihkYXRhc2V0LGRhdGEpCn0KYGBgCgojIyBNZXJnZSBjb2hvcnRzCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpjb2hvcnRfbHMgPC0gbGlzdCgKICBMb25kb25fUEZDID0gTG9uZG9uLAogIE10U2luYWkgPSBNdFNpbmFpLAogIFJPU01BUCA9IFJPU01BUAopCgojIyMgb3V0ZXIgam9pbiBpbnB1dCByZWdpb24KbXVsdGlfY29ob3J0cyA8LSBSZWR1Y2UoCiAgZnVuY3Rpb24oeCx5LCAuLi4pIG1lcmdlKHgsIHksIGJ5ID0gImNwZyIsIGFsbCA9IFRSVUUsIC4uLiksCiAgY29ob3J0X2xzCikgI2RpbTogNDUwNzkzIDEwCmRpbShtdWx0aV9jb2hvcnRzKQpgYGAKCiMjIE1ldGEgYW5hbHlzaXMgIAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KIyMjIGNhbGN1bGF0ZSBtZXRhIGFuYWx5c2lzIHogc2NvcmVzIGFuZCBwIHZhbHVlcwpkb1BhcmFsbGVsOjpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKS8yKQptZXRhX2RmIDwtIHBseXI6OmFkcGx5KAogIC5kYXRhID0gbXVsdGlfY29ob3J0cywgCiAgLm1hcmdpbnMgPSAxLCAKICAuZnVuID0gIGZ1bmN0aW9uKHJvd09uZV9kZil7CiAgICAKICAgIGVzdCA8LSByb3dPbmVfZGZbZ3JlcCgiZXN0aW1hdGUiLGNvbG5hbWVzKHJvd09uZV9kZikpXSAlPiUgYXMubnVtZXJpYwogICAgCiAgICBkaXJlY3Rpb24gPC0gIHBhc3RlKGlmZWxzZSgKICAgICAgaXMubmEoZXN0KSwgIi4iLAogICAgICBpZmVsc2UoZXN0ID4gMCwgIisiLCAiLSIpCiAgICApLGNvbGxhcHNlID0gIiIpCiAgICAKICAgIHNlIDwtIHJvd09uZV9kZltncmVwKCJzZSIsY29sbmFtZXMocm93T25lX2RmKSldICU+JSBhcy5udW1lcmljCiAgICBjb2hvcnQgPC0gZ3N1YigiX3NlIiwiIixncmVwKCJzZSIsY29sbmFtZXMocm93T25lX2RmKSx2YWx1ZSA9IFQpKQogICAgcm93T25lX3JlZm9ybV9kZiA8LSBkYXRhLmZyYW1lKAogICAgICBjb2hvcnQgPSBjb2hvcnQsCiAgICAgIGVzdCA9IGVzdCwKICAgICAgc2UgPSBzZSwKICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICApCiAgICAKICAgIGYgPC0gbWV0YWdlbigKICAgICAgVEUgPSBlc3QsCiAgICAgIHNlVEUgPSBzZSwKICAgICAgZGF0YSA9IHJvd09uZV9yZWZvcm1fZGYKICAgICkKICAgIAogICAgdGliYmxlOjp0aWJibGUoCiAgICAgIGNwZyA9IHJvd09uZV9kZiRjcGcsCiAgICAgIGVzdGltYXRlID0gZiRURS5maXhlZCwKICAgICAgc2UgPSBmJHNlVEUuZml4ZWQsCiAgICAgIHBWYWwuZml4ZWQgPSBmJHB2YWwuZml4ZWQsCiAgICAgIHBWYWwucmFuZG9tID0gZiRwdmFsLnJhbmRvbSwKICAgICAgcFZhbFEgPSBmJHB2YWwuUSwKICAgICAgZGlyZWN0aW9uID0gZGlyZWN0aW9uCiAgICApCiAgfSAgLCAucHJvZ3Jlc3MgPSAidGltZSIsCiAgLnBhcmFsbGVsID0gVFJVRSwKICAuaWQgPSBOVUxMCikKCiMjIyBjcmVhdGUgZmluYWwgcFZhbAptZXRhX2RmJHBWYWwuZmluYWwgPC0gaWZlbHNlKAogIG1ldGFfZGYkcFZhbFEgPiAwLjA1LCBtZXRhX2RmJHBWYWwuZml4ZWQsIG1ldGFfZGYkcFZhbC5yYW5kb20KKQoKIyMjIGNhbGN1bGF0ZSBmZHIKbWV0YV9kZiRmZHIgPC0gcC5hZGp1c3QobWV0YV9kZiRwVmFsLmZpbmFsLCBtZXRob2QgPSAiZmRyIikKCiMjIyBvcmRlciBtZXRhX2RmCm1ldGFfZmluYWxfZGYgPC0gbWV0YV9kZlssIGMoZ3JlcCgiXyIsY29sbmFtZXMobWV0YV9kZiksaW52ZXJ0ID0gVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcCgiXyIsY29sbmFtZXMobWV0YV9kZiksaW52ZXJ0ID0gRikpXQptZXRhX2ZpbmFsX29yZGVyZWRfZGYgPC0gbWV0YV9maW5hbF9kZltvcmRlcihtZXRhX2ZpbmFsX2RmJHBWYWwuZmluYWwpLF0KYGBgCgojIyBBZGQgYW5ub3RhdGlvbiAgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHNlc2FtZSkKcHJvYmVzLmluZm8gPC0gc2VzYW1lRGF0YUdldCgiSE00NTAuaGcxOS5tYW5pZmVzdCIpCnByb2Jlcy5pbmZvIDwtIHByb2Jlcy5pbmZvW21ldGFfZmluYWxfb3JkZXJlZF9kZiRjcGcgJT4lIGFzLmNoYXJhY3RlcigpXSAlPiUgCiAgYXMuZGF0YS5mcmFtZSAlPiUgCiAgZHBseXI6OnNlbGVjdChjKCJzZXFuYW1lcyIsInN0YXJ0IiwiZW5kIikpCgpyZXN1bHRfYW5ub3RfZGYgPC0gbWVyZ2UoCiAgeSA9IG1ldGFfZmluYWxfb3JkZXJlZF9kZiwKICB4ID0gcHJvYmVzLmluZm8sCiAgYnkueSA9ICJjcGciLAogIGJ5LnggPSAicm93Lm5hbWVzIiwKICBhbGwueSA9IFRSVUUsCiAgc29ydCA9IEZBTFNFCikKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQojIyMgZmluYWwgcmF3IGRhdGEKd3JpdGUuY3N2KAogIHJlc3VsdF9hbm5vdF9kZiAgJT4lIGFzLmRhdGEuZnJhbWUoKSwKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIm1ldGFfYW5hbHlzaXNfbWF0Y2hlZF9zaW5nbGVfY3BnX2RmLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQojIyMgcHJlcGFyZSBkYXRhIGZvciBjb21iLXAKcmVzdWx0X2Zvcl9jb21icF9kZiA8LSByZXN1bHRfYW5ub3RfZGZbCiAgLCBjKCJzZXFuYW1lcyIsICJzdGFydCIsICJlbmQiLCAicFZhbC5maW5hbCIpCiAgXQpjb2xuYW1lcyhyZXN1bHRfZm9yX2NvbWJwX2RmKVtjKDEsNCldIDwtIGMoImNociIsICJwVmFsdWUiKQpyZXN1bHRfZm9yX2NvbWJwX2RmJGNociA8LSBhcy5jaGFyYWN0ZXIocmVzdWx0X2Zvcl9jb21icF9kZiRjaHIpCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0Kd3JpdGUuY3N2KAogIHJlc3VsdF9mb3JfY29tYnBfZGYsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2luZ2xlX2NwZ19kZl9mb3JfY29tYnAuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQpgYGAKCiMjIERlbGV0ZSBjcm9zcy1oeWJyaWRpemluZyBhbmQgc21va2luZyBwcm9iZXMgZnJvbSBzaWcgcHJvYmVzIAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KIyMjIEV4Y2x1ZGUgbm9uLXNpZ25pZmljYW50IHByb2JlcwpyZXN1bHRfYW5ub3Rfc2lnX2RmIDwtIHJlc3VsdF9hbm5vdF9kZiAlPiUgZmlsdGVyKGZkciA8IDAuMDUpIApgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CndyaXRlLmNzdigKICByZXN1bHRfYW5ub3Rfc2lnX2RmICU+JSBhcy5kYXRhLmZyYW1lKCksCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2luZ2xlX2NwZ19zaWdfZGYuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CiMjIyBHZXQgY3Jvc3NoeWJyZGl6aW5nIHByb2JlcwpsaWJyYXJ5KEV4cGVyaW1lbnRIdWIpCgplaCA9IEV4cGVyaW1lbnRIdWIoKQpxdWVyeShlaCwgIkRNUmNhdGUiKQpjcm9zc2h5YiA8LSBlaFtbIkVIMzEyOSJdXQoKIyMjIEdldCBzaWduaWZpY2FudCBzbW9raW5nIHByb2JlcwpzbW9raW5nLmZpbGUgPC0gImh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzUyNjczMjUvYmluL05JSE1TODE3MjczLXN1cHBsZW1lbnQtMDAxNTA2Xy1fU3VwcGxlbWVudGFsX1RhYmxlcy54bHN4IgoKaWYoIWZpbGUuZXhpc3RzKGJhc2VuYW1lKHNtb2tpbmcuZmlsZSkpKSBkb3dubG9hZGVyOjpkb3dubG9hZChzbW9raW5nLmZpbGUsYmFzZW5hbWUoc21va2luZy5maWxlKSkKCnNtb2tpbmcgPC0gcmVhZHhsOjpyZWFkX3hsc3goCiAgYmFzZW5hbWUoc21va2luZy5maWxlKSwKICBzaGVldCA9ICIwMiIsCiAgc2tpcCA9IDIKKQpzbW9raW5nLnNpZy5wcm9iZXMgPC0gc21va2luZyAlPiUgZHBseXI6OmZpbHRlcihgUC12YWx1ZWAgPCAxKjEwXigtNykpICU+JSBwdWxsKCJQcm9iZSBJRCIpCgoKIyMjIEV4Y2x1ZGUgY3Jvc3MtaHlicmlkaXppbmcgYW5kIHNtb2tpbmcgcHJvYmVzCnJlc3VsdF9hbm5vdF9zaWdfZGYkUm93Lm5hbWVzIDwtIGFzLmNoYXJhY3RlcihyZXN1bHRfYW5ub3Rfc2lnX2RmJFJvdy5uYW1lcykKCnJlc3VsdF9hbm5vdF9zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZiA8LSByZXN1bHRfYW5ub3Rfc2lnX2RmWwogICEoKHJlc3VsdF9hbm5vdF9zaWdfZGYkUm93Lm5hbWVzICVpbiUgY3Jvc3NoeWIpIHwKICAgICAgKHJlc3VsdF9hbm5vdF9zaWdfZGYkUm93Lm5hbWVzICVpbiUgc21va2luZy5zaWcucHJvYmVzKSksCiAgXSAjZGltOiA2NDIgMjQKCiMjIyBBZGQgYW5ub3RhdGlvbgpsaWJyYXJ5KGNvTWV0aERNUikKcmVzdWx0X2Fubm90X3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmJGNocm9tIDwtIGFzLmNoYXJhY3RlcigKICByZXN1bHRfYW5ub3Rfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYkc2VxbmFtZXMKKQpyZXN1bHRfZmluYWwgPC0gQW5ub3RhdGVSZXN1bHRzKHJlc3VsdF9hbm5vdF9zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZikKCnJlc3VsdF9maW5hbF9vcmRlcmVkIDwtIHJlc3VsdF9maW5hbFsKICAsYygxOjQsIDIzOjI2LCA1OjIxKQpdCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0Kd3JpdGUuY3N2KAogIHJlc3VsdF9maW5hbF9vcmRlcmVkICU+JSBhcy5kYXRhLmZyYW1lKCksCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2luZ2xlX2NwZ19zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZi5jc3YiKSwKICByb3cubmFtZXMgPSBGQUxTRQopICAgICAgICAgICAKYGBgCgojIyBSZXN1bHRzCgpgYGB7UiwgZWNobyA9IEZBTFNFfQpyZXMgPC0gcmVhZHI6OnJlYWRfY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpbmdsZV9jcGdfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYuY3N2IiksCiAgY29sX3R5cGVzID0gcmVhZHI6OmNvbHMoKQopIApyZXMgPC0gcmVzW29yZGVyKHJlcyRmZHIpLF0KcmVzCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQojIyBDb21wYXJlIHdpdGggc2lnIENwR3MgZnJvbSBtYWluIGFuYWx5c2lzCgojIyMgQ2FsbCBpbiBkYXRhc2V0cwptYWluX2NwZ3MgPC0gcmVhZC5jc3YoCiAgcGFzdGUwKGRpci5zaW5nbGUuY3BnLCAibWV0YV9hbmFseXNpc19zaW5nbGVfY3BnX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmLmNzdiIpCikgI2RpbTogMzc1MSAzMgptYWluX2NwZ3MgPC0gbWFpbl9jcGdzICU+JSBwdWxsKGNwZykgJT4lIGFzLmNoYXJhY3RlcgoKbWF0Y2hlZF9jcGdzIDwtIHJlYWQuY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpbmdsZV9jcGdfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYuY3N2IikKKSAjZGltOiAxNTEgMjUKbWF0Y2hlZF9jcGdzIDwtIG1hdGNoZWRfY3BncyAlPiUgcHVsbCgxKSAlPiUgYXMuY2hhcmFjdGVyCgoKIyMjIENyZWF0ZSBWZW5uIERpYWdyYW0KbGlicmFyeShWZW5uRGlhZ3JhbSkKbGlicmFyeShnZ3Bsb3QyKQoKIyBNYWtlIHRoZSBwbG90CnZlbm4uZGlhZ3JhbSgKICB4ID0gbGlzdCgKICAgIG1haW5fY3BncyAlPiUgdW5pcXVlICwKICAgIG1hdGNoZWRfY3BncyAlPiUgdW5pcXVlCiAgKSwgCiAgY2F0ZWdvcnkubmFtZXMgPSBjKCJNYWluIGFuYWx5c2lzIHNpZy4gcHJvYmVzIiAsICJNYXRjaGVkIGFuYWx5c2lzIHNpZy4gcHJvYmVzIiApLAogIGZpbGVuYW1lID0gZmlsZS5wYXRoKGRpci5yZXN1bHQsICcvdmVubl9tYWluX21hdGNoZWRfc2lnX2NwZ3MucG5nJyksCiAgb3V0cHV0ID0gVFJVRSAsCiAgaW1hZ2V0eXBlID0gInBuZyIgLAogIGhlaWdodCA9IDcwMCAsCiAgd2lkdGggPSA3MDAgLAogIHJlc29sdXRpb24gPSAzMDAsCiAgY29tcHJlc3Npb24gPSAibHp3IiwKICBsd2QgPSAxLAogIGNvbCA9IGMoIiM0NDAxNTRmZiIsICcjMjE5MDhkZmYnKSwKICBmaWxsID0gYyhhbHBoYSgiIzQ0MDE1NGZmIiwwLjMpLCBhbHBoYSgnIzIxOTA4ZGZmJywwLjMpKSwKICBjZXggPSAwLjUsCiAgY2F0LmNleCA9IDAuMywKICBjYXQuZGVmYXVsdC5wb3MgPSAib3V0ZXIiLAogIGNhdC5wb3MgPSBjKDAsIDApLAogIGNhdC5kaXN0ID0gYygwLjAxLCAwLjAxKQopCmBgYAoKIyBDb21wYXJlIG1hdGNoZWQtYW5hbHlzaXMgcmVzdWx0cyB3aXRoIG1haW4gbWV0YS1hbmFseXNpcyByZXN1bHRzCgpgYGB7Un0Kc2lnX2NwZ3MgPC0gcmVhZHhsOjpyZWFkX3hsc3goCiAgICBwYXRoID0gZGlyKGRpci5yZXZpc2lvbixwYXR0ZXJuID0gIkFMTCIsZnVsbC5uYW1lcyA9IFRSVUUpLAogICAgc2hlZXQgPSAiU3VwcCBUYWJsZSAxIiwKICAgIHNraXAgPSAzCikKCnNpZ19kbXJzIDwtIHJlYWR4bDo6cmVhZF94bHN4KAogICAgcGF0aCA9IGRpcihkaXIucmV2aXNpb24scGF0dGVybiA9ICJBTEwiLGZ1bGwubmFtZXMgPSBUUlVFKSwKICAgIHNoZWV0ID0gIlN1cHAgVGFibGUgMiIsCiAgICBza2lwID0gMwopCgptYXRjaGVkX2NwZ3MgPC0gcmVhZC5jc3YoCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2luZ2xlX2NwZ19zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZi5jc3YiKQopCgptYXRjaGVkX2RtcnMgPC0gcmVhZC5jc3YoCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYuY3N2IikKKQpgYGAKCmBgYHtSfQpzaWdfY3BncyA8LSBzaWdfY3Bnc1ssIDE6MTddCm1hdGNoZWRfY3BncyA8LSBtYXRjaGVkX2NwZ3NbLCBjKDEsIDk6MTYpXQpjb2xuYW1lcyhtYXRjaGVkX2NwZ3MpIDwtIGMoCiAgICAiY3BnIiwgcGFzdGUwKCJtYXRjaGVkXyIsIGNvbG5hbWVzKG1hdGNoZWRfY3BncylbMjpuY29sKG1hdGNoZWRfY3BncyldKQopCnNpZ19tYXRjaGVkX2NwZ3MgPC0gbWVyZ2UoCiAgICBzaWdfY3BncywgCiAgICBtYXRjaGVkX2NwZ3MsCiAgICBieSA9ICJjcGciLAogICAgc29ydCA9IEZBTFNFCikKbnJvdyhzaWdfbWF0Y2hlZF9jcGdzKQoKbWV0YV9jcGdfZGlyZWN0aW9uIDwtIGlmZWxzZSgKICAgIHNpZ19tYXRjaGVkX2NwZ3MkZXN0aW1hdGUgPiAwLCAiKyIsICItIgopCm1hdGNoZWRfY3BnX2RpcmVjdGlvbiA8LSBpZmVsc2UoCiAgICBzaWdfbWF0Y2hlZF9jcGdzJG1hdGNoZWRfZXN0aW1hdGUgPiAwLCAiKyIsICItIgopCnRhYmxlKG1ldGFfY3BnX2RpcmVjdGlvbiA9PSBtYXRjaGVkX2NwZ19kaXJlY3Rpb24pCmBgYAoKYGBge1J9CnNpZ19kbXJzIDwtIHNpZ19kbXJzWywgMToxNV0KbWF0Y2hlZF9kbXJzIDwtIG1hdGNoZWRfZG1yc1ssIGMoMSwgNjoxMyldCmNvbG5hbWVzKG1hdGNoZWRfZG1ycykgPC0gYygKICAgICJETVIiLCBwYXN0ZTAoIm1hdGNoZWRfIiwgY29sbmFtZXMobWF0Y2hlZF9kbXJzKVsyOm5jb2wobWF0Y2hlZF9kbXJzKV0pCikKc2lnX21hdGNoZWRfZG1ycyA8LSBtZXJnZSgKICAgIHNpZ19kbXJzLCBtYXRjaGVkX2RtcnMsCiAgICBieSA9ICJETVIiLAogICAgc29ydCA9IEZBTFNFCikKbnJvdyhzaWdfbWF0Y2hlZF9kbXJzKQoKbWV0YV9kbXJfZGlyZWN0aW9uIDwtIGlmZWxzZSgKICAgIHNpZ19tYXRjaGVkX2RtcnMkZXN0aW1hdGUgPiAwLCAiKyIsICItIgopCm1hdGNoZWRfZG1yX2RpcmVjdGlvbiA8LSBpZmVsc2UoCiAgICBzaWdfbWF0Y2hlZF9kbXJzJG1hdGNoZWRfZXN0aW1hdGUgPiAwLCAiKyIsICItIgopCnRhYmxlKG1ldGFfZG1yX2RpcmVjdGlvbiA9PSBtYXRjaGVkX2Rtcl9kaXJlY3Rpb24pCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0Kd3JpdGUuY3N2KAogICBzaWdfbWF0Y2hlZF9jcGdzLAogICBwYXN0ZTAoZGlyLnJlc3VsdCwgIm1ldGFfbWF0Y2hlZF9jb21tb25fY3Bncy5jc3YiKSwKICAgcm93Lm5hbWVzID0gRkFMU0UKKQoKd3JpdGUuY3N2KAogICBzaWdfbWF0Y2hlZF9kbXJzLAogICBwYXN0ZTAoZGlyLnJlc3VsdCwgIm1ldGFfbWF0Y2hlZF9jb21tb25fZG1ycy5jc3YiKSwKICAgcm93Lm5hbWVzID0gRkFMU0UKKQpgYGAKCiMgU2Vzc2lvbiBpbmZvcm1hdGlvbgoKYGBge1J9CmRldnRvb2xzOjpzZXNzaW9uX2luZm8oKQpgYGA=