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

load('data/processed-data.Rdata')
theme_set(theme_bw() + theme(legend.title = element_blank()))
source('utils.R')

Prevalence analysis

Calculate percentages:

code_prevalence <- diag_icd_9 %>% 
  mutate(time = fct_relevel(time, c('After admission', 'Before admission'))) %>% 
  select(- contains('ICD10_')) %>% 
  group_by(siteid, time, icd, `Neurological Disease Category`, full_icd) %>% 
  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 = 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`,
         loc_type = 'Country')

diff_code_loc <- diff_code %>% 
  bind_rows(diff_code_country)

diff_heat <- diff_code_loc %>% 
  ggplot(aes(y = icd, x = location, fill = percent_diff)) +
  geom_tile() +
  labs(y = NULL, x = NULL, fill = 'After - Before') +
  scale_x_discrete(drop = F) + 
  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', scale = 'free') +
  heat_theme_bottom() +
  theme(panel.spacing.x = unit(10, "points"),
        plot.margin = unit(c(1,8,0.5,12), "lines"),
        axis.text.x = element_text(hjust = 1)) +
  # theme() +
  NULL
diff_heat

 # ggsave('figs/icd9_diff_heatmap.png', diff_heat, height = 4, width = 8)

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)
##  [1] icd          estimate     statistic    p.value      parameter   
##  [6] conf.low     conf.high    method       alternative  p_value_holm
## [11] p_value_bh  
## <0 rows> (or 0-length row.names)

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/icd9_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)
icd9_time <- diag_icd_9 %>% 
  mutate(time = fct_relevel(time, c('After admission', 'Before admission'))) %>% 
  group_by(full_icd, time) %>% 
  summarise(pats_icd9_time = sum(num_patients_icd, na.rm = T), .groups = 'drop') 

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)

total_icd <- icd9_time %>% 
  ggplot(aes(x = pats_icd9_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),
        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),
        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)

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

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

plot_grid(diff_heat, icd9_prevalence_plots, rel_heights = c(1, 0.85),
          ncol = 1, labels = 'AUTO') %>%
  ggsave('figs/tiffs/efig_4.tiff', ., height = 8, width = 9.7, dpi = 300)
before_code_prev <- diff_code_loc %>% 
  ggplot(aes(y = icd, x = location, fill = `Before admission`)) +
  geom_tile() +
  labs(y = NULL, x = NULL) +
  #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(drop = F) + 
  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(drop = F) + 
  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/icd9_heatmap.png', heats, height = 10, width = 7)
ggsave('figs/tiffs/efig_3.tiff', heats, height = 10, width = 7, dpi = 300)
diff_code %>% 
  filter(icd == 'R41') %>% 
  ungroup() %>% 
  count(percent_diff > 0)
## # A tibble: 0 x 2
## # … with 2 variables: `percent_diff > 0` <lgl>, n <int>
diff_code %>% 
  filter(icd == 'G93') %>% 
  ungroup() %>% 
  count(percent_diff > 0)
## # A tibble: 0 x 2
## # … with 2 variables: `percent_diff > 0` <lgl>, n <int>
diff_code %>% 
  filter(icd == 'R42') %>% 
  ungroup() %>% 
  count(percent_diff > 0)
## # A tibble: 0 x 2
## # … with 2 variables: `percent_diff > 0` <lgl>, n <int>

Severity descriptive statistics

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

severe_code <- diag_icd_9 %>% 
  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(drop = F) + 
  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(drop = F) + 
  #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/icd9_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].

Note that several ICD-9 codes have very small sample size (small number of patients associated with these codes). Therefore, we should take that into account when interpreting the result of the statistical tests for these codes.

First, remove ICD with very small number of patients across site:

small_icds <- diag_icd_9 %>% 
  group_by(icd) %>% 
  summarise(num_pats_icd = sum(num_patients_icd, na.rm = TRUE), .groups = 'drop') %>% 
  filter(num_pats_icd < 5) %>% 
  pull(icd)
diag_icd_9 <- diag_icd_9 %>% 
  filter(!icd %in% small_icds)

Before admission

contingency_df <- diag_icd_9 %>% 
  # filter(time == 'Before admission') %>% 
  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),
         ci = paste0('(', round(log2(lower), 1), ', ', 
                     round(log2(upper), 1), ')'),
         lestimate = log2(estimate)) 
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')

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, ci, 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' = 'ci', 
         '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/icd9_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/icd9_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'
    )) %>% 
  {.}
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') %>% 
    mutate(presentation = droplevels(presentation)),
  plot_obs_exp_right %>% filter(time == 'Before admission') %>% 
    slice(match(sorted_icds, full_icd)) %>% 
    mutate(presentation = droplevels(presentation)),
  nudge = 1.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') %>% 
    mutate(presentation = droplevels(presentation)),
  plot_obs_exp_right %>% filter(time == 'After admission') %>% slice(match(sorted_icds, full_icd)) %>% 
    mutate(presentation = droplevels(presentation)),
  nudge = 1.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 <- 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/icd9_severe_after.png', enrichment_plot_after, width = 9.5, height = 3.5)
ggsave('figs/tiffs/efig_5.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 50 rows containing missing values (geom_pointrangeh).

ggsave('figs/icd9_severe.png', plot_enrich_both, width = 9, height = 5)
## Warning: Removed 50 rows containing missing values (geom_pointrangeh).
LS0tCnRpdGxlOiAiTmV1cm8gYW5hbHlzZXMsIElDRC05IGNvZGUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGx1bWVuCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGhpZ2hsaWdodDogdGFuZ28Ka25pdDogKGZ1bmN0aW9uKGlucHV0RmlsZSwgZW5jb2RpbmcpIHsKICBybWFya2Rvd246OnJlbmRlcihpbnB1dEZpbGUsIGVuY29kaW5nID0gZW5jb2RpbmcsIG91dHB1dF9kaXIgPSAiZG9jcyIpIH0pCi0tLQoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShyZWFkcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJjYXJ0b2NvbG9yKQpsaWJyYXJ5KGZvcmNhdHMpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkoRFQpCmxpYnJhcnkoY293cGxvdCkKCmxvYWQoJ2RhdGEvcHJvY2Vzc2VkLWRhdGEuUmRhdGEnKQp0aGVtZV9zZXQodGhlbWVfYncoKSArIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkpCnNvdXJjZSgndXRpbHMuUicpCmBgYAoKIyMgUHJldmFsZW5jZSBhbmFseXNpcwoKQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzOgoKYGBge3IgZmlnLmhlaWdodD05fQpjb2RlX3ByZXZhbGVuY2UgPC0gZGlhZ19pY2RfOSAlPiUgCiAgbXV0YXRlKHRpbWUgPSBmY3RfcmVsZXZlbCh0aW1lLCBjKCdBZnRlciBhZG1pc3Npb24nLCAnQmVmb3JlIGFkbWlzc2lvbicpKSkgJT4lIAogIHNlbGVjdCgtIGNvbnRhaW5zKCdJQ0QxMF8nKSkgJT4lIAogIGdyb3VwX2J5KHNpdGVpZCwgdGltZSwgaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCBmdWxsX2ljZCkgJT4lIAogIG11dGF0ZShwZXJjZW50X3BhdHNfc2l0ZSA9IG51bV9wYXRpZW50c19pY2QvbnVtX3BhdGllbnRzX2FsbCwKICAgICAgICAgc2l0ZWlkID0gYXMuZmFjdG9yKHNpdGVpZCkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KENvdW50cnksIHRpbWUsIGljZCwgYE5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5YCwgZnVsbF9pY2QpICU+JSAKICBtdXRhdGUocGVyY2VudF9wYXRzX2NvdW50cnkgPSBzdW0obnVtX3BhdGllbnRzX2ljZCkvYWxsX3BhdHNfY291bnRyeSkgJT4lIAogIHVuZ3JvdXAoKQpgYGAKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CiMgZGlhZ19pY2RfOSAlPiUgCiMgICBjb3VudChzaXRlaWQsIGljZCwgYE5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5YCkgJT4lIAojICAgcHVsbChpY2QpICU+JSAKIyAgIHVuaXF1ZSgpICU+JSAKIyAgIGxlbmd0aCgpCiMgCiMgY2hlY2sgPSBkaWFnX2ljZF85ICU+JQojICAgY291bnQoaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgKQpkaWFnX2ljZF85ICU+JSAKICBjb3VudChzaXRlaWQsIENvdW50cnksIG51bV9wYXRpZW50c19hbGwpCgpkaWFnX2ljZF85ICU+JSAKICBjb3VudChzaXRlaWQsIENvdW50cnksIG51bV9wYXRpZW50c19hbGwpICU+JSAKICBmaWx0ZXIoQ291bnRyeSA9PSAnSXRhbHknKQoKZGlhZ19pY2RfOSAlPiUgZmlsdGVyKAogIHNpdGVpZCAlaW4lIGludGVyc2VjdCh1bmlxdWUoZGlhZ19pY2RfOSRzaXRlaWQpLCB1bmlxdWUoZGlhZ19pY2RfMTAkc2l0ZWlkKSkKKSAlPiUgCiAgc2VsZWN0KENvdW50cnksIHNpdGVpZCkgJT4lIAogIGRpc3RpbmN0KCkKCmRpYWdfaWNkXzkgJT4lIAogIGZpbHRlcihDb3VudHJ5ID09ICdVU0EnKSAlPiUgCiAgZ3JvdXBfYnkoc2l0ZWlkKSAlPiUgCiAgc3VtbWFyaXNlKHNpdGVfcGF0c185ID0gc3VtKG51bV9wYXRpZW50c19pY2QpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUgCiAgbGVmdF9qb2luKGRpYWdfaWNkXzEwICU+JSAKICBmaWx0ZXIoc2l0ZWlkICVpbiUgZGlhZ19pY2RfOSRzaXRlaWQpICU+JSAKICBncm91cF9ieShzaXRlaWQpICU+JSAKICBzdW1tYXJpc2Uoc2l0ZV9wYXRzXzEwID0gc3VtKG51bV9wYXRpZW50c19pY2QpLCAuZ3JvdXBzID0gJ2Ryb3AnKSwKICBieSA9ICdzaXRlaWQnKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKC0gc2l0ZWlkKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gc2l0ZWlkLCB5ID0gdmFsdWUsIGZpbGwgPSBuYW1lKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gJ2RvZGdlJykKYGBgCgoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTB9CmRpZmZfY29kZSA8LSBjb2RlX3ByZXZhbGVuY2UgJT4lIAogIHNlbGVjdChsb2NhdGlvbiA9IHNpdGVpZCwgaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCB0aW1lLCBwZXJjZW50X3BhdHNfc2l0ZSkgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB0aW1lLCB2YWx1ZXNfZnJvbSA9IHBlcmNlbnRfcGF0c19zaXRlLCAKICAgICAgICAgICAgICBpZF9jb2xzID0gYyhsb2NhdGlvbiwgaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgKSwgCiAgICAgICAgICAgICAgdmFsdWVzX2ZpbGwgPSBsaXN0KHBlcmNlbnRfcGF0c19zaXRlID0gMCkpICU+JSAKICBtdXRhdGUocGVyY2VudF9kaWZmID0gYEFmdGVyIGFkbWlzc2lvbmAgLSBgQmVmb3JlIGFkbWlzc2lvbmAsCiAgICAgICAgIGxvY190eXBlID0gJ1NpdGUnLCBsb2NhdGlvbiA9IHRvbG93ZXIobG9jYXRpb24pKQoKZGlmZl9jb2RlX2NvdW50cnkgPC0gY29kZV9wcmV2YWxlbmNlICU+JSAKICBzZWxlY3QobG9jYXRpb24gPSBDb3VudHJ5LCBpY2QsIGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWAsIHRpbWUsIHBlcmNlbnRfcGF0c19jb3VudHJ5KSAlPiUgCiAgZGlzdGluY3QoKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHRpbWUsIHZhbHVlc19mcm9tID0gcGVyY2VudF9wYXRzX2NvdW50cnksIAogICAgICAgICAgICAgIGlkX2NvbHMgPSBjKGxvY2F0aW9uLCBpY2QsIGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCAKICAgICAgICAgICAgICB2YWx1ZXNfZmlsbCA9IGxpc3QocGVyY2VudF9wYXRzX2NvdW50cnkgPSAwKSkgJT4lIAogIG11dGF0ZShwZXJjZW50X2RpZmYgPSBgQWZ0ZXIgYWRtaXNzaW9uYCAtIGBCZWZvcmUgYWRtaXNzaW9uYCwKICAgICAgICAgbG9jX3R5cGUgPSAnQ291bnRyeScpCgpkaWZmX2NvZGVfbG9jIDwtIGRpZmZfY29kZSAlPiUgCiAgYmluZF9yb3dzKGRpZmZfY29kZV9jb3VudHJ5KQoKZGlmZl9oZWF0IDwtIGRpZmZfY29kZV9sb2MgJT4lIAogIGdncGxvdChhZXMoeSA9IGljZCwgeCA9IGxvY2F0aW9uLCBmaWxsID0gcGVyY2VudF9kaWZmKSkgKwogIGdlb21fdGlsZSgpICsKICBsYWJzKHkgPSBOVUxMLCB4ID0gTlVMTCwgZmlsbCA9ICdBZnRlciAtIEJlZm9yZScpICsKICBzY2FsZV94X2Rpc2NyZXRlKGRyb3AgPSBGKSArIAogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCMwMDkzOTIsIzM5YjE4NSwjOWNjYjg2LCNlOWUyOWMsI2VlYjQ3OSwjZTg4NDcxLCNjZjU5N2UKICAgIGxvdyA9ICcjNzk4MjM0JywKICAgIGhpZ2ggPSAnI2NmNTk3ZScsCiAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCAKICAgICAgICAgICAgIGNvbHMgPSB2YXJzKGZjdF9yZXYobG9jX3R5cGUpKSwgc3BhY2UgPSAnZnJlZScsIHNjYWxlID0gJ2ZyZWUnKSArCiAgaGVhdF90aGVtZV9ib3R0b20oKSArCiAgdGhlbWUocGFuZWwuc3BhY2luZy54ID0gdW5pdCgxMCwgInBvaW50cyIpLAogICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsOCwwLjUsMTIpLCAibGluZXMiKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKSArCiAgIyB0aGVtZSgpICsKICBOVUxMCmRpZmZfaGVhdAoKICMgZ2dzYXZlKCdmaWdzL2ljZDlfZGlmZl9oZWF0bWFwLnBuZycsIGRpZmZfaGVhdCwgaGVpZ2h0ID0gNCwgd2lkdGggPSA4KQpgYGAKCgoKIyMjIEh5cG90aGVzaXMgdGVzdGluZwoKYGBge3J9Cm15X3QgPC0gZnVuY3Rpb24oaWNkaSl7CiAgZGlmZl9pY2QgPC0gZGlmZl9jb2RlICU+JSBmaWx0ZXIoaWNkID09IGljZGkpCiAgZGF0YS5mcmFtZSgKICAgIGljZCA9IGljZGksCiAgICB0LnRlc3QoZGlmZl9pY2QkcGVyY2VudF9kaWZmKSAlPiUgCiAgICBicm9vbTo6dGlkeSgpCiAgKQp9CnByZXZhbGVuY2Vfc3RhdHMgPC0gdW5pcXVlKGRpZmZfY29kZSRpY2QpICU+JSAKICBsYXBwbHkobXlfdCkgJT4lIAogIGJpbmRfcm93cygpCnByZXZhbGVuY2Vfc3RhdHMkcF92YWx1ZV9ob2xtIDwtICBwLmFkanVzdChwcmV2YWxlbmNlX3N0YXRzJHAudmFsdWUsICdob2xtJykKcHJldmFsZW5jZV9zdGF0cyRwX3ZhbHVlX2JoIDwtIHAuYWRqdXN0KHByZXZhbGVuY2Vfc3RhdHMkcC52YWx1ZSwgJ0JIJykKcHJldmFsZW5jZV9zdGF0cyAlPiUgCiAgZmlsdGVyKHBfdmFsdWVfYmggPCAwLjA1KQoKYGBgCgoKIyMjIFByZXZhbGVuY2UgY2hhbmdlIHRhYmxlCgpgYGB7cn0KcHJldmFsZW5jZV9zdGF0cyAlPiUgCiAgYXJyYW5nZShkZXNjKGVzdGltYXRlKSkgJT4lIAogIG11dGF0ZShjaSA9IHBhc3RlMCgnKCcsIHJvdW5kKGNvbmYubG93KjEwMCwgMiksICclLCAnLCAKICAgICAgICAgICAgICAgICAgICAgcm91bmQoY29uZi5oaWdoKjEwMCwgMiksICclKScpKSAlPiUgCiAgc2VsZWN0KC0gYyhwYXJhbWV0ZXIsIG1ldGhvZCwgYWx0ZXJuYXRpdmUsIGNvbmYubG93LCBjb25mLmhpZ2gpKSAlPiUgCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsIGZpbHRlciA9ICd0b3AnKSAlPiUgCiAgZm9ybWF0Um91bmQoYygnc3RhdGlzdGljJyksIDEpICU+JQogIGZvcm1hdFBlcmNlbnRhZ2UoJ2VzdGltYXRlJywgMSkgJT4lIAogIGZvcm1hdFNpZ25pZihjKCdwLnZhbHVlJywgJ3BfdmFsdWVfaG9sbScsICdwX3ZhbHVlX2JoJyksIDMpICU+JQogIHsufQoKcHJldmFsZW5jZV9zdGF0cyAlPiUgCiAgd3JpdGVfY3N2KCdyZXN1bHRzL2ljZDlfcHJldmFsZW5jZV9zdGF0cy5jc3YnKQpgYGAKCgojIyMgQ29tcHV0ZSBjb25maWRlbmNlIGludmVydmFsIApvZiB0aGUgbWVhbiBwcm9wb3J0aW9uIG9mIHBhdGllbnRzIGRpYWdub3NlZCB3aXRoIGVhY2ggSUNECgpgYGB7cn0KYWxwaGFfdGhyZXNob2xkIDwtIHFub3JtKDAuOTc1KQoKY2lfcHJldmFsZW5jZSA8LSBjb2RlX3ByZXZhbGVuY2UgJT4lIAogIGdyb3VwX2J5KHRpbWUsIGZ1bGxfaWNkKSAlPiUKICBhZGRfY291bnQoKSAlPiUKICBzdW1tYXJpc2UoCiAgICBtZWFuX3Byb3AgPSBtZWFuKHBlcmNlbnRfcGF0c19zaXRlLCBuYS5ybSA9IFQpLAogICAgc2RfcHJvYiA9IHNkKHBlcmNlbnRfcGF0c19zaXRlLCBuYS5ybSA9IFQpLAogICAgbiA9IG1lYW4obiksCiAgICBtZV9wcm9wID0gYWxwaGFfdGhyZXNob2xkICogc2RfcHJvYiAvIHNxcnQobikKICApICU+JQogIHVuZ3JvdXAoKQpgYGAKCgoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9CmljZDlfdGltZSA8LSBkaWFnX2ljZF85ICU+JSAKICBtdXRhdGUodGltZSA9IGZjdF9yZWxldmVsKHRpbWUsIGMoJ0FmdGVyIGFkbWlzc2lvbicsICdCZWZvcmUgYWRtaXNzaW9uJykpKSAlPiUgCiAgZ3JvdXBfYnkoZnVsbF9pY2QsIHRpbWUpICU+JSAKICBzdW1tYXJpc2UocGF0c19pY2Q5X3RpbWUgPSBzdW0obnVtX3BhdGllbnRzX2ljZCwgbmEucm0gPSBUKSwgLmdyb3VwcyA9ICdkcm9wJykgCgpzb3J0ZWRfaWNkcyA8LSBjb2RlX3ByZXZhbGVuY2UgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgc2VsZWN0KHRpbWUsIHBlcmNlbnRfcGF0c19zaXRlLCBmdWxsX2ljZCwgc2l0ZWlkKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHRpbWUsIHZhbHVlc19mcm9tID0gcGVyY2VudF9wYXRzX3NpdGUsIHZhbHVlc19maWxsID0gbGlzdChwZXJjZW50X3BhdHNfc2l0ZSA9IDApKSAlPiUgCiAgZ3JvdXBfYnkoZnVsbF9pY2QpICU+JSAKICBzdW1tYXJpc2UoYWZ0ZXIgPSBtZWFuKGBBZnRlciBhZG1pc3Npb25gLCBuYS5ybSA9IFQpLAogICAgICAgICBiZWZvcmUgPSBtZWFuKGBCZWZvcmUgYWRtaXNzaW9uYCwgbmEucm0gPSBUKSwKICAgICAgICAgZGlmZl9wcmV2ID0gYWZ0ZXIgLSBiZWZvcmUsCiAgICAgICAgIC5ncm91cHMgPSAnZHJvcCcpICU+JSAKICBhcnJhbmdlKGRpZmZfcHJldikgJT4lIAogIHB1bGwoZnVsbF9pY2QpCgp0b3RhbF9pY2QgPC0gaWNkOV90aW1lICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBwYXRzX2ljZDlfdGltZSwgeSA9IGZjdF9yZWxldmVsKGZ1bGxfaWNkLCBzb3J0ZWRfaWNkcyksIGZpbGwgPSB0aW1lKSkgKwogIHNjYWxlX2ZpbGxfY2FydG9fZChwYWxldHRlID0gNCwgZ3VpZGUgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAnZG9kZ2UnKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBzY2FsZV94X3JldmVyc2UoZXhwYW5kID0gZXhwYW5zaW9uKGFkZCA9IGMoMCwwKSkpICsKICBzY2FsZV95X2Rpc2NyZXRlKGxhYmVscyA9IE5VTEwpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMywgMC4xNSksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCg0LCAnbW0nKSwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbih0ID0gMSwgciA9IDAuNSwgbCA9IDAuNSwgdW5pdCA9ICdsaW5lcycpKSArIAogIGxhYnMoeCA9ICdUb3RhbCBudW1iZXIgb2YgcGF0aWVudHMgYXQgYWxsIHNpdGVzJywgeSA9IE5VTEwpCgpwZXJjZW50X2ljZCA8LSAKICBjaV9wcmV2YWxlbmNlICU+JSAKICBnZ3Bsb3QoYWVzKGdyb3VwID0gdGltZSkpICsKICBnZ3N0YW5jZTo6Z2VvbV9wb2ludHJhbmdlaCgKICAgIGFlcyh4ID0gbWVhbl9wcm9wLAogICAgICAgIHkgPSBmY3RfcmVsZXZlbChmdWxsX2ljZCwgc29ydGVkX2ljZHMpLAogICAgICAgIHhtaW4gPSBtZWFuX3Byb3AgLSBtZV9wcm9wLAogICAgICAgIHhtYXggPSBtZWFuX3Byb3AgKyBtZV9wcm9wLAogICAgICAgIGNvbG9yID0gdGltZSksCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC4zKSwgCiAgICBzdHJva2UgPSAwLjEsIGZhdHRlbiA9IDMsIHNpemUgPSAwLjcgIAogICkgKwogIHNjYWxlX2NvbG9yX2NhcnRvX2QocGFsZXR0ZSA9IDQsIGd1aWRlID0gTlVMTCkgKwogIHRoZW1lX21pbmltYWwoKSArICAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuNzUsIDAuMSksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbih0ID0gMSwgdW5pdCA9ICdsaW5lcycpKSArIAogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24oYWRkID0gYygwLCAwLjAzKSksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKwogIGxhYnMoeCA9ICdQcm9wb3J0aW9uIG9mIHBhdGllbnRzIGVhY2ggc2l0ZScsIHkgPSBOVUxMKQoKaWNkOV9wcmV2YWxlbmNlX3Bsb3RzIDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZCh0b3RhbF9pY2QsIHBlcmNlbnRfaWNkLCBuY29sID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbF93aWR0aHMgPSBjKDEsIDIuNSkpCmljZDlfcHJldmFsZW5jZV9wbG90cwojIGdnc2F2ZSgnZmlncy9pY2Q5X3ByZXZhbGVuY2UucG5nJywgaWNkOV9wcmV2YWxlbmNlX3Bsb3RzLCBoZWlnaHQgPSA1LCB3aWR0aCA9IDEwKQpgYGAKCmBgYHtyfQpwbG90X2dyaWQoZGlmZl9oZWF0LCBpY2Q5X3ByZXZhbGVuY2VfcGxvdHMsIHJlbF9oZWlnaHRzID0gYygxLCAwLjg1KSwKICAgICAgICAgIG5jb2wgPSAxLCBsYWJlbHMgPSAnQVVUTycpICU+JQogIGdnc2F2ZSgnZmlncy9pY2Q5X3ByZXZhbGVuY2VfQUIucG5nJywgLiwgaGVpZ2h0ID0gOCwgd2lkdGggPSA5LjcpCgpwbG90X2dyaWQoZGlmZl9oZWF0LCBpY2Q5X3ByZXZhbGVuY2VfcGxvdHMsIHJlbF9oZWlnaHRzID0gYygxLCAwLjg1KSwKICAgICAgICAgIG5jb2wgPSAxLCBsYWJlbHMgPSAnQVVUTycpICU+JQogIGdnc2F2ZSgnZmlncy90aWZmcy9lZmlnXzQudGlmZicsIC4sIGhlaWdodCA9IDgsIHdpZHRoID0gOS43LCBkcGkgPSAzMDApCmBgYAoKYGBge3IgZmlnLmhlaWdodD05fQpiZWZvcmVfY29kZV9wcmV2IDwtIGRpZmZfY29kZV9sb2MgJT4lIAogIGdncGxvdChhZXMoeSA9IGljZCwgeCA9IGxvY2F0aW9uLCBmaWxsID0gYEJlZm9yZSBhZG1pc3Npb25gKSkgKwogIGdlb21fdGlsZSgpICsKICBsYWJzKHkgPSBOVUxMLCB4ID0gTlVMTCkgKwogICNmNmQyYTksI2Y1Yjc4ZSwjZjE5YzdjLCNlYTgxNzEsI2RkNjg2YywjY2E1MjY4LCNiMTNmNjQKICBzY2FsZV9maWxsX2dyYWRpZW50KAogICAgbG93ID0gJyNkMWVlZWEnLCBoaWdoID0gJyMyYTU2NzQnLAogICAgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpLAogICAgbGltaXRzID0gYygwLCBtYXgoZGlmZl9jb2RlX2xvYyRgQWZ0ZXIgYWRtaXNzaW9uYCkpLAogICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChmaWxsID0gIndoaXRlIikpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShkcm9wID0gRikgKyAKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCAKICAgICAgICAgICAgIGNvbHMgPSB2YXJzKGZjdF9yZXYobG9jX3R5cGUpKSwgc3BhY2UgPSAnZnJlZScsIHNjYWxlID0gJ2ZyZWUnKSArCiAgaGVhdF90aGVtZV90b3AoKSArCiAgTlVMTAoKYWZ0ZXJfY29kZV9wcmV2IDwtIGRpZmZfY29kZV9sb2MgJT4lIAogIGdncGxvdChhZXMoeSA9IGljZCwgeCA9IGxvY2F0aW9uLCBmaWxsID0gYEFmdGVyIGFkbWlzc2lvbmApKSArCiAgZ2VvbV90aWxlKCkgKwogIGxhYnMoeSA9IE5VTEwsIHggPSBOVUxMLCBmaWxsID0gJ1BhdGllbnQgJSBwZXIKc2l0ZS9jb3VudHJ5JykgKwogIHNjYWxlX3hfZGlzY3JldGUoZHJvcCA9IEYpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAnI2QxZWVlYScsIGhpZ2ggPSAnIzJhNTY3NCcsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIG1heChkaWZmX2NvZGVfbG9jJGBBZnRlciBhZG1pc3Npb25gKSkpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCAKICAgICAgICAgICAgIGNvbHMgPSB2YXJzKGZjdF9yZXYobG9jX3R5cGUpKSwgc3BhY2UgPSAnZnJlZScsIHNjYWxlID0gJ2ZyZWUnKSArCiAgaGVhdF90aGVtZV9ib3R0b20oKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMCkpICsKICBOVUxMCgpoZWF0cyA8LSBjb3dwbG90OjpwbG90X2dyaWQoYmVmb3JlX2NvZGVfcHJldiwgYWZ0ZXJfY29kZV9wcmV2LCBuY29sID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygxLCAxLjE1KSwKICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ0EuIEJlZm9yZSBhZG1pc3Npb24nLCAnQi4gQWZ0ZXIgYWRtaXNzaW9uJykpCmhlYXRzCmdnc2F2ZSgnZmlncy9pY2Q5X2hlYXRtYXAucG5nJywgaGVhdHMsIGhlaWdodCA9IDEwLCB3aWR0aCA9IDcpCmdnc2F2ZSgnZmlncy90aWZmcy9lZmlnXzMudGlmZicsIGhlYXRzLCBoZWlnaHQgPSAxMCwgd2lkdGggPSA3LCBkcGkgPSAzMDApCmBgYAoKCmBgYHtyfQpkaWZmX2NvZGUgJT4lIAogIGZpbHRlcihpY2QgPT0gJ1I0MScpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGNvdW50KHBlcmNlbnRfZGlmZiA+IDApCgpkaWZmX2NvZGUgJT4lIAogIGZpbHRlcihpY2QgPT0gJ0c5MycpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGNvdW50KHBlcmNlbnRfZGlmZiA+IDApCgpkaWZmX2NvZGUgJT4lIAogIGZpbHRlcihpY2QgPT0gJ1I0MicpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGNvdW50KHBlcmNlbnRfZGlmZiA+IDApCmBgYAoKCiMgU2V2ZXJpdHkgZGVzY3JpcHRpdmUgc3RhdGlzdGljcwoKV2hhdCBJQ0QgY29kZSBoYXMgdGhlIG1vc3QgbnVtYmVyIHNldmVyZSBwYXRpZW50cz8KU2l0ZXMgd2l0aCBtb3JlIHNldmVyZSBwYXRpZW50cz8KCmBgYHtyIGZpZy5oZWlnaHQ9OX0Kc2V2ZXJlX2NvZGUgPC0gZGlhZ19pY2RfOSAlPiUgCiAgbXV0YXRlKHBlcmNlbnRfc2V2ZXJlX3NpdGUgPSBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMS9udW1fcGF0aWVudHNfaWNkLAogICAgICAgICBzaXRlaWQgPSBhcy5mYWN0b3Ioc2l0ZWlkKSkKCnNldmVyZV9iZWZfaGVhdCA8LSBzZXZlcmVfY29kZSAlPiUgCiAgZmlsdGVyKHRpbWUgPT0gJ0JlZm9yZSBhZG1pc3Npb24nKSAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gaWNkLCB4ID0gc2l0ZWlkLCBmaWxsID0gcGVyY2VudF9zZXZlcmVfc2l0ZSkpICsKICBnZW9tX3RpbGUoKSArCiAgbGFicyh5ID0gTlVMTCwgeCA9IE5VTEwsIGZpbGwgPSAnU2V2ZXJlICUgcGVyIElDRCcpICsKICAjM2Q1OTQxLCM3Nzg4NjgsI2I1Yjk5MSwjZjZlZGJkLCNlZGJiOGEsI2RlOGE1YSwjY2E1NjJjCiAgc2NhbGVfZmlsbF9ncmFkaWVudCgKICAgICMgbG93ID0gJyNmNmQyYTknLCBoaWdoID0gJyNiMTNmNjQnLAogICAgbG93ID0gJ3doaXRlJywgaGlnaCA9ICcjY2E1NjJjJywKICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoZmlsbCA9ICJ3aGl0ZSIpKSwKICAgICkgKwogIHNjYWxlX3hfZGlzY3JldGUoZHJvcCA9IEYpICsgCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgKSwgc3BhY2UgPSAnZnJlZScsIHNjYWxlID0gJ2ZyZWUnKSArCiAgaGVhdF90aGVtZV90b3AoKSArCiAgTlVMTAoKc2V2ZXJlX2FmdF9oZWF0IDwtIHNldmVyZV9jb2RlICU+JSAKICBmaWx0ZXIodGltZSA9PSAnQWZ0ZXIgYWRtaXNzaW9uJykgJT4lIAogIGdncGxvdChhZXMoeSA9IGljZCwgeCA9IHNpdGVpZCwgZmlsbCA9IHBlcmNlbnRfc2V2ZXJlX3NpdGUpKSArCiAgZ2VvbV90aWxlKCkgKwogIGxhYnMoeSA9IE5VTEwsIHggPSBOVUxMLCBmaWxsID0gJ1NldmVyZSAlIHBlciBJQ0QnKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShkcm9wID0gRikgKyAKICAjM2Q1OTQxLCM3Nzg4NjgsI2I1Yjk5MSwjZjZlZGJkLCNlZGJiOGEsI2RlOGE1YSwjY2E1NjJjCiAgc2NhbGVfZmlsbF9ncmFkaWVudCgKICAgIGxvdyA9ICd3aGl0ZScsIGhpZ2ggPSAnI2NhNTYyYycsCiAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKGBOZXVyb2xvZ2ljYWwgRGlzZWFzZSBDYXRlZ29yeWApLCBzcGFjZSA9ICdmcmVlJywgc2NhbGUgPSAnZnJlZScpICsKICBoZWF0X3RoZW1lX2JvdHRvbSgpICsKICBOVUxMCgpzZXZlcmVfaGVhdHMgPC0gY293cGxvdDo6cGxvdF9ncmlkKHNldmVyZV9iZWZfaGVhdCwgc2V2ZXJlX2FmdF9oZWF0LCBuY29sID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygxLCAxLjE1KSwKICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoJ0EuIEJlZm9yZSBhZG1pc3Npb24nLCAnQi4gQWZ0ZXIgYWRtaXNzaW9uJykpCnNldmVyZV9oZWF0cwpnZ3NhdmUoJ2ZpZ3MvaWNkOV9zZXZlcmVfaGVhdG1hcC5wbmcnLCBzZXZlcmVfaGVhdHMsIGhlaWdodCA9IDEwLCB3aWR0aCA9IDcpCmBgYAoKCgojIFNldmVyaXR5IGVucmljaG1lbnQgYW5hbHlzaXMKCiMjIE51bGwgaHlwb3RoZXNpcyAKRm9yIGVhY2ggSUNEIGNvZGUsIHRoZSBwcm9wb3J0aW9uIG9mIHNldmVyZSBwYXRpZW50cyB3aG8gd2VyZSBkaWFnbm9zZWQgd2l0aCB0aGF0IElDRCBjb2RlIGlzIHNpbWlsYXIgdG8gdGhlIHByb3BvcnRpb24gb2YgbmV2ZXItc2V2ZXJlIHBhdGllbnRzIHdobyB3ZXJlIGRpYWdub3NlZCB3aXRoIHRoYXQgc2FtZSBJQ0QgY29kZS4KCkZvciB0aGUgc2FrZSBvZiBzaW1wbGljaXR5IGluIHRoaXMgbm90ZWJvb2ssIHdlJ3JlIGdvaW5nIHRvIGRlbm90ZSBwYXRpZW50cyB3aG9zZSBzeW1wdG9tcyBoYXZlIGJlZW4gY2F0ZWdvcml6ZWQgYXMgc2V2ZXJlIChiYXNlZCBvbiByZXNwaXJhdG9yeSBzdGF0dXMgKy8tIHJlcXVpcmluZyBJQ1UpIGF0IGxlYXN0IG9uY2UgYXMgKnNldmVyZSBwYXRpZW50cyosIGFuZCBwYXRpZW50cyB3aG9zZSBzeW1wdG9tcyBoYXZlIE5FVkVSIGJlZW4gY2F0ZWdvcml6ZWQgYXMgc2V2ZXJlIGFzICpub24tc2V2ZXJlIHBhdGllbnRzKi4KCkZvciBlYWNoIElDRCBjb2RlLCB3ZSBjb21wdXRlZCB0aGUgZXhwZWN0ZWQgbnVtYmVyIG9mICpzZXZlcmUgcGF0aWVudHMqIGJ5IG11bHRpcGx5aW5nIHRoZSBwcm9wb3J0aW9uIG9mICpub24tc2V2ZXJlIHBhdGllbnRzKiB3aG8gd2VyZSBkaWFnbm9zZWQgd2l0aCB0aGF0IGNvZGUgd2l0aCB0aGUgdG90YWwgbnVtYmVyIG9mICpzZXZlcmUgcGF0aWVudHMqLgpXZSBwZXJmb3JtZWQgYW4gZW5yaWNobWVudCBhbmFseXNpcyB0byBleGFtaW5lIHRoZSBkaWZmZXJlbmNlIG9mICpzZXZlcmUgcGF0aWVudHMqIHByb3BvcnRpb25zIGFjcm9zcyBJQ0QgY29kZXMuCldlIGNhbGN1bGF0ZWQgZWFjaCBJQ0QgY29kZSdzIGVucmljaG1lbnQgYnkgZGl2aWRpbmcgdGhlIG9ic2VydmVkIHByb3BvcnRpb24gb2YgaG9ub3JlZXMgYnkgdGhlIGV4cGVjdGVkIHByb3BvcnRpb24gb2YgaG9ub3JlZXMgYW5kIHJlcG9ydGVkIGEgdmFsdWUgb2YgbG9nMiBlbnJpY2htZW50IChMT0UpIGFuZCBpdHMgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzLgpUaGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb2YgdGhlIExPRSB3YXMgZXN0aW1hdGVkIHVzaW5nIHRoZSBQb2lzc29uIG1vZGVsIG1ldGhvZCBbQGlzYm46OTc4MDg0OTM5NDQ0N10uCgpOb3RlIHRoYXQgc2V2ZXJhbCBJQ0QtOSBjb2RlcyBoYXZlIHZlcnkgc21hbGwgc2FtcGxlIHNpemUgKHNtYWxsIG51bWJlciBvZiBwYXRpZW50cyBhc3NvY2lhdGVkIHdpdGggdGhlc2UgY29kZXMpLgpUaGVyZWZvcmUsIHdlIHNob3VsZCB0YWtlIHRoYXQgaW50byBhY2NvdW50IHdoZW4gaW50ZXJwcmV0aW5nIHRoZSByZXN1bHQgb2YgdGhlIHN0YXRpc3RpY2FsIHRlc3RzIGZvciB0aGVzZSBjb2Rlcy4KCkZpcnN0LCByZW1vdmUgSUNEIHdpdGggdmVyeSBzbWFsbCBudW1iZXIgb2YgcGF0aWVudHMgYWNyb3NzIHNpdGU6CgpgYGB7cn0Kc21hbGxfaWNkcyA8LSBkaWFnX2ljZF85ICU+JSAKICBncm91cF9ieShpY2QpICU+JSAKICBzdW1tYXJpc2UobnVtX3BhdHNfaWNkID0gc3VtKG51bV9wYXRpZW50c19pY2QsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAnZHJvcCcpICU+JSAKICBmaWx0ZXIobnVtX3BhdHNfaWNkIDwgNSkgJT4lIAogIHB1bGwoaWNkKQpkaWFnX2ljZF85IDwtIGRpYWdfaWNkXzkgJT4lIAogIGZpbHRlcighaWNkICVpbiUgc21hbGxfaWNkcykKYGBgCgojIyBCZWZvcmUgYWRtaXNzaW9uCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpjb250aW5nZW5jeV9kZiA8LSBkaWFnX2ljZF85ICU+JSAKICAjIGZpbHRlcih0aW1lID09ICdCZWZvcmUgYWRtaXNzaW9uJykgJT4lIAogIGdyb3VwX2J5KGZ1bGxfaWNkLCB0aW1lKSAlPiUgCiAgc3VtbWFyaXNlKGFjcm9zcyhjb250YWlucygnc2V2ZXJlJyksIC5mbnMgPSBzdW0sIG5hLnJtID0gVCksIC5ncm91cHMgPSAnZHJvcCcpICU+JSAKICBtdXRhdGUoT2JzZXJ2ZWQgPSBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMSwgCiAgICAgICAgIG51bV9ub25fc2V2ZXJlID0gbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QxICsgbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QwLAogICAgICAgICBudW1fc2V2ZXJlID0gbnVtX3BhdGllbnRzX2V2ZXJfc2V2ZXJlX2ljZDEgKyBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMCwKICAgICAgICAgRXhwZWN0ZWQgPSBudW1fcGF0aWVudHNfbmV2ZXJfc2V2ZXJlX2ljZDEvbnVtX25vbl9zZXZlcmUqbnVtX3NldmVyZSwKICAgICAgICAgb3Zlcl9zZXYgPSBPYnNlcnZlZCAtIEV4cGVjdGVkKQoKbmVzdGVkX29ic19leHAgPC0gY29udGluZ2VuY3lfZGYgJT4lCiAgc2VsZWN0KGZ1bGxfaWNkLCAKICAgICAgICAgdGltZSwKICAgICAgICAgbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QwLCAKICAgICAgICAgbnVtX3BhdGllbnRzX25ldmVyX3NldmVyZV9pY2QxLCAKICAgICAgICAgbnVtX3BhdGllbnRzX2V2ZXJfc2V2ZXJlX2ljZDAsIAogICAgICAgICBudW1fcGF0aWVudHNfZXZlcl9zZXZlcmVfaWNkMSkgJT4lIAogIGdyb3VwX2J5KGZ1bGxfaWNkLCB0aW1lKSAlPiUgCiAgbmVzdCgpIAoKZmlzaF9vYnNfZXhwIDwtIG5lc3RlZF9vYnNfZXhwICU+JSAKICBtdXRhdGUoZmlzaCA9IG1hcChkYXRhLCBteV9maXNoKSkgJT4lIAogIGRwbHlyOjpzZWxlY3QoLWRhdGEpICU+JSAKICB1bm5lc3QoY29scyA9IGMoZmlzaCkpICU+JSAKICBtdXRhdGUodXBwZXIgPSBpZmVsc2UoaXMubmEodXBwZXIpLCBJbmYsIHVwcGVyKSwKICAgICAgICAgY2kgPSBwYXN0ZTAoJygnLCByb3VuZChsb2cyKGxvd2VyKSwgMSksICcsICcsIAogICAgICAgICAgICAgICAgICAgICByb3VuZChsb2cyKHVwcGVyKSwgMSksICcpJyksCiAgICAgICAgIGxlc3RpbWF0ZSA9IGxvZzIoZXN0aW1hdGUpKSAKZmlzaF9vYnNfZXhwJGBQIHZhbHVlIChIb2xtKWAgPC0gIHAuYWRqdXN0KGZpc2hfb2JzX2V4cCRwX3ZhbHVlLCAnaG9sbScpCmZpc2hfb2JzX2V4cCRgUCB2YWx1ZSAoRkRSKWAgPC0gcC5hZGp1c3QoZmlzaF9vYnNfZXhwJHBfdmFsdWUsICdCSCcpCmBgYAoKV2hpbGUgdGhlIFdhcm5pbmcgbWVzc2FnZXMgbWVudGlvbmVkIENoaS1zcXVhcmVkLCB0aGUgcC12YWx1ZXMgd2VyZSBhY3R1YWxseSBjYWxjdWxhdGVkIHVzaW5nIEZpc2hlcidzIEV4YWN0IHRlc3QgKHNlZSBtb3JlIGluIGBlcGl0b29sczo6dGFiMmJ5Mi50ZXN0KClgKS4KCk5vdGU6IHNtYWxsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZm9yIElDRC0xMCBjb2RlIEcwNCwgRzAzLCBHNjU6CgpgYGB7cn0KY29udGluZ2VuY3lfZGYgJT4lIAogIGZpbHRlcihncmVwbCgnRzA0fEcwM3xHNjUnLCBmdWxsX2ljZCkpICU+JSAKICBkYXRhdGFibGUoKQpgYGAKCgojIyMgQ291bnRyeSBlbnJpY2htZW50IHRhYmxlIHsjZW5yaWNobWVudF90YWJ9CgpUaGUgZnVsbCB0YWJsZSB3aXRoIGFsbCBJQ0QgY29kZXMgYW5kIHRoZWlyIGNvcnJlc3BvbmRpbmcgZW5yaWNobWVudCBjYW4gYmUgYnJvd3NlZCBpbnRlcmFjdGl2ZWx5IGJlbG93OgpgYGB7cn0KbGlicmFyeShEVCkKZmlzaF90YWIgPC0gZmlzaF9vYnNfZXhwICU+JSAKICBsZWZ0X2pvaW4oY29udGluZ2VuY3lfZGYsIGJ5ID0gYygnZnVsbF9pY2QnLCAndGltZScpKSAlPiUgCiAgc2VsZWN0KGZ1bGxfaWNkLCB0aW1lLCBPYnNlcnZlZCwgRXhwZWN0ZWQsIG92ZXJfc2V2LAogICAgICAgICBlc3RpbWF0ZSwgbGVzdGltYXRlLCBjaSwgcF92YWx1ZSwKICAgICAgICAgYFAgdmFsdWUgKEhvbG0pYCwgYFAgdmFsdWUgKEZEUilgKSAlPiUgCiAgYXJyYW5nZShkZXNjKG92ZXJfc2V2KSkgJT4lIAogIHJlbmFtZSgnSUNEJyA9ICdmdWxsX2ljZCcsCiAgICAgICAgICdPYnNlcnZlZCAtIEV4cGVjdGVkJyA9ICdvdmVyX3NldicsCiAgICAgICAgICdFbnJpY2htZW50JyA9ICdlc3RpbWF0ZScsCiAgICAgICAgICdMb2cyKGVucmljaG1lbnQpJyA9ICdsZXN0aW1hdGUnLAogICAgICAgICAnOTUlIENvbmZpZGVuY2UgaW50ZXJ2YWwnID0gJ2NpJywgCiAgICAgICAgICdQIHZhbHVlIChyYXcpJyA9ICdwX3ZhbHVlJykKCmZpc2hfdGFiICU+JSAKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwgZmlsdGVyID0gJ3RvcCcpICU+JSAKICBmb3JtYXRSb3VuZChjKCdPYnNlcnZlZCcsICdFeHBlY3RlZCcsICdPYnNlcnZlZCAtIEV4cGVjdGVkJywKICAgICAgICAgICAgICAgICdFbnJpY2htZW50JywgJ0xvZzIoZW5yaWNobWVudCknKSwgMSkgJT4lCiAgZm9ybWF0U2lnbmlmKGMoJ1AgdmFsdWUgKHJhdyknLCAnUCB2YWx1ZSAoSG9sbSknLCAnUCB2YWx1ZSAoRkRSKScpLCAzKSAlPiUgCiAgey59CgpmaXNoX3RhYiAlPiUgCiAgd3JpdGVfY3N2KCdyZXN1bHRzL2ljZDlfZmlzaF90YWIuY3N2JykKCmZpc2hfb2JzX2V4cCAlPiUgCiAgZmlsdGVyKGBQIHZhbHVlIChGRFIpYCA8IDAuMDUpICU+JSAKICBhcnJhbmdlKHRpbWUpICU+JSAKICBtdXRhdGUobGVzdGltYXRlID0gcm91bmQobGVzdGltYXRlLCAyKSwKICAgICAgICAgZHJyID0gcm91bmQoKGVzdGltYXRlIC0gMSkqMTAwLCAwKSwKICAgICAgICAgbG93ZXJfZHJyID0gKGxvd2VyIC0gMSkqMTAwLAogICAgICAgICB1cHBlcl9kcnIgPSAodXBwZXIgLSAxKSoxMDAsCiAgICAgICAgIGNpX2RyciA9IHBhc3RlMCgnKCcsIHJvdW5kKGxvd2VyX2RyciwgMCksICcsICcsIAogICAgICAgICAgICAgICAgICAgICByb3VuZCh1cHBlcl9kcnIsIDApLCAnKScpLAogICAgICAgICBwX2ZkciA9IGZvcm1hdChgUCB2YWx1ZSAoRkRSKWAsIGRpZ2l0cyA9IDIpKSAlPiUgCiAgc2VsZWN0KHRpbWUsIGZ1bGxfaWNkLCBsZXN0aW1hdGUsIGRyciwgY2lfZHJyLCBwX2ZkcikgJT4lIAogIHdyaXRlX2NzdigncmVzdWx0cy9pY2Q5X3NpZ25pLmNzdicpCmBgYAoKQSBwb3NpdGl2ZSB2YWx1ZSBvZiBMT0UgaW5kaWNhdGVzIGEgaGlnaGVyIHByb3BvcnRpb24gb2YgKnNldmVyZSBwYXRpZW50cyogd2l0aCB0aGF0IElDRCBjb2RlIGNvbXBhcmVkIHRvICpub24tc2V2ZXJlIHBhdGllbnRzKi4KQSBMT0UgdmFsdWUgb2YgMSByZXByZXNlbnRzIGEgb25lLWZvbGQgZW5yaWNobWVudCAoaS5lLiwgb2JzZXJ2ZWQgbnVtYmVyIG9mICpzZXZlcmUgcGF0aWVudHMqIGlzIHR3aWNlIGFzIG11Y2ggYXMgZXhwZWN0ZWQpLgpXZSBmb3VuZCBhbiBleGNlc3Mgb2YgKnNldmVyZSBwYXRpZW50cyogd2l0aCB0aGUgZm9sbG93aW5nIElDRCBjb2RlczoKCi0gT3RoZXIgZGlzb3JkZXJzIG9mIHRoZSBicmFpbiAoRzkzKTogNjMgbW9yZSAqc2V2ZXJlIHBhdGllbnRzKiB0aGFuIGV4cGVjdGVkLCBMT0UgPSBbXSwgOTUlIENJIFtdCi0gT3RoZXIgYW5kIHVuc3BlY2lmaWVkIG15b3BhdGhpZXMgKEc3Mik6IDM4IG1vcmUgKnNldmVyZSBwYXRpZW50cyogdGhhbiBleHBlY3RlZCwgTE9FID0gW10sIDk1JSBDSSBbXQotIE15b3NpdGlzIChNNjApOiA2IG1vcmUgKnNldmVyZSBwYXRpZW50cyogdGhhbiBleHBlY3RlZCwgTE9FID0gW10sIDk1JSBDSSBbXQoKIyMjIENvbXB1dGUgZW5yaWNobWVudCBmcm9tIHByb3BvcnRpb24gY29tcGFyaXNvbnMKCmBgYHtyIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSAzLjV9CmZpbHRlcmVkX29ic19leHAgPC0gY29udGluZ2VuY3lfZGYgJT4lIAogIGxlZnRfam9pbihmaXNoX29ic19leHAsIGJ5ID0gYygnZnVsbF9pY2QnLCAndGltZScpKSAlPiUgCiAgbXV0YXRlKAogICAgZGlzdGFuY2VfdG9fbnVsbCA9IGNhc2Vfd2hlbigKICAgICAgbG93ZXIgPiAxIH4gbG93ZXIgLSAxLAogICAgICBUUlVFIH4gdXBwZXIgLSAyCiAgICApLAogICAgcHJlc2VudGF0aW9uID0gY2FzZV93aGVuKAogICAgICBsb3dlciA+IDEgJiBgUCB2YWx1ZSAoRkRSKWAgPCAwLjA1IH4gJyNkNDY3ODAnLCAKICAgICAgdXBwZXIgPCAxICYgYFAgdmFsdWUgKEZEUilgIDwgMC4wNSB+ICcjNzk4MjM0JywKICAgICAgVFJVRSB+ICdncmV5MjAnCiAgICApKSAlPiUgCiAgey59CmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0Kc29ydGVkX2ljZHMgPC0gZmlsdGVyZWRfb2JzX2V4cCAlPiUgCiAgZmlsdGVyKHRpbWUgPT0gJ0FmdGVyIGFkbWlzc2lvbicpICU+JSAKICBhcnJhbmdlKEV4cGVjdGVkKSAlPiUgCiAgcHVsbChmdWxsX2ljZCkKCnBsb3Rfb2JzX2V4cCA8LSBmaWx0ZXJlZF9vYnNfZXhwICU+JQogIG11dGF0ZShsZXN0aW1hdGUgPSBsb2cyKGVzdGltYXRlKSwKICAgICAgICAgbGxvd2VyID0gbG9nMihsb3dlciksIAogICAgICAgICBsdXBwZXIgPSBsb2cyKHVwcGVyKSkgJT4lIAogIHNlbGVjdChmdWxsX2ljZCwgdGltZSwgbGVzdGltYXRlLCBsbG93ZXIsIGx1cHBlciwgcHJlc2VudGF0aW9uLCBvdmVyX3NldiwgT2JzZXJ2ZWQsIEV4cGVjdGVkKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKC0gYyhmdWxsX2ljZCwgdGltZSwgcHJlc2VudGF0aW9uLCBvdmVyX3NldiksIG5hbWVzX3RvID0gJ3R5cGUnKSAlPiUgCiAgbXV0YXRlKHN1YnR5cGUgPSBpZmVsc2UodHlwZSA9PSAnRXhwZWN0ZWQnIHwgdHlwZSA9PSAnT2JzZXJ2ZWQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAnU3FydChudW1iZXIgb2YgaG9ub3JlZXMpJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgJ0xvZzIgZW5yaWNobWVudCwgOTUlIENJJykpICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdHlwZSkgJT4lIAogIG11dGF0ZShwcmVzZW50YXRpb24gPSBhcy5mYWN0b3IocHJlc2VudGF0aW9uKSwKICAgICAgICAgZnVsbF9pY2QgPSBmY3RfcmVsZXZlbChmdWxsX2ljZCwgc29ydGVkX2ljZHMpKQoKcGxvdF9vYnNfZXhwX3JpZ2h0IDwtIHBsb3Rfb2JzX2V4cCAlPiUgZmlsdGVyKHN1YnR5cGUgPT0gJ1NxcnQobnVtYmVyIG9mIGhvbm9yZWVzKScpIApwbG90X29ic19leHBfbGVmdCA8LSBwbG90X29ic19leHAgJT4lIGZpbHRlcihzdWJ0eXBlICE9ICdTcXJ0KG51bWJlciBvZiBob25vcmVlcyknKSAKYGBgCgoKYGBge3IgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQplbnJpY2htZW50X3Bsb3RfYmVmb3JlIDwtIHBsb3RfZW5yaWNoKAogIHBsb3Rfb2JzX2V4cF9sZWZ0ICU+JSBmaWx0ZXIodGltZSA9PSAnQmVmb3JlIGFkbWlzc2lvbicpICU+JSAKICAgIG11dGF0ZShwcmVzZW50YXRpb24gPSBkcm9wbGV2ZWxzKHByZXNlbnRhdGlvbikpLAogIHBsb3Rfb2JzX2V4cF9yaWdodCAlPiUgZmlsdGVyKHRpbWUgPT0gJ0JlZm9yZSBhZG1pc3Npb24nKSAlPiUgCiAgICBzbGljZShtYXRjaChzb3J0ZWRfaWNkcywgZnVsbF9pY2QpKSAlPiUgCiAgICBtdXRhdGUocHJlc2VudGF0aW9uID0gZHJvcGxldmVscyhwcmVzZW50YXRpb24pKSwKICBudWRnZSA9IDEuNSkKCmVucmljaG1lbnRfcGxvdF9hZnRlciA8LSBwbG90X2VucmljaCgKICBwbG90X29ic19leHBfbGVmdCAlPiUgZmlsdGVyKHRpbWUgPT0gJ0FmdGVyIGFkbWlzc2lvbicpICU+JSAKICAgIG11dGF0ZShwcmVzZW50YXRpb24gPSBkcm9wbGV2ZWxzKHByZXNlbnRhdGlvbikpLAogIHBsb3Rfb2JzX2V4cF9yaWdodCAlPiUgZmlsdGVyKHRpbWUgPT0gJ0FmdGVyIGFkbWlzc2lvbicpICU+JSBzbGljZShtYXRjaChzb3J0ZWRfaWNkcywgZnVsbF9pY2QpKSAlPiUgCiAgICBtdXRhdGUocHJlc2VudGF0aW9uID0gZHJvcGxldmVscyhwcmVzZW50YXRpb24pKSwKICBudWRnZSA9IDEuNSkKCmVucmljaG1lbnRfcGxvdCA8LSBjb3dwbG90OjpwbG90X2dyaWQoCiAgZW5yaWNobWVudF9wbG90X2JlZm9yZSwKICBlbnJpY2htZW50X3Bsb3RfYWZ0ZXIsCiAgbmNvbCA9IDEsIGhqdXN0ID0gLTAuMDUsCiAgbGFiZWxzID0gYygnQS4gQmVmb3JlIGFkbWlzc2lvbicsICdCLiBBZnRlciBhZG1pc3Npb24nKQopCmVucmljaG1lbnRfcGxvdApnZ3NhdmUoJ2ZpZ3MvaWNkOV9zZXZlcmVfYWZ0ZXIucG5nJywgZW5yaWNobWVudF9wbG90X2FmdGVyLCB3aWR0aCA9IDkuNSwgaGVpZ2h0ID0gMy41KQpnZ3NhdmUoJ2ZpZ3MvdGlmZnMvZWZpZ181LnRpZmYnLCBlbnJpY2htZW50X3Bsb3RfYWZ0ZXIsIHdpZHRoID0gOS41LCBoZWlnaHQgPSAzLjUsIGRwaSA9IDMwMCkKYGBgCgpGaWd1cmUgY2FwdGlvbjogCkVhY2ggSUNEIGNvZGUncyBsb2cyIGVucmljaG1lbnQgKExPRSkgYW5kIGl0cyA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCAobGVmdCksIGFuZCB0aGUgYWJzb2x1dGUgZGlmZmVyZW5jZSBiZXR3ZWVuIG9ic2VydmVkICh0cmlhbmdsZSkgYW5kIGV4cGVjdGVkIChjaXJjbGUpIG51bWJlciBvZiAqc2V2ZXJlIHBhdGllbnRzKiAocmlnaHQpIGJlZm9yZSBhZG1pc3Npb24uClBvc2l0aXZlIHZhbHVlIG9mIExPRSBpbmRpY2F0ZXMgYSBoaWdoZXIgcHJvcG9ydGlvbiBvZiAqc2V2ZXJlIHBhdGllbnRzKiB3aXRoIHRoYXQgSUNEIGNvZGUgY29tcGFyZWQgdG8gKm5vbi1zZXZlcmUgcGF0aWVudHMqLgpOZXVyb2xvZ2ljYWwgSUNEIGNvZGVzIGFyZSBvcmRlcmVkIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgKnNldmVyZSBwYXRpZW50cyouCkRpZmZlcmVuY2UgaGFzIGJlZW4gcm91bmRlZC4KCmBgYHtyIGZpZy53aWR0aD05fQpwbG90X2VucmljaF9ib3RoIDwtIHBsb3Rfb2JzX2V4cCAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gZmN0X3JlbGV2ZWwoZnVsbF9pY2QsIHNvcnRlZF9pY2RzKSkpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksIGxpbmV0eXBlID0gMikgKwogIGdnc3RhbmNlOjpnZW9tX3BvaW50cmFuZ2VoKAogICAgYWVzKHggPSBsZXN0aW1hdGUsCiAgICAgICAgeG1pbiA9IGxsb3dlciwKICAgICAgICB4bWF4ID0gbHVwcGVyLAogICAgICAgIGNvbG9yID0gdGltZSksCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC4zKSwgCiAgICBzdHJva2UgPSAwLjEsIGZhdHRlbiA9IDMsIHNpemUgPSAwLjcgIAogICkgKwogIGxhYnMoeSA9IE5VTEwsIHggPSBicXVvdGUoTG9nWzJdIH4gJ2VucmljaG1lbnQsIDk1JSBDSScpKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbig1LjUsIDIsIDUuNSwgNS41LCB1bml0ID0gJ3B0JykKICApICsKICBzY2FsZV9jb2xvcl9jYXJ0b19kKHBhbGV0dGUgPSA0LCBkaXJlY3Rpb24gPSAtMSkgKwogIE5VTEwKcGxvdF9lbnJpY2hfYm90aApnZ3NhdmUoJ2ZpZ3MvaWNkOV9zZXZlcmUucG5nJywgcGxvdF9lbnJpY2hfYm90aCwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpgYGAK