library(dplyr)
library(tidyr)
library(readr)
library(ggplot2)
library(rcartocolor)
library(forcats)
library(purrr)
library(stringr)
library(DT)
library(cowplot)

load('data/processed-data.Rdata')
theme_set(theme_bw() + theme(legend.title = element_blank()))
source('utils.R')
countries <- c("France", "Germany", "Spain", "Italy", "Singapore", "USA" )

Prevalence analysis

Calculate percentages:

code_prevalence <- diag_icd_10 %>% 
  group_by(siteid, Country, time, icd, `Neurological Disease Category`, full_icd) %>% 
  # summarise(pats_time_icd_site = sum(num_patients_icd), .groups = 'drop') %>% 
  # left_join(demo_ana, by = 'siteid') %>% 
  mutate(percent_pats_site = num_patients_icd/num_patients_all,
         siteid = as.factor(siteid)) %>% 
  ungroup() %>% 
  group_by(Country, time, icd, `Neurological Disease Category`, full_icd) %>% 
  mutate(percent_pats_country = sum(num_patients_icd)/all_pats_country) %>% 
  ungroup()
diff_code <- code_prevalence %>% 
  select(location = siteid, icd, `Neurological Disease Category`, time, percent_pats_site) %>% 
  pivot_wider(names_from = time, values_from = percent_pats_site, 
              id_cols = c(location, icd, `Neurological Disease Category`), 
              values_fill = list(percent_pats_site = 0)) %>% 
  mutate(percent_diff = `After admission` - `Before admission`,
         loc_type = 'Site', location = as.factor(tolower(location)))

diff_code_country <- code_prevalence %>% 
  select(location = Country, icd, `Neurological Disease Category`, time, percent_pats_country) %>% 
  distinct() %>% 
  pivot_wider(names_from = time, values_from = percent_pats_country, 
              id_cols = c(location, icd, `Neurological Disease Category`), 
              values_fill = list(percent_pats_country = 0)) %>% 
  mutate(percent_diff = `After admission` - `Before admission`,
         location = fct_relevel(location, countries),
         loc_type = 'Country')
## Warning: Problem with `mutate()` input `location`.
## ℹ Unknown levels in `f`: Italy
## ℹ Input `location` is `fct_relevel(location, countries)`.
## Warning: Unknown levels in `f`: Italy
diff_code_loc <- diff_code %>% 
  bind_rows(diff_code_country)

diff_heat <- diff_code_loc %>% 
  ggplot(aes(y = fct_rev(icd), x = location, fill = percent_diff)) +
  geom_tile() +
  labs(y = NULL, x = NULL, fill = 'After - Before') +
  scale_x_discrete() + 
  scale_fill_gradient2(#009392,#39b185,#9ccb86,#e9e29c,#eeb479,#e88471,#cf597e
    low = '#798234',
    high = '#cf597e',
    labels = scales::percent_format(accuracy = 1)) +
  facet_grid(rows = vars(`Neurological Disease Category`), 
             cols = vars(fct_rev(loc_type)), space = 'free', scales = 'free') +
  heat_theme_bottom() +
  theme(plot.margin = unit(c(1,0.2,0.2,3), "lines")) +
  NULL
diff_heat

# ggsave('figs/icd_diff_heat_sitemap.png', diff_heat_site, height = 4, width = 8)
before_code_prev <- diff_code_loc %>% 
  ggplot(aes(y = icd, x = location, fill = `Before admission`)) +
  geom_tile() +
  labs(y = NULL, x = NULL, fill = 'Patient % per
site/country') +
  #f6d2a9,#f5b78e,#f19c7c,#ea8171,#dd686c,#ca5268,#b13f64
  scale_fill_gradient(
    low = '#d1eeea', high = '#2a5674',
    labels = scales::percent_format(accuracy = 1),
    limits = c(0, max(diff_code_loc$`After admission`)),
    guide = guide_legend(override.aes = list(fill = "white"))) +
  scale_x_discrete() + 
  facet_grid(rows = vars(`Neurological Disease Category`), 
             cols = vars(fct_rev(loc_type)), space = 'free', scale = 'free') +
  heat_theme_top() +
  NULL

after_code_prev <- diff_code_loc %>% 
  ggplot(aes(y = icd, x = location, fill = `After admission`)) +
  geom_tile() +
  labs(y = NULL, x = NULL, fill = 'Patient % per
site/country') +
  scale_x_discrete() + 
  scale_fill_gradient(low = '#d1eeea', high = '#2a5674',
                      labels = scales::percent_format(accuracy = 1),
                      limits = c(0, max(diff_code_loc$`After admission`))) +
  facet_grid(rows = vars(`Neurological Disease Category`), 
             cols = vars(fct_rev(loc_type)), space = 'free', scale = 'free') +
  heat_theme_bottom() +
  theme(legend.title = element_text(hjust = 0)) +
  NULL

heats <- cowplot::plot_grid(before_code_prev, after_code_prev, ncol = 1,
                            rel_heights = c(1, 1.15),
                   labels = c('A. Before admission', 'B. After admission'))
heats

ggsave('figs/icd_heatmap.png', heats, height = 10, width = 9)
ggsave('figs/tiffs/efig_1.tiff', heats, height = 10, width = 9, dpi = 300)

Hypothesis testing

my_t <- function(icdi){
  diff_icd <- diff_code %>% filter(icd == icdi)
  data.frame(
    icd = icdi,
    t.test(diff_icd$percent_diff) %>% 
    broom::tidy()
  )
}
prevalence_stats <- unique(diff_code$icd) %>% 
  lapply(my_t) %>% 
  bind_rows()
prevalence_stats$p_value_holm <-  p.adjust(prevalence_stats$p.value, 'holm')
prevalence_stats$p_value_bh <- p.adjust(prevalence_stats$p.value, 'BH')
prevalence_stats %>% 
  filter(p_value_bh < 0.05)
##   icd   estimate statistic      p.value parameter   conf.low  conf.high
## 1 R41 0.05765947  5.652728 1.705131e-06        38 0.03701006 0.07830888
## 2 G93 0.08116207  6.897838 4.471201e-08        36 0.05729890 0.10502525
##              method alternative p_value_holm   p_value_bh
## 1 One Sample t-test   two.sided 3.239748e-05 1.705131e-05
## 2 One Sample t-test   two.sided 8.942402e-07 8.942402e-07
check = diff_code %>% 
  filter(loc_type == 'Site', icd == 'G93') %>% 
  left_join(diag_icd_10 %>% select(siteid, Country) %>% distinct() %>% mutate(siteid = tolower(siteid)),
            by = c('location' = 'siteid'))
check %>% group_by(Country) %>% 
  summarise(country_diff = mean(percent_diff), .groups = 'drop')
## # A tibble: 4 x 2
##   Country country_diff
##   <chr>          <dbl>
## 1 France       0.00850
## 2 Germany      0.0161 
## 3 Spain       -0.00422
## 4 USA          0.0924

Prevalence change table

prevalence_stats %>% 
  arrange(desc(estimate)) %>% 
  mutate(ci = paste0('(', round(conf.low*100, 2), '%, ', 
                     round(conf.high*100, 2), '%)')) %>% 
  select(- c(parameter, method, alternative, conf.low, conf.high)) %>% 
  datatable(rownames = FALSE, filter = 'top') %>% 
  formatRound(c('statistic'), 1) %>%
  formatPercentage('estimate', 1) %>% 
  formatSignif(c('p.value', 'p_value_holm', 'p_value_bh'), 3) %>%
  {.}
prevalence_stats %>% 
  write_csv('results/icd10_prevalence_stats.csv')

Compute confidence inverval

of the mean proportion of patients diagnosed with each ICD

alpha_threshold <- qnorm(0.975)

ci_prevalence <- code_prevalence %>% 
  group_by(time, full_icd) %>%
  add_count() %>%
  summarise(
    mean_prop = mean(percent_pats_site, na.rm = T),
    sd_prob = sd(percent_pats_site, na.rm = T),
    n = mean(n),
    me_prop = alpha_threshold * sd_prob / sqrt(n)
  ) %>%
  ungroup()
## `summarise()` regrouping output by 'time' (override with `.groups` argument)
icd_time <- diag_icd_10 %>% 
  mutate(time = fct_relevel(time, c('After admission', 'Before admission'))) %>% 
  group_by(full_icd, time) %>% 
  summarise(pats_icd_time = sum(num_patients_icd, na.rm = T), .groups = 'drop') 
# sorted_icds <- icd_time %>% 
#   filter(time == 'After admission') %>% 
#   arrange(pats_icd_time) %>% 
#   pull(full_icd)
# sorted_icds <- code_prevalence %>% 
#   ungroup() %>% 
#   select(time, percent_pats_site, full_icd, siteid) %>% 
#   pivot_wider(names_from = time, values_from = percent_pats_site, 
#               values_fill = list(percent_pats_site = 0)) %>% 
#   group_by(full_icd) %>% 
#   summarise(after = mean(`After admission`, na.rm = T),
#          before = mean(`Before admission`, na.rm = T),
#          diff_prev = after - before,
#          .groups = 'drop') %>% 
#   arrange(diff_prev) %>% 
#   pull(full_icd)
sorted_icds <- code_prevalence %>% 
  distinct(full_icd, icd, `Neurological Disease Category`) %>% 
  arrange(desc(`Neurological Disease Category`), desc(icd)) %>% 
  pull(full_icd)

total_icd <- icd_time %>% 
  ggplot(aes(x = pats_icd_time, y = fct_relevel(full_icd, sorted_icds), fill = time)) +
  scale_fill_carto_d(palette = 4, guide = guide_legend(reverse = TRUE)) +
  geom_col(position = 'dodge') +
  theme_minimal() +
  scale_x_reverse(expand = expansion(add = c(0,0))) +
  scale_y_discrete(labels = NULL) +
  theme(legend.position = c(0.3, 0.15),
        panel.grid.minor = element_blank(),
        legend.title = element_blank(),
        legend.key.height = unit(4, 'mm'),
        plot.margin = margin(t = 1, r = 0.5, l = 0.5, unit = 'lines')) + 
  labs(x = 'Total number of patients at all sites', y = NULL)

percent_icd <- 
  ci_prevalence %>% 
  ggplot(aes(group = time)) +
  ggstance::geom_pointrangeh(
    aes(x = mean_prop,
        y = fct_relevel(full_icd, sorted_icds),
        xmin = mean_prop - me_prop,
        xmax = mean_prop + me_prop,
        color = time),
    position = position_dodge(width = 0.3), 
    stroke = 0.1, fatten = 3, size = 0.7  
  ) +
  scale_color_carto_d(palette = 4, guide = NULL) +
  theme_minimal() +  
  theme(legend.position = c(0.75, 0.1),
        panel.grid.minor = element_blank(),
        legend.title = element_blank(),
        axis.text.y = element_text(hjust = 0.5),
        plot.margin = margin(t = 1, unit = 'lines')) + 
  scale_x_continuous(expand = expansion(add = c(0, 0.03)),
                     labels = scales::percent_format(accuracy = 1)) +
  labs(x = 'Proportion of patients each site', y = NULL)

icd_prevalence_plots <- cowplot::plot_grid(total_icd, percent_icd, ncol = 2,
                                           rel_widths = c(1, 3))
icd_prevalence_plots

# ggsave('figs/icd_prevalence.png', icd_prevalence_plots, height = 5, width = 10)
plot_grid(diff_heat, icd_prevalence_plots, rel_heights = c(1, 0.85),
          ncol = 1, labels = 'AUTO') %>%
  ggsave('figs/icd_prevalence_AB.png', ., height = 8, width = 9.7)

plot_grid(diff_heat, icd_prevalence_plots, rel_heights = c(1, 0.85),
          ncol = 1, labels = 'AUTO') %>%
ggsave('figs/tiffs/fig_3.tiff', ., height = 8, width = 9.7, dpi = 300)
diff_code %>% 
  filter(icd == 'R41') %>% 
  ungroup() %>% 
  count(percent_diff > 0)
## # A tibble: 2 x 2
##   `percent_diff > 0`     n
##   <lgl>              <int>
## 1 FALSE                  9
## 2 TRUE                  30
diff_code %>% 
  filter(icd == 'G93') %>% 
  ungroup() %>% 
  count(percent_diff > 0)
## # A tibble: 2 x 2
##   `percent_diff > 0`     n
##   <lgl>              <int>
## 1 FALSE                  6
## 2 TRUE                  31
diff_code %>% 
  filter(icd == 'R42') %>% 
  ungroup() %>% 
  count(percent_diff > 0)
## # A tibble: 2 x 2
##   `percent_diff > 0`     n
##   <lgl>              <int>
## 1 FALSE                 28
## 2 TRUE                  10

Severity descriptive statistics

What ICD code has the most number severe patients? Sites with more severe patients?

severe_code <- diag_icd_10 %>% 
  # filter(siteid != 'SITE309') %>%
  mutate(percent_severe_site = num_patients_ever_severe_icd1/num_patients_icd,
         siteid = as.factor(siteid))

severe_bef_heat <- severe_code %>% 
  filter(time == 'Before admission') %>% 
  ggplot(aes(y = icd, x = siteid, fill = percent_severe_site)) +
  geom_tile() +
  labs(y = NULL, x = NULL, fill = 'Severe % per ICD') +
  #3d5941,#778868,#b5b991,#f6edbd,#edbb8a,#de8a5a,#ca562c
  scale_fill_gradient(
    # low = '#f6d2a9', high = '#b13f64',
    low = 'white', high = '#ca562c',
    guide = guide_legend(override.aes = list(fill = "white")),
    ) +
  scale_x_discrete() + 
  facet_grid(rows = vars(`Neurological Disease Category`), space = 'free', scale = 'free') +
  heat_theme_top() +
  NULL

severe_aft_heat <- severe_code %>% 
  filter(time == 'After admission') %>% 
  ggplot(aes(y = icd, x = siteid, fill = percent_severe_site)) +
  geom_tile() +
  labs(y = NULL, x = NULL, fill = 'Severe % per ICD') +
  scale_x_discrete() + 
  #3d5941,#778868,#b5b991,#f6edbd,#edbb8a,#de8a5a,#ca562c
  scale_fill_gradient(
    low = 'white', high = '#ca562c',
    labels = scales::percent_format(accuracy = 1)) +
  facet_grid(rows = vars(`Neurological Disease Category`), space = 'free', scale = 'free') +
  heat_theme_bottom() +
  NULL

severe_heats <- cowplot::plot_grid(severe_bef_heat, severe_aft_heat, ncol = 1,
                            rel_heights = c(1, 1.15),
                   labels = c('A. Before admission', 'B. After admission'))
severe_heats

ggsave('figs/icd_severe_heatmap.png', severe_heats, height = 10, width = 7)

Severity enrichment analysis

Null hypothesis

For each ICD code, the proportion of severe patients who were diagnosed with that ICD code is similar to the proportion of never-severe patients who were diagnosed with that same ICD code.

For the sake of simplicity in this notebook, we’re going to denote patients whose symptoms have been categorized as severe (based on respiratory status +/- requiring ICU) at least once as severe patients, and patients whose symptoms have NEVER been categorized as severe as non-severe patients.

For each ICD code, we computed the expected number of severe patients by multiplying the proportion of non-severe patients who were diagnosed with that code with the total number of severe patients. We performed an enrichment analysis to examine the difference of severe patients proportions across ICD codes. We calculated each ICD code’s enrichment by dividing the observed proportion of honorees by the expected proportion of honorees and reported a value of log2 enrichment (LOE) and its 95% confidence intervals. The 95% confidence interval of the LOE was estimated using the Poisson model method [@isbn:9780849394447].

contingency_continent <- diag_icd_10 %>%
  mutate(continent = ifelse(Country == 'USA', 'US', 'NonUS')) %>% 
  group_by(full_icd, time, continent) %>%
  summarise(across(contains('severe'), .fns = sum, na.rm = T), .groups = 'drop') %>%
  mutate(Observed = num_patients_ever_severe_icd1,
         num_non_severe = num_patients_never_severe_icd1 + num_patients_never_severe_icd0,
         num_severe = num_patients_ever_severe_icd1 + num_patients_ever_severe_icd0,
         Expected = num_patients_never_severe_icd1/num_non_severe*num_severe,
         over_sev = Observed - Expected)

ind_fish <- contingency_continent %>%
  filter(!is.na(Expected), num_patients_never_severe_icd0 >= 0) %>%
  select(full_icd, time, continent,
         num_patients_never_severe_icd0,
         num_patients_never_severe_icd1,
         num_patients_ever_severe_icd0,
         num_patients_ever_severe_icd1) %>%
  group_by(full_icd, time, continent) %>%
  nest() %>%
  mutate(fish = map(data, my_fish)) %>%
  dplyr::select(-data) %>%
  unnest(cols = c(fish)) %>% 
  mutate(upper = ifelse(is.na(upper), Inf, upper)) %>% 
  mutate(lci = paste0('(', round(log2(lower), 1), ', ', 
                     round(log2(upper), 1), ')'),
         lestimate = log2(estimate))
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 8: full_icd = "Disturbances of smell and taste (R43)", time = "Before admission", continent = "US".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 15: full_icd = "Encephalitis, myelitis and encephalomyelitis (G04)", time = "Before admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 16: full_icd = "Encephalitis, myelitis and encephalomyelitis (G04)", time = "Before admission", continent = "US".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 23: full_icd = "Inflammatory polyneuropathy (G61)", time = "Before admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 24: full_icd = "Inflammatory polyneuropathy (G61)", time = "Before admission", continent = "US".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 25: full_icd = "Meningitis due to other and unspecified causes (G03)", time = "After admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 27: full_icd = "Meningitis due to other and unspecified causes (G03)", time = "Before admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 28: full_icd = "Meningitis due to other and unspecified causes (G03)", time = "Before admission", continent = "US".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 29: full_icd = "Myositis (M60)", time = "After admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 31: full_icd = "Myositis (M60)", time = "Before admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 47: full_icd = "Other and unspecified nontraumatic intracranial hemorrhage (I62)", time = "Before admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ NaNs produced
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 59: full_icd = "Other headache syndromes (G44)", time = "Before admission", continent = "NonUS".
## Warning in sqrt(1/(a1 + 0.1) - 1/(n1) + 1/(a0 + 0.1) - 1/(n0)): NaNs produced
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 59: full_icd = "Other headache syndromes (G44)", time = "Before admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 69: full_icd = "Sequelae of inflammatory and toxic polyneuropathies (G65)", time = "After admission", continent = "US".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 70: full_icd = "Sequelae of inflammatory and toxic polyneuropathies (G65)", time = "Before admission", continent = "US".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 77: full_icd = "Unspecified psychosis not due to a substance or known physiological condition (F29)", time = "Before admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 79: full_icd = "Vascular syndromes of brain in cerebrovascular disease (G46)", time = "After admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 80: full_icd = "Vascular syndromes of brain in cerebrovascular disease (G46)", time = "After admission", continent = "US".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 81: full_icd = "Vascular syndromes of brain in cerebrovascular disease (G46)", time = "Before admission", continent = "NonUS".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 82: full_icd = "Vascular syndromes of brain in cerebrovascular disease (G46)", time = "Before admission", continent = "US".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
ind_fish$`P value (Holm)` <-  p.adjust(ind_fish$p_value, 'holm')
ind_fish$`P value (FDR)` <- p.adjust(ind_fish$p_value, 'BH')

madata <- contingency_continent %>% 
  left_join(ind_fish, by = c('full_icd', 'continent', 'time')) %>% 
  filter(time == 'After admission') %>% 
  mutate(
    distance_to_null = case_when(
      lower > 1 ~ lower - 1,
      TRUE ~ upper - 2
    ),
    presentation = case_when(
      lower > 1 & `P value (FDR)` < 0.05 ~ '#d46780', 
      upper < 1 & `P value (FDR)` < 0.05 ~ '#798234',
      TRUE ~ 'grey20'
    ),
    upper = ifelse(is.na(upper), Inf, upper))
  
plot_obs_exp <- madata %>%
  mutate(lestimate = log2(estimate),
         llower = log2(lower), 
         lupper = log2(upper)) %>% 
  select(full_icd, continent, lestimate, llower, lupper, presentation, over_sev, Observed, Expected) %>% 
  pivot_longer(- c(full_icd, continent, presentation, over_sev), names_to = 'type') %>% 
  mutate(subtype = ifelse(type == 'Expected' | type == 'Observed', 
                          'Sqrt(number of honorees)', 
                          'Log2 enrichment, 95% CI')) %>% 
  pivot_wider(names_from = type, values_from = value) %>% 
  mutate(presentation = as.factor(presentation),
         full_icd = fct_relevel(full_icd, sorted_icds))

sorted_icds <- madata %>% 
  filter(time == 'After admission') %>% 
  group_by(full_icd) %>% 
  summarise(total_expected = sum(Expected), .groups = 'drop') %>% 
  arrange(total_expected) %>% 
  pull(full_icd)

plot_obs_exp_right <- plot_obs_exp %>% filter(subtype == 'Sqrt(number of honorees)') 
plot_obs_exp_left <- plot_obs_exp %>% filter(subtype != 'Sqrt(number of honorees)') 

enrichment_plot_us <- plot_enrich(
  plot_obs_exp_left %>% filter(continent == 'US'),
  plot_obs_exp_right %>% filter(continent == 'US') %>% 
    slice(match(sorted_icds, full_icd)),
  nudge = 2.5)
## Warning: Vectorized input to `element_text()` is not officially supported.
## Results may be unexpected or may change in future versions of ggplot2.
enrichment_plot_nonus <- plot_enrich(
  plot_obs_exp_left %>% filter(continent == 'NonUS'),
  plot_obs_exp_right %>% filter(continent == 'NonUS') %>% slice(match(sorted_icds, full_icd)),
  nudge = 2.5)
## Warning: Vectorized input to `element_text()` is not officially supported.
## Results may be unexpected or may change in future versions of ggplot2.
enrichment_plot_cont <- cowplot::plot_grid(
  enrichment_plot_us,
  enrichment_plot_nonus,
  ncol = 1, hjust = -0.05,
  labels = c('A. US sites', 'B. Non-US sites')
)



ggsave('figs/icd_severe_after_us_nonus.png', enrichment_plot_cont, width = 9.5, height = 7)
ggsave('figs/tiffs/efig_2.tiff', enrichment_plot_cont, width = 9.5, height = 7, dpi = 300)

madata %>% 
  write_csv('results/icd10_fish_tab_us_nonus.csv')

Before and after admission

contingency_df <- diag_icd_10 %>% 
  # filter(time == 'Before admission') %>%
  select(- c(num_patients_all, num_patients_ever_severe, num_patients_never_severe)) %>% 
  group_by(full_icd, time) %>% 
  summarise(across(contains('severe'), .fns = sum, na.rm = T), .groups = 'drop') %>% 
  mutate(Observed = num_patients_ever_severe_icd1, 
         num_non_severe = num_patients_never_severe_icd1 + num_patients_never_severe_icd0,
         num_severe = num_patients_ever_severe_icd1 + num_patients_ever_severe_icd0,
         Expected = num_patients_never_severe_icd1/num_non_severe*num_severe,
         over_sev = Observed - Expected)

nested_obs_exp <- contingency_df %>%
  select(full_icd, 
         time,
         num_patients_never_severe_icd0, 
         num_patients_never_severe_icd1, 
         num_patients_ever_severe_icd0, 
         num_patients_ever_severe_icd1) %>% 
  group_by(full_icd, time) %>% 
  nest() 

fish_obs_exp <- nested_obs_exp %>% 
  mutate(fish = map(data, my_fish)) %>% 
  dplyr::select(-data) %>% 
  unnest(cols = c(fish)) %>% 
  mutate(upper = ifelse(is.na(upper), Inf, upper)) %>% 
  mutate(lci = paste0('(', round(log2(lower), 1), ', ', 
                     round(log2(upper), 1), ')'),
         lestimate = log2(estimate))
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 8: full_icd = "Encephalitis, myelitis and encephalomyelitis (G04)", time = "Before admission".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 14: full_icd = "Meningitis due to other and unspecified causes (G03)", time = "Before admission".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 35: full_icd = "Sequelae of inflammatory and toxic polyneuropathies (G65)", time = "After admission".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
## Warning: Problem with `mutate()` input `fish`.
## ℹ Chi-squared approximation may be incorrect
## ℹ Input `fish` is `map(data, my_fish)`.
## ℹ The error occurred in group 36: full_icd = "Sequelae of inflammatory and toxic polyneuropathies (G65)", time = "Before admission".
## Warning in chisq.test(xx, correct = correction): Chi-squared approximation may
## be incorrect
fish_obs_exp$`P value (Holm)` <-  p.adjust(fish_obs_exp$p_value, 'holm')
fish_obs_exp$`P value (FDR)` <- p.adjust(fish_obs_exp$p_value, 'BH')
fish_obs_exp %>% 
  filter(`P value (FDR)` < 0.05,
         time == 'After admission')
## # A tibble: 13 x 10
## # Groups:   full_icd, time [13]
##    full_icd time  estimate lower upper  p_value lci   lestimate `P value (Holm)`
##    <chr>    <chr>    <dbl> <dbl> <dbl>    <dbl> <chr>     <dbl>            <dbl>
##  1 Blindne… Afte…    0.771 0.669 0.889 5.33e- 5 (-0.…    -0.375         1.71e- 3
##  2 Dizzine… Afte…    0.724 0.669 0.785 6.01e-20 (-0.…    -0.465         2.34e-18
##  3 Encepha… Afte…    1.37  1.17  1.60  3.09e- 3 (0.2…     0.455         8.33e- 2
##  4 Nontrau… Afte…    1.36  1.23  1.51  6.81e- 6 (0.3…     0.446         2.38e- 4
##  5 Nontrau… Afte…    1.28  1.10  1.48  7.53e- 3 (0.1…     0.355         1.96e- 1
##  6 Other a… Afte…    1.72  1.67  1.77  4.21e-46 (0.7…     0.780         1.72e-44
##  7 Other a… Afte…    1.34  1.20  1.50  8.67e- 5 (0.3…     0.425         2.69e- 3
##  8 Other c… Afte…    1.24  1.13  1.35  4.75e- 5 (0.2…     0.307         1.57e- 3
##  9 Other d… Afte…    1.36  1.32  1.40  1.34e-74 (0.4…     0.442         5.63e-73
## 10 Other h… Afte…    0.531 0.409 0.690 1.34e- 9 (-1.…    -0.913         4.97e- 8
## 11 Other s… Afte…    1.22  1.19  1.25  1.52e-40 (0.2…     0.283         6.08e-39
## 12 Transie… Afte…    0.554 0.444 0.693 6.01e-11 (-1.…    -0.851         2.28e- 9
## 13 Unspeci… Afte…    0.559 0.434 0.720 1.29e- 8 (-1.…    -0.839         4.64e- 7
## # … with 1 more variable: `P value (FDR)` <dbl>

While the Warning messages mentioned Chi-squared, the p-values were actually calculated using Fisher’s Exact test (see more in epitools::tab2by2.test()).

Note: small number of observations for ICD-10 code G04, G03, G65:

contingency_df %>% 
  filter(grepl('G04|G03|G65', full_icd)) %>% 
  datatable()

Country enrichment table

The full table with all ICD codes and their corresponding enrichment can be browsed interactively below:

library(DT)
fish_tab <- fish_obs_exp  %>% 
  left_join(contingency_df, by = c('full_icd', 'time')) %>% 
  select(full_icd, time, Observed, Expected, over_sev,
         estimate, lestimate, lci, p_value,
         `P value (Holm)`, `P value (FDR)`) %>% 
  arrange(desc(over_sev)) %>% 
  rename('ICD' = 'full_icd',
         'Observed - Expected' = 'over_sev',
         'Enrichment' = 'estimate',
         'Log2(enrichment)' = 'lestimate',
         '95% Confidence interval' = 'lci', 
         'P value (raw)' = 'p_value')

fish_tab %>% 
  datatable(rownames = FALSE, filter = 'top') %>% 
  formatRound(c('Observed', 'Expected', 'Observed - Expected',
                'Enrichment', 'Log2(enrichment)'), 1) %>%
  formatSignif(c('P value (raw)', 'P value (Holm)', 'P value (FDR)'), 3) %>% 
  {.}
fish_tab %>% 
  write_csv('results/icd10_fish_tab.csv')

fish_obs_exp %>% 
  filter(`P value (FDR)` < 0.05) %>% 
  arrange(time) %>% 
  mutate(lestimate = round(lestimate, 2),
         drr = round((estimate - 1)*100, 0),
         lower_drr = (lower - 1)*100,
         upper_drr = (upper - 1)*100,
         ci_drr = paste0('(', round(lower_drr, 0), ', ', 
                     round(upper_drr, 0), ')'),
         p_fdr = format(`P value (FDR)`, digits = 2)) %>% 
  select(time, full_icd, lestimate, drr, ci_drr, p_fdr) %>% 
  write_csv('results/icd10_signi.csv')

A positive value of LOE indicates a higher proportion of severe patients with that ICD code compared to non-severe patients. A LOE value of 1 represents a one-fold enrichment (i.e., observed number of severe patients is twice as much as expected). We found an excess of severe patients with the following ICD codes:

  • Other disorders of the brain (G93): 63 more severe patients than expected, LOE = [], 95% CI []
  • Other and unspecified myopathies (G72): 38 more severe patients than expected, LOE = [], 95% CI []
  • Myositis (M60): 6 more severe patients than expected, LOE = [], 95% CI []

Compute enrichment from proportion comparisons

filtered_obs_exp <- contingency_df %>% 
  left_join(fish_obs_exp, by = c('full_icd', 'time')) %>% 
  mutate(
    distance_to_null = case_when(
      lower > 1 ~ lower - 1,
      TRUE ~ upper - 2
    ),
    presentation = case_when(
      lower > 1 & `P value (FDR)` < 0.05 ~ '#d46780', 
      upper < 1 & `P value (FDR)` < 0.05 ~ '#798234',
      TRUE ~ 'grey20'
    )) %>% 
  {.}
# plot_obs_exp_before <- plot_obs_exp_left
# sorted_icds <- rev(as.character(filtered_obs_exp$full_icd))
# sorted_icds <- filtered_obs_exp %>%
#   select(full_icd, time, estimate) %>% 
#   pivot_wider(names_from = time, values_from = estimate) %>% 
#   mutate(diff_est = log2(`After admission`) - log2(`Before admission`),
#          diff_est = ifelse(is.na(diff_est), -Inf, diff_est),
#          full_icd = as.character(full_icd)) %>% 
#   arrange(diff_est) %>% 
#   pull(full_icd)
sorted_icds <- filtered_obs_exp %>% 
  filter(time == 'After admission') %>% 
  arrange(Expected) %>% 
  pull(full_icd)

plot_obs_exp <- filtered_obs_exp %>%
  mutate(lestimate = log2(estimate),
         llower = log2(lower), 
         lupper = log2(upper)) %>% 
  select(full_icd, time, lestimate, llower, lupper, presentation, over_sev, Observed, Expected) %>% 
  pivot_longer(- c(full_icd, time, presentation, over_sev), names_to = 'type') %>% 
  mutate(subtype = ifelse(type == 'Expected' | type == 'Observed', 
                          'Sqrt(number of honorees)', 
                          'Log2 enrichment, 95% CI')) %>% 
  pivot_wider(names_from = type) %>% 
  mutate(presentation = as.factor(presentation),
         full_icd = fct_relevel(full_icd, sorted_icds))

plot_obs_exp_right <- plot_obs_exp %>% filter(subtype == 'Sqrt(number of honorees)') 
plot_obs_exp_left <- plot_obs_exp %>% filter(subtype != 'Sqrt(number of honorees)') 
enrichment_plot_before <- plot_enrich(
  plot_obs_exp_left %>% filter(time == 'Before admission'),
  plot_obs_exp_right %>% filter(time == 'Before admission') %>% 
    slice(match(sorted_icds, full_icd)),
  nudge = 2.5)
## Warning: Vectorized input to `element_text()` is not officially supported.
## Results may be unexpected or may change in future versions of ggplot2.
enrichment_plot_after <- plot_enrich(
  plot_obs_exp_left %>% filter(time == 'After admission'),
  plot_obs_exp_right %>% filter(time == 'After admission') %>% slice(match(sorted_icds, full_icd)),
  nudge = 4)
## Warning: Vectorized input to `element_text()` is not officially supported.
## Results may be unexpected or may change in future versions of ggplot2.
enrichment_plot <- cowplot::plot_grid(
  enrichment_plot_before,
  enrichment_plot_after,
  ncol = 1, hjust = -0.05,
  labels = c('A. Before admission', 'B. After admission')
)
enrichment_plot

ggsave('figs/icd_severe_after.png', enrichment_plot_after, width = 9.5, height = 3.5)
ggsave('figs/tiffs/fig_4.tiff', enrichment_plot_after, width = 9.5, height = 3.5, dpi = 300)

Figure caption: Each ICD code’s log2 enrichment (LOE) and its 95% confidence interval (left), and the absolute difference between observed (triangle) and expected (circle) number of severe patients (right) before admission. Positive value of LOE indicates a higher proportion of severe patients with that ICD code compared to non-severe patients. Neurological ICD codes are ordered based on the number of severe patients. Difference has been rounded.

plot_enrich_both <- plot_obs_exp %>% 
  ggplot(aes(y = fct_relevel(full_icd, sorted_icds))) +
  geom_vline(aes(xintercept = 0), linetype = 2) +
  ggstance::geom_pointrangeh(
    aes(x = lestimate,
        xmin = llower,
        xmax = lupper,
        color = time),
    position = position_dodge(width = 0.3), 
    stroke = 0.1, fatten = 3, size = 0.7  
  ) +
  labs(y = NULL, x = bquote(Log[2] ~ 'enrichment, 95% CI')) +
  theme(
    legend.position = 'bottom',
    legend.background = element_blank(),
    axis.title = element_text(size = 9),
    plot.margin = margin(5.5, 2, 5.5, 5.5, unit = 'pt')
  ) +
  scale_color_carto_d(palette = 4, direction = -1) +
  NULL
plot_enrich_both
## Warning: Removed 42 rows containing missing values (geom_pointrangeh).

ggsave('figs/icd_severe.png', plot_enrich_both, width = 9, height = 5)
## Warning: Removed 42 rows containing missing values (geom_pointrangeh).
LS0tCnRpdGxlOiAiTmV1cm8gYW5hbHlzZXMsIElDRC0xMCBjb2RlIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBsdW1lbgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBoaWdobGlnaHQ6IHRhbmdvCmtuaXQ6IChmdW5jdGlvbihpbnB1dEZpbGUsIGVuY29kaW5nKSB7CiAgcm1hcmtkb3duOjpyZW5kZXIoaW5wdXRGaWxlLCBlbmNvZGluZyA9IGVuY29kaW5nLCBvdXRwdXRfZGlyID0gImRvY3MiKSB9KQotLS0KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyY2FydG9jb2xvcikKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoRFQpCmxpYnJhcnkoY293cGxvdCkKCmxvYWQoJ2RhdGEvcHJvY2Vzc2VkLWRhdGEuUmRhdGEnKQp0aGVtZV9zZXQodGhlbWVfYncoKSArIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkpCnNvdXJjZSgndXRpbHMuUicpCmNvdW50cmllcyA8LSBjKCJGcmFuY2UiLCAiR2VybWFueSIsICJTcGFpbiIsICJJdGFseSIsICJTaW5nYXBvcmUiLCAiVVNBIiApCmBgYAoKIyMgUHJldmFsZW5jZSBhbmFseXNpcwoKQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzOgoKYGBge3IgZmlnLmhlaWdodD05fQpjb2RlX3ByZXZhbGVuY2UgPC0gZGlhZ19pY2RfMTAgJT4lIAogIGdyb3VwX2J5KHNpdGVpZCwgQ291bnRyeSwgdGltZSwgaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCBmdWxsX2ljZCkgJT4lIAogICMgc3VtbWFyaXNlKHBhdHNfdGltZV9pY2Rfc2l0ZSA9IHN1bShudW1fcGF0aWVudHNfaWNkKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lIAogICMgbGVmdF9qb2luKGRlbW9fYW5hLCBieSA9ICdzaXRlaWQnKSAlPiUgCiAgbXV0YXRlKHBlcmNlbnRfcGF0c19zaXRlID0gbnVtX3BhdGllbnRzX2ljZC9udW1fcGF0aWVudHNfYWxsLAogICAgICAgICBzaXRlaWQgPSBhcy5mYWN0b3Ioc2l0ZWlkKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeSwgdGltZSwgaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCBmdWxsX2ljZCkgJT4lIAogIG11dGF0ZShwZXJjZW50X3BhdHNfY291bnRyeSA9IHN1bShudW1fcGF0aWVudHNfaWNkKS9hbGxfcGF0c19jb3VudHJ5KSAlPiUgCiAgdW5ncm91cCgpCmBgYAoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9OH0KZGlmZl9jb2RlIDwtIGNvZGVfcHJldmFsZW5jZSAlPiUgCiAgc2VsZWN0KGxvY2F0aW9uID0gc2l0ZWlkLCBpY2QsIGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWAsIHRpbWUsIHBlcmNlbnRfcGF0c19zaXRlKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHRpbWUsIHZhbHVlc19mcm9tID0gcGVyY2VudF9wYXRzX3NpdGUsIAogICAgICAgICAgICAgIGlkX2NvbHMgPSBjKGxvY2F0aW9uLCBpY2QsIGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCAKICAgICAgICAgICAgICB2YWx1ZXNfZmlsbCA9IGxpc3QocGVyY2VudF9wYXRzX3NpdGUgPSAwKSkgJT4lIAogIG11dGF0ZShwZXJjZW50X2RpZmYgPSBgQWZ0ZXIgYWRtaXNzaW9uYCAtIGBCZWZvcmUgYWRtaXNzaW9uYCwKICAgICAgICAgbG9jX3R5cGUgPSAnU2l0ZScsIGxvY2F0aW9uID0gYXMuZmFjdG9yKHRvbG93ZXIobG9jYXRpb24pKSkKCmRpZmZfY29kZV9jb3VudHJ5IDwtIGNvZGVfcHJldmFsZW5jZSAlPiUgCiAgc2VsZWN0KGxvY2F0aW9uID0gQ291bnRyeSwgaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCB0aW1lLCBwZXJjZW50X3BhdHNfY291bnRyeSkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB0aW1lLCB2YWx1ZXNfZnJvbSA9IHBlcmNlbnRfcGF0c19jb3VudHJ5LCAKICAgICAgICAgICAgICBpZF9jb2xzID0gYyhsb2NhdGlvbiwgaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgKSwgCiAgICAgICAgICAgICAgdmFsdWVzX2ZpbGwgPSBsaXN0KHBlcmNlbnRfcGF0c19jb3VudHJ5ID0gMCkpICU+JSAKICBtdXRhdGUocGVyY2VudF9kaWZmID0gYEFmdGVyIGFkbWlzc2lvbmAgLSBgQmVmb3JlIGFkbWlzc2lvbmAsCiAgICAgICAgIGxvY2F0aW9uID0gZmN0X3JlbGV2ZWwobG9jYXRpb24sIGNvdW50cmllcyksCiAgICAgICAgIGxvY190eXBlID0gJ0NvdW50cnknKQoKZGlmZl9jb2RlX2xvYyA8LSBkaWZmX2NvZGUgJT4lIAogIGJpbmRfcm93cyhkaWZmX2NvZGVfY291bnRyeSkKCmRpZmZfaGVhdCA8LSBkaWZmX2NvZGVfbG9jICU+JSAKICBnZ3Bsb3QoYWVzKHkgPSBmY3RfcmV2KGljZCksIHggPSBsb2NhdGlvbiwgZmlsbCA9IHBlcmNlbnRfZGlmZikpICsKICBnZW9tX3RpbGUoKSArCiAgbGFicyh5ID0gTlVMTCwgeCA9IE5VTEwsIGZpbGwgPSAnQWZ0ZXIgLSBCZWZvcmUnKSArCiAgc2NhbGVfeF9kaXNjcmV0ZSgpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIoIzAwOTM5MiwjMzliMTg1LCM5Y2NiODYsI2U5ZTI5YywjZWViNDc5LCNlODg0NzEsI2NmNTk3ZQogICAgbG93ID0gJyM3OTgyMzQnLAogICAgaGlnaCA9ICcjY2Y1OTdlJywKICAgIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKwogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoYE5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5YCksIAogICAgICAgICAgICAgY29scyA9IHZhcnMoZmN0X3Jldihsb2NfdHlwZSkpLCBzcGFjZSA9ICdmcmVlJywgc2NhbGVzID0gJ2ZyZWUnKSArCiAgaGVhdF90aGVtZV9ib3R0b20oKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMSwwLjIsMC4yLDMpLCAibGluZXMiKSkgKwogIE5VTEwKZGlmZl9oZWF0CgoKIyBnZ3NhdmUoJ2ZpZ3MvaWNkX2RpZmZfaGVhdF9zaXRlbWFwLnBuZycsIGRpZmZfaGVhdF9zaXRlLCBoZWlnaHQgPSA0LCB3aWR0aCA9IDgpCmBgYAoKYGBge3IgZmlnLmhlaWdodD05fQpiZWZvcmVfY29kZV9wcmV2IDwtIGRpZmZfY29kZV9sb2MgJT4lIAogIGdncGxvdChhZXMoeSA9IGljZCwgeCA9IGxvY2F0aW9uLCBmaWxsID0gYEJlZm9yZSBhZG1pc3Npb25gKSkgKwogIGdlb21fdGlsZSgpICsKICBsYWJzKHkgPSBOVUxMLCB4ID0gTlVMTCwgZmlsbCA9ICdQYXRpZW50ICUgcGVyCnNpdGUvY291bnRyeScpICsKICAjZjZkMmE5LCNmNWI3OGUsI2YxOWM3YywjZWE4MTcxLCNkZDY4NmMsI2NhNTI2OCwjYjEzZjY0CiAgc2NhbGVfZmlsbF9ncmFkaWVudCgKICAgIGxvdyA9ICcjZDFlZWVhJywgaGlnaCA9ICcjMmE1Njc0JywKICAgIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSwKICAgIGxpbWl0cyA9IGMoMCwgbWF4KGRpZmZfY29kZV9sb2MkYEFmdGVyIGFkbWlzc2lvbmApKSwKICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoZmlsbCA9ICJ3aGl0ZSIpKSkgKwogIHNjYWxlX3hfZGlzY3JldGUoKSArIAogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoYE5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5YCksIAogICAgICAgICAgICAgY29scyA9IHZhcnMoZmN0X3Jldihsb2NfdHlwZSkpLCBzcGFjZSA9ICdmcmVlJywgc2NhbGUgPSAnZnJlZScpICsKICBoZWF0X3RoZW1lX3RvcCgpICsKICBOVUxMCgphZnRlcl9jb2RlX3ByZXYgPC0gZGlmZl9jb2RlX2xvYyAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gaWNkLCB4ID0gbG9jYXRpb24sIGZpbGwgPSBgQWZ0ZXIgYWRtaXNzaW9uYCkpICsKICBnZW9tX3RpbGUoKSArCiAgbGFicyh5ID0gTlVMTCwgeCA9IE5VTEwsIGZpbGwgPSAnUGF0aWVudCAlIHBlcgpzaXRlL2NvdW50cnknKSArCiAgc2NhbGVfeF9kaXNjcmV0ZSgpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAnI2QxZWVlYScsIGhpZ2ggPSAnIzJhNTY3NCcsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIG1heChkaWZmX2NvZGVfbG9jJGBBZnRlciBhZG1pc3Npb25gKSkpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCAKICAgICAgICAgICAgIGNvbHMgPSB2YXJzKGZjdF9yZXYobG9jX3R5cGUpKSwgc3BhY2UgPSAnZnJlZScsIHNjYWxlID0gJ2ZyZWUnKSArCiAgaGVhdF90aGVtZV9ib3R0b20oKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCkpICsKICBOVUxMCgpoZWF0cyA8LSBjb3dwbG90OjpwbG90X2dyaWQoYmVmb3JlX2NvZGVfcHJldiwgYWZ0ZXJfY29kZV9wcmV2LCBuY29sID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygxLCAxLjE1KSwKICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ0EuIEJlZm9yZSBhZG1pc3Npb24nLCAnQi4gQWZ0ZXIgYWRtaXNzaW9uJykpCmhlYXRzCmdnc2F2ZSgnZmlncy9pY2RfaGVhdG1hcC5wbmcnLCBoZWF0cywgaGVpZ2h0ID0gMTAsIHdpZHRoID0gOSkKZ2dzYXZlKCdmaWdzL3RpZmZzL2VmaWdfMS50aWZmJywgaGVhdHMsIGhlaWdodCA9IDEwLCB3aWR0aCA9IDksIGRwaSA9IDMwMCkKYGBgCgoKIyMjIEh5cG90aGVzaXMgdGVzdGluZwoKYGBge3J9Cm15X3QgPC0gZnVuY3Rpb24oaWNkaSl7CiAgZGlmZl9pY2QgPC0gZGlmZl9jb2RlICU+JSBmaWx0ZXIoaWNkID09IGljZGkpCiAgZGF0YS5mcmFtZSgKICAgIGljZCA9IGljZGksCiAgICB0LnRlc3QoZGlmZl9pY2QkcGVyY2VudF9kaWZmKSAlPiUgCiAgICBicm9vbTo6dGlkeSgpCiAgKQp9CnByZXZhbGVuY2Vfc3RhdHMgPC0gdW5pcXVlKGRpZmZfY29kZSRpY2QpICU+JSAKICBsYXBwbHkobXlfdCkgJT4lIAogIGJpbmRfcm93cygpCnByZXZhbGVuY2Vfc3RhdHMkcF92YWx1ZV9ob2xtIDwtICBwLmFkanVzdChwcmV2YWxlbmNlX3N0YXRzJHAudmFsdWUsICdob2xtJykKcHJldmFsZW5jZV9zdGF0cyRwX3ZhbHVlX2JoIDwtIHAuYWRqdXN0KHByZXZhbGVuY2Vfc3RhdHMkcC52YWx1ZSwgJ0JIJykKcHJldmFsZW5jZV9zdGF0cyAlPiUgCiAgZmlsdGVyKHBfdmFsdWVfYmggPCAwLjA1KQoKYGBgCgpgYGB7cn0KY2hlY2sgPSBkaWZmX2NvZGUgJT4lIAogIGZpbHRlcihsb2NfdHlwZSA9PSAnU2l0ZScsIGljZCA9PSAnRzkzJykgJT4lIAogIGxlZnRfam9pbihkaWFnX2ljZF8xMCAlPiUgc2VsZWN0KHNpdGVpZCwgQ291bnRyeSkgJT4lIGRpc3RpbmN0KCkgJT4lIG11dGF0ZShzaXRlaWQgPSB0b2xvd2VyKHNpdGVpZCkpLAogICAgICAgICAgICBieSA9IGMoJ2xvY2F0aW9uJyA9ICdzaXRlaWQnKSkKY2hlY2sgJT4lIGdyb3VwX2J5KENvdW50cnkpICU+JSAKICBzdW1tYXJpc2UoY291bnRyeV9kaWZmID0gbWVhbihwZXJjZW50X2RpZmYpLCAuZ3JvdXBzID0gJ2Ryb3AnKQpgYGAKCiMjIyBQcmV2YWxlbmNlIGNoYW5nZSB0YWJsZQoKYGBge3J9CnByZXZhbGVuY2Vfc3RhdHMgJT4lIAogIGFycmFuZ2UoZGVzYyhlc3RpbWF0ZSkpICU+JSAKICBtdXRhdGUoY2kgPSBwYXN0ZTAoJygnLCByb3VuZChjb25mLmxvdyoxMDAsIDIpLCAnJSwgJywgCiAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGNvbmYuaGlnaCoxMDAsIDIpLCAnJSknKSkgJT4lIAogIHNlbGVjdCgtIGMocGFyYW1ldGVyLCBtZXRob2QsIGFsdGVybmF0aXZlLCBjb25mLmxvdywgY29uZi5oaWdoKSkgJT4lIAogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLCBmaWx0ZXIgPSAndG9wJykgJT4lIAogIGZvcm1hdFJvdW5kKGMoJ3N0YXRpc3RpYycpLCAxKSAlPiUKICBmb3JtYXRQZXJjZW50YWdlKCdlc3RpbWF0ZScsIDEpICU+JSAKICBmb3JtYXRTaWduaWYoYygncC52YWx1ZScsICdwX3ZhbHVlX2hvbG0nLCAncF92YWx1ZV9iaCcpLCAzKSAlPiUKICB7Ln0KCnByZXZhbGVuY2Vfc3RhdHMgJT4lIAogIHdyaXRlX2NzdigncmVzdWx0cy9pY2QxMF9wcmV2YWxlbmNlX3N0YXRzLmNzdicpCmBgYAoKCiMjIyBDb21wdXRlIGNvbmZpZGVuY2UgaW52ZXJ2YWwgCm9mIHRoZSBtZWFuIHByb3BvcnRpb24gb2YgcGF0aWVudHMgZGlhZ25vc2VkIHdpdGggZWFjaCBJQ0QKCmBgYHtyfQphbHBoYV90aHJlc2hvbGQgPC0gcW5vcm0oMC45NzUpCgpjaV9wcmV2YWxlbmNlIDwtIGNvZGVfcHJldmFsZW5jZSAlPiUgCiAgZ3JvdXBfYnkodGltZSwgZnVsbF9pY2QpICU+JQogIGFkZF9jb3VudCgpICU+JQogIHN1bW1hcmlzZSgKICAgIG1lYW5fcHJvcCA9IG1lYW4ocGVyY2VudF9wYXRzX3NpdGUsIG5hLnJtID0gVCksCiAgICBzZF9wcm9iID0gc2QocGVyY2VudF9wYXRzX3NpdGUsIG5hLnJtID0gVCksCiAgICBuID0gbWVhbihuKSwKICAgIG1lX3Byb3AgPSBhbHBoYV90aHJlc2hvbGQgKiBzZF9wcm9iIC8gc3FydChuKQogICkgJT4lCiAgdW5ncm91cCgpCmBgYAoKCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0KaWNkX3RpbWUgPC0gZGlhZ19pY2RfMTAgJT4lIAogIG11dGF0ZSh0aW1lID0gZmN0X3JlbGV2ZWwodGltZSwgYygnQWZ0ZXIgYWRtaXNzaW9uJywgJ0JlZm9yZSBhZG1pc3Npb24nKSkpICU+JSAKICBncm91cF9ieShmdWxsX2ljZCwgdGltZSkgJT4lIAogIHN1bW1hcmlzZShwYXRzX2ljZF90aW1lID0gc3VtKG51bV9wYXRpZW50c19pY2QsIG5hLnJtID0gVCksIC5ncm91cHMgPSAnZHJvcCcpIAojIHNvcnRlZF9pY2RzIDwtIGljZF90aW1lICU+JSAKIyAgIGZpbHRlcih0aW1lID09ICdBZnRlciBhZG1pc3Npb24nKSAlPiUgCiMgICBhcnJhbmdlKHBhdHNfaWNkX3RpbWUpICU+JSAKIyAgIHB1bGwoZnVsbF9pY2QpCiMgc29ydGVkX2ljZHMgPC0gY29kZV9wcmV2YWxlbmNlICU+JSAKIyAgIHVuZ3JvdXAoKSAlPiUgCiMgICBzZWxlY3QodGltZSwgcGVyY2VudF9wYXRzX3NpdGUsIGZ1bGxfaWNkLCBzaXRlaWQpICU+JSAKIyAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB0aW1lLCB2YWx1ZXNfZnJvbSA9IHBlcmNlbnRfcGF0c19zaXRlLCAKIyAgICAgICAgICAgICAgIHZhbHVlc19maWxsID0gbGlzdChwZXJjZW50X3BhdHNfc2l0ZSA9IDApKSAlPiUgCiMgICBncm91cF9ieShmdWxsX2ljZCkgJT4lIAojICAgc3VtbWFyaXNlKGFmdGVyID0gbWVhbihgQWZ0ZXIgYWRtaXNzaW9uYCwgbmEucm0gPSBUKSwKIyAgICAgICAgICBiZWZvcmUgPSBtZWFuKGBCZWZvcmUgYWRtaXNzaW9uYCwgbmEucm0gPSBUKSwKIyAgICAgICAgICBkaWZmX3ByZXYgPSBhZnRlciAtIGJlZm9yZSwKIyAgICAgICAgICAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUgCiMgICBhcnJhbmdlKGRpZmZfcHJldikgJT4lIAojICAgcHVsbChmdWxsX2ljZCkKc29ydGVkX2ljZHMgPC0gY29kZV9wcmV2YWxlbmNlICU+JSAKICBkaXN0aW5jdChmdWxsX2ljZCwgaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgKSAlPiUgCiAgYXJyYW5nZShkZXNjKGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCBkZXNjKGljZCkpICU+JSAKICBwdWxsKGZ1bGxfaWNkKQoKdG90YWxfaWNkIDwtIGljZF90aW1lICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBwYXRzX2ljZF90aW1lLCB5ID0gZmN0X3JlbGV2ZWwoZnVsbF9pY2QsIHNvcnRlZF9pY2RzKSwgZmlsbCA9IHRpbWUpKSArCiAgc2NhbGVfZmlsbF9jYXJ0b19kKHBhbGV0dGUgPSA0LCBndWlkZSA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9ICdkb2RnZScpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX3hfcmV2ZXJzZShleHBhbmQgPSBleHBhbnNpb24oYWRkID0gYygwLDApKSkgKwogIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gTlVMTCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4zLCAwLjE1KSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoNCwgJ21tJyksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4odCA9IDEsIHIgPSAwLjUsIGwgPSAwLjUsIHVuaXQgPSAnbGluZXMnKSkgKyAKICBsYWJzKHggPSAnVG90YWwgbnVtYmVyIG9mIHBhdGllbnRzIGF0IGFsbCBzaXRlcycsIHkgPSBOVUxMKQoKcGVyY2VudF9pY2QgPC0gCiAgY2lfcHJldmFsZW5jZSAlPiUgCiAgZ2dwbG90KGFlcyhncm91cCA9IHRpbWUpKSArCiAgZ2dzdGFuY2U6Omdlb21fcG9pbnRyYW5nZWgoCiAgICBhZXMoeCA9IG1lYW5fcHJvcCwKICAgICAgICB5ID0gZmN0X3JlbGV2ZWwoZnVsbF9pY2QsIHNvcnRlZF9pY2RzKSwKICAgICAgICB4bWluID0gbWVhbl9wcm9wIC0gbWVfcHJvcCwKICAgICAgICB4bWF4ID0gbWVhbl9wcm9wICsgbWVfcHJvcCwKICAgICAgICBjb2xvciA9IHRpbWUpLAogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuMyksIAogICAgc3Ryb2tlID0gMC4xLCBmYXR0ZW4gPSAzLCBzaXplID0gMC43ICAKICApICsKICBzY2FsZV9jb2xvcl9jYXJ0b19kKHBhbGV0dGUgPSA0LCBndWlkZSA9IE5VTEwpICsKICB0aGVtZV9taW5pbWFsKCkgKyAgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjc1LCAwLjEpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbih0ID0gMSwgdW5pdCA9ICdsaW5lcycpKSArIAogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oYWRkID0gYygwLCAwLjAzKSksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKwogIGxhYnMoeCA9ICdQcm9wb3J0aW9uIG9mIHBhdGllbnRzIGVhY2ggc2l0ZScsIHkgPSBOVUxMKQoKaWNkX3ByZXZhbGVuY2VfcGxvdHMgPC0gY293cGxvdDo6cGxvdF9ncmlkKHRvdGFsX2ljZCwgcGVyY2VudF9pY2QsIG5jb2wgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsX3dpZHRocyA9IGMoMSwgMykpCmljZF9wcmV2YWxlbmNlX3Bsb3RzCiMgZ2dzYXZlKCdmaWdzL2ljZF9wcmV2YWxlbmNlLnBuZycsIGljZF9wcmV2YWxlbmNlX3Bsb3RzLCBoZWlnaHQgPSA1LCB3aWR0aCA9IDEwKQpgYGAKCmBgYHtyfQpwbG90X2dyaWQoZGlmZl9oZWF0LCBpY2RfcHJldmFsZW5jZV9wbG90cywgcmVsX2hlaWdodHMgPSBjKDEsIDAuODUpLAogICAgICAgICAgbmNvbCA9IDEsIGxhYmVscyA9ICdBVVRPJykgJT4lCiAgZ2dzYXZlKCdmaWdzL2ljZF9wcmV2YWxlbmNlX0FCLnBuZycsIC4sIGhlaWdodCA9IDgsIHdpZHRoID0gOS43KQoKcGxvdF9ncmlkKGRpZmZfaGVhdCwgaWNkX3ByZXZhbGVuY2VfcGxvdHMsIHJlbF9oZWlnaHRzID0gYygxLCAwLjg1KSwKICAgICAgICAgIG5jb2wgPSAxLCBsYWJlbHMgPSAnQVVUTycpICU+JQpnZ3NhdmUoJ2ZpZ3MvdGlmZnMvZmlnXzMudGlmZicsIC4sIGhlaWdodCA9IDgsIHdpZHRoID0gOS43LCBkcGkgPSAzMDApCgpgYGAKCgoKCmBgYHtyfQpkaWZmX2NvZGUgJT4lIAogIGZpbHRlcihpY2QgPT0gJ1I0MScpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGNvdW50KHBlcmNlbnRfZGlmZiA+IDApCgpkaWZmX2NvZGUgJT4lIAogIGZpbHRlcihpY2QgPT0gJ0c5MycpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGNvdW50KHBlcmNlbnRfZGlmZiA+IDApCgpkaWZmX2NvZGUgJT4lIAogIGZpbHRlcihpY2QgPT0gJ1I0MicpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGNvdW50KHBlcmNlbnRfZGlmZiA+IDApCmBgYAoKCiMgU2V2ZXJpdHkgZGVzY3JpcHRpdmUgc3RhdGlzdGljcwoKV2hhdCBJQ0QgY29kZSBoYXMgdGhlIG1vc3QgbnVtYmVyIHNldmVyZSBwYXRpZW50cz8KU2l0ZXMgd2l0aCBtb3JlIHNldmVyZSBwYXRpZW50cz8KCmBgYHtyIGZpZy5oZWlnaHQ9OX0Kc2V2ZXJlX2NvZGUgPC0gZGlhZ19pY2RfMTAgJT4lIAogICMgZmlsdGVyKHNpdGVpZCAhPSAnU0lURTMwOScpICU+JQogIG11dGF0ZShwZXJjZW50X3NldmVyZV9zaXRlID0gbnVtX3BhdGllbnRzX2V2ZXJfc2V2ZXJlX2ljZDEvbnVtX3BhdGllbnRzX2ljZCwKICAgICAgICAgc2l0ZWlkID0gYXMuZmFjdG9yKHNpdGVpZCkpCgpzZXZlcmVfYmVmX2hlYXQgPC0gc2V2ZXJlX2NvZGUgJT4lIAogIGZpbHRlcih0aW1lID09ICdCZWZvcmUgYWRtaXNzaW9uJykgJT4lIAogIGdncGxvdChhZXMoeSA9IGljZCwgeCA9IHNpdGVpZCwgZmlsbCA9IHBlcmNlbnRfc2V2ZXJlX3NpdGUpKSArCiAgZ2VvbV90aWxlKCkgKwogIGxhYnMoeSA9IE5VTEwsIHggPSBOVUxMLCBmaWxsID0gJ1NldmVyZSAlIHBlciBJQ0QnKSArCiAgIzNkNTk0MSwjNzc4ODY4LCNiNWI5OTEsI2Y2ZWRiZCwjZWRiYjhhLCNkZThhNWEsI2NhNTYyYwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoCiAgICAjIGxvdyA9ICcjZjZkMmE5JywgaGlnaCA9ICcjYjEzZjY0JywKICAgIGxvdyA9ICd3aGl0ZScsIGhpZ2ggPSAnI2NhNTYyYycsCiAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGZpbGwgPSAid2hpdGUiKSksCiAgICApICsKICBzY2FsZV94X2Rpc2NyZXRlKCkgKyAKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCBzcGFjZSA9ICdmcmVlJywgc2NhbGUgPSAnZnJlZScpICsKICBoZWF0X3RoZW1lX3RvcCgpICsKICBOVUxMCgpzZXZlcmVfYWZ0X2hlYXQgPC0gc2V2ZXJlX2NvZGUgJT4lIAogIGZpbHRlcih0aW1lID09ICdBZnRlciBhZG1pc3Npb24nKSAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gaWNkLCB4ID0gc2l0ZWlkLCBmaWxsID0gcGVyY2VudF9zZXZlcmVfc2l0ZSkpICsKICBnZW9tX3RpbGUoKSArCiAgbGFicyh5ID0gTlVMTCwgeCA9IE5VTEwsIGZpbGwgPSAnU2V2ZXJlICUgcGVyIElDRCcpICsKICBzY2FsZV94X2Rpc2NyZXRlKCkgKyAKICAjM2Q1OTQxLCM3Nzg4NjgsI2I1Yjk5MSwjZjZlZGJkLCNlZGJiOGEsI2RlOGE1YSwjY2E1NjJjCiAgc2NhbGVfZmlsbF9ncmFkaWVudCgKICAgIGxvdyA9ICd3aGl0ZScsIGhpZ2ggPSAnI2NhNTYyYycsCiAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCBzcGFjZSA9ICdmcmVlJywgc2NhbGUgPSAnZnJlZScpICsKICBoZWF0X3RoZW1lX2JvdHRvbSgpICsKICBOVUxMCgpzZXZlcmVfaGVhdHMgPC0gY293cGxvdDo6cGxvdF9ncmlkKHNldmVyZV9iZWZfaGVhdCwgc2V2ZXJlX2FmdF9oZWF0LCBuY29sID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygxLCAxLjE1KSwKICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ0EuIEJlZm9yZSBhZG1pc3Npb24nLCAnQi4gQWZ0ZXIgYWRtaXNzaW9uJykpCnNldmVyZV9oZWF0cwpnZ3NhdmUoJ2ZpZ3MvaWNkX3NldmVyZV9oZWF0bWFwLnBuZycsIHNldmVyZV9oZWF0cywgaGVpZ2h0ID0gMTAsIHdpZHRoID0gNykKCmBgYAoKCgojIFNldmVyaXR5IGVucmljaG1lbnQgYW5hbHlzaXMKCiMjIE51bGwgaHlwb3RoZXNpcyAKRm9yIGVhY2ggSUNEIGNvZGUsIHRoZSBwcm9wb3J0aW9uIG9mIHNldmVyZSBwYXRpZW50cyB3aG8gd2VyZSBkaWFnbm9zZWQgd2l0aCB0aGF0IElDRCBjb2RlIGlzIHNpbWlsYXIgdG8gdGhlIHByb3BvcnRpb24gb2YgbmV2ZXItc2V2ZXJlIHBhdGllbnRzIHdobyB3ZXJlIGRpYWdub3NlZCB3aXRoIHRoYXQgc2FtZSBJQ0QgY29kZS4KCkZvciB0aGUgc2FrZSBvZiBzaW1wbGljaXR5IGluIHRoaXMgbm90ZWJvb2ssIHdlJ3JlIGdvaW5nIHRvIGRlbm90ZSBwYXRpZW50cyB3aG9zZSBzeW1wdG9tcyBoYXZlIGJlZW4gY2F0ZWdvcml6ZWQgYXMgc2V2ZXJlIChiYXNlZCBvbiByZXNwaXJhdG9yeSBzdGF0dXMgKy8tIHJlcXVpcmluZyBJQ1UpIGF0IGxlYXN0IG9uY2UgYXMgKnNldmVyZSBwYXRpZW50cyosIGFuZCBwYXRpZW50cyB3aG9zZSBzeW1wdG9tcyBoYXZlIE5FVkVSIGJlZW4gY2F0ZWdvcml6ZWQgYXMgc2V2ZXJlIGFzICpub24tc2V2ZXJlIHBhdGllbnRzKi4KCkZvciBlYWNoIElDRCBjb2RlLCB3ZSBjb21wdXRlZCB0aGUgZXhwZWN0ZWQgbnVtYmVyIG9mICpzZXZlcmUgcGF0aWVudHMqIGJ5IG11bHRpcGx5aW5nIHRoZSBwcm9wb3J0aW9uIG9mICpub24tc2V2ZXJlIHBhdGllbnRzKiB3aG8gd2VyZSBkaWFnbm9zZWQgd2l0aCB0aGF0IGNvZGUgd2l0aCB0aGUgdG90YWwgbnVtYmVyIG9mICpzZXZlcmUgcGF0aWVudHMqLgpXZSBwZXJmb3JtZWQgYW4gZW5yaWNobWVudCBhbmFseXNpcyB0byBleGFtaW5lIHRoZSBkaWZmZXJlbmNlIG9mICpzZXZlcmUgcGF0aWVudHMqIHByb3BvcnRpb25zIGFjcm9zcyBJQ0QgY29kZXMuCldlIGNhbGN1bGF0ZWQgZWFjaCBJQ0QgY29kZSdzIGVucmljaG1lbnQgYnkgZGl2aWRpbmcgdGhlIG9ic2VydmVkIHByb3BvcnRpb24gb2YgaG9ub3JlZXMgYnkgdGhlIGV4cGVjdGVkIHByb3BvcnRpb24gb2YgaG9ub3JlZXMgYW5kIHJlcG9ydGVkIGEgdmFsdWUgb2YgbG9nMiBlbnJpY2htZW50IChMT0UpIGFuZCBpdHMgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzLgpUaGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgdGhlIExPRSB3YXMgZXN0aW1hdGVkIHVzaW5nIHRoZSBQb2lzc29uIG1vZGVsIG1ldGhvZCBbQGlzYm46OTc4MDg0OTM5NDQ0N10uCgpgYGB7cn0KY29udGluZ2VuY3lfY29udGluZW50IDwtIGRpYWdfaWNkXzEwICU+JQogIG11dGF0ZShjb250aW5lbnQgPSBpZmVsc2UoQ291bnRyeSA9PSAnVVNBJywgJ1VTJywgJ05vblVTJykpICU+JSAKICBncm91cF9ieShmdWxsX2ljZCwgdGltZSwgY29udGluZW50KSAlPiUKICBzdW1tYXJpc2UoYWNyb3NzKGNvbnRhaW5zKCdzZXZlcmUnKSwgLmZucyA9IHN1bSwgbmEucm0gPSBUKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgbXV0YXRlKE9ic2VydmVkID0gbnVtX3BhdGllbnRzX2V2ZXJfc2V2ZXJlX2ljZDEsCiAgICAgICAgIG51bV9ub25fc2V2ZXJlID0gbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QxICsgbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QwLAogICAgICAgICBudW1fc2V2ZXJlID0gbnVtX3BhdGllbnRzX2V2ZXJfc2V2ZXJlX2ljZDEgKyBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMCwKICAgICAgICAgRXhwZWN0ZWQgPSBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDEvbnVtX25vbl9zZXZlcmUqbnVtX3NldmVyZSwKICAgICAgICAgb3Zlcl9zZXYgPSBPYnNlcnZlZCAtIEV4cGVjdGVkKQoKaW5kX2Zpc2ggPC0gY29udGluZ2VuY3lfY29udGluZW50ICU+JQogIGZpbHRlcighaXMubmEoRXhwZWN0ZWQpLCBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDAgPj0gMCkgJT4lCiAgc2VsZWN0KGZ1bGxfaWNkLCB0aW1lLCBjb250aW5lbnQsCiAgICAgICAgIG51bV9wYXRpZW50c19uZXZlcl9zZXZlcmVfaWNkMCwKICAgICAgICAgbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QxLAogICAgICAgICBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMCwKICAgICAgICAgbnVtX3BhdGllbnRzX2V2ZXJfc2V2ZXJlX2ljZDEpICU+JQogIGdyb3VwX2J5KGZ1bGxfaWNkLCB0aW1lLCBjb250aW5lbnQpICU+JQogIG5lc3QoKSAlPiUKICBtdXRhdGUoZmlzaCA9IG1hcChkYXRhLCBteV9maXNoKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtZGF0YSkgJT4lCiAgdW5uZXN0KGNvbHMgPSBjKGZpc2gpKSAlPiUgCiAgbXV0YXRlKHVwcGVyID0gaWZlbHNlKGlzLm5hKHVwcGVyKSwgSW5mLCB1cHBlcikpICU+JSAKICBtdXRhdGUobGNpID0gcGFzdGUwKCcoJywgcm91bmQobG9nMihsb3dlciksIDEpLCAnLCAnLCAKICAgICAgICAgICAgICAgICAgICAgcm91bmQobG9nMih1cHBlciksIDEpLCAnKScpLAogICAgICAgICBsZXN0aW1hdGUgPSBsb2cyKGVzdGltYXRlKSkKaW5kX2Zpc2gkYFAgdmFsdWUgKEhvbG0pYCA8LSAgcC5hZGp1c3QoaW5kX2Zpc2gkcF92YWx1ZSwgJ2hvbG0nKQppbmRfZmlzaCRgUCB2YWx1ZSAoRkRSKWAgPC0gcC5hZGp1c3QoaW5kX2Zpc2gkcF92YWx1ZSwgJ0JIJykKCm1hZGF0YSA8LSBjb250aW5nZW5jeV9jb250aW5lbnQgJT4lIAogIGxlZnRfam9pbihpbmRfZmlzaCwgYnkgPSBjKCdmdWxsX2ljZCcsICdjb250aW5lbnQnLCAndGltZScpKSAlPiUgCiAgZmlsdGVyKHRpbWUgPT0gJ0FmdGVyIGFkbWlzc2lvbicpICU+JSAKICBtdXRhdGUoCiAgICBkaXN0YW5jZV90b19udWxsID0gY2FzZV93aGVuKAogICAgICBsb3dlciA+IDEgfiBsb3dlciAtIDEsCiAgICAgIFRSVUUgfiB1cHBlciAtIDIKICAgICksCiAgICBwcmVzZW50YXRpb24gPSBjYXNlX3doZW4oCiAgICAgIGxvd2VyID4gMSAmIGBQIHZhbHVlIChGRFIpYCA8IDAuMDUgfiAnI2Q0Njc4MCcsIAogICAgICB1cHBlciA8IDEgJiBgUCB2YWx1ZSAoRkRSKWAgPCAwLjA1IH4gJyM3OTgyMzQnLAogICAgICBUUlVFIH4gJ2dyZXkyMCcKICAgICksCiAgICB1cHBlciA9IGlmZWxzZShpcy5uYSh1cHBlciksIEluZiwgdXBwZXIpKQogIApwbG90X29ic19leHAgPC0gbWFkYXRhICU+JQogIG11dGF0ZShsZXN0aW1hdGUgPSBsb2cyKGVzdGltYXRlKSwKICAgICAgICAgbGxvd2VyID0gbG9nMihsb3dlciksIAogICAgICAgICBsdXBwZXIgPSBsb2cyKHVwcGVyKSkgJT4lIAogIHNlbGVjdChmdWxsX2ljZCwgY29udGluZW50LCBsZXN0aW1hdGUsIGxsb3dlciwgbHVwcGVyLCBwcmVzZW50YXRpb24sIG92ZXJfc2V2LCBPYnNlcnZlZCwgRXhwZWN0ZWQpICU+JSAKICBwaXZvdF9sb25nZXIoLSBjKGZ1bGxfaWNkLCBjb250aW5lbnQsIHByZXNlbnRhdGlvbiwgb3Zlcl9zZXYpLCBuYW1lc190byA9ICd0eXBlJykgJT4lIAogIG11dGF0ZShzdWJ0eXBlID0gaWZlbHNlKHR5cGUgPT0gJ0V4cGVjdGVkJyB8IHR5cGUgPT0gJ09ic2VydmVkJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NxcnQobnVtYmVyIG9mIGhvbm9yZWVzKScsIAogICAgICAgICAgICAgICAgICAgICAgICAgICdMb2cyIGVucmljaG1lbnQsIDk1JSBDSScpKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHR5cGUsIHZhbHVlc19mcm9tID0gdmFsdWUpICU+JSAKICBtdXRhdGUocHJlc2VudGF0aW9uID0gYXMuZmFjdG9yKHByZXNlbnRhdGlvbiksCiAgICAgICAgIGZ1bGxfaWNkID0gZmN0X3JlbGV2ZWwoZnVsbF9pY2QsIHNvcnRlZF9pY2RzKSkKCnNvcnRlZF9pY2RzIDwtIG1hZGF0YSAlPiUgCiAgZmlsdGVyKHRpbWUgPT0gJ0FmdGVyIGFkbWlzc2lvbicpICU+JSAKICBncm91cF9ieShmdWxsX2ljZCkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF9leHBlY3RlZCA9IHN1bShFeHBlY3RlZCksIC5ncm91cHMgPSAnZHJvcCcpICU+JSAKICBhcnJhbmdlKHRvdGFsX2V4cGVjdGVkKSAlPiUgCiAgcHVsbChmdWxsX2ljZCkKCnBsb3Rfb2JzX2V4cF9yaWdodCA8LSBwbG90X29ic19leHAgJT4lIGZpbHRlcihzdWJ0eXBlID09ICdTcXJ0KG51bWJlciBvZiBob25vcmVlcyknKSAKcGxvdF9vYnNfZXhwX2xlZnQgPC0gcGxvdF9vYnNfZXhwICU+JSBmaWx0ZXIoc3VidHlwZSAhPSAnU3FydChudW1iZXIgb2YgaG9ub3JlZXMpJykgCgplbnJpY2htZW50X3Bsb3RfdXMgPC0gcGxvdF9lbnJpY2goCiAgcGxvdF9vYnNfZXhwX2xlZnQgJT4lIGZpbHRlcihjb250aW5lbnQgPT0gJ1VTJyksCiAgcGxvdF9vYnNfZXhwX3JpZ2h0ICU+JSBmaWx0ZXIoY29udGluZW50ID09ICdVUycpICU+JSAKICAgIHNsaWNlKG1hdGNoKHNvcnRlZF9pY2RzLCBmdWxsX2ljZCkpLAogIG51ZGdlID0gMi41KQoKZW5yaWNobWVudF9wbG90X25vbnVzIDwtIHBsb3RfZW5yaWNoKAogIHBsb3Rfb2JzX2V4cF9sZWZ0ICU+JSBmaWx0ZXIoY29udGluZW50ID09ICdOb25VUycpLAogIHBsb3Rfb2JzX2V4cF9yaWdodCAlPiUgZmlsdGVyKGNvbnRpbmVudCA9PSAnTm9uVVMnKSAlPiUgc2xpY2UobWF0Y2goc29ydGVkX2ljZHMsIGZ1bGxfaWNkKSksCiAgbnVkZ2UgPSAyLjUpCgplbnJpY2htZW50X3Bsb3RfY29udCA8LSBjb3dwbG90OjpwbG90X2dyaWQoCiAgZW5yaWNobWVudF9wbG90X3VzLAogIGVucmljaG1lbnRfcGxvdF9ub251cywKICBuY29sID0gMSwgaGp1c3QgPSAtMC4wNSwKICBsYWJlbHMgPSBjKCdBLiBVUyBzaXRlcycsICdCLiBOb24tVVMgc2l0ZXMnKQopCgoKCmdnc2F2ZSgnZmlncy9pY2Rfc2V2ZXJlX2FmdGVyX3VzX25vbnVzLnBuZycsIGVucmljaG1lbnRfcGxvdF9jb250LCB3aWR0aCA9IDkuNSwgaGVpZ2h0ID0gNykKZ2dzYXZlKCdmaWdzL3RpZmZzL2VmaWdfMi50aWZmJywgZW5yaWNobWVudF9wbG90X2NvbnQsIHdpZHRoID0gOS41LCBoZWlnaHQgPSA3LCBkcGkgPSAzMDApCgptYWRhdGEgJT4lIAogIHdyaXRlX2NzdigncmVzdWx0cy9pY2QxMF9maXNoX3RhYl91c19ub251cy5jc3YnKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIGV2YWw9RkFMU0V9CiMgTWV0YWFuYWx5c2lzCiMgbGlicmFyeShtZXRhKQoKIyBpbmRfZmlzaCA8LSBkaWFnX2ljZF8xMCAlPiUgCiMgICBncm91cF9ieShmdWxsX2ljZCwgdGltZSwgc2l0ZWlkKSAlPiUgCiMgICBzdW1tYXJpc2UoYWNyb3NzKGNvbnRhaW5zKCdzZXZlcmUnKSwgLmZucyA9IHN1bSwgbmEucm0gPSBUKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lIAojICAgbXV0YXRlKE9ic2VydmVkID0gbnVtX3BhdGllbnRzX2V2ZXJfc2V2ZXJlX2ljZDEsIAojICAgICAgICAgIG51bV9ub25fc2V2ZXJlID0gbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QxICsgbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QwLAojICAgICAgICAgIG51bV9zZXZlcmUgPSBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMSArIG51bV9wYXRpZW50c19ldmVyX3NldmVyZV9pY2QwLAojICAgICAgICAgIEV4cGVjdGVkID0gbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QxL251bV9ub25fc2V2ZXJlKm51bV9zZXZlcmUsCiMgICAgICAgICAgb3Zlcl9zZXYgPSBPYnNlcnZlZCAtIEV4cGVjdGVkKSAlPiUKIyAgIGZpbHRlcighaXMubmEoRXhwZWN0ZWQpLCBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDAgPj0gMCkgJT4lIAojICAgc2VsZWN0KGZ1bGxfaWNkLCB0aW1lLCBzaXRlaWQsCiMgICAgICAgICAgbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QwLCAKIyAgICAgICAgICBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDEsCiMgICAgICAgICAgbnVtX3BhdGllbnRzX2V2ZXJfc2V2ZXJlX2ljZDAsIAojICAgICAgICAgIG51bV9wYXRpZW50c19ldmVyX3NldmVyZV9pY2QxKSAlPiUgCiMgICBncm91cF9ieShmdWxsX2ljZCwgdGltZSwgc2l0ZWlkKSAlPiUgCiMgICBuZXN0KCkgJT4lIAojICAgbXV0YXRlKGZpc2ggPSBtYXAoZGF0YSwgbXlfZmlzaCkpICU+JSAKIyAgIGRwbHlyOjpzZWxlY3QoLWRhdGEpICU+JSAKIyAgIHVubmVzdChjb2xzID0gYyhmaXNoKSkgCiMgCiMgbWFkYXRhIDwtIGluZF9maXNoICU+JSAKIyAgIG11dGF0ZSh1cHBlciA9IGlmZWxzZShpcy5uYSh1cHBlciksIEluZiwgdXBwZXIpLAojICAgICAgICAgIGxlc3RpbWF0ZSA9IGxvZzIoZXN0aW1hdGUpLAojICAgICAgICAgIGxsb3dlciA9IGxvZzIobG93ZXIpLAojICAgICAgICAgIGx1cHBlciA9IGxvZzIodXBwZXIpLAojICAgICAgICAgIG1hc2UgPSAobHVwcGVyIC0gbGxvd2VyKS8oMiphbHBoYV90aHJlc2hvbGQpKQoKIyBtZXRhX3JlcyA8LSBkYXRhLmZyYW1lKCkKIyBmb3IgKHRpbWVpIGluIGMoJ0JlZm9yZSBhZG1pc3Npb24nLCAnQWZ0ZXIgYWRtaXNzaW9uJykpewojICAgZm9yIChpY2RpIGluIHVuaXF1ZShtYWRhdGEkZnVsbF9pY2QpKXsKIyAgICAgbSA8LSBtYWRhdGEgJT4lIAojICAgICAgIGZpbHRlcihmdWxsX2ljZCA9PSBpY2RpLCB0aW1lID09IHRpbWVpLCAhaXMubmEoZXN0aW1hdGUpKSAlPiUgCiMgICAgICAgbWV0YWdlbihsZXN0aW1hdGUsCiMgICAgICAgICAgICAgICBtYXNlLAojICAgICAgICAgICAgICAgZGF0YSA9IC4sCiMgICAgICAgICAgICAgICBtZXRob2QudGF1ID0gIlNKIiwKIyAgICAgICAgICAgICAgIHNtID0gJ1JSJywKIyAgICAgICAgICAgICAgIHN0dWRsYWI9c2l0ZWlkKQojICAgICAKIyAgICAgbWV0YV9yZXMgPC0gbWV0YV9yZXMgJT4lIAojICAgICAgIHJiaW5kKGRhdGEuZnJhbWUobl9zaXRlcyA9IG5yb3cobSRkYXRhKSwgZnVsbF9pY2QgPSBpY2RpLCB0aW1lID0gdGltZWksCiMgICAgICAgICAgICAgICAgICAgICAgICBlc3RpbWF0ZSA9IG0kVEUuZml4ZWQsIHB2YWwgPSBtJHB2YWwuZml4ZWQsCiMgICAgICAgICAgICAgICAgICAgICAgICBpMiA9IG0kSTIsIGxpMiA9IG0kbG93ZXIuSTIsIHVpMiA9IG0kdXBwZXIuSTIsIAojICAgICAgICAgICAgICAgICAgICAgICAgUSA9IG0kUSwgcFEgPSBtJHB2YWwuUSkpCiMgICB9CiMgfQojIAojIG1ldGFfcmVzJHB2YWxfZmRyIDwtICBwLmFkanVzdChtZXRhX3JlcyRwdmFsLCAnQkgnKQojIHdyaXRlX2NzdihtZXRhX3JlcywgJ3Jlc3VsdHMvbWV0YWFuYWx5c2lzX3Jlc3VsdHMuY3N2JykKYGBgCgojIyBCZWZvcmUgYW5kIGFmdGVyIGFkbWlzc2lvbgoKYGBge3J9CmNvbnRpbmdlbmN5X2RmIDwtIGRpYWdfaWNkXzEwICU+JSAKICAjIGZpbHRlcih0aW1lID09ICdCZWZvcmUgYWRtaXNzaW9uJykgJT4lCiAgc2VsZWN0KC0gYyhudW1fcGF0aWVudHNfYWxsLCBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmUsIG51bV9wYXRpZW50c19uZXZlcl9zZXZlcmUpKSAlPiUgCiAgZ3JvdXBfYnkoZnVsbF9pY2QsIHRpbWUpICU+JSAKICBzdW1tYXJpc2UoYWNyb3NzKGNvbnRhaW5zKCdzZXZlcmUnKSwgLmZucyA9IHN1bSwgbmEucm0gPSBUKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lIAogIG11dGF0ZShPYnNlcnZlZCA9IG51bV9wYXRpZW50c19ldmVyX3NldmVyZV9pY2QxLCAKICAgICAgICAgbnVtX25vbl9zZXZlcmUgPSBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDEgKyBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDAsCiAgICAgICAgIG51bV9zZXZlcmUgPSBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMSArIG51bV9wYXRpZW50c19ldmVyX3NldmVyZV9pY2QwLAogICAgICAgICBFeHBlY3RlZCA9IG51bV9wYXRpZW50c19uZXZlcl9zZXZlcmVfaWNkMS9udW1fbm9uX3NldmVyZSpudW1fc2V2ZXJlLAogICAgICAgICBvdmVyX3NldiA9IE9ic2VydmVkIC0gRXhwZWN0ZWQpCgpuZXN0ZWRfb2JzX2V4cCA8LSBjb250aW5nZW5jeV9kZiAlPiUKICBzZWxlY3QoZnVsbF9pY2QsIAogICAgICAgICB0aW1lLAogICAgICAgICBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDAsIAogICAgICAgICBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDEsIAogICAgICAgICBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMCwgCiAgICAgICAgIG51bV9wYXRpZW50c19ldmVyX3NldmVyZV9pY2QxKSAlPiUgCiAgZ3JvdXBfYnkoZnVsbF9pY2QsIHRpbWUpICU+JSAKICBuZXN0KCkgCgpmaXNoX29ic19leHAgPC0gbmVzdGVkX29ic19leHAgJT4lIAogIG11dGF0ZShmaXNoID0gbWFwKGRhdGEsIG15X2Zpc2gpKSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtZGF0YSkgJT4lIAogIHVubmVzdChjb2xzID0gYyhmaXNoKSkgJT4lIAogIG11dGF0ZSh1cHBlciA9IGlmZWxzZShpcy5uYSh1cHBlciksIEluZiwgdXBwZXIpKSAlPiUgCiAgbXV0YXRlKGxjaSA9IHBhc3RlMCgnKCcsIHJvdW5kKGxvZzIobG93ZXIpLCAxKSwgJywgJywgCiAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGxvZzIodXBwZXIpLCAxKSwgJyknKSwKICAgICAgICAgbGVzdGltYXRlID0gbG9nMihlc3RpbWF0ZSkpCmZpc2hfb2JzX2V4cCRgUCB2YWx1ZSAoSG9sbSlgIDwtICBwLmFkanVzdChmaXNoX29ic19leHAkcF92YWx1ZSwgJ2hvbG0nKQpmaXNoX29ic19leHAkYFAgdmFsdWUgKEZEUilgIDwtIHAuYWRqdXN0KGZpc2hfb2JzX2V4cCRwX3ZhbHVlLCAnQkgnKQpmaXNoX29ic19leHAgJT4lIAogIGZpbHRlcihgUCB2YWx1ZSAoRkRSKWAgPCAwLjA1LAogICAgICAgICB0aW1lID09ICdBZnRlciBhZG1pc3Npb24nKQpgYGAKCldoaWxlIHRoZSBXYXJuaW5nIG1lc3NhZ2VzIG1lbnRpb25lZCBDaGktc3F1YXJlZCwgdGhlIHAtdmFsdWVzIHdlcmUgYWN0dWFsbHkgY2FsY3VsYXRlZCB1c2luZyBGaXNoZXIncyBFeGFjdCB0ZXN0IChzZWUgbW9yZSBpbiBgZXBpdG9vbHM6OnRhYjJieTIudGVzdCgpYCkuCgpOb3RlOiBzbWFsbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZvciBJQ0QtMTAgY29kZSBHMDQsIEcwMywgRzY1OgoKYGBge3J9CmNvbnRpbmdlbmN5X2RmICU+JSAKICBmaWx0ZXIoZ3JlcGwoJ0cwNHxHMDN8RzY1JywgZnVsbF9pY2QpKSAlPiUgCiAgZGF0YXRhYmxlKCkKYGBgCgoKIyMjIENvdW50cnkgZW5yaWNobWVudCB0YWJsZSB7I2VucmljaG1lbnRfdGFifQoKVGhlIGZ1bGwgdGFibGUgd2l0aCBhbGwgSUNEIGNvZGVzIGFuZCB0aGVpciBjb3JyZXNwb25kaW5nIGVucmljaG1lbnQgY2FuIGJlIGJyb3dzZWQgaW50ZXJhY3RpdmVseSBiZWxvdzoKYGBge3J9CmxpYnJhcnkoRFQpCmZpc2hfdGFiIDwtIGZpc2hfb2JzX2V4cCAgJT4lIAogIGxlZnRfam9pbihjb250aW5nZW5jeV9kZiwgYnkgPSBjKCdmdWxsX2ljZCcsICd0aW1lJykpICU+JSAKICBzZWxlY3QoZnVsbF9pY2QsIHRpbWUsIE9ic2VydmVkLCBFeHBlY3RlZCwgb3Zlcl9zZXYsCiAgICAgICAgIGVzdGltYXRlLCBsZXN0aW1hdGUsIGxjaSwgcF92YWx1ZSwKICAgICAgICAgYFAgdmFsdWUgKEhvbG0pYCwgYFAgdmFsdWUgKEZEUilgKSAlPiUgCiAgYXJyYW5nZShkZXNjKG92ZXJfc2V2KSkgJT4lIAogIHJlbmFtZSgnSUNEJyA9ICdmdWxsX2ljZCcsCiAgICAgICAgICdPYnNlcnZlZCAtIEV4cGVjdGVkJyA9ICdvdmVyX3NldicsCiAgICAgICAgICdFbnJpY2htZW50JyA9ICdlc3RpbWF0ZScsCiAgICAgICAgICdMb2cyKGVucmljaG1lbnQpJyA9ICdsZXN0aW1hdGUnLAogICAgICAgICAnOTUlIENvbmZpZGVuY2UgaW50ZXJ2YWwnID0gJ2xjaScsIAogICAgICAgICAnUCB2YWx1ZSAocmF3KScgPSAncF92YWx1ZScpCgpmaXNoX3RhYiAlPiUgCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsIGZpbHRlciA9ICd0b3AnKSAlPiUgCiAgZm9ybWF0Um91bmQoYygnT2JzZXJ2ZWQnLCAnRXhwZWN0ZWQnLCAnT2JzZXJ2ZWQgLSBFeHBlY3RlZCcsCiAgICAgICAgICAgICAgICAnRW5yaWNobWVudCcsICdMb2cyKGVucmljaG1lbnQpJyksIDEpICU+JQogIGZvcm1hdFNpZ25pZihjKCdQIHZhbHVlIChyYXcpJywgJ1AgdmFsdWUgKEhvbG0pJywgJ1AgdmFsdWUgKEZEUiknKSwgMykgJT4lIAogIHsufQoKZmlzaF90YWIgJT4lIAogIHdyaXRlX2NzdigncmVzdWx0cy9pY2QxMF9maXNoX3RhYi5jc3YnKQoKZmlzaF9vYnNfZXhwICU+JSAKICBmaWx0ZXIoYFAgdmFsdWUgKEZEUilgIDwgMC4wNSkgJT4lIAogIGFycmFuZ2UodGltZSkgJT4lIAogIG11dGF0ZShsZXN0aW1hdGUgPSByb3VuZChsZXN0aW1hdGUsIDIpLAogICAgICAgICBkcnIgPSByb3VuZCgoZXN0aW1hdGUgLSAxKSoxMDAsIDApLAogICAgICAgICBsb3dlcl9kcnIgPSAobG93ZXIgLSAxKSoxMDAsCiAgICAgICAgIHVwcGVyX2RyciA9ICh1cHBlciAtIDEpKjEwMCwKICAgICAgICAgY2lfZHJyID0gcGFzdGUwKCcoJywgcm91bmQobG93ZXJfZHJyLCAwKSwgJywgJywgCiAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHVwcGVyX2RyciwgMCksICcpJyksCiAgICAgICAgIHBfZmRyID0gZm9ybWF0KGBQIHZhbHVlIChGRFIpYCwgZGlnaXRzID0gMikpICU+JSAKICBzZWxlY3QodGltZSwgZnVsbF9pY2QsIGxlc3RpbWF0ZSwgZHJyLCBjaV9kcnIsIHBfZmRyKSAlPiUgCiAgd3JpdGVfY3N2KCdyZXN1bHRzL2ljZDEwX3NpZ25pLmNzdicpCmBgYAoKQSBwb3NpdGl2ZSB2YWx1ZSBvZiBMT0UgaW5kaWNhdGVzIGEgaGlnaGVyIHByb3BvcnRpb24gb2YgKnNldmVyZSBwYXRpZW50cyogd2l0aCB0aGF0IElDRCBjb2RlIGNvbXBhcmVkIHRvICpub24tc2V2ZXJlIHBhdGllbnRzKi4KQSBMT0UgdmFsdWUgb2YgMSByZXByZXNlbnRzIGEgb25lLWZvbGQgZW5yaWNobWVudCAoaS5lLiwgb2JzZXJ2ZWQgbnVtYmVyIG9mICpzZXZlcmUgcGF0aWVudHMqIGlzIHR3aWNlIGFzIG11Y2ggYXMgZXhwZWN0ZWQpLgpXZSBmb3VuZCBhbiBleGNlc3Mgb2YgKnNldmVyZSBwYXRpZW50cyogd2l0aCB0aGUgZm9sbG93aW5nIElDRCBjb2RlczoKCi0gT3RoZXIgZGlzb3JkZXJzIG9mIHRoZSBicmFpbiAoRzkzKTogNjMgbW9yZSAqc2V2ZXJlIHBhdGllbnRzKiB0aGFuIGV4cGVjdGVkLCBMT0UgPSBbXSwgOTUlIENJIFtdCi0gT3RoZXIgYW5kIHVuc3BlY2lmaWVkIG15b3BhdGhpZXMgKEc3Mik6IDM4IG1vcmUgKnNldmVyZSBwYXRpZW50cyogdGhhbiBleHBlY3RlZCwgTE9FID0gW10sIDk1JSBDSSBbXQotIE15b3NpdGlzIChNNjApOiA2IG1vcmUgKnNldmVyZSBwYXRpZW50cyogdGhhbiBleHBlY3RlZCwgTE9FID0gW10sIDk1JSBDSSBbXQoKIyMjIENvbXB1dGUgZW5yaWNobWVudCBmcm9tIHByb3BvcnRpb24gY29tcGFyaXNvbnMKCmBgYHtyIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSAzLjV9CmZpbHRlcmVkX29ic19leHAgPC0gY29udGluZ2VuY3lfZGYgJT4lIAogIGxlZnRfam9pbihmaXNoX29ic19leHAsIGJ5ID0gYygnZnVsbF9pY2QnLCAndGltZScpKSAlPiUgCiAgbXV0YXRlKAogICAgZGlzdGFuY2VfdG9fbnVsbCA9IGNhc2Vfd2hlbigKICAgICAgbG93ZXIgPiAxIH4gbG93ZXIgLSAxLAogICAgICBUUlVFIH4gdXBwZXIgLSAyCiAgICApLAogICAgcHJlc2VudGF0aW9uID0gY2FzZV93aGVuKAogICAgICBsb3dlciA+IDEgJiBgUCB2YWx1ZSAoRkRSKWAgPCAwLjA1IH4gJyNkNDY3ODAnLCAKICAgICAgdXBwZXIgPCAxICYgYFAgdmFsdWUgKEZEUilgIDwgMC4wNSB+ICcjNzk4MjM0JywKICAgICAgVFJVRSB+ICdncmV5MjAnCiAgICApKSAlPiUgCiAgey59CmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KIyBwbG90X29ic19leHBfYmVmb3JlIDwtIHBsb3Rfb2JzX2V4cF9sZWZ0CiMgc29ydGVkX2ljZHMgPC0gcmV2KGFzLmNoYXJhY3RlcihmaWx0ZXJlZF9vYnNfZXhwJGZ1bGxfaWNkKSkKIyBzb3J0ZWRfaWNkcyA8LSBmaWx0ZXJlZF9vYnNfZXhwICU+JQojICAgc2VsZWN0KGZ1bGxfaWNkLCB0aW1lLCBlc3RpbWF0ZSkgJT4lIAojICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHRpbWUsIHZhbHVlc19mcm9tID0gZXN0aW1hdGUpICU+JSAKIyAgIG11dGF0ZShkaWZmX2VzdCA9IGxvZzIoYEFmdGVyIGFkbWlzc2lvbmApIC0gbG9nMihgQmVmb3JlIGFkbWlzc2lvbmApLAojICAgICAgICAgIGRpZmZfZXN0ID0gaWZlbHNlKGlzLm5hKGRpZmZfZXN0KSwgLUluZiwgZGlmZl9lc3QpLAojICAgICAgICAgIGZ1bGxfaWNkID0gYXMuY2hhcmFjdGVyKGZ1bGxfaWNkKSkgJT4lIAojICAgYXJyYW5nZShkaWZmX2VzdCkgJT4lIAojICAgcHVsbChmdWxsX2ljZCkKc29ydGVkX2ljZHMgPC0gZmlsdGVyZWRfb2JzX2V4cCAlPiUgCiAgZmlsdGVyKHRpbWUgPT0gJ0FmdGVyIGFkbWlzc2lvbicpICU+JSAKICBhcnJhbmdlKEV4cGVjdGVkKSAlPiUgCiAgcHVsbChmdWxsX2ljZCkKCnBsb3Rfb2JzX2V4cCA8LSBmaWx0ZXJlZF9vYnNfZXhwICU+JQogIG11dGF0ZShsZXN0aW1hdGUgPSBsb2cyKGVzdGltYXRlKSwKICAgICAgICAgbGxvd2VyID0gbG9nMihsb3dlciksIAogICAgICAgICBsdXBwZXIgPSBsb2cyKHVwcGVyKSkgJT4lIAogIHNlbGVjdChmdWxsX2ljZCwgdGltZSwgbGVzdGltYXRlLCBsbG93ZXIsIGx1cHBlciwgcHJlc2VudGF0aW9uLCBvdmVyX3NldiwgT2JzZXJ2ZWQsIEV4cGVjdGVkKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKC0gYyhmdWxsX2ljZCwgdGltZSwgcHJlc2VudGF0aW9uLCBvdmVyX3NldiksIG5hbWVzX3RvID0gJ3R5cGUnKSAlPiUgCiAgbXV0YXRlKHN1YnR5cGUgPSBpZmVsc2UodHlwZSA9PSAnRXhwZWN0ZWQnIHwgdHlwZSA9PSAnT2JzZXJ2ZWQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAnU3FydChudW1iZXIgb2YgaG9ub3JlZXMpJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ0xvZzIgZW5yaWNobWVudCwgOTUlIENJJykpICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdHlwZSkgJT4lIAogIG11dGF0ZShwcmVzZW50YXRpb24gPSBhcy5mYWN0b3IocHJlc2VudGF0aW9uKSwKICAgICAgICAgZnVsbF9pY2QgPSBmY3RfcmVsZXZlbChmdWxsX2ljZCwgc29ydGVkX2ljZHMpKQoKcGxvdF9vYnNfZXhwX3JpZ2h0IDwtIHBsb3Rfb2JzX2V4cCAlPiUgZmlsdGVyKHN1YnR5cGUgPT0gJ1NxcnQobnVtYmVyIG9mIGhvbm9yZWVzKScpIApwbG90X29ic19leHBfbGVmdCA8LSBwbG90X29ic19leHAgJT4lIGZpbHRlcihzdWJ0eXBlICE9ICdTcXJ0KG51bWJlciBvZiBob25vcmVlcyknKSAKYGBgCgoKYGBge3IgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQplbnJpY2htZW50X3Bsb3RfYmVmb3JlIDwtIHBsb3RfZW5yaWNoKAogIHBsb3Rfb2JzX2V4cF9sZWZ0ICU+JSBmaWx0ZXIodGltZSA9PSAnQmVmb3JlIGFkbWlzc2lvbicpLAogIHBsb3Rfb2JzX2V4cF9yaWdodCAlPiUgZmlsdGVyKHRpbWUgPT0gJ0JlZm9yZSBhZG1pc3Npb24nKSAlPiUgCiAgICBzbGljZShtYXRjaChzb3J0ZWRfaWNkcywgZnVsbF9pY2QpKSwKICBudWRnZSA9IDIuNSkKCmVucmljaG1lbnRfcGxvdF9hZnRlciA8LSBwbG90X2VucmljaCgKICBwbG90X29ic19leHBfbGVmdCAlPiUgZmlsdGVyKHRpbWUgPT0gJ0FmdGVyIGFkbWlzc2lvbicpLAogIHBsb3Rfb2JzX2V4cF9yaWdodCAlPiUgZmlsdGVyKHRpbWUgPT0gJ0FmdGVyIGFkbWlzc2lvbicpICU+JSBzbGljZShtYXRjaChzb3J0ZWRfaWNkcywgZnVsbF9pY2QpKSwKICBudWRnZSA9IDQpCgplbnJpY2htZW50X3Bsb3QgPC0gY293cGxvdDo6cGxvdF9ncmlkKAogIGVucmljaG1lbnRfcGxvdF9iZWZvcmUsCiAgZW5yaWNobWVudF9wbG90X2FmdGVyLAogIG5jb2wgPSAxLCBoanVzdCA9IC0wLjA1LAogIGxhYmVscyA9IGMoJ0EuIEJlZm9yZSBhZG1pc3Npb24nLCAnQi4gQWZ0ZXIgYWRtaXNzaW9uJykKKQplbnJpY2htZW50X3Bsb3QKZ2dzYXZlKCdmaWdzL2ljZF9zZXZlcmVfYWZ0ZXIucG5nJywgZW5yaWNobWVudF9wbG90X2FmdGVyLCB3aWR0aCA9IDkuNSwgaGVpZ2h0ID0gMy41KQpnZ3NhdmUoJ2ZpZ3MvdGlmZnMvZmlnXzQudGlmZicsIGVucmljaG1lbnRfcGxvdF9hZnRlciwgd2lkdGggPSA5LjUsIGhlaWdodCA9IDMuNSwgZHBpID0gMzAwKQpgYGAKCgpGaWd1cmUgY2FwdGlvbjogCkVhY2ggSUNEIGNvZGUncyBsb2cyIGVucmljaG1lbnQgKExPRSkgYW5kIGl0cyA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCAobGVmdCksIGFuZCB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSBiZXR3ZWVuIG9ic2VydmVkICh0cmlhbmdsZSkgYW5kIGV4cGVjdGVkIChjaXJjbGUpIG51bWJlciBvZiAqc2V2ZXJlIHBhdGllbnRzKiAocmlnaHQpIGJlZm9yZSBhZG1pc3Npb24uClBvc2l0aXZlIHZhbHVlIG9mIExPRSBpbmRpY2F0ZXMgYSBoaWdoZXIgcHJvcG9ydGlvbiBvZiAqc2V2ZXJlIHBhdGllbnRzKiB3aXRoIHRoYXQgSUNEIGNvZGUgY29tcGFyZWQgdG8gKm5vbi1zZXZlcmUgcGF0aWVudHMqLgpOZXVyb2xvZ2ljYWwgSUNEIGNvZGVzIGFyZSBvcmRlcmVkIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgKnNldmVyZSBwYXRpZW50cyouCkRpZmZlcmVuY2UgaGFzIGJlZW4gcm91bmRlZC4KCmBgYHtyIGZpZy53aWR0aD05fQpwbG90X2VucmljaF9ib3RoIDwtIHBsb3Rfb2JzX2V4cCAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gZmN0X3JlbGV2ZWwoZnVsbF9pY2QsIHNvcnRlZF9pY2RzKSkpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksIGxpbmV0eXBlID0gMikgKwogIGdnc3RhbmNlOjpnZW9tX3BvaW50cmFuZ2VoKAogICAgYWVzKHggPSBsZXN0aW1hdGUsCiAgICAgICAgeG1pbiA9IGxsb3dlciwKICAgICAgICB4bWF4ID0gbHVwcGVyLAogICAgICAgIGNvbG9yID0gdGltZSksCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC4zKSwgCiAgICBzdHJva2UgPSAwLjEsIGZhdHRlbiA9IDMsIHNpemUgPSAwLjcgIAogICkgKwogIGxhYnMoeSA9IE5VTEwsIHggPSBicXVvdGUoTG9nWzJdIH4gJ2VucmljaG1lbnQsIDk1JSBDSScpKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbig1LjUsIDIsIDUuNSwgNS41LCB1bml0ID0gJ3B0JykKICApICsKICBzY2FsZV9jb2xvcl9jYXJ0b19kKHBhbGV0dGUgPSA0LCBkaXJlY3Rpb24gPSAtMSkgKwogIE5VTEwKcGxvdF9lbnJpY2hfYm90aApnZ3NhdmUoJ2ZpZ3MvaWNkX3NldmVyZS5wbmcnLCBwbG90X2VucmljaF9ib3RoLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmBgYAoKCg==