Piping
This is the new functor we’ll be using today:
We’ll pronounce that “pipe.”
So far, every time we’ve used a function, like nrow()
, we’ve passed it its arguments by putting them inside the parentheses:
nrow(iy_ah)
[1] 44818
What %>%
does is take everything to its left, and injects it into the first argument of the function to its right.
iy_ah %>% nrow()
[1] 44818
We’ll pronounce this “piping iy_ah
into nrow()
.”
This might not seem so earth shattering right away, but it really shines when you want to apply a function to the output of the previous function. Let’s say you wanted to check how many rows of “iy” data there is. Using normal functional application, you’d have to do this:
nrow(filter(iy_ah, plt_vclass == "iy"))
[1] 23699
Here, the first function you apply is the most deeply embedded, and the last function you apply is the outermost. Some syntacticians feel very comfortable with bracket notation like this, but it rapidly gets unreadable, and mis-matching closing parentheses is a very common kind of error. Here’s how these same exact operations look like with the pipes.
iy_ah %>%
filter(plt_vclass == "iy") %>%
nrow()
[1] 23699
This is much more readable, and the functions are visually ordered in the same order they’re applied.
The benefit of piping becomes most apparent if we revisit our data cleaning pipeline from our last meeting. Here’s iy_ah
wide again.
iy_ah_wide
Last time, we made this data wide to long by creating intermediate data frames. However, we could do it all in a single shot by nesting functions like so:
spread(
separate(
gather(iy_ah_wide,
key = "vowel_formant",
value = "hz",
ah_F1:iy_F2),
"vowel_formant",
into = c("vowel", "formant"),
sep = "_"),
formant,
hz)
But good luck writing that twice, or sharing your code with anyone. Notice how the additional arguments for spread()
are maximally far away from the actual call to the function at the top?
Piping makes it much better.
iy_ah_wide %>%
gather(key = "vowel_formant", value = "hz", ah_F1:iy_F2) %>%
separate("vowel_formant", into = c("vowel", "formant"), sep = "_") %>%
spread(formant, hz)
Another nice thing about these chains is that they can be built up incrementally. That is, just the first two lines will work:
iy_ah_wide %>%
gather(key = "vowel_formant", value = "hz", ah_F1:iy_F2)
And so will just the fist three:
iy_ah_wide %>%
gather(key = "vowel_formant", value = "hz", ah_F1:iy_F2) %>%
separate("vowel_formant", into = c("vowel", "formant"), sep = "_")
The same is not true of the embedded functional application, because it depends on matching parentheses between the beginning and end.
Idiom
I am going to recommend that you put a new line after every pipe, %>%
, and indent the following line with two spaces when chaining together functions.
~5 minute activity
Re-create the fruit
data frame:
fruit <- data.frame(person = c("Oakley", "Charlie"),
apples_bought = c(5, 3),
apples_ate = c(1, 2),
oranges_bought = c(5, 4),
oranges_ate = c(3, 3))
Using pipes, recreate the gather
, separate
and spread
workflow from the last meeting to produce this table (you can double check the code from last time).
Charlie |
apples |
2 |
3 |
Charlie |
oranges |
3 |
4 |
Oakley |
apples |
1 |
5 |
Oakley |
oranges |
3 |
5 |
Split-Apply-Comine
When doing data analysis, you’re going to find yourself doing these following steps a lot:
- Splitting the data up into subsets.
- Applying some kind of function to those subsets.
- Combining the results back together
Here is some fake data from an experiment of speakers’ fundamental frequency. Go ahead and run this code:
pitch <- data_frame(speaker = rep(c("Oakley", "Charlie", "Azaria"),
times = c(3,3,2)),
F0 = c(124, 109, 125, 103, 121, 123, 181, 206))
Oakley |
124 |
Oakley |
109 |
Oakley |
125 |
Charlie |
103 |
Charlie |
121 |
Charlie |
123 |
Azaria |
181 |
Azaria |
206 |
One thing we might want to know is what the average F0 is for each speaker. Here’s how we do that, conceptually, with the Split-Apply-Combine workflow.
Split the data up
First, split the data up into subsets based on the “speaker” column:
Charlie |
103 |
Charlie |
121 |
Charlie |
123 |
Oakley |
124 |
Oakley |
109 |
Oakley |
125 |
Apply some function to the data
In each subset, calculate the average F.
Combine the result
Combine these results into a new table.
Azaria |
193.5000 |
Charlie |
115.6667 |
Oakley |
119.3333 |
Split-Apply-Combine in dplyr
The dplyr
package was written specifically to streamline the Split-Apply-Combine workflow. All dplyr
“verbs” take a data frame as input, does something to them, and returns another data frame. Let’s start with the summarise()
function.
summarise(data,
new_col1 = summary_function1,
new_col2 = summary_function2)
data
- The data frame you want to summarise
- summary functions
- This isn’t a named argument. Here, you give
summarise()
a function that can take many values as input, and returns a single value as output.
For example, let’s calculate the mean F0 across all of the data in the pitch
data frame.
pitch %>%
summarise(mean_F0 = mean(F0))
This doesn’t look like much, because it’s just a one row data frame with one column called mean_F0
with one value, which is the mean of all of the F0 measurements from each speaker.
It’s also possible to do multiple summaries at once by just adding more summary functions to the summarise()
verb.
pitch %>%
summarise(mean_F0 = mean(F0),
median_F0 = median(F0),
sd_F0 = sd(F0),
mad_F0 = mad(F0),
range_F0 = max(F0)-min(F0),
ndata = length(speaker))
The summarise()
function is what we want to apply to our data, but for the Split-Apply-Combine workflow, we want to split the data according to the speaker, apply the summarise()
function to each subset, then combine the results back together.
To split up the data, we first pass the pitch
data frame to group_by()
.
group_by(data, groupA, groupB)
data
- The data frame you want to apply Split-Apply-Combine to.
- groupings
- These are the names of columns in the data frame that you want to split the data up by.
group_by()
will create subsets of every unique combination of values in these columns.
When you apply group_by()
to a data frame by itself, nothing really special happens:
pitch %>%
group_by(speaker)
But when you apply summarise()
after group_by()
, you get the mean F0 for each speaker:
pitch %>%
group_by(speaker) %>%
summarise(mean_F0 = mean(F0))
Here’s what just happened.
group_by()
to split the data frame up according to the unique values of speaker
.
summarise()
calculated the mean F0 within each subset.
- They were then automatically combined back together.
~5 minute activity
Just to refresh your memories, the iy_ah
data frame contains all data from the vowels /i:/ and /ɑ/ from the PNC. The column idstring
is a unique ID for each speaker, and plt_vclass
codes the vowel class.
Calculate the average F1 and F2 for /i:/ and /ɑ/ for each speaker.
Additional dplyr
verbs
Here are a few more dplyr
verbs that you can string together.
filter() |
We’ve already used this to subset dataframes. |
summarise() |
This takes a data frame, and outputs a new data frame based on the summary you asked for |
mutate() |
This takes a data frame, and adds additional columns based on the formula you give it |
select() |
This takes a data frame, and returns only the columns you ask for |
arrange() |
Reorders the rows of the data frame |
The one function here that requires some additional instruction is mutate()
.
mutate(data, new_col = value)
data
- the data that you want to mutate
- new columns
- Here, you name new columns, and provide some kind of value, usually using some other function.
The simplest and least useful use of mutate()
adds a new column with a single value:
pitch %>%
mutate(jawn = "foo")
However, a much more useful use of mutate()
creates transformed columns. For example, sometimes when working with pitch, you might estimate pitch differences in terms of semitones. Every doubling of pitch is 12 semitones. It’s the case that if you calculate the difference between the log2 transform of any two numbers that are doubles of eachother, you get 1.
# 1 octave differences
log2(6) - log2(3)
[1] 1
log2(18) - log2(9)
[1] 1
# 2 octave differences
log2(12) - log2(3)
[1] 2
If you wanted to estimate, for each speaker, their pitch range in terms of semitones, step 1 would be to convert their F0 in Hz into log2(Hz).
pitch %>%
mutate(log2_F0 = log2(F0))
Then, for every speaker, subtract their max log2_F0
from their min log2_F0
.
pitch %>%
mutate(log2_F0 = log2(F0)) %>%
group_by(speaker)%>%
summarise(octave_range = max(log2_F0) - min(log2_F0),
semitone_range = octave_range * 12)
~5 minute activity
Try the following:
pitch %>%
mutate(number = n())
What happened? What do you think n()
does?
Now try this:
pitch %>%
group_by(speaker)%>%
mutate(number = n())
What’s different this time?
Split-Apply-Combine for other data types
Data frames are not the only kind of data that you might want to use the Split-Apply-Combine workflow on. For example, the joe_vowel_files
directory (that you unzipped earlier) contains one .csv for each vowel class from when I was an interviewer once. This is an unconventional data organization scheme, but not unheard of. For example, you might have a separate file of vowel data for each speaker.
We can use a variant of the Split-Apply-Combine workflow to load and combine all of these files at once.
~2 minute activity
This part may require some assistance. The Sys.glob()
function searchers for all files that match the pattern you specify. The command below says “List all files that end in csv
that are in the ../data/joe_vowel_files/
directory”. The preceeding ..
in the path means that it should start the search from one directory above where the R notebook is saved. If you have set up your course directory as recommended, this should work. If you don’t get output that looks like this, I’ll come around and try to figure out why.
Sys.glob("../data/joe_vowel_files/*csv")
[1] "../data/joe_vowel_files/*hr.csv"
[2] "../data/joe_vowel_files/Tuw.csv"
[3] "../data/joe_vowel_files/ae.csv"
[4] "../data/joe_vowel_files/ahr.csv"
[5] "../data/joe_vowel_files/aw.csv"
[6] "../data/joe_vowel_files/ay.csv"
[7] "../data/joe_vowel_files/ay0.csv"
[8] "../data/joe_vowel_files/e.csv"
[9] "../data/joe_vowel_files/ey.csv"
[10] "../data/joe_vowel_files/eyF.csv"
[11] "../data/joe_vowel_files/i.csv"
[12] "../data/joe_vowel_files/iy.csv"
[13] "../data/joe_vowel_files/iyF.csv"
[14] "../data/joe_vowel_files/iyr.csv"
[15] "../data/joe_vowel_files/o.csv"
[16] "../data/joe_vowel_files/oh.csv"
[17] "../data/joe_vowel_files/ow.csv"
[18] "../data/joe_vowel_files/owF.csv"
[19] "../data/joe_vowel_files/owr.csv"
[20] "../data/joe_vowel_files/uh.csv"
[21] "../data/joe_vowel_files/uw.csv"
Assign this vector of file names to the variable vowel_files
.
vowel_files <- Sys.glob("../data/joe_vowel_files/*csv")
If we apply read.csv()
to the first vowel file name, it will read it in as a data frame.
read.csv(vowel_files[1])
But there are 21 vowel files, and what we don’t want to do is write out something like vowel_2 <- read_csv(vowel_files[2])
21 times! Even copy-pasting will involve errors.
To Split-Apply-Combine read_csv()
to the vector of file names vowel_files
, we’ll use map()
.
list
- the list of things that you want to split up
function
- the function that you want to apply to every item in the list
...
- any extra arguments you need to pass to the function.
It’s easiest to see this at work:
all_vowels <- map(vowel_files, read.csv)
all_vowels[1]
[[1]]
NA
all_vowels[21]
[[1]]
NA
Here’s what has happened:
map()
applied read.csv()
to each file name.
read.csv()
read each file in as a dataframe.
map()
returned a list of dataframes that read.csv()
produced.
There’s one last step we need combine all of these dataframes together. bind_rows()
will combine all of these dataframes into one big dataframe.
vowels_df <- bind_rows(all_vowels)
head(vowels_df)
Of course, this can be chained together with pipes:
vowel_files %>%
map(read.csv)%>%
bind_rows()
Unequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorUnequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector
Recursive Data Frames
There is one last use of map()
that is really useful, but might be a bit mind-bending for right now, but will hopefully start to make more sense as we work through the course, or when you re-read these notes. I wouldn’t necessarilly file this under “required core R competency”, but it is really very useful.
Let’s say we were interested in doing a correlation test between F1 and F2 for all of my vowels (this isn’t an especially interesting thing to do, it’s just for illustration). We could do that like so:
cor.test(vowels_df$F1, vowels_df$F2)
Pearson's product-moment correlation
data: vowels_df$F1 and vowels_df$F2
t = -1.9254, df = 327, p-value = 0.05505
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
-0.211579986 0.002279927
sample estimates:
cor
-0.1058742
What if we wanted to do a correlation test, not on all of my vowels together, but within each vowel? Step 1 would be to generalize the line that ran cor.test()
above into its own function.
#' @name vowel_cor_test
#' @description conducts a correlation test on the data frame (df) using the columns F1 and F2
vowel_cor_test <- function(df){
cor.test(df$F1, df$F2)
}
vowel_cor_test(vowels_df)
Pearson's product-moment correlation
data: df$F1 and df$F2
t = -1.9254, df = 327, p-value = 0.05505
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
-0.211579986 0.002279927
sample estimates:
cor
-0.1058742
The vowel_cor_test()
function we just created is what we want to apply. Next we need to figure out how to split up the data, which we’rem going to do by creating a recursive dataframe:
vowels_nested <- vowels_df %>%
group_by(plt_vclass)%>%
filter(n() > 5)%>%
nest()
vowels_nested
This is the mind bending part. In vowels_nested
, the first column is the vowel class, for which there is one row each. In the new data
column, the value in each cell is actually a whole data frame.
vowels_nested$data[1]
[[1]]
NA
This means we can now use map()
to apply the vowel_cor_test()
function to each of these data frames.
vowels_test <- vowels_nested %>%
mutate(cor = map(data, vowel_cor_test))
vowels_test
Each item in the cor
column is now a correlation test result.
vowels_test$cor[1]
[[1]]
Pearson's product-moment correlation
data: df$F1 and df$F2
t = -0.39131, df = 23, p-value = 0.6992
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
-0.4616203 0.3242266
sample estimates:
cor
-0.08132266
We’ve successfully fit a correlation test for every vowel, but the results are kind of trapped away in these strange print outs. We can liberate them with the function tidy()
from the broom
package. tidy()
will convert many statistical tests like this into a dataframe representation.
tidy(vowels_test$cor[[1]])
We actually need to map the tidy()
function to the cor
column for this to work:
vowels_results <- vowels_test %>%
mutate(cor_df = map(cor, tidy))%>%
unnest(cor_df)
vowels_results %>%
arrange(-abs(estimate))
And, as usual, this whole procedure could be chained together into one big analysis.
vowels_df %>%
group_by(plt_vclass)%>%
filter(n() > 5)%>%
nest() %>%
mutate(cor = map(data, vowel_cor_test),
cor_df = map(cor, tidy))%>%
unnest(cor_df)%>%
arrange(-abs(estimate))
LS0tCnRpdGxlOiAiU3BsaXQtQXBwbHktQ29tYmluZSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogbm9uZQogICAgY3NzOiAuLi9jdXN0b20uY3NzCiAgICB0aGVtZTogZmxhdGx5CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogMwotLS0KCgojIE91dGxpbmUKClRvZGF5LCB3ZSdyZSBnb2luZyB0byBiZSBjb3ZlcmluZyB0d28gcmVhbGx5IGltcG9ydGFudCBjb25jZXB0cwoKMS4gUGlwaW5nCjIuIFNwbGl0LUFwcGx5LUNvbWJpbmUKClBpcGluZyB3YXMgaW50cm9kdWNlZCB0byBSIHdpdGggdGhlIGBtYWdyaXR0cmAgcGFja2FnZSAoZ2V0IGl0PyApLCBhbmQgb3ZlciB0aGUgcGFzdCA0IHllYXJzIGhhcyBkcmFtYXRpY2FsbHkgYWx0ZXJlZCBob3cgcHJvZ3JhbW1pbmcgaW4gUiB3b3Jrcy4gClNwbGl0LUFwcGx5LUNvbWJpbmUgaXMgYSBkYXRhIGFuYWx5c2lzIHRlY2huaXF1ZSB0aGF0IHlvdSBtYXkgaGF2ZSBhbHJlYWR5IHVzZWQsIGJ1dCBpcyBzdHJlYW1saW5lZCBpbiB0aGUgYGRwbHlyYCBhbmQgYHB1cnJyYCBwYWNrYWdlcy4KCgoKIyMgU2V0dXAKPGRpdiBjbGFzcyA9ICJicmVhayBib3giPgo8c3BhbiBjbGFzcyA9ICdiaWctbGFiZWwnPn4yIG1pbnV0ZSBzZXR1cDwvc3Bhbj4KCkNyZWF0ZSBhbmQgc2F2ZSB5b3VyIFIgbm90ZWJvb2sgZm9yIHRvZGF5LiBDbGVhciB0aGUgd29ya3NwYWNlIGluIGNhc2UgdGhlcmUncyBhbnl0aGluZyBsZWZ0IG92ZXIgZnJvbSBsYXN0IHRpbWUuIExvYWQgdGhlIGltcG9ydGFudCBwYWNrYWdlcyBmb3IgdG9kYXkncyB3b3JrOgoKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoImJyb29tIikKCmxpYnJhcnkobHNhMjAxNykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoYnJvb20pCmBgYAoKQWxzbywgZG93bmxvYWQgYW5kIHVuemlwIHRoZSBmaWxlIGNhbGxlZCBgam9lX3Zvd2VsX2ZpbGVzLnppcGAgZnJvbSBDYW52YXMuCgo8L2Rpdj4KCjxoci8+CgojIFBpcGluZwoKVGhpcyBpcyB0aGUgbmV3IGZ1bmN0b3Igd2UnbGwgYmUgdXNpbmcgdG9kYXk6Cgo8ZGl2IGNsYXNzID0gImlsbHVzdHJhdGUiPgo8c3BhbiBjbGFzcyA9ICJwb3AiPiU+JTwvc3Bhbj4KPC9kaXY+CgpXZSdsbCBwcm9ub3VuY2UgdGhhdCAicGlwZS4iCgpTbyBmYXIsIGV2ZXJ5IHRpbWUgd2UndmUgdXNlZCBhIGZ1bmN0aW9uLCBsaWtlIGBucm93KClgLCB3ZSd2ZSBwYXNzZWQgaXQgaXRzIGFyZ3VtZW50cyBieSBwdXR0aW5nIHRoZW0gaW5zaWRlIHRoZSBwYXJlbnRoZXNlczoKCmBgYHtyfQpucm93KGl5X2FoKQpgYGAKCldoYXQgYCU+JWAgZG9lcyBpcyB0YWtlIGV2ZXJ5dGhpbmcgdG8gaXRzIGxlZnQsIGFuZCBpbmplY3RzIGl0IGludG8gdGhlIGZpcnN0IGFyZ3VtZW50IG9mIHRoZSBmdW5jdGlvbiB0byBpdHMgcmlnaHQuCgpgYGB7cn0KaXlfYWggJT4lIG5yb3coKQpgYGAKCldlJ2xsIHByb25vdW5jZSB0aGlzICJwaXBpbmcgYGl5X2FoYCBpbnRvIGBucm93KClgLiIKClRoaXMgbWlnaHQgbm90IHNlZW0gc28gZWFydGggc2hhdHRlcmluZyByaWdodCBhd2F5LCBidXQgaXQgcmVhbGx5IHNoaW5lcyB3aGVuIHlvdSB3YW50IHRvIGFwcGx5IGEgZnVuY3Rpb24gdG8gdGhlIG91dHB1dCBvZiB0aGUgcHJldmlvdXMgZnVuY3Rpb24uIExldCdzIHNheSB5b3Ugd2FudGVkIHRvIGNoZWNrIGhvdyBtYW55IHJvd3Mgb2YgCiJpeSIgZGF0YSB0aGVyZSBpcy4gVXNpbmcgbm9ybWFsIGZ1bmN0aW9uYWwgYXBwbGljYXRpb24sIHlvdSdkIGhhdmUgdG8gZG8gdGhpczoKCmBgYHtyfQpucm93KGZpbHRlcihpeV9haCwgcGx0X3ZjbGFzcyA9PSAiaXkiKSkKYGBgCgpIZXJlLCB0aGUgZmlyc3QgZnVuY3Rpb24geW91IGFwcGx5IGlzIHRoZSBtb3N0IGRlZXBseSBlbWJlZGRlZCwgYW5kIHRoZSBsYXN0IGZ1bmN0aW9uIHlvdSBhcHBseSBpcyB0aGUgb3V0ZXJtb3N0LiBTb21lIHN5bnRhY3RpY2lhbnMgZmVlbCB2ZXJ5IGNvbWZvcnRhYmxlIHdpdGggYnJhY2tldCBub3RhdGlvbiBsaWtlIHRoaXMsIGJ1dCBpdCByYXBpZGx5IGdldHMgdW5yZWFkYWJsZSwgYW5kIG1pcy1tYXRjaGluZyBjbG9zaW5nIHBhcmVudGhlc2VzIGlzIGEgdmVyeSBjb21tb24ga2luZCBvZiBlcnJvci4gSGVyZSdzIGhvdyB0aGVzZSBzYW1lIGV4YWN0IG9wZXJhdGlvbnMgbG9vayBsaWtlIHdpdGggdGhlIHBpcGVzLgpgYGB7cn0KaXlfYWggJT4lCiAgZmlsdGVyKHBsdF92Y2xhc3MgPT0gIml5IikgJT4lCiAgbnJvdygpCmBgYAoKVGhpcyBpcyBtdWNoIG1vcmUgcmVhZGFibGUsIGFuZCB0aGUgZnVuY3Rpb25zIGFyZSB2aXN1YWxseSBvcmRlcmVkIGluIHRoZSBzYW1lIG9yZGVyIHRoZXkncmUgYXBwbGllZC4KCgoKClRoZSBiZW5lZml0IG9mIHBpcGluZyBiZWNvbWVzIG1vc3QgYXBwYXJlbnQgaWYgd2UgcmV2aXNpdCBvdXIgZGF0YSBjbGVhbmluZyBwaXBlbGluZSBmcm9tIG91ciBsYXN0IG1lZXRpbmcuIEhlcmUncyBgaXlfYWhgIHdpZGUgYWdhaW4uCgpgYGB7cn0KaXlfYWhfd2lkZQpgYGAKCkxhc3QgdGltZSwgd2UgbWFkZSB0aGlzIGRhdGEgd2lkZSB0byBsb25nIGJ5IGNyZWF0aW5nIGludGVybWVkaWF0ZSBkYXRhIGZyYW1lcy4gSG93ZXZlciwgd2UgY291bGQgZG8gaXQgYWxsIGluIGEgc2luZ2xlIHNob3QgYnkgbmVzdGluZyBmdW5jdGlvbnMgbGlrZSBzbzoKCmBgYHtyfQpzcHJlYWQoCiAgc2VwYXJhdGUoCiAgICBnYXRoZXIoaXlfYWhfd2lkZSwgCiAgICAgICAgICAga2V5ID0gInZvd2VsX2Zvcm1hbnQiLCAKICAgICAgICAgICB2YWx1ZSA9ICJoeiIsIAogICAgICAgICAgIGFoX0YxOml5X0YyKSwgCiAgICAidm93ZWxfZm9ybWFudCIsIAogICAgaW50byA9IGMoInZvd2VsIiwgImZvcm1hbnQiKSwgCiAgICBzZXAgPSAiXyIpLCAKICBmb3JtYW50LCAKICBoeikKYGBgCgpCdXQgZ29vZCBsdWNrIHdyaXRpbmcgdGhhdCB0d2ljZSwgb3Igc2hhcmluZyB5b3VyIGNvZGUgd2l0aCBhbnlvbmUuIE5vdGljZSBob3cgdGhlIGFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBgc3ByZWFkKClgIGFyZSBtYXhpbWFsbHkgZmFyIGF3YXkgZnJvbSB0aGUgYWN0dWFsIGNhbGwgdG8gdGhlIGZ1bmN0aW9uIGF0IHRoZSB0b3A/CgpQaXBpbmcgbWFrZXMgaXQgbXVjaCBiZXR0ZXIuCgpgYGB7cn0KaXlfYWhfd2lkZSAlPiUKICBnYXRoZXIoa2V5ID0gInZvd2VsX2Zvcm1hbnQiLCB2YWx1ZSA9ICJoeiIsIGFoX0YxOml5X0YyKSAlPiUKICBzZXBhcmF0ZSgidm93ZWxfZm9ybWFudCIsIGludG8gID0gYygidm93ZWwiLCAiZm9ybWFudCIpLCBzZXAgPSAiXyIpICU+JQogIHNwcmVhZChmb3JtYW50LCBoeikKYGBgCgpBbm90aGVyIG5pY2UgdGhpbmcgYWJvdXQgdGhlc2UgY2hhaW5zIGlzIHRoYXQgdGhleSBjYW4gYmUgYnVpbHQgdXAgaW5jcmVtZW50YWxseS4gVGhhdCBpcywganVzdCB0aGUgZmlyc3QgdHdvIGxpbmVzIHdpbGwgd29yazoKCmBgYHtyfQppeV9haF93aWRlICU+JQogIGdhdGhlcihrZXkgPSAidm93ZWxfZm9ybWFudCIsIHZhbHVlID0gImh6IiwgYWhfRjE6aXlfRjIpCmBgYAoKQW5kIHNvIHdpbGwganVzdCB0aGUgZmlzdCB0aHJlZToKCmBgYHtyfQppeV9haF93aWRlICU+JQogIGdhdGhlcihrZXkgPSAidm93ZWxfZm9ybWFudCIsIHZhbHVlID0gImh6IiwgYWhfRjE6aXlfRjIpICU+JQogIHNlcGFyYXRlKCJ2b3dlbF9mb3JtYW50IiwgaW50byAgPSBjKCJ2b3dlbCIsICJmb3JtYW50IiksIHNlcCA9ICJfIikKYGBgCgpUaGUgc2FtZSBpcyAqKm5vdCoqIHRydWUgb2YgdGhlIGVtYmVkZGVkIGZ1bmN0aW9uYWwgYXBwbGljYXRpb24sIGJlY2F1c2UgaXQgZGVwZW5kcyBvbiBtYXRjaGluZyBwYXJlbnRoZXNlcyBiZXR3ZWVuIHRoZSBiZWdpbm5pbmcgYW5kIGVuZC4KCgoKCjxkaXYgY2xhc3MgPSAiYm94IGlkaW9tIj4KPHNwYW4gY2xhc3MgPSAibGFiZWwiPklkaW9tPC9zcGFuPgoKSSBhbSBnb2luZyB0byByZWNvbW1lbmQgdGhhdCB5b3UgcHV0IGEgbmV3IGxpbmUgYWZ0ZXIgZXZlcnkgcGlwZSwgYCU+JWAsIGFuZCBpbmRlbnQgdGhlIGZvbGxvd2luZyBsaW5lIHdpdGggdHdvIHNwYWNlcyB3aGVuIGNoYWluaW5nIHRvZ2V0aGVyIGZ1bmN0aW9ucy4KPC9kaXY+Cgo8ZGl2IGNsYXNzID0gImJveCBicmVhayI+CjxzcGFuIGNsYXNzID0gImJpZy1sYWJlbCI+fjUgbWludXRlIGFjdGl2aXR5PC9zcGFuPgoKUmUtY3JlYXRlIHRoZSBgZnJ1aXRgIGRhdGEgZnJhbWU6CgpgYGB7cn0KZnJ1aXQgPC0gZGF0YS5mcmFtZShwZXJzb24gPSBjKCJPYWtsZXkiLCAiQ2hhcmxpZSIpLAogICAgICAgICAgICAgICAgIGFwcGxlc19ib3VnaHQgPSBjKDUsIDMpLAogICAgICAgICAgICAgICAgIGFwcGxlc19hdGUgPSBjKDEsIDIpLAogICAgICAgICAgICAgICAgIG9yYW5nZXNfYm91Z2h0ID0gYyg1LCA0KSwKICAgICAgICAgICAgICAgICBvcmFuZ2VzX2F0ZSA9IGMoMywgMykpCmBgYAoKVXNpbmcgcGlwZXMsIHJlY3JlYXRlIHRoZSBgZ2F0aGVyYCwgYHNlcGFyYXRlYCBhbmQgYHNwcmVhZGAgd29ya2Zsb3cgZnJvbSB0aGUgbGFzdCBtZWV0aW5nIHRvIHByb2R1Y2UgdGhpcyB0YWJsZSAoeW91IGNhbiBkb3VibGUgY2hlY2sgdGhlIGNvZGUgZnJvbSBsYXN0IHRpbWUpLgoKPGRpdiBzdHlsZSA9ICJ3aWR0aDo1MCU7IG1hcmdpbjphdXRvO2JhY2tncm91bmQtY29sb3I6ICNERUY3RjUiPgpgYGB7ciBlY2hvID0gRiwgcmVzdWx0cyA9ICdhc2lzJ30KbGlicmFyeShrbml0cikKCmZydWl0ICU+JQogIGdhdGhlcihrZXksIHZhbHVlLCAyOjUpJT4lCiAgc2VwYXJhdGUoa2V5LCBpbnRvID0gYygiZnJ1aXQiLCAiYmVoYXZpb3IiKSwgc2VwID0gIl8iKSU+JQogIHNwcmVhZChiZWhhdmlvciwgdmFsdWUpJT4lCiAga2FibGUoKQpgYGAKPC9kaXY+CgoKPC9kaXY+CgojIFNwbGl0LUFwcGx5LUNvbWluZQoKV2hlbiBkb2luZyBkYXRhIGFuYWx5c2lzLCB5b3UncmUgZ29pbmcgdG8gZmluZCB5b3Vyc2VsZiBkb2luZyB0aGVzZSBmb2xsb3dpbmcgc3RlcHMgYSBsb3Q6CgoxLiBTcGxpdHRpbmcgdGhlIGRhdGEgdXAgaW50byBzdWJzZXRzLgoyLiBBcHBseWluZyBzb21lIGtpbmQgb2YgZnVuY3Rpb24gdG8gdGhvc2Ugc3Vic2V0cy4KMy4gQ29tYmluaW5nIHRoZSByZXN1bHRzIGJhY2sgdG9nZXRoZXIKCgpIZXJlIGlzIHNvbWUgZmFrZSBkYXRhIGZyb20gYW4gZXhwZXJpbWVudCBvZiBzcGVha2VycycgZnVuZGFtZW50YWwgZnJlcXVlbmN5LiBHbyBhaGVhZCBhbmQgcnVuIHRoaXMgY29kZToKCmBgYHtyfQogIHBpdGNoIDwtIGRhdGFfZnJhbWUoc3BlYWtlciA9IHJlcChjKCJPYWtsZXkiLCAiQ2hhcmxpZSIsICJBemFyaWEiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVzID0gIGMoMywzLDIpKSwKICAgICAgICAgICAgICAgICAgICAgICBGMCA9IGMoMTI0LCAxMDksIDEyNSwgMTAzLCAxMjEsIDEyMywgMTgxLCAyMDYpKQpgYGAKCjxkaXYgc3R5bGU9IndpZHRoOjI1JSI+CgpgYGB7ciBlY2hvID0gRiwgcmVzdWx0cyA9ICdhc2lzJ30KICBwaXRjaCAlPiUgCiAgICBrYWJsZSgpCmBgYAoKPC9kaXY+CgpPbmUgdGhpbmcgd2UgbWlnaHQgd2FudCB0byBrbm93IGlzIHdoYXQgdGhlIGF2ZXJhZ2UgRjAgaXMgZm9yIGVhY2ggc3BlYWtlci4gSGVyZSdzIGhvdyB3ZSBkbyB0aGF0LCBjb25jZXB0dWFsbHksIHdpdGggdGhlIFNwbGl0LUFwcGx5LUNvbWJpbmUgd29ya2Zsb3cuCgojIyBTcGxpdCB0aGUgZGF0YSB1cAoKRmlyc3QsIHNwbGl0IHRoZSBkYXRhIHVwIGludG8gc3Vic2V0cyBiYXNlZCBvbiB0aGUgInNwZWFrZXIiIGNvbHVtbjoKCjxkaXYgc3R5bGU9IndpZHRoOjEwMCU7ZmxvYXQ6bGVmdDsiPgpgYGB7ciBlY2hvID0gRiwgcmVzdWx0cz0nYXNpcyd9CmZvcihpIGluIHNvcnQodW5pcXVlKHBpdGNoJHNwZWFrZXIpKSl7CiAgY2F0KCI8ZGl2IHN0eWxlPSd3aWR0aDozMCU7ZmxvYXQ6bGVmdDttYXJnaW46MTBweDsnPlxuXG4iKQogIGZvbyA8LSBrYWJsZShzdWJzZXQocGl0Y2gsIHNwZWFrZXIgPT0gaSkpCiAgZm9yKGsgaW4gZm9vKXsKICAgIGNhdChrKQogICAgY2F0KCJcbiIpCiAgfQogIGNhdCgiXG4iKQogIGNhdCgiPC9kaXY+XG4iKQp9CmNhdCgiPC9icj4iKQpgYGAKPC9kaXY+CgojIyBBcHBseSBzb21lIGZ1bmN0aW9uIHRvIHRoZSBkYXRhCgpJbiBlYWNoIHN1YnNldCwgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIEYuCgo8ZGl2IHN0eWxlPSJ3aWR0aDoxMDAlO2Zsb2F0OmxlZnQ7Ij4KYGBge3IgZWNobyA9IEYsIHJlc3VsdHM9J2FzaXMnfQpmb3IoaSBpbiBzb3J0KHVuaXF1ZShwaXRjaCRzcGVha2VyKSkpewogIGNhdCgiPGRpdiBzdHlsZT0nd2lkdGg6MjUlO2Zsb2F0OmxlZnQ7bWFyZ2luOjEwcHg7Jz5cblxuIikKICBmb28gPC0gcGl0Y2ggJT4lCiAgICBmaWx0ZXIoc3BlYWtlciA9PSBpKSU+JQogICAgZ3JvdXBfYnkoc3BlYWtlciklPiUKICAgIHN1bW1hcmlzZShtZWFuX0YwID0gbWVhbihGMCkpJT4lCiAgICBrYWJsZSgpCiAgZm9yKGsgaW4gZm9vKXsKICAgIGNhdChrKQogICAgY2F0KCJcbiIpCiAgfQogIGNhdCgiXG4iKQogIGNhdCgiPC9kaXY+XG4iKQp9CmNhdCgiPC9icj4iKQoKYGBgCjwvZGl2PgoKCiMjIENvbWJpbmUgdGhlIHJlc3VsdAoKQ29tYmluZSB0aGVzZSByZXN1bHRzIGludG8gYSBuZXcgdGFibGUuCgo8ZGl2IHN0eWxlPSJ3aWR0aDoyNSUiPgoKYGBge3IgZWNobyA9IEYsIHJlc3VsdHMgPSAnYXNpcyd9CiAgcGl0Y2ggJT4lIAogICAgZ3JvdXBfYnkoc3BlYWtlcikgJT4lCiAgICBzdW1tYXJpc2UobWVhbl9GMCA9IG1lYW4oRjApKSU+JQogICAga2FibGUoKQpgYGAKCjwvZGl2PgoKCiMjIFNwbGl0LUFwcGx5LUNvbWJpbmUgaW4gYGRwbHlyYAoKVGhlIGBkcGx5cmAgcGFja2FnZSB3YXMgd3JpdHRlbiBzcGVjaWZpY2FsbHkgdG8gc3RyZWFtbGluZSB0aGUgU3BsaXQtQXBwbHktQ29tYmluZSB3b3JrZmxvdy4gQWxsIGBkcGx5cmAgInZlcmJzIiB0YWtlIGEgZGF0YSBmcmFtZSBhcyBpbnB1dCwgZG9lcyBzb21ldGhpbmcgdG8gdGhlbSwgYW5kIHJldHVybnMgYW5vdGhlciBkYXRhIGZyYW1lLiBMZXQncyBzdGFydCB3aXRoIHRoZSBgc3VtbWFyaXNlKClgIGZ1bmN0aW9uLgoKPGRpdiBjbGFzcyA9ICJpbGx1c3RyYXRlIj4Kc3VtbWFyaXNlKDxzcGFuIGNsYXNzID0gInBvcCI+ZGF0YTwvc3Bhbj4sIDxici8+PHNwYW4gY2xhc3MgPSAicG9wIj5uZXdfY29sMSA9IHN1bW1hcnlfZnVuY3Rpb24xPC9zcGFuPiw8YnIgLz4gPHNwYW4gY2xhc3MgPSAicG9wIj5uZXdfY29sMiA9IHN1bW1hcnlfZnVuY3Rpb24yPC9zcGFuPikKPC9kaXY+CgotIGBkYXRhYCAKICAgIC0gVGhlIGRhdGEgZnJhbWUgeW91IHdhbnQgdG8gc3VtbWFyaXNlCi0gc3VtbWFyeSBmdW5jdGlvbnMKICAgIC0gVGhpcyBpc24ndCBhIG5hbWVkIGFyZ3VtZW50LiBIZXJlLCB5b3UgZ2l2ZSBgc3VtbWFyaXNlKClgIGEgZnVuY3Rpb24gdGhhdCBjYW4gdGFrZSBtYW55IHZhbHVlcyBhcyBpbnB1dCwgYW5kIHJldHVybnMgYSBzaW5nbGUgdmFsdWUgYXMgb3V0cHV0LgogICAgCkZvciBleGFtcGxlLCBsZXQncyBjYWxjdWxhdGUgdGhlIG1lYW4gRjAgYWNyb3NzIGFsbCBvZiB0aGUgZGF0YSBpbiB0aGUgYHBpdGNoYCBkYXRhIGZyYW1lLgoKCmBgYHtyfQpwaXRjaCAlPiUKICBzdW1tYXJpc2UobWVhbl9GMCA9IG1lYW4oRjApKQpgYGAKClRoaXMgZG9lc24ndCBsb29rIGxpa2UgbXVjaCwgYmVjYXVzZSBpdCdzIGp1c3QgYSBvbmUgcm93IGRhdGEgZnJhbWUgd2l0aCBvbmUgY29sdW1uIGNhbGxlZCBgbWVhbl9GMGAgd2l0aCBvbmUgdmFsdWUsIHdoaWNoIGlzIHRoZSBtZWFuIG9mIGFsbCBvZiB0aGUgRjAgbWVhc3VyZW1lbnRzIGZyb20gZWFjaCBzcGVha2VyLgoKSXQncyBhbHNvIHBvc3NpYmxlIHRvIGRvIG11bHRpcGxlIHN1bW1hcmllcyBhdCBvbmNlIGJ5IGp1c3QgYWRkaW5nIG1vcmUgc3VtbWFyeSBmdW5jdGlvbnMgdG8gdGhlIGBzdW1tYXJpc2UoKWAgdmVyYi4KCgoKYGBge3J9CnBpdGNoICU+JQogIHN1bW1hcmlzZShtZWFuX0YwID0gbWVhbihGMCksCiAgICAgICAgICAgIG1lZGlhbl9GMCA9IG1lZGlhbihGMCksCiAgICAgICAgICAgIHNkX0YwID0gc2QoRjApLAogICAgICAgICAgICBtYWRfRjAgPSBtYWQoRjApLAogICAgICAgICAgICByYW5nZV9GMCA9IG1heChGMCktbWluKEYwKSwKICAgICAgICAgICAgbmRhdGEgPSBsZW5ndGgoc3BlYWtlcikpCmBgYAoKVGhlIGBzdW1tYXJpc2UoKWAgZnVuY3Rpb24gaXMgd2hhdCB3ZSB3YW50IHRvICphcHBseSogdG8gb3VyIGRhdGEsIGJ1dCBmb3IgdGhlIFNwbGl0LUFwcGx5LUNvbWJpbmUgd29ya2Zsb3csIHdlIHdhbnQgdG8gKnNwbGl0KiB0aGUgZGF0YSBhY2NvcmRpbmcgdG8gdGhlIHNwZWFrZXIsICphcHBseSogdGhlIGBzdW1tYXJpc2UoKWAgZnVuY3Rpb24gdG8gZWFjaCBzdWJzZXQsIHRoZW4gKmNvbWJpbmUqIHRoZSByZXN1bHRzIGJhY2sgdG9nZXRoZXIuIAoKVG8gc3BsaXQgdXAgdGhlIGRhdGEsIHdlIGZpcnN0IHBhc3MgdGhlIGBwaXRjaGAgZGF0YSBmcmFtZSB0byBgZ3JvdXBfYnkoKWAuCgo8ZGl2IGNsYXNzPSAiaWxsdXN0cmF0ZSI+Cmdyb3VwX2J5KDxzcGFuIGNsYXNzID0gInBvcCI+ZGF0YTwvc3Bhbj4sIDxzcGFuIGNsYXNzID0gInBvcCI+Z3JvdXBBPC9zcGFuPiwgPHNwYW4gY2xhc3MgPSAicG9wIj5ncm91cEI8L3NwYW4+KQo8L2Rpdj4KCi0gYGRhdGFgCiAgICAtIFRoZSBkYXRhIGZyYW1lIHlvdSB3YW50IHRvIGFwcGx5IFNwbGl0LUFwcGx5LUNvbWJpbmUgdG8uCi0gZ3JvdXBpbmdzCiAgICAtIFRoZXNlIGFyZSB0aGUgbmFtZXMgb2YgY29sdW1ucyBpbiB0aGUgZGF0YSBmcmFtZSB0aGF0IHlvdSB3YW50IHRvIHNwbGl0IHRoZSBkYXRhIHVwIGJ5LiBgZ3JvdXBfYnkoKWAgd2lsbCBjcmVhdGUgc3Vic2V0cyBvZiBldmVyeSB1bmlxdWUgY29tYmluYXRpb24gb2YgdmFsdWVzIGluIHRoZXNlIGNvbHVtbnMuCiAgICAKV2hlbiB5b3UgYXBwbHkgYGdyb3VwX2J5KClgIHRvIGEgZGF0YSBmcmFtZSBieSBpdHNlbGYsIG5vdGhpbmcgcmVhbGx5IHNwZWNpYWwgaGFwcGVuczoKCmBgYHtyfQpwaXRjaCAlPiUgCiAgZ3JvdXBfYnkoc3BlYWtlcikKYGBgCgpCdXQgd2hlbiB5b3UgYXBwbHkgYHN1bW1hcmlzZSgpYCBhZnRlciBgZ3JvdXBfYnkoKWAsIHlvdSBnZXQgdGhlIG1lYW4gRjAgZm9yIGVhY2ggc3BlYWtlcjoKCmBgYHtyfQpwaXRjaCAlPiUgCiAgZ3JvdXBfYnkoc3BlYWtlcikgJT4lCiAgc3VtbWFyaXNlKG1lYW5fRjAgPSBtZWFuKEYwKSkKYGBgCgpIZXJlJ3Mgd2hhdCBqdXN0IGhhcHBlbmVkLgoKMS4gYGdyb3VwX2J5KClgIHRvIHNwbGl0IHRoZSBkYXRhIGZyYW1lIHVwIGFjY29yZGluZyB0byB0aGUgdW5pcXVlIHZhbHVlcyBvZiBgc3BlYWtlcmAuCjIuIGBzdW1tYXJpc2UoKWAgY2FsY3VsYXRlZCB0aGUgbWVhbiBGMCB3aXRoaW4gZWFjaCBzdWJzZXQuCjMuIFRoZXkgd2VyZSB0aGVuIGF1dG9tYXRpY2FsbHkgY29tYmluZWQgYmFjayB0b2dldGhlci4KCgo8ZGl2IGNsYXNzID0gImJveCBicmVhayI+CjxzcGFuIGNsYXNzID0gImJpZy1sYWJlbCI+fjUgbWludXRlIGFjdGl2aXR5PC9zcGFuPgoKSnVzdCB0byByZWZyZXNoIHlvdXIgbWVtb3JpZXMsIHRoZSBgaXlfYWhgIGRhdGEgZnJhbWUgY29udGFpbnMgYWxsIGRhdGEgZnJvbSB0aGUgdm93ZWxzIC9pOi8gYW5kIC/JkS8gZnJvbSB0aGUgUE5DLiBUaGUgY29sdW1uIGBpZHN0cmluZ2AgaXMgYSB1bmlxdWUgSUQgZm9yIGVhY2ggc3BlYWtlciwgYW5kIGBwbHRfdmNsYXNzYCBjb2RlcyB0aGUgdm93ZWwgY2xhc3MuCgpDYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgRjEgYW5kIEYyIGZvciAvaTovIGFuZCAvyZEvIGZvciBlYWNoIHNwZWFrZXIuCgo8L2Rpdj4KCgojIyMgQWRkaXRpb25hbCBgZHBseXJgIHZlcmJzCgpIZXJlIGFyZSBhIGZldyBtb3JlIGBkcGx5cmAgdmVyYnMgdGhhdCB5b3UgY2FuIHN0cmluZyB0b2dldGhlci4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCnZlcmIgICAgICAgICAgIGRlc2NyaXB0aW9uICAgICAgICAgICAgICAgCi0tLS0tLS0tLS0tLS0gIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiBgZmlsdGVyKClgICAgICBXZSd2ZSBhbHJlYWR5IHVzZWQgdGhpcyB0byBzdWJzZXQgZGF0YWZyYW1lcy4gCgpgc3VtbWFyaXNlKClgICAgIFRoaXMgdGFrZXMgYSBkYXRhIGZyYW1lLCBhbmQgb3V0cHV0cyBhIG5ldwogICAgICAgICAgICAgICAgIGRhdGEgZnJhbWUgYmFzZWQgb24gdGhlIHN1bW1hcnkgeW91IGFza2VkIGZvcgoKYG11dGF0ZSgpYCAgICAgIFRoaXMgdGFrZXMgYSBkYXRhIGZyYW1lLCBhbmQgYWRkcyBhZGRpdGlvbmFsIAogICAgICAgICAgICAgICAgY29sdW1ucyBiYXNlZCBvbiB0aGUgZm9ybXVsYSB5b3UgZ2l2ZSBpdAoKYHNlbGVjdCgpYCAgICAgIFRoaXMgdGFrZXMgYSBkYXRhIGZyYW1lLCBhbmQgcmV0dXJucyBvbmx5IHRoZSBjb2x1bW5zIHlvdSBhc2sgZm9yIAoKYGFycmFuZ2UoKWAgICAgIFJlb3JkZXJzIHRoZSByb3dzIG9mIHRoZSBkYXRhIGZyYW1lIAotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KClRoZSBvbmUgZnVuY3Rpb24gaGVyZSB0aGF0IHJlcXVpcmVzIHNvbWUgYWRkaXRpb25hbCBpbnN0cnVjdGlvbiBpcyBgbXV0YXRlKClgLgoKPGRpdiBjbGFzcyA9ICJpbGx1c3RyYXRlIj4KbXV0YXRlKDxzcGFuIGNsYXNzID0gInBvcCI+ZGF0YTwvc3Bhbj4sIDxzcGFuIGNsYXNzID0gInBvcCI+bmV3X2NvbCA9IHZhbHVlPC9zcGFuPikKPC9kaXY+CgotIGBkYXRhYAogICAgLSB0aGUgZGF0YSB0aGF0IHlvdSB3YW50IHRvIG11dGF0ZQotIG5ldyBjb2x1bW5zCiAgICAtIEhlcmUsIHlvdSBuYW1lIG5ldyBjb2x1bW5zLCBhbmQgcHJvdmlkZSBzb21lIGtpbmQgb2YgdmFsdWUsIHVzdWFsbHkgdXNpbmcgc29tZSBvdGhlciBmdW5jdGlvbi4KICAgClRoZSBzaW1wbGVzdCBhbmQgbGVhc3QgdXNlZnVsIHVzZSBvZiBgbXV0YXRlKClgIGFkZHMgYSBuZXcgY29sdW1uIHdpdGggYSBzaW5nbGUgdmFsdWU6CgpgYGB7cn0KcGl0Y2ggJT4lCiAgbXV0YXRlKGphd24gPSAiZm9vIikKYGBgCgpIb3dldmVyLCBhIG11Y2ggbW9yZSB1c2VmdWwgdXNlIG9mIGBtdXRhdGUoKWAgY3JlYXRlcyB0cmFuc2Zvcm1lZCBjb2x1bW5zLiBGb3IgZXhhbXBsZSwgc29tZXRpbWVzIHdoZW4gd29ya2luZyB3aXRoIHBpdGNoLCB5b3UgbWlnaHQgZXN0aW1hdGUgcGl0Y2ggZGlmZmVyZW5jZXMgaW4gdGVybXMgb2Ygc2VtaXRvbmVzLiBFdmVyeSBkb3VibGluZyBvZiBwaXRjaCBpcyAxMiBzZW1pdG9uZXMuIEl0J3MgdGhlIGNhc2UgdGhhdCBpZiB5b3UgY2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGxvZzIgdHJhbnNmb3JtIG9mIGFueSB0d28gbnVtYmVycyB0aGF0IGFyZSBkb3VibGVzIG9mIGVhY2hvdGhlciwgeW91IGdldCAxLgoKYGBge3J9CiMgMSBvY3RhdmUgZGlmZmVyZW5jZXMKbG9nMig2KSAtIGxvZzIoMykKbG9nMigxOCkgLSBsb2cyKDkpCgojIDIgb2N0YXZlIGRpZmZlcmVuY2VzCmxvZzIoMTIpIC0gbG9nMigzKQpgYGAKCklmIHlvdSB3YW50ZWQgdG8gZXN0aW1hdGUsIGZvciBlYWNoIHNwZWFrZXIsIHRoZWlyIHBpdGNoIHJhbmdlIGluIHRlcm1zIG9mIHNlbWl0b25lcywgc3RlcCAxIHdvdWxkIGJlIHRvIGNvbnZlcnQgdGhlaXIgRjAgaW4gSHogaW50byBsb2cyKEh6KS4KCmBgYHtyfQpwaXRjaCAlPiUKICBtdXRhdGUobG9nMl9GMCA9IGxvZzIoRjApKQpgYGAKClRoZW4sIGZvciBldmVyeSBzcGVha2VyLCBzdWJ0cmFjdCB0aGVpciBtYXggYGxvZzJfRjBgIGZyb20gdGhlaXIgbWluIGBsb2cyX0YwYC4gCgpgYGB7cn0KcGl0Y2ggJT4lCiAgbXV0YXRlKGxvZzJfRjAgPSBsb2cyKEYwKSkgJT4lCiAgZ3JvdXBfYnkoc3BlYWtlciklPiUKICBzdW1tYXJpc2Uob2N0YXZlX3JhbmdlID0gbWF4KGxvZzJfRjApIC0gbWluKGxvZzJfRjApLAogICAgICAgICAgICBzZW1pdG9uZV9yYW5nZSA9IG9jdGF2ZV9yYW5nZSAqIDEyKQpgYGAKCjxkaXYgY2xhc3MgPSAiYm94IGJyZWFrIj4KPHNwYW4gY2xhc3MgPSAiYmlnLWxhYmVsIj5+NSBtaW51dGUgYWN0aXZpdHk8L3NwYW4+CgpUcnkgdGhlIGZvbGxvd2luZzoKCmBgYHtyfQpwaXRjaCAlPiUKICBtdXRhdGUobnVtYmVyID0gbigpKQpgYGAKCldoYXQgaGFwcGVuZWQ/IFdoYXQgZG8geW91IHRoaW5rIGBuKClgIGRvZXM/CgpOb3cgdHJ5IHRoaXM6CmBgYHtyfQpwaXRjaCAlPiUKICBncm91cF9ieShzcGVha2VyKSU+JQogIG11dGF0ZShudW1iZXIgPSBuKCkpCmBgYAoKV2hhdCdzIGRpZmZlcmVudCB0aGlzIHRpbWU/Cgo8L2Rpdj4KCgoKIyBTcGxpdC1BcHBseS1Db21iaW5lIGZvciBvdGhlciBkYXRhIHR5cGVzCgpEYXRhIGZyYW1lcyBhcmUgbm90IHRoZSBvbmx5IGtpbmQgb2YgZGF0YSB0aGF0IHlvdSBtaWdodCB3YW50IHRvIHVzZSB0aGUgU3BsaXQtQXBwbHktQ29tYmluZSB3b3JrZmxvdyBvbi4gRm9yIGV4YW1wbGUsIHRoZSBgam9lX3Zvd2VsX2ZpbGVzYCBkaXJlY3RvcnkgKHRoYXQgeW91IHVuemlwcGVkIGVhcmxpZXIpIGNvbnRhaW5zIG9uZSAuY3N2IGZvciBlYWNoIHZvd2VsIGNsYXNzIGZyb20gd2hlbiBJIHdhcyBhbiBpbnRlcnZpZXdlciBvbmNlLiBUaGlzIGlzIGFuIHVuY29udmVudGlvbmFsIGRhdGEgb3JnYW5pemF0aW9uIHNjaGVtZSwgYnV0IG5vdCB1bmhlYXJkIG9mLiBGb3IgZXhhbXBsZSwgeW91IG1pZ2h0IGhhdmUgYSBzZXBhcmF0ZSBmaWxlIG9mIHZvd2VsIGRhdGEgZm9yIGVhY2ggc3BlYWtlci4KCldlIGNhbiB1c2UgYSB2YXJpYW50IG9mIHRoZSBTcGxpdC1BcHBseS1Db21iaW5lIHdvcmtmbG93IHRvIGxvYWQgYW5kIGNvbWJpbmUgKmFsbCogb2YgdGhlc2UgZmlsZXMgYXQgb25jZS4KCjxkaXYgY2xhc3MgPSAiYnJlYWsgYm94Ij4KCjxzcGFuIGNsYXNzPSAiYmlnLWxhYmVsIj5+MiBtaW51dGUgYWN0aXZpdHk8L3NwYW4+CgpUaGlzIHBhcnQgbWF5IHJlcXVpcmUgc29tZSBhc3Npc3RhbmNlLiBUaGUgYFN5cy5nbG9iKClgIGZ1bmN0aW9uIHNlYXJjaGVycyBmb3IgYWxsIGZpbGVzIHRoYXQgbWF0Y2ggdGhlIHBhdHRlcm4geW91IHNwZWNpZnkuIFRoZSBjb21tYW5kIGJlbG93IHNheXMgIkxpc3QgYWxsIGZpbGVzIHRoYXQgZW5kIGluIGBjc3ZgIHRoYXQgYXJlIGluIHRoZSBgLi4vZGF0YS9qb2Vfdm93ZWxfZmlsZXMvYCBkaXJlY3RvcnkiLiBUaGUgcHJlY2VlZGluZyBgLi5gIGluIHRoZSBwYXRoIG1lYW5zIHRoYXQgaXQgc2hvdWxkIHN0YXJ0IHRoZSBzZWFyY2ggZnJvbSBvbmUgZGlyZWN0b3J5IGFib3ZlIHdoZXJlIHRoZSBSIG5vdGVib29rIGlzIHNhdmVkLiBJZiB5b3UgaGF2ZSBzZXQgdXAgeW91ciBjb3Vyc2UgZGlyZWN0b3J5IGFzIHJlY29tbWVuZGVkLCB0aGlzIHNob3VsZCB3b3JrLiBJZiB5b3UgZG9uJ3QgZ2V0IG91dHB1dCB0aGF0IGxvb2tzIGxpa2UgdGhpcywgSSdsbCBjb21lIGFyb3VuZCBhbmQgdHJ5IHRvIGZpZ3VyZSBvdXQgd2h5LgoKYGBge3J9ClN5cy5nbG9iKCIuLi9kYXRhL2pvZV92b3dlbF9maWxlcy8qY3N2IikKYGBgCgo8L2Rpdj4KCkFzc2lnbiB0aGlzIHZlY3RvciBvZiBmaWxlIG5hbWVzIHRvIHRoZSB2YXJpYWJsZSBgdm93ZWxfZmlsZXNgLgoKYGBge3J9CnZvd2VsX2ZpbGVzIDwtIFN5cy5nbG9iKCIuLi9kYXRhL2pvZV92b3dlbF9maWxlcy8qY3N2IikKYGBgCgoKSWYgd2UgYXBwbHkgYHJlYWQuY3N2KClgIHRvIHRoZSBmaXJzdCB2b3dlbCBmaWxlIG5hbWUsIGl0IHdpbGwgcmVhZCBpdCBpbiBhcyBhIGRhdGEgZnJhbWUuCgpgYGB7cn0KcmVhZC5jc3Yodm93ZWxfZmlsZXNbMV0pCmBgYAoKQnV0IHRoZXJlIGFyZSAyMSB2b3dlbCBmaWxlcywgYW5kIHdoYXQgd2UgZG9uJ3Qgd2FudCB0byBkbyBpcyB3cml0ZSBvdXQgc29tZXRoaW5nIGxpa2UgYHZvd2VsXzIgPC0gcmVhZF9jc3Yodm93ZWxfZmlsZXNbMl0pYCAyMSB0aW1lcyEgRXZlbiBjb3B5LXBhc3Rpbmcgd2lsbCBpbnZvbHZlIGVycm9ycy4gCgpUbyBTcGxpdC1BcHBseS1Db21iaW5lIGByZWFkX2NzdigpYCB0byB0aGUgdmVjdG9yIG9mIGZpbGUgbmFtZXMgYHZvd2VsX2ZpbGVzYCwgd2UnbGwgdXNlIGBtYXAoKWAuCgo8ZGl2IGNsYXNzID0gImlsbHVzdHJhdGUiPgptYXAoPHNwYW4gY2xhc3MgPSAicG9wIj5saXN0PC9zcGFuPiwgPHNwYW4gY2xhc3MgPSAicG9wIj5mdW5jdGlvbjwvc3Bhbj4sIC4uLikKPC9kaXY+CgotIGBsaXN0YAogICAgLSB0aGUgbGlzdCBvZiB0aGluZ3MgdGhhdCB5b3Ugd2FudCB0byBzcGxpdCB1cAotIGBmdW5jdGlvbmAKICAgIC0gdGhlIGZ1bmN0aW9uIHRoYXQgeW91IHdhbnQgdG8gYXBwbHkgdG8gZXZlcnkgaXRlbSBpbiB0aGUgbGlzdAotIGAuLi5gCiAgICAtIGFueSBleHRyYSBhcmd1bWVudHMgeW91IG5lZWQgdG8gcGFzcyB0byB0aGUgZnVuY3Rpb24uCiAgICAKSXQncyBlYXNpZXN0IHRvIHNlZSB0aGlzIGF0IHdvcms6CgpgYGB7cn0KYWxsX3Zvd2VscyA8LSBtYXAodm93ZWxfZmlsZXMsIHJlYWQuY3N2KQpgYGAKCmBgYHtyfQphbGxfdm93ZWxzWzFdCmBgYApgYGB7cn0KYWxsX3Zvd2Vsc1syMV0KYGBgCgoKSGVyZSdzIHdoYXQgaGFzIGhhcHBlbmVkOgoKLSBgbWFwKClgIGFwcGxpZWQgYHJlYWQuY3N2KClgIHRvIGVhY2ggZmlsZSBuYW1lLgotIGByZWFkLmNzdigpYCByZWFkIGVhY2ggZmlsZSBpbiBhcyBhIGRhdGFmcmFtZS4KLSBgbWFwKClgIHJldHVybmVkIGEgbGlzdCBvZiBkYXRhZnJhbWVzIHRoYXQgYHJlYWQuY3N2KClgIHByb2R1Y2VkLgoKVGhlcmUncyBvbmUgbGFzdCBzdGVwIHdlIG5lZWQgY29tYmluZSBhbGwgb2YgdGhlc2UgZGF0YWZyYW1lcyB0b2dldGhlci4gYGJpbmRfcm93cygpYCB3aWxsIGNvbWJpbmUgYWxsIG9mIHRoZXNlIGRhdGFmcmFtZXMgaW50byBvbmUgYmlnIGRhdGFmcmFtZS4KCmBgYHtyfQp2b3dlbHNfZGYgPC0gYmluZF9yb3dzKGFsbF92b3dlbHMpCmBgYAoKCmBgYHtyfQpoZWFkKHZvd2Vsc19kZikKYGBgCgpPZiBjb3Vyc2UsIHRoaXMgY2FuIGJlIGNoYWluZWQgdG9nZXRoZXIgd2l0aCBwaXBlczoKCmBgYHtyfQp2b3dlbF9maWxlcyAlPiUKICBtYXAocmVhZC5jc3YpJT4lCiAgYmluZF9yb3dzKCkKYGBgCgoKIyMgUmVjdXJzaXZlIERhdGEgRnJhbWVzCgpUaGVyZSBpcyBvbmUgbGFzdCB1c2Ugb2YgYG1hcCgpYCB0aGF0IGlzIHJlYWxseSB1c2VmdWwsIGJ1dCBtaWdodCBiZSBhIGJpdCBtaW5kLWJlbmRpbmcgZm9yIHJpZ2h0IG5vdywgYnV0IHdpbGwgaG9wZWZ1bGx5IHN0YXJ0IHRvIG1ha2UgbW9yZSBzZW5zZSBhcyB3ZSB3b3JrIHRocm91Z2ggdGhlIGNvdXJzZSwgb3Igd2hlbiB5b3UgcmUtcmVhZCB0aGVzZSBub3Rlcy4gSSB3b3VsZG4ndCBuZWNlc3NhcmlsbHkgZmlsZSB0aGlzIHVuZGVyICJyZXF1aXJlZCBjb3JlIFIgY29tcGV0ZW5jeSIsIGJ1dCBpdCBpcyByZWFsbHkgdmVyeSB1c2VmdWwuCgpMZXQncyBzYXkgd2Ugd2VyZSBpbnRlcmVzdGVkIGluIGRvaW5nIGEgY29ycmVsYXRpb24gdGVzdCBiZXR3ZWVuIEYxIGFuZCBGMiBmb3IgYWxsIG9mIG15IHZvd2VscyAodGhpcyBpc24ndCBhbiBlc3BlY2lhbGx5IGludGVyZXN0aW5nIHRoaW5nIHRvIGRvLCBpdCdzIGp1c3QgZm9yIGlsbHVzdHJhdGlvbikuIFdlIGNvdWxkIGRvIHRoYXQgbGlrZSBzbzoKCmBgYHtyfQpjb3IudGVzdCh2b3dlbHNfZGYkRjEsIHZvd2Vsc19kZiRGMikKYGBgCgpXaGF0IGlmIHdlIHdhbnRlZCB0byBkbyBhIGNvcnJlbGF0aW9uIHRlc3QsIG5vdCBvbiBhbGwgb2YgbXkgdm93ZWxzIHRvZ2V0aGVyLCBidXQgd2l0aGluIGVhY2ggdm93ZWw/IFN0ZXAgMSB3b3VsZCBiZSB0byBnZW5lcmFsaXplIHRoZSBsaW5lIHRoYXQgcmFuIGBjb3IudGVzdCgpYCBhYm92ZSBpbnRvIGl0cyBvd24gZnVuY3Rpb24uCgpgYGB7cn0KIycgQG5hbWUgdm93ZWxfY29yX3Rlc3QKIycgQGRlc2NyaXB0aW9uIGNvbmR1Y3RzIGEgY29ycmVsYXRpb24gdGVzdCBvbiB0aGUgZGF0YSBmcmFtZSAoZGYpIHVzaW5nIHRoZSBjb2x1bW5zIEYxIGFuZCBGMgp2b3dlbF9jb3JfdGVzdCA8LSBmdW5jdGlvbihkZil7CiAgY29yLnRlc3QoZGYkRjEsIGRmJEYyKQp9CmBgYAoKYGBge3J9CnZvd2VsX2Nvcl90ZXN0KHZvd2Vsc19kZikKYGBgCgpUaGUgYHZvd2VsX2Nvcl90ZXN0KClgIGZ1bmN0aW9uIHdlIGp1c3QgY3JlYXRlZCBpcyB3aGF0IHdlIHdhbnQgdG8gKmFwcGx5Ki4gTmV4dCB3ZSBuZWVkIHRvIGZpZ3VyZSBvdXQgaG93IHRvICpzcGxpdCogdXAgdGhlIGRhdGEsIHdoaWNoIHdlJ3JlbSBnb2luZyB0byBkbyBieSBjcmVhdGluZyBhIHJlY3Vyc2l2ZSBkYXRhZnJhbWU6CgpgYGB7cn0Kdm93ZWxzX25lc3RlZCA8LSB2b3dlbHNfZGYgJT4lCiAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkocGx0X3ZjbGFzcyklPiUKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIobigpID4gNSklPiUKICAgICAgICAgICAgICAgICAgICBuZXN0KCkKdm93ZWxzX25lc3RlZApgYGAKClRoaXMgaXMgdGhlIG1pbmQgYmVuZGluZyBwYXJ0LiBJbiBgdm93ZWxzX25lc3RlZGAsIHRoZSBmaXJzdCBjb2x1bW4gaXMgdGhlIHZvd2VsIGNsYXNzLCBmb3Igd2hpY2ggdGhlcmUgaXMgb25lIHJvdyBlYWNoLiBJbiB0aGUgbmV3IGBkYXRhYCBjb2x1bW4sIHRoZSB2YWx1ZSBpbiBlYWNoIGNlbGwgaXMgYWN0dWFsbHkgYSB3aG9sZSBkYXRhIGZyYW1lLgoKYGBge3J9CnZvd2Vsc19uZXN0ZWQkZGF0YVsxXQpgYGAKClRoaXMgbWVhbnMgd2UgY2FuIG5vdyB1c2UgYG1hcCgpYCB0byBhcHBseSB0aGUgYHZvd2VsX2Nvcl90ZXN0KClgIGZ1bmN0aW9uIHRvIGVhY2ggb2YgdGhlc2UgZGF0YSBmcmFtZXMuCgpgYGB7cn0Kdm93ZWxzX3Rlc3QgPC0gdm93ZWxzX25lc3RlZCAlPiUKICAgICAgICAgICAgICAgICAgbXV0YXRlKGNvciA9IG1hcChkYXRhLCB2b3dlbF9jb3JfdGVzdCkpCnZvd2Vsc190ZXN0CmBgYAoKRWFjaCBpdGVtIGluIHRoZSBgY29yYCBjb2x1bW4gaXMgbm93IGEgY29ycmVsYXRpb24gdGVzdCByZXN1bHQuCgpgYGB7cn0Kdm93ZWxzX3Rlc3QkY29yWzFdCmBgYAoKV2UndmUgc3VjY2Vzc2Z1bGx5IGZpdCBhIGNvcnJlbGF0aW9uIHRlc3QgZm9yIGV2ZXJ5IHZvd2VsLCBidXQgdGhlIHJlc3VsdHMgYXJlIGtpbmQgb2YgdHJhcHBlZCBhd2F5IGluIHRoZXNlIHN0cmFuZ2UgcHJpbnQgb3V0cy4gV2UgY2FuIGxpYmVyYXRlIHRoZW0gd2l0aCB0aGUgZnVuY3Rpb24gYHRpZHkoKWAgZnJvbSB0aGUgYGJyb29tYCBwYWNrYWdlLiBgdGlkeSgpYCB3aWxsIGNvbnZlcnQgbWFueSBzdGF0aXN0aWNhbCB0ZXN0cyBsaWtlIHRoaXMgaW50byBhIGRhdGFmcmFtZSByZXByZXNlbnRhdGlvbi4KCmBgYHtyfQp0aWR5KHZvd2Vsc190ZXN0JGNvcltbMV1dKQpgYGAKCldlIGFjdHVhbGx5IG5lZWQgdG8gbWFwIHRoZSBgdGlkeSgpYCBmdW5jdGlvbiB0byB0aGUgYGNvcmAgY29sdW1uIGZvciB0aGlzIHRvIHdvcms6CgpgYGB7cn0Kdm93ZWxzX3Jlc3VsdHMgPC0gdm93ZWxzX3Rlc3QgJT4lCiAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGNvcl9kZiA9IG1hcChjb3IsIHRpZHkpKSU+JQogICAgICAgICAgICAgICAgICAgIHVubmVzdChjb3JfZGYpCnZvd2Vsc19yZXN1bHRzICU+JQogIGFycmFuZ2UoLWFicyhlc3RpbWF0ZSkpCmBgYAoKQW5kLCBhcyB1c3VhbCwgdGhpcyB3aG9sZSBwcm9jZWR1cmUgY291bGQgYmUgY2hhaW5lZCB0b2dldGhlciBpbnRvIG9uZSBiaWcgYW5hbHlzaXMuCgoKYGBge3J9CnZvd2Vsc19kZiAlPiUKICBncm91cF9ieShwbHRfdmNsYXNzKSU+JQogIGZpbHRlcihuKCkgPiA1KSU+JQogIG5lc3QoKSAlPiUKICBtdXRhdGUoY29yID0gbWFwKGRhdGEsIHZvd2VsX2Nvcl90ZXN0KSwKICAgICAgICAgY29yX2RmID0gbWFwKGNvciwgdGlkeSkpJT4lCiAgdW5uZXN0KGNvcl9kZiklPiUKICBhcnJhbmdlKC1hYnMoZXN0aW1hdGUpKQpgYGAKCgoK