Data Wrangling In this workshop we will explore data
manipulation and techniques for summarizing data using the library
dplyr.
#If you need to install the tidyverse use "install.packages("tidyverse"), otherwise
library(tidyverse) # loads readr, dplyr and many others libraries useful for working with data including ggplot2
In this exercise we will learn to 1) Read data from a CSV file 2)
Identify and convert data types in your data file 3) Manipulate data
frames 4) Extract subsets of data using base R 5) Extract subsets of
data using ‘dplyr’ 6) Add new variables to a data frame, select, sort,
aggregate and summarize the data in a data frame.
Working with Data Sets Data frames are the most common and
convenient data objects to work with in R. However, you may also run
across matrices and lists which are conceptually not too different from
a data frame and much of the logic you learn from working with data
frames can be translated among different data objects.
Reminder 2 ways to read in data from a csv file stored on
your computer
# base R method:
mydata <- read.csv("filename.csv", stringsAsFactors = FALSE,
strip.white = TRUE, na.strings = c("NA", "") )
Warning: cannot open file 'filename.csv': No such file or directoryError in file(file, "rt") : cannot open the connection
To read in data using the readr package from the tidyverse,
everything should be the same as above except you use the function
read_csv()
# using readr package
mydata <- read_csv("mammals.csv")
Rows: 5731 Columns: 7── Column specification ──────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (6): continent, status, order, family, genus, species
dbl (1): mass.grams
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Checking your Data
After you have imported your data there are a variety of tools that
you can use to visualize the data frame to make sure it is what you were
expecting. Here are a list of the most helpful…
glimpse(mydata)
Rows: 5,731
Columns: 7
$ continent <chr> "AF", "AF", "AF", "AF", "AF", "AF", "AF", "AF", "AF", "AF", "AF", "AF", "…
$ status <chr> "extant", "extant", "extant", "extant", "extant", "extant", "extinct", "e…
$ order <chr> "Artiodactyla", "Artiodactyla", "Artiodactyla", "Artiodactyla", "Artiodac…
$ family <chr> "Bovidae", "Bovidae", "Bovidae", "Bovidae", "Bovidae", "Bovidae", "Bovida…
$ genus <chr> "Addax", "Aepyceros", "Alcelaphus", "Ammodorcas", "Ammotragus", "Antidorc…
$ species <chr> "nasomaculatus", "melampus", "buselaphus", "clarkei", "lervia", "marsupia…
$ mass.grams <dbl> 70000.3, 52500.1, 171001.5, 28049.8, 48000.0, 39049.9, 34000.0, 40000.0, …
Variable types in your data
When you read your data into R, the program automatically classifies
each of your variables (i.e. columns) into data types based on the
objects in the column.
Columns with only numbers are made into numeric or integer
variables (dbl=double-precision floating-point format).
Columns that have any non-numeric characters (even its supposed
to be a numeric data column and there was a data entry error) is read in
as either characters (read_csv) or factors (read.csv). By default,
read.csv() converts character variables into factors, which can be
annoying to work with. Circumvent this by specifying stringsAsFactors =
FALSE in the read.csv() call.
Factors are categorical variables whose categories represent levels.
These levels have names, but they additionally have a numeric
interpretation. If a variable A has 3 categories “a”, “b”, and “c”, R
will order the levels alphabetically, by default, and give them the
corresponding numerical interpretations 1, 2, and 3. This will determine
the order that the categories appear in graphs and tables.
A <- factor( c("c","a","b"))
You can always change the order of the levels. For example, if you
want “c” to be first (e.g., because it refers to the control group), set
the order as follows: A <- factor(A, levels = c(“c”,“a”,“b”))
A <- factor(A, levels = c("c","a","b"))
Reclassifying your data If your data have not been
classified correctly you can convert among types. We will learn this
below.
Saving your data frame You can also save a manipulated
version of a data frame or a new set of data (e.g. from a simulation) as
a csv file.
write_csv(mydata, path = "/directoryname/mammals.csv")
Error: Cannot open file for writing:
* 'C:\directoryname\mammals.csv'
Tips for creating your Spreadsheet file so that it plays
nicely with R
It is easiest to enter data using a spreadsheet and use the standard
approach of columns for variables and rows for individual sampling
units. Carefully considering what you should put into your spreadsheet
may help save you some time and frustration.
- Column names should be brief but informative names for variable
using plain text
- Detailed explanations of variables can be kept in a separate
metadata (text file).
- You should avoid spaces in variable names – use a dot or underscore
instead (e.g., mass.grams or mass_grams).
- Leave missing cells (i.e. no data) blank and avoid non-numeric
characters in columns of numeric data. (a period, etc. does not need to
be added for missing data)
If you enter in a character for missing data, R will assume that the
entire column is non-numeric. Similarly, avoid using a question mark
“12.67?” to indicate a number you are not sure about…add a notes
column.
- Do not put in a zero for missing data…zeros are assumed to be
data!
- Dates should be in international format (YYYY-MM-DD) or use separate
columns for year, month and day.
- Avoid commas in your data set entirely, because they are column
delimiters in your .csv file.
There are two common layouts for data: “Long” vs “wide” layouts.
Wide layout Plot Site species1 species2 species3 1 A 0 12 4
2 A 88 2 0 3 B 12 4 1
…
Long layout Plot Site Species Number 1 A 1 0 1 A 2 12 1 A 3
4 2 A 1 88 2 A 2 2 2 A 3 0 3 B 1 12 3 B 2 4 3 B 3 1 …
In general a “long” layout is recommended for conducting analyses in
R, rather than a “wide” layout. However the “wide layout” can sometimes
be more feasible for accurate data entry. You can convert between these,
however before learning how to do that there are a some important
utility function for data wrangling that you need to be familiar
with.
Data wrangling with dplyr and tidyr
The package dplyr has been said to be the most perfectly
suited package for streamlining workflow for real analytics. Its utility
comes from the fact that it uses a combination of five primary verbs
(i.e. functions or commands) and a process called chaining.
The five verbs are filter() select()
mutate() arrange()
summarize()
library(dplyr)
library(ggplot2)
glimpse(diamonds)
Rows: 53,940
Columns: 10
$ carat <dbl> 0.23, 0.21, 0.23, 0.29, 0.31, 0.24, 0.24, 0.26, 0.22, 0.23, 0.30, 0.23, 0.22…
$ cut <ord> Ideal, Premium, Good, Premium, Good, Very Good, Very Good, Very Good, Fair, …
$ color <ord> E, E, E, I, J, J, I, H, E, H, J, J, F, J, E, E, I, J, J, J, I, E, H, J, J, G…
$ clarity <ord> SI2, SI1, VS1, VS2, SI2, VVS2, VVS1, SI1, VS2, VS1, SI1, VS1, SI1, SI2, SI2,…
$ depth <dbl> 61.5, 59.8, 56.9, 62.4, 63.3, 62.8, 62.3, 61.9, 65.1, 59.4, 64.0, 62.8, 60.4…
$ table <dbl> 55, 61, 65, 58, 58, 57, 57, 55, 61, 61, 55, 56, 61, 54, 62, 58, 54, 54, 56, …
$ price <int> 326, 326, 327, 334, 335, 336, 336, 337, 337, 338, 339, 340, 342, 344, 345, 3…
$ x <dbl> 3.95, 3.89, 4.05, 4.20, 4.34, 3.94, 3.95, 4.07, 3.87, 4.00, 4.25, 3.93, 3.88…
$ y <dbl> 3.98, 3.84, 4.07, 4.23, 4.35, 3.96, 3.98, 4.11, 3.78, 4.05, 4.28, 3.90, 3.84…
$ z <dbl> 2.43, 2.31, 2.31, 2.63, 2.75, 2.48, 2.47, 2.53, 2.49, 2.39, 2.73, 2.46, 2.33…
Filter
The filter() command allows you to subset a data set
only retaining data for rows that you are interested in. For example, we
will use one of R's built in data sets on diamond cuts to
illustrate different aspects of the packages utilities. Lets load and
examine the data set first.
Okay, lets use the filtering command to subset these data so that we
only retain values for the cases where cut variable is
"ideal".
d1=filter(diamonds,cut=="Ideal")
d1
Select Similarly we can choose only a subset of the
columns (variables) in the data frame. For example, we may want to
simplify things by creating a new working data frame that only has the
variables of interest for a given analysis. In this case lets create a
data set that only retains the variables:
“cut”,“carat”,“color”,“price”,and “clarity”.
This is easily accomplished using the select()
command.
d2=select(diamonds, carat, cut, color, price, clarity)
d2
Mutate Another very common task in data analytic or
any programming task is adding variables. We can do this using the
mutate() command. For example, we might want to add a
variable that reflects the costs of diamonds per carat of quality.
d3=mutate(diamonds,price_per=price/carat)
d3
Arrange
arrange() works like an ordering command, but using
`arrange()’makes the syntax much simpler.
Lets make up a data set to illustrate this function.
scramble=data.frame(num_var = c(2,3,5,1,4))
arrange(scramble,num_var)
arrange(scramble,desc(num_var))
Summarize Finally the summarize() command does
exactly what it sounds like it does…it allows you to generate summaries
or summarized versions of the data. For example we can use it to
calculate a simple mean using our subset data set from above.
summarize(diamonds, avg_price = mean(price, na.rm = TRUE))
group_by
We can also use this to generate more complicated summaries of the
data using a sub function called group_by(). For example we
can go back to the original diamonds data set and summarize the data by
calculating means according to all cut types.
head(diamonds)
d1=group_by(diamonds,cut,color)
summarize(d1, avg_price = mean(price, na.rm = TRUE),sd.price=sd(price,na.rm=TRUE))
`summarise()` has grouped output by 'cut'. You can override using the `.groups` argument.
Chains or Pipes The real power of this library is
not fully realized however until you start chaining
commands together. You can chain together different verbs of
dplyr using the %>% operator. All this
operator does is allow you to connect commands together so that the
output of one command becomes the input for the next down a chain. For
example we can do all the steps above on the diamonds data set in a
single chain of commands.
groups
function (x)
{
UseMethod("groups")
}
<bytecode: 0x00000175f87ccf28>
<environment: namespace:dplyr>
final.diamonds= diamonds %>%
filter(cut=="Ideal") %>%
select(carat, cut, color, price, clarity) %>%
mutate(price_per_carat = price/carat)
This chained set of syntax literally says: – “Take the diamonds data
set’” – “Then filter it, keeping only the rows where ‘cut’ equals
‘Ideal’” – “Then select specific variables, ‘carat’, ‘cut’, ‘color’,
‘price, ‘clarity’” – “Then create a new variable, ‘price_per_carat’
using ‘mutate()’”
Finally, dplyr can also be a powerful tool for data
exploration when paired with ggplot. Its power comes from
the fact that you can chain together dplyr commands and ggplot commands.
For example we can create a box plot for just the ideal diamonds by
diamonds %>% # Start with the 'diamonds' dataset
filter(cut == "Ideal") %>% # Then, filter down to rows where cut == Ideal
ggplot(aes(x=color,y=price)) + # Then, plot using ggplot
geom_boxplot()

diamonds %>% # Start with the 'diamonds' dataset
filter(cut == "Premium") %>% # Then, filter down to rows where cut == Ideal
ggplot(aes(x=carat,y=price)) + # Then, plot using ggplot
geom_point()

or a histogram by
diamonds %>% # Start with the 'diamonds' dataset
filter(cut == "Ideal") %>% # Then, filter down to rows where cut == Ideal
ggplot(aes(price)) + # Then, plot using ggplot
geom_histogram() + # and plot histograms
facet_wrap(~ color) # in a 'small multiple' plot, broken out by 'color'

Wide to Long and Back again In R most functions
expect data to be in a long format rather than a wide format, however
ease of data entry and data formatted for some other statistical
software (e.g. SPSS) may result in data in the wide format. To deal with
this problem there are some nifty methods using the
gather() and spread() functions from the
tidyr library.
First, we will generate (simulate) data that is collected in wide and
in long format. The data are exactly the same for both formats. Then we
can use dplyr to convert each to the other format below.
First we will make the wide formatted data….take note on the
generation of these data…
wide <- read.table(header=TRUE, text='
subject sex control cond1 cond2
1 M 7.9 12.3 10.7
2 F 6.3 10.6 11.1
3 F 9.5 13.1 13.8
4 M 11.5 13.4 12.9
')
Challenge use dplyr to turn subject into a
factor)
# Make sure the subject column is a factor
And now the same data in the long format.
long <- read.table(header=TRUE, text='
subject sex condition measurement
1 M control 7.9
1 M cond1 12.3
1 M cond2 10.7
2 F control 6.3
2 F cond1 10.6
2 F cond2 11.1
3 F control 9.5
3 F cond1 13.1
3 F cond2 13.8
4 M control 11.5
4 M cond1 13.4
4 M cond2 12.9
')
Challenge use dplyr to turn subject into a
factor)
# Make sure the subject column is a factor
To convert the data from wide to long format we will use the
gather function. This function has several key pieces of
input (arguments) that are needed. These are
gather(data,key,value,...sources,factor key) # - data:
Data object # - key: Name of new key column (made from names of data
columns) # - value: Name of new value column # - …: Names of source
columns that contain values # - factor_key: Treat the new key column as
a factor (instead of character vector)
So to implement we just need to run:
require(tidyr)
wide_to_long <- gather(wide, condition, measurement, control:cond2, factor_key=TRUE)
wide_to_long
In this example, the source columns that are gathered are specified
with control:cond2. This means to use all the columns,
positional, between control and cond2. Another
way of doing it is to name the columns individually, as in:
wide_to_long <- gather(wide, condition, measurement, control, cond1, cond2, factor_key=TRUE)
wide_to_long
In some cases you may want to use gather() internally in
some larger function and so you will want to use variables containing
column names rather than the column names themselves. To do this use the
gather_() function instead.
keycol <- "condition"
valuecol <- "measurement"
gathercols <- c("control", "cond1", "cond2")
gather_(wide, keycol, valuecol, gathercols)
To convert our data from the long format to the wide format we do the
same steps except we use the spread function instead of the
gather function.
long_to_wide <- spread(wide_to_long, condition, measurement)
long_to_wide
Contrasting base R and dplyr
Below are some useful data manipulation approaches using base R
functionality and the new functions. It is useful to learn both because
the base R version gives a more intuitive understanding of what th code
is actually doing, where as the new functionality of dply provides some
efficient tools. I often use these interchangeably depending on the
application, I prefer one or the other.
Transform a column (variable) within a data frame
For example, log transform a variable named mass.grams and save the
result as a new variable named logsize in the data frame. (log yields
the natural log, whereas the function log10 yields log base 10.)
mydata$logsize <- log(mydata$body_mass_kg)
Error in log(mydata$body_mass_kg) :
non-numeric argument to mathematical function
Delete a variable from data frame
For example, to delete the variable species from mydata, use
mydata2 <- select(mydata, -name) # dplyr method
Error in `select()`:
! Can't subset columns that don't exist.
✖ Column `name` doesn't exist.
Backtrace:
1. dplyr::select(mydata, -name)
2. dplyr:::select.data.frame(mydata, -name)
5. tidyselect::eval_select(expr(c(...)), .data)
6. tidyselect:::eval_select_impl(...)
15. tidyselect:::vars_select_eval(...)
...
18. tidyselect:::reduce_sels(node, data_mask, context_mask, init = init)
19. tidyselect:::walk_data_tree(new, data_mask, context_mask)
20. tidyselect:::as_indices_sel_impl(...)
21. tidyselect:::as_indices_impl(x, vars, call = call, strict = strict)
22. tidyselect:::chr_as_locations(x, vars, call = call)
Extract a data subset
There are several ways. One is to use indicators inside square
brackets using the following format: mydata[rows, columns]. Be sure to
look after each line below to see what the commands did to the data
set
newdata <- mydata[ , c(2,3)] # all rows, columns 2 and 3 only;
newdata <- mydata[ , -1] # all rows, leave out first column
newdata <- mydata[1:3, 1:2] # first three rows, first two columns
The subset command in base R is easy to use to extract rows and
columns. Use the select argument to select columns (variables). For
example, to pull out rows corresponding to continent AF with logsize
< 25, and the three variables, “continent”,“status”,“order” you could
use the following.
newdata <- subset(mydata, body_mass_kg>= 10 & brain_mass_g < 250)
Error in eval(e, x, parent.frame()) : object 'body_mass_kg' not found
**Sort and order the rows*
To re-order the rows of a data frame mydata to correspond to the
sorted order of one of its variables, say x, use
mydata.x <- mydata[order(mydata$x), ] # base R
Error in base::"order"(...) : argument 1 is not a vector
Challenge1
Write a single command (which can span multiple lines and includes
pipes) that will produce a data frame from the diamonds data that only
has the values for color “J” of the variables price, cut and carat.
Challenge 2
Calculate the average price per color which has the highest average
price and which has the shortest average price?
Challenge 3
In one piped or chained set of code filter generate a plot that plots
the means and CIs of a new variable called unit_price that is the price
per carat but only for diamonds that were rated as Ideal, Premium, or
Very Good with each color of diamond depicted in a different color and
in a diffrent panel.
LS0tDQp0aXRsZTogIkRhdGEgV3JhbmdsaW5nIg0KYXV0aG9yOiAiTWljaGFlbCBXLiBNY0NveSINCmRhdGU6ICJMYXN0IHVwZGF0ZWQgb24gYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQoqKkRhdGEgV3JhbmdsaW5nKioNCkluIHRoaXMgd29ya3Nob3Agd2Ugd2lsbCBleHBsb3JlIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCB0ZWNobmlxdWVzIGZvciBzdW1tYXJpemluZyBkYXRhIHVzaW5nIHRoZSBsaWJyYXJ5IGBkcGx5cmAuIA0KDQpgYGB7cn0NCiNJZiB5b3UgbmVlZCB0byBpbnN0YWxsIHRoZSB0aWR5dmVyc2UgdXNlICJpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKSwgb3RoZXJ3aXNlIA0KbGlicmFyeSh0aWR5dmVyc2UpICMgbG9hZHMgcmVhZHIsIGRwbHlyIGFuZCBtYW55IG90aGVycyBsaWJyYXJpZXMgdXNlZnVsIGZvciB3b3JraW5nIHdpdGggZGF0YSBpbmNsdWRpbmcgZ2dwbG90Mg0KYGBgDQoNCkluIHRoaXMgZXhlcmNpc2Ugd2Ugd2lsbCBsZWFybiB0byANCiAgMSkgUmVhZCBkYXRhIGZyb20gYSBDU1YgZmlsZQ0KICAyKSBJZGVudGlmeSBhbmQgY29udmVydCBkYXRhIHR5cGVzIGluIHlvdXIgZGF0YSBmaWxlDQogIDMpIE1hbmlwdWxhdGUgZGF0YSBmcmFtZXMNCiAgNCkgRXh0cmFjdCBzdWJzZXRzIG9mIGRhdGEgdXNpbmcgYmFzZSBSDQogIDUpIEV4dHJhY3Qgc3Vic2V0cyBvZiBkYXRhIHVzaW5nICdkcGx5cicNCiAgNikgQWRkIG5ldyB2YXJpYWJsZXMgdG8gYSBkYXRhIGZyYW1lLCBzZWxlY3QsIHNvcnQsIGFnZ3JlZ2F0ZSBhbmQgc3VtbWFyaXplIHRoZSBkYXRhIGluIGEgZGF0YSBmcmFtZS4gIA0KICANCipXb3JraW5nIHdpdGggRGF0YSBTZXRzKg0KRGF0YSBmcmFtZXMgYXJlIHRoZSBtb3N0IGNvbW1vbiBhbmQgY29udmVuaWVudCBkYXRhIG9iamVjdHMgdG8gd29yayB3aXRoIGluIFIuIEhvd2V2ZXIsIHlvdSBtYXkgYWxzbyBydW4gYWNyb3NzIG1hdHJpY2VzIGFuZCBsaXN0cyB3aGljaCBhcmUgY29uY2VwdHVhbGx5IG5vdCB0b28gZGlmZmVyZW50IGZyb20gYSBkYXRhIGZyYW1lIGFuZCBtdWNoIG9mIHRoZSBsb2dpYyB5b3UgbGVhcm4gZnJvbSB3b3JraW5nIHdpdGggZGF0YSBmcmFtZXMgY2FuIGJlIHRyYW5zbGF0ZWQgYW1vbmcgZGlmZmVyZW50IGRhdGEgb2JqZWN0cy4NCg0KKlJlbWluZGVyKiAyIHdheXMgdG8gcmVhZCBpbiBkYXRhIGZyb20gYSBjc3YgZmlsZSBzdG9yZWQgb24geW91ciBjb21wdXRlcg0KYGBge3J9DQojIGJhc2UgUiBtZXRob2Q6DQpteWRhdGEgPC0gcmVhZC5jc3YoImZpbGVuYW1lLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgIHN0cmlwLndoaXRlID0gVFJVRSwgbmEuc3RyaW5ncyA9IGMoIk5BIiwgIiIpICkNCiMgdXNpbmcgcmVhZHIgcGFja2FnZQ0KbXlkYXRhIDwtIHJlYWRfY3N2KCIvZGlyZWN0b3J5bmFtZS9maWxlbmFtZS5jc3YiLCBuYSA9IGMoIk5BIiwgIiIpKQ0KYGBgDQoNClRvIHJlYWQgaW4gZGF0YSB1c2luZyB0aGUgcmVhZHIgcGFja2FnZSBmcm9tIHRoZSB0aWR5dmVyc2UsIGV2ZXJ5dGhpbmcgc2hvdWxkIGJlIHRoZSBzYW1lIGFzIGFib3ZlIGV4Y2VwdCB5b3UgdXNlIHRoZSBmdW5jdGlvbiByZWFkX2NzdigpDQoNCmBgYHtyfQ0KIyB1c2luZyByZWFkciBwYWNrYWdlDQpteWRhdGEgPC0gcmVhZF9jc3YoIm1hbW1hbHMuY3N2IikNCg0KYGBgDQoNCipDaGVja2luZyB5b3VyIERhdGEqDQoNCkFmdGVyIHlvdSBoYXZlIGltcG9ydGVkIHlvdXIgZGF0YSB0aGVyZSBhcmUgYSB2YXJpZXR5IG9mIHRvb2xzIHRoYXQgeW91IGNhbiB1c2UgdG8gdmlzdWFsaXplIHRoZSBkYXRhIGZyYW1lIHRvIG1ha2Ugc3VyZSBpdCBpcyB3aGF0IHlvdSB3ZXJlIGV4cGVjdGluZy4gSGVyZSBhcmUgYSBsaXN0IG9mIHRoZSBtb3N0IGhlbHBmdWwuLi4NCg0KYGBge3J9DQpnbGltcHNlKG15ZGF0YSkNCmBgYA0KDQoqVmFyaWFibGUgdHlwZXMgaW4geW91ciBkYXRhKg0KDQpXaGVuIHlvdSByZWFkIHlvdXIgZGF0YSBpbnRvIFIsIHRoZSBwcm9ncmFtIGF1dG9tYXRpY2FsbHkgY2xhc3NpZmllcyBlYWNoIG9mIHlvdXIgdmFyaWFibGVzIChpLmUuIGNvbHVtbnMpIGludG8gZGF0YSB0eXBlcyBiYXNlZCBvbiB0aGUgb2JqZWN0cyBpbiB0aGUgY29sdW1uLiANCg0KMS4gQ29sdW1ucyB3aXRoIG9ubHkgbnVtYmVycyBhcmUgbWFkZSBpbnRvIG51bWVyaWMgb3IgaW50ZWdlciB2YXJpYWJsZXMgKGRibD1kb3VibGUtcHJlY2lzaW9uIGZsb2F0aW5nLXBvaW50IGZvcm1hdCkuDQoNCjIuIENvbHVtbnMgdGhhdCBoYXZlIGFueSBub24tbnVtZXJpYyBjaGFyYWN0ZXJzIChldmVuIGl0cyBzdXBwb3NlZCB0byBiZSBhIG51bWVyaWMgZGF0YSBjb2x1bW4gYW5kIHRoZXJlIHdhcyBhIGRhdGEgZW50cnkgZXJyb3IpIGlzIHJlYWQgaW4gYXMgZWl0aGVyIGNoYXJhY3RlcnMgKHJlYWRfY3N2KSAgb3IgZmFjdG9ycyAocmVhZC5jc3YpLiBCeSBkZWZhdWx0LCByZWFkLmNzdigpIGNvbnZlcnRzIGNoYXJhY3RlciB2YXJpYWJsZXMgaW50byBmYWN0b3JzLCB3aGljaCBjYW4gYmUgYW5ub3lpbmcgdG8gd29yayB3aXRoLiBDaXJjdW12ZW50IHRoaXMgYnkgc3BlY2lmeWluZyBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UgaW4gdGhlIHJlYWQuY3N2KCkgY2FsbC4NCg0KRmFjdG9ycyBhcmUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdob3NlIGNhdGVnb3JpZXMgcmVwcmVzZW50IGxldmVscy4gVGhlc2UgbGV2ZWxzIGhhdmUgbmFtZXMsIGJ1dCB0aGV5IGFkZGl0aW9uYWxseSBoYXZlIGEgbnVtZXJpYyBpbnRlcnByZXRhdGlvbi4gSWYgYSB2YXJpYWJsZSBBIGhhcyAzIGNhdGVnb3JpZXMg4oCcYeKAnSwg4oCcYuKAnSwgYW5kIOKAnGPigJ0sIFIgd2lsbCBvcmRlciB0aGUgbGV2ZWxzIGFscGhhYmV0aWNhbGx5LCBieSBkZWZhdWx0LCBhbmQgZ2l2ZSB0aGVtIHRoZSBjb3JyZXNwb25kaW5nIG51bWVyaWNhbCBpbnRlcnByZXRhdGlvbnMgMSwgMiwgYW5kIDMuIFRoaXMgd2lsbCBkZXRlcm1pbmUgdGhlIG9yZGVyIHRoYXQgdGhlIGNhdGVnb3JpZXMgYXBwZWFyIGluIGdyYXBocyBhbmQgdGFibGVzLiANCg0KYGBge3J9DQpBIDwtIGZhY3RvciggYygiYyIsImEiLCJiIikpDQoNCmBgYA0KDQpZb3UgY2FuIGFsd2F5cyBjaGFuZ2UgdGhlIG9yZGVyIG9mIHRoZSBsZXZlbHMuIEZvciBleGFtcGxlLCBpZiB5b3Ugd2FudCDigJxj4oCdIHRvIGJlIGZpcnN0IChlLmcuLCBiZWNhdXNlIGl0IHJlZmVycyB0byB0aGUgY29udHJvbCBncm91cCksIHNldCB0aGUgb3JkZXIgYXMgZm9sbG93czogIEEgPC0gZmFjdG9yKEEsIGxldmVscyA9IGMoImMiLCJhIiwiYiIpKQ0KDQpgYGB7cn0NCkEgPC0gZmFjdG9yKEEsIGxldmVscyA9IGMoImMiLCJhIiwiYiIpKQ0KYGBgDQoNCipSZWNsYXNzaWZ5aW5nIHlvdXIgZGF0YSoNCklmIHlvdXIgZGF0YSBoYXZlIG5vdCBiZWVuIGNsYXNzaWZpZWQgY29ycmVjdGx5IHlvdSBjYW4gY29udmVydCBhbW9uZyB0eXBlcy4gIFdlIHdpbGwgbGVhcm4gdGhpcyBiZWxvdy4NCg0KKlNhdmluZyB5b3VyIGRhdGEgZnJhbWUqDQpZb3UgY2FuIGFsc28gc2F2ZSBhIG1hbmlwdWxhdGVkIHZlcnNpb24gb2YgYSBkYXRhIGZyYW1lIG9yIGEgbmV3IHNldCBvZiBkYXRhIChlLmcuIGZyb20gYSBzaW11bGF0aW9uKSBhcyBhIGNzdiBmaWxlLg0KDQpgYGB7cn0NCiMgYmFzZSBSDQp3cml0ZS5jc3YobXlkYXRhLCBmaWxlPSIvZGlyZWN0b3J5bmFtZS9tYW1tYWxzLmNzdiIsIHJvd25hbWVzID0gRkFMU0UpDQp3cml0ZV9jc3YobXlkYXRhLCBwYXRoID0gIi9kaXJlY3RvcnluYW1lL21hbW1hbHMuY3N2IikgICAgICAgICAgDQpgYGANCg0KKipUaXBzIGZvciBjcmVhdGluZyB5b3VyIFNwcmVhZHNoZWV0IGZpbGUgc28gdGhhdCBpdCBwbGF5cyBuaWNlbHkgd2l0aCBSKioNCg0KSXQgaXMgZWFzaWVzdCB0byBlbnRlciBkYXRhIHVzaW5nIGEgc3ByZWFkc2hlZXQgYW5kIHVzZSB0aGUgc3RhbmRhcmQgYXBwcm9hY2ggb2YgY29sdW1ucyBmb3IgdmFyaWFibGVzIGFuZCByb3dzIGZvciBpbmRpdmlkdWFsIHNhbXBsaW5nIHVuaXRzLiBDYXJlZnVsbHkgY29uc2lkZXJpbmcgd2hhdCB5b3Ugc2hvdWxkIHB1dCBpbnRvIHlvdXIgc3ByZWFkc2hlZXQgbWF5IGhlbHAgc2F2ZSB5b3Ugc29tZSB0aW1lIGFuZCBmcnVzdHJhdGlvbi4NCg0KICBhLiBDb2x1bW4gbmFtZXMgc2hvdWxkIGJlIGJyaWVmIGJ1dCBpbmZvcm1hdGl2ZSBuYW1lcyBmb3IgdmFyaWFibGUgdXNpbmcgcGxhaW4gdGV4dCANCiAgYi4gRGV0YWlsZWQgZXhwbGFuYXRpb25zIG9mIHZhcmlhYmxlcyBjYW4gYmUga2VwdCBpbiBhIHNlcGFyYXRlIG1ldGFkYXRhICh0ZXh0IGZpbGUpLiANCiAgYy4gWW91IHNob3VsZCBhdm9pZCBzcGFjZXMgaW4gdmFyaWFibGUgbmFtZXMg4oCTIHVzZSBhIGRvdCBvciB1bmRlcnNjb3JlIGluc3RlYWQgKGUuZy4sIG1hc3MuZ3JhbXMgb3IgbWFzc19ncmFtcykuIA0KICBkLiBMZWF2ZSBtaXNzaW5nIGNlbGxzIChpLmUuIG5vIGRhdGEpIGJsYW5rIGFuZCBhdm9pZCBub24tbnVtZXJpYyBjaGFyYWN0ZXJzIGluIGNvbHVtbnMgb2YgbnVtZXJpYyBkYXRhLg0KICAgIChhIHBlcmlvZCwgZXRjLiBkb2VzIG5vdCBuZWVkIHRvIGJlIGFkZGVkIGZvciBtaXNzaW5nIGRhdGEpDQoNCklmIHlvdSBlbnRlciBpbiBhIGNoYXJhY3RlciBmb3IgbWlzc2luZyBkYXRhLCBSIHdpbGwgYXNzdW1lIHRoYXQgdGhlIGVudGlyZSBjb2x1bW4gaXMgbm9uLW51bWVyaWMuIFNpbWlsYXJseSwgYXZvaWQgdXNpbmcgYSBxdWVzdGlvbiBtYXJrIOKAnDEyLjY3P+KAnSB0byBpbmRpY2F0ZSBhIG51bWJlciB5b3UgYXJlIG5vdCBzdXJlIGFib3V0Li4uYWRkIGEgbm90ZXMgY29sdW1uLiANCiAgDQogIGUuIERvIG5vdCBwdXQgaW4gYSB6ZXJvIGZvciBtaXNzaW5nIGRhdGEuLi56ZXJvcyBhcmUgYXNzdW1lZCB0byBiZSBkYXRhIQ0KICBmLiBEYXRlcyBzaG91bGQgYmUgaW4gaW50ZXJuYXRpb25hbCBmb3JtYXQgKFlZWVktTU0tREQpIG9yIHVzZSBzZXBhcmF0ZSBjb2x1bW5zIGZvciB5ZWFyLCBtb250aCBhbmQgZGF5Lg0KICBnLiBBdm9pZCBjb21tYXMgaW4geW91ciBkYXRhIHNldCBlbnRpcmVseSwgYmVjYXVzZSB0aGV5IGFyZSBjb2x1bW4gZGVsaW1pdGVycyBpbiB5b3VyIC5jc3YgZmlsZS4NCg0KVGhlcmUgYXJlIHR3byBjb21tb24gbGF5b3V0cyBmb3IgZGF0YTogIkxvbmciIHZzICJ3aWRlIiBsYXlvdXRzLiAgDQoNCipXaWRlIGxheW91dCoNClBsb3QgICAgU2l0ZSAgICAgIHNwZWNpZXMxICAgc3BlY2llczIgICBzcGVjaWVzMw0KIDEgICAgICAgIEEgICAgICAgICAgIDAgICAgICAgICAgMTIgICAgICAgICA0DQogMiAgICAgICAgQSAgICAgICAgICA4OCAgICAgICAgICAgMiAgICAgICAgIDANCiAzICAgICAgICBCICAgICAgICAgIDEyICAgICAgICAgICA0ICAgICAgICAgMSAgIA0KLi4uDQoNCipMb25nIGxheW91dCoNClBsb3QgICBTaXRlICBTcGVjaWVzIE51bWJlcg0KIDEgICAgICBBICAgICAgMSAgICAgIDANCiAxICAgICAgQSAgICAgIDIgICAgIDEyDQogMSAgICAgIEEgICAgICAzICAgICAgNA0KIDIgICAgICBBICAgICAgMSAgICAgODgNCiAyICAgICAgQSAgICAgIDIgICAgICAyDQogMiAgICAgIEEgICAgICAzICAgICAgMA0KIDMgICAgICBCICAgICAgMSAgICAgMTINCiAzICAgICAgQiAgICAgIDIgICAgICA0DQogMyAgICAgIEIgICAgICAzICAgICAgMQ0KLi4uDQoNCkluIGdlbmVyYWwgYSDigJxsb25n4oCdIGxheW91dCBpcyByZWNvbW1lbmRlZCBmb3IgY29uZHVjdGluZyBhbmFseXNlcyBpbiBSLCByYXRoZXIgdGhhbiBhIOKAnHdpZGXigJ0gbGF5b3V0LiBIb3dldmVyIHRoZSAid2lkZSBsYXlvdXQiIGNhbiBzb21ldGltZXMgYmUgbW9yZSBmZWFzaWJsZSBmb3IgYWNjdXJhdGUgZGF0YSBlbnRyeS4gWW91IGNhbiBjb252ZXJ0IGJldHdlZW4gdGhlc2UsIGhvd2V2ZXIgYmVmb3JlIGxlYXJuaW5nIGhvdyB0byBkbyB0aGF0IHRoZXJlIGFyZSBhIHNvbWUgaW1wb3J0YW50IHV0aWxpdHkgZnVuY3Rpb24gZm9yIGRhdGEgd3JhbmdsaW5nIHRoYXQgeW91IG5lZWQgdG8gYmUgZmFtaWxpYXIgd2l0aC4gDQoNCg0KKipEYXRhIHdyYW5nbGluZyB3aXRoIGRwbHlyIGFuZCB0aWR5cioqDQoNClRoZSBwYWNrYWdlIGBkcGx5cmAgaGFzIGJlZW4gc2FpZCB0byBiZSB0aGUgbW9zdCBwZXJmZWN0bHkgc3VpdGVkIHBhY2thZ2UgZm9yIHN0cmVhbWxpbmluZyB3b3JrZmxvdyBmb3IgcmVhbCBhbmFseXRpY3MuICBJdHMgdXRpbGl0eSBjb21lcyBmcm9tIHRoZSBmYWN0IHRoYXQgaXQgdXNlcyBhIGNvbWJpbmF0aW9uIG9mIGZpdmUgcHJpbWFyeSB2ZXJicyAoaS5lLiBmdW5jdGlvbnMgb3IgY29tbWFuZHMpIGFuZCBhIHByb2Nlc3MgY2FsbGVkIGNoYWluaW5nLg0KDQpUaGUgZml2ZSB2ZXJicyBhcmUNCmBmaWx0ZXIoKWANCmBzZWxlY3QoKWANCmBtdXRhdGUoKWANCmBhcnJhbmdlKClgDQpgc3VtbWFyaXplKClgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpnbGltcHNlKGRpYW1vbmRzKQ0KYGBgDQoNCioqRmlsdGVyKioNCg0KVGhlIGBmaWx0ZXIoKWAgY29tbWFuZCBhbGxvd3MgeW91IHRvIHN1YnNldCBhIGRhdGEgc2V0IG9ubHkgcmV0YWluaW5nIGRhdGEgZm9yIHJvd3MgdGhhdCB5b3UgYXJlIGludGVyZXN0ZWQgaW4uICBGb3IgZXhhbXBsZSwgd2Ugd2lsbCB1c2Ugb25lIG9mIGBSJ3NgIGJ1aWx0IGluIGRhdGEgc2V0cyBvbiBkaWFtb25kIGN1dHMgdG8gaWxsdXN0cmF0ZSBkaWZmZXJlbnQgYXNwZWN0cyBvZiB0aGUgcGFja2FnZXMgdXRpbGl0aWVzLiBMZXRzIGxvYWQgYW5kIGV4YW1pbmUgdGhlIGRhdGEgc2V0IGZpcnN0Lg0KDQpPa2F5LCBsZXRzIHVzZSB0aGUgZmlsdGVyaW5nIGNvbW1hbmQgdG8gc3Vic2V0IHRoZXNlIGRhdGEgc28gdGhhdCB3ZSBvbmx5IHJldGFpbiB2YWx1ZXMgZm9yIHRoZSBjYXNlcyB3aGVyZSBgY3V0YCB2YXJpYWJsZSBpcyBgImlkZWFsImAuDQoNCmBgYHtyfQ0KZDE9ZmlsdGVyKGRpYW1vbmRzLGN1dD09IklkZWFsIikNCmQxDQpgYGANCg0KKipTZWxlY3QqKg0KU2ltaWxhcmx5IHdlIGNhbiBjaG9vc2Ugb25seSBhIHN1YnNldCBvZiB0aGUgY29sdW1ucyAodmFyaWFibGVzKSBpbiB0aGUgZGF0YSBmcmFtZS4gRm9yIGV4YW1wbGUsIHdlIG1heSB3YW50IHRvIHNpbXBsaWZ5IHRoaW5ncyBieSBjcmVhdGluZyBhIG5ldyB3b3JraW5nIGRhdGEgZnJhbWUgdGhhdCBvbmx5IGhhcyB0aGUgdmFyaWFibGVzIG9mIGludGVyZXN0IGZvciBhIGdpdmVuIGFuYWx5c2lzLiAgSW4gdGhpcyBjYXNlIGxldHMgY3JlYXRlIGEgZGF0YSBzZXQgdGhhdCBvbmx5IHJldGFpbnMgdGhlIHZhcmlhYmxlczoNCg0KImN1dCIsImNhcmF0IiwiY29sb3IiLCJwcmljZSIsYW5kICJjbGFyaXR5Ii4gIA0KDQpUaGlzIGlzIGVhc2lseSBhY2NvbXBsaXNoZWQgdXNpbmcgdGhlIGBzZWxlY3QoKWAgY29tbWFuZC4NCg0KYGBge3J9DQoNCmQyPXNlbGVjdChkaWFtb25kcywgY2FyYXQsIGN1dCwgY29sb3IsIHByaWNlLCBjbGFyaXR5KQ0KZDINCmBgYA0KDQoqKk11dGF0ZSoqDQpBbm90aGVyIHZlcnkgY29tbW9uIHRhc2sgaW4gZGF0YSBhbmFseXRpYyBvciBhbnkgcHJvZ3JhbW1pbmcgdGFzayBpcyBhZGRpbmcgdmFyaWFibGVzLiAgV2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlIGBtdXRhdGUoKWAgY29tbWFuZC4gIEZvciBleGFtcGxlLCB3ZSBtaWdodCB3YW50IHRvIGFkZCBhIHZhcmlhYmxlIHRoYXQgcmVmbGVjdHMgdGhlIGNvc3RzIG9mIGRpYW1vbmRzIHBlciBjYXJhdCBvZiBxdWFsaXR5Lg0KDQpgYGB7cn0NCmQzPW11dGF0ZShkaWFtb25kcyxwcmljZV9wZXI9cHJpY2UvY2FyYXQpDQpkMw0KYGBgDQoNCioqQXJyYW5nZSoqDQoNCmBhcnJhbmdlKClgIHdvcmtzIGxpa2UgYW4gb3JkZXJpbmcgY29tbWFuZCwgYnV0IHVzaW5nIGBhcnJhbmdlKCknbWFrZXMgdGhlIHN5bnRheCBtdWNoIHNpbXBsZXIuICANCg0KTGV0cyBtYWtlIHVwIGEgZGF0YSBzZXQgdG8gaWxsdXN0cmF0ZSB0aGlzIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCnNjcmFtYmxlPWRhdGEuZnJhbWUobnVtX3ZhciA9IGMoMiwzLDUsMSw0KSkNCmFycmFuZ2Uoc2NyYW1ibGUsbnVtX3ZhcikNCmFycmFuZ2Uoc2NyYW1ibGUsZGVzYyhudW1fdmFyKSkNCmBgYA0KDQoqKlN1bW1hcml6ZSoqDQpGaW5hbGx5IHRoZSBzdW1tYXJpemUoKSBjb21tYW5kIGRvZXMgZXhhY3RseSB3aGF0IGl0IHNvdW5kcyBsaWtlIGl0IGRvZXMuLi5pdCBhbGxvd3MgeW91IHRvIGdlbmVyYXRlIHN1bW1hcmllcyBvciBzdW1tYXJpemVkIHZlcnNpb25zIG9mIHRoZSBkYXRhLiAgRm9yIGV4YW1wbGUgd2UgY2FuIHVzZSBpdCB0byBjYWxjdWxhdGUgYSBzaW1wbGUgbWVhbiB1c2luZyBvdXIgc3Vic2V0IGRhdGEgc2V0IGZyb20gYWJvdmUuDQoNCmBgYHtyfQ0Kc3VtbWFyaXplKGRpYW1vbmRzLCBhdmdfcHJpY2UgPSBtZWFuKHByaWNlLCBuYS5ybSA9IFRSVUUpKQ0KYGBgDQoNCioqZ3JvdXBfYnkqKg0KDQpXZSBjYW4gYWxzbyB1c2UgdGhpcyB0byBnZW5lcmF0ZSBtb3JlIGNvbXBsaWNhdGVkIHN1bW1hcmllcyBvZiB0aGUgZGF0YSB1c2luZyBhIHN1YiBmdW5jdGlvbiBjYWxsZWQgYGdyb3VwX2J5KClgLiAgRm9yIGV4YW1wbGUgd2UgY2FuIGdvIGJhY2sgdG8gdGhlIG9yaWdpbmFsIGRpYW1vbmRzIGRhdGEgc2V0IGFuZCBzdW1tYXJpemUgdGhlIGRhdGEgYnkgY2FsY3VsYXRpbmcgbWVhbnMgYWNjb3JkaW5nIHRvIGFsbCBjdXQgdHlwZXMuDQoNCmBgYHtyfQ0KaGVhZChkaWFtb25kcykNCmQxPWdyb3VwX2J5KGRpYW1vbmRzLGN1dCxjb2xvcikNCnN1bW1hcml6ZShkMSwgYXZnX3ByaWNlID0gbWVhbihwcmljZSwgbmEucm0gPSBUUlVFKSxzZC5wcmljZT1zZChwcmljZSxuYS5ybT1UUlVFKSkNCmBgYA0KDQoqKkNoYWlucyBvciBQaXBlcyoqDQpUaGUgcmVhbCBwb3dlciBvZiB0aGlzIGxpYnJhcnkgaXMgbm90IGZ1bGx5IHJlYWxpemVkIGhvd2V2ZXIgdW50aWwgeW91IHN0YXJ0IGBjaGFpbmluZ2AgY29tbWFuZHMgdG9nZXRoZXIuICBZb3UgY2FuIGNoYWluIHRvZ2V0aGVyIGRpZmZlcmVudCB2ZXJicyBvZiBgZHBseXJgIHVzaW5nIHRoZSBgJT4lYCBvcGVyYXRvci4gIEFsbCB0aGlzIG9wZXJhdG9yIGRvZXMgaXMgYWxsb3cgeW91IHRvIGNvbm5lY3QgY29tbWFuZHMgdG9nZXRoZXIgc28gdGhhdCB0aGUgb3V0cHV0IG9mIG9uZSBjb21tYW5kIGJlY29tZXMgdGhlIGlucHV0IGZvciB0aGUgbmV4dCBkb3duIGEgY2hhaW4uICBGb3IgZXhhbXBsZSB3ZSBjYW4gZG8gYWxsIHRoZSBzdGVwcyBhYm92ZSBvbiB0aGUgZGlhbW9uZHMgZGF0YSBzZXQgaW4gYSAgc2luZ2xlIGNoYWluIG9mIGNvbW1hbmRzLg0KDQpgYGB7cn0NCmdyb3Vwcw0KZmluYWwuZGlhbW9uZHM9IGRpYW1vbmRzICU+JQ0KICAgICAgICAgICAgICAgIGZpbHRlcihjdXQ9PSJJZGVhbCIpICU+JQ0KICAgICAgICAgICAgICAgIHNlbGVjdChjYXJhdCwgY3V0LCBjb2xvciwgcHJpY2UsIGNsYXJpdHkpICU+JQ0KICAgICAgICAgICAgICAgIG11dGF0ZShwcmljZV9wZXJfY2FyYXQgPSBwcmljZS9jYXJhdCkNCmBgYA0KVGhpcyBjaGFpbmVkIHNldCBvZiBzeW50YXggbGl0ZXJhbGx5IHNheXM6DQrigJMg4oCcVGFrZSB0aGUgZGlhbW9uZHMgZGF0YSBzZXTigJkg4oCdDQrigJMg4oCcVGhlbiBmaWx0ZXIgaXQsIGtlZXBpbmcgb25seSB0aGUgcm93cyB3aGVyZSDigJhjdXTigJkgZXF1YWxzIOKAmElkZWFs4oCZIOKAnQ0K4oCTIOKAnFRoZW4gc2VsZWN0IHNwZWNpZmljIHZhcmlhYmxlcywg4oCYY2FyYXTigJksIOKAmGN1dOKAmSwg4oCYY29sb3LigJksIOKAmHByaWNlLCDigJhjbGFyaXR54oCZIOKAnQ0K4oCTIOKAnFRoZW4gY3JlYXRlIGEgbmV3IHZhcmlhYmxlLCDigJhwcmljZV9wZXJfY2FyYXTigJkgdXNpbmcg4oCYbXV0YXRlKCnigJkg4oCdDQoNCkZpbmFsbHksIGBkcGx5cmAgY2FuIGFsc28gYmUgYSBwb3dlcmZ1bCB0b29sIGZvciBkYXRhIGV4cGxvcmF0aW9uIHdoZW4gcGFpcmVkIHdpdGggYGdncGxvdGAuICBJdHMgcG93ZXIgY29tZXMgZnJvbSB0aGUgZmFjdCB0aGF0IHlvdSBjYW4gY2hhaW4gdG9nZXRoZXIgZHBseXIgY29tbWFuZHMgYW5kIGdncGxvdCBjb21tYW5kcy4gRm9yIGV4YW1wbGUgd2UgY2FuIGNyZWF0ZSBhIGJveCBwbG90IGZvciBqdXN0IHRoZSBpZGVhbCBkaWFtb25kcyBieQ0KDQpgYGB7cn0NCiAgZGlhbW9uZHMgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3RhcnQgd2l0aCB0aGUgJ2RpYW1vbmRzJyBkYXRhc2V0DQogIGZpbHRlcihjdXQgPT0gIklkZWFsIikgJT4lICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGVuLCBmaWx0ZXIgZG93biB0byByb3dzIHdoZXJlIGN1dCA9PSBJZGVhbA0KICBnZ3Bsb3QoYWVzKHg9Y29sb3IseT1wcmljZSkpICsgICAgICAgICAgICAgICAgICAgICAjIFRoZW4sIHBsb3QgdXNpbmcgZ2dwbG90DQogIGdlb21fYm94cGxvdCgpICAgICAgDQoNCmRpYW1vbmRzICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFN0YXJ0IHdpdGggdGhlICdkaWFtb25kcycgZGF0YXNldA0KICBmaWx0ZXIoY3V0ID09ICJQcmVtaXVtIikgJT4lICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGVuLCBmaWx0ZXIgZG93biB0byByb3dzIHdoZXJlIGN1dCA9PSBJZGVhbA0KICBnZ3Bsb3QoYWVzKHg9Y2FyYXQseT1wcmljZSkpICsgICAgICAgICAgICAgICAgICAgICAjIFRoZW4sIHBsb3QgdXNpbmcgZ2dwbG90DQogIGdlb21fcG9pbnQoKQ0KDQpgYGANCg0Kb3IgYSBoaXN0b2dyYW0gYnkNCg0KYGBge3J9DQpkaWFtb25kcyAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdGFydCB3aXRoIHRoZSAnZGlhbW9uZHMnIGRhdGFzZXQNCiAgZmlsdGVyKGN1dCA9PSAiSWRlYWwiKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAjIFRoZW4sIGZpbHRlciBkb3duIHRvIHJvd3Mgd2hlcmUgY3V0ID09IElkZWFsDQogIGdncGxvdChhZXMocHJpY2UpKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVGhlbiwgcGxvdCB1c2luZyBnZ3Bsb3QNCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFuZCBwbG90IGhpc3RvZ3JhbXMNCiAgICBmYWNldF93cmFwKH4gY29sb3IpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIGEgJ3NtYWxsIG11bHRpcGxlJyBwbG90LCBicm9rZW4gb3V0IGJ5ICdjb2xvcicNCmBgYA0KDQoqKldpZGUgdG8gTG9uZyBhbmQgQmFjayBhZ2FpbioqDQpJbiBSIG1vc3QgZnVuY3Rpb25zIGV4cGVjdCBkYXRhIHRvIGJlIGluIGEgbG9uZyBmb3JtYXQgcmF0aGVyIHRoYW4gYSB3aWRlIGZvcm1hdCwgaG93ZXZlciBlYXNlIG9mIGRhdGEgZW50cnkgYW5kIGRhdGEgZm9ybWF0dGVkIGZvciBzb21lIG90aGVyIHN0YXRpc3RpY2FsIHNvZnR3YXJlIChlLmcuIFNQU1MpIG1heSByZXN1bHQgaW4gZGF0YSBpbiB0aGUgd2lkZSBmb3JtYXQuIFRvIGRlYWwgd2l0aCB0aGlzIHByb2JsZW0gdGhlcmUgYXJlIHNvbWUgbmlmdHkgbWV0aG9kcyB1c2luZyB0aGUgYGdhdGhlcigpYCBhbmQgYHNwcmVhZCgpYCBmdW5jdGlvbnMgZnJvbSB0aGUgYHRpZHlyYCBsaWJyYXJ5Lg0KDQpGaXJzdCwgIHdlIHdpbGwgZ2VuZXJhdGUgKHNpbXVsYXRlKSBkYXRhIHRoYXQgaXMgY29sbGVjdGVkIGluIHdpZGUgYW5kIGluIGxvbmcgZm9ybWF0LiAgVGhlIGRhdGEgYXJlIGV4YWN0bHkgdGhlIHNhbWUgZm9yIGJvdGggZm9ybWF0cy4gVGhlbiB3ZSBjYW4gdXNlIGRwbHlyIHRvIGNvbnZlcnQgZWFjaCB0byB0aGUgb3RoZXIgZm9ybWF0IGJlbG93Lg0KDQpGaXJzdCB3ZSB3aWxsIG1ha2UgdGhlIHdpZGUgZm9ybWF0dGVkIGRhdGEuLi4udGFrZSBub3RlIG9uIHRoZSBnZW5lcmF0aW9uIG9mIHRoZXNlIGRhdGEuLi4NCg0KYGBge3J9DQp3aWRlIDwtIHJlYWQudGFibGUoaGVhZGVyPVRSVUUsIHRleHQ9Jw0KIHN1YmplY3Qgc2V4IGNvbnRyb2wgY29uZDEgY29uZDINCiAgICAgICAxICAgTSAgICAgNy45ICAxMi4zICAxMC43DQogICAgICAgMiAgIEYgICAgIDYuMyAgMTAuNiAgMTEuMQ0KICAgICAgIDMgICBGICAgICA5LjUgIDEzLjEgIDEzLjgNCiAgICAgICA0ICAgTSAgICAxMS41ICAxMy40ICAxMi45DQonKQ0KDQpgYGANCioqQ2hhbGxlbmdlIHVzZSBkcGx5ciB0byB0dXJuIHN1YmplY3QgaW50byBhIGZhY3RvcikqKg0KYGBge3J9DQojIE1ha2Ugc3VyZSB0aGUgc3ViamVjdCBjb2x1bW4gaXMgYSBmYWN0b3INCg0KYGBgDQoNCkFuZCBub3cgdGhlIHNhbWUgZGF0YSBpbiB0aGUgbG9uZyBmb3JtYXQuDQoNCmBgYHtyfQ0KbG9uZyA8LSByZWFkLnRhYmxlKGhlYWRlcj1UUlVFLCB0ZXh0PScNCiBzdWJqZWN0IHNleCBjb25kaXRpb24gbWVhc3VyZW1lbnQNCiAgICAgICAxICAgTSAgIGNvbnRyb2wgICAgICAgICA3LjkNCiAgICAgICAxICAgTSAgICAgY29uZDEgICAgICAgIDEyLjMNCiAgICAgICAxICAgTSAgICAgY29uZDIgICAgICAgIDEwLjcNCiAgICAgICAyICAgRiAgIGNvbnRyb2wgICAgICAgICA2LjMNCiAgICAgICAyICAgRiAgICAgY29uZDEgICAgICAgIDEwLjYNCiAgICAgICAyICAgRiAgICAgY29uZDIgICAgICAgIDExLjENCiAgICAgICAzICAgRiAgIGNvbnRyb2wgICAgICAgICA5LjUNCiAgICAgICAzICAgRiAgICAgY29uZDEgICAgICAgIDEzLjENCiAgICAgICAzICAgRiAgICAgY29uZDIgICAgICAgIDEzLjgNCiAgICAgICA0ICAgTSAgIGNvbnRyb2wgICAgICAgIDExLjUNCiAgICAgICA0ICAgTSAgICAgY29uZDEgICAgICAgIDEzLjQNCiAgICAgICA0ICAgTSAgICAgY29uZDIgICAgICAgIDEyLjkNCicpDQpgYGANCg0KKipDaGFsbGVuZ2UgdXNlIGRwbHlyIHRvIHR1cm4gc3ViamVjdCBpbnRvIGEgZmFjdG9yKSoqDQpgYGB7cn0NCiMgTWFrZSBzdXJlIHRoZSBzdWJqZWN0IGNvbHVtbiBpcyBhIGZhY3Rvcg0KDQpgYGANCg0KVG8gY29udmVydCB0aGUgZGF0YSBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQgd2Ugd2lsbCB1c2UgdGhlIGBnYXRoZXJgIGZ1bmN0aW9uLiAgVGhpcyBmdW5jdGlvbiBoYXMgc2V2ZXJhbCBrZXkgcGllY2VzIG9mIGlucHV0IChhcmd1bWVudHMpIHRoYXQgYXJlIG5lZWRlZC4gVGhlc2UgYXJlDQoNCmBnYXRoZXIoZGF0YSxrZXksdmFsdWUsLi4uc291cmNlcyxmYWN0b3Iga2V5KWANCiAgIyAtIGRhdGE6IERhdGEgb2JqZWN0DQogICMgLSBrZXk6IE5hbWUgb2YgbmV3IGtleSBjb2x1bW4gKG1hZGUgZnJvbSBuYW1lcyBvZiBkYXRhIGNvbHVtbnMpDQogICMgLSB2YWx1ZTogTmFtZSBvZiBuZXcgdmFsdWUgY29sdW1uDQogICMgLSAuLi46IE5hbWVzIG9mIHNvdXJjZSBjb2x1bW5zIHRoYXQgY29udGFpbiB2YWx1ZXMNCiAgIyAtIGZhY3Rvcl9rZXk6IFRyZWF0IHRoZSBuZXcga2V5IGNvbHVtbiBhcyBhIGZhY3RvciAoaW5zdGVhZCBvZiBjaGFyYWN0ZXIgdmVjdG9yKQ0KICANClNvIHRvIGltcGxlbWVudCB3ZSBqdXN0IG5lZWQgdG8gcnVuOg0KYGBge3J9DQpyZXF1aXJlKHRpZHlyKQ0Kd2lkZV90b19sb25nIDwtIGdhdGhlcih3aWRlLCBjb25kaXRpb24sIG1lYXN1cmVtZW50LCBjb250cm9sOmNvbmQyLCBmYWN0b3Jfa2V5PVRSVUUpDQp3aWRlX3RvX2xvbmcNCmBgYA0KDQpJbiB0aGlzIGV4YW1wbGUsIHRoZSBzb3VyY2UgY29sdW1ucyB0aGF0IGFyZSBnYXRoZXJlZCBhcmUgc3BlY2lmaWVkIHdpdGggYGNvbnRyb2w6Y29uZDJgLiBUaGlzIG1lYW5zIHRvIHVzZSBhbGwgdGhlIGNvbHVtbnMsIHBvc2l0aW9uYWwsIGJldHdlZW4gYGNvbnRyb2xgIGFuZCBgY29uZDJgLiBBbm90aGVyIHdheSBvZiBkb2luZyBpdCBpcyB0byBuYW1lIHRoZSBjb2x1bW5zIGluZGl2aWR1YWxseSwgYXMgaW46DQoNCmBgYHtyfQ0Kd2lkZV90b19sb25nIDwtIGdhdGhlcih3aWRlLCBjb25kaXRpb24sIG1lYXN1cmVtZW50LCBjb250cm9sLCBjb25kMSwgY29uZDIsIGZhY3Rvcl9rZXk9VFJVRSkNCndpZGVfdG9fbG9uZw0KYGBgDQoNCkluIHNvbWUgY2FzZXMgeW91IG1heSB3YW50IHRvIHVzZSBgZ2F0aGVyKClgIGludGVybmFsbHkgaW4gc29tZSBsYXJnZXIgZnVuY3Rpb24gYW5kIHNvIHlvdSB3aWxsIHdhbnQgdG8gdXNlIHZhcmlhYmxlcyBjb250YWluaW5nIGNvbHVtbiBuYW1lcyByYXRoZXIgdGhhbiB0aGUgY29sdW1uIG5hbWVzIHRoZW1zZWx2ZXMuIFRvIGRvIHRoaXMgdXNlIHRoZSBgZ2F0aGVyXygpYCBmdW5jdGlvbiBpbnN0ZWFkLg0KDQpgYGB7cn0NCmtleWNvbCA8LSAiY29uZGl0aW9uIg0KdmFsdWVjb2wgPC0gIm1lYXN1cmVtZW50Ig0KZ2F0aGVyY29scyA8LSBjKCJjb250cm9sIiwgImNvbmQxIiwgImNvbmQyIikNCg0KZ2F0aGVyXyh3aWRlLCBrZXljb2wsIHZhbHVlY29sLCBnYXRoZXJjb2xzKQ0KYGBgDQoNClRvIGNvbnZlcnQgb3VyIGRhdGEgZnJvbSB0aGUgbG9uZyBmb3JtYXQgdG8gdGhlIHdpZGUgZm9ybWF0IHdlIGRvIHRoZSBzYW1lIHN0ZXBzIGV4Y2VwdCB3ZSB1c2UgdGhlIGBzcHJlYWRgIGZ1bmN0aW9uIGluc3RlYWQgb2YgdGhlIGBnYXRoZXJgIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCmxvbmdfdG9fd2lkZSA8LSBzcHJlYWQod2lkZV90b19sb25nLCBjb25kaXRpb24sIG1lYXN1cmVtZW50KQ0KbG9uZ190b193aWRlDQpgYGANCg0KKipDb250cmFzdGluZyBiYXNlIFIgYW5kIGRwbHlyKioNCg0KQmVsb3cgYXJlIHNvbWUgdXNlZnVsIGRhdGEgbWFuaXB1bGF0aW9uIGFwcHJvYWNoZXMgdXNpbmcgYmFzZSBSIGZ1bmN0aW9uYWxpdHkgYW5kIHRoZSBuZXcgZnVuY3Rpb25zLiBJdCBpcyB1c2VmdWwgdG8gbGVhcm4gYm90aCBiZWNhdXNlIHRoZSBiYXNlIFIgdmVyc2lvbiBnaXZlcyBhIG1vcmUgaW50dWl0aXZlIHVuZGVyc3RhbmRpbmcgb2Ygd2hhdCB0aCBjb2RlIGlzIGFjdHVhbGx5IGRvaW5nLCB3aGVyZSBhcyB0aGUgbmV3IGZ1bmN0aW9uYWxpdHkgb2YgZHBseSBwcm92aWRlcyBzb21lIGVmZmljaWVudCB0b29scy4gIEkgb2Z0ZW4gdXNlIHRoZXNlIGludGVyY2hhbmdlYWJseSBkZXBlbmRpbmcgb24gdGhlIGFwcGxpY2F0aW9uLCBJIHByZWZlciBvbmUgb3IgdGhlIG90aGVyLg0KDQoqVHJhbnNmb3JtIGEgY29sdW1uICh2YXJpYWJsZSkgd2l0aGluIGEgZGF0YSBmcmFtZSoNCg0KRm9yIGV4YW1wbGUsIGxvZyB0cmFuc2Zvcm0gYSB2YXJpYWJsZSBuYW1lZCBtYXNzLmdyYW1zIGFuZCBzYXZlIHRoZSByZXN1bHQgYXMgYSBuZXcgdmFyaWFibGUgbmFtZWQgbG9nc2l6ZSBpbiB0aGUgZGF0YSBmcmFtZS4gKGxvZyB5aWVsZHMgdGhlIG5hdHVyYWwgbG9nLCB3aGVyZWFzIHRoZSBmdW5jdGlvbiBsb2cxMCB5aWVsZHMgbG9nIGJhc2UgMTAuKQ0KDQpgYGB7cn0NCm15ZGF0YSRsb2dzaXplIDwtIGxvZyhteWRhdGEkYm9keV9tYXNzX2tnKSAgICAgICAgICAgIA0Kc3RyKG15ZGF0YSkNCm15ZGF0YSA8LSBtdXRhdGUobXlkYXRhLCBsb2cxMHNpemUgPSBsb2cxMChib2R5X21hc3Nfa2cpKSANCnN0cihteWRhdGEpDQoNCmBgYA0KDQoqRGVsZXRlIGEgdmFyaWFibGUgZnJvbSBkYXRhIGZyYW1lKg0KDQpGb3IgZXhhbXBsZSwgdG8gZGVsZXRlIHRoZSB2YXJpYWJsZSBzcGVjaWVzIGZyb20gbXlkYXRhLCB1c2UNCmBgYHtyfQ0KbXlkYXRhMiA8LSBzZWxlY3QobXlkYXRhLCAtbmFtZSkgIyBkcGx5ciBtZXRob2QNCm15ZGF0YTINCmBgYA0KDQoqRXh0cmFjdCBhIGRhdGEgc3Vic2V0Kg0KDQpUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzLiBPbmUgaXMgdG8gdXNlIGluZGljYXRvcnMgaW5zaWRlIHNxdWFyZSBicmFja2V0cyB1c2luZyB0aGUgZm9sbG93aW5nIGZvcm1hdDogbXlkYXRhW3Jvd3MsIGNvbHVtbnNdLiBCZSBzdXJlIHRvIGxvb2sgYWZ0ZXIgZWFjaCBsaW5lIGJlbG93IHRvIHNlZSB3aGF0IHRoZSBjb21tYW5kcyBkaWQgdG8gdGhlIGRhdGEgc2V0DQoNCmBgYHtyfQ0KbmV3ZGF0YSA8LSBteWRhdGFbICwgYygyLDMpXSAgICMgYWxsIHJvd3MsIGNvbHVtbnMgMiBhbmQgMyBvbmx5Ow0KbmV3ZGF0YSA8LSBteWRhdGFbICwgLTFdICAgICAgICMgYWxsIHJvd3MsIGxlYXZlIG91dCBmaXJzdCBjb2x1bW4NCm5ld2RhdGEgPC0gbXlkYXRhWzE6MywgMToyXSAgICAjIGZpcnN0IHRocmVlIHJvd3MsIGZpcnN0IHR3byBjb2x1bW5zDQpgYGANCg0KDQpUaGUgc3Vic2V0IGNvbW1hbmQgaW4gYmFzZSBSIGlzIGVhc3kgdG8gdXNlIHRvIGV4dHJhY3Qgcm93cyBhbmQgY29sdW1ucy4gVXNlIHRoZSBzZWxlY3QgYXJndW1lbnQgdG8gc2VsZWN0IGNvbHVtbnMgKHZhcmlhYmxlcykuIEZvciBleGFtcGxlLCB0byBwdWxsIG91dCByb3dzIGNvcnJlc3BvbmRpbmcgdG8gY29udGluZW50IEFGIHdpdGggbG9nc2l6ZSA8IDI1LCBhbmQgdGhlIHRocmVlIHZhcmlhYmxlcywgImNvbnRpbmVudCIsInN0YXR1cyIsIm9yZGVyIiB5b3UgY291bGQgIHVzZSB0aGUgZm9sbG93aW5nLg0KDQpgYGB7cn0NCm5ld2RhdGEgPC0gc3Vic2V0KG15ZGF0YSwgYm9keV9tYXNzX2tnPj0gMTAgJiBicmFpbl9tYXNzX2cgPCAyNTApDQpuZXdkYXRhDQpgYGANCg0KKipTb3J0IGFuZCBvcmRlciB0aGUgcm93cyoNCg0KVG8gcmUtb3JkZXIgdGhlIHJvd3Mgb2YgYSBkYXRhIGZyYW1lIG15ZGF0YSB0byBjb3JyZXNwb25kIHRvIHRoZSBzb3J0ZWQgb3JkZXIgb2Ygb25lIG9mIGl0cyB2YXJpYWJsZXMsIHNheSB4LCB1c2UNCg0KYGBge3J9DQpteWRhdGEueCA8LSBteWRhdGFbb3JkZXIobXlkYXRhJHgpLCBdICAjIGJhc2UgUg0KbXlkYXRhLnggPC0gYXJyYW5nZShteWRhdGEsIDEpICAgICAgICAgIyBkcGx5ciBtZXRob2QNCmBgYA0KDQoNCioqQ2hhbGxlbmdlMSoqDQoNCldyaXRlIGEgc2luZ2xlIGNvbW1hbmQgKHdoaWNoIGNhbiBzcGFuIG11bHRpcGxlIGxpbmVzIGFuZCBpbmNsdWRlcyBwaXBlcykgdGhhdCB3aWxsIHByb2R1Y2UgYSBkYXRhIGZyYW1lIGZyb20gdGhlIGRpYW1vbmRzIGRhdGEgdGhhdCBvbmx5IGhhcyB0aGUgdmFsdWVzIGZvciBjb2xvciAiSiIgb2YgdGhlIHZhcmlhYmxlcyBwcmljZSwgY3V0IGFuZCBjYXJhdC4gDQoNCmBgYHtyfQ0KDQoNCmBgYA0KKipDaGFsbGVuZ2UgMioqDQoNCkNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBwcmljZSBwZXIgY29sb3Igd2hpY2ggaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgcHJpY2UgYW5kIHdoaWNoIGhhcyB0aGUgc2hvcnRlc3QgYXZlcmFnZSBwcmljZT8NCg0KYGBge3J9DQoNCmBgYA0KKipDaGFsbGVuZ2UgMyoqDQoNCkluIG9uZSBwaXBlZCBvciBjaGFpbmVkIHNldCBvZiBjb2RlIGZpbHRlciBnZW5lcmF0ZSBhIHBsb3QgdGhhdCBwbG90cyB0aGUgbWVhbnMgYW5kIENJcyBvZiBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgdW5pdF9wcmljZSB0aGF0IGlzIHRoZSBwcmljZSBwZXIgY2FyYXQgYnV0IG9ubHkgZm9yIGRpYW1vbmRzIHRoYXQgd2VyZSByYXRlZCBhcyBJZGVhbCwgUHJlbWl1bSwgb3IgVmVyeSBHb29kIHdpdGggZWFjaCBjb2xvciBvZiBkaWFtb25kIGRlcGljdGVkIGluIGEgZGlmZmVyZW50IGNvbG9yIGFuZCBpbiBhIGRpZmZyZW50IHBhbmVsLiANCg0KYGBge3J9DQoNCmBgYA0KDQo=