1 Installing Packages

1.1 Install data.table

Before we start work with tables, let’s install the data.table package which we shall use heavily:

# Installs data.table
# install.packages("data.table")
# Loads the package
library(data.table)

1.2 Installing dplyr

Before we start manipulating data let’s install the dpylr package:

# Installs dplyr
# install.packages("dplyr")
# Loads the package
library(dplyr)

1.3 Installing ggplot2

We will use this package for data visualisation:

# Installs ggplot2
# install.packages("ggplot2")
# Loads the package
library(ggplot2)

2 Import/Export Data

2.1 Read Tables

Within RStudio in the top right corner one can import a dataset from the Enivronment Pane. Alternatively, one can navigate to File > Import Dataset. Aside from using RStudio’s native UI there are built-in functions and packages that enable you to import datasets into R. Let’s try importing the data present in a particular path and make use of the View(...) function of RStudio:

# Import CSV (relative path) into a dataframe
titanic_df <- read.csv("../Data/train.csv")
# Converts it to a data table
titanic_df <- data.table(titanic_df)
# Check the data
titanic_df

If you recieved an error it’s probably because you didn’t download the github repository. There are two causes for the error: - you probably didn’t replicate the folder structure that I have - or, you didn’t set a working directory The following code will help you resolve the error:

# Importing data using the absolute path
# titanic_df <- read.csv("your-path-here/train.csv")
# Setting a working directory
# setwd("your-path-here/BeginR/Code")
# View the data
# View(titanic_df)

2.2 Write tables

Let’s write a table to our laptops now:

# Create a table
df <- data.frame(names = c("X","Y","Z"),
                 gender = c("Male","Female","Female"),
                 score = c(67, 99, 85))
# Writing to a table without the rownames
write.csv(x = df, file = "../Code/something.csv", row.names = FALSE)

3 Understanding the Data

3.1 Structure of data

Let’s explore the structure of the data:

# Structure of the data
str(titanic_df)
Classes ‘data.table’ and 'data.frame':  891 obs. of  12 variables:
 $ PassengerId: int  1 2 3 4 5 6 7 8 9 10 ...
 $ Survived   : int  0 1 1 1 0 0 0 0 1 1 ...
 $ Pclass     : int  3 1 3 1 3 3 1 3 3 2 ...
 $ Name       : Factor w/ 891 levels "Abbing, Mr. Anthony",..: 109 191 358 277 16 559 520 629 417 581 ...
 $ Sex        : Factor w/ 2 levels "female","male": 2 1 1 1 2 2 2 2 1 1 ...
 $ Age        : num  22 38 26 35 35 NA 54 2 27 14 ...
 $ SibSp      : int  1 1 0 1 0 0 0 3 0 1 ...
 $ Parch      : int  0 0 0 0 0 0 0 1 2 0 ...
 $ Ticket     : Factor w/ 681 levels "110152","110413",..: 524 597 670 50 473 276 86 396 345 133 ...
 $ Fare       : num  7.25 71.28 7.92 53.1 8.05 ...
 $ Cabin      : Factor w/ 148 levels "","A10","A14",..: 1 83 1 57 1 1 131 1 1 1 ...
 $ Embarked   : Factor w/ 4 levels "","C","Q","S": 4 2 4 4 4 3 4 4 4 2 ...
 - attr(*, ".internal.selfref")=<externalptr> 

Most variable names are not illuminating. So let’s understand the data description from the source:

Variable Name Description
Survived Survived (1) or died (0)
Pclass Passenger’s class
Name Passenger’s name
Sex Passenger’s sex
Age Passenger’s age
SibSp Number of siblings/spouses aboard
Parch Number of parents/children aboard
Ticket Ticket number
Fare Fare
Cabin Cabin
Embarked Port of embarkation

3.2 Summary statistics of data

# Displays the summary of all variables in the dataframe
summary(titanic_df)
  PassengerId       Survived          Pclass     
 Min.   :  1.0   Min.   :0.0000   Min.   :1.000  
 1st Qu.:223.5   1st Qu.:0.0000   1st Qu.:2.000  
 Median :446.0   Median :0.0000   Median :3.000  
 Mean   :446.0   Mean   :0.3838   Mean   :2.309  
 3rd Qu.:668.5   3rd Qu.:1.0000   3rd Qu.:3.000  
 Max.   :891.0   Max.   :1.0000   Max.   :3.000  
                                                 
                                    Name         Sex           Age       
 Abbing, Mr. Anthony                  :  1   female:314   Min.   : 0.42  
 Abbott, Mr. Rossmore Edward          :  1   male  :577   1st Qu.:20.12  
 Abbott, Mrs. Stanton (Rosa Hunt)     :  1                Median :28.00  
 Abelson, Mr. Samuel                  :  1                Mean   :29.70  
 Abelson, Mrs. Samuel (Hannah Wizosky):  1                3rd Qu.:38.00  
 Adahl, Mr. Mauritz Nils Martin       :  1                Max.   :80.00  
 (Other)                              :885                NA's   :177    
     SibSp           Parch             Ticket         Fare                Cabin    
 Min.   :0.000   Min.   :0.0000   1601    :  7   Min.   :  0.00              :687  
 1st Qu.:0.000   1st Qu.:0.0000   347082  :  7   1st Qu.:  7.91   B96 B98    :  4  
 Median :0.000   Median :0.0000   CA. 2343:  7   Median : 14.45   C23 C25 C27:  4  
 Mean   :0.523   Mean   :0.3816   3101295 :  6   Mean   : 32.20   G6         :  4  
 3rd Qu.:1.000   3rd Qu.:0.0000   347088  :  6   3rd Qu.: 31.00   C22 C26    :  3  
 Max.   :8.000   Max.   :6.0000   CA 2144 :  6   Max.   :512.33   D          :  3  
                                  (Other) :852                    (Other)    :186  
 Embarked
  :  2   
 C:168   
 Q: 77   
 S:644   
         
         
         

4 Data Manipulation and Viz

4.1 Missing values

Note here that Age has 177 NA’s. Also, Embarked has 2 observations with "" as entries. This implies that there is missing data. In most cases, there will be some sort of imputation that happens for variables. First let’s learn to retrieve a subset of the data using the filter(...) function:

# Filter the "" in Embarked
filter(titanic_df, Embarked == "")

Let’s see if we can remove these two observations from our data:

# Filter the "" in Embarked
titanic_df <- filter(titanic_df, !Embarked == "")
titanic_df

The row count decreased by 2 so we have correctly removed the missing observations for Embarked. But in the case of Age we cannot simply remove 177 observations from our data. Similarly, there seem to be missing values in Cabin:

# Displays the first 5 rows
head(titanic_df)

How many missing values are there in both Age and Cabin combined?

filter(titanic_df, Cabin == "")

Wow, 687 rows without any information for Cabin, this means that the data in this column is extremely sparse. Well, let’s look at other insights our data can provide because imputing missing values is out of scope for this workshop.

4.2 Investigation I

Are there several family names and do the ticket fares differ for them?

Let’s create another variable called Surname using srtsplit(...) to split Name and apply that function on every row in the table using sapply(...):

# Splits every Name based on ',' or '.'
titanic_df$Surname <- sapply(titanic_df$Name, 
                             function(x) {
                                 strsplit(as.character(x), 
                                          split = '[,.]')[[1]][1]
                                 }
                             )
# Display the table
titanic_df

Let’s group the surnames using the group_by(...) function and then use the summarise(...) function to generate an understanding on classism and family names:

# Group the data based on surnames
grouped_surnames <- group_by(titanic_df, Surname)
# Create two columns MeanFare and Total count
summarise(grouped_surnames, MeanFare = mean(Fare), Total = n())

Well frankly, this investigation hasn’t revealed anything useful. More often than not, it’s important to ask the right questions. Let’s try something simpler.

4.3 Investigation II

Do families sink or swim together?

We’re going to make a family size variable based on number of siblings/spouse(s) (maybe someone has more than one spouse?) and number of children/parents.

# Create a family size variable including the passenger themselves
titanic_df <- mutate(titanic_df, Fsize = SibSp + Parch + 1)
# Display the result
titanic_df

What does our family size variable look like? To help us understand how it may relate to survival, let’s plot it using the geom_bar(...) function:

# Use ggplot2 to visualize the relationship between family size & survival
# geom_bar() plots a bar chart
ggplot(titanic_df, aes(x = Fsize, fill = factor(Survived))) + 
    geom_bar(position = 'dodge') + 
    scale_x_continuous(breaks = 1:11)

We can see that there’s a survival penalty to singletons and those with family sizes above 4.

4.4 Investigation III

Before we investigate another phenomenon let’s review what a box-whisker plot is.

Box-Whisker plot

Box-Whisker plot

Do the Fares of the passengers vary due to Passenger Class and Port of Embarkment?

Here I will also introduce the pipe %>% operator. Think of it as a machine that takes the result of the function to the left of it and passes it as an argument to the function on the right.

# Group the data on Embarked and Pclass
titanic_df %>%
    group_by(Embarked, Pclass) %>%
    summarise(meanF = mean(Fare), medianF = median(Fare), maxF = max(Fare), Count = n())

Well, visualizing numerical data is powerful tool in understanding the spread of the data and more importantly the outliers. Let’s plot these values using the geom_boxplot(...) function:

# geom_boxplot() plots a boxplot
ggplot(titanic_df, aes(x = Embarked, y = Fare, fill = factor(Pclass))) +
  geom_boxplot()

We can see there are some clear outliers here. Moreover, Q2 and Q3 seem to be the cheapest tickets.

4.5 Investigation IV

What factors could possibly influence survival rates ?

We examine the correlation (standardised covariance) which is calculated by the formula \(Cov(X,Y) = E(XY) - E(X)E(Y)\) for numerical vectors:

# Convert categorical variables to numerical, select those columns and calculate correlation
cor_df <- titanic_df %>%
    mutate(SexN = as.numeric(Sex), EmbarkedN = as.numeric(Embarked)) %>%
    select(Survived, EmbarkedN, Pclass, SexN, SibSp, Parch, Fsize, Fare) %>%
    cor()
# Display the data
cor_df
             Survived   EmbarkedN      Pclass       SexN       SibSp       Parch
Survived   1.00000000 -0.16971768 -0.33554886 -0.5415849 -0.03404000  0.08315078
EmbarkedN -0.16971768  1.00000000  0.16468071  0.1103200  0.06889991  0.04044863
Pclass    -0.33554886  0.16468071  1.00000000  0.1277409  0.08165562  0.01682449
SexN      -0.54158492  0.11031996  0.12774090  1.0000000 -0.11634817 -0.24750798
SibSp     -0.03404000  0.06889991  0.08165562 -0.1163482  1.00000000  0.41454164
Parch      0.08315078  0.04044863  0.01682449 -0.2475080  0.41454164  1.00000000
Fsize      0.01827747  0.06730499  0.06422053 -0.2031915  0.89065367  0.78298776
Fare       0.25529046 -0.22631118 -0.54819329 -0.1799575  0.16088685  0.21753204
                Fsize       Fare
Survived   0.01827747  0.2552905
EmbarkedN  0.06730499 -0.2263112
Pclass     0.06422053 -0.5481933
SexN      -0.20319145 -0.1799575
SibSp      0.89065367  0.1608869
Parch      0.78298776  0.2175320
Fsize      1.00000000  0.2186582
Fare       0.21865817  1.0000000

Next we create a melt(...) the correlation matrix into the long form:

# Melts the dataframe
output <- melt(cor_df)
# Display
output

Let’s visualise the correlation using the geom_tile(...) function:

# geom_tile() plots a correlation matrix
ggplot(output, aes(x=Var1, y=Var2, fill=value)) + 
    geom_tile()

Correlation is not Causation but:

  • There seems to be a high positice correlation between SibSp, Parch and Fsize. What is the reason for this ?
  • There is a negative correlation between SexN and Survival. It suggests that one sex was more likely to survive. But which one is it ? Can you infer from str(titanic_df)
  • What other things can you see ?

4.6 Investigation V

Does the Fare follow a normal distribution ?

Most random variables in nature follow a normal distribution. Let’s find out density plot of the Fare variable using the geom_density(...) function:

# geom_density() plots a density plot
ggplot(titanic_df, aes(x = Fare)) + geom_density()

Here’s a some code to get the density values which uses all the tables operations we learnt so far. Make sure to experiment with every line in the code below:

density_values <- titanic_df %>%                    
    group_by(Fare) %>%                               # Groups the data by Fare
    summarise(Frequency = n()) %>%                   # Generetes frequency of Fare
    mutate(Density = Frequency/sum(Frequency)) %>%   # Creates a column Density
    select(Fare, Density)                                  # Outputs the Density column only
# Display density_values
density_values
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyAoRURBKSBhbmQgVGFibGUgT3BlcmF0aW9ucyIKYXV0aG9yOiAiSWMzZnIwZyIKZGF0ZTogJ2ByIFN5cy5EYXRlKClgJwpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHllcwogICAgZmlnX2hlaWdodDogNC41CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHllcwogICAgZmlnX2hlaWdodDogNC41CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCi0tLQoKCiMgSW5zdGFsbGluZyBQYWNrYWdlcyB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIEluc3RhbGwgZGF0YS50YWJsZQpCZWZvcmUgd2Ugc3RhcnQgd29yayB3aXRoIHRhYmxlcywgbGV0J3MgaW5zdGFsbCB0aGUgYGRhdGEudGFibGVgIHBhY2thZ2Ugd2hpY2ggd2Ugc2hhbGwgdXNlIGhlYXZpbHk6CmBgYHtyfQojIEluc3RhbGxzIGRhdGEudGFibGUKIyBpbnN0YWxsLnBhY2thZ2VzKCJkYXRhLnRhYmxlIikKIyBMb2FkcyB0aGUgcGFja2FnZQpsaWJyYXJ5KGRhdGEudGFibGUpCmBgYAoKIyMgSW5zdGFsbGluZyBkcGx5cgpCZWZvcmUgd2Ugc3RhcnQgbWFuaXB1bGF0aW5nIGRhdGEgbGV0J3MgaW5zdGFsbCB0aGUgYGRweWxyYCBwYWNrYWdlOgpgYGB7cn0KIyBJbnN0YWxscyBkcGx5cgojIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKIyBMb2FkcyB0aGUgcGFja2FnZQpsaWJyYXJ5KGRwbHlyKQpgYGAKCiMjIEluc3RhbGxpbmcgZ2dwbG90MgpXZSB3aWxsIHVzZSB0aGlzIHBhY2thZ2UgZm9yIGRhdGEgdmlzdWFsaXNhdGlvbjoKYGBge3J9CiMgSW5zdGFsbHMgZ2dwbG90MgojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQojIExvYWRzIHRoZSBwYWNrYWdlCmxpYnJhcnkoZ2dwbG90MikKYGBgCgojIEltcG9ydC9FeHBvcnQgRGF0YSB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIFJlYWQgVGFibGVzCldpdGhpbiBSU3R1ZGlvIGluIHRoZSB0b3AgcmlnaHQgY29ybmVyIG9uZSBjYW4gaW1wb3J0IGEgZGF0YXNldCBmcm9tIHRoZSBFbml2cm9ubWVudCBQYW5lLiBBbHRlcm5hdGl2ZWx5LCBvbmUgY2FuIG5hdmlnYXRlIHRvIGBGaWxlID4gSW1wb3J0IERhdGFzZXRgLiBBc2lkZSBmcm9tIHVzaW5nIFJTdHVkaW8ncyBuYXRpdmUgVUkgdGhlcmUgYXJlIGJ1aWx0LWluIGZ1bmN0aW9ucyBhbmQgcGFja2FnZXMgdGhhdCBlbmFibGUgeW91IHRvIGltcG9ydCBkYXRhc2V0cyBpbnRvIFIuIExldCdzIHRyeSBpbXBvcnRpbmcgdGhlIGRhdGEgcHJlc2VudCBpbiBhIHBhcnRpY3VsYXIgcGF0aCBhbmQgbWFrZSB1c2Ugb2YgdGhlIGBWaWV3KC4uLilgIGZ1bmN0aW9uIG9mIFJTdHVkaW86IApgYGB7cn0KIyBJbXBvcnQgQ1NWIChyZWxhdGl2ZSBwYXRoKSBpbnRvIGEgZGF0YWZyYW1lCnRpdGFuaWNfZGYgPC0gcmVhZC5jc3YoIi4uL0RhdGEvdHJhaW4uY3N2IikKIyBDb252ZXJ0cyBpdCB0byBhIGRhdGEgdGFibGUKdGl0YW5pY19kZiA8LSBkYXRhLnRhYmxlKHRpdGFuaWNfZGYpCiMgQ2hlY2sgdGhlIGRhdGEKdGl0YW5pY19kZgpgYGAKSWYgeW91IHJlY2lldmVkIGFuIGVycm9yIGl0J3MgcHJvYmFibHkgYmVjYXVzZSB5b3UgZGlkbid0IGRvd25sb2FkIHRoZSBnaXRodWIgcmVwb3NpdG9yeS4gVGhlcmUgYXJlIHR3byBjYXVzZXMgZm9yIHRoZSBlcnJvcjoKLSB5b3UgcHJvYmFibHkgZGlkbid0IHJlcGxpY2F0ZSB0aGUgZm9sZGVyIHN0cnVjdHVyZSB0aGF0IEkgaGF2ZQotIG9yLCB5b3UgZGlkbid0IHNldCBhIHdvcmtpbmcgZGlyZWN0b3J5ClRoZSBmb2xsb3dpbmcgY29kZSB3aWxsIGhlbHAgeW91IHJlc29sdmUgdGhlIGVycm9yOgpgYGB7cn0KIyBJbXBvcnRpbmcgZGF0YSB1c2luZyB0aGUgYWJzb2x1dGUgcGF0aAojIHRpdGFuaWNfZGYgPC0gcmVhZC5jc3YoInlvdXItcGF0aC1oZXJlL3RyYWluLmNzdiIpCiMgU2V0dGluZyBhIHdvcmtpbmcgZGlyZWN0b3J5CiMgc2V0d2QoInlvdXItcGF0aC1oZXJlL0JlZ2luUi9Db2RlIikKIyBWaWV3IHRoZSBkYXRhCiMgVmlldyh0aXRhbmljX2RmKQpgYGAKCiMjIFdyaXRlIHRhYmxlcwpMZXQncyB3cml0ZSBhIHRhYmxlIHRvIG91ciBsYXB0b3BzIG5vdzoKYGBge3J9CiMgQ3JlYXRlIGEgdGFibGUKZGYgPC0gZGF0YS5mcmFtZShuYW1lcyA9IGMoIlgiLCJZIiwiWiIpLAogICAgICAgICAgICAgICAgIGdlbmRlciA9IGMoIk1hbGUiLCJGZW1hbGUiLCJGZW1hbGUiKSwKICAgICAgICAgICAgICAgICBzY29yZSA9IGMoNjcsIDk5LCA4NSkpCgojIFdyaXRpbmcgdG8gYSB0YWJsZSB3aXRob3V0IHRoZSByb3duYW1lcwp3cml0ZS5jc3YoeCA9IGRmLCBmaWxlID0gIi4uL0NvZGUvc29tZXRoaW5nLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCgojIFVuZGVyc3RhbmRpbmcgdGhlIERhdGEgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyBTdHJ1Y3R1cmUgb2YgZGF0YQpMZXQncyBleHBsb3JlIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGE6CmBgYHtyfQojIFN0cnVjdHVyZSBvZiB0aGUgZGF0YQpzdHIodGl0YW5pY19kZikKYGBgCk1vc3QgdmFyaWFibGUgbmFtZXMgYXJlIG5vdCBpbGx1bWluYXRpbmcuIFNvIGxldCdzIHVuZGVyc3RhbmQgdGhlIGRhdGEgZGVzY3JpcHRpb24gZnJvbSB0aGUgc291cmNlOgoKVmFyaWFibGUgTmFtZSB8IERlc2NyaXB0aW9uCi0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0KU3Vydml2ZWQgICAgICB8IFN1cnZpdmVkICgxKSBvciBkaWVkICgwKQpQY2xhc3MgICAgICAgIHwgUGFzc2VuZ2VyJ3MgY2xhc3MKTmFtZSAgICAgICAgICB8IFBhc3NlbmdlcidzIG5hbWUKU2V4ICAgICAgICAgICB8IFBhc3NlbmdlcidzIHNleApBZ2UgICAgICAgICAgIHwgUGFzc2VuZ2VyJ3MgYWdlClNpYlNwICAgICAgICAgfCBOdW1iZXIgb2Ygc2libGluZ3Mvc3BvdXNlcyBhYm9hcmQKUGFyY2ggICAgICAgICB8IE51bWJlciBvZiBwYXJlbnRzL2NoaWxkcmVuIGFib2FyZApUaWNrZXQgICAgICAgIHwgVGlja2V0IG51bWJlcgpGYXJlICAgICAgICAgIHwgRmFyZQpDYWJpbiAgICAgICAgIHwgQ2FiaW4KRW1iYXJrZWQgICAgICB8IFBvcnQgb2YgZW1iYXJrYXRpb24KCiMjIFN1bW1hcnkgc3RhdGlzdGljcyBvZiBkYXRhCmBgYHtyfQojIERpc3BsYXlzIHRoZSBzdW1tYXJ5IG9mIGFsbCB2YXJpYWJsZXMgaW4gdGhlIGRhdGFmcmFtZQpzdW1tYXJ5KHRpdGFuaWNfZGYpCmBgYAoKCiMgRGF0YSBNYW5pcHVsYXRpb24gYW5kIFZpeiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIE1pc3NpbmcgdmFsdWVzCk5vdGUgaGVyZSB0aGF0IGBBZ2VgIGhhcyAxNzcgYE5BYCdzLiBBbHNvLCBgRW1iYXJrZWRgIGhhcyAyIG9ic2VydmF0aW9ucyB3aXRoIGAiImAgYXMgZW50cmllcy4gVGhpcyBpbXBsaWVzIHRoYXQgdGhlcmUgaXMgbWlzc2luZyBkYXRhLiBJbiBtb3N0IGNhc2VzLCB0aGVyZSB3aWxsIGJlIHNvbWUgc29ydCBvZiBpbXB1dGF0aW9uIHRoYXQgaGFwcGVucyBmb3IgdmFyaWFibGVzLiBGaXJzdCBsZXQncyBsZWFybiB0byByZXRyaWV2ZSBhIHN1YnNldCBvZiB0aGUgZGF0YSB1c2luZyB0aGUgYGZpbHRlciguLi4pYCBmdW5jdGlvbjoKYGBge3J9CiMgRmlsdGVyIHRoZSAiIiBpbiBFbWJhcmtlZApmaWx0ZXIodGl0YW5pY19kZiwgRW1iYXJrZWQgPT0gIiIpCmBgYApMZXQncyBzZWUgaWYgd2UgY2FuIHJlbW92ZSB0aGVzZSB0d28gb2JzZXJ2YXRpb25zIGZyb20gb3VyIGRhdGE6CmBgYHtyfQojIEZpbHRlciB0aGUgIiIgaW4gRW1iYXJrZWQKdGl0YW5pY19kZiA8LSBmaWx0ZXIodGl0YW5pY19kZiwgIUVtYmFya2VkID09ICIiKQp0aXRhbmljX2RmCmBgYApUaGUgcm93IGNvdW50IGRlY3JlYXNlZCBieSAyIHNvIHdlIGhhdmUgY29ycmVjdGx5IHJlbW92ZWQgdGhlIG1pc3Npbmcgb2JzZXJ2YXRpb25zIGZvciBgRW1iYXJrZWRgLiBCdXQgaW4gdGhlIGNhc2Ugb2YgYEFnZWAgd2UgY2Fubm90IHNpbXBseSByZW1vdmUgMTc3IG9ic2VydmF0aW9ucyBmcm9tIG91ciBkYXRhLiBTaW1pbGFybHksIHRoZXJlIHNlZW0gdG8gYmUgbWlzc2luZyB2YWx1ZXMgaW4gYENhYmluYDoKYGBge3J9CiMgRGlzcGxheXMgdGhlIGZpcnN0IDUgcm93cwpoZWFkKHRpdGFuaWNfZGYpCmBgYApIb3cgbWFueSBtaXNzaW5nIHZhbHVlcyBhcmUgdGhlcmUgaW4gYm90aCBgQWdlYCBhbmQgYENhYmluYCBjb21iaW5lZD8KYGBge3J9CmZpbHRlcih0aXRhbmljX2RmLCBDYWJpbiA9PSAiIikKYGBgCldvdywgNjg3IHJvd3Mgd2l0aG91dCBhbnkgaW5mb3JtYXRpb24gZm9yIGBDYWJpbmAsIHRoaXMgbWVhbnMgdGhhdCB0aGUgZGF0YSBpbiB0aGlzIGNvbHVtbiBpcyBleHRyZW1lbHkgc3BhcnNlLgpXZWxsLCBsZXQncyBsb29rIGF0IG90aGVyIGluc2lnaHRzIG91ciBkYXRhIGNhbiBwcm92aWRlIGJlY2F1c2UgaW1wdXRpbmcgbWlzc2luZyB2YWx1ZXMgaXMgb3V0IG9mIHNjb3BlIGZvciB0aGlzIHdvcmtzaG9wLgoKIyMgSW52ZXN0aWdhdGlvbiBJCioqQXJlIHRoZXJlIHNldmVyYWwgZmFtaWx5IG5hbWVzIGFuZCBkbyB0aGUgdGlja2V0IGZhcmVzIGRpZmZlciBmb3IgdGhlbT8qKgoKTGV0J3MgY3JlYXRlIGFub3RoZXIgdmFyaWFibGUgY2FsbGVkIGBTdXJuYW1lYCB1c2luZyBgc3J0c3BsaXQoLi4uKWAgdG8gc3BsaXQgYE5hbWVgIGFuZCBhcHBseSB0aGF0IGZ1bmN0aW9uIG9uIGV2ZXJ5IHJvdyBpbiB0aGUgdGFibGUgdXNpbmcgYHNhcHBseSguLi4pYDoKYGBge3J9CiMgU3BsaXRzIGV2ZXJ5IE5hbWUgYmFzZWQgb24gJywnIG9yICcuJwp0aXRhbmljX2RmJFN1cm5hbWUgPC0gc2FwcGx5KHRpdGFuaWNfZGYkTmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoeCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdCA9ICdbLC5dJylbWzFdXVsxXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojIERpc3BsYXkgdGhlIHRhYmxlCnRpdGFuaWNfZGYKYGBgCkxldCdzIGdyb3VwIHRoZSBzdXJuYW1lcyB1c2luZyB0aGUgYGdyb3VwX2J5KC4uLilgIGZ1bmN0aW9uIGFuZCB0aGVuIHVzZSB0aGUgYHN1bW1hcmlzZSguLi4pYCBmdW5jdGlvbiB0byBnZW5lcmF0ZSBhbiB1bmRlcnN0YW5kaW5nIG9uIGNsYXNzaXNtIGFuZCBmYW1pbHkgbmFtZXM6CmBgYHtyfQojIEdyb3VwIHRoZSBkYXRhIGJhc2VkIG9uIHN1cm5hbWVzCmdyb3VwZWRfc3VybmFtZXMgPC0gZ3JvdXBfYnkodGl0YW5pY19kZiwgU3VybmFtZSkKIyBDcmVhdGUgdHdvIGNvbHVtbnMgTWVhbkZhcmUgYW5kIFRvdGFsIGNvdW50CnN1bW1hcmlzZShncm91cGVkX3N1cm5hbWVzLCBNZWFuRmFyZSA9IG1lYW4oRmFyZSksIFRvdGFsID0gbigpKQpgYGAKV2VsbCBmcmFua2x5LCB0aGlzIGludmVzdGlnYXRpb24gaGFzbid0IHJldmVhbGVkIGFueXRoaW5nIHVzZWZ1bC4gTW9yZSBvZnRlbiB0aGFuIG5vdCwgaXQncyBpbXBvcnRhbnQgdG8gYXNrIHRoZSByaWdodCBxdWVzdGlvbnMuIExldCdzIHRyeSBzb21ldGhpbmcgc2ltcGxlci4KCiMjIEludmVzdGlnYXRpb24gSUkKKipEbyBmYW1pbGllcyBzaW5rIG9yIHN3aW0gdG9nZXRoZXI/KioKCldlJ3JlIGdvaW5nIHRvIG1ha2UgYSBgZmFtaWx5IHNpemVgIHZhcmlhYmxlIGJhc2VkIG9uIG51bWJlciBvZiBzaWJsaW5ncy9zcG91c2UocykgKG1heWJlIHNvbWVvbmUgaGFzIG1vcmUgdGhhbiBvbmUgc3BvdXNlPykgYW5kIG51bWJlciBvZiBjaGlsZHJlbi9wYXJlbnRzLgpgYGB7cn0KIyBDcmVhdGUgYSBmYW1pbHkgc2l6ZSB2YXJpYWJsZSBpbmNsdWRpbmcgdGhlIHBhc3NlbmdlciB0aGVtc2VsdmVzCnRpdGFuaWNfZGYgPC0gbXV0YXRlKHRpdGFuaWNfZGYsIEZzaXplID0gU2liU3AgKyBQYXJjaCArIDEpCiMgRGlzcGxheSB0aGUgcmVzdWx0CnRpdGFuaWNfZGYKYGBgCldoYXQgZG9lcyBvdXIgZmFtaWx5IHNpemUgdmFyaWFibGUgbG9vayBsaWtlPyBUbyBoZWxwIHVzIHVuZGVyc3RhbmQgaG93IGl0IG1heSByZWxhdGUgdG8gc3Vydml2YWwsIGxldCdzIHBsb3QgaXQgdXNpbmcgdGhlIGBnZW9tX2JhciguLi4pYCBmdW5jdGlvbjoKYGBge3J9CiMgVXNlIGdncGxvdDIgdG8gdmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBmYW1pbHkgc2l6ZSAmIHN1cnZpdmFsCiMgZ2VvbV9iYXIoKSBwbG90cyBhIGJhciBjaGFydApnZ3Bsb3QodGl0YW5pY19kZiwgYWVzKHggPSBGc2l6ZSwgZmlsbCA9IGZhY3RvcihTdXJ2aXZlZCkpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAnZG9kZ2UnKSArIAogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6MTEpCmBgYApXZSBjYW4gc2VlIHRoYXQgdGhlcmXigJlzIGEgc3Vydml2YWwgcGVuYWx0eSB0byBzaW5nbGV0b25zIGFuZCB0aG9zZSB3aXRoIGZhbWlseSBzaXplcyBhYm92ZSA0LgoKIyMgSW52ZXN0aWdhdGlvbiBJSUkKQmVmb3JlIHdlIGludmVzdGlnYXRlIGFub3RoZXIgcGhlbm9tZW5vbiBsZXQncyByZXZpZXcgd2hhdCBhIGJveC13aGlza2VyIHBsb3QgaXMuCgohW0JveC1XaGlza2VyIHBsb3RdKC4uL0ltYWdlcy9ib3hfcGxvdC5wbmcpCgoqKkRvIHRoZSBGYXJlcyBvZiB0aGUgcGFzc2VuZ2VycyB2YXJ5IGR1ZSB0byBQYXNzZW5nZXIgQ2xhc3MgYW5kIFBvcnQgb2YgRW1iYXJrbWVudD8qKgoKSGVyZSBJIHdpbGwgYWxzbyBpbnRyb2R1Y2UgdGhlIHBpcGUgYCU+JWAgb3BlcmF0b3IuIFRoaW5rIG9mIGl0IGFzIGEgbWFjaGluZSB0aGF0IHRha2VzIHRoZSByZXN1bHQgb2YgdGhlIGZ1bmN0aW9uIHRvIHRoZSBsZWZ0IG9mIGl0IGFuZCBwYXNzZXMgaXQgYXMgYW4gYXJndW1lbnQgdG8gdGhlIGZ1bmN0aW9uIG9uIHRoZSByaWdodC4KYGBge3J9CiMgR3JvdXAgdGhlIGRhdGEgb24gRW1iYXJrZWQgYW5kIFBjbGFzcwp0aXRhbmljX2RmICU+JQogICAgZ3JvdXBfYnkoRW1iYXJrZWQsIFBjbGFzcykgJT4lCiAgICBzdW1tYXJpc2UobWVhbkYgPSBtZWFuKEZhcmUpLCBtZWRpYW5GID0gbWVkaWFuKEZhcmUpLCBtYXhGID0gbWF4KEZhcmUpLCBDb3VudCA9IG4oKSkKYGBgCldlbGwsIHZpc3VhbGl6aW5nIG51bWVyaWNhbCBkYXRhIGlzIHBvd2VyZnVsIHRvb2wgaW4gdW5kZXJzdGFuZGluZyB0aGUgc3ByZWFkIG9mIHRoZSBkYXRhIGFuZCBtb3JlIGltcG9ydGFudGx5IHRoZSBvdXRsaWVycy4gTGV0J3MgcGxvdCB0aGVzZSB2YWx1ZXMgdXNpbmcgdGhlIGBnZW9tX2JveHBsb3QoLi4uKWAgZnVuY3Rpb246CmBgYHtyfQojIGdlb21fYm94cGxvdCgpIHBsb3RzIGEgYm94cGxvdApnZ3Bsb3QodGl0YW5pY19kZiwgYWVzKHggPSBFbWJhcmtlZCwgeSA9IEZhcmUsIGZpbGwgPSBmYWN0b3IoUGNsYXNzKSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKV2UgY2FuIHNlZSB0aGVyZSBhcmUgc29tZSBjbGVhciBvdXRsaWVycyBoZXJlLiBNb3Jlb3ZlciwgUTIgYW5kIFEzIHNlZW0gdG8gYmUgdGhlIGNoZWFwZXN0IHRpY2tldHMuCgojIyBJbnZlc3RpZ2F0aW9uIElWCioqV2hhdCBmYWN0b3JzIGNvdWxkIHBvc3NpYmx5IGluZmx1ZW5jZSBzdXJ2aXZhbCByYXRlcyA/KioKCldlIGV4YW1pbmUgdGhlIGNvcnJlbGF0aW9uIChzdGFuZGFyZGlzZWQgY292YXJpYW5jZSkgd2hpY2ggaXMgY2FsY3VsYXRlZCBieSB0aGUgZm9ybXVsYSAkQ292KFgsWSkgPSBFKFhZKSAtIEUoWClFKFkpJCBmb3IgbnVtZXJpY2FsIHZlY3RvcnM6CmBgYHtyfQojIENvbnZlcnQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRvIG51bWVyaWNhbCwgc2VsZWN0IHRob3NlIGNvbHVtbnMgYW5kIGNhbGN1bGF0ZSBjb3JyZWxhdGlvbgpjb3JfZGYgPC0gdGl0YW5pY19kZiAlPiUKICAgIG11dGF0ZShTZXhOID0gYXMubnVtZXJpYyhTZXgpLCBFbWJhcmtlZE4gPSBhcy5udW1lcmljKEVtYmFya2VkKSkgJT4lCiAgICBzZWxlY3QoU3Vydml2ZWQsIEVtYmFya2VkTiwgUGNsYXNzLCBTZXhOLCBTaWJTcCwgUGFyY2gsIEZzaXplLCBGYXJlKSAlPiUKICAgIGNvcigpCiMgRGlzcGxheSB0aGUgZGF0YQpjb3JfZGYKYGBgCk5leHQgd2UgY3JlYXRlIGEgYG1lbHQoLi4uKWAgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBpbnRvIHRoZSBsb25nIGZvcm06CmBgYHtyfQojIE1lbHRzIHRoZSBkYXRhZnJhbWUKb3V0cHV0IDwtIG1lbHQoY29yX2RmKQojIERpc3BsYXkKb3V0cHV0CmBgYApMZXQncyB2aXN1YWxpc2UgdGhlIGNvcnJlbGF0aW9uIHVzaW5nIHRoZSBgZ2VvbV90aWxlKC4uLilgIGZ1bmN0aW9uOgpgYGB7cn0KIyBnZW9tX3RpbGUoKSBwbG90cyBhIGNvcnJlbGF0aW9uIG1hdHJpeApnZ3Bsb3Qob3V0cHV0LCBhZXMoeD1WYXIxLCB5PVZhcjIsIGZpbGw9dmFsdWUpKSArIAogICAgZ2VvbV90aWxlKCkKYGBgCgoqKkNvcnJlbGF0aW9uIGlzIG5vdCBDYXVzYXRpb24qKiBidXQ6CgogLSBUaGVyZSBzZWVtcyB0byBiZSBhIGhpZ2ggcG9zaXRpY2UgY29ycmVsYXRpb24gYmV0d2VlbiBgU2liU3BgLCBgUGFyY2hgIGFuZCBgRnNpemVgLiBXaGF0IGlzIHRoZSByZWFzb24gZm9yIHRoaXMgPwogLSBUaGVyZSBpcyBhIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gYFNleE5gIGFuZCBgU3Vydml2YWxgLiBJdCBzdWdnZXN0cyB0aGF0IG9uZSBzZXggd2FzIG1vcmUgbGlrZWx5IHRvIHN1cnZpdmUuIEJ1dCB3aGljaCBvbmUgaXMgaXQgPyBDYW4geW91IGluZmVyIGZyb20gYHN0cih0aXRhbmljX2RmKWAKIC0gV2hhdCBvdGhlciB0aGluZ3MgY2FuIHlvdSBzZWUgPwoKIyMgSW52ZXN0aWdhdGlvbiBWCioqRG9lcyB0aGUgYEZhcmVgIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24gPyoqCgpNb3N0IHJhbmRvbSB2YXJpYWJsZXMgaW4gbmF0dXJlIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIExldCdzIGZpbmQgb3V0IGRlbnNpdHkgcGxvdCBvZiB0aGUgYEZhcmVgIHZhcmlhYmxlIHVzaW5nIHRoZSBgZ2VvbV9kZW5zaXR5KC4uLilgIGZ1bmN0aW9uOgpgYGB7cn0KIyBnZW9tX2RlbnNpdHkoKSBwbG90cyBhIGRlbnNpdHkgcGxvdApnZ3Bsb3QodGl0YW5pY19kZiwgYWVzKHggPSBGYXJlKSkgKyBnZW9tX2RlbnNpdHkoKQpgYGAKSGVyZSdzIGEgc29tZSBjb2RlIHRvIGdldCB0aGUgZGVuc2l0eSB2YWx1ZXMgd2hpY2ggdXNlcyBhbGwgdGhlIHRhYmxlcyBvcGVyYXRpb25zIHdlIGxlYXJudCBzbyBmYXIuIE1ha2Ugc3VyZSB0byBleHBlcmltZW50IHdpdGggZXZlcnkgbGluZSBpbiB0aGUgY29kZSBiZWxvdzoKYGBge3J9CmRlbnNpdHlfdmFsdWVzIDwtIHRpdGFuaWNfZGYgJT4lICAgICAgICAgICAgICAgICAgICAKICAgIGdyb3VwX2J5KEZhcmUpICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdyb3VwcyB0aGUgZGF0YSBieSBGYXJlCiAgICBzdW1tYXJpc2UoRnJlcXVlbmN5ID0gbigpKSAlPiUgICAgICAgICAgICAgICAgICAgIyBHZW5lcmV0ZXMgZnJlcXVlbmN5IG9mIEZhcmUKICAgIG11dGF0ZShEZW5zaXR5ID0gRnJlcXVlbmN5L3N1bShGcmVxdWVuY3kpKSAlPiUgICAjIENyZWF0ZXMgYSBjb2x1bW4gRGVuc2l0eQogICAgc2VsZWN0KEZhcmUsIERlbnNpdHkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgT3V0cHV0cyB0aGUgRGVuc2l0eSBjb2x1bW4gb25seQojIERpc3BsYXkgZGVuc2l0eV92YWx1ZXMKZGVuc2l0eV92YWx1ZXMKCmBgYAo=