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.

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.

Figure 1: Data and associated correlation statistics.

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=