1 What does Linearity & Non-Linearity mean?

  • The majority of psychologists often use these terms to describe the relationship between Y~X
  • Linearity = mathematical relationship between variables can be fit with a straight line
  • Non-Linear (Curvilinear) = mathematical relationship between variables can be fit with non-straight (Curvy) lines
  • However, be careful as for other fields or specialty areas of psychology these terms can mean different things: When people say non-linear, as them what they mean
  • We will use non-linear to describe the mathematical relationship between variables

2 Non-Linear Fit Types that can be tranformed for linear regression (basic types)

  • Power & Orthogonal Polynomials
  • Growth: Exponential, Power, logarithmic
  • Rates: Reciprocal
  • Correlations

2.1 Power Polynomials

  • Models that simply curves, such as quadratic or cubic effects
  • Linear: \(Y =B_{1}X + B_0\)
  • Quadratic: \(Y = B_{1.2}X + B_{2.1}X^2 + B_0\)
  • Cubic: \(Y = B_{1.23}X + B_{2.13}X^2 + B_{3.12}X^3 + B_0\)
  • Let simulate a relationship ExamScore ~ Anxiety level
  • Our relationship will be defined as: \(Y = 17.5X -2.5X^2 + 40 + \epsilon\)
  • Error term, $= $ Random Normal with Mean[0] & SD[10]
library(car) #graph data
set.seed(42)
# 250 people
n <- 250
# Uniform distrobution of likert scores between 0 to 7 (0 = No anxiety)
x <- runif(n, 0, 7)
# Our equation to  create Y
y <- -2.5*x^2 + 17.5*x+40+rnorm(n, sd=10)
#Built our data frame
Quad.Data<-data.frame(Anxiety=x,ExamScore=y)

# Plot: Notes: reg.line=FALSE means remove best fit line. 
# smoother=loessLine means add smoothed line called the loess
scatterplot(ExamScore~Anxiety, data= Quad.Data, reg.line=FALSE, smoother=loessLine)

2.1.1 The loess Line

  • Locally Weighted Scatterplot Smoothing
  • Does lots of regressions on small sections of the scatter plot to get a best fit (curvy) line
  • Lets you visualize complex relationships, but it can overfit the relationship (so be careful)
  • Good diagnostic tool, but rarely used in papers

2.2 Process of fitting Power Polynomials

  • Forward Selection Approach
  • Model 1: Linear fit
  • Model 2: Quadratic fit
  • Model i: keep going up until you are satisfied
  • Check the change in \(R^2\) and select the best fit models
  • Note: Higher order terms ALWAYS increase \(R^2\), you need to make sure it is both a significant improvement and meaningful improvement
  • in R there are two ways to add Power Polynomials: I(Term^2) or using the poly command
#Linear model
Model.1<-lm(ExamScore~Anxiety, data= Quad.Data)
Model.2<-lm(ExamScore~Anxiety+I(Anxiety^2), data= Quad.Data)

anova(Model.1,Model.2)
## Analysis of Variance Table
## 
## Model 1: ExamScore ~ Anxiety
## Model 2: ExamScore ~ Anxiety + I(Anxiety^2)
##   Res.Df   RSS Df Sum of Sq      F    Pr(>F)    
## 1    248 43354                                  
## 2    247 21319  1     22036 255.31 < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
library(stargazer)
stargazer(Model.1,Model.2,type="html",
          column.labels = c("Linear", "Quadratic"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE)
Dependent variable:
ExamScore
Linear Quadratic
(1) (2)
Constant 60.333*** 39.681***
(1.686) (1.753)
Anxiety -0.137 17.753***
(0.411) (1.156)
I(Anxiety2) -2.559***
(0.160)
Observations 250 250
R2 0.0004 0.508
Adjusted R2 -0.004 0.505
Residual Std. Error 13.222 (df = 248) 9.290 (df = 247)
F Statistic 0.111 (df = 1; 248) 127.767*** (df = 2; 247)
Note: p<0.1; p<0.05; p<0.01
  • The change in \(R^2\) = 0.5080438 and the we can see that the change was significant
  • Would the third order term be any better?
#Linear model
Model.3<-lm(ExamScore~Anxiety+I(Anxiety^2)+I(Anxiety^3), data= Quad.Data)

anova(Model.1,Model.2,Model.3)
## Analysis of Variance Table
## 
## Model 1: ExamScore ~ Anxiety
## Model 2: ExamScore ~ Anxiety + I(Anxiety^2)
## Model 3: ExamScore ~ Anxiety + I(Anxiety^2) + I(Anxiety^3)
##   Res.Df   RSS Df Sum of Sq        F Pr(>F)    
## 1    248 43354                                 
## 2    247 21319  1   22035.8 255.0382 <2e-16 ***
## 3    246 21255  1      63.8   0.7378 0.3912    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
library(stargazer)
stargazer(Model.1,Model.2,Model.3,type="html",
          column.labels = c("Linear", "Quadratic","Cubic"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE)
Dependent variable:
ExamScore
Linear Quadratic Cubic
(1) (2) (3)
Constant 60.333*** 39.681*** 38.386***
(1.686) (1.753) (2.312)
Anxiety -0.137 17.753*** 20.023***
(0.411) (1.156) (2.885)
I(Anxiety2) -2.559*** -3.371***
(0.160) (0.959)
I(Anxiety3) 0.077
(0.090)
Observations 250 250 250
R2 0.0004 0.508 0.510
Adjusted R2 -0.004 0.505 0.504
Residual Std. Error 13.222 (df = 248) 9.290 (df = 247) 9.295 (df = 246)
F Statistic 0.111 (df = 1; 248) 127.767*** (df = 2; 247) 85.333*** (df = 3; 246)
Note: p<0.1; p<0.05; p<0.01
  • So second order power polynomial was our best fit

2.2.1 Lets examine Model 2 in detail

  • Since we simulated our model we know what the answers should be
  • Simulation: \(Y = 17.5X -2.5X^2 + 40 + \epsilon\)
  • Lets get our terms from our regression:
LT<-round(Model.2$coefficients[2],2)
QT<-round(Model.2$coefficients[3],2)
IT<-round(Model.2$coefficients[1],2)
  • Regression: \(Y =\) 17.75\(X\) -2.56\(X^2 +\) 39.68 \(+ \epsilon\)
  • Our regression did a good job, but what if our sample was smaller?
  • Let redo our analysis with n = 5 to 60 and see how the terms change [see simulation in separate R file]
  • the gray band = 95% CI

  • Its important to know that in small samples polynomial estimates can be very unstable
  • if you flip the quadratic term the line will flip direction

2.2.2 How to plot this function (and not the loess line)

  • Literally you can solve the regression equation for different values of x
  • in R you can call the effects package to do work for you
library(effects)
plot(effect("Anxiety", Model.2,
            xlevels=list(Anxiety=0:7)))

2.3 Orthogonal Polynomials

  • Problem with power polynomials is interpreting them independently
  • The higher order terms depend on the lower order terms
  • Thus linear term can be significant cause the higher order term is significant, but actually there is no linear trend in the data (just like in today’s example)
  • This is because power polynomials correlate with each other (they are are not unique predictors)
  • For Example:
LinearTerms<-c(1,2,3,4,5,6,7)
QuadTerms<-LinearTerms^2
round(cor(LinearTerms,QuadTerms),3)
## [1] 0.977
  • Thus they are basically multicollinear
  • lets run our VIF to verify
vif(Model.2)
##      Anxiety I(Anxiety^2) 
##     16.05275     16.05275
  • Technically, its OK that they are multicollinear but you cannot interpret the Pvalues on each term
  • The solution to make the linear and quadratic terms correlate with each at zero (just as we did with ANOVA)
  • we can use the poly code in R.
  • Here is an example of what R is going to do:
LinearTerms<-c(1,2,3,4,5,6,7)

#make them Orthogonal
O.Poly<-poly(LinearTerms,2,simple = TRUE)
round(O.Poly,5)
##             1        2
## [1,] -0.56695  0.54554
## [2,] -0.37796  0.00000
## [3,] -0.18898 -0.32733
## [4,]  0.00000 -0.43644
## [5,]  0.18898 -0.32733
## [6,]  0.37796  0.00000
## [7,]  0.56695  0.54554
#run our correlation
round(cor(O.Poly[,1],O.Poly[,2]),3)
## [1] 0
#lets make a new poly vector
Quad.Data$Anxiety.poly.1<-poly(Quad.Data$Anxiety,1,simple = TRUE)[,1]
Quad.Data$Anxiety.poly.2<-poly(Quad.Data$Anxiety,2,simple = TRUE)[,2]

Model.1.O<-lm(ExamScore~Anxiety.poly.1, data= Quad.Data)
Model.2.O<-lm(ExamScore~Anxiety.poly.1+Anxiety.poly.2, data= Quad.Data)

anova(Model.1.O,Model.2.O)
## Analysis of Variance Table
## 
## Model 1: ExamScore ~ Anxiety.poly.1
## Model 2: ExamScore ~ Anxiety.poly.1 + Anxiety.poly.2
##   Res.Df   RSS Df Sum of Sq      F    Pr(>F)    
## 1    248 43354                                  
## 2    247 21319  1     22036 255.31 < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
stargazer(Model.1.O,Model.2.O,type="html",
          column.labels = c("Linear.Ortho", "Quadratic.Ortho"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE)
Dependent variable:
ExamScore
Linear.Ortho Quadratic.Ortho
(1) (2)
Constant 59.845*** 59.845***
(0.836) (0.588)
Anxiety.poly.1 -4.403 -4.403
(13.222) (9.290)
Anxiety.poly.2 -148.444***
(9.290)
Observations 250 250
R2 0.0004 0.508
Adjusted R2 -0.004 0.505
Residual Std. Error 13.222 (df = 248) 9.290 (df = 247)
F Statistic 0.111 (df = 1; 248) 127.767*** (df = 2; 247)
Note: p<0.1; p<0.05; p<0.01
  • Notice that the linear term is not significant, but the quadratic is
  • BUT the actual estimate of the slopes are nonsensical
  • this is because you have rescaled the xvalues in a funny way
  • But no more multicollinearity
  • lets run our VIF to verify
vif(Model.2.O)
## Anxiety.poly.1 Anxiety.poly.2 
##              1              1
  • Also the \(R^2\) do not differ between the power or orthogonal polynomials
  • Other pros of using orthogonal polynomials will come next week when we start looking at interactions

2.3.1 Coding short cut (Use this for your analysis)

  • You don’t need to premake the vector
  • You can put the poly code right into the model
  • but the labeling will change
  • Also much easier to makes plots of the final model this way
Model.2.O.S<-lm(ExamScore~poly(Anxiety,2), data= Quad.Data)
stargazer(Model.2.O.S,type="html",
          column.labels = c("Quadratic.Ortho"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE)
Dependent variable:
ExamScore
Quadratic.Ortho
Constant 59.845***
(0.588)
poly(Anxiety, 2)1 -4.403
(9.290)
poly(Anxiety, 2)2 -148.444***
(9.290)
Observations 250
R2 0.508
Adjusted R2 0.505
Residual Std. Error 9.290 (df = 247)
F Statistic 127.767*** (df = 2; 247)
Note: p<0.1; p<0.05; p<0.01
plot(effect("poly(Anxiety,2)", Model.2.O.S,
           xlevels=list(Anxiety=0:7)))

  • Also, you could have said poly(Anxiety,2,raw=TRUE) to get power polynomial on the fly

2.4 Diagnotics

  • When you have the wrong number of polynomials you will get funny looking residuals
  • Here is when we use 1, when we need 2
plot(Model.1, which=1)

  • When you have the right number of polynomials you will get correct looking residuals
  • Here is when we use 2, when we need 2
plot(Model.2.O.S, which=1)

3 Dealing Growth models

  • These are models that cannot be fit with polynomials
  • Exponential growth: \(Y = ce^{dx}\)
  • Severity of panic attack (0 to 500) predicted by anxiety level
  • Exponential Example: \(Y = .09e^{1.2x+\epsilon}\)
  • Note: Euler’s number, \(e =\) 2.7182818, is an irrational math constant and the base value of the natural log
set.seed(42)
n <- 250
x <- runif(n, 0, 7)
#lets make our e value (not the error term)
e<-exp(1)
y <-.09*e^(1.20*(x+rnorm(n, sd=.25)))
Exp.Data<-data.frame(Anxiety=x,PanicAttack=y)
scatterplot(PanicAttack~Anxiety, data= Exp.Data, reg.line=FALSE, smoother=loessLine)

3.1 Do Polynomials fail us?

  • Lets try a second order fit since it one curve
Model.Panic.Poly<-lm(PanicAttack~poly(Anxiety,2), data= Exp.Data)
stargazer(Model.Panic.Poly,type="html",
          column.labels = c("Quadratic.Ortho"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE)
Dependent variable:
PanicAttack
Quadratic.Ortho
Constant 50.000***
(2.515)
poly(Anxiety, 2)1 1,006.757***
(39.762)
poly(Anxiety, 2)2 772.384***
(39.762)
Observations 250
R2 0.805
Adjusted R2 0.803
Residual Std. Error 39.762 (df = 247)
F Statistic 509.199*** (df = 2; 247)
Note: p<0.1; p<0.05; p<0.01
  • Using the poly right into the package can be useful for the effects package because we can now view independent impact of the linear and quadratic terms
plot(effect("poly(Anxiety,2)", Model.Panic.Poly,
           xlevels=list(Anxiety=0:7)))

- Yikes, based on this you might tell people having zero anxiety is worse then having lower anxiety

  • Let check the residuals
plot(Model.Panic.Poly, which=1)

  • Also that looks pretty odd

3.2 Transformations of Exponential Model

  • The solution to this problem is often to transform the offending variable
  • The type of transform depends on the type of growth model you have
  • Exponential model: Dependent variable = log(y) [in r, log = natural log]
  • Thus: \(log(Y) = B_0 + BX\)
  • And to convert from log into meaningful values:
  • \(Y = e^{B_0 + BX}\)
  • Lets look at Log(Y) by Anxiety
scatterplot(log(PanicAttack)~Anxiety, data= Exp.Data)

  • Looks like a line now
  • Lets look at the model fit
Model.Panic.Log<-lm(log(PanicAttack)~Anxiety, data= Exp.Data)
stargazer(Model.Panic.Log,type="html",
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE)
Dependent variable:
log(PanicAttack)
Constant -2.403***
(0.035)
Anxiety 1.195***
(0.009)
Observations 250
R2 0.987
Adjusted R2 0.987
Residual Std. Error 0.278 (df = 248)
F Statistic 19,129.080*** (df = 1; 248)
Note: p<0.1; p<0.05; p<0.01
  • Let plot the fit
plot(effect("Anxiety", Model.Panic.Log,
           xlevels=list(Anxiety=0:7)))

  • or you plot it non-log units
  • You have to tell the package to covert it over
plot(effect("Anxiety", Model.Panic.Log,
          transformation=list(link=log, inverse=exp)))

  • Let plot the fit
  • Let check the residuals
plot(Model.Panic.Log, which=1)

  • Those look as they should

3.2.1 Challange of Transforms

  • Problem is our scores are in log (not raw score)
  • to convert from log into meaningful values:
  • \(Y = e^{B_0 + BX}\)
#intercept
IT.L<-round(Model.Panic.Log$coefficients[1],2)
#slope
LT.L<-round(Model.Panic.Log$coefficients[2],2)
  • \(Y = e\)^(-2.4 + 1.2X)
  • For Anxiety score of X = 0, Y = 0.090718
  • For Anxiety score of X = 4, Y = 11.0231764
  • For Anxiety score of X = 7, Y = 403.4287935
  • [Note: Be careful about which log you use (natural or base 10, you can use either but make sure to specify)]

3.3 Power law

  • Common function in motor control and perception experiments
  • \(Y = cX^d\)
  • Severity of sweating (0 to 100) predicted by anxiety level
  • Exponential Example: \(Y = .07(X)^{1.7}+\epsilon\)
  • Note: Natural log of 0 = -inf, so if you have zeros you need to always add 1
set.seed(42)
n <- 250
x <- runif(n, 0, 7)+1
y <-.07*(x)^1.7
y = y * (.05* (5+rnorm(n, sd=.25)))
Power.Data<-data.frame(Anxiety=x,Sweating=y)
scatterplot(Sweating~Anxiety, data= Power.Data, reg.line=FALSE, smoother=loessLine)

3.3.1 Transformations of Power Law

  • The solution to this problem is often to transform the offending variable
  • Power law model: DV and IV get logged: log(y) & log(x)
  • Thus: \(log(Y) = B_0 + B(logX)\)
  • And to convert from log into meaningful values:
  • \(Y = e^{B_0 + B(logX)}\)
  • Lets look at the log-log scatterplot
scatterplot(log(Sweating)~log(Anxiety), data= Power.Data)

Model.Sweat.LL<-lm(log(Sweating)~log(Anxiety), data= Power.Data)

-Looks like a line now -Lets look at the model fit

stargazer(Model.Sweat.LL,type="html",
          column.labels = c("Log-Log Transform"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE)
Dependent variable:
log(Sweating)
Log-Log Transform
Constant -4.046***
(0.008)
log(Anxiety) 1.698***
(0.005)
Observations 250
R2 0.998
Adjusted R2 0.998
Residual Std. Error 0.046 (df = 248)
F Statistic 100,505.500*** (df = 1; 248)
Note: p<0.1; p<0.05; p<0.01
  • Let plot the fit and tell the package that you did a transform
plot(effect("Anxiety", Model.Sweat.LL,
           transformation=list(link=log, inverse=exp)))

  • Let check the residuals
plot(Model.Sweat.LL, which=1)

  • They look OK

3.4 Logarithmic

  • These functions are often physical (sound intensity, earthquakes, etc)
  • \(c^Y = dX\)
  • Breathing rate (in ms between breaths) predicted by anxiety level
  • Logarithmic Example: \(Y = .05(X*\epsilon)^{3.8}\)
set.seed(42)
n <- 250
x <- runif(n, 0, 7)+1
y <-200*log(x*(5+rnorm(n, sd=1)))
Logarithmic.Data<-data.frame(Anxiety=x,Breathing=y)
scatterplot(Breathing~Anxiety, data= Logarithmic.Data, reg.line=FALSE, smoother=loessLine)

scatterplot(Breathing~log(Anxiety), data= Logarithmic.Data)

Model.Breath.Log<-lm(Breathing~log(Anxiety), data= Logarithmic.Data)

-Looks like a line now -Lets look at the model fit

stargazer(Model.Breath.Log,type="html",
          column.labels = c("Log Transform"),
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE)
Dependent variable:
Breathing
Log Transform
Constant 319.641***
(6.656)
log(Anxiety) 197.847***
(4.456)
Observations 250
R2 0.888
Adjusted R2 0.888
Residual Std. Error 38.680 (df = 248)
F Statistic 1,971.251*** (df = 1; 248)
Note: p<0.1; p<0.05; p<0.01
  • Let plot the fit
plot(effect("Anxiety", Model.Breath.Log))

  • Let check the residuals
plot(Model.Breath.Log, which=1)

3.5 Reciprocals

  • Common fix for rate data (things involving time)
  • \(1/Y\)
  • Adrenaline level in the blood predicted by mins after a panic attack (mins)
  • Reciprocal Example: \(Y = (1/X)*\epsilon\)
set.seed(42)
n <- 250
# Length of panic attack
x <- 15*(runif(n, 1, 4))
y <- 1/(x)*rnorm(n,5,sd=.1)
Reciprocal.Data<-data.frame(PostPanic=x,Adrenaline=y)
scatterplot(Adrenaline~PostPanic, data= Reciprocal.Data, reg.line=FALSE, smoother=loessLine)

scatterplot(1/Adrenaline~PostPanic, data= Reciprocal.Data)

  • Looks like a line now
  • Lets look at the model fit
Model.Calm.Rep<-lm(1/Adrenaline~PostPanic, data= Reciprocal.Data)
stargazer(Model.Calm.Rep,type="latex",
          intercept.bottom = FALSE,
          single.row=FALSE, 
          notes.append = FALSE,          
          header=FALSE)
  • Let plot the fit
plot(effect("PostPanic", Model.Calm.Rep))

  • Let check the residuals
plot(Model.Calm.Rep, which=1)

  • A little off (but I add in noise in a simple way)

3.6 Wierd Bulges in the data (not full on curves)

  • Data do not always fit our nice predefined functions
  • Sometimes they clearly have bulges, but are we sure those are real?
  • Bulges can be not real in small samples or can results from some latent factor you are not accounting for
  • Lets assume that if the data has a bulge that is real, meaningful, and we need to account for it
  • We can use the Box-Cox Transform (fits different polynomials which we call \(\lambda\))
  • \(Y = Y^\lambda - 1 / \lambda\), where \(\lambda \neq 0\) OR \(Y = lnY\), where \(\lambda = 0\)
  • In this case, we will allow \(\lambda\) to be selected based on which makes the data most normal
  • Lets make a case with a weird power polynomial of 1.3 (something you would not notice in your raw data)
set.seed(42)
# 250 people
n <- 250
x <- runif(n, 0, 7)
y <- -4*x^1.3+ 20*x+rnorm(n, sd=2)+20
SortaQuad.Data<-data.frame(Anxiety=x,ExamScore=y)
scatterplot(ExamScore~Anxiety, data= SortaQuad.Data, reg.line=FALSE, smoother=loessLine)

  • Lets try a linear model
Model.SortaQuad<-lm(ExamScore~Anxiety, data= SortaQuad.Data)
plot(Model.SortaQuad, which=1)

  • Seems there is a slight curve in our data
library(MASS)
bc<-boxcox(ExamScore ~Anxiety, data = SortaQuad.Data,
       lambda = seq(-2, 2, len = 20))

#we can extract the lambda
(lambda <- bc$x[which.max(bc$y)])
## [1] 1.272727
  • Lets plot the scatter and residuals from our transformed DV
scatterplot(ExamScore^lambda~Anxiety, data= SortaQuad.Data, reg.line=FALSE, smoother=loessLine)

Model.SortaQuad.BC<-lm(ExamScore^lambda ~ Anxiety, data= SortaQuad.Data)
plot(Model.SortaQuad.BC, which=1)

3.6.1 Some issues

  • Box-Cox is guess and requires some fine turning (like adjusting the range of lambdas)
  • You have to keep track of your zeros in the DV (so you can add constants to fix it)
  • Most importantly, you have make it harder to make sense of the DV (but your fits will be far better using it)

4 Doing regression with correlation values as the DV

  • You should not just use the correlation values straight away
  • Correlations are NOT normal, but luckily Fisher give us a fix
  • Fisher’s r to Z
  • You convert your correlation values to Z scores, do your analysis, predict your values and convert them back to r values for graphing
  • see Demos, et al (2012) for an example.
  • Functions below you can use to transform
FisherRtoZ <-function (z) {
  (exp(2 * z) - 1)/(1 + exp(2 * z))
}

FisherZtoR <-function (z) {
  tanh(z)
}
LS0tDQp0aXRsZTogJ05vbi1MaW5lYXIgTW9kZWxzJw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGZvbnRzaXplOiA4cHQNCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQotLS0NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9NSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcuaGVpZ2h0PTMuNzUpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmFsaWduPSdjZW50ZXInKSANCmBgYA0KDQojIFdoYXQgZG9lcyBMaW5lYXJpdHkgJiBOb24tTGluZWFyaXR5IG1lYW4/DQotIFRoZSBtYWpvcml0eSBvZiBwc3ljaG9sb2dpc3RzIG9mdGVuIHVzZSB0aGVzZSB0ZXJtcyB0byBkZXNjcmliZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gWX5YDQotIExpbmVhcml0eSA9IG1hdGhlbWF0aWNhbCByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXMgY2FuIGJlIGZpdCB3aXRoIGEgKnN0cmFpZ2h0KiBsaW5lDQotIE5vbi1MaW5lYXIgKEN1cnZpbGluZWFyKSA9IG1hdGhlbWF0aWNhbCByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXMgY2FuIGJlIGZpdCB3aXRoICpub24tc3RyYWlnaHQqICgqKkN1cnZ5KiopIGxpbmVzDQotIEhvd2V2ZXIsIGJlIGNhcmVmdWwgYXMgZm9yIG90aGVyIGZpZWxkcyBvciBzcGVjaWFsdHkgYXJlYXMgb2YgcHN5Y2hvbG9neSB0aGVzZSB0ZXJtcyBjYW4gbWVhbiBkaWZmZXJlbnQgdGhpbmdzOiBXaGVuIHBlb3BsZSBzYXkgbm9uLWxpbmVhciwgYXMgdGhlbSB3aGF0IHRoZXkgbWVhbg0KLSBXZSB3aWxsIHVzZSBub24tbGluZWFyIHRvIGRlc2NyaWJlIHRoZSBtYXRoZW1hdGljYWwgcmVsYXRpb25zaGlwIGJldHdlZW4gdmFyaWFibGVzIA0KDQojIE5vbi1MaW5lYXIgRml0IFR5cGVzIHRoYXQgY2FuIGJlIHRyYW5mb3JtZWQgZm9yIGxpbmVhciByZWdyZXNzaW9uIChiYXNpYyB0eXBlcykNCi0gUG93ZXIgJiBPcnRob2dvbmFsIFBvbHlub21pYWxzDQotIEdyb3d0aDogIEV4cG9uZW50aWFsLCBQb3dlciwgbG9nYXJpdGhtaWMgIA0KLSBSYXRlczogUmVjaXByb2NhbA0KLSBDb3JyZWxhdGlvbnMNCg0KDQojIyBQb3dlciBQb2x5bm9taWFscyANCi0gTW9kZWxzIHRoYXQgc2ltcGx5IGN1cnZlcywgc3VjaCBhcyBxdWFkcmF0aWMgb3IgY3ViaWMgZWZmZWN0cyAgDQotIExpbmVhcjogJFkgPUJfezF9WCArIEJfMCQNCi0gUXVhZHJhdGljOiAkWSA9IEJfezEuMn1YICsgQl97Mi4xfVheMiArIEJfMCQNCi0gQ3ViaWM6ICRZID0gQl97MS4yM31YICsgQl97Mi4xM31YXjIgKyBCX3szLjEyfVheMyArIEJfMCQNCi0gTGV0IHNpbXVsYXRlIGEgcmVsYXRpb25zaGlwIEV4YW1TY29yZSB+IEFueGlldHkgbGV2ZWwgDQotIE91ciByZWxhdGlvbnNoaXAgd2lsbCBiZSBkZWZpbmVkIGFzOiAkWSA9IDE3LjVYIC0yLjVYXjIgKyA0MCArIFxlcHNpbG9uJA0KLSBFcnJvciB0ZXJtLCAkXGVwc2lsb24gPSAkIFJhbmRvbSBOb3JtYWwgd2l0aCBNZWFuWzBdICYgU0RbMTBdDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoY2FyKSAjZ3JhcGggZGF0YQ0Kc2V0LnNlZWQoNDIpDQojIDI1MCBwZW9wbGUNCm4gPC0gMjUwDQojIFVuaWZvcm0gZGlzdHJvYnV0aW9uIG9mIGxpa2VydCBzY29yZXMgYmV0d2VlbiAwIHRvIDcgKDAgPSBObyBhbnhpZXR5KQ0KeCA8LSBydW5pZihuLCAwLCA3KQ0KIyBPdXIgZXF1YXRpb24gdG8gIGNyZWF0ZSBZDQp5IDwtIC0yLjUqeF4yICsgMTcuNSp4KzQwK3Jub3JtKG4sIHNkPTEwKQ0KI0J1aWx0IG91ciBkYXRhIGZyYW1lDQpRdWFkLkRhdGE8LWRhdGEuZnJhbWUoQW54aWV0eT14LEV4YW1TY29yZT15KQ0KDQojIFBsb3Q6IE5vdGVzOiByZWcubGluZT1GQUxTRSBtZWFucyByZW1vdmUgYmVzdCBmaXQgbGluZS4gDQojIHNtb290aGVyPWxvZXNzTGluZSBtZWFucyBhZGQgc21vb3RoZWQgbGluZSBjYWxsZWQgdGhlIGxvZXNzDQpzY2F0dGVycGxvdChFeGFtU2NvcmV+QW54aWV0eSwgZGF0YT0gUXVhZC5EYXRhLCByZWcubGluZT1GQUxTRSwgc21vb3RoZXI9bG9lc3NMaW5lKQ0KDQpgYGANCg0KIyMjIFRoZSBsb2VzcyBMaW5lDQotICoqTG8qKmNhbGx5IFcqKmUqKmlnaHRlZCAqKlMqKmNhdHRlcnBsb3QgKipTKiptb290aGluZw0KLSBEb2VzIGxvdHMgb2YgcmVncmVzc2lvbnMgb24gc21hbGwgc2VjdGlvbnMgb2YgdGhlIHNjYXR0ZXIgcGxvdCB0byBnZXQgYSBiZXN0IGZpdCAoY3VydnkpIGxpbmUNCi0gTGV0cyB5b3UgdmlzdWFsaXplIGNvbXBsZXggcmVsYXRpb25zaGlwcywgYnV0IGl0IGNhbiAqb3ZlcmZpdCogdGhlIHJlbGF0aW9uc2hpcCAoc28gYmUgY2FyZWZ1bCkNCi0gR29vZCBkaWFnbm9zdGljIHRvb2wsIGJ1dCByYXJlbHkgdXNlZCBpbiBwYXBlcnMgDQoNCiMjIFByb2Nlc3Mgb2YgZml0dGluZyBQb3dlciBQb2x5bm9taWFscyANCi0gRm9yd2FyZCBTZWxlY3Rpb24gQXBwcm9hY2gNCi0gTW9kZWwgMTogTGluZWFyIGZpdA0KLSBNb2RlbCAyOiBRdWFkcmF0aWMgZml0DQotIE1vZGVsIGk6IGtlZXAgZ29pbmcgdXAgdW50aWwgeW91IGFyZSBzYXRpc2ZpZWQgDQotIENoZWNrIHRoZSBjaGFuZ2UgaW4gJFJeMiQgYW5kIHNlbGVjdCB0aGUgYmVzdCBmaXQgbW9kZWxzDQotIE5vdGU6IEhpZ2hlciBvcmRlciB0ZXJtcyAqKkFMV0FZUyoqIGluY3JlYXNlICRSXjIkLCB5b3UgbmVlZCB0byBtYWtlIHN1cmUgaXQgaXMgYm90aCBhIHNpZ25pZmljYW50IGltcHJvdmVtZW50IGFuZCBtZWFuaW5nZnVsIGltcHJvdmVtZW50IA0KLSBpbiBSIHRoZXJlIGFyZSB0d28gd2F5cyB0byBhZGQgUG93ZXIgUG9seW5vbWlhbHM6ICpJKFRlcm1eMikqIG9yIHVzaW5nIHRoZSAqcG9seSogY29tbWFuZA0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCiNMaW5lYXIgbW9kZWwNCk1vZGVsLjE8LWxtKEV4YW1TY29yZX5BbnhpZXR5LCBkYXRhPSBRdWFkLkRhdGEpDQpNb2RlbC4yPC1sbShFeGFtU2NvcmV+QW54aWV0eStJKEFueGlldHleMiksIGRhdGE9IFF1YWQuRGF0YSkNCg0KYW5vdmEoTW9kZWwuMSxNb2RlbC4yKQ0KYGBgDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQ0KbGlicmFyeShzdGFyZ2F6ZXIpDQpzdGFyZ2F6ZXIoTW9kZWwuMSxNb2RlbC4yLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKCJMaW5lYXIiLCAiUXVhZHJhdGljIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLA0KICAgICAgICAgIHNpbmdsZS5yb3c9RkFMU0UsIA0KICAgICAgICAgIG5vdGVzLmFwcGVuZCA9IEZBTFNFKQ0KDQpgYGANCg0KLSBUaGUgY2hhbmdlIGluICRSXjIkID0gYHIgc3VtbWFyeShNb2RlbC4yKSRyLnNxdWFyZSAtIHN1bW1hcnkoTW9kZWwuMSkkci5zcXVhcmVgIGFuZCB0aGUgd2UgY2FuIHNlZSB0aGF0IHRoZSBjaGFuZ2Ugd2FzIHNpZ25pZmljYW50DQotIFdvdWxkIHRoZSB0aGlyZCBvcmRlciB0ZXJtIGJlIGFueSBiZXR0ZXI/IA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KI0xpbmVhciBtb2RlbA0KTW9kZWwuMzwtbG0oRXhhbVNjb3JlfkFueGlldHkrSShBbnhpZXR5XjIpK0koQW54aWV0eV4zKSwgZGF0YT0gUXVhZC5EYXRhKQ0KDQphbm92YShNb2RlbC4xLE1vZGVsLjIsTW9kZWwuMykNCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0nYXNpcyd9DQpsaWJyYXJ5KHN0YXJnYXplcikNCnN0YXJnYXplcihNb2RlbC4xLE1vZGVsLjIsTW9kZWwuMyx0eXBlPSJodG1sIiwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiTGluZWFyIiwgIlF1YWRyYXRpYyIsIkN1YmljIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLA0KICAgICAgICAgIHNpbmdsZS5yb3c9RkFMU0UsIA0KICAgICAgICAgIG5vdGVzLmFwcGVuZCA9IEZBTFNFKQ0KYGBgDQoNCi0gU28gc2Vjb25kIG9yZGVyIHBvd2VyIHBvbHlub21pYWwgd2FzIG91ciBiZXN0IGZpdA0KDQojIyMgTGV0cyBleGFtaW5lIE1vZGVsIDIgaW4gZGV0YWlsDQotIFNpbmNlIHdlIHNpbXVsYXRlZCBvdXIgbW9kZWwgd2Uga25vdyB3aGF0IHRoZSBhbnN3ZXJzICoqc2hvdWxkKiogYmUNCi0gU2ltdWxhdGlvbjogJFkgPSAxNy41WCAtMi41WF4yICsgNDAgKyBcZXBzaWxvbiQNCi0gTGV0cyBnZXQgb3VyIHRlcm1zIGZyb20gb3VyIHJlZ3Jlc3Npb246DQpgYGB7ciwgZWNobz1UUlVFfQ0KTFQ8LXJvdW5kKE1vZGVsLjIkY29lZmZpY2llbnRzWzJdLDIpDQpRVDwtcm91bmQoTW9kZWwuMiRjb2VmZmljaWVudHNbM10sMikNCklUPC1yb3VuZChNb2RlbC4yJGNvZWZmaWNpZW50c1sxXSwyKQ0KYGBgDQotIFJlZ3Jlc3Npb246ICRZID0kIGByIExUYCRYJCBgciBRVGAkWF4yICskIGByIElUYCAkKyBcZXBzaWxvbiQNCi0gT3VyIHJlZ3Jlc3Npb24gZGlkIGEgZ29vZCBqb2IsIGJ1dCB3aGF0IGlmIG91ciBzYW1wbGUgd2FzIHNtYWxsZXI/DQotIExldCByZWRvIG91ciBhbmFseXNpcyB3aXRoIG4gPSA1IHRvIDYwIGFuZCBzZWUgaG93IHRoZSB0ZXJtcyBjaGFuZ2UgW3NlZSBzaW11bGF0aW9uIGluIHNlcGFyYXRlIFIgZmlsZV0NCi0gdGhlIGdyYXkgYmFuZCA9IDk1JSBDSQ0KIVtdKFJlZ3Jlc3Npb25DbGFzcy9MNV9OTE0vU2ltcGxvdDEuanBlZykNCg0KLSBJdHMgaW1wb3J0YW50IHRvIGtub3cgdGhhdCBpbiBzbWFsbCBzYW1wbGVzIHBvbHlub21pYWwgZXN0aW1hdGVzIGNhbiBiZSB2ZXJ5IHVuc3RhYmxlDQotIGlmIHlvdSBmbGlwIHRoZSBxdWFkcmF0aWMgdGVybSB0aGUgbGluZSB3aWxsIGZsaXAgZGlyZWN0aW9uDQoNCiMjIyBIb3cgdG8gcGxvdCB0aGlzIGZ1bmN0aW9uIChhbmQgbm90IHRoZSBsb2VzcyBsaW5lKQ0KLSBMaXRlcmFsbHkgeW91IGNhbiBzb2x2ZSB0aGUgcmVncmVzc2lvbiBlcXVhdGlvbiBmb3IgZGlmZmVyZW50IHZhbHVlcyBvZiB4DQotIGluIFIgeW91IGNhbiBjYWxsIHRoZSBlZmZlY3RzIHBhY2thZ2UgdG8gZG8gd29yayBmb3IgeW91DQoNCg0KYGBge3IsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShlZmZlY3RzKQ0KcGxvdChlZmZlY3QoIkFueGlldHkiLCBNb2RlbC4yLA0KICAgICAgICAgICAgeGxldmVscz1saXN0KEFueGlldHk9MDo3KSkpDQoNCmBgYA0KDQojIyBPcnRob2dvbmFsIFBvbHlub21pYWxzDQotIFByb2JsZW0gd2l0aCBwb3dlciBwb2x5bm9taWFscyBpcyBpbnRlcnByZXRpbmcgdGhlbSBpbmRlcGVuZGVudGx5DQotIFRoZSBoaWdoZXIgb3JkZXIgdGVybXMgZGVwZW5kIG9uIHRoZSBsb3dlciBvcmRlciB0ZXJtcw0KLSBUaHVzIGxpbmVhciB0ZXJtIGNhbiBiZSBzaWduaWZpY2FudCBjYXVzZSB0aGUgaGlnaGVyIG9yZGVyIHRlcm0gaXMgc2lnbmlmaWNhbnQsIGJ1dCBhY3R1YWxseSB0aGVyZSBpcyBubyBsaW5lYXIgdHJlbmQgaW4gdGhlIGRhdGEgKGp1c3QgbGlrZSBpbiB0b2RheSdzIGV4YW1wbGUpDQotIFRoaXMgaXMgYmVjYXVzZSBwb3dlciBwb2x5bm9taWFscyBjb3JyZWxhdGUgd2l0aCBlYWNoIG90aGVyICh0aGV5IGFyZSBhcmUgbm90IHVuaXF1ZSBwcmVkaWN0b3JzKQ0KLSBGb3IgRXhhbXBsZToNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCkxpbmVhclRlcm1zPC1jKDEsMiwzLDQsNSw2LDcpDQpRdWFkVGVybXM8LUxpbmVhclRlcm1zXjINCnJvdW5kKGNvcihMaW5lYXJUZXJtcyxRdWFkVGVybXMpLDMpDQoNCmBgYA0KDQotIFRodXMgdGhleSBhcmUgYmFzaWNhbGx5IG11bHRpY29sbGluZWFyDQotIGxldHMgcnVuIG91ciBWSUYgdG8gdmVyaWZ5DQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQp2aWYoTW9kZWwuMikNCmBgYA0KDQotIFRlY2huaWNhbGx5LCBpdHMgT0sgdGhhdCB0aGV5IGFyZSBtdWx0aWNvbGxpbmVhciBidXQgeW91IGNhbm5vdCBpbnRlcnByZXQgdGhlIFB2YWx1ZXMgb24gZWFjaCB0ZXJtDQotIFRoZSBzb2x1dGlvbiB0byBtYWtlIHRoZSBsaW5lYXIgYW5kIHF1YWRyYXRpYyB0ZXJtcyBjb3JyZWxhdGUgd2l0aCBlYWNoIGF0IHplcm8gKGp1c3QgYXMgd2UgZGlkIHdpdGggQU5PVkEpDQotIHdlIGNhbiB1c2UgdGhlIHBvbHkgY29kZSBpbiBSLiANCi0gSGVyZSBpcyBhbiBleGFtcGxlIG9mIHdoYXQgUiBpcyBnb2luZyB0byBkbzogDQoNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCkxpbmVhclRlcm1zPC1jKDEsMiwzLDQsNSw2LDcpDQoNCiNtYWtlIHRoZW0gT3J0aG9nb25hbA0KTy5Qb2x5PC1wb2x5KExpbmVhclRlcm1zLDIsc2ltcGxlID0gVFJVRSkNCnJvdW5kKE8uUG9seSw1KQ0KDQojcnVuIG91ciBjb3JyZWxhdGlvbg0Kcm91bmQoY29yKE8uUG9seVssMV0sTy5Qb2x5WywyXSksMykNCmBgYA0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQojbGV0cyBtYWtlIGEgbmV3IHBvbHkgdmVjdG9yDQpRdWFkLkRhdGEkQW54aWV0eS5wb2x5LjE8LXBvbHkoUXVhZC5EYXRhJEFueGlldHksMSxzaW1wbGUgPSBUUlVFKVssMV0NClF1YWQuRGF0YSRBbnhpZXR5LnBvbHkuMjwtcG9seShRdWFkLkRhdGEkQW54aWV0eSwyLHNpbXBsZSA9IFRSVUUpWywyXQ0KDQpNb2RlbC4xLk88LWxtKEV4YW1TY29yZX5BbnhpZXR5LnBvbHkuMSwgZGF0YT0gUXVhZC5EYXRhKQ0KTW9kZWwuMi5PPC1sbShFeGFtU2NvcmV+QW54aWV0eS5wb2x5LjErQW54aWV0eS5wb2x5LjIsIGRhdGE9IFF1YWQuRGF0YSkNCg0KYW5vdmEoTW9kZWwuMS5PLE1vZGVsLjIuTykNCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0nYXNpcyd9DQpzdGFyZ2F6ZXIoTW9kZWwuMS5PLE1vZGVsLjIuTyx0eXBlPSJodG1sIiwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiTGluZWFyLk9ydGhvIiwgIlF1YWRyYXRpYy5PcnRobyIpLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwNCiAgICAgICAgICBzaW5nbGUucm93PUZBTFNFLCANCiAgICAgICAgICBub3Rlcy5hcHBlbmQgPSBGQUxTRSkNCg0KYGBgDQoNCi0gTm90aWNlIHRoYXQgdGhlIGxpbmVhciB0ZXJtIGlzIG5vdCBzaWduaWZpY2FudCwgYnV0IHRoZSBxdWFkcmF0aWMgaXMNCi0gQlVUIHRoZSBhY3R1YWwgZXN0aW1hdGUgb2YgdGhlIHNsb3BlcyBhcmUgbm9uc2Vuc2ljYWwNCi0gdGhpcyBpcyBiZWNhdXNlIHlvdSBoYXZlICoqcmVzY2FsZWQqKiB0aGUgeHZhbHVlcyBpbiBhIGZ1bm55IHdheQ0KLSBCdXQgbm8gbW9yZSBtdWx0aWNvbGxpbmVhcml0eQ0KLSBsZXRzIHJ1biBvdXIgVklGIHRvIHZlcmlmeQ0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KdmlmKE1vZGVsLjIuTykNCmBgYA0KDQotIEFsc28gdGhlICRSXjIkIGRvIG5vdCBkaWZmZXIgYmV0d2VlbiB0aGUgcG93ZXIgb3Igb3J0aG9nb25hbCBwb2x5bm9taWFscw0KLSBPdGhlciBwcm9zIG9mIHVzaW5nIG9ydGhvZ29uYWwgcG9seW5vbWlhbHMgd2lsbCBjb21lIG5leHQgd2VlayB3aGVuIHdlIHN0YXJ0IGxvb2tpbmcgYXQgaW50ZXJhY3Rpb25zDQoNCiMjIyBDb2Rpbmcgc2hvcnQgY3V0IChVc2UgdGhpcyBmb3IgeW91ciBhbmFseXNpcykNCi0gWW91IGRvbid0IG5lZWQgdG8gcHJlbWFrZSB0aGUgdmVjdG9yDQotIFlvdSBjYW4gcHV0IHRoZSBwb2x5IGNvZGUgcmlnaHQgaW50byB0aGUgbW9kZWwNCi0gYnV0IHRoZSBsYWJlbGluZyB3aWxsIGNoYW5nZQ0KLSBBbHNvIG11Y2ggZWFzaWVyIHRvIG1ha2VzIHBsb3RzIG9mIHRoZSBmaW5hbCBtb2RlbCB0aGlzIHdheQ0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KTW9kZWwuMi5PLlM8LWxtKEV4YW1TY29yZX5wb2x5KEFueGlldHksMiksIGRhdGE9IFF1YWQuRGF0YSkNCg0KYGBgDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdhc2lzJ30NCnN0YXJnYXplcihNb2RlbC4yLk8uUyx0eXBlPSJodG1sIiwNCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYygiUXVhZHJhdGljLk9ydGhvIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLA0KICAgICAgICAgIHNpbmdsZS5yb3c9RkFMU0UsIA0KICAgICAgICAgIG5vdGVzLmFwcGVuZCA9IEZBTFNFKQ0KDQpgYGANCg0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KcGxvdChlZmZlY3QoInBvbHkoQW54aWV0eSwyKSIsIE1vZGVsLjIuTy5TLA0KICAgICAgICAgICB4bGV2ZWxzPWxpc3QoQW54aWV0eT0wOjcpKSkNCg0KYGBgDQoNCi0gQWxzbywgeW91IGNvdWxkIGhhdmUgc2FpZCBwb2x5KEFueGlldHksMixyYXc9VFJVRSkgdG8gZ2V0IHBvd2VyIHBvbHlub21pYWwgb24gdGhlIGZseQ0KDQoNCiMjIERpYWdub3RpY3MNCg0KLSBXaGVuIHlvdSBoYXZlIHRoZSAqKndyb25nKiogbnVtYmVyIG9mIHBvbHlub21pYWxzIHlvdSB3aWxsIGdldCBmdW5ueSBsb29raW5nIHJlc2lkdWFscw0KLSBIZXJlIGlzIHdoZW4gd2UgdXNlIDEsIHdoZW4gd2UgbmVlZCAyDQpgYGB7ciwgZWNobz1UUlVFLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwbG90KE1vZGVsLjEsIHdoaWNoPTEpDQoNCmBgYA0KDQotIFdoZW4geW91IGhhdmUgdGhlICoqcmlnaHQqKiBudW1iZXIgb2YgcG9seW5vbWlhbHMgeW91IHdpbGwgZ2V0IGNvcnJlY3QgbG9va2luZyByZXNpZHVhbHMNCi0gSGVyZSBpcyB3aGVuIHdlIHVzZSAyLCB3aGVuIHdlIG5lZWQgMg0KYGBge3IsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGxvdChNb2RlbC4yLk8uUywgd2hpY2g9MSkNCmBgYA0KDQojIERlYWxpbmcgR3Jvd3RoIG1vZGVscyANCi0gVGhlc2UgYXJlIG1vZGVscyB0aGF0IGNhbm5vdCBiZSBmaXQgd2l0aCBwb2x5bm9taWFscw0KLSBFeHBvbmVudGlhbCBncm93dGg6ICRZID0gY2Vee2R4fSQNCi0gU2V2ZXJpdHkgb2YgcGFuaWMgYXR0YWNrICgwIHRvIDUwMCkgcHJlZGljdGVkIGJ5IGFueGlldHkgbGV2ZWwNCi0gRXhwb25lbnRpYWwgRXhhbXBsZTogJFkgPSAuMDllXnsxLjJ4K1xlcHNpbG9ufSQNCi0gTm90ZTogRXVsZXIncyBudW1iZXIsICRlID0kIGByICBleHAoMSlgLCBpcyBhbiBpcnJhdGlvbmFsIG1hdGggY29uc3RhbnQgYW5kIHRoZSBiYXNlIHZhbHVlIG9mIHRoZSBuYXR1cmFsIGxvZw0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDQyKQ0KbiA8LSAyNTANCnggPC0gcnVuaWYobiwgMCwgNykNCiNsZXRzIG1ha2Ugb3VyIGUgdmFsdWUgKG5vdCB0aGUgZXJyb3IgdGVybSkNCmU8LWV4cCgxKQ0KeSA8LS4wOSplXigxLjIwKih4K3Jub3JtKG4sIHNkPS4yNSkpKQ0KRXhwLkRhdGE8LWRhdGEuZnJhbWUoQW54aWV0eT14LFBhbmljQXR0YWNrPXkpDQpzY2F0dGVycGxvdChQYW5pY0F0dGFja35BbnhpZXR5LCBkYXRhPSBFeHAuRGF0YSwgcmVnLmxpbmU9RkFMU0UsIHNtb290aGVyPWxvZXNzTGluZSkNCg0KYGBgDQoNCiMjIERvIFBvbHlub21pYWxzIGZhaWwgdXM/DQotIExldHMgdHJ5IGEgc2Vjb25kIG9yZGVyIGZpdCBzaW5jZSBpdCBvbmUgY3VydmUNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpNb2RlbC5QYW5pYy5Qb2x5PC1sbShQYW5pY0F0dGFja35wb2x5KEFueGlldHksMiksIGRhdGE9IEV4cC5EYXRhKQ0KDQpgYGANCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQ0Kc3RhcmdhemVyKE1vZGVsLlBhbmljLlBvbHksdHlwZT0iaHRtbCIsDQogICAgICAgICAgY29sdW1uLmxhYmVscyA9IGMoIlF1YWRyYXRpYy5PcnRobyIpLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwNCiAgICAgICAgICBzaW5nbGUucm93PUZBTFNFLCANCiAgICAgICAgICBub3Rlcy5hcHBlbmQgPSBGQUxTRSkNCg0KYGBgDQoNCi0gVXNpbmcgdGhlIHBvbHkgcmlnaHQgaW50byB0aGUgcGFja2FnZSBjYW4gYmUgdXNlZnVsIGZvciB0aGUgZWZmZWN0cyBwYWNrYWdlIGJlY2F1c2Ugd2UgY2FuIG5vdyB2aWV3IGluZGVwZW5kZW50IGltcGFjdCBvZiB0aGUgbGluZWFyIGFuZCBxdWFkcmF0aWMgdGVybXMNCg0KYGBge3IsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpwbG90KGVmZmVjdCgicG9seShBbnhpZXR5LDIpIiwgTW9kZWwuUGFuaWMuUG9seSwNCiAgICAgICAgICAgeGxldmVscz1saXN0KEFueGlldHk9MDo3KSkpDQoNCmBgYA0KLSBZaWtlcywgYmFzZWQgb24gdGhpcyB5b3UgbWlnaHQgdGVsbCBwZW9wbGUgaGF2aW5nIHplcm8gYW54aWV0eSBpcyB3b3JzZSB0aGVuIGhhdmluZyBsb3dlciBhbnhpZXR5DQoNCi0gTGV0IGNoZWNrIHRoZSByZXNpZHVhbHMNCmBgYHtyLCBlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3QoTW9kZWwuUGFuaWMuUG9seSwgd2hpY2g9MSkNCmBgYA0KDQotIEFsc28gdGhhdCBsb29rcyBwcmV0dHkgb2RkDQoNCiMjIFRyYW5zZm9ybWF0aW9ucyBvZiBFeHBvbmVudGlhbCBNb2RlbA0KLSBUaGUgc29sdXRpb24gdG8gdGhpcyBwcm9ibGVtIGlzIG9mdGVuIHRvIHRyYW5zZm9ybSB0aGUgb2ZmZW5kaW5nIHZhcmlhYmxlDQotIFRoZSB0eXBlIG9mIHRyYW5zZm9ybSBkZXBlbmRzIG9uIHRoZSB0eXBlIG9mIGdyb3d0aCBtb2RlbCB5b3UgaGF2ZQ0KLSBFeHBvbmVudGlhbCBtb2RlbDogRGVwZW5kZW50IHZhcmlhYmxlID0gbG9nKHkpIFtpbiByLCBsb2cgPSBuYXR1cmFsIGxvZ10NCi0gVGh1czogJGxvZyhZKSA9IEJfMCArIEJYJA0KLSBBbmQgdG8gY29udmVydCBmcm9tIGxvZyBpbnRvIG1lYW5pbmdmdWwgdmFsdWVzOg0KLSAkWSA9IGVee0JfMCArIEJYfSQNCi0gTGV0cyBsb29rIGF0IExvZyhZKSBieSBBbnhpZXR5DQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpzY2F0dGVycGxvdChsb2coUGFuaWNBdHRhY2spfkFueGlldHksIGRhdGE9IEV4cC5EYXRhKQ0KYGBgDQoNCi0gTG9va3MgbGlrZSBhIGxpbmUgbm93DQotIExldHMgbG9vayBhdCB0aGUgbW9kZWwgZml0DQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UscmVzdWx0cz0nYXNpcyd9DQpNb2RlbC5QYW5pYy5Mb2c8LWxtKGxvZyhQYW5pY0F0dGFjayl+QW54aWV0eSwgZGF0YT0gRXhwLkRhdGEpDQpzdGFyZ2F6ZXIoTW9kZWwuUGFuaWMuTG9nLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwNCiAgICAgICAgICBzaW5nbGUucm93PUZBTFNFLCANCiAgICAgICAgICBub3Rlcy5hcHBlbmQgPSBGQUxTRSkNCg0KYGBgDQoNCi0gTGV0IHBsb3QgdGhlIGZpdA0KYGBge3IsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpwbG90KGVmZmVjdCgiQW54aWV0eSIsIE1vZGVsLlBhbmljLkxvZywNCiAgICAgICAgICAgeGxldmVscz1saXN0KEFueGlldHk9MDo3KSkpDQoNCmBgYA0KDQotIG9yIHlvdSBwbG90IGl0IG5vbi1sb2cgdW5pdHMNCi0gWW91IGhhdmUgdG8gdGVsbCB0aGUgcGFja2FnZSB0byBjb3ZlcnQgaXQgb3Zlcg0KYGBge3IsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGxvdChlZmZlY3QoIkFueGlldHkiLCBNb2RlbC5QYW5pYy5Mb2csDQogICAgICAgICAgdHJhbnNmb3JtYXRpb249bGlzdChsaW5rPWxvZywgaW52ZXJzZT1leHApKSkNCg0KYGBgDQoNCg0KLSBMZXQgcGxvdCB0aGUgZml0DQotIExldCBjaGVjayB0aGUgcmVzaWR1YWxzDQoNCmBgYHtyLCBlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3QoTW9kZWwuUGFuaWMuTG9nLCB3aGljaD0xKQ0KYGBgDQoNCi0gVGhvc2UgbG9vayBhcyB0aGV5IHNob3VsZA0KDQojIyMgQ2hhbGxhbmdlIG9mIFRyYW5zZm9ybXMNCi0gUHJvYmxlbSBpcyBvdXIgc2NvcmVzIGFyZSBpbiBsb2cgKG5vdCByYXcgc2NvcmUpDQotIHRvIGNvbnZlcnQgZnJvbSBsb2cgaW50byBtZWFuaW5nZnVsIHZhbHVlczoNCi0gJFkgPSBlXntCXzAgKyBCWH0kDQpgYGB7ciwgZWNobz1UUlVFfQ0KI2ludGVyY2VwdA0KSVQuTDwtcm91bmQoTW9kZWwuUGFuaWMuTG9nJGNvZWZmaWNpZW50c1sxXSwyKQ0KI3Nsb3BlDQpMVC5MPC1yb3VuZChNb2RlbC5QYW5pYy5Mb2ckY29lZmZpY2llbnRzWzJdLDIpDQpgYGANCi0gJFkgPSBlJF4oYHIgSVQuTGAgKyBgciBMVC5MYFgpDQotIEZvciBBbnhpZXR5IHNjb3JlIG9mIFggPSAwLCBZID0gYHIgZXhwKDEpXihJVC5MICsgTFQuTCowKWANCi0gRm9yIEFueGlldHkgc2NvcmUgb2YgWCA9IDQsIFkgPSBgciBleHAoMSleKElULkwgKyBMVC5MKjQpYA0KLSBGb3IgQW54aWV0eSBzY29yZSBvZiBYID0gNywgWSA9IGByIGV4cCgxKV4oSVQuTCArIExULkwqNylgDQotIFtOb3RlOiBCZSBjYXJlZnVsIGFib3V0IHdoaWNoIGxvZyB5b3UgdXNlIChuYXR1cmFsIG9yIGJhc2UgMTAsIHlvdSBjYW4gdXNlIGVpdGhlciBidXQgbWFrZSBzdXJlIHRvIHNwZWNpZnkpXQ0KDQojIyBQb3dlciBsYXcNCi0gQ29tbW9uIGZ1bmN0aW9uIGluIG1vdG9yIGNvbnRyb2wgYW5kIHBlcmNlcHRpb24gZXhwZXJpbWVudHMNCi0gJFkgPSBjWF5kJA0KLSBTZXZlcml0eSBvZiBzd2VhdGluZyAoMCB0byAxMDApIHByZWRpY3RlZCBieSBhbnhpZXR5IGxldmVsDQotIEV4cG9uZW50aWFsIEV4YW1wbGU6ICRZID0gLjA3KFgpXnsxLjd9K1xlcHNpbG9uJA0KLSBOb3RlOiBOYXR1cmFsIGxvZyBvZiAwID0gIC1pbmYsIHNvIGlmIHlvdSBoYXZlIHplcm9zIHlvdSBuZWVkIHRvIGFsd2F5cyBhZGQgMQ0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDQyKQ0KbiA8LSAyNTANCnggPC0gcnVuaWYobiwgMCwgNykrMQ0KeSA8LS4wNyooeCleMS43DQp5ID0geSAqICguMDUqICg1K3Jub3JtKG4sIHNkPS4yNSkpKQ0KUG93ZXIuRGF0YTwtZGF0YS5mcmFtZShBbnhpZXR5PXgsU3dlYXRpbmc9eSkNCnNjYXR0ZXJwbG90KFN3ZWF0aW5nfkFueGlldHksIGRhdGE9IFBvd2VyLkRhdGEsIHJlZy5saW5lPUZBTFNFLCBzbW9vdGhlcj1sb2Vzc0xpbmUpDQoNCmBgYA0KDQojIyMgVHJhbnNmb3JtYXRpb25zIG9mIFBvd2VyIExhdw0KLSBUaGUgc29sdXRpb24gdG8gdGhpcyBwcm9ibGVtIGlzIG9mdGVuIHRvIHRyYW5zZm9ybSB0aGUgb2ZmZW5kaW5nIHZhcmlhYmxlDQotIFBvd2VyIGxhdyBtb2RlbDogRFYgYW5kIElWIGdldCBsb2dnZWQ6IGxvZyh5KSAmIGxvZyh4KSANCi0gVGh1czogJGxvZyhZKSA9IEJfMCArIEIobG9nWCkkDQotIEFuZCB0byBjb252ZXJ0IGZyb20gbG9nIGludG8gbWVhbmluZ2Z1bCB2YWx1ZXM6DQotICRZID0gZV57Ql8wICsgQihsb2dYKX0kDQotIExldHMgbG9vayBhdCB0aGUgbG9nLWxvZyBzY2F0dGVycGxvdA0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnNjYXR0ZXJwbG90KGxvZyhTd2VhdGluZyl+bG9nKEFueGlldHkpLCBkYXRhPSBQb3dlci5EYXRhKQ0KTW9kZWwuU3dlYXQuTEw8LWxtKGxvZyhTd2VhdGluZyl+bG9nKEFueGlldHkpLCBkYXRhPSBQb3dlci5EYXRhKQ0KDQpgYGANCi1Mb29rcyBsaWtlIGEgbGluZSBub3cNCi1MZXRzIGxvb2sgYXQgdGhlIG1vZGVsIGZpdA0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQ0Kc3RhcmdhemVyKE1vZGVsLlN3ZWF0LkxMLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKCJMb2ctTG9nIFRyYW5zZm9ybSIpLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwNCiAgICAgICAgICBzaW5nbGUucm93PUZBTFNFLCANCiAgICAgICAgICBub3Rlcy5hcHBlbmQgPSBGQUxTRSkNCg0KYGBgDQoNCi0gTGV0IHBsb3QgdGhlIGZpdCBhbmQgdGVsbCB0aGUgcGFja2FnZSB0aGF0IHlvdSBkaWQgYSB0cmFuc2Zvcm0NCmBgYHtyLCBlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3QoZWZmZWN0KCJBbnhpZXR5IiwgTW9kZWwuU3dlYXQuTEwsDQogICAgICAgICAgIHRyYW5zZm9ybWF0aW9uPWxpc3QobGluaz1sb2csIGludmVyc2U9ZXhwKSkpDQpgYGANCg0KLSBMZXQgY2hlY2sgdGhlIHJlc2lkdWFscw0KYGBge3IsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGxvdChNb2RlbC5Td2VhdC5MTCwgd2hpY2g9MSkNCmBgYA0KDQoNCi0gVGhleSBsb29rIE9LDQoNCg0KIyMgTG9nYXJpdGhtaWMgDQotIFRoZXNlIGZ1bmN0aW9ucyBhcmUgb2Z0ZW4gcGh5c2ljYWwgKHNvdW5kIGludGVuc2l0eSwgZWFydGhxdWFrZXMsIGV0YykNCi0gJGNeWSA9IGRYJA0KLSBCcmVhdGhpbmcgcmF0ZSAoaW4gbXMgYmV0d2VlbiBicmVhdGhzKSBwcmVkaWN0ZWQgYnkgYW54aWV0eSBsZXZlbA0KLSBMb2dhcml0aG1pYyBFeGFtcGxlOiAkWSA9IC4wNShYKlxlcHNpbG9uKV57My44fSQNCg0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0Kc2V0LnNlZWQoNDIpDQpuIDwtIDI1MA0KeCA8LSBydW5pZihuLCAwLCA3KSsxDQp5IDwtMjAwKmxvZyh4Kig1K3Jub3JtKG4sIHNkPTEpKSkNCkxvZ2FyaXRobWljLkRhdGE8LWRhdGEuZnJhbWUoQW54aWV0eT14LEJyZWF0aGluZz15KQ0Kc2NhdHRlcnBsb3QoQnJlYXRoaW5nfkFueGlldHksIGRhdGE9IExvZ2FyaXRobWljLkRhdGEsIHJlZy5saW5lPUZBTFNFLCBzbW9vdGhlcj1sb2Vzc0xpbmUpDQoNCmBgYA0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnNjYXR0ZXJwbG90KEJyZWF0aGluZ35sb2coQW54aWV0eSksIGRhdGE9IExvZ2FyaXRobWljLkRhdGEpDQpNb2RlbC5CcmVhdGguTG9nPC1sbShCcmVhdGhpbmd+bG9nKEFueGlldHkpLCBkYXRhPSBMb2dhcml0aG1pYy5EYXRhKQ0KDQpgYGANCg0KLUxvb2tzIGxpa2UgYSBsaW5lIG5vdw0KLUxldHMgbG9vayBhdCB0aGUgbW9kZWwgZml0DQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxyZXN1bHRzPSdhc2lzJ30NCnN0YXJnYXplcihNb2RlbC5CcmVhdGguTG9nLHR5cGU9Imh0bWwiLA0KICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKCJMb2cgVHJhbnNmb3JtIiksDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLA0KICAgICAgICAgIHNpbmdsZS5yb3c9RkFMU0UsIA0KICAgICAgICAgIG5vdGVzLmFwcGVuZCA9IEZBTFNFKQ0KDQpgYGANCg0KLSBMZXQgcGxvdCB0aGUgZml0IA0KYGBge3IsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGxvdChlZmZlY3QoIkFueGlldHkiLCBNb2RlbC5CcmVhdGguTG9nKSkNCmBgYA0KDQotIExldCBjaGVjayB0aGUgcmVzaWR1YWxzDQpgYGB7ciwgZWNobz1UUlVFLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwbG90KE1vZGVsLkJyZWF0aC5Mb2csIHdoaWNoPTEpDQpgYGANCg0KDQojIyBSZWNpcHJvY2Fscw0KLSBDb21tb24gZml4IGZvciByYXRlIGRhdGEgKHRoaW5ncyBpbnZvbHZpbmcgdGltZSkNCi0gJDEvWSQNCi0gQWRyZW5hbGluZSBsZXZlbCBpbiB0aGUgYmxvb2QgcHJlZGljdGVkIGJ5IG1pbnMgYWZ0ZXIgYSBwYW5pYyBhdHRhY2sgKG1pbnMpDQotIFJlY2lwcm9jYWwgRXhhbXBsZTogJFkgPSAoMS9YKSpcZXBzaWxvbiQNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDQyKQ0KbiA8LSAyNTANCiMgTGVuZ3RoIG9mIHBhbmljIGF0dGFjaw0KeCA8LSAxNSoocnVuaWYobiwgMSwgNCkpDQp5IDwtIDEvKHgpKnJub3JtKG4sNSxzZD0uMSkNClJlY2lwcm9jYWwuRGF0YTwtZGF0YS5mcmFtZShQb3N0UGFuaWM9eCxBZHJlbmFsaW5lPXkpDQpzY2F0dGVycGxvdChBZHJlbmFsaW5lflBvc3RQYW5pYywgZGF0YT0gUmVjaXByb2NhbC5EYXRhLCByZWcubGluZT1GQUxTRSwgc21vb3RoZXI9bG9lc3NMaW5lKQ0KDQpgYGANCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpzY2F0dGVycGxvdCgxL0FkcmVuYWxpbmV+UG9zdFBhbmljLCBkYXRhPSBSZWNpcHJvY2FsLkRhdGEpDQpgYGANCg0KLSBMb29rcyBsaWtlIGEgbGluZSBub3cNCi0gTGV0cyBsb29rIGF0IHRoZSBtb2RlbCBmaXQNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLHJlc3VsdHM9J2FzaXMnfQ0KTW9kZWwuQ2FsbS5SZXA8LWxtKDEvQWRyZW5hbGluZX5Qb3N0UGFuaWMsIGRhdGE9IFJlY2lwcm9jYWwuRGF0YSkNCnN0YXJnYXplcihNb2RlbC5DYWxtLlJlcCx0eXBlPSJsYXRleCIsDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLA0KICAgICAgICAgIHNpbmdsZS5yb3c9RkFMU0UsIA0KICAgICAgICAgIG5vdGVzLmFwcGVuZCA9IEZBTFNFLCAgICAgICAgICANCiAgICAgICAgICBoZWFkZXI9RkFMU0UpDQoNCmBgYA0KDQotIExldCBwbG90IHRoZSBmaXQgDQpgYGB7ciwgZWNobz1UUlVFLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpwbG90KGVmZmVjdCgiUG9zdFBhbmljIiwgTW9kZWwuQ2FsbS5SZXApKQ0KYGBgDQoNCi0gTGV0IGNoZWNrIHRoZSByZXNpZHVhbHMNCmBgYHtyLCBlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3QoTW9kZWwuQ2FsbS5SZXAsIHdoaWNoPTEpDQpgYGANCg0KLSBBIGxpdHRsZSBvZmYgKGJ1dCBJIGFkZCBpbiBub2lzZSBpbiBhIHNpbXBsZSB3YXkpDQoNCiMjIFdpZXJkIEJ1bGdlcyBpbiB0aGUgZGF0YSAobm90IGZ1bGwgb24gY3VydmVzKQ0KLSBEYXRhIGRvIG5vdCBhbHdheXMgZml0IG91ciBuaWNlIHByZWRlZmluZWQgZnVuY3Rpb25zDQotIFNvbWV0aW1lcyB0aGV5IGNsZWFybHkgaGF2ZSAqKmJ1bGdlcyoqLCBidXQgYXJlIHdlIHN1cmUgdGhvc2UgYXJlIHJlYWw/DQotIEJ1bGdlcyBjYW4gYmUgbm90IHJlYWwgaW4gc21hbGwgc2FtcGxlcyBvciBjYW4gcmVzdWx0cyBmcm9tIHNvbWUgbGF0ZW50IGZhY3RvciB5b3UgYXJlIG5vdCBhY2NvdW50aW5nIGZvcg0KLSBMZXRzIGFzc3VtZSB0aGF0IGlmIHRoZSBkYXRhIGhhcyBhIGJ1bGdlIHRoYXQgaXMgcmVhbCwgbWVhbmluZ2Z1bCwgYW5kIHdlIG5lZWQgdG8gYWNjb3VudCBmb3IgaXQNCi0gV2UgY2FuIHVzZSB0aGUgQm94LUNveCBUcmFuc2Zvcm0gKGZpdHMgZGlmZmVyZW50IHBvbHlub21pYWxzIHdoaWNoIHdlIGNhbGwgJFxsYW1iZGEkKQ0KLSAkWSA9IFleXGxhbWJkYSAtIDEgLyBcbGFtYmRhJCwgd2hlcmUgJFxsYW1iZGEgXG5lcSAwJCBPUiAkWSA9IGxuWSQsIHdoZXJlICRcbGFtYmRhID0gMCQNCi0gSW4gdGhpcyBjYXNlLCB3ZSB3aWxsIGFsbG93ICRcbGFtYmRhJCB0byBiZSBzZWxlY3RlZCBiYXNlZCBvbiB3aGljaCBtYWtlcyB0aGUgZGF0YSBtb3N0IG5vcm1hbA0KLSBMZXRzIG1ha2UgYSBjYXNlIHdpdGggYSB3ZWlyZCBwb3dlciBwb2x5bm9taWFsIG9mIDEuMyAoc29tZXRoaW5nIHlvdSB3b3VsZCBub3Qgbm90aWNlIGluIHlvdXIgcmF3IGRhdGEpDQpgYGB7ciwgZWNobz1UUlVFLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCg0Kc2V0LnNlZWQoNDIpDQojIDI1MCBwZW9wbGUNCm4gPC0gMjUwDQp4IDwtIHJ1bmlmKG4sIDAsIDcpDQp5IDwtIC00KnheMS4zKyAyMCp4K3Jub3JtKG4sIHNkPTIpKzIwDQpTb3J0YVF1YWQuRGF0YTwtZGF0YS5mcmFtZShBbnhpZXR5PXgsRXhhbVNjb3JlPXkpDQpzY2F0dGVycGxvdChFeGFtU2NvcmV+QW54aWV0eSwgZGF0YT0gU29ydGFRdWFkLkRhdGEsIHJlZy5saW5lPUZBTFNFLCBzbW9vdGhlcj1sb2Vzc0xpbmUpDQoNCmBgYA0KDQotIExldHMgdHJ5IGEgbGluZWFyIG1vZGVsDQoNCmBgYHtyLCBlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCk1vZGVsLlNvcnRhUXVhZDwtbG0oRXhhbVNjb3JlfkFueGlldHksIGRhdGE9IFNvcnRhUXVhZC5EYXRhKQ0KcGxvdChNb2RlbC5Tb3J0YVF1YWQsIHdoaWNoPTEpDQoNCmBgYA0KDQotIFNlZW1zIHRoZXJlIGlzIGEgc2xpZ2h0IGN1cnZlIGluIG91ciBkYXRhDQoNCmBgYHtyLCBlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoTUFTUykNCmJjPC1ib3hjb3goRXhhbVNjb3JlIH5BbnhpZXR5LCBkYXRhID0gU29ydGFRdWFkLkRhdGEsDQogICAgICAgbGFtYmRhID0gc2VxKC0yLCAyLCBsZW4gPSAyMCkpDQojd2UgY2FuIGV4dHJhY3QgdGhlIGxhbWJkYQ0KKGxhbWJkYSA8LSBiYyR4W3doaWNoLm1heChiYyR5KV0pDQpgYGANCg0KLSBMZXRzIHBsb3QgdGhlIHNjYXR0ZXIgYW5kIHJlc2lkdWFscyBmcm9tIG91ciB0cmFuc2Zvcm1lZCBEVg0KYGBge3IsIGVjaG89VFJVRSxtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2NhdHRlcnBsb3QoRXhhbVNjb3JlXmxhbWJkYX5BbnhpZXR5LCBkYXRhPSBTb3J0YVF1YWQuRGF0YSwgcmVnLmxpbmU9RkFMU0UsIHNtb290aGVyPWxvZXNzTGluZSkNCk1vZGVsLlNvcnRhUXVhZC5CQzwtbG0oRXhhbVNjb3JlXmxhbWJkYSB+IEFueGlldHksIGRhdGE9IFNvcnRhUXVhZC5EYXRhKQ0KcGxvdChNb2RlbC5Tb3J0YVF1YWQuQkMsIHdoaWNoPTEpDQoNCmBgYA0KDQojIyMgU29tZSBpc3N1ZXMNCi0gQm94LUNveCBpcyBndWVzcyBhbmQgcmVxdWlyZXMgc29tZSBmaW5lIHR1cm5pbmcgKGxpa2UgYWRqdXN0aW5nIHRoZSByYW5nZSBvZiBsYW1iZGFzKQ0KLSBZb3UgaGF2ZSB0byBrZWVwIHRyYWNrIG9mIHlvdXIgemVyb3MgaW4gdGhlIERWIChzbyB5b3UgY2FuIGFkZCBjb25zdGFudHMgdG8gZml4IGl0KQ0KLSBNb3N0IGltcG9ydGFudGx5LCB5b3UgaGF2ZSBtYWtlIGl0IGhhcmRlciB0byBtYWtlIHNlbnNlIG9mIHRoZSBEViAoYnV0IHlvdXIgZml0cyB3aWxsIGJlIGZhciBiZXR0ZXIgdXNpbmcgaXQpDQoNCg0KIyBEb2luZyByZWdyZXNzaW9uIHdpdGggY29ycmVsYXRpb24gdmFsdWVzIGFzIHRoZSBEVg0KLSBZb3Ugc2hvdWxkIG5vdCBqdXN0IHVzZSB0aGUgY29ycmVsYXRpb24gdmFsdWVzIHN0cmFpZ2h0IGF3YXkNCi0gQ29ycmVsYXRpb25zIGFyZSBOT1Qgbm9ybWFsLCBidXQgbHVja2lseSBGaXNoZXIgZ2l2ZSB1cyBhIGZpeA0KLSBGaXNoZXIncyAqciogdG8gWg0KLSBZb3UgY29udmVydCB5b3VyIGNvcnJlbGF0aW9uIHZhbHVlcyB0byBaIHNjb3JlcywgZG8geW91ciBhbmFseXNpcywgcHJlZGljdCB5b3VyIHZhbHVlcyBhbmQgY29udmVydCB0aGVtIGJhY2sgdG8gciB2YWx1ZXMgZm9yIGdyYXBoaW5nDQotIHNlZSBEZW1vcywgZXQgYWwgKDIwMTIpIGZvciBhbiBleGFtcGxlLg0KLSBGdW5jdGlvbnMgYmVsb3cgeW91IGNhbiB1c2UgdG8gdHJhbnNmb3JtDQoNCmBgYHtyLCBlY2hvPVRSVUUsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCkZpc2hlclJ0b1ogPC1mdW5jdGlvbiAoeikgew0KICAoZXhwKDIgKiB6KSAtIDEpLygxICsgZXhwKDIgKiB6KSkNCn0NCg0KRmlzaGVyWnRvUiA8LWZ1bmN0aW9uICh6KSB7DQogIHRhbmgoeikNCn0NCg0KYGBgDQoNCg0KDQo8c2NyaXB0Pg0KICAoZnVuY3Rpb24oaSxzLG8sZyxyLGEsbSl7aVsnR29vZ2xlQW5hbHl0aWNzT2JqZWN0J109cjtpW3JdPWlbcl18fGZ1bmN0aW9uKCl7DQogIChpW3JdLnE9aVtyXS5xfHxbXSkucHVzaChhcmd1bWVudHMpfSxpW3JdLmw9MSpuZXcgRGF0ZSgpO2E9cy5jcmVhdGVFbGVtZW50KG8pLA0KICBtPXMuZ2V0RWxlbWVudHNCeVRhZ05hbWUobylbMF07YS5hc3luYz0xO2Euc3JjPWc7bS5wYXJlbnROb2RlLmluc2VydEJlZm9yZShhLG0pDQogIH0pKHdpbmRvdyxkb2N1bWVudCwnc2NyaXB0JywnaHR0cHM6Ly93d3cuZ29vZ2xlLWFuYWx5dGljcy5jb20vYW5hbHl0aWNzLmpzJywnZ2EnKTsNCg0KICBnYSgnY3JlYXRlJywgJ1VBLTkwNDE1MTYwLTEnLCAnYXV0bycpOw0KICBnYSgnc2VuZCcsICdwYWdldmlldycpOw0KDQo8L3NjcmlwdD4=