Intro

This doc will describe some of the average trends at specific ages from 21 high income countries.

It is inspired by Christensen 2010, which showed

However, as per the figures in White 2002), the average of 21 high income countries will be used instead, as well as the best-performing country at these different age groups.

Additionally, the 12 month mortality probabilities at the following ages will also be calculated:

The aim will be to determine how strongly the (log of these) trends are correlated.

Pre reqs

pacman::p_load(
  tidyverse, HMDHFDplus,
  ggrepel
)
dta_Mx <- read_rds("tidy_data/Mx_data.rds")

Define countries

source("scripts/country_definitions.R")

Replication of Fig 2 of Christenson et al 2010

dta_Mx %>% 
  filter(sex != "total") %>% 
  filter(age %in% c(80, 90)) %>% 
  filter(
    code %in% c("GBRTENW", "FRATNP", "DEUTE", "DEUTW", "JPN", "SWE", "USA")
  ) %>%      
  filter(between(year, 1950, 2003)) %>% 
  ggplot(
    aes(x = year, y = Mx, colour = code, group = code)
  ) + 
  geom_line() +
  facet_grid(age ~ sex)

And how has this developed since?

dta_Mx %>% 
  filter(sex != "total") %>% 
  filter(age %in% c(80, 90)) %>% 
  filter(
    code %in% c("GBRTENW", "FRATNP", "DEUTE", "DEUTW", "JPN", "SWE", "USA")
  ) %>%      
  filter(between(year, 1950, 2017)) %>% 
  ggplot(
    aes(x = year, y = Mx, colour = code, group = code)
  ) + 
  geom_line() +
  facet_grid(age ~ sex)

It’s interesting that life expectancies have at age 80 have converged, for males especially. And that they’ve levelled off in Japan over the 2000s, while continued to improve in the USA.

In England/Wales they Mx at age 90 seem to have increased for females especially in recent years.

Let’s look at this for the average of the 21 countries

Now let’s do this for the average of the 21 high income countries

dta_Mx %>% 
  filter(sex != "total") %>% 
  filter(age %in% c(80, 90)) %>% 
  filter(code %in% high_income_countries) %>% 
  filter(between(year, 1950, 2016)) %>% 
  group_by(year, age, sex) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  ggplot(aes(x = year, y = mean_Mx, colour = sex)) + 
  facet_wrap(~age) +
  geom_point()

Let’s now use log rather than linear

dta_Mx %>% 
  filter(sex != "total") %>% 
  filter(age %in% c(80, 90)) %>% 
  filter(code %in% high_income_countries) %>% 
  filter(between(year, 1950, 2016)) %>% 
  group_by(year, age, sex) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  ggplot(aes(x = year, y = mean_Mx, colour = sex)) + 
  facet_wrap(~age) +
  geom_point() +
  scale_y_log10()

So, the trends on % reductions have been more continuous for age 90 than age 80, and more continuous for females than males.

Let’s now do this for a couple of additional age groups

dta_Mx %>% 
  filter(sex != "total") %>% 
  filter(age %in% c(0, 30, 80, 90)) %>% 
  filter(code %in% high_income_countries) %>% 
  filter(between(year, 1950, 2016)) %>% 
  group_by(year, age, sex) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  ggplot(aes(x = year, y = mean_Mx, colour = sex)) + 
  facet_wrap(~age) +
  geom_point() +
  scale_y_log10()

Now let’s estimate the correlation between these trends

dta_trnd <- dta_Mx %>% 
  filter(sex == "total") %>% 
  filter(between(year, 1955, 2016))  %>% 
  filter(age %in% c(0, 30, 80, 90)) %>% 
  group_by(year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx, 10)) 
dta_trnd %>% 
  select(-mean_Mx) %>% 
  mutate(age = paste0("age_", age)) %>% 
  spread(age, log_mean_Mx) %>%
  select(-year) %>% 
  cor()
           age_0    age_30    age_80    age_90
age_0  1.0000000 0.9201582 0.9765313 0.9827364
age_30 0.9201582 1.0000000 0.9502384 0.9410485
age_80 0.9765313 0.9502384 1.0000000 0.9855633
age_90 0.9827364 0.9410485 0.9855633 1.0000000

So, as expected, % improvements in infancy are more strongly correlated with those at age 80 and 90 than at age 30, and % changes at age 90 are less strongly correlated than with those at other ages.

Let’s do this as a heatmap for all ages

dta_trnd <- dta_Mx %>% 
  filter(sex == "total") %>% 
  filter(between(year, 1955, 2016))  %>% 
  filter(age <= 109) %>% 
  group_by(year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx, 10)) 
tmp <- dta_trnd %>% 
  select(-mean_Mx) %>% 
  spread(age, log_mean_Mx) %>%
  select(-year) %>% 
  cor() 
cor_df <- tmp %>% 
  as_tibble() %>% 
  mutate(from_age = rownames(tmp)) %>% 
  gather(key="to_age", value = "value", -from_age) %>% 
  mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
cor_df %>% 
  filter(from_age <= 100, to_age <= 100) %>% 
  ggplot(aes(x = from_age, y = to_age, fill = value)) + 
  geom_tile() +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(0, 100, by = 10)) +
  scale_y_continuous(breaks = seq(0, 100, by = 10)) +
  coord_equal()

Definitely a ‘wow’ figure!

Interesting that the trends that apply at older ages don’t apply at the oldest ages (from around age 96 onwards)

Let’s see how different it looks by gender.

dta_trnd <- dta_Mx %>% 
  filter(sex != "total") %>% 
  filter(between(year, 1955, 2016))  %>% 
  filter(age <= 109) %>% 
  group_by(sex, year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx, 10)) %>% 
  group_by(sex) %>% 
  nest()
cors_df <- dta_trnd %>% 
  mutate(cors = map(
    data, 
    function(X) {
      X %>% 
        select(-mean_Mx) %>% 
        spread(age, log_mean_Mx) %>% 
        select(-year) %>% 
        cor()
      }
    )
  ) %>% 
  mutate(cor_df = map(
    cors,
    function(X){
      X %>% 
        as_tibble() %>% 
        mutate(from_age = rownames(X)) %>% 
        gather(key = "to_age", value = "value", -from_age) %>% 
        mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
      }
    )
  ) %>% 
  select(sex, cor_df) %>% 
  unnest()
cors_df %>% 
  filter(from_age <= 100, to_age <= 100) %>% 
  ggplot(aes(x = from_age, y = to_age, fill = value)) + 
  geom_tile() +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(0, 100, by = 10)) +
  scale_y_continuous(breaks = seq(0, 100, by = 10)) +
  coord_equal() + 
  facet_wrap(~sex)

Again, beautiful, staggering, and awesome. This is like seeing the genome of population health improvement being mapped. The gender differences in the differences in correlation are very apparent.

Let’s extend this to a few few select countries:

dta_trnd <- dta_Mx %>% 
  filter(sex != "total") %>%
  filter(code %in% c("FRATNP", "SWE", "GBR_NP", "USA", "ESP", "JPN")) %>% 
  filter(between(year, 1955, 2016))  %>% 
  filter(age <= 109) %>% 
  group_by(code, sex, year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
  group_by(sex, code) %>% 
  nest()
cors_df <- dta_trnd %>% 
  mutate(cors = map(
    data, 
    function(X) {
      X %>% 
        select(-mean_Mx) %>% 
        spread(age, log_mean_Mx) %>% 
        select(-year) %>% 
        cor()
      }
    )
  ) %>% 
  mutate(cor_df = map(
    cors,
    function(X){
      X %>% 
        as_tibble() %>% 
        mutate(from_age = rownames(X)) %>% 
        gather(key = "to_age", value = "value", -from_age) %>% 
        mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
      }
    )
  ) %>% 
  select(sex, code, cor_df) %>% 
  unnest()
cors_df %>% 
  filter(from_age <= 100, to_age <= 100) %>% 
  ggplot(aes(x = from_age, y = to_age, fill = value)) + 
  geom_tile() +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(0, 100, by = 10)) +
  scale_y_continuous(breaks = seq(0, 100, by = 10)) +
  coord_equal() + 
  facet_grid(sex ~ code)

Let’s do this just for the UK nations

dta_trnd <- dta_Mx %>% 
  filter(sex != "total") %>%
  filter(code %in% c("GBRTENW", "GBR_SCO", "GBR_NIR")) %>% 
  filter(between(year, 1955, 2016))  %>% 
  filter(age <= 109) %>% 
  group_by(code, sex, year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
  group_by(sex, code) %>% 
  nest()
cors_df <- dta_trnd %>% 
  mutate(cors = map(
    data, 
    function(X) {
      X %>% 
        select(-mean_Mx) %>% 
        spread(age, log_mean_Mx) %>% 
        select(-year) %>% 
        cor()
      }
    )
  ) %>% 
  mutate(cor_df = map(
    cors,
    function(X){
      X %>% 
        as_tibble() %>% 
        mutate(from_age = rownames(X)) %>% 
        gather(key = "to_age", value = "value", -from_age) %>% 
        mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
      }
    )
  ) %>% 
  select(sex, code, cor_df) %>% 
  unnest()
cors_df %>% 
  filter(from_age <= 100, to_age <= 100) %>% 
  ggplot(aes(x = from_age, y = to_age, fill = value)) + 
  geom_tile() +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(0, 100, by = 10)) +
  scale_y_continuous(breaks = seq(0, 100, by = 10)) +
  coord_equal() + 
  facet_grid(sex ~code)

For Scotland and England & Wales (independently), how have the correlations changed over time?

England/Wales first:

dta_trnd <- dta_Mx %>% 
  filter(sex != "total") %>%
  filter(code %in% c("GBRTENW")) %>% 
  filter(between(year, 1950, 2010))  %>%
  filter(age <= 109) %>% 
  mutate(
    decade = cut(year, breaks = seq(1950, 2010, by = 10), labels = c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s"), include.lowest = TRUE)
  ) %>% 
  group_by(sex, decade, year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx, 10)) %>% # Correction for Sweden
  group_by(sex, decade) %>% 
  nest()
cors_df <- dta_trnd %>% 
  mutate(cors = map(
    data, 
    function(X) {
      X %>% 
        select(-mean_Mx) %>% 
        spread(age, log_mean_Mx) %>% 
        select(-year) %>% 
        cor()
      }
    )
  ) %>% 
  mutate(cor_df = map(
    cors,
    function(X){
      X %>% 
        as_tibble() %>% 
        mutate(from_age = rownames(X)) %>% 
        gather(key = "to_age", value = "value", -from_age) %>% 
        mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
      }
    )
  ) %>% 
  select(sex, decade, cor_df) %>% 
  unnest()
cors_df %>% 
  filter(from_age <= 100, to_age <= 100) %>% 
  ggplot(aes(x = from_age, y = to_age, fill = value)) + 
  geom_tile() +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(0, 100, by = 10)) +
  scale_y_continuous(breaks = seq(0, 100, by = 10)) +
  coord_equal() + 
  facet_grid(sex ~ decade)

Now for Scotland.

dta_trnd <- dta_Mx %>% 
  filter(sex != "total") %>%
  filter(code %in% c("GBR_SCO")) %>% 
  filter(between(year, 1950, 2010))  %>%
  filter(age <= 109) %>% 
  mutate(
    decade = cut(year, breaks = seq(1950, 2010, by = 10), labels = c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s"), include.lowest = TRUE)
  ) %>% 
  group_by(sex, decade, year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
  group_by(sex, decade) %>% 
  nest()
cors_df <- dta_trnd %>% 
  mutate(cors = map(
    data, 
    function(X) {
      X %>% 
        select(-mean_Mx) %>% 
        spread(age, log_mean_Mx) %>% 
        select(-year) %>% 
        cor()
      }
    )
  ) %>% 
  mutate(cor_df = map(
    cors,
    function(X){
      X %>% 
        as_tibble() %>% 
        mutate(from_age = rownames(X)) %>% 
        gather(key = "to_age", value = "value", -from_age) %>% 
        mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
      }
    )
  ) %>% 
  select(sex, decade, cor_df) %>% 
  unnest()
cors_df %>% 
  filter(from_age <= 100, to_age <= 100) %>% 
  ggplot(aes(x = from_age, y = to_age, fill = value)) + 
  geom_tile() +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(0, 100, by = 10)) +
  scale_y_continuous(breaks = seq(0, 100, by = 10)) +
  coord_equal() + 
  facet_grid(sex ~ decade)

dta_trnd <- dta_Mx %>% 
  filter(sex != "total") %>%
  filter(code %in% c("GBRTENW")) %>% 
  filter(between(year, 1955, 2015))  %>%
  filter(age <= 109) %>% 
  mutate(
    decade = cut(year, breaks = seq(1955, 2015, by = 10), include.lowest = TRUE)
  ) %>% 
  group_by(sex, decade, year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx, 10)) %>% # Correction for Sweden
  group_by(sex, decade) %>% 
  nest()
cors_df <- dta_trnd %>% 
  mutate(cors = map(
    data, 
    function(X) {
      X %>% 
        select(-mean_Mx) %>% 
        spread(age, log_mean_Mx) %>% 
        select(-year) %>% 
        cor()
      }
    )
  ) %>% 
  mutate(cor_df = map(
    cors,
    function(X){
      X %>% 
        as_tibble() %>% 
        mutate(from_age = rownames(X)) %>% 
        gather(key = "to_age", value = "value", -from_age) %>% 
        mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
      }
    )
  ) %>% 
  select(sex, decade, cor_df) %>% 
  unnest()
cors_df %>% 
  filter(from_age <= 100, to_age <= 100) %>% 
  ggplot(aes(x = from_age, y = to_age, fill = value)) + 
  geom_tile() +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(0, 100, by = 10)) +
  scale_y_continuous(breaks = seq(0, 100, by = 10)) +
  coord_equal() + 
  facet_grid(sex ~ decade)

Now for Scotland.

dta_trnd <- dta_Mx %>% 
  filter(sex != "total") %>%
  filter(code %in% c("GBR_SCO")) %>% 
  filter(between(year, 1955, 2015))  %>%
  filter(age <= 109) %>% 
  mutate(
    decade = cut(year, breaks = seq(1955, 2015, by = 10), include.lowest = TRUE)
  ) %>% 
  group_by(sex, decade, year, age) %>% 
  summarise(mean_Mx = mean(Mx, na.rm = T)) %>% 
  ungroup() %>% 
  mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
  group_by(sex, decade) %>% 
  nest()
cors_df <- dta_trnd %>% 
  mutate(cors = map(
    data, 
    function(X) {
      X %>% 
        select(-mean_Mx) %>% 
        spread(age, log_mean_Mx) %>% 
        select(-year) %>% 
        cor()
      }
    )
  ) %>% 
  mutate(cor_df = map(
    cors,
    function(X){
      X %>% 
        as_tibble() %>% 
        mutate(from_age = rownames(X)) %>% 
        gather(key = "to_age", value = "value", -from_age) %>% 
        mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
      }
    )
  ) %>% 
  select(sex, decade, cor_df) %>% 
  unnest()
cors_df %>% 
  filter(from_age <= 100, to_age <= 100) %>% 
  ggplot(aes(x = from_age, y = to_age, fill = value)) + 
  geom_tile() +
  scale_fill_viridis_c() +
  scale_x_continuous(breaks = seq(0, 100, by = 10)) +
  scale_y_continuous(breaks = seq(0, 100, by = 10)) +
  coord_equal() + 
  facet_grid(sex ~ decade)

LS0tDQp0aXRsZTogIkFnZSBTcGVjaWZpYyBNb3J0YWxpdHkgUmF0ZSBUcmVuZHMgIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBJbnRybw0KDQpUaGlzIGRvYyB3aWxsIGRlc2NyaWJlIHNvbWUgb2YgdGhlIGF2ZXJhZ2UgdHJlbmRzIGF0IHNwZWNpZmljIGFnZXMgZnJvbSAyMSBoaWdoIGluY29tZSBjb3VudHJpZXMuDQoNCkl0IGlzIGluc3BpcmVkIGJ5IFtDaHJpc3RlbnNlbiAyMDEwXShodHRwczovL3d3dy50aGVsYW5jZXQuY29tL2pvdXJuYWxzL2xhbmNldC9hcnRpY2xlL1BJSVMwMTQwLTY3MzYoMDkpNjE0NjAtNC9mdWxsdGV4dCksIHdoaWNoIHNob3dlZA0KDQoqIFByb2JhYmlsaXR5IG9mIGR5aW5nIGluIG5leHQgMTIgbW9udGhzDQoqIEF0IGFnZSA4MCBhbmQgYWdlIDkwDQoqIE1hbGVzIGFuZCBmZW1hbGVzIA0KKiBTZWxlY3RlZCBjb3VudHJpZXMgDQogICAgKiBFbmdsYW5kICYgV2FsZXMNCiAgICAqIEZyYW5jZQ0KICAgICogRWFzdCBHZXJtYW55DQogICAgKiBXZXN0IEdlcm1hbnkNCiAgICAqIEphcGFuDQogICAgKiBTd2VkZW4NCiAgICAqIFVTQQ0KICANCiAgDQpIb3dldmVyLCBhcyBwZXIgdGhlIGZpZ3VyZXMgaW4gW1doaXRlIDIwMDJdKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL3BkZi8xMC4xMTExL2ouMTcyOC00NDU3LjIwMDIuMDAwNTkueCkpLCB0aGUgYXZlcmFnZSBvZiAyMSBoaWdoIGluY29tZSBjb3VudHJpZXMgd2lsbCBiZSB1c2VkIGluc3RlYWQsIGFzIHdlbGwgYXMgdGhlIGJlc3QtcGVyZm9ybWluZyBjb3VudHJ5IGF0IHRoZXNlIGRpZmZlcmVudCBhZ2UgZ3JvdXBzLiANCg0KQWRkaXRpb25hbGx5LCB0aGUgMTIgbW9udGggbW9ydGFsaXR5IHByb2JhYmlsaXRpZXMgYXQgdGhlIGZvbGxvd2luZyBhZ2VzIHdpbGwgYWxzbyBiZSBjYWxjdWxhdGVkOg0KDQoqIDAtMSB5ZWFycw0KKiA0MCB5ZWFycw0KDQpUaGUgYWltIHdpbGwgYmUgdG8gZGV0ZXJtaW5lIGhvdyBzdHJvbmdseSB0aGUgKGxvZyBvZiB0aGVzZSkgdHJlbmRzIGFyZSBjb3JyZWxhdGVkLiANCg0KIyBQcmUgcmVxcw0KDQoNCmBgYHtyfQ0KcGFjbWFuOjpwX2xvYWQoDQogIHRpZHl2ZXJzZSwgSE1ESEZEcGx1cywNCiAgZ2dyZXBlbA0KKQ0KDQpkdGFfTXggPC0gcmVhZF9yZHMoInRpZHlfZGF0YS9NeF9kYXRhLnJkcyIpDQoNCmBgYA0KDQogRGVmaW5lIGNvdW50cmllcyANCmBgYHtyfQ0Kc291cmNlKCJzY3JpcHRzL2NvdW50cnlfZGVmaW5pdGlvbnMuUiIpDQoNCg0KYGBgDQoNCiMgUmVwbGljYXRpb24gb2YgRmlnIDIgb2YgQ2hyaXN0ZW5zb24gZXQgYWwgMjAxMA0KDQpgYGB7cn0NCg0KZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihhZ2UgJWluJSBjKDgwLCA5MCkpICU+JSANCiAgZmlsdGVyKA0KICAgIGNvZGUgJWluJSBjKCJHQlJURU5XIiwgIkZSQVROUCIsICJERVVURSIsICJERVVUVyIsICJKUE4iLCAiU1dFIiwgIlVTQSIpDQogICkgJT4lICAgICAgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMDMpKSAlPiUgDQogIGdncGxvdCgNCiAgICBhZXMoeCA9IHllYXIsIHkgPSBNeCwgY29sb3VyID0gY29kZSwgZ3JvdXAgPSBjb2RlKQ0KICApICsgDQogIGdlb21fbGluZSgpICsNCiAgZmFjZXRfZ3JpZChhZ2UgfiBzZXgpDQoNCmBgYA0KDQpBbmQgaG93IGhhcyB0aGlzIGRldmVsb3BlZCBzaW5jZT8NCg0KYGBge3J9DQoNCmR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYWdlICVpbiUgYyg4MCwgOTApKSAlPiUgDQogIGZpbHRlcigNCiAgICBjb2RlICVpbiUgYygiR0JSVEVOVyIsICJGUkFUTlAiLCAiREVVVEUiLCAiREVVVFciLCAiSlBOIiwgIlNXRSIsICJVU0EiKQ0KICApICU+JSAgICAgIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTUwLCAyMDE3KSkgJT4lIA0KICBnZ3Bsb3QoDQogICAgYWVzKHggPSB5ZWFyLCB5ID0gTXgsIGNvbG91ciA9IGNvZGUsIGdyb3VwID0gY29kZSkNCiAgKSArIA0KICBnZW9tX2xpbmUoKSArDQogIGZhY2V0X2dyaWQoYWdlIH4gc2V4KQ0KDQpgYGANCg0KSXQncyBpbnRlcmVzdGluZyB0aGF0IGxpZmUgZXhwZWN0YW5jaWVzIGhhdmUgYXQgYWdlIDgwIGhhdmUgY29udmVyZ2VkLCBmb3IgbWFsZXMgZXNwZWNpYWxseS4gDQpBbmQgdGhhdCB0aGV5J3ZlIGxldmVsbGVkIG9mZiBpbiBKYXBhbiBvdmVyIHRoZSAyMDAwcywgd2hpbGUgY29udGludWVkIHRvIGltcHJvdmUgaW4gdGhlIFVTQS4gDQoNCkluIEVuZ2xhbmQvV2FsZXMgdGhleSBNeCBhdCBhZ2UgOTAgc2VlbSB0byBoYXZlIGluY3JlYXNlZCBmb3IgZmVtYWxlcyBlc3BlY2lhbGx5IGluIHJlY2VudCB5ZWFycy4gDQoNCkxldCdzIGxvb2sgYXQgdGhpcyBmb3IgdGhlIGF2ZXJhZ2Ugb2YgdGhlIDIxIGNvdW50cmllcyANCg0KTm93IGxldCdzIGRvIHRoaXMgZm9yIHRoZSBhdmVyYWdlIG9mIHRoZSAyMSBoaWdoIGluY29tZSBjb3VudHJpZXMgDQoNCmBgYHtyfQ0KDQpkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JSANCiAgZmlsdGVyKGFnZSAlaW4lIGMoODAsIDkwKSkgJT4lIA0KICBmaWx0ZXIoY29kZSAlaW4lIGhpZ2hfaW5jb21lX2NvdW50cmllcykgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTUwLCAyMDE2KSkgJT4lIA0KICBncm91cF9ieSh5ZWFyLCBhZ2UsIHNleCkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IG1lYW5fTXgsIGNvbG91ciA9IHNleCkpICsgDQogIGZhY2V0X3dyYXAofmFnZSkgKw0KICBnZW9tX3BvaW50KCkNCg0KDQpgYGANCg0KTGV0J3Mgbm93IHVzZSBsb2cgcmF0aGVyIHRoYW4gbGluZWFyIA0KDQpgYGB7cn0NCmR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYWdlICVpbiUgYyg4MCwgOTApKSAlPiUgDQogIGZpbHRlcihjb2RlICVpbiUgaGlnaF9pbmNvbWVfY291bnRyaWVzKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTYpKSAlPiUgDQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbWVhbl9NeCwgY29sb3VyID0gc2V4KSkgKyANCiAgZmFjZXRfd3JhcCh+YWdlKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3lfbG9nMTAoKQ0KDQoNCmBgYA0KDQpTbywgdGhlIHRyZW5kcyBvbiAlIHJlZHVjdGlvbnMgaGF2ZSBiZWVuIG1vcmUgY29udGludW91cyBmb3IgYWdlIDkwIHRoYW4gYWdlIDgwLCBhbmQgbW9yZSBjb250aW51b3VzIGZvciBmZW1hbGVzIHRoYW4gbWFsZXMuIA0KDQpMZXQncyBub3cgZG8gdGhpcyBmb3IgYSBjb3VwbGUgb2YgYWRkaXRpb25hbCBhZ2UgZ3JvdXBzIA0KDQpgYGB7cn0NCmR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYWdlICVpbiUgYygwLCAzMCwgODAsIDkwKSkgJT4lIA0KICBmaWx0ZXIoY29kZSAlaW4lIGhpZ2hfaW5jb21lX2NvdW50cmllcykgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTUwLCAyMDE2KSkgJT4lIA0KICBncm91cF9ieSh5ZWFyLCBhZ2UsIHNleCkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IG1lYW5fTXgsIGNvbG91ciA9IHNleCkpICsgDQogIGZhY2V0X3dyYXAofmFnZSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV95X2xvZzEwKCkNCg0KDQpgYGANCg0KTm93IGxldCdzIGVzdGltYXRlIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZXNlIHRyZW5kcw0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggPT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE2KSkgICU+JSANCiAgZmlsdGVyKGFnZSAlaW4lIGMoMCwgMzAsIDgwLCA5MCkpICU+JSANCiAgZ3JvdXBfYnkoeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCwgMTApKSANCg0KZHRhX3RybmQgJT4lIA0KICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgbXV0YXRlKGFnZSA9IHBhc3RlMCgiYWdlXyIsIGFnZSkpICU+JSANCiAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JQ0KICBzZWxlY3QoLXllYXIpICU+JSANCiAgY29yKCkNCg0KYGBgDQoNClNvLCBhcyBleHBlY3RlZCwgJSBpbXByb3ZlbWVudHMgaW4gaW5mYW5jeSBhcmUgbW9yZSBzdHJvbmdseSBjb3JyZWxhdGVkIHdpdGggdGhvc2UgYXQgYWdlIDgwIGFuZCA5MCB0aGFuIGF0IGFnZSAzMCwgYW5kICUgY2hhbmdlcyBhdCBhZ2UgOTAgYXJlIGxlc3Mgc3Ryb25nbHkgY29ycmVsYXRlZCB0aGFuIHdpdGggdGhvc2UgYXQgb3RoZXIgYWdlcy4gDQoNCkxldCdzIGRvIHRoaXMgYXMgYSBoZWF0bWFwIGZvciBhbGwgYWdlcyANCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ID09ICJ0b3RhbCIpICU+JSANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1NSwgMjAxNikpICAlPiUgDQogIGZpbHRlcihhZ2UgPD0gMTA5KSAlPiUgDQogIGdyb3VwX2J5KHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXgsIDEwKSkgDQoNCnRtcCA8LSBkdGFfdHJuZCAlPiUgDQogIHNlbGVjdCgtbWVhbl9NeCkgJT4lIA0KICBzcHJlYWQoYWdlLCBsb2dfbWVhbl9NeCkgJT4lDQogIHNlbGVjdCgteWVhcikgJT4lIA0KICBjb3IoKSANCg0KY29yX2RmIDwtIHRtcCAlPiUgDQogIGFzX3RpYmJsZSgpICU+JSANCiAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXModG1wKSkgJT4lIA0KICBnYXRoZXIoa2V5PSJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KDQpjb3JfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkNCg0KYGBgDQoNCg0KRGVmaW5pdGVseSBhICd3b3cnIGZpZ3VyZSEgDQoNCkludGVyZXN0aW5nIHRoYXQgdGhlIHRyZW5kcyB0aGF0IGFwcGx5IGF0IG9sZGVyIGFnZXMgZG9uJ3QgYXBwbHkgYXQgdGhlIG9sZGVzdCBhZ2VzIChmcm9tIGFyb3VuZCBhZ2UgOTYgb253YXJkcykNCg0KTGV0J3Mgc2VlIGhvdyBkaWZmZXJlbnQgaXQgbG9va3MgYnkgZ2VuZGVyLg0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE2KSkgICU+JSANCiAgZmlsdGVyKGFnZSA8PSAxMDkpICU+JSANCiAgZ3JvdXBfYnkoc2V4LCB5ZWFyLCBhZ2UpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShsb2dfbWVhbl9NeCA9IGxvZyhtZWFuX014LCAxMCkpICU+JSANCiAgZ3JvdXBfYnkoc2V4KSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBjb3JfZGYpICU+JSANCiAgdW5uZXN0KCkNCg0KDQpjb3JzX2RmICU+JSANCiAgZmlsdGVyKGZyb21fYWdlIDw9IDEwMCwgdG9fYWdlIDw9IDEwMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmcm9tX2FnZSwgeSA9IHRvX2FnZSwgZmlsbCA9IHZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBjb29yZF9lcXVhbCgpICsgDQogIGZhY2V0X3dyYXAofnNleCkNCg0KYGBgDQoNCkFnYWluLCBiZWF1dGlmdWwsIHN0YWdnZXJpbmcsIGFuZCBhd2Vzb21lLiBUaGlzIGlzIGxpa2Ugc2VlaW5nIHRoZSBnZW5vbWUgb2YgcG9wdWxhdGlvbiBoZWFsdGggaW1wcm92ZW1lbnQgYmVpbmcgbWFwcGVkLiBUaGUgZ2VuZGVyIGRpZmZlcmVuY2VzIGluIHRoZSBkaWZmZXJlbmNlcyBpbiBjb3JyZWxhdGlvbiBhcmUgdmVyeSBhcHBhcmVudC4gDQoNCkxldCdzIGV4dGVuZCB0aGlzIHRvIGEgZmV3IGZldyBzZWxlY3QgY291bnRyaWVzOiANCg0KKiBGcmFuY2UNCiogU3dlZGVuPw0KKiBVSw0KKiBVU0EgDQoqIFNwYWluIChJZGVudGlmaWVkIGFzIG1vc3QgY29tcGF0aWJsZSB3aXRoIExlZS1DYXJ0ZXIgbW9kZWxsaW5nIGFwcHJvYWNoKQ0KKiBKYXBhbg0KDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUNCiAgZmlsdGVyKGNvZGUgJWluJSBjKCJGUkFUTlAiLCAiU1dFIiwgIkdCUl9OUCIsICJVU0EiLCAiRVNQIiwgIkpQTiIpKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTUsIDIwMTYpKSAgJT4lIA0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBncm91cF9ieShjb2RlLCBzZXgsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXggKyAwLjAwMDAxLCAxMCkpICU+JSAjIENvcnJlY3Rpb24gZm9yIFN3ZWRlbg0KICBncm91cF9ieShzZXgsIGNvZGUpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGNvZGUsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfZ3JpZChzZXggfiBjb2RlKQ0KDQpgYGANCg0KDQpMZXQncyBkbyB0aGlzIGp1c3QgZm9yIHRoZSBVSyBuYXRpb25zIA0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lDQogIGZpbHRlcihjb2RlICVpbiUgYygiR0JSVEVOVyIsICJHQlJfU0NPIiwgIkdCUl9OSVIiKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE2KSkgICU+JSANCiAgZmlsdGVyKGFnZSA8PSAxMDkpICU+JSANCiAgZ3JvdXBfYnkoY29kZSwgc2V4LCB5ZWFyLCBhZ2UpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShsb2dfbWVhbl9NeCA9IGxvZyhtZWFuX014ICsgMC4wMDAwMSwgMTApKSAlPiUgIyBDb3JyZWN0aW9uIGZvciBTd2VkZW4NCiAgZ3JvdXBfYnkoc2V4LCBjb2RlKSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBjb2RlLCBjb3JfZGYpICU+JSANCiAgdW5uZXN0KCkNCg0KDQpjb3JzX2RmICU+JSANCiAgZmlsdGVyKGZyb21fYWdlIDw9IDEwMCwgdG9fYWdlIDw9IDEwMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmcm9tX2FnZSwgeSA9IHRvX2FnZSwgZmlsbCA9IHZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBjb29yZF9lcXVhbCgpICsgDQogIGZhY2V0X2dyaWQoc2V4IH5jb2RlKQ0KDQoNCmBgYA0KDQpGb3IgU2NvdGxhbmQgYW5kIEVuZ2xhbmQgJiBXYWxlcyAoaW5kZXBlbmRlbnRseSksIGhvdyBoYXZlIHRoZSBjb3JyZWxhdGlvbnMgY2hhbmdlZCBvdmVyIHRpbWU/IA0KDQpFbmdsYW5kL1dhbGVzIGZpcnN0OiANCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkdCUlRFTlciKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTUwLCAyMDEwKSkgICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGVjYWRlID0gY3V0KHllYXIsIGJyZWFrcyA9IHNlcSgxOTUwLCAyMDEwLCBieSA9IDEwKSwgbGFiZWxzID0gYygiMTk1MHMiLCAiMTk2MHMiLCAiMTk3MHMiLCAiMTk4MHMiLCAiMTk5MHMiLCAiMjAwMHMiKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KICApICU+JSANCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXgsIDEwKSkgJT4lICMgQ29ycmVjdGlvbiBmb3IgU3dlZGVuDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlKSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBkZWNhZGUsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfZ3JpZChzZXggfiBkZWNhZGUpDQoNCmBgYA0KDQoNCg0KTm93IGZvciBTY290bGFuZC4gDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUNCiAgZmlsdGVyKGNvZGUgJWluJSBjKCJHQlJfU0NPIikpICU+JSANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1MCwgMjAxMCkpICAlPiUNCiAgZmlsdGVyKGFnZSA8PSAxMDkpICU+JSANCiAgbXV0YXRlKA0KICAgIGRlY2FkZSA9IGN1dCh5ZWFyLCBicmVha3MgPSBzZXEoMTk1MCwgMjAxMCwgYnkgPSAxMCksIGxhYmVscyA9IGMoIjE5NTBzIiwgIjE5NjBzIiwgIjE5NzBzIiwgIjE5ODBzIiwgIjE5OTBzIiwgIjIwMDBzIiksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkNCiAgKSAlPiUgDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlLCB5ZWFyLCBhZ2UpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShsb2dfbWVhbl9NeCA9IGxvZyhtZWFuX014ICsgMC4wMDAwMSwgMTApKSAlPiUgIyBDb3JyZWN0aW9uIGZvciBTd2VkZW4NCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGRlY2FkZSwgY29yX2RmKSAlPiUgDQogIHVubmVzdCgpDQoNCg0KY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKSArIA0KICBmYWNldF9ncmlkKHNleCB+IGRlY2FkZSkNCg0KYGBgDQoNCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkdCUlRFTlciKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE1KSkgICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGVjYWRlID0gY3V0KHllYXIsIGJyZWFrcyA9IHNlcSgxOTU1LCAyMDE1LCBieSA9IDEwKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KICApICU+JSANCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXgsIDEwKSkgJT4lICMgQ29ycmVjdGlvbiBmb3IgU3dlZGVuDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlKSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBkZWNhZGUsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfZ3JpZChzZXggfiBkZWNhZGUpDQoNCmBgYA0KDQoNCg0KTm93IGZvciBTY290bGFuZC4gDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUNCiAgZmlsdGVyKGNvZGUgJWluJSBjKCJHQlJfU0NPIikpICU+JSANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1NSwgMjAxNSkpICAlPiUNCiAgZmlsdGVyKGFnZSA8PSAxMDkpICU+JSANCiAgbXV0YXRlKA0KICAgIGRlY2FkZSA9IGN1dCh5ZWFyLCBicmVha3MgPSBzZXEoMTk1NSwgMjAxNSwgYnkgPSAxMCksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkNCiAgKSAlPiUgDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlLCB5ZWFyLCBhZ2UpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShsb2dfbWVhbl9NeCA9IGxvZyhtZWFuX014ICsgMC4wMDAwMSwgMTApKSAlPiUgIyBDb3JyZWN0aW9uIGZvciBTd2VkZW4NCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGRlY2FkZSwgY29yX2RmKSAlPiUgDQogIHVubmVzdCgpDQoNCg0KY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKSArIA0KICBmYWNldF9ncmlkKHNleCB+IGRlY2FkZSkNCg0KYGBg