Pearson Correlation Coefficient
Pearson’s Correlation Coefficient is a parametric measure of the linear relationship between two numerical variables. It’s also referred to as rho (pronounced like “row”) and can be written shorthand as a lowercase \(r\). The Pearson Correlation Coefficient can take on values between -1 and 1, including zero.
A value of 0 indicates that there is no correlation between the two variables.
A negative value indicates that there is a negative correlation between the two variables. In other words, as the value of x increases, the value of y decreases. Or, as the value of x decreases, the value of y increases.
A positive value indicates that there is a positive correlation between the two variables. As the value of x increases, the value of y increases. Or as the value of x decreases, the value of y decreases.
A note of caution: When the relationship between two variables is nonlinear or when outliers are present, the correlation coefficient might incorrectly estimate the strength of the relationship. Plotting the data enables you to verify the linear relationship and to identify the potential outliers.
Calculating r
In this first code chunk, we’re going to use some simple simulated data to develop an intuition about describing the relationship between two continuous variables.
# Load the Tidyverse package
library(tidyverse)
set.seed(123)
df <- tibble(
id = 1:20,
x = sample(x = 0:100, size = 20, replace = TRUE),
y = sample(x = 0:100, size = 20, replace = TRUE)
)
df
Here’s what we did above:
We created a data frame with 3 simulated variables – id, x, and y.
We used the sample()
function to create x and y by sampling a number between 0 and 100 at random, 20 times.
The replace = TRUE
option tells R that the same number can be selected more than once.
The set.seed()
function is to ensure that I get the same random numbers every time I run the code chunk.
There is nothing special about 0 and 100; they are totally arbitrary. But, because all of these values are chosen at random, we have no reason to believe that there should be any relationship between them. Accordingly, we should also expect the Pearson Correlation Coefficient to be 0 (or very close to it).
In order to develop an intuition, let’s first plot this data, and get a feel for what it looks like.
ggplot(df, aes(x, y)) +
geom_point() +
theme_bw()
Above, we’ve created a nice scatter plot using ggplot2()
. But, how do we interpret it? Well, each dot corresponds to a person in our data at the point where their x value intersects with their y value. This is made more clear by adding a geom_text()
layer to our plot.
ggplot(df, aes(x, y)) +
geom_point() +
geom_text(aes(label = id), nudge_x = 1.5, nudge_y = 2) +
theme_bw()
Here’s what we did above:
We added a geom_text()
layer to our plot in order to make it clear which person each dot reprsents.
The nudge_x = 1.5
option moves or text (the id number) to the right 1.5 units. The nudge_y = 2
option moves our text 2 units up. We did this to make the id number easier to read. If we had not nudged them, they would have been placed directly on top of the points.
For example, person 1 in our simulated data had an x value of 30 and a y value of 24. When you look at the plot above, does it look like person 1’s point is approximately at (x = 30, y = 71)? If we want to emphasize the point even further, we can plot a vertical line at x = 30 and a horizontal line at y = 71. Let’s do that below.
ggplot(df, aes(x, y)) +
geom_text(aes(label = id), nudge_x = 1.5, nudge_y = 2) +
geom_vline(xintercept = 30, col = "red", size = 0.25) +
geom_hline(yintercept = 71, col = "red", size = 0.25) +
geom_point() +
theme_bw()
As you can see, the dot representing id 1 is at the intersection of these two lines.
So, we know how to read the plot now, but we still don’t really know anything about the relationship between x and y. Remember, we want to be able to characterize x and y as having one of these 5 relationships:
Looking again at our scatter plot, which relationship do you think x and y have?
ggplot(df, aes(x, y)) +
geom_point() +
geom_text(aes(label = id), nudge_x = 1.5, nudge_y = 2) +
geom_point(aes(x, y), data.frame(x = 100, y = 80), shape = 1, size = 16, col = "red") +
geom_point(aes(x, y), data.frame(x = 90, y = 8), shape = 1, size = 16, col = "blue") +
theme_bw()
Well, if you look at id 9 above, x is a high number (100) and y is a high number (80). But if you look at id 15, x is a high number (90) and y is a low number (8). In other words, these dots are scattered all over the chart area. There doesn’t appear to be much of a pattern, trend, or relationship. And that’s exactly what we would expect from randomly generated data.
Now that we know what this data looks like, and we intuitively feel as though x and y are unrelated, it would be nice to quantify our results in some way. And, that is precisely what the Pearson Correlation Coefficient does.
cor.test(x = df$x, y = df$y)
Pearson's product-moment correlation
data: df$x and df$y
t = -0.60281, df = 18, p-value = 0.5542
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
-0.5490152 0.3218878
sample estimates:
cor
-0.1406703
Here’s what we did above:
By default, R’s cor.test()
function gives us a list of information about the relationship between x and y. The very last number in the output (-0.1406703) is the Pearson Correlation Coefficient.
The fact that this value is negative (between -1 and 0) tells us that x and y tend to vary in opposite directions.
The numeric value (0.1406703) tells us something about the strength of the relationship between x and y. In this case, the relationship is not strong – exactly what we expected.
You will sometimes hear rules of thumb for interpreting the strength of \(r\) such as1:
Rules of thumb like this are useful as you are learning; however, you want to make sure you don’t become overly reliant on them. As you get more experience, you will want to start interpreting effect sizes in the context of your data and the specific research question at hand.
The p-value (0.5542) tells us that we’d be pretty likely to get the result we got even if there really were no relationship between x and y – assuming all other assumptions are satisfied and the sample was collected without bias.
Taken together, the weak negative correlation and p-value tell us that there is not much – if any – relationship between x and y. Another way to say the same thing is, “x and y are statistically independent.”
Correlation intuition
To further bolster our intuition about these relationships, let’s look at a few positively and negatively correlated variables.
# Positively correlated data
tibble(
x = 1:10,
y = 100:109,
r = cor(x, y)
) %>%
ggplot() +
geom_point(aes(x, y)) +
geom_text(aes(x = 2.5, y = 107.5, label = paste("r = ", r)), col = "blue") +
theme_classic()
Above, we created positively correlated data. In fact, this data is perfectly positively correlated. That is, every time the value of x increases, the value of y increases by a proportional amount. Now, instead of being randomly scattered around the plot area, the dots line up in a perfect, upward-sloping, diagonal line. I also, went ahead and added the correlation coefficient directly to the plot. As you can see, it is exactly 1. This is what you should expect from perfectly positively correlated data.
How about this next data set? Now, every time x decreases by one, y decreases by one. Is this positively or negatively correlated data?
df <- tibble(
x = 1:-8,
y = 100:91
)
df
df %>%
mutate(r = cor(x, y)) %>%
ggplot() +
geom_point(aes(x, y)) +
geom_text(aes(x = -6, y = 98, label = paste("r = ", r)), col = "blue") +
theme_classic()
This is still perfectly positively correlated data. The values for x and y are still changing in the same direction proportionatly. The fact that the direction is one of decreasing value makes no difference.
One last made up example here. This time, as x increases by one, y decreases by one. Let’s plot this data and calculate the Pearson Correlation Coefficient.
tibble(
x = 1:10,
y = 100:91,
r = cor(x, y)
) %>%
ggplot() +
geom_point(aes(x, y)) +
geom_text(aes(x = 7.5, y = 98, label = paste("r = ", r)), col = "blue") +
theme_classic()
This is what perfectly negatively correlated data looks like. The dots line up in a perfect, downward-sloping, diagonal line, and when we check the value of rho, we see that it is exactly -1.
Of course, as you may have suspected, in real life things are never this cut and dry. Now let’s investigate the relationship between continuous variables using more realistic data.
In this demonstration I’m using data from a class survey I actually conducted in the past:
class <- tibble(
ht_in = c(70, 63, 62, 67, 67, 58, 64, 69, 65, 68, 63, 68, 69, 66, 67, 65,
64, 75, 67, 63, 60, 67, 64, 73, 62, 69, 67, 62, 68, 66, 66, 62,
64, 68, NA, 68, 70, 68, 68, 66, 71, 61, 62, 64, 64, 63, 67, 66,
69, 76, NA, 63, 64, 65, 65, 71, 66, 65, 65, 71, 64, 71, 60, 62,
61, 69, 66, NA),
wt_lbs = c(216, 106, 145, 195, 143, 125, 138, 140, 158, 167, 145, 297, 146,
125, 111, 125, 130, 182, 170, 121, 98, 150, 132, 250, 137, 124,
186, 148, 134, 155, 122, 142, 110, 132, 188, 176, 188, 166, 136,
147, 178, 125, 102, 140, 139, 60, 147, 147, 141, 232, 186, 212,
110, 110, 115, 154, 140, 150, 130, NA, 171, 156, 92, 122, 102,
163, 141, NA)
)
Next, I’m going to use a scatter plot to explore the relationship between height and weight in this data.
ggplot(class, aes(ht_in, wt_lbs)) +
geom_jitter() +
theme_classic()
Quickly, what do you think? Will height and weight be positively correlated, negatively correlated, or not correlated?
cor.test(class$ht_in, class$wt_lbs)
Pearson's product-moment correlation
data: class$ht_in and class$wt_lbs
t = 5.7398, df = 62, p-value = 3.051e-07
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.4013642 0.7292714
sample estimates:
cor
0.5890576
The dots don’t line up in a perfectly upward – or downward – slope. But the general trend is still an upward slope. Additionally, we can see that height and weight are positively correlated because the value of the correlation coefficient is between 0 and positive 1 (0.5890576). By looking at the p-value (3.051e-07), we can also see that the probability of finding a correlation value this large or larger in our sample if the true value of the correlation coefficient in the population from which our sample was drawn is zero, is very small.
That’s quite a mouthful, right? In more relatable terms you can just think of it this way. According to our data, as height increase weight tends to increase as well. Our p-value indicates that it’s pretty unlikely that we would get this result by if there were truly no relationship in the population this sample was drawn from – assuming it’s an unbiased sample.
Quick detour: The p-value above is written in scientific notation, which you may not have seen before. I’ll quickly show you how to basically disable scientific notation in R.
options(scipen = 999)
cor.test(class$ht_in, class$wt_lbs)
Pearson's product-moment correlation
data: class$ht_in and class$wt_lbs
t = 5.7398, df = 62, p-value = 0.0000003051
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.4013642 0.7292714
sample estimates:
cor
0.5890576
Here’s what we did above:
- We used the R global option
options(scipen = 999)
to display decimal numbers instead of scientific notation. Because this is a global option, it will remain in effect until your restart your R session. If you do restart your R session, you will have to run options(scipen = 999)
again to disable scientific notation.
Finally, wouldn’t it be nice if we could draw a line through this graph that sort of quickly summarizes this relationship (or lack thereof). Well, that is exactly what an Ordinary Least Squares (OLS) regression line does.
To add a regression line to our plot, all we need to do is add a geom_smooth()
layer to our scatterplot with the “method” argument set to “lm.” Let’s do that below and take a look.
ggplot(class, aes(ht_in, wt_lbs)) +
geom_smooth(method = "lm") +
geom_jitter() +
theme_classic()
The exact calculation for deriving this line is beyond the scope of this lesson. In general, though, you can think of it as cutting through the middle of all of your points, and representing the average change in the y value given a one-unit change in the x value. So here, the upward slope indicates that, on average, as height (the x value) increases, so does weight (the y value). And that is completely consistent with our previous conclusions about the relationship between height and weight.
LS0tCnRpdGxlOiAiRGVzY3JpYmluZyB0aGUgUmVsYXRpb25zaGlwIEJldHdlZW4gYSBDb250aW51b3VzIE91dGNvbWUgYW5kIGEgQ29udGludW91cyBQcmVkaWN0b3IiCmF1dGhvcjogIkJyYWQgQ2FubmVsbCIKZGF0ZTogIkNyZWF0ZWQ6IDIwMTktMDQtMjIgPGJyPiBVcGRhdGVkOiBgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjc3M6ICIuLi8uLi9jc3MvbG0tbWFya2Rvd24tc3R5bGVzLmNzcyIKYmlibGlvZ3JhcGh5OiBjaXRhdGlvbnMuYmlidGV4CmNzbDogYW1hLmNzbAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCkJlZm9yZSBjb3ZlcmluZyBhbnl0aGluZyBuZXcsIGxldOKAmXMgcXVpY2tseSByZXZpZXcgdGhlIGltcG9ydGFuY2UgYW5kIHV0aWxpdHkgb2YgZGVzY3JpcHRpdmUgYW5hbHlzaXMuIAoKMS4gV2UgY2FuIHVzZSBkZXNjcmlwdGl2ZSBhbmFseXNpcyB0byB1bmNvdmVyIGVycm9ycyBpbiBvdXIgZGF0YQoKMi4gSXQgaGVscHMgdXMgdW5kZXJzdGFuZCB0aGUgZGlzdHJpYnV0aW9uIG9mIHZhbHVlcyBpbiBvdXIgdmFyaWFibGVzCgozLiBEZXNjcmlwdGl2ZSBhbmFseXNpcyBzZXJ2ZSBhcyBhIHN0YXJ0aW5nIHBvaW50IGZvciB1bmRlcnN0YW5kaW5nIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBvdXIgdmFyaWFibGVzCgpJbiB0aGUgZmlyc3QgZmV3IGxlc3NvbnMgb24gZGVzY3JpcHRpdmUgYW5hbHlzaXMgd2UgY292ZXJlZCBwZXJmb3JtaW5nIF9fdW5pdmFyaWF0ZV9fIGFuYWx5c2lzLiBUaGF0IGlzLCBhbmFseXppbmcgYSBzaW5nbGUgbnVtZXJpY2FsIG9yIGEgc2luZ2xlIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiBJbiB0aGlzIG1vZHVsZSwgd2XigJlsbCBsZWFybiBtZXRob2RzIHRvIGRlc2NyaWJlIHJlbGF0aW9uc2hpcHMgX19iZXR3ZWVuX18gdHdvIHZhcmlhYmxlcy4gVGhpcyBpcyBhbHNvIGNhbGxlZCBfX2JpdmFyaWF0ZV9fIGFuYWx5c2lzLiAKCkZvciBleGFtcGxlLCB3ZSBtYXkgYmUgaW50ZXJlc3RlZCBpbiBrbm93aW5nIGlmIHRoZXJlIGlzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gaGVhcnQgcmF0ZSBhbmQgZXhlcmNpc2UuIElmIHNvLCB3ZSBtYXkgYXNrIG91cnNlbHZlcyBpZiBoZWFydCByYXRlIGRpZmZlcnMsIG9uIGF2ZXJhZ2UsIGJ5IGRhaWx5IG1pbnV0ZXMgb2YgZXhlcmNpc2UuIEFuZCwgd2UgY291bGQgYW5zd2VyIHRoYXQgcXVlc3Rpb24gd2l0aCB0aGUgdXNpbmcgYSBiaXZhcmlhdGUgZGVzY3JpcHRpdmUgYW5hbHlzaXMuCgo8IS0tIEFkZCBwaWN0dXJlIG9mIGV4ZXJjaXNlIGFuZCBoZWFydCByYXRlIC0tPgoKQmVmb3JlIHBlcmZvcm1pbmcgYW55IHN1Y2ggYml2YXJpYXRlIGRlc2NyaXB0aXZlIGFuYWx5c2lzLCB5b3Ugc2hvdWxkIGFzayB5b3Vyc2VsZiB3aGF0IHR5cGVzIG9mIHZhcmlhYmxlcyB5b3Ugd2lsbCBhbmFseXplLiBXZeKAmXZlIGFscmVhZHkgZGlzY3Vzc2VkIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gbnVtZXJpY2FsIHZhcmlhYmxlcyBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGVzLCBidXQgd2Ugd2lsbCBhbHNvIG5lZWQgdG8gZGVjaWRlIHdoZXRoZXIgZWFjaCB2YXJpYWJsZSBpcyBhbiBvdXRjb21lIG9yIGEgcHJlZGljdG9yLgoKKipUaGUgdmFyaWFibGUgd2hvc2UgdmFsdWUgd2UgYXJlIGF0dGVtcHRpbmcgdG8gcHJlZGljdCwgZXN0aW1hdGUsIG9yIGRldGVybWluZSBpcyB0aGUgb3V0Y29tZSB2YXJpYWJsZS4qKiBUaGUgb3V0Y29tZSB2YXJpYWJsZSBtYXkgYWxzbyBiZSByZWZlcnJlZCB0byBhcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIG9yIHRoZSByZXNwb25zZSB2YXJpYWJsZS4KCioqVGhlIHZhcmlhYmxlIHRoYXQgd2UgdGhpbmsgd2lsbCBkZXRlcm1pbmUsIG9yIGF0IGxlYXN0IGhlbHAgdXMgcHJlZGljdCwgdGhlIHZhbHVlIG9mIHRoZSBvdXRjb21lIHZhcmlhYmxlIGlzIGNhbGxlZCB0aGUgcHJlZGljdG9yIHZhcmlhYmxlLioqIFRoZSBwcmVkaWN0b3IgdmFyaWFibGUgbWF5IGFsc28gYmUgcmVmZXJyZWQgdG8gYXMgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlIG9yIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZS4KCiFbXShpbWdfZGVzY3JpcHRpdmVfYW5hbHlzaXMvY29udF9jb250XzAxLnBuZykKClNvIGdvaW5nIGJhY2sgdG8gb3VyIGludGVyZXN0IGluIHdoZXRoZXIgb3Igbm90IGhlYXJ0IHJhdGUgZGlmZmVycyBieSBkYWlseSBtaW51dGVzIG9mIGV4ZXJjaXNlLiBJbiB0aGlzIHNjZW5hcmlvIHdoaWNoIHZhcmlhYmxlIGlzIHRoZSBwcmVkaWN0b3IgYW5kIHdoaWNoIGlzIHRoZSBvdXRjb21lPwoKSW4gdGhpcyBzY2VuYXJpbyBkYWlseSBtaW51dGVzIG9mIGV4ZXJjaXNlIGlzIHRoZSBwcmVkaWN0b3IgYW5kIGhlYXJ0IHJhdGUgaXMgdGhlIG91dGNvbWUuCgo8IS0tIE1ha2UgdGhhdCAgbW9yZSBjbGV2ZXIgc29tZWhvdy4gSGlkZSBpdCBvciBtYWtlIHNsaWRlIG9yIHNvbWV0aGluZyAtLT4KPCEtLSBodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYXJuci9xdWVzdGlvbnMuaHRtbCAtLT4KCkhlYXJ0IHJhdGUgaXMgdGhlIHZhcmlhYmxlIHdl4oCZcmUgaW50ZXJlc3RlZCBpbiBwcmVkaWN0aW5nIG9yIHVuZGVyc3RhbmRpbmcsIGFuZCBleGVyY2lzZSBpcyBhIHZhcmlhYmxlIHRoYXQgd2UgdGhpbmsgaGVscHMgdG8gcHJlZGljdCBvciBleHBsYWluIGhlYXJ0IHJhdGUuCgpJbiB0aGlzIGZpcnN0IGxlc3NvbiBvbiBiaXZhcmlhdGUgYW5hbHlzaXMgd2Ugd2lsbCBsZWFybiBhIHNpbXBsZSBtZXRob2QgZm9yIGRlc2NyaWJpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgY29udGludW91cyBvdXRjb21lIHZhcmlhYmxlIGFuZCBhIGNvbnRpbnVvdXMgcHJlZGljdG9yIHZhcmlhYmxlIHN1Y2ggYXMgaGVhcnQgcmF0ZSBhbmQgbWludXRlcyBvZiBkYWlseSBleGVyY2lzZSAtLSB0aGUgUGVhcnNvbiBDb3JyZWxhdGlvbiBDb2VmZmljaWVudC4KCiFbXShpbWdfZGVzY3JpcHRpdmVfYW5hbHlzaXMvY29udF9jb250XzAyLnBuZykKCiMgUGVhcnNvbiBDb3JyZWxhdGlvbiBDb2VmZmljaWVudAoKUGVhcnNvbuKAmXMgQ29ycmVsYXRpb24gQ29lZmZpY2llbnQgaXMgYSBwYXJhbWV0cmljIG1lYXN1cmUgb2YgdGhlIF9fbGluZWFyX18gcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMuIEl04oCZcyBhbHNvIHJlZmVycmVkIHRvIGFzIHJobyAocHJvbm91bmNlZCBsaWtlICJyb3ciKSBhbmQgY2FuIGJlIHdyaXR0ZW4gc2hvcnRoYW5kIGFzIGEgbG93ZXJjYXNlICRyJC4gVGhlIFBlYXJzb24gQ29ycmVsYXRpb24gQ29lZmZpY2llbnQgY2FuIHRha2Ugb24gdmFsdWVzIGJldHdlZW4gLTEgYW5kIDEsIGluY2x1ZGluZyB6ZXJvLiAKCiFbXShpbWdfZGVzY3JpcHRpdmVfYW5hbHlzaXMvY29udF9jb250XzAzX2NvcnJlbGF0aW9uLnBuZykKCkEgdmFsdWUgb2YgMCBpbmRpY2F0ZXMgdGhhdCB0aGVyZSBpcyBubyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLiAKCiFbXShpbWdfZGVzY3JpcHRpdmVfYW5hbHlzaXMvY29udF9jb250XzA0X2NvcnJlbGF0aW9uLnBuZykKCkEgbmVnYXRpdmUgdmFsdWUgaW5kaWNhdGVzIHRoYXQgdGhlcmUgaXMgYSBuZWdhdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLiBJbiBvdGhlciB3b3JkcywgYXMgdGhlIHZhbHVlIG9mIHggaW5jcmVhc2VzLCB0aGUgdmFsdWUgb2YgeSBkZWNyZWFzZXMuIE9yLCBhcyB0aGUgdmFsdWUgb2YgeCBkZWNyZWFzZXMsIHRoZSB2YWx1ZSBvZiB5IGluY3JlYXNlcy4KCiFbXShpbWdfZGVzY3JpcHRpdmVfYW5hbHlzaXMvY29udF9jb250XzA1X2NvcnJlbGF0aW9uLnBuZykKCkEgcG9zaXRpdmUgdmFsdWUgaW5kaWNhdGVzIHRoYXQgdGhlcmUgaXMgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLiBBcyB0aGUgdmFsdWUgb2YgeCBpbmNyZWFzZXMsIHRoZSB2YWx1ZSBvZiB5IGluY3JlYXNlcy4gT3IgYXMgdGhlIHZhbHVlIG9mIHggZGVjcmVhc2VzLCB0aGUgdmFsdWUgb2YgeSBkZWNyZWFzZXMuCgohW10oaW1nX2Rlc2NyaXB0aXZlX2FuYWx5c2lzL2NvbnRfY29udF8wNl9jb3JyZWxhdGlvbi5wbmcpCgoqKkEgbm90ZSBvZiBjYXV0aW9uOioqIFdoZW4gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMgaXMgbm9ubGluZWFyIG9yIHdoZW4gb3V0bGllcnMgYXJlIHByZXNlbnQsIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBtaWdodCBpbmNvcnJlY3RseSBlc3RpbWF0ZSB0aGUgc3RyZW5ndGggb2YgdGhlIHJlbGF0aW9uc2hpcC4gUGxvdHRpbmcgdGhlIGRhdGEgZW5hYmxlcyB5b3UgdG8gdmVyaWZ5IHRoZSBsaW5lYXIgcmVsYXRpb25zaGlwIGFuZCB0byBpZGVudGlmeSB0aGUgcG90ZW50aWFsIG91dGxpZXJzLgoKPCEtLSBzaG93IGFuIGV4YW1wbGUgLS0+CgojIyBDYWxjdWxhdGluZyByCgpJbiB0aGlzIGZpcnN0IGNvZGUgY2h1bmssIHdl4oCZcmUgZ29pbmcgdG8gdXNlIHNvbWUgc2ltcGxlIHNpbXVsYXRlZCBkYXRhIHRvIGRldmVsb3AgYW4gaW50dWl0aW9uIGFib3V0IGRlc2NyaWJpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBjb250aW51b3VzIHZhcmlhYmxlcy4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgTG9hZCB0aGUgVGlkeXZlcnNlIHBhY2thZ2UKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKYGBge3J9CnNldC5zZWVkKDEyMykKZGYgPC0gdGliYmxlKAogIGlkID0gMToyMCwKICB4ICA9IHNhbXBsZSh4ID0gMDoxMDAsIHNpemUgPSAyMCwgcmVwbGFjZSA9IFRSVUUpLAogIHkgID0gc2FtcGxlKHggPSAwOjEwMCwgc2l6ZSA9IDIwLCByZXBsYWNlID0gVFJVRSkKKQpkZgpgYGAKCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqICAgCgoqIFdlIGNyZWF0ZWQgYSBkYXRhIGZyYW1lIHdpdGggMyBzaW11bGF0ZWQgdmFyaWFibGVzIC0tIGlkLCB4LCBhbmQgeS4KCiogV2UgdXNlZCB0aGUgYHNhbXBsZSgpYCBmdW5jdGlvbiB0byBjcmVhdGUgeCBhbmQgeSBieSBzYW1wbGluZyBhIG51bWJlciBiZXR3ZWVuIDAgYW5kIDEwMCBhdCByYW5kb20sIDIwIHRpbWVzLiAKCiogVGhlIGByZXBsYWNlID0gVFJVRWAgb3B0aW9uIHRlbGxzIFIgdGhhdCB0aGUgc2FtZSBudW1iZXIgY2FuIGJlIHNlbGVjdGVkIG1vcmUgdGhhbiBvbmNlLgoKKiBUaGUgYHNldC5zZWVkKClgIGZ1bmN0aW9uIGlzIHRvIGVuc3VyZSB0aGF0IEkgZ2V0IHRoZSBzYW1lIHJhbmRvbSBudW1iZXJzIGV2ZXJ5IHRpbWUgSSBydW4gdGhlIGNvZGUgY2h1bmsuIAoKVGhlcmUgaXMgbm90aGluZyBzcGVjaWFsIGFib3V0IDAgYW5kIDEwMDsgdGhleSBhcmUgdG90YWxseSBhcmJpdHJhcnkuIEJ1dCwgYmVjYXVzZSBhbGwgb2YgdGhlc2UgdmFsdWVzIGFyZSBjaG9zZW4gYXQgcmFuZG9tLCB3ZSBoYXZlIG5vIHJlYXNvbiB0byBiZWxpZXZlIHRoYXQgdGhlcmUgc2hvdWxkIGJlIGFueSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVtLiBBY2NvcmRpbmdseSwgd2Ugc2hvdWxkIGFsc28gZXhwZWN0IHRoZSBQZWFyc29uIENvcnJlbGF0aW9uIENvZWZmaWNpZW50IHRvIGJlIDAgKG9yIHZlcnkgY2xvc2UgdG8gaXQpLgoKSW4gb3JkZXIgdG8gZGV2ZWxvcCBhbiBpbnR1aXRpb24sIGxldOKAmXMgZmlyc3QgcGxvdCB0aGlzIGRhdGEsIGFuZCBnZXQgYSBmZWVsIGZvciB3aGF0IGl0IGxvb2tzIGxpa2UuCgpgYGB7cn0KZ2dwbG90KGRmLCBhZXMoeCwgeSkpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2J3KCkKYGBgCgpBYm92ZSwgd2UndmUgY3JlYXRlZCBhIG5pY2Ugc2NhdHRlciBwbG90IHVzaW5nIGBnZ3Bsb3QyKClgLiBCdXQsIGhvdyBkbyB3ZSBpbnRlcnByZXQgaXQ/IFdlbGwsIGVhY2ggZG90IGNvcnJlc3BvbmRzIHRvIGEgcGVyc29uIGluIG91ciBkYXRhIGF0IHRoZSBwb2ludCB3aGVyZSB0aGVpciB4IHZhbHVlIGludGVyc2VjdHMgd2l0aCB0aGVpciB5IHZhbHVlLiBUaGlzIGlzIG1hZGUgbW9yZSBjbGVhciBieSBhZGRpbmcgYSBgZ2VvbV90ZXh0KClgIGxheWVyIHRvIG91ciBwbG90LiAKCmBgYHtyfQpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGlkKSwgbnVkZ2VfeCA9IDEuNSwgbnVkZ2VfeSA9IDIpICsKICB0aGVtZV9idygpCmBgYAoKKipIZXJlJ3Mgd2hhdCB3ZSBkaWQgYWJvdmU6KioKCiogV2UgYWRkZWQgYSBgZ2VvbV90ZXh0KClgIGxheWVyIHRvIG91ciBwbG90IGluIG9yZGVyIHRvIG1ha2UgaXQgY2xlYXIgd2hpY2ggcGVyc29uIGVhY2ggZG90IHJlcHJzZW50cy4gCgoqIFRoZSBgbnVkZ2VfeCA9IDEuNWAgb3B0aW9uIG1vdmVzIG9yIHRleHQgKHRoZSBpZCBudW1iZXIpIHRvIHRoZSByaWdodCAxLjUgdW5pdHMuIFRoZSBgbnVkZ2VfeSA9IDJgIG9wdGlvbiBtb3ZlcyBvdXIgdGV4dCAyIHVuaXRzIHVwLiBXZSBkaWQgdGhpcyB0byBtYWtlIHRoZSBpZCBudW1iZXIgZWFzaWVyIHRvIHJlYWQuIElmIHdlIGhhZCBub3QgbnVkZ2VkIHRoZW0sIHRoZXkgd291bGQgaGF2ZSBiZWVuIHBsYWNlZCBkaXJlY3RseSBvbiB0b3Agb2YgdGhlIHBvaW50cy4KCkZvciBleGFtcGxlLCBwZXJzb24gMSBpbiBvdXIgc2ltdWxhdGVkIGRhdGEgaGFkIGFuIHggdmFsdWUgb2YgMzAgYW5kIGEgeSB2YWx1ZSBvZiAyNC4gV2hlbiB5b3UgbG9vayBhdCB0aGUgcGxvdCBhYm92ZSwgZG9lcyBpdCBsb29rIGxpa2UgcGVyc29uIDEncyBwb2ludCBpcyBhcHByb3hpbWF0ZWx5IGF0ICh4ID0gMzAsIHkgPSA3MSk/IElmIHdlIHdhbnQgdG8gZW1waGFzaXplIHRoZSBwb2ludCBldmVuIGZ1cnRoZXIsIHdlIGNhbiBwbG90IGEgdmVydGljYWwgbGluZSBhdCB4ID0gMzAgYW5kIGEgaG9yaXpvbnRhbCBsaW5lIGF0IHkgPSA3MS4gTGV0J3MgZG8gdGhhdCBiZWxvdy4KCmBgYHtyfQpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBpZCksIG51ZGdlX3ggPSAxLjUsIG51ZGdlX3kgPSAyKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMzAsIGNvbCA9ICJyZWQiLCBzaXplID0gMC4yNSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDcxLCBjb2wgPSAicmVkIiwgc2l6ZSA9IDAuMjUpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX2J3KCkKYGBgCgpBcyB5b3UgY2FuIHNlZSwgdGhlIGRvdCByZXByZXNlbnRpbmcgaWQgMSBpcyBhdCB0aGUgaW50ZXJzZWN0aW9uIG9mIHRoZXNlIHR3byBsaW5lcy4KClNvLCB3ZSBrbm93IGhvdyB0byByZWFkIHRoZSBwbG90IG5vdywgYnV0IHdlIHN0aWxsIGRvbuKAmXQgcmVhbGx5IGtub3cgYW55dGhpbmcgYWJvdXQgdGhlIF9fcmVsYXRpb25zaGlwX18gYmV0d2VlbiB4IGFuZCB5LiBSZW1lbWJlciwgd2Ugd2FudCB0byBiZSBhYmxlIHRvIGNoYXJhY3Rlcml6ZSB4IGFuZCB5IGFzIGhhdmluZyBvbmUgb2YgdGhlc2UgNSByZWxhdGlvbnNoaXBzOgoKIVtdKGltZ19kZXNjcmlwdGl2ZV9hbmFseXNpcy9jb250X2NvbnRfMDdfY29ycmVsYXRpb24ucG5nKQoKTG9va2luZyBhZ2FpbiBhdCBvdXIgc2NhdHRlciBwbG90LCB3aGljaCByZWxhdGlvbnNoaXAgZG8geW91IHRoaW5rIHggYW5kIHkgaGF2ZT8gCgpgYGB7cn0KZ2dwbG90KGRmLCBhZXMoeCwgeSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBpZCksIG51ZGdlX3ggPSAxLjUsIG51ZGdlX3kgPSAyKSArCiAgZ2VvbV9wb2ludChhZXMoeCwgeSksIGRhdGEuZnJhbWUoeCA9IDEwMCwgeSA9IDgwKSwgc2hhcGUgPSAxLCBzaXplID0gMTYsIGNvbCA9ICJyZWQiKSArCiAgZ2VvbV9wb2ludChhZXMoeCwgeSksIGRhdGEuZnJhbWUoeCA9IDkwLCB5ID0gOCksIHNoYXBlID0gMSwgc2l6ZSA9IDE2LCBjb2wgPSAiYmx1ZSIpICsKICB0aGVtZV9idygpCmBgYAoKV2VsbCwgaWYgeW91IGxvb2sgYXQgaWQgOSBhYm92ZSwgeCBpcyBhIGhpZ2ggbnVtYmVyICgxMDApIGFuZCB5IGlzIGEgaGlnaCBudW1iZXIgKDgwKS4gQnV0IGlmIHlvdSBsb29rIGF0IGlkIDE1LCB4IGlzIGEgaGlnaCBudW1iZXIgKDkwKSBhbmQgeSBpcyBhIGxvdyBudW1iZXIgKDgpLiBJbiBvdGhlciB3b3JkcywgdGhlc2UgZG90cyBhcmUgc2NhdHRlcmVkIGFsbCBvdmVyIHRoZSBjaGFydCBhcmVhLiBUaGVyZSBkb2VzbuKAmXQgYXBwZWFyIHRvIGJlIG11Y2ggb2YgYSBwYXR0ZXJuLCB0cmVuZCwgb3IgcmVsYXRpb25zaGlwLiBBbmQgdGhhdOKAmXMgZXhhY3RseSB3aGF0IHdlIHdvdWxkIGV4cGVjdCBmcm9tIHJhbmRvbWx5IGdlbmVyYXRlZCBkYXRhLiAKCk5vdyB0aGF0IHdlIGtub3cgd2hhdCB0aGlzIGRhdGEgbG9va3MgbGlrZSwgYW5kIHdlIGludHVpdGl2ZWx5IGZlZWwgYXMgdGhvdWdoIHggYW5kIHkgYXJlIHVucmVsYXRlZCwgaXQgd291bGQgYmUgbmljZSB0byBxdWFudGlmeSBvdXIgcmVzdWx0cyBpbiBzb21lIHdheS4gQW5kLCB0aGF0IGlzIHByZWNpc2VseSB3aGF0IHRoZSBQZWFyc29uIENvcnJlbGF0aW9uIENvZWZmaWNpZW50IGRvZXMuCgpgYGB7cn0KY29yLnRlc3QoeCA9IGRmJHgsIHkgPSBkZiR5KQpgYGAKCioqSGVyZSdzIHdoYXQgd2UgZGlkIGFib3ZlOioqCgoqIEJ5IGRlZmF1bHQsIFIncyBgY29yLnRlc3QoKWAgZnVuY3Rpb24gZ2l2ZXMgdXMgYSBsaXN0IG9mIGluZm9ybWF0aW9uIGFib3V0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB4IGFuZCB5LiBUaGUgdmVyeSBsYXN0IG51bWJlciBpbiB0aGUgb3V0cHV0ICgtMC4xNDA2NzAzKSBpcyB0aGUgUGVhcnNvbiBDb3JyZWxhdGlvbiBDb2VmZmljaWVudC4gCgoqIFRoZSBmYWN0IHRoYXQgdGhpcyB2YWx1ZSBpcyBuZWdhdGl2ZSAoYmV0d2VlbiAtMSBhbmQgMCkgdGVsbHMgdXMgdGhhdCB4IGFuZCB5IHRlbmQgdG8gdmFyeSBpbiBvcHBvc2l0ZSBkaXJlY3Rpb25zLgoKKiBUaGUgbnVtZXJpYyB2YWx1ZSAoMC4xNDA2NzAzKSB0ZWxscyB1cyBzb21ldGhpbmcgYWJvdXQgdGhlIHN0cmVuZ3RoIG9mIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB4IGFuZCB5LiBJbiB0aGlzIGNhc2UsIHRoZSByZWxhdGlvbnNoaXAgaXMgbm90IHN0cm9uZyAtLSBleGFjdGx5IHdoYXQgd2UgZXhwZWN0ZWQuIAoKICAtIFlvdSB3aWxsIHNvbWV0aW1lcyBoZWFyIHJ1bGVzIG9mIHRodW1iIGZvciBpbnRlcnByZXRpbmcgdGhlIHN0cmVuZ3RoIG9mICRyJCBzdWNoIGFzW0BGaWVsZDIwMTMtem9dOiAKICAKICAgICAgLSDCsTAuMSA9IFdlYWsgY29ycmVsYXRpb24KICAgICAgCiAgICAgIC0gwrEwLjMgPSBNZWRpdW0gY29ycmVsYXRpb24KICAgICAgCiAgICAgIC0gwrEwLjUgPSBTdHJvbmcgY29ycmVsYXRpb24KICAgICAgCiAgLSBSdWxlcyBvZiB0aHVtYiBsaWtlIHRoaXMgYXJlIHVzZWZ1bCBhcyB5b3UgYXJlIGxlYXJuaW5nOyBob3dldmVyLCB5b3Ugd2FudCB0byBtYWtlIHN1cmUgeW91IGRvbid0IGJlY29tZSBvdmVybHkgcmVsaWFudCBvbiB0aGVtLiBBcyB5b3UgZ2V0IG1vcmUgZXhwZXJpZW5jZSwgeW91IHdpbGwgd2FudCB0byBzdGFydCBpbnRlcnByZXRpbmcgZWZmZWN0IHNpemVzIGluIHRoZSBjb250ZXh0IG9mIHlvdXIgZGF0YSBhbmQgdGhlIHNwZWNpZmljIHJlc2VhcmNoIHF1ZXN0aW9uIGF0IGhhbmQuCgoqIFRoZSBwLXZhbHVlICgwLjU1NDIpIHRlbGxzIHVzIHRoYXQgd2UnZCBiZSBwcmV0dHkgbGlrZWx5IHRvIGdldCB0aGUgcmVzdWx0IHdlIGdvdCBldmVuIGlmIHRoZXJlIHJlYWxseSB3ZXJlIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHggYW5kIHkgLS0gX19hc3N1bWluZyBhbGwgb3RoZXIgYXNzdW1wdGlvbnMgYXJlIHNhdGlzZmllZCBhbmQgdGhlIHNhbXBsZSB3YXMgY29sbGVjdGVkIHdpdGhvdXQgYmlhcy5fXwoKKiBUYWtlbiB0b2dldGhlciwgdGhlIHdlYWsgbmVnYXRpdmUgY29ycmVsYXRpb24gYW5kIHAtdmFsdWUgdGVsbCB1cyB0aGF0IHRoZXJlIGlzIG5vdCBtdWNoIC0tIGlmIGFueSAtLSByZWxhdGlvbnNoaXAgYmV0d2VlbiB4IGFuZCB5LiBBbm90aGVyIHdheSB0byBzYXkgdGhlIHNhbWUgdGhpbmcgaXMsICJ4IGFuZCB5IGFyZSBzdGF0aXN0aWNhbGx5IGluZGVwZW5kZW50LiIKCiMgQ29ycmVsYXRpb24gaW50dWl0aW9uCgpUbyBmdXJ0aGVyIGJvbHN0ZXIgb3VyIGludHVpdGlvbiBhYm91dCB0aGVzZSByZWxhdGlvbnNoaXBzLCBsZXTigJlzIGxvb2sgYXQgYSBmZXcgcG9zaXRpdmVseSBhbmQgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHZhcmlhYmxlcy4KCmBgYHtyfQojIFBvc2l0aXZlbHkgY29ycmVsYXRlZCBkYXRhCnRpYmJsZSgKICB4ID0gMToxMCwKICB5ID0gMTAwOjEwOSwKICByID0gY29yKHgsIHkpCikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoYWVzKHgsIHkpKSArCiAgICBnZW9tX3RleHQoYWVzKHggPSAyLjUsIHkgPSAxMDcuNSwgbGFiZWwgPSBwYXN0ZSgiciA9ICIsIHIpKSwgY29sID0gImJsdWUiKSArCiAgICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpBYm92ZSwgd2UgY3JlYXRlZCBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgZGF0YS4gSW4gZmFjdCwgdGhpcyBkYXRhIGlzIHBlcmZlY3RseSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQuIFRoYXQgaXMsIGV2ZXJ5IHRpbWUgdGhlIHZhbHVlIG9mIHggaW5jcmVhc2VzLCB0aGUgdmFsdWUgb2YgeSBpbmNyZWFzZXMgYnkgYSBwcm9wb3J0aW9uYWwgYW1vdW50LiBOb3csIGluc3RlYWQgb2YgYmVpbmcgcmFuZG9tbHkgc2NhdHRlcmVkIGFyb3VuZCB0aGUgcGxvdCBhcmVhLCB0aGUgZG90cyBsaW5lIHVwIGluIGEgcGVyZmVjdCwgdXB3YXJkLXNsb3BpbmcsIGRpYWdvbmFsIGxpbmUuIEkgYWxzbywgd2VudCBhaGVhZCBhbmQgYWRkZWQgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGRpcmVjdGx5IHRvIHRoZSBwbG90LiBBcyB5b3UgY2FuIHNlZSwgaXQgaXMgZXhhY3RseSAxLiBUaGlzIGlzIHdoYXQgeW91IHNob3VsZCBleHBlY3QgZnJvbSBwZXJmZWN0bHkgcG9zaXRpdmVseSBjb3JyZWxhdGVkIGRhdGEuCgpIb3cgYWJvdXQgdGhpcyBuZXh0IGRhdGEgc2V0PyBOb3csIGV2ZXJ5IHRpbWUgeCBkZWNyZWFzZXMgYnkgb25lLCB5IGRlY3JlYXNlcyBieSBvbmUuIElzIHRoaXMgcG9zaXRpdmVseSBvciBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQgZGF0YT8KCmBgYHtyfQpkZiA8LSB0aWJibGUoCiAgeCA9IDE6LTgsCiAgeSA9IDEwMDo5MQopCmRmCmBgYAoKYGBge3J9CmRmICU+JSAKICBtdXRhdGUociA9IGNvcih4LCB5KSkgJT4lIAogIGdncGxvdCgpICsKICAgICAgZ2VvbV9wb2ludChhZXMoeCwgeSkpICsKICAgICAgZ2VvbV90ZXh0KGFlcyh4ID0gLTYsIHkgPSA5OCwgbGFiZWwgPSBwYXN0ZSgiciA9ICIsIHIpKSwgY29sID0gImJsdWUiKSArCiAgICAgIHRoZW1lX2NsYXNzaWMoKQpgYGAKClRoaXMgaXMgc3RpbGwgcGVyZmVjdGx5IHBvc2l0aXZlbHkgY29ycmVsYXRlZCBkYXRhLiBUaGUgdmFsdWVzIGZvciB4IGFuZCB5IGFyZSBzdGlsbCBjaGFuZ2luZyBpbiB0aGUgc2FtZSBkaXJlY3Rpb24gcHJvcG9ydGlvbmF0bHkuIFRoZSBmYWN0IHRoYXQgdGhlIGRpcmVjdGlvbiBpcyBvbmUgb2YgZGVjcmVhc2luZyB2YWx1ZSBtYWtlcyBubyBkaWZmZXJlbmNlLgoKT25lIGxhc3QgbWFkZSB1cCBleGFtcGxlIGhlcmUuIFRoaXMgdGltZSwgYXMgeCBpbmNyZWFzZXMgYnkgb25lLCB5IGRlY3JlYXNlcyBieSBvbmUuIExldOKAmXMgcGxvdCB0aGlzIGRhdGEgYW5kIGNhbGN1bGF0ZSB0aGUgUGVhcnNvbiBDb3JyZWxhdGlvbiBDb2VmZmljaWVudC4KCmBgYHtyfQp0aWJibGUoCiAgeCA9IDE6MTAsCiAgeSA9IDEwMDo5MSwKICByID0gY29yKHgsIHkpCikgJT4lIAogIGdncGxvdCgpICsKICAgIGdlb21fcG9pbnQoYWVzKHgsIHkpKSArCiAgICBnZW9tX3RleHQoYWVzKHggPSA3LjUsIHkgPSA5OCwgbGFiZWwgPSBwYXN0ZSgiciA9ICIsIHIpKSwgY29sID0gImJsdWUiKSArCiAgICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpUaGlzIGlzIHdoYXQgcGVyZmVjdGx5IG5lZ2F0aXZlbHkgY29ycmVsYXRlZCBkYXRhIGxvb2tzIGxpa2UuIFRoZSBkb3RzIGxpbmUgdXAgaW4gYSBwZXJmZWN0LCBkb3dud2FyZC1zbG9waW5nLCBkaWFnb25hbCBsaW5lLCBhbmQgd2hlbiB3ZSBjaGVjayB0aGUgdmFsdWUgb2YgcmhvLCB3ZSBzZWUgdGhhdCBpdCBpcyBleGFjdGx5IC0xLgoKT2YgY291cnNlLCBhcyB5b3UgbWF5IGhhdmUgc3VzcGVjdGVkLCBfX2luIHJlYWwgbGlmZSB0aGluZ3MgYXJlIG5ldmVyIHRoaXMgY3V0IGFuZCBkcnlfXy4gTm93IGxldOKAmXMgaW52ZXN0aWdhdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNvbnRpbnVvdXMgdmFyaWFibGVzIHVzaW5nIG1vcmUgcmVhbGlzdGljIGRhdGEuCgpJbiB0aGlzIGRlbW9uc3RyYXRpb24gSeKAmW0gdXNpbmcgZGF0YSBmcm9tIGEgY2xhc3Mgc3VydmV5IEkgYWN0dWFsbHkgY29uZHVjdGVkIGluIHRoZSBwYXN0OgoKYGBge3J9CmNsYXNzIDwtIHRpYmJsZSgKICBodF9pbiA9IGMoNzAsIDYzLCA2MiwgNjcsIDY3LCA1OCwgNjQsIDY5LCA2NSwgNjgsIDYzLCA2OCwgNjksIDY2LCA2NywgNjUsIAogICAgICAgICAgICA2NCwgNzUsIDY3LCA2MywgNjAsIDY3LCA2NCwgNzMsIDYyLCA2OSwgNjcsIDYyLCA2OCwgNjYsIDY2LCA2MiwgCiAgICAgICAgICAgIDY0LCA2OCwgTkEsIDY4LCA3MCwgNjgsIDY4LCA2NiwgNzEsIDYxLCA2MiwgNjQsIDY0LCA2MywgNjcsIDY2LCAKICAgICAgICAgICAgNjksIDc2LCBOQSwgNjMsIDY0LCA2NSwgNjUsIDcxLCA2NiwgNjUsIDY1LCA3MSwgNjQsIDcxLCA2MCwgNjIsIAogICAgICAgICAgICA2MSwgNjksIDY2LCBOQSksCiAgd3RfbGJzID0gYygyMTYsIDEwNiwgMTQ1LCAxOTUsIDE0MywgMTI1LCAxMzgsIDE0MCwgMTU4LCAxNjcsIDE0NSwgMjk3LCAxNDYsIAogICAgICAgICAgICAgMTI1LCAxMTEsIDEyNSwgMTMwLCAxODIsIDE3MCwgMTIxLCA5OCwgMTUwLCAxMzIsIDI1MCwgMTM3LCAxMjQsIAogICAgICAgICAgICAgMTg2LCAxNDgsIDEzNCwgMTU1LCAxMjIsIDE0MiwgMTEwLCAxMzIsIDE4OCwgMTc2LCAxODgsIDE2NiwgMTM2LCAKICAgICAgICAgICAgIDE0NywgMTc4LCAxMjUsIDEwMiwgMTQwLCAxMzksIDYwLCAxNDcsIDE0NywgMTQxLCAyMzIsIDE4NiwgMjEyLCAKICAgICAgICAgICAgIDExMCwgMTEwLCAxMTUsIDE1NCwgMTQwLCAxNTAsIDEzMCwgTkEsIDE3MSwgMTU2LCA5MiwgMTIyLCAxMDIsIAogICAgICAgICAgICAgMTYzLCAxNDEsIE5BKQopCmBgYAoKTmV4dCwgSeKAmW0gZ29pbmcgdG8gdXNlIGEgc2NhdHRlciBwbG90IHRvIGV4cGxvcmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGhlaWdodCBhbmQgd2VpZ2h0IGluIHRoaXMgZGF0YS4KCmBgYHtyfQpnZ3Bsb3QoY2xhc3MsIGFlcyhodF9pbiwgd3RfbGJzKSkgKwogIGdlb21faml0dGVyKCkgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKClF1aWNrbHksIHdoYXQgZG8geW91IHRoaW5rPyBXaWxsIGhlaWdodCBhbmQgd2VpZ2h0IGJlIHBvc2l0aXZlbHkgY29ycmVsYXRlZCwgbmVnYXRpdmVseSBjb3JyZWxhdGVkLCBvciBub3QgY29ycmVsYXRlZD8KCmBgYHtyfQpjb3IudGVzdChjbGFzcyRodF9pbiwgY2xhc3Mkd3RfbGJzKQpgYGAKClRoZSBkb3RzIGRvbuKAmXQgbGluZSB1cCBpbiBhIHBlcmZlY3RseSB1cHdhcmQg4oCTIG9yIGRvd253YXJkIOKAkyBzbG9wZS4gQnV0IHRoZSBnZW5lcmFsIHRyZW5kIGlzIHN0aWxsIGFuIHVwd2FyZCBzbG9wZS4gQWRkaXRpb25hbGx5LCB3ZSBjYW4gc2VlIHRoYXQgaGVpZ2h0IGFuZCB3ZWlnaHQgYXJlIHBvc2l0aXZlbHkgY29ycmVsYXRlZCBiZWNhdXNlIHRoZSB2YWx1ZSBvZiB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgaXMgYmV0d2VlbiAwIGFuZCBwb3NpdGl2ZSAxICgwLjU4OTA1NzYpLiBCeSBsb29raW5nIGF0IHRoZSBwLXZhbHVlICgzLjA1MWUtMDcpLCB3ZSBjYW4gYWxzbyBzZWUgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgZmluZGluZyBhIGNvcnJlbGF0aW9uIHZhbHVlIHRoaXMgbGFyZ2Ugb3IgbGFyZ2VyIGluIG91ciBzYW1wbGUgaWYgdGhlIHRydWUgdmFsdWUgb2YgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGluIHRoZSBwb3B1bGF0aW9uIGZyb20gd2hpY2ggb3VyIHNhbXBsZSB3YXMgZHJhd24gaXMgemVybywgaXMgdmVyeSBzbWFsbC4KClRoYXTigJlzIHF1aXRlIGEgbW91dGhmdWwsIHJpZ2h0PyBJbiBtb3JlIHJlbGF0YWJsZSB0ZXJtcyB5b3UgY2FuIGp1c3QgdGhpbmsgb2YgaXQgdGhpcyB3YXkuIEFjY29yZGluZyB0byBvdXIgZGF0YSwgYXMgaGVpZ2h0IGluY3JlYXNlIHdlaWdodCB0ZW5kcyB0byBpbmNyZWFzZSBhcyB3ZWxsLiBPdXIgcC12YWx1ZSBpbmRpY2F0ZXMgdGhhdCBpdOKAmXMgcHJldHR5IHVubGlrZWx5IHRoYXQgd2Ugd291bGQgZ2V0IHRoaXMgcmVzdWx0IGJ5IGlmIHRoZXJlIHdlcmUgdHJ1bHkgbm8gcmVsYXRpb25zaGlwIGluIHRoZSBwb3B1bGF0aW9uIHRoaXMgc2FtcGxlIHdhcyBkcmF3biBmcm9tIC0tIGFzc3VtaW5nIGl0J3MgYW4gdW5iaWFzZWQgc2FtcGxlLgoKX19RdWljayBkZXRvdXJfXzogVGhlIHAtdmFsdWUgYWJvdmUgaXMgd3JpdHRlbiBpbiBzY2llbnRpZmljIG5vdGF0aW9uLCB3aGljaCB5b3UgbWF5IG5vdCBoYXZlIHNlZW4gYmVmb3JlLiBJJ2xsIHF1aWNrbHkgc2hvdyB5b3UgaG93IHRvIGJhc2ljYWxseSBkaXNhYmxlIHNjaWVudGlmaWMgbm90YXRpb24gaW4gUi4KCmBgYHtyfQpvcHRpb25zKHNjaXBlbiA9IDk5OSkKY29yLnRlc3QoY2xhc3MkaHRfaW4sIGNsYXNzJHd0X2xicykKYGBgCgoqKkhlcmUncyB3aGF0IHdlIGRpZCBhYm92ZToqKgoKKiBXZSB1c2VkIHRoZSBSIGdsb2JhbCBvcHRpb24gYG9wdGlvbnMoc2NpcGVuID0gOTk5KWAgdG8gZGlzcGxheSBkZWNpbWFsIG51bWJlcnMgaW5zdGVhZCBvZiBzY2llbnRpZmljIG5vdGF0aW9uLiBCZWNhdXNlIHRoaXMgaXMgYSBnbG9iYWwgb3B0aW9uLCBpdCB3aWxsIHJlbWFpbiBpbiBlZmZlY3QgdW50aWwgeW91ciByZXN0YXJ0IHlvdXIgUiBzZXNzaW9uLiBJZiB5b3UgZG8gcmVzdGFydCB5b3VyIFIgc2Vzc2lvbiwgeW91IHdpbGwgaGF2ZSB0byBydW4gYG9wdGlvbnMoc2NpcGVuID0gOTk5KWAgYWdhaW4gdG8gZGlzYWJsZSBzY2llbnRpZmljIG5vdGF0aW9uLgoKRmluYWxseSwgd291bGRu4oCZdCBpdCBiZSBuaWNlIGlmIHdlIGNvdWxkIGRyYXcgYSBsaW5lIHRocm91Z2ggdGhpcyBncmFwaCB0aGF0IHNvcnQgb2YgcXVpY2tseSBzdW1tYXJpemVzIHRoaXMgcmVsYXRpb25zaGlwIChvciBsYWNrIHRoZXJlb2YpLiBXZWxsLCB0aGF0IGlzIGV4YWN0bHkgd2hhdCBhbiBPcmRpbmFyeSBMZWFzdCBTcXVhcmVzIChPTFMpIHJlZ3Jlc3Npb24gbGluZSBkb2VzLgoKVG8gYWRkIGEgcmVncmVzc2lvbiBsaW5lIHRvIG91ciBwbG90LCBhbGwgd2UgbmVlZCB0byBkbyBpcyBhZGQgYSBgZ2VvbV9zbW9vdGgoKWAgbGF5ZXIgdG8gb3VyIHNjYXR0ZXJwbG90IHdpdGggdGhlICJtZXRob2QiIGFyZ3VtZW50IHNldCB0byAibG0uIiBMZXTigJlzIGRvIHRoYXQgYmVsb3cgYW5kIHRha2UgYSBsb29rLgoKYGBge3J9CmdncGxvdChjbGFzcywgYWVzKGh0X2luLCB3dF9sYnMpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogIGdlb21faml0dGVyKCkgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKClRoZSBleGFjdCBjYWxjdWxhdGlvbiBmb3IgZGVyaXZpbmcgdGhpcyBsaW5lIGlzIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyBsZXNzb24uIEluIGdlbmVyYWwsIHRob3VnaCwgeW91IGNhbiB0aGluayBvZiBpdCBhcyBjdXR0aW5nIHRocm91Z2ggdGhlIG1pZGRsZSBvZiBhbGwgb2YgeW91ciBwb2ludHMsIGFuZCByZXByZXNlbnRpbmcgdGhlIGF2ZXJhZ2UgY2hhbmdlIGluIHRoZSB5IHZhbHVlIGdpdmVuIGEgb25lLXVuaXQgY2hhbmdlIGluIHRoZSB4IHZhbHVlLiBTbyBoZXJlLCB0aGUgdXB3YXJkIHNsb3BlIGluZGljYXRlcyB0aGF0LCBvbiBhdmVyYWdlLCBhcyBoZWlnaHQgKHRoZSB4IHZhbHVlKSBpbmNyZWFzZXMsIHNvIGRvZXMgd2VpZ2h0ICh0aGUgeSB2YWx1ZSkuIEFuZCB0aGF0IGlzIGNvbXBsZXRlbHkgY29uc2lzdGVudCB3aXRoIG91ciBwcmV2aW91cyBjb25jbHVzaW9ucyBhYm91dCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaGVpZ2h0IGFuZCB3ZWlnaHQuCgojIFJlZmVyZW5jZXM=