Data Visualization Exercise

Dataset: TidyTuesday 2020 Week 02 - Transit Cost Project

Data source: Transit Costs Project

Dataset features (from TidyTuesday):

Load packages

library(tidyverse)
library(janitor)
library(Hmisc)
library(ggsci)
library(wesanderson)
library(patchwork)
library(countrycode)

Import data

transit_cost <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-01-05/transit_cost.csv')

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  e = col_double(),
  rr = col_double(),
  length = col_double(),
  tunnel = col_double(),
  stations = col_double(),
  cost = col_double(),
  year = col_double(),
  ppp_rate = col_double(),
  cost_km_millions = col_double()
)
ℹ Use `spec()` for the full column specifications.
dim(transit_cost) 
[1] 544  20

Data preparation

# drop obs with no ID
transit_cost = transit_cost %>% filter(!is.na(e))
dim(transit_cost)
[1] 537  20
# parse numeric variables interpreted as character
transit_cost <- transit_cost %>% 
  mutate(tunnel_per = replace_na(parse_number(tunnel_per), 0),
         real_cost = parse_number(real_cost),
         across(start_year:end_year, as.numeric))
Problem with `mutate()` input `..3`.
ℹ NAs introduced by coercion
ℹ Input `..3` is `across(start_year:end_year, as.numeric)`.NAs introduced by coercionProblem with `mutate()` input `..3`.
ℹ NAs introduced by coercion
ℹ Input `..3` is `across(start_year:end_year, as.numeric)`.NAs introduced by coercion
# number of countries
length(unique(transit_cost$country))
[1] 56
# get country names
transit_cost$country2= countrycode(transit_cost$country,"iso2c", "country.name")
Some values were not matched unambiguously: UK
# get continent
transit_cost$continent <- countrycode(sourcevar = transit_cost[["country"]],
                            origin = "iso2c",
                            destination = "continent")
Some values were not matched unambiguously: UK
# change NA to United Kingdom 
transit_cost = transit_cost %>% mutate(country2=ifelse(is.na(country2),"United Kingdom",country2)) %>% mutate(continent=ifelse(is.na(continent),"Europe",continent))
# summary of project count by continent 
transit_cost %>% group_by(continent) %>% tally() %>% mutate(proportion=round(n/sum(n),3))
# cost/station
transit_cost$cost_station = transit_cost$real_cost/transit_cost$stations
# drop country with Nan in avg_cost_station
transit_cost = transit_cost %>% filter(country2!="Qatar")

# railroad and non-railroad 
Hmisc::describe(as.factor(transit_cost$rr)) 
as.factor(transit_cost$rr) 
       n  missing distinct 
     535        1        2 
                      
Value          0     1
Frequency    501    34
Proportion 0.936 0.064
# railroad df 
railroad = transit_cost %>% filter(rr=="1")
# non-railroad df 
not_railroad = transit_cost %>% filter(rr=="0")

Cost of non-railroad transit projects by continent

p1 = not_railroad %>% ggplot(aes(x=continent, y=real_cost, color=continent)) + geom_boxplot(outlier.color="white",alpha=0) + scale_color_uchicago() + theme(legend.position="none") + geom_jitter(shape=16, position=position_jitter(0.2), alpha=0.7) + labs(subtitle="Real Cost", y="Real cost in USD Millions", x="")
p2 = not_railroad %>% ggplot(aes(x=continent, y=cost_km_millions, color=continent)) + geom_boxplot(outlier.color="white",alpha=0) + scale_color_uchicago() + theme(legend.position="none") + geom_jitter(shape=16, position=position_jitter(0.2), alpha=0.7) + labs(subtitle="Cost per Kilometer", y="Cost/km in USD millions", x="")
p3 = not_railroad %>% ggplot(aes(x=continent, y=cost_station, color=continent)) + geom_boxplot(outlier.color="white",alpha=0) + scale_color_uchicago() + theme(legend.position="none") + geom_jitter(shape=16, position=position_jitter(0.2), alpha=0.7) + labs(subtitle="Cost per Station",y="Cost/station in USD millions", x="")
library(ggpubr)
figure1 = ggarrange(p1, p2, p3, ncol=2, nrow=2)
annotate_figure(figure1, top= text_grob("Costs of Transit Projects by Continent", color="black",face="bold",size=16), 
  bottom=text_grob("Data source: Transit Costs Project", size=10))

Countries with highest non-railroad project costs

# non_railroad project costs
nr_pc = not_railroad %>% group_by(country2) %>% summarise(count_project = n(), avg_real_cost = mean(real_cost,na.rm=TRUE), avg_cost_km = mean(cost_km_millions,na.rm=TRUE), avg_cost_station=mean(cost_station, na.rm=TRUE)) %>% as.data.frame()
# append count to country name
nr_pc$country2_p = paste0(nr_pc$country2,"(",nr_pc$count_project,")")
# individual plots
w1 = nr_pc %>% arrange(desc(count_project)) %>% head(10) %>% ggplot(aes(x=reorder(country2_p,count_project), y=count_project)) + geom_col(fill="#606c38", width=0.7) + coord_flip() + labs(y="Project count", x="Country(project count)", title= "Top 10 by project count") + theme_minimal() 

w2 = nr_pc %>% arrange(desc(avg_real_cost)) %>% head(10) %>% ggplot(aes(x=reorder(country2_p,avg_real_cost), y=avg_real_cost)) + geom_col(fill="#283618", width=0.7) + coord_flip() + labs(y="Avg. real cost (in USD millions)", x="", title= "Top 10 by average real cost") + theme_minimal() 

w3 = nr_pc %>% arrange(desc(avg_cost_km)) %>% head(10) %>% ggplot(aes(x=reorder(country2_p,avg_cost_km), y=avg_cost_km)) + geom_col(fill="#dda15e", width=0.7) + coord_flip() + labs(y="Avg. cost/km (in USD millions)", x="Country(project count)", title= "Top 10 by average cost/km") + theme_minimal() 

w4 = nr_pc %>% arrange(desc(avg_cost_station)) %>% head(10) %>% ggplot(aes(x=reorder(country2_p,avg_cost_station), y=avg_cost_station)) + geom_col(fill="#bc6c25", width=0.7) + coord_flip() + labs(y="Avg. cost/station (in USD millions)", x="", title= "Top 10 by average cost/station") + theme_minimal() 
# combined plot
figure3= ggarrange(w1,w2,w3,w4,ncol=2, nrow=2)
annotate_figure(figure3, top= text_grob("Non-railroad Transit Projects by Countries", color="black",face="bold",size=16), 
  bottom=text_grob("Data source: Transit Costs Project", size=10))

European cities with highest non-railroad project costs (excl. Russia)

# get averages
eu1= not_railroad %>% filter(continent=="Europe") %>% filter(country2!="Russia") %>% group_by(country, city) %>% summarise(projects_n=n(), avg_real_cost = mean(real_cost,na.rm=TRUE), avg_cost_km = mean(cost_km_millions,na.rm=TRUE), avg_cost_station=mean(cost_station, na.rm=TRUE))
`summarise()` regrouping output by 'country' (override with `.groups` argument)
# append country to city 
eu1$city_country = paste0(eu1$city,",",eu1$country)

# individual plots
e1 = eu1 %>% arrange(desc(projects_n)) %>% head(10) %>% ggplot(aes(x=reorder(city_country,projects_n), y=projects_n)) + geom_col(fill="#335c67", width=0.7) + coord_flip() + labs(y="Project count", x="City,Country", title= "Top 10 by project count") + theme_minimal() + scale_y_continuous(limits=c(0,11))

e2 = eu1 %>% arrange(desc(avg_real_cost)) %>% head(10) %>% ggplot(aes(x=reorder(city_country,avg_real_cost), y=avg_real_cost)) + geom_col(fill="#e09f3e", width=0.7) + coord_flip() + labs(y="Avg. real cost (in USD millions)", x="", title= "Top 10 by average real cost") + theme_minimal() + scale_y_continuous(limits=c(0,5500))

e3 = eu1 %>% arrange(desc(avg_cost_km)) %>% head(10) %>% ggplot(aes(x=reorder(city_country,avg_cost_km), y=avg_cost_km)) + geom_col(fill="#9e2a2b", width=0.7) + coord_flip() + labs(y="Avg. cost/km (in USD millions)", x="City,Country", title= "Top 10 by average cost/km") + theme_minimal() + scale_y_continuous(limits=c(0,550))

e4 =eu1 %>% arrange(desc(avg_cost_station)) %>% head(10) %>% ggplot(aes(x=reorder(city_country,avg_cost_station), y=avg_cost_station)) + geom_col(fill="#540b0e", width=0.7) + coord_flip() + labs(y="Avg. cost/station (in USD millions)", x="", title= "Top 10 by average cost/station") + theme_minimal() + scale_y_continuous(limits=c(0,850))
# combined plot
figure2= ggarrange(e1,e2,e3,e4,nrow=2,ncol=2)
annotate_figure(figure2, top= text_grob("European Cities Non-railroad Transit Projects", color="black",face="bold",size=16), 
  bottom=text_grob("Data source: Transit Costs Project", size=10))

Length/stations

# length_stations
transit_cost = transit_cost %>% mutate(length_stations = length/stations)
transit_cost$length_stations[sapply(transit_cost$length_stations, is.infinite)] <- NA
summary(transit_cost$length_stations)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
 0.5667  1.1424  1.3833  1.6889  1.8713 14.6667      10 

length/stations of railroad and non-railroad projects

#plot
transit_cost %>% filter(!is.na(rr)) %>% ggplot(aes(x=factor(rr), y=length_stations, color=factor(rr))) + geom_boxplot(outlier.color="white",alpha=0.1) + geom_jitter(shape=16, position=position_jitter(0.2), alpha=0.7) + scale_color_jama() + coord_flip() + theme_minimal() + theme(legend.position="none", panel.grid.minor.y=element_blank(),panel.grid.major.y=element_blank()) + labs(y="length(km) / stations", x="Is Railraod")

length/stations and city (non-railroad projects)

lsc = transit_cost %>% filter(rr==0) %>% group_by(city) %>% summarise(avg_length_stations = mean(length_stations)) %>% filter(!is.na(avg_length_stations))
`summarise()` ungrouping output (override with `.groups` argument)
summary(lsc$avg_length_stations)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.5667  1.0543  1.3056  1.4072  1.6467  3.5275 
# cities with lowest average length/stations
lsc %>% arrange(avg_length_stations) %>% head(5)
# cities with highest average length/stations
lsc %>% arrange(desc(avg_length_stations)) %>% head(5)
# density plot
ggplot(lsc, aes(x = avg_length_stations)) + geom_density(aes(y = ..count..), fill = "#5c6d70",alpha=0.7) +
  geom_vline(aes(xintercept = mean(avg_length_stations)), 
             linetype = "dashed", size = 0.6,
             color = "#FC4E07") +
  annotate("text", x = 1.8, y = 100, label = "Mean = 1.4 km") +
  theme_minimal() +
  labs(x="Avg. length/stations (km)",y="City count")

Length and real cost (non-railroad projects)

outliers_length = boxplot(not_railroad$length, plot=FALSE)$out # get outliers in length
min(outliers_length) # get minimum
[1] 66.7
# scatter plot with correlation coefficient and regression line
nrr1= not_railroad %>% filter(length <66.7) # drop outliers
ggscatter(nrr1, x = "length", y = "real_cost", add = "reg.line", color="#457b9d") +
  stat_cor(label.x = 3, label.y = 32000) +
  stat_regline_equation(label.x = 3, label.y = 30000) + labs(y="real_cost (in USD millions)", title="Real Cost ~ Length")

Stations and real cost (non-railroad projects)

outliers_stations = boxplot(not_railroad$stations, plot=FALSE)$out # outliers in stations
min(outliers_stations) # get minimum
[1] 43
nrr2= not_railroad %>% filter(stations < 43) # drop outliers
ggscatter(nrr2, x = "stations", y = "real_cost", add = "reg.line", color="#dda15e") +
  stat_cor(label.x = 3, label.y = 32000) +
  stat_regline_equation(label.x = 3, label.y = 30000) + labs(y="real_cost (in USD millions)", title="Real Cost ~ Stations")

Years to complete and real cost (non-railroad projects)

not_railroad = not_railroad %>% mutate(years_to_complete = end_year - start_year)
summary(not_railroad$years_to_complete)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  1.000   4.000   5.000   5.558   7.000  23.000      73 
outliers_years = boxplot(not_railroad$years_to_complete, plot=FALSE)$out
min(outliers_years)
[1] 12
nrr3= not_railroad %>% filter(stations < 12) # drop outliers
ggscatter(nrr3, x = "years_to_complete", y = "real_cost", add = "reg.line", color="#3c6e71") +
  stat_cor(label.x = 2, label.y = 32000) +
  stat_regline_equation(label.x = 2, label.y = 30000) + labs(y="real_cost (in USD millions)", title="Real Cost ~ Years to Complete")

LS0tCnRpdGxlOiAiVHJhbnNpdCBQcm9qZWN0cyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIERhdGEgVmlzdWFsaXphdGlvbiBFeGVyY2lzZQoKX19EYXRhc2V0X186IFtUaWR5VHVlc2RheSAyMDIwIFdlZWsgMDIgLSBUcmFuc2l0IENvc3QgUHJvamVjdF0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9ibG9iL21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wMS0wNS9yZWFkbWUubWQpICAKCl9fRGF0YSBzb3VyY2VfXzogW1RyYW5zaXQgQ29zdHMgUHJvamVjdF0oaHR0cHM6Ly90cmFuc2l0Y29zdHMuY29tLykKCl9fRGF0YXNldCBmZWF0dXJlc19fIChmcm9tIFtUaWR5VHVlc2RheV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9ibG9iL21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wMS0wNS9yZWFkbWUubWQpKToKCiAgKiBlOiBJRCAKICAqIGNvdW50cnk6IENvdW50cnkgQ29kZSAtIGNhbiBiZSBqb2luZWQgYWdhaW5zdCBjb3VudHJ5Y29kZSB2aWEgZWNiIG9yIGlzbzJjIAogICogY2l0eTogQ2l0eSB3aGVyZSB0cmFuc2l0IHR1bm5lbCBpcyBiZWluZyBjcmVhdGVkIAogICogbGluZTogTGluZSBuYW1lIG9yIHBhdGggCiAgKiBzdGFydF95ZWFyOiBZZWFyIHN0YXJ0ZWQgCiAgKiBlbmRfeWVhcjogWWVhciBlbmRlZCAocHJlZGljdGVkIG9yIGFjdHVhbCkgCiAgKiBycjogSSB0aGluayB0aGlzIGlzIFJhaWxyb2FkICgwIG9yIDEpLCB3aGVyZSAxID09IFJhaWxyb2FkPyAKICAqIGxlbmd0aDogTGVuZ3RoIG9mIHByb3Bvc2VkIGxpbmUgaW4ga20gCiAgKiB0dW5uZWxfcGVyOiBQZXJjZW50IG9mIGxpbmUgbGVuZ3RoIGNvbXBsZXRlZCAKICAqIHR1bm5lbDogVHVubmVsIGxlbmd0aCBvZiBsaW5lIGNvbXBsZXRlZCBpbiBrbSAoY2FuIHRha2UgdGhpcyBkaXZpZGVkIGJ5IGxlbmd0aCB0byBnZXQgdHVubmVsX3BlcikgCiAgKiBzdGF0aW9uczogTnVtYmVyIG9mIHN0YXRpb25zIHdoZXJlIHBhc3NlbmdlcnMgY2FuIGJvYXJkL2xlYXZlIAogICogc291cmNlMTogV2hlcmUgd2FzIGRhdGEgc291cmNlZCAKICAqIGNvc3Q6IENvc3QgaW4gbWlsbGlvbnMgb2YgbG9jYWwgY3VycmVuY3kgCiAgKiBjdXJyZW5jeTogQ3VycmVuY3kgdHlwZSAKICAqIHllYXI6IE1pZHBvaW50IHllYXIgb2YgY29uc3RydWN0aW9uIAogICogcHBwX3JhdGU6IHB1cmNoYXNpbmcgcG93ZXIgcGFyaXR5IChQUFApLCBiYXNlZCBvbiB0aGUgbWlkcG9pbnQgb2YgY29uc3RydWN0aW9uIAogICogcmVhbF9jb3N0OiBSZWFsIGNvc3QgaW4gTWlsbGlvbnMgb2YgVVNEIAogICogY29zdF9rbV9taWxsaW9uczogQ29zdC9rbSBpbiBtaWxsaW9ucyBvZiBVU0QgCiAgKiBzb3VyY2UyOiBXaGVyZSB3YXMgZGF0YSBzb3VyY2VkIGZvciBjb3N0IAogICogcmVmZXJlbmNlOiByZWZlcmVuY2UgCgoKIyMjIExvYWQgcGFja2FnZXMKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoSG1pc2MpCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkod2VzYW5kZXJzb24pCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGNvdW50cnljb2RlKQpgYGAKCiMjIyBJbXBvcnQgZGF0YQpgYGB7cn0KdHJhbnNpdF9jb3N0IDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDEtMDUvdHJhbnNpdF9jb3N0LmNzdicpCmRpbSh0cmFuc2l0X2Nvc3QpIApgYGAKCiMjIyBEYXRhIHByZXBhcmF0aW9uCmBgYHtyfQojIGRyb3Agb2JzIHdpdGggbm8gSUQKdHJhbnNpdF9jb3N0ID0gdHJhbnNpdF9jb3N0ICU+JSBmaWx0ZXIoIWlzLm5hKGUpKQpkaW0odHJhbnNpdF9jb3N0KQpgYGAKCmBgYHtyfQojIHBhcnNlIG51bWVyaWMgdmFyaWFibGVzIGludGVycHJldGVkIGFzIGNoYXJhY3Rlcgp0cmFuc2l0X2Nvc3QgPC0gdHJhbnNpdF9jb3N0ICU+JSAKICBtdXRhdGUodHVubmVsX3BlciA9IHJlcGxhY2VfbmEocGFyc2VfbnVtYmVyKHR1bm5lbF9wZXIpLCAwKSwKICAgICAgICAgcmVhbF9jb3N0ID0gcGFyc2VfbnVtYmVyKHJlYWxfY29zdCksCiAgICAgICAgIGFjcm9zcyhzdGFydF95ZWFyOmVuZF95ZWFyLCBhcy5udW1lcmljKSkKYGBgCgpgYGB7cn0KIyBudW1iZXIgb2YgY291bnRyaWVzCmxlbmd0aCh1bmlxdWUodHJhbnNpdF9jb3N0JGNvdW50cnkpKQojIGdldCBjb3VudHJ5IG5hbWVzCnRyYW5zaXRfY29zdCRjb3VudHJ5Mj0gY291bnRyeWNvZGUodHJhbnNpdF9jb3N0JGNvdW50cnksImlzbzJjIiwgImNvdW50cnkubmFtZSIpCiMgZ2V0IGNvbnRpbmVudAp0cmFuc2l0X2Nvc3QkY29udGluZW50IDwtIGNvdW50cnljb2RlKHNvdXJjZXZhciA9IHRyYW5zaXRfY29zdFtbImNvdW50cnkiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmlnaW4gPSAiaXNvMmMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzdGluYXRpb24gPSAiY29udGluZW50IikKIyBjaGFuZ2UgTkEgdG8gVW5pdGVkIEtpbmdkb20gCnRyYW5zaXRfY29zdCA9IHRyYW5zaXRfY29zdCAlPiUgbXV0YXRlKGNvdW50cnkyPWlmZWxzZShpcy5uYShjb3VudHJ5MiksIlVuaXRlZCBLaW5nZG9tIixjb3VudHJ5MikpICU+JSBtdXRhdGUoY29udGluZW50PWlmZWxzZShpcy5uYShjb250aW5lbnQpLCJFdXJvcGUiLGNvbnRpbmVudCkpCmBgYAoKYGBge3J9CiMgc3VtbWFyeSBvZiBwcm9qZWN0IGNvdW50IGJ5IGNvbnRpbmVudCAKdHJhbnNpdF9jb3N0ICU+JSBncm91cF9ieShjb250aW5lbnQpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcG9ydGlvbj1yb3VuZChuL3N1bShuKSwzKSkKYGBgCgpgYGB7cn0KIyBjb3N0L3N0YXRpb24KdHJhbnNpdF9jb3N0JGNvc3Rfc3RhdGlvbiA9IHRyYW5zaXRfY29zdCRyZWFsX2Nvc3QvdHJhbnNpdF9jb3N0JHN0YXRpb25zCiMgZHJvcCBjb3VudHJ5IHdpdGggTmFuIGluIGF2Z19jb3N0X3N0YXRpb24KdHJhbnNpdF9jb3N0ID0gdHJhbnNpdF9jb3N0ICU+JSBmaWx0ZXIoY291bnRyeTIhPSJRYXRhciIpCgojIHJhaWxyb2FkIGFuZCBub24tcmFpbHJvYWQgCkhtaXNjOjpkZXNjcmliZShhcy5mYWN0b3IodHJhbnNpdF9jb3N0JHJyKSkgCiMgcmFpbHJvYWQgZGYgCnJhaWxyb2FkID0gdHJhbnNpdF9jb3N0ICU+JSBmaWx0ZXIocnI9PSIxIikKIyBub24tcmFpbHJvYWQgZGYgCm5vdF9yYWlscm9hZCA9IHRyYW5zaXRfY29zdCAlPiUgZmlsdGVyKHJyPT0iMCIpCmBgYAoKCiMjIyBDb3N0IG9mIG5vbi1yYWlscm9hZCB0cmFuc2l0IHByb2plY3RzIGJ5IGNvbnRpbmVudApgYGB7cn0KcDEgPSBub3RfcmFpbHJvYWQgJT4lIGdncGxvdChhZXMoeD1jb250aW5lbnQsIHk9cmVhbF9jb3N0LCBjb2xvcj1jb250aW5lbnQpKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yPSJ3aGl0ZSIsYWxwaGE9MCkgKyBzY2FsZV9jb2xvcl91Y2hpY2FnbygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBnZW9tX2ppdHRlcihzaGFwZT0xNiwgcG9zaXRpb249cG9zaXRpb25faml0dGVyKDAuMiksIGFscGhhPTAuNykgKyBsYWJzKHN1YnRpdGxlPSJSZWFsIENvc3QiLCB5PSJSZWFsIGNvc3QgaW4gVVNEIE1pbGxpb25zIiwgeD0iIikKcDIgPSBub3RfcmFpbHJvYWQgJT4lIGdncGxvdChhZXMoeD1jb250aW5lbnQsIHk9Y29zdF9rbV9taWxsaW9ucywgY29sb3I9Y29udGluZW50KSkgKyBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvcj0id2hpdGUiLGFscGhhPTApICsgc2NhbGVfY29sb3JfdWNoaWNhZ28oKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgZ2VvbV9qaXR0ZXIoc2hhcGU9MTYsIHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcigwLjIpLCBhbHBoYT0wLjcpICsgbGFicyhzdWJ0aXRsZT0iQ29zdCBwZXIgS2lsb21ldGVyIiwgeT0iQ29zdC9rbSBpbiBVU0QgbWlsbGlvbnMiLCB4PSIiKQpwMyA9IG5vdF9yYWlscm9hZCAlPiUgZ2dwbG90KGFlcyh4PWNvbnRpbmVudCwgeT1jb3N0X3N0YXRpb24sIGNvbG9yPWNvbnRpbmVudCkpICsgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3I9IndoaXRlIixhbHBoYT0wKSArIHNjYWxlX2NvbG9yX3VjaGljYWdvKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIGdlb21faml0dGVyKHNoYXBlPTE2LCBwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIoMC4yKSwgYWxwaGE9MC43KSArIGxhYnMoc3VidGl0bGU9IkNvc3QgcGVyIFN0YXRpb24iLHk9IkNvc3Qvc3RhdGlvbiBpbiBVU0QgbWlsbGlvbnMiLCB4PSIiKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD01fQpsaWJyYXJ5KGdncHVicikKZmlndXJlMSA9IGdnYXJyYW5nZShwMSwgcDIsIHAzLCBuY29sPTIsIG5yb3c9MikKYW5ub3RhdGVfZmlndXJlKGZpZ3VyZTEsIHRvcD0gdGV4dF9ncm9iKCJDb3N0cyBvZiBUcmFuc2l0IFByb2plY3RzIGJ5IENvbnRpbmVudCIsIGNvbG9yPSJibGFjayIsZmFjZT0iYm9sZCIsc2l6ZT0xNiksIAogIGJvdHRvbT10ZXh0X2dyb2IoIkRhdGEgc291cmNlOiBUcmFuc2l0IENvc3RzIFByb2plY3QiLCBzaXplPTEwKSkKYGBgCgojIyMgQ291bnRyaWVzIHdpdGggaGlnaGVzdCBub24tcmFpbHJvYWQgcHJvamVjdCBjb3N0cwoKYGBge3J9CiMgbm9uX3JhaWxyb2FkIHByb2plY3QgY29zdHMKbnJfcGMgPSBub3RfcmFpbHJvYWQgJT4lIGdyb3VwX2J5KGNvdW50cnkyKSAlPiUgc3VtbWFyaXNlKGNvdW50X3Byb2plY3QgPSBuKCksIGF2Z19yZWFsX2Nvc3QgPSBtZWFuKHJlYWxfY29zdCxuYS5ybT1UUlVFKSwgYXZnX2Nvc3Rfa20gPSBtZWFuKGNvc3Rfa21fbWlsbGlvbnMsbmEucm09VFJVRSksIGF2Z19jb3N0X3N0YXRpb249bWVhbihjb3N0X3N0YXRpb24sIG5hLnJtPVRSVUUpKSAlPiUgYXMuZGF0YS5mcmFtZSgpCiMgYXBwZW5kIGNvdW50IHRvIGNvdW50cnkgbmFtZQpucl9wYyRjb3VudHJ5Ml9wID0gcGFzdGUwKG5yX3BjJGNvdW50cnkyLCIoIixucl9wYyRjb3VudF9wcm9qZWN0LCIpIikKYGBgCgpgYGB7cn0KIyBpbmRpdmlkdWFsIHBsb3RzCncxID0gbnJfcGMgJT4lIGFycmFuZ2UoZGVzYyhjb3VudF9wcm9qZWN0KSkgJT4lIGhlYWQoMTApICU+JSBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihjb3VudHJ5Ml9wLGNvdW50X3Byb2plY3QpLCB5PWNvdW50X3Byb2plY3QpKSArIGdlb21fY29sKGZpbGw9IiM2MDZjMzgiLCB3aWR0aD0wLjcpICsgY29vcmRfZmxpcCgpICsgbGFicyh5PSJQcm9qZWN0IGNvdW50IiwgeD0iQ291bnRyeShwcm9qZWN0IGNvdW50KSIsIHRpdGxlPSAiVG9wIDEwIGJ5IHByb2plY3QgY291bnQiKSArIHRoZW1lX21pbmltYWwoKSAKCncyID0gbnJfcGMgJT4lIGFycmFuZ2UoZGVzYyhhdmdfcmVhbF9jb3N0KSkgJT4lIGhlYWQoMTApICU+JSBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihjb3VudHJ5Ml9wLGF2Z19yZWFsX2Nvc3QpLCB5PWF2Z19yZWFsX2Nvc3QpKSArIGdlb21fY29sKGZpbGw9IiMyODM2MTgiLCB3aWR0aD0wLjcpICsgY29vcmRfZmxpcCgpICsgbGFicyh5PSJBdmcuIHJlYWwgY29zdCAoaW4gVVNEIG1pbGxpb25zKSIsIHg9IiIsIHRpdGxlPSAiVG9wIDEwIGJ5IGF2ZXJhZ2UgcmVhbCBjb3N0IikgKyB0aGVtZV9taW5pbWFsKCkgCgp3MyA9IG5yX3BjICU+JSBhcnJhbmdlKGRlc2MoYXZnX2Nvc3Rfa20pKSAlPiUgaGVhZCgxMCkgJT4lIGdncGxvdChhZXMoeD1yZW9yZGVyKGNvdW50cnkyX3AsYXZnX2Nvc3Rfa20pLCB5PWF2Z19jb3N0X2ttKSkgKyBnZW9tX2NvbChmaWxsPSIjZGRhMTVlIiwgd2lkdGg9MC43KSArIGNvb3JkX2ZsaXAoKSArIGxhYnMoeT0iQXZnLiBjb3N0L2ttIChpbiBVU0QgbWlsbGlvbnMpIiwgeD0iQ291bnRyeShwcm9qZWN0IGNvdW50KSIsIHRpdGxlPSAiVG9wIDEwIGJ5IGF2ZXJhZ2UgY29zdC9rbSIpICsgdGhlbWVfbWluaW1hbCgpIAoKdzQgPSBucl9wYyAlPiUgYXJyYW5nZShkZXNjKGF2Z19jb3N0X3N0YXRpb24pKSAlPiUgaGVhZCgxMCkgJT4lIGdncGxvdChhZXMoeD1yZW9yZGVyKGNvdW50cnkyX3AsYXZnX2Nvc3Rfc3RhdGlvbiksIHk9YXZnX2Nvc3Rfc3RhdGlvbikpICsgZ2VvbV9jb2woZmlsbD0iI2JjNmMyNSIsIHdpZHRoPTAuNykgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHk9IkF2Zy4gY29zdC9zdGF0aW9uIChpbiBVU0QgbWlsbGlvbnMpIiwgeD0iIiwgdGl0bGU9ICJUb3AgMTAgYnkgYXZlcmFnZSBjb3N0L3N0YXRpb24iKSArIHRoZW1lX21pbmltYWwoKSAKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9Nn0KIyBjb21iaW5lZCBwbG90CmZpZ3VyZTM9IGdnYXJyYW5nZSh3MSx3Mix3Myx3NCxuY29sPTIsIG5yb3c9MikKYW5ub3RhdGVfZmlndXJlKGZpZ3VyZTMsIHRvcD0gdGV4dF9ncm9iKCJOb24tcmFpbHJvYWQgVHJhbnNpdCBQcm9qZWN0cyBieSBDb3VudHJpZXMiLCBjb2xvcj0iYmxhY2siLGZhY2U9ImJvbGQiLHNpemU9MTYpLCAKICBib3R0b209dGV4dF9ncm9iKCJEYXRhIHNvdXJjZTogVHJhbnNpdCBDb3N0cyBQcm9qZWN0Iiwgc2l6ZT0xMCkpCmBgYAoKCiMjIyBFdXJvcGVhbiBjaXRpZXMgd2l0aCBoaWdoZXN0IG5vbi1yYWlscm9hZCBwcm9qZWN0IGNvc3RzIChleGNsLiBSdXNzaWEpCmBgYHtyfQojIGdldCBhdmVyYWdlcwpldTE9IG5vdF9yYWlscm9hZCAlPiUgZmlsdGVyKGNvbnRpbmVudD09IkV1cm9wZSIpICU+JSBmaWx0ZXIoY291bnRyeTIhPSJSdXNzaWEiKSAlPiUgZ3JvdXBfYnkoY291bnRyeSwgY2l0eSkgJT4lIHN1bW1hcmlzZShwcm9qZWN0c19uPW4oKSwgYXZnX3JlYWxfY29zdCA9IG1lYW4ocmVhbF9jb3N0LG5hLnJtPVRSVUUpLCBhdmdfY29zdF9rbSA9IG1lYW4oY29zdF9rbV9taWxsaW9ucyxuYS5ybT1UUlVFKSwgYXZnX2Nvc3Rfc3RhdGlvbj1tZWFuKGNvc3Rfc3RhdGlvbiwgbmEucm09VFJVRSkpCiMgYXBwZW5kIGNvdW50cnkgdG8gY2l0eSAKZXUxJGNpdHlfY291bnRyeSA9IHBhc3RlMChldTEkY2l0eSwiLCIsZXUxJGNvdW50cnkpCgojIGluZGl2aWR1YWwgcGxvdHMKZTEgPSBldTEgJT4lIGFycmFuZ2UoZGVzYyhwcm9qZWN0c19uKSkgJT4lIGhlYWQoMTApICU+JSBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihjaXR5X2NvdW50cnkscHJvamVjdHNfbiksIHk9cHJvamVjdHNfbikpICsgZ2VvbV9jb2woZmlsbD0iIzMzNWM2NyIsIHdpZHRoPTAuNykgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHk9IlByb2plY3QgY291bnQiLCB4PSJDaXR5LENvdW50cnkiLCB0aXRsZT0gIlRvcCAxMCBieSBwcm9qZWN0IGNvdW50IikgKyB0aGVtZV9taW5pbWFsKCkgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMCwxMSkpCgplMiA9IGV1MSAlPiUgYXJyYW5nZShkZXNjKGF2Z19yZWFsX2Nvc3QpKSAlPiUgaGVhZCgxMCkgJT4lIGdncGxvdChhZXMoeD1yZW9yZGVyKGNpdHlfY291bnRyeSxhdmdfcmVhbF9jb3N0KSwgeT1hdmdfcmVhbF9jb3N0KSkgKyBnZW9tX2NvbChmaWxsPSIjZTA5ZjNlIiwgd2lkdGg9MC43KSArIGNvb3JkX2ZsaXAoKSArIGxhYnMoeT0iQXZnLiByZWFsIGNvc3QgKGluIFVTRCBtaWxsaW9ucykiLCB4PSIiLCB0aXRsZT0gIlRvcCAxMCBieSBhdmVyYWdlIHJlYWwgY29zdCIpICsgdGhlbWVfbWluaW1hbCgpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsNTUwMCkpCgplMyA9IGV1MSAlPiUgYXJyYW5nZShkZXNjKGF2Z19jb3N0X2ttKSkgJT4lIGhlYWQoMTApICU+JSBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihjaXR5X2NvdW50cnksYXZnX2Nvc3Rfa20pLCB5PWF2Z19jb3N0X2ttKSkgKyBnZW9tX2NvbChmaWxsPSIjOWUyYTJiIiwgd2lkdGg9MC43KSArIGNvb3JkX2ZsaXAoKSArIGxhYnMoeT0iQXZnLiBjb3N0L2ttIChpbiBVU0QgbWlsbGlvbnMpIiwgeD0iQ2l0eSxDb3VudHJ5IiwgdGl0bGU9ICJUb3AgMTAgYnkgYXZlcmFnZSBjb3N0L2ttIikgKyB0aGVtZV9taW5pbWFsKCkgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMCw1NTApKQoKZTQgPWV1MSAlPiUgYXJyYW5nZShkZXNjKGF2Z19jb3N0X3N0YXRpb24pKSAlPiUgaGVhZCgxMCkgJT4lIGdncGxvdChhZXMoeD1yZW9yZGVyKGNpdHlfY291bnRyeSxhdmdfY29zdF9zdGF0aW9uKSwgeT1hdmdfY29zdF9zdGF0aW9uKSkgKyBnZW9tX2NvbChmaWxsPSIjNTQwYjBlIiwgd2lkdGg9MC43KSArIGNvb3JkX2ZsaXAoKSArIGxhYnMoeT0iQXZnLiBjb3N0L3N0YXRpb24gKGluIFVTRCBtaWxsaW9ucykiLCB4PSIiLCB0aXRsZT0gIlRvcCAxMCBieSBhdmVyYWdlIGNvc3Qvc3RhdGlvbiIpICsgdGhlbWVfbWluaW1hbCgpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsODUwKSkKCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTZ9CiMgY29tYmluZWQgcGxvdApmaWd1cmUyPSBnZ2FycmFuZ2UoZTEsZTIsZTMsZTQsbnJvdz0yLG5jb2w9MikKYW5ub3RhdGVfZmlndXJlKGZpZ3VyZTIsIHRvcD0gdGV4dF9ncm9iKCJFdXJvcGVhbiBDaXRpZXMgTm9uLXJhaWxyb2FkIFRyYW5zaXQgUHJvamVjdHMiLCBjb2xvcj0iYmxhY2siLGZhY2U9ImJvbGQiLHNpemU9MTYpLCAKICBib3R0b209dGV4dF9ncm9iKCJEYXRhIHNvdXJjZTogVHJhbnNpdCBDb3N0cyBQcm9qZWN0Iiwgc2l6ZT0xMCkpCmBgYAoKIyMjIExlbmd0aC9zdGF0aW9ucwoqIGdldCBhcHByb3hpbWF0ZSBkaXN0YW5jZSBiZXR3ZWVuIHN0YXRpb25zIHVzaW5nIGxlbmd0aChrbSkgZGl2aWRlZCBieSBzdGF0aW9ucwoKYGBge3J9CiMgbGVuZ3RoX3N0YXRpb25zCnRyYW5zaXRfY29zdCA9IHRyYW5zaXRfY29zdCAlPiUgbXV0YXRlKGxlbmd0aF9zdGF0aW9ucyA9IGxlbmd0aC9zdGF0aW9ucykKdHJhbnNpdF9jb3N0JGxlbmd0aF9zdGF0aW9uc1tzYXBwbHkodHJhbnNpdF9jb3N0JGxlbmd0aF9zdGF0aW9ucywgaXMuaW5maW5pdGUpXSA8LSBOQQpzdW1tYXJ5KHRyYW5zaXRfY29zdCRsZW5ndGhfc3RhdGlvbnMpCmBgYAoKCiMjIyMgbGVuZ3RoL3N0YXRpb25zIG9mIHJhaWxyb2FkIGFuZCBub24tcmFpbHJvYWQgcHJvamVjdHMgCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojcGxvdAp0cmFuc2l0X2Nvc3QgJT4lIGZpbHRlcighaXMubmEocnIpKSAlPiUgZ2dwbG90KGFlcyh4PWZhY3RvcihyciksIHk9bGVuZ3RoX3N0YXRpb25zLCBjb2xvcj1mYWN0b3IocnIpKSkgKyBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvcj0id2hpdGUiLGFscGhhPTAuMSkgKyBnZW9tX2ppdHRlcihzaGFwZT0xNiwgcG9zaXRpb249cG9zaXRpb25faml0dGVyKDAuMiksIGFscGhhPTAuNykgKyBzY2FsZV9jb2xvcl9qYW1hKCkgKyBjb29yZF9mbGlwKCkgKyB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBwYW5lbC5ncmlkLm1pbm9yLnk9ZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWFqb3IueT1lbGVtZW50X2JsYW5rKCkpICsgbGFicyh5PSJsZW5ndGgoa20pIC8gc3RhdGlvbnMiLCB4PSJJcyBSYWlscmFvZCIpCmBgYAoKIyMjIyBsZW5ndGgvc3RhdGlvbnMgYW5kIGNpdHkgKG5vbi1yYWlscm9hZCBwcm9qZWN0cykKYGBge3J9CmxzYyA9IHRyYW5zaXRfY29zdCAlPiUgZmlsdGVyKHJyPT0wKSAlPiUgZ3JvdXBfYnkoY2l0eSkgJT4lIHN1bW1hcmlzZShhdmdfbGVuZ3RoX3N0YXRpb25zID0gbWVhbihsZW5ndGhfc3RhdGlvbnMpKSAlPiUgZmlsdGVyKCFpcy5uYShhdmdfbGVuZ3RoX3N0YXRpb25zKSkKc3VtbWFyeShsc2MkYXZnX2xlbmd0aF9zdGF0aW9ucykKYGBgCgpgYGB7cn0KIyBjaXRpZXMgd2l0aCBsb3dlc3QgYXZlcmFnZSBsZW5ndGgvc3RhdGlvbnMKbHNjICU+JSBhcnJhbmdlKGF2Z19sZW5ndGhfc3RhdGlvbnMpICU+JSBoZWFkKDUpCiMgY2l0aWVzIHdpdGggaGlnaGVzdCBhdmVyYWdlIGxlbmd0aC9zdGF0aW9ucwpsc2MgJT4lIGFycmFuZ2UoZGVzYyhhdmdfbGVuZ3RoX3N0YXRpb25zKSkgJT4lIGhlYWQoNSkKYGBgCgpgYGB7cn0KIyBkZW5zaXR5IHBsb3QKZ2dwbG90KGxzYywgYWVzKHggPSBhdmdfbGVuZ3RoX3N0YXRpb25zKSkgKyBnZW9tX2RlbnNpdHkoYWVzKHkgPSAuLmNvdW50Li4pLCBmaWxsID0gIiM1YzZkNzAiLGFscGhhPTAuNykgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKGF2Z19sZW5ndGhfc3RhdGlvbnMpKSwgCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMC42LAogICAgICAgICAgICAgY29sb3IgPSAiI0ZDNEUwNyIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxLjgsIHkgPSAxMDAsIGxhYmVsID0gIk1lYW4gPSAxLjQga20iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHg9IkF2Zy4gbGVuZ3RoL3N0YXRpb25zIChrbSkiLHk9IkNpdHkgY291bnQiKQpgYGAKCiMjIyBMZW5ndGggYW5kIHJlYWwgY29zdCAobm9uLXJhaWxyb2FkIHByb2plY3RzKQoqIGluc3BpcmVkIGJ5IFtATWFydGluUG9uc10oaHR0cHM6Ly90d2l0dGVyLmNvbS9NYXJ0aW5Qb25zTS9zdGF0dXMvMTM0NzA3OTE5Mzk5ODMyNzgxMCkKCmBgYHtyfQpvdXRsaWVyc19sZW5ndGggPSBib3hwbG90KG5vdF9yYWlscm9hZCRsZW5ndGgsIHBsb3Q9RkFMU0UpJG91dCAjIGdldCBvdXRsaWVycyBpbiBsZW5ndGgKbWluKG91dGxpZXJzX2xlbmd0aCkgIyBnZXQgbWluaW11bQpgYGAKCmBgYHtyfQojIHNjYXR0ZXIgcGxvdCB3aXRoIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGFuZCByZWdyZXNzaW9uIGxpbmUKbnJyMT0gbm90X3JhaWxyb2FkICU+JSBmaWx0ZXIobGVuZ3RoIDw2Ni43KSAjIGRyb3Agb3V0bGllcnMKZ2dzY2F0dGVyKG5ycjEsIHggPSAibGVuZ3RoIiwgeSA9ICJyZWFsX2Nvc3QiLCBhZGQgPSAicmVnLmxpbmUiLCBjb2xvcj0iIzQ1N2I5ZCIpICsKICBzdGF0X2NvcihsYWJlbC54ID0gMywgbGFiZWwueSA9IDMyMDAwKSArCiAgc3RhdF9yZWdsaW5lX2VxdWF0aW9uKGxhYmVsLnggPSAzLCBsYWJlbC55ID0gMzAwMDApICsgbGFicyh5PSJyZWFsX2Nvc3QgKGluIFVTRCBtaWxsaW9ucykiLCB0aXRsZT0iUmVhbCBDb3N0IH4gTGVuZ3RoIikKYGBgCgojIyMgU3RhdGlvbnMgYW5kIHJlYWwgY29zdCAobm9uLXJhaWxyb2FkIHByb2plY3RzKQoKYGBge3J9Cm91dGxpZXJzX3N0YXRpb25zID0gYm94cGxvdChub3RfcmFpbHJvYWQkc3RhdGlvbnMsIHBsb3Q9RkFMU0UpJG91dCAjIG91dGxpZXJzIGluIHN0YXRpb25zCm1pbihvdXRsaWVyc19zdGF0aW9ucykgIyBnZXQgbWluaW11bQpgYGAKCmBgYHtyfQpucnIyPSBub3RfcmFpbHJvYWQgJT4lIGZpbHRlcihzdGF0aW9ucyA8IDQzKSAjIGRyb3Agb3V0bGllcnMKZ2dzY2F0dGVyKG5ycjIsIHggPSAic3RhdGlvbnMiLCB5ID0gInJlYWxfY29zdCIsIGFkZCA9ICJyZWcubGluZSIsIGNvbG9yPSIjZGRhMTVlIikgKwogIHN0YXRfY29yKGxhYmVsLnggPSAzLCBsYWJlbC55ID0gMzIwMDApICsKICBzdGF0X3JlZ2xpbmVfZXF1YXRpb24obGFiZWwueCA9IDMsIGxhYmVsLnkgPSAzMDAwMCkgKyBsYWJzKHk9InJlYWxfY29zdCAoaW4gVVNEIG1pbGxpb25zKSIsIHRpdGxlPSJSZWFsIENvc3QgfiBTdGF0aW9ucyIpCmBgYAoKIyMjIFllYXJzIHRvIGNvbXBsZXRlIGFuZCByZWFsIGNvc3QgKG5vbi1yYWlscm9hZCBwcm9qZWN0cykKYGBge3J9Cm5vdF9yYWlscm9hZCA9IG5vdF9yYWlscm9hZCAlPiUgbXV0YXRlKHllYXJzX3RvX2NvbXBsZXRlID0gZW5kX3llYXIgLSBzdGFydF95ZWFyKQpzdW1tYXJ5KG5vdF9yYWlscm9hZCR5ZWFyc190b19jb21wbGV0ZSkKYGBgCgpgYGB7cn0Kb3V0bGllcnNfeWVhcnMgPSBib3hwbG90KG5vdF9yYWlscm9hZCR5ZWFyc190b19jb21wbGV0ZSwgcGxvdD1GQUxTRSkkb3V0ICNnZXQgb3V0bGllcnMKbWluKG91dGxpZXJzX3llYXJzKSAjZ2V0IG1pbmltdW0KYGBgCgoKYGBge3Isd2FybmluZz1GQUxTRX0KbnJyMz0gbm90X3JhaWxyb2FkICU+JSBmaWx0ZXIoc3RhdGlvbnMgPCAxMikgIyBkcm9wIG91dGxpZXJzCmdnc2NhdHRlcihucnIzLCB4ID0gInllYXJzX3RvX2NvbXBsZXRlIiwgeSA9ICJyZWFsX2Nvc3QiLCBhZGQgPSAicmVnLmxpbmUiLCBjb2xvcj0iIzNjNmU3MSIpICsKICBzdGF0X2NvcihsYWJlbC54ID0gMiwgbGFiZWwueSA9IDMyMDAwKSArCiAgc3RhdF9yZWdsaW5lX2VxdWF0aW9uKGxhYmVsLnggPSAyLCBsYWJlbC55ID0gMzAwMDApICsgbGFicyh5PSJyZWFsX2Nvc3QgKGluIFVTRCBtaWxsaW9ucykiLCB0aXRsZT0iUmVhbCBDb3N0IH4gWWVhcnMgdG8gQ29tcGxldGUiKQpgYGAKCgo=