Get to know the data through visualization
Each of the following questions are targeted at making sure we understand our data better. A great way to get a “feel” for a dataset is to visualize it. Answer each of the below questions with a (publishable-quality) picture.
1. How is lifeExp
, pop
and gdpPercap
variables distributed?
Two ways to think about this: one is to plot each figure individually.
# Life Expectancy
gapminder %>%
ggplot(aes(lifeExp)) +
geom_histogram(bins=30) +
theme_bw()
# Population
gapminder %>%
ggplot(aes(pop)) +
geom_histogram(bins=30) +
theme_bw()
# GDP
gapminder %>%
ggplot(aes(gdpPercap)) +
geom_histogram(bins=30) +
theme_bw()
The better way is to plot all the functions at once using the pivot_longer()
function from last time with facet_wrap()
gapminder %>%
pivot_longer(cols=c(lifeExp,pop,gdpPercap)) %>%
ggplot(aes(value)) +
geom_histogram(bins=30) +
facet_wrap(~name,scales="free") +
theme_bw()
We can quickly see that there is large right skews in both gdpPercap
and pop
. Let’s transform these variables and re-plot.
gapminder %>%
mutate(ln_pop = log(pop),
ln_gdppc = log(gdpPercap)) %>%
pivot_longer(cols=c(lifeExp,ln_pop,ln_gdppc)) %>%
ggplot(aes(value)) +
geom_histogram(bins=30) +
facet_wrap(~name,scales="free") +
theme_bw()
Now let’s make things look professional!
gapminder %>%
mutate(ln_pop = log(pop),
ln_gdppc = log(gdpPercap)) %>%
pivot_longer(cols=c(lifeExp,ln_pop,ln_gdppc)) %>%
mutate(name = case_when(
name == "lifeExp" ~ "Life Expectancy",
name == "ln_gdppc" ~ "Log GDP Per Capita",
name == "ln_pop" ~ "Log Population"
)) %>%
ggplot(aes(value,fill=name)) +
geom_histogram(bins=30,color="white",alpha=.5,show.legend = F) +
facet_wrap(~name,scales="free_x") +
labs(caption="Source: gapminder.org") +
scale_fill_economist() +
theme_fivethirtyeight() +
theme(text=element_text(family="serif",face="bold",size=16))
2. What’s the relationship between economic development and life expectancy? Is the relationship the same for all continents?
gapminder %>%
mutate(ln_pop = log(pop),
ln_gdppc = log(gdpPercap)) %>%
ggplot(aes(ln_gdppc,lifeExp)) +
geom_point() +
geom_smooth(method = "lm",se=F) # Let's fit a line to the data.
Useful but there are a number of small aesthetic adjustments we could make to really help use distinguish between what is going on.
gapminder %>%
mutate(ln_pop = log(pop),
ln_gdppc = log(gdpPercap)) %>%
ggplot(aes(ln_gdppc,lifeExp)) +
geom_point(alpha=.4,color="grey30") +
geom_smooth(method = "loess",se=F,color="darkred",size=1.5) +
theme_minimal()
Is the trend the same by continent?
gapminder %>%
mutate(ln_pop = log(pop),
ln_gdppc = log(gdpPercap)) %>%
ggplot(aes(ln_gdppc,lifeExp,color=continent)) +
geom_point(alpha=.4,) +
geom_smooth(method = "loess",se=F,size=1.5) +
theme_minimal()
Generally speaking, it appears so, but it’s difficult to hone in on any one continent. There is just a lot going on. Let’s consider two alternative ways of presenting this same data.
Way 1: separate plots using facet_
gapminder %>%
mutate(ln_pop = log(pop),
ln_gdppc = log(gdpPercap)) %>%
ggplot(aes(ln_gdppc,lifeExp,color=continent)) +
geom_point(alpha=.3,) +
geom_smooth(method = "loess",se=F,size=1.5) +
facet_wrap(~continent,nrow=1) +
labs(x="Log GDP Per Capita",y = "Life Expectancy",color="") +
scale_color_gdocs() +
theme_minimal() +
theme(legend.position = "bottom",
text = element_text(size=14,family="serif",face="bold"))
Way 2: Plot the trends but not the individual data points
gapminder %>%
mutate(ln_pop = log(pop),
ln_gdppc = log(gdpPercap)) %>%
ggplot(aes(ln_gdppc,lifeExp,color=continent)) +
geom_smooth(method = "loess",se=F,size=1.5) +
labs(x="Log GDP Per Capita",y = "Life Expectancy",color="") +
scale_color_gdocs() +
theme_minimal() +
theme(legend.position = "bottom",
text = element_text(size=14,family="serif",face="bold"))
3. Which countries in Africa have the lowest levels of life expectancy?
Two ways we could go about answering a question like this. The first might just be an ordered barplot. Here we might rephrase the question as: “Which countries in Africa have the lowest levels of life expectancy on average?”
gapminder %>%
filter(continent == "Africa") %>%
group_by(country) %>%
summarize(lifeExp = mean(lifeExp),.groups="drop") %>%
ggplot(aes(lifeExp,country)) +
geom_col()
Nice, but ordering the factor fields would go a long way. Doing so is easy using the tidy forcats
package (which is part of the tidyverse), and why we’re at it, lets’ add a little polish.
gapminder %>%
filter(continent == "Africa") %>%
group_by(country) %>%
summarize(lifeExp = mean(lifeExp),.groups="drop") %>%
ggplot(aes(lifeExp,fct_reorder(country,desc(lifeExp)),fill=lifeExp)) +
geom_col(show.legend = F) +
scale_fill_gradient2_tableau() +
labs(x="Life Expectancy",y="",
title = "Average Life Expectancy in Africa",
subtitle = "1952 - 2007",
caption = "Source gapminder.org") +
theme_hc() +
theme(text=element_text(family = "serif",face="bold",size=14))
Another way we could approach this is to lay everything out spatially. ggplot with the maps
package provides a useful way to extract map data on the fly.
map_data("world") %>%
ggplot(aes(x=long,y=lat,group=group)) +
geom_polygon()
Our focus is the African continent, so we’ll just focus on that portion of the data using the data wrangling principals.
# Simplify the map data
world <-
map_data("world") %>%
select(long,lat,group,country=region) %>%
# Again standardize the country names
mutate(country = countrycode::countrycode(country,"country.name","country.name")) %>%
mutate(country = ifelse(country == "South Sudan","Sudan",country))
# subset the relevant African countries in the data.
africa <-
gapminder %>%
filter(continent == "Africa") %>%
group_by(country) %>%
summarize(lifeExp = mean(lifeExp),.groups="drop") %>%
mutate(country = countrycode::countrycode(country,"country.name","country.name")) %>%
inner_join(world,by="country")
Let’s plot the map.
africa %>%
ggplot(aes(x=long,y=lat,group=group)) +
geom_polygon()
Now let’s fill in the fields on the map using the average life expectancy values.
africa %>%
ggplot(aes(x=long,y=lat,group=group,fill=lifeExp)) +
geom_polygon(color="white",size=.25) +
scale_fill_gradient2_tableau() +
theme_map() +
labs(fill="Life Expectancy",
title = "Average Life Expectancy in Africa",
subtitle = "1952 - 2007",
caption = "Source gapminder.org") +
theme(text=element_text(family = "serif",face="bold",size=14))
What if we wanted to look at how these spatial patterns shifted over time? Not a problem, we just need to tweak the data and plot code a little bit.
# DON'T aggregate the lifeExp variable this time.
gapminder %>%
filter(continent == "Africa") %>%
mutate(country = countrycode::countrycode(country,"country.name","country.name")) %>%
inner_join(world,by="country") %>%
ggplot(aes(x=long,y=lat,group=group,fill=lifeExp)) +
geom_polygon(color="white",size=.25) +
scale_fill_gradient2_tableau() +
theme_map() +
labs(fill="Life Expectancy",
title = "Average Life Expectancy in Africa",
subtitle = "1952 - 2007",
caption = "Source gapminder.org") +
facet_wrap(~year) +
theme(text=element_text(family = "serif",face="bold",size=18),
legend.position = "bottom")
LS0tCnRpdGxlOiAiUFBPTCA2NzAgfCBXZWVrIDUgfCBXYWx0aHJvdWdoIChBbnN3ZXJzKSIKc3VidGl0bGU6IHwgCiAgfCBEYXRhIFZpc3VhbGl6YXRpb24gQXBwbGljYXRpb24gLSBHYXBtaW5kZXIgRGF0YSAKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogNQotLS0KCjxicj48YnI+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT0gRn0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yPUYsd2FybmluZyA9IEYsY29tbWVudD1GKQpgYGAKCgoKYGBge3IgZGVwZW5kZW5jaWVzfQojaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKcmVxdWlyZSh0aWR5dmVyc2UpICMgVGhlIHRpZHl2ZXJzZSBwYWNrYWdlIGNvdmVyZWQgbGFzdCB0aW1lIAoKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3RoZW1lcyIpCnJlcXVpcmUoZ2d0aGVtZXMpICMgZm9yIGdyZWF0IHZpc3VhbGl6YXRpb24gY29sb3JzIGFuZCB0aGVtZXMuIAoKIyBpbnN0YWxsLnBhY2thZ2VzKCJtYXBzIikKcmVxdWlyZShtYXBzKSAjIGZvciBzb21lIG1hcHMgZGF0YQoKIyBHYXBtaW5kZXIgZGF0YSAoZm9yIGV4YW1wbGUpCiNpbnN0YWxsLnBhY2thZ2VzKCJnYXBtaW5kZXIiKQpyZXF1aXJlKGdhcG1pbmRlcikKYGBgCgojIERhdGEgCgpUaGUgW2BHYXBtaW5kZXJgIGRhdGFzZXRdKGh0dHBzOi8vd3d3LmdhcG1pbmRlci5vcmcvKSBpcyBhIGZhbW91cyBkYXRhc2V0IHVzZWQgYnkgSGFucyBSb3NsaW5nIHRvIHZpc3VhbGl6ZSBkZXZlbG9wbWVudCBvdXRjb21lcy4gVGhlIGRhdGEgY292ZXJzIDE5NTIgdG8gMjAwNyBpbiBmaXZlIHllYXIgaW50ZXJ2YWxzIGFuZCBtZWFzdXJlcyBsaWZlIGV4cGVjdGFuY3ksIHBvcHVsYXRpb24sIGFuZCBHRFAgUGVyIENhcGl0YS4gCgpgYGB7cn0KZ2FwbWluZGVyICU+JSBoZWFkKCkKYGBgCgoKIyBHZXQgdG8ga25vdyB0aGUgZGF0YSB0aHJvdWdoIHZpc3VhbGl6YXRpb24KCkVhY2ggb2YgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMgYXJlIHRhcmdldGVkIGF0IG1ha2luZyBzdXJlIHdlIHVuZGVyc3RhbmQgb3VyIGRhdGEgYmV0dGVyLiBBIGdyZWF0IHdheSB0byBnZXQgYSAiZmVlbCIgZm9yIGEgZGF0YXNldCBpcyB0byB2aXN1YWxpemUgaXQuIEFuc3dlciBlYWNoIG9mIHRoZSBiZWxvdyBxdWVzdGlvbnMgd2l0aCBhIChwdWJsaXNoYWJsZS1xdWFsaXR5KSBwaWN0dXJlLiAKCiMjIyMgMS4gSG93IGlzIGBsaWZlRXhwYCwgYHBvcGAgYW5kIGBnZHBQZXJjYXBgIHZhcmlhYmxlcyBkaXN0cmlidXRlZD8KClR3byB3YXlzIHRvIHRoaW5rIGFib3V0IHRoaXM6IG9uZSBpcyB0byBwbG90IGVhY2ggZmlndXJlIGluZGl2aWR1YWxseS4gCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIixmaWcud2lkdGg9NyxmaWcuaGVpZ2h0PTR9CiMgTGlmZSBFeHBlY3RhbmN5CmdhcG1pbmRlciAlPiUgCiAgZ2dwbG90KGFlcyhsaWZlRXhwKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9MzApICsKICB0aGVtZV9idygpIAoKIyBQb3B1bGF0aW9uCmdhcG1pbmRlciAlPiUgCiAgZ2dwbG90KGFlcyhwb3ApKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz0zMCkgKwogIHRoZW1lX2J3KCkgCgojIEdEUApnYXBtaW5kZXIgJT4lIAogIGdncGxvdChhZXMoZ2RwUGVyY2FwKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9MzApICsKICB0aGVtZV9idygpIApgYGAKCgpUaGUgYmV0dGVyIHdheSBpcyB0byBwbG90IGFsbCB0aGUgZnVuY3Rpb25zIGF0IG9uY2UgdXNpbmcgdGhlIGBwaXZvdF9sb25nZXIoKWAgZnVuY3Rpb24gZnJvbSBsYXN0IHRpbWUgd2l0aCBgZmFjZXRfd3JhcCgpYAoKYGBge3IsZmlnLmFsaWduPSJjZW50ZXIiLGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTR9CmdhcG1pbmRlciAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHM9YyhsaWZlRXhwLHBvcCxnZHBQZXJjYXApKSAlPiUgCiAgZ2dwbG90KGFlcyh2YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwKSArCiAgZmFjZXRfd3JhcCh+bmFtZSxzY2FsZXM9ImZyZWUiKSArCiAgdGhlbWVfYncoKSAKYGBgCldlIGNhbiBxdWlja2x5IHNlZSB0aGF0IHRoZXJlIGlzIGxhcmdlIHJpZ2h0IHNrZXdzIGluIGJvdGggYGdkcFBlcmNhcGAgYW5kIGBwb3BgLiBMZXQncyB0cmFuc2Zvcm0gdGhlc2UgdmFyaWFibGVzIGFuZCByZS1wbG90LgoKYGBge3IsZmlnLmFsaWduPSJjZW50ZXIiLGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTR9CmdhcG1pbmRlciAlPiUgCiAgbXV0YXRlKGxuX3BvcCA9IGxvZyhwb3ApLAogICAgICAgICBsbl9nZHBwYyA9ICBsb2coZ2RwUGVyY2FwKSkgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzPWMobGlmZUV4cCxsbl9wb3AsbG5fZ2RwcGMpKSAlPiUgCiAgZ2dwbG90KGFlcyh2YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwKSArCiAgZmFjZXRfd3JhcCh+bmFtZSxzY2FsZXM9ImZyZWUiKSArCiAgdGhlbWVfYncoKSAKYGBgCgpOb3cgbGV0J3MgbWFrZSB0aGluZ3MgbG9vayBwcm9mZXNzaW9uYWwhCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciIsZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9NH0KZ2FwbWluZGVyICU+JSAKICBtdXRhdGUobG5fcG9wID0gbG9nKHBvcCksCiAgICAgICAgIGxuX2dkcHBjID0gIGxvZyhnZHBQZXJjYXApKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHM9YyhsaWZlRXhwLGxuX3BvcCxsbl9nZHBwYykpICU+JSAKICBtdXRhdGUobmFtZSA9IGNhc2Vfd2hlbigKICAgIG5hbWUgPT0gImxpZmVFeHAiIH4gIkxpZmUgRXhwZWN0YW5jeSIsCiAgICBuYW1lID09ICJsbl9nZHBwYyIgfiAiTG9nIEdEUCBQZXIgQ2FwaXRhIiwKICAgIG5hbWUgPT0gImxuX3BvcCIgfiAiTG9nIFBvcHVsYXRpb24iCiAgKSkgJT4lIAogIGdncGxvdChhZXModmFsdWUsZmlsbD1uYW1lKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9MzAsY29sb3I9IndoaXRlIixhbHBoYT0uNSxzaG93LmxlZ2VuZCA9IEYpICsKICBmYWNldF93cmFwKH5uYW1lLHNjYWxlcz0iZnJlZV94IikgKwogIGxhYnMoY2FwdGlvbj0iU291cmNlOiBnYXBtaW5kZXIub3JnIikgKwogIHNjYWxlX2ZpbGxfZWNvbm9taXN0KCkgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChmYW1pbHk9InNlcmlmIixmYWNlPSJib2xkIixzaXplPTE2KSkKYGBgCgoKCiMjIyMgMi4gV2hhdCdzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBlY29ub21pYyBkZXZlbG9wbWVudCBhbmQgbGlmZSBleHBlY3RhbmN5PyBJcyB0aGUgcmVsYXRpb25zaGlwIHRoZSBzYW1lIGZvciBhbGwgY29udGluZW50cz8KCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIixmaWcud2lkdGg9MTAsZmlnLmhlaWdodD00fQpnYXBtaW5kZXIgJT4lIAogIG11dGF0ZShsbl9wb3AgPSBsb2cocG9wKSwKICAgICAgICAgbG5fZ2RwcGMgPSAgbG9nKGdkcFBlcmNhcCkpICU+JSAKICBnZ3Bsb3QoYWVzKGxuX2dkcHBjLGxpZmVFeHApKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLHNlPUYpICMgTGV0J3MgZml0IGEgbGluZSB0byB0aGUgZGF0YS4KYGBgCgpVc2VmdWwgYnV0IHRoZXJlIGFyZSBhIG51bWJlciBvZiBzbWFsbCBhZXN0aGV0aWMgYWRqdXN0bWVudHMgd2UgY291bGQgbWFrZSB0byByZWFsbHkgaGVscCB1c2UgZGlzdGluZ3Vpc2ggYmV0d2VlbiB3aGF0IGlzIGdvaW5nIG9uLiAKCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIixmaWcud2lkdGg9MTAsZmlnLmhlaWdodD00fQpnYXBtaW5kZXIgJT4lIAogIG11dGF0ZShsbl9wb3AgPSBsb2cocG9wKSwKICAgICAgICAgbG5fZ2RwcGMgPSAgbG9nKGdkcFBlcmNhcCkpICU+JSAKICBnZ3Bsb3QoYWVzKGxuX2dkcHBjLGxpZmVFeHApKSArCiAgZ2VvbV9wb2ludChhbHBoYT0uNCxjb2xvcj0iZ3JleTMwIikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsc2U9Rixjb2xvcj0iZGFya3JlZCIsc2l6ZT0xLjUpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpJcyB0aGUgdHJlbmQgdGhlIHNhbWUgYnkgY29udGluZW50PwoKYGBge3IsZmlnLmFsaWduPSJjZW50ZXIiLGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTR9CmdhcG1pbmRlciAlPiUgCiAgbXV0YXRlKGxuX3BvcCA9IGxvZyhwb3ApLAogICAgICAgICBsbl9nZHBwYyA9ICBsb2coZ2RwUGVyY2FwKSkgJT4lIAogIGdncGxvdChhZXMobG5fZ2RwcGMsbGlmZUV4cCxjb2xvcj1jb250aW5lbnQpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0uNCwpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLHNlPUYsc2l6ZT0xLjUpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpHZW5lcmFsbHkgc3BlYWtpbmcsIGl0IGFwcGVhcnMgc28sIGJ1dCBpdCdzIGRpZmZpY3VsdCB0byBob25lIGluIG9uIGFueSBvbmUgY29udGluZW50LiBUaGVyZSBpcyBqdXN0IGEgbG90IGdvaW5nIG9uLiBMZXQncyBjb25zaWRlciB0d28gYWx0ZXJuYXRpdmUgd2F5cyBvZiBwcmVzZW50aW5nIHRoaXMgc2FtZSBkYXRhLiAKCldheSAxOiBzZXBhcmF0ZSBwbG90cyB1c2luZyBgZmFjZXRfYAoKYGBge3IsZmlnLmFsaWduPSJjZW50ZXIiLGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTR9CmdhcG1pbmRlciAlPiUgCiAgbXV0YXRlKGxuX3BvcCA9IGxvZyhwb3ApLAogICAgICAgICBsbl9nZHBwYyA9ICBsb2coZ2RwUGVyY2FwKSkgJT4lIAogIGdncGxvdChhZXMobG5fZ2RwcGMsbGlmZUV4cCxjb2xvcj1jb250aW5lbnQpKSArCiAgZ2VvbV9wb2ludChhbHBoYT0uMywpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLHNlPUYsc2l6ZT0xLjUpICsKICBmYWNldF93cmFwKH5jb250aW5lbnQsbnJvdz0xKSArCiAgbGFicyh4PSJMb2cgR0RQIFBlciBDYXBpdGEiLHkgPSAiTGlmZSBFeHBlY3RhbmN5Iixjb2xvcj0iIikgKwogIHNjYWxlX2NvbG9yX2dkb2NzKCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE0LGZhbWlseT0ic2VyaWYiLGZhY2U9ImJvbGQiKSkKYGBgCgpXYXkgMjogUGxvdCB0aGUgdHJlbmRzIGJ1dCBub3QgdGhlIGluZGl2aWR1YWwgZGF0YSBwb2ludHMKCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIixmaWcud2lkdGg9NyxmaWcuaGVpZ2h0PTV9CmdhcG1pbmRlciAlPiUgCiAgbXV0YXRlKGxuX3BvcCA9IGxvZyhwb3ApLAogICAgICAgICBsbl9nZHBwYyA9ICBsb2coZ2RwUGVyY2FwKSkgJT4lIAogIGdncGxvdChhZXMobG5fZ2RwcGMsbGlmZUV4cCxjb2xvcj1jb250aW5lbnQpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIixzZT1GLHNpemU9MS41KSArCiAgbGFicyh4PSJMb2cgR0RQIFBlciBDYXBpdGEiLHkgPSAiTGlmZSBFeHBlY3RhbmN5Iixjb2xvcj0iIikgKwogIHNjYWxlX2NvbG9yX2dkb2NzKCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTE0LGZhbWlseT0ic2VyaWYiLGZhY2U9ImJvbGQiKSkKYGBgCgojIyMjIDMuIFdoaWNoIGNvdW50cmllcyBpbiBBZnJpY2EgaGF2ZSB0aGUgbG93ZXN0IGxldmVscyBvZiBsaWZlIGV4cGVjdGFuY3k/CgpUd28gd2F5cyB3ZSBjb3VsZCBnbyBhYm91dCBhbnN3ZXJpbmcgYSBxdWVzdGlvbiBsaWtlIHRoaXMuIFRoZSBmaXJzdCBtaWdodCBqdXN0IGJlIGFuIG9yZGVyZWQgYmFycGxvdC4gSGVyZSB3ZSBtaWdodCByZXBocmFzZSB0aGUgcXVlc3Rpb24gYXM6ICJXaGljaCBjb3VudHJpZXMgaW4gQWZyaWNhIGhhdmUgdGhlIGxvd2VzdCBsZXZlbHMgb2YgbGlmZSBleHBlY3RhbmN5IF9vbiBhdmVyYWdlXz8iCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciIsZmlnLndpZHRoPTcsZmlnLmhlaWdodD03fQpnYXBtaW5kZXIgJT4lIAogIGZpbHRlcihjb250aW5lbnQgPT0gIkFmcmljYSIpICU+JSAKICBncm91cF9ieShjb3VudHJ5KSAlPiUgCiAgc3VtbWFyaXplKGxpZmVFeHAgPSBtZWFuKGxpZmVFeHApLC5ncm91cHM9ImRyb3AiKSAlPiUgCiAgZ2dwbG90KGFlcyhsaWZlRXhwLGNvdW50cnkpKSArCiAgZ2VvbV9jb2woKSAKYGBgCgpOaWNlLCBidXQgb3JkZXJpbmcgdGhlIGZhY3RvciBmaWVsZHMgd291bGQgZ28gYSBsb25nIHdheS4gRG9pbmcgc28gaXMgZWFzeSB1c2luZyB0aGUgdGlkeSBgZm9yY2F0c2AgcGFja2FnZSAod2hpY2ggaXMgcGFydCBvZiB0aGUgdGlkeXZlcnNlKSwgYW5kIHdoeSB3ZSdyZSBhdCBpdCwgbGV0cycgYWRkIGEgbGl0dGxlIHBvbGlzaC4gCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciIsZmlnLndpZHRoPTcsZmlnLmhlaWdodD03LjV9CmdhcG1pbmRlciAlPiUgCiAgZmlsdGVyKGNvbnRpbmVudCA9PSAiQWZyaWNhIikgJT4lIAogIGdyb3VwX2J5KGNvdW50cnkpICU+JSAKICBzdW1tYXJpemUobGlmZUV4cCA9IG1lYW4obGlmZUV4cCksLmdyb3Vwcz0iZHJvcCIpICU+JSAKICBnZ3Bsb3QoYWVzKGxpZmVFeHAsZmN0X3Jlb3JkZXIoY291bnRyeSxkZXNjKGxpZmVFeHApKSxmaWxsPWxpZmVFeHApKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudDJfdGFibGVhdSgpICsKICBsYWJzKHg9IkxpZmUgRXhwZWN0YW5jeSIseT0iIiwKICAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgTGlmZSBFeHBlY3RhbmN5IGluIEFmcmljYSIsCiAgICAgICBzdWJ0aXRsZSA9ICIxOTUyIC0gMjAwNyIsCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZSBnYXBtaW5kZXIub3JnIikgKwogIHRoZW1lX2hjKCkgKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsZmFjZT0iYm9sZCIsc2l6ZT0xNCkpCmBgYAoKQW5vdGhlciB3YXkgd2UgY291bGQgYXBwcm9hY2ggdGhpcyBpcyB0byBsYXkgZXZlcnl0aGluZyBvdXQgc3BhdGlhbGx5LiBnZ3Bsb3Qgd2l0aCB0aGUgYG1hcHNgIHBhY2thZ2UgcHJvdmlkZXMgYSB1c2VmdWwgd2F5IHRvIGV4dHJhY3QgbWFwIGRhdGEgb24gdGhlIGZseS4gCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciIsZmlnLndpZHRoPTExLGZpZy5oZWlnaHQ9N30KbWFwX2RhdGEoIndvcmxkIikgJT4lCiAgZ2dwbG90KGFlcyh4PWxvbmcseT1sYXQsZ3JvdXA9Z3JvdXApKSArCiAgZ2VvbV9wb2x5Z29uKCkKYGBgCgpPdXIgZm9jdXMgaXMgdGhlIEFmcmljYW4gY29udGluZW50LCBzbyB3ZSdsbCBqdXN0IGZvY3VzIG9uIHRoYXQgcG9ydGlvbiBvZiB0aGUgZGF0YSB1c2luZyB0aGUgZGF0YSB3cmFuZ2xpbmcgcHJpbmNpcGFscy4gCgpgYGB7cn0KCiMgU2ltcGxpZnkgdGhlIG1hcCBkYXRhIAp3b3JsZCA8LSAKICBtYXBfZGF0YSgid29ybGQiKSAlPiUgCiAgc2VsZWN0KGxvbmcsbGF0LGdyb3VwLGNvdW50cnk9cmVnaW9uKSAlPiUgCiAgCiAgIyBBZ2FpbiBzdGFuZGFyZGl6ZSB0aGUgY291bnRyeSBuYW1lcwogIG11dGF0ZShjb3VudHJ5ID0gY291bnRyeWNvZGU6OmNvdW50cnljb2RlKGNvdW50cnksImNvdW50cnkubmFtZSIsImNvdW50cnkubmFtZSIpKSAlPiUgCiAgbXV0YXRlKGNvdW50cnkgPSBpZmVsc2UoY291bnRyeSA9PSAiU291dGggU3VkYW4iLCJTdWRhbiIsY291bnRyeSkpCgojIHN1YnNldCB0aGUgcmVsZXZhbnQgQWZyaWNhbiBjb3VudHJpZXMgaW4gdGhlIGRhdGEuCmFmcmljYSA8LSAKICBnYXBtaW5kZXIgJT4lIAogIGZpbHRlcihjb250aW5lbnQgPT0gIkFmcmljYSIpICU+JSAKICBncm91cF9ieShjb3VudHJ5KSAlPiUgCiAgc3VtbWFyaXplKGxpZmVFeHAgPSBtZWFuKGxpZmVFeHApLC5ncm91cHM9ImRyb3AiKSAgJT4lIAogIG11dGF0ZShjb3VudHJ5ID0gY291bnRyeWNvZGU6OmNvdW50cnljb2RlKGNvdW50cnksImNvdW50cnkubmFtZSIsImNvdW50cnkubmFtZSIpKSAlPiUgCiAgaW5uZXJfam9pbih3b3JsZCxieT0iY291bnRyeSIpCmBgYAoKTGV0J3MgcGxvdCB0aGUgbWFwLiAKCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIixmaWcud2lkdGg9NixmaWcuaGVpZ2h0PTd9CmFmcmljYSAlPiUgCiAgZ2dwbG90KGFlcyh4PWxvbmcseT1sYXQsZ3JvdXA9Z3JvdXApKSArCiAgZ2VvbV9wb2x5Z29uKCkKYGBgCgoKTm93IGxldCdzIGZpbGwgaW4gdGhlIGZpZWxkcyBvbiB0aGUgbWFwIHVzaW5nIHRoZSBhdmVyYWdlIGxpZmUgZXhwZWN0YW5jeSB2YWx1ZXMuCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciIsZmlnLndpZHRoPTYsZmlnLmhlaWdodD03fQphZnJpY2EgJT4lIAogIGdncGxvdChhZXMoeD1sb25nLHk9bGF0LGdyb3VwPWdyb3VwLGZpbGw9bGlmZUV4cCkpICsKICBnZW9tX3BvbHlnb24oY29sb3I9IndoaXRlIixzaXplPS4yNSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyX3RhYmxlYXUoKSArCiAgdGhlbWVfbWFwKCkgKwogIGxhYnMoZmlsbD0iTGlmZSBFeHBlY3RhbmN5IiwKICAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgTGlmZSBFeHBlY3RhbmN5IGluIEFmcmljYSIsCiAgICAgICBzdWJ0aXRsZSA9ICIxOTUyIC0gMjAwNyIsCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZSBnYXBtaW5kZXIub3JnIikgKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KGZhbWlseSA9ICJzZXJpZiIsZmFjZT0iYm9sZCIsc2l6ZT0xNCkpCmBgYAoKV2hhdCBpZiB3ZSB3YW50ZWQgdG8gbG9vayBhdCBob3cgdGhlc2Ugc3BhdGlhbCBwYXR0ZXJucyBzaGlmdGVkIG92ZXIgdGltZT8gTm90IGEgcHJvYmxlbSwgd2UganVzdCBuZWVkIHRvIHR3ZWFrIHRoZSBkYXRhIGFuZCBwbG90IGNvZGUgYSBsaXR0bGUgYml0LiAKCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIixmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0xNX0KIyBET04nVCBhZ2dyZWdhdGUgdGhlIGxpZmVFeHAgdmFyaWFibGUgdGhpcyB0aW1lLiAKZ2FwbWluZGVyICU+JSAKICBmaWx0ZXIoY29udGluZW50ID09ICJBZnJpY2EiKSAlPiUgCiAgbXV0YXRlKGNvdW50cnkgPSBjb3VudHJ5Y29kZTo6Y291bnRyeWNvZGUoY291bnRyeSwiY291bnRyeS5uYW1lIiwiY291bnRyeS5uYW1lIikpICU+JSAKICBpbm5lcl9qb2luKHdvcmxkLGJ5PSJjb3VudHJ5IikgJT4lIAogIGdncGxvdChhZXMoeD1sb25nLHk9bGF0LGdyb3VwPWdyb3VwLGZpbGw9bGlmZUV4cCkpICsKICBnZW9tX3BvbHlnb24oY29sb3I9IndoaXRlIixzaXplPS4yNSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyX3RhYmxlYXUoKSArCiAgdGhlbWVfbWFwKCkgKwogIGxhYnMoZmlsbD0iTGlmZSBFeHBlY3RhbmN5IiwKICAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgTGlmZSBFeHBlY3RhbmN5IGluIEFmcmljYSIsCiAgICAgICBzdWJ0aXRsZSA9ICIxOTUyIC0gMjAwNyIsCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZSBnYXBtaW5kZXIub3JnIikgKwogIGZhY2V0X3dyYXAofnllYXIpICsKICB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLGZhY2U9ImJvbGQiLHNpemU9MTgpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCgoKCg==