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==