1 Learning Objectives

library(tidyverse)
library(gapminder)

The resources underpinning today’s class are:

  1. Care and Feeding of Data in R
  2. dplyr resources: part 1 and part 2.
  3. R for Data Science, Ch5 for dplyr.

1.1 1. A quick look at data types in R

1.1.1 1.1 Overview

Data types are summarized nicely in JB’s small slideshow.

1.1.2 1.2 Investigation using R

We’ve seen that R understands numbers – a data type. What other type of objects does R recognize? Let’s identify them, and test them with the typeof function. There are

  • Doubles (as we’ve seen – they’re numbers)
typeof(5.6)
  • Integers: (special type of number – need an L at the end)
typeof(4L)
typeof(4)
  • logicals are true/false (known as booleans in other languages). We highly recommend using TRUE/FALSE instead of T/F (you can assign T/F as variables, as we’ll see later).
typeof(TRUE)
typeof(T)
typeof(FALSE)
typeof(F)
  • characters:
typeof("Hi, my name is Vincenzo.")

Exercise: Find the data type of "5". Explain the output.

typeof("5")

Exercise: Use the as.numeric function to convert "5" to an object of type "double".

as.numeric("5")

Exercise: Describe this output:

typeof(typeof(15.2))

1.2 2. Data Frames in R

1.2.1 2.1 “Care and Feeding of Data”: Exercises

Last class, we went through the Care and Feeding of Data in R tutorial. Let’s do some exercises, this time with the iris dataset (comes pre-loaded in the datasets package). Use any method you’d like to answer the following questions.

  1. How many variables (columns) are in the iris dataset, and what are their names?
ncol(iris)
head(iris)
  1. How many rows are in the data set?
nrow(iris)
dim(iris)
  1. What are the smallest values of each numeric variable?
summary(iris)
  1. Extract the Petal.Width column to get a vector of observations (we’ll see vectors in more detail in a later class), and
    1. Make a histogram
    2. Make a table of frequencies
x <- iris$Petal.Width
hist(x)
table(x)

1.2.2 2.2 dplyr fundamentals

Next, we’ll learn about dplyr, a handy R package for manipulating data frames, through six main functions. According to the R for data science book, the main five (in different order) are:

  • Pick observations by their values (filter()).
  • Pick variables by their names (select()).
  • (We’ll sneak “piping” into here)
  • Reorder the rows (arrange()).
  • Create new variables with functions of existing variables (mutate()).
  • Collapse many values down to a single summary (summarise()).

Then there’s group_by(), which can be used in conjunction with all of these.

We’ll get through as much as we can in this class, and will continue in cm006, possibly with some more exercises and more features of dplyr.

Resources underpinning this section can be found in two parts: part 1 and part 2.

1.2.2.1 filter

filter subsets data frames according to some logical expression.

Basic example:

filter(gapminder, country=="Canada")

Logical expressions are governed by relational operators, that output either TRUE or FALSE based on the validity of the statement you give it. Here’s a summary of the operators:

Operation Outputs TRUE or FALSE based on the validity of the statement…
a == b a is equal to b
a != b a is not equal to b.
a > b a is greater than b.
a < b a is less than b.
a >= b a is greater than or equal to b.
a <= b a is less than or equal to b.
a %in% b a is an element in b.

Let’s see some examples:

5 == 3
c(1, 2, 3) < 3   # vectorized!
4 %in% c(1, 2, 3, 4, 5)
my_equality <- 5 == 3
print(my_equality)

There are logical operators too, and they follow Boolean Algebra. They’re listed below – the first three are fundamental, but the others are useful too.

Operation Outputs TRUE or FALSE based on the validity of the statement…
a & b, a && b Both a and b are TRUE
a | b, a || b Either a or b is TRUE.
!a a is not TRUE (in other words, take the complement or “flip” a)
xor(a, b) Either a or b is TRUE, but not both.
all(a,b,c,...) a, b, c, … are all TRUE.
any(a,b,c,...) Any one of a, b, c, … is TRUE.

We can filter by more than one condition:

#filter(gapminder, country=="Canada" & year < 1980) # same as...
filter(gapminder, country=="Canada", year < 1970)
filter(gapminder, country=="Canada" | year == 1952)

From the Part 1 notes… never do the following!

excerpt <- gapminder[241:252, ]

Why is this a terrible idea?

  • It is not self-documenting. What is so special about rows 241 through 252?
  • It is fragile. This line of code will produce different results if someone changes the row order of gapminder, e.g. sorts the data earlier in the script.

Exercises: Let’s try some exercises using the gapminder data set.

  1. Find all entries of Canada and Algeria.
filter(gapminder, 
       country == "Canada" |
           country == "Algeria")
filter(gapminder,
       country %in% c("Canada", "Algeria"))
  1. Find all entries of Canada and Algeria, occuring in the ’60s.
filter(gapminder,
       country %in% c("Canada", "Algeria"), year < 1970, year >= 1960)
  1. Find all entries of Canada, and entries of Algeria occuring in the ’60s.
filter(gapminder,
       (country == "Canada") |
           (country == "Algeria" & 
            year %in% 1960:1969))
  1. Find all entries not including European countries.
filter(gapminder, 
       continent != "Europe")

1.2.2.2 select

select subsets data by columns/variable names.

select(gapminder, continent, country)
  • Always returns a tibble.
  • Drop variables with -.
  • Note that re-ordering happens here.

1.2.2.3 piping

What if we wanted to do more than one operation? For example:

  • take all entries of Canada and Algeria occuring in the ’60s, and
  • select the country, year, and gdpPercap columns.

We could do…

select(filter(gapminder, 
              country %in% c("Canada", "Algeria"), 
              year <= 1969, year >= 1960),
       country, year, gdpPercap)

But the chain of functions can get quite long and hard to read.

The pipe operator %>% feeds the output of a function into another function. Syntax:

gapminder %>% 
    f1() %>% 
    f2() %>% 
    f3(options=something)

This says:

  1. Start with the gapminder data, then
  2. apply f1 to it, then
  3. apply f2, then
  4. apply f3 with the options=something argument.

The same operation above becomes:

gapminder %>% 
    filter(country %in% c("Canada", "Algeria"), 
           year <= 1969, year >= 1960) %>% 
    select(country, year, gdpPercap)

You can read this as:

  1. start with the gapminder data, then
  2. take all entries of Canada and Algeria occuring in the ’60s (filter), then
  3. select the country, year, and gdpPercap columns.

Exercise: Take all countries in Europe that have a GPD per capita greater than 10000, and select all variables except gdpPercap. (Hint: use -).

gapminder %>% 
    filter(continent == "Europe",
           gdpPercap > 10000) %>% 
    select(-gdpPercap)

1.2.2.4 arrange

arrange sorts a data frame by shuffling the order of the rows appropriately. Use desc to sort by descending order.

Order gapminder by population, then life expectancy:

arrange(gapminder, pop, lifeExp)

Exercises:

  1. Order the data frame by year, then descending by life expectancy.
arrange(gapminder, year, desc(lifeExp))
  1. In addition to the above exercise, rearrange the variables so that year comes first, followed by life expectancy. (Hint: check the documentation for the select function for a related handy function).
gapminder %>% 
    arrange(year, desc(lifeExp)) %>% 
    select(year, lifeExp, everything())

1.2.2.5 mutate

mutate creates a new variable by calculating from other variables. Let’s get GDP by multiplying GPD per capita with population:

mutate(gapminder, gdp = gdpPercap * pop)

You can define multiple new variables – even back-dependent on new ones! Let’s also create a column for GDP in billions, rounded to one decimal:

mutate(gapminder, 
       gdp     = gdpPercap * pop, 
       gdpBill = round(gdp/1000000000, 1))

transmute works the same way, but drops all other variables.

Exercise: Make a new column called cc that pastes the country name followed by the continent, separated by a comma. (Hint: use the paste function with the sep=", " argument).

mutate(gapminder, cc = paste(country, continent, sep=", "))

1.2.2.6 summarize and group_by

summarize reduces a tibble according to summary statistics.

summarize(gapminder, mean_pop=mean(pop), sd_pop=sd(pop))

Not very useful by itself! But, with the group_by function, the summarize function is very useful:

gapminder %>% 
    group_by(country) %>% 
    summarize(mean_pop=mean(pop), sd_pop=sd(pop))

The group_by function splits the tibble into parts – in the above case, by country. Notice the “Groups” indicator in the following output:

group_by(gapminder, continent, country, year < 1970)

Let’s get a summary of this grouping:

(out1 <- gapminder %>% 
    group_by(continent, country, year < 1970) %>% 
    summarize(mean_pop=mean(pop), sd_pop=sd(pop)))

Note that the output is still a tibble, but one “layer” of grouping has been peeled back: the year < 1970 variable. summarize again and you’ll see that the tibble was no longer grouped by year < 1970.

out1 %>% 
    summarize(mean_pop=mean(mean_pop))

Exercise: Find the minimum GDP per capita experienced by each country

gapminder %>%
    group_by(country) %>% 
    summarize(mingdp=min(gdpPercap))

Exercise: How many years of record does each country have?

gapminder %>%
    group_by(country) %>% 
    summarize(n_distinct(year))

Exercise: Within Asia, what are the min and max life expectancies experienced in each year?

gapminder %>% 
    filter(continent=="Asia") %>% 
    group_by(year) %>% 
    summarize(minexp=min(lifeExp),
              maxexp=max(lifeExp))
LS0tCnRpdGxlOiAiU1RBVCA1NDUgQ2xhc3MgTWVldGluZyAwNSIKb3V0cHV0OgogICAgaHRtbF9ub3RlYm9vazoKICAgICAgICB0b2M6IHRydWUKICAgICAgICB0aGVtZTogY2VydWxlYW4KICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIExlYXJuaW5nIE9iamVjdGl2ZXMKCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2FwbWluZGVyKQpgYGAKClRoZSByZXNvdXJjZXMgdW5kZXJwaW5uaW5nIHRvZGF5J3MgY2xhc3MgYXJlOgoKMS4gW0NhcmUgYW5kIEZlZWRpbmcgb2YgRGF0YSBpbiBSXShodHRwOi8vc3RhdDU0NS5jb20vYmxvY2swMDZfY2FyZS1mZWVkaW5nLWRhdGEuaHRtbCkKMi4gYGRwbHlyYCByZXNvdXJjZXM6IFtwYXJ0IDFdKGh0dHA6Ly9zdGF0NTQ1LmNvbS9ibG9jazAwOV9kcGx5ci1pbnRyby5odG1sKSBhbmQgW3BhcnQgMl0oaHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDEwX2RwbHlyLWVuZC1zaW5nbGUtdGFibGUuaHRtbCkuCjMuIFtSIGZvciBEYXRhIFNjaWVuY2UsIENoNV0oaHR0cDovL3I0ZHMuaGFkLmNvLm56L3RyYW5zZm9ybS5odG1sKSBmb3IgYGRwbHlyYC4gCgoKIyMgMS4gQSBxdWljayBsb29rIGF0IGRhdGEgdHlwZXMgaW4gUgoKIyMjIDEuMSBPdmVydmlldwoKRGF0YSB0eXBlcyBhcmUgc3VtbWFyaXplZCBuaWNlbHkgaW4gW0pCJ3Mgc21hbGwgc2xpZGVzaG93XShodHRwczovL3NwZWFrZXJkZWNrLmNvbS9qZW5ueWJjL3NpbXBsZS12aWV3LW9mLXItb2JqZWN0cykuCgojIyMgMS4yIEludmVzdGlnYXRpb24gdXNpbmcgUgoKV2UndmUgc2VlbiB0aGF0IFIgdW5kZXJzdGFuZHMgbnVtYmVycyAtLSBhIF9kYXRhIHR5cGVfLiBXaGF0IG90aGVyIHR5cGUgb2Ygb2JqZWN0cyBkb2VzIFIgcmVjb2duaXplPyBMZXQncyBpZGVudGlmeSB0aGVtLCBhbmQgdGVzdCB0aGVtIHdpdGggdGhlIGB0eXBlb2ZgIGZ1bmN0aW9uLiBUaGVyZSBhcmUKCi0gYERvdWJsZWBzIChhcyB3ZSd2ZSBzZWVuIC0tIHRoZXkncmUgbnVtYmVycykKCmBgYHtyfQp0eXBlb2YoNS42KQpgYGAKCi0gYEludGVnZXJgczogKHNwZWNpYWwgdHlwZSBvZiBudW1iZXIgLS0gbmVlZCBhbiBgTGAgYXQgdGhlIGVuZCkKCmBgYHtyfQp0eXBlb2YoNEwpCnR5cGVvZig0KQpgYGAKCi0gYGxvZ2ljYWxgcyBhcmUgdHJ1ZS9mYWxzZSAoa25vd24gYXMgYGJvb2xlYW5gcyBpbiBvdGhlciBsYW5ndWFnZXMpLiBXZSBoaWdobHkgcmVjb21tZW5kIHVzaW5nIFRSVUUvRkFMU0UgaW5zdGVhZCBvZiBUL0YgKHlvdSBjYW4gYXNzaWduIFQvRiBhcyB2YXJpYWJsZXMsIGFzIHdlJ2xsIHNlZSBsYXRlcikuIAoKYGBge3J9CnR5cGVvZihUUlVFKQp0eXBlb2YoVCkKdHlwZW9mKEZBTFNFKQp0eXBlb2YoRikKYGBgCgotIGBjaGFyYWN0ZXJgczoKCmBgYHtyfQp0eXBlb2YoIkhpLCBteSBuYW1lIGlzIFZpbmNlbnpvLiIpCmBgYAoKX19FeGVyY2lzZV9fOiBGaW5kIHRoZSBkYXRhIHR5cGUgb2YgYCI1ImAuIEV4cGxhaW4gdGhlIG91dHB1dC4gCgpgYGB7cn0KdHlwZW9mKCI1IikKYGBgCgoKX19FeGVyY2lzZV9fOiBVc2UgdGhlIGBhcy5udW1lcmljYCBmdW5jdGlvbiB0byBjb252ZXJ0IGAiNSJgIHRvIGFuIG9iamVjdCBvZiB0eXBlIGAiZG91YmxlImAuIAoKYGBge3J9CmFzLm51bWVyaWMoIjUiKQpgYGAKCgpfX0V4ZXJjaXNlX186IERlc2NyaWJlIHRoaXMgb3V0cHV0OgoKYGBge3J9CnR5cGVvZih0eXBlb2YoMTUuMikpCmBgYAoKCgojIyAyLiBEYXRhIEZyYW1lcyBpbiBSCgoKIyMjIDIuMSAiQ2FyZSBhbmQgRmVlZGluZyBvZiBEYXRhIjogRXhlcmNpc2VzCgpMYXN0IGNsYXNzLCB3ZSB3ZW50IHRocm91Z2ggdGhlIFtDYXJlIGFuZCBGZWVkaW5nIG9mIERhdGEgaW4gUl0oaHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDA2X2NhcmUtZmVlZGluZy1kYXRhLmh0bWwpIHR1dG9yaWFsLiBMZXQncyBkbyBzb21lIGV4ZXJjaXNlcywgdGhpcyB0aW1lIHdpdGggdGhlIGBpcmlzYCBkYXRhc2V0IChjb21lcyBwcmUtbG9hZGVkIGluIHRoZSBgZGF0YXNldHNgIHBhY2thZ2UpLiBVc2UgYW55IG1ldGhvZCB5b3UnZCBsaWtlIHRvIGFuc3dlciB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ucy4gCgoxLiBIb3cgbWFueSB2YXJpYWJsZXMgKGNvbHVtbnMpIGFyZSBpbiB0aGUgYGlyaXNgIGRhdGFzZXQsIGFuZCB3aGF0IGFyZSB0aGVpciBuYW1lcz8KCmBgYHtyfQpuY29sKGlyaXMpCmhlYWQoaXJpcykKYGBgCgoKMi4gSG93IG1hbnkgcm93cyBhcmUgaW4gdGhlIGRhdGEgc2V0PwoKYGBge3J9Cm5yb3coaXJpcykKZGltKGlyaXMpCmBgYAoKCjMuIFdoYXQgYXJlIHRoZSBzbWFsbGVzdCB2YWx1ZXMgb2YgZWFjaCBudW1lcmljIHZhcmlhYmxlPwoKYGBge3J9CnN1bW1hcnkoaXJpcykKYGBgCgoKNC4gRXh0cmFjdCB0aGUgYFBldGFsLldpZHRoYCBjb2x1bW4gdG8gZ2V0IGEgdmVjdG9yIG9mIG9ic2VydmF0aW9ucyAod2UnbGwgc2VlIHZlY3RvcnMgaW4gbW9yZSBkZXRhaWwgaW4gYSBsYXRlciBjbGFzcyksIGFuZAogICAgKGEpIE1ha2UgYSBoaXN0b2dyYW0KICAgIChiKSBNYWtlIGEgdGFibGUgb2YgZnJlcXVlbmNpZXMKCmBgYHtyfQp4IDwtIGlyaXMkUGV0YWwuV2lkdGgKaGlzdCh4KQp0YWJsZSh4KQpgYGAKCgojIyMgMi4yIGBkcGx5cmAgZnVuZGFtZW50YWxzCgpOZXh0LCB3ZSdsbCBsZWFybiBhYm91dCBgZHBseXJgLCBhIGhhbmR5IFIgcGFja2FnZSBmb3IgbWFuaXB1bGF0aW5nIGRhdGEgZnJhbWVzLCB0aHJvdWdoIHNpeCBtYWluIGZ1bmN0aW9ucy4gQWNjb3JkaW5nIHRvIHRoZSBbUiBmb3IgZGF0YSBzY2llbmNlXShodHRwOi8vcjRkcy5oYWQuY28ubnovdHJhbnNmb3JtLmh0bWwpIGJvb2ssIHRoZSBtYWluIGZpdmUgKGluIGRpZmZlcmVudCBvcmRlcikgYXJlOgoKPiAtIFBpY2sgb2JzZXJ2YXRpb25zIGJ5IHRoZWlyIHZhbHVlcyAoYGZpbHRlcigpYCkuCj4gLSBQaWNrIHZhcmlhYmxlcyBieSB0aGVpciBuYW1lcyAoYHNlbGVjdCgpYCkuCiAgICAKLSAoV2UnbGwgc25lYWsgInBpcGluZyIgaW50byBoZXJlKQoKPiAtIFJlb3JkZXIgdGhlIHJvd3MgKGBhcnJhbmdlKClgKS4KPiAtIENyZWF0ZSBuZXcgdmFyaWFibGVzIHdpdGggZnVuY3Rpb25zIG9mIGV4aXN0aW5nIHZhcmlhYmxlcyAoYG11dGF0ZSgpYCkuCj4gLSBDb2xsYXBzZSBtYW55IHZhbHVlcyBkb3duIHRvIGEgc2luZ2xlIHN1bW1hcnkgKGBzdW1tYXJpc2UoKWApLgoKVGhlbiB0aGVyZSdzIGBncm91cF9ieSgpYCwgd2hpY2ggY2FuIGJlIHVzZWQgaW4gY29uanVuY3Rpb24gd2l0aCBhbGwgb2YgdGhlc2UuCgpXZSdsbCBnZXQgdGhyb3VnaCBhcyBtdWNoIGFzIHdlIGNhbiBpbiB0aGlzIGNsYXNzLCBhbmQgd2lsbCBjb250aW51ZSBpbiBjbTAwNiwgcG9zc2libHkgd2l0aCBzb21lIG1vcmUgZXhlcmNpc2VzIGFuZCBtb3JlIGZlYXR1cmVzIG9mIGBkcGx5cmAuCgpSZXNvdXJjZXMgdW5kZXJwaW5uaW5nIHRoaXMgc2VjdGlvbiBjYW4gYmUgZm91bmQgaW4gdHdvIHBhcnRzOiBbcGFydCAxXShodHRwOi8vc3RhdDU0NS5jb20vYmxvY2swMDlfZHBseXItaW50cm8uaHRtbCkgYW5kIFtwYXJ0IDJdKGh0dHA6Ly9zdGF0NTQ1LmNvbS9ibG9jazAxMF9kcGx5ci1lbmQtc2luZ2xlLXRhYmxlLmh0bWwpLgoKIyMjIyBgZmlsdGVyYAoKYGZpbHRlcmAgc3Vic2V0cyBkYXRhIGZyYW1lcyBhY2NvcmRpbmcgdG8gc29tZSBsb2dpY2FsIGV4cHJlc3Npb24uIAoKQmFzaWMgZXhhbXBsZToKCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5PT0iQ2FuYWRhIikKYGBgCgpMb2dpY2FsIGV4cHJlc3Npb25zIGFyZSBnb3Zlcm5lZCBieSBfX3JlbGF0aW9uYWwgb3BlcmF0b3JzX18sIHRoYXQgb3V0cHV0IGVpdGhlciBgVFJVRWAgb3IgYEZBTFNFYCBiYXNlZCBvbiB0aGUgdmFsaWRpdHkgb2YgdGhlIHN0YXRlbWVudCB5b3UgZ2l2ZSBpdC4gSGVyZSdzIGEgc3VtbWFyeSBvZiB0aGUgb3BlcmF0b3JzOgoKfCBPcGVyYXRpb24gfCBPdXRwdXRzIGBUUlVFYCBvciBgRkFMU0VgIGJhc2VkIG9uIHRoZSB2YWxpZGl0eSBvZiB0aGUgc3RhdGVtZW50Li4uIHwKfCAtLS0tLS0gfCAtLS0tLSB8CnwgYGEgPT0gYmAgfCBgYWAgaXMgZXF1YWwgdG8gYGJgIHwKfCBgYSAhPSBiYCB8IGBhYCBpcyBub3QgZXF1YWwgdG8gYGJgLiB8CnwgYGEgPiBiYCB8IGBhYCBpcyBncmVhdGVyIHRoYW4gYGJgLiB8CnwgYGEgPCBiYCB8IGBhYCBpcyBsZXNzIHRoYW4gYGJgLiB8CnwgYGEgPj0gYmAgfCBgYWAgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIGBiYC4gfAp8IGBhIDw9IGJgIHwgYGFgIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byBgYmAuIHwKfCBgYSAlaW4lIGJgIHwgYGFgIGlzIGFuIGVsZW1lbnQgaW4gYGJgLiB8IAoKTGV0J3Mgc2VlIHNvbWUgZXhhbXBsZXM6CgoKYGBge3J9CjUgPT0gMwpjKDEsIDIsIDMpIDwgMyAgICMgdmVjdG9yaXplZCEKNCAlaW4lIGMoMSwgMiwgMywgNCwgNSkKbXlfZXF1YWxpdHkgPC0gNSA9PSAzCnByaW50KG15X2VxdWFsaXR5KQpgYGAKCgpUaGVyZSBhcmUgX19sb2dpY2FsX18gb3BlcmF0b3JzIHRvbywgYW5kIHRoZXkgZm9sbG93IEJvb2xlYW4gQWxnZWJyYS4gVGhleSdyZSBsaXN0ZWQgYmVsb3cgLS0gdGhlIGZpcnN0IHRocmVlIGFyZSBmdW5kYW1lbnRhbCwgYnV0IHRoZSBvdGhlcnMgYXJlIHVzZWZ1bCB0b28uCgp8IE9wZXJhdGlvbiB8IE91dHB1dHMgYFRSVUVgIG9yIGBGQUxTRWAgYmFzZWQgb24gdGhlIHZhbGlkaXR5IG9mIHRoZSBzdGF0ZW1lbnQuLi4gfAp8IC0tLS0tLSB8IC0tLS0tIHwKfCBgYSAmIGJgLCBgYSAmJiBiYCB8IEJvdGggYGFgIF9fYW5kX18gYGJgIGFyZSBgVFJVRWAgfAp8IGBhIHwgYmAsIGBhIHx8IGJgIHwgRWl0aGVyIGBhYCBfX29yX18gYGJgIGlzIGBUUlVFYC4gfAp8IGAhYWAgfCBgYWAgaXMgX19ub3RfXyBgVFJVRWAgKGluIG90aGVyIHdvcmRzLCB0YWtlIHRoZSBjb21wbGVtZW50IG9yICJmbGlwIiBgYWApIHwKfCBgeG9yKGEsIGIpYCB8IEVpdGhlciBgYWAgb3IgYGJgIGlzIGBUUlVFYCwgYnV0IG5vdCBib3RoLiB8CnwgYGFsbChhLGIsYywuLi4pYCB8IGBhYCwgYGJgLCBgY2AsIC4uLiBhcmUgX19hbGxfXyBgVFJVRWAuIHwKfCBgYW55KGEsYixjLC4uLilgIHwgX19BbnlfXyBvbmUgb2YgYGFgLCBgYmAsIGBjYCwgLi4uIGlzIGBUUlVFYC4gfAoKV2UgY2FuIGBmaWx0ZXJgIGJ5IG1vcmUgdGhhbiBvbmUgY29uZGl0aW9uOgoKYGBge3J9CiNmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5PT0iQ2FuYWRhIiAmIHllYXIgPCAxOTgwKSAjIHNhbWUgYXMuLi4KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeT09IkNhbmFkYSIsIHllYXIgPCAxOTcwKQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5PT0iQ2FuYWRhIiB8IHllYXIgPT0gMTk1MikKYGBgCgpGcm9tIHRoZSBQYXJ0IDEgbm90ZXMuLi4gbmV2ZXIgZG8gdGhlIGZvbGxvd2luZyEKCj4gYGBgCj4gZXhjZXJwdCA8LSBnYXBtaW5kZXJbMjQxOjI1MiwgXQo+IGBgYAo+IAo+IFdoeSBpcyB0aGlzIGEgdGVycmlibGUgaWRlYT8KPiAKPiAtIEl0IGlzIG5vdCBzZWxmLWRvY3VtZW50aW5nLiBXaGF0IGlzIHNvIHNwZWNpYWwgYWJvdXQgcm93cyAyNDEgdGhyb3VnaCAyNTI/Cj4gLSBJdCBpcyBmcmFnaWxlLiBUaGlzIGxpbmUgb2YgY29kZSB3aWxsIHByb2R1Y2UgZGlmZmVyZW50IHJlc3VsdHMgaWYgc29tZW9uZSBjaGFuZ2VzIHRoZSByb3cgb3JkZXIgb2YgYGdhcG1pbmRlcmAsIGUuZy4gc29ydHMgdGhlIGRhdGEgZWFybGllciBpbiB0aGUgc2NyaXB0LgoKX19FeGVyY2lzZXNfXzogTGV0J3MgdHJ5IHNvbWUgZXhlcmNpc2VzIHVzaW5nIHRoZSBgZ2FwbWluZGVyYCBkYXRhIHNldC4gCgoxLiBGaW5kIGFsbCBlbnRyaWVzIG9mIENhbmFkYSBhbmQgQWxnZXJpYS4gCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgCiAgICAgICBjb3VudHJ5ID09ICJDYW5hZGEiIHwKICAgICAgICAgICBjb3VudHJ5ID09ICJBbGdlcmlhIikKZmlsdGVyKGdhcG1pbmRlciwKICAgICAgIGNvdW50cnkgJWluJSBjKCJDYW5hZGEiLCAiQWxnZXJpYSIpKQpgYGAKCgoyLiBGaW5kIGFsbCBlbnRyaWVzIG9mIENhbmFkYSBhbmQgQWxnZXJpYSwgb2NjdXJpbmcgaW4gdGhlICc2MHMuIAoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsCiAgICAgICBjb3VudHJ5ICVpbiUgYygiQ2FuYWRhIiwgIkFsZ2VyaWEiKSwgeWVhciA8IDE5NzAsIHllYXIgPj0gMTk2MCkKYGBgCgoKMy4gRmluZCBhbGwgZW50cmllcyBvZiBDYW5hZGEsIGFuZCBlbnRyaWVzIG9mIEFsZ2VyaWEgb2NjdXJpbmcgaW4gdGhlICc2MHMuIApgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwKICAgICAgIChjb3VudHJ5ID09ICJDYW5hZGEiKSB8CiAgICAgICAgICAgKGNvdW50cnkgPT0gIkFsZ2VyaWEiICYgCiAgICAgICAgICAgIHllYXIgJWluJSAxOTYwOjE5NjkpKQpgYGAKCgo0LiBGaW5kIGFsbCBlbnRyaWVzIF9ub3RfIGluY2x1ZGluZyBFdXJvcGVhbiBjb3VudHJpZXMuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgCiAgICAgICBjb250aW5lbnQgIT0gIkV1cm9wZSIpCmBgYAoKCiMjIyMgYHNlbGVjdGAKCmBzZWxlY3RgIHN1YnNldHMgZGF0YSBieSBjb2x1bW5zL3ZhcmlhYmxlIG5hbWVzLgoKYGBge3J9CnNlbGVjdChnYXBtaW5kZXIsIGNvbnRpbmVudCwgY291bnRyeSkKYGBgCgotIEFsd2F5cyByZXR1cm5zIGEgdGliYmxlLiAKLSBEcm9wIHZhcmlhYmxlcyB3aXRoIGAtYC4gCi0gTm90ZSB0aGF0IHJlLW9yZGVyaW5nIGhhcHBlbnMgaGVyZS4gCgojIyMjIHBpcGluZwoKV2hhdCBpZiB3ZSB3YW50ZWQgdG8gZG8gbW9yZSB0aGFuIG9uZSBvcGVyYXRpb24/IEZvciBleGFtcGxlOgoKLSB0YWtlIGFsbCBlbnRyaWVzIG9mIENhbmFkYSBhbmQgQWxnZXJpYSBvY2N1cmluZyBpbiB0aGUgJzYwcywgYW5kCi0gc2VsZWN0IHRoZSBgY291bnRyeWAsIGB5ZWFyYCwgYW5kIGBnZHBQZXJjYXBgIGNvbHVtbnMuCgpXZSBjb3VsZCBkby4uLgoKYGBge3J9CnNlbGVjdChmaWx0ZXIoZ2FwbWluZGVyLCAKICAgICAgICAgICAgICBjb3VudHJ5ICVpbiUgYygiQ2FuYWRhIiwgIkFsZ2VyaWEiKSwgCiAgICAgICAgICAgICAgeWVhciA8PSAxOTY5LCB5ZWFyID49IDE5NjApLAogICAgICAgY291bnRyeSwgeWVhciwgZ2RwUGVyY2FwKQpgYGAKCkJ1dCB0aGUgY2hhaW4gb2YgZnVuY3Rpb25zIGNhbiBnZXQgcXVpdGUgbG9uZyBhbmQgaGFyZCB0byByZWFkLgoKVGhlIF9fcGlwZV9fIG9wZXJhdG9yIGAlPiVgIGZlZWRzIHRoZSBvdXRwdXQgb2YgYSBmdW5jdGlvbiBpbnRvIGFub3RoZXIgZnVuY3Rpb24uIFN5bnRheDoKCmBgYApnYXBtaW5kZXIgJT4lIAogICAgZjEoKSAlPiUgCiAgICBmMigpICU+JSAKICAgIGYzKG9wdGlvbnM9c29tZXRoaW5nKQpgYGAKClRoaXMgc2F5czoKCjEuIFN0YXJ0IHdpdGggdGhlIGBnYXBtaW5kZXJgIGRhdGEsIHRoZW4KMi4gYXBwbHkgYGYxYCB0byBpdCwgdGhlbgozLiBhcHBseSBgZjJgLCB0aGVuCjQuIGFwcGx5IGBmM2Agd2l0aCB0aGUgYG9wdGlvbnM9c29tZXRoaW5nYCBhcmd1bWVudC4gCgpUaGUgc2FtZSBvcGVyYXRpb24gYWJvdmUgYmVjb21lczoKCmBgYHtyfQpnYXBtaW5kZXIgJT4lIAogICAgZmlsdGVyKGNvdW50cnkgJWluJSBjKCJDYW5hZGEiLCAiQWxnZXJpYSIpLCAKICAgICAgICAgICB5ZWFyIDw9IDE5NjksIHllYXIgPj0gMTk2MCkgJT4lIAogICAgc2VsZWN0KGNvdW50cnksIHllYXIsIGdkcFBlcmNhcCkKYGBgCgpZb3UgY2FuIHJlYWQgdGhpcyBhczoKCjEuIHN0YXJ0IHdpdGggdGhlIGBnYXBtaW5kZXJgIGRhdGEsIHRoZW4KMi4gdGFrZSBhbGwgZW50cmllcyBvZiBDYW5hZGEgYW5kIEFsZ2VyaWEgb2NjdXJpbmcgaW4gdGhlICc2MHMgKGBmaWx0ZXJgKSwgdGhlbgozLiBzZWxlY3QgdGhlIGBjb3VudHJ5YCwgYHllYXJgLCBhbmQgYGdkcFBlcmNhcGAgY29sdW1ucy4gCgpfX0V4ZXJjaXNlX186IFRha2UgYWxsIGNvdW50cmllcyBpbiBFdXJvcGUgdGhhdCBoYXZlIGEgR1BEIHBlciBjYXBpdGEgZ3JlYXRlciB0aGFuIDEwMDAwLCBhbmQgc2VsZWN0IGFsbCB2YXJpYWJsZXMgZXhjZXB0IGBnZHBQZXJjYXBgLiAoSGludDogdXNlIGAtYCkuCgpgYGB7cn0KZ2FwbWluZGVyICU+JSAKICAgIGZpbHRlcihjb250aW5lbnQgPT0gIkV1cm9wZSIsCiAgICAgICAgICAgZ2RwUGVyY2FwID4gMTAwMDApICU+JSAKICAgIHNlbGVjdCgtZ2RwUGVyY2FwKQpgYGAKCgojIyMjIGBhcnJhbmdlYAoKYGFycmFuZ2VgIHNvcnRzIGEgZGF0YSBmcmFtZSBieSBzaHVmZmxpbmcgdGhlIG9yZGVyIG9mIHRoZSByb3dzIGFwcHJvcHJpYXRlbHkuIFVzZSBgZGVzY2AgdG8gc29ydCBieSBkZXNjZW5kaW5nIG9yZGVyLgoKT3JkZXIgYGdhcG1pbmRlcmAgYnkgcG9wdWxhdGlvbiwgdGhlbiBsaWZlIGV4cGVjdGFuY3k6CgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIsIHBvcCwgbGlmZUV4cCkKYGBgCgoKX19FeGVyY2lzZXNfXzoKCjEuIE9yZGVyIHRoZSBkYXRhIGZyYW1lIGJ5IHllYXIsIHRoZW4gZGVzY2VuZGluZyBieSBsaWZlIGV4cGVjdGFuY3kuCgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIsIHllYXIsIGRlc2MobGlmZUV4cCkpCmBgYAoKCjIuIEluIGFkZGl0aW9uIHRvIHRoZSBhYm92ZSBleGVyY2lzZSwgcmVhcnJhbmdlIHRoZSB2YXJpYWJsZXMgc28gdGhhdCBgeWVhcmAgY29tZXMgZmlyc3QsIGZvbGxvd2VkIGJ5IGxpZmUgZXhwZWN0YW5jeS4gKEhpbnQ6IGNoZWNrIHRoZSBkb2N1bWVudGF0aW9uIGZvciB0aGUgYHNlbGVjdGAgZnVuY3Rpb24gZm9yIGEgcmVsYXRlZCBoYW5keSBmdW5jdGlvbikuCgpgYGB7cn0KZ2FwbWluZGVyICU+JSAKICAgIGFycmFuZ2UoeWVhciwgZGVzYyhsaWZlRXhwKSkgJT4lIAogICAgc2VsZWN0KHllYXIsIGxpZmVFeHAsIGV2ZXJ5dGhpbmcoKSkKYGBgCgoKIyMjIyBgbXV0YXRlYAoKYG11dGF0ZWAgY3JlYXRlcyBhIG5ldyB2YXJpYWJsZSBieSBjYWxjdWxhdGluZyBmcm9tIG90aGVyIHZhcmlhYmxlcy4gTGV0J3MgZ2V0IEdEUCBieSBtdWx0aXBseWluZyBHUEQgcGVyIGNhcGl0YSB3aXRoIHBvcHVsYXRpb246CgpgYGB7cn0KbXV0YXRlKGdhcG1pbmRlciwgZ2RwID0gZ2RwUGVyY2FwICogcG9wKQpgYGAKCllvdSBjYW4gZGVmaW5lIG11bHRpcGxlIG5ldyB2YXJpYWJsZXMgLS0gZXZlbiBiYWNrLWRlcGVuZGVudCBvbiBuZXcgb25lcyEgTGV0J3MgYWxzbyBjcmVhdGUgYSBjb2x1bW4gZm9yIEdEUCBpbiBiaWxsaW9ucywgcm91bmRlZCB0byBvbmUgZGVjaW1hbDoKCmBgYHtyfQptdXRhdGUoZ2FwbWluZGVyLCAKICAgICAgIGdkcCAgICAgPSBnZHBQZXJjYXAgKiBwb3AsIAogICAgICAgZ2RwQmlsbCA9IHJvdW5kKGdkcC8xMDAwMDAwMDAwLCAxKSkKYGBgCgpgdHJhbnNtdXRlYCB3b3JrcyB0aGUgc2FtZSB3YXksIGJ1dCBkcm9wcyBhbGwgb3RoZXIgdmFyaWFibGVzLiAKCl9fRXhlcmNpc2VfXzogTWFrZSBhIG5ldyBjb2x1bW4gY2FsbGVkIGBjY2AgdGhhdCBwYXN0ZXMgdGhlIGNvdW50cnkgbmFtZSBmb2xsb3dlZCBieSB0aGUgY29udGluZW50LCBzZXBhcmF0ZWQgYnkgYSBjb21tYS4gKEhpbnQ6IHVzZSB0aGUgYHBhc3RlYCBmdW5jdGlvbiB3aXRoIHRoZSBgc2VwPSIsICJgIGFyZ3VtZW50KS4KCmBgYHtyfQptdXRhdGUoZ2FwbWluZGVyLCBjYyA9IHBhc3RlKGNvdW50cnksIGNvbnRpbmVudCwgc2VwPSIsICIpKQpgYGAKCgojIyMjIGBzdW1tYXJpemVgIGFuZCBgZ3JvdXBfYnlgCgpgc3VtbWFyaXplYCByZWR1Y2VzIGEgdGliYmxlIGFjY29yZGluZyB0byBzdW1tYXJ5IHN0YXRpc3RpY3MuCgpgYGB7cn0Kc3VtbWFyaXplKGdhcG1pbmRlciwgbWVhbl9wb3A9bWVhbihwb3ApLCBzZF9wb3A9c2QocG9wKSkKYGBgCgpOb3QgdmVyeSB1c2VmdWwgYnkgaXRzZWxmISBCdXQsIHdpdGggdGhlIGBncm91cF9ieWAgZnVuY3Rpb24sIHRoZSBgc3VtbWFyaXplYCBmdW5jdGlvbiBpcyB2ZXJ5IHVzZWZ1bDoKCmBgYHtyfQpnYXBtaW5kZXIgJT4lIAogICAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIAogICAgc3VtbWFyaXplKG1lYW5fcG9wPW1lYW4ocG9wKSwgc2RfcG9wPXNkKHBvcCkpCmBgYAoKVGhlIGBncm91cF9ieWAgZnVuY3Rpb24gc3BsaXRzIHRoZSB0aWJibGUgaW50byBwYXJ0cyAtLSBpbiB0aGUgYWJvdmUgY2FzZSwgYnkgY291bnRyeS4gTm90aWNlIHRoZSAiR3JvdXBzIiBpbmRpY2F0b3IgaW4gdGhlIGZvbGxvd2luZyBvdXRwdXQ6CgpgYGB7cn0KZ3JvdXBfYnkoZ2FwbWluZGVyLCBjb250aW5lbnQsIGNvdW50cnksIHllYXIgPCAxOTcwKQpgYGAKCkxldCdzIGdldCBhIHN1bW1hcnkgb2YgdGhpcyBncm91cGluZzoKCmBgYHtyfQoob3V0MSA8LSBnYXBtaW5kZXIgJT4lIAogICAgZ3JvdXBfYnkoY29udGluZW50LCBjb3VudHJ5LCB5ZWFyIDwgMTk3MCkgJT4lIAogICAgc3VtbWFyaXplKG1lYW5fcG9wPW1lYW4ocG9wKSwgc2RfcG9wPXNkKHBvcCkpKQpgYGAKCk5vdGUgdGhhdCB0aGUgb3V0cHV0IGlzIHN0aWxsIGEgdGliYmxlLCBidXQgb25lICJsYXllciIgb2YgZ3JvdXBpbmcgaGFzIGJlZW4gcGVlbGVkIGJhY2s6IHRoZSBgeWVhciA8IDE5NzBgIHZhcmlhYmxlLiBgc3VtbWFyaXplYCBhZ2FpbiBhbmQgeW91J2xsIHNlZSB0aGF0IHRoZSB0aWJibGUgd2FzIG5vIGxvbmdlciBncm91cGVkIGJ5IGB5ZWFyIDwgMTk3MGAuIAoKYGBge3J9Cm91dDEgJT4lIAogICAgc3VtbWFyaXplKG1lYW5fcG9wPW1lYW4obWVhbl9wb3ApKQpgYGAKCgpfX0V4ZXJjaXNlX186IEZpbmQgdGhlIG1pbmltdW0gR0RQIHBlciBjYXBpdGEgZXhwZXJpZW5jZWQgYnkgZWFjaCBjb3VudHJ5CgpgYGB7cn0KZ2FwbWluZGVyICU+JQogICAgZ3JvdXBfYnkoY291bnRyeSkgJT4lIAogICAgc3VtbWFyaXplKG1pbmdkcD1taW4oZ2RwUGVyY2FwKSkKYGBgCgoKX19FeGVyY2lzZV9fOiBIb3cgbWFueSB5ZWFycyBvZiByZWNvcmQgZG9lcyBlYWNoIGNvdW50cnkgaGF2ZT8KCmBgYHtyfQpnYXBtaW5kZXIgJT4lCiAgICBncm91cF9ieShjb3VudHJ5KSAlPiUgCiAgICBzdW1tYXJpemUobl9kaXN0aW5jdCh5ZWFyKSkKYGBgCgoKX19FeGVyY2lzZV9fOiBXaXRoaW4gQXNpYSwgd2hhdCBhcmUgdGhlIG1pbiBhbmQgbWF4IGxpZmUgZXhwZWN0YW5jaWVzIGV4cGVyaWVuY2VkIGluIGVhY2ggeWVhcj8KCmBgYHtyfQpnYXBtaW5kZXIgJT4lIAogICAgZmlsdGVyKGNvbnRpbmVudD09IkFzaWEiKSAlPiUgCiAgICBncm91cF9ieSh5ZWFyKSAlPiUgCiAgICBzdW1tYXJpemUobWluZXhwPW1pbihsaWZlRXhwKSwKICAgICAgICAgICAgICBtYXhleHA9bWF4KGxpZmVFeHApKQpgYGAKCg==