1 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.

2 Preparation

Load the tidyverse package.

library(tidyverse)

3 Datasets

3.1 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
trees %>% head()
  • 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
trees2 %>% head()
  • 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 ggsave(trees2.plot, filename = ‘your_file.png’, …) with appropriate parameters for ggsave
    • read the help of the function to create a 10x10cm plot named “trees2.plot.png”
ggsave(trees2.plot, filename = "scatterplot.png", width = 10, height = 10, units = "cm")

3.2 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)
summary(PlantGrowth)
     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)

3.3 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
summary(CO2)
     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()

3.4 Built-in dataset: WorldPhones

The number of telephones in various regions of the world (in thousands).

  • show the matrix WorldPhones
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))

3.5 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
mtcars %>% head()
  • 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)

3.6 Titanic dataset

3.6.1 Prepare the data

  • Load the titanic dataset into variable titanic.source from the following URL: http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/titanic.tsv
titanic.source <- read_tsv("http://cbdm-01.zdv.uni-mainz.de/~stalbrec/RcourseData/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()

3.6.2 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.
titanic.stats
  • 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))

3.6.3 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+JSANCiAgc3VtbWFyaXNlKA0KICAgIG1lYW4uZGlhbWV0ZXI9bWVhbihEaWFtZXRlciksDQogICAgbWVhbi5oZWlnaHQ9bWVhbihIZWlnaHQpLA0KICAgIG1lYW4udm9sPW1lYW4oVm9sdW1lKQ0KICAgICkNCmBgYA0KDQoqIFNhdmUgaW4gdmFyaWFibGUgKip0cmVlczIucGxvdCoqIGEgc2NhdHRlciBwbG90IG9mIHRoZSBkaWFtZXRlciB2cyBoZWlnaHQgDQogICAgKiBjb2xvciBwb2ludHMgYnkgKipWb2x1bWUqKg0KICAgICogYWRkIGEgdGl0bGUgdG8gdGhlIHBsb3QgdXNpbmcgKipnZ3RpdGxlKCkqKiAgIA0KYGBge3J9DQp0cmVlczIucGxvdCA8LSB0cmVlczIgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9RGlhbWV0ZXIsIHk9SGVpZ2h0LCBjb2xvcj1Wb2x1bWUpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBnZ3RpdGxlKCJTY2F0dGVyIFBsb3QiKQ0KYGBgDQoNCg0KKiBzYXZlIHRoZSBwbG90IGluIGEgUE5HIGltYWdlIGZpbGUgb24geW91ciBjb21wdXRlcg0KICAgICogdXNlICoqZ2dzYXZlKHRyZWVzMi5wbG90LCBmaWxlbmFtZSA9ICd5b3VyX2ZpbGUucG5nJywgLi4uKSoqIHdpdGggYXBwcm9wcmlhdGUgcGFyYW1ldGVycyBmb3IgKipnZ3NhdmUqKg0KICAgICogcmVhZCB0aGUgaGVscCBvZiB0aGUgZnVuY3Rpb24gdG8gY3JlYXRlIGEgMTB4MTBjbSBwbG90IG5hbWVkICoqInRyZWVzMi5wbG90LnBuZyIqKg0KYGBge3J9DQpnZ3NhdmUodHJlZXMyLnBsb3QsIGZpbGVuYW1lID0gInNjYXR0ZXJwbG90LnBuZyIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEwLCB1bml0cyA9ICJjbSIpDQpgYGANCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgQnVpbHQtaW4gZGF0YXNldDogUGxhbnRHcm93dGgNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KUmVzdWx0cyBmcm9tIGFuIGV4cGVyaW1lbnQgdG8gY29tcGFyZSB5aWVsZHMgKGFzIG1lYXN1cmVkIGJ5IGRyaWVkIHdlaWdodCBvZiANCnBsYW50cykgb2J0YWluZWQgdW5kZXIgYSBjb250cm9sIGFuZCB0d28gZGlmZmVyZW50IHRyZWF0bWVudCBjb25kaXRpb25zLg0KDQoqIFNob3cgYSBzdW1tYXJ5IG9mIHRoZSB0YWJsZSB1c2luZyAqKnN1bW1hcnkoVEFCTEUpKiogKG5vdCBhIHRpZHl2ZXJzZSdzIGZ1bmN0aW9uKQ0KYGBge3J9DQpzdW1tYXJ5KFBsYW50R3Jvd3RoKQ0KYGBgDQoNCiogU2hvdyBhIGRlbnNpdHkgcGxvdCBvZiB0aGUgKip3ZWlnaHQqKiB2YWx1ZXMgZGl2aWRlZCBieSAqKmdyb3VwKiogaW4gYSBzaW5nbGUgcGxvdA0KYGBge3J9DQpQbGFudEdyb3d0aCAlPiUgDQogIGdncGxvdChhZXMoeD13ZWlnaHQsIGZpbGw9Z3JvdXApKSArDQogIGdlb21fZGVuc2l0eSgpDQpgYGANCiogVHVuaW5nIHRoZSBwbG90cyBpcyBzb21ldGltZXMgYXMgc2ltcGxlIGFzIHVzaW5nIGEgc3BlY2lhbCBwYXJhbWV0ZXIgdG8gYSBnZ3Bsb3QgbGF5ZXINCiAgICAqIHJlcGxvdCB0aGUgc2FtZSBwbG90IHdpdGggdGhlIGZvbGxvd2luZyBzZXR0aW5nIGluICoqZ2VvbV9kZW5zaXR5KCkqKiB0byBzZXQgdGhlIHRyYW5zcGFyZW5jeTogKiphbHBoYT0wLjIqKg0KICAgICogYWxwaGEgY2FuIHRha2UgdmFsdWVzIGZyb20gMCB0byAxLCB0ZXN0ICoqYWxwaGE9MC41KiogYW5kICoqYWxwaGE9MC44KioNCmBgYHtyfQ0KUGxhbnRHcm93dGggJT4lIA0KICBnZ3Bsb3QoYWVzKHg9d2VpZ2h0LCBmaWxsPWdyb3VwKSkgKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC4yKQ0KUGxhbnRHcm93dGggJT4lIA0KICBnZ3Bsb3QoYWVzKHg9d2VpZ2h0LCBmaWxsPWdyb3VwKSkgKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC41KQ0KUGxhbnRHcm93dGggJT4lIA0KICBnZ3Bsb3QoYWVzKHg9d2VpZ2h0LCBmaWxsPWdyb3VwKSkgKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9MC44KQ0KYGBgDQoNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCiMjIEJ1aWx0LWluIGRhdGFzZXQ6IENPMg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KDQpUaGUgQ08yIGRhdGEgZnJhbWUgaGFzIDg0IHJvd3MgYW5kIDUgY29sdW1ucyBvZiBkYXRhIGZyb20gYW4gZXhwZXJpbWVudCBvbiB0aGUgY29sZCB0b2xlcmFuY2Ugb2YgdGhlIGdyYXNzIHNwZWNpZXMgRWNoaW5vY2hsb2EgY3J1cy1nYWxsaS4NCg0KKiByZWFkIHRoZSBkb2N1bWVudGF0aW9uIG9mIHRoZSAqKkNPMioqIGRhdGFzZXQgdG8gdW5kZXJzdGFuZCB0aGUgY29sdW1ucw0KKiBzaG93IGEgc3VtbWFyeSBvZiB0aGUgdGFibGUNCmBgYHtyfQ0Kc3VtbWFyeShDTzIpDQpgYGANCg0KKiBDYWxjdWxhdGUgdGhlIG1pbmltdW0gYW5kIG1heGltdW0gdXB0YWtlIHBlciBnZW9ncmFwaGljYWwgcGxhY2Ugb2Ygb3JpZ2luDQpgYGB7cn0NCkNPMiAlPiUgDQogIGdyb3VwX2J5KFR5cGUpICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIG1pbj1taW4odXB0YWtlKSwNCiAgICBtYXg9bWF4KHVwdGFrZSksDQogICkNCmBgYA0KDQoqIENyZWF0ZSBhIGxpbmUgZ3JhcGggc2hvd2luZyB1cHRha2UgYnkgY29uY2VudHJhdGlvbiBmb3IgZWFjaCBwbGFudA0KYGBge3J9DQpDTzIgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9Y29uYywgeT11cHRha2UsIGNvbG9yPVBsYW50KSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCg0KPCEtLSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIC0tPg0KIyMgQnVpbHQtaW4gZGF0YXNldDogV29ybGRQaG9uZXMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KVGhlIG51bWJlciBvZiB0ZWxlcGhvbmVzIGluIHZhcmlvdXMgcmVnaW9ucyBvZiB0aGUgd29ybGQgKGluIHRob3VzYW5kcykuDQoNCiogc2hvdyB0aGUgbWF0cml4ICoqV29ybGRQaG9uZXMqKg0KYGBge3J9DQpXb3JsZFBob25lcw0KYGBgDQoNCiogQ29udmVydCB0aGUgbWF0cml4IGludG8gYSB0aWJibGUgbmFtZWQgKipwaG9uZXMqKiBhbmQgc2hvdyB0aGUgdGliYmxlDQogICAgKiBhZGFwdCB0aGUgZm9sbG93aW5nIHRlbXBsYXRlOiAqKmFzX3RpYmJsZShNQVRSSVgsIHJvd25hbWVzPSJ5ZWFyIikqKg0KICAgICogUGFyYW1ldGVyICoqcm93bmFtZXMqKiBpcyBuZWVkZWQgYmVjYXVzZSBieSBkZWZhdWx0IHJvdyBuYW1lcyBhcmUgbm90IGtlcHQgYnkgKiphc190aWJibGUoKSoqDQoNCmBgYHtyfQ0KcGhvbmVzIDwtIGFzX3RpYmJsZShXb3JsZFBob25lcywgcm93bmFtZXM9InllYXIiKQ0KcGhvbmVzDQpgYGANCg0KKiBUaWR5IHVwIHRoZSB0aWJibGUgaW4gb3JkZXIgdG8gbWFrZSBhbiBvYnNlcnZhdGlvbiBhIGdlb2dyYXBoaWNhbCBhcmVhIGluIGEgeWVhciANCmBgYHtyfQ0KcGhvbmVzIDwtIHBob25lcyAlPiUgDQogIGdhdGhlcihOLkFtZXI6TWlkLkFtZXIsIGtleT0iYXJlYSIsIHZhbHVlPSJwaG9uZXMiKQ0KcGhvbmVzDQpgYGANCg0KKiBDcmVhdGUgYSBwbG90IHRvIHNob3cgdGhlIG51bWJlciBvZiBwaG9uZXMgYnkgeWVhciBpbiBlYWNoIGdlb2dyYXBoaWNhbCBhcmVhDQogICAgKiB1c2UgZmFjZXRzIGFuZCBjb2xvcnMgZm9yIHRoZSBhcmVhcyANCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQ0KcGhvbmVzICU+JSANCiAgZ2dwbG90KGFlcyh4PXllYXIsIHk9cGhvbmVzLCBjb2xvcj1hcmVhKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBmYWNldF93cmFwKHZhcnMoYXJlYSkpDQpgYGANCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBCdWlsdC1pbiBkYXRhc2V0OiBtdGNhcnMNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KVGhlIGRhdGEgd2FzIGV4dHJhY3RlZCBmcm9tIHRoZSAxOTc0IE1vdG9yIFRyZW5kIFVTIG1hZ2F6aW5lLCBhbmQgY29tcHJpc2VzIGZ1ZWwgY29uc3VtcHRpb24gYW5kIDEwIGFzcGVjdHMgb2YgYXV0b21vYmlsZSBkZXNpZ24gYW5kIHBlcmZvcm1hbmNlIGZvciAzMiBhdXRvbW9iaWxlcyAoMTk3M+KAkzc0IG1vZGVscykuDQoNCiogcmVhZCB0aGUgcmVsYXRlZCBoZWxwIHBhZ2UgdG8gdW5kZXJzdGFuZCB0aGUgY29sdW1ucw0KKiBzaG93IHRoZSBoZWFkIG9mIHRoZSBkYXRhIGZyYW1lDQpgYGB7cn0NCm10Y2FycyAlPiUgaGVhZCgpDQpgYGANCg0KKiBUbyBjb21wYXJlIGVuZ2luZSB0eXBlcyAodnMpLCBjYWxjdWxhdGUgbWVhbiBncm9zcyBob3JzZXBvd2VyIGFuZCBtZWFuIHRpbWUgcGVyIDEvNCBtaWxlDQpgYGB7cn0NCm10Y2FycyAlPiUNCiAgZ3JvdXBfYnkodnMpICU+JSANCiAgc3VtbWFyaXNlKG1lYW4ucG93ZXI9bWVhbihocCksIG1lYW4udGltZT1tZWFuKHFzZWMpKQ0KYGBgDQoNCg0KKiBjcmVhdGUgYSBib3hwbG90IHRvIGNvbXBhcmUgdGhlIHdlaWdodCBwZXIgZW5naW5lIHR5cGUgKDEgcGxvdCB3aXRoIDIgYm94ZXMpDQogICAgKiBIaW50OiB0aGUgeCBheGlzIG9mIGEgYm94cGxvdCBzaG91bGQgbm90IGJlIGEgbnVtZXJpYyBjb2x1bW4NCg0KYGBge3J9DQptdGNhcnMgJT4lIA0KICBtdXRhdGUodnM9YXMuY2hhcmFjdGVyKHZzKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9dnMsIHk9d3QpKSArDQogIGdlb21fYm94cGxvdCgpDQpgYGANCiogZmFzdGVzdCBjYXIgcGVyIGNhdGVnb3J5DQogICAgKiBhIGNhdGVnb3J5IGlzIGRlZmluZWQgYnkgYSBwYXJ0aWN1bGFyIGNvbWJpbmF0aW9uIG9mIGVuZ2luZSAodnMpIGFuZCB0cmFuc21pc3Npb24gKGFtKQ0KICAgICogY2FsY3VsYXRlIHRoZSBmYXN0ZXN0IGNhciBpbiBlYWNoIGNhdGVnb3J5IA0KICAgICAgICAqIHRoZSBkYXRhIGZyYW1lJ3Mgcm93bmFtZXMgbXVzdCBiZSBmaXJzdCB0cmFuc2Zvcm1lZCBhcyBhIG5vcm1hbCBjb2x1bW4gdXNpbmcgKipyb3duYW1lc190b19jb2x1bW4oImNhciIpKioNCiAgICAgICAgDQpgYGB7cn0NCm10Y2FycyAlPiUgDQogIHJvd25hbWVzX3RvX2NvbHVtbigiY2FyIikgJT4lIA0KICBncm91cF9ieSh2cywgYW0pICU+JSANCiAgZmlsdGVyKHFzZWM9PW1heChxc2VjKSkNCmBgYA0KKiBmYXN0ZXN0IGNhciBwZXIgY2F0ZWdvcnkNCiAgICAqIHJlcHJvZHVjZSB0aGUgcmVzdWx0IGFib3ZlIHVzaW5nIHRoZSAqKnNsaWNlX21heCoqIGRwbHlyJ3MgZnVuY3Rpb24NCiAgICAgICAgKiBhZGFwdCB0aGUgZm9sbG93aW5nIHRlbXBsYXRlOiAqKnNsaWNlX21heChDT0xVTU4sIG49MSkqKg0KYGBge3J9DQptdGNhcnMgJT4lIA0KICByb3duYW1lc190b19jb2x1bW4oImNhciIpICU+JSANCiAgZ3JvdXBfYnkodnMsIGFtKSAlPiUgDQogIHNsaWNlX21heChxc2VjKQ0KYGBgDQoNCg0KDQo8IS0tICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgLS0+DQojIyBUaXRhbmljIGRhdGFzZXQNCjwhLS0gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyAtLT4NCg0KIyMjIFByZXBhcmUgdGhlIGRhdGENCg0KKiBMb2FkIHRoZSB0aXRhbmljIGRhdGFzZXQgaW50byB2YXJpYWJsZSAqKnRpdGFuaWMuc291cmNlKiogZnJvbSB0aGUgZm9sbG93aW5nIFVSTDogIGBodHRwOi8vY2JkbS0wMS56ZHYudW5pLW1haW56LmRlL35zdGFsYnJlYy9SY291cnNlRGF0YS90aXRhbmljLnRzdmANCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQp0aXRhbmljLnNvdXJjZSA8LSByZWFkX3RzdigiaHR0cDovL2NiZG0tMDEuemR2LnVuaS1tYWluei5kZS9+c3RhbGJyZWMvUmNvdXJzZURhdGEvdGl0YW5pYy50c3YiKSAgDQpgYGANCg0KKiBvYnNlcnZlIHRoZSAyMCBmaXJzdCByb3dzIHVzaW5nICoqaGVhZCgpKiogZnVuY3Rpb24gKHNlZSBoZWxwIHBhZ2UgdG8gc2VlIGhvdyANCnRvIGRlZmluZSBudW1iZXIgb2Ygcm93cykuIFdvdWxkIGl0IGJlIHBvc3NpYmxlIGFmdGVyIHNvbWUgcHJvY2Vzc2luZyB0byBkZXJpdmUgDQphIG51bWVyaWNhbCBhZ2UgdmFsdWUgZm9yIGVhY2ggcm93Pw0KYGBge3J9DQp0aXRhbmljLnNvdXJjZSAlPiUgaGVhZChuPTIwKQ0KYGBgDQoNCiogY3JlYXRlIHZhcmlhYmxlICoqdGl0YW5pYyoqIHdpdGggYSBjb3B5IG9mICoqdGl0YW5pYy5zb3VyY2UqKg0KICAgICogZmlsdGVyIHRoZSB0YWJsZSB0byBrZWVwIHJvd3MgdGhhdCBjb250YWlucyBhIHBvc3NpYmxlIHZhbHVlIGZvciBhZ2UNCiAgICAqIHRpZHkgdXAgdGhlIHRhYmxlIGluIG9yZGVyIHRvIGhhdmUgYSBudW1lcmljYWwgY29sdW1uIGZvciAqKkFnZSoqDQogICAgKiByZW1vdmUgYW55IHRlbXBvcmFyeSBjb2x1bW4gY3JlYXRlZCBkdXJpbmcgdGhpcyB0YXNrLCBpZiByZWxldmFudA0KKiBzaG93IHRoZSBoZWFkIG9mIHRoZSBuZXcgdGFibGUNCg0KYGBge3J9DQp0aXRhbmljIDwtIHRpdGFuaWMuc291cmNlICU+JSANCiAgZmlsdGVyKEFnZSE9Ik5vdCBBdmFpbGFibGUiKSAlPiUgDQogIHNlcGFyYXRlKEFnZSwgaW50byA9IGMoIkFnZSIsICJyZXN0IiksIHNlcD0iLCAiKSAlPiUgDQogIG11dGF0ZShBZ2U9YXMubnVtZXJpYyhBZ2UpKSAlPiUgDQogIHNlbGVjdCgtcmVzdCkgDQp0aXRhbmljICU+JSBoZWFkKCkNCmBgYA0KDQoNCiMjIyBTdXJ2aXZhbCBzdGF0dXMNCg0KKiBEaXNwbGF5IGEgYmFyIHBsb3Qgd2l0aCBudW1iZXJzIG9mIHN1cnZpdmVkIGFuZCBkaWVkIHBhc3NlbmdlcnMgaW4gZWFjaCBjbGFzcw0KICAgICogdXNlICoqdGl0YW5pYyoqIHRhYmxlDQogICAgKiB1c2UgKiphZXMoKSoqIHdpdGggcGFyYW1ldGVyICoqeCoqIGFuZCAqKmZpbGwqKg0KKiBUdW5pbmcgcGxvdHMgY2FuIGdldCBjb21wbGljYXRlZDogYWRkIHRoZSBmb2xsb3dpbmcgZ2dwbG90IGxheWVyIGFmdGVyICoqZ2VvbV9iYXIoKSoqIHRvIHNob3cgbnVtYmVycyBpbiBiYXJzOg0KICAgICogYHN0YXRfY291bnQoZ2VvbSA9ICJ0ZXh0IiwgY29sb3VyID0gIndoaXRlIiwgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgcG9zaXRpb249cG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSlgDQoNCmBgYHtyfQ0KdGl0YW5pYyAlPiUgDQogIGdncGxvdChhZXMoeD1DbGFzcywgZmlsbD1TdXJ2aXZhbFN0YXR1cykpICsNCiAgZ2VvbV9iYXIoKSArDQogIHN0YXRfY291bnQoZ2VvbSA9ICJ0ZXh0IiwgY29sb3VyID0gIndoaXRlIiwgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgcG9zaXRpb249cG9zaXRpb25fc3RhY2sodmp1c3Q9MC41KSkNCmBgYA0KDQoqIEluIGEgbmV3IHRhYmxlICoqdGl0YW5pYy5zdGF0cyoqLCBjYWxjdWxhdGUgbnVtYmVycyBvZiBzdXJ2aXZlZCBhbmQgZGllZCBwYXNzZW5nZXJzIGluIGVhY2ggY2xhc3MgDQogICAgKiB1c2UgKip0aXRhbmljKiogdGFibGUNCiAgICAqIGhpbnQ6IHVzZSAqKnN1bW1hcmlzZShuPW4oKSkqKiB3aGVyZSBmdW5jdGlvbiAqKm4oKSoqIGNvdW50cyBudW1iZXIgb2Ygcm93cw0KDQpgYGB7cn0NCnRpdGFuaWMuc3RhdHMgPC0gdGl0YW5pYyAlPiUNCiAgZ3JvdXBfYnkoQ2xhc3MsIFN1cnZpdmFsU3RhdHVzKSAlPiUgDQogIHN1bW1hcmlzZShuPW4oKSkNCnRpdGFuaWMuc3RhdHMNCmBgYA0KDQoqIHRpZHkgdGhlICoqdGl0YW5pYy5zdGF0cyoqIHRhYmxlIHRvIG1ha2UgYSBjbGFzcyBhbiBvYnNlcnZhdGlvbiwgcmVzdWx0aW5nIGluIGEgbmV3IHRhYmxlIHdpdGggMyBjb2x1bW5zIChDbGFzcywgZGllZCwgc3Vydml2ZWQpDQpgYGB7cn0NCnRpdGFuaWMuc3RhdHMgPC0gdGl0YW5pYy5zdGF0cyAlPiUgDQogIHNwcmVhZChTdXJ2aXZhbFN0YXR1cywgbikNCnRpdGFuaWMuc3RhdHMNCmBgYA0KDQoqIFVzaW5nIHRoZSAqKnRpdGFuaWMuc3RhdHMqKiB0YWJsZSwgY2FsY3VsYXRlIHRoZSBmcmVxdWVuY3kgZm9yIG1hbGUgb3IgZmVtYWxlIHBhc3NlbmdlcnMgdG8gZGllIGluIGVhY2ggY2xhc3MNCg0KYGBge3J9DQp0aXRhbmljLnN0YXRzICU+JSANCiAgbXV0YXRlKGZyZXE9ZGllZC8oZGllZCtzdXJ2aXZlZCkpDQpgYGANCg0KDQojIyMgRGlzdHJpYnV0aW9uIG9mIGFnZQ0KDQoqIENhbGN1bGF0ZSB0aGUgbWVhbiBhZ2UgaW4gZWFjaCBjbGFzcyBmb3IgbWFsZSBhbmQgZmVtYWxlIHBhc3NlbmdlcnMgdXNpbmcgKipzdW1tYXJpc2UoKSoqDQogICAgKiB1c2UgdGl0YW5pYyB0YWJsZQ0KICAgICogTm90ZSB0aGF0IHRoZSBzZXR0aW5nICoqbmEucm09VFJVRSoqIGZvciB0aGUgKiptZWFuKCkqKiBmdW5jdGlvbiBwcmV2ZW50cyB0aGUgY2FsY3VsYXRpb24gdG8gZmFpbCBpbiBjYXNlcyBvZiBtaXNzaW5nIHZhbHVlcy4gQXMgd2UgZmlsdGVyZWQgb3V0IG1pc3NpbmcgdmFsdWVzIGVhcmxpZXIgaXQgc2hvdWxkIG5vdCBiZSBuZWNlc3NhcnkuIEFsc28gYXBwbGllcyB0byAqKnN1bSwgbWluIGFuZCBtYXgqKiBmdW5jdGlvbnMuICANCmBgYHtyfQ0KdGl0YW5pYyAlPiUgDQogIGdyb3VwX2J5KENsYXNzLCBTZXgpICU+JSANCiAgc3VtbWFyaXNlKGF2Zz1tZWFuKEFnZSwgbmEucm09VFJVRSkpDQpgYGANCg0KKiBwbG90IHRoZSBkaXN0cmlidXRpb24gb2YgYWdlIGluIGVhY2ggY2xhc3MgZm9yIG1hbGUgYW5kIGZlbWFsZSBwYXNzZW5nZXJzIHVzaW5nIGJveHBsb3RzDQogICAgKiB1c2UgcGFyYW1ldGVycyAqKngqKiwgKip5KiogYW5kICoqZmlsbCoqIGluICoqYWVzKCkqKg0KYGBge3J9DQp0aXRhbmljICU+JSANCiAgZ2dwbG90KGFlcyh5PUFnZSwgeD1TZXgsIGZpbGw9Q2xhc3MpKSArDQogIGdlb21fYm94cGxvdCgpDQpgYGANCiogQ3JlYXRlIHN1YnBsb3RzIG9mIHRoZSBwcmV2aW91cyBwbG90IGJ5IHN1cnZpdmFsIHN0YXR1cw0KYGBge3J9DQp0aXRhbmljICU+JSANCiAgZ2dwbG90KGFlcyh5PUFnZSwgeD1TZXgsIGZpbGw9Q2xhc3MpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgZmFjZXRfd3JhcCh2YXJzKFN1cnZpdmFsU3RhdHVzKSkNCmBgYA0K