### Load packages
library(tidyverse) # Collection of all the good stuff like dplyr, ggplot2 ect.
library(magrittr) # For extra-piping operators (eg. %<>%)

Introduction

Welcome to the applied session in data visualization for Exploratory Data Analysis (EDA) in R.

Introduction to gglot2

ggplot2 can be thought of as a mini-language (domain-specific language) within the R language. It is an R implementation of Wilkinson’s Grammar of Graphics book. A Layered Grammar of Graphics describes Hadley’s implementation of these thoughts in the ggplot2’s design. Due to its conceptional richness as well as the rich functionality provided, ggplot2 has over time become the main sub-ecosustem for rgaphic visualization. Most packages dedicated to specialized forms of visualization (networks, interactions, etc.) will use the ggplot package as underlying plattform. So, it makes sense to dive a bit deeper into it functionality

Conceptually, the main idea behind the Grammar of Graphics is that a statistical graphic is a mapping from variables to aesthetic attributes (x axis value, y axis value, color, shape, size) of geometric objects (points, line, bars).

While the Grammar of Graphic contains more elements, we will focus in this brief intro in the two main ones, aestetics and geometries.

  • Aestetics: Devine the “surface” of your plot, in terms of what has to be mapped (size, coplor) on the x and y (and potentially adittional) axes. Aesteticts are defined within the aes() function.
  • Geometries: Visual elements you can see in the plot itself, such as bars, lines, and points. They are defined within various geom_XYZ() functions.

Basically, you define a surface grid and then plot something on top. We will talk about all of that in depth in later sessions, for now that’s all you need to know to understand the following simple examples.

Application: the BIXI Bikeshare Data dataset

Lets take a step back and zoom a bit into different forms of visualization. We will now take a look at the BIXI Bikeshare Data, covering 500k bike-rides in the BIXI bike-sharing system in Montreal.

bike <- readRDS(url("https://github.com/SDS-AAU/SDS-master/raw/master/00_data/bikes_montreal.rds?dl=1"))

Lets take a look:

bike %>% glimpse()
Rows: 500,000
Columns: 12
$ start_date         <dttm> 2017-08-16 12:10:00, 2017-06-25 23:22:00, 2017-08-10 17:26:00, 2017-08-17 15:25:00, 2017-10-12 10:39:00, 2017-07-03 08:53:00, 2…
$ start_station_code <int> 6213, 6393, 6114, 6044, 6389, 6411, 6738, 6425, 7042, 6034, 6213, 6184, 6008, 6202, 6048, 6087, 6195, 6013, 6168, 6154, 6901, 62…
$ end_date           <dttm> 2017-08-16 12:30:00, 2017-06-25 23:27:00, 2017-08-10 17:29:00, 2017-08-17 15:32:00, 2017-10-12 10:49:00, 2017-07-03 08:55:00, 2…
$ end_station_code   <int> 6391, 6394, 6113, 6015, 6262, 6206, 6090, 6406, 6185, 6039, 6188, 6142, 6012, 6038, 6020, 6032, 7080, 6078, 6302, 6164, 6011, 62…
$ duration_sec       <int> 1237, 294, 156, 419, 601, 108, 438, 757, 1144, 578, 366, 326, 154, 835, 863, 565, 929, 262, 633, 559, 1071, 120, 847, 1223, 287,…
$ start_day          <date> 2017-08-16, 2017-06-25, 2017-08-10, 2017-08-17, 2017-10-12, 2017-07-03, 2017-07-04, 2017-06-27, 2017-11-09, 2017-07-03, 2017-05…
$ start_dow          <fct> Wed, Sun, Thu, Thu, Thu, Mon, Tue, Tue, Thu, Mon, Wed, Thu, Wed, Thu, Wed, Wed, Mon, Sun, Thu, Wed, Thu, Wed, Sun, Sat, Sun, Thu…
$ weekday            <fct> workweek, weekend, workweek, workweek, workweek, workweek, workweek, workweek, workweek, workweek, workweek, workweek, workweek,…
$ start_hod          <dbl> 12, 23, 17, 15, 10, 8, 16, 17, 9, 17, 16, 8, 10, 18, 7, 11, 8, 15, 23, 16, 8, 22, 9, 22, 19, 17, 14, 18, 15, 14, 21, 20, 14, 17,…
$ start_mon          <dbl> 8, 6, 8, 8, 10, 7, 7, 6, 11, 7, 5, 6, 8, 8, 10, 7, 8, 10, 6, 5, 8, 5, 4, 8, 6, 5, 9, 4, 10, 8, 9, 10, 8, 5, 5, 6, 7, 6, 6, 6, 10…
$ start_wk           <dbl> 33, 26, 32, 33, 41, 27, 27, 26, 45, 27, 22, 26, 33, 32, 40, 30, 33, 41, 26, 19, 35, 22, 17, 33, 23, 19, 39, 16, 42, 33, 37, 43, …
$ membership         <fct> member, non-member, member, member, member, member, member, member, member, member, member, member, member, member, member, memb…
bike %>% head()

We see here a number of different variable types present, namely:

  • Continuous variables
  • Categorical variables
  • Temporal variables

First of all: Lets remember, the first thing we do is defining the aestetics, first of all the dimensions (x, y) of the visualization.

bike %>% ggplot(aes(x = weekday, y = start_hod)) 

The result will be an empty plane with the dimensions we defined. Note that there are more aestetic dimensions which can be used to convey informations visualy, such as for instance:

  • Position (x, y)
  • Color
  • Shape
  • Alpha (Transparency)

We will explore them later.

Basic visualization of variable types

Summaries of One Variable: Continuous

When attempting to summarize a single variable, histograms and density distributions are often the visualization of choice. We can do that easily by using the geom_histogram() layer. Notice that we only define a x aestetic, since we only summarize one variable

bike %>% ggplot(aes(x = duration_sec)) +
  geom_histogram()

To plot a probability density function (PDF) instead, we can use the geom_density() layer.

bike %>% ggplot(aes(x = duration_sec)) +
  geom_density()

Note the distribution appears right-skewed, since we have some outliers of very long bike rides. Adding a log-scale on the x-axis might help to reduce their impact on the visualization.

bike %>% ggplot(aes(x = duration_sec)) +
  geom_histogram() +
  scale_x_log10() 

In case we would already like to start looking at conditional distributions, we could add an adittional fill aestetic.

bike %>% ggplot(aes(x = duration_sec, fill = weekday)) +
  geom_histogram() +
  scale_x_log10() 

Summaries of One Variable: Discrete

To do the same for a discrete variable, we would start with a simple barplot via geom_bar(). Notice again that we only define a x aestetic. ggplot per default will use the count on the y-axis.

bike %>% ggplot(aes(x = start_dow)) +
  geom_bar()

We could also use the membership as fill aestetic to map further information in the plot.

bike %>% ggplot(aes(x = start_dow, fill = membership)) +
  geom_bar()

Summaries of One Variable: Temporal

A temporal variable can also be visualized as a line-plot with geom_line().

bike %>%
  count(start_wk) %>%
  ggplot(aes(x = start_wk, y = n)) +
  geom_line()

To instead (or in addition) add a trendline, we can use geom_smooth()

bike %>%
  count(start_wk) %>%
  ggplot(aes(x = start_wk, y = n)) +
  geom_smooth()

Summarizing multiple variablea jointly

Ok, that was pretty easy. However, the insights gained so far are pretty little. To tease out interesting pattern in our data, it might not be enough to only look at one variable at a time. To display relationships between multiple variables, we mainly can:

  • Use aestetics such as color, fill, size, shape (alter the aestetics within one plot)
  • Use facet_wrap()(produce multiple plots)

Lets look at some examples:

First, we could take a look at the number of daily rides with workweek / weekend days colored differently.

# Compute daily counts & plot
bike %>%
  count(start_day, weekday) %>%
  ggplot(aes(start_day, n, color = weekday)) +
  geom_point()

Now let’s look at how rides are distributed according to the time of day. Let’s make a summary plot of weekly ride counts faceted by start hour of day and broken down by workweek/weekend. Here, we will use the facet_grid

# Compute week_hod & plot
bike %>%
  count(start_wk, start_hod, weekday) %>%
  ggplot(aes(start_wk, n, color = weekday)) +
  geom_point() +
  facet_grid(~ start_hod) +
  scale_y_sqrt()

Expanding on the previous plot, let’s add one more variable into our summary, adding a facet dimension for whether or not the rider is a member of BIXI.

# Compute wk_memb_hod & plot
bike %>%
  count(start_wk, start_hod, weekday, membership) %>%
  ggplot(aes(start_wk, n, color = weekday)) +
  geom_point() +
  facet_grid(membership ~ start_hod) +
  scale_y_sqrt()

Let’s now look at the number of rides vs. hour for each day. To start, we’ll create a summary dataset for the first full month in the dataset (May) and look at it.

# Compute daily_may & 
bike %>%
  filter(start_mon == 5) %>%
  count(start_day, start_hod, membership) %>%
  ggplot(aes(start_hod, n, color = membership)) +
  geom_point() +
  facet_wrap(~ start_day, ncol = 7)

Endnotes

References

Suggestions for further study

Own exploration

There is so much more to explore. However, since time is limited, I will leave it up to you to explore more.

  • Take a moment to review the different geoms offered by ggplot here.
  • For inspiration what can be done, check here.
  • Check ggplot2 addons here. Some of my favorite are:
    • ggforce: For a collection of adittional features
    • patchwork: For easy inegration of multiple plots jointly
    • GGally: Collection fo many cool plotting features, including many standard stats plot for correlation, distribution etc.
    • ggmap: For geoplotting
    • ggraph: For network plots (will be handled later)
    • ggridges: Ridge features, for example to create joy-plots
    • ggalluvial: For alluvial plots

Datacamp

Other online courses

Papers, Ebooks & chapters

Session Info

sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.6

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] plotly_4.9.2.1   patchwork_1.0.1  ggpubr_0.4.0     GGally_2.0.0     kableExtra_1.1.0 knitr_1.29       magrittr_1.5     forcats_0.5.0    stringr_1.4.0   
[10] dplyr_1.0.2      purrr_0.3.4      readr_1.3.1      tidyr_1.1.1      tibble_3.0.3     ggplot2_3.3.2    tidyverse_1.3.0 

loaded via a namespace (and not attached):
 [1] nlme_3.1-149       fs_1.5.0           lubridate_1.7.9    webshot_0.5.2      RColorBrewer_1.1-2 progress_1.2.2     httr_1.4.2         tools_4.0.2       
 [9] backports_1.1.8    utf8_1.1.4         R6_2.4.1           mgcv_1.8-32        DBI_1.1.0          lazyeval_0.2.2     colorspace_1.4-1   withr_2.2.0       
[17] tidyselect_1.1.0   prettyunits_1.1.1  curl_4.3           compiler_4.0.2     cli_2.0.2          rvest_0.3.6        pacman_0.5.1       xml2_1.3.2        
[25] labeling_0.3       scales_1.1.1       digest_0.6.25      foreign_0.8-80     rmarkdown_2.3      rio_0.5.16         base64enc_0.1-3    pkgconfig_2.0.3   
[33] htmltools_0.5.0    fastmap_1.0.1      dbplyr_1.4.4       htmlwidgets_1.5.1  rlang_0.4.7        readxl_1.3.1       rstudioapi_0.11    shiny_1.5.0       
[41] farver_2.0.3       generics_0.0.2     jsonlite_1.7.0     crosstalk_1.1.0.1  zip_2.1.0          car_3.0-9          Matrix_1.2-18      Rcpp_1.0.5        
[49] munsell_0.5.0      fansi_0.4.1        abind_1.4-5        lifecycle_0.2.0    stringi_1.4.6      yaml_2.2.1         carData_3.0-4      plyr_1.8.6        
[57] grid_4.0.2         blob_1.2.1         promises_1.1.1     crayon_1.3.4       lattice_0.20-41    splines_4.0.2      haven_2.3.1        hms_0.5.3         
[65] pillar_1.4.6       ggsignif_0.6.0     reprex_0.3.0       glue_1.4.1         evaluate_0.14      data.table_1.13.0  modelr_0.1.8       vctrs_0.3.2       
[73] httpuv_1.5.4       cellranger_1.1.0   gtable_0.3.0       reshape_0.8.8      assertthat_0.2.1   xfun_0.16          openxlsx_4.1.5     mime_0.9          
[81] xtable_1.8-4       broom_0.7.0        rstatix_0.6.0      later_1.1.0.1      rsconnect_0.8.16   viridisLite_0.3.0  ellipsis_0.3.1    
LS0tCnRpdGxlOiAnQXBwbGljYXRpb246IERhdGEgVmlzdWFsaXphdGlvbiBpbiBSJwphdXRob3I6ICJEYW5pZWwgUy4gSGFpbiAoZHNoQGJ1c2luZXNzLmFhdS5kaykiCmRhdGU6ICJVcGRhdGVkIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICB0aGVtZTogZmxhdGx5Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgS25pdHIgb3B0aW9ucwojIyMgR2VuZXJpYyBwcmVhbWJsZQpTeXMuc2V0ZW52KExBTkcgPSAiZW4iKSAjIEZvciBlbmdsaXNoIGxhbmd1YWdlCm9wdGlvbnMoc2NpcGVuID0gNSkgIyBUbyBkZWFjdGl2YXRlIGFubm95aW5nIHNjaWVudGlmaWMgbnVtYmVyIG5vdGF0aW9uCgojIHJtKGxpc3Q9bHMoKSk7IGdyYXBoaWNzLm9mZigpICMgZ2V0IHJpZCBvZiBldmVyeXRoaW5nIGluIHRoZSB3b3Jrc3BhY2UKaWYgKCFyZXF1aXJlKCJrbml0ciIpKSBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpOyBsaWJyYXJ5KGtuaXRyKSAjIEZvciBkaXNwbGF5IG9mIHRoZSBtYXJrZG93bgoKIyMjIEtuaXRyIG9wdGlvbnMKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmc9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0iY2VudGVyIgogICAgICAgICAgICAgICAgICAgICApCmBgYAoKYGBge3J9CiMjIyBMb2FkIHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKSAjIENvbGxlY3Rpb24gb2YgYWxsIHRoZSBnb29kIHN0dWZmIGxpa2UgZHBseXIsIGdncGxvdDIgZWN0LgpsaWJyYXJ5KG1hZ3JpdHRyKSAjIEZvciBleHRyYS1waXBpbmcgb3BlcmF0b3JzIChlZy4gJTw+JSkKYGBgCgoKIyBJbnRyb2R1Y3Rpb24gCgpXZWxjb21lIHRvIHRoZSBhcHBsaWVkIHNlc3Npb24gaW4gZGF0YSB2aXN1YWxpemF0aW9uIGZvciBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpIGluIGBSYC4KCiMgSW50cm9kdWN0aW9uIHRvIGBnZ2xvdDJgCiAKW2BnZ3Bsb3QyYF0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvKSBjYW4gYmUgdGhvdWdodCBvZiBhcyBhIG1pbmktbGFuZ3VhZ2UgKGRvbWFpbi1zcGVjaWZpYyBsYW5ndWFnZSkgd2l0aGluIHRoZSBgUmAgbGFuZ3VhZ2UuIEl0IGlzIGFuIFIgaW1wbGVtZW50YXRpb24gb2YgW1dpbGtpbnNvbidzIEdyYW1tYXIgb2YgR3JhcGhpY3MgYm9va10oaHR0cHM6Ly93d3cuc3ByaW5nZXIuY29tL2dwL2Jvb2svOTc4MDM4NzI0NTQ0NykuIFtBIExheWVyZWQgR3JhbW1hciBvZiBHcmFwaGljc10oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIucGRmKSBkZXNjcmliZXMgSGFkbGV5J3MgaW1wbGVtZW50YXRpb24gb2YgdGhlc2UgdGhvdWdodHMgaW4gdGhlIGdncGxvdDIncyBkZXNpZ24uIER1ZSB0byBpdHMgY29uY2VwdGlvbmFsIHJpY2huZXNzIGFzIHdlbGwgYXMgdGhlIHJpY2ggZnVuY3Rpb25hbGl0eSBwcm92aWRlZCwgYGdncGxvdDJgIGhhcyBvdmVyIHRpbWUgYmVjb21lIHRoZSBtYWluIHN1Yi1lY29zdXN0ZW0gZm9yIHJnYXBoaWMgdmlzdWFsaXphdGlvbi4gTW9zdCBwYWNrYWdlcyBkZWRpY2F0ZWQgdG8gc3BlY2lhbGl6ZWQgZm9ybXMgb2YgdmlzdWFsaXphdGlvbiAobmV0d29ya3MsIGludGVyYWN0aW9ucywgZXRjLikgd2lsbCB1c2UgdGhlIGBnZ3Bsb3RgIHBhY2thZ2UgYXMgdW5kZXJseWluZyBwbGF0dGZvcm0uIFNvLCBpdCBtYWtlcyBzZW5zZSB0byBkaXZlIGEgYml0IGRlZXBlciBpbnRvIGl0IGZ1bmN0aW9uYWxpdHkKCkNvbmNlcHR1YWxseSwgdGhlIG1haW4gaWRlYSBiZWhpbmQgdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MgaXMgdGhhdCBhIHN0YXRpc3RpY2FsIGdyYXBoaWMgaXMgYSBtYXBwaW5nIGZyb20gdmFyaWFibGVzIHRvIGFlc3RoZXRpYyBhdHRyaWJ1dGVzICh4IGF4aXMgdmFsdWUsIHkgYXhpcyB2YWx1ZSwgY29sb3IsIHNoYXBlLCBzaXplKSBvZiBnZW9tZXRyaWMgb2JqZWN0cyAocG9pbnRzLCBsaW5lLCBiYXJzKS4gCgogV2hpbGUgdGhlIEdyYW1tYXIgb2YgR3JhcGhpYyBjb250YWlucyBtb3JlIGVsZW1lbnRzLCB3ZSB3aWxsIGZvY3VzIGluIHRoaXMgYnJpZWYgaW50cm8gaW4gdGhlIHR3byBtYWluIG9uZXMsIGFlc3RldGljcyBhbmQgZ2VvbWV0cmllcy4gCgoqICoqQWVzdGV0aWNzOioqIERldmluZSB0aGUgInN1cmZhY2UiIG9mIHlvdXIgcGxvdCwgaW4gdGVybXMgb2Ygd2hhdCBoYXMgdG8gYmUgbWFwcGVkIChzaXplLCBjb3Bsb3IpIG9uIHRoZSB4IGFuZCB5IChhbmQgcG90ZW50aWFsbHkgYWRpdHRpb25hbCkgYXhlcy4gQWVzdGV0aWN0cyBhcmUgZGVmaW5lZCB3aXRoaW4gdGhlIGBhZXMoKWAgZnVuY3Rpb24uCiogKipHZW9tZXRyaWVzOioqIFZpc3VhbCBlbGVtZW50cyB5b3UgY2FuIHNlZSBpbiB0aGUgcGxvdCBpdHNlbGYsIHN1Y2ggYXMgYmFycywgbGluZXMsIGFuZCBwb2ludHMuIFRoZXkgYXJlIGRlZmluZWQgd2l0aGluIHZhcmlvdXMgYGdlb21fWFlaKClgIGZ1bmN0aW9ucy4KCiFbXShodHRwczovL2dpdGh1Yi5jb20vU0RTLUFBVS9TRFMtbWFzdGVyL3Jhdy9tYXN0ZXIvMDBfbWVkaWEvZ2dwbG90X3N0cnVjdHVyZS5wbmcpCgpCYXNpY2FsbHksIHlvdSBkZWZpbmUgYSBzdXJmYWNlIGdyaWQgYW5kIHRoZW4gcGxvdCBzb21ldGhpbmcgb24gdG9wLiBXZSB3aWxsIHRhbGsgYWJvdXQgYWxsIG9mIHRoYXQgaW4gZGVwdGggaW4gbGF0ZXIgc2Vzc2lvbnMsIGZvciBub3cgdGhhdCdzIGFsbCB5b3UgbmVlZCB0byBrbm93IHRvIHVuZGVyc3RhbmQgdGhlIGZvbGxvd2luZyBzaW1wbGUgZXhhbXBsZXMuCgojIEFwcGxpY2F0aW9uOiB0aGUgYEJJWEkgQmlrZXNoYXJlIERhdGFgIGRhdGFzZXQKCkxldHMgdGFrZSBhIHN0ZXAgYmFjayBhbmQgem9vbSBhIGJpdCBpbnRvIGRpZmZlcmVudCBmb3JtcyBvZiB2aXN1YWxpemF0aW9uLiBXZSB3aWxsIG5vdyB0YWtlIGEgbG9vayBhdCB0aGUgYEJJWEkgQmlrZXNoYXJlIERhdGFgLCBjb3ZlcmluZyA1MDBrIGJpa2UtcmlkZXMgaW4gdGhlIEJJWEkgYmlrZS1zaGFyaW5nIHN5c3RlbSBpbiBNb250cmVhbC4KCmBgYHtyfQpiaWtlIDwtIHJlYWRSRFModXJsKCJodHRwczovL2dpdGh1Yi5jb20vU0RTLUFBVS9TRFMtbWFzdGVyL3Jhdy9tYXN0ZXIvMDBfZGF0YS9iaWtlc19tb250cmVhbC5yZHMiKSkKYGBgCgpMZXRzIHRha2UgYSBsb29rOgoKYGBge3J9CmJpa2UgJT4lIGdsaW1wc2UoKQpgYGAKCmBgYHtyfQpiaWtlICU+JSBoZWFkKCkKYGBgCgpXZSBzZWUgaGVyZSBhIG51bWJlciBvZiBkaWZmZXJlbnQgdmFyaWFibGUgdHlwZXMgcHJlc2VudCwgbmFtZWx5OgoKKiBDb250aW51b3VzIHZhcmlhYmxlcwoqIENhdGVnb3JpY2FsIHZhcmlhYmxlcwoqIFRlbXBvcmFsIHZhcmlhYmxlcwoKRmlyc3Qgb2YgYWxsOiBMZXRzIHJlbWVtYmVyLCB0aGUgZmlyc3QgdGhpbmcgd2UgZG8gaXMgZGVmaW5pbmcgdGhlIGFlc3RldGljcywgZmlyc3Qgb2YgYWxsIHRoZSBkaW1lbnNpb25zICh4LCB5KSBvZiB0aGUgdmlzdWFsaXphdGlvbi4KCmBgYHtyfQpiaWtlICU+JSBnZ3Bsb3QoYWVzKHggPSB3ZWVrZGF5LCB5ID0gc3RhcnRfaG9kKSkgCmBgYAoKVGhlIHJlc3VsdCB3aWxsIGJlIGFuIGVtcHR5IHBsYW5lIHdpdGggdGhlIGRpbWVuc2lvbnMgd2UgZGVmaW5lZC4gTm90ZSB0aGF0IHRoZXJlIGFyZSBtb3JlIGFlc3RldGljIGRpbWVuc2lvbnMgd2hpY2ggY2FuIGJlIHVzZWQgdG8gY29udmV5IGluZm9ybWF0aW9ucyB2aXN1YWx5LCBzdWNoIGFzIGZvciBpbnN0YW5jZToKCiogUG9zaXRpb24gKHgsIHkpCiogQ29sb3IKKiBTaGFwZQoqIEFscGhhIChUcmFuc3BhcmVuY3kpCgpXZSB3aWxsIGV4cGxvcmUgdGhlbSBsYXRlci4KCiMjIEJhc2ljIHZpc3VhbGl6YXRpb24gb2YgdmFyaWFibGUgdHlwZXMKCiMjIyBTdW1tYXJpZXMgb2YgT25lIFZhcmlhYmxlOiBDb250aW51b3VzCgpXaGVuIGF0dGVtcHRpbmcgdG8gc3VtbWFyaXplIGEgc2luZ2xlIHZhcmlhYmxlLCBoaXN0b2dyYW1zIGFuZCBkZW5zaXR5IGRpc3RyaWJ1dGlvbnMgYXJlIG9mdGVuIHRoZSB2aXN1YWxpemF0aW9uIG9mIGNob2ljZS4gV2UgY2FuIGRvIHRoYXQgZWFzaWx5IGJ5IHVzaW5nIHRoZSBgZ2VvbV9oaXN0b2dyYW0oKWAgbGF5ZXIuIE5vdGljZSB0aGF0IHdlIG9ubHkgZGVmaW5lIGEgYHhgIGFlc3RldGljLCBzaW5jZSB3ZSBvbmx5IHN1bW1hcml6ZSBvbmUgdmFyaWFibGUKCmBgYHtyfQpiaWtlICU+JSBnZ3Bsb3QoYWVzKHggPSBkdXJhdGlvbl9zZWMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKClRvIHBsb3QgYSBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIChQREYpIGluc3RlYWQsIHdlIGNhbiB1c2UgdGhlIGBnZW9tX2RlbnNpdHkoKWAgbGF5ZXIuCgpgYGB7cn0KYmlrZSAlPiUgZ2dwbG90KGFlcyh4ID0gZHVyYXRpb25fc2VjKSkgKwogIGdlb21fZGVuc2l0eSgpCmBgYAoKTm90ZSB0aGUgZGlzdHJpYnV0aW9uIGFwcGVhcnMgcmlnaHQtc2tld2VkLCBzaW5jZSB3ZSBoYXZlIHNvbWUgb3V0bGllcnMgb2YgdmVyeSBsb25nIGJpa2UgcmlkZXMuIEFkZGluZyBhIGxvZy1zY2FsZSBvbiB0aGUgeC1heGlzIG1pZ2h0IGhlbHAgdG8gcmVkdWNlIHRoZWlyIGltcGFjdCBvbiB0aGUgdmlzdWFsaXphdGlvbi4KCgpgYGB7cn0KYmlrZSAlPiUgZ2dwbG90KGFlcyh4ID0gZHVyYXRpb25fc2VjKSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIHNjYWxlX3hfbG9nMTAoKSAKYGBgCgpJbiBjYXNlIHdlIHdvdWxkIGFscmVhZHkgbGlrZSB0byBzdGFydCBsb29raW5nIGF0IGNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbnMsIHdlIGNvdWxkIGFkZCBhbiBhZGl0dGlvbmFsIGBmaWxsYCBhZXN0ZXRpYy4KCmBgYHtyfQpiaWtlICU+JSBnZ3Bsb3QoYWVzKHggPSBkdXJhdGlvbl9zZWMsIGZpbGwgPSB3ZWVrZGF5KSkgKwogIGdlb21faGlzdG9ncmFtKCkgKwogIHNjYWxlX3hfbG9nMTAoKSAKYGBgCgojIyMgU3VtbWFyaWVzIG9mIE9uZSBWYXJpYWJsZTogRGlzY3JldGUKClRvIGRvIHRoZSBzYW1lIGZvciBhIGRpc2NyZXRlIHZhcmlhYmxlLCB3ZSB3b3VsZCBzdGFydCB3aXRoIGEgc2ltcGxlIGJhcnBsb3QgdmlhIGBnZW9tX2JhcigpYC4gTm90aWNlIGFnYWluIHRoYXQgd2Ugb25seSBkZWZpbmUgYSB4IGFlc3RldGljLiBgZ2dwbG90YCBwZXIgZGVmYXVsdCB3aWxsIHVzZSB0aGUgY291bnQgb24gdGhlIHktYXhpcy4KCmBgYHtyfQpiaWtlICU+JSBnZ3Bsb3QoYWVzKHggPSBzdGFydF9kb3cpKSArCiAgZ2VvbV9iYXIoKQpgYGAKCldlIGNvdWxkIGFsc28gdXNlIHRoZSBtZW1iZXJzaGlwIGFzIGZpbGwgYWVzdGV0aWMgdG8gbWFwIGZ1cnRoZXIgaW5mb3JtYXRpb24gaW4gdGhlIHBsb3QuCgpgYGB7cn0KYmlrZSAlPiUgZ2dwbG90KGFlcyh4ID0gc3RhcnRfZG93LCBmaWxsID0gbWVtYmVyc2hpcCkpICsKICBnZW9tX2JhcigpCmBgYAoKIyMjIFN1bW1hcmllcyBvZiBPbmUgVmFyaWFibGU6IFRlbXBvcmFsCgpBIHRlbXBvcmFsIHZhcmlhYmxlIGNhbiBhbHNvIGJlIHZpc3VhbGl6ZWQgYXMgYSBsaW5lLXBsb3Qgd2l0aCBgZ2VvbV9saW5lKClgLgoKYGBge3J9CmJpa2UgJT4lCiAgY291bnQoc3RhcnRfd2spICU+JQogIGdncGxvdChhZXMoeCA9IHN0YXJ0X3drLCB5ID0gbikpICsKICBnZW9tX2xpbmUoKQpgYGAKClRvIGluc3RlYWQgKG9yIGluIGFkZGl0aW9uKSBhZGQgYSB0cmVuZGxpbmUsIHdlIGNhbiB1c2UgYGdlb21fc21vb3RoKClgCgpgYGB7cn0KYmlrZSAlPiUKICBjb3VudChzdGFydF93aykgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc3RhcnRfd2ssIHkgPSBuKSkgKwogIGdlb21fc21vb3RoKCkKYGBgCgoKIyMgU3VtbWFyaXppbmcgbXVsdGlwbGUgdmFyaWFibGVhIGpvaW50bHkKCk9rLCB0aGF0IHdhcyBwcmV0dHkgZWFzeS4gSG93ZXZlciwgdGhlIGluc2lnaHRzIGdhaW5lZCBzbyBmYXIgYXJlIHByZXR0eSBsaXR0bGUuIFRvIHRlYXNlIG91dCBpbnRlcmVzdGluZyBwYXR0ZXJuIGluIG91ciBkYXRhLCBpdCBtaWdodCBub3QgYmUgZW5vdWdoIHRvIG9ubHkgbG9vayBhdCBvbmUgdmFyaWFibGUgYXQgYSB0aW1lLiBUbyBkaXNwbGF5IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBtdWx0aXBsZSB2YXJpYWJsZXMsIHdlIG1haW5seSBjYW46CgoqIFVzZSBhZXN0ZXRpY3Mgc3VjaCBhcyBgY29sb3JgLCBgZmlsbGAsIGBzaXplYCwgYHNoYXBlYCAoYWx0ZXIgdGhlIGFlc3RldGljcyB3aXRoaW4gb25lIHBsb3QpCiogVXNlIGBmYWNldF93cmFwKClgKHByb2R1Y2UgIG11bHRpcGxlIHBsb3RzKQoKTGV0cyBsb29rIGF0IHNvbWUgZXhhbXBsZXM6CgpGaXJzdCwgd2UgY291bGQgdGFrZSBhIGxvb2sgYXQgdGhlIG51bWJlciBvZiBkYWlseSByaWRlcyB3aXRoICB3b3Jrd2VlayAvIHdlZWtlbmQgZGF5cyBjb2xvcmVkIGRpZmZlcmVudGx5LgoKYGBge3J9CiMgQ29tcHV0ZSBkYWlseSBjb3VudHMgJiBwbG90CmJpa2UgJT4lCiAgY291bnQoc3RhcnRfZGF5LCB3ZWVrZGF5KSAlPiUKICBnZ3Bsb3QoYWVzKHN0YXJ0X2RheSwgbiwgY29sb3IgPSB3ZWVrZGF5KSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCk5vdyBsZXQncyBsb29rIGF0IGhvdyByaWRlcyBhcmUgZGlzdHJpYnV0ZWQgYWNjb3JkaW5nIHRvIHRoZSB0aW1lIG9mIGRheS4gTGV0J3MgbWFrZSBhIHN1bW1hcnkgcGxvdCBvZiB3ZWVrbHkgcmlkZSBjb3VudHMgZmFjZXRlZCBieSBzdGFydCBob3VyIG9mIGRheSBhbmQgYnJva2VuIGRvd24gYnkgd29ya3dlZWsvd2Vla2VuZC4gSGVyZSwgd2Ugd2lsbCB1c2UgdGhlIGBmYWNldF9ncmlkYAoKCmBgYHtyLGZpZy5oZWlnaHQ9NSAsIGZpZy53aWR0aD0xNX0KIyBDb21wdXRlIHdlZWtfaG9kICYgcGxvdApiaWtlICU+JQogIGNvdW50KHN0YXJ0X3drLCBzdGFydF9ob2QsIHdlZWtkYXkpICU+JQogIGdncGxvdChhZXMoc3RhcnRfd2ssIG4sIGNvbG9yID0gd2Vla2RheSkpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X2dyaWQofiBzdGFydF9ob2QpICsKICBzY2FsZV95X3NxcnQoKQpgYGAKCkV4cGFuZGluZyBvbiB0aGUgcHJldmlvdXMgcGxvdCwgbGV0J3MgYWRkIG9uZSBtb3JlIHZhcmlhYmxlIGludG8gb3VyIHN1bW1hcnksIGFkZGluZyBhIGZhY2V0IGRpbWVuc2lvbiBmb3Igd2hldGhlciBvciBub3QgdGhlIHJpZGVyIGlzIGEgbWVtYmVyIG9mIEJJWEkuCgpgYGB7cixmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTE1fQojIENvbXB1dGUgd2tfbWVtYl9ob2QgJiBwbG90CmJpa2UgJT4lCiAgY291bnQoc3RhcnRfd2ssIHN0YXJ0X2hvZCwgd2Vla2RheSwgbWVtYmVyc2hpcCkgJT4lCiAgZ2dwbG90KGFlcyhzdGFydF93aywgbiwgY29sb3IgPSB3ZWVrZGF5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfZ3JpZChtZW1iZXJzaGlwIH4gc3RhcnRfaG9kKSArCiAgc2NhbGVfeV9zcXJ0KCkKYGBgCgpMZXQncyBub3cgbG9vayBhdCB0aGUgbnVtYmVyIG9mIHJpZGVzIHZzLiBob3VyIGZvciBlYWNoIGRheS4gVG8gc3RhcnQsIHdlJ2xsIGNyZWF0ZSBhIHN1bW1hcnkgZGF0YXNldCBmb3IgdGhlIGZpcnN0IGZ1bGwgbW9udGggaW4gdGhlIGRhdGFzZXQgKE1heSkgYW5kIGxvb2sgYXQgaXQuCgpgYGB7cixmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTE1fQojIENvbXB1dGUgZGFpbHlfbWF5ICYgCmJpa2UgJT4lCiAgZmlsdGVyKHN0YXJ0X21vbiA9PSA1KSAlPiUKICBjb3VudChzdGFydF9kYXksIHN0YXJ0X2hvZCwgbWVtYmVyc2hpcCkgJT4lCiAgZ2dwbG90KGFlcyhzdGFydF9ob2QsIG4sIGNvbG9yID0gbWVtYmVyc2hpcCkpICsKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAofiBzdGFydF9kYXksIG5jb2wgPSA3KQpgYGAKCiMgRW5kbm90ZXMKCiMjIyBSZWZlcmVuY2VzCgojIyMgU3VnZ2VzdGlvbnMgZm9yIGZ1cnRoZXIgc3R1ZHkKCiMjIyMgT3duIGV4cGxvcmF0aW9uClRoZXJlIGlzIHNvIG11Y2ggbW9yZSB0byBleHBsb3JlLiBIb3dldmVyLCBzaW5jZSB0aW1lIGlzIGxpbWl0ZWQsIEkgd2lsbCBsZWF2ZSBpdCB1cCB0byB5b3UgdG8gZXhwbG9yZSBtb3JlLiAKCiogVGFrZSBhIG1vbWVudCB0byByZXZpZXcgdGhlIGRpZmZlcmVudCBnZW9tcyBvZmZlcmVkIGJ5IGBnZ3Bsb3RgIFtoZXJlXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCkuIAoqIEZvciBpbnNwaXJhdGlvbiB3aGF0IGNhbiBiZSBkb25lLCBjaGVjayBbaGVyZV0oaHR0cDovL3Itc3RhdGlzdGljcy5jby9Ub3A1MC1HZ3Bsb3QyLVZpc3VhbGl6YXRpb25zLU1hc3Rlckxpc3QtUi1Db2RlLmh0bWwpLgoqIENoZWNrIGBnZ3Bsb3QyYCBhZGRvbnMgIFtoZXJlXShodHRwczovL2V4dHMuZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykuIFNvbWUgb2YgbXkgZmF2b3JpdGUgYXJlOgogICAqIGBnZ2ZvcmNlYDogRm9yIGEgY29sbGVjdGlvbiBvZiBhZGl0dGlvbmFsIGZlYXR1cmVzCiAgICogYHBhdGNod29ya2A6IEZvciBlYXN5IGluZWdyYXRpb24gb2YgbXVsdGlwbGUgcGxvdHMgam9pbnRseQogICAqIGBHR2FsbHlgOiBDb2xsZWN0aW9uIGZvIG1hbnkgY29vbCBwbG90dGluZyBmZWF0dXJlcywgaW5jbHVkaW5nIG1hbnkgc3RhbmRhcmQgc3RhdHMgcGxvdCBmb3IgY29ycmVsYXRpb24sIGRpc3RyaWJ1dGlvbiBldGMuCiAgICogYGdnbWFwYDogRm9yIGdlb3Bsb3R0aW5nCiAgICogYGdncmFwaGA6IEZvciBuZXR3b3JrIHBsb3RzICh3aWxsIGJlIGhhbmRsZWQgbGF0ZXIpCiAgICogYGdncmlkZ2VzYDogUmlkZ2UgZmVhdHVyZXMsIGZvciBleGFtcGxlIHRvIGNyZWF0ZSBqb3ktcGxvdHMKICAgKiBgZ2dhbGx1dmlhbGA6IEZvciBhbGx1dmlhbCBwbG90cwogICAKIyMjIyBEYXRhY2FtcAoKKiBbSW50cm9kdWN0aW9uIHRvIERhdGEgVmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDJdKGh0dHBzOi8vbGVhcm4uZGF0YWNhbXAuY29tL2NvdXJzZXMvaW50cm9kdWN0aW9uLXRvLWRhdGEtdmlzdWFsaXphdGlvbi13aXRoLWdncGxvdDIpOiBSZWFsbHkgZ29vZCBhbmQgdGhyb3Jyb3VnaCBnZ3Bsb3QyIGludHJvZHVjdGlvbi4gUmljayBoYXMgYWxzbyBtb3JlIGFkdmFuY2VkIGdncGxvdCBjb3Vyc2VzIGZvciB0aGUgb25lcyB3aG8gd2FudCB0byBnbyBkZWVwZXIuCiogW2h0dHBzOi8vbGVhcm4uZGF0YWNhbXAuY29tL2NvdXJzZXMvY29tbXVuaWNhdGluZy13aXRoLWRhdGEtaW4tdGhlLXRpZHl2ZXJzZV0oQ29tbXVuaWNhdGluZyB3aXRoIERhdGEgaW4gdGhlIFRpZHl2ZXJzZSk6IEFsc28gaW5jbHVkZXMgbW9yZSB3b3JrZmxvd3MgZm9yIHVzaW5nIGRhdGF2aXogZm9yIHJlcG9ydGluZyAmIGNvbW11bmljYXRpbmcgdG8gZGlmZmVyZW50IGF1ZGllbmNlcywgZWcuIHVzaW5nIFJtYXJrZG93bi4KKiBbSW50ZXJhY3RpdmUgRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggcGxvdGx5IGluIFJdKGh0dHBzOi8vbGVhcm4uZGF0YWNhbXAuY29tL2NvdXJzZXMvaW50ZXJhY3RpdmUtZGF0YS12aXN1YWxpemF0aW9uLXdpdGgtcGxvdGx5LWluLXIpOiBGb3IgdGhlIG9uZXMgd2hvIHdhbnQgdG8gZ28gZGVlcGVyIGludG8gaW50ZXJhY3RpdmUgcGxvdHRpbmcuCgojIyMjIE90aGVyIG9ubGluZSBjb3Vyc2VzCgoqIFtEYXRhIFZpc3VhbGl6YXRpb25dKGh0dHBzOi8vZGF0YXZpem0yMC5jbGFzc2VzLmFuZHJld2hlaXNzLmNvbS8pOiBBbWF6aW5nIGZyZWUgb25saW5lIG1hdGVyaWFsLCBpbnRyb2R1Y2luZyB5b3UgdG8gbWFueSBkZXNpZ24gY29uY2VwdHMgYW5kIHJlZmxlY3Rpb25zIG9uIGRhdGF2aXosIGNvbWJpbmVkIHdpdGggaGFuZHMtb24gZXhhbXBsZXMuCiogW0RhdGFDYXJwZW50cnk6IERhdGEgVmlzdWFsaXphdGlvbiB3aXRoIGdncGxvdDJdKGh0dHBzOi8vZGF0YWNhcnBlbnRyeS5vcmcvUi1lY29sb2d5LWxlc3Nvbi8wNC12aXN1YWxpemF0aW9uLWdncGxvdDIuaHRtbCNQbG90dGluZ193aXRoX2dncGxvdDIpOiBHZW5lcmFsbHkgZ29vZCBjb3Vyc2UuIFlvdSBtaWdodCBoZXJlIGVuam95IHRoZSBhdHRlbnRpb24gcGFpZCB0byBzdGF0aXN0aWNhbCBwbG90cy4KCiMjIyMgUGFwZXJzLCBFYm9va3MgJiBjaGFwdGVycwoqIFIgZm9yIERhdGEgU2NpZW5jZSAoR3JvbGVtdW5kICYgV2lja2hhbSkKICAgKiBbQ2hhcHRlciAzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sKTogQmFzaWNzIG9mIERhdGFWaXoKICAgKiBbQ2hhcHRlciA3XShodHRwczovL3I0ZHMuaGFkLmNvLm56L2V4cGxvcmF0b3J5LWRhdGEtYW5hbHlzaXMuaHRtbCk6IERhdGFWaXogZm9yIEVEQQogICAqIFtDaGFwdGVyIDI4XShodHRwczovL3I0ZHMuaGFkLmNvLm56L2dyYXBoaWNzLWZvci1jb21tdW5pY2F0aW9uLmh0bWwpOiBGaW5ldHVuaW5nIERhdGFWaXoKKiBbV2lja2hhbSwgSGFkbGV5LiAiQSBsYXllcmVkIGdyYW1tYXIgb2YgZ3JhcGhpY3MuIiBKb3VybmFsIG9mIENvbXB1dGF0aW9uYWwgYW5kIEdyYXBoaWNhbCBTdGF0aXN0aWNzIDE5LjEgKDIwMTApOiAzLTI4Ll0oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIucGRmKTogR2VuZXJhbCBjb25jZXB0IG9mIHRoZSBncmFtbWFyIG9mIGdyYXBoaWNzCiogSGVhbHksIEtpZXJhbi4gRGF0YSB2aXN1YWxpemF0aW9uOiBhIHByYWN0aWNhbCBpbnRyb2R1Y3Rpb24uIFByaW5jZXRvbiBVbml2ZXJzaXR5IFByZXNzLCAyMDE4LiBbb25saW5lIGF2YWlsYWJsZSBoZXJlXShodHRwczovL3NvY3Zpei5jby9pbmRleC5odG1sI3ByZWZhY2UpOiBHb29kIGlucnRvIHdpdGggbWFueSBiZXN0LXByYWN0aWNlIGFkdmljZXMuCgoKIyMjIFNlc3Npb24gSW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGBgCg==