#install.packages("tidyverse")
require(tidyverse) # The tidyverse package covered last time 

# install.packages("ggthemes")
require(ggthemes) # for great visualization colors and themes. 

# install.packages("maps")
require(maps) # for some maps data

# Gapminder data (for example)
#install.packages("gapminder")
require(gapminder)

Data

The Gapminder dataset is a famous dataset used by Hans Rosling to visualize development outcomes. The data covers 1952 to 2007 in five year intervals and measures life expectancy, population, and GDP Per Capita.

gapminder %>% head()

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==