1 Correlations

  • Everything correlates with everything (Ambient Correlational Noise).
  • Our goal today is to determine how much
  • Deal with 2 variables: Next we explore the problems with 3 or more

Correlations between two variables

  • Pearson’s (interval by interval)
  • Spearman’s (interval by ordinal)
  • Point-by-serial (interval by dichotomous)
  • Polychoric (ordinal vs ordinal)
  • Tetrachroic (dichotomous vs dichotomous)

1.1 Pearson’s Correlation

\(r_{xy}=\frac{\sum{xy}}{\sqrt{\sum{x^2}\sum{y^2}}}\)

  • Numerator = How much they vary together (co-variance)
  • Denominator = How much they vary alone (variance)
  • values is bounded between -1 and 1
  • Let’s create two random normal variables that correlate with each other
  • to do this we need to make a covariance matrix with a correlation at .6
#packages we will need to conduct to create and graph our data
library(MASS) #create data
library(car) #graph data
r=.6 #Correlation value
Means.XY<- c(10,10) #set the means of X and Y variables
CovMatrix.XY <- matrix(c(1,r,r,1),2,2) # creates the covariate matrix 

CovMatrix.XY #nice and simple 2x2 matrix
##      [,1] [,2]
## [1,]  1.0  0.6
## [2,]  0.6  1.0
#build the correlated variables. 
# Note: empirical=TRUE means make the correlation EXACTLY r. 
# empirical=FALSE, the correlation value would be normally distributed around r
CorrData<-mvrnorm(n=100, mu=Means.XY,Sigma=CovMatrix.XY, empirical=TRUE)

#Convert them to a "Data.Frame", which is like SPSS data window
CorrData<-as.data.frame(CorrData)
#lets add our labels to the vectors we created
colnames(CorrData) <- c("Happiness","IceCream")
#Lets view the first few subjects
head(CorrData)
##   Happiness  IceCream
## 1 10.774760 10.907732
## 2  8.700742  8.816727
## 3  9.722170  9.617680
## 4 10.927843 10.499182
## 5 11.584289 10.692111
## 6  9.641194  9.557827
#make the scatter plot
scatterplot(Happiness~IceCream,CorrData, smoother=FALSE)

#calculate the Pearson's correlation 
cor.test(CorrData$Happiness, CorrData$IceCream, 
         method = c("pearson"))
## 
##  Pearson's product-moment correlation
## 
## data:  CorrData$Happiness and CorrData$IceCream
## t = 7.4246, df = 98, p-value = 4.193e-11
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4574985 0.7124547
## sample estimates:
## cor 
## 0.6

1.1.1 Pearson’s correlation is scale independent!

  • No matter the mean differences or range of scores, the Pearson’s r will give the same results.
  • We can also z-score the data and get the same result
  • However if they are scaled non-linearly (sqrt, ^2, log,…) the correlation will change.
#lets add (change the mean)
Happiness.big<-CorrData$Happiness+1000
IceCream<-CorrData$IceCream
cor.test(Happiness.big, IceCream, method = c("pearson"))
## 
##  Pearson's product-moment correlation
## 
## data:  Happiness.big and IceCream
## t = 7.4246, df = 98, p-value = 4.193e-11
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4574985 0.7124547
## sample estimates:
## cor 
## 0.6
#zscored
Happiness.z<-scale(CorrData$Happiness)
IceCream.z<-scale(CorrData$IceCream)
cor.test(Happiness.z, IceCream.z, method = c("pearson"))
## 
##  Pearson's product-moment correlation
## 
## data:  Happiness.z and IceCream.z
## t = 7.4246, df = 98, p-value = 4.193e-11
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4574985 0.7124547
## sample estimates:
## cor 
## 0.6
#what happens if I LINEARLY scale them differently?
cor.test(Happiness.big, IceCream.z, method = c("pearson"))
## 
##  Pearson's product-moment correlation
## 
## data:  Happiness.big and IceCream.z
## t = 7.4246, df = 98, p-value = 4.193e-11
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4574985 0.7124547
## sample estimates:
## cor 
## 0.6
#what happens if I NON-LINEARLY scale them differently?
Happiness<-CorrData$Happiness # orginal
IceCream.sq4<-(CorrData$IceCream)^4 #Non-linear

scatterplot(Happiness~IceCream.sq4, smoother=FALSE)

cor.test(Happiness, IceCream.sq4, method = c("pearson"))
## 
##  Pearson's product-moment correlation
## 
## data:  Happiness and IceCream.sq4
## t = 6.6871, df = 98, p-value = 1.409e-09
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4082241 0.6812735
## sample estimates:
##       cor 
## 0.5597593

1.1.2 Pearson’s: Let visualize our result

  • Overlap between the two variables is defined by \(r^2\)
library(VennDiagram) # lets us plot our results (like the book)
## Loading required package: grid
## Loading required package: futile.logger
## 
## Attaching package: 'VennDiagram'
## The following object is masked from 'package:car':
## 
##     ellipse
# calculate r-squared
overlap=r^2 

Simple.Corr.Venn<-draw.pairwise.venn(1, 1, overlap, c("Happiness", "IceCream"))
grid.draw(Simple.Corr.Venn)

1.2 Spearmen’s Correlation

  • Spearman is a Pearson Correlation on rank ordered data.
  • Let’s rank order our random correlated data
  • You have to rank each variable independently first
CorrData$X.rank<-rank(CorrData$Happiness)
CorrData$Y.rank<-rank(CorrData$IceCream)

scatterplot(Y.rank~X.rank,CorrData, smoother=FALSE)

cor.test(CorrData$Y.rank, CorrData$X.rank, method = c("pearson"))
## 
##  Pearson's product-moment correlation
## 
## data:  CorrData$Y.rank and CorrData$X.rank
## t = 6.7079, df = 98, p-value = 1.278e-09
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.4096673 0.6822010
## sample estimates:
##       cor 
## 0.5609481
# Or you can just use the built in spearman correlation and get the same result
cor.test(CorrData$Y, CorrData$X, method = c("spearman"))
## 
##  Spearman's rank correlation rho
## 
## data:  CorrData$Y and CorrData$X
## S = 73168, p-value = 1.923e-09
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##       rho 
## 0.5609481

1.3 Point-by-Serial

  • This time lets make up some data on the fly
library(polycor) #Advanced Correlations
## 
## Attaching package: 'polycor'
## The following object is masked from 'package:psych':
## 
##     polyserial
set.seed(42)
ratings<-c(rnorm(25,mean=5,sd = .5),rnorm(25,mean=2,sd = .5))
Flavors<-c(rep(0,25),c(rep(1,25)))
FlavorNames<-c(rep("cookie Dough",25),c(rep("Rum-Raisin",25)))

#Build data frame
Ice.Cream.Data<-data.frame(
  ratings = ratings,
  Flavors = Flavors,
  Names = FlavorNames)
head(Ice.Cream.Data)
##    ratings Flavors        Names
## 1 5.685479       0 cookie Dough
## 2 4.717651       0 cookie Dough
## 3 5.181564       0 cookie Dough
## 4 5.316431       0 cookie Dough
## 5 5.202134       0 cookie Dough
## 6 4.946938       0 cookie Dough
scatterplot(ratings~Flavors,Ice.Cream.Data, smoother=FALSE)

polyserial(Ice.Cream.Data$Flavors,Ice.Cream.Data$ratings)
## [1] -0.8868748

2 Regression

  • Correlation and regression are similar
  • Correlation determines the standardized relationship between X and Y
  • Linear regression = 1 DV and 1 IV, where the relationship is a straight line
  • Linear regression determines how X predicts Y
  • Multiple (linear) regression = 1 DV and 2+ IV (also straight lines)
  • Multiple regression determines how X,z, and etc, predict Y [next week]

2.1 Basic Regression Equation

  • Linear Regression equation you learned when younger was probably \(y = MX + b\)
  • \(y\) = predict value
  • \(M\) = slope
  • \(X\) = Variable used to predict Y
  • \(b\) = intercept

2.2 Modern Regression Equation

  • \(Y=B_{YX}X + B_0 + e\)
  • \(Y\) = predict value
  • \(B_{YX}\) = slope
  • \(B_{0}\) = intercept
  • \(e\) = error term (observed - predicted). Also called the residual.

2.3 Ice cream example

  • Specify the model with the lm function.
  • We are going to predict happiness scores from ice cream!
scatterplot(Happiness~IceCream, smoother=FALSE)

Happy.Model.1<-lm(Happiness~IceCream,data = CorrData)
summary(Happy.Model.1)
## 
## Call:
## lm(formula = Happiness ~ IceCream, data = CorrData)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.54020 -0.62443 -0.01595  0.61440  1.93532 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  4.00000    0.81211   4.925 3.42e-06 ***
## IceCream     0.60000    0.08081   7.425 4.19e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8041 on 98 degrees of freedom
## Multiple R-squared:   0.36,  Adjusted R-squared:  0.3535 
## F-statistic: 55.13 on 1 and 98 DF,  p-value: 4.193e-11

2.3.1 Intercept

  • 4 is where the line hit the y-intercept (when happiness = 0).

2.3.2 Slope

  • 0.6 is the rise over run
  • for each 0.6 change in ice cream value, there is a corresponding change in happiness!
  • so we can predict happiness from ice cream score:

(0.6 * 5 spoons of ice cream + baseline happiness intercept: 4) = 7

  • This is your predicted happiness score if you had 5 spoons of ice cream

2.3.3 Error for this prediction?

R will actually do all the prediction for us for each value of ice cream residuals = observed - predicted

  • Red dots = observed above predictor line
  • Blue dots = observed below predictor line
  • the stronger the color the more an impact that point has in pulling the line in its direction
  • Hollow dots = predicted
  • The gray lines are the distance between observed and predicted values!

What should the mean of the residuals equal?

CorrData$predicted <- predict(Happy.Model.1)   # Save the predicted values with our real data
CorrData$residuals <- residuals(Happy.Model.1) # Save the residual values


library(ggplot2) # Note: a more powerful graphing tool I am forced to use to show this to you
ggplot(data = CorrData, aes(x = IceCream, y = Happiness)) +
  geom_smooth(method = "lm", se = FALSE, color = "lightgrey") +  # Plot regression slope
  geom_point(aes(color = residuals)) +  # Color mapped here
  scale_color_gradient2(low = "blue", mid = "white", high = "red") +  # Colors to use here
  guides(color = FALSE) +
  geom_segment(aes(xend = IceCream, yend = predicted), alpha = .2) +  # alpha to fade lines
  geom_point(aes(y = predicted), shape = 1) +
  theme_bw()  # Add theme for cleaner look

2.3.4 Ordinary least squares (OLS)

  • Linear regression finds the best fit line by trying to minimize the sum of the squares of the differences between the observed responses those predicted by the line.
  • OLS computationally simple to get the slope value, but is actually inaccurate

\[B_{YX}=\frac{\sum{XY}-\frac{1}{n}\sum{X}\sum{Y}}{\sum{x^2}-\frac{1}{n}\sum{x}^2} = \frac{Cov_{XY}}{var_x}\]

  • Modern methods use an alternative (ML, REML) we will examine later when we get to GLM

2.4 SE on the terms in the models (how good is the fit?)

  • Residual Standard error = \(\sqrt\frac{\sum{e^2}}{n-2}\)
  • in R language:
n=length(CorrData$residuals)

RSE = sqrt(sum(CorrData$residuals^2) / (n-2))
RSE
## [1] 0.8040713
  • So our error on the prediction is 0.8040713 happiness points based on our model.

2.4.1 SE on the Intercept

  • Intercept Standard error = \(RSE\sqrt {\frac{1}{n}+\frac{M_x^2}{(n-1)var_x}}\)
  • in R language:
ISE = RSE*(sqrt( 1 / n + mean(CorrData$IceCream)^2 / (n - 1)*var(CorrData$IceCream)))
ISE
## [1] 0.8121124

2.4.2 SE on the Slope

  • Intercept Standard error = \(\frac{sd_y}{sd_x}\sqrt{\frac{1 - r_YX^2}{n-2}}\)
  • in R language:
#lets extract the r2 from the model
r2.model<-summary(Happy.Model.1)$r.squared

SSE = sd(CorrData$Happiness)/sd(CorrData$IceCream) * sqrt((1- r2.model)/ (n - 2))
SSE
## [1] 0.0808122

2.4.3 t-tests on slope and intercept and \(r^2\) value

  • Values are tested against 0, so its all one sample t-tests
  • slope: \(t = \frac{B_{YX} - H_0}{SE_{B_{YX}}}\)
  • intercept: \(t = \frac{B_{0} - H_0}{SE_{B_{0}}}\)

\(r^2\) is a little different as its a correlation value

  • correlations are not normally distributed
  • Fisher created a conversion for r to make it a z (called Fishers’ \(r\) to \(Z\))
  • \(r^2\): \(t = \frac{r_{XY}\sqrt{n-2}-H_0}{\sqrt{1-r_{XY}^2}}\) , where df = n - 2
  • its often given for as an F value, remember \(t = F^2\)
#intercept
t.I= Happy.Model.1$coefficients[1]/ISE
t.I
## (Intercept) 
##    4.925427
#Slope
t.S= Happy.Model.1$coefficients[2]/SSE
t.S
## IceCream 
## 7.424621
# For r-squared
t.r2xy = r2.model^.5*sqrt(n-2)/sqrt(1-r2.model)
F.r2xy = t.r2xy^2
F.r2xy
## [1] 55.125

Note: We are testing null hypothesis value for slope, i.e., null = 0. But it’s a terrible guess. Everything correlates with everything, so it’s important to keep this in mind moving forward. So that would be the NILL hypothesis. NILL can be tested better with bootstrapping.

3 Power and Regression

  • We studied power last semester
  • I will skip ahead to calculating it in R.
  • for regression, we will need to convert our \(r^2\) into cohen’s \(f^2\)
  • \(f^2 = \frac{r^2}{1-r^2}\)
library(pwr) #power analysis
#power for GLM
# u  = degrees of freedom for numerator
# v = degrees of freedom for denominator
# f2 = effect size
# sig.level= (Type I error probability)
# power = (1 minus Type II error probability)
f2.icecream <- r^2 / (1-r^2)

pwr.f2.test(u = 1, v = n-2, f2 = f2.icecream, sig.level = 0.05, power = NULL)
## 
##      Multiple regression power calculation 
## 
##               u = 1
##               v = 98
##              f2 = 0.5625
##       sig.level = 0.05
##           power = 1

So we had a power of basically 1 given this sample size and our true effect size of 0.6

4 Final Notes

  • Testing between correlations using old fashion Fisher’s test is old fashion (Cohen et al., p. 49). The modern approach is the bootstrap, as the old method is under-powered.
  • Your book is little out of date: CIs are better but bootstrapped CIs are becoming more standard.
LS0tDQp0aXRsZTogIkNvcnJlbGF0aW9ucyBhbmQgUmVncmVzc2lvbnMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgZm9udHNpemU6IDhwdA0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCi0tLQ0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD01KQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5oZWlnaHQ9My43NSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcuYWxpZ249J2NlbnRlcicpIA0KYGBgDQoNCiMgQ29ycmVsYXRpb25zDQoNCi0gRXZlcnl0aGluZyBjb3JyZWxhdGVzIHdpdGggZXZlcnl0aGluZyAoQW1iaWVudCBDb3JyZWxhdGlvbmFsIE5vaXNlKS4gDQotIE91ciBnb2FsIHRvZGF5IGlzIHRvIGRldGVybWluZSBob3cgbXVjaCANCi0gRGVhbCB3aXRoIDIgdmFyaWFibGVzOiBOZXh0IHdlIGV4cGxvcmUgdGhlIHByb2JsZW1zIHdpdGggMyBvciBtb3JlDQoNCkNvcnJlbGF0aW9ucyBiZXR3ZWVuIHR3byB2YXJpYWJsZXMgDQoNCi0gUGVhcnNvbidzIChpbnRlcnZhbCBieSBpbnRlcnZhbCkNCi0gU3BlYXJtYW4ncyAoaW50ZXJ2YWwgYnkgb3JkaW5hbCkNCi0gUG9pbnQtYnktc2VyaWFsIChpbnRlcnZhbCBieSBkaWNob3RvbW91cykNCi0gUG9seWNob3JpYyAob3JkaW5hbCB2cyBvcmRpbmFsKQ0KLSBUZXRyYWNocm9pYyAoZGljaG90b21vdXMgdnMgZGljaG90b21vdXMpDQoNCg0KIyMgUGVhcnNvbidzIENvcnJlbGF0aW9uDQoNCiRyX3t4eX09XGZyYWN7XHN1bXt4eX19e1xzcXJ0e1xzdW17eF4yfVxzdW17eV4yfX19JA0KDQotIE51bWVyYXRvciA9IEhvdyBtdWNoIHRoZXkgdmFyeSB0b2dldGhlciAoY28tdmFyaWFuY2UpDQotIERlbm9taW5hdG9yID0gSG93IG11Y2ggdGhleSB2YXJ5IGFsb25lICh2YXJpYW5jZSkNCi0gdmFsdWVzIGlzIGJvdW5kZWQgYmV0d2VlbiAtMSBhbmQgMQ0KLSBMZXQncyBjcmVhdGUgdHdvIHJhbmRvbSBub3JtYWwgdmFyaWFibGVzIHRoYXQgY29ycmVsYXRlIHdpdGggZWFjaCBvdGhlcg0KLSB0byBkbyB0aGlzIHdlIG5lZWQgdG8gbWFrZSBhIGNvdmFyaWFuY2UgbWF0cml4IHdpdGggYSBjb3JyZWxhdGlvbiBhdCAuNg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KI3BhY2thZ2VzIHdlIHdpbGwgbmVlZCB0byBjb25kdWN0IHRvIGNyZWF0ZSBhbmQgZ3JhcGggb3VyIGRhdGENCmxpYnJhcnkoTUFTUykgI2NyZWF0ZSBkYXRhDQpsaWJyYXJ5KGNhcikgI2dyYXBoIGRhdGENCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KDQpyPS42ICNDb3JyZWxhdGlvbiB2YWx1ZQ0KTWVhbnMuWFk8LSBjKDEwLDEwKSAjc2V0IHRoZSBtZWFucyBvZiBYIGFuZCBZIHZhcmlhYmxlcw0KQ292TWF0cml4LlhZIDwtIG1hdHJpeChjKDEscixyLDEpLDIsMikgIyBjcmVhdGVzIHRoZSBjb3ZhcmlhdGUgbWF0cml4IA0KDQpDb3ZNYXRyaXguWFkgI25pY2UgYW5kIHNpbXBsZSAyeDIgbWF0cml4DQoNCiNidWlsZCB0aGUgY29ycmVsYXRlZCB2YXJpYWJsZXMuIA0KIyBOb3RlOiBlbXBpcmljYWw9VFJVRSBtZWFucyBtYWtlIHRoZSBjb3JyZWxhdGlvbiBFWEFDVExZIHIuIA0KIyBlbXBpcmljYWw9RkFMU0UsIHRoZSBjb3JyZWxhdGlvbiB2YWx1ZSB3b3VsZCBiZSBub3JtYWxseSBkaXN0cmlidXRlZCBhcm91bmQgcg0KQ29yckRhdGE8LW12cm5vcm0obj0xMDAsIG11PU1lYW5zLlhZLFNpZ21hPUNvdk1hdHJpeC5YWSwgZW1waXJpY2FsPVRSVUUpDQoNCiNDb252ZXJ0IHRoZW0gdG8gYSAiRGF0YS5GcmFtZSIsIHdoaWNoIGlzIGxpa2UgU1BTUyBkYXRhIHdpbmRvdw0KQ29yckRhdGE8LWFzLmRhdGEuZnJhbWUoQ29yckRhdGEpDQojbGV0cyBhZGQgb3VyIGxhYmVscyB0byB0aGUgdmVjdG9ycyB3ZSBjcmVhdGVkDQpjb2xuYW1lcyhDb3JyRGF0YSkgPC0gYygiSGFwcGluZXNzIiwiSWNlQ3JlYW0iKQ0KI0xldHMgdmlldyB0aGUgZmlyc3QgZmV3IHN1YmplY3RzDQpoZWFkKENvcnJEYXRhKQ0KI21ha2UgdGhlIHNjYXR0ZXIgcGxvdA0Kc2NhdHRlcnBsb3QoSGFwcGluZXNzfkljZUNyZWFtLENvcnJEYXRhLCBzbW9vdGhlcj1GQUxTRSkNCg0KI2NhbGN1bGF0ZSB0aGUgUGVhcnNvbidzIGNvcnJlbGF0aW9uIA0KY29yLnRlc3QoQ29yckRhdGEkSGFwcGluZXNzLCBDb3JyRGF0YSRJY2VDcmVhbSwgDQogICAgICAgICBtZXRob2QgPSBjKCJwZWFyc29uIikpDQoNCmBgYA0KDQojIyMgUGVhcnNvbidzIGNvcnJlbGF0aW9uIGlzIHNjYWxlIGluZGVwZW5kZW50ISANCi0gTm8gbWF0dGVyIHRoZSBtZWFuIGRpZmZlcmVuY2VzIG9yIHJhbmdlIG9mIHNjb3JlcywgdGhlIFBlYXJzb24ncyByIHdpbGwgZ2l2ZSB0aGUgc2FtZSByZXN1bHRzLiANCi0gV2UgY2FuIGFsc28gei1zY29yZSB0aGUgZGF0YSBhbmQgZ2V0IHRoZSBzYW1lIHJlc3VsdA0KLSBIb3dldmVyIGlmIHRoZXkgYXJlIHNjYWxlZCBub24tbGluZWFybHkgKHNxcnQsIF4yLCBsb2csLi4uKSB0aGUgY29ycmVsYXRpb24gd2lsbCBjaGFuZ2UuIA0KDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQoNCiNsZXRzIGFkZCAoY2hhbmdlIHRoZSBtZWFuKQ0KSGFwcGluZXNzLmJpZzwtQ29yckRhdGEkSGFwcGluZXNzKzEwMDANCkljZUNyZWFtPC1Db3JyRGF0YSRJY2VDcmVhbQ0KY29yLnRlc3QoSGFwcGluZXNzLmJpZywgSWNlQ3JlYW0sIG1ldGhvZCA9IGMoInBlYXJzb24iKSkNCg0KI3pzY29yZWQNCkhhcHBpbmVzcy56PC1zY2FsZShDb3JyRGF0YSRIYXBwaW5lc3MpDQpJY2VDcmVhbS56PC1zY2FsZShDb3JyRGF0YSRJY2VDcmVhbSkNCmNvci50ZXN0KEhhcHBpbmVzcy56LCBJY2VDcmVhbS56LCBtZXRob2QgPSBjKCJwZWFyc29uIikpDQoNCiN3aGF0IGhhcHBlbnMgaWYgSSBMSU5FQVJMWSBzY2FsZSB0aGVtIGRpZmZlcmVudGx5Pw0KY29yLnRlc3QoSGFwcGluZXNzLmJpZywgSWNlQ3JlYW0ueiwgbWV0aG9kID0gYygicGVhcnNvbiIpKQ0KDQojd2hhdCBoYXBwZW5zIGlmIEkgTk9OLUxJTkVBUkxZIHNjYWxlIHRoZW0gZGlmZmVyZW50bHk/DQpIYXBwaW5lc3M8LUNvcnJEYXRhJEhhcHBpbmVzcyAjIG9yZ2luYWwNCkljZUNyZWFtLnNxNDwtKENvcnJEYXRhJEljZUNyZWFtKV40ICNOb24tbGluZWFyDQoNCnNjYXR0ZXJwbG90KEhhcHBpbmVzc35JY2VDcmVhbS5zcTQsIHNtb290aGVyPUZBTFNFKQ0KY29yLnRlc3QoSGFwcGluZXNzLCBJY2VDcmVhbS5zcTQsIG1ldGhvZCA9IGMoInBlYXJzb24iKSkNCg0KDQpgYGANCg0KIyMjIFBlYXJzb24nczogTGV0IHZpc3VhbGl6ZSBvdXIgcmVzdWx0DQotIE92ZXJsYXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcyBpcyBkZWZpbmVkIGJ5ICRyXjIkDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KFZlbm5EaWFncmFtKSAjIGxldHMgdXMgcGxvdCBvdXIgcmVzdWx0cyAobGlrZSB0aGUgYm9vaykNCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KIyBjYWxjdWxhdGUgci1zcXVhcmVkDQpvdmVybGFwPXJeMiANCg0KU2ltcGxlLkNvcnIuVmVubjwtZHJhdy5wYWlyd2lzZS52ZW5uKDEsIDEsIG92ZXJsYXAsIGMoIkhhcHBpbmVzcyIsICJJY2VDcmVhbSIpKQ0KZ3JpZC5kcmF3KFNpbXBsZS5Db3JyLlZlbm4pDQoNCmBgYA0KDQojIyBTcGVhcm1lbidzIENvcnJlbGF0aW9uDQotIFNwZWFybWFuIGlzIGEgUGVhcnNvbiBDb3JyZWxhdGlvbiBvbiByYW5rIG9yZGVyZWQgZGF0YS4gDQotIExldCdzIHJhbmsgb3JkZXIgb3VyIHJhbmRvbSBjb3JyZWxhdGVkIGRhdGENCi0gWW91IGhhdmUgdG8gcmFuayBlYWNoIHZhcmlhYmxlIGluZGVwZW5kZW50bHkgZmlyc3QNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCkNvcnJEYXRhJFgucmFuazwtcmFuayhDb3JyRGF0YSRIYXBwaW5lc3MpDQpDb3JyRGF0YSRZLnJhbms8LXJhbmsoQ29yckRhdGEkSWNlQ3JlYW0pDQoNCnNjYXR0ZXJwbG90KFkucmFua35YLnJhbmssQ29yckRhdGEsIHNtb290aGVyPUZBTFNFKQ0KDQpjb3IudGVzdChDb3JyRGF0YSRZLnJhbmssIENvcnJEYXRhJFgucmFuaywgbWV0aG9kID0gYygicGVhcnNvbiIpKQ0KDQojIE9yIHlvdSBjYW4ganVzdCB1c2UgdGhlIGJ1aWx0IGluIHNwZWFybWFuIGNvcnJlbGF0aW9uIGFuZCBnZXQgdGhlIHNhbWUgcmVzdWx0DQpjb3IudGVzdChDb3JyRGF0YSRZLCBDb3JyRGF0YSRYLCBtZXRob2QgPSBjKCJzcGVhcm1hbiIpKQ0KDQoNCmBgYA0KDQojIyBQb2ludC1ieS1TZXJpYWwNCg0KLSBUaGlzIHRpbWUgbGV0cyBtYWtlIHVwIHNvbWUgZGF0YSBvbiB0aGUgZmx5DQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHBvbHljb3IpICNBZHZhbmNlZCBDb3JyZWxhdGlvbnMNCnNldC5zZWVkKDQyKQ0KcmF0aW5nczwtYyhybm9ybSgyNSxtZWFuPTUsc2QgPSAuNSkscm5vcm0oMjUsbWVhbj0yLHNkID0gLjUpKQ0KRmxhdm9yczwtYyhyZXAoMCwyNSksYyhyZXAoMSwyNSkpKQ0KRmxhdm9yTmFtZXM8LWMocmVwKCJjb29raWUgRG91Z2giLDI1KSxjKHJlcCgiUnVtLVJhaXNpbiIsMjUpKSkNCg0KI0J1aWxkIGRhdGEgZnJhbWUNCkljZS5DcmVhbS5EYXRhPC1kYXRhLmZyYW1lKA0KICByYXRpbmdzID0gcmF0aW5ncywNCiAgRmxhdm9ycyA9IEZsYXZvcnMsDQogIE5hbWVzID0gRmxhdm9yTmFtZXMpDQpoZWFkKEljZS5DcmVhbS5EYXRhKQ0KDQpzY2F0dGVycGxvdChyYXRpbmdzfkZsYXZvcnMsSWNlLkNyZWFtLkRhdGEsIHNtb290aGVyPUZBTFNFKQ0KcG9seXNlcmlhbChJY2UuQ3JlYW0uRGF0YSRGbGF2b3JzLEljZS5DcmVhbS5EYXRhJHJhdGluZ3MpDQogICAgICAgICAgIA0KYGBgDQoNCiMgUmVncmVzc2lvbg0KLSBDb3JyZWxhdGlvbiBhbmQgcmVncmVzc2lvbiBhcmUgc2ltaWxhcg0KLSBDb3JyZWxhdGlvbiBkZXRlcm1pbmVzIHRoZSBzdGFuZGFyZGl6ZWQgcmVsYXRpb25zaGlwIGJldHdlZW4gWCBhbmQgWQ0KLSBMaW5lYXIgcmVncmVzc2lvbiA9IDEgRFYgYW5kIDEgSVYsIHdoZXJlIHRoZSByZWxhdGlvbnNoaXAgaXMgYSBzdHJhaWdodCBsaW5lDQotIExpbmVhciByZWdyZXNzaW9uIGRldGVybWluZXMgaG93IFggcHJlZGljdHMgWQ0KLSBNdWx0aXBsZSAobGluZWFyKSByZWdyZXNzaW9uID0gMSBEViBhbmQgMisgSVYgKGFsc28gc3RyYWlnaHQgbGluZXMpDQotIE11bHRpcGxlIHJlZ3Jlc3Npb24gZGV0ZXJtaW5lcyBob3cgWCx6LCBhbmQgZXRjLCBwcmVkaWN0IFkgW25leHQgd2Vla10NCg0KIyMgQmFzaWMgUmVncmVzc2lvbiBFcXVhdGlvbg0KLSBMaW5lYXIgUmVncmVzc2lvbiBlcXVhdGlvbiB5b3UgbGVhcm5lZCB3aGVuIHlvdW5nZXIgd2FzIHByb2JhYmx5ICR5ID0gTVggKyBiJA0KLSAkeSQgPSBwcmVkaWN0IHZhbHVlDQotICRNJCA9IHNsb3BlDQotICRYJCA9IFZhcmlhYmxlIHVzZWQgdG8gcHJlZGljdCBZDQotICRiJCA9IGludGVyY2VwdA0KDQojIyBNb2Rlcm4gUmVncmVzc2lvbiBFcXVhdGlvbg0KLSAkWT1CX3tZWH1YICsgQl8wICsgZSQNCi0gJFkkID0gcHJlZGljdCB2YWx1ZQ0KLSAkQl97WVh9JCA9IHNsb3BlDQotICRCX3swfSQgPSBpbnRlcmNlcHQNCi0gJGUkID0gZXJyb3IgdGVybSAob2JzZXJ2ZWQgLSBwcmVkaWN0ZWQpLiBBbHNvIGNhbGxlZCB0aGUgcmVzaWR1YWwuDQoNCiMjIEljZSBjcmVhbSBleGFtcGxlDQotIFNwZWNpZnkgdGhlIG1vZGVsIHdpdGggdGhlIGxtIGZ1bmN0aW9uLiANCi0gV2UgYXJlIGdvaW5nIHRvIHByZWRpY3QgaGFwcGluZXNzIHNjb3JlcyBmcm9tIGljZSBjcmVhbSEgDQoNCg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KDQpzY2F0dGVycGxvdChIYXBwaW5lc3N+SWNlQ3JlYW0sIHNtb290aGVyPUZBTFNFKQ0KSGFwcHkuTW9kZWwuMTwtbG0oSGFwcGluZXNzfkljZUNyZWFtLGRhdGEgPSBDb3JyRGF0YSkNCnN1bW1hcnkoSGFwcHkuTW9kZWwuMSkNCg0KYGBgDQoNCg0KIyMjIEludGVyY2VwdA0KLSBgciBIYXBweS5Nb2RlbC4xJGNvZWZmaWNpZW50c1sxXWAgaXMgd2hlcmUgdGhlIGxpbmUgaGl0IHRoZSB5LWludGVyY2VwdCAod2hlbiBoYXBwaW5lc3MgPSAwKS4gDQoNCiMjIyBTbG9wZQ0KLSBgciBIYXBweS5Nb2RlbC4xJGNvZWZmaWNpZW50c1syXWAgaXMgKip0aGUgcmlzZSBvdmVyIHJ1bioqDQotIGZvciBlYWNoIGByIEhhcHB5Lk1vZGVsLjEkY29lZmZpY2llbnRzWzJdYCBjaGFuZ2UgaW4gaWNlIGNyZWFtIHZhbHVlLCB0aGVyZSBpcyBhIGNvcnJlc3BvbmRpbmcgY2hhbmdlIGluIGhhcHBpbmVzcyENCi0gc28gd2UgY2FuIHByZWRpY3QgaGFwcGluZXNzIGZyb20gaWNlIGNyZWFtIHNjb3JlOiANCg0KPiAoYHIgSGFwcHkuTW9kZWwuMSRjb2VmZmljaWVudHNbMl1gICogNSBzcG9vbnMgb2YgaWNlIGNyZWFtICsgYmFzZWxpbmUgaGFwcGluZXNzIGludGVyY2VwdDogYHIgSGFwcHkuTW9kZWwuMSRjb2VmZmljaWVudHNbMV1gKSANCj0gIGByIEhhcHB5Lk1vZGVsLjEkY29lZmZpY2llbnRzWzJdICogNSArSGFwcHkuTW9kZWwuMSRjb2VmZmljaWVudHNbMV1gDQoNCi0gVGhpcyBpcyB5b3VyICpwcmVkaWN0ZWQqIGhhcHBpbmVzcyBzY29yZSBpZiB5b3UgaGFkIDUgc3Bvb25zIG9mIGljZSBjcmVhbQ0KDQojIyMgRXJyb3IgZm9yIHRoaXMgcHJlZGljdGlvbj8NCg0KUiB3aWxsIGFjdHVhbGx5IGRvIGFsbCB0aGUgcHJlZGljdGlvbiBmb3IgdXMgZm9yIGVhY2ggdmFsdWUgb2YgaWNlIGNyZWFtDQpyZXNpZHVhbHMgPSAgKipvYnNlcnZlZCoqIC0gKipwcmVkaWN0ZWQqKg0KDQotIFJlZCBkb3RzID0gKipvYnNlcnZlZCoqICphYm92ZSogcHJlZGljdG9yIGxpbmUNCi0gQmx1ZSBkb3RzID0gKipvYnNlcnZlZCoqICpiZWxvdyogcHJlZGljdG9yIGxpbmUNCi0gdGhlIHN0cm9uZ2VyIHRoZSBjb2xvciB0aGUgbW9yZSBhbiBpbXBhY3QgdGhhdCBwb2ludCBoYXMgaW4gcHVsbGluZyB0aGUgbGluZSBpbiBpdHMgZGlyZWN0aW9uDQotIEhvbGxvdyBkb3RzID0gKipwcmVkaWN0ZWQqKg0KLSBUaGUgZ3JheSBsaW5lcyBhcmUgdGhlIGRpc3RhbmNlICBiZXR3ZWVuICoqb2JzZXJ2ZWQqKiBhbmQgKipwcmVkaWN0ZWQqKiB2YWx1ZXMhDQoNCldoYXQgc2hvdWxkIHRoZSBtZWFuIG9mIHRoZSByZXNpZHVhbHMgZXF1YWw/DQoNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCg0KQ29yckRhdGEkcHJlZGljdGVkIDwtIHByZWRpY3QoSGFwcHkuTW9kZWwuMSkgICAjIFNhdmUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgd2l0aCBvdXIgcmVhbCBkYXRhDQpDb3JyRGF0YSRyZXNpZHVhbHMgPC0gcmVzaWR1YWxzKEhhcHB5Lk1vZGVsLjEpICMgU2F2ZSB0aGUgcmVzaWR1YWwgdmFsdWVzDQoNCg0KbGlicmFyeShnZ3Bsb3QyKSAjIE5vdGU6IGEgbW9yZSBwb3dlcmZ1bCBncmFwaGluZyB0b29sIEkgYW0gZm9yY2VkIHRvIHVzZSB0byBzaG93IHRoaXMgdG8geW91DQpnZ3Bsb3QoZGF0YSA9IENvcnJEYXRhLCBhZXMoeCA9IEljZUNyZWFtLCB5ID0gSGFwcGluZXNzKSkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJsaWdodGdyZXkiKSArICAjIFBsb3QgcmVncmVzc2lvbiBzbG9wZQ0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHJlc2lkdWFscykpICsgICMgQ29sb3IgbWFwcGVkIGhlcmUNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiKSArICAjIENvbG9ycyB0byB1c2UgaGVyZQ0KICBndWlkZXMoY29sb3IgPSBGQUxTRSkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHhlbmQgPSBJY2VDcmVhbSwgeWVuZCA9IHByZWRpY3RlZCksIGFscGhhID0gLjIpICsgICMgYWxwaGEgdG8gZmFkZSBsaW5lcw0KICBnZW9tX3BvaW50KGFlcyh5ID0gcHJlZGljdGVkKSwgc2hhcGUgPSAxKSArDQogIHRoZW1lX2J3KCkgICMgQWRkIHRoZW1lIGZvciBjbGVhbmVyIGxvb2sNCg0KYGBgDQoNCiMjIyBPcmRpbmFyeSBsZWFzdCBzcXVhcmVzIChPTFMpDQotIExpbmVhciByZWdyZXNzaW9uIGZpbmRzIHRoZSBiZXN0IGZpdCBsaW5lIGJ5IHRyeWluZyB0byBtaW5pbWl6ZSB0aGUgc3VtIG9mIHRoZSBzcXVhcmVzIG9mIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBvYnNlcnZlZCByZXNwb25zZXMgdGhvc2UgcHJlZGljdGVkIGJ5IHRoZSBsaW5lLiANCi0gT0xTIGNvbXB1dGF0aW9uYWxseSBzaW1wbGUgdG8gZ2V0IHRoZSBzbG9wZSB2YWx1ZSwgYnV0IGlzIGFjdHVhbGx5IGluYWNjdXJhdGUgDQoNCiQkQl97WVh9PVxmcmFje1xzdW17WFl9LVxmcmFjezF9e259XHN1bXtYfVxzdW17WX19e1xzdW17eF4yfS1cZnJhY3sxfXtufVxzdW17eH1eMn0gPSBcZnJhY3tDb3Zfe1hZfX17dmFyX3h9JCQNCg0KLSBNb2Rlcm4gbWV0aG9kcyB1c2UgYW4gYWx0ZXJuYXRpdmUgKE1MLCBSRU1MKSB3ZSB3aWxsIGV4YW1pbmUgbGF0ZXIgd2hlbiB3ZSBnZXQgdG8gR0xNDQoNCg0KIyMgU0Ugb24gdGhlIHRlcm1zIGluIHRoZSBtb2RlbHMgKGhvdyBnb29kIGlzIHRoZSBmaXQ/KQ0KLSBSZXNpZHVhbCBTdGFuZGFyZCBlcnJvciA9ICRcc3FydFxmcmFje1xzdW17ZV4yfX17bi0yfSQNCi0gaW4gUiBsYW5ndWFnZTogDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpuPWxlbmd0aChDb3JyRGF0YSRyZXNpZHVhbHMpDQoNClJTRSA9IHNxcnQoc3VtKENvcnJEYXRhJHJlc2lkdWFsc14yKSAvIChuLTIpKQ0KUlNFDQoNCmBgYA0KDQotIFNvIG91ciBlcnJvciBvbiB0aGUgcHJlZGljdGlvbiBpcyBgciBSU0VgIGhhcHBpbmVzcyBwb2ludHMgYmFzZWQgb24gb3VyIG1vZGVsLiAgDQoNCiMjIyBTRSBvbiB0aGUgSW50ZXJjZXB0DQotIEludGVyY2VwdCBTdGFuZGFyZCBlcnJvciA9ICRSU0Vcc3FydCB7XGZyYWN7MX17bn0rXGZyYWN7TV94XjJ9eyhuLTEpdmFyX3h9fSQNCi0gaW4gUiBsYW5ndWFnZTogDQogIA0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCklTRSA9IFJTRSooc3FydCggMSAvIG4gKyBtZWFuKENvcnJEYXRhJEljZUNyZWFtKV4yIC8gKG4gLSAxKSp2YXIoQ29yckRhdGEkSWNlQ3JlYW0pKSkNCklTRQ0KYGBgDQoNCiMjIyBTRSBvbiB0aGUgU2xvcGUNCi0gSW50ZXJjZXB0IFN0YW5kYXJkIGVycm9yID0gJFxmcmFje3NkX3l9e3NkX3h9XHNxcnR7XGZyYWN7MSAtIHJfWVheMn17bi0yfX0kDQotIGluIFIgbGFuZ3VhZ2U6IA0KICANCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQoNCiNsZXRzIGV4dHJhY3QgdGhlIHIyIGZyb20gdGhlIG1vZGVsDQpyMi5tb2RlbDwtc3VtbWFyeShIYXBweS5Nb2RlbC4xKSRyLnNxdWFyZWQNCg0KU1NFID0gc2QoQ29yckRhdGEkSGFwcGluZXNzKS9zZChDb3JyRGF0YSRJY2VDcmVhbSkgKiBzcXJ0KCgxLSByMi5tb2RlbCkvIChuIC0gMikpDQpTU0UNCmBgYA0KDQojIyMgdC10ZXN0cyBvbiBzbG9wZSBhbmQgaW50ZXJjZXB0IGFuZCAkcl4yJCB2YWx1ZQ0KLSBWYWx1ZXMgYXJlIHRlc3RlZCBhZ2FpbnN0IDAsIHNvIGl0cyBhbGwgb25lIHNhbXBsZSB0LXRlc3RzDQotIHNsb3BlOiAkdCA9IFxmcmFje0Jfe1lYfSAtIEhfMH17U0Vfe0Jfe1lYfX19JA0KLSBpbnRlcmNlcHQ6ICR0ID0gXGZyYWN7Ql97MH0gLSBIXzB9e1NFX3tCX3swfX19JA0KDQokcl4yJCBpcyBhIGxpdHRsZSBkaWZmZXJlbnQgYXMgaXRzIGEgY29ycmVsYXRpb24gdmFsdWUNCg0KLSBjb3JyZWxhdGlvbnMgYXJlIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZA0KLSBGaXNoZXIgY3JlYXRlZCBhIGNvbnZlcnNpb24gZm9yIHIgdG8gbWFrZSBpdCBhIHogKGNhbGxlZCBGaXNoZXJzJyAkciQgdG8gJFokKQ0KLSAkcl4yJDogJHQgPSBcZnJhY3tyX3tYWX1cc3FydHtuLTJ9LUhfMH17XHNxcnR7MS1yX3tYWX1eMn19JCAsIHdoZXJlIGRmID0gbiAtIDINCi0gaXRzIG9mdGVuIGdpdmVuIGZvciBhcyBhbiBGIHZhbHVlLCByZW1lbWJlciAkdCA9IEZeMiQgDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQojaW50ZXJjZXB0DQp0Lkk9IEhhcHB5Lk1vZGVsLjEkY29lZmZpY2llbnRzWzFdL0lTRQ0KdC5JDQojU2xvcGUNCnQuUz0gSGFwcHkuTW9kZWwuMSRjb2VmZmljaWVudHNbMl0vU1NFDQp0LlMNCg0KIyBGb3Igci1zcXVhcmVkDQp0LnIyeHkgPSByMi5tb2RlbF4uNSpzcXJ0KG4tMikvc3FydCgxLXIyLm1vZGVsKQ0KRi5yMnh5ID0gdC5yMnh5XjINCkYucjJ4eQ0KDQpgYGANCg0KTm90ZTogV2UgYXJlIHRlc3RpbmcgbnVsbCBoeXBvdGhlc2lzIHZhbHVlIGZvciBzbG9wZSwgaS5lLiwgbnVsbCA9IDAuIEJ1dCBpdCdzIGEgdGVycmlibGUgZ3Vlc3MuIEV2ZXJ5dGhpbmcgY29ycmVsYXRlcyB3aXRoIGV2ZXJ5dGhpbmcsIHNvIGl0J3MgaW1wb3J0YW50IHRvIGtlZXAgdGhpcyBpbiBtaW5kIG1vdmluZyBmb3J3YXJkLiBTbyB0aGF0IHdvdWxkIGJlIHRoZSBOSUxMIGh5cG90aGVzaXMuIE5JTEwgY2FuIGJlIHRlc3RlZCBiZXR0ZXIgd2l0aCBib290c3RyYXBwaW5nLiANCg0KDQojIFBvd2VyIGFuZCBSZWdyZXNzaW9uDQotIFdlIHN0dWRpZWQgcG93ZXIgbGFzdCBzZW1lc3Rlcg0KLSBJIHdpbGwgc2tpcCBhaGVhZCB0byBjYWxjdWxhdGluZyBpdCBpbiBSLiANCi0gZm9yIHJlZ3Jlc3Npb24sIHdlIHdpbGwgbmVlZCB0byBjb252ZXJ0IG91ciAkcl4yJCBpbnRvIGNvaGVuJ3MgJGZeMiQNCi0gJGZeMiA9IFxmcmFje3JeMn17MS1yXjJ9JA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShwd3IpICNwb3dlciBhbmFseXNpcw0KI3Bvd2VyIGZvciBHTE0NCiMgdQkgPSBkZWdyZWVzIG9mIGZyZWVkb20gZm9yIG51bWVyYXRvcg0KIyB2CT0gZGVncmVlcyBvZiBmcmVlZG9tIGZvciBkZW5vbWluYXRvcg0KIyBmMiA9IGVmZmVjdCBzaXplDQojIHNpZy5sZXZlbD0gKFR5cGUgSSBlcnJvciBwcm9iYWJpbGl0eSkNCiMgcG93ZXIgPSAoMSBtaW51cyBUeXBlIElJIGVycm9yIHByb2JhYmlsaXR5KQ0KZjIuaWNlY3JlYW0gPC0gcl4yIC8gKDEtcl4yKQ0KDQpwd3IuZjIudGVzdCh1ID0gMSwgdiA9IG4tMiwgZjIgPSBmMi5pY2VjcmVhbSwgc2lnLmxldmVsID0gMC4wNSwgcG93ZXIgPSBOVUxMKQ0KYGBgDQoNClNvIHdlIGhhZCBhIHBvd2VyIG9mIGJhc2ljYWxseSAxIGdpdmVuIHRoaXMgc2FtcGxlIHNpemUgYW5kIG91ciB0cnVlIGVmZmVjdCBzaXplIG9mIGByIHJgDQoNCg0KIyBGaW5hbCBOb3RlcyANCi0gVGVzdGluZyBiZXR3ZWVuIGNvcnJlbGF0aW9ucyB1c2luZyBvbGQgZmFzaGlvbiBGaXNoZXIncyB0ZXN0IGlzIG9sZCBmYXNoaW9uIChDb2hlbiBldCBhbC4sIHAuIDQ5KS4gVGhlIG1vZGVybiBhcHByb2FjaCBpcyB0aGUgYm9vdHN0cmFwLCBhcyB0aGUgb2xkIG1ldGhvZCBpcyB1bmRlci1wb3dlcmVkLiANCi0gWW91ciBib29rIGlzIGxpdHRsZSBvdXQgb2YgZGF0ZTogQ0lzIGFyZSBiZXR0ZXIgYnV0IGJvb3RzdHJhcHBlZCBDSXMgYXJlIGJlY29taW5nIG1vcmUgc3RhbmRhcmQuIA0KDQoNCg0KPHNjcmlwdD4NCiAgKGZ1bmN0aW9uKGkscyxvLGcscixhLG0pe2lbJ0dvb2dsZUFuYWx5dGljc09iamVjdCddPXI7aVtyXT1pW3JdfHxmdW5jdGlvbigpew0KICAoaVtyXS5xPWlbcl0ucXx8W10pLnB1c2goYXJndW1lbnRzKX0saVtyXS5sPTEqbmV3IERhdGUoKTthPXMuY3JlYXRlRWxlbWVudChvKSwNCiAgbT1zLmdldEVsZW1lbnRzQnlUYWdOYW1lKG8pWzBdO2EuYXN5bmM9MTthLnNyYz1nO20ucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoYSxtKQ0KICB9KSh3aW5kb3csZG9jdW1lbnQsJ3NjcmlwdCcsJ2h0dHBzOi8vd3d3Lmdvb2dsZS1hbmFseXRpY3MuY29tL2FuYWx5dGljcy5qcycsJ2dhJyk7DQoNCiAgZ2EoJ2NyZWF0ZScsICdVQS05MDQxNTE2MC0xJywgJ2F1dG8nKTsNCiAgZ2EoJ3NlbmQnLCAncGFnZXZpZXcnKTsNCg0KPC9zY3JpcHQ+DQo=