This notebook describes the 106,229 adult and pediatric hospitalized COVID-19 positive patients that were studied as part of the Consortium for Clinical Characterization of COVID-19 by EHR Neurology group. This analysis contains the results of patients from 21 different healthcare systems spanning 6 countries.

library(tidyverse)
library(gridGraphics)
library(gridExtra)
library(ggpubr)
library(DT)
library(kableExtra)
library(cowplot)
library(RColorBrewer)
library(glue)
library(egg)
source("R/plot_theme.R")
source("R/demo_tables.R")
source("R/utils.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 aggregated counts and summary statistics (means, medians, hazard ratios, p-values, etc). 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"))

## load in the pre-processed comorbidity and meta-data
load("processed/comorbidity_table_cnps.rda")
load("processed/neuro_pt_counts.rda") 

Pre-processing

The following code pre-processes the demographic and clinical course data in order to construct a Table 1.

1. Format primary demographic characteristics

# create list of hospitals for adult and pediatric analyses
adult_sites = sorted_sites[!sorted_sites %in% c("BCH_results", "GOSH_results")]

pediatric_sites = sorted_sites[!sorted_sites %in% c("VA1_results", "VA2_results", "VA3_results", "VA4_results", "VA5_results")]

demo_table_adult <- create_demo_tableone(sorted_sites = adult_sites, is_pediatric = FALSE)

demo_table_pediatric <- create_demo_tableone(sorted_sites = pediatric_sites, is_pediatric = TRUE)

demo_table_combine <- rbind(demo_table_adult, demo_table_pediatric)

2. Format clinical characteristics (i.e: continuous variables such as median time to discharge)

clinical_table_adult <- create_clinical_tableone(sorted_sites = adult_sites, is_pediatric = FALSE)

clinical_table_pediatric <- create_clinical_tableone(sorted_sites = pediatric_sites, is_pediatric = TRUE)

clinical_table_combine <- rbind(clinical_table_adult, clinical_table_pediatric)

3. Conduct additional pre-processing of clinical variables

clinical_table_adult_clean <- clean_clinical_tables(clinical_table = clinical_table_adult)

clinical_table_pediatric_clean <- clean_clinical_tables(clinical_table = clinical_table_pediatric)

clinical_table_clean_combine <- rbind(clinical_table_adult_clean, clinical_table_pediatric_clean)

4. Add Comorbidity data

Here we create separate lists of the top 4 comorbidities in the adult and pediatric populations

comorbidity_table_adults <- comorb_table_wide %>%
  filter(population == "Adult") %>% 
  arrange(desc(Comorb_Total)) %>% 
  slice(1:4) # return top comorbidities

comorbidity_table_pediatric <- comorb_table_wide %>%
  filter(population == "Pediatric") %>% 
  arrange(desc(Comorb_Total)) %>% 
  slice(1:4) # return top comorbidities

top_comorb_adults <- comorbidity_table_adults$Comorbidity
top_comorb_pediatrics <- comorbidity_table_pediatric$Comorbidity

Demographics

Table One - Combined

The below table contains the counts and summary stats of the entire cohort (adult & pediatric patients).

neuro_pt_counts_sum <- colSums(neuro_pt_counts[,-1]) %>% 
  data.frame() %>% 
  t() %>% 
  data.frame()

# specify the order of Table 1
row_order_adult <- c(
  "All Patients", "Female", "Male", "Unknown Sex",
  "0-2", "3-5", "6-11", "12-17", "18-25",
  "26-49", "50-69", "70-79", "80+", "Unknown Age",
  "American Indian", "Asian", "Black",
  "Hawaiian/Pacific Islander",
  "Hispanic/Latino", "White", "Other",
  top_comorb_adults, 
  "Median Elixhauser score  (sd) ",
  "Median pre admission cns  (sd) ",
  "Median pre admission pns  (sd) ",
  "Non-Severe", "Severe",
  "Median time to severe  (sd) ",
  "Alive", "Deceased",
  "Median time to death  (sd) ",
  "Discharged", "Not Discharged",
  "Median time to first discharge  (sd) ",
  "Not Readmitted", "Readmitted",
  "Median time to first readmission  (sd) ",
  "Median number of readmissions  (sd) "
  )


tableone_combine <- create_tableone(demo_table = demo_table_combine,
                                    clinical_table = clinical_table_clean_combine) %>%
  rbind(., comorb_table_wide %>%
          group_by(Comorbidity) %>%
          mutate(Comorb_Total = sum(Comorb_Total, na.rm = TRUE),
          None_sum = sum(None_Total, na.rm = TRUE),
          CNS_sum = sum(CNS_Total, na.rm = TRUE),
          PNS_sum = sum(PNS_Total, na.rm = TRUE),
          None_perc = round(None_sum/neuro_pt_counts_sum$None_n*100,1),
          Central_perc = round(CNS_sum/neuro_pt_counts_sum$CNS_n*100,1),
          Peripheral_perc = round(PNS_sum/neuro_pt_counts_sum$PNS_n*100,1),
          None = paste(None_sum, "(", None_perc, ")"),
          Central = paste(CNS_sum, "(", Central_perc, ")"),
          Peripheral = paste(PNS_sum, "(", Peripheral_perc, ")")) %>%
          distinct(Comorbidity, Comorb_Total, None, Central, Peripheral) %>%
          rename(`Table 1` = "Comorbidity",
          N = "Comorb_Total")) %>%
  slice(match(row_order_adult, `Table 1`))

kbl(tableone_combine %>%
      rename("No Neurological Condition (NNC)" = None)) %>%
      add_header_above(c(
      " ",
      "Total Patients" = 1,
      "Neurological Disease" = 3
      )) %>%
      kable_paper("striped", full_width = F) %>%
      pack_rows("Sex", 2, 4) %>%
      pack_rows("Age", 5, 13) %>%
      pack_rows("Race & Ethnicity", 14, 20) %>%
      pack_rows("Past Medical History", 21, 27) %>%
      pack_rows("Severity", 28, 30) %>%
      pack_rows("Survival", 31, 33) %>%
      pack_rows("Discharge", 34, 36) %>%
      pack_rows("Readmission", 37, 40)
Total Patients
Neurological Disease
Table 1 N No Neurological Condition (NNC) Central Peripheral
All Patients 106229 88340 (100%) 15101 (100%) 2788 (100%)
Sex
Female 37590 32399 (36.7%) 4247 (28.1%) 944 (33.9%)
Male 68638 55940 (63.3%) 10854 (71.9%) 1844 (66.1%)
Unknown Sex 1 1 (0%) 0 (0%) 0 (0%)
Age
0-2 769 747 (0.8%) 22 (0.1%) 0 (0%)
3-5 275 251 (0.3%) 24 (0.2%) 0 (0%)
6-11 403 368 (0.4%) 33 (0.2%) 2 (0.1%)
12-17 707 637 (0.7%) 60 (0.4%) 10 (0.4%)
18-25 2328 2138 (2.4%) 145 (1%) 45 (1.6%)
26-49 17799 16122 (18.3%) 1166 (7.7%) 511 (18.5%)
50-69 36885 31556 (35.7%) 4169 (27.7%) 1160 (41.9%)
70-79 24860 19672 (22.3%) 4508 (30%) 680 (24.6%)
80+ 22094 16813 (19%) 4920 (32.7%) 361 (13%)
Race & Ethnicity
American Indian 350 295 (0.3%) 55 (0.4%) 0 (0%)
Asian 1694 1464 (1.7%) 205 (1.4%) 25 (0.9%)
Black 16815 13368 (15.1%) 2995 (19.9%) 452 (16.5%)
Hawaiian/Pacific Islander 297 255 (0.3%) 41 (0.3%) 1 (0%)
Hispanic/Latino 870 819 (0.9%) 29 (0.2%) 22 (0.8%)
White 41871 33360 (37.8%) 7401 (49.1%) 1110 (40.5%)
Other 44220 38750 (43.9%) 4336 (28.8%) 1134 (41.3%)
Past Medical History
Hypertension 35587 27323 ( 30.9 ) 7277 ( 48.2 ) 987 ( 35.4 )
Alcohol abuse 30130 23065 ( 26.1 ) 6194 ( 41 ) 871 ( 31.2 )
Drug abuse 26596 20287 ( 23 ) 5528 ( 36.6 ) 781 ( 28 )
Diabetes 21969 16905 ( 19.1 ) 4429 ( 29.3 ) 635 ( 22.8 )
Median Elixhauser score (sd) 0.3 (0.7) 0.2 (0.5) 1.4 (2) 0.6 (1.7)
Median pre admission cns (sd) 0 (0) 0 (0) 0 (0) 0 (0)
Median pre admission pns (sd) 0 (0) 0 (0) 0 (0) 0 (0.1)
Severity
Non-Severe 66252 57771 (65.4%) 7007 (46.4%) 1474 (52.7%)
Severe 39985 30576 (34.6%) 8084 (53.6%) 1325 (47.3%)
Median time to severe (sd) 0.6 (1) 0.8 (1.3) 0.4 (0.7) 0.8 (1.1)
Survival
Alive 87376 74389 (84.2%) 10392 (68.8%) 2595 (93.1%)
Deceased 18849 13953 (15.8%) 4705 (31.2%) 191 (6.9%)
Median time to death (sd) 15.3 (6.4) 15.9 (5.8) 22.4 (32.5) 36.9 (41.4)
Discharge
Discharged 103165 85639 (96.9%) 14801 (98%) 2725 (97.7%)
Not Discharged 3063 2701 (3.1%) 297 (2%) 65 (2.3%)
Median time to first discharge (sd) 8.4 (4.2) 6.5 (3.6) 12.8 (6.1) 14.5 (18.3)
Readmission
Not Readmitted 91646 76877 (87%) 12435 (82.4%) 2334 (83.4%)
Readmitted 14569 11456 (13%) 2650 (17.6%) 463 (16.6%)
Median time to first readmission (sd) 28.1 (20.3) 26.4 (17.6) 31.9 (33.7) 38.1 (28.4)
Median number of readmissions (sd) 0.1 (0.3) 0 (0) 0.1 (0.3) 0.1 (0.3)
write.csv(tableone_combine, 'tables/table1_combined.csv', row.names = FALSE)

Table One - Adults

tableone_adult <- create_tableone(demo_table = demo_table_adult,
                                  clinical_table = clinical_table_adult_clean) %>%
                                  rbind(., comorbidity_table_adults %>%
                                          select(Comorbidity, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
                                          rename(`Table 1` = "Comorbidity",
                                                 N = "Comorb_Total",
                                                 None = "NNC_N_%",
                                                 Central = "CNS_N_%",
                                                 Peripheral = "PNS_N_%")) %>% 
  slice(match(row_order_adult, `Table 1`))

kbl(tableone_adult %>%
    rename("No Neurological Condition (NNC)" = None)) %>%
  add_header_above(c(
  " ",
  "Total Patients" = 1,
  "Neurological Disease" = 3
  )) %>%
  kable_paper("striped", full_width = F) %>%
  pack_rows("Sex", 2, 4) %>%
  pack_rows("Age", 5, 9) %>%
  pack_rows("Race & Ethnicity", 10, 16) %>%
  pack_rows("Past Medical History", 17, 23) %>%
  pack_rows("Severity", 24, 26) %>%
  pack_rows("Survival", 27, 29) %>%
  pack_rows("Discharge", 30, 32) %>%
  pack_rows("Readmission", 33, 36)
Total Patients
Neurological Disease
Table 1 N No Neurological Condition (NNC) Central Peripheral
All Patients 104031 86321 (100%) 14938 (100%) 2772 (100%)
Sex
Female 36564 31445 (36.4%) 4185 (28%) 934 (33.7%)
Male 67466 54875 (63.6%) 10753 (72%) 1838 (66.3%)
Unknown Sex 1 1 (0%) 0 (0%) 0 (0%)
Age
18-25 2328 2138 (2.5%) 145 (1%) 45 (1.6%)
26-49 17799 16122 (18.7%) 1166 (7.8%) 511 (18.5%)
50-69 36885 31556 (36.6%) 4169 (28%) 1160 (42.1%)
70-79 24860 19672 (22.8%) 4508 (30.2%) 680 (24.7%)
80+ 22094 16813 (19.5%) 4920 (33%) 361 (13.1%)
Race & Ethnicity
American Indian 349 294 (0.3%) 55 (0.4%) 0 (0%)
Asian 1633 1412 (1.6%) 196 (1.3%) 25 (0.9%)
Black 16631 13202 (15.3%) 2979 (20%) 450 (16.5%)
Hawaiian/Pacific Islander 296 254 (0.3%) 41 (0.3%) 1 (0%)
Hispanic/Latino 850 799 (0.9%) 29 (0.2%) 22 (0.8%)
White 41206 32752 (38%) 7354 (49.3%) 1100 (40.3%)
Other 42971 37587 (43.6%) 4254 (28.5%) 1130 (41.4%)
Past Medical History
Hypertension 35566 27302 ( 31.6 %) 7277 ( 48.7 %) 987 ( 35.6 %)
Alcohol abuse 29918 22879 ( 26.5 %) 6169 ( 41.3 %) 870 ( 31.4 %)
Drug abuse 26389 20097 ( 23.3 %) 5512 ( 36.9 %) 780 ( 28.1 %)
Diabetes 21952 16888 ( 19.6 %) 4429 ( 29.6 %) 635 ( 22.9 %)
Median Elixhauser score (sd) 0.3 (0.7) 0.2 (0.5) 1.4 (2) 0.6 (1.7)
Median pre admission cns (sd) 0 (0) 0 (0) 0 (0) 0 (0)
Median pre admission pns (sd) 0 (0) 0 (0) 0 (0) 0 (0.1)
Severity
Non-Severe 64578 56181 (65.1%) 6935 (46.4%) 1462 (52.6%)
Severe 39468 30140 (34.9%) 8008 (53.6%) 1320 (47.4%)
Median time to severe (sd) 0.6 (1) 0.8 (1.3) 0.4 (0.7) 0.8 (1.1)
Survival
Alive 85212 72388 (83.9%) 10247 (68.6%) 2577 (93.1%)
Deceased 18820 13933 (16.1%) 4696 (31.4%) 191 (6.9%)
Median time to death (sd) 15.3 (6.4) 15.9 (5.8) 22.4 (32.5) 36.9 (41.4)
Discharge
Discharged 101032 83675 (96.9%) 14650 (98%) 2707 (97.7%)
Not Discharged 3005 2645 (3.1%) 295 (2%) 65 (2.3%)
Median time to first discharge (sd) 8.4 (4.2) 6.5 (3.6) 12.8 (6.1) 14.5 (18.3)
Readmission
Not Readmitted 89802 75170 (87.1%) 12312 (82.4%) 2320 (83.5%)
Readmitted 14237 11151 (12.9%) 2626 (17.6%) 460 (16.5%)
Median time to first readmission (sd) 28.1 (20.3) 26.4 (17.6) 31.9 (33.7) 38.1 (28.4)
Median number of readmissions (sd) 0.1 (0.3) 0 (0) 0.1 (0.3) 0.1 (0.3)
write.csv(tableone_adult, 'tables/table1_adults.csv', row.names = FALSE)

Table One - Pediatrics

# specify the order of Table 1
row_order_pediatric <- c(
  "All Patients", "Female", "Male", "Unknown Sex",
  "0-2", "3-5", "6-11", "12-17", "18-25",
  "26-49", "50-69", "70-79", "80+", "Unknown Age",
  "American Indian", "Asian", "Black",
  "Hawaiian/Pacific Islander",
  "Hispanic/Latino", "White", "Other",
  top_comorb_pediatrics, 
  "Median Elixhauser score  (sd) ",
  "Median pre admission cns  (sd) ",
  "Median pre admission pns  (sd) ",
  "Non-Severe", "Severe",
  "Median time to severe  (sd) ",
  "Alive", "Deceased",
  "Median time to death  (sd) ",
  "Discharged", "Not Discharged",
  "Median time to first discharge  (sd) ",
  "Not Readmitted", "Readmitted",
  "Median time to first readmission  (sd) ",
  "Median number of readmissions  (sd) "
  )

tableone_pediatric <- create_tableone(demo_table = demo_table_pediatric,
                                      clinical_table = clinical_table_pediatric_clean)  %>%
  rbind(., comorbidity_table_pediatric %>%
          select(Comorbidity, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>% 
          rename(`Table 1` = "Comorbidity",
                  N = "Comorb_Total",
                  None = "NNC_N_%",
                  Central = "CNS_N_%",
                  Peripheral = "PNS_N_%")) %>% 
  slice(match(row_order_pediatric, `Table 1`))

kbl(tableone_pediatric %>%
      rename("No Neurological Condition (NNC)" = None)) %>%
  add_header_above(c(
  " ",
  "Total Patients" = 1,
  "Neurological Disease" = 3
  )) %>%
  kable_paper("striped", full_width = F) %>%
  pack_rows("Sex", 2, 4) %>%
  pack_rows("Age", 5, 8) %>%
  pack_rows("Race & Ethnicity", 9, 15) %>%
  pack_rows("Past Medical History", 16, 22) %>%
  pack_rows("Severity", 23, 25) %>%
  pack_rows("Survival", 26, 28) %>%
  pack_rows("Discharge", 29, 31) %>%
  pack_rows("Readmission", 32, 35)
Total Patients
Neurological Disease
Table 1 N No Neurological Condition (NNC) Central Peripheral
All Patients 2198 2019 (100%) 163 (100%) 16 (100%)
Sex
Female 1026 954 (47.3%) 62 (38%) 10 (62.5%)
Male 1172 1065 (52.7%) 101 (62%) 6 (37.5%)
Unknown Sex 0 0 (0%) 0 (0%) 0 (0%)
Age
0-2 769 747 (37.3%) 22 (15.8%) 0 (0%)
3-5 275 251 (12.5%) 24 (17.3%) 0 (0%)
6-11 403 368 (18.4%) 33 (23.7%) 2 (16.7%)
12-17 707 637 (31.8%) 60 (43.2%) 10 (83.3%)
Race & Ethnicity
American Indian 1 1 (0%) 0 (0%) 0 (0%)
Asian 61 52 (2.6%) 9 (5.8%) 0 (0%)
Black 184 166 (8.3%) 16 (10.4%) 2 (12.5%)
Hawaiian/Pacific Islander 1 1 (0%) 0 (0%) 0 (0%)
Hispanic/Latino 20 20 (1%) 0 (0%) 0 (0%)
White 665 608 (30.2%) 47 (30.5%) 10 (62.5%)
Other 1249 1163 (57.8%) 82 (53.2%) 4 (25%)
Past Medical History
Alcohol abuse 212 186 ( 9.2 %) 25 ( 15.3 %) 1 ( 6.2 %)
Drug abuse 207 190 ( 9.4 %) 16 ( 9.8 %) 1 ( 6.2 %)
Weight loss 123 109 ( 5.4 %) 13 ( 8 %) 1 ( 6.2 %)
Cardiac arrhythmias 109 94 ( 4.7 %) 12 ( 7.4 %) 3 ( 18.8 %)
Median Elixhauser score (sd) 2.2 (4.5) 0 (0) 4.4 (9.2) 2.5 (3.6)
Median pre admission cns (sd) 0.2 (0.7) 0.1 (0.2) 1.9 (3.9) 0 (0)
Median pre admission pns (sd) 0 (0) 0 (0) 0 (0) 0.2 (0.6)
Severity
Non-Severe 1674 1590 (78.5%) 72 (48.6%) 12 (70.6%)
Severe 517 436 (21.5%) 76 (51.4%) 5 (29.4%)
Median time to severe (sd) 2.4 (5.3) 3.8 (9.5) 10.8 (33.1) 0.9 (2.5)
Survival
Alive 2164 2001 (99%) 145 (94.2%) 18 (100%)
Deceased 29 20 (1%) 9 (5.8%) 0 (0%)
Median time to death (sd) 12.6 (36.3) 53.5 (76.3) 5.8 (10.2) 95 (268.7)
Discharge
Discharged 2133 1964 (97.2%) 151 (98.7%) 18 (100%)
Not Discharged 58 56 (2.8%) 2 (1.3%) 0 (0%)
Median time to first discharge (sd) 5.1 (4.2) 3.8 (4.2) 5.8 (2.7) 5.9 (4.5)
Readmission
Not Readmitted 1844 1707 (84.8%) 123 (83.7%) 14 (82.4%)
Readmitted 332 305 (15.2%) 24 (16.3%) 3 (17.6%)
Median time to first readmission (sd) 20 (20.2) 20.1 (19) 75.3 (86) 14.4 (22.8)
Median number of readmissions (sd) 0.1 (0.3) 0 (0) 0.2 (0.4) 0.4 (0.5)
write.csv(tableone_pediatric, 'tables/table1_pediatric.csv', row.names = FALSE)

Demographic & Clinical Course Analysis

Next we will evaluate characteristics among patients stratified by neurological status: 1) patients with no neurological condition (NNC), 2) patients with a central nervous system condition (CNS), and 3) patients with a peripheral nervous system condition (PNS).

Categorical variables will be evaluated with chi-square tests, while continuous variables will be evaluated with a non-parametric anova (Kruskal-Wallis) test.

1. Conduct chi-squared tests for categorical demographic variables

compute_chi_square <- function(tableone, demo_table) {
  
  N = tableone %>% select(N) %>% head(1) %>% as.numeric()

  # sum of all neuro condition groups
  cns_n = as.numeric(gsub( " .*$", "", tableone[1,"Central"]))
  pns_n = as.numeric(gsub( " .*$", "", tableone[1,"Peripheral"]))
  none_n = as.numeric(gsub( " .*$", "", tableone[1,"None"]))
  
  # sum up the total counts of each demographic variable across healthcare systems
  tableOne_sums <- demo_table %>%
    group_by(variable, Demo_var, Demo_var_i) %>%
    summarise(across(
      starts_with("n_var"),
      function(x) sum(x, na.rm = TRUE)
    ), .groups = "drop")
  
  # create list of demographic/clinical variables
  vars = tableOne_sums$variable
  
  # for binary variables, we should group by category 
  # Thus, only run the analysis for sex.male rather than both sex.female AND sex.male which would be redundant
  # we will also remove `age_group.unknown` since their is only 1 patient here
  exclude <- c("readmitted.false", "sex.female", "sex.other", "survival.deceased", "severity.non-severe",
               "covid_discharged.discharged", "age_group.Unknown")
  vars <- vars[!vars %in% exclude]
  
  # create empty list for chi.square test results
  chi_result_list = list()
  
  # for each variable, we will run the chi.square test
  for(i in vars) {
    test = calc_chisq(i, tableOne_sums, none_n, pns_n, cns_n)
    X2 <- round(test$statistic, 4)
    p_value <- test$p.value
    df <- test$parameter
    Variable = paste(i)
    
    chi_results <- cbind(Variable, X2, p_value, df) %>% 
      data.frame() %>% 
      mutate(p_value = as.numeric(p_value))
    
    rownames(chi_results) <- NULL
    
    chi_result_list[[i]] <- chi_results
  }
  
  # save list of chi.square test results
  chisq_results <- bind_rows(chi_result_list)
  
  return(chisq_results)
  
  
  }

Combined Adult & Pediatric Chi-Square Results

chisq_combine <- compute_chi_square(tableone = tableone_combine %>%
                                      filter(!`Table 1` == 'Discharged',
                                             !`Table 1` == 'Not Discharged'),
                                    demo_table = demo_table_combine %>%
                                      filter(!variable == 'covid_discharged.discharged',
                                             !variable == 'covid_discharged.not discharged'))

datatable(chisq_combine %>% arrange(p_value))

Adult Chi-Square Results

chisq_adult <- compute_chi_square(tableone = tableone_adult %>%
                                      filter(!`Table 1` == 'Discharged',
                                             !`Table 1` == 'Not Discharged'),
                                    demo_table = demo_table_adult %>%
                                      filter(!variable == 'covid_discharged.discharged',
                                             !variable == 'covid_discharged.not discharged'))

datatable(chisq_adult %>% arrange(p_value))

Pediatric Chi-Square Results

chisq_pediatric <- compute_chi_square(tableone = tableone_pediatric %>%
                                      filter(!`Table 1` == 'Discharged',
                                             !`Table 1` == 'Not Discharged'),
                                    demo_table = demo_table_pediatric %>%
                                      filter(!variable == 'covid_discharged.discharged',
                                             !variable == 'covid_discharged.not discharged'))

datatable(chisq_pediatric %>% arrange(p_value))

2. Conduct non-parametric anova (Kruskal-Wallis) tests for continuous clinical variables

compute_kruskal <- function(clinical_table) {

  # create empty list to save processed variables
  processed_list_figs <- list()
  
  # create a list of clinical variables to further pre-process
  vars_to_process <- unique(clinical_table$name)
  
  # for each variable, we will remove the [min, max] as before
  for (i in vars_to_process) {
    mod_table <- clinical_table %>%
      rename("var" = name) %>%
      filter(var == i) %>%
      mutate(
        site = toupper(site),
        None = sub("(\\(.*|\\[.*)", "", None),
        Peripheral = sub("(\\(.*|\\[.*)", "", Peripheral),
        Central = sub("(\\(.*|\\[.*)", "", Central)
      ) %>%
      mutate(across(None:Central, as.numeric))
  
    processed_list_figs[[i]] <- mod_table
  }
  
  clinical_table_anova <- bind_rows(processed_list_figs)
  
  # format the continuous variables
  cont_vars = clinical_table_anova %>%
    mutate(
      # create a variable without mean/median prefix
      grouped_var = gsub("Mean |Median | \\[Min, Max\\]| \\(SD\\)", "", var)
    ) %>%
    pivot_longer(
      cols = None:Central,
      names_to = "type"
    ) %>%
    mutate(type = factor(type, levels = c("None", "Central", "Peripheral"))) %>% 
    # select variables to analyze
    filter(grouped_var %in% c("Elixhauser score",
                              "pre admission cns", 
                              "pre admission pns", 
                              "time to death", 
                              "time to first discharge", 
                              "time to severe",
                              "number of readmissions",
                              "time to first readmission")) %>% 
    distinct()
  
  # create function to conduct anova
  cont_var_results <- function(variable) {
    
    df <- cont_vars %>% 
    filter(grouped_var == paste(variable))
    
    # kruskal.test calculates the kruskal-Wallis H-statistic 
    # does not assume normality between groups
    # also called the one-way ANOVA on ranks
    test = kruskal.test(df$value ~ df$type)
    
  }
  
  # create empty list for Kruskal-Wallis results
  cont_vars_list = list()
  
  # create list of unique variables
  outcome_cont_vars = unique(cont_vars$grouped_var)
  
  for(i in outcome_cont_vars) {
    
    test = cont_var_results(i)
    X2 <- round(test$statistic, 4)
    p_value <- test$p.value
    df <- test$parameter
    Variable = paste(i)
    
    kw <- cbind(Variable, X2, p_value, df) %>% 
      data.frame() 
    
    rownames(kw) <- NULL
    
    cont_vars_list[[i]] <- kw
  
    
  }
  
  anova_results <- bind_rows(cont_vars_list)
  
  }

Combined Adult & Pediatric Kruskal-Wallis Results

kw_combine <- compute_kruskal(clinical_table = clinical_table_combine)
datatable(kw_combine %>% arrange(p_value))

Adult Kruskal-Wallis Results

Note: median CNS is 0 for all sites (reason for the NA values)

kw_adult <- compute_kruskal(clinical_table = clinical_table_adult)
datatable(kw_adult %>% arrange(p_value))

Pediatric Kruskal-Wallis Results

kw_pediatric <- compute_kruskal(clinical_table = clinical_table_pediatric)
datatable(kw_pediatric %>% arrange(p_value))

3. Combine chi-square and Kruskal-Wallis results

We will evaluate significance when controlling for False Discovery Rate (FDR)

tableone_stats <- function(chisq_results, anova_results, population) {

  demo_stats <- rbind(chisq_results %>% select(Variable, p_value),
                      anova_results %>% select(Variable, p_value))
  
  # adjust p-values with FDR
  demo_stats$adj_p_value <- p.adjust(demo_stats$p_value, method = "fdr")
  
  # round & format p-value 
  demo_stats <- demo_stats %>% 
    mutate(p_value = as.numeric(p_value),
           pvalue = if_else(p_value < 0.001, "< 0.001", paste(round(p_value, 3))),
           adj.p_value = if_else(adj_p_value < 0.001, "< 0.001", paste(round(adj_p_value, 3))))
  
  # tidy up the demographics stat table
  demo_stats_tidy <- demo_stats %>% 
    rename(`Unadjusted P-value (raw)` = p_value,
           `FDR Adjusted P-value (raw)` = adj_p_value,
           `Unadjusted P-value (tidy)` = pvalue,
           `FDR Adjusted P-value (tidy)` = adj.p_value) %>% 
    select(Variable, `Unadjusted P-value (raw)`, `FDR Adjusted P-value (raw)`, `Unadjusted P-value (tidy)`, `FDR Adjusted P-value (tidy)`)

  write.csv(demo_stats_tidy, paste0("tables/Table1_pvals_", population, ".csv"), row.names = FALSE)
  
  return(demo_stats_tidy)
  
}

4. Incorporate Comorbidities

We will also add the top 4 comorbidities for each population to our table one. For the combined adult and pediatric table, we will use the top 4 comorbidities of the adult population.

# define matrix for each comorbidity
comorb_matrix <- function(comorbidity_table, comorbidity, population) {

    if(population=='Combined') {
      comorbidity_table = comorbidity_table
    } else if (population=="Adult") {
      comorbidity_table = comorbidity_table %>% 
        filter(population == "Adult")
    } else if (population=="Pediatric") {
      comorbidity_table = comorbidity_table %>% 
        filter(population == "Pediatric")
    } else {
      print('incorrect population specification')
    }
  
  mat_comorb <- comorbidity_table %>%
    filter(Comorbidity == paste(comorbidity)) %>%
    group_by(Comorbidity) %>% 
    mutate(None_comorb = sum(None_Total, na.rm = TRUE),
    CNS_comorb = sum(CNS_Total, na.rm = TRUE),
    PNS_comorb = sum(PNS_Total, na.rm = TRUE)) %>%
    ungroup() %>%
    distinct(None_comorb, CNS_comorb, PNS_comorb) %>%
    mutate(none_dif = neuro_pt_counts_sum$None_n - None_comorb,
    pns_dif = neuro_pt_counts_sum$PNS_n - PNS_comorb,
    cns_dif = neuro_pt_counts_sum$CNS_n - CNS_comorb) %>%
    data.frame() %>%
    #as.integer() %>%
    matrix(nrow = 2, ncol = 3, byrow = TRUE)

  comorb_results <- chisq.test(unlist(mat_comorb))
  
  results_df <- data.frame(Variable = paste(comorbidity),
                                X2 = round(comorb_results$statistic, 4),
                                p_value = comorb_results$p.value,
                                df = comorb_results$parameter)
  row.names(results_df) <- NULL
  
  return(results_df)
}

## combined comorbidities
comorbidity_combined_chi_list <- list()

for(i in top_comorb_adults) {
  
  comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Combined")
  comorbidity_combined_chi_list[[i]] <- comorb_chi_results
}

comorbidity_combined_chi <- comorbidity_combined_chi_list %>%
  bind_rows() 

chisq_combine_comorb <- chisq_combine %>%
  mutate(X2 = as.numeric(X2),
  df = as.numeric(df)) %>%
  bind_rows(comorbidity_combined_chi) 



## adult comorbidities
comorbidity_adult_chi_list <- list()

for(i in top_comorb_adults) {
  
  comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Adult")
  comorbidity_adult_chi_list[[i]] <- comorb_chi_results
}

comorbidity_adult_chi <- comorbidity_adult_chi_list %>%
  bind_rows() 

chisq_adult_comorb <- chisq_adult %>%
  mutate(X2 = as.numeric(X2),
  df = as.numeric(df)) %>%
  bind_rows(comorbidity_adult_chi) 

## pediatric comorbidities
comorbidity_pediatric_chi_list <- list()

for(i in top_comorb_pediatrics) {
  
  comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Pediatric")
  comorbidity_pediatric_chi_list[[i]] <- comorb_chi_results
}

comorbidity_pediatric_chi <- comorbidity_pediatric_chi_list %>%
  bind_rows() 

chisq_pediatric_comorb <- chisq_pediatric %>%
  mutate(X2 = as.numeric(X2),
  df = as.numeric(df)) %>%
  bind_rows(comorbidity_pediatric_chi) 

Table One - Combined Analysis

combine_tableone_stats <- tableone_stats(chisq_results = chisq_combine_comorb,
                                         anova_results = kw_combine,
                                         population = "combined") 
  

datatable(combine_tableone_stats)

Table One - Adult Analysis

adult_tableone_stats <- tableone_stats(chisq_results = chisq_adult_comorb,
                                         anova_results = kw_adult,
                                         population = "adult") 
  

datatable(adult_tableone_stats)

Table One - Pediatric Analysis

pediatric_tableone_stats <- tableone_stats(chisq_results = chisq_pediatric_comorb,
                                         anova_results = kw_pediatric,
                                         population = "pediatric") 
  

datatable(pediatric_tableone_stats)

Neurological Diagnosis Analysis

Next, we will evaluate the diagnoses captured with the CNS and PNS groups. Specifically, we will examine the frequency of CNS and PNS diagnoses by patient age group and by clinical outcomes (mortality and COVID-19 severity).

1. Format neurological icd code data

# import list of ICD-9/ICD-10 codes
neuro_icds_10 <-
  readxl::read_excel("public-data/2020-02-22_neuro-icd10_CNSvPNS.xlsx", sheet = 2) %>% 
  rename(
    "icd" = `ICD-10`,
    "pns_cns" = `Nervous system Involvement (1=central, 2=peripheral, 3=irrelevant)`,
    "type" = `ICD-10_type (1=first three alphanumeric code only, 2=digits after decimal point)`) %>%
  filter(type == 1) %>% 
  mutate(pns_cns = as.factor(pns_cns) %>% fct_recode(
    Central = "1",
    Peripheral = "2"
  )) %>%
  distinct(icd, `Neurological Disease Category`, pns_cns, `ICD-10 Description Revised`) %>%
  rename("description" = `ICD-10 Description Revised`) %>%
  mutate(concept_type = "icd-10")

neuro_icds_9 <- read.csv("public-data/icd9_tab_CNSvPNS.csv") %>%
  rename(
    "Neurological Disease Category" = "Neurological.Disease.Category",
    "pns_cns" = `Nervous.system.Involvement..1.central..2.peripheral.`,
    "icd_description" = `icd9_desc_revised`
  ) %>%
  mutate(
    pns_cns = as.factor(pns_cns) %>% fct_recode(
      Central = "1",
      Peripheral = "2"
    ),
    concept_type = "DIAG-ICD9"
  ) %>%
  select(icd, `Neurological Disease Category`, pns_cns, icd_description) %>%
  rename("description" = icd_description) %>%
  mutate(concept_type = "icd-9")

# bind list of ICD-9/ICD-10 codes
icds <- rbind(neuro_icds_10, neuro_icds_9)

2. Calculate total number of neurological diagnoses for each adults & pediatrics

compute_neuro_counts <- function(sorted_sites, sample_size, is_pediatric = FALSE) {
  
    # create empty list to save processed diagnostic data
    diag_table_list <- list()
  
    if(is_pediatric==FALSE) {
      population = "adults"
      } else {
      population = "pediatrics"
      }
  
  # for each healthcare system, we will calculate the total number of patients with each ICD code
  for (i in sorted_sites) {
    tmp <- get(i)
    tmp_diag <- tmp[[c(
      "first_hosp_results",
      "icd_tables",
      paste0("icd_tables_", population),
      "demo_table"
    )]]
    tmp_diag$site <- tmp[["site"]]
    diag_table_list[[i]] <- tmp_diag %>%
      filter(variable == "sex.female" | variable == "sex.male" | variable == "sex.other") %>%
      select(site, contains("n_var"))
  }
  
  diag_table_wide <- bind_rows(diag_table_list)
  
  # we will count the total number and percent across healthcare systems
  diag_counts <- colSums(Filter(is.numeric, diag_table_wide), na.rm = TRUE) %>% 
    data.frame() %>% 
    mutate(perc = round(./sample_size * 100, 1))
  
  # format diagnostic table
  diag_counts <- tibble::rownames_to_column(diag_counts, "icd") %>%
    mutate(icd = gsub("n_var_", "", icd)) %>%
    rename(`Number of Patients` = ".") %>%
    full_join(., icds, by = "icd") %>%
    dplyr::arrange(desc(`Number of Patients`)) %>% 
    mutate(`Number of Patients (% of Cohort)` = paste(`Number of Patients`, "(", perc, ")", sep = " ")) %>% 
    select(pns_cns, `Neurological Disease Category`, concept_type, 
           icd, description, `Number of Patients (% of Cohort)`) %>% 
      rename(Code = "icd",
           `Nervous System` = "pns_cns",
           Description = "description",
           `ICD Version` = "concept_type") %>% 
    mutate(`Number of Patients (% of Cohort)` = if_else(`Number of Patients (% of Cohort)` == "NA ( NA )", "0 ( 0 )", `Number of Patients (% of Cohort)`),
           Code = if_else(Code == "NN", "No Neurological Disease (NNC)", Code))
  
  return(diag_counts)
  
  }
# create vectors containing patient sample sizes
n_adult <- as.numeric(tableone_adult$N)[1]
n_pediatric <- as.numeric(tableone_pediatric$N)[1]

# calculate the total neurological patient counts 
neuro_counts_adult <- compute_neuro_counts(sorted_sites = adult_sites,
                                           sample_size = n_adult, 
                                           is_pediatric = FALSE)

neuro_counts_pediatric <- compute_neuro_counts(sorted_sites = pediatric_sites,
                                           sample_size = n_pediatric, 
                                           is_pediatric = TRUE)

neuro_counts_combined <- neuro_counts_adult %>% 
  rename(`Number of Adult Patients (% of Cohort)` = `Number of Patients (% of Cohort)`) %>% 
  left_join(., neuro_counts_pediatric %>% 
              select(`ICD Version`, Code, `Number of Patients (% of Cohort)`) %>% 
              rename("Number of Pediatric Patients (% of Cohort`)" = `Number of Patients (% of Cohort)`))

write.csv(neuro_counts_combined, paste0("tables/Supp.Table2_diagnoses", ".csv"), row.names = FALSE)

Number (%) of Adult Patients with each Neurological Diagnosis

datatable(neuro_counts_adult) 

Number (%) of Pediatric Patients with each Neurological Diagnosis

datatable(neuro_counts_pediatric) 

Evaluate Diagnoses by Age & Outcome

compute_stratified_neuro_counts <- function(tableone, sorted_sites, is_pediatric = FALSE) {
  
  # create empty list to save processed diagnostic data
    diag_table_list <- list()
  
    if(is_pediatric==FALSE) {
      population = "adults"
      } else {
      population = "pediatrics"
      }
  
  # for each healthcare system, we will calculate the total number of patients with each ICD code
  for (i in sorted_sites) {
    #print(i)
    tmp <- get(i)
    tmp_diag <- tmp[[c(
      "first_hosp_results",
      "icd_tables",
      paste0("icd_tables_", population),
      "demo_table"
    )]]
    tmp_diag$site <- tmp[["site"]]
    try(
    diag_table_list[[i]] <- tmp_diag %>%
      select(site, variable, starts_with("n_var")) %>% 
      pivot_longer(cols = starts_with("n_var"), names_to = "code", values_to = "freq") %>% 
      ungroup()
    )
  }

  diag_table_wide_all <- bind_rows(diag_table_list) %>% 
      group_by(variable, code) %>% 
    # total number of patients who had each code for each variable across sites
      mutate(total_n_var  = sum(freq, na.rm = TRUE)) %>% 
    distinct(variable, code, total_n_var) %>% 
    mutate(code = gsub("n_var_", "", code),
           variable = gsub("Severity.", "", variable),
           variable = gsub("Survival.", "", variable),
           variable = gsub("readmitted", "", variable),
           variable = gsub("age_group", "", variable),
           variable = gsub("covid_discharged", "", variable),
           variable = if_else(variable == ".TRUE", "Readmitted", variable)) %>% 
    left_join(., icds %>% rename(code = "icd"), by = "code") %>% 
    ungroup() %>% 
    filter(!concept_type == "icd-9")


  # refactor neuro status
  diag_table_wide_all$pns_cns <- factor(diag_table_wide_all$pns_cns,
                                        levels=c("Central", "Peripheral"),
                                        labels=c("CNS", "PNS"))
  
  
  return(diag_table_wide_all)
  
  }
stratified_neuro_counts_adult <- compute_stratified_neuro_counts(sorted_sites = adult_sites,
                                                                 is_pediatric = FALSE,
                                                                 tableone = tableone_adult)

stratified_neuro_counts_pediatric <- compute_stratified_neuro_counts(sorted_sites = pediatric_sites,
                                                                 is_pediatric = TRUE,
                                                                 tableone = tableone_pediatric)
compute_stratified_neuro_counts_age <- function(stratified_neuro_counts, tableone) {
  
  stratified_neuro_counts$variable <- factor(stratified_neuro_counts$variable, 
                                     levels=c(".00to02", ".03to05", ".06to11", ".12to17",
                                              ".18to25", ".26to49", ".50to69", ".70to79", ".80plus"), 
                                     labels=c("0-2 Years", "3-5 Years", "6-11 Years", "12-17 Years",
                                              "18-25 Years", "26-49 Years", "50-69 Years", "70-79 Years", "80+ Years"))
  
  # total counts
  n_0_2 <- get_table_n(tableone, var = "0-2")
  n_3_5 = get_table_n(tableone, var = "3-5")
  n_6_11 = get_table_n(tableone, var = "6-11")
  n_12_17 = get_table_n(tableone, var = "12-17")
  n_18_25 = get_table_n(tableone, var = "18-25")
  n_26_49 = get_table_n(tableone, var = "26-49")
  n_50_69 = get_table_n(tableone, var = "50-69")
  n_70_79 = get_table_n(tableone, var = "70-79")
  n_80 = get_table_n(tableone, var = "80+")
  
  age_df <- data.frame("variable" = c("0-2 Years",
                                    "3-5 Years", 
                                    "6-11 Years", 
                                    "12-17 Years",
                                    "18-25 Years", 
                                    "26-49 Years", 
                                    "50-69 Years", 
                                    "70-79 Years", 
                                    "80+ Years"),
                     "n_var" = c(n_0_2,
                               n_3_5,
                               n_6_11,
                               n_12_17,
                               n_18_25,
                               n_26_49,
                               n_50_69,
                               n_70_79,
                               n_80))

  
  stratified_neuro_counts <- stratified_neuro_counts %>% 
    filter(grepl("Years", variable),
                !code == "NN", 
                !total_n_var == 0) %>% 
         # total_n would be calculating total codes across each age group which is not true reflection of patient counts b/c patients can have more than one code
         #mutate(total_n = sum(total_n_var)) %>% 
         ungroup() %>% 
         left_join(., age_df, by = "variable") %>% 
         mutate(n_var = as.numeric(n_var),
                perc = as.character(round(total_n_var/n_var*100,1)),
                perc = if_else(perc < 0.1, '<0.1', perc),
                perc = paste0(perc, "%")) 
  
  return(stratified_neuro_counts)
  
  
}
stratified_neuro_counts_age_adult <- compute_stratified_neuro_counts_age(stratified_neuro_counts = stratified_neuro_counts_adult,
                                                                         tableone = tableone_adult)

stratified_neuro_counts_age_pediatric <- compute_stratified_neuro_counts_age(stratified_neuro_counts = stratified_neuro_counts_pediatric,
                                                                 tableone = tableone_pediatric)

Diagnoses by Adult and Pediatric Populations

n_0_2 <- get_table_n(tableone_combine, var = "0-2")
#n_3_5 <- get_table_n(tableone_combine, var = "3-5")
n_6_11 <- get_table_n(tableone_combine, var = "6-11")
n_12_17 <- get_table_n(tableone_combine, var = "12-17")

total_ped_age_count = as.numeric(n_0_2) +  as.numeric(n_6_11) + as.numeric(n_12_17)

stratified_neuro_counts_age_pediatric_reformat = stratified_neuro_counts_age_pediatric %>%
  select(variable, pns_cns, description, n_var, total_n_var) %>%
  mutate(variable = "0-17 Years") %>%
  group_by(pns_cns, description) %>%
  mutate(total_n_var = sum(total_n_var, na.rm = TRUE)) %>%
  ungroup() %>%
  # n_var is the total per `age_group` stratification
  select(-n_var) %>%
  distinct() %>%
  mutate(perc = round(total_n_var/total_ped_age_count*100,1),
         perc = if_else(perc < 0.1, '<0.1', as.character(perc)),
         perc = paste0(perc, "%"))

stratified_neuro_counts_age_combined <- stratified_neuro_counts_age_adult %>%
  select(variable, pns_cns, description, total_n_var, perc) %>%
  rbind(., stratified_neuro_counts_age_pediatric_reformat %>%
          select(variable, pns_cns, description, total_n_var, perc))

stratified_neuro_counts_age_combined$variable <- factor(stratified_neuro_counts_age_combined$variable,
                                                                      levels=c("0-17 Years", "18-25 Years", "26-49 Years", "50-69 Years", "70-79 Years", "80+ Years"))
group.colors <- c(NNC = "gray", PNS = "slateblue", CNS ="tomato")

adult_ped_combined_plot <- ggplot(stratified_neuro_counts_age_combined,
                                                aes(x = total_n_var, y = reorder(description,
                                                                                 total_n_var), 
                                                    fill = pns_cns)) +
  geom_bar(stat = "identity") +
  geom_text(
    aes(x = total_n_var, y = description, label = perc),
    hjust = -0.1,
    #hjust="inward",
    size = 6,
    inherit.aes = TRUE) +
  scale_x_continuous(expand = expansion(mult = c(0,0.2))) + # to ensure percent labels are within the figure
  facet_grid(pns_cns ~ variable, scales = "free", space = "free_y") + 
  scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +   
  scale_fill_manual(values = group.colors) +
  xlab("Number of Patients") +
  theme(legend.position = "none",
        strip.text.y = element_text(size = 40),
        strip.text.x = element_text(size = 35),
        axis.text.y.left = element_text(size = 25, face = "bold"),
        axis.text.x = element_text(size = 25),
        axis.title.x = element_text(size = 35))

ggsave("figures/Fig3_diagnoses_by_age_adult_ped_18group.png", adult_ped_combined_plot, width = 40, height = 19, dpi = 300)

Evaluate Diagnoses by Outcome

stratified_neuro_counts_adult$population = "adult"
stratified_neuro_counts_pediatric$population = "pediatric"

stratified_neuro_counts_combine <- rbind(stratified_neuro_counts_adult, stratified_neuro_counts_pediatric)

## determine total number of patients who died or severe
n_death_adult = get_table_n(df = tableone_adult, var = "Deceased") %>% as.numeric()
n_severe_adult = get_table_n(df = tableone_adult, var = "Severe") %>% as.numeric()

n_death_pediatric = get_table_n(df = tableone_pediatric, var = "Deceased") %>% as.numeric()
n_severe_pediatric = get_table_n(df = tableone_pediatric, var = "Severe") %>% as.numeric()

stratified_neuro_counts_outcomes <- stratified_neuro_counts_combine %>% 
  filter(variable %in% c("Severe", "Deceased"),
         !code == "NN", 
         !total_n_var == 0) %>%          
         # total_n would be calculating total codes across each outcome group which is not true reflection of patient counts b/c patients can have more than one code
         #mutate(total_n = sum(total_n_var)) %>% 
         group_by(variable, description, population) %>% 
  mutate(total_n_var = sum(total_n_var)) %>% 
  ungroup() %>% 
  mutate(perc = case_when(variable == "Severe" & population == "adult" ~ round(total_n_var/n_severe_adult*100,1),
                                 variable == "Deceased" & population == "adult" ~ round(total_n_var/n_death_adult*100,1),
                                 variable == "Severe" & population == "pediatric" ~ round(total_n_var/n_severe_pediatric*100,1),
                                 variable == "Deceased" & population == "pediatric" ~ round(total_n_var/n_death_pediatric*100,1)),
         perc = if_else(perc < 0.1, '<0.1', as.character(perc)),
       perc = paste0(perc, "%")) %>% 
  select(-code, -concept_type) %>% 
  distinct()
adult_outcomes = ggplot(stratified_neuro_counts_outcomes %>% 
         filter(population == "adult") %>% 
           mutate(variable = as.factor(variable) %>% 
                                          fct_recode(
                                            Mortality = "Deceased")),
       aes(x = total_n_var, y = reorder(description, total_n_var), fill = pns_cns)) +
  geom_bar(stat = "identity") +
 geom_text(
    aes(x = total_n_var, y = description, label = perc),
    hjust = -0.1,
    #hjust="inward",
    size = 5,
    inherit.aes = TRUE) +
  scale_x_continuous(expand = expansion(mult = c(0,0.2))) + 
  facet_grid(pns_cns ~ variable, scales = "free", space = "free_y") + 
  scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +   
  scale_fill_manual(values = group.colors) +
  xlab("Number of Patients") + 
  ggtitle("B. Adult") + 
  theme(legend.position = "none",
        plot.title = element_text(size = 44, face = "bold", hjust = -0.5), 
        strip.text.y = element_text(size = 35),
        strip.text.x = element_text(size = 30),
        axis.text.y.left = element_text(size = 20, face = "bold"),
        axis.text.x = element_text(size = 15),
        axis.title.x = element_text(size = 35))

ggsave("figures/SuppFig1_diagnoses_by_outcomes_adult.png", adult_outcomes, width = 27, height = 15, dpi = 300)
pediatric_outcomes = ggplot(stratified_neuro_counts_outcomes %>% 
         filter(population == "pediatric") %>% 
           mutate(variable = as.factor(variable) %>% 
                                          fct_recode(
                                            Mortality = "Deceased")),
       aes(x = total_n_var, y = reorder(description, total_n_var), fill = pns_cns)) +
  geom_bar(stat = "identity") +
 geom_text(
    aes(x = total_n_var, y = description, label = perc),
    hjust = -0.1,
    #hjust="inward",
    size = 5,
    inherit.aes = TRUE) +
  scale_x_continuous(expand = expansion(mult = c(0,0.2))) + 
  facet_grid(pns_cns ~ variable, scales = "free", space = "free_y") + 
  scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +   
  scale_fill_manual(values = group.colors) +
  xlab("") + 
  ggtitle("A. Pediatric") + 
  theme(legend.position = "none",
        plot.title = element_text(size = 44, face = "bold", hjust = -0.5), 
        strip.text.y = element_text(size = 35),
        strip.text.x = element_text(size = 30),
        axis.text.y.left = element_text(size = 20, face = "bold"),
        axis.text.x = element_text(size = 15),
        axis.title.x = element_text(size = 35))

ggsave("figures/SuppFig1_diagnoses_by_outcomes_pediatric.png", pediatric_outcomes, width = 27, height = 9, dpi = 300)

Evaluate patients with “Both” CNS and PNS diseases

These patients were excluded from the primary analysis due to potential confounding.

# create empty list to store the counts of "both" patients
both_counts_list <- list()

# for each healthcare system, retrieve the number of excluded "both" patients recorded
for (i in sorted_sites) {
  n_both <- results[[paste(i)]][["first_hosp_results"]][["both_counts"]]
  
  both_counts_list[[i]] <- n_both
}

both_counts <- bind_rows(both_counts_list) %>% t() %>% data.frame() %>% sum()

print(paste(both_counts, "(", round(both_counts/(as.numeric(tableone_combine$N[1])+both_counts)*100,2), "%)")) 
## [1] "1990 ( 1.84 %)"

Comorbidity Analysis

Next, we will evaluate the frequency of comorbidities in our population and the associated risk that comorbidities may incur for acquiring a CNS or PNS diagnosis during COVID-19 hospitalization.

1. Create a table of counts for each comorbidity and stratified by neurological status.

comorbidity_table_adult <- comorb_table_wide %>%
  select(Comorbidity, population, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
  filter(population == "Adult") %>% 
  rename(N = "Comorb_Total",
         NNC = "NNC_N_%",
         CNS = "CNS_N_%",
         PNS = "PNS_N_%") %>% 
  mutate(N = paste0(N, " (", round(N/n_adult, 2)*100, "%)"))

comorbidity_table_pediatric <- comorb_table_wide %>%
  select(Comorbidity, population, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
  filter(population == "Pediatric") %>% 
  rename(N = "Comorb_Total",
         NNC = "NNC_N_%",
         CNS = "CNS_N_%",
         PNS = "PNS_N_%") %>% 
  mutate(N = paste0(N, " (", round(N/n_pediatric, 2)*100, "%)"))

supp_comorbidity_table <- rbind(comorbidity_table_adult, comorbidity_table_pediatric) %>% 
  arrange(Comorbidity)

write.csv(supp_comorbidity_table, "tables/SuppTable_Comorbidity_Counts.csv", row.names = FALSE)

2. Create vector counts of patients with comorbidity data

neuro_pt_counts_sum_adult <- neuro_pt_counts %>% 
  filter(population == "Adult") %>% 
  select(-population)

neuro_pt_counts_sum_pediatric <- neuro_pt_counts %>% 
  filter(population == "Pediatric") %>% 
  select(-population)

none_n_adult = neuro_pt_counts_sum_adult$None_n
cns_n_adult = neuro_pt_counts_sum_adult$CNS_n
pns_n_adult = neuro_pt_counts_sum_adult$PNS_n

none_n_pediatric = neuro_pt_counts_sum_pediatric$None_n
cns_n_pediatric = neuro_pt_counts_sum_pediatric$CNS_n
pns_n_pediatric = neuro_pt_counts_sum_pediatric$PNS_n

3. Calculate the relative risk (RR) of developing a CNS or PNS diagnosis for patients with each comorbidity

calculate_rr <- function(is_pediatric = FALSE) {
  
  if(is_pediatric == FALSE) {
    cns_n = cns_n_adult
    pns_n = pns_n_adult
    neuro_pt_counts_sum = neuro_pt_counts_sum_adult
    pop = "Adult"
    # create a list of comorbidities
    list_of_comorbs <- comorb_table_wide %>% 
      filter(population == paste(pop)) %>% 
      distinct(Comorbidity) 
    list_of_comorbs <- list_of_comorbs$Comorbidity
    } else  {
    cns_n = cns_n_pediatric
    pns_n = pns_n_pediatric
    neuro_pt_counts_sum = neuro_pt_counts_sum_pediatric
    pop = "Pediatric"
    # create a list of comorbidities
    list_of_comorbs <- comorb_table_wide %>% 
      filter(population == paste(pop)) %>% 
      distinct(Comorbidity) %>% 
      as.vector()
    list_of_comorbs <- list_of_comorbs$Comorbidity
  }
  
  
  # create empty list to save relative risk calculations
  rr_calcs <- list()
  
  # for each comorbidity, we will calculate the relative risk of having a CNS and PNS diagnosis
  for (i in list_of_comorbs) {
  
    # https://www.cdc.gov/csels/dsepd/ss1978/lesson3/section5.html
    # create matrix as:
    # exposed group - with outcome, without outcome
    # non exposed group - with outcome, without outcome
  
    # where variables ending with `_Total` are the total number of CNS, PNS, or NNC patients with the respective comorbidity.
    # variables ending with `_n` are the total numbers in the entire population
  
    # to calculate confidence intervals
  #https://sphweb.bumc.bu.edu/otlt/mph-modules/bs/bs704_confidence_intervals/bs704_confidence_intervals8.html
  
    rr_cns <- comorb_table_wide %>%
      filter(population == paste(pop),
             Comorbidity == paste(i)) %>%
      mutate(a = CNS_Total, # number CNS w/comorb
             b = Comorb_Total - CNS_Total, # number of comorb pts w/o CNS
             c = cns_n - CNS_Total, # number of CNS pts w/o comorb
             d = sum(neuro_pt_counts_sum) - Comorb_Total - c, # number of pts w/o comorb or CNS
             risk_cns = a/(a+b),
             risk_non_cns = c/(c+d),
             rr_CNS = risk_cns/risk_non_cns,
             # for confidence intervals
             ci_1 = (b/a)/(a+b), # (n1-x1)/n1 
             ci_2 = (d/c)/(c+d), # (n2-x2)/n2
             ci_root = sqrt(ci_1 + ci_2),
             ci_lower = log(rr_CNS) - (1.96*ci_root),
             ci_upper = log(rr_CNS) + (1.96*ci_root),
             l_ci_cns = exp(ci_lower),
             u_ci_cns = exp(ci_upper)) %>%
      select(Comorbidity, Comorb_Total, None_Total, CNS_Total, PNS_Total, rr_CNS, l_ci_cns, u_ci_cns)
  
      rr_pns <- comorb_table_wide %>%
      filter(population == paste(pop),
             Comorbidity == paste(i)) %>%
      mutate(a = PNS_Total, # number PNS w/comorb
             b = Comorb_Total - PNS_Total, # number of comorb pts w/o PNS
             c = pns_n - PNS_Total, # number of PNS pts w/o comorb
             d = sum(neuro_pt_counts_sum) - Comorb_Total - c, # number of pts w/o comorb or PNS
             risk_pns = a/(a+b),
             risk_non_pns = c/(c+d),
             rr_PNS = risk_pns/risk_non_pns,
             # for confidence intervals
             ci_1 = (b/a)/(a+b),
             ci_2 = (d/c)/(c+d),
             ci_root = sqrt(ci_1 + ci_2),
             ci_lower = log(rr_PNS) - (1.96*ci_root),
             ci_upper = log(rr_PNS) + (1.96*ci_root),
             l_ci_pns = exp(ci_lower),
             u_ci_pns = exp(ci_upper)) %>%
      select(Comorbidity, rr_PNS, l_ci_pns, u_ci_pns)
  
      rr <- rr_cns %>%
        left_join(., rr_pns, by = c("Comorbidity"))
  
    rr_calcs[[i]] <- rr
  
  }
  
  rr_results <- bind_rows(rr_calcs) %>%
      mutate(rr_CNS = round(rr_CNS, 2),
           l_ci_cns = round(l_ci_cns, 2),
           u_ci_cns = round(u_ci_cns, 2),
           rr_PNS = round(rr_PNS, 2),
           l_ci_pns = round(l_ci_pns, 2),
           u_ci_pns = round(u_ci_pns, 2),
           `RR CNS (95% CI)` = paste(rr_CNS, "(", l_ci_cns, ",", u_ci_cns, ")"),
           `RR PNS (95% CI)` = paste(rr_PNS, "(", l_ci_pns, ",", u_ci_pns, ")")) %>%
    select(Comorbidity, rr_CNS, l_ci_cns, u_ci_cns, `RR CNS (95% CI)`,
           rr_PNS, l_ci_pns, u_ci_pns, `RR PNS (95% CI)`)
  
  # tidy up data
  write.csv(rr_results %>%
              arrange(desc(rr_CNS)) %>%
              select(Comorbidity, `RR CNS (95% CI)`, `RR PNS (95% CI)`), 
            paste0("tables/Supp.Table4_RR_", pop, ".csv"), row.names = FALSE)
  
    # reformat data
  rr_results_tidy_rr <- rr_results %>%
    select(Comorbidity, rr_CNS, rr_PNS) %>%
    pivot_longer(cols = rr_CNS:rr_PNS, names_to = "Neuro Status", values_to = "RR") %>%
    mutate(`Neuro Status` = if_else(`Neuro Status` == "rr_CNS", "CNS", "PNS"))
  
  rr_results_tidy_l_ci <- rr_results %>%
    select(Comorbidity, l_ci_cns, l_ci_pns) %>%
    pivot_longer(cols = l_ci_cns:l_ci_pns, names_to = "Neuro Status", values_to = "l_CI") %>%
    mutate(`Neuro Status` = if_else(`Neuro Status` == "l_ci_cns", "CNS", "PNS"))
  
  rr_results_tidy_u_ci <- rr_results %>%
    select(Comorbidity, u_ci_cns, u_ci_pns) %>%
    pivot_longer(cols = u_ci_cns:u_ci_pns, names_to = "Neuro Status", values_to = "u_CI") %>%
    mutate(`Neuro Status` = if_else(`Neuro Status` == "u_ci_cns", "CNS", "PNS"))
  
  # combine all tidy results
  rr_results_tidy <- rr_results_tidy_rr %>%
    left_join(., rr_results_tidy_l_ci,
                               by = c("Comorbidity", "Neuro Status")) %>%
    left_join(., rr_results_tidy_u_ci,
                               by = c("Comorbidity", "Neuro Status"))
  
  # order by CNS diagnoses with higher RR
  cns_top_results <- rr_results_tidy %>%
    filter(`Neuro Status` == "CNS") %>%
    arrange(RR) %>%
    select(Comorbidity)
  
  rr_results_tidy$Comorbidity <-  ordered(rr_results_tidy$Comorbidity, levels = cns_top_results$Comorbidity)
  
  cns_rr_plot <- ggplot(rr_results_tidy %>%
           filter(`Neuro Status` == "CNS"),
         aes(x = RR, y = Comorbidity)) +
    geom_vline(aes(xintercept = 1), size = .25, linetype = "dashed") +
      geom_errorbarh(aes(xmax = u_CI, xmin = l_CI), size = 0.7, height = 0.2, color = "gray50") +
      geom_point(size = 1.5, color = "tomato") +
      scale_x_continuous(breaks = seq(0, 4, 1), labels = seq(0, 4, 1),
                         limits = c(0,4)) +
    ylab("") +
    xlab("Relative Risk") +
    ggtitle("CNS Patients") + 
    theme(axis.text.y = element_text(face = "bold"))
  
  # arrange by top PNS results
  pns_top_results <- rr_results_tidy %>%
    filter(`Neuro Status` == "PNS") %>%
    arrange(RR) %>%
    select(Comorbidity)
  
  rr_results_tidy$Comorbidity <-  ordered(rr_results_tidy$Comorbidity, levels = pns_top_results$Comorbidity)
  
  
  pns_rr_plot <- ggplot(rr_results_tidy %>%
           filter(`Neuro Status` == "PNS"),
         aes(x = RR, y = Comorbidity)) +
    geom_vline(aes(xintercept = 1), size = .25, linetype = "dashed") +
      geom_errorbarh(aes(xmax = u_CI, xmin = l_CI), size = 0.70, height = 0.2, color = "gray50") +
      geom_point(size = 1.5, color = "slateblue") +
      scale_x_continuous(breaks = seq(0, 1.5, 0.5), labels = seq(0, 1.5, 0.5),
                         limits = c(0,1.5)) +
    ylab("") +
    xlab("Relative Risk") +
    ggtitle("PNS Patients") + 
    theme(axis.text.y = element_text(face = "bold"))
  
  pns_rr_plot <- set_panel_size(pns_rr_plot,
                            width  = unit(7, "cm"),
                            height = unit(6.5, "in"))
  
  cns_rr_plot <- set_panel_size(cns_rr_plot,
                            width  = unit(7, "cm"),
                            height = unit(6.5, "in"))
  
  #grid.arrange(cns_rr_plot, pns_rr_plot, ncol=2)
  
  ggsave(paste0("figures/Figure4.relative_risk_", pop, ".png"), grid.arrange(cns_rr_plot, pns_rr_plot, ncol=2), width = 15, height = 8, dpi = 300)
  
  }

Relative Risk (RR)

Here we plot the relative risk of acquiring a CNS or PNS diagnosis in adult patients who have each comorbidity of the ECI compared to those who do not.

We are not able to compute this analysis in pediatric patients, due to the overall low incidence of comorbidities and neurological illness during COVID-19 hospitalization.

LPCA deviance explained

During the local analysis for each participating healthcare system, we computed the top 10 principal components using logistic principal component analysis (LPCA). LPCA was performed on the matrix of npatients x 29 ECI comorbidities. Across healthcare systems, 10 principal components explained at least 75% of the model deviance. 

This supplementary analysis was performed in order to determine the best way to adjust for each patient’s comorbidity burden in our downstream survival models.

compare_deviance <- function(sorted_sites, is_pediatric=FALSE) {
  
  pca_dev <- list()
  
  if(is_pediatric==FALSE) {
    population = "adults"
    sorted_sites = sorted_sites[!sorted_sites%in% c("GOSH_results", "BCH_results")]
    } else {
    population = "pediatrics"
    }

  # for each healthcare system, we will calculate the total number of patients with each ICD code
  for (i in sorted_sites) {
    print(i)
    tmp <- get(i)
    tmp_pca <- tmp[[c(
      "comorbidities",
      paste0("comorb_", population),
      "deviance_expl"
    )]] %>% 
      data.frame() %>% 
      rename(dev_expl = '.')
    tmp_pca$site <- tmp[["site"]]
    pca_dev[[i]] <- tmp_pca
    
  }
  
  pca_df <- bind_rows(pca_dev)
  
  return(pca_df)
  
}

pca_adult <- compare_deviance(sorted_sites = adult_sites, is_pediatric = FALSE)
## [1] "APHP_results"
## [1] "FRBDX_results"
## [1] "UKFR_results"
## [1] "ICSM_results"
## [1] "HPG23_results"
## [1] "NUH_results"
## [1] "MGB_results"
## [1] "UPENN_results"
## [1] "UMICH_results"
## [1] "NWU_results"
## [1] "UPITT_results"
## [1] "H12O_results"
## [1] "UKY_results"
## [1] "VA1_results"
## [1] "VA2_results"
## [1] "VA3_results"
## [1] "VA4_results"
## [1] "VA5_results"
## [1] "UCLA_results"
pca_data <- bind_rows(pca_adult)


theme_set(theme_classic())

pca_plot <- ggplot(pca_data, 
       aes(x = fct_reorder(site, -dev_expl), y = dev_expl, group = 1)) +
  geom_point(size = 2.5) +
  geom_line() +
  xlab("Healthcare System") + 
  ylab("Proportion of Deviance explained") +  
  ylim(0,1) + 
  theme(axis.text.x=element_text(angle=90, hjust=1, face = "bold", size = 12),
        axis.text.y=element_text(face = "bold"), 
        axis.title.y = element_text(face = "bold", size = 15)); pca_plot

ggsave("figures/SuppFig_pca_deviance.png", pca_plot, height = 6, width = 6)
LS0tDQp0aXRsZTogIioqUGF0aWVudCBDaGFyYWN0ZXJpc3RpY3MqKiINCmF1dGhvcjogIk1lZyBIdXRjaCwgSmkgU29uLCBUcmFuZyBMZSwgQ2h1YW4gSG9uZywgWHVhbiBXYW5nLCBaYWhyYSBTaGFrZXJpIg0KZGF0ZTogIjA0LzExLzIwMjMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiBzcGFjZWxhYg0KLS0tDQoNClRoaXMgbm90ZWJvb2sgZGVzY3JpYmVzIHRoZSAqKjEwNiwyMjkqKiBhZHVsdCBhbmQgcGVkaWF0cmljIGhvc3BpdGFsaXplZCBDT1ZJRC0xOSBwb3NpdGl2ZSBwYXRpZW50cyB0aGF0IHdlcmUgc3R1ZGllZCBhcyBwYXJ0IG9mIHRoZSBbQ29uc29ydGl1bSBmb3IgQ2xpbmljYWwgQ2hhcmFjdGVyaXphdGlvbiBvZiBDT1ZJRC0xOSBieSBFSFJdKGh0dHBzOi8vY292aWRjbGluaWNhbC5uZXQvKSBOZXVyb2xvZ3kgZ3JvdXAuIFRoaXMgYW5hbHlzaXMgY29udGFpbnMgdGhlIHJlc3VsdHMgb2YgcGF0aWVudHMgZnJvbSAyMSBkaWZmZXJlbnQgaGVhbHRoY2FyZSBzeXN0ZW1zIHNwYW5uaW5nIDYgY291bnRyaWVzLg0KDQo8Y2VudGVyPiFbXShkb2NzL2Fzc2V0cy9uZXVyby1zdHJhdGlmaWNhdGlvbi1kaWFncmFtLnBuZyl7d2lkdGg9IjM3MyJ9PC9jZW50ZXI+DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ3JpZEdyYXBoaWNzKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KGdncHVicikNCmxpYnJhcnkoRFQpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KGNvd3Bsb3QpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZ2x1ZSkNCmxpYnJhcnkoZWdnKQ0Kc291cmNlKCJSL3Bsb3RfdGhlbWUuUiIpDQpzb3VyY2UoIlIvZGVtb190YWJsZXMuUiIpDQpzb3VyY2UoIlIvdXRpbHMuUiIpDQpgYGANCg0KIyAqKkltcG9ydCBEYXRhIGZyb20gZWFjaCBIZWFsdGhjYXJlIFN5c3RlbSoqDQoNCkVhY2ggcGFydGljaXBhdGluZyBoZWFsdGhjYXJlIHN5c3RlbSByYW4gdGhlIGFuYWx5c2lzIGxvY2FsbHkgb24gcGF0aWVudCBsZXZlbCBkYXRhIHVzaW5nIG91ciBbY3VzdG9taXplZCBSIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9jb3ZpZGNsaW5pY2FsL1BoYXNlMi4xTmV1cm9SUGFja2FnZSkuIFRoZSBvdXRwdXQgb2YgZWFjaCBsb2NhbCBhbmFseXNpcyBjb25zaXN0ZWQgb2YgYWdncmVnYXRlZCBjb3VudHMgYW5kIHN1bW1hcnkgc3RhdGlzdGljcyAobWVhbnMsIG1lZGlhbnMsIGhhemFyZCByYXRpb3MsIHAtdmFsdWVzLCBldGMpLiBTdW1tYXJ5IHJlc3VsdHMgZnJvbSBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtIHdlcmUgdGhlbiBhZ2dyZWdhdGVkIGFuZCBhcmUgaW1wb3J0ZWQgYW5kIHRoZW4gY29tYmluZWQgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyByZWFkIGluIGZpbGVzIGZyb20gcmVzdWx0cyBmb2xkZXIgDQojIHRoaXMgZm9sZGVyIGNvbnRhaW5zIGFsbCBvZiB0aGUgbG9jYWwgaGVhbHRoY2FyZSBzeXN0ZW0gbGV2ZWwgYW5hbHlzZXMNCnJkYXMgPC0gbGlzdC5maWxlcygNCiAgcGF0aCA9ICJyZXN1bHRzIiwNCiAgcGF0dGVybiA9ICIucmRhIiwNCiAgZnVsbC5uYW1lcyA9IFRSVUUNCikNCg0KDQpmb3IgKHJkYSBpbiByZGFzKSB7DQogIGxvYWQocmRhKQ0KfQ0KDQpybShyZGFzLCByZGEpDQoNCiMgY3JlYXRlIGEgbGlzdCBvZiBwYXJ0aWNpcGF0aW5nIGhlYWx0aGNhcmUgc3lzdGVtcyBmcm9tIG91ciBzdHVkeSB0cmFja2luZyBzcHJlYWRzaGVldA0Kc2l0ZV9nb29nbGVfdXJsIDwtICJodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xZXBjWU5kXzBqQ1VNa3RPSGY4bXo1djY1MXp5MUpBTEQ2UGd6b2JyR1dEWS9lZGl0P3VzcD1zaGFyaW5nIg0KDQojIGxvYWQgc2l0ZSBwYXJhbWV0ZXJzDQpzaXRlX3BhcmFtcyA8LSBnb29nbGVzaGVldHM0OjpyZWFkX3NoZWV0KHNpdGVfZ29vZ2xlX3VybCwgc2hlZXQgPSAxKQ0Kc2l0ZV9hdmFpbHMgPC0gZ29vZ2xlc2hlZXRzNDo6cmVhZF9zaGVldChzaXRlX2dvb2dsZV91cmwsIHNoZWV0ID0gMikNCg0KIyBmaWx0ZXIgdGhlIGxpc3Qgb2Ygc2l0ZXMgd2hvIHJhbiB0aGUgYW5hbHlzaXMNCnNvcnRlZF9zaXRlcyA8LSBzaXRlX2F2YWlscyAlPiUNCiAgZmlsdGVyKCFpcy5uYShkYXRlX3Y0X3JlY2VpdmVkKSkgJT4lDQogIHB1bGwoc2l0ZWlkKSAlPiUNCiAgcGFzdGUoInJlc3VsdHMiLCBzZXAgPSAiXyIpDQoNCiMgbGlzdCBzaXRlcyB3aXRob3V0IHJhY2UNCnNpdGVzX3dvX3JhY2UgPC0gc2l0ZV9wYXJhbXMgJT4lDQogIGZpbHRlcighaW5jbHVkZV9yYWNlKSAlPiUNCiAgcHVsbChzaXRlaWQpDQoNCiMgY29tYmluZSBhbGwgcmRhIGZpbGVzIHdpdGggJ3Jlc3VsdHMnIGluIG5hbWUNCnJlc3VsdHMgPC0gbWdldChscyhwYXR0ZXJuID0gInJlc3VsdHMiKSkNCg0KIyMgbG9hZCBpbiB0aGUgcHJlLXByb2Nlc3NlZCBjb21vcmJpZGl0eSBhbmQgbWV0YS1kYXRhDQpsb2FkKCJwcm9jZXNzZWQvY29tb3JiaWRpdHlfdGFibGVfY25wcy5yZGEiKQ0KbG9hZCgicHJvY2Vzc2VkL25ldXJvX3B0X2NvdW50cy5yZGEiKSANCmBgYA0KDQojICoqUHJlLXByb2Nlc3NpbmcqKg0KDQpUaGUgZm9sbG93aW5nIGNvZGUgcHJlLXByb2Nlc3NlcyB0aGUgZGVtb2dyYXBoaWMgYW5kIGNsaW5pY2FsIGNvdXJzZSBkYXRhIGluIG9yZGVyIHRvIGNvbnN0cnVjdCBhIFRhYmxlIDEuDQoNCioqMS4gRm9ybWF0IHByaW1hcnkgZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzKioNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgY3JlYXRlIGxpc3Qgb2YgaG9zcGl0YWxzIGZvciBhZHVsdCBhbmQgcGVkaWF0cmljIGFuYWx5c2VzDQphZHVsdF9zaXRlcyA9IHNvcnRlZF9zaXRlc1shc29ydGVkX3NpdGVzICVpbiUgYygiQkNIX3Jlc3VsdHMiLCAiR09TSF9yZXN1bHRzIildDQoNCnBlZGlhdHJpY19zaXRlcyA9IHNvcnRlZF9zaXRlc1shc29ydGVkX3NpdGVzICVpbiUgYygiVkExX3Jlc3VsdHMiLCAiVkEyX3Jlc3VsdHMiLCAiVkEzX3Jlc3VsdHMiLCAiVkE0X3Jlc3VsdHMiLCAiVkE1X3Jlc3VsdHMiKV0NCg0KZGVtb190YWJsZV9hZHVsdCA8LSBjcmVhdGVfZGVtb190YWJsZW9uZShzb3J0ZWRfc2l0ZXMgPSBhZHVsdF9zaXRlcywgaXNfcGVkaWF0cmljID0gRkFMU0UpDQoNCmRlbW9fdGFibGVfcGVkaWF0cmljIDwtIGNyZWF0ZV9kZW1vX3RhYmxlb25lKHNvcnRlZF9zaXRlcyA9IHBlZGlhdHJpY19zaXRlcywgaXNfcGVkaWF0cmljID0gVFJVRSkNCg0KZGVtb190YWJsZV9jb21iaW5lIDwtIHJiaW5kKGRlbW9fdGFibGVfYWR1bHQsIGRlbW9fdGFibGVfcGVkaWF0cmljKQ0KYGBgDQoNCioqMi4gRm9ybWF0IGNsaW5pY2FsIGNoYXJhY3RlcmlzdGljcyAoaS5lOiBjb250aW51b3VzIHZhcmlhYmxlcyBzdWNoIGFzIG1lZGlhbiB0aW1lIHRvIGRpc2NoYXJnZSkqKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2xpbmljYWxfdGFibGVfYWR1bHQgPC0gY3JlYXRlX2NsaW5pY2FsX3RhYmxlb25lKHNvcnRlZF9zaXRlcyA9IGFkdWx0X3NpdGVzLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KY2xpbmljYWxfdGFibGVfcGVkaWF0cmljIDwtIGNyZWF0ZV9jbGluaWNhbF90YWJsZW9uZShzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsIGlzX3BlZGlhdHJpYyA9IFRSVUUpDQoNCmNsaW5pY2FsX3RhYmxlX2NvbWJpbmUgPC0gcmJpbmQoY2xpbmljYWxfdGFibGVfYWR1bHQsIGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpYykNCmBgYA0KDQoqKjMuIENvbmR1Y3QgYWRkaXRpb25hbCBwcmUtcHJvY2Vzc2luZyBvZiBjbGluaWNhbCB2YXJpYWJsZXMqKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2xpbmljYWxfdGFibGVfYWR1bHRfY2xlYW4gPC0gY2xlYW5fY2xpbmljYWxfdGFibGVzKGNsaW5pY2FsX3RhYmxlID0gY2xpbmljYWxfdGFibGVfYWR1bHQpDQoNCmNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpY19jbGVhbiA8LSBjbGVhbl9jbGluaWNhbF90YWJsZXMoY2xpbmljYWxfdGFibGUgPSBjbGluaWNhbF90YWJsZV9wZWRpYXRyaWMpDQoNCmNsaW5pY2FsX3RhYmxlX2NsZWFuX2NvbWJpbmUgPC0gcmJpbmQoY2xpbmljYWxfdGFibGVfYWR1bHRfY2xlYW4sIGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpY19jbGVhbikNCmBgYA0KDQoqKjQuIEFkZCBDb21vcmJpZGl0eSBkYXRhKioNCg0KSGVyZSB3ZSBjcmVhdGUgc2VwYXJhdGUgbGlzdHMgb2YgdGhlIHRvcCA0IGNvbW9yYmlkaXRpZXMgaW4gdGhlIGFkdWx0IGFuZCBwZWRpYXRyaWMgcG9wdWxhdGlvbnMNCg0KYGBge3J9DQpjb21vcmJpZGl0eV90YWJsZV9hZHVsdHMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogIGZpbHRlcihwb3B1bGF0aW9uID09ICJBZHVsdCIpICU+JSANCiAgYXJyYW5nZShkZXNjKENvbW9yYl9Ub3RhbCkpICU+JSANCiAgc2xpY2UoMTo0KSAjIHJldHVybiB0b3AgY29tb3JiaWRpdGllcw0KDQpjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogIGZpbHRlcihwb3B1bGF0aW9uID09ICJQZWRpYXRyaWMiKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhDb21vcmJfVG90YWwpKSAlPiUgDQogIHNsaWNlKDE6NCkgIyByZXR1cm4gdG9wIGNvbW9yYmlkaXRpZXMNCg0KdG9wX2NvbW9yYl9hZHVsdHMgPC0gY29tb3JiaWRpdHlfdGFibGVfYWR1bHRzJENvbW9yYmlkaXR5DQp0b3BfY29tb3JiX3BlZGlhdHJpY3MgPC0gY29tb3JiaWRpdHlfdGFibGVfcGVkaWF0cmljJENvbW9yYmlkaXR5DQpgYGANCg0KIyAqKkRlbW9ncmFwaGljcyoqDQoNCiMjIFRhYmxlIE9uZSAtIENvbWJpbmVkDQoNClRoZSBiZWxvdyB0YWJsZSBjb250YWlucyB0aGUgY291bnRzIGFuZCBzdW1tYXJ5IHN0YXRzIG9mIHRoZSBlbnRpcmUgY29ob3J0IChhZHVsdCAmIHBlZGlhdHJpYyBwYXRpZW50cykuDQoNCmBgYHtyfQ0KbmV1cm9fcHRfY291bnRzX3N1bSA8LSBjb2xTdW1zKG5ldXJvX3B0X2NvdW50c1ssLTFdKSAlPiUgDQogIGRhdGEuZnJhbWUoKSAlPiUgDQogIHQoKSAlPiUgDQogIGRhdGEuZnJhbWUoKQ0KDQojIHNwZWNpZnkgdGhlIG9yZGVyIG9mIFRhYmxlIDENCnJvd19vcmRlcl9hZHVsdCA8LSBjKA0KICAiQWxsIFBhdGllbnRzIiwgIkZlbWFsZSIsICJNYWxlIiwgIlVua25vd24gU2V4IiwNCiAgIjAtMiIsICIzLTUiLCAiNi0xMSIsICIxMi0xNyIsICIxOC0yNSIsDQogICIyNi00OSIsICI1MC02OSIsICI3MC03OSIsICI4MCsiLCAiVW5rbm93biBBZ2UiLA0KICAiQW1lcmljYW4gSW5kaWFuIiwgIkFzaWFuIiwgIkJsYWNrIiwNCiAgIkhhd2FpaWFuL1BhY2lmaWMgSXNsYW5kZXIiLA0KICAiSGlzcGFuaWMvTGF0aW5vIiwgIldoaXRlIiwgIk90aGVyIiwNCiAgdG9wX2NvbW9yYl9hZHVsdHMsIA0KICAiTWVkaWFuIEVsaXhoYXVzZXIgc2NvcmUgIChzZCkgIiwNCiAgIk1lZGlhbiBwcmUgYWRtaXNzaW9uIGNucyAgKHNkKSAiLA0KICAiTWVkaWFuIHByZSBhZG1pc3Npb24gcG5zICAoc2QpICIsDQogICJOb24tU2V2ZXJlIiwgIlNldmVyZSIsDQogICJNZWRpYW4gdGltZSB0byBzZXZlcmUgIChzZCkgIiwNCiAgIkFsaXZlIiwgIkRlY2Vhc2VkIiwNCiAgIk1lZGlhbiB0aW1lIHRvIGRlYXRoICAoc2QpICIsDQogICJEaXNjaGFyZ2VkIiwgIk5vdCBEaXNjaGFyZ2VkIiwNCiAgIk1lZGlhbiB0aW1lIHRvIGZpcnN0IGRpc2NoYXJnZSAgKHNkKSAiLA0KICAiTm90IFJlYWRtaXR0ZWQiLCAiUmVhZG1pdHRlZCIsDQogICJNZWRpYW4gdGltZSB0byBmaXJzdCByZWFkbWlzc2lvbiAgKHNkKSAiLA0KICAiTWVkaWFuIG51bWJlciBvZiByZWFkbWlzc2lvbnMgIChzZCkgIg0KICApDQoNCg0KdGFibGVvbmVfY29tYmluZSA8LSBjcmVhdGVfdGFibGVvbmUoZGVtb190YWJsZSA9IGRlbW9fdGFibGVfY29tYmluZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3RhYmxlID0gY2xpbmljYWxfdGFibGVfY2xlYW5fY29tYmluZSkgJT4lDQogIHJiaW5kKC4sIGNvbW9yYl90YWJsZV93aWRlICU+JQ0KICAgICAgICAgIGdyb3VwX2J5KENvbW9yYmlkaXR5KSAlPiUNCiAgICAgICAgICBtdXRhdGUoQ29tb3JiX1RvdGFsID0gc3VtKENvbW9yYl9Ub3RhbCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICBOb25lX3N1bSA9IHN1bShOb25lX1RvdGFsLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgIENOU19zdW0gPSBzdW0oQ05TX1RvdGFsLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgIFBOU19zdW0gPSBzdW0oUE5TX1RvdGFsLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgIE5vbmVfcGVyYyA9IHJvdW5kKE5vbmVfc3VtL25ldXJvX3B0X2NvdW50c19zdW0kTm9uZV9uKjEwMCwxKSwNCiAgICAgICAgICBDZW50cmFsX3BlcmMgPSByb3VuZChDTlNfc3VtL25ldXJvX3B0X2NvdW50c19zdW0kQ05TX24qMTAwLDEpLA0KICAgICAgICAgIFBlcmlwaGVyYWxfcGVyYyA9IHJvdW5kKFBOU19zdW0vbmV1cm9fcHRfY291bnRzX3N1bSRQTlNfbioxMDAsMSksDQogICAgICAgICAgTm9uZSA9IHBhc3RlKE5vbmVfc3VtLCAiKCIsIE5vbmVfcGVyYywgIikiKSwNCiAgICAgICAgICBDZW50cmFsID0gcGFzdGUoQ05TX3N1bSwgIigiLCBDZW50cmFsX3BlcmMsICIpIiksDQogICAgICAgICAgUGVyaXBoZXJhbCA9IHBhc3RlKFBOU19zdW0sICIoIiwgUGVyaXBoZXJhbF9wZXJjLCAiKSIpKSAlPiUNCiAgICAgICAgICBkaXN0aW5jdChDb21vcmJpZGl0eSwgQ29tb3JiX1RvdGFsLCBOb25lLCBDZW50cmFsLCBQZXJpcGhlcmFsKSAlPiUNCiAgICAgICAgICByZW5hbWUoYFRhYmxlIDFgID0gIkNvbW9yYmlkaXR5IiwNCiAgICAgICAgICBOID0gIkNvbW9yYl9Ub3RhbCIpKSAlPiUNCiAgc2xpY2UobWF0Y2gocm93X29yZGVyX2FkdWx0LCBgVGFibGUgMWApKQ0KDQprYmwodGFibGVvbmVfY29tYmluZSAlPiUNCiAgICAgIHJlbmFtZSgiTm8gTmV1cm9sb2dpY2FsIENvbmRpdGlvbiAoTk5DKSIgPSBOb25lKSkgJT4lDQogICAgICBhZGRfaGVhZGVyX2Fib3ZlKGMoDQogICAgICAiICIsDQogICAgICAiVG90YWwgUGF0aWVudHMiID0gMSwNCiAgICAgICJOZXVyb2xvZ2ljYWwgRGlzZWFzZSIgPSAzDQogICAgICApKSAlPiUNCiAgICAgIGthYmxlX3BhcGVyKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYpICU+JQ0KICAgICAgcGFja19yb3dzKCJTZXgiLCAyLCA0KSAlPiUNCiAgICAgIHBhY2tfcm93cygiQWdlIiwgNSwgMTMpICU+JQ0KICAgICAgcGFja19yb3dzKCJSYWNlICYgRXRobmljaXR5IiwgMTQsIDIwKSAlPiUNCiAgICAgIHBhY2tfcm93cygiUGFzdCBNZWRpY2FsIEhpc3RvcnkiLCAyMSwgMjcpICU+JQ0KICAgICAgcGFja19yb3dzKCJTZXZlcml0eSIsIDI4LCAzMCkgJT4lDQogICAgICBwYWNrX3Jvd3MoIlN1cnZpdmFsIiwgMzEsIDMzKSAlPiUNCiAgICAgIHBhY2tfcm93cygiRGlzY2hhcmdlIiwgMzQsIDM2KSAlPiUNCiAgICAgIHBhY2tfcm93cygiUmVhZG1pc3Npb24iLCAzNywgNDApDQoNCndyaXRlLmNzdih0YWJsZW9uZV9jb21iaW5lLCAndGFibGVzL3RhYmxlMV9jb21iaW5lZC5jc3YnLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBUYWJsZSBPbmUgLSBBZHVsdHMNCg0KYGBge3J9DQp0YWJsZW9uZV9hZHVsdCA8LSBjcmVhdGVfdGFibGVvbmUoZGVtb190YWJsZSA9IGRlbW9fdGFibGVfYWR1bHQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdGFibGUgPSBjbGluaWNhbF90YWJsZV9hZHVsdF9jbGVhbikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmJpbmQoLiwgY29tb3JiaWRpdHlfdGFibGVfYWR1bHRzICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KENvbW9yYmlkaXR5LCBDb21vcmJfVG90YWwsIGBOTkNfTl8lYCwgYENOU19OXyVgLCBgUE5TX05fJWApICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVuYW1lKGBUYWJsZSAxYCA9ICJDb21vcmJpZGl0eSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiA9ICJDb21vcmJfVG90YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5vbmUgPSAiTk5DX05fJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2VudHJhbCA9ICJDTlNfTl8lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQZXJpcGhlcmFsID0gIlBOU19OXyUiKSkgJT4lIA0KICBzbGljZShtYXRjaChyb3dfb3JkZXJfYWR1bHQsIGBUYWJsZSAxYCkpDQoNCmtibCh0YWJsZW9uZV9hZHVsdCAlPiUNCiAgICByZW5hbWUoIk5vIE5ldXJvbG9naWNhbCBDb25kaXRpb24gKE5OQykiID0gTm9uZSkpICU+JQ0KICBhZGRfaGVhZGVyX2Fib3ZlKGMoDQogICIgIiwNCiAgIlRvdGFsIFBhdGllbnRzIiA9IDEsDQogICJOZXVyb2xvZ2ljYWwgRGlzZWFzZSIgPSAzDQogICkpICU+JQ0KICBrYWJsZV9wYXBlcigic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGKSAlPiUNCiAgcGFja19yb3dzKCJTZXgiLCAyLCA0KSAlPiUNCiAgcGFja19yb3dzKCJBZ2UiLCA1LCA5KSAlPiUNCiAgcGFja19yb3dzKCJSYWNlICYgRXRobmljaXR5IiwgMTAsIDE2KSAlPiUNCiAgcGFja19yb3dzKCJQYXN0IE1lZGljYWwgSGlzdG9yeSIsIDE3LCAyMykgJT4lDQogIHBhY2tfcm93cygiU2V2ZXJpdHkiLCAyNCwgMjYpICU+JQ0KICBwYWNrX3Jvd3MoIlN1cnZpdmFsIiwgMjcsIDI5KSAlPiUNCiAgcGFja19yb3dzKCJEaXNjaGFyZ2UiLCAzMCwgMzIpICU+JQ0KICBwYWNrX3Jvd3MoIlJlYWRtaXNzaW9uIiwgMzMsIDM2KQ0KDQp3cml0ZS5jc3YodGFibGVvbmVfYWR1bHQsICd0YWJsZXMvdGFibGUxX2FkdWx0cy5jc3YnLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBUYWJsZSBPbmUgLSBQZWRpYXRyaWNzDQoNCmBgYHtyfQ0KIyBzcGVjaWZ5IHRoZSBvcmRlciBvZiBUYWJsZSAxDQpyb3dfb3JkZXJfcGVkaWF0cmljIDwtIGMoDQogICJBbGwgUGF0aWVudHMiLCAiRmVtYWxlIiwgIk1hbGUiLCAiVW5rbm93biBTZXgiLA0KICAiMC0yIiwgIjMtNSIsICI2LTExIiwgIjEyLTE3IiwgIjE4LTI1IiwNCiAgIjI2LTQ5IiwgIjUwLTY5IiwgIjcwLTc5IiwgIjgwKyIsICJVbmtub3duIEFnZSIsDQogICJBbWVyaWNhbiBJbmRpYW4iLCAiQXNpYW4iLCAiQmxhY2siLA0KICAiSGF3YWlpYW4vUGFjaWZpYyBJc2xhbmRlciIsDQogICJIaXNwYW5pYy9MYXRpbm8iLCAiV2hpdGUiLCAiT3RoZXIiLA0KICB0b3BfY29tb3JiX3BlZGlhdHJpY3MsIA0KICAiTWVkaWFuIEVsaXhoYXVzZXIgc2NvcmUgIChzZCkgIiwNCiAgIk1lZGlhbiBwcmUgYWRtaXNzaW9uIGNucyAgKHNkKSAiLA0KICAiTWVkaWFuIHByZSBhZG1pc3Npb24gcG5zICAoc2QpICIsDQogICJOb24tU2V2ZXJlIiwgIlNldmVyZSIsDQogICJNZWRpYW4gdGltZSB0byBzZXZlcmUgIChzZCkgIiwNCiAgIkFsaXZlIiwgIkRlY2Vhc2VkIiwNCiAgIk1lZGlhbiB0aW1lIHRvIGRlYXRoICAoc2QpICIsDQogICJEaXNjaGFyZ2VkIiwgIk5vdCBEaXNjaGFyZ2VkIiwNCiAgIk1lZGlhbiB0aW1lIHRvIGZpcnN0IGRpc2NoYXJnZSAgKHNkKSAiLA0KICAiTm90IFJlYWRtaXR0ZWQiLCAiUmVhZG1pdHRlZCIsDQogICJNZWRpYW4gdGltZSB0byBmaXJzdCByZWFkbWlzc2lvbiAgKHNkKSAiLA0KICAiTWVkaWFuIG51bWJlciBvZiByZWFkbWlzc2lvbnMgIChzZCkgIg0KICApDQoNCnRhYmxlb25lX3BlZGlhdHJpYyA8LSBjcmVhdGVfdGFibGVvbmUoZGVtb190YWJsZSA9IGRlbW9fdGFibGVfcGVkaWF0cmljLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpY19jbGVhbikgICU+JQ0KICByYmluZCguLCBjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMgJT4lDQogICAgICAgICAgc2VsZWN0KENvbW9yYmlkaXR5LCBDb21vcmJfVG90YWwsIGBOTkNfTl8lYCwgYENOU19OXyVgLCBgUE5TX05fJWApICU+JSANCiAgICAgICAgICByZW5hbWUoYFRhYmxlIDFgID0gIkNvbW9yYmlkaXR5IiwNCiAgICAgICAgICAgICAgICAgIE4gPSAiQ29tb3JiX1RvdGFsIiwNCiAgICAgICAgICAgICAgICAgIE5vbmUgPSAiTk5DX05fJSIsDQogICAgICAgICAgICAgICAgICBDZW50cmFsID0gIkNOU19OXyUiLA0KICAgICAgICAgICAgICAgICAgUGVyaXBoZXJhbCA9ICJQTlNfTl8lIikpICU+JSANCiAgc2xpY2UobWF0Y2gocm93X29yZGVyX3BlZGlhdHJpYywgYFRhYmxlIDFgKSkNCg0Ka2JsKHRhYmxlb25lX3BlZGlhdHJpYyAlPiUNCiAgICAgIHJlbmFtZSgiTm8gTmV1cm9sb2dpY2FsIENvbmRpdGlvbiAoTk5DKSIgPSBOb25lKSkgJT4lDQogIGFkZF9oZWFkZXJfYWJvdmUoYygNCiAgIiAiLA0KICAiVG90YWwgUGF0aWVudHMiID0gMSwNCiAgIk5ldXJvbG9naWNhbCBEaXNlYXNlIiA9IDMNCiAgKSkgJT4lDQogIGthYmxlX3BhcGVyKCJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYpICU+JQ0KICBwYWNrX3Jvd3MoIlNleCIsIDIsIDQpICU+JQ0KICBwYWNrX3Jvd3MoIkFnZSIsIDUsIDgpICU+JQ0KICBwYWNrX3Jvd3MoIlJhY2UgJiBFdGhuaWNpdHkiLCA5LCAxNSkgJT4lDQogIHBhY2tfcm93cygiUGFzdCBNZWRpY2FsIEhpc3RvcnkiLCAxNiwgMjIpICU+JQ0KICBwYWNrX3Jvd3MoIlNldmVyaXR5IiwgMjMsIDI1KSAlPiUNCiAgcGFja19yb3dzKCJTdXJ2aXZhbCIsIDI2LCAyOCkgJT4lDQogIHBhY2tfcm93cygiRGlzY2hhcmdlIiwgMjksIDMxKSAlPiUNCiAgcGFja19yb3dzKCJSZWFkbWlzc2lvbiIsIDMyLCAzNSkNCg0Kd3JpdGUuY3N2KHRhYmxlb25lX3BlZGlhdHJpYywgJ3RhYmxlcy90YWJsZTFfcGVkaWF0cmljLmNzdicsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMgKipEZW1vZ3JhcGhpYyAmIENsaW5pY2FsIENvdXJzZSBBbmFseXNpcyoqDQoNCk5leHQgd2Ugd2lsbCBldmFsdWF0ZSBjaGFyYWN0ZXJpc3RpY3MgYW1vbmcgcGF0aWVudHMgc3RyYXRpZmllZCBieSBuZXVyb2xvZ2ljYWwgc3RhdHVzOiAxKSBwYXRpZW50cyB3aXRoIG5vIG5ldXJvbG9naWNhbCBjb25kaXRpb24gKE5OQyksIDIpIHBhdGllbnRzIHdpdGggYSBjZW50cmFsIG5lcnZvdXMgc3lzdGVtIGNvbmRpdGlvbiAoQ05TKSwgYW5kIDMpIHBhdGllbnRzIHdpdGggYSBwZXJpcGhlcmFsIG5lcnZvdXMgc3lzdGVtIGNvbmRpdGlvbiAoUE5TKS4NCg0KQ2F0ZWdvcmljYWwgdmFyaWFibGVzIHdpbGwgYmUgZXZhbHVhdGVkIHdpdGggY2hpLXNxdWFyZSB0ZXN0cywgd2hpbGUgY29udGludW91cyB2YXJpYWJsZXMgd2lsbCBiZSBldmFsdWF0ZWQgd2l0aCBhIG5vbi1wYXJhbWV0cmljIGFub3ZhIChLcnVza2FsLVdhbGxpcykgdGVzdC4NCg0KKioxLiBDb25kdWN0IGNoaS1zcXVhcmVkIHRlc3RzIGZvciBjYXRlZ29yaWNhbCBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMqKg0KDQpgYGB7cn0NCmNvbXB1dGVfY2hpX3NxdWFyZSA8LSBmdW5jdGlvbih0YWJsZW9uZSwgZGVtb190YWJsZSkgew0KICANCiAgTiA9IHRhYmxlb25lICU+JSBzZWxlY3QoTikgJT4lIGhlYWQoMSkgJT4lIGFzLm51bWVyaWMoKQ0KDQogICMgc3VtIG9mIGFsbCBuZXVybyBjb25kaXRpb24gZ3JvdXBzDQogIGNuc19uID0gYXMubnVtZXJpYyhnc3ViKCAiIC4qJCIsICIiLCB0YWJsZW9uZVsxLCJDZW50cmFsIl0pKQ0KICBwbnNfbiA9IGFzLm51bWVyaWMoZ3N1YiggIiAuKiQiLCAiIiwgdGFibGVvbmVbMSwiUGVyaXBoZXJhbCJdKSkNCiAgbm9uZV9uID0gYXMubnVtZXJpYyhnc3ViKCAiIC4qJCIsICIiLCB0YWJsZW9uZVsxLCJOb25lIl0pKQ0KICANCiAgIyBzdW0gdXAgdGhlIHRvdGFsIGNvdW50cyBvZiBlYWNoIGRlbW9ncmFwaGljIHZhcmlhYmxlIGFjcm9zcyBoZWFsdGhjYXJlIHN5c3RlbXMNCiAgdGFibGVPbmVfc3VtcyA8LSBkZW1vX3RhYmxlICU+JQ0KICAgIGdyb3VwX2J5KHZhcmlhYmxlLCBEZW1vX3ZhciwgRGVtb192YXJfaSkgJT4lDQogICAgc3VtbWFyaXNlKGFjcm9zcygNCiAgICAgIHN0YXJ0c193aXRoKCJuX3ZhciIpLA0KICAgICAgZnVuY3Rpb24oeCkgc3VtKHgsIG5hLnJtID0gVFJVRSkNCiAgICApLCAuZ3JvdXBzID0gImRyb3AiKQ0KICANCiAgIyBjcmVhdGUgbGlzdCBvZiBkZW1vZ3JhcGhpYy9jbGluaWNhbCB2YXJpYWJsZXMNCiAgdmFycyA9IHRhYmxlT25lX3N1bXMkdmFyaWFibGUNCiAgDQogICMgZm9yIGJpbmFyeSB2YXJpYWJsZXMsIHdlIHNob3VsZCBncm91cCBieSBjYXRlZ29yeSANCiAgIyBUaHVzLCBvbmx5IHJ1biB0aGUgYW5hbHlzaXMgZm9yIHNleC5tYWxlIHJhdGhlciB0aGFuIGJvdGggc2V4LmZlbWFsZSBBTkQgc2V4Lm1hbGUgd2hpY2ggd291bGQgYmUgcmVkdW5kYW50DQogICMgd2Ugd2lsbCBhbHNvIHJlbW92ZSBgYWdlX2dyb3VwLnVua25vd25gIHNpbmNlIHRoZWlyIGlzIG9ubHkgMSBwYXRpZW50IGhlcmUNCiAgZXhjbHVkZSA8LSBjKCJyZWFkbWl0dGVkLmZhbHNlIiwgInNleC5mZW1hbGUiLCAic2V4Lm90aGVyIiwgInN1cnZpdmFsLmRlY2Vhc2VkIiwgInNldmVyaXR5Lm5vbi1zZXZlcmUiLA0KICAgICAgICAgICAgICAgImNvdmlkX2Rpc2NoYXJnZWQuZGlzY2hhcmdlZCIsICJhZ2VfZ3JvdXAuVW5rbm93biIpDQogIHZhcnMgPC0gdmFyc1shdmFycyAlaW4lIGV4Y2x1ZGVdDQogIA0KICAjIGNyZWF0ZSBlbXB0eSBsaXN0IGZvciBjaGkuc3F1YXJlIHRlc3QgcmVzdWx0cw0KICBjaGlfcmVzdWx0X2xpc3QgPSBsaXN0KCkNCiAgDQogICMgZm9yIGVhY2ggdmFyaWFibGUsIHdlIHdpbGwgcnVuIHRoZSBjaGkuc3F1YXJlIHRlc3QNCiAgZm9yKGkgaW4gdmFycykgew0KICAgIHRlc3QgPSBjYWxjX2NoaXNxKGksIHRhYmxlT25lX3N1bXMsIG5vbmVfbiwgcG5zX24sIGNuc19uKQ0KICAgIFgyIDwtIHJvdW5kKHRlc3Qkc3RhdGlzdGljLCA0KQ0KICAgIHBfdmFsdWUgPC0gdGVzdCRwLnZhbHVlDQogICAgZGYgPC0gdGVzdCRwYXJhbWV0ZXINCiAgICBWYXJpYWJsZSA9IHBhc3RlKGkpDQogICAgDQogICAgY2hpX3Jlc3VsdHMgPC0gY2JpbmQoVmFyaWFibGUsIFgyLCBwX3ZhbHVlLCBkZikgJT4lIA0KICAgICAgZGF0YS5mcmFtZSgpICU+JSANCiAgICAgIG11dGF0ZShwX3ZhbHVlID0gYXMubnVtZXJpYyhwX3ZhbHVlKSkNCiAgICANCiAgICByb3duYW1lcyhjaGlfcmVzdWx0cykgPC0gTlVMTA0KICAgIA0KICAgIGNoaV9yZXN1bHRfbGlzdFtbaV1dIDwtIGNoaV9yZXN1bHRzDQogIH0NCiAgDQogICMgc2F2ZSBsaXN0IG9mIGNoaS5zcXVhcmUgdGVzdCByZXN1bHRzDQogIGNoaXNxX3Jlc3VsdHMgPC0gYmluZF9yb3dzKGNoaV9yZXN1bHRfbGlzdCkNCiAgDQogIHJldHVybihjaGlzcV9yZXN1bHRzKQ0KICANCiAgDQogIH0NCmBgYA0KDQoqKkNvbWJpbmVkIEFkdWx0ICYgUGVkaWF0cmljIENoaS1TcXVhcmUgUmVzdWx0cyoqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjaGlzcV9jb21iaW5lIDwtIGNvbXB1dGVfY2hpX3NxdWFyZSh0YWJsZW9uZSA9IHRhYmxlb25lX2NvbWJpbmUgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighYFRhYmxlIDFgID09ICdEaXNjaGFyZ2VkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFgVGFibGUgMWAgPT0gJ05vdCBEaXNjaGFyZ2VkJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW1vX3RhYmxlID0gZGVtb190YWJsZV9jb21iaW5lICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIXZhcmlhYmxlID09ICdjb3ZpZF9kaXNjaGFyZ2VkLmRpc2NoYXJnZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXZhcmlhYmxlID09ICdjb3ZpZF9kaXNjaGFyZ2VkLm5vdCBkaXNjaGFyZ2VkJykpDQoNCmRhdGF0YWJsZShjaGlzcV9jb21iaW5lICU+JSBhcnJhbmdlKHBfdmFsdWUpKQ0KYGBgDQoNCioqQWR1bHQgQ2hpLVNxdWFyZSBSZXN1bHRzKioNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNoaXNxX2FkdWx0IDwtIGNvbXB1dGVfY2hpX3NxdWFyZSh0YWJsZW9uZSA9IHRhYmxlb25lX2FkdWx0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIWBUYWJsZSAxYCA9PSAnRGlzY2hhcmdlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhYFRhYmxlIDFgID09ICdOb3QgRGlzY2hhcmdlZCcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVtb190YWJsZSA9IGRlbW9fdGFibGVfYWR1bHQgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcighdmFyaWFibGUgPT0gJ2NvdmlkX2Rpc2NoYXJnZWQuZGlzY2hhcmdlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhdmFyaWFibGUgPT0gJ2NvdmlkX2Rpc2NoYXJnZWQubm90IGRpc2NoYXJnZWQnKSkNCg0KZGF0YXRhYmxlKGNoaXNxX2FkdWx0ICU+JSBhcnJhbmdlKHBfdmFsdWUpKQ0KYGBgDQoNCioqUGVkaWF0cmljIENoaS1TcXVhcmUgUmVzdWx0cyoqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjaGlzcV9wZWRpYXRyaWMgPC0gY29tcHV0ZV9jaGlfc3F1YXJlKHRhYmxlb25lID0gdGFibGVvbmVfcGVkaWF0cmljICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIWBUYWJsZSAxYCA9PSAnRGlzY2hhcmdlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhYFRhYmxlIDFgID09ICdOb3QgRGlzY2hhcmdlZCcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVtb190YWJsZSA9IGRlbW9fdGFibGVfcGVkaWF0cmljICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIXZhcmlhYmxlID09ICdjb3ZpZF9kaXNjaGFyZ2VkLmRpc2NoYXJnZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXZhcmlhYmxlID09ICdjb3ZpZF9kaXNjaGFyZ2VkLm5vdCBkaXNjaGFyZ2VkJykpDQoNCmRhdGF0YWJsZShjaGlzcV9wZWRpYXRyaWMgJT4lIGFycmFuZ2UocF92YWx1ZSkpDQpgYGANCg0KKioyLiBDb25kdWN0IG5vbi1wYXJhbWV0cmljIGFub3ZhIChLcnVza2FsLVdhbGxpcykgdGVzdHMgZm9yIGNvbnRpbnVvdXMgY2xpbmljYWwgdmFyaWFibGVzKioNCg0KYGBge3J9DQpjb21wdXRlX2tydXNrYWwgPC0gZnVuY3Rpb24oY2xpbmljYWxfdGFibGUpIHsNCg0KICAjIGNyZWF0ZSBlbXB0eSBsaXN0IHRvIHNhdmUgcHJvY2Vzc2VkIHZhcmlhYmxlcw0KICBwcm9jZXNzZWRfbGlzdF9maWdzIDwtIGxpc3QoKQ0KICANCiAgIyBjcmVhdGUgYSBsaXN0IG9mIGNsaW5pY2FsIHZhcmlhYmxlcyB0byBmdXJ0aGVyIHByZS1wcm9jZXNzDQogIHZhcnNfdG9fcHJvY2VzcyA8LSB1bmlxdWUoY2xpbmljYWxfdGFibGUkbmFtZSkNCiAgDQogICMgZm9yIGVhY2ggdmFyaWFibGUsIHdlIHdpbGwgcmVtb3ZlIHRoZSBbbWluLCBtYXhdIGFzIGJlZm9yZQ0KICBmb3IgKGkgaW4gdmFyc190b19wcm9jZXNzKSB7DQogICAgbW9kX3RhYmxlIDwtIGNsaW5pY2FsX3RhYmxlICU+JQ0KICAgICAgcmVuYW1lKCJ2YXIiID0gbmFtZSkgJT4lDQogICAgICBmaWx0ZXIodmFyID09IGkpICU+JQ0KICAgICAgbXV0YXRlKA0KICAgICAgICBzaXRlID0gdG91cHBlcihzaXRlKSwNCiAgICAgICAgTm9uZSA9IHN1YigiKFxcKC4qfFxcWy4qKSIsICIiLCBOb25lKSwNCiAgICAgICAgUGVyaXBoZXJhbCA9IHN1YigiKFxcKC4qfFxcWy4qKSIsICIiLCBQZXJpcGhlcmFsKSwNCiAgICAgICAgQ2VudHJhbCA9IHN1YigiKFxcKC4qfFxcWy4qKSIsICIiLCBDZW50cmFsKQ0KICAgICAgKSAlPiUNCiAgICAgIG11dGF0ZShhY3Jvc3MoTm9uZTpDZW50cmFsLCBhcy5udW1lcmljKSkNCiAgDQogICAgcHJvY2Vzc2VkX2xpc3RfZmlnc1tbaV1dIDwtIG1vZF90YWJsZQ0KICB9DQogIA0KICBjbGluaWNhbF90YWJsZV9hbm92YSA8LSBiaW5kX3Jvd3MocHJvY2Vzc2VkX2xpc3RfZmlncykNCiAgDQogICMgZm9ybWF0IHRoZSBjb250aW51b3VzIHZhcmlhYmxlcw0KICBjb250X3ZhcnMgPSBjbGluaWNhbF90YWJsZV9hbm92YSAlPiUNCiAgICBtdXRhdGUoDQogICAgICAjIGNyZWF0ZSBhIHZhcmlhYmxlIHdpdGhvdXQgbWVhbi9tZWRpYW4gcHJlZml4DQogICAgICBncm91cGVkX3ZhciA9IGdzdWIoIk1lYW4gfE1lZGlhbiB8IFxcW01pbiwgTWF4XFxdfCBcXChTRFxcKSIsICIiLCB2YXIpDQogICAgKSAlPiUNCiAgICBwaXZvdF9sb25nZXIoDQogICAgICBjb2xzID0gTm9uZTpDZW50cmFsLA0KICAgICAgbmFtZXNfdG8gPSAidHlwZSINCiAgICApICU+JQ0KICAgIG11dGF0ZSh0eXBlID0gZmFjdG9yKHR5cGUsIGxldmVscyA9IGMoIk5vbmUiLCAiQ2VudHJhbCIsICJQZXJpcGhlcmFsIikpKSAlPiUgDQogICAgIyBzZWxlY3QgdmFyaWFibGVzIHRvIGFuYWx5emUNCiAgICBmaWx0ZXIoZ3JvdXBlZF92YXIgJWluJSBjKCJFbGl4aGF1c2VyIHNjb3JlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwcmUgYWRtaXNzaW9uIGNucyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInByZSBhZG1pc3Npb24gcG5zIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidGltZSB0byBkZWF0aCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWUgdG8gZmlyc3QgZGlzY2hhcmdlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidGltZSB0byBzZXZlcmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm51bWJlciBvZiByZWFkbWlzc2lvbnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWUgdG8gZmlyc3QgcmVhZG1pc3Npb24iKSkgJT4lIA0KICAgIGRpc3RpbmN0KCkNCiAgDQogICMgY3JlYXRlIGZ1bmN0aW9uIHRvIGNvbmR1Y3QgYW5vdmENCiAgY29udF92YXJfcmVzdWx0cyA8LSBmdW5jdGlvbih2YXJpYWJsZSkgew0KICAgIA0KICAgIGRmIDwtIGNvbnRfdmFycyAlPiUgDQogICAgZmlsdGVyKGdyb3VwZWRfdmFyID09IHBhc3RlKHZhcmlhYmxlKSkNCiAgICANCiAgICAjIGtydXNrYWwudGVzdCBjYWxjdWxhdGVzIHRoZSBrcnVza2FsLVdhbGxpcyBILXN0YXRpc3RpYyANCiAgICAjIGRvZXMgbm90IGFzc3VtZSBub3JtYWxpdHkgYmV0d2VlbiBncm91cHMNCiAgICAjIGFsc28gY2FsbGVkIHRoZSBvbmUtd2F5IEFOT1ZBIG9uIHJhbmtzDQogICAgdGVzdCA9IGtydXNrYWwudGVzdChkZiR2YWx1ZSB+IGRmJHR5cGUpDQogICAgDQogIH0NCiAgDQogICMgY3JlYXRlIGVtcHR5IGxpc3QgZm9yIEtydXNrYWwtV2FsbGlzIHJlc3VsdHMNCiAgY29udF92YXJzX2xpc3QgPSBsaXN0KCkNCiAgDQogICMgY3JlYXRlIGxpc3Qgb2YgdW5pcXVlIHZhcmlhYmxlcw0KICBvdXRjb21lX2NvbnRfdmFycyA9IHVuaXF1ZShjb250X3ZhcnMkZ3JvdXBlZF92YXIpDQogIA0KICBmb3IoaSBpbiBvdXRjb21lX2NvbnRfdmFycykgew0KICAgIA0KICAgIHRlc3QgPSBjb250X3Zhcl9yZXN1bHRzKGkpDQogICAgWDIgPC0gcm91bmQodGVzdCRzdGF0aXN0aWMsIDQpDQogICAgcF92YWx1ZSA8LSB0ZXN0JHAudmFsdWUNCiAgICBkZiA8LSB0ZXN0JHBhcmFtZXRlcg0KICAgIFZhcmlhYmxlID0gcGFzdGUoaSkNCiAgICANCiAgICBrdyA8LSBjYmluZChWYXJpYWJsZSwgWDIsIHBfdmFsdWUsIGRmKSAlPiUgDQogICAgICBkYXRhLmZyYW1lKCkgDQogICAgDQogICAgcm93bmFtZXMoa3cpIDwtIE5VTEwNCiAgICANCiAgICBjb250X3ZhcnNfbGlzdFtbaV1dIDwtIGt3DQogIA0KICAgIA0KICB9DQogIA0KICBhbm92YV9yZXN1bHRzIDwtIGJpbmRfcm93cyhjb250X3ZhcnNfbGlzdCkNCiAgDQogIH0NCmBgYA0KDQoqKkNvbWJpbmVkIEFkdWx0ICYgUGVkaWF0cmljIEtydXNrYWwtV2FsbGlzIFJlc3VsdHMqKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQprd19jb21iaW5lIDwtIGNvbXB1dGVfa3J1c2thbChjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX2NvbWJpbmUpDQpkYXRhdGFibGUoa3dfY29tYmluZSAlPiUgYXJyYW5nZShwX3ZhbHVlKSkNCmBgYA0KDQoqKkFkdWx0IEtydXNrYWwtV2FsbGlzIFJlc3VsdHMqKg0KDQoqTm90ZTogbWVkaWFuIENOUyBpcyAwIGZvciBhbGwgc2l0ZXMgKHJlYXNvbiBmb3IgdGhlIE5BIHZhbHVlcykqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmt3X2FkdWx0IDwtIGNvbXB1dGVfa3J1c2thbChjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX2FkdWx0KQ0KZGF0YXRhYmxlKGt3X2FkdWx0ICU+JSBhcnJhbmdlKHBfdmFsdWUpKQ0KYGBgDQoNCioqUGVkaWF0cmljIEtydXNrYWwtV2FsbGlzIFJlc3VsdHMqKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQprd19wZWRpYXRyaWMgPC0gY29tcHV0ZV9rcnVza2FsKGNsaW5pY2FsX3RhYmxlID0gY2xpbmljYWxfdGFibGVfcGVkaWF0cmljKQ0KZGF0YXRhYmxlKGt3X3BlZGlhdHJpYyAlPiUgYXJyYW5nZShwX3ZhbHVlKSkNCmBgYA0KDQoqKjMuIENvbWJpbmUgY2hpLXNxdWFyZSBhbmQgS3J1c2thbC1XYWxsaXMgcmVzdWx0cyoqDQoNCldlIHdpbGwgZXZhbHVhdGUgc2lnbmlmaWNhbmNlIHdoZW4gY29udHJvbGxpbmcgZm9yIEZhbHNlIERpc2NvdmVyeSBSYXRlIChGRFIpDQoNCmBgYHtyfQ0KdGFibGVvbmVfc3RhdHMgPC0gZnVuY3Rpb24oY2hpc3FfcmVzdWx0cywgYW5vdmFfcmVzdWx0cywgcG9wdWxhdGlvbikgew0KDQogIGRlbW9fc3RhdHMgPC0gcmJpbmQoY2hpc3FfcmVzdWx0cyAlPiUgc2VsZWN0KFZhcmlhYmxlLCBwX3ZhbHVlKSwNCiAgICAgICAgICAgICAgICAgICAgICBhbm92YV9yZXN1bHRzICU+JSBzZWxlY3QoVmFyaWFibGUsIHBfdmFsdWUpKQ0KICANCiAgIyBhZGp1c3QgcC12YWx1ZXMgd2l0aCBGRFINCiAgZGVtb19zdGF0cyRhZGpfcF92YWx1ZSA8LSBwLmFkanVzdChkZW1vX3N0YXRzJHBfdmFsdWUsIG1ldGhvZCA9ICJmZHIiKQ0KICANCiAgIyByb3VuZCAmIGZvcm1hdCBwLXZhbHVlIA0KICBkZW1vX3N0YXRzIDwtIGRlbW9fc3RhdHMgJT4lIA0KICAgIG11dGF0ZShwX3ZhbHVlID0gYXMubnVtZXJpYyhwX3ZhbHVlKSwNCiAgICAgICAgICAgcHZhbHVlID0gaWZfZWxzZShwX3ZhbHVlIDwgMC4wMDEsICI8IDAuMDAxIiwgcGFzdGUocm91bmQocF92YWx1ZSwgMykpKSwNCiAgICAgICAgICAgYWRqLnBfdmFsdWUgPSBpZl9lbHNlKGFkal9wX3ZhbHVlIDwgMC4wMDEsICI8IDAuMDAxIiwgcGFzdGUocm91bmQoYWRqX3BfdmFsdWUsIDMpKSkpDQogIA0KICAjIHRpZHkgdXAgdGhlIGRlbW9ncmFwaGljcyBzdGF0IHRhYmxlDQogIGRlbW9fc3RhdHNfdGlkeSA8LSBkZW1vX3N0YXRzICU+JSANCiAgICByZW5hbWUoYFVuYWRqdXN0ZWQgUC12YWx1ZSAocmF3KWAgPSBwX3ZhbHVlLA0KICAgICAgICAgICBgRkRSIEFkanVzdGVkIFAtdmFsdWUgKHJhdylgID0gYWRqX3BfdmFsdWUsDQogICAgICAgICAgIGBVbmFkanVzdGVkIFAtdmFsdWUgKHRpZHkpYCA9IHB2YWx1ZSwNCiAgICAgICAgICAgYEZEUiBBZGp1c3RlZCBQLXZhbHVlICh0aWR5KWAgPSBhZGoucF92YWx1ZSkgJT4lIA0KICAgIHNlbGVjdChWYXJpYWJsZSwgYFVuYWRqdXN0ZWQgUC12YWx1ZSAocmF3KWAsIGBGRFIgQWRqdXN0ZWQgUC12YWx1ZSAocmF3KWAsIGBVbmFkanVzdGVkIFAtdmFsdWUgKHRpZHkpYCwgYEZEUiBBZGp1c3RlZCBQLXZhbHVlICh0aWR5KWApDQoNCiAgd3JpdGUuY3N2KGRlbW9fc3RhdHNfdGlkeSwgcGFzdGUwKCJ0YWJsZXMvVGFibGUxX3B2YWxzXyIsIHBvcHVsYXRpb24sICIuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQ0KICANCiAgcmV0dXJuKGRlbW9fc3RhdHNfdGlkeSkNCiAgDQp9DQpgYGANCg0KKio0LiBJbmNvcnBvcmF0ZSBDb21vcmJpZGl0aWVzKioNCg0KV2Ugd2lsbCBhbHNvIGFkZCB0aGUgdG9wIDQgY29tb3JiaWRpdGllcyBmb3IgZWFjaCBwb3B1bGF0aW9uIHRvIG91ciB0YWJsZSBvbmUuIEZvciB0aGUgY29tYmluZWQgYWR1bHQgYW5kIHBlZGlhdHJpYyB0YWJsZSwgd2Ugd2lsbCB1c2UgdGhlIHRvcCA0IGNvbW9yYmlkaXRpZXMgb2YgdGhlIGFkdWx0IHBvcHVsYXRpb24uDQoNCmBgYHtyfQ0KIyBkZWZpbmUgbWF0cml4IGZvciBlYWNoIGNvbW9yYmlkaXR5DQpjb21vcmJfbWF0cml4IDwtIGZ1bmN0aW9uKGNvbW9yYmlkaXR5X3RhYmxlLCBjb21vcmJpZGl0eSwgcG9wdWxhdGlvbikgew0KDQogICAgaWYocG9wdWxhdGlvbj09J0NvbWJpbmVkJykgew0KICAgICAgY29tb3JiaWRpdHlfdGFibGUgPSBjb21vcmJpZGl0eV90YWJsZQ0KICAgIH0gZWxzZSBpZiAocG9wdWxhdGlvbj09IkFkdWx0Iikgew0KICAgICAgY29tb3JiaWRpdHlfdGFibGUgPSBjb21vcmJpZGl0eV90YWJsZSAlPiUgDQogICAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09ICJBZHVsdCIpDQogICAgfSBlbHNlIGlmIChwb3B1bGF0aW9uPT0iUGVkaWF0cmljIikgew0KICAgICAgY29tb3JiaWRpdHlfdGFibGUgPSBjb21vcmJpZGl0eV90YWJsZSAlPiUgDQogICAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09ICJQZWRpYXRyaWMiKQ0KICAgIH0gZWxzZSB7DQogICAgICBwcmludCgnaW5jb3JyZWN0IHBvcHVsYXRpb24gc3BlY2lmaWNhdGlvbicpDQogICAgfQ0KICANCiAgbWF0X2NvbW9yYiA8LSBjb21vcmJpZGl0eV90YWJsZSAlPiUNCiAgICBmaWx0ZXIoQ29tb3JiaWRpdHkgPT0gcGFzdGUoY29tb3JiaWRpdHkpKSAlPiUNCiAgICBncm91cF9ieShDb21vcmJpZGl0eSkgJT4lIA0KICAgIG11dGF0ZShOb25lX2NvbW9yYiA9IHN1bShOb25lX1RvdGFsLCBuYS5ybSA9IFRSVUUpLA0KICAgIENOU19jb21vcmIgPSBzdW0oQ05TX1RvdGFsLCBuYS5ybSA9IFRSVUUpLA0KICAgIFBOU19jb21vcmIgPSBzdW0oUE5TX1RvdGFsLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgZGlzdGluY3QoTm9uZV9jb21vcmIsIENOU19jb21vcmIsIFBOU19jb21vcmIpICU+JQ0KICAgIG11dGF0ZShub25lX2RpZiA9IG5ldXJvX3B0X2NvdW50c19zdW0kTm9uZV9uIC0gTm9uZV9jb21vcmIsDQogICAgcG5zX2RpZiA9IG5ldXJvX3B0X2NvdW50c19zdW0kUE5TX24gLSBQTlNfY29tb3JiLA0KICAgIGNuc19kaWYgPSBuZXVyb19wdF9jb3VudHNfc3VtJENOU19uIC0gQ05TX2NvbW9yYikgJT4lDQogICAgZGF0YS5mcmFtZSgpICU+JQ0KICAgICNhcy5pbnRlZ2VyKCkgJT4lDQogICAgbWF0cml4KG5yb3cgPSAyLCBuY29sID0gMywgYnlyb3cgPSBUUlVFKQ0KDQogIGNvbW9yYl9yZXN1bHRzIDwtIGNoaXNxLnRlc3QodW5saXN0KG1hdF9jb21vcmIpKQ0KICANCiAgcmVzdWx0c19kZiA8LSBkYXRhLmZyYW1lKFZhcmlhYmxlID0gcGFzdGUoY29tb3JiaWRpdHkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBYMiA9IHJvdW5kKGNvbW9yYl9yZXN1bHRzJHN0YXRpc3RpYywgNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBfdmFsdWUgPSBjb21vcmJfcmVzdWx0cyRwLnZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZiA9IGNvbW9yYl9yZXN1bHRzJHBhcmFtZXRlcikNCiAgcm93Lm5hbWVzKHJlc3VsdHNfZGYpIDwtIE5VTEwNCiAgDQogIHJldHVybihyZXN1bHRzX2RmKQ0KfQ0KDQojIyBjb21iaW5lZCBjb21vcmJpZGl0aWVzDQpjb21vcmJpZGl0eV9jb21iaW5lZF9jaGlfbGlzdCA8LSBsaXN0KCkNCg0KZm9yKGkgaW4gdG9wX2NvbW9yYl9hZHVsdHMpIHsNCiAgDQogIGNvbW9yYl9jaGlfcmVzdWx0cyA8LSBjb21vcmJfbWF0cml4KGNvbW9yYmlkaXR5X3RhYmxlID0gY29tb3JiX3RhYmxlX3dpZGUsIGksIHBvcHVsYXRpb24gPSAiQ29tYmluZWQiKQ0KICBjb21vcmJpZGl0eV9jb21iaW5lZF9jaGlfbGlzdFtbaV1dIDwtIGNvbW9yYl9jaGlfcmVzdWx0cw0KfQ0KDQpjb21vcmJpZGl0eV9jb21iaW5lZF9jaGkgPC0gY29tb3JiaWRpdHlfY29tYmluZWRfY2hpX2xpc3QgJT4lDQogIGJpbmRfcm93cygpIA0KDQpjaGlzcV9jb21iaW5lX2NvbW9yYiA8LSBjaGlzcV9jb21iaW5lICU+JQ0KICBtdXRhdGUoWDIgPSBhcy5udW1lcmljKFgyKSwNCiAgZGYgPSBhcy5udW1lcmljKGRmKSkgJT4lDQogIGJpbmRfcm93cyhjb21vcmJpZGl0eV9jb21iaW5lZF9jaGkpIA0KDQoNCg0KIyMgYWR1bHQgY29tb3JiaWRpdGllcw0KY29tb3JiaWRpdHlfYWR1bHRfY2hpX2xpc3QgPC0gbGlzdCgpDQoNCmZvcihpIGluIHRvcF9jb21vcmJfYWR1bHRzKSB7DQogIA0KICBjb21vcmJfY2hpX3Jlc3VsdHMgPC0gY29tb3JiX21hdHJpeChjb21vcmJpZGl0eV90YWJsZSA9IGNvbW9yYl90YWJsZV93aWRlLCBpLCBwb3B1bGF0aW9uID0gIkFkdWx0IikNCiAgY29tb3JiaWRpdHlfYWR1bHRfY2hpX2xpc3RbW2ldXSA8LSBjb21vcmJfY2hpX3Jlc3VsdHMNCn0NCg0KY29tb3JiaWRpdHlfYWR1bHRfY2hpIDwtIGNvbW9yYmlkaXR5X2FkdWx0X2NoaV9saXN0ICU+JQ0KICBiaW5kX3Jvd3MoKSANCg0KY2hpc3FfYWR1bHRfY29tb3JiIDwtIGNoaXNxX2FkdWx0ICU+JQ0KICBtdXRhdGUoWDIgPSBhcy5udW1lcmljKFgyKSwNCiAgZGYgPSBhcy5udW1lcmljKGRmKSkgJT4lDQogIGJpbmRfcm93cyhjb21vcmJpZGl0eV9hZHVsdF9jaGkpIA0KDQojIyBwZWRpYXRyaWMgY29tb3JiaWRpdGllcw0KY29tb3JiaWRpdHlfcGVkaWF0cmljX2NoaV9saXN0IDwtIGxpc3QoKQ0KDQpmb3IoaSBpbiB0b3BfY29tb3JiX3BlZGlhdHJpY3MpIHsNCiAgDQogIGNvbW9yYl9jaGlfcmVzdWx0cyA8LSBjb21vcmJfbWF0cml4KGNvbW9yYmlkaXR5X3RhYmxlID0gY29tb3JiX3RhYmxlX3dpZGUsIGksIHBvcHVsYXRpb24gPSAiUGVkaWF0cmljIikNCiAgY29tb3JiaWRpdHlfcGVkaWF0cmljX2NoaV9saXN0W1tpXV0gPC0gY29tb3JiX2NoaV9yZXN1bHRzDQp9DQoNCmNvbW9yYmlkaXR5X3BlZGlhdHJpY19jaGkgPC0gY29tb3JiaWRpdHlfcGVkaWF0cmljX2NoaV9saXN0ICU+JQ0KICBiaW5kX3Jvd3MoKSANCg0KY2hpc3FfcGVkaWF0cmljX2NvbW9yYiA8LSBjaGlzcV9wZWRpYXRyaWMgJT4lDQogIG11dGF0ZShYMiA9IGFzLm51bWVyaWMoWDIpLA0KICBkZiA9IGFzLm51bWVyaWMoZGYpKSAlPiUNCiAgYmluZF9yb3dzKGNvbW9yYmlkaXR5X3BlZGlhdHJpY19jaGkpIA0KYGBgDQoNCiMjIFRhYmxlIE9uZSAtIENvbWJpbmVkIEFuYWx5c2lzDQoNCmBgYHtyfQ0KY29tYmluZV90YWJsZW9uZV9zdGF0cyA8LSB0YWJsZW9uZV9zdGF0cyhjaGlzcV9yZXN1bHRzID0gY2hpc3FfY29tYmluZV9jb21vcmIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFub3ZhX3Jlc3VsdHMgPSBrd19jb21iaW5lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gImNvbWJpbmVkIikgDQogIA0KDQpkYXRhdGFibGUoY29tYmluZV90YWJsZW9uZV9zdGF0cykNCmBgYA0KDQojIyBUYWJsZSBPbmUgLSBBZHVsdCBBbmFseXNpcw0KDQpgYGB7cn0NCmFkdWx0X3RhYmxlb25lX3N0YXRzIDwtIHRhYmxlb25lX3N0YXRzKGNoaXNxX3Jlc3VsdHMgPSBjaGlzcV9hZHVsdF9jb21vcmIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFub3ZhX3Jlc3VsdHMgPSBrd19hZHVsdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiA9ICJhZHVsdCIpIA0KICANCg0KZGF0YXRhYmxlKGFkdWx0X3RhYmxlb25lX3N0YXRzKQ0KYGBgDQoNCiMjIFRhYmxlIE9uZSAtIFBlZGlhdHJpYyBBbmFseXNpcw0KDQpgYGB7cn0NCnBlZGlhdHJpY190YWJsZW9uZV9zdGF0cyA8LSB0YWJsZW9uZV9zdGF0cyhjaGlzcV9yZXN1bHRzID0gY2hpc3FfcGVkaWF0cmljX2NvbW9yYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5vdmFfcmVzdWx0cyA9IGt3X3BlZGlhdHJpYywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiA9ICJwZWRpYXRyaWMiKSANCiAgDQoNCmRhdGF0YWJsZShwZWRpYXRyaWNfdGFibGVvbmVfc3RhdHMpDQpgYGANCg0KIyAqKk5ldXJvbG9naWNhbCBEaWFnbm9zaXMgQW5hbHlzaXMqKg0KDQpOZXh0LCB3ZSB3aWxsIGV2YWx1YXRlIHRoZSBkaWFnbm9zZXMgY2FwdHVyZWQgd2l0aCB0aGUgQ05TIGFuZCBQTlMgZ3JvdXBzLiBTcGVjaWZpY2FsbHksIHdlIHdpbGwgZXhhbWluZSB0aGUgZnJlcXVlbmN5IG9mIENOUyBhbmQgUE5TIGRpYWdub3NlcyBieSBwYXRpZW50IGFnZSBncm91cCBhbmQgYnkgY2xpbmljYWwgb3V0Y29tZXMgKG1vcnRhbGl0eSBhbmQgQ09WSUQtMTkgc2V2ZXJpdHkpLg0KDQoqKjEuIEZvcm1hdCBuZXVyb2xvZ2ljYWwgaWNkIGNvZGUgZGF0YSoqDQoNCmBgYHtyIGZpZy53aWR0aD0xMn0NCiMgaW1wb3J0IGxpc3Qgb2YgSUNELTkvSUNELTEwIGNvZGVzDQpuZXVyb19pY2RzXzEwIDwtDQogIHJlYWR4bDo6cmVhZF9leGNlbCgicHVibGljLWRhdGEvMjAyMC0wMi0yMl9uZXVyby1pY2QxMF9DTlN2UE5TLnhsc3giLCBzaGVldCA9IDIpICU+JSANCiAgcmVuYW1lKA0KICAgICJpY2QiID0gYElDRC0xMGAsDQogICAgInBuc19jbnMiID0gYE5lcnZvdXMgc3lzdGVtIEludm9sdmVtZW50ICgxPWNlbnRyYWwsIDI9cGVyaXBoZXJhbCwgMz1pcnJlbGV2YW50KWAsDQogICAgInR5cGUiID0gYElDRC0xMF90eXBlICgxPWZpcnN0IHRocmVlIGFscGhhbnVtZXJpYyBjb2RlIG9ubHksIDI9ZGlnaXRzIGFmdGVyIGRlY2ltYWwgcG9pbnQpYCkgJT4lDQogIGZpbHRlcih0eXBlID09IDEpICU+JSANCiAgbXV0YXRlKHBuc19jbnMgPSBhcy5mYWN0b3IocG5zX2NucykgJT4lIGZjdF9yZWNvZGUoDQogICAgQ2VudHJhbCA9ICIxIiwNCiAgICBQZXJpcGhlcmFsID0gIjIiDQogICkpICU+JQ0KICBkaXN0aW5jdChpY2QsIGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWAsIHBuc19jbnMsIGBJQ0QtMTAgRGVzY3JpcHRpb24gUmV2aXNlZGApICU+JQ0KICByZW5hbWUoImRlc2NyaXB0aW9uIiA9IGBJQ0QtMTAgRGVzY3JpcHRpb24gUmV2aXNlZGApICU+JQ0KICBtdXRhdGUoY29uY2VwdF90eXBlID0gImljZC0xMCIpDQoNCm5ldXJvX2ljZHNfOSA8LSByZWFkLmNzdigicHVibGljLWRhdGEvaWNkOV90YWJfQ05TdlBOUy5jc3YiKSAlPiUNCiAgcmVuYW1lKA0KICAgICJOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeSIgPSAiTmV1cm9sb2dpY2FsLkRpc2Vhc2UuQ2F0ZWdvcnkiLA0KICAgICJwbnNfY25zIiA9IGBOZXJ2b3VzLnN5c3RlbS5JbnZvbHZlbWVudC4uMS5jZW50cmFsLi4yLnBlcmlwaGVyYWwuYCwNCiAgICAiaWNkX2Rlc2NyaXB0aW9uIiA9IGBpY2Q5X2Rlc2NfcmV2aXNlZGANCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIHBuc19jbnMgPSBhcy5mYWN0b3IocG5zX2NucykgJT4lIGZjdF9yZWNvZGUoDQogICAgICBDZW50cmFsID0gIjEiLA0KICAgICAgUGVyaXBoZXJhbCA9ICIyIg0KICAgICksDQogICAgY29uY2VwdF90eXBlID0gIkRJQUctSUNEOSINCiAgKSAlPiUNCiAgc2VsZWN0KGljZCwgYE5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5YCwgcG5zX2NucywgaWNkX2Rlc2NyaXB0aW9uKSAlPiUNCiAgcmVuYW1lKCJkZXNjcmlwdGlvbiIgPSBpY2RfZGVzY3JpcHRpb24pICU+JQ0KICBtdXRhdGUoY29uY2VwdF90eXBlID0gImljZC05IikNCg0KIyBiaW5kIGxpc3Qgb2YgSUNELTkvSUNELTEwIGNvZGVzDQppY2RzIDwtIHJiaW5kKG5ldXJvX2ljZHNfMTAsIG5ldXJvX2ljZHNfOSkNCmBgYA0KDQoqKjIuIENhbGN1bGF0ZSB0b3RhbCBudW1iZXIgb2YgbmV1cm9sb2dpY2FsIGRpYWdub3NlcyBmb3IgZWFjaCBhZHVsdHMgJiBwZWRpYXRyaWNzKioNCg0KYGBge3J9DQpjb21wdXRlX25ldXJvX2NvdW50cyA8LSBmdW5jdGlvbihzb3J0ZWRfc2l0ZXMsIHNhbXBsZV9zaXplLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkgew0KICANCiAgICAjIGNyZWF0ZSBlbXB0eSBsaXN0IHRvIHNhdmUgcHJvY2Vzc2VkIGRpYWdub3N0aWMgZGF0YQ0KICAgIGRpYWdfdGFibGVfbGlzdCA8LSBsaXN0KCkNCiAgDQogICAgaWYoaXNfcGVkaWF0cmljPT1GQUxTRSkgew0KICAgICAgcG9wdWxhdGlvbiA9ICJhZHVsdHMiDQogICAgICB9IGVsc2Ugew0KICAgICAgcG9wdWxhdGlvbiA9ICJwZWRpYXRyaWNzIg0KICAgICAgfQ0KICANCiAgIyBmb3IgZWFjaCBoZWFsdGhjYXJlIHN5c3RlbSwgd2Ugd2lsbCBjYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBwYXRpZW50cyB3aXRoIGVhY2ggSUNEIGNvZGUNCiAgZm9yIChpIGluIHNvcnRlZF9zaXRlcykgew0KICAgIHRtcCA8LSBnZXQoaSkNCiAgICB0bXBfZGlhZyA8LSB0bXBbW2MoDQogICAgICAiZmlyc3RfaG9zcF9yZXN1bHRzIiwNCiAgICAgICJpY2RfdGFibGVzIiwNCiAgICAgIHBhc3RlMCgiaWNkX3RhYmxlc18iLCBwb3B1bGF0aW9uKSwNCiAgICAgICJkZW1vX3RhYmxlIg0KICAgICldXQ0KICAgIHRtcF9kaWFnJHNpdGUgPC0gdG1wW1sic2l0ZSJdXQ0KICAgIGRpYWdfdGFibGVfbGlzdFtbaV1dIDwtIHRtcF9kaWFnICU+JQ0KICAgICAgZmlsdGVyKHZhcmlhYmxlID09ICJzZXguZmVtYWxlIiB8IHZhcmlhYmxlID09ICJzZXgubWFsZSIgfCB2YXJpYWJsZSA9PSAic2V4Lm90aGVyIikgJT4lDQogICAgICBzZWxlY3Qoc2l0ZSwgY29udGFpbnMoIm5fdmFyIikpDQogIH0NCiAgDQogIGRpYWdfdGFibGVfd2lkZSA8LSBiaW5kX3Jvd3MoZGlhZ190YWJsZV9saXN0KQ0KICANCiAgIyB3ZSB3aWxsIGNvdW50IHRoZSB0b3RhbCBudW1iZXIgYW5kIHBlcmNlbnQgYWNyb3NzIGhlYWx0aGNhcmUgc3lzdGVtcw0KICBkaWFnX2NvdW50cyA8LSBjb2xTdW1zKEZpbHRlcihpcy5udW1lcmljLCBkaWFnX3RhYmxlX3dpZGUpLCBuYS5ybSA9IFRSVUUpICU+JSANCiAgICBkYXRhLmZyYW1lKCkgJT4lIA0KICAgIG11dGF0ZShwZXJjID0gcm91bmQoLi9zYW1wbGVfc2l6ZSAqIDEwMCwgMSkpDQogIA0KICAjIGZvcm1hdCBkaWFnbm9zdGljIHRhYmxlDQogIGRpYWdfY291bnRzIDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKGRpYWdfY291bnRzLCAiaWNkIikgJT4lDQogICAgbXV0YXRlKGljZCA9IGdzdWIoIm5fdmFyXyIsICIiLCBpY2QpKSAlPiUNCiAgICByZW5hbWUoYE51bWJlciBvZiBQYXRpZW50c2AgPSAiLiIpICU+JQ0KICAgIGZ1bGxfam9pbiguLCBpY2RzLCBieSA9ICJpY2QiKSAlPiUNCiAgICBkcGx5cjo6YXJyYW5nZShkZXNjKGBOdW1iZXIgb2YgUGF0aWVudHNgKSkgJT4lIA0KICAgIG11dGF0ZShgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgID0gcGFzdGUoYE51bWJlciBvZiBQYXRpZW50c2AsICIoIiwgcGVyYywgIikiLCBzZXAgPSAiICIpKSAlPiUgDQogICAgc2VsZWN0KHBuc19jbnMsIGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWAsIGNvbmNlcHRfdHlwZSwgDQogICAgICAgICAgIGljZCwgZGVzY3JpcHRpb24sIGBOdW1iZXIgb2YgUGF0aWVudHMgKCUgb2YgQ29ob3J0KWApICU+JSANCiAgICAgIHJlbmFtZShDb2RlID0gImljZCIsDQogICAgICAgICAgIGBOZXJ2b3VzIFN5c3RlbWAgPSAicG5zX2NucyIsDQogICAgICAgICAgIERlc2NyaXB0aW9uID0gImRlc2NyaXB0aW9uIiwNCiAgICAgICAgICAgYElDRCBWZXJzaW9uYCA9ICJjb25jZXB0X3R5cGUiKSAlPiUgDQogICAgbXV0YXRlKGBOdW1iZXIgb2YgUGF0aWVudHMgKCUgb2YgQ29ob3J0KWAgPSBpZl9lbHNlKGBOdW1iZXIgb2YgUGF0aWVudHMgKCUgb2YgQ29ob3J0KWAgPT0gIk5BICggTkEgKSIsICIwICggMCApIiwgYE51bWJlciBvZiBQYXRpZW50cyAoJSBvZiBDb2hvcnQpYCksDQogICAgICAgICAgIENvZGUgPSBpZl9lbHNlKENvZGUgPT0gIk5OIiwgIk5vIE5ldXJvbG9naWNhbCBEaXNlYXNlIChOTkMpIiwgQ29kZSkpDQogIA0KICByZXR1cm4oZGlhZ19jb3VudHMpDQogIA0KICB9DQoNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBjcmVhdGUgdmVjdG9ycyBjb250YWluaW5nIHBhdGllbnQgc2FtcGxlIHNpemVzDQpuX2FkdWx0IDwtIGFzLm51bWVyaWModGFibGVvbmVfYWR1bHQkTilbMV0NCm5fcGVkaWF0cmljIDwtIGFzLm51bWVyaWModGFibGVvbmVfcGVkaWF0cmljJE4pWzFdDQoNCiMgY2FsY3VsYXRlIHRoZSB0b3RhbCBuZXVyb2xvZ2ljYWwgcGF0aWVudCBjb3VudHMgDQpuZXVyb19jb3VudHNfYWR1bHQgPC0gY29tcHV0ZV9uZXVyb19jb3VudHMoc29ydGVkX3NpdGVzID0gYWR1bHRfc2l0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX3NpemUgPSBuX2FkdWx0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KbmV1cm9fY291bnRzX3BlZGlhdHJpYyA8LSBjb21wdXRlX25ldXJvX2NvdW50cyhzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX3NpemUgPSBuX3BlZGlhdHJpYywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcGVkaWF0cmljID0gVFJVRSkNCg0KbmV1cm9fY291bnRzX2NvbWJpbmVkIDwtIG5ldXJvX2NvdW50c19hZHVsdCAlPiUgDQogIHJlbmFtZShgTnVtYmVyIG9mIEFkdWx0IFBhdGllbnRzICglIG9mIENvaG9ydClgID0gYE51bWJlciBvZiBQYXRpZW50cyAoJSBvZiBDb2hvcnQpYCkgJT4lIA0KICBsZWZ0X2pvaW4oLiwgbmV1cm9fY291bnRzX3BlZGlhdHJpYyAlPiUgDQogICAgICAgICAgICAgIHNlbGVjdChgSUNEIFZlcnNpb25gLCBDb2RlLCBgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgKSAlPiUgDQogICAgICAgICAgICAgIHJlbmFtZSgiTnVtYmVyIG9mIFBlZGlhdHJpYyBQYXRpZW50cyAoJSBvZiBDb2hvcnRgKSIgPSBgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgKSkNCg0Kd3JpdGUuY3N2KG5ldXJvX2NvdW50c19jb21iaW5lZCwgcGFzdGUwKCJ0YWJsZXMvU3VwcC5UYWJsZTJfZGlhZ25vc2VzIiwgIi5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpDQogIA0KYGBgDQoNCiMjIE51bWJlciAoJSkgb2YgQWR1bHQgUGF0aWVudHMgd2l0aCBlYWNoIE5ldXJvbG9naWNhbCBEaWFnbm9zaXMNCg0KYGBge3J9DQpkYXRhdGFibGUobmV1cm9fY291bnRzX2FkdWx0KSANCmBgYA0KDQojIyBOdW1iZXIgKCUpIG9mIFBlZGlhdHJpYyBQYXRpZW50cyB3aXRoIGVhY2ggTmV1cm9sb2dpY2FsIERpYWdub3Npcw0KDQpgYGB7cn0NCmRhdGF0YWJsZShuZXVyb19jb3VudHNfcGVkaWF0cmljKSANCmBgYA0KDQojIyBFdmFsdWF0ZSBEaWFnbm9zZXMgYnkgQWdlICYgT3V0Y29tZQ0KDQpgYGB7cn0NCmNvbXB1dGVfc3RyYXRpZmllZF9uZXVyb19jb3VudHMgPC0gZnVuY3Rpb24odGFibGVvbmUsIHNvcnRlZF9zaXRlcywgaXNfcGVkaWF0cmljID0gRkFMU0UpIHsNCiAgDQogICMgY3JlYXRlIGVtcHR5IGxpc3QgdG8gc2F2ZSBwcm9jZXNzZWQgZGlhZ25vc3RpYyBkYXRhDQogICAgZGlhZ190YWJsZV9saXN0IDwtIGxpc3QoKQ0KICANCiAgICBpZihpc19wZWRpYXRyaWM9PUZBTFNFKSB7DQogICAgICBwb3B1bGF0aW9uID0gImFkdWx0cyINCiAgICAgIH0gZWxzZSB7DQogICAgICBwb3B1bGF0aW9uID0gInBlZGlhdHJpY3MiDQogICAgICB9DQogIA0KICAjIGZvciBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtLCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHBhdGllbnRzIHdpdGggZWFjaCBJQ0QgY29kZQ0KICBmb3IgKGkgaW4gc29ydGVkX3NpdGVzKSB7DQogICAgI3ByaW50KGkpDQogICAgdG1wIDwtIGdldChpKQ0KICAgIHRtcF9kaWFnIDwtIHRtcFtbYygNCiAgICAgICJmaXJzdF9ob3NwX3Jlc3VsdHMiLA0KICAgICAgImljZF90YWJsZXMiLA0KICAgICAgcGFzdGUwKCJpY2RfdGFibGVzXyIsIHBvcHVsYXRpb24pLA0KICAgICAgImRlbW9fdGFibGUiDQogICAgKV1dDQogICAgdG1wX2RpYWckc2l0ZSA8LSB0bXBbWyJzaXRlIl1dDQogICAgdHJ5KA0KICAgIGRpYWdfdGFibGVfbGlzdFtbaV1dIDwtIHRtcF9kaWFnICU+JQ0KICAgICAgc2VsZWN0KHNpdGUsIHZhcmlhYmxlLCBzdGFydHNfd2l0aCgibl92YXIiKSkgJT4lIA0KICAgICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgibl92YXIiKSwgbmFtZXNfdG8gPSAiY29kZSIsIHZhbHVlc190byA9ICJmcmVxIikgJT4lIA0KICAgICAgdW5ncm91cCgpDQogICAgKQ0KICB9DQoNCiAgZGlhZ190YWJsZV93aWRlX2FsbCA8LSBiaW5kX3Jvd3MoZGlhZ190YWJsZV9saXN0KSAlPiUgDQogICAgICBncm91cF9ieSh2YXJpYWJsZSwgY29kZSkgJT4lIA0KICAgICMgdG90YWwgbnVtYmVyIG9mIHBhdGllbnRzIHdobyBoYWQgZWFjaCBjb2RlIGZvciBlYWNoIHZhcmlhYmxlIGFjcm9zcyBzaXRlcw0KICAgICAgbXV0YXRlKHRvdGFsX25fdmFyICA9IHN1bShmcmVxLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogICAgZGlzdGluY3QodmFyaWFibGUsIGNvZGUsIHRvdGFsX25fdmFyKSAlPiUgDQogICAgbXV0YXRlKGNvZGUgPSBnc3ViKCJuX3Zhcl8iLCAiIiwgY29kZSksDQogICAgICAgICAgIHZhcmlhYmxlID0gZ3N1YigiU2V2ZXJpdHkuIiwgIiIsIHZhcmlhYmxlKSwNCiAgICAgICAgICAgdmFyaWFibGUgPSBnc3ViKCJTdXJ2aXZhbC4iLCAiIiwgdmFyaWFibGUpLA0KICAgICAgICAgICB2YXJpYWJsZSA9IGdzdWIoInJlYWRtaXR0ZWQiLCAiIiwgdmFyaWFibGUpLA0KICAgICAgICAgICB2YXJpYWJsZSA9IGdzdWIoImFnZV9ncm91cCIsICIiLCB2YXJpYWJsZSksDQogICAgICAgICAgIHZhcmlhYmxlID0gZ3N1YigiY292aWRfZGlzY2hhcmdlZCIsICIiLCB2YXJpYWJsZSksDQogICAgICAgICAgIHZhcmlhYmxlID0gaWZfZWxzZSh2YXJpYWJsZSA9PSAiLlRSVUUiLCAiUmVhZG1pdHRlZCIsIHZhcmlhYmxlKSkgJT4lIA0KICAgIGxlZnRfam9pbiguLCBpY2RzICU+JSByZW5hbWUoY29kZSA9ICJpY2QiKSwgYnkgPSAiY29kZSIpICU+JSANCiAgICB1bmdyb3VwKCkgJT4lIA0KICAgIGZpbHRlcighY29uY2VwdF90eXBlID09ICJpY2QtOSIpDQoNCg0KICAjIHJlZmFjdG9yIG5ldXJvIHN0YXR1cw0KICBkaWFnX3RhYmxlX3dpZGVfYWxsJHBuc19jbnMgPC0gZmFjdG9yKGRpYWdfdGFibGVfd2lkZV9hbGwkcG5zX2NucywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiQ2VudHJhbCIsICJQZXJpcGhlcmFsIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkNOUyIsICJQTlMiKSkNCiAgDQogIA0KICByZXR1cm4oZGlhZ190YWJsZV93aWRlX2FsbCkNCiAgDQogIH0NCmBgYA0KDQpgYGB7cn0NCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FkdWx0IDwtIGNvbXB1dGVfc3RyYXRpZmllZF9uZXVyb19jb3VudHMoc29ydGVkX3NpdGVzID0gYWR1bHRfc2l0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzX3BlZGlhdHJpYyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWJsZW9uZSA9IHRhYmxlb25lX2FkdWx0KQ0KDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19wZWRpYXRyaWMgPC0gY29tcHV0ZV9zdHJhdGlmaWVkX25ldXJvX2NvdW50cyhzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzX3BlZGlhdHJpYyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlb25lID0gdGFibGVvbmVfcGVkaWF0cmljKQ0KYGBgDQoNCmBgYHtyfQ0KY29tcHV0ZV9zdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2UgPC0gZnVuY3Rpb24oc3RyYXRpZmllZF9uZXVyb19jb3VudHMsIHRhYmxlb25lKSB7DQogIA0KICBzdHJhdGlmaWVkX25ldXJvX2NvdW50cyR2YXJpYWJsZSA8LSBmYWN0b3Ioc3RyYXRpZmllZF9uZXVyb19jb3VudHMkdmFyaWFibGUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKCIuMDB0bzAyIiwgIi4wM3RvMDUiLCAiLjA2dG8xMSIsICIuMTJ0bzE3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiLjE4dG8yNSIsICIuMjZ0bzQ5IiwgIi41MHRvNjkiLCAiLjcwdG83OSIsICIuODBwbHVzIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCIwLTIgWWVhcnMiLCAiMy01IFllYXJzIiwgIjYtMTEgWWVhcnMiLCAiMTItMTcgWWVhcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxOC0yNSBZZWFycyIsICIyNi00OSBZZWFycyIsICI1MC02OSBZZWFycyIsICI3MC03OSBZZWFycyIsICI4MCsgWWVhcnMiKSkNCiAgDQogICMgdG90YWwgY291bnRzDQogIG5fMF8yIDwtIGdldF90YWJsZV9uKHRhYmxlb25lLCB2YXIgPSAiMC0yIikNCiAgbl8zXzUgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjMtNSIpDQogIG5fNl8xMSA9IGdldF90YWJsZV9uKHRhYmxlb25lLCB2YXIgPSAiNi0xMSIpDQogIG5fMTJfMTcgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjEyLTE3IikNCiAgbl8xOF8yNSA9IGdldF90YWJsZV9uKHRhYmxlb25lLCB2YXIgPSAiMTgtMjUiKQ0KICBuXzI2XzQ5ID0gZ2V0X3RhYmxlX24odGFibGVvbmUsIHZhciA9ICIyNi00OSIpDQogIG5fNTBfNjkgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjUwLTY5IikNCiAgbl83MF83OSA9IGdldF90YWJsZV9uKHRhYmxlb25lLCB2YXIgPSAiNzAtNzkiKQ0KICBuXzgwID0gZ2V0X3RhYmxlX24odGFibGVvbmUsIHZhciA9ICI4MCsiKQ0KICANCiAgYWdlX2RmIDwtIGRhdGEuZnJhbWUoInZhcmlhYmxlIiA9IGMoIjAtMiBZZWFycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMy01IFllYXJzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNi0xMSBZZWFycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEyLTE3IFllYXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxOC0yNSBZZWFycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjI2LTQ5IFllYXJzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNTAtNjkgWWVhcnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI3MC03OSBZZWFycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjgwKyBZZWFycyIpLA0KICAgICAgICAgICAgICAgICAgICAgIm5fdmFyIiA9IGMobl8wXzIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl8zXzUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl82XzExLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fMTJfMTcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl8xOF8yNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuXzI2XzQ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fNTBfNjksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl83MF83OSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuXzgwKSkNCg0KICANCiAgc3RyYXRpZmllZF9uZXVyb19jb3VudHMgPC0gc3RyYXRpZmllZF9uZXVyb19jb3VudHMgJT4lIA0KICAgIGZpbHRlcihncmVwbCgiWWVhcnMiLCB2YXJpYWJsZSksDQogICAgICAgICAgICAgICAgIWNvZGUgPT0gIk5OIiwgDQogICAgICAgICAgICAgICAgIXRvdGFsX25fdmFyID09IDApICU+JSANCiAgICAgICAgICMgdG90YWxfbiB3b3VsZCBiZSBjYWxjdWxhdGluZyB0b3RhbCBjb2RlcyBhY3Jvc3MgZWFjaCBhZ2UgZ3JvdXAgd2hpY2ggaXMgbm90IHRydWUgcmVmbGVjdGlvbiBvZiBwYXRpZW50IGNvdW50cyBiL2MgcGF0aWVudHMgY2FuIGhhdmUgbW9yZSB0aGFuIG9uZSBjb2RlDQogICAgICAgICAjbXV0YXRlKHRvdGFsX24gPSBzdW0odG90YWxfbl92YXIpKSAlPiUgDQogICAgICAgICB1bmdyb3VwKCkgJT4lIA0KICAgICAgICAgbGVmdF9qb2luKC4sIGFnZV9kZiwgYnkgPSAidmFyaWFibGUiKSAlPiUgDQogICAgICAgICBtdXRhdGUobl92YXIgPSBhcy5udW1lcmljKG5fdmFyKSwNCiAgICAgICAgICAgICAgICBwZXJjID0gYXMuY2hhcmFjdGVyKHJvdW5kKHRvdGFsX25fdmFyL25fdmFyKjEwMCwxKSksDQogICAgICAgICAgICAgICAgcGVyYyA9IGlmX2Vsc2UocGVyYyA8IDAuMSwgJzwwLjEnLCBwZXJjKSwNCiAgICAgICAgICAgICAgICBwZXJjID0gcGFzdGUwKHBlcmMsICIlIikpIA0KICANCiAgcmV0dXJuKHN0cmF0aWZpZWRfbmV1cm9fY291bnRzKQ0KICANCiAgDQp9DQoNCg0KYGBgDQoNCmBgYHtyfQ0Kc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX2FkdWx0IDwtIGNvbXB1dGVfc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlKHN0cmF0aWZpZWRfbmV1cm9fY291bnRzID0gc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWR1bHQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFibGVvbmUgPSB0YWJsZW9uZV9hZHVsdCkNCg0Kc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX3BlZGlhdHJpYyA8LSBjb21wdXRlX3N0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZShzdHJhdGlmaWVkX25ldXJvX2NvdW50cyA9IHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX3BlZGlhdHJpYywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFibGVvbmUgPSB0YWJsZW9uZV9wZWRpYXRyaWMpDQpgYGANCg0KKipEaWFnbm9zZXMgYnkgQWR1bHQgYW5kIFBlZGlhdHJpYyBQb3B1bGF0aW9ucyoqDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0xNX0NCm5fMF8yIDwtIGdldF90YWJsZV9uKHRhYmxlb25lX2NvbWJpbmUsIHZhciA9ICIwLTIiKQ0KI25fM181IDwtIGdldF90YWJsZV9uKHRhYmxlb25lX2NvbWJpbmUsIHZhciA9ICIzLTUiKQ0Kbl82XzExIDwtIGdldF90YWJsZV9uKHRhYmxlb25lX2NvbWJpbmUsIHZhciA9ICI2LTExIikNCm5fMTJfMTcgPC0gZ2V0X3RhYmxlX24odGFibGVvbmVfY29tYmluZSwgdmFyID0gIjEyLTE3IikNCg0KdG90YWxfcGVkX2FnZV9jb3VudCA9IGFzLm51bWVyaWMobl8wXzIpICsgIGFzLm51bWVyaWMobl82XzExKSArIGFzLm51bWVyaWMobl8xMl8xNykNCg0Kc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX3BlZGlhdHJpY19yZWZvcm1hdCA9IHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9wZWRpYXRyaWMgJT4lDQogIHNlbGVjdCh2YXJpYWJsZSwgcG5zX2NucywgZGVzY3JpcHRpb24sIG5fdmFyLCB0b3RhbF9uX3ZhcikgJT4lDQogIG11dGF0ZSh2YXJpYWJsZSA9ICIwLTE3IFllYXJzIikgJT4lDQogIGdyb3VwX2J5KHBuc19jbnMsIGRlc2NyaXB0aW9uKSAlPiUNCiAgbXV0YXRlKHRvdGFsX25fdmFyID0gc3VtKHRvdGFsX25fdmFyLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICAjIG5fdmFyIGlzIHRoZSB0b3RhbCBwZXIgYGFnZV9ncm91cGAgc3RyYXRpZmljYXRpb24NCiAgc2VsZWN0KC1uX3ZhcikgJT4lDQogIGRpc3RpbmN0KCkgJT4lDQogIG11dGF0ZShwZXJjID0gcm91bmQodG90YWxfbl92YXIvdG90YWxfcGVkX2FnZV9jb3VudCoxMDAsMSksDQogICAgICAgICBwZXJjID0gaWZfZWxzZShwZXJjIDwgMC4xLCAnPDAuMScsIGFzLmNoYXJhY3RlcihwZXJjKSksDQogICAgICAgICBwZXJjID0gcGFzdGUwKHBlcmMsICIlIikpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9jb21iaW5lZCA8LSBzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2VfYWR1bHQgJT4lDQogIHNlbGVjdCh2YXJpYWJsZSwgcG5zX2NucywgZGVzY3JpcHRpb24sIHRvdGFsX25fdmFyLCBwZXJjKSAlPiUNCiAgcmJpbmQoLiwgc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX3BlZGlhdHJpY19yZWZvcm1hdCAlPiUNCiAgICAgICAgICBzZWxlY3QodmFyaWFibGUsIHBuc19jbnMsIGRlc2NyaXB0aW9uLCB0b3RhbF9uX3ZhciwgcGVyYykpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9jb21iaW5lZCR2YXJpYWJsZSA8LSBmYWN0b3Ioc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX2NvbWJpbmVkJHZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKCIwLTE3IFllYXJzIiwgIjE4LTI1IFllYXJzIiwgIjI2LTQ5IFllYXJzIiwgIjUwLTY5IFllYXJzIiwgIjcwLTc5IFllYXJzIiwgIjgwKyBZZWFycyIpKQ0KYGBgDQoNCmBgYHtyIGZpZy5oZWlnaHQ9MjAsIGZpZy53aWR0aD0xMn0NCmdyb3VwLmNvbG9ycyA8LSBjKE5OQyA9ICJncmF5IiwgUE5TID0gInNsYXRlYmx1ZSIsIENOUyA9InRvbWF0byIpDQoNCmFkdWx0X3BlZF9jb21iaW5lZF9wbG90IDwtIGdncGxvdChzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2VfY29tYmluZWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IHRvdGFsX25fdmFyLCB5ID0gcmVvcmRlcihkZXNjcmlwdGlvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsX25fdmFyKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHBuc19jbnMpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGdlb21fdGV4dCgNCiAgICBhZXMoeCA9IHRvdGFsX25fdmFyLCB5ID0gZGVzY3JpcHRpb24sIGxhYmVsID0gcGVyYyksDQogICAgaGp1c3QgPSAtMC4xLA0KICAgICNoanVzdD0iaW53YXJkIiwNCiAgICBzaXplID0gNiwNCiAgICBpbmhlcml0LmFlcyA9IFRSVUUpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMikpKSArICMgdG8gZW5zdXJlIHBlcmNlbnQgbGFiZWxzIGFyZSB3aXRoaW4gdGhlIGZpZ3VyZQ0KICBmYWNldF9ncmlkKHBuc19jbnMgfiB2YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlX3kiKSArIA0KICBzY2FsZV95X2Rpc2NyZXRlKCIiLCBwb3NpdGlvbj0ibGVmdCIsIGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTAwKSkgKyAgIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBncm91cC5jb2xvcnMpICsNCiAgeGxhYigiTnVtYmVyIG9mIFBhdGllbnRzIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNDApLA0KICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDM1KSwNCiAgICAgICAgYXhpcy50ZXh0LnkubGVmdCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjUsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjUpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDM1KSkNCg0KZ2dzYXZlKCJmaWd1cmVzL0ZpZzNfZGlhZ25vc2VzX2J5X2FnZV9hZHVsdF9wZWRfMThncm91cC5wbmciLCBhZHVsdF9wZWRfY29tYmluZWRfcGxvdCwgd2lkdGggPSA0MCwgaGVpZ2h0ID0gMTksIGRwaSA9IDMwMCkNCmBgYA0KDQohW10oZmlndXJlcy9GaWczX2RpYWdub3Nlc19ieV9hZ2VfYWR1bHRfcGVkXzE4Z3JvdXAucG5nKXt3aWR0aD0iMTAzMSIgaGVpZ2h0PSIzMzcifQ0KDQojIyBFdmFsdWF0ZSBEaWFnbm9zZXMgYnkgT3V0Y29tZQ0KDQpgYGB7cn0NCg0Kc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWR1bHQkcG9wdWxhdGlvbiA9ICJhZHVsdCINCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX3BlZGlhdHJpYyRwb3B1bGF0aW9uID0gInBlZGlhdHJpYyINCg0Kc3RyYXRpZmllZF9uZXVyb19jb3VudHNfY29tYmluZSA8LSByYmluZChzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZHVsdCwgc3RyYXRpZmllZF9uZXVyb19jb3VudHNfcGVkaWF0cmljKQ0KDQojIyBkZXRlcm1pbmUgdG90YWwgbnVtYmVyIG9mIHBhdGllbnRzIHdobyBkaWVkIG9yIHNldmVyZQ0Kbl9kZWF0aF9hZHVsdCA9IGdldF90YWJsZV9uKGRmID0gdGFibGVvbmVfYWR1bHQsIHZhciA9ICJEZWNlYXNlZCIpICU+JSBhcy5udW1lcmljKCkNCm5fc2V2ZXJlX2FkdWx0ID0gZ2V0X3RhYmxlX24oZGYgPSB0YWJsZW9uZV9hZHVsdCwgdmFyID0gIlNldmVyZSIpICU+JSBhcy5udW1lcmljKCkNCg0Kbl9kZWF0aF9wZWRpYXRyaWMgPSBnZXRfdGFibGVfbihkZiA9IHRhYmxlb25lX3BlZGlhdHJpYywgdmFyID0gIkRlY2Vhc2VkIikgJT4lIGFzLm51bWVyaWMoKQ0Kbl9zZXZlcmVfcGVkaWF0cmljID0gZ2V0X3RhYmxlX24oZGYgPSB0YWJsZW9uZV9wZWRpYXRyaWMsIHZhciA9ICJTZXZlcmUiKSAlPiUgYXMubnVtZXJpYygpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX291dGNvbWVzIDwtIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2NvbWJpbmUgJT4lIA0KICBmaWx0ZXIodmFyaWFibGUgJWluJSBjKCJTZXZlcmUiLCAiRGVjZWFzZWQiKSwNCiAgICAgICAgICFjb2RlID09ICJOTiIsIA0KICAgICAgICAgIXRvdGFsX25fdmFyID09IDApICU+JSAgICAgICAgICANCiAgICAgICAgICMgdG90YWxfbiB3b3VsZCBiZSBjYWxjdWxhdGluZyB0b3RhbCBjb2RlcyBhY3Jvc3MgZWFjaCBvdXRjb21lIGdyb3VwIHdoaWNoIGlzIG5vdCB0cnVlIHJlZmxlY3Rpb24gb2YgcGF0aWVudCBjb3VudHMgYi9jIHBhdGllbnRzIGNhbiBoYXZlIG1vcmUgdGhhbiBvbmUgY29kZQ0KICAgICAgICAgI211dGF0ZSh0b3RhbF9uID0gc3VtKHRvdGFsX25fdmFyKSkgJT4lIA0KICAgICAgICAgZ3JvdXBfYnkodmFyaWFibGUsIGRlc2NyaXB0aW9uLCBwb3B1bGF0aW9uKSAlPiUgDQogIG11dGF0ZSh0b3RhbF9uX3ZhciA9IHN1bSh0b3RhbF9uX3ZhcikpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKHBlcmMgPSBjYXNlX3doZW4odmFyaWFibGUgPT0gIlNldmVyZSIgJiBwb3B1bGF0aW9uID09ICJhZHVsdCIgfiByb3VuZCh0b3RhbF9uX3Zhci9uX3NldmVyZV9hZHVsdCoxMDAsMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9PSAiRGVjZWFzZWQiICYgcG9wdWxhdGlvbiA9PSAiYWR1bHQiIH4gcm91bmQodG90YWxfbl92YXIvbl9kZWF0aF9hZHVsdCoxMDAsMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9PSAiU2V2ZXJlIiAmIHBvcHVsYXRpb24gPT0gInBlZGlhdHJpYyIgfiByb3VuZCh0b3RhbF9uX3Zhci9uX3NldmVyZV9wZWRpYXRyaWMqMTAwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPT0gIkRlY2Vhc2VkIiAmIHBvcHVsYXRpb24gPT0gInBlZGlhdHJpYyIgfiByb3VuZCh0b3RhbF9uX3Zhci9uX2RlYXRoX3BlZGlhdHJpYyoxMDAsMSkpLA0KICAgICAgICAgcGVyYyA9IGlmX2Vsc2UocGVyYyA8IDAuMSwgJzwwLjEnLCBhcy5jaGFyYWN0ZXIocGVyYykpLA0KICAgICAgIHBlcmMgPSBwYXN0ZTAocGVyYywgIiUiKSkgJT4lIA0KICBzZWxlY3QoLWNvZGUsIC1jb25jZXB0X3R5cGUpICU+JSANCiAgZGlzdGluY3QoKQ0KYGBgDQoNCmBgYHtyfQ0KYWR1bHRfb3V0Y29tZXMgPSBnZ3Bsb3Qoc3RyYXRpZmllZF9uZXVyb19jb3VudHNfb3V0Y29tZXMgJT4lIA0KICAgICAgICAgZmlsdGVyKHBvcHVsYXRpb24gPT0gImFkdWx0IikgJT4lIA0KICAgICAgICAgICBtdXRhdGUodmFyaWFibGUgPSBhcy5mYWN0b3IodmFyaWFibGUpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZjdF9yZWNvZGUoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1vcnRhbGl0eSA9ICJEZWNlYXNlZCIpKSwNCiAgICAgICBhZXMoeCA9IHRvdGFsX25fdmFyLCB5ID0gcmVvcmRlcihkZXNjcmlwdGlvbiwgdG90YWxfbl92YXIpLCBmaWxsID0gcG5zX2NucykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiBnZW9tX3RleHQoDQogICAgYWVzKHggPSB0b3RhbF9uX3ZhciwgeSA9IGRlc2NyaXB0aW9uLCBsYWJlbCA9IHBlcmMpLA0KICAgIGhqdXN0ID0gLTAuMSwNCiAgICAjaGp1c3Q9Imlud2FyZCIsDQogICAgc2l6ZSA9IDUsDQogICAgaW5oZXJpdC5hZXMgPSBUUlVFKSArDQogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwwLjIpKSkgKyANCiAgZmFjZXRfZ3JpZChwbnNfY25zIH4gdmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZV95IikgKyANCiAgc2NhbGVfeV9kaXNjcmV0ZSgiIiwgcG9zaXRpb249ImxlZnQiLCBsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDEwMCkpICsgICANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZ3JvdXAuY29sb3JzKSArDQogIHhsYWIoIk51bWJlciBvZiBQYXRpZW50cyIpICsgDQogIGdndGl0bGUoIkIuIEFkdWx0IikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA0NCwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAtMC41KSwgDQogICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzUpLA0KICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMwKSwNCiAgICAgICAgYXhpcy50ZXh0LnkubGVmdCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDM1KSkNCg0KZ2dzYXZlKCJmaWd1cmVzL1N1cHBGaWcxX2RpYWdub3Nlc19ieV9vdXRjb21lc19hZHVsdC5wbmciLCBhZHVsdF9vdXRjb21lcywgd2lkdGggPSAyNywgaGVpZ2h0ID0gMTUsIGRwaSA9IDMwMCkNCmBgYA0KDQpgYGB7cn0NCnBlZGlhdHJpY19vdXRjb21lcyA9IGdncGxvdChzdHJhdGlmaWVkX25ldXJvX2NvdW50c19vdXRjb21lcyAlPiUgDQogICAgICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAicGVkaWF0cmljIikgJT4lIA0KICAgICAgICAgICBtdXRhdGUodmFyaWFibGUgPSBhcy5mYWN0b3IodmFyaWFibGUpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZjdF9yZWNvZGUoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1vcnRhbGl0eSA9ICJEZWNlYXNlZCIpKSwNCiAgICAgICBhZXMoeCA9IHRvdGFsX25fdmFyLCB5ID0gcmVvcmRlcihkZXNjcmlwdGlvbiwgdG90YWxfbl92YXIpLCBmaWxsID0gcG5zX2NucykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiBnZW9tX3RleHQoDQogICAgYWVzKHggPSB0b3RhbF9uX3ZhciwgeSA9IGRlc2NyaXB0aW9uLCBsYWJlbCA9IHBlcmMpLA0KICAgIGhqdXN0ID0gLTAuMSwNCiAgICAjaGp1c3Q9Imlud2FyZCIsDQogICAgc2l6ZSA9IDUsDQogICAgaW5oZXJpdC5hZXMgPSBUUlVFKSArDQogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwwLjIpKSkgKyANCiAgZmFjZXRfZ3JpZChwbnNfY25zIH4gdmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZV95IikgKyANCiAgc2NhbGVfeV9kaXNjcmV0ZSgiIiwgcG9zaXRpb249ImxlZnQiLCBsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDEwMCkpICsgICANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZ3JvdXAuY29sb3JzKSArDQogIHhsYWIoIiIpICsgDQogIGdndGl0bGUoIkEuIFBlZGlhdHJpYyIpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNDQsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gLTAuNSksIA0KICAgICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDM1KSwNCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCksDQogICAgICAgIGF4aXMudGV4dC55LmxlZnQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSkpDQoNCmdnc2F2ZSgiZmlndXJlcy9TdXBwRmlnMV9kaWFnbm9zZXNfYnlfb3V0Y29tZXNfcGVkaWF0cmljLnBuZyIsIHBlZGlhdHJpY19vdXRjb21lcywgd2lkdGggPSAyNywgaGVpZ2h0ID0gOSwgZHBpID0gMzAwKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQojZ3JpZC5hcnJhbmdlKHBlZGlhdHJpY19vdXRjb21lcywgYWR1bHRfb3V0Y29tZXMsIG5jb2w9MSkNCg0KZ2dzYXZlKCJmaWd1cmVzL1N1cHBGaWcxX2RpYWdub3Nlc19ieV9vdXRjb21lX2NvbWJpbmVkLnBuZyIsIGdyaWQuYXJyYW5nZShwZWRpYXRyaWNfb3V0Y29tZXMsIGFkdWx0X291dGNvbWVzLCBuY29sPTEpLCB3aWR0aCA9IDI3LCBoZWlnaHQgPSAyNSwgZHBpID0gMzAwKQ0KYGBgDQoNCiFbXShmaWd1cmVzL1N1cHBGaWcxX2RpYWdub3Nlc19ieV9vdXRjb21lX2NvbWJpbmVkLnBuZyl7LmNsYXNzIHdpZHRoPSI4MDAiIGhlaWdodD0iODAwcHgifQ0KDQojIyBFdmFsdWF0ZSBwYXRpZW50cyB3aXRoICJCb3RoIiBDTlMgYW5kIFBOUyBkaXNlYXNlcw0KDQpUaGVzZSBwYXRpZW50cyB3ZXJlIGV4Y2x1ZGVkIGZyb20gdGhlIHByaW1hcnkgYW5hbHlzaXMgZHVlIHRvIHBvdGVudGlhbCBjb25mb3VuZGluZy4NCg0KYGBge3J9DQojIGNyZWF0ZSBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBjb3VudHMgb2YgImJvdGgiIHBhdGllbnRzDQpib3RoX2NvdW50c19saXN0IDwtIGxpc3QoKQ0KDQojIGZvciBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtLCByZXRyaWV2ZSB0aGUgbnVtYmVyIG9mIGV4Y2x1ZGVkICJib3RoIiBwYXRpZW50cyByZWNvcmRlZA0KZm9yIChpIGluIHNvcnRlZF9zaXRlcykgew0KICBuX2JvdGggPC0gcmVzdWx0c1tbcGFzdGUoaSldXVtbImZpcnN0X2hvc3BfcmVzdWx0cyJdXVtbImJvdGhfY291bnRzIl1dDQogIA0KICBib3RoX2NvdW50c19saXN0W1tpXV0gPC0gbl9ib3RoDQp9DQoNCmJvdGhfY291bnRzIDwtIGJpbmRfcm93cyhib3RoX2NvdW50c19saXN0KSAlPiUgdCgpICU+JSBkYXRhLmZyYW1lKCkgJT4lIHN1bSgpDQoNCnByaW50KHBhc3RlKGJvdGhfY291bnRzLCAiKCIsIHJvdW5kKGJvdGhfY291bnRzLyhhcy5udW1lcmljKHRhYmxlb25lX2NvbWJpbmUkTlsxXSkrYm90aF9jb3VudHMpKjEwMCwyKSwgIiUpIikpIA0KYGBgDQoNCiMgKipDb21vcmJpZGl0eSBBbmFseXNpcyoqDQoNCk5leHQsIHdlIHdpbGwgZXZhbHVhdGUgdGhlIGZyZXF1ZW5jeSBvZiBjb21vcmJpZGl0aWVzIGluIG91ciBwb3B1bGF0aW9uIGFuZCB0aGUgYXNzb2NpYXRlZCByaXNrIHRoYXQgY29tb3JiaWRpdGllcyBtYXkgaW5jdXIgZm9yIGFjcXVpcmluZyBhIENOUyBvciBQTlMgZGlhZ25vc2lzIGR1cmluZyBDT1ZJRC0xOSBob3NwaXRhbGl6YXRpb24uDQoNCioqMS4gQ3JlYXRlIGEgdGFibGUgb2YgY291bnRzIGZvciBlYWNoIGNvbW9yYmlkaXR5IGFuZCBzdHJhdGlmaWVkIGJ5IG5ldXJvbG9naWNhbCBzdGF0dXMuKioNCg0KYGBge3J9DQpjb21vcmJpZGl0eV90YWJsZV9hZHVsdCA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgc2VsZWN0KENvbW9yYmlkaXR5LCBwb3B1bGF0aW9uLCBDb21vcmJfVG90YWwsIGBOTkNfTl8lYCwgYENOU19OXyVgLCBgUE5TX05fJWApICU+JQ0KICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiQWR1bHQiKSAlPiUgDQogIHJlbmFtZShOID0gIkNvbW9yYl9Ub3RhbCIsDQogICAgICAgICBOTkMgPSAiTk5DX05fJSIsDQogICAgICAgICBDTlMgPSAiQ05TX05fJSIsDQogICAgICAgICBQTlMgPSAiUE5TX05fJSIpICU+JSANCiAgbXV0YXRlKE4gPSBwYXN0ZTAoTiwgIiAoIiwgcm91bmQoTi9uX2FkdWx0LCAyKSoxMDAsICIlKSIpKQ0KDQpjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogIHNlbGVjdChDb21vcmJpZGl0eSwgcG9wdWxhdGlvbiwgQ29tb3JiX1RvdGFsLCBgTk5DX05fJWAsIGBDTlNfTl8lYCwgYFBOU19OXyVgKSAlPiUNCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIlBlZGlhdHJpYyIpICU+JSANCiAgcmVuYW1lKE4gPSAiQ29tb3JiX1RvdGFsIiwNCiAgICAgICAgIE5OQyA9ICJOTkNfTl8lIiwNCiAgICAgICAgIENOUyA9ICJDTlNfTl8lIiwNCiAgICAgICAgIFBOUyA9ICJQTlNfTl8lIikgJT4lIA0KICBtdXRhdGUoTiA9IHBhc3RlMChOLCAiICgiLCByb3VuZChOL25fcGVkaWF0cmljLCAyKSoxMDAsICIlKSIpKQ0KDQpzdXBwX2NvbW9yYmlkaXR5X3RhYmxlIDwtIHJiaW5kKGNvbW9yYmlkaXR5X3RhYmxlX2FkdWx0LCBjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMpICU+JSANCiAgYXJyYW5nZShDb21vcmJpZGl0eSkNCg0Kd3JpdGUuY3N2KHN1cHBfY29tb3JiaWRpdHlfdGFibGUsICJ0YWJsZXMvU3VwcFRhYmxlX0NvbW9yYmlkaXR5X0NvdW50cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQoqKjIuIENyZWF0ZSB2ZWN0b3IgY291bnRzIG9mIHBhdGllbnRzIHdpdGggY29tb3JiaWRpdHkgZGF0YSoqDQoNCmBgYHtyfQ0KbmV1cm9fcHRfY291bnRzX3N1bV9hZHVsdCA8LSBuZXVyb19wdF9jb3VudHMgJT4lIA0KICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiQWR1bHQiKSAlPiUgDQogIHNlbGVjdCgtcG9wdWxhdGlvbikNCg0KbmV1cm9fcHRfY291bnRzX3N1bV9wZWRpYXRyaWMgPC0gbmV1cm9fcHRfY291bnRzICU+JSANCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIlBlZGlhdHJpYyIpICU+JSANCiAgc2VsZWN0KC1wb3B1bGF0aW9uKQ0KDQpub25lX25fYWR1bHQgPSBuZXVyb19wdF9jb3VudHNfc3VtX2FkdWx0JE5vbmVfbg0KY25zX25fYWR1bHQgPSBuZXVyb19wdF9jb3VudHNfc3VtX2FkdWx0JENOU19uDQpwbnNfbl9hZHVsdCA9IG5ldXJvX3B0X2NvdW50c19zdW1fYWR1bHQkUE5TX24NCg0Kbm9uZV9uX3BlZGlhdHJpYyA9IG5ldXJvX3B0X2NvdW50c19zdW1fcGVkaWF0cmljJE5vbmVfbg0KY25zX25fcGVkaWF0cmljID0gbmV1cm9fcHRfY291bnRzX3N1bV9wZWRpYXRyaWMkQ05TX24NCnBuc19uX3BlZGlhdHJpYyA9IG5ldXJvX3B0X2NvdW50c19zdW1fcGVkaWF0cmljJFBOU19uDQpgYGANCg0KKiozLiBDYWxjdWxhdGUgdGhlIHJlbGF0aXZlIHJpc2sgKFJSKSBvZiBkZXZlbG9waW5nIGEgQ05TIG9yIFBOUyBkaWFnbm9zaXMgZm9yIHBhdGllbnRzIHdpdGggZWFjaCBjb21vcmJpZGl0eSoqDQoNCmBgYHtyfQ0KY2FsY3VsYXRlX3JyIDwtIGZ1bmN0aW9uKGlzX3BlZGlhdHJpYyA9IEZBTFNFKSB7DQogIA0KICBpZihpc19wZWRpYXRyaWMgPT0gRkFMU0UpIHsNCiAgICBjbnNfbiA9IGNuc19uX2FkdWx0DQogICAgcG5zX24gPSBwbnNfbl9hZHVsdA0KICAgIG5ldXJvX3B0X2NvdW50c19zdW0gPSBuZXVyb19wdF9jb3VudHNfc3VtX2FkdWx0DQogICAgcG9wID0gIkFkdWx0Ig0KICAgICMgY3JlYXRlIGEgbGlzdCBvZiBjb21vcmJpZGl0aWVzDQogICAgbGlzdF9vZl9jb21vcmJzIDwtIGNvbW9yYl90YWJsZV93aWRlICU+JSANCiAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09IHBhc3RlKHBvcCkpICU+JSANCiAgICAgIGRpc3RpbmN0KENvbW9yYmlkaXR5KSANCiAgICBsaXN0X29mX2NvbW9yYnMgPC0gbGlzdF9vZl9jb21vcmJzJENvbW9yYmlkaXR5DQogICAgfSBlbHNlICB7DQogICAgY25zX24gPSBjbnNfbl9wZWRpYXRyaWMNCiAgICBwbnNfbiA9IHBuc19uX3BlZGlhdHJpYw0KICAgIG5ldXJvX3B0X2NvdW50c19zdW0gPSBuZXVyb19wdF9jb3VudHNfc3VtX3BlZGlhdHJpYw0KICAgIHBvcCA9ICJQZWRpYXRyaWMiDQogICAgIyBjcmVhdGUgYSBsaXN0IG9mIGNvbW9yYmlkaXRpZXMNCiAgICBsaXN0X29mX2NvbW9yYnMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lIA0KICAgICAgZmlsdGVyKHBvcHVsYXRpb24gPT0gcGFzdGUocG9wKSkgJT4lIA0KICAgICAgZGlzdGluY3QoQ29tb3JiaWRpdHkpICU+JSANCiAgICAgIGFzLnZlY3RvcigpDQogICAgbGlzdF9vZl9jb21vcmJzIDwtIGxpc3Rfb2ZfY29tb3JicyRDb21vcmJpZGl0eQ0KICB9DQogIA0KICANCiAgIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzYXZlIHJlbGF0aXZlIHJpc2sgY2FsY3VsYXRpb25zDQogIHJyX2NhbGNzIDwtIGxpc3QoKQ0KICANCiAgIyBmb3IgZWFjaCBjb21vcmJpZGl0eSwgd2Ugd2lsbCBjYWxjdWxhdGUgdGhlIHJlbGF0aXZlIHJpc2sgb2YgaGF2aW5nIGEgQ05TIGFuZCBQTlMgZGlhZ25vc2lzDQogIGZvciAoaSBpbiBsaXN0X29mX2NvbW9yYnMpIHsNCiAgDQogICAgIyBodHRwczovL3d3dy5jZGMuZ292L2NzZWxzL2RzZXBkL3NzMTk3OC9sZXNzb24zL3NlY3Rpb241Lmh0bWwNCiAgICAjIGNyZWF0ZSBtYXRyaXggYXM6DQogICAgIyBleHBvc2VkIGdyb3VwIC0gd2l0aCBvdXRjb21lLCB3aXRob3V0IG91dGNvbWUNCiAgICAjIG5vbiBleHBvc2VkIGdyb3VwIC0gd2l0aCBvdXRjb21lLCB3aXRob3V0IG91dGNvbWUNCiAgDQogICAgIyB3aGVyZSB2YXJpYWJsZXMgZW5kaW5nIHdpdGggYF9Ub3RhbGAgYXJlIHRoZSB0b3RhbCBudW1iZXIgb2YgQ05TLCBQTlMsIG9yIE5OQyBwYXRpZW50cyB3aXRoIHRoZSByZXNwZWN0aXZlIGNvbW9yYmlkaXR5Lg0KICAgICMgdmFyaWFibGVzIGVuZGluZyB3aXRoIGBfbmAgYXJlIHRoZSB0b3RhbCBudW1iZXJzIGluIHRoZSBlbnRpcmUgcG9wdWxhdGlvbg0KICANCiAgICAjIHRvIGNhbGN1bGF0ZSBjb25maWRlbmNlIGludGVydmFscw0KICAjaHR0cHM6Ly9zcGh3ZWIuYnVtYy5idS5lZHUvb3RsdC9tcGgtbW9kdWxlcy9icy9iczcwNF9jb25maWRlbmNlX2ludGVydmFscy9iczcwNF9jb25maWRlbmNlX2ludGVydmFsczguaHRtbA0KICANCiAgICBycl9jbnMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSBwYXN0ZShwb3ApLA0KICAgICAgICAgICAgIENvbW9yYmlkaXR5ID09IHBhc3RlKGkpKSAlPiUNCiAgICAgIG11dGF0ZShhID0gQ05TX1RvdGFsLCAjIG51bWJlciBDTlMgdy9jb21vcmINCiAgICAgICAgICAgICBiID0gQ29tb3JiX1RvdGFsIC0gQ05TX1RvdGFsLCAjIG51bWJlciBvZiBjb21vcmIgcHRzIHcvbyBDTlMNCiAgICAgICAgICAgICBjID0gY25zX24gLSBDTlNfVG90YWwsICMgbnVtYmVyIG9mIENOUyBwdHMgdy9vIGNvbW9yYg0KICAgICAgICAgICAgIGQgPSBzdW0obmV1cm9fcHRfY291bnRzX3N1bSkgLSBDb21vcmJfVG90YWwgLSBjLCAjIG51bWJlciBvZiBwdHMgdy9vIGNvbW9yYiBvciBDTlMNCiAgICAgICAgICAgICByaXNrX2NucyA9IGEvKGErYiksDQogICAgICAgICAgICAgcmlza19ub25fY25zID0gYy8oYytkKSwNCiAgICAgICAgICAgICBycl9DTlMgPSByaXNrX2Nucy9yaXNrX25vbl9jbnMsDQogICAgICAgICAgICAgIyBmb3IgY29uZmlkZW5jZSBpbnRlcnZhbHMNCiAgICAgICAgICAgICBjaV8xID0gKGIvYSkvKGErYiksICMgKG4xLXgxKS9uMSANCiAgICAgICAgICAgICBjaV8yID0gKGQvYykvKGMrZCksICMgKG4yLXgyKS9uMg0KICAgICAgICAgICAgIGNpX3Jvb3QgPSBzcXJ0KGNpXzEgKyBjaV8yKSwNCiAgICAgICAgICAgICBjaV9sb3dlciA9IGxvZyhycl9DTlMpIC0gKDEuOTYqY2lfcm9vdCksDQogICAgICAgICAgICAgY2lfdXBwZXIgPSBsb2cocnJfQ05TKSArICgxLjk2KmNpX3Jvb3QpLA0KICAgICAgICAgICAgIGxfY2lfY25zID0gZXhwKGNpX2xvd2VyKSwNCiAgICAgICAgICAgICB1X2NpX2NucyA9IGV4cChjaV91cHBlcikpICU+JQ0KICAgICAgc2VsZWN0KENvbW9yYmlkaXR5LCBDb21vcmJfVG90YWwsIE5vbmVfVG90YWwsIENOU19Ub3RhbCwgUE5TX1RvdGFsLCBycl9DTlMsIGxfY2lfY25zLCB1X2NpX2NucykNCiAgDQogICAgICBycl9wbnMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSBwYXN0ZShwb3ApLA0KICAgICAgICAgICAgIENvbW9yYmlkaXR5ID09IHBhc3RlKGkpKSAlPiUNCiAgICAgIG11dGF0ZShhID0gUE5TX1RvdGFsLCAjIG51bWJlciBQTlMgdy9jb21vcmINCiAgICAgICAgICAgICBiID0gQ29tb3JiX1RvdGFsIC0gUE5TX1RvdGFsLCAjIG51bWJlciBvZiBjb21vcmIgcHRzIHcvbyBQTlMNCiAgICAgICAgICAgICBjID0gcG5zX24gLSBQTlNfVG90YWwsICMgbnVtYmVyIG9mIFBOUyBwdHMgdy9vIGNvbW9yYg0KICAgICAgICAgICAgIGQgPSBzdW0obmV1cm9fcHRfY291bnRzX3N1bSkgLSBDb21vcmJfVG90YWwgLSBjLCAjIG51bWJlciBvZiBwdHMgdy9vIGNvbW9yYiBvciBQTlMNCiAgICAgICAgICAgICByaXNrX3BucyA9IGEvKGErYiksDQogICAgICAgICAgICAgcmlza19ub25fcG5zID0gYy8oYytkKSwNCiAgICAgICAgICAgICBycl9QTlMgPSByaXNrX3Bucy9yaXNrX25vbl9wbnMsDQogICAgICAgICAgICAgIyBmb3IgY29uZmlkZW5jZSBpbnRlcnZhbHMNCiAgICAgICAgICAgICBjaV8xID0gKGIvYSkvKGErYiksDQogICAgICAgICAgICAgY2lfMiA9IChkL2MpLyhjK2QpLA0KICAgICAgICAgICAgIGNpX3Jvb3QgPSBzcXJ0KGNpXzEgKyBjaV8yKSwNCiAgICAgICAgICAgICBjaV9sb3dlciA9IGxvZyhycl9QTlMpIC0gKDEuOTYqY2lfcm9vdCksDQogICAgICAgICAgICAgY2lfdXBwZXIgPSBsb2cocnJfUE5TKSArICgxLjk2KmNpX3Jvb3QpLA0KICAgICAgICAgICAgIGxfY2lfcG5zID0gZXhwKGNpX2xvd2VyKSwNCiAgICAgICAgICAgICB1X2NpX3BucyA9IGV4cChjaV91cHBlcikpICU+JQ0KICAgICAgc2VsZWN0KENvbW9yYmlkaXR5LCBycl9QTlMsIGxfY2lfcG5zLCB1X2NpX3BucykNCiAgDQogICAgICByciA8LSBycl9jbnMgJT4lDQogICAgICAgIGxlZnRfam9pbiguLCBycl9wbnMsIGJ5ID0gYygiQ29tb3JiaWRpdHkiKSkNCiAgDQogICAgcnJfY2FsY3NbW2ldXSA8LSBycg0KICANCiAgfQ0KICANCiAgcnJfcmVzdWx0cyA8LSBiaW5kX3Jvd3MocnJfY2FsY3MpICU+JQ0KICAgICAgbXV0YXRlKHJyX0NOUyA9IHJvdW5kKHJyX0NOUywgMiksDQogICAgICAgICAgIGxfY2lfY25zID0gcm91bmQobF9jaV9jbnMsIDIpLA0KICAgICAgICAgICB1X2NpX2NucyA9IHJvdW5kKHVfY2lfY25zLCAyKSwNCiAgICAgICAgICAgcnJfUE5TID0gcm91bmQocnJfUE5TLCAyKSwNCiAgICAgICAgICAgbF9jaV9wbnMgPSByb3VuZChsX2NpX3BucywgMiksDQogICAgICAgICAgIHVfY2lfcG5zID0gcm91bmQodV9jaV9wbnMsIDIpLA0KICAgICAgICAgICBgUlIgQ05TICg5NSUgQ0kpYCA9IHBhc3RlKHJyX0NOUywgIigiLCBsX2NpX2NucywgIiwiLCB1X2NpX2NucywgIikiKSwNCiAgICAgICAgICAgYFJSIFBOUyAoOTUlIENJKWAgPSBwYXN0ZShycl9QTlMsICIoIiwgbF9jaV9wbnMsICIsIiwgdV9jaV9wbnMsICIpIikpICU+JQ0KICAgIHNlbGVjdChDb21vcmJpZGl0eSwgcnJfQ05TLCBsX2NpX2NucywgdV9jaV9jbnMsIGBSUiBDTlMgKDk1JSBDSSlgLA0KICAgICAgICAgICBycl9QTlMsIGxfY2lfcG5zLCB1X2NpX3BucywgYFJSIFBOUyAoOTUlIENJKWApDQogIA0KICAjIHRpZHkgdXAgZGF0YQ0KICB3cml0ZS5jc3YocnJfcmVzdWx0cyAlPiUNCiAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKHJyX0NOUykpICU+JQ0KICAgICAgICAgICAgICBzZWxlY3QoQ29tb3JiaWRpdHksIGBSUiBDTlMgKDk1JSBDSSlgLCBgUlIgUE5TICg5NSUgQ0kpYCksIA0KICAgICAgICAgICAgcGFzdGUwKCJ0YWJsZXMvU3VwcC5UYWJsZTRfUlJfIiwgcG9wLCAiLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkNCiAgDQogICAgIyByZWZvcm1hdCBkYXRhDQogIHJyX3Jlc3VsdHNfdGlkeV9yciA8LSBycl9yZXN1bHRzICU+JQ0KICAgIHNlbGVjdChDb21vcmJpZGl0eSwgcnJfQ05TLCBycl9QTlMpICU+JQ0KICAgIHBpdm90X2xvbmdlcihjb2xzID0gcnJfQ05TOnJyX1BOUywgbmFtZXNfdG8gPSAiTmV1cm8gU3RhdHVzIiwgdmFsdWVzX3RvID0gIlJSIikgJT4lDQogICAgbXV0YXRlKGBOZXVybyBTdGF0dXNgID0gaWZfZWxzZShgTmV1cm8gU3RhdHVzYCA9PSAicnJfQ05TIiwgIkNOUyIsICJQTlMiKSkNCiAgDQogIHJyX3Jlc3VsdHNfdGlkeV9sX2NpIDwtIHJyX3Jlc3VsdHMgJT4lDQogICAgc2VsZWN0KENvbW9yYmlkaXR5LCBsX2NpX2NucywgbF9jaV9wbnMpICU+JQ0KICAgIHBpdm90X2xvbmdlcihjb2xzID0gbF9jaV9jbnM6bF9jaV9wbnMsIG5hbWVzX3RvID0gIk5ldXJvIFN0YXR1cyIsIHZhbHVlc190byA9ICJsX0NJIikgJT4lDQogICAgbXV0YXRlKGBOZXVybyBTdGF0dXNgID0gaWZfZWxzZShgTmV1cm8gU3RhdHVzYCA9PSAibF9jaV9jbnMiLCAiQ05TIiwgIlBOUyIpKQ0KICANCiAgcnJfcmVzdWx0c190aWR5X3VfY2kgPC0gcnJfcmVzdWx0cyAlPiUNCiAgICBzZWxlY3QoQ29tb3JiaWRpdHksIHVfY2lfY25zLCB1X2NpX3BucykgJT4lDQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSB1X2NpX2Nuczp1X2NpX3BucywgbmFtZXNfdG8gPSAiTmV1cm8gU3RhdHVzIiwgdmFsdWVzX3RvID0gInVfQ0kiKSAlPiUNCiAgICBtdXRhdGUoYE5ldXJvIFN0YXR1c2AgPSBpZl9lbHNlKGBOZXVybyBTdGF0dXNgID09ICJ1X2NpX2NucyIsICJDTlMiLCAiUE5TIikpDQogIA0KICAjIGNvbWJpbmUgYWxsIHRpZHkgcmVzdWx0cw0KICBycl9yZXN1bHRzX3RpZHkgPC0gcnJfcmVzdWx0c190aWR5X3JyICU+JQ0KICAgIGxlZnRfam9pbiguLCBycl9yZXN1bHRzX3RpZHlfbF9jaSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIkNvbW9yYmlkaXR5IiwgIk5ldXJvIFN0YXR1cyIpKSAlPiUNCiAgICBsZWZ0X2pvaW4oLiwgcnJfcmVzdWx0c190aWR5X3VfY2ksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJDb21vcmJpZGl0eSIsICJOZXVybyBTdGF0dXMiKSkNCiAgDQogICMgb3JkZXIgYnkgQ05TIGRpYWdub3NlcyB3aXRoIGhpZ2hlciBSUg0KICBjbnNfdG9wX3Jlc3VsdHMgPC0gcnJfcmVzdWx0c190aWR5ICU+JQ0KICAgIGZpbHRlcihgTmV1cm8gU3RhdHVzYCA9PSAiQ05TIikgJT4lDQogICAgYXJyYW5nZShSUikgJT4lDQogICAgc2VsZWN0KENvbW9yYmlkaXR5KQ0KICANCiAgcnJfcmVzdWx0c190aWR5JENvbW9yYmlkaXR5IDwtICBvcmRlcmVkKHJyX3Jlc3VsdHNfdGlkeSRDb21vcmJpZGl0eSwgbGV2ZWxzID0gY25zX3RvcF9yZXN1bHRzJENvbW9yYmlkaXR5KQ0KICANCiAgY25zX3JyX3Bsb3QgPC0gZ2dwbG90KHJyX3Jlc3VsdHNfdGlkeSAlPiUNCiAgICAgICAgICAgZmlsdGVyKGBOZXVybyBTdGF0dXNgID09ICJDTlMiKSwNCiAgICAgICAgIGFlcyh4ID0gUlIsIHkgPSBDb21vcmJpZGl0eSkpICsNCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMSksIHNpemUgPSAuMjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgICAgIGdlb21fZXJyb3JiYXJoKGFlcyh4bWF4ID0gdV9DSSwgeG1pbiA9IGxfQ0kpLCBzaXplID0gMC43LCBoZWlnaHQgPSAwLjIsIGNvbG9yID0gImdyYXk1MCIpICsNCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAidG9tYXRvIikgKw0KICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA0LCAxKSwgbGFiZWxzID0gc2VxKDAsIDQsIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCw0KSkgKw0KICAgIHlsYWIoIiIpICsNCiAgICB4bGFiKCJSZWxhdGl2ZSBSaXNrIikgKw0KICAgIGdndGl0bGUoIkNOUyBQYXRpZW50cyIpICsgDQogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIikpDQogIA0KICAjIGFycmFuZ2UgYnkgdG9wIFBOUyByZXN1bHRzDQogIHBuc190b3BfcmVzdWx0cyA8LSBycl9yZXN1bHRzX3RpZHkgJT4lDQogICAgZmlsdGVyKGBOZXVybyBTdGF0dXNgID09ICJQTlMiKSAlPiUNCiAgICBhcnJhbmdlKFJSKSAlPiUNCiAgICBzZWxlY3QoQ29tb3JiaWRpdHkpDQogIA0KICBycl9yZXN1bHRzX3RpZHkkQ29tb3JiaWRpdHkgPC0gIG9yZGVyZWQocnJfcmVzdWx0c190aWR5JENvbW9yYmlkaXR5LCBsZXZlbHMgPSBwbnNfdG9wX3Jlc3VsdHMkQ29tb3JiaWRpdHkpDQogIA0KICANCiAgcG5zX3JyX3Bsb3QgPC0gZ2dwbG90KHJyX3Jlc3VsdHNfdGlkeSAlPiUNCiAgICAgICAgICAgZmlsdGVyKGBOZXVybyBTdGF0dXNgID09ICJQTlMiKSwNCiAgICAgICAgIGFlcyh4ID0gUlIsIHkgPSBDb21vcmJpZGl0eSkpICsNCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMSksIHNpemUgPSAuMjUsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgICAgIGdlb21fZXJyb3JiYXJoKGFlcyh4bWF4ID0gdV9DSSwgeG1pbiA9IGxfQ0kpLCBzaXplID0gMC43MCwgaGVpZ2h0ID0gMC4yLCBjb2xvciA9ICJncmF5NTAiKSArDQogICAgICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gInNsYXRlYmx1ZSIpICsNCiAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMS41LCAwLjUpLCBsYWJlbHMgPSBzZXEoMCwgMS41LCAwLjUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwxLjUpKSArDQogICAgeWxhYigiIikgKw0KICAgIHhsYWIoIlJlbGF0aXZlIFJpc2siKSArDQogICAgZ2d0aXRsZSgiUE5TIFBhdGllbnRzIikgKyANCiAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkNCiAgDQogIHBuc19ycl9wbG90IDwtIHNldF9wYW5lbF9zaXplKHBuc19ycl9wbG90LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoICA9IHVuaXQoNywgImNtIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gdW5pdCg2LjUsICJpbiIpKQ0KICANCiAgY25zX3JyX3Bsb3QgPC0gc2V0X3BhbmVsX3NpemUoY25zX3JyX3Bsb3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggID0gdW5pdCg3LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSB1bml0KDYuNSwgImluIikpDQogIA0KICAjZ3JpZC5hcnJhbmdlKGNuc19ycl9wbG90LCBwbnNfcnJfcGxvdCwgbmNvbD0yKQ0KICANCiAgZ2dzYXZlKHBhc3RlMCgiZmlndXJlcy9GaWd1cmU0LnJlbGF0aXZlX3Jpc2tfIiwgcG9wLCAiLnBuZyIpLCBncmlkLmFycmFuZ2UoY25zX3JyX3Bsb3QsIHBuc19ycl9wbG90LCBuY29sPTIpLCB3aWR0aCA9IDE1LCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApDQogIA0KICB9DQpgYGANCg0KIyMjIFJlbGF0aXZlIFJpc2sgKFJSKQ0KDQpIZXJlIHdlIHBsb3QgdGhlIHJlbGF0aXZlIHJpc2sgb2YgYWNxdWlyaW5nIGEgQ05TIG9yIFBOUyBkaWFnbm9zaXMgaW4gYWR1bHQgcGF0aWVudHMgd2hvIGhhdmUgZWFjaCBjb21vcmJpZGl0eSBvZiB0aGUgRUNJIGNvbXBhcmVkIHRvIHRob3NlIHdobyBkbyBub3QuDQoNCldlIGFyZSBub3QgYWJsZSB0byBjb21wdXRlIHRoaXMgYW5hbHlzaXMgaW4gcGVkaWF0cmljIHBhdGllbnRzLCBkdWUgdG8gdGhlIG92ZXJhbGwgbG93IGluY2lkZW5jZSBvZiBjb21vcmJpZGl0aWVzIGFuZCBuZXVyb2xvZ2ljYWwgaWxsbmVzcyBkdXJpbmcgQ09WSUQtMTkgaG9zcGl0YWxpemF0aW9uLg0KDQpgYGB7ciBldmFsPVRSVUUsIGluY2x1ZGU9RkFMU0V9DQpycl9hZHVsdCA8LSBjYWxjdWxhdGVfcnIoaXNfcGVkaWF0cmljID0gRkFMU0UpDQojcnJfcGVkaWF0cmljIDwtIGNhbGN1bGF0ZV9ycihpc19wZWRpYXRyaWMgPSBUUlVFKQ0KYGBgDQoNCiFbXShmaWd1cmVzL0ZpZ3VyZTQucmVsYXRpdmVfcmlza19BZHVsdC5wbmcpey5jbGFzcyB3aWR0aD0iODI3IiBoZWlnaHQ9IjQ1MiJ9DQoNCiMjIyBMUENBIGRldmlhbmNlIGV4cGxhaW5lZA0KDQpEdXJpbmcgdGhlIGxvY2FsIGFuYWx5c2lzIGZvciBlYWNoIHBhcnRpY2lwYXRpbmcgaGVhbHRoY2FyZSBzeXN0ZW0sIHdlIGNvbXB1dGVkIHRoZSB0b3AgMTAgcHJpbmNpcGFsIGNvbXBvbmVudHMgdXNpbmcgbG9naXN0aWMgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyAoTFBDQSkuIExQQ0Egd2FzIHBlcmZvcm1lZCBvbiB0aGUgbWF0cml4IG9mIG5wYXRpZW50cyB4IDI5IEVDSSBjb21vcmJpZGl0aWVzLiBBY3Jvc3MgaGVhbHRoY2FyZSBzeXN0ZW1zLCAxMCBwcmluY2lwYWwgY29tcG9uZW50cyBleHBsYWluZWQgYXQgbGVhc3QgNzUlIG9mIHRoZSBtb2RlbCBkZXZpYW5jZS7CoA0KDQpUaGlzIHN1cHBsZW1lbnRhcnkgYW5hbHlzaXMgd2FzIHBlcmZvcm1lZCBpbiBvcmRlciB0byBkZXRlcm1pbmUgdGhlIGJlc3Qgd2F5IHRvIGFkanVzdCBmb3IgZWFjaCBwYXRpZW50J3MgY29tb3JiaWRpdHkgYnVyZGVuIGluIG91ciBkb3duc3RyZWFtIHN1cnZpdmFsIG1vZGVscy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNvbXBhcmVfZGV2aWFuY2UgPC0gZnVuY3Rpb24oc29ydGVkX3NpdGVzLCBpc19wZWRpYXRyaWM9RkFMU0UpIHsNCiAgDQogIHBjYV9kZXYgPC0gbGlzdCgpDQogIA0KICBpZihpc19wZWRpYXRyaWM9PUZBTFNFKSB7DQogICAgcG9wdWxhdGlvbiA9ICJhZHVsdHMiDQogICAgc29ydGVkX3NpdGVzID0gc29ydGVkX3NpdGVzWyFzb3J0ZWRfc2l0ZXMlaW4lIGMoIkdPU0hfcmVzdWx0cyIsICJCQ0hfcmVzdWx0cyIpXQ0KICAgIH0gZWxzZSB7DQogICAgcG9wdWxhdGlvbiA9ICJwZWRpYXRyaWNzIg0KICAgIH0NCg0KICAjIGZvciBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtLCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHBhdGllbnRzIHdpdGggZWFjaCBJQ0QgY29kZQ0KICBmb3IgKGkgaW4gc29ydGVkX3NpdGVzKSB7DQogICAgcHJpbnQoaSkNCiAgICB0bXAgPC0gZ2V0KGkpDQogICAgdG1wX3BjYSA8LSB0bXBbW2MoDQogICAgICAiY29tb3JiaWRpdGllcyIsDQogICAgICBwYXN0ZTAoImNvbW9yYl8iLCBwb3B1bGF0aW9uKSwNCiAgICAgICJkZXZpYW5jZV9leHBsIg0KICAgICldXSAlPiUgDQogICAgICBkYXRhLmZyYW1lKCkgJT4lIA0KICAgICAgcmVuYW1lKGRldl9leHBsID0gJy4nKQ0KICAgIHRtcF9wY2Ekc2l0ZSA8LSB0bXBbWyJzaXRlIl1dDQogICAgcGNhX2RldltbaV1dIDwtIHRtcF9wY2ENCiAgICANCiAgfQ0KICANCiAgcGNhX2RmIDwtIGJpbmRfcm93cyhwY2FfZGV2KQ0KICANCiAgcmV0dXJuKHBjYV9kZikNCiAgDQp9DQoNCnBjYV9hZHVsdCA8LSBjb21wYXJlX2RldmlhbmNlKHNvcnRlZF9zaXRlcyA9IGFkdWx0X3NpdGVzLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KDQpwY2FfZGF0YSA8LSBiaW5kX3Jvd3MocGNhX2FkdWx0KQ0KDQoNCnRoZW1lX3NldCh0aGVtZV9jbGFzc2ljKCkpDQoNCnBjYV9wbG90IDwtIGdncGxvdChwY2FfZGF0YSwgDQogICAgICAgYWVzKHggPSBmY3RfcmVvcmRlcihzaXRlLCAtZGV2X2V4cGwpLCB5ID0gZGV2X2V4cGwsIGdyb3VwID0gMSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMi41KSArDQogIGdlb21fbGluZSgpICsNCiAgeGxhYigiSGVhbHRoY2FyZSBTeXN0ZW0iKSArIA0KICB5bGFiKCJQcm9wb3J0aW9uIG9mIERldmlhbmNlIGV4cGxhaW5lZCIpICsgIA0KICB5bGltKDAsMSkgKyANCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTIpLA0KICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksIA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE1KSk7IHBjYV9wbG90DQoNCmdnc2F2ZSgiZmlndXJlcy9TdXBwRmlnX3BjYV9kZXZpYW5jZS5wbmciLCBwY2FfcGxvdCwgaGVpZ2h0ID0gNiwgd2lkdGggPSA2KQ0KYGBgDQo=