1 Causal Modeling

  • X causes Y. Eating too many cookies will make you gain weight
  • X -> Y ; Cookies -> Wt
  • We need to make sure that the cause is not spurious.
  • Causal Path analysis

1.1 Direct and Indirect Effects

  • Direct: X1 -> Y (which can control for X2)
  • Indirect X1 -> X2 -> Y ; Cookies -> Wt (moderated by exercise)
  • We will look at these concepts more in detail when we talk moderation analysis

1.1.1 Back to our last weeks example:

  • Model we used was Predicted Happiness (Y) ~ Slope Ice Cream(X1) + Slope Brownies (x2) + Intercept -in formulas:
#packages we will need to conduct to create and graph our data
library(MASS) #create data
library(car) #graph data
py1 =.6 #Cor between X1 (ice cream) and happiness
py2 =.4 #Cor between X2 (Brownies) and happiness
p12= .2 #Cor between X1 (ice cream) and X2 (Brownies)

Means.X1X2Y<- c(10,10,10) #set the means of X and Y variables
CovMatrix.X1X2Y <- matrix(c(1,p12,py1,
                            p12,1,py2,
                            py1,py2,1),3,3) # creates the covariate matrix 

#build the correlated variables. Note: empirical=TRUE means make the correlation EXACTLY r. 
set.seed(42)
CorrDataT<-mvrnorm(n=100, mu=Means.X1X2Y,Sigma=CovMatrix.X1X2Y, empirical=TRUE)
#Covert them to a "Data.Frame", which is like SPSS data window
CorrDataT<-as.data.frame(CorrDataT)
#lets add our labels to the vectors we created
colnames(CorrDataT) <- c("IceCream","Brownies","Happiness")

###############Model 1 
Ice.Model<-lm(Happiness~ IceCream, data = CorrDataT)
summary(Ice.Model)
## 
## Call:
## lm(formula = Happiness ~ IceCream, data = CorrDataT)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.62669 -0.59548  0.03645  0.64933  2.01161 
## 
## 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
###############Model 2
Ice.Brown.Model<-lm(Happiness~ IceCream+Brownies, data = CorrDataT)
summary(Ice.Brown.Model)
## 
## Call:
## lm(formula = Happiness ~ IceCream + Brownies, data = CorrDataT)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.03238 -0.49255 -0.08491  0.55931  1.74670 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.66667    0.98236   1.697 0.092981 .  
## IceCream     0.54167    0.07743   6.995 3.41e-10 ***
## Brownies     0.29167    0.07743   3.767 0.000284 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.7549 on 97 degrees of freedom
## Multiple R-squared:  0.4417, Adjusted R-squared:  0.4302 
## F-statistic: 38.37 on 2 and 97 DF,  p-value: 5.301e-13

1.1.2 Plots

  • Now piloting it a bit more complex as we have two predictors
  • Simple scatter plots are not semi-partialed
  • Also while we are not interacting the two variables, each has an effect on the DV
  • We must visualize both effects
  • There are different suggestions on how to do this [we will come back to differences in plotting later]
#packages we will need to plot our results
library(effects)
#plot individual effects
Ice.Brown.Model.Plot <- allEffects(Ice.Brown.Model, xlevels=list(IceCream=seq(8, 12, 1),Brownies=seq(8, 12, 1)))
plot(Ice.Brown.Model.Plot, 'IceCream', ylab="Happiness")

plot(Ice.Brown.Model.Plot, 'Brownies', ylab="Happiness")

#plot both effects
Ice.Brown.Model.Plot2<-Effect(c("IceCream", "Brownies"), Ice.Brown.Model,xlevels=list(IceCream=c(8, 12), Brownies=c(8,12)))
plot(Ice.Brown.Model.Plot2)

1.1.3 Fully Indrect

  • Not spurious, but X1 is not direct to Y (X2 is intervening completely)

1.1.4 Full Redundancy (Spurious models)

  • when, \(r_{Y2} \approx r_{Y1}r_{12}\)
  • X1 is confounded in X2
  • X1 and X2 are completely redundant
I.py1 =.6 #Cor between X1 Oreos and happiness
I.py2 =.6 #Cor between X2 Cookies and happiness
I.p12 = .99 #Cor between X1 Oreos and X2 Cookies

I.Means.X1X2Y<- c(10,10,10) #set the means of X and Y variables
I.CovMatrix.X1X2Y <- matrix(c(1,I.p12,I.py1,
                            I.p12,1,I.py2,
                            I.py1,I.py2,1),3,3) # creates the covariate matrix 

#build the correlated variables. Note: empirical=TRUE means make the correlation EXACTLY r. 
# if we say empirical=FALSE, the correlation would be normally distributed around r
set.seed(42)
IData<-mvrnorm(n=100, mu=I.Means.X1X2Y,Sigma=I.CovMatrix.X1X2Y, empirical=TRUE)
#Covert them to a "Data.Frame", which is like SPSS data window
IData<-as.data.frame(IData)
#lets add our labels to the vectors we created
colnames(IData) <- c("Oreos","Cookies","Happiness")

###############Model 1 
summary(lm(Happiness~ Oreos, data = IData))
## 
## Call:
## lm(formula = Happiness ~ Oreos, data = IData)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.31637 -0.39257 -0.00598  0.36991  2.45566 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  4.00000    0.81211   4.925 3.42e-06 ***
## Oreos        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
###############Model 2 
summary(lm(Happiness~ Cookies, data = IData))
## 
## Call:
## lm(formula = Happiness ~ Cookies, data = IData)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.19446 -0.37045 -0.00244  0.38964  2.37326 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  4.00000    0.81211   4.925 3.42e-06 ***
## Cookies      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
###############Model 3
summary(lm(Happiness~ Oreos+Cookies, data = IData))
## 
## Call:
## lm(formula = Happiness ~ Oreos + Cookies, data = IData)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.25666 -0.38691  0.00358  0.38119  2.41233 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   3.9698     0.8172   4.858 4.55e-06 ***
## Oreos         0.3015     0.5750   0.524    0.601    
## Cookies       0.3015     0.5750   0.524    0.601    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.8071 on 97 degrees of freedom
## Multiple R-squared:  0.3618, Adjusted R-squared:  0.3487 
## F-statistic:  27.5 on 2 and 97 DF,  p-value: 3.468e-10

1.1.5 Suppression

  • Suppression is a strange case where the IV -> DV relationship is hidden (suppressed) by another variable
  • Tax cuts cause growth, tax cuts cause inflation. Tax cuts alone might look +, but add in inflation and now tax cuts could make cause the growth to look different
  • Or the the suppressor variable can cause a flip in the sign of the relationship
  • Here is an example where the effect was hidden:
sup.py1 =  2.5 #Covar between tax cuts and growth 
sup.py2 = -5.5 #Covar between inflation and growth 
sup.p12 =  4  #Covar between tax cuts and inflation


Supp.X1X2Y<- c(5,5,5) #set the means of X and Y variables
Supp.CovMatrix.X1X2Y <- matrix(c(10,sup.p12,sup.py1,
                                 sup.p12,10,sup.py2,
                                 sup.py1,sup.py2,10),3,3) # creates the covariate matrix 


#build the correlated variables. Note: empirical=TRUE means make the correlation EXACTLY r. 
# if we say empirical=FALSE, the correlation would be normally distributed around r
set.seed(42)
SuppData<-mvrnorm(n=100, mu=Supp.X1X2Y,Sigma=Supp.CovMatrix.X1X2Y, empirical=TRUE)

#Covert them to a "Data.Frame"
SuppData<-as.data.frame(SuppData)
colnames(SuppData) <- c("TaxCuts","inflation","growth")

###############Model 1 
TaxCutsOnly<-(lm(growth~ TaxCuts, data = SuppData))
summary(TaxCutsOnly)
## 
## Call:
## lm(formula = growth ~ TaxCuts, data = SuppData)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -5.7381 -2.0879 -0.1388  2.3052  9.6160 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  3.75000    0.57781   6.490 3.53e-09 ***
## TaxCuts      0.25000    0.09781   2.556   0.0121 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 3.077 on 98 degrees of freedom
## Multiple R-squared:  0.0625, Adjusted R-squared:  0.05293 
## F-statistic: 6.533 on 1 and 98 DF,  p-value: 0.01212
###############Model 2
Full.Model<-lm(growth~ TaxCuts+inflation, data = SuppData)
summary(Full.Model)
## 
## Call:
## lm(formula = growth ~ TaxCuts + inflation, data = SuppData)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -5.797 -1.358  0.015  1.324  6.913 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  6.07143    0.45203  13.431  < 2e-16 ***
## TaxCuts      0.55952    0.07303   7.662 1.39e-11 ***
## inflation   -0.77381    0.07303 -10.596  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.106 on 97 degrees of freedom
## Multiple R-squared:  0.5655, Adjusted R-squared:  0.5565 
## F-statistic: 63.12 on 2 and 97 DF,  p-value: < 2.2e-16
  • You will notice tax cuts alone, 0.25, was lower than 0.5595238 controlling for inflation.
  • However, you will notice inflation,-0.7738095, has a bigger effect (and negative) effect than tax cuts 0.5595238
  • So in other words, yes tax cuts work but they don’t override inflation!

1.1.6 Lets try to graph our predictions

  • Challenge is that we need to account for 2 predictors now.
  • So simple scatter plots will not work
  • Also how you plot depends on your theory/experiment/story
  • We need to control for inflation (or view tax slope at different levels of inflation)
  • First lets view the tax slope without controlling for inflation
#plot individual effects
Tax.Model.Plot <- allEffects(TaxCutsOnly, xlevels=list(TaxCuts=seq(3, 7, 1)))
plot(Tax.Model.Plot, 'TaxCuts', ylab="Growth")

  • Now lets view it controlling for inflation
#plot individual effects
Full.Model.Plot <- allEffects(Full.Model, xlevels=list(TaxCuts=seq(3, 7, 1),inflation=seq(3, 7, 1)))
plot(Full.Model.Plot, 'TaxCuts', ylab="Growth")

plot(Full.Model.Plot, 'inflation', ylab="Growth")

#plot both effects
Full.Model.Plot2<-Effect(c("TaxCuts", "inflation"), Full.Model,xlevels=list(TaxCuts=c(3, 7), inflation=c(3,7)))
plot(Full.Model.Plot2)

2 Estimating population R-squared

  • \(\rho^2\) is estimated by multiple \(R^2\)
  • Problem: in small samples correlations are unstable (and inflated)
  • Also, as we add predictors, \(R^2\) gets inflated
  • So we use an adjusted multiple \(R^2\)
  • Adjusted \(R^2 = 1 - 1- R_Y^2 \frac{n-1}{n-k-1}\)

3 Multiple Linear Regression Assumptions

  • Multicollinearity: Predictors cannot be fully (or nearly fully) redundant [check the correlations between predictors]
  • Homoscedasticity of residuals to fitted values
  • Normal distribution of residuals
  • Absence of outliers
  • Ratio of cases to predictors
  • Number of cases must exceed the number of predictors
  • Barely acceptable minimum: 5 cases per predictor
  • Preferred minimum: 20-30 cases per predictor
  • Linearity of prediction
  • Independence (no auto-correlated errors)

3.1 How to check assumptions?

  • First lets fit the model
Linear.Model<-lm(Happiness~ IceCream+Brownies, data = CorrDataT)

3.1.1 Lets test for Multicollinearity

  • Multicollinearity can be detected when the variance on the terms you are interested in become inflated
  • So we need to test the variance on the factors.
  • Basic Logical step 1) get Variance on predictor (only that term in model) [Vmin]
  • Basic Logical step 2) get Variance (Vmax) on predictor (all other terms in model) [Vmax]
  • Basic Logical step 3) ratio: Vmax/Vmin. if the value > 4 you have a problem
  • that’s alot of steps. R can do it for you basically with one line of code
  • Variance inflation factors (vif)
vif(Linear.Model) # variance inflation factors 
## IceCream Brownies 
## 1.041667 1.041667
vif(Linear.Model) > 4 # problem?
## IceCream Brownies 
##    FALSE    FALSE

3.1.2 lets check normality, outlinears visually first

#To see them all at once
layout(matrix(c(1,2,3,4),2,2)) # optional 4 graphs/page 
plot(Linear.Model)

### Residuals vs fitted and sqaure-rooted residuals vs fitted - should be Homoscedastistic (randomly distributed around 0) - There is statistical test (in R, but not SPSS)

# Evaluate homoscedasticity
# non-constant error variance test
ncvTest(Linear.Model)
## Non-constant Variance Score Test 
## Variance formula: ~ fitted.values 
## Chisquare = 0.8701472    Df = 1     p = 0.3509146

3.1.3 Q-Q plot

  • should be a straight line
  • you can also visualize the residuals as a histogram
# distribution of studentized residuals
sresid <- studres(Linear.Model) 
hist(sresid, freq=FALSE, 
     main="Distribution of Studentized Residuals")
xfit<-seq(min(sresid),max(sresid),length=40) 
yfit<-dnorm(xfit) 
lines(xfit, yfit)

3.1.4 Cook’s distances

  • Its possible for one or more observations to “pull” the regression line away from the trend in the rest of the data
  • Cook’s Distance is one of several possible measures of influence.
  • Cook’s D is the sum of the differences between the predicted value and the other predicted values in the data set, divided by the error variance times the number of parameters in the model.
  • R did this automatically in plot 4 and we can see a few people with numbers (those are the outliers)
  • We can also see the influence with a little R coding (set a more conservative threshold)
# Cook's D plot
# identify D values > 4/(n-k-1) 
cutoff <- 4/((nrow(CorrDataT)-length(Linear.Model$coefficients)-2)) 
cutoff
## [1] 0.04210526
plot(Linear.Model, which=4, cook.levels=cutoff)

plot(Linear.Model, which=5, cook.levels=cutoff)

  • Remove those with large Cooks’ Distances (if we want too)
CorrDataT$CooksD<-(cooks.distance(Linear.Model))

CorrDataT_Cleaned<-subset(CorrDataT,CooksD < cutoff)
nrow(CorrDataT_Cleaned)
## [1] 94

3.1.5 Another method of testing for influence

  • Bonferonni p-value for most extreme obs
  • Notice it gives different number of outliers
  • Not all tests agree
# Assessing Outliers
outlierTest(Linear.Model) # Bonferonni p-value for most extreme obs
## 
## No Studentized residuals with Bonferonni p < 0.05
## Largest |rstudent|:
##     rstudent unadjusted p-value Bonferonni p
## 94 -2.839201          0.0055205      0.55205

3.1.6 Linearity and autocorrelation tests

  • Green lines should show straight lines relative to the red dotted line
  • The auto-correlation test should be non-significant (means that each residual is not correlated to the next residual)
# Evaluate Nonlinearity
# component + residual plot aka partial-residual plots
crPlots(Linear.Model)

# Test for Autocorrelated Errors
durbinWatsonTest(Linear.Model)
##  lag Autocorrelation D-W Statistic p-value
##    1     -0.01907508      1.967401   0.878
##  Alternative hypothesis: rho != 0
LS0tDQp0aXRsZTogJ011bHRpcGxlIFJlZ3Jlc3Npb24nDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgZm9udHNpemU6IDhwdA0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCi0tLQ0KDQojIENhdXNhbCBNb2RlbGluZw0KLSBYIGNhdXNlcyBZLiBFYXRpbmcgdG9vIG1hbnkgY29va2llcyB3aWxsIG1ha2UgeW91IGdhaW4gd2VpZ2h0DQotIFggLT4gWSA7IENvb2tpZXMgLT4gV3QNCi0gV2UgbmVlZCB0byBtYWtlIHN1cmUgdGhhdCB0aGUgY2F1c2UgaXMgbm90IHNwdXJpb3VzLg0KLSBDYXVzYWwgUGF0aCBhbmFseXNpcyANCg0KIyMgRGlyZWN0IGFuZCBJbmRpcmVjdCBFZmZlY3RzIA0KLSBEaXJlY3Q6IFgxIC0+IFkgKHdoaWNoIGNhbiBjb250cm9sIGZvciBYMikNCi0gSW5kaXJlY3QgWDEgLT4gWDIgLT4gWSA7IENvb2tpZXMgLT4gV3QgKG1vZGVyYXRlZCBieSBleGVyY2lzZSkNCi0gV2Ugd2lsbCBsb29rIGF0IHRoZXNlIGNvbmNlcHRzIG1vcmUgaW4gZGV0YWlsIHdoZW4gd2UgdGFsayBtb2RlcmF0aW9uIGFuYWx5c2lzDQoNCiMjIyBCYWNrIHRvIG91ciBsYXN0IHdlZWtzIGV4YW1wbGU6IA0KLSBNb2RlbCB3ZSB1c2VkIHdhcyBQcmVkaWN0ZWQgSGFwcGluZXNzIChZKSB+IFNsb3BlIEljZSBDcmVhbShYMSkgKyBTbG9wZSBCcm93bmllcyAoeDIpICsgSW50ZXJjZXB0DQotaW4gZm9ybXVsYXM6DQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCiNwYWNrYWdlcyB3ZSB3aWxsIG5lZWQgdG8gY29uZHVjdCB0byBjcmVhdGUgYW5kIGdyYXBoIG91ciBkYXRhDQpsaWJyYXJ5KE1BU1MpICNjcmVhdGUgZGF0YQ0KbGlicmFyeShjYXIpICNncmFwaCBkYXRhDQpgYGANCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnB5MSA9LjYgI0NvciBiZXR3ZWVuIFgxIChpY2UgY3JlYW0pIGFuZCBoYXBwaW5lc3MNCnB5MiA9LjQgI0NvciBiZXR3ZWVuIFgyIChCcm93bmllcykgYW5kIGhhcHBpbmVzcw0KcDEyPSAuMiAjQ29yIGJldHdlZW4gWDEgKGljZSBjcmVhbSkgYW5kIFgyIChCcm93bmllcykNCg0KTWVhbnMuWDFYMlk8LSBjKDEwLDEwLDEwKSAjc2V0IHRoZSBtZWFucyBvZiBYIGFuZCBZIHZhcmlhYmxlcw0KQ292TWF0cml4LlgxWDJZIDwtIG1hdHJpeChjKDEscDEyLHB5MSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwMTIsMSxweTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHkxLHB5MiwxKSwzLDMpICMgY3JlYXRlcyB0aGUgY292YXJpYXRlIG1hdHJpeCANCg0KI2J1aWxkIHRoZSBjb3JyZWxhdGVkIHZhcmlhYmxlcy4gTm90ZTogZW1waXJpY2FsPVRSVUUgbWVhbnMgbWFrZSB0aGUgY29ycmVsYXRpb24gRVhBQ1RMWSByLiANCnNldC5zZWVkKDQyKQ0KQ29yckRhdGFUPC1tdnJub3JtKG49MTAwLCBtdT1NZWFucy5YMVgyWSxTaWdtYT1Db3ZNYXRyaXguWDFYMlksIGVtcGlyaWNhbD1UUlVFKQ0KI0NvdmVydCB0aGVtIHRvIGEgIkRhdGEuRnJhbWUiLCB3aGljaCBpcyBsaWtlIFNQU1MgZGF0YSB3aW5kb3cNCkNvcnJEYXRhVDwtYXMuZGF0YS5mcmFtZShDb3JyRGF0YVQpDQojbGV0cyBhZGQgb3VyIGxhYmVscyB0byB0aGUgdmVjdG9ycyB3ZSBjcmVhdGVkDQpjb2xuYW1lcyhDb3JyRGF0YVQpIDwtIGMoIkljZUNyZWFtIiwiQnJvd25pZXMiLCJIYXBwaW5lc3MiKQ0KDQojIyMjIyMjIyMjIyMjIyNNb2RlbCAxIA0KSWNlLk1vZGVsPC1sbShIYXBwaW5lc3N+IEljZUNyZWFtLCBkYXRhID0gQ29yckRhdGFUKQ0Kc3VtbWFyeShJY2UuTW9kZWwpDQojIyMjIyMjIyMjIyMjIyNNb2RlbCAyDQpJY2UuQnJvd24uTW9kZWw8LWxtKEhhcHBpbmVzc34gSWNlQ3JlYW0rQnJvd25pZXMsIGRhdGEgPSBDb3JyRGF0YVQpDQpzdW1tYXJ5KEljZS5Ccm93bi5Nb2RlbCkNCg0KYGBgDQoNCiMjIyBQbG90cw0KLSBOb3cgcGlsb3RpbmcgaXQgYSBiaXQgbW9yZSBjb21wbGV4IGFzIHdlIGhhdmUgdHdvIHByZWRpY3RvcnMNCi0gU2ltcGxlIHNjYXR0ZXIgcGxvdHMgYXJlIG5vdCBzZW1pLXBhcnRpYWxlZA0KLSBBbHNvIHdoaWxlIHdlIGFyZSBub3QgaW50ZXJhY3RpbmcgdGhlIHR3byB2YXJpYWJsZXMsIGVhY2ggaGFzIGFuIGVmZmVjdCBvbiB0aGUgRFYNCi0gV2UgbXVzdCB2aXN1YWxpemUgYm90aCBlZmZlY3RzDQotIFRoZXJlIGFyZSBkaWZmZXJlbnQgc3VnZ2VzdGlvbnMgb24gaG93IHRvIGRvIHRoaXMgW3dlIHdpbGwgY29tZSBiYWNrIHRvIGRpZmZlcmVuY2VzIGluIHBsb3R0aW5nIGxhdGVyXQ0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQojcGFja2FnZXMgd2Ugd2lsbCBuZWVkIHRvIHBsb3Qgb3VyIHJlc3VsdHMNCmxpYnJhcnkoZWZmZWN0cykNCmBgYA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KI3Bsb3QgaW5kaXZpZHVhbCBlZmZlY3RzDQpJY2UuQnJvd24uTW9kZWwuUGxvdCA8LSBhbGxFZmZlY3RzKEljZS5Ccm93bi5Nb2RlbCwgeGxldmVscz1saXN0KEljZUNyZWFtPXNlcSg4LCAxMiwgMSksQnJvd25pZXM9c2VxKDgsIDEyLCAxKSkpDQpwbG90KEljZS5Ccm93bi5Nb2RlbC5QbG90LCAnSWNlQ3JlYW0nLCB5bGFiPSJIYXBwaW5lc3MiKQ0KcGxvdChJY2UuQnJvd24uTW9kZWwuUGxvdCwgJ0Jyb3duaWVzJywgeWxhYj0iSGFwcGluZXNzIikNCg0KI3Bsb3QgYm90aCBlZmZlY3RzDQpJY2UuQnJvd24uTW9kZWwuUGxvdDI8LUVmZmVjdChjKCJJY2VDcmVhbSIsICJCcm93bmllcyIpLCBJY2UuQnJvd24uTW9kZWwseGxldmVscz1saXN0KEljZUNyZWFtPWMoOCwgMTIpLCBCcm93bmllcz1jKDgsMTIpKSkNCnBsb3QoSWNlLkJyb3duLk1vZGVsLlBsb3QyKQ0KDQpgYGANCg0KIyMjIEZ1bGx5IEluZHJlY3QNCi0gTm90IHNwdXJpb3VzLCBidXQgWDEgaXMgbm90IGRpcmVjdCB0byBZIChYMiBpcyBpbnRlcnZlbmluZyBjb21wbGV0ZWx5KQ0KDQoNCiMjIyBGdWxsIFJlZHVuZGFuY3kgKFNwdXJpb3VzIG1vZGVscykNCi0gd2hlbiwgJHJfe1kyfSBcYXBwcm94IHJfe1kxfXJfezEyfSQNCi0gWDEgaXMgY29uZm91bmRlZCBpbiBYMiANCi0gWDEgYW5kIFgyIGFyZSBjb21wbGV0ZWx5IHJlZHVuZGFudA0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KSS5weTEgPS42ICNDb3IgYmV0d2VlbiBYMSBPcmVvcyBhbmQgaGFwcGluZXNzDQpJLnB5MiA9LjYgI0NvciBiZXR3ZWVuIFgyIENvb2tpZXMgYW5kIGhhcHBpbmVzcw0KSS5wMTIgPSAuOTkgI0NvciBiZXR3ZWVuIFgxIE9yZW9zIGFuZCBYMiBDb29raWVzDQoNCkkuTWVhbnMuWDFYMlk8LSBjKDEwLDEwLDEwKSAjc2V0IHRoZSBtZWFucyBvZiBYIGFuZCBZIHZhcmlhYmxlcw0KSS5Db3ZNYXRyaXguWDFYMlkgPC0gbWF0cml4KGMoMSxJLnAxMixJLnB5MSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBJLnAxMiwxLEkucHkyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIEkucHkxLEkucHkyLDEpLDMsMykgIyBjcmVhdGVzIHRoZSBjb3ZhcmlhdGUgbWF0cml4IA0KDQojYnVpbGQgdGhlIGNvcnJlbGF0ZWQgdmFyaWFibGVzLiBOb3RlOiBlbXBpcmljYWw9VFJVRSBtZWFucyBtYWtlIHRoZSBjb3JyZWxhdGlvbiBFWEFDVExZIHIuIA0KIyBpZiB3ZSBzYXkgZW1waXJpY2FsPUZBTFNFLCB0aGUgY29ycmVsYXRpb24gd291bGQgYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYXJvdW5kIHINCnNldC5zZWVkKDQyKQ0KSURhdGE8LW12cm5vcm0obj0xMDAsIG11PUkuTWVhbnMuWDFYMlksU2lnbWE9SS5Db3ZNYXRyaXguWDFYMlksIGVtcGlyaWNhbD1UUlVFKQ0KI0NvdmVydCB0aGVtIHRvIGEgIkRhdGEuRnJhbWUiLCB3aGljaCBpcyBsaWtlIFNQU1MgZGF0YSB3aW5kb3cNCklEYXRhPC1hcy5kYXRhLmZyYW1lKElEYXRhKQ0KI2xldHMgYWRkIG91ciBsYWJlbHMgdG8gdGhlIHZlY3RvcnMgd2UgY3JlYXRlZA0KY29sbmFtZXMoSURhdGEpIDwtIGMoIk9yZW9zIiwiQ29va2llcyIsIkhhcHBpbmVzcyIpDQoNCiMjIyMjIyMjIyMjIyMjI01vZGVsIDEgDQpzdW1tYXJ5KGxtKEhhcHBpbmVzc34gT3Jlb3MsIGRhdGEgPSBJRGF0YSkpDQoNCiMjIyMjIyMjIyMjIyMjI01vZGVsIDIgDQpzdW1tYXJ5KGxtKEhhcHBpbmVzc34gQ29va2llcywgZGF0YSA9IElEYXRhKSkNCg0KIyMjIyMjIyMjIyMjIyMjTW9kZWwgMw0Kc3VtbWFyeShsbShIYXBwaW5lc3N+IE9yZW9zK0Nvb2tpZXMsIGRhdGEgPSBJRGF0YSkpDQpgYGANCg0KDQojIyMgU3VwcHJlc3Npb24NCi0gU3VwcHJlc3Npb24gaXMgYSBzdHJhbmdlIGNhc2Ugd2hlcmUgdGhlIElWIC0+IERWIHJlbGF0aW9uc2hpcCBpcyBoaWRkZW4gKHN1cHByZXNzZWQpIGJ5IGFub3RoZXIgdmFyaWFibGUNCi0gVGF4IGN1dHMgY2F1c2UgZ3Jvd3RoLCB0YXggY3V0cyBjYXVzZSBpbmZsYXRpb24uIFRheCBjdXRzIGFsb25lIG1pZ2h0IGxvb2sgKywgYnV0IGFkZCBpbiBpbmZsYXRpb24gYW5kIG5vdyB0YXggY3V0cyBjb3VsZCBtYWtlIGNhdXNlIHRoZSBncm93dGggdG8gbG9vayBkaWZmZXJlbnQNCi0gT3IgdGhlIHRoZSBzdXBwcmVzc29yIHZhcmlhYmxlIGNhbiBjYXVzZSBhIGZsaXAgaW4gdGhlIHNpZ24gb2YgdGhlIHJlbGF0aW9uc2hpcA0KLSBIZXJlIGlzIGFuIGV4YW1wbGUgd2hlcmUgdGhlIGVmZmVjdCB3YXMgaGlkZGVuOg0KIA0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCnN1cC5weTEgPSAgMi41ICNDb3ZhciBiZXR3ZWVuIHRheCBjdXRzIGFuZCBncm93dGggDQpzdXAucHkyID0gLTUuNSAjQ292YXIgYmV0d2VlbiBpbmZsYXRpb24gYW5kIGdyb3d0aCANCnN1cC5wMTIgPSAgNCAgI0NvdmFyIGJldHdlZW4gdGF4IGN1dHMgYW5kIGluZmxhdGlvbg0KDQoNClN1cHAuWDFYMlk8LSBjKDUsNSw1KSAjc2V0IHRoZSBtZWFucyBvZiBYIGFuZCBZIHZhcmlhYmxlcw0KU3VwcC5Db3ZNYXRyaXguWDFYMlkgPC0gbWF0cml4KGMoMTAsc3VwLnAxMixzdXAucHkxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VwLnAxMiwxMCxzdXAucHkyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VwLnB5MSxzdXAucHkyLDEwKSwzLDMpICMgY3JlYXRlcyB0aGUgY292YXJpYXRlIG1hdHJpeCANCg0KDQojYnVpbGQgdGhlIGNvcnJlbGF0ZWQgdmFyaWFibGVzLiBOb3RlOiBlbXBpcmljYWw9VFJVRSBtZWFucyBtYWtlIHRoZSBjb3JyZWxhdGlvbiBFWEFDVExZIHIuIA0KIyBpZiB3ZSBzYXkgZW1waXJpY2FsPUZBTFNFLCB0aGUgY29ycmVsYXRpb24gd291bGQgYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYXJvdW5kIHINCnNldC5zZWVkKDQyKQ0KU3VwcERhdGE8LW12cm5vcm0obj0xMDAsIG11PVN1cHAuWDFYMlksU2lnbWE9U3VwcC5Db3ZNYXRyaXguWDFYMlksIGVtcGlyaWNhbD1UUlVFKQ0KDQojQ292ZXJ0IHRoZW0gdG8gYSAiRGF0YS5GcmFtZSINClN1cHBEYXRhPC1hcy5kYXRhLmZyYW1lKFN1cHBEYXRhKQ0KY29sbmFtZXMoU3VwcERhdGEpIDwtIGMoIlRheEN1dHMiLCJpbmZsYXRpb24iLCJncm93dGgiKQ0KDQojIyMjIyMjIyMjIyMjIyNNb2RlbCAxIA0KVGF4Q3V0c09ubHk8LShsbShncm93dGh+IFRheEN1dHMsIGRhdGEgPSBTdXBwRGF0YSkpDQpzdW1tYXJ5KFRheEN1dHNPbmx5KQ0KDQojIyMjIyMjIyMjIyMjIyNNb2RlbCAyDQpGdWxsLk1vZGVsPC1sbShncm93dGh+IFRheEN1dHMraW5mbGF0aW9uLCBkYXRhID0gU3VwcERhdGEpDQpzdW1tYXJ5KEZ1bGwuTW9kZWwpDQoNCmBgYA0KDQotIFlvdSB3aWxsIG5vdGljZSB0YXggY3V0cyBhbG9uZSwgYHIgVGF4Q3V0c09ubHkkY29lZmZpY2llbnRzWzJdYCwgd2FzIGxvd2VyIHRoYW4gYHIgRnVsbC5Nb2RlbCRjb2VmZmljaWVudHNbMl1gIGNvbnRyb2xsaW5nIGZvciBpbmZsYXRpb24uIA0KLSBIb3dldmVyLCB5b3Ugd2lsbCBub3RpY2UgaW5mbGF0aW9uLGByIEZ1bGwuTW9kZWwkY29lZmZpY2llbnRzWzNdYCwgaGFzIGEgYmlnZ2VyIGVmZmVjdCAoYW5kIG5lZ2F0aXZlKSBlZmZlY3QgdGhhbiB0YXggY3V0cyBgciBGdWxsLk1vZGVsJGNvZWZmaWNpZW50c1syXWAgDQotIFNvIGluIG90aGVyIHdvcmRzLCB5ZXMgdGF4IGN1dHMgd29yayBidXQgdGhleSBkb24ndCBvdmVycmlkZSBpbmZsYXRpb24hIA0KDQojIyMgTGV0cyB0cnkgdG8gZ3JhcGggb3VyIHByZWRpY3Rpb25zDQotIENoYWxsZW5nZSBpcyB0aGF0IHdlIG5lZWQgdG8gYWNjb3VudCBmb3IgMiBwcmVkaWN0b3JzIG5vdy4gDQotIFNvIHNpbXBsZSBzY2F0dGVyIHBsb3RzIHdpbGwgbm90IHdvcmsNCi0gQWxzbyBob3cgeW91IHBsb3QgZGVwZW5kcyBvbiB5b3VyIHRoZW9yeS9leHBlcmltZW50L3N0b3J5DQotIFdlIG5lZWQgdG8gY29udHJvbCBmb3IgaW5mbGF0aW9uIChvciB2aWV3IHRheCBzbG9wZSBhdCBkaWZmZXJlbnQgbGV2ZWxzIG9mIGluZmxhdGlvbikNCi0gRmlyc3QgbGV0cyB2aWV3IHRoZSB0YXggc2xvcGUgd2l0aG91dCBjb250cm9sbGluZyBmb3IgaW5mbGF0aW9uDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQojcGxvdCBpbmRpdmlkdWFsIGVmZmVjdHMNClRheC5Nb2RlbC5QbG90IDwtIGFsbEVmZmVjdHMoVGF4Q3V0c09ubHksIHhsZXZlbHM9bGlzdChUYXhDdXRzPXNlcSgzLCA3LCAxKSkpDQpwbG90KFRheC5Nb2RlbC5QbG90LCAnVGF4Q3V0cycsIHlsYWI9Ikdyb3d0aCIpDQoNCmBgYA0KDQotIE5vdyBsZXRzIHZpZXcgaXQgY29udHJvbGxpbmcgZm9yIGluZmxhdGlvbg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCg0KI3Bsb3QgaW5kaXZpZHVhbCBlZmZlY3RzDQpGdWxsLk1vZGVsLlBsb3QgPC0gYWxsRWZmZWN0cyhGdWxsLk1vZGVsLCB4bGV2ZWxzPWxpc3QoVGF4Q3V0cz1zZXEoMywgNywgMSksaW5mbGF0aW9uPXNlcSgzLCA3LCAxKSkpDQpwbG90KEZ1bGwuTW9kZWwuUGxvdCwgJ1RheEN1dHMnLCB5bGFiPSJHcm93dGgiKQ0KcGxvdChGdWxsLk1vZGVsLlBsb3QsICdpbmZsYXRpb24nLCB5bGFiPSJHcm93dGgiKQ0KDQojcGxvdCBib3RoIGVmZmVjdHMNCkZ1bGwuTW9kZWwuUGxvdDI8LUVmZmVjdChjKCJUYXhDdXRzIiwgImluZmxhdGlvbiIpLCBGdWxsLk1vZGVsLHhsZXZlbHM9bGlzdChUYXhDdXRzPWMoMywgNyksIGluZmxhdGlvbj1jKDMsNykpKQ0KcGxvdChGdWxsLk1vZGVsLlBsb3QyKQ0KDQpgYGANCg0KDQoNCiMgRXN0aW1hdGluZyBwb3B1bGF0aW9uIFItc3F1YXJlZA0KLSAkXHJob14yJCBpcyBlc3RpbWF0ZWQgYnkgbXVsdGlwbGUgJFJeMiQNCi0gUHJvYmxlbTogaW4gc21hbGwgc2FtcGxlcyBjb3JyZWxhdGlvbnMgYXJlIHVuc3RhYmxlIChhbmQgaW5mbGF0ZWQpDQotIEFsc28sIGFzIHdlIGFkZCBwcmVkaWN0b3JzLCAkUl4yJCBnZXRzIGluZmxhdGVkDQotIFNvIHdlIHVzZSBhbiBhZGp1c3RlZCBtdWx0aXBsZSAkUl4yJA0KLSBBZGp1c3RlZCAkUl4yID0gMSAtIDEtIFJfWV4yIFxmcmFje24tMX17bi1rLTF9JA0KDQojICBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbiBBc3N1bXB0aW9ucw0KKiBNdWx0aWNvbGxpbmVhcml0eTogUHJlZGljdG9ycyBjYW5ub3QgYmUgZnVsbHkgKG9yIG5lYXJseSBmdWxseSkgcmVkdW5kYW50IFtjaGVjayB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gcHJlZGljdG9yc10NCiogSG9tb3NjZWRhc3RpY2l0eSBvZiByZXNpZHVhbHMgdG8gZml0dGVkIHZhbHVlcw0KKiBOb3JtYWwgZGlzdHJpYnV0aW9uIG9mIHJlc2lkdWFscw0KKiBBYnNlbmNlIG9mIG91dGxpZXJzDQoqIFJhdGlvIG9mIGNhc2VzIHRvIHByZWRpY3RvcnMNCiAgKyBOdW1iZXIgb2YgY2FzZXMgbXVzdCBleGNlZWQgdGhlIG51bWJlciBvZiBwcmVkaWN0b3JzIA0KICArIEJhcmVseSBhY2NlcHRhYmxlIG1pbmltdW06IDUgY2FzZXMgcGVyIHByZWRpY3Rvcg0KICArIFByZWZlcnJlZCBtaW5pbXVtOiAyMC0zMCBjYXNlcyBwZXIgcHJlZGljdG9yDQoqIExpbmVhcml0eSBvZiBwcmVkaWN0aW9uDQoqIEluZGVwZW5kZW5jZSAobm8gYXV0by1jb3JyZWxhdGVkIGVycm9ycykNCg0KIyMgSG93IHRvIGNoZWNrIGFzc3VtcHRpb25zPw0KLSBGaXJzdCBsZXRzIGZpdCB0aGUgbW9kZWwNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCkxpbmVhci5Nb2RlbDwtbG0oSGFwcGluZXNzfiBJY2VDcmVhbStCcm93bmllcywgZGF0YSA9IENvcnJEYXRhVCkNCmBgYA0KDQojIyMgTGV0cyB0ZXN0IGZvciBNdWx0aWNvbGxpbmVhcml0eQ0KLSBNdWx0aWNvbGxpbmVhcml0eSBjYW4gYmUgZGV0ZWN0ZWQgd2hlbiB0aGUgdmFyaWFuY2Ugb24gdGhlIHRlcm1zIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBiZWNvbWUgaW5mbGF0ZWQNCi0gU28gd2UgbmVlZCB0byB0ZXN0IHRoZSB2YXJpYW5jZSBvbiB0aGUgZmFjdG9ycy4gDQotIEJhc2ljIExvZ2ljYWwgc3RlcCAxKSBnZXQgVmFyaWFuY2UgIG9uIHByZWRpY3RvciAob25seSB0aGF0IHRlcm0gaW4gbW9kZWwpIFtWbWluXQ0KLSBCYXNpYyBMb2dpY2FsIHN0ZXAgMikgZ2V0IFZhcmlhbmNlIChWbWF4KSBvbiBwcmVkaWN0b3IgKGFsbCBvdGhlciB0ZXJtcyBpbiBtb2RlbCkgW1ZtYXhdDQotIEJhc2ljIExvZ2ljYWwgc3RlcCAzKSByYXRpbzogVm1heC9WbWluLiBpZiB0aGUgdmFsdWUgPiA0IHlvdSBoYXZlIGEgcHJvYmxlbQ0KLSB0aGF0J3MgYWxvdCBvZiBzdGVwcy4gUiBjYW4gZG8gaXQgZm9yIHlvdSBiYXNpY2FsbHkgd2l0aCBvbmUgbGluZSBvZiBjb2RlDQotIFZhcmlhbmNlIGluZmxhdGlvbiBmYWN0b3JzICh2aWYpDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KdmlmKExpbmVhci5Nb2RlbCkgIyB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9ycyANCnZpZihMaW5lYXIuTW9kZWwpID4gNCAjIHByb2JsZW0/DQpgYGANCg0KIyMjIGxldHMgY2hlY2sgbm9ybWFsaXR5LCBvdXRsaW5lYXJzIHZpc3VhbGx5IGZpcnN0IA0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCiNUbyBzZWUgdGhlbSBhbGwgYXQgb25jZQ0KbGF5b3V0KG1hdHJpeChjKDEsMiwzLDQpLDIsMikpICMgb3B0aW9uYWwgNCBncmFwaHMvcGFnZSANCnBsb3QoTGluZWFyLk1vZGVsKQ0KYGBgDQojIyMgUmVzaWR1YWxzIHZzIGZpdHRlZCBhbmQgc3FhdXJlLXJvb3RlZCByZXNpZHVhbHMgdnMgZml0dGVkIA0KLSBzaG91bGQgYmUgSG9tb3NjZWRhc3Rpc3RpYyAocmFuZG9tbHkgZGlzdHJpYnV0ZWQgYXJvdW5kIDApDQotIFRoZXJlIGlzIHN0YXRpc3RpY2FsIHRlc3QgKGluIFIsIGJ1dCBub3QgU1BTUykNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCg0KIyBFdmFsdWF0ZSBob21vc2NlZGFzdGljaXR5DQojIG5vbi1jb25zdGFudCBlcnJvciB2YXJpYW5jZSB0ZXN0DQpuY3ZUZXN0KExpbmVhci5Nb2RlbCkNCg0KDQoNCmBgYA0KDQojIyMgUS1RIHBsb3QgDQotIHNob3VsZCBiZSBhIHN0cmFpZ2h0IGxpbmUNCi0geW91IGNhbiBhbHNvIHZpc3VhbGl6ZSB0aGUgcmVzaWR1YWxzIGFzIGEgaGlzdG9ncmFtDQoNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQojIGRpc3RyaWJ1dGlvbiBvZiBzdHVkZW50aXplZCByZXNpZHVhbHMNCnNyZXNpZCA8LSBzdHVkcmVzKExpbmVhci5Nb2RlbCkgDQpoaXN0KHNyZXNpZCwgZnJlcT1GQUxTRSwgDQogICAgIG1haW49IkRpc3RyaWJ1dGlvbiBvZiBTdHVkZW50aXplZCBSZXNpZHVhbHMiKQ0KeGZpdDwtc2VxKG1pbihzcmVzaWQpLG1heChzcmVzaWQpLGxlbmd0aD00MCkgDQp5Zml0PC1kbm9ybSh4Zml0KSANCmxpbmVzKHhmaXQsIHlmaXQpDQpgYGANCg0KIyMjIENvb2sncyBkaXN0YW5jZXMNCi0gSXRzIHBvc3NpYmxlIGZvciBvbmUgb3IgbW9yZSBvYnNlcnZhdGlvbnMgdG8gInB1bGwiIHRoZSByZWdyZXNzaW9uIGxpbmUgYXdheSBmcm9tIHRoZSB0cmVuZCBpbiB0aGUgcmVzdCBvZiB0aGUgZGF0YQ0KLSBDb29rJ3MgRGlzdGFuY2UgaXMgb25lIG9mIHNldmVyYWwgcG9zc2libGUgbWVhc3VyZXMgb2YgaW5mbHVlbmNlLg0KLSBDb29rJ3MgRCBpcyB0aGUgc3VtIG9mIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgdmFsdWUgYW5kIHRoZSBvdGhlciBwcmVkaWN0ZWQgdmFsdWVzIGluIHRoZSBkYXRhIHNldCwgZGl2aWRlZCBieSB0aGUgZXJyb3IgdmFyaWFuY2UgdGltZXMgdGhlIG51bWJlciBvZiBwYXJhbWV0ZXJzIGluIHRoZSBtb2RlbC4gDQotIFIgZGlkIHRoaXMgYXV0b21hdGljYWxseSBpbiBwbG90IDQgYW5kIHdlIGNhbiBzZWUgYSBmZXcgcGVvcGxlIHdpdGggbnVtYmVycyAodGhvc2UgYXJlIHRoZSBvdXRsaWVycykNCi0gV2UgY2FuIGFsc28gc2VlIHRoZSBpbmZsdWVuY2Ugd2l0aCBhIGxpdHRsZSBSIGNvZGluZyAoc2V0IGEgbW9yZSBjb25zZXJ2YXRpdmUgdGhyZXNob2xkKQ0KDQpgYGB7ciwgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDb29rJ3MgRCBwbG90DQojIGlkZW50aWZ5IEQgdmFsdWVzID4gNC8obi1rLTEpIA0KY3V0b2ZmIDwtIDQvKChucm93KENvcnJEYXRhVCktbGVuZ3RoKExpbmVhci5Nb2RlbCRjb2VmZmljaWVudHMpLTIpKSANCmN1dG9mZg0KcGxvdChMaW5lYXIuTW9kZWwsIHdoaWNoPTQsIGNvb2subGV2ZWxzPWN1dG9mZikNCnBsb3QoTGluZWFyLk1vZGVsLCB3aGljaD01LCBjb29rLmxldmVscz1jdXRvZmYpDQpgYGANCg0KLSBSZW1vdmUgdGhvc2Ugd2l0aCBsYXJnZSBDb29rcycgRGlzdGFuY2VzIChpZiB3ZSB3YW50IHRvbykNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpDb3JyRGF0YVQkQ29va3NEPC0oY29va3MuZGlzdGFuY2UoTGluZWFyLk1vZGVsKSkNCg0KQ29yckRhdGFUX0NsZWFuZWQ8LXN1YnNldChDb3JyRGF0YVQsQ29va3NEIDwgY3V0b2ZmKQ0KbnJvdyhDb3JyRGF0YVRfQ2xlYW5lZCkNCg0KYGBgDQoNCiMjIyBBbm90aGVyIG1ldGhvZCBvZiB0ZXN0aW5nIGZvciBpbmZsdWVuY2UNCi0gQm9uZmVyb25uaSBwLXZhbHVlIGZvciBtb3N0IGV4dHJlbWUgb2JzDQotIE5vdGljZSBpdCBnaXZlcyBkaWZmZXJlbnQgbnVtYmVyIG9mIG91dGxpZXJzDQotIE5vdCBhbGwgdGVzdHMgYWdyZWUNCg0KYGBge3IsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRX0NCiMgQXNzZXNzaW5nIE91dGxpZXJzDQpvdXRsaWVyVGVzdChMaW5lYXIuTW9kZWwpICMgQm9uZmVyb25uaSBwLXZhbHVlIGZvciBtb3N0IGV4dHJlbWUgb2JzDQoNCmBgYA0KDQojIyMgTGluZWFyaXR5IGFuZCBhdXRvY29ycmVsYXRpb24gdGVzdHMNCi0gR3JlZW4gbGluZXMgc2hvdWxkIHNob3cgc3RyYWlnaHQgbGluZXMgcmVsYXRpdmUgdG8gdGhlIHJlZCBkb3R0ZWQgbGluZQ0KLSBUaGUgYXV0by1jb3JyZWxhdGlvbiB0ZXN0IHNob3VsZCBiZSBub24tc2lnbmlmaWNhbnQgKG1lYW5zIHRoYXQgZWFjaCByZXNpZHVhbCBpcyBub3QgY29ycmVsYXRlZCB0byB0aGUgbmV4dCByZXNpZHVhbCkNCmBgYHtyLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQojIEV2YWx1YXRlIE5vbmxpbmVhcml0eQ0KIyBjb21wb25lbnQgKyByZXNpZHVhbCBwbG90IGFrYSBwYXJ0aWFsLXJlc2lkdWFsIHBsb3RzDQpjclBsb3RzKExpbmVhci5Nb2RlbCkNCg0KDQojIFRlc3QgZm9yIEF1dG9jb3JyZWxhdGVkIEVycm9ycw0KZHVyYmluV2F0c29uVGVzdChMaW5lYXIuTW9kZWwpDQoNCg0KYGBgDQoNCg0KDQo8c2NyaXB0Pg0KICAoZnVuY3Rpb24oaSxzLG8sZyxyLGEsbSl7aVsnR29vZ2xlQW5hbHl0aWNzT2JqZWN0J109cjtpW3JdPWlbcl18fGZ1bmN0aW9uKCl7DQogIChpW3JdLnE9aVtyXS5xfHxbXSkucHVzaChhcmd1bWVudHMpfSxpW3JdLmw9MSpuZXcgRGF0ZSgpO2E9cy5jcmVhdGVFbGVtZW50KG8pLA0KICBtPXMuZ2V0RWxlbWVudHNCeVRhZ05hbWUobylbMF07YS5hc3luYz0xO2Euc3JjPWc7bS5wYXJlbnROb2RlLmluc2VydEJlZm9yZShhLG0pDQogIH0pKHdpbmRvdyxkb2N1bWVudCwnc2NyaXB0JywnaHR0cHM6Ly93d3cuZ29vZ2xlLWFuYWx5dGljcy5jb20vYW5hbHl0aWNzLmpzJywnZ2EnKTsNCg0KICBnYSgnY3JlYXRlJywgJ1VBLTkwNDE1MTYwLTEnLCAnYXV0bycpOw0KICBnYSgnc2VuZCcsICdwYWdldmlldycpOw0KDQo8L3NjcmlwdD4=