suppressPackageStartupMessages(library("tidyverse"))
package 㤼㸱tidyverse㤼㸲 was built under R version 3.6.3
suppressPackageStartupMessages(library("nycflights13"))
package 㤼㸱nycflights13㤼㸲 was built under R version 3.6.3
#These additional package provide functions that will be used in answering some questions
suppressPackageStartupMessages(library("ggstance"))
package 㤼㸱ggstance㤼㸲 was built under R version 3.6.3
suppressPackageStartupMessages(library("lvplot"))
package 㤼㸱lvplot㤼㸲 was built under R version 3.6.3
suppressPackageStartupMessages(library("ggbeeswarm"))
package 㤼㸱ggbeeswarm㤼㸲 was built under R version 3.6.3

1. Use what you’ve learned to improve the visualization of the departure times of cancelled vs. non-cancelled flights.

Instead of a freqplot use a box-plot

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() +
  geom_boxplot(mapping = aes(y = sched_dep_time, x = cancelled))

2. What variable in the diamonds dataset is most important for predicting the price of a diamond? How is that variable correlated with cut? Why does the combination of those two relationships lead to lower quality diamonds being more expensive?

What are the general relationships of each variable with the price of the diamonds? I will consider the variables: carat, clarity, color, and cut. I ignore the dimensions of the diamond since carat measures size, and thus incorporates most of the information contained in these variables.

Since both price and carat are continuous variables, I use a scatter plot to visualize their relationship.

ggplot(diamonds, aes(x = carat, y = price)) +
  geom_point()

However, since there is a large number of points in the data, I will use a boxplot by binning carat (as suggested in the chapter).

ggplot(data = diamonds, mapping = aes(x = carat, y = price)) +
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))

Note that the choice of the binning width is important, as if it were too large it would obscure any relationship, and if it were too small, the values in the bins could be too variable to reveal underlying trends.

The variables color and clarity are ordered categorical variables. The chapter suggests visualizing a categorical and continuous variable using frequency polygons or boxplots. In this case, I will use a box plot since it will better show a relationship between the variables.

There is a weak negative relationship between color and price. The scale of diamond color goes from D (best) to J (worst). Currently, the levels of diamonds$color are in the wrong order. Before plotting, I will reverse the order of the color levels so they will be in increasing order of quality on the x-axis. The color column is an example of a factor variable.

diamonds %>%
  mutate(color = fct_rev(color)) %>%
  ggplot(aes(x = color, y = price)) +
  geom_boxplot()

There is also weak negative relationship between clarity and price. The scale of clarity goes from I1 (worst) to IF (best).

ggplot(data = diamonds) +
  geom_boxplot(mapping = aes(x = clarity, y = price))

For both clarity and color, there is a much larger amount of variation within each category than between categories. Carat is clearly the single best predictor of diamond prices.

Now that we have established that carat appears to be the best predictor of price, what is the relationship between it and cut? Since this is an example of a continuous (carat) and categorical (cut) variable, it can be visualized with a box plot.

ggplot(diamonds, aes(x = cut, y = carat)) +
  geom_boxplot()

There is a lot of variability in the distribution of carat sizes within each cut category. There is a slight negative relationship between carat and cut. Noticeably, the largest carat diamonds have a cut of “Fair” (the lowest).

This negative relationship can be due to the way in which diamonds are selected for sale. A larger diamond can be profitably sold with a lower quality cut, while a smaller diamond requires a better cut.

3. Install the ggstance package, and create a horizontal box plot. How does this compare to using coord_flip()?

Earlier, we created this horizontal box plot of the distribution hwy by class, using geom_boxplot() and coord_flip():

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy)) +
  coord_flip()

In this case the output looks the same, but x and y aesthetics are flipped.

#geom_boxploth() in ggstance library
ggplot(data = mpg) +
  geom_boxploth(mapping = aes(y = reorder(class, hwy, FUN = median), x = hwy))

4. One problem with box plots is that they were developed in an era of much smaller datasets and tend to display a prohibitively large number of “outlying values”. One approach to remedy this problem is the letter value plot. Install the lvplot package, and try using geom_lv() to display the distribution of price vs cut. What do you learn?

How do you interpret the plots?

Like box-plots, the boxes of the letter-value plot correspond to quantiles. However, they incorporate far more quantiles than box-plots. They are useful for larger datasets because,

  1. larger datasets can give precise estimates of quantiles beyond the quartiles, and
  2. in expectation, larger datasets should have more outliers (in absolute numbers).
#geom_lv() in lvplot library
ggplot(diamonds, aes(x = cut, y = price)) +
  geom_lv()

The letter-value plot is described in Hofmann, Wickham, and Kafadar ( 2017 ).

5. Compare and contrast geom_violin() with a faceted geom_histogram(), or a colored geom_freqpoly(). What are the pros and cons of each method?

I produce plots for these three methods below. The geom_freqpoly() is better for look-up: meaning that given a price, it is easy to tell which cut has the highest density. However, the overlapping lines makes it difficult to distinguish how the overall distributions relate to each other. The geom_violin() and faceted geom_histogram() have similar strengths and weaknesses. It is easy to visually distinguish differences in the overall shape of the distributions (skewness, central values, variance, etc). However, since we can’t easily compare the vertical values of the distribution, it is difficult to look up which category has the highest density for a given price. All of these methods depend on tuning parameters to determine the level of smoothness of the distribution.

ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) +
  geom_freqpoly(mapping = aes(color = cut), binwidth = 500)


ggplot(data = diamonds, mapping = aes(x = price)) +
  geom_histogram() +
  facet_wrap(~cut, ncol = 1, scales = "free_y")


ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
  geom_violin() +
  coord_flip()

The violin plot was first described in Hintze and Nelson ( 1998 ).

6. If you have a small dataset, it’s sometimes useful to use geom_jitter() to see the relationship between a continuous and categorical variable. The ggbeeswarm package provides a number of methods similar to geom_jitter(). List them and briefly describe what each one does.

There are two methods:

  • geom_quasirandom() produces plots that are a mix of jitter and violin plots. There are several different methods that determine exactly how the random location of the points is generated.
  • geom_beeswarm() produces a plot similar to a violin plot, but by offsetting the points.

I’ll use the mpg box plot example since these methods display individual points, they are better suited for smaller datasets.

ggplot(data = mpg) +
  geom_quasirandom(mapping = aes(
    x = reorder(class, hwy, FUN = median),
    y = hwy
  ))


ggplot(data = mpg) +
  geom_quasirandom(
    mapping = aes(
      x = reorder(class, hwy, FUN = median),
      y = hwy
    ),
    method = "tukey"
  )


ggplot(data = mpg) +
  geom_quasirandom(
    mapping = aes(
      x = reorder(class, hwy, FUN = median),
      y = hwy
    ),
    method = "tukeyDense"
  )


ggplot(data = mpg) +
  geom_quasirandom(
    mapping = aes(
      x = reorder(class, hwy, FUN = median),
      y = hwy
    ),
    method = "frowney"
  )


ggplot(data = mpg) +
  geom_quasirandom(
    mapping = aes(
      x = reorder(class, hwy, FUN = median),
      y = hwy
    ),
    method = "smiley"
  )


ggplot(data = mpg) +
  geom_beeswarm(mapping = aes(
    x = reorder(class, hwy, FUN = median),
    y = hwy
  ))

LS0tDQp0aXRsZTogIkVEQSBDb3ZhcmlhdGlvbiINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQotLS0NCg0KYGBge3IgbG9hZGxpYnJhcnl9DQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgidGlkeXZlcnNlIikpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgibnljZmxpZ2h0czEzIikpDQojVGhlc2UgYWRkaXRpb25hbCBwYWNrYWdlIHByb3ZpZGUgZnVuY3Rpb25zIHRoYXQgd2lsbCBiZSB1c2VkIGluIGFuc3dlcmluZyBzb21lIHF1ZXN0aW9ucw0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoImdnc3RhbmNlIikpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgibHZwbG90IikpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgiZ2diZWVzd2FybSIpKQ0KYGBgDQoNCiMjIyAxLiBVc2Ugd2hhdCB5b3XigJl2ZSBsZWFybmVkIHRvIGltcHJvdmUgdGhlIHZpc3VhbGl6YXRpb24gb2YgdGhlIGRlcGFydHVyZSB0aW1lcyBvZiBjYW5jZWxsZWQgdnMuIG5vbi1jYW5jZWxsZWQgZmxpZ2h0cy4NCg0KSW5zdGVhZCBvZiBhIGBmcmVxcGxvdGAgdXNlIGEgYm94LXBsb3QNCg0KYGBge3J9DQpueWNmbGlnaHRzMTM6OmZsaWdodHMgJT4lDQogIG11dGF0ZSgNCiAgICBjYW5jZWxsZWQgPSBpcy5uYShkZXBfdGltZSksDQogICAgc2NoZWRfaG91ciA9IHNjaGVkX2RlcF90aW1lICUvJSAxMDAsDQogICAgc2NoZWRfbWluID0gc2NoZWRfZGVwX3RpbWUgJSUgMTAwLA0KICAgIHNjaGVkX2RlcF90aW1lID0gc2NoZWRfaG91ciArIHNjaGVkX21pbiAvIDYwDQogICkgJT4lDQogIGdncGxvdCgpICsNCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeSA9IHNjaGVkX2RlcF90aW1lLCB4ID0gY2FuY2VsbGVkKSkNCmBgYA0KDQojIyMgMi4gV2hhdCB2YXJpYWJsZSBpbiB0aGUgZGlhbW9uZHMgZGF0YXNldCBpcyBtb3N0IGltcG9ydGFudCBmb3IgcHJlZGljdGluZyB0aGUgcHJpY2Ugb2YgYSBkaWFtb25kPyBIb3cgaXMgdGhhdCB2YXJpYWJsZSBjb3JyZWxhdGVkIHdpdGggY3V0PyBXaHkgZG9lcyB0aGUgY29tYmluYXRpb24gb2YgdGhvc2UgdHdvIHJlbGF0aW9uc2hpcHMgbGVhZCB0byBsb3dlciBxdWFsaXR5IGRpYW1vbmRzIGJlaW5nIG1vcmUgZXhwZW5zaXZlPw0KDQpXaGF0IGFyZSB0aGUgZ2VuZXJhbCByZWxhdGlvbnNoaXBzIG9mIGVhY2ggdmFyaWFibGUgd2l0aCB0aGUgcHJpY2Ugb2YgdGhlIGRpYW1vbmRzPyBJIHdpbGwgY29uc2lkZXIgdGhlIHZhcmlhYmxlczogYGNhcmF0YCwgYGNsYXJpdHlgLCBgY29sb3JgLCBhbmQgYGN1dGAuIEkgaWdub3JlIHRoZSBkaW1lbnNpb25zIG9mIHRoZSBkaWFtb25kIHNpbmNlIGBjYXJhdGAgbWVhc3VyZXMgc2l6ZSwgYW5kIHRodXMgaW5jb3Jwb3JhdGVzIG1vc3Qgb2YgdGhlIGluZm9ybWF0aW9uIGNvbnRhaW5lZCBpbiB0aGVzZSB2YXJpYWJsZXMuDQoNClNpbmNlIGJvdGggYHByaWNlYCBhbmQgYGNhcmF0YCBhcmUgY29udGludW91cyB2YXJpYWJsZXMsIEkgdXNlIGEgc2NhdHRlciBwbG90IHRvIHZpc3VhbGl6ZSB0aGVpciByZWxhdGlvbnNoaXAuDQoNCmBgYHtyfQ0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCkhvd2V2ZXIsIHNpbmNlIHRoZXJlIGlzIGEgbGFyZ2UgbnVtYmVyIG9mIHBvaW50cyBpbiB0aGUgZGF0YSwgSSB3aWxsIHVzZSBhIGJveHBsb3QgYnkgYmlubmluZyBgY2FyYXRgIChhcyBzdWdnZXN0ZWQgaW4gdGhlIGNoYXB0ZXIpLg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArDQogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKGdyb3VwID0gY3V0X3dpZHRoKGNhcmF0LCAwLjEpKSkNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIGNob2ljZSBvZiB0aGUgYmlubmluZyB3aWR0aCBpcyBpbXBvcnRhbnQsIGFzIGlmIGl0IHdlcmUgdG9vIGxhcmdlIGl0IHdvdWxkIG9ic2N1cmUgYW55IHJlbGF0aW9uc2hpcCwgYW5kIGlmIGl0IHdlcmUgdG9vIHNtYWxsLCB0aGUgdmFsdWVzIGluIHRoZSBiaW5zIGNvdWxkIGJlIHRvbyB2YXJpYWJsZSB0byByZXZlYWwgdW5kZXJseWluZyB0cmVuZHMuDQoNClRoZSB2YXJpYWJsZXMgYGNvbG9yYCBhbmQgYGNsYXJpdHlgIGFyZSBvcmRlcmVkIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gVGhlIGNoYXB0ZXIgc3VnZ2VzdHMgdmlzdWFsaXppbmcgYSBjYXRlZ29yaWNhbCBhbmQgY29udGludW91cyB2YXJpYWJsZSB1c2luZyBmcmVxdWVuY3kgcG9seWdvbnMgb3IgYm94cGxvdHMuIEluIHRoaXMgY2FzZSwgSSB3aWxsIHVzZSBhIGJveCBwbG90IHNpbmNlIGl0IHdpbGwgYmV0dGVyIHNob3cgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaWFibGVzLg0KDQpUaGVyZSBpcyBhIHdlYWsgbmVnYXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gYGNvbG9yYCBhbmQgYHByaWNlYC4gVGhlIHNjYWxlIG9mIGRpYW1vbmQgY29sb3IgZ29lcyBmcm9tIEQgKGJlc3QpIHRvIEogKHdvcnN0KS4gQ3VycmVudGx5LCB0aGUgbGV2ZWxzIG9mIGBkaWFtb25kcyRjb2xvcmAgYXJlIGluIHRoZSB3cm9uZyBvcmRlci4gQmVmb3JlIHBsb3R0aW5nLCBJIHdpbGwgcmV2ZXJzZSB0aGUgb3JkZXIgb2YgdGhlIGBjb2xvcmAgbGV2ZWxzIHNvIHRoZXkgd2lsbCBiZSBpbiBpbmNyZWFzaW5nIG9yZGVyIG9mIHF1YWxpdHkgb24gdGhlIHgtYXhpcy4gVGhlIGBjb2xvcmAgY29sdW1uIGlzIGFuIGV4YW1wbGUgb2YgYSBmYWN0b3IgdmFyaWFibGUuDQoNCmBgYHtyfQ0KZGlhbW9uZHMgJT4lDQogIG11dGF0ZShjb2xvciA9IGZjdF9yZXYoY29sb3IpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gY29sb3IsIHkgPSBwcmljZSkpICsNCiAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQpUaGVyZSBpcyBhbHNvIHdlYWsgbmVnYXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gYGNsYXJpdHlgIGFuZCBgcHJpY2VgLiBUaGUgc2NhbGUgb2YgY2xhcml0eSBnb2VzIGZyb20gSTEgKHdvcnN0KSB0byBJRiAoYmVzdCkuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKw0KICBnZW9tX2JveHBsb3QobWFwcGluZyA9IGFlcyh4ID0gY2xhcml0eSwgeSA9IHByaWNlKSkNCmBgYA0KDQpGb3IgYm90aCBgY2xhcml0eWAgYW5kIGBjb2xvcmAsIHRoZXJlIGlzIGEgbXVjaCBsYXJnZXIgYW1vdW50IG9mIHZhcmlhdGlvbiB3aXRoaW4gZWFjaCBjYXRlZ29yeSB0aGFuIGJldHdlZW4gY2F0ZWdvcmllcy4gQ2FyYXQgaXMgY2xlYXJseSB0aGUgc2luZ2xlIGJlc3QgcHJlZGljdG9yIG9mIGRpYW1vbmQgcHJpY2VzLg0KDQpOb3cgdGhhdCB3ZSBoYXZlIGVzdGFibGlzaGVkIHRoYXQgY2FyYXQgYXBwZWFycyB0byBiZSB0aGUgYmVzdCBwcmVkaWN0b3Igb2YgcHJpY2UsIHdoYXQgaXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGl0IGFuZCBjdXQ/IFNpbmNlIHRoaXMgaXMgYW4gZXhhbXBsZSBvZiBhIGNvbnRpbnVvdXMgKGNhcmF0KSBhbmQgY2F0ZWdvcmljYWwgKGN1dCkgdmFyaWFibGUsIGl0IGNhbiBiZSB2aXN1YWxpemVkIHdpdGggYSBib3ggcGxvdC4NCg0KYGBge3J9DQpnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY3V0LCB5ID0gY2FyYXQpKSArDQogIGdlb21fYm94cGxvdCgpDQpgYGANCg0KVGhlcmUgaXMgYSBsb3Qgb2YgdmFyaWFiaWxpdHkgaW4gdGhlIGRpc3RyaWJ1dGlvbiBvZiBjYXJhdCBzaXplcyB3aXRoaW4gZWFjaCBjdXQgY2F0ZWdvcnkuIFRoZXJlIGlzIGEgc2xpZ2h0IG5lZ2F0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNhcmF0IGFuZCBjdXQuIE5vdGljZWFibHksIHRoZSBsYXJnZXN0IGNhcmF0IGRpYW1vbmRzIGhhdmUgYSBjdXQgb2Yg4oCcRmFpcuKAnSAodGhlIGxvd2VzdCkuDQoNClRoaXMgbmVnYXRpdmUgcmVsYXRpb25zaGlwIGNhbiBiZSBkdWUgdG8gdGhlIHdheSBpbiB3aGljaCBkaWFtb25kcyBhcmUgc2VsZWN0ZWQgZm9yIHNhbGUuIEEgbGFyZ2VyIGRpYW1vbmQgY2FuIGJlIHByb2ZpdGFibHkgc29sZCB3aXRoIGEgbG93ZXIgcXVhbGl0eSBjdXQsIHdoaWxlIGEgc21hbGxlciBkaWFtb25kIHJlcXVpcmVzIGEgYmV0dGVyIGN1dC4NCg0KIyMjIDMuIEluc3RhbGwgdGhlIGdnc3RhbmNlIHBhY2thZ2UsIGFuZCBjcmVhdGUgYSBob3Jpem9udGFsIGJveCBwbG90LiBIb3cgZG9lcyB0aGlzIGNvbXBhcmUgdG8gdXNpbmcgY29vcmRfZmxpcCgpPw0KDQpFYXJsaWVyLCB3ZSBjcmVhdGVkIHRoaXMgaG9yaXpvbnRhbCBib3ggcGxvdCBvZiB0aGUgZGlzdHJpYnV0aW9uIGh3eSBieSBjbGFzcywgdXNpbmcgYGdlb21fYm94cGxvdCgpYCBhbmQgYGNvb3JkX2ZsaXAoKWA6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsNCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOID0gbWVkaWFuKSwgeSA9IGh3eSkpICsNCiAgY29vcmRfZmxpcCgpDQpgYGANCg0KSW4gdGhpcyBjYXNlIHRoZSBvdXRwdXQgbG9va3MgdGhlIHNhbWUsIGJ1dCBgeGAgYW5kIGB5YCBhZXN0aGV0aWNzIGFyZSBmbGlwcGVkLg0KDQpgYGB7cn0NCiNnZW9tX2JveHBsb3RoKCkgaW4gZ2dzdGFuY2UgbGlicmFyeQ0KZ2dwbG90KGRhdGEgPSBtcGcpICsNCiAgZ2VvbV9ib3hwbG90aChtYXBwaW5nID0gYWVzKHkgPSByZW9yZGVyKGNsYXNzLCBod3ksIEZVTiA9IG1lZGlhbiksIHggPSBod3kpKQ0KYGBgDQoNCiMjIyA0LiBPbmUgcHJvYmxlbSB3aXRoIGJveCBwbG90cyBpcyB0aGF0IHRoZXkgd2VyZSBkZXZlbG9wZWQgaW4gYW4gZXJhIG9mIG11Y2ggc21hbGxlciBkYXRhc2V0cyBhbmQgdGVuZCB0byBkaXNwbGF5IGEgcHJvaGliaXRpdmVseSBsYXJnZSBudW1iZXIgb2Yg4oCcb3V0bHlpbmcgdmFsdWVz4oCdLiBPbmUgYXBwcm9hY2ggdG8gcmVtZWR5IHRoaXMgcHJvYmxlbSBpcyB0aGUgbGV0dGVyIHZhbHVlIHBsb3QuIEluc3RhbGwgdGhlIFtsdnBsb3RdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9sdnBsb3QvdmVyc2lvbnMvMC4yLjApIHBhY2thZ2UsIGFuZCB0cnkgdXNpbmcgYGdlb21fbHYoKWAgdG8gZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIHByaWNlIHZzIGN1dC4gV2hhdCBkbyB5b3UgbGVhcm4/DQoNCkhvdyBkbyB5b3UgaW50ZXJwcmV0IHRoZSBwbG90cz8NCg0KTGlrZSBib3gtcGxvdHMsIHRoZSBib3hlcyBvZiB0aGUgbGV0dGVyLXZhbHVlIHBsb3QgY29ycmVzcG9uZCB0byBxdWFudGlsZXMuIEhvd2V2ZXIsIHRoZXkgaW5jb3Jwb3JhdGUgZmFyIG1vcmUgcXVhbnRpbGVzIHRoYW4gYm94LXBsb3RzLiBUaGV5IGFyZSB1c2VmdWwgZm9yIGxhcmdlciBkYXRhc2V0cyBiZWNhdXNlLA0KDQoxLiBsYXJnZXIgZGF0YXNldHMgY2FuIGdpdmUgcHJlY2lzZSBlc3RpbWF0ZXMgb2YgcXVhbnRpbGVzIGJleW9uZCB0aGUgcXVhcnRpbGVzLCBhbmQNCjIuIGluIGV4cGVjdGF0aW9uLCBsYXJnZXIgZGF0YXNldHMgc2hvdWxkIGhhdmUgbW9yZSBvdXRsaWVycyAoaW4gYWJzb2x1dGUgbnVtYmVycykuDQoNCmBgYHtyfQ0KI2dlb21fbHYoKSBpbiBsdnBsb3QgbGlicmFyeQ0KZ2dwbG90KGRpYW1vbmRzLCBhZXMoeCA9IGN1dCwgeSA9IHByaWNlKSkgKw0KICBnZW9tX2x2KCkNCmBgYA0KDQpUaGUgbGV0dGVyLXZhbHVlIHBsb3QgaXMgZGVzY3JpYmVkIGluIEhvZm1hbm4sIFdpY2toYW0sIGFuZCBLYWZhZGFyICggWzIwMTddKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDgwLzEwNjE4NjAwLjIwMTcuMTMwNTI3NykgKS4NCg0KIyMjIDUuIENvbXBhcmUgYW5kIGNvbnRyYXN0IGBnZW9tX3Zpb2xpbigpYCB3aXRoIGEgZmFjZXRlZCBgZ2VvbV9oaXN0b2dyYW0oKWAsIG9yIGEgY29sb3JlZCBgZ2VvbV9mcmVxcG9seSgpYC4gV2hhdCBhcmUgdGhlIHByb3MgYW5kIGNvbnMgb2YgZWFjaCBtZXRob2Q/DQoNCkkgcHJvZHVjZSBwbG90cyBmb3IgdGhlc2UgdGhyZWUgbWV0aG9kcyBiZWxvdy4gVGhlIGBnZW9tX2ZyZXFwb2x5KClgIGlzIGJldHRlciBmb3IgbG9vay11cDogbWVhbmluZyB0aGF0IGdpdmVuIGEgcHJpY2UsIGl0IGlzIGVhc3kgdG8gdGVsbCB3aGljaCBgY3V0YCBoYXMgdGhlIGhpZ2hlc3QgZGVuc2l0eS4gSG93ZXZlciwgdGhlIG92ZXJsYXBwaW5nIGxpbmVzIG1ha2VzIGl0IGRpZmZpY3VsdCB0byBkaXN0aW5ndWlzaCBob3cgdGhlIG92ZXJhbGwgZGlzdHJpYnV0aW9ucyByZWxhdGUgdG8gZWFjaCBvdGhlci4gVGhlIGBnZW9tX3Zpb2xpbigpYCBhbmQgZmFjZXRlZCBgZ2VvbV9oaXN0b2dyYW0oKWAgaGF2ZSBzaW1pbGFyIHN0cmVuZ3RocyBhbmQgd2Vha25lc3Nlcy4gSXQgaXMgZWFzeSB0byB2aXN1YWxseSBkaXN0aW5ndWlzaCBkaWZmZXJlbmNlcyBpbiB0aGUgb3ZlcmFsbCBzaGFwZSBvZiB0aGUgZGlzdHJpYnV0aW9ucyAoc2tld25lc3MsIGNlbnRyYWwgdmFsdWVzLCB2YXJpYW5jZSwgZXRjKS4gSG93ZXZlciwgc2luY2Ugd2UgY2Fu4oCZdCBlYXNpbHkgY29tcGFyZSB0aGUgdmVydGljYWwgdmFsdWVzIG9mIHRoZSBkaXN0cmlidXRpb24sIGl0IGlzIGRpZmZpY3VsdCB0byBsb29rIHVwIHdoaWNoIGNhdGVnb3J5IGhhcyB0aGUgaGlnaGVzdCBkZW5zaXR5IGZvciBhIGdpdmVuIHByaWNlLiBBbGwgb2YgdGhlc2UgbWV0aG9kcyBkZXBlbmQgb24gdHVuaW5nIHBhcmFtZXRlcnMgdG8gZGV0ZXJtaW5lIHRoZSBsZXZlbCBvZiBzbW9vdGhuZXNzIG9mIHRoZSBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gcHJpY2UsIHkgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9mcmVxcG9seShtYXBwaW5nID0gYWVzKGNvbG9yID0gY3V0KSwgYmlud2lkdGggPSA1MDApDQoNCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZmFjZXRfd3JhcCh+Y3V0LCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeSIpDQoNCmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IGN1dCwgeSA9IHByaWNlKSkgKw0KICBnZW9tX3Zpb2xpbigpICsNCiAgY29vcmRfZmxpcCgpDQpgYGANCg0KVGhlIHZpb2xpbiBwbG90IHdhcyBmaXJzdCBkZXNjcmliZWQgaW4gSGludHplIGFuZCBOZWxzb24gKCBbMTk5OF0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwODAvMDAwMzEzMDUuMTk5OC4xMDQ4MDU1OSkgKS4NCg0KIyMjIDYuIElmIHlvdSBoYXZlIGEgc21hbGwgZGF0YXNldCwgaXTigJlzIHNvbWV0aW1lcyB1c2VmdWwgdG8gdXNlIGBnZW9tX2ppdHRlcigpYCB0byBzZWUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgY29udGludW91cyBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGUuIFRoZSBbZ2diZWVzd2FybV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2dnYmVlc3dhcm0vdmVyc2lvbnMvMC41LjMpIHBhY2thZ2UgcHJvdmlkZXMgYSBudW1iZXIgb2YgbWV0aG9kcyBzaW1pbGFyIHRvIGdlb21faml0dGVyKCkuIExpc3QgdGhlbSBhbmQgYnJpZWZseSBkZXNjcmliZSB3aGF0IGVhY2ggb25lIGRvZXMuDQoNClRoZXJlIGFyZSB0d28gbWV0aG9kczoNCg0KIC0gYGdlb21fcXVhc2lyYW5kb20oKWAgcHJvZHVjZXMgcGxvdHMgdGhhdCBhcmUgYSBtaXggb2Ygaml0dGVyIGFuZCB2aW9saW4gcGxvdHMuIFRoZXJlIGFyZSBzZXZlcmFsIGRpZmZlcmVudCBtZXRob2RzIHRoYXQgZGV0ZXJtaW5lIGV4YWN0bHkgaG93IHRoZSByYW5kb20gbG9jYXRpb24gb2YgdGhlIHBvaW50cyBpcyBnZW5lcmF0ZWQuDQogLSBgZ2VvbV9iZWVzd2FybSgpYCBwcm9kdWNlcyBhIHBsb3Qgc2ltaWxhciB0byBhIHZpb2xpbiBwbG90LCBidXQgYnkgb2Zmc2V0dGluZyB0aGUgcG9pbnRzLg0KDQpJ4oCZbGwgdXNlIHRoZSBgbXBnYCBib3ggcGxvdCBleGFtcGxlIHNpbmNlIHRoZXNlIG1ldGhvZHMgZGlzcGxheSBpbmRpdmlkdWFsIHBvaW50cywgdGhleSBhcmUgYmV0dGVyIHN1aXRlZCBmb3Igc21hbGxlciBkYXRhc2V0cy4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IG1wZykgKw0KICBnZW9tX3F1YXNpcmFuZG9tKG1hcHBpbmcgPSBhZXMoDQogICAgeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOID0gbWVkaWFuKSwNCiAgICB5ID0gaHd5DQogICkpDQoNCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fcXVhc2lyYW5kb20oDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHggPSByZW9yZGVyKGNsYXNzLCBod3ksIEZVTiA9IG1lZGlhbiksDQogICAgICB5ID0gaHd5DQogICAgKSwNCiAgICBtZXRob2QgPSAidHVrZXkiDQogICkNCg0KZ2dwbG90KGRhdGEgPSBtcGcpICsNCiAgZ2VvbV9xdWFzaXJhbmRvbSgNCiAgICBtYXBwaW5nID0gYWVzKA0KICAgICAgeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOID0gbWVkaWFuKSwNCiAgICAgIHkgPSBod3kNCiAgICApLA0KICAgIG1ldGhvZCA9ICJ0dWtleURlbnNlIg0KICApDQoNCmdncGxvdChkYXRhID0gbXBnKSArDQogIGdlb21fcXVhc2lyYW5kb20oDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHggPSByZW9yZGVyKGNsYXNzLCBod3ksIEZVTiA9IG1lZGlhbiksDQogICAgICB5ID0gaHd5DQogICAgKSwNCiAgICBtZXRob2QgPSAiZnJvd25leSINCiAgKQ0KDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKw0KICBnZW9tX3F1YXNpcmFuZG9tKA0KICAgIG1hcHBpbmcgPSBhZXMoDQogICAgICB4ID0gcmVvcmRlcihjbGFzcywgaHd5LCBGVU4gPSBtZWRpYW4pLA0KICAgICAgeSA9IGh3eQ0KICAgICksDQogICAgbWV0aG9kID0gInNtaWxleSINCiAgKQ0KDQpnZ3Bsb3QoZGF0YSA9IG1wZykgKw0KICBnZW9tX2JlZXN3YXJtKG1hcHBpbmcgPSBhZXMoDQogICAgeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOID0gbWVkaWFuKSwNCiAgICB5ID0gaHd5DQogICkpDQpgYGA=