4.2 - Using the Gapminder Dataset


Faceting

🎞 Faceting

📖 Faceting

  • Faceting makes multiple side-by-side plots stratified by some variable. This is a way to ease comparisons.
  • The facet_grid() function allows faceting by up to two variables, with rows faceted by one variable and columns faceted by the other variable. To facet by only one variable, use the dot operator as the other variable.
  • The facet_wrap() function facets by one variable and automatically wraps the series of plots so they have readable dimensions.
  • Faceting keeps the axes fixed across all plots, easing comparisons between plots.
  • The data suggest that the developing versus Western world view no longer makes sense in 2012.
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(dslabs)
library(ggplot2)
data(gapminder)
# Facet by continent and year
filter(gapminder, year %in% c(1962, 2012)) %>%
    ggplot(aes(fertility, life_expectancy, col=continent)) +
    geom_point() +
    facet_grid(continent~year)

# Facet by year only
filter(gapminder, year %in% c(1962, 1980, 2012)) %>%
    ggplot(aes(fertility, life_expectancy, col=continent)) +
    geom_point() +
    facet_grid(.~year)

# Facet by year, plots wrapped onto multiple rows
years <- c(1962, 1980, 1990, 2000, 2012)
continents <- c("Europe", "Asia")
gapminder %>%
    filter(year %in% years & continent %in% continents) %>%
    ggplot(aes(fertility, life_expectancy, col=continent)) +
    geom_point() +
    facet_wrap(~year)


Time Series Plots

🎞 Time Series Plots

📖 Time series plots

  • Time series plots have time on the x-axis and a variable of interest on the y-axis.
  • The geom_line() geometry connects adjacent data points to form a continuous line. A line plot is appropriate when points are regularly spaced, densely packed and from a single data series.
  • You can plot multiple lines on the same graph. Remember to group or color by a variable so that the lines are plotted independently.
  • Labeling is usually preferred over legends. However, legends are easier to make and appear by default. Add a label with geom_text(), specifying the coordinates where the label should appear on the graph.

Code: Single time series

# Scatterplot of US fertility by year
gapminder %>%
    filter(country=="United States") %>%
    ggplot(aes(year, fertility)) +
    geom_point()

# Line plot of US fertility by year
gapminder %>%
    filter(country=="United States") %>%
    ggplot(aes(year, fertility)) +
    geom_line()

Code: Multiple time series

# Line plot fertility time series for two countries- only one line (incorrect)
countries <- c("South Korea", "Germany")
gapminder %>% filter(country %in% countries) %>%
    ggplot(aes(year, fertility)) +
    geom_line()

# Line plot fertility time series for two countries - one line per country
gapminder %>% filter(country %in% countries) %>%
    ggplot(aes(year, fertility, group=country)) +
    geom_line()

# Fertility time series for two countries - lines colored by country
gapminder %>% filter(country %in% countries) %>%
    ggplot(aes(year, fertility, col=country)) +
    geom_line()

Code: Adding text labels to a plot

# Life expectancy time series - lines colored by country and labeled, no legend
labels <- data.frame(country=countries, x=c(1975, 1965), y=c(60, 72))
gapminder %>% filter(country %in% countries) %>%
    ggplot(aes(year, life_expectancy, col=country)) +
    geom_line() +
    geom_text(data=labels, aes(x, y, label=country), size=5) +
    theme(legend.position="none")


Transformations

🎞 Transformations

📖 Data transformations

📖 Visualizing multimodal distributions

  • We use GDP data to compute income in US dollars per day, adjusted for inflation.
  • Log transformations convert multiplicative changes into additive changes.
  • Common transformations are the log base 2 transformation and the log base 10 transformation. The choice of base depends on the range of the data. The natural log is not recommended for visualization because it is difficult to interpret.
  • The mode of a distribution is the value with the highest frequency. The mode of a normal distribution is the average. A distribution can have multiple local modes.
  • There are two ways to use log transformations in plots: transform the data before plotting or transform the axes of the plot. Log scales have the advantage of showing the original values as axis labels, while log transformed values ease interpretation of intermediate values between labels.
  • Scale the x-axis using scale_x_continuous() or scale_x_log10() layers in ggplot2. Similar functions exist for the y-axis.
  • In 1970, income distribution is bimodal, consistent with the dichotomous Western versus developing worldview.
# Add dollars per day variable
gapminder <- gapminder %>%
    mutate(dollars_per_day=gdp/population/365)

# Histogram of dollars per day
past_year <- 1970
gapminder %>%
    filter(year==past_year & !is.na(gdp)) %>%
    ggplot(aes(dollars_per_day)) +
    geom_histogram(binwidth=1, color="black")

# Repeat histogram with log2 scaled data
gapminder %>%
    filter(year==past_year & !is.na(gdp)) %>%
    ggplot(aes(log2(dollars_per_day))) +
    geom_histogram(binwidth=1, color="black")

# Repeat histogram with log2 scaled x-axis
gapminder %>%
    filter(year==past_year & !is.na(gdp)) %>%
    ggplot(aes(dollars_per_day)) +
    geom_histogram(binwidth=1, color="black") +
    scale_x_continuous(trans="log2")


Transformations

🎞 Stratify and Boxplot

📖 Comparing multiple distributions with boxplots and ridge plots

Note that many boxplots from the video are instead dot plots in the textbook and that a different boxplot is constructed in the textbook. Also read that section to see an example of grouping factors with the case_when function.

  • Make boxplots stratified by a categorical variable using the geom_boxplot() geometry.
  • Rotate axis labels by changing the theme through element_text(). You can change the angle and justification of the text labels.
  • Consider ordering your factors by a meaningful value with the reorder() function, which changes the order of factor levels based on a related numeric vector. This is a way to ease comparisons.
  • Show the data by adding data points to the boxplot with a geom_point() layer. This adds information beyond the five-number summary to your plot, but too many data points can obfuscate your message.

Code: Boxplot of GDP by region

# Add dollars per day variable
gapminder <- gapminder %>%
    mutate(dollars_per_day = gdp/population/365)
# Number of regions
length(levels(gapminder$region))
[1] 22
# Boxplot of GDP by region in 1970
past_year <- 1970
p <- gapminder %>%
    filter(year == past_year & !is.na(gdp)) %>%
    ggplot(aes(region, dollars_per_day))
p + geom_boxplot()

# Rotate names on x-axis
p + geom_boxplot() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1))

Code: The reorder function

# By default, factor order is alphabetical
fac <- factor(c("Asia", "Asia", "West", "West", "West"))
levels(fac)
[1] "Asia" "West"
# Reorder factor by the category means
value <- c(10, 11, 12, 6, 4)
fac <- reorder(fac, value, FUN = mean)
levels(fac)
[1] "West" "Asia"

Code: Enhanced boxplot ordered by median income, scaled, and showing data

# Reorder by median income and color by continent
p <- gapminder %>%
    filter(year == past_year & !is.na(gdp)) %>%
    mutate(region = reorder(region, dollars_per_day, FUN = median)) %>%    # Reorder
    ggplot(aes(region, dollars_per_day, fill = continent)) +    # Color by continent
    geom_boxplot() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("")
p

# log2 scale y-axis
p + scale_y_continuous(trans = "log2")

# Add data points
p + scale_y_continuous(trans = "log2") + geom_point(show.legend = FALSE)

LS0tCnRpdGxlOiAiQ291cnNlIDIgLSBEYXRhIFNjaWVuY2U6IFZpc3VhbGl6YXRpb24iCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIHRoZW1lOiBmbGF0bHkKLS0tCgojIyA0LjIgLSBVc2luZyB0aGUgR2FwbWluZGVyIERhdGFzZXQKCl9fXyAKIyMjIyAqKkZhY2V0aW5nKioKCj4g8J+OniBbRmFjZXRpbmddKGh0dHBzOi8vZWR4LXZpZGVvLm5ldC9IQVJCMDIwRDIwMTctVjAwMjUwMF9EVEgubXA0KQo+IAo+IPCfk5YgW0ZhY2V0aW5nXShodHRwczovL3JhZmFsYWIuZ2l0aHViLmlvL2RzYm9vay9nYXBtaW5kZXIuaHRtbCNmYWNldGluZykKCi0gRmFjZXRpbmcgbWFrZXMgbXVsdGlwbGUgc2lkZS1ieS1zaWRlIHBsb3RzIHN0cmF0aWZpZWQgYnkgc29tZSB2YXJpYWJsZS4gVGhpcyBpcyBhIHdheSB0byBlYXNlIGNvbXBhcmlzb25zLgotIFRoZSBgZmFjZXRfZ3JpZCgpYCBmdW5jdGlvbiBhbGxvd3MgZmFjZXRpbmcgYnkgdXAgdG8gdHdvIHZhcmlhYmxlcywgd2l0aCByb3dzIGZhY2V0ZWQgYnkgb25lIHZhcmlhYmxlIGFuZCBjb2x1bW5zIGZhY2V0ZWQgYnkgdGhlIG90aGVyIHZhcmlhYmxlLiBUbyBmYWNldCBieSBvbmx5IG9uZSB2YXJpYWJsZSwgdXNlIHRoZSAqZG90IG9wZXJhdG9yKiBhcyB0aGUgb3RoZXIgdmFyaWFibGUuCi0gVGhlIGBmYWNldF93cmFwKClgIGZ1bmN0aW9uIGZhY2V0cyBieSBvbmUgdmFyaWFibGUgYW5kIGF1dG9tYXRpY2FsbHkgd3JhcHMgdGhlIHNlcmllcyBvZiBwbG90cyBzbyB0aGV5IGhhdmUgcmVhZGFibGUgZGltZW5zaW9ucy4KLSBGYWNldGluZyBrZWVwcyB0aGUgYXhlcyBmaXhlZCBhY3Jvc3MgYWxsIHBsb3RzLCBlYXNpbmcgY29tcGFyaXNvbnMgYmV0d2VlbiBwbG90cy4KLSBUaGUgZGF0YSBzdWdnZXN0IHRoYXQgdGhlIGRldmVsb3BpbmcgdmVyc3VzIFdlc3Rlcm4gd29ybGQgdmlldyBubyBsb25nZXIgbWFrZXMgc2Vuc2UgaW4gMjAxMi4KYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZHNsYWJzKQpsaWJyYXJ5KGdncGxvdDIpCmRhdGEoZ2FwbWluZGVyKQojIEZhY2V0IGJ5IGNvbnRpbmVudCBhbmQgeWVhcgpmaWx0ZXIoZ2FwbWluZGVyLCB5ZWFyICVpbiUgYygxOTYyLCAyMDEyKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKGZlcnRpbGl0eSwgbGlmZV9leHBlY3RhbmN5LCBjb2w9Y29udGluZW50KSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGZhY2V0X2dyaWQoY29udGluZW50fnllYXIpCiMgRmFjZXQgYnkgeWVhciBvbmx5CmZpbHRlcihnYXBtaW5kZXIsIHllYXIgJWluJSBjKDE5NjIsIDE5ODAsIDIwMTIpKSAlPiUKICAgIGdncGxvdChhZXMoZmVydGlsaXR5LCBsaWZlX2V4cGVjdGFuY3ksIGNvbD1jb250aW5lbnQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZmFjZXRfZ3JpZCgufnllYXIpCiMgRmFjZXQgYnkgeWVhciwgcGxvdHMgd3JhcHBlZCBvbnRvIG11bHRpcGxlIHJvd3MKeWVhcnMgPC0gYygxOTYyLCAxOTgwLCAxOTkwLCAyMDAwLCAyMDEyKQpjb250aW5lbnRzIDwtIGMoIkV1cm9wZSIsICJBc2lhIikKZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgJWluJSB5ZWFycyAmIGNvbnRpbmVudCAlaW4lIGNvbnRpbmVudHMpICU+JQogICAgZ2dwbG90KGFlcyhmZXJ0aWxpdHksIGxpZmVfZXhwZWN0YW5jeSwgY29sPWNvbnRpbmVudCkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBmYWNldF93cmFwKH55ZWFyKQpgYGAKX19fIAojIyMjICoqVGltZSBTZXJpZXMgUGxvdHMqKgoKPiDwn46eIFtUaW1lIFNlcmllcyBQbG90c10oaHR0cHM6Ly9lZHgtdmlkZW8ubmV0L0hBUkIwMjBEMjAxNy1WMDAyOTAwX0RUSC5tcDQpCj4gCj4g8J+TliBbVGltZSBzZXJpZXMgcGxvdHNdKGh0dHBzOi8vcmFmYWxhYi5naXRodWIuaW8vZHNib29rL2dhcG1pbmRlci5odG1sI3RpbWUtc2VyaWVzLXBsb3RzKQoKLSBUaW1lIHNlcmllcyBwbG90cyBoYXZlIHRpbWUgb24gdGhlIHgtYXhpcyBhbmQgYSB2YXJpYWJsZSBvZiBpbnRlcmVzdCBvbiB0aGUgeS1heGlzLgotIFRoZSBgZ2VvbV9saW5lKClgIGdlb21ldHJ5IGNvbm5lY3RzIGFkamFjZW50IGRhdGEgcG9pbnRzIHRvIGZvcm0gYSBjb250aW51b3VzIGxpbmUuIEEgbGluZSBwbG90IGlzIGFwcHJvcHJpYXRlIHdoZW4gcG9pbnRzIGFyZSByZWd1bGFybHkgc3BhY2VkLCBkZW5zZWx5IHBhY2tlZCBhbmQgZnJvbSBhIHNpbmdsZSBkYXRhIHNlcmllcy4KLSBZb3UgY2FuIHBsb3QgbXVsdGlwbGUgbGluZXMgb24gdGhlIHNhbWUgZ3JhcGguIFJlbWVtYmVyIHRvIGdyb3VwIG9yIGNvbG9yIGJ5IGEgdmFyaWFibGUgc28gdGhhdCB0aGUgbGluZXMgYXJlIHBsb3R0ZWQgaW5kZXBlbmRlbnRseS4KLSBMYWJlbGluZyBpcyB1c3VhbGx5IHByZWZlcnJlZCBvdmVyIGxlZ2VuZHMuIEhvd2V2ZXIsIGxlZ2VuZHMgYXJlIGVhc2llciB0byBtYWtlIGFuZCBhcHBlYXIgYnkgZGVmYXVsdC4gQWRkIGEgbGFiZWwgd2l0aCBgZ2VvbV90ZXh0KClgLCBzcGVjaWZ5aW5nIHRoZSBjb29yZGluYXRlcyB3aGVyZSB0aGUgbGFiZWwgc2hvdWxkIGFwcGVhciBvbiB0aGUgZ3JhcGguCgpDb2RlOiBTaW5nbGUgdGltZSBzZXJpZXMKYGBge3J9CiMgU2NhdHRlcnBsb3Qgb2YgVVMgZmVydGlsaXR5IGJ5IHllYXIKZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKGNvdW50cnk9PSJVbml0ZWQgU3RhdGVzIikgJT4lCiAgICBnZ3Bsb3QoYWVzKHllYXIsIGZlcnRpbGl0eSkpICsKICAgIGdlb21fcG9pbnQoKQojIExpbmUgcGxvdCBvZiBVUyBmZXJ0aWxpdHkgYnkgeWVhcgpnYXBtaW5kZXIgJT4lCiAgICBmaWx0ZXIoY291bnRyeT09IlVuaXRlZCBTdGF0ZXMiKSAlPiUKICAgIGdncGxvdChhZXMoeWVhciwgZmVydGlsaXR5KSkgKwogICAgZ2VvbV9saW5lKCkKYGBgCkNvZGU6IE11bHRpcGxlIHRpbWUgc2VyaWVzCmBgYHtyfQojIExpbmUgcGxvdCBmZXJ0aWxpdHkgdGltZSBzZXJpZXMgZm9yIHR3byBjb3VudHJpZXMtIG9ubHkgb25lIGxpbmUgKGluY29ycmVjdCkKY291bnRyaWVzIDwtIGMoIlNvdXRoIEtvcmVhIiwgIkdlcm1hbnkiKQpnYXBtaW5kZXIgJT4lIGZpbHRlcihjb3VudHJ5ICVpbiUgY291bnRyaWVzKSAlPiUKICAgIGdncGxvdChhZXMoeWVhciwgZmVydGlsaXR5KSkgKwogICAgZ2VvbV9saW5lKCkKIyBMaW5lIHBsb3QgZmVydGlsaXR5IHRpbWUgc2VyaWVzIGZvciB0d28gY291bnRyaWVzIC0gb25lIGxpbmUgcGVyIGNvdW50cnkKZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIGNvdW50cmllcykgJT4lCiAgICBnZ3Bsb3QoYWVzKHllYXIsIGZlcnRpbGl0eSwgZ3JvdXA9Y291bnRyeSkpICsKICAgIGdlb21fbGluZSgpCiMgRmVydGlsaXR5IHRpbWUgc2VyaWVzIGZvciB0d28gY291bnRyaWVzIC0gbGluZXMgY29sb3JlZCBieSBjb3VudHJ5CmdhcG1pbmRlciAlPiUgZmlsdGVyKGNvdW50cnkgJWluJSBjb3VudHJpZXMpICU+JQogICAgZ2dwbG90KGFlcyh5ZWFyLCBmZXJ0aWxpdHksIGNvbD1jb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKCkKYGBgCkNvZGU6IEFkZGluZyB0ZXh0IGxhYmVscyB0byBhIHBsb3QKYGBge3J9CiMgTGlmZSBleHBlY3RhbmN5IHRpbWUgc2VyaWVzIC0gbGluZXMgY29sb3JlZCBieSBjb3VudHJ5IGFuZCBsYWJlbGVkLCBubyBsZWdlbmQKbGFiZWxzIDwtIGRhdGEuZnJhbWUoY291bnRyeT1jb3VudHJpZXMsIHg9YygxOTc1LCAxOTY1KSwgeT1jKDYwLCA3MikpCmdhcG1pbmRlciAlPiUgZmlsdGVyKGNvdW50cnkgJWluJSBjb3VudHJpZXMpICU+JQogICAgZ2dwbG90KGFlcyh5ZWFyLCBsaWZlX2V4cGVjdGFuY3ksIGNvbD1jb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZ2VvbV90ZXh0KGRhdGE9bGFiZWxzLCBhZXMoeCwgeSwgbGFiZWw9Y291bnRyeSksIHNpemU9NSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKYGBgCl9fXyAKIyMjIyAqKlRyYW5zZm9ybWF0aW9ucyoqCgo+IPCfjp4gW1RyYW5zZm9ybWF0aW9uc10oaHR0cHM6Ly9lZHgtdmlkZW8ubmV0L0hBUkIwMjBEMjAxNy1WMDAyNzAwX0RUSC5tcDQpCj4gCj4g8J+TliBbRGF0YSB0cmFuc2Zvcm1hdGlvbnNdKGh0dHBzOi8vcmFmYWxhYi5naXRodWIuaW8vZHNib29rL2dhcG1pbmRlci5odG1sI2RhdGEtdHJhbnNmb3JtYXRpb25zKQo+IAo+IPCfk5YgW1Zpc3VhbGl6aW5nIG11bHRpbW9kYWwgZGlzdHJpYnV0aW9uc10oaHR0cHM6Ly9yYWZhbGFiLmdpdGh1Yi5pby9kc2Jvb2svZ2FwbWluZGVyLmh0bWwjdmlzdWFsaXppbmctbXVsdGltb2RhbC1kaXN0cmlidXRpb25zKQoKLSBXZSB1c2UgR0RQIGRhdGEgdG8gY29tcHV0ZSBpbmNvbWUgaW4gVVMgZG9sbGFycyBwZXIgZGF5LCBhZGp1c3RlZCBmb3IgaW5mbGF0aW9uLgotIExvZyB0cmFuc2Zvcm1hdGlvbnMgY29udmVydCBtdWx0aXBsaWNhdGl2ZSBjaGFuZ2VzIGludG8gYWRkaXRpdmUgY2hhbmdlcy4KLSBDb21tb24gdHJhbnNmb3JtYXRpb25zIGFyZSB0aGUgbG9nIGJhc2UgMiB0cmFuc2Zvcm1hdGlvbiBhbmQgdGhlIGxvZyBiYXNlIDEwIHRyYW5zZm9ybWF0aW9uLiBUaGUgY2hvaWNlIG9mIGJhc2UgZGVwZW5kcyBvbiB0aGUgcmFuZ2Ugb2YgdGhlIGRhdGEuIFRoZSBuYXR1cmFsIGxvZyBpcyBub3QgcmVjb21tZW5kZWQgZm9yIHZpc3VhbGl6YXRpb24gYmVjYXVzZSBpdCBpcyBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0LgotIFRoZSBtb2RlIG9mIGEgZGlzdHJpYnV0aW9uIGlzIHRoZSB2YWx1ZSB3aXRoIHRoZSBoaWdoZXN0IGZyZXF1ZW5jeS4gVGhlIG1vZGUgb2YgYSBub3JtYWwgZGlzdHJpYnV0aW9uIGlzIHRoZSBhdmVyYWdlLiBBIGRpc3RyaWJ1dGlvbiBjYW4gaGF2ZSBtdWx0aXBsZSBsb2NhbCBtb2Rlcy4KLSBUaGVyZSBhcmUgdHdvIHdheXMgdG8gdXNlIGxvZyB0cmFuc2Zvcm1hdGlvbnMgaW4gcGxvdHM6IHRyYW5zZm9ybSB0aGUgZGF0YSBiZWZvcmUgcGxvdHRpbmcgb3IgdHJhbnNmb3JtIHRoZSBheGVzIG9mIHRoZSBwbG90LiBMb2cgc2NhbGVzIGhhdmUgdGhlIGFkdmFudGFnZSBvZiBzaG93aW5nIHRoZSBvcmlnaW5hbCB2YWx1ZXMgYXMgYXhpcyBsYWJlbHMsIHdoaWxlIGxvZyB0cmFuc2Zvcm1lZCB2YWx1ZXMgZWFzZSBpbnRlcnByZXRhdGlvbiBvZiBpbnRlcm1lZGlhdGUgdmFsdWVzIGJldHdlZW4gbGFiZWxzLgotIFNjYWxlIHRoZSB4LWF4aXMgdXNpbmcgYHNjYWxlX3hfY29udGludW91cygpYCBvciBgc2NhbGVfeF9sb2cxMCgpYCBsYXllcnMgaW4gKmdncGxvdDIqLiBTaW1pbGFyIGZ1bmN0aW9ucyBleGlzdCBmb3IgdGhlIHktYXhpcy4KLSBJbiAxOTcwLCBpbmNvbWUgZGlzdHJpYnV0aW9uIGlzIGJpbW9kYWwsIGNvbnNpc3RlbnQgd2l0aCB0aGUgZGljaG90b21vdXMgV2VzdGVybiB2ZXJzdXMgZGV2ZWxvcGluZyB3b3JsZHZpZXcuCmBgYHtyfQojIEFkZCBkb2xsYXJzIHBlciBkYXkgdmFyaWFibGUKZ2FwbWluZGVyIDwtIGdhcG1pbmRlciAlPiUKICAgIG11dGF0ZShkb2xsYXJzX3Blcl9kYXk9Z2RwL3BvcHVsYXRpb24vMzY1KQoKIyBIaXN0b2dyYW0gb2YgZG9sbGFycyBwZXIgZGF5CnBhc3RfeWVhciA8LSAxOTcwCmdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyPT1wYXN0X3llYXIgJiAhaXMubmEoZ2RwKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKGRvbGxhcnNfcGVyX2RheSkpICsKICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEsIGNvbG9yPSJibGFjayIpCiMgUmVwZWF0IGhpc3RvZ3JhbSB3aXRoIGxvZzIgc2NhbGVkIGRhdGEKZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXI9PXBhc3RfeWVhciAmICFpcy5uYShnZHApKSAlPiUKICAgIGdncGxvdChhZXMobG9nMihkb2xsYXJzX3Blcl9kYXkpKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MSwgY29sb3I9ImJsYWNrIikKIyBSZXBlYXQgaGlzdG9ncmFtIHdpdGggbG9nMiBzY2FsZWQgeC1heGlzCmdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyPT1wYXN0X3llYXIgJiAhaXMubmEoZ2RwKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKGRvbGxhcnNfcGVyX2RheSkpICsKICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEsIGNvbG9yPSJibGFjayIpICsKICAgIHNjYWxlX3hfY29udGludW91cyh0cmFucz0ibG9nMiIpCmBgYApfX18gCiMjIyMgKipUcmFuc2Zvcm1hdGlvbnMqKgoKPiDwn46eIFtTdHJhdGlmeSBhbmQgQm94cGxvdF0oaHR0cHM6Ly9lZHgtdmlkZW8ubmV0L0hBUkIwMjBEMjAxNy1WMDAyNjAwX0RUSC5tcDQpCj4gCj4g8J+TliBbQ29tcGFyaW5nIG11bHRpcGxlIGRpc3RyaWJ1dGlvbnMgd2l0aCBib3hwbG90cyBhbmQgcmlkZ2UgcGxvdHNdKGh0dHBzOi8vcmFmYWxhYi5naXRodWIuaW8vZHNib29rL2dhcG1pbmRlci5odG1sI2NvbXBhcmluZy1tdWx0aXBsZS1kaXN0cmlidXRpb25zLXdpdGgtYm94cGxvdHMtYW5kLXJpZGdlLXBsb3RzKQoKPiBOb3RlIHRoYXQgbWFueSBib3hwbG90cyBmcm9tIHRoZSB2aWRlbyBhcmUgaW5zdGVhZCBkb3QgcGxvdHMgaW4gdGhlIHRleHRib29rIGFuZCB0aGF0IGEgZGlmZmVyZW50IGJveHBsb3QgaXMgY29uc3RydWN0ZWQgaW4gdGhlIHRleHRib29rLiBBbHNvIHJlYWQgdGhhdCBzZWN0aW9uIHRvIHNlZSBhbiBleGFtcGxlIG9mIGdyb3VwaW5nIGZhY3RvcnMgd2l0aCB0aGUgY2FzZV93aGVuIGZ1bmN0aW9uLgoKLSBNYWtlIGJveHBsb3RzIHN0cmF0aWZpZWQgYnkgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB1c2luZyB0aGUgYGdlb21fYm94cGxvdCgpYCBnZW9tZXRyeS4KLSBSb3RhdGUgYXhpcyBsYWJlbHMgYnkgY2hhbmdpbmcgdGhlIHRoZW1lIHRocm91Z2ggYGVsZW1lbnRfdGV4dCgpYC4gWW91IGNhbiBjaGFuZ2UgdGhlIGFuZ2xlIGFuZCBqdXN0aWZpY2F0aW9uIG9mIHRoZSB0ZXh0IGxhYmVscy4KLSBDb25zaWRlciBvcmRlcmluZyB5b3VyIGZhY3RvcnMgYnkgYSBtZWFuaW5nZnVsIHZhbHVlIHdpdGggdGhlIGByZW9yZGVyKClgIGZ1bmN0aW9uLCB3aGljaCBjaGFuZ2VzIHRoZSBvcmRlciBvZiBmYWN0b3IgbGV2ZWxzIGJhc2VkIG9uIGEgcmVsYXRlZCBudW1lcmljIHZlY3Rvci4gVGhpcyBpcyBhIHdheSB0byBlYXNlIGNvbXBhcmlzb25zLgotIFNob3cgdGhlIGRhdGEgYnkgYWRkaW5nIGRhdGEgcG9pbnRzIHRvIHRoZSBib3hwbG90IHdpdGggYSBgZ2VvbV9wb2ludCgpYCBsYXllci4gVGhpcyBhZGRzIGluZm9ybWF0aW9uIGJleW9uZCB0aGUgZml2ZS1udW1iZXIgc3VtbWFyeSB0byB5b3VyIHBsb3QsIGJ1dCB0b28gbWFueSBkYXRhIHBvaW50cyBjYW4gb2JmdXNjYXRlIHlvdXIgbWVzc2FnZS4KCkNvZGU6IEJveHBsb3Qgb2YgR0RQIGJ5IHJlZ2lvbgpgYGB7cn0KIyBBZGQgZG9sbGFycyBwZXIgZGF5IHZhcmlhYmxlCmdhcG1pbmRlciA8LSBnYXBtaW5kZXIgJT4lCiAgICBtdXRhdGUoZG9sbGFyc19wZXJfZGF5PWdkcC9wb3B1bGF0aW9uLzM2NSkKIyBOdW1iZXIgb2YgcmVnaW9ucwpsZW5ndGgobGV2ZWxzKGdhcG1pbmRlciRyZWdpb24pKQojIEJveHBsb3Qgb2YgR0RQIGJ5IHJlZ2lvbiBpbiAxOTcwCnBhc3RfeWVhciA8LSAxOTcwCnAgPC0gZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gcGFzdF95ZWFyICYgIWlzLm5hKGdkcCkpICU+JQogICAgZ2dwbG90KGFlcyhyZWdpb24sIGRvbGxhcnNfcGVyX2RheSkpCnAgKyBnZW9tX2JveHBsb3QoKQojIFJvdGF0ZSBuYW1lcyBvbiB4LWF4aXMKcCArIGdlb21fYm94cGxvdCgpICsKICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpCmBgYApDb2RlOiBUaGUgcmVvcmRlciBmdW5jdGlvbgpgYGB7cn0KIyBCeSBkZWZhdWx0LCBmYWN0b3Igb3JkZXIgaXMgYWxwaGFiZXRpY2FsCmZhYyA8LSBmYWN0b3IoYygiQXNpYSIsICJBc2lhIiwgIldlc3QiLCAiV2VzdCIsICJXZXN0IikpCmxldmVscyhmYWMpCiMgUmVvcmRlciBmYWN0b3IgYnkgdGhlIGNhdGVnb3J5IG1lYW5zCnZhbHVlIDwtIGMoMTAsIDExLCAxMiwgNiwgNCkKZmFjIDwtIHJlb3JkZXIoZmFjLCB2YWx1ZSwgRlVOPW1lYW4pCmxldmVscyhmYWMpCmBgYApDb2RlOiBFbmhhbmNlZCBib3hwbG90IG9yZGVyZWQgYnkgbWVkaWFuIGluY29tZSwgc2NhbGVkLCBhbmQgc2hvd2luZyBkYXRhCmBgYHtyfQojIFJlb3JkZXIgYnkgbWVkaWFuIGluY29tZSBhbmQgY29sb3IgYnkgY29udGluZW50CnAgPC0gZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gcGFzdF95ZWFyICYgIWlzLm5hKGdkcCkpICU+JQogICAgbXV0YXRlKHJlZ2lvbj1yZW9yZGVyKHJlZ2lvbiwgZG9sbGFyc19wZXJfZGF5LCBGVU49bWVkaWFuKSkgJT4lICAgICMgUmVvcmRlcgogICAgZ2dwbG90KGFlcyhyZWdpb24sIGRvbGxhcnNfcGVyX2RheSwgZmlsbD1jb250aW5lbnQpKSArICAgICMgQ29sb3IgYnkgY29udGluZW50CiAgICBnZW9tX2JveHBsb3QoKSArCiAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArCiAgICB4bGFiKCIiKQpwCiMgbG9nMiBzY2FsZSB5LWF4aXMKcCArIHNjYWxlX3lfY29udGludW91cyh0cmFucz0ibG9nMiIpCiMgQWRkIGRhdGEgcG9pbnRzCnAgKyBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9ImxvZzIiKSArIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQ9RkFMU0UpCmBgYAoK