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=