Introduction
We’ll begin our discussion of descriptive statistics in the categorical half of our flow chart. Specifically, we’ll start by numerically describing categorical variables. As a reminder, categorical variables are variables whose values fit into categories.
Some examples of categorical variables commonly seen in public health data are: sex, race or ethnicity, and level of educational attainment.
Notice that there is no inherent numeric value to any of these categories. Having said that, we can, and often will, assign a numeric value to each category using R.
The two most common numerical descriptions of categorical variables are probably the frequency count (you will often hear this referred to as the n) and the proportion or percentage.
The count is simply the number of observations, in this case people, which fall into each possible category.
The proportion is just the count divided by the total number of observations. In this example, 2 people out of 5 people (40%) are Asian.
Now let’s go over how to calculate frequency counts and percentages using R.
Height and Weight Data
Below, we’re going to learn to do descriptive analysis in R by experimenting with some simulated data that contains several people’s sex, height, and weight. You can follow along with this lesson by copying and pasting the code chunks below in your R session.
# Load the tidyverse package to make our code easier to read
library(tidyverse)
# Simulate some data
height_and_weight_20 <- tibble(
id = c("001", "002", "003", "004", "005", "006", "007", "008", "009",
"010", "011", "012", "013", "014", "015", "016", "017", "018",
"019", "020"),
sex = c("Male", "Male", "Female", "Female", "Male", "Male", "Female", "Male",
"Female", "Male", "Male", "Female", "Female", "Female", "Male",
"Female", "Female", "Female", "Female", 'Female'),
ht_in = c(71, 69, 64, 65, 73, 69, 68, 73, 71, 66, 71, 69, 66, 68, 75, 69,
66, 65, 65, 65),
wt_lbs = c(190, 176, 130, 154, 173, 182, 140, 185, 157, 155, 213, 151, 147,
196, 212, 190, 194, 176, 176, 102)
)
View the data
Let’s start our analysis by taking a quick look at our data…
height_and_weight_20
Here’s what we did above:
Calculating frequencies
Now that we’re able to easily view or data, let’s return to the original purpose of this demonstration – calculating frequencies and proportions. At this point, I suspect that few of you would have any trouble telling me that the frequency of females in this data is 12 and the frequency of males in this data is 8. It’s pretty easy to just count the number of females and males in this small dataset with only 20 rows. Further, if I asked you what proportion of this sample is female, most of you would still be able to easily tell me 12/20 = 0.6
, or 60%. But, what if we had 100 observations or 1,000,000 observations? You’d get sick of counting pretty quickly. Fortunately, you don’t have to.
As is always the case with R, there are multiple ways we can calculate the statistics that we’re interested in – frequencies at the moment. For example, we can use the base R table
function like this:
The base R table function
table(height_and_weight_20$sex)
Female Male
12 8
Additionally, we can use the CrossTable
function from the gmodels
package, which gives us a little more information by default:
The gmodels CrossTable function
gmodels::CrossTable(height_and_weight_20$sex)
Cell Contents
|-------------------------|
| N |
| N / Table Total |
|-------------------------|
Total Observations in Table: 20
| Female | Male |
|-----------|-----------|
| 12 | 8 |
| 0.600 | 0.400 |
|-----------|-----------|
The tidyverse way
The final way I’m going to discuss here is the tidyverse
way, which is my preference. We will have to write a little additional code, but the end result will be more flexible, more readable, and will return our statistics to us in a data frame that we can save and use for further analysis. Let’s walk through this step by step…
The pipe operator: You should already be familiar with the pipe operator (%>%
), but if it doesn’t look familiar to you, you can learn more about it here
If you are using RStudio, you can use the keyboard shortcut shift + command + m
to type the pipe operator
First, we don’t want to view the individual values in our data frame. Instead, we want to condense those values into summary statistics. This is a job for the summarise() function.
height_and_weight_20 %>%
summarise()
As you can see, summarise()
doesn’t do anything interesting on its own. We need to tell it what kind of summary information we want. We can use the n() function to count rows. By, default, it will count all the rows in the data frame. For example:
height_and_weight_20 %>%
summarise(n())
So, what we’ve done above is pass our entire data frame to R and asked R to count the number of rows in the data frame. The result we get from R is a new data frame with 1 column (named n()
) and one row with the value 20 (the number of rows in the original data frame).
This is a great start. However, we really want to count the number of rows that have the value “female” for sex, and then separately count the number of rows that have the value “male” for sex. Said another way, we want to break our data frame up into smaller data frames – one for each value of sex
– and then count the rows. This is exactly what dplyr
’s group_by() function does.
height_and_weight_20 %>%
group_by(sex) %>%
summarise(n())
And, that’s what we want.
However, it will be awkward to work with a variable named n()
in the future. Let’s go ahead and assign it a different name. We can assign it any name we want. Some names that might make sense are n, frequency, or count. I’m going to go ahead an just name it n
without the parentheses.
height_and_weight_20 %>%
group_by(sex) %>%
summarise(n = n())
Finally, estimating categorical frequencies like this is such a common operation that dplyr
has a shortcut for it. We can use the count function to get the same result that we got above.
height_and_weight_20 %>% count(sex)
Calculating percentages
In addition to frequencies, we will often be interested in calculating percentages for categorical variables. As always, there are many was to accomplish this in R. From here on out, I’m going to use the tidyverse
functions.
In this case, the proportion of people in our data who are female can be calculated as the number who are female (12) divided by the total number of people in the data. We already know that there are 20 people in the data, so we could calculate proportions like so:
height_and_weight_20 %>%
count(sex) %>%
mutate(prop = n / 20)
Here’s what we did above:
Because the count()
function returns a data frame just like any other data frame, we can manipulate it in the same ways we can manipulate any other data frame.
So, we used dplyr
’s mutate() verb to create a new variable in the data frame named prop
. Again, we could have named it anything.
Then we set the value of prop
to be equal to the value of n
divided by 20.
This works, but it would be better to have R calculate the total number of observations for the denominator (20) than for us to manually type it in. In this case, we can do that with the sum()
function.
height_and_weight_20 %>%
count(sex) %>%
mutate(prop = n / sum(n))
Here’s what we did above:
- Instead of manually typing in the total count for our denominator (20), we had R calculate it for us using the
sum()
function. The sum()
function added together all the values of the variable n
(i.e., 12 + 8 = 20).
Finally, we just need to multiply our proportion by 100 to convert it to a percentage.
height_and_weight_20 %>%
count(sex) %>%
mutate(percent = n / sum(n) * 100)
Here’s what we did above:
Changed the name of the variable we are creating from prop
to percent
. But, we could have named it anything.
Multiplied the proportion by 100 to convert it to a percentage.
Missing data
In the real world, you will frequently encounter data that has missing values. Let’s quickly take a look at an example by adding some missing values to our data frame.
height_and_weight_20 <- height_and_weight_20 %>%
mutate(sex = replace(sex, c(2, 9), NA)) %>%
print()
Here’s what we did above:
- Replaced the 2nd and 9th value of sex with
NA
(missing)
Now let’s see how our code from above handles this
height_and_weight_20 %>%
count(sex) %>%
mutate(percent = n / sum(n) * 100)
As you can see, we are now treating missing as if it were a category of sex. Sometimes this will be the result you want. However, often you will want the n and percent of non-missing values for your categorical variable. This is sometimes referred to as a complete case analysis. There’s a couple of different ways we can handle this. I will simply filter out rows with a missing value for sex with dplyr
’s filter verb.
height_and_weight_20 %>%
filter(!is.na(sex)) %>%
count(sex) %>%
mutate(percent = n / sum(n) * 100)
Here’s what we did above:
Making code reusable
In the sections above, we learned how we can use the dplyr
functions to calculate the frequency and percent of each category of a categorical variable. However, there is a fair amount of code writing involved. This would be tedious and error prone if we had to type it out many times. Luckily, you already learned a little bit about how writing your own functions in R solves both of those problems.
This looks like a great opportunity to write our own function. Let’s walk through it step-by-step.
First, let’s start with the basic structure for creating any function in R…
n_percent <- function() {
}
Here’s what we did above:
- We created a function called
n_percent()
. Currently, the function takes no arguments and does nothing.
Next, let’s copy our n and percent code from above and poste it into our empty function.
n_percent <- function() {
height_and_weight_20 %>%
filter(!is.na(sex)) %>%
count(sex) %>%
mutate(percent = (n / sum(n) * 100) %>% round(2))
}
Here’s what we did above:
- We added code to the body of
n_percent()
. However, the code above still isn’t reusable.
If you still have the height_and_weight data frame loaded, then when you run n_percent()
you should get the same n and percent for males and females that we got earlier.
If you don’t have the height_and_weight data frame loaded, then you should get an error when you run n_percent()
that says something like, “Error in eval(lhs, parent, parent) : object ‘height_and_weight_20’ not found”
Now, let’s make this function more flexible.
Use our function with other data frames
The data frame “height_and_weight_20” is hard coded into the body of the function we wrote above. That means that we can’t currently use our function with any other data frame, which completely defeats the purpose of what we’re trying to do. Below, we will add a parameter to our function that will allow us to “pass” any data frame into the body of our function.
n_percent <- function(df) {
df %>%
filter(!is.na(sex)) %>%
count(sex) %>%
mutate(percent = (n / sum(n) * 100) %>% round(2))
}
n_percent(height_and_weight_20)
Here’s what we did above:
We added a parameter – df
– to our n_percent()
function.
We replaced height_and_weight_20
in the body of our function with the df
parameter.
That parameter can now accept any data frame as an argument and “pass” that data frame to the rest of our code inside the function. So, now we can use the n_percent()
function on any data frame.
However, the variable sex
is still hard coded into our function. Meaning we are still telling the filter()
function and count()
function to look for a variable called sex
.
Use our function with other variables
Next, let’s add another parameter to our function that will allow us to “pass” any variable to the code inside our function – not just sex
.
n_percent <- function(df, x) {
# Use equo to convert x into a "quoture"
x <- enquo(x)
df %>%
filter(!is.na(!!x)) %>% # Use "!!" because x is a quoture
count(!!x) %>% # Use "!!" because x is a quoture
mutate(percent = (n / sum(n) * 100) %>% round(2))
}
n_percent(height_and_weight_20, sex)
Here’s what we did above:
We added a parameter – x
– to our n_percent()
function.
We replaced sex
in the body of our function with the x
parameter.
That parameter can now accept any variable in the data frame as an argument.
Tidy evaluation
You may notice that we used two new functions – enquo()
and !!
– in the latest version of our n_percent()
function. These functions are part of the tidy evaluation package, which is beyond the scope of this lesson. However, those of you who would like to learn more about tidy evaluation can click here.
Many of you will find tidy evaluation confusing. I still get confused by tidy evaluation sometimes. But, hang in there with me for a second.
We need to use tidy evaluation functions whenever we use dplyr
functions inside the body of the functions we are writing.
Specifically, we need to use equo()
to convert variables into something called “quotures” and we must then use the !!
notation in front of those quotures anywhere in our code that we want to use them like variables.
If the whole tidy evaluation thing is really throwing you for a loop, then you have a couple options:
You can just use enquo()
and !!
now in the way I demonstrated above even though you don’t really understand why you are using them.
You can continue to write out the code from above manually without writing your own functions at this point. For example:
height_and_weight_20 %>%
filter(!is.na(sex)) %>%
count(sex) %>%
mutate(percent = (n / sum(n) * 100) %>% round(2))
- You can make sure not to use any
dplyr
functions inside the body of the functions you write. For example:
n_percent_2 <- function(x) {
x <- x[!is.na(x)] # Filter out missing
results <- data.frame(categories = unique(x)) # Create data frame to contain calculations
results$n <- as.numeric(table(x)) # Get counts
results$percent <- results$n / sum(results$n) * 100 # Calculate percent
results$percent <- round(results$percent, 2) # Round to two decimal places
results # Print out results
}
n_percent_2(height_and_weight_20$sex)
For now, I suggest option 1 or 2 above for most of you. However, these are all just different means to the same end. Ultimately, the “best” method is the one that works “best” for you.
Finally, for demonstration purposes, I’ll load the built-in mtcars
data frame here and use the n_percent()
function to do some calculations on it.
# Load the built-in mtcars data frame
data("mtcars")
mtcars
# Calculate n and percent of cyl manually
mtcars %>%
filter(!is.na(cyl)) %>%
count(cyl) %>%
mutate(percent = (n / sum(n) * 100) %>% round(2))
# Now use our n_percent function
mtcars %>%
n_percent(cyl)
LS0tCnRpdGxlOiAiRGVzY3JpYmluZyBDYXRlZ29yaWNhbCBWYXJpYWJsZXMiCmF1dGhvcjogIkJyYWQgQ2FubmVsbCIKZGF0ZTogIkNyZWF0ZWQ6IDIwMTktMDQtMjIgPGJyPiBVcGRhdGVkOiBgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjc3M6ICIuLi8uLi9jc3MvbG0tbWFya2Rvd24tc3R5bGVzLmNzcyIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgSW50cm9kdWN0aW9uCgpXZeKAmWxsIGJlZ2luIG91ciBkaXNjdXNzaW9uIG9mIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgaW4gdGhlIGNhdGVnb3JpY2FsIGhhbGYgb2Ygb3VyIGZsb3cgY2hhcnQuIFNwZWNpZmljYWxseSwgd2XigJlsbCBzdGFydCBieSBudW1lcmljYWxseSBkZXNjcmliaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gQXMgYSByZW1pbmRlciwgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFyZSB2YXJpYWJsZXMgd2hvc2UgdmFsdWVzIGZpdCBpbnRvIGNhdGVnb3JpZXMuCgohW10oaW1nX2Rlc2NyaXB0aXZlX2FuYWx5c2lzL2Rlc2NyaXB0aXZlX2FuYWx5c2lzX2Zsb3djaGFydF8wNV9jYXRfbnVtLnBuZykKClNvbWUgZXhhbXBsZXMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGNvbW1vbmx5IHNlZW4gaW4gcHVibGljIGhlYWx0aCBkYXRhIGFyZTogc2V4LCByYWNlIG9yIGV0aG5pY2l0eSwgYW5kIGxldmVsIG9mIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQuCgohW10oaW1nX2Rlc2NyaXB0aXZlX2FuYWx5c2lzL2NhdGVnb3JpY2FsX3ZhcmlhYmxlc18wMS5qcGVnKQoKTm90aWNlIHRoYXQgdGhlcmUgaXMgbm8gaW5oZXJlbnQgbnVtZXJpYyB2YWx1ZSB0byBhbnkgb2YgdGhlc2UgY2F0ZWdvcmllcy4gSGF2aW5nIHNhaWQgdGhhdCwgd2UgY2FuLCBhbmQgb2Z0ZW4gd2lsbCwgYXNzaWduIGEgbnVtZXJpYyB2YWx1ZSB0byBlYWNoIGNhdGVnb3J5IHVzaW5nIFIuCgohW10oaW1nX2Rlc2NyaXB0aXZlX2FuYWx5c2lzL2NhdGVnb3JpY2FsX3ZhcmlhYmxlc18wMi5qcGVnKQoKVGhlIHR3byBtb3N0IGNvbW1vbiBudW1lcmljYWwgZGVzY3JpcHRpb25zIG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcmUgcHJvYmFibHkgdGhlIGZyZXF1ZW5jeSBjb3VudCAoeW91IHdpbGwgb2Z0ZW4gaGVhciB0aGlzIHJlZmVycmVkIHRvIGFzIHRoZSBuKSBhbmQgdGhlIHByb3BvcnRpb24gb3IgcGVyY2VudGFnZS4KCiFbXShpbWdfZGVzY3JpcHRpdmVfYW5hbHlzaXMvY2F0ZWdvcmljYWxfdmFyaWFibGVzXzAzLmpwZWcpCgpUaGUgY291bnQgaXMgc2ltcGx5IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zLCBpbiB0aGlzIGNhc2UgcGVvcGxlLCB3aGljaCBmYWxsIGludG8gZWFjaCBwb3NzaWJsZSBjYXRlZ29yeS4KClRoZSBwcm9wb3J0aW9uIGlzIGp1c3QgdGhlIGNvdW50IGRpdmlkZWQgYnkgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMuIEluIHRoaXMgZXhhbXBsZSwgMiBwZW9wbGUgb3V0IG9mIDUgcGVvcGxlICg0MCUpIGFyZSBBc2lhbi4KCk5vdyBsZXTigJlzIGdvIG92ZXIgX2hvd18gdG8gY2FsY3VsYXRlIGZyZXF1ZW5jeSBjb3VudHMgYW5kIHBlcmNlbnRhZ2VzIHVzaW5nIFIuCgojIEhlaWdodCBhbmQgV2VpZ2h0IERhdGEKCkJlbG93LCB3ZSdyZSBnb2luZyB0byBsZWFybiB0byBkbyBkZXNjcmlwdGl2ZSBhbmFseXNpcyBpbiBSIGJ5IGV4cGVyaW1lbnRpbmcgd2l0aCBzb21lIHNpbXVsYXRlZCBkYXRhIHRoYXQgY29udGFpbnMgc2V2ZXJhbCBwZW9wbGUncyBzZXgsIGhlaWdodCwgYW5kIHdlaWdodC4gWW91IGNhbiBmb2xsb3cgYWxvbmcgd2l0aCB0aGlzIGxlc3NvbiBieSBjb3B5aW5nIGFuZCBwYXN0aW5nIHRoZSBjb2RlIGNodW5rcyBiZWxvdyBpbiB5b3VyIFIgc2Vzc2lvbi4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgTG9hZCB0aGUgdGlkeXZlcnNlIHBhY2thZ2UgdG8gbWFrZSBvdXIgY29kZSBlYXNpZXIgdG8gcmVhZApsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7cn0KIyBTaW11bGF0ZSBzb21lIGRhdGEKaGVpZ2h0X2FuZF93ZWlnaHRfMjAgPC0gdGliYmxlKAogIGlkICAgICA9IGMoIjAwMSIsICIwMDIiLCAiMDAzIiwgIjAwNCIsICIwMDUiLCAiMDA2IiwgIjAwNyIsICIwMDgiLCAiMDA5IiwgCiAgICAgICAgICAgICAiMDEwIiwgIjAxMSIsICIwMTIiLCAiMDEzIiwgIjAxNCIsICIwMTUiLCAiMDE2IiwgIjAxNyIsICIwMTgiLCAKICAgICAgICAgICAgICIwMTkiLCAiMDIwIiksCiAgc2V4ICAgID0gYygiTWFsZSIsICJNYWxlIiwgIkZlbWFsZSIsICJGZW1hbGUiLCAiTWFsZSIsICJNYWxlIiwgIkZlbWFsZSIsICJNYWxlIiwgCiAgICAgICAgICAgICAiRmVtYWxlIiwgIk1hbGUiLCAiTWFsZSIsICJGZW1hbGUiLCAiRmVtYWxlIiwgIkZlbWFsZSIsICJNYWxlIiwgCiAgICAgICAgICAgICAiRmVtYWxlIiwgIkZlbWFsZSIsICJGZW1hbGUiLCAiRmVtYWxlIiwgJ0ZlbWFsZScpLAogIGh0X2luICA9IGMoNzEsIDY5LCA2NCwgNjUsIDczLCA2OSwgNjgsIDczLCA3MSwgNjYsIDcxLCA2OSwgNjYsIDY4LCA3NSwgNjksIAogICAgICAgICAgICAgNjYsIDY1LCA2NSwgNjUpLAogIHd0X2xicyA9IGMoMTkwLCAxNzYsIDEzMCwgMTU0LCAxNzMsIDE4MiwgMTQwLCAxODUsIDE1NywgMTU1LCAyMTMsIDE1MSwgMTQ3LCAKICAgICAgICAgICAgIDE5NiwgMjEyLCAxOTAsIDE5NCwgMTc2LCAxNzYsIDEwMikKKQpgYGAKCgojIyBWaWV3IHRoZSBkYXRhCgpMZXQncyBzdGFydCBvdXIgYW5hbHlzaXMgYnkgdGFraW5nIGEgcXVpY2sgbG9vayBhdCBvdXIgZGF0YS4uLgoKYGBge3J9CmhlaWdodF9hbmRfd2VpZ2h0XzIwCmBgYAoKKipIZXJlJ3Mgd2hhdCB3ZSBkaWQgYWJvdmU6KiogICAKCiogU2ltdWxhdGVkIHNvbWUgZGF0YSB0aGF0IHdlIGNhbiB1c2UgdG8gcHJhY3RpY2UgY2F0ZWdvcmljYWwgZGF0YSBhbmFseXNpcy4KCiogV2Ugdmlld2VkIHRoZSBkYXRhIGFuZCBmb3VuZCB0aGF0IGl0IGhhcyA0IHZhcmlhYmxlcyAoY29sdW1ucykgYW5kIDIwIG9ic2VydmF0aW9ucyAocm93cykuCgoKIyBDYWxjdWxhdGluZyBmcmVxdWVuY2llcwoKTm93IHRoYXQgd2XigJlyZSBhYmxlIHRvIGVhc2lseSB2aWV3IG9yIGRhdGEsIGxldOKAmXMgcmV0dXJuIHRvIHRoZSBvcmlnaW5hbCBwdXJwb3NlIG9mIHRoaXMgZGVtb25zdHJhdGlvbiDigJMgY2FsY3VsYXRpbmcgZnJlcXVlbmNpZXMgYW5kIHByb3BvcnRpb25zLiBBdCB0aGlzIHBvaW50LCBJIHN1c3BlY3QgdGhhdCBmZXcgb2YgeW91IHdvdWxkIGhhdmUgYW55IHRyb3VibGUgdGVsbGluZyBtZSB0aGF0IHRoZSBmcmVxdWVuY3kgb2YgZmVtYWxlcyBpbiB0aGlzIGRhdGEgaXMgMTIgYW5kIHRoZSBmcmVxdWVuY3kgb2YgbWFsZXMgaW4gdGhpcyBkYXRhIGlzIDguIEl0J3MgcHJldHR5IGVhc3kgdG8ganVzdCBjb3VudCB0aGUgbnVtYmVyIG9mIGZlbWFsZXMgYW5kIG1hbGVzIGluIHRoaXMgc21hbGwgZGF0YXNldCB3aXRoIG9ubHkgMjAgcm93cy4gRnVydGhlciwgaWYgSSBhc2tlZCB5b3Ugd2hhdCBwcm9wb3J0aW9uIG9mIHRoaXMgc2FtcGxlIGlzIGZlbWFsZSwgbW9zdCBvZiB5b3Ugd291bGQgc3RpbGwgYmUgYWJsZSB0byBlYXNpbHkgdGVsbCBtZSBgMTIvMjAgPSAwLjZgLCBvciA2MCUuIEJ1dCwgd2hhdCBpZiB3ZSBoYWQgMTAwIG9ic2VydmF0aW9ucyBvciAxLDAwMCwwMDAgb2JzZXJ2YXRpb25zPyBZb3XigJlkIGdldCBzaWNrIG9mIGNvdW50aW5nIHByZXR0eSBxdWlja2x5LiBGb3J0dW5hdGVseSwgeW91IGRvbuKAmXQgaGF2ZSB0by4KCkFzIGlzIGFsd2F5cyB0aGUgY2FzZSB3aXRoIFIsIHRoZXJlIGFyZSBtdWx0aXBsZSB3YXlzIHdlIGNhbiBjYWxjdWxhdGUgdGhlIHN0YXRpc3RpY3MgdGhhdCB3ZSdyZSBpbnRlcmVzdGVkIGluIC0tIGZyZXF1ZW5jaWVzIGF0IHRoZSBtb21lbnQuIEZvciBleGFtcGxlLCB3ZSBjYW4gdXNlIHRoZSBiYXNlIFIgYHRhYmxlYCBmdW5jdGlvbiBsaWtlIHRoaXM6CgojIyBUaGUgYmFzZSBSIHRhYmxlIGZ1bmN0aW9uCgpgYGB7cn0KdGFibGUoaGVpZ2h0X2FuZF93ZWlnaHRfMjAkc2V4KQpgYGAKCkFkZGl0aW9uYWxseSwgd2UgY2FuIHVzZSB0aGUgYENyb3NzVGFibGVgIGZ1bmN0aW9uIGZyb20gdGhlIGBnbW9kZWxzYCBwYWNrYWdlLCB3aGljaCBnaXZlcyB1cyBhIGxpdHRsZSBtb3JlIGluZm9ybWF0aW9uIGJ5IGRlZmF1bHQ6CgojIyBUaGUgZ21vZGVscyBDcm9zc1RhYmxlIGZ1bmN0aW9uCgpgYGB7cn0KZ21vZGVsczo6Q3Jvc3NUYWJsZShoZWlnaHRfYW5kX3dlaWdodF8yMCRzZXgpCmBgYAoKIyMgVGhlIHRpZHl2ZXJzZSB3YXkKClRoZSBmaW5hbCB3YXkgSSdtIGdvaW5nIHRvIGRpc2N1c3MgaGVyZSBpcyB0aGUgYHRpZHl2ZXJzZWAgd2F5LCB3aGljaCBpcyBteSBwcmVmZXJlbmNlLiBXZSB3aWxsIGhhdmUgdG8gd3JpdGUgYSBsaXR0bGUgYWRkaXRpb25hbCBjb2RlLCBidXQgdGhlIGVuZCByZXN1bHQgd2lsbCBiZSBtb3JlIGZsZXhpYmxlLCBtb3JlIHJlYWRhYmxlLCBhbmQgd2lsbCByZXR1cm4gb3VyIHN0YXRpc3RpY3MgdG8gdXMgaW4gYSBkYXRhIGZyYW1lIHRoYXQgd2UgY2FuIHNhdmUgYW5kIHVzZSBmb3IgZnVydGhlciBhbmFseXNpcy4gTGV0J3Mgd2FsayB0aHJvdWdoIHRoaXMgc3RlcCBieSBzdGVwLi4uCgo8ZGl2IGNsYXNzPSJtb3JlLWluZm8iPgogIDxwPlRoZSBwaXBlIG9wZXJhdG9yOiBZb3Ugc2hvdWxkIGFscmVhZHkgYmUgZmFtaWxpYXIgd2l0aCB0aGUgcGlwZSBvcGVyYXRvciAoYCU+JWApLCBidXQgaWYgaXQgZG9lc24ndCBsb29rIGZhbWlsaWFyIHRvIHlvdSwgeW91IGNhbiBsZWFybiBtb3JlIGFib3V0IGl0IFtoZXJlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3BpcGVzLmh0bWwpPC9wPgogIDxwPklmIHlvdSBhcmUgdXNpbmcgUlN0dWRpbywgeW91IGNhbiB1c2UgdGhlIGtleWJvYXJkIHNob3J0Y3V0IGBzaGlmdCArIGNvbW1hbmQgKyBtYCB0byB0eXBlIHRoZSBwaXBlIG9wZXJhdG9yPC9wPgo8L2Rpdj4KCkZpcnN0LCB3ZSBkb24ndCB3YW50IHRvIHZpZXcgdGhlIGluZGl2aWR1YWwgdmFsdWVzIGluIG91ciBkYXRhIGZyYW1lLiBJbnN0ZWFkLCB3ZSB3YW50IHRvIGNvbmRlbnNlIHRob3NlIHZhbHVlcyBpbnRvIHN1bW1hcnkgc3RhdGlzdGljcy4gVGhpcyBpcyBhIGpvYiBmb3IgdGhlIFtzdW1tYXJpc2UoKV0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zdW1tYXJpc2UuaHRtbCkgZnVuY3Rpb24uIAoKYGBge3J9CmhlaWdodF9hbmRfd2VpZ2h0XzIwICU+JSAKICBzdW1tYXJpc2UoKQpgYGAKCkFzIHlvdSBjYW4gc2VlLCBgc3VtbWFyaXNlKClgIGRvZXNuJ3QgZG8gYW55dGhpbmcgaW50ZXJlc3Rpbmcgb24gaXRzIG93bi4gV2UgbmVlZCB0byB0ZWxsIGl0IHdoYXQga2luZCBvZiBzdW1tYXJ5IGluZm9ybWF0aW9uIHdlIHdhbnQuIFdlIGNhbiB1c2UgdGhlIFtuKCldKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvbi5odG1sKSBmdW5jdGlvbiB0byBjb3VudCByb3dzLiBCeSwgZGVmYXVsdCwgaXQgd2lsbCBjb3VudCBhbGwgdGhlIHJvd3MgaW4gdGhlIGRhdGEgZnJhbWUuIEZvciBleGFtcGxlOgoKYGBge3J9CmhlaWdodF9hbmRfd2VpZ2h0XzIwICU+JSAKICBzdW1tYXJpc2UobigpKQpgYGAKClNvLCB3aGF0IHdlJ3ZlIGRvbmUgYWJvdmUgaXMgcGFzcyBvdXIgZW50aXJlIGRhdGEgZnJhbWUgdG8gUiBhbmQgYXNrZWQgUiB0byBjb3VudCB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIGRhdGEgZnJhbWUuIFRoZSByZXN1bHQgd2UgZ2V0IGZyb20gUiBpcyBhIG5ldyBkYXRhIGZyYW1lIHdpdGggMSBjb2x1bW4gKG5hbWVkIGBuKClgKSBhbmQgb25lIHJvdyB3aXRoIHRoZSB2YWx1ZSAyMCAodGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lKS4KClRoaXMgaXMgYSBncmVhdCBzdGFydC4gSG93ZXZlciwgd2UgcmVhbGx5IHdhbnQgdG8gY291bnQgdGhlIG51bWJlciBvZiByb3dzIHRoYXQgaGF2ZSB0aGUgdmFsdWUgImZlbWFsZSIgZm9yIHNleCwgYW5kIHRoZW4gc2VwYXJhdGVseSBjb3VudCB0aGUgbnVtYmVyIG9mIHJvd3MgdGhhdCBoYXZlIHRoZSB2YWx1ZSAibWFsZSIgZm9yIHNleC4gU2FpZCBhbm90aGVyIHdheSwgd2Ugd2FudCB0byBicmVhayBvdXIgZGF0YSBmcmFtZSB1cCBpbnRvIHNtYWxsZXIgZGF0YSBmcmFtZXMgLS0gb25lIGZvciBlYWNoIHZhbHVlIG9mIGBzZXhgIC0tIGFuZCB0aGVuIGNvdW50IHRoZSByb3dzLiBUaGlzIGlzIGV4YWN0bHkgd2hhdCBgZHBseXJgJ3MgW2dyb3VwX2J5KCldKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ3JvdXBfYnkuaHRtbCkgZnVuY3Rpb24gZG9lcy4KCgpgYGB7cn0KaGVpZ2h0X2FuZF93ZWlnaHRfMjAgJT4lCiAgZ3JvdXBfYnkoc2V4KSAlPiUgCiAgc3VtbWFyaXNlKG4oKSkKYGBgCgpBbmQsIHRoYXQncyB3aGF0IHdlIHdhbnQuCgpIb3dldmVyLCBpdCB3aWxsIGJlIGF3a3dhcmQgdG8gd29yayB3aXRoIGEgdmFyaWFibGUgbmFtZWQgYG4oKWAgaW4gdGhlIGZ1dHVyZS4gTGV0J3MgZ28gYWhlYWQgYW5kIGFzc2lnbiBpdCBhIGRpZmZlcmVudCBuYW1lLiBXZSBjYW4gYXNzaWduIGl0IGFueSBuYW1lIHdlIHdhbnQuIFNvbWUgbmFtZXMgdGhhdCBtaWdodCBtYWtlIHNlbnNlIGFyZSBuLCBmcmVxdWVuY3ksIG9yIGNvdW50LiBJJ20gZ29pbmcgdG8gZ28gYWhlYWQgYW4ganVzdCBuYW1lIGl0IGBuYCB3aXRob3V0IHRoZSBwYXJlbnRoZXNlcy4KCmBgYHtyfQpoZWlnaHRfYW5kX3dlaWdodF8yMCAlPiUKICBncm91cF9ieShzZXgpICU+JSAKICBzdW1tYXJpc2UobiA9IG4oKSkKYGBgCgpGaW5hbGx5LCBlc3RpbWF0aW5nIGNhdGVnb3JpY2FsIGZyZXF1ZW5jaWVzIGxpa2UgdGhpcyBpcyBzdWNoIGEgY29tbW9uIG9wZXJhdGlvbiB0aGF0IGBkcGx5cmAgaGFzIGEgc2hvcnRjdXQgZm9yIGl0LiBXZSBjYW4gdXNlIHRoZSBbY291bnRdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvdGFsbHkuaHRtbCkgZnVuY3Rpb24gdG8gZ2V0IHRoZSBzYW1lIHJlc3VsdCB0aGF0IHdlIGdvdCBhYm92ZS4KCmBgYHtyfQpoZWlnaHRfYW5kX3dlaWdodF8yMCAlPiUgY291bnQoc2V4KQpgYGAKCgojIENhbGN1bGF0aW5nIHBlcmNlbnRhZ2VzCgpJbiBhZGRpdGlvbiB0byBmcmVxdWVuY2llcywgd2Ugd2lsbCBvZnRlbiBiZSBpbnRlcmVzdGVkIGluIGNhbGN1bGF0aW5nIHBlcmNlbnRhZ2VzIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIEFzIGFsd2F5cywgdGhlcmUgYXJlIG1hbnkgd2FzIHRvIGFjY29tcGxpc2ggdGhpcyBpbiBSLiBGcm9tIGhlcmUgb24gb3V0LCBJJ20gZ29pbmcgdG8gdXNlIHRoZSBgdGlkeXZlcnNlYCBmdW5jdGlvbnMuIAoKSW4gdGhpcyBjYXNlLCB0aGUgcHJvcG9ydGlvbiBvZiBwZW9wbGUgaW4gb3VyIGRhdGEgd2hvIGFyZSBmZW1hbGUgY2FuIGJlIGNhbGN1bGF0ZWQgYXMgdGhlIG51bWJlciB3aG8gYXJlIGZlbWFsZSAoMTIpIGRpdmlkZWQgYnkgdGhlIHRvdGFsIG51bWJlciBvZiBwZW9wbGUgaW4gdGhlIGRhdGEuIFdlIGFscmVhZHkga25vdyB0aGF0IHRoZXJlIGFyZSAyMCBwZW9wbGUgaW4gdGhlIGRhdGEsIHNvIHdlIGNvdWxkIGNhbGN1bGF0ZSBwcm9wb3J0aW9ucyBsaWtlIHNvOgoKYGBge3J9CmhlaWdodF9hbmRfd2VpZ2h0XzIwICU+JSAKICBjb3VudChzZXgpICU+JSAKICBtdXRhdGUocHJvcCA9IG4gLyAyMCkKYGBgCgoqKkhlcmUncyB3aGF0IHdlIGRpZCBhYm92ZToqKiAgIAoKKiBCZWNhdXNlIHRoZSBgY291bnQoKWAgZnVuY3Rpb24gcmV0dXJucyBhIGRhdGEgZnJhbWUganVzdCBsaWtlIGFueSBvdGhlciBkYXRhIGZyYW1lLCB3ZSBjYW4gbWFuaXB1bGF0ZSBpdCBpbiB0aGUgc2FtZSB3YXlzIHdlIGNhbiBtYW5pcHVsYXRlIGFueSBvdGhlciBkYXRhIGZyYW1lLiAgICAKCiogU28sIHdlIHVzZWQgYGRwbHlyYCdzIFttdXRhdGUoKV0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9tdXRhdGUuaHRtbCkgdmVyYiB0byBjcmVhdGUgYSBuZXcgdmFyaWFibGUgaW4gdGhlIGRhdGEgZnJhbWUgbmFtZWQgYHByb3BgLiBBZ2Fpbiwgd2UgY291bGQgaGF2ZSBuYW1lZCBpdCBhbnl0aGluZy4gICAKCiogVGhlbiB3ZSBzZXQgdGhlIHZhbHVlIG9mIGBwcm9wYCB0byBiZSBlcXVhbCB0byB0aGUgdmFsdWUgb2YgYG5gIGRpdmlkZWQgYnkgMjAuCgpUaGlzIHdvcmtzLCBidXQgaXQgd291bGQgYmUgYmV0dGVyIHRvIGhhdmUgUiBjYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZm9yIHRoZSBkZW5vbWluYXRvciAoMjApIHRoYW4gZm9yIHVzIHRvIG1hbnVhbGx5IHR5cGUgaXQgaW4uIEluIHRoaXMgY2FzZSwgd2UgY2FuIGRvIHRoYXQgd2l0aCB0aGUgYHN1bSgpYCBmdW5jdGlvbi4KCmBgYHtyfQpoZWlnaHRfYW5kX3dlaWdodF8yMCAlPiUgCiAgY291bnQoc2V4KSAlPiUgCiAgbXV0YXRlKHByb3AgPSBuIC8gc3VtKG4pKQpgYGAKCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqCgoqIEluc3RlYWQgb2YgbWFudWFsbHkgdHlwaW5nIGluIHRoZSB0b3RhbCBjb3VudCBmb3Igb3VyIGRlbm9taW5hdG9yICgyMCksIHdlIGhhZCBSIGNhbGN1bGF0ZSBpdCBmb3IgdXMgdXNpbmcgdGhlIGBzdW0oKWAgZnVuY3Rpb24uIFRoZSBgc3VtKClgIGZ1bmN0aW9uIGFkZGVkIHRvZ2V0aGVyIGFsbCB0aGUgdmFsdWVzIG9mIHRoZSB2YXJpYWJsZSBgbmAgKGkuZS4sIDEyICsgOCA9IDIwKS4KCkZpbmFsbHksIHdlIGp1c3QgbmVlZCB0byBtdWx0aXBseSBvdXIgcHJvcG9ydGlvbiBieSAxMDAgdG8gY29udmVydCBpdCB0byBhIHBlcmNlbnRhZ2UuCgpgYGB7cn0KaGVpZ2h0X2FuZF93ZWlnaHRfMjAgJT4lIAogIGNvdW50KHNleCkgJT4lIAogIG11dGF0ZShwZXJjZW50ID0gbiAvIHN1bShuKSAqIDEwMCkKYGBgCgoqKkhlcmUncyB3aGF0IHdlIGRpZCBhYm92ZToqKgoKKiBDaGFuZ2VkIHRoZSBuYW1lIG9mIHRoZSB2YXJpYWJsZSB3ZSBhcmUgY3JlYXRpbmcgZnJvbSBgcHJvcGAgdG8gYHBlcmNlbnRgLiBCdXQsIHdlIGNvdWxkIGhhdmUgbmFtZWQgaXQgYW55dGhpbmcuCgoqIE11bHRpcGxpZWQgdGhlIHByb3BvcnRpb24gYnkgMTAwIHRvIGNvbnZlcnQgaXQgdG8gYSBwZXJjZW50YWdlLgoKCiMgTWlzc2luZyBkYXRhCgpJbiB0aGUgcmVhbCB3b3JsZCwgeW91IHdpbGwgZnJlcXVlbnRseSBlbmNvdW50ZXIgZGF0YSB0aGF0IGhhcyBtaXNzaW5nIHZhbHVlcy4gTGV0J3MgcXVpY2tseSB0YWtlIGEgbG9vayBhdCBhbiBleGFtcGxlIGJ5IGFkZGluZyBzb21lIG1pc3NpbmcgdmFsdWVzIHRvIG91ciBkYXRhIGZyYW1lLgoKYGBge3J9CmhlaWdodF9hbmRfd2VpZ2h0XzIwIDwtIGhlaWdodF9hbmRfd2VpZ2h0XzIwICU+JSAKICBtdXRhdGUoc2V4ID0gcmVwbGFjZShzZXgsIGMoMiwgOSksIE5BKSkgJT4lIAogIHByaW50KCkKYGBgCgoqKkhlcmUncyB3aGF0IHdlIGRpZCBhYm92ZToqKgoKKiBSZXBsYWNlZCB0aGUgMm5kIGFuZCA5dGggdmFsdWUgb2Ygc2V4IHdpdGggYE5BYCAobWlzc2luZykKCk5vdyBsZXQncyBzZWUgaG93IG91ciBjb2RlIGZyb20gYWJvdmUgaGFuZGxlcyB0aGlzCgpgYGB7cn0KaGVpZ2h0X2FuZF93ZWlnaHRfMjAgJT4lIAogIGNvdW50KHNleCkgJT4lIAogIG11dGF0ZShwZXJjZW50ID0gbiAvIHN1bShuKSAqIDEwMCkKYGBgCgpBcyB5b3UgY2FuIHNlZSwgd2UgYXJlIG5vdyB0cmVhdGluZyBtaXNzaW5nIGFzIGlmIGl0IHdlcmUgYSBjYXRlZ29yeSBvZiBzZXguIFNvbWV0aW1lcyB0aGlzIHdpbGwgYmUgdGhlIHJlc3VsdCB5b3Ugd2FudC4gSG93ZXZlciwgb2Z0ZW4geW91IHdpbGwgd2FudCB0aGUgbiBhbmQgcGVyY2VudCBvZiBfbm9uLW1pc3NpbmdfIHZhbHVlcyBmb3IgeW91ciBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gVGhpcyBpcyBzb21ldGltZXMgcmVmZXJyZWQgdG8gYXMgYSBfY29tcGxldGUgY2FzZSBhbmFseXNpc18uIFRoZXJlJ3MgYSBjb3VwbGUgb2YgZGlmZmVyZW50IHdheXMgd2UgY2FuIGhhbmRsZSB0aGlzLiBJIHdpbGwgc2ltcGx5IGZpbHRlciBvdXQgcm93cyB3aXRoIGEgbWlzc2luZyB2YWx1ZSBmb3Igc2V4IHdpdGggYGRwbHlyYCdzIFtmaWx0ZXJdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZmlsdGVyLmh0bWwpIHZlcmIuCgpgYGB7cn0KaGVpZ2h0X2FuZF93ZWlnaHRfMjAgJT4lIAogIGZpbHRlcighaXMubmEoc2V4KSkgJT4lIAogIGNvdW50KHNleCkgJT4lIAogIG11dGF0ZShwZXJjZW50ID0gbiAvIHN1bShuKSAqIDEwMCkKYGBgCgoqKkhlcmUncyB3aGF0IHdlIGRpZCBhYm92ZToqKgoKKiBXZSB1c2VkIGBmaWx0ZXIoKWAgdG8ga2VlcCBvbmx5IHRoZSByb3dzIHRoYXQgaGF2ZSBhIF9ub24tbWlzc2luZ18gdmFsdWUgZm9yIHNleC4KCiogVXNlZCBvdXIgY29kZSBmcm9tIGFib3ZlIHRvIGNhbGN1bGF0ZSB0aGUgbiBhbmQgcGVyY2VudCBvZiBub24tbWlzc2luZyB2YWx1ZXMgb2Ygc2V4LgoKCiMgRm9ybWF0dGluZyByZXN1bHRzCgpOb3RpY2UgdGhhdCBub3cgb3VyIHBlcmNlbnRhZ2VzIGFyZSBiZWluZyBkaXNwbGF5ZWQgd2l0aCA1IGRpZ2l0cyB0byB0aGUgcmlnaHQgb2YgdGhlIGRlY2ltYWwuIElmIHdlIHdhbnRlZCB0byBwcmVzZW50IG91ciBmaW5kaW5ncyBzb21ld2hlcmUgKGUuZy4sIGEgam91cm5hbCBhcnRpY2xlIG9yIGEgcmVwb3J0IGZvciBvdXIgZW1wbG95ZXIpIHdlIHdvdWxkIGFsbW9zdCBuZXZlciB3YW50IHRvIGRpc3BsYXkgdGhpcyBtYW55IGRpZ2l0cy4gTGV0J3MgZ2V0IFIgdG8gcm91bmQgdGhlc2UgbnVtYmVycyBmb3IgdXMuCgpgYGB7cn0KaGVpZ2h0X2FuZF93ZWlnaHRfMjAgJT4lIAogIGZpbHRlcighaXMubmEoc2V4KSkgJT4lIAogIGNvdW50KHNleCkgJT4lIAogIG11dGF0ZShwZXJjZW50ID0gKG4gLyBzdW0obikgKiAxMDApICU+JSByb3VuZCgyKSkKYGBgCgoqKkhlcmUncyB3aGF0IHdlIGRpZCBhYm92ZToqKgoKKiBXZSB1c2VkIHRoZSBgcm91bmQoKWAgZnVuY3Rpb24gdG8gcm91bmQgb3VyIHBlcmNlbnRhZ2VzIHRvIDIgZGVjaW1hbCBwbGFjZXMuCgoKIyBNYWtpbmcgY29kZSByZXVzYWJsZQoKSW4gdGhlIHNlY3Rpb25zIGFib3ZlLCB3ZSBsZWFybmVkIGhvdyB3ZSBjYW4gdXNlIHRoZSBgZHBseXJgIGZ1bmN0aW9ucyB0byBjYWxjdWxhdGUgdGhlIGZyZXF1ZW5jeSBhbmQgcGVyY2VudCBvZiBlYWNoIGNhdGVnb3J5IG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUuIEhvd2V2ZXIsIHRoZXJlIGlzIGEgZmFpciBhbW91bnQgb2YgY29kZSB3cml0aW5nIGludm9sdmVkLiBUaGlzIHdvdWxkIGJlIHRlZGlvdXMgYW5kIGVycm9yIHByb25lIGlmIHdlIGhhZCB0byB0eXBlIGl0IG91dCBtYW55IHRpbWVzLiBMdWNraWx5LCB5b3UgYWxyZWFkeSBsZWFybmVkIGEgbGl0dGxlIGJpdCBhYm91dCBob3cgd3JpdGluZyB5b3VyIG93biBmdW5jdGlvbnMgaW4gUiBzb2x2ZXMgYm90aCBvZiB0aG9zZSBwcm9ibGVtcy4gCgo8ZGl2IGNsYXNzPSJtb3JlLWluZm8iPgogIDxwPlJlZnJlc2hlcjogPGEgaHJlZj0iaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9mdW5jdGlvbnMuaHRtbCB0YXJnZXQ9X2JsYW5rIj5SIDQgRGF0YSBTY2llbmNlLCBDaGFwdGVyIDE5LCBGdW5jdGlvbnM8L2E+PC9wPgo8L2Rpdj4KClRoaXMgbG9va3MgbGlrZSBhIGdyZWF0IG9wcG9ydHVuaXR5IHRvIHdyaXRlIG91ciBvd24gZnVuY3Rpb24uIExldOKAmXMgd2FsayB0aHJvdWdoIGl0IHN0ZXAtYnktc3RlcC4KCkZpcnN0LCBsZXQncyBzdGFydCB3aXRoIHRoZSBiYXNpYyBzdHJ1Y3R1cmUgZm9yIGNyZWF0aW5nIGFueSBmdW5jdGlvbiBpbiBSLi4uCgpgYGB7cn0Kbl9wZXJjZW50IDwtIGZ1bmN0aW9uKCkgewoKfQpgYGAKCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqCgoqIFdlIGNyZWF0ZWQgYSBmdW5jdGlvbiBjYWxsZWQgYG5fcGVyY2VudCgpYC4gQ3VycmVudGx5LCB0aGUgZnVuY3Rpb24gdGFrZXMgbm8gYXJndW1lbnRzIGFuZCBkb2VzIG5vdGhpbmcuCgpOZXh0LCBsZXQncyBjb3B5IG91ciBuIGFuZCBwZXJjZW50IGNvZGUgZnJvbSBhYm92ZSBhbmQgcG9zdGUgaXQgaW50byBvdXIgZW1wdHkgZnVuY3Rpb24uCgpgYGB7cn0Kbl9wZXJjZW50IDwtIGZ1bmN0aW9uKCkgewogIGhlaWdodF9hbmRfd2VpZ2h0XzIwICU+JSAKICAgIGZpbHRlcighaXMubmEoc2V4KSkgJT4lIAogICAgY291bnQoc2V4KSAlPiUgCiAgICBtdXRhdGUocGVyY2VudCA9IChuIC8gc3VtKG4pICogMTAwKSAlPiUgcm91bmQoMikpCn0KYGBgCgoqKkhlcmUncyB3aGF0IHdlIGRpZCBhYm92ZToqKgoKKiBXZSBhZGRlZCBjb2RlIHRvIHRoZSBib2R5IG9mIGBuX3BlcmNlbnQoKWAuIEhvd2V2ZXIsIHRoZSBjb2RlIGFib3ZlIHN0aWxsIGlzbid0IHJldXNhYmxlLiAKCjxkaXYgY2xhc3M9Im1vcmUtaW5mbyI+CiAgSWYgeW91IHN0aWxsIGhhdmUgdGhlIGhlaWdodF9hbmRfd2VpZ2h0IGRhdGEgZnJhbWUgbG9hZGVkLCB0aGVuIHdoZW4geW91IHJ1biBgbl9wZXJjZW50KClgIHlvdSBzaG91bGQgZ2V0IHRoZSBzYW1lIG4gYW5kIHBlcmNlbnQgZm9yIG1hbGVzIGFuZCBmZW1hbGVzIHRoYXQgd2UgZ290IGVhcmxpZXIuIAogIAogIElmIHlvdSBkb24ndCBoYXZlIHRoZSBoZWlnaHRfYW5kX3dlaWdodCBkYXRhIGZyYW1lIGxvYWRlZCwgdGhlbiB5b3Ugc2hvdWxkIGdldCBhbiBlcnJvciB3aGVuIHlvdSBydW4gYG5fcGVyY2VudCgpYCB0aGF0IHNheXMgc29tZXRoaW5nIGxpa2UsICJFcnJvciBpbiBldmFsKGxocywgcGFyZW50LCBwYXJlbnQpIDogb2JqZWN0ICdoZWlnaHRfYW5kX3dlaWdodF8yMCcgbm90IGZvdW5kIgo8L2Rpdj4KCk5vdywgbGV0J3MgbWFrZSB0aGlzIGZ1bmN0aW9uIG1vcmUgZmxleGlibGUuCgojIyBVc2Ugb3VyIGZ1bmN0aW9uIHdpdGggb3RoZXIgZGF0YSBmcmFtZXMKClRoZSBkYXRhIGZyYW1lICJoZWlnaHRfYW5kX3dlaWdodF8yMCIgaXMgaGFyZCBjb2RlZCBpbnRvIHRoZSBib2R5IG9mIHRoZSBmdW5jdGlvbiB3ZSB3cm90ZSBhYm92ZS4gVGhhdCBtZWFucyB0aGF0IHdlIGNhbid0IGN1cnJlbnRseSB1c2Ugb3VyIGZ1bmN0aW9uIHdpdGggYW55IG90aGVyIGRhdGEgZnJhbWUsIHdoaWNoIGNvbXBsZXRlbHkgZGVmZWF0cyB0aGUgcHVycG9zZSBvZiB3aGF0IHdlJ3JlIHRyeWluZyB0byBkby4gQmVsb3csIHdlIHdpbGwgYWRkIGEgcGFyYW1ldGVyIHRvIG91ciBmdW5jdGlvbiB0aGF0IHdpbGwgYWxsb3cgdXMgdG8gInBhc3MiIGFueSBkYXRhIGZyYW1lIGludG8gdGhlIGJvZHkgb2Ygb3VyIGZ1bmN0aW9uLgoKYGBge3J9Cm5fcGVyY2VudCA8LSBmdW5jdGlvbihkZikgewogIGRmICU+JSAKICAgIGZpbHRlcighaXMubmEoc2V4KSkgJT4lIAogICAgY291bnQoc2V4KSAlPiUgCiAgICBtdXRhdGUocGVyY2VudCA9IChuIC8gc3VtKG4pICogMTAwKSAlPiUgcm91bmQoMikpCn0KYGBgCgpgYGB7cn0Kbl9wZXJjZW50KGhlaWdodF9hbmRfd2VpZ2h0XzIwKQpgYGAKCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqCgoqIFdlIGFkZGVkIGEgcGFyYW1ldGVyIC0tIGBkZmAgLS0gdG8gb3VyIGBuX3BlcmNlbnQoKWAgZnVuY3Rpb24uCgoqIFdlIHJlcGxhY2VkIGBoZWlnaHRfYW5kX3dlaWdodF8yMGAgaW4gdGhlIGJvZHkgb2Ygb3VyIGZ1bmN0aW9uIHdpdGggdGhlIGBkZmAgcGFyYW1ldGVyLgoKKiBUaGF0IHBhcmFtZXRlciBjYW4gbm93IGFjY2VwdCBhbnkgZGF0YSBmcmFtZSBhcyBhbiBhcmd1bWVudCBhbmQgInBhc3MiIHRoYXQgZGF0YSBmcmFtZSB0byB0aGUgcmVzdCBvZiBvdXIgY29kZSBpbnNpZGUgdGhlIGZ1bmN0aW9uLiBTbywgbm93IHdlIGNhbiB1c2UgdGhlIGBuX3BlcmNlbnQoKWAgZnVuY3Rpb24gb24gYW55IGRhdGEgZnJhbWUuCgoqIEhvd2V2ZXIsIHRoZSB2YXJpYWJsZSBgc2V4YCBpcyBzdGlsbCBoYXJkIGNvZGVkIGludG8gb3VyIGZ1bmN0aW9uLiBNZWFuaW5nIHdlIGFyZSBzdGlsbCB0ZWxsaW5nIHRoZSBgZmlsdGVyKClgIGZ1bmN0aW9uIGFuZCBgY291bnQoKWAgZnVuY3Rpb24gdG8gbG9vayBmb3IgYSB2YXJpYWJsZSBjYWxsZWQgYHNleGAuCgojIyBVc2Ugb3VyIGZ1bmN0aW9uIHdpdGggb3RoZXIgdmFyaWFibGVzCgpOZXh0LCBsZXQncyBhZGQgYW5vdGhlciBwYXJhbWV0ZXIgdG8gb3VyIGZ1bmN0aW9uIHRoYXQgd2lsbCBhbGxvdyB1cyB0byAicGFzcyIgYW55IHZhcmlhYmxlIHRvIHRoZSBjb2RlIGluc2lkZSBvdXIgZnVuY3Rpb24gLS0gbm90IGp1c3QgYHNleGAuCgpgYGB7cn0Kbl9wZXJjZW50IDwtIGZ1bmN0aW9uKGRmLCB4KSB7CiAgCiAgIyBVc2UgZXF1byB0byBjb252ZXJ0IHggaW50byBhICJxdW90dXJlIgogIHggPC0gZW5xdW8oeCkKICAKICBkZiAlPiUgCiAgICBmaWx0ZXIoIWlzLm5hKCEheCkpICU+JSAjIFVzZSAiISEiIGJlY2F1c2UgeCBpcyBhIHF1b3R1cmUKICAgIGNvdW50KCEheCkgJT4lICAgICAgICAgICMgVXNlICIhISIgYmVjYXVzZSB4IGlzIGEgcXVvdHVyZQogICAgbXV0YXRlKHBlcmNlbnQgPSAobiAvIHN1bShuKSAqIDEwMCkgJT4lIHJvdW5kKDIpKQp9CmBgYAoKYGBge3J9Cm5fcGVyY2VudChoZWlnaHRfYW5kX3dlaWdodF8yMCwgc2V4KQpgYGAKCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqCgoqIFdlIGFkZGVkIGEgcGFyYW1ldGVyIC0tIGB4YCAtLSB0byBvdXIgYG5fcGVyY2VudCgpYCBmdW5jdGlvbi4KCiogV2UgcmVwbGFjZWQgYHNleGAgaW4gdGhlIGJvZHkgb2Ygb3VyIGZ1bmN0aW9uIHdpdGggdGhlIGB4YCBwYXJhbWV0ZXIuCgoqIFRoYXQgcGFyYW1ldGVyIGNhbiBub3cgYWNjZXB0IGFueSB2YXJpYWJsZSBpbiB0aGUgZGF0YSBmcmFtZSBhcyBhbiBhcmd1bWVudC4KCiMjIFRpZHkgZXZhbHVhdGlvbgoKWW91IG1heSBub3RpY2UgdGhhdCB3ZSB1c2VkIHR3byBuZXcgZnVuY3Rpb25zIC0tIGBlbnF1bygpYCBhbmQgYCEhYCAtLSBpbiB0aGUgbGF0ZXN0IHZlcnNpb24gb2Ygb3VyIGBuX3BlcmNlbnQoKWAgZnVuY3Rpb24uIFRoZXNlIGZ1bmN0aW9ucyBhcmUgcGFydCBvZiB0aGUgdGlkeSBldmFsdWF0aW9uIHBhY2thZ2UsIHdoaWNoIGlzIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyBsZXNzb24uIFtIb3dldmVyLCB0aG9zZSBvZiB5b3Ugd2hvIHdvdWxkIGxpa2UgdG8gbGVhcm4gbW9yZSBhYm91dCB0aWR5IGV2YWx1YXRpb24gY2FuIGNsaWNrIGhlcmVdKGh0dHBzOi8vdGlkeWV2YWwudGlkeXZlcnNlLm9yZy8pLiAKCk1hbnkgb2YgeW91IHdpbGwgZmluZCB0aWR5IGV2YWx1YXRpb24gY29uZnVzaW5nLiBJIHN0aWxsIGdldCBjb25mdXNlZCBieSB0aWR5IGV2YWx1YXRpb24gc29tZXRpbWVzLiBCdXQsIGhhbmcgaW4gdGhlcmUgd2l0aCBtZSBmb3IgYSBzZWNvbmQuCgoqIFdlIG5lZWQgdG8gdXNlIHRpZHkgZXZhbHVhdGlvbiBmdW5jdGlvbnMgd2hlbmV2ZXIgd2UgdXNlIGBkcGx5cmAgZnVuY3Rpb25zIGluc2lkZSB0aGUgYm9keSBvZiB0aGUgZnVuY3Rpb25zIHdlIGFyZSB3cml0aW5nLgoKKiBTcGVjaWZpY2FsbHksIHdlIG5lZWQgdG8gdXNlIGBlcXVvKClgIHRvIGNvbnZlcnQgdmFyaWFibGVzIGludG8gc29tZXRoaW5nIGNhbGxlZCAicXVvdHVyZXMiIGFuZCB3ZSBtdXN0IHRoZW4gdXNlIHRoZSBgISFgIG5vdGF0aW9uIGluIGZyb250IG9mIHRob3NlIHF1b3R1cmVzIGFueXdoZXJlIGluIG91ciBjb2RlIHRoYXQgd2Ugd2FudCB0byB1c2UgdGhlbSBsaWtlIHZhcmlhYmxlcy4KCioqSWYgdGhlIHdob2xlIHRpZHkgZXZhbHVhdGlvbiB0aGluZyBpcyByZWFsbHkgdGhyb3dpbmcgeW91IGZvciBhIGxvb3AsIHRoZW4geW91IGhhdmUgYSBjb3VwbGUgb3B0aW9uczoqKgoKMS4gWW91IGNhbiBqdXN0IHVzZSBgZW5xdW8oKWAgYW5kIGAhIWAgbm93IGluIHRoZSB3YXkgSSBkZW1vbnN0cmF0ZWQgYWJvdmUgZXZlbiB0aG91Z2ggeW91IGRvbid0IHJlYWxseSB1bmRlcnN0YW5kIF93aHlfIHlvdSBhcmUgdXNpbmcgdGhlbS4KCjIuIFlvdSBjYW4gY29udGludWUgdG8gd3JpdGUgb3V0IHRoZSBjb2RlIGZyb20gYWJvdmUgbWFudWFsbHkgd2l0aG91dCB3cml0aW5nIHlvdXIgb3duIGZ1bmN0aW9ucyBhdCB0aGlzIHBvaW50LiBGb3IgZXhhbXBsZTogCgpgYGB7ciBldmFsPUZBTFNFfQpoZWlnaHRfYW5kX3dlaWdodF8yMCAlPiUgCiAgZmlsdGVyKCFpcy5uYShzZXgpKSAlPiUgCiAgY291bnQoc2V4KSAlPiUgCiAgbXV0YXRlKHBlcmNlbnQgPSAobiAvIHN1bShuKSAqIDEwMCkgJT4lIHJvdW5kKDIpKQpgYGAKCjMuIFlvdSBjYW4gbWFrZSBzdXJlIG5vdCB0byB1c2UgYW55IGBkcGx5cmAgZnVuY3Rpb25zIGluc2lkZSB0aGUgYm9keSBvZiB0aGUgZnVuY3Rpb25zIHlvdSB3cml0ZS4gRm9yIGV4YW1wbGU6CgpgYGB7cn0Kbl9wZXJjZW50XzIgPC0gZnVuY3Rpb24oeCkgewogIHggICAgICAgICAgICAgICA8LSB4WyFpcy5uYSh4KV0gICAgICAgICAgICAgICAgICAgICAgICMgRmlsdGVyIG91dCBtaXNzaW5nCiAgcmVzdWx0cyAgICAgICAgIDwtIGRhdGEuZnJhbWUoY2F0ZWdvcmllcyA9IHVuaXF1ZSh4KSkgIyBDcmVhdGUgZGF0YSBmcmFtZSB0byBjb250YWluIGNhbGN1bGF0aW9ucwogIHJlc3VsdHMkbiAgICAgICA8LSBhcy5udW1lcmljKHRhYmxlKHgpKSAgICAgICAgICAgICAgICMgR2V0IGNvdW50cwogIHJlc3VsdHMkcGVyY2VudCA8LSByZXN1bHRzJG4gLyBzdW0ocmVzdWx0cyRuKSAqIDEwMCAgICMgQ2FsY3VsYXRlIHBlcmNlbnQKICByZXN1bHRzJHBlcmNlbnQgPC0gcm91bmQocmVzdWx0cyRwZXJjZW50LCAyKSAgICAgICAgICAjIFJvdW5kIHRvIHR3byBkZWNpbWFsIHBsYWNlcwogIHJlc3VsdHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUHJpbnQgb3V0IHJlc3VsdHMKfQoKbl9wZXJjZW50XzIoaGVpZ2h0X2FuZF93ZWlnaHRfMjAkc2V4KQpgYGAKCioqRm9yIG5vdywgSSBzdWdnZXN0IG9wdGlvbiAxIG9yIDIgYWJvdmUgZm9yIG1vc3Qgb2YgeW91LiBIb3dldmVyLCB0aGVzZSBhcmUgYWxsIGp1c3QgZGlmZmVyZW50IG1lYW5zIHRvIHRoZSBzYW1lIGVuZC4gVWx0aW1hdGVseSwgdGhlICJiZXN0IiBtZXRob2QgaXMgdGhlIG9uZSB0aGF0IHdvcmtzICJiZXN0IiBmb3IgeW91LioqCgpGaW5hbGx5LCBmb3IgZGVtb25zdHJhdGlvbiBwdXJwb3NlcywgSSdsbCBsb2FkIHRoZSBidWlsdC1pbiBgbXRjYXJzYCBkYXRhIGZyYW1lIGhlcmUgYW5kIHVzZSB0aGUgYG5fcGVyY2VudCgpYCBmdW5jdGlvbiB0byBkbyBzb21lIGNhbGN1bGF0aW9ucyBvbiBpdC4KCmBgYHtyfQojIExvYWQgdGhlIGJ1aWx0LWluIG10Y2FycyBkYXRhIGZyYW1lCmRhdGEoIm10Y2FycyIpCm10Y2FycwpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSBuIGFuZCBwZXJjZW50IG9mIGN5bCBtYW51YWxseQptdGNhcnMgJT4lIAogIGZpbHRlcighaXMubmEoY3lsKSkgJT4lIAogIGNvdW50KGN5bCkgJT4lIAogIG11dGF0ZShwZXJjZW50ID0gKG4gLyBzdW0obikgKiAxMDApICU+JSByb3VuZCgyKSkKYGBgCgpgYGB7cn0KIyBOb3cgdXNlIG91ciBuX3BlcmNlbnQgZnVuY3Rpb24KbXRjYXJzICU+JSAKICBuX3BlcmNlbnQoY3lsKQpgYGAKCjwhLS0KW0NsaWNrIGhlcmUgdG8gY29udGludWVdKGh0dHBzOi8vYnJhZC1jYW5uZWxsLmdpdGh1Yi5pby9jb3Vyc2UtSW50cm9kdWN0aW9uLXRvLVItUHJvZ3JhbW1pbmctZm9yLUVwaWRlbWlvbG9naWMtUmVzZWFyY2gvZ3JhcGhpY2FsX2Rlc2NyaXB0aW9uc19vZl9jYXRlZ29yaWNhbF92YXJpYWJsZXMuaHRtbCkKLS0+