Note: some output from the code is not included here to keep the file size smaller. Run the code to see the output.
This is an R Markdown document. Follow the link to learn more about R Markdown and the notebook format used during the workshop.
Setup
It’s important you have tidyr version 1.1.0 (at least) installed. Check this with:
packageVersion("tidyr")
## [1] '1.1.4'
If needed, update it with:
install.packages("tidyr")
Load it as part of tidyverse
library(tidyverse)
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).
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 have 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
Pivot the pops data frame below to be in a longer format, with rows for each country-year instead of each country.
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:
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?
# 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+)")
# 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:
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
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).
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.
students <- tribble(~name, ~department,
"Christina Maimone", "Political Science",
"Colby Witherup-Wood", "Plant Biology",
"Jackie Milhans", "Materials Science",
"Andre Archer", "Applied Math",
"Dan Turner", "Linguistics")
students
We could split the name column into first name and last name, using a space character as a delimiter.
separate(students, # 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 Colby’s name had a space instead of a hyphen:
students <- tribble(~name, ~department,
"Christina Maimone", "Political Science",
"Colby Witherup Wood", "Plant Biology",
"Jackie Milhans", "Materials Science",
"Andre Archer", "Applied Math",
"Dan Turner", "Linguistics")
separate(students,
name,
into=c("first", "last"),
sep=" ")
## Warning: Expected 2 pieces. Additional pieces discarded in 1 rows [2].
We get a warning, and “Wood” 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(students,
name,
into=c("first", "last1", "last2"),
sep=" ")
## Warning: Expected 3 pieces. Missing pieces filled with `NA` in 4 rows [1, 3, 4,
## 5].
Now the warning is telling us it filled in NA values where it didn’t find 3 pieces after separating.
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
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,
"Christina", "Evanston, 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
LS0tCnRpdGxlOiAndGlkeXInCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgotLS0KCmBgYHtyLCBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWw9VFJVRSkKYGBgCgoqTm90ZTogc29tZSBvdXRwdXQgZnJvbSB0aGUgY29kZSBpcyBub3QgaW5jbHVkZWQgaGVyZSB0byBrZWVwIHRoZSBmaWxlIHNpemUgc21hbGxlci4gIFJ1biB0aGUgY29kZSB0byBzZWUgdGhlIG91dHB1dC4qCgpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS8pIGRvY3VtZW50LiAgRm9sbG93IHRoZSBsaW5rIHRvIGxlYXJuIG1vcmUgYWJvdXQgUiBNYXJrZG93biBhbmQgdGhlIG5vdGVib29rIGZvcm1hdCB1c2VkIGR1cmluZyB0aGUgd29ya3Nob3AuCgojIFNldHVwCgpJdCdzIGltcG9ydGFudCB5b3UgaGF2ZSB0aWR5ciB2ZXJzaW9uIDEuMS4wIChhdCBsZWFzdCkgaW5zdGFsbGVkLiAgQ2hlY2sgdGhpcyB3aXRoOgoKYGBge3J9CnBhY2thZ2VWZXJzaW9uKCJ0aWR5ciIpCmBgYAoKSWYgbmVlZGVkLCB1cGRhdGUgaXQgd2l0aDoKCmBgYHtyLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5ciIpCmBgYAoKTG9hZCBpdCBhcyBwYXJ0IG9mIHRpZHl2ZXJzZQoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgoKIyBEYXRhCgpXZSdyZSBnb2luZyB0byB3b3JrIHdpdGggYSBkYXRhc2V0IHRoYXQncyBwYXJ0IG9mIHRoZSB0aWR5ciBwYWNrYWdlOiBgd2hvYAoKVGhlIGRldGFpbHM6IEEgc3Vic2V0IG9mIGRhdGEgZnJvbSB0aGUgV29ybGQgSGVhbHRoIE9yZ2FuaXphdGlvbiBHbG9iYWwgVHViZXJjdWxvc2lzIFJlcG9ydCwgYW5kIGFjY29tcGFueWluZyBnbG9iYWwgcG9wdWxhdGlvbnMuICBUaGUgZGF0YSB1c2VzIHRoZSBvcmlnaW5hbCBjb2RlcyBnaXZlbiBieSB0aGUgV29ybGQgSGVhbHRoIE9yZ2FuaXphdGlvbi4gVGhlIGNvbHVtbiBuYW1lcyBmb3IgY29sdW1ucyA1IHRocm91Z2ggNjAgYXJlIG1hZGUgYnkgY29tYmluaW5nICJuZXdfIiB0byBhIGNvZGUgZm9yIG1ldGhvZCBvZiBkaWFnbm9zaXMgKHJlbCA9IHJlbGFwc2UsIHNuID0gbmVnYXRpdmUgcHVsbW9uYXJ5IHNtZWFyLCBzcCA9IHBvc2l0aXZlIHB1bG1vbmFyeSBzbWVhciwgZXAgPSBleHRyYXB1bG1vbmFyeSkgdG8gYSBjb2RlIGZvciBnZW5kZXIgKGYgPSBmZW1hbGUsIG0gPSBtYWxlKSB0byBhIGNvZGUgZm9yIGFnZSBncm91cCAoMDE0ID0gMC0xNCB5cnMgb2YgYWdlLCAxNTI0ID0gMTUtMjQgeWVhcnMgb2YgYWdlLCAyNTM0ID0gMjUgdG8gMzQgeWVhcnMgb2YgYWdlLCAzNTQ0ID0gMzUgdG8gNDQgeWVhcnMgb2YgYWdlLCA0NTU0ID0gNDUgdG8gNTQgeWVhcnMgb2YgYWdlLCA1NTY0ID0gNTUgdG8gNjQgeWVhcnMgb2YgYWdlLCA2NSA9IDY1IHllYXJzIG9mIGFnZSBvciBvbGRlcikuCgpDb2x1bW4gbmFtZXM6CgpgYGB7cn0KbmFtZXMod2hvKQpgYGAKCgpMZXQncyBsb29rIGF0IGEgZmV3IHJhbmRvbSByb3dzOgoKYGBge3J9CnNhbXBsZV9uKHdobywgMTApCmBgYAoKSW4gdGhpcyBkYXRhIHNldCwgZWFjaCByb3cgKGVhY2ggb2JzZXJ2YXRpb24pIGlzIGEgY291bnRyeS15ZWFyLiAgRWFjaCBjb2x1bW4gaXMgYSBtZWFzdXJlLWFnZSB2YWx1ZS4gIAoKVGhlcmUgYXJlIHNpdHVhdGlvbnMgdGhvdWdoIHdoZXJlIHdlIG1pZ2h0IHdhbnQgZWFjaCByb3cgdG8gYmUgYSBkaWZmZXJlbnQgdW5pdCBvZiBvYnNlcnZhdGlvbi4gIFdlIGNvdWxkIG1ha2UgYSByZWFsbHkgd2lkZSBkYXRhIHNldCAob25lIHdpdGggbG90cyBvZiBjb2x1bW5zKSwgd2hlcmUgZWFjaCByb3cgaXMgYSBzaW5nbGUgY291bnRyeSBhbmQgdGhlcmUgYXJlIGNvbHVtbnMgZm9yIGV2ZXJ5IG1lYXN1cmUgaW4gZXZlcnkgeWVhci4gIE9yIHdlIGNvdWxkIG1ha2UgYSBkYXRhIHNldCB0aGF0J3MgcmVhbGx5IGxvbmcsIHdoZXJlIGVhY2ggcm93IGlzIGEgY291bnRyeS15ZWFyLW1lYXN1cmUtYWdlIG9ic2VydmF0aW9uLiAgT3Igb25lIHRoYXQncyBpbiBiZXR3ZWVuLCB3aXRoIGVhY2ggcm93IGJlaW5nIGNvdW50cnkteWVhci1hZ2UsIGFuZCB0aGVyZSBhcmUgY29sdW1ucyBmb3IgZWFjaCBtZWFzdXJlLgoKVGhlc2UgZGlmZmVyZW50IGNvbmZpZ3VyYXRpb25zIHN1cHBvcnQgZGlmZmVyZW50IHR5cGVzIG9mIHN0YXRpc3RpY2FsIG1vZGVscywgZGF0YSB0cmFuc2Zvcm1hdGlvbnMsIGFuZCBkYXRhIHZpc3VhbGl6YXRpb25zLiAgTm8gY29uZmlndXJhdGlvbiBpcyBuZWNlc3NhcmlseSB0aGUgcmlnaHQgb25lIG9yIGJldHRlciB0aGFuIHRoZSBvdGhlcnMgaW4gYSB2YWN1dW0gLSBpdCBkZXBlbmRzIG9uIHdoYXQgeW91IHdhbnQgdG8gZG8gd2l0aCB0aGUgZGF0YSEKCkknbSBnb2luZyB0byByZWR1Y2UgdGhpcyBkYXRhc2V0IHRvIGp1c3QgYSBmZXcgY291bnRyaWVzIHRoYXQgaGF2ZSBhdCBsZWFzdCBzb21lIGRhdGEgZmlsbGVkIGluIHRvIG1ha2UgaXQgZWFzaWVyIGZvciB1cyB0byB3b3JrIHdpdGg6CgpgYGB7cn0Kd2hvMiA8LSBmaWx0ZXIod2hvLCBjb3VudHJ5ICVpbiUgYygiQ2FuYWRhIiwgIkN6ZWNoIFJlcHVibGljIiwgIk1vcm9jY28iKSkKYGBgCgoKCiMgUGl2b3QgTG9uZ2VyCgpXZSBtYWtlIGEgZGF0YSBzZXQgbG9uZ2VyIGJ5IHJlZHVjaW5nIHRoZSBudW1iZXIgb2YgY29sdW1ucyBhbmQgaW5jcmVhc2luZyB0aGUgbnVtYmVyIG9mIHJvd3MuICBXZSdsbCBzdGFjayBzb21lIG9mIHRoZSBjb2x1bW5zIG9uIHRvcCBvZiBlYWNoIG90aGVyLCBhbmQgZHVwbGljYXRlIHRoZSB2YWx1ZXMgaW4gb3RoZXIgY29sdW1ucyB0byBtYWtlIHRoaXMgaGFwcGVuLiAgVGhpcyBjaGFuZ2VzIHdoYXQgdGhlIHVuaXQgb2Ygb2JzZXJ2YXRpb24gaXMgKHdoYXQgZWFjaCByb3cgcmVwcmVzZW50cykuCgojIyBBIE1pbmkgRXhhbXBsZQoKQmVmb3JlIHdlIHdvcmsgd2l0aCBvdXIgbGFyZ2VyIGRhdGEgc2V0LCBsZXQncyBsb29rIGF0IGEgdmVyeSBzbWFsbCBvbmUgc28gaXQncyBlYXNpZXIgdG8gc2VlIHdoYXQncyBoYXBwZW5pbmcuCgpgdHJpYmJsZSgpYCBpcyBhIGZ1bmN0aW9uIHRoYXQgbWFrZXMgaXQgZWFzaWVyIHRvIG1hbnVhbGx5IGNyZWF0ZSBhIHRpYmJsZSAoZGF0YSBmcmFtZSkKCmBgYHtyfQpmb29kcyA8LSB0cmliYmxlKH5pZCwgfmEsIH5iLCB+YywKICAgICAgICAgICAgICAgICAxLCAiYXBwbGUiLCAiYmFuYW5hIiwgImNvZCIsIAogICAgICAgICAgICAgICAgIDIsICJhc3BhcmFndXMiLCAiYmFjb24iLCAiY2hvY29sYXRlIikKZm9vZHMKYGBgCgpJbiBgZm9vZHNgLCBlYWNoIHJvdyAob2JzZXJ2YXRpb24pIGlzIGEgcGVyc29uIC0tIGVhY2ggcGVyc29uJ3MgZmF2b3JpdGUgZm9vZCBzdGFydGluZyB3aXRoIGVhY2ggbGV0dGVyLiAgQnV0IHdlIHdhbnQgdG8gZ2V0IHRoaXMgZGF0YSBzZXQgdG8gbG9vayBsaWtlOgoKYGBgCiAgICBpZCBjYXRlZ29yeSBpdGVtICAgICAKICAgICAxIGEgICAgICAgIGFwcGxlICAgIAogICAgIDEgYiAgICAgICAgYmFuYW5hICAgCiAgICAgMSBjICAgICAgICBjb2QgICAgICAKICAgICAyIGEgICAgICAgIGFzcGFyYWd1cwogICAgIDIgYiAgICAgICAgYmFjb24gICAgCiAgICAgMiBjICAgICAgICBjaG9jb2xhdGUKYGBgCgpXaGVyZSBlYWNoIHJvdyAob2JzZXJ2YXRpb24pIGlzIGEgcGVyc29uLWZvb2QgY29tYmluYXRpb246IGVhY2ggZm9vZCBpcyBpbiBhIHJvdyBieSBpdHNlbGYsIGFuZCB0aGUgY29sdW1uIG5hbWVzIGhhdmUgYmVjb21lIGVuY29kZWQgaW4gYSB2YXJpYWJsZS4gIAoKVG8gZG8gdGhpcywgd2UgdXNlIHRoZSBmdW5jdGlvbiBgcGl2b3RfbG9uZ2VyKClgIC0gaW4gcHJldmlvdXMgdmVyc2lvbnMgb2YgdGlkeXIsIHRoaXMgZnVuY3Rpb24gd2FzIGNhbGxlZCBgZ2F0aGVyYC4gIFRoZSBzeW50YXggd2FzIHNpbWlsYXIsIGJ1dCB0aGUgYXJndW1lbnRzIGhhZCBkaWZmZXJlbnQgbmFtZXMuICBJbiBiYXNlIFIsIHlvdSBjYW4gZG8gc2ltaWxhciB0cmFuc2Zvcm1hdGlvbnMgd2l0aCB0aGUgYHJlc2hhcGVgIGZ1bmN0aW9uLgoKTGlrZSBvdGhlciB0aWR5dmVyc2UgZnVuY3Rpb25zLCB3ZSdsbCBzdGFydCB3aXRoIHRoZSBkYXRhIGZyYW1lIG5hbWUuICBUaGVuIHdlJ2xsIHNlbGVjdCB3aGljaCB3ZSdyZSBnb2luZyB0byBjb2xsYXBzZSAtLSBtZWFuaW5nIHRoZXJlIHdvbid0IGJlIGNvbHVtbnMgd2l0aCB0aG9zZSBuYW1lcyBhbnltb3JlIGJlY2F1c2Ugd2UnbGwgc3RhY2sgdGhlIG5hbWVzIGFuZCB2YWx1ZXMgb24gdG9wIG9mIGVhY2ggb3RoZXIgdG8gbWFrZSBtb3JlIHJvd3MuICBUaGVuIHdlIG5lZWQgdG8gdGVsbCBpdCB3aGF0IHRvIG5hbWUgdGhlIG5ldyB2YXJpYWJsZSBpdCB3aWxsIG1ha2UgdGhhdCB3aWxsIGhhdmUgdGhlIGNvbHVtbiBuYW1lcyBpbiBpdCAob3VyIG5ldyBjYXRlZ29yaWNhbCB2YXJpYWJsZSksIGFuZCBhIG5hbWUgYXMgd2VsbCBmb3IgdGhlIG5ldyBjb2x1bW4gdGhhdCB3aWxsIGhhdmUgdGhlIHZhbHVlcyBpbiBpdCAodGhlIHZhbHVlcyB0aGF0IHdlcmUgaW4gdGhlIGNvbHVtbnMgb3JpZ2luYWxseSkuCgpXZSBzcGVjaWZ5IHdoaWNoIGNvbHVtbnMgd2Ugd2FudCB1c2luZyBhbnkgb2YgdGhlIHNlbGVjdCBzeW50YXggb3Igc2VsZWN0IGhlbHBlcnMgd2UndmUgYmVlbiB0YWxraW5nIGFib3V0IGluIHByZXZpb3VzIHNlc3Npb25zLiAgCgpgYGB7cn0KcGl2b3RfbG9uZ2VyKGZvb2RzLCAKICAgICAgICAgICAgIGNvbHMgPSAtaWQsICMgY29sbGFwc2UgYWxsIGNvbHVtbnMgZXhjZXB0ICgtKSBpZAogICAgICAgICAgICAgbmFtZXNfdG8gPSAiY2F0ZWdvcnkiLCAgIyBuYW1lIGZvciB0aGUgbmV3IGNvbHVtbiB0aGF0IHdpbGwgaGF2ZSB0aGUgb2xkIGNvbHVtbiBuYW1lcyBhcyB2YWx1ZXMKICAgICAgICAgICAgIHZhbHVlc190byA9ICJpdGVtIikgICMgbmFtZSBmb3IgdGhlIG5ldyBjb2x1bW4gdGhhdCB3aWxsIGhhdmUgdGhlIGRhdGEgZnJvbSB0aGUgb2xkIGNvbHVtbnMKYGBgCgpUaGUgYGlkYCBjb2x1bW4gaGFzIHZhbHVlcyBkdXBsaWNhdGVkLCBvbmNlIGZvciBlYWNoIG9mIHRoZSBvcmlnaW5hbCAzIGNvbHVtbnMgdGhhdCB3ZSBjb2xsYXBzZWQsIGFuZCBlYWNoIHZhbHVlIG9mIG91ciBuZXcgYGNhdGVnb3J5YCBjb2x1bW4gYXBwZWFycyB0d2ljZSwgYmVjYXVzZSB0aGVyZSB3ZXJlIDIgcm93cyBpbiBgZm9vZGAgd2hlbiB3ZSBzdGFydGVkLgoKCiMjIyBFWEVSQ0lTRQoKUGl2b3QgdGhlIHBvcHMgZGF0YSBmcmFtZSBiZWxvdyB0byBiZSBpbiBhIGxvbmdlciBmb3JtYXQsIHdpdGggcm93cyBmb3IgZWFjaCBjb3VudHJ5LXllYXIgaW5zdGVhZCBvZiBlYWNoIGNvdW50cnkuCgpgYGB7cn0KcG9wcyA8LSB0cmliYmxlKH5jb3VudHJ5LCAgfnllYXIyMDExLCAgfnllYXIyMDEyLCAgfnllYXIyMDEzLAogICAgICAgICAgICAgICAgIkJyYXppbCIsICAxOTY5MzUxMzQsIDE5ODY1NjAxOSwgMjAwMzYxOTI1LAogICAgICAgICAgICAgICAgIkdlcm1hbnkiLCAgODI4OTI5MDQsICA4MjgwMDEyMSwgIDgyNzI2NjI2LAogICAgICAgICAgICAgICAgIktlbnlhIiwgICAgNDIwMjc4OTEsICA0MzE3ODE0MSwgIDQ0MzUzNjkxKQoKYGBgCgoKIyMgV0hPIGRhdGEKCk5vdywgdGhlIFdITyBUQiBkYXRhIGlzIGEgYml0IG1vcmUgY29tcGxpY2F0ZWQgdGhhbiB0aGUgc2ltcGxlIGV4YW1wbGUuICBXZSBrbm93IGhvdyB0byB0YWtlIHRoZSBkYXRhIHNldCBpbnRvIGNvbXBsZXRlbHkgbG9uZyBmb3JtYXQsIGJ5IGNvbGxhcHNpbmcgYWxsIG9mIHRoZSBjb2x1bW5zIGV4Y2VwdCB0aGUgZmlyc3QgNCB0aGF0IGhhdmUgY291bnRyeSBuYW1lIGluZm8gYW5kIHllYXI6CgpgYGB7cn0KbmFtZXMod2hvMikKYGBgCgpFYWNoIG9mIHRoZSBtZWFzdXJlbWVudCBjb2x1bW5zIGhhcyB0aGUgY291bnQgb2YgdGhlIG51bWJlciBvZiBwZW9wbGUgd2l0aCB0aGUgZ2l2ZW4gZGlhZ25vc2lzLCBnZW5kZXIsIGFuZCBhZ2UgZ3JvdXAuCgpgYGB7ciwgZXZhbD1GQUxTRX0KcGl2b3RfbG9uZ2VyKHdobzIsIAogICAgICAgICAgICAgLWNvdW50cnk6LXllYXIsICAjIGFsbCBleGNlcHQgZmlyc3QgNCBjb2x1bW5zICh0aGlzIGlzIHRoZSBjb2xzIGFyZ3VtZW50KQogICAgICAgICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZSIsICAjIG1ha2UgdXAgYSBuZXcgbmFtZQogICAgICAgICAgICAgdmFsdWVzX3RvID0gImNvdW50IikgICMgbWFrZSB1cCBhIG5ldyBuYW1lCmBgYAoKVGhlcmUgd2VyZSA1NiBjb2x1bW5zIHRoYXQgd2UgY29sbGFwc2VkIC0tIHRoYXQgd2VyZW4ndCB0aGUgSUQgdmFyaWFibGVzLiAgU28gdGhlIGZpcnN0IHJvdyBpbiB0aGUgb3JpZ2luYWwgZGF0YSBleHBhbmRlZCB0byBub3cgYmUgNTYgcm93cyBpbiB0aGUgbG9uZ2VyIGRhdGEgc2V0LgoKQnV0IHdoYXQgaWYgd2UgZG9uJ3Qgd2FudCB0byBjb2xsYXBzZSB0aGVtIGNvbXBsZXRlbHk/ICBXaGF0IGlmIHdlIHdhbnQgb25lIGNvbHVtbiBwZXIgYWdlIGdyb3VwPyAgb3IgcGVyIG1lYXN1cmUgaW5zdGVhZD8gIE9yIHdoYXQgaWYgYWxsIG9mIHRoZSBjb2x1bW5zIGFyZW4ndCBvZiB0aGUgc2FtZSB0eXBlPyAgSWYgc29tZSBhcmUgbnVtZXJpYyB3aGlsZSBvdGhlcnMgaGF2ZSBjaGFyYWN0ZXIgb3IgZmFjdG9yIGRhdGE/CgpJbiByZWFsbHkgY29tcGxpY2F0ZWQgY2FzZXMsIHlvdSBtYXkgbmVlZCB0byBzcGxpdCB5b3VyIGRhdGEgaW50byBtdWx0aXBsZSBkYXRhIHNldHMsIHJlc2hhcGUgdGhlIHBhcnRzLCBhbmQgdGhlbiBqb2luIHRoZSByZXN1bHRzIGJhY2sgdG9nZXRoZXIuCgpJbiBtb3N0IGNhc2VzIHRob3VnaCwgeW91IGNhbiBwaXZvdCB0byBhIGNvbXBsZXRlbHkgbG9uZyBmb3JtYXQsIGFuZCB0aGVuIHNwcmVhZCBjb2x1bW5zIGJhY2sgb3V0IHdpZGVyIGFmdGVyIHNvbWUgYWRkaXRpb25hbCB0cmFuc2Zvcm1hdGlvbnMuICBJZiB0aGVyZSBhcmUgZGlmZmVyZW50IGRhdGEgdHlwZXMsIHRoZXkgZ2V0IGNvbnZlcnRlZCB0byB0aGUgbW9yZSBnZW5lcmFsIG9uZSwgZm9sbG93aW5nOiBib29sZWFuIC0+IGludGVnZXIgLT4gbnVtZXJpYyAtPiBjaGFyYWN0ZXIuICBXaGlsZSB5b3UgbmVlZCB0byBiZSBjYXJlZnVsIGlmIHlvdSBhcmUgd29ya2luZyB3aXRoIHByZWNpc2UgbnVtZXJpY2FsIHZhbHVlcyAoZGVjaW1hbCB2YWx1ZXMpLCBpZiB5b3UncmUgd29ya2luZyB3aXRoIHdob2xlIG51bWJlcnMgb3IgdGhlIHByZWNpc2lvbiAobnVtYmVyIG9mIHNpZ25pZmljYW50IGRpZ2l0cykgaW4gdGhlIGRhdGEgaXMgc21hbGwsIGNvbnZlcnRpbmcgZnJvbSBudW1lcmljIHRvIGNoYXJhY3RlciBhbmQgYmFjayBhZ2FpbiB3aWxsIG5vdCBsb3NlIGluZm9ybWF0aW9uLiAgCgoKIyMgU2VwYXJhdGUgdmFyaWFibGUgbmFtZXMKCk9rLCBiYWNrIHRvIG91ciBXSE8gZGF0YS4gIE9uZSB0cmljayBiZWZvcmUgd2UgbGVhcm4gaG93IHRvIHBpdm90IHdpZGVyIHRvIHVuZG8gd2hhdCB3ZSBkaWQuICBXaGVuIHdlIHBpdm90IGxvbmdlciwgb3VyIG5ldyAibWVhc3VyZSIgdmFyaWFibGUgYWN0dWFsbHkgaGFzIG11bHRpcGxlIHBpZWNlcyBvZiBpbmZvcm1hdGlvbiBpbiBpdC4gIFRoZSB2YXJpYWJsZSBuYW1lcyBhcmUgb2YgdGhlIGZvcm1hdDogIm5ld18iIGRpYWdub3NpcyBfIG0vZiBhZ2UuICBJZGVhbGx5LCB3ZSdkIGxpa2UgdGhpcyBpbmZvcm1hdGlvbiBzZXBhcmF0ZWQsIHNvIHdlIGhhdmUgYSBjb2x1bW4gZm9yIHRoZSBkaWFnbm9zaXMgbWV0aG9kLCBhIGNvbHVtbiBmb3IgdGhlIGdlbmRlciwgYW5kIGEgY29sdW1uIGZvciB0aGUgYWdlIGdyb3VwLgoKV2UgY2FuIHRlbGwgcGl2b3RfbG9uZ2VyIHRvIGRvIHRoaXMgZm9yIHVzOgoKYGBge3IsIGV2YWw9RkFMU0V9CnBpdm90X2xvbmdlcih3aG8yLCAtY291bnRyeToteWVhciwKICAgICAgICAgICAgICMgd2UnbGwga2VlcCAzIGNvbHVtbnM6IG1ha2UgdXAgYSBuYW1lIGZvciBlYWNoCiAgICAgICAgICAgICBuYW1lc190bz1jKCJkaWFnbm9zaXMiLCAiZ2VuZGVyIiwgImFnZSIpLCAgCiAgICAgICAgICAgICAjIHRoaXMgaXMgYSByZWd1bGFyIGV4cHJlc3Npb24gdGhhdCBjYXB0dXJlcyB0aGUgcGF0dGVybgogICAgICAgICAgICAgIyBlYWNoICgpIHNldCBpcyBhIHZhbHVlIHRvIGtlZXAgYW5kIHR1cm4gaW50byBhIGNvbHVtbjsKICAgICAgICAgICAgICMgd2UgYXJlIGRyb3BwaW5nICJuZXciCiAgICAgICAgICAgICBuYW1lc19wYXR0ZXJuID0gIm5ld18/KC4rKV8oW21mXSkoXFxkKykiLAogICAgICAgICAgICAgdmFsdWVzX3RvPSJjb3VudCIpICAKYGBgCgpJZiB0aGUgdmFyaWFibGUgbmFtZXMgaGFkIGEgc2ltcGxlciBwYXR0ZXJuLCBmb3IgZXhhbXBsZTogeWVhcl9tZWFzdXJlLCB0aGVuIHdlIGNvdWxkIHVzZSB0aGUgIm5hbWVzX3NlcCIgYXJndW1lbnQgaW5zdGVhZCBvZiB0aGUgbW9yZSBjb21wbGljYXRlZCAibmFtZXNfcGF0dGVybiIgdG8gc3BsaXQgdXAgdGhlIGNvbHVtbi4KCgojIFBpdm90IFdpZGVyCgpOb3csIHdlJ3ZlIGdvdCBvdXIgY29tcGxldGVseSBsb25nIGRhdGEuICBIb3cgZG8gd2UgZ2V0IGl0IGludG8gYSBmb3JtIHdoZXJlIHRoZXJlJ3Mgb25lIHJvdyBwZXIgY291bnRyeS15ZWFyLWdlbmRlci1hZ2UsIHdpdGggb25lIGNvbHVtbiBwZXIgbWVhc3VyZT8KCmBgYHtyfQojIHNhbWUgYXMgYWJvdmUsIGp1c3Qgc2F2ZSBpdApsb25nX2RhdGEgPC0gcGl2b3RfbG9uZ2VyKHdobzIsIC1jb3VudHJ5Oi15ZWFyLAogICAgICAgICAgICAgbmFtZXNfdG89YygiZGlhZ25vc2lzIiwgImdlbmRlciIsICJhZ2UiKSwgCiAgICAgICAgICAgICB2YWx1ZXNfdG89ImNvdW50IiwKICAgICAgICAgICAgIG5hbWVzX3BhdHRlcm4gPSAibmV3Xz8oLispXyhbbWZdKShcXGQrKSIpIApgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojIHNwcmVhZCBpdCBiYWNrIG91dApwaXZvdF93aWRlcihsb25nX2RhdGEsIAogICAgICAgICAgICBuYW1lc19mcm9tPWRpYWdub3NpcywKICAgICAgICAgICAgdmFsdWVzX2Zyb209Y291bnQpCmBgYAoKCldoYXQgaGFwcGVucyBpZiBvdXIgbG9uZyBkYXRhIGRvZXNuJ3QgaGF2ZSBldmVyeSBjb21iaW5hdGlvbiBvZiB2YWx1ZXMgdG8gZmlsbCBpbnRvIG91ciBuZXcgZGF0YSBzZXQ/ICBGb3IgZXhhbXBsZSwgaWYgdGhlIGRhdGEgc3RhcnRlZCBsb25nIGluc3RlYWQgb2Ygd2lkZT8KCkZvciBleGFtcGxlLCBpZiB3ZSBoYWQgZGF0YSB0aGF0IGxvb2tlZCBsaWtlOgoKYGBge3IsIGV2YWw9RkFMU0V9CmxvbmdfZGF0YSAlPiUKICBmaWx0ZXIoIWlzLm5hKGNvdW50KSkgJT4lCiAgc2FtcGxlX24oMjApICU+JQogIGFycmFuZ2UoY291bnRyeSwgeWVhciwgZGlhZ25vc2lzLCBhZ2UsIGdlbmRlcikKYGBgCgpJdCdzIG9rIC0gd2UgY2FuIHN0aWxsIHBpdm90IHdpZGVyLiAgQnkgZGVmYXVsdCwgaXQgd2lsbCBmaWxsIGluIGBOQWAgd2hlcmUgaXQgZG9lc24ndCBoYXZlIGEgdmFsdWUsIG9yIHdlIGNhbiB0ZWxsIGl0IHRvIGZpbGwgd2l0aCBhbm90aGVyIHZhbHVlIChzdWNoIGFzIDApLiAgCgpgYGB7ciwgZXZhbD1GQUxTRX0KbG9uZ19kYXRhICU+JQogIGZpbHRlcighaXMubmEoY291bnQpKSAlPiUKICBzYW1wbGVfbigyMCkgJT4lCiAgYXJyYW5nZShjb3VudHJ5LCB5ZWFyLCBkaWFnbm9zaXMsIGFnZSwgZ2VuZGVyKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tPWRpYWdub3NpcywgCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb209Y291bnQpCmBgYAoKV2UgY291bGQgYWxzbyBwaXZvdCB3aWRlciBtb3JlIHRoYW4gb25lIGNvbHVtbiBhdCBvbmNlLiAgTGV0J3MgaGF2ZSBvbmUgcm93IHBlciBjb3VudHJ5LXllYXItZGlhZ25vc2lzLCBhbmQgb25lIGNvbHVtbiBmb3IgZWFjaCBhZ2UtZ2VuZGVyIGNvbWJpbmF0aW9uOgoKYGBge3J9CmxvbmdfZGF0YSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tPWMoYWdlLCBnZW5kZXIpLAogICAgICAgICAgICAgICMgY29udHJvbHMgaG93IG5ldyB2YXJpYWJsZSBuYW1lcyBhcmUgY3JlYXRlZAogICAgICAgICAgICAgICMgdXNlcyBzeW50YXggZnJvbSB0aGUgZ2x1ZSBwYWNrYWdlCiAgICAgICAgICAgICAgbmFtZXNfZ2x1ZT0ie2dlbmRlcn1fe2FnZX0iLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbT1jb3VudCkKYGBgCgoKIyMjIEVYRVJDSVNFCgpQaXZvdCB0aGUgYGxvbmdfZGF0YWAgZGF0YSBmcmFtZSB3aWRlciBzbyB0aGF0IGVhY2ggcm93IGlzIGEgY291bnRyeS1kaWFnbm9zaXMtZ2VuZGVyLWFnZSBvYnNlcnZhdGlvbiAocGl2b3QgInllYXIiIHNvIHlvdSBoYXZlIG9uZSBjb2x1bW4gZm9yIGVhY2ggeWVhciBpbiB0aGUgb3V0cHV0KS4KCmBgYHtyfQoKYGBgCgoKIyBTZXBhcmF0ZQoKTXVjaCBsaWtlIHdlIGhhZCBgcGl2b3RfbG9uZ2VyKClgIHNwbGl0IHVwIHRoZSBjb21wb25lbnRzIG9mIHRoZSB2YXJpYWJsZSBuYW1lcyBmb3IgdXMgaW50byBtZWFzdXJlLCBhZ2UsIGFuZCBnZW5kZXIsIHdlIGNhbiB1c2UgdGhlIGBzZXBhcmF0ZSgpYCBmdW5jdGlvbiB0byBkbyB0aGF0IHRvIGNvbHVtbnMgb3V0c2lkZSBvZiBwaXZvdGluZy4KCmBgYHtyfQpzdHVkZW50cyA8LSB0cmliYmxlKH5uYW1lLCB+ZGVwYXJ0bWVudCwKICAgICAgICAgICAgICAgICAgICAiQ2hyaXN0aW5hIE1haW1vbmUiLCAiUG9saXRpY2FsIFNjaWVuY2UiLAogICAgICAgICAgICAgICAgICAgICJDb2xieSBXaXRoZXJ1cC1Xb29kIiwgIlBsYW50IEJpb2xvZ3kiLAogICAgICAgICAgICAgICAgICAgICJKYWNraWUgTWlsaGFucyIsICJNYXRlcmlhbHMgU2NpZW5jZSIsCiAgICAgICAgICAgICAgICAgICAgIkFuZHJlIEFyY2hlciIsICJBcHBsaWVkIE1hdGgiLAogICAgICAgICAgICAgICAgICAgICJEYW4gVHVybmVyIiwgIkxpbmd1aXN0aWNzIikKc3R1ZGVudHMKYGBgCgpXZSBjb3VsZCBzcGxpdCB0aGUgbmFtZSBjb2x1bW4gaW50byBmaXJzdCBuYW1lIGFuZCBsYXN0IG5hbWUsIHVzaW5nIGEgc3BhY2UgY2hhcmFjdGVyIGFzIGEgZGVsaW1pdGVyLiAgCgpgYGB7cn0Kc2VwYXJhdGUoc3R1ZGVudHMsICAjIGRhdGEgZnJhbWUgbmFtZQogICAgICAgICBuYW1lLCAgIyBleGlzdGluZyBjb2x1bW4gbmFtZSB0byBzcGxpdAogICAgICAgICBpbnRvPWMoImZpcnN0IiwgImxhc3QiKSwgICMgbmV3IGNvbHVtbiBuYW1lcyAKICAgICAgICAgc2VwPSIgIikgICMgd2hhdCB0byBzcGxpdCBvbgpgYGAKCllvdSBuZWVkIHRvIGtub3cgdGhlIG1heGltdW0gbnVtYmVyIG9mIHBpZWNlcyB0aGF0IHlvdXIgZGF0YSB3aWxsIGJlIHNwbGl0IGludG8sIG9yIHlvdSdsbCBsb3NlIHNvbWUgb2YgdGhlIGluZm9ybWF0aW9uLiAgSWYgQ29sYnkncyBuYW1lIGhhZCBhIHNwYWNlIGluc3RlYWQgb2YgYSBoeXBoZW46CgpgYGB7cn0Kc3R1ZGVudHMgPC0gdHJpYmJsZSh+bmFtZSwgfmRlcGFydG1lbnQsCiAgICAgICAgICAgICAgICAgICAgIkNocmlzdGluYSBNYWltb25lIiwgIlBvbGl0aWNhbCBTY2llbmNlIiwKICAgICAgICAgICAgICAgICAgICAiQ29sYnkgV2l0aGVydXAgV29vZCIsICJQbGFudCBCaW9sb2d5IiwKICAgICAgICAgICAgICAgICAgICAiSmFja2llIE1pbGhhbnMiLCAiTWF0ZXJpYWxzIFNjaWVuY2UiLAogICAgICAgICAgICAgICAgICAgICJBbmRyZSBBcmNoZXIiLCAiQXBwbGllZCBNYXRoIiwKICAgICAgICAgICAgICAgICAgICAiRGFuIFR1cm5lciIsICJMaW5ndWlzdGljcyIpCnNlcGFyYXRlKHN0dWRlbnRzLAogICAgICAgICBuYW1lLAogICAgICAgICBpbnRvPWMoImZpcnN0IiwgImxhc3QiKSwKICAgICAgICAgc2VwPSIgIikKYGBgCgpXZSBnZXQgYSB3YXJuaW5nLCBhbmQgIldvb2QiIHdhcyBqdXN0IGRyb3BwZWQgY29tcGxldGVseSEgIChZb3UgY2FuIGNvbnRyb2wgdGhpcyBiZWhhdmlvciB0byBzb21lIGV4dGVudCB3aXRoIHRoZSBgZXh0cmFgIGFyZ3VtZW50LikKCkl0J3MgYmV0dGVyIHRvIHNwZWNpZnkgdGhlIG1heGltdW0gbnVtYmVyIG9mIGNvbHVtbnMgcG9zc2libGUgaW5zdGVhZDoKCmBgYHtyfQpzZXBhcmF0ZShzdHVkZW50cywKICAgICAgICAgbmFtZSwKICAgICAgICAgaW50bz1jKCJmaXJzdCIsICJsYXN0MSIsICJsYXN0MiIpLAogICAgICAgICBzZXA9IiAiKQpgYGAKCk5vdyB0aGUgd2FybmluZyBpcyB0ZWxsaW5nIHVzIGl0IGZpbGxlZCBpbiBOQSB2YWx1ZXMgd2hlcmUgaXQgZGlkbid0IGZpbmQgMyBwaWVjZXMgYWZ0ZXIgc2VwYXJhdGluZy4KCkJ1dCBgc2VwYXJhdGVgIHJlYWxseSB3b3JrcyBiZXN0IHdoZW4geW91J3JlIHNlcGFyYXRpbmcgc29tZXRoaW5nIHRoYXQgd2lsbCBzZXBhcmF0ZSBpbnRvIGEgZml4ZWQgbnVtYmVyIG9mIGNvbHVtbnMuICAKClNpZGUgbm90ZTogd29ya2luZyB3aXRoIG5hbWVzIGlzIEhBUkQhICBGb3Igc29tZSBleGFtcGxlcyB3aHksIHNlZSBodHRwczovL3d3dy5rYWx6dW1ldXMuY29tLzIwMTAvMDYvMTcvZmFsc2Vob29kcy1wcm9ncmFtbWVycy1iZWxpZXZlLWFib3V0LW5hbWVzLwoKIyMgRVhFUkNJU0UKClNlcGFyYXRlIHRoZSAibG9jYXRpb24iIHZhcmlhYmxlIGluIGBhZGRyZXNzZXNgIGludG8gc2VwYXJhdGUgImNpdHkiIGFuZCAic3RhdGUiIGNvbHVtbnMuICBIaW50OiB0aGUgInNlcCIgdmFsdWUgY2FuIGJlIG1vcmUgdGhhbiBvbmUgY2hhcmFjdGVyIGluIGxlbmd0aC4gIAoKYGBge3J9CmFkZHJlc3NlcyA8LSB0cmliYmxlKH5uYW1lLCB+bG9jYXRpb24sCiAgICAgICAgICAgICAgICAgICAgICJDaHJpc3RpbmEiLCAiRXZhbnN0b24sIElMIiwKICAgICAgICAgICAgICAgICAgICAgIkJvYiIsICJCZW5kLCBPUiIsCiAgICAgICAgICAgICAgICAgICAgICJMb3VrYSIsICJIb25vbHVsdSwgSEkiLAogICAgICAgICAgICAgICAgICAgICAiQWxleCIsICJTYW4gSm9zZSwgQ0EiKQpgYGAKCgoKIyBzZXBhcmF0ZV9yb3dzKCkKCkluIGNhc2VzIHdoZXJlIHlvdSB3YW50IHRvIHNlcGFyYXRlIG91dCBhIGNvbHVtbiwgYnV0IHlvdSBkb24ndCBrbm93IGhvdyBtYW55IHZhbHVlcyBtaWdodCBiZSBpbiBpdCwgYW5vdGhlciBvcHRpb24gaXMgdG8gbWFrZSBvbmUgcm93LCBpbnN0ZWFkIG9mIG9uZSBjb2x1bW4sIHBlciB2YWx1ZS4gIEl0J3MgY29tYmluaW5nIGEgc2VwYXJhdGUgd2l0aCBhIHBpdm90IGxvbmdlci4KClRoaXMgY2FuIGJlIHVzZWZ1bCBpZiB5b3UgaGF2ZSBhIGNvbHVtbiB3aXRoIGEgZGVsaW1pdGVkIGxpc3Qgb2YgdmFsdWVzIGluIGl0LiAgRm9yIGV4YW1wbGUsIGlmIHlvdSBhc2sgYSBtdWx0aXBsZSBjaG9pY2UgcXVlc3Rpb24gdGhhdCBhbGxvd3MgbXVsdGlwbGUgYW5zd2VycyBpbiBRdWFsdHJpY3MgKHN1cnZleSBwbGF0Zm9ybSksIHlvdSdsbCBnZXQgYSBzaW5nbGUgY29sdW1uIHdpdGggYWxsIG9mIHRoZSBhbnN3ZXIgdGhhdCBwZW9wbGUgY2hlY2tlZDoKCmBgYHtyfQpzdXJ2ZXkgPC0gdHJpYmJsZSh+aWQsIH5xMSwgfnEyLAogICAgICAgICAgICAgICAgICA0NSwgNCwgIlRpbWUiLAogICAgICAgICAgICAgICAgICA0NiwgMywgIlF1YWxpdHk7Q29zdCIsCiAgICAgICAgICAgICAgICAgIDQ3LCA0LCAiVGltZTtDb3N0O0NvbXBhdGliaWxpdHk7V2FycmFudHkiKQpzdXJ2ZXkKYGBgCgpIb3cgZG8gd2UgZGVhbCB3aXRoIHEyPwoKYGBge3J9CnNlcGFyYXRlX3Jvd3Moc3VydmV5LCAKICAgICAgICAgICAgICBxMiwgICMgd2hpY2ggY29sdW1uIHRvIHNlcGFyYXRlCiAgICAgICAgICAgICAgc2VwPSI7IikKYGBgCgojIyBDcmVhdGluZyBJbmRpY2F0b3IgVmFyaWFibGVzCgpUaGVuLCB3ZSBtYXkgd2FudCB0byBjcmVhdGUgb25lIGNvbHVtbiBwZXIgYW5zd2VyIGNob2ljZSAtLSBjcmVhdGUgaW5kaWNhdG9yIHZhcmlhYmxlcyAoYWthIGR1bW15IHZhcmlhYmxlcyBvciBvbmUtaG90IGVuY29kaW5nKS4gIAoKYGBge3J9CnNlcGFyYXRlX3Jvd3Moc3VydmV5LCAKICAgICAgICAgICAgICBxMiwgICMgd2hpY2ggY29sdW1uIHRvIHNlcGFyYXRlCiAgICAgICAgICAgICAgc2VwPSI7IikgJT4lCiAgbXV0YXRlKGV4aXN0cz0xKSAlPiUgICMgYWRkIGEgY29sdW1uIHRvIGVuY29kZSB2YWx1ZXMgbGF0ZXIKICBwaXZvdF93aWRlcihuYW1lc19mcm9tPXEyLCAgIyBuZXcgY29sdW1uIG5hbWVzLCBvbmUgZm9yIGVhY2ggdmFsdWUKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbT1leGlzdHMsICAjIHNvIHRoZXJlIHdpbGwgYmUgYSAxIHdoZXJlIHRoZXJlIHdhcyBhIHZhbHVlCiAgICAgICAgICAgICAgdmFsdWVzX2ZpbGw9MCkgIyMgZmlsbHMgaW4gbWlzc2luZyB2YWxzIHdpdGggMCBpbnN0ZWFkIG9mIE5BCmBgYAoKIyMgTGVhcm5pbmcgTW9yZQoKVGhlIHRpZHlyIGRvY3VtZW50YXRpb24gaGFzIGEgdHV0b3JpYWwgb24gcGl2b3Rpbmc6IGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9waXZvdC5odG1sIAoKCg==