1 Main libraries and configuration

dir.result <- "matched_analysis_results/"
dir.dmr <- "meta_analysis_region_results/step4_dmr_vs_cpgs/" 
dir.single.cpg <- "meta_analysis_single_cpg_results/"        
dir.combp <- "../../Michael/data_for_combp-matchedAnalysis_1-29-2020/results/"  
dir.create(dir.result)

2 Matched analysis

library(dplyr)
library(ExperimentHub)
library(GenomicRanges)
library(e1071)
devtools::source_gist("https://gist.github.com/tiagochst/d3a7b1639acf603916c315d23b1efb3e")
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_death <- as.numeric(as.character(rosmap$age_death))
rosmap$age <- ifelse(is.na(rosmap$age_death), 90, 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)


{
  set.seed(5) # matchControl uses a distance calculation, if ties, it uses sample function
  m_ros <- matchControls(
    formula = status ~ age,
    data = rosmap,
    contlabel = "Case",
    caselabel = "Control"
  )
}

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

outFileControl <- merge(outFile, 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(outFileControl, 
                             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)

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

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] 256

2.1.2 Linear model

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

info_df <- readRDS(
  dir("DATASETS/ROSMAP////",pattern = "info",recursive = TRUE,full.names = TRUE)
)
mediansMval_df <- readRDS(
  dir("DATASETS/ROSMAP/",pattern = "medians",recursive = TRUE,full.names = TRUE)
)
pheno_df <- readRDS(
  dir("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("msex", "prop.neuron", "Slide", "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("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':    256 obs. of  29 variables:
##  $ Sample                : chr  "PT-313T" "PT-35PM" "PT-BY8G" "PT-BY9P" ...
##  $ Sentrix_ID            : num  5.82e+09 5.82e+09 6.04e+09 5.82e+09 5.82e+09 ...
##  $ Sentrix_Position      : chr  "R04C01" "R05C01" "R05C02" "R05C02" ...
##  $ Sample_Plate          : chr  "WG0001091-MSA4" "WG0001091-MSA4" "WG0003259-MSA4" "WG0001091-MSA4" ...
##  $ Sample_Well           : chr  "H08" "A03" "C02" "C05" ...
##  $ Sample_Group          : chr  "PT-313T" "PT-35PM" "PT-BY8G" "PT-BY9P" ...
##  $ batch                 : Factor w/ 2 levels "0","1": 2 2 2 2 1 1 1 2 2 2 ...
##  $ Sentrix               : chr  "5822020006_R04C01" "5822020007_R05C01" "6042324057_R05C02" "5822020004_R05C02" ...
##  $ Slide                 : num  5.82e+09 5.82e+09 6.04e+09 5.82e+09 5.82e+09 ...
##  $ projid                : num  11464261 22209673 78452313 39484737 46251007 ...
##  $ Study                 : chr  "ROS" "ROS" "MAP" "MAP" ...
##  $ msex                  : num  1 0 1 1 0 0 1 1 0 0 ...
##  $ educ                  : num  16 18 16 13 16 16 14 13 20 11 ...
##  $ race                  : Factor w/ 4 levels "1","2","3","7": 1 2 1 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 5 5 4 4 4 2 4 4 5 ...
##  $ age_at_visit_max      : chr  "83.507186858316217" "68.668035592060235" "75.493497604380565" "85.30595482546201" ...
##  $ age_first_ad_dx       : chr  NA NA "71.444216290212182" NA ...
##  $ age_death             : num  84.4 69.2 76 86.3 89.1 ...
##  $ cts_mmse30_first_ad_dx: num  NA NA 21 NA NA NA NA NA 23 NA ...
##  $ cts_mmse30_lv         : num  24 26 2 27 29 28 29 27 5 30 ...
##  $ pmi                   : num  4 8.5 6.08 8.73 6.12 ...
##  $ braaksc               : num  1 3 5 1 2 2 1 2 3 5 ...
##  $ ceradsc               : num  4 2 1 3 4 2 4 4 1 1 ...
##  $ cogdx                 : num  4 6 4 3 1 1 1 1 4 1 ...
##  $ stage3                : chr  "0-2" "3-4" "5-6" "0-2" ...
##  $ sex                   : Factor w/ 2 levels "Female","Male": 2 1 2 2 1 1 2 2 1 1 ...
##  $ prop.neuron           : num  0.458 0.41 0.41 0.449 0.463 ...
##  $ slide                 : Factor w/ 65 levels "5772325072","5772325089",..: 30 31 65 28 17 10 3 38 21 6 ...
is(pheno_df$braaksc,"numeric")
## [1] TRUE
is(pheno_df$prop.neuron,"numeric")
## [1] TRUE
predictors_char <- "braaksc"
covariates_char <- c("sex", "prop.neuron", "slide", "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,
    data = london,
    contlabel = "Case",
    caselabel = "Control"
  )
}
outFile <-  data.frame(
  "matchedControls" = london[m_london$cases, "sample"],
  "matchedCases" = london[m_london$controls, "sample"]
)

outFileControl <- merge(outFile,
                        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(outFileControl,
                             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("DATASETS/LONDON///",pattern = "info",recursive = TRUE,full.names = TRUE)
)
mediansMval_df <- readRDS(
  dir("DATASETS/LONDON/",pattern = "medians",recursive = TRUE,full.names = TRUE)
)
pheno_df <- readRDS(
  dir("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 10 13 18 25 26 ...
##  $ 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 82 80 82 97 87 ...
##  $ sex        : chr  "FEMALE" "FEMALE" "FEMALE" "FEMALE" ...
##  $ stage      : num  0 2 1 2 1 6 5 6 5 3 ...
##  $ 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 <- c("sex", "prop.neuron", "slide")

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 10 13 18 25 26 ...
##  $ sentrix_id : chr  "6042316048_R05C01" "6042316066_R05C01" "7786923107_R02C01" "6042316121_R04C02" ...
##  $ slide      : Factor w/ 12 levels "6042316035","6042316048",..: 2 3 9 8 6 11 7 2 2 9 ...
##  $ age.brain  : num  82 82 81 92 78 82 80 82 97 87 ...
##  $ sex        : Factor w/ 2 levels "FEMALE","MALE": 1 1 1 1 2 2 1 1 1 1 ...
##  $ stage      : num  0 2 1 2 1 6 5 6 5 3 ...
##  $ 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 <- c("sex", "prop.neuron", "slide")


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
results_ordered_df <- readr::read_csv(
  paste0(dir.result, "Gasparoni_matched_single_cpg_linear_df.csv"),
  col_types = readr::cols()
)
dim(results_ordered_df)
## [1] 446792      4
results_ordered_df %>% head

2.3 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,
    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.3.1 Samples

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

2.3.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':    70 obs. of  9 variables:
##  $ sample     : chr  "GSM2139163" "GSM2139164" "GSM2139166" "GSM2139169" ...
##  $ subject.id : chr  "MS66" "MS546" "MS1549" "MS504" ...
##  $ sentrix_id : chr  "7796806144_R01C01" "7796806144_R01C02" "7796806144_R03C01" "7796806144_R04C02" ...
##  $ slide      : chr  "7796806144" "7796806144" "7796806144" "7796806144" ...
##  $ age.brain  : num  89 80 78 85 83 78 86 73 81 93 ...
##  $ sex        : chr  "male" "female" "male" "male" ...
##  $ stage      : num  5 2 4 5 1 2 6 6 2 6 ...
##  $ prop.neuron: num  0.243 0.202 0.304 0.248 0.329 ...
##  $ Sample     : chr  "GSM2139163" "GSM2139164" "GSM2139166" "GSM2139169" ...
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 <- c("sex", "prop.neuron", "slide")

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.3.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':    70 obs. of  9 variables:
##  $ sample     : chr  "GSM2139163" "GSM2139164" "GSM2139166" "GSM2139169" ...
##  $ subject.id : chr  "MS66" "MS546" "MS1549" "MS504" ...
##  $ sentrix_id : chr  "7796806144_R01C01" "7796806144_R01C02" "7796806144_R03C01" "7796806144_R04C02" ...
##  $ slide      : Factor w/ 12 levels "7796806144","7796814008",..: 1 1 1 1 1 1 1 2 2 2 ...
##  $ age.brain  : num  89 80 78 85 83 78 86 73 81 93 ...
##  $ sex        : Factor w/ 2 levels "female","male": 2 1 2 2 1 2 1 2 2 1 ...
##  $ stage      : num  5 2 4 5 1 2 6 6 2 6 ...
##  $ prop.neuron: num  0.243 0.202 0.304 0.248 0.329 ...
##  $ Sample     : chr  "GSM2139163" "GSM2139164" "GSM2139166" "GSM2139169" ...
is(pheno_df$stage,"numeric")
## [1] TRUE
is(pheno_df$prop.neuron,"numeric")
## [1] TRUE
predictors_char <- "stage"
covariates_char <- c("sex", "prop.neuron", "slide")


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

library(dplyr)
library(tidyr)
preMeta <- function(cohort){
  
  ### Load data
  file <- dir(path = "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

library(meta)

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: 102 41
row.names(meta_all_sig) <- NULL

write.csv(
  meta_all_sig,
  paste0(dir.result, "meta_analysis_matched_sig_df.csv"),
  row.names = FALSE
)
library(GenomicRanges)

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: 102 41

### Find files with regions and probes
files <- dir(pattern = paste0(".*_residuals_cometh_input_ls.rds"),
             recursive = T,
             full.names = TRUE,
             ignore.case = T)
files <- grep("GASPARONI|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)
  )
}

Gasparoni_crossHyb_df <- extractCrosHybridization(cometh.probes.list$GASPARONI)
colnames(Gasparoni_crossHyb_df) <- paste0("GASPARONI_",colnames(Gasparoni_crossHyb_df))
plyr::count(
  Gasparoni_crossHyb_df, 
  vars = grep("_bi",colnames(Gasparoni_crossHyb_df),value = TRUE)
)

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(Gasparoni_crossHyb_df)  %>% 
  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("Gasparoni|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: 83 59
### 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

library(dplyr)
library(tidyr)

results.files <- dir("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(
  Gasparoni = Gasparoni,
  London_PFC = London,
  MtSinai = MtSinai,
  ROSMAP = ROSMAP
)

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

4.3 Meta analysis

### calculate meta analysis z scores and p values
library(meta)

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, 26:29, 5:24)
  ]
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 Session information

devtools::session_info()
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value                                             
##  version  R Under development (unstable) (2020-02-25 r77857)
##  os       macOS Catalina 10.15.3                            
##  system   x86_64, darwin15.6.0                              
##  ui       X11                                               
##  language (EN)                                              
##  collate  en_US.UTF-8                                       
##  ctype    en_US.UTF-8                                       
##  tz       America/New_York                                  
##  date     2020-04-26                                        
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package                * version     date       lib
##  AnnotationDbi            1.49.1      2020-01-25 [1]
##  AnnotationHub          * 2.19.11     2020-04-16 [1]
##  assertthat               0.2.1       2019-03-21 [1]
##  backports                1.1.6       2020-04-05 [1]
##  base64enc                0.1-3       2015-07-28 [1]
##  Biobase                  2.47.3      2020-03-16 [1]
##  BiocFileCache          * 1.11.6      2020-04-16 [1]
##  BiocGenerics           * 0.33.3      2020-03-23 [1]
##  BiocManager              1.30.10     2019-11-16 [1]
##  BiocVersion              3.11.1      2019-11-13 [1]
##  bit                      1.1-15.2    2020-02-10 [1]
##  bit64                    0.9-7       2017-05-08 [1]
##  bitops                   1.0-6       2013-08-17 [1]
##  blob                     1.2.1       2020-01-20 [1]
##  callr                    3.4.3       2020-03-28 [1]
##  class                    7.3-16      2020-03-25 [1]
##  cli                      2.0.2       2020-02-28 [1]
##  crayon                   1.3.4       2017-09-16 [1]
##  curl                     4.3         2019-12-02 [1]
##  DBI                      1.1.0       2019-12-15 [1]
##  dbplyr                 * 1.4.3       2020-04-19 [1]
##  desc                     1.2.0       2018-05-01 [1]
##  devtools                 2.3.0       2020-04-10 [1]
##  digest                   0.6.25      2020-02-23 [1]
##  dplyr                  * 0.8.99.9002 2020-04-02 [1]
##  e1071                  * 1.7-3       2019-11-26 [1]
##  ellipsis                 0.3.0       2019-09-20 [1]
##  evaluate                 0.14        2019-05-28 [1]
##  ExperimentHub          * 1.13.7      2020-04-16 [1]
##  fansi                    0.4.1       2020-01-08 [1]
##  fastmap                  1.0.1       2019-10-08 [1]
##  fs                       1.4.1       2020-04-04 [1]
##  generics                 0.0.2       2018-11-29 [1]
##  GenomeInfoDb           * 1.23.17     2020-04-13 [1]
##  GenomeInfoDbData         1.2.3       2020-04-20 [1]
##  GenomicRanges          * 1.39.3      2020-04-08 [1]
##  glue                     1.4.0       2020-04-03 [1]
##  hms                      0.5.3       2020-01-08 [1]
##  htmltools                0.4.0       2019-10-04 [1]
##  httpuv                   1.5.2       2019-09-11 [1]
##  httr                     1.4.1       2019-08-05 [1]
##  interactiveDisplayBase   1.25.0      2019-11-06 [1]
##  IRanges                * 2.21.8      2020-03-25 [1]
##  jsonlite                 1.6.1       2020-02-02 [1]
##  knitr                    1.28        2020-02-06 [1]
##  later                    1.0.0       2019-10-04 [1]
##  lifecycle                0.2.0       2020-03-06 [1]
##  magrittr                 1.5         2014-11-22 [1]
##  memoise                  1.1.0       2017-04-21 [1]
##  mime                     0.9         2020-02-04 [1]
##  pillar                   1.4.3       2019-12-20 [1]
##  pkgbuild                 1.0.6       2019-10-09 [1]
##  pkgconfig                2.0.3       2019-09-22 [1]
##  pkgload                  1.0.2       2018-10-29 [1]
##  prettyunits              1.1.1       2020-01-24 [1]
##  processx                 3.4.2       2020-02-09 [1]
##  promises                 1.1.0       2019-10-04 [1]
##  ps                       1.3.2       2020-02-13 [1]
##  purrr                    0.3.4       2020-04-17 [1]
##  R6                       2.4.1       2019-11-12 [1]
##  rappdirs                 0.3.1       2016-03-28 [1]
##  Rcpp                     1.0.4.6     2020-04-09 [1]
##  RCurl                    1.98-1.2    2020-04-18 [1]
##  readr                    1.3.1       2018-12-21 [1]
##  remotes                  2.1.1       2020-02-15 [1]
##  rlang                    0.4.5.9000  2020-03-20 [1]
##  rmarkdown                2.1         2020-01-20 [1]
##  rprojroot                1.3-2       2018-01-03 [1]
##  RSQLite                  2.2.0       2020-01-07 [1]
##  S4Vectors              * 0.25.15     2020-04-04 [1]
##  sessioninfo              1.1.1       2018-11-05 [1]
##  shiny                    1.4.0.2     2020-03-13 [1]
##  stringi                  1.4.6       2020-02-17 [1]
##  stringr                  1.4.0       2019-02-10 [1]
##  testthat                 2.3.2       2020-03-02 [1]
##  tibble                   3.0.1       2020-04-20 [1]
##  tidyselect               1.0.0       2020-01-27 [1]
##  usethis                  1.6.0       2020-04-09 [1]
##  vctrs                    0.2.99.9010 2020-04-02 [1]
##  withr                    2.1.2       2018-03-15 [1]
##  xfun                     0.13        2020-04-13 [1]
##  xtable                   1.8-4       2019-04-21 [1]
##  XVector                  0.27.2      2020-03-24 [1]
##  yaml                     2.2.1       2020-02-01 [1]
##  zlibbioc                 1.33.1      2020-01-24 [1]
##  source                                     
##  Bioconductor                               
##  Bioconductor                               
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Bioconductor                               
##  Bioconductor                               
##  Bioconductor                               
##  CRAN (R 4.0.0)                             
##  Bioconductor                               
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Github (tidyverse/dplyr@affb977)           
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Bioconductor                               
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Bioconductor                               
##  Bioconductor                               
##  Github (Bioconductor/GenomicRanges@70e6e69)
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Bioconductor                               
##  Bioconductor                               
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Github (r-lib/rlang@a90b04b)               
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Bioconductor                               
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Github (r-lib/vctrs@fd24927)               
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  CRAN (R 4.0.0)                             
##  Bioconductor                               
##  CRAN (R 4.0.0)                             
##  Bioconductor                               
## 
## [1] /Library/Frameworks/R.framework/Versions/4.0/Resources/library
LS0tCnRpdGxlOiAiTWV0YS1hbmFseXNpcyBkYXRhc2V0IgphdXRob3I6ICJMYW55dSBaaGFuZywgVGlhZ28gQy4gU2lsdmEsIExpbHkgV2FuZyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgcm1hcmtkb3duOjpodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGx1bWVuCiAgICB0b2M6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogeWVzCiAgICB0b2NfZGVwdGg6IDMKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZSAgICAKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpCmBgYAoKCiMgTWFpbiBsaWJyYXJpZXMgYW5kIGNvbmZpZ3VyYXRpb24KCmBgYHtSfQpkaXIucmVzdWx0IDwtICJtYXRjaGVkX2FuYWx5c2lzX3Jlc3VsdHMvIgpkaXIuZG1yIDwtICJtZXRhX2FuYWx5c2lzX3JlZ2lvbl9yZXN1bHRzL3N0ZXA0X2Rtcl92c19jcGdzLyIgCmRpci5zaW5nbGUuY3BnIDwtICJtZXRhX2FuYWx5c2lzX3NpbmdsZV9jcGdfcmVzdWx0cy8iICAgICAgICAKZGlyLmNvbWJwIDwtICIuLi8uLi9NaWNoYWVsL2RhdGFfZm9yX2NvbWJwLW1hdGNoZWRBbmFseXNpc18xLTI5LTIwMjAvcmVzdWx0cy8iICAKZGlyLmNyZWF0ZShkaXIucmVzdWx0KQpgYGAKCiMgTWF0Y2hlZCBhbmFseXNpcwoKYGBge1IgTWF0Y2hlZF9saWJzLG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KbGlicmFyeShkcGx5cikKbGlicmFyeShFeHBlcmltZW50SHViKQpsaWJyYXJ5KEdlbm9taWNSYW5nZXMpCmxpYnJhcnkoZTEwNzEpCmRldnRvb2xzOjpzb3VyY2VfZ2lzdCgiaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdGlhZ29jaHN0L2QzYTdiMTYzOWFjZjYwMzkxNmMzMTVkMjNiMWVmYjNlIikKYGBgCgpgYGB7Un0KZmlsZXMgPC0gZGlyKHBhdGggPSAiREFUQVNFVFMiLAogICAgICAgICAgICAgcGF0dGVybiA9IHBhc3RlMCgiLipuZXVybyIpLAogICAgICAgICAgICAgcmVjdXJzaXZlID0gVFJVRSwKICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBUUlVFKQpgYGAKCmBgYHtSLCBlY2hvID0gRkFMU0V9CmZpbGVzIDwtIGZpbGVzW2MoMSwyLDQsNSldCmBgYAoKYGBge1J9CmZpbGVzCnBoZW5vLmxzIDwtIGxhcHBseShmaWxlcywgZnVuY3Rpb24gKGYpewogIHJlYWRSRFMoZikKfSkKbmFtZXMocGhlbm8ubHMpIDwtIGZpbGVzICU+JSBkaXJuYW1lICU+JSBkaXJuYW1lICU+JSBiYXNlbmFtZQpsYXBwbHkocGhlbm8ubHMsZGltKQpgYGAKCiMjIFJvc21hcCBkYXRhc2V0CgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpyb3NtYXAgPC0gcGhlbm8ubHMkUk9TTUFQWywgYygiU2FtcGxlIiwgIm1zZXgiLCAiYnJhYWtzYyIsICJhZ2VfZGVhdGgiKV0Kcm9zbWFwJGFnZV9kZWF0aCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcihyb3NtYXAkYWdlX2RlYXRoKSkKcm9zbWFwJGFnZSA8LSBpZmVsc2UoaXMubmEocm9zbWFwJGFnZV9kZWF0aCksIDkwLCByb3NtYXAkYWdlX2RlYXRoKQpyb3NtYXAkc3RhdHVzIDwtIGlmZWxzZShyb3NtYXAkYnJhYWtzYyA8IDMsICJDb250cm9sIiwgIkNhc2UiKQpyb3NtYXAkc3RhdHVzIDwtIGFzLmZhY3Rvcihyb3NtYXAkc3RhdHVzKQpyb3NtYXAkc2V4IDwtIGlmZWxzZShyb3NtYXAkbXNleCA9PSAxLCAiTWFsZSIsICJGZW1hbGUiKQpyb3NtYXAkc2V4IDwtIGFzLmZhY3Rvcihyb3NtYXAkc2V4KQpzdHIocm9zbWFwKQoKCnsKICBzZXQuc2VlZCg1KSAjIG1hdGNoQ29udHJvbCB1c2VzIGEgZGlzdGFuY2UgY2FsY3VsYXRpb24sIGlmIHRpZXMsIGl0IHVzZXMgc2FtcGxlIGZ1bmN0aW9uCiAgbV9yb3MgPC0gbWF0Y2hDb250cm9scygKICAgIGZvcm11bGEgPSBzdGF0dXMgfiBhZ2UsCiAgICBkYXRhID0gcm9zbWFwLAogICAgY29udGxhYmVsID0gIkNhc2UiLAogICAgY2FzZWxhYmVsID0gIkNvbnRyb2wiCiAgKQp9CgpvdXRGaWxlIDwtIGRhdGEuZnJhbWUoCiAgIm1hdGNoZWRDb250cm9scyIgPSByb3NtYXBbbV9yb3MkY2FzZXMsICJTYW1wbGUiXSwgCiAgIm1hdGNoZWRDYXNlcyIgPSByb3NtYXBbbV9yb3MkY29udHJvbHMsICJTYW1wbGUiXQopCgpvdXRGaWxlQ29udHJvbCA8LSBtZXJnZShvdXRGaWxlLCByb3NtYXBbLCBjKCJTYW1wbGUiLCAic3RhdHVzIiwgImFnZSIsICJzZXgiKV0sIAogICAgICAgICAgICAgICAgICAgICAgICBieS54ID0gIm1hdGNoZWRDb250cm9scyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGJ5LnkgPSAiU2FtcGxlIikKCmNvbG5hbWVzKG91dEZpbGVDb250cm9sKVszOjVdIDwtIGMoInN0YXR1c19jb250cm9sIiwgImFnZV9jb250cm9sIiwgInNleF9jb250cm9sIikKCm91dEZpbGVDb250cm9sQ2FzZXMgPC0gbWVyZ2Uob3V0RmlsZUNvbnRyb2wsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvc21hcFssIGMoIlNhbXBsZSIsICJzdGF0dXMiLCAiYWdlIiwgInNleCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieS54ID0gIm1hdGNoZWRDYXNlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkueSA9ICJTYW1wbGUiKQoKY29sbmFtZXMob3V0RmlsZUNvbnRyb2xDYXNlcylbNjo4XSA8LSBjKCJzdGF0dXNfY2FzZSIsICJhZ2VfY2FzZSIsICJzZXhfY2FzZSIpCgpvdXRGaWxlQ29udHJvbENhc2VzIDwtIG91dEZpbGVDb250cm9sQ2FzZXNbLCBjKDIsIDMsIDQsIDUsIDEsIDYsIDcsIDgpXQoKd3JpdGUuY3N2KAogIG91dEZpbGVDb250cm9sQ2FzZXMsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJSb3NtYXBfbWF0Y2hlZENhc2VDb250cm9scy5jc3YiKSwKICByb3cubmFtZXMgPSBGQUxTRQopCgpST1NNQVBfbWF0Y2hBZ2VTZXggPC0gIG91dEZpbGVDb250cm9sQ2FzZXMgJT4lIAogIGRwbHlyOjpmaWx0ZXIoYWJzKGFnZV9jb250cm9sIC0gYWdlX2Nhc2UpIDw9IDEpCmRpbShST1NNQVBfbWF0Y2hBZ2VTZXgpCgp0YWJsZShST1NNQVBfbWF0Y2hBZ2VTZXgkc3RhdHVzX2NvbnRyb2wsIFJPU01BUF9tYXRjaEFnZVNleCRzZXhfY29udHJvbCkKdGFibGUoUk9TTUFQX21hdGNoQWdlU2V4JHN0YXR1c19jYXNlLCBST1NNQVBfbWF0Y2hBZ2VTZXgkc2V4X2Nhc2UpCgpST1NNQVBfc2FtcGxlcyA8LSBjKAogIGFzLmNoYXJhY3RlcihST1NNQVBfbWF0Y2hBZ2VTZXgkbWF0Y2hlZENvbnRyb2xzKSwKICBhcy5jaGFyYWN0ZXIoUk9TTUFQX21hdGNoQWdlU2V4JG1hdGNoZWRDYXNlcykKKQpzYXZlUkRTKAogIFJPU01BUF9zYW1wbGVzLAogIHBhc3RlMChkaXIucmVzdWx0LCAiUk9TTUFQX3NhbXBsZXMuUkRTIikKKQpgYGAKCiMjIyBTYW1wbGVzCgpgYGB7Un0KbGVuZ3RoKHJlYWRSRFMocGFzdGUwKGRpci5yZXN1bHQsICJST1NNQVBfc2FtcGxlcy5SRFMiKSkpCmBgYAoKIyMjIExpbmVhciBtb2RlbAoKYGBge1IgUk9TTUFQX0RNUiwgZXZhbCA9IEZBTFNFfQojIyMgSW1wb3J0IGRhdGFzZXRzCmNvaG9ydCA8LSAiUk9TTUFQIgpwaGVubyA8LSAicGhlbm8iCgppbmZvX2RmIDwtIHJlYWRSRFMoCiAgZGlyKCJEQVRBU0VUUy9ST1NNQVAvLy8vIixwYXR0ZXJuID0gImluZm8iLHJlY3Vyc2l2ZSA9IFRSVUUsZnVsbC5uYW1lcyA9IFRSVUUpCikKbWVkaWFuc012YWxfZGYgPC0gcmVhZFJEUygKICBkaXIoIkRBVEFTRVRTL1JPU01BUC8iLHBhdHRlcm4gPSAibWVkaWFucyIscmVjdXJzaXZlID0gVFJVRSxmdWxsLm5hbWVzID0gVFJVRSkKKQpwaGVub19kZiA8LSByZWFkUkRTKAogIGRpcigiREFUQVNFVFMvUk9TTUFQLyIscGF0dGVybiA9ICJOZXVyb24iLHJlY3Vyc2l2ZSA9IFRSVUUsZnVsbC5uYW1lcyA9IFRSVUUpCikKc2FtcGxlcyA8LSByZWFkUkRTKAogIHBhc3RlMChkaXIucmVzdWx0LCAiUk9TTUFQX3NhbXBsZXMuUkRTIikKKQoKIyMjIExpbWl0IHNhbXBsZXMKbWVkaWFuc012YWxfZGYgPC0gbWVkaWFuc012YWxfZGZbLCBzYW1wbGVzXQpwaGVub19kZiA8LSBwaGVub19kZltwaGVub19kZiRTYW1wbGUgJWluJSBzYW1wbGVzLCBdCgptZWRpYW5zTXZhbF9kZiA8LSBtZWRpYW5zTXZhbF9kZlssIHBoZW5vX2RmJFNhbXBsZV0KCmlkZW50aWNhbChwaGVub19kZiRTYW1wbGUsIGNvbG5hbWVzKG1lZGlhbnNNdmFsX2RmKSkKCnN0cihwaGVub19kZikKCnBoZW5vX2RmJGFnZV9kZWF0aCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihwaGVub19kZiRhZ2VfZGVhdGgpKQpwaGVub19kZiRtc2V4IDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIocGhlbm9fZGYkbXNleCkpCnBoZW5vX2RmJFNsaWRlIDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIocGhlbm9fZGYkU2xpZGUpKQpwaGVub19kZiRiYXRjaCA8LSBhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKHBoZW5vX2RmJGJhdGNoKSkKCnByZWRpY3RvcnNfY2hhciA8LSAiYnJhYWtzYyIKY292YXJpYXRlc19jaGFyIDwtIGMoIm1zZXgiLCAicHJvcC5uZXVyb24iLCAiU2xpZGUiLCAiYmF0Y2giKQoKCnJlc19kZiA8LSBUZXN0QWxsUmVnaW9uc19ub0luZm8oCiAgcHJlZGljdG9yc19jaGFyID0gcHJlZGljdG9yc19jaGFyLAogIGNvdmFyaWF0ZXNfY2hhciA9IGNvdmFyaWF0ZXNfY2hhciwKICBwaGVub19kZiA9IHBoZW5vX2RmLAogIHN1bW1hcml6ZWRSZWdpb25zX2RmID0gbWVkaWFuc012YWxfZGYsCiAgY29yZXMgPSA0CikKCmNvbG5hbWVzKHJlc19kZikgPC0gYygKICBwYXN0ZTAoY29ob3J0LCAiX2VzdGltYXRlIiksCiAgcGFzdGUwKGNvaG9ydCwgIl9zZSIpLAogIHBhc3RlMChjb2hvcnQsICJfcFZhbCIpLAogIHBhc3RlMChjb2hvcnQsICJfZmRyIikKKQoKcmVzX3dpdGhJbmZvX2RmIDwtIGNiaW5kKGluZm9fZGYsIHJlc19kZikKCnNhdmVSRFMoCiAgcmVzX3dpdGhJbmZvX2RmLAogIHBhc3RlMChkaXIucmVzdWx0LCBjb2hvcnQsICJfbWF0Y2hlZF9saW5lYXJfZGYucmRzIikKKQpgYGAKCiMjIyBTaW5nbGUgY3BnIGxpbmVhciBtb2RlbAoKYGBge1IgUk9TTUFQX3NpbmdsZUNwRywgZXZhbCA9IFRSVUV9CiMjIyBJbXBvcnQgZGF0YXNldHMKYmV0YV9tYXQgPC0gcmVhZFJEUygKICAiREFUQVNFVFMvUk9TTUFQL3N0ZXA3X3BjYV9maWx0ZXJpbmcvUk9TTUFQX1FOQk1JUV9QQ2ZpbHRlcmVkLlJEUyIKKSAKcGhlbm9fZGYgPC0gcmVhZFJEUygKICBkaXIoIkRBVEFTRVRTL1JPU01BUC8iLHBhdHRlcm4gPSAiTmV1cm9uIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnNhbXBsZXMgPC0gcmVhZFJEUygKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIlJPU01BUF9zYW1wbGVzLlJEUyIpCikKCiMjIyBMaW1pdCBzYW1wbGVzCmJldGFfbWF0IDwtIGJldGFfbWF0Wywgc2FtcGxlc10KcGhlbm9fZGYgPC0gcGhlbm9fZGZbcGhlbm9fZGYkU2FtcGxlICVpbiUgc2FtcGxlcywgXQoKYmV0YV9tYXQgPC0gYmV0YV9tYXRbLCBwaGVub19kZiRTYW1wbGVdCgppZGVudGljYWwocGhlbm9fZGYkU2FtcGxlLCBjb2xuYW1lcyhiZXRhX21hdCkpCgojIyMgQ29tcHV0ZSBNIHZhbHVlcwptdmFsX21hdCA8LSBsb2cyKGJldGFfbWF0IC8gKDEgLSBiZXRhX21hdCkpCgpwaGVub19kZiRzZXggPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNleCkKcGhlbm9fZGYkc2xpZGUgPC0gYXMuZmFjdG9yKHBoZW5vX2RmJFNlbnRyaXhfSUQpCnBoZW5vX2RmJGJhdGNoIDwtIGFzLmZhY3RvcihwaGVub19kZiRiYXRjaCkKCnN0cihwaGVub19kZikKCmlzKHBoZW5vX2RmJGJyYWFrc2MsIm51bWVyaWMiKQppcyhwaGVub19kZiRwcm9wLm5ldXJvbiwibnVtZXJpYyIpCmBgYAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KcHJlZGljdG9yc19jaGFyIDwtICJicmFha3NjIgpjb3ZhcmlhdGVzX2NoYXIgPC0gYygic2V4IiwgInByb3AubmV1cm9uIiwgInNsaWRlIiwgImJhdGNoIikKCgpkb1BhcmFsbGVsOjpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKS8yKQpkZXZ0b29sczo6c291cmNlX2dpc3QoImh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3RpYWdvY2hzdC9kM2E3YjE2MzlhY2Y2MDM5MTZjMzE1ZDIzYjFlZmIzZSIpCgpyZXN1bHRzX29yZGVyZWRfZGYgPC0gcGx5cjo6YWRwbHkobXZhbF9tYXQsMSwgZnVuY3Rpb24ocm93KXsKICAKICBzdW1PbmVSZWdpb25fZGYgPC0gYXMuZGF0YS5mcmFtZSh0KHJvdykpCiAgCiAgcmVzdWx0IDwtIFRlc3RTaW5nbGVSZWdpb24oCiAgICBwcmVkaWN0b3JzX2NoYXIgPSBwcmVkaWN0b3JzX2NoYXIsCiAgICBjb3ZhcmlhdGVzX2NoYXIgPSBjb3ZhcmlhdGVzX2NoYXIsCiAgICBwaGVub19kZiA9IHBoZW5vX2RmLAogICAgc3VtT25lUmVnaW9uX2RmID0gc3VtT25lUmVnaW9uX2RmCiAgKQogIHJlc3VsdAp9LCAucHJvZ3Jlc3MgPSAidGltZSIsLnBhcmFsbGVsID0gVFJVRSwuaWQgPSAiY3BnIikKY29sbmFtZXMocmVzdWx0c19vcmRlcmVkX2RmKVsxXSA8LSAiY3BnIgoKaWRlbnRpY2FsKHJvdy5uYW1lcyhtdmFsX21hdCksIHJlc3VsdHNfb3JkZXJlZF9kZiRjcGcgJT4lIGFzLmNoYXJhY3RlcigpKQoKd3JpdGUuY3N2KAogIHJlc3VsdHNfb3JkZXJlZF9kZiwKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIlJPU01BUF9tYXRjaGVkX3NpbmdsZV9jcGdfbGluZWFyX2RmLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKYGBgCgpgYGB7Un0KcmVzdWx0c19vcmRlcmVkX2RmIDwtIHJlYWRyOjpyZWFkX2NzdigKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIlJPU01BUF9tYXRjaGVkX3NpbmdsZV9jcGdfbGluZWFyX2RmLmNzdiIpLAogIGNvbF90eXBlcyA9IHJlYWRyOjpjb2xzKCkKKQpkaW0ocmVzdWx0c19vcmRlcmVkX2RmKQpyZXN1bHRzX29yZGVyZWRfZGYgJT4lIGhlYWQKYGBgCgoKIyMgTG9uZG9uIGRhdGFzZXQKCmBgYHtSLCBldmFsID0gRkFMU0V9CmxvbmRvbiA8LSBwaGVuby5scyRMT05ET05bLCBjKCJzYW1wbGUiLCAic2V4IiwgInN0YWdlIiwgImFnZS5icmFpbiIpXQpsb25kb24kc3RhdHVzIDwtIGlmZWxzZShsb25kb24kc3RhZ2UgPCAzLCAiQ29udHJvbCIsICJDYXNlIikKbG9uZG9uJHN0YXR1cyA8LSBhcy5mYWN0b3IobG9uZG9uJHN0YXR1cykKbG9uZG9uJHNleCA8LSBhcy5mYWN0b3IobG9uZG9uJHNleCkKc3RyKGxvbmRvbikKCnsKICBzZXQuc2VlZCg1KQogIG1fbG9uZG9uIDwtIG1hdGNoQ29udHJvbHMoCiAgICBmb3JtdWxhID0gc3RhdHVzIH4gYWdlLmJyYWluLAogICAgZGF0YSA9IGxvbmRvbiwKICAgIGNvbnRsYWJlbCA9ICJDYXNlIiwKICAgIGNhc2VsYWJlbCA9ICJDb250cm9sIgogICkKfQpvdXRGaWxlIDwtICBkYXRhLmZyYW1lKAogICJtYXRjaGVkQ29udHJvbHMiID0gbG9uZG9uW21fbG9uZG9uJGNhc2VzLCAic2FtcGxlIl0sCiAgIm1hdGNoZWRDYXNlcyIgPSBsb25kb25bbV9sb25kb24kY29udHJvbHMsICJzYW1wbGUiXQopCgpvdXRGaWxlQ29udHJvbCA8LSBtZXJnZShvdXRGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICBsb25kb25bLCBjKCJzYW1wbGUiLCAic3RhdHVzIiwgImFnZS5icmFpbiIsICJzZXgiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgIGJ5LnggPSAibWF0Y2hlZENvbnRyb2xzIiwKICAgICAgICAgICAgICAgICAgICAgICAgYnkueSA9ICJzYW1wbGUiKQpjb2xuYW1lcyhvdXRGaWxlQ29udHJvbClbMzo1XSA8LSBjKCJzdGF0dXNfY29udHJvbCIsICJhZ2VfY29udHJvbCIsICJzZXhfY29udHJvbCIpCgpvdXRGaWxlQ29udHJvbENhc2VzIDwtIG1lcmdlKG91dEZpbGVDb250cm9sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvbmRvblssIGMoInNhbXBsZSIsICJzdGF0dXMiLCAiYWdlLmJyYWluIiwgInNleCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieS54ID0gIm1hdGNoZWRDYXNlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkueSA9ICJzYW1wbGUiKQpjb2xuYW1lcyhvdXRGaWxlQ29udHJvbENhc2VzKVs2OjhdIDwtIGMoInN0YXR1c19jYXNlIiwgImFnZV9jYXNlIiwgInNleF9jYXNlIikKb3V0RmlsZUNvbnRyb2xDYXNlcyA8LSBvdXRGaWxlQ29udHJvbENhc2VzWywgYygyLCAzLCA0LCA1LCAxLCA2LCA3LCA4KV0KCndyaXRlLmNzdigKICBvdXRGaWxlQ29udHJvbENhc2VzLAogIHBhc3RlMChkaXIucmVzdWx0LCAiTG9uZG9uX21hdGNoZWRDYXNlQ29udHJvbHMuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQoKCgpMb25kb25fbWF0Y2hBZ2VTZXggPC0gb3V0RmlsZUNvbnRyb2xDYXNlcyAlPiUgCiAgZHBseXI6OmZpbHRlcihhYnMoYWdlX2NvbnRyb2wgLSBhZ2VfY2FzZSkgPD0gMSkKCmRpbShMb25kb25fbWF0Y2hBZ2VTZXgpCgp0YWJsZShMb25kb25fbWF0Y2hBZ2VTZXgkc3RhdHVzX2NvbnRyb2wsIExvbmRvbl9tYXRjaEFnZVNleCRzZXhfY29udHJvbCkKdGFibGUoTG9uZG9uX21hdGNoQWdlU2V4JHN0YXR1c19jYXNlLCBMb25kb25fbWF0Y2hBZ2VTZXgkc2V4X2Nhc2UpCkxvbmRvbl9zYW1wbGVzIDwtIGMoCiAgYXMuY2hhcmFjdGVyKExvbmRvbl9tYXRjaEFnZVNleCRtYXRjaGVkQ29udHJvbHMpLAogIGFzLmNoYXJhY3RlcihMb25kb25fbWF0Y2hBZ2VTZXgkbWF0Y2hlZENhc2VzKQopCnNhdmVSRFMoCiAgTG9uZG9uX3NhbXBsZXMsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJMb25kb25fc2FtcGxlcy5SRFMiKQopCgpgYGAKCgoKIyMjIFNhbXBsZXMKCgpgYGB7Un0KbGVuZ3RoKHJlYWRSRFMocGFzdGUwKGRpci5yZXN1bHQsICJMb25kb25fc2FtcGxlcy5SRFMiKSkpCmBgYAoKCgojIyMgTGluZWFyIG1vZGVsCgoKYGBge1IgTG9uZG9uX0RNUiwgZXZhbCA9IFRSVUV9CiMjIyBJbXBvcnQgZGF0YXNldHMKY29ob3J0IDwtICJMb25kb24iCnBoZW5vIDwtICJwaGVubyIKCmluZm9fZGYgPC0gcmVhZFJEUygKICBkaXIoIkRBVEFTRVRTL0xPTkRPTi8vLyIscGF0dGVybiA9ICJpbmZvIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCm1lZGlhbnNNdmFsX2RmIDwtIHJlYWRSRFMoCiAgZGlyKCJEQVRBU0VUUy9MT05ET04vIixwYXR0ZXJuID0gIm1lZGlhbnMiLHJlY3Vyc2l2ZSA9IFRSVUUsZnVsbC5uYW1lcyA9IFRSVUUpCikKcGhlbm9fZGYgPC0gcmVhZFJEUygKICBkaXIoIkRBVEFTRVRTL0xPTkRPTi8iLHBhdHRlcm4gPSAiTmV1cm9uUHJvcF9kZiIscmVjdXJzaXZlID0gVFJVRSxmdWxsLm5hbWVzID0gVFJVRSkKKQpzYW1wbGVzIDwtIHJlYWRSRFMoCiAgcGFzdGUwKGRpci5yZXN1bHQsICJMb25kb25fc2FtcGxlcy5SRFMiKQopCgojIyMgTGltaXQgc2FtcGxlcwptZWRpYW5zTXZhbF9kZiA8LSBtZWRpYW5zTXZhbF9kZlssIHNhbXBsZXNdCnBoZW5vX2RmIDwtIHBoZW5vX2RmW3BoZW5vX2RmJHNhbXBsZSAlaW4lIHNhbXBsZXMsIF0KCm1lZGlhbnNNdmFsX2RmIDwtIG1lZGlhbnNNdmFsX2RmWywgcGhlbm9fZGYkc2FtcGxlXQoKIyMjIENoZWNrIHZhcmlhYmxlcyBiZWZvcmUgZml0dGluZyBtb2RlbApwaGVub19kZiRTYW1wbGUgPC0gcGhlbm9fZGYkc2FtcGxlCgppZGVudGljYWwocGhlbm9fZGYkU2FtcGxlLCBjb2xuYW1lcyhtZWRpYW5zTXZhbF9kZikpCgpzdHIocGhlbm9fZGYpCgpwaGVub19kZiRzZXggPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNleCkKcGhlbm9fZGYkc2xpZGUgPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNsaWRlKQojIElmIHJvc21hcCBjb2hvcnQsIGRvbid0IGZvcmdldCBiYXRjaCBlZmZlY3QKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpkZXZ0b29sczo6c291cmNlX2dpc3QoImh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3RpYWdvY2hzdC9kM2E3YjE2MzlhY2Y2MDM5MTZjMzE1ZDIzYjFlZmIzZSIpCnByZWRpY3RvcnNfY2hhciA8LSAic3RhZ2UiCmNvdmFyaWF0ZXNfY2hhciA8LSBjKCJzZXgiLCAicHJvcC5uZXVyb24iLCAic2xpZGUiKQoKcmVzX2RmIDwtIFRlc3RBbGxSZWdpb25zX25vSW5mbygKICBwcmVkaWN0b3JzX2NoYXIgPSBwcmVkaWN0b3JzX2NoYXIsCiAgY292YXJpYXRlc19jaGFyID0gY292YXJpYXRlc19jaGFyLAogIHBoZW5vX2RmID0gcGhlbm9fZGYsCiAgc3VtbWFyaXplZFJlZ2lvbnNfZGYgPSBtZWRpYW5zTXZhbF9kZiwKICBjb3JlcyA9IDQKKQoKY29sbmFtZXMocmVzX2RmKSA8LSBjKAogIHBhc3RlMChjb2hvcnQsICJfZXN0aW1hdGUiKSwKICBwYXN0ZTAoY29ob3J0LCAiX3NlIiksCiAgcGFzdGUwKGNvaG9ydCwgIl9wVmFsIiksCiAgcGFzdGUwKGNvaG9ydCwgIl9mZHIiKQopCgpyZXNfd2l0aEluZm9fZGYgPC0gY2JpbmQoaW5mb19kZiwgcmVzX2RmKQoKc2F2ZVJEUygKICByZXNfd2l0aEluZm9fZGYsCiAgcGFzdGUwKGRpci5yZXN1bHQsIGNvaG9ydCwgIl9tYXRjaGVkX2xpbmVhcl9kZi5yZHMiKQopCmBgYAoKIyMjIFNpbmdsZSBjcGcgbGluZWFyIG1vZGVsCgpgYGB7UiBMb25kb25fc2luZ2xlQ3BHLCBldmFsID0gVFJVRX0KIyMjIEltcG9ydCBkYXRhc2V0cwpiZXRhX21hdCA8LSByZWFkUkRTKAogICJEQVRBU0VUUy9MT05ET04vc3RlcDVfcGNhX2ZpbHRlcmluZy9Mb25kb25fUEZDX1FOQk1JUV9QQ2ZpbHRlcmVkLlJEUyIKKSAKcGhlbm9fZGYgPC0gcmVhZFJEUygKICBkaXIoIkRBVEFTRVRTL0xPTkRPTi8iLHBhdHRlcm4gPSAiTmV1cm9uUHJvcF9kZiIscmVjdXJzaXZlID0gVFJVRSxmdWxsLm5hbWVzID0gVFJVRSkKKQpzYW1wbGVzIDwtIHJlYWRSRFMoCiAgcGFzdGUwKGRpci5yZXN1bHQsICJMb25kb25fc2FtcGxlcy5SRFMiKQopCgojIyMgTGltaXQgc2FtcGxlcwpwaGVub19kZiRTYW1wbGUgPC0gcGhlbm9fZGYkc2FtcGxlCgpiZXRhX21hdCA8LSBiZXRhX21hdFssIHNhbXBsZXNdCnBoZW5vX2RmIDwtIHBoZW5vX2RmW3BoZW5vX2RmJFNhbXBsZSAlaW4lIHNhbXBsZXMsIF0KCmJldGFfbWF0IDwtIGJldGFfbWF0WywgcGhlbm9fZGYkU2FtcGxlXQoKaWRlbnRpY2FsKHBoZW5vX2RmJFNhbXBsZSwgY29sbmFtZXMoYmV0YV9tYXQpKQoKIyMjIENvbXB1dGUgTSB2YWx1ZXMKbXZhbF9tYXQgPC0gbG9nMihiZXRhX21hdCAvICgxIC0gYmV0YV9tYXQpKQoKcGhlbm9fZGYkc2V4IDwtIGFzLmZhY3RvcihwaGVub19kZiRzZXgpCnBoZW5vX2RmJHNsaWRlIDwtIGFzLmZhY3RvcihwaGVub19kZiRzbGlkZSkKCnN0cihwaGVub19kZikKCmlzKHBoZW5vX2RmJHN0YWdlLCJudW1lcmljIikKaXMocGhlbm9fZGYkcHJvcC5uZXVyb24sIm51bWVyaWMiKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CnByZWRpY3RvcnNfY2hhciA8LSAic3RhZ2UiCmNvdmFyaWF0ZXNfY2hhciA8LSBjKCJzZXgiLCAicHJvcC5uZXVyb24iLCAic2xpZGUiKQoKCmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbChjb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLzIpCmRldnRvb2xzOjpzb3VyY2VfZ2lzdCgiaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdGlhZ29jaHN0L2QzYTdiMTYzOWFjZjYwMzkxNmMzMTVkMjNiMWVmYjNlIikKCnJlc3VsdHNfb3JkZXJlZF9kZiA8LSBwbHlyOjphZHBseShtdmFsX21hdCwxLCBmdW5jdGlvbihyb3cpewogIAogIHN1bU9uZVJlZ2lvbl9kZiA8LSBhcy5kYXRhLmZyYW1lKHQocm93KSkKICAKICByZXN1bHQgPC0gVGVzdFNpbmdsZVJlZ2lvbigKICAgIHByZWRpY3RvcnNfY2hhciA9IHByZWRpY3RvcnNfY2hhciwKICAgIGNvdmFyaWF0ZXNfY2hhciA9IGNvdmFyaWF0ZXNfY2hhciwKICAgIHBoZW5vX2RmID0gcGhlbm9fZGYsCiAgICBzdW1PbmVSZWdpb25fZGYgPSBzdW1PbmVSZWdpb25fZGYKICApCiAgcmVzdWx0Cn0sIC5wcm9ncmVzcyA9ICJ0aW1lIiwucGFyYWxsZWwgPSBUUlVFLC5pZCA9ICJjcGciKQpjb2xuYW1lcyhyZXN1bHRzX29yZGVyZWRfZGYpWzFdIDwtICJjcGciCgppZGVudGljYWwocm93Lm5hbWVzKG12YWxfbWF0KSwgcmVzdWx0c19vcmRlcmVkX2RmJGNwZyAlPiUgYXMuY2hhcmFjdGVyKCkpCgp3cml0ZS5jc3YoCiAgcmVzdWx0c19vcmRlcmVkX2RmLAogIHBhc3RlMChkaXIucmVzdWx0LCAiTG9uZG9uX21hdGNoZWRfc2luZ2xlX2NwZ19saW5lYXJfZGYuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQpgYGAKCmBgYHtSfQpyZXN1bHRzX29yZGVyZWRfZGYgPC0gcmVhZHI6OnJlYWRfY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAiTG9uZG9uX21hdGNoZWRfc2luZ2xlX2NwZ19saW5lYXJfZGYuY3N2IiksCiAgY29sX3R5cGVzID0gcmVhZHI6OmNvbHMoKQopCmRpbShyZXN1bHRzX29yZGVyZWRfZGYpCnJlc3VsdHNfb3JkZXJlZF9kZiAlPiUgaGVhZApgYGAKCgpgYGB7UiwgZXZhbCA9IEZBTFNFLCBpbmNsdWRlID0gRkFMU0V9CgojIyBHYXNwYXJvbmkgZGF0YXNldApnYXNwYXJvbmkgPC0gcGhlbm8ubHMkR0FTUEFST05JWywgYygic2FtcGxlIiwgInNleCIsICJzdGFnZSIsICJhZ2UuYnJhaW4iKV0KZ2FzcGFyb25pIDwtIGdhc3Bhcm9uaVssIGMoInNhbXBsZSIsICJzZXgiLCAic3RhZ2UiLCAiYWdlLmJyYWluIildCmdhc3Bhcm9uaSRzdGF0dXMgPC0gaWZlbHNlKGdhc3Bhcm9uaSRzdGFnZSA8IDMsICJDb250cm9sIiwgIkNhc2UiKQpnYXNwYXJvbmkkc3RhdHVzIDwtIGFzLmZhY3RvcihnYXNwYXJvbmkkc3RhdHVzKQpnYXNwYXJvbmkkc2V4IDwtIGFzLmZhY3RvcihnYXNwYXJvbmkkc2V4KQpzdHIoZ2FzcGFyb25pKQoKewogIHNldC5zZWVkKDUpCiAgbV9nYXNwYXJvbmkgPC0gbWF0Y2hDb250cm9scygKICAgIGZvcm11bGEgPSBzdGF0dXMgfiBhZ2UuYnJhaW4sCiAgICBkYXRhID0gZ2FzcGFyb25pLAogICAgY29udGxhYmVsID0gIkNhc2UiLAogICAgY2FzZWxhYmVsID0gIkNvbnRyb2wiCiAgKQp9CgpvdXRGaWxlIDwtIGRhdGEuZnJhbWUoIm1hdGNoZWRDb250cm9scyIgPSBnYXNwYXJvbmlbbV9nYXNwYXJvbmkkY2FzZXMsICJzYW1wbGUiXSwgCiAgICAgICAgICAgICAgICAgICAgICAibWF0Y2hlZENhc2VzIiA9IGdhc3Bhcm9uaVttX2dhc3Bhcm9uaSRjb250cm9scywgInNhbXBsZSJdCikKCm91dEZpbGVDb250cm9sIDwtIG1lcmdlKG91dEZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgIGdhc3Bhcm9uaVssIGMoInNhbXBsZSIsICJzdGF0dXMiLCAiYWdlLmJyYWluIiwgInNleCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgYnkueCA9ICJtYXRjaGVkQ29udHJvbHMiLAogICAgICAgICAgICAgICAgICAgICAgICBieS55ID0gInNhbXBsZSIpCmNvbG5hbWVzKG91dEZpbGVDb250cm9sKVszOjVdIDwtICAgYygic3RhdHVzX2NvbnRyb2wiLCAiYWdlX2NvbnRyb2wiLCAic2V4X2NvbnRyb2wiKQoKb3V0RmlsZUNvbnRyb2xDYXNlcyA8LSBtZXJnZShvdXRGaWxlQ29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnYXNwYXJvbmlbLCBjKCJzYW1wbGUiLCAic3RhdHVzIiwgImFnZS5icmFpbiIsICJzZXgiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkueCA9ICJtYXRjaGVkQ2FzZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5LnkgPSAic2FtcGxlIikKY29sbmFtZXMob3V0RmlsZUNvbnRyb2xDYXNlcylbNjo4XSA8LSBjKCJzdGF0dXNfY2FzZSIsICJhZ2VfY2FzZSIsICJzZXhfY2FzZSIpCgpvdXRGaWxlQ29udHJvbENhc2VzIDwtIG91dEZpbGVDb250cm9sQ2FzZXNbLCBjKDIsIDMsIDQsIDUsIDEsIDYsIDcsIDgpXQoKdGFibGUob3V0RmlsZUNvbnRyb2xDYXNlcyRzdGF0dXNfY29udHJvbCwKICAgICAgb3V0RmlsZUNvbnRyb2xDYXNlcyRzZXhfY29udHJvbCkKdGFibGUob3V0RmlsZUNvbnRyb2xDYXNlcyRzdGF0dXNfY2FzZSwKICAgICAgb3V0RmlsZUNvbnRyb2xDYXNlcyRzZXhfY2FzZSkKCndyaXRlLmNzdigKICBvdXRGaWxlQ29udHJvbENhc2VzLAogIHBhc3RlMChkaXIucmVzdWx0LCAiR2FzcGFyb25pX21hdGNoZWRDYXNlQ29udHJvbHMuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQoKCkdhc3Bhcm9uaV9tYXRjaEFnZVNleCA8LSAgb3V0RmlsZUNvbnRyb2xDYXNlcyAlPiUgCiAgZHBseXI6OmZpbHRlcihhYnMoYWdlX2NvbnRyb2wgLSBhZ2VfY2FzZSkgPD0gMSkKZGltKEdhc3Bhcm9uaV9tYXRjaEFnZVNleCkKCnRhYmxlKEdhc3Bhcm9uaV9tYXRjaEFnZVNleCRzdGF0dXNfY29udHJvbCwgR2FzcGFyb25pX21hdGNoQWdlU2V4JHNleF9jb250cm9sKQp0YWJsZShHYXNwYXJvbmlfbWF0Y2hBZ2VTZXgkc3RhdHVzX2Nhc2UsIEdhc3Bhcm9uaV9tYXRjaEFnZVNleCRzZXhfY2FzZSkKCkdhc3Bhcm9uaV9zYW1wbGVzIDwtIGMoCiAgYXMuY2hhcmFjdGVyKEdhc3Bhcm9uaV9tYXRjaEFnZVNleCRtYXRjaGVkQ29udHJvbHMpLAogIGFzLmNoYXJhY3RlcihHYXNwYXJvbmlfbWF0Y2hBZ2VTZXgkbWF0Y2hlZENhc2VzKQopCnNhdmVSRFMoCiAgR2FzcGFyb25pX3NhbXBsZXMsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJHYXNwYXJvbmlfc2FtcGxlcy5SRFMiKQopCgpgYGAKCgoKYGBge1IsIGV2YWw9IEZBTFNFLCBpbmNsdWRlID0gRkFMU0V9CiMjIyBTYW1wbGVzCmxlbmd0aChyZWFkUkRTKHBhc3RlMChkaXIucmVzdWx0LCAiR2FzcGFyb25pX3NhbXBsZXMuUkRTIikpKQpgYGAKCmBgYHtSIGdhc3Bhcm9uaV9ETVIsIGV2YWwgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQojIyMgTGluZWFyIG1vZGVsCiMjIyBJbXBvcnQgZGF0YXNldHMKY29ob3J0IDwtICJHYXNwYXJvbmkiCnBoZW5vIDwtICJwaGVubyIKCmluZm9fZGYgPC0gcmVhZFJEUygKICBkaXIoIkRBVEFTRVRTL0dBU1BBUk9OSS8iLHBhdHRlcm4gPSAiaW5mbyIscmVjdXJzaXZlID0gVFJVRSxmdWxsLm5hbWVzID0gVFJVRSkKKQptZWRpYW5zTXZhbF9kZiA8LSByZWFkUkRTKAogIGRpcigiREFUQVNFVFMvR0FTUEFST05JLyIscGF0dGVybiA9ICJtZWRpYW5zIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnBoZW5vX2RmIDwtIHJlYWRSRFMoCiAgZGlyKCJEQVRBU0VUUy9HQVNQQVJPTkkvc3RlcDhfbmV1cm9uX2NvbXAvIixwYXR0ZXJuID0gIiIscmVjdXJzaXZlID0gVFJVRSxmdWxsLm5hbWVzID0gVFJVRSkKKQpzYW1wbGVzIDwtIHJlYWRSRFMoCiAgcGFzdGUwKGRpci5yZXN1bHQsICJHYXNwYXJvbmlfc2FtcGxlcy5SRFMiKQopCgojIyMgTGltaXQgc2FtcGxlcwptZWRpYW5zTXZhbF9kZiA8LSBtZWRpYW5zTXZhbF9kZlssIHNhbXBsZXNdCnBoZW5vX2RmIDwtIHBoZW5vX2RmW3BoZW5vX2RmJHNhbXBsZSAlaW4lIHNhbXBsZXMsIF0KCm1lZGlhbnNNdmFsX2RmIDwtIG1lZGlhbnNNdmFsX2RmWywgcGhlbm9fZGYkc2FtcGxlXQoKIyMjIENoZWNrIHZhcmlhYmxlcyBiZWZvcmUgZml0dGluZyBtb2RlbApwaGVub19kZiRTYW1wbGUgPC0gcGhlbm9fZGYkc2FtcGxlCgppZGVudGljYWwocGhlbm9fZGYkU2FtcGxlLCBjb2xuYW1lcyhtZWRpYW5zTXZhbF9kZikpCgpzdHIocGhlbm9fZGYpCgpwaGVub19kZiRzZXggPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNleCkKcGhlbm9fZGYkc2xpZGUgPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNsaWRlKQojIElmIHJvc21hcCBjb2hvcnQsIGRvbid0IGZvcmdldCBiYXRjaCBlZmZlY3QKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFLCBpbmNsdWRlID0gRkFMU0V9CmRldnRvb2xzOjpzb3VyY2VfZ2lzdCgiaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdGlhZ29jaHN0L2QzYTdiMTYzOWFjZjYwMzkxNmMzMTVkMjNiMWVmYjNlIikKcHJlZGljdG9yc19jaGFyIDwtICJzdGFnZSIKY292YXJpYXRlc19jaGFyIDwtIGMoInNleCIsICJwcm9wLm5ldXJvbiIsICJzbGlkZSIpCgpyZXNfZGYgPC0gVGVzdEFsbFJlZ2lvbnNfbm9JbmZvKAogIHByZWRpY3RvcnNfY2hhciA9IHByZWRpY3RvcnNfY2hhciwKICBjb3ZhcmlhdGVzX2NoYXIgPSBjb3ZhcmlhdGVzX2NoYXIsCiAgcGhlbm9fZGYgPSBwaGVub19kZiwKICBzdW1tYXJpemVkUmVnaW9uc19kZiA9IG1lZGlhbnNNdmFsX2RmLAogIGNvcmVzID0gNAopCgpjb2xuYW1lcyhyZXNfZGYpIDwtIGMoCiAgcGFzdGUwKGNvaG9ydCwgIl9lc3RpbWF0ZSIpLAogIHBhc3RlMChjb2hvcnQsICJfc2UiKSwKICBwYXN0ZTAoY29ob3J0LCAiX3BWYWwiKSwKICBwYXN0ZTAoY29ob3J0LCAiX2ZkciIpCikKCnJlc193aXRoSW5mb19kZiA8LSBjYmluZChpbmZvX2RmLCByZXNfZGYpCgpzYXZlUkRTKAogIHJlc193aXRoSW5mb19kZiwKICBwYXN0ZTAoZGlyLnJlc3VsdCwgY29ob3J0LCAiX21hdGNoZWRfbGluZWFyX2RmLnJkcyIpCikKCmBgYAoKYGBge1IgZ2FzcGFyb25pX3NpbmdsZUNwRywgZXZhbCA9IEZBTFNFLCBpbmNsdWRlID0gRkFMU0V9CiMjIyBTaW5nbGUgY3BnIGxpbmVhciBtb2RlbCAKCiMjIyBJbXBvcnQgZGF0YXNldHMKYmV0YV9tYXQgPC0gcmVhZFJEUygKICAiREFUQVNFVFMvR0FTUEFST05JL3N0ZXA3X3BjYV9maWx0ZXJpbmcvR2FzcGFyb25pX1FOQk1JUV9QQ2ZpbHRlcmVkLlJEUyIKKSAKcGhlbm9fZGYgPC0gcmVhZFJEUygKICBkaXIoIkRBVEFTRVRTL0dBU1BBUk9OSS8iLHBhdHRlcm4gPSAiTmV1cm9uIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnNhbXBsZXMgPC0gcmVhZFJEUygKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIkdhc3Bhcm9uaV9zYW1wbGVzLlJEUyIpCikKCiMjIyBMaW1pdCBzYW1wbGVzCnBoZW5vX2RmJFNhbXBsZSA8LSBwaGVub19kZiRzYW1wbGUKCmJldGFfbWF0IDwtIGJldGFfbWF0Wywgc2FtcGxlc10KcGhlbm9fZGYgPC0gcGhlbm9fZGZbcGhlbm9fZGYkU2FtcGxlICVpbiUgc2FtcGxlcywgXQoKYmV0YV9tYXQgPC0gYmV0YV9tYXRbLCBwaGVub19kZiRTYW1wbGVdCgppZGVudGljYWwocGhlbm9fZGYkU2FtcGxlLCBjb2xuYW1lcyhiZXRhX21hdCkpCgojIyMgQ29tcHV0ZSBNIHZhbHVlcwptdmFsX21hdCA8LSBsb2cyKGJldGFfbWF0IC8gKDEgLSBiZXRhX21hdCkpCgpwaGVub19kZiRzZXggPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNleCkKcGhlbm9fZGYkc2xpZGUgPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNsaWRlKQoKc3RyKHBoZW5vX2RmKQoKaXMocGhlbm9fZGYkc3RhZ2UsIm51bWVyaWMiKQppcyhwaGVub19kZiRwcm9wLm5ldXJvbiwibnVtZXJpYyIpCmBgYAoKCmBgYHtSLCBldmFsID0gRkFMU0UsIGluY2x1ZGUgPSBGQUxTRX0KcHJlZGljdG9yc19jaGFyIDwtICJzdGFnZSIKY292YXJpYXRlc19jaGFyIDwtIGMoInNleCIsICJwcm9wLm5ldXJvbiIsICJzbGlkZSIpCgoKZG9QYXJhbGxlbDo6cmVnaXN0ZXJEb1BhcmFsbGVsKGNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkvMikKZGV2dG9vbHM6OnNvdXJjZV9naXN0KCJodHRwczovL2dpc3QuZ2l0aHViLmNvbS90aWFnb2Noc3QvZDNhN2IxNjM5YWNmNjAzOTE2YzMxNWQyM2IxZWZiM2UiKQoKcmVzdWx0c19vcmRlcmVkX2RmIDwtIHBseXI6OmFkcGx5KG12YWxfbWF0LDEsIGZ1bmN0aW9uKHJvdyl7CiAgCiAgc3VtT25lUmVnaW9uX2RmIDwtIGFzLmRhdGEuZnJhbWUodChyb3cpKQogIAogIHJlc3VsdCA8LSBUZXN0U2luZ2xlUmVnaW9uKAogICAgcHJlZGljdG9yc19jaGFyID0gcHJlZGljdG9yc19jaGFyLAogICAgY292YXJpYXRlc19jaGFyID0gY292YXJpYXRlc19jaGFyLAogICAgcGhlbm9fZGYgPSBwaGVub19kZiwKICAgIHN1bU9uZVJlZ2lvbl9kZiA9IHN1bU9uZVJlZ2lvbl9kZgogICkKICByZXN1bHQKfSwgLnByb2dyZXNzID0gInRpbWUiLC5wYXJhbGxlbCA9IFRSVUUsLmlkID0gImNwZyIpCmNvbG5hbWVzKHJlc3VsdHNfb3JkZXJlZF9kZilbMV0gPC0gImNwZyIKCmlkZW50aWNhbChyb3cubmFtZXMobXZhbF9tYXQpLCByZXN1bHRzX29yZGVyZWRfZGYkY3BnICU+JSBhcy5jaGFyYWN0ZXIoKSkKCndyaXRlLmNzdigKICByZXN1bHRzX29yZGVyZWRfZGYsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJHYXNwYXJvbmlfbWF0Y2hlZF9zaW5nbGVfY3BnX2xpbmVhcl9kZi5jc3YiKSwKICByb3cubmFtZXMgPSBGQUxTRQopCmBgYApgYGB7Un0KcmVzdWx0c19vcmRlcmVkX2RmIDwtIHJlYWRyOjpyZWFkX2NzdigKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIkdhc3Bhcm9uaV9tYXRjaGVkX3NpbmdsZV9jcGdfbGluZWFyX2RmLmNzdiIpLAogIGNvbF90eXBlcyA9IHJlYWRyOjpjb2xzKCkKKQpkaW0ocmVzdWx0c19vcmRlcmVkX2RmKQpyZXN1bHRzX29yZGVyZWRfZGYgJT4lIGhlYWQKYGBgCgoKIyMgTXQuU2luYWkgZGF0YXNldAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KbXRzaW5haSA8LSBwaGVuby5scyRNdFNpbmFpWywgYygic2FtcGxlIiwgInNleCIsICJzdGFnZSIsICJhZ2UuYnJhaW4iKV0KbXRzaW5haSRzdGF0dXMgPC0gaWZlbHNlKG10c2luYWkkc3RhZ2UgPCAzLCAiQ29udHJvbCIsICJDYXNlIikKbXRzaW5haSRzdGF0dXMgPC0gYXMuZmFjdG9yKG10c2luYWkkc3RhdHVzKQptdHNpbmFpJHNleCA8LSBhcy5mYWN0b3IobXRzaW5haSRzZXgpCnN0cihtdHNpbmFpKQoKewogIHNldC5zZWVkKDUpCiAgbV9tdHNpbmFpIDwtIG1hdGNoQ29udHJvbHMoCiAgICBmb3JtdWxhID0gc3RhdHVzIH4gYWdlLmJyYWluLAogICAgZGF0YSA9IG10c2luYWksCiAgICBjb250bGFiZWwgPSAiQ2FzZSIsCiAgICBjYXNlbGFiZWwgPSAiQ29udHJvbCIKICApCn0Kb3V0RmlsZSA8LSBkYXRhLmZyYW1lKAogICJtYXRjaGVkQ29udHJvbHMiID0gbXRzaW5haVttX210c2luYWkkY2FzZXMsICJzYW1wbGUiXSwgCiAgIm1hdGNoZWRDYXNlcyIgPSBtdHNpbmFpW21fbXRzaW5haSRjb250cm9scywgInNhbXBsZSJdCikKCm91dEZpbGVDb250cm9sIDwtIG1lcmdlKG91dEZpbGUsIAogICAgICAgICAgICAgICAgICAgICAgICBtdHNpbmFpWywgYygic2FtcGxlIiwgInN0YXR1cyIsICJhZ2UuYnJhaW4iLCAic2V4IildLCAKICAgICAgICAgICAgICAgICAgICAgICAgYnkueCA9ICJtYXRjaGVkQ29udHJvbHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgYnkueSA9ICJzYW1wbGUiKQoKY29sbmFtZXMob3V0RmlsZUNvbnRyb2wpWzM6NV0gPC0gYygic3RhdHVzX2NvbnRyb2wiLCAiYWdlX2NvbnRyb2wiLCAic2V4X2NvbnRyb2wiKQpvdXRGaWxlQ29udHJvbENhc2VzIDwtICBtZXJnZShvdXRGaWxlQ29udHJvbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10c2luYWlbLCBjKCJzYW1wbGUiLCAic3RhdHVzIiwgImFnZS5icmFpbiIsICJzZXgiKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieS54ID0gIm1hdGNoZWRDYXNlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieS55ID0gInNhbXBsZSIpCmNvbG5hbWVzKG91dEZpbGVDb250cm9sQ2FzZXMpWzY6OF0gPC0gYygic3RhdHVzX2Nhc2UiLCAiYWdlX2Nhc2UiLCAic2V4X2Nhc2UiKQpvdXRGaWxlQ29udHJvbENhc2VzIDwtIG91dEZpbGVDb250cm9sQ2FzZXNbLCBjKDIsIDMsIDQsIDUsIDEsIDYsIDcsIDgpXQoKd3JpdGUuY3N2KAogIG91dEZpbGVDb250cm9sQ2FzZXMsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJNdFNpbmFpX21hdGNoZWRDYXNlQ29udHJvbHMuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQoKCgpNdFNpbmFpX21hdGNoQWdlU2V4IDwtICBvdXRGaWxlQ29udHJvbENhc2VzICU+JSAKICBkcGx5cjo6ZmlsdGVyKGFicyhhZ2VfY29udHJvbCAtIGFnZV9jYXNlKSA8PSAxKQpkaW0oTXRTaW5haV9tYXRjaEFnZVNleCkKCgp0YWJsZShNdFNpbmFpX21hdGNoQWdlU2V4JHN0YXR1c19jb250cm9sLCBNdFNpbmFpX21hdGNoQWdlU2V4JHNleF9jb250cm9sKQp0YWJsZShNdFNpbmFpX21hdGNoQWdlU2V4JHN0YXR1c19jYXNlLCBNdFNpbmFpX21hdGNoQWdlU2V4JHNleF9jYXNlKQoKTXRTaW5haV9zYW1wbGVzIDwtIGMoCiAgYXMuY2hhcmFjdGVyKE10U2luYWlfbWF0Y2hBZ2VTZXgkbWF0Y2hlZENvbnRyb2xzKSwKICBhcy5jaGFyYWN0ZXIoTXRTaW5haV9tYXRjaEFnZVNleCRtYXRjaGVkQ2FzZXMpCikKCnNhdmVSRFMoCiAgTXRTaW5haV9zYW1wbGVzLAogIHBhc3RlMChkaXIucmVzdWx0LCAiTXRTaW5haV9zYW1wbGVzLlJEUyIpCikKYGBgCgojIyMgU2FtcGxlcwoKYGBge1J9Cmxlbmd0aChyZWFkUkRTKHBhc3RlMChkaXIucmVzdWx0LCAiTXRTaW5haV9zYW1wbGVzLlJEUyIpKSkKYGBgCgoKIyMjIExpbmVhciBtb2RlbAoKYGBge1IgTXRTaW5haV9ETVIsIGV2YWwgPSBUUlVFfQoKIyMjIEltcG9ydCBkYXRhc2V0cwpjb2hvcnQgPC0gIk10U2luYWkiCnBoZW5vIDwtICJwaGVubyIKCmluZm9fZGYgPC0gcmVhZFJEUygKICBkaXIoIkRBVEFTRVRTL010U2luYWkvLyIscGF0dGVybiA9ICJpbmZvIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCm1lZGlhbnNNdmFsX2RmIDwtIHJlYWRSRFMoCiAgZGlyKCJEQVRBU0VUUy9NdFNpbmFpLyIscGF0dGVybiA9ICJtZWRpYW5zIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnBoZW5vX2RmIDwtIHJlYWRSRFMoCiAgZGlyKCJEQVRBU0VUUy9NdFNpbmFpLyIscGF0dGVybiA9ICJOZXVyb25Qcm9wIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnNhbXBsZXMgPC0gcmVhZFJEUygKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIk10U2luYWlfc2FtcGxlcy5SRFMiKQopCgojIyMgTGltaXQgc2FtcGxlcwptZWRpYW5zTXZhbF9kZiA8LSBtZWRpYW5zTXZhbF9kZlssIHNhbXBsZXNdCnBoZW5vX2RmIDwtIHBoZW5vX2RmW3BoZW5vX2RmJHNhbXBsZSAlaW4lIHNhbXBsZXMsIF0KCm1lZGlhbnNNdmFsX2RmIDwtIG1lZGlhbnNNdmFsX2RmWywgcGhlbm9fZGYkc2FtcGxlXQoKIyMjIENoZWNrIHZhcmlhYmxlcyBiZWZvcmUgZml0dGluZyBtb2RlbApwaGVub19kZiRTYW1wbGUgPC0gcGhlbm9fZGYkc2FtcGxlCgppZGVudGljYWwocGhlbm9fZGYkU2FtcGxlLCBjb2xuYW1lcyhtZWRpYW5zTXZhbF9kZikpCgpzdHIocGhlbm9fZGYpCgpwaGVub19kZiRzZXggPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNleCkKcGhlbm9fZGYkc2xpZGUgPC0gYXMuZmFjdG9yKHBoZW5vX2RmJHNsaWRlKQojIElmIHJvc21hcCBjb2hvcnQsIGRvbid0IGZvcmdldCBiYXRjaCBlZmZlY3QKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpkZXZ0b29sczo6c291cmNlX2dpc3QoImh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3RpYWdvY2hzdC9kM2E3YjE2MzlhY2Y2MDM5MTZjMzE1ZDIzYjFlZmIzZSIpCnByZWRpY3RvcnNfY2hhciA8LSAic3RhZ2UiCmNvdmFyaWF0ZXNfY2hhciA8LSBjKCJzZXgiLCAicHJvcC5uZXVyb24iLCAic2xpZGUiKQoKcmVzX2RmIDwtIFRlc3RBbGxSZWdpb25zX25vSW5mbygKICBwcmVkaWN0b3JzX2NoYXIgPSBwcmVkaWN0b3JzX2NoYXIsCiAgY292YXJpYXRlc19jaGFyID0gY292YXJpYXRlc19jaGFyLAogIHBoZW5vX2RmID0gcGhlbm9fZGYsCiAgc3VtbWFyaXplZFJlZ2lvbnNfZGYgPSBtZWRpYW5zTXZhbF9kZiwKICBjb3JlcyA9IDQKKQoKY29sbmFtZXMocmVzX2RmKSA8LSBjKAogIHBhc3RlMChjb2hvcnQsICJfZXN0aW1hdGUiKSwKICBwYXN0ZTAoY29ob3J0LCAiX3NlIiksCiAgcGFzdGUwKGNvaG9ydCwgIl9wVmFsIiksCiAgcGFzdGUwKGNvaG9ydCwgIl9mZHIiKQopCgpyZXNfd2l0aEluZm9fZGYgPC0gY2JpbmQoaW5mb19kZiwgcmVzX2RmKQoKc2F2ZVJEUygKICByZXNfd2l0aEluZm9fZGYsCiAgcGFzdGUwKGRpci5yZXN1bHQsIGNvaG9ydCwgIl9tYXRjaGVkX2xpbmVhcl9kZi5yZHMiKQopCmBgYAoKIyMjIFNpbmdsZSBjcGcgbGluZWFyIG1vZGVsCgpgYGB7UiBNdFNpbmFpX3NpbmdsZUNwRywgZXZhbCA9IFRSVUV9CiMjIyBJbXBvcnQgZGF0YXNldHMKYmV0YV9tYXQgPC0gcmVhZFJEUygKICAiREFUQVNFVFMvTXRTaW5haS9zdGVwNV9wY2FfZmlsdGVyaW5nL010U2luYWlfUU5CTUlRX1BDZmlsdGVyZWQuUkRTIgopIApwaGVub19kZiA8LSByZWFkUkRTKAogIGRpcigiREFUQVNFVFMvTXRTaW5haS8iLHBhdHRlcm4gPSAiTmV1cm9uIixyZWN1cnNpdmUgPSBUUlVFLGZ1bGwubmFtZXMgPSBUUlVFKQopCnNhbXBsZXMgPC0gcmVhZFJEUygKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIk10U2luYWlfc2FtcGxlcy5SRFMiKQopCgojIyMgTGltaXQgc2FtcGxlcwpwaGVub19kZiRTYW1wbGUgPC0gcGhlbm9fZGYkc2FtcGxlCgpiZXRhX21hdCA8LSBiZXRhX21hdFssIHNhbXBsZXNdCnBoZW5vX2RmIDwtIHBoZW5vX2RmW3BoZW5vX2RmJFNhbXBsZSAlaW4lIHNhbXBsZXMsIF0KCmJldGFfbWF0IDwtIGJldGFfbWF0WywgcGhlbm9fZGYkU2FtcGxlXQoKaWRlbnRpY2FsKHBoZW5vX2RmJFNhbXBsZSwgY29sbmFtZXMoYmV0YV9tYXQpKQoKIyMjIENvbXB1dGUgTSB2YWx1ZXMKbXZhbF9tYXQgPC0gbG9nMihiZXRhX21hdCAvICgxIC0gYmV0YV9tYXQpKQoKcGhlbm9fZGYkc2V4IDwtIGFzLmZhY3RvcihwaGVub19kZiRzZXgpCnBoZW5vX2RmJHNsaWRlIDwtIGFzLmZhY3RvcihwaGVub19kZiRzbGlkZSkKCnN0cihwaGVub19kZikKCmlzKHBoZW5vX2RmJHN0YWdlLCJudW1lcmljIikKaXMocGhlbm9fZGYkcHJvcC5uZXVyb24sIm51bWVyaWMiKQpgYGAKCmBgYHtSLCBldmFsID0gRkFMU0V9CnByZWRpY3RvcnNfY2hhciA8LSAic3RhZ2UiCmNvdmFyaWF0ZXNfY2hhciA8LSBjKCJzZXgiLCAicHJvcC5uZXVyb24iLCAic2xpZGUiKQoKCmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbChjb3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLzIpCmRldnRvb2xzOjpzb3VyY2VfZ2lzdCgiaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vdGlhZ29jaHN0L2QzYTdiMTYzOWFjZjYwMzkxNmMzMTVkMjNiMWVmYjNlIikKCnJlc3VsdHNfb3JkZXJlZF9kZiA8LSBwbHlyOjphZHBseShtdmFsX21hdCwxLCBmdW5jdGlvbihyb3cpewogIAogIHN1bU9uZVJlZ2lvbl9kZiA8LSBhcy5kYXRhLmZyYW1lKHQocm93KSkKICAKICByZXN1bHQgPC0gVGVzdFNpbmdsZVJlZ2lvbigKICAgIHByZWRpY3RvcnNfY2hhciA9IHByZWRpY3RvcnNfY2hhciwKICAgIGNvdmFyaWF0ZXNfY2hhciA9IGNvdmFyaWF0ZXNfY2hhciwKICAgIHBoZW5vX2RmID0gcGhlbm9fZGYsCiAgICBzdW1PbmVSZWdpb25fZGYgPSBzdW1PbmVSZWdpb25fZGYKICApCiAgcmVzdWx0Cn0sIC5wcm9ncmVzcyA9ICJ0aW1lIiwucGFyYWxsZWwgPSBUUlVFLC5pZCA9ICJjcGciKQpjb2xuYW1lcyhyZXN1bHRzX29yZGVyZWRfZGYpWzFdIDwtICJjcGciCgppZGVudGljYWwocm93Lm5hbWVzKG12YWxfbWF0KSwgcmVzdWx0c19vcmRlcmVkX2RmJGNwZyAlPiUgYXMuY2hhcmFjdGVyKCkpCgp3cml0ZS5jc3YoCiAgcmVzdWx0c19vcmRlcmVkX2RmLAogIHBhc3RlMChkaXIucmVzdWx0LCAiTXRTaW5haV9tYXRjaGVkX3NpbmdsZV9jcGdfbGluZWFyX2RmLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKYGBgCmBgYHtSfQpyZXN1bHRzX29yZGVyZWRfZGYgPC0gcmVhZHI6OnJlYWRfY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAiTXRTaW5haV9tYXRjaGVkX3NpbmdsZV9jcGdfbGluZWFyX2RmLmNzdiIpLAogIGNvbF90eXBlcyA9IHJlYWRyOjpjb2xzKCkKKQpkaW0ocmVzdWx0c19vcmRlcmVkX2RmKQpyZXN1bHRzX29yZGVyZWRfZGYgJT4lIGhlYWQKYGBgCgoKIyBNZXRhLWFuYWx5c2lzIG9mIEdlbm9taWMgUmVnaW9ucwoKIyMgSW1wb3J0IGRhdGFzZXRzIGFuZCBwcmUtcHJvY2VzcyBmb3IgZWFjaCBjb2hvcnQgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpwcmVNZXRhIDwtIGZ1bmN0aW9uKGNvaG9ydCl7CiAgCiAgIyMjIExvYWQgZGF0YQogIGZpbGUgPC0gZGlyKHBhdGggPSAibWF0Y2hlZF9hbmFseXNpc19yZXN1bHRzIiwKICAgICAgICAgICAgICBwYXR0ZXJuID0gcGFzdGUwKGNvaG9ydCwiLiptYXRjaGVkX2xpbmVhcl9kZiIpLAogICAgICAgICAgICAgIHJlY3Vyc2l2ZSA9IFQsCiAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBUKQogIGxpbmVhci5yZXN1bHRzLmZpbmFsIDwtIHJlYWRSRFMoZmlsZSkKICAKICAjIyMgc2VsZWN0IHRoZSBtb3N0IHNpZyBjb21ldGggcmVnaW9uIGZvciBlYWNoIGlucHV0IHJlZ2lvbgogIHB2YS5jb2wgPC0gZ3JlcCgiX3BWYWwiLGNvbG5hbWVzKGxpbmVhci5yZXN1bHRzLmZpbmFsKSx2YWx1ZSA9IFRSVUUpCiAgY29sbmFtZXMobGluZWFyLnJlc3VsdHMuZmluYWwpW2dyZXAoImlucHV0UmVnaW9uIixjb2xuYW1lcyhsaW5lYXIucmVzdWx0cy5maW5hbCkpXSA8LSAiaW5wdXRSZWdpb24iCiAgCiAgIyAhISBpcyB1c2VkIHRvIHVucXVvdGUgYW4gaW5wdXQgCiAgIyBodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvcHJvZ3JhbW1pbmcuaHRtbAogIHJlc3VsdF9zaWcgPC0gbGluZWFyLnJlc3VsdHMuZmluYWwgJT4lCiAgICBncm91cF9ieShpbnB1dFJlZ2lvbikgJT4lCiAgICBmaWx0ZXIoKCEhYXMuc3ltYm9sKHB2YS5jb2wpKSA9PSBtaW4oISFhcy5zeW1ib2wocHZhLmNvbCkpKQogIAogIGRhdGEuZnJhbWUocmVzdWx0X3NpZywgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQp9CgpMb25kb25fUEZDIDwtIHByZU1ldGEoY29ob3J0ID0gIkxvbmRvbiIpCmRpbShMb25kb25fUEZDKQoKTXRTaW5haSA8LSBwcmVNZXRhKGNvaG9ydCA9ICJNdFNpbmFpIikgCmRpbShNdFNpbmFpKQoKUk9TTUFQIDwtIHByZU1ldGEoY29ob3J0ID0gIlJPU01BUCIpCmRpbShST1NNQVApCmBgYAoKCiMjIE1lcmdlIGNvaG9ydHMgCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQojIyMgbWVyZ2UgZGF0YXNldHMKY29ob3J0X2xzIDwtIGxpc3QoCiAgTG9uZG9uX1BGQyA9IExvbmRvbl9QRkMsCiAgTXRTaW5haSA9IE10U2luYWksCiAgUk9TTUFQID0gUk9TTUFQCikKCiMjIyBvdXRlciBqb2luIGlucHV0IHJlZ2lvbgptdWx0aV9jb2hvcnRzIDwtIFJlZHVjZSgKICBmdW5jdGlvbih4LHksIC4uLikgbWVyZ2UoeCwgeSwgYnkgPSAiaW5wdXRSZWdpb24iLCBhbGwgPSBUUlVFLCAuLi4pLAogIGNvaG9ydF9scwopCmBgYAoKIyMgTWV0YSBhbmFseXNpcwoKYGBge1IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShtZXRhKQoKZG9QYXJhbGxlbDo6cmVnaXN0ZXJEb1BhcmFsbGVsKGNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkvMikKbWV0YV9kZiA8LSBwbHlyOjphZHBseSguZGF0YSA9IG11bHRpX2NvaG9ydHMsIAogICAgICAgICAgICAgICAgICAgICAgIC5tYXJnaW5zID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgLmZ1biA9ICBmdW5jdGlvbihyb3dPbmVfZGYpewogICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICBlc3QgPC0gcm93T25lX2RmW2dyZXAoImVzdGltYXRlIixjb2xuYW1lcyhyb3dPbmVfZGYpKV0gJT4lIGFzLm51bWVyaWMKICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uIDwtICBwYXN0ZShpZmVsc2UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKGVzdCksICIuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGVzdCA+IDAsICIrIiwgIi0iKQogICAgICAgICAgICAgICAgICAgICAgICAgKSxjb2xsYXBzZSA9ICIiKQogICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICBzZSA8LSByb3dPbmVfZGZbZ3JlcCgic2UiLGNvbG5hbWVzKHJvd09uZV9kZikpXSAlPiUgYXMubnVtZXJpYwogICAgICAgICAgICAgICAgICAgICAgICAgY29ob3J0IDwtIGdzdWIoIl9zZSIsIiIsZ3JlcCgic2UiLGNvbG5hbWVzKHJvd09uZV9kZiksdmFsdWUgPSBUKSkKICAgICAgICAgICAgICAgICAgICAgICAgIHJvd09uZV9yZWZvcm1fZGYgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29ob3J0ID0gY29ob3J0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBlc3QgPSBlc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlID0gc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICBmIDwtIG1ldGFnZW4oCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRFID0gZXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBzZVRFID0gc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSByb3dPbmVfcmVmb3JtX2RmCiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgIHRpYmJsZTo6dGliYmxlKAogICAgICAgICAgICAgICAgICAgICAgICAgICBpbnB1dFJlZ2lvbiA9IHJvd09uZV9kZiRpbnB1dFJlZ2lvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZXN0aW1hdGUgPSBmJFRFLmZpeGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzZSA9IGYkc2VURS5maXhlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcFZhbC5maXhlZCA9IGYkcHZhbC5maXhlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcFZhbC5yYW5kb20gPSBmJHB2YWwucmFuZG9tLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwVmFsUSA9IGYkcHZhbC5RLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSBkaXJlY3Rpb24KICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICB9ICAsIC5wcm9ncmVzcyA9ICJ0aW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAucGFyYWxsZWwgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgIC5pZCA9IE5VTEwKKQoKIyMjIGNyZWF0ZSBmaW5hbCBwVmFsCm1ldGFfZGYkcFZhbC5maW5hbCA8LSBpZmVsc2UoCiAgbWV0YV9kZiRwVmFsUSA+IDAuMDUsIG1ldGFfZGYkcFZhbC5maXhlZCwgbWV0YV9kZiRwVmFsLnJhbmRvbQopCgojIyMgY2FsY3VsYXRlIGZkcgptZXRhX2RmJGZkciA8LSBwLmFkanVzdChtZXRhX2RmJHBWYWwuZmluYWwsIG1ldGhvZCA9ICJmZHIiKQoKIyMjIG9yZGVyIG1ldGFfZGYKbWV0YV9maW5hbF9kZiA8LSBtZXRhX2RmWywgYyhncmVwKCJfIixjb2xuYW1lcyhtZXRhX2RmKSxpbnZlcnQgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwKCJfIixjb2xuYW1lcyhtZXRhX2RmKSxpbnZlcnQgPSBGKSldCm1ldGFfZmluYWxfb3JkZXJlZF9kZiA8LSBtZXRhX2ZpbmFsX2RmW29yZGVyKG1ldGFfZmluYWxfZGYkcFZhbC5maW5hbCksXQpgYGAKCiMjIEFkZCBhbm5vdGF0aW9uIHRvIGlucHV0IHJlZ2lvbnMKCmBgYHtSLCBldmFsID0gRkFMU0V9CiMjIyBmaW5kIGFubm90YXRpb25zCmxpYnJhcnkoY29NZXRoRE1SKQoKc3BsaXRlZF9pbnB1dCA8LSBtZXRhX2ZpbmFsX29yZGVyZWRfZGYgJT4lIAogIHRpZHlyOjpzZXBhcmF0ZShjb2wgPSBpbnB1dFJlZ2lvbixpbnRvID0gIGMoImNocm9tIiwgInN0YXJ0IiwgImVuZCIpLHJlbW92ZSA9IEZBTFNFKQpzcGxpdGVkX2lucHV0X2Fubm90IDwtIEFubm90YXRlUmVzdWx0cyhzcGxpdGVkX2lucHV0WyxjKCJjaHJvbSIsICJzdGFydCIsICJlbmQiKV0sbkNvcmVzX2ludCA9IDEwKSAKCiMjIyBtZXJnZSBhbm5vdGF0aW9uIHdpdGggbWV0YSBhbmFseXNpcyBkYXRhCm1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGYgPC0gY2JpbmQoCiAgbWV0YV9maW5hbF9vcmRlcmVkX2RmLCBzcGxpdGVkX2lucHV0X2Fubm90WywgNDo3XQopCgojIyMgb3JkZXIgY29sdW1ucwptZXRhX29yZGVyZWRfd2l0aEFubm90X2RmIDwtIG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGYgJT4lIAogIGRwbHlyOjpzZWxlY3QoYygxLAogICAgICAgICAgICAgICAgICAobmNvbChtZXRhX29yZGVyZWRfd2l0aEFubm90X2RmKSAtIDMpOm5jb2wobWV0YV9vcmRlcmVkX3dpdGhBbm5vdF9kZiksCiAgICAgICAgICAgICAgICAgIDI6KG5jb2wobWV0YV9vcmRlcmVkX3dpdGhBbm5vdF9kZikgLSA0KSkKICApCiMjIyBzYXZlIGRhdGFzZXQKd3JpdGUuY3N2KAogIG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGYsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfZGYuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQoKbWV0YV9hbGxfc2lnIDwtIG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGZbCiAgIWlzLm5hKG1ldGFfb3JkZXJlZF93aXRoQW5ub3RfZGYkZmRyKSAmCiAgICAobWV0YV9vcmRlcmVkX3dpdGhBbm5vdF9kZiRmZHIgPCAwLjA1KSwgIAogIF0gI2RpbTogMTAyIDQxCnJvdy5uYW1lcyhtZXRhX2FsbF9zaWcpIDwtIE5VTEwKCndyaXRlLmNzdigKICBtZXRhX2FsbF9zaWcsCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2lnX2RmLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKCmBgYAoKCgpgYGB7UiBldmFsID0gRkFMU0V9CmxpYnJhcnkoR2Vub21pY1JhbmdlcykKYGBgCgpgYGB7UiwgZXZhbCA9IEZBTFNFLCBpbmNsdWRlID0gRkFMU0V9CgojIyBPdmVybGFwIHdpdGggY29tYi1wIERNUnMKbWF0Y2hlZF9kbXJzIDwtIHJlYWQuY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpZ19kZi5jc3YiKQopCm1hdGNoZWRfZG1yc19nciA8LSBtYXRjaGVkX2RtcnMgJT4lCiAgdGlkeXI6OnNlcGFyYXRlKCJpbnB1dFJlZ2lvbiIsIGMoImNocm9tIiwgInN0YXJ0IiwgImVuZCIpKSAlPiUKICBtYWtlR1Jhbmdlc0Zyb21EYXRhRnJhbWUoKQoKY29tYnBfZG1ycyA8LSByZWFkLnRhYmxlKAogIHBhc3RlMChkaXIuY29tYnAsICJjbmV3LnJlZ2lvbnMtcC5iZWQiKSwKICBoZWFkZXIgPSBUUlVFCikKY29sbmFtZXMoY29tYnBfZG1ycykgPC0gYygKICAiY2hyb20iLCAic3RhcnQiLCAiZW5kIiwgIm1pbl9wIiwgIm5fcHJvYmVzIiwgInpfcCIsICJ6X3NpZGFrX3AiCikKY29tYnBfc2lnIDwtIGNvbWJwX2RtcnMgJT4lIGZpbHRlcih6X3NpZGFrX3AgPCAwLjA1ICYgbl9wcm9iZXMgPiAyKQpjb21icF9zaWdfZ3IgPC0gbWFrZUdSYW5nZXNGcm9tRGF0YUZyYW1lKGNvbWJwX3NpZykKCm92ZXJsYXBwaW5nLnJlc3VsdHMgPC0gbWF0Y2hlZF9kbXJzWwogIHF1ZXJ5SGl0cyhmaW5kT3ZlcmxhcHMobWF0Y2hlZF9kbXJzX2dyLGNvbWJwX3NpZ19ncikpLAogIF0KI05vIG92ZXJsYXAgd2l0aCBjb21icAoKIyBvdmVybGFwcGluZy5yZXN1bHRzLnVuaXF1ZSA8LSB1bmlxdWUob3ZlcmxhcHBpbmcucmVzdWx0cykKIyAKIyB3cml0ZS5jc3YoCiMgICAgIG92ZXJsYXBwaW5nLnJlc3VsdHMudW5pcXVlLAojICAgICBwYXN0ZTAoZGlyLmNvbWJwLCAibWF0Y2hlZF9hbmFseXNpc19vdl9jb21iX3AuY3N2IiksCiMgICAgIHJvdy5uYW1lcyA9IEZBTFNFCiMgKQpgYGAKCiMjIERlbGV0ZSBzaWcgRE1ScyB3aXRoIGNyb3NzLWh5YnJpZGl6aW5nIG9yIHNtb2tpbmcgcHJvYmVzIAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KIyMjIGNhbGwgaW4gYWxsIGNyb3NzIGh5YnJpZGl6aW5nIHByb2JlcwplaCA9IEV4cGVyaW1lbnRIdWIoKQpxdWVyeShlaCwgIkRNUmNhdGUiKQpjcm9zc2h5YiA8LSBlaFtbIkVIMzEyOSJdXQoKIyMjIEdldCBzaWduaWZpY2FudCBzbW9raW5nIHByb2JlcwpzbW9raW5nLmZpbGUgPC0gImh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzUyNjczMjUvYmluL05JSE1TODE3MjczLXN1cHBsZW1lbnQtMDAxNTA2Xy1fU3VwcGxlbWVudGFsX1RhYmxlcy54bHN4IgoKaWYoIWZpbGUuZXhpc3RzKGJhc2VuYW1lKHNtb2tpbmcuZmlsZSkpKSBkb3dubG9hZGVyOjpkb3dubG9hZChzbW9raW5nLmZpbGUsYmFzZW5hbWUoc21va2luZy5maWxlKSkKCnNtb2tpbmcgPC0gcmVhZHhsOjpyZWFkX3hsc3goCiAgYmFzZW5hbWUoc21va2luZy5maWxlKSwKICBzaGVldCA9ICIwMiIsCiAgc2tpcCA9IDIKKQpzbW9raW5nLnNpZy5wcm9iZXMgPC0gc21va2luZyAlPiUgZHBseXI6OmZpbHRlcihgUC12YWx1ZWAgPCAxKjEwXigtNykpICU+JSBwdWxsKCJQcm9iZSBJRCIpIApsZW5ndGgoc21va2luZy5zaWcucHJvYmVzKQoKIyMjIENhbGwgaW4gbWV0YSBhbmFseXNpcyBmaW5hbCByZXN1bHRzCm1ldGFfYWxsIDwtIHJlYWQuY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpZ19kZi5jc3YiKQopICNkaW06IDEwMiA0MQoKIyMjIEZpbmQgZmlsZXMgd2l0aCByZWdpb25zIGFuZCBwcm9iZXMKZmlsZXMgPC0gZGlyKHBhdHRlcm4gPSBwYXN0ZTAoIi4qX3Jlc2lkdWFsc19jb21ldGhfaW5wdXRfbHMucmRzIiksCiAgICAgICAgICAgICByZWN1cnNpdmUgPSBULAogICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IFQpCmZpbGVzIDwtIGdyZXAoIkdBU1BBUk9OSXxMT05ET05fUEZDfE10U2luYWl8Uk9TTUFQIixmaWxlcyx2YWx1ZSA9IFRSVUUsaWdub3JlLmNhc2UgPSBUUlVFKQoKIyMjIFJlYWQgZmlsZXMgYW5kIExpbWl0IHRoZSBjb2hvcnRfbHMgdG8gY29ob3J0X2NvTWV0aFJlZ2lvbiBpbiBtZXRhX2FsbCAKY29tZXRoLnByb2Jlcy5saXN0IDwtIGxhcHBseShmaWxlcywgZnVuY3Rpb24gKGYpewogIHByaW50KGYpCiAgYWxsLmNsdXN0ZXJzIDwtIHJlYWRSRFMoZikkY29NZXRoX2xzICMgUmVhZCBmaWxlcwogIGNvaG9ydCA8LSBmICU+JSBkaXJuYW1lICU+JSBkaXJuYW1lICU+JSBiYXNlbmFtZSAjIGdldCBjb2hvcnQgZnJvbSBmb2xkZXIgbmFtZQogIAogIGNvbCA8LSBncmVwKHBhc3RlMChjb2hvcnQsIl9jb01ldGhSZWdpb24iKSwKICAgICAgICAgICAgICBjb2xuYW1lcyhtZXRhX2FsbCksCiAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBUUlVFLAogICAgICAgICAgICAgIHZhbHVlID0gVFJVRSkgIyBnZXQgY29sdW1uIHdpdGggY29ob3J0IHNpZyByZWdpb25zCiAgCiAgIyBrZWVwIHNpZyByZWdpb25zIG9ubHkKICBhbGwuY2x1c3RlcnNbbmFtZXMoYWxsLmNsdXN0ZXJzKSAlaW4lIG1ldGFfYWxsW1tjb2xdXV0KfSkKbmFtZXMoY29tZXRoLnByb2Jlcy5saXN0KSA8LSAgZmlsZXMgJT4lIGRpcm5hbWUgJT4lIGRpcm5hbWUgJT4lIGJhc2VuYW1lCgpsYXBwbHkoY29tZXRoLnByb2Jlcy5saXN0LGxlbmd0aCkKCiMjIyBNYXAgcHJvYmVzIGluIGVhY2ggcmVnaW9uIHRvIHNtb2tpbmcgYW5kIGNyb3NzaHlicmRpemluZwpleHRyYWN0Q3Jvc0h5YnJpZGl6YXRpb24gPC0gZnVuY3Rpb24ocHJvYmVzLmxpc3QpewogIGNyb3NzaHliLmV4dHJhY3RlZCA8LSBwbHlyOjpsYXBseShwcm9iZXMubGlzdCxmdW5jdGlvbihwcm9iZXMpewogICAgcGFzdGUoaW50ZXJzZWN0KHByb2JlcywgY3Jvc3NoeWIpLCAKICAgICAgICAgIGNvbGxhcHNlID0gIiwgIgogICAgKQogIH0pCiAgc21va2luZy5leHRyYWN0ZWQgPC0gcGx5cjo6bGFwbHkocHJvYmVzLmxpc3QsZnVuY3Rpb24ocHJvYmVzKXsKICAgIHBhc3RlKGludGVyc2VjdChwcm9iZXMsIHNtb2tpbmcuc2lnLnByb2JlcyksIAogICAgICAgICAgY29sbGFwc2UgPSAiLCAiCiAgICApCiAgfSkKICB0aWJibGU6OnRpYmJsZSgKICAgICJjb01ldGhSZWdpb24iID0gbmFtZXMocHJvYmVzLmxpc3QpLAogICAgImNyb3NzSHliIiA9IGNyb3NzaHliLmV4dHJhY3RlZCwgCiAgICAiY3Jvc3NIeWJfYmkiID0gaWZlbHNlKGNyb3NzaHliLmV4dHJhY3RlZCA9PSAiIiwwLDEpLAogICAgInNtb2tlIiA9IHNtb2tpbmcuZXh0cmFjdGVkLAogICAgInNtb2tlX2JpIiA9IGlmZWxzZShzbW9raW5nLmV4dHJhY3RlZCA9PSAiIiwwLDEpCiAgKQp9CgpHYXNwYXJvbmlfY3Jvc3NIeWJfZGYgPC0gZXh0cmFjdENyb3NIeWJyaWRpemF0aW9uKGNvbWV0aC5wcm9iZXMubGlzdCRHQVNQQVJPTkkpCmNvbG5hbWVzKEdhc3Bhcm9uaV9jcm9zc0h5Yl9kZikgPC0gcGFzdGUwKCJHQVNQQVJPTklfIixjb2xuYW1lcyhHYXNwYXJvbmlfY3Jvc3NIeWJfZGYpKQpwbHlyOjpjb3VudCgKICBHYXNwYXJvbmlfY3Jvc3NIeWJfZGYsIAogIHZhcnMgPSBncmVwKCJfYmkiLGNvbG5hbWVzKEdhc3Bhcm9uaV9jcm9zc0h5Yl9kZiksdmFsdWUgPSBUUlVFKQopCgpMb25kb25fUEZDX2Nyb3NzSHliX2RmIDwtIGV4dHJhY3RDcm9zSHlicmlkaXphdGlvbihjb21ldGgucHJvYmVzLmxpc3QkTE9ORE9OKQpjb2xuYW1lcyhMb25kb25fUEZDX2Nyb3NzSHliX2RmKSA8LSBwYXN0ZTAoIkxvbmRvbl8iLGNvbG5hbWVzKExvbmRvbl9QRkNfY3Jvc3NIeWJfZGYpKQpwbHlyOjpjb3VudCgKICBMb25kb25fUEZDX2Nyb3NzSHliX2RmLCAKICB2YXJzID0gZ3JlcCgiX2JpIixjb2xuYW1lcyhMb25kb25fUEZDX2Nyb3NzSHliX2RmKSx2YWx1ZSA9IFRSVUUpCikKCk10U2luYWlfY3Jvc3NIeWJfZGYgPC0gZXh0cmFjdENyb3NIeWJyaWRpemF0aW9uKGNvbWV0aC5wcm9iZXMubGlzdCRNdFNpbmFpKQpjb2xuYW1lcyhNdFNpbmFpX2Nyb3NzSHliX2RmKSA8LSBwYXN0ZTAoIk10U2luYWlfIixjb2xuYW1lcyhNdFNpbmFpX2Nyb3NzSHliX2RmKSkKcGx5cjo6Y291bnQoCiAgTXRTaW5haV9jcm9zc0h5Yl9kZiwgCiAgdmFycyA9IGdyZXAoIl9iaSIsY29sbmFtZXMoTXRTaW5haV9jcm9zc0h5Yl9kZiksdmFsdWUgPSBUUlVFKQopCgpST1NNQVBfY3Jvc3NIeWJfZGYgPC0gZXh0cmFjdENyb3NIeWJyaWRpemF0aW9uKGNvbWV0aC5wcm9iZXMubGlzdCRST1NNQVApCmNvbG5hbWVzKFJPU01BUF9jcm9zc0h5Yl9kZikgPC0gcGFzdGUwKCJST1NNQVBfIixjb2xuYW1lcyhST1NNQVBfY3Jvc3NIeWJfZGYpKQpwbHlyOjpjb3VudChST1NNQVBfY3Jvc3NIeWJfZGYsIHZhcnMgPSBncmVwKCJfYmkiLGNvbG5hbWVzKFJPU01BUF9jcm9zc0h5Yl9kZiksdmFsdWUgPSBUUlVFKSkKCiMjIyBNZXJnZSBzbW9raW5nIGFuZCBjcm9zc0h5YiBwcm9iZXMgIGluZm9ybWF0aW9uIHdpdGggbWV0YSBhbmFseXNpcyByZXN1bHRzIAptZXRhX2FsbF9maW5hbCA8LSBtZXRhX2FsbCAlPiUgbGVmdF9qb2luKEdhc3Bhcm9uaV9jcm9zc0h5Yl9kZikgICU+JSAKICBsZWZ0X2pvaW4oTG9uZG9uX1BGQ19jcm9zc0h5Yl9kZikgJT4lIAogIGxlZnRfam9pbihNdFNpbmFpX2Nyb3NzSHliX2RmKSAlPiUgCiAgbGVmdF9qb2luKFJPU01BUF9jcm9zc0h5Yl9kZikKCiMjIyBBZGQgaW5mb3JtYXRpb24gd2l0aCBpbnB1dCByZWdpb25zIHdpdGggYW55IGNyb3NzIGh5YnJpZGl6YXRpb24gaW4gY29ob3J0cyAKbWV0YV9hbGxfZmluYWwkY3Jvc3NIeWJfYmkgPC0gcm93U3VtcyhtZXRhX2FsbF9maW5hbFssZ3JlcCgiY3Jvc3NIeWJfYmkiLGNvbG5hbWVzKG1ldGFfYWxsX2ZpbmFsKSldLG5hLnJtID0gVFJVRSkgPiAwCm1ldGFfYWxsX2ZpbmFsJHNtb2tlX2JpIDwtIHJvd1N1bXMobWV0YV9hbGxfZmluYWxbLGdyZXAoInNtb2tlX2JpIixjb2xuYW1lcyhtZXRhX2FsbF9maW5hbCkpXSxuYS5ybSA9IFRSVUUpID4gMAoKIyBTb3J0IGJ5IHJlZ2lvbiBtZXRhIGFuYWx5c2lzIEZEUgojIENsdXN0ZXIgY29sdW1ucyBvZiB0aGUgcHJvamVjdHMgdG9nZXRoZXIKbWV0YV9hbGxfZmluYWwgPC0gbWV0YV9hbGxfZmluYWxbCiAgb3JkZXIobWV0YV9hbGxfZmluYWwkZmRyKSwKICBjKGdyZXAoIkdhc3Bhcm9uaXxNdFNpbmFpfExvbmRvbnxST1NNQVAiLGNvbG5hbWVzKG1ldGFfYWxsX2ZpbmFsKSxpZ25vcmUuY2FzZSA9IFRSVUUsaW52ZXJ0ID0gVFJVRSksCiAgICBncmVwKCJHYXNwYXJvbmkiLGNvbG5hbWVzKG1ldGFfYWxsX2ZpbmFsKSxpZ25vcmUuY2FzZSA9IFRSVUUpLAogICAgZ3JlcCgiTXRTaW5haSIsY29sbmFtZXMobWV0YV9hbGxfZmluYWwpLGlnbm9yZS5jYXNlID0gVFJVRSksCiAgICBncmVwKCJMb25kb24iLGNvbG5hbWVzKG1ldGFfYWxsX2ZpbmFsKSxpZ25vcmUuY2FzZSA9IFRSVUUpLAogICAgZ3JlcCgiUk9TTUFQIixjb2xuYW1lcyhtZXRhX2FsbF9maW5hbCksaWdub3JlLmNhc2UgPSBUUlVFKQogICkKICBdCnN0cihtZXRhX2FsbF9maW5hbCkKCm1ldGFfc2lnX2ZpbmFsIDwtIG1ldGFfYWxsX2ZpbmFsWwogIG1ldGFfYWxsX2ZpbmFsJGNyb3NzSHliX2JpID09IDAgJgogICAgbWV0YV9hbGxfZmluYWwkc21va2VfYmkgPT0gMCwKICBdICNkaW06IDgzIDU5CiMjIyBTYXZlCndyaXRlLmNzdigKICBtZXRhX3NpZ19maW5hbCAlPiUgYXMuZGF0YS5mcmFtZSwKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIm1ldGFfYW5hbHlzaXNfbWF0Y2hlZF9zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZi5jc3YiKSwKICByb3cubmFtZXMgPSBGQUxTRQopCmBgYAoKIyMgUmVzdWx0cwoKYGBge1IsIGVjaG8gPSBGQUxTRX0KcmVzIDwtIHJlYWRyOjpyZWFkX2NzdigKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIm1ldGFfYW5hbHlzaXNfbWF0Y2hlZF9zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZi5jc3YiKSwKICBjb2xfdHlwZXMgPSByZWFkcjo6Y29scygpCikgCnJlcyA8LSByZXNbb3JkZXIocmVzJGZkciksXQpyZXMKYGBgCgoKYGBge1IsIGV2YWwgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQojIyBDb21wYXJlIHdpdGggc2lnIERNUnMgZnJvbSBtYWluIGFuYWx5c2lzCgojIyMgQ2FsbCBpbiBkYXRhc2V0cwptYWluX2RtcnMgPC0gcmVhZC5jc3YoCiAgcGFzdGUwKGRpci5kbXIsICJtZXRhX2FuYWx5c2lzX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX292X2NvbWJfcF93aXRoX3NpZ19zaW5nbGVfY3Bncy5jc3YiKQopICNkaW06IDExOSA2MAoKbWF0Y2hlZF9kbXJzIDwtIHJlYWQuY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmLmNzdiIpCikgI2RpbTogODMgNTkKCiMjIyBUdXJuIGlucHV0IHJlZ2lvbnMgaW50byBncmFuZ2VzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoR2Vub21pY1JhbmdlcykKbWFpbl9kbXJzX2dyIDwtIG1haW5fZG1ycyAlPiUKICB0aWR5cjo6c2VwYXJhdGUoImlucHV0UmVnaW9uIixjKCJjaHJvbSIsInN0YXJ0IiwiZW5kIikpICU+JQogIG1ha2VHUmFuZ2VzRnJvbURhdGFGcmFtZSgpICU+JQogIHVuaXF1ZQoKbWF0Y2hlZF9kbXJzX2dyIDwtIG1hdGNoZWRfZG1ycyAlPiUKICB0aWR5cjo6c2VwYXJhdGUoImlucHV0UmVnaW9uIiwgYygiY2hyb20iLCAic3RhcnQiLCAiZW5kIikpICU+JQogIG1ha2VHUmFuZ2VzRnJvbURhdGFGcmFtZSgpICU+JQogIHVuaXF1ZQoKIyMjIENyZWF0ZSBWZW5uIERpYWdyYW0KbGlicmFyeShDaElQcGVha0Fubm8pCmxpYnJhcnkoZ2dwbG90MikKCm1ldGhvZHMgICAgICA8LSBjKCJtYWluIGFuYWx5c2lzIiwgIm1hdGNoZWQgYW5hbHlzaXMiKQptZXRob2RzTGFiZWwgPC0gYygibWFpbiBhbmFseXNpcyBzaWcgRE1ScyIsICJtYXRjaGVkIGFuYWx5c2lzIHNpZyBETVJzIikKCgpuIDwtIGxlbmd0aChtZXRob2RzKQoKZ2dfY29sb3JfaHVlIDwtIGZ1bmN0aW9uKG4pewogIGh1ZXMgPSBzZXEoMTUsIDM3NSwgbGVuZ3RoID0gbiArIDEpCiAgaGNsKGggPSBodWVzLCBsID0gNjUsIGMgPSAxMDApWzE6bl0KfQoKY29scyA9IGdnX2NvbG9yX2h1ZShuKQoKcmFuZ2VzLmxpc3QgPC0gbGlzdChtYWluX2RtcnNfZ3IsIG1hdGNoZWRfZG1yc19ncikKCiMgcGRmKAojICBmaWxlID0gcGFzdGUwKGRpci5yZXN1bHQsInZlbm5fbWFpbl9tYXRjaGVkX3NpZ19ETVJzLnBkZiIpLAojICB3aWR0aCA9IDUsIGhlaWdodCA9IDUKIykKbWFrZVZlbm5EaWFncmFtKAogIHJhbmdlcy5saXN0LAogIE5hbWVPZlBlYWtzID0gbWV0aG9kc0xhYmVsWzE6bl0sCiAgdG90YWxUZXN0ID0gMzcxNTksIAogIGJ5ID0gInJlZ2lvbiIsCiAgbWFpbiA9IHBhc3RlMCgiICIpLAogIGNvbCA9IGMoIiM0NDAxNTRmZiIsICcjMjE5MDhkZmYnKSwKICBmaWxsID0gYyhhbHBoYSgiIzQ0MDE1NGZmIiwwLjMpLCBhbHBoYSgnIzIxOTA4ZGZmJywwLjMpKSwKICBjZXggPSAxLAogIGZvbnRmYW1pbHkgPSAic2FucyIsCiAgY2F0LmNleCA9IDEsCiAgY2F0LmRlZmF1bHQucG9zID0gIm91dGVyIiwKICBjYXQucG9zID0gYygxODAsIDE4MCksCiAgY2F0LmRpc3QgPSBjKC0wLjA1NSwgLTAuMDU1KSwKICBjYXQuZm9udGZhbWlseSA9ICJzYW5zIiwKICBjYXQuY29sID0gYygiIzQ0MDE1NGZmIiwgJyMyMTkwOGRmZicpKQojIGRldi5vZmYoKQpgYGAKCgojIE1ldGEtYW5hbHlzaXMgU2luZ2xlIENwR3MKCjEuIG1lcmdlIGNvaG9ydHMKMi4gbWV0YSBhbmFseXNpcwozLiBhZGQgYW5ub3RhdGlvbgo0LiBEZWxldGUgY3Jvc3MtaHlicmlkaXppbmcgYW5kIHNtb2tpbmcgcHJvYmVzIGZyb20gc2lnIHByb2JlcwoKIyMgSW1wb3J0IGRhdGFzZXRzICAKCmBgYHtSLCBldmFsID0gRkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCgpyZXN1bHRzLmZpbGVzIDwtIGRpcigibWF0Y2hlZF9hbmFseXNpc19yZXN1bHRzLyIsCiAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAic2luZ2xlX2NwZ19saW5lYXJfZGYuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgcmVjdXJzaXZlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVFJVRSkKCmZvcihpIGluIHJlc3VsdHMuZmlsZXMpewogIGRhdGEgPC0gcmVhZHI6OnJlYWRfY3N2KGkpCiAgZGF0YXNldCA8LSB1bmxpc3Qoc3RyaW5ncjo6c3RyX3NwbGl0KGJhc2VuYW1lKGkpLCJcXC98XFxfIikpWzFdICAlPiUgYXMuY2hhcmFjdGVyKCkKICBhdXggPC0gcGFzdGUwKGRhdGFzZXQsYygiX2VzdGltYXRlIiwgIl9zZSIsICJfcFZhbHVlIikpCiAgY29sbmFtZXMoZGF0YSkgPC0gYygiY3BnIiwgYXV4KQogIGFzc2lnbihkYXRhc2V0LGRhdGEpCn0KYGBgCgojIyBNZXJnZSBjb2hvcnRzCgpgYGB7UiwgZXZhbCA9IEZBTFNFfQpjb2hvcnRfbHMgPC0gbGlzdCgKICBHYXNwYXJvbmkgPSBHYXNwYXJvbmksCiAgTG9uZG9uX1BGQyA9IExvbmRvbiwKICBNdFNpbmFpID0gTXRTaW5haSwKICBST1NNQVAgPSBST1NNQVAKKQoKIyMjIG91dGVyIGpvaW4gaW5wdXQgcmVnaW9uCm11bHRpX2NvaG9ydHMgPC0gUmVkdWNlKAogIGZ1bmN0aW9uKHgseSwgLi4uKSBtZXJnZSh4LCB5LCBieSA9ICJjcGciLCBhbGwgPSBUUlVFLCAuLi4pLAogIGNvaG9ydF9scwopICNkaW06IDQ1MDc5MyAxMwpkaW0obXVsdGlfY29ob3J0cykKYGBgCgojIyBNZXRhIGFuYWx5c2lzICAKCmBgYHtSLCBldmFsID0gRkFMU0V9CiMjIyBjYWxjdWxhdGUgbWV0YSBhbmFseXNpcyB6IHNjb3JlcyBhbmQgcCB2YWx1ZXMKbGlicmFyeShtZXRhKQoKZG9QYXJhbGxlbDo6cmVnaXN0ZXJEb1BhcmFsbGVsKGNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkvMikKbWV0YV9kZiA8LSBwbHlyOjphZHBseSguZGF0YSA9IG11bHRpX2NvaG9ydHMsIAogICAgICAgICAgICAgICAgICAgICAgIC5tYXJnaW5zID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgLmZ1biA9ICBmdW5jdGlvbihyb3dPbmVfZGYpewogICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICBlc3QgPC0gcm93T25lX2RmW2dyZXAoImVzdGltYXRlIixjb2xuYW1lcyhyb3dPbmVfZGYpKV0gJT4lIGFzLm51bWVyaWMKICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uIDwtICBwYXN0ZShpZmVsc2UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKGVzdCksICIuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGVzdCA+IDAsICIrIiwgIi0iKQogICAgICAgICAgICAgICAgICAgICAgICAgKSxjb2xsYXBzZSA9ICIiKQogICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICBzZSA8LSByb3dPbmVfZGZbZ3JlcCgic2UiLGNvbG5hbWVzKHJvd09uZV9kZikpXSAlPiUgYXMubnVtZXJpYwogICAgICAgICAgICAgICAgICAgICAgICAgY29ob3J0IDwtIGdzdWIoIl9zZSIsIiIsZ3JlcCgic2UiLGNvbG5hbWVzKHJvd09uZV9kZiksdmFsdWUgPSBUKSkKICAgICAgICAgICAgICAgICAgICAgICAgIHJvd09uZV9yZWZvcm1fZGYgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29ob3J0ID0gY29ob3J0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBlc3QgPSBlc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlID0gc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICBmIDwtIG1ldGFnZW4oCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRFID0gZXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBzZVRFID0gc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSByb3dPbmVfcmVmb3JtX2RmCiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgIHRpYmJsZTo6dGliYmxlKAogICAgICAgICAgICAgICAgICAgICAgICAgICBjcGcgPSByb3dPbmVfZGYkY3BnLAogICAgICAgICAgICAgICAgICAgICAgICAgICBlc3RpbWF0ZSA9IGYkVEUuZml4ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlID0gZiRzZVRFLmZpeGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwVmFsLmZpeGVkID0gZiRwdmFsLmZpeGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwVmFsLnJhbmRvbSA9IGYkcHZhbC5yYW5kb20sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBWYWxRID0gZiRwdmFsLlEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9IGRpcmVjdGlvbgogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgIH0gICwgLnByb2dyZXNzID0gInRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgIC5wYXJhbGxlbCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgLmlkID0gTlVMTAopCgojIyMgY3JlYXRlIGZpbmFsIHBWYWwKbWV0YV9kZiRwVmFsLmZpbmFsIDwtIGlmZWxzZSgKICBtZXRhX2RmJHBWYWxRID4gMC4wNSwgbWV0YV9kZiRwVmFsLmZpeGVkLCBtZXRhX2RmJHBWYWwucmFuZG9tCikKCiMjIyBjYWxjdWxhdGUgZmRyCm1ldGFfZGYkZmRyIDwtIHAuYWRqdXN0KG1ldGFfZGYkcFZhbC5maW5hbCwgbWV0aG9kID0gImZkciIpCgojIyMgb3JkZXIgbWV0YV9kZgptZXRhX2ZpbmFsX2RmIDwtIG1ldGFfZGZbLCBjKGdyZXAoIl8iLGNvbG5hbWVzKG1ldGFfZGYpLGludmVydCA9IFQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXAoIl8iLGNvbG5hbWVzKG1ldGFfZGYpLGludmVydCA9IEYpKV0KbWV0YV9maW5hbF9vcmRlcmVkX2RmIDwtIG1ldGFfZmluYWxfZGZbb3JkZXIobWV0YV9maW5hbF9kZiRwVmFsLmZpbmFsKSxdCmBgYAoKIyMgQWRkIGFubm90YXRpb24gIAoKYGBge1IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShzZXNhbWUpCnByb2Jlcy5pbmZvIDwtIHNlc2FtZURhdGFHZXQoIkhNNDUwLmhnMTkubWFuaWZlc3QiKQpwcm9iZXMuaW5mbyA8LSBwcm9iZXMuaW5mb1ttZXRhX2ZpbmFsX29yZGVyZWRfZGYkY3BnICU+JSBhcy5jaGFyYWN0ZXIoKV0gJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGRwbHlyOjpzZWxlY3QoYygic2VxbmFtZXMiLCJzdGFydCIsImVuZCIpKQoKcmVzdWx0X2Fubm90X2RmIDwtIG1lcmdlKAogIHkgPSBtZXRhX2ZpbmFsX29yZGVyZWRfZGYsCiAgeCA9IHByb2Jlcy5pbmZvLAogIGJ5LnkgPSAiY3BnIiwKICBieS54ID0gInJvdy5uYW1lcyIsCiAgYWxsLnkgPSBUUlVFLAogIHNvcnQgPSBGQUxTRQopCgojIyMgZmluYWwgcmF3IGRhdGEKd3JpdGUuY3N2KAogIHJlc3VsdF9hbm5vdF9kZiAgJT4lIGFzLmRhdGEuZnJhbWUoKSwKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIm1ldGFfYW5hbHlzaXNfbWF0Y2hlZF9zaW5nbGVfY3BnX2RmLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKCiMjIyBwcmVwYXJlIGRhdGEgZm9yIGNvbWItcApyZXN1bHRfZm9yX2NvbWJwX2RmIDwtIHJlc3VsdF9hbm5vdF9kZlsKICAsIGMoInNlcW5hbWVzIiwgInN0YXJ0IiwgImVuZCIsICJwVmFsLmZpbmFsIikKICBdCmNvbG5hbWVzKHJlc3VsdF9mb3JfY29tYnBfZGYpW2MoMSw0KV0gPC0gYygiY2hyIiwgInBWYWx1ZSIpCnJlc3VsdF9mb3JfY29tYnBfZGYkY2hyIDwtIGFzLmNoYXJhY3RlcihyZXN1bHRfZm9yX2NvbWJwX2RmJGNocikKCndyaXRlLmNzdigKICByZXN1bHRfZm9yX2NvbWJwX2RmLAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpbmdsZV9jcGdfZGZfZm9yX2NvbWJwLmNzdiIpLAogIHJvdy5uYW1lcyA9IEZBTFNFCikKYGBgCgojIyBEZWxldGUgY3Jvc3MtaHlicmlkaXppbmcgYW5kIHNtb2tpbmcgcHJvYmVzIGZyb20gc2lnIHByb2JlcyAKCmBgYHtSLCBldmFsID0gRkFMU0V9CiMjIyBFeGNsdWRlIG5vbi1zaWduaWZpY2FudCBwcm9iZXMKcmVzdWx0X2Fubm90X3NpZ19kZiA8LSByZXN1bHRfYW5ub3RfZGYgJT4lIGZpbHRlcihmZHIgPCAwLjA1KSAKCndyaXRlLmNzdigKICByZXN1bHRfYW5ub3Rfc2lnX2RmICU+JSBhcy5kYXRhLmZyYW1lKCksCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2luZ2xlX2NwZ19zaWdfZGYuY3N2IiksCiAgcm93Lm5hbWVzID0gRkFMU0UKKQoKIyMjIEdldCBjcm9zc2h5YnJkaXppbmcgcHJvYmVzCmxpYnJhcnkoRXhwZXJpbWVudEh1YikKCmVoID0gRXhwZXJpbWVudEh1YigpCnF1ZXJ5KGVoLCAiRE1SY2F0ZSIpCmNyb3NzaHliIDwtIGVoW1siRUgzMTI5Il1dCgoKIyMjIEdldCBzaWduaWZpY2FudCBzbW9raW5nIHByb2JlcwpzbW9raW5nLmZpbGUgPC0gImh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzUyNjczMjUvYmluL05JSE1TODE3MjczLXN1cHBsZW1lbnQtMDAxNTA2Xy1fU3VwcGxlbWVudGFsX1RhYmxlcy54bHN4IgoKaWYoIWZpbGUuZXhpc3RzKGJhc2VuYW1lKHNtb2tpbmcuZmlsZSkpKSBkb3dubG9hZGVyOjpkb3dubG9hZChzbW9raW5nLmZpbGUsYmFzZW5hbWUoc21va2luZy5maWxlKSkKCnNtb2tpbmcgPC0gcmVhZHhsOjpyZWFkX3hsc3goCiAgYmFzZW5hbWUoc21va2luZy5maWxlKSwKICBzaGVldCA9ICIwMiIsCiAgc2tpcCA9IDIKKQpzbW9raW5nLnNpZy5wcm9iZXMgPC0gc21va2luZyAlPiUgZHBseXI6OmZpbHRlcihgUC12YWx1ZWAgPCAxKjEwXigtNykpICU+JSBwdWxsKCJQcm9iZSBJRCIpCgoKIyMjIEV4Y2x1ZGUgY3Jvc3MtaHlicmlkaXppbmcgYW5kIHNtb2tpbmcgcHJvYmVzCnJlc3VsdF9hbm5vdF9zaWdfZGYkUm93Lm5hbWVzIDwtIGFzLmNoYXJhY3RlcihyZXN1bHRfYW5ub3Rfc2lnX2RmJFJvdy5uYW1lcykKCnJlc3VsdF9hbm5vdF9zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZiA8LSByZXN1bHRfYW5ub3Rfc2lnX2RmWwogICEoKHJlc3VsdF9hbm5vdF9zaWdfZGYkUm93Lm5hbWVzICVpbiUgY3Jvc3NoeWIpIHwKICAgICAgKHJlc3VsdF9hbm5vdF9zaWdfZGYkUm93Lm5hbWVzICVpbiUgc21va2luZy5zaWcucHJvYmVzKSksCiAgXSAjZGltOiA2NDIgMjQKCiMjIyBBZGQgYW5ub3RhdGlvbgpsaWJyYXJ5KGNvTWV0aERNUikKcmVzdWx0X2Fubm90X3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmJGNocm9tIDwtIGFzLmNoYXJhY3RlcigKICByZXN1bHRfYW5ub3Rfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYkc2VxbmFtZXMKKQpyZXN1bHRfZmluYWwgPC0gQW5ub3RhdGVSZXN1bHRzKHJlc3VsdF9hbm5vdF9zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZikKCnJlc3VsdF9maW5hbF9vcmRlcmVkIDwtIHJlc3VsdF9maW5hbFsKICAsYygxOjQsIDI2OjI5LCA1OjI0KQogIF0Kd3JpdGUuY3N2KAogIHJlc3VsdF9maW5hbF9vcmRlcmVkICU+JSBhcy5kYXRhLmZyYW1lKCksCiAgcGFzdGUwKGRpci5yZXN1bHQsICJtZXRhX2FuYWx5c2lzX21hdGNoZWRfc2luZ2xlX2NwZ19zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZi5jc3YiKSwKICByb3cubmFtZXMgPSBGQUxTRQopICAgICAgICAgICAKYGBgCgojIyBSZXN1bHRzCgpgYGB7UiwgZWNobyA9IEZBTFNFfQpyZXMgPC0gcmVhZHI6OnJlYWRfY3N2KAogIHBhc3RlMChkaXIucmVzdWx0LCAibWV0YV9hbmFseXNpc19tYXRjaGVkX3NpbmdsZV9jcGdfc2lnX25vX2Nyb3NzSHliX3Ntb2tpbmdfZGYuY3N2IiksCiAgY29sX3R5cGVzID0gcmVhZHI6OmNvbHMoKQopIApyZXMgPC0gcmVzW29yZGVyKHJlcyRmZHIpLF0KcmVzCmBgYAoKCmBgYHtSLCBldmFsID0gRkFMU0UsIGluY2x1ZGUgPSBGQUxTRX0KIyMgQ29tcGFyZSB3aXRoIHNpZyBDcEdzIGZyb20gbWFpbiBhbmFseXNpcwoKIyMjIENhbGwgaW4gZGF0YXNldHMKbWFpbl9jcGdzIDwtIHJlYWQuY3N2KAogIHBhc3RlMChkaXIuc2luZ2xlLmNwZywgIm1ldGFfYW5hbHlzaXNfc2luZ2xlX2NwZ19zaWdfbm9fY3Jvc3NIeWJfc21va2luZ19kZi5jc3YiKQopICNkaW06IDM3NTEgMjgKbWFpbl9jcGdzIDwtIG1haW5fY3BncyAlPiUgcHVsbChjcGcpICU+JSBhcy5jaGFyYWN0ZXIKCm1hdGNoZWRfY3BncyA8LSByZWFkLmNzdigKICBwYXN0ZTAoZGlyLnJlc3VsdCwgIm1ldGFfYW5hbHlzaXNfbWF0Y2hlZF9zaW5nbGVfY3BnX3NpZ19ub19jcm9zc0h5Yl9zbW9raW5nX2RmLmNzdiIpCikgI2RpbTogNjQyIDI0Cm1hdGNoZWRfY3BncyA8LSBtYXRjaGVkX2NwZ3MgJT4lIHB1bGwoMSkgJT4lIGFzLmNoYXJhY3RlcgoKCiMjIyBDcmVhdGUgVmVubiBEaWFncmFtCmxpYnJhcnkoVmVubkRpYWdyYW0pCmxpYnJhcnkoZ2dwbG90MikKCiMgTWFrZSB0aGUgcGxvdAp2ZW5uIDwtIHZlbm4uZGlhZ3JhbSgKICB4ID0gbGlzdCgKICAgIG1haW5fY3BncyAlPiUgdW5pcXVlICwKICAgIG1hdGNoZWRfY3BncyAlPiUgdW5pcXVlCiAgKSwgCiAgY2F0ZWdvcnkubmFtZXMgPSBjKCJNYWluIGFuYWx5c2lzIHNpZy4gcHJvYmVzIiAsICJNYXRjaGVkIGFuYWx5c2lzIHNpZy4gcHJvYmVzIiApLAogIGZpbGVuYW1lID0gZmlsZS5wYXRoKGRpci5yZXN1bHQsICcvdmVubl9tYWluX21hdGNoZWRfc2lnX2NwZ3MucG5nJyksCiAgb3V0cHV0ID0gVFJVRSAsCiAgaW1hZ2V0eXBlID0gInBuZyIgLAogIGhlaWdodCA9IDcwMCAsCiAgd2lkdGggPSA3MDAgLAogIHJlc29sdXRpb24gPSAzMDAsCiAgY29tcHJlc3Npb24gPSAibHp3IiwKICBsd2QgPSAxLAogIGNvbCA9IGMoIiM0NDAxNTRmZiIsICcjMjE5MDhkZmYnKSwKICBmaWxsID0gYyhhbHBoYSgiIzQ0MDE1NGZmIiwwLjMpLCBhbHBoYSgnIzIxOTA4ZGZmJywwLjMpKSwKICBjZXggPSAwLjUsCiAgY2F0LmNleCA9IDAuMywKICBjYXQuZGVmYXVsdC5wb3MgPSAib3V0ZXIiLAogIGNhdC5wb3MgPSBjKDAsIDApLAogIGNhdC5kaXN0ID0gYygwLjAxLCAwLjAxKQopCmBgYAoKCgoKIyBTZXNzaW9uIGluZm9ybWF0aW9uCmBgYHtSfQpkZXZ0b29sczo6c2Vzc2lvbl9pbmZvKCkKYGBgCgo=