In these exercises we will see the power of the libraries ggplot2 and plotly to make sense of statistical data. The goal is to reproduce the moving chart that you can see in this video from Hans Rosling – I invite you to watch his other videos, they are quite enlightening and inspiring:



For this, we will need to gather the data:


1 Data handling

The first thing to do is to load and regroup all these datasets into a single one.

  1. Load the tidyverse library and, using read_csv(), load the 4 datasets in 4 separate tibbles called children, income, pop and religion.
library(tidyverse)
library(readxl)
children <- read_csv("Data/children_per_woman_total_fertility.csv")
income   <- read_csv("Data/income_per_person_gdppercapita_ppp_inflation_adjusted.csv")
pop      <- read_csv("Data/population_total.csv")
religion <- read_csv("Data/religion.csv")
  1. To reproduce the chart on the video, we need to determine the dominant religion in each country. In the religion dataset, add a column Religion that will give the name of the dominant religion for each country. For this, you need to make the table contain just the Country and all religions, make the table tidy, and then select the religion with the highest proportion for each country. We will filter the data to get only the year 2020.
religion <- religion |>
        filter(Year==2020) |>
        select(Country, Buddhists:Unaffiliated) |> 
        pivot_longer(cols=-Country, 
                     names_to="Religion", 
                     values_to="Proportion") |>
        filter(.by = Country,
               Proportion==max(Proportion)) |>
        select(-Proportion)
  1. Using pivot_longer(), make all datasets tidy.
  • children should now contain 3 columns: Country, Year and Fertility.
  • income should now contain 3 columns: Country, Year and Income.
  • pop should now contain 3 columns: Country, Year and Population.

We will only consider data from 1900 to 2018. Example of syntax using the pipe operator |>:

DF <- read_table("name  2010  2011  2012  2014
Kevin  10    11   12   123
Jane   122   56   23   4
"
)
DF
## # A tibble: 2 × 5
##   name  `2010` `2011` `2012` `2014`
##   <chr>  <dbl>  <dbl>  <dbl>  <dbl>
## 1 Kevin     10     11     12    123
## 2 Jane     122     56     23      4
DF |> 
    select(name, '2010':'2012') |> 
    pivot_longer(col=-name,
                 names_to="Year", 
                 values_to="Score",
                 names_transform=list(Year = as.numeric))
## # A tibble: 6 × 3
##   name   Year Score
##   <chr> <dbl> <dbl>
## 1 Kevin  2010    10
## 2 Kevin  2011    11
## 3 Kevin  2012    12
## 4 Jane   2010   122
## 5 Jane   2011    56
## 6 Jane   2012    23

The line names_transform=list(Year = as.numeric) is here to convert the character year values to numerical values.

children <- children |> 
                select(Country, '1900':'2018') |> 
                pivot_longer(col=-Country, 
                             names_to="Year", 
                             values_to="Fertility",
                             names_transform=list(Year = as.numeric))
income   <- income |> 
                select(Country, '1900':'2018') |> 
                pivot_longer(col=-Country, 
                             names_to="Year", 
                             values_to="Income",
                             names_transform=list(Year = as.numeric))
pop      <- pop |> 
                select(Country, '1900':'2018') |> 
                pivot_longer(col=-Country, 
                             names_to="Year", 
                             values_to="Population",
                             names_transform=list(Year = as.numeric))
  1. Now we want to combine all these datasets into a single one called dat, containing the columns Country, Year, Population, Religion, Fertility and Income. Look into the inner_join() function of the dplyr library (which is part of the tidyverse library).
dat <- inner_join(children, income) |>
        inner_join(pop) |>
        inner_join(religion)
# write_csv(dat, "Data/dat.csv")

You should end up with a dataset like this one:

## # A tibble: 20,587 × 6
##    Country      Year Fertility Income Population Religion
##    <chr>       <dbl>     <dbl>  <dbl>      <dbl> <chr>   
##  1 Afghanistan  1900         7    793    5020000 Muslims 
##  2 Afghanistan  1901         7    796    5050000 Muslims 
##  3 Afghanistan  1902         7    798    5090000 Muslims 
##  4 Afghanistan  1903         7    801    5120000 Muslims 
##  5 Afghanistan  1904         7    804    5150000 Muslims 
##  6 Afghanistan  1905         7    807    5180000 Muslims 
##  7 Afghanistan  1906         7    809    5220000 Muslims 
##  8 Afghanistan  1907         7    812    5250000 Muslims 
##  9 Afghanistan  1908         7    815    5280000 Muslims 
## 10 Afghanistan  1909         7    818    5320000 Muslims 
## # ℹ 20,577 more rows

In case you struggled to get there, download the archive with the button at the top and get the dat tibble with dat <- read_csv("Data/dat.csv").

Now our dataset is ready, let’s plot it.

2 Plotting

  1. Load the library ggplot2 and set the global theme to theme_bw() using theme_set()
library(ggplot2)
theme_set(theme_bw())
  1. Create a subset of dat concerning your origin country. For me it will be dat_france
dat_france <- dat |> filter(Country=="France")
  1. Plot the evolution of the income per capita and the number of children per woman as a function of the years, and make it look like that (notice the kinks during the two world wars):
ggplot(data=dat_france, aes(x=Year, y=Income))+
    ggtitle("Household income in France")+
    xlab("Year")+
    ylab("Household income per capita per year [constant $]")+
    annotate("rect", xmin=1914, xmax=1918, ymin=-Inf, ymax=Inf, alpha=.3)+
    annotate("rect", xmin=1939, xmax=1945, ymin=-Inf, ymax=Inf, alpha=.3)+
    geom_point(alpha=0.2, size=5)+
    geom_smooth()

ggplot(data=dat_france, aes(x=Year, y=Fertility))+
    ggtitle("Fertility in France")+
    xlab("Year")+
    lims(y=c(0,5))+
    ylab("Children per woman")+
    annotate("rect", xmin=1914, xmax=1918, ymin=-Inf, ymax=Inf, alpha=.3)+
    annotate("rect", xmin=1939, xmax=1945, ymin=-Inf, ymax=Inf, alpha=.3)+
    geom_line(size=2, color="red")

  1. Create a subset of dat containing the data for your country plus all the neighbor countries (if you come from an island, the nearest countries…). For me, dat_france_region will contain data from France, Spain, Italy, Switzerland, Germany, Luxembourg and Belgium.
dat_france_region <- dat |>
        filter(Country %in% c("France", "Spain", "Italy", "Switzerland", "Germany", "Luxembourg", "Belgium"))
  1. Plot again income and fertility as a function of the years, but add a color corresponding to the country and a point size to its population:
ggplot(data=dat_france_region, aes(x=Year, y=Income, col=Country, size=Population))+
    ggtitle("Household income in France and its region")+
    xlab("Year")+
    ylab("Household income per capita per year [constant $]")+
    annotate("rect", xmin=1914, xmax=1918, ymin=-Inf, ymax=Inf, alpha=.3)+
    annotate("rect", xmin=1939, xmax=1945, ymin=-Inf, ymax=Inf, alpha=.3)+
    geom_point(alpha=0.5)

ggplot(data=dat_france_region, aes(x=Year, y=Fertility, col=Country, size=Population))+
    ggtitle("Fertility in France and its region")+
    xlab("Year")+
    lims(y=c(0,5))+
    ylab("Children per woman")+
    annotate("rect", xmin=1914, xmax=1918, ymin=-Inf, ymax=Inf, alpha=.3)+
    annotate("rect", xmin=1939, xmax=1945, ymin=-Inf, ymax=Inf, alpha=.3)+
    geom_point(alpha=.5)

  1. Load the library plotly and make the previous graphs interactive. You can make an interactive graph by calling ggplotly(), like that:
library(plotly)
P <- ggplot(data = dat_france, aes(x=Population, y=Income))+
        geom_point()
ggplotly(P) # add dynamicTicks=TRUE allows redrawing ticks when zooming in
  1. Finally, you can add a slider to the interactive graph allowing selecting a value for another variable (just like in the video) by adding the keyword frame = in the chart’s aesthetics. So now, make the graph of the video ! (you can also add the aesthetics id=Country to show the country name in the popup when hovering on a point).
library(plotly)
P <- ggplot(data=dat, aes(x=Income, 
                          y=Fertility, 
                          frame=Year, 
                          col=Religion, 
                          size=Population,
                          id=Country))+
    geom_point(alpha=0.5)+
    ggtitle("Fertility vs. Income in the World")+
    xlab("Household income per capita per year [constant $]")+
    lims(y=c(0,8))+
    scale_size(range=c(1, 15))+
    ylab("Children per woman")
ggplotly(P, dynamicTicks = TRUE) |> 
  layout(yaxis = list(autorange = FALSE), 
         xaxis = list(autorange = FALSE, type = 'log', range=list(2, 5.5)))
LS0tCnRpdGxlIDogIlIgRXhlcmNpc2VzIC0gUmVsaWdpb24gYW5kIGJhYmllcyIKZGF0ZSAgOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgICBodG1sX2RvY3VtZW50OgogICAgICAgIHRvYyAgICAgICAgICAgIDogdHJ1ZQogICAgICAgIHRvY19mbG9hdCAgICAgIDogdHJ1ZQogICAgICAgIHRvY19kZXB0aCAgICAgIDogNAogICAgICAgIGhpZ2hsaWdodCAgICAgIDogdGFuZ28KICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgICAgICBjb2RlX2Rvd25sb2FkICA6IHRydWUKcGFyYW1zOiAKICAgIHNvbHV0aW9uOgogICAgICAgIHZhbHVlOiB0cnVlCi0tLQoKYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLmFsaWduPSJjZW50ZXIifQpsaWJyYXJ5KGRvd25sb2FkdGhpcykKZG93bmxvYWRfbGluaygKICBsaW5rID0gIi4vQXJjaGl2ZS56aXAiLAogIG91dHB1dF9uYW1lID0gIkRhdGEgRmlsZXMiLAogIGJ1dHRvbl9sYWJlbCA9ICJEb3dubG9hZCBEYXRhIEZpbGVzIiwKICBidXR0b25fdHlwZSA9ICJkZWZhdWx0IiwKICBoYXNfaWNvbiA9IFRSVUUsCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwKICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFCikKYGBgCjxicj4KCi0tLS0KCkluIHRoZXNlIGV4ZXJjaXNlcyB3ZSB3aWxsIHNlZSB0aGUgcG93ZXIgb2YgdGhlIGxpYnJhcmllcyBgZ2dwbG90MmAgYW5kIGBwbG90bHlgIHRvIG1ha2Ugc2Vuc2Ugb2Ygc3RhdGlzdGljYWwgZGF0YS4gVGhlIGdvYWwgaXMgdG8gcmVwcm9kdWNlIHRoZSBtb3ZpbmcgY2hhcnQgdGhhdCB5b3UgY2FuIHNlZSBpbiB0aGlzIHZpZGVvIGZyb20gSGFucyBSb3NsaW5nIC0tIEkgaW52aXRlIHlvdSB0byB3YXRjaCBoaXMgb3RoZXIgdmlkZW9zLCB0aGV5IGFyZSBxdWl0ZSBlbmxpZ2h0ZW5pbmcgYW5kIGluc3BpcmluZzoKCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPgo8YSBocmVmPSJodHRwczovL3d3dy50ZWQuY29tL3RhbGtzL2hhbnNfcm9zbGluZ19yZWxpZ2lvbnNfYW5kX2JhYmllcz9sYW5ndWFnZT1lbiIgdGFyZ2V0PSJfYmxhbmsiPgo8aSBjbGFzcz0iZmFiIGZhLXlvdXR1YmUgZmEtNXgiPjwvaT4KPC9hPgo8L2Rpdj4KCjxicj4KPGJyPgoKRm9yIHRoaXMsIHdlIHdpbGwgbmVlZCB0byBnYXRoZXIgdGhlIGRhdGE6CgotIEZyb20gW0dhcG1pbmRlcl0oaHR0cHM6Ly93d3cuZ2FwbWluZGVyLm9yZy9kYXRhLyksIGRhdGEgcGVyIGNvdW50cnkgYW5kIHBlciB5ZWFyIGZyb20gMTgwMCB0byAyMDE4OgogICAgLSBbVGhlIGNoaWxkcmVuIHBlciB3b21hbiB0b3RhbCBmZXJ0aWxpdHldKERhdGEvY2hpbGRyZW5fcGVyX3dvbWFuX3RvdGFsX2ZlcnRpbGl0eS5jc3YpCiAgICAtIFtUaGUgaW5jb21lIHBlciBjYXBpdGFdKERhdGEvaW5jb21lX3Blcl9wZXJzb25fZ2RwcGVyY2FwaXRhX3BwcF9pbmZsYXRpb25fYWRqdXN0ZWQuY3N2KQogICAgLSBbVGhlIHRvdGFsIHBvcHVsYXRpb25dKERhdGEvcG9wdWxhdGlvbl90b3RhbC5jc3YpCi0gRnJvbSB0aGUgW1BFVyByZXNlYXJjaCBjZW50ZXJdKGh0dHBzOi8vd3d3LnBld2ZvcnVtLm9yZy8yMDE1LzA0LzAyL3JlbGlnaW91cy1wcm9qZWN0aW9uLXRhYmxlLzIwMTAvcGVyY2VudC9hbGwvKSwgZGF0YSBwZXIgY291bnRyeToKICAgICsgW1RoZSByZWxpZ2lvdXMgY29tcG9zaXRpb25dKERhdGEvcmVsaWdpb24uY3N2KQoKLS0tLS0tLSAKCiMgRGF0YSBoYW5kbGluZwoKVGhlIGZpcnN0IHRoaW5nIHRvIGRvIGlzIHRvIGxvYWQgYW5kIHJlZ3JvdXAgYWxsIHRoZXNlIGRhdGFzZXRzIGludG8gYSBzaW5nbGUgb25lLgoKMS4gTG9hZCB0aGUgYHRpZHl2ZXJzZWAgbGlicmFyeSBhbmQsIHVzaW5nIGByZWFkX2NzdigpYCwgbG9hZCB0aGUgNCBkYXRhc2V0cyBpbiA0IHNlcGFyYXRlIHRpYmJsZXMgY2FsbGVkIGBjaGlsZHJlbmAsIGBpbmNvbWVgLCBgcG9wYCBhbmQgYHJlbGlnaW9uYC4KCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkeGwpCmNoaWxkcmVuIDwtIHJlYWRfY3N2KCJEYXRhL2NoaWxkcmVuX3Blcl93b21hbl90b3RhbF9mZXJ0aWxpdHkuY3N2IikKaW5jb21lICAgPC0gcmVhZF9jc3YoIkRhdGEvaW5jb21lX3Blcl9wZXJzb25fZ2RwcGVyY2FwaXRhX3BwcF9pbmZsYXRpb25fYWRqdXN0ZWQuY3N2IikKcG9wICAgICAgPC0gcmVhZF9jc3YoIkRhdGEvcG9wdWxhdGlvbl90b3RhbC5jc3YiKQpyZWxpZ2lvbiA8LSByZWFkX2NzdigiRGF0YS9yZWxpZ2lvbi5jc3YiKQpgYGAKCjIuIFRvIHJlcHJvZHVjZSB0aGUgY2hhcnQgb24gdGhlIHZpZGVvLCB3ZSBuZWVkIHRvIGRldGVybWluZSB0aGUgZG9taW5hbnQgcmVsaWdpb24gaW4gZWFjaCBjb3VudHJ5LiBJbiB0aGUgYHJlbGlnaW9uYCBkYXRhc2V0LCBhZGQgYSBjb2x1bW4gYFJlbGlnaW9uYCB0aGF0IHdpbGwgZ2l2ZSB0aGUgbmFtZSBvZiB0aGUgZG9taW5hbnQgcmVsaWdpb24gZm9yIGVhY2ggY291bnRyeS4gRm9yIHRoaXMsIHlvdSBuZWVkIHRvIG1ha2UgdGhlIHRhYmxlIGNvbnRhaW4ganVzdCB0aGUgYENvdW50cnlgIGFuZCBhbGwgcmVsaWdpb25zLCBtYWtlIHRoZSB0YWJsZSB0aWR5LCBhbmQgdGhlbiBzZWxlY3QgdGhlIHJlbGlnaW9uIHdpdGggdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbiBmb3IgZWFjaCBjb3VudHJ5LiBXZSB3aWxsIGZpbHRlciB0aGUgZGF0YSB0byBnZXQgb25seSB0aGUgeWVhciAyMDIwLgoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CnJlbGlnaW9uIDwtIHJlbGlnaW9uIHw+CiAgICAgICAgZmlsdGVyKFllYXI9PTIwMjApIHw+CiAgICAgICAgc2VsZWN0KENvdW50cnksIEJ1ZGRoaXN0czpVbmFmZmlsaWF0ZWQpIHw+IAogICAgICAgIHBpdm90X2xvbmdlcihjb2xzPS1Db3VudHJ5LCAKICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG89IlJlbGlnaW9uIiwgCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190bz0iUHJvcG9ydGlvbiIpIHw+CiAgICAgICAgZmlsdGVyKC5ieSA9IENvdW50cnksCiAgICAgICAgICAgICAgIFByb3BvcnRpb249PW1heChQcm9wb3J0aW9uKSkgfD4KICAgICAgICBzZWxlY3QoLVByb3BvcnRpb24pCmBgYAoKMy4gVXNpbmcgYHBpdm90X2xvbmdlcigpYCwgbWFrZSBhbGwgZGF0YXNldHMgdGlkeS4gCgotIGBjaGlsZHJlbmAgc2hvdWxkIG5vdyBjb250YWluIDMgY29sdW1uczogYENvdW50cnlgLCBgWWVhcmAgYW5kIGBGZXJ0aWxpdHlgLiAKLSBgaW5jb21lYCBzaG91bGQgbm93IGNvbnRhaW4gMyBjb2x1bW5zOiBgQ291bnRyeWAsIGBZZWFyYCBhbmQgYEluY29tZWAuIAotIGBwb3BgIHNob3VsZCBub3cgY29udGFpbiAzIGNvbHVtbnM6IGBDb3VudHJ5YCwgYFllYXJgIGFuZCBgUG9wdWxhdGlvbmAuIAoKV2Ugd2lsbCBvbmx5IGNvbnNpZGVyIGRhdGEgZnJvbSAxOTAwIHRvIDIwMTguIEV4YW1wbGUgb2Ygc3ludGF4IHVzaW5nIHRoZSBwaXBlIG9wZXJhdG9yIGB8PmA6CgpgYGB7cn0KREYgPC0gcmVhZF90YWJsZSgibmFtZSAgMjAxMCAgMjAxMSAgMjAxMiAgMjAxNApLZXZpbiAgMTAgICAgMTEgICAxMiAgIDEyMwpKYW5lICAgMTIyICAgNTYgICAyMyAgIDQKIgopCkRGCkRGIHw+IAogICAgc2VsZWN0KG5hbWUsICcyMDEwJzonMjAxMicpIHw+IAogICAgcGl2b3RfbG9uZ2VyKGNvbD0tbmFtZSwKICAgICAgICAgICAgICAgICBuYW1lc190bz0iWWVhciIsIAogICAgICAgICAgICAgICAgIHZhbHVlc190bz0iU2NvcmUiLAogICAgICAgICAgICAgICAgIG5hbWVzX3RyYW5zZm9ybT1saXN0KFllYXIgPSBhcy5udW1lcmljKSkKYGBgClRoZSBsaW5lIGBuYW1lc190cmFuc2Zvcm09bGlzdChZZWFyID0gYXMubnVtZXJpYylgIGlzIGhlcmUgdG8gY29udmVydCB0aGUgY2hhcmFjdGVyIHllYXIgdmFsdWVzIHRvIG51bWVyaWNhbCB2YWx1ZXMuCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1GQUxTRX0KY2hpbGRyZW4gPC0gY2hpbGRyZW4gfD4gCiAgICAgICAgICAgICAgICBzZWxlY3QoQ291bnRyeSwgJzE5MDAnOicyMDE4JykgfD4gCiAgICAgICAgICAgICAgICBwaXZvdF9sb25nZXIoY29sPS1Db3VudHJ5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc190bz0iWWVhciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190bz0iRmVydGlsaXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc190cmFuc2Zvcm09bGlzdChZZWFyID0gYXMubnVtZXJpYykpCmluY29tZSAgIDwtIGluY29tZSB8PiAKICAgICAgICAgICAgICAgIHNlbGVjdChDb3VudHJ5LCAnMTkwMCc6JzIwMTgnKSB8PiAKICAgICAgICAgICAgICAgIHBpdm90X2xvbmdlcihjb2w9LUNvdW50cnksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvPSJZZWFyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX3RvPSJJbmNvbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RyYW5zZm9ybT1saXN0KFllYXIgPSBhcy5udW1lcmljKSkKcG9wICAgICAgPC0gcG9wIHw+IAogICAgICAgICAgICAgICAgc2VsZWN0KENvdW50cnksICcxOTAwJzonMjAxOCcpIHw+IAogICAgICAgICAgICAgICAgcGl2b3RfbG9uZ2VyKGNvbD0tQ291bnRyeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG89IlllYXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG89IlBvcHVsYXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RyYW5zZm9ybT1saXN0KFllYXIgPSBhcy5udW1lcmljKSkKYGBgCgo0LiBOb3cgd2Ugd2FudCB0byBjb21iaW5lIGFsbCB0aGVzZSBkYXRhc2V0cyBpbnRvIGEgc2luZ2xlIG9uZSBjYWxsZWQgYGRhdGAsIGNvbnRhaW5pbmcgdGhlIGNvbHVtbnMgYENvdW50cnlgLCBgWWVhcmAsIGBQb3B1bGF0aW9uYCwgYFJlbGlnaW9uYCwgYEZlcnRpbGl0eWAgYW5kIGBJbmNvbWVgLiBMb29rIGludG8gdGhlIGBpbm5lcl9qb2luKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIGxpYnJhcnkgKHdoaWNoIGlzIHBhcnQgb2YgdGhlIGB0aWR5dmVyc2VgIGxpYnJhcnkpLiAKCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPUZBTFNFfQpkYXQgPC0gaW5uZXJfam9pbihjaGlsZHJlbiwgaW5jb21lKSB8PgogICAgICAgIGlubmVyX2pvaW4ocG9wKSB8PgogICAgICAgIGlubmVyX2pvaW4ocmVsaWdpb24pCiMgd3JpdGVfY3N2KGRhdCwgIkRhdGEvZGF0LmNzdiIpCmBgYAoKWW91IHNob3VsZCBlbmQgdXAgd2l0aCBhIGRhdGFzZXQgbGlrZSB0aGlzIG9uZToKCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CmRhdApgYGAKCkluIGNhc2UgeW91IHN0cnVnZ2xlZCB0byBnZXQgdGhlcmUsIGRvd25sb2FkIHRoZSBhcmNoaXZlIHdpdGggdGhlIGJ1dHRvbiBhdCB0aGUgdG9wIGFuZCBnZXQgdGhlIGBkYXRgIHRpYmJsZSB3aXRoIGBkYXQgPC0gcmVhZF9jc3YoIkRhdGEvZGF0LmNzdiIpYHsuUn0uCgpOb3cgb3VyIGRhdGFzZXQgaXMgcmVhZHksIGxldCdzIHBsb3QgaXQuCgojIFBsb3R0aW5nCgoxLiBMb2FkIHRoZSBsaWJyYXJ5IGBnZ3Bsb3QyYCBhbmQgc2V0IHRoZSBnbG9iYWwgdGhlbWUgdG8gYHRoZW1lX2J3KClgIHVzaW5nIGB0aGVtZV9zZXQoKWAKCmBgYHtyIGluY2x1ZGU9cGFyYW1zJHNvbHV0aW9uLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCnRoZW1lX3NldCh0aGVtZV9idygpKQpgYGAKCjIuIENyZWF0ZSBhIHN1YnNldCBvZiBgZGF0YCBjb25jZXJuaW5nIHlvdXIgb3JpZ2luIGNvdW50cnkuIEZvciBtZSBpdCB3aWxsIGJlIGBkYXRfZnJhbmNlYAoKYGBge3IgaW5jbHVkZT1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CmRhdF9mcmFuY2UgPC0gZGF0IHw+IGZpbHRlcihDb3VudHJ5PT0iRnJhbmNlIikKYGBgCgozLiBQbG90IHRoZSBldm9sdXRpb24gb2YgdGhlIGluY29tZSBwZXIgY2FwaXRhIGFuZCB0aGUgbnVtYmVyIG9mIGNoaWxkcmVuIHBlciB3b21hbiBhcyBhIGZ1bmN0aW9uIG9mIHRoZSB5ZWFycywgYW5kIG1ha2UgaXQgbG9vayBsaWtlIHRoYXQgKG5vdGljZSB0aGUga2lua3MgZHVyaW5nIHRoZSB0d28gd29ybGQgd2Fycyk6CgpgYGB7ciBlY2hvPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1GQUxTRX0KZ2dwbG90KGRhdGE9ZGF0X2ZyYW5jZSwgYWVzKHg9WWVhciwgeT1JbmNvbWUpKSsKICAgIGdndGl0bGUoIkhvdXNlaG9sZCBpbmNvbWUgaW4gRnJhbmNlIikrCiAgICB4bGFiKCJZZWFyIikrCiAgICB5bGFiKCJIb3VzZWhvbGQgaW5jb21lIHBlciBjYXBpdGEgcGVyIHllYXIgW2NvbnN0YW50ICRdIikrCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MTkxNCwgeG1heD0xOTE4LCB5bWluPS1JbmYsIHltYXg9SW5mLCBhbHBoYT0uMykrCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MTkzOSwgeG1heD0xOTQ1LCB5bWluPS1JbmYsIHltYXg9SW5mLCBhbHBoYT0uMykrCiAgICBnZW9tX3BvaW50KGFscGhhPTAuMiwgc2l6ZT01KSsKICAgIGdlb21fc21vb3RoKCkKZ2dwbG90KGRhdGE9ZGF0X2ZyYW5jZSwgYWVzKHg9WWVhciwgeT1GZXJ0aWxpdHkpKSsKICAgIGdndGl0bGUoIkZlcnRpbGl0eSBpbiBGcmFuY2UiKSsKICAgIHhsYWIoIlllYXIiKSsKICAgIGxpbXMoeT1jKDAsNSkpKwogICAgeWxhYigiQ2hpbGRyZW4gcGVyIHdvbWFuIikrCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MTkxNCwgeG1heD0xOTE4LCB5bWluPS1JbmYsIHltYXg9SW5mLCBhbHBoYT0uMykrCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MTkzOSwgeG1heD0xOTQ1LCB5bWluPS1JbmYsIHltYXg9SW5mLCBhbHBoYT0uMykrCiAgICBnZW9tX2xpbmUoc2l6ZT0yLCBjb2xvcj0icmVkIikKYGBgCgo0LiBDcmVhdGUgYSBzdWJzZXQgb2YgYGRhdGAgY29udGFpbmluZyB0aGUgZGF0YSBmb3IgeW91ciBjb3VudHJ5IHBsdXMgYWxsIHRoZSBuZWlnaGJvciBjb3VudHJpZXMgKGlmIHlvdSBjb21lIGZyb20gYW4gaXNsYW5kLCB0aGUgbmVhcmVzdCBjb3VudHJpZXMuLi4pLiBGb3IgbWUsIGBkYXRfZnJhbmNlX3JlZ2lvbmAgd2lsbCBjb250YWluIGRhdGEgZnJvbSBGcmFuY2UsIFNwYWluLCBJdGFseSwgU3dpdHplcmxhbmQsIEdlcm1hbnksIEx1eGVtYm91cmcgYW5kIEJlbGdpdW0uCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1GQUxTRX0KZGF0X2ZyYW5jZV9yZWdpb24gPC0gZGF0IHw+CiAgICAgICAgZmlsdGVyKENvdW50cnkgJWluJSBjKCJGcmFuY2UiLCAiU3BhaW4iLCAiSXRhbHkiLCAiU3dpdHplcmxhbmQiLCAiR2VybWFueSIsICJMdXhlbWJvdXJnIiwgIkJlbGdpdW0iKSkKYGBgCgo1LiBQbG90IGFnYWluIGluY29tZSBhbmQgZmVydGlsaXR5IGFzIGEgZnVuY3Rpb24gb2YgdGhlIHllYXJzLCBidXQgYWRkIGEgY29sb3IgY29ycmVzcG9uZGluZyB0byB0aGUgY291bnRyeSBhbmQgYSBwb2ludCBzaXplIHRvIGl0cyBwb3B1bGF0aW9uOgoKYGBge3IgZWNobz1wYXJhbXMkc29sdXRpb24sIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CmdncGxvdChkYXRhPWRhdF9mcmFuY2VfcmVnaW9uLCBhZXMoeD1ZZWFyLCB5PUluY29tZSwgY29sPUNvdW50cnksIHNpemU9UG9wdWxhdGlvbikpKwogICAgZ2d0aXRsZSgiSG91c2Vob2xkIGluY29tZSBpbiBGcmFuY2UgYW5kIGl0cyByZWdpb24iKSsKICAgIHhsYWIoIlllYXIiKSsKICAgIHlsYWIoIkhvdXNlaG9sZCBpbmNvbWUgcGVyIGNhcGl0YSBwZXIgeWVhciBbY29uc3RhbnQgJF0iKSsKICAgIGFubm90YXRlKCJyZWN0IiwgeG1pbj0xOTE0LCB4bWF4PTE5MTgsIHltaW49LUluZiwgeW1heD1JbmYsIGFscGhhPS4zKSsKICAgIGFubm90YXRlKCJyZWN0IiwgeG1pbj0xOTM5LCB4bWF4PTE5NDUsIHltaW49LUluZiwgeW1heD1JbmYsIGFscGhhPS4zKSsKICAgIGdlb21fcG9pbnQoYWxwaGE9MC41KQpnZ3Bsb3QoZGF0YT1kYXRfZnJhbmNlX3JlZ2lvbiwgYWVzKHg9WWVhciwgeT1GZXJ0aWxpdHksIGNvbD1Db3VudHJ5LCBzaXplPVBvcHVsYXRpb24pKSsKICAgIGdndGl0bGUoIkZlcnRpbGl0eSBpbiBGcmFuY2UgYW5kIGl0cyByZWdpb24iKSsKICAgIHhsYWIoIlllYXIiKSsKICAgIGxpbXMoeT1jKDAsNSkpKwogICAgeWxhYigiQ2hpbGRyZW4gcGVyIHdvbWFuIikrCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MTkxNCwgeG1heD0xOTE4LCB5bWluPS1JbmYsIHltYXg9SW5mLCBhbHBoYT0uMykrCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW49MTkzOSwgeG1heD0xOTQ1LCB5bWluPS1JbmYsIHltYXg9SW5mLCBhbHBoYT0uMykrCiAgICBnZW9tX3BvaW50KGFscGhhPS41KQpgYGAKCjYuIExvYWQgdGhlIGxpYnJhcnkgYHBsb3RseWAgYW5kIG1ha2UgdGhlIHByZXZpb3VzIGdyYXBocyBpbnRlcmFjdGl2ZS4gWW91IGNhbiBtYWtlIGFuIGludGVyYWN0aXZlIGdyYXBoIGJ5IGNhbGxpbmcgYGdncGxvdGx5KClgLCBsaWtlIHRoYXQ6CgpgYGB7ciBpbmNsdWRlPVRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9RkFMU0V9CmxpYnJhcnkocGxvdGx5KQpQIDwtIGdncGxvdChkYXRhID0gZGF0X2ZyYW5jZSwgYWVzKHg9UG9wdWxhdGlvbiwgeT1JbmNvbWUpKSsKICAgICAgICBnZW9tX3BvaW50KCkKZ2dwbG90bHkoUCkgIyBhZGQgZHluYW1pY1RpY2tzPVRSVUUgYWxsb3dzIHJlZHJhd2luZyB0aWNrcyB3aGVuIHpvb21pbmcgaW4KYGBgCgoKNy4gRmluYWxseSwgeW91IGNhbiBhZGQgYSBzbGlkZXIgdG8gdGhlIGludGVyYWN0aXZlIGdyYXBoIGFsbG93aW5nIHNlbGVjdGluZyBhIHZhbHVlIGZvciBhbm90aGVyIHZhcmlhYmxlIChqdXN0IGxpa2UgaW4gdGhlIHZpZGVvKSBieSBhZGRpbmcgdGhlIGtleXdvcmQgYGZyYW1lID1gIGluIHRoZSBjaGFydCdzIGFlc3RoZXRpY3MuIFNvIG5vdywgbWFrZSB0aGUgZ3JhcGggb2YgdGhlIHZpZGVvICEgKHlvdSBjYW4gYWxzbyBhZGQgdGhlIGFlc3RoZXRpY3MgYGlkPUNvdW50cnlgIHRvIHNob3cgdGhlIGNvdW50cnkgbmFtZSBpbiB0aGUgcG9wdXAgd2hlbiBob3ZlcmluZyBvbiBhIHBvaW50KS4KCgpgYGB7ciBpbmNsdWRlPXBhcmFtcyRzb2x1dGlvbiwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjYWNoZT1GQUxTRX0KbGlicmFyeShwbG90bHkpClAgPC0gZ2dwbG90KGRhdGE9ZGF0LCBhZXMoeD1JbmNvbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHk9RmVydGlsaXR5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICBmcmFtZT1ZZWFyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9UmVsaWdpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9UG9wdWxhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBpZD1Db3VudHJ5KSkrCiAgICBnZW9tX3BvaW50KGFscGhhPTAuNSkrCiAgICBnZ3RpdGxlKCJGZXJ0aWxpdHkgdnMuIEluY29tZSBpbiB0aGUgV29ybGQiKSsKICAgIHhsYWIoIkhvdXNlaG9sZCBpbmNvbWUgcGVyIGNhcGl0YSBwZXIgeWVhciBbY29uc3RhbnQgJF0iKSsKICAgIGxpbXMoeT1jKDAsOCkpKwogICAgc2NhbGVfc2l6ZShyYW5nZT1jKDEsIDE1KSkrCiAgICB5bGFiKCJDaGlsZHJlbiBwZXIgd29tYW4iKQpnZ3Bsb3RseShQLCBkeW5hbWljVGlja3MgPSBUUlVFKSB8PiAKICBsYXlvdXQoeWF4aXMgPSBsaXN0KGF1dG9yYW5nZSA9IEZBTFNFKSwgCiAgICAgICAgIHhheGlzID0gbGlzdChhdXRvcmFuZ2UgPSBGQUxTRSwgdHlwZSA9ICdsb2cnLCByYW5nZT1saXN0KDIsIDUuNSkpKQpgYGAK