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:
- France
- Sweden?
- UK
- USA
- Spain (Identified as most compatible with Lee-Carter modelling approach)
- Japan
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