Chapter 5 Exploratory Data Analysis
EDA is an iterative cycle where you: 1. Generate questions about your data 2. Search for answers by visualizing, transforming and modeling your data 3. Use what you learn to refine your questions and/or generate new questions
The two types of questions: 1. What type of variations occurs within each variable? 2. What type of covariation occurs between variables ?
Definitions: Variable is a quantity or quality or property that you can measure Value is the state of the variable when you measure it. Observation or a case , is a set of measurements made under similar conditions Tabular data is a set of values, each associated with a avraible and an observation. Variation is the tendency of the values of a variable to change from measurement to measurement
Visualizing Distributions
rr library(tidyverse) ggplot(data = diamonds)+ geom_bar(mapping = aes(x=cut))

An example for a categorical variable (usually saved in R as factors)
rr diamonds %>% count(cut)
As example for continuous variable (better use a histogram)
rr ggplot(data = diamonds) + geom_histogram(mapping = aes(x = carat), binwidth =0.5)

You can compute by hand by combining dplyr::count() and ggplot2::cut_width()
rr diamonds %>% count(cut_width(carat, 0.5))
If you want just the diamond with a size of less than three carats and choose a small binwidth:
rr smaller <- diamonds %>% filter(carat < 3) ggplot(data = smaller,mapping=aes(x=carat))+ geom_histogram(binwidth = 0.1)

You can also use geom_freqpoly to use lines instead
rr ggplot(data = smaller,mapping = aes(x = carat, color=cut)) + geom_freqpoly(binwidth = 0.1)

Typical Values
In both bar charts and histograms, tall bars shows the common values, and shorter bars shows the less-common values. Places that do not have bars reveal values that were not seen in your data /br>
rr ggplot(data = faithful, mapping = aes(x = eruptions)) + geom_histogram(binwidth = 0.25)

Unusual Values
Sometimes the only evidence of outliers is the unusually wide limits on the y-axis
rr ggplot(diamonds)+ geom_histogram(mapping = aes(x = y), binwidth = 0.5)

The values may be so small that you barely see it. To make it pop up use a lower coordinate system
rr ggplot(diamonds) + geom_histogram(mapping = aes(x = y), binwidth = 0.5) + coord_cartesian(ylim = c(0,50))

rr unusual <- diamonds %>% filter(y < 3 | y > 20) %>% arrange(y) # display values unusual
Missing Values
You have two options to work with outliers: 1. drop the row with the outliers
rr diamonds2 <- diamonds %>% filter(between(y,3,20)) diamonds2
or : 2. replace the unusal values with missing values. The easiest way is to use mutate() to replace the variable with a modified copy. you can use the ifelse() function to replace unusual values with NA:
rr diamonds2 <- diamonds %>% mutate(y = ifelse(y < 3 | y > 20, NA, y)) select(diamonds2)
When using GGplot with missing values, it will place a warning that it removed xx rows containing missin values
library(tidyverse)
package <U+393C><U+3E31>tidyverse<U+393C><U+3E32> was built under R version 3.3.3Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages ------------------------------------------------------------------------------------
filter(): dplyr, stats
lag(): dplyr, stats
ggplot(data = diamonds2, mapping = aes(x = x, y = y)) +
geom_point()

To suppress the warning, use na.rm=TRUE
ggplot(data = diamonds2, mapping = aes(x = x, y = y)) +
geom_point(na.rm=TRUE)

Say you wanted to compare the departure times for cancelled and non-cancelled flights.
nycflights13::flights %>%
mutate(cancelled = is.na(dep_time),
sched_hour = sched_dep_time %/% 100,
sched_min = sched_dep_time %% 100,
sched_dep_time = sched_hour + sched_min / 60) %>%
ggplot(mapping = aes(sched_dep_time)) +
geom_freqpoly(mapping = aes(color = cancelled), binwidth = 1/4)

You can really see any patterns in the TRUE (colored light blue) as there are a lot more non cancelled flights.
Covariation
Covariation describes the behavior between variables. It is the tendency of two or more variables to vary together in a related way
But sometimes the pattern is not discernible as one variable has a large measure. To make comparison easier, we use density instead of count() so that the area under each frequency polygon is one.
ggplot(data=diamonds, mapping = aes(x = price, y= ..density..)) +
geom_freqpoly(mapping = aes(color = cut), binwidth = 500)

Another way to display the distriubtion of a continuous variable broken down by categorical variable is the boxplot.
ggplot(data = diamonds , mapping = aes(x = cut, y = price)) +
geom_boxplot()

The better cuts are cheaper on the average cheaper! Many categorical values dont have an intrinsic order. You might want to reorder them to make a more informative display. One way to do that is with the reorder() function.
To illustrate the point, here is the unordered plot :
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
geom_boxplot()

And here is the reordered plot:
ggplot(data = mpg) +
geom_boxplot(
mapping = aes( x = reorder(class, hwy, FUN = median), y = hwy)
)

you can also flip the boxplot chart 90 degrees by using the coord_flip() function. This displays particularly long variable names better.
ggplot(data = mpg) +
geom_boxplot(
mapping = aes(
x = reorder(class, hwy, FUN=median),
y = hwy)
) +
coord_flip()

NA
Two Categorical Variables
To visualize the covariation between categorical variables, you will need to count the number of observations for EACH combination. The way to do that is with geom_count()
ggplot(data = diamonds) +
geom_count(mapping = aes(x =cut, y = color))

Another approach is to compute the count with dplyr
diamonds %>%
count(color,cut)
You then visualize with geom_tile(). This looks like a heat map.
diamonds %>%
count(color,cut) %>%
ggplot(mapping = aes(x = color, y = cut)) +
geom_tile(mapping = aes(fill = n))

Check out seriation package, d3heatmap or heatmaply packages.
Two continuous variables
Use geom_point() to create scatterplots.
ggplot(data = diamonds) +
geom_point(mapping = aes(x = carat, y = price))

As the data points increase, the data points will pile up and block each other. Use alpha() aesthtetics to add transparency:
ggplot(data = diamonds) +
geom_point(mapping = aes(x = carat, y = price),
alpha= 1/100)

You can also use histograms (2d) with geom_bin2d and geom_hex()
ggplot(data = smaller) +
geom_bin2d(mapping = aes(x = carat, y = price))

# package hexbin required for stat_binhex
# you need to install hexbin package
library(hexbin)
package <U+393C><U+3E31>hexbin<U+393C><U+3E32> was built under R version 3.3.3
ggplot(data = smaller) +
geom_hex(mapping = aes(x = carat, y = price))

Another option is to convert one continuous variable into a bin using the cut_width() function
ggplot(data = smaller, mapping =aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))

One weakness of boxplots is that by default it doesnt show how many observations there are…unless you use the function varwidth=TRUE using cut_number instead of cut_width
ggplot(data = smaller, mapping =aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_number(carat, 20)))

Patterns and Models
A sample pattern of old faithful geyser
ggplot(data = faithful) +
geom_point(mapping = aes(x = eruptions, y = waiting))

How to remove the effect of one covariance to be able to examine other subtle relationships. The residual data of the simple linear regression model is the difference between the observed data of the dependent variable y and the fitted values y.
library(modelr)
mod <- lm(log(price) ~ log(carat), data = diamonds)
diamonds2 <- diamonds %>%
add_residuals(mod) %>%
mutate(resid = exp(resid))
ggplot(data = diamonds2) +
geom_point(mapping = aes(x = carat, y = resid))

NA
Once you’ve removed the strong relationship between carat and price, you can see what you expect in the relationship between cut and price - relative to their size, beter uality diamonds are more expensive. As can be seen in the graph below.
ggplot(data = diamonds2) +
geom_boxplot(mapping = aes(x = cut, y = resid))

Check out also : Graphical Data Analysis with R by Antony Unwin. 
LS0tDQp0aXRsZTogIlIgRm9yIERhdGEgU2NpZW5jZSBDaGFwdGVyIDUiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQo8aDE+IENoYXB0ZXIgNSBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIDwvaDE+DQpFREEgaXMgYW4gaXRlcmF0aXZlIGN5Y2xlIHdoZXJlICB5b3U6PC9icj4NCjEuIEdlbmVyYXRlIHF1ZXN0aW9ucyBhYm91dCB5b3VyIGRhdGE8L2JyPg0KMi4gU2VhcmNoIGZvciBhbnN3ZXJzIGJ5IHZpc3VhbGl6aW5nLCB0cmFuc2Zvcm1pbmcgYW5kIG1vZGVsaW5nIHlvdXIgZGF0YTwvYnI+DQozLiBVc2Ugd2hhdCB5b3UgbGVhcm4gdG8gcmVmaW5lIHlvdXIgcXVlc3Rpb25zIGFuZC9vciBnZW5lcmF0ZSBuZXcgcXVlc3Rpb25zDQo8L3A+DQoNClRoZSB0d28gdHlwZXMgb2YgcXVlc3Rpb25zOiA8L2JyPg0KMS4gV2hhdCB0eXBlIG9mIHZhcmlhdGlvbnMgb2NjdXJzIHdpdGhpbiBlYWNoIHZhcmlhYmxlPzwvYnI+DQoyLiBXaGF0IHR5cGUgb2YgY292YXJpYXRpb24gb2NjdXJzIGJldHdlZW4gdmFyaWFibGVzID8gPC9wPg0KDQpEZWZpbml0aW9uczogPC9icj4NClZhcmlhYmxlIGlzIGEgcXVhbnRpdHkgb3IgcXVhbGl0eSBvciBwcm9wZXJ0eSB0aGF0IHlvdSBjYW4gbWVhc3VyZTwvYnI+DQpWYWx1ZSBpcyB0aGUgc3RhdGUgb2YgdGhlIHZhcmlhYmxlIHdoZW4geW91IG1lYXN1cmUgaXQuPC9icj4NCk9ic2VydmF0aW9uIG9yIGEgY2FzZSAsIGlzIGEgc2V0IG9mIG1lYXN1cmVtZW50cyBtYWRlIHVuZGVyIHNpbWlsYXIgY29uZGl0aW9uczwvYnI+DQpUYWJ1bGFyIGRhdGEgaXMgYSBzZXQgb2YgdmFsdWVzLCBlYWNoIGFzc29jaWF0ZWQgd2l0aCBhIGF2cmFpYmxlIGFuZCBhbiBvYnNlcnZhdGlvbi4gPC9icj4NClZhcmlhdGlvbiBpcyB0aGUgdGVuZGVuY3kgb2YgdGhlIHZhbHVlcyBvZiBhIHZhcmlhYmxlIHRvIGNoYW5nZSBmcm9tIG1lYXN1cmVtZW50IHRvIG1lYXN1cmVtZW50PC9icj4NCg0KPGgyPiBWaXN1YWxpemluZyBEaXN0cmlidXRpb25zIDwvaDI+DQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArDQogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGN1dCkpDQpgYGANCkFuIGV4YW1wbGUgZm9yIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgKHVzdWFsbHkgc2F2ZWQgaW4gUiBhcyBmYWN0b3JzKQ0KYGBge3J9DQpkaWFtb25kcyAlPiUNCiAgY291bnQoY3V0KQ0KYGBgDQoNCkFzIGV4YW1wbGUgZm9yIGNvbnRpbnVvdXMgdmFyaWFibGUgKGJldHRlciB1c2UgYSBoaXN0b2dyYW0pDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKw0KICBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSBjYXJhdCksIGJpbndpZHRoID0wLjUpDQpgYGANCg0KWW91IGNhbiBjb21wdXRlIGJ5IGhhbmQgYnkgY29tYmluaW5nIGRwbHlyOjpjb3VudCgpIGFuZCBnZ3Bsb3QyOjpjdXRfd2lkdGgoKQ0KDQpgYGB7cn0NCmRpYW1vbmRzICU+JQ0KICBjb3VudChjdXRfd2lkdGgoY2FyYXQsIDAuNSkpDQpgYGANCg0KSWYgeW91IHdhbnQganVzdCB0aGUgZGlhbW9uZCB3aXRoIGEgc2l6ZSBvZiBsZXNzIHRoYW4gdGhyZWUgY2FyYXRzIGFuZCBjaG9vc2UgYSBzbWFsbCBiaW53aWR0aDoNCmBgYHtyfQ0Kc21hbGxlciA8LSBkaWFtb25kcyAlPiUNCiAgZmlsdGVyKGNhcmF0IDwgMykNCg0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLG1hcHBpbmc9YWVzKHg9Y2FyYXQpKSsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEpDQpgYGANCg0KWW91IGNhbiBhbHNvIHVzZSBnZW9tX2ZyZXFwb2x5IHRvIHVzZSBsaW5lcyBpbnN0ZWFkDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyLG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCBjb2xvciA9IGN1dCkpICsNCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMSkNCmBgYA0KDQo8aDI+IFR5cGljYWwgVmFsdWVzIDwvaDI+DQpJbiBib3RoIGJhciBjaGFydHMgYW5kIGhpc3RvZ3JhbXMsIHRhbGwgYmFycyBzaG93cyB0aGUgY29tbW9uIHZhbHVlcywgYW5kIHNob3J0ZXIgYmFycyBzaG93cyB0aGUgbGVzcy1jb21tb24gdmFsdWVzLiBQbGFjZXMgdGhhdCBkbyBub3QgaGF2ZSBiYXJzIHJldmVhbCB2YWx1ZXMgdGhhdCB3ZXJlIG5vdCBzZWVuIGluIHlvdXIgZGF0YSAvYnI+DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBmYWl0aGZ1bCwgbWFwcGluZyA9IGFlcyh4ID0gZXJ1cHRpb25zKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMjUpDQpgYGANCg0KDQo8aDI+IFVudXN1YWwgVmFsdWVzIDwvaDI+DQpTb21ldGltZXMgdGhlIG9ubHkgZXZpZGVuY2Ugb2Ygb3V0bGllcnMgaXMgdGhlIHVudXN1YWxseSB3aWRlIGxpbWl0cyBvbiB0aGUgeS1heGlzIDwvYnI+DQoNCmBgYHtyfQ0KZ2dwbG90KGRpYW1vbmRzKSsNCiAgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh4ID0geSksIGJpbndpZHRoID0gMC41KQ0KDQpgYGANCg0KVGhlIHZhbHVlcyBtYXkgYmUgc28gc21hbGwgdGhhdCB5b3UgYmFyZWx5IHNlZSBpdC4gVG8gbWFrZSBpdCBwb3AgdXAgdXNlIGEgbG93ZXIgY29vcmRpbmF0ZSBzeXN0ZW0NCg0KYGBge3J9DQpnZ3Bsb3QoZGlhbW9uZHMpICsNCiAgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh4ID0geSksIGJpbndpZHRoID0gMC41KSArDQogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDUwKSkNCg0KYGBgDQoNCg0KYGBge3J9DQp1bnVzdWFsIDwtICBkaWFtb25kcyAlPiUNCiAgZmlsdGVyKHkgPCAzIHwgeSA+IDIwKSAlPiUNCiAgYXJyYW5nZSh5KQ0KDQojIGRpc3BsYXkgdmFsdWVzDQp1bnVzdWFsDQpgYGANCg0KPGgyPiBNaXNzaW5nIFZhbHVlcyA8L2gyPg0KDQpZb3UgaGF2ZSB0d28gb3B0aW9ucyB0byB3b3JrIHdpdGggb3V0bGllcnM6IDwvYnI+DQoxLiBkcm9wIHRoZSByb3cgd2l0aCB0aGUgb3V0bGllcnMgDQpgYGB7cn0NCg0KZGlhbW9uZHMyIDwtIGRpYW1vbmRzICU+JQ0KICBmaWx0ZXIoYmV0d2Vlbih5LDMsMjApKQ0KZGlhbW9uZHMyDQoNCmBgYA0KDQpvciA6IDwvYnI+DQoyLiByZXBsYWNlIHRoZSB1bnVzYWwgdmFsdWVzIHdpdGggbWlzc2luZyB2YWx1ZXMuIFRoZSBlYXNpZXN0IHdheSBpcyB0byB1c2UgbXV0YXRlKCkgdG8gcmVwbGFjZSB0aGUgdmFyaWFibGUgd2l0aCBhIG1vZGlmaWVkIGNvcHkuIHlvdSBjYW4gdXNlIHRoZSBpZmVsc2UoKSBmdW5jdGlvbiB0byByZXBsYWNlIHVudXN1YWwgdmFsdWVzIHdpdGggTkE6DQoNCmBgYHtyfQ0KZGlhbW9uZHMyIDwtICBkaWFtb25kcyAlPiUNCiAgbXV0YXRlKHkgPSBpZmVsc2UoeSA8IDMgfCB5ID4gMjAsIE5BLCB5KSkNCg0KZGlhbW9uZHMyDQpgYGANCldoZW4gdXNpbmcgR0dwbG90IHdpdGggbWlzc2luZyB2YWx1ZXMsIGl0IHdpbGwgcGxhY2UgYSB3YXJuaW5nIHRoYXQgaXQgcmVtb3ZlZCB4eCByb3dzIGNvbnRhaW5pbmcgbWlzc2luIHZhbHVlcyANCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmdncGxvdChkYXRhID0gZGlhbW9uZHMyLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSkpICsNCiAgZ2VvbV9wb2ludCgpDQoNCmBgYA0KDQpUbyBzdXBwcmVzcyB0aGUgd2FybmluZywgdXNlIG5hLnJtPVRSVUUNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzMiwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHkpKSArDQogIGdlb21fcG9pbnQobmEucm09VFJVRSkNCmBgYA0KU2F5IHlvdSB3YW50ZWQgdG8gY29tcGFyZSB0aGUgZGVwYXJ0dXJlIHRpbWVzIGZvciBjYW5jZWxsZWQgYW5kIG5vbi1jYW5jZWxsZWQgZmxpZ2h0cy4NCg0KYGBge3J9DQpueWNmbGlnaHRzMTM6OmZsaWdodHMgJT4lDQogIG11dGF0ZShjYW5jZWxsZWQgPSBpcy5uYShkZXBfdGltZSksIA0KICAgICAgICAgc2NoZWRfaG91ciA9IHNjaGVkX2RlcF90aW1lICUvJSAxMDAsDQogICAgICAgICBzY2hlZF9taW4gPSBzY2hlZF9kZXBfdGltZSAlJSAxMDAsDQogICAgICAgICBzY2hlZF9kZXBfdGltZSA9IHNjaGVkX2hvdXIgKyBzY2hlZF9taW4gLyA2MCkgJT4lDQogIA0KICBnZ3Bsb3QobWFwcGluZyA9IGFlcyhzY2hlZF9kZXBfdGltZSkpICsNCiAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG9yID0gY2FuY2VsbGVkKSwgYmlud2lkdGggPSAxLzQpDQpgYGANCllvdSBjYW4gcmVhbGx5IHNlZSBhbnkgcGF0dGVybnMgaW4gdGhlIFRSVUUgKGNvbG9yZWQgbGlnaHQgYmx1ZSkgYXMgdGhlcmUgYXJlIGEgbG90IG1vcmUgbm9uIGNhbmNlbGxlZCBmbGlnaHRzLiANCg0KPGgyPiBDb3ZhcmlhdGlvbiA8L2gyPg0KQ292YXJpYXRpb24gZGVzY3JpYmVzIHRoZSBiZWhhdmlvciBiZXR3ZWVuIHZhcmlhYmxlcy4gSXQgaXMgdGhlIHRlbmRlbmN5IG9mIHR3byBvciBtb3JlIHZhcmlhYmxlcyB0byB2YXJ5IHRvZ2V0aGVyIGluIGEgcmVsYXRlZCB3YXkgPC9wPg0KDQpCdXQgc29tZXRpbWVzIHRoZSBwYXR0ZXJuIGlzIG5vdCBkaXNjZXJuaWJsZSBhcyBvbmUgdmFyaWFibGUgaGFzIGEgbGFyZ2UgbWVhc3VyZS4gVG8gbWFrZSBjb21wYXJpc29uIGVhc2llciwgd2UgdXNlIGRlbnNpdHkgaW5zdGVhZCBvZiBjb3VudCgpICBzbyB0aGF0IHRoZSBhcmVhIHVuZGVyIGVhY2ggZnJlcXVlbmN5IHBvbHlnb24gaXMgb25lLiANCg0KYGBge3J9DQoNCmdncGxvdChkYXRhPWRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSAgcHJpY2UsIHk9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2ZyZXFwb2x5KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBjdXQpLCBiaW53aWR0aCA9IDUwMCkNCmBgYA0KDQpBbm90aGVyIHdheSB0byBkaXNwbGF5IHRoZSBkaXN0cml1YnRpb24gb2YgYSBjb250aW51b3VzIHZhcmlhYmxlIGJyb2tlbiBkb3duIGJ5IGNhdGVnb3JpY2FsIHZhcmlhYmxlIGlzIHRoZSBib3hwbG90LiANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzICwgbWFwcGluZyA9IGFlcyh4ID0gY3V0LCB5ID0gcHJpY2UpKSArDQogIGdlb21fYm94cGxvdCgpDQpgYGANClRoZSBiZXR0ZXIgY3V0cyBhcmUgY2hlYXBlciBvbiB0aGUgYXZlcmFnZSBjaGVhcGVyIQ0KTWFueSBjYXRlZ29yaWNhbCB2YWx1ZXMgZG9udCBoYXZlIGFuIGludHJpbnNpYyBvcmRlci4gWW91IG1pZ2h0IHdhbnQgdG8gcmVvcmRlciB0aGVtIHRvIG1ha2UgYSBtb3JlIGluZm9ybWF0aXZlIGRpc3BsYXkuIE9uZSB3YXkgdG8gZG8gdGhhdCBpcyB3aXRoIHRoZSByZW9yZGVyKCkgZnVuY3Rpb24uIDwvcD4NCg0KVG8gaWxsdXN0cmF0ZSB0aGUgcG9pbnQsIGhlcmUgaXMgdGhlIHVub3JkZXJlZCBwbG90IDoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gY2xhc3MsIHkgPSBod3kpKSArDQogIGdlb21fYm94cGxvdCgpDQoNCmBgYA0KDQoNCg0KQW5kIGhlcmUgaXMgdGhlIHJlb3JkZXJlZCBwbG90Og0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fYm94cGxvdCgNCiAgICBtYXBwaW5nID0gYWVzKCB4ID0gcmVvcmRlcihjbGFzcywgaHd5LCBGVU4gPSBtZWRpYW4pLCB5ID0gaHd5KQ0KICApDQpgYGANCg0KDQp5b3UgY2FuIGFsc28gZmxpcCB0aGUgYm94cGxvdCBjaGFydCA5MCBkZWdyZWVzIGJ5IHVzaW5nIHRoZSBjb29yZF9mbGlwKCkgZnVuY3Rpb24uIFRoaXMgZGlzcGxheXMgcGFydGljdWxhcmx5IGxvbmcgdmFyaWFibGUgbmFtZXMgYmV0dGVyLiANCg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fYm94cGxvdCgNCiAgICBtYXBwaW5nID0gYWVzKA0KICAgICAgeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOPW1lZGlhbiksDQogICAgICB5ID0gaHd5KQ0KICApICsNCiAgY29vcmRfZmxpcCgpDQogIA0KYGBgDQoNCjxoMj4gVHdvIENhdGVnb3JpY2FsIFZhcmlhYmxlcyA8L2gyPg0KDQpUbyB2aXN1YWxpemUgdGhlIGNvdmFyaWF0aW9uIGJldHdlZW4gY2F0ZWdvcmljYWwgdmFyaWFibGVzLCB5b3Ugd2lsbCBuZWVkIHRvIGNvdW50IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZvciBFQUNIIGNvbWJpbmF0aW9uLiBUaGUgd2F5IHRvIGRvIHRoYXQgaXMgd2l0aCBnZW9tX2NvdW50KCkgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKw0KICBnZW9tX2NvdW50KG1hcHBpbmcgPSBhZXMoeCA9Y3V0LCB5ID0gY29sb3IpKQ0KYGBgDQoNCkFub3RoZXIgYXBwcm9hY2ggaXMgdG8gY29tcHV0ZSB0aGUgY291bnQgd2l0aCBkcGx5cg0KDQpgYGB7cn0NCmRpYW1vbmRzICU+JQ0KICBjb3VudChjb2xvcixjdXQpDQoNCmBgYA0KDQoNCllvdSB0aGVuIHZpc3VhbGl6ZSB3aXRoIGdlb21fdGlsZSgpLiBUaGlzIGxvb2tzIGxpa2UgYSBoZWF0IG1hcC4NCg0KYGBge3J9DQpkaWFtb25kcyAlPiUNCiAgY291bnQoY29sb3IsY3V0KSAlPiUNCiAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGNvbG9yLCB5ID0gY3V0KSkgKw0KICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gbikpDQoNCg0KYGBgDQoNCkNoZWNrIG91dCAgc2VyaWF0aW9uIHBhY2thZ2UsIGQzaGVhdG1hcCBvciBoZWF0bWFwbHkgcGFja2FnZXMuDQoNCg0KPGgyPiBUd28gY29udGludW91cyB2YXJpYWJsZXMgPC9oMj4NClVzZSBnZW9tX3BvaW50KCkgdG8gY3JlYXRlIHNjYXR0ZXJwbG90cy4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArDQogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpDQpgYGANCg0KQXMgdGhlIGRhdGEgcG9pbnRzIGluY3JlYXNlLCB0aGUgZGF0YSBwb2ludHMgd2lsbCBwaWxlIHVwIGFuZCBibG9jayBlYWNoIG90aGVyLiBVc2UgYWxwaGEoKSBhZXN0aHRldGljcyB0byBhZGQgdHJhbnNwYXJlbmN5Og0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsNCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSwNCiAgICAgICAgICAgICBhbHBoYSA9IDEvMTAwKQ0KDQpgYGANCg0KWW91IGNhbiBhbHNvIHVzZSBoaXN0b2dyYW1zICgyZCkgd2l0aCBnZW9tX2JpbjJkIGFuZCBnZW9tX2hleCgpDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyKSArIA0KICBnZW9tX2JpbjJkKG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCiMgcGFja2FnZSBoZXhiaW4gcmVxdWlyZWQgZm9yIHN0YXRfYmluaGV4DQojIHlvdSBuZWVkIHRvIGluc3RhbGwgaGV4YmluIHBhY2thZ2UNCmxpYnJhcnkoaGV4YmluKQ0KZ2dwbG90KGRhdGEgPSBzbWFsbGVyKSArIA0KICBnZW9tX2hleChtYXBwaW5nID0gYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkNCmBgYA0KDQpBbm90aGVyIG9wdGlvbiBpcyB0byBjb252ZXJ0IG9uZSBjb250aW51b3VzIHZhcmlhYmxlIGludG8gYSBiaW4gdXNpbmcgdGhlIGN1dF93aWR0aCgpIGZ1bmN0aW9uDQoNCmBgYHtyfQ0KDQpnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIsIG1hcHBpbmcgPWFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpICsNCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoZ3JvdXAgPSBjdXRfd2lkdGgoY2FyYXQsIDAuMSkpKQ0KYGBgDQoNCk9uZSB3ZWFrbmVzcyBvZiBib3hwbG90cyBpcyB0aGF0IGJ5IGRlZmF1bHQgaXQgZG9lc250IHNob3cgaG93IG1hbnkgb2JzZXJ2YXRpb25zIHRoZXJlIGFyZS4uLnVubGVzcyB5b3UgdXNlIHRoZSBmdW5jdGlvbiB2YXJ3aWR0aD1UUlVFIHVzaW5nIGN1dF9udW1iZXIgaW5zdGVhZCBvZiBjdXRfd2lkdGgNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHNtYWxsZXIsIG1hcHBpbmcgPWFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpICsNCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoZ3JvdXAgPSBjdXRfbnVtYmVyKGNhcmF0LCAyMCkpKQ0KYGBgDQoNCjxoMj4gUGF0dGVybnMgYW5kIE1vZGVscyA8L2gyPg0KQSBzYW1wbGUgcGF0dGVybiBvZiBvbGQgZmFpdGhmdWwgZ2V5c2VyDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBmYWl0aGZ1bCkgKw0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGVydXB0aW9ucywgeSA9IHdhaXRpbmcpKQ0KYGBgDQoNCkhvdyB0byByZW1vdmUgdGhlIGVmZmVjdCBvZiBvbmUgY292YXJpYW5jZSB0byBiZSBhYmxlIHRvIGV4YW1pbmUgb3RoZXIgc3VidGxlIHJlbGF0aW9uc2hpcHMuDQpUaGUgcmVzaWR1YWwgZGF0YSBvZiB0aGUgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG9ic2VydmVkIGRhdGEgb2YgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSB5IGFuZCB0aGUgZml0dGVkIHZhbHVlcyB5Lg0KYGBge3J9DQpsaWJyYXJ5KG1vZGVscikNCiMgZXh0cmFjdCB0aGUgcHJpY2UgYW5kIGNhcmF0IHJlbGF0aW9uc2hpcA0KbW9kIDwtICBsbShsb2cocHJpY2UpIH4gbG9nKGNhcmF0KSwgZGF0YSA9IGRpYW1vbmRzKQ0KDQojIHVzZSBhZGQgcmVzaWR1YWxzIA0KZGlhbW9uZHMyIDwtICBkaWFtb25kcyAlPiUNCiAgYWRkX3Jlc2lkdWFscyhtb2QpICU+JQ0KICBtdXRhdGUocmVzaWQgPSBleHAocmVzaWQpKQ0KDQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzMikgKw0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcmVzaWQpKQ0KICAgICAgICAgIA0KYGBgDQoNCk9uY2UgeW91J3ZlIHJlbW92ZWQgdGhlIHN0cm9uZyByZWxhdGlvbnNoaXAgYmV0d2VlbiBjYXJhdCBhbmQgcHJpY2UsIHlvdSBjYW4gc2VlIHdoYXQgeW91IGV4cGVjdCBpbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY3V0IGFuZCBwcmljZSAtIHJlbGF0aXZlIHRvIHRoZWlyIHNpemUsIGJldGVyIHVhbGl0eSBkaWFtb25kcyBhcmUgbW9yZSBleHBlbnNpdmUuIA0KQXMgY2FuIGJlIHNlZW4gaW4gdGhlIGdyYXBoIGJlbG93Lg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZGlhbW9uZHMyKSArDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSByZXNpZCkpDQpgYGANCg0KQ2hlY2sgb3V0IGFsc28gOiBHcmFwaGljYWwgRGF0YSBBbmFseXNpcyB3aXRoIFIgYnkgQW50b255IFVud2luLjwvYnI+DQo8aW1nIHNyYz0iaHR0cHM6Ly9pbWFnZXMudGFuZGYuY28udWsvY29tbW9uL2phY2tldHMvYW1hem9uLzk3ODE0OTg3MS85NzgxNDk4NzE1MjMyLmpwZyI+PC9pbWc+DQoNCg==