This is a notebook that introduces the most important features of the tidyverse: ggplot() (for plots), piping, and some key functions for data wrangling (filter, arrange, gather, spread, group_by & summarise).

GETTING STARTED

First, we install and load the most important package (environment) in R for Data Science: the tidyverse.

if (!require("tidyverse")) install.packages(c("tidyverse")) # Only installs if missing
library(tidyverse)

A glimpse of the data: the first step is to have a look! We use the head() function: it gives the first 6 lines of its argument. We will work with the diamond dataset, which is embedded within the tidyverse. The tail() function shows the last 6 lines.

head(diamonds) # diamonds is a built-in dataset in the tidyverse (ggplot2, actually)
# Below the name of the variable, you will find the variable type:
# dbl  = double = real number,
# int  = integer = integer number,
# ord  = ordered factor,
# fact = unordered factor,
# char = character.
tail(diamonds)

FIRST PLOTS

A first example of one use of ggplot, the master function for plotting in R. The syntax is a bit strange at first, but after many examples, it becomes clear. The type of graph is determined by a ‘geom’: geom_point for scatter plot, geom_line for a line and geom_histogram… is explicit. Plot are always defined by the variable shown on the x-axis and sometimes by that featured on the y-axis. This information is embedded in the aesthetics wrapper aes(). The aes() can be specified either at the root of ggplot() or at the geom level. As we show below, many (many) plotting options are available.

ggplot(diamonds) + geom_point(aes(x = carat, y = price), size = 0.5, color = "#004C99") + xlim(0,3) + 
    stat_smooth(aes(x =carat, y = price), method = "lm", color = "red")

Note: 32 instances are absent because the corresponing diamonds are too big. The red line (stat_smooth part) shows the linearized relationship between size and price. The color can be provided using the hexadecimal code for RGB colors. See, e.g., https://www.w3schools.com/colors/colors_picker.asp

We can add features: titles, limitation on both axis, nonlinear scales for these axes, etc. In the graph below, the clarity of each diamond is shown with a color and the quality of cut represented by its size. The first choice is a good one, the second one could be debated.

ggplot(diamonds) + geom_point(aes(x = carat, y = price, color = clarity, size = cut), alpha = 3/10) + 
  scale_y_sqrt() + ggtitle("Plot of diamond prices") + xlim(0.1,4) 

Another popular graph type is the barplot. It does not require a y-axis specification: it counts the number of occurrence in the sample. If a y is specified, then inside the geom_bar, ‘stat = “identity”’ must be activated. One example will show how to proceed below.

ggplot(diamonds) + geom_bar(aes(x = cut, fill = clarity)) + ggtitle("Number of diamonds") 

Plotting is closely related to layers (like in Photoshop). Several layers can be superposed. Alternative colour palettes can be used. Below, we add one layer that consists of one horizontal line.

ggplot(diamonds,aes(x = price, fill = color)) + geom_hline(yintercept = 1000) + 
  geom_histogram(bins = 15, position= "dodge") + ggtitle("Number of diamonds") +
    scale_fill_brewer(palette = "RdYlBu")

Further references for graph types:
https://developers.google.com/chart/interactive/docs/gallery
https://plot.ly/r/
https://www.r-graph-gallery.com/all-graphs/
https://plot.ly/r/shiny-gallery/ (dynamic & interactive)

DATA MANIPULATION

First, it is imperative to get to know the data. Usually, we first resort to descriptive statistics.

summary(diamonds)
     carat               cut        color        clarity          depth           table      
 Min.   :0.2000   Fair     : 1610   D: 6775   SI1    :13065   Min.   :43.00   Min.   :43.00  
 1st Qu.:0.4000   Good     : 4906   E: 9797   VS2    :12258   1st Qu.:61.00   1st Qu.:56.00  
 Median :0.7000   Very Good:12082   F: 9542   SI2    : 9194   Median :61.80   Median :57.00  
 Mean   :0.7979   Premium  :13791   G:11292   VS1    : 8171   Mean   :61.75   Mean   :57.46  
 3rd Qu.:1.0400   Ideal    :21551   H: 8304   VVS2   : 5066   3rd Qu.:62.50   3rd Qu.:59.00  
 Max.   :5.0100                     I: 5422   VVS1   : 3655   Max.   :79.00   Max.   :95.00  
                                    J: 2808   (Other): 2531                                  
     price             x                y                z         
 Min.   :  326   Min.   : 0.000   Min.   : 0.000   Min.   : 0.000  
 1st Qu.:  950   1st Qu.: 4.710   1st Qu.: 4.720   1st Qu.: 2.910  
 Median : 2401   Median : 5.700   Median : 5.710   Median : 3.530  
 Mean   : 3933   Mean   : 5.731   Mean   : 5.735   Mean   : 3.539  
 3rd Qu.: 5324   3rd Qu.: 6.540   3rd Qu.: 6.540   3rd Qu.: 4.040  
 Max.   :18823   Max.   :10.740   Max.   :58.900   Max.   :31.800  
                                                                   

If need be, the data can be ordered, according to specific variables, using the arrange() function.

arrange(diamonds, desc(carat), price) # First, descending carat, then, increasing price.

The data is first ordered according to descending weight, and for a given weight (carat), it is sorted according to increasing price.

Filters

Often, people are interested by subsets of databases. Subsetting (i.e. filtering) can be performed over rows (occurrences). filter() is an amazing function that does just that.

filter(diamonds, carat > 4)
filter(diamonds, carat > 3 & cut == "Ideal")

And subsetting can be performed over columns as well. Indeed, sometimes, only a few columns matter and it can be useful to only keep those (possibly in a separate variable). We use select() to this purpose.

select(diamonds, carat, cut, color, price)
select(diamonds, - clarity, - x, - y, - z) # Using the minus sign performs the opposite manipulation and removes the corresponding variable

Piping

Data manipulation is all about sequences of tasks: filtering rows, selecting columns, re-arranging, plotting, etc. A very convenient way to write these sequences is to resort to the PIPE operator %>%. It works like this:
Data variable %>% Instruction 1 %>% Instruction 2 %>% Instruction 3 %>% …
Below, we apply successively a filter and a selection. On top of that, you can add a plot.

filter(diamonds, carat > 4) %>% select(carat, cut, color, price) # Or, equivalently,
diamonds %>% 
    filter(carat > 4) %>% 
    select(carat, cut, color, price)
diamonds %>% 
    filter(cut == "Fair") %>% 
    ggplot(aes(x = carat, y = price)) + geom_point() + geom_smooth() 

# The last part aims to ease visual pattern detection

The blue curve show the conditional (local) average of prices for a given level of size (carat). For ease of readability, it is customary to skip a line after a pipe. I often fail to comply to this rule and apologise for that.

Pivot tables

The tidyverse is very efficient at building pivot tables. Effortlessly (almost), they can then be plotted. The procedure requires 2 steps and 2 functions. First, the variables of interest are specified via group_by(). Second, the desired metric is defined via summarise().

diamonds %>% 
    group_by(cut) %>%                                # We focus on cut
    summarise(number = n(), avg_price = mean(price)) # n() simply counts the number of occurrences
diamonds %>% 
    group_by(cut, clarity) %>% 
    summarise(avg_carat = mean(carat), avg_price = mean(price)) %>%
    ggplot() + geom_point(aes(x = avg_carat, y = avg_price, color = clarity, shape = cut, size = 2)) # Yes, you can control the point size!

diamonds %>% 
    group_by(clarity) %>%
    summarise(avg_carat = mean(carat)) %>%
    ggplot() + geom_bar(aes(x = clarity, y = avg_carat), stat = "identity")

The second graph shows clusters of colours, hence of clarity. Clarity is a big deal for large diamonds. Internally flawless diamonds are very expensive. We used shape to denote cut, but this decision is ill-advised. Size would be slightly better, though not perfect.
The last graph shows that, as purity increases, diamond size shrinks (on average).

Spreading and gathering

Rectangular data can be organized is several ways. Plot via ggplot() can only be obtained via ‘columnised’ data. From a matrix (2 variables: one for rows and one for columns), you obtain a columnised variant via gather(). The opposite operation from spread().

diamonds %>% group_by(cut, color) %>% summarise(avg_price = mean(price)) 
# this is one way to see things.. how do I get a matrix out of this? => spread()!
diamonds %>% group_by(cut, color) %>% summarise(avg_price = mean(price)) %>% spread(key = cut, value = avg_price)

The other way around, via gather().

m <- matrix(1:15, nrow = 5) %>% data.frame      # Creates a matrix embedded into a dataframe
colnames(m) <- c("small", "medium", "large")    # Let's add some column names
m                                               # Let's see it    
gather(m, key = size, value = number)           # 'columnised version'

The gather() function has taken all columns and concatenated them vertically. The name of each column is stored in the key argument (here, it is ‘size’), the content inside the original matrix has name value (‘number’ in our case).

EXERCISES

First steps

  1. Set your working directory and load the tidyverse package
  2. Import the anes dataset (choose the RData file: technical spoiler).
  3. Have a quick look at the data (e.g., the first 6 lines).
  4. Summarise the dataset very briefly.

Working tidily

  1. Filter out the males aged exactly 87 at the time of the study. How numerous are they?
  2. How old is the oldest man/woman in the sample?
  3. Create a pivot table (PT) that computes, for each gender and religion, the average age of the respondents.
  4. From that PT, create a 2*4 matrix (each line corresponds to a gender, each column to a religion).

Plotting

  1. Plot the histogram of the Age variable. Choose the ‘best’ number of bins!
  2. Create a pivot table that counts the number of respondents for each gender, religion and party affiliation.
  3. Plot (in any way you find relevant) the resulting table.
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIHRoZSB0aWR5dmVyc2UiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgdG9jOiB0cnVlCiAgIHRvY19mbG9hdDogdHJ1ZQogICNnaXRodWJfZG9jdW1lbnQ6IAogICAgI3RvYzogdHJ1ZQotLS0KClRoaXMgaXMgYSBub3RlYm9vayB0aGF0IGludHJvZHVjZXMgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmVzIG9mIHRoZSAqKnRpZHl2ZXJzZSoqOiBnZ3Bsb3QoKSAoZm9yIHBsb3RzKSwgcGlwaW5nLCBhbmQgc29tZSBrZXkgZnVuY3Rpb25zIGZvciBkYXRhIHdyYW5nbGluZyAoZmlsdGVyLCBhcnJhbmdlLCBnYXRoZXIsIHNwcmVhZCwgZ3JvdXBfYnkgJiBzdW1tYXJpc2UpLgoKIyMgR0VUVElORyBTVEFSVEVECkZpcnN0LCB3ZSBpbnN0YWxsIGFuZCBsb2FkIHRoZSBtb3N0IGltcG9ydGFudCBwYWNrYWdlIChlbnZpcm9ubWVudCkgaW4gUiBmb3IgRGF0YSBTY2llbmNlOiB0aGUgKip0aWR5dmVyc2UqKi4KYGBge3IgSW5zdGFsbGluZyBhbmQgbG9hZGluZyBwYWNrYWdlcywgbWVzc2FnZSA9IEZBTFNFfQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSBpbnN0YWxsLnBhY2thZ2VzKGMoInRpZHl2ZXJzZSIpKSAjIE9ubHkgaW5zdGFsbHMgaWYgbWlzc2luZwpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpBIGdsaW1wc2Ugb2YgdGhlIGRhdGE6IHRoZSBmaXJzdCBzdGVwIGlzIHRvIGhhdmUgYSBsb29rISBXZSB1c2UgdGhlICoqaGVhZCoqKCkgZnVuY3Rpb246IGl0IGdpdmVzIHRoZSBmaXJzdCA2IGxpbmVzIG9mIGl0cyBhcmd1bWVudC4gV2Ugd2lsbCB3b3JrIHdpdGggdGhlICoqZGlhbW9uZCBkYXRhc2V0KiosIHdoaWNoIGlzIGVtYmVkZGVkIHdpdGhpbiB0aGUgdGlkeXZlcnNlLiBUaGUgKip0YWlsKiooKSBmdW5jdGlvbiBzaG93cyB0aGUgbGFzdCA2IGxpbmVzLgpgYGB7ciBWaXN1YWxpemluZyB0aGUgZGF0YXNldH0KaGVhZChkaWFtb25kcykgIyBkaWFtb25kcyBpcyBhIGJ1aWx0LWluIGRhdGFzZXQgaW4gdGhlIHRpZHl2ZXJzZSAoZ2dwbG90MiwgYWN0dWFsbHkpCiMgQmVsb3cgdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlLCB5b3Ugd2lsbCBmaW5kIHRoZSB2YXJpYWJsZSB0eXBlOgojIGRibCAgPSBkb3VibGUgPSByZWFsIG51bWJlciwKIyBpbnQgID0gaW50ZWdlciA9IGludGVnZXIgbnVtYmVyLAojIG9yZCAgPSBvcmRlcmVkIGZhY3RvciwKIyBmYWN0ID0gdW5vcmRlcmVkIGZhY3RvciwKIyBjaGFyID0gY2hhcmFjdGVyLgp0YWlsKGRpYW1vbmRzKQpgYGAKCiMjIEZJUlNUIFBMT1RTCkEgZmlyc3QgZXhhbXBsZSBvZiBvbmUgdXNlIG9mICoqZ2dwbG90KiosIHRoZSBtYXN0ZXIgZnVuY3Rpb24gZm9yIHBsb3R0aW5nIGluIFIuIFRoZSBzeW50YXggaXMgYSBiaXQgc3RyYW5nZSBhdCBmaXJzdCwgYnV0IGFmdGVyIG1hbnkgZXhhbXBsZXMsIGl0IGJlY29tZXMgY2xlYXIuIFRoZSB0eXBlIG9mIGdyYXBoIGlzIGRldGVybWluZWQgYnkgYSAnKipnZW9tKionOiBnZW9tX3BvaW50IGZvciBzY2F0dGVyIHBsb3QsIGdlb21fbGluZSBmb3IgYSBsaW5lIGFuZCBnZW9tX2hpc3RvZ3JhbS4uLiBpcyBleHBsaWNpdC4gUGxvdCBhcmUgYWx3YXlzIGRlZmluZWQgYnkgdGhlIHZhcmlhYmxlIHNob3duIG9uIHRoZSB4LWF4aXMgYW5kIHNvbWV0aW1lcyBieSB0aGF0IGZlYXR1cmVkIG9uIHRoZSB5LWF4aXMuIFRoaXMgaW5mb3JtYXRpb24gaXMgZW1iZWRkZWQgaW4gdGhlIGFlc3RoZXRpY3Mgd3JhcHBlciAqKmFlcygpKiouIFRoZSBhZXMoKSBjYW4gYmUgc3BlY2lmaWVkIGVpdGhlciBhdCB0aGUgcm9vdCBvZiBnZ3Bsb3QoKSBvciBhdCB0aGUgZ2VvbSBsZXZlbC4gQXMgd2Ugc2hvdyBiZWxvdywgbWFueSAobWFueSkgcGxvdHRpbmcgb3B0aW9ucyBhcmUgYXZhaWxhYmxlLgpgYGB7ciBQbG90dGluZyBkaWFtb25kcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmdncGxvdChkaWFtb25kcykgKyBnZW9tX3BvaW50KGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSksIHNpemUgPSAwLjUsIGNvbG9yID0gIiMwMDRDOTkiKSArIHhsaW0oMCwzKSArIAogICAgc3RhdF9zbW9vdGgoYWVzKHggPWNhcmF0LCB5ID0gcHJpY2UpLCBtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiKQpgYGAKICAKTm90ZTogMzIgaW5zdGFuY2VzIGFyZSBhYnNlbnQgYmVjYXVzZSB0aGUgY29ycmVzcG9uaW5nIGRpYW1vbmRzIGFyZSB0b28gYmlnLiBUaGUgcmVkIGxpbmUgKHN0YXRfc21vb3RoIHBhcnQpIHNob3dzIHRoZSBsaW5lYXJpemVkIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNpemUgYW5kIHByaWNlLiBUaGUgY29sb3IgY2FuIGJlIHByb3ZpZGVkIHVzaW5nIHRoZSBoZXhhZGVjaW1hbCBjb2RlIGZvciBSR0IgY29sb3JzLiBTZWUsIGUuZy4sCmh0dHBzOi8vd3d3Lnczc2Nob29scy5jb20vY29sb3JzL2NvbG9yc19waWNrZXIuYXNwCgoKV2UgY2FuIGFkZCAqKmZlYXR1cmVzKio6IHRpdGxlcywgbGltaXRhdGlvbiBvbiBib3RoIGF4aXMsIG5vbmxpbmVhciBzY2FsZXMgZm9yIHRoZXNlIGF4ZXMsIGV0Yy4gSW4gdGhlIGdyYXBoIGJlbG93LCB0aGUgY2xhcml0eSBvZiBlYWNoIGRpYW1vbmQgaXMgc2hvd24gd2l0aCBhIGNvbG9yIGFuZCB0aGUgcXVhbGl0eSBvZiBjdXQgcmVwcmVzZW50ZWQgYnkgaXRzIHNpemUuIFRoZSBmaXJzdCBjaG9pY2UgaXMgYSBnb29kIG9uZSwgdGhlIHNlY29uZCBvbmUgY291bGQgYmUgZGViYXRlZC4KYGBge3IgQWRkaW5nIGdyYXBoaWNhbCBmZWF0dXJlcywgd2FybmluZyA9IEZBTFNFfQpnZ3Bsb3QoZGlhbW9uZHMpICsgZ2VvbV9wb2ludChhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UsIGNvbG9yID0gY2xhcml0eSwgc2l6ZSA9IGN1dCksIGFscGhhID0gMy8xMCkgKyAKICBzY2FsZV95X3NxcnQoKSArIGdndGl0bGUoIlBsb3Qgb2YgZGlhbW9uZCBwcmljZXMiKSArIHhsaW0oMC4xLDQpIApgYGAKCkFub3RoZXIgcG9wdWxhciBncmFwaCB0eXBlIGlzIHRoZSAqKmJhcnBsb3QqKi4gSXQgZG9lcyBub3QgcmVxdWlyZSBhIHktYXhpcyBzcGVjaWZpY2F0aW9uOiBpdCBjb3VudHMgdGhlIG51bWJlciBvZiBvY2N1cnJlbmNlIGluIHRoZSBzYW1wbGUuIElmIGEgeSBpcyBzcGVjaWZpZWQsIHRoZW4gaW5zaWRlIHRoZSBnZW9tX2JhciwgJ3N0YXQgPSAiaWRlbnRpdHkiJyBtdXN0IGJlIGFjdGl2YXRlZC4gT25lIGV4YW1wbGUgd2lsbCBzaG93IGhvdyB0byBwcm9jZWVkIGJlbG93LgpgYGB7ciBBbHRlcm5hdGl2ZSByZXByZXNlbnRhdGlvbn0KZ2dwbG90KGRpYW1vbmRzKSArIGdlb21fYmFyKGFlcyh4ID0gY3V0LCBmaWxsID0gY2xhcml0eSkpICsgZ2d0aXRsZSgiTnVtYmVyIG9mIGRpYW1vbmRzIikgCgpgYGAKClBsb3R0aW5nIGlzIGNsb3NlbHkgcmVsYXRlZCB0byBsYXllcnMgKGxpa2UgaW4gUGhvdG9zaG9wKS4gU2V2ZXJhbCBsYXllcnMgY2FuIGJlIHN1cGVycG9zZWQuIEFsdGVybmF0aXZlICoqY29sb3VyIHBhbGV0dGVzKiogY2FuIGJlIHVzZWQuIEJlbG93LCB3ZSBhZGQgb25lIGxheWVyIHRoYXQgY29uc2lzdHMgb2Ygb25lIGhvcml6b250YWwgbGluZS4KYGBge3IgR29pbmcgZXZlbiBmdXJ0aGVyICYgYWRkaW5nIGEgbGF5ZXIgKyBjaGFuZ2luZyBjb2xvdXIgcGFsZXR0ZX0KZ2dwbG90KGRpYW1vbmRzLGFlcyh4ID0gcHJpY2UsIGZpbGwgPSBjb2xvcikpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMTAwMCkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTUsIHBvc2l0aW9uPSAiZG9kZ2UiKSArIGdndGl0bGUoIk51bWJlciBvZiBkaWFtb25kcyIpICsKICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUmRZbEJ1IikgICMgUmVkLVllbGxvdy1CbHVlIHBhbGV0dGUKYGBgCgpGdXJ0aGVyIHJlZmVyZW5jZXMgZm9yIGdyYXBoIHR5cGVzOiAgCmh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL2NoYXJ0L2ludGVyYWN0aXZlL2RvY3MvZ2FsbGVyeSAgCmh0dHBzOi8vcGxvdC5seS9yLyAgCmh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vYWxsLWdyYXBocy8gIApodHRwczovL3Bsb3QubHkvci9zaGlueS1nYWxsZXJ5LyAoZHluYW1pYyAmIGludGVyYWN0aXZlKQoKIyMgREFUQSBNQU5JUFVMQVRJT04KCkZpcnN0LCBpdCBpcyBpbXBlcmF0aXZlIHRvIGdldCB0byBrbm93IHRoZSBkYXRhLiBVc3VhbGx5LCB3ZSBmaXJzdCByZXNvcnQgdG8gKipkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzKiouCgpgYGB7ciBBIGxvb2sgYXQgdGhlIGRhdGE6IHN1bW1hcnkoKX0Kc3VtbWFyeShkaWFtb25kcykKYGBgCgpJZiBuZWVkIGJlLCB0aGUgZGF0YSBjYW4gYmUgb3JkZXJlZCwgYWNjb3JkaW5nIHRvIHNwZWNpZmljIHZhcmlhYmxlcywgdXNpbmcgdGhlICoqYXJyYW5nZSgpKiogZnVuY3Rpb24uCgpgYGB7ciBvcmRlcn0KYXJyYW5nZShkaWFtb25kcywgZGVzYyhjYXJhdCksIHByaWNlKSAjIEZpcnN0LCBkZXNjZW5kaW5nIGNhcmF0LCB0aGVuLCBpbmNyZWFzaW5nIHByaWNlLgpgYGAKClRoZSBkYXRhIGlzIGZpcnN0IG9yZGVyZWQgYWNjb3JkaW5nIHRvIGRlc2NlbmRpbmcgd2VpZ2h0LCBhbmQgZm9yIGEgZ2l2ZW4gd2VpZ2h0IChjYXJhdCksIGl0IGlzIHNvcnRlZCBhY2NvcmRpbmcgdG8gaW5jcmVhc2luZyBwcmljZS4KCiMjIyBGaWx0ZXJzCk9mdGVuLCBwZW9wbGUgYXJlIGludGVyZXN0ZWQgYnkgc3Vic2V0cyBvZiBkYXRhYmFzZXMuIFN1YnNldHRpbmcgKGkuZS4gZmlsdGVyaW5nKSBjYW4gYmUgcGVyZm9ybWVkIG92ZXIgcm93cyAob2NjdXJyZW5jZXMpLiAqKmZpbHRlcioqKCkgaXMgYW4gYW1hemluZyBmdW5jdGlvbiB0aGF0IGRvZXMganVzdCB0aGF0LgpgYGB7ciBmaWx0ZXIoKToga2VlcCBvbmx5IHNvbWUgc3BlY2lmaWMgcm93c30KZmlsdGVyKGRpYW1vbmRzLCBjYXJhdCA+IDQpCmZpbHRlcihkaWFtb25kcywgY2FyYXQgPiAzICYgY3V0ID09ICJJZGVhbCIpCmBgYAoKQW5kIHN1YnNldHRpbmcgY2FuIGJlIHBlcmZvcm1lZCBvdmVyIGNvbHVtbnMgYXMgd2VsbC4gSW5kZWVkLCBzb21ldGltZXMsIG9ubHkgYSBmZXcgY29sdW1ucyBtYXR0ZXIgYW5kIGl0IGNhbiBiZSB1c2VmdWwgdG8gb25seSBrZWVwIHRob3NlIChwb3NzaWJseSBpbiBhIHNlcGFyYXRlIHZhcmlhYmxlKS4gV2UgdXNlICoqc2VsZWN0KiooKSB0byB0aGlzIHB1cnBvc2UuCgpgYGB7ciBzZWxlY3QoKToga2VlcCBvbmx5IHNwZWNpZmljIGNvbHVtbnN9CnNlbGVjdChkaWFtb25kcywgY2FyYXQsIGN1dCwgY29sb3IsIHByaWNlKQpzZWxlY3QoZGlhbW9uZHMsIC0gY2xhcml0eSwgLSB4LCAtIHksIC0geikgCiMgVXNpbmcgdGhlIG1pbnVzIHNpZ24gcGVyZm9ybXMgdGhlIG9wcG9zaXRlIG1hbmlwdWxhdGlvbiBhbmQgcmVtb3ZlcyB0aGUgY29ycmVzcG9uZGluZyB2YXJpYWJsZQpgYGAKCiMjIyBQaXBpbmcKRGF0YSBtYW5pcHVsYXRpb24gaXMgYWxsIGFib3V0IHNlcXVlbmNlcyBvZiB0YXNrczogZmlsdGVyaW5nIHJvd3MsIHNlbGVjdGluZyBjb2x1bW5zLCByZS1hcnJhbmdpbmcsIHBsb3R0aW5nLCBldGMuIEEgdmVyeSBjb252ZW5pZW50IHdheSB0byB3cml0ZSB0aGVzZSBzZXF1ZW5jZXMgaXMgdG8gcmVzb3J0IHRvIHRoZSAqKlBJUEUqKiBvcGVyYXRvciAqKiU+JSoqLiBJdCB3b3JrcyBsaWtlIHRoaXM6ICAKRGF0YSB2YXJpYWJsZSAlPiUgSW5zdHJ1Y3Rpb24gMSAlPiUgSW5zdHJ1Y3Rpb24gMiAlPiUgSW5zdHJ1Y3Rpb24gMyAlPiUgLi4uICAKQmVsb3csIHdlIGFwcGx5IHN1Y2Nlc3NpdmVseSBhIGZpbHRlciBhbmQgYSBzZWxlY3Rpb24uIE9uIHRvcCBvZiB0aGF0LCB5b3UgY2FuIGFkZCBhIHBsb3QuCmBgYHtyIHBpcGluZzogY29tYmluZSB0d28gb3IgbW9yZSBmdW5jdGlvbnMhLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KZmlsdGVyKGRpYW1vbmRzLCBjYXJhdCA+IDQpICU+JSBzZWxlY3QoY2FyYXQsIGN1dCwgY29sb3IsIHByaWNlKSAjIE9yLCBlcXVpdmFsZW50bHksCmRpYW1vbmRzICU+JSAKICAgIGZpbHRlcihjYXJhdCA+IDQpICU+JSAKICAgIHNlbGVjdChjYXJhdCwgY3V0LCBjb2xvciwgcHJpY2UpCmRpYW1vbmRzICU+JSAKICAgIGZpbHRlcihjdXQgPT0gIkZhaXIiKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpIAojIFRoZSBsYXN0IHBhcnQgYWltcyB0byBlYXNlIHZpc3VhbCBwYXR0ZXJuIGRldGVjdGlvbgpgYGAKClRoZSBibHVlIGN1cnZlIHNob3cgdGhlIGNvbmRpdGlvbmFsIChsb2NhbCkgYXZlcmFnZSBvZiBwcmljZXMgZm9yIGEgZ2l2ZW4gbGV2ZWwgb2Ygc2l6ZSAoY2FyYXQpLgpGb3IgZWFzZSBvZiByZWFkYWJpbGl0eSwgaXQgaXMgY3VzdG9tYXJ5IHRvIHNraXAgYSBsaW5lIGFmdGVyIGEgcGlwZS4gSSBvZnRlbiBmYWlsIHRvIGNvbXBseSB0byB0aGlzIHJ1bGUgYW5kIGFwb2xvZ2lzZSBmb3IgdGhhdC4gCgojIyMgUGl2b3QgdGFibGVzClRoZSB0aWR5dmVyc2UgaXMgdmVyeSBlZmZpY2llbnQgYXQgYnVpbGRpbmcgcGl2b3QgdGFibGVzLiBFZmZvcnRsZXNzbHkgKGFsbW9zdCksIHRoZXkgY2FuIHRoZW4gYmUgcGxvdHRlZC4gVGhlIHByb2NlZHVyZSByZXF1aXJlcyAyIHN0ZXBzIGFuZCAyIGZ1bmN0aW9ucy4gRmlyc3QsIHRoZSB2YXJpYWJsZXMgb2YgaW50ZXJlc3QgYXJlIHNwZWNpZmllZCB2aWEgKipncm91cF9ieSgpKiouIFNlY29uZCwgdGhlIGRlc2lyZWQgbWV0cmljIGlzIGRlZmluZWQgdmlhICoqc3VtbWFyaXNlKCkqKi4gCmBgYHtyIEFuYWx5dGljcyAxMDE6IGNvbWJpbmUgZ3JvdXBfYnkoKSBhbmQgc3VtbWFyaXNlKCkgJiBnZXQgcGl2b3QgdGFibGVzIX0KZGlhbW9uZHMgJT4lIAogICAgZ3JvdXBfYnkoY3V0KSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgV2UgZm9jdXMgb24gY3V0CiAgICBzdW1tYXJpc2UobnVtYmVyID0gbigpLCBhdmdfcHJpY2UgPSBtZWFuKHByaWNlKSkgIyBuKCkgc2ltcGx5IGNvdW50cyB0aGUgbnVtYmVyIG9mIG9jY3VycmVuY2VzCmRpYW1vbmRzICU+JSAKICAgIGdyb3VwX2J5KGN1dCwgY2xhcml0eSkgJT4lIAogICAgc3VtbWFyaXNlKGF2Z19jYXJhdCA9IG1lYW4oY2FyYXQpLCBhdmdfcHJpY2UgPSBtZWFuKHByaWNlKSkgJT4lCiAgICBnZ3Bsb3QoKSArIGdlb21fcG9pbnQoYWVzKHggPSBhdmdfY2FyYXQsIHkgPSBhdmdfcHJpY2UsIGNvbG9yID0gY2xhcml0eSwgc2hhcGUgPSBjdXQsIHNpemUgPSAyKSkgIyBZZXMsIHlvdSBjYW4gY29udHJvbCB0aGUgcG9pbnQgc2l6ZSEKZGlhbW9uZHMgJT4lIAogICAgZ3JvdXBfYnkoY2xhcml0eSkgJT4lCiAgICBzdW1tYXJpc2UoYXZnX2NhcmF0ID0gbWVhbihjYXJhdCkpICU+JQogICAgZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeCA9IGNsYXJpdHksIHkgPSBhdmdfY2FyYXQpLCBzdGF0ID0gImlkZW50aXR5IikKYGBgCgpUaGUgc2Vjb25kIGdyYXBoIHNob3dzIGNsdXN0ZXJzIG9mIGNvbG91cnMsIGhlbmNlIG9mIGNsYXJpdHkuIENsYXJpdHkgaXMgYSBiaWcgZGVhbCBmb3IgbGFyZ2UgZGlhbW9uZHMuIEludGVybmFsbHkgZmxhd2xlc3MgZGlhbW9uZHMgYXJlIHZlcnkgZXhwZW5zaXZlLiAgV2UgdXNlZCBzaGFwZSB0byBkZW5vdGUgY3V0LCBidXQgdGhpcyBkZWNpc2lvbiBpcyBpbGwtYWR2aXNlZC4gU2l6ZSB3b3VsZCBiZSBzbGlnaHRseSBiZXR0ZXIsIHRob3VnaCBub3QgcGVyZmVjdC4gIApUaGUgbGFzdCBncmFwaCBzaG93cyB0aGF0LCBhcyBwdXJpdHkgaW5jcmVhc2VzLCBkaWFtb25kIHNpemUgc2hyaW5rcyAob24gYXZlcmFnZSkuCgoKIyMjIFNwcmVhZGluZyBhbmQgZ2F0aGVyaW5nClJlY3Rhbmd1bGFyIGRhdGEgY2FuIGJlIG9yZ2FuaXplZCBpcyBzZXZlcmFsIHdheXMuIFBsb3QgdmlhIGdncGxvdCgpIGNhbiBvbmx5IGJlIG9idGFpbmVkIHZpYSAnY29sdW1uaXNlZCcgZGF0YS4gRnJvbSBhIG1hdHJpeCAoMiB2YXJpYWJsZXM6IG9uZSBmb3Igcm93cyBhbmQgb25lIGZvciBjb2x1bW5zKSwgeW91IG9idGFpbiBhIGNvbHVtbmlzZWQgdmFyaWFudCB2aWEgKipnYXRoZXIoKSoqLiBUaGUgb3Bwb3NpdGUgb3BlcmF0aW9uIGZyb20gKipzcHJlYWQoKSoqLgpgYGB7ciBzcHJlYWQoKSAmIGdhdGhlcigpOiBleHBsb3JpbmcgdGFibGUgZm9ybWF0c30KZGlhbW9uZHMgJT4lIGdyb3VwX2J5KGN1dCwgY29sb3IpICU+JSBzdW1tYXJpc2UoYXZnX3ByaWNlID0gbWVhbihwcmljZSkpIAojIHRoaXMgaXMgb25lIHdheSB0byBzZWUgdGhpbmdzLi4gaG93IGRvIEkgZ2V0IGEgbWF0cml4IG91dCBvZiB0aGlzPyA9PiBzcHJlYWQoKSEKZGlhbW9uZHMgJT4lIGdyb3VwX2J5KGN1dCwgY29sb3IpICU+JSBzdW1tYXJpc2UoYXZnX3ByaWNlID0gbWVhbihwcmljZSkpICU+JSBzcHJlYWQoa2V5ID0gY3V0LCB2YWx1ZSA9IGF2Z19wcmljZSkKYGBgCgpUaGUgb3RoZXIgd2F5IGFyb3VuZCwgdmlhIGdhdGhlcigpLgoKYGBge3IgZ2F0aGVyfQptIDwtIG1hdHJpeCgxOjE1LCBucm93ID0gNSkgJT4lIGRhdGEuZnJhbWUgICAgICAjIENyZWF0ZXMgYSBtYXRyaXggZW1iZWRkZWQgaW50byBhIGRhdGFmcmFtZQpjb2xuYW1lcyhtKSA8LSBjKCJzbWFsbCIsICJtZWRpdW0iLCAibGFyZ2UiKSAgICAjIExldCdzIGFkZCBzb21lIGNvbHVtbiBuYW1lcwptICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIExldCdzIHNlZSBpdCAgICAKZ2F0aGVyKG0sIGtleSA9IHNpemUsIHZhbHVlID0gbnVtYmVyKSAgICAgICAgICAgIyAnY29sdW1uaXNlZCB2ZXJzaW9uJwpgYGAKClRoZSBnYXRoZXIoKSBmdW5jdGlvbiBoYXMgdGFrZW4gYWxsIGNvbHVtbnMgYW5kIGNvbmNhdGVuYXRlZCB0aGVtIHZlcnRpY2FsbHkuIFRoZSBuYW1lIG9mIGVhY2ggY29sdW1uIGlzIHN0b3JlZCBpbiB0aGUga2V5IGFyZ3VtZW50IChoZXJlLCBpdCBpcyAnc2l6ZScpLCB0aGUgY29udGVudCBpbnNpZGUgdGhlIG9yaWdpbmFsIG1hdHJpeCBoYXMgbmFtZSB2YWx1ZSAoJ251bWJlcicgaW4gb3VyIGNhc2UpLgoKCiMjIEVYRVJDSVNFUwoKIyMjIEZpcnN0IHN0ZXBzCjEpIFNldCB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5IGFuZCBsb2FkIHRoZSB0aWR5dmVyc2UgcGFja2FnZQoyKSBJbXBvcnQgdGhlIGFuZXMgZGF0YXNldCAoY2hvb3NlIHRoZSBSRGF0YSBmaWxlOiB0ZWNobmljYWwgc3BvaWxlcikuIAozKSBIYXZlIGEgcXVpY2sgbG9vayBhdCB0aGUgZGF0YSAoZS5nLiwgdGhlIGZpcnN0IDYgbGluZXMpLiAgCjQpIFN1bW1hcmlzZSB0aGUgZGF0YXNldCB2ZXJ5IGJyaWVmbHkuICAKCmBgYHtyIHlvdXIgdHVybiF9CgpgYGAKCgoKIyMjIFdvcmtpbmcgdGlkaWx5CjEpIEZpbHRlciBvdXQgdGhlIG1hbGVzIGFnZWQgZXhhY3RseSA4NyBhdCB0aGUgdGltZSBvZiB0aGUgc3R1ZHkuIEhvdyBudW1lcm91cyBhcmUgdGhleT8gIAoyKSBIb3cgb2xkIGlzIHRoZSBvbGRlc3QgbWFuL3dvbWFuIGluIHRoZSBzYW1wbGU/ICAKMykgQ3JlYXRlIGEgcGl2b3QgdGFibGUgKFBUKSB0aGF0IGNvbXB1dGVzLCBmb3IgZWFjaCBnZW5kZXIgYW5kIHJlbGlnaW9uLCB0aGUgYXZlcmFnZSBhZ2Ugb2YgdGhlIHJlc3BvbmRlbnRzLiAgIAo0KSBGcm9tIHRoYXQgUFQsIGNyZWF0ZSBhIDIqNCBtYXRyaXggKGVhY2ggbGluZSBjb3JyZXNwb25kcyB0byBhIGdlbmRlciwgZWFjaCBjb2x1bW4gdG8gYSByZWxpZ2lvbikuICAKCgojIyMgUGxvdHRpbmcKMSkgUGxvdCB0aGUgaGlzdG9ncmFtIG9mIHRoZSBBZ2UgdmFyaWFibGUuIENob29zZSB0aGUgJ2Jlc3QnIG51bWJlciBvZiBiaW5zIQoyKSBDcmVhdGUgYSBwaXZvdCB0YWJsZSB0aGF0IGNvdW50cyB0aGUgbnVtYmVyIG9mIHJlc3BvbmRlbnRzIGZvciBlYWNoIGdlbmRlciwgcmVsaWdpb24gYW5kIHBhcnR5IGFmZmlsaWF0aW9uLiAgCjMpIFBsb3QgKGluIGFueSB3YXkgeW91IGZpbmQgcmVsZXZhbnQpIHRoZSByZXN1bHRpbmcgdGFibGUuICAKCgoKCgo=