Today is more of a hodgepodge of tips, tricks, and tools rather than a coherent “lesson” or data analysis.

I’ll illustrate techniques using simulated data, and you’ll use gapminder data for your submission. (You’ll need to install the package and run library(gapminder) in order to access the data). The gapminder data was compiled by Hans Rosling, another household name in data viz, and there’s a super cool online tool for exploring the data.

Spend a little bit of time using the online tool to explore the variables and visualizations, thinking about what geoms the variables could be mapped to in order to recreate the visualizations.

First, we’re going to define a ggplot theme for the entire document. This is helpful when you want multiple graphs to share the same theme, but don’t want to change the theme every single time. In the chunk of code below, I’m defining a variable called lab06_theme that I’ll use as my theme throughout this lab.

lab06_theme = theme_minimal() + 
  theme(text = element_text(family = "serif", size = 12))

Part 1: Nonlinear regression

First, we’ll look at some data which is clearly non-linear:

data = read_csv("data/swarthmore.csv", col_names = FALSE)
ggplot(data, aes(x = X1, y = X2)) + 
  geom_point() +
  lab06_theme

We can add an lm() line to this graph:

ggplot(data, aes(x = X1, y = X2)) + 
  geom_point() +
  geom_smooth(method = "lm", se = FALSE) +
  lab06_theme

As expected, it adds absolutely no value! Let’s try adding a polynomial - the poly(x,2) creates an orthogonal polynomial of degree 2 (you should rarely fit these models in practice, so it’s OK to skip understanding the details! Think of it as a quadratic equation best fit to the data)

ggplot(data, aes(x = X1, y = X2)) + 
  geom_point() +
  geom_smooth(method = "lm", formula = y ~ poly(x, 2), se = FALSE) +
  geom_smooth(method = "lm", se = FALSE, col = "lightgray") +
  lab06_theme

Now, let’s try a better statistical method. Add geom_smooth(method = "loess", se = FALSE) to the graph above - your output should look something like the graph below (you can delete the other lines if you like). LOESS stands for Locally Estimated Scatterplot Smoothing. The basic idea is that it takes a moving window and draws the best fit line for that window, and then smooths all the windows together.

We can change the size of the moving window by changing the span argument. Smaller spans lead to bumpier curves, and larger spans lead to smoother fits. The plot below uses span = 0.2, but you should try a few different values of span:

ggplot(data, aes(x = X1, y = X2)) + 
  geom_point() +
  geom_smooth(method = "loess", span = .2, se = FALSE) +
  geom_smooth(method = "lm", formula = y ~ poly(x, 2), se = FALSE, col = "lightgray") +
  geom_smooth(method = "lm", se = FALSE, alpha =.2, col = "lightgray") +
  lab06_theme

Another option in geom_smooth is gam, which stands for Generalized Additive Model. GAMs are super flexible but still interpretable (as compared to more “black box” methods often used in machine learning). There’s a great free interactive course on GAMs in R which I highly recommend if you want to learn more about them. Add geom_smooth(method = "gam", se = FALSE) to see what a GAM looks like on this graph.

If we’re adding a bunch of different fits to the data, it would be helpful if our legend showed what they were! Here’s a sneaky way to define a color aesthetic as we go, where each of the lines is assigned a color that’s equal to the model name, and then we rename the aesthetic “Model” at the end in order to make the legend.

ggplot(data, aes(x = X1, y = X2)) + 
  geom_point() +
  geom_smooth(method = "gam", se = FALSE, aes(col = "GAM")) +
  geom_smooth(method = "loess", span = .2, se = FALSE, aes(col = "Loess")) +
  geom_smooth(method = "lm", formula = y ~ poly(x, 2), se = FALSE, aes(col = "Quadratic")) +
  geom_smooth(method = "lm", se = FALSE, alpha =.2, aes(col = "OLS")) +
  lab06_theme + 
  scale_color_viridis_d("Model", end = .75, option = "C") 

Part 2: Density Plots

Like we saw in class today, density plots are closely related to histograms, and you’ve all seen a histogram! There’s a great Interactive Essay you can check out if you have time to see how complicated putting together histograms really is under the hood.

One of the main problems with histograms is the bumpiness of the bars. This is often because we’re plotting a sample, while a density estimate provides an estimate for the population. Here’s an example of how different samples can look when they’re drawn from the exact same distribution. The set.seed function makes sure that if you run this same code on your computer, you’ll get the exact same set of random samples. The rnorm function tells R to draw 1000 random samples of a normally distributed variable, and the first few lines of code saves that data in a tibble with 10 columns (corresponding to 10 samples) and 1000 rows (corresponding to observations).

set.seed(011221)
samples = data.frame(matrix(rnorm(1000), ncol = 10))
colnames(samples) = paste0("sample", 1:10)
samples = tibble(samples)
samples %>%
  pivot_longer(1:10) %>%
  ggplot(., aes(x = value, fill = name)) + 
  geom_histogram() + 
  facet_wrap(vars(name)) + 
  scale_fill_viridis_d(end = .75, option = "C") + 
  lab06_theme + 
  theme(legend.position = "none")

The code below adds a density estimate to each of the histograms. Note that I had to change the y aesthetic in geom_histogram to map to ..density.. rather than ..count... This is for the probability reasons we talked about in class today, so that the area under the density curve is equal to one. Here, I’ve added bw = .1 to the geom_density, which results in a pretty bumpy curve. Try playing around with different values of bw to get a smoother curve that looks roughly the same across all of our samples.

samples %>%
  pivot_longer(1:10) %>%
  ggplot(., aes(x = value, fill = name)) + 
  geom_histogram(aes(y = ..density..)) + 
  geom_density(alpha = .3, bw = .1, aes(col = name)) +
  facet_wrap(vars(name)) + 
  scale_fill_viridis_d(end = .75, option = "C") + 
  scale_color_viridis_d(end = .75, option = "C") + 
  lab06_theme + 
  theme(legend.position = "none")

Just like a density plot is related to a histogram, a violin plot is related to a box plot. Below is the same set of samples, but plotted using boxplots instead of histograms.

samples %>%
  pivot_longer(1:10) %>%
  ggplot(., aes(y = value, x = name, fill = name)) + 
  geom_boxplot(alpha = .7) + 
  scale_fill_viridis_d(end = .75, option = "C") + 
  scale_color_viridis_d(end = .75, option = "C") + 
  lab06_theme + 
  theme(legend.position = "none")

Now, add geom_jitter(alpha = X, aes(col = name)) to the code above to replicate the graph below. Play around with different alpha values to get something that looks nice. This is a great example of not hiding the raw data behind a model.

Finally, let’s replace geom_boxplot with geom_violin. A violin plot is just a density estimate rotated to be on the other axis, and then mirrored to create a “violin” shape. This graph is a little “busy” and I’d want to tweak it before publishing, but it does a nice job of showing the raw data (geom_jitter) alongside the statistical model (geom_violin).

Finally, if we have many distributions that we want to compare to each other, sometimes it’s easiest if we have a single axes to deal with. Enter ggridges! (Yes, this is yet another package)

library(ggridges)
samples %>%
  pivot_longer(1:10) %>%
  ggplot(., aes(y = name, x = value, fill = name)) + 
  geom_density_ridges(alpha = .8) +
  scale_fill_viridis_d(end = .75, option = "C") + 
  scale_color_viridis_d(end = .75, option = "C") + 
  lab06_theme + 
  theme(legend.position = "none")

Part 3: Open-ended

Reminder: here you’ll use the gapminder data:

library(gapminder)
head(gapminder)
## # A tibble: 6 x 6
##   country     continent  year lifeExp      pop gdpPercap
##   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
## 1 Afghanistan Asia       1952    28.8  8425333      779.
## 2 Afghanistan Asia       1957    30.3  9240934      821.
## 3 Afghanistan Asia       1962    32.0 10267083      853.
## 4 Afghanistan Asia       1967    34.0 11537966      836.
## 5 Afghanistan Asia       1972    36.1 13079460      740.
## 6 Afghanistan Asia       1977    38.4 14880372      786.

Nonlinear smoothers

Find two variables from the gapminder, make a scatterplot, add a geom_smooth layer with a linear relationship and a geom_smooth layer with a nonlinear relationship (your choice!). Which fits better? Add colors and a legend.

Density

Choose a variable (or combination of variables) from the gapminder dataset and make either a kernel density plot, violin plot, or density ridges plot. Write 1-2 sentences summarizing your findings.

LS0tCnRpdGxlOiAiTGFiMDY6IE5vbmxpbmVhciBNb2RlbHMiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY3NzOiBzdGF0NDEtbGFiLXRoZW1lLmNzcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5bW9kZWxzKQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UpCmBgYAoKClRvZGF5IGlzIG1vcmUgb2YgYSBob2RnZXBvZGdlIG9mIHRpcHMsIHRyaWNrcywgYW5kIHRvb2xzIHJhdGhlciB0aGFuIGEgY29oZXJlbnQgImxlc3NvbiIgb3IgZGF0YSBhbmFseXNpcy4gIAoKSSdsbCBpbGx1c3RyYXRlIHRlY2huaXF1ZXMgdXNpbmcgc2ltdWxhdGVkIGRhdGEsIGFuZCB5b3UnbGwgdXNlIGBnYXBtaW5kZXJgIGRhdGEgZm9yIHlvdXIgc3VibWlzc2lvbi4gKFlvdSdsbCBuZWVkIHRvIGluc3RhbGwgdGhlIHBhY2thZ2UgYW5kIHJ1biBgbGlicmFyeShnYXBtaW5kZXIpYCBpbiBvcmRlciB0byBhY2Nlc3MgdGhlIGRhdGEpLiBUaGUgYGdhcG1pbmRlcmAgZGF0YSB3YXMgY29tcGlsZWQgYnkgSGFucyBSb3NsaW5nLCBhbm90aGVyIGhvdXNlaG9sZCBuYW1lIGluIGRhdGEgdml6LCBhbmQgdGhlcmUncyBhIHN1cGVyIGNvb2wgW29ubGluZSB0b29sXShodHRwczovL3d3dy5nYXBtaW5kZXIub3JnL3Rvb2xzLykgZm9yIGV4cGxvcmluZyB0aGUgZGF0YS4gCgpTcGVuZCBhIGxpdHRsZSBiaXQgb2YgdGltZSB1c2luZyB0aGUgb25saW5lIHRvb2wgdG8gZXhwbG9yZSB0aGUgdmFyaWFibGVzIGFuZCB2aXN1YWxpemF0aW9ucywgdGhpbmtpbmcgYWJvdXQgd2hhdCBgZ2VvbWBzIHRoZSB2YXJpYWJsZXMgY291bGQgYmUgbWFwcGVkIHRvIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSB2aXN1YWxpemF0aW9ucy4KCkZpcnN0LCB3ZSdyZSBnb2luZyB0byBkZWZpbmUgYSBnZ3Bsb3QgdGhlbWUgZm9yIHRoZSBlbnRpcmUgZG9jdW1lbnQuIFRoaXMgaXMgaGVscGZ1bCB3aGVuIHlvdSB3YW50IG11bHRpcGxlIGdyYXBocyB0byBzaGFyZSB0aGUgc2FtZSB0aGVtZSwgYnV0IGRvbid0IHdhbnQgdG8gY2hhbmdlIHRoZSB0aGVtZSBldmVyeSBzaW5nbGUgdGltZS4gSW4gdGhlIGNodW5rIG9mIGNvZGUgYmVsb3csIEknbSBkZWZpbmluZyBhIHZhcmlhYmxlIGNhbGxlZCBgbGFiMDZfdGhlbWVgIHRoYXQgSSdsbCB1c2UgYXMgbXkgdGhlbWUgdGhyb3VnaG91dCB0aGlzIGxhYi4KCmBgYHtyfQpsYWIwNl90aGVtZSA9IHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiwgc2l6ZSA9IDEyKSkKYGBgCgojIFBhcnQgMTogTm9ubGluZWFyIHJlZ3Jlc3Npb24KCkZpcnN0LCB3ZSdsbCBsb29rIGF0IHNvbWUgZGF0YSB3aGljaCBpcyBjbGVhcmx5IG5vbi1saW5lYXI6IAoKYGBge3J9CmRhdGEgPSByZWFkX2NzdigiZGF0YS9zd2FydGhtb3JlLmNzdiIsIGNvbF9uYW1lcyA9IEZBTFNFKQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBYMSwgeSA9IFgyKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGxhYjA2X3RoZW1lCmBgYAoKV2UgY2FuIGFkZCBhbiBgbG0oKWAgbGluZSB0byB0aGlzIGdyYXBoOiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBYMSwgeSA9IFgyKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsKICBsYWIwNl90aGVtZQpgYGAKCkFzIGV4cGVjdGVkLCBpdCBhZGRzIGFic29sdXRlbHkgbm8gdmFsdWUhIExldCdzIHRyeSBhZGRpbmcgYSBwb2x5bm9taWFsIC0gdGhlIGBwb2x5KHgsMilgIGNyZWF0ZXMgYW4gb3J0aG9nb25hbCBwb2x5bm9taWFsIG9mIGRlZ3JlZSAyICh5b3Ugc2hvdWxkIHJhcmVseSBmaXQgdGhlc2UgbW9kZWxzIGluIHByYWN0aWNlLCBzbyBpdCdzIE9LIHRvIHNraXAgdW5kZXJzdGFuZGluZyB0aGUgZGV0YWlscyEgVGhpbmsgb2YgaXQgYXMgYSBxdWFkcmF0aWMgZXF1YXRpb24gYmVzdCBmaXQgdG8gdGhlIGRhdGEpCgpgYGB7cn0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gWDEsIHkgPSBYMikpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHBvbHkoeCwgMiksIHNlID0gRkFMU0UpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2wgPSAibGlnaHRncmF5IikgKwogIGxhYjA2X3RoZW1lCmBgYAoKTm93LCBsZXQncyB0cnkgYSBiZXR0ZXIgc3RhdGlzdGljYWwgbWV0aG9kLiBBZGQgYGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UpYCB0byB0aGUgZ3JhcGggYWJvdmUgLSB5b3VyIG91dHB1dCBzaG91bGQgbG9vayBzb21ldGhpbmcgbGlrZSB0aGUgZ3JhcGggYmVsb3cgKHlvdSBjYW4gZGVsZXRlIHRoZSBvdGhlciBsaW5lcyBpZiB5b3UgbGlrZSkuIExPRVNTIHN0YW5kcyBmb3IgTG9jYWxseSBFc3RpbWF0ZWQgU2NhdHRlcnBsb3QgU21vb3RoaW5nLiBUaGUgYmFzaWMgaWRlYSBpcyB0aGF0IGl0IHRha2VzIGEgbW92aW5nIHdpbmRvdyBhbmQgZHJhd3MgdGhlIGJlc3QgZml0IGxpbmUgZm9yIHRoYXQgd2luZG93LCBhbmQgdGhlbiBzbW9vdGhzIGFsbCB0aGUgd2luZG93cyB0b2dldGhlci4gCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBYMSwgeSA9IFgyKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gRkFMU0UpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHBvbHkoeCwgMiksIHNlID0gRkFMU0UsIGNvbCA9ICJsaWdodGdyYXkiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgYWxwaGEgPS4yLCBjb2wgPSAibGlnaHRncmF5IikgKwogIGxhYjA2X3RoZW1lCmBgYApXZSBjYW4gY2hhbmdlIHRoZSBzaXplIG9mIHRoZSBtb3Zpbmcgd2luZG93IGJ5IGNoYW5naW5nIHRoZSBgc3BhbmAgYXJndW1lbnQuIFNtYWxsZXIgYHNwYW5gcyBsZWFkIHRvIGJ1bXBpZXIgY3VydmVzLCBhbmQgbGFyZ2VyIGBzcGFuYHMgbGVhZCB0byBzbW9vdGhlciBmaXRzLiBUaGUgcGxvdCBiZWxvdyB1c2VzIGBzcGFuID0gMC4yYCwgYnV0IHlvdSBzaG91bGQgdHJ5IGEgZmV3IGRpZmZlcmVudCB2YWx1ZXMgb2YgYHNwYW5gOiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBYMSwgeSA9IFgyKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNwYW4gPSAuMiwgc2UgPSBGQUxTRSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAyKSwgc2UgPSBGQUxTRSwgY29sID0gImxpZ2h0Z3JheSIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBhbHBoYSA9LjIsIGNvbCA9ICJsaWdodGdyYXkiKSArCiAgbGFiMDZfdGhlbWUKYGBgCgpBbm90aGVyIG9wdGlvbiBpbiBgZ2VvbV9zbW9vdGhgIGlzIGBnYW1gLCB3aGljaCBzdGFuZHMgZm9yIEdlbmVyYWxpemVkIEFkZGl0aXZlIE1vZGVsLiBHQU1zIGFyZSBzdXBlciBmbGV4aWJsZSBidXQgc3RpbGwgaW50ZXJwcmV0YWJsZSAoYXMgY29tcGFyZWQgdG8gbW9yZSAiYmxhY2sgYm94IiBtZXRob2RzIG9mdGVuIHVzZWQgaW4gbWFjaGluZSBsZWFybmluZykuIFRoZXJlJ3MgYSBncmVhdCBmcmVlIGludGVyYWN0aXZlIGNvdXJzZSBvbiBbR0FNcyBpbiBSXShodHRwczovL25vYW1yb3NzLmdpdGh1Yi5pby9nYW1zLWluLXItY291cnNlLykgd2hpY2ggSSBoaWdobHkgcmVjb21tZW5kIGlmIHlvdSB3YW50IHRvIGxlYXJuIG1vcmUgYWJvdXQgdGhlbS4gQWRkIGBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2FtIiwgc2UgPSBGQUxTRSlgIHRvIHNlZSB3aGF0IGEgR0FNIGxvb2tzIGxpa2Ugb24gdGhpcyBncmFwaC4gCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBYMSwgeSA9IFgyKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnYW0iLCBzZSA9IEZBTFNFKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc3BhbiA9IC4yLCBzZSA9IEZBTFNFLCBjb2wgPSAibGlnaHRncmF5IikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAyKSwgc2UgPSBGQUxTRSwgY29sID0gImxpZ2h0Z3JheSIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBhbHBoYSA9LjIsIGNvbCA9ICJsaWdodGdyYXkiKSArCiAgbGFiMDZfdGhlbWUKYGBgCgpJZiB3ZSdyZSBhZGRpbmcgYSBidW5jaCBvZiBkaWZmZXJlbnQgZml0cyB0byB0aGUgZGF0YSwgaXQgd291bGQgYmUgaGVscGZ1bCBpZiBvdXIgbGVnZW5kIHNob3dlZCB3aGF0IHRoZXkgd2VyZSEgSGVyZSdzIGEgc25lYWt5IHdheSB0byBkZWZpbmUgYSBgY29sb3JgIGFlc3RoZXRpYyBhcyB3ZSBnbywgd2hlcmUgZWFjaCBvZiB0aGUgbGluZXMgaXMgYXNzaWduZWQgYSBjb2xvciB0aGF0J3MgZXF1YWwgdG8gdGhlIG1vZGVsIG5hbWUsIGFuZCB0aGVuIHdlIHJlbmFtZSB0aGUgYWVzdGhldGljICJNb2RlbCIgYXQgdGhlIGVuZCBpbiBvcmRlciB0byBtYWtlIHRoZSBsZWdlbmQuIAoKYGBge3J9CmdncGxvdChkYXRhLCBhZXMoeCA9IFgxLCB5ID0gWDIpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImdhbSIsIHNlID0gRkFMU0UsIGFlcyhjb2wgPSAiR0FNIikpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzcGFuID0gLjIsIHNlID0gRkFMU0UsIGFlcyhjb2wgPSAiTG9lc3MiKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAyKSwgc2UgPSBGQUxTRSwgYWVzKGNvbCA9ICJRdWFkcmF0aWMiKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGFscGhhID0uMiwgYWVzKGNvbCA9ICJPTFMiKSkgKwogIGxhYjA2X3RoZW1lICsgCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKCJNb2RlbCIsIGVuZCA9IC43NSwgb3B0aW9uID0gIkMiKSAKYGBgCgojIFBhcnQgMjogRGVuc2l0eSBQbG90cwoKTGlrZSB3ZSBzYXcgaW4gY2xhc3MgdG9kYXksIGRlbnNpdHkgcGxvdHMgYXJlIGNsb3NlbHkgcmVsYXRlZCB0byBoaXN0b2dyYW1zLCBhbmQgeW91J3ZlIGFsbCBzZWVuIGEgaGlzdG9ncmFtISBUaGVyZSdzIGEgZ3JlYXQgW0ludGVyYWN0aXZlIEVzc2F5XShodHRwczovL3RpbmxpenppZS5vcmcvaGlzdG9ncmFtcy8pIHlvdSBjYW4gY2hlY2sgb3V0IGlmIHlvdSBoYXZlIHRpbWUgdG8gc2VlIGhvdyBjb21wbGljYXRlZCBwdXR0aW5nIHRvZ2V0aGVyIGhpc3RvZ3JhbXMgcmVhbGx5IGlzIHVuZGVyIHRoZSBob29kLiAKCk9uZSBvZiB0aGUgbWFpbiBwcm9ibGVtcyB3aXRoIGhpc3RvZ3JhbXMgaXMgdGhlIGJ1bXBpbmVzcyBvZiB0aGUgYmFycy4gVGhpcyBpcyBvZnRlbiBiZWNhdXNlIHdlJ3JlIHBsb3R0aW5nIGEgKnNhbXBsZSosIHdoaWxlIGEgZGVuc2l0eSBlc3RpbWF0ZSBwcm92aWRlcyBhbiBlc3RpbWF0ZSBmb3IgdGhlICpwb3B1bGF0aW9uKi4gSGVyZSdzIGFuIGV4YW1wbGUgb2YgaG93IGRpZmZlcmVudCBzYW1wbGVzIGNhbiBsb29rIHdoZW4gdGhleSdyZSBkcmF3biBmcm9tIHRoZSBleGFjdCBzYW1lIGRpc3RyaWJ1dGlvbi4gVGhlIGBzZXQuc2VlZGAgZnVuY3Rpb24gbWFrZXMgc3VyZSB0aGF0IGlmIHlvdSBydW4gdGhpcyBzYW1lIGNvZGUgb24geW91ciBjb21wdXRlciwgeW91J2xsIGdldCB0aGUgZXhhY3Qgc2FtZSBzZXQgb2YgcmFuZG9tIHNhbXBsZXMuIFRoZSBgcm5vcm1gIGZ1bmN0aW9uIHRlbGxzIFIgdG8gZHJhdyAxMDAwIHJhbmRvbSBzYW1wbGVzIG9mIGEgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgdmFyaWFibGUsIGFuZCB0aGUgZmlyc3QgZmV3IGxpbmVzIG9mIGNvZGUgc2F2ZXMgdGhhdCBkYXRhIGluIGEgdGliYmxlIHdpdGggMTAgY29sdW1ucyAoY29ycmVzcG9uZGluZyB0byAxMCBzYW1wbGVzKSBhbmQgMTAwMCByb3dzIChjb3JyZXNwb25kaW5nIHRvIG9ic2VydmF0aW9ucykuIAoKYGBge3J9CnNldC5zZWVkKDAxMTIyMSkKc2FtcGxlcyA9IGRhdGEuZnJhbWUobWF0cml4KHJub3JtKDEwMDApLCBuY29sID0gMTApKQpjb2xuYW1lcyhzYW1wbGVzKSA9IHBhc3RlMCgic2FtcGxlIiwgMToxMCkKc2FtcGxlcyA9IHRpYmJsZShzYW1wbGVzKQpzYW1wbGVzICU+JQogIHBpdm90X2xvbmdlcigxOjEwKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHggPSB2YWx1ZSwgZmlsbCA9IG5hbWUpKSArIAogIGdlb21faGlzdG9ncmFtKCkgKyAKICBmYWNldF93cmFwKHZhcnMobmFtZSkpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjc1LCBvcHRpb24gPSAiQyIpICsgCiAgbGFiMDZfdGhlbWUgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKVGhlIGNvZGUgYmVsb3cgYWRkcyBhIGRlbnNpdHkgZXN0aW1hdGUgdG8gZWFjaCBvZiB0aGUgaGlzdG9ncmFtcy4gTm90ZSB0aGF0IEkgaGFkIHRvIGNoYW5nZSB0aGUgeSBhZXN0aGV0aWMgaW4gYGdlb21faGlzdG9ncmFtYCB0byBtYXAgdG8gYC4uZGVuc2l0eS4uYCByYXRoZXIgdGhhbiBgLi5jb3VudC4uYC4gVGhpcyBpcyBmb3IgdGhlIHByb2JhYmlsaXR5IHJlYXNvbnMgd2UgdGFsa2VkIGFib3V0IGluIGNsYXNzIHRvZGF5LCBzbyB0aGF0IHRoZSBhcmVhIHVuZGVyIHRoZSBkZW5zaXR5IGN1cnZlIGlzIGVxdWFsIHRvIG9uZS4gSGVyZSwgSSd2ZSBhZGRlZCBgYncgPSAuMWAgdG8gdGhlIGBnZW9tX2RlbnNpdHlgLCB3aGljaCByZXN1bHRzIGluIGEgcHJldHR5IGJ1bXB5IGN1cnZlLiBUcnkgcGxheWluZyBhcm91bmQgd2l0aCBkaWZmZXJlbnQgdmFsdWVzIG9mIGBid2AgdG8gZ2V0IGEgc21vb3RoZXIgY3VydmUgdGhhdCBsb29rcyByb3VnaGx5IHRoZSBzYW1lIGFjcm9zcyBhbGwgb2Ygb3VyIHNhbXBsZXMuIAoKYGBge3J9CnNhbXBsZXMgJT4lCiAgcGl2b3RfbG9uZ2VyKDE6MTApICU+JQogIGdncGxvdCguLCBhZXMoeCA9IHZhbHVlLCBmaWxsID0gbmFtZSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLikpICsgCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gLjMsIGJ3ID0gLjEsIGFlcyhjb2wgPSBuYW1lKSkgKwogIGZhY2V0X3dyYXAodmFycyhuYW1lKSkgKyAKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChlbmQgPSAuNzUsIG9wdGlvbiA9ICJDIikgKyAKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoZW5kID0gLjc1LCBvcHRpb24gPSAiQyIpICsgCiAgbGFiMDZfdGhlbWUgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKSnVzdCBsaWtlIGEgZGVuc2l0eSBwbG90IGlzIHJlbGF0ZWQgdG8gYSBoaXN0b2dyYW0sIGEgKip2aW9saW4gcGxvdCoqIGlzIHJlbGF0ZWQgdG8gYSAqKmJveCBwbG90KiouIEJlbG93IGlzIHRoZSBzYW1lIHNldCBvZiBzYW1wbGVzLCBidXQgcGxvdHRlZCB1c2luZyBib3hwbG90cyBpbnN0ZWFkIG9mIGhpc3RvZ3JhbXMuCgpgYGB7cn0Kc2FtcGxlcyAlPiUKICBwaXZvdF9sb25nZXIoMToxMCkgJT4lCiAgZ2dwbG90KC4sIGFlcyh5ID0gdmFsdWUsIHggPSBuYW1lLCBmaWxsID0gbmFtZSkpICsgCiAgZ2VvbV9ib3hwbG90KGFscGhhID0gLjcpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjc1LCBvcHRpb24gPSAiQyIpICsgCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGVuZCA9IC43NSwgb3B0aW9uID0gIkMiKSArIAogIGxhYjA2X3RoZW1lICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCk5vdywgYWRkIGBnZW9tX2ppdHRlcihhbHBoYSA9IFgsIGFlcyhjb2wgPSBuYW1lKSlgIHRvIHRoZSBjb2RlIGFib3ZlIHRvIHJlcGxpY2F0ZSB0aGUgZ3JhcGggYmVsb3cuIFBsYXkgYXJvdW5kIHdpdGggZGlmZmVyZW50IGBhbHBoYWAgdmFsdWVzIHRvIGdldCBzb21ldGhpbmcgdGhhdCBsb29rcyBuaWNlLiBUaGlzIGlzIGEgZ3JlYXQgZXhhbXBsZSBvZiBub3QgaGlkaW5nIHRoZSByYXcgZGF0YSBiZWhpbmQgYSBtb2RlbC4gCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpzYW1wbGVzICU+JQogIHBpdm90X2xvbmdlcigxOjEwKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHkgPSB2YWx1ZSwgeCA9IG5hbWUsIGZpbGwgPSBuYW1lKSkgKyAKICBnZW9tX2JveHBsb3QoYWxwaGE9IC43KSArIAogIGdlb21faml0dGVyKGFscGhhID0gLjUsIGFlcyhjb2wgPSBuYW1lKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC43NSwgb3B0aW9uID0gIkMiKSArIAogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChlbmQgPSAuNzUsIG9wdGlvbiA9ICJDIikgKyAKIGxhYjA2X3RoZW1lICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkZpbmFsbHksIGxldCdzIHJlcGxhY2UgYGdlb21fYm94cGxvdGAgd2l0aCBgZ2VvbV92aW9saW5gLiBBIHZpb2xpbiBwbG90IGlzIGp1c3QgYSBkZW5zaXR5IGVzdGltYXRlIHJvdGF0ZWQgdG8gYmUgb24gdGhlIG90aGVyIGF4aXMsIGFuZCB0aGVuIG1pcnJvcmVkIHRvIGNyZWF0ZSBhICJ2aW9saW4iIHNoYXBlLiBUaGlzIGdyYXBoIGlzIGEgbGl0dGxlICJidXN5IiBhbmQgSSdkIHdhbnQgdG8gdHdlYWsgaXQgYmVmb3JlIHB1Ymxpc2hpbmcsIGJ1dCBpdCBkb2VzIGEgbmljZSBqb2Igb2Ygc2hvd2luZyB0aGUgcmF3IGRhdGEgKGBnZW9tX2ppdHRlcmApIGFsb25nc2lkZSB0aGUgc3RhdGlzdGljYWwgbW9kZWwgKGBnZW9tX3Zpb2xpbmApLiAKCmBgYHtyLCBlY2hvID0gRkFMU0V9CnNhbXBsZXMgJT4lCiAgcGl2b3RfbG9uZ2VyKDE6MTApICU+JQogIGdncGxvdCguLCBhZXMoeSA9IHZhbHVlLCB4ID0gbmFtZSwgZmlsbCA9IG5hbWUpKSArIAogIGdlb21fdmlvbGluKGFscGhhPSAuNykgKyAKICBnZW9tX2ppdHRlcihhbHBoYSA9IC40LCBhZXMoY29sID0gbmFtZSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChlbmQgPSAuNzUsIG9wdGlvbiA9ICJDIikgKyAKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QoZW5kID0gLjc1LCBvcHRpb24gPSAiQyIpICsgCiAgbGFiMDZfdGhlbWUgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKRmluYWxseSwgaWYgd2UgaGF2ZSBtYW55IGRpc3RyaWJ1dGlvbnMgdGhhdCB3ZSB3YW50IHRvIGNvbXBhcmUgdG8gZWFjaCBvdGhlciwgc29tZXRpbWVzIGl0J3MgZWFzaWVzdCBpZiB3ZSBoYXZlIGEgc2luZ2xlIGF4ZXMgdG8gZGVhbCB3aXRoLiBFbnRlciBgZ2dyaWRnZXNgISAoWWVzLCB0aGlzIGlzIHlldCBhbm90aGVyIHBhY2thZ2UpCgpgYGB7cn0KbGlicmFyeShnZ3JpZGdlcykKc2FtcGxlcyAlPiUKICBwaXZvdF9sb25nZXIoMToxMCkgJT4lCiAgZ2dwbG90KC4sIGFlcyh5ID0gbmFtZSwgeCA9IHZhbHVlLCBmaWxsID0gbmFtZSkpICsgCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhbHBoYSA9IC44KSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjc1LCBvcHRpb24gPSAiQyIpICsgCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGVuZCA9IC43NSwgb3B0aW9uID0gIkMiKSArIAogIGxhYjA2X3RoZW1lICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMgUGFydCAzOiBPcGVuLWVuZGVkCgpSZW1pbmRlcjogaGVyZSB5b3UnbGwgdXNlIHRoZSBgZ2FwbWluZGVyYCBkYXRhOiAKCmBgYHtyfQpsaWJyYXJ5KGdhcG1pbmRlcikKaGVhZChnYXBtaW5kZXIpCmBgYAojIyBOb25saW5lYXIgc21vb3RoZXJzCgpGaW5kIHR3byB2YXJpYWJsZXMgZnJvbSB0aGUgYGdhcG1pbmRlcmAsIG1ha2UgYSBzY2F0dGVycGxvdCwgYWRkIGEgYGdlb21fc21vb3RoYCBsYXllciB3aXRoIGEgbGluZWFyIHJlbGF0aW9uc2hpcCBhbmQgYSBgZ2VvbV9zbW9vdGhgIGxheWVyIHdpdGggYSBub25saW5lYXIgcmVsYXRpb25zaGlwICh5b3VyIGNob2ljZSEpLiBXaGljaCBmaXRzIGJldHRlcj8gQWRkIGNvbG9ycyBhbmQgYSBsZWdlbmQuIAoKIyMgRGVuc2l0eSAKCkNob29zZSBhIHZhcmlhYmxlIChvciBjb21iaW5hdGlvbiBvZiB2YXJpYWJsZXMpIGZyb20gdGhlIGBnYXBtaW5kZXJgIGRhdGFzZXQgYW5kIG1ha2UgZWl0aGVyIGEga2VybmVsIGRlbnNpdHkgcGxvdCwgdmlvbGluIHBsb3QsIG9yIGRlbnNpdHkgcmlkZ2VzIHBsb3QuIFdyaXRlIDEtMiBzZW50ZW5jZXMgc3VtbWFyaXppbmcgeW91ciBmaW5kaW5ncy4gCgo=