This notebook conducts and demonstrates the results of the Random-effects meta-analysis performed on each healthcare system’s Cox-proportional hazards (Cox-PH) model results.

Of note, this analysis is performed only on adult patients due to the pediatric cohort’s low incidence of both poor health outcomes and neurological diagnoses during COVID-19 hospitalization.

library(tidyverse)
library(metafor)
library(meta)
library(cowplot)
source("R/forester_custom.R")
source("R/analyze_survival.R")

Import Data from each Healthcare system

Each participating healthcare system ran the analysis locally on patient level data using our customized R package. The output of each local analysis consisted of only the summary results (rather than patient level data). The model summary results from each healthcare system were then aggregated and are imported and then combined using the following code:

# read in files from results folder 
# this folder contains all of the local healthcare system level analyses
rdas <- list.files(
  path = "results",
  pattern = ".rda",
  full.names = TRUE
)
for (rda in rdas) {
  load(rda)
}

rm(rdas, rda)

# create a list of participating healthcare systems from our study tracking spreadsheet
site_google_url <- "https://docs.google.com/spreadsheets/d/1epcYNd_0jCUMktOHf8mz5v651zy1JALD6PgzobrGWDY/edit?usp=sharing"

# load site parameters
site_params <- googlesheets4::read_sheet(site_google_url, sheet = 1)
site_avails <- googlesheets4::read_sheet(site_google_url, sheet = 2)

# filter the list of sites who ran the analysis
sorted_sites <- site_avails %>%
  filter(!is.na(date_v4_received)) %>%
  pull(siteid) %>%
  paste("results", sep = "_")

# list sites without race
sites_wo_race <- site_params %>%
  filter(!include_race) %>%
  pull(siteid)

# combine all rda files with 'results' in name
results <- mget(ls(pattern = "results"))

## read in pt counts
pt_counts_df <- read.csv('tables/site_pt_counts.csv')

Pre-processing

Define parameters to specify models to evaluate

During the locally run analysis, each healthcare system ran a series of Cox-PH models in order to perform supplementary analysis to evaluate the optimal comorbidity adjustment method and censoring cutoff point.

outcomes <- c("time_first_discharge_reg_elix", "deceased_reg_elix")

comorb_adj <- c("lpca", "score", "ind")

censor_cut <- c("30", "60", "90")

Identify sites to include in the analysis

In this analysis, we only included a healthcare system if they had >= 3 adult patients with a neurological diagnosis.

sites_adult <- pt_counts_df %>% 
  filter(population == "Adult") %>% 
  mutate(neuro_sum = n_var_Central + n_var_Peripheral) %>% 
  filter(!neuro_sum < 3) %>% 
  distinct(site) %>% 
  mutate(site = gsub("_results", "", site))

Random-Effects Meta-Analysis

Get Cox-PH model results

This notebook specifies the parameters to load in the Cox-PH results using a censor_cutoff=‘90’ (days since initial hospitalization) and the comorb_method=‘ind’ which adjusts for patient comorbidity burden by treating each individual comorbidity of the Elixhauser Comorbidity Index as an individual model covariate (binary variable indicating whether or not the patient previously was diagnosed with the respective comorbidity).

cox_results_adults <- list()

for (outcome_i in outcomes) {
  cox_results_adults[[outcome_i]] <-
    results %>%
    lapply(get_cox_row, population = "adults", comorb_method = "ind", censor_cutoff = "90", outcome = outcome_i) %>%
    bind_rows() %>%
    mutate(outcome = outcome_i)
}

Random-effects meta-analysis - CNS

Next, we will perform a random-effects meta-analysis to evaluate the risk of mortality or prolonged hospital stay in the CNS group vs the NNC group. The following code will compute the random-effects model for each outcome (mortality and discharge).

Notes:

  • Set sm = "HR" when estimate is logHR (which is coef in our model).

    • Search logHR in cran to see an example
## adults 
meta_results_adults_cns <- list()

for (outcome_i in outcomes) {
  meta_results_adults_cns[[outcome_i]] <-
    cox_results_adults[[outcome_i]] %>%
    filter(site %in% sites_adult$site) %>% 
    bind_rows() %>%
    data.frame() %>%
    filter(variable == "neuro_postCentral") %>%
    metagen(
      TE = coef,
      seTE = se.coef.,
      data = .,
      sm = "HR", # hazard ratios
      comb.random = TRUE,
      comb.fixed = FALSE,
      method.tau = "DL", # default tau method
      hakn = FALSE,
      prediction = TRUE,
      studlab = site
    )
}

Random-effects meta-analysis - PNS

Next, we will perform a random-effects meta-analysis to evaluate the risk of mortality or prolonged hospital stay in the PNS group vs the NNC group. The following code will compute the random-effects model for each outcome (mortality and discharge).

## adults
meta_results_adults_pns <- list()

for (outcome_i in outcomes) {
  meta_results_adults_pns[[outcome_i]] <-
    cox_results_adults[[outcome_i]] %>%
    filter(site %in% sites_adult$site) %>% 
    bind_rows() %>%
    data.frame() %>%
    filter(variable == "neuro_postPeripheral") %>%
    metagen(
      TE = coef,
      seTE = se.coef.,
      data = .,
      sm = "HR", # hazard ratios
      comb.random = TRUE,
      comb.fixed = FALSE,
      method.tau = "DL", # default tau method
      hakn = FALSE,
      prediction = TRUE,
      studlab = site
    )
}

Format meta-analysis results

Here we will format the output of the meta-analysis for both CNS and PNS models

# cns
meta_results_adults_cns_df <- format_meta(meta_results_adults_cns)

# pns
meta_results_adults_pns_df <- format_meta(meta_results_adults_pns)

Save model weights to generate the survival curves

Next, we will save the model weights for each healthcare system in order to help us generate the associated survival curves in our downstream analysis.

The benefit of the random-effects model is that it allows us to weight each healthcare system by considering the healthcare system variance \({s^2}\) and the between healthcare system variance \(tau^2\).

4.1.2.1 Estimators of the Between-Study Heterogeneity: https://bookdown.org/MathiasHarrer/Doing_Meta_Analysis_in_R/pooling-es.html

“inverse of the variance is used to determine the weight of each study”

  • where “study” in our analysis is the healthcare system

For random effects, the calculation is performed by calculating an adjusted random-effects weight w∗k for each observation. Where k is the healthcare system:

\(w^*_k = \frac{1}{s^2 + tau^2}\)

# save all cns weights
for (outcome_i in outcomes) {
  
  save_weights(meta_results_df = meta_results_adults_cns_df,
               population = "adults", 
               outcome = outcome_i, 
               comorb_method = "ind", 
               censor_cutoff = "90",
               cns_pns = "CNS")
}


# save all pns weights
for (outcome_i in outcomes) {
  
  save_weights(meta_results_df = meta_results_adults_pns_df,
               population = "adults", 
               outcome = outcome_i, 
               comorb_method = "ind", 
               censor_cutoff = "90",
               cns_pns = "PNS")
}

Analyze Risk via Forest Plots

Prepare data for forest plots

# adults
meta_results_adults_cns_df_forest = prepare_data_forest_plots(meta_results_adults_cns_df)
meta_results_adults_pns_df_forest = prepare_data_forest_plots(meta_results_adults_pns_df)

Tidy up results

meta_results_adults_cns_df_forest_tidy <- meta_results_adults_cns_df_forest %>% 
  distinct(analysis, TE.random, lower.random, upper.random,`p-value`, lower.predict, upper.predict, I2, lower.I2, upper.I2, H, lower.H, upper.H, tau2, lower.tau2, upper.tau2, CI) %>% 
  mutate(neuro_status = "CNS") %>% 
  select(neuro_status, everything())

meta_results_adults_pns_df_forest_tidy <- meta_results_adults_pns_df_forest %>% 
  distinct(analysis, TE.random, lower.random, upper.random,`p-value`, lower.predict, upper.predict, I2, lower.I2, upper.I2, H, lower.H, upper.H, tau2, lower.tau2, upper.tau2, CI) %>% 
  mutate(neuro_status = "PNS") %>% 
  select(neuro_status, everything())

meta_results_tidy <- rbind(meta_results_adults_cns_df_forest_tidy, 
                           meta_results_adults_pns_df_forest_tidy)

write.csv(meta_results_tidy, "tables/Table2_meta_results.csv", row.names = FALSE)

Next, we will create a scale function to identify the min and max CI for each healthcare system. This will help us exclude healthcare systems with values of ‘Inf’ or very large confidence intervals.

scale_plot <- function(meta_results_df_forest, site_exclude = NULL) {
  scale <- meta_results_df_forest %>%
    filter(
      !Site %in% site_exclude,
      #!analysis %in% invalid_sites,
      !CI.Low == "Inf",
      !CI.High == "Inf"
    ) %>%
    mutate(
      min_est = min(CI.Low, na.rm = TRUE) - 1,
      max_est = max(CI.High, na.rm = TRUE) + 1,
      min_est = if_else(min_est + 1 >= -1, min(CI.Low, na.rm = TRUE)-0.1, min_est),
      min_est = if_else(min_est > 1, 0.5, min_est),
      max_est = if_else(max_est - 1 < 1, 1.1, max_est)) %>%
    distinct(min_est, max_est)

  return(scale)
}

CNS - Risk of Discharge

te.random = meta_results_adults_cns_df_forest %>% 
  filter(analysis == "time_first_discharge_reg_elix") %>% 
  distinct(TE.random) %>% 
  as.numeric() %>% 
  round(.,2)

te.random.ci_lower = meta_results_adults_cns_df_forest %>% 
  filter(analysis == "time_first_discharge_reg_elix") %>% 
  distinct(lower.random) %>% 
  as.numeric() 

te.random.ci_upper = meta_results_adults_cns_df_forest %>% 
  filter(analysis == "time_first_discharge_reg_elix") %>% 
  distinct(upper.random) %>% 
  as.numeric() 

length_stay_first_results <- meta_results_adults_cns_df_forest %>%
  slice(grep(paste("time_first_discharge_reg_elix", collapse = "|"), analysis)) %>% 
  #add_row(.before = 1) %>%
  add_row(.after = nrow(.)) %>% 
  mutate(Site = ifelse(is.na(Site), "Effect size", Site),
         `Hazard Ratio` = ifelse(is.na(`Hazard Ratio`), round(te.random, 2), `Hazard Ratio`),
         `p-value` = ifelse(`p-value` < 0.001, "< .001*", round(`p-value`, 3))) %>% 
  fill(`p-value`, .direction = "down") %>% 
  mutate(`p-value` = ifelse(Site == "Effect size", `p-value`, ""),
         CI.Low = ifelse(Site == "Effect size", te.random.ci_lower, CI.Low),
         CI.High = ifelse(Site == "Effect size", te.random.ci_upper, CI.High),
         `Hazard Ratio ` = paste(`Hazard Ratio`, '(', CI.Low, ',', CI.High, ')'))


## define forest plot shapes
# shape #16 is normal circle; #18 is diamond
shapes <- rep(16, times = nrow(length_stay_first_results))
shapes[length(shapes)] <- 18
sizes <- rep(3.25, times = nrow(length_stay_first_results))
sizes[length(sizes)] <- 5
scale <- scale_plot(length_stay_first_results, site_exclude = c("ICSM"))

# note: `Hazard Ratio ` with confidence intervals has an extra space and used by `right_side_data` param
p1 <- forester(
  left_side_data = length_stay_first_results %>% 
    select(Site) %>% 
    rename("Healthcare System" = "Site"),
  estimate = length_stay_first_results$`Hazard Ratio`,
  ci_low = length_stay_first_results$CI.Low,
  ci_high = length_stay_first_results$CI.High,
  right_side_data = length_stay_first_results[, c("Hazard Ratio ", "p-value")],
  display = TRUE,
  nudge_x = .5,
  font_family = "arial",
  arrows = TRUE,
  arrow_labels = c("Decreased Risk", "Increased Risk"),
  null_line_at = 1,
  xlim = c(scale$min_est, scale$max_est),
  #xbreaks = c(scale$min_est, 1, scale$max_est),
  point_sizes = sizes,
  point_shapes = shapes,
  file_path = here::here(paste0("figures/forestplot_adult_cns_discharge.png")))

p1

CNS - Risk of Mortality

te.random = meta_results_adults_cns_df_forest %>% 
  filter(analysis == "deceased_reg_elix") %>% 
  distinct(TE.random) %>% 
  as.numeric() %>% 
  round(.,2)

te.random.ci_lower = meta_results_adults_cns_df_forest %>% 
  filter(analysis == "deceased_reg_elix") %>% 
  distinct(lower.random) %>% 
  as.numeric() 

te.random.ci_upper = meta_results_adults_cns_df_forest %>% 
  filter(analysis == "deceased_reg_elix") %>% 
  distinct(upper.random) %>% 
  as.numeric() 

deceased_results <- meta_results_adults_cns_df_forest %>%
  slice(grep(paste("deceased_reg_elix", collapse = "|"), analysis)) %>% 
  #add_row(.before = 1) %>%
  add_row(.after = nrow(.)) %>% 
  mutate(Site = ifelse(is.na(Site), "Effect size", Site),
         `Hazard Ratio` = ifelse(is.na(`Hazard Ratio`), round(te.random, 2), `Hazard Ratio`),
         `p-value` = ifelse(`p-value` < 0.001, "< .001*", round(`p-value`, 3))) %>% 
  fill(`p-value`, .direction = "down") %>% 
  mutate(`p-value` = ifelse(Site == "Effect size", `p-value`, ""),
         CI.Low = ifelse(Site == "Effect size", te.random.ci_lower, CI.Low),
         CI.High = ifelse(Site == "Effect size", te.random.ci_upper, CI.High),
         `Hazard Ratio ` = paste(`Hazard Ratio`, '(', CI.Low, ',', CI.High, ')'))


## define forest plot shapes
# shape #16 is normal circle; #18 is diamond
shapes <- rep(16, times = nrow(deceased_results))
shapes[length(shapes)] <- 18
sizes <- rep(3.25, times = nrow(deceased_results))
sizes[length(sizes)] <- 5
scale <- scale_plot(deceased_results, site_exclude = c("ICSM"))

# note: `Hazard Ratio ` with confidence intervals has an extra space and used by `right_side_data` param
p2 <- forester(
  left_side_data = deceased_results %>% 
    select(Site) %>% 
    rename("Healthcare System" = "Site"),
  estimate = deceased_results$`Hazard Ratio`,
  ci_low = deceased_results$CI.Low,
  ci_high = deceased_results$CI.High,
  right_side_data = deceased_results[, c("Hazard Ratio ", "p-value")],
  display = TRUE,
  nudge_x = .5,
  font_family = "arial",
  arrows = TRUE,
  arrow_labels = c("Decreased Risk", "Increased Risk"),
  null_line_at = 1,
  xlim = c(scale$min_est, scale$max_est),
  #xbreaks = c(scale$min_est, 1, scale$max_est),
  point_sizes = sizes,
  point_shapes = shapes,
  file_path = here::here(paste0("figures/forestplot_adult_cns_mortality.png")))

p2

PNS - Risk of Discharge

te.random = meta_results_adults_pns_df_forest %>% 
  filter(analysis == "time_first_discharge_reg_elix") %>% 
  distinct(TE.random) %>% 
  as.numeric() %>% 
  round(.,2)

te.random.ci_lower = meta_results_adults_pns_df_forest %>% 
  filter(analysis == "time_first_discharge_reg_elix") %>% 
  distinct(lower.random) %>% 
  as.numeric() 

te.random.ci_upper = meta_results_adults_pns_df_forest %>% 
  filter(analysis == "time_first_discharge_reg_elix") %>% 
  distinct(upper.random) %>% 
  as.numeric() 

length_stay_first_results <- meta_results_adults_pns_df_forest %>%
  slice(grep(paste("time_first_discharge_reg_elix", collapse = "|"), analysis)) %>% 
  #add_row(.before = 1) %>%
  add_row(.after = nrow(.)) %>% 
  mutate(Site = ifelse(is.na(Site), "Effect size", Site),
         `Hazard Ratio` = ifelse(is.na(`Hazard Ratio`), round(te.random, 2), `Hazard Ratio`),
         `p-value` = ifelse(`p-value` < 0.001, "< .001*", round(`p-value`, 3))) %>% 
  fill(`p-value`, .direction = "down") %>% 
  mutate(`p-value` = ifelse(Site == "Effect size", `p-value`, ""),
         CI.Low = ifelse(Site == "Effect size", te.random.ci_lower, CI.Low),
         CI.High = ifelse(Site == "Effect size", te.random.ci_upper, CI.High),
         `Hazard Ratio ` = paste(`Hazard Ratio`, '(', CI.Low, ',', CI.High, ')'))


## define forest plot shapes
# shape #16 is normal circle; #18 is diamond
shapes <- rep(16, times = nrow(length_stay_first_results))
shapes[length(shapes)] <- 18
sizes <- rep(3.25, times = nrow(length_stay_first_results))
sizes[length(sizes)] <- 5
scale <- scale_plot(length_stay_first_results, site_exclude = c("ICSM"))

# note: `Hazard Ratio ` with confidence intervals has an extra space and used by `right_side_data` param
p3 <- forester(
  left_side_data = length_stay_first_results %>% 
    select(Site) %>% 
    rename("Healthcare System" = "Site"),
  estimate = length_stay_first_results$`Hazard Ratio`,
  ci_low = length_stay_first_results$CI.Low,
  ci_high = length_stay_first_results$CI.High,
  right_side_data = length_stay_first_results[, c("Hazard Ratio ", "p-value")],
  display = TRUE,
  nudge_x = .5,
  font_family = "arial",
  arrows = TRUE,
  arrow_labels = c("Decreased Risk", "Increased Risk"),
  null_line_at = 1,
  xlim = c(scale$min_est, scale$max_est),
  #xbreaks = c(scale$min_est, 1, scale$max_est),
  point_sizes = sizes,
  point_shapes = shapes,
  file_path = here::here(paste0("figures/forestplot_adult_pns_discharge.png")))

p3

PNS - Risk of Mortality

te.random = meta_results_adults_pns_df_forest %>% 
  filter(analysis == "deceased_reg_elix") %>% 
  distinct(TE.random) %>% 
  as.numeric() %>% 
  round(.,2)

te.random.ci_lower = meta_results_adults_pns_df_forest %>% 
  filter(analysis == "deceased_reg_elix") %>% 
  distinct(lower.random) %>% 
  as.numeric() 

te.random.ci_upper = meta_results_adults_pns_df_forest %>% 
  filter(analysis == "deceased_reg_elix") %>% 
  distinct(upper.random) %>% 
  as.numeric() 

deceased_results <- meta_results_adults_pns_df_forest %>%
  slice(grep(paste("deceased_reg_elix", collapse = "|"), analysis)) %>% 
  #add_row(.before = 1) %>%
  add_row(.after = nrow(.)) %>% 
  mutate(Site = ifelse(is.na(Site), "Effect size", Site),
         `Hazard Ratio` = ifelse(is.na(`Hazard Ratio`), round(te.random, 2), `Hazard Ratio`),
         `p-value` = ifelse(`p-value` < 0.001, "< .001*", round(`p-value`, 3))) %>% 
  fill(`p-value`, .direction = "down") %>% 
  mutate(`p-value` = ifelse(Site == "Effect size", `p-value`, ""),
         CI.Low = ifelse(Site == "Effect size", te.random.ci_lower, CI.Low),
         CI.High = ifelse(Site == "Effect size", te.random.ci_upper, CI.High),
         `Hazard Ratio ` = paste(`Hazard Ratio`, '(', CI.Low, ',', CI.High, ')'))


## define forest plot shapes
# shape #16 is normal circle; #18 is diamond
shapes <- rep(16, times = nrow(deceased_results))
shapes[length(shapes)] <- 18
sizes <- rep(3.25, times = nrow(deceased_results))
sizes[length(sizes)] <- 5
scale <- scale_plot(deceased_results, site_exclude = c("ICSM", "HPG23"))

# note: `Hazard Ratio ` with confidence intervals has an extra space and used by `right_side_data` param
p4 <- forester(
  left_side_data = deceased_results %>% 
    select(Site) %>% 
    rename("Healthcare System" = "Site"),
  estimate = deceased_results$`Hazard Ratio`,
  ci_low = deceased_results$CI.Low,
  ci_high = deceased_results$CI.High,
  right_side_data = deceased_results[, c("Hazard Ratio ", "p-value")],
  display = TRUE,
  nudge_x = .5,
  font_family = "arial",
  arrows = TRUE,
  arrow_labels = c("Decreased Risk", "Increased Risk"),
  null_line_at = 1,
  xlim = c(scale$min_est, scale$max_est),
  #xbreaks = c(scale$min_est, 1, scale$max_est),
  point_sizes = sizes,
  point_shapes = shapes,
  file_path = here::here(paste0("figures/forestplot_adult_pns_mortality.png")))

p4

LS0tDQp0aXRsZTogIk1ldGEtQW5hbHlzaXM6IENveC1QSCBNb2RlbHMiDQpkYXRlOiAiMDQvMTEvMjAyMyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHNwYWNlbGFiDQotLS0NCg0KVGhpcyBub3RlYm9vayBjb25kdWN0cyBhbmQgZGVtb25zdHJhdGVzIHRoZSByZXN1bHRzIG9mIHRoZSBSYW5kb20tZWZmZWN0cyBtZXRhLWFuYWx5c2lzIHBlcmZvcm1lZCBvbiBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtJ3MgQ294LXByb3BvcnRpb25hbCBoYXphcmRzIChDb3gtUEgpIG1vZGVsIHJlc3VsdHMuDQoNCk9mIG5vdGUsIHRoaXMgYW5hbHlzaXMgaXMgcGVyZm9ybWVkIG9ubHkgb24gYWR1bHQgcGF0aWVudHMgZHVlIHRvIHRoZSBwZWRpYXRyaWMgY29ob3J0J3MgbG93IGluY2lkZW5jZSBvZiBib3RoIHBvb3IgaGVhbHRoIG91dGNvbWVzIGFuZCBuZXVyb2xvZ2ljYWwgZGlhZ25vc2VzIGR1cmluZyBDT1ZJRC0xOSBob3NwaXRhbGl6YXRpb24uDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobWV0YWZvcikNCmxpYnJhcnkobWV0YSkNCmxpYnJhcnkoY293cGxvdCkNCnNvdXJjZSgiUi9mb3Jlc3Rlcl9jdXN0b20uUiIpDQpzb3VyY2UoIlIvYW5hbHl6ZV9zdXJ2aXZhbC5SIikNCmBgYA0KDQojICoqSW1wb3J0IERhdGEgZnJvbSBlYWNoIEhlYWx0aGNhcmUgc3lzdGVtKioNCg0KRWFjaCBwYXJ0aWNpcGF0aW5nIGhlYWx0aGNhcmUgc3lzdGVtIHJhbiB0aGUgYW5hbHlzaXMgbG9jYWxseSBvbiBwYXRpZW50IGxldmVsIGRhdGEgdXNpbmcgb3VyIFtjdXN0b21pemVkIFIgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2NvdmlkY2xpbmljYWwvUGhhc2UyLjFOZXVyb1JQYWNrYWdlKS4gVGhlIG91dHB1dCBvZiBlYWNoIGxvY2FsIGFuYWx5c2lzIGNvbnNpc3RlZCBvZiBvbmx5IHRoZSBzdW1tYXJ5IHJlc3VsdHMgKHJhdGhlciB0aGFuIHBhdGllbnQgbGV2ZWwgZGF0YSkuIFRoZSBtb2RlbCBzdW1tYXJ5IHJlc3VsdHMgZnJvbSBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtIHdlcmUgdGhlbiBhZ2dyZWdhdGVkIGFuZCBhcmUgaW1wb3J0ZWQgYW5kIHRoZW4gY29tYmluZWQgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyByZWFkIGluIGZpbGVzIGZyb20gcmVzdWx0cyBmb2xkZXIgDQojIHRoaXMgZm9sZGVyIGNvbnRhaW5zIGFsbCBvZiB0aGUgbG9jYWwgaGVhbHRoY2FyZSBzeXN0ZW0gbGV2ZWwgYW5hbHlzZXMNCnJkYXMgPC0gbGlzdC5maWxlcygNCiAgcGF0aCA9ICJyZXN1bHRzIiwNCiAgcGF0dGVybiA9ICIucmRhIiwNCiAgZnVsbC5uYW1lcyA9IFRSVUUNCikNCmZvciAocmRhIGluIHJkYXMpIHsNCiAgbG9hZChyZGEpDQp9DQoNCnJtKHJkYXMsIHJkYSkNCg0KIyBjcmVhdGUgYSBsaXN0IG9mIHBhcnRpY2lwYXRpbmcgaGVhbHRoY2FyZSBzeXN0ZW1zIGZyb20gb3VyIHN0dWR5IHRyYWNraW5nIHNwcmVhZHNoZWV0DQpzaXRlX2dvb2dsZV91cmwgPC0gImh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFlcGNZTmRfMGpDVU1rdE9IZjhtejV2NjUxenkxSkFMRDZQZ3pvYnJHV0RZL2VkaXQ/dXNwPXNoYXJpbmciDQoNCiMgbG9hZCBzaXRlIHBhcmFtZXRlcnMNCnNpdGVfcGFyYW1zIDwtIGdvb2dsZXNoZWV0czQ6OnJlYWRfc2hlZXQoc2l0ZV9nb29nbGVfdXJsLCBzaGVldCA9IDEpDQpzaXRlX2F2YWlscyA8LSBnb29nbGVzaGVldHM0OjpyZWFkX3NoZWV0KHNpdGVfZ29vZ2xlX3VybCwgc2hlZXQgPSAyKQ0KDQojIGZpbHRlciB0aGUgbGlzdCBvZiBzaXRlcyB3aG8gcmFuIHRoZSBhbmFseXNpcw0Kc29ydGVkX3NpdGVzIDwtIHNpdGVfYXZhaWxzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGRhdGVfdjRfcmVjZWl2ZWQpKSAlPiUNCiAgcHVsbChzaXRlaWQpICU+JQ0KICBwYXN0ZSgicmVzdWx0cyIsIHNlcCA9ICJfIikNCg0KIyBsaXN0IHNpdGVzIHdpdGhvdXQgcmFjZQ0Kc2l0ZXNfd29fcmFjZSA8LSBzaXRlX3BhcmFtcyAlPiUNCiAgZmlsdGVyKCFpbmNsdWRlX3JhY2UpICU+JQ0KICBwdWxsKHNpdGVpZCkNCg0KIyBjb21iaW5lIGFsbCByZGEgZmlsZXMgd2l0aCAncmVzdWx0cycgaW4gbmFtZQ0KcmVzdWx0cyA8LSBtZ2V0KGxzKHBhdHRlcm4gPSAicmVzdWx0cyIpKQ0KDQojIyByZWFkIGluIHB0IGNvdW50cw0KcHRfY291bnRzX2RmIDwtIHJlYWQuY3N2KCd0YWJsZXMvc2l0ZV9wdF9jb3VudHMuY3N2JykNCmBgYA0KIyAqKlByZS1wcm9jZXNzaW5nKioNCg0KIyMgRGVmaW5lIHBhcmFtZXRlcnMgdG8gc3BlY2lmeSBtb2RlbHMgdG8gZXZhbHVhdGUNCg0KRHVyaW5nIHRoZSBsb2NhbGx5IHJ1biBhbmFseXNpcywgZWFjaCBoZWFsdGhjYXJlIHN5c3RlbSByYW4gYSBzZXJpZXMgb2YgQ294LVBIIG1vZGVscyBpbiBvcmRlciB0byBwZXJmb3JtIHN1cHBsZW1lbnRhcnkgYW5hbHlzaXMgdG8gZXZhbHVhdGUgdGhlIG9wdGltYWwgY29tb3JiaWRpdHkgYWRqdXN0bWVudCBtZXRob2QgYW5kIGNlbnNvcmluZyBjdXRvZmYgcG9pbnQuDQoNCmBgYHtyfQ0Kb3V0Y29tZXMgPC0gYygidGltZV9maXJzdF9kaXNjaGFyZ2VfcmVnX2VsaXgiLCAiZGVjZWFzZWRfcmVnX2VsaXgiKQ0KDQpjb21vcmJfYWRqIDwtIGMoImxwY2EiLCAic2NvcmUiLCAiaW5kIikNCg0KY2Vuc29yX2N1dCA8LSBjKCIzMCIsICI2MCIsICI5MCIpDQpgYGANCg0KIyMgSWRlbnRpZnkgc2l0ZXMgdG8gaW5jbHVkZSBpbiB0aGUgYW5hbHlzaXMNCg0KSW4gdGhpcyBhbmFseXNpcywgd2Ugb25seSBpbmNsdWRlZCBhIGhlYWx0aGNhcmUgc3lzdGVtIGlmIHRoZXkgaGFkIFw+PSAzIGFkdWx0IHBhdGllbnRzIHdpdGggYSBuZXVyb2xvZ2ljYWwgZGlhZ25vc2lzLg0KDQpgYGB7cn0NCnNpdGVzX2FkdWx0IDwtIHB0X2NvdW50c19kZiAlPiUgDQogIGZpbHRlcihwb3B1bGF0aW9uID09ICJBZHVsdCIpICU+JSANCiAgbXV0YXRlKG5ldXJvX3N1bSA9IG5fdmFyX0NlbnRyYWwgKyBuX3Zhcl9QZXJpcGhlcmFsKSAlPiUgDQogIGZpbHRlcighbmV1cm9fc3VtIDwgMykgJT4lIA0KICBkaXN0aW5jdChzaXRlKSAlPiUgDQogIG11dGF0ZShzaXRlID0gZ3N1YigiX3Jlc3VsdHMiLCAiIiwgc2l0ZSkpDQpgYGANCg0KIyAqKlJhbmRvbS1FZmZlY3RzIE1ldGEtQW5hbHlzaXMqKg0KDQojIyBHZXQgQ294LVBIIG1vZGVsIHJlc3VsdHMNCg0KVGhpcyBub3RlYm9vayBzcGVjaWZpZXMgdGhlIHBhcmFtZXRlcnMgdG8gbG9hZCBpbiB0aGUgQ294LVBIIHJlc3VsdHMgdXNpbmcgYSBgY2Vuc29yX2N1dG9mZmA9JzkwJyAoZGF5cyBzaW5jZSBpbml0aWFsIGhvc3BpdGFsaXphdGlvbikgYW5kIHRoZSBgY29tb3JiX21ldGhvZGA9J2luZCcgd2hpY2ggYWRqdXN0cyBmb3IgcGF0aWVudCBjb21vcmJpZGl0eSBidXJkZW4gYnkgdHJlYXRpbmcgZWFjaCBpbmRpdmlkdWFsIGNvbW9yYmlkaXR5IG9mIHRoZSBFbGl4aGF1c2VyIENvbW9yYmlkaXR5IEluZGV4IGFzIGFuIGluZGl2aWR1YWwgbW9kZWwgY292YXJpYXRlIChiaW5hcnkgdmFyaWFibGUgaW5kaWNhdGluZyB3aGV0aGVyIG9yIG5vdCB0aGUgcGF0aWVudCBwcmV2aW91c2x5IHdhcyBkaWFnbm9zZWQgd2l0aCB0aGUgcmVzcGVjdGl2ZSBjb21vcmJpZGl0eSkuDQoNCmBgYHtyfQ0KY294X3Jlc3VsdHNfYWR1bHRzIDwtIGxpc3QoKQ0KDQpmb3IgKG91dGNvbWVfaSBpbiBvdXRjb21lcykgew0KICBjb3hfcmVzdWx0c19hZHVsdHNbW291dGNvbWVfaV1dIDwtDQogICAgcmVzdWx0cyAlPiUNCiAgICBsYXBwbHkoZ2V0X2NveF9yb3csIHBvcHVsYXRpb24gPSAiYWR1bHRzIiwgY29tb3JiX21ldGhvZCA9ICJpbmQiLCBjZW5zb3JfY3V0b2ZmID0gIjkwIiwgb3V0Y29tZSA9IG91dGNvbWVfaSkgJT4lDQogICAgYmluZF9yb3dzKCkgJT4lDQogICAgbXV0YXRlKG91dGNvbWUgPSBvdXRjb21lX2kpDQp9DQpgYGANCg0KIyMgUmFuZG9tLWVmZmVjdHMgbWV0YS1hbmFseXNpcyAtIENOUw0KDQpOZXh0LCB3ZSB3aWxsIHBlcmZvcm0gYSByYW5kb20tZWZmZWN0cyBtZXRhLWFuYWx5c2lzIHRvIGV2YWx1YXRlIHRoZSByaXNrIG9mIG1vcnRhbGl0eSBvciBwcm9sb25nZWQgaG9zcGl0YWwgc3RheSBpbiB0aGUgQ05TIGdyb3VwIHZzIHRoZSBOTkMgZ3JvdXAuIFRoZSBmb2xsb3dpbmcgY29kZSB3aWxsIGNvbXB1dGUgdGhlIHJhbmRvbS1lZmZlY3RzIG1vZGVsIGZvciBlYWNoIG91dGNvbWUgKG1vcnRhbGl0eSBhbmQgZGlzY2hhcmdlKS4NCg0KTm90ZXM6DQoNCi0gICBTZXQgYHNtID0gIkhSImAgd2hlbiBlc3RpbWF0ZSBpcyBsb2dIUiAod2hpY2ggaXMgY29lZiBpbiBvdXIgbW9kZWwpLg0KDQogICAgLSAgIFNlYXJjaCBsb2dIUiBpbiBbY3Jhbl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21ldGEvbWV0YS5wZGYpIHRvIHNlZSBhbiBleGFtcGxlDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyBhZHVsdHMgDQptZXRhX3Jlc3VsdHNfYWR1bHRzX2NucyA8LSBsaXN0KCkNCg0KZm9yIChvdXRjb21lX2kgaW4gb3V0Y29tZXMpIHsNCiAgbWV0YV9yZXN1bHRzX2FkdWx0c19jbnNbW291dGNvbWVfaV1dIDwtDQogICAgY294X3Jlc3VsdHNfYWR1bHRzW1tvdXRjb21lX2ldXSAlPiUNCiAgICBmaWx0ZXIoc2l0ZSAlaW4lIHNpdGVzX2FkdWx0JHNpdGUpICU+JSANCiAgICBiaW5kX3Jvd3MoKSAlPiUNCiAgICBkYXRhLmZyYW1lKCkgJT4lDQogICAgZmlsdGVyKHZhcmlhYmxlID09ICJuZXVyb19wb3N0Q2VudHJhbCIpICU+JQ0KICAgIG1ldGFnZW4oDQogICAgICBURSA9IGNvZWYsDQogICAgICBzZVRFID0gc2UuY29lZi4sDQogICAgICBkYXRhID0gLiwNCiAgICAgIHNtID0gIkhSIiwgIyBoYXphcmQgcmF0aW9zDQogICAgICBjb21iLnJhbmRvbSA9IFRSVUUsDQogICAgICBjb21iLmZpeGVkID0gRkFMU0UsDQogICAgICBtZXRob2QudGF1ID0gIkRMIiwgIyBkZWZhdWx0IHRhdSBtZXRob2QNCiAgICAgIGhha24gPSBGQUxTRSwNCiAgICAgIHByZWRpY3Rpb24gPSBUUlVFLA0KICAgICAgc3R1ZGxhYiA9IHNpdGUNCiAgICApDQp9DQpgYGANCg0KIyMgUmFuZG9tLWVmZmVjdHMgbWV0YS1hbmFseXNpcyAtIFBOUw0KDQpOZXh0LCB3ZSB3aWxsIHBlcmZvcm0gYSByYW5kb20tZWZmZWN0cyBtZXRhLWFuYWx5c2lzIHRvIGV2YWx1YXRlIHRoZSByaXNrIG9mIG1vcnRhbGl0eSBvciBwcm9sb25nZWQgaG9zcGl0YWwgc3RheSBpbiB0aGUgUE5TIGdyb3VwIHZzIHRoZSBOTkMgZ3JvdXAuIFRoZSBmb2xsb3dpbmcgY29kZSB3aWxsIGNvbXB1dGUgdGhlIHJhbmRvbS1lZmZlY3RzIG1vZGVsIGZvciBlYWNoIG91dGNvbWUgKG1vcnRhbGl0eSBhbmQgZGlzY2hhcmdlKS4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIGFkdWx0cw0KbWV0YV9yZXN1bHRzX2FkdWx0c19wbnMgPC0gbGlzdCgpDQoNCmZvciAob3V0Y29tZV9pIGluIG91dGNvbWVzKSB7DQogIG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zW1tvdXRjb21lX2ldXSA8LQ0KICAgIGNveF9yZXN1bHRzX2FkdWx0c1tbb3V0Y29tZV9pXV0gJT4lDQogICAgZmlsdGVyKHNpdGUgJWluJSBzaXRlc19hZHVsdCRzaXRlKSAlPiUgDQogICAgYmluZF9yb3dzKCkgJT4lDQogICAgZGF0YS5mcmFtZSgpICU+JQ0KICAgIGZpbHRlcih2YXJpYWJsZSA9PSAibmV1cm9fcG9zdFBlcmlwaGVyYWwiKSAlPiUNCiAgICBtZXRhZ2VuKA0KICAgICAgVEUgPSBjb2VmLA0KICAgICAgc2VURSA9IHNlLmNvZWYuLA0KICAgICAgZGF0YSA9IC4sDQogICAgICBzbSA9ICJIUiIsICMgaGF6YXJkIHJhdGlvcw0KICAgICAgY29tYi5yYW5kb20gPSBUUlVFLA0KICAgICAgY29tYi5maXhlZCA9IEZBTFNFLA0KICAgICAgbWV0aG9kLnRhdSA9ICJETCIsICMgZGVmYXVsdCB0YXUgbWV0aG9kDQogICAgICBoYWtuID0gRkFMU0UsDQogICAgICBwcmVkaWN0aW9uID0gVFJVRSwNCiAgICAgIHN0dWRsYWIgPSBzaXRlDQogICAgKQ0KfQ0KYGBgDQoNCiMjIEZvcm1hdCBtZXRhLWFuYWx5c2lzIHJlc3VsdHMNCg0KSGVyZSB3ZSB3aWxsIGZvcm1hdCB0aGUgb3V0cHV0IG9mIHRoZSBtZXRhLWFuYWx5c2lzIGZvciBib3RoIENOUyBhbmQgUE5TIG1vZGVscw0KDQpgYGB7cn0NCiMgY25zDQptZXRhX3Jlc3VsdHNfYWR1bHRzX2Nuc19kZiA8LSBmb3JtYXRfbWV0YShtZXRhX3Jlc3VsdHNfYWR1bHRzX2NucykNCg0KIyBwbnMNCm1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmIDwtIGZvcm1hdF9tZXRhKG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zKQ0KYGBgDQoNCiMjIFNhdmUgbW9kZWwgd2VpZ2h0cyB0byBnZW5lcmF0ZSB0aGUgc3Vydml2YWwgY3VydmVzDQoNCk5leHQsIHdlIHdpbGwgc2F2ZSB0aGUgbW9kZWwgd2VpZ2h0cyBmb3IgZWFjaCBoZWFsdGhjYXJlIHN5c3RlbSBpbiBvcmRlciB0byBoZWxwIHVzIGdlbmVyYXRlIHRoZSBhc3NvY2lhdGVkIHN1cnZpdmFsIGN1cnZlcyBpbiBvdXIgZG93bnN0cmVhbSBhbmFseXNpcy4NCg0KVGhlIGJlbmVmaXQgb2YgdGhlIHJhbmRvbS1lZmZlY3RzIG1vZGVsIGlzIHRoYXQgaXQgYWxsb3dzIHVzIHRvIHdlaWdodCBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtIGJ5IGNvbnNpZGVyaW5nIHRoZSBoZWFsdGhjYXJlIHN5c3RlbSB2YXJpYW5jZSAke3NeMn0kIGFuZCB0aGUgYmV0d2VlbiBoZWFsdGhjYXJlIHN5c3RlbSB2YXJpYW5jZSAkdGF1XjIkLg0KDQoqKjQuMS4yLjEgRXN0aW1hdG9ycyBvZiB0aGUgQmV0d2Vlbi1TdHVkeSBIZXRlcm9nZW5laXR5OioqIDxodHRwczovL2Jvb2tkb3duLm9yZy9NYXRoaWFzSGFycmVyL0RvaW5nX01ldGFfQW5hbHlzaXNfaW5fUi9wb29saW5nLWVzLmh0bWw+DQoNCj4gImludmVyc2Ugb2YgdGhlIHZhcmlhbmNlIGlzIHVzZWQgdG8gZGV0ZXJtaW5lIHRoZSB3ZWlnaHQgb2YgZWFjaCBzdHVkeSINCj4NCj4gLSAgICp3aGVyZSAic3R1ZHkiIGluIG91ciBhbmFseXNpcyBpcyB0aGUgaGVhbHRoY2FyZSBzeXN0ZW0qDQo+DQo+IEZvciByYW5kb20gZWZmZWN0cywgdGhlIGNhbGN1bGF0aW9uIGlzIHBlcmZvcm1lZCBieSBjYWxjdWxhdGluZyBhbiBhZGp1c3RlZCByYW5kb20tZWZmZWN0cyB3ZWlnaHQgd+KIl2sgZm9yIGVhY2ggb2JzZXJ2YXRpb24uIFdoZXJlIGsgaXMgdGhlIGhlYWx0aGNhcmUgc3lzdGVtOg0KDQokd14qX2sgPSBcZnJhY3sxfXtzXjIgKyB0YXVeMn0kDQoNCmBgYHtyfQ0KIyBzYXZlIGFsbCBjbnMgd2VpZ2h0cw0KZm9yIChvdXRjb21lX2kgaW4gb3V0Y29tZXMpIHsNCiAgDQogIHNhdmVfd2VpZ2h0cyhtZXRhX3Jlc3VsdHNfZGYgPSBtZXRhX3Jlc3VsdHNfYWR1bHRzX2Nuc19kZiwNCiAgICAgICAgICAgICAgIHBvcHVsYXRpb24gPSAiYWR1bHRzIiwgDQogICAgICAgICAgICAgICBvdXRjb21lID0gb3V0Y29tZV9pLCANCiAgICAgICAgICAgICAgIGNvbW9yYl9tZXRob2QgPSAiaW5kIiwgDQogICAgICAgICAgICAgICBjZW5zb3JfY3V0b2ZmID0gIjkwIiwNCiAgICAgICAgICAgICAgIGNuc19wbnMgPSAiQ05TIikNCn0NCg0KDQojIHNhdmUgYWxsIHBucyB3ZWlnaHRzDQpmb3IgKG91dGNvbWVfaSBpbiBvdXRjb21lcykgew0KICANCiAgc2F2ZV93ZWlnaHRzKG1ldGFfcmVzdWx0c19kZiA9IG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmLA0KICAgICAgICAgICAgICAgcG9wdWxhdGlvbiA9ICJhZHVsdHMiLCANCiAgICAgICAgICAgICAgIG91dGNvbWUgPSBvdXRjb21lX2ksIA0KICAgICAgICAgICAgICAgY29tb3JiX21ldGhvZCA9ICJpbmQiLCANCiAgICAgICAgICAgICAgIGNlbnNvcl9jdXRvZmYgPSAiOTAiLA0KICAgICAgICAgICAgICAgY25zX3BucyA9ICJQTlMiKQ0KfQ0KYGBgDQoNCiMgKipBbmFseXplIFJpc2sgdmlhIEZvcmVzdCBQbG90cyoqDQoNCioqUHJlcGFyZSBkYXRhIGZvciBmb3Jlc3QgcGxvdHMqKg0KDQpgYGB7cn0NCiMgYWR1bHRzDQptZXRhX3Jlc3VsdHNfYWR1bHRzX2Nuc19kZl9mb3Jlc3QgPSBwcmVwYXJlX2RhdGFfZm9yZXN0X3Bsb3RzKG1ldGFfcmVzdWx0c19hZHVsdHNfY25zX2RmKQ0KbWV0YV9yZXN1bHRzX2FkdWx0c19wbnNfZGZfZm9yZXN0ID0gcHJlcGFyZV9kYXRhX2ZvcmVzdF9wbG90cyhtZXRhX3Jlc3VsdHNfYWR1bHRzX3Buc19kZikNCmBgYA0KDQoqKlRpZHkgdXAgcmVzdWx0cyoqDQoNCmBgYHtyfQ0KbWV0YV9yZXN1bHRzX2FkdWx0c19jbnNfZGZfZm9yZXN0X3RpZHkgPC0gbWV0YV9yZXN1bHRzX2FkdWx0c19jbnNfZGZfZm9yZXN0ICU+JSANCiAgZGlzdGluY3QoYW5hbHlzaXMsIFRFLnJhbmRvbSwgbG93ZXIucmFuZG9tLCB1cHBlci5yYW5kb20sYHAtdmFsdWVgLCBsb3dlci5wcmVkaWN0LCB1cHBlci5wcmVkaWN0LCBJMiwgbG93ZXIuSTIsIHVwcGVyLkkyLCBILCBsb3dlci5ILCB1cHBlci5ILCB0YXUyLCBsb3dlci50YXUyLCB1cHBlci50YXUyLCBDSSkgJT4lIA0KICBtdXRhdGUobmV1cm9fc3RhdHVzID0gIkNOUyIpICU+JSANCiAgc2VsZWN0KG5ldXJvX3N0YXR1cywgZXZlcnl0aGluZygpKQ0KDQptZXRhX3Jlc3VsdHNfYWR1bHRzX3Buc19kZl9mb3Jlc3RfdGlkeSA8LSBtZXRhX3Jlc3VsdHNfYWR1bHRzX3Buc19kZl9mb3Jlc3QgJT4lIA0KICBkaXN0aW5jdChhbmFseXNpcywgVEUucmFuZG9tLCBsb3dlci5yYW5kb20sIHVwcGVyLnJhbmRvbSxgcC12YWx1ZWAsIGxvd2VyLnByZWRpY3QsIHVwcGVyLnByZWRpY3QsIEkyLCBsb3dlci5JMiwgdXBwZXIuSTIsIEgsIGxvd2VyLkgsIHVwcGVyLkgsIHRhdTIsIGxvd2VyLnRhdTIsIHVwcGVyLnRhdTIsIENJKSAlPiUgDQogIG11dGF0ZShuZXVyb19zdGF0dXMgPSAiUE5TIikgJT4lIA0KICBzZWxlY3QobmV1cm9fc3RhdHVzLCBldmVyeXRoaW5nKCkpDQoNCm1ldGFfcmVzdWx0c190aWR5IDwtIHJiaW5kKG1ldGFfcmVzdWx0c19hZHVsdHNfY25zX2RmX2ZvcmVzdF90aWR5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmX2ZvcmVzdF90aWR5KQ0KDQp3cml0ZS5jc3YobWV0YV9yZXN1bHRzX3RpZHksICJ0YWJsZXMvVGFibGUyX21ldGFfcmVzdWx0cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQpOZXh0LCB3ZSB3aWxsIGNyZWF0ZSBhIHNjYWxlIGZ1bmN0aW9uIHRvIGlkZW50aWZ5IHRoZSBtaW4gYW5kIG1heCBDSSBmb3IgZWFjaCBoZWFsdGhjYXJlIHN5c3RlbS4gVGhpcyB3aWxsIGhlbHAgdXMgZXhjbHVkZSBoZWFsdGhjYXJlIHN5c3RlbXMgd2l0aCB2YWx1ZXMgb2YgJ0luZicgb3IgdmVyeSBsYXJnZSBjb25maWRlbmNlIGludGVydmFscy4NCg0KYGBge3J9DQpzY2FsZV9wbG90IDwtIGZ1bmN0aW9uKG1ldGFfcmVzdWx0c19kZl9mb3Jlc3QsIHNpdGVfZXhjbHVkZSA9IE5VTEwpIHsNCiAgc2NhbGUgPC0gbWV0YV9yZXN1bHRzX2RmX2ZvcmVzdCAlPiUNCiAgICBmaWx0ZXIoDQogICAgICAhU2l0ZSAlaW4lIHNpdGVfZXhjbHVkZSwNCiAgICAgICMhYW5hbHlzaXMgJWluJSBpbnZhbGlkX3NpdGVzLA0KICAgICAgIUNJLkxvdyA9PSAiSW5mIiwNCiAgICAgICFDSS5IaWdoID09ICJJbmYiDQogICAgKSAlPiUNCiAgICBtdXRhdGUoDQogICAgICBtaW5fZXN0ID0gbWluKENJLkxvdywgbmEucm0gPSBUUlVFKSAtIDEsDQogICAgICBtYXhfZXN0ID0gbWF4KENJLkhpZ2gsIG5hLnJtID0gVFJVRSkgKyAxLA0KICAgICAgbWluX2VzdCA9IGlmX2Vsc2UobWluX2VzdCArIDEgPj0gLTEsIG1pbihDSS5Mb3csIG5hLnJtID0gVFJVRSktMC4xLCBtaW5fZXN0KSwNCiAgICAgIG1pbl9lc3QgPSBpZl9lbHNlKG1pbl9lc3QgPiAxLCAwLjUsIG1pbl9lc3QpLA0KICAgICAgbWF4X2VzdCA9IGlmX2Vsc2UobWF4X2VzdCAtIDEgPCAxLCAxLjEsIG1heF9lc3QpKSAlPiUNCiAgICBkaXN0aW5jdChtaW5fZXN0LCBtYXhfZXN0KQ0KDQogIHJldHVybihzY2FsZSkNCn0NCmBgYA0KDQojIyBDTlMgLSBSaXNrIG9mIERpc2NoYXJnZQ0KDQpgYGB7ciBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRlLnJhbmRvbSA9IG1ldGFfcmVzdWx0c19hZHVsdHNfY25zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAidGltZV9maXJzdF9kaXNjaGFyZ2VfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KFRFLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgJT4lIA0KICByb3VuZCguLDIpDQoNCnRlLnJhbmRvbS5jaV9sb3dlciA9IG1ldGFfcmVzdWx0c19hZHVsdHNfY25zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAidGltZV9maXJzdF9kaXNjaGFyZ2VfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KGxvd2VyLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgDQoNCnRlLnJhbmRvbS5jaV91cHBlciA9IG1ldGFfcmVzdWx0c19hZHVsdHNfY25zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAidGltZV9maXJzdF9kaXNjaGFyZ2VfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KHVwcGVyLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgDQoNCmxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMgPC0gbWV0YV9yZXN1bHRzX2FkdWx0c19jbnNfZGZfZm9yZXN0ICU+JQ0KICBzbGljZShncmVwKHBhc3RlKCJ0aW1lX2ZpcnN0X2Rpc2NoYXJnZV9yZWdfZWxpeCIsIGNvbGxhcHNlID0gInwiKSwgYW5hbHlzaXMpKSAlPiUgDQogICNhZGRfcm93KC5iZWZvcmUgPSAxKSAlPiUNCiAgYWRkX3JvdyguYWZ0ZXIgPSBucm93KC4pKSAlPiUgDQogIG11dGF0ZShTaXRlID0gaWZlbHNlKGlzLm5hKFNpdGUpLCAiRWZmZWN0IHNpemUiLCBTaXRlKSwNCiAgICAgICAgIGBIYXphcmQgUmF0aW9gID0gaWZlbHNlKGlzLm5hKGBIYXphcmQgUmF0aW9gKSwgcm91bmQodGUucmFuZG9tLCAyKSwgYEhhemFyZCBSYXRpb2ApLA0KICAgICAgICAgYHAtdmFsdWVgID0gaWZlbHNlKGBwLXZhbHVlYCA8IDAuMDAxLCAiPCAuMDAxKiIsIHJvdW5kKGBwLXZhbHVlYCwgMykpKSAlPiUgDQogIGZpbGwoYHAtdmFsdWVgLCAuZGlyZWN0aW9uID0gImRvd24iKSAlPiUgDQogIG11dGF0ZShgcC12YWx1ZWAgPSBpZmVsc2UoU2l0ZSA9PSAiRWZmZWN0IHNpemUiLCBgcC12YWx1ZWAsICIiKSwNCiAgICAgICAgIENJLkxvdyA9IGlmZWxzZShTaXRlID09ICJFZmZlY3Qgc2l6ZSIsIHRlLnJhbmRvbS5jaV9sb3dlciwgQ0kuTG93KSwNCiAgICAgICAgIENJLkhpZ2ggPSBpZmVsc2UoU2l0ZSA9PSAiRWZmZWN0IHNpemUiLCB0ZS5yYW5kb20uY2lfdXBwZXIsIENJLkhpZ2gpLA0KICAgICAgICAgYEhhemFyZCBSYXRpbyBgID0gcGFzdGUoYEhhemFyZCBSYXRpb2AsICcoJywgQ0kuTG93LCAnLCcsIENJLkhpZ2gsICcpJykpDQoNCg0KIyMgZGVmaW5lIGZvcmVzdCBwbG90IHNoYXBlcw0KIyBzaGFwZSAjMTYgaXMgbm9ybWFsIGNpcmNsZTsgIzE4IGlzIGRpYW1vbmQNCnNoYXBlcyA8LSByZXAoMTYsIHRpbWVzID0gbnJvdyhsZW5ndGhfc3RheV9maXJzdF9yZXN1bHRzKSkNCnNoYXBlc1tsZW5ndGgoc2hhcGVzKV0gPC0gMTgNCnNpemVzIDwtIHJlcCgzLjI1LCB0aW1lcyA9IG5yb3cobGVuZ3RoX3N0YXlfZmlyc3RfcmVzdWx0cykpDQpzaXplc1tsZW5ndGgoc2l6ZXMpXSA8LSA1DQpzY2FsZSA8LSBzY2FsZV9wbG90KGxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMsIHNpdGVfZXhjbHVkZSA9IGMoIklDU00iKSkNCg0KIyBub3RlOiBgSGF6YXJkIFJhdGlvIGAgd2l0aCBjb25maWRlbmNlIGludGVydmFscyBoYXMgYW4gZXh0cmEgc3BhY2UgYW5kIHVzZWQgYnkgYHJpZ2h0X3NpZGVfZGF0YWAgcGFyYW0NCnAxIDwtIGZvcmVzdGVyKA0KICBsZWZ0X3NpZGVfZGF0YSA9IGxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMgJT4lIA0KICAgIHNlbGVjdChTaXRlKSAlPiUgDQogICAgcmVuYW1lKCJIZWFsdGhjYXJlIFN5c3RlbSIgPSAiU2l0ZSIpLA0KICBlc3RpbWF0ZSA9IGxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMkYEhhemFyZCBSYXRpb2AsDQogIGNpX2xvdyA9IGxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMkQ0kuTG93LA0KICBjaV9oaWdoID0gbGVuZ3RoX3N0YXlfZmlyc3RfcmVzdWx0cyRDSS5IaWdoLA0KICByaWdodF9zaWRlX2RhdGEgPSBsZW5ndGhfc3RheV9maXJzdF9yZXN1bHRzWywgYygiSGF6YXJkIFJhdGlvICIsICJwLXZhbHVlIildLA0KICBkaXNwbGF5ID0gVFJVRSwNCiAgbnVkZ2VfeCA9IC41LA0KICBmb250X2ZhbWlseSA9ICJhcmlhbCIsDQogIGFycm93cyA9IFRSVUUsDQogIGFycm93X2xhYmVscyA9IGMoIkRlY3JlYXNlZCBSaXNrIiwgIkluY3JlYXNlZCBSaXNrIiksDQogIG51bGxfbGluZV9hdCA9IDEsDQogIHhsaW0gPSBjKHNjYWxlJG1pbl9lc3QsIHNjYWxlJG1heF9lc3QpLA0KICAjeGJyZWFrcyA9IGMoc2NhbGUkbWluX2VzdCwgMSwgc2NhbGUkbWF4X2VzdCksDQogIHBvaW50X3NpemVzID0gc2l6ZXMsDQogIHBvaW50X3NoYXBlcyA9IHNoYXBlcywNCiAgZmlsZV9wYXRoID0gaGVyZTo6aGVyZShwYXN0ZTAoImZpZ3VyZXMvZm9yZXN0cGxvdF9hZHVsdF9jbnNfZGlzY2hhcmdlLnBuZyIpKSkNCg0KcDENCmBgYA0KDQojIyBDTlMgLSBSaXNrIG9mIE1vcnRhbGl0eQ0KDQpgYGB7ciBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRlLnJhbmRvbSA9IG1ldGFfcmVzdWx0c19hZHVsdHNfY25zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAiZGVjZWFzZWRfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KFRFLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgJT4lIA0KICByb3VuZCguLDIpDQoNCnRlLnJhbmRvbS5jaV9sb3dlciA9IG1ldGFfcmVzdWx0c19hZHVsdHNfY25zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAiZGVjZWFzZWRfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KGxvd2VyLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgDQoNCnRlLnJhbmRvbS5jaV91cHBlciA9IG1ldGFfcmVzdWx0c19hZHVsdHNfY25zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAiZGVjZWFzZWRfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KHVwcGVyLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgDQoNCmRlY2Vhc2VkX3Jlc3VsdHMgPC0gbWV0YV9yZXN1bHRzX2FkdWx0c19jbnNfZGZfZm9yZXN0ICU+JQ0KICBzbGljZShncmVwKHBhc3RlKCJkZWNlYXNlZF9yZWdfZWxpeCIsIGNvbGxhcHNlID0gInwiKSwgYW5hbHlzaXMpKSAlPiUgDQogICNhZGRfcm93KC5iZWZvcmUgPSAxKSAlPiUNCiAgYWRkX3JvdyguYWZ0ZXIgPSBucm93KC4pKSAlPiUgDQogIG11dGF0ZShTaXRlID0gaWZlbHNlKGlzLm5hKFNpdGUpLCAiRWZmZWN0IHNpemUiLCBTaXRlKSwNCiAgICAgICAgIGBIYXphcmQgUmF0aW9gID0gaWZlbHNlKGlzLm5hKGBIYXphcmQgUmF0aW9gKSwgcm91bmQodGUucmFuZG9tLCAyKSwgYEhhemFyZCBSYXRpb2ApLA0KICAgICAgICAgYHAtdmFsdWVgID0gaWZlbHNlKGBwLXZhbHVlYCA8IDAuMDAxLCAiPCAuMDAxKiIsIHJvdW5kKGBwLXZhbHVlYCwgMykpKSAlPiUgDQogIGZpbGwoYHAtdmFsdWVgLCAuZGlyZWN0aW9uID0gImRvd24iKSAlPiUgDQogIG11dGF0ZShgcC12YWx1ZWAgPSBpZmVsc2UoU2l0ZSA9PSAiRWZmZWN0IHNpemUiLCBgcC12YWx1ZWAsICIiKSwNCiAgICAgICAgIENJLkxvdyA9IGlmZWxzZShTaXRlID09ICJFZmZlY3Qgc2l6ZSIsIHRlLnJhbmRvbS5jaV9sb3dlciwgQ0kuTG93KSwNCiAgICAgICAgIENJLkhpZ2ggPSBpZmVsc2UoU2l0ZSA9PSAiRWZmZWN0IHNpemUiLCB0ZS5yYW5kb20uY2lfdXBwZXIsIENJLkhpZ2gpLA0KICAgICAgICAgYEhhemFyZCBSYXRpbyBgID0gcGFzdGUoYEhhemFyZCBSYXRpb2AsICcoJywgQ0kuTG93LCAnLCcsIENJLkhpZ2gsICcpJykpDQoNCg0KIyMgZGVmaW5lIGZvcmVzdCBwbG90IHNoYXBlcw0KIyBzaGFwZSAjMTYgaXMgbm9ybWFsIGNpcmNsZTsgIzE4IGlzIGRpYW1vbmQNCnNoYXBlcyA8LSByZXAoMTYsIHRpbWVzID0gbnJvdyhkZWNlYXNlZF9yZXN1bHRzKSkNCnNoYXBlc1tsZW5ndGgoc2hhcGVzKV0gPC0gMTgNCnNpemVzIDwtIHJlcCgzLjI1LCB0aW1lcyA9IG5yb3coZGVjZWFzZWRfcmVzdWx0cykpDQpzaXplc1tsZW5ndGgoc2l6ZXMpXSA8LSA1DQpzY2FsZSA8LSBzY2FsZV9wbG90KGRlY2Vhc2VkX3Jlc3VsdHMsIHNpdGVfZXhjbHVkZSA9IGMoIklDU00iKSkNCg0KIyBub3RlOiBgSGF6YXJkIFJhdGlvIGAgd2l0aCBjb25maWRlbmNlIGludGVydmFscyBoYXMgYW4gZXh0cmEgc3BhY2UgYW5kIHVzZWQgYnkgYHJpZ2h0X3NpZGVfZGF0YWAgcGFyYW0NCnAyIDwtIGZvcmVzdGVyKA0KICBsZWZ0X3NpZGVfZGF0YSA9IGRlY2Vhc2VkX3Jlc3VsdHMgJT4lIA0KICAgIHNlbGVjdChTaXRlKSAlPiUgDQogICAgcmVuYW1lKCJIZWFsdGhjYXJlIFN5c3RlbSIgPSAiU2l0ZSIpLA0KICBlc3RpbWF0ZSA9IGRlY2Vhc2VkX3Jlc3VsdHMkYEhhemFyZCBSYXRpb2AsDQogIGNpX2xvdyA9IGRlY2Vhc2VkX3Jlc3VsdHMkQ0kuTG93LA0KICBjaV9oaWdoID0gZGVjZWFzZWRfcmVzdWx0cyRDSS5IaWdoLA0KICByaWdodF9zaWRlX2RhdGEgPSBkZWNlYXNlZF9yZXN1bHRzWywgYygiSGF6YXJkIFJhdGlvICIsICJwLXZhbHVlIildLA0KICBkaXNwbGF5ID0gVFJVRSwNCiAgbnVkZ2VfeCA9IC41LA0KICBmb250X2ZhbWlseSA9ICJhcmlhbCIsDQogIGFycm93cyA9IFRSVUUsDQogIGFycm93X2xhYmVscyA9IGMoIkRlY3JlYXNlZCBSaXNrIiwgIkluY3JlYXNlZCBSaXNrIiksDQogIG51bGxfbGluZV9hdCA9IDEsDQogIHhsaW0gPSBjKHNjYWxlJG1pbl9lc3QsIHNjYWxlJG1heF9lc3QpLA0KICAjeGJyZWFrcyA9IGMoc2NhbGUkbWluX2VzdCwgMSwgc2NhbGUkbWF4X2VzdCksDQogIHBvaW50X3NpemVzID0gc2l6ZXMsDQogIHBvaW50X3NoYXBlcyA9IHNoYXBlcywNCiAgZmlsZV9wYXRoID0gaGVyZTo6aGVyZShwYXN0ZTAoImZpZ3VyZXMvZm9yZXN0cGxvdF9hZHVsdF9jbnNfbW9ydGFsaXR5LnBuZyIpKSkNCg0KcDINCmBgYA0KDQojIyBQTlMgLSBSaXNrIG9mIERpc2NoYXJnZQ0KDQpgYGB7ciBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRlLnJhbmRvbSA9IG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAidGltZV9maXJzdF9kaXNjaGFyZ2VfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KFRFLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgJT4lIA0KICByb3VuZCguLDIpDQoNCnRlLnJhbmRvbS5jaV9sb3dlciA9IG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAidGltZV9maXJzdF9kaXNjaGFyZ2VfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KGxvd2VyLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgDQoNCnRlLnJhbmRvbS5jaV91cHBlciA9IG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAidGltZV9maXJzdF9kaXNjaGFyZ2VfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KHVwcGVyLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgDQoNCmxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMgPC0gbWV0YV9yZXN1bHRzX2FkdWx0c19wbnNfZGZfZm9yZXN0ICU+JQ0KICBzbGljZShncmVwKHBhc3RlKCJ0aW1lX2ZpcnN0X2Rpc2NoYXJnZV9yZWdfZWxpeCIsIGNvbGxhcHNlID0gInwiKSwgYW5hbHlzaXMpKSAlPiUgDQogICNhZGRfcm93KC5iZWZvcmUgPSAxKSAlPiUNCiAgYWRkX3JvdyguYWZ0ZXIgPSBucm93KC4pKSAlPiUgDQogIG11dGF0ZShTaXRlID0gaWZlbHNlKGlzLm5hKFNpdGUpLCAiRWZmZWN0IHNpemUiLCBTaXRlKSwNCiAgICAgICAgIGBIYXphcmQgUmF0aW9gID0gaWZlbHNlKGlzLm5hKGBIYXphcmQgUmF0aW9gKSwgcm91bmQodGUucmFuZG9tLCAyKSwgYEhhemFyZCBSYXRpb2ApLA0KICAgICAgICAgYHAtdmFsdWVgID0gaWZlbHNlKGBwLXZhbHVlYCA8IDAuMDAxLCAiPCAuMDAxKiIsIHJvdW5kKGBwLXZhbHVlYCwgMykpKSAlPiUgDQogIGZpbGwoYHAtdmFsdWVgLCAuZGlyZWN0aW9uID0gImRvd24iKSAlPiUgDQogIG11dGF0ZShgcC12YWx1ZWAgPSBpZmVsc2UoU2l0ZSA9PSAiRWZmZWN0IHNpemUiLCBgcC12YWx1ZWAsICIiKSwNCiAgICAgICAgIENJLkxvdyA9IGlmZWxzZShTaXRlID09ICJFZmZlY3Qgc2l6ZSIsIHRlLnJhbmRvbS5jaV9sb3dlciwgQ0kuTG93KSwNCiAgICAgICAgIENJLkhpZ2ggPSBpZmVsc2UoU2l0ZSA9PSAiRWZmZWN0IHNpemUiLCB0ZS5yYW5kb20uY2lfdXBwZXIsIENJLkhpZ2gpLA0KICAgICAgICAgYEhhemFyZCBSYXRpbyBgID0gcGFzdGUoYEhhemFyZCBSYXRpb2AsICcoJywgQ0kuTG93LCAnLCcsIENJLkhpZ2gsICcpJykpDQoNCg0KIyMgZGVmaW5lIGZvcmVzdCBwbG90IHNoYXBlcw0KIyBzaGFwZSAjMTYgaXMgbm9ybWFsIGNpcmNsZTsgIzE4IGlzIGRpYW1vbmQNCnNoYXBlcyA8LSByZXAoMTYsIHRpbWVzID0gbnJvdyhsZW5ndGhfc3RheV9maXJzdF9yZXN1bHRzKSkNCnNoYXBlc1tsZW5ndGgoc2hhcGVzKV0gPC0gMTgNCnNpemVzIDwtIHJlcCgzLjI1LCB0aW1lcyA9IG5yb3cobGVuZ3RoX3N0YXlfZmlyc3RfcmVzdWx0cykpDQpzaXplc1tsZW5ndGgoc2l6ZXMpXSA8LSA1DQpzY2FsZSA8LSBzY2FsZV9wbG90KGxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMsIHNpdGVfZXhjbHVkZSA9IGMoIklDU00iKSkNCg0KIyBub3RlOiBgSGF6YXJkIFJhdGlvIGAgd2l0aCBjb25maWRlbmNlIGludGVydmFscyBoYXMgYW4gZXh0cmEgc3BhY2UgYW5kIHVzZWQgYnkgYHJpZ2h0X3NpZGVfZGF0YWAgcGFyYW0NCnAzIDwtIGZvcmVzdGVyKA0KICBsZWZ0X3NpZGVfZGF0YSA9IGxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMgJT4lIA0KICAgIHNlbGVjdChTaXRlKSAlPiUgDQogICAgcmVuYW1lKCJIZWFsdGhjYXJlIFN5c3RlbSIgPSAiU2l0ZSIpLA0KICBlc3RpbWF0ZSA9IGxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMkYEhhemFyZCBSYXRpb2AsDQogIGNpX2xvdyA9IGxlbmd0aF9zdGF5X2ZpcnN0X3Jlc3VsdHMkQ0kuTG93LA0KICBjaV9oaWdoID0gbGVuZ3RoX3N0YXlfZmlyc3RfcmVzdWx0cyRDSS5IaWdoLA0KICByaWdodF9zaWRlX2RhdGEgPSBsZW5ndGhfc3RheV9maXJzdF9yZXN1bHRzWywgYygiSGF6YXJkIFJhdGlvICIsICJwLXZhbHVlIildLA0KICBkaXNwbGF5ID0gVFJVRSwNCiAgbnVkZ2VfeCA9IC41LA0KICBmb250X2ZhbWlseSA9ICJhcmlhbCIsDQogIGFycm93cyA9IFRSVUUsDQogIGFycm93X2xhYmVscyA9IGMoIkRlY3JlYXNlZCBSaXNrIiwgIkluY3JlYXNlZCBSaXNrIiksDQogIG51bGxfbGluZV9hdCA9IDEsDQogIHhsaW0gPSBjKHNjYWxlJG1pbl9lc3QsIHNjYWxlJG1heF9lc3QpLA0KICAjeGJyZWFrcyA9IGMoc2NhbGUkbWluX2VzdCwgMSwgc2NhbGUkbWF4X2VzdCksDQogIHBvaW50X3NpemVzID0gc2l6ZXMsDQogIHBvaW50X3NoYXBlcyA9IHNoYXBlcywNCiAgZmlsZV9wYXRoID0gaGVyZTo6aGVyZShwYXN0ZTAoImZpZ3VyZXMvZm9yZXN0cGxvdF9hZHVsdF9wbnNfZGlzY2hhcmdlLnBuZyIpKSkNCg0KcDMNCmBgYA0KDQojIyBQTlMgLSBSaXNrIG9mIE1vcnRhbGl0eQ0KDQpgYGB7ciBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRlLnJhbmRvbSA9IG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAiZGVjZWFzZWRfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KFRFLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgJT4lIA0KICByb3VuZCguLDIpDQoNCnRlLnJhbmRvbS5jaV9sb3dlciA9IG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAiZGVjZWFzZWRfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KGxvd2VyLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgDQoNCnRlLnJhbmRvbS5jaV91cHBlciA9IG1ldGFfcmVzdWx0c19hZHVsdHNfcG5zX2RmX2ZvcmVzdCAlPiUgDQogIGZpbHRlcihhbmFseXNpcyA9PSAiZGVjZWFzZWRfcmVnX2VsaXgiKSAlPiUgDQogIGRpc3RpbmN0KHVwcGVyLnJhbmRvbSkgJT4lIA0KICBhcy5udW1lcmljKCkgDQoNCmRlY2Vhc2VkX3Jlc3VsdHMgPC0gbWV0YV9yZXN1bHRzX2FkdWx0c19wbnNfZGZfZm9yZXN0ICU+JQ0KICBzbGljZShncmVwKHBhc3RlKCJkZWNlYXNlZF9yZWdfZWxpeCIsIGNvbGxhcHNlID0gInwiKSwgYW5hbHlzaXMpKSAlPiUgDQogICNhZGRfcm93KC5iZWZvcmUgPSAxKSAlPiUNCiAgYWRkX3JvdyguYWZ0ZXIgPSBucm93KC4pKSAlPiUgDQogIG11dGF0ZShTaXRlID0gaWZlbHNlKGlzLm5hKFNpdGUpLCAiRWZmZWN0IHNpemUiLCBTaXRlKSwNCiAgICAgICAgIGBIYXphcmQgUmF0aW9gID0gaWZlbHNlKGlzLm5hKGBIYXphcmQgUmF0aW9gKSwgcm91bmQodGUucmFuZG9tLCAyKSwgYEhhemFyZCBSYXRpb2ApLA0KICAgICAgICAgYHAtdmFsdWVgID0gaWZlbHNlKGBwLXZhbHVlYCA8IDAuMDAxLCAiPCAuMDAxKiIsIHJvdW5kKGBwLXZhbHVlYCwgMykpKSAlPiUgDQogIGZpbGwoYHAtdmFsdWVgLCAuZGlyZWN0aW9uID0gImRvd24iKSAlPiUgDQogIG11dGF0ZShgcC12YWx1ZWAgPSBpZmVsc2UoU2l0ZSA9PSAiRWZmZWN0IHNpemUiLCBgcC12YWx1ZWAsICIiKSwNCiAgICAgICAgIENJLkxvdyA9IGlmZWxzZShTaXRlID09ICJFZmZlY3Qgc2l6ZSIsIHRlLnJhbmRvbS5jaV9sb3dlciwgQ0kuTG93KSwNCiAgICAgICAgIENJLkhpZ2ggPSBpZmVsc2UoU2l0ZSA9PSAiRWZmZWN0IHNpemUiLCB0ZS5yYW5kb20uY2lfdXBwZXIsIENJLkhpZ2gpLA0KICAgICAgICAgYEhhemFyZCBSYXRpbyBgID0gcGFzdGUoYEhhemFyZCBSYXRpb2AsICcoJywgQ0kuTG93LCAnLCcsIENJLkhpZ2gsICcpJykpDQoNCg0KIyMgZGVmaW5lIGZvcmVzdCBwbG90IHNoYXBlcw0KIyBzaGFwZSAjMTYgaXMgbm9ybWFsIGNpcmNsZTsgIzE4IGlzIGRpYW1vbmQNCnNoYXBlcyA8LSByZXAoMTYsIHRpbWVzID0gbnJvdyhkZWNlYXNlZF9yZXN1bHRzKSkNCnNoYXBlc1tsZW5ndGgoc2hhcGVzKV0gPC0gMTgNCnNpemVzIDwtIHJlcCgzLjI1LCB0aW1lcyA9IG5yb3coZGVjZWFzZWRfcmVzdWx0cykpDQpzaXplc1tsZW5ndGgoc2l6ZXMpXSA8LSA1DQpzY2FsZSA8LSBzY2FsZV9wbG90KGRlY2Vhc2VkX3Jlc3VsdHMsIHNpdGVfZXhjbHVkZSA9IGMoIklDU00iLCAiSFBHMjMiKSkNCg0KIyBub3RlOiBgSGF6YXJkIFJhdGlvIGAgd2l0aCBjb25maWRlbmNlIGludGVydmFscyBoYXMgYW4gZXh0cmEgc3BhY2UgYW5kIHVzZWQgYnkgYHJpZ2h0X3NpZGVfZGF0YWAgcGFyYW0NCnA0IDwtIGZvcmVzdGVyKA0KICBsZWZ0X3NpZGVfZGF0YSA9IGRlY2Vhc2VkX3Jlc3VsdHMgJT4lIA0KICAgIHNlbGVjdChTaXRlKSAlPiUgDQogICAgcmVuYW1lKCJIZWFsdGhjYXJlIFN5c3RlbSIgPSAiU2l0ZSIpLA0KICBlc3RpbWF0ZSA9IGRlY2Vhc2VkX3Jlc3VsdHMkYEhhemFyZCBSYXRpb2AsDQogIGNpX2xvdyA9IGRlY2Vhc2VkX3Jlc3VsdHMkQ0kuTG93LA0KICBjaV9oaWdoID0gZGVjZWFzZWRfcmVzdWx0cyRDSS5IaWdoLA0KICByaWdodF9zaWRlX2RhdGEgPSBkZWNlYXNlZF9yZXN1bHRzWywgYygiSGF6YXJkIFJhdGlvICIsICJwLXZhbHVlIildLA0KICBkaXNwbGF5ID0gVFJVRSwNCiAgbnVkZ2VfeCA9IC41LA0KICBmb250X2ZhbWlseSA9ICJhcmlhbCIsDQogIGFycm93cyA9IFRSVUUsDQogIGFycm93X2xhYmVscyA9IGMoIkRlY3JlYXNlZCBSaXNrIiwgIkluY3JlYXNlZCBSaXNrIiksDQogIG51bGxfbGluZV9hdCA9IDEsDQogIHhsaW0gPSBjKHNjYWxlJG1pbl9lc3QsIHNjYWxlJG1heF9lc3QpLA0KICAjeGJyZWFrcyA9IGMoc2NhbGUkbWluX2VzdCwgMSwgc2NhbGUkbWF4X2VzdCksDQogIHBvaW50X3NpemVzID0gc2l6ZXMsDQogIHBvaW50X3NoYXBlcyA9IHNoYXBlcywNCiAgZmlsZV9wYXRoID0gaGVyZTo6aGVyZShwYXN0ZTAoImZpZ3VyZXMvZm9yZXN0cGxvdF9hZHVsdF9wbnNfbW9ydGFsaXR5LnBuZyIpKSkNCg0KcDQNCmBgYA0K