Intro and Overview
This document has a set of exercises for training your R programming skills using the tidyverse packages to process and analyses example datasets.
You will need:
- to have been introduced to R and tidyverse
- R and RStudio installed
- the tidyverse package installed
Datasets:
- Exercises will use built-in datasets
- built-in datasets are already loaded in R and ready to use
- you should read help pages of the datasets you analyze
- The titanic dataset is not built-in but it will be accessible by an URL
Solution to exercises can be revealed by clicking on the [Code] buttons displayed at the right-hand side of the exercises.
Preparation
Load the tidyverse package.
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
-- Attaching packages ----------------------------------------------- tidyverse 1.3.0 --
v ggplot2 3.3.3 v purrr 0.3.4
v tibble 3.0.6 v dplyr 1.0.4
v tidyr 1.1.2 v stringr 1.4.0
v readr 1.4.0 v forcats 0.5.1
-- Conflicts -------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
Datasets
Built-in dataset: trees
This data set provides measurements of the diameter, height and volume of timber in 31 felled black cherry trees. Note that the diameter (in inches) is erroneously labelled Girth in the data. It is measured at 4 ft 6 in above the ground.
- Show the head of table trees
- Create trees2 variable by copying trees and
- Renaming column Girth to Diameter
- Converting Diameter and Height to centimeters (1 inch = 2,54 cm)
- Converting Volume in cubic meters (1 cibic foot = 0,0283168 cubic meter)
trees2 <- trees %>%
rename(Diameter=Girth) %>%
mutate(Diameter=Diameter*2.54, Height=Height*2.54) %>%
mutate(Volume=Volume*0.0283168)
- Show the head of table trees2
- Calculate the mean value of each column
trees2 %>%
summarise(
mean.diameter=mean(Diameter),
mean.height=mean(Height),
mean.vol=mean(Volume)
)
- Save in variable trees2.plot a scatter plot of the diameter vs height
- color points by Volume
- add a title to the plot using ggtitle()
trees2.plot <- trees2 %>%
ggplot(aes(x=Diameter, y=Height, color=Volume)) +
geom_point() +
ggtitle("Scatter Plot")
- save the plot in a PNG image file on your computer
- use trees2.plot + ggsave() with appropriate parameters for ggsave
- read the help of the function to create a 10x10cm plot named “trees2.plot.png”
trees2.plot +
ggsave(filename = "scatterplot.png", width = 10, height = 10, units = "cm")

Built-in dataset: PlantGrowth
Results from an experiment to compare yields (as measured by dried weight of plants) obtained under a control and two different treatment conditions.
- Show a summary of the table using summary(TABLE) (not a tidyverse’s function)
weight group
Min. :3.590 ctrl:10
1st Qu.:4.550 trt1:10
Median :5.155 trt2:10
Mean :5.073
3rd Qu.:5.530
Max. :6.310
- Show a density plot of the weight values divided by group in a single plot
PlantGrowth %>%
ggplot(aes(x=weight, fill=group)) +
geom_density()

- Tuning the plots is sometimes as simple as using a special parameter to a ggplot layer
- replot the same plot with the following setting in geom_density() to set the transparency: alpha=0.2
- alpha can take values from 0 to 1, test alpha=0.5 and alpha=0.8
PlantGrowth %>%
ggplot(aes(x=weight, fill=group)) +
geom_density(alpha=0.2)

PlantGrowth %>%
ggplot(aes(x=weight, fill=group)) +
geom_density(alpha=0.5)

PlantGrowth %>%
ggplot(aes(x=weight, fill=group)) +
geom_density(alpha=0.8)

Built-in dataset: CO2
The CO2 data frame has 84 rows and 5 columns of data from an experiment on the cold tolerance of the grass species Echinochloa crus-galli.
- read the documentation of the CO2 dataset to understand the columns
- show a summary of the table
Plant Type Treatment conc uptake
Qn1 : 7 Quebec :42 nonchilled:42 Min. : 95 Min. : 7.70
Qn2 : 7 Mississippi:42 chilled :42 1st Qu.: 175 1st Qu.:17.90
Qn3 : 7 Median : 350 Median :28.30
Qc1 : 7 Mean : 435 Mean :27.21
Qc3 : 7 3rd Qu.: 675 3rd Qu.:37.12
Qc2 : 7 Max. :1000 Max. :45.50
(Other):42
- Calculate the minimum and maximum uptake per geographical place of origin
CO2 %>%
group_by(Type) %>%
summarise(
min=min(uptake),
max=max(uptake),
)
- Create a line graph showing uptake by concentration for each plant
CO2 %>%
ggplot(aes(x=conc, y=uptake, color=Plant)) +
geom_line() +
geom_point()

Built-in dataset: WorldPhones
The number of telephones in various regions of the world (in thousands).
- show the matrix WorldPhones
N.Amer Europe Asia S.Amer Oceania Africa Mid.Amer
1951 45939 21574 2876 1815 1646 89 555
1956 60423 29990 4708 2568 2366 1411 733
1957 64721 32510 5230 2695 2526 1546 773
1958 68484 35218 6662 2845 2691 1663 836
1959 71799 37598 6856 3000 2868 1769 911
1960 76036 40341 8220 3145 3054 1905 1008
1961 79831 43173 9053 3338 3224 2005 1076
- Convert the matrix into a tibble named phones and show the tibble
- adapt the following template: as_tibble(MATRIX, rownames=“year”)
- Parameter rownames is needed because by default row names are not kept by as_tibble()
phones <- as_tibble(WorldPhones, rownames="year")
phones
- Tidy up the tibble in order to make an observation a geographical area in a year
phones <- phones %>%
gather(N.Amer:Mid.Amer, key="area", value="phones")
phones
- Create a plot to show the number of phones by year in each geographical area
- use facets and colors for the areas
phones %>%
ggplot(aes(x=year, y=phones, color=area)) +
geom_point() +
facet_wrap(vars(area))

Built-in dataset: mtcars
The data was extracted from the 1974 Motor Trend US magazine, and comprises fuel consumption and 10 aspects of automobile design and performance for 32 automobiles (1973–74 models).
- read the related help page to understand the columns
- show the head of the data frame
- To compare engine types (vs), calculate mean gross horsepower and mean time per 1/4 mile
mtcars %>%
group_by(vs) %>%
summarise(mean.power=mean(hp), mean.time=mean(qsec))
- create a boxplot to compare the weight per engine type (1 plot with 2 boxes)
- Hint: the x axis of a boxplot should not be a numeric column
mtcars %>%
mutate(vs=as.character(vs)) %>%
ggplot(aes(x=vs, y=wt)) +
geom_boxplot()

- fastest car per category
- a category is defined by a particular combination of engine (vs) and transmission (am)
- calculate the fastest car in each category
- the data frame’s rownames must be first transformed as a normal column using rownames_to_column(“car”)
mtcars %>%
rownames_to_column("car") %>%
group_by(vs, am) %>%
filter(qsec==max(qsec))
- fastest car per category
- reproduce the result above using the slice_max dplyr’s function
- adapt the following template: slice_max(COLUMN, n=1)
mtcars %>%
rownames_to_column("car") %>%
group_by(vs, am) %>%
slice_max(qsec)
Titanic dataset
Prepare the data
- Load the titanic dataset into variable titanic.source from the following URL:
http://cbdm-01.zdv.uni-mainz.de/~jfontain/files/r_tuto_ggplot/titanic.source
titanic.source <- read_tsv("http://cbdm-01.zdv.uni-mainz.de/~jfontain/files/r_tuto_ggplot/titanic.tsv")
- observe the 20 first rows using head() function (see help page to see how to define number of rows). Would it be possible after some processing to derive a numerical age value for each row?
titanic.source %>% head(n=20)
- create variable titanic with a copy of titanic.source
- filter the table to keep rows that contains a possible value for age
- tidy up the table in order to have a numerical column for Age
- remove any temporary column created during this task, if relevant
- show the head of the new table
titanic <- titanic.source %>%
filter(Age!="Not Available") %>%
separate(Age, into = c("Age", "rest"), sep=", ") %>%
mutate(Age=as.numeric(Age)) %>%
select(-rest)
titanic %>% head()
Survival status
- Display a bar plot with numbers of survived and died passengers in each class
- use titanic table
- use aes() with parameter x and fill
- Tuning plots can get complicated: add the following ggplot layer after geom_bar() to show numbers in bars:
stat_count(geom = "text", colour = "white", aes(label = ..count..), position=position_stack(vjust=0.5))
titanic %>%
ggplot(aes(x=Class, fill=SurvivalStatus)) +
geom_bar() +
stat_count(geom = "text", colour = "white", aes(label = ..count..), position=position_stack(vjust=0.5))

- In a new table titanic.stats, calculate numbers of survived and died passengers in each class
- use titanic table
- hint: use summarise(n=n()) where function n() counts number of rows
titanic.stats <- titanic %>%
group_by(Class, SurvivalStatus) %>%
summarise(n=n())
`summarise()` has grouped output by 'Class'. You can override using the `.groups` argument.
- tidy the titanic.stats table to make a class an observation, resulting in a new table with 3 columns (Class, died, survived)
titanic.stats <- titanic.stats %>%
spread(SurvivalStatus, n)
titanic.stats
- Using the titanic.stats table, calculate the frequency for male or female passengers to die in each class
titanic.stats %>%
mutate(freq=died/(died+survived))
Distribution of age
- Calculate the mean age in each class for male and female passengers using summarise()
- use titanic table
- Note that the setting na.rm=TRUE for the mean() function prevents the calculation to fail in cases of missing values. As we filtered out missing values earlier it should not be necessary. Also applies to sum, min and max functions.
titanic %>%
group_by(Class, Sex) %>%
summarise(avg=mean(Age, na.rm=TRUE))
`summarise()` has grouped output by 'Class'. You can override using the `.groups` argument.
- plot the distribution of age in each class for male and female passengers using boxplots
- use parameters x, y and fill in aes()
titanic %>%
ggplot(aes(y=Age, x=Sex, fill=Class)) +
geom_boxplot()

- Create subplots of the previous plot by survival status
titanic %>%
ggplot(aes(y=Age, x=Sex, fill=Class)) +
geom_boxplot() +
facet_wrap(vars(SurvivalStatus))

LS0tDQp0aXRsZTogIkRhdGEgYW5hbHlzaXMgd2l0aCBSIGFuZCB0aGUgdGlkeXZlcnNlIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogcmVhZGFibGUNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyBJbnRybyBhbmQgT3ZlcnZpZXcNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KVGhpcyBkb2N1bWVudCBoYXMgYSBzZXQgb2YgZXhlcmNpc2VzIGZvciB0cmFpbmluZyB5b3VyIFIgcHJvZ3JhbW1pbmcgc2tpbGxzIA0KdXNpbmcgdGhlIHRpZHl2ZXJzZSBwYWNrYWdlcyB0byBwcm9jZXNzIGFuZCBhbmFseXNlcyBleGFtcGxlIGRhdGFzZXRzLg0KDQpZb3Ugd2lsbCBuZWVkOg0KDQoqIHRvIGhhdmUgYmVlbiBpbnRyb2R1Y2VkIHRvIFIgYW5kIHRpZHl2ZXJzZQ0KKiBSIGFuZCBSU3R1ZGlvIGluc3RhbGxlZA0KKiB0aGUgdGlkeXZlcnNlIHBhY2thZ2UgaW5zdGFsbGVkDQoNCkRhdGFzZXRzOg0KDQoqIEV4ZXJjaXNlcyB3aWxsIHVzZSBidWlsdC1pbiBkYXRhc2V0cw0KKiBidWlsdC1pbiBkYXRhc2V0cyBhcmUgYWxyZWFkeSBsb2FkZWQgaW4gUiBhbmQgcmVhZHkgdG8gdXNlDQoqIHlvdSBzaG91bGQgcmVhZCBoZWxwIHBhZ2VzIG9mIHRoZSBkYXRhc2V0cyB5b3UgYW5hbHl6ZSANCiogVGhlIHRpdGFuaWMgZGF0YXNldCBpcyBub3QgYnVpbHQtaW4gYnV0IGl0IHdpbGwgYmUgYWNjZXNzaWJsZSBieSBhbiBVUkwNCg0KU29sdXRpb24gdG8gZXhlcmNpc2VzIGNhbiBiZSByZXZlYWxlZCBieSBjbGlja2luZyBvbiB0aGUgKipbQ29kZV0qKiBidXR0b25zIGRpc3BsYXllZCANCmF0IHRoZSByaWdodC1oYW5kIHNpZGUgb2YgdGhlIGV4ZXJjaXNlcy4gDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyBQcmVwYXJhdGlvbg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KDQpMb2FkIHRoZSB0aWR5dmVyc2UgcGFja2FnZS4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQojIGRhdGEoKQ0Kcm9jaw0Kc3RhdGUueDc3DQphaXJxdWFsaXR5DQpPcmFuZ2UNCg0KDQojIHRyYWluLmRhdGEgPC0gcmVhZF90c3YoImh0dHBzOi8vd3d3LndvbGZyYW1jbG91ZC5jb20vb2JqZWN0cy84YmJlOTc1Yy00OGE5LTRkMzYtYTM1OC0xZGRlN2M1YzU3MmEiKQ0KIyB0cmFpbi5kYXRhICU+JSBtdXRhdGUoQWdlID0gc3RyX3JlcGxhY2UoQWdlLCAiUXVhbnRpdHlcXFsiLCAiIikpICU+JSANCiMgICBtdXRhdGUoQWdlID0gc3RyX3JlcGxhY2UoQWdlLCAiTWlzc2luZ1xcWyIsICIiKSkgJT4lICANCiMgICBtdXRhdGUoQWdlID0gc3RyX3JlcGxhY2UoQWdlLCAiXFxdIiwgIiIpKSAlPiUgDQojICAgbXV0YXRlKEFnZSA9IHN0cl9yZXBsYWNlX2FsbChBZ2UsICciJywgIiIpKSAlPiUgDQojICAgbXV0YXRlKEFnZSA9IHN0cl9yZXBsYWNlKEFnZSwgIlxcLiwiLCAiLjAsIikpICAlPiUgDQojICAgd3JpdGVfdHN2KCJ0aXRhbmljLnRzdiIpDQojIHRpdGFuaWMgJT4lIGhlYWQobj0yMCkNCmBgYA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMgRGF0YXNldHMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgQnVpbHQtaW4gZGF0YXNldDogdHJlZXMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KVGhpcyBkYXRhIHNldCBwcm92aWRlcyBtZWFzdXJlbWVudHMgb2YgdGhlIGRpYW1ldGVyLCBoZWlnaHQgYW5kIHZvbHVtZSBvZiB0aW1iZXIgaW4gMzEgZmVsbGVkIGJsYWNrIGNoZXJyeSB0cmVlcy4gTm90ZSB0aGF0IHRoZSBkaWFtZXRlciAoaW4gaW5jaGVzKSBpcyBlcnJvbmVvdXNseSBsYWJlbGxlZCBHaXJ0aCBpbiB0aGUgZGF0YS4gSXQgaXMgbWVhc3VyZWQgYXQgNCBmdCA2IGluIGFib3ZlIHRoZSBncm91bmQuDQoNCiogU2hvdyB0aGUgaGVhZCBvZiB0YWJsZSAqKnRyZWVzKiogDQpgYGB7cn0NCnRyZWVzICU+JSBoZWFkKCkNCmBgYA0KDQoqIENyZWF0ZSAqKnRyZWVzMioqIHZhcmlhYmxlIGJ5IGNvcHlpbmcgKip0cmVlcyoqIGFuZA0KICAgICogUmVuYW1pbmcgY29sdW1uICoqR2lydGgqKiB0byAqKkRpYW1ldGVyKioNCiAgICAqIENvbnZlcnRpbmcgKipEaWFtZXRlcioqIGFuZCAqKkhlaWdodCoqIHRvIGNlbnRpbWV0ZXJzICgxIGluY2ggPSAyLDU0IGNtKQ0KICAgICogQ29udmVydGluZyBWb2x1bWUgaW4gY3ViaWMgbWV0ZXJzICgxIGNpYmljIGZvb3QgPSAwLDAyODMxNjggY3ViaWMgbWV0ZXIpDQpgYGB7cn0NCnRyZWVzMiA8LSB0cmVlcyAlPiUgDQogIHJlbmFtZShEaWFtZXRlcj1HaXJ0aCkgJT4lIA0KICBtdXRhdGUoRGlhbWV0ZXI9RGlhbWV0ZXIqMi41NCwgSGVpZ2h0PUhlaWdodCoyLjU0KSAlPiUgDQogIG11dGF0ZShWb2x1bWU9Vm9sdW1lKjAuMDI4MzE2OCkNCmBgYA0KDQoqIFNob3cgdGhlIGhlYWQgb2YgdGFibGUgKip0cmVlczIqKiANCmBgYHtyfQ0KdHJlZXMyICU+JSBoZWFkKCkNCmBgYA0KDQoqIENhbGN1bGF0ZSB0aGUgbWVhbiB2YWx1ZSBvZiBlYWNoIGNvbHVtbiANCmBgYHtyfQ0KdHJlZXMyICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIG1lYW4uZGlhbWV0ZXI9bWVhbihEaWFtZXRlciksDQogICAgbWVhbi5oZWlnaHQ9bWVhbihIZWlnaHQpLA0KICAgIG1lYW4udm9sPW1lYW4oVm9sdW1lKQ0KICAgICkNCmBgYA0KDQoqIFNhdmUgaW4gdmFyaWFibGUgKip0cmVlczIucGxvdCoqIGEgc2NhdHRlciBwbG90IG9mIHRoZSBkaWFtZXRlciB2cyBoZWlnaHQgDQogICAgKiBjb2xvciBwb2ludHMgYnkgKipWb2x1bWUqKg0KICAgICogYWRkIGEgdGl0bGUgdG8gdGhlIHBsb3QgdXNpbmcgKipnZ3RpdGxlKCkqKiAgIA0KYGBge3J9DQp0cmVlczIucGxvdCA8LSB0cmVlczIgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9RGlhbWV0ZXIsIHk9SGVpZ2h0LCBjb2xvcj1Wb2x1bWUpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBnZ3RpdGxlKCJTY2F0dGVyIFBsb3QiKQ0KYGBgDQoNCg0KKiBzYXZlIHRoZSBwbG90IGluIGEgUE5HIGltYWdlIGZpbGUgb24geW91ciBjb21wdXRlcg0KICAgICogdXNlIHRyZWVzMi5wbG90ICsgKipnZ3NhdmUoKSoqIHdpdGggYXBwcm9wcmlhdGUgcGFyYW1ldGVycyBmb3IgKipnZ3NhdmUqKg0KICAgICogcmVhZCB0aGUgaGVscCBvZiB0aGUgZnVuY3Rpb24gdG8gY3JlYXRlIGEgMTB4MTBjbSBwbG90IG5hbWVkICoqInRyZWVzMi5wbG90LnBuZyIqKg0KYGBge3J9DQp0cmVlczIucGxvdCArDQogIGdnc2F2ZShmaWxlbmFtZSA9ICJzY2F0dGVycGxvdC5wbmciLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCwgdW5pdHMgPSAiY20iKQ0KYGBgDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIEJ1aWx0LWluIGRhdGFzZXQ6IFBsYW50R3Jvd3RoDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQoNClJlc3VsdHMgZnJvbSBhbiBleHBlcmltZW50IHRvIGNvbXBhcmUgeWllbGRzIChhcyBtZWFzdXJlZCBieSBkcmllZCB3ZWlnaHQgb2YgDQpwbGFudHMpIG9idGFpbmVkIHVuZGVyIGEgY29udHJvbCBhbmQgdHdvIGRpZmZlcmVudCB0cmVhdG1lbnQgY29uZGl0aW9ucy4NCg0KKiBTaG93IGEgc3VtbWFyeSBvZiB0aGUgdGFibGUgdXNpbmcgKipzdW1tYXJ5KFRBQkxFKSoqIChub3QgYSB0aWR5dmVyc2UncyBmdW5jdGlvbikNCmBgYHtyfQ0Kc3VtbWFyeShQbGFudEdyb3d0aCkNCmBgYA0KDQoqIFNob3cgYSBkZW5zaXR5IHBsb3Qgb2YgdGhlICoqd2VpZ2h0KiogdmFsdWVzIGRpdmlkZWQgYnkgKipncm91cCoqIGluIGEgc2luZ2xlIHBsb3QNCmBgYHtyfQ0KUGxhbnRHcm93dGggJT4lIA0KICBnZ3Bsb3QoYWVzKHg9d2VpZ2h0LCBmaWxsPWdyb3VwKSkgKw0KICBnZW9tX2RlbnNpdHkoKQ0KYGBgDQoqIFR1bmluZyB0aGUgcGxvdHMgaXMgc29tZXRpbWVzIGFzIHNpbXBsZSBhcyB1c2luZyBhIHNwZWNpYWwgcGFyYW1ldGVyIHRvIGEgZ2dwbG90IGxheWVyDQogICAgKiByZXBsb3QgdGhlIHNhbWUgcGxvdCB3aXRoIHRoZSBmb2xsb3dpbmcgc2V0dGluZyBpbiAqKmdlb21fZGVuc2l0eSgpKiogdG8gc2V0IHRoZSB0cmFuc3BhcmVuY3k6ICoqYWxwaGE9MC4yKioNCiAgICAqIGFscGhhIGNhbiB0YWtlIHZhbHVlcyBmcm9tIDAgdG8gMSwgdGVzdCAqKmFscGhhPTAuNSoqIGFuZCAqKmFscGhhPTAuOCoqDQpgYGB7cn0NClBsYW50R3Jvd3RoICU+JSANCiAgZ2dwbG90KGFlcyh4PXdlaWdodCwgZmlsbD1ncm91cCkpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuMikNClBsYW50R3Jvd3RoICU+JSANCiAgZ2dwbG90KGFlcyh4PXdlaWdodCwgZmlsbD1ncm91cCkpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSkNClBsYW50R3Jvd3RoICU+JSANCiAgZ2dwbG90KGFlcyh4PXdlaWdodCwgZmlsbD1ncm91cCkpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuOCkNCmBgYA0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBCdWlsdC1pbiBkYXRhc2V0OiBDTzINCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KVGhlIENPMiBkYXRhIGZyYW1lIGhhcyA4NCByb3dzIGFuZCA1IGNvbHVtbnMgb2YgZGF0YSBmcm9tIGFuIGV4cGVyaW1lbnQgb24gdGhlIGNvbGQgdG9sZXJhbmNlIG9mIHRoZSBncmFzcyBzcGVjaWVzIEVjaGlub2NobG9hIGNydXMtZ2FsbGkuDQoNCiogcmVhZCB0aGUgZG9jdW1lbnRhdGlvbiBvZiB0aGUgKipDTzIqKiBkYXRhc2V0IHRvIHVuZGVyc3RhbmQgdGhlIGNvbHVtbnMNCiogc2hvdyBhIHN1bW1hcnkgb2YgdGhlIHRhYmxlDQpgYGB7cn0NCnN1bW1hcnkoQ08yKQ0KYGBgDQoNCiogQ2FsY3VsYXRlIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHVwdGFrZSBwZXIgZ2VvZ3JhcGhpY2FsIHBsYWNlIG9mIG9yaWdpbg0KYGBge3J9DQpDTzIgJT4lIA0KICBncm91cF9ieShUeXBlKSAlPiUgDQogIHN1bW1hcmlzZSgNCiAgICBtaW49bWluKHVwdGFrZSksDQogICAgbWF4PW1heCh1cHRha2UpLA0KICApDQpgYGANCg0KKiBDcmVhdGUgYSBsaW5lIGdyYXBoIHNob3dpbmcgdXB0YWtlIGJ5IGNvbmNlbnRyYXRpb24gZm9yIGVhY2ggcGxhbnQNCmBgYHtyfQ0KQ08yICU+JSANCiAgZ2dwbG90KGFlcyh4PWNvbmMsIHk9dXB0YWtlLCBjb2xvcj1QbGFudCkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIEJ1aWx0LWluIGRhdGFzZXQ6IFdvcmxkUGhvbmVzDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQoNClRoZSBudW1iZXIgb2YgdGVsZXBob25lcyBpbiB2YXJpb3VzIHJlZ2lvbnMgb2YgdGhlIHdvcmxkIChpbiB0aG91c2FuZHMpLg0KDQoqIHNob3cgdGhlIG1hdHJpeCAqKldvcmxkUGhvbmVzKioNCmBgYHtyfQ0KV29ybGRQaG9uZXMNCmBgYA0KDQoqIENvbnZlcnQgdGhlIG1hdHJpeCBpbnRvIGEgdGliYmxlIG5hbWVkICoqcGhvbmVzKiogYW5kIHNob3cgdGhlIHRpYmJsZQ0KICAgICogYWRhcHQgdGhlIGZvbGxvd2luZyB0ZW1wbGF0ZTogKiphc190aWJibGUoTUFUUklYLCByb3duYW1lcz0ieWVhciIpKioNCiAgICAqIFBhcmFtZXRlciAqKnJvd25hbWVzKiogaXMgbmVlZGVkIGJlY2F1c2UgYnkgZGVmYXVsdCByb3cgbmFtZXMgYXJlIG5vdCBrZXB0IGJ5ICoqYXNfdGliYmxlKCkqKg0KDQpgYGB7cn0NCnBob25lcyA8LSBhc190aWJibGUoV29ybGRQaG9uZXMsIHJvd25hbWVzPSJ5ZWFyIikNCnBob25lcw0KYGBgDQoNCiogVGlkeSB1cCB0aGUgdGliYmxlIGluIG9yZGVyIHRvIG1ha2UgYW4gb2JzZXJ2YXRpb24gYSBnZW9ncmFwaGljYWwgYXJlYSBpbiBhIHllYXIgDQpgYGB7cn0NCnBob25lcyA8LSBwaG9uZXMgJT4lIA0KICBnYXRoZXIoTi5BbWVyOk1pZC5BbWVyLCBrZXk9ImFyZWEiLCB2YWx1ZT0icGhvbmVzIikNCnBob25lcw0KYGBgDQoNCiogQ3JlYXRlIGEgcGxvdCB0byBzaG93IHRoZSBudW1iZXIgb2YgcGhvbmVzIGJ5IHllYXIgaW4gZWFjaCBnZW9ncmFwaGljYWwgYXJlYQ0KICAgICogdXNlIGZhY2V0cyBhbmQgY29sb3JzIGZvciB0aGUgYXJlYXMgDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0NCnBob25lcyAlPiUgDQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXBob25lcywgY29sb3I9YXJlYSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGFyZWEpKQ0KYGBgDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgQnVpbHQtaW4gZGF0YXNldDogbXRjYXJzDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQoNClRoZSBkYXRhIHdhcyBleHRyYWN0ZWQgZnJvbSB0aGUgMTk3NCBNb3RvciBUcmVuZCBVUyBtYWdhemluZSwgYW5kIGNvbXByaXNlcyBmdWVsIGNvbnN1bXB0aW9uIGFuZCAxMCBhc3BlY3RzIG9mIGF1dG9tb2JpbGUgZGVzaWduIGFuZCBwZXJmb3JtYW5jZSBmb3IgMzIgYXV0b21vYmlsZXMgKDE5NzPigJM3NCBtb2RlbHMpLg0KDQoqIHJlYWQgdGhlIHJlbGF0ZWQgaGVscCBwYWdlIHRvIHVuZGVyc3RhbmQgdGhlIGNvbHVtbnMNCiogc2hvdyB0aGUgaGVhZCBvZiB0aGUgZGF0YSBmcmFtZQ0KYGBge3J9DQptdGNhcnMgJT4lIGhlYWQoKQ0KYGBgDQoNCiogVG8gY29tcGFyZSBlbmdpbmUgdHlwZXMgKHZzKSwgY2FsY3VsYXRlIG1lYW4gZ3Jvc3MgaG9yc2Vwb3dlciBhbmQgbWVhbiB0aW1lIHBlciAxLzQgbWlsZQ0KYGBge3J9DQptdGNhcnMgJT4lDQogIGdyb3VwX2J5KHZzKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuLnBvd2VyPW1lYW4oaHApLCBtZWFuLnRpbWU9bWVhbihxc2VjKSkNCmBgYA0KDQoNCiogY3JlYXRlIGEgYm94cGxvdCB0byBjb21wYXJlIHRoZSB3ZWlnaHQgcGVyIGVuZ2luZSB0eXBlICgxIHBsb3Qgd2l0aCAyIGJveGVzKQ0KICAgICogSGludDogdGhlIHggYXhpcyBvZiBhIGJveHBsb3Qgc2hvdWxkIG5vdCBiZSBhIG51bWVyaWMgY29sdW1uDQoNCmBgYHtyfQ0KbXRjYXJzICU+JSANCiAgbXV0YXRlKHZzPWFzLmNoYXJhY3Rlcih2cykpICU+JSANCiAgZ2dwbG90KGFlcyh4PXZzLCB5PXd0KSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoqIGZhc3Rlc3QgY2FyIHBlciBjYXRlZ29yeQ0KICAgICogYSBjYXRlZ29yeSBpcyBkZWZpbmVkIGJ5IGEgcGFydGljdWxhciBjb21iaW5hdGlvbiBvZiBlbmdpbmUgKHZzKSBhbmQgdHJhbnNtaXNzaW9uIChhbSkNCiAgICAqIGNhbGN1bGF0ZSB0aGUgZmFzdGVzdCBjYXIgaW4gZWFjaCBjYXRlZ29yeSANCiAgICAgICAgKiB0aGUgZGF0YSBmcmFtZSdzIHJvd25hbWVzIG11c3QgYmUgZmlyc3QgdHJhbnNmb3JtZWQgYXMgYSBub3JtYWwgY29sdW1uIHVzaW5nICoqcm93bmFtZXNfdG9fY29sdW1uKCJjYXIiKSoqDQogICAgICAgIA0KYGBge3J9DQptdGNhcnMgJT4lIA0KICByb3duYW1lc190b19jb2x1bW4oImNhciIpICU+JSANCiAgZ3JvdXBfYnkodnMsIGFtKSAlPiUgDQogIGZpbHRlcihxc2VjPT1tYXgocXNlYykpDQpgYGANCiogZmFzdGVzdCBjYXIgcGVyIGNhdGVnb3J5DQogICAgKiByZXByb2R1Y2UgdGhlIHJlc3VsdCBhYm92ZSB1c2luZyB0aGUgKipzbGljZV9tYXgqKiBkcGx5cidzIGZ1bmN0aW9uDQogICAgICAgICogYWRhcHQgdGhlIGZvbGxvd2luZyB0ZW1wbGF0ZTogKipzbGljZV9tYXgoQ09MVU1OLCBuPTEpKioNCmBgYHtyfQ0KbXRjYXJzICU+JSANCiAgcm93bmFtZXNfdG9fY29sdW1uKCJjYXIiKSAlPiUgDQogIGdyb3VwX2J5KHZzLCBhbSkgJT4lIA0KICBzbGljZV9tYXgocXNlYykNCmBgYA0KDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgVGl0YW5pYyBkYXRhc2V0DQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQoNCiMjIyBQcmVwYXJlIHRoZSBkYXRhDQoNCiogTG9hZCB0aGUgdGl0YW5pYyBkYXRhc2V0IGludG8gdmFyaWFibGUgKip0aXRhbmljLnNvdXJjZSoqIGZyb20gdGhlIGZvbGxvd2luZyBVUkw6ICBgaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+amZvbnRhaW4vZmlsZXMvcl90dXRvX2dncGxvdC90aXRhbmljLnNvdXJjZWANCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQp0aXRhbmljLnNvdXJjZSA8LSByZWFkX3RzdigiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+amZvbnRhaW4vZmlsZXMvcl90dXRvX2dncGxvdC90aXRhbmljLnRzdiIpICANCmBgYA0KDQoqIG9ic2VydmUgdGhlIDIwIGZpcnN0IHJvd3MgdXNpbmcgKipoZWFkKCkqKiBmdW5jdGlvbiAoc2VlIGhlbHAgcGFnZSB0byBzZWUgaG93IA0KdG8gZGVmaW5lIG51bWJlciBvZiByb3dzKS4gV291bGQgaXQgYmUgcG9zc2libGUgYWZ0ZXIgc29tZSBwcm9jZXNzaW5nIHRvIGRlcml2ZSANCmEgbnVtZXJpY2FsIGFnZSB2YWx1ZSBmb3IgZWFjaCByb3c/DQpgYGB7cn0NCnRpdGFuaWMuc291cmNlICU+JSBoZWFkKG49MjApDQpgYGANCg0KKiBjcmVhdGUgdmFyaWFibGUgKip0aXRhbmljKiogd2l0aCBhIGNvcHkgb2YgKip0aXRhbmljLnNvdXJjZSoqDQogICAgKiBmaWx0ZXIgdGhlIHRhYmxlIHRvIGtlZXAgcm93cyB0aGF0IGNvbnRhaW5zIGEgcG9zc2libGUgdmFsdWUgZm9yIGFnZQ0KICAgICogdGlkeSB1cCB0aGUgdGFibGUgaW4gb3JkZXIgdG8gaGF2ZSBhIG51bWVyaWNhbCBjb2x1bW4gZm9yICoqQWdlKioNCiAgICAqIHJlbW92ZSBhbnkgdGVtcG9yYXJ5IGNvbHVtbiBjcmVhdGVkIGR1cmluZyB0aGlzIHRhc2ssIGlmIHJlbGV2YW50DQoqIHNob3cgdGhlIGhlYWQgb2YgdGhlIG5ldyB0YWJsZQ0KDQpgYGB7cn0NCnRpdGFuaWMgPC0gdGl0YW5pYy5zb3VyY2UgJT4lIA0KICBmaWx0ZXIoQWdlIT0iTm90IEF2YWlsYWJsZSIpICU+JSANCiAgc2VwYXJhdGUoQWdlLCBpbnRvID0gYygiQWdlIiwgInJlc3QiKSwgc2VwPSIsICIpICU+JSANCiAgbXV0YXRlKEFnZT1hcy5udW1lcmljKEFnZSkpICU+JSANCiAgc2VsZWN0KC1yZXN0KSANCnRpdGFuaWMgJT4lIGhlYWQoKQ0KYGBgDQoNCg0KIyMjIFN1cnZpdmFsIHN0YXR1cw0KDQoqIERpc3BsYXkgYSBiYXIgcGxvdCB3aXRoIG51bWJlcnMgb2Ygc3Vydml2ZWQgYW5kIGRpZWQgcGFzc2VuZ2VycyBpbiBlYWNoIGNsYXNzDQogICAgKiB1c2UgKip0aXRhbmljKiogdGFibGUNCiAgICAqIHVzZSAqKmFlcygpKiogd2l0aCBwYXJhbWV0ZXIgKip4KiogYW5kICoqZmlsbCoqDQoqIFR1bmluZyBwbG90cyBjYW4gZ2V0IGNvbXBsaWNhdGVkOiBhZGQgdGhlIGZvbGxvd2luZyBnZ3Bsb3QgbGF5ZXIgYWZ0ZXIgKipnZW9tX2JhcigpKiogdG8gc2hvdyBudW1iZXJzIGluIGJhcnM6DQogICAgKiBgc3RhdF9jb3VudChnZW9tID0gInRleHQiLCBjb2xvdXIgPSAid2hpdGUiLCBhZXMobGFiZWwgPSAuLmNvdW50Li4pLCBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpKWANCg0KYGBge3J9DQp0aXRhbmljICU+JSANCiAgZ2dwbG90KGFlcyh4PUNsYXNzLCBmaWxsPVN1cnZpdmFsU3RhdHVzKSkgKw0KICBnZW9tX2JhcigpICsNCiAgc3RhdF9jb3VudChnZW9tID0gInRleHQiLCBjb2xvdXIgPSAid2hpdGUiLCBhZXMobGFiZWwgPSAuLmNvdW50Li4pLCBwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpKQ0KYGBgDQoNCiogSW4gYSBuZXcgdGFibGUgKip0aXRhbmljLnN0YXRzKiosIGNhbGN1bGF0ZSBudW1iZXJzIG9mIHN1cnZpdmVkIGFuZCBkaWVkIHBhc3NlbmdlcnMgaW4gZWFjaCBjbGFzcyANCiAgICAqIHVzZSAqKnRpdGFuaWMqKiB0YWJsZQ0KICAgICogaGludDogdXNlICoqc3VtbWFyaXNlKG49bigpKSoqIHdoZXJlIGZ1bmN0aW9uICoqbigpKiogY291bnRzIG51bWJlciBvZiByb3dzDQoNCmBgYHtyfQ0KdGl0YW5pYy5zdGF0cyA8LSB0aXRhbmljICU+JQ0KICBncm91cF9ieShDbGFzcywgU3Vydml2YWxTdGF0dXMpICU+JSANCiAgc3VtbWFyaXNlKG49bigpKQ0KdGl0YW5pYy5zdGF0cw0KYGBgDQoNCiogdGlkeSB0aGUgKip0aXRhbmljLnN0YXRzKiogdGFibGUgdG8gbWFrZSBhIGNsYXNzIGFuIG9ic2VydmF0aW9uLCByZXN1bHRpbmcgaW4gYSBuZXcgdGFibGUgd2l0aCAzIGNvbHVtbnMgKENsYXNzLCBkaWVkLCBzdXJ2aXZlZCkNCmBgYHtyfQ0KdGl0YW5pYy5zdGF0cyA8LSB0aXRhbmljLnN0YXRzICU+JSANCiAgc3ByZWFkKFN1cnZpdmFsU3RhdHVzLCBuKQ0KdGl0YW5pYy5zdGF0cw0KYGBgDQoNCiogVXNpbmcgdGhlICoqdGl0YW5pYy5zdGF0cyoqIHRhYmxlLCBjYWxjdWxhdGUgdGhlIGZyZXF1ZW5jeSBmb3IgbWFsZSBvciBmZW1hbGUgcGFzc2VuZ2VycyB0byBkaWUgaW4gZWFjaCBjbGFzcw0KDQpgYGB7cn0NCnRpdGFuaWMuc3RhdHMgJT4lIA0KICBtdXRhdGUoZnJlcT1kaWVkLyhkaWVkK3N1cnZpdmVkKSkNCmBgYA0KDQoNCiMjIyBEaXN0cmlidXRpb24gb2YgYWdlDQoNCiogQ2FsY3VsYXRlIHRoZSBtZWFuIGFnZSBpbiBlYWNoIGNsYXNzIGZvciBtYWxlIGFuZCBmZW1hbGUgcGFzc2VuZ2VycyB1c2luZyAqKnN1bW1hcmlzZSgpKioNCiAgICAqIHVzZSB0aXRhbmljIHRhYmxlDQogICAgKiBOb3RlIHRoYXQgdGhlIHNldHRpbmcgKipuYS5ybT1UUlVFKiogZm9yIHRoZSAqKm1lYW4oKSoqIGZ1bmN0aW9uIHByZXZlbnRzIHRoZSBjYWxjdWxhdGlvbiB0byBmYWlsIGluIGNhc2VzIG9mIG1pc3NpbmcgdmFsdWVzLiBBcyB3ZSBmaWx0ZXJlZCBvdXQgbWlzc2luZyB2YWx1ZXMgZWFybGllciBpdCBzaG91bGQgbm90IGJlIG5lY2Vzc2FyeS4gQWxzbyBhcHBsaWVzIHRvICoqc3VtLCBtaW4gYW5kIG1heCoqIGZ1bmN0aW9ucy4gIA0KYGBge3J9DQp0aXRhbmljICU+JSANCiAgZ3JvdXBfYnkoQ2xhc3MsIFNleCkgJT4lIA0KICBzdW1tYXJpc2UoYXZnPW1lYW4oQWdlLCBuYS5ybT1UUlVFKSkNCmBgYA0KDQoqIHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhZ2UgaW4gZWFjaCBjbGFzcyBmb3IgbWFsZSBhbmQgZmVtYWxlIHBhc3NlbmdlcnMgdXNpbmcgYm94cGxvdHMNCiAgICAqIHVzZSBwYXJhbWV0ZXJzICoqeCoqLCAqKnkqKiBhbmQgKipmaWxsKiogaW4gKiphZXMoKSoqDQpgYGB7cn0NCnRpdGFuaWMgJT4lIA0KICBnZ3Bsb3QoYWVzKHk9QWdlLCB4PVNleCwgZmlsbD1DbGFzcykpICsNCiAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KKiBDcmVhdGUgc3VicGxvdHMgb2YgdGhlIHByZXZpb3VzIHBsb3QgYnkgc3Vydml2YWwgc3RhdHVzDQpgYGB7cn0NCnRpdGFuaWMgJT4lIA0KICBnZ3Bsb3QoYWVzKHk9QWdlLCB4PVNleCwgZmlsbD1DbGFzcykpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBmYWNldF93cmFwKHZhcnMoU3Vydml2YWxTdGF0dXMpKQ0KYGBgDQo=