1 Introduction

Data visualization is a form of data analysis (also called visual analytics). This means we need to prepare data sets that are appropriate for visualizations. Recall the following work flow of data visualizations mentioned in earlier notes.

include_graphics("img/Workflow.png")
Data visualization work-flow.

Data visualization work-flow.

The major data management tasks are data aggregation and extraction.

  • Information Aggregation - combining information in different relational data sets to make an integrated single data set for data visualization.

  • Information Extraction - subsetting a single data set to make small data sets that have specific information for creating a visualization.

The base R package has some powerful and easy-to-use functions to perform these types of data management.

2 Data Cleaning and Preparation for Visualization

2.1 Data Cleaning

Data cleaning refers to the process of making a data set possibly from different sources of raw data for modeling, visualization, and relevant analysis. Before starting cleaning, we need to create a backup plan of the original data (master data files) since things can go wrong easily the data processing stage. It’s important to create and stick to a plan for backing up your data.

  • Store a copy of your master files in a separate location from your working files
  • Set up a regular backup schedule
    • What files will be backed up?
    • How frequently will the files be backed up?

All data cleaning tasks must be performed based on the working data files. The major tasks vary from projects to projects, some of the common tasks are listed in the following:

  • Removing unnecessary variables
    • observation ID
    • Irrelevant variables
  • Deleting duplicate rows/observations
    • Caution: Never do this in bootstrap samples!
  • Addressing outliers or invalid data
    • Genuine observation?
    • mistakenly recorded data points?
  • Dealing with missing values
    • Missing value - the exists but for was not recorded
    • Null value - the value does not exist
  • Standardizing or categorizing values
    • Caution with standardizing!
    • Combining sparse categories in a meaningful way.
    • Discrtizing continuous variables.
  • Correcting typographical errors
  • Data Augmentation or Extension
    • Adding new columns to the existing dataset
    • Combining existing dataset with another dataset

2.2 Data Preparation for Visualization

For a specific data analysis such as modeling or data visualization, we need to create an analytic data set based on clean data sets.

Formatting/Conversion

  • Formatting columns appropriately (numbers are treated as numbers, dates as dates)
  • Convert values into appropriate units

Filtering/Subsetting

  • Filter your data to focus on the specific data that interests you.
  • Group data and create aggregate values for groups (Counts, Min, Max, Mean, Median, Mode)
  • Extract values from complex columns

Aggregation/Merging

  • Combine variables to create new columns
  • Merge different relational data sets

3 Basic Data Management: Merging Data Sets

There are different packages in R that have various functions capable of doing data management. In this note, we introduce the commonly used functions in base R and tidyverse.

3.1 Merge Data Sets

It is very common that the information we are interested in resides in different data sources. In order to merge different data sets, there must be at least one variable “key” that links to different data sets.

There are several different operations in SQL to create different types of the merged data set. The following are the most commonly used ones.

include_graphics("img/joins.png")
Inner Join.

Inner Join.

The next figure shows the basic operations with tiny tables illustrating the operations.

include_graphics("img/JoinExamples.png")
Illustration of inner join.

Illustration of inner join.

3.2 Merging Data in Base R

The base R function merge() can be used to perform different joins. To illustrate, we use the tiny toy data set in the above figure to show you how to use merge() function.

  • Defining Data Frames
employee = data.frame(EmpID = c(1,2,3), 
                      EmpName = c("Rajendra", "Kusum", "Akshita"))
city = data.frame(ID = c(1,2,7,8), 
                  City =c("Jaipur", "Delhi", "Raipur", "Bangalore"))
  • Inner Join
innerjoin = merge(x=employee, city, by.x = 'EmpID', by.y ='ID', all = FALSE)
innerjoin
  EmpID  EmpName   City
1     1 Rajendra Jaipur
2     2    Kusum  Delhi
  • Outer Join
innerjoin = merge(x = employee, y = city, by.x = 'EmpID', by.y ='ID', all = TRUE)
innerjoin
  EmpID  EmpName      City
1     1 Rajendra    Jaipur
2     2    Kusum     Delhi
3     3  Akshita      <NA>
4     7     <NA>    Raipur
5     8     <NA> Bangalore
  • Left Join
leftjoin = merge(x = employee, y = city, by.x = 'EmpID', by.y ='ID', all.x = TRUE)
leftjoin 
  EmpID  EmpName   City
1     1 Rajendra Jaipur
2     2    Kusum  Delhi
3     3  Akshita   <NA>
  • Right Join
rightjoin = merge(x = employee, y = city, by.x = 'EmpID', by.y ='ID', all.y = TRUE)
rightjoin
  EmpID  EmpName      City
1     1 Rajendra    Jaipur
2     2    Kusum     Delhi
3     7     <NA>    Raipur
4     8     <NA> Bangalore

3.3 Merging Data with Mutating Joins in dplyr

The package dplyr has the following four join functions corresponding to the options in the base R function ’merge()`.

The mutating joins add columns from y to x, matching rows based on the keys:

  • inner_join(): includes all rows in x and y.

  • left_join(): includes all rows in x.

  • right_join(): includes all rows in y.

  • full_join() (also called outer join): includes all rows in x or y (also called outer join)..

If a row in x matches multiple rows in y, all the rows in y will be returned once for each matching row in x.

To use mutating joins, we first rename key variables so that primary keys have the same name.

For large tables dplyr join functions is much faster than merge(). The advantages of using dplyr package for merging dataframes are:

  1. They are much faster.

  2. Shows the keys explicitly when merging data.

  3. They are flexible and work with database tables.

The following are simple illustrative examples.

employee.new = employee
employee.new$ID = employee$EmpID  # adding the new renamed ID
employee.new = employee.new[, -1]     # drop the old ID variable
employee.new
   EmpName ID
1 Rajendra  1
2    Kusum  2
3  Akshita  3
  • Inner Join
inner_join(employee.new, city, by = "ID")
   EmpName ID   City
1 Rajendra  1 Jaipur
2    Kusum  2  Delhi
  • Left Join
left_join(employee.new, city, by = "ID")
   EmpName ID   City
1 Rajendra  1 Jaipur
2    Kusum  2  Delhi
3  Akshita  3   <NA>
  • right Join
right_join(employee.new, city, by = "ID")
   EmpName ID      City
1 Rajendra  1    Jaipur
2    Kusum  2     Delhi
3     <NA>  7    Raipur
4     <NA>  8 Bangalore
  • Full (Outer) Join
full_join(employee.new, city, by = "ID")
   EmpName ID      City
1 Rajendra  1    Jaipur
2    Kusum  2     Delhi
3  Akshita  3      <NA>
4     <NA>  7    Raipur
5     <NA>  8 Bangalore

3.4 Use of Pipe Operator %>% with Mutating Joins

The pipe operator, written as %>% takes the output of one function and passes it into another function as an argument. This allows us to link a sequence of analysis steps using functions in dplyr and tidyr in data wrangling.

  • Inner Join
pipe.innerjoin <- employee %>% inner_join(city, by = c("EmpID" = "ID"))
pipe.innerjoin
  EmpID  EmpName   City
1     1 Rajendra Jaipur
2     2    Kusum  Delhi
  • Full (Outer) Join
pipe.outerjoin <- employee %>% full_join(city, by = c("EmpID" = "ID"))
pipe.outerjoin
  EmpID  EmpName      City
1     1 Rajendra    Jaipur
2     2    Kusum     Delhi
3     3  Akshita      <NA>
4     7     <NA>    Raipur
5     8     <NA> Bangalore
  • Left Join
pipe.leftjoin <- employee %>% left_join(city, by = c("EmpID" = "ID"))
pipe.leftjoin
  EmpID  EmpName   City
1     1 Rajendra Jaipur
2     2    Kusum  Delhi
3     3  Akshita   <NA>
  • Right Join
pipe.rightjoin <- employee %>% right_join(city, by = c("EmpID" = "ID"))
pipe.rightjoin
  EmpID  EmpName      City
1     1 Rajendra    Jaipur
2     2    Kusum     Delhi
3     7     <NA>    Raipur
4     8     <NA> Bangalore

4 Basic Data Management: Subsetting Data

Another important data management task is to subset data sets to extract the desired information for analyses and visualization.

Two operations are used to subset a data set: select/drop columns and select rows that meet certain conditions.

The working data set in the section is the well-known iris data set that has 4 numerical variables (attributes of iris flowers) and a categorical variable (species of iris flowers).

4.1 Accessors in R [, [[ and $

When subsetting a data set, it is unavoidable to access the value(s) of certain variable(s). Three R accessors are commonly used in R coding.

  • [ subsetting a data set

This R accessor is probably the most commonly used. When we want a subset of an object using [. Remember that when we take a subset of the object you get the same type of thing. Thus, the subset of a vector will be a vector, the subset of a list will be a list and the subset of a data.frame will be a data.frame.

  • [[ extracting one item

The double square brackets are used to extract one element from potentially many. For vectors yield vectors with a single value; data frames give a column vector; for a list, one element:

For example,

letters[[3]]            # extracts the third element in the vector of all lower case letters
[1] "c"
iris[["Petal.Length"]]  # extract the variable named 'Petal.Length' in the data frame.
  [1] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 1.5 1.6 1.4 1.1 1.2 1.5 1.3 1.4
 [19] 1.7 1.5 1.7 1.5 1.0 1.7 1.9 1.6 1.6 1.5 1.4 1.6 1.6 1.5 1.5 1.4 1.5 1.2
 [37] 1.3 1.4 1.3 1.5 1.3 1.3 1.3 1.6 1.9 1.4 1.6 1.4 1.5 1.4 4.7 4.5 4.9 4.0
 [55] 4.6 4.5 4.7 3.3 4.6 3.9 3.5 4.2 4.0 4.7 3.6 4.4 4.5 4.1 4.5 3.9 4.8 4.0
 [73] 4.9 4.7 4.3 4.4 4.8 5.0 4.5 3.5 3.8 3.7 3.9 5.1 4.5 4.5 4.7 4.4 4.1 4.0
 [91] 4.4 4.6 4.0 3.3 4.2 4.2 4.2 4.3 3.0 4.1 6.0 5.1 5.9 5.6 5.8 6.6 4.5 6.3
[109] 5.8 6.1 5.1 5.3 5.5 5.0 5.1 5.3 5.5 6.7 6.9 5.0 5.7 4.9 6.7 4.9 5.7 6.0
[127] 4.8 4.9 5.6 5.8 6.1 6.4 5.6 5.1 5.6 6.1 5.6 5.5 4.8 5.4 5.6 5.1 5.1 5.9
[145] 5.7 5.2 5.0 5.2 5.4 5.1

The double square bracket looks as if we are asking for something deep within a container. We are not taking a slice but reaching to get at the one thing at the core.

  • Interact with $

The accessor that provides the least unique utility is also probably used the most often used. $ is a special case of [[ in which we access a single item by actual name. The following are equivalent:

iris$Petal.Length
  [1] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 1.5 1.6 1.4 1.1 1.2 1.5 1.3 1.4
 [19] 1.7 1.5 1.7 1.5 1.0 1.7 1.9 1.6 1.6 1.5 1.4 1.6 1.6 1.5 1.5 1.4 1.5 1.2
 [37] 1.3 1.4 1.3 1.5 1.3 1.3 1.3 1.6 1.9 1.4 1.6 1.4 1.5 1.4 4.7 4.5 4.9 4.0
 [55] 4.6 4.5 4.7 3.3 4.6 3.9 3.5 4.2 4.0 4.7 3.6 4.4 4.5 4.1 4.5 3.9 4.8 4.0
 [73] 4.9 4.7 4.3 4.4 4.8 5.0 4.5 3.5 3.8 3.7 3.9 5.1 4.5 4.5 4.7 4.4 4.1 4.0
 [91] 4.4 4.6 4.0 3.3 4.2 4.2 4.2 4.3 3.0 4.1 6.0 5.1 5.9 5.6 5.8 6.6 4.5 6.3
[109] 5.8 6.1 5.1 5.3 5.5 5.0 5.1 5.3 5.5 6.7 6.9 5.0 5.7 4.9 6.7 4.9 5.7 6.0
[127] 4.8 4.9 5.6 5.8 6.1 6.4 5.6 5.1 5.6 6.1 5.6 5.5 4.8 5.4 5.6 5.1 5.1 5.9
[145] 5.7 5.2 5.0 5.2 5.4 5.1
iris[["Petal.Length"]]
  [1] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 1.5 1.6 1.4 1.1 1.2 1.5 1.3 1.4
 [19] 1.7 1.5 1.7 1.5 1.0 1.7 1.9 1.6 1.6 1.5 1.4 1.6 1.6 1.5 1.5 1.4 1.5 1.2
 [37] 1.3 1.4 1.3 1.5 1.3 1.3 1.3 1.6 1.9 1.4 1.6 1.4 1.5 1.4 4.7 4.5 4.9 4.0
 [55] 4.6 4.5 4.7 3.3 4.6 3.9 3.5 4.2 4.0 4.7 3.6 4.4 4.5 4.1 4.5 3.9 4.8 4.0
 [73] 4.9 4.7 4.3 4.4 4.8 5.0 4.5 3.5 3.8 3.7 3.9 5.1 4.5 4.5 4.7 4.4 4.1 4.0
 [91] 4.4 4.6 4.0 3.3 4.2 4.2 4.2 4.3 3.0 4.1 6.0 5.1 5.9 5.6 5.8 6.6 4.5 6.3
[109] 5.8 6.1 5.1 5.3 5.5 5.0 5.1 5.3 5.5 6.7 6.9 5.0 5.7 4.9 6.7 4.9 5.7 6.0
[127] 4.8 4.9 5.6 5.8 6.1 6.4 5.6 5.1 5.6 6.1 5.6 5.5 4.8 5.4 5.6 5.1 5.1 5.9
[145] 5.7 5.2 5.0 5.2 5.4 5.1

4.2 Subsetting Data in Base R

Selecting/Dropping Columns

Subsetting a data set by selecting or dropping a subset of variables (columns) from a data set is straightforward.

For example, we can define a subset of the iris data set by selecting all numerical variables.

iris.names = names(iris)
iris0 = iris[, iris.names[1:4]]
DT::datatable(iris0, fillContainer = FALSE, options = list(pageLength = 10))

We can also create the same data set by dropping variables in the original data set. For example

iris02 = iris[, -5]
DT::datatable(iris02, fillContainer = FALSE, options = list(pageLength = 10))

Selection/Dropping Rows

This is also relatively straightforward. The basic idea is to identify row IDs to select or drop the corresponding rows. The R function which() can this trick!

The following example illustrates the way of using which() to subsetting data.

  1. Selecting One Species of Iris Flowers
setosa.id = which(iris$Species == "setosa")
setosa.flower = iris[setosa.id,]
DT::datatable(setosa.flower, fillContainer = FALSE, options = list(pageLength = 10))
  1. Selecting Two Species of Iris Flowers

The following three code chunks create the same data set.

Method 1:

not.setosa.id01 = which(iris$Species != "setosa")
not.setosa.flower01 = iris[not.setosa.id01,]
DT::datatable(not.setosa.flower01, fillContainer = FALSE, options = list(pageLength = 10))

Method 2:

not.setosa.id02 = which(iris$Species == "virginica" | iris$Species =="versicolor")
not.setosa.flower02 = iris[not.setosa.id02,]
DT::datatable(not.setosa.flower02, fillContainer = FALSE, options = list(pageLength = 10))

Method 3:

not.setosa.id03 = which(iris$Species %in% c("versicolor", "virginica"))
not.setosa.flower03 = iris[not.setosa.id01,]
DT::datatable(not.setosa.flower03, fillContainer = FALSE, options = list(pageLength = 10))

4.3 Subsetting Data with dplyr

dplyr provides helper tools for the most common data manipulation tasks. It is built to work directly with data frames and has the ability to work directly with data stored in an external database. We can conduct queries on the database directly and pull back into R only what we need for analysis.

Since selecting/dropping variables is straightforward (particularly when using %>%). Next, we provide a few examples showing how to use filter() to select/drop rows with certain conditions.

  • Filtering by one criterion
setosa = filter(iris, Species == "setosa")
DT::datatable(setosa, fillContainer = FALSE, options = list(pageLength = 10))
iris.ge.6 = filter(iris, Sepal.Length > 6)
DT::datatable(iris.ge.6, fillContainer = FALSE, options = list(pageLength = 10))
  • When multiple expressions are used, they are combined using & (logical AND) or | (logical OR)
setosa.ge5 = filter(iris, Species == "setosa" & Sepal.Length > 5 )
DT::datatable(setosa.ge5, fillContainer = FALSE, options = list(pageLength = 10))
setosa.ge7 = filter(iris, Species == "setosa" | Sepal.Length > 7 )
DT::datatable(setosa.ge7, fillContainer = FALSE, options = list(pageLength = 10))
  • To refer to column names that are stored as strings, use the .data pronoun:
vars <- c("Sepal.Length", "Petal.Length")
cond <- c(6, 5)
subset.iris <- iris %>%
  filter(
    .data[[vars[[1]]]] > cond[[1]],
    .data[[vars[[2]]]] < cond[[2]]
  )
#subset.iris
DT::datatable(subset.iris, fillContainer = FALSE, options = list(pageLength = 10))

4.4 Variable Definition and Variable Type Conversion

  • Define New Variables

Defining new variables based on the existing variables is straightforward in R using the basic arithmetic and mathematical operations. When using %>%, dplyr() is used to define new variables.

  • Variable Type Conversion

Type conversions in R work as you would expect. For example, adding a character string to a numeric vector converts all the elements in the vector to the character.

  1. Use is.foo to test for data type foo. Returns TRUE or FALSE

is.numeric(), is.character(), is.vector(), is.matrix(), is.data.frame()

  1. Use as.foo to explicitly convert it.

as.numeric(), as.character(), as.vector(), as.matrix(), as.data.frame)

5 Importing/Exporting Data

5.1 Importing Dara

There are different functions in various R libraries to read data to R.

  • Base R and Libraries Come with Base R

R loading functions in {utils}: read.table(), read.csv(), read.csv2(), read.delim(), and read.delim2()

  • Functions in {tidyverse}

As a part of {tidyverse}, the library {readr} has several functions to read the data in common formats.

read_table(), read_delim(), read_csv(), read_csv2(), read_tsv()

  • Read data set generated by other programs such as SAS, SPSS, etc.

Several libraries are useful to load special formats of data to R. Three important libraries are

{xlsx, Hmisc, foreign}.

5.2 Exporting Data

We have learned how to use dplyr to extract information from or summarize your raw data, we may want to export these new data sets to share them with other people or for archival.

Similar to the read_csv() function used for reading CSV files into R, there is a write_csv() function that generates CSV files from data frames.

Let’s assume our data set under the name, final_data, is ready, we can save it as a CSV file in our data folder using the following code.

write_csv(final_data, file = "data/final_data.csv")

6 Overview of Tidyverse

There are several R libraries that have powerful tools for data wrangling and information extraction. Tidyverse is a collection of essential R packages for data science. There 8 packages under the tidyverse umbrella that help us in performing and interacting with the data.

6.1 Packages for Data Wrangling and Transformation

  • dplyr provides helper tools for the most common data manipulation tasks. It is built to work directly with data frames and has the ability to work directly with data stored in an external database. We can conduct queries on the database directly, and pull back into R only what we need for analysis.

  • tidyr addresses the common problem of wanting to reshape the data with a sophisticated layout for plotting and usage by different R functions.

  • stringr deals with string variables. It plays a big role in processing raw data into a cleaner and easily understandable format.

  • forcats is dedicated to dealing with categorical variables or factors. Anyone who has worked with categorical data knows what a nightmare they can be.

6.2 Packages for Data Import and Management

  • tibble is a new modern data frame with nicer behavior around printing, subsetting, and factor handling. It keeps many important features of the original data frame and removes many of the outdated features.

  • readr package is recently developed to deal with reading in large flat files quickly. The package provides replacements for functions like read.table() and read.csv(). The analogous functions in {readr} are read_table() and read_csv().

6.3 Functional Programming with Library {purrr}

  • purrr is a new package that fills in the missing pieces in R’s functional programming tools. This is not a coding class. We will not use ‘purrr’ in this class.

6.4 Data Visualization and Exploration

ggplot2 is a powerful and flexible R package for producing elegant graphics. The concept behind ggplot2 divides plot into three different fundamental parts: Plot = data + Aesthetics + Geometry.

The principal components of every plot can be defined as follow:

  • Aesthetics is used to indicate x and y variables. It can also be used to control the color, the size or the shape of points, the height of bars, etc.

  • Geometry defines the type of graphics (histogram, box plot, line plot, density plot, dot plot, etc.)

This will be one of the primary tools for this class.

7 Data Management with dplyr (Optional)

What we can do in the standard SQL can also be done with dyplr. For the convenience of illustration, we use a simple well-known built-in iris data set.

7.1 Common dplyr Functions

The following is the list of functions in dplyr.

  • select(): sub-setting columns.

To select columns of a data frame, use select(). The first argument to this function is the data frame (iris), and the subsequent arguments are the columns to keep. For example

iris.petal = select(iris, Petal.Length, Petal.Width, Species)
str(iris.petal)
'data.frame':   150 obs. of  3 variables:
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

To select all columns except certain ones, put a “-” in front of the variable to exclude it. For example, we exclude Petal information and only keep Sepal information, we can use the following code

iris.sepal = select(iris, -Petal.Length, -Petal.Width)
str(iris.sepal)
'data.frame':   150 obs. of  3 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

This will select all the variables in surveys except for Petal.Length, and Petal.Width.

  • filter(): sub-setting rows on conditions.

For example, if we only select one species Versicolor, we can use the following code.

versicolor = filter(iris, Species =="versicolor")
summary(versicolor)
  Sepal.Length    Sepal.Width     Petal.Length   Petal.Width          Species  
 Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000   setosa    : 0  
 1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200   versicolor:50  
 Median :5.900   Median :2.800   Median :4.35   Median :1.300   virginica : 0  
 Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326                  
 3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500                  
 Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800                  

If we subset a data set by selecting a certain number of columns and row with multiple conditions, pipe operator %>% will make subsetting easy. For example, if we only want to study sepal width and length of setosa where petal length is less than 1.5. The following code using %>%

resulting.subset <- iris %>%
             filter(Petal.Length < 1.5, Species =="setosa") %>%
             select(Sepal.Length, Sepal.Width, Species)
summary(resulting.subset)
  Sepal.Length    Sepal.Width          Species  
 Min.   :4.300   Min.   :2.300   setosa    :24  
 1st Qu.:4.600   1st Qu.:3.000   versicolor: 0  
 Median :4.900   Median :3.350   virginica : 0  
 Mean   :4.896   Mean   :3.333                  
 3rd Qu.:5.100   3rd Qu.:3.525                  
 Max.   :5.800   Max.   :4.200                  

Note that, multiple conditional statements are separated by , or &. Using %>%, we don’t need to include the data set as the first argument.

  • mutate(): creating new columns by using information from other columns.

Frequently we want to create new columns based on the values in existing columns. For example, we want to define two ratios of the sepal and petal widths and sepal and petal lengths. For this, we’ll use mutate().

expanded.data <- iris %>%
             mutate(length.ratio = Sepal.Length/Petal.Length,
                    width.ratio = Sepal.Width/Petal.Width)
summary(expanded.data)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
       Species    length.ratio    width.ratio    
 setosa    :50   Min.   :1.050   Min.   : 1.130  
 versicolor:50   1st Qu.:1.230   1st Qu.: 1.603  
 virginica :50   Median :1.411   Median : 2.148  
                 Mean   :2.018   Mean   : 6.628  
                 3rd Qu.:3.176   3rd Qu.:11.583  
                 Max.   :4.833   Max.   :41.000  
  • group_by() and summarize(): creating summary statistics on grouped data.

‘group_by()’ is often used together with ‘summarize()’, which collapses each group into a single-row summary of that group. ‘group_by()’ takes as arguments the column names that contain the categorical variables for which you want to calculate the summary statistics.

The following code yields a set of summarized statistics including the mean of sepal width and length as well as the correlation coefficients in each of the three species.

summary.stats <- iris %>%
             group_by(Species) %>%
             summarize(sepal.width.avg = mean(Sepal.Width),
                       sepal.length.avg = mean(Sepal.Length),
                       corr.sepal = cor(Sepal.Length, Sepal.Width)) 
summary.stats
# A tibble: 3 × 4
  Species    sepal.width.avg sepal.length.avg corr.sepal
  <fct>                <dbl>            <dbl>      <dbl>
1 setosa                3.43             5.01      0.743
2 versicolor            2.77             5.94      0.526
3 virginica             2.97             6.59      0.457

All R functions such as min(), max(), that yield summarized statistics can be used with summarize(). We can also filter out some observations before we compute the summary statistics.

summary.stats.filtering <- iris %>%
             filter(Petal.Length < 5) %>%
             group_by(Species) %>%
             summarize(sepal.width.avg = mean(Sepal.Width),
                       sepal.length.avg = mean(Sepal.Length),
                       corr.sepal = cor(Sepal.Length, Sepal.Width)) 
summary.stats.filtering
# A tibble: 3 × 4
  Species    sepal.width.avg sepal.length.avg corr.sepal
  <fct>                <dbl>            <dbl>      <dbl>
1 setosa                3.43             5.01      0.743
2 versicolor            2.77             5.92      0.519
3 virginica             2.8              5.85      0.643
  • arrange(): sorting results.

To sort in descending order, we need to add the desc() function. If we want to sort the results by decreasing the order of mean weight.

summary.stats.sort <- iris %>%
             filter(Petal.Length < 5) %>%
             group_by(Species) %>%
             summarize(sepal.width.avg = mean(Sepal.Width),
                       sepal.length.avg = mean(Sepal.Length),
                       corr.sepal = cor(Sepal.Length, Sepal.Width)) %>%
              arrange(desc(corr.sepal))
summary.stats.sort
# A tibble: 3 × 4
  Species    sepal.width.avg sepal.length.avg corr.sepal
  <fct>                <dbl>            <dbl>      <dbl>
1 setosa                3.43             5.01      0.743
2 virginica             2.8              5.85      0.643
3 versicolor            2.77             5.92      0.519

The resulting data set can also be sorted by multiple variables.

  • count(): counting discrete values.

When working with data, we often want to know the number of observations found for each factor or combination of factors. For this task, dplyr provides count(). For example, if we wanted to count the number of rows of data for each species after we filter out all records with Petal Length < 5, we would do:

summary.count <- iris %>%
             filter(Petal.Length < 5) %>%
             group_by(Species) %>%
             summarize(count = n(), sort = TRUE) 
summary.count
# A tibble: 3 × 3
  Species    count sort 
  <fct>      <int> <lgl>
1 setosa        50 TRUE 
2 versicolor    48 TRUE 
3 virginica      6 TRUE 

If we wanted to count a combination of factors, say factor A and factor B, we would specify the first and the second factor as the arguments of count(factor A, factor B).

7.2 Reshaping Functions in tidyr

The tidyr package complements dplyr perfectly. It boosts the power of dplyr for data manipulation and pre-processing. To illustrate how to use these functions, we consider defining a subset from iris that contains only two variables: Sepal Length and Species.

life.expectancy <-read.csv("life_expectancy_years.csv")
life.expectancy[1:5, 1:10]
               country X1799 X1800 X1801 X1802 X1803 X1804 X1805 X1806 X1807
1          Afghanistan  28.2  28.2  28.2  28.2  28.2  28.2  28.1  28.1  28.1
2               Angola  27.0  27.0  27.0  27.0  27.0  27.0  27.0  27.0  27.0
3              Albania  35.4  35.4  35.4  35.4  35.4  35.4  35.4  35.4  35.4
4              Andorra    NA    NA    NA    NA    NA    NA    NA    NA    NA
5 United Arab Emirates  30.7  30.7  30.7  30.7  30.7  30.7  30.7  30.7  30.7

sub.iris is called a long table. We can reshape this long table to a wide table using spread() function.

  • gather(): The function “gathers” multiple columns from the data set and converts them into key-value pairs. gather() takes four principal arguments:

    • data set
    • the key column variable we wish to create from column names.
    • the values column variable we wish to create and fill with values associated with the key.

The names of the columns we use to fill the key variable (or to drop).

Here we exclude country from being gather()ed.

life.expectancy.long <- life.expectancy %>%
  gather(key = "Year",       # the column names of the wide table
         value = "lifeExp",  # the numerical values of the table
         - country,          # drop country variable: its value will not be gathered (stacked)!
         na.rm = TRUE)       # removing records with missing values
##
head(life.expectancy.long)
               country  Year lifeExp
1          Afghanistan X1799    28.2
2               Angola X1799    27.0
3              Albania X1799    35.4
5 United Arab Emirates X1799    30.7
6            Argentina X1799    33.2
7              Armenia X1799    34.0

We can use substr() to remove X from the variable Year as shown in the following code.

correct.life.exp.data <- life.expectancy.long %>%
                      mutate(year = substr(Year,2,5)) %>%
                      select(-Year)
head(correct.life.exp.data)
               country lifeExp year
1          Afghanistan    28.2 1799
2               Angola    27.0 1799
3              Albania    35.4 1799
5 United Arab Emirates    30.7 1799
6            Argentina    33.2 1799
7              Armenia    34.0 1799

For illustrative purposes, we look at a small subset of the iris data set.

 mini.iris <- iris[c(1, 51, 101), ]
 mini.iris
    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1            5.1         3.5          1.4         0.2     setosa
51           7.0         3.2          4.7         1.4 versicolor
101          6.3         3.3          6.0         2.5  virginica

We list (select) the columns to be stacked explicitly as arguments of gather() in the following code.

mini.iris.w2l <- mini.iris %>%
          gather(key = "flower.att", value = "measurement",
           Sepal.Length, Sepal.Width, Petal.Length, Petal.Width)
mini.iris.w2l
      Species   flower.att measurement
1      setosa Sepal.Length         5.1
2  versicolor Sepal.Length         7.0
3   virginica Sepal.Length         6.3
4      setosa  Sepal.Width         3.5
5  versicolor  Sepal.Width         3.2
6   virginica  Sepal.Width         3.3
7      setosa Petal.Length         1.4
8  versicolor Petal.Length         4.7
9   virginica Petal.Length         6.0
10     setosa  Petal.Width         0.2
11 versicolor  Petal.Width         1.4
12  virginica  Petal.Width         2.5

We can also use "-" operator to exclude the column(s) to be gather()ed to make the code cleaner.

mini.iris.w2l0 <- mini.iris %>%
          gather(key = "flower.att", value = "measurement", -Species)
mini.iris.w2l0
      Species   flower.att measurement
1      setosa Sepal.Length         5.1
2  versicolor Sepal.Length         7.0
3   virginica Sepal.Length         6.3
4      setosa  Sepal.Width         3.5
5  versicolor  Sepal.Width         3.2
6   virginica  Sepal.Width         3.3
7      setosa Petal.Length         1.4
8  versicolor Petal.Length         4.7
9   virginica Petal.Length         6.0
10     setosa  Petal.Width         0.2
11 versicolor  Petal.Width         1.4
12  virginica  Petal.Width         2.5
  • spread(): takes two columns and “spreads” them into multiple columns. It takes three principal arguments:

    • the data
    • the key column (categorical) variable whose values will become new column names.
    • the value column (numerical or categorical) variable whose values will fill the new column variables.

Further arguments include filling which, if set, fills in missing values with the value provided.

mini.iris.l2w <- mini.iris.w2l %>%
  spread(key = "flower.att", value = "measurement")
head(mini.iris.l2w)
     Species Petal.Length Petal.Width Sepal.Length Sepal.Width
1     setosa          1.4         0.2          5.1         3.5
2 versicolor          4.7         1.4          7.0         3.2
3  virginica          6.0         2.5          6.3         3.3
LS0tDQp0aXRsZTogIkRhdGEgUHJvY2Vzc2luZyBmb3IgVmlzdWFsaXphdGlvbiINCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGZpZ193aWR0aDogNg0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHRydWUNCiAgICB0aGVtZTogcmVhZGFibGUNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogNQ0KICAgIGZpZ19oZWlnaHQ6IDQNCi0tLQ0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KLyogVGFibGUgb2YgY29udGVudCAtIG5hdmlnYXRpb24gKi8NCmRpdiNUT0MgbGkgew0KICAgIGxpc3Qtc3R5bGU6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOnNreWJsdWU7DQogICAgYmFja2dyb3VuZC1pbWFnZTpub25lOw0KICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7DQogICAgYmFja2dyb3VuZC1wb3NpdGlvbjowOw0KICAgIGZvbnQtZmFtaWx5OiBBcmlhbCwgSGVsdmV0aWNhLCBzYW5zLXNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KfQ0KDQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCg0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMiB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE1cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KPC9zdHlsZT4NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCiAgIGxpYnJhcnkodGlkeXZlcnNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KIyBrbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICJDOi9Vc2Vycy83NUNQRU5HL09uZURyaXZlIC0gV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkgb2YgUEEvRG9jdW1lbnRzIikNCiMga25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAiQzpcXFNUQTQ5MFxcdzA1IikNCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gVFJVRSwgICANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BKQ0KYGBgDQoNClwNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KRGF0YSB2aXN1YWxpemF0aW9uIGlzIGEgZm9ybSBvZiBkYXRhIGFuYWx5c2lzIChhbHNvIGNhbGxlZCB2aXN1YWwgYW5hbHl0aWNzKS4gVGhpcyBtZWFucyB3ZSBuZWVkIHRvIHByZXBhcmUgZGF0YSBzZXRzIHRoYXQgYXJlIGFwcHJvcHJpYXRlIGZvciB2aXN1YWxpemF0aW9ucy4gUmVjYWxsIHRoZSBmb2xsb3dpbmcgd29yayBmbG93IG9mIGRhdGEgdmlzdWFsaXphdGlvbnMgbWVudGlvbmVkIGluIGVhcmxpZXIgbm90ZXMuIA0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iOTAlIiwgZmlnLmNhcD0iRGF0YSB2aXN1YWxpemF0aW9uIHdvcmstZmxvdy4ifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL1dvcmtmbG93LnBuZyIpDQpgYGANCg0KDQpUaGUgbWFqb3IgZGF0YSBtYW5hZ2VtZW50IHRhc2tzIGFyZSBkYXRhIGFnZ3JlZ2F0aW9uIGFuZCBleHRyYWN0aW9uLg0KDQoqICoqSW5mb3JtYXRpb24gQWdncmVnYXRpb24qKiAtIGNvbWJpbmluZyBpbmZvcm1hdGlvbiBpbiBkaWZmZXJlbnQgcmVsYXRpb25hbCBkYXRhIHNldHMgdG8gbWFrZSBhbiBpbnRlZ3JhdGVkIHNpbmdsZSBkYXRhIHNldCBmb3IgZGF0YSB2aXN1YWxpemF0aW9uLg0KDQoqICoqSW5mb3JtYXRpb24gRXh0cmFjdGlvbioqIC0gc3Vic2V0dGluZyBhIHNpbmdsZSBkYXRhIHNldCB0byBtYWtlIHNtYWxsIGRhdGEgc2V0cyB0aGF0IGhhdmUgc3BlY2lmaWMgaW5mb3JtYXRpb24gZm9yIGNyZWF0aW5nIGEgdmlzdWFsaXphdGlvbi4NCg0KVGhlIGJhc2UgUiBwYWNrYWdlIGhhcyBzb21lIHBvd2VyZnVsIGFuZCBlYXN5LXRvLXVzZSBmdW5jdGlvbnMgdG8gcGVyZm9ybSB0aGVzZSB0eXBlcyBvZiBkYXRhIG1hbmFnZW1lbnQuDQoNCg0KIyBEYXRhIENsZWFuaW5nIGFuZCBQcmVwYXJhdGlvbiBmb3IgVmlzdWFsaXphdGlvbg0KDQoNCiMjIERhdGEgQ2xlYW5pbmcNCg0KRGF0YSBjbGVhbmluZyByZWZlcnMgdG8gdGhlIHByb2Nlc3Mgb2YgbWFraW5nIGEgZGF0YSBzZXQgcG9zc2libHkgZnJvbSBkaWZmZXJlbnQgc291cmNlcyBvZiBgcmF3IGRhdGFgIGZvciBtb2RlbGluZywgdmlzdWFsaXphdGlvbiwgYW5kIHJlbGV2YW50IGFuYWx5c2lzLiBCZWZvcmUgc3RhcnRpbmcgY2xlYW5pbmcsIHdlIG5lZWQgdG8gY3JlYXRlIGEgYmFja3VwIHBsYW4gb2YgdGhlIG9yaWdpbmFsIGRhdGEgKG1hc3RlciBkYXRhIGZpbGVzKSBzaW5jZSB0aGluZ3MgY2FuIGdvIHdyb25nIGVhc2lseSB0aGUgZGF0YSBwcm9jZXNzaW5nIHN0YWdlLiBJdCdzIGltcG9ydGFudCB0byBjcmVhdGUgYW5kIHN0aWNrIHRvIGEgcGxhbiBmb3IgYmFja2luZyB1cCB5b3VyIGRhdGEuDQoNCiogU3RvcmUgYSBjb3B5IG9mIHlvdXIgbWFzdGVyIGZpbGVzIGluIGEgc2VwYXJhdGUgbG9jYXRpb24gZnJvbSB5b3VyIHdvcmtpbmcgZmlsZXMNCiogU2V0IHVwIGEgcmVndWxhciBiYWNrdXAgc2NoZWR1bGUNCiAgKyBXaGF0IGZpbGVzIHdpbGwgYmUgYmFja2VkIHVwPw0KICArIEhvdyBmcmVxdWVudGx5IHdpbGwgdGhlIGZpbGVzIGJlIGJhY2tlZCB1cD8NCg0KDQpBbGwgZGF0YSBjbGVhbmluZyB0YXNrcyBtdXN0IGJlIHBlcmZvcm1lZCBiYXNlZCBvbiB0aGUgd29ya2luZyBkYXRhIGZpbGVzLiBUaGUgbWFqb3IgdGFza3MgdmFyeSBmcm9tIHByb2plY3RzIHRvIHByb2plY3RzLCBzb21lIG9mIHRoZSBjb21tb24gdGFza3MgYXJlIGxpc3RlZCBpbiB0aGUgZm9sbG93aW5nOiANCg0KKiBSZW1vdmluZyB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMNCiAgKyBvYnNlcnZhdGlvbiBJRA0KICArIElycmVsZXZhbnQgdmFyaWFibGVzDQoqIERlbGV0aW5nIGR1cGxpY2F0ZSByb3dzL29ic2VydmF0aW9ucw0KICArIDxmb250IGNvbG9yPSJicm93biI+PGI+Q2F1dGlvbjwvYj48L2ZvbnQ+OiBOZXZlciBkbyB0aGlzIGluIGJvb3RzdHJhcCBzYW1wbGVzIQ0KKiBBZGRyZXNzaW5nIG91dGxpZXJzIG9yIGludmFsaWQgZGF0YQ0KICArIEdlbnVpbmUgb2JzZXJ2YXRpb24/DQogICsgbWlzdGFrZW5seSByZWNvcmRlZCBkYXRhIHBvaW50cz8NCiogRGVhbGluZyB3aXRoIG1pc3NpbmcgdmFsdWVzDQogICsgTWlzc2luZyB2YWx1ZSAtIHRoZSBleGlzdHMgYnV0IGZvciB3YXMgbm90IHJlY29yZGVkDQogICsgTnVsbCB2YWx1ZSAtIHRoZSB2YWx1ZSBkb2VzIG5vdCBleGlzdA0KKiBTdGFuZGFyZGl6aW5nIG9yIGNhdGVnb3JpemluZyB2YWx1ZXMNCiAgKyA8Zm9udCBjb2xvcj0icmVkIj48Yj5DYXV0aW9uIHdpdGggc3RhbmRhcmRpemluZyE8L2I+PC9mb250Pg0KICArIENvbWJpbmluZyBzcGFyc2UgY2F0ZWdvcmllcyBpbiBhIG1lYW5pbmdmdWwgd2F5Lg0KICArIERpc2NydGl6aW5nIGNvbnRpbnVvdXMgdmFyaWFibGVzLg0KKiBDb3JyZWN0aW5nIHR5cG9ncmFwaGljYWwgZXJyb3JzDQoqIERhdGEgQXVnbWVudGF0aW9uIG9yIEV4dGVuc2lvbg0KICArIEFkZGluZyBuZXcgY29sdW1ucyB0byB0aGUgZXhpc3RpbmcgZGF0YXNldA0KICArIENvbWJpbmluZyAgZXhpc3RpbmcgZGF0YXNldCB3aXRoIGFub3RoZXIgZGF0YXNldA0KDQoNCg0KDQojIyBEYXRhIFByZXBhcmF0aW9uIGZvciBWaXN1YWxpemF0aW9uDQoNCkZvciBhIHNwZWNpZmljIGRhdGEgYW5hbHlzaXMgc3VjaCBhcyBtb2RlbGluZyBvciBkYXRhIHZpc3VhbGl6YXRpb24sIHdlIG5lZWQgdG8gY3JlYXRlIGFuIGFuYWx5dGljIGRhdGEgc2V0IGJhc2VkIG9uIGNsZWFuIGRhdGEgc2V0cy4NCg0KKipGb3JtYXR0aW5nL0NvbnZlcnNpb24qKiANCg0KKiBGb3JtYXR0aW5nIGNvbHVtbnMgYXBwcm9wcmlhdGVseSAobnVtYmVycyBhcmUgdHJlYXRlZCBhcyBudW1iZXJzLCBkYXRlcyBhcyBkYXRlcykNCiogQ29udmVydCB2YWx1ZXMgaW50byBhcHByb3ByaWF0ZSB1bml0cw0KDQoqKkZpbHRlcmluZy9TdWJzZXR0aW5nKioNCg0KKiBGaWx0ZXIgeW91ciBkYXRhIHRvIGZvY3VzIG9uIHRoZSBzcGVjaWZpYyBkYXRhIHRoYXQgaW50ZXJlc3RzIHlvdS4NCiogR3JvdXAgZGF0YSBhbmQgY3JlYXRlIGFnZ3JlZ2F0ZSB2YWx1ZXMgZm9yIGdyb3VwcyAoQ291bnRzLCBNaW4sIE1heCwgTWVhbiwgTWVkaWFuLCBNb2RlKQ0KKiBFeHRyYWN0IHZhbHVlcyBmcm9tIGNvbXBsZXggY29sdW1ucw0KDQoqKkFnZ3JlZ2F0aW9uL01lcmdpbmcqKg0KDQoqIENvbWJpbmUgdmFyaWFibGVzIHRvIGNyZWF0ZSBuZXcgY29sdW1ucw0KKiBNZXJnZSBkaWZmZXJlbnQgcmVsYXRpb25hbCBkYXRhIHNldHMNCg0KDQojIEJhc2ljIERhdGEgTWFuYWdlbWVudDogTWVyZ2luZyBEYXRhIFNldHMNCg0KVGhlcmUgYXJlIGRpZmZlcmVudCBwYWNrYWdlcyBpbiBSIHRoYXQgaGF2ZSB2YXJpb3VzIGZ1bmN0aW9ucyBjYXBhYmxlIG9mIGRvaW5nIGRhdGEgbWFuYWdlbWVudC4gSW4gdGhpcyBub3RlLCB3ZSBpbnRyb2R1Y2UgdGhlIGNvbW1vbmx5IHVzZWQgZnVuY3Rpb25zIGluIGJhc2UgUiBhbmQgYHRpZHl2ZXJzZWAuDQoNCiMjIE1lcmdlIERhdGEgU2V0cw0KDQpJdCBpcyB2ZXJ5IGNvbW1vbiB0aGF0IHRoZSBpbmZvcm1hdGlvbiB3ZSBhcmUgaW50ZXJlc3RlZCBpbiByZXNpZGVzIGluIGRpZmZlcmVudCBkYXRhIHNvdXJjZXMuIEluIG9yZGVyIHRvIG1lcmdlIGRpZmZlcmVudCBkYXRhIHNldHMsIHRoZXJlIG11c3QgYmUgYXQgbGVhc3Qgb25lIHZhcmlhYmxlICJrZXkiIHRoYXQgbGlua3MgdG8gZGlmZmVyZW50IGRhdGEgc2V0cy4gDQoNClRoZXJlIGFyZSBzZXZlcmFsIGRpZmZlcmVudCBvcGVyYXRpb25zIGluIFNRTCB0byBjcmVhdGUgZGlmZmVyZW50IHR5cGVzIG9mIHRoZSBtZXJnZWQgZGF0YSBzZXQuIFRoZSBmb2xsb3dpbmcgYXJlIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgb25lcy4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IklubmVyIEpvaW4uIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9qb2lucy5wbmciKQ0KYGBgDQoNCg0KVGhlIG5leHQgZmlndXJlIHNob3dzIHRoZSBiYXNpYyBvcGVyYXRpb25zIHdpdGggdGlueSB0YWJsZXMgaWxsdXN0cmF0aW5nIHRoZSBvcGVyYXRpb25zLg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iOTAlIiwgZmlnLmNhcD0iSWxsdXN0cmF0aW9uIG9mIGlubmVyIGpvaW4uIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9Kb2luRXhhbXBsZXMucG5nIikNCmBgYA0KDQoNCg0KIyMgTWVyZ2luZyBEYXRhIGluIEJhc2UgUiANCg0KVGhlIGJhc2UgUiBmdW5jdGlvbiBgbWVyZ2UoKWAgY2FuIGJlIHVzZWQgdG8gcGVyZm9ybSBkaWZmZXJlbnQgam9pbnMuIFRvIGlsbHVzdHJhdGUsIHdlIHVzZSB0aGUgdGlueSB0b3kgZGF0YSBzZXQgaW4gdGhlIGFib3ZlIGZpZ3VyZSB0byBzaG93IHlvdSBob3cgdG8gdXNlIGBtZXJnZSgpYCBmdW5jdGlvbi4NCg0KDQoNCiogRGVmaW5pbmcgRGF0YSBGcmFtZXMNCmBgYHtyfQ0KZW1wbG95ZWUgPSBkYXRhLmZyYW1lKEVtcElEID0gYygxLDIsMyksIA0KICAgICAgICAgICAgICAgICAgICAgIEVtcE5hbWUgPSBjKCJSYWplbmRyYSIsICJLdXN1bSIsICJBa3NoaXRhIikpDQpjaXR5ID0gZGF0YS5mcmFtZShJRCA9IGMoMSwyLDcsOCksIA0KICAgICAgICAgICAgICAgICAgQ2l0eSA9YygiSmFpcHVyIiwgIkRlbGhpIiwgIlJhaXB1ciIsICJCYW5nYWxvcmUiKSkNCmBgYA0KDQoNCiogSW5uZXIgSm9pbg0KDQpgYGB7cn0NCmlubmVyam9pbiA9IG1lcmdlKHg9ZW1wbG95ZWUsIGNpdHksIGJ5LnggPSAnRW1wSUQnLCBieS55ID0nSUQnLCBhbGwgPSBGQUxTRSkNCmlubmVyam9pbg0KYGBgDQoNCiogT3V0ZXIgSm9pbg0KDQpgYGB7cn0NCmlubmVyam9pbiA9IG1lcmdlKHggPSBlbXBsb3llZSwgeSA9IGNpdHksIGJ5LnggPSAnRW1wSUQnLCBieS55ID0nSUQnLCBhbGwgPSBUUlVFKQ0KaW5uZXJqb2luDQpgYGANCg0KDQoqIExlZnQgSm9pbg0KDQpgYGB7cn0NCmxlZnRqb2luID0gbWVyZ2UoeCA9IGVtcGxveWVlLCB5ID0gY2l0eSwgYnkueCA9ICdFbXBJRCcsIGJ5LnkgPSdJRCcsIGFsbC54ID0gVFJVRSkNCmxlZnRqb2luIA0KYGBgDQoNCg0KKiBSaWdodCBKb2luDQoNCmBgYHtyfQ0KcmlnaHRqb2luID0gbWVyZ2UoeCA9IGVtcGxveWVlLCB5ID0gY2l0eSwgYnkueCA9ICdFbXBJRCcsIGJ5LnkgPSdJRCcsIGFsbC55ID0gVFJVRSkNCnJpZ2h0am9pbg0KYGBgDQoNCg0KDQoNCiMjIE1lcmdpbmcgRGF0YSB3aXRoIE11dGF0aW5nIEpvaW5zIGluICoqZHBseXIqKg0KDQpUaGUgcGFja2FnZSAqKmRwbHlyKiogaGFzIHRoZSBmb2xsb3dpbmcgZm91ciBqb2luIGZ1bmN0aW9ucyBjb3JyZXNwb25kaW5nIHRvIHRoZSBvcHRpb25zIGluIHRoZSBiYXNlIFIgZnVuY3Rpb24gJ21lcmdlKClgLiANCg0KDQpUaGUgbXV0YXRpbmcgam9pbnMgYWRkIGNvbHVtbnMgZnJvbSB5IHRvIHgsIG1hdGNoaW5nIHJvd3MgYmFzZWQgb24gdGhlIGtleXM6DQoNCiogYGlubmVyX2pvaW4oKWA6IGluY2x1ZGVzIGFsbCByb3dzIGluIHggYW5kIHkuDQoNCiogYGxlZnRfam9pbigpYDogaW5jbHVkZXMgYWxsIHJvd3MgaW4geC4NCg0KKiBgcmlnaHRfam9pbigpYDogaW5jbHVkZXMgYWxsIHJvd3MgaW4geS4NCg0KKiBgZnVsbF9qb2luKClgIChhbHNvIGNhbGxlZCAqKm91dGVyIGpvaW4qKik6IGluY2x1ZGVzIGFsbCByb3dzIGluIHggb3IgeSAoYWxzbyBjYWxsZWQgb3V0ZXIgam9pbikuLg0KDQpJZiBhIHJvdyBpbiB4IG1hdGNoZXMgbXVsdGlwbGUgcm93cyBpbiB5LCBhbGwgdGhlIHJvd3MgaW4geSB3aWxsIGJlIHJldHVybmVkIG9uY2UgZm9yIGVhY2ggbWF0Y2hpbmcgcm93IGluIHguDQoNClRvIHVzZSBtdXRhdGluZyBqb2lucywgd2UgZmlyc3QgcmVuYW1lIGtleSB2YXJpYWJsZXMgc28gdGhhdCBwcmltYXJ5IGtleXMgaGF2ZSB0aGUgc2FtZSBuYW1lLg0KDQpGb3IgbGFyZ2UgdGFibGVzIGRwbHlyIGpvaW4gZnVuY3Rpb25zIGlzIG11Y2ggZmFzdGVyIHRoYW4gbWVyZ2UoKS4gVGhlIGFkdmFudGFnZXMgb2YgdXNpbmcgZHBseXIgcGFja2FnZSBmb3IgbWVyZ2luZyBkYXRhZnJhbWVzIGFyZToNCg0KMS4gVGhleSBhcmUgbXVjaCBmYXN0ZXIuDQoNCjIuIFNob3dzIHRoZSBrZXlzIGV4cGxpY2l0bHkgd2hlbiBtZXJnaW5nIGRhdGEuDQoNCjMuIFRoZXkgYXJlIGZsZXhpYmxlIGFuZCB3b3JrIHdpdGggZGF0YWJhc2UgdGFibGVzLg0KDQoNCg0KVGhlIGZvbGxvd2luZyBhcmUgc2ltcGxlIGlsbHVzdHJhdGl2ZSBleGFtcGxlcy4NCg0KDQpgYGB7cn0NCmVtcGxveWVlLm5ldyA9IGVtcGxveWVlDQplbXBsb3llZS5uZXckSUQgPSBlbXBsb3llZSRFbXBJRCAgIyBhZGRpbmcgdGhlIG5ldyByZW5hbWVkIElEDQplbXBsb3llZS5uZXcgPSBlbXBsb3llZS5uZXdbLCAtMV0gICAgICMgZHJvcCB0aGUgb2xkIElEIHZhcmlhYmxlDQplbXBsb3llZS5uZXcNCmBgYA0KDQoqIElubmVyIEpvaW4NCg0KYGBge3J9DQppbm5lcl9qb2luKGVtcGxveWVlLm5ldywgY2l0eSwgYnkgPSAiSUQiKQ0KYGBgDQoNCiogTGVmdCBKb2luDQoNCmBgYHtyfQ0KbGVmdF9qb2luKGVtcGxveWVlLm5ldywgY2l0eSwgYnkgPSAiSUQiKQ0KYGBgDQoNCg0KKiByaWdodCBKb2luDQoNCmBgYHtyfQ0KcmlnaHRfam9pbihlbXBsb3llZS5uZXcsIGNpdHksIGJ5ID0gIklEIikNCmBgYA0KDQoqIEZ1bGwgKE91dGVyKSBKb2luDQoNCmBgYHtyfQ0KZnVsbF9qb2luKGVtcGxveWVlLm5ldywgY2l0eSwgYnkgPSAiSUQiKQ0KYGBgDQoNCg0KIyMgIFVzZSBvZiBQaXBlIE9wZXJhdG9yIGAlPiVgIHdpdGggTXV0YXRpbmcgSm9pbnMNCg0KVGhlIHBpcGUgb3BlcmF0b3IsIHdyaXR0ZW4gYXMgYCU+JWAgdGFrZXMgdGhlIG91dHB1dCBvZiBvbmUgZnVuY3Rpb24gYW5kIHBhc3NlcyBpdCBpbnRvIGFub3RoZXIgZnVuY3Rpb24gYXMgYW4gYXJndW1lbnQuIFRoaXMgYWxsb3dzIHVzIHRvIGxpbmsgKmEgc2VxdWVuY2Ugb2YgYW5hbHlzaXMgc3RlcHMqIHVzaW5nIGZ1bmN0aW9ucyBpbiBgZHBseXJgIGFuZCBgdGlkeXJgIGluIGRhdGEgd3JhbmdsaW5nLg0KDQoNCiogSW5uZXIgSm9pbg0KDQpgYGB7cn0NCnBpcGUuaW5uZXJqb2luIDwtIGVtcGxveWVlICU+JSBpbm5lcl9qb2luKGNpdHksIGJ5ID0gYygiRW1wSUQiID0gIklEIikpDQpwaXBlLmlubmVyam9pbg0KYGBgDQoNCiogRnVsbCAoT3V0ZXIpIEpvaW4NCg0KYGBge3J9DQpwaXBlLm91dGVyam9pbiA8LSBlbXBsb3llZSAlPiUgZnVsbF9qb2luKGNpdHksIGJ5ID0gYygiRW1wSUQiID0gIklEIikpDQpwaXBlLm91dGVyam9pbg0KYGBgDQoNCg0KKiBMZWZ0IEpvaW4NCg0KYGBge3J9DQpwaXBlLmxlZnRqb2luIDwtIGVtcGxveWVlICU+JSBsZWZ0X2pvaW4oY2l0eSwgYnkgPSBjKCJFbXBJRCIgPSAiSUQiKSkNCnBpcGUubGVmdGpvaW4NCmBgYA0KDQoNCiogUmlnaHQgSm9pbg0KDQpgYGB7cn0NCnBpcGUucmlnaHRqb2luIDwtIGVtcGxveWVlICU+JSByaWdodF9qb2luKGNpdHksIGJ5ID0gYygiRW1wSUQiID0gIklEIikpDQpwaXBlLnJpZ2h0am9pbg0KYGBgDQoNCg0KIyBCYXNpYyBEYXRhIE1hbmFnZW1lbnQ6IFN1YnNldHRpbmcgRGF0YSANCg0KQW5vdGhlciBpbXBvcnRhbnQgZGF0YSBtYW5hZ2VtZW50IHRhc2sgaXMgdG8gc3Vic2V0IGRhdGEgc2V0cyB0byBleHRyYWN0IHRoZSBkZXNpcmVkIGluZm9ybWF0aW9uIGZvciBhbmFseXNlcyBhbmQgdmlzdWFsaXphdGlvbi4NCg0KVHdvIG9wZXJhdGlvbnMgYXJlIHVzZWQgdG8gc3Vic2V0IGEgZGF0YSBzZXQ6IHNlbGVjdC9kcm9wIGNvbHVtbnMgYW5kIHNlbGVjdCByb3dzIHRoYXQgbWVldCBjZXJ0YWluIGNvbmRpdGlvbnMuDQoNClRoZSB3b3JraW5nIGRhdGEgc2V0IGluIHRoZSBzZWN0aW9uIGlzIHRoZSB3ZWxsLWtub3duIGBpcmlzYCBkYXRhIHNldCB0aGF0IGhhcyA0IG51bWVyaWNhbCB2YXJpYWJsZXMgKGF0dHJpYnV0ZXMgb2YgaXJpcyBmbG93ZXJzKSBhbmQgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSAoc3BlY2llcyBvZiBpcmlzIGZsb3dlcnMpLg0KDQoNCiMjIEFjY2Vzc29ycyBpbiBSIGBbYCwgYFtbYCBhbmQgYCRgDQoNCldoZW4gc3Vic2V0dGluZyBhIGRhdGEgc2V0LCBpdCBpcyB1bmF2b2lkYWJsZSB0byBhY2Nlc3MgdGhlIHZhbHVlKHMpIG9mIGNlcnRhaW4gdmFyaWFibGUocykuIFRocmVlIFIgYWNjZXNzb3JzIGFyZSBjb21tb25seSB1c2VkIGluIFIgY29kaW5nLg0KDQoqIGBbYCBzdWJzZXR0aW5nIGEgZGF0YSBzZXQNCg0KVGhpcyBSIGFjY2Vzc29yIGlzIHByb2JhYmx5IHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQuIFdoZW4gd2Ugd2FudCBhIHN1YnNldCBvZiBhbiBvYmplY3QgdXNpbmcgYFtgLiBSZW1lbWJlciB0aGF0IHdoZW4gd2UgdGFrZSBhIHN1YnNldCBvZiB0aGUgb2JqZWN0IHlvdSBnZXQgYHRoZSBzYW1lIHR5cGVgIG9mIHRoaW5nLiBUaHVzLCB0aGUgc3Vic2V0IG9mIGEgdmVjdG9yIHdpbGwgYmUgYSB2ZWN0b3IsIHRoZSBzdWJzZXQgb2YgYSBsaXN0IHdpbGwgYmUgYSBsaXN0IGFuZCB0aGUgc3Vic2V0IG9mIGEgZGF0YS5mcmFtZSB3aWxsIGJlIGEgZGF0YS5mcmFtZS4NCiANCiogYFtbYCBleHRyYWN0aW5nIG9uZSBpdGVtDQoNClRoZSBkb3VibGUgc3F1YXJlIGJyYWNrZXRzIGFyZSB1c2VkIHRvIGV4dHJhY3Qgb25lIGVsZW1lbnQgZnJvbSBwb3RlbnRpYWxseSBtYW55LiBGb3IgdmVjdG9ycyB5aWVsZCB2ZWN0b3JzIHdpdGggYSBzaW5nbGUgdmFsdWU7IGRhdGEgZnJhbWVzIGdpdmUgYSBjb2x1bW4gdmVjdG9yOyBmb3IgYSBsaXN0LCBvbmUgZWxlbWVudDoNCg0KRm9yIGV4YW1wbGUsIA0KDQpgYGB7cn0NCmxldHRlcnNbWzNdXSAgICAgICAgICAgICMgZXh0cmFjdHMgdGhlIHRoaXJkIGVsZW1lbnQgaW4gdGhlIHZlY3RvciBvZiBhbGwgbG93ZXIgY2FzZSBsZXR0ZXJzDQppcmlzW1siUGV0YWwuTGVuZ3RoIl1dICAjIGV4dHJhY3QgdGhlIHZhcmlhYmxlIG5hbWVkICdQZXRhbC5MZW5ndGgnIGluIHRoZSBkYXRhIGZyYW1lLg0KYGBgDQoNClRoZSBkb3VibGUgc3F1YXJlIGJyYWNrZXQgbG9va3MgYXMgaWYgd2UgYXJlIGFza2luZyBmb3Igc29tZXRoaW5nIGRlZXAgd2l0aGluIGEgY29udGFpbmVyLiBXZSBhcmUgbm90IHRha2luZyBhIHNsaWNlIGJ1dCByZWFjaGluZyB0byBnZXQgYXQgdGhlIG9uZSB0aGluZyBhdCB0aGUgY29yZS4NCg0KKiBJbnRlcmFjdCB3aXRoIGAkYA0KDQpUaGUgYWNjZXNzb3IgdGhhdCBwcm92aWRlcyB0aGUgbGVhc3QgdW5pcXVlIHV0aWxpdHkgaXMgYWxzbyBwcm9iYWJseSB1c2VkIHRoZSBtb3N0IG9mdGVuIHVzZWQuIGAkYCBpcyBhIHNwZWNpYWwgY2FzZSBvZiBgW1tgIGluIHdoaWNoIHdlIGFjY2VzcyBhIHNpbmdsZSBpdGVtIGJ5IGFjdHVhbCBuYW1lLiBUaGUgZm9sbG93aW5nIGFyZSBlcXVpdmFsZW50Og0KDQpgYGB7cn0NCmlyaXMkUGV0YWwuTGVuZ3RoDQppcmlzW1siUGV0YWwuTGVuZ3RoIl1dDQpgYGANCg0KDQoNCiMjIFN1YnNldHRpbmcgRGF0YSBpbiBCYXNlIFINCg0KKipTZWxlY3RpbmcvRHJvcHBpbmcgQ29sdW1ucyoqDQoNClN1YnNldHRpbmcgYSBkYXRhIHNldCBieSBzZWxlY3Rpbmcgb3IgZHJvcHBpbmcgYSBzdWJzZXQgb2YgdmFyaWFibGVzIChjb2x1bW5zKSBmcm9tIGEgZGF0YSBzZXQgaXMgc3RyYWlnaHRmb3J3YXJkLiANCg0KRm9yIGV4YW1wbGUsIHdlIGNhbiBkZWZpbmUgYSBzdWJzZXQgb2YgdGhlIGBpcmlzYCBkYXRhIHNldCBieSBzZWxlY3RpbmcgYWxsIG51bWVyaWNhbCB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KaXJpcy5uYW1lcyA9IG5hbWVzKGlyaXMpDQppcmlzMCA9IGlyaXNbLCBpcmlzLm5hbWVzWzE6NF1dDQpEVDo6ZGF0YXRhYmxlKGlyaXMwLCBmaWxsQ29udGFpbmVyID0gRkFMU0UsIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCkpDQpgYGANCg0KV2UgY2FuIGFsc28gY3JlYXRlIHRoZSBzYW1lIGRhdGEgc2V0IGJ5IGRyb3BwaW5nIHZhcmlhYmxlcyBpbiB0aGUgb3JpZ2luYWwgZGF0YSBzZXQuIEZvciBleGFtcGxlDQoNCmBgYHtyfQ0KaXJpczAyID0gaXJpc1ssIC01XQ0KRFQ6OmRhdGF0YWJsZShpcmlzMDIsIGZpbGxDb250YWluZXIgPSBGQUxTRSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwKSkNCmBgYA0KDQoqKlNlbGVjdGlvbi9Ecm9wcGluZyBSb3dzKioNCg0KVGhpcyBpcyBhbHNvIHJlbGF0aXZlbHkgc3RyYWlnaHRmb3J3YXJkLiBUaGUgYmFzaWMgaWRlYSBpcyB0byBpZGVudGlmeSByb3cgSURzIHRvIHNlbGVjdCBvciBkcm9wIHRoZSBjb3JyZXNwb25kaW5nIHJvd3MuIFRoZSBSIGZ1bmN0aW9uIGB3aGljaCgpYCBjYW4gdGhpcyB0cmljayENCg0KVGhlIGZvbGxvd2luZyBleGFtcGxlIGlsbHVzdHJhdGVzIHRoZSB3YXkgb2YgdXNpbmcgYHdoaWNoKClgIHRvIHN1YnNldHRpbmcgZGF0YS4NCg0KMS4gU2VsZWN0aW5nIE9uZSBTcGVjaWVzIG9mIElyaXMgRmxvd2Vycw0KDQpgYGB7cn0NCnNldG9zYS5pZCA9IHdoaWNoKGlyaXMkU3BlY2llcyA9PSAic2V0b3NhIikNCnNldG9zYS5mbG93ZXIgPSBpcmlzW3NldG9zYS5pZCxdDQpEVDo6ZGF0YXRhYmxlKHNldG9zYS5mbG93ZXIsIGZpbGxDb250YWluZXIgPSBGQUxTRSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwKSkNCmBgYA0KDQoyLiBTZWxlY3RpbmcgVHdvIFNwZWNpZXMgb2YgSXJpcyBGbG93ZXJzDQoNClRoZSBmb2xsb3dpbmcgdGhyZWUgY29kZSBjaHVua3MgY3JlYXRlIHRoZSBzYW1lIGRhdGEgc2V0Lg0KDQpNZXRob2QgMToNCg0KYGBge3J9DQpub3Quc2V0b3NhLmlkMDEgPSB3aGljaChpcmlzJFNwZWNpZXMgIT0gInNldG9zYSIpDQpub3Quc2V0b3NhLmZsb3dlcjAxID0gaXJpc1tub3Quc2V0b3NhLmlkMDEsXQ0KRFQ6OmRhdGF0YWJsZShub3Quc2V0b3NhLmZsb3dlcjAxLCBmaWxsQ29udGFpbmVyID0gRkFMU0UsIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCkpDQpgYGANCg0KTWV0aG9kIDI6DQoNCmBgYHtyfQ0Kbm90LnNldG9zYS5pZDAyID0gd2hpY2goaXJpcyRTcGVjaWVzID09ICJ2aXJnaW5pY2EiIHwgaXJpcyRTcGVjaWVzID09InZlcnNpY29sb3IiKQ0Kbm90LnNldG9zYS5mbG93ZXIwMiA9IGlyaXNbbm90LnNldG9zYS5pZDAyLF0NCkRUOjpkYXRhdGFibGUobm90LnNldG9zYS5mbG93ZXIwMiwgZmlsbENvbnRhaW5lciA9IEZBTFNFLCBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTApKQ0KYGBgDQpNZXRob2QgMzoNCg0KYGBge3J9DQpub3Quc2V0b3NhLmlkMDMgPSB3aGljaChpcmlzJFNwZWNpZXMgJWluJSBjKCJ2ZXJzaWNvbG9yIiwgInZpcmdpbmljYSIpKQ0Kbm90LnNldG9zYS5mbG93ZXIwMyA9IGlyaXNbbm90LnNldG9zYS5pZDAxLF0NCkRUOjpkYXRhdGFibGUobm90LnNldG9zYS5mbG93ZXIwMywgZmlsbENvbnRhaW5lciA9IEZBTFNFLCBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTApKQ0KYGBgDQoNCg0KIyMgU3Vic2V0dGluZyBEYXRhIHdpdGggYGRwbHlyYA0KDQoqKmRwbHlyKiogcHJvdmlkZXMgaGVscGVyIHRvb2xzIGZvciB0aGUgbW9zdCBjb21tb24gZGF0YSBtYW5pcHVsYXRpb24gdGFza3MuIEl0IGlzIGJ1aWx0IHRvIHdvcmsgZGlyZWN0bHkgd2l0aCBkYXRhIGZyYW1lcyBhbmQgaGFzIHRoZSBhYmlsaXR5IHRvIHdvcmsgZGlyZWN0bHkgd2l0aCBkYXRhIHN0b3JlZCBpbiBhbiBleHRlcm5hbCBkYXRhYmFzZS4gV2UgY2FuIGNvbmR1Y3QgcXVlcmllcyBvbiB0aGUgZGF0YWJhc2UgZGlyZWN0bHkgYW5kIHB1bGwgYmFjayBpbnRvIFIgb25seSB3aGF0IHdlIG5lZWQgZm9yIGFuYWx5c2lzLg0KDQpTaW5jZSBzZWxlY3RpbmcvZHJvcHBpbmcgdmFyaWFibGVzIGlzIHN0cmFpZ2h0Zm9yd2FyZCAocGFydGljdWxhcmx5IHdoZW4gdXNpbmcgJT4lKS4gTmV4dCwgd2UgcHJvdmlkZSBhIGZldyBleGFtcGxlcyBzaG93aW5nIGhvdyB0byB1c2UgYGZpbHRlcigpYCB0byBzZWxlY3QvZHJvcCByb3dzIHdpdGggY2VydGFpbiBjb25kaXRpb25zLg0KDQoqIEZpbHRlcmluZyBieSBvbmUgY3JpdGVyaW9uDQpgYGB7cn0NCnNldG9zYSA9IGZpbHRlcihpcmlzLCBTcGVjaWVzID09ICJzZXRvc2EiKQ0KRFQ6OmRhdGF0YWJsZShzZXRvc2EsIGZpbGxDb250YWluZXIgPSBGQUxTRSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwKSkNCmBgYA0KDQpgYGB7cn0NCmlyaXMuZ2UuNiA9IGZpbHRlcihpcmlzLCBTZXBhbC5MZW5ndGggPiA2KQ0KRFQ6OmRhdGF0YWJsZShpcmlzLmdlLjYsIGZpbGxDb250YWluZXIgPSBGQUxTRSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwKSkNCmBgYA0KDQoqIFdoZW4gbXVsdGlwbGUgZXhwcmVzc2lvbnMgYXJlIHVzZWQsIHRoZXkgYXJlIGNvbWJpbmVkIHVzaW5nIGAmYCAobG9naWNhbCBBTkQpIG9yIGB8YCAobG9naWNhbCBPUikNCg0KYGBge3J9DQpzZXRvc2EuZ2U1ID0gZmlsdGVyKGlyaXMsIFNwZWNpZXMgPT0gInNldG9zYSIgJiBTZXBhbC5MZW5ndGggPiA1ICkNCkRUOjpkYXRhdGFibGUoc2V0b3NhLmdlNSwgZmlsbENvbnRhaW5lciA9IEZBTFNFLCBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTApKQ0KYGBgDQoNCg0KYGBge3J9DQpzZXRvc2EuZ2U3ID0gZmlsdGVyKGlyaXMsIFNwZWNpZXMgPT0gInNldG9zYSIgfCBTZXBhbC5MZW5ndGggPiA3ICkNCkRUOjpkYXRhdGFibGUoc2V0b3NhLmdlNywgZmlsbENvbnRhaW5lciA9IEZBTFNFLCBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTApKQ0KYGBgDQoNCiogVG8gcmVmZXIgdG8gY29sdW1uIG5hbWVzIHRoYXQgYXJlIHN0b3JlZCBhcyBzdHJpbmdzLCB1c2UgdGhlIGAuZGF0YWAgcHJvbm91bjoNCg0KYGBge3J9DQp2YXJzIDwtIGMoIlNlcGFsLkxlbmd0aCIsICJQZXRhbC5MZW5ndGgiKQ0KY29uZCA8LSBjKDYsIDUpDQpzdWJzZXQuaXJpcyA8LSBpcmlzICU+JQ0KICBmaWx0ZXIoDQogICAgLmRhdGFbW3ZhcnNbWzFdXV1dID4gY29uZFtbMV1dLA0KICAgIC5kYXRhW1t2YXJzW1syXV1dXSA8IGNvbmRbWzJdXQ0KICApDQojc3Vic2V0LmlyaXMNCkRUOjpkYXRhdGFibGUoc3Vic2V0LmlyaXMsIGZpbGxDb250YWluZXIgPSBGQUxTRSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwKSkNCmBgYA0KDQoNCiMjIFZhcmlhYmxlIERlZmluaXRpb24gYW5kIFZhcmlhYmxlIFR5cGUgQ29udmVyc2lvbg0KDQoqIERlZmluZSBOZXcgVmFyaWFibGVzDQoNCkRlZmluaW5nIG5ldyB2YXJpYWJsZXMgYmFzZWQgb24gdGhlIGV4aXN0aW5nIHZhcmlhYmxlcyBpcyBzdHJhaWdodGZvcndhcmQgaW4gUiB1c2luZyB0aGUgYmFzaWMgYXJpdGhtZXRpYyBhbmQgbWF0aGVtYXRpY2FsIG9wZXJhdGlvbnMuIFdoZW4gdXNpbmcgYCU+JWAsIGBkcGx5cigpYCBpcyB1c2VkIHRvIGRlZmluZSBuZXcgdmFyaWFibGVzLg0KDQoNCiogVmFyaWFibGUgVHlwZSBDb252ZXJzaW9uDQoNClR5cGUgY29udmVyc2lvbnMgaW4gUiB3b3JrIGFzIHlvdSB3b3VsZCBleHBlY3QuIEZvciBleGFtcGxlLCBhZGRpbmcgYSBjaGFyYWN0ZXIgc3RyaW5nIHRvIGEgbnVtZXJpYyB2ZWN0b3IgY29udmVydHMgYWxsIHRoZSBlbGVtZW50cyBpbiB0aGUgdmVjdG9yIHRvIHRoZSBjaGFyYWN0ZXIuDQoNCjEuIFVzZSBgaXMuZm9vYCB0byB0ZXN0IGZvciBkYXRhIHR5cGUgZm9vLiBSZXR1cm5zIFRSVUUgb3IgRkFMU0UNCg0KYGlzLm51bWVyaWMoKSwgaXMuY2hhcmFjdGVyKCksIGlzLnZlY3RvcigpLCBpcy5tYXRyaXgoKSwgaXMuZGF0YS5mcmFtZSgpYA0KDQoyLiBVc2UgYGFzLmZvb2AgdG8gZXhwbGljaXRseSBjb252ZXJ0IGl0Lg0KDQpgYXMubnVtZXJpYygpLCBhcy5jaGFyYWN0ZXIoKSwgYXMudmVjdG9yKCksIGFzLm1hdHJpeCgpLCBhcy5kYXRhLmZyYW1lKWANCg0KDQoNCg0KIyBJbXBvcnRpbmcvRXhwb3J0aW5nIERhdGENCg0KIyMgSW1wb3J0aW5nIERhcmENCg0KVGhlcmUgYXJlIGRpZmZlcmVudCBmdW5jdGlvbnMgaW4gdmFyaW91cyBSIGxpYnJhcmllcyB0byByZWFkIGRhdGEgdG8gUi4NCg0KKiBCYXNlIFIgYW5kIExpYnJhcmllcyBDb21lIHdpdGggQmFzZSBSIA0KDQpSIGxvYWRpbmcgZnVuY3Rpb25zIGluIHt1dGlsc306IGByZWFkLnRhYmxlKClgLCBgcmVhZC5jc3YoKWAsICBgcmVhZC5jc3YyKClgLCBgcmVhZC5kZWxpbSgpYCwgYW5kIGByZWFkLmRlbGltMigpYA0KDQoqIEZ1bmN0aW9ucyBpbiB7dGlkeXZlcnNlfQ0KDQpBcyBhIHBhcnQgb2Yge3RpZHl2ZXJzZX0sIHRoZSBsaWJyYXJ5IHtyZWFkcn0gaGFzIHNldmVyYWwgZnVuY3Rpb25zIHRvIHJlYWQgdGhlIGRhdGEgaW4gY29tbW9uIGZvcm1hdHMuDQoNCmByZWFkX3RhYmxlKCksIHJlYWRfZGVsaW0oKSwgcmVhZF9jc3YoKSwgcmVhZF9jc3YyKCksIHJlYWRfdHN2KClgDQoNCiogUmVhZCBkYXRhIHNldCBnZW5lcmF0ZWQgYnkgb3RoZXIgcHJvZ3JhbXMgc3VjaCBhcyBTQVMsIFNQU1MsIGV0Yy4NCg0KU2V2ZXJhbCBsaWJyYXJpZXMgYXJlIHVzZWZ1bCB0byBsb2FkIHNwZWNpYWwgZm9ybWF0cyBvZiBkYXRhIHRvIFIuIFRocmVlIGltcG9ydGFudCBsaWJyYXJpZXMgYXJlDQoNCmB7eGxzeCwgSG1pc2MsIGZvcmVpZ259YC4NCg0KDQoNCiMjIEV4cG9ydGluZyBEYXRhDQoNCldlIGhhdmUgbGVhcm5lZCBob3cgdG8gdXNlIGBkcGx5cmAgdG8gZXh0cmFjdCBpbmZvcm1hdGlvbiBmcm9tIG9yIHN1bW1hcml6ZSB5b3VyIHJhdyBkYXRhLCB3ZSBtYXkgd2FudCB0byBleHBvcnQgdGhlc2UgbmV3IGRhdGEgc2V0cyB0byBzaGFyZSB0aGVtIHdpdGggb3RoZXIgcGVvcGxlIG9yIGZvciBhcmNoaXZhbC4NCg0KU2ltaWxhciB0byB0aGUgYHJlYWRfY3N2KClgIGZ1bmN0aW9uIHVzZWQgZm9yIHJlYWRpbmcgQ1NWIGZpbGVzIGludG8gUiwgdGhlcmUgaXMgYSBgd3JpdGVfY3N2KClgIGZ1bmN0aW9uIHRoYXQgZ2VuZXJhdGVzIENTViBmaWxlcyBmcm9tIGRhdGEgZnJhbWVzLg0KDQpMZXQncyBhc3N1bWUgb3VyIGRhdGEgc2V0IHVuZGVyIHRoZSBuYW1lLCBgZmluYWxfZGF0YWAsICBpcyByZWFkeSwgd2UgY2FuIHNhdmUgaXQgYXMgYSBDU1YgZmlsZSBpbiBvdXIgYGRhdGFgIGZvbGRlciB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGUuDQoNCmB3cml0ZV9jc3YoZmluYWxfZGF0YSwgZmlsZSA9ICJkYXRhL2ZpbmFsX2RhdGEuY3N2IilgDQoNCg0KDQojIE92ZXJ2aWV3IG9mIGBUaWR5dmVyc2VgDQoNClRoZXJlIGFyZSBzZXZlcmFsIFIgbGlicmFyaWVzIHRoYXQgaGF2ZSBwb3dlcmZ1bCB0b29scyBmb3IgZGF0YSB3cmFuZ2xpbmcgYW5kIGluZm9ybWF0aW9uIGV4dHJhY3Rpb24uIFRpZHl2ZXJzZSBpcyBhIGNvbGxlY3Rpb24gb2YgZXNzZW50aWFsIFIgcGFja2FnZXMgZm9yIGRhdGEgc2NpZW5jZS4gVGhlcmUgOCBwYWNrYWdlcyB1bmRlciB0aGUgYHRpZHl2ZXJzZSB1bWJyZWxsYWAgdGhhdCBoZWxwIHVzIGluIHBlcmZvcm1pbmcgYW5kIGludGVyYWN0aW5nIHdpdGggdGhlIGRhdGEuICANCg0KDQojIyBQYWNrYWdlcyBmb3IgRGF0YSBXcmFuZ2xpbmcgYW5kIFRyYW5zZm9ybWF0aW9uDQoNCiogKipkcGx5cioqIHByb3ZpZGVzIGhlbHBlciB0b29scyBmb3IgdGhlIG1vc3QgY29tbW9uIGRhdGEgbWFuaXB1bGF0aW9uIHRhc2tzLiBJdCBpcyBidWlsdCB0byB3b3JrIGRpcmVjdGx5IHdpdGggZGF0YSBmcmFtZXMgYW5kIGhhcyB0aGUgYWJpbGl0eSB0byB3b3JrIGRpcmVjdGx5IHdpdGggZGF0YSBzdG9yZWQgaW4gYW4gZXh0ZXJuYWwgZGF0YWJhc2UuIFdlIGNhbiBjb25kdWN0IHF1ZXJpZXMgb24gdGhlIGRhdGFiYXNlIGRpcmVjdGx5LCBhbmQgcHVsbCBiYWNrIGludG8gUiBvbmx5IHdoYXQgd2UgbmVlZCBmb3IgYW5hbHlzaXMuDQoNCiogKip0aWR5cioqIGFkZHJlc3NlcyB0aGUgY29tbW9uIHByb2JsZW0gb2Ygd2FudGluZyB0byByZXNoYXBlIHRoZSBkYXRhIHdpdGggYSBzb3BoaXN0aWNhdGVkIGxheW91dCBmb3IgcGxvdHRpbmcgYW5kIHVzYWdlIGJ5IGRpZmZlcmVudCBSIGZ1bmN0aW9ucy4gDQoNCg0KKiAqKnN0cmluZ3IqKiBkZWFscyB3aXRoIHN0cmluZyB2YXJpYWJsZXMuIEl0IHBsYXlzIGEgYmlnIHJvbGUgaW4gcHJvY2Vzc2luZyByYXcgZGF0YSBpbnRvIGEgY2xlYW5lciBhbmQgZWFzaWx5IHVuZGVyc3RhbmRhYmxlIGZvcm1hdC4gDQoNCiogKipmb3JjYXRzKiogaXMgZGVkaWNhdGVkIHRvIGRlYWxpbmcgd2l0aCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgb3IgZmFjdG9ycy4gQW55b25lIHdobyBoYXMgd29ya2VkIHdpdGggY2F0ZWdvcmljYWwgZGF0YSBrbm93cyB3aGF0IGEgbmlnaHRtYXJlIHRoZXkgY2FuIGJlLg0KICANCiAgDQojIyBQYWNrYWdlcyBmb3IgRGF0YSBJbXBvcnQgYW5kIE1hbmFnZW1lbnQNCg0KKiAqKnRpYmJsZSoqIGlzIGEgbmV3IG1vZGVybiBkYXRhIGZyYW1lIHdpdGggbmljZXIgYmVoYXZpb3IgYXJvdW5kIHByaW50aW5nLCBzdWJzZXR0aW5nLCBhbmQgZmFjdG9yIGhhbmRsaW5nLiBJdCBrZWVwcyBtYW55IGltcG9ydGFudCBmZWF0dXJlcyBvZiB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZSBhbmQgcmVtb3ZlcyBtYW55IG9mIHRoZSBvdXRkYXRlZCBmZWF0dXJlcy4gDQoNCiogKipyZWFkcioqIHBhY2thZ2UgaXMgcmVjZW50bHkgZGV2ZWxvcGVkIHRvIGRlYWwgd2l0aCByZWFkaW5nIGluIGxhcmdlIGZsYXQgZmlsZXMgcXVpY2tseS4gVGhlIHBhY2thZ2UgcHJvdmlkZXMgcmVwbGFjZW1lbnRzIGZvciBmdW5jdGlvbnMgbGlrZSBgcmVhZC50YWJsZSgpYCBhbmQgYHJlYWQuY3N2KCkuYCBUaGUgYW5hbG9nb3VzIGZ1bmN0aW9ucyBpbiB7cmVhZHJ9IGFyZSBgcmVhZF90YWJsZSgpYCBhbmQgYHJlYWRfY3N2KClgLiANCg0KDQoNCiMjIEZ1bmN0aW9uYWwgUHJvZ3JhbW1pbmcgd2l0aCBMaWJyYXJ5ICoqe3B1cnJyfSoqDQogIA0KKiAqKnB1cnJyKiogaXMgYSBuZXcgcGFja2FnZSB0aGF0IGZpbGxzIGluIHRoZSBtaXNzaW5nIHBpZWNlcyBpbiBS4oCZcyBmdW5jdGlvbmFsIHByb2dyYW1taW5nIHRvb2xzLiBUaGlzIGlzIG5vdCBhIGNvZGluZyBjbGFzcy4gV2Ugd2lsbCBub3QgdXNlIOKAmHB1cnJy4oCZIGluIHRoaXMgY2xhc3MuDQoNCg0KIyMgRGF0YSBWaXN1YWxpemF0aW9uIGFuZCBFeHBsb3JhdGlvbg0KDQoqKmdncGxvdDIqKiBpcyBhIHBvd2VyZnVsIGFuZCBmbGV4aWJsZSBSIHBhY2thZ2UgZm9yIHByb2R1Y2luZyBlbGVnYW50IGdyYXBoaWNzLiBUaGUgY29uY2VwdCBiZWhpbmQgYGdncGxvdDJgIGRpdmlkZXMgcGxvdCBpbnRvIHRocmVlIGRpZmZlcmVudCBmdW5kYW1lbnRhbCBwYXJ0czogYFBsb3QgPSBkYXRhICsgQWVzdGhldGljcyArIEdlb21ldHJ5YC4NCg0KVGhlIHByaW5jaXBhbCBjb21wb25lbnRzIG9mIGV2ZXJ5IHBsb3QgY2FuIGJlIGRlZmluZWQgYXMgZm9sbG93Og0KDQoqICoqQWVzdGhldGljcyoqIGlzIHVzZWQgdG8gaW5kaWNhdGUgeCBhbmQgeSB2YXJpYWJsZXMuIEl0IGNhbiBhbHNvIGJlIHVzZWQgdG8gY29udHJvbCB0aGUgY29sb3IsIHRoZSBzaXplIG9yIHRoZSBzaGFwZSBvZiBwb2ludHMsIHRoZSBoZWlnaHQgb2YgYmFycywgZXRjLg0KDQoqICoqR2VvbWV0cnkqKiBkZWZpbmVzIHRoZSB0eXBlIG9mIGdyYXBoaWNzIChoaXN0b2dyYW0sIGJveCBwbG90LCBsaW5lIHBsb3QsIGRlbnNpdHkgcGxvdCwgZG90IHBsb3QsIGV0Yy4pDQoNClRoaXMgd2lsbCBiZSBvbmUgb2YgdGhlIHByaW1hcnkgdG9vbHMgZm9yIHRoaXMgY2xhc3MuDQoNCg0KDQoNCg0KDQojIERhdGEgTWFuYWdlbWVudCB3aXRoIGBkcGx5cmAgIChPcHRpb25hbCkNCg0KV2hhdCB3ZSBjYW4gZG8gaW4gdGhlIHN0YW5kYXJkIFNRTCBjYW4gYWxzbyBiZSBkb25lIHdpdGggYGR5cGxyYC4gRm9yIHRoZSBjb252ZW5pZW5jZSBvZiBpbGx1c3RyYXRpb24sIHdlIHVzZSBhIHNpbXBsZSB3ZWxsLWtub3duIGJ1aWx0LWluIGBpcmlzIGRhdGEgc2V0YC4NCg0KDQojIyBDb21tb24gYGRwbHlyYCBGdW5jdGlvbnMNCg0KVGhlIGZvbGxvd2luZyBpcyB0aGUgbGlzdCBvZiBmdW5jdGlvbnMgaW4gYGRwbHlyYC4NCg0KKiAqKnNlbGVjdCgpKio6IHN1Yi1zZXR0aW5nIGNvbHVtbnMuDQoNClRvIHNlbGVjdCBjb2x1bW5zIG9mIGEgZGF0YSBmcmFtZSwgdXNlIGBzZWxlY3QoKWAuIFRoZSBmaXJzdCBhcmd1bWVudCB0byB0aGlzIGZ1bmN0aW9uIGlzIHRoZSBkYXRhIGZyYW1lIChgaXJpc2ApLCBhbmQgdGhlIHN1YnNlcXVlbnQgYXJndW1lbnRzIGFyZSB0aGUgY29sdW1ucyB0byBrZWVwLiBGb3IgZXhhbXBsZQ0KDQpgYGB7cn0NCmlyaXMucGV0YWwgPSBzZWxlY3QoaXJpcywgUGV0YWwuTGVuZ3RoLCBQZXRhbC5XaWR0aCwgU3BlY2llcykNCnN0cihpcmlzLnBldGFsKQ0KYGBgDQpUbyBzZWxlY3QgYWxsIGNvbHVtbnMgZXhjZXB0IGNlcnRhaW4gb25lcywgcHV0IGEg4oCcLeKAnSBpbiBmcm9udCBvZiB0aGUgdmFyaWFibGUgdG8gZXhjbHVkZSBpdC4gRm9yIGV4YW1wbGUsIHdlICoqZXhjbHVkZSoqIFBldGFsIGluZm9ybWF0aW9uIGFuZCBvbmx5ICoqa2VlcCoqIFNlcGFsIGluZm9ybWF0aW9uLCB3ZSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgY29kZQ0KDQpgYGB7cn0NCmlyaXMuc2VwYWwgPSBzZWxlY3QoaXJpcywgLVBldGFsLkxlbmd0aCwgLVBldGFsLldpZHRoKQ0Kc3RyKGlyaXMuc2VwYWwpDQpgYGANCg0KVGhpcyB3aWxsIHNlbGVjdCBhbGwgdGhlIHZhcmlhYmxlcyBpbiBzdXJ2ZXlzIGV4Y2VwdCBmb3IgYFBldGFsLkxlbmd0aGAsICBhbmQgYFBldGFsLldpZHRoYC4NCg0KKiAqKmZpbHRlcigpKio6IHN1Yi1zZXR0aW5nIHJvd3Mgb24gY29uZGl0aW9ucy4NCg0KRm9yIGV4YW1wbGUsIGlmIHdlIG9ubHkgc2VsZWN0IG9uZSBzcGVjaWVzIGBWZXJzaWNvbG9yYCwgd2UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIGNvZGUuDQoNCmBgYHtyfQ0KdmVyc2ljb2xvciA9IGZpbHRlcihpcmlzLCBTcGVjaWVzID09InZlcnNpY29sb3IiKQ0Kc3VtbWFyeSh2ZXJzaWNvbG9yKQ0KYGBgDQoNCklmIHdlIHN1YnNldCBhIGRhdGEgc2V0IGJ5IHNlbGVjdGluZyBhIGNlcnRhaW4gbnVtYmVyIG9mIGNvbHVtbnMgYW5kIHJvdyB3aXRoIG11bHRpcGxlIGNvbmRpdGlvbnMsIHBpcGUgb3BlcmF0b3IgYCU+JWAgd2lsbCBtYWtlIHN1YnNldHRpbmcgZWFzeS4gRm9yIGV4YW1wbGUsIGlmIHdlIG9ubHkgd2FudCB0byBzdHVkeSBgc2VwYWwgd2lkdGhgIGFuZCBgbGVuZ3RoYCBvZiAgYHNldG9zYWAgd2hlcmUgcGV0YWwgbGVuZ3RoIGlzIGxlc3MgdGhhbiAxLjUuIFRoZSBmb2xsb3dpbmcgY29kZSB1c2luZyBgJT4lYCANCg0KYGBge3J9DQpyZXN1bHRpbmcuc3Vic2V0IDwtIGlyaXMgJT4lDQogICAgICAgICAgICAgZmlsdGVyKFBldGFsLkxlbmd0aCA8IDEuNSwgU3BlY2llcyA9PSJzZXRvc2EiKSAlPiUNCiAgICAgICAgICAgICBzZWxlY3QoU2VwYWwuTGVuZ3RoLCBTZXBhbC5XaWR0aCwgU3BlY2llcykNCnN1bW1hcnkocmVzdWx0aW5nLnN1YnNldCkNCmBgYA0KDQpOb3RlIHRoYXQsIG11bHRpcGxlIGNvbmRpdGlvbmFsIHN0YXRlbWVudHMgYXJlIHNlcGFyYXRlZCBieSBgLGAgb3IgYCZgLiBVc2luZyBgJT4lYCwgd2UgZG9uJ3QgbmVlZCB0byBpbmNsdWRlIHRoZSBkYXRhIHNldCBhcyB0aGUgZmlyc3QgYXJndW1lbnQuDQoNCg0KKiAqKm11dGF0ZSgpKio6IGNyZWF0aW5nIG5ldyBjb2x1bW5zIGJ5IHVzaW5nIGluZm9ybWF0aW9uIGZyb20gb3RoZXIgY29sdW1ucy4NCg0KRnJlcXVlbnRseSB3ZSB3YW50IHRvIGNyZWF0ZSBuZXcgY29sdW1ucyBiYXNlZCBvbiB0aGUgdmFsdWVzIGluIGV4aXN0aW5nIGNvbHVtbnMuIEZvciBleGFtcGxlLCB3ZSB3YW50IHRvIGRlZmluZSB0d28gcmF0aW9zIG9mIHRoZSBzZXBhbCBhbmQgcGV0YWwgd2lkdGhzIGFuZCBzZXBhbCBhbmQgcGV0YWwgbGVuZ3Rocy4gRm9yIHRoaXMsIHdl4oCZbGwgdXNlIG11dGF0ZSgpLg0KDQpgYGB7cn0NCmV4cGFuZGVkLmRhdGEgPC0gaXJpcyAlPiUNCiAgICAgICAgICAgICBtdXRhdGUobGVuZ3RoLnJhdGlvID0gU2VwYWwuTGVuZ3RoL1BldGFsLkxlbmd0aCwNCiAgICAgICAgICAgICAgICAgICAgd2lkdGgucmF0aW8gPSBTZXBhbC5XaWR0aC9QZXRhbC5XaWR0aCkNCnN1bW1hcnkoZXhwYW5kZWQuZGF0YSkNCmBgYA0KDQoqICoqZ3JvdXBfYnkoKSoqIGFuZCAqKnN1bW1hcml6ZSgpKio6IGNyZWF0aW5nIHN1bW1hcnkgc3RhdGlzdGljcyBvbiBncm91cGVkIGRhdGEuDQoNCuKAmGdyb3VwX2J5KCnigJkgaXMgb2Z0ZW4gdXNlZCB0b2dldGhlciB3aXRoIOKAmHN1bW1hcml6ZSgp4oCZLCB3aGljaCBjb2xsYXBzZXMgZWFjaCBncm91cCBpbnRvIGEgc2luZ2xlLXJvdyBzdW1tYXJ5IG9mIHRoYXQgZ3JvdXAuIOKAmGdyb3VwX2J5KCnigJkgdGFrZXMgYXMgYXJndW1lbnRzIHRoZSBjb2x1bW4gbmFtZXMgdGhhdCBjb250YWluIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgZm9yIHdoaWNoIHlvdSB3YW50IHRvIGNhbGN1bGF0ZSB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzLg0KDQpUaGUgZm9sbG93aW5nIGNvZGUgeWllbGRzIGEgc2V0IG9mIHN1bW1hcml6ZWQgc3RhdGlzdGljcyBpbmNsdWRpbmcgdGhlIG1lYW4gb2Ygc2VwYWwgd2lkdGggYW5kIGxlbmd0aCBhcyB3ZWxsIGFzIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgaW4gZWFjaCBvZiB0aGUgdGhyZWUgc3BlY2llcy4NCg0KYGBge3J9DQpzdW1tYXJ5LnN0YXRzIDwtIGlyaXMgJT4lDQogICAgICAgICAgICAgZ3JvdXBfYnkoU3BlY2llcykgJT4lDQogICAgICAgICAgICAgc3VtbWFyaXplKHNlcGFsLndpZHRoLmF2ZyA9IG1lYW4oU2VwYWwuV2lkdGgpLA0KICAgICAgICAgICAgICAgICAgICAgICBzZXBhbC5sZW5ndGguYXZnID0gbWVhbihTZXBhbC5MZW5ndGgpLA0KICAgICAgICAgICAgICAgICAgICAgICBjb3JyLnNlcGFsID0gY29yKFNlcGFsLkxlbmd0aCwgU2VwYWwuV2lkdGgpKSANCnN1bW1hcnkuc3RhdHMNCmBgYA0KDQpBbGwgUiBmdW5jdGlvbnMgc3VjaCBhcyBgbWluKClgLCBgbWF4KClgLCAgdGhhdCB5aWVsZCBzdW1tYXJpemVkIHN0YXRpc3RpY3MgY2FuIGJlIHVzZWQgd2l0aCBgc3VtbWFyaXplKClgLiBXZSBjYW4gYWxzbyBmaWx0ZXIgb3V0IHNvbWUgb2JzZXJ2YXRpb25zIGJlZm9yZSB3ZSBjb21wdXRlIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MuDQoNCmBgYHtyfQ0Kc3VtbWFyeS5zdGF0cy5maWx0ZXJpbmcgPC0gaXJpcyAlPiUNCiAgICAgICAgICAgICBmaWx0ZXIoUGV0YWwuTGVuZ3RoIDwgNSkgJT4lDQogICAgICAgICAgICAgZ3JvdXBfYnkoU3BlY2llcykgJT4lDQogICAgICAgICAgICAgc3VtbWFyaXplKHNlcGFsLndpZHRoLmF2ZyA9IG1lYW4oU2VwYWwuV2lkdGgpLA0KICAgICAgICAgICAgICAgICAgICAgICBzZXBhbC5sZW5ndGguYXZnID0gbWVhbihTZXBhbC5MZW5ndGgpLA0KICAgICAgICAgICAgICAgICAgICAgICBjb3JyLnNlcGFsID0gY29yKFNlcGFsLkxlbmd0aCwgU2VwYWwuV2lkdGgpKSANCnN1bW1hcnkuc3RhdHMuZmlsdGVyaW5nDQpgYGANCg0KKiAqKmFycmFuZ2UoKSoqOiBzb3J0aW5nIHJlc3VsdHMuDQoNClRvIHNvcnQgaW4gZGVzY2VuZGluZyBvcmRlciwgd2UgbmVlZCB0byBhZGQgdGhlIGBkZXNjKClgIGZ1bmN0aW9uLiBJZiB3ZSB3YW50IHRvIHNvcnQgdGhlIHJlc3VsdHMgYnkgZGVjcmVhc2luZyB0aGUgb3JkZXIgb2YgbWVhbiB3ZWlnaHQuDQoNCmBgYHtyfQ0Kc3VtbWFyeS5zdGF0cy5zb3J0IDwtIGlyaXMgJT4lDQogICAgICAgICAgICAgZmlsdGVyKFBldGFsLkxlbmd0aCA8IDUpICU+JQ0KICAgICAgICAgICAgIGdyb3VwX2J5KFNwZWNpZXMpICU+JQ0KICAgICAgICAgICAgIHN1bW1hcml6ZShzZXBhbC53aWR0aC5hdmcgPSBtZWFuKFNlcGFsLldpZHRoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgc2VwYWwubGVuZ3RoLmF2ZyA9IG1lYW4oU2VwYWwuTGVuZ3RoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgY29yci5zZXBhbCA9IGNvcihTZXBhbC5MZW5ndGgsIFNlcGFsLldpZHRoKSkgJT4lDQogICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhjb3JyLnNlcGFsKSkNCnN1bW1hcnkuc3RhdHMuc29ydA0KYGBgDQoNClRoZSByZXN1bHRpbmcgZGF0YSBzZXQgY2FuIGFsc28gYmUgc29ydGVkIGJ5IG11bHRpcGxlIHZhcmlhYmxlcy4NCg0KDQoqICoqY291bnQoKSoqOiBjb3VudGluZyBkaXNjcmV0ZSB2YWx1ZXMuDQoNCldoZW4gd29ya2luZyB3aXRoIGRhdGEsIHdlIG9mdGVuIHdhbnQgdG8ga25vdyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBmb3VuZCBmb3IgZWFjaCBmYWN0b3Igb3IgY29tYmluYXRpb24gb2YgZmFjdG9ycy4gRm9yIHRoaXMgdGFzaywgYGRwbHlyYCBwcm92aWRlcyBgY291bnQoKWAuIEZvciBleGFtcGxlLCBpZiB3ZSB3YW50ZWQgdG8gY291bnQgdGhlIG51bWJlciBvZiByb3dzIG9mIGRhdGEgZm9yIGVhY2ggc3BlY2llcyBhZnRlciB3ZSBmaWx0ZXIgb3V0IGFsbCByZWNvcmRzIHdpdGggUGV0YWwgTGVuZ3RoIDwgNSwgd2Ugd291bGQgZG86DQoNCmBgYHtyfQ0Kc3VtbWFyeS5jb3VudCA8LSBpcmlzICU+JQ0KICAgICAgICAgICAgIGZpbHRlcihQZXRhbC5MZW5ndGggPCA1KSAlPiUNCiAgICAgICAgICAgICBncm91cF9ieShTcGVjaWVzKSAlPiUNCiAgICAgICAgICAgICBzdW1tYXJpemUoY291bnQgPSBuKCksIHNvcnQgPSBUUlVFKSANCnN1bW1hcnkuY291bnQNCmBgYA0KDQpJZiB3ZSB3YW50ZWQgdG8gY291bnQgYSBjb21iaW5hdGlvbiBvZiBmYWN0b3JzLCBzYXkgYGZhY3RvciBBYCBhbmQgYGZhY3RvciBCYCwgd2Ugd291bGQgc3BlY2lmeSB0aGUgZmlyc3QgYW5kIHRoZSBzZWNvbmQgZmFjdG9yIGFzIHRoZSBhcmd1bWVudHMgb2YgYGNvdW50KGZhY3RvciBBLCBmYWN0b3IgQilgLg0KDQoNCiMjIFJlc2hhcGluZyBGdW5jdGlvbnMgaW4gYHRpZHlyYA0KDQpUaGUgYHRpZHlyYCBwYWNrYWdlIGNvbXBsZW1lbnRzIGBkcGx5cmAgcGVyZmVjdGx5LiBJdCBib29zdHMgdGhlIHBvd2VyIG9mIGRwbHlyIGZvciBkYXRhIG1hbmlwdWxhdGlvbiBhbmQgcHJlLXByb2Nlc3NpbmcuIFRvIGlsbHVzdHJhdGUgaG93IHRvIHVzZSB0aGVzZSBmdW5jdGlvbnMsIHdlIGNvbnNpZGVyIGRlZmluaW5nIGEgc3Vic2V0IGZyb20gYGlyaXNgIHRoYXQgY29udGFpbnMgb25seSB0d28gdmFyaWFibGVzOiBTZXBhbCBMZW5ndGggYW5kIFNwZWNpZXMuDQoNCmBgYHtyfQ0KbGlmZS5leHBlY3RhbmN5IDwtcmVhZC5jc3YoImxpZmVfZXhwZWN0YW5jeV95ZWFycy5jc3YiKQ0KbGlmZS5leHBlY3RhbmN5WzE6NSwgMToxMF0NCmBgYA0KDQpgc3ViLmlyaXNgIGlzIGNhbGxlZCBhICoqbG9uZyB0YWJsZSoqLiBXZSBjYW4gcmVzaGFwZSB0aGlzICoqbG9uZyB0YWJsZSoqIHRvIGEgKip3aWRlIHRhYmxlKiogdXNpbmcgYHNwcmVhZCgpYCBmdW5jdGlvbi4NCg0KDQoqICoqZ2F0aGVyKCkqKjogVGhlIGZ1bmN0aW9uIOKAnGdhdGhlcnPigJ0gbXVsdGlwbGUgY29sdW1ucyBmcm9tIHRoZSBkYXRhIHNldCBhbmQgY29udmVydHMgdGhlbSBpbnRvIGtleS12YWx1ZSBwYWlycy4gYGdhdGhlcigpYCB0YWtlcyBmb3VyIHByaW5jaXBhbCBhcmd1bWVudHM6DQoNCiAgKyBkYXRhIHNldA0KICArIHRoZSBrZXkgY29sdW1uIHZhcmlhYmxlIHdlIHdpc2ggdG8gY3JlYXRlIGZyb20gY29sdW1uIG5hbWVzLg0KICArIHRoZSB2YWx1ZXMgY29sdW1uIHZhcmlhYmxlIHdlIHdpc2ggdG8gY3JlYXRlIGFuZCBmaWxsIHdpdGggdmFsdWVzIGFzc29jaWF0ZWQgd2l0aCB0aGUga2V5Lg0KICANCmBUaGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgd2UgdXNlIHRvIGZpbGwgdGhlIGtleSB2YXJpYWJsZSAob3IgdG8gZHJvcClgLg0KDQpIZXJlIHdlIGV4Y2x1ZGUgYGNvdW50cnlgIGZyb20gYmVpbmcgYGdhdGhlcigpYGVkLg0KDQpgYGB7cn0NCmxpZmUuZXhwZWN0YW5jeS5sb25nIDwtIGxpZmUuZXhwZWN0YW5jeSAlPiUNCiAgZ2F0aGVyKGtleSA9ICJZZWFyIiwgICAgICAgIyB0aGUgY29sdW1uIG5hbWVzIG9mIHRoZSB3aWRlIHRhYmxlDQogICAgICAgICB2YWx1ZSA9ICJsaWZlRXhwIiwgICMgdGhlIG51bWVyaWNhbCB2YWx1ZXMgb2YgdGhlIHRhYmxlDQogICAgICAgICAtIGNvdW50cnksICAgICAgICAgICMgZHJvcCBjb3VudHJ5IHZhcmlhYmxlOiBpdHMgdmFsdWUgd2lsbCBub3QgYmUgZ2F0aGVyZWQgKHN0YWNrZWQpIQ0KICAgICAgICAgbmEucm0gPSBUUlVFKSAgICAgICAjIHJlbW92aW5nIHJlY29yZHMgd2l0aCBtaXNzaW5nIHZhbHVlcw0KIyMNCmhlYWQobGlmZS5leHBlY3RhbmN5LmxvbmcpDQpgYGANCg0KV2UgY2FuIHVzZSBgc3Vic3RyKClgIHRvIHJlbW92ZSBgWGAgZnJvbSB0aGUgdmFyaWFibGUgYFllYXJgIGFzIHNob3duIGluIHRoZSBmb2xsb3dpbmcgY29kZS4NCg0KYGBge3J9DQpjb3JyZWN0LmxpZmUuZXhwLmRhdGEgPC0gbGlmZS5leHBlY3RhbmN5LmxvbmcgJT4lDQogICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKHllYXIgPSBzdWJzdHIoWWVhciwyLDUpKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLVllYXIpDQpoZWFkKGNvcnJlY3QubGlmZS5leHAuZGF0YSkNCmBgYA0KDQoNCkZvciBpbGx1c3RyYXRpdmUgcHVycG9zZXMsIHdlIGxvb2sgYXQgYSBzbWFsbCBzdWJzZXQgb2YgdGhlIGlyaXMgZGF0YSBzZXQuDQogDQpgYGB7cn0NCiBtaW5pLmlyaXMgPC0gaXJpc1tjKDEsIDUxLCAxMDEpLCBdDQogbWluaS5pcmlzDQpgYGANCg0KV2UgbGlzdCAoYHNlbGVjdGApIHRoZSBjb2x1bW5zIHRvIGJlIHN0YWNrZWQgZXhwbGljaXRseSBhcyBhcmd1bWVudHMgb2YgYGdhdGhlcigpYCBpbiB0aGUgZm9sbG93aW5nIGNvZGUuDQoNCmBgYHtyfQ0KbWluaS5pcmlzLncybCA8LSBtaW5pLmlyaXMgJT4lDQogICAgICAgICAgZ2F0aGVyKGtleSA9ICJmbG93ZXIuYXR0IiwgdmFsdWUgPSAibWVhc3VyZW1lbnQiLA0KICAgICAgICAgICBTZXBhbC5MZW5ndGgsIFNlcGFsLldpZHRoLCBQZXRhbC5MZW5ndGgsIFBldGFsLldpZHRoKQ0KbWluaS5pcmlzLncybA0KYGBgDQoNCldlIGNhbiBhbHNvIHVzZSBgIi0iYCBvcGVyYXRvciB0byBleGNsdWRlIHRoZSBjb2x1bW4ocykgdG8gYmUgYGdhdGhlcigpZWRgIHRvIG1ha2UgdGhlIGNvZGUgY2xlYW5lci4NCg0KYGBge3J9DQptaW5pLmlyaXMudzJsMCA8LSBtaW5pLmlyaXMgJT4lDQogICAgICAgICAgZ2F0aGVyKGtleSA9ICJmbG93ZXIuYXR0IiwgdmFsdWUgPSAibWVhc3VyZW1lbnQiLCAtU3BlY2llcykNCm1pbmkuaXJpcy53MmwwDQpgYGANCg0KDQoqICoqc3ByZWFkKCkqKjogdGFrZXMgdHdvIGNvbHVtbnMgYW5kIOKAnHNwcmVhZHPigJ0gdGhlbSBpbnRvIG11bHRpcGxlIGNvbHVtbnMuIEl0IHRha2VzIHRocmVlIHByaW5jaXBhbCBhcmd1bWVudHM6DQoNCiAgKyB0aGUgZGF0YQ0KICArIHRoZSBrZXkgY29sdW1uIGAoY2F0ZWdvcmljYWwpYCB2YXJpYWJsZSB3aG9zZSB2YWx1ZXMgd2lsbCBiZWNvbWUgbmV3IGNvbHVtbiBuYW1lcy4NCiAgKyB0aGUgdmFsdWUgY29sdW1uIGAobnVtZXJpY2FsIG9yIGNhdGVnb3JpY2FsKWAgdmFyaWFibGUgd2hvc2UgdmFsdWVzIHdpbGwgZmlsbCB0aGUgbmV3IGNvbHVtbiB2YXJpYWJsZXMuDQoNCkZ1cnRoZXIgYXJndW1lbnRzIGluY2x1ZGUgZmlsbGluZyB3aGljaCwgaWYgc2V0LCBmaWxscyBpbiBtaXNzaW5nIHZhbHVlcyB3aXRoIHRoZSB2YWx1ZSBwcm92aWRlZC4NCg0KDQpgYGB7cn0NCm1pbmkuaXJpcy5sMncgPC0gbWluaS5pcmlzLncybCAlPiUNCiAgc3ByZWFkKGtleSA9ICJmbG93ZXIuYXR0IiwgdmFsdWUgPSAibWVhc3VyZW1lbnQiKQ0KaGVhZChtaW5pLmlyaXMubDJ3KQ0KYGBgDQo=