Patient Selection

  • Patients meeting inclusion criteria: 16242
  • Reasons for exclusion:
    • Pregnant: 203
    • Prisoner: 77
    • ICU stay < 12 hours: 1077
    • Diagnosis coding contained both ICD-9-CM and ICD-10-CM: 548
    • Missing data needed to calculate risk scores: 9165
    • Re-encounters (only included each patient once) : 1782
  • Total number of eligible patients remaining to sample from: 3390

Patient Demographics

deceased <- "Deceased/Hospice"
home <- "Home"
transfer <- "Transferred"
dispos <- list("Deceased" = deceased,
               "Expired/Donor" = deceased, 
               "Hospice-Home" = deceased, 
               "Hospice-Medical Facility" = deceased,
               "Home Care with Home Health" = home,
               "Home or Self Care" = home,
               "Left Against Medical Advise" = home,
               "Skilled Nursing Facility" = transfer,
               "DC/DISC TO REHAB" = transfer,
               "Long Term Care" = transfer,
               "Intermediate Care" = transfer,
               "Acute Care" = transfer,
               "Federal Healthcare Facility" = transfer,
               "DC/TF To Psych Hosp" = transfer)
df <- data_demographics %>%
    dmap_at("disposition", str_replace_all, pattern = dispos) %>%
    dmap_at("race", factor) %>%
    dmap_at("race", fct_lump, n = 4) %>%
    select(-pie.id, Age = age, Sex = sex, Race = race, Disposition = disposition, `Hospital Length of Stay (days)` = length.stay, `ICU Location` = location) 
vars <- names(df)
tbl <- CreateTableOne(vars, data = df)
ptbl <- print(tbl, printToggle = FALSE)
rownames(ptbl) <- str_replace_all(rownames(ptbl), "   ", "- ")
knitr::kable(ptbl, caption = "Demographics")
Overall
n 200
Age (mean (sd)) 60.68 (16.54)
Sex = Male (%) 123 (61.5)
Race (%)
- African American 49 (28.0)
- Asian 8 ( 4.6)
- White/Caucasian 73 (41.7)
- Other 45 (25.7)
Disposition (%)
- Deceased/Hospice 37 (18.5)
- Home 109 (54.5)
- Transferred 54 (27.0)
Hospital Length of Stay (days) (mean (sd)) 12.07 (12.04)
ICU Location (%)
- Cullen 2 E Medical Intensive Care Unit 60 (30.0)
- Hermann 3 Shock Trauma Intensive Care Unit 14 ( 7.0)
- Hermann 3 Transplant Surgical ICU 19 ( 9.5)
- HVI Cardiac Care Unit 20 (10.0)
- HVI Cardiovascular Intensive Care Unit 34 (17.0)
- Jones 7 J Elective Neuro ICU 53 (26.5)

Primary endpoint

APACHE II

  • Mean and median score for each comorbidity set
scores <- ls(pattern = "score_apache2")
all_scores <- map(scores, ~mutate(get(.x), score = .x)) %>%
    map(~select(.x, pie.id, score, apache2)) %>%
    map_df(~semi_join(.x, manual_patients, by = "pie.id")) %>%
    arrange(pie.id, score) %>%
    dmap_at("score", str_replace_all, pattern = "score_apache2_", replacement = "") 
man <- filter(all_scores, score == "manual")$apache2
result <- all_scores %>%
    select(-pie.id) %>%
    group_by(score) %>%
    summarize_all(funs(mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(result)
score mean sd median IQR_low IQR_high
ahrq 20.20 6.858220 20 15 24.00
ahrq_drg 19.79 6.832225 19 15 24.00
ek 20.05 6.909865 20 15 24.00
ek_drg 19.65 6.866824 19 15 24.00
elixhauser 20.18 6.927304 20 15 24.00
elixhauser_drg 19.77 6.900375 19 15 24.00
manual 19.87 7.020651 19 15 24.25
quan 20.18 6.927304 20 15 24.00
quan_drg 19.78 6.891959 19 15 24.00
ggplot(all_scores, aes(x = score, y = apache2)) +
    geom_boxplot() 

    # geom_jitter(color = "light blue", alpha = 0.6)
ggplot(all_scores, aes(x = apache2)) +
    geom_histogram(binwidth = 0.5) +
    facet_wrap(~ score)

df <- all_scores %>%
    spread(score, apache2) %>%
    gather(score, apache2, -pie.id, -manual)
# ggplot(df, aes(x = apache2, y = manual)) +
#     geom_jitter(aes(color = score), alpha = 0.6) 
ggplot(df, aes(x = apache2, y = manual)) +
    geom_point() +
    # geom_smooth() +
    facet_wrap(~ score)

  • Comparison of scores
    • Results run using both the t-test and Wilcoxon signed rank test (assumes non-parametric data, is more difficult to achieve significance)
    • To account for performing multiple comparisons, a BH (Benjamini and Hochberg) correction is applied to the p-values; use the bh.adjust value as the p-value
t <- all_scores[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(t.test(.$apache2, man, paired = TRUE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(t, digits = 4)
score estimate statistic p.value bh.adjust parameter conf.low conf.high method alternative
ahrq 0.33 2.0591 0.0408 0.1306 199 0.0140 0.6460 Paired t-test two.sided
ahrq_drg -0.08 -0.4981 0.6190 0.6190 199 -0.3967 0.2367 Paired t-test two.sided
ek 0.18 1.1170 0.2653 0.4245 199 -0.1378 0.4978 Paired t-test two.sided
ek_drg -0.22 -1.3754 0.1706 0.3411 199 -0.5354 0.0954 Paired t-test two.sided
elixhauser 0.31 1.9808 0.0490 0.1306 199 0.0014 0.6186 Paired t-test two.sided
elixhauser_drg -0.10 -0.6386 0.5238 0.6190 199 -0.4088 0.2088 Paired t-test two.sided
quan 0.31 1.9808 0.0490 0.1306 199 0.0014 0.6186 Paired t-test two.sided
quan_drg -0.09 -0.5734 0.5670 0.6190 199 -0.3995 0.2195 Paired t-test two.sided
mw <- all_scores[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(wilcox.test(.$apache2, man, paired = TRUE, conf.int = TRUE, exact = FALSE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(mw, digits = 4)
score estimate statistic p.value bh.adjust conf.low conf.high method alternative
ahrq 1.5 1644.0 0.0563 0.1676 -0.0001 2.0000 Wilcoxon signed rank test with continuity correction two.sided
ahrq_drg 0.0 1070.0 0.5179 0.5179 -1.5000 0.0000 Wilcoxon signed rank test with continuity correction two.sided
ek 0.0 1448.5 0.3147 0.5036 -0.0001 2.0000 Wilcoxon signed rank test with continuity correction two.sided
ek_drg 0.0 936.0 0.1357 0.2715 -1.5000 0.0000 Wilcoxon signed rank test with continuity correction two.sided
elixhauser 1.5 1551.0 0.0628 0.1676 -0.0001 1.9999 Wilcoxon signed rank test with continuity correction two.sided
elixhauser_drg 0.0 990.0 0.4483 0.5179 -1.5000 0.0001 Wilcoxon signed rank test with continuity correction two.sided
quan 1.5 1551.0 0.0628 0.1676 -0.0001 1.9999 Wilcoxon signed rank test with continuity correction two.sided
quan_drg 0.0 1030.5 0.4863 0.5179 -1.5000 0.0000 Wilcoxon signed rank test with continuity correction two.sided
options(scipen = 999)
mw_pair <- all_scores %>%
    dmap_at("score", factor) %>%
    dmap_at("score", ~fct_relevel(.x, c("manual", "ek", "ek_drg"))) %>%
    do(tidy(pairwise.wilcox.test(.$apache2, .$score, p.adj = "BH", paired = TRUE, exact = FALSE)))

Linear Regression Model

lm <- df %>%
    group_by(score) %>%
    do(fit = lm(manual ~ apache2, data = .))
knitr::kable(glance(lm, fit), digits = 4)
score r.squared adj.r.squared sigma statistic p.value df logLik AIC BIC deviance df.residual
ahrq 0.8967 0.8962 2.2624 1718.366 0 2 -446.0658 898.1315 908.0265 1013.4322 198
ahrq_drg 0.8960 0.8955 2.2695 1706.410 0 2 -446.6916 899.3832 909.2782 1019.7944 198
ek 0.8960 0.8955 2.2693 1706.705 0 2 -446.6761 899.3522 909.2472 1019.6365 198
ek_drg 0.8971 0.8966 2.2574 1726.889 0 2 -445.6220 897.2440 907.1390 1008.9449 198
elixhauser 0.9020 0.9015 2.2035 1822.143 0 2 -440.7920 887.5840 897.4790 961.3710 198
elixhauser_drg 0.9016 0.9011 2.2078 1814.341 0 2 -441.1790 888.3579 898.2529 965.0983 198
quan 0.9020 0.9015 2.2035 1822.143 0 2 -440.7920 887.5840 897.4790 961.3710 198
quan_drg 0.9011 0.9006 2.2135 1804.009 0 2 -441.6937 889.3874 899.2824 970.0789 198
augment(lm, fit) %>%
    ggplot(aes(x = .fitted, y = .resid)) + 
    geom_point() +
    # geom_smooth(se = FALSE) +
    facet_wrap(~ score)

mod_df <- df %>%
    filter(score == "ek_drg")
mod <- lm(manual ~ apache2, data = mod_df)
par(mfrow = c(2, 2))
plot(mod)

  • Compare differences in score from comorbidity sets vs. manual
    • Column num_diff indicates the number of patients with a score that differed from manual
    • Mean and median indicate the difference from the manual score
      • These were only calculated for those patients whose score was different from manual
      • If all patients were included, the number is skewed towards 0 by all the patients with the same scores
diff <- all_scores %>%
    spread(score, apache2) %>%
    mutate_at(vars(-pie.id, -manual), funs(abs(. - manual))) %>%
    gather(score, apache2, -pie.id, -manual) %>%
    select(-pie.id, -manual) %>%
    filter(apache2 > 0) %>%
    group_by(score) %>%
    summarize_all(funs(num_diff = length, mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(diff, caption = "Differences in APACHE II Scores")
score num_diff mean sd median IQR_low IQR_high
ahrq 72 3.500000 1.510526 3.5 2 5
ahrq_drg 68 3.588235 1.508536 5.0 2 5
ek 71 3.521127 1.510526 5.0 2 5
ek_drg 68 3.588235 1.508536 5.0 2 5
elixhauser 70 3.457143 1.510214 2.0 2 5
elixhauser_drg 66 3.545454 1.510800 5.0 2 5
quan 70 3.457143 1.510214 2.0 2 5
quan_drg 67 3.522388 1.511153 5.0 2 5

APACHE III

scores <- ls(pattern = "score_apache3")
all_scores <- map(scores, ~mutate(get(.x), score = .x)) %>%
    map(~select(.x, pie.id, score, apache3)) %>%
    map_df(~semi_join(.x, manual_patients, by = "pie.id")) %>%
    arrange(pie.id, score) %>%
    dmap_at("score", str_replace_all, pattern = "score_apache3_", replacement = "") 
man <- filter(all_scores, score == "manual")$apache3
result <- all_scores %>%
    select(-pie.id) %>%
    group_by(score) %>%
    summarize_all(funs(mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(result)
score mean sd median IQR_low IQR_high
ahrq 85.715 29.29976 82.0 65.75 103.25
ahrq_drg 85.555 29.25932 82.0 65.00 103.25
ek 85.715 29.29976 82.0 65.75 103.25
ek_drg 85.555 29.25932 82.0 65.00 103.25
elixhauser 85.715 29.29976 82.0 65.75 103.25
elixhauser_drg 85.555 29.25932 82.0 65.00 103.25
manual 84.805 28.99847 81.5 63.75 103.00
quan 85.715 29.29976 82.0 65.75 103.25
quan_drg 85.555 29.25932 82.0 65.00 103.25
ggplot(all_scores, aes(x = score, y = apache3)) +
    geom_boxplot()

df <- all_scores %>%
    spread(score, apache3) %>%
    gather(score, apache3, -pie.id, -manual)
ggplot(df, aes(x = apache3, y = manual)) +
    geom_point() +
    # geom_smooth() +
    facet_wrap(~ score)

  • Comparison of scores
t <- all_scores[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(t.test(.$apache3, man, paired = TRUE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(t, digits = 4)
score estimate statistic p.value bh.adjust parameter conf.low conf.high method alternative
ahrq 0.91 2.9503 0.0036 0.0071 199 0.3018 1.5182 Paired t-test two.sided
ahrq_drg 0.75 2.5453 0.0117 0.0117 199 0.1690 1.3310 Paired t-test two.sided
ek 0.91 2.9503 0.0036 0.0071 199 0.3018 1.5182 Paired t-test two.sided
ek_drg 0.75 2.5453 0.0117 0.0117 199 0.1690 1.3310 Paired t-test two.sided
elixhauser 0.91 2.9503 0.0036 0.0071 199 0.3018 1.5182 Paired t-test two.sided
elixhauser_drg 0.75 2.5453 0.0117 0.0117 199 0.1690 1.3310 Paired t-test two.sided
quan 0.91 2.9503 0.0036 0.0071 199 0.3018 1.5182 Paired t-test two.sided
quan_drg 0.75 2.5453 0.0117 0.0117 199 0.1690 1.3310 Paired t-test two.sided
mw <- all_scores[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(wilcox.test(.$apache3, man, paired = TRUE, conf.int = TRUE, exact = FALSE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(mw, digits = 4)
score estimate statistic p.value bh.adjust conf.low conf.high method alternative
ahrq 9.0000 283.5 0.0062 0.0123 2.9999 13.0 Wilcoxon signed rank test with continuity correction two.sided
ahrq_drg 7.0001 251.0 0.0176 0.0176 1.5000 11.5 Wilcoxon signed rank test with continuity correction two.sided
ek 9.0000 283.5 0.0062 0.0123 2.9999 13.0 Wilcoxon signed rank test with continuity correction two.sided
ek_drg 7.0001 251.0 0.0176 0.0176 1.5000 11.5 Wilcoxon signed rank test with continuity correction two.sided
elixhauser 9.0000 283.5 0.0062 0.0123 2.9999 13.0 Wilcoxon signed rank test with continuity correction two.sided
elixhauser_drg 7.0001 251.0 0.0176 0.0176 1.5000 11.5 Wilcoxon signed rank test with continuity correction two.sided
quan 9.0000 283.5 0.0062 0.0123 2.9999 13.0 Wilcoxon signed rank test with continuity correction two.sided
quan_drg 7.0001 251.0 0.0176 0.0176 1.5000 11.5 Wilcoxon signed rank test with continuity correction two.sided

Linear Regression Model

lm <- df %>%
    group_by(score) %>%
    do(fit = lm(manual ~ apache3, data = .))
knitr::kable(glance(lm, fit), digits = 4)
score r.squared adj.r.squared sigma statistic p.value df logLik AIC BIC deviance df.residual
ahrq 0.9778 0.9777 4.3280 8735.812 0 2 -575.8020 1157.604 1167.499 3708.786 198
ahrq_drg 0.9797 0.9796 4.1402 9564.716 0 2 -566.9292 1139.858 1149.753 3393.891 198
ek 0.9778 0.9777 4.3280 8735.812 0 2 -575.8020 1157.604 1167.499 3708.786 198
ek_drg 0.9797 0.9796 4.1402 9564.716 0 2 -566.9292 1139.858 1149.753 3393.891 198
elixhauser 0.9778 0.9777 4.3280 8735.812 0 2 -575.8020 1157.604 1167.499 3708.786 198
elixhauser_drg 0.9797 0.9796 4.1402 9564.716 0 2 -566.9292 1139.858 1149.753 3393.891 198
quan 0.9778 0.9777 4.3280 8735.812 0 2 -575.8020 1157.604 1167.499 3708.786 198
quan_drg 0.9797 0.9796 4.1402 9564.716 0 2 -566.9292 1139.858 1149.753 3393.891 198
augment(lm, fit) %>%
    ggplot(aes(x = .fitted, y = .resid)) + 
    geom_point() +
    facet_wrap(~ score)

  • Differences in score
diff <- all_scores %>%
    spread(score, apache3) %>%
    mutate_at(vars(-pie.id, -manual), funs(abs(. - manual))) %>%
    gather(score, apache3, -pie.id, -manual) %>%
    select(-pie.id, -manual) %>%
    filter(apache3 > 0) %>%
    group_by(score) %>%
    summarize_all(funs(num_diff = length, mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(diff, caption = "Difference in APACHE III Scores")
score num_diff mean sd median IQR_low IQR_high
ahrq 26 11.38462 4.825573 12 10 16
ahrq_drg 25 10.88000 5.035871 12 6 16
ek 26 11.38462 4.825573 12 10 16
ek_drg 25 10.88000 5.035871 12 6 16
elixhauser 26 11.38462 4.825573 12 10 16
elixhauser_drg 25 10.88000 5.035871 12 6 16
quan 26 11.38462 4.825573 12 10 16
quan_drg 25 10.88000 5.035871 12 6 16

SAPS II

scores <- ls(pattern = "score_saps2")
all_scores <- map(scores, ~mutate(get(.x), score = .x)) %>%
    map(~select(.x, pie.id, score, saps2)) %>%
    map_df(~semi_join(.x, manual_patients, by = "pie.id")) %>%
    arrange(pie.id, score) %>%
    dmap_at("score", str_replace_all, pattern = "score_saps2_", replacement = "") 
man <- filter(all_scores, score == "manual")$saps2
result <- all_scores %>%
    select(-pie.id) %>%
    group_by(score) %>%
    summarize_all(funs(mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(result)
score mean sd median IQR_low IQR_high
ahrq 37.12 14.56386 36.0 26 47.00
ahrq_drg 37.12 14.56386 36.0 26 47.00
ek 37.12 14.56386 36.0 26 47.00
ek_drg 37.12 14.56386 36.0 26 47.00
elixhauser 37.12 14.56386 36.0 26 47.00
elixhauser_drg 37.12 14.56386 36.0 26 47.00
manual 36.94 14.44888 34.5 26 46.25
quan 37.12 14.56386 36.0 26 47.00
quan_drg 37.12 14.56386 36.0 26 47.00
ggplot(all_scores, aes(x = score, y = saps2)) +
    geom_boxplot()

df <- all_scores %>%
    spread(score, saps2) %>%
    gather(score, saps2, -pie.id, -manual)
ggplot(df, aes(x = saps2, y = manual)) +
    geom_point() +
    # geom_smooth() +
    facet_wrap(~ score)

  • Comparison of scores
t <- all_scores[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(t.test(.$saps2, man, paired = TRUE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(t, digits = 4)
score estimate statistic p.value bh.adjust parameter conf.low conf.high method alternative
ahrq 0.18 1.3406 0.1816 0.1816 199 -0.0848 0.4448 Paired t-test two.sided
ahrq_drg 0.18 1.3406 0.1816 0.1816 199 -0.0848 0.4448 Paired t-test two.sided
ek 0.18 1.3406 0.1816 0.1816 199 -0.0848 0.4448 Paired t-test two.sided
ek_drg 0.18 1.3406 0.1816 0.1816 199 -0.0848 0.4448 Paired t-test two.sided
elixhauser 0.18 1.3406 0.1816 0.1816 199 -0.0848 0.4448 Paired t-test two.sided
elixhauser_drg 0.18 1.3406 0.1816 0.1816 199 -0.0848 0.4448 Paired t-test two.sided
quan 0.18 1.3406 0.1816 0.1816 199 -0.0848 0.4448 Paired t-test two.sided
quan_drg 0.18 1.3406 0.1816 0.1816 199 -0.0848 0.4448 Paired t-test two.sided
  • Confidence interval was unable to be calculated for Wilcoxon analysis here
mw <- all_scores[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(wilcox.test(.$saps2, man, paired = TRUE, exact = FALSE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(mw, digits = 4)
score statistic p.value bh.adjust method alternative
ahrq 23 0.5182 0.5182 Wilcoxon signed rank test with continuity correction two.sided
ahrq_drg 23 0.5182 0.5182 Wilcoxon signed rank test with continuity correction two.sided
ek 23 0.5182 0.5182 Wilcoxon signed rank test with continuity correction two.sided
ek_drg 23 0.5182 0.5182 Wilcoxon signed rank test with continuity correction two.sided
elixhauser 23 0.5182 0.5182 Wilcoxon signed rank test with continuity correction two.sided
elixhauser_drg 23 0.5182 0.5182 Wilcoxon signed rank test with continuity correction two.sided
quan 23 0.5182 0.5182 Wilcoxon signed rank test with continuity correction two.sided
quan_drg 23 0.5182 0.5182 Wilcoxon signed rank test with continuity correction two.sided

Linear Regression Model

lm <- df %>%
    group_by(score) %>%
    do(fit = lm(manual ~ saps2, data = .))
knitr::kable(glance(lm, fit), digits = 4)
score r.squared adj.r.squared sigma statistic p.value df logLik AIC BIC deviance df.residual
ahrq 0.983 0.9829 1.8886 11449.91 0 2 -409.9484 825.8968 835.7917 706.2182 198
ahrq_drg 0.983 0.9829 1.8886 11449.91 0 2 -409.9484 825.8968 835.7917 706.2182 198
ek 0.983 0.9829 1.8886 11449.91 0 2 -409.9484 825.8968 835.7917 706.2182 198
ek_drg 0.983 0.9829 1.8886 11449.91 0 2 -409.9484 825.8968 835.7917 706.2182 198
elixhauser 0.983 0.9829 1.8886 11449.91 0 2 -409.9484 825.8968 835.7917 706.2182 198
elixhauser_drg 0.983 0.9829 1.8886 11449.91 0 2 -409.9484 825.8968 835.7917 706.2182 198
quan 0.983 0.9829 1.8886 11449.91 0 2 -409.9484 825.8968 835.7917 706.2182 198
quan_drg 0.983 0.9829 1.8886 11449.91 0 2 -409.9484 825.8968 835.7917 706.2182 198
augment(lm, fit) %>%
    ggplot(aes(x = .fitted, y = .resid)) + 
    geom_point() +
    facet_wrap(~ score)

  • Differences in score
diff <- all_scores %>%
    spread(score, saps2) %>%
    mutate_at(vars(-pie.id, -manual), funs(abs(. - manual))) %>%
    gather(score, saps2, -pie.id, -manual) %>%
    select(-pie.id, -manual) %>%
    filter(saps2 > 0) %>%
    group_by(score) %>%
    summarize_all(funs(num_diff = length, mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(diff, caption = "Difference in SAPS II Scores")
score num_diff mean sd median IQR_low IQR_high
ahrq 8 9.5 0.5345225 9.5 9 10
ahrq_drg 8 9.5 0.5345225 9.5 9 10
ek 8 9.5 0.5345225 9.5 9 10
ek_drg 8 9.5 0.5345225 9.5 9 10
elixhauser 8 9.5 0.5345225 9.5 9 10
elixhauser_drg 8 9.5 0.5345225 9.5 9 10
quan 8 9.5 0.5345225 9.5 9 10
quan_drg 8 9.5 0.5345225 9.5 9 10

Secondary endpoints

Predicted mortality

SAPS II

  • Mean and median risk of mortality (as a percent) for each comorbidity set
comorbid <- ls(pattern = "score_saps2_")
all_df <- map(comorbid, ~mutate(get(.x), score = .x)) %>%
    map(~select(.x, pie.id, score, saps2)) %>%
    map_df(~semi_join(.x, manual_patients, by = "pie.id")) %>%
    arrange(pie.id, score) %>%
    dmap_at("score", str_replace_all, pattern = "score_saps2_", replacement = "") %>%
    distinct(pie.id, score, .keep_all = TRUE) %>%
    mutate(logit = -7.763 + 0.0737 * saps2 + 0.9971 * log(saps2 + 1),
           mortality = exp(logit) / (1 + exp(logit))) %>%
    select(pie.id, score, mortality) 
man <- filter(all_df, score == "manual")$mortality
result <- all_df %>%
    select(-pie.id) %>%
    group_by(score) %>%
    summarize_all(funs(mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(result, digits = 4)
score mean sd median IQR_low IQR_high
ahrq 0.2513 0.2299 0.1810 0.0717 0.3919
ahrq_drg 0.2513 0.2299 0.1810 0.0717 0.3919
ek 0.2513 0.2299 0.1810 0.0717 0.3919
ek_drg 0.2513 0.2299 0.1810 0.0717 0.3919
elixhauser 0.2513 0.2299 0.1810 0.0717 0.3919
elixhauser_drg 0.2513 0.2299 0.1810 0.0717 0.3919
manual 0.2476 0.2280 0.1597 0.0717 0.3752
quan 0.2513 0.2299 0.1810 0.0717 0.3919
quan_drg 0.2513 0.2299 0.1810 0.0717 0.3919
t <- all_df[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>% 
    do(tidy(t.test(.$mortality, man, paired = TRUE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(t, digits = 4)
score estimate statistic p.value bh.adjust parameter conf.low conf.high method alternative
ahrq 0.0037 1.8305 0.0687 0.0687 199 -0.0003 0.0077 Paired t-test two.sided
ahrq_drg 0.0037 1.8305 0.0687 0.0687 199 -0.0003 0.0077 Paired t-test two.sided
ek 0.0037 1.8305 0.0687 0.0687 199 -0.0003 0.0077 Paired t-test two.sided
ek_drg 0.0037 1.8305 0.0687 0.0687 199 -0.0003 0.0077 Paired t-test two.sided
elixhauser 0.0037 1.8305 0.0687 0.0687 199 -0.0003 0.0077 Paired t-test two.sided
elixhauser_drg 0.0037 1.8305 0.0687 0.0687 199 -0.0003 0.0077 Paired t-test two.sided
quan 0.0037 1.8305 0.0687 0.0687 199 -0.0003 0.0077 Paired t-test two.sided
quan_drg 0.0037 1.8305 0.0687 0.0687 199 -0.0003 0.0077 Paired t-test two.sided
mw <- all_df[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(wilcox.test(.$mortality, man, paired = TRUE, conf.int = TRUE, exact = FALSE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(mw, digits = 4)
score estimate statistic p.value bh.adjust conf.low conf.high method alternative
ahrq 0.1146 31 0.0801 0.0801 -0.0088 0.1865 Wilcoxon signed rank test with continuity correction two.sided
ahrq_drg 0.1146 31 0.0801 0.0801 -0.0088 0.1865 Wilcoxon signed rank test with continuity correction two.sided
ek 0.1146 31 0.0801 0.0801 -0.0088 0.1865 Wilcoxon signed rank test with continuity correction two.sided
ek_drg 0.1146 31 0.0801 0.0801 -0.0088 0.1865 Wilcoxon signed rank test with continuity correction two.sided
elixhauser 0.1146 31 0.0801 0.0801 -0.0088 0.1865 Wilcoxon signed rank test with continuity correction two.sided
elixhauser_drg 0.1146 31 0.0801 0.0801 -0.0088 0.1865 Wilcoxon signed rank test with continuity correction two.sided
quan 0.1146 31 0.0801 0.0801 -0.0088 0.1865 Wilcoxon signed rank test with continuity correction two.sided
quan_drg 0.1146 31 0.0801 0.0801 -0.0088 0.1865 Wilcoxon signed rank test with continuity correction two.sided
  • Difference in risk of mortality
    • Mean and median calculated using only those patients whose predicted mortality score was different from manual
diff <- all_df %>%
    spread(score, mortality) %>%
    mutate_at(vars(-pie.id, -manual), funs(abs(. - manual))) %>%
    gather(score, saps2, -pie.id, -manual) %>%
    select(-pie.id, -manual) %>%
    filter(saps2 > 0) %>%
    group_by(score) %>%
    summarize_all(funs(num_diff = length, mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(diff, caption = "Difference in Predicted Mortality using SAPS II")
score num_diff mean sd median IQR_low IQR_high
ahrq 8 0.1353385 0.0544702 0.1366591 0.0862261 0.1691449
ahrq_drg 8 0.1353385 0.0544702 0.1366591 0.0862261 0.1691449
ek 8 0.1353385 0.0544702 0.1366591 0.0862261 0.1691449
ek_drg 8 0.1353385 0.0544702 0.1366591 0.0862261 0.1691449
elixhauser 8 0.1353385 0.0544702 0.1366591 0.0862261 0.1691449
elixhauser_drg 8 0.1353385 0.0544702 0.1366591 0.0862261 0.1691449
quan 8 0.1353385 0.0544702 0.1366591 0.0862261 0.1691449
quan_drg 8 0.1353385 0.0544702 0.1366591 0.0862261 0.1691449

Number of comorbidities

APACHE II

  • Mean represents the average number of comorbidities per patient
comorbid <- ls(pattern = "data_apache2_")
all_df <- map(comorbid, ~mutate(get(.x), score = .x)) %>%
    map(~distinct(.x, pie.id, score, liver, cardiovasc, respiratory, renal, immunocomp, comorbidity)) %>%
    map_df(~semi_join(.x, manual_patients, by = "pie.id")) %>%
    arrange(pie.id, score) %>%
    dmap_at("score", str_replace_all, pattern = "data_apache2_", replacement = "") %>%
    mutate_if(is.logical, as.numeric) %>%
    by_row(function(x) sum(x[, 3:7], na.rm = TRUE), .collate = "rows", .to = "num_comorbid") %>%
    select(pie.id, score, num_comorbid) 
man <- filter(all_df, score == "manual")$num_comorbid
result <- all_df %>%
    select(-pie.id) %>%
    group_by(score) %>%
    summarize_all(funs(mean, sd, median, IQR_low = quantile(., 0.25), IQR_high = quantile(., 0.75)))
knitr::kable(result)
score mean sd median IQR_low IQR_high
ahrq 0.760 0.7314885 1 0 1
ahrq_drg 0.595 0.6732319 0 0 1
ek 0.690 0.7186671 1 0 1
ek_drg 0.530 0.6412143 0 0 1
elixhauser 0.745 0.7366739 1 0 1
elixhauser_drg 0.575 0.6607252 0 0 1
manual 0.605 0.7222070 0 0 1
quan 0.750 0.7417892 1 0 1
quan_drg 0.585 0.6670832 0 0 1
t <- all_df[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(t.test(.$num_comorbid, man, paired = TRUE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(t, digits = 4)
score estimate statistic p.value bh.adjust parameter conf.low conf.high method alternative
ahrq 0.155 2.5932 0.0102 0.0513 199 0.0371 0.2729 Paired t-test two.sided
ahrq_drg -0.010 -0.1737 0.8623 0.8623 199 -0.1236 0.1036 Paired t-test two.sided
ek 0.085 1.4253 0.1556 0.2997 199 -0.0326 0.2026 Paired t-test two.sided
ek_drg -0.075 -1.3232 0.1873 0.2997 199 -0.1868 0.0368 Paired t-test two.sided
elixhauser 0.140 2.3598 0.0193 0.0513 199 0.0230 0.2570 Paired t-test two.sided
elixhauser_drg -0.030 -0.5336 0.5942 0.7923 199 -0.1409 0.0809 Paired t-test two.sided
quan 0.145 2.4379 0.0157 0.0513 199 0.0277 0.2623 Paired t-test two.sided
quan_drg -0.020 -0.3528 0.7246 0.8281 199 -0.1318 0.0918 Paired t-test two.sided
mw <- all_df[, -1] %>%
    filter(score != "manual") %>%
    group_by(score) %>%
    do(tidy(wilcox.test(.$num_comorbid, man, paired = TRUE, conf.int = TRUE, exact = FALSE))) %>%
    ungroup() %>%
    mutate(bh.adjust = p.adjust(p.value, "BH")) %>%
    select(score:p.value, bh.adjust, everything())
knitr::kable(mw, digits = 4)
score estimate statistic p.value bh.adjust conf.low conf.high method alternative
ahrq 0 3059.5 0.0077 0.0521 0.0001 1.0000 Wilcoxon signed rank test with continuity correction two.sided
ahrq_drg 0 1933.0 0.9118 0.9118 0.0000 0.0000 Wilcoxon signed rank test with continuity correction two.sided
ek 0 2553.5 0.1276 0.2552 0.0000 0.5000 Wilcoxon signed rank test with continuity correction two.sided
ek_drg 0 1560.5 0.2053 0.3285 -0.5000 0.0000 Wilcoxon signed rank test with continuity correction two.sided
elixhauser 0 2752.5 0.0196 0.0521 0.0000 0.5000 Wilcoxon signed rank test with continuity correction two.sided
elixhauser_drg 0 1672.0 0.5878 0.7837 0.0000 0.0001 Wilcoxon signed rank test with continuity correction two.sided
quan 0 2826.0 0.0161 0.0521 0.0000 0.5000 Wilcoxon signed rank test with continuity correction two.sided
quan_drg 0 1791.0 0.7129 0.8148 0.0000 0.0000 Wilcoxon signed rank test with continuity correction two.sided

Proportion with differing comorbidity

APACHE II

  • Proportion of patients where a comorbidity was identified by ICD or by manual, but not both
comorbid <- ls(pattern = "data_apache2_")
all_df <- map(comorbid, ~mutate(get(.x), score = .x)) %>%
    map(~distinct(.x, pie.id, score, comorbidity)) %>%
    map_df(~semi_join(.x, manual_patients, by = "pie.id")) %>%
    arrange(pie.id, score) %>%
    dmap_at("score", str_replace_all, pattern = "data_apache2_", replacement = "") %>%
    spread(score, comorbidity) %>%
    mutate_at(vars(-pie.id, -manual), funs(. != manual)) %>%
    mutate_if(is.logical, as.numeric) %>%
    ungroup() %>%
    gather(score, comorbidity, -pie.id, -manual) %>%
    select(-pie.id, -manual) %>%
    group_by(score) %>%
    summarize_all(funs(num_diff = sum(. > 0), proportion = mean))
knitr::kable(all_df)
score num_diff proportion
ahrq 72 0.360
ahrq_drg 68 0.340
ek 71 0.355
ek_drg 68 0.340
elixhauser 70 0.350
elixhauser_drg 66 0.330
quan 70 0.350
quan_drg 67 0.335

APACHE III

  • Proportion of patients where the primary comorbidity identified by ICD varied from primary comorbidity identified by manual
comorbid <- ls(pattern = "data_apache3_")
all_df <- map(comorbid, ~mutate(get(.x), score = .x)) %>%
    map(~distinct(.x, pie.id, score, comorbidity)) %>%
    map_df(~semi_join(.x, manual_patients, by = "pie.id")) %>%
    arrange(pie.id, score) %>%
    dmap_at("score", str_replace_all, pattern = "data_apache3_", replacement = "") %>%
    spread(score, comorbidity, fill = "") %>%
    dmap(str_replace_all, pattern = "none", replacement = "") %>%
    mutate_at(vars(-pie.id, -manual), funs(. != manual)) %>%
    mutate_if(is.logical, as.numeric) %>%
    gather(score, comorbidity, -pie.id, -manual) %>%
    select(-pie.id, -manual) %>%
    group_by(score) %>%
    summarize_all(funs(num_diff = sum(. > 0), proportion = mean))
knitr::kable(all_df)
score num_diff proportion
ahrq 32 0.160
ahrq_drg 31 0.155
ek 32 0.160
ek_drg 31 0.155
elixhauser 32 0.160
elixhauser_drg 31 0.155
quan 32 0.160
quan_drg 31 0.155

SAPS II

  • Proportion of patients where the primary comorbidity identified by ICD varied from primary comorbidity identified by manual
comorbid <- ls(pattern = "data_saps2_")
all_df <- map(comorbid, ~mutate(get(.x), score = .x)) %>%
    map(~distinct(.x, pie.id, score, comorbidity)) %>%
    map_df(~semi_join(.x, manual_patients, by = "pie.id")) %>%
    arrange(pie.id, score) %>%
    dmap_at("score", str_replace_all, pattern = "data_saps2_", replacement = "") %>%
    spread(score, comorbidity, fill = "") %>%
    dmap(str_replace_all, pattern = "none", replacement = "") %>%
    mutate_at(vars(-pie.id, -manual), funs(. != manual)) %>%
    mutate_if(is.logical, as.numeric) %>%
    gather(score, comorbidity, -pie.id, -manual) %>%
    select(-pie.id, -manual) %>%
    group_by(score) %>%
    summarize_all(funs(num_diff = sum(. > 0), proportion = mean))
knitr::kable(all_df)
score num_diff proportion
ahrq 8 0.04
ahrq_drg 8 0.04
ek 8 0.04
ek_drg 8 0.04
elixhauser 8 0.04
elixhauser_drg 8 0.04
quan 8 0.04
quan_drg 8 0.04
LS0tDQp0aXRsZTogVmFsaWRhdGlvbiBvZiBJQ0QtOS1DTS9JQ0QtMTAtQ00gQ29kZXMgZm9yIEF1dG9tYXRlZCBFbGVjdHJvbmljIFNjb3Jpbmcgb2YNCiAgQVBBQ0hFIElJLCBBUEFDSEUgSUlJLCBhbmQgU0FQUyBJSQ0Kc3VidGl0bGU6IEFuYWx5c2lzIGZvciBBU0hQIFBvc3Rlcg0KYXV0aG9yOiBFcmljIEthbywgQnJpYW4gR3VsYmlzDQpkYXRlOiAnYHIgZm9ybWF0KFN5cy5EYXRlKCksICIlQiAlZCwgJVkiKWAnDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGZvcmNhdHMpDQpsaWJyYXJ5KHRhYmxlb25lKQ0KbGlicmFyeShicm9vbSkNCg0KZGlycjo6Z2V0X3JkcygiLi4vZGF0YS9maW5hbCIpDQpgYGANCg0KIyMgUGF0aWVudCBTZWxlY3Rpb24NCg0KKiBQYXRpZW50cyBtZWV0aW5nIGluY2x1c2lvbiBjcml0ZXJpYTogYHIgZXhjbHVkZSRzY3JlZW5gDQoqIFJlYXNvbnMgZm9yIGV4Y2x1c2lvbjoNCiAgICAtIFByZWduYW50OiBgciBleGNsdWRlJHByZWduYW50YA0KICAgIC0gUHJpc29uZXI6IGByIGV4Y2x1ZGUkcHJpc29uZXJzYA0KICAgIC0gSUNVIHN0YXkgPCAxMiBob3VyczogYHIgZXhjbHVkZSRpY3Vfc2hvcnRgDQogICAgLSBEaWFnbm9zaXMgY29kaW5nIGNvbnRhaW5lZCBib3RoIElDRC05LUNNIGFuZCBJQ0QtMTAtQ006IGByIGV4Y2x1ZGUkbXVsdF9pY2RfdHlwZXNgDQogICAgLSBNaXNzaW5nIGRhdGEgbmVlZGVkIHRvIGNhbGN1bGF0ZSByaXNrIHNjb3JlczogYHIgZXhjbHVkZSRsYWJzX21pc3NpbmdgDQogICAgLSBSZS1lbmNvdW50ZXJzIChvbmx5IGluY2x1ZGVkIGVhY2ggcGF0aWVudCBvbmNlKSA6IGByIGV4Y2x1ZGUkcmVlbmNvdW50ZXJgDQoqIFRvdGFsIG51bWJlciBvZiBlbGlnaWJsZSBwYXRpZW50cyByZW1haW5pbmcgdG8gc2FtcGxlIGZyb206IGByIGZsYXR0ZW5faW50KGV4Y2x1ZGUpWzFdIC0gc3VtKGZsYXR0ZW5faW50KGV4Y2x1ZGUpWy0xXSlgDQoNCiMjIFBhdGllbnQgRGVtb2dyYXBoaWNzDQoNCmBgYHtyfQ0KZGVjZWFzZWQgPC0gIkRlY2Vhc2VkL0hvc3BpY2UiDQpob21lIDwtICJIb21lIg0KdHJhbnNmZXIgPC0gIlRyYW5zZmVycmVkIg0KDQpkaXNwb3MgPC0gbGlzdCgiRGVjZWFzZWQiID0gZGVjZWFzZWQsDQogICAgICAgICAgICAgICAiRXhwaXJlZC9Eb25vciIgPSBkZWNlYXNlZCwgDQogICAgICAgICAgICAgICAiSG9zcGljZS1Ib21lIiA9IGRlY2Vhc2VkLCANCiAgICAgICAgICAgICAgICJIb3NwaWNlLU1lZGljYWwgRmFjaWxpdHkiID0gZGVjZWFzZWQsDQogICAgICAgICAgICAgICAiSG9tZSBDYXJlIHdpdGggSG9tZSBIZWFsdGgiID0gaG9tZSwNCiAgICAgICAgICAgICAgICJIb21lIG9yIFNlbGYgQ2FyZSIgPSBob21lLA0KICAgICAgICAgICAgICAgIkxlZnQgQWdhaW5zdCBNZWRpY2FsIEFkdmlzZSIgPSBob21lLA0KICAgICAgICAgICAgICAgIlNraWxsZWQgTnVyc2luZyBGYWNpbGl0eSIgPSB0cmFuc2ZlciwNCiAgICAgICAgICAgICAgICJEQy9ESVNDIFRPIFJFSEFCIiA9IHRyYW5zZmVyLA0KICAgICAgICAgICAgICAgIkxvbmcgVGVybSBDYXJlIiA9IHRyYW5zZmVyLA0KICAgICAgICAgICAgICAgIkludGVybWVkaWF0ZSBDYXJlIiA9IHRyYW5zZmVyLA0KICAgICAgICAgICAgICAgIkFjdXRlIENhcmUiID0gdHJhbnNmZXIsDQogICAgICAgICAgICAgICAiRmVkZXJhbCBIZWFsdGhjYXJlIEZhY2lsaXR5IiA9IHRyYW5zZmVyLA0KICAgICAgICAgICAgICAgIkRDL1RGIFRvIFBzeWNoIEhvc3AiID0gdHJhbnNmZXIpDQoNCmRmIDwtIGRhdGFfZGVtb2dyYXBoaWNzICU+JQ0KICAgIGRtYXBfYXQoImRpc3Bvc2l0aW9uIiwgc3RyX3JlcGxhY2VfYWxsLCBwYXR0ZXJuID0gZGlzcG9zKSAlPiUNCiAgICBkbWFwX2F0KCJyYWNlIiwgZmFjdG9yKSAlPiUNCiAgICBkbWFwX2F0KCJyYWNlIiwgZmN0X2x1bXAsIG4gPSA0KSAlPiUNCiAgICBzZWxlY3QoLXBpZS5pZCwgQWdlID0gYWdlLCBTZXggPSBzZXgsIFJhY2UgPSByYWNlLCBEaXNwb3NpdGlvbiA9IGRpc3Bvc2l0aW9uLCBgSG9zcGl0YWwgTGVuZ3RoIG9mIFN0YXkgKGRheXMpYCA9IGxlbmd0aC5zdGF5LCBgSUNVIExvY2F0aW9uYCA9IGxvY2F0aW9uKSANCg0KdmFycyA8LSBuYW1lcyhkZikNCnRibCA8LSBDcmVhdGVUYWJsZU9uZSh2YXJzLCBkYXRhID0gZGYpDQpwdGJsIDwtIHByaW50KHRibCwgcHJpbnRUb2dnbGUgPSBGQUxTRSkNCnJvd25hbWVzKHB0YmwpIDwtIHN0cl9yZXBsYWNlX2FsbChyb3duYW1lcyhwdGJsKSwgIiAgICIsICItICIpDQprbml0cjo6a2FibGUocHRibCwgY2FwdGlvbiA9ICJEZW1vZ3JhcGhpY3MiKQ0KYGBgDQoNCg0KIyMgUHJpbWFyeSBlbmRwb2ludA0KDQojIyMgQVBBQ0hFIElJDQoNCiogTWVhbiBhbmQgbWVkaWFuIHNjb3JlIGZvciBlYWNoIGNvbW9yYmlkaXR5IHNldA0KDQpgYGB7cn0NCnNjb3JlcyA8LSBscyhwYXR0ZXJuID0gInNjb3JlX2FwYWNoZTIiKQ0KDQphbGxfc2NvcmVzIDwtIG1hcChzY29yZXMsIH5tdXRhdGUoZ2V0KC54KSwgc2NvcmUgPSAueCkpICU+JQ0KICAgIG1hcCh+c2VsZWN0KC54LCBwaWUuaWQsIHNjb3JlLCBhcGFjaGUyKSkgJT4lDQogICAgbWFwX2RmKH5zZW1pX2pvaW4oLngsIG1hbnVhbF9wYXRpZW50cywgYnkgPSAicGllLmlkIikpICU+JQ0KICAgIGFycmFuZ2UocGllLmlkLCBzY29yZSkgJT4lDQogICAgZG1hcF9hdCgic2NvcmUiLCBzdHJfcmVwbGFjZV9hbGwsIHBhdHRlcm4gPSAic2NvcmVfYXBhY2hlMl8iLCByZXBsYWNlbWVudCA9ICIiKSANCg0KbWFuIDwtIGZpbHRlcihhbGxfc2NvcmVzLCBzY29yZSA9PSAibWFudWFsIikkYXBhY2hlMg0KDQpyZXN1bHQgPC0gYWxsX3Njb3JlcyAlPiUNCiAgICBzZWxlY3QoLXBpZS5pZCkgJT4lDQogICAgZ3JvdXBfYnkoc2NvcmUpICU+JQ0KICAgIHN1bW1hcml6ZV9hbGwoZnVucyhtZWFuLCBzZCwgbWVkaWFuLCBJUVJfbG93ID0gcXVhbnRpbGUoLiwgMC4yNSksIElRUl9oaWdoID0gcXVhbnRpbGUoLiwgMC43NSkpKQ0KDQprbml0cjo6a2FibGUocmVzdWx0KQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGFsbF9zY29yZXMsIGFlcyh4ID0gc2NvcmUsIHkgPSBhcGFjaGUyKSkgKw0KICAgIGdlb21fYm94cGxvdCgpIA0KICAgICMgZ2VvbV9qaXR0ZXIoY29sb3IgPSAibGlnaHQgYmx1ZSIsIGFscGhhID0gMC42KQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGFsbF9zY29yZXMsIGFlcyh4ID0gYXBhY2hlMikpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuNSkgKw0KICAgIGZhY2V0X3dyYXAofiBzY29yZSkNCmBgYA0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02fQ0KZGYgPC0gYWxsX3Njb3JlcyAlPiUNCiAgICBzcHJlYWQoc2NvcmUsIGFwYWNoZTIpICU+JQ0KICAgIGdhdGhlcihzY29yZSwgYXBhY2hlMiwgLXBpZS5pZCwgLW1hbnVhbCkNCg0KIyBnZ3Bsb3QoZGYsIGFlcyh4ID0gYXBhY2hlMiwgeSA9IG1hbnVhbCkpICsNCiMgICAgIGdlb21faml0dGVyKGFlcyhjb2xvciA9IHNjb3JlKSwgYWxwaGEgPSAwLjYpIA0KDQpnZ3Bsb3QoZGYsIGFlcyh4ID0gYXBhY2hlMiwgeSA9IG1hbnVhbCkpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgICMgZ2VvbV9zbW9vdGgoKSArDQogICAgZmFjZXRfd3JhcCh+IHNjb3JlKQ0KYGBgDQoNCg0KKiBDb21wYXJpc29uIG9mIHNjb3Jlcw0KICAgIC0gUmVzdWx0cyBydW4gdXNpbmcgYm90aCB0aGUgdC10ZXN0IGFuZCBXaWxjb3hvbiBzaWduZWQgcmFuayB0ZXN0IChhc3N1bWVzIG5vbi1wYXJhbWV0cmljIGRhdGEsIGlzIG1vcmUgZGlmZmljdWx0IHRvIGFjaGlldmUgc2lnbmlmaWNhbmNlKQ0KICAgIC0gVG8gYWNjb3VudCBmb3IgcGVyZm9ybWluZyBtdWx0aXBsZSBjb21wYXJpc29ucywgYSBCSCAoQmVuamFtaW5pIGFuZCBIb2NoYmVyZykgY29ycmVjdGlvbiBpcyBhcHBsaWVkIHRvIHRoZSBwLXZhbHVlczsgKip1c2UgdGhlIGJoLmFkanVzdCB2YWx1ZSBhcyB0aGUgcC12YWx1ZSoqDQogICAgICAgICsgUmVmZXJlbmNlOiBodHRwOi8vaWVzLmVkLmdvdi9uY2VlL3B1YnMvMjAwODQwMTgvYXBwX2IuYXNwDQoNCmBgYHtyfQ0KdCA8LSBhbGxfc2NvcmVzWywgLTFdICU+JQ0KICAgIGZpbHRlcihzY29yZSAhPSAibWFudWFsIikgJT4lDQogICAgZ3JvdXBfYnkoc2NvcmUpICU+JQ0KICAgIGRvKHRpZHkodC50ZXN0KC4kYXBhY2hlMiwgbWFuLCBwYWlyZWQgPSBUUlVFKSkpICU+JQ0KICAgIHVuZ3JvdXAoKSAlPiUNCiAgICBtdXRhdGUoYmguYWRqdXN0ID0gcC5hZGp1c3QocC52YWx1ZSwgIkJIIikpICU+JQ0KICAgIHNlbGVjdChzY29yZTpwLnZhbHVlLCBiaC5hZGp1c3QsIGV2ZXJ5dGhpbmcoKSkNCg0Ka25pdHI6OmthYmxlKHQsIGRpZ2l0cyA9IDQpDQpgYGANCg0KYGBge3J9DQptdyA8LSBhbGxfc2NvcmVzWywgLTFdICU+JQ0KICAgIGZpbHRlcihzY29yZSAhPSAibWFudWFsIikgJT4lDQogICAgZ3JvdXBfYnkoc2NvcmUpICU+JQ0KICAgIGRvKHRpZHkod2lsY294LnRlc3QoLiRhcGFjaGUyLCBtYW4sIHBhaXJlZCA9IFRSVUUsIGNvbmYuaW50ID0gVFJVRSwgZXhhY3QgPSBGQUxTRSkpKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKGJoLmFkanVzdCA9IHAuYWRqdXN0KHAudmFsdWUsICJCSCIpKSAlPiUNCiAgICBzZWxlY3Qoc2NvcmU6cC52YWx1ZSwgYmguYWRqdXN0LCBldmVyeXRoaW5nKCkpDQoNCmtuaXRyOjprYWJsZShtdywgZGlnaXRzID0gNCkNCmBgYA0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KbXdfcGFpciA8LSBhbGxfc2NvcmVzICU+JQ0KICAgIGRtYXBfYXQoInNjb3JlIiwgZmFjdG9yKSAlPiUNCiAgICBkbWFwX2F0KCJzY29yZSIsIH5mY3RfcmVsZXZlbCgueCwgYygibWFudWFsIiwgImVrIiwgImVrX2RyZyIpKSkgJT4lDQogICAgZG8odGlkeShwYWlyd2lzZS53aWxjb3gudGVzdCguJGFwYWNoZTIsIC4kc2NvcmUsIHAuYWRqID0gIkJIIiwgcGFpcmVkID0gVFJVRSwgZXhhY3QgPSBGQUxTRSkpKQ0KYGBgDQoNCg0KIyMjIyBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbA0KDQpgYGB7cn0NCmxtIDwtIGRmICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBkbyhmaXQgPSBsbShtYW51YWwgfiBhcGFjaGUyLCBkYXRhID0gLikpDQoNCmtuaXRyOjprYWJsZShnbGFuY2UobG0sIGZpdCksIGRpZ2l0cyA9IDQpDQpgYGANCg0KYGBge3J9DQphdWdtZW50KGxtLCBmaXQpICU+JQ0KICAgIGdncGxvdChhZXMoeCA9IC5maXR0ZWQsIHkgPSAucmVzaWQpKSArIA0KICAgIGdlb21fcG9pbnQoKSArDQogICAgIyBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArDQogICAgZmFjZXRfd3JhcCh+IHNjb3JlKQ0KYGBgDQoNCmBgYHtyfQ0KbW9kX2RmIDwtIGRmICU+JQ0KICAgIGZpbHRlcihzY29yZSA9PSAiZWtfZHJnIikNCg0KbW9kIDwtIGxtKG1hbnVhbCB+IGFwYWNoZTIsIGRhdGEgPSBtb2RfZGYpDQoNCnBhcihtZnJvdyA9IGMoMiwgMikpDQpwbG90KG1vZCkNCmBgYA0KDQoNCg0KKiBDb21wYXJlIGRpZmZlcmVuY2VzIGluIHNjb3JlIGZyb20gY29tb3JiaWRpdHkgc2V0cyB2cy4gbWFudWFsDQogICAgLSBDb2x1bW4gKm51bV9kaWZmKiBpbmRpY2F0ZXMgdGhlIG51bWJlciBvZiBwYXRpZW50cyB3aXRoIGEgc2NvcmUgdGhhdCBkaWZmZXJlZCBmcm9tIG1hbnVhbA0KICAgIC0gTWVhbiBhbmQgbWVkaWFuIGluZGljYXRlIHRoZSBkaWZmZXJlbmNlIGZyb20gdGhlIG1hbnVhbCBzY29yZQ0KICAgICAgICArIFRoZXNlIHdlcmUgb25seSBjYWxjdWxhdGVkIGZvciB0aG9zZSBwYXRpZW50cyB3aG9zZSBzY29yZSB3YXMgZGlmZmVyZW50IGZyb20gbWFudWFsDQogICAgICAgICsgSWYgYWxsIHBhdGllbnRzIHdlcmUgaW5jbHVkZWQsIHRoZSBudW1iZXIgaXMgc2tld2VkIHRvd2FyZHMgMCBieSBhbGwgdGhlIHBhdGllbnRzIHdpdGggdGhlIHNhbWUgc2NvcmVzDQoNCmBgYHtyfQ0KZGlmZiA8LSBhbGxfc2NvcmVzICU+JQ0KICAgIHNwcmVhZChzY29yZSwgYXBhY2hlMikgJT4lDQogICAgbXV0YXRlX2F0KHZhcnMoLXBpZS5pZCwgLW1hbnVhbCksIGZ1bnMoYWJzKC4gLSBtYW51YWwpKSkgJT4lDQogICAgZ2F0aGVyKHNjb3JlLCBhcGFjaGUyLCAtcGllLmlkLCAtbWFudWFsKSAlPiUNCiAgICBzZWxlY3QoLXBpZS5pZCwgLW1hbnVhbCkgJT4lDQogICAgZmlsdGVyKGFwYWNoZTIgPiAwKSAlPiUNCiAgICBncm91cF9ieShzY29yZSkgJT4lDQogICAgc3VtbWFyaXplX2FsbChmdW5zKG51bV9kaWZmID0gbGVuZ3RoLCBtZWFuLCBzZCwgbWVkaWFuLCBJUVJfbG93ID0gcXVhbnRpbGUoLiwgMC4yNSksIElRUl9oaWdoID0gcXVhbnRpbGUoLiwgMC43NSkpKQ0KDQprbml0cjo6a2FibGUoZGlmZiwgY2FwdGlvbiA9ICJEaWZmZXJlbmNlcyBpbiBBUEFDSEUgSUkgU2NvcmVzIikNCmBgYA0KDQojIyMgQVBBQ0hFIElJSQ0KDQpgYGB7cn0NCnNjb3JlcyA8LSBscyhwYXR0ZXJuID0gInNjb3JlX2FwYWNoZTMiKQ0KDQphbGxfc2NvcmVzIDwtIG1hcChzY29yZXMsIH5tdXRhdGUoZ2V0KC54KSwgc2NvcmUgPSAueCkpICU+JQ0KICAgIG1hcCh+c2VsZWN0KC54LCBwaWUuaWQsIHNjb3JlLCBhcGFjaGUzKSkgJT4lDQogICAgbWFwX2RmKH5zZW1pX2pvaW4oLngsIG1hbnVhbF9wYXRpZW50cywgYnkgPSAicGllLmlkIikpICU+JQ0KICAgIGFycmFuZ2UocGllLmlkLCBzY29yZSkgJT4lDQogICAgZG1hcF9hdCgic2NvcmUiLCBzdHJfcmVwbGFjZV9hbGwsIHBhdHRlcm4gPSAic2NvcmVfYXBhY2hlM18iLCByZXBsYWNlbWVudCA9ICIiKSANCg0KbWFuIDwtIGZpbHRlcihhbGxfc2NvcmVzLCBzY29yZSA9PSAibWFudWFsIikkYXBhY2hlMw0KDQpyZXN1bHQgPC0gYWxsX3Njb3JlcyAlPiUNCiAgICBzZWxlY3QoLXBpZS5pZCkgJT4lDQogICAgZ3JvdXBfYnkoc2NvcmUpICU+JQ0KICAgIHN1bW1hcml6ZV9hbGwoZnVucyhtZWFuLCBzZCwgbWVkaWFuLCBJUVJfbG93ID0gcXVhbnRpbGUoLiwgMC4yNSksIElRUl9oaWdoID0gcXVhbnRpbGUoLiwgMC43NSkpKQ0KDQprbml0cjo6a2FibGUocmVzdWx0KQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGFsbF9zY29yZXMsIGFlcyh4ID0gc2NvcmUsIHkgPSBhcGFjaGUzKSkgKw0KICAgIGdlb21fYm94cGxvdCgpDQpgYGANCg0KYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9DQpkZiA8LSBhbGxfc2NvcmVzICU+JQ0KICAgIHNwcmVhZChzY29yZSwgYXBhY2hlMykgJT4lDQogICAgZ2F0aGVyKHNjb3JlLCBhcGFjaGUzLCAtcGllLmlkLCAtbWFudWFsKQ0KDQpnZ3Bsb3QoZGYsIGFlcyh4ID0gYXBhY2hlMywgeSA9IG1hbnVhbCkpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgICMgZ2VvbV9zbW9vdGgoKSArDQogICAgZmFjZXRfd3JhcCh+IHNjb3JlKQ0KYGBgDQoNCiogQ29tcGFyaXNvbiBvZiBzY29yZXMNCg0KYGBge3J9DQp0IDwtIGFsbF9zY29yZXNbLCAtMV0gJT4lDQogICAgZmlsdGVyKHNjb3JlICE9ICJtYW51YWwiKSAlPiUNCiAgICBncm91cF9ieShzY29yZSkgJT4lDQogICAgZG8odGlkeSh0LnRlc3QoLiRhcGFjaGUzLCBtYW4sIHBhaXJlZCA9IFRSVUUpKSkgJT4lDQogICAgdW5ncm91cCgpICU+JQ0KICAgIG11dGF0ZShiaC5hZGp1c3QgPSBwLmFkanVzdChwLnZhbHVlLCAiQkgiKSkgJT4lDQogICAgc2VsZWN0KHNjb3JlOnAudmFsdWUsIGJoLmFkanVzdCwgZXZlcnl0aGluZygpKQ0KDQprbml0cjo6a2FibGUodCwgZGlnaXRzID0gNCkNCmBgYA0KDQpgYGB7cn0NCm13IDwtIGFsbF9zY29yZXNbLCAtMV0gJT4lDQogICAgZmlsdGVyKHNjb3JlICE9ICJtYW51YWwiKSAlPiUNCiAgICBncm91cF9ieShzY29yZSkgJT4lDQogICAgZG8odGlkeSh3aWxjb3gudGVzdCguJGFwYWNoZTMsIG1hbiwgcGFpcmVkID0gVFJVRSwgY29uZi5pbnQgPSBUUlVFLCBleGFjdCA9IEZBTFNFKSkpICU+JQ0KICAgIHVuZ3JvdXAoKSAlPiUNCiAgICBtdXRhdGUoYmguYWRqdXN0ID0gcC5hZGp1c3QocC52YWx1ZSwgIkJIIikpICU+JQ0KICAgIHNlbGVjdChzY29yZTpwLnZhbHVlLCBiaC5hZGp1c3QsIGV2ZXJ5dGhpbmcoKSkNCg0Ka25pdHI6OmthYmxlKG13LCBkaWdpdHMgPSA0KQ0KYGBgDQoNCiMjIyMgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwNCg0KYGBge3J9DQpsbSA8LSBkZiAlPiUNCiAgICBncm91cF9ieShzY29yZSkgJT4lDQogICAgZG8oZml0ID0gbG0obWFudWFsIH4gYXBhY2hlMywgZGF0YSA9IC4pKQ0KDQprbml0cjo6a2FibGUoZ2xhbmNlKGxtLCBmaXQpLCBkaWdpdHMgPSA0KQ0KYGBgDQoNCmBgYHtyfQ0KYXVnbWVudChsbSwgZml0KSAlPiUNCiAgICBnZ3Bsb3QoYWVzKHggPSAuZml0dGVkLCB5ID0gLnJlc2lkKSkgKyANCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGZhY2V0X3dyYXAofiBzY29yZSkNCmBgYA0KDQoqIERpZmZlcmVuY2VzIGluIHNjb3JlDQoNCmBgYHtyfQ0KZGlmZiA8LSBhbGxfc2NvcmVzICU+JQ0KICAgIHNwcmVhZChzY29yZSwgYXBhY2hlMykgJT4lDQogICAgbXV0YXRlX2F0KHZhcnMoLXBpZS5pZCwgLW1hbnVhbCksIGZ1bnMoYWJzKC4gLSBtYW51YWwpKSkgJT4lDQogICAgZ2F0aGVyKHNjb3JlLCBhcGFjaGUzLCAtcGllLmlkLCAtbWFudWFsKSAlPiUNCiAgICBzZWxlY3QoLXBpZS5pZCwgLW1hbnVhbCkgJT4lDQogICAgZmlsdGVyKGFwYWNoZTMgPiAwKSAlPiUNCiAgICBncm91cF9ieShzY29yZSkgJT4lDQogICAgc3VtbWFyaXplX2FsbChmdW5zKG51bV9kaWZmID0gbGVuZ3RoLCBtZWFuLCBzZCwgbWVkaWFuLCBJUVJfbG93ID0gcXVhbnRpbGUoLiwgMC4yNSksIElRUl9oaWdoID0gcXVhbnRpbGUoLiwgMC43NSkpKQ0KDQprbml0cjo6a2FibGUoZGlmZiwgY2FwdGlvbiA9ICJEaWZmZXJlbmNlIGluIEFQQUNIRSBJSUkgU2NvcmVzIikNCmBgYA0KDQojIyMgU0FQUyBJSQ0KDQpgYGB7cn0NCnNjb3JlcyA8LSBscyhwYXR0ZXJuID0gInNjb3JlX3NhcHMyIikNCg0KYWxsX3Njb3JlcyA8LSBtYXAoc2NvcmVzLCB+bXV0YXRlKGdldCgueCksIHNjb3JlID0gLngpKSAlPiUNCiAgICBtYXAofnNlbGVjdCgueCwgcGllLmlkLCBzY29yZSwgc2FwczIpKSAlPiUNCiAgICBtYXBfZGYofnNlbWlfam9pbigueCwgbWFudWFsX3BhdGllbnRzLCBieSA9ICJwaWUuaWQiKSkgJT4lDQogICAgYXJyYW5nZShwaWUuaWQsIHNjb3JlKSAlPiUNCiAgICBkbWFwX2F0KCJzY29yZSIsIHN0cl9yZXBsYWNlX2FsbCwgcGF0dGVybiA9ICJzY29yZV9zYXBzMl8iLCByZXBsYWNlbWVudCA9ICIiKSANCg0KbWFuIDwtIGZpbHRlcihhbGxfc2NvcmVzLCBzY29yZSA9PSAibWFudWFsIikkc2FwczINCg0KcmVzdWx0IDwtIGFsbF9zY29yZXMgJT4lDQogICAgc2VsZWN0KC1waWUuaWQpICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBzdW1tYXJpemVfYWxsKGZ1bnMobWVhbiwgc2QsIG1lZGlhbiwgSVFSX2xvdyA9IHF1YW50aWxlKC4sIDAuMjUpLCBJUVJfaGlnaCA9IHF1YW50aWxlKC4sIDAuNzUpKSkNCg0Ka25pdHI6OmthYmxlKHJlc3VsdCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChhbGxfc2NvcmVzLCBhZXMoeCA9IHNjb3JlLCB5ID0gc2FwczIpKSArDQogICAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0NCmRmIDwtIGFsbF9zY29yZXMgJT4lDQogICAgc3ByZWFkKHNjb3JlLCBzYXBzMikgJT4lDQogICAgZ2F0aGVyKHNjb3JlLCBzYXBzMiwgLXBpZS5pZCwgLW1hbnVhbCkNCg0KZ2dwbG90KGRmLCBhZXMoeCA9IHNhcHMyLCB5ID0gbWFudWFsKSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgIyBnZW9tX3Ntb290aCgpICsNCiAgICBmYWNldF93cmFwKH4gc2NvcmUpDQpgYGANCg0KKiBDb21wYXJpc29uIG9mIHNjb3Jlcw0KDQpgYGB7cn0NCnQgPC0gYWxsX3Njb3Jlc1ssIC0xXSAlPiUNCiAgICBmaWx0ZXIoc2NvcmUgIT0gIm1hbnVhbCIpICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBkbyh0aWR5KHQudGVzdCguJHNhcHMyLCBtYW4sIHBhaXJlZCA9IFRSVUUpKSkgJT4lDQogICAgdW5ncm91cCgpICU+JQ0KICAgIG11dGF0ZShiaC5hZGp1c3QgPSBwLmFkanVzdChwLnZhbHVlLCAiQkgiKSkgJT4lDQogICAgc2VsZWN0KHNjb3JlOnAudmFsdWUsIGJoLmFkanVzdCwgZXZlcnl0aGluZygpKQ0KDQprbml0cjo6a2FibGUodCwgZGlnaXRzID0gNCkNCmBgYA0KDQoqIENvbmZpZGVuY2UgaW50ZXJ2YWwgd2FzIHVuYWJsZSB0byBiZSBjYWxjdWxhdGVkIGZvciBXaWxjb3hvbiBhbmFseXNpcyBoZXJlDQoNCmBgYHtyfQ0KbXcgPC0gYWxsX3Njb3Jlc1ssIC0xXSAlPiUNCiAgICBmaWx0ZXIoc2NvcmUgIT0gIm1hbnVhbCIpICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBkbyh0aWR5KHdpbGNveC50ZXN0KC4kc2FwczIsIG1hbiwgcGFpcmVkID0gVFJVRSwgZXhhY3QgPSBGQUxTRSkpKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKGJoLmFkanVzdCA9IHAuYWRqdXN0KHAudmFsdWUsICJCSCIpKSAlPiUNCiAgICBzZWxlY3Qoc2NvcmU6cC52YWx1ZSwgYmguYWRqdXN0LCBldmVyeXRoaW5nKCkpDQoNCmtuaXRyOjprYWJsZShtdywgZGlnaXRzID0gNCkNCmBgYA0KDQojIyMjIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsDQoNCmBgYHtyfQ0KbG0gPC0gZGYgJT4lDQogICAgZ3JvdXBfYnkoc2NvcmUpICU+JQ0KICAgIGRvKGZpdCA9IGxtKG1hbnVhbCB+IHNhcHMyLCBkYXRhID0gLikpDQoNCmtuaXRyOjprYWJsZShnbGFuY2UobG0sIGZpdCksIGRpZ2l0cyA9IDQpDQpgYGANCg0KYGBge3J9DQphdWdtZW50KGxtLCBmaXQpICU+JQ0KICAgIGdncGxvdChhZXMoeCA9IC5maXR0ZWQsIHkgPSAucmVzaWQpKSArIA0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZmFjZXRfd3JhcCh+IHNjb3JlKQ0KYGBgDQoNCiogRGlmZmVyZW5jZXMgaW4gc2NvcmUNCg0KYGBge3J9DQpkaWZmIDwtIGFsbF9zY29yZXMgJT4lDQogICAgc3ByZWFkKHNjb3JlLCBzYXBzMikgJT4lDQogICAgbXV0YXRlX2F0KHZhcnMoLXBpZS5pZCwgLW1hbnVhbCksIGZ1bnMoYWJzKC4gLSBtYW51YWwpKSkgJT4lDQogICAgZ2F0aGVyKHNjb3JlLCBzYXBzMiwgLXBpZS5pZCwgLW1hbnVhbCkgJT4lDQogICAgc2VsZWN0KC1waWUuaWQsIC1tYW51YWwpICU+JQ0KICAgIGZpbHRlcihzYXBzMiA+IDApICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBzdW1tYXJpemVfYWxsKGZ1bnMobnVtX2RpZmYgPSBsZW5ndGgsIG1lYW4sIHNkLCBtZWRpYW4sIElRUl9sb3cgPSBxdWFudGlsZSguLCAwLjI1KSwgSVFSX2hpZ2ggPSBxdWFudGlsZSguLCAwLjc1KSkpDQoNCmtuaXRyOjprYWJsZShkaWZmLCBjYXB0aW9uID0gIkRpZmZlcmVuY2UgaW4gU0FQUyBJSSBTY29yZXMiKQ0KYGBgDQoNCiMjIFNlY29uZGFyeSBlbmRwb2ludHMNCg0KIyMjIFByZWRpY3RlZCBtb3J0YWxpdHkNCg0KIyMjIyBTQVBTIElJDQoNCiogTWVhbiBhbmQgbWVkaWFuIHJpc2sgb2YgbW9ydGFsaXR5IChhcyBhIHBlcmNlbnQpIGZvciBlYWNoIGNvbW9yYmlkaXR5IHNldA0KDQpgYGB7cn0NCmNvbW9yYmlkIDwtIGxzKHBhdHRlcm4gPSAic2NvcmVfc2FwczJfIikNCg0KYWxsX2RmIDwtIG1hcChjb21vcmJpZCwgfm11dGF0ZShnZXQoLngpLCBzY29yZSA9IC54KSkgJT4lDQogICAgbWFwKH5zZWxlY3QoLngsIHBpZS5pZCwgc2NvcmUsIHNhcHMyKSkgJT4lDQogICAgbWFwX2RmKH5zZW1pX2pvaW4oLngsIG1hbnVhbF9wYXRpZW50cywgYnkgPSAicGllLmlkIikpICU+JQ0KICAgIGFycmFuZ2UocGllLmlkLCBzY29yZSkgJT4lDQogICAgZG1hcF9hdCgic2NvcmUiLCBzdHJfcmVwbGFjZV9hbGwsIHBhdHRlcm4gPSAic2NvcmVfc2FwczJfIiwgcmVwbGFjZW1lbnQgPSAiIikgJT4lDQogICAgZGlzdGluY3QocGllLmlkLCBzY29yZSwgLmtlZXBfYWxsID0gVFJVRSkgJT4lDQogICAgbXV0YXRlKGxvZ2l0ID0gLTcuNzYzICsgMC4wNzM3ICogc2FwczIgKyAwLjk5NzEgKiBsb2coc2FwczIgKyAxKSwNCiAgICAgICAgICAgbW9ydGFsaXR5ID0gZXhwKGxvZ2l0KSAvICgxICsgZXhwKGxvZ2l0KSkpICU+JQ0KICAgIHNlbGVjdChwaWUuaWQsIHNjb3JlLCBtb3J0YWxpdHkpIA0KDQptYW4gPC0gZmlsdGVyKGFsbF9kZiwgc2NvcmUgPT0gIm1hbnVhbCIpJG1vcnRhbGl0eQ0KDQpyZXN1bHQgPC0gYWxsX2RmICU+JQ0KICAgIHNlbGVjdCgtcGllLmlkKSAlPiUNCiAgICBncm91cF9ieShzY29yZSkgJT4lDQogICAgc3VtbWFyaXplX2FsbChmdW5zKG1lYW4sIHNkLCBtZWRpYW4sIElRUl9sb3cgPSBxdWFudGlsZSguLCAwLjI1KSwgSVFSX2hpZ2ggPSBxdWFudGlsZSguLCAwLjc1KSkpDQoNCmtuaXRyOjprYWJsZShyZXN1bHQsIGRpZ2l0cyA9IDQpDQpgYGANCg0KYGBge3J9DQp0IDwtIGFsbF9kZlssIC0xXSAlPiUNCiAgICBmaWx0ZXIoc2NvcmUgIT0gIm1hbnVhbCIpICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUgDQogICAgZG8odGlkeSh0LnRlc3QoLiRtb3J0YWxpdHksIG1hbiwgcGFpcmVkID0gVFJVRSkpKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKGJoLmFkanVzdCA9IHAuYWRqdXN0KHAudmFsdWUsICJCSCIpKSAlPiUNCiAgICBzZWxlY3Qoc2NvcmU6cC52YWx1ZSwgYmguYWRqdXN0LCBldmVyeXRoaW5nKCkpDQoNCmtuaXRyOjprYWJsZSh0LCBkaWdpdHMgPSA0KQ0KYGBgDQoNCmBgYHtyfQ0KbXcgPC0gYWxsX2RmWywgLTFdICU+JQ0KICAgIGZpbHRlcihzY29yZSAhPSAibWFudWFsIikgJT4lDQogICAgZ3JvdXBfYnkoc2NvcmUpICU+JQ0KICAgIGRvKHRpZHkod2lsY294LnRlc3QoLiRtb3J0YWxpdHksIG1hbiwgcGFpcmVkID0gVFJVRSwgY29uZi5pbnQgPSBUUlVFLCBleGFjdCA9IEZBTFNFKSkpICU+JQ0KICAgIHVuZ3JvdXAoKSAlPiUNCiAgICBtdXRhdGUoYmguYWRqdXN0ID0gcC5hZGp1c3QocC52YWx1ZSwgIkJIIikpICU+JQ0KICAgIHNlbGVjdChzY29yZTpwLnZhbHVlLCBiaC5hZGp1c3QsIGV2ZXJ5dGhpbmcoKSkNCg0Ka25pdHI6OmthYmxlKG13LCBkaWdpdHMgPSA0KQ0KYGBgDQoNCiogRGlmZmVyZW5jZSBpbiByaXNrIG9mIG1vcnRhbGl0eQ0KICAgIC0gTWVhbiBhbmQgbWVkaWFuIGNhbGN1bGF0ZWQgdXNpbmcgb25seSB0aG9zZSBwYXRpZW50cyB3aG9zZSBwcmVkaWN0ZWQgbW9ydGFsaXR5IHNjb3JlIHdhcyBkaWZmZXJlbnQgZnJvbSBtYW51YWwNCiAgICANCmBgYHtyfQ0KZGlmZiA8LSBhbGxfZGYgJT4lDQogICAgc3ByZWFkKHNjb3JlLCBtb3J0YWxpdHkpICU+JQ0KICAgIG11dGF0ZV9hdCh2YXJzKC1waWUuaWQsIC1tYW51YWwpLCBmdW5zKGFicyguIC0gbWFudWFsKSkpICU+JQ0KICAgIGdhdGhlcihzY29yZSwgc2FwczIsIC1waWUuaWQsIC1tYW51YWwpICU+JQ0KICAgIHNlbGVjdCgtcGllLmlkLCAtbWFudWFsKSAlPiUNCiAgICBmaWx0ZXIoc2FwczIgPiAwKSAlPiUNCiAgICBncm91cF9ieShzY29yZSkgJT4lDQogICAgc3VtbWFyaXplX2FsbChmdW5zKG51bV9kaWZmID0gbGVuZ3RoLCBtZWFuLCBzZCwgbWVkaWFuLCBJUVJfbG93ID0gcXVhbnRpbGUoLiwgMC4yNSksIElRUl9oaWdoID0gcXVhbnRpbGUoLiwgMC43NSkpKQ0KDQprbml0cjo6a2FibGUoZGlmZiwgY2FwdGlvbiA9ICJEaWZmZXJlbmNlIGluIFByZWRpY3RlZCBNb3J0YWxpdHkgdXNpbmcgU0FQUyBJSSIpDQpgYGANCg0KIyMjIE51bWJlciBvZiBjb21vcmJpZGl0aWVzDQoNCiMjIyMgQVBBQ0hFIElJDQoNCiogTWVhbiByZXByZXNlbnRzIHRoZSBhdmVyYWdlIG51bWJlciBvZiBjb21vcmJpZGl0aWVzIHBlciBwYXRpZW50DQoNCmBgYHtyfQ0KY29tb3JiaWQgPC0gbHMocGF0dGVybiA9ICJkYXRhX2FwYWNoZTJfIikNCg0KYWxsX2RmIDwtIG1hcChjb21vcmJpZCwgfm11dGF0ZShnZXQoLngpLCBzY29yZSA9IC54KSkgJT4lDQogICAgbWFwKH5kaXN0aW5jdCgueCwgcGllLmlkLCBzY29yZSwgbGl2ZXIsIGNhcmRpb3Zhc2MsIHJlc3BpcmF0b3J5LCByZW5hbCwgaW1tdW5vY29tcCwgY29tb3JiaWRpdHkpKSAlPiUNCiAgICBtYXBfZGYofnNlbWlfam9pbigueCwgbWFudWFsX3BhdGllbnRzLCBieSA9ICJwaWUuaWQiKSkgJT4lDQogICAgYXJyYW5nZShwaWUuaWQsIHNjb3JlKSAlPiUNCiAgICBkbWFwX2F0KCJzY29yZSIsIHN0cl9yZXBsYWNlX2FsbCwgcGF0dGVybiA9ICJkYXRhX2FwYWNoZTJfIiwgcmVwbGFjZW1lbnQgPSAiIikgJT4lDQogICAgbXV0YXRlX2lmKGlzLmxvZ2ljYWwsIGFzLm51bWVyaWMpICU+JQ0KICAgIGJ5X3JvdyhmdW5jdGlvbih4KSBzdW0oeFssIDM6N10sIG5hLnJtID0gVFJVRSksIC5jb2xsYXRlID0gInJvd3MiLCAudG8gPSAibnVtX2NvbW9yYmlkIikgJT4lDQogICAgc2VsZWN0KHBpZS5pZCwgc2NvcmUsIG51bV9jb21vcmJpZCkgDQoNCm1hbiA8LSBmaWx0ZXIoYWxsX2RmLCBzY29yZSA9PSAibWFudWFsIikkbnVtX2NvbW9yYmlkDQoNCnJlc3VsdCA8LSBhbGxfZGYgJT4lDQogICAgc2VsZWN0KC1waWUuaWQpICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBzdW1tYXJpemVfYWxsKGZ1bnMobWVhbiwgc2QsIG1lZGlhbiwgSVFSX2xvdyA9IHF1YW50aWxlKC4sIDAuMjUpLCBJUVJfaGlnaCA9IHF1YW50aWxlKC4sIDAuNzUpKSkNCg0Ka25pdHI6OmthYmxlKHJlc3VsdCkNCmBgYA0KDQpgYGB7cn0NCnQgPC0gYWxsX2RmWywgLTFdICU+JQ0KICAgIGZpbHRlcihzY29yZSAhPSAibWFudWFsIikgJT4lDQogICAgZ3JvdXBfYnkoc2NvcmUpICU+JQ0KICAgIGRvKHRpZHkodC50ZXN0KC4kbnVtX2NvbW9yYmlkLCBtYW4sIHBhaXJlZCA9IFRSVUUpKSkgJT4lDQogICAgdW5ncm91cCgpICU+JQ0KICAgIG11dGF0ZShiaC5hZGp1c3QgPSBwLmFkanVzdChwLnZhbHVlLCAiQkgiKSkgJT4lDQogICAgc2VsZWN0KHNjb3JlOnAudmFsdWUsIGJoLmFkanVzdCwgZXZlcnl0aGluZygpKQ0KDQprbml0cjo6a2FibGUodCwgZGlnaXRzID0gNCkNCmBgYA0KDQpgYGB7cn0NCm13IDwtIGFsbF9kZlssIC0xXSAlPiUNCiAgICBmaWx0ZXIoc2NvcmUgIT0gIm1hbnVhbCIpICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBkbyh0aWR5KHdpbGNveC50ZXN0KC4kbnVtX2NvbW9yYmlkLCBtYW4sIHBhaXJlZCA9IFRSVUUsIGNvbmYuaW50ID0gVFJVRSwgZXhhY3QgPSBGQUxTRSkpKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKGJoLmFkanVzdCA9IHAuYWRqdXN0KHAudmFsdWUsICJCSCIpKSAlPiUNCiAgICBzZWxlY3Qoc2NvcmU6cC52YWx1ZSwgYmguYWRqdXN0LCBldmVyeXRoaW5nKCkpDQoNCmtuaXRyOjprYWJsZShtdywgZGlnaXRzID0gNCkNCmBgYA0KDQojIyMgUHJvcG9ydGlvbiB3aXRoIGRpZmZlcmluZyBjb21vcmJpZGl0eQ0KDQojIyMjIEFQQUNIRSBJSQ0KDQoqIFByb3BvcnRpb24gb2YgcGF0aWVudHMgd2hlcmUgYSBjb21vcmJpZGl0eSB3YXMgaWRlbnRpZmllZCBieSBJQ0Qgb3IgYnkgbWFudWFsLCBidXQgbm90IGJvdGgNCg0KYGBge3J9DQpjb21vcmJpZCA8LSBscyhwYXR0ZXJuID0gImRhdGFfYXBhY2hlMl8iKQ0KDQphbGxfZGYgPC0gbWFwKGNvbW9yYmlkLCB+bXV0YXRlKGdldCgueCksIHNjb3JlID0gLngpKSAlPiUNCiAgICBtYXAofmRpc3RpbmN0KC54LCBwaWUuaWQsIHNjb3JlLCBjb21vcmJpZGl0eSkpICU+JQ0KICAgIG1hcF9kZih+c2VtaV9qb2luKC54LCBtYW51YWxfcGF0aWVudHMsIGJ5ID0gInBpZS5pZCIpKSAlPiUNCiAgICBhcnJhbmdlKHBpZS5pZCwgc2NvcmUpICU+JQ0KICAgIGRtYXBfYXQoInNjb3JlIiwgc3RyX3JlcGxhY2VfYWxsLCBwYXR0ZXJuID0gImRhdGFfYXBhY2hlMl8iLCByZXBsYWNlbWVudCA9ICIiKSAlPiUNCiAgICBzcHJlYWQoc2NvcmUsIGNvbW9yYmlkaXR5KSAlPiUNCiAgICBtdXRhdGVfYXQodmFycygtcGllLmlkLCAtbWFudWFsKSwgZnVucyguICE9IG1hbnVhbCkpICU+JQ0KICAgIG11dGF0ZV9pZihpcy5sb2dpY2FsLCBhcy5udW1lcmljKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgZ2F0aGVyKHNjb3JlLCBjb21vcmJpZGl0eSwgLXBpZS5pZCwgLW1hbnVhbCkgJT4lDQogICAgc2VsZWN0KC1waWUuaWQsIC1tYW51YWwpICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBzdW1tYXJpemVfYWxsKGZ1bnMobnVtX2RpZmYgPSBzdW0oLiA+IDApLCBwcm9wb3J0aW9uID0gbWVhbikpDQoNCmtuaXRyOjprYWJsZShhbGxfZGYpDQpgYGANCg0KIyMjIyBBUEFDSEUgSUlJDQoNCiogUHJvcG9ydGlvbiBvZiBwYXRpZW50cyB3aGVyZSB0aGUgcHJpbWFyeSBjb21vcmJpZGl0eSBpZGVudGlmaWVkIGJ5IElDRCB2YXJpZWQgZnJvbSBwcmltYXJ5IGNvbW9yYmlkaXR5IGlkZW50aWZpZWQgYnkgbWFudWFsDQoNCmBgYHtyfQ0KY29tb3JiaWQgPC0gbHMocGF0dGVybiA9ICJkYXRhX2FwYWNoZTNfIikNCg0KYWxsX2RmIDwtIG1hcChjb21vcmJpZCwgfm11dGF0ZShnZXQoLngpLCBzY29yZSA9IC54KSkgJT4lDQogICAgbWFwKH5kaXN0aW5jdCgueCwgcGllLmlkLCBzY29yZSwgY29tb3JiaWRpdHkpKSAlPiUNCiAgICBtYXBfZGYofnNlbWlfam9pbigueCwgbWFudWFsX3BhdGllbnRzLCBieSA9ICJwaWUuaWQiKSkgJT4lDQogICAgYXJyYW5nZShwaWUuaWQsIHNjb3JlKSAlPiUNCiAgICBkbWFwX2F0KCJzY29yZSIsIHN0cl9yZXBsYWNlX2FsbCwgcGF0dGVybiA9ICJkYXRhX2FwYWNoZTNfIiwgcmVwbGFjZW1lbnQgPSAiIikgJT4lDQogICAgc3ByZWFkKHNjb3JlLCBjb21vcmJpZGl0eSwgZmlsbCA9ICIiKSAlPiUNCiAgICBkbWFwKHN0cl9yZXBsYWNlX2FsbCwgcGF0dGVybiA9ICJub25lIiwgcmVwbGFjZW1lbnQgPSAiIikgJT4lDQogICAgbXV0YXRlX2F0KHZhcnMoLXBpZS5pZCwgLW1hbnVhbCksIGZ1bnMoLiAhPSBtYW51YWwpKSAlPiUNCiAgICBtdXRhdGVfaWYoaXMubG9naWNhbCwgYXMubnVtZXJpYykgJT4lDQogICAgZ2F0aGVyKHNjb3JlLCBjb21vcmJpZGl0eSwgLXBpZS5pZCwgLW1hbnVhbCkgJT4lDQogICAgc2VsZWN0KC1waWUuaWQsIC1tYW51YWwpICU+JQ0KICAgIGdyb3VwX2J5KHNjb3JlKSAlPiUNCiAgICBzdW1tYXJpemVfYWxsKGZ1bnMobnVtX2RpZmYgPSBzdW0oLiA+IDApLCBwcm9wb3J0aW9uID0gbWVhbikpDQoNCmtuaXRyOjprYWJsZShhbGxfZGYpDQpgYGANCg0KIyMjIyBTQVBTIElJDQoNCiogUHJvcG9ydGlvbiBvZiBwYXRpZW50cyB3aGVyZSB0aGUgcHJpbWFyeSBjb21vcmJpZGl0eSBpZGVudGlmaWVkIGJ5IElDRCB2YXJpZWQgZnJvbSBwcmltYXJ5IGNvbW9yYmlkaXR5IGlkZW50aWZpZWQgYnkgbWFudWFsDQoNCmBgYHtyfQ0KY29tb3JiaWQgPC0gbHMocGF0dGVybiA9ICJkYXRhX3NhcHMyXyIpDQoNCmFsbF9kZiA8LSBtYXAoY29tb3JiaWQsIH5tdXRhdGUoZ2V0KC54KSwgc2NvcmUgPSAueCkpICU+JQ0KICAgIG1hcCh+ZGlzdGluY3QoLngsIHBpZS5pZCwgc2NvcmUsIGNvbW9yYmlkaXR5KSkgJT4lDQogICAgbWFwX2RmKH5zZW1pX2pvaW4oLngsIG1hbnVhbF9wYXRpZW50cywgYnkgPSAicGllLmlkIikpICU+JQ0KICAgIGFycmFuZ2UocGllLmlkLCBzY29yZSkgJT4lDQogICAgZG1hcF9hdCgic2NvcmUiLCBzdHJfcmVwbGFjZV9hbGwsIHBhdHRlcm4gPSAiZGF0YV9zYXBzMl8iLCByZXBsYWNlbWVudCA9ICIiKSAlPiUNCiAgICBzcHJlYWQoc2NvcmUsIGNvbW9yYmlkaXR5LCBmaWxsID0gIiIpICU+JQ0KICAgIGRtYXAoc3RyX3JlcGxhY2VfYWxsLCBwYXR0ZXJuID0gIm5vbmUiLCByZXBsYWNlbWVudCA9ICIiKSAlPiUNCiAgICBtdXRhdGVfYXQodmFycygtcGllLmlkLCAtbWFudWFsKSwgZnVucyguICE9IG1hbnVhbCkpICU+JQ0KICAgIG11dGF0ZV9pZihpcy5sb2dpY2FsLCBhcy5udW1lcmljKSAlPiUNCiAgICBnYXRoZXIoc2NvcmUsIGNvbW9yYmlkaXR5LCAtcGllLmlkLCAtbWFudWFsKSAlPiUNCiAgICBzZWxlY3QoLXBpZS5pZCwgLW1hbnVhbCkgJT4lDQogICAgZ3JvdXBfYnkoc2NvcmUpICU+JQ0KICAgIHN1bW1hcml6ZV9hbGwoZnVucyhudW1fZGlmZiA9IHN1bSguID4gMCksIHByb3BvcnRpb24gPSBtZWFuKSkNCg0Ka25pdHI6OmthYmxlKGFsbF9kZikNCmBgYA0K