The output of most of the R chunks isn’t included in the HTML
version of the file to keep it to a more reasonable file size. You can
run the code in R to see the output.
Setup
It’s important you have {tidyr}
version 1.1.0 (at least)
installed. Check this with:
packageVersion("tidyr")
## [1] '1.3.1'
If needed, update it with:
install.packages("tidyr")
Load it as part of tidyverse
library(tidyverse)
Tidy data
The goal of {tidyr}
is to help you create tidy
data. Tidy data is data where:
Each variable is a column; each column is a variable.
Each observation is a row; each row is an observation.
Each value is a cell; each cell is a single value.
Tidy data describes a standard way of storing data that is used
wherever possible throughout the tidyverse. If you ensure that your
data is tidy, you’ll spend less time fighting with the tools and more
time working on your analysis.
Data
We’re going to work with a dataset that’s part of the
{tidyr}
package: who
The details: A subset of data from the World Health Organization
Global Tuberculosis Report, and accompanying global populations. The
data uses the original codes given by the World Health Organization.
The column names for columns 5 through 60 are made by combining
“new_” to a code for method of diagnosis
(rel = relapse, sn =
negative pulmonary smear, sp = positive
pulmonary smear, ep = extrapulmonary) to a
code for gender (f = female,
m = male) to a code for age group
(014 = 0-14 yrs of age,
1524 = 15-24 years of age,
2534 = 25 to 34 years of age,
3544 = 35 to 44 years of age,
4554 = 45 to 54 years of age,
5564 = 55 to 64 years of age,
65 = 65 years of age or older).
Column names:
names(who)
## [1] "country" "iso2" "iso3" "year" "new_sp_m014"
## [6] "new_sp_m1524" "new_sp_m2534" "new_sp_m3544" "new_sp_m4554" "new_sp_m5564"
## [11] "new_sp_m65" "new_sp_f014" "new_sp_f1524" "new_sp_f2534" "new_sp_f3544"
## [16] "new_sp_f4554" "new_sp_f5564" "new_sp_f65" "new_sn_m014" "new_sn_m1524"
## [21] "new_sn_m2534" "new_sn_m3544" "new_sn_m4554" "new_sn_m5564" "new_sn_m65"
## [26] "new_sn_f014" "new_sn_f1524" "new_sn_f2534" "new_sn_f3544" "new_sn_f4554"
## [31] "new_sn_f5564" "new_sn_f65" "new_ep_m014" "new_ep_m1524" "new_ep_m2534"
## [36] "new_ep_m3544" "new_ep_m4554" "new_ep_m5564" "new_ep_m65" "new_ep_f014"
## [41] "new_ep_f1524" "new_ep_f2534" "new_ep_f3544" "new_ep_f4554" "new_ep_f5564"
## [46] "new_ep_f65" "newrel_m014" "newrel_m1524" "newrel_m2534" "newrel_m3544"
## [51] "newrel_m4554" "newrel_m5564" "newrel_m65" "newrel_f014" "newrel_f1524"
## [56] "newrel_f2534" "newrel_f3544" "newrel_f4554" "newrel_f5564" "newrel_f65"
Let’s look at a few random rows:
sample_n(who, 10)
In this data set, each row (each observation) is a country-year. Each
column is a measure-age value.
There are situations though where we might want each row to be a
different unit of observation. We could make a really wide data set (one
with lots of columns), where each row is a single country and there are
columns for every measure in every year. Or we could make a data set
that’s really long, where each row is a country-year-measure-age
observation. Or one that’s in between, with each row being
country-year-age, and there are columns for each measure.
These different configurations support different types of statistical
models, data transformations, and data visualizations. No configuration
is necessarily the right one or better than the others in a vacuum - it
depends on what you want to do with the data!
I’m going to reduce this dataset to just a few countries that have at
least some data filled in to make it easier for us to work with:
who2 <- filter(who, country %in% c("Canada", "Czech Republic", "Morocco"))
Pivot Longer
We make a data set longer by reducing the number of columns and
increasing the number of rows. We’ll stack some of the columns on top of
each other, and duplicate the values in other columns to make this
happen. This changes what the unit of observation is (what each row
represents).

Image source: https://epirhandbook.com/en/pivoting-data.html
A Mini Example
Before we work with our larger data set, let’s look at a very small
one so it’s easier to see what’s happening.
tribble()
is a function that makes it easier to manually
create a tibble (data frame)
foods <- tribble(~id, ~a, ~b, ~c,
1, "apple", "banana", "cod",
2, "asparagus", "bacon", "chocolate")
foods
In foods
, each row (observation) is a person – each
person’s favorite food starting with each letter. But we want to get
this data set to look like:
id category item
1 a apple
1 b banana
1 c cod
2 a asparagus
2 b bacon
2 c chocolate
Where each row (observation) is a person-food combination: each food
is in a row by itself, and the column names have become encoded in a
variable.
To do this, we use the function pivot_longer()
- in
previous versions of {tidyr}
, this function was called
gather
. The syntax was similar, but the arguments had
different names. In base R, you can do similar transformations with the
reshape
function.
Like other tidyverse functions, we’ll start with the data frame name.
Then we’ll select which we’re going to collapse – meaning there won’t be
columns with those names anymore because we’ll stack the names and
values on top of each other to make more rows. Then we need to tell it
what to name the new variable it will make that will have the column
names in it (our new categorical variable), and a name as well for the
new column that will have the values in it (the values that were in the
columns originally).
We specify which columns we want using any of the select syntax or
select helpers we’ve been talking about in previous sessions.
pivot_longer(foods,
cols = -id, # collapse all columns except (-) id
names_to = "category", # name for the new column that will have the old column names as values
values_to = "item") # name for the new column that will store the data from the old columns
The id
column has values duplicated, once for each of
the original 3 columns that we collapsed, and each value of our new
category
column appears twice, because there were 2 rows in
food
when we started.
EXERCISE 1
Pivot the pops data frame below to be in a longer format, with rows
for each country-year instead of each country.
Hint: Break down the problem into steps. Think about which
columns of the original dataset need to be collapsed together to make
the data “long”.
pops <- tribble(~country, ~year2011, ~year2012, ~year2013,
"Brazil", 196935134, 198656019, 200361925,
"Germany", 82892904, 82800121, 82726626,
"Kenya", 42027891, 43178141, 44353691)
WHO data
Now, the WHO TB data is a bit more complicated than the simple
example. We know how to take the data set into completely long format,
by collapsing all of the columns except the first 4 that have country
name info and year:
names(who2)
## [1] "country" "iso2" "iso3" "year" "new_sp_m014"
## [6] "new_sp_m1524" "new_sp_m2534" "new_sp_m3544" "new_sp_m4554" "new_sp_m5564"
## [11] "new_sp_m65" "new_sp_f014" "new_sp_f1524" "new_sp_f2534" "new_sp_f3544"
## [16] "new_sp_f4554" "new_sp_f5564" "new_sp_f65" "new_sn_m014" "new_sn_m1524"
## [21] "new_sn_m2534" "new_sn_m3544" "new_sn_m4554" "new_sn_m5564" "new_sn_m65"
## [26] "new_sn_f014" "new_sn_f1524" "new_sn_f2534" "new_sn_f3544" "new_sn_f4554"
## [31] "new_sn_f5564" "new_sn_f65" "new_ep_m014" "new_ep_m1524" "new_ep_m2534"
## [36] "new_ep_m3544" "new_ep_m4554" "new_ep_m5564" "new_ep_m65" "new_ep_f014"
## [41] "new_ep_f1524" "new_ep_f2534" "new_ep_f3544" "new_ep_f4554" "new_ep_f5564"
## [46] "new_ep_f65" "newrel_m014" "newrel_m1524" "newrel_m2534" "newrel_m3544"
## [51] "newrel_m4554" "newrel_m5564" "newrel_m65" "newrel_f014" "newrel_f1524"
## [56] "newrel_f2534" "newrel_f3544" "newrel_f4554" "newrel_f5564" "newrel_f65"
Each of the measurement columns has the count of the number of people
with the given diagnosis, gender, and age group.
pivot_longer(who2,
-country:-year, # all except first 4 columns (this is the cols argument)
names_to = "measure", # make up a new name
values_to = "count") # make up a new name
There were 56 columns that we collapsed – that weren’t the ID
variables. So the first row in the original data expanded to now be 56
rows in the longer data set.
But what if we don’t want to collapse them completely? What if we
want one column per age group? or per measure instead? Or what if all of
the columns aren’t of the same type? If some are numeric while others
have character or factor data?
In really complicated cases, you may need to split your data into
multiple data sets, reshape the parts, and then join the results back
together.
In most cases though, you can pivot to a completely long format, and
then spread columns back out wider after some additional
transformations. If there are different data types, they get converted
to the more general one, following: boolean -> integer -> numeric
-> character. While you need to be careful if you are working with
precise numerical values (decimal values), if you’re working with whole
numbers or the precision (number of significant digits) in the data is
small, converting from numeric to character and back again will not lose
information.
Separate variable names
Ok, back to our WHO data. One trick before we learn how to pivot
wider to undo what we did. When we pivot longer, our new “measure”
variable actually has multiple pieces of information in it. The variable
names are of the format: “new_” diagnosis _ m/f age. Ideally, we’d like
this information separated, so we have a column for the diagnosis
method, a column for the gender, and a column for the age group.
We can tell pivot_longer to do this for us:
names(who2)
pivot_longer(who2, -country:-year,
# we'll keep 3 columns: make up a name for each
names_to=c("diagnosis", "gender", "age"),
# this is a regular expression that captures the pattern
# each () set is a value to keep and turn into a column;
# we are dropping "new"
names_pattern = "new_?(.+)_([mf])(\\d+)",
values_to="count")
If the variable names had a simpler pattern, for example:
year_measure, then we could use the “names_sep” argument instead of the
more complicated “names_pattern” to split up the column.
Pivot Wider
Now, we’ve got our completely long data. How do we get it into a form
where there’s one row per country-year-gender-age, with one column per
measure? Essentially, we want to split the diagnosis column into
multiple columns for each type of diagnosis.

Image Source: https://epirhandbook.com/en/pivoting-data.html
# same as above, just save it
long_data <- pivot_longer(who2, -country:-year,
names_to=c("diagnosis", "gender", "age"),
values_to="count",
names_pattern = "new_?(.+)_([mf])(\\d+)")
names(long_data)
## [1] "country" "iso2" "iso3" "year" "diagnosis" "gender"
## [7] "age" "count"
# spread it back out
pivot_wider(long_data,
names_from=diagnosis,
values_from=count)
What happens if our long data doesn’t have every combination of
values to fill into our new data set? For example, if the data started
long instead of wide?
For example, if we had data that looked like:
long_data %>%
filter(!is.na(count)) %>%
sample_n(20) %>%
arrange(country, year, diagnosis, age, gender)
It’s ok - we can still pivot wider. By default, it will fill in
NA
where it doesn’t have a value, or we can tell it to fill
with another value (such as 0).
long_data %>%
filter(!is.na(count)) %>%
sample_n(20) %>%
arrange(country, year, diagnosis, age, gender) %>%
pivot_wider(names_from=diagnosis,
values_from=count)
We could also pivot wider more than one column at once. Let’s have
one row per country-year-diagnosis, and one column for each age-gender
combination:
names(long_data)
## [1] "country" "iso2" "iso3" "year" "diagnosis" "gender"
## [7] "age" "count"
long_data %>%
pivot_wider(names_from=c(age, gender),
# controls how new variable names are created
# uses syntax from the glue package
names_glue="{gender}_{age}",
values_from=count)
EXERCISE 2
Pivot the long_data
data frame wider so that each row is
a country-diagnosis-gender-age observation (pivot “year” so you have one
column for each year in the output).
Hint: Break down the problem into steps. Think about which
column(s) in your original dataset need to be spread out into multiple
columns to make the data “wide”.
Separate
Much like we had pivot_longer()
split up the components
of the variable names for us into measure, age, and gender, we can use
the separate()
function to do that to columns outside of
pivoting.
nobels <- tribble(~name, ~category,
"Bob Dylan", "Literature",
"Anne L'Huillier", "Physics",
"Narges Mohammadi", "Peace",
"Roger Penrose", "Physics",
"Jennifer Doudna", "Chemistry",
"Abhijit Banerjee", "Economics",
"Jean-Pierre Sauvage", "Chemistry")
nobels
We could split the name column into first name and last name, using a
space character as a delimiter.
separate(nobels, # data frame name
name, # existing column name to split
into=c("first", "last"), # new column names
sep=" ") # what to split on
You need to know the maximum number of pieces that your data will be
split into, or you’ll lose some of the information. If Jean-Pierre
Sauvage’s name had a space instead of a hyphen:
nobels <- tribble(~name, ~category,
"Bob Dylan", "Literature",
"Anne L'Huillier", "Physics",
"Narges Mohammadi", "Peace",
"Roger Penrose", "Physics",
"Jennifer Doudna", "Chemistry",
"Abhijit Banerjee", "Economics",
"Jean Pierre Sauvage", "Chemistry")
separate(nobels,
name,
into=c("first", "last"),
sep=" ")
## Warning: Expected 2 pieces. Additional pieces discarded in 1 rows [7].
We get a warning, and “Sauvage” was just dropped completely! (You can
control this behavior to some extent with the extra
argument.)
It’s better to specify the maximum number of columns possible
instead:
separate(nobels,
name,
into=c("first1", "first2", "last"),
sep=" ")
## Warning: Expected 3 pieces. Missing pieces filled with `NA` in 6 rows [1, 2, 3, 4, 5,
## 6].
Now the warning is telling us it filled in NA
values
where it didn’t find 3 pieces after separating. Also, the new column
names are not exactly suitable since other observations in the dataset
all have last names (not NA
).
But separate
really works best when you’re separating
something that will separate into a fixed number of columns.
Side note: working with names is HARD! For some examples why, see https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/
EXERCISE 3
Separate the “location” variable in addresses
into
separate “city” and “state” columns. Hint: the “sep” value can be more
than one character in length.
addresses <- tribble(~name, ~location,
"Katherine", "Chicago, IL",
"Bob", "Bend, OR",
"Louka", "Honolulu, HI",
"Alex", "San Jose, CA")
separate_rows()
In cases where you want to separate out a column, but you don’t know
how many values might be in it, another option is to make one row,
instead of one column, per value. It’s combining a separate with a pivot
longer.
This can be useful if you have a column with a delimited list of
values in it. For example, if you ask a multiple choice question that
allows multiple answers in Qualtrics (survey platform), you’ll get a
single column with all of the answer that people checked:
survey <- tribble(~id, ~q1, ~q2,
45, 4, "Time",
46, 3, "Quality;Cost",
47, 4, "Time;Cost;Compatibility;Warranty")
survey
How do we deal with q2?
separate_rows(survey,
q2, # which column to separate
sep=";")
Creating Indicator Variables
Then, we may want to create one column per answer choice – create
indicator variables (aka dummy variables or one-hot encoding).
separate_rows(survey,
q2, # which column to separate
sep=";") %>%
mutate(exists=1) %>% # add a column to encode values later
pivot_wider(names_from=q2, # new column names, one for each value
values_from=exists, # so there will be a 1 where there was a value
values_fill=0) ## fills in missing vals with 0 instead of NA
Recap
You now can use different {tidyr}
commands, such as
pivot_longer
, pivot_wider
,
separate
, separate_rows
, to reshape your data
in a wide variety of ways.
Next session: the {ggplot2}
functions for visualizing
your data.
Answers to the exercises
EXERCISE 1
# solution 1
pivot_longer(pops,
cols=c("year2011", "year2012", "year2013"), # columns to collapse together
names_to = "year", # column that will store the year names
values_to = "population") # column that will store population values
# solution 2
pivot_longer(pops,
cols=-country, # same as selecting all the year columns
names_to = "year",
values_to = "population")
EXERCISE 2
# check columns in starting dataset
names(long_data)
## [1] "country" "iso2" "iso3" "year" "diagnosis" "gender"
## [7] "age" "count"
# solution 1
long_data %>%
pivot_wider(names_from = year,
values_from = count)
# solution 2 - more elegant columns names after pivoting
long_data %>%
pivot_wider(names_from = year,
names_glue = "year_{year}",
values_from = count)
EXERCISE 3
separate(addresses,
location,
into=c("city", "state"),
sep=", ")
LS0tCnRpdGxlOiAndGlkeXInCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCmBgYHtyLCBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWw9VFJVRSkKYGBgCgoqVGhlIG91dHB1dCBvZiBtb3N0IG9mIHRoZSBSIGNodW5rcyBpc24ndCBpbmNsdWRlZCBpbiB0aGUgSFRNTCB2ZXJzaW9uIG9mIHRoZSBmaWxlIHRvIGtlZXAgaXQgdG8gYSBtb3JlIHJlYXNvbmFibGUgZmlsZSBzaXplLiBZb3UgY2FuIHJ1biB0aGUgY29kZSBpbiBSIHRvIHNlZSB0aGUgb3V0cHV0LioKCiMgU2V0dXAKCkl0J3MgaW1wb3J0YW50IHlvdSBoYXZlIGB7dGlkeXJ9YCB2ZXJzaW9uIDEuMS4wIChhdCBsZWFzdCkgaW5zdGFsbGVkLiBDaGVjayB0aGlzIHdpdGg6CgpgYGB7cn0KcGFja2FnZVZlcnNpb24oInRpZHlyIikKYGBgCgpJZiBuZWVkZWQsIHVwZGF0ZSBpdCB3aXRoOgoKYGBge3IsIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInRpZHlyIikKYGBgCgpMb2FkIGl0IGFzIHBhcnQgb2YgdGlkeXZlcnNlCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMgVGlkeSBkYXRhCgpUaGUgZ29hbCBvZiBge3RpZHlyfWAgaXMgdG8gaGVscCB5b3UgY3JlYXRlICoqdGlkeSBkYXRhKiouIFRpZHkgZGF0YSBpcyBkYXRhIHdoZXJlOgoKMS4gIEVhY2ggdmFyaWFibGUgaXMgYSBjb2x1bW47IGVhY2ggY29sdW1uIGlzIGEgdmFyaWFibGUuCgoyLiAgRWFjaCBvYnNlcnZhdGlvbiBpcyBhIHJvdzsgZWFjaCByb3cgaXMgYW4gb2JzZXJ2YXRpb24uCgozLiAgRWFjaCB2YWx1ZSBpcyBhIGNlbGw7IGVhY2ggY2VsbCBpcyBhIHNpbmdsZSB2YWx1ZS4KClRpZHkgZGF0YSBkZXNjcmliZXMgYSBzdGFuZGFyZCB3YXkgb2Ygc3RvcmluZyBkYXRhIHRoYXQgaXMgdXNlZCB3aGVyZXZlciBwb3NzaWJsZSB0aHJvdWdob3V0IHRoZSBbdGlkeXZlcnNlXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykuIElmIHlvdSBlbnN1cmUgdGhhdCB5b3VyIGRhdGEgaXMgdGlkeSwgeW91J2xsIHNwZW5kIGxlc3MgdGltZSBmaWdodGluZyB3aXRoIHRoZSB0b29scyBhbmQgbW9yZSB0aW1lIHdvcmtpbmcgb24geW91ciBhbmFseXNpcy4KCiMgRGF0YQoKV2UncmUgZ29pbmcgdG8gd29yayB3aXRoIGEgZGF0YXNldCB0aGF0J3MgcGFydCBvZiB0aGUgYHt0aWR5cn1gIHBhY2thZ2U6IGB3aG9gCgpUaGUgZGV0YWlsczogQSBzdWJzZXQgb2YgZGF0YSBmcm9tIHRoZSBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIEdsb2JhbCBUdWJlcmN1bG9zaXMgUmVwb3J0LCBhbmQgYWNjb21wYW55aW5nIGdsb2JhbCBwb3B1bGF0aW9ucy4gVGhlIGRhdGEgdXNlcyB0aGUgb3JpZ2luYWwgY29kZXMgZ2l2ZW4gYnkgdGhlIFdvcmxkIEhlYWx0aCBPcmdhbml6YXRpb24uCgpUaGUgY29sdW1uIG5hbWVzIGZvciBjb2x1bW5zIDUgdGhyb3VnaCA2MCBhcmUgbWFkZSBieSBjb21iaW5pbmcgIm5ld1xfIiB0byBhIGNvZGUgZm9yIG1ldGhvZCBvZiBbZGlhZ25vc2lzXXsudW5kZXJsaW5lfSAoKioqcmVsKioqID0gcmVsYXBzZSwgKioqc24qKiogPSBuZWdhdGl2ZSBwdWxtb25hcnkgc21lYXIsICoqKnNwKioqID0gcG9zaXRpdmUgcHVsbW9uYXJ5IHNtZWFyLCAqKiplcCoqKiA9IGV4dHJhcHVsbW9uYXJ5KSB0byBhIGNvZGUgZm9yIFtnZW5kZXJdey51bmRlcmxpbmV9ICgqKipmKioqID0gZmVtYWxlLCAqKiptKioqID0gbWFsZSkgdG8gYSBjb2RlIGZvciBbYWdlIGdyb3VwXXsudW5kZXJsaW5lfSAoKioqMDE0KioqID0gMC0xNCB5cnMgb2YgYWdlLCAqKioxNTI0KioqID0gMTUtMjQgeWVhcnMgb2YgYWdlLCAqKioyNTM0KioqID0gMjUgdG8gMzQgeWVhcnMgb2YgYWdlLCAqKiozNTQ0KioqID0gMzUgdG8gNDQgeWVhcnMgb2YgYWdlLCAqKio0NTU0KioqID0gNDUgdG8gNTQgeWVhcnMgb2YgYWdlLCAqKio1NTY0KioqID0gNTUgdG8gNjQgeWVhcnMgb2YgYWdlLCAqKio2NSoqKiA9IDY1IHllYXJzIG9mIGFnZSBvciBvbGRlcikuCgpDb2x1bW4gbmFtZXM6CgpgYGB7cn0KbmFtZXMod2hvKQpgYGAKCkxldCdzIGxvb2sgYXQgYSBmZXcgcmFuZG9tIHJvd3M6CgpgYGB7cn0Kc2FtcGxlX24od2hvLCAxMCkKYGBgCgpJbiB0aGlzIGRhdGEgc2V0LCBlYWNoIHJvdyAoZWFjaCBvYnNlcnZhdGlvbikgaXMgYSBjb3VudHJ5LXllYXIuIEVhY2ggY29sdW1uIGlzIGEgbWVhc3VyZS1hZ2UgdmFsdWUuCgpUaGVyZSBhcmUgc2l0dWF0aW9ucyB0aG91Z2ggd2hlcmUgd2UgbWlnaHQgd2FudCBlYWNoIHJvdyB0byBiZSBhIGRpZmZlcmVudCB1bml0IG9mIG9ic2VydmF0aW9uLiBXZSBjb3VsZCBtYWtlIGEgcmVhbGx5IHdpZGUgZGF0YSBzZXQgKG9uZSB3aXRoIGxvdHMgb2YgY29sdW1ucyksIHdoZXJlIGVhY2ggcm93IGlzIGEgc2luZ2xlIGNvdW50cnkgYW5kIHRoZXJlIGFyZSBjb2x1bW5zIGZvciBldmVyeSBtZWFzdXJlIGluIGV2ZXJ5IHllYXIuIE9yIHdlIGNvdWxkIG1ha2UgYSBkYXRhIHNldCB0aGF0J3MgcmVhbGx5IGxvbmcsIHdoZXJlIGVhY2ggcm93IGlzIGEgY291bnRyeS15ZWFyLW1lYXN1cmUtYWdlIG9ic2VydmF0aW9uLiBPciBvbmUgdGhhdCdzIGluIGJldHdlZW4sIHdpdGggZWFjaCByb3cgYmVpbmcgY291bnRyeS15ZWFyLWFnZSwgYW5kIHRoZXJlIGFyZSBjb2x1bW5zIGZvciBlYWNoIG1lYXN1cmUuCgpUaGVzZSBkaWZmZXJlbnQgY29uZmlndXJhdGlvbnMgc3VwcG9ydCBkaWZmZXJlbnQgdHlwZXMgb2Ygc3RhdGlzdGljYWwgbW9kZWxzLCBkYXRhIHRyYW5zZm9ybWF0aW9ucywgYW5kIGRhdGEgdmlzdWFsaXphdGlvbnMuIE5vIGNvbmZpZ3VyYXRpb24gaXMgbmVjZXNzYXJpbHkgdGhlIHJpZ2h0IG9uZSBvciBiZXR0ZXIgdGhhbiB0aGUgb3RoZXJzIGluIGEgdmFjdXVtIC0gaXQgZGVwZW5kcyBvbiB3aGF0IHlvdSB3YW50IHRvIGRvIHdpdGggdGhlIGRhdGEhCgpJJ20gZ29pbmcgdG8gcmVkdWNlIHRoaXMgZGF0YXNldCB0byBqdXN0IGEgZmV3IGNvdW50cmllcyB0aGF0IGhhdmUgYXQgbGVhc3Qgc29tZSBkYXRhIGZpbGxlZCBpbiB0byBtYWtlIGl0IGVhc2llciBmb3IgdXMgdG8gd29yayB3aXRoOgoKYGBge3J9CndobzIgPC0gZmlsdGVyKHdobywgY291bnRyeSAlaW4lIGMoIkNhbmFkYSIsICJDemVjaCBSZXB1YmxpYyIsICJNb3JvY2NvIikpCmBgYAoKIyBQaXZvdCBMb25nZXIKCldlIG1ha2UgYSBkYXRhIHNldCBsb25nZXIgYnkgcmVkdWNpbmcgdGhlIG51bWJlciBvZiBjb2x1bW5zIGFuZCBpbmNyZWFzaW5nIHRoZSBudW1iZXIgb2Ygcm93cy4gV2UnbGwgc3RhY2sgc29tZSBvZiB0aGUgY29sdW1ucyBvbiB0b3Agb2YgZWFjaCBvdGhlciwgYW5kIGR1cGxpY2F0ZSB0aGUgdmFsdWVzIGluIG90aGVyIGNvbHVtbnMgdG8gbWFrZSB0aGlzIGhhcHBlbi4gVGhpcyBjaGFuZ2VzIHdoYXQgdGhlIHVuaXQgb2Ygb2JzZXJ2YXRpb24gaXMgKHdoYXQgZWFjaCByb3cgcmVwcmVzZW50cykuCgohW10ocGl2b3RfbG9uZ2VyX25ldy5wbmcpCgpJbWFnZSBzb3VyY2U6IDxodHRwczovL2VwaXJoYW5kYm9vay5jb20vZW4vcGl2b3RpbmctZGF0YS5odG1sPgoKIyMgQSBNaW5pIEV4YW1wbGUKCkJlZm9yZSB3ZSB3b3JrIHdpdGggb3VyIGxhcmdlciBkYXRhIHNldCwgbGV0J3MgbG9vayBhdCBhIHZlcnkgc21hbGwgb25lIHNvIGl0J3MgZWFzaWVyIHRvIHNlZSB3aGF0J3MgaGFwcGVuaW5nLgoKYHRyaWJibGUoKWAgaXMgYSBmdW5jdGlvbiB0aGF0IG1ha2VzIGl0IGVhc2llciB0byBtYW51YWxseSBjcmVhdGUgYSB0aWJibGUgKGRhdGEgZnJhbWUpCgpgYGB7cn0KZm9vZHMgPC0gdHJpYmJsZSh+aWQsIH5hLCB+YiwgfmMsCiAgICAgICAgICAgICAgICAgMSwgImFwcGxlIiwgImJhbmFuYSIsICJjb2QiLCAKICAgICAgICAgICAgICAgICAyLCAiYXNwYXJhZ3VzIiwgImJhY29uIiwgImNob2NvbGF0ZSIpCmZvb2RzCmBgYAoKSW4gYGZvb2RzYCwgZWFjaCByb3cgKG9ic2VydmF0aW9uKSBpcyBhIHBlcnNvbiAtLSBlYWNoIHBlcnNvbidzIGZhdm9yaXRlIGZvb2Qgc3RhcnRpbmcgd2l0aCBlYWNoIGxldHRlci4gQnV0IHdlIHdhbnQgdG8gZ2V0IHRoaXMgZGF0YSBzZXQgdG8gbG9vayBsaWtlOgoKYGBgICAgICAgICAgCiAgICBpZCBjYXRlZ29yeSBpdGVtICAgICAKICAgICAxIGEgICAgICAgIGFwcGxlICAgIAogICAgIDEgYiAgICAgICAgYmFuYW5hICAgCiAgICAgMSBjICAgICAgICBjb2QgICAgICAKICAgICAyIGEgICAgICAgIGFzcGFyYWd1cwogICAgIDIgYiAgICAgICAgYmFjb24gICAgCiAgICAgMiBjICAgICAgICBjaG9jb2xhdGUKYGBgCgpXaGVyZSBlYWNoIHJvdyAob2JzZXJ2YXRpb24pIGlzIGEgcGVyc29uLWZvb2QgY29tYmluYXRpb246IGVhY2ggZm9vZCBpcyBpbiBhIHJvdyBieSBpdHNlbGYsIGFuZCB0aGUgY29sdW1uIG5hbWVzIGhhdmUgYmVjb21lIGVuY29kZWQgaW4gYSB2YXJpYWJsZS4KClRvIGRvIHRoaXMsIHdlIHVzZSB0aGUgZnVuY3Rpb24gYHBpdm90X2xvbmdlcigpYCAtIGluIHByZXZpb3VzIHZlcnNpb25zIG9mIGB7dGlkeXJ9YCwgdGhpcyBmdW5jdGlvbiB3YXMgY2FsbGVkIGBnYXRoZXJgLiBUaGUgc3ludGF4IHdhcyBzaW1pbGFyLCBidXQgdGhlIGFyZ3VtZW50cyBoYWQgZGlmZmVyZW50IG5hbWVzLiBJbiBiYXNlIFIsIHlvdSBjYW4gZG8gc2ltaWxhciB0cmFuc2Zvcm1hdGlvbnMgd2l0aCB0aGUgYHJlc2hhcGVgIGZ1bmN0aW9uLgoKTGlrZSBvdGhlciB0aWR5dmVyc2UgZnVuY3Rpb25zLCB3ZSdsbCBzdGFydCB3aXRoIHRoZSBkYXRhIGZyYW1lIG5hbWUuIFRoZW4gd2UnbGwgc2VsZWN0IHdoaWNoIHdlJ3JlIGdvaW5nIHRvIGNvbGxhcHNlIC0tIG1lYW5pbmcgdGhlcmUgd29uJ3QgYmUgY29sdW1ucyB3aXRoIHRob3NlIG5hbWVzIGFueW1vcmUgYmVjYXVzZSB3ZSdsbCBzdGFjayB0aGUgbmFtZXMgYW5kIHZhbHVlcyBvbiB0b3Agb2YgZWFjaCBvdGhlciB0byBtYWtlIG1vcmUgcm93cy4gVGhlbiB3ZSBuZWVkIHRvIHRlbGwgaXQgd2hhdCB0byBuYW1lIHRoZSBuZXcgdmFyaWFibGUgaXQgd2lsbCBtYWtlIHRoYXQgd2lsbCBoYXZlIHRoZSBjb2x1bW4gbmFtZXMgaW4gaXQgKG91ciBuZXcgY2F0ZWdvcmljYWwgdmFyaWFibGUpLCBhbmQgYSBuYW1lIGFzIHdlbGwgZm9yIHRoZSBuZXcgY29sdW1uIHRoYXQgd2lsbCBoYXZlIHRoZSB2YWx1ZXMgaW4gaXQgKHRoZSB2YWx1ZXMgdGhhdCB3ZXJlIGluIHRoZSBjb2x1bW5zIG9yaWdpbmFsbHkpLgoKV2Ugc3BlY2lmeSB3aGljaCBjb2x1bW5zIHdlIHdhbnQgdXNpbmcgYW55IG9mIHRoZSBzZWxlY3Qgc3ludGF4IG9yIHNlbGVjdCBoZWxwZXJzIHdlJ3ZlIGJlZW4gdGFsa2luZyBhYm91dCBpbiBwcmV2aW91cyBzZXNzaW9ucy4KCmBgYHtyfQpwaXZvdF9sb25nZXIoZm9vZHMsIAogICAgICAgICAgICAgY29scyA9IC1pZCwgIyBjb2xsYXBzZSBhbGwgY29sdW1ucyBleGNlcHQgKC0pIGlkCiAgICAgICAgICAgICBuYW1lc190byA9ICJjYXRlZ29yeSIsICAjIG5hbWUgZm9yIHRoZSBuZXcgY29sdW1uIHRoYXQgd2lsbCBoYXZlIHRoZSBvbGQgY29sdW1uIG5hbWVzIGFzIHZhbHVlcwogICAgICAgICAgICAgdmFsdWVzX3RvID0gIml0ZW0iKSAgIyBuYW1lIGZvciB0aGUgbmV3IGNvbHVtbiB0aGF0IHdpbGwgc3RvcmUgdGhlIGRhdGEgZnJvbSB0aGUgb2xkIGNvbHVtbnMKYGBgCgpUaGUgYGlkYCBjb2x1bW4gaGFzIHZhbHVlcyBkdXBsaWNhdGVkLCBvbmNlIGZvciBlYWNoIG9mIHRoZSBvcmlnaW5hbCAzIGNvbHVtbnMgdGhhdCB3ZSBjb2xsYXBzZWQsIGFuZCBlYWNoIHZhbHVlIG9mIG91ciBuZXcgYGNhdGVnb3J5YCBjb2x1bW4gYXBwZWFycyB0d2ljZSwgYmVjYXVzZSB0aGVyZSB3ZXJlIDIgcm93cyBpbiBgZm9vZGAgd2hlbiB3ZSBzdGFydGVkLgoKIyMjIEVYRVJDSVNFIDEKClBpdm90IHRoZSBwb3BzIGRhdGEgZnJhbWUgYmVsb3cgdG8gYmUgaW4gYSBsb25nZXIgZm9ybWF0LCB3aXRoIHJvd3MgZm9yIGVhY2ggY291bnRyeS15ZWFyIGluc3RlYWQgb2YgZWFjaCBjb3VudHJ5LgoKW0hpbnRdey51bmRlcmxpbmV9OiBCcmVhayBkb3duIHRoZSBwcm9ibGVtIGludG8gc3RlcHMuIFRoaW5rIGFib3V0IHdoaWNoIGNvbHVtbnMgb2YgdGhlIG9yaWdpbmFsIGRhdGFzZXQgbmVlZCB0byBiZSBjb2xsYXBzZWQgdG9nZXRoZXIgdG8gbWFrZSB0aGUgZGF0YSAibG9uZyIuCgpgYGB7cn0KcG9wcyA8LSB0cmliYmxlKH5jb3VudHJ5LCAgfnllYXIyMDExLCAgfnllYXIyMDEyLCAgfnllYXIyMDEzLAogICAgICAgICAgICAgICAgIkJyYXppbCIsICAxOTY5MzUxMzQsIDE5ODY1NjAxOSwgMjAwMzYxOTI1LAogICAgICAgICAgICAgICAgIkdlcm1hbnkiLCAgODI4OTI5MDQsICA4MjgwMDEyMSwgIDgyNzI2NjI2LAogICAgICAgICAgICAgICAgIktlbnlhIiwgICAgNDIwMjc4OTEsICA0MzE3ODE0MSwgIDQ0MzUzNjkxKQoKYGBgCgojIyBXSE8gZGF0YQoKTm93LCB0aGUgV0hPIFRCIGRhdGEgaXMgYSBiaXQgbW9yZSBjb21wbGljYXRlZCB0aGFuIHRoZSBzaW1wbGUgZXhhbXBsZS4gV2Uga25vdyBob3cgdG8gdGFrZSB0aGUgZGF0YSBzZXQgaW50byBjb21wbGV0ZWx5IGxvbmcgZm9ybWF0LCBieSBjb2xsYXBzaW5nIGFsbCBvZiB0aGUgY29sdW1ucyBleGNlcHQgdGhlIGZpcnN0IDQgdGhhdCBoYXZlIGNvdW50cnkgbmFtZSBpbmZvIGFuZCB5ZWFyOgoKYGBge3J9Cm5hbWVzKHdobzIpCmBgYAoKRWFjaCBvZiB0aGUgbWVhc3VyZW1lbnQgY29sdW1ucyBoYXMgdGhlIGNvdW50IG9mIHRoZSBudW1iZXIgb2YgcGVvcGxlIHdpdGggdGhlIGdpdmVuIGRpYWdub3NpcywgZ2VuZGVyLCBhbmQgYWdlIGdyb3VwLgoKYGBge3IsIGV2YWw9RkFMU0V9CnBpdm90X2xvbmdlcih3aG8yLCAKICAgICAgICAgICAgIC1jb3VudHJ5Oi15ZWFyLCAgIyBhbGwgZXhjZXB0IGZpcnN0IDQgY29sdW1ucyAodGhpcyBpcyB0aGUgY29scyBhcmd1bWVudCkKICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1lYXN1cmUiLCAgIyBtYWtlIHVwIGEgbmV3IG5hbWUKICAgICAgICAgICAgIHZhbHVlc190byA9ICJjb3VudCIpICAjIG1ha2UgdXAgYSBuZXcgbmFtZQpgYGAKClRoZXJlIHdlcmUgNTYgY29sdW1ucyB0aGF0IHdlIGNvbGxhcHNlZCAtLSB0aGF0IHdlcmVuJ3QgdGhlIElEIHZhcmlhYmxlcy4gU28gdGhlIGZpcnN0IHJvdyBpbiB0aGUgb3JpZ2luYWwgZGF0YSBleHBhbmRlZCB0byBub3cgYmUgNTYgcm93cyBpbiB0aGUgbG9uZ2VyIGRhdGEgc2V0LgoKQnV0IHdoYXQgaWYgd2UgZG9uJ3Qgd2FudCB0byBjb2xsYXBzZSB0aGVtIGNvbXBsZXRlbHk/IFdoYXQgaWYgd2Ugd2FudCBvbmUgY29sdW1uIHBlciBhZ2UgZ3JvdXA/IG9yIHBlciBtZWFzdXJlIGluc3RlYWQ/IE9yIHdoYXQgaWYgYWxsIG9mIHRoZSBjb2x1bW5zIGFyZW4ndCBvZiB0aGUgc2FtZSB0eXBlPyBJZiBzb21lIGFyZSBudW1lcmljIHdoaWxlIG90aGVycyBoYXZlIGNoYXJhY3RlciBvciBmYWN0b3IgZGF0YT8KCkluIHJlYWxseSBjb21wbGljYXRlZCBjYXNlcywgeW91IG1heSBuZWVkIHRvIHNwbGl0IHlvdXIgZGF0YSBpbnRvIG11bHRpcGxlIGRhdGEgc2V0cywgcmVzaGFwZSB0aGUgcGFydHMsIGFuZCB0aGVuIGpvaW4gdGhlIHJlc3VsdHMgYmFjayB0b2dldGhlci4KCkluIG1vc3QgY2FzZXMgdGhvdWdoLCB5b3UgY2FuIHBpdm90IHRvIGEgY29tcGxldGVseSBsb25nIGZvcm1hdCwgYW5kIHRoZW4gc3ByZWFkIGNvbHVtbnMgYmFjayBvdXQgd2lkZXIgYWZ0ZXIgc29tZSBhZGRpdGlvbmFsIHRyYW5zZm9ybWF0aW9ucy4gSWYgdGhlcmUgYXJlIGRpZmZlcmVudCBkYXRhIHR5cGVzLCB0aGV5IGdldCBjb252ZXJ0ZWQgdG8gdGhlIG1vcmUgZ2VuZXJhbCBvbmUsIGZvbGxvd2luZzogYm9vbGVhbiAtXD4gaW50ZWdlciAtXD4gbnVtZXJpYyAtXD4gY2hhcmFjdGVyLiBXaGlsZSB5b3UgbmVlZCB0byBiZSBjYXJlZnVsIGlmIHlvdSBhcmUgd29ya2luZyB3aXRoIHByZWNpc2UgbnVtZXJpY2FsIHZhbHVlcyAoZGVjaW1hbCB2YWx1ZXMpLCBpZiB5b3UncmUgd29ya2luZyB3aXRoIHdob2xlIG51bWJlcnMgb3IgdGhlIHByZWNpc2lvbiAobnVtYmVyIG9mIHNpZ25pZmljYW50IGRpZ2l0cykgaW4gdGhlIGRhdGEgaXMgc21hbGwsIGNvbnZlcnRpbmcgZnJvbSBudW1lcmljIHRvIGNoYXJhY3RlciBhbmQgYmFjayBhZ2FpbiB3aWxsIG5vdCBsb3NlIGluZm9ybWF0aW9uLgoKIyMgU2VwYXJhdGUgdmFyaWFibGUgbmFtZXMKCk9rLCBiYWNrIHRvIG91ciBXSE8gZGF0YS4gT25lIHRyaWNrIGJlZm9yZSB3ZSBsZWFybiBob3cgdG8gcGl2b3Qgd2lkZXIgdG8gdW5kbyB3aGF0IHdlIGRpZC4gV2hlbiB3ZSBwaXZvdCBsb25nZXIsIG91ciBuZXcgIm1lYXN1cmUiIHZhcmlhYmxlIGFjdHVhbGx5IGhhcyBtdWx0aXBsZSBwaWVjZXMgb2YgaW5mb3JtYXRpb24gaW4gaXQuIFRoZSB2YXJpYWJsZSBuYW1lcyBhcmUgb2YgdGhlIGZvcm1hdDogIm5ld1xfIiBkaWFnbm9zaXMgXF8gbS9mIGFnZS4gSWRlYWxseSwgd2UnZCBsaWtlIHRoaXMgaW5mb3JtYXRpb24gc2VwYXJhdGVkLCBzbyB3ZSBoYXZlIGEgY29sdW1uIGZvciB0aGUgZGlhZ25vc2lzIG1ldGhvZCwgYSBjb2x1bW4gZm9yIHRoZSBnZW5kZXIsIGFuZCBhIGNvbHVtbiBmb3IgdGhlIGFnZSBncm91cC4KCldlIGNhbiB0ZWxsIHBpdm90X2xvbmdlciB0byBkbyB0aGlzIGZvciB1czoKCmBgYHtyLCBldmFsPUZBTFNFfQpuYW1lcyh3aG8yKQoKcGl2b3RfbG9uZ2VyKHdobzIsIC1jb3VudHJ5Oi15ZWFyLAogICAgICAgICAgICAgIyB3ZSdsbCBrZWVwIDMgY29sdW1uczogbWFrZSB1cCBhIG5hbWUgZm9yIGVhY2gKICAgICAgICAgICAgIG5hbWVzX3RvPWMoImRpYWdub3NpcyIsICJnZW5kZXIiLCAiYWdlIiksICAKICAgICAgICAgICAgICMgdGhpcyBpcyBhIHJlZ3VsYXIgZXhwcmVzc2lvbiB0aGF0IGNhcHR1cmVzIHRoZSBwYXR0ZXJuCiAgICAgICAgICAgICAjIGVhY2ggKCkgc2V0IGlzIGEgdmFsdWUgdG8ga2VlcCBhbmQgdHVybiBpbnRvIGEgY29sdW1uOwogICAgICAgICAgICAgIyB3ZSBhcmUgZHJvcHBpbmcgIm5ldyIKICAgICAgICAgICAgIG5hbWVzX3BhdHRlcm4gPSAibmV3Xz8oLispXyhbbWZdKShcXGQrKSIsCiAgICAgICAgICAgICB2YWx1ZXNfdG89ImNvdW50IikgIApgYGAKCklmIHRoZSB2YXJpYWJsZSBuYW1lcyBoYWQgYSBzaW1wbGVyIHBhdHRlcm4sIGZvciBleGFtcGxlOiB5ZWFyX21lYXN1cmUsIHRoZW4gd2UgY291bGQgdXNlIHRoZSAibmFtZXNfc2VwIiBhcmd1bWVudCBpbnN0ZWFkIG9mIHRoZSBtb3JlIGNvbXBsaWNhdGVkICJuYW1lc19wYXR0ZXJuIiB0byBzcGxpdCB1cCB0aGUgY29sdW1uLgoKIyBQaXZvdCBXaWRlcgoKTm93LCB3ZSd2ZSBnb3Qgb3VyIGNvbXBsZXRlbHkgbG9uZyBkYXRhLiBIb3cgZG8gd2UgZ2V0IGl0IGludG8gYSBmb3JtIHdoZXJlIHRoZXJlJ3Mgb25lIHJvdyBwZXIgY291bnRyeS15ZWFyLWdlbmRlci1hZ2UsIHdpdGggb25lIGNvbHVtbiBwZXIgbWVhc3VyZT8gRXNzZW50aWFsbHksIHdlIHdhbnQgdG8gc3BsaXQgdGhlIGRpYWdub3NpcyBjb2x1bW4gaW50byBtdWx0aXBsZSBjb2x1bW5zIGZvciBlYWNoIHR5cGUgb2YgZGlhZ25vc2lzLgoKIVtdKHBpdm90X3dpZGVyX25ldy5wbmcpCgpJbWFnZSBTb3VyY2U6IDxodHRwczovL2VwaXJoYW5kYm9vay5jb20vZW4vcGl2b3RpbmctZGF0YS5odG1sPgoKYGBge3J9CiMgc2FtZSBhcyBhYm92ZSwganVzdCBzYXZlIGl0CmxvbmdfZGF0YSA8LSBwaXZvdF9sb25nZXIod2hvMiwgLWNvdW50cnk6LXllYXIsCiAgICAgICAgICAgICBuYW1lc190bz1jKCJkaWFnbm9zaXMiLCAiZ2VuZGVyIiwgImFnZSIpLCAKICAgICAgICAgICAgIHZhbHVlc190bz0iY291bnQiLAogICAgICAgICAgICAgbmFtZXNfcGF0dGVybiA9ICJuZXdfPyguKylfKFttZl0pKFxcZCspIikgCgpuYW1lcyhsb25nX2RhdGEpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CiMgc3ByZWFkIGl0IGJhY2sgb3V0CnBpdm90X3dpZGVyKGxvbmdfZGF0YSwgCiAgICAgICAgICAgIG5hbWVzX2Zyb209ZGlhZ25vc2lzLAogICAgICAgICAgICB2YWx1ZXNfZnJvbT1jb3VudCkKYGBgCgpXaGF0IGhhcHBlbnMgaWYgb3VyIGxvbmcgZGF0YSBkb2Vzbid0IGhhdmUgZXZlcnkgY29tYmluYXRpb24gb2YgdmFsdWVzIHRvIGZpbGwgaW50byBvdXIgbmV3IGRhdGEgc2V0PyBGb3IgZXhhbXBsZSwgaWYgdGhlIGRhdGEgc3RhcnRlZCBsb25nIGluc3RlYWQgb2Ygd2lkZT8KCkZvciBleGFtcGxlLCBpZiB3ZSBoYWQgZGF0YSB0aGF0IGxvb2tlZCBsaWtlOgoKYGBge3IsIGV2YWw9RkFMU0V9CmxvbmdfZGF0YSAlPiUKICBmaWx0ZXIoIWlzLm5hKGNvdW50KSkgJT4lCiAgc2FtcGxlX24oMjApICU+JQogIGFycmFuZ2UoY291bnRyeSwgeWVhciwgZGlhZ25vc2lzLCBhZ2UsIGdlbmRlcikKYGBgCgpJdCdzIG9rIC0gd2UgY2FuIHN0aWxsIHBpdm90IHdpZGVyLiBCeSBkZWZhdWx0LCBpdCB3aWxsIGZpbGwgaW4gYE5BYCB3aGVyZSBpdCBkb2Vzbid0IGhhdmUgYSB2YWx1ZSwgb3Igd2UgY2FuIHRlbGwgaXQgdG8gZmlsbCB3aXRoIGFub3RoZXIgdmFsdWUgKHN1Y2ggYXMgMCkuCgpgYGB7ciwgZXZhbD1GQUxTRX0KbG9uZ19kYXRhICU+JQogIGZpbHRlcighaXMubmEoY291bnQpKSAlPiUKICBzYW1wbGVfbigyMCkgJT4lCiAgYXJyYW5nZShjb3VudHJ5LCB5ZWFyLCBkaWFnbm9zaXMsIGFnZSwgZ2VuZGVyKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tPWRpYWdub3NpcywgCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb209Y291bnQpCmBgYAoKV2UgY291bGQgYWxzbyBwaXZvdCB3aWRlciBtb3JlIHRoYW4gb25lIGNvbHVtbiBhdCBvbmNlLiBMZXQncyBoYXZlIG9uZSByb3cgcGVyIGNvdW50cnkteWVhci1kaWFnbm9zaXMsIGFuZCBvbmUgY29sdW1uIGZvciBlYWNoIGFnZS1nZW5kZXIgY29tYmluYXRpb246CgpgYGB7cn0KbmFtZXMobG9uZ19kYXRhKQoKbG9uZ19kYXRhICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb209YyhhZ2UsIGdlbmRlciksCiAgICAgICAgICAgICAgIyBjb250cm9scyBob3cgbmV3IHZhcmlhYmxlIG5hbWVzIGFyZSBjcmVhdGVkCiAgICAgICAgICAgICAgIyB1c2VzIHN5bnRheCBmcm9tIHRoZSBnbHVlIHBhY2thZ2UKICAgICAgICAgICAgICBuYW1lc19nbHVlPSJ7Z2VuZGVyfV97YWdlfSIsIAogICAgICAgICAgICAgIHZhbHVlc19mcm9tPWNvdW50KQpgYGAKCiMjIyBFWEVSQ0lTRSAyCgpQaXZvdCB0aGUgYGxvbmdfZGF0YWAgZGF0YSBmcmFtZSB3aWRlciBzbyB0aGF0IGVhY2ggcm93IGlzIGEgY291bnRyeS1kaWFnbm9zaXMtZ2VuZGVyLWFnZSBvYnNlcnZhdGlvbiAocGl2b3QgInllYXIiIHNvIHlvdSBoYXZlIG9uZSBjb2x1bW4gZm9yIGVhY2ggeWVhciBpbiB0aGUgb3V0cHV0KS4KCltIaW50XXsudW5kZXJsaW5lfTogQnJlYWsgZG93biB0aGUgcHJvYmxlbSBpbnRvIHN0ZXBzLiBUaGluayBhYm91dCB3aGljaCBjb2x1bW4ocykgaW4geW91ciBvcmlnaW5hbCBkYXRhc2V0IG5lZWQgdG8gYmUgc3ByZWFkIG91dCBpbnRvIG11bHRpcGxlIGNvbHVtbnMgdG8gbWFrZSB0aGUgZGF0YSAid2lkZSIuCgpgYGB7cn0KCmBgYAoKIyBTZXBhcmF0ZQoKTXVjaCBsaWtlIHdlIGhhZCBgcGl2b3RfbG9uZ2VyKClgIHNwbGl0IHVwIHRoZSBjb21wb25lbnRzIG9mIHRoZSB2YXJpYWJsZSBuYW1lcyBmb3IgdXMgaW50byBtZWFzdXJlLCBhZ2UsIGFuZCBnZW5kZXIsIHdlIGNhbiB1c2UgdGhlIGBzZXBhcmF0ZSgpYCBmdW5jdGlvbiB0byBkbyB0aGF0IHRvIGNvbHVtbnMgb3V0c2lkZSBvZiBwaXZvdGluZy4KCmBgYHtyfQpub2JlbHMgPC0gdHJpYmJsZSh+bmFtZSwgfmNhdGVnb3J5LAogICAgICAgICAgICAgICAgICAgICJCb2IgRHlsYW4iLCAiTGl0ZXJhdHVyZSIsCiAgICAgICAgICAgICAgICAgICAgIkFubmUgTCdIdWlsbGllciIsICJQaHlzaWNzIiwKICAgICAgICAgICAgICAgICAgICAiTmFyZ2VzIE1vaGFtbWFkaSIsICJQZWFjZSIsCiAgICAgICAgICAgICAgICAgICAgIlJvZ2VyIFBlbnJvc2UiLCAiUGh5c2ljcyIsCiAgICAgICAgICAgICAgICAgICAgIkplbm5pZmVyIERvdWRuYSIsICJDaGVtaXN0cnkiLAogICAgICAgICAgICAgICAgICAgICJBYmhpaml0IEJhbmVyamVlIiwgIkVjb25vbWljcyIsCiAgICAgICAgICAgICAgICAgICAgIkplYW4tUGllcnJlIFNhdXZhZ2UiLCAiQ2hlbWlzdHJ5IikKbm9iZWxzCmBgYAoKV2UgY291bGQgc3BsaXQgdGhlIG5hbWUgY29sdW1uIGludG8gZmlyc3QgbmFtZSBhbmQgbGFzdCBuYW1lLCB1c2luZyBhIHNwYWNlIGNoYXJhY3RlciBhcyBhIGRlbGltaXRlci4KCmBgYHtyfQpzZXBhcmF0ZShub2JlbHMsICAjIGRhdGEgZnJhbWUgbmFtZQogICAgICAgICBuYW1lLCAgIyBleGlzdGluZyBjb2x1bW4gbmFtZSB0byBzcGxpdAogICAgICAgICBpbnRvPWMoImZpcnN0IiwgImxhc3QiKSwgICMgbmV3IGNvbHVtbiBuYW1lcyAKICAgICAgICAgc2VwPSIgIikgICMgd2hhdCB0byBzcGxpdCBvbgpgYGAKCllvdSBuZWVkIHRvIGtub3cgdGhlIG1heGltdW0gbnVtYmVyIG9mIHBpZWNlcyB0aGF0IHlvdXIgZGF0YSB3aWxsIGJlIHNwbGl0IGludG8sIG9yIHlvdSdsbCBsb3NlIHNvbWUgb2YgdGhlIGluZm9ybWF0aW9uLiBJZiBKZWFuLVBpZXJyZSBTYXV2YWdlJ3MgbmFtZSBoYWQgYSBzcGFjZSBpbnN0ZWFkIG9mIGEgaHlwaGVuOgoKYGBge3J9Cm5vYmVscyA8LSB0cmliYmxlKH5uYW1lLCB+Y2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgICAgIkJvYiBEeWxhbiIsICJMaXRlcmF0dXJlIiwKICAgICAgICAgICAgICAgICAgICAiQW5uZSBMJ0h1aWxsaWVyIiwgIlBoeXNpY3MiLAogICAgICAgICAgICAgICAgICAgICJOYXJnZXMgTW9oYW1tYWRpIiwgIlBlYWNlIiwKICAgICAgICAgICAgICAgICAgICAiUm9nZXIgUGVucm9zZSIsICJQaHlzaWNzIiwKICAgICAgICAgICAgICAgICAgICAiSmVubmlmZXIgRG91ZG5hIiwgIkNoZW1pc3RyeSIsCiAgICAgICAgICAgICAgICAgICAgIkFiaGlqaXQgQmFuZXJqZWUiLCAiRWNvbm9taWNzIiwKICAgICAgICAgICAgICAgICAgICAiSmVhbiBQaWVycmUgU2F1dmFnZSIsICJDaGVtaXN0cnkiKQoKc2VwYXJhdGUobm9iZWxzLAogICAgICAgICBuYW1lLAogICAgICAgICBpbnRvPWMoImZpcnN0IiwgImxhc3QiKSwKICAgICAgICAgc2VwPSIgIikKYGBgCgpXZSBnZXQgYSB3YXJuaW5nLCBhbmQgIlNhdXZhZ2UiIHdhcyBqdXN0IGRyb3BwZWQgY29tcGxldGVseSEgKFlvdSBjYW4gY29udHJvbCB0aGlzIGJlaGF2aW9yIHRvIHNvbWUgZXh0ZW50IHdpdGggdGhlIGBleHRyYWAgYXJndW1lbnQuKQoKSXQncyBiZXR0ZXIgdG8gc3BlY2lmeSB0aGUgbWF4aW11bSBudW1iZXIgb2YgY29sdW1ucyBwb3NzaWJsZSBpbnN0ZWFkOgoKYGBge3J9CnNlcGFyYXRlKG5vYmVscywKICAgICAgICAgbmFtZSwKICAgICAgICAgaW50bz1jKCJmaXJzdDEiLCAiZmlyc3QyIiwgImxhc3QiKSwKICAgICAgICAgc2VwPSIgIikKYGBgCgpOb3cgdGhlIHdhcm5pbmcgaXMgdGVsbGluZyB1cyBpdCBmaWxsZWQgaW4gYE5BYCB2YWx1ZXMgd2hlcmUgaXQgZGlkbid0IGZpbmQgMyBwaWVjZXMgYWZ0ZXIgc2VwYXJhdGluZy4gQWxzbywgdGhlIG5ldyBjb2x1bW4gbmFtZXMgYXJlIG5vdCBleGFjdGx5IHN1aXRhYmxlIHNpbmNlIG90aGVyIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YXNldCBhbGwgaGF2ZSBsYXN0IG5hbWVzIChub3QgYE5BYCkuCgpCdXQgYHNlcGFyYXRlYCByZWFsbHkgd29ya3MgYmVzdCB3aGVuIHlvdSdyZSBzZXBhcmF0aW5nIHNvbWV0aGluZyB0aGF0IHdpbGwgc2VwYXJhdGUgaW50byBhIGZpeGVkIG51bWJlciBvZiBjb2x1bW5zLgoKU2lkZSBub3RlOiB3b3JraW5nIHdpdGggbmFtZXMgaXMgSEFSRCEgRm9yIHNvbWUgZXhhbXBsZXMgd2h5LCBzZWUgPGh0dHBzOi8vd3d3LmthbHp1bWV1cy5jb20vMjAxMC8wNi8xNy9mYWxzZWhvb2RzLXByb2dyYW1tZXJzLWJlbGlldmUtYWJvdXQtbmFtZXMvPgoKIyMjIEVYRVJDSVNFIDMKClNlcGFyYXRlIHRoZSAibG9jYXRpb24iIHZhcmlhYmxlIGluIGBhZGRyZXNzZXNgIGludG8gc2VwYXJhdGUgImNpdHkiIGFuZCAic3RhdGUiIGNvbHVtbnMuIEhpbnQ6IHRoZSAic2VwIiB2YWx1ZSBjYW4gYmUgbW9yZSB0aGFuIG9uZSBjaGFyYWN0ZXIgaW4gbGVuZ3RoLgoKYGBge3J9CmFkZHJlc3NlcyA8LSB0cmliYmxlKH5uYW1lLCB+bG9jYXRpb24sCiAgICAgICAgICAgICAgICAgICAgICJLYXRoZXJpbmUiLCAiQ2hpY2FnbywgSUwiLAogICAgICAgICAgICAgICAgICAgICAiQm9iIiwgIkJlbmQsIE9SIiwKICAgICAgICAgICAgICAgICAgICAgIkxvdWthIiwgIkhvbm9sdWx1LCBISSIsCiAgICAgICAgICAgICAgICAgICAgICJBbGV4IiwgIlNhbiBKb3NlLCBDQSIpCmBgYAoKIyBzZXBhcmF0ZV9yb3dzKCkKCkluIGNhc2VzIHdoZXJlIHlvdSB3YW50IHRvIHNlcGFyYXRlIG91dCBhIGNvbHVtbiwgYnV0IHlvdSBkb24ndCBrbm93IGhvdyBtYW55IHZhbHVlcyBtaWdodCBiZSBpbiBpdCwgYW5vdGhlciBvcHRpb24gaXMgdG8gbWFrZSBvbmUgcm93LCBpbnN0ZWFkIG9mIG9uZSBjb2x1bW4sIHBlciB2YWx1ZS4gSXQncyBjb21iaW5pbmcgYSBzZXBhcmF0ZSB3aXRoIGEgcGl2b3QgbG9uZ2VyLgoKVGhpcyBjYW4gYmUgdXNlZnVsIGlmIHlvdSBoYXZlIGEgY29sdW1uIHdpdGggYSBkZWxpbWl0ZWQgbGlzdCBvZiB2YWx1ZXMgaW4gaXQuIEZvciBleGFtcGxlLCBpZiB5b3UgYXNrIGEgbXVsdGlwbGUgY2hvaWNlIHF1ZXN0aW9uIHRoYXQgYWxsb3dzIG11bHRpcGxlIGFuc3dlcnMgaW4gUXVhbHRyaWNzIChzdXJ2ZXkgcGxhdGZvcm0pLCB5b3UnbGwgZ2V0IGEgc2luZ2xlIGNvbHVtbiB3aXRoIGFsbCBvZiB0aGUgYW5zd2VyIHRoYXQgcGVvcGxlIGNoZWNrZWQ6CgpgYGB7cn0Kc3VydmV5IDwtIHRyaWJibGUofmlkLCB+cTEsIH5xMiwKICAgICAgICAgICAgICAgICAgNDUsIDQsICJUaW1lIiwKICAgICAgICAgICAgICAgICAgNDYsIDMsICJRdWFsaXR5O0Nvc3QiLAogICAgICAgICAgICAgICAgICA0NywgNCwgIlRpbWU7Q29zdDtDb21wYXRpYmlsaXR5O1dhcnJhbnR5IikKc3VydmV5CmBgYAoKSG93IGRvIHdlIGRlYWwgd2l0aCBxMj8KCmBgYHtyfQpzZXBhcmF0ZV9yb3dzKHN1cnZleSwgCiAgICAgICAgICAgICAgcTIsICAjIHdoaWNoIGNvbHVtbiB0byBzZXBhcmF0ZQogICAgICAgICAgICAgIHNlcD0iOyIpCmBgYAoKIyMgQ3JlYXRpbmcgSW5kaWNhdG9yIFZhcmlhYmxlcwoKVGhlbiwgd2UgbWF5IHdhbnQgdG8gY3JlYXRlIG9uZSBjb2x1bW4gcGVyIGFuc3dlciBjaG9pY2UgLS0gY3JlYXRlIGluZGljYXRvciB2YXJpYWJsZXMgKGFrYSBkdW1teSB2YXJpYWJsZXMgb3Igb25lLWhvdCBlbmNvZGluZykuCgpgYGB7cn0Kc2VwYXJhdGVfcm93cyhzdXJ2ZXksIAogICAgICAgICAgICAgIHEyLCAgIyB3aGljaCBjb2x1bW4gdG8gc2VwYXJhdGUKICAgICAgICAgICAgICBzZXA9IjsiKSAlPiUKICBtdXRhdGUoZXhpc3RzPTEpICU+JSAgIyBhZGQgYSBjb2x1bW4gdG8gZW5jb2RlIHZhbHVlcyBsYXRlcgogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb209cTIsICAjIG5ldyBjb2x1bW4gbmFtZXMsIG9uZSBmb3IgZWFjaCB2YWx1ZQogICAgICAgICAgICAgIHZhbHVlc19mcm9tPWV4aXN0cywgICMgc28gdGhlcmUgd2lsbCBiZSBhIDEgd2hlcmUgdGhlcmUgd2FzIGEgdmFsdWUKICAgICAgICAgICAgICB2YWx1ZXNfZmlsbD0wKSAjIyBmaWxscyBpbiBtaXNzaW5nIHZhbHMgd2l0aCAwIGluc3RlYWQgb2YgTkEKYGBgCgojIyBMZWFybmluZyBNb3JlCgpUaGUgYHt0aWR5cn1gIGRvY3VtZW50YXRpb24gaGFzIGEgdHV0b3JpYWwgb24gcGl2b3Rpbmc6IDxodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvcGl2b3QuaHRtbD4KCiMgUmVjYXAKCllvdSBub3cgY2FuIHVzZSBkaWZmZXJlbnQgYHt0aWR5cn1gIGNvbW1hbmRzLCBzdWNoIGFzIGBwaXZvdF9sb25nZXJgLCBgcGl2b3Rfd2lkZXJgLCBgc2VwYXJhdGVgLCBgc2VwYXJhdGVfcm93c2AsIHRvIHJlc2hhcGUgeW91ciBkYXRhIGluIGEgd2lkZSB2YXJpZXR5IG9mIHdheXMuCgpOZXh0IHNlc3Npb246IHRoZSBge2dncGxvdDJ9YCBmdW5jdGlvbnMgZm9yIHZpc3VhbGl6aW5nIHlvdXIgZGF0YS4KCiMgQW5zd2VycyB0byB0aGUgZXhlcmNpc2VzCgojIyMgRVhFUkNJU0UgMQoKYGBge3J9CgojIHNvbHV0aW9uIDEKcGl2b3RfbG9uZ2VyKHBvcHMsIAogICAgICAgICAgICAgY29scz1jKCJ5ZWFyMjAxMSIsICJ5ZWFyMjAxMiIsICJ5ZWFyMjAxMyIpLCAjIGNvbHVtbnMgdG8gY29sbGFwc2UgdG9nZXRoZXIKICAgICAgICAgICAgIG5hbWVzX3RvID0gInllYXIiLCAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjb2x1bW4gdGhhdCB3aWxsIHN0b3JlIHRoZSB5ZWFyIG5hbWVzCiAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicG9wdWxhdGlvbiIpICAgICAgICAgICAgICAgICAgICMgY29sdW1uIHRoYXQgd2lsbCBzdG9yZSBwb3B1bGF0aW9uIHZhbHVlcwoKIyBzb2x1dGlvbiAyCnBpdm90X2xvbmdlcihwb3BzLCAKICAgICAgICAgICAgIGNvbHM9LWNvdW50cnksICMgc2FtZSBhcyBzZWxlY3RpbmcgYWxsIHRoZSB5ZWFyIGNvbHVtbnMKICAgICAgICAgICAgIG5hbWVzX3RvID0gInllYXIiLCAKICAgICAgICAgICAgIHZhbHVlc190byA9ICJwb3B1bGF0aW9uIikKYGBgCgojIyMgRVhFUkNJU0UgMgoKYGBge3J9CiMgY2hlY2sgY29sdW1ucyBpbiBzdGFydGluZyBkYXRhc2V0Cm5hbWVzKGxvbmdfZGF0YSkKCiMgc29sdXRpb24gMQpsb25nX2RhdGEgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB5ZWFyLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IGNvdW50KQoKIyBzb2x1dGlvbiAyIC0gbW9yZSBlbGVnYW50IGNvbHVtbnMgbmFtZXMgYWZ0ZXIgcGl2b3RpbmcKbG9uZ19kYXRhICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0geWVhciwgCiAgICAgICAgICAgICAgbmFtZXNfZ2x1ZSA9ICJ5ZWFyX3t5ZWFyfSIsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBjb3VudCkKYGBgCgojIyMgRVhFUkNJU0UgMwoKYGBge3J9CnNlcGFyYXRlKGFkZHJlc3NlcywgCiAgICAgICAgIGxvY2F0aW9uLCAKICAgICAgICAgaW50bz1jKCJjaXR5IiwgInN0YXRlIiksIAogICAgICAgICBzZXA9IiwgIikKYGBgCg==