Correlations with 2 or more variables

  • Correlations with more than 2 variables present a new challenge
  • What if a third variable (X2) actually explains the relationship between X1 and Y?
  • We need to find a way to figure out how X2 might relate to X1 and Y!

Example

  • Practice Time (X1)
  • Performance Anxiety (X2)
  • Memory Errors (Y)
  • Question, how much does Practice Time and Performance Anxiety predict/explain Memory Errors in performance
  • Lets simulate our dataset with the new variables and generate a specific correlation matrix
Variable Practice Time (X1) Performance Anxiety (X2) Memory Errors (Y)
Practice Time (X1) 1 .3 .6
Performance Anxiety (X2) .3 1 .4
Memory Errors (Y) .6 .4 1
  • Set the mean number for each variable to be 10
#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 (Practice Time) and Memory Errors
py2 =.4 #Cor between X2 (Performance Anxiety) and Memory Errors
p12= .3 #Cor between X1 (Practice Time) and X2 (Performance Anxiety)
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. 
# if we say empirical=FALSE, the correlation would be normally distributed around r
set.seed(42)
CorrDataT<-mvrnorm(n=100, mu=Means.X1X2Y,Sigma=CovMatrix.X1X2Y, empirical=TRUE)
#Convert them to a "Data.Frame" & add our labels to the vectors we created
CorrDataT<-as.data.frame(CorrDataT)
colnames(CorrDataT) <- c("Practice","Anxiety","Memory")
#make the scatter plots
scatterplot(Memory~Practice,CorrDataT, smoother=FALSE)
scatterplot(Memory~Anxiety,CorrDataT, smoother=FALSE)
scatterplot(Anxiety~Practice,CorrDataT, smoother=FALSE)
# Pearson Correlations
ry1<-cor(CorrDataT$Memory,CorrDataT$Practice)
ry2<-cor(CorrDataT$Memory,CorrDataT$Anxiety)
r12<-cor(CorrDataT$Anxiety,CorrDataT$Practice)

What the problem?

  • Practice Time can explain Memory Errors, \(r^2 =\) 0.36
  • Anxiety can explain Memory Errors, \(r^2 =\) 0.16
  • But how we do know whether Practice Time and Anxiety are explaining the same variance? Anxiety and Practice Time explain each other a little, \(r^2 =\) 0.09

Multiple R

  • We use the capital letter, \(R\), now cause we have multiple variables
  • \(R_{Y.12} = \sqrt{\frac{r_{Y1}^2 + r_{Y2}^2 - 2r_{Y1} r_{Y2} r_{12}} {1 - r_{12}^2}}\)
  • \(R_{Y.12} =\) 0.6427961
  • if we square that value, 0.3340659, we get the Multiple \(R^2\)
    • i.e., the total variance explained by these variables on Memory

Semipartial (part) correlation

  • We need to define to contribution of each X variable on Y
  • Semipartial (also called part) is one of two methods; the other is called partial
    • is called semi, cause it removes the effect of one IV relative to the other without removing the relationship to Y
  • Semipartial correlations indicate the “unique” contribution of an independent variable on the dependent variable.
    • When we get to back to regression, “What is the contribution of this X above and beyond the other X variable?”

Ballantine for X1, X2, and Y

  • \(R_{Y.12}^2 = a + b + c\)
  • \(sr_1^2: a = R_{Y.12}^2 - r_{Y2}^2\)
  • \(sr_2^2:b = R_{Y.12}^2 - r_{Y1}^2\)

Calcuation

  • Calculate unique variance
R2y.12<-sqrt((ry1^2+ry2^2 - (2*ry1*ry2*r12))/(1-r12^2))^2
a = R2y.12 -ry2^2 
b = R2y.12 -ry1^2 
  • In other words,
    • In total we explained, 0.4131868 of the Memory Errors
    • Practice Time uniquely explained, 0.2531868 of Memory Errors
    • Anxiety uniquely explained, 0.0531868 of Memory Errors
    • We should not solve for c cause it can be negative (in some weird cases)

Seeing control in action in regression

  • Another way to understand it:
    • What if you want to know about Memory Errors and how Practice Time uniquely explains it?
      • Controlling the effect of Anxiety on Practice Time

Memory (Y) ~ Practice(X1) [Control Anxiety (X2)]

  1. We can remove the effect of Anxiety on Practice Time by extracting the residuals from lm(X1~X2)
  2. Remember the residuals are the leftover (after extracting what was explainable)
scatterplot(Practice~Anxiety,CorrDataT, smoother=FALSE)
CorrDataT$Practice.control.Anxiety<-residuals(lm(Practice~Anxiety, CorrDataT))
scatterplot(Practice.control.Anxiety~Anxiety,CorrDataT, smoother=FALSE)

  1. Next we can correlate Memory Errors with the residualized Practice Time, cor(Memory,Practice[control Anxiety])
Sr1.alt<-cor(CorrDataT$Memory,CorrDataT$Practice.control.Anxiety)

If we square the correlation value we got 0.5031767, it becomes 0.2531868 which matches our \(a\) from the analysis above.

Memory (Y) ~ Anxiety(X2) [Control Practice (X1)]

  1. We can remove effect of Practice on Anxiety by extracting the residuals from lm(X2~X1)
  2. Remember the residuals are the leftover (after extracting what was explainable)
scatterplot(Anxiety~Practice,CorrDataT, smoother=FALSE)
CorrDataT$Anxiety.control.Practice<-residuals(lm(Anxiety~Practice, CorrDataT))
scatterplot(Anxiety.control.Practice~Practice,CorrDataT, smoother=FALSE)

  1. Next we can correlate Memory Errors with the residualized Anxiety, cor(Memory,Anxiety[control Practice])
Sr2.alt<-cor(CorrDataT$Memory,CorrDataT$Anxiety.control.Practice)

If we square the correlation value we got 0.2306227, it becomes 0.0531868 which matches our \(b\) from the analysis above.

  • In regression when we have more than one predictors they are controlling for each other!

Semipartial notes:

  • It can be written as \(sr\) or more specifically, \(sr_1\) for X1 (with X2 removed) and \(sr_2\) (with X1 removed)
  • correlations with no control variables are called the zero-order correlations
  • in R you can calculate the \(sr\) rather quickly using the ppcor library
  • The last variable in the list is the control
library(ppcor)
Sr1<-spcor.test(CorrDataT$Memory, CorrDataT$Practice, CorrDataT$Anxiety)
Sr2<-spcor.test(CorrDataT$Memory, CorrDataT$Anxiety, CorrDataT$Practice)
# Extract result call for Sr1$estimate
  • The Semi-partial correlation between Memory and Practice (but controlling for Anxiety) was \(sr\) =0.503
    • If we square it becomes \(sr^2\) = 0.253 which matches our \(a\) from the analysis above.
  • The Semi-partial correlation between Memory and Anxiety (but controlling for Practice) was \(sr\) = 0.231
    • If we square it becomes \(sr^2\) = 0.053 which matches our \(b\) from the analysis above.

Relationship to Regression

  • Lets say we want to report how practice is related to memory in performance, but we want to control for anxiety?
  • The \(sr^2\) for a variable tells us how much \(R^2\) will decrease if that variable is removed from the regression equation
    • Lets test it
    • Lets run 3 regression models (I zscored everything to make the scales all the same)
      • lm(Memory ~ Practice)
      • lm(Memory ~ Anxiety)
      • lm(Memory ~ Practice + Anxiety)
# Center variables (if you said scale = TRUE it would zscore the predictors)
CorrDataT$Memory.Z<-scale(CorrDataT$Memory, scale=TRUE, center=TRUE)[,]
CorrDataT$Practice.Z<-scale(CorrDataT$Practice, scale=TRUE, center=TRUE)[,]
CorrDataT$Anxiety.Z<-scale(CorrDataT$Anxiety, scale=TRUE, center=TRUE)[,]
###############Model 1
M.Model.1<-lm(Memory.Z~ Practice.Z, data = CorrDataT)
M.Model.2<-lm(Memory.Z~ Anxiety.Z, data = CorrDataT)
M.Model.3<-lm(Memory.Z~ Practice.Z+Anxiety.Z, data = CorrDataT)
library(stargazer)
stargazer(M.Model.1,M.Model.2,M.Model.3,type="latex",
          intercept.bottom = FALSE, single.row=TRUE, 
          star.cutoffs=c(.05,.01,.001), notes.append = FALSE,
          header=FALSE)

Thus, \[R_{model.3_{P+A}}^2 - sr_{Anxiety}^2 = R^2_{Practice.only}\]

  • In R code:
R2.Practice.only = (summary(M.Model.3)$r.squared) - (Sr2$estimate^2)
  • So 0.36 = \(R_{Model.1_{Practice}}^2\), as expected

Thus, \[R_{model.3.P+A}^2 - sr_{Practice}^2 = R^2_{Anxiety.only}\]

  • In R code:
R2.Anxiety.only = (summary(M.Model.3)$r.squared) - (Sr1$estimate^2)
  • So 0.16 = \(R_{Model.2_{Anxiety}}^2\), as expected

Residualized into Regression

  • Lets take our residualised effects: Practice (controlling for Anxiety) & Anxiety (controlling for Practice) and compare them to regression model where we enter the two variables into are regression
    • If our regression is semi-partialing we should get the same estimates (if we z-score everything cause residuals we are using are z-scored)
M.Model.4<-lm(Memory.Z~ Practice.control.Anxiety, data = CorrDataT)
M.Model.5<-lm(Memory.Z~ Anxiety.control.Practice, data = CorrDataT)

stargazer(M.Model.3,M.Model.4,M.Model.5,type="latex",
          intercept.bottom = FALSE, single.row=TRUE, 
          star.cutoffs=c(.05,.01,.001), notes.append = FALSE,
          header=FALSE)
  • The regression matches our results of when we by-hand residualized

Partial correlation

  • Partial correlation asks how much of the Y variance, which is not estimated by the other IVs, is estimated by this variable.

  • It removes the shared variance of the control variable (Say X2) from both Y and X1.

  • \(pr_1^2: = \frac{a}{a+e} = \frac{R_{Y.12}^2 - r_{Y2}^2}{1-r_{Y2}^2}\)

  • \(pr_2^2: \frac{b}{b+e} = \frac{R_{Y.12}^2 - r_{Y1}^2}{1-r_{Y1}^2}\)

Seeing control in action

Another way to understand it:

  • What if you want to know about Memory Errors and Practice Time while controlling for Anxiety on both Practice Time and Anxiety (cause Anxiety affect both Memory and Practice Time)
    • We take residuals of lm(Y~X2) and correlate it with the residuals of lm(X1~X2)
      • Remember the residuals are the leftover (after extracting what was explainable)
    • if you want to control for Practice Time you would: residuals of lm(Y~X1) with the residuals of lm(X2~X1)
# Control for Anxiety
CorrDataT$Memory.control.Anxiety<-residuals(lm(Memory~Anxiety, CorrDataT))
CorrDataT$Practice.control.Anxiety<-residuals(lm(Practice~Anxiety, CorrDataT))
scatterplot(Memory.control.Anxiety~Practice.control.Anxiety,CorrDataT, smoother=FALSE)

# Control for Practice Time
CorrDataT$Memory.control.Practice<-residuals(lm(Memory~Practice, CorrDataT))
CorrDataT$Anxiety.control.Practice<-residuals(lm(Anxiety~Practice, CorrDataT))
scatterplot(Memory.control.Practice~Anxiety.control.Practice,CorrDataT, smoother=FALSE)

Correlations based on residuals

library(apa)
Res.pr1<-cor_apa(cor.test(CorrDataT$Practice.control.Anxiety,CorrDataT$Memory.control.Anxiety,
                 method = c("pearson")),format ="latex",print = FALSE)
Res.pr2<-cor_apa(cor.test(CorrDataT$Anxiety.control.Practice,CorrDataT$Memory.control.Practice,
                 method = c("pearson")),format ="latex",print = FALSE)
  • The pearson correlation Memory (controling for Anxiety) and Practice (controling for Anxiety) was (98)=.55, <.001
  • The pearson correlation Memory (controling for Practice) and Anxiety (controling for Practice)was (98)=.29, =.004

Correlations based on R Functions

  • in R you can calculate the \(pr\) directly via the functions
pr1<-pcor.test(CorrDataT$Memory, CorrDataT$Practice, CorrDataT$Anxiety)
pr2<-pcor.test(CorrDataT$Memory, CorrDataT$Anxiety, CorrDataT$Practice)
  • The Partial correlation between Memory (controlling for Anxiety) and Practice (controlling for Anxiety) was \(pr\) =0.55
    • If we square it becomes \(pr^2\) = 0.3
  • The Partial correlation between Memory (controlling for Practice) and Anxiety (controlling for Practice) was \(pr\) = 0.29
    • If we square it becomes \(pr^2\) = 0.08
  • These values all match our hand residualized calculations

Partial Correlation in Regression?

  • What if control for Anxiety?
    • We lose the effect of Anxiety
Partial.Model.1<-lm(Memory.control.Anxiety~ Practice.control.Anxiety+Anxiety.Z, 
                    data = CorrDataT)

stargazer(Partial.Model.1,type="latex",
          intercept.bottom = FALSE, single.row=TRUE, 
          star.cutoffs=c(.05,.01,.001), notes.append = FALSE,
          header=FALSE)
  • What if control for Practice?
    • We lose the effect of Practice
    Partial.Model.2<-lm(Memory.control.Practice~ Anxiety.control.Practice+Practice.Z, 
                    data = CorrDataT)
    
    stargazer(Partial.Model.2,type="latex",
          intercept.bottom = FALSE, single.row=TRUE, 
          star.cutoffs=c(.05,.01,.001), notes.append = FALSE,
          header=FALSE)
LS0tDQp0aXRsZTogIlBhcnRpYWwgYW5kIFNlbWlwYXJ0aWFsIChwYXJ0KSBDb3JyZWxhdGlvbiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBmb250c2l6ZTogOHB0DQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQotLS0NCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojc2V0d2QoJ0M6L1dlYnNpdGUvV2Vic2l0ZSBEZWMnKQ0KYGBgDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIHNldHVwIGZvciBSbm90ZWJvb2tzDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpICNTaG93IGFsbCBzY3JpcHQgYnkgZGVmYXVsdA0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSkgI2hpZGUgbWVzc2FnZXMgDQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9ICBGQUxTRSkgI2hpZGUgcGFja2FnZSB3YXJuaW5ncyANCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9NC4yNSkgI1NldCBkZWZhdWx0IGZpZ3VyZSBzaXplcw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5oZWlnaHQ9NC4wKSAjU2V0IGRlZmF1bHQgZmlndXJlIHNpemVzDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmFsaWduPSdjZW50ZXInKSAjU2V0IGRlZmF1bHQgZmlndXJlDQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLnNob3cgPSAiaG9sZCIpICNTZXQgZGVmYXVsdCBmaWd1cmUNCmBgYA0KDQpccGFnZWJyZWFrDQoNCiMgQ29ycmVsYXRpb25zIHdpdGggMiBvciBtb3JlIHZhcmlhYmxlcw0KLSBDb3JyZWxhdGlvbnMgd2l0aCBtb3JlIHRoYW4gMiB2YXJpYWJsZXMgcHJlc2VudCBhIG5ldyBjaGFsbGVuZ2UgDQotIFdoYXQgaWYgYSB0aGlyZCB2YXJpYWJsZSAoWDIpIGFjdHVhbGx5IGV4cGxhaW5zIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBYMSBhbmQgWT8NCi0gV2UgbmVlZCB0byBmaW5kIGEgd2F5IHRvIGZpZ3VyZSBvdXQgaG93IFgyIG1pZ2h0IHJlbGF0ZSB0byBYMSBhbmQgWSEgDQoNCiMjIEV4YW1wbGUNCi0gUHJhY3RpY2UgVGltZSAoWDEpDQotIFBlcmZvcm1hbmNlIEFueGlldHkgKFgyKQ0KLSBNZW1vcnkgRXJyb3JzIChZKQ0KLSBRdWVzdGlvbiwgaG93IG11Y2ggZG9lcyBQcmFjdGljZSBUaW1lIGFuZCBQZXJmb3JtYW5jZSBBbnhpZXR5IHByZWRpY3QvZXhwbGFpbiBNZW1vcnkgRXJyb3JzIGluIHBlcmZvcm1hbmNlDQotIExldHMgc2ltdWxhdGUgb3VyIGRhdGFzZXQgd2l0aCB0aGUgbmV3IHZhcmlhYmxlcyBhbmQgZ2VuZXJhdGUgYSBzcGVjaWZpYyBjb3JyZWxhdGlvbiBtYXRyaXgNCg0KVmFyaWFibGUgICAgICAgICAgICAgICAgIHwgIFByYWN0aWNlIFRpbWUgKFgxKSB8IFBlcmZvcm1hbmNlIEFueGlldHkgKFgyKSB8IE1lbW9yeSBFcnJvcnMgKFkpIA0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLQ0KUHJhY3RpY2UgVGltZSAoWDEpICAgICAgIHwgICAgICAgICAgIDEgICAgICAgICB8ICAgICAgICAgIC4zICAgICAgICAgICAgICB8ICAgICAuNiAgICAgICAgDQpQZXJmb3JtYW5jZSBBbnhpZXR5IChYMikgfCAgICAgICAgICAgLjMgICAgICAgIHwgICAgICAgICAgMSAgICAgICAgICAgICAgIHwgICAgIC40ICAgICAgICANCk1lbW9yeSBFcnJvcnMgKFkpICAgICAgICB8ICAgICAgICAgICAuNiAgICAgICAgfCAgICAgICAgICAuNCAgICAgICAgICAgICAgfCAgICAgMSAgICAgICAgIA0KDQotIFNldCB0aGUgbWVhbiBudW1iZXIgZm9yIGVhY2ggdmFyaWFibGUgdG8gYmUgMTANCg0KYGBge3IsIG91dC53aWR0aD0nLjQ5XFxsaW5ld2lkdGgnLCBmaWcud2lkdGg9My4yNSwgZmlnLmhlaWdodD0zLjI1LGZpZy5zaG93PSdob2xkJyxmaWcuYWxpZ249J2NlbnRlcid9DQojcGFja2FnZXMgd2Ugd2lsbCBuZWVkIHRvIGNvbmR1Y3QgdG8gY3JlYXRlIGFuZCBncmFwaCBvdXIgZGF0YQ0KbGlicmFyeShNQVNTKSAjY3JlYXRlIGRhdGENCmxpYnJhcnkoY2FyKSAjZ3JhcGggZGF0YQ0KcHkxID0uNiAjQ29yIGJldHdlZW4gWDEgKFByYWN0aWNlIFRpbWUpIGFuZCBNZW1vcnkgRXJyb3JzDQpweTIgPS40ICNDb3IgYmV0d2VlbiBYMiAoUGVyZm9ybWFuY2UgQW54aWV0eSkgYW5kIE1lbW9yeSBFcnJvcnMNCnAxMj0gLjMgI0NvciBiZXR3ZWVuIFgxIChQcmFjdGljZSBUaW1lKSBhbmQgWDIgKFBlcmZvcm1hbmNlIEFueGlldHkpDQpNZWFucy5YMVgyWTwtIGMoMTAsMTAsMTApICNzZXQgdGhlIG1lYW5zIG9mIFggYW5kIFkgdmFyaWFibGVzDQpDb3ZNYXRyaXguWDFYMlkgPC0gbWF0cml4KGMoMSxwMTIscHkxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHAxMiwxLHB5MiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBweTEscHkyLDEpLDMsMykgIyBjcmVhdGVzIHRoZSBjb3ZhcmlhdGUgbWF0cml4IA0KI2J1aWxkIHRoZSBjb3JyZWxhdGVkIHZhcmlhYmxlcy4gTm90ZTogZW1waXJpY2FsPVRSVUUgbWVhbnMgbWFrZSB0aGUgY29ycmVsYXRpb24gRVhBQ1RMWSByLiANCiMgaWYgd2Ugc2F5IGVtcGlyaWNhbD1GQUxTRSwgdGhlIGNvcnJlbGF0aW9uIHdvdWxkIGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFyb3VuZCByDQpzZXQuc2VlZCg0MikNCkNvcnJEYXRhVDwtbXZybm9ybShuPTEwMCwgbXU9TWVhbnMuWDFYMlksU2lnbWE9Q292TWF0cml4LlgxWDJZLCBlbXBpcmljYWw9VFJVRSkNCiNDb252ZXJ0IHRoZW0gdG8gYSAiRGF0YS5GcmFtZSIgJiBhZGQgb3VyIGxhYmVscyB0byB0aGUgdmVjdG9ycyB3ZSBjcmVhdGVkDQpDb3JyRGF0YVQ8LWFzLmRhdGEuZnJhbWUoQ29yckRhdGFUKQ0KY29sbmFtZXMoQ29yckRhdGFUKSA8LSBjKCJQcmFjdGljZSIsIkFueGlldHkiLCJNZW1vcnkiKQ0KI21ha2UgdGhlIHNjYXR0ZXIgcGxvdHMNCnNjYXR0ZXJwbG90KE1lbW9yeX5QcmFjdGljZSxDb3JyRGF0YVQsIHNtb290aGVyPUZBTFNFKQ0Kc2NhdHRlcnBsb3QoTWVtb3J5fkFueGlldHksQ29yckRhdGFULCBzbW9vdGhlcj1GQUxTRSkNCnNjYXR0ZXJwbG90KEFueGlldHl+UHJhY3RpY2UsQ29yckRhdGFULCBzbW9vdGhlcj1GQUxTRSkNCiMgUGVhcnNvbiBDb3JyZWxhdGlvbnMNCnJ5MTwtY29yKENvcnJEYXRhVCRNZW1vcnksQ29yckRhdGFUJFByYWN0aWNlKQ0KcnkyPC1jb3IoQ29yckRhdGFUJE1lbW9yeSxDb3JyRGF0YVQkQW54aWV0eSkNCnIxMjwtY29yKENvcnJEYXRhVCRBbnhpZXR5LENvcnJEYXRhVCRQcmFjdGljZSkNCmBgYA0KDQojIyMgV2hhdCB0aGUgcHJvYmxlbT8NCi0gUHJhY3RpY2UgVGltZSBjYW4gZXhwbGFpbiBNZW1vcnkgRXJyb3JzLCAkcl4yID0kIGByIHJ5MV4yYA0KLSBBbnhpZXR5IGNhbiBleHBsYWluIE1lbW9yeSBFcnJvcnMsICRyXjIgPSQgYHIgcnkyXjJgDQotIEJ1dCBob3cgd2UgZG8ga25vdyB3aGV0aGVyIFByYWN0aWNlIFRpbWUgYW5kIEFueGlldHkgYXJlIGV4cGxhaW5pbmcgdGhlIHNhbWUgdmFyaWFuY2U/IEFueGlldHkgYW5kIFByYWN0aWNlIFRpbWUgZXhwbGFpbiBlYWNoIG90aGVyIGEgbGl0dGxlLCAkcl4yID0kIGByIHIxMl4yYA0KDQojIyMgTXVsdGlwbGUgUiANCi0gV2UgdXNlIHRoZSBjYXBpdGFsIGxldHRlciwgJFIkLCBub3cgY2F1c2Ugd2UgaGF2ZSBtdWx0aXBsZSB2YXJpYWJsZXMNCi0gJFJfe1kuMTJ9ID0gXHNxcnR7XGZyYWN7cl97WTF9XjIgKyByX3tZMn1eMiAtIDJyX3tZMX0gcl97WTJ9IHJfezEyfX0gezEgLSByX3sxMn1eMn19JA0KLSAkUl97WS4xMn0gPSQgYHIgc3FydCgocnkxXjIrcnkyXjIgLSAyKnJ5MSpyeTIqcjEyKS8oMS1yMTJeMikpYA0KLSBpZiB3ZSBzcXVhcmUgdGhhdCB2YWx1ZSwgYHIgc3FydCgocnkxXjIrcnkyXjIgLSAyKnJ5MSpyeTEqcjEyKS8oMS1yMTJeMikpXjJgLCB3ZSBnZXQgdGhlIE11bHRpcGxlICRSXjIkIA0KICAgIC0gaS5lLiwgdGhlIHRvdGFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSB0aGVzZSB2YXJpYWJsZXMgb24gTWVtb3J5IA0KDQpccGFnZWJyZWFrDQoNCiMgU2VtaXBhcnRpYWwgKHBhcnQpIGNvcnJlbGF0aW9uDQotIFdlIG5lZWQgdG8gZGVmaW5lIHRvIGNvbnRyaWJ1dGlvbiBvZiBlYWNoIFggdmFyaWFibGUgb24gWQ0KLSBTZW1pcGFydGlhbCAoYWxzbyBjYWxsZWQgcGFydCkgaXMgb25lIG9mIHR3byBtZXRob2RzOyB0aGUgb3RoZXIgaXMgY2FsbGVkIHBhcnRpYWwNCiAgICAtIGlzIGNhbGxlZCBzZW1pLCBjYXVzZSBpdCByZW1vdmVzIHRoZSBlZmZlY3Qgb2Ygb25lIElWIHJlbGF0aXZlIHRvIHRoZSBvdGhlciB3aXRob3V0IHJlbW92aW5nIHRoZSByZWxhdGlvbnNoaXAgdG8gWQ0KLSAgKipTZW1pcGFydGlhbCBjb3JyZWxhdGlvbnMgaW5kaWNhdGUgdGhlICJ1bmlxdWUiIGNvbnRyaWJ1dGlvbiBvZiBhbiBpbmRlcGVuZGVudCB2YXJpYWJsZSBvbiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlKiouICANCiAgICAtIFdoZW4gd2UgZ2V0IHRvIGJhY2sgdG8gcmVncmVzc2lvbiwgIldoYXQgaXMgdGhlIGNvbnRyaWJ1dGlvbiBvZiB0aGlzIFggYWJvdmUgYW5kIGJleW9uZCB0aGUgb3RoZXIgWCB2YXJpYWJsZT8iIA0KDQo8Y2VudGVyPg0KIVtCYWxsYW50aW5lIGZvciBYMSwgWDIsIGFuZCBZXShSZWdyZXNzaW9uQ2xhc3MvQmFsbGFudGluZS5qcGcpeyB3aWR0aD0yMCUgfQ0KPC9jZW50ZXI+DQoNCg0KLSAkUl97WS4xMn1eMiA9IGEgKyBiICsgYyQNCi0gJHNyXzFeMjogYSA9IFJfe1kuMTJ9XjIgLSByX3tZMn1eMiQNCi0gJHNyXzJeMjpiID0gUl97WS4xMn1eMiAtIHJfe1kxfV4yJA0KDQojIyBDYWxjdWF0aW9uDQotIENhbGN1bGF0ZSB1bmlxdWUgdmFyaWFuY2UNCg0KYGBge3J9DQpSMnkuMTI8LXNxcnQoKHJ5MV4yK3J5Ml4yIC0gKDIqcnkxKnJ5MipyMTIpKS8oMS1yMTJeMikpXjINCmEgPSBSMnkuMTIgLXJ5Ml4yIA0KYiA9IFIyeS4xMiAtcnkxXjIgDQpgYGANCg0KLSBJbiBvdGhlciB3b3JkcywNCiAgICAtIEluIHRvdGFsIHdlIGV4cGxhaW5lZCwgYHIgUjJ5LjEyYCBvZiB0aGUgTWVtb3J5IEVycm9ycyANCiAgICAtIFByYWN0aWNlIFRpbWUgdW5pcXVlbHkgZXhwbGFpbmVkLCBgciBhYCBvZiBNZW1vcnkgRXJyb3JzDQogICAgLSBBbnhpZXR5IHVuaXF1ZWx5IGV4cGxhaW5lZCwgYHIgYmAgb2YgTWVtb3J5IEVycm9ycyANCiAgICAtIFdlIHNob3VsZCBub3Qgc29sdmUgZm9yICpjKiBjYXVzZSBpdCBjYW4gYmUgbmVnYXRpdmUgKGluIHNvbWUgd2VpcmQgY2FzZXMpDQoNCiMjIFNlZWluZyBjb250cm9sIGluIGFjdGlvbiBpbiByZWdyZXNzaW9uDQotIEFub3RoZXIgd2F5IHRvIHVuZGVyc3RhbmQgaXQ6IA0KICAgIC0gV2hhdCBpZiB5b3Ugd2FudCB0byBrbm93IGFib3V0IE1lbW9yeSBFcnJvcnMgYW5kIGhvdyBQcmFjdGljZSBUaW1lIHVuaXF1ZWx5IGV4cGxhaW5zIGl0PyANCiAgICAgICAgLSAqQ29udHJvbGxpbmcgdGhlIGVmZmVjdCBvZiBBbnhpZXR5IG9uIFByYWN0aWNlIFRpbWUqDQoNCiMjIyBNZW1vcnkgKFkpIH4gUHJhY3RpY2UoWDEpIFtDb250cm9sIEFueGlldHkgKFgyKV0NCjEuIFdlIGNhbiByZW1vdmUgdGhlIGVmZmVjdCBvZiBBbnhpZXR5IG9uIFByYWN0aWNlIFRpbWUgYnkgZXh0cmFjdGluZyB0aGUgcmVzaWR1YWxzIGZyb20gKmxtKFgxflgyKSoNCjIuIFJlbWVtYmVyIHRoZSByZXNpZHVhbHMgYXJlIHRoZSBsZWZ0b3ZlciAoYWZ0ZXIgZXh0cmFjdGluZyB3aGF0IHdhcyBleHBsYWluYWJsZSkNCg0KYGBge3IsIG91dC53aWR0aD0nLjQ5XFxsaW5ld2lkdGgnLCBmaWcud2lkdGg9My4yNSwgZmlnLmhlaWdodD0zLjI1LGZpZy5zaG93PSdob2xkJyxmaWcuYWxpZ249J2NlbnRlcid9DQpzY2F0dGVycGxvdChQcmFjdGljZX5BbnhpZXR5LENvcnJEYXRhVCwgc21vb3RoZXI9RkFMU0UpDQpDb3JyRGF0YVQkUHJhY3RpY2UuY29udHJvbC5BbnhpZXR5PC1yZXNpZHVhbHMobG0oUHJhY3RpY2V+QW54aWV0eSwgQ29yckRhdGFUKSkNCnNjYXR0ZXJwbG90KFByYWN0aWNlLmNvbnRyb2wuQW54aWV0eX5BbnhpZXR5LENvcnJEYXRhVCwgc21vb3RoZXI9RkFMU0UpDQpgYGANCiAgIA0KMy4gTmV4dCB3ZSBjYW4gY29ycmVsYXRlIE1lbW9yeSBFcnJvcnMgd2l0aCB0aGUgcmVzaWR1YWxpemVkIFByYWN0aWNlIFRpbWUsICpjb3IoTWVtb3J5LFByYWN0aWNlW2NvbnRyb2wgQW54aWV0eV0pKiANCg0KYGBge3J9DQpTcjEuYWx0PC1jb3IoQ29yckRhdGFUJE1lbW9yeSxDb3JyRGF0YVQkUHJhY3RpY2UuY29udHJvbC5BbnhpZXR5KQ0KYGBgDQoNCklmIHdlIHNxdWFyZSB0aGUgY29ycmVsYXRpb24gdmFsdWUgd2UgZ290IGByIFNyMS5hbHRgLCBpdCBiZWNvbWVzIGByIFNyMS5hbHReMmAgd2hpY2ggbWF0Y2hlcyBvdXIgJGEkIGZyb20gdGhlIGFuYWx5c2lzIGFib3ZlLiANCg0KIyMjIE1lbW9yeSAoWSkgfiBBbnhpZXR5KFgyKSBbQ29udHJvbCBQcmFjdGljZSAoWDEpXQ0KDQoxLiBXZSBjYW4gcmVtb3ZlIGVmZmVjdCBvZiBQcmFjdGljZSBvbiBBbnhpZXR5IGJ5IGV4dHJhY3RpbmcgdGhlIHJlc2lkdWFscyBmcm9tICpsbShYMn5YMSkqDQoyLiBSZW1lbWJlciB0aGUgcmVzaWR1YWxzIGFyZSB0aGUgbGVmdG92ZXIgKGFmdGVyIGV4dHJhY3Rpbmcgd2hhdCB3YXMgZXhwbGFpbmFibGUpDQoNCmBgYHtyLCBvdXQud2lkdGg9Jy40OVxcbGluZXdpZHRoJywgZmlnLndpZHRoPTMuMjUsIGZpZy5oZWlnaHQ9My4yNSxmaWcuc2hvdz0naG9sZCcsZmlnLmFsaWduPSdjZW50ZXInfQ0Kc2NhdHRlcnBsb3QoQW54aWV0eX5QcmFjdGljZSxDb3JyRGF0YVQsIHNtb290aGVyPUZBTFNFKQ0KQ29yckRhdGFUJEFueGlldHkuY29udHJvbC5QcmFjdGljZTwtcmVzaWR1YWxzKGxtKEFueGlldHl+UHJhY3RpY2UsIENvcnJEYXRhVCkpDQpzY2F0dGVycGxvdChBbnhpZXR5LmNvbnRyb2wuUHJhY3RpY2V+UHJhY3RpY2UsQ29yckRhdGFULCBzbW9vdGhlcj1GQUxTRSkNCmBgYA0KICAgDQozLiBOZXh0IHdlIGNhbiBjb3JyZWxhdGUgTWVtb3J5IEVycm9ycyB3aXRoIHRoZSByZXNpZHVhbGl6ZWQgQW54aWV0eSwgKmNvcihNZW1vcnksQW54aWV0eVtjb250cm9sIFByYWN0aWNlXSkqIA0KDQpgYGB7cn0NClNyMi5hbHQ8LWNvcihDb3JyRGF0YVQkTWVtb3J5LENvcnJEYXRhVCRBbnhpZXR5LmNvbnRyb2wuUHJhY3RpY2UpDQpgYGANCg0KSWYgd2Ugc3F1YXJlIHRoZSBjb3JyZWxhdGlvbiB2YWx1ZSB3ZSBnb3QgYHIgU3IyLmFsdGAsIGl0IGJlY29tZXMgYHIgU3IyLmFsdF4yYCB3aGljaCBtYXRjaGVzIG91ciAkYiQgZnJvbSB0aGUgYW5hbHlzaXMgYWJvdmUuIA0KDQotICoqSW4gcmVncmVzc2lvbiB3aGVuIHdlIGhhdmUgbW9yZSB0aGFuIG9uZSBwcmVkaWN0b3JzIHRoZXkgYXJlIGNvbnRyb2xsaW5nIGZvciBlYWNoIG90aGVyISoqDQoNCiMjIFNlbWlwYXJ0aWFsIG5vdGVzOiANCi0gSXQgY2FuIGJlIHdyaXR0ZW4gYXMgJHNyJCBvciBtb3JlIHNwZWNpZmljYWxseSwgJHNyXzEkIGZvciBYMSAod2l0aCBYMiByZW1vdmVkKSBhbmQgJHNyXzIkICh3aXRoIFgxIHJlbW92ZWQpIA0KLSBjb3JyZWxhdGlvbnMgd2l0aCBubyBjb250cm9sIHZhcmlhYmxlcyBhcmUgY2FsbGVkIHRoZSAqemVyby1vcmRlciogY29ycmVsYXRpb25zDQotIGluIFIgeW91IGNhbiBjYWxjdWxhdGUgdGhlICRzciQgcmF0aGVyIHF1aWNrbHkgdXNpbmcgdGhlICpwcGNvciogbGlicmFyeQ0KLSBUaGUgbGFzdCB2YXJpYWJsZSBpbiB0aGUgbGlzdCBpcyB0aGUgY29udHJvbA0KDQpgYGB7cn0NCmxpYnJhcnkocHBjb3IpDQpTcjE8LXNwY29yLnRlc3QoQ29yckRhdGFUJE1lbW9yeSwgQ29yckRhdGFUJFByYWN0aWNlLCBDb3JyRGF0YVQkQW54aWV0eSkNClNyMjwtc3Bjb3IudGVzdChDb3JyRGF0YVQkTWVtb3J5LCBDb3JyRGF0YVQkQW54aWV0eSwgQ29yckRhdGFUJFByYWN0aWNlKQ0KIyBFeHRyYWN0IHJlc3VsdCBjYWxsIGZvciBTcjEkZXN0aW1hdGUNCmBgYA0KDQotIFRoZSAqU2VtaS1wYXJ0aWFsKiBjb3JyZWxhdGlvbiBiZXR3ZWVuIE1lbW9yeSBhbmQgUHJhY3RpY2UgKGJ1dCBjb250cm9sbGluZyBmb3IgQW54aWV0eSkgd2FzICRzciQgPWByIHJvdW5kKFNyMSRlc3RpbWF0ZSwzKWANCiAgICAtIElmIHdlIHNxdWFyZSBpdCBiZWNvbWVzICRzcl4yJCA9IGByIHJvdW5kKFNyMSRlc3RpbWF0ZV4yLDMpYCB3aGljaCBtYXRjaGVzIG91ciAkYSQgZnJvbSB0aGUgYW5hbHlzaXMgYWJvdmUuIA0KDQotIFRoZSAqU2VtaS1wYXJ0aWFsKiBjb3JyZWxhdGlvbiBiZXR3ZWVuIE1lbW9yeSBhbmQgQW54aWV0eSAoYnV0IGNvbnRyb2xsaW5nIGZvciBQcmFjdGljZSkgd2FzICRzciQgPSBgciByb3VuZChTcjIkZXN0aW1hdGUsMylgDQogICAgLSBJZiB3ZSBzcXVhcmUgaXQgYmVjb21lcyAkc3JeMiQgPSAgYHIgcm91bmQoU3IyJGVzdGltYXRlXjIsMylgIHdoaWNoIG1hdGNoZXMgb3VyICRiJCBmcm9tIHRoZSBhbmFseXNpcyBhYm92ZS4gDQoNCiMjIFJlbGF0aW9uc2hpcCB0byBSZWdyZXNzaW9uIA0KLSBMZXRzIHNheSB3ZSB3YW50IHRvIHJlcG9ydCBob3cgcHJhY3RpY2UgaXMgcmVsYXRlZCB0byBtZW1vcnkgaW4gcGVyZm9ybWFuY2UsIGJ1dCB3ZSB3YW50IHRvIGNvbnRyb2wgZm9yIGFueGlldHk/DQotIFRoZSAkc3JeMiQgZm9yIGEgdmFyaWFibGUgdGVsbHMgdXMgaG93IG11Y2ggJFJeMiQgd2lsbCBkZWNyZWFzZSBpZiB0aGF0IHZhcmlhYmxlIGlzIHJlbW92ZWQgZnJvbSB0aGUgcmVncmVzc2lvbiBlcXVhdGlvbg0KICAgIC0gICoqTGV0cyB0ZXN0IGl0KioNCiAgICAtIExldHMgcnVuIDMgcmVncmVzc2lvbiBtb2RlbHMgKEkgenNjb3JlZCBldmVyeXRoaW5nIHRvIG1ha2UgdGhlIHNjYWxlcyBhbGwgdGhlIHNhbWUpDQogICAgICAgIC0gbG0oTWVtb3J5IH4gUHJhY3RpY2UpIA0KICAgICAgICAtIGxtKE1lbW9yeSB+IEFueGlldHkpICANCiAgICAgICAgLSBsbShNZW1vcnkgfiBQcmFjdGljZSArIEFueGlldHkpDQogICAgICAgIA0KYGBge3J9DQojIENlbnRlciB2YXJpYWJsZXMgKGlmIHlvdSBzYWlkIHNjYWxlID0gVFJVRSBpdCB3b3VsZCB6c2NvcmUgdGhlIHByZWRpY3RvcnMpDQpDb3JyRGF0YVQkTWVtb3J5Llo8LXNjYWxlKENvcnJEYXRhVCRNZW1vcnksIHNjYWxlPVRSVUUsIGNlbnRlcj1UUlVFKVssXQ0KQ29yckRhdGFUJFByYWN0aWNlLlo8LXNjYWxlKENvcnJEYXRhVCRQcmFjdGljZSwgc2NhbGU9VFJVRSwgY2VudGVyPVRSVUUpWyxdDQpDb3JyRGF0YVQkQW54aWV0eS5aPC1zY2FsZShDb3JyRGF0YVQkQW54aWV0eSwgc2NhbGU9VFJVRSwgY2VudGVyPVRSVUUpWyxdDQojIyMjIyMjIyMjIyMjIyNNb2RlbCAxDQpNLk1vZGVsLjE8LWxtKE1lbW9yeS5afiBQcmFjdGljZS5aLCBkYXRhID0gQ29yckRhdGFUKQ0KTS5Nb2RlbC4yPC1sbShNZW1vcnkuWn4gQW54aWV0eS5aLCBkYXRhID0gQ29yckRhdGFUKQ0KTS5Nb2RlbC4zPC1sbShNZW1vcnkuWn4gUHJhY3RpY2UuWitBbnhpZXR5LlosIGRhdGEgPSBDb3JyRGF0YVQpDQpgYGANCg0KYGBge3IsICxyZXN1bHRzPSdhc2lzJ30NCmxpYnJhcnkoc3RhcmdhemVyKQ0Kc3RhcmdhemVyKE0uTW9kZWwuMSxNLk1vZGVsLjIsTS5Nb2RlbC4zLHR5cGU9ImxhdGV4IiwNCiAgICAgICAgICBpbnRlcmNlcHQuYm90dG9tID0gRkFMU0UsIHNpbmdsZS5yb3c9VFJVRSwgDQogICAgICAgICAgc3Rhci5jdXRvZmZzPWMoLjA1LC4wMSwuMDAxKSwgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgaGVhZGVyPUZBTFNFKQ0KYGBgDQoNClxwYWdlYnJlYWsNCg0KVGh1cywgJCRSX3ttb2RlbC4zX3tQK0F9fV4yIC0gc3Jfe0FueGlldHl9XjIgID0gIFJeMl97UHJhY3RpY2Uub25seX0kJA0KDQotIEluIFIgY29kZTogDQpgYGB7cn0NClIyLlByYWN0aWNlLm9ubHkgPSAoc3VtbWFyeShNLk1vZGVsLjMpJHIuc3F1YXJlZCkgLSAoU3IyJGVzdGltYXRlXjIpDQpgYGANCg0KLSBTbyBgciBSMi5QcmFjdGljZS5vbmx5YCA9ICRSX3tNb2RlbC4xX3tQcmFjdGljZX19XjIkLCBhcyBleHBlY3RlZCANCg0KVGh1cywgJCRSX3ttb2RlbC4zLlArQX1eMiAtIHNyX3tQcmFjdGljZX1eMiAgPSAgUl4yX3tBbnhpZXR5Lm9ubHl9JCQNCg0KLSBJbiBSIGNvZGU6IA0KYGBge3J9DQpSMi5BbnhpZXR5Lm9ubHkgPSAoc3VtbWFyeShNLk1vZGVsLjMpJHIuc3F1YXJlZCkgLSAoU3IxJGVzdGltYXRlXjIpDQpgYGANCi0gU28gYHIgUjIuQW54aWV0eS5vbmx5YCA9ICRSX3tNb2RlbC4yX3tBbnhpZXR5fX1eMiQsIGFzIGV4cGVjdGVkIA0KDQojIyMgUmVzaWR1YWxpemVkIGludG8gUmVncmVzc2lvbg0KLSBMZXRzIHRha2Ugb3VyIHJlc2lkdWFsaXNlZCBlZmZlY3RzOiBQcmFjdGljZSAoY29udHJvbGxpbmcgZm9yIEFueGlldHkpICYgIEFueGlldHkgKGNvbnRyb2xsaW5nIGZvciBQcmFjdGljZSkgYW5kIGNvbXBhcmUgdGhlbSB0byByZWdyZXNzaW9uIG1vZGVsIHdoZXJlIHdlIGVudGVyIHRoZSB0d28gdmFyaWFibGVzIGludG8gYXJlIHJlZ3Jlc3Npb24NCiAgICAtIElmIG91ciByZWdyZXNzaW9uIGlzIHNlbWktcGFydGlhbGluZyB3ZSBzaG91bGQgZ2V0IHRoZSBzYW1lIGVzdGltYXRlcyAoaWYgd2Ugei1zY29yZSBldmVyeXRoaW5nIGNhdXNlIHJlc2lkdWFscyB3ZSBhcmUgdXNpbmcgYXJlIHotc2NvcmVkKQ0KDQpgYGB7ciwgLHJlc3VsdHM9J2FzaXMnfQ0KTS5Nb2RlbC40PC1sbShNZW1vcnkuWn4gUHJhY3RpY2UuY29udHJvbC5BbnhpZXR5LCBkYXRhID0gQ29yckRhdGFUKQ0KTS5Nb2RlbC41PC1sbShNZW1vcnkuWn4gQW54aWV0eS5jb250cm9sLlByYWN0aWNlLCBkYXRhID0gQ29yckRhdGFUKQ0KDQpzdGFyZ2F6ZXIoTS5Nb2RlbC4zLE0uTW9kZWwuNCxNLk1vZGVsLjUsdHlwZT0ibGF0ZXgiLA0KICAgICAgICAgIGludGVyY2VwdC5ib3R0b20gPSBGQUxTRSwgc2luZ2xlLnJvdz1UUlVFLCANCiAgICAgICAgICBzdGFyLmN1dG9mZnM9YyguMDUsLjAxLC4wMDEpLCBub3Rlcy5hcHBlbmQgPSBGQUxTRSwNCiAgICAgICAgICBoZWFkZXI9RkFMU0UpDQpgYGANCg0KLSBUaGUgcmVncmVzc2lvbiBtYXRjaGVzIG91ciByZXN1bHRzIG9mIHdoZW4gd2UgYnktaGFuZCByZXNpZHVhbGl6ZWQNCg0KIyBQYXJ0aWFsIGNvcnJlbGF0aW9uDQotIFBhcnRpYWwgY29ycmVsYXRpb24gYXNrcyBob3cgbXVjaCBvZiB0aGUgWSB2YXJpYW5jZSwgd2hpY2ggaXMgbm90IGVzdGltYXRlZCBieSB0aGUgb3RoZXIgSVZzLCBpcyBlc3RpbWF0ZWQgYnkgdGhpcyB2YXJpYWJsZS4NCi0gSXQgcmVtb3ZlcyB0aGUgc2hhcmVkIHZhcmlhbmNlIG9mIHRoZSBjb250cm9sIHZhcmlhYmxlIChTYXkgWDIpIGZyb20gYm90aCBZIGFuZCBYMS4gDQoNCi0gJHByXzFeMjogPSBcZnJhY3thfXthK2V9ID0gXGZyYWN7Ul97WS4xMn1eMiAtIHJfe1kyfV4yfXsxLXJfe1kyfV4yfSQNCi0gJHByXzJeMjogXGZyYWN7Yn17YitlfSA9IFxmcmFje1Jfe1kuMTJ9XjIgLSByX3tZMX1eMn17MS1yX3tZMX1eMn0kDQoNCiMjIFNlZWluZyBjb250cm9sIGluIGFjdGlvbg0KQW5vdGhlciB3YXkgdG8gdW5kZXJzdGFuZCBpdDogDQoNCi0gV2hhdCBpZiB5b3Ugd2FudCB0byBrbm93IGFib3V0IE1lbW9yeSBFcnJvcnMgYW5kIFByYWN0aWNlIFRpbWUgd2hpbGUgY29udHJvbGxpbmcgZm9yIEFueGlldHkgb24gYm90aCBQcmFjdGljZSBUaW1lIGFuZCBBbnhpZXR5IChjYXVzZSBBbnhpZXR5IGFmZmVjdCBib3RoIE1lbW9yeSBhbmQgUHJhY3RpY2UgVGltZSkNCiAgICAtIFdlIHRha2UgcmVzaWR1YWxzIG9mIGxtKFl+WDIpIGFuZCBjb3JyZWxhdGUgaXQgd2l0aCB0aGUgcmVzaWR1YWxzIG9mIGxtKFgxflgyKQ0KICAgICAgICAtIFJlbWVtYmVyIHRoZSByZXNpZHVhbHMgYXJlIHRoZSBsZWZ0b3ZlciAoYWZ0ZXIgZXh0cmFjdGluZyB3aGF0IHdhcyBleHBsYWluYWJsZSkNCiAgICAtIGlmIHlvdSB3YW50IHRvIGNvbnRyb2wgZm9yIFByYWN0aWNlIFRpbWUgeW91IHdvdWxkOiByZXNpZHVhbHMgb2YgbG0oWX5YMSkgd2l0aCB0aGUgcmVzaWR1YWxzIG9mIGxtKFgyflgxKQ0KDQpgYGB7ciwgb3V0LndpZHRoPScuNDlcXGxpbmV3aWR0aCcsIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTMsZmlnLnNob3c9J2hvbGQnLGZpZy5hbGlnbj0nY2VudGVyJ30NCiMgQ29udHJvbCBmb3IgQW54aWV0eQ0KQ29yckRhdGFUJE1lbW9yeS5jb250cm9sLkFueGlldHk8LXJlc2lkdWFscyhsbShNZW1vcnl+QW54aWV0eSwgQ29yckRhdGFUKSkNCkNvcnJEYXRhVCRQcmFjdGljZS5jb250cm9sLkFueGlldHk8LXJlc2lkdWFscyhsbShQcmFjdGljZX5BbnhpZXR5LCBDb3JyRGF0YVQpKQ0Kc2NhdHRlcnBsb3QoTWVtb3J5LmNvbnRyb2wuQW54aWV0eX5QcmFjdGljZS5jb250cm9sLkFueGlldHksQ29yckRhdGFULCBzbW9vdGhlcj1GQUxTRSkNCg0KIyBDb250cm9sIGZvciBQcmFjdGljZSBUaW1lDQpDb3JyRGF0YVQkTWVtb3J5LmNvbnRyb2wuUHJhY3RpY2U8LXJlc2lkdWFscyhsbShNZW1vcnl+UHJhY3RpY2UsIENvcnJEYXRhVCkpDQpDb3JyRGF0YVQkQW54aWV0eS5jb250cm9sLlByYWN0aWNlPC1yZXNpZHVhbHMobG0oQW54aWV0eX5QcmFjdGljZSwgQ29yckRhdGFUKSkNCnNjYXR0ZXJwbG90KE1lbW9yeS5jb250cm9sLlByYWN0aWNlfkFueGlldHkuY29udHJvbC5QcmFjdGljZSxDb3JyRGF0YVQsIHNtb290aGVyPUZBTFNFKQ0KYGBgDQoNCiMjIyBDb3JyZWxhdGlvbnMgYmFzZWQgb24gcmVzaWR1YWxzDQpgYGB7cn0NCmxpYnJhcnkoYXBhKQ0KUmVzLnByMTwtY29yX2FwYShjb3IudGVzdChDb3JyRGF0YVQkUHJhY3RpY2UuY29udHJvbC5BbnhpZXR5LENvcnJEYXRhVCRNZW1vcnkuY29udHJvbC5BbnhpZXR5LA0KICAgICAgICAgICAgICAgICBtZXRob2QgPSBjKCJwZWFyc29uIikpLGZvcm1hdCA9ImxhdGV4IixwcmludCA9IEZBTFNFKQ0KUmVzLnByMjwtY29yX2FwYShjb3IudGVzdChDb3JyRGF0YVQkQW54aWV0eS5jb250cm9sLlByYWN0aWNlLENvcnJEYXRhVCRNZW1vcnkuY29udHJvbC5QcmFjdGljZSwNCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gYygicGVhcnNvbiIpKSxmb3JtYXQgPSJsYXRleCIscHJpbnQgPSBGQUxTRSkNCmBgYA0KDQotIFRoZSBwZWFyc29uIGNvcnJlbGF0aW9uIE1lbW9yeSAoY29udHJvbGluZyBmb3IgQW54aWV0eSkgYW5kIFByYWN0aWNlIChjb250cm9saW5nIGZvciBBbnhpZXR5KSB3YXMgYHIgUmVzLnByMWANCi0gVGhlIHBlYXJzb24gY29ycmVsYXRpb24gTWVtb3J5IChjb250cm9saW5nIGZvciBQcmFjdGljZSkgYW5kIEFueGlldHkgKGNvbnRyb2xpbmcgZm9yIFByYWN0aWNlKXdhcyBgciBSZXMucHIyYA0KDQojIyMgQ29ycmVsYXRpb25zIGJhc2VkIG9uIFIgRnVuY3Rpb25zDQotIGluIFIgeW91IGNhbiBjYWxjdWxhdGUgdGhlICRwciQgZGlyZWN0bHkgdmlhIHRoZSBmdW5jdGlvbnMNCg0KYGBge3J9DQpwcjE8LXBjb3IudGVzdChDb3JyRGF0YVQkTWVtb3J5LCBDb3JyRGF0YVQkUHJhY3RpY2UsIENvcnJEYXRhVCRBbnhpZXR5KQ0KcHIyPC1wY29yLnRlc3QoQ29yckRhdGFUJE1lbW9yeSwgQ29yckRhdGFUJEFueGlldHksIENvcnJEYXRhVCRQcmFjdGljZSkNCmBgYA0KDQotIFRoZSAqUGFydGlhbCogY29ycmVsYXRpb24gYmV0d2VlbiBNZW1vcnkgKGNvbnRyb2xsaW5nIGZvciBBbnhpZXR5KSBhbmQgUHJhY3RpY2UgKGNvbnRyb2xsaW5nIGZvciBBbnhpZXR5KSB3YXMgJHByJCA9YHIgcm91bmQocHIxJGVzdGltYXRlLDIpYA0KICAgIC0gSWYgd2Ugc3F1YXJlIGl0IGJlY29tZXMgJHByXjIkID0gYHIgcm91bmQocHIxJGVzdGltYXRlXjIsMilgDQoNCi0gVGhlICpQYXJ0aWFsKiBjb3JyZWxhdGlvbiBiZXR3ZWVuIE1lbW9yeSAoY29udHJvbGxpbmcgZm9yIFByYWN0aWNlKSBhbmQgQW54aWV0eSAoY29udHJvbGxpbmcgZm9yIFByYWN0aWNlKSB3YXMgJHByJCA9IGByIHJvdW5kKHByMiRlc3RpbWF0ZSwyKWANCiAgICAtIElmIHdlIHNxdWFyZSBpdCBiZWNvbWVzICRwcl4yJCA9ICBgciByb3VuZChwcjIkZXN0aW1hdGVeMiwyKWANCi0gKipUaGVzZSB2YWx1ZXMgYWxsIG1hdGNoIG91ciBoYW5kIHJlc2lkdWFsaXplZCBjYWxjdWxhdGlvbnMqKg0KDQojIyBQYXJ0aWFsIENvcnJlbGF0aW9uIGluIFJlZ3Jlc3Npb24/DQotIFdoYXQgaWYgY29udHJvbCBmb3IgQW54aWV0eT8gDQogICAgLSBXZSBsb3NlIHRoZSBlZmZlY3Qgb2YgQW54aWV0eQ0KDQpgYGB7ciwgLHJlc3VsdHM9J2FzaXMnfQ0KUGFydGlhbC5Nb2RlbC4xPC1sbShNZW1vcnkuY29udHJvbC5BbnhpZXR5fiBQcmFjdGljZS5jb250cm9sLkFueGlldHkrQW54aWV0eS5aLCANCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IENvcnJEYXRhVCkNCg0Kc3RhcmdhemVyKFBhcnRpYWwuTW9kZWwuMSx0eXBlPSJsYXRleCIsDQogICAgICAgICAgaW50ZXJjZXB0LmJvdHRvbSA9IEZBTFNFLCBzaW5nbGUucm93PVRSVUUsIA0KICAgICAgICAgIHN0YXIuY3V0b2Zmcz1jKC4wNSwuMDEsLjAwMSksIG5vdGVzLmFwcGVuZCA9IEZBTFNFLA0KICAgICAgICAgIGhlYWRlcj1GQUxTRSkNCmBgYA0KDQpccGFnZWJyZWFrDQoNCi0gV2hhdCBpZiBjb250cm9sIGZvciBQcmFjdGljZT8gDQogICAgLSBXZSBsb3NlIHRoZSBlZmZlY3Qgb2YgUHJhY3RpY2UNCiAgICANCiAgICBgYGB7ciwgLHJlc3VsdHM9J2FzaXMnfQ0KUGFydGlhbC5Nb2RlbC4yPC1sbShNZW1vcnkuY29udHJvbC5QcmFjdGljZX4gQW54aWV0eS5jb250cm9sLlByYWN0aWNlK1ByYWN0aWNlLlosIA0KICAgICAgICAgICAgICAgICAgICBkYXRhID0gQ29yckRhdGFUKQ0KDQpzdGFyZ2F6ZXIoUGFydGlhbC5Nb2RlbC4yLHR5cGU9ImxhdGV4IiwNCiAgICAgICAgICBpbnRlcmNlcHQuYm90dG9tID0gRkFMU0UsIHNpbmdsZS5yb3c9VFJVRSwgDQogICAgICAgICAgc3Rhci5jdXRvZmZzPWMoLjA1LC4wMSwuMDAxKSwgbm90ZXMuYXBwZW5kID0gRkFMU0UsDQogICAgICAgICAgaGVhZGVyPUZBTFNFKQ0KYGBgDQoNCjxzY3JpcHQ+DQogIChmdW5jdGlvbihpLHMsbyxnLHIsYSxtKXtpWydHb29nbGVBbmFseXRpY3NPYmplY3QnXT1yO2lbcl09aVtyXXx8ZnVuY3Rpb24oKXsNCiAgKGlbcl0ucT1pW3JdLnF8fFtdKS5wdXNoKGFyZ3VtZW50cyl9LGlbcl0ubD0xKm5ldyBEYXRlKCk7YT1zLmNyZWF0ZUVsZW1lbnQobyksDQogIG09cy5nZXRFbGVtZW50c0J5VGFnTmFtZShvKVswXTthLmFzeW5jPTE7YS5zcmM9ZzttLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGEsbSkNCiAgfSkod2luZG93LGRvY3VtZW50LCdzY3JpcHQnLCdodHRwczovL3d3dy5nb29nbGUtYW5hbHl0aWNzLmNvbS9hbmFseXRpY3MuanMnLCdnYScpOw0KDQogIGdhKCdjcmVhdGUnLCAnVUEtOTA0MTUxNjAtMScsICdhdXRvJyk7DQogIGdhKCdzZW5kJywgJ3BhZ2V2aWV3Jyk7DQoNCjwvc2NyaXB0Pg0K