Chi Yen Tseng1, Christine M. Custer2, Thomas W. Custer2, Paul M. Dummer2, Natalie Karouna‐Renier3 and Cole W. Matson1

  1. Department of Environmental Science, The Institute of Ecological, Earth, and Environmental Sciences (TIE3S), and the Center for Reservoir and Aquatic Systems Research (CRASR), Baylor University, Waco, Texas 76798, United States
  2. Upper Midwest Environmental Sciences Center, U.S. Geological Survey, La Crosse, Wisconsin 54603, United States
  3. U.S. Geological Survey, Eastern Ecological Science Center (EESC) at Patuxent, Beltsville, Maryland 20705, United States


Image of GLRI_site All the sites included in this study

Detail location can be downloaded here at GLRI_site_details


Load all packages

library(glmnet)
library(edgeR)
library(readr)
library(tidyverse)
library(foreach)
library(doParallel)
library(reshape2)
library(stringr)
library(ggpubr)
library(ggrepel)
library(ComplexHeatmap)
library(ggplot2)
library(GGally)


Prepare global gene expression data and contaminant data


GLRI_coldata
chemistry data (“Dioxin” “MultiRedsiduePest” “PAHs” “PBDEs” “LRPCBs” “HRPCBs” “Pesticides” “PFCs” “PPCPs”)  
- data.contaminants.majorSubset.geo.bysite
geometric mean of each contaminant key by site
contaminants.coldata.subset
only includes the nestlings with genomic information and removes PAHs data
ID.convert
gene ID convertion table between tree swallow gene pseudo name and chicken gene name
siteMAP.all
site ID and propersite name convertion Table
dds.bothSex.adjusted or counts.tswallow
normalized count matrix for all nestlings, adjusted for batch difference
Cont.major.list
major contaminants (“Nonachlor, cis-_C”, “Heptachlor Epoxide_C”, “Nonachlor, trans-_C”, “PFDA_C”, “Chlordane, oxy-_C”, “Total Parent PAHs”, “Total PBDE”, “Total PAHs”,“Total PCBs”,“Total_PFCs”)
counts.tswallow.norm
vst transformed normalized counts


1. Determine lasso regression models to predict PCB concentrations


Load top genes list


topgenelistPCBs.rds, including 91 genes which were appeared more than 5% in the first round of lasso regression analysis in the cross-validation. Detail script of first round lasso models were provided here

Load self-determined lasso training and permorance evaluation models


  • lasso.by.site.PCBs.top100.lasso(i,Con,Con_ind, test.genelist, s, lam.m)
    leave one ( i site) out to build lasso regression models to predict concentrations of contaminant (Con and Con_ind) using top predictor genes (genelist), model parameter lamda (s: 1se and lam.m: minimum log10 lamda value to test model fit)
  • Run_lasso.by.site.PCBs.top100.lasso(re, Con, Con_ind, s, lam.m)
    Run lasso.by.site.PCBs.top100.lasso function re times
  • evaluation_funciton.bysite
    calculate site performance by site, mean square error (MSE), accuracy, and error rate are calculated based on predicted site geometric mean versus actual geometric mean
  • evaluation_funciton.byindividual
    calculate site performance by individual nest, ignoring site factor, mean square error (MSE), accuracy, and error rate are calculated based on predicted vs actual value
## Con, Con_ind: contaminant type; test.genelist: top genes selected from the first run; s: lamda.sequence (l.1se,l.min,l.median); lam.m: lambda = 10^seq(0,lam.m,length=600)
lasso.by.site.PCBs.top100.lasso <- function(i,Con,Con_ind, test.genelist, s, lam.m) {
  ## genomic samples with the same nest id
  nestid <- contaminants.coldata.subset2 %>% filter(Key == Con_ind) %>% dplyr::pull(MergeID)
  match1 <- colnames(counts.tswallow.norm) %in% nestid
  counts.con  <-  counts.tswallow.norm[,match1]
  counts.con.batch <- as.factor(as.character(dds.bothSex.adjusted$batch2)[match1])
  counts.con.sex <- as.factor(as.character(dds.bothSex.adjusted$sex)[match1])
  contamin.raw <- contaminants.coldata.subset2 %>% filter(Key == Con_ind) %>% dplyr::pull(value.adjust) %>% log10()
  contamin.matrix <- contaminants.coldata.subset2 %>% dplyr::slice(match(colnames(counts.con), contaminants.coldata.subset2$MergeID)) %>% filter(Key == Con_ind)
  counts.con.contamin <- log10(contamin.matrix$value.adjust) ## get log10 value to suppress potential outlier
  site.list = unique(as.character(dds.bothSex.adjusted$site[match1]))
  ## Building matrix
  if (length(levels(counts.con.batch)) > 1) {
    counts.con.t <- as.data.frame(t(counts.con[test.genelist,]))
    counts.con.t <- cbind(counts.con.t, batch = counts.con.batch, sex = counts.con.sex, contaminant = counts.con.contamin)
    m <- model.matrix(contaminant ~ batch +., counts.con.t) # m for training
    m <- m[,-1] } else {
      counts.con.t <- as.data.frame(t(counts.con[test.genelist,]))
      counts.con.t <- cbind(counts.con.t, sex = counts.con.sex, contaminant = counts.con.contamin)
      m <- model.matrix(contaminant ~ ., counts.con.t)
      m <- m[,-1] }
  ## to include all genomic samples as m2
  nestid <- contaminants.coldata.subset2 %>% filter(Key == Con_ind) %>% dplyr::pull(MergeID)
  counts.con2  <-  counts.tswallow.norm
  counts.con.batch2 <- as.factor(as.character(dds.bothSex.adjusted$batch2))
  counts.con.sex2 <- as.factor(as.character(dds.bothSex.adjusted$sex))

  ## Building matrix
  if (length(levels(counts.con.batch2)) > 1) {
    counts.con.t2 <- as.data.frame(t(counts.con2[test.genelist,]))
    counts.con.t2 <- cbind(counts.con.t2, batch = counts.con.batch2, sex = counts.con.sex2)
    m2 <- model.matrix(~ batch +., counts.con.t2) # m2 for validation in leave one site out
    m2 <- m2[,-1] } else {
      counts.con.t2 <- as.data.frame(t(counts.con2[test.genelist,]))
      counts.con.t2 <- cbind(counts.con.t2, sex = counts.con.sex2)
      m2 <- model.matrix( ~ ., counts.con.t2)
      m2 <- m2[,-1] }

  site.count.nest.subset = table(str_sub(colnames(counts.con),3,4))
  site.count.genomic.subset = table(str_sub(colnames(counts.con2),3,4))
  # sites_verifications = c("ER", "HW", "LS", "PB", "SC", "SP", "ST", "TH", "WK")

  ## CV leave one out by site
    test.site = str_sub(row.names(m),3,4) %in% site.list[i]
    l.1se <- {} ; l.min <- {}

    for (j in 1:20) {
      cvfit <- cv.glmnet(x=m[!test.site,], y=counts.con.t$contaminant[!test.site], nfold = 10, alpha = 1, lambda = 10^seq(0,lam.m,length=600), relax =FALSE)
      
      l.1se <- c(l.1se, cvfit$lambda.1se)
      l.min <- c(l.min, cvfit$lambda.min)
      
    } # repeat 20 times and pick median lamda to avoid picking extreme in 10 folds cross validation
    l.1se <- median(l.1se)
    l.min <- median(l.min)
    l.median <- exp(mean(c(log(l.min),log(l.1se))))
    lamda.sequence <- c(l.1se,l.min,l.median)

    ByNestResult.withlamdaP <- {}
    training.site = sample(which(!test.site), round(sum(!test.site)*0.9))
    fit.lasso <- glmnet(x=m[training.site,], y=counts.con.t$contaminant[training.site], alpha = 1, lambda = 10^seq(0,lam.m,length=600)) # lasso model using training set
    
    ## get all the metrics c(features, dev.ratio, lamda)
    variables = coef(fit.lasso, s = lamda.sequence[s])[,1]
    variables = variables[variables != 0][-1]
    dev.ratio = fit.lasso$dev.ratio[which(sort(c(lamda.sequence[s],10^seq(0,lam.m,length=600)),decreasing = TRUE) == lamda.sequence[s])[1] -1]
    
    ## get all predictions using all avaialble genomic samples
    test.site2 = str_sub(row.names(m2),3,4) == site.list[i]
    
    ## lasso
    predict.all.genomic.samples.subset <- predict(fit.lasso, newx = m[test.site,], s = lamda.sequence[s]) ## only use overlapped portion
    predict.all.genomic.samples <- predict(fit.lasso, newx = m2[test.site2,], s = lamda.sequence[s]) ## only use overlapped portion

    predict.all.genomic.samples.subset.lasso = predict.all.genomic.samples.subset[,1]
    predict.all.genomic.samples.lasso = predict.all.genomic.samples[,1]

    predict.all.genomic.samples.subset.lasso.mean = mean(predict.all.genomic.samples.subset.lasso)
    names(predict.all.genomic.samples.subset.lasso.mean) = site.list[i]
    predict.all.genomic.samples.lasso.mean = mean(predict.all.genomic.samples.lasso)
    names(predict.all.genomic.samples.lasso.mean) = site.list[i]

    ## combine
    result = list(variables =variables, dev.ratio = dev.ratio ,lamda.sequence = lamda.sequence, predict.all.genomic.samples.subset.lasso = predict.all.genomic.samples.subset.lasso, predict.all.genomic.samples.subset.lasso.mean = predict.all.genomic.samples.subset.lasso.mean, predict.all.genomic.samples.lasso = predict.all.genomic.samples.lasso, predict.all.genomic.samples.lasso.mean = predict.all.genomic.samples.lasso.mean)
  return(result)
}
Run_lasso.by.site.PCBs.top100.lasso <- function(re, Con, Con_ind, s, lam.m) {
  lassoCVbysite <- foreach(i = rep(1:31, each = re), .packages = c("dplyr","glmnet","stringr"), .combine = 'cbind', .export = c("lasso.by.site.PCBs.top100.lasso", "contaminants.coldata.subset2","counts.tswallow.norm","dds.bothSex.adjusted", "test.genelist", "data.contaminants.majorSubset.geo.bysite")) %dopar% {
    LassoCVbysite_result <- Vectorize(lasso.by.site.PCBs.top100.lasso(i,Con,Con_ind, test.genelist, s, lam.m))
    return(LassoCVbysite_result)
  }

  return(lassoCVbysite)
}
evaluation_funciton.bysite <- function(Result) {
  result.all <- {}
  evaluation.result <- {}
  predictions = Result
  ## geoMean: only use overlapped genomic site individulas training samples to calculate geoMean
  predictions2 = data.frame(site = names(Result), predictions, geoMean = contaminant.geoMean.bysite.genomic[names(Result)], b33 =  quantile(contaminant.geoMean.bysite, probs = 0.33, na.rm = FALSE, names = FALSE), t33 = quantile(contaminant.geoMean.bysite, probs = 0.67, na.rm = FALSE, names = FALSE))
  predictions3 = cbind(predictions2, is.top33 = predictions2$geoMean >= predictions2$t33, is.bottom33 = predictions2$geoMean <= predictions2$b33, is.top.predict = predictions2$predictions >= predictions2$t33, is.bottom.predict = predictions2$predictions <= predictions2$b33)
  predictions.all = predictions3
  predictions.all$delta = (predictions.all$predictions - predictions.all$geoMean)^2
  MSE = sum(predictions.all$delta)/nrow(predictions.all)
  
  error.top =  sum(predictions.all$is.top.predict & predictions.all$is.bottom33)/ sum(predictions.all$is.top.predict)
  
  error.bottom = sum(predictions.all$is.bottom.predict & predictions.all$is.top33)/ sum(predictions.all$is.bottom.predict) 
  
  error.both = sum((predictions.all$is.top.predict & predictions.all$is.bottom33)  | (predictions.all$is.bottom.predict & predictions.all$is.top33))/ sum(predictions.all$is.bottom.predict | predictions.all$is.top.predict) 
  
  accuracy.top =  sum(predictions.all$is.top.predict & predictions.all$is.top33)/ sum(predictions.all$is.top.predict) 
  
  accuracy.bottom = sum(predictions.all$is.bottom.predict & predictions.all$is.bottom33)/ sum(predictions.all$is.bottom.predict) 
  
  accuracy.both = sum((predictions.all$is.top.predict & predictions.all$is.top33)  | (predictions.all$is.bottom.predict & predictions.all$is.bottom33))/ sum(predictions.all$is.bottom.predict | predictions.all$is.top.predict) 
  
  testing_ratio = c(top_ratio = sum(predictions.all$is.top33)/nrow(predictions.all), bottom_ratio = sum(predictions.all$is.bottom33)/nrow(predictions.all))
  
  performance.result = c(error.top = error.top,error.bottom = error.bottom, error.both = error.both, accuracy.top = accuracy.top ,accuracy.bottom = accuracy.bottom,accuracy.both = accuracy.both, testing_ratio, MSE = MSE)
  evaluation.result = list(predictions.by.site=predictions.all, performance.result = performance.result)
  return(evaluation.result)
}
evaluation_funciton.byindividual <- function(Result) {
  result.all <- {}
  evaluation.result <- {}
  predictions = Result
  ## geoMean: only use overlapped genomic site individulas training samples to calculate geoMean
  predictions2 = data.frame(mergeID = names(Result), predictions, actural = contamin.array[names(predictions)] , b33 =  quantile(contaminant.geoMean.bysite, probs = 0.33, na.rm = FALSE, names = FALSE), t33 = quantile(contaminant.geoMean.bysite, probs = 0.67, na.rm = FALSE, names = FALSE))
  predictions3 = cbind(predictions2, is.top33 = predictions2$actural >= predictions2$t33, is.bottom33 = predictions2$actural <= predictions2$b33, is.top.predict = predictions2$predictions >= predictions2$t33, is.bottom.predict = predictions2$predictions <= predictions2$b33)
  predictions3 = predictions3[!is.na(predictions3$is.bottom33),]
  predictions.all = predictions3
  predictions.all$delta = (predictions.all$predictions - predictions.all$actural)^2
  MSE = sum(predictions.all$delta)/nrow(predictions.all)
  
  
  error.top =  sum(predictions.all$is.top.predict & predictions.all$is.bottom33)/ sum(predictions.all$is.top.predict)
  
  error.bottom = sum(predictions.all$is.bottom.predict & predictions.all$is.top33)/ sum(predictions.all$is.bottom.predict) 
  
  error.both = sum(predictions.all$is.top.predict & predictions.all$is.bottom33 | predictions.all$is.bottom.predict & predictions.all$is.top33)/ sum(predictions.all$is.top.predict | predictions.all$is.bottom.predict) 
  
  accuracy.top =  sum(predictions.all$is.top.predict & predictions.all$is.top33)/ sum(predictions.all$is.top.predict) 
  
  accuracy.bottom = sum(predictions.all$is.bottom.predict & predictions.all$is.bottom33)/ sum(predictions.all$is.bottom.predict) 
  
  accuracy.both = sum((predictions.all$is.top.predict & predictions.all$is.top33)  | (predictions.all$is.bottom.predict & predictions.all$is.bottom33))/ sum(predictions.all$is.bottom.predict | predictions.all$is.top.predict) 
  
  testing_ratio = c(top_ratio = sum(predictions.all$is.top33)/nrow(predictions.all), bottom_ratio = sum(predictions.all$is.bottom33)/nrow(predictions.all))
  
  performance.result = c(error.top = error.top,error.bottom = error.bottom, error.both = error.both, accuracy.top = accuracy.top ,accuracy.bottom = accuracy.bottom,accuracy.both = accuracy.both, testing_ratio, MSE = MSE)
  evaluation.result = list(predictions.all,performance.result)
  return(evaluation.result)
}

Run Lasso regression analysis between top Gene (91 genes) and PCB concentrations


Run lasso analysis with leave one site out cross validatino with 40 rounds, 40 * 31 sites = 1240 cross-validation and resample 90% of training individuals every trial.

### Total PCBs
Con="PCBs_data";Con_ind="TOTAL PCBs"
## refresh between chemicals 
lamda.sequence <- {}
Result.lasso.assessment <- {}
variables <- {}
predict.site.test <- {}
## result measures 
rlassoResult <- list()

# prepare data 
contaminants.coldata.subset2 <-contaminants.coldata.subset
### Individual chemicals 
contaminants.coldata.subset2 <- contaminants.coldata.subset2 %>% filter(Key == Con_ind)
contaminants.coldata.subset2 <- contaminants.coldata.subset2 %>% filter(!is.na(value.adjust)) ## remove those with value == "NR"
## combine all duplicated items using mean value becasue they have the same nest id 
contaminants.coldata.subset2 <- contaminants.coldata.subset2 %>% group_by(MergeID,Species,Matrix,AOC,Proper_Site,Key,type,class,MergeSite,proper_site2) %>% summarise(value.adjust2 = mean(value.adjust)) %>% ungroup()
colnames(contaminants.coldata.subset2)[colnames(contaminants.coldata.subset2) == "value.adjust2"] <- "value.adjust" ## change the name back
# print(sum(duplicated(contaminants.coldata.subset2$MergeID))) ## check duplication again
## read topgenelist 91 genes 
test.genelist = readRDS("topgenelistPCBs.rds")$total.PCBs

contaminant.geoMean.bysite <- data.contaminants.majorSubset.geo.bysite %>% filter(Key == Con_ind) %>% dplyr::pull(value.geoMean) %>% log10() ## log10 of contaminant value
names(contaminant.geoMean.bysite) <- data.contaminants.majorSubset.geo.bysite %>% filter(Key == Con_ind) %>% dplyr::pull(MergeSite) 
nestid <- contaminants.coldata.subset2 %>% filter(Key == Con_ind) %>% dplyr::pull(MergeID)
match1 <- colnames(counts.tswallow.norm) %in% nestid
counts.con  <-  counts.tswallow.norm[,match1]
contamin.matrix <- contaminants.coldata.subset2 %>% dplyr::slice(match(colnames(counts.con), contaminants.coldata.subset2$MergeID)) %>% filter(Key == Con_ind) %>% mutate(value.adjust.log = log10(value.adjust))
contamin.array <- contamin.matrix$value.adjust.log
names(contamin.array) = contamin.matrix$MergeID
contamin.matrix.mean <- contamin.matrix %>% group_by(MergeSite) %>% summarise(geoMean = mean(value.adjust.log)) %>% ungroup()
contamin.matrix.mean$MergeSite[31] = "NA"
contaminant.geoMean.bysite.genomic <- contamin.matrix.mean$geoMean
names(contaminant.geoMean.bysite.genomic) <- contamin.matrix.mean$MergeSite
# Run lasso analysis with leave one site out cross validatino with 40 rounds, 40 * 31 sites = 1240 cross-validation and resample 90% of training individuals every trial. 
print(c("2nd lasso cv",Con_ind))
t1 =  Sys.time()
cl <- parallel::makeCluster(12)
doParallel::registerDoParallel(cl)
t1 = Sys.time()
PCBs_CV_bySite_result = Run_lasso.by.site.PCBs.top100.lasso(re = 40, Con = Con, Con_ind = Con_ind, s = 1,lam.m = -2)
t2 =  Sys.time()
t2 -t1
parallel::stopCluster(cl)

# Result list : 1. variables, 2.  dev.ratio 3. lamda.sequence 4. predict.all.genomic.samples.subset.lasso 5. predict.all.genomic.samples.subset.lasso.mean 6. predict.all.genomic.samples.lasso 
# 7. predict.all.genomic.samples.lasso.mean 
# saveRDS(PCBs_CV_bySite_result, "PCBs_CV_bySite_result2nd.rds")

Lasso regression analysis results for predicting PCB concentrations

PCBs_CV_bySite_result <- readRDS("PCBs_CV_bySite_result2nd.rds")
# PCBs_CV_bySite_result by site
Result = unlist(unname(PCBs_CV_bySite_result[5,])) ##  genomic samples overlapped with contaminated samples 
PCBs.performance.bysite.overlapped.report = evaluation_funciton.bysite(Result = Result) 

Result = unlist(unname(PCBs_CV_bySite_result[7,])) ## all genomic samples 
PCBs.performance.bysite.allgenomic.report = evaluation_funciton.bysite(Result = Result) 
# 
# ## overlapped portion PCBs
# $performance.result
#  error.top    error.bottom      error.both    accuracy.top accuracy.bottom   accuracy.both       top_ratio    bottom_ratio             MSE 
#     0.132075472     0.006072874     0.050065876     0.569811321     0.844129555     0.748353096     0.322580645     0.451612903     0.230140947 

# # ## all genomic samples PCBs
# $performance.result
# error.top    error.bottom      error.both    accuracy.top accuracy.bottom   accuracy.both       top_ratio    bottom_ratio             MSE 
#     0.090551181     0.006507592     0.036363636     0.614173228     0.854663774     0.769230769     0.322580645     0.451612903     0.233187702 

# PCBs_CV_bySite_result by individual, only use overlapped portion 
Result = unlist(unname(PCBs_CV_bySite_result[4,])) ##  genomic samples overlapped with contaminated samples 
PCBs.performance.byindividual.overlapped.report = evaluation_funciton.byindividual(Result = Result) 

# error.top    error.bottom      error.both    accuracy.top accuracy.bottom   accuracy.both       top_ratio    bottom_ratio             MSE 
#       0.1002028       0.1817585       0.1452929       0.7346856       0.6286089       0.6760385       0.4234694       0.3724490       0.3985988 

## other performance metrics
# determine predictor genes; variablers
variables.length.summary <-  summary(sapply(1:length(PCBs_CV_bySite_result[1,]), function(x) length(PCBs_CV_bySite_result[1,][[x]])))
variables.length.p95 <- quantile(sapply(1:length(PCBs_CV_bySite_result[1,]), function(x) length(PCBs_CV_bySite_result[1,][[x]])),0.95)
variables <- names(sort(table(names(unlist(unname(PCBs_CV_bySite_result[1,])))),decreasing = TRUE)[1:variables.length.p95])
variables <- list(id = variables, name = na.omit(ID.convert$GeneName[match(variables,ID.convert$GeneID2)]))
variables.coef = unlist(lapply(1:ncol(PCBs_CV_bySite_result), function(x) na.omit(PCBs_CV_bySite_result[1,][[x]][variables$id])))
geomean_func = function(x) {exp(mean(log(x)))}
variables.coef.aggregate = aggregate(variables.coef, list(Gene = names(variables.coef)),mean)
colnames(variables.coef.aggregate)[2] = "coef"
variables.coef.aggregate = data.frame(name = ID.convert$GeneName[match(variables.coef.aggregate$Gene, ID.convert$GeneID2)], variables.coef.aggregate)

# R square, or deviance ratio

# dev.ratio
dev.ratio.n <- ncol(PCBs_CV_bySite_result)
dev.ratio.mean <- mean(unlist(unname(PCBs_CV_bySite_result[2,])))
dev.ratio.s <- sd(unlist(unname(PCBs_CV_bySite_result[2,])))
margin <- qt(0.975,df=dev.ratio.n-1)*dev.ratio.s/sqrt(dev.ratio.n)
dev.ratio.PCBs = c(mean = dev.ratio.mean, margin = margin)
# print(dev.ratio.PCBs)
# mean      margin 
# 0.766489759 0.001045139 


# ## 
predict.result = unlist(unname(PCBs_CV_bySite_result[4,])) # only the overlapping part 
predict.result2 = unlist(unname(PCBs_CV_bySite_result[5,])) # only the overlapping part site average 
Predictions.by.site = tibble(site= names(predict.result2), predictions= predict.result2,  genomic.PCB.mean = contaminant.geoMean.bysite.genomic[names(predict.result2)], proper_site2 = siteMAP.all$propersite[match(names(predict.result2), siteMAP.all$SiteID)])
Predictions.by.individual = tibble(MergeID = names(predict.result), proper_site2 = siteMAP.all$propersite[match(str_sub(names(predict.result),3,4), siteMAP.all$SiteID)], predicted.PCBs = predict.result,  measured.PCBs = contamin.array[names(predict.result)])
PCBs.predictions.lasso.aggregate = list(by.site = Predictions.by.site, by.individual = Predictions.by.individual)
# saveRDS(PCBs.predictions.lasso.aggregate, "PCBs.predictions.lasso.aggregate.rds")  

# list all predictions, variables, R2 and model performance 
PCBs.performance.report <- list(PCBs.performance.bysite.overlapped.report = PCBs.performance.bysite.overlapped.report, PCBs.performance.bysite.allgenomic.report = PCBs.performance.bysite.allgenomic.report, PCBs.performance.byindividual.overlapped.report = PCBs.performance.byindividual.overlapped.report, PCB.variables = list(variables,variables.coef.aggregate), PCBs.dev.ratio = dev.ratio.PCBs)
# saveRDS(PCBs.performance.report, "PCBs.performance.report.rds")


Because not evey nest was selected for measuring chemistry, the following performance metrics are calculated by

  • PCBs_CV_bySite_result by site: only include those nest with chemistry measurements and calculate accuracy and errors using predicted site geometric mean
  • PCBs.performance.bysite.allgenomic: include all nest with or without chemistry measurements and calculate accuracy and errors using predicted site geometric mean
  • PCBs.performance.byindividual: include all nest with chemistry measurements and calculate accuracy and errors using predicted individual concentrations.
Result = unlist(unname(PCBs_CV_bySite_result[5,])) ##  genomic samples overlapped with contaminated samples 
PCBs.performance.bysite.overlapped.report = evaluation_funciton.bysite(Result = Result) 

Result = unlist(unname(PCBs_CV_bySite_result[7,])) ## all genomic samples 
PCBs.performance.bysite.allgenomic.report = evaluation_funciton.bysite(Result = Result) 
# 
# ## overlapped portion PCBs
# $performance.result
# error.top    error.bottom    accuracy.top accuracy.bottom   accuracy.both       top_ratio    bottom_ratio             MSE
# 0.111969112     0.004056795     0.583011583     0.845841785     0.755319149     0.322580645     0.451612903     0.229426453
# ## all genomic samples PCBs
# $performance.result
# error.top    error.bottom    accuracy.top accuracy.bottom   accuracy.both       top_ratio    bottom_ratio             MSE
# 0.065306122     0.004347826     0.632653061     0.847826087     0.773049645     0.322580645     0.451612903     0.232414433

# PCBs_CV_bySite_result by individual, only use overlapped portion 
Result = unlist(unname(PCBs_CV_bySite_result[4,])) ##  genomic samples overlapped with contaminated samples 
PCBs.performance.byindividual.overlapped.report = evaluation_funciton.byindividual(Result = Result) 

# error.top    error.bottom    accuracy.top accuracy.bottom   accuracy.both       top_ratio    bottom_ratio             MSE 
# 0.1002028       0.1817585       0.7346856       0.6286089       0.6760385       0.4234694       0.3724490       0.3985988 

performance.metrics <- round(data.frame(PCBs.performance.bysite.overlapped.report$performance.result, PCBs.performance.bysite.allgenomic.report$performance.result, PCBs.performance.byindividual.overlapped.report[[2]]),3)
colnames(performance.metrics) <- c("by.site (with chemistry only)", "by.site (all genomic samples)", "by.nest (with chemistry only)")
# saveRDS(performance.metrics, "PCB.performance.metrics.rds")


PCBs lasso prediction results

  • Predicted vs actural yy plot by site
    predicted vs actural PCB concentration; color by those site with prediction value above or below top 1/3 or bottom 1/3 PCB among all the sites
  • Predicted vs actural yy plot by site with 99.9% confidence interval
    add 99.9% CI on Predicted vs actural yy plot by site
  • Remediation effectiveness evaluation in Waukegan
    add Waukegan value throughout the year on Predicted vs actural yy plot by site
  • Predicted vs actural yy plot by nest
    the model is trained by individual nest
  • model performance
    accuracy and error ; accurate predicting top 1/3 or bottom 1/3 ; error : predicting top 1/3 but end up bottom 1/3 or vice versa
  • Predictor genes selected in the model
    list the predictor genes in the model

Predicted vs actural yy plot by site

Predicted vs actural PCB concentrations yy plot by site

Predicted vs actural PCB concentrations yy plot by site

Predicted vs actural yy plot by site with 99.9% confidence interval

Remediation effectiveness evaluation in Waukegan

Predicted vs actural yy plot by nest

model performance

PCBs lasso regression model performance metrics
by.site (with chemistry only) by.site (all genomic samples) by.nest (with chemistry only)
error.top 0.132 0.091 0.100
error.bottom 0.006 0.007 0.182
error.both 0.050 0.036 0.145
accuracy.top 0.570 0.614 0.735
accuracy.bottom 0.844 0.855 0.629
accuracy.both 0.748 0.769 0.676
top_ratio 0.323 0.323 0.423
bottom_ratio 0.452 0.452 0.372
MSE 0.230 0.233 0.399

Predictor genes selected in the model

PCBs top predictor genes and coefficient
name coef Gene
OPRM1 0.0626781 TacBic00000248
HHIPL2 0.0629860 TacBic00000723
BROX -0.0765754 TacBic00000728
ALK -0.0343974 TacBic00000778
ahrr 0.2032059 TacBic00001060
DNAH5 -0.0421367 TacBic00001152
MT-CO2 0.0306515 TacBic00001169
EDN1 0.2580644 TacBic00001272
MRC1 -0.0445834 TacBic00001408
NA -0.0345405 TacBic00001866
PDE1A -0.0335550 TacBic00002231
TDH -0.0368326 TacBic00002440
WDR35 -0.0689035 TacBic00002543
CPSF3 -0.1160736 TacBic00002590
LMBRD1 -0.0738019 TacBic00002694
LD -0.0339494 TacBic00004095
PLEKHH1 -0.1973106 TacBic00004124
NA 0.0719234 TacBic00004433
NA -0.0637095 TacBic00005036
NA -0.0547057 TacBic00005289
MAPK6 -0.0716260 TacBic00006048
CCDC78 0.0286038 TacBic00006808
DAGLB -0.0965885 TacBic00006993
HYDIN 0.0160680 TacBic00007206
FLT4 -0.0471608 TacBic00007722
DBN1 -0.1532679 TacBic00007823
NA -0.1268583 TacBic00007892
SLC17A9 0.0313378 TacBic00008664
ZC2HC1A -0.0751931 TacBic00009014
LOC423119 -0.1004947 TacBic00009296
MN1 -0.1422477 TacBic00009683
NOS1 -0.1495260 TacBic00009882
NA -0.0715350 TacBic00010159
SLCO1A2 -0.0209454 TacBic00010681
PIK3C3 0.0274661 TacBic00011042
ZNF185L -0.0567324 TacBic00011230
ARHGAP36 0.1152496 TacBic00011246
SPIA9 0.0417792 TacBic00011339
ETNPPL -0.0423135 TacBic00011604
ARHGAP6 0.0735161 TacBic00011724
RPL3 0.0500954 TacBic00012455
NA 0.0131619 TacBic00012685
CLDN18 0.0408827 TacBic00012986
NA -0.1276152 TacBic00013588
P2RY10 0.0980806 TacBic00013697
HMGCR 0.0355349 TacBic00013782
ERRFI1 -0.0187293 TacBic00014439
ABCA9 0.0371875 TacBic00014601
NA -0.0291986 TacBic00015118
CMTM4 0.0638626 TacBic00015406
CHSY3 -0.0525856 TacBic00015571
KCNIP1 0.1172414 TacBic00015722
NA 0.0512586 TacBic00016990
NA 0.0300411 TacBic00017215
CYP1A5 0.0917243 TacBic00018777
CD248 0.2015480 TacBic00019767
CYP1A4 0.0381010 TacBic00019894
ERAL1 -0.1732755 TacBic00020260
LOC776507 0.0547346 TacBic00021387
NA -0.3323687 TacBic00021964
NA 0.0631366 TacBic00022010
SNX27 -0.1014123 TacBic00022200

MSE by sample size

PCBs top gene predictors functions and pathways

PCBs top predictor genes heatmap
correlation between PCB concentration, top predictor gene logFC, top predictor gene model coef. , top predictor gene up or downregulated and their corresponding Reactome pathways
PCBs lasso selected genes heatmap deatil functions and pathways
detail GO terms and Reactome pathways
PCB correlation between logFC logCPM and model coef
the correlation between top predictor genes between logFC, logCPM (counts), and PCB model coef. measured in top 1/3 logFC individuals

PCBs top predictor genes heatmap

PCBs lasso selected genes heatmap deatil functions and pathways

PCB correlation between logFC logCPM and model coef


2. Determine lasso regression models to predict PAH concentrations


Start from first round top genes (110) genes and refine lasso regression analysis for the second round

Load data


GLRI_coldata
chemistry data (“Dioxin” “MultiRedsiduePest” “PAHs” “PBDEs” “LRPCBs” “HRPCBs” “Pesticides” “PFCs” “PPCPs”)
data.contaminants.majorSubset.geo.bysite
geometric mean of each contaminant key by site
contaminants.coldata.subset
only includes the nestlings with genomic information and removes PAHs data
ID.convert
gene ID convertion table between tree swallow gene pseudo name and chicken gene name
siteMAP.all
site ID and propersite name convertion Table
dds.bothSex.adjusted or counts.tswallow
normalized count matrix for all nestlings, adjusted for batch difference
Cont.major.list
major contaminants (“Nonachlor, cis-_C”, “Heptachlor Epoxide_C”, “Nonachlor, trans-_C”, “PFDA_C”, “Chlordane, oxy-_C”, “Total Parent PAHs”, “Total PBDE”, “Total PAHs”,“Total PCBs”,“Total_PFCs”)
counts.tswallow.norm
vst transformed normalized counts
topgene.list
110 top genes determined from the first round of lasso regression analysis. Detail can be found here

Load self-determined functions


  • lasso.by.site.top100(Con,Con_ind,i,Min_L,s) : Lasso leave one (site) out cross-validaiton model training, Min_L for the lowest the lamda cv goes 10^(-2,-3,-4); s: lamda.sequence: 1) 1se 2) min 3) log median

  • Run_lasso_by_site.top100(n,Con,Con_ind,N_train,Min_L,s) : Run lasso.by.site.top100 with n runs throughout all 28 sites = 28n

  • evaluation_funciton.bysite.PAH : generate performance metrics

# N: N repeat leave one (site) out validation;  Min_L for the lowest the lamda cv goes 10^(-2,-3,-4); s: lamda.sequence: 1) 1se 2) min 3) log median 
lasso.by.site.top100 <- function(Con,Con_ind,i,Min_L,s) {
  BySiteResult.withlamdaP <- {} ## refresh
  variables.all <- list()
  predict.site.test.all <- list()
  match1 <- str_sub(colnames(counts.tswallow.norm),3,4) %in% contaminants.coldata.subset2$MergeSite
  counts.con  <-  counts.tswallow.norm[,match1]
  counts.con.batch <- as.factor(as.character(dds.bothSex.adjusted$batch2)[match1])
  counts.con.sex <- as.factor(as.character(dds.bothSex.adjusted$sex)[match1])
  counts.con.contamin <- log10(contaminants.coldata.subset2$value.geoMean[match(str_sub(colnames(counts.con),3,4), contaminants.coldata.subset2$MergeSite)])
  ## Building matrix 
  if (length(levels(counts.con.batch)) > 1) {
    counts.con.t <- as.data.frame(t(counts.con[topgene.list,]))
    counts.con.t <- cbind(counts.con.t, batch = counts.con.batch, sex = counts.con.sex, contaminant = counts.con.contamin)
    m <- model.matrix(contaminant ~ batch +., counts.con.t)
    m <- m[,-1] } else {
      counts.con.t <- as.data.frame(t(counts.con[topgene.list,]))
      counts.con.t <- cbind(counts.con.t, sex = counts.con.sex, contaminant = counts.con.contamin)
      m <- model.matrix(contaminant ~ ., counts.con.t)
      m <- m[,-1] }  
  
  site.all <- str_sub(colnames(counts.con),3,4)
  # print(length(unique(site.all)))
  site <- unique(str_sub(colnames(counts.con),3,4))
  site.test <- site[i]
  match1.test <- site.all == site.test
  training = sample(which(!match1.test), size =  round(sum(!match1.test)*0.9))
  l.1se.all = {}
  l.min.all = {}
  l.median.all = {}
  for (j in 1:20) {
  l.1se <- {} ## repeat 10 times to avoid outlier in cross validation 
  l.min <- {}
  cvfit <- cv.glmnet(x=m[training,], y=counts.con.t$contaminant[training], nfolds = 10, alpha = 1, lambda = 10^seq(0,Min_L,length=600))
  l.1se <- cvfit$lambda.1se
  l.min <- cvfit$lambda.min
  l.median <- exp(mean(c(log(l.1se),log(l.min))))
  l.1se.all <- c(l.1se.all,l.1se)
  l.min.all <- c(l.min.all,l.min)
  l.median.all <- c(l.median.all,l.median)
  }
  l.1se = median(l.1se.all)
  l.min = median(l.min.all)
  l.median = median(l.median.all)
  lamda.sequence <- c(l.1se,l.min,l.median)  
  names(lamda.sequence) <- c("1se","min","median")
  fit <- glmnet(x=m[training,], y=counts.con.t$contaminant[training], alpha = 1, lambda = 10^seq(0,Min_L,length=600))
  error.result <- assess.glmnet(fit, newx = m[match1.test,], newy = counts.con.t$contaminant[match1.test], s = lamda.sequence[s])
  error.term <- c(error.result$mse, error.result$mae)
  names(error.term) <- c("mse","mae")
  variables <- coef(fit, s = lamda.sequence[s], gamma = 1)  
  variables <- variables[,1][!(variables[,1] == 0)]
  variables <- variables[-1]
  pseudo_R2 <- fit$dev.ratio[which(sort(c(lamda.sequence[s],10^seq(0,Min_L,length=600)),decreasing = TRUE) == lamda.sequence[s])[1] -1]
  predict.site <- predict(fit, newx = m[match1.test,], s = lamda.sequence[s])
  result = list(lamda.sequence,error.term,variables,pseudo_R2,predict.site)
  names(result) <- c("lamda.sequence","mse_mae","variables","pseudo_R2","predict.site")
  return(result)
}
Run_lasso_by_site.top100 <- function(n,Con,Con_ind,N_train,Min_L,s) {
  TIME1 <- Sys.time()
  lassositeResult.run <- foreach(i = rep(1:28,each= n) , .packages = c("stringr", "dplyr","glmnet"), .export = c("lasso.by.site.top100", "contaminants.coldata.subset2","topgene.list","counts.tswallow.norm","dds.bothSex.adjusted", "data.contaminants.majorSubset.geo.bysite")) %dopar% {
    lasso_result <- Vectorize(lasso.by.site.top100)(Con,Con_ind,i,Min_L,s)
    return(lasso_result)
  }
  TIME2 <- Sys.time()
  print(TIME2 - TIME1)
  return(lassositeResult.run)
  
}
evaluation_funciton.bysite.PAH <- function(Result) {
  result.all <- {}
  evaluation.result <- {}
  predictions = Result
  ## geoMean: only use overlapped genomic site individulas training samples to calculate geoMean
  predictions2 = data.frame(site = names(Result), predictions, geoMean = log10(contaminants.coldata.subset2$value.geoMean[match(names(Result),contaminants.coldata.subset2$MergeSite)]), b33 =  log10(quantile(contaminants.coldata.subset2$value.geoMean, probs = 0.33, na.rm = FALSE, names = FALSE)), t33 = log10(quantile(contaminants.coldata.subset2$value.geoMean, probs = 0.67, na.rm = FALSE, names = FALSE)))
  predictions3 = cbind(predictions2, is.top33 = predictions2$geoMean >= predictions2$t33, is.bottom33 = predictions2$geoMean <= predictions2$b33, is.top.predict = predictions2$predictions >= predictions2$t33, is.bottom.predict = predictions2$predictions <= predictions2$b33)
  predictions.all = predictions3
  predictions.all$delta = (predictions.all$predictions - predictions.all$geoMean)^2
  MSE = sum(predictions.all$delta)/nrow(predictions.all)
  
  error.top =  sum(predictions.all$is.top.predict & predictions.all$is.bottom33)/ sum(predictions.all$is.top.predict)
  
  error.bottom = sum(predictions.all$is.bottom.predict & predictions.all$is.top33)/ sum(predictions.all$is.bottom.predict) # 0 / 0 
  
  eorror.both = sum((predictions.all$is.top.predict & predictions.all$is.bottom33) | (predictions.all$is.bottom.predict & predictions.all$is.top33))/ sum(predictions.all$is.top.predict | predictions.all$is.bottom.predict)
  
  accuracy.top =  sum(predictions.all$is.top.predict & predictions.all$is.top33)/ sum(predictions.all$is.top.predict) 
  
  accuracy.bottom = sum(predictions.all$is.bottom.predict & predictions.all$is.bottom33)/ sum(predictions.all$is.bottom.predict) 
  
  accuracy.both = sum((predictions.all$is.top.predict & predictions.all$is.top33)  | (predictions.all$is.bottom.predict & predictions.all$is.bottom33))/ sum(predictions.all$is.bottom.predict | predictions.all$is.top.predict) 
  
  testing_ratio = c(top_ratio = sum(predictions.all$is.top33)/nrow(predictions.all), bottom_ratio = sum(predictions.all$is.bottom33)/nrow(predictions.all))
  
  performance.result = c(error.top = error.top,error.bottom = error.bottom, error.both = eorror.both, accuracy.top = accuracy.top ,accuracy.bottom = accuracy.bottom,accuracy.both = accuracy.both, testing_ratio, MSE = MSE)
  evaluation.result = list(predictions.by.site=predictions.all, performance.result = performance.result)
  return(evaluation.result)
}


Builid lasso regression model to predict pool stomach PAH concentrations by site

Run Run_lasso_by_site.top100 with 40 re-sampling, 1120 leave one out cross validation; lamda 1se then evaluate the model performance using evaluation_funciton.bysite.PAH

Con="PAHs_data";Con_ind="Total PAHs"
### edit subset for overlappign genomic samples with contaminant data 
contaminants.coldata.subset2 <-data.contaminants.majorSubset.geo.bysite
### Individual chemicals 
contaminants.coldata.subset2 <- contaminants.coldata.subset2 %>% filter(Key == Con_ind)
## combine all duplicated items using mean value becasue they have the same MergeSite ## only RiverRaisin will be affected 
contaminants.coldata.subset2 <- contaminants.coldata.subset2 %>% group_by(MergeSite, Key) %>% summarise(value.geoMean2 = mean(value.geoMean)) %>% ungroup()
colnames(contaminants.coldata.subset2)[colnames(contaminants.coldata.subset2) == "value.geoMean2"] <- "value.geoMean" ## change the name back
# print(sum(duplicated(contaminants.coldata.subset2$MergeSite))) ## check duplication again
cl <- parallel::makeCluster(12)
doParallel::registerDoParallel(cl)
PAHs_CV_bySite_result = Run_lasso_by_site.top100(n=40,Con=Con,Con_ind = Con_ind, Min_L = -2, s = 1)
parallel::stopCluster(cl)
# saveRDS(PAHs_CV_bySite_result, "PAHs_CV_bySite_result.rds")
PAHs_CV_bySite_result <- readRDS("PAHs_CV_bySite_result.rds")
Result = sapply(1:1120, function(x) mean(PAHs_CV_bySite_result[[x]][5][[1]]))
names(Result) = sapply(1:1120, function(x) str_sub(row.names(PAHs_CV_bySite_result[[x]][5][[1]])[1],3,4))
PAHs.performance.bysite.report = evaluation_funciton.bysite.PAH(Result = Result) 
# print(PAHs.performance.bysite.report$performance.result)
 # error.top    error.bottom      error.both    accuracy.top accuracy.bottom   accuracy.both       top_ratio    bottom_ratio             MSE 
 #     0.07827476      0.00000000      0.07790143      0.74281150      1.00000000      0.74403816      0.42857143      0.17857143      0.13560998 

# Edit variables
variables.length.summary <-  summary(sapply(1:length(PAHs_CV_bySite_result), function(x) length(PAHs_CV_bySite_result[[x]][3][[1]])))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#   37.00   54.00   57.00   56.59   60.00   70.00 
variables.length.p95 <- quantile(sapply(1:length(PAHs_CV_bySite_result), function(x) length(PAHs_CV_bySite_result[[x]][3][[1]])),0.95)
variables <- names(sort(table(unlist(sapply(1:length(PAHs_CV_bySite_result), function(x) names(PAHs_CV_bySite_result[[x]][3][[1]])))),decreasing = TRUE)[1:variables.length.p95])
variables <- list(variables)
variables$name <- ID.convert$GeneName[match(variables[[1]],ID.convert$GeneID2)] 
variables.coef = unlist(lapply(1:length(PAHs_CV_bySite_result), function(x) na.omit(PAHs_CV_bySite_result[[x]][3][[1]][variables[[1]]])))
# geomean_func = function(x) {exp(mean(log(x)))}
variables.coef.aggregate = aggregate(variables.coef, list(Gene = names(variables.coef)),mean)
colnames(variables.coef.aggregate)[2] = "coef"
variables.coef.aggregate = data.frame(name = ID.convert$GeneName[match(variables.coef.aggregate$Gene, ID.convert$GeneID2)], variables.coef.aggregate)
# remove duplicates TFCP2L1 and CSMD1 
variables.coef.aggregate <- variables.coef.aggregate[c(-8,-10),] 

# dev.ratio
dev.ratio.n <- length(PAHs_CV_bySite_result)
dev.ratio <- sapply(1:dev.ratio.n, function(x) PAHs_CV_bySite_result[[x]][[4]])
dev.ratio.mean <- mean(dev.ratio)
dev.ratio.s <- sd(dev.ratio)
margin <- qt(0.975,df=dev.ratio.n-1)*dev.ratio.s/sqrt(dev.ratio.n)
dev.ratio.PAHs = c(mean = dev.ratio.mean, margin = margin)
# print(dev.ratio.PAHs)
# mean      margin 
# 0.734270005 0.001355791 

# Construct panther GO analysis 
library(xml2)
PAHs_bp.data.raw <- read_xml("PAHs_2ndLasso_funcionts.xml") ## from  PANTHER17.0 http://www.pantherdb.org/
id = xml_text(xml_find_all(PAHs_bp.data.raw, "//result/term/id"))
term = xml_text(xml_find_all(PAHs_bp.data.raw, "//result/term/label"))[-319] # remove no class item
number_in_list = as.numeric(xml_text(xml_find_all(PAHs_bp.data.raw, "//result/input_list/number_in_list")))[-319]
number_in_reference = as.numeric(xml_text(xml_find_all(PAHs_bp.data.raw, "//result/number_in_reference")))[-319]
pValue = as.numeric(xml_text(xml_find_all(PAHs_bp.data.raw, "//pValue")))[-319]
fold_enrichment = as.numeric(xml_text(xml_find_all(PAHs_bp.data.raw, "//fold_enrichment")))[-319]
mapped_id = sapply(1:319, function(x) xml_text(xml_children(xml_find_all(PAHs_bp.data.raw, "//result/input_list/mapped_id_list")[x])))[-319]
names(mapped_id) <- id
PAHs_bp.panther = tibble(id = id, term = term, number_in_list = number_in_list, number_in_reference = number_in_reference, pValue = pValue, fold_enrichment = fold_enrichment, mapped_id = mapped_id)
PAHs_bp.panther.mapped_id = PAHs_bp.panther$mapped_id
## check lasso selected PAHs related genes funcions and plot as heatmap 
PAHs_bp.panther.trim = PAHs_bp.panther %>% filter(number_in_reference < 5000 & number_in_reference > 200)  # select more general go terms 
PAHs_bp.panther.mapped_id$all = unique(na.omit(variables.coef.aggregate$name))
PAHs_bp.panther.data = data.frame(GOBP_ID = rep(names(PAHs_bp.panther.mapped_id), sapply(1:319, function(x) length(PAHs_bp.panther.mapped_id[[x]])) ), name = unlist(PAHs_bp.panther.mapped_id, use.names = FALSE))
PAHs_bp.panther.data = dplyr::left_join(PAHs_bp.panther.data, variables.coef.aggregate)
PAHs_bp.panther2 = PAHs_bp.panther
colnames(PAHs_bp.panther2)[1] = "GOBP_ID"
PAHs_bp.panther.data = dplyr::left_join(PAHs_bp.panther.data, PAHs_bp.panther2[,1:6])

## Using Revigo to remove redundnat GO terms : PLoS ONE 2011. doi:10.1371/journal.pone.0021800 
# The Gene Ontology database (go.obo) which is dated Thursday, November 3, 2022.
# The UniProt-to-GO mapping database from the EBI GOA project (goa_uniprot_gcrp.gaf.gz) which is dated Friday, September 16, 2022.
Revigo.selected.gobp <- c(
  "all",
  "GO:0009987",
  "GO:0007010",
  "GO:0007154",
  "GO:0023052",
  "GO:0030029",
  "GO:0033554",
  "GO:0051716",
  "GO:0042592",
  "GO:0048523",
  "GO:0048878",
  "GO:0050896",
  "GO:0072359"
  )
PAHs_bp.panther.data$term[PAHs_bp.panther.data$GOBP_ID == "all"] = "all"
data <- as_tibble(PAHs_bp.panther.data[,c(1,2,4,5)])
data <- data %>% filter(GOBP_ID %in% Revigo.selected.gobp)
data <- data %>% spread(key = "name" , value = "coef")
data.matrix = data[,3:59]

data.matrix = as.matrix(data.matrix)
row.names(data.matrix) = data$term
data.matrix[is.na(data.matrix)] = 0

PAHs_lasso_by_site_50_functional_data = list(panther_result = PAHs_bp.panther, gene_mapped_panther_result = PAHs_bp.panther.data, heatmap_plot_data = data.matrix, top_go_terms = Revigo.selected.gobp)
# saveRDS(PAHs_lasso_by_site_50_functional_data,  "PAHs_lasso_by_site_50_functional_data.rds")
PAHs.performance.report <- list(PAHs.performance.bysite.report = PAHs.performance.bysite.report, PAHs.variables = list(variables,variables.coef.aggregate, PAHs_bp.panther.data), PAHs.dev.ratio = dev.ratio.PAHs)
# saveRDS(PAHs.performance.report, "PAHs.performance.reportaggregate.rds")

performance.metrics <- round(data.frame(PAHs.performance.report$PAHs.performance.bysite.report$performance.result),3)
colnames(performance.metrics) <- c("Pooled PAHs concentrations by site")
# saveRDS(performance.metrics, "PAHs.performance.metrics.rds")

PAHs lasso prediction results

Predicted vs actural yy plot by site

Predicted vs actural yy plot by site with CI

model performance

PAHs lasso regression model performance metrics
Pooled PAHs concentrations by site
error.top 0.078
error.bottom 0.000
error.both 0.078
accuracy.top 0.743
accuracy.bottom 1.000
accuracy.both 0.744
top_ratio 0.429
bottom_ratio 0.179
MSE 0.136

Predictor genes selected in the model

PAHs top predictor genes and coefficient
name coef Gene
EPHX2 -0.0237888 TacBic00000001
CTGFL 0.0116673 TacBic00000185
PHACTR2 0.0229792 TacBic00000280
ahrr 0.0187001 TacBic00001060
MASTL -0.0720205 TacBic00001352
IGF2BP3 0.0840547 TacBic00001551
HEG1 -0.0364054 TacBic00001970
TFCP2L1 -0.0422021 TacBic00002010
CSMD1 0.0525004 TacBic00002636
PDCD11 -0.0643387 TacBic00002956
PLCE1 0.1743130 TacBic00003080
BMF 0.0329964 TacBic00004110
PLEK2 0.0184932 TacBic00004117
AVPR1A -0.0714536 TacBic00004508
FAM19A5 -0.1237966 TacBic00004633
SEPT14 0.0202087 TacBic00005539
SLC12A1 0.1045253 TacBic00006063
ROBO2 0.0174586 TacBic00006592
PRUNE2 0.0225887 TacBic00006667
ROGDI 0.0294154 TacBic00006760
NME5 0.0259847 TacBic00007699
STC2 0.0249300 TacBic00007847
MAP3K15 0.0296867 TacBic00008341
SLC7A7 0.0173609 TacBic00008970
FABP4 0.0055431 TacBic00008996
GIF -0.0183763 TacBic00009241
CCDC174 -0.0532543 TacBic00009931
P4HTM 0.0340958 TacBic00009961
TENM1 0.0271381 TacBic00011055
COL4A2 0.0234530 TacBic00011068
FYTTD1L 0.0339534 TacBic00011106
GPR143 0.0838167 TacBic00011733
HEPH 0.0414136 TacBic00012310
VSIG4 0.0190829 TacBic00012312
IL1RAPL1 0.0196637 TacBic00012352
MCM5 0.0513213 TacBic00012529
ARMC10 0.0867078 TacBic00012663
DUSP28 0.0496005 TacBic00013079
DCLK2 0.0212307 TacBic00013136
RPL29 0.0239895 TacBic00013847
EVPL 0.0161164 TacBic00014829
LRRN4 0.0279689 TacBic00015286
gadd45 0.0131892 TacBic00015586
MPP7 0.0197704 TacBic00016314
TIMELESS -0.0238267 TacBic00018586
MRPL34 -0.0133720 TacBic00018618
SRF -0.0227717 TacBic00018836
MT-ND5 0.1604075 TacBic00019663
CYP1A4 0.0752220 TacBic00019894
CITED2 0.0209557 TacBic00019942
LBR 0.0313310 TacBic00020007
NR1D1 -0.0967599 TacBic00020399
LOC107054425 -0.0069665 TacBic00020762
NELFE -0.0157811 TacBic00021126
### PAHs top ge ne predictors functions and pathways {.tabset}

PAHs lasso selected genes heatmap

PAH correlation between logFC, logCPM, and model coef

PAHs top gene predictors function heatmap

SessionInfo

sessionInfo() 
## R version 4.2.2 Patched (2022-11-10 r83330)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 18.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
##  [1] stats4    grid      parallel  stats     graphics  grDevices utils    
##  [8] datasets  methods   base     
## 
## other attached packages:
##  [1] gghighlight_0.4.0           dendsort_0.3.4             
##  [3] circlize_0.4.15             xml2_1.3.3                 
##  [5] DESeq2_1.36.0               SummarizedExperiment_1.26.1
##  [7] Biobase_2.56.0              MatrixGenerics_1.8.1       
##  [9] matrixStats_0.62.0          GenomicRanges_1.48.0       
## [11] GenomeInfoDb_1.32.4         IRanges_2.30.1             
## [13] S4Vectors_0.34.0            BiocGenerics_0.42.0        
## [15] GGally_2.1.2                ComplexHeatmap_2.12.1      
## [17] ggrepel_0.9.2               ggpubr_0.4.0               
## [19] reshape2_1.4.4              doParallel_1.0.17          
## [21] iterators_1.0.14            foreach_1.5.2              
## [23] forcats_0.5.2               stringr_1.5.0              
## [25] dplyr_1.0.10                purrr_0.3.5                
## [27] tidyr_1.2.1                 tibble_3.1.8               
## [29] ggplot2_3.4.0               tidyverse_1.3.2            
## [31] readr_2.1.3                 edgeR_3.38.4               
## [33] limma_3.52.4                glmnet_4.1-4               
## [35] Matrix_1.5-3               
## 
## loaded via a namespace (and not attached):
##   [1] readxl_1.4.1           backports_1.4.1        plyr_1.8.8            
##   [4] splines_4.2.2          BiocParallel_1.30.4    digest_0.6.31         
##   [7] htmltools_0.5.4        fansi_1.0.4            magrittr_2.0.3        
##  [10] memoise_2.0.1          googlesheets4_1.0.1    cluster_2.1.4         
##  [13] tzdb_0.3.0             Biostrings_2.64.1      annotate_1.74.0       
##  [16] modelr_0.1.10          vroom_1.6.0            timechange_0.1.1      
##  [19] colorspace_2.1-0       blob_1.2.3             rvest_1.0.3           
##  [22] haven_2.5.1            xfun_0.37              crayon_1.5.2          
##  [25] RCurl_1.98-1.9         jsonlite_1.8.4         genefilter_1.78.0     
##  [28] survival_3.4-0         glue_1.6.2             gtable_0.3.1          
##  [31] gargle_1.2.1           zlibbioc_1.42.0        XVector_0.36.0        
##  [34] GetoptLong_1.0.5       DelayedArray_0.22.0    car_3.1-1             
##  [37] shape_1.4.6            abind_1.4-5            scales_1.2.1          
##  [40] DBI_1.1.3              rstatix_0.7.1          Rcpp_1.0.9            
##  [43] xtable_1.8-4           clue_0.3-62            bit_4.0.4             
##  [46] httr_1.4.4             RColorBrewer_1.1-3     ellipsis_0.3.2        
##  [49] pkgconfig_2.0.3        reshape_0.8.9          XML_3.99-0.12         
##  [52] farver_2.1.1           sass_0.4.5             dbplyr_2.2.1          
##  [55] locfit_1.5-9.6         utf8_1.2.3             tidyselect_1.2.0      
##  [58] labeling_0.4.2         rlang_1.0.6            AnnotationDbi_1.58.0  
##  [61] munsell_0.5.0          cellranger_1.1.0       tools_4.2.2           
##  [64] cachem_1.0.6           cli_3.6.0              generics_0.1.3        
##  [67] RSQLite_2.2.18         broom_1.0.1            evaluate_0.20         
##  [70] fastmap_1.1.0          yaml_2.3.7             knitr_1.42            
##  [73] bit64_4.0.5            fs_1.6.0               KEGGREST_1.36.3       
##  [76] nlme_3.1-160           compiler_4.2.2         rstudioapi_0.14       
##  [79] png_0.1-7              ggsignif_0.6.4         reprex_2.0.2          
##  [82] geneplotter_1.74.0     bslib_0.4.2            stringi_1.7.12        
##  [85] highr_0.10             lattice_0.20-45        vctrs_0.5.2           
##  [88] pillar_1.8.1           lifecycle_1.0.3        jquerylib_0.1.4       
##  [91] GlobalOptions_0.1.2    bitops_1.0-7           R6_2.5.1              
##  [94] codetools_0.2-18       assertthat_0.2.1       rjson_0.2.21          
##  [97] withr_2.5.0            GenomeInfoDbData_1.2.8 mgcv_1.8-41           
## [100] hms_1.1.2              rmarkdown_2.20         carData_3.0-5         
## [103] googledrive_2.0.0      lubridate_1.9.0
LS0tCnRpdGxlOiAnSW50ZWdyYXRlZCBhbmFseXNpcyBhbmQgbW9kZWxsaW5nIG9mIGNvbnRhbWluYW50IG1peHR1cmVzIGFuZCB0cmFuc2NyaXB0b21pYyByZXNwb25zZXMgaW4gVHJlZSBTd2FsbG93IChUYWNoeWNpbmV0YSBiaWNvbG9yKSBuZXN0bGluZ3MgaW4gdGhlIEdyZWF0IExha2VzJwpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdGhlbWU6IGNlcnVsZWFuCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKQ2hpIFllbiBUc2VuZ14xXiwgQ2hyaXN0aW5lIE0uIEN1c3Rlcl4yXiwgVGhvbWFzIFcuIEN1c3Rlcl4yXiwgUGF1bCBNLiBEdW1tZXJeMl4sIE5hdGFsaWUgS2Fyb3VuYeKAkFJlbmllcl4zXiBhbmQgQ29sZSBXLiBNYXRzb25eMV4KCjEuIERlcGFydG1lbnQgb2YgRW52aXJvbm1lbnRhbCBTY2llbmNlLCBUaGUgSW5zdGl0dXRlIG9mIEVjb2xvZ2ljYWwsIEVhcnRoLCBhbmQgRW52aXJvbm1lbnRhbCBTY2llbmNlcyAoVElFM1MpLCBhbmQgdGhlIENlbnRlciBmb3IgUmVzZXJ2b2lyIGFuZCBBcXVhdGljIFN5c3RlbXMgUmVzZWFyY2ggKENSQVNSKSwgQmF5bG9yIFVuaXZlcnNpdHksIFdhY28sIFRleGFzIDc2Nzk4LCBVbml0ZWQgU3RhdGVzIAoyLiBVcHBlciBNaWR3ZXN0IEVudmlyb25tZW50YWwgU2NpZW5jZXMgQ2VudGVyLCBVLlMuIEdlb2xvZ2ljYWwgU3VydmV5LCBMYSBDcm9zc2UsIFdpc2NvbnNpbiA1NDYwMywgVW5pdGVkIFN0YXRlcyAKMy4gVS5TLiBHZW9sb2dpY2FsIFN1cnZleSwgRWFzdGVybiBFY29sb2dpY2FsIFNjaWVuY2UgQ2VudGVyIChFRVNDKSBhdCBQYXR1eGVudCwgQmVsdHN2aWxsZSwgTWFyeWxhbmQgMjA3MDUsIFVuaXRlZCBTdGF0ZXMKCjwvYnI+CgohW0ltYWdlIG9mIEdMUklfc2l0ZV0oR0xSSV9zaXRlX21hcC5wbmcpCioqQWxsIHRoZSBzaXRlcyBpbmNsdWRlZCBpbiB0aGlzIHN0dWR5KiogIAoKRGV0YWlsIGxvY2F0aW9uIGNhbiBiZSBkb3dubG9hZGVkIGhlcmUgYXQgW0dMUklfc2l0ZV9kZXRhaWxzXShUYWJsZTFfc2l0ZU5hbWVfbG9jYXRpb25zLmNzdikKCmBgYHtjc3MsIGVjaG89RkFMU0V9CnByZSB7CiAgbWF4LWhlaWdodDogMzAwcHg7CiAgb3ZlcmZsb3cteTogYXV0bzsKfQoKcHJlW2NsYXNzXSB7CiAgbWF4LWhlaWdodDogMTAwcHg7Cn0KYGBgCjwvYnI+CgojIyBMb2FkIGFsbCBwYWNrYWdlcwpgYGB7ciBMb2FkIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGdsbW5ldCkKbGlicmFyeShlZGdlUikKbGlicmFyeShyZWFkcikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZm9yZWFjaCkKbGlicmFyeShkb1BhcmFsbGVsKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShHR2FsbHkpCgpgYGAKPC9icj4KCiMjIFByZXBhcmUgZ2xvYmFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGFuZCBjb250YW1pbmFudCBkYXRhICAKPC9icj4KCiBgR0xSSV9jb2xkYXRhYCAgCjogY2hlbWlzdHJ5IGRhdGEgKCJEaW94aW4iICJNdWx0aVJlZHNpZHVlUGVzdCIgIlBBSHMiICJQQkRFcyIgIkxSUENCcyIgIkhSUENCcyIgIlBlc3RpY2lkZXMiICJQRkNzIiAiUFBDUHMiKSAgJm5ic3A7ICAKLSBgZGF0YS5jb250YW1pbmFudHMubWFqb3JTdWJzZXQuZ2VvLmJ5c2l0ZWAKOiBnZW9tZXRyaWMgbWVhbiBvZiBlYWNoIGNvbnRhbWluYW50IGtleSBieSBzaXRlICAKCiBgY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0YAo6IG9ubHkgaW5jbHVkZXMgdGhlIG5lc3RsaW5ncyB3aXRoIGdlbm9taWMgaW5mb3JtYXRpb24gYW5kIHJlbW92ZXMgUEFIcyBkYXRhICAKCiBgSUQuY29udmVydGAKOiBnZW5lIElEIGNvbnZlcnRpb24gdGFibGUgYmV0d2VlbiB0cmVlIHN3YWxsb3cgZ2VuZSBwc2V1ZG8gbmFtZSBhbmQgY2hpY2tlbiBnZW5lIG5hbWUgIAoKIGBzaXRlTUFQLmFsbGAKOiBzaXRlIElEIGFuZCBwcm9wZXJzaXRlIG5hbWUgY29udmVydGlvbiBUYWJsZSAgCgogYGRkcy5ib3RoU2V4LmFkanVzdGVkIG9yIGNvdW50cy50c3dhbGxvd2AKOiBub3JtYWxpemVkIGNvdW50IG1hdHJpeCBmb3IgYWxsIG5lc3RsaW5ncywgYWRqdXN0ZWQgZm9yIGJhdGNoIGRpZmZlcmVuY2UgIAoKIGBDb250Lm1ham9yLmxpc3RgCjogbWFqb3IgY29udGFtaW5hbnRzICgiTm9uYWNobG9yLCBjaXMtX0MiLCAiSGVwdGFjaGxvciBFcG94aWRlX0MiLCAgICJOb25hY2hsb3IsIHRyYW5zLV9DIiwgICJQRkRBX0MiLCAiQ2hsb3JkYW5lLCBveHktX0MiLCAiVG90YWwgUGFyZW50IFBBSHMiLCAiVG90YWwgUEJERSIsICJUb3RhbCBQQUhzIiwiVG90YWwgUENCcyIsIlRvdGFsX1BGQ3MiKQoKIGBjb3VudHMudHN3YWxsb3cubm9ybWAKOiB2c3QgdHJhbnNmb3JtZWQgbm9ybWFsaXplZCBjb3VudHMgICAKYGBge3IgTG9hZCBkYXRhLCBpbmNsdWRlPUZBTFNFfQpzZXR3ZCgiL2hvbWUvY2hpeWVuL0RvY3VtZW50cy93b3JrL1Rzd2FsbG93X2NoZW1fR0xSSV91cGRhdGUvR0xSSV9NUzJfYWxsIikKIyBsb2FkIGRhdGEgCkdMUklfY29sZGF0YSA8LSByZWFkUkRTKCJHTFJJX2NvbGRhdGFfdG8yMDIwLnJkcyIpCiMgRGV0ZXJtaW5lIG1pbiBjb250YW1pbiB2YWx1ZSBmb3IgZWFjaCBLZXkKR0xSSV9jb2xkYXRhIDwtIEdMUklfY29sZGF0YSRjb250YW1pbmFudHMgJT4lIGZpbHRlcighaXMubmEoVmFsdWUpKQojIExvYWQgR2VvTWVhbgpkYXRhLmNvbnRhbWluYW50cy5tYWpvclN1YnNldC5nZW8uYnlzaXRlIDwtIHJlYWRSRFMoImRhdGFfY29udGFtaW5hbnRzX21ham9yU3Vic2V0X2dlb19ieXNpdGUucmRzIikKY29udGFtaW5hbnRzLm1pbi5hZGp1c3RlZCA8LSBHTFJJX2NvbGRhdGEgJT4lIGdyb3VwX2J5KEtleSxjbGFzcykgJT4lIHN1bW1hcmlzZShtaW5WYWx1ZSA9IG1pbihhcy5udW1lcmljKHZhbHVlLmFkanVzdCksbmEucm0gPSBUUlVFKSkgJT4lIHVuZ3JvdXAoKQpjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQgPC0gcmVhZF9jc3YoIkdMUklfVFJFU19jb250YW1pbmFudHN0bzIwMjBfVHJhbnNjcmlwdG9tZV9zdWJzZXRfQ1QuY3N2IikKY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0IDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldCAlPiUgZmlsdGVyKCFpcy5uYShWYWx1ZSkpICMjIHJlbW92ZSB0aG9zZSB3aXRoIG9ubHkgTkEKY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0IDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldCAlPiUgbGVmdF9qb2luKGNvbnRhbWluYW50cy5taW4uYWRqdXN0ZWQsIGJ5ID0gYygiS2V5IiwiY2xhc3MiKSkKY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0IDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldCAlPiUgZmlsdGVyKCFtaW5WYWx1ZSA9PSAiSW5mIikgIyMgcmVtb3ZlIHRob3NlIHdpdGggb25seSAiLiIKY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0JHZhbHVlLmFkanVzdFtjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQkVmFsdWUgPT0gIi4iXSA8LSBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQkbWluVmFsdWVbY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0JFZhbHVlID09ICIuIl0vMyAjICIuIiBjb252ZXJ0cyB0byAxLzMgb2YgbG93ZWFzdCBkZXRldGFibGUgdmFsdWUKaXMuQU9DLnRhYmxlIDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldCAlPiUgZHBseXI6OnNlbGVjdChBT0MsIHByb3Blcl9zaXRlMikgJT4lIGRpc3RpbmN0KCkKCklELmNvbnZlcnQgPC0gcmVhZFJEUygiSURtYXBfZmluYWwucmRzIikgIyMgZ2VuZSBJRCBDb252ZXJ0IHRhYmxlCnNpdGVNQVAuYWxsIDwtIGFzX3RpYmJsZShyZWFkUkRTKCJzaXRlTUFQX2FsbC5yZHMiKSkgIyMgc2l0ZSBJRCBjb252ZXJ0IFRhYmxlCnNpdGVNQVAuYWxsJFNpdGVJRFtzaXRlTUFQLmFsbCRwcm9wZXJzaXRlID09ICJTdGFyTGFrZSJdIDwtICJTTCIKZGRzLmJvdGhTZXguYWRqdXN0ZWQgPC0gcmVhZFJEUygiZGRzLmJvdGhTZXguYWRqdXN0ZWQub3V0bGllclJNLnJkcyIpICMjIG5vcm1hbGl6ZWQgY291bnQgbWF0cml4CmRkcy5ib3RoU2V4LmFkanVzdGVkIDwtIGRkcy5ib3RoU2V4LmFkanVzdGVkWyxjb2xuYW1lcyhkZHMuYm90aFNleC5hZGp1c3RlZCkgIT0gIjE5SFc1NzZCIl0gIyMgcmVtb3ZlICIxOUhXNTc2QiIgb25seSB1c2UgQSBjaGljayBmb3IgdGhlIGFuYWx5c2lzIGZvciByZWdyZXNzaW9uIGFuYWx5c2lzCmNvbG5hbWVzKGRkcy5ib3RoU2V4LmFkanVzdGVkKSA8LSBnc3ViKCJBJHxCJCIsICIiLCBjb2xuYW1lcyhkZHMuYm90aFNleC5hZGp1c3RlZCkpICMjIHJlbW92ZSBhbGwgdGhlIEEgb3IgQiB0YWlsIApkZHMuYm90aFNleC5hZGp1c3RlZCRwcm9wZXJzaXRlMiA8LSBhcy5jaGFyYWN0ZXIoc2l0ZU1BUC5hbGwkcHJvcGVyc2l0ZVttYXRjaChkZHMuYm90aFNleC5hZGp1c3RlZCRzaXRlLCBzaXRlTUFQLmFsbCRTaXRlSUQpXSkgCmNvdW50cy50c3dhbGxvdyA8LSBhc3NheShkZHMuYm90aFNleC5hZGp1c3RlZCkKQ29udC5tYWpvci5saXN0IDwtIGMoIk5vbmFjaGxvciwgY2lzLV9DIiwgIkhlcHRhY2hsb3IgRXBveGlkZV9DIiwgICAiTm9uYWNobG9yLCB0cmFucy1fQyIsICAiUEZEQV9DIiwgIkNobG9yZGFuZSwgb3h5LV9DIiwgIlRvdGFsIFBhcmVudCBQQUhzIiwgIlRvdGFsIFBCREUiLCAiVG90YWwgUEFIcyIsIlRvdGFsIFBDQnMiLCJUb3RhbF9QRkNzIikKIyMgY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MjogVGhlcmUgYXJlIHNvbWUgUENCcyBhcmUgTFJQQ0JzLCBzb21lIGFyZSBIUlBDQnMuIENvbWJpbmluZyBhbGwgb2YgICJUT1RBTCBQQ0JzX0MiLCBhbmQgIlRPVEFMIFBDQnMiIGludG8gIlRPVEFMIFBDQnMiIGFuZCBpZiB0aGVyZSBhcmUgb3ZlcmxhcHBpbmcsIHBpY2sgTFJQQ0JzIGZpcnN0IApjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQgPC0gY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0ICU+JSBmaWx0ZXIoY2xhc3MgIT0gIlBBSHMiKQpjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQgPC0gY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0ICU+JSBtdXRhdGUoTWVyZ2VTaXRlPXJlcGxhY2UoTWVyZ2VTaXRlLCBNZXJnZVNpdGUgPT0gInJlZiIsICJTTCIpKSAjIyByZXBsYWNlIHJlZiB0byBTTCAKY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0JE1lcmdlSUQgPC0gZ3N1YigiLSIsIiIsIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldCRNZXJnZUlEKSAjIyByZW1vdmUgYWxsIGRhc2gKY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0JE1lcmdlSUQgPC0gZ3N1YigiQSQiLCIiLCBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQkTWVyZ2VJRCkgIyMgcmVtb3ZlIEEgdGFpbApjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQgPC0gY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0ICU+JSBtdXRhdGUoS2V5PXJlcGxhY2UoS2V5LCBLZXkgPT0gIlRPVEFMIFBDQnNfQyIgfCBLZXkgPT0gIlRPVEFMIFBDQnMiLCAiVE9UQUwgUENCcyIpKQpjb3VudHMudHN3YWxsb3cubm9ybSA8LSB2c3QoZGRzLmJvdGhTZXguYWRqdXN0ZWQpCmNvdW50cy50c3dhbGxvdy5ub3JtIDwtIGFzc2F5KGNvdW50cy50c3dhbGxvdy5ub3JtKQpgYGAKPC9icj4KCiMjIDEuIERldGVybWluZSBsYXNzbyByZWdyZXNzaW9uIG1vZGVscyB0byBwcmVkaWN0IFBDQiBjb25jZW50cmF0aW9ucyAKPC9icj4KCiMjIyBMb2FkIHRvcCBnZW5lcyBsaXN0ICAKPC9icj4KCmB0b3BnZW5lbGlzdFBDQnMucmRzYCwgaW5jbHVkaW5nIDkxIGdlbmVzIHdoaWNoIHdlcmUgYXBwZWFyZWQgbW9yZSB0aGFuIDUlIGluIHRoZSBmaXJzdCByb3VuZCBvZiBsYXNzbyByZWdyZXNzaW9uIGFuYWx5c2lzIGluIHRoZSBjcm9zcy12YWxpZGF0aW9uLiBEZXRhaWwgc2NyaXB0IG9mIGZpcnN0IHJvdW5kIGxhc3NvIG1vZGVscyB3ZXJlIHByb3ZpZGVkIFtoZXJlXShHTFJJX01TMl9GaXJzdFJvdW5kX2xhc3NvLm5iLmh0bWwpICAKPC9icj4KCiMjIyBMb2FkIHNlbGYtZGV0ZXJtaW5lZCBsYXNzbyB0cmFpbmluZyBhbmQgcGVybW9yYW5jZSBldmFsdWF0aW9uIG1vZGVscwo8L2JyPgoKLSAgYGxhc3NvLmJ5LnNpdGUuUENCcy50b3AxMDAubGFzc28oaSxDb24sQ29uX2luZCwgdGVzdC5nZW5lbGlzdCwgcywgbGFtLm0pYAogIDogbGVhdmUgb25lICggYGlgIHNpdGUpIG91dCB0byBidWlsZCBsYXNzbyByZWdyZXNzaW9uIG1vZGVscyB0byBwcmVkaWN0IGNvbmNlbnRyYXRpb25zIG9mIGNvbnRhbWluYW50IChgQ29uYCBhbmQgYENvbl9pbmRgKSB1c2luZyB0b3AgcHJlZGljdG9yIGdlbmVzIChgZ2VuZWxpc3RgKSwgbW9kZWwgcGFyYW1ldGVyIGxhbWRhIChgc2A6IDFzZSBhbmQgYGxhbS5tYDogbWluaW11bSBsb2cxMCBsYW1kYSB2YWx1ZSB0byB0ZXN0IG1vZGVsIGZpdCkgIAogIAotICBgUnVuX2xhc3NvLmJ5LnNpdGUuUENCcy50b3AxMDAubGFzc28ocmUsIENvbiwgQ29uX2luZCwgcywgbGFtLm0pYAogIDogUnVuIGBsYXNzby5ieS5zaXRlLlBDQnMudG9wMTAwLmxhc3NvYCBmdW5jdGlvbiBgcmVgIHRpbWVzICAKICAKLSAgYGV2YWx1YXRpb25fZnVuY2l0b24uYnlzaXRlYAogIDogY2FsY3VsYXRlIHNpdGUgcGVyZm9ybWFuY2UgYnkgc2l0ZSwgbWVhbiBzcXVhcmUgZXJyb3IgKE1TRSksIGFjY3VyYWN5LCBhbmQgZXJyb3IgcmF0ZSBhcmUgY2FsY3VsYXRlZCBiYXNlZCBvbiBwcmVkaWN0ZWQgc2l0ZSBnZW9tZXRyaWMgbWVhbiB2ZXJzdXMgYWN0dWFsIGdlb21ldHJpYyBtZWFuICAKICAKLSAgYGV2YWx1YXRpb25fZnVuY2l0b24uYnlpbmRpdmlkdWFsYAogIDogY2FsY3VsYXRlIHNpdGUgcGVyZm9ybWFuY2UgYnkgaW5kaXZpZHVhbCBuZXN0LCBpZ25vcmluZyBzaXRlIGZhY3RvciwgbWVhbiBzcXVhcmUgZXJyb3IgKE1TRSksIGFjY3VyYWN5LCBhbmQgZXJyb3IgcmF0ZSBhcmUgY2FsY3VsYXRlZCBiYXNlZCBvbiBwcmVkaWN0ZWQgdnMgYWN0dWFsIHZhbHVlCiAgCmBgYHtyIExvYWQgUENCcyBsYXNzbyB0cmFpbmluZyBmdW5jdGlvbnMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIENvbiwgQ29uX2luZDogY29udGFtaW5hbnQgdHlwZTsgdGVzdC5nZW5lbGlzdDogdG9wIGdlbmVzIHNlbGVjdGVkIGZyb20gdGhlIGZpcnN0IHJ1bjsgczogbGFtZGEuc2VxdWVuY2UgKGwuMXNlLGwubWluLGwubWVkaWFuKTsgbGFtLm06IGxhbWJkYSA9IDEwXnNlcSgwLGxhbS5tLGxlbmd0aD02MDApCmxhc3NvLmJ5LnNpdGUuUENCcy50b3AxMDAubGFzc28gPC0gZnVuY3Rpb24oaSxDb24sQ29uX2luZCwgdGVzdC5nZW5lbGlzdCwgcywgbGFtLm0pIHsKICAjIyBnZW5vbWljIHNhbXBsZXMgd2l0aCB0aGUgc2FtZSBuZXN0IGlkCiAgbmVzdGlkIDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIgJT4lIGZpbHRlcihLZXkgPT0gQ29uX2luZCkgJT4lIGRwbHlyOjpwdWxsKE1lcmdlSUQpCiAgbWF0Y2gxIDwtIGNvbG5hbWVzKGNvdW50cy50c3dhbGxvdy5ub3JtKSAlaW4lIG5lc3RpZAogIGNvdW50cy5jb24gIDwtICBjb3VudHMudHN3YWxsb3cubm9ybVssbWF0Y2gxXQogIGNvdW50cy5jb24uYmF0Y2ggPC0gYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihkZHMuYm90aFNleC5hZGp1c3RlZCRiYXRjaDIpW21hdGNoMV0pCiAgY291bnRzLmNvbi5zZXggPC0gYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihkZHMuYm90aFNleC5hZGp1c3RlZCRzZXgpW21hdGNoMV0pCiAgY29udGFtaW4ucmF3IDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIgJT4lIGZpbHRlcihLZXkgPT0gQ29uX2luZCkgJT4lIGRwbHlyOjpwdWxsKHZhbHVlLmFkanVzdCkgJT4lIGxvZzEwKCkKICBjb250YW1pbi5tYXRyaXggPC0gY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiAlPiUgZHBseXI6OnNsaWNlKG1hdGNoKGNvbG5hbWVzKGNvdW50cy5jb24pLCBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyJE1lcmdlSUQpKSAlPiUgZmlsdGVyKEtleSA9PSBDb25faW5kKQogIGNvdW50cy5jb24uY29udGFtaW4gPC0gbG9nMTAoY29udGFtaW4ubWF0cml4JHZhbHVlLmFkanVzdCkgIyMgZ2V0IGxvZzEwIHZhbHVlIHRvIHN1cHByZXNzIHBvdGVudGlhbCBvdXRsaWVyCiAgc2l0ZS5saXN0ID0gdW5pcXVlKGFzLmNoYXJhY3RlcihkZHMuYm90aFNleC5hZGp1c3RlZCRzaXRlW21hdGNoMV0pKQogICMjIEJ1aWxkaW5nIG1hdHJpeAogIGlmIChsZW5ndGgobGV2ZWxzKGNvdW50cy5jb24uYmF0Y2gpKSA+IDEpIHsKICAgIGNvdW50cy5jb24udCA8LSBhcy5kYXRhLmZyYW1lKHQoY291bnRzLmNvblt0ZXN0LmdlbmVsaXN0LF0pKQogICAgY291bnRzLmNvbi50IDwtIGNiaW5kKGNvdW50cy5jb24udCwgYmF0Y2ggPSBjb3VudHMuY29uLmJhdGNoLCBzZXggPSBjb3VudHMuY29uLnNleCwgY29udGFtaW5hbnQgPSBjb3VudHMuY29uLmNvbnRhbWluKQogICAgbSA8LSBtb2RlbC5tYXRyaXgoY29udGFtaW5hbnQgfiBiYXRjaCArLiwgY291bnRzLmNvbi50KSAjIG0gZm9yIHRyYWluaW5nCiAgICBtIDwtIG1bLC0xXSB9IGVsc2UgewogICAgICBjb3VudHMuY29uLnQgPC0gYXMuZGF0YS5mcmFtZSh0KGNvdW50cy5jb25bdGVzdC5nZW5lbGlzdCxdKSkKICAgICAgY291bnRzLmNvbi50IDwtIGNiaW5kKGNvdW50cy5jb24udCwgc2V4ID0gY291bnRzLmNvbi5zZXgsIGNvbnRhbWluYW50ID0gY291bnRzLmNvbi5jb250YW1pbikKICAgICAgbSA8LSBtb2RlbC5tYXRyaXgoY29udGFtaW5hbnQgfiAuLCBjb3VudHMuY29uLnQpCiAgICAgIG0gPC0gbVssLTFdIH0KICAjIyB0byBpbmNsdWRlIGFsbCBnZW5vbWljIHNhbXBsZXMgYXMgbTIKICBuZXN0aWQgPC0gY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiAlPiUgZmlsdGVyKEtleSA9PSBDb25faW5kKSAlPiUgZHBseXI6OnB1bGwoTWVyZ2VJRCkKICBjb3VudHMuY29uMiAgPC0gIGNvdW50cy50c3dhbGxvdy5ub3JtCiAgY291bnRzLmNvbi5iYXRjaDIgPC0gYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihkZHMuYm90aFNleC5hZGp1c3RlZCRiYXRjaDIpKQogIGNvdW50cy5jb24uc2V4MiA8LSBhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKGRkcy5ib3RoU2V4LmFkanVzdGVkJHNleCkpCgogICMjIEJ1aWxkaW5nIG1hdHJpeAogIGlmIChsZW5ndGgobGV2ZWxzKGNvdW50cy5jb24uYmF0Y2gyKSkgPiAxKSB7CiAgICBjb3VudHMuY29uLnQyIDwtIGFzLmRhdGEuZnJhbWUodChjb3VudHMuY29uMlt0ZXN0LmdlbmVsaXN0LF0pKQogICAgY291bnRzLmNvbi50MiA8LSBjYmluZChjb3VudHMuY29uLnQyLCBiYXRjaCA9IGNvdW50cy5jb24uYmF0Y2gyLCBzZXggPSBjb3VudHMuY29uLnNleDIpCiAgICBtMiA8LSBtb2RlbC5tYXRyaXgofiBiYXRjaCArLiwgY291bnRzLmNvbi50MikgIyBtMiBmb3IgdmFsaWRhdGlvbiBpbiBsZWF2ZSBvbmUgc2l0ZSBvdXQKICAgIG0yIDwtIG0yWywtMV0gfSBlbHNlIHsKICAgICAgY291bnRzLmNvbi50MiA8LSBhcy5kYXRhLmZyYW1lKHQoY291bnRzLmNvbjJbdGVzdC5nZW5lbGlzdCxdKSkKICAgICAgY291bnRzLmNvbi50MiA8LSBjYmluZChjb3VudHMuY29uLnQyLCBzZXggPSBjb3VudHMuY29uLnNleDIpCiAgICAgIG0yIDwtIG1vZGVsLm1hdHJpeCggfiAuLCBjb3VudHMuY29uLnQyKQogICAgICBtMiA8LSBtMlssLTFdIH0KCiAgc2l0ZS5jb3VudC5uZXN0LnN1YnNldCA9IHRhYmxlKHN0cl9zdWIoY29sbmFtZXMoY291bnRzLmNvbiksMyw0KSkKICBzaXRlLmNvdW50Lmdlbm9taWMuc3Vic2V0ID0gdGFibGUoc3RyX3N1Yihjb2xuYW1lcyhjb3VudHMuY29uMiksMyw0KSkKICAjIHNpdGVzX3ZlcmlmaWNhdGlvbnMgPSBjKCJFUiIsICJIVyIsICJMUyIsICJQQiIsICJTQyIsICJTUCIsICJTVCIsICJUSCIsICJXSyIpCgogICMjIENWIGxlYXZlIG9uZSBvdXQgYnkgc2l0ZQogICAgdGVzdC5zaXRlID0gc3RyX3N1Yihyb3cubmFtZXMobSksMyw0KSAlaW4lIHNpdGUubGlzdFtpXQogICAgbC4xc2UgPC0ge30gOyBsLm1pbiA8LSB7fQoKICAgIGZvciAoaiBpbiAxOjIwKSB7CiAgICAgIGN2Zml0IDwtIGN2LmdsbW5ldCh4PW1bIXRlc3Quc2l0ZSxdLCB5PWNvdW50cy5jb24udCRjb250YW1pbmFudFshdGVzdC5zaXRlXSwgbmZvbGQgPSAxMCwgYWxwaGEgPSAxLCBsYW1iZGEgPSAxMF5zZXEoMCxsYW0ubSxsZW5ndGg9NjAwKSwgcmVsYXggPUZBTFNFKQogICAgICAKICAgICAgbC4xc2UgPC0gYyhsLjFzZSwgY3ZmaXQkbGFtYmRhLjFzZSkKICAgICAgbC5taW4gPC0gYyhsLm1pbiwgY3ZmaXQkbGFtYmRhLm1pbikKICAgICAgCiAgICB9ICMgcmVwZWF0IDIwIHRpbWVzIGFuZCBwaWNrIG1lZGlhbiBsYW1kYSB0byBhdm9pZCBwaWNraW5nIGV4dHJlbWUgaW4gMTAgZm9sZHMgY3Jvc3MgdmFsaWRhdGlvbgogICAgbC4xc2UgPC0gbWVkaWFuKGwuMXNlKQogICAgbC5taW4gPC0gbWVkaWFuKGwubWluKQogICAgbC5tZWRpYW4gPC0gZXhwKG1lYW4oYyhsb2cobC5taW4pLGxvZyhsLjFzZSkpKSkKICAgIGxhbWRhLnNlcXVlbmNlIDwtIGMobC4xc2UsbC5taW4sbC5tZWRpYW4pCgogICAgQnlOZXN0UmVzdWx0LndpdGhsYW1kYVAgPC0ge30KICAgIHRyYWluaW5nLnNpdGUgPSBzYW1wbGUod2hpY2goIXRlc3Quc2l0ZSksIHJvdW5kKHN1bSghdGVzdC5zaXRlKSowLjkpKQogICAgZml0Lmxhc3NvIDwtIGdsbW5ldCh4PW1bdHJhaW5pbmcuc2l0ZSxdLCB5PWNvdW50cy5jb24udCRjb250YW1pbmFudFt0cmFpbmluZy5zaXRlXSwgYWxwaGEgPSAxLCBsYW1iZGEgPSAxMF5zZXEoMCxsYW0ubSxsZW5ndGg9NjAwKSkgIyBsYXNzbyBtb2RlbCB1c2luZyB0cmFpbmluZyBzZXQKICAgIAogICAgIyMgZ2V0IGFsbCB0aGUgbWV0cmljcyBjKGZlYXR1cmVzLCBkZXYucmF0aW8sIGxhbWRhKQogICAgdmFyaWFibGVzID0gY29lZihmaXQubGFzc28sIHMgPSBsYW1kYS5zZXF1ZW5jZVtzXSlbLDFdCiAgICB2YXJpYWJsZXMgPSB2YXJpYWJsZXNbdmFyaWFibGVzICE9IDBdWy0xXQogICAgZGV2LnJhdGlvID0gZml0Lmxhc3NvJGRldi5yYXRpb1t3aGljaChzb3J0KGMobGFtZGEuc2VxdWVuY2Vbc10sMTBec2VxKDAsbGFtLm0sbGVuZ3RoPTYwMCkpLGRlY3JlYXNpbmcgPSBUUlVFKSA9PSBsYW1kYS5zZXF1ZW5jZVtzXSlbMV0gLTFdCiAgICAKICAgICMjIGdldCBhbGwgcHJlZGljdGlvbnMgdXNpbmcgYWxsIGF2YWlhbGJsZSBnZW5vbWljIHNhbXBsZXMKICAgIHRlc3Quc2l0ZTIgPSBzdHJfc3ViKHJvdy5uYW1lcyhtMiksMyw0KSA9PSBzaXRlLmxpc3RbaV0KICAgIAogICAgIyMgbGFzc28KICAgIHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5zdWJzZXQgPC0gcHJlZGljdChmaXQubGFzc28sIG5ld3ggPSBtW3Rlc3Quc2l0ZSxdLCBzID0gbGFtZGEuc2VxdWVuY2Vbc10pICMjIG9ubHkgdXNlIG92ZXJsYXBwZWQgcG9ydGlvbgogICAgcHJlZGljdC5hbGwuZ2Vub21pYy5zYW1wbGVzIDwtIHByZWRpY3QoZml0Lmxhc3NvLCBuZXd4ID0gbTJbdGVzdC5zaXRlMixdLCBzID0gbGFtZGEuc2VxdWVuY2Vbc10pICMjIG9ubHkgdXNlIG92ZXJsYXBwZWQgcG9ydGlvbgoKICAgIHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5zdWJzZXQubGFzc28gPSBwcmVkaWN0LmFsbC5nZW5vbWljLnNhbXBsZXMuc3Vic2V0WywxXQogICAgcHJlZGljdC5hbGwuZ2Vub21pYy5zYW1wbGVzLmxhc3NvID0gcHJlZGljdC5hbGwuZ2Vub21pYy5zYW1wbGVzWywxXQoKICAgIHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5zdWJzZXQubGFzc28ubWVhbiA9IG1lYW4ocHJlZGljdC5hbGwuZ2Vub21pYy5zYW1wbGVzLnN1YnNldC5sYXNzbykKICAgIG5hbWVzKHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5zdWJzZXQubGFzc28ubWVhbikgPSBzaXRlLmxpc3RbaV0KICAgIHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5sYXNzby5tZWFuID0gbWVhbihwcmVkaWN0LmFsbC5nZW5vbWljLnNhbXBsZXMubGFzc28pCiAgICBuYW1lcyhwcmVkaWN0LmFsbC5nZW5vbWljLnNhbXBsZXMubGFzc28ubWVhbikgPSBzaXRlLmxpc3RbaV0KCiAgICAjIyBjb21iaW5lCiAgICByZXN1bHQgPSBsaXN0KHZhcmlhYmxlcyA9dmFyaWFibGVzLCBkZXYucmF0aW8gPSBkZXYucmF0aW8gLGxhbWRhLnNlcXVlbmNlID0gbGFtZGEuc2VxdWVuY2UsIHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5zdWJzZXQubGFzc28gPSBwcmVkaWN0LmFsbC5nZW5vbWljLnNhbXBsZXMuc3Vic2V0Lmxhc3NvLCBwcmVkaWN0LmFsbC5nZW5vbWljLnNhbXBsZXMuc3Vic2V0Lmxhc3NvLm1lYW4gPSBwcmVkaWN0LmFsbC5nZW5vbWljLnNhbXBsZXMuc3Vic2V0Lmxhc3NvLm1lYW4sIHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5sYXNzbyA9IHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5sYXNzbywgcHJlZGljdC5hbGwuZ2Vub21pYy5zYW1wbGVzLmxhc3NvLm1lYW4gPSBwcmVkaWN0LmFsbC5nZW5vbWljLnNhbXBsZXMubGFzc28ubWVhbikKICByZXR1cm4ocmVzdWx0KQp9ClJ1bl9sYXNzby5ieS5zaXRlLlBDQnMudG9wMTAwLmxhc3NvIDwtIGZ1bmN0aW9uKHJlLCBDb24sIENvbl9pbmQsIHMsIGxhbS5tKSB7CiAgbGFzc29DVmJ5c2l0ZSA8LSBmb3JlYWNoKGkgPSByZXAoMTozMSwgZWFjaCA9IHJlKSwgLnBhY2thZ2VzID0gYygiZHBseXIiLCJnbG1uZXQiLCJzdHJpbmdyIiksIC5jb21iaW5lID0gJ2NiaW5kJywgLmV4cG9ydCA9IGMoImxhc3NvLmJ5LnNpdGUuUENCcy50b3AxMDAubGFzc28iLCAiY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiIsImNvdW50cy50c3dhbGxvdy5ub3JtIiwiZGRzLmJvdGhTZXguYWRqdXN0ZWQiLCAidGVzdC5nZW5lbGlzdCIsICJkYXRhLmNvbnRhbWluYW50cy5tYWpvclN1YnNldC5nZW8uYnlzaXRlIikpICVkb3BhciUgewogICAgTGFzc29DVmJ5c2l0ZV9yZXN1bHQgPC0gVmVjdG9yaXplKGxhc3NvLmJ5LnNpdGUuUENCcy50b3AxMDAubGFzc28oaSxDb24sQ29uX2luZCwgdGVzdC5nZW5lbGlzdCwgcywgbGFtLm0pKQogICAgcmV0dXJuKExhc3NvQ1ZieXNpdGVfcmVzdWx0KQogIH0KCiAgcmV0dXJuKGxhc3NvQ1ZieXNpdGUpCn0KZXZhbHVhdGlvbl9mdW5jaXRvbi5ieXNpdGUgPC0gZnVuY3Rpb24oUmVzdWx0KSB7CiAgcmVzdWx0LmFsbCA8LSB7fQogIGV2YWx1YXRpb24ucmVzdWx0IDwtIHt9CiAgcHJlZGljdGlvbnMgPSBSZXN1bHQKICAjIyBnZW9NZWFuOiBvbmx5IHVzZSBvdmVybGFwcGVkIGdlbm9taWMgc2l0ZSBpbmRpdmlkdWxhcyB0cmFpbmluZyBzYW1wbGVzIHRvIGNhbGN1bGF0ZSBnZW9NZWFuCiAgcHJlZGljdGlvbnMyID0gZGF0YS5mcmFtZShzaXRlID0gbmFtZXMoUmVzdWx0KSwgcHJlZGljdGlvbnMsIGdlb01lYW4gPSBjb250YW1pbmFudC5nZW9NZWFuLmJ5c2l0ZS5nZW5vbWljW25hbWVzKFJlc3VsdCldLCBiMzMgPSAgcXVhbnRpbGUoY29udGFtaW5hbnQuZ2VvTWVhbi5ieXNpdGUsIHByb2JzID0gMC4zMywgbmEucm0gPSBGQUxTRSwgbmFtZXMgPSBGQUxTRSksIHQzMyA9IHF1YW50aWxlKGNvbnRhbWluYW50Lmdlb01lYW4uYnlzaXRlLCBwcm9icyA9IDAuNjcsIG5hLnJtID0gRkFMU0UsIG5hbWVzID0gRkFMU0UpKQogIHByZWRpY3Rpb25zMyA9IGNiaW5kKHByZWRpY3Rpb25zMiwgaXMudG9wMzMgPSBwcmVkaWN0aW9uczIkZ2VvTWVhbiA+PSBwcmVkaWN0aW9uczIkdDMzLCBpcy5ib3R0b20zMyA9IHByZWRpY3Rpb25zMiRnZW9NZWFuIDw9IHByZWRpY3Rpb25zMiRiMzMsIGlzLnRvcC5wcmVkaWN0ID0gcHJlZGljdGlvbnMyJHByZWRpY3Rpb25zID49IHByZWRpY3Rpb25zMiR0MzMsIGlzLmJvdHRvbS5wcmVkaWN0ID0gcHJlZGljdGlvbnMyJHByZWRpY3Rpb25zIDw9IHByZWRpY3Rpb25zMiRiMzMpCiAgcHJlZGljdGlvbnMuYWxsID0gcHJlZGljdGlvbnMzCiAgcHJlZGljdGlvbnMuYWxsJGRlbHRhID0gKHByZWRpY3Rpb25zLmFsbCRwcmVkaWN0aW9ucyAtIHByZWRpY3Rpb25zLmFsbCRnZW9NZWFuKV4yCiAgTVNFID0gc3VtKHByZWRpY3Rpb25zLmFsbCRkZWx0YSkvbnJvdyhwcmVkaWN0aW9ucy5hbGwpCiAgCiAgZXJyb3IudG9wID0gIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tMzMpLyBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLnRvcC5wcmVkaWN0KQogIAogIGVycm9yLmJvdHRvbSA9IHN1bShwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMudG9wMzMpLyBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0KSAKICAKICBlcnJvci5ib3RoID0gc3VtKChwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tMzMpICB8IChwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMudG9wMzMpKS8gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20ucHJlZGljdCB8IHByZWRpY3Rpb25zLmFsbCRpcy50b3AucHJlZGljdCkgCiAgCiAgYWNjdXJhY3kudG9wID0gIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMudG9wMzMpLyBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLnRvcC5wcmVkaWN0KSAKICAKICBhY2N1cmFjeS5ib3R0b20gPSBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbTMzKS8gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20ucHJlZGljdCkgCiAgCiAgYWNjdXJhY3kuYm90aCA9IHN1bSgocHJlZGljdGlvbnMuYWxsJGlzLnRvcC5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLnRvcDMzKSAgfCAocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbTMzKSkvIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QgfCBwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QpIAogIAogIHRlc3RpbmdfcmF0aW8gPSBjKHRvcF9yYXRpbyA9IHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wMzMpL25yb3cocHJlZGljdGlvbnMuYWxsKSwgYm90dG9tX3JhdGlvID0gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20zMykvbnJvdyhwcmVkaWN0aW9ucy5hbGwpKQogIAogIHBlcmZvcm1hbmNlLnJlc3VsdCA9IGMoZXJyb3IudG9wID0gZXJyb3IudG9wLGVycm9yLmJvdHRvbSA9IGVycm9yLmJvdHRvbSwgZXJyb3IuYm90aCA9IGVycm9yLmJvdGgsIGFjY3VyYWN5LnRvcCA9IGFjY3VyYWN5LnRvcCAsYWNjdXJhY3kuYm90dG9tID0gYWNjdXJhY3kuYm90dG9tLGFjY3VyYWN5LmJvdGggPSBhY2N1cmFjeS5ib3RoLCB0ZXN0aW5nX3JhdGlvLCBNU0UgPSBNU0UpCiAgZXZhbHVhdGlvbi5yZXN1bHQgPSBsaXN0KHByZWRpY3Rpb25zLmJ5LnNpdGU9cHJlZGljdGlvbnMuYWxsLCBwZXJmb3JtYW5jZS5yZXN1bHQgPSBwZXJmb3JtYW5jZS5yZXN1bHQpCiAgcmV0dXJuKGV2YWx1YXRpb24ucmVzdWx0KQp9CmV2YWx1YXRpb25fZnVuY2l0b24uYnlpbmRpdmlkdWFsIDwtIGZ1bmN0aW9uKFJlc3VsdCkgewogIHJlc3VsdC5hbGwgPC0ge30KICBldmFsdWF0aW9uLnJlc3VsdCA8LSB7fQogIHByZWRpY3Rpb25zID0gUmVzdWx0CiAgIyMgZ2VvTWVhbjogb25seSB1c2Ugb3ZlcmxhcHBlZCBnZW5vbWljIHNpdGUgaW5kaXZpZHVsYXMgdHJhaW5pbmcgc2FtcGxlcyB0byBjYWxjdWxhdGUgZ2VvTWVhbgogIHByZWRpY3Rpb25zMiA9IGRhdGEuZnJhbWUobWVyZ2VJRCA9IG5hbWVzKFJlc3VsdCksIHByZWRpY3Rpb25zLCBhY3R1cmFsID0gY29udGFtaW4uYXJyYXlbbmFtZXMocHJlZGljdGlvbnMpXSAsIGIzMyA9ICBxdWFudGlsZShjb250YW1pbmFudC5nZW9NZWFuLmJ5c2l0ZSwgcHJvYnMgPSAwLjMzLCBuYS5ybSA9IEZBTFNFLCBuYW1lcyA9IEZBTFNFKSwgdDMzID0gcXVhbnRpbGUoY29udGFtaW5hbnQuZ2VvTWVhbi5ieXNpdGUsIHByb2JzID0gMC42NywgbmEucm0gPSBGQUxTRSwgbmFtZXMgPSBGQUxTRSkpCiAgcHJlZGljdGlvbnMzID0gY2JpbmQocHJlZGljdGlvbnMyLCBpcy50b3AzMyA9IHByZWRpY3Rpb25zMiRhY3R1cmFsID49IHByZWRpY3Rpb25zMiR0MzMsIGlzLmJvdHRvbTMzID0gcHJlZGljdGlvbnMyJGFjdHVyYWwgPD0gcHJlZGljdGlvbnMyJGIzMywgaXMudG9wLnByZWRpY3QgPSBwcmVkaWN0aW9uczIkcHJlZGljdGlvbnMgPj0gcHJlZGljdGlvbnMyJHQzMywgaXMuYm90dG9tLnByZWRpY3QgPSBwcmVkaWN0aW9uczIkcHJlZGljdGlvbnMgPD0gcHJlZGljdGlvbnMyJGIzMykKICBwcmVkaWN0aW9uczMgPSBwcmVkaWN0aW9uczNbIWlzLm5hKHByZWRpY3Rpb25zMyRpcy5ib3R0b20zMyksXQogIHByZWRpY3Rpb25zLmFsbCA9IHByZWRpY3Rpb25zMwogIHByZWRpY3Rpb25zLmFsbCRkZWx0YSA9IChwcmVkaWN0aW9ucy5hbGwkcHJlZGljdGlvbnMgLSBwcmVkaWN0aW9ucy5hbGwkYWN0dXJhbCleMgogIE1TRSA9IHN1bShwcmVkaWN0aW9ucy5hbGwkZGVsdGEpL25yb3cocHJlZGljdGlvbnMuYWxsKQogIAogIAogIGVycm9yLnRvcCA9ICBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLnRvcC5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbTMzKS8gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy50b3AucHJlZGljdCkKICAKICBlcnJvci5ib3R0b20gPSBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLnRvcDMzKS8gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20ucHJlZGljdCkgCiAgCiAgZXJyb3IuYm90aCA9IHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tMzMgfCBwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMudG9wMzMpLyBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLnRvcC5wcmVkaWN0IHwgcHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0KSAKICAKICBhY2N1cmFjeS50b3AgPSAgc3VtKHByZWRpY3Rpb25zLmFsbCRpcy50b3AucHJlZGljdCAmIHByZWRpY3Rpb25zLmFsbCRpcy50b3AzMykvIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QpIAogIAogIGFjY3VyYWN5LmJvdHRvbSA9IHN1bShwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tMzMpLyBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0KSAKICAKICBhY2N1cmFjeS5ib3RoID0gc3VtKChwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMudG9wMzMpICB8IChwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tMzMpKS8gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20ucHJlZGljdCB8IHByZWRpY3Rpb25zLmFsbCRpcy50b3AucHJlZGljdCkgCiAgCiAgdGVzdGluZ19yYXRpbyA9IGModG9wX3JhdGlvID0gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy50b3AzMykvbnJvdyhwcmVkaWN0aW9ucy5hbGwpLCBib3R0b21fcmF0aW8gPSBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbTMzKS9ucm93KHByZWRpY3Rpb25zLmFsbCkpCiAgCiAgcGVyZm9ybWFuY2UucmVzdWx0ID0gYyhlcnJvci50b3AgPSBlcnJvci50b3AsZXJyb3IuYm90dG9tID0gZXJyb3IuYm90dG9tLCBlcnJvci5ib3RoID0gZXJyb3IuYm90aCwgYWNjdXJhY3kudG9wID0gYWNjdXJhY3kudG9wICxhY2N1cmFjeS5ib3R0b20gPSBhY2N1cmFjeS5ib3R0b20sYWNjdXJhY3kuYm90aCA9IGFjY3VyYWN5LmJvdGgsIHRlc3RpbmdfcmF0aW8sIE1TRSA9IE1TRSkKICBldmFsdWF0aW9uLnJlc3VsdCA9IGxpc3QocHJlZGljdGlvbnMuYWxsLHBlcmZvcm1hbmNlLnJlc3VsdCkKICByZXR1cm4oZXZhbHVhdGlvbi5yZXN1bHQpCn0KCmBgYAojIyMgUnVuIExhc3NvIHJlZ3Jlc3Npb24gYW5hbHlzaXMgYmV0d2VlbiB0b3AgR2VuZSAoOTEgZ2VuZXMpIGFuZCBQQ0IgY29uY2VudHJhdGlvbnMgIAo8L2JyPgoKUnVuIGxhc3NvIGFuYWx5c2lzIHdpdGggbGVhdmUgb25lIHNpdGUgb3V0IGNyb3NzIHZhbGlkYXRpbm8gd2l0aCA0MCByb3VuZHMsIDQwICogMzEgc2l0ZXMgPSAxMjQwIGNyb3NzLXZhbGlkYXRpb24gYW5kIHJlc2FtcGxlIDkwJSBvZiB0cmFpbmluZyBpbmRpdmlkdWFscyBldmVyeSB0cmlhbC4gIAoKYGBge3IgTGFzc28gcmVncmVzc2lvbiBhbmFseXNpcyBiZXR3ZWVuIHRvcCBHZW5lICh+IDEwMCBnZW5lcykgYW5kIFBDQiBjb25jZW50cmF0aW9ucywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyMjIFRvdGFsIFBDQnMKQ29uPSJQQ0JzX2RhdGEiO0Nvbl9pbmQ9IlRPVEFMIFBDQnMiCiMjIHJlZnJlc2ggYmV0d2VlbiBjaGVtaWNhbHMgCmxhbWRhLnNlcXVlbmNlIDwtIHt9ClJlc3VsdC5sYXNzby5hc3Nlc3NtZW50IDwtIHt9CnZhcmlhYmxlcyA8LSB7fQpwcmVkaWN0LnNpdGUudGVzdCA8LSB7fQojIyByZXN1bHQgbWVhc3VyZXMgCnJsYXNzb1Jlc3VsdCA8LSBsaXN0KCkKCiMgcHJlcGFyZSBkYXRhIApjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyIDwtY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0CiMjIyBJbmRpdmlkdWFsIGNoZW1pY2FscyAKY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiA8LSBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyICU+JSBmaWx0ZXIoS2V5ID09IENvbl9pbmQpCmNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIgPC0gY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiAlPiUgZmlsdGVyKCFpcy5uYSh2YWx1ZS5hZGp1c3QpKSAjIyByZW1vdmUgdGhvc2Ugd2l0aCB2YWx1ZSA9PSAiTlIiCiMjIGNvbWJpbmUgYWxsIGR1cGxpY2F0ZWQgaXRlbXMgdXNpbmcgbWVhbiB2YWx1ZSBiZWNhc3VlIHRoZXkgaGF2ZSB0aGUgc2FtZSBuZXN0IGlkIApjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyIDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIgJT4lIGdyb3VwX2J5KE1lcmdlSUQsU3BlY2llcyxNYXRyaXgsQU9DLFByb3Blcl9TaXRlLEtleSx0eXBlLGNsYXNzLE1lcmdlU2l0ZSxwcm9wZXJfc2l0ZTIpICU+JSBzdW1tYXJpc2UodmFsdWUuYWRqdXN0MiA9IG1lYW4odmFsdWUuYWRqdXN0KSkgJT4lIHVuZ3JvdXAoKQpjb2xuYW1lcyhjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyKVtjb2xuYW1lcyhjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyKSA9PSAidmFsdWUuYWRqdXN0MiJdIDwtICJ2YWx1ZS5hZGp1c3QiICMjIGNoYW5nZSB0aGUgbmFtZSBiYWNrCiMgcHJpbnQoc3VtKGR1cGxpY2F0ZWQoY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiRNZXJnZUlEKSkpICMjIGNoZWNrIGR1cGxpY2F0aW9uIGFnYWluCiMjIHJlYWQgdG9wZ2VuZWxpc3QgOTEgZ2VuZXMgCnRlc3QuZ2VuZWxpc3QgPSByZWFkUkRTKCJ0b3BnZW5lbGlzdFBDQnMucmRzIikkdG90YWwuUENCcwoKY29udGFtaW5hbnQuZ2VvTWVhbi5ieXNpdGUgPC0gZGF0YS5jb250YW1pbmFudHMubWFqb3JTdWJzZXQuZ2VvLmJ5c2l0ZSAlPiUgZmlsdGVyKEtleSA9PSBDb25faW5kKSAlPiUgZHBseXI6OnB1bGwodmFsdWUuZ2VvTWVhbikgJT4lIGxvZzEwKCkgIyMgbG9nMTAgb2YgY29udGFtaW5hbnQgdmFsdWUKbmFtZXMoY29udGFtaW5hbnQuZ2VvTWVhbi5ieXNpdGUpIDwtIGRhdGEuY29udGFtaW5hbnRzLm1ham9yU3Vic2V0Lmdlby5ieXNpdGUgJT4lIGZpbHRlcihLZXkgPT0gQ29uX2luZCkgJT4lIGRwbHlyOjpwdWxsKE1lcmdlU2l0ZSkgCm5lc3RpZCA8LSBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyICU+JSBmaWx0ZXIoS2V5ID09IENvbl9pbmQpICU+JSBkcGx5cjo6cHVsbChNZXJnZUlEKQptYXRjaDEgPC0gY29sbmFtZXMoY291bnRzLnRzd2FsbG93Lm5vcm0pICVpbiUgbmVzdGlkCmNvdW50cy5jb24gIDwtICBjb3VudHMudHN3YWxsb3cubm9ybVssbWF0Y2gxXQpjb250YW1pbi5tYXRyaXggPC0gY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiAlPiUgZHBseXI6OnNsaWNlKG1hdGNoKGNvbG5hbWVzKGNvdW50cy5jb24pLCBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyJE1lcmdlSUQpKSAlPiUgZmlsdGVyKEtleSA9PSBDb25faW5kKSAlPiXjgIBtdXRhdGUodmFsdWUuYWRqdXN0LmxvZyA9IGxvZzEwKHZhbHVlLmFkanVzdCkpCmNvbnRhbWluLmFycmF5IDwtIGNvbnRhbWluLm1hdHJpeCR2YWx1ZS5hZGp1c3QubG9nCm5hbWVzKGNvbnRhbWluLmFycmF5KSA9IGNvbnRhbWluLm1hdHJpeCRNZXJnZUlECmNvbnRhbWluLm1hdHJpeC5tZWFuIDwtIGNvbnRhbWluLm1hdHJpeCAlPiUgZ3JvdXBfYnkoTWVyZ2VTaXRlKSAlPiUgc3VtbWFyaXNlKGdlb01lYW4gPSBtZWFuKHZhbHVlLmFkanVzdC5sb2cpKSAlPiUgdW5ncm91cCgpCmNvbnRhbWluLm1hdHJpeC5tZWFuJE1lcmdlU2l0ZVszMV0gPSAiTkEiCmNvbnRhbWluYW50Lmdlb01lYW4uYnlzaXRlLmdlbm9taWMgPC0gY29udGFtaW4ubWF0cml4Lm1lYW4kZ2VvTWVhbgpuYW1lcyhjb250YW1pbmFudC5nZW9NZWFuLmJ5c2l0ZS5nZW5vbWljKSA8LSBjb250YW1pbi5tYXRyaXgubWVhbiRNZXJnZVNpdGUKYGBgCgoKYGBge3IgUnVuIExhc3NvIHJlZ3Jlc3Npb24gYW5hbHlzaXMgYmV0d2VlbiB0b3AgR2VuZSAofiAxMDAgZ2VuZXMpIGFuZCBQQ0IgY29uY2VudHJhdGlvbnMsIGV2YWw9RkFMU0V9CiMgUnVuIGxhc3NvIGFuYWx5c2lzIHdpdGggbGVhdmUgb25lIHNpdGUgb3V0IGNyb3NzIHZhbGlkYXRpbm8gd2l0aCA0MCByb3VuZHMsIDQwICogMzEgc2l0ZXMgPSAxMjQwIGNyb3NzLXZhbGlkYXRpb24gYW5kIHJlc2FtcGxlIDkwJSBvZiB0cmFpbmluZyBpbmRpdmlkdWFscyBldmVyeSB0cmlhbC4gCnByaW50KGMoIjJuZCBsYXNzbyBjdiIsQ29uX2luZCkpCnQxID0gIFN5cy50aW1lKCkKY2wgPC0gcGFyYWxsZWw6Om1ha2VDbHVzdGVyKDEyKQpkb1BhcmFsbGVsOjpyZWdpc3RlckRvUGFyYWxsZWwoY2wpCnQxID0gU3lzLnRpbWUoKQpQQ0JzX0NWX2J5U2l0ZV9yZXN1bHQgPSBSdW5fbGFzc28uYnkuc2l0ZS5QQ0JzLnRvcDEwMC5sYXNzbyhyZSA9IDQwLCBDb24gPSBDb24sIENvbl9pbmQgPSBDb25faW5kLCBzID0gMSxsYW0ubSA9IC0yKQp0MiA9ICBTeXMudGltZSgpCnQyIC10MQpwYXJhbGxlbDo6c3RvcENsdXN0ZXIoY2wpCgojIFJlc3VsdCBsaXN0IDogMS4gdmFyaWFibGVzLCAyLiAgZGV2LnJhdGlvIDMuIGxhbWRhLnNlcXVlbmNlIDQuIHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5zdWJzZXQubGFzc28gNS4gcHJlZGljdC5hbGwuZ2Vub21pYy5zYW1wbGVzLnN1YnNldC5sYXNzby5tZWFuIDYuIHByZWRpY3QuYWxsLmdlbm9taWMuc2FtcGxlcy5sYXNzbyAKIyA3LiBwcmVkaWN0LmFsbC5nZW5vbWljLnNhbXBsZXMubGFzc28ubWVhbiAKIyBzYXZlUkRTKFBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdCwgIlBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdDJuZC5yZHMiKQpgYGAKCiMjIyBMYXNzbyByZWdyZXNzaW9uIGFuYWx5c2lzIHJlc3VsdHMgZm9yIHByZWRpY3RpbmcgUENCIGNvbmNlbnRyYXRpb25zIApgYGB7ciBldmFsdWF0ZSBwZXJmb3JtYW5jZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KUENCc19DVl9ieVNpdGVfcmVzdWx0IDwtIHJlYWRSRFMoIlBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdDJuZC5yZHMiKQojIFBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdCBieSBzaXRlClJlc3VsdCA9IHVubGlzdCh1bm5hbWUoUENCc19DVl9ieVNpdGVfcmVzdWx0WzUsXSkpICMjICBnZW5vbWljIHNhbXBsZXMgb3ZlcmxhcHBlZCB3aXRoIGNvbnRhbWluYXRlZCBzYW1wbGVzIApQQ0JzLnBlcmZvcm1hbmNlLmJ5c2l0ZS5vdmVybGFwcGVkLnJlcG9ydCA9IGV2YWx1YXRpb25fZnVuY2l0b24uYnlzaXRlKFJlc3VsdCA9IFJlc3VsdCkgCgpSZXN1bHQgPSB1bmxpc3QodW5uYW1lKFBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdFs3LF0pKSAjIyBhbGwgZ2Vub21pYyBzYW1wbGVzIApQQ0JzLnBlcmZvcm1hbmNlLmJ5c2l0ZS5hbGxnZW5vbWljLnJlcG9ydCA9IGV2YWx1YXRpb25fZnVuY2l0b24uYnlzaXRlKFJlc3VsdCA9IFJlc3VsdCkgCiMgCiMgIyMgb3ZlcmxhcHBlZCBwb3J0aW9uIFBDQnMKIyAkcGVyZm9ybWFuY2UucmVzdWx0CiMgIGVycm9yLnRvcCAgICBlcnJvci5ib3R0b20gICAgICBlcnJvci5ib3RoICAgIGFjY3VyYWN5LnRvcCBhY2N1cmFjeS5ib3R0b20gICBhY2N1cmFjeS5ib3RoICAgICAgIHRvcF9yYXRpbyAgICBib3R0b21fcmF0aW8gICAgICAgICAgICAgTVNFIAojICAgICAwLjEzMjA3NTQ3MiAgICAgMC4wMDYwNzI4NzQgICAgIDAuMDUwMDY1ODc2ICAgICAwLjU2OTgxMTMyMSAgICAgMC44NDQxMjk1NTUgICAgIDAuNzQ4MzUzMDk2ICAgICAwLjMyMjU4MDY0NSAgICAgMC40NTE2MTI5MDMgICAgIDAuMjMwMTQwOTQ3IAoKIyAjICMjIGFsbCBnZW5vbWljIHNhbXBsZXMgUENCcwojICRwZXJmb3JtYW5jZS5yZXN1bHQKIyBlcnJvci50b3AgICAgZXJyb3IuYm90dG9tICAgICAgZXJyb3IuYm90aCAgICBhY2N1cmFjeS50b3AgYWNjdXJhY3kuYm90dG9tICAgYWNjdXJhY3kuYm90aCAgICAgICB0b3BfcmF0aW8gICAgYm90dG9tX3JhdGlvICAgICAgICAgICAgIE1TRSAKIyAgICAgMC4wOTA1NTExODEgICAgIDAuMDA2NTA3NTkyICAgICAwLjAzNjM2MzYzNiAgICAgMC42MTQxNzMyMjggICAgIDAuODU0NjYzNzc0ICAgICAwLjc2OTIzMDc2OSAgICAgMC4zMjI1ODA2NDUgICAgIDAuNDUxNjEyOTAzICAgICAwLjIzMzE4NzcwMiAKCiMgUENCc19DVl9ieVNpdGVfcmVzdWx0IGJ5IGluZGl2aWR1YWwsIG9ubHkgdXNlIG92ZXJsYXBwZWQgcG9ydGlvbiAKUmVzdWx0ID0gdW5saXN0KHVubmFtZShQQ0JzX0NWX2J5U2l0ZV9yZXN1bHRbNCxdKSkgIyMgIGdlbm9taWMgc2FtcGxlcyBvdmVybGFwcGVkIHdpdGggY29udGFtaW5hdGVkIHNhbXBsZXMgClBDQnMucGVyZm9ybWFuY2UuYnlpbmRpdmlkdWFsLm92ZXJsYXBwZWQucmVwb3J0ID0gZXZhbHVhdGlvbl9mdW5jaXRvbi5ieWluZGl2aWR1YWwoUmVzdWx0ID0gUmVzdWx0KSAKCiMgZXJyb3IudG9wICAgIGVycm9yLmJvdHRvbSAgICAgIGVycm9yLmJvdGggICAgYWNjdXJhY3kudG9wIGFjY3VyYWN5LmJvdHRvbSAgIGFjY3VyYWN5LmJvdGggICAgICAgdG9wX3JhdGlvICAgIGJvdHRvbV9yYXRpbyAgICAgICAgICAgICBNU0UgCiMgICAgICAgMC4xMDAyMDI4ICAgICAgIDAuMTgxNzU4NSAgICAgICAwLjE0NTI5MjkgICAgICAgMC43MzQ2ODU2ICAgICAgIDAuNjI4NjA4OSAgICAgICAwLjY3NjAzODUgICAgICAgMC40MjM0Njk0ICAgICAgIDAuMzcyNDQ5MCAgICAgICAwLjM5ODU5ODggCgojIyBvdGhlciBwZXJmb3JtYW5jZSBtZXRyaWNzCiMgZGV0ZXJtaW5lIHByZWRpY3RvciBnZW5lczsgdmFyaWFibGVycwp2YXJpYWJsZXMubGVuZ3RoLnN1bW1hcnkgPC0gIHN1bW1hcnkoc2FwcGx5KDE6bGVuZ3RoKFBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdFsxLF0pLCBmdW5jdGlvbih4KSBsZW5ndGgoUENCc19DVl9ieVNpdGVfcmVzdWx0WzEsXVtbeF1dKSkpCnZhcmlhYmxlcy5sZW5ndGgucDk1IDwtIHF1YW50aWxlKHNhcHBseSgxOmxlbmd0aChQQ0JzX0NWX2J5U2l0ZV9yZXN1bHRbMSxdKSwgZnVuY3Rpb24oeCkgbGVuZ3RoKFBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdFsxLF1bW3hdXSkpLDAuOTUpCnZhcmlhYmxlcyA8LSBuYW1lcyhzb3J0KHRhYmxlKG5hbWVzKHVubGlzdCh1bm5hbWUoUENCc19DVl9ieVNpdGVfcmVzdWx0WzEsXSkpKSksZGVjcmVhc2luZyA9IFRSVUUpWzE6dmFyaWFibGVzLmxlbmd0aC5wOTVdKQp2YXJpYWJsZXMgPC0gbGlzdChpZCA9IHZhcmlhYmxlcywgbmFtZSA9IG5hLm9taXQoSUQuY29udmVydCRHZW5lTmFtZVttYXRjaCh2YXJpYWJsZXMsSUQuY29udmVydCRHZW5lSUQyKV0pKQp2YXJpYWJsZXMuY29lZiA9IHVubGlzdChsYXBwbHkoMTpuY29sKFBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdCksIGZ1bmN0aW9uKHgpIG5hLm9taXQoUENCc19DVl9ieVNpdGVfcmVzdWx0WzEsXVtbeF1dW3ZhcmlhYmxlcyRpZF0pKSkKZ2VvbWVhbl9mdW5jID0gZnVuY3Rpb24oeCkge2V4cChtZWFuKGxvZyh4KSkpfQp2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUgPSBhZ2dyZWdhdGUodmFyaWFibGVzLmNvZWYsIGxpc3QoR2VuZSA9IG5hbWVzKHZhcmlhYmxlcy5jb2VmKSksbWVhbikKY29sbmFtZXModmFyaWFibGVzLmNvZWYuYWdncmVnYXRlKVsyXSA9ICJjb2VmIgp2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUgPSBkYXRhLmZyYW1lKG5hbWUgPSBJRC5jb252ZXJ0JEdlbmVOYW1lW21hdGNoKHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZSRHZW5lLCBJRC5jb252ZXJ0JEdlbmVJRDIpXSwgdmFyaWFibGVzLmNvZWYuYWdncmVnYXRlKQoKIyBSIHNxdWFyZSwgb3IgZGV2aWFuY2UgcmF0aW8KCiMgZGV2LnJhdGlvCmRldi5yYXRpby5uIDwtIG5jb2woUENCc19DVl9ieVNpdGVfcmVzdWx0KQpkZXYucmF0aW8ubWVhbiA8LSBtZWFuKHVubGlzdCh1bm5hbWUoUENCc19DVl9ieVNpdGVfcmVzdWx0WzIsXSkpKQpkZXYucmF0aW8ucyA8LSBzZCh1bmxpc3QodW5uYW1lKFBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdFsyLF0pKSkKbWFyZ2luIDwtIHF0KDAuOTc1LGRmPWRldi5yYXRpby5uLTEpKmRldi5yYXRpby5zL3NxcnQoZGV2LnJhdGlvLm4pCmRldi5yYXRpby5QQ0JzID0gYyhtZWFuID0gZGV2LnJhdGlvLm1lYW4sIG1hcmdpbiA9IG1hcmdpbikKIyBwcmludChkZXYucmF0aW8uUENCcykKIyBtZWFuICAgICAgbWFyZ2luIAojIDAuNzY2NDg5NzU5IDAuMDAxMDQ1MTM5IAoKCiMgIyMgCnByZWRpY3QucmVzdWx0ID0gdW5saXN0KHVubmFtZShQQ0JzX0NWX2J5U2l0ZV9yZXN1bHRbNCxdKSkgIyBvbmx5IHRoZSBvdmVybGFwcGluZyBwYXJ0IApwcmVkaWN0LnJlc3VsdDIgPSB1bmxpc3QodW5uYW1lKFBDQnNfQ1ZfYnlTaXRlX3Jlc3VsdFs1LF0pKSAjIG9ubHkgdGhlIG92ZXJsYXBwaW5nIHBhcnQgc2l0ZSBhdmVyYWdlIApQcmVkaWN0aW9ucy5ieS5zaXRlID0gdGliYmxlKHNpdGU9IG5hbWVzKHByZWRpY3QucmVzdWx0MiksIHByZWRpY3Rpb25zPSBwcmVkaWN0LnJlc3VsdDIsICBnZW5vbWljLlBDQi5tZWFuID0gY29udGFtaW5hbnQuZ2VvTWVhbi5ieXNpdGUuZ2Vub21pY1tuYW1lcyhwcmVkaWN0LnJlc3VsdDIpXSwgcHJvcGVyX3NpdGUyID0gc2l0ZU1BUC5hbGwkcHJvcGVyc2l0ZVttYXRjaChuYW1lcyhwcmVkaWN0LnJlc3VsdDIpLCBzaXRlTUFQLmFsbCRTaXRlSUQpXSkKUHJlZGljdGlvbnMuYnkuaW5kaXZpZHVhbCA9IHRpYmJsZShNZXJnZUlEID0gbmFtZXMocHJlZGljdC5yZXN1bHQpLCBwcm9wZXJfc2l0ZTIgPSBzaXRlTUFQLmFsbCRwcm9wZXJzaXRlW21hdGNoKHN0cl9zdWIobmFtZXMocHJlZGljdC5yZXN1bHQpLDMsNCksIHNpdGVNQVAuYWxsJFNpdGVJRCldLCBwcmVkaWN0ZWQuUENCcyA9IHByZWRpY3QucmVzdWx0LCAgbWVhc3VyZWQuUENCcyA9IGNvbnRhbWluLmFycmF5W25hbWVzKHByZWRpY3QucmVzdWx0KV0pClBDQnMucHJlZGljdGlvbnMubGFzc28uYWdncmVnYXRlID0gbGlzdChieS5zaXRlID0gUHJlZGljdGlvbnMuYnkuc2l0ZSwgYnkuaW5kaXZpZHVhbCA9IFByZWRpY3Rpb25zLmJ5LmluZGl2aWR1YWwpCiMgc2F2ZVJEUyhQQ0JzLnByZWRpY3Rpb25zLmxhc3NvLmFnZ3JlZ2F0ZSwgIlBDQnMucHJlZGljdGlvbnMubGFzc28uYWdncmVnYXRlLnJkcyIpICAKCiMgbGlzdCBhbGwgcHJlZGljdGlvbnMsIHZhcmlhYmxlcywgUjIgYW5kIG1vZGVsIHBlcmZvcm1hbmNlIApQQ0JzLnBlcmZvcm1hbmNlLnJlcG9ydCA8LSBsaXN0KFBDQnMucGVyZm9ybWFuY2UuYnlzaXRlLm92ZXJsYXBwZWQucmVwb3J0ID0gUENCcy5wZXJmb3JtYW5jZS5ieXNpdGUub3ZlcmxhcHBlZC5yZXBvcnQsIFBDQnMucGVyZm9ybWFuY2UuYnlzaXRlLmFsbGdlbm9taWMucmVwb3J0ID0gUENCcy5wZXJmb3JtYW5jZS5ieXNpdGUuYWxsZ2Vub21pYy5yZXBvcnQsIFBDQnMucGVyZm9ybWFuY2UuYnlpbmRpdmlkdWFsLm92ZXJsYXBwZWQucmVwb3J0ID0gUENCcy5wZXJmb3JtYW5jZS5ieWluZGl2aWR1YWwub3ZlcmxhcHBlZC5yZXBvcnQsIFBDQi52YXJpYWJsZXMgPSBsaXN0KHZhcmlhYmxlcyx2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUpLCBQQ0JzLmRldi5yYXRpbyA9IGRldi5yYXRpby5QQ0JzKQojIHNhdmVSRFMoUENCcy5wZXJmb3JtYW5jZS5yZXBvcnQsICJQQ0JzLnBlcmZvcm1hbmNlLnJlcG9ydC5yZHMiKQoKYGBgCjwvYnI+CkJlY2F1c2Ugbm90IGV2ZXkgbmVzdCB3YXMgc2VsZWN0ZWQgZm9yIG1lYXN1cmluZyBjaGVtaXN0cnksIHRoZSBmb2xsb3dpbmcgcGVyZm9ybWFuY2UgbWV0cmljcyBhcmUgY2FsY3VsYXRlZCBieSAgIAoKLSBQQ0JzX0NWX2J5U2l0ZV9yZXN1bHQgYnkgc2l0ZTogb25seSBpbmNsdWRlIHRob3NlIG5lc3Qgd2l0aCBjaGVtaXN0cnkgbWVhc3VyZW1lbnRzIGFuZCBjYWxjdWxhdGUgYWNjdXJhY3kgYW5kIGVycm9ycyB1c2luZyBwcmVkaWN0ZWQgc2l0ZSBnZW9tZXRyaWMgbWVhbiAgCi0gUENCcy5wZXJmb3JtYW5jZS5ieXNpdGUuYWxsZ2Vub21pYzogaW5jbHVkZSBhbGwgbmVzdCB3aXRoIG9yIHdpdGhvdXQgY2hlbWlzdHJ5IG1lYXN1cmVtZW50cyBhbmQgY2FsY3VsYXRlIGFjY3VyYWN5IGFuZCBlcnJvcnMgdXNpbmcgcHJlZGljdGVkIHNpdGUgZ2VvbWV0cmljIG1lYW4gCi0gUENCcy5wZXJmb3JtYW5jZS5ieWluZGl2aWR1YWw6IGluY2x1ZGUgYWxsIG5lc3Qgd2l0aCBjaGVtaXN0cnkgbWVhc3VyZW1lbnRzIGFuZCBjYWxjdWxhdGUgYWNjdXJhY3kgYW5kIGVycm9ycyB1c2luZyBwcmVkaWN0ZWQgaW5kaXZpZHVhbCBjb25jZW50cmF0aW9ucy4gCgpgYGB7ciBjb25zdHJ1Y3QgcGVyZm9ybWFuY2UubWV0cmljcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KUmVzdWx0ID0gdW5saXN0KHVubmFtZShQQ0JzX0NWX2J5U2l0ZV9yZXN1bHRbNSxdKSkgIyMgIGdlbm9taWMgc2FtcGxlcyBvdmVybGFwcGVkIHdpdGggY29udGFtaW5hdGVkIHNhbXBsZXMgClBDQnMucGVyZm9ybWFuY2UuYnlzaXRlLm92ZXJsYXBwZWQucmVwb3J0ID0gZXZhbHVhdGlvbl9mdW5jaXRvbi5ieXNpdGUoUmVzdWx0ID0gUmVzdWx0KSAKClJlc3VsdCA9IHVubGlzdCh1bm5hbWUoUENCc19DVl9ieVNpdGVfcmVzdWx0WzcsXSkpICMjIGFsbCBnZW5vbWljIHNhbXBsZXMgClBDQnMucGVyZm9ybWFuY2UuYnlzaXRlLmFsbGdlbm9taWMucmVwb3J0ID0gZXZhbHVhdGlvbl9mdW5jaXRvbi5ieXNpdGUoUmVzdWx0ID0gUmVzdWx0KSAKIyAKIyAjIyBvdmVybGFwcGVkIHBvcnRpb24gUENCcwojICRwZXJmb3JtYW5jZS5yZXN1bHQKIyBlcnJvci50b3AgICAgZXJyb3IuYm90dG9tICAgIGFjY3VyYWN5LnRvcCBhY2N1cmFjeS5ib3R0b20gICBhY2N1cmFjeS5ib3RoICAgICAgIHRvcF9yYXRpbyAgICBib3R0b21fcmF0aW8gICAgICAgICAgICAgTVNFCiMgMC4xMTE5NjkxMTIgICAgIDAuMDA0MDU2Nzk1ICAgICAwLjU4MzAxMTU4MyAgICAgMC44NDU4NDE3ODUgICAgIDAuNzU1MzE5MTQ5ICAgICAwLjMyMjU4MDY0NSAgICAgMC40NTE2MTI5MDMgICAgIDAuMjI5NDI2NDUzCiMgIyMgYWxsIGdlbm9taWMgc2FtcGxlcyBQQ0JzCiMgJHBlcmZvcm1hbmNlLnJlc3VsdAojIGVycm9yLnRvcCAgICBlcnJvci5ib3R0b20gICAgYWNjdXJhY3kudG9wIGFjY3VyYWN5LmJvdHRvbSAgIGFjY3VyYWN5LmJvdGggICAgICAgdG9wX3JhdGlvICAgIGJvdHRvbV9yYXRpbyAgICAgICAgICAgICBNU0UKIyAwLjA2NTMwNjEyMiAgICAgMC4wMDQzNDc4MjYgICAgIDAuNjMyNjUzMDYxICAgICAwLjg0NzgyNjA4NyAgICAgMC43NzMwNDk2NDUgICAgIDAuMzIyNTgwNjQ1ICAgICAwLjQ1MTYxMjkwMyAgICAgMC4yMzI0MTQ0MzMKCiMgUENCc19DVl9ieVNpdGVfcmVzdWx0IGJ5IGluZGl2aWR1YWwsIG9ubHkgdXNlIG92ZXJsYXBwZWQgcG9ydGlvbiAKUmVzdWx0ID0gdW5saXN0KHVubmFtZShQQ0JzX0NWX2J5U2l0ZV9yZXN1bHRbNCxdKSkgIyMgIGdlbm9taWMgc2FtcGxlcyBvdmVybGFwcGVkIHdpdGggY29udGFtaW5hdGVkIHNhbXBsZXMgClBDQnMucGVyZm9ybWFuY2UuYnlpbmRpdmlkdWFsLm92ZXJsYXBwZWQucmVwb3J0ID0gZXZhbHVhdGlvbl9mdW5jaXRvbi5ieWluZGl2aWR1YWwoUmVzdWx0ID0gUmVzdWx0KSAKCiMgZXJyb3IudG9wICAgIGVycm9yLmJvdHRvbSAgICBhY2N1cmFjeS50b3AgYWNjdXJhY3kuYm90dG9tICAgYWNjdXJhY3kuYm90aCAgICAgICB0b3BfcmF0aW8gICAgYm90dG9tX3JhdGlvICAgICAgICAgICAgIE1TRSAKIyAwLjEwMDIwMjggICAgICAgMC4xODE3NTg1ICAgICAgIDAuNzM0Njg1NiAgICAgICAwLjYyODYwODkgICAgICAgMC42NzYwMzg1ICAgICAgIDAuNDIzNDY5NCAgICAgICAwLjM3MjQ0OTAgICAgICAgMC4zOTg1OTg4IAoKcGVyZm9ybWFuY2UubWV0cmljcyA8LSByb3VuZChkYXRhLmZyYW1lKFBDQnMucGVyZm9ybWFuY2UuYnlzaXRlLm92ZXJsYXBwZWQucmVwb3J0JHBlcmZvcm1hbmNlLnJlc3VsdCwgUENCcy5wZXJmb3JtYW5jZS5ieXNpdGUuYWxsZ2Vub21pYy5yZXBvcnQkcGVyZm9ybWFuY2UucmVzdWx0LCBQQ0JzLnBlcmZvcm1hbmNlLmJ5aW5kaXZpZHVhbC5vdmVybGFwcGVkLnJlcG9ydFtbMl1dKSwzKQpjb2xuYW1lcyhwZXJmb3JtYW5jZS5tZXRyaWNzKSA8LSBjKCJieS5zaXRlICh3aXRoIGNoZW1pc3RyeSBvbmx5KSIsICJieS5zaXRlIChhbGwgZ2Vub21pYyBzYW1wbGVzKSIsICJieS5uZXN0ICh3aXRoIGNoZW1pc3RyeSBvbmx5KSIpCiMgc2F2ZVJEUyhwZXJmb3JtYW5jZS5tZXRyaWNzLCAiUENCLnBlcmZvcm1hbmNlLm1ldHJpY3MucmRzIikKCmBgYAo8L2JyPgoKIyMjIFBDQnMgbGFzc28gcHJlZGljdGlvbiByZXN1bHRzIHsudGFic2V0fQotIFByZWRpY3RlZCB2cyBhY3R1cmFsIHl5IHBsb3QgYnkgc2l0ZSAgCjogcHJlZGljdGVkIHZzIGFjdHVyYWwgUENCIGNvbmNlbnRyYXRpb247IGNvbG9yIGJ5IHRob3NlIHNpdGUgd2l0aCBwcmVkaWN0aW9uIHZhbHVlIGFib3ZlIG9yIGJlbG93IHRvcCAxLzMgb3IgYm90dG9tIDEvMyBQQ0IgYW1vbmcgYWxsIHRoZSBzaXRlcyAKCi0gUHJlZGljdGVkIHZzIGFjdHVyYWwgeXkgcGxvdCBieSBzaXRlIHdpdGggOTkuOSUgY29uZmlkZW5jZSBpbnRlcnZhbCAgCjogYWRkIDk5LjklIENJIG9uIFByZWRpY3RlZCB2cyBhY3R1cmFsIHl5IHBsb3QgYnkgc2l0ZSAgCgotIFJlbWVkaWF0aW9uIGVmZmVjdGl2ZW5lc3MgZXZhbHVhdGlvbiBpbiBXYXVrZWdhbiAgCjogYWRkIFdhdWtlZ2FuIHZhbHVlIHRocm91Z2hvdXQgdGhlIHllYXIgb24gUHJlZGljdGVkIHZzIGFjdHVyYWwgeXkgcGxvdCBieSBzaXRlICAKCi0gUHJlZGljdGVkIHZzIGFjdHVyYWwgeXkgcGxvdCBieSBuZXN0ICAKOiB0aGUgbW9kZWwgaXMgdHJhaW5lZCBieSBpbmRpdmlkdWFsIG5lc3QgIAoKLSBtb2RlbCBwZXJmb3JtYW5jZSAgCjogYWNjdXJhY3kgYW5kIGVycm9yIDsgYWNjdXJhdGUgcHJlZGljdGluZyB0b3AgMS8zIG9yIGJvdHRvbSAxLzMgOyBlcnJvciA6IHByZWRpY3RpbmcgdG9wIDEvMyBidXQgZW5kIHVwIGJvdHRvbSAxLzMgb3IgdmljZSB2ZXJzYQoKLSBQcmVkaWN0b3IgZ2VuZXMgc2VsZWN0ZWQgaW4gdGhlIG1vZGVsICAKOiBsaXN0IHRoZSBwcmVkaWN0b3IgZ2VuZXMgaW4gdGhlIG1vZGVsIAoKCiMjIyMgUHJlZGljdGVkIHZzIGFjdHVyYWwgeXkgcGxvdCBieSBzaXRlIApgYGB7ciBQcmVkaWN0ZWQgdnMgYWN0dXJhbCB5eSBwbG90IGJ5IHNpdGUsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXAgPSAiUHJlZGljdGVkIHZzIGFjdHVyYWwgUENCIGNvbmNlbnRyYXRpb25zIHl5IHBsb3QgYnkgc2l0ZSIgLCBlY2hvPUZBTFNFfQojI0ZpZ3VyZSBTMmEgUENCcyB5eSBwbG90IApwbG90X2RhdGEgPC0gcmVhZFJEUygiUENCcy5wcmVkaWN0aW9ucy5sYXNzby5hZ2dyZWdhdGUucmRzIikKcGxvdF9kYXRhMiA8LSBwbG90X2RhdGEkYnkuc2l0ZQpwbG90X2RhdGEyIDwtIHBsb3RfZGF0YTIgJT4lIGdyb3VwX2J5KHByb3Blcl9zaXRlMikgJT4lIHN1bW1hcmlzZShwcmVkaWN0aW9ucyA9IG1lYW4ocHJlZGljdGlvbnMpLCBnZW9tZXRyaWMubWVhbiA9IG1lYW4oZ2Vub21pYy5QQ0IubWVhbikpICU+JSB1bmdyb3VwKCkKcGxvdF9kYXRhMiRwZXJjZW50aWxlIDwtICJNIgpwbG90X2RhdGEyJHBlcmNlbnRpbGVbcGxvdF9kYXRhMiRwcmVkaWN0aW9ucyA8PSAyLjI2XSA8LSAiTCIKcGxvdF9kYXRhMiRwZXJjZW50aWxlW3Bsb3RfZGF0YTIkcHJlZGljdGlvbnMgPj0gMi43MTVdIDwtICJIIgoKIyBTY2F0dGVycGxvdAp5eXBsb3QuYnlzaXRlIDwtIGdncGxvdChwbG90X2RhdGEyLCBhZXMoeD1wcmVkaWN0aW9ucywgeT1nZW9tZXRyaWMubWVhbikpICsKICBnZW9tX3BvaW50KGFlcyhjb2wgPSBwZXJjZW50aWxlKSkgKwogIGxhYnMoeT0ibG9nMTAgVG90YWwgUENCcyAobmcvZyB3ZXQgd2VpZ2h0KSIsCiAgICAgICB4PSJQcmVkaWN0ZWQgbG9nMTAgVG90YWwgUENCcyAobmcvZyB3ZXQgd2VpZ2h0KSIsCiAgICAgICB0aXRsZT0iIikrCiAgc3RhdF9jb3IobWV0aG9kID0gInNwZWFybWFuIiwgbGFiZWwueCA9IDEuNzUsIGxhYmVsLnkgPSAzLjUsIGNvci5jb2VmLm5hbWUgPSAicmhvIiwgc2l6ZSA9IDIuNSkgKwogIHN0YXRfY29yKG1ldGhvZCA9ICJwZWFyc29uIiwgbGFiZWwueCA9IDEuNzUsIGxhYmVsLnkgPSAzLjQsIGNvci5jb2VmLm5hbWUgPSAiUiIgLCBzaXplID0gMi41KSArCiAgZ2VvbV90ZXh0KGFlcygxLjc1LCAzLjYsIGhqdXN0ID0gMCwgbGFiZWwgPSBwYXN0ZTAoIk1TRSIsIiA9ICIgLHJvdW5kKHBlcmZvcm1hbmNlLm1ldHJpY3MkYGJ5LnNpdGUgKHdpdGggY2hlbWlzdHJ5IG9ubHkpYFs5XSxkaWdpdHMgPSAzKSkgKSwgc2l6ZSA9IDIuNSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJQcmVkaWN0ZWQgW1BDQnNdIiwgCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImFib3ZlIDY3dGgiLCAibWlkZGxlIiwgImJlbG93IDMzcmQiKSwgCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIkgiPSJyZWQiLCAiTSIgPSAibGlnaHRncmF5IiwgIkwiPSJibHVlIikpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9Mi4yNiwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIuNzE1LCBsaW5ldHlwZT0iZG90dGVkIiwgY29sb3IgPSAicmVkIikrCiAgZ2VvbV90ZXh0KGFlcygzLjE1LCAyLjA1LCBsYWJlbCA9ICIzM3JkIHBlcmNlbnRpbGUiLCB2anVzdCA9IC0gMSksIGNvbG9yID0gImJsdWUiKSArCiAgZ2VvbV90ZXh0KGFlcygzLjE1LCAyLjc1LCBsYWJlbCA9ICI2N3RoIHBlcmNlbnRpbGUiLCB2anVzdCA9IC0gMSksIGNvbG9yID0gInJlZCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9Mi4yNiwgbGluZXR5cGU9ImRvdGRhc2giLCBjb2xvciA9ICJibHVlIikrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTIuNzE1LCBsaW5ldHlwZT0iZG90ZGFzaCIsIGNvbG9yID0gInJlZCIpKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDExKQp5eXBsb3QuYnlzaXRlCmBgYAojIyMjIFByZWRpY3RlZCB2cyBhY3R1cmFsIHl5IHBsb3QgYnkgc2l0ZSB3aXRoIDk5LjklIGNvbmZpZGVuY2UgaW50ZXJ2YWwgCgpgYGB7ciBQcmVkaWN0ZWQgdnMgYWN0dXJhbCB5eSBwbG90IGJ5IHNpdGUgd2l0aCAwLjk5OSBjb25maWRlbmNlIGludGVydmFsLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01LCBlY2hvPUZBTFNFfQoKIyNGaWd1cmUgUzJhLjIgUENCcyB5eSBwbG90IAoKcGxvdF9kYXRhIDwtIHJlYWRSRFMoIlBDQnMucHJlZGljdGlvbnMubGFzc28uYWdncmVnYXRlLnJkcyIpCnBsb3RfZGF0YTIgPC0gcGxvdF9kYXRhJGJ5LnNpdGUKcGxvdF9kYXRhMiA8LSBwbG90X2RhdGEyICU+JSBncm91cF9ieShwcm9wZXJfc2l0ZTIpICU+JSBzdW1tYXJpc2UocHJlZGljdGlvbnMgPSBtZWFuKHByZWRpY3Rpb25zKSwgZ2VvbWV0cmljLm1lYW4gPSBtZWFuKGdlbm9taWMuUENCLm1lYW4pKSAlPiUgdW5ncm91cCgpCgpmaXQuUENCIDwtIGxtKGdlb21ldHJpYy5tZWFuIH4gcHJlZGljdGlvbnMsIGRhdGEgPSBwbG90X2RhdGEyKQpkYXQgPC0gcHJlZGljdChmaXQuUENCLCBpbnRlcnZhbD0iY29uZmlkZW5jZSIsIGxldmVsID0gMC45OTkpCnBsb3RfZGF0YTIkaW5zaWRlIDwtIGlmZWxzZShwbG90X2RhdGEyJGdlb21ldHJpYy5tZWFuIDwgZGF0WywidXByIl0gJiBwbG90X2RhdGEyJGdlb21ldHJpYy5tZWFuID4gZGF0WywibHdyIl0sICJpbiIsICJvdXQiKQoKIyBwbG90X2RhdGEyJHN0ciA8LSAiQyIKIyBwbG90X2RhdGEyJHN0cltwbG90X2RhdGEyJGluc2lkZSA9PSAib3V0IiAmIHBsb3RfZGF0YTIkZ2VvbWV0cmljLm1lYW4gPCAyLjI2XSA8LSAiTCIKIyBwbG90X2RhdGEyJHN0cltwbG90X2RhdGEyJGluc2lkZSA9PSAib3V0IiAmIHBsb3RfZGF0YTIkZ2VvbWV0cmljLm1lYW4gPiAyLjcxNV0gPC0gIkgiCgojIAojIHl5cGxvdC5ieXNpdGUxXzEgPC0gZ2dwbG90KHBsb3RfZGF0YTIsIGFlcyh4PXByZWRpY3Rpb25zLCB5PWdlb21ldHJpYy5tZWFuKSkgKwojICAgZ2VvbV9wb2ludChhZXMoY29sID0gc3RyKSwgIHNob3cubGVnZW5kID0gRkFMU0UpICsKIyAgIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLCAKIyAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJJIiwgIiIsICJJSSIpLCAKIyAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJIIj0icmVkIiwgIkMiID0gImRhcmsgZ3JheSIsICJMIj0iYmx1ZSIpKSArCiMgICBsYWJzKHk9ImxvZzEwIFRvdGFsIFBDQnMgKG5nL2cgd2V0IHdlaWdodCkiLAojICAgICAgICB4PSJQcmVkaWN0ZWQgbG9nMTAgVG90YWwgUENCcyAobmcvZyB3ZXQgd2VpZ2h0KSIsCiMgICAgICAgIHRpdGxlPSIiKSsKIyAgICMgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBwbG90X2RhdGEyW3Bsb3RfZGF0YTIkc3RyID09ICJjIixdLCBhZXMobGFiZWwgPSBwcm9wZXJfc2l0ZTIsIHg9IHByZWRpY3Rpb25zLCB5PSBnZW9tZXRyaWMubWVhbikpKwojICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBwbG90X2RhdGEyLCBhZXMobGFiZWwgPSBwcm9wZXJfc2l0ZTIsIHg9cHJlZGljdGlvbnMsIHk9Z2VvbWV0cmljLm1lYW4pKSArCiMgICBnZW9tX2xhYmVsKHg9Mi4yLCB5PTMuNiwgbGFiZWw9IkkiLCBzaXplID0gMTAsIGNvbG9yID0gInJlZCIsIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAuNTUsICJsaW5lcyIpKSArIAojICAgZ2VvbV9sYWJlbCh4PTIuOCwgeT0xLjUsIGxhYmVsPSJJSSIsIHNpemUgPSAxMCwgY29sb3IgPSAiYmx1ZSIsIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAuNTUsICJsaW5lcyIpICMgUmVjdGFuZ2xlIHNpemUgYXJvdW5kIGxhYmVsCiMgICAgICAgICAgICAgICkgKyAKIyAgICMgc3RhdF9jb3IobWV0aG9kID0gInNwZWFybWFuIiwgbGFiZWwueCA9IDEuNzUsIGxhYmVsLnkgPSAzLjcsIGNvci5jb2VmLm5hbWUgPSAicmhvIiwgc2l6ZSA9IDIuNSkgKwojICAgIyBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSAxLjc1LCBsYWJlbC55ID0gNCwgY29yLmNvZWYubmFtZSA9ICJSIiAsIHNpemUgPSAzLjUsIGNvbG9yID0gInJlZCIpICsKIyAgICMgZ2VvbV90ZXh0KGFlcygxLjc1LCAzLjgsIGhqdXN0ID0gMCwgbGFiZWwgPSBwYXN0ZTAoIk1TRSIsIiA9ICIgLHJvdW5kKHBlcmZvcm1hbmNlLm1ldHJpY3MkYGJ5LnNpdGUgKHdpdGggY2hlbWlzdHJ5IG9ubHkpYFs5XSxkaWdpdHMgPSAzKSkgKSwgc2l6ZSA9IDIuNSkgKwojICAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLCBjb2xvcj0nZ3JheScsIGZpbGw9J2xpZ2h0Ymx1ZScsIGxldmVsPTAuOTk5LCBmb3JtdWxhID0gInkgfiB4IikgKwojICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkKCiMgCiMgIyBTY2F0dGVycGxvdAp5eXBsb3QuYnlzaXRlMi4xIDwtIGdncGxvdChwbG90X2RhdGEyLCBhZXMoeD1wcmVkaWN0aW9ucywgeT1nZW9tZXRyaWMubWVhbikpICsKICBnZW9tX3BvaW50KGFlcyhjb2wgPSBpbnNpZGUpLCAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJvdXQiLCAiaW4iKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygib3V0Ij0icmVkIiwgImluIj0iZ3JheSIpKSArCiAgbGFicyh5PSJsb2cxMCBUb3RhbCBQQ0JzIChuZy9nIHdldCB3ZWlnaHQpIiwKICAgICAgIHg9IlByZWRpY3RlZCBsb2cxMCBUb3RhbCBQQ0JzIChuZy9nIHdldCB3ZWlnaHQpIiwKICAgICAgIHRpdGxlPSIiKSsKICAjIGdlb21fdGV4dF9yZXBlbChkYXRhID0gcGxvdF9kYXRhMlshcGxvdF9kYXRhMiRzdHIgPT0gIkMiLF0sIGFlcyhsYWJlbCA9IHByb3Blcl9zaXRlMiwgeD1wcmVkaWN0aW9ucywgeT1nZW9tZXRyaWMubWVhbikpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGNvbG9yPSdncmF5JywgZmlsbD0nbGlnaHRibHVlJywgbGV2ZWw9MC45OTksIGZvcm11bGEgPSAieSB+IHgiKSArCiAgIyBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgY29sb3I9J2dyYXknLCBmaWxsPSdsaWdodGJsdWUnLCBsZXZlbD0wLjk1LCBmb3JtdWxhID0gInkgfiB4IikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpCgp5eXBsb3QuYnlzaXRlMi4yIDwtIGdncGxvdChwbG90X2RhdGEyLCBhZXMoeD1wcmVkaWN0aW9ucywgeT1nZW9tZXRyaWMubWVhbikpICsKICBnZW9tX3BvaW50KGFlcyhjb2wgPSBpbnNpZGUpLCAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJvdXQiLCAiaW4iKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygib3V0Ij0icmVkIiwgImluIj0iZ3JheSIpKSArCiAgbGFicyh5PSJsb2cxMCBUb3RhbCBQQ0JzIChuZy9nIHdldCB3ZWlnaHQpIiwKICAgICAgIHg9IlByZWRpY3RlZCBsb2cxMCBUb3RhbCBQQ0JzIChuZy9nIHdldCB3ZWlnaHQpIiwKICAgICAgIHRpdGxlPSIiKSsKICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHBsb3RfZGF0YTIsIGFlcyhsYWJlbCA9IHByb3Blcl9zaXRlMiwgeD1wcmVkaWN0aW9ucywgeT1nZW9tZXRyaWMubWVhbikpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGNvbG9yPSdncmF5JywgZmlsbD0nbGlnaHRibHVlJywgbGV2ZWw9MC45OTksIGZvcm11bGEgPSAieSB+IHgiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMSkKCgoKeXlwbG90LmJ5c2l0ZTIuMQoKCgojIHRpZmYoIn4vRG9jdW1lbnRzL3dvcmsvVHN3YWxsb3dfY2hlbV9HTFJJX3VwZGF0ZS9HTFJJX01TMl9hbGwvQWxsX2ZpZ3VyZXMvRmlndXJlX3BlbmRpbmdfUENCc19kaXNjdXNzY29tcGxleG1peHR1cmUudGlmZiIsIHVuaXRzPSJpbiIsIHBvaW50c2l6ZSA9IDEwLCB3aWR0aD02LCBoZWlnaHQ9NiwgcmVzPTMwMCwgY29tcHJlc3Npb24gPSAnbHp3JykKIyB5eXBsb3QuYnlzaXRlMi4xCiMgZGV2Lm9mZigpCiMgCiMgdGlmZigifi9Eb2N1bWVudHMvd29yay9Uc3dhbGxvd19jaGVtX0dMUklfdXBkYXRlL0dMUklfTVMyX2FsbC9BbGxfZmlndXJlcy9GaWd1cmVfcGVuZGluZ19QQ0JzdjJfZGlzY3Vzc2NvbXBsZXhtaXh0dXJlLnRpZmYiLCB1bml0cz0iaW4iLCBwb2ludHNpemUgPSAxMCwgd2lkdGg9NiwgaGVpZ2h0PTYsIHJlcz0zMDAsIGNvbXByZXNzaW9uID0gJ2x6dycpCiMgeXlwbG90LmJ5c2l0ZTIuMgojIGRldi5vZmYoKQoKCmBgYAoKIyMjIyBSZW1lZGlhdGlvbiBlZmZlY3RpdmVuZXNzIGV2YWx1YXRpb24gaW4gV2F1a2VnYW4KCmBgYHtyIGNoZWNrIHJlbWVkaWF0aW9uIHN0YXR1cywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NiwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcGxvdF9kYXRhIDwtIHJlYWRSRFMoIlBDQnMucHJlZGljdGlvbnMubGFzc28uYWdncmVnYXRlLnJkcyIpCnBsb3RfZGF0YTIgPC0gcGxvdF9kYXRhJGJ5LnNpdGUKcGxvdF9kYXRhMiA8LSBwbG90X2RhdGEyICU+JSBncm91cF9ieShwcm9wZXJfc2l0ZTIpICU+JSBzdW1tYXJpc2UocHJlZGljdGlvbnMgPSBtZWFuKHByZWRpY3Rpb25zKSwgZ2VvbWV0cmljLm1lYW4gPSBtZWFuKGdlbm9taWMuUENCLm1lYW4pKSAlPiUgdW5ncm91cCgpCnBsb3RfZGF0YTIuUk0ud2sgPC0gcGxvdF9kYXRhMlshcGxvdF9kYXRhMiRwcm9wZXJfc2l0ZTIgPT0gIldhdWtlZ2FuIixdCnBsb3RfZGF0YTMgPC0gcGxvdF9kYXRhJGJ5LmluZGl2aWR1YWwKcGxvdF9kYXRhMy53ayA8LSAgcGxvdF9kYXRhM1twbG90X2RhdGEzJHByb3Blcl9zaXRlMiA9PSAiV2F1a2VnYW4iLF0gJT4lIGdyb3VwX2J5KE1lcmdlSUQgKSAlPiUgc3VtbWFyaXNlKHByZWRpY3Rpb25zID0gbWVhbiggcHJlZGljdGVkLlBDQnMpLCBhY3R1cmFsID0gbWVhbihtZWFzdXJlZC5QQ0JzKSkgJT4lIHVuZ3JvdXAoKQpwbG90X2RhdGEzLndrJHllYXIgPC0gcGFzdGUwKCIyMCIsc3RyX3N1YihwbG90X2RhdGEzLndrJE1lcmdlSUQsIDEsMikpCnBsb3RfZGF0YTMud2skeWVhcltwbG90X2RhdGEzLndrJHllYXIgJWluJSBjKCIyMDEwIiwiMjAxMSIsIjIwMTIiKV0gPC0gIkJlZm9yZSAyMDEzIgpwbG90X2RhdGEzLndrJHJlbWVkaWF0aW9uIDwtICJBZnRlciBkcmVkZ2luZyIKcGxvdF9kYXRhMy53ayRyZW1lZGlhdGlvbltwbG90X2RhdGEzLndrJHllYXIgPT0gIjIwMTMiXSA8LSAiWWVhciBvZiBkcmVkZ2luZyIKcGxvdF9kYXRhMy53ayRyZW1lZGlhdGlvbltwbG90X2RhdGEzLndrJHllYXIgPT0gIkJlZm9yZSAyMDEzIl0gPC0gIkJlZm9yZSBkcmVkZ2luZyIKcGxvdF9kYXRhMy53azIgPC0gcGxvdF9kYXRhMy53ayAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHN1bW1hcmlzZShwcmVkaWN0aW9ucyA9IG1lYW4ocHJlZGljdGlvbnMpLCBhY3R1cmFsID0gbWVhbihhY3R1cmFsKSwgcmVtZWRpYXRpb24gPSByZW1lZGlhdGlvbikgJT4lIHVuZ3JvdXAoKSAlPiUgZGlzdGluY3QoKQoKY2JiUGFsZXR0ZSA8LSBjKCIjMDAwMDAwIiwgIiNFNjlGMDAiLCAiIzU2QjRFOSIsICIjMDA5RTczIiwgIiNGMEU0NDIiLCAiIzAwNzJCMiIsICIjRDU1RTAwIiwgIiNDQzc5QTciKQoKIyMgbG0gcmVncmVzc2lvbiBmaXQgbGluZSBmb3IgcGxvdHRpbmcgYXJyb3cgCmZpdC5QQ0Iud2sgPC0gbG0oYWN0dXJhbCAgfiBwcmVkaWN0aW9ucywgZGF0YSA9IHBsb3RfZGF0YTMud2spCmRhdC53ayA8LSBwcmVkaWN0KGZpdC5QQ0Iud2spCgp5eXBsb3QuYnlzaXRlLnJlbWVkaWF0aW9uIDwtIGdncGxvdChwbG90X2RhdGEyLCBhZXMoeD1wcmVkaWN0aW9ucywgeT1nZW9tZXRyaWMubWVhbikpICsKICBnZW9tX3BvaW50KGRhdGE9IHBsb3RfZGF0YTMud2syLCBhZXMoeD1wcmVkaWN0aW9ucyAsIHk9YWN0dXJhbCwgc2hhcGUgPSByZW1lZGlhdGlvbiksIHNpemUgPSAzKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9IHBsb3RfZGF0YTMud2syLCBhZXMobGFiZWwgPSB5ZWFyLCB4PXByZWRpY3Rpb25zICwgeT1hY3R1cmFsKSwgc2l6ZSA9IDMpICsgCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz0gYygxNSwxNiwxNyksIGJyZWFrcyA9IGMoIkJlZm9yZSBkcmVkZ2luZyIsICJZZWFyIG9mIGRyZWRnaW5nIiwgIkFmdGVyIGRyZWRnaW5nIiksIAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJCZWZvcmUgZHJlZGdpbmciLCAiWWVhciBvZiBkcmVkZ2luZyIsICJBZnRlciBkcmVkZ2luZyIpKSArIAogIGxhYnMoeT0ibG9nMTAgVG90YWwgUENCcyAobmcvZyB3ZXQgd2VpZ2h0KSIsIHg9IlByZWRpY3RlZCBsb2cxMCBUb3RhbCBQQ0JzIChuZy9nIHdldCB3ZWlnaHQpIix0aXRsZT0iIikrCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLCBjb2xvcj0nZ3JheScsIGZpbGw9J2xpZ2h0Z3JheScsIGxldmVsPTAuOTk5LCBmb3JtdWxhID0gInkgfiB4IikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTEpCgp5eXBsb3QuYnlzaXRlLnJlbWVkaWF0aW9uCiMgdGlmZigifi9Eb2N1bWVudHMvd29yay9Uc3dhbGxvd19jaGVtX0dMUklfdXBkYXRlL0dMUklfTVMyX2FsbC9BbGxfZmlndXJlcy9GaWd1cmVfcGVuZGluZ19QQ0JzX3l5cGxvdC5yZW1lZGlhdGlvbi50aWZmIiwgdW5pdHM9ImluIiwgcG9pbnRzaXplID0gMTAsIHdpZHRoPTYsIGhlaWdodD02LCByZXM9MzAwLCBjb21wcmVzc2lvbiA9ICdsencnKQojIHl5cGxvdC5ieXNpdGUucmVtZWRpYXRpb24KIyBkZXYub2ZmKCkKCmBgYAoKIyMjIyBQcmVkaWN0ZWQgdnMgYWN0dXJhbCB5eSBwbG90IGJ5IG5lc3QKYGBge3IgUHJlZGljdGVkIHZzIGFjdHVyYWwgeXkgcGxvdCBieSBuZXN0LCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02LCBlY2hvPUZBTFNFfQojI0ZpZ3VyZSBTMmEgUENCcyB5eSBwbG90IApwbG90X2RhdGEgPC0gcmVhZFJEUygiUENCcy5wcmVkaWN0aW9ucy5sYXNzby5hZ2dyZWdhdGUucmRzIikKcGxvdF9kYXRhIDwtIHBsb3RfZGF0YSRieS5pbmRpdmlkdWFsICU+JSBncm91cF9ieShNZXJnZUlEKSAlPiUgc3VtbWFyaXNlKHByZWRpY3Rpb25zID0gbWVhbihwcmVkaWN0ZWQuUENCcyksIGFjdHVyYWwgPSBtZWFuKG1lYXN1cmVkLlBDQnMpKSAlPiUgdW5ncm91cCgpCiMgU2NhdHRlcnBsb3QKeXlwbG90LmJ5bmVzdCA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeD1wcmVkaWN0aW9ucyAsIHk9YWN0dXJhbCkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoeT0ibG9nMTAgVG90YWwgUENCcyAobmcvZyB3ZXQgd2VpZ2h0KSIsCiAgICAgICB4PSJQcmVkaWN0ZWQgbG9nMTAgVG90YWwgUENCcyAobmcvZyB3ZXQgd2VpZ2h0KSAiLAogICAgICAgdGl0bGU9IiIpKwogIHN0YXRfY29yKG1ldGhvZCA9ICJzcGVhcm1hbiIsIGxhYmVsLnggPSAxLCBsYWJlbC55ID0gMy44LCBjb3IuY29lZi5uYW1lID0gInJobyIsIHNpemUgPSAyLjUpICsKICBzdGF0X2NvcihtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSAxLCBsYWJlbC55ID0gMy42LCBjb3IuY29lZi5uYW1lID0gIlIiICwgc2l6ZSA9IDIuNSkgKwogIGdlb21fdGV4dChhZXMoMSwgNCwgaGp1c3QgPSAwLCBsYWJlbCA9IHBhc3RlMCgiTVNFIiwiID0gIiAscm91bmQocGVyZm9ybWFuY2UubWV0cmljcyRgYnkubmVzdCAod2l0aCBjaGVtaXN0cnkgb25seSlgWzldLGRpZ2l0cyA9IDMpKSksIHNpemUgPSAyLjUpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSA4KQp5eXBsb3QuYnluZXN0CgpgYGAKCiMjIyMgbW9kZWwgcGVyZm9ybWFuY2UKYGBge3Igc2hvdyB0YWJsZSwgZWNobz1GQUxTRX0KcGVyZm9ybWFuY2UubWV0cmljcyA8LSByZWFkUkRTKCIvaG9tZS9jaGl5ZW4vRG9jdW1lbnRzL3dvcmsvVHN3YWxsb3dfY2hlbV9HTFJJX3VwZGF0ZS9HTFJJX01TMl9hbGwvUENCLnBlcmZvcm1hbmNlLm1ldHJpY3MucmRzIikKa25pdHI6OmthYmxlKHBlcmZvcm1hbmNlLm1ldHJpY3MsIGNhcHRpb24gPSAiUENCcyBsYXNzbyByZWdyZXNzaW9uIG1vZGVsIHBlcmZvcm1hbmNlIG1ldHJpY3MiKQpgYGAKCiMjIyMgUHJlZGljdG9yIGdlbmVzIHNlbGVjdGVkIGluIHRoZSBtb2RlbCAgIAoKYGBge3IgZGV2LnJhdGlvLCBlY2hvPUZBTFNFfQprbml0cjo6a2FibGUoUENCcy5wZXJmb3JtYW5jZS5yZXBvcnQkUENCLnZhcmlhYmxlc1tbMl1dWyxjKDEsMywyKV0sICJzaW1wbGUiLCBjYXB0aW9uID0gIlBDQnMgdG9wIHByZWRpY3RvciBnZW5lcyBhbmQgY29lZmZpY2llbnQiLCBhbGlnbiA9ICJsY2MiKQpgYGAKYGBge3IgUENCIE1TRSBieSBzYW1wbGUgc2l6ZSwgaW5jbHVkZT1GQUxTRX0KIyMgdGhlbiBjYWxjdWF0ZSBNU0UgYW5kIFJNU0Ugc2l0ZXNfdmVyaWZpY2F0aW9ucyA9IGMoIldLIiwiSFciLCJTUCIpCkNvbj0iUENCc19kYXRhIjtDb25faW5kPSJUT1RBTCBQQ0JzIgpjb250YW1pbmFudC5nZW9NZWFuLmJ5c2l0ZSA8LSBkYXRhLmNvbnRhbWluYW50cy5tYWpvclN1YnNldC5nZW8uYnlzaXRlICU+JSBmaWx0ZXIoS2V5ID09IENvbl9pbmQpICU+JSBkcGx5cjo6cHVsbCh2YWx1ZS5nZW9NZWFuKSAlPiUgbG9nMTAoKSAjIyBsb2cxMCBvZiBjb250YW1pbmFudCB2YWx1ZQpuYW1lcyhjb250YW1pbmFudC5nZW9NZWFuLmJ5c2l0ZSkgPC0gZGF0YS5jb250YW1pbmFudHMubWFqb3JTdWJzZXQuZ2VvLmJ5c2l0ZSAlPiUgZmlsdGVyKEtleSA9PSBDb25faW5kKSAlPiUgZHBseXI6OnB1bGwoTWVyZ2VTaXRlKSAKbmVzdGlkIDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIgJT4lIGZpbHRlcihLZXkgPT0gQ29uX2luZCkgJT4lIGRwbHlyOjpwdWxsKE1lcmdlSUQpCm1hdGNoMSA8LSBjb2xuYW1lcyhjb3VudHMudHN3YWxsb3cubm9ybSkgJWluJSBuZXN0aWQKY291bnRzLmNvbiAgPC0gIGNvdW50cy50c3dhbGxvdy5ub3JtWyxtYXRjaDFdCmNvbnRhbWluLm1hdHJpeCA8LSBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyICU+JSBkcGx5cjo6c2xpY2UobWF0Y2goY29sbmFtZXMoY291bnRzLmNvbiksIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIkTWVyZ2VJRCkpICU+JSBmaWx0ZXIoS2V5ID09IENvbl9pbmQpICU+JeOAgG11dGF0ZSh2YWx1ZS5hZGp1c3QubG9nID0gbG9nMTAodmFsdWUuYWRqdXN0KSkKY29udGFtaW4uYXJyYXkgPC0gY29udGFtaW4ubWF0cml4JHZhbHVlLmFkanVzdC5sb2cKbmFtZXMoY29udGFtaW4uYXJyYXkpID0gY29udGFtaW4ubWF0cml4JE1lcmdlSUQKY29udGFtaW4ubWF0cml4Lm1lYW4gPC0gY29udGFtaW4ubWF0cml4ICU+JSBncm91cF9ieShNZXJnZVNpdGUpICU+JSBzdW1tYXJpc2UoZ2VvTWVhbiA9IG1lYW4odmFsdWUuYWRqdXN0LmxvZykpICU+JSB1bmdyb3VwKCkKY29udGFtaW4ubWF0cml4Lm1lYW4kTWVyZ2VTaXRlWzMxXSA9ICJOQSIKY29udGFtaW5hbnQuZ2VvTWVhbi5ieXNpdGUuZ2Vub21pYyA8LSBjb250YW1pbi5tYXRyaXgubWVhbiRnZW9NZWFuCm5hbWVzKGNvbnRhbWluYW50Lmdlb01lYW4uYnlzaXRlLmdlbm9taWMpIDwtIGNvbnRhbWluLm1hdHJpeC5tZWFuJE1lcmdlU2l0ZQp0ZXN0LnJlc3VsdCA9d2hpY2goc3RyX3N1YihuYW1lcyhzYXBwbHkoMToxMjQwLCBmdW5jdGlvbih4KSB1bm5hbWUoUENCc19DVl9ieVNpdGVfcmVzdWx0WzQsXSlbW3hdXVsxXSkpLDMsNCkgJWluJSBjKCJXSyIsIkhXIiwiU1AiKSkgIyBzZWxlY3QgdGhvc2UgaW5kaXZpZHVhbHMgaW4gV0sgSFcgU1AgClJlc3VsdCA9IHVubmFtZShQQ0JzX0NWX2J5U2l0ZV9yZXN1bHRbNCxdKVt0ZXN0LnJlc3VsdF0gIyMgMTIwIHRvdGFsCgoKV0subXNlLmFsbCA8LSB7fQpTUC5tc2UuYWxsIDwtIHt9CkhXLm1zZS5hbGwgPC0ge30KZm9yIChrIGluIDE6MTAwKSB7CiMjIHJhbmRvbWx5IHNlbGVjdCAzIHRvIDEwIGluZGl2aWR1YWxzIHRvIGNhbGN1bGF0ZSBNU0UKcHJlZGljdGlvbnMuYnkuc2l6ZSA8LSB7fQpvdXRwdXQuYWxsIDwtIGxpc3QoKQpmb3IgKGogaW4gcmVwKDM6MTAsIGVhY2g9MTApKSB7CiAgcHJlZGljdGlvbnMubWVhbi5hbGwgPC0ge30KICBmb3IgKGkgaW4gMToxMjApIHsKICBwcmVkaWN0aW9ucy5tZWFuID0gbWVhbihzYW1wbGUoUmVzdWx0W1tpXV0saikpCiAgcHJlZGljdGlvbnMubWVhbi5hbGwgPSBjKHByZWRpY3Rpb25zLm1lYW4uYWxsLCBwcmVkaWN0aW9ucy5tZWFuKQogIG91dHB1dCA9IGxpc3QobmFtZXMoc2FtcGxlKFJlc3VsdFtbaV1dLGopKSkKICBvdXRwdXQuYWxsID0gYyhvdXRwdXQuYWxsLCBvdXRwdXQpCiAgfQogIHByZWRpY3Rpb25zLm1lYW4uYWxsMiA9IGRhdGEuZnJhbWUodmFsdWUgPSBwcmVkaWN0aW9ucy5tZWFuLmFsbCwgc2l6ZSA9IGosIHNpdGUgPSByZXAoYygiV0siLCJTUCIsIkhXIiksIGVhY2ggPSA0MCkpCiAgcHJlZGljdGlvbnMuYnkuc2l6ZSA9IHJiaW5kKHByZWRpY3Rpb25zLmJ5LnNpemUsIHByZWRpY3Rpb25zLm1lYW4uYWxsMikKfQp9CiMjIGdlb01lYW46IG9ubHkgdXNlIG92ZXJsYXBwZWQgZ2Vub21pYyBzaXRlIGluZGl2aWR1bGFzIHRyYWluaW5nIHNhbXBsZXMgdG8gY2FsY3VsYXRlIGdlb01lYW4KcHJlZGljdGlvbnMyID0gY2JpbmQoYXNfdGliYmxlKHByZWRpY3Rpb25zLmJ5LnNpemUpLCBhY3R1cmFsLm1lYW4gPSBjb250YW1pbmFudC5nZW9NZWFuLmJ5c2l0ZS5nZW5vbWljW3ByZWRpY3Rpb25zLmJ5LnNpemUkc2l0ZV0pICMgcHJlZGljdGlvbiBhbmQgbWVhc3VyZWQgZ2VvbWV0cmljIG1lYW4gYnkgc2l6ZQpwcmVkaWN0aW9uczMgPSBwcmVkaWN0aW9uczIgJT4lIG11dGF0ZShkZWx0YS5zcXVhcmUgPSAodmFsdWUgLSBhY3R1cmFsLm1lYW4pXjIpICU+JSBncm91cF9ieShzaXplLCBzaXRlKSAlPiUgc3VtbWFyaXNlKE1TRSA9IG1lYW4oZGVsdGEuc3F1YXJlKSkgIyBjYWxjdWxhdGUgTVNFCnByZWRpY3Rpb25zNCA9IGFzLmRhdGEuZnJhbWUocHJlZGljdGlvbnMzKQpwcmVkaWN0aW9uczQkc2l6ZSA9IGFzLmZhY3RvcihwcmVkaWN0aW9uczQkc2l6ZSkKcHJlZGljdGlvbnM0ID0gcHJlZGljdGlvbnM0ICU+JSBncm91cF9ieShzaXRlKSAlPiUgbXV0YXRlKE1TRS5ub3JtID0gYXMubnVtZXJpYyhzY2FsZShNU0UpKSkgJT4lIHVuZ3JvdXAoKSAjIG5vcm1hbGl6ZWQgei1zY29yZSBNU0UKcHJlZGljdGlvbnM0JHNpemUgPSBhcy5udW1lcmljKHByZWRpY3Rpb25zNCRzaXplKQpwcmVkaWN0aW9uczQkcHJvcGVyc2l0ZSA9ICJOQSIKcHJlZGljdGlvbnM0JHByb3BlcnNpdGVbcHJlZGljdGlvbnM0JHNpdGUgPT0gIkhXIl0gPSAiWnVnSXNsYW5kIgpwcmVkaWN0aW9uczQkcHJvcGVyc2l0ZVtwcmVkaWN0aW9uczQkc2l0ZSA9PSAiU1AiXSA9ICJSaXZlclJhaXNpbiIKcHJlZGljdGlvbnM0JHByb3BlcnNpdGVbcHJlZGljdGlvbnM0JHNpdGUgPT0gIldLIl0gPSAiV2F1a2VnYW4iCgojIyBmaW5kIGVsYm93IHNhbXBsZSBzaXplIApnZXQuZWxib3cucG9pbnRzLmluZGljZXMgPC0gZnVuY3Rpb24oeCwgeSwgdGhyZXNob2xkKSB7CiAgZDEgPC0gZGlmZih5KSAvIGRpZmYoeCkgIyBmaXJzdCBkZXJpdmF0aXZlCiAgZDIgPC0gZGlmZihkMSkgLyBkaWZmKHhbLTFdKSAjIHNlY29uZCBkZXJpdmF0aXZlCiAgaW5kaWNlcyA8LSB3aGljaChhYnMoZDIpID4gdGhyZXNob2xkKSAgCiAgcmV0dXJuKGluZGljZXMpCn0KCmxvZXNzLnNhbXBsZXNpemUgPSBsb2VzcyhNU0Uubm9ybX5zaXplLCBkYXRhID0gcHJlZGljdGlvbnM0KQpzbW9vdGhlZCA8LSBwcmVkaWN0KGxvZXNzLnNhbXBsZXNpemUpCnNtb290aGVkIDwtIHNtb290aGVkWyFkdXBsaWNhdGVkKHNtb290aGVkKV0KCiMgZmlyc3QgYXBwcm94aW1hdGUgdGhlIGZ1bmN0aW9uLCBzaW5jZSB3ZSBoYXZlIG9ubHkgYSBmZXcgcG9pbnRzCnggPC0gMTo4CnkgPC0gc21vb3RoZWQKYXAgPC0gYXBwcm94KHgsIHksIG49MTAwMCwgeWxlZnQ9bWluKHkpLCB5cmlnaHQ9bWF4KHkpKQp4ID0gYXAkeAp5ID0gYXAkeQppbmRpY2VzIDwtIGdldC5lbGJvdy5wb2ludHMuaW5kaWNlcyh4LCB5LCAyMCkjIHRocmVzaG9sZCBmb3IgaHVnZSBqdW1wID0gMWU0CnhbaW5kaWNlc10KI1sxXSA0Ljk5ICMgdGhlcmUgaXMgb25lIHN1Y2ggcG9pbnQKcGxvdCh4LCB5LCBwY2g9MTkpCnBvaW50cyh4W2luZGljZXNdLCB5W2luZGljZXNdLCBwY2g9MTksIGNvbD0ncmVkJykgIyMgcGljayB0aGUgc2Vjb25kIG9uZQoKIyBzYXZlUkRTKHByZWRpY3Rpb25zNCwgIi9ob21lL2NoaXllbi9Eb2N1bWVudHMvd29yay9Uc3dhbGxvd19jaGVtX0dMUklfdXBkYXRlL0dMUklfTVMyX2FsbC9zYW1wbGVzaXplX3ZzX01TRV9ieV9zaXRlLnBsb3RkYXRhdjIucmRzIikKCmBgYAojIyMjIE1TRSBieSBzYW1wbGUgc2l6ZSAKYGBge3IgTVNFIGJ5IHNhbXBsZSBzaXplLCBlY2hvPUZBTFNFLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD01LCBtZXNzYWdlPUZBTFNFfQpwcmVkaWN0aW9uczQgPC0gcmVhZFJEUygic2FtcGxlc2l6ZV92c19NU0VfYnlfc2l0ZS5wbG90ZGF0YXYyLnJkcyIpCk1TRV9zYW1wbGVzaXplX3Bsb3QxIDwtIGdncGxvdChkYXRhPXByZWRpY3Rpb25zNCwgYWVzKHg9c2l6ZSwgeT1NU0Uubm9ybSkpICsKICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHByb3BlcnNpdGUpLCBzaXplID0gMSkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIpKwogIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQodGl0bGU9InNpdGUiKSxzaGFwZT1ndWlkZV9sZWdlbmQodGl0bGU9InNhbXBsZSBzaXplIikpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQuOTksIGxpbmV0eXBlPSJkb3R0ZWQiKSsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEyKQoKTVNFX3NhbXBsZXNpemVfcGxvdDEKIyB0aWZmKCJ+L0RvY3VtZW50cy93b3JrL1Rzd2FsbG93X2NoZW1fR0xSSV91cGRhdGUvR0xSSV9NUzJfYWxsL0FsbF9maWd1cmVzL0ZpZ3VyZTZfTVNFX3NhbXBsZXNpemVfcGxvdFYyLnRpZmYiLCB1bml0cz0iaW4iLCB3aWR0aD01LCBoZWlnaHQ9NCwgcmVzPTMwMCwgY29tcHJlc3Npb24gPSAnbHp3JykgCiMgTVNFX3NhbXBsZXNpemVfcGxvdDEKIyBkZXYub2ZmKCkKYGBgCgojIyMgUENCcyB0b3AgZ2VuZSBwcmVkaWN0b3JzIGZ1bmN0aW9ucyBhbmQgcGF0aHdheXMgIHsudGFic2V0fQoKUENCcyB0b3AgcHJlZGljdG9yIGdlbmVzIGhlYXRtYXAgIAo6IGNvcnJlbGF0aW9uIGJldHdlZW4gUENCIGNvbmNlbnRyYXRpb24sIHRvcCBwcmVkaWN0b3IgZ2VuZSBsb2dGQywgdG9wIHByZWRpY3RvciBnZW5lIG1vZGVsIGNvZWYuICwgdG9wIHByZWRpY3RvciBnZW5lIHVwIG9yIGRvd25yZWd1bGF0ZWQgYW5kIHRoZWlyIGNvcnJlc3BvbmRpbmcgUmVhY3RvbWUgcGF0aHdheXMgIAoKUENCcyBsYXNzbyBzZWxlY3RlZCBnZW5lcyBoZWF0bWFwIGRlYXRpbCBmdW5jdGlvbnMgYW5kIHBhdGh3YXlzICAKOiBkZXRhaWwgR08gdGVybXMgYW5kIFJlYWN0b21lIHBhdGh3YXlzCgpQQ0IgY29ycmVsYXRpb24gYmV0d2VlbiBsb2dGQyBsb2dDUE0gYW5kIG1vZGVsIGNvZWYgIAo6IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRvcCBwcmVkaWN0b3IgZ2VuZXMgYmV0d2VlbiBsb2dGQywgbG9nQ1BNIChjb3VudHMpLCBhbmQgUENCIG1vZGVsIGNvZWYuIG1lYXN1cmVkIGluIHRvcCAxLzMgbG9nRkMgaW5kaXZpZHVhbHMgCgpgYGB7ciBQQ0JzIGZ1bmN0aW9ucyBoZWF0bWFwIGRhdGEgcHJvY2Vzc2luZywgaW5jbHVkZT1GQUxTRX0KIyMgZWRpdHRpbmcgZ28gYW5kIHJlYWN0b21lIHRlcm1zIAoKIyMgbG9hZCBmdW5jdGlvbi8gcGF0aHdheSBleHBhbGFuYXRpb24gCmxpYnJhcnkoeG1sMikKUENCc19icC5kYXRhLnJhdyA8LSByZWFkX3htbCgiUENCc18ybmRMYXNzb19mdW5jaW9udHMueG1sIikgIyMgZnJvbSAgUEFOVEhFUjE3LjAgYWRkIGNpdGF0aW9uIGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9uYXIvYXJ0aWNsZS80OS9EMS9EMzk0LzYwMjc4MTI/bG9naW49ZmFsc2UKaWQgPSB4bWxfdGV4dCh4bWxfZmluZF9hbGwoUENCc19icC5kYXRhLnJhdywgIi8vcmVzdWx0L3Rlcm0vaWQiKSkKdGVybSA9IHhtbF90ZXh0KHhtbF9maW5kX2FsbChQQ0JzX2JwLmRhdGEucmF3LCAiLy9yZXN1bHQvdGVybS9sYWJlbCIpKQpudW1iZXJfaW5fbGlzdCA9IGFzLm51bWVyaWMoeG1sX3RleHQoeG1sX2ZpbmRfYWxsKFBDQnNfYnAuZGF0YS5yYXcsICIvL3Jlc3VsdC9pbnB1dF9saXN0L251bWJlcl9pbl9saXN0IikpKQpudW1iZXJfaW5fcmVmZXJlbmNlID0gYXMubnVtZXJpYyh4bWxfdGV4dCh4bWxfZmluZF9hbGwoUENCc19icC5kYXRhLnJhdywgIi8vcmVzdWx0L251bWJlcl9pbl9yZWZlcmVuY2UiKSkpCnBWYWx1ZSA9IGFzLm51bWVyaWMoeG1sX3RleHQoeG1sX2ZpbmRfYWxsKFBDQnNfYnAuZGF0YS5yYXcsICIvL3BWYWx1ZSIpKSkKZm9sZF9lbnJpY2htZW50ID0gYXMubnVtZXJpYyh4bWxfdGV4dCh4bWxfZmluZF9hbGwoUENCc19icC5kYXRhLnJhdywgIi8vZm9sZF9lbnJpY2htZW50IikpKQpxdWVzdC5pZCA9IGxhcHBseSgxOjExMjA4LCBmdW5jdGlvbih4KSBwYXN0ZTAoInJlc3VsdFsiLCB4LCAiXS9pbnB1dF9saXN0L21hcHBlZF9pZF9saXN0L21hcHBlZF9pZCIpKQptYXBwZWRfaWQgPSBsYXBwbHkoMToxMTIwOCwgZnVuY3Rpb24oeCkgeG1sX3RleHQoeG1sX2ZpbmRfYWxsKFBDQnNfYnAuZGF0YS5yYXcsIHF1ZXN0LmlkW1t4XV0pKSkKbWFwcGVkX2lkID0gbWFwcGVkX2lkWy0xN10KbmFtZXMobWFwcGVkX2lkKSA9IGlkCgp0YWJsZS5wYW50aGVyID0gdGliYmxlKHRlcm0gPSB0ZXJtLCBudW1iZXJfaW5fbGlzdCA9IG51bWJlcl9pbl9saXN0LCBudW1iZXJfaW5fcmVmZXJlbmNlID0gbnVtYmVyX2luX3JlZmVyZW5jZSwgcFZhbHVlID0gcFZhbHVlLCBmb2xkX2VucmljaG1lbnQgPSBmb2xkX2VucmljaG1lbnQpCiMjIHJlbW92ZSBVTkNMQVNTSUZJRUQKdGFibGUucGFudGhlciA9IHRhYmxlLnBhbnRoZXJbLTE3LF0KIyMgcmVtb3ZlIGZvbGQgZW5yaWNobWVudCA9IDAgCnRhYmxlLnBhbnRoZXIgPSB0aWJibGUoaWQgPSBpZCAsIHRhYmxlLnBhbnRoZXIpCnRhYmxlLnBhbnRoZXIgPSB0YWJsZS5wYW50aGVyICU+JSBmaWx0ZXIoZm9sZF9lbnJpY2htZW50ID4gMCkKUENCc19icC5wYW50aGVyID0gdGFibGUucGFudGhlcgpQQ0JzX2JwLnBhbnRoZXIubWFwcGVkX2lkID0gbWFwcGVkX2lkW1BDQnNfYnAucGFudGhlciRpZF0KCiMjIGNoZWNrIGxhc3NvIHNlbGVjdGVkIFBBSHMgcmVsYXRlZCBnZW5lcyBmdW5jaW9ucyBhbmQgcGxvdCBhcyBoZWF0bWFwIApQQ0JzX2JwLnBhbnRoZXIudHJpbSA9IFBDQnNfYnAucGFudGhlciAlPiUgZmlsdGVyKG51bWJlcl9pbl9yZWZlcmVuY2UgPCA0MDAwICYgbnVtYmVyX2luX3JlZmVyZW5jZSA+IDMwKSAlPiUgIGZpbHRlcihudW1iZXJfaW5fbGlzdCAgPiAyKSAgIyBwaWNrIDQwMDAgYW5kIDMwIGZvciBtb3JlIGNvbW1vbiB0ZXJtcyBmb3IgY2xhc3NpZmljYXRpb24KClJldmlnb19wY2JfdHJpbV9saXN0IDwtIHJlYWQudGFibGUoIlJldmlnb19CUF9PblNjcmVlblRhYmxlLnRzdiIpICMgZG9pOjEwLjEzNzEvam91cm5hbC5wb25lLjAwMjE4MDAsIHVzaW5nIHNtYWxsLCAgcmVtb3ZlIG9ic29sZXRlIEdPIHRlcm1zOiBZRVMsIHdvcmsgd2l0aCBnYWxsdXMgZ2FsbHVzIDkwMzEsIHNlbWFudGljIHNpbWlsYXJpdHkgbWVhc3VyZSBkZWZhdWx0IHNldHRpbmcKY29sbmFtZXMoUmV2aWdvX3BjYl90cmltX2xpc3QpID0gUmV2aWdvX3BjYl90cmltX2xpc3RbMSxdClJldmlnb19wY2JfdHJpbV9saXN0ID0gYXNfdGliYmxlKFJldmlnb19wY2JfdHJpbV9saXN0Wy0xLF0pICU+JSB0b3BfbigtMzAsIERpc3BlbnNhYmlsaXR5KSAjIHBpY2sgdG9wIDMwIHRlcm1zIGFmdGVyIHVzaW5nIFJldmlnbyAKUmV2aWdvX3BjYl90cmltX2xpc3QgPC0gUmV2aWdvX3BjYl90cmltX2xpc3QkVGVybUlEClBDQnNfYnAucGFudGhlci50cmltID0gUENCc19icC5wYW50aGVyLnRyaW0gJT4lIGZpbHRlcihpZCAlaW4lIFJldmlnb19wY2JfdHJpbV9saXN0KQoKUENCc19icC5wYW50aGVyLm1hcHBlZF9pZCRhbGwgPSB1bmlxdWUobmEub21pdCh2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUkbmFtZSkpClBDQnNfYnAucGFudGhlci5kYXRhID0gdW5saXN0KFBDQnNfYnAucGFudGhlci5tYXBwZWRfaWQpCm5hbWVzKFBDQnNfYnAucGFudGhlci5kYXRhKSA9IHN0cl9zdWIobmFtZXMoUENCc19icC5wYW50aGVyLmRhdGEpLCAxLDEwKQpuYW1lcyhQQ0JzX2JwLnBhbnRoZXIuZGF0YSlbZ3JlcCgiYWxsIixuYW1lcyhQQ0JzX2JwLnBhbnRoZXIuZGF0YSkpXSA9ICJhbGwiClBDQnNfYnAucGFudGhlci5kYXRhID0gZGF0YS5mcmFtZShHT0JQX0lEID0gbmFtZXMoUENCc19icC5wYW50aGVyLmRhdGEpLCBuYW1lID0gUENCc19icC5wYW50aGVyLmRhdGEpClBDQnNfYnAucGFudGhlci5kYXRhID0gZHBseXI6OmxlZnRfam9pbihQQ0JzX2JwLnBhbnRoZXIuZGF0YSwgdmFyaWFibGVzLmNvZWYuYWdncmVnYXRlKQpQQ0JzX2JwLnBhbnRoZXIyID0gUENCc19icC5wYW50aGVyCmNvbG5hbWVzKFBDQnNfYnAucGFudGhlcjIpWzFdID0gIkdPQlBfSUQiClBDQnNfYnAucGFudGhlci5kYXRhID0gZHBseXI6OmxlZnRfam9pbihQQ0JzX2JwLnBhbnRoZXIuZGF0YSwgUENCc19icC5wYW50aGVyMikKClBDQnMuYnAuc2VsZWN0ID0gYygiYWxsIiwgUENCc19icC5wYW50aGVyLnRyaW0kaWQpCgpQQ0JzX2JwLnBhbnRoZXIuZGF0YSR0ZXJtW1BDQnNfYnAucGFudGhlci5kYXRhJEdPQlBfSUQgPT0gImFsbCJdID0gImFsbCIKZGF0YSA8LSBhc190aWJibGUoUENCc19icC5wYW50aGVyLmRhdGFbLGMoMSwyLDQsNSldKQpkYXRhIDwtIHBpdm90X3dpZGVyKGRhdGEsIG5hbWVzX2Zyb20gPSAibmFtZSIsdmFsdWVzX2Zyb20gPSAiY29lZiIpCmRhdGEgPC0gZGF0YSAlPiUgZmlsdGVyKEdPQlBfSUQgJWluJSBQQ0JzLmJwLnNlbGVjdCkKZGF0YS5tYXRyaXggPSBkYXRhWywzOjUxXQojIGRhdGEubWF0cml4W2lzLm5hKGRhdGEubWF0cml4KV0gPSAwCmRhdGEubWF0cml4ID0gYXMubWF0cml4KGRhdGEubWF0cml4KQpyb3cubmFtZXMoZGF0YS5tYXRyaXgpID0gZGF0YSR0ZXJtCmRhdGEubWF0cml4W2lzLm5hKGRhdGEubWF0cml4KV0gPSAwCkdPQlBfZGF0YS5tYXRyaXggPSBkYXRhLm1hdHJpeAoKIyMgbG9hZCBQQ0JzIHBhdGh3YXkgIApsaWJyYXJ5KHhtbDIpCmxpYnJhcnkoZHBseXIpClBDQnNfcmVhY3RvbWUuZGF0YS5yYXcgPC0gcmVhZF94bWwoIlBDQnNfMm5kTGFzc29fcGF0aHdheXMueG1sIikgIyMgZnJvbSAgUEFOVEhFUjE3LjAgYWRkIGNpdGF0aW9uIGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9uYXIvYXJ0aWNsZS80OS9EMS9EMzk0LzYwMjc4MTI/bG9naW49ZmFsc2UKaWQgPSB4bWxfdGV4dCggeG1sX2ZpbmRfYWxsKCBQQ0JzX3JlYWN0b21lLmRhdGEucmF3LCAiLy90ZXJtL2lkIiApICkKbGFiZWwgPSB4bWxfdGV4dCggeG1sX2ZpbmRfYWxsKCBQQ0JzX3JlYWN0b21lLmRhdGEucmF3LCAiLy90ZXJtL2xhYmVsIiApICkKbnVtYmVyX2luX3JlZmVyZW5jZSA9IHhtbF90ZXh0KCB4bWxfZmluZF9hbGwoIFBDQnNfcmVhY3RvbWUuZGF0YS5yYXcsICIvL251bWJlcl9pbl9yZWZlcmVuY2UiICkpCm51bWJlcl9pbl9saXN0ID0geG1sX3RleHQoIHhtbF9maW5kX2FsbCggUENCc19yZWFjdG9tZS5kYXRhLnJhdywgIi8vbnVtYmVyX2luX2xpc3QiICkpCnBWYWx1ZSA9IHhtbF90ZXh0KCB4bWxfZmluZF9hbGwoIFBDQnNfcmVhY3RvbWUuZGF0YS5yYXcsICIvL3BWYWx1ZSIgKSkKZm9sZF9lbnJpY2htZW50ID0geG1sX3RleHQoIHhtbF9maW5kX2FsbCggUENCc19yZWFjdG9tZS5kYXRhLnJhdywgIi8vZm9sZF9lbnJpY2htZW50IiApKQpxdWVzdC5saXN0ID0gbGFwcGx5KDE6MTYzMSwgZnVuY3Rpb24oeCkgcGFzdGUwKCJyZXN1bHRbIiwgeCwgIl0vaW5wdXRfbGlzdC9tYXBwZWRfaWRfbGlzdC9tYXBwZWRfaWQiKSkKbWFwcGVkX2lkID0gICBsYXBwbHkoMToxNjMxLCBmdW5jdGlvbih4KSB4bWxfdGV4dCggeG1sX2ZpbmRfYWxsKCBQQ0JzX3JlYWN0b21lLmRhdGEucmF3LCBxdWVzdC5saXN0W1t4XV0pKSkKbWFwcGVkX2lkID0gbWFwcGVkX2lkWy0xXQpuYW1lcyhtYXBwZWRfaWQpID0gaWQKClBDQnNfcmVhY3RvbWUuZGF0YSA9IGRhdGEuZnJhbWUobGFiZWwgPSBsYWJlbCxudW1iZXJfaW5fcmVmZXJlbmNlPWFzLm51bWVyaWMobnVtYmVyX2luX3JlZmVyZW5jZSksIG51bWJlcl9pbl9saXN0PWFzLm51bWVyaWMobnVtYmVyX2luX2xpc3QpLHBWYWx1ZT1hcy5udW1lcmljKHBWYWx1ZSksZm9sZF9lbnJpY2htZW50PWFzLm51bWVyaWMoZm9sZF9lbnJpY2htZW50KSlbLTEsXQpQQ0JzX3JlYWN0b21lLmRhdGEgPSBjYmluZChpZCA9IGlkICwgUENCc19yZWFjdG9tZS5kYXRhKQpQQ0JzX3JlYWN0b21lLmRhdGEgPSBhc190aWJibGUoUENCc19yZWFjdG9tZS5kYXRhKQpQQ0JzX3JlYWN0b21lLmRhdGEgPSBQQ0JzX3JlYWN0b21lLmRhdGEgJT4lIGZpbHRlcihmb2xkX2VucmljaG1lbnQgPiAwKSAlPiUgZmlsdGVyKG51bWJlcl9pbl9saXN0ID4gMSkgIyMgZmlsdGVyIG91dCBlbXBseSBjZWxscyBhbmQgb25seSBvbmUgaGl0IHJlc3VsdApQQ0JzX3JlYWN0b21lLmRhdGEubWFwcGVkX2lkID0gbWFwcGVkX2lkW1BDQnNfcmVhY3RvbWUuZGF0YSRpZF0KUENCc19yZWFjdG9tZS5kYXRhLm1hcHBlZF9pZCRhbGwgPSB1bmlxdWUobmEub21pdCh2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUkbmFtZSkpICMjIGFkZCBhbGwgcm93CgpQQ0JzX3JlYWN0b21lLmRhdGEubWFwcGVkLmRhdGEgPSB1bmxpc3QoUENCc19yZWFjdG9tZS5kYXRhLm1hcHBlZF9pZCkKbmFtZXMoUENCc19yZWFjdG9tZS5kYXRhLm1hcHBlZC5kYXRhKSA9IHN0cl9yZW1vdmUobmFtZXMoUENCc19yZWFjdG9tZS5kYXRhLm1hcHBlZC5kYXRhKSwgIlxcZCQiKSAKbmFtZXMoUENCc19yZWFjdG9tZS5kYXRhLm1hcHBlZC5kYXRhKVtncmVwKCJhbGwiLG5hbWVzKFBDQnNfcmVhY3RvbWUuZGF0YS5tYXBwZWQuZGF0YSkpXSA9ICJhbGwiCm5hbWVzKFBDQnNfcmVhY3RvbWUuZGF0YS5tYXBwZWQuZGF0YSlbbmFtZXMoUENCc19yZWFjdG9tZS5kYXRhLm1hcHBlZC5kYXRhKSA9PSAiUi1HR0EtMTYyNTgyMSIgXSA9ICJSLUdHQS0xNjI1ODIiCgpQQ0JzX3JlYWN0b21lLnBhbnRoZXIuZGF0YSA9IGRhdGEuZnJhbWUoUmVhY3RvbWVfSUQgPSBuYW1lcyhQQ0JzX3JlYWN0b21lLmRhdGEubWFwcGVkLmRhdGEpLCBuYW1lID0gUENCc19yZWFjdG9tZS5kYXRhLm1hcHBlZC5kYXRhKQpQQ0JzX3JlYWN0b21lLnBhbnRoZXIuZGF0YSA9IGRwbHlyOjpsZWZ0X2pvaW4oUENCc19yZWFjdG9tZS5wYW50aGVyLmRhdGEsIHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZSkKUENCc19yZWFjdG9tZS5wYW50aGVyMiA9IFBDQnNfcmVhY3RvbWUuZGF0YVssMTo1XQpjb2xuYW1lcyhQQ0JzX3JlYWN0b21lLnBhbnRoZXIyKVsxXSA9ICJSZWFjdG9tZV9JRCIKUENCc19yZWFjdG9tZS5wYW50aGVyLmRhdGEgPSBkcGx5cjo6bGVmdF9qb2luKFBDQnNfcmVhY3RvbWUucGFudGhlci5kYXRhLCBQQ0JzX3JlYWN0b21lLnBhbnRoZXIyKQoKUENCc19yZWFjdG9tZS5wYW50aGVyLmRhdGEkbGFiZWxbUENCc19yZWFjdG9tZS5wYW50aGVyLmRhdGEkUmVhY3RvbWVfSUQgPT0gImFsbCJdID0gImFsbCIKZGF0YSA8LSBhc190aWJibGUoUENCc19yZWFjdG9tZS5wYW50aGVyLmRhdGFbLGMoMSwyLDQsNSldKQpkYXRhIDwtIHBpdm90X3dpZGVyKGRhdGEsIG5hbWVzX2Zyb20gPSAibmFtZSIsdmFsdWVzX2Zyb20gPSAiY29lZiIpCmRhdGEubWF0cml4ID0gZGF0YVssMzo1MV0KIyBkYXRhLm1hdHJpeFtpcy5uYShkYXRhLm1hdHJpeCldID0gMApkYXRhLm1hdHJpeCA9IGFzLm1hdHJpeChkYXRhLm1hdHJpeCkKcm93Lm5hbWVzKGRhdGEubWF0cml4KSA9IGRhdGEkbGFiZWwKZGF0YS5tYXRyaXhbaXMubmEoZGF0YS5tYXRyaXgpXSA9IDAKUmVhY3RvbWVfZGF0YS5tYXRyaXggPSBkYXRhLm1hdHJpeAoKUENCc19sYXNzb19ieV9zaXRlXzUwX2Z1bmN0aW9uYWxfZGF0YSA9IGxpc3QocGFudGhlcl9yZXN1bHQgPSBQQ0JzX2JwLnBhbnRoZXIsIGdlbmVfbWFwcGVkX3BhbnRoZXJfcmVzdWx0ID0gUENCc19icC5wYW50aGVyLmRhdGEsIGhlYXRtYXAuYnAucGxvdC5kYXRhID0gR09CUF9kYXRhLm1hdHJpeCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXAucmVhY3RvbWUucGxvdC5kYXRhID0gUmVhY3RvbWVfZGF0YS5tYXRyaXgsIHRvcF9nb190ZXJtcyA9IFBDQnMuYnAuc2VsZWN0KQojIHNhdmVSRFMoUENCc19sYXNzb19ieV9zaXRlXzUwX2Z1bmN0aW9uYWxfZGF0YSwgICJQQ0JzX2xhc3NvX2J5X3NpdGVfNTBfZnVuY3Rpb25hbF9kYXRhLnJkcyIpCgoKIyMgbWFraW5nIGhlYXRtYXAgZGF0YSAobG9nRkMsIFBDQnMgY29uY2VudHJhdGlvbiwgUENCIGNvZWYgKQpDb249IlBDQnNfZGF0YSI7Q29uX2luZD0iVE9UQUwgUENCcyIKc2l0ZU1BUC5hbGwyID0gc2l0ZU1BUC5hbGwgJT4lIGRpc3RpbmN0KCkKc2l0ZU1BUC5hbGwyID0gc2l0ZU1BUC5hbGwyICU+JSBtdXRhdGUoQU9DX25hbWUgPSBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyJEFPQ1ttYXRjaChhcy5jaGFyYWN0ZXIoc2l0ZU1BUC5hbGwyJFNpdGVJRCksIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIkTWVyZ2VTaXRlKV0pCnNpdGVNQVAuYWxsMiRBT0NfbmFtZVtzaXRlTUFQLmFsbDIkU2l0ZUlEID09ICJOQSJdIDwtICJNaWx3YXVrZWVFc3R1YXJ5IgpzaXRlTUFQLmFsbDIgPSBzaXRlTUFQLmFsbDJbIWlzLm5hKHNpdGVNQVAuYWxsMiRBT0NfbmFtZSksXQpzaXRlTUFQLmFsbDIkQU9DX25hbWVbc2l0ZU1BUC5hbGwyJEFPQ19uYW1lID09ICIuIl0gPSAiUmVmIgpzaXRlTUFQLmFsbDIkQU9DX25hbWVbZ3JlcCgiTm9uIixzaXRlTUFQLmFsbDIkQU9DX25hbWUpXSA9ICJOb24tQU9DIgpuZXN0aWQgPC0gY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiAlPiUgZmlsdGVyKEtleSA9PSBDb25faW5kKSAlPiUgZHBseXI6OnB1bGwoTWVyZ2VJRCkKbWF0Y2gxIDwtIGNvbG5hbWVzKGNvdW50cy50c3dhbGxvdy5ub3JtKSAlaW4lIG5lc3RpZAojIyBjb3VudHMgZGF0YSBmb3IgUENCcwpjb3VudHMuY29uICA8LSAgY291bnRzLnRzd2FsbG93Lm5vcm1bLG1hdGNoMV0KCmNvdW50cy5jb24uc3Vic2V0IDwtIGNvdW50cy5jb25bUENCcy5wZXJmb3JtYW5jZS5yZXBvcnRbWzRdXVtbMl1dWywyXVshaXMubmEoUENCcy5wZXJmb3JtYW5jZS5yZXBvcnRbWzRdXVtbMl1dWywxXSldLF0gIyBzZWxlY3QgdGhvc2Ugd2l0aCBwcm9wZXIgZ2VuZSBuYW1lIApyb3cubmFtZXMoY291bnRzLmNvbi5zdWJzZXQpIDwtIGFzLmNoYXJhY3RlcihuYS5vbWl0KFBDQnMucGVyZm9ybWFuY2UucmVwb3J0W1s0XV1bWzJdXVssMV0pKSAjIHJlbmFtZSB0byBwcm9wZXIgZ2VuZW4gbmFtZQojIyBtYWtlIG1hdHJpeCAKY291bnRzLmNvbi5zdWJzZXQubWF0cml4IDwtIGFzLm1hdHJpeChjb3VudHMuY29uLnN1YnNldCkKY291bnRzLmNvbi5zdWJzZXQuc2l0ZSA8LSAgc2l0ZU1BUC5hbGwkcHJvcGVyc2l0ZVttYXRjaChzdHJfc3ViKGNvbG5hbWVzKGNvdW50cy5jb24uc3Vic2V0Lm1hdHJpeCksMyw0KSwgc2l0ZU1BUC5hbGwkU2l0ZUlEKV0KY291bnRzLmNvbi5zdWJzZXQuQU9DIDwtIHNpdGVNQVAuYWxsMiRBT0NfbmFtZVttYXRjaChjb3VudHMuY29uLnN1YnNldC5zaXRlLHNpdGVNQVAuYWxsMiRwcm9wZXJzaXRlKV0KCmNvbnRhbWluYW50Lmdlb01lYW4gPSBjb250YW1pbmFudC5nZW9NZWFuLmJ5c2l0ZS5nZW5vbWljCm5hbWVzKGNvbnRhbWluYW50Lmdlb01lYW4pID0gc2l0ZU1BUC5hbGwyJHByb3BlcnNpdGVbbWF0Y2gobmFtZXMoY29udGFtaW5hbnQuZ2VvTWVhbiksIHNpdGVNQVAuYWxsMiRTaXRlSUQpXQoKY291bnRzLmNvbi5zdWJzZXQuc2l0ZVBDQmxhYmVsIDwtIGNvbnRhbWluYW50Lmdlb01lYW5bY291bnRzLmNvbi5zdWJzZXQuc2l0ZV0KbmFtZXMoY291bnRzLmNvbi5zdWJzZXQuc2l0ZVBDQmxhYmVsKSA8LSBjb3VudHMuY29uLnN1YnNldC5zaXRlCmNvdW50cy5jb24uc3Vic2V0LnNpdGVQQ0JsYWJlbFtjb3VudHMuY29uLnN1YnNldC5zaXRlUENCbGFiZWwgPCAyLjI2NF0gPC0gIjwgMzN0aCBwZXJjZW50aWxlIiAgCmNvdW50cy5jb24uc3Vic2V0LnNpdGVQQ0JsYWJlbFtjb3VudHMuY29uLnN1YnNldC5zaXRlUENCbGFiZWwgPiAyLjcxNV0gPC0gIj4gNjd0aCBwZXJjZW50aWxlIgpjb3VudHMuY29uLnN1YnNldC5zaXRlUENCbGFiZWxbY291bnRzLmNvbi5zdWJzZXQuQU9DID09ICJOb24tQU9DIl0gPC0gIk5vbi1BT0MiCmNvdW50cy5jb24uc3Vic2V0LnNpdGVQQ0JsYWJlbFtjb3VudHMuY29uLnN1YnNldC5BT0MgPT0gIlJlZiJdIDwtICJSZWYiCmNvdW50cy5jb24uc3Vic2V0LnNpdGVQQ0JsYWJlbFtncmVwKCJcXC4iLGNvdW50cy5jb24uc3Vic2V0LnNpdGVQQ0JsYWJlbCldIDwtICIzMyAtIDY3IHBlcmNlbnRpbGUiCmNvdW50cy5jb24uc3Vic2V0LnNpdGVQQ0JsYWJlbCA8LSBmYWN0b3IoY291bnRzLmNvbi5zdWJzZXQuc2l0ZVBDQmxhYmVsLCBsZXZlbHMgPSBjKCI+IDY3dGggcGVyY2VudGlsZSIsIjMzIC0gNjcgcGVyY2VudGlsZSIsIjwgMzN0aCBwZXJjZW50aWxlIiAsIk5vbi1BT0MiLCAiUmVmIikpCgojIyBtYXRjaGluZyBQQ0JzIGRhdGEgCmNvbnRhbWluLm1hdHJpeCA8LSBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyICU+JSBkcGx5cjo6c2xpY2UobWF0Y2goY29sbmFtZXMoY291bnRzLmNvbiksIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIkTWVyZ2VJRCkpICU+JSBmaWx0ZXIoS2V5ID09IENvbl9pbmQpICU+JeOAgG11dGF0ZSh2YWx1ZS5hZGp1c3QubG9nID0gbG9nMTAodmFsdWUuYWRqdXN0KSkKY29udGFtaW4uYXJyYXkgPC0gY29udGFtaW4ubWF0cml4JHZhbHVlLmFkanVzdC5sb2cKY29udGFtaW4uYXJyYXkub3JkZXIgPC0gb3JkZXIoY29udGFtaW4uYXJyYXkpCm5hbWVzKGNvbnRhbWluLmFycmF5KSA9IGNvbnRhbWluLm1hdHJpeCRNZXJnZUlECmBgYAoKIyMjIyBQQ0JzIHRvcCBwcmVkaWN0b3IgZ2VuZXMgaGVhdG1hcAoKYGBge3IgUENCcyB0b3AgcHJlZGljdG9yIGdlbmVzIGhlYXRtYXAsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0UsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTEwfQpSZWFjdG9tZS5QQ0JzLnN1YnNldCA9IGMoIlNpZ25hbCBUcmFuc2R1Y3Rpb24iICwgIk1ldGFib2xpc20gb2YgbGlwaWRzIiAsICJJbW11bmUgU3lzdGVtIiAsICJDYXJkaWFjIGNvbmR1Y3Rpb24iLCAiSGVtb3N0YXNpcyIpClJlYWN0b21lX2RhdGEubWF0cml4LnQgPSB0KFJlYWN0b21lX2RhdGEubWF0cml4Wyxjb2xuYW1lcyhHT0JQX2RhdGEubWF0cml4KV0pCgpoZWF0bWFwLmFubm8gPSBhcy5kYXRhLmZyYW1lKFJlYWN0b21lX2RhdGEubWF0cml4LnRbcm93Lm5hbWVzKGNvdW50cy5jb24uc3Vic2V0Lm1hdHJpeCksUmVhY3RvbWUuUENCcy5zdWJzZXRdKSAjIG1ha2Ugcm93IGFubm90YWlvbiBmcm9tIGxhc3NvIGNvZWYgYW5kIHJlYWN0b21lIHBhdGh3YXlzIApoZWF0bWFwLmFubm9baGVhdG1hcC5hbm5vID4gMF0gPC0gInVwIgpoZWF0bWFwLmFubm9baGVhdG1hcC5hbm5vIDwgMF0gPC0gImRvd24iCmhlYXRtYXAuYW5ub1toZWF0bWFwLmFubm8gPT0gMF0gPC0gTkEKaGVhdG1hcC5hbm5vIDwtIGNiaW5kKFBDQi5jb2VmID0gUmVhY3RvbWVfZGF0YS5tYXRyaXgudFtyb3cubmFtZXMoY291bnRzLmNvbi5zdWJzZXQubWF0cml4KSwiYWxsIl0sIGhlYXRtYXAuYW5ubykKaGVhdG1hcC5hbm5vJGBTaWduYWwgVHJhbnNkdWN0aW9uYCA8LSBhcy5mYWN0b3IoaGVhdG1hcC5hbm5vJGBTaWduYWwgVHJhbnNkdWN0aW9uYCkKaGVhdG1hcC5hbm5vJGBNZXRhYm9saXNtIG9mIGxpcGlkc2AgPC0gYXMuZmFjdG9yKGhlYXRtYXAuYW5ubyRgTWV0YWJvbGlzbSBvZiBsaXBpZHNgKQpoZWF0bWFwLmFubm8kYEltbXVuZSBTeXN0ZW1gIDwtIGFzLmZhY3RvcihoZWF0bWFwLmFubm8kYEltbXVuZSBTeXN0ZW1gKQpoZWF0bWFwLmFubm8kYENhcmRpYWMgY29uZHVjdGlvbmAgPC0gYXMuZmFjdG9yKGhlYXRtYXAuYW5ubyRgQ2FyZGlhYyBjb25kdWN0aW9uYCkKaGVhdG1hcC5hbm5vJEhlbW9zdGFzaXMgPC0gYXMuZmFjdG9yKGhlYXRtYXAuYW5ubyRIZW1vc3Rhc2lzKQoKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShkZW5kc29ydCkKY29sX2Z1biA9IGNvbG9yUmFtcDIoYygtMC4yLCAwLCAwLjIpLCBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQoKCiMjIGZpZ3VyZSBQQ0JzX2xhc3NvNTBfaGVhdG1hcC52NAojIyBub3JtYWxpemVkIGJ5IHN0YXJsYWtlIGF2ZXJhZ2UgY291bnRzCmNvdW50cy5jb24uc3Vic2V0Lm1hdHJpeC5jb3JyZWN0ZWQgPSBjb3VudHMuY29uLnN1YnNldC5tYXRyaXggLSByb3dNZWFucyhjb3VudHMuY29uLnN1YnNldC5tYXRyaXhbLGdyZXAoIlNMIiwgY29sbmFtZXMoY291bnRzLmNvbi5zdWJzZXQubWF0cml4KSldKQojIHJvd19kZW5kID0gZGVuZHNvcnQoaGNsdXN0KGRpc3QoY291bnRzLmNvbi5zdWJzZXQubWF0cml4LmNvcnJlY3RlZCksIG1ldGhvZCA9ICJ3YXJkLkQyIikpCmNvbF9kZW5kID0gZGVuZHNvcnQoaGNsdXN0KGRpc3QodChjb3VudHMuY29uLnN1YnNldC5tYXRyaXguY29ycmVjdGVkKSwgbWV0aG9kID0gImV1Y2xpZGVhbiIpLCBtZXRob2QgPSAiY29tcGxldGUiKSkKcm93cy5jb3IgPC0gZGVuZHNvcnQoaGNsdXN0KGFzLmRpc3QoMS0gY29yKHQoY291bnRzLmNvbi5zdWJzZXQubWF0cml4LmNvcnJlY3RlZCksIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAicGVhcnNvbiIpKSwgbWV0aG9kID0gImNvbXBsZXRlIiksIGlzUmV2ZXJzZSA9IFRSVUUpCgojIyBhZGQgZXhwcmVzc2lvbiAKaGVhdG1hcC5hbm5vMiA9IGhlYXRtYXAuYW5ubwpoZWF0bWFwLmFubm8yJGV4cHJlc3Npb24gPSByb3dNZWFucyhjb3VudHMuY29uLnN1YnNldC5tYXRyaXgpCmhlYXRtYXAuYW5ubzIgPSBoZWF0bWFwLmFubm8yWyxjKDEsNywyLDMsNCw1LDYpXQoKbGdkID0gTGVnZW5kKHRpdGxlID0gIlJlYWN0b21lIHBhdGh3YXlzIiwgbGVnZW5kX2dwICA9IGdwYXIoZmlsbCA9IGMoInJlZCIsImJsdWUiKSksIAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygidXAiLCAiZG93biIpKQpQQ0JzX2xhc3NvNTBfaGVhdG1hcC52NCA8LQogIEhlYXRtYXAodChjb3VudHMuY29uLnN1YnNldC5tYXRyaXguY29ycmVjdGVkKSwgbmFtZSA9ICJsb2dGQyIsIGNsdXN0ZXJfY29sdW1ucyA9IHJvd3MuY29yLCBjbHVzdGVyX3Jvd3MgPSBjb2xfZGVuZCwgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwgY29sdW1uX25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDgpLAogICAgICAgICAgY29sdW1uX3RpdGxlID0gIiIsIGxlZnRfYW5ub3RhdGlvbiA9IHJvd0Fubm90YXRpb24oYFtQQ0JzXWAgPSBhbm5vX3BvaW50cyhjb250YW1pbi5hcnJheSksIHNpdGUuUENCcyA9IGNvdW50cy5jb24uc3Vic2V0LnNpdGVQQ0JsYWJlbCwgZ2FwID0gdW5pdCgwLjIsICJjbSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3Qoc2l0ZS5QQ0JzID0gYygiPiA2N3RoIHBlcmNlbnRpbGUiID0gIiNFNzhBQzMiLCAiMzMgLSA2NyBwZXJjZW50aWxlIiA9ICIjRkM4RDYyIiwgIjwgMzN0aCBwZXJjZW50aWxlIiA9ICIjNjZDMkE1IiwgIk5vbi1BT0MiID0gIiNBNkQ4NTQiLCAgIlJlZiIgPSAiIzhEQTBDQiIpKSksIAogICAgICAgICAgYm90dG9tX2Fubm90YXRpb24gPSBIZWF0bWFwQW5ub3RhdGlvbihkZiA9IGhlYXRtYXAuYW5ubzJbLGMoMSwzOjcpXSwgY29sID0gbGlzdChQQ0IuY29lZiA9IGNvbF9mdW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgU2lnbmFsIFRyYW5zZHVjdGlvbmAgPSBjKCJkb3duIiA9ICJibHVlIiwgIk5BIiA9ICJ3aGl0ZSIsICJ1cCIgPSAicmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBNZXRhYm9saXNtIG9mIGxpcGlkc2AgPSBjKCJkb3duIiA9ICJibHVlIiwgInVwIiA9ICJyZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYEltbXVuZSBTeXN0ZW1gID0gYygiZG93biIgPSAiYmx1ZSIsICJ1cCIgPSAicmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBDYXJkaWFjIGNvbmR1Y3Rpb25gID0gYygiZG93biIgPSAiYmx1ZSIsICJ1cCIgPSAicmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBIZW1vc3Rhc2lzYCA9IGMoImRvd24iID0gImJsdWUiLCAidXAiID0gInJlZCIpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfbGVnZW5kID0gYyhUUlVFLEZBTFNFLCBGQUxTRSwgRkFMU0UsIEZBTFNFLCBGQUxTRSksIGFubm90YXRpb25fbmFtZV9ncCA9IGdwYXIoZm9udHNpemUgPSAxMCkpKQoKZHJhdyhQQ0JzX2xhc3NvNTBfaGVhdG1hcC52NCwgYW5ub3RhdGlvbl9sZWdlbmRfbGlzdCA9IGxnZCkKIyBzYXZlUkRTKFBDQnNfbGFzc281MF9oZWF0bWFwLnY0LCAiUENCc19sb2dGQ19oZWF0bWFwLnJkcyIpCgojIHRpZmYoIn4vRG9jdW1lbnRzL3dvcmsvVHN3YWxsb3dfY2hlbV9HTFJJX3VwZGF0ZS9HTFJJX01TMl9hbGwvQWxsX2ZpZ3VyZXMvRmlndXJlNWFfUENCc19sb2dGQ19oZWF0bWFwLnRpZmYiLCB1bml0cz0iaW4iLCB3aWR0aD05LCBoZWlnaHQ9MTAsIHJlcz0zMDAsIGNvbXByZXNzaW9uID0gJ2x6dycpICMjIGFzcGVjdCByYXRpbyAxLjYKIyAjIyBhZGQgbGVnZW5kIGFubm90YXRpb24gCiMgZHJhdyhQQ0JzX2xhc3NvNTBfaGVhdG1hcC52NCwgYW5ub3RhdGlvbl9sZWdlbmRfbGlzdCA9IGxnZCkKIyBkZXYub2ZmKCkKYGBgCgojIyMjICBQQ0JzIGxhc3NvIHNlbGVjdGVkIGdlbmVzIGhlYXRtYXAgZGVhdGlsIGZ1bmN0aW9ucyBhbmQgcGF0aHdheXMKCmBgYHtyIFBDQnMgbGFzc28gc2VsZWN0ZWQgZ2VuZXMgaGVhdG1hcCBkZWF0aWwgZnVuY3Rpb25zIGFuZCBwYXRod2F5cywgZWNobz1GQUxTRSwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9MTB9ClBDQnNfbGFzc29fYnlfc2l0ZV81MF9mdW5jdGlvbmFsX2RhdGEgPC0gcmVhZFJEUygiUENCc19sYXNzb19ieV9zaXRlXzUwX2Z1bmN0aW9uYWxfZGF0YS5yZHMiKQpHT0JQX2RhdGEubWF0cml4IDwtIFBDQnNfbGFzc29fYnlfc2l0ZV81MF9mdW5jdGlvbmFsX2RhdGEkaGVhdG1hcC5icC5wbG90LmRhdGEKUmVhY3RvbWVfZGF0YS5tYXRyaXggPC0gUENCc19sYXNzb19ieV9zaXRlXzUwX2Z1bmN0aW9uYWxfZGF0YSRoZWF0bWFwLnJlYWN0b21lLnBsb3QuZGF0YQogIAojIyBmaWd1cmUgc3VwcGxlbWVudGFyeQojIyBjb21iaW5lIGdvIGFuZCByZWFjdG9tZSAKR09CUF9kYXRhLm1hdHJpeC50PSB0KEdPQlBfZGF0YS5tYXRyaXgpCiMjIHJlbW92ZSAiYWxsIgpHT0JQX2RhdGEubWF0cml4LnQyID0gR09CUF9kYXRhLm1hdHJpeC50WywtMzFdCiMjIGNvcnJlY3Qgbm90IGVxdWFsIHRvIDAgPSAxIApHT0JQX2RhdGEubWF0cml4LnQyW0dPQlBfZGF0YS5tYXRyaXgudDIgIT0wXSA9IDEKCgpSZWFjdG9tZV9kYXRhLm1hdHJpeC50ID0gdChSZWFjdG9tZV9kYXRhLm1hdHJpeFssY29sbmFtZXMoR09CUF9kYXRhLm1hdHJpeCldKQpSZWFjdG9tZV9kYXRhLm1hdHJpeC50MiA9IFJlYWN0b21lX2RhdGEubWF0cml4LnQKUmVhY3RvbWVfZGF0YS5tYXRyaXgudDIgPSBSZWFjdG9tZV9kYXRhLm1hdHJpeC50MlssLTMxXQpSZWFjdG9tZV9kYXRhLm1hdHJpeC50MltSZWFjdG9tZV9kYXRhLm1hdHJpeC50MiAhPSAwXSA9IDEKCiMjIGhlYXRtYXAgYW5ub3RhdGlvbgpoYSA9IHJvd0Fubm90YXRpb24oUENCLmNvZWYgPSBhbm5vX251bWVyaWMocm91bmQoYXMubnVtZXJpYyhHT0JQX2RhdGEubWF0cml4LnRbLCJhbGwiXSksIGRpZ2l0cyA9MiksIGJnX2dwID0gZ3BhcihmaWxsID0gYygiZ3JlZW4iLCAicmVkIikpKSAsIGFubm90YXRpb25fbmFtZV9yb3QgPSAwKQoKCnJvd19kZW5kID0gZGVuZHNvcnQoaGNsdXN0KGRpc3QoR09CUF9kYXRhLm1hdHJpeC50WywtMzFdKSkpCgojIyBHTyB0ZXJtcyAKSDEgPSBIZWF0bWFwKEdPQlBfZGF0YS5tYXRyaXgudDIsIG5hbWUgPSAiVG90YWwgUENCcyAtIFxuIExhc3NvIHJlZ3Jlc3Npb24gXG4gYXZlcmFnZSBjb2VmLiIsIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSA5KSwgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDExKSwgY29sdW1uX3RpdGxlID0gIkdPOkJQIiwgCiAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSByb3dfZGVuZCwgc2hvd19yb3dfZGVuZCA9IEZBTFNFLCBjb2wgPSBjKCJ3aGl0ZSIsICJibGFjayIpLCByZWN0X2dwID0gZ3Bhcihjb2wgPSAiZ3JheSIsIGx3ZCA9IDEpLHNob3dfaGVhdG1hcF9sZWdlbmQgPSBGQUxTRSkKIyMgb3JkZXIgYnkgY29lZgpIMS52MiA9IEhlYXRtYXAoR09CUF9kYXRhLm1hdHJpeC50MiwgbmFtZSA9ICJUb3RhbCBQQ0JzIC0gXG4gTGFzc28gcmVncmVzc2lvbiBcbiBhdmVyYWdlIGNvZWYuIiwgY29sdW1uX25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDkpLCByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTEpLCBjb2x1bW5fdGl0bGUgPSAiR086QlAiLCAKICAgICAgICAgICAgICAgIHJvd19vcmRlciA9IG9yZGVyKGFzLm51bWVyaWMoR09CUF9kYXRhLm1hdHJpeC50WywiYWxsIl0pKSwgY29sID0gYygid2hpdGUiLCAiYmxhY2siKSwgcmVjdF9ncCA9IGdwYXIoY29sID0gImdyYXkiLCBsd2QgPSAxKSwgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IEZBTFNFKQojIyBSZWFjdG9tZSBwYXRod2F5cyAgICAgICAgICAgICAgICAgCkgyID0gSGVhdG1hcChSZWFjdG9tZV9kYXRhLm1hdHJpeC50Miwgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IEZBTFNFLCBjb2x1bW5fbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gOSksIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMSksIGNvbHVtbl90aXRsZSA9ICJSZWFjdG9tZSIsIHNob3dfcm93X2RlbmQgPSBGQUxTRSwgcmVjdF9ncCA9IGdwYXIoY29sID0gImdyYXkiLCBsd2QgPSAxKSwgY29sID0gYygid2hpdGUiLCAiYmxhY2siKSwgcmlnaHRfYW5ub3RhdGlvbiA9IGhhKQoKIyBQQ0JzX0JQX1JlYWN0b21lX2hlYXRtYXAgPSBIMSArIEgyClBDQnNfQlBfUmVhY3RvbWVfaGVhdG1hcC52MiA9IEgxLnYyICsgSDIKIyBzYXZlUkRTKFBDQnNfQlBfUmVhY3RvbWVfaGVhdG1hcC52MiwgIlBDQnNfQlBfUmVhY3RvbWVfaGVhdG1hcC5yZHMiKQoKIyMgZmlndXJlNWF2MgojIGRyYXcoSDErSDIsIHBhZGRpbmcgPSB1bml0KGMoMjQsIDUsIDIsIDIpLCAibW0iKSwgKSAjIyBzZWUgcmlnaHQgaGVhdG1hcCBpbiBmb2xsb3dpbmcKCiMjIGZpZ3VyZTVhdjMKZHJhdyhIMS52MitIMiwgcGFkZGluZyA9IHVuaXQoYygyNCwgNSwgMiwgMiksICJtbSIpLCApICMjIHNlZSByaWdodCBoZWF0bWFwIGluIGZvbGxvd2luZwoKUmVhY3RvbWUuUENCcy5zdWJzZXQgPSBjKCJTaWduYWwgVHJhbnNkdWN0aW9uIiAsICJNZXRhYm9saXNtIG9mIGxpcGlkcyIgLCAiSW1tdW5lIFN5c3RlbSIgLCAiQ2FyZGlhYyBjb25kdWN0aW9uIiwgIkhlbW9zdGFzaXMiKQoKYGBgCgojIyMjIFBDQiBjb3JyZWxhdGlvbiBiZXR3ZWVuIGxvZ0ZDIGxvZ0NQTSBhbmQgbW9kZWwgY29lZgoKYGBge3IgY29ycmVsYXRpb24gYmV0d2VlbiBsb2dGQyBsb2dDUE0gYW5kIG1vZGVsIGNvZWYsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTYsIGVjaG89RkFMU0V9CiMjIGF2ZXJhZ2UgbG9nRkMgZm9yIGxhc3NvIHNlbGVjdGVkIGdlbmVzIGJ5IGNhbGN1bGF0aW5nICBpbmRpdmlkdWFscyB3aXRoIHRvcCBQQ0JzIHRlcnRpbGUgYmFzZWQgb24gYWJvdmUgY29sX2RlbmQKUENCc19nZW5lX2xvZ0ZDX2FicyA8LSBhYnMocm93U3Vtcyhjb3VudHMuY29uLnN1YnNldC5tYXRyaXguY29ycmVjdGVkWyxjb2xfZGVuZCRsYWJlbHNbY29sX2RlbmQkb3JkZXIgPiAxMzBdXSkvNjYpICMjIG9ubHkgY2hvb3NlIHRob3NlIGJpcmRzIHdpdGggPiAyLzMgbG9nRkMgcmVsYXRpdmUgdG8gc3RhciBsYWtlIApQQ0JzX2dlbmVfbG9nQ1BNIDwtIHJvd01lYW5zKGNvdW50cy5jb24uc3Vic2V0Lm1hdHJpeCkKUENCc19nZW5lX2xhc3NvX2NvZWYgPC0gaGVhdG1hcC5hbm5vJFBDQi5jb2VmCm5hbWVzKFBDQnNfZ2VuZV9sYXNzb19jb2VmKSA8LSByb3cubmFtZXMoaGVhdG1hcC5hbm5vKQpQQ0JzX2dlbmVfaW5fbGFzc21vZGVsLm1hdHJpeCA8LSBkYXRhLmZyYW1lKGxvZ0ZDX2FicyA9IFBDQnNfZ2VuZV9sb2dGQ19hYnMsIGxvZ19jb3VudHMgPSBQQ0JzX2dlbmVfbG9nQ1BNLCBQQ0JzX2NvZWZfYWJzID0gYWJzKFBDQnNfZ2VuZV9sYXNzb19jb2VmKSkKUENCc19nZW5lX2luX2xhc3Ntb2RlbC5tYXRyaXggJT4lIGdncGFpcnModXBwZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJjb3IiLCBtZXRob2QgPSAicGVhcnNvbiIpKSkrdGhlbWVfYncoKQoKUENCX2xvZ0ZDX2NvcnJlbGF0aW9uX3Bsb3QgPC0gUENCc19nZW5lX2luX2xhc3Ntb2RlbC5tYXRyaXggJT4lIGdncGFpcnModXBwZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJjb3IiLCBtZXRob2QgPSAicGVhcnNvbiIpKSkrdGhlbWVfYncoKQojIHNhdmVSRFMoUENCX2xvZ0ZDX2NvcnJlbGF0aW9uX3Bsb3QsIkZpZ3VyZVM0YV9QQ0JfbG9nRkNfY29ycmVsYXRpb25fcGxvdC5yZHMiKQojIAojIAojIAojIHRpZmYoIn4vRG9jdW1lbnRzL3dvcmsvVHN3YWxsb3dfY2hlbV9HTFJJX3VwZGF0ZS9HTFJJX01TMl9hbGwvQWxsX2ZpZ3VyZXMvRmlndXJlUzRhX1BDQnNfY291bnRzX2xvZ0ZDX2NvZWZfY29ycmVsYXRpb24udGlmZiIsIHVuaXRzPSJpbiIsIHdpZHRoPTUsIGhlaWdodD01LCByZXM9MzAwLCBjb21wcmVzc2lvbiA9ICdsencnKSAjIyBhc3BlY3QgcmF0aW8gMS42CiMgUENCc19nZW5lX2luX2xhc3Ntb2RlbC5tYXRyaXggJT4lIGdncGFpcnModXBwZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJjb3IiLCBtZXRob2QgPSAicGVhcnNvbiIpKSkrdGhlbWVfYncoKQojIGRldi5vZmYoKQpgYGAKPC9icj4KCiMjIDIuIERldGVybWluZSBsYXNzbyByZWdyZXNzaW9uIG1vZGVscyB0byBwcmVkaWN0IFBBSCBjb25jZW50cmF0aW9ucyAgIAo8L2JyPgoKKlN0YXJ0IGZyb20gZmlyc3Qgcm91bmQgdG9wIGdlbmVzICgxMTApIGdlbmVzIGFuZCByZWZpbmUgbGFzc28gcmVncmVzc2lvbiBhbmFseXNpcyBmb3IgdGhlIHNlY29uZCByb3VuZCogIAo8L2JyPgoKIyMjIExvYWQgZGF0YSAgCjwvYnI+CgpgR0xSSV9jb2xkYXRhYCAgCjogY2hlbWlzdHJ5IGRhdGEgKCJEaW94aW4iICJNdWx0aVJlZHNpZHVlUGVzdCIgIlBBSHMiICJQQkRFcyIgIkxSUENCcyIgIkhSUENCcyIgIlBlc3RpY2lkZXMiICJQRkNzIiAiUFBDUHMiKSAgCgpgZGF0YS5jb250YW1pbmFudHMubWFqb3JTdWJzZXQuZ2VvLmJ5c2l0ZWAKOiBnZW9tZXRyaWMgbWVhbiBvZiBlYWNoIGNvbnRhbWluYW50IGtleSBieSBzaXRlICAKCmBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXRgCjogb25seSBpbmNsdWRlcyB0aGUgbmVzdGxpbmdzIHdpdGggZ2Vub21pYyBpbmZvcm1hdGlvbiBhbmQgcmVtb3ZlcyBQQUhzIGRhdGEgIAoKYElELmNvbnZlcnRgCjogZ2VuZSBJRCBjb252ZXJ0aW9uIHRhYmxlIGJldHdlZW4gdHJlZSBzd2FsbG93IGdlbmUgcHNldWRvIG5hbWUgYW5kIGNoaWNrZW4gZ2VuZSBuYW1lICAKCmBzaXRlTUFQLmFsbGAKOiBzaXRlIElEIGFuZCBwcm9wZXJzaXRlIG5hbWUgY29udmVydGlvbiBUYWJsZSAgCgpgZGRzLmJvdGhTZXguYWRqdXN0ZWQgb3IgY291bnRzLnRzd2FsbG93YAo6IG5vcm1hbGl6ZWQgY291bnQgbWF0cml4IGZvciBhbGwgbmVzdGxpbmdzLCBhZGp1c3RlZCBmb3IgYmF0Y2ggZGlmZmVyZW5jZSAgCgpgQ29udC5tYWpvci5saXN0YAo6IG1ham9yIGNvbnRhbWluYW50cyAoIk5vbmFjaGxvciwgY2lzLV9DIiwgIkhlcHRhY2hsb3IgRXBveGlkZV9DIiwgICAiTm9uYWNobG9yLCB0cmFucy1fQyIsICAiUEZEQV9DIiwgIkNobG9yZGFuZSwgb3h5LV9DIiwgIlRvdGFsIFBhcmVudCBQQUhzIiwgIlRvdGFsIFBCREUiLCAiVG90YWwgUEFIcyIsIlRvdGFsIFBDQnMiLCJUb3RhbF9QRkNzIikKCmBjb3VudHMudHN3YWxsb3cubm9ybWAKOiB2c3QgdHJhbnNmb3JtZWQgbm9ybWFsaXplZCBjb3VudHMgICAKCmB0b3BnZW5lLmxpc3RgCjogMTEwIHRvcCBnZW5lcyBkZXRlcm1pbmVkIGZyb20gdGhlIGZpcnN0IHJvdW5kIG9mIGxhc3NvIHJlZ3Jlc3Npb24gYW5hbHlzaXMuIERldGFpbCBjYW4gYmUgZm91bmQgW2hlcmVdKEdMUklfTVMyX0ZpcnN0Um91bmRfbGFzc28ubmIuaHRtbCkgIAoKCmBgYHtyIExvYWQgUEFIcyBkYXRhLCBpbmNsdWRlPUZBTFNFfQojIyAybmQgcm91bmQgcmVncmVzc2lvbiB3aXRoIFBBSHMgYnkgc2l0ZQojIyBsYXNzbyByZWdyZXNzaW9uIGJ5IHNpdGUgYXZlcmFnZSBnZW9NZWFuIC8gY29udGFtaW5hbnRzIAojIyBEZXRlcm1pbmUgdG9wMTAwMCBnZW5lcyBmcm9tIGVhY2ggY29udGFtaW5hbnRzCiMgbG9hZCBkYXRhIApHTFJJX2NvbGRhdGEgPC0gcmVhZFJEUygiR0xSSV9jb2xkYXRhX3RvMjAyMC5yZHMiKQojIERldGVybWluZSBtaW4gY29udGFtaW4gdmFsdWUgZm9yIGVhY2ggS2V5CkdMUklfY29sZGF0YSA8LSBHTFJJX2NvbGRhdGEkY29udGFtaW5hbnRzICU+JSBmaWx0ZXIoIWlzLm5hKFZhbHVlKSkKIyBMb2FkIEdlb01lYW4KZGF0YS5jb250YW1pbmFudHMubWFqb3JTdWJzZXQuZ2VvLmJ5c2l0ZSA8LSByZWFkUkRTKCJkYXRhX2NvbnRhbWluYW50c19tYWpvclN1YnNldF9nZW9fYnlzaXRlLnJkcyIpCmRhdGEuY29udGFtaW5hbnRzLm1ham9yU3Vic2V0Lmdlby5ieXNpdGUkTWVyZ2VTaXRlW2RhdGEuY29udGFtaW5hbnRzLm1ham9yU3Vic2V0Lmdlby5ieXNpdGUkTWVyZ2VTaXRlID09ICJyZWYiXSA8LSAiU0wiCiMjIGNvbWJpbmUgYWxsIGR1cGxpY2F0ZWQgaXRlbXMgYnkgTWVyZ2VTaXRlCmRhdGEuY29udGFtaW5hbnRzLm1ham9yU3Vic2V0Lmdlby5ieXNpdGUgPC0gZGF0YS5jb250YW1pbmFudHMubWFqb3JTdWJzZXQuZ2VvLmJ5c2l0ZSAlPiUgZ3JvdXBfYnkocHJvcGVyX3NpdGUyLE1lcmdlU2l0ZSxLZXkpICU+JSBzdW1tYXJpc2UodmFsdWUuZ2VvTWVhbjIgPSBtZWFuKHZhbHVlLmdlb01lYW4pKSAlPiUgdW5ncm91cCgpCmNvbG5hbWVzKGRhdGEuY29udGFtaW5hbnRzLm1ham9yU3Vic2V0Lmdlby5ieXNpdGUpW2NvbG5hbWVzKGRhdGEuY29udGFtaW5hbnRzLm1ham9yU3Vic2V0Lmdlby5ieXNpdGUpID09ICJ2YWx1ZS5nZW9NZWFuMiJdIDwtICJ2YWx1ZS5nZW9NZWFuIiAjIyBjaGFuZ2UgdGhlIG5hbWUgYmFjawojIExvYWQgR2Vub21pYyBJRCBjb252ZXJ0IHRhYmxlIApJRC5jb252ZXJ0IDwtIHJlYWRSRFMoIklEbWFwX2ZpbmFsLnJkcyIpICMjIGdlbmUgSUQgQ29udmVydCB0YWJsZQojIExvYWQgc2l0ZSBjb2xkYXRhIG1hcApzaXRlTUFQLmFsbCA8LSBhc190aWJibGUocmVhZFJEUygic2l0ZU1BUF9hbGwucmRzIikpICMjIHNpdGUgSUQgY29udmVydCBUYWJsZQpzaXRlTUFQLmFsbCRTaXRlSURbc2l0ZU1BUC5hbGwkcHJvcGVyc2l0ZSA9PSAiU3Rhckxha2UiXSA8LSAiU0wiCiMgTG9hZCBnZW5lIGV4cHJlc3Npb24gZGF0YQpkZHMuYm90aFNleC5hZGp1c3RlZCA8LSByZWFkUkRTKCJkZHMuYm90aFNleC5hZGp1c3RlZC5vdXRsaWVyUk0ucmRzIikgIyMgbm9ybWFsaXplZCBjb3VudCBtYXRyaXgKZGRzLmJvdGhTZXguYWRqdXN0ZWQkcHJvcGVyc2l0ZTIgPC0gYXMuY2hhcmFjdGVyKHNpdGVNQVAuYWxsJHByb3BlcnNpdGVbbWF0Y2goZGRzLmJvdGhTZXguYWRqdXN0ZWQkc2l0ZSwgc2l0ZU1BUC5hbGwkU2l0ZUlEKV0pIyBhZGQgcHJvcGVyc2l0ZTIgYXMgc2l0ZSBmdWxsIG5hbWUgCmNvdW50cy50c3dhbGxvdyA8LSBhc3NheShkZHMuYm90aFNleC5hZGp1c3RlZCkgIyBjb3VudCBtYXRyaXgKIyBtYWpvciBjb250YW1pbmFudCBmb3IgY29tcGFyaXNvbgojIENvbnQubWFqb3IubGlzdCA8LSBjKCJOb25hY2hsb3IsIGNpcy1fQyIsICJIZXB0YWNobG9yIEVwb3hpZGVfQyIsICAgIk5vbmFjaGxvciwgdHJhbnMtX0MiLCAgIlBGREFfQyIsICJDaGxvcmRhbmUsIG94eS1fQyIsICJUb3RhbCBQYXJlbnQgUEFIcyIsICJUb3RhbCBQQkRFIiwgIlRvdGFsIFBBSHMiLCJUb3RhbCBQQ0JzIiwiVG90YWxfUEZDcyIpCmNvdW50cy50c3dhbGxvdy5ub3JtIDwtIHZzdChkZHMuYm90aFNleC5hZGp1c3RlZCkKY291bnRzLnRzd2FsbG93Lm5vcm0gPC0gYXNzYXkoY291bnRzLnRzd2FsbG93Lm5vcm0pCnRvcGdlbmUubGlzdCA9IHJlYWRSRFMoImxhc3NvX3RvcGdlbmVfYnlTaXRlX1BBSHMucmRzIikgIyMgdG9wIDExMCBnZW5lcyAKCmBgYAoKIyMjIExvYWQgc2VsZi1kZXRlcm1pbmVkIGZ1bmN0aW9ucyAKPC9icj4KCgotIGBsYXNzby5ieS5zaXRlLnRvcDEwMChDb24sQ29uX2luZCxpLE1pbl9MLHMpYCA6IExhc3NvIGxlYXZlIG9uZSAoc2l0ZSkgb3V0IGNyb3NzLXZhbGlkYWl0b24gbW9kZWwgdHJhaW5pbmcsIE1pbl9MIGZvciB0aGUgbG93ZXN0IHRoZSBsYW1kYSBjdiBnb2VzIDEwXigtMiwtMywtNCk7IHM6IGxhbWRhLnNlcXVlbmNlOiAxKSAxc2UgMikgbWluIDMpIGxvZyBtZWRpYW4gIAoKLSBgUnVuX2xhc3NvX2J5X3NpdGUudG9wMTAwKG4sQ29uLENvbl9pbmQsTl90cmFpbixNaW5fTCxzKWAgOiBSdW4gYGxhc3NvLmJ5LnNpdGUudG9wMTAwYCB3aXRoIG4gcnVucyB0aHJvdWdob3V0IGFsbCAyOCBzaXRlcyA9IDI4biAgIAoKLSBgZXZhbHVhdGlvbl9mdW5jaXRvbi5ieXNpdGUuUEFIYCA6IGdlbmVyYXRlIHBlcmZvcm1hbmNlIG1ldHJpY3MgCgoKYGBge3IgTG9hZCBzZWxmLWRldGVybWluZWQgZnVuY3Rpb25zLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQoKIyBOOiBOIHJlcGVhdCBsZWF2ZSBvbmUgKHNpdGUpIG91dCB2YWxpZGF0aW9uOyAgTWluX0wgZm9yIHRoZSBsb3dlc3QgdGhlIGxhbWRhIGN2IGdvZXMgMTBeKC0yLC0zLC00KTsgczogbGFtZGEuc2VxdWVuY2U6IDEpIDFzZSAyKSBtaW4gMykgbG9nIG1lZGlhbiAKbGFzc28uYnkuc2l0ZS50b3AxMDAgPC0gZnVuY3Rpb24oQ29uLENvbl9pbmQsaSxNaW5fTCxzKSB7CiAgQnlTaXRlUmVzdWx0LndpdGhsYW1kYVAgPC0ge30gIyMgcmVmcmVzaAogIHZhcmlhYmxlcy5hbGwgPC0gbGlzdCgpCiAgcHJlZGljdC5zaXRlLnRlc3QuYWxsIDwtIGxpc3QoKQogIG1hdGNoMSA8LSBzdHJfc3ViKGNvbG5hbWVzKGNvdW50cy50c3dhbGxvdy5ub3JtKSwzLDQpICVpbiUgY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiRNZXJnZVNpdGUKICBjb3VudHMuY29uICA8LSAgY291bnRzLnRzd2FsbG93Lm5vcm1bLG1hdGNoMV0KICBjb3VudHMuY29uLmJhdGNoIDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoZGRzLmJvdGhTZXguYWRqdXN0ZWQkYmF0Y2gyKVttYXRjaDFdKQogIGNvdW50cy5jb24uc2V4IDwtIGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIoZGRzLmJvdGhTZXguYWRqdXN0ZWQkc2V4KVttYXRjaDFdKQogIGNvdW50cy5jb24uY29udGFtaW4gPC0gbG9nMTAoY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiR2YWx1ZS5nZW9NZWFuW21hdGNoKHN0cl9zdWIoY29sbmFtZXMoY291bnRzLmNvbiksMyw0KSwgY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiRNZXJnZVNpdGUpXSkKICAjIyBCdWlsZGluZyBtYXRyaXggCiAgaWYgKGxlbmd0aChsZXZlbHMoY291bnRzLmNvbi5iYXRjaCkpID4gMSkgewogICAgY291bnRzLmNvbi50IDwtIGFzLmRhdGEuZnJhbWUodChjb3VudHMuY29uW3RvcGdlbmUubGlzdCxdKSkKICAgIGNvdW50cy5jb24udCA8LSBjYmluZChjb3VudHMuY29uLnQsIGJhdGNoID0gY291bnRzLmNvbi5iYXRjaCwgc2V4ID0gY291bnRzLmNvbi5zZXgsIGNvbnRhbWluYW50ID0gY291bnRzLmNvbi5jb250YW1pbikKICAgIG0gPC0gbW9kZWwubWF0cml4KGNvbnRhbWluYW50IH4gYmF0Y2ggKy4sIGNvdW50cy5jb24udCkKICAgIG0gPC0gbVssLTFdIH0gZWxzZSB7CiAgICAgIGNvdW50cy5jb24udCA8LSBhcy5kYXRhLmZyYW1lKHQoY291bnRzLmNvblt0b3BnZW5lLmxpc3QsXSkpCiAgICAgIGNvdW50cy5jb24udCA8LSBjYmluZChjb3VudHMuY29uLnQsIHNleCA9IGNvdW50cy5jb24uc2V4LCBjb250YW1pbmFudCA9IGNvdW50cy5jb24uY29udGFtaW4pCiAgICAgIG0gPC0gbW9kZWwubWF0cml4KGNvbnRhbWluYW50IH4gLiwgY291bnRzLmNvbi50KQogICAgICBtIDwtIG1bLC0xXSB9ICAKICAKICBzaXRlLmFsbCA8LSBzdHJfc3ViKGNvbG5hbWVzKGNvdW50cy5jb24pLDMsNCkKICAjIHByaW50KGxlbmd0aCh1bmlxdWUoc2l0ZS5hbGwpKSkKICBzaXRlIDwtIHVuaXF1ZShzdHJfc3ViKGNvbG5hbWVzKGNvdW50cy5jb24pLDMsNCkpCiAgc2l0ZS50ZXN0IDwtIHNpdGVbaV0KICBtYXRjaDEudGVzdCA8LSBzaXRlLmFsbCA9PSBzaXRlLnRlc3QKICB0cmFpbmluZyA9IHNhbXBsZSh3aGljaCghbWF0Y2gxLnRlc3QpLCBzaXplID0gIHJvdW5kKHN1bSghbWF0Y2gxLnRlc3QpKjAuOSkpCiAgbC4xc2UuYWxsID0ge30KICBsLm1pbi5hbGwgPSB7fQogIGwubWVkaWFuLmFsbCA9IHt9CiAgZm9yIChqIGluIDE6MjApIHsKICBsLjFzZSA8LSB7fSAjIyByZXBlYXQgMTAgdGltZXMgdG8gYXZvaWQgb3V0bGllciBpbiBjcm9zcyB2YWxpZGF0aW9uIAogIGwubWluIDwtIHt9CiAgY3ZmaXQgPC0gY3YuZ2xtbmV0KHg9bVt0cmFpbmluZyxdLCB5PWNvdW50cy5jb24udCRjb250YW1pbmFudFt0cmFpbmluZ10sIG5mb2xkcyA9IDEwLCBhbHBoYSA9IDEsIGxhbWJkYSA9IDEwXnNlcSgwLE1pbl9MLGxlbmd0aD02MDApKQogIGwuMXNlIDwtIGN2Zml0JGxhbWJkYS4xc2UKICBsLm1pbiA8LSBjdmZpdCRsYW1iZGEubWluCiAgbC5tZWRpYW4gPC0gZXhwKG1lYW4oYyhsb2cobC4xc2UpLGxvZyhsLm1pbikpKSkKICBsLjFzZS5hbGwgPC0gYyhsLjFzZS5hbGwsbC4xc2UpCiAgbC5taW4uYWxsIDwtIGMobC5taW4uYWxsLGwubWluKQogIGwubWVkaWFuLmFsbCA8LSBjKGwubWVkaWFuLmFsbCxsLm1lZGlhbikKICB9CiAgbC4xc2UgPSBtZWRpYW4obC4xc2UuYWxsKQogIGwubWluID0gbWVkaWFuKGwubWluLmFsbCkKICBsLm1lZGlhbiA9IG1lZGlhbihsLm1lZGlhbi5hbGwpCiAgbGFtZGEuc2VxdWVuY2UgPC0gYyhsLjFzZSxsLm1pbixsLm1lZGlhbikgIAogIG5hbWVzKGxhbWRhLnNlcXVlbmNlKSA8LSBjKCIxc2UiLCJtaW4iLCJtZWRpYW4iKQogIGZpdCA8LSBnbG1uZXQoeD1tW3RyYWluaW5nLF0sIHk9Y291bnRzLmNvbi50JGNvbnRhbWluYW50W3RyYWluaW5nXSwgYWxwaGEgPSAxLCBsYW1iZGEgPSAxMF5zZXEoMCxNaW5fTCxsZW5ndGg9NjAwKSkKICBlcnJvci5yZXN1bHQgPC0gYXNzZXNzLmdsbW5ldChmaXQsIG5ld3ggPSBtW21hdGNoMS50ZXN0LF0sIG5ld3kgPSBjb3VudHMuY29uLnQkY29udGFtaW5hbnRbbWF0Y2gxLnRlc3RdLCBzID0gbGFtZGEuc2VxdWVuY2Vbc10pCiAgZXJyb3IudGVybSA8LSBjKGVycm9yLnJlc3VsdCRtc2UsIGVycm9yLnJlc3VsdCRtYWUpCiAgbmFtZXMoZXJyb3IudGVybSkgPC0gYygibXNlIiwibWFlIikKICB2YXJpYWJsZXMgPC0gY29lZihmaXQsIHMgPSBsYW1kYS5zZXF1ZW5jZVtzXSwgZ2FtbWEgPSAxKSAgCiAgdmFyaWFibGVzIDwtIHZhcmlhYmxlc1ssMV1bISh2YXJpYWJsZXNbLDFdID09IDApXQogIHZhcmlhYmxlcyA8LSB2YXJpYWJsZXNbLTFdCiAgcHNldWRvX1IyIDwtIGZpdCRkZXYucmF0aW9bd2hpY2goc29ydChjKGxhbWRhLnNlcXVlbmNlW3NdLDEwXnNlcSgwLE1pbl9MLGxlbmd0aD02MDApKSxkZWNyZWFzaW5nID0gVFJVRSkgPT0gbGFtZGEuc2VxdWVuY2Vbc10pWzFdIC0xXQogIHByZWRpY3Quc2l0ZSA8LSBwcmVkaWN0KGZpdCwgbmV3eCA9IG1bbWF0Y2gxLnRlc3QsXSwgcyA9IGxhbWRhLnNlcXVlbmNlW3NdKQogIHJlc3VsdCA9IGxpc3QobGFtZGEuc2VxdWVuY2UsZXJyb3IudGVybSx2YXJpYWJsZXMscHNldWRvX1IyLHByZWRpY3Quc2l0ZSkKICBuYW1lcyhyZXN1bHQpIDwtIGMoImxhbWRhLnNlcXVlbmNlIiwibXNlX21hZSIsInZhcmlhYmxlcyIsInBzZXVkb19SMiIsInByZWRpY3Quc2l0ZSIpCiAgcmV0dXJuKHJlc3VsdCkKfQpSdW5fbGFzc29fYnlfc2l0ZS50b3AxMDAgPC0gZnVuY3Rpb24obixDb24sQ29uX2luZCxOX3RyYWluLE1pbl9MLHMpIHsKICBUSU1FMSA8LSBTeXMudGltZSgpCiAgbGFzc29zaXRlUmVzdWx0LnJ1biA8LSBmb3JlYWNoKGkgPSByZXAoMToyOCxlYWNoPSBuKSAsIC5wYWNrYWdlcyA9IGMoInN0cmluZ3IiLCAiZHBseXIiLCJnbG1uZXQiKSwgLmV4cG9ydCA9IGMoImxhc3NvLmJ5LnNpdGUudG9wMTAwIiwgImNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIiLCJ0b3BnZW5lLmxpc3QiLCJjb3VudHMudHN3YWxsb3cubm9ybSIsImRkcy5ib3RoU2V4LmFkanVzdGVkIiwgImRhdGEuY29udGFtaW5hbnRzLm1ham9yU3Vic2V0Lmdlby5ieXNpdGUiKSkgJWRvcGFyJSB7CiAgICBsYXNzb19yZXN1bHQgPC0gVmVjdG9yaXplKGxhc3NvLmJ5LnNpdGUudG9wMTAwKShDb24sQ29uX2luZCxpLE1pbl9MLHMpCiAgICByZXR1cm4obGFzc29fcmVzdWx0KQogIH0KICBUSU1FMiA8LSBTeXMudGltZSgpCiAgcHJpbnQoVElNRTIgLSBUSU1FMSkKICByZXR1cm4obGFzc29zaXRlUmVzdWx0LnJ1bikKICAKfQpldmFsdWF0aW9uX2Z1bmNpdG9uLmJ5c2l0ZS5QQUggPC0gZnVuY3Rpb24oUmVzdWx0KSB7CiAgcmVzdWx0LmFsbCA8LSB7fQogIGV2YWx1YXRpb24ucmVzdWx0IDwtIHt9CiAgcHJlZGljdGlvbnMgPSBSZXN1bHQKICAjIyBnZW9NZWFuOiBvbmx5IHVzZSBvdmVybGFwcGVkIGdlbm9taWMgc2l0ZSBpbmRpdmlkdWxhcyB0cmFpbmluZyBzYW1wbGVzIHRvIGNhbGN1bGF0ZSBnZW9NZWFuCiAgcHJlZGljdGlvbnMyID0gZGF0YS5mcmFtZShzaXRlID0gbmFtZXMoUmVzdWx0KSwgcHJlZGljdGlvbnMsIGdlb01lYW4gPSBsb2cxMChjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyJHZhbHVlLmdlb01lYW5bbWF0Y2gobmFtZXMoUmVzdWx0KSxjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyJE1lcmdlU2l0ZSldKSwgYjMzID0gIGxvZzEwKHF1YW50aWxlKGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIkdmFsdWUuZ2VvTWVhbiwgcHJvYnMgPSAwLjMzLCBuYS5ybSA9IEZBTFNFLCBuYW1lcyA9IEZBTFNFKSksIHQzMyA9IGxvZzEwKHF1YW50aWxlKGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIkdmFsdWUuZ2VvTWVhbiwgcHJvYnMgPSAwLjY3LCBuYS5ybSA9IEZBTFNFLCBuYW1lcyA9IEZBTFNFKSkpCiAgcHJlZGljdGlvbnMzID0gY2JpbmQocHJlZGljdGlvbnMyLCBpcy50b3AzMyA9IHByZWRpY3Rpb25zMiRnZW9NZWFuID49IHByZWRpY3Rpb25zMiR0MzMsIGlzLmJvdHRvbTMzID0gcHJlZGljdGlvbnMyJGdlb01lYW4gPD0gcHJlZGljdGlvbnMyJGIzMywgaXMudG9wLnByZWRpY3QgPSBwcmVkaWN0aW9uczIkcHJlZGljdGlvbnMgPj0gcHJlZGljdGlvbnMyJHQzMywgaXMuYm90dG9tLnByZWRpY3QgPSBwcmVkaWN0aW9uczIkcHJlZGljdGlvbnMgPD0gcHJlZGljdGlvbnMyJGIzMykKICBwcmVkaWN0aW9ucy5hbGwgPSBwcmVkaWN0aW9uczMKICBwcmVkaWN0aW9ucy5hbGwkZGVsdGEgPSAocHJlZGljdGlvbnMuYWxsJHByZWRpY3Rpb25zIC0gcHJlZGljdGlvbnMuYWxsJGdlb01lYW4pXjIKICBNU0UgPSBzdW0ocHJlZGljdGlvbnMuYWxsJGRlbHRhKS9ucm93KHByZWRpY3Rpb25zLmFsbCkKICAKICBlcnJvci50b3AgPSAgc3VtKHByZWRpY3Rpb25zLmFsbCRpcy50b3AucHJlZGljdCAmIHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20zMykvIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QpCiAgCiAgZXJyb3IuYm90dG9tID0gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20ucHJlZGljdCAmIHByZWRpY3Rpb25zLmFsbCRpcy50b3AzMykvIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QpICMgMCAvIDAgCiAgCiAgZW9ycm9yLmJvdGggPSBzdW0oKHByZWRpY3Rpb25zLmFsbCRpcy50b3AucHJlZGljdCAmIHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20zMykgfCAocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLnRvcDMzKSkvIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QgfCBwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QpCiAgCiAgYWNjdXJhY3kudG9wID0gIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QgJiBwcmVkaWN0aW9ucy5hbGwkaXMudG9wMzMpLyBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLnRvcC5wcmVkaWN0KSAKICAKICBhY2N1cmFjeS5ib3R0b20gPSBzdW0ocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbTMzKS8gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20ucHJlZGljdCkgCiAgCiAgYWNjdXJhY3kuYm90aCA9IHN1bSgocHJlZGljdGlvbnMuYWxsJGlzLnRvcC5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLnRvcDMzKSAgfCAocHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbS5wcmVkaWN0ICYgcHJlZGljdGlvbnMuYWxsJGlzLmJvdHRvbTMzKSkvIHN1bShwcmVkaWN0aW9ucy5hbGwkaXMuYm90dG9tLnByZWRpY3QgfCBwcmVkaWN0aW9ucy5hbGwkaXMudG9wLnByZWRpY3QpIAogIAogIHRlc3RpbmdfcmF0aW8gPSBjKHRvcF9yYXRpbyA9IHN1bShwcmVkaWN0aW9ucy5hbGwkaXMudG9wMzMpL25yb3cocHJlZGljdGlvbnMuYWxsKSwgYm90dG9tX3JhdGlvID0gc3VtKHByZWRpY3Rpb25zLmFsbCRpcy5ib3R0b20zMykvbnJvdyhwcmVkaWN0aW9ucy5hbGwpKQogIAogIHBlcmZvcm1hbmNlLnJlc3VsdCA9IGMoZXJyb3IudG9wID0gZXJyb3IudG9wLGVycm9yLmJvdHRvbSA9IGVycm9yLmJvdHRvbSwgZXJyb3IuYm90aCA9IGVvcnJvci5ib3RoLCBhY2N1cmFjeS50b3AgPSBhY2N1cmFjeS50b3AgLGFjY3VyYWN5LmJvdHRvbSA9IGFjY3VyYWN5LmJvdHRvbSxhY2N1cmFjeS5ib3RoID0gYWNjdXJhY3kuYm90aCwgdGVzdGluZ19yYXRpbywgTVNFID0gTVNFKQogIGV2YWx1YXRpb24ucmVzdWx0ID0gbGlzdChwcmVkaWN0aW9ucy5ieS5zaXRlPXByZWRpY3Rpb25zLmFsbCwgcGVyZm9ybWFuY2UucmVzdWx0ID0gcGVyZm9ybWFuY2UucmVzdWx0KQogIHJldHVybihldmFsdWF0aW9uLnJlc3VsdCkKfQoKYGBgCjwvYnI+CgojIyMgQnVpbGlkIGxhc3NvIHJlZ3Jlc3Npb24gbW9kZWwgdG8gcHJlZGljdCBwb29sIHN0b21hY2ggUEFIIGNvbmNlbnRyYXRpb25zIGJ5IHNpdGUKKlJ1biBgUnVuX2xhc3NvX2J5X3NpdGUudG9wMTAwYCB3aXRoIDQwIHJlLXNhbXBsaW5nLCAxMTIwIGxlYXZlIG9uZSBvdXQgY3Jvc3MgdmFsaWRhdGlvbjsgbGFtZGEgMXNlCnRoZW4gZXZhbHVhdGUgdGhlIG1vZGVsIHBlcmZvcm1hbmNlIHVzaW5nIGBldmFsdWF0aW9uX2Z1bmNpdG9uLmJ5c2l0ZS5QQUhgKgoKYGBge3IgRGF0YSBpbnB1dCBmb3IgUnVuIGxhc3NvIGZvciBQQUhzIHByZWRpY3Rpb24sIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CkNvbj0iUEFIc19kYXRhIjtDb25faW5kPSJUb3RhbCBQQUhzIgojIyMgZWRpdCBzdWJzZXQgZm9yIG92ZXJsYXBwaWduIGdlbm9taWMgc2FtcGxlcyB3aXRoIGNvbnRhbWluYW50IGRhdGEgCmNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIgPC1kYXRhLmNvbnRhbWluYW50cy5tYWpvclN1YnNldC5nZW8uYnlzaXRlCiMjIyBJbmRpdmlkdWFsIGNoZW1pY2FscyAKY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiA8LSBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyICU+JSBmaWx0ZXIoS2V5ID09IENvbl9pbmQpCiMjIGNvbWJpbmUgYWxsIGR1cGxpY2F0ZWQgaXRlbXMgdXNpbmcgbWVhbiB2YWx1ZSBiZWNhc3VlIHRoZXkgaGF2ZSB0aGUgc2FtZSBNZXJnZVNpdGUgIyMgb25seSBSaXZlclJhaXNpbiB3aWxsIGJlIGFmZmVjdGVkIApjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyIDwtIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIgJT4lIGdyb3VwX2J5KE1lcmdlU2l0ZSwgS2V5KSAlPiUgc3VtbWFyaXNlKHZhbHVlLmdlb01lYW4yID0gbWVhbih2YWx1ZS5nZW9NZWFuKSkgJT4lIHVuZ3JvdXAoKQpjb2xuYW1lcyhjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyKVtjb2xuYW1lcyhjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyKSA9PSAidmFsdWUuZ2VvTWVhbjIiXSA8LSAidmFsdWUuZ2VvTWVhbiIgIyMgY2hhbmdlIHRoZSBuYW1lIGJhY2sKIyBwcmludChzdW0oZHVwbGljYXRlZChjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyJE1lcmdlU2l0ZSkpKSAjIyBjaGVjayBkdXBsaWNhdGlvbiBhZ2FpbgpgYGAKCgpgYGB7ciBSdW4gbGFzc28gZm9yIFBBSHMgcHJlZGljdGlvbiwgZXZhbD1GQUxTRX0KY2wgPC0gcGFyYWxsZWw6Om1ha2VDbHVzdGVyKDEyKQpkb1BhcmFsbGVsOjpyZWdpc3RlckRvUGFyYWxsZWwoY2wpClBBSHNfQ1ZfYnlTaXRlX3Jlc3VsdCA9IFJ1bl9sYXNzb19ieV9zaXRlLnRvcDEwMChuPTQwLENvbj1Db24sQ29uX2luZCA9IENvbl9pbmQsIE1pbl9MID0gLTIsIHMgPSAxKQpwYXJhbGxlbDo6c3RvcENsdXN0ZXIoY2wpCiMgc2F2ZVJEUyhQQUhzX0NWX2J5U2l0ZV9yZXN1bHQsICJQQUhzX0NWX2J5U2l0ZV9yZXN1bHQucmRzIikKYGBgCgoKYGBge3IgUnVuIGxhc3NvIGZvciBQQUhzIHByZWRpY3Rpb24gYW5kIHJlc3VsdCBwcm9jZXNzaW5nLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpQQUhzX0NWX2J5U2l0ZV9yZXN1bHQgPC0gcmVhZFJEUygiUEFIc19DVl9ieVNpdGVfcmVzdWx0LnJkcyIpClJlc3VsdCA9IHNhcHBseSgxOjExMjAsIGZ1bmN0aW9uKHgpIG1lYW4oUEFIc19DVl9ieVNpdGVfcmVzdWx0W1t4XV1bNV1bWzFdXSkpCm5hbWVzKFJlc3VsdCkgPSBzYXBwbHkoMToxMTIwLCBmdW5jdGlvbih4KSBzdHJfc3ViKHJvdy5uYW1lcyhQQUhzX0NWX2J5U2l0ZV9yZXN1bHRbW3hdXVs1XVtbMV1dKVsxXSwzLDQpKQpQQUhzLnBlcmZvcm1hbmNlLmJ5c2l0ZS5yZXBvcnQgPSBldmFsdWF0aW9uX2Z1bmNpdG9uLmJ5c2l0ZS5QQUgoUmVzdWx0ID0gUmVzdWx0KSAKIyBwcmludChQQUhzLnBlcmZvcm1hbmNlLmJ5c2l0ZS5yZXBvcnQkcGVyZm9ybWFuY2UucmVzdWx0KQogIyBlcnJvci50b3AgICAgZXJyb3IuYm90dG9tICAgICAgZXJyb3IuYm90aCAgICBhY2N1cmFjeS50b3AgYWNjdXJhY3kuYm90dG9tICAgYWNjdXJhY3kuYm90aCAgICAgICB0b3BfcmF0aW8gICAgYm90dG9tX3JhdGlvICAgICAgICAgICAgIE1TRSAKICMgICAgIDAuMDc4Mjc0NzYgICAgICAwLjAwMDAwMDAwICAgICAgMC4wNzc5MDE0MyAgICAgIDAuNzQyODExNTAgICAgICAxLjAwMDAwMDAwICAgICAgMC43NDQwMzgxNiAgICAgIDAuNDI4NTcxNDMgICAgICAwLjE3ODU3MTQzICAgICAgMC4xMzU2MDk5OCAKCiMgRWRpdCB2YXJpYWJsZXMKdmFyaWFibGVzLmxlbmd0aC5zdW1tYXJ5IDwtICBzdW1tYXJ5KHNhcHBseSgxOmxlbmd0aChQQUhzX0NWX2J5U2l0ZV9yZXN1bHQpLCBmdW5jdGlvbih4KSBsZW5ndGgoUEFIc19DVl9ieVNpdGVfcmVzdWx0W1t4XV1bM11bWzFdXSkpKQojIE1pbi4gMXN0IFF1LiAgTWVkaWFuICAgIE1lYW4gM3JkIFF1LiAgICBNYXguIAojICAgMzcuMDAgICA1NC4wMCAgIDU3LjAwICAgNTYuNTkgICA2MC4wMCAgIDcwLjAwIAp2YXJpYWJsZXMubGVuZ3RoLnA5NSA8LSBxdWFudGlsZShzYXBwbHkoMTpsZW5ndGgoUEFIc19DVl9ieVNpdGVfcmVzdWx0KSwgZnVuY3Rpb24oeCkgbGVuZ3RoKFBBSHNfQ1ZfYnlTaXRlX3Jlc3VsdFtbeF1dWzNdW1sxXV0pKSwwLjk1KQp2YXJpYWJsZXMgPC0gbmFtZXMoc29ydCh0YWJsZSh1bmxpc3Qoc2FwcGx5KDE6bGVuZ3RoKFBBSHNfQ1ZfYnlTaXRlX3Jlc3VsdCksIGZ1bmN0aW9uKHgpIG5hbWVzKFBBSHNfQ1ZfYnlTaXRlX3Jlc3VsdFtbeF1dWzNdW1sxXV0pKSkpLGRlY3JlYXNpbmcgPSBUUlVFKVsxOnZhcmlhYmxlcy5sZW5ndGgucDk1XSkKdmFyaWFibGVzIDwtIGxpc3QodmFyaWFibGVzKQp2YXJpYWJsZXMkbmFtZSA8LSBJRC5jb252ZXJ0JEdlbmVOYW1lW21hdGNoKHZhcmlhYmxlc1tbMV1dLElELmNvbnZlcnQkR2VuZUlEMildIAp2YXJpYWJsZXMuY29lZiA9IHVubGlzdChsYXBwbHkoMTpsZW5ndGgoUEFIc19DVl9ieVNpdGVfcmVzdWx0KSwgZnVuY3Rpb24oeCkgbmEub21pdChQQUhzX0NWX2J5U2l0ZV9yZXN1bHRbW3hdXVszXVtbMV1dW3ZhcmlhYmxlc1tbMV1dXSkpKQojIGdlb21lYW5fZnVuYyA9IGZ1bmN0aW9uKHgpIHtleHAobWVhbihsb2coeCkpKX0KdmFyaWFibGVzLmNvZWYuYWdncmVnYXRlID0gYWdncmVnYXRlKHZhcmlhYmxlcy5jb2VmLCBsaXN0KEdlbmUgPSBuYW1lcyh2YXJpYWJsZXMuY29lZikpLG1lYW4pCmNvbG5hbWVzKHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZSlbMl0gPSAiY29lZiIKdmFyaWFibGVzLmNvZWYuYWdncmVnYXRlID0gZGF0YS5mcmFtZShuYW1lID0gSUQuY29udmVydCRHZW5lTmFtZVttYXRjaCh2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUkR2VuZSwgSUQuY29udmVydCRHZW5lSUQyKV0sIHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZSkKIyByZW1vdmUgZHVwbGljYXRlcyBURkNQMkwxIGFuZCBDU01EMSAKdmFyaWFibGVzLmNvZWYuYWdncmVnYXRlIDwtIHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZVtjKC04LC0xMCksXSAKCiMgZGV2LnJhdGlvCmRldi5yYXRpby5uIDwtIGxlbmd0aChQQUhzX0NWX2J5U2l0ZV9yZXN1bHQpCmRldi5yYXRpbyA8LSBzYXBwbHkoMTpkZXYucmF0aW8ubiwgZnVuY3Rpb24oeCkgUEFIc19DVl9ieVNpdGVfcmVzdWx0W1t4XV1bWzRdXSkKZGV2LnJhdGlvLm1lYW4gPC0gbWVhbihkZXYucmF0aW8pCmRldi5yYXRpby5zIDwtIHNkKGRldi5yYXRpbykKbWFyZ2luIDwtIHF0KDAuOTc1LGRmPWRldi5yYXRpby5uLTEpKmRldi5yYXRpby5zL3NxcnQoZGV2LnJhdGlvLm4pCmRldi5yYXRpby5QQUhzID0gYyhtZWFuID0gZGV2LnJhdGlvLm1lYW4sIG1hcmdpbiA9IG1hcmdpbikKIyBwcmludChkZXYucmF0aW8uUEFIcykKIyBtZWFuICAgICAgbWFyZ2luIAojIDAuNzM0MjcwMDA1IDAuMDAxMzU1NzkxIAoKIyBDb25zdHJ1Y3QgcGFudGhlciBHTyBhbmFseXNpcyAKbGlicmFyeSh4bWwyKQpQQUhzX2JwLmRhdGEucmF3IDwtIHJlYWRfeG1sKCJQQUhzXzJuZExhc3NvX2Z1bmNpb250cy54bWwiKSAjIyBmcm9tICBQQU5USEVSMTcuMCBodHRwOi8vd3d3LnBhbnRoZXJkYi5vcmcvCmlkID0geG1sX3RleHQoeG1sX2ZpbmRfYWxsKFBBSHNfYnAuZGF0YS5yYXcsICIvL3Jlc3VsdC90ZXJtL2lkIikpCnRlcm0gPSB4bWxfdGV4dCh4bWxfZmluZF9hbGwoUEFIc19icC5kYXRhLnJhdywgIi8vcmVzdWx0L3Rlcm0vbGFiZWwiKSlbLTMxOV0gIyByZW1vdmUgbm8gY2xhc3MgaXRlbQpudW1iZXJfaW5fbGlzdCA9IGFzLm51bWVyaWMoeG1sX3RleHQoeG1sX2ZpbmRfYWxsKFBBSHNfYnAuZGF0YS5yYXcsICIvL3Jlc3VsdC9pbnB1dF9saXN0L251bWJlcl9pbl9saXN0IikpKVstMzE5XQpudW1iZXJfaW5fcmVmZXJlbmNlID0gYXMubnVtZXJpYyh4bWxfdGV4dCh4bWxfZmluZF9hbGwoUEFIc19icC5kYXRhLnJhdywgIi8vcmVzdWx0L251bWJlcl9pbl9yZWZlcmVuY2UiKSkpWy0zMTldCnBWYWx1ZSA9IGFzLm51bWVyaWMoeG1sX3RleHQoeG1sX2ZpbmRfYWxsKFBBSHNfYnAuZGF0YS5yYXcsICIvL3BWYWx1ZSIpKSlbLTMxOV0KZm9sZF9lbnJpY2htZW50ID0gYXMubnVtZXJpYyh4bWxfdGV4dCh4bWxfZmluZF9hbGwoUEFIc19icC5kYXRhLnJhdywgIi8vZm9sZF9lbnJpY2htZW50IikpKVstMzE5XQptYXBwZWRfaWQgPSBzYXBwbHkoMTozMTksIGZ1bmN0aW9uKHgpIHhtbF90ZXh0KHhtbF9jaGlsZHJlbih4bWxfZmluZF9hbGwoUEFIc19icC5kYXRhLnJhdywgIi8vcmVzdWx0L2lucHV0X2xpc3QvbWFwcGVkX2lkX2xpc3QiKVt4XSkpKVstMzE5XQpuYW1lcyhtYXBwZWRfaWQpIDwtIGlkClBBSHNfYnAucGFudGhlciA9IHRpYmJsZShpZCA9IGlkLCB0ZXJtID0gdGVybSwgbnVtYmVyX2luX2xpc3QgPSBudW1iZXJfaW5fbGlzdCwgbnVtYmVyX2luX3JlZmVyZW5jZSA9IG51bWJlcl9pbl9yZWZlcmVuY2UsIHBWYWx1ZSA9IHBWYWx1ZSwgZm9sZF9lbnJpY2htZW50ID0gZm9sZF9lbnJpY2htZW50LCBtYXBwZWRfaWQgPSBtYXBwZWRfaWQpClBBSHNfYnAucGFudGhlci5tYXBwZWRfaWQgPSBQQUhzX2JwLnBhbnRoZXIkbWFwcGVkX2lkCiMjIGNoZWNrIGxhc3NvIHNlbGVjdGVkIFBBSHMgcmVsYXRlZCBnZW5lcyBmdW5jaW9ucyBhbmQgcGxvdCBhcyBoZWF0bWFwIApQQUhzX2JwLnBhbnRoZXIudHJpbSA9IFBBSHNfYnAucGFudGhlciAlPiUgZmlsdGVyKG51bWJlcl9pbl9yZWZlcmVuY2UgPCA1MDAwICYgbnVtYmVyX2luX3JlZmVyZW5jZSA+IDIwMCkgICMgc2VsZWN0IG1vcmUgZ2VuZXJhbCBnbyB0ZXJtcyAKUEFIc19icC5wYW50aGVyLm1hcHBlZF9pZCRhbGwgPSB1bmlxdWUobmEub21pdCh2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUkbmFtZSkpClBBSHNfYnAucGFudGhlci5kYXRhID0gZGF0YS5mcmFtZShHT0JQX0lEID0gcmVwKG5hbWVzKFBBSHNfYnAucGFudGhlci5tYXBwZWRfaWQpLCBzYXBwbHkoMTozMTksIGZ1bmN0aW9uKHgpIGxlbmd0aChQQUhzX2JwLnBhbnRoZXIubWFwcGVkX2lkW1t4XV0pKSApLCBuYW1lID0gdW5saXN0KFBBSHNfYnAucGFudGhlci5tYXBwZWRfaWQsIHVzZS5uYW1lcyA9IEZBTFNFKSkKUEFIc19icC5wYW50aGVyLmRhdGEgPSBkcGx5cjo6bGVmdF9qb2luKFBBSHNfYnAucGFudGhlci5kYXRhLCB2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUpClBBSHNfYnAucGFudGhlcjIgPSBQQUhzX2JwLnBhbnRoZXIKY29sbmFtZXMoUEFIc19icC5wYW50aGVyMilbMV0gPSAiR09CUF9JRCIKUEFIc19icC5wYW50aGVyLmRhdGEgPSBkcGx5cjo6bGVmdF9qb2luKFBBSHNfYnAucGFudGhlci5kYXRhLCBQQUhzX2JwLnBhbnRoZXIyWywxOjZdKQoKIyMgVXNpbmcgUmV2aWdvIHRvIHJlbW92ZSByZWR1bmRuYXQgR08gdGVybXMgOiBQTG9TIE9ORSAyMDExLiBkb2k6MTAuMTM3MS9qb3VybmFsLnBvbmUuMDAyMTgwMCAKIyBUaGUgR2VuZSBPbnRvbG9neSBkYXRhYmFzZSAoZ28ub2JvKSB3aGljaCBpcyBkYXRlZCBUaHVyc2RheSwgTm92ZW1iZXIgMywgMjAyMi4KIyBUaGUgVW5pUHJvdC10by1HTyBtYXBwaW5nIGRhdGFiYXNlIGZyb20gdGhlIEVCSSBHT0EgcHJvamVjdCAoZ29hX3VuaXByb3RfZ2NycC5nYWYuZ3opIHdoaWNoIGlzIGRhdGVkIEZyaWRheSwgU2VwdGVtYmVyIDE2LCAyMDIyLgpSZXZpZ28uc2VsZWN0ZWQuZ29icCA8LSBjKAogICJhbGwiLAogICJHTzowMDA5OTg3IiwKICAiR086MDAwNzAxMCIsCiAgIkdPOjAwMDcxNTQiLAogICJHTzowMDIzMDUyIiwKICAiR086MDAzMDAyOSIsCiAgIkdPOjAwMzM1NTQiLAogICJHTzowMDUxNzE2IiwKICAiR086MDA0MjU5MiIsCiAgIkdPOjAwNDg1MjMiLAogICJHTzowMDQ4ODc4IiwKICAiR086MDA1MDg5NiIsCiAgIkdPOjAwNzIzNTkiCiAgKQpQQUhzX2JwLnBhbnRoZXIuZGF0YSR0ZXJtW1BBSHNfYnAucGFudGhlci5kYXRhJEdPQlBfSUQgPT0gImFsbCJdID0gImFsbCIKZGF0YSA8LSBhc190aWJibGUoUEFIc19icC5wYW50aGVyLmRhdGFbLGMoMSwyLDQsNSldKQpkYXRhIDwtIGRhdGEgJT4lIGZpbHRlcihHT0JQX0lEICVpbiUgUmV2aWdvLnNlbGVjdGVkLmdvYnApCmRhdGEgPC0gZGF0YSAlPiUgc3ByZWFkKGtleSA9ICJuYW1lIiAsIHZhbHVlID0gImNvZWYiKQpkYXRhLm1hdHJpeCA9IGRhdGFbLDM6NTldCgpkYXRhLm1hdHJpeCA9IGFzLm1hdHJpeChkYXRhLm1hdHJpeCkKcm93Lm5hbWVzKGRhdGEubWF0cml4KSA9IGRhdGEkdGVybQpkYXRhLm1hdHJpeFtpcy5uYShkYXRhLm1hdHJpeCldID0gMAoKUEFIc19sYXNzb19ieV9zaXRlXzUwX2Z1bmN0aW9uYWxfZGF0YSA9IGxpc3QocGFudGhlcl9yZXN1bHQgPSBQQUhzX2JwLnBhbnRoZXIsIGdlbmVfbWFwcGVkX3BhbnRoZXJfcmVzdWx0ID0gUEFIc19icC5wYW50aGVyLmRhdGEsIGhlYXRtYXBfcGxvdF9kYXRhID0gZGF0YS5tYXRyaXgsIHRvcF9nb190ZXJtcyA9IFJldmlnby5zZWxlY3RlZC5nb2JwKQojIHNhdmVSRFMoUEFIc19sYXNzb19ieV9zaXRlXzUwX2Z1bmN0aW9uYWxfZGF0YSwgICJQQUhzX2xhc3NvX2J5X3NpdGVfNTBfZnVuY3Rpb25hbF9kYXRhLnJkcyIpClBBSHMucGVyZm9ybWFuY2UucmVwb3J0IDwtIGxpc3QoUEFIcy5wZXJmb3JtYW5jZS5ieXNpdGUucmVwb3J0ID0gUEFIcy5wZXJmb3JtYW5jZS5ieXNpdGUucmVwb3J0LCBQQUhzLnZhcmlhYmxlcyA9IGxpc3QodmFyaWFibGVzLHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZSwgUEFIc19icC5wYW50aGVyLmRhdGEpLCBQQUhzLmRldi5yYXRpbyA9IGRldi5yYXRpby5QQUhzKQojIHNhdmVSRFMoUEFIcy5wZXJmb3JtYW5jZS5yZXBvcnQsICJQQUhzLnBlcmZvcm1hbmNlLnJlcG9ydGFnZ3JlZ2F0ZS5yZHMiKQoKcGVyZm9ybWFuY2UubWV0cmljcyA8LSByb3VuZChkYXRhLmZyYW1lKFBBSHMucGVyZm9ybWFuY2UucmVwb3J0JFBBSHMucGVyZm9ybWFuY2UuYnlzaXRlLnJlcG9ydCRwZXJmb3JtYW5jZS5yZXN1bHQpLDMpCmNvbG5hbWVzKHBlcmZvcm1hbmNlLm1ldHJpY3MpIDwtIGMoIlBvb2xlZCBQQUhzIGNvbmNlbnRyYXRpb25zIGJ5IHNpdGUiKQojIHNhdmVSRFMocGVyZm9ybWFuY2UubWV0cmljcywgIlBBSHMucGVyZm9ybWFuY2UubWV0cmljcy5yZHMiKQoKYGBgCgojIyMgUEFIcyBsYXNzbyBwcmVkaWN0aW9uIHJlc3VsdHMgey50YWJzZXR9CgojIyMjIFByZWRpY3RlZCB2cyBhY3R1cmFsIHl5IHBsb3QgYnkgc2l0ZSAKYGBge3IgUEFIcyB5eSBwbG90LCBlY2hvPUZBTFNFfQojIyBQQUhzIGxhc3NvIHBsb3QgClBBSHMucGVyZm9ybWFuY2UucmVwb3J0IDwtIHJlYWRSRFMoIlBBSHMucGVyZm9ybWFuY2UucmVwb3J0YWdncmVnYXRlLnJkcyIpCgojIyBmaWd1cmUgZm9yIHBsb3R0aW5nIFBBSCB5eSBwcmVkaWNpdG9uIHZzIG1lYXN1cmVkIHBsb3QgCgpsaWJyYXJ5KGdnaGlnaGxpZ2h0KQpsaWJyYXJ5KGdncHVicikKIyMgUEFIcyBsYXNzbyBwbG90IAoKUEFIcy5wZXJmb3JtYW5jZS5yZXBvcnQkUEFIcy5wZXJmb3JtYW5jZS5ieXNpdGUucmVwb3J0JHByZWRpY3Rpb25zLmJ5LnNpdGUgPC0gUEFIcy5wZXJmb3JtYW5jZS5yZXBvcnQkUEFIcy5wZXJmb3JtYW5jZS5ieXNpdGUucmVwb3J0JHByZWRpY3Rpb25zLmJ5LnNpdGUgJT4lIG11dGF0ZShwcmVkaWN0ZWQucGVyY2VudGlsZSA9ICJtaWRkbGUiKQpQQUhzLnBlcmZvcm1hbmNlLnJlcG9ydCRQQUhzLnBlcmZvcm1hbmNlLmJ5c2l0ZS5yZXBvcnQkcHJlZGljdGlvbnMuYnkuc2l0ZSRwcmVkaWN0ZWQucGVyY2VudGlsZVtQQUhzLnBlcmZvcm1hbmNlLnJlcG9ydCRQQUhzLnBlcmZvcm1hbmNlLmJ5c2l0ZS5yZXBvcnQkcHJlZGljdGlvbnMuYnkuc2l0ZSRpcy50b3AucHJlZGljdF0gPC0gInRvcCIKUEFIcy5wZXJmb3JtYW5jZS5yZXBvcnQkUEFIcy5wZXJmb3JtYW5jZS5ieXNpdGUucmVwb3J0JHByZWRpY3Rpb25zLmJ5LnNpdGUkcHJlZGljdGVkLnBlcmNlbnRpbGVbUEFIcy5wZXJmb3JtYW5jZS5yZXBvcnQkUEFIcy5wZXJmb3JtYW5jZS5ieXNpdGUucmVwb3J0JHByZWRpY3Rpb25zLmJ5LnNpdGUkaXMuYm90dG9tLnByZWRpY3RdIDwtICJib3R0b20iCgoKdGhlbWVfc2V0KHRoZW1lX2NsYXNzaWMoKSkKcCA8LSBnZ3Bsb3QoZGF0YT1QQUhzLnBlcmZvcm1hbmNlLnJlcG9ydCRQQUhzLnBlcmZvcm1hbmNlLmJ5c2l0ZS5yZXBvcnQkcHJlZGljdGlvbnMuYnkuc2l0ZSwgYWVzKHg9cHJlZGljdGlvbnMsIHk9Z2VvTWVhbikpICsgeGxhYigicHJlZGljdGVkIGxvZzEwIFRvdGFsIFBBSHMgKG5nL2cgd2V0IHd0LikiKSArIHlsYWIoImxvZzEwIFRvdGFsIFBBSHMgKG5nL2cgd2V0IHd0LikiKQoKClBBSHMucHJlZGljdGlvbnMueXlwbG90LnYyIDwtCiAgcCArIAogIGdlb21fcG9pbnQoYWVzKGNvbCA9ICBwcmVkaWN0ZWQucGVyY2VudGlsZSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iUHJlZGljdGVkIFtQQUhzXSIsIAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJhYm92ZSA2N3RoIiwgIm1pZGRsZSIsICJiZWxvdyAzM3JkIiksIAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJ0b3AiPSJyZWQiLCAibWlkZGxlIiA9ICJsaWdodGdyYXkiLCAiYm90dG9tIj0iYmx1ZSIpKSsKICAgICAgICAgICAgICAgICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdD0xLjgwNSwgbGluZXR5cGU9ImRvdHRlZCIsIGNvbG9yID0gImJsdWUiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIuMTkwLCBsaW5ldHlwZT0iZG90dGVkIiwgY29sb3IgPSAicmVkIikrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTIuMTkwLCBsaW5ldHlwZT0iZG90ZGFzaCIsIGNvbG9yID0gInJlZCIpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0xLjgwNSwgbGluZXR5cGU9ImRvdGRhc2giLCBjb2xvciA9ICJibHVlIikrCiAgZ2VvbV90ZXh0KGFlcygyLjYxLCAxLjgwNSwgbGFiZWwgPSAiMzNyZCBwZXJjZW50aWxlIiwgdmp1c3QgPSAtIDEpLCBjb2xvciA9ICJibHVlIikgKwogIGdlb21fdGV4dChhZXMoMi42MSwgMi4xOTAsIGxhYmVsID0gIjY3dGggcGVyY2VudGlsZSIsIHZqdXN0ID0gLSAxKSwgY29sb3IgPSAicmVkIikgKwogIHN0YXRfY29yKG1ldGhvZCA9ICJzcGVhcm1hbiIsIGxhYmVsLnggPSAxLjc1LCBsYWJlbC55ID0gMy41LCBjb3IuY29lZi5uYW1lID0gInJobyIpKwogIHN0YXRfY29yKG1ldGhvZCA9ICJwZWFyc29uIiwgbGFiZWwueCA9IDEuNzUsIGxhYmVsLnkgPSAzLjQsIGNvci5jb2VmLm5hbWUgPSAiUiIpKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDEyKQoKUEFIcy5wcmVkaWN0aW9ucy55eXBsb3QudjIKYGBgCgojIyMjIFByZWRpY3RlZCB2cyBhY3R1cmFsIHl5IHBsb3QgYnkgc2l0ZSB3aXRoIENJICAKYGBge3IgUHJlZGljdGVkIHZzIGFjdHVyYWwgeXkgcGxvdCBieSBzaXRlIHdpdGggQ0ksIGVjaG89RkFMU0V9CiMjIFBBSHMgbGFzc28gcGxvdCAKUEFIcy5wZXJmb3JtYW5jZS5yZXBvcnQgPC0gcmVhZFJEUygiUEFIcy5wZXJmb3JtYW5jZS5yZXBvcnRhZ2dyZWdhdGUucmRzIikKCiMjIGZpZ3VyZSBmb3IgcGxvdHRpbmcgUEFIIHl5IHByZWRpY2l0b24gdnMgbWVhc3VyZWQgcGxvdCAKCmxpYnJhcnkoZ2doaWdobGlnaHQpCmxpYnJhcnkoZ2dwdWJyKQojIyBQQUhzIGxhc3NvIHBsb3QgCnBsb3QuZGF0YS5QQUhzIDwtIGFzX3RpYmJsZShQQUhzLnBlcmZvcm1hbmNlLmJ5c2l0ZS5yZXBvcnQkcHJlZGljdGlvbnMuYnkuc2l0ZVssYygxLDIsMyldKQpwbG90LmRhdGEuUEFIcyA8LSBwbG90LmRhdGEuUEFIcyAlPiUgZ3JvdXBfYnkoc2l0ZSkgJT4lIHN1bW1hcmlzZShwcmVkaWN0aW9ucyA9IG1lYW4ocHJlZGljdGlvbnMpLCBtZWFzdXJlZCA9IG1lYW4oZ2VvTWVhbikpICU+JSB1bmdyb3VwKCkKcGxvdC5kYXRhLlBBSHMgPC0gcGxvdC5kYXRhLlBBSHMgJT4lIG11dGF0ZShwcm9wZXJfc2l0ZTIgPSBzaXRlTUFQLmFsbCRwcm9wZXJzaXRlW21hdGNoKHBsb3QuZGF0YS5QQUhzJHNpdGUsIHNpdGVNQVAuYWxsJFNpdGVJRCldKQoKZml0LlBBSHMgPC0gbG0obWVhc3VyZWQgfiBwcmVkaWN0aW9ucywgZGF0YSA9IHBsb3QuZGF0YS5QQUhzKQpkYXQgPC0gcHJlZGljdChmaXQuUEFIcywgaW50ZXJ2YWw9ImNvbmZpZGVuY2UiLCBsZXZlbCA9IDAuOTk5KQpwbG90LmRhdGEuUEFIcyRpbnNpZGUgPC0gaWZlbHNlKHBsb3QuZGF0YS5QQUhzJG1lYXN1cmVkIDwgZGF0WywidXByIl0gJiBwbG90LmRhdGEuUEFIcyRtZWFzdXJlZCA+IGRhdFssImx3ciJdLCAiaW4iLCAib3V0IikKCgojIAojICMgU2NhdHRlcnBsb3QKUEFIcy55eXBsb3QuYnlzaXRlMi4xIDwtIGdncGxvdChwbG90LmRhdGEuUEFIcywgYWVzKHg9cHJlZGljdGlvbnMsIHk9bWVhc3VyZWQpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sID0gaW5zaWRlKSwgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygib3V0IiwgImluIiksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIm91dCI9InJlZCIsICJpbiI9ImdyYXkiKSkgKwogIGxhYnMoeT0ibG9nMTAgVG90YWwgUEFIcyAobmcvZyB3ZXQgd3QuKSIsCiAgICAgICB4PSJQcmVkaWN0ZWQgbG9nMTAgVG90YWwgUEFIcyAobmcvZyB3ZXQgd3QuKSIsCiAgICAgICB0aXRsZT0iIikrCiAgIyBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHBsb3RfZGF0YTJbIXBsb3RfZGF0YTIkc3RyID09ICJDIixdLCBhZXMobGFiZWwgPSBwcm9wZXJfc2l0ZTIsIHg9cHJlZGljdGlvbnMsIHk9Z2VvbWV0cmljLm1lYW4pKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLCBjb2xvcj0nZ3JheScsIGZpbGw9J2xpZ2h0Ymx1ZScsIGxldmVsPTAuOTk5LCBmb3JtdWxhID0gInkgfiB4IikgKwogICMgZ2VvbV9zbW9vdGgobWV0aG9kPSJsb2VzcyIsIGNvbG9yPSdncmF5JywgZmlsbD0nbGlnaHRibHVlJywgbGV2ZWw9MC45NSwgZm9ybXVsYSA9ICJ5IH4geCIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKQoKUEFIcy55eXBsb3QuYnlzaXRlMi4yIDwtIGdncGxvdChwbG90LmRhdGEuUEFIcywgYWVzKHg9cHJlZGljdGlvbnMsIHk9bWVhc3VyZWQpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sID0gaW5zaWRlKSwgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygib3V0IiwgImluIiksCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIm91dCI9InJlZCIsICJpbiI9ImdyYXkiKSkgKwogIGxhYnMoeT0ibG9nMTAgVG90YWwgUEFIcyAobmcvZyB3ZXQgd3QuKSIsCiAgICAgICB4PSJQcmVkaWN0ZWQgbG9nMTAgVG90YWwgUEFIcyAobmcvZyB3ZXQgd3QuKSIsCiAgICAgICB0aXRsZT0iIikrCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBwbG90LmRhdGEuUEFIcywgYWVzKGxhYmVsID0gcHJvcGVyX3NpdGUyLCB4PXByZWRpY3Rpb25zLCB5PW1lYXN1cmVkKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgY29sb3I9J2dyYXknLCBmaWxsPSdsaWdodGJsdWUnLCBsZXZlbD0wLjk5OSwgZm9ybXVsYSA9ICJ5IH4geCIpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDExKQoKCiMgIyAKIyB0aWZmKCJ+L0RvY3VtZW50cy93b3JrL1Rzd2FsbG93X2NoZW1fR0xSSV91cGRhdGUvR0xSSV9NUzJfYWxsL0FsbF9maWd1cmVzL0ZpZ3VyZV9wZW5kaW5nX1BBSHNzX2Rpc2N1c3Njb21wbGV4bWl4dHVyZS50aWZmIiwgdW5pdHM9ImluIiwgcG9pbnRzaXplID0gMTAsIHdpZHRoPTYsIGhlaWdodD02LCByZXM9MzAwLCBjb21wcmVzc2lvbiA9ICdsencnKQojIFBBSHMueXlwbG90LmJ5c2l0ZTIuMQojIGRldi5vZmYoKQojIAojIHRpZmYoIn4vRG9jdW1lbnRzL3dvcmsvVHN3YWxsb3dfY2hlbV9HTFJJX3VwZGF0ZS9HTFJJX01TMl9hbGwvQWxsX2ZpZ3VyZXMvRmlndXJlX3BlbmRpbmdfUEFIc3NWMl9kaXNjdXNzY29tcGxleG1peHR1cmUudGlmZiIsIHVuaXRzPSJpbiIsIHBvaW50c2l6ZSA9IDEwLCB3aWR0aD02LCBoZWlnaHQ9NiwgcmVzPTMwMCwgY29tcHJlc3Npb24gPSAnbHp3JykKIyBQQUhzLnl5cGxvdC5ieXNpdGUyLjIKIyBkZXYub2ZmKCkKIyAKIyB0aWZmKCJ+L0RvY3VtZW50cy93b3JrL1Rzd2FsbG93X2NoZW1fR0xSSV91cGRhdGUvR0xSSV9NUzJfYWxsL0FsbF9maWd1cmVzL0ZpZ3VyZV9wZW5kaW5nX1BBSHNzVjJfZGlzY3Vzc2NvbXBsZXhtaXh0dXJlLnRpZmYiLCB1bml0cz0iaW4iLCBwb2ludHNpemUgPSAxMCwgd2lkdGg9NiwgaGVpZ2h0PTYsIHJlcz0zMDAsIGNvbXByZXNzaW9uID0gJ2x6dycpCiMgUEFIcy55eXBsb3QuYnlzaXRlMi4yCiMgZGV2Lm9mZigpCiMgCiMgdGlmZigifi9Eb2N1bWVudHMvd29yay9Uc3dhbGxvd19jaGVtX0dMUklfdXBkYXRlL0dMUklfTVMyX2FsbC9BbGxfZmlndXJlcy9GaWd1cmVfcGVuZGluZ19kaXNjdXNzY29tcGxleG1peHR1cmVWMS50aWZmIiwgdW5pdHM9ImluIiwgcG9pbnRzaXplID0gMTIsIHdpZHRoPTUsIGhlaWdodD0xMCwgcmVzPTMwMCwgY29tcHJlc3Npb24gPSAnbHp3JykKIyBnZ2FycmFuZ2UoeXlwbG90LmJ5c2l0ZTIuMSwgUEFIcy55eXBsb3QuYnlzaXRlMi4xLAojICAgICAgICAgICBsYWJlbHMgPSBjKCJhIiwgImIiKSwKIyAgICAgICAgICAgbmNvbCA9IDEsIG5yb3cgPSAyKQojIGRldi5vZmYoKQojIAojIHRpZmYoIn4vRG9jdW1lbnRzL3dvcmsvVHN3YWxsb3dfY2hlbV9HTFJJX3VwZGF0ZS9HTFJJX01TMl9hbGwvQWxsX2ZpZ3VyZXMvRmlndXJlX3BlbmRpbmdfZGlzY3Vzc2NvbXBsZXhtaXh0dXJlVjIudGlmZiIsIHVuaXRzPSJpbiIsIHBvaW50c2l6ZSA9IDEyLCB3aWR0aD02LCBoZWlnaHQ9MTIsIHJlcz0zMDAsIGNvbXByZXNzaW9uID0gJ2x6dycpCiMgZ2dhcnJhbmdlKHl5cGxvdC5ieXNpdGUyLjIsIFBBSHMueXlwbG90LmJ5c2l0ZTIuMiwKIyAgICAgICAgICAgbGFiZWxzID0gYygiYSIsICJiIiksCiMgICAgICAgICAgIG5jb2wgPSAxLCBucm93ID0gMikKIyBkZXYub2ZmKCkKCgpQQUhzLnl5cGxvdC5ieXNpdGUyLjEKYGBgCgojIyMjIG1vZGVsIHBlcmZvcm1hbmNlCmBgYHtyIFBBSHMgc2hvdyBwZXJmb3JtYW5jZSB0YWJsZSwgZWNobz1GQUxTRX0KcGVyZm9ybWFuY2UubWV0cmljcyA8LSByZWFkUkRTKCJQQUhzLnBlcmZvcm1hbmNlLm1ldHJpY3MucmRzIikKa25pdHI6OmthYmxlKHBlcmZvcm1hbmNlLm1ldHJpY3MsIGNhcHRpb24gPSAiUEFIcyBsYXNzbyByZWdyZXNzaW9uIG1vZGVsIHBlcmZvcm1hbmNlIG1ldHJpY3MiKQpgYGAKCgojIyMjIFByZWRpY3RvciBnZW5lcyBzZWxlY3RlZCBpbiB0aGUgbW9kZWwgICAKCmBgYHtyIFBBSHMgZGV2LnJhdGlvLCBlY2hvPUZBTFNFfQpQQUhzLnRvcC5wcmVkaWN0b3JzIDwtIGFzX3RpYmJsZShQQUhzLnBlcmZvcm1hbmNlLnJlcG9ydCRQQUhzLnZhcmlhYmxlc1tbMl1dWyFpcy5uYShQQUhzLnBlcmZvcm1hbmNlLnJlcG9ydCRQQUhzLnZhcmlhYmxlc1tbMl1dJG5hbWUpICxjKDEsMywyKV0pCmtuaXRyOjprYWJsZShQQUhzLnRvcC5wcmVkaWN0b3JzLCAic2ltcGxlIiwgY2FwdGlvbiA9ICJQQUhzIHRvcCBwcmVkaWN0b3IgZ2VuZXMgYW5kIGNvZWZmaWNpZW50IiwgYWxpZ24gPSAibGNjIikKYGBgCiMjIyBQQUhzIHRvcCBnZW5lIHByZWRpY3RvcnMgZnVuY3Rpb25zIGFuZCBwYXRod2F5cyB7LnRhYnNldH0KCgojIyMjIFBBSHMgbGFzc28gc2VsZWN0ZWQgZ2VuZXMgaGVhdG1hcAoKYGBge3IgRmlndXJlIDViIFBBSHMgbGFzc28gc2VsZWN0ZWQgZ2VuZXMgaGVhdG1hcCwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTExLCBmaWcuaGVpZ2h0PTl9CgojIyBmaWd1cmUgcGxvdHRpbmcgc2l0ZSBhdmVyYWdlIGNvdW50IGhlYXRtYXAgYWdhaW5zdCBQQUhzIGNvbmNlbnRyYXRpb24gClBBSHNfR09CUF9zdWJzZXQgPSBjKCJjZWxsdWxhciBwcm9jZXNzIiwicmVzcG9uc2UgdG8gc3RpbXVsdXMiLCJuZWdhdGl2ZSByZWd1bGF0aW9uIG9mIGNlbGx1bGFyIHByb2Nlc3MiLCJob21lb3N0YXRpYyBwcm9jZXNzIiwiY2VsbHVsYXIgcmVzcG9uc2UgdG8gc3RyZXNzIiwiY3l0b3NrZWxldG9uIG9yZ2FuaXphdGlvbiIpCgojIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIKbWF0Y2gxIDwtIHN0cl9zdWIoY29sbmFtZXMoY291bnRzLnRzd2FsbG93Lm5vcm0pLDMsNCkgJWluJSBjb250YW1pbmFudHMuY29sZGF0YS5zdWJzZXQyJE1lcmdlU2l0ZQpjb3VudHMuY29uICA8LSAgY291bnRzLnRzd2FsbG93Lm5vcm1bLG1hdGNoMV0KY291bnRzLmNvbi5tZWFuLnN1YnNldCA8LSB0KGNvdW50cy5jb25bdmFyaWFibGVzLmNvZWYuYWdncmVnYXRlJEdlbmVbIWlzLm5hKHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZSRuYW1lKV0sXSkKY291bnRzLmNvbi5tZWFuLnN1YnNldCA8LSBhc190aWJibGUoY2JpbmQoaWQgPSBzdHJfc3ViKHJvdy5uYW1lcyhjb3VudHMuY29uLm1lYW4uc3Vic2V0KSwzLDQpLCBjb3VudHMuY29uLm1lYW4uc3Vic2V0KSkKY291bnRzLmNvbi5tZWFuLnN1YnNldCA8LSBjb3VudHMuY29uLm1lYW4uc3Vic2V0ICU+JSBnYXRoZXIoa2V5ID0gImdlbmUiLCB2YWx1ZSA9ICJub3JtYWxpemVkLmNvdW50cyIsIC1pZCkgCmNvdW50cy5jb24ubWVhbi5zdWJzZXQkbm9ybWFsaXplZC5jb3VudHMgPC0gYXMubnVtZXJpYyhjb3VudHMuY29uLm1lYW4uc3Vic2V0JG5vcm1hbGl6ZWQuY291bnRzKQpjb3VudHMuY29uLm1lYW4uc3Vic2V0IDwtIGNvdW50cy5jb24ubWVhbi5zdWJzZXQgJT4lIGdyb3VwX2J5KGlkLGdlbmUpICU+JSBzdW1tYXJpc2UoY291bnRzLm1lYW4gPSBtZWFuKG5vcm1hbGl6ZWQuY291bnRzKSkgJT4lIHVuZ3JvdXAoKQpjb3VudHMuY29uLm1lYW4uc3Vic2V0IDwtIGNvdW50cy5jb24ubWVhbi5zdWJzZXQgJT4lIHNwcmVhZChrZXk9Z2VuZSwgdmFsdWUgPSBjb3VudHMubWVhbikKY291bnRzLmNvbi5tZWFuLnN1YnNldC5tYXRyaXggPSBhcy5tYXRyaXgoY291bnRzLmNvbi5tZWFuLnN1YnNldFssMjo1NV0pCmNvbG5hbWVzKGNvdW50cy5jb24ubWVhbi5zdWJzZXQubWF0cml4KSA9ICB2YXJpYWJsZXMuY29lZi5hZ2dyZWdhdGUkbmFtZVttYXRjaChjb2xuYW1lcyhjb3VudHMuY29uLm1lYW4uc3Vic2V0KVsyOjU1XSwgdmFyaWFibGVzLmNvZWYuYWdncmVnYXRlJEdlbmUpXQpjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeCA8LSBjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeFssIWR1cGxpY2F0ZWQoY29sbmFtZXMoY291bnRzLmNvbi5tZWFuLnN1YnNldC5tYXRyaXgpKV0gIyByZW1vdmUgZHVwbGljYXRlIApyb3cubmFtZXMoY291bnRzLmNvbi5tZWFuLnN1YnNldC5tYXRyaXgpIDwtIHNpdGVNQVAuYWxsJHByb3BlcnNpdGVbbWF0Y2goY291bnRzLmNvbi5tZWFuLnN1YnNldCRpZCwgc2l0ZU1BUC5hbGwkU2l0ZUlEKV0KCiMjIGNvcnJlY3RlZCBQQUhzIGNvdW50IG1hdHJpeCBzdWJzZXQgZm9yIGhlYXRtYXAgZGl2aWRlIGJ5IFN0YXJMYWtlIGV4cHJlc3Npb24gbGV2ZWwKY291bnRzLmNvbi5tZWFuLnN1YnNldC5tYXRyaXguU0xjb3JyZWN0IDwtIHQodChjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeCkgLSB0KGNvdW50cy5jb24ubWVhbi5zdWJzZXQubWF0cml4KVssIlN0YXJMYWtlIl0pWy0yMCxdCmNvdW50cy5jb24uY29udGFtaW4gPC0gbG9nMTAoY29udGFtaW5hbnRzLmNvbGRhdGEuc3Vic2V0MiR2YWx1ZS5nZW9NZWFuW21hdGNoKGNvdW50cy5jb24ubWVhbi5zdWJzZXQkaWQsIGNvbnRhbWluYW50cy5jb2xkYXRhLnN1YnNldDIkTWVyZ2VTaXRlKV0pCm5hbWVzKGNvdW50cy5jb24uY29udGFtaW4pID0gc2l0ZU1BUC5hbGwkcHJvcGVyc2l0ZVttYXRjaChjb3VudHMuY29uLm1lYW4uc3Vic2V0JGlkLCBzaXRlTUFQLmFsbCRTaXRlSUQpXQojIyBhZGQgcGVyY2VudGlsZSBsYWJlbCBmb3IgYW5ub3RhaW9uIApjb3VudHMuY29uLmNvbnRhbWluLmxhYmVsIDwtIGNvdW50cy5jb24uY29udGFtaW4KY291bnRzLmNvbi5jb250YW1pbi5sYWJlbFtjb3VudHMuY29uLmNvbnRhbWluID4gMi4xOTBdIDwtICAiPiA2N3RoIHBlcmNlbnRpbGUiCmNvdW50cy5jb24uY29udGFtaW4ubGFiZWxbY291bnRzLmNvbi5jb250YW1pbiA8IDEuODA1XSA8LSAiPCAzM3RoIHBlcmNlbnRpbGUiCmNvdW50cy5jb24uY29udGFtaW4ubGFiZWxbY291bnRzLmNvbi5jb250YW1pbiA8IDIuMTkwICYgY291bnRzLmNvbi5jb250YW1pbiA+IDEuODA1XSA8LSAiMzMgLSA2NyBwZXJjZW50aWxlIgpjb3VudHMuY29uLmNvbnRhbWluLmxhYmVsW25hbWVzKGNvdW50cy5jb24uY29udGFtaW4ubGFiZWwpICVpbiUgYygiR3JlZW5NbnQiLCJJbmRpYW5SaWRnZSIsIldpbGRSaWNlTGFrZSIsIk1hcnlKYW5lIiwiT3Njb2RhIildIDwtICJOb24tQU9DIgpjb3VudHMuY29uLmNvbnRhbWluLmxhYmVsIDwtIGZhY3Rvcihjb3VudHMuY29uLmNvbnRhbWluLmxhYmVsLCBsZXZlbHMgPSBjKCI+IDY3dGggcGVyY2VudGlsZSIsIjMzIC0gNjcgcGVyY2VudGlsZSIsIjwgMzN0aCBwZXJjZW50aWxlIiwgIk5vbi1BT0MiKSkKIyMgZWRpdCBkYXRhLm1hdHJpeCBmb3IgUEFIc19HT0JQX3N1YnNldApQQUhzX0dPQlBfc3Vic2V0Lm1hdHJpeCA8LSBkYXRhLm1hdHJpeFtQQUhzX0dPQlBfc3Vic2V0LF0KUEFIc19HT0JQX3N1YnNldC5tYXRyaXgudGVtcCA8LSBQQUhzX0dPQlBfc3Vic2V0Lm1hdHJpeApQQUhzX0dPQlBfc3Vic2V0Lm1hdHJpeFtQQUhzX0dPQlBfc3Vic2V0Lm1hdHJpeC50ZW1wID4gMF0gPC0gInVwIgpQQUhzX0dPQlBfc3Vic2V0Lm1hdHJpeFtQQUhzX0dPQlBfc3Vic2V0Lm1hdHJpeC50ZW1wIDwgMF0gPC0gImRvd24iClBBSHNfR09CUF9zdWJzZXQubWF0cml4W1BBSHNfR09CUF9zdWJzZXQubWF0cml4LnRlbXAgPT0gMF0gPC0gTkEKUEFIc19HT0JQX3N1YnNldC5tYXRyaXggPC0gdChQQUhzX0dPQlBfc3Vic2V0Lm1hdHJpeCkKUEFIc19HT0JQX3N1YnNldC5tYXRyaXggPC0gUEFIc19HT0JQX3N1YnNldC5tYXRyaXhbY29sbmFtZXMoY291bnRzLmNvbi5tZWFuLnN1YnNldC5tYXRyaXguU0xjb3JyZWN0KSxdCgpQQUhzLmhlYXRtYXAuYW5ubyA8LSBkYXRhLmZyYW1lKGdlbmVfbmFtZSA9IGNvbG5hbWVzKGNvdW50cy5jb24ubWVhbi5zdWJzZXQubWF0cml4LlNMY29ycmVjdCksIFBBSHMuY29lZiA9IHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZSRjb2VmW21hdGNoKGNvbG5hbWVzKGNvdW50cy5jb24ubWVhbi5zdWJzZXQubWF0cml4LlNMY29ycmVjdCksIHZhcmlhYmxlcy5jb2VmLmFnZ3JlZ2F0ZSRuYW1lKV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cHJlc3Npb24gPSBjb2xNZWFucyhjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeCksIFBBSHNfR09CUF9zdWJzZXQubWF0cml4KQpjb2xuYW1lcyhQQUhzLmhlYXRtYXAuYW5ubylbNDpuY29sKFBBSHMuaGVhdG1hcC5hbm5vKV0gPC0gY29sbmFtZXMoUEFIc19HT0JQX3N1YnNldC5tYXRyaXgpICMgY29ycmVjdCBjb2x1bW4gbmFtZSB0byAic3BhY2UiIGdhcHBlZCAKCmxpYnJhcnkoZGVuZHNvcnQpCmxpYnJhcnkoY2lyY2xpemUpCmNvbF9mdW4gPSBjb2xvclJhbXAyKGMoLTAuMiwgMCwgMC4yKSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkKCiMgcm93X2RlbmQgPSBkZW5kc29ydChoY2x1c3QoZGlzdChjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeC5TTGNvcnJlY3QsIG1ldGhvZCA9ICJldWNsaWRpYW4iKSwgbWV0aG9kID0gImNvbXBsZXRlIiksIGlzUmV2ZXJzZSA9IFRSVUUpCmNvbF9kZW5kID0gZGVuZHNvcnQoaGNsdXN0KGRpc3QodChjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeC5TTGNvcnJlY3QpLCBtZXRob2QgPSAiZXVjbGlkaWFuIiksbWV0aG9kID0gIndhcmQuRDIiKSkKcm93cy5jb3IgPC0gZGVuZHNvcnQoaGNsdXN0KGFzLmRpc3QoMS0gY29yKHQoY291bnRzLmNvbi5tZWFuLnN1YnNldC5tYXRyaXguU0xjb3JyZWN0KSwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIsIG1ldGhvZCA9ICJwZWFyc29uIikpLCBtZXRob2QgPSAiY29tcGxldGUiKSwgaXNSZXZlcnNlID0gVFJVRSkgIyMgdXNpbmcgcGVhcnNvbiBjb3JyZWxhdGlvbiB0byBzZWUgaWYgc2l0ZXMgaGF2ZSBzaW1pbGFyIGNoYW5nZSBvZiBnZW5lIGV4cHJlc3Npb24gCgoKIyMgZmlndXJlIFBBSHNfbGFzc281MF9oZWF0bWFwLnYzClBBSHNfbGFzc281MF9oZWF0bWFwLnYxIDwtCiAgSGVhdG1hcChjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeC5TTGNvcnJlY3QsIG5hbWUgPSAibG9nRkMiLCBjbHVzdGVyX2NvbHVtbnMgPSBjb2xfZGVuZCwgY2x1c3Rlcl9yb3dzID0gcm93cy5jb3IsIHNob3dfcm93X25hbWVzID0gVFJVRSwgY29sdW1uX25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDgpLAogICAgICAgICAgY29sdW1uX3RpdGxlID0gIiIsIGxlZnRfYW5ub3RhdGlvbiA9IHJvd0Fubm90YXRpb24oYFtQQUhzXWAgPSBhbm5vX3BvaW50cyhjb3VudHMuY29uLmNvbnRhbWluW3Jvdy5uYW1lcyhjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeC5TTGNvcnJlY3QpXSksIHNpdGUuUEFIcyA9IGNvdW50cy5jb24uY29udGFtaW4ubGFiZWxbcm93Lm5hbWVzKGNvdW50cy5jb24ubWVhbi5zdWJzZXQubWF0cml4LlNMY29ycmVjdCldLCBnYXAgPSB1bml0KDAuMiwgImNtIiksIAogICAgICAgICAgY29sID0gbGlzdChzaXRlLlBBSHMgPSBjKCI+IDY3dGggcGVyY2VudGlsZSIgPSAiI0U3OEFDMyIsICIzMyAtIDY3IHBlcmNlbnRpbGUiID0gIiNGQzhENjIiLCAiPCAzM3RoIHBlcmNlbnRpbGUiID0gIiM2NkMyQTUiLCAiTm9uLUFPQyIgPSAiI0E2RDg1NCIsICAiUmVmIiA9ICIjOERBMENCIikpKSwgCiAgICAgICAgICBib3R0b21fYW5ub3RhdGlvbiA9IEhlYXRtYXBBbm5vdGF0aW9uKGRmID0gUEFIcy5oZWF0bWFwLmFubm9bLGMoLTEsLTMpXSwgY29sID0gbGlzdChQQUhzLmNvZWYgPSBjb2xfZnVuLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBleHByZXNzaW9uID0gY29sb3JSYW1wMihjKDQsIDEyLjcpLCBjKCJ3aGl0ZSIsICJyZWQiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBjZWxsdWxhciBwcm9jZXNzYCA9IGMoImRvd24iID0gImJsdWUiLCAidXAiID0gInJlZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgcmVzcG9uc2UgdG8gc3RpbXVsdXNgID0gYygiZG93biIgPSAiYmx1ZSIsICJ1cCIgPSAicmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBuZWdhdGl2ZSByZWd1bGF0aW9uIG9mIGNlbGx1bGFyIHByb2Nlc3NgID0gYygiZG93biIgPSAiYmx1ZSIsICJ1cCIgPSAicmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBob21lb3N0YXRpYyBwcm9jZXNzYCA9IGMoImRvd24iID0gImJsdWUiLCAidXAiID0gInJlZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgY2VsbHVsYXIgcmVzcG9uc2UgdG8gc3RyZXNzYCA9IGMoImRvd24iID0gImJsdWUiLCAidXAiID0gInJlZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgY3l0b3NrZWxldG9uIG9yZ2FuaXphdGlvbmAgPSBjKCJkb3duIiA9ICJibHVlIiwgInVwIiA9ICJyZWQiKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfbGVnZW5kID0gYyhUUlVFLCAgRkFMU0UsIEZBTFNFLCBGQUxTRSwgRkFMU0UsIEZBTFNFLCBGQUxTRSksIGFubm90YXRpb25fbmFtZV9ncCA9IGdwYXIoZm9udHNpemUgPSAxMCkpKQpsZ2RfQnBfdGVybXMgPSBMZWdlbmQodGl0bGUgPSAiR086QlAgdGVybXMiLCBsZWdlbmRfZ3AgID0gZ3BhcihmaWxsID0gYygicmVkIiwiYmx1ZSIpKSwgCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJ1cCIsICJkb3duIikpCmRyYXcoUEFIc19sYXNzbzUwX2hlYXRtYXAudjEsIGFubm90YXRpb25fbGVnZW5kX2xpc3QgPSBsZ2RfQnBfdGVybXMsIGFkanVzdF9hbm5vdGF0aW9uX2V4dGVuc2lvbiA9IEZBTFNFKQoKIyAKIyAKIyB0aWZmKCJ+L0RvY3VtZW50cy93b3JrL1Rzd2FsbG93X2NoZW1fR0xSSV91cGRhdGUvR0xSSV9NUzJfYWxsL0FsbF9maWd1cmVzL0ZpZ3VyZTViX1BBSHNfbG9nRkNfaGVhdG1hcC50aWZmIiwgdW5pdHM9ImluIiwgd2lkdGg9OSwgaGVpZ2h0PTksIHJlcz0zMDAsIGNvbXByZXNzaW9uID0gJ2x6dycpICMjIGFzcGVjdCByYXRpbyAxLjYKIyAjIyBhZGQgbGVnZW5kIGFubm90YXRpb24KIyBkcmF3KFBBSHNfbGFzc281MF9oZWF0bWFwLnYxLCBhbm5vdGF0aW9uX2xlZ2VuZF9saXN0ID0gbGdkX0JwX3Rlcm1zLCBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIsIGFubm90YXRpb25fbGVnZW5kX3NpZGUgPSAiYm90dG9tIiwgbWVyZ2VfbGVnZW5kID0gVFJVRSkKIyBkZXYub2ZmKCkKCmBgYAoKIyMjIyBQQUggY29ycmVsYXRpb24gYmV0d2VlbiBsb2dGQywgbG9nQ1BNLCBhbmQgbW9kZWwgY29lZgoKYGBge3IgUEFIIGNvcnJlbGF0aW9uIGJldHdlZW4gbG9nRkMsIGxvZ0NQTSwgYW5kIG1vZGVsIGNvZWYsIGVjaG89RkFMU0V9CiMjIGF2ZXJhZ2UgbG9nRkMgZm9yIGxhc3NvIHNlbGVjdGVkIGdlbmVzIGJ5IGNhbGN1bGF0aW5nICBzaXRlcyB3aXRoIHRvcCBQQUhzIHRlcnRpbGUgYmFzZWQgb24gYWJvdmUgY29sX2RlbmQKUEFIc19nZW5lX2xvZ0ZDX2FicyA8LSBhYnMoY29sU3Vtcyhjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeC5TTGNvcnJlY3Rbcm93cy5jb3IkbGFiZWxzW3Jvd3MuY29yJG9yZGVyID4gMThdLF0pLzkpICMjIHBpY2sgdG9wIDEvMyBsb2dGQyBzaXRlcyAKUEFIc19nZW5lX2xvZ0NQTSA8LSBjb2xNZWFucyhjb3VudHMuY29uLm1lYW4uc3Vic2V0Lm1hdHJpeCkKUEFIc19nZW5lX2xhc3NvX2NvZWYgPC0gUEFIcy5wZXJmb3JtYW5jZS5yZXBvcnQkUEFIcy52YXJpYWJsZXNbWzJdXSRjb2VmW21hdGNoKG5hbWVzKFBBSHNfZ2VuZV9sb2dGQ19hYnMpLFBBSHMucGVyZm9ybWFuY2UucmVwb3J0JFBBSHMudmFyaWFibGVzW1syXV0kbmFtZSldCm5hbWVzKFBBSHNfZ2VuZV9sYXNzb19jb2VmKSA8LSBuYW1lcyhQQUhzX2dlbmVfbG9nRkNfYWJzKQpQQUhzX2dlbmVfaW5fbGFzc21vZGVsLm1hdHJpeCA8LSBkYXRhLmZyYW1lKGxvZ0ZDX2FicyA9IFBBSHNfZ2VuZV9sb2dGQ19hYnMsIGxvZ19jb3VudHMgPSBQQUhzX2dlbmVfbG9nQ1BNLCBQQUhzX2NvZWZfYWJzID0gYWJzKFBBSHNfZ2VuZV9sYXNzb19jb2VmKSkKbGlicmFyeSgiZ2dwbG90MiIpCmxpYnJhcnkoIkdHYWxseSIpCgpQQUhzX2xvZ0ZDX2NvcnJlbGF0aW9uX3Bsb3QgPC0KUEFIc19nZW5lX2luX2xhc3Ntb2RlbC5tYXRyaXggJT4lIGdncGFpcnModXBwZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJjb3IiLCBtZXRob2QgPSAicGVhcnNvbiIpKSkrdGhlbWVfYncoKQoKClBBSHNfbG9nRkNfY29ycmVsYXRpb25fcGxvdAojIHNhdmVSRFMoUEFIc19sb2dGQ19jb3JyZWxhdGlvbl9wbG90LCJGaWd1cmVTNGJfUEFIc19sb2dGQ19jb3JyZWxhdGlvbl9wbG90LnJkcyIpCgoKIyB0aWZmKCJ+L0RvY3VtZW50cy93b3JrL1Rzd2FsbG93X2NoZW1fR0xSSV91cGRhdGUvR0xSSV9NUzJfYWxsL0FsbF9maWd1cmVzL0ZpZ3VyZVM0Yl9QQUhzX2NvdW50c19sb2dGQ19jb2VmX2NvcnJlbGF0aW9uLnRpZmYiLCB1bml0cz0iaW4iLCB3aWR0aD01LCBoZWlnaHQ9NSwgcmVzPTMwMCwgY29tcHJlc3Npb24gPSAnbHp3JykgIyMgYXNwZWN0IHJhdGlvIDEuNgojIFBBSHNfbG9nRkNfY29ycmVsYXRpb25fcGxvdAojIGRldi5vZmYoKQoKYGBgCgojIyMjIFBBSHMgdG9wIGdlbmUgcHJlZGljdG9ycyBmdW5jdGlvbiBoZWF0bWFwIApgYGB7ciBQQUhzIHRvcCBnZW5lIHByZWRpY3RvcnMgZnVuY3Rpb24gaGVhdG1hcCwgZWNobz1GQUxTRSwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9MTF9CmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoZGVuZHNvcnQpCgpQQUhzX0dPQlBfZGF0YS5tYXRyaXgudCA9IHQoUEFIc19sYXNzb19ieV9zaXRlXzUwX2Z1bmN0aW9uYWxfZGF0YSRoZWF0bWFwX3Bsb3RfZGF0YVstMSxdKQpQQUhzX0dPQlBfZGF0YS5tYXRyaXgudDIgPSBQQUhzX0dPQlBfZGF0YS5tYXRyaXgudApQQUhzX0dPQlBfZGF0YS5tYXRyaXgudDJbUEFIc19HT0JQX2RhdGEubWF0cml4LnQyICE9IDBdID0gMQpoYSA9IHJvd0Fubm90YXRpb24oUEFILmNvZWYgPSBhbm5vX251bWVyaWMocm91bmQoYXMubnVtZXJpYyhQQUhzX2xhc3NvX2J5X3NpdGVfNTBfZnVuY3Rpb25hbF9kYXRhJGhlYXRtYXBfcGxvdF9kYXRhWyJhbGwiLF0pLCBkaWdpdHMgPTIpLCBiZ19ncCA9IGdwYXIoZmlsbCA9IGMoImdyZWVuIiwgInJlZCIpKSkgLCBhbm5vdGF0aW9uX25hbWVfcm90ID0gMCkKcm93X2RlbmQgPSBkZW5kc29ydChoY2x1c3QoZGlzdChQQUhzX0dPQlBfZGF0YS5tYXRyaXgudCkpKQpQQUhzLkgxID0gSGVhdG1hcChQQUhzX0dPQlBfZGF0YS5tYXRyaXgudDIsIG5hbWUgPSAiVG90YWwgUEFIcyAtIFxuIExhc3NvIHJlZ3Jlc3Npb24gXG4gYXZlcmFnZSBjb2VmLiIsIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMSksIGNvbHVtbl90aXRsZSA9ICJHTzpCUCIsIHNob3dfcm93X2RlbmQgPSBGQUxTRSwgY2x1c3Rlcl9yb3dzID0gcm93X2RlbmQsIAogICAgICAgICAgICAgICAgICBjb2wgPSBjKCJ3aGl0ZSIsICJibGFjayIpLCByZWN0X2dwID0gZ3Bhcihjb2wgPSAiZ3JheSIsIGx3ZCA9IDEpLHNob3dfaGVhdG1hcF9sZWdlbmQgPSBGQUxTRSwgcmlnaHRfYW5ub3RhdGlvbiA9IGhhICkKCiMgc2F2ZVJEUyhQQUhzX0dPQlBfZGF0YS5tYXRyaXgudDIsICJ+L0RvY3VtZW50cy93b3JrL1Rzd2FsbG93X2NoZW1fR0xSSV91cGRhdGUvR0xSSV9NUzJfYWxsL1BBSHNfR09CUF9kYXRhLm1hdHJpeC50Mi5yZHMiKQojIEZpZ3VyZSBTM2IKUEFIcy5IMS52MiA9IEhlYXRtYXAoUEFIc19HT0JQX2RhdGEubWF0cml4LnQyLCBuYW1lID0gIlRvdGFsIFBBSHMgLSBcbiBMYXNzbyByZWdyZXNzaW9uIFxuIGF2ZXJhZ2UgY29lZi4iLCBjb2x1bW5fbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTApLCByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gMTEpLCBjb2x1bW5fdGl0bGUgPSAiR086QlAiLCBzaG93X3Jvd19kZW5kID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICByb3dfb3JkZXIgPSBvcmRlcihhcy5udW1lcmljKFBBSHNfbGFzc29fYnlfc2l0ZV81MF9mdW5jdGlvbmFsX2RhdGEkaGVhdG1hcF9wbG90X2RhdGFbImFsbCIsXSkpLCBjb2wgPSBjKCJ3aGl0ZSIsICJibGFjayIpLCByZWN0X2dwID0gZ3Bhcihjb2wgPSAiZ3JheSIsIGx3ZCA9IDEpLHNob3dfaGVhdG1hcF9sZWdlbmQgPSBGQUxTRSwgcmlnaHRfYW5ub3RhdGlvbiA9IGhhICkKClBBSHMuSDEudjIKCmBgYAoKIyMjIFNlc3Npb25JbmZvCmBgYHtyIHNlc3Npb25JbmZvfQpzZXNzaW9uSW5mbygpIApgYGAKCg==