1 Recap

1.1 Vectors

Vectors are a series of numbers, characters or boolean values.

# Assignment
numeric_vector <- 1:10    # OR assign('numeric_vector', 1:10)
# Squared numbers
squared_numbers <- numeric_vector^2
# All squared numbers but the last 3
squared_numbers[-(8:10)]
[1]  1  4  9 16 25 36 49

The length of a vector can be determined like so:

length(squared_numbers)
[1] 10

1.2 Matrices

Matrices are two dimensional arrays containing elements of the same data-type:

# List of albums
albums <- c('Infinite', 'The Slim Shady LP', 'The Marshall Mathers LP', 
            'The Eminem Show', 'Encore', 'Relapse', 'Recovery', 
            'The Marshall Mathers LP 2', 'Revival')
# Year of Release
years <- c(1996, 1999, 2000, 2002, 2004, 2009, 2010, 2013, 2017)
# Eminem Matrix album
eminem_album_releases <- matrix(c(albums,years), nrow = 9, ncol = 2)
# Colnames of the matrix
colnames(eminem_album_releases) <- c("Albums","Release Year")
# Display
eminem_album_releases
      Albums                      Release Year
 [1,] "Infinite"                  "1996"      
 [2,] "The Slim Shady LP"         "1999"      
 [3,] "The Marshall Mathers LP"   "2000"      
 [4,] "The Eminem Show"           "2002"      
 [5,] "Encore"                    "2004"      
 [6,] "Relapse"                   "2009"      
 [7,] "Recovery"                  "2010"      
 [8,] "The Marshall Mathers LP 2" "2013"      
 [9,] "Revival"                   "2017"      

Let’s get the second album of Eminem:

# Will the Real Slim Shady please stand up ?
eminem_album_releases[2, 'Albums']
             Albums 
"The Slim Shady LP" 

2 Summary Statistics

2.1 Normal Distribution

In R, you can generate a sequence of random numbers that are normally distributed with a mean of 0 and standard deviation of 1 by default:

# Generate 20 random numbers
x.norm <- rnorm(n = 20)
# Display 'x.norm'
x.norm
 [1] -0.75269984  0.16712765 -0.68080459 -0.21067393  0.86269931 -1.05480882  0.69760359 -0.60681224 -0.11318004
[10]  0.81313333 -0.98551445  2.27346538 -2.26660936 -0.12874622  0.61728827 -1.14924134  1.27515620  0.90347140
[19]  0.42464996 -0.02620376

Let’s sort this data, using sort(...):

# Sorting
x.norm <- sort(x.norm)
# Display
x.norm
 [1] -2.26660936 -1.14924134 -1.05480882 -0.98551445 -0.75269984 -0.68080459 -0.60681224 -0.21067393 -0.12874622
[10] -0.11318004 -0.02620376  0.16712765  0.42464996  0.61728827  0.69760359  0.81313333  0.86269931  0.90347140
[19]  1.27515620  2.27346538

2.2 Mean, Median and Standard Deviation

The mean or the average value of x.norm can be calculated by:

# Average
mean(x.norm)
[1] 0.002965025

The median refers to the middle of the all observations:

# Median
median(x.norm)
[1] -0.0696919

The standard deviation, which calculates how far the observations stray from the mean,can also be calculated using the formula: \[\sqrt{\frac{\sum_{i=1}^N (x_i - \bar{x})^2}{N-1}}\] The same can be done in R like so:

# Standard deviation formula in R
sqrt(sum((x.norm - mean(x.norm))^2)/(length(x.norm) - 1))
[1] 1.028719

Mom’s spaghetti!!! There’s always a better way to do things, we can use the sd(...) function:

# SD from the function
sd(x.norm)
[1] 1.028719

2.3 Minimum, Maximum, Quantiles and Summary

Let’s see the functions min(...) and max(...) in action:

# Returns the smallest and biggest element
print(c(min(x.norm), max(x.norm)))
[1] -2.266609  2.273465

Quantiles are cutpoints between the observations that splits the data into equal parts. There are several quantile systems, but we generally refer to the 4-quantile system:

# Find the quantiles
q <- quantile(x.norm)
# Display
q
        0%        25%        50%        75%       100% 
-2.2666094 -0.6987784 -0.0696919  0.7264860  2.2734654 

Using what we learnt from vector logic, let’s see how the quantiles splits x.norm:

# First quantile
x.norm[x.norm < q[2]]
[1] -2.2666094 -1.1492413 -1.0548088 -0.9855144 -0.7526998
# Second quantile
x.norm[x.norm > q[2] & x.norm < q[3]]
[1] -0.6808046 -0.6068122 -0.2106739 -0.1287462 -0.1131800

Well, in this case as well there seems to better things to calculate the basic statistics using summary(...):

# Generic function to describe an object
summary(x.norm)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
-2.266609 -0.698778 -0.069692  0.002965  0.726486  2.273465 

2.4 Plotting Statistics

Let’s plot 20 random points to understand the normal distribution:

plot(rnorm(20))

Hmm, I think it will easier to appreciate the properties of the normal distribution (0 mean and 1 standard deviation) with more points:

plot(rnorm(1000))

For people interested in seeing the density function for these points:

w <- rnorm(1000) 
hist(w, col = "red", freq = F, xlim = c(-5,5))
curve(dnorm, -5, 5, add = T, col = "blue")

Let’s introduce a function abline(...) which enables us to add horizontal and vertical lines to an existing plot:

plot(x.norm)
# 'h' means horizontal line
# 'col' means color
abline(h = mean(x.norm), col = 'red')
abline(h = median(x.norm), col = 'blue')
abline(h = mean(x.norm) + sd(x.norm), col = 'green')
abline(h = mean(x.norm) - sd(x.norm), col = 'green')

Let’s try to plot the quantiles from before:

plot(x.norm)
abline(h = median(x.norm), col = 'blue')
abline(h = summary(x.norm)[2], col = 'red')
abline(h = summary(x.norm)[5], col = 'red')

3 Factors

3.1 Categorical Variables

The term factor refers to a statistical data type used to store categorical variables. The difference between a categorical variable and a continuous variable is that a categorical variable can belong to a limited number of categories. A continuous variable, on the other hand, can correspond to an infinite number of values. Let’s invoke the factor(...) function:

# Language Codes
lang <- c("en","fr","hi","hi","ru","ru","ru","ru","fr","hi","en","cn")
# Factor of Language codes
langf <- factor(lang)
langf
 [1] en fr hi hi ru ru ru ru fr hi en cn
Levels: cn en fr hi ru

There are two types of categorical variables: a nominal categorical variable and an ordinal categorical variable. A nominal variable is a categorical variable without an implied order. This means that it is impossible to say that ‘one is worth more than the other’. While, the ordinal variable will have some inherent ordering. We’ve just seen an example of a nominal variable, let’s see an example of the ordinal variable:

# Speed traps measuring car speeds
speed <- c("high","med","high","med","low")
# The ordering is given by specifying the levels
speedf <- factor(speed, ordered = TRUE, levels = c("low","med","high"))
speedf
[1] high med  high med  low 
Levels: low < med < high

3.2 Levels

Notice also that there are no quotes around the values. That’s because they’re not strings; they’re actually integer references to one of the factor’s levels. But what is a level? It is simply the unique values in the vector:

levels(speedf)
[1] "low"  "med"  "high"

Let’s try comparing ordered factors. In our previous example, we want to test whether the second car is going slower than the third car:

# Ordered factors can compare strings
speedf[2] < speedf[3]
[1] TRUE

3.3 Frequency tables

Try calling summary(...) on every object from now on:

# Returns frequencies
summary(langf)
cn en fr hi ru 
 1  2  2  3  4 

3.4 Tests for Factors

At this point, there are built-in functions that are similar for nearly every data structure, so don’t be surprised with a is.factor(...):

# Performs a check for factor
is.factor(langf)
[1] TRUE

Or, something like this:

# Returns a vector
as.vector(langf)
 [1] "en" "fr" "hi" "hi" "ru" "ru" "ru" "ru" "fr" "hi" "en" "cn"

Or, this:

# Returns a column
as.matrix(langf)
      [,1]
 [1,] "en"
 [2,] "fr"
 [3,] "hi"
 [4,] "hi"
 [5,] "ru"
 [6,] "ru"
 [7,] "ru"
 [8,] "ru"
 [9,] "fr"
[10,] "hi"
[11,] "en"
[12,] "cn"

4 Dataframes

4.1 Explore a Dataset

There are several built-in datasets within R. Arguably, the ‘Hello World’ example of these datasets is mtcars:

# Motor Trend Car Road Tests
mtcars

Let’s have a look at the top rows from this dataset using head(...):

# Top rows
head(mtcars)

Let’s have a look at the bottom rows from this dataset using tail(...):

# Bottom rows
tail(mtcars)

Invoking class(mtcars) will tell us it’s an object of type data.frame. But let’s go a step further and explore the structure of the data frame using str(...):

# Returns the structure
str(mtcars)
'data.frame':   32 obs. of  11 variables:
 $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
 $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
 $ disp: num  160 160 108 258 360 ...
 $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
 $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
 $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
 $ qsec: num  16.5 17 18.6 19.4 17 ...
 $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
 $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
 $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
 $ carb: num  4 4 1 1 2 1 4 2 2 4 ...

4.2 Creating Dataframes

Calling the data.frame(...) function enables us to create a dataframe. Notice the vector names become the column names automatically. Each data argument to the data.frame(...) function takes the form of either value or tag = value:

# Definition of vectors
name <- c("Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune")
type <- c("Terrestrial planet", "Terrestrial planet", "Terrestrial planet", 
          "Terrestrial planet", "Gas giant", "Gas giant", "Gas giant", "Gas giant")
diameter <- c(0.382, 0.949, 1, 0.532, 11.209, 9.449, 4.007, 3.883)
rotation <- c(58.64, -243.02, 1, 1.03, 0.41, 0.43, -0.72, 0.67)
rings <- c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE)
# Create a data frame from the vectors
planets_df <- data.frame(name, type, diameter, rotation, rings)
planets_df

Let’s explore the structure once again:

# Returns the structure of the planets
str(planets_df)
'data.frame':   8 obs. of  5 variables:
 $ name    : Factor w/ 8 levels "Earth","Jupiter",..: 4 8 1 3 2 6 7 5
 $ type    : Factor w/ 2 levels "Gas giant","Terrestrial planet": 2 2 2 2 1 1 1 1
 $ diameter: num  0.382 0.949 1 0.532 11.209 ...
 $ rotation: num  58.64 -243.02 1 1.03 0.41 ...
 $ rings   : logi  FALSE FALSE FALSE FALSE TRUE TRUE ...

4.3 Dataframe Access

Dataframes share a lot in common with matrices. Think of dataframes as extended matrices, without the necessity to contain elements of the same data type. However, there are a few quirks. Let’s try accessing some columns:

# First column by index
planets_df[[1]]
[1] Mercury Venus   Earth   Mars    Jupiter Saturn  Uranus  Neptune
Levels: Earth Jupiter Mars Mercury Neptune Saturn Uranus Venus
# First column by name
planets_df[['type']]
[1] Terrestrial planet Terrestrial planet Terrestrial planet Terrestrial planet Gas giant         
[6] Gas giant          Gas giant          Gas giant         
Levels: Gas giant Terrestrial planet
# By using the '$' atomic vector
planets_df$diameter
[1]  0.382  0.949  1.000  0.532 11.209  9.449  4.007  3.883

4.4 Tests for Dataframes

TLDR, is.data.frame(...):

# Checks if 'planets_df' is a dataframe
is.data.frame(planets_df)
[1] TRUE

You can also coerce other objects into dataframes:

# Coerces matrix to dataframe
as.data.frame(matrix(1:10, nrow = 5, ncol = 2, dimnames = list(NULL, c("X","Y"))))

5 Practice! Practice! Practice!

5.1 Access by Indices

What’s the diameter of Mercury?

# Print out diameter of Mercury (row 1, column 3)
planets_df[1,3]
[1] 0.382

Show me all the data for Mars!

# Print out data for Mars (entire fourth row)
planets_df[4, ]

5.2 Access by names

What are the diameters of all planets?

# planets_df[['diameters']] is also valid
planets_df$diameter
[1]  0.382  0.949  1.000  0.532 11.209  9.449  4.007  3.883

What type of planets are the first 3 planets?

# planets_df[1:3,"type"] is also valid
planets_df$type[1:3]
[1] Terrestrial planet Terrestrial planet Terrestrial planet
Levels: Gas giant Terrestrial planet

5.3 Access by logical vectors

Which planets have positive rotation?

# planets_df[,'rotation'] > 0 is also valid
positive_rotation <- planets_df$rotation > 0
# Plantes with positive rotation
planets_df[positive_rotation,]

Names of the planets that have positive rotation and rings:

# Give yourself a cookie if you got this right!!!
planets_df[planets_df$rotation > 0 & planets_df$rings, 'name']
[1] Jupiter Saturn  Neptune
Levels: Earth Jupiter Mars Mercury Neptune Saturn Uranus Venus

6 Lists

6.1 Creating Lists

Lists, as opposed to vectors, can hold components of different types. These objects can be matrices, vectors, data frames, even other lists, etc. It is not even required that these objects are related to each other in any way.

heroes <- c('Ironman', 'Thor', 'Captain America', 'Wonder Woman', 'Batman')
actors <- c('Sir Robert Downey Jr', 'Chris Hemsworth', 'Chris Evans', 'Gal Gadot', 'Christian Bale')
level <- c(100, 1000, 10, 10000, 100000)
# Initialise the list
super_heroes <- list(power_level = data.frame(heroes,level),
                     serial_no = 1:5, 
                     actor_names = actors)
super_heroes
$power_level

$serial_no
[1] 1 2 3 4 5

$actor_names
[1] "Sir Robert Downey Jr" "Chris Hemsworth"      "Chris Evans"          "Gal Gadot"           
[5] "Christian Bale"      

6.2 Access for lists

We can access the elements in a list through indexing through double squared brackets [[...]]:

# Displays the second element in the list
super_heroes[[2]]
[1] "Sir Robert Downey Jr" "Chris Hemsworth"      "Chris Evans"          "Gal Gadot"           
[5] "Christian Bale"      

The list can be accessed through the named elements as well. Let’s use names(...) to see the named elements of the list:

# Named elements of the list
names(super_heroes)
[1] "serial_no"   "actor_names" "power_level"

There are two fundamental ways to access a list through names, here we use the element name:

# Through element names
super_heroes[["serial_no"]]
[1] 1 2 3 4 5

And here we invoke the atomic vector

super_heroes$power_level

6.3 Test for list

The function is.list(...) tests for lists and returns a boolean value:

is.list(super_heroes)
[1] TRUE

One can also coerce an object into a list using the function as.list(...):

# Create a dataframe
The_Simpsons <- data.frame(first_name = c("Bart","Lisa","Homer","Marge","Baby"), 
                           last_name = rep("Simpson", 5))
The_Simpsons
# Coercing a dataframe into a list
as.list(The_Simpsons)
$first_name
[1] Bart  Lisa  Homer Marge Baby 
Levels: Baby Bart Homer Lisa Marge

$last_name
[1] Simpson Simpson Simpson Simpson Simpson
Levels: Simpson

But more often we will use str(...) to describe objects like lists:

# Displays the entire structure of the list
str(super_heroes)
List of 3
 $ power_level:'data.frame':    5 obs. of  2 variables:
  ..$ heroes: Factor w/ 5 levels "Batman","Captain America",..: 3 4 2 5 1
  ..$ level : num [1:5] 1e+02 1e+03 1e+01 1e+04 1e+05
 $ serial_no  : int [1:5] 1 2 3 4 5
 $ actor_names: chr [1:5] "Sir Robert Downey Jr" "Chris Hemsworth" "Chris Evans" "Gal Gadot" ...
LS0tCnRpdGxlOiAiU3VtbWFyeSBTdGF0aXN0aWNzLCBGYWN0b3JzLCBEYXRhZnJhbWVzIGFuZCBMaXN0cyIKYXV0aG9yOiAiSWMzZnIwZyIKZGF0ZTogJ2ByIFN5cy5EYXRlKClgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHllcwogICAgZmlnX2hlaWdodDogNC41CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHllcwogICAgZmlnX2hlaWdodDogNC41CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKCiMgUmVjYXAgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyBWZWN0b3JzClZlY3RvcnMgYXJlIGEgc2VyaWVzIG9mIG51bWJlcnMsIGNoYXJhY3RlcnMgb3IgYm9vbGVhbiB2YWx1ZXMuCmBgYHtyfQojIEFzc2lnbm1lbnQKbnVtZXJpY192ZWN0b3IgPC0gMToxMCAgICAjIE9SIGFzc2lnbignbnVtZXJpY192ZWN0b3InLCAxOjEwKQojIFNxdWFyZWQgbnVtYmVycwpzcXVhcmVkX251bWJlcnMgPC0gbnVtZXJpY192ZWN0b3JeMgojIEFsbCBzcXVhcmVkIG51bWJlcnMgYnV0IHRoZSBsYXN0IDMKc3F1YXJlZF9udW1iZXJzWy0oODoxMCldCmBgYApUaGUgbGVuZ3RoIG9mIGEgdmVjdG9yIGNhbiBiZSBkZXRlcm1pbmVkIGxpa2Ugc286CmBgYHtyfQpsZW5ndGgoc3F1YXJlZF9udW1iZXJzKQpgYGAKCiMjIE1hdHJpY2VzCk1hdHJpY2VzIGFyZSB0d28gZGltZW5zaW9uYWwgYXJyYXlzIGNvbnRhaW5pbmcgZWxlbWVudHMgb2YgdGhlIHNhbWUgZGF0YS10eXBlOgpgYGB7cn0KIyBMaXN0IG9mIGFsYnVtcwphbGJ1bXMgPC0gYygnSW5maW5pdGUnLCAnVGhlIFNsaW0gU2hhZHkgTFAnLCAnVGhlIE1hcnNoYWxsIE1hdGhlcnMgTFAnLCAKICAgICAgICAgICAgJ1RoZSBFbWluZW0gU2hvdycsICdFbmNvcmUnLCAnUmVsYXBzZScsICdSZWNvdmVyeScsIAogICAgICAgICAgICAnVGhlIE1hcnNoYWxsIE1hdGhlcnMgTFAgMicsICdSZXZpdmFsJykKIyBZZWFyIG9mIFJlbGVhc2UKeWVhcnMgPC0gYygxOTk2LCAxOTk5LCAyMDAwLCAyMDAyLCAyMDA0LCAyMDA5LCAyMDEwLCAyMDEzLCAyMDE3KQojIEVtaW5lbSBNYXRyaXggYWxidW0KZW1pbmVtX2FsYnVtX3JlbGVhc2VzIDwtIG1hdHJpeChjKGFsYnVtcyx5ZWFycyksIG5yb3cgPSA5LCBuY29sID0gMikKIyBDb2xuYW1lcyBvZiB0aGUgbWF0cml4CmNvbG5hbWVzKGVtaW5lbV9hbGJ1bV9yZWxlYXNlcykgPC0gYygiQWxidW1zIiwiUmVsZWFzZSBZZWFyIikKIyBEaXNwbGF5CmVtaW5lbV9hbGJ1bV9yZWxlYXNlcwpgYGAKTGV0J3MgZ2V0IHRoZSBzZWNvbmQgYWxidW0gb2YgRW1pbmVtOgpgYGB7cn0KIyBXaWxsIHRoZSBSZWFsIFNsaW0gU2hhZHkgcGxlYXNlIHN0YW5kIHVwID8KZW1pbmVtX2FsYnVtX3JlbGVhc2VzWzIsICdBbGJ1bXMnXQpgYGAKCiMgU3VtbWFyeSBTdGF0aXN0aWNzIHsudGFic2V0IC50YWJlc3QtZmFkZSAudGFiZXN0LXBpbGxzfQoKIyMgTm9ybWFsIERpc3RyaWJ1dGlvbgpJbiBSLCB5b3UgY2FuIGdlbmVyYXRlIGEgc2VxdWVuY2Ugb2YgcmFuZG9tIG51bWJlcnMgdGhhdCBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aCBhIG1lYW4gb2YgMCBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDEgYnkgZGVmYXVsdDoKYGBge3J9CiMgR2VuZXJhdGUgMjAgcmFuZG9tIG51bWJlcnMKeC5ub3JtIDwtIHJub3JtKG4gPSAyMCkKIyBEaXNwbGF5ICd4Lm5vcm0nCngubm9ybQpgYGAKTGV0J3Mgc29ydCB0aGlzIGRhdGEsIHVzaW5nIGBzb3J0KC4uLilgOgpgYGB7cn0KIyBTb3J0aW5nCngubm9ybSA8LSBzb3J0KHgubm9ybSkKIyBEaXNwbGF5Cngubm9ybQpgYGAKCiMjIE1lYW4sIE1lZGlhbiBhbmQgU3RhbmRhcmQgRGV2aWF0aW9uClRoZSBtZWFuIG9yIHRoZSBhdmVyYWdlIHZhbHVlIG9mIGB4Lm5vcm1gIGNhbiBiZSBjYWxjdWxhdGVkIGJ5OgpgYGB7cn0KIyBBdmVyYWdlCm1lYW4oeC5ub3JtKQpgYGAKVGhlIG1lZGlhbiByZWZlcnMgdG8gdGhlIG1pZGRsZSBvZiB0aGUgYWxsIG9ic2VydmF0aW9uczoKYGBge3J9CiMgTWVkaWFuCm1lZGlhbih4Lm5vcm0pCmBgYApUaGUgc3RhbmRhcmQgZGV2aWF0aW9uLCB3aGljaCBjYWxjdWxhdGVzIGhvdyBmYXIgdGhlIG9ic2VydmF0aW9ucyBzdHJheSBmcm9tIHRoZSBtZWFuLGNhbiBhbHNvIGJlIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIGZvcm11bGE6IAokJFxzcXJ0e1xmcmFje1xzdW1fe2k9MX1eTiAoeF9pIC0gXGJhcnt4fSleMn17Ti0xfX0kJApUaGUgc2FtZSBjYW4gYmUgZG9uZSBpbiBSIGxpa2Ugc286CmBgYHtyfQojIFN0YW5kYXJkIGRldmlhdGlvbiBmb3JtdWxhIGluIFIKc3FydChzdW0oKHgubm9ybSAtIG1lYW4oeC5ub3JtKSleMikvKGxlbmd0aCh4Lm5vcm0pIC0gMSkpCmBgYAoqTW9tJ3Mgc3BhZ2hldHRpISEhKiBUaGVyZSdzIGFsd2F5cyBhIGJldHRlciB3YXkgdG8gZG8gdGhpbmdzLCB3ZSBjYW4gdXNlIHRoZSBgc2QoLi4uKWAgZnVuY3Rpb246CmBgYHtyfQojIFNEIGZyb20gdGhlIGZ1bmN0aW9uCnNkKHgubm9ybSkKYGBgCgojIyBNaW5pbXVtLCBNYXhpbXVtLCBRdWFudGlsZXMgYW5kIFN1bW1hcnkKTGV0J3Mgc2VlIHRoZSBmdW5jdGlvbnMgYG1pbiguLi4pYCBhbmQgYG1heCguLi4pYCBpbiBhY3Rpb246CmBgYHtyfQojIFJldHVybnMgdGhlIHNtYWxsZXN0IGFuZCBiaWdnZXN0IGVsZW1lbnQKcHJpbnQoYyhtaW4oeC5ub3JtKSwgbWF4KHgubm9ybSkpKQpgYGAKUXVhbnRpbGVzIGFyZSBjdXRwb2ludHMgYmV0d2VlbiB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgc3BsaXRzIHRoZSBkYXRhIGludG8gZXF1YWwgcGFydHMuIFRoZXJlIGFyZSBzZXZlcmFsIHF1YW50aWxlIHN5c3RlbXMsIGJ1dCB3ZSBnZW5lcmFsbHkgcmVmZXIgdG8gdGhlIDQtcXVhbnRpbGUgc3lzdGVtOgpgYGB7cn0KIyBGaW5kIHRoZSBxdWFudGlsZXMKcSA8LSBxdWFudGlsZSh4Lm5vcm0pCiMgRGlzcGxheQpxCmBgYApVc2luZyB3aGF0IHdlIGxlYXJudCBmcm9tIHZlY3RvciBsb2dpYywgbGV0J3Mgc2VlIGhvdyB0aGUgcXVhbnRpbGVzIHNwbGl0cyBgeC5ub3JtYDoKYGBge3J9CiMgRmlyc3QgcXVhbnRpbGUKeC5ub3JtW3gubm9ybSA8IHFbMl1dCmBgYApgYGB7cn0KIyBTZWNvbmQgcXVhbnRpbGUKeC5ub3JtW3gubm9ybSA+IHFbMl0gJiB4Lm5vcm0gPCBxWzNdXQpgYGAKV2VsbCwgaW4gdGhpcyBjYXNlIGFzIHdlbGwgdGhlcmUgc2VlbXMgdG8gYmV0dGVyIHRoaW5ncyB0byBjYWxjdWxhdGUgdGhlIGJhc2ljIHN0YXRpc3RpY3MgdXNpbmcgYHN1bW1hcnkoLi4uKWA6CmBgYHtyfQojIEdlbmVyaWMgZnVuY3Rpb24gdG8gZGVzY3JpYmUgYW4gb2JqZWN0CnN1bW1hcnkoeC5ub3JtKQpgYGAKCiMjIFBsb3R0aW5nIFN0YXRpc3RpY3MKTGV0J3MgcGxvdCAyMCByYW5kb20gcG9pbnRzIHRvIHVuZGVyc3RhbmQgdGhlIG5vcm1hbCBkaXN0cmlidXRpb246CmBgYHtyfQpwbG90KHJub3JtKDIwKSkKYGBgCgpIbW0sIEkgdGhpbmsgaXQgd2lsbCBlYXNpZXIgdG8gYXBwcmVjaWF0ZSB0aGUgcHJvcGVydGllcyBvZiB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiAqKDAgbWVhbiBhbmQgMSBzdGFuZGFyZCBkZXZpYXRpb24pKiB3aXRoIG1vcmUgcG9pbnRzOgpgYGB7cn0KcGxvdChybm9ybSgxMDAwKSkKYGBgCgpGb3IgcGVvcGxlIGludGVyZXN0ZWQgaW4gc2VlaW5nIHRoZSBkZW5zaXR5IGZ1bmN0aW9uIGZvciB0aGVzZSBwb2ludHM6CmBgYHtyfQp3IDwtIHJub3JtKDEwMDApIApoaXN0KHcsIGNvbCA9ICJyZWQiLCBmcmVxID0gRiwgeGxpbSA9IGMoLTUsNSkpCmN1cnZlKGRub3JtLCAtNSwgNSwgYWRkID0gVCwgY29sID0gImJsdWUiKQpgYGAKCkxldCdzIGludHJvZHVjZSBhIGZ1bmN0aW9uIGBhYmxpbmUoLi4uKWAgd2hpY2ggZW5hYmxlcyB1cyB0byBhZGQgaG9yaXpvbnRhbCBhbmQgdmVydGljYWwgbGluZXMgdG8gYW4gZXhpc3RpbmcgcGxvdDogCmBgYHtyfQpwbG90KHgubm9ybSkKIyAnaCcgbWVhbnMgaG9yaXpvbnRhbCBsaW5lCiMgJ2NvbCcgbWVhbnMgY29sb3IKYWJsaW5lKGggPSBtZWFuKHgubm9ybSksIGNvbCA9ICdyZWQnKQphYmxpbmUoaCA9IG1lZGlhbih4Lm5vcm0pLCBjb2wgPSAnYmx1ZScpCmFibGluZShoID0gbWVhbih4Lm5vcm0pICsgc2QoeC5ub3JtKSwgY29sID0gJ2dyZWVuJykKYWJsaW5lKGggPSBtZWFuKHgubm9ybSkgLSBzZCh4Lm5vcm0pLCBjb2wgPSAnZ3JlZW4nKQpgYGAKCkxldCdzIHRyeSB0byBwbG90IHRoZSBxdWFudGlsZXMgZnJvbSBiZWZvcmU6CmBgYHtyfQpwbG90KHgubm9ybSkKYWJsaW5lKGggPSBtZWRpYW4oeC5ub3JtKSwgY29sID0gJ2JsdWUnKQphYmxpbmUoaCA9IHN1bW1hcnkoeC5ub3JtKVsyXSwgY29sID0gJ3JlZCcpCmFibGluZShoID0gc3VtbWFyeSh4Lm5vcm0pWzVdLCBjb2wgPSAncmVkJykKYGBgCgoKIyBGYWN0b3JzIHsudGFic2V0IC50YWJlc3QtZmFkZSAudGFiZXN0LXBpbGxzfQoKIyMgQ2F0ZWdvcmljYWwgVmFyaWFibGVzClRoZSB0ZXJtIGZhY3RvciByZWZlcnMgdG8gYSBzdGF0aXN0aWNhbCBkYXRhIHR5cGUgdXNlZCB0byBzdG9yZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIFRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhbmQgYSBjb250aW51b3VzIHZhcmlhYmxlIGlzIHRoYXQgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBjYW4gYmVsb25nIHRvIGEgbGltaXRlZCBudW1iZXIgb2YgY2F0ZWdvcmllcy4gQSBjb250aW51b3VzIHZhcmlhYmxlLCBvbiB0aGUgb3RoZXIgaGFuZCwgY2FuIGNvcnJlc3BvbmQgdG8gYW4gaW5maW5pdGUgbnVtYmVyIG9mIHZhbHVlcy4gTGV0J3MgaW52b2tlIHRoZSBgZmFjdG9yKC4uLilgIGZ1bmN0aW9uOgpgYGB7cn0KIyBMYW5ndWFnZSBDb2RlcwpsYW5nIDwtIGMoImVuIiwiZnIiLCJoaSIsImhpIiwicnUiLCJydSIsInJ1IiwicnUiLCJmciIsImhpIiwiZW4iLCJjbiIpCiMgRmFjdG9yIG9mIExhbmd1YWdlIGNvZGVzCmxhbmdmIDwtIGZhY3RvcihsYW5nKQpsYW5nZgpgYGAKVGhlcmUgYXJlIHR3byB0eXBlcyBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXM6IGEgKipub21pbmFsKiogY2F0ZWdvcmljYWwgdmFyaWFibGUgYW5kIGFuICoqb3JkaW5hbCoqIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBBIG5vbWluYWwgdmFyaWFibGUgaXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRob3V0IGFuIGltcGxpZWQgb3JkZXIuIFRoaXMgbWVhbnMgdGhhdCBpdCBpcyBpbXBvc3NpYmxlIHRvIHNheSB0aGF0ICdvbmUgaXMgd29ydGggbW9yZSB0aGFuIHRoZSBvdGhlcicuIFdoaWxlLCB0aGUgb3JkaW5hbCB2YXJpYWJsZSB3aWxsIGhhdmUgc29tZSBpbmhlcmVudCBvcmRlcmluZy4gV2UndmUganVzdCBzZWVuIGFuIGV4YW1wbGUgb2YgYSBub21pbmFsIHZhcmlhYmxlLCBsZXQncyBzZWUgYW4gZXhhbXBsZSBvZiB0aGUgb3JkaW5hbCB2YXJpYWJsZToKYGBge3J9CiMgU3BlZWQgdHJhcHMgbWVhc3VyaW5nIGNhciBzcGVlZHMKc3BlZWQgPC0gYygiaGlnaCIsIm1lZCIsImhpZ2giLCJtZWQiLCJsb3ciKQojIFRoZSBvcmRlcmluZyBpcyBnaXZlbiBieSBzcGVjaWZ5aW5nIHRoZSBsZXZlbHMKc3BlZWRmIDwtIGZhY3RvcihzcGVlZCwgb3JkZXJlZCA9IFRSVUUsIGxldmVscyA9IGMoImxvdyIsIm1lZCIsImhpZ2giKSkKc3BlZWRmCmBgYAoKIyMgTGV2ZWxzCk5vdGljZSBhbHNvIHRoYXQgdGhlcmUgYXJlIG5vIHF1b3RlcyBhcm91bmQgdGhlIHZhbHVlcy4gVGhhdCdzIGJlY2F1c2UgdGhleSdyZSBub3Qgc3RyaW5nczsgdGhleSdyZSBhY3R1YWxseSBpbnRlZ2VyIHJlZmVyZW5jZXMgdG8gb25lIG9mIHRoZSBmYWN0b3IncyBsZXZlbHMuIEJ1dCB3aGF0IGlzIGEgKmxldmVsKj8gSXQgaXMgc2ltcGx5IHRoZSB1bmlxdWUgdmFsdWVzIGluIHRoZSB2ZWN0b3I6CmBgYHtyfQpsZXZlbHMoc3BlZWRmKQpgYGAKTGV0J3MgdHJ5IGNvbXBhcmluZyBvcmRlcmVkIGZhY3RvcnMuIEluIG91ciBwcmV2aW91cyBleGFtcGxlLCB3ZSB3YW50IHRvIHRlc3Qgd2hldGhlciB0aGUgc2Vjb25kIGNhciBpcyBnb2luZyBzbG93ZXIgdGhhbiB0aGUgdGhpcmQgY2FyOgpgYGB7cn0KIyBPcmRlcmVkIGZhY3RvcnMgY2FuIGNvbXBhcmUgc3RyaW5ncwpzcGVlZGZbMl0gPCBzcGVlZGZbM10KYGBgCgojIyBGcmVxdWVuY3kgdGFibGVzClRyeSBjYWxsaW5nIGBzdW1tYXJ5KC4uLilgIG9uIGV2ZXJ5IG9iamVjdCBmcm9tIG5vdyBvbjoKYGBge3J9CiMgUmV0dXJucyBmcmVxdWVuY2llcwpzdW1tYXJ5KGxhbmdmKQpgYGAKCiMjIFRlc3RzIGZvciBGYWN0b3JzCkF0IHRoaXMgcG9pbnQsIHRoZXJlIGFyZSBidWlsdC1pbiBmdW5jdGlvbnMgdGhhdCBhcmUgc2ltaWxhciBmb3IgbmVhcmx5IGV2ZXJ5IGRhdGEgc3RydWN0dXJlLCBzbyBkb24ndCBiZSBzdXJwcmlzZWQgd2l0aCBhIGBpcy5mYWN0b3IoLi4uKWA6CmBgYHtyfQojIFBlcmZvcm1zIGEgY2hlY2sgZm9yIGZhY3Rvcgppcy5mYWN0b3IobGFuZ2YpCmBgYApPciwgc29tZXRoaW5nIGxpa2UgdGhpczoKYGBge3J9CiMgUmV0dXJucyBhIHZlY3Rvcgphcy52ZWN0b3IobGFuZ2YpCmBgYApPciwgdGhpczoKYGBge3J9CiMgUmV0dXJucyBhIGNvbHVtbgphcy5tYXRyaXgobGFuZ2YpCmBgYAoKCiMgRGF0YWZyYW1lcyB7LnRhYnNldCAudGFiZXN0LWZhZGUgLnRhYmVzdC1waWxsc30KCiMjIEV4cGxvcmUgYSBEYXRhc2V0ClRoZXJlIGFyZSBzZXZlcmFsIGJ1aWx0LWluIGRhdGFzZXRzIHdpdGhpbiBSLiBBcmd1YWJseSwgdGhlICdIZWxsbyBXb3JsZCcgZXhhbXBsZSBvZiB0aGVzZSBkYXRhc2V0cyBpcyBgbXRjYXJzYDoKYGBge3J9CiMgTW90b3IgVHJlbmQgQ2FyIFJvYWQgVGVzdHMKbXRjYXJzCmBgYApMZXQncyBoYXZlIGEgbG9vayBhdCB0aGUgdG9wIHJvd3MgZnJvbSB0aGlzIGRhdGFzZXQgdXNpbmcgYGhlYWQoLi4uKWA6CmBgYHtyfQojIFRvcCByb3dzCmhlYWQobXRjYXJzKQpgYGAKTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIGJvdHRvbSByb3dzIGZyb20gdGhpcyBkYXRhc2V0IHVzaW5nIGB0YWlsKC4uLilgOgpgYGB7cn0KIyBCb3R0b20gcm93cwp0YWlsKG10Y2FycykKYGBgCkludm9raW5nIGBjbGFzcyhtdGNhcnMpYCB3aWxsIHRlbGwgdXMgaXQncyBhbiBvYmplY3Qgb2YgdHlwZSBgZGF0YS5mcmFtZWAuIEJ1dCBsZXQncyBnbyBhIHN0ZXAgZnVydGhlciBhbmQgZXhwbG9yZSB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhIGZyYW1lIHVzaW5nIGBzdHIoLi4uKWA6CmBgYHtyfQojIFJldHVybnMgdGhlIHN0cnVjdHVyZQpzdHIobXRjYXJzKQpgYGAKCiMjIENyZWF0aW5nIERhdGFmcmFtZXMKQ2FsbGluZyB0aGUgYGRhdGEuZnJhbWUoLi4uKWAgZnVuY3Rpb24gZW5hYmxlcyB1cyB0byBjcmVhdGUgYSBkYXRhZnJhbWUuIE5vdGljZSB0aGUgdmVjdG9yIG5hbWVzIGJlY29tZSB0aGUgY29sdW1uIG5hbWVzIGF1dG9tYXRpY2FsbHkuIEVhY2ggZGF0YSBhcmd1bWVudCB0byB0aGUgYGRhdGEuZnJhbWUoLi4uKWAgZnVuY3Rpb24gdGFrZXMgdGhlIGZvcm0gb2YgZWl0aGVyICp2YWx1ZSogb3IgKnRhZyA9IHZhbHVlKjoKYGBge3J9CiMgRGVmaW5pdGlvbiBvZiB2ZWN0b3JzCm5hbWUgPC0gYygiTWVyY3VyeSIsICJWZW51cyIsICJFYXJ0aCIsICJNYXJzIiwgIkp1cGl0ZXIiLCAiU2F0dXJuIiwgIlVyYW51cyIsICJOZXB0dW5lIikKdHlwZSA8LSBjKCJUZXJyZXN0cmlhbCBwbGFuZXQiLCAiVGVycmVzdHJpYWwgcGxhbmV0IiwgIlRlcnJlc3RyaWFsIHBsYW5ldCIsIAogICAgICAgICAgIlRlcnJlc3RyaWFsIHBsYW5ldCIsICJHYXMgZ2lhbnQiLCAiR2FzIGdpYW50IiwgIkdhcyBnaWFudCIsICJHYXMgZ2lhbnQiKQpkaWFtZXRlciA8LSBjKDAuMzgyLCAwLjk0OSwgMSwgMC41MzIsIDExLjIwOSwgOS40NDksIDQuMDA3LCAzLjg4MykKcm90YXRpb24gPC0gYyg1OC42NCwgLTI0My4wMiwgMSwgMS4wMywgMC40MSwgMC40MywgLTAuNzIsIDAuNjcpCnJpbmdzIDwtIGMoRkFMU0UsIEZBTFNFLCBGQUxTRSwgRkFMU0UsIFRSVUUsIFRSVUUsIFRSVUUsIFRSVUUpCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZnJvbSB0aGUgdmVjdG9ycwpwbGFuZXRzX2RmIDwtIGRhdGEuZnJhbWUobmFtZSwgdHlwZSwgZGlhbWV0ZXIsIHJvdGF0aW9uLCByaW5ncykKcGxhbmV0c19kZgpgYGAKTGV0J3MgZXhwbG9yZSB0aGUgc3RydWN0dXJlIG9uY2UgYWdhaW46CmBgYHtyfQojIFJldHVybnMgdGhlIHN0cnVjdHVyZSBvZiB0aGUgcGxhbmV0cwpzdHIocGxhbmV0c19kZikKYGBgCgojIyBEYXRhZnJhbWUgQWNjZXNzCkRhdGFmcmFtZXMgc2hhcmUgYSBsb3QgaW4gY29tbW9uIHdpdGggbWF0cmljZXMuIFRoaW5rIG9mIGRhdGFmcmFtZXMgYXMgZXh0ZW5kZWQgbWF0cmljZXMsIHdpdGhvdXQgdGhlIG5lY2Vzc2l0eSB0byBjb250YWluIGVsZW1lbnRzIG9mIHRoZSBzYW1lIGRhdGEgdHlwZS4gSG93ZXZlciwgdGhlcmUgYXJlIGEgZmV3IHF1aXJrcy4gTGV0J3MgdHJ5IGFjY2Vzc2luZyBzb21lIGNvbHVtbnM6CmBgYHtyfQojIEZpcnN0IGNvbHVtbiBieSBpbmRleApwbGFuZXRzX2RmW1sxXV0KYGBgCmBgYHtyfQojIEZpcnN0IGNvbHVtbiBieSBuYW1lCnBsYW5ldHNfZGZbWyd0eXBlJ11dCmBgYApgYGB7cn0KIyBCeSB1c2luZyB0aGUgJyQnIGF0b21pYyB2ZWN0b3IKcGxhbmV0c19kZiRkaWFtZXRlcgpgYGAKCiMjIFRlc3RzIGZvciBEYXRhZnJhbWVzClRMRFIsIGBpcy5kYXRhLmZyYW1lKC4uLilgOgpgYGB7cn0KIyBDaGVja3MgaWYgJ3BsYW5ldHNfZGYnIGlzIGEgZGF0YWZyYW1lCmlzLmRhdGEuZnJhbWUocGxhbmV0c19kZikKYGBgCllvdSBjYW4gYWxzbyBjb2VyY2Ugb3RoZXIgb2JqZWN0cyBpbnRvIGRhdGFmcmFtZXM6CmBgYHtyfQojIENvZXJjZXMgbWF0cml4IHRvIGRhdGFmcmFtZQphcy5kYXRhLmZyYW1lKG1hdHJpeCgxOjEwLCBucm93ID0gNSwgbmNvbCA9IDIsIGRpbW5hbWVzID0gbGlzdChOVUxMLCBjKCJYIiwiWSIpKSkpCmBgYAoKCiMgUHJhY3RpY2UhIFByYWN0aWNlISBQcmFjdGljZSEgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyBBY2Nlc3MgYnkgSW5kaWNlcwpXaGF0J3MgdGhlIGRpYW1ldGVyIG9mIE1lcmN1cnk/CmBgYHtyfQojIFByaW50IG91dCBkaWFtZXRlciBvZiBNZXJjdXJ5IChyb3cgMSwgY29sdW1uIDMpCnBsYW5ldHNfZGZbMSwzXQpgYGAKU2hvdyBtZSBhbGwgdGhlIGRhdGEgZm9yIE1hcnMhCmBgYHtyfQojIFByaW50IG91dCBkYXRhIGZvciBNYXJzIChlbnRpcmUgZm91cnRoIHJvdykKcGxhbmV0c19kZls0LCBdCmBgYAoKIyMgQWNjZXNzIGJ5IG5hbWVzCldoYXQgYXJlIHRoZSBkaWFtZXRlcnMgb2YgYWxsIHBsYW5ldHM/CmBgYHtyfQojIHBsYW5ldHNfZGZbWydkaWFtZXRlcnMnXV0gaXMgYWxzbyB2YWxpZApwbGFuZXRzX2RmJGRpYW1ldGVyCmBgYApXaGF0IHR5cGUgb2YgcGxhbmV0cyBhcmUgdGhlIGZpcnN0IDMgcGxhbmV0cz8KYGBge3J9CiMgcGxhbmV0c19kZlsxOjMsInR5cGUiXSBpcyBhbHNvIHZhbGlkCnBsYW5ldHNfZGYkdHlwZVsxOjNdCmBgYAoKIyMgQWNjZXNzIGJ5IGxvZ2ljYWwgdmVjdG9ycwpXaGljaCBwbGFuZXRzIGhhdmUgcG9zaXRpdmUgcm90YXRpb24/CmBgYHtyfQojIHBsYW5ldHNfZGZbLCdyb3RhdGlvbiddID4gMCBpcyBhbHNvIHZhbGlkCnBvc2l0aXZlX3JvdGF0aW9uIDwtIHBsYW5ldHNfZGYkcm90YXRpb24gPiAwCiMgUGxhbnRlcyB3aXRoIHBvc2l0aXZlIHJvdGF0aW9uCnBsYW5ldHNfZGZbcG9zaXRpdmVfcm90YXRpb24sXQpgYGAKTmFtZXMgb2YgdGhlIHBsYW5ldHMgdGhhdCBoYXZlIHBvc2l0aXZlIHJvdGF0aW9uIGFuZCByaW5nczoKYGBge3J9CiMgR2l2ZSB5b3Vyc2VsZiBhIGNvb2tpZSBpZiB5b3UgZ290IHRoaXMgcmlnaHQhISEKcGxhbmV0c19kZltwbGFuZXRzX2RmJHJvdGF0aW9uID4gMCAmIHBsYW5ldHNfZGYkcmluZ3MsICduYW1lJ10KYGBgCgoKIyBMaXN0cyB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIENyZWF0aW5nIExpc3RzCkxpc3RzLCBhcyBvcHBvc2VkIHRvIHZlY3RvcnMsIGNhbiBob2xkIGNvbXBvbmVudHMgb2YgZGlmZmVyZW50IHR5cGVzLiBUaGVzZSBvYmplY3RzIGNhbiBiZSBtYXRyaWNlcywgdmVjdG9ycywgZGF0YSBmcmFtZXMsIGV2ZW4gb3RoZXIgbGlzdHMsIGV0Yy4gSXQgaXMgbm90IGV2ZW4gcmVxdWlyZWQgdGhhdCB0aGVzZSBvYmplY3RzIGFyZSByZWxhdGVkIHRvIGVhY2ggb3RoZXIgaW4gYW55IHdheS4KYGBge3J9CiMgQ3JlYXRlIHNvbWUgdmVjdG9ycwpoZXJvZXMgPC0gYygnSXJvbm1hbicsICdUaG9yJywgJ0NhcHRhaW4gQW1lcmljYScsICdXb25kZXIgV29tYW4nLCAnQmF0bWFuJykKYWN0b3JzIDwtIGMoJ1NpciBSb2JlcnQgRG93bmV5IEpyJywgJ0NocmlzIEhlbXN3b3J0aCcsICdDaHJpcyBFdmFucycsICdHYWwgR2Fkb3QnLCAnQ2hyaXN0aWFuIEJhbGUnKQpsZXZlbCA8LSBjKDEwMCwgMTAwMCwgMTAsIDEwMDAwLCAxMDAwMDApCiMgSW5pdGlhbGlzZSB0aGUgbGlzdApzdXBlcl9oZXJvZXMgPC0gbGlzdChwb3dlcl9sZXZlbCA9IGRhdGEuZnJhbWUoaGVyb2VzLGxldmVsKSwKICAgICAgICAgICAgICAgICAgICAgc2VyaWFsX25vID0gMTo1LCAKICAgICAgICAgICAgICAgICAgICAgYWN0b3JfbmFtZXMgPSBhY3RvcnMpCnN1cGVyX2hlcm9lcwpgYGAKCiMjIEFjY2VzcyBmb3IgbGlzdHMKV2UgY2FuIGFjY2VzcyB0aGUgZWxlbWVudHMgaW4gYSBsaXN0IHRocm91Z2ggaW5kZXhpbmcgdGhyb3VnaCBkb3VibGUgc3F1YXJlZCBicmFja2V0cyBgW1suLi5dXWA6CmBgYHtyfQojIERpc3BsYXlzIHRoZSBzZWNvbmQgZWxlbWVudCBpbiB0aGUgbGlzdApzdXBlcl9oZXJvZXNbWzJdXQpgYGAKVGhlIGxpc3QgY2FuIGJlIGFjY2Vzc2VkIHRocm91Z2ggdGhlIG5hbWVkIGVsZW1lbnRzIGFzIHdlbGwuIExldCdzIHVzZSBgbmFtZXMoLi4uKWAgdG8gc2VlIHRoZSBuYW1lZCBlbGVtZW50cyBvZiB0aGUgbGlzdDoKYGBge3J9CiMgTmFtZWQgZWxlbWVudHMgb2YgdGhlIGxpc3QKbmFtZXMoc3VwZXJfaGVyb2VzKQpgYGAKVGhlcmUgYXJlIHR3byBmdW5kYW1lbnRhbCB3YXlzIHRvIGFjY2VzcyBhIGxpc3QgdGhyb3VnaCBuYW1lcywgaGVyZSB3ZSB1c2UgdGhlIGVsZW1lbnQgbmFtZToKYGBge3J9CiMgQWNjZXNzIHVzaW5nIGVsZW1lbnQgbmFtZXMKc3VwZXJfaGVyb2VzW1sic2VyaWFsX25vIl1dCmBgYApBbmQgaGVyZSB3ZSBpbnZva2UgdGhlIGF0b21pYyB2ZWN0b3IKYGBge3J9CiMgQWNjZXNzIHdpdGggYW4gYXRvbWljIHZlY3RvcgpzdXBlcl9oZXJvZXMkcG93ZXJfbGV2ZWwKYGBgCgojIyBUZXN0IGZvciBsaXN0ClRoZSBmdW5jdGlvbiBgaXMubGlzdCguLi4pYCB0ZXN0cyBmb3IgbGlzdHMgYW5kIHJldHVybnMgYSBib29sZWFuIHZhbHVlOgpgYGB7cn0KIyBSZXR1cm5zIGEgYm9vbGVhbiB2YWx1ZQppcy5saXN0KHN1cGVyX2hlcm9lcykKYGBgCk9uZSBjYW4gYWxzbyBjb2VyY2UgYW4gb2JqZWN0IGludG8gYSBsaXN0IHVzaW5nIHRoZSBmdW5jdGlvbiBgYXMubGlzdCguLi4pYDoKYGBge3J9CiMgQ3JlYXRlIGEgZGF0YWZyYW1lClRoZV9TaW1wc29ucyA8LSBkYXRhLmZyYW1lKGZpcnN0X25hbWUgPSBjKCJCYXJ0IiwiTGlzYSIsIkhvbWVyIiwiTWFyZ2UiLCJCYWJ5IiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYXN0X25hbWUgPSByZXAoIlNpbXBzb24iLCA1KSkKVGhlX1NpbXBzb25zCiMgQ29lcmNpbmcgYSBkYXRhZnJhbWUgaW50byBhIGxpc3QKYXMubGlzdChUaGVfU2ltcHNvbnMpCmBgYApCdXQgbW9yZSBvZnRlbiB3ZSB3aWxsIHVzZSBgc3RyKC4uLilgIHRvIGRlc2NyaWJlIG9iamVjdHMgbGlrZSBsaXN0czoKYGBge3J9CiMgRGlzcGxheXMgdGhlIGVudGlyZSBzdHJ1Y3R1cmUgb2YgdGhlIGxpc3QKc3RyKHN1cGVyX2hlcm9lcykKYGBgCg==