Reminder: Libraries!

As always, we start our file with a code chunk that loads the libraries we will use throughout this document. Install any new libraries before running the code.

library(tidyverse) #used for visualization, summarization, and basic wrangling
library(ggridges) #New! Used for making density ridge plots
library(fueleconomy) #for the dataset

GOAL:

Expand on the previous section to investigate where variability in our response variable is coming from. We noticed that there was variability across vehicles’ highway mpg but didn’t investigate why. Are there other variables that might explain some of the variation? Which variables might have stronger or weaker relationships? How can we visualize these relationships?

Specifically, we will investigate relationship between:

Response/dependent/outcome variable: the variable whose variability we would like to explain (eg. hwy)

Predictors/explanatory variables/independent variables/covariates: variables that might explain some of the variability in the response (eg. displ)

We will construct visualizations that allow us to examine:

  • Relationship trends (form and direction of the relationship, eg. positive and linear) or differences across groups.

  • Relationship strength (degree of variability from the trend or how tight the points are around the envisioned trend)

  • Outliers in the relationship

By the end of these notes and activities, you should be able to do the following tasks.

  • Create a scatterplot to examine and relationship between two quantitative variables.
  • Describe the from, strength, and direction that you observe in a scatterplot.
  • Compute the correlation between two quantitative variables.
  • Create a side-by-side boxplot to examine the relationship between a categorical and quantitative variable and describe any meaningful differences among the groups.
  • Create a ridge plot to examine the relationship between a categorical and quantitative variable and describe any meaningful differences among the groups.
  • Understand when to use more advanced methods, like coloring a by a categorical variable, adding transparency, and faceting.

The data

We will again use a subset of the vehicles data from the fueleconomy dataset throughout. For a reminder of what is all in the dataset, ?vehicles in the console.

vehicles_2015 <- vehicles %>% filter(year == 2015)

Quantitative vs. quantitative: scatterplots

Examine the following plot that shows the relationship between hwy and displ (engine displacement). Comment on:

  • the form of the relationship (line/curve/other)
  • the strength of the relationship (weak/moderate/strong)
  • the direction of the relationship (positive/negative)
  • outliers
ggplot(data = vehicles_2015) +
  geom_point(aes(x = displ, y = hwy)) +
  labs(x = "Engine Displacement", 
       y = "Highway MPG") +
  theme_minimal()
## Warning: Removed 1 rows containing missing values (geom_point).

Notice that we got a warning letting us know that one data point was removed. It was removed because it had a missing value for displ. The car with a missing value is the Nissan Leaf. It has no engine displacement because it is an electric vehicle. There are a variety of ways we could try to fix this problem, but for now, we will create a new dataset that excludes any row with missing values (it turns out the Leaf is the only one with missing values anyways).

vehicles_2015_no_na <- vehicles_2015 %>% drop_na()
ggplot(data = vehicles_2015_no_na) +
  geom_point(aes(x = displ, y = hwy)) +
  labs(x = "Engine Displacement", 
       y = "Highway MPG") +
  theme_minimal()

The strength of the linear relationship between two quantitative variables is measured by the correlation coefficient. The code below will compute this.

vehicles_2015_no_na %>% 
  summarize(corr = cor(displ, hwy))

Notice if I tried to do this with the dataset with a missing value, it returns a missing value, NA. We can add an argument to the function to compute it for all pairs with no missing values.

vehicles_2015 %>% 
  summarize(corr = cor(displ, hwy),
            corr2 = cor(displ, hwy, use = "pairwise.complete.obs"))

YOUR TURN!

  1. Create a scatterplot of hwy vs. cty miles per gallon. How would you describe the relationship?
  2. Compute the correlation between hwy and cty.

More complex scatterplots

We can jitter the points, adding a little bit of noise to each point, in order to see if they are overplotted. Do you prefer this plot?

ggplot(data = vehicles_2015_no_na) +
  geom_jitter(aes(x = displ, y = hwy)) + #noice the code change here
  labs(x = "Engine Displacement", 
       y = "Highway MPG") +
  theme_minimal()

We can add an alpha argument, which controls the transparency of points. This can help if points are overplotted or if there is an overwhelming amount of data.

ggplot(data = vehicles_2015_no_na) +
  geom_point(aes(x = displ, y = hwy), alpha = .2) +
  labs(x = "Engine Displacement", 
       y = "Highway MPG") +
  theme_minimal()

We can make the points smaller in size. This can also help when there is a large amount of data.

ggplot(data = vehicles_2015_no_na) +
  geom_point(aes(x = displ, y = hwy), size = .5) +
  labs(x = "Engine Displacement", y = "Highway MPG") +
  theme_minimal()

With large datasets, you may want to use a combination of these options.

ggplot(data = vehicles %>% drop_na(displ)) +
  geom_jitter(aes(x = displ, y = hwy), size = .5, alpha = .2) +
  labs(x = "Engine Displacement", 
       y = "Highway MPG") +
  theme_minimal()

We also might be interested in the affect of a third variable while still wanting to look at the relationship between the two quantitative variables. If the third variable is categorical, we have a couple options.

We can color the points by that variable.

ggplot(data = vehicles_2015_no_na) +
  geom_jitter(aes(x = displ, y = hwy, color = drive)) +
  labs(x = "Engine Displacement", 
       y = "Highway MPG", 
       color = "Drive") +
  theme_minimal()

Or we can facet by that variable.

ggplot(data = vehicles_2015_no_na) +
  geom_jitter(aes(x = displ, y = hwy)) +
  facet_wrap(vars(drive)) +
  labs(x = "Engine Displacement", 
       y = "Highway MPG") +
  theme_minimal()

YOUR TURN!

Create 2-3 more complex plots that examine how some variables we have not investigated yet affect hwy.

Quantitative vs. categorical: side-by-side boxplots and density “ridge” plots

Examine the side-by-side boxplot below. What are some observations you would make? What does the boxplot show? What do each of the lines on the box represent? What information is missing?

ggplot(data = vehicles_2015_no_na) +
  geom_boxplot(aes(x = drive, y = hwy)) +
  labs(x = "Drive", 
       y = "Highway MPG") +
  theme_minimal()

We could also examine this relationship using density plots. Notice that the x and y axis are the opposite of what they were in the boxplot. What do you observe here? Anything you didn’t observe in the boxplot?

ggplot(data = vehicles_2015_no_na) +
  geom_density_ridges(aes(x = hwy, y = drive)) +
  labs(x = "Highway MPG", 
       y = "Drive") +
  theme_minimal()
## Picking joint bandwidth of 1.26

We may also want to compute some statistics about the response variable for each level of the categorical variable. We can use the group_by() function along with summarize() to do that. Explain what the following code does.

vehicles_2015_no_na %>% 
  group_by(drive) %>% 
  summarize(avg_hwy = mean(hwy),
            sd_hwy = sd(hwy),
            med_hwy = median(hwy),
            Q3 = quantile(hwy, probs = .75))

YOUR TURN!

  1. What are some pros and cons of the different methods for examining the relationship between a categorical and a quantitative variable? When would you use the different methods?
  2. Examine the relationship between hwy and another categorical variable in the dataset. Try some of the different methods we used above. Calculate some statistics (means, medians, sd, etc.) to describe how highway mpg differs across the categorical variable.
  3. Try using the large vehicles dataset with hwy as the response. Use year as a categorical variable. When you do that, you will need to use factor(year) so that R treats it as a categorical variable rather than a number. Answer the same questions as above.

A helpful table

This list is definitely not exhaustive but should help you make a good choice about which graphs will be useful in which scenarios.

For what? Graph ggplot geom hints
Distribution of quantitative Histogram geom_histogram(aes(x = quantitative variable))
Distribution of quantitative Density plot geom_density(aes(x = quantitative variable))
Distribution of categorical Barplot geom_bar(aes(x = categorical variable)) (or on y axis)
Distribution of categorical (if you already know the counts) Barplot geom_col(aes(x = categorical variable, y = count variable)) (or flip the axes)
Relationship between quant. response and quant. predictor Scatterplot geom_point(aes(x = quant. predictor, y = quant. response))
Relationship between quant. response and cat. predictor side-by-side boxplots geom_boxplot(aes(x = cat. predictor, y = quant. response)) (or flip the axes)
Relationship between quant. response and cat. predictor density ridge plots geom_density_ridges(aes(x = quant. response, y = cat. predictor))

So many more plots!

We have focused on the plots we will need most to better understand the data that we will use to create linear models. We will continue to use these plots, and we will add to them as needed. I also encourage you to explore more on your own. The esquisse package is especially helpful when you are first starting your explorations.

Other resources

  • You can watch a short video of me applying some of these functions to the penguins dataset. You can download the files to follow along below.
LS0tCnRpdGxlOiAnV29ya2luZyB3aXRoIG1vcmUgdGhhbiBvbmUgdmFyaWFibGU6IFxuIG11bHRpdmFyaWF0ZSByZWxhdGlvbnNoaXBzJwpvdXRwdXQ6ICAgCiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvciA9IFRSVUUpCmBgYAoKIyMgUmVtaW5kZXI6IExpYnJhcmllcyEKCkFzIGFsd2F5cywgd2Ugc3RhcnQgb3VyIGZpbGUgd2l0aCBhIGNvZGUgY2h1bmsgdGhhdCBsb2FkcyB0aGUgbGlicmFyaWVzIHdlIHdpbGwgdXNlIHRocm91Z2hvdXQgdGhpcyBkb2N1bWVudC4gSW5zdGFsbCBhbnkgbmV3IGxpYnJhcmllcyBiZWZvcmUgcnVubmluZyB0aGUgY29kZS4KCmBgYHtyIGxpYnJhcmllcywgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpICN1c2VkIGZvciB2aXN1YWxpemF0aW9uLCBzdW1tYXJpemF0aW9uLCBhbmQgYmFzaWMgd3JhbmdsaW5nCmxpYnJhcnkoZ2dyaWRnZXMpICNOZXchIFVzZWQgZm9yIG1ha2luZyBkZW5zaXR5IHJpZGdlIHBsb3RzCmxpYnJhcnkoZnVlbGVjb25vbXkpICNmb3IgdGhlIGRhdGFzZXQKYGBgCgoKPGNlbnRlcj4KIVtdKGltYWdlcy9tb2Rlcm5kaXZlX2Zsb3djaGFydF9leHBsb3JlLnBuZyl7d2lkdGg9NjAwcHh9CjwvY2VudGVyPgoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtc3VjY2VzcyI+CiAgPHN0cm9uZz5HT0FMOjwvc3Ryb25nPgoKRXhwYW5kIG9uIHRoZSBwcmV2aW91cyBzZWN0aW9uIHRvIGludmVzdGlnYXRlIHdoZXJlIHZhcmlhYmlsaXR5IGluIG91ciAqKnJlc3BvbnNlIHZhcmlhYmxlKiogaXMgY29taW5nIGZyb20uIFdlIG5vdGljZWQgdGhhdCB0aGVyZSB3YXMgdmFyaWFiaWxpdHkgYWNyb3NzIHZlaGljbGVzJyBoaWdod2F5IG1wZyBidXQgZGlkbid0IGludmVzdGlnYXRlICp3aHkqLiBBcmUgdGhlcmUgb3RoZXIgdmFyaWFibGVzIHRoYXQgbWlnaHQgZXhwbGFpbiBzb21lIG9mIHRoZSB2YXJpYXRpb24/IFdoaWNoIHZhcmlhYmxlcyBtaWdodCBoYXZlIHN0cm9uZ2VyIG9yIHdlYWtlciByZWxhdGlvbnNoaXBzPyBIb3cgY2FuIHdlIHZpc3VhbGl6ZSB0aGVzZSByZWxhdGlvbnNoaXBzPwoKU3BlY2lmaWNhbGx5LCB3ZSB3aWxsIGludmVzdGlnYXRlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuOgoKKipSZXNwb25zZS9kZXBlbmRlbnQvb3V0Y29tZSB2YXJpYWJsZSoqOiB0aGUgdmFyaWFibGUgd2hvc2UgdmFyaWFiaWxpdHkgd2Ugd291bGQgbGlrZSB0byBleHBsYWluIChlZy4gYGh3eWApCgoqKlByZWRpY3RvcnMvZXhwbGFuYXRvcnkgdmFyaWFibGVzL2luZGVwZW5kZW50IHZhcmlhYmxlcy9jb3ZhcmlhdGVzKio6IHZhcmlhYmxlcyB0aGF0IG1pZ2h0IGV4cGxhaW4gc29tZSBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIHJlc3BvbnNlIChlZy4gYGRpc3BsYCkKCldlIHdpbGwgY29uc3RydWN0IHZpc3VhbGl6YXRpb25zIHRoYXQgYWxsb3cgdXMgdG8gZXhhbWluZToKCiogUmVsYXRpb25zaGlwICp0cmVuZHMqIChmb3JtIGFuZCBkaXJlY3Rpb24gb2YgdGhlIHJlbGF0aW9uc2hpcCwgZWcuIHBvc2l0aXZlIGFuZCBsaW5lYXIpIG9yIGRpZmZlcmVuY2VzIGFjcm9zcyBncm91cHMuCiogUmVsYXRpb25zaGlwICpzdHJlbmd0aCogKGRlZ3JlZSBvZiB2YXJpYWJpbGl0eSBmcm9tIHRoZSB0cmVuZCBvciBob3cgdGlnaHQgdGhlIHBvaW50cyBhcmUgYXJvdW5kIHRoZSBlbnZpc2lvbmVkIHRyZW5kKSAgICAKCiogKk91dGxpZXJzKiBpbiB0aGUgcmVsYXRpb25zaGlwCgpCeSB0aGUgZW5kIG9mIHRoZXNlIG5vdGVzIGFuZCBhY3Rpdml0aWVzLCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gZG8gdGhlIGZvbGxvd2luZyB0YXNrcy4KCiogQ3JlYXRlIGEgc2NhdHRlcnBsb3QgdG8gZXhhbWluZSBhbmQgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuICAKKiBEZXNjcmliZSB0aGUgZnJvbSwgc3RyZW5ndGgsIGFuZCBkaXJlY3Rpb24gdGhhdCB5b3Ugb2JzZXJ2ZSBpbiBhIHNjYXR0ZXJwbG90LiAgCiogQ29tcHV0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0d28gcXVhbnRpdGF0aXZlIHZhcmlhYmxlcy4gIAoqIENyZWF0ZSBhIHNpZGUtYnktc2lkZSBib3hwbG90IHRvIGV4YW1pbmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgY2F0ZWdvcmljYWwgYW5kIHF1YW50aXRhdGl2ZSB2YXJpYWJsZSBhbmQgZGVzY3JpYmUgYW55IG1lYW5pbmdmdWwgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIGdyb3Vwcy4gIAoqIENyZWF0ZSBhIHJpZGdlIHBsb3QgdG8gZXhhbWluZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSBjYXRlZ29yaWNhbCBhbmQgcXVhbnRpdGF0aXZlIHZhcmlhYmxlIGFuZCBkZXNjcmliZSBhbnkgbWVhbmluZ2Z1bCBkaWZmZXJlbmNlcyBhbW9uZyB0aGUgZ3JvdXBzLiAgCiogVW5kZXJzdGFuZCB3aGVuIHRvIHVzZSBtb3JlIGFkdmFuY2VkIG1ldGhvZHMsIGxpa2UgY29sb3JpbmcgYSBieSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCBhZGRpbmcgdHJhbnNwYXJlbmN5LCBhbmQgZmFjZXRpbmcuCgo8L2Rpdj4KCiMjIFRoZSBkYXRhCgpXZSB3aWxsIGFnYWluIHVzZSBhIHN1YnNldCBvZiB0aGUgYHZlaGljbGVzYCBkYXRhIGZyb20gdGhlIGBmdWVsZWNvbm9teWAgZGF0YXNldCB0aHJvdWdob3V0LiBGb3IgYSByZW1pbmRlciBvZiB3aGF0IGlzIGFsbCBpbiB0aGUgZGF0YXNldCwgYD92ZWhpY2xlc2AgaW4gdGhlIGNvbnNvbGUuCgpgYGB7cn0KdmVoaWNsZXNfMjAxNSA8LSB2ZWhpY2xlcyAlPiUgZmlsdGVyKHllYXIgPT0gMjAxNSkKYGBgCgojIyBRdWFudGl0YXRpdmUgdnMuIHF1YW50aXRhdGl2ZTogc2NhdHRlcnBsb3RzIAoKRXhhbWluZSB0aGUgZm9sbG93aW5nIHBsb3QgdGhhdCBzaG93cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYGh3eWAgYW5kIGBkaXNwbGAgKGVuZ2luZSBkaXNwbGFjZW1lbnQpLiBDb21tZW50IG9uOgogICAgICAgIAoqIHRoZSBmb3JtIG9mIHRoZSByZWxhdGlvbnNoaXAgKGxpbmUvY3VydmUvb3RoZXIpCiogdGhlIHN0cmVuZ3RoIG9mIHRoZSByZWxhdGlvbnNoaXAgKHdlYWsvbW9kZXJhdGUvc3Ryb25nKQoqIHRoZSBkaXJlY3Rpb24gb2YgdGhlIHJlbGF0aW9uc2hpcCAocG9zaXRpdmUvbmVnYXRpdmUpCiogb3V0bGllcnMgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSB2ZWhpY2xlc18yMDE1KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKwogIGxhYnMoeCA9ICJFbmdpbmUgRGlzcGxhY2VtZW50IiwgCiAgICAgICB5ID0gIkhpZ2h3YXkgTVBHIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCk5vdGljZSB0aGF0IHdlIGdvdCBhIHdhcm5pbmcgbGV0dGluZyB1cyBrbm93IHRoYXQgb25lIGRhdGEgcG9pbnQgd2FzIHJlbW92ZWQuIEl0IHdhcyByZW1vdmVkIGJlY2F1c2UgaXQgaGFkIGEgbWlzc2luZyB2YWx1ZSBmb3IgYGRpc3BsYC4gVGhlIGNhciB3aXRoIGEgbWlzc2luZyB2YWx1ZSBpcyB0aGUgTmlzc2FuIExlYWYuIEl0IGhhcyBubyBlbmdpbmUgZGlzcGxhY2VtZW50IGJlY2F1c2UgaXQgaXMgYW4gZWxlY3RyaWMgdmVoaWNsZS4gVGhlcmUgYXJlIGEgdmFyaWV0eSBvZiB3YXlzIHdlIGNvdWxkIHRyeSB0byBmaXggdGhpcyBwcm9ibGVtLCBidXQgZm9yIG5vdywgd2Ugd2lsbCBjcmVhdGUgYSBuZXcgZGF0YXNldCB0aGF0IGV4Y2x1ZGVzIGFueSByb3cgd2l0aCBtaXNzaW5nIHZhbHVlcyAoaXQgdHVybnMgb3V0IHRoZSBMZWFmIGlzIHRoZSBvbmx5IG9uZSB3aXRoIG1pc3NpbmcgdmFsdWVzIGFueXdheXMpLgoKYGBge3J9CnZlaGljbGVzXzIwMTVfbm9fbmEgPC0gdmVoaWNsZXNfMjAxNSAlPiUgZHJvcF9uYSgpCmBgYAoKYGBge3J9CmdncGxvdChkYXRhID0gdmVoaWNsZXNfMjAxNV9ub19uYSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsKICBsYWJzKHggPSAiRW5naW5lIERpc3BsYWNlbWVudCIsIAogICAgICAgeSA9ICJIaWdod2F5IE1QRyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpUaGUgc3RyZW5ndGggb2YgdGhlICpsaW5lYXIqIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBxdWFudGl0YXRpdmUgdmFyaWFibGVzIGlzIG1lYXN1cmVkIGJ5IHRoZSAqKmNvcnJlbGF0aW9uIGNvZWZmaWNpZW50KiouIFRoZSBjb2RlIGJlbG93IHdpbGwgY29tcHV0ZSB0aGlzLgoKYGBge3J9CnZlaGljbGVzXzIwMTVfbm9fbmEgJT4lIAogIHN1bW1hcml6ZShjb3JyID0gY29yKGRpc3BsLCBod3kpKQpgYGAKCk5vdGljZSBpZiBJIHRyaWVkIHRvIGRvIHRoaXMgd2l0aCB0aGUgZGF0YXNldCB3aXRoIGEgbWlzc2luZyB2YWx1ZSwgaXQgcmV0dXJucyBhIG1pc3NpbmcgdmFsdWUsIGBOQWAuIFdlIGNhbiBhZGQgYW4gYXJndW1lbnQgdG8gdGhlIGZ1bmN0aW9uIHRvIGNvbXB1dGUgaXQgZm9yIGFsbCBwYWlycyB3aXRoIG5vIG1pc3NpbmcgdmFsdWVzLgoKYGBge3J9CnZlaGljbGVzXzIwMTUgJT4lIAogIHN1bW1hcml6ZShjb3JyID0gY29yKGRpc3BsLCBod3kpLAogICAgICAgICAgICBjb3JyMiA9IGNvcihkaXNwbCwgaHd5LCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikpCmBgYAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CiAgPHN0cm9uZz5ZT1VSIFRVUk4hPC9zdHJvbmc+IAoKMS4gQ3JlYXRlIGEgc2NhdHRlcnBsb3Qgb2YgKmh3eSogdnMuICpjdHkqIG1pbGVzIHBlciBnYWxsb24uIEhvdyB3b3VsZCB5b3UgZGVzY3JpYmUgdGhlIHJlbGF0aW9uc2hpcD8gIAoyLiBDb21wdXRlIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuICpod3kqIGFuZCAqY3R5Ki4KCjwvZGl2PgoKIyMjIE1vcmUgY29tcGxleCBzY2F0dGVycGxvdHMKCldlIGNhbiBqaXR0ZXIgdGhlIHBvaW50cywgYWRkaW5nIGEgbGl0dGxlIGJpdCBvZiBub2lzZSB0byBlYWNoIHBvaW50LCBpbiBvcmRlciB0byBzZWUgaWYgdGhleSBhcmUgb3ZlcnBsb3R0ZWQuIERvIHlvdSBwcmVmZXIgdGhpcyBwbG90PwoKYGBge3J9CmdncGxvdChkYXRhID0gdmVoaWNsZXNfMjAxNV9ub19uYSkgKwogIGdlb21faml0dGVyKGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArICNub2ljZSB0aGUgY29kZSBjaGFuZ2UgaGVyZQogIGxhYnMoeCA9ICJFbmdpbmUgRGlzcGxhY2VtZW50IiwgCiAgICAgICB5ID0gIkhpZ2h3YXkgTVBHIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCldlIGNhbiBhZGQgYW4gYGFscGhhYCBhcmd1bWVudCwgd2hpY2ggY29udHJvbHMgdGhlIHRyYW5zcGFyZW5jeSBvZiBwb2ludHMuIFRoaXMgY2FuIGhlbHAgaWYgcG9pbnRzIGFyZSBvdmVycGxvdHRlZCBvciBpZiB0aGVyZSBpcyBhbiBvdmVyd2hlbG1pbmcgYW1vdW50IG9mIGRhdGEuIAoKYGBge3J9CmdncGxvdChkYXRhID0gdmVoaWNsZXNfMjAxNV9ub19uYSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkaXNwbCwgeSA9IGh3eSksIGFscGhhID0gLjIpICsKICBsYWJzKHggPSAiRW5naW5lIERpc3BsYWNlbWVudCIsIAogICAgICAgeSA9ICJIaWdod2F5IE1QRyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpXZSBjYW4gbWFrZSB0aGUgcG9pbnRzIHNtYWxsZXIgaW4gc2l6ZS4gVGhpcyBjYW4gYWxzbyBoZWxwIHdoZW4gdGhlcmUgaXMgYSBsYXJnZSBhbW91bnQgb2YgZGF0YS4gCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSB2ZWhpY2xlc18yMDE1X25vX25hKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSwgc2l6ZSA9IC41KSArCiAgbGFicyh4ID0gIkVuZ2luZSBEaXNwbGFjZW1lbnQiLCB5ID0gIkhpZ2h3YXkgTVBHIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCldpdGggbGFyZ2UgZGF0YXNldHMsIHlvdSBtYXkgd2FudCB0byB1c2UgYSBjb21iaW5hdGlvbiBvZiB0aGVzZSBvcHRpb25zLgoKYGBge3J9CmdncGxvdChkYXRhID0gdmVoaWNsZXMgJT4lIGRyb3BfbmEoZGlzcGwpKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHggPSBkaXNwbCwgeSA9IGh3eSksIHNpemUgPSAuNSwgYWxwaGEgPSAuMikgKwogIGxhYnMoeCA9ICJFbmdpbmUgRGlzcGxhY2VtZW50IiwgCiAgICAgICB5ID0gIkhpZ2h3YXkgTVBHIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCldlIGFsc28gbWlnaHQgYmUgaW50ZXJlc3RlZCBpbiB0aGUgYWZmZWN0IG9mIGEgdGhpcmQgdmFyaWFibGUgd2hpbGUgc3RpbGwgd2FudGluZyB0byBsb29rIGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuIElmIHRoZSB0aGlyZCB2YXJpYWJsZSBpcyBjYXRlZ29yaWNhbCwgd2UgaGF2ZSBhIGNvdXBsZSBvcHRpb25zLiAKCldlIGNhbiBjb2xvciB0aGUgcG9pbnRzIGJ5IHRoYXQgdmFyaWFibGUuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSB2ZWhpY2xlc18yMDE1X25vX25hKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSBkcml2ZSkpICsKICBsYWJzKHggPSAiRW5naW5lIERpc3BsYWNlbWVudCIsIAogICAgICAgeSA9ICJIaWdod2F5IE1QRyIsIAogICAgICAgY29sb3IgPSAiRHJpdmUiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKT3Igd2UgY2FuIGZhY2V0IGJ5IHRoYXQgdmFyaWFibGUuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSB2ZWhpY2xlc18yMDE1X25vX25hKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsKICBmYWNldF93cmFwKHZhcnMoZHJpdmUpKSArCiAgbGFicyh4ID0gIkVuZ2luZSBEaXNwbGFjZW1lbnQiLCAKICAgICAgIHkgPSAiSGlnaHdheSBNUEciKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CiAgPHN0cm9uZz5ZT1VSIFRVUk4hPC9zdHJvbmc+IAoKQ3JlYXRlIDItMyBtb3JlIGNvbXBsZXggcGxvdHMgdGhhdCBleGFtaW5lIGhvdyBzb21lIHZhcmlhYmxlcyB3ZSBoYXZlIG5vdCBpbnZlc3RpZ2F0ZWQgeWV0IGFmZmVjdCAqaHd5Ki4gCgo8L2Rpdj4KCiMjIFF1YW50aXRhdGl2ZSB2cy4gY2F0ZWdvcmljYWw6IHNpZGUtYnktc2lkZSBib3hwbG90cyBhbmQgZGVuc2l0eSAicmlkZ2UiIHBsb3RzCgpFeGFtaW5lIHRoZSBzaWRlLWJ5LXNpZGUgYm94cGxvdCBiZWxvdy4gV2hhdCBhcmUgc29tZSBvYnNlcnZhdGlvbnMgeW91IHdvdWxkIG1ha2U/IFdoYXQgZG9lcyB0aGUgYm94cGxvdCBzaG93PyBXaGF0IGRvIGVhY2ggb2YgdGhlIGxpbmVzIG9uIHRoZSBib3ggcmVwcmVzZW50PyBXaGF0IGluZm9ybWF0aW9uIGlzIG1pc3Npbmc/CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSB2ZWhpY2xlc18yMDE1X25vX25hKSArCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gZHJpdmUsIHkgPSBod3kpKSArCiAgbGFicyh4ID0gIkRyaXZlIiwgCiAgICAgICB5ID0gIkhpZ2h3YXkgTVBHIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCldlIGNvdWxkIGFsc28gZXhhbWluZSB0aGlzIHJlbGF0aW9uc2hpcCB1c2luZyBkZW5zaXR5IHBsb3RzLiBOb3RpY2UgdGhhdCB0aGUgeCBhbmQgeSBheGlzIGFyZSB0aGUgb3Bwb3NpdGUgb2Ygd2hhdCB0aGV5IHdlcmUgaW4gdGhlIGJveHBsb3QuIFdoYXQgZG8geW91IG9ic2VydmUgaGVyZT8gQW55dGhpbmcgeW91IGRpZG4ndCBvYnNlcnZlIGluIHRoZSBib3hwbG90PwoKYGBge3J9CmdncGxvdChkYXRhID0gdmVoaWNsZXNfMjAxNV9ub19uYSkgKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoYWVzKHggPSBod3ksIHkgPSBkcml2ZSkpICsKICBsYWJzKHggPSAiSGlnaHdheSBNUEciLCAKICAgICAgIHkgPSAiRHJpdmUiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKV2UgbWF5IGFsc28gd2FudCB0byBjb21wdXRlIHNvbWUgc3RhdGlzdGljcyBhYm91dCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgZm9yIGVhY2ggbGV2ZWwgb2YgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBXZSBjYW4gdXNlIHRoZSBgZ3JvdXBfYnkoKWAgZnVuY3Rpb24gYWxvbmcgd2l0aCBgc3VtbWFyaXplKClgIHRvIGRvIHRoYXQuIEV4cGxhaW4gd2hhdCB0aGUgZm9sbG93aW5nIGNvZGUgZG9lcy4KCmBgYHtyfQp2ZWhpY2xlc18yMDE1X25vX25hICU+JSAKICBncm91cF9ieShkcml2ZSkgJT4lIAogIHN1bW1hcml6ZShhdmdfaHd5ID0gbWVhbihod3kpLAogICAgICAgICAgICBzZF9od3kgPSBzZChod3kpLAogICAgICAgICAgICBtZWRfaHd5ID0gbWVkaWFuKGh3eSksCiAgICAgICAgICAgIFEzID0gcXVhbnRpbGUoaHd5LCBwcm9icyA9IC43NSkpCmBgYAoKPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+CiAgPHN0cm9uZz5ZT1VSIFRVUk4hPC9zdHJvbmc+IAoKICAxLiBXaGF0IGFyZSBzb21lIHByb3MgYW5kIGNvbnMgb2YgdGhlIGRpZmZlcmVudCBtZXRob2RzIGZvciBleGFtaW5pbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgY2F0ZWdvcmljYWwgYW5kIGEgcXVhbnRpdGF0aXZlIHZhcmlhYmxlPyBXaGVuIHdvdWxkIHlvdSB1c2UgdGhlIGRpZmZlcmVudCBtZXRob2RzPyAKICAyLiBFeGFtaW5lIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgaHd5YCBhbmQgYW5vdGhlciBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpbiB0aGUgZGF0YXNldC4gVHJ5IHNvbWUgb2YgdGhlIGRpZmZlcmVudCBtZXRob2RzIHdlIHVzZWQgYWJvdmUuIENhbGN1bGF0ZSBzb21lIHN0YXRpc3RpY3MgKG1lYW5zLCBtZWRpYW5zLCBzZCwgZXRjLikgdG8gZGVzY3JpYmUgaG93IGhpZ2h3YXkgbXBnIGRpZmZlcnMgYWNyb3NzIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4KICAzLiBUcnkgdXNpbmcgdGhlIGxhcmdlIGB2ZWhpY2xlc2AgZGF0YXNldCB3aXRoIGBod3lgIGFzIHRoZSByZXNwb25zZS4gVXNlIGB5ZWFyYCBhcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBXaGVuIHlvdSBkbyB0aGF0LCB5b3Ugd2lsbCBuZWVkIHRvIHVzZSBgZmFjdG9yKHllYXIpYCBzbyB0aGF0IFIgdHJlYXRzIGl0IGFzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgcmF0aGVyIHRoYW4gYSBudW1iZXIuIEFuc3dlciB0aGUgc2FtZSBxdWVzdGlvbnMgYXMgYWJvdmUuCgojIyBBIGhlbHBmdWwgdGFibGUKClRoaXMgbGlzdCBpcyBkZWZpbml0ZWx5IG5vdCBleGhhdXN0aXZlIGJ1dCBzaG91bGQgaGVscCB5b3UgbWFrZSBhIGdvb2QgY2hvaWNlIGFib3V0IHdoaWNoIGdyYXBocyB3aWxsIGJlIHVzZWZ1bCBpbiB3aGljaCBzY2VuYXJpb3MuCgpGb3Igd2hhdD8gfCBHcmFwaCB8YGdncGxvdGAgYGdlb21gIGhpbnRzCi0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0KRGlzdHJpYnV0aW9uIG9mIHF1YW50aXRhdGl2ZSB8IEhpc3RvZ3JhbSB8IGBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHF1YW50aXRhdGl2ZSB2YXJpYWJsZSkpYCAKRGlzdHJpYnV0aW9uIG9mIHF1YW50aXRhdGl2ZSB8IERlbnNpdHkgcGxvdCB8IGBnZW9tX2RlbnNpdHkoYWVzKHggPSBxdWFudGl0YXRpdmUgdmFyaWFibGUpKWAgCkRpc3RyaWJ1dGlvbiBvZiBjYXRlZ29yaWNhbCB8IEJhcnBsb3QgfCBgZ2VvbV9iYXIoYWVzKHggPSBjYXRlZ29yaWNhbCB2YXJpYWJsZSkpYCAgKG9yIG9uIHkgYXhpcykKRGlzdHJpYnV0aW9uIG9mIGNhdGVnb3JpY2FsIChpZiB5b3UgYWxyZWFkeSBrbm93IHRoZSBjb3VudHMpIHwgQmFycGxvdCB8IGBnZW9tX2NvbChhZXMoeCA9IGNhdGVnb3JpY2FsIHZhcmlhYmxlLCB5ID0gY291bnQgdmFyaWFibGUpKWAgKG9yIGZsaXAgdGhlIGF4ZXMpClJlbGF0aW9uc2hpcCBiZXR3ZWVuIHF1YW50LiByZXNwb25zZSBhbmQgcXVhbnQuIHByZWRpY3RvciB8IFNjYXR0ZXJwbG90fCBgZ2VvbV9wb2ludChhZXMoeCA9IHF1YW50LiBwcmVkaWN0b3IsIHkgPSBxdWFudC4gcmVzcG9uc2UpKWAgICAKUmVsYXRpb25zaGlwIGJldHdlZW4gcXVhbnQuIHJlc3BvbnNlIGFuZCBjYXQuIHByZWRpY3RvciB8IHNpZGUtYnktc2lkZSBib3hwbG90cyB8IGBnZW9tX2JveHBsb3QoYWVzKHggPSBjYXQuIHByZWRpY3RvciwgeSA9IHF1YW50LiByZXNwb25zZSkpYCAob3IgZmxpcCB0aGUgYXhlcykKUmVsYXRpb25zaGlwIGJldHdlZW4gcXVhbnQuIHJlc3BvbnNlIGFuZCBjYXQuIHByZWRpY3RvciB8IGRlbnNpdHkgcmlkZ2UgcGxvdHMgfCBgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhZXMoeCA9IHF1YW50LiByZXNwb25zZSwgeSA9IGNhdC4gcHJlZGljdG9yKSlgCgojIyBTbyBtYW55IG1vcmUgcGxvdHMhIAoKV2UgaGF2ZSBmb2N1c2VkIG9uIHRoZSBwbG90cyB3ZSB3aWxsIG5lZWQgbW9zdCB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgZGF0YSB0aGF0IHdlIHdpbGwgdXNlIHRvIGNyZWF0ZSBsaW5lYXIgbW9kZWxzLiBXZSB3aWxsIGNvbnRpbnVlIHRvIHVzZSB0aGVzZSBwbG90cywgYW5kIHdlIHdpbGwgYWRkIHRvIHRoZW0gYXMgbmVlZGVkLiBJIGFsc28gZW5jb3VyYWdlIHlvdSB0byBleHBsb3JlIG1vcmUgb24geW91ciBvd24uIFRoZSBgZXNxdWlzc2VgIHBhY2thZ2UgaXMgZXNwZWNpYWxseSBoZWxwZnVsIHdoZW4geW91IGFyZSBmaXJzdCBzdGFydGluZyB5b3VyIGV4cGxvcmF0aW9ucy4KCiMjIE90aGVyIHJlc291cmNlcwoKKiBZb3UgY2FuIHdhdGNoIGEgW3Nob3J0IHZpZGVvXShodHRwczovL3lvdXR1LmJlL3lmSXAwQldrcFUwKSBvZiBtZSBhcHBseWluZyBzb21lIG9mIHRoZXNlIGZ1bmN0aW9ucyB0byB0aGUgYHBlbmd1aW5zYCBkYXRhc2V0LiBZb3UgY2FuIGRvd25sb2FkIHRoZSBmaWxlcyB0byBmb2xsb3cgYWxvbmcgYmVsb3cuIAoKYGBge3IsIGVjaG89RkFMU0V9CmxpYnJhcnkoZG93bmxvYWR0aGlzKQpkb3dubG9hZF9maWxlKAogIHBhdGggPSAiLi4vZGVtb3MvMDFfbXVsdGl2YXJpYXRlX25vX2NvZGUuUm1kIiwKICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgZGVtbyBmaWxlICh3aXRob3V0IGNvZGUpIiwKICBidXR0b25fdHlwZSA9ICJ3YXJuaW5nIiwKICBoYXNfaWNvbiA9IFRSVUUsCiAgaWNvbiA9ICJmYSBmYS1zYXZlIiwKICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFCikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KZG93bmxvYWRfZmlsZSgKICBwYXRoID0gIi4uL2RlbW9zLzAxX211bHRpdmFyaWF0ZV9jb2RlLlJtZCIsCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGRlbW8gZmlsZSAod2l0aCBjb2RlKSIsCiAgYnV0dG9uX3R5cGUgPSAiaW5mbyIsCiAgaGFzX2ljb24gPSBUUlVFLAogIGljb24gPSAiZmEgZmEtc2F2ZSIsCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQopCmBgYA==