This topic is going to focus on developing the theory of correlations.
Correlation is a test to see if two variables change in a coordinate fashion. However, this does not imply they are functionally linked or causal in nature.
Consider the following data consisting of the the decade from 1999 - 2009 and recording the number of movies each year by the actor Nicolas Cage (source IMDB) and the number of people who accidentally died by falling into a swimming pool (source U.S. Centers for Disease Control).
df <- data.frame( Year = 1999:2009 )
df$`Nicolas Cage Movies` <- c( 2, 2, 2, 3, 1, 1, 2, 3, 4, 1, 4)
df$`Drowning Deaths in Pools` <- c( 109, 102, 102, 98, 85, 95, 96, 98, 123, 94, 102 )
df
If we look at these data by year, it does not look like there is much of a trend (at least temporally).
library( reshape2 )
Attaching package: 'reshape2'
The following object is masked from 'package:tidyr':
smiths
df %>%
melt( id = "Year" ) %>%
ggplot( aes( Year, value, color = variable) ) +
geom_line() +
geom_point()

However, if we look at the two variables together we see an entirely different thing.
df %>%
ggplot( aes(`Nicolas Cage Movies`, `Drowning Deaths in Pools` ) ) +
geom_point() +
stat_smooth( formula=y ~ x,
method='lm',
se=FALSE,
color = "red",
size = 0.5) +
geom_text( aes(x=1.5,
y=115,
label = paste( "Correlation = ", format( cor(df[,2],df[,3]), digits=3) ) ) )

And in fact, if we run the statistical test on these data.
cor.test( df$`Nicolas Cage Movies`, df$`Drowning Deaths in Pools`)
Pearson's product-moment correlation
data: df$`Nicolas Cage Movies` and df$`Drowning Deaths in Pools`
t = 2.6785, df = 9, p-value = 0.02527
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.1101273 0.9045101
sample estimates:
cor
0.6660043
We do in fact seee a significant (P = 0.025) relationship.
Now, do we think that because Nicolas Cage makes more movies people are dying at an increased rate? No. These are spurious correlations, though do prove a point about causation.
Some New Data
For this topic, I thought I would turn to a bit of a more digestible set of data—data describing beer styles! There is a new CSV data set on the GitHub site located at the following URL.
beer_url <- "https://raw.githubusercontent.com/dyerlab/ENVS-Lectures/master/data/Beer_Styles.csv"
beer <- read_csv( beer_url )
Parsed with column specification:
cols(
Styles = col_character(),
Yeast = col_character(),
ABV_Min = col_double(),
ABV_Max = col_double(),
IBU_Min = col_double(),
IBU_Max = col_double(),
SRM_Min = col_double(),
SRM_Max = col_double(),
OG_Min = col_double(),
OG_Max = col_double(),
FG_Min = col_double(),
FG_Max = col_double()
)
summary( beer )
Styles Yeast ABV_Min ABV_Max
Length:100 Length:100 Min. :2.400 Min. : 3.200
Class :character Class :character 1st Qu.:4.200 1st Qu.: 5.475
Mode :character Mode :character Median :4.600 Median : 6.000
Mean :4.947 Mean : 6.768
3rd Qu.:5.500 3rd Qu.: 8.000
Max. :9.000 Max. :14.000
IBU_Min IBU_Max SRM_Min SRM_Max
Min. : 0.00 Min. : 8.00 Min. : 2.00 Min. : 3.00
1st Qu.:15.00 1st Qu.: 25.00 1st Qu.: 3.50 1st Qu.: 7.00
Median :20.00 Median : 35.00 Median : 8.00 Median :17.00
Mean :21.97 Mean : 38.98 Mean : 9.82 Mean :17.76
3rd Qu.:25.00 3rd Qu.: 45.00 3rd Qu.:14.00 3rd Qu.:22.00
Max. :60.00 Max. :120.00 Max. :30.00 Max. :40.00
OG_Min OG_Max FG_Min FG_Max
Min. :1.026 Min. :1.032 Min. :0.998 Min. :1.006
1st Qu.:1.040 1st Qu.:1.052 1st Qu.:1.008 1st Qu.:1.012
Median :1.046 Median :1.060 Median :1.010 Median :1.015
Mean :1.049 Mean :1.065 Mean :1.009 Mean :1.016
3rd Qu.:1.056 3rd Qu.:1.075 3rd Qu.:1.010 3rd Qu.:1.018
Max. :1.080 Max. :1.130 Max. :1.020 Max. :1.040
The data consist of the following categories of data. For all but he first two columns of data, a range is given for the appropriate values for each style with Min
and Max
values.
- Styles - The official name of the beer style. Yes, there is an international standard that is officiated by the Beer Judge Certification Program.
- Yeast Type - The species of yeast most commonly used for fermenation, consists of top fermenting Ale yeasts and bottom fermenting Lager yeasts.
- ABV - The amount of alcohol in the finished beer as a percentage of the volume. This is a non-negative numerical value.
- IBU - The ‘International Bitterness Unit’ which roughly measures the amont of \(\alpha\)-acids (asymptotically) added to the beer by the hops. This is a non-negative numerical value, with higher values indicating more bitter beer, though human ability to taste increasingly bitter beer is asymptotic.
- SRM - The Standard Reference Method calibration measuring the color of the finished beer. This is a non-negative integer going from 1 - 40 (light straw color - dark opaque).
- OG - The amount of dissolved sugars in the wort (the pre-beer liquid prior to putting in yeast and the initiation of fermentation), relative to pure water. This is a measurement ‘relative’ to water, which is 1.0. Values less than 1.0 have lower liquid densities than pure water and those greater than 1.0 have more dissolved sugars than pure water.
- FG - The amount of dissolved sugars in the beer after fermentation has been completed. Same as above but the difference in OG and FG can tell us what the ABV should be. Hihger FG beers are more sweet and have more body than lower OG beers (which may appear to have a cleaner, drier, mouth feel—yes that is a real term as well).
As we talk about correlations, we will use these as examples.
Parameters & Estimates
In statistics, we have two kinds of entities, parameters and estimates, which are dualities of each other. The TRUE
mean of a set of data is referred to by \(\mu\) whereas the mean of the data we measured is referred to as \(\bar{x}\). The greek version is the idealized value for the parameter, something that we are striving to find the real estimate of. However, as a Frequentist, we can never actually get to that parameter (remember the actual population of data is infinite but we can only sample a small amount of it) and when we talk about the data associated with what we collect, we refer to it as a estimate and use normal variable names.
Parametric Assumptions
For much of the statistics we use, there are underlying assumptions about the form of the data that we shold look at.
Testing for Normality.
The data can be estimated by a normal density function, or at least can be transformed into data that is reasonably normal in distribution.
The normal distribution function is defined as:
\[
f(x) = \frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{1}{2}(\frac{x - \mu}{\sigma})}
\]
where \(\mu\) and \(\sigma\) are the true value of the underlying mean and standard deviation. This distribution is denoted as \(N(\mu,\sigma)\) and the differences in the mean value (\(\mu\)) and the variation measured by the standard deviation (\(\sigma\)) are shown below for \(N(0,1)\), \(N(0,5)\), and \(N(10,1)\).
N <- 1000
data.frame( Distribution = rep(c("N(0,1)","N(10,1)", "N(0,5)"), each=N ),
Data = c( rnorm(N,0,1),
rnorm(N,10,1),
rnorm(N,0,5) ) ) %>%
ggplot( aes( Data ) ) +
geom_histogram( alpha=0.75,
bins = 50) +
facet_grid(Distribution ~.)

There are a couple of ways to look at our data to see if they can be considered as normal. First, visually we can plot the theoretical (parameter) quantiles of the data against the sample quantiles using the qqnorm()
plot. What this does is sort the data by expectation and observation and plot them and if the data are normal, then they should roughly be in a straight line. The qqline()
function shows the expected line (n.b., this is another one of those things where you have to run the whole chunk to get both points and lines on the same graph if you are working in Markdown).
qqnorm( beer$ABV_Min )
qqline( beer$ABV_Min, col="red")

So, what we commonly see is most of the data falling along the line throughout the middle portion of the distribution and then deviating around the edges. What this does not do is give you a statistic to test to see if we can reject the hypothesis \(H_O: Data\;is\;normal\). For this, we can use the Shapiro-Wilkes Normality test which produces the statistic:
\[
W = \frac{\left(\sum_{i=1}^Na_iR_{x_i}\right)^2}{\sum_{i=1}^N(x_i - \bar{x})^2}
\]
where \(N\) is the number of samples, \(a_i\) is a standardizing coeeficient, \(x_i\) is the \(i^{th}\) value of \(x\), \(\bar{x}\) is the mean of the observed values, and \(R_{x_i}\) is the rank of the \(x_i^{th}\) observation.
shapiro.test( beer$ABV_Min )
Shapiro-Wilk normality test
data: beer$ABV_Min
W = 0.94595, p-value = 0.0004532
Rejection of the null hypothesis (e.g., a small p-value
from the test) indicates that the data are not to be considered as coming from a normal distribution. So, for the ABV_Min
data above, it appears that it is not actually normally distributed. So what do we do?
Transformations
If the data are not normal, we can look towards trying to see if we can transform it to a normally distributed variable. There are a lot of
Studentized Data - One way to standardize the data is to make it have a mean of 0.0 and a standard deviation of 1.0. To do this, we subtract the mean()
and divide by the sd()
.
x <- beer$ABV_Min
x.std <- (x - mean(x)) / sd( x )
There are times when this can be a nice way to compare the
Box Cox - In 1964, Box & Cox defined a family of transformations known as the Box/Cox. This family is defined by a single parameter, \(\lambda\), whose value may vary depending upon the data. The original data, \(x\), is then transformed using the following relationship
\[
\tilde{x} = \frac{x^\lambda - 1}{\lambda}
\]
As long as \(\lambda \ne 0\) (else we would be dividing by zero, which is not a good thing)!
One way to use this transformation is to look at a range of values for \(\lambda\) and determine if the transformation
test_boxcox <- function( x, lambdas = seq(-1.1, 1.1, by = 0.015) ) {
ret <- data.frame( Lambda = lambdas,
W = NA,
P = NA)
for( lambda in lambdas ) {
x.tilde <- (x^lambda - 1) / lambda
w <- shapiro.test( x.tilde )
ret$W[ ret$Lambda == lambda ] <- w$statistic
ret$P[ ret$Lambda == lambda ] <- w$p.value
}
return( ret )
}
vals <- test_boxcox( beer$ABV_Min )
vals %>%
ggplot( aes(Lambda, P) ) +
geom_line() +
ylab("P-Value")

So if you look at this plot, it shows the P-value of the Shapiro-Wilkes test across a range of values. Depending upon the level of rigor, this approaches the \(\alpha = 0.05\) value closest at:
vals[ which(vals$P == max( vals$P)),]
with \(\lambda = 0.115\) and a \(P = 0.044\).
Arc-Sine Square Root When dealing with fractions, it is common that they do not behave very well when they are very close to 0.0 or 1.0. One of the common transformations to use with these kinds of data is the arc-sin square root transformation. For us, the ABV columns in the data is a percentage (but listed in numerical form as percent not as fraction). So to transform it we can do the following.
abv <- beer$ABV_Min / 100.0
asin( sqrt( abv ) ) -> abv.1
shapiro.test( abv.1)
Shapiro-Wilk normality test
data: abv.1
W = 0.96746, p-value = 0.01418
Equal Variance
Another parametric assumption is the equality of variance across a range of the data. This means, for example, that the variance from one part of the experiment should not be different than the variance in samples from another portion of data. We will return to this when we evaluate regression models.
Independence of Data
The samples you collect, and the way that you design your experiments are most important to ensure that your data are individually independent. You need to think about this very carefully as you design your experiments.
Correlation Tests
The following types of correlation statistics are a sample of the most common approaches.
Parametric Test: Pearson Product Moment Correlations
By far, the most common correlation statistic we see is the Pearson Product Moment Correlation, denoted as \(\rho\). For two variables, \(x\) and \(y\), the correlation parameter is estimated as:
\[
\rho = \frac{\sum_{i=1}^N(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_{i=1}^N(x_i - \bar{x})^2}\sqrt{\sum_{i=1}^N(y_i - \bar{y})^2}}
\]
The values of these data fall wihtin the range of: \(-1 \le \rho \le +1\) with negative values indicating that when one variable goes up, the other goes down. Positive values of a correlation indicate that both variable change systematically in the same direction (e.g., both up or both down).
Here are some examples of the distribution of two variables and their associated correlation coefficient.
Significance testing for a correlation such as \(\rho\) determine the extent to which we thing the value of is deviant from zero. The Null Hypothesis is \(H_O: \rho \ne 0\) and can be evaluated using the Student’s t.test. With large enough sample sizes, it can be approximated by:
\[
t = r \frac{N-2}{1-r^2}
\]
However, we should probably rely upon R
to look up the critical values of the statistic.
The default value for cor.test()
is the Pearson. Here is an example of its use and the output that we’ve seen before.
cor.test( beer$OG_Max, beer$FG_Max ) -> OG.FG.pearson
OG.FG.pearson
Pearson's product-moment correlation
data: beer$OG_Max and beer$FG_Max
t = 15.168, df = 98, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.7671910 0.8878064
sample estimates:
cor
0.8374184
Of particular note are the components associated with the results object that allows you to gain access to specifics for any analysis.
names( OG.FG.pearson )
[1] "statistic" "parameter" "p.value" "estimate" "null.value"
[6] "alternative" "method" "data.name" "conf.int"
statistic
parameter
p.value
estimate
null.value
alternative
method
data.name
conf.int
Non-Parametric Test: Spearman’s Rho
Another way to de a correlation test that does not rely upon parametric assumptions is to use non-parametric approaches. Most non-parametric tests are based upon ranks of the data rather than the assumption of normality of the data that is necessary for the Pearson Product Moment statistic. One of the constraints for non-parametric statistics is that they are often evaluated for probability based upon permutations.
The form of the estimator for this is almost identical to that of the Pearson statistic except that instead of the raw data, we are replacing values with the ranks of each value instead. In doing so, there is a loss of the breadth of the raw data since we are just using ranks, and if the underlying data are poorly behaved because of outliers or other issues, this takes care of it.
\[
\rho_{Spearman} = \frac{ \sum_{i=1}^N(R_{x_i} - \bar{R_{x}})(R_{y_i} - \bar{R_{y}})}{\sqrt{\sum_{i=1}^N(R_{x_i} - \bar{R_{x}})^2}\sqrt{\sum_{i=1}^N(R_{y_i} - \bar{R_{y}})^2}}
\]
With the same data, it does provide potentially different estimates of the amount of correlation between the variables.
OG.FG.spearman <- cor.test( beer$OG_Max, beer$FG_Max,
method = "spearman" )
Warning in cor.test.default(beer$OG_Max, beer$FG_Max, method = "spearman"):
Cannot compute exact p-value with ties
OG.FG.spearman
Spearman's rank correlation rho
data: beer$OG_Max and beer$FG_Max
S = 39257, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
rho
0.7644328
Permutation Testing for Significance
In both of the previous methods, we used specific approaches to evaluate the significance of the statistic. For Pearson, we approximated using the \(t\). For the Spearman test with small numbers of samples, an approximation of the \(t\) test is used, based upon counting ranks and the number of ways we can get different combinations of ranks. For larger sample size tests using Spearman, an approximation using the \(t\) test can be used.
Another way of doing this is based upon permutation and this approach can be applied to a wide array of questions. For correlation’s, if we consider the null hypothesis \(H_O: \rho = 0\) we can make a few inferences. If this hypothesis is true then we are, essentially, saying that the current relationship between \(x_i\) and \(y_i\) has no intrinsic relationship as there is no correlation. This is, by default, what the null hypothesis says.
If that is true, however, that means that any permutation of one of the variables, say \(y\), should produce a correlation statistic that is just as large as any other permutation of the data. This is key.
So, if we assume the \(H_O\) is true then we should be able to shuffle one of the data and estimate a correlation statistic a large number of times. We can then create a permuted distribution of values for the correlation, Assuming the NULL Hypothesis is true. To this distribution, we can evaluate the magnitude of the original correlation. Here is an example using the data from above.
x <- beer$OG_Max
y <- beer$FG_Max
df <- data.frame( Estimate = factor( c( "Original",
rep("Permuted", 999))),
rho = c( cor.test( x, y )$estimate,
rep(NA, 999)) )
summary( df )
Estimate rho
Original: 1 Min. :0.8374
Permuted:999 1st Qu.:0.8374
Median :0.8374
Mean :0.8374
3rd Qu.:0.8374
Max. :0.8374
NA's :999
Now, we can go through the 999 NA
values we put into that data frame and:
1. Permute one of the variables 2. Run the analysis
3. Store the statistic.
for( i in 2:1000) {
yhat <- sample( y, # this shuffles the data in y
size = length(y),
replace = FALSE)
model <- cor.test( x, yhat )
df$rho[i] <- model$estimate
}
Now we can look at the distribution of permuted values and the original one and see the relationship. If:
- The observed value is within the body of the permuted values, then it is not too rare—given \(H_O\), or
- If the observed value is way outside those permuted values, then it appears to be somewhat rare.
ggplot( df ) +
geom_histogram( aes(rho, fill=Estimate ) )
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

If you look at the graph above, you see that the original value is way bigger than the values that would be found if and only if
\(H_O\) were true. This suggests that the correlation is not zero and in fact it is the largest observation of the 1000 observations (a P estimate of \(\frac{1}{1000}\)…).
LS0tCnRpdGxlOiAiQ29ycmVsYXRpb24gTW9kZWxzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSggdGlkeXZlcnNlICkKdGhlbWVfc2V0KCB0aGVtZV9taW5pbWFsKCBiYXNlX3NpemUgPSAxNiApICkKYGBgCgoKVGhpcyB0b3BpYyBpcyBnb2luZyB0byBmb2N1cyBvbiBkZXZlbG9waW5nIHRoZSB0aGVvcnkgb2YgY29ycmVsYXRpb25zLiAgCgoKPiBDb3JyZWxhdGlvbiBpcyBhIHRlc3QgdG8gc2VlIGlmIHR3byB2YXJpYWJsZXMgY2hhbmdlIGluIGEgY29vcmRpbmF0ZSBmYXNoaW9uLiAgSG93ZXZlciwgdGhpcyBkb2VzIG5vdCBpbXBseSB0aGV5IGFyZSBmdW5jdGlvbmFsbHkgbGlua2VkIG9yIGNhdXNhbCBpbiBuYXR1cmUuCgoKQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBkYXRhIGNvbnNpc3Rpbmcgb2YgdGhlIHRoZSBkZWNhZGUgZnJvbSAxOTk5IC0gMjAwOSBhbmQgcmVjb3JkaW5nIHRoZSBudW1iZXIgb2YgbW92aWVzIGVhY2ggeWVhciBieSB0aGUgYWN0b3IgTmljb2xhcyBDYWdlIChzb3VyY2UgSU1EQikgYW5kIHRoZSBudW1iZXIgb2YgcGVvcGxlIHdobyBhY2NpZGVudGFsbHkgZGllZCBieSBmYWxsaW5nIGludG8gYSBzd2ltbWluZyBwb29sIChzb3VyY2UgVS5TLiBDZW50ZXJzIGZvciBEaXNlYXNlIENvbnRyb2wpLgoKYGBge3J9CmRmIDwtIGRhdGEuZnJhbWUoIFllYXIgPSAxOTk5OjIwMDkgKQpkZiRgTmljb2xhcyBDYWdlIE1vdmllc2AgPC0gYyggMiwgMiwgMiwgMywgMSwgMSwgMiwgMywgNCwgMSwgNCkKZGYkYERyb3duaW5nIERlYXRocyBpbiBQb29sc2AgPC0gYyggMTA5LCAxMDIsIDEwMiwgOTgsIDg1LCA5NSwgOTYsIDk4LCAxMjMsIDk0LCAxMDIgKSAKZGYKYGBgCgpJZiB3ZSBsb29rIGF0IHRoZXNlIGRhdGEgYnkgeWVhciwgaXQgZG9lcyBub3QgbG9vayBsaWtlIHRoZXJlIGlzIG11Y2ggb2YgYSB0cmVuZCAoYXQgbGVhc3QgdGVtcG9yYWxseSkuCgpgYGB7cn0KbGlicmFyeSggcmVzaGFwZTIgKQpkZiAlPiUKICBtZWx0KCBpZCA9ICJZZWFyIiApICU+JQogIGdncGxvdCggYWVzKCBZZWFyLCB2YWx1ZSwgY29sb3IgPSB2YXJpYWJsZSkgKSArIAogIGdlb21fbGluZSgpICArCiAgZ2VvbV9wb2ludCgpICAKYGBgCgpIb3dldmVyLCBpZiB3ZSBsb29rIGF0IHRoZSB0d28gdmFyaWFibGVzIHRvZ2V0aGVyIHdlIHNlZSBhbiBlbnRpcmVseSBkaWZmZXJlbnQgdGhpbmcuCgpgYGB7cn0KZGYgJT4lCiAgZ2dwbG90KCBhZXMoYE5pY29sYXMgQ2FnZSBNb3ZpZXNgLCBgRHJvd25pbmcgRGVhdGhzIGluIFBvb2xzYCApICkgKwogIGdlb21fcG9pbnQoKSArIAogIHN0YXRfc21vb3RoKCBmb3JtdWxhPXkgfiB4LAogICAgICAgICAgICAgICBtZXRob2Q9J2xtJywKICAgICAgICAgICAgICAgc2U9RkFMU0UsCiAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgICAgIHNpemUgPSAwLjUpICsKICBnZW9tX3RleHQoIGFlcyh4PTEuNSwKICAgICAgICAgICAgICAgICB5PTExNSwKICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCAiQ29ycmVsYXRpb24gPSAiLCBmb3JtYXQoIGNvcihkZlssMl0sZGZbLDNdKSwgZGlnaXRzPTMpICkgKSApCmBgYAoKQW5kIGluIGZhY3QsIGlmIHdlIHJ1biB0aGUgc3RhdGlzdGljYWwgdGVzdCBvbiB0aGVzZSBkYXRhLgoKYGBge3J9CmNvci50ZXN0KCBkZiRgTmljb2xhcyBDYWdlIE1vdmllc2AsIGRmJGBEcm93bmluZyBEZWF0aHMgaW4gUG9vbHNgKQpgYGAKCldlIGRvIGluIGZhY3Qgc2VlZSBhIHNpZ25pZmljYW50IChQID0gMC4wMjUpIHJlbGF0aW9uc2hpcC4gIAoKTm93LCBkbyB3ZSB0aGluayB0aGF0IGJlY2F1c2UgTmljb2xhcyBDYWdlIG1ha2VzIG1vcmUgbW92aWVzIHBlb3BsZSBhcmUgZHlpbmcgYXQgYW4gaW5jcmVhc2VkIHJhdGU/ICBOby4gIFRoZXNlIGFyZSBzcHVyaW91cyBjb3JyZWxhdGlvbnMsIHRob3VnaCBkbyBwcm92ZSBhIHBvaW50IGFib3V0IGNhdXNhdGlvbi4KCiMjIFNvbWUgTmV3IERhdGEKCkZvciB0aGlzIHRvcGljLCBJIHRob3VnaHQgSSB3b3VsZCB0dXJuIHRvIGEgYml0IG9mIGEgbW9yZSBkaWdlc3RpYmxlIHNldCBvZiBkYXRh4oCUZGF0YSBkZXNjcmliaW5nIGJlZXIgc3R5bGVzISBUaGVyZSBpcyBhIG5ldyBDU1YgZGF0YSBzZXQgb24gdGhlIEdpdEh1YiBzaXRlIGxvY2F0ZWQgYXQgdGhlIGZvbGxvd2luZyBVUkwuCgpgYGB7cn0KYmVlcl91cmwgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9keWVybGFiL0VOVlMtTGVjdHVyZXMvbWFzdGVyL2RhdGEvQmVlcl9TdHlsZXMuY3N2IgpiZWVyIDwtIHJlYWRfY3N2KCBiZWVyX3VybCApCnN1bW1hcnkoIGJlZXIgKQpgYGAKCgpUaGUgZGF0YSBjb25zaXN0IG9mIHRoZSBmb2xsb3dpbmcgY2F0ZWdvcmllcyBvZiBkYXRhLiAgRm9yIGFsbCBidXQgaGUgZmlyc3QgdHdvIGNvbHVtbnMgb2YgZGF0YSwgYSByYW5nZSBpcyBnaXZlbiBmb3IgdGhlIGFwcHJvcHJpYXRlIHZhbHVlcyBmb3IgZWFjaCBzdHlsZSB3aXRoIGBNaW5gIGFuZCBgTWF4YCB2YWx1ZXMuCgotICpTdHlsZXMqIC0gVGhlIG9mZmljaWFsIG5hbWUgb2YgdGhlIGJlZXIgc3R5bGUuICBZZXMsIHRoZXJlIGlzIGFuIGludGVybmF0aW9uYWwgc3RhbmRhcmQgdGhhdCBpcyBvZmZpY2lhdGVkIGJ5IHRoZSBbQmVlciBKdWRnZSBDZXJ0aWZpY2F0aW9uIFByb2dyYW1dKGh0dHBzOi8vYmpjcC5vcmcvKS4gCi0gKlllYXN0IFR5cGUqIC0gVGhlIHNwZWNpZXMgb2YgeWVhc3QgbW9zdCBjb21tb25seSB1c2VkIGZvciBmZXJtZW5hdGlvbiwgY29uc2lzdHMgb2YgdG9wIGZlcm1lbnRpbmcgQWxlIHllYXN0cyBhbmQgYm90dG9tIGZlcm1lbnRpbmcgTGFnZXIgeWVhc3RzLiAgCi0gKkFCViogLSBUaGUgYW1vdW50IG9mIGFsY29ob2wgaW4gdGhlIGZpbmlzaGVkIGJlZXIgYXMgYSBwZXJjZW50YWdlIG9mIHRoZSB2b2x1bWUuICBUaGlzIGlzIGEgbm9uLW5lZ2F0aXZlIG51bWVyaWNhbCB2YWx1ZS4KLSAqSUJVKiAtIFRoZSAnSW50ZXJuYXRpb25hbCBCaXR0ZXJuZXNzIFVuaXQnIHdoaWNoIHJvdWdobHkgbWVhc3VyZXMgdGhlIGFtb250IG9mICRcYWxwaGEkLWFjaWRzIChhc3ltcHRvdGljYWxseSkgYWRkZWQgdG8gdGhlIGJlZXIgYnkgdGhlIGhvcHMuICBUaGlzIGlzIGEgbm9uLW5lZ2F0aXZlIG51bWVyaWNhbCB2YWx1ZSwgd2l0aCBoaWdoZXIgdmFsdWVzIGluZGljYXRpbmcgbW9yZSBiaXR0ZXIgYmVlciwgdGhvdWdoIGh1bWFuIGFiaWxpdHkgdG8gdGFzdGUgaW5jcmVhc2luZ2x5IGJpdHRlciBiZWVyIGlzIGFzeW1wdG90aWMuCi0gKlNSTSogLSBUaGUgU3RhbmRhcmQgUmVmZXJlbmNlIE1ldGhvZCBjYWxpYnJhdGlvbiBtZWFzdXJpbmcgdGhlIGNvbG9yIG9mIHRoZSBmaW5pc2hlZCBiZWVyLiAgVGhpcyBpcyBhIG5vbi1uZWdhdGl2ZSBpbnRlZ2VyIGdvaW5nIGZyb20gMSAtIDQwIChsaWdodCBzdHJhdyBjb2xvciAtIGRhcmsgb3BhcXVlKS4KLSAqT0cqIC0gVGhlIGFtb3VudCBvZiBkaXNzb2x2ZWQgc3VnYXJzIGluIHRoZSB3b3J0ICh0aGUgcHJlLWJlZXIgbGlxdWlkIHByaW9yIHRvIHB1dHRpbmcgaW4geWVhc3QgYW5kIHRoZSBpbml0aWF0aW9uIG9mIGZlcm1lbnRhdGlvbiksIHJlbGF0aXZlIHRvIHB1cmUgd2F0ZXIuICBUaGlzIGlzIGEgbWVhc3VyZW1lbnQgJ3JlbGF0aXZlJyB0byB3YXRlciwgd2hpY2ggaXMgMS4wLiAgVmFsdWVzIGxlc3MgdGhhbiAxLjAgaGF2ZSBsb3dlciBsaXF1aWQgZGVuc2l0aWVzIHRoYW4gcHVyZSB3YXRlciBhbmQgdGhvc2UgZ3JlYXRlciB0aGFuIDEuMCBoYXZlIG1vcmUgZGlzc29sdmVkIHN1Z2FycyB0aGFuIHB1cmUgd2F0ZXIuCi0gKkZHKiAtIFRoZSBhbW91bnQgb2YgZGlzc29sdmVkIHN1Z2FycyBpbiB0aGUgYmVlciBhZnRlciBmZXJtZW50YXRpb24gaGFzIGJlZW4gY29tcGxldGVkLiAgU2FtZSBhcyBhYm92ZSBidXQgdGhlIGRpZmZlcmVuY2UgaW4gKk9HKiBhbmQgKkZHKiBjYW4gdGVsbCB1cyB3aGF0IHRoZSAqQUJWKiBzaG91bGQgYmUuICBIaWhnZXIgKkZHKiBiZWVycyBhcmUgbW9yZSBzd2VldCBhbmQgaGF2ZSBtb3JlIGJvZHkgdGhhbiBsb3dlciAqT0cqIGJlZXJzICh3aGljaCBtYXkgYXBwZWFyIHRvIGhhdmUgYSBjbGVhbmVyLCBkcmllciwgbW91dGggZmVlbOKAlHllcyB0aGF0IGlzIGEgcmVhbCB0ZXJtIGFzIHdlbGwpLgoKQXMgd2UgdGFsayBhYm91dCBjb3JyZWxhdGlvbnMsIHdlIHdpbGwgdXNlIHRoZXNlIGFzIGV4YW1wbGVzLgoKCgoKIyMgUGFyYW1ldGVycyAmIEVzdGltYXRlcwoKSW4gc3RhdGlzdGljcywgd2UgaGF2ZSB0d28ga2luZHMgb2YgZW50aXRpZXMsIHBhcmFtZXRlcnMgYW5kIGVzdGltYXRlcywgd2hpY2ggYXJlIGR1YWxpdGllcyBvZiBlYWNoIG90aGVyLiAgVGhlIGBUUlVFYCBtZWFuIG9mIGEgc2V0IG9mIGRhdGEgaXMgcmVmZXJyZWQgdG8gYnkgJFxtdSQgd2hlcmVhcyB0aGUgbWVhbiBvZiB0aGUgZGF0YSB3ZSBtZWFzdXJlZCBpcyByZWZlcnJlZCB0byBhcyAkXGJhcnt4fSQuICBUaGUgZ3JlZWsgdmVyc2lvbiBpcyB0aGUgKmlkZWFsaXplZCogdmFsdWUgZm9yIHRoZSBwYXJhbWV0ZXIsIHNvbWV0aGluZyB0aGF0IHdlIGFyZSBzdHJpdmluZyB0byBmaW5kIHRoZSByZWFsIGVzdGltYXRlIG9mLiAgSG93ZXZlciwgYXMgYSBGcmVxdWVudGlzdCwgd2UgY2FuIG5ldmVyIGFjdHVhbGx5IGdldCB0byB0aGF0IHBhcmFtZXRlciAocmVtZW1iZXIgdGhlIGFjdHVhbCBwb3B1bGF0aW9uIG9mIGRhdGEgaXMgaW5maW5pdGUgYnV0IHdlIGNhbiBvbmx5IHNhbXBsZSBhIHNtYWxsIGFtb3VudCBvZiBpdCkgYW5kIHdoZW4gd2UgdGFsayBhYm91dCB0aGUgZGF0YSBhc3NvY2lhdGVkIHdpdGggd2hhdCB3ZSBjb2xsZWN0LCB3ZSByZWZlciB0byBpdCBhcyBhIGVzdGltYXRlIGFuZCB1c2Ugbm9ybWFsIHZhcmlhYmxlIG5hbWVzLgoKCiMjIFBhcmFtZXRyaWMgQXNzdW1wdGlvbnMKCkZvciBtdWNoIG9mIHRoZSBzdGF0aXN0aWNzIHdlIHVzZSwgdGhlcmUgYXJlIHVuZGVybHlpbmcgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIGZvcm0gb2YgdGhlIGRhdGEgdGhhdCB3ZSBzaG9sZCBsb29rIGF0LgoKIyMjIFRlc3RpbmcgZm9yIE5vcm1hbGl0eS4KCj4gVGhlIGRhdGEgY2FuIGJlIGVzdGltYXRlZCBieSBhIG5vcm1hbCBkZW5zaXR5IGZ1bmN0aW9uLCBvciBhdCBsZWFzdCBjYW4gYmUgdHJhbnNmb3JtZWQgaW50byBkYXRhIHRoYXQgaXMgcmVhc29uYWJseSBub3JtYWwgaW4gZGlzdHJpYnV0aW9uLgoKVGhlIG5vcm1hbCBkaXN0cmlidXRpb24gZnVuY3Rpb24gaXMgZGVmaW5lZCBhczoKClxbCmYoeCkgPSBcZnJhY3sxfXtcc2lnbWFcc3FydHsyXHBpfX1lXnstXGZyYWN7MX17Mn0oXGZyYWN7eCAtIFxtdX17XHNpZ21hfSl9ClxdCgp3aGVyZSAkXG11JCBhbmQgJFxzaWdtYSQgYXJlIHRoZSB0cnVlIHZhbHVlIG9mIHRoZSB1bmRlcmx5aW5nIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbi4gIFRoaXMgZGlzdHJpYnV0aW9uIGlzIGRlbm90ZWQgYXMgJE4oXG11LFxzaWdtYSkkIGFuZCB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIG1lYW4gdmFsdWUgKCRcbXUkKSBhbmQgdGhlIHZhcmlhdGlvbiBtZWFzdXJlZCBieSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uICgkXHNpZ21hJCkgYXJlIHNob3duIGJlbG93IGZvciAkTigwLDEpJCwgJE4oMCw1KSQsIGFuZCAkTigxMCwxKSQuCgpgYGB7cn0KTiA8LSAxMDAwCmRhdGEuZnJhbWUoIERpc3RyaWJ1dGlvbiA9IHJlcChjKCJOKDAsMSkiLCJOKDEwLDEpIiwgIk4oMCw1KSIpLCBlYWNoPU4gKSwKICAgICAgICAgICAgRGF0YSA9IGMoIHJub3JtKE4sMCwxKSwKICAgICAgICAgICAgICAgICAgICAgIHJub3JtKE4sMTAsMSksCiAgICAgICAgICAgICAgICAgICAgICBybm9ybShOLDAsNSkgKSApICU+JQogIGdncGxvdCggYWVzKCBEYXRhICkgKSArIAogIGdlb21faGlzdG9ncmFtKCBhbHBoYT0wLjc1LCAKICAgICAgICAgICAgICAgICAgYmlucyA9IDUwKSArIAogIGZhY2V0X2dyaWQoRGlzdHJpYnV0aW9uIH4uKQoKYGBgCgpUaGVyZSBhcmUgYSBjb3VwbGUgb2Ygd2F5cyB0byBsb29rIGF0IG91ciBkYXRhIHRvIHNlZSBpZiB0aGV5IGNhbiBiZSBjb25zaWRlcmVkIGFzIG5vcm1hbC4gIEZpcnN0LCB2aXN1YWxseSB3ZSBjYW4gcGxvdCB0aGUgdGhlb3JldGljYWwgKHBhcmFtZXRlcikgcXVhbnRpbGVzIG9mIHRoZSBkYXRhIGFnYWluc3QgdGhlIHNhbXBsZSBxdWFudGlsZXMgdXNpbmcgdGhlIGBxcW5vcm0oKWAgcGxvdC4gIFdoYXQgdGhpcyBkb2VzIGlzIHNvcnQgdGhlIGRhdGEgYnkgZXhwZWN0YXRpb24gYW5kIG9ic2VydmF0aW9uIGFuZCBwbG90IHRoZW0gYW5kIGlmIHRoZSBkYXRhIGFyZSBub3JtYWwsIHRoZW4gdGhleSBzaG91bGQgcm91Z2hseSBiZSBpbiBhIHN0cmFpZ2h0IGxpbmUuICBUaGUgYHFxbGluZSgpYCBmdW5jdGlvbiBzaG93cyB0aGUgZXhwZWN0ZWQgbGluZSAobi5iLiwgdGhpcyBpcyBhbm90aGVyIG9uZSBvZiB0aG9zZSB0aGluZ3Mgd2hlcmUgeW91IGhhdmUgdG8gcnVuIHRoZSB3aG9sZSBjaHVuayB0byBnZXQgYm90aCBwb2ludHMgYW5kIGxpbmVzIG9uIHRoZSBzYW1lIGdyYXBoIGlmIHlvdSBhcmUgd29ya2luZyBpbiBNYXJrZG93bikuCgpgYGB7cn0KcXFub3JtKCBiZWVyJEFCVl9NaW4gKQpxcWxpbmUoIGJlZXIkQUJWX01pbiwgY29sPSJyZWQiKQpgYGAKClNvLCB3aGF0IHdlIGNvbW1vbmx5IHNlZSBpcyBtb3N0IG9mIHRoZSBkYXRhIGZhbGxpbmcgYWxvbmcgdGhlIGxpbmUgdGhyb3VnaG91dCB0aGUgbWlkZGxlIHBvcnRpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgdGhlbiBkZXZpYXRpbmcgYXJvdW5kIHRoZSBlZGdlcy4gIFdoYXQgdGhpcyBkb2VzIG5vdCBkbyBpcyBnaXZlIHlvdSBhIHN0YXRpc3RpYyB0byB0ZXN0IHRvIHNlZSBpZiB3ZSBjYW4gcmVqZWN0IHRoZSBoeXBvdGhlc2lzICRIX086IERhdGFcO2lzXDtub3JtYWwkLiAgRm9yIHRoaXMsIHdlIGNhbiB1c2UgdGhlIFNoYXBpcm8tV2lsa2VzIE5vcm1hbGl0eSB0ZXN0IHdoaWNoIHByb2R1Y2VzIHRoZSBzdGF0aXN0aWM6CgpcWwpXID0gXGZyYWN7XGxlZnQoXHN1bV97aT0xfV5OYV9pUl97eF9pfVxyaWdodCleMn17XHN1bV97aT0xfV5OKHhfaSAtIFxiYXJ7eH0pXjJ9ClxdCgp3aGVyZSAkTiQgaXMgdGhlIG51bWJlciBvZiBzYW1wbGVzLCAkYV9pJCBpcyBhIHN0YW5kYXJkaXppbmcgY29lZWZpY2llbnQsICR4X2kkIGlzIHRoZSAkaV57dGh9JCB2YWx1ZSBvZiAkeCQsICRcYmFye3h9JCBpcyB0aGUgbWVhbiBvZiB0aGUgb2JzZXJ2ZWQgdmFsdWVzLCBhbmQgJFJfe3hfaX0kIGlzIHRoZSByYW5rIG9mIHRoZSAkeF9pXnt0aH0kIG9ic2VydmF0aW9uLgoKYGBge3J9CnNoYXBpcm8udGVzdCggYmVlciRBQlZfTWluICkKYGBgCgpSZWplY3Rpb24gb2YgdGhlIG51bGwgaHlwb3RoZXNpcyAoZS5nLiwgYSBzbWFsbCBgcC12YWx1ZWAgZnJvbSB0aGUgdGVzdCkgaW5kaWNhdGVzIHRoYXQgdGhlIGRhdGEgKmFyZSBub3QqIHRvIGJlIGNvbnNpZGVyZWQgYXMgY29taW5nIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uLiAgU28sIGZvciB0aGUgYEFCVl9NaW5gIGRhdGEgYWJvdmUsIGl0IGFwcGVhcnMgdGhhdCBpdCBpcyBub3QgYWN0dWFsbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuICBTbyB3aGF0IGRvIHdlIGRvPwoKCiMjIyBUcmFuc2Zvcm1hdGlvbnMKCklmIHRoZSBkYXRhIGFyZSBub3Qgbm9ybWFsLCB3ZSBjYW4gbG9vayB0b3dhcmRzIHRyeWluZyB0byBzZWUgaWYgd2UgY2FuIHRyYW5zZm9ybSBpdCB0byBhIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHZhcmlhYmxlLiAgVGhlcmUgYXJlIGEgbG90IG9mIAoKCgoqU3R1ZGVudGl6ZWQgRGF0YSogLSBPbmUgd2F5IHRvIHN0YW5kYXJkaXplIHRoZSBkYXRhIGlzIHRvIG1ha2UgaXQgaGF2ZSBhIG1lYW4gb2YgMC4wIGFuZCBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiAxLjAuICBUbyBkbyB0aGlzLCB3ZSBzdWJ0cmFjdCB0aGUgYG1lYW4oKWAgYW5kIGRpdmlkZSBieSB0aGUgYHNkKClgLgoKYGBge3J9CnggPC0gYmVlciRBQlZfTWluIAp4LnN0ZCA8LSAoeCAtIG1lYW4oeCkpIC8gc2QoIHggKQpgYGAKClRoZXJlIGFyZSB0aW1lcyB3aGVuIHRoaXMgY2FuIGJlIGEgbmljZSB3YXkgdG8gY29tcGFyZSB0aGUgCgoKCipCb3ggQ294KiAtIEluIDE5NjQsIEJveCAmIENveCBkZWZpbmVkIGEgKmZhbWlseSogb2YgdHJhbnNmb3JtYXRpb25zIGtub3duIGFzIHRoZSBCb3gvQ294LiAgVGhpcyBmYW1pbHkgaXMgZGVmaW5lZCBieSBhIHNpbmdsZSBwYXJhbWV0ZXIsICRcbGFtYmRhJCwgd2hvc2UgdmFsdWUgbWF5IHZhcnkgZGVwZW5kaW5nIHVwb24gdGhlIGRhdGEuICBUaGUgb3JpZ2luYWwgZGF0YSwgJHgkLCBpcyB0aGVuIHRyYW5zZm9ybWVkIHVzaW5nIHRoZSBmb2xsb3dpbmcgcmVsYXRpb25zaGlwCgpcWwpcdGlsZGV7eH0gPSBcZnJhY3t4XlxsYW1iZGEgLSAxfXtcbGFtYmRhfQpcXQoKKipBcyBsb25nIGFzICRcbGFtYmRhIFxuZSAwJCAoZWxzZSB3ZSB3b3VsZCBiZSBkaXZpZGluZyBieSB6ZXJvLCB3aGljaCBpcyBub3QgYSBnb29kIHRoaW5nKSEqKiAgCgpPbmUgd2F5IHRvIHVzZSB0aGlzIHRyYW5zZm9ybWF0aW9uIGlzIHRvIGxvb2sgYXQgYSByYW5nZSBvZiB2YWx1ZXMgZm9yICRcbGFtYmRhJCBhbmQgZGV0ZXJtaW5lIGlmIHRoZSB0cmFuc2Zvcm1hdGlvbiAKCgpgYGB7cn0KdGVzdF9ib3hjb3ggPC0gZnVuY3Rpb24oIHgsIGxhbWJkYXMgPSBzZXEoLTEuMSwgMS4xLCBieSA9IDAuMDE1KSApIHsKICByZXQgPC0gZGF0YS5mcmFtZSggTGFtYmRhID0gbGFtYmRhcywKICAgICAgICAgICAgICAgICAgICAgVyA9IE5BLAogICAgICAgICAgICAgICAgICAgICBQID0gTkEpCiAgCiAgZm9yKCBsYW1iZGEgaW4gbGFtYmRhcyApIHsKICAgIHgudGlsZGUgPC0gKHhebGFtYmRhIC0gMSkgLyBsYW1iZGEgICAKICAgIHcgPC0gc2hhcGlyby50ZXN0KCB4LnRpbGRlICkKICAgIHJldCRXWyByZXQkTGFtYmRhID09IGxhbWJkYSBdIDwtIHckc3RhdGlzdGljCiAgICByZXQkUFsgcmV0JExhbWJkYSA9PSBsYW1iZGEgXSA8LSB3JHAudmFsdWUKICB9CiAgCiAgcmV0dXJuKCByZXQgKQp9Cgp2YWxzIDwtIHRlc3RfYm94Y294KCBiZWVyJEFCVl9NaW4gKSAKCgp2YWxzICU+JQogIGdncGxvdCggYWVzKExhbWJkYSwgUCkgKSArIAogIGdlb21fbGluZSgpICsgCiAgeWxhYigiUC1WYWx1ZSIpCgpgYGAKClNvIGlmIHlvdSBsb29rIGF0IHRoaXMgcGxvdCwgaXQgc2hvd3MgdGhlIFAtdmFsdWUgb2YgdGhlIFNoYXBpcm8tV2lsa2VzIHRlc3QgYWNyb3NzIGEgcmFuZ2Ugb2YgdmFsdWVzLiAgRGVwZW5kaW5nIHVwb24gdGhlIGxldmVsIG9mIHJpZ29yLCB0aGlzIGFwcHJvYWNoZXMgdGhlICRcYWxwaGEgPSAwLjA1JCB2YWx1ZSBjbG9zZXN0IGF0OgoKYGBge3J9CnZhbHNbIHdoaWNoKHZhbHMkUCA9PSBtYXgoIHZhbHMkUCkpLF0KYGBgCgp3aXRoICRcbGFtYmRhID0gMC4xMTUkIGFuZCBhICRQID0gMC4wNDQkLgoKKkFyYy1TaW5lIFNxdWFyZSBSb290KiAgV2hlbiBkZWFsaW5nIHdpdGggZnJhY3Rpb25zLCBpdCBpcyBjb21tb24gdGhhdCB0aGV5IGRvIG5vdCBiZWhhdmUgdmVyeSB3ZWxsIHdoZW4gdGhleSBhcmUgdmVyeSBjbG9zZSB0byAwLjAgb3IgMS4wLiAgT25lIG9mIHRoZSBjb21tb24gdHJhbnNmb3JtYXRpb25zIHRvIHVzZSB3aXRoIHRoZXNlIGtpbmRzIG9mIGRhdGEgaXMgdGhlIGFyYy1zaW4gc3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24uICBGb3IgdXMsIHRoZSBBQlYgY29sdW1ucyBpbiB0aGUgZGF0YSBpcyBhIHBlcmNlbnRhZ2UgKGJ1dCBsaXN0ZWQgaW4gbnVtZXJpY2FsIGZvcm0gYXMgcGVyY2VudCBub3QgYXMgZnJhY3Rpb24pLiAgU28gdG8gdHJhbnNmb3JtIGl0IHdlIGNhbiBkbyB0aGUgZm9sbG93aW5nLgoKYGBge3J9CmFidiA8LSBiZWVyJEFCVl9NaW4gLyAxMDAuMAphc2luKCBzcXJ0KCBhYnYgKSApIC0+IGFidi4xCnNoYXBpcm8udGVzdCggYWJ2LjEpCmBgYAoKCgojIyBFcXVhbCBWYXJpYW5jZQoKQW5vdGhlciBwYXJhbWV0cmljIGFzc3VtcHRpb24gaXMgdGhlIGVxdWFsaXR5IG9mIHZhcmlhbmNlIGFjcm9zcyBhIHJhbmdlIG9mIHRoZSBkYXRhLiAgVGhpcyBtZWFucywgZm9yIGV4YW1wbGUsIHRoYXQgdGhlIHZhcmlhbmNlIGZyb20gb25lIHBhcnQgb2YgdGhlIGV4cGVyaW1lbnQgc2hvdWxkIG5vdCBiZSBkaWZmZXJlbnQgdGhhbiB0aGUgdmFyaWFuY2UgaW4gc2FtcGxlcyBmcm9tIGFub3RoZXIgcG9ydGlvbiBvZiBkYXRhLiAgV2Ugd2lsbCByZXR1cm4gdG8gdGhpcyB3aGVuIHdlIGV2YWx1YXRlIHJlZ3Jlc3Npb24gbW9kZWxzLgoKCiMjIEluZGVwZW5kZW5jZSBvZiBEYXRhCgpUaGUgc2FtcGxlcyB5b3UgY29sbGVjdCwgYW5kIHRoZSB3YXkgdGhhdCB5b3UgZGVzaWduIHlvdXIgZXhwZXJpbWVudHMgYXJlIG1vc3QgaW1wb3J0YW50IHRvIGVuc3VyZSB0aGF0IHlvdXIgZGF0YSBhcmUgaW5kaXZpZHVhbGx5IGluZGVwZW5kZW50LiAgWW91IG5lZWQgdG8gdGhpbmsgYWJvdXQgdGhpcyB2ZXJ5IGNhcmVmdWxseSBhcyB5b3UgZGVzaWduIHlvdXIgZXhwZXJpbWVudHMuCgoKIyBDb3JyZWxhdGlvbiBUZXN0cwoKVGhlIGZvbGxvd2luZyB0eXBlcyBvZiBjb3JyZWxhdGlvbiBzdGF0aXN0aWNzIGFyZSBhIHNhbXBsZSBvZiB0aGUgbW9zdCBjb21tb24gYXBwcm9hY2hlcy4KCiMjIFBhcmFtZXRyaWMgVGVzdDogUGVhcnNvbiBQcm9kdWN0IE1vbWVudCBDb3JyZWxhdGlvbnMKCkJ5IGZhciwgdGhlIG1vc3QgY29tbW9uIGNvcnJlbGF0aW9uIHN0YXRpc3RpYyB3ZSBzZWUgaXMgdGhlIFBlYXJzb24gUHJvZHVjdCBNb21lbnQgQ29ycmVsYXRpb24sIGRlbm90ZWQgYXMgJFxyaG8kLiAgRm9yIHR3byB2YXJpYWJsZXMsICR4JCBhbmQgJHkkLCB0aGUgY29ycmVsYXRpb24gcGFyYW1ldGVyIGlzIGVzdGltYXRlZCBhczoKClxbClxyaG8gPSBcZnJhY3tcc3VtX3tpPTF9Xk4oeF9pIC0gXGJhcnt4fSkoeV9pIC0gXGJhcnt5fSl9e1xzcXJ0e1xzdW1fe2k9MX1eTih4X2kgLSBcYmFye3h9KV4yfVxzcXJ0e1xzdW1fe2k9MX1eTih5X2kgLSBcYmFye3l9KV4yfX0KXF0KClRoZSB2YWx1ZXMgb2YgdGhlc2UgZGF0YSBmYWxsIHdpaHRpbiB0aGUgcmFuZ2Ugb2Y6ICQtMSBcbGUgXHJobyBcbGUgKzEkIHdpdGggbmVnYXRpdmUgdmFsdWVzIGluZGljYXRpbmcgdGhhdCB3aGVuIG9uZSB2YXJpYWJsZSBnb2VzIHVwLCB0aGUgb3RoZXIgZ29lcyBkb3duLiAgUG9zaXRpdmUgdmFsdWVzIG9mIGEgY29ycmVsYXRpb24gaW5kaWNhdGUgdGhhdCBib3RoIHZhcmlhYmxlIGNoYW5nZSBzeXN0ZW1hdGljYWxseSBpbiB0aGUgc2FtZSBkaXJlY3Rpb24gKGUuZy4sIGJvdGggdXAgb3IgYm90aCBkb3duKS4KCkhlcmUgYXJlIHNvbWUgZXhhbXBsZXMgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0d28gdmFyaWFibGVzIGFuZCB0aGVpciBhc3NvY2lhdGVkIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50LgoKIVsqRmlndXJlIDE6IERhdGEgYW5kIGFzc29jaWF0ZWQgY29ycmVsYXRpb24gc3RhdGlzdGljcy4qXShodHRwczovL2xpdmUuc3RhdGljZmxpY2tyLmNvbS82NTUzNS81MDU2OTQzNjgyOF9hMTEwNTE1YTIxX2NfZC5qcGcpCgpTaWduaWZpY2FuY2UgdGVzdGluZyBmb3IgYSBjb3JyZWxhdGlvbiBzdWNoIGFzICRccmhvJCBkZXRlcm1pbmUgdGhlIGV4dGVudCB0byB3aGljaCB3ZSB0aGluZyB0aGUgdmFsdWUgb2YgaXMgZGV2aWFudCBmcm9tIHplcm8uICBUaGUgTnVsbCBIeXBvdGhlc2lzIGlzICRIX086IFxyaG8gXG5lIDAkIGFuZCBjYW4gYmUgZXZhbHVhdGVkIHVzaW5nIHRoZSBTdHVkZW50J3MgdC50ZXN0LiAgV2l0aCBsYXJnZSBlbm91Z2ggc2FtcGxlIHNpemVzLCBpdCBjYW4gYmUgYXBwcm94aW1hdGVkIGJ5OiAKClxbCnQgPSByIFxmcmFje04tMn17MS1yXjJ9ClxdCgpIb3dldmVyLCB3ZSBzaG91bGQgcHJvYmFibHkgcmVseSB1cG9uIGBSYCB0byBsb29rIHVwIHRoZSBjcml0aWNhbCB2YWx1ZXMgb2YgdGhlIHN0YXRpc3RpYy4gCgpUaGUgZGVmYXVsdCB2YWx1ZSBmb3IgYGNvci50ZXN0KClgIGlzIHRoZSBQZWFyc29uLiAgSGVyZSBpcyBhbiBleGFtcGxlIG9mIGl0cyB1c2UgYW5kIHRoZSBvdXRwdXQgdGhhdCB3ZSd2ZSBzZWVuIGJlZm9yZS4KCmBgYHtyfQpjb3IudGVzdCggYmVlciRPR19NYXgsIGJlZXIkRkdfTWF4ICkgLT4gT0cuRkcucGVhcnNvbgpPRy5GRy5wZWFyc29uCmBgYAoKT2YgcGFydGljdWxhciBub3RlIGFyZSB0aGUgY29tcG9uZW50cyBhc3NvY2lhdGVkIHdpdGggdGhlIHJlc3VsdHMgb2JqZWN0IHRoYXQgYWxsb3dzIHlvdSB0byBnYWluIGFjY2VzcyB0byBzcGVjaWZpY3MgZm9yIGFueSBhbmFseXNpcy4KCmBgYHtyfQpuYW1lcyggT0cuRkcucGVhcnNvbiApCmBgYAoKIyMgTm9uLVBhcmFtZXRyaWMgVGVzdDogU3BlYXJtYW4ncyBSaG8KCkFub3RoZXIgd2F5IHRvIGRlIGEgY29ycmVsYXRpb24gdGVzdCB0aGF0IGRvZXMgbm90IHJlbHkgdXBvbiBwYXJhbWV0cmljIGFzc3VtcHRpb25zIGlzIHRvIHVzZSBub24tcGFyYW1ldHJpYyBhcHByb2FjaGVzLiAgTW9zdCBub24tcGFyYW1ldHJpYyB0ZXN0cyBhcmUgYmFzZWQgdXBvbiByYW5rcyBvZiB0aGUgZGF0YSByYXRoZXIgdGhhbiB0aGUgYXNzdW1wdGlvbiBvZiBub3JtYWxpdHkgb2YgdGhlIGRhdGEgdGhhdCBpcyBuZWNlc3NhcnkgZm9yIHRoZSBQZWFyc29uIFByb2R1Y3QgTW9tZW50IHN0YXRpc3RpYy4gIE9uZSBvZiB0aGUgY29uc3RyYWludHMgZm9yIG5vbi1wYXJhbWV0cmljIHN0YXRpc3RpY3MgaXMgdGhhdCB0aGV5IGFyZSBvZnRlbiBldmFsdWF0ZWQgZm9yIHByb2JhYmlsaXR5IGJhc2VkIHVwb24gcGVybXV0YXRpb25zLgoKVGhlIGZvcm0gb2YgdGhlIGVzdGltYXRvciBmb3IgdGhpcyBpcyBhbG1vc3QgaWRlbnRpY2FsIHRvIHRoYXQgb2YgdGhlIFBlYXJzb24gc3RhdGlzdGljIGV4Y2VwdCB0aGF0IGluc3RlYWQgb2YgdGhlIHJhdyBkYXRhLCB3ZSBhcmUgcmVwbGFjaW5nIHZhbHVlcyB3aXRoIHRoZSByYW5rcyBvZiBlYWNoIHZhbHVlIGluc3RlYWQuICBJbiBkb2luZyBzbywgdGhlcmUgaXMgYSBsb3NzIG9mIHRoZSBicmVhZHRoIG9mIHRoZSByYXcgZGF0YSBzaW5jZSB3ZSBhcmUganVzdCB1c2luZyByYW5rcywgYW5kIGlmIHRoZSB1bmRlcmx5aW5nIGRhdGEgYXJlIHBvb3JseSBiZWhhdmVkIGJlY2F1c2Ugb2Ygb3V0bGllcnMgb3Igb3RoZXIgaXNzdWVzLCB0aGlzIHRha2VzIGNhcmUgb2YgaXQuCgpcWwpccmhvX3tTcGVhcm1hbn0gPSBcZnJhY3sgXHN1bV97aT0xfV5OKFJfe3hfaX0gLSBcYmFye1Jfe3h9fSkoUl97eV9pfSAtIFxiYXJ7Ul97eX19KX17XHNxcnR7XHN1bV97aT0xfV5OKFJfe3hfaX0gLSBcYmFye1Jfe3h9fSleMn1cc3FydHtcc3VtX3tpPTF9Xk4oUl97eV9pfSAtIFxiYXJ7Ul97eX19KV4yfX0KXF0KCldpdGggdGhlIHNhbWUgZGF0YSwgaXQgZG9lcyBwcm92aWRlIHBvdGVudGlhbGx5IGRpZmZlcmVudCBlc3RpbWF0ZXMgb2YgdGhlIGFtb3VudCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMuCgpgYGB7cn0KT0cuRkcuc3BlYXJtYW4gPC0gY29yLnRlc3QoIGJlZXIkT0dfTWF4LCBiZWVyJEZHX01heCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3BlYXJtYW4iICkKT0cuRkcuc3BlYXJtYW4KYGBgCgoKIyMgUGVybXV0YXRpb24gVGVzdGluZyBmb3IgU2lnbmlmaWNhbmNlCgpJbiBib3RoIG9mIHRoZSBwcmV2aW91cyBtZXRob2RzLCB3ZSB1c2VkIHNwZWNpZmljIGFwcHJvYWNoZXMgdG8gZXZhbHVhdGUgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGUgc3RhdGlzdGljLiBGb3IgUGVhcnNvbiwgd2UgYXBwcm94aW1hdGVkIHVzaW5nIHRoZSAkdCQuICBGb3IgdGhlIFNwZWFybWFuIHRlc3Qgd2l0aCBzbWFsbCBudW1iZXJzIG9mIHNhbXBsZXMsIGFuIGFwcHJveGltYXRpb24gb2YgdGhlICR0JCB0ZXN0IGlzIHVzZWQsIGJhc2VkIHVwb24gY291bnRpbmcgcmFua3MgYW5kIHRoZSBudW1iZXIgb2Ygd2F5cyB3ZSBjYW4gZ2V0IGRpZmZlcmVudCBjb21iaW5hdGlvbnMgb2YgcmFua3MuICBGb3IgbGFyZ2VyIHNhbXBsZSBzaXplIHRlc3RzIHVzaW5nIFNwZWFybWFuLCBhbiBhcHByb3hpbWF0aW9uIHVzaW5nIHRoZSAkdCQgdGVzdCBjYW4gYmUgdXNlZC4KCkFub3RoZXIgd2F5IG9mIGRvaW5nIHRoaXMgaXMgYmFzZWQgdXBvbiBwZXJtdXRhdGlvbiBhbmQgdGhpcyBhcHByb2FjaCBjYW4gYmUgYXBwbGllZCB0byBhIHdpZGUgYXJyYXkgb2YgcXVlc3Rpb25zLiAgRm9yIGNvcnJlbGF0aW9uJ3MsIGlmIHdlIGNvbnNpZGVyIHRoZSBudWxsIGh5cG90aGVzaXMgJEhfTzogXHJobyA9IDAkIHdlIGNhbiBtYWtlIGEgZmV3IGluZmVyZW5jZXMuICBJZiB0aGlzIGh5cG90aGVzaXMgaXMgdHJ1ZSB0aGVuIHdlIGFyZSwgZXNzZW50aWFsbHksIHNheWluZyB0aGF0IHRoZSBjdXJyZW50IHJlbGF0aW9uc2hpcCBiZXR3ZWVuICR4X2kkIGFuZCAkeV9pJCBoYXMgbm8gaW50cmluc2ljIHJlbGF0aW9uc2hpcCBhcyB0aGVyZSBpcyBubyBjb3JyZWxhdGlvbi4gIFRoaXMgaXMsIGJ5IGRlZmF1bHQsIHdoYXQgdGhlIG51bGwgaHlwb3RoZXNpcyBzYXlzLiAgCgpJZiB0aGF0IGlzIHRydWUsIGhvd2V2ZXIsIHRoYXQgbWVhbnMgdGhhdCBhbnkgcGVybXV0YXRpb24gb2Ygb25lIG9mIHRoZSB2YXJpYWJsZXMsIHNheSAkeSQsIHNob3VsZCBwcm9kdWNlIGEgY29ycmVsYXRpb24gc3RhdGlzdGljIHRoYXQgaXMganVzdCBhcyBsYXJnZSBhcyBhbnkgb3RoZXIgcGVybXV0YXRpb24gb2YgdGhlIGRhdGEuICBUaGlzIGlzIGtleS4gIAoKU28sIGlmIHdlIGFzc3VtZSB0aGUgJEhfTyQgaXMgdHJ1ZSB0aGVuIHdlIHNob3VsZCBiZSBhYmxlIHRvIHNodWZmbGUgb25lIG9mIHRoZSBkYXRhIGFuZCBlc3RpbWF0ZSBhIGNvcnJlbGF0aW9uIHN0YXRpc3RpYyBhIGxhcmdlIG51bWJlciBvZiB0aW1lcy4gIFdlIGNhbiB0aGVuIGNyZWF0ZSBhIHBlcm11dGVkIGRpc3RyaWJ1dGlvbiBvZiB2YWx1ZXMgZm9yIHRoZSBjb3JyZWxhdGlvbiwgKipBc3N1bWluZyB0aGUgTlVMTCBIeXBvdGhlc2lzIGlzIHRydWUuKiogIFRvIHRoaXMgZGlzdHJpYnV0aW9uLCB3ZSBjYW4gZXZhbHVhdGUgdGhlIG1hZ25pdHVkZSBvZiB0aGUgb3JpZ2luYWwgY29ycmVsYXRpb24uICBIZXJlIGlzIGFuIGV4YW1wbGUgdXNpbmcgdGhlIGRhdGEgZnJvbSBhYm92ZS4KCmBgYHtyfQp4IDwtIGJlZXIkT0dfTWF4CnkgPC0gYmVlciRGR19NYXgKZGYgPC0gZGF0YS5mcmFtZSggRXN0aW1hdGUgPSBmYWN0b3IoIGMoICJPcmlnaW5hbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIlBlcm11dGVkIiwgOTk5KSkpLCAKICAgICAgICAgICAgICAgICAgcmhvID0gIGMoIGNvci50ZXN0KCB4LCB5ICkkZXN0aW1hdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoTkEsIDk5OSkpICkKCnN1bW1hcnkoIGRmICkKYGBgCgpOb3csIHdlIGNhbiBnbyB0aHJvdWdoIHRoZSA5OTkgYE5BYCB2YWx1ZXMgd2UgcHV0IGludG8gdGhhdCBkYXRhIGZyYW1lIGFuZDogIAoxLiBQZXJtdXRlIG9uZSBvZiB0aGUgdmFyaWFibGVzCjIuIFJ1biB0aGUgYW5hbHlzaXMgIAozLiBTdG9yZSB0aGUgc3RhdGlzdGljLgoKCmBgYHtyfQpmb3IoIGkgaW4gMjoxMDAwKSB7CiAgeWhhdCA8LSBzYW1wbGUoIHksICAgIyB0aGlzIHNodWZmbGVzIHRoZSBkYXRhIGluIHkKICAgICAgICAgICAgICAgICAgc2l6ZSA9IGxlbmd0aCh5KSwgCiAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSBGQUxTRSkKICBtb2RlbCA8LSBjb3IudGVzdCggeCwgeWhhdCApCiAgZGYkcmhvW2ldIDwtIG1vZGVsJGVzdGltYXRlIAp9CmBgYAoKCk5vdyB3ZSBjYW4gbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHBlcm11dGVkIHZhbHVlcyBhbmQgdGhlIG9yaWdpbmFsIG9uZSBhbmQgc2VlIHRoZSByZWxhdGlvbnNoaXAuICBJZjogIAoKLSBUaGUgb2JzZXJ2ZWQgdmFsdWUgaXMgd2l0aGluIHRoZSBib2R5IG9mIHRoZSBwZXJtdXRlZCB2YWx1ZXMsIHRoZW4gaXQgaXMgbm90IHRvbyByYXJl4oCUZ2l2ZW4gJEhfTyQsIG9yCi0gSWYgdGhlIG9ic2VydmVkIHZhbHVlIGlzIHdheSBvdXRzaWRlIHRob3NlIHBlcm11dGVkIHZhbHVlcywgdGhlbiBpdCBhcHBlYXJzIHRvIGJlIHNvbWV3aGF0IHJhcmUuCgoKYGBge3J9CmdncGxvdCggZGYgKSArIAogIGdlb21faGlzdG9ncmFtKCBhZXMocmhvLCBmaWxsPUVzdGltYXRlICkgKQpgYGAKCklmIHlvdSBsb29rIGF0IHRoZSBncmFwaCBhYm92ZSwgeW91IHNlZSB0aGF0IHRoZSBvcmlnaW5hbCB2YWx1ZSBpcyAqKndheSBiaWdnZXIqKiB0aGFuIHRoZSB2YWx1ZXMgdGhhdCB3b3VsZCBiZSBmb3VuZCBgaWYgYW5kIG9ubHkgaWZgICRIX08kIHdlcmUgdHJ1ZS4gIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgY29ycmVsYXRpb24gaXMgbm90IHplcm8gYW5kIGluIGZhY3QgaXQgaXMgdGhlIGxhcmdlc3Qgb2JzZXJ2YXRpb24gb2YgdGhlIDEwMDAgb2JzZXJ2YXRpb25zIChhIFAgZXN0aW1hdGUgb2YgJFxmcmFjezF9ezEwMDB9JC4uLikuCgoKCgoKCgoKCgoKCgoKCgoKCgo=