To see a summary of the regression assumptions and more ways run test diagnostics, see our previous lab

pkgs <- c("tidyverse", 
          "dplyr", 
          "haven", 
          "foreign", 
          "lme4", 
          "nlme", 
          "lsr", 
          "emmeans", 
          "afex", 
          "knitr", 
          "kableExtra", 
          "car",
          "mediation",
          "rockchalk",
          "multilevel",
          "bda",
          "gvlma",
          "stargazer",
          "QuantPsyc",
          "pequod",
          "MASS",
          "texreg",
          "pwr",
          "effectsize",
          "semPlot",
          "lmtest",
          "semptools",
          "conflicted",
          "nnet",
          "ordinal",
          "DescTools")


packages <- rownames(installed.packages())
p_to_install <- pkgs[!(pkgs %in% packages)]

if(length(p_to_install) > 0){
  install.packages(p_to_install)
}

lapply(pkgs, library, character.only = TRUE)

# devtools::install_github("cardiomoon/semMediation")
library(semMediation)

# tell R which package to use for functions that are in multiple packages
these_functions <- c("mutate", "select", "summarize", "filter")
lapply(these_functions, conflict_prefer, "dplyr")

conflict_prefer("mutate", "dplyr")
conflict_prefer("select", "dplyr")
conflict_prefer("summarize", "dplyr")

1 Quick check-in on homework 2

1.0.1 Power analysis for ANOVA and chi-square

1.0.1.1 ANOVA

# quick ANOVA for demonstration
# create id variable for error term
iris_afex <- iris %>% 
  dplyr::mutate(id = row_number())

anova_ex <- afex::aov_car(Sepal.Length ~ Species + Error(id), data = iris_afex)
summary(anova_ex)
Anova Table (Type 3 tests)

Response: Sepal.Length
        num Df den Df     MSE      F     ges                Pr(>F)    
Species      2    147 0.26501 119.26 0.61871 < 0.00000000000000022 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# convert eta-squared to cohen's F with this function:
library(effectsize)
cohens_f(anova_ex)
# Effect Size for ANOVA (Type III)

Parameter | Cohen's f |      95% CI
-----------------------------------
Species   |      1.27 | [1.09, Inf]

- One-sided CIs: upper bound fixed at [Inf].

1.0.1.2 Chi-square

# here is another power analysis function that allows you to use cohen's w.
library(pwr)
pwr.chisq.test(w=0.2,df=1,power=.95,sig.level=0.05)

     Chi squared power calculation 

              w = 0.2
              N = 324.8677
             df = 1
      sig.level = 0.05
          power = 0.95

NOTE: N is the number of observations

1.0.1.3 Regression

library(effectsize)

# Get f-squared
reg_ex <- lm(Sepal.Length ~ Species, data = iris_afex)
cohens_f_squared(reg_ex)
# Effect Size for ANOVA

Parameter | Cohen's f2 |      95% CI
------------------------------------
Species   |       1.62 | [1.18, Inf]

- One-sided CIs: upper bound fixed at [Inf].
# Manually calculate f-squared
r_squared <- summary(reg_ex)$r.squared
f_squared <- r_squared / (1 - r_squared)

library(pwr)
power_analysis <- pwr.f2.test(u = 2, f2 = .15, sig.level = 0.05, power = 0.95)
print(power_analysis)

     Multiple regression power calculation 

              u = 2
              v = 103.0185
             f2 = 0.15
      sig.level = 0.05
          power = 0.95

1.0.2 Planned contrasts vs pairwise comparisons

Gender (Male, Female) Film (Bridget Jones, Memento)

For people who watched Memento, do women report higher arousal after watching the film than men?

1.0.2.1 Planned contrasts

d.data <- read.delim("/Users/kareenadelrosario/Downloads/ChickFlick.dat")

d.data <- d.data %>% 
  mutate(gender = as.factor(ifelse(gender == "Female", 0, 1)),
         film = as.factor(ifelse(film == "Memento", 0, 1)))

contrasts(d.data$gender) = contr.treatment(2, base = 2) # female = 1, male = 0
contrasts(d.data$film) # memento = 0, bridget jones = 1
  1
0 0
1 1
d_model <- lm(arousal ~ gender * film, data = d.data)
summary(d_model)

Call:
lm(formula = arousal ~ gender * film, data = d.data)

Residuals:
    Min      1Q  Median      3Q     Max 
-10.700  -4.350   0.450   5.425  11.300 

Coefficients:
              Estimate Std. Error t value           Pr(>|t|)    
(Intercept)     25.800      2.019  12.778 0.0000000000000061 ***
gender1         -1.100      2.856  -0.385            0.70234    
film1           -8.600      2.856  -3.012            0.00473 ** 
gender1:film1   -3.700      4.038  -0.916            0.36564    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 6.385 on 36 degrees of freedom
Multiple R-squared:  0.4525,    Adjusted R-squared:  0.4069 
F-statistic:  9.92 on 3 and 36 DF,  p-value: 0.00006611

1.0.2.2 Pairwise comparisons

d_emm <- emmeans(d_model, specs = c("gender", "film"))
pairs(d_emm)
 contrast                      estimate   SE df t.ratio p.value
 gender0 film0 - gender1 film0     -1.1 2.86 36  -0.385  0.9803
 gender0 film0 - gender0 film1     12.3 2.86 36   4.307  0.0007
 gender0 film0 - gender1 film1      7.5 2.86 36   2.627  0.0582
 gender1 film0 - gender0 film1     13.4 2.86 36   4.693  0.0002
 gender1 film0 - gender1 film1      8.6 2.86 36   3.012  0.0234
 gender0 film1 - gender1 film1     -4.8 2.86 36  -1.681  0.3483

P value adjustment: tukey method for comparing a family of 4 estimates 

1.0.2.3 Why can’t I just filter out Bridget Jones?

d.data.f <- d.data %>% 
  filter(film == 0)

f_model <- lm(arousal ~ gender, data = d.data.f)
summary(f_model)

Call:
lm(formula = arousal ~ gender, data = d.data.f)

Residuals:
    Min      1Q  Median      3Q     Max 
-10.700  -5.050  -0.700   6.225  11.300 

Coefficients:
            Estimate Std. Error t value      Pr(>|t|)    
(Intercept)   25.800      2.309  11.173 0.00000000158 ***
gender1       -1.100      3.265  -0.337          0.74    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 7.302 on 18 degrees of freedom
Multiple R-squared:  0.006265,  Adjusted R-squared:  -0.04894 
F-statistic: 0.1135 on 1 and 18 DF,  p-value: 0.7401

2 Robust Regression

credit: https://stats.oarc.ucla.edu/r/dae/robust-regression/

What is the difference between OLS and robust regression? When would you use one over the other?


2.1 Dealing with outliers using Hubert and bisquare weights

Robust regression is suitable for cases where you’d typically use OLS regression but encounter outliers or high leverage points that aren’t errors or from a different population. Instead of excluding these points or treating all data equally as in OLS regression, robust regression offers a middle ground by assigning different weights to observations based on their reliability. Essentially, it’s a weighted least squares approach that adjusts weights in response to the features of the data.

Terms to know:

- Residual: The difference between the predicted value (based on the regression equation) and the actual, observed value.

- Outlier: In linear regression, an outlier is an observation with large residual. In other words, it is an observation whose dependent-variable value is unusual given its value on the predictor variables. An outlier may indicate a sample peculiarity or may indicate a data entry error or other problem.

- Leverage: An observation with an extreme value on a predictor variable is a point with high leverage. Leverage is a measure of how far an independent variable deviates from its mean. High leverage points can have a great amount of effect on the estimate of regression coefficients.

- Influence: An observation is said to be influential if removing the observation substantially changes the estimate of the regression coefficients. Influence can be thought of as the product of leverage and outlierness.

- Cook’s distance (or Cook’s D): A measure that combines the information of leverage and residual of the observation.
cdata <- read.dta("https://stats.idre.ucla.edu/stat/data/crime.dta")

head(cdata)
  sid state crime murder pctmetro pctwhite pcths poverty single
1   1    ak   761    9.0     41.8     75.2  86.6     9.1   14.3
2   2    al   780   11.6     67.4     73.5  66.9    17.4   11.5
3   3    ar   593   10.2     44.7     82.9  66.3    20.0   10.7
4   4    az   715    8.6     84.7     88.6  78.7    15.4   12.1
5   5    ca  1078   13.1     96.7     79.3  76.2    18.2   12.5
6   6    co   567    5.8     81.8     92.5  84.4     9.9   12.1

2.1.0.1 Variables

  • (sid) State ID
  • (state) State name
  • (crime) Violent crimes per 100,000 people
  • (poverty) Percent of population living under poverty line
  • (single) Percent of population that are single parents
    • Total observations: 51
    • crime ~ poverty + single

2.1.0.2 Run OLS

We will begin by running an OLS regression and looking at diagnostic plots examining residuals, fitted values, Cook’s distance, and leverage.

summary(ols <- lm(crime ~ poverty + single, data = cdata))

Call:
lm(formula = crime ~ poverty + single, data = cdata)

Residuals:
    Min      1Q  Median      3Q     Max 
-811.14 -114.27  -22.44  121.86  689.82 

Coefficients:
             Estimate Std. Error t value        Pr(>|t|)    
(Intercept) -1368.189    187.205  -7.308 0.0000000024786 ***
poverty         6.787      8.989   0.755           0.454    
single        166.373     19.423   8.566 0.0000000000312 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 243.6 on 48 degrees of freedom
Multiple R-squared:  0.7072,    Adjusted R-squared:  0.695 
F-statistic: 57.96 on 2 and 48 DF,  p-value: 0.0000000000001578

2.1.1 Identify outliers

plot(ols)

From these plots, we can identify observations 9, 25, and 51 as possibly problematic to our model. We can look at these observations to see which states they represent.

cdata[c(9, 25, 51), 1:2]
   sid state
9    9    fl
25  25    ms
51  51    dc

DC, Florida and Mississippi have either high leverage or large residuals. We can display the observations that have relatively large values of Cook’s D. A conventional cut-off point is 4/n, where n is the number of observations in the data set.

d1 <- cooks.distance(ols) # captures influential observations
r <- stdres(ols) # computes standardized residuals
a <- cbind(cdata, d1, r) 
a[d1 > 4/51, ] # Filters the combined data frame a to display only those observations for which Cook's distance is greater than 4/n. n = 51 
   sid state crime murder pctmetro pctwhite pcths poverty single        d1         r
1    1    ak   761    9.0     41.8     75.2  86.6     9.1   14.3 0.1254750 -1.397418
9    9    fl  1206    8.9     93.0     83.5  74.4    17.8   10.6 0.1425891  2.902663
25  25    ms   434   13.5     30.7     63.3  64.3    24.7   14.7 0.6138721 -3.562990
51  51    dc  2922   78.5    100.0     31.8  73.1    26.4   22.1 2.6362519  2.616447

Now we will look at the residuals. We will generate a new variable called absr1, which is the absolute value of the residuals (because the sign of the residual doesn’t matter). We then print the ten observations with the highest absolute residual values.

rabs <- abs(r)
a <- cbind(cdata, d1, r, rabs)
asorted <- a[order(-rabs), ]
asorted[1:10, ]
   sid state crime murder pctmetro pctwhite pcths poverty single         d1         r
25  25    ms   434   13.5     30.7     63.3  64.3    24.7   14.7 0.61387212 -3.562990
9    9    fl  1206    8.9     93.0     83.5  74.4    17.8   10.6 0.14258909  2.902663
51  51    dc  2922   78.5    100.0     31.8  73.1    26.4   22.1 2.63625193  2.616447
46  46    vt   114    3.6     27.0     98.4  80.8    10.0   11.0 0.04271548 -1.742409
26  26    mt   178    3.0     24.0     92.6  81.0    14.9   10.8 0.01675501 -1.460885
21  21    me   126    1.6     35.7     98.5  78.8    10.7   10.6 0.02233128 -1.426741
1    1    ak   761    9.0     41.8     75.2  86.6     9.1   14.3 0.12547500 -1.397418
31  31    nj   627    5.3    100.0     80.8  76.7    10.9    9.6 0.02229184  1.354149
14  14    il   960   11.4     84.0     81.0  76.2    13.6   11.5 0.01265689  1.338192
20  20    md   998   12.7     92.8     68.9  78.4     9.7   12.0 0.03569623  1.287087
       rabs
25 3.562990
9  2.902663
51 2.616447
46 1.742409
26 1.460885
21 1.426741
1  1.397418
31 1.354149
14 1.338192
20 1.287087

2.1.2 IRLS with Huber weights

Now let’s run our first robust regression. Robust regression is done by iterated re-weighted least squares (IRLS). The command for running robust regression is rlm in the MASS package. There are several weighting functions that can be used for IRLS. We are going to first use the Huber weights in this example. We will then look at the final weights created by the IRLS process. This can be very useful.

summary(rr.huber <- rlm(crime ~ poverty + single, data = cdata))

Call: rlm(formula = crime ~ poverty + single, data = cdata)
Residuals:
    Min      1Q  Median      3Q     Max 
-846.09 -125.80  -16.49  119.15  679.94 

Coefficients:
            Value      Std. Error t value   
(Intercept) -1423.0373   167.5899    -8.4912
poverty         8.8677     8.0467     1.1020
single        168.9858    17.3878     9.7186

Residual standard error: 181.8 on 48 degrees of freedom

Let’s take a look at the weights of the top 15 largest residuals

hweights <- data.frame(state = cdata$state, resid = rr.huber$resid, weight = rr.huber$w)
hweights2 <- hweights[order(rr.huber$w), ]
hweights2[1:15, ]
   state      resid    weight
25    ms -846.08536 0.2889618
9     fl  679.94327 0.3595480
46    vt -410.48310 0.5955740
51    dc  376.34468 0.6494131
26    mt -356.13760 0.6864625
21    me -337.09622 0.7252263
31    nj  331.11603 0.7383578
14    il  319.10036 0.7661169
1     ak -313.15532 0.7807432
20    md  307.19142 0.7958154
19    ma  291.20817 0.8395172
18    la -266.95752 0.9159411
2     al  105.40319 1.0000000
3     ar   30.53589 1.0000000
4     az  -43.25299 1.0000000

We can see that roughly, as the absolute residual goes down, the weight goes up.

All observations not shown above have a weight of 1. In OLS regression, all cases have a weight of 1. Hence, the more cases in the robust regression that have a weight close to one, the closer the results of the OLS and robust regressions.

2.1.3 IRLS with bisquare weights

Next, let’s run the same model, but using the bisquare weighting function. Again, we can look at the weights.

rr.bisquare <- rlm(crime ~ poverty + single, data=cdata, psi = psi.bisquare)
summary(rr.bisquare)

Call: rlm(formula = crime ~ poverty + single, data = cdata, psi = psi.bisquare)
Residuals:
    Min      1Q  Median      3Q     Max 
-905.59 -140.97  -14.98  114.65  668.38 

Coefficients:
            Value      Std. Error t value   
(Intercept) -1535.3338   164.5062    -9.3330
poverty        11.6903     7.8987     1.4800
single        175.9303    17.0678    10.3077

Residual standard error: 202.3 on 48 degrees of freedom
# if you need significance values. MASS doesn't trust p-values and won't report them.
# texreg(rr.bisquare, ci.force=TRUE)

Here are the weights for the same 15 observations using bisquare weights

biweights <- data.frame(state = cdata$state, resid = rr.bisquare$resid, weight = rr.bisquare$w)
biweights2 <- biweights[order(rr.bisquare$w), ]
biweights2[1:15, ]
   state     resid      weight
25    ms -905.5931 0.007652565
9     fl  668.3844 0.252870542
46    vt -402.8031 0.671495418
26    mt -360.8997 0.731136908
31    nj  345.9780 0.751347695
18    la -332.6527 0.768938330
21    me -328.6143 0.774103322
1     ak -325.8519 0.777662383
14    il  313.1466 0.793658594
20    md  308.7737 0.799065530
19    ma  297.6068 0.812596833
51    dc  260.6489 0.854441716
50    wy -234.1952 0.881660897
5     ca  201.4407 0.911713981
10    ga -186.5799 0.924033113

We can see that the weight given to Mississippi is dramatically lower using the bisquare weighting function than the Huber weighting function and the parameter estimates from these two different weighting methods differ.

When comparing the results of a regular OLS regression and a robust regression, if the results are very different, you will most likely want to use the results from the robust regression. Large differences suggest that the model parameters are being highly influenced by outliers. Different functions have advantages and drawbacks. Huber weights can have difficulties with severe outliers, and bisquare weights can have difficulties converging.


2.2 GLS for heteroscedasticity

Robust regression does not directly address heteroscedasticity. To account for heteroscedasticity or autocorrelation in GLS, you would typically need to specify a particular structure for the covariance matrix of the errors that reflects the nature of the heteroscedasticity or correlation you’re dealing with. In other words, what is driving the difference in variance?

Heteroskedasticity.
Heteroskedasticity.

Check the residuals vs fitted plot to see if the line is curved. That would indicate heteroscedasticity

res <- residuals(ols)
yhat <- fitted(ols)
plot(cdata$poverty,res, xlab="poverty", ylab="residuals")

plot(cdata$single,res, xlab="single", ylab="residuals")

2.2.1 Breush Pagan Test

# why might this be significant?
bptest(ols)

    studentized Breusch-Pagan test

data:  ols
BP = 7.6296, df = 2, p-value = 0.02204
ggplot(cdata, aes(x = single, y = crime)) +
  geom_point(aes(color = poverty), alpha = 0.5) +  # Color points by poverty level
  geom_smooth(method = "lm", formula = y ~ x, se = FALSE, color = "green") +
  labs(x = "Single Parent Percentage", y = "Crime Rate",
       title = "Crime Rate vs. Single Parent Percentage",
       subtitle = "Colored by Poverty Level") +
  theme_minimal()

2.2.1.1 Using weights in the GLS Model

# Let's say we find that as single-parent household % increases, crime variance increases. This suggests a proportional relationship between single-parent and crime, so we'll account for variance changes as a power function 
glsModel <- gls(crime ~ poverty + single, data = cdata, 
               weights = varPower(form = ~ single))
summary(glsModel)
Generalized least squares fit by REML
  Model: crime ~ poverty + single 
  Data: cdata 
       AIC      BIC    logLik
  680.0737 689.4297 -335.0368

Variance function:
 Structure: Power of variance covariate
 Formula: ~single 
 Parameter estimates:
   power 
1.848189 

Coefficients:
                 Value Std.Error   t-value p-value
(Intercept) -1215.4468 229.36175 -5.299257  0.0000
poverty         8.7081   8.10173  1.074840  0.2878
single        150.3019  23.44772  6.410087  0.0000

 Correlation: 
        (Intr) povrty
poverty -0.055       
single  -0.892 -0.387

Standardized residuals:
       Min         Q1        Med         Q3        Max 
-2.1045268 -0.4871898 -0.1202581  0.5487129  3.3453653 

Residual standard error: 2.563121 
Degrees of freedom: 51 total; 48 residual
# Plotting standardized residuals vs fitted values
plot(fitted(glsModel), residuals(glsModel, type = "normalized"),
     xlab = "Fitted values", ylab = "Standardized Residuals")
abline(h = 0, lty = 2)

# Let's say that the variability in crime scores differs as a function of the state (could formally test with levene's test). We could account for that on the weights line. Note this doesn't work here because each state only has one entry.
glsModel2 <- gls(crime ~ poverty + single, data = cdata, 
               weights = varIdent(form = ~ 1 | state))

summary(glsModel2)
Generalized least squares fit by REML
  Model: crime ~ poverty + single 
  Data: cdata 
       AIC      BIC    logLik
  689.3053 790.3501 -290.6526

Variance function:
 Structure: Different standard deviations per stratum
 Formula: ~1 | state 
 Parameter estimates:
             ak              al              ar              az              ca 
 1.000000000000  2.048549058024  0.068085574850  0.576119727497  4.300623446230 
             co              ct              de              fl              ga 
 0.000005454337  2.461595107494  2.922156518667  9.754497652367  0.418252848397 
             hi              ia              id              il              in 
 1.615479026361  2.084911823269  0.267399898634  5.737528373745  0.600830252092 
             ks              ky              la              ma              md 
 2.097280314278  1.744754031846  2.671187574741  5.420342182435  6.481315070471 
             me              mi              mn              mo              ms 
 3.861041771601  0.039565313233  0.077530452236  2.998643425989 10.913292603298 
             mt              nc              nd              ne              nh 
 4.671621555841  2.174747057479  0.603570546230  1.539150720484  0.890348399478 
             nj              nm              nv              ny              oh 
 5.171393725120  0.000028911708  3.937796504993  4.388504628947  0.510888858428 
             ok              or              pa              ri              sc 
 0.029274306912  0.000004348616  1.489888746595  0.374030689892  3.728761133878 
             sd              tn              tx              ut              va 
 1.468792274624  1.769559166951  1.237389620521  0.226046963631  0.536417315386 
             vt              wa              wi              wv              wy 
 4.569384180392  0.645710065105  2.037106623757  3.747919048775  2.651974336771 
             dc 
11.049378262028 

Coefficients:
                 Value   Std.Error   t-value p-value
(Intercept) -1161.1918 0.007238506 -160418.7       0
poverty        19.6750 0.000208372   94422.5       0
single        126.7281 0.000529827  239187.5       0

 Correlation: 
        (Intr) povrty
poverty -0.586       
single  -0.952  0.311

Standardized residuals:
       Min         Q1        Med         Q3        Max 
-1.0000071 -0.9999989  0.9999944  1.0000002  1.0000075 

Residual standard error: 69.06124 
Degrees of freedom: 51 total; 48 residual

2.3 Generating robust CIs with bootstrapping

Bootstrapping can be helpful if you have a small sample or your data violate the regression assumptions and you want to get more accurate CI estimates. While this won’t correct for heterogeneity of variance, bootstrapping can be used to estimate robust standard errors for regression coefficients.

When to use bootstrapping:

  • Assumptions are violated
  • Sample size is small
  • Presence of outliers
bootReg <- function(formula, data, indices) {
  d <- data[indices, ]  # Use 'indices' to create a subset dataframe 'd'
  fit <- lm(formula, data = d)
  return(coef(fit))
}

bootResults <- boot (statistic = bootReg, formula = crime ~ poverty + single, data = cdata, R = 2000)

# based on the bootstrapping, how biased are our estimates?
summary(bootResults)

Number of bootstrap replications R = 2000 
    original bootBias  bootSE   bootMed
1 -1368.1887  93.4373 336.521 -1364.583
2     6.7874   1.5695  10.896     8.219
3   166.3727 -10.2531  31.364   163.437
summary(ols)

Call:
lm(formula = crime ~ poverty + single, data = cdata)

Residuals:
    Min      1Q  Median      3Q     Max 
-811.14 -114.27  -22.44  121.86  689.82 

Coefficients:
             Estimate Std. Error t value        Pr(>|t|)    
(Intercept) -1368.189    187.205  -7.308 0.0000000024786 ***
poverty         6.787      8.989   0.755           0.454    
single        166.373     19.423   8.566 0.0000000000312 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 243.6 on 48 degrees of freedom
Multiple R-squared:  0.7072,    Adjusted R-squared:  0.695 
F-statistic: 57.96 on 2 and 48 DF,  p-value: 0.0000000000001578
# intercept (index = 1)
boot.ci(bootResults, type = "bca", index = 1) # bca = bias corrected and accelerated
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 2000 bootstrap replicates

CALL : 
boot.ci(boot.out = bootResults, type = "bca", index = 1)

Intervals : 
Level       BCa          
95%   (-1764,  -590 )  
Calculations and Intervals on Original Scale
# poverty (index = 2)
boot.ci(bootResults, type = "bca", index = 2) 
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 2000 bootstrap replicates

CALL : 
boot.ci(boot.out = bootResults, type = "bca", index = 2)

Intervals : 
Level       BCa          
95%   (-16.145,  26.785 )  
Calculations and Intervals on Original Scale
# single (index = 3)
boot.ci(bootResults, type = "bca", index = 3) 
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 2000 bootstrap replicates

CALL : 
boot.ci(boot.out = bootResults, type = "bca", index = 3)

Intervals : 
Level       BCa          
95%   (103.9, 214.5 )  
Calculations and Intervals on Original Scale
Some BCa intervals may be unstable

How do these compare to the OLS confidence intervals?

confint(ols)
                  2.5 %     97.5 %
(Intercept) -1744.58988 -991.78745
poverty       -11.28529   24.86001
single        127.32029  205.42505

2.4 Your turn!

A study was carried out to explore the relationship between Aggression and several potential predicting factors in 666 children who had an older sibling.

Variables measured were:

  • Parenting_Style (high score = bad parenting practices)
  • Computer_ Games (high score = more time spent playing computer games)
  • Television (high score = more time spent watching television)
  • Diet (high score = the child has a good diet low in additives)
  • Sibling_Aggression (high score = more aggression seen in their older sibling).

The data are in the file ChildAggression.dat.

  1. Run a multiple regression testing the effect of these variables, using “Aggression” as the DV.
  2. Obtain the standardized parameter estimates (aka standardized betas).
  3. Check whether the model meets the regression assumptions using both plots and formal tests: a. homoscedasticity b. multicollinearity c. independence (no autocorrelation in residuals)
  4. Report your results – how would you interpret the R-squared? Which variables significantly predicted aggression?
df <- read_delim("/Users/kareenadelrosario/Downloads/ChildAggression.dat", delim = " ")
model <- lm(Aggression ~ Television + Computer_Games + Sibling_Aggression + Diet + Parenting_Style, data = df)

summary(model)

Call:
lm(formula = Aggression ~ Television + Computer_Games + Sibling_Aggression + 
    Diet + Parenting_Style, data = df)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.12629 -0.15253 -0.00421  0.15222  1.17669 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)        -0.004988   0.011983  -0.416 0.677350    
Television          0.032916   0.046057   0.715 0.475059    
Computer_Games      0.142161   0.036920   3.851 0.000129 ***
Sibling_Aggression  0.081684   0.038780   2.106 0.035550 *  
Diet               -0.109054   0.038076  -2.864 0.004315 ** 
Parenting_Style     0.056648   0.014557   3.891 0.000110 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3071 on 660 degrees of freedom
Multiple R-squared:  0.08258,   Adjusted R-squared:  0.07563 
F-statistic: 11.88 on 5 and 660 DF,  p-value: 0.00000000005025
lm.beta(model)
        Television     Computer_Games Sibling_Aggression               Diet 
        0.03192490         0.15211518         0.08357717        -0.11503080 
   Parenting_Style 
        0.17735588 

Residuals vs Fitted: is used to check the assumptions of linearity. If the residuals are spread equally around a horizontal line without distinct patterns (red line is approximately horizontal at zero), that is a good indication of having a linear relationship.

Normal Q-Q: is used to check the normality of residuals assumption. If the majority of the residuals follow the straight dashed line, then the assumption is fulfilled.

Scale-Location: is used to check the homoscedasticity of residuals (equal variance of residuals). If the residuals are spread randomly and the see a horizontal line with equally (randomly) spread points, then the assumption is fulfilled.

Residuals vs Leverage: is used to identify any influential value in our dataset. Influential values are extreme values that might influence the regression results when included or excluded from the analysis. Look for cases outside of a dashed line (if there is a concerning point, you’ll see red lines pop up).

Homoscedasticity: residuals vs fitted & bp

plot(model)

bptest(model)

    studentized Breusch-Pagan test

data:  model
BP = 10.439, df = 5, p-value = 0.0637

Multicollinearity

  • If the largest VIF is greater 10, then there’s cause for concern
  • If the average VIF is substantially greater than 1, the model may be biased
  • Tolerance below 0.1 means there’s a serious problem
  • Tolerance below 0.2 means there’s a potential problem
# vif
vif(model)
        Television     Computer_Games Sibling_Aggression               Diet 
          1.435525           1.122719           1.132618           1.160466 
   Parenting_Style 
          1.494296 
# average vif
mean(vif(model))
[1] 1.269125
# tolerance
1/vif(model)
        Television     Computer_Games Sibling_Aggression               Diet 
         0.6966095          0.8906946          0.8829104          0.8617231 
   Parenting_Style 
         0.6692115 

Independence

durbinWatsonTest(model)
 lag Autocorrelation D-W Statistic p-value
   1      0.04218005      1.912808   0.286
 Alternative hypothesis: rho != 0

Skew

gvlma(model)

Call:
lm(formula = Aggression ~ Television + Computer_Games + Sibling_Aggression + 
    Diet + Parenting_Style, data = df)

Coefficients:
       (Intercept)          Television      Computer_Games  Sibling_Aggression  
         -0.004988            0.032916            0.142161            0.081684  
              Diet     Parenting_Style  
         -0.109054            0.056648  


ASSESSMENT OF THE LINEAR MODEL ASSUMPTIONS
USING THE GLOBAL TEST ON 4 DEGREES-OF-FREEDOM:
Level of Significance =  0.05 

Call:
 gvlma(x = model) 

                     Value              p-value                   Decision
Global Stat        62.8865 0.000000000000716982 Assumptions NOT satisfied!
Skewness            0.4924 0.482846201232194572    Assumptions acceptable.
Kurtosis           61.4485 0.000000000000004552 Assumptions NOT satisfied!
Link Function       0.3833 0.535842599337205128    Assumptions acceptable.
Heteroscedasticity  0.5623 0.453336961025912588    Assumptions acceptable.

3 Centering, rescaling, standardizing, contrast coding your predictors

adapted from: https://advstats.psychstat.org/book/mregression/index.php

Note that you never HAVE to center/recode your predictors. It should never influence the test significance– just the interpretation of your intercept and coefficients.

  • If you have continuous predictors, you can center.
  • If you have categorical predictors, you can dummy code or effects code (see ANOVA labs for info).
  • If you have predictors with different scales, you can standardize.

Let’s say we’re interested in the effects of high school GPA (h.gpa), SAT, and quality of recommendation letters (recommd) on college GPA (c.gpa).

gpa_df <- read.csv("/Users/kareenadelrosario/Downloads/gpa.csv")

head(gpa_df)
  c.gpa h.gpa  SAT recommd
1  2.04  2.01 1070       5
2  2.56  3.40 1254       6
3  3.75  3.68 1466       6
4  1.10  1.54  706       4
5  3.00  3.32 1160       5
6  0.05  0.33  756       3
par(mfrow=c(2,2))
plot(gpa_df$h.gpa, gpa_df$c.gpa)
plot(gpa_df$SAT, gpa_df$c.gpa)
plot(gpa_df$recommd, gpa_df$c.gpa)


# original model
gpaModel<- lm(c.gpa ~ h.gpa + SAT + recommd, data = gpa_df)

summary(gpaModel)

Call:
lm(formula = c.gpa ~ h.gpa + SAT + recommd, data = gpa_df)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.0979 -0.4407 -0.0094  0.3859  1.7606 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.1532639  0.3229381  -0.475 0.636156    
h.gpa        0.3763511  0.1142615   3.294 0.001385 ** 
SAT          0.0012269  0.0003032   4.046 0.000105 ***
recommd      0.0226843  0.0509817   0.445 0.657358    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.5895 on 96 degrees of freedom
Multiple R-squared:  0.3997,    Adjusted R-squared:  0.381 
F-statistic: 21.31 on 3 and 96 DF,  p-value: 0.000000000116

How would you interpret these estimates?

3.0.0.1 Centering

# center separately
gpa_df$h.gpaC <- scale(gpa_df$h.gpa, center=TRUE, scale=FALSE) # mean center without rescaling 

# shortcut
centeredModel<- lm(c.gpa~ h.gpaC + scale(SAT,scale=F) + scale(recommd,scale=F), data=gpa_df) 

summary(centeredModel)

Call:
lm(formula = c.gpa ~ h.gpaC + scale(SAT, scale = F) + scale(recommd, 
    scale = F), data = gpa_df)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.0979 -0.4407 -0.0094  0.3859  1.7606 

Coefficients:
                           Estimate Std. Error t value             Pr(>|t|)    
(Intercept)               1.9805000  0.0589476  33.598 < 0.0000000000000002 ***
h.gpaC                    0.3763511  0.1142615   3.294             0.001385 ** 
scale(SAT, scale = F)     0.0012269  0.0003032   4.046             0.000105 ***
scale(recommd, scale = F) 0.0226843  0.0509817   0.445             0.657358    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.5895 on 96 degrees of freedom
Multiple R-squared:  0.3997,    Adjusted R-squared:  0.381 
F-statistic: 21.31 on 3 and 96 DF,  p-value: 0.000000000116

3.0.0.2 Standardizing

When predictors like GPA (typically on a 0 to 4 scale) and SAT scores (typically on a 400 to 1600 scale) are involved, their coefficients can be difficult to compare directly because a one-unit change means something very different for each predictor. Through standardization, however, we can remove the scales of the predictors and therefore make the coefficients relatively more comparable. We can standardize predictors only or both predictors and the outcome variable.

After standardization, the variable means are all 0 and variances are all 1. The beta coefficient (output), tells us how many standard deviations the predicted DV changes given one standard deviation change in the IV when the other IVs are held constant.

In R, scale() can also be used for standardization. Note that to skip the estimation of the intercept, one can add -1 in the regression model formular.

# same as above except we remove the scale = F statement
betaModel<-lm(scale(c.gpa) ~ scale(h.gpa) + scale(SAT) + scale(recommd)-1, data=gpa_df)

summary(betaModel)

Call:
lm(formula = scale(c.gpa) ~ scale(h.gpa) + scale(SAT) + scale(recommd) - 
    1, data = gpa_df)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.46544 -0.58820 -0.01254  0.51510  2.34994 

Coefficients:
               Estimate Std. Error t value  Pr(>|t|)    
scale(h.gpa)    0.36285    0.10959   3.311   0.00131 ** 
scale(SAT)      0.35593    0.08751   4.067 0.0000968 ***
scale(recommd)  0.04528    0.10123   0.447   0.65568    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.7827 on 97 degrees of freedom
Multiple R-squared:  0.3997,    Adjusted R-squared:  0.3812 
F-statistic: 21.53 on 3 and 97 DF,  p-value: 0.00000000009028

Alternatively, you could feed your centered model through lm.beta() to get the betas without rerunning the model

lm.beta(centeredModel)
                   h.gpaC     scale(SAT, scale = F) scale(recommd, scale = F) 
                0.3628458                 0.3559267                 0.0452765 

4 Moderation vs Mediation

moderation & mediation content was adapted from: https://ademos.people.uic.edu/Chapter14.html#

You can think of this distinction as mediation answers how two variables are related whereas moderation tells us it depends.

  • Mediation analysis tests for a hypothetical causal relationship in which X -> Y, and the underlying mechanism can be explained by M.
  • Moderation looks at the influence of Z on X->Y to test for when or under what conditions an effect occurs. Moderators can strengthen, weaken, or reverse the nature of a relationship.

Mediators describe the how or why of a (typically well-established) relationship between two other variables and are sometimes called intermediary variables since they often describe the process through which an effect occurs. This is also sometimes called an indirect effect. For instance, people with higher incomes tend to live longer but this effect is explained by the mediating influence of having access to better health care.

5 Moderation Analyses

Moderation tests whether a variable (Z) affects the direction and/or strength of the relation between an IV (X) and a DV (Y). In other words, moderation tests for interactions that affect WHEN relationships between variables occur. Moderators are conceptually different from mediators (when versus how/why) but some variables may be a moderator or a mediator depending on your question.

Like mediation, moderation assumes that there is little to no measurement error in the moderator variable and that the DV did not CAUSE the moderator.

Basic Moderation Model.
Basic Moderation Model.

5.1 Example Moderation Data

In this example we’ll say we are interested in whether the relationship between the number of hours of sleep (X) a graduate student receives and the attention that they pay to this tutorial (Y) is influenced by their consumption of coffee (Z).

Here we create the moderation effect by making our DV (Y) the product of levels of the IV (X) and our moderator (Z).

set.seed(123)#Standardizes the numbers generated by rnorm

N  <- 100 #Number of participants; graduate students
X.hours  <- abs(rnorm(N, 6, 4)) #IV; Hours of sleep
X1 <- abs(rnorm(N, 60, 30)) #Adding some systematic variance for our DV
Z.coffee  <- rnorm(N, 30, 8) #Moderator; Ounces of coffee consumed
Y.attn  <- abs((-0.8*X.hours) * (0.2*Z.coffee) - 0.5*X.hours - 0.4*X1 + 10 + rnorm(N, 0, 3)) #DV; Attention Paid
Moddata <- data.frame(X.hours, X1, Z.coffee, Y.attn)

summary(Moddata)
    X.hours             X1             Z.coffee         Y.attn       
 Min.   : 0.195   Min.   :  1.597   Min.   :15.95   Min.   :  2.386  
 1st Qu.: 4.025   1st Qu.: 35.967   1st Qu.:25.75   1st Qu.: 30.155  
 Median : 6.247   Median : 53.225   Median :30.29   Median : 47.761  
 Mean   : 6.483   Mean   : 56.806   Mean   :30.96   Mean   : 47.763  
 3rd Qu.: 8.767   3rd Qu.: 74.035   3rd Qu.:36.11   3rd Qu.: 61.727  
 Max.   :14.749   Max.   :157.231   Max.   :48.34   Max.   :136.947  

Moderation can be tested by looking for significant interactions between the moderating variable (Z) and the IV (X). It is important to mean center both your moderator and your IV to reduce multicollinearity and make interpretation easier. Centering can be done using the scale function, which subtracts the mean of a variable from each value in that variable.

#Moderation 
fitMod <- lm(Y.attn ~ X.hours + Z.coffee + X.hours:Z.coffee, data = Moddata) #Model interacts IV & moderator

# Alternative formatting
# fitMod <- lm(Y.wake ~ X.hours*Z.coffee) 


print_p(coef(summary(fitMod)))
                 Estimate Std. Error t value Pr(>|t|)
(Intercept)       27.5216    10.4032  2.6455   0.0095
X.hours           -2.0323     1.3796 -1.4732   0.1440
Z.coffee          -0.4114     0.3075 -1.3381   0.1840
X.hours:Z.coffee   0.2338     0.0413  5.6563   0.0000

5.1.1 How would you interpret the coefficients and intercept?


5.2 Centering predictors

Sometimes our coefficients don’t make any sense. We can make the 0 meaningful by centering our predictors.

### Two ways to center
Moddata <- Moddata %>% 
  mutate(X.hoursC = X.hours - mean(X.hours),
         Z.coffeeC = Z.coffee - mean(Z.coffee))

# Centering Data
X.hoursC    <- scale(X.hours, center=TRUE, scale=FALSE) #Centering IV; hours of sleep
Z.coffeeC    <- scale(Z.coffee,  center=TRUE, scale=FALSE) #Centering moderator; coffee consumption


##################################

# Reanalyze with centered variables
fitModC <- lm(Y.attn ~ X.hoursC + Z.coffeeC + X.hoursC:Z.coffeeC, data = Moddata) 
print_p(coef(summary(fitModC)))
                   Estimate Std. Error t value Pr(>|t|)
(Intercept)         48.5444     1.1729 41.3899        0
X.hoursC             5.2081     0.3487 14.9358        0
Z.coffeeC            1.1044     0.1554  7.1083        0
X.hoursC:Z.coffeeC   0.2338     0.0413  5.6563        0

gvlma(fitModC) #data is positively skewed; could log transform 

Call:
lm(formula = Y.attn ~ X.hoursC + Z.coffeeC + X.hoursC:Z.coffeeC, 
    data = Moddata)

Coefficients:
       (Intercept)            X.hoursC           Z.coffeeC  X.hoursC:Z.coffeeC  
           48.5444              5.2081              1.1044              0.2338  


ASSESSMENT OF THE LINEAR MODEL ASSUMPTIONS
USING THE GLOBAL TEST ON 4 DEGREES-OF-FREEDOM:
Level of Significance =  0.05 

Call:
 gvlma(x = fitModC) 

                     Value p-value                   Decision
Global Stat        7.68778 0.10371    Assumptions acceptable.
Skewness           5.97432 0.01452 Assumptions NOT satisfied!
Kurtosis           0.94082 0.33207    Assumptions acceptable.
Link Function      0.73540 0.39114    Assumptions acceptable.
Heteroscedasticity 0.03724 0.84698    Assumptions acceptable.
#Data Summary
stargazer(fitModC, type="html", digits = 2, font.size = "footnotesize", title = "Sleep and Coffee on Attention")
Sleep and Coffee on Attention
Dependent variable:
Y.attn
X.hoursC 5.21***
(0.35)
Z.coffeeC 1.10***
(0.16)
X.hoursC:Z.coffeeC 0.23***
(0.04)
Constant 48.54***
(1.17)
Observations 100
R2 0.77
Adjusted R2 0.76
Residual Std. Error 11.65 (df = 96)
F Statistic 104.78*** (df = 3; 96)
Note: p<0.1; p<0.05; p<0.01
#Plotting
ps  <- plotSlopes(fitModC, plotx="X.hoursC", modx="Z.coffeeC", xlab = "Sleep", ylab = "Attention Paid", modxVals = "std.dev")

5.3 Interpreting Moderation Results

Our model shows a significant interaction between hours slept and coffee consumption on attention paid to this tutorial (b = .23, SE = .04, p < .001). However, we’ll need to unpack this interaction visually to get a better idea of what this means.

The rockchalk function will automatically plot the simple slopes (1 SD above and 1 SD below the mean) of the moderating effect.

  • This figure shows that those who drank less coffee (the black line) paid more attention with the more sleep that they got last night but paid less attention overall that average (the blue line).
  • Those who drank more coffee (the green line) paid more attention when they slept more as well and paid more attention than average.
  • The difference in the slopes for those who drank more or less coffee shows that coffee consumption moderates the relationship between hours of sleep and attention paid.

5.4 Decomposing interactions

credit: https://stats.oarc.ucla.edu/r/seminars/interactions-r/#s2

You know that hours spent exercising improves weight loss, but how does it interact with effort?

This is a hypothetical study of weight loss for 900 participants in a year-long study of 3 different exercise programs, a jogging program, a swimming program, and a reading program which serves as a control activity. Variables include

  • loss: weight loss (continuous), positive = weight loss, negative scores = weight gain
  • hours: hours spent exercising (continuous)
  • effort: effort during exercise (continuous), 0 = minimal physical effort and 50 = maximum effort
  • gender: participant gender (binary)
    • male=1
    • female=2
dat <- read.csv("https://stats.idre.ucla.edu/wp-content/uploads/2019/03/exercise.csv")

dat$gender <- factor(dat$gender,labels=c("male","female"))

5.4.1 Continuous by continuous

How do we interpret an interaction with continuous predictors? And what is extrapolating?

\(Weight\ Loss = b_0 + b_1Hours + b_2Effort + b_3Hours*Effort\)

contcont <- lm(loss~hours*effort,data=dat)
summary(contcont)

Call:
lm(formula = loss ~ hours * effort, data = dat)

Residuals:
   Min     1Q Median     3Q    Max 
-29.52 -10.60  -1.78  11.13  34.51 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)  
(Intercept)   7.79864   11.60362   0.672   0.5017  
hours        -9.37568    5.66392  -1.655   0.0982 .
effort       -0.08028    0.38465  -0.209   0.8347  
hours:effort  0.39335    0.18750   2.098   0.0362 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 13.56 on 896 degrees of freedom
Multiple R-squared:  0.07818,   Adjusted R-squared:  0.07509 
F-statistic: 25.33 on 3 and 896 DF,  p-value: 0.0000000000000009826

Although we may think that the slope of Hours should be positive at levels of Effort, remember that in this case, Effort is zero. As we see in our data, this is improbable as the minimum value of effort is 12.95.

summary(dat$effort)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  12.95   26.26   29.63   29.66   33.10   44.08 

What would we expect weight loss to look like at average effort?

(mylist <- list(hours=2,effort=30))
$hours
[1] 2

$effort
[1] 30
emmeans(contcont, ~ hours*effort, at=mylist)
 hours effort emmean    SE  df lower.CL upper.CL
     2     30   10.2 0.453 896     9.35     11.1

Confidence level used: 0.95 

The results show that predicted weight loss is 10.2 pounds if we put in two hours of exercise and an effort level of 30; this seems reasonable. Let’s see what happens when we predict weight loss for two hours of exercise given an effort level of 0.

mylist <- list(hours=2,effort=0) 
emmeans(contcont, ~ hours*effort, at=mylist)
 hours effort emmean   SE  df lower.CL upper.CL
     2      0    -11 2.65 896    -16.1    -5.76

Confidence level used: 0.95 

The predicted weight gain is now 11 pounds. Weird, right? This is an example of extrapolating, which means we are making predictions about our data beyond what the data can support. This is why we should always choose reasonable values of our predictors in order to interpret our data properly.

extrapolating
extrapolating

We know to choose reasonable values when predicting values. The same concept applies when decomposing an interaction. Our output suggests that Hours varies by levels of Effort. Since effort is continuous, we can choose an infinite set of values with which to fix effort.

For ease of presentation, we can use what’s called “spotlight analysis” (Aiken & West, 1991). To do a spotlight analysis, we’ll divide Effort into three levels: low, average, high.

$EffA = + (Effort) $

$Eff = $

$EffB = - (Effort) $

# high effort
effa <- mean(dat$effort) + sd(dat$effort)
(effar <- round(effa,1))
[1] 34.8
# average effort
eff <- mean(dat$effort)
(effr <- round(eff,1))
[1] 29.7
# low effort
effb <- mean(dat$effort) - sd(dat$effort)
(effbr <- round(effb,1))
[1] 24.5

Recall that our simple slope is the relationship of hours on weight loss fixed a particular values of effort. In R, we can obtain simple slopes using the function emtrends. We first create a list which incorporates the three values of effort we found above in preparation for spotlight analysis.

mylist <- list(effort=c(effbr,effr,effar))

emtrends(contcont, pairwise ~effort, var="hours",at=mylist, adjust="none")
$emtrends
 effort hours.trend    SE  df lower.CL upper.CL
   24.5       0.261 1.352 896   -2.392     2.91
   29.7       2.307 0.915 896    0.511     4.10
   34.8       4.313 1.308 896    1.745     6.88

Confidence level used: 0.95 

$contrasts
 contrast                estimate    SE  df t.ratio p.value
 effort24.5 - effort29.7    -2.05 0.975 896  -2.098  0.0362
 effort24.5 - effort34.8    -4.05 1.931 896  -2.098  0.0362
 effort29.7 - effort34.8    -2.01 0.956 896  -2.098  0.0362

All comparisons of simple slopes result in the same p-value as the interaction itself.

Based on the tests above, yes the maginitude of the slope is larger between “low” and “high” versus “medium” and “high”, but the two p-values are the same.

(mylist <- list(hours=seq(0,4,by=0.4),effort=c(effbr,effr,effar)))
$hours
 [1] 0.0 0.4 0.8 1.2 1.6 2.0 2.4 2.8 3.2 3.6 4.0

$effort
[1] 24.5 29.7 34.8
emmip(contcont,effort~hours,at=mylist, CIs=TRUE)

The results suggest that hours spent exercising is only effective for weight loss if we put in more effort, which supports the rationale for high intensity interval training.

5.5 Continuous by Categorical

The research question here is, do men and women (W) differ in the relationship between Hours (X) and Weight loss? Here, we’re going to use dummy coding.

dat$gender <- relevel(dat$gender, ref="female") 

contcat <- lm(loss~hours*gender,data=dat)
summary(contcat)

Call:
lm(formula = loss ~ hours * gender, data = dat)

Residuals:
    Min      1Q  Median      3Q     Max 
-27.118 -11.350  -1.963  10.001  42.376 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)         3.335      2.731   1.221    0.222  
hours               3.315      1.332   2.489    0.013 *
gendermale          3.571      3.915   0.912    0.362  
hours:gendermale   -1.724      1.898  -0.908    0.364  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 14.06 on 896 degrees of freedom
Multiple R-squared:  0.008433,  Adjusted R-squared:  0.005113 
F-statistic:  2.54 on 3 and 896 DF,  p-value: 0.05523

5.5.0.1 Obtaining simple slopes by each level of the categorical moderator

Since our goal is to obtain simple slopes of Hours by gender we use emtrends. We do not use emmeans because this function gives us the predicted values rather than slopes.

emtrends(contcat, ~ gender, var="hours")
 gender hours.trend   SE  df lower.CL upper.CL
 female        3.32 1.33 896    0.702     5.93
 male          1.59 1.35 896   -1.063     4.25

Confidence level used: 0.95 

A common misconception is that since the simple slope of Hours is significant for females but not males, we should have seen a significant interaction. However, the interaction tests the difference of the Hours slope for males and females and not whether each simple slope is different from zero (which is what we have from the output above).

To test the difference in slopes, we add pairwise ~ gender to tell the function that we want the pairwise difference in the simple slope of Hours for females versus males.

emtrends(contcat, pairwise ~ gender, var="hours")
$emtrends
 gender hours.trend   SE  df lower.CL upper.CL
 female        3.32 1.33 896    0.702     5.93
 male          1.59 1.35 896   -1.063     4.25

Confidence level used: 0.95 

$contrasts
 contrast      estimate  SE  df t.ratio p.value
 female - male     1.72 1.9 896   0.908  0.3639

Recall from our summary table, this is exactly the same as the interaction, which verifies that we have in fact obtained the interaction coefficient.

(mylist <- list(hours=seq(0,4,by=0.4),gender=c("female","male")))
$hours
 [1] 0.0 0.4 0.8 1.2 1.6 2.0 2.4 2.8 3.2 3.6 4.0

$gender
[1] "female" "male"  
emmip(contcat, gender ~hours, at=mylist,CIs=TRUE)

Another common approach to plotting predicted values:

newdata <- expand.grid(hours = seq(0, 4, by = 0.4), gender = c("female", "male"))
prediction <- predict(contcat, newdata, interval="confidence", level = 0.95)

# combine
newdata <- cbind(newdata, prediction)


ggplot(newdata, aes(x = hours, y = fit, color = gender)) +
  geom_line(size = 1) + 
  geom_point(size = 2, shape = 1, fill = "white") +
  geom_ribbon(aes(ymin = lwr, ymax = upr, fill = gender), alpha = 0.2, color = NA) +
  theme_minimal() +
  scale_color_manual(values = c("#4878D0", "#D65F5F")) +
  scale_fill_manual(values = c("#4878D0", "#D65F5F")) +
  labs(
    title = "Predicted Loss by Hours and Gender",
    x = "Hours",
    y = "Predicted Loss",
    color = "Gender",
    fill = "Gender"
  )

5.5.0.2 In sum

  • Predicted values are points on the graph.
  • Simple effects or slopes are the difference between two predicted values (i.e., two points).
  • Interactions are the difference between simple effects of slopes;
    • for an interaction involving a continuous IV, it’s the difference of two slopes (i.e., two lines)
    • for a categorical by categorical interaction, the interaction is the difference in the height of the bars in one group versus the difference of the heights in another group (a.k.a., difference of differences).

6 Mediation Analyses

The Baron & Kenny method is among the original methods for testing for mediation but tends to have low statistical power. We’re covering it here because it provides a very clear approach to establishing relationships between variables and is still occasionally requested by reviewers.

Mediation tests whether the effects of X (the independent variable) on Y (the dependent variable) operate through a third variable, M (the mediator). In this way, mediators explain the causal relationship between two variables or “how” the relationship works.

Basic Mediation Model.
Basic Mediation Model.
c = the total effect of X on Y with no consideration of mediator variables
c'= the direct effect of X on Y after controlling for M 
ab= the indirect effect of X on Y through M

The above shows the standard mediation model. Full mediation occurs when the effect of X on Y disappears with M in the model. Partial mediation occurs when the effect of X on Y decreases by a nontrivial amount (the actual amount is up for debate) with M in the model.

6.0.1 Example Mediation Data

In this example we’ll say we are interested in whether the number of hours since dawn (X) affect the subjective ratings of wakefulness (Y) 100 graduate students through the consumption of coffee (M).

Note that we are intentionally creating a mediation effect here (because statistics is always more fun if we have something to find) and we do so below by creating M so that it is related to X and Y so that it is related to M. This creates the causal chain for our analysis to parse.

#setwd("user location") #Working directory
set.seed(123) #Standardizes the numbers generated by rnorm; see Chapter 5
N <- 100 #Number of participants; graduate students
X.hours <- rnorm(N, 175, 7) #IV; hours since dawn
M.coffee <- 0.7*X.hours + rnorm(N, 0, 5) #Suspected mediator; coffee consumption 
Y.wake <- 0.4*M.coffee + rnorm(N, 0, 5) #DV; wakefulness
Meddata <- data.frame(X.hours, M.coffee, Y.wake)

6.1 Method 1: Baron & Kenny

This is the original 4-step method used to describe a mediation effect. Steps 1 and 2 use basic linear regression while steps 3 and 4 use multiple regression.

Now, let’s apply the Baron & Kenny method to our data:

The Steps:

  1. Estimate the relationship between X on Y (hours since dawn on degree of wakefulness)
    • Path “c” must be significantly different from 0; must have a total effect between the IV & DV
    • Y ~ X = SIGNIFICANT
  2. Estimate the relationship between X on M (hours since dawn on coffee consumption)
    • Path “a” must be significantly different from 0; IV and mediator must be related.
    • M ~ X = SIGNIFICANT
  3. Estimate the relationship between M on Y controlling for X (coffee consumption on wakefulness, controlling for hours since dawn)
    • Path “b” must be significantly different from 0; mediator and DV must be related.
    • Y ~ M + X = SIGNIFICANT
  4. Estimate the relationship between Y on X controlling for M (wakefulness on hours since dawn, controlling for coffee consumption)
    • Should be non-significant and nearly 0.
    • X ~ Y + M = NON-SIGNIFICANT
#1. Total Effect
fit <- lm(Y.wake ~ X.hours, data=Meddata)

#2. Path A (X on M)
fita <- lm(M.coffee ~ X.hours, data=Meddata)

#3. Path B (M on Y, controlling for X)
fitb <- lm(Y.wake ~ M.coffee + X.hours, data=Meddata)

#4. Reversed Path C (Y on X, controlling for M)
fitc <- lm(X.hours ~ Y.wake + M.coffee, data=Meddata)
#Summary Table
stargazer::stargazer(fit, fita, fitb, fitc, type="html", digits = 2, font.size = "footnotesize",title = "Baron and Kenny Method")
Baron and Kenny Method
Dependent variable:
Y.wake M.coffee Y.wake X.hours
(1) (2) (3) (4)
Y.wake -0.11
(0.10)
M.coffee 0.42*** 0.70***
(0.10) (0.08)
X.hours 0.17** 0.66*** -0.11
(0.08) (0.08) (0.10)
Constant 19.88 6.04 17.32 96.11***
(14.26) (13.42) (13.16) (9.28)
Observations 100 100 100 100
R2 0.04 0.43 0.19 0.44
Adjusted R2 0.03 0.43 0.18 0.43
Residual Std. Error 5.16 (df = 98) 4.85 (df = 98) 4.76 (df = 97) 4.82 (df = 97)
F Statistic 4.34** (df = 1; 98) 75.31*** (df = 1; 98) 11.72*** (df = 2; 97) 38.39*** (df = 2; 97)
Note: p<0.1; p<0.05; p<0.01

6.1.1 Interpreting Baron & Kenny Results

  • Here we find that our total effect model shows a significant positive relationship between hours since dawn (X) and wakefulness (Y).
  • Our Path A model shows that hours since dawn (X) is also positively related to coffee consumption (M).
  • Our Path B model then shows that coffee consumption (M) positively predicts wakefulness (Y) when controlling for hours since dawn (X).
  • Finally, wakefulness (Y) does not predict hours since dawn (X) when controlling for coffee consumption (M).

Since the relationship between hours since dawn and wakefulness is no longer significant when controlling for coffee consumption, this suggests that coffee consumption does in fact mediate this relationship. However, this method alone does not allow for a formal test of the indirect effect so we don’t know if the change in this relationship is truly meaningful.

There are two primary methods for formally testing the significance of the indirect test: the Sobel test & bootstrapping (covered under the mediation method). These are considered outdated, so we’re going to move onto our preferred method.

6.2 Method 2: The Mediation Package Method

This package uses the more recent bootstrapping method of Preacher & Hayes (2004) to address the power limitations of the Sobel Test. This method computes the point estimate of the indirect effect (ab) over a large number of random sample (typically 1000) so it does not assume that the data are normally distributed and is especially more suitable for small sample sizes than the Baron & Kenny method.

To run the mediate function, we will again need a model of our IV (hours since dawn), predicting our mediator (coffee consumption) like our Path A model above. We will also need a model of the direct effect of our IV (hours since dawn) on our DV (wakefulness), when controlling for our mediator (coffee consumption). When can then use mediate to repeatedly simulate a comparsion between these models and to test the significance of the indirect effect of coffee consumption.

# Path A
fitM <- lm(M.coffee ~ X.hours,     data=Meddata) #IV on M; Hours since dawn predicting coffee consumption

# Total
fitY <- lm(Y.wake ~ X.hours + M.coffee, data=Meddata) #IV and M on DV; Hours since dawn and coffee predicting wakefulness

# Check skew
gvlma(fitM) #data is positively skewed; could log transform

Call:
lm(formula = M.coffee ~ X.hours, data = Meddata)

Coefficients:
(Intercept)      X.hours  
     6.0449       0.6625  


ASSESSMENT OF THE LINEAR MODEL ASSUMPTIONS
USING THE GLOBAL TEST ON 4 DEGREES-OF-FREEDOM:
Level of Significance =  0.05 

Call:
 gvlma(x = fitM) 

                   Value p-value                   Decision
Global Stat        8.833 0.06542    Assumptions acceptable.
Skewness           6.314 0.01198 Assumptions NOT satisfied!
Kurtosis           1.219 0.26949    Assumptions acceptable.
Link Function      1.076 0.29959    Assumptions acceptable.
Heteroscedasticity 0.223 0.63674    Assumptions acceptable.
gvlma(fitY)

Call:
lm(formula = Y.wake ~ X.hours + M.coffee, data = Meddata)

Coefficients:
(Intercept)      X.hours     M.coffee  
    17.3218      -0.1118       0.4238  


ASSESSMENT OF THE LINEAR MODEL ASSUMPTIONS
USING THE GLOBAL TEST ON 4 DEGREES-OF-FREEDOM:
Level of Significance =  0.05 

Call:
 gvlma(x = fitY) 

                     Value p-value                Decision
Global Stat        3.41844  0.4904 Assumptions acceptable.
Skewness           1.85648  0.1730 Assumptions acceptable.
Kurtosis           0.77788  0.3778 Assumptions acceptable.
Link Function      0.71512  0.3977 Assumptions acceptable.
Heteroscedasticity 0.06896  0.7929 Assumptions acceptable.
fitMed <- mediate(fitM, fitY, treat="X.hours", mediator="M.coffee")
summary(fitMed)

Causal Mediation Analysis 

Quasi-Bayesian Confidence Intervals

               Estimate 95% CI Lower 95% CI Upper             p-value    
ACME             0.2808       0.1437         0.42 <0.0000000000000002 ***
ADE             -0.1133      -0.3116         0.09               0.258    
Total Effect     0.1674       0.0208         0.34               0.028 *  
Prop. Mediated   1.6428       0.5631         8.44               0.028 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Sample Size Used: 100 


Simulations: 1000 
plot(fitMed)

#Bootstrap
fitMedBoot <- mediate(fitM, fitY, boot=TRUE, sims=2000, treat="X.hours", mediator="M.coffee")
summary(fitMedBoot)

Causal Mediation Analysis 

Nonparametric Bootstrap Confidence Intervals with the Percentile Method

                Estimate 95% CI Lower 95% CI Upper             p-value    
ACME            0.280784     0.136681         0.43 <0.0000000000000002 ***
ADE            -0.111790    -0.288539         0.09               0.254    
Total Effect    0.168993    -0.000457         0.34               0.051 .  
Prop. Mediated  1.661509    -1.369638         9.83               0.051 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Sample Size Used: 100 


Simulations: 2000 
plot(fitMedBoot)

6.2.1 Interpreting Mediation Results

The mediate function gives us:

  • Average Causal Mediation Effects (ACME)
  • Average Direct Effects (ADE), our combined indirect and direct effects (Total Effect)
  • the ratio of these estimates (Prop. Mediated)

The ACME here is the indirect effect of M (total effect - direct effect) and thus this value tells us if our mediation effect is significant. If ACME is significant, but ADE is not, then we have full mediation. If ADE is significant, but ACME is not, then we have no mediation.

In this case, our fitMed model again shows a significant effect of coffee consumption on the relationship between hours since dawn and feelings of wakefulness, (ACME = .28, p < .001) with no direct effect of hours since dawn (ADE = -0.11, p = .27) and significant total effect (p < .05).

We can then bootstrap this comparison to verify this result in fitMedBoot and again find a significant mediation effect (ACME = .28, p < .001) and no direct effect of hours since dawn (ADE = -0.11, p = .27). However, with increased power, this analysis no longer shows a significant total effect (p = .07).

6.3 Method 3: Mediation using lavaan

library(lavaan)

model <-"
Y.wake ~ a*X.hours + b*M.coffee
M.coffee ~ c*X.hours

#indirect effect
ind:=b*c

#total effect
total:=a+(b*c)
"
medsem<-sem(model = model, data = Meddata, se="bootstrap")

summary(medsem,standardized=T,fit.measures=T,rsquare=T)
lavaan 0.6.17 ended normally after 1 iteration

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                         5

  Number of observations                           100

Model Test User Model:
                                                      
  Test statistic                                 0.000
  Degrees of freedom                                 0

Model Test Baseline Model:

  Test statistic                                78.650
  Degrees of freedom                                 3
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    1.000
  Tucker-Lewis Index (TLI)                       1.000

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)               -595.177
  Loglikelihood unrestricted model (H1)       -595.177
                                                      
  Akaike (AIC)                                1200.354
  Bayesian (BIC)                              1213.380
  Sample-size adjusted Bayesian (SABIC)       1197.589

Root Mean Square Error of Approximation:

  RMSEA                                          0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: RMSEA <= 0.050                       NA
  P-value H_0: RMSEA >= 0.080                       NA

Standardized Root Mean Square Residual:

  SRMR                                           0.000

Parameter Estimates:

  Standard errors                            Bootstrap
  Number of requested bootstrap draws             1000
  Number of successful bootstrap draws             999

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  Y.wake ~                                                              
    X.hours    (a)   -0.112    0.102   -1.101    0.271   -0.112   -0.136
    M.coffee   (b)    0.424    0.102    4.153    0.000    0.424    0.519
  M.coffee ~                                                            
    X.hours    (c)    0.663    0.075    8.823    0.000    0.663    0.659

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Y.wake           21.945    2.699    8.130    0.000   21.945    0.805
   .M.coffee         23.086    3.567    6.472    0.000   23.086    0.565

R-Square:
                   Estimate
    Y.wake            0.195
    M.coffee          0.435

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    ind               0.281    0.075    3.762    0.000    0.281    0.342
    total             0.169    0.092    1.836    0.066    0.169    0.206
semPaths(object = medsem, whatLabels = "par") # "std" = standardized parameter estimates

mediationPlot(medsem, indirect = TRUE, whatLabels = "est")


7 Other types of mediation

7.1 Mediation with Categorical Predictors

When you have categorical predictors with more than 2 levels, you will need to model the mediation effects for each level of that variable. To do that, we’re going to dummy code our categorical variable. We’re going to use the same dataset but create a 3-level predictor: a lot of sleep, average sleep, little sleep. I know this would be considered ordinal, but let’s ignore that for now.

IMPORTANT NOTES:

  • When dummy coding a categorical variable with K categories, include only K−1 dummy variables in the model, treating one category as a reference. This avoids the issue of perfect multicollinearity.
    • If all dummy-coded variables for a categorical predictor are included in the model, this violates the assumption of independence because the sum of all dummy variables for a given observation is always 1 (if you include a dummy for each category). In other words, you’ll get perfect multicollinearity, where one variable can be perfectly predicted from the others, leading to issues in estimating the model parameters.
    • The interpretation of these coefficients are now in reference to the excluded dummy (i.e., the reference category).

7.1.0.1 Lavaan

Here, the reference group will be the people who got ‘a lot’ of sleep (sleep=0)

# creating our dataset
set.seed(123)
N <- 100 

# sleep: 0 = a lot, 1 = average, 2 = a little
X.sleep <- sample(0:2, N, replace = TRUE) 
sleep_effect <- -2 
M.coffee <- 0.7 * X.hours + sleep_effect * X.sleep + rnorm(N, 0, 5)
Y.wake <- 0.4 * M.coffee + rnorm(N, 0, 5) 
Meddata_cat <- data.frame(X.hours, X.sleep, M.coffee, Y.wake)
Meddata_cat$sleep <- factor(Meddata_cat$X.sleep)

# dummy code sleep
library(fastDummies)

Meddata_cat <- dummy_cols(Meddata_cat, 
                   select_columns = "X.sleep")

# 'a lot' of sleep is the reference category, so you can omit X.sleep_0 from the model
model_cat <-"
# Direct Effects on wakefulness
Y.wake ~ a1*X.sleep_1 + a2*X.sleep_2 + b*M.coffee

# Effect of sleep on coffee (note: now we use only two dummies, assuming one as reference)
M.coffee ~ c1*X.sleep_1 + c2*X.sleep_2

# Indirect effects (now must include dummy paths for only two levels)
ind1:=b*c1
ind2:=b*c2

# Total effects
total1 := a1 + (b*c1)
total2 := a2 + (b*c2)
"

med_cat<-sem(model = model_cat, data = Meddata_cat)

summary(med_cat,standardized=T,fit.measures=T,rsquare=T)
lavaan 0.6.17 ended normally after 1 iteration

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                         7

  Number of observations                           100

Model Test User Model:
                                                      
  Test statistic                                 0.000
  Degrees of freedom                                 0

Model Test Baseline Model:

  Test statistic                                49.270
  Degrees of freedom                                 5
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    1.000
  Tucker-Lewis Index (TLI)                       1.000

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)               -628.077
  Loglikelihood unrestricted model (H1)       -628.077
                                                      
  Akaike (AIC)                                1270.155
  Bayesian (BIC)                              1288.391
  Sample-size adjusted Bayesian (SABIC)       1266.283

Root Mean Square Error of Approximation:

  RMSEA                                          0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: RMSEA <= 0.050                       NA
  P-value H_0: RMSEA >= 0.080                       NA

Standardized Root Mean Square Residual:

  SRMR                                           0.000

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  Y.wake ~                                                              
    X.sleep_1 (a1)   -0.491    1.160   -0.423    0.672   -0.491   -0.042
    X.sleep_2 (a2)   -0.936    1.208   -0.775    0.438   -0.936   -0.081
    M.coffee   (b)    0.383    0.070    5.481    0.000    0.383    0.503
  M.coffee ~                                                            
    X.sleep_1 (c1)   -0.180    1.660   -0.109    0.913   -0.180   -0.012
    X.sleep_2 (c2)   -5.957    1.624   -3.669    0.000   -5.957   -0.392

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Y.wake           21.848    3.090    7.071    0.000   21.848    0.718
   .M.coffee         44.776    6.332    7.071    0.000   44.776    0.851

R-Square:
                   Estimate
    Y.wake            0.282
    M.coffee          0.149

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    ind1             -0.069    0.636   -0.109    0.913   -0.069   -0.006
    ind2             -2.281    0.748   -3.049    0.002   -2.281   -0.197
    total1           -0.560    1.322   -0.423    0.672   -0.560   -0.047
    total2           -3.217    1.293   -2.487    0.013   -3.217   -0.278
semPaths(object = med_cat, whatLabels = "par") 

7.1.0.2 Mediation package

The process is similar when using the mediation, but note that you’ll need to run separate mediations for each level.

# Model predicting mediator (coffee)
model_mediator <- lm(M.coffee ~ X.sleep_1 + X.sleep_2, data = Meddata_cat)

# Model predicting outcome (wake), including mediator
model_outcome <- lm(Y.wake ~ X.sleep_1 + X.sleep_2 + M.coffee, data = Meddata_cat)

# average sleep vs a lot of sleep
med.1 <- mediate(model_mediator, model_outcome, treat = "X.sleep_1", mediator = "M.coffee")
summary(med.1)

Causal Mediation Analysis 

Quasi-Bayesian Confidence Intervals

               Estimate 95% CI Lower 95% CI Upper p-value
ACME            -0.0714      -1.4659         1.19    0.93
ADE             -0.4922      -2.8570         1.79    0.67
Total Effect    -0.5636      -3.1142         2.05    0.66
Prop. Mediated   0.2188      -3.1517         4.22    0.68

Sample Size Used: 100 


Simulations: 1000 
# little sleep vs a lot of sleep
med.2 <- mediate(model_mediator, model_outcome, treat = "X.sleep_2", mediator = "M.coffee")
summary(med.2)

Causal Mediation Analysis 

Quasi-Bayesian Confidence Intervals

               Estimate 95% CI Lower 95% CI Upper             p-value    
ACME             -2.283       -3.946        -0.97 <0.0000000000000002 ***
ADE              -0.946       -3.331         1.51                0.46    
Total Effect     -3.228       -5.672        -0.76                0.01 ** 
Prop. Mediated    0.712        0.276         2.29                0.01 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Sample Size Used: 100 


Simulations: 1000 

7.2 Parallel Mediation

Meddata$M2.activity <- 2.1 * Meddata$X.hours + rnorm(nrow(Meddata), mean = 0, sd = 1)

summary(lm(Y.wake ~ M2.activity, data = Meddata))

Call:
lm(formula = Y.wake ~ M2.activity, data = Meddata)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.7903  -3.7035  -0.2119   2.8524  12.5561 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept) 19.01365   14.47448   1.314   0.1920  
M2.activity  0.08282    0.03921   2.112   0.0372 *
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 5.157 on 98 degrees of freedom
Multiple R-squared:  0.04353,   Adjusted R-squared:  0.03377 
F-statistic: 4.461 on 1 and 98 DF,  p-value: 0.03723
# Define the model for the outcome variable
parallelModel <- lm(Y.wake ~ X.hours + M.coffee + M2.activity, data = Meddata)

# Define the models for the mediator variables
medModel_coffee <- lm(M.coffee ~ X.hours, data = Meddata)
medModel_activity <- lm(M2.activity ~ X.hours, data = Meddata)

# Perform the mediation analysis for each mediator
mediation_coffee <- mediate(medModel_coffee, parallelModel, 
                                treat = "X.hours", mediator = "M.coffee", 
                                boot = TRUE, sims = 500)

mediation_activity <- mediate(medModel_activity, parallelModel, 
                           treat = "X.hours", mediator = "M2.activity", 
                           boot = TRUE, sims = 500)

# Summarize the mediation analysis results
summary(mediation_coffee)

Causal Mediation Analysis 

Nonparametric Bootstrap Confidence Intervals with the Percentile Method

               Estimate 95% CI Lower 95% CI Upper             p-value    
ACME              0.280        0.141         0.42 <0.0000000000000002 ***
ADE              -0.394       -2.568         1.53                0.73    
Total Effect     -0.114       -2.277         1.84                0.86    
Prop. Mediated   -2.458       -3.849         2.55                0.86    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Sample Size Used: 100 


Simulations: 500 
summary(mediation_activity)

Causal Mediation Analysis 

Nonparametric Bootstrap Confidence Intervals with the Percentile Method

               Estimate 95% CI Lower 95% CI Upper p-value
ACME              0.283       -1.627         2.29    0.80
ADE              -0.394       -2.459         1.49    0.71
Total Effect     -0.111       -0.295         0.08    0.22
Prop. Mediated   -2.553     -104.088        46.85    0.87

Sample Size Used: 100 


Simulations: 500 
multipleMediation <- '

# Direct
Y.wake ~ b1 * M.coffee + b2 * M2.activity + c * X.hours
M.coffee ~ a1 * X.hours
M2.activity ~ a2 * X.hours

# Indirect
indirect1 := a1 * b1
indirect2 := a2 * b2

# Total
total := c + (a1 * b1) + (a2 * b2)

# Covariance between mediators
M.coffee ~~ M2.activity
'
fit_mult <- sem(model = multipleMediation, data = Meddata)
summary(fit_mult, fit.measures = TRUE, standardized = TRUE)
lavaan 0.6.17 ended normally after 15 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                         9

  Number of observations                           100

Model Test User Model:
                                                      
  Test statistic                                 0.000
  Degrees of freedom                                 0

Model Test Baseline Model:

  Test statistic                               605.568
  Degrees of freedom                                 6
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    1.000
  Tucker-Lewis Index (TLI)                       1.000

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)               -731.262
  Loglikelihood unrestricted model (H1)       -731.262
                                                      
  Akaike (AIC)                                1480.523
  Bayesian (BIC)                              1503.970
  Sample-size adjusted Bayesian (SABIC)       1475.545

Root Mean Square Error of Approximation:

  RMSEA                                          0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: RMSEA <= 0.050                       NA
  P-value H_0: RMSEA >= 0.080                       NA

Standardized Root Mean Square Residual:

  SRMR                                           0.000

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  Y.wake ~                                                              
    M.coffee  (b1)    0.422    0.098    4.326    0.000    0.422    0.517
    M2.actvty (b2)    0.137    0.496    0.276    0.782    0.137    0.345
    X.hours    (c)   -0.394    1.025   -0.384    0.701   -0.394   -0.479
  M.coffee ~                                                            
    X.hours   (a1)    0.663    0.076    8.766    0.000    0.663    0.659
  M2.activity ~                                                         
    X.hours   (a2)    2.063    0.015  138.745    0.000    2.063    0.997

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
 .M.coffee ~~                                                           
   .M2.activity       0.254    0.455    0.559    0.576    0.254    0.056

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Y.wake           21.929    3.101    7.071    0.000   21.929    0.805
   .M.coffee         23.086    3.265    7.071    0.000   23.086    0.565
   .M2.activity       0.894    0.126    7.071    0.000    0.894    0.005

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    indirect1         0.280    0.072    3.880    0.000    0.280    0.341
    indirect2         0.283    1.024    0.276    0.782    0.283    0.344
    total             0.169    0.080    2.103    0.035    0.169    0.206
multMed<-sem(model = multipleMediation, data = Meddata, se="bootstrap")

summary(multMed,standardized=T,fit.measures=T,rsquare=T)
lavaan 0.6.17 ended normally after 15 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                         9

  Number of observations                           100

Model Test User Model:
                                                      
  Test statistic                                 0.000
  Degrees of freedom                                 0

Model Test Baseline Model:

  Test statistic                               605.568
  Degrees of freedom                                 6
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    1.000
  Tucker-Lewis Index (TLI)                       1.000

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)               -731.262
  Loglikelihood unrestricted model (H1)       -731.262
                                                      
  Akaike (AIC)                                1480.523
  Bayesian (BIC)                              1503.970
  Sample-size adjusted Bayesian (SABIC)       1475.545

Root Mean Square Error of Approximation:

  RMSEA                                          0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: RMSEA <= 0.050                       NA
  P-value H_0: RMSEA >= 0.080                       NA

Standardized Root Mean Square Residual:

  SRMR                                           0.000

Parameter Estimates:

  Standard errors                            Bootstrap
  Number of requested bootstrap draws             1000
  Number of successful bootstrap draws             974

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  Y.wake ~                                                              
    M.coffee  (b1)    0.422    0.099    4.245    0.000    0.422    0.517
    M2.actvty (b2)    0.137    0.529    0.259    0.796    0.137    0.345
    X.hours    (c)   -0.394    1.097   -0.359    0.720   -0.394   -0.479
  M.coffee ~                                                            
    X.hours   (a1)    0.663    0.074    8.968    0.000    0.663    0.659
  M2.activity ~                                                         
    X.hours   (a2)    2.063    0.013  164.862    0.000    2.063    0.997

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
 .M.coffee ~~                                                           
   .M2.activity       0.254    0.440    0.578    0.563    0.254    0.056

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Y.wake           21.929    2.655    8.259    0.000   21.929    0.805
   .M.coffee         23.086    3.552    6.500    0.000   23.086    0.565
   .M2.activity       0.894    0.141    6.327    0.000    0.894    0.005

R-Square:
                   Estimate
    Y.wake            0.195
    M.coffee          0.435
    M2.activity       0.995

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    indirect1         0.280    0.074    3.782    0.000    0.280    0.341
    indirect2         0.283    1.092    0.259    0.796    0.283    0.344
    total             0.169    0.093    1.810    0.070    0.169    0.206
semPaths(object = multMed, whatLabels = "std")

mediationPlot(multMed, indirect = TRUE, whatLabels = "par", secondIndirect = TRUE) #standardized estimates

# Generate random ages between 18 and 65
Meddata$age <- round(runif(N, min = 18, max = 65))

covMediation <- '
# Direct paths
M.coffee ~ a1 * X.hours + age
M2.activity ~ a2 * X.hours + age
Y.wake ~ b1 * M.coffee + b2 * M2.activity + c * X.hours + age

# Covariance between mediators
M.coffee ~~ M2.activity

# Indirect effects
indirect1 := a1 * b1
indirect2 := a2 * b2

# Total effect
total := c + (a1 * b1) + (a2 * b2)
'

fit_cov <- sem(model = covMediation, data = Meddata)

summary(fit_cov, fit.measures = TRUE, standardized = TRUE)  # Adds fit measures and standardized estimates
lavaan 0.6.17 ended normally after 20 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                        12

  Number of observations                           100

Model Test User Model:
                                                      
  Test statistic                                 0.000
  Degrees of freedom                                 0

Model Test Baseline Model:

  Test statistic                               606.331
  Degrees of freedom                                 9
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    1.000
  Tucker-Lewis Index (TLI)                       1.000

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)               -730.880
  Loglikelihood unrestricted model (H1)       -730.880
                                                      
  Akaike (AIC)                                1485.760
  Bayesian (BIC)                              1517.022
  Sample-size adjusted Bayesian (SABIC)       1479.123

Root Mean Square Error of Approximation:

  RMSEA                                          0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: RMSEA <= 0.050                       NA
  P-value H_0: RMSEA >= 0.080                       NA

Standardized Root Mean Square Residual:

  SRMR                                           0.000

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  M.coffee ~                                                            
    X.hours   (a1)    0.662    0.076    8.747    0.000    0.662    0.658
    age              -0.008    0.039   -0.212    0.832   -0.008   -0.016
  M2.activity ~                                                         
    X.hours   (a2)    2.063    0.015  138.629    0.000    2.063    0.998
    age               0.002    0.008    0.248    0.804    0.002    0.002
  Y.wake ~                                                              
    M.coffee  (b1)    0.424    0.097    4.357    0.000    0.424    0.519
    M2.actvty (b2)    0.127    0.495    0.256    0.798    0.127    0.319
    X.hours    (c)   -0.370    1.022   -0.363    0.717   -0.370   -0.451
    age               0.030    0.037    0.808    0.419    0.030    0.072

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
 .M.coffee ~~                                                           
   .M2.activity       0.257    0.455    0.565    0.572    0.257    0.057

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .M.coffee         23.076    3.263    7.071    0.000   23.076    0.565
   .M2.activity       0.893    0.126    7.071    0.000    0.893    0.005
   .Y.wake           21.787    3.081    7.071    0.000   21.787    0.800

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    indirect1         0.281    0.072    3.900    0.000    0.281    0.342
    indirect2         0.261    1.021    0.256    0.798    0.261    0.318
    total             0.172    0.080    2.138    0.033    0.172    0.209
covMed<-sem(model = covMediation, data = Meddata, se="bootstrap")

semPaths(object = covMed, whatLabels = "std")

7.3 Mediation with binary outcomes

Lavaan automatically detects binary DVs. There’s no need to add any special arguments.

Create binary DV: Pass or fail

pass_threshold <- median(Meddata$Y.wake)

# Create binary DV 'pass_fail', where 1 = pass, 0 = fail
Meddata$pass_fail <- ifelse(Meddata$Y.wake > pass_threshold, 1, 0)
binaryMediation <- '
# Direct paths to mediators
M.coffee ~ a1 * X.hours + age
M2.activity ~ a2 * X.hours + age

# Binary outcome path using logit
pass_fail ~ b1 * M.coffee + b2 * M2.activity + c * X.hours + age

# Covariance between mediators
M.coffee ~~ M2.activity

# Indirect effects
indirect1 := a1 * b1
indirect2 := a2 * b2

# Total effect
total := c + (a1 * b1) + (a2 * b2)
'

# Fitting the model with lavaan
fitBinary <- sem(model = binaryMediation, data = Meddata, estimator = "ML", se = "bootstrap", bootstrap = 1000)

summary(fitBinary, fit.measures = TRUE, standardized = TRUE)
lavaan 0.6.17 ended normally after 37 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                        12

  Number of observations                           100

Model Test User Model:
                                                      
  Test statistic                                 0.000
  Degrees of freedom                                 0

Model Test Baseline Model:

  Test statistic                               598.365
  Degrees of freedom                                 9
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    1.000
  Tucker-Lewis Index (TLI)                       1.000

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)               -500.302
  Loglikelihood unrestricted model (H1)       -500.302
                                                      
  Akaike (AIC)                                1024.604
  Bayesian (BIC)                              1055.866
  Sample-size adjusted Bayesian (SABIC)       1017.967

Root Mean Square Error of Approximation:

  RMSEA                                          0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: RMSEA <= 0.050                       NA
  P-value H_0: RMSEA >= 0.080                       NA

Standardized Root Mean Square Residual:

  SRMR                                           0.000

Parameter Estimates:

  Standard errors                            Bootstrap
  Number of requested bootstrap draws             1000
  Number of successful bootstrap draws            1000

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  M.coffee ~                                                            
    X.hours   (a1)    0.662    0.075    8.806    0.000    0.662    0.658
    age              -0.008    0.039   -0.209    0.835   -0.008   -0.016
  M2.activity ~                                                         
    X.hours   (a2)    2.063    0.012  167.090    0.000    2.063    0.998
    age               0.002    0.009    0.217    0.829    0.002    0.002
  pass_fail ~                                                           
    M.coffee  (b1)    0.030    0.009    3.221    0.001    0.030    0.386
    M2.actvty (b2)    0.024    0.057    0.420    0.674    0.024    0.630
    X.hours    (c)   -0.055    0.118   -0.468    0.640   -0.055   -0.701
    age               0.005    0.004    1.394    0.163    0.005    0.130

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
 .M.coffee ~~                                                           
   .M2.activity       0.257    0.423    0.608    0.544    0.257    0.057

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .M.coffee         23.076    3.691    6.251    0.000   23.076    0.565
   .M2.activity       0.893    0.141    6.318    0.000    0.893    0.005
   .pass_fail         0.216    0.016   13.747    0.000    0.216    0.866

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    indirect1         0.020    0.007    3.066    0.002    0.020    0.254
    indirect2         0.049    0.118    0.420    0.674    0.049    0.628
    total             0.014    0.008    1.848    0.065    0.014    0.182
binMed<-sem(model = fitBinary, data = Meddata, se="bootstrap")

# basic
semPaths(object = binMed, whatLabels = "std")

# change shape and label size
binSem <- semPaths(binMed, 
         whatLabels = "std", 
         edge.label.cex = 0.75,  
         label.cex = 1.5,       
         layout = "spring")

Add labels

# change node label
binMed_lab <- change_node_label(binSem,
                           c(X.h = "Hours",
                             M.c = "Coffee",
                             M2. = "Activity",
                             ps_ = "Grade"),
                           label.cex = 1.1)
plot(binMed_lab)

Indicate which effects are significant

sig_plot <- binMed_lab %>%      
  mark_sig(fitBinary) %>% 
  mark_se(fitBinary, sep = "\n")

plot(sig_plot)

7.4 Moderated mediation

credit: https://ademos.people.uic.edu/Chapter15.html#31_describe_the_dataset

These are challenging to understand, so we’re going to walk through a different example.

Let’s examine the question: does spending more time in grad school predict more offers after graduation? We’ll see if this relationship is explained by number of publications (i.e., the more time spent in grad school, the more publications one has, and the more publications one has, the more job offers they get). However, this causal chain may only work for people who spend their time in graduate school networking.

We are going to simulate a dataset that measured the following:

  • X = Time spent in graduate school (we will change the name to “time” when we create the data frame)
  • Z = Time spent networking
  • M = Number of publications in grad school
  • Y = Number of job offers
### We are intentionally creating a moderated mediation effect here and we do so below by setting the relationships (the paths) between our causal chain variables and setting the relationships for our interaction terms

set.seed(42) #This makes sure that everyone gets the same numbers generated through rnorm function

a1 = -.59 #Set the path a1 strength (effect of X on M)

a2 = -.17 #Set path a2 strength (effect of Z on M)

a3 = .29 #Set path a3 strength (interaction between X and Z on M)

b = .59 #Set path b strength (effect of M on Y)

cdash1 = .27 #Set path c'1 strength (effect of X on Y)

cdash2 = .01 #Set path c'2 strength (effect of Z on Y)

cdash3 = -.01 #Set path c'3 strength (interaction betwee X and Z on Y)

### Here we are creating the values of our variables for each subject

n <- 200 #Set sample size

X <- rnorm(n, 7, 1) #IV: Time spent in grad school (M = 7, SD = 1)

Z <- rnorm(n, 5, 1) #Moderator: Time spent (hours per week) with Professor Demos in class or in office hours (M = 5, SD = 1)

M <- a1*X + a2*Z + a3*X*Z + rnorm(n, 0, .1) #Mediator: Number of publications in grad school
#The mediator variable is created as a function of the IV, moderator, and their interaction with some random noise thrown in the mix

Y <- cdash1*X + cdash2*Z + cdash3*X*Z + b*M + rnorm(n, 0, .1) #DV: Number of job offers
#Similar to the mediator, the DV is a function of the IV, moderator, their interaction, and the mediator with some random noise thrown in the mix

Success.ModMed <- data.frame(jobs = Y, time = X, pubs = M, network = Z) #Build our data frame and give it recognizable variable names

Because we have interaction terms in our regression analyses, we need to mean center our IV and Moderator (Z)

Success.ModMed$time.c <- scale(Success.ModMed$time, center = TRUE, scale = FALSE)[,] #Scale returns a matrix so we have to make it a vector by indexing one column

Success.ModMed$network.c <- scale(Success.ModMed$network, center = TRUE, scale = FALSE)[,]
Mod.Med.Lavaan <- '
#Regressions
#These are the same regression equations from our previous example
#Except in this code we are naming the coefficients that are produced from the regression equations
#E.g., the regression coefficient for the effect of time on pubs is named "a1"
pubs ~ a1*time.c + a2*network.c + a3*time.c:network.c
jobs ~ cdash1*time.c + cdash2*network.c + cdash3*time.c:network.c + b1*pubs

#Mean of centered network (for use in simple slopes)
#This is making a coefficient labeled "network.c.mean" which equals the intercept because of the "1"
#(Y~1) gives you the intercept, which is the mean for our network.c variable
network.c ~ network.c.mean*1

#Variance of centered network (for use in simple slopes)
#This is making a coefficient labeled "network.c.var" which equals the variance 
#Two tildes separating the same variable gives you the variance
network.c ~~ network.c.var*network.c

#Indirect effects conditional on moderator (a1 + a3*ModValue)*b1
indirect.SDbelow := (a1 + a3*(network.c.mean-sqrt(network.c.var)))*b1 
indirect.SDabove := (a1 + a3*(network.c.mean+sqrt(network.c.var)))*b1

#Direct effects conditional on moderator (cdash1 + cdash3*ModValue)
#We have to do it this way because you cannot call the mean and sd functions in lavaan package
direct.SDbelow := cdash1 + cdash3*(network.c.mean-sqrt(network.c.var)) 
direct.SDabove := cdash1 + cdash3*(network.c.mean+sqrt(network.c.var))

#Total effects conditional on moderator
total.SDbelow := direct.SDbelow + indirect.SDbelow
total.SDabove := direct.SDabove + indirect.SDabove

#Proportion mediated conditional on moderator
#To match the output of "mediate" package
prop.mediated.SDbelow := indirect.SDbelow / total.SDbelow
prop.mediated.SDabove := indirect.SDabove / total.SDabove

#Index of moderated mediation
#An alternative way of testing if conditional indirect effects are significantly different from each other
index.mod.med := a3*b1
'

#Fit model
Mod.Med.SEM <- sem(model = Mod.Med.Lavaan,
                   data = Success.ModMed,
                   se = "bootstrap",
                   bootstrap = 10,
                   fixed.x = FALSE) # pubs should model the variance

#Fit measures
summary(Mod.Med.SEM,
        fit.measures = FALSE,
        standardize = TRUE,
        rsquare = TRUE)
lavaan 0.6.17 ended normally after 51 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                        18

  Number of observations                           200

Model Test User Model:
                                                      
  Test statistic                                12.599
  Degrees of freedom                                 2
  P-value (Chi-square)                           0.002

Parameter Estimates:

  Standard errors                            Bootstrap
  Number of requested bootstrap draws               10
  Number of successful bootstrap draws              10

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  pubs ~                                                                
    time.c    (a1)    0.858    0.006  138.664    0.000    0.858    0.425
    ntwrk.c   (a2)    1.854    0.006  309.044    0.000    1.854    0.892
    tm.c:n.   (a3)    0.309    0.010   30.402    0.000    0.309    0.149
  jobs ~                                                                
    time.c  (cds1)    0.219    0.051    4.339    0.000    0.219    0.175
    ntwrk.c (cds2)   -0.048    0.104   -0.462    0.644   -0.048   -0.037
    tm.c:n. (cds3)   -0.009    0.017   -0.556    0.578   -0.009   -0.007
    pubs      (b1)    0.584    0.057   10.308    0.000    0.584    0.942

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  time.c ~~                                                             
    time.c:ntwrk.c   -0.008    0.127   -0.062    0.951   -0.008   -0.009

Intercepts:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    ntwrk.c (nt..)   -0.000    0.084   -0.000    1.000   -0.000   -0.000
   .pubs              5.163    0.007  723.506    0.000    5.163    2.629
   .jobs              1.602    0.292    5.480    0.000    1.602    1.316
    time.c            0.000    0.091    0.000    1.000    0.000    0.000
    tm.c:n.          -0.074    0.041   -1.809    0.070   -0.074   -0.078

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    ntwrk.c (nt..)    0.892    0.050   17.959    0.000    0.892    1.000
   .pubs              0.010    0.001   11.043    0.000    0.010    0.003
   .jobs              0.009    0.001   11.307    0.000    0.009    0.006
    time.c            0.945    0.075   12.565    0.000    0.945    1.000
    tm.c:n.           0.889    0.136    6.513    0.000    0.889    1.000

R-Square:
                   Estimate
    pubs              0.997
    jobs              0.994

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    indirect.SDblw    0.330    0.036    9.182    0.000    0.330    0.260
    indirect.SDabv    0.672    0.061   11.090    0.000    0.672    0.540
    direct.SDbelow    0.228    0.040    5.704    0.000    0.228    0.182
    direct.SDabove    0.210    0.068    3.101    0.002    0.210    0.168
    total.SDbelow     0.559    0.014   41.333    0.000    0.559    0.443
    total.SDabove     0.882    0.017   51.107    0.000    0.882    0.708
    prp.mdtd.SDblw    0.591    0.067    8.771    0.000    0.591    0.588
    prop.mdtd.SDbv    0.762    0.075   10.201    0.000    0.762    0.763
    index.mod.med     0.181    0.019    9.693    0.000    0.181    0.140
  • pubs ~
    • A1 (M ~ X): Significant main effect of time spent in grad school on number of publications
    • A2 (M ~ Z): Significant main effect of time spent networking on number of publications
    • A3 (M ~ X * Z): Significant interaction between time spent in grad school and time spent networking on number of publications
  • jobs ~
    • (cds1) (Y ~ X): Significant main effect of time spent in grad school on number of job offers
    • (cds2) (Y ~ Z): No effect of time spent networking on number of job offers
    • (cds3) (Y ~ M): Significant main effect of number of publications on number of job offers
    • (b1) (Y ~ X * Z): No interaction between time spent in grad school and time spent networking on number of job offers

To look at the moderator effect, we can use the +/- 1SD from the mean

  • those who spend little time networking
    • (direct.SDblw) Significant direct effect of time spent in grad school on job offers (for those who don’t spend a lot of time networking)
    • (indirect.SDbelow) Significant indirect effect of time spent in grad school on job offers through publications (for those who don’t spend a lot of time networking)
  • those who spend a lot of time networking
    • (direct.SDabv) Significant direct effect of time spent in grad school on job offers (for those who spend a lot of time networking)
    • (indirect.SDabv) Significant indirect effect of time spent in grad school on job offers through publications (for those who spend a lot of time networking)
  • what does this mean?
    • Time spent in graduate school positively affects the number of job offers, both directly and indirectly through the number of publications. The strength of this indirect effect is depends on the amount of time spent networking.
    • Specifically, while the mediated path was significant regardless of networking time, the strength of the publications explaining the relationship between time spent in grad school and job offers was stronger for people who spent a lot of time networking.
mm_plot <- semPaths(Mod.Med.SEM, 
         whatLabels = "est", 
         edge.label.cex = 0.75,  
         label.cex = 1.5,       
         layout = "spring",
         intercepts = FALSE)

# change node label
Modmed_lab <- change_node_label(mm_plot,
                           c(jbs = "Y.Jobs",
                             pbs = "M.Pubs",
                             tm. = "X.Time",
                             nt. = "Z.Network",
                             't.:' = "T*N"),
                           label.cex = 1.1)

sig_plot_mm <- Modmed_lab %>%      
  mark_sig(Mod.Med.SEM) %>% 
  mark_se(Mod.Med.SEM, sep = "\n")

plot(sig_plot_mm)

#Bootstraps
parameterEstimates(Mod.Med.SEM,
                   boot.ci.type = "bca.simple",
                   level = .95, ci = TRUE,
                   standardized = FALSE)[c(19:27),c(4:10)] #We index the matrix to only display columns we are interested in
                   label   est    se      z pvalue ci.lower ci.upper
19      indirect.SDbelow 0.330 0.036  9.182  0.000    0.287    0.395
20      indirect.SDabove 0.672 0.061 11.090  0.000    0.571    0.749
21        direct.SDbelow 0.228 0.040  5.704  0.000    0.156    0.273
22        direct.SDabove 0.210 0.068  3.101  0.002    0.117    0.296
23         total.SDbelow 0.559 0.014 41.333  0.000    0.544    0.589
24         total.SDabove 0.882 0.017 51.107  0.000    0.864    0.921
25 prop.mediated.SDbelow 0.591 0.067  8.771  0.000    0.523    0.715
26 prop.mediated.SDabove 0.762 0.075 10.201  0.000    0.678    0.865
27         index.mod.med 0.181 0.019  9.693  0.000    0.155    0.208

The difference is most likely a result of bootstrap estimation differences (e.g., lavaan uses bias-corrected but not accelerated bootstrapping for their confidence intervals)

#install.packages("JSmediation")
library(JSmediation)

Success.ModMed$pubs.c <- scale(Success.ModMed$pubs, center = TRUE, scale = FALSE)[,]

moderated_mediation_fit <- 
  mdt_moderated(data = Success.ModMed,
                IV   = time.c,
                DV   = jobs, 
                M    = pubs.c,
                Mod  = network.c)

moderated_mediation_fit %>% add_index(stage = "first")
Test of mediation (moderated mediation)
==============================================

Variables:

- IV: time.c 
- DV: jobs 
- M: pubs.c 
- Mod: network.c 

Paths:

========  ==============  =====  =========================
Path      Point estimate     SE  APA                      
========  ==============  =====  =========================
a                  0.858  0.008  t(196) = 114.12, p < .001
a * Mod            0.309  0.008  t(196) = 38.91, p < .001 
b                  0.584  0.065  t(194) = 8.99, p < .001  
b * Mod           -0.003  0.003  t(194) = 1.20, p = .233  
c                  0.720  0.008  t(196) = 88.82, p < .001 
c * Mod            0.171  0.009  t(196) = 19.96, p < .001 
c'                 0.222  0.056  t(194) = 3.96, p < .001  
c' * Mod          -0.008  0.021  t(194) = 0.37, p = .709  
========  ==============  =====  =========================

Indirect effect index:

- type: Mediated moderation index (First stage) 
- point estimate: 0.18 
- confidence interval:
  - method: Monte Carlo (5000 iterations)
  - level: 0.05 
  - CI: [0.139; 0.221]

Fitted models:

- X * Mod -> Y 
- X * Mod -> M 
- (X + M) * Mod -> Y 
compute_indirect_effect_for(moderated_mediation_fit, Mod = 1) 
- type: Conditional simple mediation index (Mod = 1) 
- point estimate: 0.677 
- confidence interval:
  - method: Monte Carlo (5000 iterations)
  - level: 0.05 
  - CI: [0.526; 0.832]

8 Nonlinear regression

This dataset has a binary response (outcome, dependent) variable called admit. There are three predictor variables: gre, gpa and rank. We will treat the variables gre and gpa as continuous. The variable rank takes on the values 1 through 4. Institutions with a rank of 1 have the highest prestige, while those with a rank of 4 have the lowest.

mydata <- read.csv("https://stats.idre.ucla.edu/stat/data/binary.csv")

xtabs(~admit + rank, data = mydata)
     rank
admit  1  2  3  4
    0 28 97 93 55
    1 33 54 28 12

8.1 Logistic regression

mydata$rank <- factor(mydata$rank)
mylogit <- glm(admit ~ gre + gpa + rank, data = mydata, family = "binomial")

summary(mylogit)

Call:
glm(formula = admit ~ gre + gpa + rank, family = "binomial", 
    data = mydata)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.6268  -0.8662  -0.6388   1.1490   2.0790  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept) -3.989979   1.139951  -3.500 0.000465 ***
gre          0.002264   0.001094   2.070 0.038465 *  
gpa          0.804038   0.331819   2.423 0.015388 *  
rank2       -0.675443   0.316490  -2.134 0.032829 *  
rank3       -1.340204   0.345306  -3.881 0.000104 ***
rank4       -1.551464   0.417832  -3.713 0.000205 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 499.98  on 399  degrees of freedom
Residual deviance: 458.52  on 394  degrees of freedom
AIC: 470.52

Number of Fisher Scoring iterations: 4
  • For every one unit change in gre, the log odds of admission (versus non-admission) increases by 0.002.
  • For a one unit increase in gpa, the log odds of being admitted to graduate school increases by 0.804.
  • The indicator variables for rank have a slightly different interpretation. For example, having attended an undergraduate institution with rank of 2, versus an institution with a rank of 1, changes the log odds of admission by -0.675.
confint(mylogit)
                    2.5 %       97.5 %
(Intercept) -6.2716202334 -1.792547080
gre          0.0001375921  0.004435874
gpa          0.1602959439  1.464142727
rank2       -1.3008888002 -0.056745722
rank3       -2.0276713127 -0.670372346
rank4       -2.4000265384 -0.753542605
## odds ratios and 95% CI
exp(cbind(OR = coef(mylogit), confint(mylogit)))
                   OR       2.5 %    97.5 %
(Intercept) 0.0185001 0.001889165 0.1665354
gre         1.0022670 1.000137602 1.0044457
gpa         2.2345448 1.173858216 4.3238349
rank2       0.5089310 0.272289674 0.9448343
rank3       0.2617923 0.131641717 0.5115181
rank4       0.2119375 0.090715546 0.4706961

8.1.1 Wald-test (like an omnibus test)

Main effect of rank

library(aod)
wald.test(b = coef(mylogit), Sigma = vcov(mylogit), Terms = 4:6)
Wald test:
----------

Chi-squared test:
X2 = 20.9, df = 3, P(> X2) = 0.00011

The chi-squared test statistic of 20.9, with three degrees of freedom is associated with a p-value of 0.00011 indicating that the overall effect of rank is statistically significant.

Main effect of gre

wald.test(b = coef(mylogit), Sigma = vcov(mylogit), Terms = 2)
Wald test:
----------

Chi-squared test:
X2 = 4.3, df = 1, P(> X2) = 0.038

Main effect of gpa

wald.test(b = coef(mylogit), Sigma = vcov(mylogit), Terms = 3)
Wald test:
----------

Chi-squared test:
X2 = 5.9, df = 1, P(> X2) = 0.015

8.1.1.1 Pseudo-R squared for logisitic regression

PseudoR2(mylogit, which = c("CoxSnell","Nagelkerke"))
  CoxSnell Nagelkerke 
0.09845702 0.13799580 

8.1.2 Graph logistic regression

  1. Start by calculating the predicted probability of the outcome at each level of the categorical/ordinal predictor variable and/or holding continuous variables at their means.
  2. Create the predicted probabilities using the new dataframe.
  3. Start creating the predicted probability version of your variables
  4. Set the SE to display
  5. Graph
# step 1 
newdata1 <- with(mydata,
                 data.frame(gre = mean(gre), gpa = mean(gpa), rank = factor(1:4)))

# step 2
newdata1$rankP <- predict(mylogit, newdata = newdata1, type = "response")
newdata1
    gre    gpa rank     rankP
1 587.7 3.3899    1 0.5166016
2 587.7 3.3899    2 0.3522846
3 587.7 3.3899    3 0.2186120
4 587.7 3.3899    4 0.1846684
# step 3
newdata2 <- with(mydata,
                 data.frame(gre = rep(seq(from = 200, to = 800, length.out = 100), 4),
                            gpa = mean(gpa), rank = factor(rep(1:4, each = 100))))

# step 4
newdata3 <- cbind(newdata2, predict(mylogit, newdata = newdata2, type="link", se=TRUE))
newdata3 <- within(newdata3, {
  PredictedProb <- plogis(fit)
  LL <- plogis(fit - (1.96 * se.fit))
  UL <- plogis(fit + (1.96 * se.fit))
})

# graph
PredProbPlot <- ggplot(newdata3, aes(x = gre, y = PredictedProb)) +
  geom_ribbon(aes(ymin = LL, ymax = UL, fill = rank), alpha = .2) +
  geom_line(aes(colour = rank), size=1) +theme_minimal()

PredProbPlot + ggtitle("Admission to Grad School by Rank")

8.1.2.1 Simpler prediction plot

library(ggeffects)
plot(ggpredict(mylogit,c("gre","rank")))

8.2 Multinomial Regression

The data set contains variables on 200 students. The outcome variable is prog, program type. The predictor variables are social economic status, ses, a three-level categorical variable and writing score, write, a continuous variable.

(Research Question): When high school students choose the program (general, vocational, and academic programs), how do their math and science scores and their social economic status (SES) affect their decision?

hsb <- read.dta("https://stats.idre.ucla.edu/stat/data/hsbdemo.dta")

with(hsb, table(ses, prog))
        prog
ses      general academic vocation
  low         16       19       12
  middle      20       44       31
  high         9       42        7
# Since we are going to use Academic as the reference group, we need relevel the group.
hsb$prog2 <- relevel(as.factor(hsb$prog), ref = 2)
hsb$ses <- as.factor(hsb$ses)
levels(hsb$prog2)
[1] "academic" "general"  "vocation"
# Run a multinomial model
multi_mo <- multinom(prog2 ~ ses + math + science + math*science, data = hsb,model=TRUE)
# weights:  21 (12 variable)
initial  value 219.722458 
iter  10 value 173.831002
iter  20 value 167.382760
final  value 166.951813 
converged
summary(multi_mo)
Call:
multinom(formula = prog2 ~ ses + math + science + math * science, 
    data = hsb, model = TRUE)

Coefficients:
         (Intercept)  sesmiddle    seshigh       math     science math:science
general     5.897618 -0.4081497 -1.1254491 -0.1852220  0.01323626  0.001025283
vocation   22.728283  0.8402168 -0.5605656 -0.5036705 -0.28297703  0.006185571

Std. Errors:
         (Intercept) sesmiddle   seshigh       math    science math:science
general  0.002304064 0.2613732 0.2134308 0.02694593 0.02953364 0.0004761369
vocation 0.003856861 0.2959741 0.1984775 0.02681947 0.03142872 0.0004760567

Residual Deviance: 333.9036 
AIC: 357.9036 

Main effects

Anova(multi_mo, type="II")
Analysis of Deviance Table (Type II tests)

Response: prog2
             LR Chisq Df     Pr(>Chisq)    
ses            12.922  4        0.01166 *  
math           39.156  2 0.000000003143 ***
science         8.134  2        0.01713 *  
math:science    5.249  2        0.07247 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
PseudoR2(multi_mo, which = c("CoxSnell","Nagelkerke"))
  CoxSnell Nagelkerke 
 0.3102655  0.3565873 
broom::tidy(multi_mo, conf.int= TRUE, conf.level = 0.95, exponentiate = T)
# A tibble: 12 × 8
   y.level  term         estimate std.error statistic  p.value conf.low conf.high
   <chr>    <chr>           <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
 1 general  (Intercept)   3.64e+2  0.00230   2560.    0         3.63e+2   3.66e+2
 2 general  sesmiddle     6.65e-1  0.261       -1.56  1.18e- 1  3.98e-1   1.11e+0
 3 general  seshigh       3.25e-1  0.213       -5.27  1.34e- 7  2.14e-1   4.93e-1
 4 general  math          8.31e-1  0.0269      -6.87  6.25e-12  7.88e-1   8.76e-1
 5 general  science       1.01e+0  0.0295       0.448 6.54e- 1  9.56e-1   1.07e+0
 6 general  math:science  1.00e+0  0.000476     2.15  3.13e- 2  1.00e+0   1.00e+0
 7 vocation (Intercept)   7.43e+9  0.00386   5893.    0         7.37e+9   7.48e+9
 8 vocation sesmiddle     2.32e+0  0.296        2.84  4.53e- 3  1.30e+0   4.14e+0
 9 vocation seshigh       5.71e-1  0.198       -2.82  4.74e- 3  3.87e-1   8.42e-1
10 vocation math          6.04e-1  0.0268     -18.8   1.10e-78  5.73e-1   6.37e-1
11 vocation science       7.54e-1  0.0314      -9.00  2.18e-19  7.09e-1   8.01e-1
12 vocation math:science  1.01e+0  0.000476    13.0   1.33e-38  1.01e+0   1.01e+0

Another way to understand the model using the predicted probabilities is to look at the averaged predicted probabilities for different values of the continuous predictor variable math within each level of ses.

# Extract coefficients
coef_df <- as.data.frame(coef(multi_mo))
coef_df$Term <- row.names(coef_df)

# Melt the dataframe for ggplot
library(reshape2)
melted_coef_df <- melt(coef_df, id.vars = c("Term", "(Intercept)"))

# Plot using ggplot2
library(ggplot2)
ggplot(melted_coef_df, aes(x = Term, y = value, fill = variable)) +
  geom_bar(stat = "identity", position = position_dodge()) +
  theme_minimal() +
  labs(x = "Term", y = "Coefficient", fill = "Outcome") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

plot(ggpredict(multi_mo,c("ses", "math")))

8.2.0.1 Write-up

(Research Question): When high school students choose the program (general, vocational, and academic programs), how do their math and science scores and their social economic status (SES) affect their decision?

A one-unit increase in math is associated with the decrease in the log odds of being in general program vs. academic program in the amount of .19 (one-unit increase in the variable math is .83 for being in general program vs. academic program).

Students in the highest social status level (SES = 3), compared to the lowest (SES = 1), are less likely to be in the general program compared to the academic one. The chance decreases by about 1.125 times, b = -1.125, Wald χ2(1) = -5.27, p <.001. This means their odds are about 0.325 times lower, making students from lower social status more inclined to pick the general program over the academic one.

Similarly, for high SES students (compared to low), the likelihood of being in the vocational program instead of the academic program drops by 0.56 times, b = -0.56, Wald χ2(1) = -2.82, p < 0.01. In other words, lower-status students (compared to high SES) prefer the vocational program over the academic one.

8.3 Poisson regression

Here, we’re looking at whether prog and math predict number of awards

p <- read.csv("https://stats.idre.ucla.edu/stat/data/poisson_sim.csv")
p <- within(p, {
  prog <- factor(prog, levels=1:3, labels=c("General", "Academic", 
                                                     "Vocational"))
  id <- factor(id)
})
summary(p)
       id        num_awards           prog          math      
 1      :  1   Min.   :0.00   General   : 45   Min.   :33.00  
 2      :  1   1st Qu.:0.00   Academic  :105   1st Qu.:45.00  
 3      :  1   Median :0.00   Vocational: 50   Median :52.00  
 4      :  1   Mean   :0.63                    Mean   :52.65  
 5      :  1   3rd Qu.:1.00                    3rd Qu.:59.00  
 6      :  1   Max.   :6.00                    Max.   :75.00  
 (Other):194                                                  
ggplot(p, aes(num_awards, fill = prog)) +
  geom_histogram(binwidth=.5, position="dodge") + theme_classic()

summary(m1 <- glm(num_awards ~ prog + math, family="poisson", data=p))

Call:
glm(formula = num_awards ~ prog + math, family = "poisson", data = p)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.2043  -0.8436  -0.5106   0.2558   2.6796  

Coefficients:
               Estimate Std. Error z value           Pr(>|z|)    
(Intercept)    -5.24712    0.65845  -7.969 0.0000000000000016 ***
progAcademic    1.08386    0.35825   3.025            0.00248 ** 
progVocational  0.36981    0.44107   0.838            0.40179    
math            0.07015    0.01060   6.619 0.0000000000362501 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for poisson family taken to be 1)

    Null deviance: 287.67  on 199  degrees of freedom
Residual deviance: 189.45  on 196  degrees of freedom
AIC: 373.5

Number of Fisher Scoring iterations: 6

Cameron and Trivedi (2009) recommended using robust standard errors for the parameter estimates to control for mild violation of the distribution assumption that the variance equals the mean.

cov.m1 <- vcovHC(m1, type="HC0")
std.err <- sqrt(diag(cov.m1))
r.est <- cbind(Estimate= coef(m1), "Robust SE" = std.err,
"Pr(>|z|)" = 2 * pnorm(abs(coef(m1)/std.err), lower.tail=FALSE),
LL = coef(m1) - 1.96 * std.err,
UL = coef(m1) + 1.96 * std.err)

r.est
                 Estimate  Robust SE                Pr(>|z|)          LL          UL
(Intercept)    -5.2471244 0.64599839 0.000000000000000456663 -6.51328124 -3.98096756
progAcademic    1.0838591 0.32104816 0.000735474482416738676  0.45460476  1.71311353
progVocational  0.3698092 0.40041731 0.355715684208826043999 -0.41500870  1.15462716
math            0.0701524 0.01043516 0.000000000017839751696  0.04969947  0.09060532

8.3.0.1 Plot predictions

## calculate and store predicted values
p$phat <- predict(m1, type="response")

## order by program and then by math
p <- p[with(p, order(prog, math)), ]

## create the plot
ggplot(p, aes(x = math, y = phat, colour = prog)) +
  geom_point(aes(y = num_awards), alpha=.5, position=position_jitter(h=.2)) +
  geom_line(size = 1) + theme_classic() +
  labs(x = "Math Score", y = "Expected number of awards")

8.3.0.2 Write up

Being in an academic program was associated with a significant increase in the number of awards, b = 1.084, SE = 0.358, z = 3.025, p = 0.00248, indicating that students in academic programs tend to receive more awards than those in general programs, holding math scores constant.

Being in a vocational program did not significantly predict the number of awards compared to being in a general program, b = 0.370, SE = 0.441, z = 0.838, p = 0.40179, suggesting no significant difference in the expected count of awards between vocational and general program students, again holding math scores constant.

Math scores were also a significant predictor of the number of awards, b = 0.07015, SE = 0.01060, z = 6.619, p < 0.0001, suggesting that, regardless of program type, an increase in math scores is associated with an increase in the expected number of awards.

8.4 Ordinal regression

library(MASS) # For ordinal logistic regression (polr)

# Generate example data
set.seed(123)
data <- data.frame(
  StudyHours = rnorm(100, mean = 5, sd = 2), # Simulating study hours
  Performance = ordered(sample(1:5, 100, replace = TRUE), levels = c(1, 2, 3, 4, 5), labels = c("Poor", "Below Average", "Average", "Above Average", "Excellent"))
)

# Fit an ordinal logistic regression model
model <- polr(Performance ~ StudyHours, data = data)

summary(model)
Call:
polr(formula = Performance ~ StudyHours, data = data)

Coefficients:
            Value Std. Error t value
StudyHours 0.1162    0.09861   1.178

Intercepts:
                        Value   Std. Error t value
Poor|Below Average      -0.5097  0.5491    -0.9284
Below Average|Average    0.4270  0.5379     0.7938
Average|Above Average    0.9074  0.5377     1.6876
Above Average|Excellent  1.5883  0.5552     2.8607

Residual Deviance: 311.9673 
AIC: 321.9673 

8.4.0.1 Get p-values

## store table
(ctable <- coef(summary(model)))
                             Value Std. Error    t value
StudyHours               0.1161846 0.09860927  1.1782316
Poor|Below Average      -0.5097395 0.54906589 -0.9283759
Below Average|Average    0.4269730 0.53789874  0.7937795
Average|Above Average    0.9073994 0.53768046  1.6876183
Above Average|Excellent  1.5882928 0.55521379  2.8606868
## calculate and store p values
p <- pnorm(abs(ctable[, "t value"]), lower.tail = FALSE) * 2

## combined table
(ctable <- cbind(ctable, "p value" = p))
                             Value Std. Error    t value     p value
StudyHours               0.1161846 0.09860927  1.1782316 0.238704296
Poor|Below Average      -0.5097395 0.54906589 -0.9283759 0.353212615
Below Average|Average    0.4269730 0.53789874  0.7937795 0.427323829
Average|Above Average    0.9073994 0.53768046  1.6876183 0.091484520
Above Average|Excellent  1.5882928 0.55521379  2.8606868 0.004227244

The significance of “Above Average|Excellent” suggests that study hours become more influential in determining whether students achieve Above Average vs Excellent.

8.4.0.2 Plot ordinal

# Create a sequence of study hours for prediction
new_data <- data.frame(StudyHours = seq(min(data$StudyHours), max(data$StudyHours), length.out = 100))

# Predict probabilities for each performance category
predicted_probs <- as.data.frame(predict(model, newdata = new_data, type = "probs"))

# Rename the columns for clarity
colnames(predicted_probs) <- c("Poor", "Below Average", "Average", "Above Average", "Excellent")

# Create a visualization
ggplot(predicted_probs, aes(x = new_data$StudyHours)) +
  geom_line(aes(y = `Poor`, color = "Poor"), size = 1) +
  geom_line(aes(y = `Below Average`, color = "Below Average"), size = 1) +
  geom_line(aes(y = `Average`, color = "Average"), size = 1) +
  geom_line(aes(y = `Above Average`, color = "Above Average"), size = 1) +
  geom_line(aes(y = `Excellent`, color = "Excellent"), size = 1) +
  labs(title = "Ordinal Logistic Regression: Predicting Student Performance",
       x = "Study Hours",
       y = "Predicted Probability") +
  scale_color_manual(values = c("Poor" = "#1f77b4", 
                                "Below Average" = "#ff7f0e", 
                                "Average" = "#2ca02c", 
                                "Above Average" = "#d62728", 
                                "Excellent" = "#9467bd"),
                     name = "Performance Level",
                     limits = c("Excellent","Above Average", "Average", "Below Average", "Poor")) + # Explicitly set the order
  theme_minimal()

LS0tCnRpdGxlOiAnUm9idXN0IFJlZ3Jlc3Npb24sIE1vZGVyYXRpb24sIE1lZGlhdGlvbiwgYW5kIE5vbmxpbmVhciBSZWdyZXNzaW9uJwphdXRob3I6ICJLYXJlZW5hIGRlbCBSb3NhcmlvIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiBzaW1wbGV4CiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIHRvYzogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IGZhbHNlCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCmBgYHs9aHRtbH0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCgpoMS50aXRsZSB7CiAgY29sb3I6IERhcmtSZWQ7Cn0KaDEgeyAvKiBIZWFkZXIgMSAqLwogIGNvbG9yOiBEYXJrUmVkOwp9CmgyIHsgLyogSGVhZGVyIDIgKi8KICBjb2xvcjogRGFya1JlZDsKfQpoMyB7IC8qIEhlYWRlciAzICovCiAgY29sb3I6IERhcmtSZWQ7Cgo8L3N0eWxlPgpgYGAKYGBge3IsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEsCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICB0aWR5ID0gJ3N0eWxlcicsCiAgICAgICAgICAgICAgICAgICAgICBlcnJvciA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIGhpZ2hsaWdodCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgcHJvbXB0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBSLm9wdGlvbnMgPSBsaXN0KHdpZHRoID0gOTApKQpvcHRpb25zKHNjaXBlbiA9IDk5OSkKCnByaW50X3AgPC0gZnVuY3Rpb24ocCkgewogIHByaW50KHJvdW5kKHAsIDQpLCBxdW90ZSA9IEZBTFNFKQp9CmBgYAoKKipUbyBzZWUgYSBzdW1tYXJ5IG9mIHRoZSByZWdyZXNzaW9uIGFzc3VtcHRpb25zIGFuZCBtb3JlIHdheXMgcnVuIHRlc3QKZGlhZ25vc3RpY3MsIHNlZSBvdXIgcHJldmlvdXMKW2xhYl0oaHR0cHM6Ly9ycHVicy5jb20va2FyZWVuYV9kZWxyb3NhcmlvL2xhYjdfbGluZWFyX3JlZ3Jlc3Npb24yMDI0KSoqCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KCnBrZ3MgPC0gYygidGlkeXZlcnNlIiwgCiAgICAgICAgICAiZHBseXIiLCAKICAgICAgICAgICJoYXZlbiIsIAogICAgICAgICAgImZvcmVpZ24iLCAKICAgICAgICAgICJsbWU0IiwgCiAgICAgICAgICAibmxtZSIsIAogICAgICAgICAgImxzciIsIAogICAgICAgICAgImVtbWVhbnMiLCAKICAgICAgICAgICJhZmV4IiwgCiAgICAgICAgICAia25pdHIiLCAKICAgICAgICAgICJrYWJsZUV4dHJhIiwgCiAgICAgICAgICAiY2FyIiwKICAgICAgICAgICJtZWRpYXRpb24iLAogICAgICAgICAgInJvY2tjaGFsayIsCiAgICAgICAgICAibXVsdGlsZXZlbCIsCiAgICAgICAgICAiYmRhIiwKICAgICAgICAgICJndmxtYSIsCiAgICAgICAgICAic3RhcmdhemVyIiwKICAgICAgICAgICJRdWFudFBzeWMiLAogICAgICAgICAgInBlcXVvZCIsCiAgICAgICAgICAiTUFTUyIsCiAgICAgICAgICAidGV4cmVnIiwKICAgICAgICAgICJwd3IiLAogICAgICAgICAgImVmZmVjdHNpemUiLAogICAgICAgICAgInNlbVBsb3QiLAogICAgICAgICAgImxtdGVzdCIsCiAgICAgICAgICAic2VtcHRvb2xzIiwKICAgICAgICAgICJjb25mbGljdGVkIiwKICAgICAgICAgICJubmV0IiwKICAgICAgICAgICJvcmRpbmFsIiwKICAgICAgICAgICJEZXNjVG9vbHMiKQoKCnBhY2thZ2VzIDwtIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKQpwX3RvX2luc3RhbGwgPC0gcGtnc1shKHBrZ3MgJWluJSBwYWNrYWdlcyldCgppZihsZW5ndGgocF90b19pbnN0YWxsKSA+IDApewogIGluc3RhbGwucGFja2FnZXMocF90b19pbnN0YWxsKQp9CgpsYXBwbHkocGtncywgbGlicmFyeSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQoKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImNhcmRpb21vb24vc2VtTWVkaWF0aW9uIikKbGlicmFyeShzZW1NZWRpYXRpb24pCgojIHRlbGwgUiB3aGljaCBwYWNrYWdlIHRvIHVzZSBmb3IgZnVuY3Rpb25zIHRoYXQgYXJlIGluIG11bHRpcGxlIHBhY2thZ2VzCnRoZXNlX2Z1bmN0aW9ucyA8LSBjKCJtdXRhdGUiLCAic2VsZWN0IiwgInN1bW1hcml6ZSIsICJmaWx0ZXIiKQpsYXBwbHkodGhlc2VfZnVuY3Rpb25zLCBjb25mbGljdF9wcmVmZXIsICJkcGx5ciIpCgpjb25mbGljdF9wcmVmZXIoIm11dGF0ZSIsICJkcGx5ciIpCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIikKY29uZmxpY3RfcHJlZmVyKCJzdW1tYXJpemUiLCAiZHBseXIiKQoKCgpgYGAKCiMgUXVpY2sgY2hlY2staW4gb24gaG9tZXdvcmsgMgoKIyMjIFBvd2VyIGFuYWx5c2lzIGZvciBBTk9WQSBhbmQgY2hpLXNxdWFyZQoKIyMjIyBBTk9WQQoKYGBge3J9CiMgcXVpY2sgQU5PVkEgZm9yIGRlbW9uc3RyYXRpb24KIyBjcmVhdGUgaWQgdmFyaWFibGUgZm9yIGVycm9yIHRlcm0KaXJpc19hZmV4IDwtIGlyaXMgJT4lIAogIGRwbHlyOjptdXRhdGUoaWQgPSByb3dfbnVtYmVyKCkpCgphbm92YV9leCA8LSBhZmV4Ojphb3ZfY2FyKFNlcGFsLkxlbmd0aCB+IFNwZWNpZXMgKyBFcnJvcihpZCksIGRhdGEgPSBpcmlzX2FmZXgpCnN1bW1hcnkoYW5vdmFfZXgpCgoKIyBjb252ZXJ0IGV0YS1zcXVhcmVkIHRvIGNvaGVuJ3MgRiB3aXRoIHRoaXMgZnVuY3Rpb246CmxpYnJhcnkoZWZmZWN0c2l6ZSkKY29oZW5zX2YoYW5vdmFfZXgpCmBgYAoKIyMjIyBDaGktc3F1YXJlCgpgYGB7cn0KIyBoZXJlIGlzIGFub3RoZXIgcG93ZXIgYW5hbHlzaXMgZnVuY3Rpb24gdGhhdCBhbGxvd3MgeW91IHRvIHVzZSBjb2hlbidzIHcuCmxpYnJhcnkocHdyKQpwd3IuY2hpc3EudGVzdCh3PTAuMixkZj0xLHBvd2VyPS45NSxzaWcubGV2ZWw9MC4wNSkKYGBgCgojIyMjIFJlZ3Jlc3Npb24KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGVmZmVjdHNpemUpCgojIEdldCBmLXNxdWFyZWQKcmVnX2V4IDwtIGxtKFNlcGFsLkxlbmd0aCB+IFNwZWNpZXMsIGRhdGEgPSBpcmlzX2FmZXgpCmNvaGVuc19mX3NxdWFyZWQocmVnX2V4KQoKIyBNYW51YWxseSBjYWxjdWxhdGUgZi1zcXVhcmVkCnJfc3F1YXJlZCA8LSBzdW1tYXJ5KHJlZ19leCkkci5zcXVhcmVkCmZfc3F1YXJlZCA8LSByX3NxdWFyZWQgLyAoMSAtIHJfc3F1YXJlZCkKCmxpYnJhcnkocHdyKQpwb3dlcl9hbmFseXNpcyA8LSBwd3IuZjIudGVzdCh1ID0gMiwgZjIgPSAuMTUsIHNpZy5sZXZlbCA9IDAuMDUsIHBvd2VyID0gMC45NSkKcHJpbnQocG93ZXJfYW5hbHlzaXMpCmBgYAoKIyMjIFBsYW5uZWQgY29udHJhc3RzIHZzIHBhaXJ3aXNlIGNvbXBhcmlzb25zCgpHZW5kZXIgKE1hbGUsIEZlbWFsZSkgRmlsbSAoQnJpZGdldCBKb25lcywgTWVtZW50bykKCipGb3IgcGVvcGxlIHdobyB3YXRjaGVkIE1lbWVudG8sIGRvIHdvbWVuIHJlcG9ydCBoaWdoZXIgYXJvdXNhbCBhZnRlcgp3YXRjaGluZyB0aGUgZmlsbSB0aGFuIG1lbj8qCgojIyMjIFBsYW5uZWQgY29udHJhc3RzCgpgYGB7cn0KZC5kYXRhIDwtIHJlYWQuZGVsaW0oIi9Vc2Vycy9rYXJlZW5hZGVscm9zYXJpby9Eb3dubG9hZHMvQ2hpY2tGbGljay5kYXQiKQoKZC5kYXRhIDwtIGQuZGF0YSAlPiUgCiAgbXV0YXRlKGdlbmRlciA9IGFzLmZhY3RvcihpZmVsc2UoZ2VuZGVyID09ICJGZW1hbGUiLCAwLCAxKSksCiAgICAgICAgIGZpbG0gPSBhcy5mYWN0b3IoaWZlbHNlKGZpbG0gPT0gIk1lbWVudG8iLCAwLCAxKSkpCgpjb250cmFzdHMoZC5kYXRhJGdlbmRlcikgPSBjb250ci50cmVhdG1lbnQoMiwgYmFzZSA9IDIpICMgZmVtYWxlID0gMSwgbWFsZSA9IDAKY29udHJhc3RzKGQuZGF0YSRmaWxtKSAjIG1lbWVudG8gPSAwLCBicmlkZ2V0IGpvbmVzID0gMQpgYGAKCmBgYHtyfQpkX21vZGVsIDwtIGxtKGFyb3VzYWwgfiBnZW5kZXIgKiBmaWxtLCBkYXRhID0gZC5kYXRhKQpzdW1tYXJ5KGRfbW9kZWwpCmBgYAoKIyMjIyBQYWlyd2lzZSBjb21wYXJpc29ucwoKYGBge3J9CmRfZW1tIDwtIGVtbWVhbnMoZF9tb2RlbCwgc3BlY3MgPSBjKCJnZW5kZXIiLCAiZmlsbSIpKQpwYWlycyhkX2VtbSkKYGBgCgojIyMjIFdoeSBjYW4ndCBJIGp1c3QgZmlsdGVyIG91dCBCcmlkZ2V0IEpvbmVzPwoKYGBge3J9CmQuZGF0YS5mIDwtIGQuZGF0YSAlPiUgCiAgZmlsdGVyKGZpbG0gPT0gMCkKCmZfbW9kZWwgPC0gbG0oYXJvdXNhbCB+IGdlbmRlciwgZGF0YSA9IGQuZGF0YS5mKQpzdW1tYXJ5KGZfbW9kZWwpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFJvYnVzdCBSZWdyZXNzaW9uCgoqY3JlZGl0OiA8aHR0cHM6Ly9zdGF0cy5vYXJjLnVjbGEuZWR1L3IvZGFlL3JvYnVzdC1yZWdyZXNzaW9uLz4qCgoqKldoYXQgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBPTFMgYW5kIHJvYnVzdCByZWdyZXNzaW9uPyBXaGVuIHdvdWxkCnlvdSB1c2Ugb25lIG92ZXIgdGhlIG90aGVyPyoqCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIERlYWxpbmcgd2l0aCBvdXRsaWVycyB1c2luZyBIdWJlcnQgYW5kIGJpc3F1YXJlIHdlaWdodHMKClJvYnVzdCByZWdyZXNzaW9uIGlzIHN1aXRhYmxlIGZvciBjYXNlcyB3aGVyZSB5b3UnZCB0eXBpY2FsbHkgdXNlIE9MUwpyZWdyZXNzaW9uIGJ1dCBlbmNvdW50ZXIgKm91dGxpZXJzIG9yIGhpZ2ggbGV2ZXJhZ2UgcG9pbnRzIHRoYXQgYXJlbid0CmVycm9ycyBvciBmcm9tIGEgZGlmZmVyZW50IHBvcHVsYXRpb24qLiBJbnN0ZWFkIG9mIGV4Y2x1ZGluZyB0aGVzZQpwb2ludHMgb3IgdHJlYXRpbmcgYWxsIGRhdGEgZXF1YWxseSBhcyBpbiBPTFMgcmVncmVzc2lvbiwgcm9idXN0CnJlZ3Jlc3Npb24gb2ZmZXJzIGEgbWlkZGxlIGdyb3VuZCBieSBhc3NpZ25pbmcgZGlmZmVyZW50IHdlaWdodHMgdG8Kb2JzZXJ2YXRpb25zIGJhc2VkIG9uIHRoZWlyIHJlbGlhYmlsaXR5LiBFc3NlbnRpYWxseSwgaXQncyBhIHdlaWdodGVkCmxlYXN0IHNxdWFyZXMgYXBwcm9hY2ggdGhhdCBhZGp1c3RzIHdlaWdodHMgaW4gcmVzcG9uc2UgdG8gdGhlIGZlYXR1cmVzCm9mIHRoZSBkYXRhLgoKVGVybXMgdG8ga25vdzoKCmBgYCAgICAgICAgIAotIFJlc2lkdWFsOiBUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgdmFsdWUgKGJhc2VkIG9uIHRoZSByZWdyZXNzaW9uIGVxdWF0aW9uKSBhbmQgdGhlIGFjdHVhbCwgb2JzZXJ2ZWQgdmFsdWUuCgotIE91dGxpZXI6IEluIGxpbmVhciByZWdyZXNzaW9uLCBhbiBvdXRsaWVyIGlzIGFuIG9ic2VydmF0aW9uIHdpdGggbGFyZ2UgcmVzaWR1YWwuIEluIG90aGVyIHdvcmRzLCBpdCBpcyBhbiBvYnNlcnZhdGlvbiB3aG9zZSBkZXBlbmRlbnQtdmFyaWFibGUgdmFsdWUgaXMgdW51c3VhbCBnaXZlbiBpdHMgdmFsdWUgb24gdGhlIHByZWRpY3RvciB2YXJpYWJsZXMuIEFuIG91dGxpZXIgbWF5IGluZGljYXRlIGEgc2FtcGxlIHBlY3VsaWFyaXR5IG9yIG1heSBpbmRpY2F0ZSBhIGRhdGEgZW50cnkgZXJyb3Igb3Igb3RoZXIgcHJvYmxlbS4KCi0gTGV2ZXJhZ2U6IEFuIG9ic2VydmF0aW9uIHdpdGggYW4gZXh0cmVtZSB2YWx1ZSBvbiBhIHByZWRpY3RvciB2YXJpYWJsZSBpcyBhIHBvaW50IHdpdGggaGlnaCBsZXZlcmFnZS4gTGV2ZXJhZ2UgaXMgYSBtZWFzdXJlIG9mIGhvdyBmYXIgYW4gaW5kZXBlbmRlbnQgdmFyaWFibGUgZGV2aWF0ZXMgZnJvbSBpdHMgbWVhbi4gSGlnaCBsZXZlcmFnZSBwb2ludHMgY2FuIGhhdmUgYSBncmVhdCBhbW91bnQgb2YgZWZmZWN0IG9uIHRoZSBlc3RpbWF0ZSBvZiByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4KCi0gSW5mbHVlbmNlOiBBbiBvYnNlcnZhdGlvbiBpcyBzYWlkIHRvIGJlIGluZmx1ZW50aWFsIGlmIHJlbW92aW5nIHRoZSBvYnNlcnZhdGlvbiBzdWJzdGFudGlhbGx5IGNoYW5nZXMgdGhlIGVzdGltYXRlIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4gSW5mbHVlbmNlIGNhbiBiZSB0aG91Z2h0IG9mIGFzIHRoZSBwcm9kdWN0IG9mIGxldmVyYWdlIGFuZCBvdXRsaWVybmVzcy4KCi0gQ29va+KAmXMgZGlzdGFuY2UgKG9yIENvb2vigJlzIEQpOiBBIG1lYXN1cmUgdGhhdCBjb21iaW5lcyB0aGUgaW5mb3JtYXRpb24gb2YgbGV2ZXJhZ2UgYW5kIHJlc2lkdWFsIG9mIHRoZSBvYnNlcnZhdGlvbi4KYGBgCgpgYGB7cn0KY2RhdGEgPC0gcmVhZC5kdGEoImh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvY3JpbWUuZHRhIikKCmhlYWQoY2RhdGEpCmBgYAoKIyMjIyBWYXJpYWJsZXMKCi0gICAoc2lkKSBTdGF0ZSBJRAotICAgKHN0YXRlKSBTdGF0ZSBuYW1lCi0gICAoY3JpbWUpIFZpb2xlbnQgY3JpbWVzIHBlciAxMDAsMDAwIHBlb3BsZQotICAgKHBvdmVydHkpIFBlcmNlbnQgb2YgcG9wdWxhdGlvbiBsaXZpbmcgdW5kZXIgcG92ZXJ0eSBsaW5lCi0gICAoc2luZ2xlKSBQZXJjZW50IG9mIHBvcHVsYXRpb24gdGhhdCBhcmUgc2luZ2xlIHBhcmVudHMKICAgIC0gICBUb3RhbCBvYnNlcnZhdGlvbnM6IDUxCiAgICAtICAgY3JpbWUgXH4gcG92ZXJ0eSArIHNpbmdsZQoKIyMjIyBSdW4gT0xTCgpXZSB3aWxsIGJlZ2luIGJ5IHJ1bm5pbmcgYW4gT0xTIHJlZ3Jlc3Npb24gYW5kIGxvb2tpbmcgYXQgZGlhZ25vc3RpYwpwbG90cyBleGFtaW5pbmcgcmVzaWR1YWxzLCBmaXR0ZWQgdmFsdWVzLCBDb29r4oCZcyBkaXN0YW5jZSwgYW5kIGxldmVyYWdlLgoKYGBge3J9CnN1bW1hcnkob2xzIDwtIGxtKGNyaW1lIH4gcG92ZXJ0eSArIHNpbmdsZSwgZGF0YSA9IGNkYXRhKSkKYGBgCgojIyMgSWRlbnRpZnkgb3V0bGllcnMKCmBgYHtyfQpwbG90KG9scykKCmBgYAoKRnJvbSB0aGVzZSBwbG90cywgd2UgY2FuIGlkZW50aWZ5IG9ic2VydmF0aW9ucyA5LCAyNSwgYW5kIDUxIGFzIHBvc3NpYmx5CnByb2JsZW1hdGljIHRvIG91ciBtb2RlbC4gV2UgY2FuIGxvb2sgYXQgdGhlc2Ugb2JzZXJ2YXRpb25zIHRvIHNlZSB3aGljaApzdGF0ZXMgdGhleSByZXByZXNlbnQuCgpgYGB7cn0KY2RhdGFbYyg5LCAyNSwgNTEpLCAxOjJdCmBgYAoKREMsIEZsb3JpZGEgYW5kIE1pc3Npc3NpcHBpIGhhdmUgZWl0aGVyIGhpZ2ggbGV2ZXJhZ2Ugb3IgbGFyZ2UKcmVzaWR1YWxzLiBXZSBjYW4gZGlzcGxheSB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgaGF2ZSByZWxhdGl2ZWx5IGxhcmdlCnZhbHVlcyBvZiBDb29r4oCZcyBELiBBIGNvbnZlbnRpb25hbCBjdXQtb2ZmIHBvaW50IGlzIDQvbiwgd2hlcmUgbiBpcyB0aGUKbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YSBzZXQuCgpgYGB7cn0KZDEgPC0gY29va3MuZGlzdGFuY2Uob2xzKSAjIGNhcHR1cmVzIGluZmx1ZW50aWFsIG9ic2VydmF0aW9ucwpyIDwtIHN0ZHJlcyhvbHMpICMgY29tcHV0ZXMgc3RhbmRhcmRpemVkIHJlc2lkdWFscwphIDwtIGNiaW5kKGNkYXRhLCBkMSwgcikgCmFbZDEgPiA0LzUxLCBdICMgRmlsdGVycyB0aGUgY29tYmluZWQgZGF0YSBmcmFtZSBhIHRvIGRpc3BsYXkgb25seSB0aG9zZSBvYnNlcnZhdGlvbnMgZm9yIHdoaWNoIENvb2sncyBkaXN0YW5jZSBpcyBncmVhdGVyIHRoYW4gNC9uLiBuID0gNTEgCmBgYAoKTm93IHdlIHdpbGwgbG9vayBhdCB0aGUgcmVzaWR1YWxzLiBXZSB3aWxsIGdlbmVyYXRlIGEgbmV3IHZhcmlhYmxlCmNhbGxlZCBhYnNyMSwgd2hpY2ggaXMgdGhlIGFic29sdXRlIHZhbHVlIG9mIHRoZSByZXNpZHVhbHMgKGJlY2F1c2UgdGhlCnNpZ24gb2YgdGhlIHJlc2lkdWFsIGRvZXNu4oCZdCBtYXR0ZXIpLiBXZSB0aGVuIHByaW50IHRoZSB0ZW4gb2JzZXJ2YXRpb25zCndpdGggdGhlIGhpZ2hlc3QgYWJzb2x1dGUgcmVzaWR1YWwgdmFsdWVzLgoKYGBge3J9CnJhYnMgPC0gYWJzKHIpCmEgPC0gY2JpbmQoY2RhdGEsIGQxLCByLCByYWJzKQphc29ydGVkIDwtIGFbb3JkZXIoLXJhYnMpLCBdCmFzb3J0ZWRbMToxMCwgXQpgYGAKCiMjIyBJUkxTIHdpdGggSHViZXIgd2VpZ2h0cwoKTm93IGxldOKAmXMgcnVuIG91ciBmaXJzdCByb2J1c3QgcmVncmVzc2lvbi4gUm9idXN0IHJlZ3Jlc3Npb24gaXMgZG9uZSBieQppdGVyYXRlZCByZS13ZWlnaHRlZCBsZWFzdCBzcXVhcmVzIChJUkxTKS4gVGhlIGNvbW1hbmQgZm9yIHJ1bm5pbmcKcm9idXN0IHJlZ3Jlc3Npb24gaXMgKipybG0qKiBpbiB0aGUgKipNQVNTKiogcGFja2FnZS4gVGhlcmUgYXJlIHNldmVyYWwKd2VpZ2h0aW5nIGZ1bmN0aW9ucyB0aGF0IGNhbiBiZSB1c2VkIGZvciBJUkxTLiBXZSBhcmUgZ29pbmcgdG8gZmlyc3QgdXNlCnRoZSBIdWJlciB3ZWlnaHRzIGluIHRoaXMgZXhhbXBsZS4gV2Ugd2lsbCB0aGVuIGxvb2sgYXQgdGhlIGZpbmFsCndlaWdodHMgY3JlYXRlZCBieSB0aGUgSVJMUyBwcm9jZXNzLiBUaGlzIGNhbiBiZSB2ZXJ5IHVzZWZ1bC4KCmBgYHtyfQpzdW1tYXJ5KHJyLmh1YmVyIDwtIHJsbShjcmltZSB+IHBvdmVydHkgKyBzaW5nbGUsIGRhdGEgPSBjZGF0YSkpCmBgYAoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIHdlaWdodHMgb2YgdGhlIHRvcCAxNSBsYXJnZXN0IHJlc2lkdWFscwoKYGBge3J9Cmh3ZWlnaHRzIDwtIGRhdGEuZnJhbWUoc3RhdGUgPSBjZGF0YSRzdGF0ZSwgcmVzaWQgPSByci5odWJlciRyZXNpZCwgd2VpZ2h0ID0gcnIuaHViZXIkdykKaHdlaWdodHMyIDwtIGh3ZWlnaHRzW29yZGVyKHJyLmh1YmVyJHcpLCBdCmh3ZWlnaHRzMlsxOjE1LCBdCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHJvdWdobHksIGFzIHRoZSBhYnNvbHV0ZSByZXNpZHVhbCBnb2VzIGRvd24sIHRoZSB3ZWlnaHQKZ29lcyB1cC4KCkFsbCBvYnNlcnZhdGlvbnMgbm90IHNob3duIGFib3ZlIGhhdmUgYSB3ZWlnaHQgb2YgMS4gSW4gT0xTIHJlZ3Jlc3Npb24sCmFsbCBjYXNlcyBoYXZlIGEgd2VpZ2h0IG9mIDEuIEhlbmNlLCB0aGUgbW9yZSBjYXNlcyBpbiB0aGUgcm9idXN0CnJlZ3Jlc3Npb24gdGhhdCBoYXZlIGEgd2VpZ2h0IGNsb3NlIHRvIG9uZSwgdGhlIGNsb3NlciB0aGUgcmVzdWx0cyBvZgp0aGUgT0xTIGFuZCByb2J1c3QgcmVncmVzc2lvbnMuCgojIyMgSVJMUyB3aXRoIGJpc3F1YXJlIHdlaWdodHMKCk5leHQsIGxldOKAmXMgcnVuIHRoZSBzYW1lIG1vZGVsLCBidXQgdXNpbmcgdGhlIGJpc3F1YXJlIHdlaWdodGluZwpmdW5jdGlvbi4gQWdhaW4sIHdlIGNhbiBsb29rIGF0IHRoZSB3ZWlnaHRzLgoKYGBge3J9CnJyLmJpc3F1YXJlIDwtIHJsbShjcmltZSB+IHBvdmVydHkgKyBzaW5nbGUsIGRhdGE9Y2RhdGEsIHBzaSA9IHBzaS5iaXNxdWFyZSkKc3VtbWFyeShyci5iaXNxdWFyZSkKCiMgaWYgeW91IG5lZWQgc2lnbmlmaWNhbmNlIHZhbHVlcy4gTUFTUyBkb2Vzbid0IHRydXN0IHAtdmFsdWVzIGFuZCB3b24ndCByZXBvcnQgdGhlbS4KIyB0ZXhyZWcocnIuYmlzcXVhcmUsIGNpLmZvcmNlPVRSVUUpCmBgYAoKSGVyZSBhcmUgdGhlIHdlaWdodHMgZm9yIHRoZSBzYW1lIDE1IG9ic2VydmF0aW9ucyB1c2luZyBiaXNxdWFyZSB3ZWlnaHRzCgpgYGB7cn0KYml3ZWlnaHRzIDwtIGRhdGEuZnJhbWUoc3RhdGUgPSBjZGF0YSRzdGF0ZSwgcmVzaWQgPSByci5iaXNxdWFyZSRyZXNpZCwgd2VpZ2h0ID0gcnIuYmlzcXVhcmUkdykKYml3ZWlnaHRzMiA8LSBiaXdlaWdodHNbb3JkZXIocnIuYmlzcXVhcmUkdyksIF0KYml3ZWlnaHRzMlsxOjE1LCBdCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZSB3ZWlnaHQgZ2l2ZW4gdG8gTWlzc2lzc2lwcGkgaXMgZHJhbWF0aWNhbGx5IGxvd2VyCnVzaW5nIHRoZSBiaXNxdWFyZSB3ZWlnaHRpbmcgZnVuY3Rpb24gdGhhbiB0aGUgSHViZXIgd2VpZ2h0aW5nIGZ1bmN0aW9uCmFuZCB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcyBmcm9tIHRoZXNlIHR3byBkaWZmZXJlbnQgd2VpZ2h0aW5nIG1ldGhvZHMKZGlmZmVyLgoKKipXaGVuIGNvbXBhcmluZyB0aGUgcmVzdWx0cyBvZiBhIHJlZ3VsYXIgT0xTIHJlZ3Jlc3Npb24gYW5kIGEgcm9idXN0CnJlZ3Jlc3Npb24sIGlmIHRoZSByZXN1bHRzIGFyZSB2ZXJ5IGRpZmZlcmVudCwgeW91IHdpbGwgbW9zdCBsaWtlbHkgd2FudAp0byB1c2UgdGhlIHJlc3VsdHMgZnJvbSB0aGUgcm9idXN0IHJlZ3Jlc3Npb24uKiogTGFyZ2UgZGlmZmVyZW5jZXMKc3VnZ2VzdCB0aGF0IHRoZSBtb2RlbCBwYXJhbWV0ZXJzIGFyZSBiZWluZyBoaWdobHkgaW5mbHVlbmNlZCBieQpvdXRsaWVycy4gRGlmZmVyZW50IGZ1bmN0aW9ucyBoYXZlIGFkdmFudGFnZXMgYW5kIGRyYXdiYWNrcy4gSHViZXIKd2VpZ2h0cyBjYW4gaGF2ZSBkaWZmaWN1bHRpZXMgd2l0aCBzZXZlcmUgb3V0bGllcnMsIGFuZCBiaXNxdWFyZSB3ZWlnaHRzCmNhbiBoYXZlIGRpZmZpY3VsdGllcyBjb252ZXJnaW5nLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBHTFMgZm9yIGhldGVyb3NjZWRhc3RpY2l0eQoKUm9idXN0IHJlZ3Jlc3Npb24gZG9lcyBub3QgZGlyZWN0bHkgYWRkcmVzcyBoZXRlcm9zY2VkYXN0aWNpdHkuIFRvCmFjY291bnQgZm9yIGhldGVyb3NjZWRhc3RpY2l0eSBvciBhdXRvY29ycmVsYXRpb24gaW4gR0xTLCB5b3Ugd291bGQKdHlwaWNhbGx5IG5lZWQgdG8gc3BlY2lmeSBhIHBhcnRpY3VsYXIgc3RydWN0dXJlIGZvciB0aGUgY292YXJpYW5jZQptYXRyaXggb2YgdGhlIGVycm9ycyB0aGF0IHJlZmxlY3RzIHRoZSBuYXR1cmUgb2YgdGhlIGhldGVyb3NjZWRhc3RpY2l0eQpvciBjb3JyZWxhdGlvbiB5b3UncmUgZGVhbGluZyB3aXRoLiBJbiBvdGhlciB3b3Jkcywgd2hhdCBpcyBkcml2aW5nIHRoZQpkaWZmZXJlbmNlIGluIHZhcmlhbmNlPwoKIVtIZXRlcm9za2VkYXN0aWNpdHkuXShoZXRlcm9za2VkYXN0aWNpdHkud2VicCkKCkNoZWNrIHRoZSByZXNpZHVhbHMgdnMgZml0dGVkIHBsb3QgdG8gc2VlIGlmIHRoZSBsaW5lIGlzIGN1cnZlZC4gVGhhdAp3b3VsZCBpbmRpY2F0ZSBoZXRlcm9zY2VkYXN0aWNpdHkKCmBgYHtyfQpyZXMgPC0gcmVzaWR1YWxzKG9scykKeWhhdCA8LSBmaXR0ZWQob2xzKQpwbG90KGNkYXRhJHBvdmVydHkscmVzLCB4bGFiPSJwb3ZlcnR5IiwgeWxhYj0icmVzaWR1YWxzIikKcGxvdChjZGF0YSRzaW5nbGUscmVzLCB4bGFiPSJzaW5nbGUiLCB5bGFiPSJyZXNpZHVhbHMiKQpgYGAKCiMjIyBCcmV1c2ggUGFnYW4gVGVzdAoKYGBge3J9CiMgd2h5IG1pZ2h0IHRoaXMgYmUgc2lnbmlmaWNhbnQ/CmJwdGVzdChvbHMpCmBgYAoKYGBge3J9CmdncGxvdChjZGF0YSwgYWVzKHggPSBzaW5nbGUsIHkgPSBjcmltZSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHBvdmVydHkpLCBhbHBoYSA9IDAuNSkgKyAgIyBDb2xvciBwb2ludHMgYnkgcG92ZXJ0eSBsZXZlbAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4geCwgc2UgPSBGQUxTRSwgY29sb3IgPSAiZ3JlZW4iKSArCiAgbGFicyh4ID0gIlNpbmdsZSBQYXJlbnQgUGVyY2VudGFnZSIsIHkgPSAiQ3JpbWUgUmF0ZSIsCiAgICAgICB0aXRsZSA9ICJDcmltZSBSYXRlIHZzLiBTaW5nbGUgUGFyZW50IFBlcmNlbnRhZ2UiLAogICAgICAgc3VidGl0bGUgPSAiQ29sb3JlZCBieSBQb3ZlcnR5IExldmVsIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyMgVXNpbmcgd2VpZ2h0cyBpbiB0aGUgR0xTIE1vZGVsCgpgYGB7cn0KIyBMZXQncyBzYXkgd2UgZmluZCB0aGF0IGFzIHNpbmdsZS1wYXJlbnQgaG91c2Vob2xkICUgaW5jcmVhc2VzLCBjcmltZSB2YXJpYW5jZSBpbmNyZWFzZXMuIFRoaXMgc3VnZ2VzdHMgYSBwcm9wb3J0aW9uYWwgcmVsYXRpb25zaGlwIGJldHdlZW4gc2luZ2xlLXBhcmVudCBhbmQgY3JpbWUsIHNvIHdlJ2xsIGFjY291bnQgZm9yIHZhcmlhbmNlIGNoYW5nZXMgYXMgYSBwb3dlciBmdW5jdGlvbiAKZ2xzTW9kZWwgPC0gZ2xzKGNyaW1lIH4gcG92ZXJ0eSArIHNpbmdsZSwgZGF0YSA9IGNkYXRhLCAKICAgICAgICAgICAgICAgd2VpZ2h0cyA9IHZhclBvd2VyKGZvcm0gPSB+IHNpbmdsZSkpCnN1bW1hcnkoZ2xzTW9kZWwpCgojIFBsb3R0aW5nIHN0YW5kYXJkaXplZCByZXNpZHVhbHMgdnMgZml0dGVkIHZhbHVlcwpwbG90KGZpdHRlZChnbHNNb2RlbCksIHJlc2lkdWFscyhnbHNNb2RlbCwgdHlwZSA9ICJub3JtYWxpemVkIiksCiAgICAgeGxhYiA9ICJGaXR0ZWQgdmFsdWVzIiwgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIikKYWJsaW5lKGggPSAwLCBsdHkgPSAyKQpgYGAKCmBgYHtyfQojIExldCdzIHNheSB0aGF0IHRoZSB2YXJpYWJpbGl0eSBpbiBjcmltZSBzY29yZXMgZGlmZmVycyBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBzdGF0ZSAoY291bGQgZm9ybWFsbHkgdGVzdCB3aXRoIGxldmVuZSdzIHRlc3QpLiBXZSBjb3VsZCBhY2NvdW50IGZvciB0aGF0IG9uIHRoZSB3ZWlnaHRzIGxpbmUuIE5vdGUgdGhpcyBkb2Vzbid0IHdvcmsgaGVyZSBiZWNhdXNlIGVhY2ggc3RhdGUgb25seSBoYXMgb25lIGVudHJ5LgpnbHNNb2RlbDIgPC0gZ2xzKGNyaW1lIH4gcG92ZXJ0eSArIHNpbmdsZSwgZGF0YSA9IGNkYXRhLCAKICAgICAgICAgICAgICAgd2VpZ2h0cyA9IHZhcklkZW50KGZvcm0gPSB+IDEgfCBzdGF0ZSkpCgpzdW1tYXJ5KGdsc01vZGVsMikKYGBgCgojIyBHZW5lcmF0aW5nIHJvYnVzdCBDSXMgd2l0aCBib290c3RyYXBwaW5nCgpCb290c3RyYXBwaW5nIGNhbiBiZSBoZWxwZnVsIGlmIHlvdSBoYXZlIGEgc21hbGwgc2FtcGxlIG9yIHlvdXIgZGF0YQp2aW9sYXRlIHRoZSByZWdyZXNzaW9uIGFzc3VtcHRpb25zIGFuZCB5b3Ugd2FudCB0byBnZXQgbW9yZSBhY2N1cmF0ZSBDSQplc3RpbWF0ZXMuIFdoaWxlIHRoaXMgd29uJ3QgY29ycmVjdCBmb3IgaGV0ZXJvZ2VuZWl0eSBvZiB2YXJpYW5jZSwKYm9vdHN0cmFwcGluZyBjYW4gYmUgdXNlZCB0byBlc3RpbWF0ZSByb2J1c3Qgc3RhbmRhcmQgZXJyb3JzIGZvcgpyZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4KCldoZW4gdG8gdXNlIGJvb3RzdHJhcHBpbmc6CgotICAgQXNzdW1wdGlvbnMgYXJlIHZpb2xhdGVkCi0gICBTYW1wbGUgc2l6ZSBpcyBzbWFsbAotICAgUHJlc2VuY2Ugb2Ygb3V0bGllcnMKCmBgYHtyfQpib290UmVnIDwtIGZ1bmN0aW9uKGZvcm11bGEsIGRhdGEsIGluZGljZXMpIHsKICBkIDwtIGRhdGFbaW5kaWNlcywgXSAgIyBVc2UgJ2luZGljZXMnIHRvIGNyZWF0ZSBhIHN1YnNldCBkYXRhZnJhbWUgJ2QnCiAgZml0IDwtIGxtKGZvcm11bGEsIGRhdGEgPSBkKQogIHJldHVybihjb2VmKGZpdCkpCn0KCmJvb3RSZXN1bHRzIDwtIGJvb3QgKHN0YXRpc3RpYyA9IGJvb3RSZWcsIGZvcm11bGEgPSBjcmltZSB+IHBvdmVydHkgKyBzaW5nbGUsIGRhdGEgPSBjZGF0YSwgUiA9IDIwMDApCgojIGJhc2VkIG9uIHRoZSBib290c3RyYXBwaW5nLCBob3cgYmlhc2VkIGFyZSBvdXIgZXN0aW1hdGVzPwpzdW1tYXJ5KGJvb3RSZXN1bHRzKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KG9scykKIyBpbnRlcmNlcHQgKGluZGV4ID0gMSkKYm9vdC5jaShib290UmVzdWx0cywgdHlwZSA9ICJiY2EiLCBpbmRleCA9IDEpICMgYmNhID0gYmlhcyBjb3JyZWN0ZWQgYW5kIGFjY2VsZXJhdGVkCmBgYAoKYGBge3J9CiMgcG92ZXJ0eSAoaW5kZXggPSAyKQpib290LmNpKGJvb3RSZXN1bHRzLCB0eXBlID0gImJjYSIsIGluZGV4ID0gMikgCmBgYAoKYGBge3J9CiMgc2luZ2xlIChpbmRleCA9IDMpCmJvb3QuY2koYm9vdFJlc3VsdHMsIHR5cGUgPSAiYmNhIiwgaW5kZXggPSAzKSAKYGBgCgpIb3cgZG8gdGhlc2UgY29tcGFyZSB0byB0aGUgT0xTIGNvbmZpZGVuY2UgaW50ZXJ2YWxzPwoKYGBge3J9CmNvbmZpbnQob2xzKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgWW91ciB0dXJuIQoKQSBzdHVkeSB3YXMgY2FycmllZCBvdXQgdG8gZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gQWdncmVzc2lvbgphbmQgc2V2ZXJhbCBwb3RlbnRpYWwgcHJlZGljdGluZyBmYWN0b3JzIGluIDY2NiBjaGlsZHJlbiB3aG8gaGFkIGFuCm9sZGVyIHNpYmxpbmcuCgpWYXJpYWJsZXMgbWVhc3VyZWQgd2VyZToKCi0gICBQYXJlbnRpbmdfU3R5bGUgKGhpZ2ggc2NvcmUgPSBiYWQgcGFyZW50aW5nIHByYWN0aWNlcykKLSAgIENvbXB1dGVyXF8gR2FtZXMgKGhpZ2ggc2NvcmUgPSBtb3JlIHRpbWUgc3BlbnQgcGxheWluZyBjb21wdXRlcgogICAgZ2FtZXMpCi0gICBUZWxldmlzaW9uIChoaWdoIHNjb3JlID0gbW9yZSB0aW1lIHNwZW50IHdhdGNoaW5nIHRlbGV2aXNpb24pCi0gICBEaWV0IChoaWdoIHNjb3JlID0gdGhlIGNoaWxkIGhhcyBhIGdvb2QgZGlldCBsb3cgaW4gYWRkaXRpdmVzKQotICAgU2libGluZ19BZ2dyZXNzaW9uIChoaWdoIHNjb3JlID0gbW9yZSBhZ2dyZXNzaW9uIHNlZW4gaW4gdGhlaXIgb2xkZXIKICAgIHNpYmxpbmcpLgoKKlRoZSBkYXRhIGFyZSBpbiB0aGUgZmlsZSBDaGlsZEFnZ3Jlc3Npb24uZGF0LioKCjEuICBSdW4gYSBtdWx0aXBsZSByZWdyZXNzaW9uIHRlc3RpbmcgdGhlIGVmZmVjdCBvZiB0aGVzZSB2YXJpYWJsZXMsCiAgICB1c2luZyAiQWdncmVzc2lvbiIgYXMgdGhlIERWLgoyLiAgT2J0YWluIHRoZSBzdGFuZGFyZGl6ZWQgcGFyYW1ldGVyIGVzdGltYXRlcyAoYWthIHN0YW5kYXJkaXplZAogICAgYmV0YXMpLgozLiAgQ2hlY2sgd2hldGhlciB0aGUgbW9kZWwgbWVldHMgdGhlIHJlZ3Jlc3Npb24gYXNzdW1wdGlvbnMgdXNpbmcgYm90aAogICAgcGxvdHMgYW5kIGZvcm1hbCB0ZXN0czogYS4gaG9tb3NjZWRhc3RpY2l0eSBiLiBtdWx0aWNvbGxpbmVhcml0eSBjLgogICAgaW5kZXBlbmRlbmNlIChubyBhdXRvY29ycmVsYXRpb24gaW4gcmVzaWR1YWxzKQo0LiAgUmVwb3J0IHlvdXIgcmVzdWx0cyAtLSBob3cgd291bGQgeW91IGludGVycHJldCB0aGUgUi1zcXVhcmVkPyBXaGljaAogICAgdmFyaWFibGVzIHNpZ25pZmljYW50bHkgcHJlZGljdGVkIGFnZ3Jlc3Npb24/CgpgYGB7cn0KZGYgPC0gcmVhZF9kZWxpbSgiL1VzZXJzL2thcmVlbmFkZWxyb3NhcmlvL0Rvd25sb2Fkcy9DaGlsZEFnZ3Jlc3Npb24uZGF0IiwgZGVsaW0gPSAiICIpCmBgYAoKYGBge3J9Cm1vZGVsIDwtIGxtKEFnZ3Jlc3Npb24gfiBUZWxldmlzaW9uICsgQ29tcHV0ZXJfR2FtZXMgKyBTaWJsaW5nX0FnZ3Jlc3Npb24gKyBEaWV0ICsgUGFyZW50aW5nX1N0eWxlLCBkYXRhID0gZGYpCgpzdW1tYXJ5KG1vZGVsKQpgYGAKCmBgYHtyfQpsbS5iZXRhKG1vZGVsKQpgYGAKCioqUmVzaWR1YWxzIHZzIEZpdHRlZDoqKiBpcyB1c2VkIHRvIGNoZWNrIHRoZSBhc3N1bXB0aW9ucyBvZiBsaW5lYXJpdHkuCklmIHRoZSByZXNpZHVhbHMgYXJlIHNwcmVhZCBlcXVhbGx5IGFyb3VuZCBhIGhvcml6b250YWwgbGluZSB3aXRob3V0CmRpc3RpbmN0IHBhdHRlcm5zIChyZWQgbGluZSBpcyBhcHByb3hpbWF0ZWx5IGhvcml6b250YWwgYXQgemVybyksIHRoYXQKaXMgYSBnb29kIGluZGljYXRpb24gb2YgaGF2aW5nIGEgbGluZWFyIHJlbGF0aW9uc2hpcC4KCioqTm9ybWFsIFEtUToqKiBpcyB1c2VkIHRvIGNoZWNrIHRoZSBub3JtYWxpdHkgb2YgcmVzaWR1YWxzIGFzc3VtcHRpb24uCklmIHRoZSBtYWpvcml0eSBvZiB0aGUgcmVzaWR1YWxzIGZvbGxvdyB0aGUgc3RyYWlnaHQgZGFzaGVkIGxpbmUsIHRoZW4KdGhlIGFzc3VtcHRpb24gaXMgZnVsZmlsbGVkLgoKKipTY2FsZS1Mb2NhdGlvbjoqKiBpcyB1c2VkIHRvIGNoZWNrIHRoZSBob21vc2NlZGFzdGljaXR5IG9mIHJlc2lkdWFscwooZXF1YWwgdmFyaWFuY2Ugb2YgcmVzaWR1YWxzKS4gSWYgdGhlIHJlc2lkdWFscyBhcmUgc3ByZWFkIHJhbmRvbWx5IGFuZAp0aGUgc2VlIGEgaG9yaXpvbnRhbCBsaW5lIHdpdGggZXF1YWxseSAocmFuZG9tbHkpIHNwcmVhZCBwb2ludHMsIHRoZW4KdGhlIGFzc3VtcHRpb24gaXMgZnVsZmlsbGVkLgoKKipSZXNpZHVhbHMgdnMgTGV2ZXJhZ2U6KiogaXMgdXNlZCB0byBpZGVudGlmeSBhbnkgaW5mbHVlbnRpYWwgdmFsdWUgaW4Kb3VyIGRhdGFzZXQuIEluZmx1ZW50aWFsIHZhbHVlcyBhcmUgZXh0cmVtZSB2YWx1ZXMgdGhhdCBtaWdodCBpbmZsdWVuY2UKdGhlIHJlZ3Jlc3Npb24gcmVzdWx0cyB3aGVuIGluY2x1ZGVkIG9yIGV4Y2x1ZGVkIGZyb20gdGhlIGFuYWx5c2lzLiBMb29rCmZvciBjYXNlcyBvdXRzaWRlIG9mIGEgZGFzaGVkIGxpbmUgKGlmIHRoZXJlIGlzIGEgY29uY2VybmluZyBwb2ludCwKeW91J2xsIHNlZSByZWQgbGluZXMgcG9wIHVwKS4KCkhvbW9zY2VkYXN0aWNpdHk6IHJlc2lkdWFscyB2cyBmaXR0ZWQgJiBicAoKYGBge3J9CnBsb3QobW9kZWwpCmBgYAoKYGBge3J9CmJwdGVzdChtb2RlbCkKYGBgCgpNdWx0aWNvbGxpbmVhcml0eQoKLSAgIElmIHRoZSBsYXJnZXN0IFZJRiBpcyBncmVhdGVyIDEwLCB0aGVuIHRoZXJlJ3MgY2F1c2UgZm9yIGNvbmNlcm4KLSAgIElmIHRoZSBhdmVyYWdlIFZJRiBpcyBzdWJzdGFudGlhbGx5IGdyZWF0ZXIgdGhhbiAxLCB0aGUgbW9kZWwgbWF5IGJlCiAgICBiaWFzZWQKLSAgIFRvbGVyYW5jZSBiZWxvdyAwLjEgbWVhbnMgdGhlcmUncyBhIHNlcmlvdXMgcHJvYmxlbQotICAgVG9sZXJhbmNlIGJlbG93IDAuMiBtZWFucyB0aGVyZSdzIGEgcG90ZW50aWFsIHByb2JsZW0KCmBgYHtyfQojIHZpZgp2aWYobW9kZWwpCgojIGF2ZXJhZ2UgdmlmCm1lYW4odmlmKG1vZGVsKSkKCiMgdG9sZXJhbmNlCjEvdmlmKG1vZGVsKQpgYGAKCkluZGVwZW5kZW5jZQoKYGBge3J9CmR1cmJpbldhdHNvblRlc3QobW9kZWwpCmBgYAoKU2tldwoKYGBge3J9Cmd2bG1hKG1vZGVsKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBDZW50ZXJpbmcsIHJlc2NhbGluZywgc3RhbmRhcmRpemluZywgY29udHJhc3QgY29kaW5nIHlvdXIgcHJlZGljdG9ycwoKKmFkYXB0ZWQgZnJvbToKPGh0dHBzOi8vYWR2c3RhdHMucHN5Y2hzdGF0Lm9yZy9ib29rL21yZWdyZXNzaW9uL2luZGV4LnBocD4qCgpOb3RlIHRoYXQgeW91IG5ldmVyIEhBVkUgdG8gY2VudGVyL3JlY29kZSB5b3VyIHByZWRpY3RvcnMuIEl0IHNob3VsZAoqKm5ldmVyKiogaW5mbHVlbmNlIHRoZSB0ZXN0IHNpZ25pZmljYW5jZS0tIGp1c3QgdGhlIGludGVycHJldGF0aW9uIG9mCnlvdXIgaW50ZXJjZXB0IGFuZCBjb2VmZmljaWVudHMuCgotICAgSWYgeW91IGhhdmUgKmNvbnRpbnVvdXMgcHJlZGljdG9ycyosIHlvdSBjYW4gKipjZW50ZXIqKi4KLSAgIElmIHlvdSBoYXZlICpjYXRlZ29yaWNhbCBwcmVkaWN0b3JzKiwgeW91IGNhbiAqKmR1bW15IGNvZGUqKiBvcgogICAgKiplZmZlY3RzIGNvZGUqKiAoc2VlIEFOT1ZBIGxhYnMgZm9yIGluZm8pLgotICAgSWYgeW91IGhhdmUgKnByZWRpY3RvcnMgd2l0aCBkaWZmZXJlbnQgc2NhbGVzKiwgeW91IGNhbiAqKnN0YW5kYXJkaXplKiouCgpMZXQncyBzYXkgd2UncmUgaW50ZXJlc3RlZCBpbiB0aGUgZWZmZWN0cyBvZiBoaWdoIHNjaG9vbCBHUEEgKGguZ3BhKSwKU0FULCBhbmQgcXVhbGl0eSBvZiByZWNvbW1lbmRhdGlvbiBsZXR0ZXJzIChyZWNvbW1kKSBvbiBjb2xsZWdlIEdQQQooYy5ncGEpLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmdwYV9kZiA8LSByZWFkLmNzdigiL1VzZXJzL2thcmVlbmFkZWxyb3NhcmlvL0Rvd25sb2Fkcy9ncGEuY3N2IikKCmhlYWQoZ3BhX2RmKQpgYGAKCmBgYHtyfQpwYXIobWZyb3c9YygyLDIpKQpwbG90KGdwYV9kZiRoLmdwYSwgZ3BhX2RmJGMuZ3BhKQpwbG90KGdwYV9kZiRTQVQsIGdwYV9kZiRjLmdwYSkKcGxvdChncGFfZGYkcmVjb21tZCwgZ3BhX2RmJGMuZ3BhKQoKCiMgb3JpZ2luYWwgbW9kZWwKZ3BhTW9kZWw8LSBsbShjLmdwYSB+IGguZ3BhICsgU0FUICsgcmVjb21tZCwgZGF0YSA9IGdwYV9kZikKCnN1bW1hcnkoZ3BhTW9kZWwpCmBgYAoKSG93IHdvdWxkIHlvdSBpbnRlcnByZXQgdGhlc2UgZXN0aW1hdGVzPwoKIyMjIyBDZW50ZXJpbmcKCmBgYHtyfQojIGNlbnRlciBzZXBhcmF0ZWx5CmdwYV9kZiRoLmdwYUMgPC0gc2NhbGUoZ3BhX2RmJGguZ3BhLCBjZW50ZXI9VFJVRSwgc2NhbGU9RkFMU0UpICMgbWVhbiBjZW50ZXIgd2l0aG91dCByZXNjYWxpbmcgCgojIHNob3J0Y3V0CmNlbnRlcmVkTW9kZWw8LSBsbShjLmdwYX4gaC5ncGFDICsgc2NhbGUoU0FULHNjYWxlPUYpICsgc2NhbGUocmVjb21tZCxzY2FsZT1GKSwgZGF0YT1ncGFfZGYpIAoKc3VtbWFyeShjZW50ZXJlZE1vZGVsKQpgYGAKCiMjIyMgU3RhbmRhcmRpemluZwoKV2hlbiBwcmVkaWN0b3JzIGxpa2UgR1BBICh0eXBpY2FsbHkgb24gYSAwIHRvIDQgc2NhbGUpIGFuZCBTQVQgc2NvcmVzCih0eXBpY2FsbHkgb24gYSA0MDAgdG8gMTYwMCBzY2FsZSkgYXJlIGludm9sdmVkLCB0aGVpciBjb2VmZmljaWVudHMgY2FuCmJlIGRpZmZpY3VsdCB0byBjb21wYXJlIGRpcmVjdGx5IGJlY2F1c2UgYSBvbmUtdW5pdCBjaGFuZ2UgbWVhbnMKc29tZXRoaW5nIHZlcnkgZGlmZmVyZW50IGZvciBlYWNoIHByZWRpY3Rvci4gVGhyb3VnaCBzdGFuZGFyZGl6YXRpb24sCmhvd2V2ZXIsIHdlIGNhbiAqcmVtb3ZlIHRoZSBzY2FsZXMgb2YgdGhlIHByZWRpY3RvcnMqIGFuZCB0aGVyZWZvcmUgbWFrZQp0aGUgY29lZmZpY2llbnRzIHJlbGF0aXZlbHkgbW9yZSBjb21wYXJhYmxlLiBXZSBjYW4gc3RhbmRhcmRpemUKcHJlZGljdG9ycyBvbmx5IG9yIGJvdGggcHJlZGljdG9ycyBhbmQgdGhlIG91dGNvbWUgdmFyaWFibGUuCgpBZnRlciBzdGFuZGFyZGl6YXRpb24sIHRoZSB2YXJpYWJsZSBtZWFucyBhcmUgYWxsIDAgYW5kIHZhcmlhbmNlcyBhcmUKYWxsIDEuIFRoZSBiZXRhIGNvZWZmaWNpZW50IChvdXRwdXQpLCB0ZWxscyB1cyBob3cgbWFueSBzdGFuZGFyZApkZXZpYXRpb25zIHRoZSBwcmVkaWN0ZWQgRFYgY2hhbmdlcyBnaXZlbiBvbmUgc3RhbmRhcmQgZGV2aWF0aW9uIGNoYW5nZQppbiB0aGUgSVYgd2hlbiB0aGUgb3RoZXIgSVZzIGFyZSBoZWxkIGNvbnN0YW50LgoKSW4gUiwgc2NhbGUoKSBjYW4gYWxzbyBiZSB1c2VkIGZvciBzdGFuZGFyZGl6YXRpb24uIE5vdGUgdGhhdCB0byBza2lwCnRoZSBlc3RpbWF0aW9uIG9mIHRoZSBpbnRlcmNlcHQsIG9uZSBjYW4gYWRkIC0xIGluIHRoZSByZWdyZXNzaW9uIG1vZGVsCmZvcm11bGFyLgoKYGBge3J9CiMgc2FtZSBhcyBhYm92ZSBleGNlcHQgd2UgcmVtb3ZlIHRoZSBzY2FsZSA9IEYgc3RhdGVtZW50CmJldGFNb2RlbDwtbG0oc2NhbGUoYy5ncGEpIH4gc2NhbGUoaC5ncGEpICsgc2NhbGUoU0FUKSArIHNjYWxlKHJlY29tbWQpLTEsIGRhdGE9Z3BhX2RmKQoKc3VtbWFyeShiZXRhTW9kZWwpCmBgYAoKQWx0ZXJuYXRpdmVseSwgeW91IGNvdWxkIGZlZWQgeW91ciBjZW50ZXJlZCBtb2RlbCB0aHJvdWdoIGxtLmJldGEoKSB0bwpnZXQgdGhlIGJldGFzIHdpdGhvdXQgcmVydW5uaW5nIHRoZSBtb2RlbAoKYGBge3J9CmxtLmJldGEoY2VudGVyZWRNb2RlbCkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgTW9kZXJhdGlvbiB2cyBNZWRpYXRpb24KCiptb2RlcmF0aW9uICYgbWVkaWF0aW9uIGNvbnRlbnQgd2FzIGFkYXB0ZWQgZnJvbToKPGh0dHBzOi8vYWRlbW9zLnBlb3BsZS51aWMuZWR1L0NoYXB0ZXIxNC5odG1sIz4qCgpZb3UgY2FuIHRoaW5rIG9mIHRoaXMgZGlzdGluY3Rpb24gYXMgbWVkaWF0aW9uIGFuc3dlcnMgKipob3cqKiB0d28KdmFyaWFibGVzIGFyZSByZWxhdGVkIHdoZXJlYXMgbW9kZXJhdGlvbiB0ZWxscyB1cyAqKml0IGRlcGVuZHMqKi4KCi0gICBNZWRpYXRpb24gYW5hbHlzaXMgdGVzdHMgZm9yIGEgaHlwb3RoZXRpY2FsIGNhdXNhbCByZWxhdGlvbnNoaXAgaW4KICAgIHdoaWNoIFggLVw+IFksIGFuZCB0aGUgdW5kZXJseWluZyBtZWNoYW5pc20gY2FuIGJlIGV4cGxhaW5lZCBieSBNLgotICAgTW9kZXJhdGlvbiBsb29rcyBhdCB0aGUgaW5mbHVlbmNlIG9mIFogb24gWC1cPlkgdG8gdGVzdCBmb3Igd2hlbiBvcgogICAgdW5kZXIgd2hhdCBjb25kaXRpb25zIGFuIGVmZmVjdCBvY2N1cnMuIE1vZGVyYXRvcnMgY2FuIHN0cmVuZ3RoZW4sCiAgICB3ZWFrZW4sIG9yIHJldmVyc2UgdGhlIG5hdHVyZSBvZiBhIHJlbGF0aW9uc2hpcC4KCk1lZGlhdG9ycyBkZXNjcmliZSB0aGUgaG93IG9yIHdoeSBvZiBhICh0eXBpY2FsbHkgd2VsbC1lc3RhYmxpc2hlZCkKcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIG90aGVyIHZhcmlhYmxlcyBhbmQgYXJlIHNvbWV0aW1lcyBjYWxsZWQKaW50ZXJtZWRpYXJ5IHZhcmlhYmxlcyBzaW5jZSB0aGV5IG9mdGVuIGRlc2NyaWJlIHRoZSBwcm9jZXNzIHRocm91Z2gKd2hpY2ggYW4gZWZmZWN0IG9jY3Vycy4gVGhpcyBpcyBhbHNvIHNvbWV0aW1lcyBjYWxsZWQgYW4gaW5kaXJlY3QKZWZmZWN0LiBGb3IgaW5zdGFuY2UsIHBlb3BsZSB3aXRoIGhpZ2hlciBpbmNvbWVzIHRlbmQgdG8gbGl2ZSBsb25nZXIgYnV0CnRoaXMgZWZmZWN0IGlzIGV4cGxhaW5lZCBieSB0aGUgbWVkaWF0aW5nIGluZmx1ZW5jZSBvZiBoYXZpbmcgYWNjZXNzIHRvCmJldHRlciBoZWFsdGggY2FyZS4KCiMgTW9kZXJhdGlvbiBBbmFseXNlcwoKTW9kZXJhdGlvbiB0ZXN0cyB3aGV0aGVyIGEgdmFyaWFibGUgKFopIGFmZmVjdHMgdGhlIGRpcmVjdGlvbiBhbmQvb3IKc3RyZW5ndGggb2YgdGhlIHJlbGF0aW9uIGJldHdlZW4gYW4gSVYgKFgpIGFuZCBhIERWIChZKS4gSW4gb3RoZXIgd29yZHMsCm1vZGVyYXRpb24gdGVzdHMgZm9yIGludGVyYWN0aW9ucyB0aGF0IGFmZmVjdCBXSEVOIHJlbGF0aW9uc2hpcHMgYmV0d2Vlbgp2YXJpYWJsZXMgb2NjdXIuIE1vZGVyYXRvcnMgYXJlIGNvbmNlcHR1YWxseSBkaWZmZXJlbnQgZnJvbSBtZWRpYXRvcnMKKHdoZW4gdmVyc3VzIGhvdy93aHkpIGJ1dCBzb21lIHZhcmlhYmxlcyBtYXkgYmUgYSBtb2RlcmF0b3Igb3IgYQptZWRpYXRvciBkZXBlbmRpbmcgb24geW91ciBxdWVzdGlvbi4KCkxpa2UgbWVkaWF0aW9uLCBtb2RlcmF0aW9uIGFzc3VtZXMgdGhhdCB0aGVyZSBpcyBsaXR0bGUgdG8gbm8KbWVhc3VyZW1lbnQgZXJyb3IgaW4gdGhlIG1vZGVyYXRvciB2YXJpYWJsZSBhbmQgdGhhdCB0aGUgRFYgZGlkIG5vdApDQVVTRSB0aGUgbW9kZXJhdG9yLgoKIVtCYXNpYyBNb2RlcmF0aW9uIE1vZGVsLl0obW9kZXJhdGlvbl9tb2RlbC5qcGVnKQoKIyMgRXhhbXBsZSBNb2RlcmF0aW9uIERhdGEKCkluIHRoaXMgZXhhbXBsZSB3ZSdsbCBzYXkgd2UgYXJlIGludGVyZXN0ZWQgaW4gd2hldGhlciB0aGUgcmVsYXRpb25zaGlwCmJldHdlZW4gdGhlICoqbnVtYmVyIG9mIGhvdXJzIG9mIHNsZWVwIChYKSoqIGEgZ3JhZHVhdGUgc3R1ZGVudCByZWNlaXZlcwphbmQgdGhlICoqYXR0ZW50aW9uIHRoYXQgdGhleSBwYXkgdG8gdGhpcyB0dXRvcmlhbCAoWSkqKiBpcyBpbmZsdWVuY2VkCmJ5IHRoZWlyICoqY29uc3VtcHRpb24gb2YgY29mZmVlIChaKSoqLgoKSGVyZSB3ZSBjcmVhdGUgdGhlIG1vZGVyYXRpb24gZWZmZWN0IGJ5IG1ha2luZyBvdXIgRFYgKFkpIHRoZSBwcm9kdWN0IG9mCmxldmVscyBvZiB0aGUgSVYgKFgpIGFuZCBvdXIgbW9kZXJhdG9yIChaKS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQoKc2V0LnNlZWQoMTIzKSNTdGFuZGFyZGl6ZXMgdGhlIG51bWJlcnMgZ2VuZXJhdGVkIGJ5IHJub3JtCgpOICA8LSAxMDAgI051bWJlciBvZiBwYXJ0aWNpcGFudHM7IGdyYWR1YXRlIHN0dWRlbnRzClguaG91cnMgIDwtIGFicyhybm9ybShOLCA2LCA0KSkgI0lWOyBIb3VycyBvZiBzbGVlcApYMSA8LSBhYnMocm5vcm0oTiwgNjAsIDMwKSkgI0FkZGluZyBzb21lIHN5c3RlbWF0aWMgdmFyaWFuY2UgZm9yIG91ciBEVgpaLmNvZmZlZSAgPC0gcm5vcm0oTiwgMzAsIDgpICNNb2RlcmF0b3I7IE91bmNlcyBvZiBjb2ZmZWUgY29uc3VtZWQKWS5hdHRuICA8LSBhYnMoKC0wLjgqWC5ob3VycykgKiAoMC4yKlouY29mZmVlKSAtIDAuNSpYLmhvdXJzIC0gMC40KlgxICsgMTAgKyBybm9ybShOLCAwLCAzKSkgI0RWOyBBdHRlbnRpb24gUGFpZApNb2RkYXRhIDwtIGRhdGEuZnJhbWUoWC5ob3VycywgWDEsIFouY29mZmVlLCBZLmF0dG4pCgpzdW1tYXJ5KE1vZGRhdGEpCmBgYAoKTW9kZXJhdGlvbiBjYW4gYmUgdGVzdGVkIGJ5IGxvb2tpbmcgZm9yIHNpZ25pZmljYW50IGludGVyYWN0aW9ucyBiZXR3ZWVuCnRoZSBtb2RlcmF0aW5nIHZhcmlhYmxlIChaKSBhbmQgdGhlIElWIChYKS4gKipJdCBpcyBpbXBvcnRhbnQgdG8gbWVhbgpjZW50ZXIgYm90aCB5b3VyIG1vZGVyYXRvciBhbmQgeW91ciBJViB0byByZWR1Y2UgbXVsdGljb2xsaW5lYXJpdHkgYW5kCm1ha2UgaW50ZXJwcmV0YXRpb24gZWFzaWVyLioqIENlbnRlcmluZyBjYW4gYmUgZG9uZSB1c2luZyB0aGUgKnNjYWxlKgpmdW5jdGlvbiwgd2hpY2ggc3VidHJhY3RzIHRoZSBtZWFuIG9mIGEgdmFyaWFibGUgZnJvbSBlYWNoIHZhbHVlIGluIHRoYXQKdmFyaWFibGUuCgpgYGB7cn0KI01vZGVyYXRpb24gCmZpdE1vZCA8LSBsbShZLmF0dG4gfiBYLmhvdXJzICsgWi5jb2ZmZWUgKyBYLmhvdXJzOlouY29mZmVlLCBkYXRhID0gTW9kZGF0YSkgI01vZGVsIGludGVyYWN0cyBJViAmIG1vZGVyYXRvcgoKIyBBbHRlcm5hdGl2ZSBmb3JtYXR0aW5nCiMgZml0TW9kIDwtIGxtKFkud2FrZSB+IFguaG91cnMqWi5jb2ZmZWUpIAoKCnByaW50X3AoY29lZihzdW1tYXJ5KGZpdE1vZCkpKQpgYGAKCiMjIyBIb3cgd291bGQgeW91IGludGVycHJldCB0aGUgY29lZmZpY2llbnRzIGFuZCBpbnRlcmNlcHQ/CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIENlbnRlcmluZyBwcmVkaWN0b3JzCgpTb21ldGltZXMgb3VyIGNvZWZmaWNpZW50cyBkb24ndCBtYWtlIGFueSBzZW5zZS4gV2UgY2FuIG1ha2UgdGhlIDAKbWVhbmluZ2Z1bCBieSBjZW50ZXJpbmcgb3VyIHByZWRpY3RvcnMuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyMjIFR3byB3YXlzIHRvIGNlbnRlcgpNb2RkYXRhIDwtIE1vZGRhdGEgJT4lIAogIG11dGF0ZShYLmhvdXJzQyA9IFguaG91cnMgLSBtZWFuKFguaG91cnMpLAogICAgICAgICBaLmNvZmZlZUMgPSBaLmNvZmZlZSAtIG1lYW4oWi5jb2ZmZWUpKQoKIyBDZW50ZXJpbmcgRGF0YQpYLmhvdXJzQyAgICA8LSBzY2FsZShYLmhvdXJzLCBjZW50ZXI9VFJVRSwgc2NhbGU9RkFMU0UpICNDZW50ZXJpbmcgSVY7IGhvdXJzIG9mIHNsZWVwClouY29mZmVlQyAgICA8LSBzY2FsZShaLmNvZmZlZSwgIGNlbnRlcj1UUlVFLCBzY2FsZT1GQUxTRSkgI0NlbnRlcmluZyBtb2RlcmF0b3I7IGNvZmZlZSBjb25zdW1wdGlvbgoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgUmVhbmFseXplIHdpdGggY2VudGVyZWQgdmFyaWFibGVzCmZpdE1vZEMgPC0gbG0oWS5hdHRuIH4gWC5ob3Vyc0MgKyBaLmNvZmZlZUMgKyBYLmhvdXJzQzpaLmNvZmZlZUMsIGRhdGEgPSBNb2RkYXRhKSAKcHJpbnRfcChjb2VmKHN1bW1hcnkoZml0TW9kQykpKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKYGBge3J9Cmd2bG1hKGZpdE1vZEMpICNkYXRhIGlzIHBvc2l0aXZlbHkgc2tld2VkOyBjb3VsZCBsb2cgdHJhbnNmb3JtIApgYGAKCmBgYHtyLCByZXN1bHRzID0gJ2FzaXMnfQoKI0RhdGEgU3VtbWFyeQpzdGFyZ2F6ZXIoZml0TW9kQywgdHlwZT0iaHRtbCIsIGRpZ2l0cyA9IDIsIGZvbnQuc2l6ZSA9ICJmb290bm90ZXNpemUiLCB0aXRsZSA9ICJTbGVlcCBhbmQgQ29mZmVlIG9uIEF0dGVudGlvbiIpCgojUGxvdHRpbmcKcHMgIDwtIHBsb3RTbG9wZXMoZml0TW9kQywgcGxvdHg9IlguaG91cnNDIiwgbW9keD0iWi5jb2ZmZWVDIiwgeGxhYiA9ICJTbGVlcCIsIHlsYWIgPSAiQXR0ZW50aW9uIFBhaWQiLCBtb2R4VmFscyA9ICJzdGQuZGV2IikKYGBgCgojIyBJbnRlcnByZXRpbmcgTW9kZXJhdGlvbiBSZXN1bHRzCgpPdXIgbW9kZWwgc2hvd3MgYSBzaWduaWZpY2FudCBpbnRlcmFjdGlvbiBiZXR3ZWVuIGhvdXJzIHNsZXB0IGFuZCBjb2ZmZWUKY29uc3VtcHRpb24gb24gYXR0ZW50aW9uIHBhaWQgdG8gdGhpcyB0dXRvcmlhbCAoYiA9IC4yMywgU0UgPSAuMDQsICpwKgpcPCAuMDAxKS4gSG93ZXZlciwgd2UnbGwgbmVlZCB0byB1bnBhY2sgdGhpcyBpbnRlcmFjdGlvbiB2aXN1YWxseSB0byBnZXQKYSBiZXR0ZXIgaWRlYSBvZiB3aGF0IHRoaXMgbWVhbnMuCgpUaGUgKnJvY2tjaGFsayogZnVuY3Rpb24gd2lsbCBhdXRvbWF0aWNhbGx5IHBsb3QgdGhlIHNpbXBsZSBzbG9wZXMgKDEgU0QKYWJvdmUgYW5kIDEgU0QgYmVsb3cgdGhlIG1lYW4pIG9mIHRoZSBtb2RlcmF0aW5nIGVmZmVjdC4gCgogICogVGhpcyBmaWd1cmUgc2hvd3MgdGhhdCB0aG9zZSB3aG8gZHJhbmsgbGVzcyBjb2ZmZWUgKHRoZSBibGFjayBsaW5lKSBwYWlkIG1vcmUKYXR0ZW50aW9uIHdpdGggdGhlIG1vcmUgc2xlZXAgdGhhdCB0aGV5IGdvdCBsYXN0IG5pZ2h0IGJ1dCBwYWlkIGxlc3MKYXR0ZW50aW9uIG92ZXJhbGwgdGhhdCBhdmVyYWdlICh0aGUgYmx1ZSBsaW5lKS4gCiAgKiBUaG9zZSB3aG8gZHJhbmsgbW9yZSBjb2ZmZWUgKHRoZSBncmVlbiBsaW5lKSBwYWlkIG1vcmUgYXR0ZW50aW9uIHdoZW4gdGhleSBzbGVwdCBtb3JlIGFzIHdlbGwgYW5kIHBhaWQgbW9yZSBhdHRlbnRpb24gdGhhbiBhdmVyYWdlLiAKICAqIFRoZSBkaWZmZXJlbmNlIGluIHRoZSBzbG9wZXMgZm9yIHRob3NlIHdobyBkcmFuayBtb3JlIG9yIGxlc3MgY29mZmVlIHNob3dzIHRoYXQgY29mZmVlIGNvbnN1bXB0aW9uIG1vZGVyYXRlcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaG91cnMgb2Ygc2xlZXAgYW5kIGF0dGVudGlvbiBwYWlkLgoKIyMgRGVjb21wb3NpbmcgaW50ZXJhY3Rpb25zCgoqY3JlZGl0OiA8aHR0cHM6Ly9zdGF0cy5vYXJjLnVjbGEuZWR1L3Ivc2VtaW5hcnMvaW50ZXJhY3Rpb25zLXIvI3MyPioKCllvdSBrbm93IHRoYXQgaG91cnMgc3BlbnQgZXhlcmNpc2luZyBpbXByb3ZlcyB3ZWlnaHQgbG9zcywgYnV0IGhvdyBkb2VzCml0IGludGVyYWN0IHdpdGggZWZmb3J0PyAKClRoaXMgaXMgYSBoeXBvdGhldGljYWwgc3R1ZHkgb2Ygd2VpZ2h0IGxvc3MgZm9yIDkwMCBwYXJ0aWNpcGFudHMgaW4gYQp5ZWFyLWxvbmcgc3R1ZHkgb2YgMyBkaWZmZXJlbnQgZXhlcmNpc2UgcHJvZ3JhbXMsIGEgam9nZ2luZyBwcm9ncmFtLCBhCnN3aW1taW5nIHByb2dyYW0sIGFuZCBhIHJlYWRpbmcgcHJvZ3JhbSB3aGljaCBzZXJ2ZXMgYXMgYSBjb250cm9sCmFjdGl2aXR5LiBWYXJpYWJsZXMgaW5jbHVkZQoKLSAgICoqbG9zczoqKiB3ZWlnaHQgbG9zcyAoY29udGludW91cyksIHBvc2l0aXZlID0gd2VpZ2h0IGxvc3MsIG5lZ2F0aXZlCiAgICBzY29yZXMgPSB3ZWlnaHQgZ2FpbgotICAgKipob3VyczoqKiBob3VycyBzcGVudCBleGVyY2lzaW5nIChjb250aW51b3VzKQotICAgKiplZmZvcnQ6KiogZWZmb3J0IGR1cmluZyBleGVyY2lzZSAoY29udGludW91cyksIDAgPSBtaW5pbWFsIHBoeXNpY2FsCiAgICBlZmZvcnQgYW5kIDUwID0gbWF4aW11bSBlZmZvcnQKLSAgICoqZ2VuZGVyOioqIHBhcnRpY2lwYW50IGdlbmRlciAoYmluYXJ5KQogICAgLSAgIG1hbGU9MQogICAgLSAgIGZlbWFsZT0yCgpgYGB7cn0KZGF0IDwtIHJlYWQuY3N2KCJodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvd3AtY29udGVudC91cGxvYWRzLzIwMTkvMDMvZXhlcmNpc2UuY3N2IikKCmRhdCRnZW5kZXIgPC0gZmFjdG9yKGRhdCRnZW5kZXIsbGFiZWxzPWMoIm1hbGUiLCJmZW1hbGUiKSkKYGBgCgoKIyMjIENvbnRpbnVvdXMgYnkgY29udGludW91cwoKKipIb3cgZG8gd2UgaW50ZXJwcmV0IGFuIGludGVyYWN0aW9uIHdpdGggY29udGludW91cyBwcmVkaWN0b3JzPyBBbmQKd2hhdCBpcyBleHRyYXBvbGF0aW5nPyoqCgokV2VpZ2h0XCBMb3NzID0gYl8wICsgYl8xSG91cnMgKyBiXzJFZmZvcnQgKyBiXzNIb3VycypFZmZvcnQkCgpgYGB7cn0KY29udGNvbnQgPC0gbG0obG9zc35ob3VycyplZmZvcnQsZGF0YT1kYXQpCnN1bW1hcnkoY29udGNvbnQpCmBgYAoKQWx0aG91Z2ggd2UgbWF5IHRoaW5rIHRoYXQgdGhlIHNsb3BlIG9mIEhvdXJzIHNob3VsZCBiZSBwb3NpdGl2ZSBhdApsZXZlbHMgb2YgRWZmb3J0LCByZW1lbWJlciB0aGF0IGluIHRoaXMgY2FzZSwgRWZmb3J0IGlzIHplcm8uIEFzIHdlIHNlZQppbiBvdXIgZGF0YSwgdGhpcyBpcyBpbXByb2JhYmxlIGFzIHRoZSBtaW5pbXVtIHZhbHVlIG9mIGVmZm9ydCBpcyAxMi45NS4KCmBgYHtyfQpzdW1tYXJ5KGRhdCRlZmZvcnQpCmBgYAoKV2hhdCB3b3VsZCB3ZSBleHBlY3Qgd2VpZ2h0IGxvc3MgdG8gbG9vayBsaWtlIGF0IGF2ZXJhZ2UgZWZmb3J0PwoKYGBge3J9CihteWxpc3QgPC0gbGlzdChob3Vycz0yLGVmZm9ydD0zMCkpCmVtbWVhbnMoY29udGNvbnQsIH4gaG91cnMqZWZmb3J0LCBhdD1teWxpc3QpCmBgYAoKVGhlIHJlc3VsdHMgc2hvdyB0aGF0IHByZWRpY3RlZCB3ZWlnaHQgbG9zcyBpcyAxMC4yIHBvdW5kcyBpZiB3ZSBwdXQgaW4KdHdvIGhvdXJzIG9mIGV4ZXJjaXNlIGFuZCBhbiBlZmZvcnQgbGV2ZWwgb2YgMzA7IHRoaXMgc2VlbXMgcmVhc29uYWJsZS4KTGV04oCZcyBzZWUgd2hhdCBoYXBwZW5zIHdoZW4gd2UgcHJlZGljdCB3ZWlnaHQgbG9zcyBmb3IgdHdvIGhvdXJzIG9mCmV4ZXJjaXNlIGdpdmVuIGFuIGVmZm9ydCBsZXZlbCBvZiAwLgoKYGBge3J9Cm15bGlzdCA8LSBsaXN0KGhvdXJzPTIsZWZmb3J0PTApIAplbW1lYW5zKGNvbnRjb250LCB+IGhvdXJzKmVmZm9ydCwgYXQ9bXlsaXN0KQpgYGAKClRoZSBwcmVkaWN0ZWQgd2VpZ2h0IGdhaW4gaXMgbm93IDExIHBvdW5kcy4gV2VpcmQsIHJpZ2h0PyBUaGlzIGlzIGFuCmV4YW1wbGUgb2YgKipleHRyYXBvbGF0aW5nKiosIHdoaWNoIG1lYW5zIHdlIGFyZSBtYWtpbmcgcHJlZGljdGlvbnMKYWJvdXQgb3VyIGRhdGEgYmV5b25kIHdoYXQgdGhlIGRhdGEgY2FuIHN1cHBvcnQuIFRoaXMgaXMgd2h5IHdlIHNob3VsZAphbHdheXMgY2hvb3NlIHJlYXNvbmFibGUgdmFsdWVzIG9mIG91ciBwcmVkaWN0b3JzIGluIG9yZGVyIHRvIGludGVycHJldApvdXIgZGF0YSBwcm9wZXJseS4KCiFbZXh0cmFwb2xhdGluZ10oZXh0cmFwb2xhdGluZy5wbmcpCgpXZSBrbm93IHRvIGNob29zZSByZWFzb25hYmxlIHZhbHVlcyB3aGVuIHByZWRpY3RpbmcgdmFsdWVzLiBUaGUgc2FtZQpjb25jZXB0IGFwcGxpZXMgd2hlbiAqZGVjb21wb3NpbmcgYW4gaW50ZXJhY3Rpb24uKiBPdXIgb3V0cHV0IHN1Z2dlc3RzCnRoYXQgSG91cnMgdmFyaWVzIGJ5IGxldmVscyBvZiBFZmZvcnQuIFNpbmNlIGVmZm9ydCBpcyBjb250aW51b3VzLCB3ZQpjYW4gY2hvb3NlIGFuIGluZmluaXRlIHNldCBvZiB2YWx1ZXMgd2l0aCB3aGljaCB0byBmaXggZWZmb3J0LgoKRm9yIGVhc2Ugb2YgcHJlc2VudGF0aW9uLCB3ZSBjYW4gdXNlIHdoYXQncyBjYWxsZWQgInNwb3RsaWdodCBhbmFseXNpcyIKKEFpa2VuICYgV2VzdCwgMTk5MSkuIFRvIGRvIGEgc3BvdGxpZ2h0IGFuYWx5c2lzLCB3ZSdsbCBkaXZpZGUgRWZmb3J0CmludG8gdGhyZWUgbGV2ZWxzOiBsb3csIGF2ZXJhZ2UsIGhpZ2guCgpcJEVmZkEgPSBcb3ZlcmxpbmV7RWZmb3J0fSArIFxzaWdtYShFZmZvcnQpIFwkCgpcJEVmZiA9IFxvdmVybGluZXtFZmZvcnR9IFwkCgpcJEVmZkIgPSBcb3ZlcmxpbmV7RWZmb3J0fSAtIFxzaWdtYShFZmZvcnQpIFwkCgpgYGB7cn0KIyBoaWdoIGVmZm9ydAplZmZhIDwtIG1lYW4oZGF0JGVmZm9ydCkgKyBzZChkYXQkZWZmb3J0KQooZWZmYXIgPC0gcm91bmQoZWZmYSwxKSkKCiMgYXZlcmFnZSBlZmZvcnQKZWZmIDwtIG1lYW4oZGF0JGVmZm9ydCkKKGVmZnIgPC0gcm91bmQoZWZmLDEpKQoKIyBsb3cgZWZmb3J0CmVmZmIgPC0gbWVhbihkYXQkZWZmb3J0KSAtIHNkKGRhdCRlZmZvcnQpCihlZmZiciA8LSByb3VuZChlZmZiLDEpKQpgYGAKClJlY2FsbCB0aGF0IG91ciBzaW1wbGUgc2xvcGUgaXMgdGhlIHJlbGF0aW9uc2hpcCBvZiBob3VycyBvbiB3ZWlnaHQgbG9zcwpmaXhlZCBhIHBhcnRpY3VsYXIgdmFsdWVzIG9mIGVmZm9ydC4gSW4gUiwgd2UgY2FuIG9idGFpbiBzaW1wbGUgc2xvcGVzCnVzaW5nIHRoZSBmdW5jdGlvbiAqZW10cmVuZHMuKiBXZSBmaXJzdCBjcmVhdGUgYSBsaXN0IHdoaWNoIGluY29ycG9yYXRlcwp0aGUgdGhyZWUgdmFsdWVzIG9mIGVmZm9ydCB3ZSBmb3VuZCBhYm92ZSBpbiBwcmVwYXJhdGlvbiBmb3Igc3BvdGxpZ2h0CmFuYWx5c2lzLgoKYGBge3J9Cm15bGlzdCA8LSBsaXN0KGVmZm9ydD1jKGVmZmJyLGVmZnIsZWZmYXIpKQoKZW10cmVuZHMoY29udGNvbnQsIHBhaXJ3aXNlIH5lZmZvcnQsIHZhcj0iaG91cnMiLGF0PW15bGlzdCwgYWRqdXN0PSJub25lIikKYGBgCgpBbGwgY29tcGFyaXNvbnMgb2Ygc2ltcGxlIHNsb3BlcyByZXN1bHQgaW4gdGhlIHNhbWUgcC12YWx1ZSBhcyB0aGUKaW50ZXJhY3Rpb24gaXRzZWxmLgoKQmFzZWQgb24gdGhlIHRlc3RzIGFib3ZlLCB5ZXMgdGhlIG1hZ2luaXR1ZGUgb2YgdGhlIHNsb3BlIGlzIGxhcmdlcgpiZXR3ZWVuIOKAnGxvd+KAnSBhbmQg4oCcaGlnaOKAnSB2ZXJzdXMg4oCcbWVkaXVt4oCdIGFuZCDigJxoaWdo4oCdLCBidXQgdGhlIHR3bwpwLXZhbHVlcyBhcmUgdGhlIHNhbWUuCgpgYGB7cn0KKG15bGlzdCA8LSBsaXN0KGhvdXJzPXNlcSgwLDQsYnk9MC40KSxlZmZvcnQ9YyhlZmZicixlZmZyLGVmZmFyKSkpCgplbW1pcChjb250Y29udCxlZmZvcnR+aG91cnMsYXQ9bXlsaXN0LCBDSXM9VFJVRSkKYGBgCgoqKlRoZSByZXN1bHRzIHN1Z2dlc3QgdGhhdCBob3VycyBzcGVudCBleGVyY2lzaW5nIGlzIG9ubHkgZWZmZWN0aXZlIGZvcgp3ZWlnaHQgbG9zcyBpZiB3ZSBwdXQgaW4gbW9yZSBlZmZvcnQsIHdoaWNoIHN1cHBvcnRzIHRoZSByYXRpb25hbGUgZm9yCmhpZ2ggaW50ZW5zaXR5IGludGVydmFsIHRyYWluaW5nLioqCgojIyBDb250aW51b3VzIGJ5IENhdGVnb3JpY2FsCgpUaGUgcmVzZWFyY2ggcXVlc3Rpb24gaGVyZSBpcywgZG8gbWVuIGFuZCB3b21lbiAoVykgZGlmZmVyIGluIHRoZQpyZWxhdGlvbnNoaXAgYmV0d2VlbiBIb3VycyAoWCkgYW5kIFdlaWdodCBsb3NzPyBIZXJlLCB3ZSdyZSBnb2luZyB0byB1c2UKZHVtbXkgY29kaW5nLgoKYGBge3J9CmRhdCRnZW5kZXIgPC0gcmVsZXZlbChkYXQkZ2VuZGVyLCByZWY9ImZlbWFsZSIpIAoKY29udGNhdCA8LSBsbShsb3NzfmhvdXJzKmdlbmRlcixkYXRhPWRhdCkKc3VtbWFyeShjb250Y2F0KQpgYGAKCiMjIyMgT2J0YWluaW5nIHNpbXBsZSBzbG9wZXMgYnkgZWFjaCBsZXZlbCBvZiB0aGUgY2F0ZWdvcmljYWwgbW9kZXJhdG9yCgpTaW5jZSBvdXIgZ29hbCBpcyB0byBvYnRhaW4gc2ltcGxlIHNsb3BlcyBvZiBIb3VycyBieSBnZW5kZXIgd2UgdXNlCmVtdHJlbmRzLiBXZSBkbyBub3QgdXNlIGVtbWVhbnMgYmVjYXVzZSB0aGlzIGZ1bmN0aW9uIGdpdmVzIHVzIHRoZQpwcmVkaWN0ZWQgdmFsdWVzIHJhdGhlciB0aGFuIHNsb3Blcy4KCmBgYHtyfQplbXRyZW5kcyhjb250Y2F0LCB+IGdlbmRlciwgdmFyPSJob3VycyIpCmBgYAoKQSBjb21tb24gbWlzY29uY2VwdGlvbiBpcyB0aGF0IHNpbmNlIHRoZSBzaW1wbGUgc2xvcGUgb2YgSG91cnMgaXMKc2lnbmlmaWNhbnQgZm9yIGZlbWFsZXMgYnV0IG5vdCBtYWxlcywgd2Ugc2hvdWxkIGhhdmUgc2VlbiBhIHNpZ25pZmljYW50CmludGVyYWN0aW9uLiBIb3dldmVyLCB0aGUgaW50ZXJhY3Rpb24gdGVzdHMgdGhlIGRpZmZlcmVuY2Ugb2YgdGhlIEhvdXJzCnNsb3BlIGZvciBtYWxlcyBhbmQgZmVtYWxlcyBhbmQgbm90IHdoZXRoZXIgZWFjaCBzaW1wbGUgc2xvcGUgaXMKZGlmZmVyZW50IGZyb20gemVybyAod2hpY2ggaXMgd2hhdCB3ZSBoYXZlIGZyb20gdGhlIG91dHB1dCBhYm92ZSkuCgpUbyB0ZXN0IHRoZSBkaWZmZXJlbmNlIGluIHNsb3Blcywgd2UgYWRkIHBhaXJ3aXNlIFx+IGdlbmRlciB0byB0ZWxsIHRoZQpmdW5jdGlvbiB0aGF0IHdlIHdhbnQgdGhlIHBhaXJ3aXNlIGRpZmZlcmVuY2UgaW4gdGhlIHNpbXBsZSBzbG9wZSBvZgpIb3VycyBmb3IgZmVtYWxlcyB2ZXJzdXMgbWFsZXMuCgpgYGB7cn0KZW10cmVuZHMoY29udGNhdCwgcGFpcndpc2UgfiBnZW5kZXIsIHZhcj0iaG91cnMiKQpgYGAKClJlY2FsbCBmcm9tIG91ciBzdW1tYXJ5IHRhYmxlLCB0aGlzIGlzIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlCmludGVyYWN0aW9uLCB3aGljaCB2ZXJpZmllcyB0aGF0IHdlIGhhdmUgaW4gZmFjdCBvYnRhaW5lZCB0aGUKaW50ZXJhY3Rpb24gY29lZmZpY2llbnQuCgpgYGB7cn0KKG15bGlzdCA8LSBsaXN0KGhvdXJzPXNlcSgwLDQsYnk9MC40KSxnZW5kZXI9YygiZmVtYWxlIiwibWFsZSIpKSkKZW1taXAoY29udGNhdCwgZ2VuZGVyIH5ob3VycywgYXQ9bXlsaXN0LENJcz1UUlVFKQpgYGAKCkFub3RoZXIgY29tbW9uIGFwcHJvYWNoIHRvIHBsb3R0aW5nIHByZWRpY3RlZCB2YWx1ZXM6CgpgYGB7cn0KbmV3ZGF0YSA8LSBleHBhbmQuZ3JpZChob3VycyA9IHNlcSgwLCA0LCBieSA9IDAuNCksIGdlbmRlciA9IGMoImZlbWFsZSIsICJtYWxlIikpCnByZWRpY3Rpb24gPC0gcHJlZGljdChjb250Y2F0LCBuZXdkYXRhLCBpbnRlcnZhbD0iY29uZmlkZW5jZSIsIGxldmVsID0gMC45NSkKCiMgY29tYmluZQpuZXdkYXRhIDwtIGNiaW5kKG5ld2RhdGEsIHByZWRpY3Rpb24pCgoKZ2dwbG90KG5ld2RhdGEsIGFlcyh4ID0gaG91cnMsIHkgPSBmaXQsIGNvbG9yID0gZ2VuZGVyKSkgKwogIGdlb21fbGluZShzaXplID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAyLCBzaGFwZSA9IDEsIGZpbGwgPSAid2hpdGUiKSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBsd3IsIHltYXggPSB1cHIsIGZpbGwgPSBnZW5kZXIpLCBhbHBoYSA9IDAuMiwgY29sb3IgPSBOQSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiM0ODc4RDAiLCAiI0Q2NUY1RiIpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzQ4NzhEMCIsICIjRDY1RjVGIikpICsKICBsYWJzKAogICAgdGl0bGUgPSAiUHJlZGljdGVkIExvc3MgYnkgSG91cnMgYW5kIEdlbmRlciIsCiAgICB4ID0gIkhvdXJzIiwKICAgIHkgPSAiUHJlZGljdGVkIExvc3MiLAogICAgY29sb3IgPSAiR2VuZGVyIiwKICAgIGZpbGwgPSAiR2VuZGVyIgogICkKCmBgYAoKIyMjIyBJbiBzdW0KCi0gICBQcmVkaWN0ZWQgdmFsdWVzIGFyZSBwb2ludHMgb24gdGhlIGdyYXBoLgotICAgU2ltcGxlIGVmZmVjdHMgb3Igc2xvcGVzIGFyZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHR3byBwcmVkaWN0ZWQKICAgIHZhbHVlcyAoaS5lLiwgdHdvIHBvaW50cykuCi0gICBJbnRlcmFjdGlvbnMgYXJlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gc2ltcGxlIGVmZmVjdHMgb2Ygc2xvcGVzOwogICAgLSAgIGZvciBhbiBpbnRlcmFjdGlvbiBpbnZvbHZpbmcgYSBjb250aW51b3VzIElWLCBpdOKAmXMgdGhlCiAgICAgICAgZGlmZmVyZW5jZSBvZiB0d28gc2xvcGVzIChpLmUuLCB0d28gbGluZXMpCiAgICAtICAgZm9yIGEgY2F0ZWdvcmljYWwgYnkgY2F0ZWdvcmljYWwgaW50ZXJhY3Rpb24sIHRoZSBpbnRlcmFjdGlvbiBpcwogICAgICAgIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBoZWlnaHQgb2YgdGhlIGJhcnMgaW4gb25lIGdyb3VwIHZlcnN1cyB0aGUKICAgICAgICBkaWZmZXJlbmNlIG9mIHRoZSBoZWlnaHRzIGluIGFub3RoZXIgZ3JvdXAgKGEuay5hLiwgZGlmZmVyZW5jZQogICAgICAgIG9mIGRpZmZlcmVuY2VzKS4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBNZWRpYXRpb24gQW5hbHlzZXMKClRoZSBCYXJvbiAmIEtlbm55IG1ldGhvZCBpcyBhbW9uZyB0aGUgb3JpZ2luYWwgbWV0aG9kcyBmb3IgdGVzdGluZyBmb3IKbWVkaWF0aW9uIGJ1dCB0ZW5kcyB0byBoYXZlIGxvdyBzdGF0aXN0aWNhbCBwb3dlci4gV2UncmUgY292ZXJpbmcgaXQKaGVyZSBiZWNhdXNlIGl0IHByb3ZpZGVzIGEgdmVyeSBjbGVhciBhcHByb2FjaCB0byBlc3RhYmxpc2hpbmcKcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyBhbmQgaXMgc3RpbGwgb2NjYXNpb25hbGx5IHJlcXVlc3RlZCBieQpyZXZpZXdlcnMuIAoKTWVkaWF0aW9uIHRlc3RzIHdoZXRoZXIgdGhlIGVmZmVjdHMgb2YgWCAodGhlIGluZGVwZW5kZW50IHZhcmlhYmxlKSBvbiBZCih0aGUgZGVwZW5kZW50IHZhcmlhYmxlKSBvcGVyYXRlIHRocm91Z2ggYSB0aGlyZCB2YXJpYWJsZSwgTSAodGhlCm1lZGlhdG9yKS4gSW4gdGhpcyB3YXksIG1lZGlhdG9ycyBleHBsYWluIHRoZSBjYXVzYWwgcmVsYXRpb25zaGlwCmJldHdlZW4gdHdvIHZhcmlhYmxlcyBvciAiaG93IiB0aGUgcmVsYXRpb25zaGlwIHdvcmtzLgoKIVtCYXNpYyBNZWRpYXRpb24gTW9kZWwuXShtZWRpYXRpb25fbW9kZWwucG5nKQoKYGBgICAgICAgICAgCmMgPSB0aGUgdG90YWwgZWZmZWN0IG9mIFggb24gWSB3aXRoIG5vIGNvbnNpZGVyYXRpb24gb2YgbWVkaWF0b3IgdmFyaWFibGVzCmMnPSB0aGUgZGlyZWN0IGVmZmVjdCBvZiBYIG9uIFkgYWZ0ZXIgY29udHJvbGxpbmcgZm9yIE0gCmFiPSB0aGUgaW5kaXJlY3QgZWZmZWN0IG9mIFggb24gWSB0aHJvdWdoIE0KCmBgYAoKVGhlIGFib3ZlIHNob3dzIHRoZSBzdGFuZGFyZCBtZWRpYXRpb24gbW9kZWwuICoqRnVsbCBtZWRpYXRpb24qKgpvY2N1cnMgd2hlbiB0aGUgZWZmZWN0IG9mIFggb24gWSBkaXNhcHBlYXJzIHdpdGggTSBpbiB0aGUgbW9kZWwuCioqUGFydGlhbCBtZWRpYXRpb24qKiBvY2N1cnMgd2hlbiB0aGUgZWZmZWN0IG9mIFggb24gWSBkZWNyZWFzZXMgYnkgYQpub250cml2aWFsIGFtb3VudCAodGhlIGFjdHVhbCBhbW91bnQgaXMgdXAgZm9yIGRlYmF0ZSkgd2l0aCBNIGluIHRoZQptb2RlbC4KCiMjIyBFeGFtcGxlIE1lZGlhdGlvbiBEYXRhCgpJbiB0aGlzIGV4YW1wbGUgd2UnbGwgc2F5IHdlIGFyZSBpbnRlcmVzdGVkIGluIHdoZXRoZXIgdGhlICoqbnVtYmVyIG9mCmhvdXJzIHNpbmNlIGRhd24gKFgpKiogYWZmZWN0IHRoZSAqKnN1YmplY3RpdmUgcmF0aW5ncyBvZiB3YWtlZnVsbmVzcwooWSkqKiAxMDAgZ3JhZHVhdGUgc3R1ZGVudHMgdGhyb3VnaCB0aGUgKipjb25zdW1wdGlvbiBvZiBjb2ZmZWUgKE0pLioqCgpOb3RlIHRoYXQgd2UgYXJlIGludGVudGlvbmFsbHkgY3JlYXRpbmcgYSBtZWRpYXRpb24gZWZmZWN0IGhlcmUgKGJlY2F1c2UKc3RhdGlzdGljcyBpcyBhbHdheXMgbW9yZSBmdW4gaWYgd2UgaGF2ZSBzb21ldGhpbmcgdG8gZmluZCkgYW5kIHdlIGRvIHNvCmJlbG93IGJ5IGNyZWF0aW5nIE0gc28gdGhhdCBpdCBpcyByZWxhdGVkIHRvIFggYW5kIFkgc28gdGhhdCBpdCBpcwpyZWxhdGVkIHRvIE0uIFRoaXMgY3JlYXRlcyB0aGUgY2F1c2FsIGNoYWluIGZvciBvdXIgYW5hbHlzaXMgdG8gcGFyc2UuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KI3NldHdkKCJ1c2VyIGxvY2F0aW9uIikgI1dvcmtpbmcgZGlyZWN0b3J5CnNldC5zZWVkKDEyMykgI1N0YW5kYXJkaXplcyB0aGUgbnVtYmVycyBnZW5lcmF0ZWQgYnkgcm5vcm07IHNlZSBDaGFwdGVyIDUKTiA8LSAxMDAgI051bWJlciBvZiBwYXJ0aWNpcGFudHM7IGdyYWR1YXRlIHN0dWRlbnRzClguaG91cnMgPC0gcm5vcm0oTiwgMTc1LCA3KSAjSVY7IGhvdXJzIHNpbmNlIGRhd24KTS5jb2ZmZWUgPC0gMC43KlguaG91cnMgKyBybm9ybShOLCAwLCA1KSAjU3VzcGVjdGVkIG1lZGlhdG9yOyBjb2ZmZWUgY29uc3VtcHRpb24gClkud2FrZSA8LSAwLjQqTS5jb2ZmZWUgKyBybm9ybShOLCAwLCA1KSAjRFY7IHdha2VmdWxuZXNzCk1lZGRhdGEgPC0gZGF0YS5mcmFtZShYLmhvdXJzLCBNLmNvZmZlZSwgWS53YWtlKQpgYGAKCiMjIE1ldGhvZCAxOiBCYXJvbiAmIEtlbm55CgpUaGlzIGlzIHRoZSBvcmlnaW5hbCA0LXN0ZXAgbWV0aG9kIHVzZWQgdG8gZGVzY3JpYmUgYSBtZWRpYXRpb24gZWZmZWN0LgpTdGVwcyAxIGFuZCAyIHVzZSBiYXNpYyBsaW5lYXIgcmVncmVzc2lvbiB3aGlsZSBzdGVwcyAzIGFuZCA0IHVzZQptdWx0aXBsZSByZWdyZXNzaW9uLgoKCk5vdywgbGV0J3MgYXBwbHkgdGhlIEJhcm9uICYgS2VubnkgbWV0aG9kIHRvIG91ciBkYXRhOgoKVGhlIFN0ZXBzOgoKMS4gIEVzdGltYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBYIG9uIFkgKGhvdXJzIHNpbmNlIGRhd24gb24gZGVncmVlCiAgICBvZiB3YWtlZnVsbmVzcykKICAgIC0gICBQYXRoICJjIiBtdXN0IGJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gMDsgbXVzdCBoYXZlIGEKICAgICAgICAqKnRvdGFsIGVmZmVjdCBiZXR3ZWVuIHRoZSBJViAmIERWKioKICAgIC0gICAqKlkgXH4gWCA9IFNJR05JRklDQU5UKioKMi4gIEVzdGltYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBYIG9uIE0gKGhvdXJzIHNpbmNlIGRhd24gb24gY29mZmVlCiAgICBjb25zdW1wdGlvbikKICAgIC0gICBQYXRoICJhIiBtdXN0IGJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gMDsgKipJViBhbmQKICAgICAgICBtZWRpYXRvciBtdXN0IGJlIHJlbGF0ZWQuKioKICAgIC0gICAqKk0gXH4gWCA9IFNJR05JRklDQU5UKioKMy4gIEVzdGltYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBNIG9uIFkgY29udHJvbGxpbmcgZm9yIFggKGNvZmZlZQogICAgY29uc3VtcHRpb24gb24gd2FrZWZ1bG5lc3MsIGNvbnRyb2xsaW5nIGZvciBob3VycyBzaW5jZSBkYXduKQogICAgLSAgIFBhdGggImIiIG11c3QgYmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSAwOyAqKm1lZGlhdG9yIGFuZAogICAgICAgIERWIG11c3QgYmUgcmVsYXRlZC4qKgogICAgLSAgICoqWSBcfiBNICsgWCA9IFNJR05JRklDQU5UKioKNC4gIEVzdGltYXRlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBZIG9uIFggY29udHJvbGxpbmcgZm9yIE0KICAgICh3YWtlZnVsbmVzcyBvbiBob3VycyBzaW5jZSBkYXduLCBjb250cm9sbGluZyBmb3IgY29mZmVlCiAgICBjb25zdW1wdGlvbikKICAgIC0gICBTaG91bGQgYmUgKipub24tc2lnbmlmaWNhbnQqKiBhbmQgbmVhcmx5IDAuCiAgICAtICAgKipYIFx+IFkgKyBNID0gTk9OLVNJR05JRklDQU5UKioKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQojMS4gVG90YWwgRWZmZWN0CmZpdCA8LSBsbShZLndha2UgfiBYLmhvdXJzLCBkYXRhPU1lZGRhdGEpCgojMi4gUGF0aCBBIChYIG9uIE0pCmZpdGEgPC0gbG0oTS5jb2ZmZWUgfiBYLmhvdXJzLCBkYXRhPU1lZGRhdGEpCgojMy4gUGF0aCBCIChNIG9uIFksIGNvbnRyb2xsaW5nIGZvciBYKQpmaXRiIDwtIGxtKFkud2FrZSB+IE0uY29mZmVlICsgWC5ob3VycywgZGF0YT1NZWRkYXRhKQoKIzQuIFJldmVyc2VkIFBhdGggQyAoWSBvbiBYLCBjb250cm9sbGluZyBmb3IgTSkKZml0YyA8LSBsbShYLmhvdXJzIH4gWS53YWtlICsgTS5jb2ZmZWUsIGRhdGE9TWVkZGF0YSkKYGBgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9CiNTdW1tYXJ5IFRhYmxlCnN0YXJnYXplcjo6c3RhcmdhemVyKGZpdCwgZml0YSwgZml0YiwgZml0YywgdHlwZT0iaHRtbCIsIGRpZ2l0cyA9IDIsIGZvbnQuc2l6ZSA9ICJmb290bm90ZXNpemUiLHRpdGxlID0gIkJhcm9uIGFuZCBLZW5ueSBNZXRob2QiKQoKYGBgCgojIyMgSW50ZXJwcmV0aW5nIEJhcm9uICYgS2VubnkgUmVzdWx0cwoKLSAgIEhlcmUgd2UgZmluZCB0aGF0IG91ciB0b3RhbCBlZmZlY3QgbW9kZWwgc2hvd3MgYSBzaWduaWZpY2FudAogICAgKipwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBob3VycyBzaW5jZSBkYXduIChYKSBhbmQgd2FrZWZ1bG5lc3MKICAgIChZKSoqLgotICAgT3VyIFBhdGggQSBtb2RlbCBzaG93cyB0aGF0ICoqaG91cnMgc2luY2UgZGF3biAoWCkgaXMgYWxzbwogICAgcG9zaXRpdmVseSByZWxhdGVkIHRvIGNvZmZlZSBjb25zdW1wdGlvbiAoTSkqKi4KLSAgIE91ciBQYXRoIEIgbW9kZWwgdGhlbiBzaG93cyB0aGF0ICoqY29mZmVlIGNvbnN1bXB0aW9uIChNKSBwb3NpdGl2ZWx5CiAgICBwcmVkaWN0cyB3YWtlZnVsbmVzcyAoWSkgd2hlbiBjb250cm9sbGluZyBmb3IgaG91cnMgc2luY2UgZGF3bgogICAgKFgpKiouCi0gICBGaW5hbGx5LCAqKndha2VmdWxuZXNzIChZKSBkb2VzIG5vdCBwcmVkaWN0IGhvdXJzIHNpbmNlIGRhd24gKFgpCiAgICB3aGVuIGNvbnRyb2xsaW5nIGZvciBjb2ZmZWUgY29uc3VtcHRpb24gKE0pKiouCgpTaW5jZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaG91cnMgc2luY2UgZGF3biBhbmQgd2FrZWZ1bG5lc3MgaXMgbm8KbG9uZ2VyIHNpZ25pZmljYW50IHdoZW4gY29udHJvbGxpbmcgZm9yIGNvZmZlZSBjb25zdW1wdGlvbiwgdGhpcwpzdWdnZXN0cyB0aGF0IGNvZmZlZSBjb25zdW1wdGlvbiBkb2VzIGluIGZhY3QgbWVkaWF0ZSB0aGlzIHJlbGF0aW9uc2hpcC4KSG93ZXZlciwgdGhpcyBtZXRob2QgYWxvbmUgZG9lcyBub3QgYWxsb3cgZm9yIGEgZm9ybWFsIHRlc3Qgb2YgdGhlCmluZGlyZWN0IGVmZmVjdCBzbyB3ZSBkb24ndCBrbm93IGlmIHRoZSBjaGFuZ2UgaW4gdGhpcyByZWxhdGlvbnNoaXAgaXMKdHJ1bHkgbWVhbmluZ2Z1bC4KClRoZXJlIGFyZSB0d28gcHJpbWFyeSBtZXRob2RzIGZvciBmb3JtYWxseSB0ZXN0aW5nIHRoZSBzaWduaWZpY2FuY2Ugb2YKdGhlIGluZGlyZWN0IHRlc3Q6IHRoZSBTb2JlbCB0ZXN0ICYgYm9vdHN0cmFwcGluZyAoY292ZXJlZCB1bmRlciB0aGUKKm1lZGlhdGlvbiogbWV0aG9kKS4gVGhlc2UgYXJlIGNvbnNpZGVyZWQgb3V0ZGF0ZWQsIHNvIHdlJ3JlIGdvaW5nIHRvCm1vdmUgb250byBvdXIgcHJlZmVycmVkIG1ldGhvZC4KCiMjIE1ldGhvZCAyOiBUaGUgKk1lZGlhdGlvbiogUGFja2FnZSBNZXRob2QKClRoaXMgcGFja2FnZSB1c2VzIHRoZSBtb3JlIHJlY2VudCBib290c3RyYXBwaW5nIG1ldGhvZCBvZiBQcmVhY2hlciAmCkhheWVzICgyMDA0KSB0byBhZGRyZXNzIHRoZSBwb3dlciBsaW1pdGF0aW9ucyBvZiB0aGUgU29iZWwgVGVzdC4gVGhpcwptZXRob2QgY29tcHV0ZXMgdGhlIHBvaW50IGVzdGltYXRlIG9mIHRoZSBpbmRpcmVjdCBlZmZlY3QgKGFiKSBvdmVyIGEKbGFyZ2UgbnVtYmVyIG9mIHJhbmRvbSBzYW1wbGUgKHR5cGljYWxseSAxMDAwKSBzbyBpdCBkb2VzIG5vdCBhc3N1bWUKdGhhdCB0aGUgZGF0YSBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYW5kIGlzIGVzcGVjaWFsbHkgbW9yZSBzdWl0YWJsZQpmb3Igc21hbGwgc2FtcGxlIHNpemVzIHRoYW4gdGhlIEJhcm9uICYgS2VubnkgbWV0aG9kLgoKVG8gcnVuIHRoZSAqbWVkaWF0ZSogZnVuY3Rpb24sIHdlIHdpbGwgYWdhaW4gbmVlZCBhIG1vZGVsIG9mIG91ciBJVgooaG91cnMgc2luY2UgZGF3biksIHByZWRpY3Rpbmcgb3VyIG1lZGlhdG9yIChjb2ZmZWUgY29uc3VtcHRpb24pIGxpa2UKb3VyIFBhdGggQSBtb2RlbCBhYm92ZS4gV2Ugd2lsbCBhbHNvIG5lZWQgYSBtb2RlbCBvZiB0aGUgZGlyZWN0IGVmZmVjdApvZiBvdXIgSVYgKGhvdXJzIHNpbmNlIGRhd24pIG9uIG91ciBEViAod2FrZWZ1bG5lc3MpLCB3aGVuIGNvbnRyb2xsaW5nCmZvciBvdXIgbWVkaWF0b3IgKGNvZmZlZSBjb25zdW1wdGlvbikuIFdoZW4gY2FuIHRoZW4gdXNlICptZWRpYXRlKiB0bwpyZXBlYXRlZGx5IHNpbXVsYXRlIGEgY29tcGFyc2lvbiBiZXR3ZWVuIHRoZXNlIG1vZGVscyBhbmQgdG8gdGVzdCB0aGUKc2lnbmlmaWNhbmNlIG9mIHRoZSBpbmRpcmVjdCBlZmZlY3Qgb2YgY29mZmVlIGNvbnN1bXB0aW9uLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CgojIFBhdGggQQpmaXRNIDwtIGxtKE0uY29mZmVlIH4gWC5ob3VycywgICAgIGRhdGE9TWVkZGF0YSkgI0lWIG9uIE07IEhvdXJzIHNpbmNlIGRhd24gcHJlZGljdGluZyBjb2ZmZWUgY29uc3VtcHRpb24KCiMgVG90YWwKZml0WSA8LSBsbShZLndha2UgfiBYLmhvdXJzICsgTS5jb2ZmZWUsIGRhdGE9TWVkZGF0YSkgI0lWIGFuZCBNIG9uIERWOyBIb3VycyBzaW5jZSBkYXduIGFuZCBjb2ZmZWUgcHJlZGljdGluZyB3YWtlZnVsbmVzcwoKIyBDaGVjayBza2V3Cmd2bG1hKGZpdE0pICNkYXRhIGlzIHBvc2l0aXZlbHkgc2tld2VkOyBjb3VsZCBsb2cgdHJhbnNmb3JtCmd2bG1hKGZpdFkpCgpmaXRNZWQgPC0gbWVkaWF0ZShmaXRNLCBmaXRZLCB0cmVhdD0iWC5ob3VycyIsIG1lZGlhdG9yPSJNLmNvZmZlZSIpCnN1bW1hcnkoZml0TWVkKQpwbG90KGZpdE1lZCkKYGBgCgpgYGB7cn0KI0Jvb3RzdHJhcApmaXRNZWRCb290IDwtIG1lZGlhdGUoZml0TSwgZml0WSwgYm9vdD1UUlVFLCBzaW1zPTIwMDAsIHRyZWF0PSJYLmhvdXJzIiwgbWVkaWF0b3I9Ik0uY29mZmVlIikKc3VtbWFyeShmaXRNZWRCb290KQoKcGxvdChmaXRNZWRCb290KQpgYGAKCiMjIyBJbnRlcnByZXRpbmcgKk1lZGlhdGlvbiogUmVzdWx0cwoKVGhlICptZWRpYXRlKiBmdW5jdGlvbiBnaXZlcyB1czoKCi0gICBBdmVyYWdlIENhdXNhbCBNZWRpYXRpb24gRWZmZWN0cyAoQUNNRSkKLSAgIEF2ZXJhZ2UgRGlyZWN0IEVmZmVjdHMgKEFERSksIG91ciBjb21iaW5lZCBpbmRpcmVjdCBhbmQgZGlyZWN0CiAgICBlZmZlY3RzIChUb3RhbCBFZmZlY3QpCi0gICB0aGUgcmF0aW8gb2YgdGhlc2UgZXN0aW1hdGVzIChQcm9wLiBNZWRpYXRlZCkKClRoZSBBQ01FIGhlcmUgaXMgdGhlIGluZGlyZWN0IGVmZmVjdCBvZiBNICh0b3RhbCBlZmZlY3QgLSBkaXJlY3QgZWZmZWN0KQphbmQgdGh1cyB0aGlzIHZhbHVlIHRlbGxzIHVzIGlmIG91ciBtZWRpYXRpb24gZWZmZWN0IGlzIHNpZ25pZmljYW50LiBJZgpBQ01FIGlzIHNpZ25pZmljYW50LCBidXQgQURFIGlzIG5vdCwgdGhlbiB3ZSBoYXZlIGZ1bGwgbWVkaWF0aW9uLiBJZiBBREUKaXMgc2lnbmlmaWNhbnQsIGJ1dCBBQ01FIGlzIG5vdCwgdGhlbiB3ZSBoYXZlIG5vIG1lZGlhdGlvbi4KCkluIHRoaXMgY2FzZSwgb3VyICoqZml0TWVkKiogbW9kZWwgYWdhaW4gc2hvd3MgYSBzaWduaWZpY2FudCBlZmZlY3Qgb2YKY29mZmVlIGNvbnN1bXB0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBob3VycyBzaW5jZSBkYXduIGFuZApmZWVsaW5ncyBvZiB3YWtlZnVsbmVzcywgKEFDTUUgPSAuMjgsICpwKiBcPCAuMDAxKSB3aXRoIG5vIGRpcmVjdCBlZmZlY3QKb2YgaG91cnMgc2luY2UgZGF3biAoQURFID0gLTAuMTEsICpwKiA9IC4yNykgYW5kIHNpZ25pZmljYW50IHRvdGFsCmVmZmVjdCAoKnAqIFw8IC4wNSkuCgpXZSBjYW4gdGhlbiBib290c3RyYXAgdGhpcyBjb21wYXJpc29uIHRvIHZlcmlmeSB0aGlzIHJlc3VsdCBpbgoqKmZpdE1lZEJvb3QqKiBhbmQgYWdhaW4gZmluZCBhIHNpZ25pZmljYW50IG1lZGlhdGlvbiBlZmZlY3QgKEFDTUUgPQouMjgsICpwKiBcPCAuMDAxKSBhbmQgbm8gZGlyZWN0IGVmZmVjdCBvZiBob3VycyBzaW5jZSBkYXduIChBREUgPSAtMC4xMSwKKnAqID0gLjI3KS4gSG93ZXZlciwgd2l0aCBpbmNyZWFzZWQgcG93ZXIsIHRoaXMgYW5hbHlzaXMgbm8gbG9uZ2VyIHNob3dzCmEgc2lnbmlmaWNhbnQgdG90YWwgZWZmZWN0ICgqcCogPSAuMDcpLgoKIyMgTWV0aG9kIDM6IE1lZGlhdGlvbiB1c2luZyBsYXZhYW4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGxhdmFhbikKCm1vZGVsIDwtIgpZLndha2UgfiBhKlguaG91cnMgKyBiKk0uY29mZmVlCk0uY29mZmVlIH4gYypYLmhvdXJzCgojaW5kaXJlY3QgZWZmZWN0CmluZDo9YipjCgojdG90YWwgZWZmZWN0CnRvdGFsOj1hKyhiKmMpCiIKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWVkc2VtPC1zZW0obW9kZWwgPSBtb2RlbCwgZGF0YSA9IE1lZGRhdGEsIHNlPSJib290c3RyYXAiKQoKc3VtbWFyeShtZWRzZW0sc3RhbmRhcmRpemVkPVQsZml0Lm1lYXN1cmVzPVQscnNxdWFyZT1UKQpgYGAKCmBgYHtyfQpzZW1QYXRocyhvYmplY3QgPSBtZWRzZW0sIHdoYXRMYWJlbHMgPSAicGFyIikgIyAic3RkIiA9IHN0YW5kYXJkaXplZCBwYXJhbWV0ZXIgZXN0aW1hdGVzCgpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9Cm1lZGlhdGlvblBsb3QobWVkc2VtLCBpbmRpcmVjdCA9IFRSVUUsIHdoYXRMYWJlbHMgPSAiZXN0IikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgT3RoZXIgdHlwZXMgb2YgbWVkaWF0aW9uIHsudGFic2V0fQoKIyMgTWVkaWF0aW9uIHdpdGggQ2F0ZWdvcmljYWwgUHJlZGljdG9ycwoKV2hlbiB5b3UgaGF2ZSBjYXRlZ29yaWNhbCBwcmVkaWN0b3JzIHdpdGggbW9yZSB0aGFuIDIgbGV2ZWxzLCB5b3Ugd2lsbCBuZWVkIHRvIG1vZGVsIHRoZSBtZWRpYXRpb24gZWZmZWN0cyBmb3IgZWFjaCBsZXZlbCBvZiB0aGF0IHZhcmlhYmxlLiBUbyBkbyB0aGF0LCB3ZSdyZSBnb2luZyB0byBkdW1teSBjb2RlIG91ciBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gV2UncmUgZ29pbmcgdG8gdXNlIHRoZSBzYW1lIGRhdGFzZXQgYnV0IGNyZWF0ZSBhIDMtbGV2ZWwgcHJlZGljdG9yOiBhIGxvdCBvZiBzbGVlcCwgYXZlcmFnZSBzbGVlcCwgbGl0dGxlIHNsZWVwLiBJIGtub3cgdGhpcyB3b3VsZCBiZSBjb25zaWRlcmVkIG9yZGluYWwsIGJ1dCBsZXQncyBpZ25vcmUgdGhhdCBmb3Igbm93LgoKKipJTVBPUlRBTlQgTk9URVM6KioKCiAgKiBXaGVuIGR1bW15IGNvZGluZyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIHdpdGggSyBjYXRlZ29yaWVzLCBpbmNsdWRlIG9ubHkgS+KIkjEgZHVtbXkgdmFyaWFibGVzIGluIHRoZSBtb2RlbCwgdHJlYXRpbmcgb25lIGNhdGVnb3J5IGFzIGEgcmVmZXJlbmNlLiBUaGlzIGF2b2lkcyB0aGUgaXNzdWUgb2YgcGVyZmVjdCBtdWx0aWNvbGxpbmVhcml0eS4KICAgICsgSWYgYWxsIGR1bW15LWNvZGVkIHZhcmlhYmxlcyBmb3IgYSBjYXRlZ29yaWNhbCBwcmVkaWN0b3IgYXJlIGluY2x1ZGVkIGluIHRoZSBtb2RlbCwgdGhpcyB2aW9sYXRlcyB0aGUgYXNzdW1wdGlvbiBvZiBpbmRlcGVuZGVuY2UgYmVjYXVzZSB0aGUgc3VtIG9mIGFsbCBkdW1teSB2YXJpYWJsZXMgZm9yIGEgZ2l2ZW4gb2JzZXJ2YXRpb24gaXMgYWx3YXlzIDEgKGlmIHlvdSBpbmNsdWRlIGEgZHVtbXkgZm9yIGVhY2ggY2F0ZWdvcnkpLiBJbiBvdGhlciB3b3JkcywgeW91J2xsIGdldCBwZXJmZWN0IG11bHRpY29sbGluZWFyaXR5LCB3aGVyZSBvbmUgdmFyaWFibGUgY2FuIGJlIHBlcmZlY3RseSBwcmVkaWN0ZWQgZnJvbSB0aGUgb3RoZXJzLCBsZWFkaW5nIHRvIGlzc3VlcyBpbiBlc3RpbWF0aW5nIHRoZSBtb2RlbCBwYXJhbWV0ZXJzLgogICAgKyBUaGUgaW50ZXJwcmV0YXRpb24gb2YgdGhlc2UgY29lZmZpY2llbnRzIGFyZSBub3cgaW4gcmVmZXJlbmNlIHRvIHRoZSBleGNsdWRlZCBkdW1teSAoaS5lLiwgdGhlIHJlZmVyZW5jZSBjYXRlZ29yeSkuIAoKIyMjIyBMYXZhYW4KCkhlcmUsIHRoZSByZWZlcmVuY2UgZ3JvdXAgd2lsbCBiZSB0aGUgcGVvcGxlIHdobyBnb3QgJ2EgbG90JyBvZiBzbGVlcCAoc2xlZXA9MCkKCmBgYHtyfQojIGNyZWF0aW5nIG91ciBkYXRhc2V0CnNldC5zZWVkKDEyMykKTiA8LSAxMDAgCgojIHNsZWVwOiAwID0gYSBsb3QsIDEgPSBhdmVyYWdlLCAyID0gYSBsaXR0bGUKWC5zbGVlcCA8LSBzYW1wbGUoMDoyLCBOLCByZXBsYWNlID0gVFJVRSkgCnNsZWVwX2VmZmVjdCA8LSAtMiAKTS5jb2ZmZWUgPC0gMC43ICogWC5ob3VycyArIHNsZWVwX2VmZmVjdCAqIFguc2xlZXAgKyBybm9ybShOLCAwLCA1KQpZLndha2UgPC0gMC40ICogTS5jb2ZmZWUgKyBybm9ybShOLCAwLCA1KSAKTWVkZGF0YV9jYXQgPC0gZGF0YS5mcmFtZShYLmhvdXJzLCBYLnNsZWVwLCBNLmNvZmZlZSwgWS53YWtlKQpNZWRkYXRhX2NhdCRzbGVlcCA8LSBmYWN0b3IoTWVkZGF0YV9jYXQkWC5zbGVlcCkKCiMgZHVtbXkgY29kZSBzbGVlcApsaWJyYXJ5KGZhc3REdW1taWVzKQoKTWVkZGF0YV9jYXQgPC0gZHVtbXlfY29scyhNZWRkYXRhX2NhdCwgCiAgICAgICAgICAgICAgICAgICBzZWxlY3RfY29sdW1ucyA9ICJYLnNsZWVwIikKCiMgJ2EgbG90JyBvZiBzbGVlcCBpcyB0aGUgcmVmZXJlbmNlIGNhdGVnb3J5LCBzbyB5b3UgY2FuIG9taXQgWC5zbGVlcF8wIGZyb20gdGhlIG1vZGVsCm1vZGVsX2NhdCA8LSIKIyBEaXJlY3QgRWZmZWN0cyBvbiB3YWtlZnVsbmVzcwpZLndha2UgfiBhMSpYLnNsZWVwXzEgKyBhMipYLnNsZWVwXzIgKyBiKk0uY29mZmVlCgojIEVmZmVjdCBvZiBzbGVlcCBvbiBjb2ZmZWUgKG5vdGU6IG5vdyB3ZSB1c2Ugb25seSB0d28gZHVtbWllcywgYXNzdW1pbmcgb25lIGFzIHJlZmVyZW5jZSkKTS5jb2ZmZWUgfiBjMSpYLnNsZWVwXzEgKyBjMipYLnNsZWVwXzIKCiMgSW5kaXJlY3QgZWZmZWN0cyAobm93IG11c3QgaW5jbHVkZSBkdW1teSBwYXRocyBmb3Igb25seSB0d28gbGV2ZWxzKQppbmQxOj1iKmMxCmluZDI6PWIqYzIKCiMgVG90YWwgZWZmZWN0cwp0b3RhbDEgOj0gYTEgKyAoYipjMSkKdG90YWwyIDo9IGEyICsgKGIqYzIpCiIKCm1lZF9jYXQ8LXNlbShtb2RlbCA9IG1vZGVsX2NhdCwgZGF0YSA9IE1lZGRhdGFfY2F0KQoKc3VtbWFyeShtZWRfY2F0LHN0YW5kYXJkaXplZD1ULGZpdC5tZWFzdXJlcz1ULHJzcXVhcmU9VCkKCnNlbVBhdGhzKG9iamVjdCA9IG1lZF9jYXQsIHdoYXRMYWJlbHMgPSAicGFyIikgCgpgYGAKCiMjIyMgTWVkaWF0aW9uIHBhY2thZ2UKClRoZSBwcm9jZXNzIGlzIHNpbWlsYXIgd2hlbiB1c2luZyB0aGUgbWVkaWF0aW9uLCBidXQgbm90ZSB0aGF0IHlvdSdsbCBuZWVkIHRvIHJ1biBzZXBhcmF0ZSBtZWRpYXRpb25zIGZvciBlYWNoIGxldmVsLgoKYGBge3J9CiMgTW9kZWwgcHJlZGljdGluZyBtZWRpYXRvciAoY29mZmVlKQptb2RlbF9tZWRpYXRvciA8LSBsbShNLmNvZmZlZSB+IFguc2xlZXBfMSArIFguc2xlZXBfMiwgZGF0YSA9IE1lZGRhdGFfY2F0KQoKIyBNb2RlbCBwcmVkaWN0aW5nIG91dGNvbWUgKHdha2UpLCBpbmNsdWRpbmcgbWVkaWF0b3IKbW9kZWxfb3V0Y29tZSA8LSBsbShZLndha2UgfiBYLnNsZWVwXzEgKyBYLnNsZWVwXzIgKyBNLmNvZmZlZSwgZGF0YSA9IE1lZGRhdGFfY2F0KQoKIyBhdmVyYWdlIHNsZWVwIHZzIGEgbG90IG9mIHNsZWVwCm1lZC4xIDwtIG1lZGlhdGUobW9kZWxfbWVkaWF0b3IsIG1vZGVsX291dGNvbWUsIHRyZWF0ID0gIlguc2xlZXBfMSIsIG1lZGlhdG9yID0gIk0uY29mZmVlIikKc3VtbWFyeShtZWQuMSkKYGBgCgpgYGB7cn0KIyBsaXR0bGUgc2xlZXAgdnMgYSBsb3Qgb2Ygc2xlZXAKbWVkLjIgPC0gbWVkaWF0ZShtb2RlbF9tZWRpYXRvciwgbW9kZWxfb3V0Y29tZSwgdHJlYXQgPSAiWC5zbGVlcF8yIiwgbWVkaWF0b3IgPSAiTS5jb2ZmZWUiKQpzdW1tYXJ5KG1lZC4yKQpgYGAKCgojIyBQYXJhbGxlbCBNZWRpYXRpb24KCmBgYHtyfQpNZWRkYXRhJE0yLmFjdGl2aXR5IDwtIDIuMSAqIE1lZGRhdGEkWC5ob3VycyArIHJub3JtKG5yb3coTWVkZGF0YSksIG1lYW4gPSAwLCBzZCA9IDEpCgpzdW1tYXJ5KGxtKFkud2FrZSB+IE0yLmFjdGl2aXR5LCBkYXRhID0gTWVkZGF0YSkpCmBgYAoKYGBge3J9CiMgRGVmaW5lIHRoZSBtb2RlbCBmb3IgdGhlIG91dGNvbWUgdmFyaWFibGUKcGFyYWxsZWxNb2RlbCA8LSBsbShZLndha2UgfiBYLmhvdXJzICsgTS5jb2ZmZWUgKyBNMi5hY3Rpdml0eSwgZGF0YSA9IE1lZGRhdGEpCgojIERlZmluZSB0aGUgbW9kZWxzIGZvciB0aGUgbWVkaWF0b3IgdmFyaWFibGVzCm1lZE1vZGVsX2NvZmZlZSA8LSBsbShNLmNvZmZlZSB+IFguaG91cnMsIGRhdGEgPSBNZWRkYXRhKQptZWRNb2RlbF9hY3Rpdml0eSA8LSBsbShNMi5hY3Rpdml0eSB+IFguaG91cnMsIGRhdGEgPSBNZWRkYXRhKQoKIyBQZXJmb3JtIHRoZSBtZWRpYXRpb24gYW5hbHlzaXMgZm9yIGVhY2ggbWVkaWF0b3IKbWVkaWF0aW9uX2NvZmZlZSA8LSBtZWRpYXRlKG1lZE1vZGVsX2NvZmZlZSwgcGFyYWxsZWxNb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlYXQgPSAiWC5ob3VycyIsIG1lZGlhdG9yID0gIk0uY29mZmVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm9vdCA9IFRSVUUsIHNpbXMgPSA1MDApCgptZWRpYXRpb25fYWN0aXZpdHkgPC0gbWVkaWF0ZShtZWRNb2RlbF9hY3Rpdml0eSwgcGFyYWxsZWxNb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZWF0ID0gIlguaG91cnMiLCBtZWRpYXRvciA9ICJNMi5hY3Rpdml0eSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBib290ID0gVFJVRSwgc2ltcyA9IDUwMCkKCiMgU3VtbWFyaXplIHRoZSBtZWRpYXRpb24gYW5hbHlzaXMgcmVzdWx0cwpzdW1tYXJ5KG1lZGlhdGlvbl9jb2ZmZWUpCnN1bW1hcnkobWVkaWF0aW9uX2FjdGl2aXR5KQpgYGAKCmBgYHtyfQptdWx0aXBsZU1lZGlhdGlvbiA8LSAnCgojIERpcmVjdApZLndha2UgfiBiMSAqIE0uY29mZmVlICsgYjIgKiBNMi5hY3Rpdml0eSArIGMgKiBYLmhvdXJzCk0uY29mZmVlIH4gYTEgKiBYLmhvdXJzCk0yLmFjdGl2aXR5IH4gYTIgKiBYLmhvdXJzCgojIEluZGlyZWN0CmluZGlyZWN0MSA6PSBhMSAqIGIxCmluZGlyZWN0MiA6PSBhMiAqIGIyCgojIFRvdGFsCnRvdGFsIDo9IGMgKyAoYTEgKiBiMSkgKyAoYTIgKiBiMikKCiMgQ292YXJpYW5jZSBiZXR3ZWVuIG1lZGlhdG9ycwpNLmNvZmZlZSB+fiBNMi5hY3Rpdml0eQonCmZpdF9tdWx0IDwtIHNlbShtb2RlbCA9IG11bHRpcGxlTWVkaWF0aW9uLCBkYXRhID0gTWVkZGF0YSkKc3VtbWFyeShmaXRfbXVsdCwgZml0Lm1lYXN1cmVzID0gVFJVRSwgc3RhbmRhcmRpemVkID0gVFJVRSkKYGBgCgpgYGB7cn0KbXVsdE1lZDwtc2VtKG1vZGVsID0gbXVsdGlwbGVNZWRpYXRpb24sIGRhdGEgPSBNZWRkYXRhLCBzZT0iYm9vdHN0cmFwIikKCnN1bW1hcnkobXVsdE1lZCxzdGFuZGFyZGl6ZWQ9VCxmaXQubWVhc3VyZXM9VCxyc3F1YXJlPVQpCgpzZW1QYXRocyhvYmplY3QgPSBtdWx0TWVkLCB3aGF0TGFiZWxzID0gInN0ZCIpCgptZWRpYXRpb25QbG90KG11bHRNZWQsIGluZGlyZWN0ID0gVFJVRSwgd2hhdExhYmVscyA9ICJwYXIiLCBzZWNvbmRJbmRpcmVjdCA9IFRSVUUpICNzdGFuZGFyZGl6ZWQgZXN0aW1hdGVzCmBgYAoKYGBge3J9CiMgR2VuZXJhdGUgcmFuZG9tIGFnZXMgYmV0d2VlbiAxOCBhbmQgNjUKTWVkZGF0YSRhZ2UgPC0gcm91bmQocnVuaWYoTiwgbWluID0gMTgsIG1heCA9IDY1KSkKCmNvdk1lZGlhdGlvbiA8LSAnCiMgRGlyZWN0IHBhdGhzCk0uY29mZmVlIH4gYTEgKiBYLmhvdXJzICsgYWdlCk0yLmFjdGl2aXR5IH4gYTIgKiBYLmhvdXJzICsgYWdlClkud2FrZSB+IGIxICogTS5jb2ZmZWUgKyBiMiAqIE0yLmFjdGl2aXR5ICsgYyAqIFguaG91cnMgKyBhZ2UKCiMgQ292YXJpYW5jZSBiZXR3ZWVuIG1lZGlhdG9ycwpNLmNvZmZlZSB+fiBNMi5hY3Rpdml0eQoKIyBJbmRpcmVjdCBlZmZlY3RzCmluZGlyZWN0MSA6PSBhMSAqIGIxCmluZGlyZWN0MiA6PSBhMiAqIGIyCgojIFRvdGFsIGVmZmVjdAp0b3RhbCA6PSBjICsgKGExICogYjEpICsgKGEyICogYjIpCicKCmZpdF9jb3YgPC0gc2VtKG1vZGVsID0gY292TWVkaWF0aW9uLCBkYXRhID0gTWVkZGF0YSkKCnN1bW1hcnkoZml0X2NvdiwgZml0Lm1lYXN1cmVzID0gVFJVRSwgc3RhbmRhcmRpemVkID0gVFJVRSkgICMgQWRkcyBmaXQgbWVhc3VyZXMgYW5kIHN0YW5kYXJkaXplZCBlc3RpbWF0ZXMKYGBgCgpgYGB7cn0KY292TWVkPC1zZW0obW9kZWwgPSBjb3ZNZWRpYXRpb24sIGRhdGEgPSBNZWRkYXRhLCBzZT0iYm9vdHN0cmFwIikKCnNlbVBhdGhzKG9iamVjdCA9IGNvdk1lZCwgd2hhdExhYmVscyA9ICJzdGQiKQpgYGAKCiMjIE1lZGlhdGlvbiB3aXRoIGJpbmFyeSBvdXRjb21lcwoKTGF2YWFuIGF1dG9tYXRpY2FsbHkgZGV0ZWN0cyBiaW5hcnkgRFZzLiBUaGVyZSdzIG5vIG5lZWQgdG8gYWRkIGFueQpzcGVjaWFsIGFyZ3VtZW50cy4KCkNyZWF0ZSBiaW5hcnkgRFY6IFBhc3Mgb3IgZmFpbAoKYGBge3J9CnBhc3NfdGhyZXNob2xkIDwtIG1lZGlhbihNZWRkYXRhJFkud2FrZSkKCiMgQ3JlYXRlIGJpbmFyeSBEViAncGFzc19mYWlsJywgd2hlcmUgMSA9IHBhc3MsIDAgPSBmYWlsCk1lZGRhdGEkcGFzc19mYWlsIDwtIGlmZWxzZShNZWRkYXRhJFkud2FrZSA+IHBhc3NfdGhyZXNob2xkLCAxLCAwKQpgYGAKCmBgYHtyfQpiaW5hcnlNZWRpYXRpb24gPC0gJwojIERpcmVjdCBwYXRocyB0byBtZWRpYXRvcnMKTS5jb2ZmZWUgfiBhMSAqIFguaG91cnMgKyBhZ2UKTTIuYWN0aXZpdHkgfiBhMiAqIFguaG91cnMgKyBhZ2UKCiMgQmluYXJ5IG91dGNvbWUgcGF0aCB1c2luZyBsb2dpdApwYXNzX2ZhaWwgfiBiMSAqIE0uY29mZmVlICsgYjIgKiBNMi5hY3Rpdml0eSArIGMgKiBYLmhvdXJzICsgYWdlCgojIENvdmFyaWFuY2UgYmV0d2VlbiBtZWRpYXRvcnMKTS5jb2ZmZWUgfn4gTTIuYWN0aXZpdHkKCiMgSW5kaXJlY3QgZWZmZWN0cwppbmRpcmVjdDEgOj0gYTEgKiBiMQppbmRpcmVjdDIgOj0gYTIgKiBiMgoKIyBUb3RhbCBlZmZlY3QKdG90YWwgOj0gYyArIChhMSAqIGIxKSArIChhMiAqIGIyKQonCgojIEZpdHRpbmcgdGhlIG1vZGVsIHdpdGggbGF2YWFuCmZpdEJpbmFyeSA8LSBzZW0obW9kZWwgPSBiaW5hcnlNZWRpYXRpb24sIGRhdGEgPSBNZWRkYXRhLCBlc3RpbWF0b3IgPSAiTUwiLCBzZSA9ICJib290c3RyYXAiLCBib290c3RyYXAgPSAxMDAwKQoKc3VtbWFyeShmaXRCaW5hcnksIGZpdC5tZWFzdXJlcyA9IFRSVUUsIHN0YW5kYXJkaXplZCA9IFRSVUUpCmBgYAoKYGBge3J9CmJpbk1lZDwtc2VtKG1vZGVsID0gZml0QmluYXJ5LCBkYXRhID0gTWVkZGF0YSwgc2U9ImJvb3RzdHJhcCIpCgojIGJhc2ljCnNlbVBhdGhzKG9iamVjdCA9IGJpbk1lZCwgd2hhdExhYmVscyA9ICJzdGQiKQoKIyBjaGFuZ2Ugc2hhcGUgYW5kIGxhYmVsIHNpemUKYmluU2VtIDwtIHNlbVBhdGhzKGJpbk1lZCwgCiAgICAgICAgIHdoYXRMYWJlbHMgPSAic3RkIiwgCiAgICAgICAgIGVkZ2UubGFiZWwuY2V4ID0gMC43NSwgIAogICAgICAgICBsYWJlbC5jZXggPSAxLjUsICAgICAgIAogICAgICAgICBsYXlvdXQgPSAic3ByaW5nIikKYGBgCgpBZGQgbGFiZWxzCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgY2hhbmdlIG5vZGUgbGFiZWwKYmluTWVkX2xhYiA8LSBjaGFuZ2Vfbm9kZV9sYWJlbChiaW5TZW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoWC5oID0gIkhvdXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNLmMgPSAiQ29mZmVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNMi4gPSAiQWN0aXZpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzXyA9ICJHcmFkZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5jZXggPSAxLjEpCnBsb3QoYmluTWVkX2xhYikKYGBgCgpJbmRpY2F0ZSB3aGljaCBlZmZlY3RzIGFyZSBzaWduaWZpY2FudAoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNpZ19wbG90IDwtIGJpbk1lZF9sYWIgJT4lICAgICAgCiAgbWFya19zaWcoZml0QmluYXJ5KSAlPiUgCiAgbWFya19zZShmaXRCaW5hcnksIHNlcCA9ICJcbiIpCgpwbG90KHNpZ19wbG90KQpgYGAKCiMjIE1vZGVyYXRlZCBtZWRpYXRpb24KCipjcmVkaXQ6CjxodHRwczovL2FkZW1vcy5wZW9wbGUudWljLmVkdS9DaGFwdGVyMTUuaHRtbCMzMV9kZXNjcmliZV90aGVfZGF0YXNldD4qCgpUaGVzZSBhcmUgY2hhbGxlbmdpbmcgdG8gdW5kZXJzdGFuZCwgc28gd2UncmUgZ29pbmcgdG8gd2FsayB0aHJvdWdoIGEKZGlmZmVyZW50IGV4YW1wbGUuCgpMZXQncyBleGFtaW5lIHRoZSBxdWVzdGlvbjogZG9lcyBzcGVuZGluZyBtb3JlIHRpbWUgaW4gZ3JhZCBzY2hvb2wKcHJlZGljdCBtb3JlIG9mZmVycyBhZnRlciBncmFkdWF0aW9uPyBXZSdsbCBzZWUgaWYgdGhpcyByZWxhdGlvbnNoaXAgaXMKZXhwbGFpbmVkIGJ5IG51bWJlciBvZiBwdWJsaWNhdGlvbnMgKGkuZS4sIHRoZSBtb3JlIHRpbWUgc3BlbnQgaW4gZ3JhZApzY2hvb2wsIHRoZSBtb3JlIHB1YmxpY2F0aW9ucyBvbmUgaGFzLCBhbmQgdGhlIG1vcmUgcHVibGljYXRpb25zIG9uZQpoYXMsIHRoZSBtb3JlIGpvYiBvZmZlcnMgdGhleSBnZXQpLiBIb3dldmVyLCB0aGlzIGNhdXNhbCBjaGFpbiBtYXkgb25seQp3b3JrIGZvciBwZW9wbGUgd2hvIHNwZW5kIHRoZWlyIHRpbWUgaW4gZ3JhZHVhdGUgc2Nob29sIG5ldHdvcmtpbmcuCgpXZSBhcmUgZ29pbmcgdG8gc2ltdWxhdGUgYSBkYXRhc2V0IHRoYXQgbWVhc3VyZWQgdGhlIGZvbGxvd2luZzoKCi0gICBYID0gVGltZSBzcGVudCBpbiBncmFkdWF0ZSBzY2hvb2wgKHdlIHdpbGwgY2hhbmdlIHRoZSBuYW1lIHRvIOKAnHRpbWXigJ0KICAgIHdoZW4gd2UgY3JlYXRlIHRoZSBkYXRhIGZyYW1lKQotICAgWiA9IFRpbWUgc3BlbnQgbmV0d29ya2luZwotICAgTSA9IE51bWJlciBvZiBwdWJsaWNhdGlvbnMgaW4gZ3JhZCBzY2hvb2wKLSAgIFkgPSBOdW1iZXIgb2Ygam9iIG9mZmVycwoKYGBge3J9CiMjIyBXZSBhcmUgaW50ZW50aW9uYWxseSBjcmVhdGluZyBhIG1vZGVyYXRlZCBtZWRpYXRpb24gZWZmZWN0IGhlcmUgYW5kIHdlIGRvIHNvIGJlbG93IGJ5IHNldHRpbmcgdGhlIHJlbGF0aW9uc2hpcHMgKHRoZSBwYXRocykgYmV0d2VlbiBvdXIgY2F1c2FsIGNoYWluIHZhcmlhYmxlcyBhbmQgc2V0dGluZyB0aGUgcmVsYXRpb25zaGlwcyBmb3Igb3VyIGludGVyYWN0aW9uIHRlcm1zCgpzZXQuc2VlZCg0MikgI1RoaXMgbWFrZXMgc3VyZSB0aGF0IGV2ZXJ5b25lIGdldHMgdGhlIHNhbWUgbnVtYmVycyBnZW5lcmF0ZWQgdGhyb3VnaCBybm9ybSBmdW5jdGlvbgoKYTEgPSAtLjU5ICNTZXQgdGhlIHBhdGggYTEgc3RyZW5ndGggKGVmZmVjdCBvZiBYIG9uIE0pCgphMiA9IC0uMTcgI1NldCBwYXRoIGEyIHN0cmVuZ3RoIChlZmZlY3Qgb2YgWiBvbiBNKQoKYTMgPSAuMjkgI1NldCBwYXRoIGEzIHN0cmVuZ3RoIChpbnRlcmFjdGlvbiBiZXR3ZWVuIFggYW5kIFogb24gTSkKCmIgPSAuNTkgI1NldCBwYXRoIGIgc3RyZW5ndGggKGVmZmVjdCBvZiBNIG9uIFkpCgpjZGFzaDEgPSAuMjcgI1NldCBwYXRoIGMnMSBzdHJlbmd0aCAoZWZmZWN0IG9mIFggb24gWSkKCmNkYXNoMiA9IC4wMSAjU2V0IHBhdGggYycyIHN0cmVuZ3RoIChlZmZlY3Qgb2YgWiBvbiBZKQoKY2Rhc2gzID0gLS4wMSAjU2V0IHBhdGggYyczIHN0cmVuZ3RoIChpbnRlcmFjdGlvbiBiZXR3ZWUgWCBhbmQgWiBvbiBZKQoKIyMjIEhlcmUgd2UgYXJlIGNyZWF0aW5nIHRoZSB2YWx1ZXMgb2Ygb3VyIHZhcmlhYmxlcyBmb3IgZWFjaCBzdWJqZWN0CgpuIDwtIDIwMCAjU2V0IHNhbXBsZSBzaXplCgpYIDwtIHJub3JtKG4sIDcsIDEpICNJVjogVGltZSBzcGVudCBpbiBncmFkIHNjaG9vbCAoTSA9IDcsIFNEID0gMSkKClogPC0gcm5vcm0obiwgNSwgMSkgI01vZGVyYXRvcjogVGltZSBzcGVudCAoaG91cnMgcGVyIHdlZWspIHdpdGggUHJvZmVzc29yIERlbW9zIGluIGNsYXNzIG9yIGluIG9mZmljZSBob3VycyAoTSA9IDUsIFNEID0gMSkKCk0gPC0gYTEqWCArIGEyKlogKyBhMypYKlogKyBybm9ybShuLCAwLCAuMSkgI01lZGlhdG9yOiBOdW1iZXIgb2YgcHVibGljYXRpb25zIGluIGdyYWQgc2Nob29sCiNUaGUgbWVkaWF0b3IgdmFyaWFibGUgaXMgY3JlYXRlZCBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBJViwgbW9kZXJhdG9yLCBhbmQgdGhlaXIgaW50ZXJhY3Rpb24gd2l0aCBzb21lIHJhbmRvbSBub2lzZSB0aHJvd24gaW4gdGhlIG1peAoKWSA8LSBjZGFzaDEqWCArIGNkYXNoMipaICsgY2Rhc2gzKlgqWiArIGIqTSArIHJub3JtKG4sIDAsIC4xKSAjRFY6IE51bWJlciBvZiBqb2Igb2ZmZXJzCiNTaW1pbGFyIHRvIHRoZSBtZWRpYXRvciwgdGhlIERWIGlzIGEgZnVuY3Rpb24gb2YgdGhlIElWLCBtb2RlcmF0b3IsIHRoZWlyIGludGVyYWN0aW9uLCBhbmQgdGhlIG1lZGlhdG9yIHdpdGggc29tZSByYW5kb20gbm9pc2UgdGhyb3duIGluIHRoZSBtaXgKClN1Y2Nlc3MuTW9kTWVkIDwtIGRhdGEuZnJhbWUoam9icyA9IFksIHRpbWUgPSBYLCBwdWJzID0gTSwgbmV0d29yayA9IFopICNCdWlsZCBvdXIgZGF0YSBmcmFtZSBhbmQgZ2l2ZSBpdCByZWNvZ25pemFibGUgdmFyaWFibGUgbmFtZXMKCmBgYAoKQmVjYXVzZSB3ZSBoYXZlIGludGVyYWN0aW9uIHRlcm1zIGluIG91ciByZWdyZXNzaW9uIGFuYWx5c2VzLCB3ZSBuZWVkIHRvCm1lYW4gY2VudGVyIG91ciBJViBhbmQgTW9kZXJhdG9yIChaKQoKYGBge3J9ClN1Y2Nlc3MuTW9kTWVkJHRpbWUuYyA8LSBzY2FsZShTdWNjZXNzLk1vZE1lZCR0aW1lLCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IEZBTFNFKVssXSAjU2NhbGUgcmV0dXJucyBhIG1hdHJpeCBzbyB3ZSBoYXZlIHRvIG1ha2UgaXQgYSB2ZWN0b3IgYnkgaW5kZXhpbmcgb25lIGNvbHVtbgoKU3VjY2Vzcy5Nb2RNZWQkbmV0d29yay5jIDwtIHNjYWxlKFN1Y2Nlc3MuTW9kTWVkJG5ldHdvcmssIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpWyxdCmBgYAoKYGBge3J9Ck1vZC5NZWQuTGF2YWFuIDwtICcKI1JlZ3Jlc3Npb25zCiNUaGVzZSBhcmUgdGhlIHNhbWUgcmVncmVzc2lvbiBlcXVhdGlvbnMgZnJvbSBvdXIgcHJldmlvdXMgZXhhbXBsZQojRXhjZXB0IGluIHRoaXMgY29kZSB3ZSBhcmUgbmFtaW5nIHRoZSBjb2VmZmljaWVudHMgdGhhdCBhcmUgcHJvZHVjZWQgZnJvbSB0aGUgcmVncmVzc2lvbiBlcXVhdGlvbnMKI0UuZy4sIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGZvciB0aGUgZWZmZWN0IG9mIHRpbWUgb24gcHVicyBpcyBuYW1lZCAiYTEiCnB1YnMgfiBhMSp0aW1lLmMgKyBhMipuZXR3b3JrLmMgKyBhMyp0aW1lLmM6bmV0d29yay5jCmpvYnMgfiBjZGFzaDEqdGltZS5jICsgY2Rhc2gyKm5ldHdvcmsuYyArIGNkYXNoMyp0aW1lLmM6bmV0d29yay5jICsgYjEqcHVicwoKI01lYW4gb2YgY2VudGVyZWQgbmV0d29yayAoZm9yIHVzZSBpbiBzaW1wbGUgc2xvcGVzKQojVGhpcyBpcyBtYWtpbmcgYSBjb2VmZmljaWVudCBsYWJlbGVkICJuZXR3b3JrLmMubWVhbiIgd2hpY2ggZXF1YWxzIHRoZSBpbnRlcmNlcHQgYmVjYXVzZSBvZiB0aGUgIjEiCiMoWX4xKSBnaXZlcyB5b3UgdGhlIGludGVyY2VwdCwgd2hpY2ggaXMgdGhlIG1lYW4gZm9yIG91ciBuZXR3b3JrLmMgdmFyaWFibGUKbmV0d29yay5jIH4gbmV0d29yay5jLm1lYW4qMQoKI1ZhcmlhbmNlIG9mIGNlbnRlcmVkIG5ldHdvcmsgKGZvciB1c2UgaW4gc2ltcGxlIHNsb3BlcykKI1RoaXMgaXMgbWFraW5nIGEgY29lZmZpY2llbnQgbGFiZWxlZCAibmV0d29yay5jLnZhciIgd2hpY2ggZXF1YWxzIHRoZSB2YXJpYW5jZSAKI1R3byB0aWxkZXMgc2VwYXJhdGluZyB0aGUgc2FtZSB2YXJpYWJsZSBnaXZlcyB5b3UgdGhlIHZhcmlhbmNlCm5ldHdvcmsuYyB+fiBuZXR3b3JrLmMudmFyKm5ldHdvcmsuYwoKI0luZGlyZWN0IGVmZmVjdHMgY29uZGl0aW9uYWwgb24gbW9kZXJhdG9yIChhMSArIGEzKk1vZFZhbHVlKSpiMQppbmRpcmVjdC5TRGJlbG93IDo9IChhMSArIGEzKihuZXR3b3JrLmMubWVhbi1zcXJ0KG5ldHdvcmsuYy52YXIpKSkqYjEgCmluZGlyZWN0LlNEYWJvdmUgOj0gKGExICsgYTMqKG5ldHdvcmsuYy5tZWFuK3NxcnQobmV0d29yay5jLnZhcikpKSpiMQoKI0RpcmVjdCBlZmZlY3RzIGNvbmRpdGlvbmFsIG9uIG1vZGVyYXRvciAoY2Rhc2gxICsgY2Rhc2gzKk1vZFZhbHVlKQojV2UgaGF2ZSB0byBkbyBpdCB0aGlzIHdheSBiZWNhdXNlIHlvdSBjYW5ub3QgY2FsbCB0aGUgbWVhbiBhbmQgc2QgZnVuY3Rpb25zIGluIGxhdmFhbiBwYWNrYWdlCmRpcmVjdC5TRGJlbG93IDo9IGNkYXNoMSArIGNkYXNoMyoobmV0d29yay5jLm1lYW4tc3FydChuZXR3b3JrLmMudmFyKSkgCmRpcmVjdC5TRGFib3ZlIDo9IGNkYXNoMSArIGNkYXNoMyoobmV0d29yay5jLm1lYW4rc3FydChuZXR3b3JrLmMudmFyKSkKCiNUb3RhbCBlZmZlY3RzIGNvbmRpdGlvbmFsIG9uIG1vZGVyYXRvcgp0b3RhbC5TRGJlbG93IDo9IGRpcmVjdC5TRGJlbG93ICsgaW5kaXJlY3QuU0RiZWxvdwp0b3RhbC5TRGFib3ZlIDo9IGRpcmVjdC5TRGFib3ZlICsgaW5kaXJlY3QuU0RhYm92ZQoKI1Byb3BvcnRpb24gbWVkaWF0ZWQgY29uZGl0aW9uYWwgb24gbW9kZXJhdG9yCiNUbyBtYXRjaCB0aGUgb3V0cHV0IG9mICJtZWRpYXRlIiBwYWNrYWdlCnByb3AubWVkaWF0ZWQuU0RiZWxvdyA6PSBpbmRpcmVjdC5TRGJlbG93IC8gdG90YWwuU0RiZWxvdwpwcm9wLm1lZGlhdGVkLlNEYWJvdmUgOj0gaW5kaXJlY3QuU0RhYm92ZSAvIHRvdGFsLlNEYWJvdmUKCiNJbmRleCBvZiBtb2RlcmF0ZWQgbWVkaWF0aW9uCiNBbiBhbHRlcm5hdGl2ZSB3YXkgb2YgdGVzdGluZyBpZiBjb25kaXRpb25hbCBpbmRpcmVjdCBlZmZlY3RzIGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIGVhY2ggb3RoZXIKaW5kZXgubW9kLm1lZCA6PSBhMypiMQonCgojRml0IG1vZGVsCk1vZC5NZWQuU0VNIDwtIHNlbShtb2RlbCA9IE1vZC5NZWQuTGF2YWFuLAogICAgICAgICAgICAgICAgICAgZGF0YSA9IFN1Y2Nlc3MuTW9kTWVkLAogICAgICAgICAgICAgICAgICAgc2UgPSAiYm9vdHN0cmFwIiwKICAgICAgICAgICAgICAgICAgIGJvb3RzdHJhcCA9IDEwLAogICAgICAgICAgICAgICAgICAgZml4ZWQueCA9IEZBTFNFKSAjIHB1YnMgc2hvdWxkIG1vZGVsIHRoZSB2YXJpYW5jZQoKI0ZpdCBtZWFzdXJlcwpzdW1tYXJ5KE1vZC5NZWQuU0VNLAogICAgICAgIGZpdC5tZWFzdXJlcyA9IEZBTFNFLAogICAgICAgIHN0YW5kYXJkaXplID0gVFJVRSwKICAgICAgICByc3F1YXJlID0gVFJVRSkKCmBgYAoKLSAgICoqcHVicyBcfioqCiAgICAtICAgQTEgKE0gXH4gWCk6IFNpZ25pZmljYW50IG1haW4gZWZmZWN0IG9mIHRpbWUgc3BlbnQgaW4gZ3JhZAogICAgICAgIHNjaG9vbCBvbiBudW1iZXIgb2YgcHVibGljYXRpb25zCiAgICAtICAgQTIgKE0gXH4gWik6IFNpZ25pZmljYW50IG1haW4gZWZmZWN0IG9mIHRpbWUgc3BlbnQgbmV0d29ya2luZyBvbgogICAgICAgIG51bWJlciBvZiBwdWJsaWNhdGlvbnMKICAgIC0gICBBMyAoTSBcfiBYIFwqIFopOiBTaWduaWZpY2FudCBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRpbWUgc3BlbnQgaW4KICAgICAgICBncmFkIHNjaG9vbCBhbmQgdGltZSBzcGVudCBuZXR3b3JraW5nIG9uIG51bWJlciBvZiBwdWJsaWNhdGlvbnMKLSAgICoqam9icyBcfioqCiAgICAtICAgKGNkczEpIChZIFx+IFgpOiBTaWduaWZpY2FudCBtYWluIGVmZmVjdCBvZiB0aW1lIHNwZW50IGluIGdyYWQKICAgICAgICBzY2hvb2wgb24gbnVtYmVyIG9mIGpvYiBvZmZlcnMKICAgIC0gICAoY2RzMikgKFkgXH4gWik6IE5vIGVmZmVjdCBvZiB0aW1lIHNwZW50IG5ldHdvcmtpbmcgb24gbnVtYmVyIG9mCiAgICAgICAgam9iIG9mZmVycwogICAgLSAgIChjZHMzKSAoWSBcfiBNKTogU2lnbmlmaWNhbnQgbWFpbiBlZmZlY3Qgb2YgbnVtYmVyIG9mCiAgICAgICAgcHVibGljYXRpb25zIG9uIG51bWJlciBvZiBqb2Igb2ZmZXJzCiAgICAtICAgKGIxKSAoWSBcfiBYIFwqIFopOiBObyBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRpbWUgc3BlbnQgaW4gZ3JhZAogICAgICAgIHNjaG9vbCBhbmQgdGltZSBzcGVudCBuZXR3b3JraW5nIG9uIG51bWJlciBvZiBqb2Igb2ZmZXJzCgoqVG8gbG9vayBhdCB0aGUgbW9kZXJhdG9yIGVmZmVjdCwgd2UgY2FuIHVzZSB0aGUgKy8tIDFTRCBmcm9tIHRoZSBtZWFuKgoKLSAgICoqdGhvc2Ugd2hvIHNwZW5kIGxpdHRsZSB0aW1lIG5ldHdvcmtpbmcqKgogICAgLSAgIChkaXJlY3QuU0RibHcpIFNpZ25pZmljYW50IGRpcmVjdCBlZmZlY3Qgb2YgdGltZSBzcGVudCBpbiBncmFkCiAgICAgICAgc2Nob29sIG9uIGpvYiBvZmZlcnMgKGZvciB0aG9zZSB3aG8gZG9u4oCZdCBzcGVuZCBhIGxvdCBvZiB0aW1lCiAgICAgICAgbmV0d29ya2luZykKICAgIC0gICAoaW5kaXJlY3QuU0RiZWxvdykgU2lnbmlmaWNhbnQgaW5kaXJlY3QgZWZmZWN0IG9mIHRpbWUgc3BlbnQgaW4KICAgICAgICBncmFkIHNjaG9vbCBvbiBqb2Igb2ZmZXJzIHRocm91Z2ggcHVibGljYXRpb25zIChmb3IgdGhvc2Ugd2hvCiAgICAgICAgZG9u4oCZdCBzcGVuZCBhIGxvdCBvZiB0aW1lIG5ldHdvcmtpbmcpCi0gICAqKnRob3NlIHdobyBzcGVuZCBhIGxvdCBvZiB0aW1lIG5ldHdvcmtpbmcqKgogICAgLSAgIChkaXJlY3QuU0RhYnYpIFNpZ25pZmljYW50IGRpcmVjdCBlZmZlY3Qgb2YgdGltZSBzcGVudCBpbiBncmFkCiAgICAgICAgc2Nob29sIG9uIGpvYiBvZmZlcnMgKGZvciB0aG9zZSB3aG8gc3BlbmQgYSBsb3Qgb2YgdGltZQogICAgICAgIG5ldHdvcmtpbmcpCiAgICAtICAgKGluZGlyZWN0LlNEYWJ2KSBTaWduaWZpY2FudCBpbmRpcmVjdCBlZmZlY3Qgb2YgdGltZSBzcGVudCBpbgogICAgICAgIGdyYWQgc2Nob29sIG9uIGpvYiBvZmZlcnMgdGhyb3VnaCBwdWJsaWNhdGlvbnMgKGZvciB0aG9zZSB3aG8KICAgICAgICBzcGVuZCBhIGxvdCBvZiB0aW1lIG5ldHdvcmtpbmcpCi0gICAqKndoYXQgZG9lcyB0aGlzIG1lYW4/KioKICAgIC0gICBUaW1lIHNwZW50IGluIGdyYWR1YXRlIHNjaG9vbCBwb3NpdGl2ZWx5IGFmZmVjdHMgdGhlIG51bWJlciBvZgogICAgICAgIGpvYiBvZmZlcnMsIGJvdGggZGlyZWN0bHkgYW5kIGluZGlyZWN0bHkgdGhyb3VnaCB0aGUgbnVtYmVyIG9mCiAgICAgICAgcHVibGljYXRpb25zLiBUaGUgc3RyZW5ndGggb2YgdGhpcyBpbmRpcmVjdCBlZmZlY3QgaXMgZGVwZW5kcyBvbgogICAgICAgIHRoZSBhbW91bnQgb2YgdGltZSBzcGVudCBuZXR3b3JraW5nLgogICAgLSAgIFNwZWNpZmljYWxseSwgd2hpbGUgdGhlIG1lZGlhdGVkIHBhdGggd2FzIHNpZ25pZmljYW50IHJlZ2FyZGxlc3MKICAgICAgICBvZiBuZXR3b3JraW5nIHRpbWUsICp0aGUgc3RyZW5ndGggb2YgdGhlIHB1YmxpY2F0aW9ucyBleHBsYWluaW5nCiAgICAgICAgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRpbWUgc3BlbnQgaW4gZ3JhZCBzY2hvb2wgYW5kIGpvYgogICAgICAgIG9mZmVycyB3YXMgc3Ryb25nZXIgZm9yIHBlb3BsZSB3aG8gc3BlbnQgYSBsb3Qgb2YgdGltZQogICAgICAgIG5ldHdvcmtpbmcuKgoKYGBge3J9Cm1tX3Bsb3QgPC0gc2VtUGF0aHMoTW9kLk1lZC5TRU0sIAogICAgICAgICB3aGF0TGFiZWxzID0gImVzdCIsIAogICAgICAgICBlZGdlLmxhYmVsLmNleCA9IDAuNzUsICAKICAgICAgICAgbGFiZWwuY2V4ID0gMS41LCAgICAgICAKICAgICAgICAgbGF5b3V0ID0gInNwcmluZyIsCiAgICAgICAgIGludGVyY2VwdHMgPSBGQUxTRSkKCiMgY2hhbmdlIG5vZGUgbGFiZWwKTW9kbWVkX2xhYiA8LSBjaGFuZ2Vfbm9kZV9sYWJlbChtbV9wbG90LAogICAgICAgICAgICAgICAgICAgICAgICAgICBjKGpicyA9ICJZLkpvYnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBicyA9ICJNLlB1YnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRtLiA9ICJYLlRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG50LiA9ICJaLk5ldHdvcmsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICd0LjonID0gIlQqTiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5jZXggPSAxLjEpCgpzaWdfcGxvdF9tbSA8LSBNb2RtZWRfbGFiICU+JSAgICAgIAogIG1hcmtfc2lnKE1vZC5NZWQuU0VNKSAlPiUgCiAgbWFya19zZShNb2QuTWVkLlNFTSwgc2VwID0gIlxuIikKCnBsb3Qoc2lnX3Bsb3RfbW0pCmBgYAoKYGBge3J9CiNCb290c3RyYXBzCnBhcmFtZXRlckVzdGltYXRlcyhNb2QuTWVkLlNFTSwKICAgICAgICAgICAgICAgICAgIGJvb3QuY2kudHlwZSA9ICJiY2Euc2ltcGxlIiwKICAgICAgICAgICAgICAgICAgIGxldmVsID0gLjk1LCBjaSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBzdGFuZGFyZGl6ZWQgPSBGQUxTRSlbYygxOToyNyksYyg0OjEwKV0gI1dlIGluZGV4IHRoZSBtYXRyaXggdG8gb25seSBkaXNwbGF5IGNvbHVtbnMgd2UgYXJlIGludGVyZXN0ZWQgaW4KYGBgCgpUaGUgZGlmZmVyZW5jZSBpcyBtb3N0IGxpa2VseSBhIHJlc3VsdCBvZiBib290c3RyYXAgZXN0aW1hdGlvbgpkaWZmZXJlbmNlcyAoZS5nLiwgbGF2YWFuIHVzZXMgYmlhcy1jb3JyZWN0ZWQgYnV0IG5vdCBhY2NlbGVyYXRlZApib290c3RyYXBwaW5nIGZvciB0aGVpciBjb25maWRlbmNlIGludGVydmFscykKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiSlNtZWRpYXRpb24iKQpsaWJyYXJ5KEpTbWVkaWF0aW9uKQoKU3VjY2Vzcy5Nb2RNZWQkcHVicy5jIDwtIHNjYWxlKFN1Y2Nlc3MuTW9kTWVkJHB1YnMsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpWyxdCgptb2RlcmF0ZWRfbWVkaWF0aW9uX2ZpdCA8LSAKICBtZHRfbW9kZXJhdGVkKGRhdGEgPSBTdWNjZXNzLk1vZE1lZCwKICAgICAgICAgICAgICAgIElWICAgPSB0aW1lLmMsCiAgICAgICAgICAgICAgICBEViAgID0gam9icywgCiAgICAgICAgICAgICAgICBNICAgID0gcHVicy5jLAogICAgICAgICAgICAgICAgTW9kICA9IG5ldHdvcmsuYykKCm1vZGVyYXRlZF9tZWRpYXRpb25fZml0ICU+JSBhZGRfaW5kZXgoc3RhZ2UgPSAiZmlyc3QiKQoKY29tcHV0ZV9pbmRpcmVjdF9lZmZlY3RfZm9yKG1vZGVyYXRlZF9tZWRpYXRpb25fZml0LCBNb2QgPSAxKSAKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgTm9ubGluZWFyIHJlZ3Jlc3Npb24KClRoaXMgZGF0YXNldCBoYXMgYSBiaW5hcnkgcmVzcG9uc2UgKG91dGNvbWUsIGRlcGVuZGVudCkgdmFyaWFibGUgY2FsbGVkCmFkbWl0LiBUaGVyZSBhcmUgdGhyZWUgcHJlZGljdG9yIHZhcmlhYmxlczogZ3JlLCBncGEgYW5kIHJhbmsuIFdlIHdpbGwKdHJlYXQgdGhlIHZhcmlhYmxlcyBncmUgYW5kIGdwYSBhcyBjb250aW51b3VzLiBUaGUgdmFyaWFibGUgcmFuayB0YWtlcwpvbiB0aGUgdmFsdWVzIDEgdGhyb3VnaCA0LiBJbnN0aXR1dGlvbnMgd2l0aCBhIHJhbmsgb2YgMSBoYXZlIHRoZQpoaWdoZXN0IHByZXN0aWdlLCB3aGlsZSB0aG9zZSB3aXRoIGEgcmFuayBvZiA0IGhhdmUgdGhlIGxvd2VzdC4KCmBgYHtyfQpteWRhdGEgPC0gcmVhZC5jc3YoImh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvYmluYXJ5LmNzdiIpCgp4dGFicyh+YWRtaXQgKyByYW5rLCBkYXRhID0gbXlkYXRhKQpgYGAKCiMjIExvZ2lzdGljIHJlZ3Jlc3Npb24KCmBgYHtyIHNpbXBsZSBsb2dpc3RpYyByZWdyZXNzaW9ufQpteWRhdGEkcmFuayA8LSBmYWN0b3IobXlkYXRhJHJhbmspCm15bG9naXQgPC0gZ2xtKGFkbWl0IH4gZ3JlICsgZ3BhICsgcmFuaywgZGF0YSA9IG15ZGF0YSwgZmFtaWx5ID0gImJpbm9taWFsIikKCnN1bW1hcnkobXlsb2dpdCkKYGBgCgotICAgRm9yIGV2ZXJ5IG9uZSB1bml0IGNoYW5nZSBpbiBncmUsIHRoZSBsb2cgb2RkcyBvZiBhZG1pc3Npb24gKHZlcnN1cwogICAgbm9uLWFkbWlzc2lvbikgaW5jcmVhc2VzIGJ5IDAuMDAyLgotICAgRm9yIGEgb25lIHVuaXQgaW5jcmVhc2UgaW4gZ3BhLCB0aGUgbG9nIG9kZHMgb2YgYmVpbmcgYWRtaXR0ZWQgdG8KICAgIGdyYWR1YXRlIHNjaG9vbCBpbmNyZWFzZXMgYnkgMC44MDQuCi0gICBUaGUgaW5kaWNhdG9yIHZhcmlhYmxlcyBmb3IgcmFuayBoYXZlIGEgc2xpZ2h0bHkgZGlmZmVyZW50CiAgICBpbnRlcnByZXRhdGlvbi4gRm9yIGV4YW1wbGUsIGhhdmluZyBhdHRlbmRlZCBhbiB1bmRlcmdyYWR1YXRlCiAgICBpbnN0aXR1dGlvbiB3aXRoIHJhbmsgb2YgMiwgdmVyc3VzIGFuIGluc3RpdHV0aW9uIHdpdGggYSByYW5rIG9mIDEsCiAgICBjaGFuZ2VzIHRoZSBsb2cgb2RkcyBvZiBhZG1pc3Npb24gYnkgLTAuNjc1LgoKYGBge3J9CmNvbmZpbnQobXlsb2dpdCkKYGBgCgpgYGB7cn0KIyMgb2RkcyByYXRpb3MgYW5kIDk1JSBDSQpleHAoY2JpbmQoT1IgPSBjb2VmKG15bG9naXQpLCBjb25maW50KG15bG9naXQpKSkKYGBgCgojIyMgV2FsZC10ZXN0IChsaWtlIGFuIG9tbmlidXMgdGVzdCkKCioqTWFpbiBlZmZlY3Qgb2YgcmFuayoqCgpgYGB7cn0KbGlicmFyeShhb2QpCndhbGQudGVzdChiID0gY29lZihteWxvZ2l0KSwgU2lnbWEgPSB2Y292KG15bG9naXQpLCBUZXJtcyA9IDQ6NikKYGBgCgpUaGUgY2hpLXNxdWFyZWQgdGVzdCBzdGF0aXN0aWMgb2YgMjAuOSwgd2l0aCB0aHJlZSBkZWdyZWVzIG9mIGZyZWVkb20gaXMKYXNzb2NpYXRlZCB3aXRoIGEgcC12YWx1ZSBvZiAwLjAwMDExIGluZGljYXRpbmcgdGhhdCB0aGUgb3ZlcmFsbCBlZmZlY3QKb2YgcmFuayBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LgoKKipNYWluIGVmZmVjdCBvZiBncmUqKgoKYGBge3J9CndhbGQudGVzdChiID0gY29lZihteWxvZ2l0KSwgU2lnbWEgPSB2Y292KG15bG9naXQpLCBUZXJtcyA9IDIpCmBgYAoKKipNYWluIGVmZmVjdCBvZiBncGEqKgoKYGBge3J9CndhbGQudGVzdChiID0gY29lZihteWxvZ2l0KSwgU2lnbWEgPSB2Y292KG15bG9naXQpLCBUZXJtcyA9IDMpCmBgYAoKIyMjIyBQc2V1ZG8tUiBzcXVhcmVkIGZvciBsb2dpc2l0aWMgcmVncmVzc2lvbgoKYGBge3J9ClBzZXVkb1IyKG15bG9naXQsIHdoaWNoID0gYygiQ294U25lbGwiLCJOYWdlbGtlcmtlIikpCmBgYAoKIyMjIEdyYXBoIGxvZ2lzdGljIHJlZ3Jlc3Npb24KCjEuICBTdGFydCBieSBjYWxjdWxhdGluZyB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXR5IG9mIHRoZSBvdXRjb21lIGF0CiAgICBlYWNoIGxldmVsIG9mIHRoZSBjYXRlZ29yaWNhbC9vcmRpbmFsIHByZWRpY3RvciB2YXJpYWJsZSBhbmQvb3IKICAgIGhvbGRpbmcgY29udGludW91cyB2YXJpYWJsZXMgYXQgdGhlaXIgbWVhbnMuCjIuICBDcmVhdGUgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIHVzaW5nIHRoZSBuZXcgZGF0YWZyYW1lLgozLiAgU3RhcnQgY3JlYXRpbmcgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0eSB2ZXJzaW9uIG9mIHlvdXIgdmFyaWFibGVzCjQuICBTZXQgdGhlIFNFIHRvIGRpc3BsYXkKNS4gIEdyYXBoCgpgYGB7cn0KIyBzdGVwIDEgCm5ld2RhdGExIDwtIHdpdGgobXlkYXRhLAogICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoZ3JlID0gbWVhbihncmUpLCBncGEgPSBtZWFuKGdwYSksIHJhbmsgPSBmYWN0b3IoMTo0KSkpCgojIHN0ZXAgMgpuZXdkYXRhMSRyYW5rUCA8LSBwcmVkaWN0KG15bG9naXQsIG5ld2RhdGEgPSBuZXdkYXRhMSwgdHlwZSA9ICJyZXNwb25zZSIpCm5ld2RhdGExCgojIHN0ZXAgMwpuZXdkYXRhMiA8LSB3aXRoKG15ZGF0YSwKICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKGdyZSA9IHJlcChzZXEoZnJvbSA9IDIwMCwgdG8gPSA4MDAsIGxlbmd0aC5vdXQgPSAxMDApLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdwYSA9IG1lYW4oZ3BhKSwgcmFuayA9IGZhY3RvcihyZXAoMTo0LCBlYWNoID0gMTAwKSkpKQoKIyBzdGVwIDQKbmV3ZGF0YTMgPC0gY2JpbmQobmV3ZGF0YTIsIHByZWRpY3QobXlsb2dpdCwgbmV3ZGF0YSA9IG5ld2RhdGEyLCB0eXBlPSJsaW5rIiwgc2U9VFJVRSkpCm5ld2RhdGEzIDwtIHdpdGhpbihuZXdkYXRhMywgewogIFByZWRpY3RlZFByb2IgPC0gcGxvZ2lzKGZpdCkKICBMTCA8LSBwbG9naXMoZml0IC0gKDEuOTYgKiBzZS5maXQpKQogIFVMIDwtIHBsb2dpcyhmaXQgKyAoMS45NiAqIHNlLmZpdCkpCn0pCgojIGdyYXBoClByZWRQcm9iUGxvdCA8LSBnZ3Bsb3QobmV3ZGF0YTMsIGFlcyh4ID0gZ3JlLCB5ID0gUHJlZGljdGVkUHJvYikpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IExMLCB5bWF4ID0gVUwsIGZpbGwgPSByYW5rKSwgYWxwaGEgPSAuMikgKwogIGdlb21fbGluZShhZXMoY29sb3VyID0gcmFuayksIHNpemU9MSkgK3RoZW1lX21pbmltYWwoKQoKUHJlZFByb2JQbG90ICsgZ2d0aXRsZSgiQWRtaXNzaW9uIHRvIEdyYWQgU2Nob29sIGJ5IFJhbmsiKQpgYGAKCiMjIyMgU2ltcGxlciBwcmVkaWN0aW9uIHBsb3QKCmBgYHtyfQpsaWJyYXJ5KGdnZWZmZWN0cykKcGxvdChnZ3ByZWRpY3QobXlsb2dpdCxjKCJncmUiLCJyYW5rIikpKQpgYGAKCiMjIE11bHRpbm9taWFsIFJlZ3Jlc3Npb24KClRoZSBkYXRhIHNldCBjb250YWlucyB2YXJpYWJsZXMgb24gMjAwIHN0dWRlbnRzLiBUaGUgb3V0Y29tZSB2YXJpYWJsZSBpcwoqKnByb2cqKiwgcHJvZ3JhbSB0eXBlLiBUaGUgcHJlZGljdG9yIHZhcmlhYmxlcyBhcmUgc29jaWFsIGVjb25vbWljCnN0YXR1cywgKipzZXMqKiwgYSB0aHJlZS1sZXZlbCBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhbmQgd3JpdGluZyBzY29yZSwKKip3cml0ZSoqLCBhIGNvbnRpbnVvdXMgdmFyaWFibGUuCgo+IChSZXNlYXJjaCBRdWVzdGlvbik6IFdoZW4gaGlnaCBzY2hvb2wgc3R1ZGVudHMgY2hvb3NlIHRoZSBwcm9ncmFtCj4gKGdlbmVyYWwsIHZvY2F0aW9uYWwsIGFuZCBhY2FkZW1pYyBwcm9ncmFtcyksIGhvdyBkbyB0aGVpciBtYXRoIGFuZAo+IHNjaWVuY2Ugc2NvcmVzIGFuZCB0aGVpciBzb2NpYWwgZWNvbm9taWMgc3RhdHVzIChTRVMpIGFmZmVjdCB0aGVpcgo+IGRlY2lzaW9uPwoKYGBge3J9CmhzYiA8LSByZWFkLmR0YSgiaHR0cHM6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3N0YXQvZGF0YS9oc2JkZW1vLmR0YSIpCgp3aXRoKGhzYiwgdGFibGUoc2VzLCBwcm9nKSkKYGBgCgpgYGB7cn0KIyBTaW5jZSB3ZSBhcmUgZ29pbmcgdG8gdXNlIEFjYWRlbWljIGFzIHRoZSByZWZlcmVuY2UgZ3JvdXAsIHdlIG5lZWQgcmVsZXZlbCB0aGUgZ3JvdXAuCmhzYiRwcm9nMiA8LSByZWxldmVsKGFzLmZhY3Rvcihoc2IkcHJvZyksIHJlZiA9IDIpCmhzYiRzZXMgPC0gYXMuZmFjdG9yKGhzYiRzZXMpCmxldmVscyhoc2IkcHJvZzIpCmBgYAoKYGBge3J9CiMgUnVuIGEgbXVsdGlub21pYWwgbW9kZWwKbXVsdGlfbW8gPC0gbXVsdGlub20ocHJvZzIgfiBzZXMgKyBtYXRoICsgc2NpZW5jZSArIG1hdGgqc2NpZW5jZSwgZGF0YSA9IGhzYixtb2RlbD1UUlVFKQoKc3VtbWFyeShtdWx0aV9tbykKYGBgCgoqKk1haW4gZWZmZWN0cyoqCgpgYGB7cn0KQW5vdmEobXVsdGlfbW8sIHR5cGU9IklJIikKYGBgCgpgYGB7cn0KUHNldWRvUjIobXVsdGlfbW8sIHdoaWNoID0gYygiQ294U25lbGwiLCJOYWdlbGtlcmtlIikpCmBgYAoKYGBge3J9CmJyb29tOjp0aWR5KG11bHRpX21vLCBjb25mLmludD0gVFJVRSwgY29uZi5sZXZlbCA9IDAuOTUsIGV4cG9uZW50aWF0ZSA9IFQpCmBgYAoKQW5vdGhlciB3YXkgdG8gdW5kZXJzdGFuZCB0aGUgbW9kZWwgdXNpbmcgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGlzCnRvIGxvb2sgYXQgdGhlIGF2ZXJhZ2VkIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGZvciBkaWZmZXJlbnQgdmFsdWVzIG9mCnRoZSBjb250aW51b3VzIHByZWRpY3RvciB2YXJpYWJsZSBtYXRoIHdpdGhpbiBlYWNoIGxldmVsIG9mIHNlcy4KCmBgYHtyfQojIEV4dHJhY3QgY29lZmZpY2llbnRzCmNvZWZfZGYgPC0gYXMuZGF0YS5mcmFtZShjb2VmKG11bHRpX21vKSkKY29lZl9kZiRUZXJtIDwtIHJvdy5uYW1lcyhjb2VmX2RmKQoKIyBNZWx0IHRoZSBkYXRhZnJhbWUgZm9yIGdncGxvdApsaWJyYXJ5KHJlc2hhcGUyKQptZWx0ZWRfY29lZl9kZiA8LSBtZWx0KGNvZWZfZGYsIGlkLnZhcnMgPSBjKCJUZXJtIiwgIihJbnRlcmNlcHQpIikpCgojIFBsb3QgdXNpbmcgZ2dwbG90MgpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChtZWx0ZWRfY29lZl9kZiwgYWVzKHggPSBUZXJtLCB5ID0gdmFsdWUsIGZpbGwgPSB2YXJpYWJsZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHggPSAiVGVybSIsIHkgPSAiQ29lZmZpY2llbnQiLCBmaWxsID0gIk91dGNvbWUiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKCmBgYAoKYGBge3J9CnBsb3QoZ2dwcmVkaWN0KG11bHRpX21vLGMoInNlcyIsICJtYXRoIikpKQpgYGAKCiMjIyMgV3JpdGUtdXAKCj4gKFJlc2VhcmNoIFF1ZXN0aW9uKTogV2hlbiBoaWdoIHNjaG9vbCBzdHVkZW50cyBjaG9vc2UgdGhlIHByb2dyYW0KPiAoZ2VuZXJhbCwgdm9jYXRpb25hbCwgYW5kIGFjYWRlbWljIHByb2dyYW1zKSwgaG93IGRvIHRoZWlyIG1hdGggYW5kCj4gc2NpZW5jZSBzY29yZXMgYW5kIHRoZWlyIHNvY2lhbCBlY29ub21pYyBzdGF0dXMgKFNFUykgYWZmZWN0IHRoZWlyCj4gZGVjaXNpb24/Cj4KPiA+IEEgb25lLXVuaXQgaW5jcmVhc2UgaW4gbWF0aCBpcyBhc3NvY2lhdGVkIHdpdGggdGhlIGRlY3JlYXNlIGluIHRoZQo+ID4gbG9nIG9kZHMgb2YgYmVpbmcgaW4gZ2VuZXJhbCBwcm9ncmFtIHZzLiBhY2FkZW1pYyBwcm9ncmFtIGluIHRoZQo+ID4gYW1vdW50IG9mIC4xOSAob25lLXVuaXQgaW5jcmVhc2UgaW4gdGhlIHZhcmlhYmxlIG1hdGggaXMgLjgzIGZvcgo+ID4gYmVpbmcgaW4gZ2VuZXJhbCBwcm9ncmFtIHZzLiBhY2FkZW1pYyBwcm9ncmFtKS4KPgo+ID4gU3R1ZGVudHMgaW4gdGhlIGhpZ2hlc3Qgc29jaWFsIHN0YXR1cyBsZXZlbCAoU0VTID0gMyksIGNvbXBhcmVkIHRvCj4gPiB0aGUgbG93ZXN0IChTRVMgPSAxKSwgYXJlIGxlc3MgbGlrZWx5IHRvIGJlIGluIHRoZSBnZW5lcmFsIHByb2dyYW0KPiA+IGNvbXBhcmVkIHRvIHRoZSBhY2FkZW1pYyBvbmUuIFRoZSBjaGFuY2UgZGVjcmVhc2VzIGJ5IGFib3V0IDEuMTI1Cj4gPiB0aW1lcywgYiA9IC0xLjEyNSwgV2FsZCDPhzIoMSkgPSAtNS4yNywgcCBcPC4wMDEuIFRoaXMgbWVhbnMgdGhlaXIKPiA+IG9kZHMgYXJlIGFib3V0IDAuMzI1IHRpbWVzIGxvd2VyLCBtYWtpbmcgc3R1ZGVudHMgZnJvbSBsb3dlciBzb2NpYWwKPiA+IHN0YXR1cyBtb3JlIGluY2xpbmVkIHRvIHBpY2sgdGhlIGdlbmVyYWwgcHJvZ3JhbSBvdmVyIHRoZSBhY2FkZW1pYwo+ID4gb25lLgo+Cj4gPiBTaW1pbGFybHksIGZvciBoaWdoIFNFUyBzdHVkZW50cyAoY29tcGFyZWQgdG8gbG93KSwgdGhlIGxpa2VsaWhvb2QKPiA+IG9mIGJlaW5nIGluIHRoZSB2b2NhdGlvbmFsIHByb2dyYW0gaW5zdGVhZCBvZiB0aGUgYWNhZGVtaWMgcHJvZ3JhbQo+ID4gZHJvcHMgYnkgMC41NiB0aW1lcywgYiA9IC0wLjU2LCBXYWxkIM+HMigxKSA9IC0yLjgyLCBwIFw8IDAuMDEuIEluCj4gPiBvdGhlciB3b3JkcywgbG93ZXItc3RhdHVzIHN0dWRlbnRzIChjb21wYXJlZCB0byBoaWdoIFNFUykgcHJlZmVyIHRoZQo+ID4gdm9jYXRpb25hbCBwcm9ncmFtIG92ZXIgdGhlIGFjYWRlbWljIG9uZS4KCiMjIFBvaXNzb24gcmVncmVzc2lvbgoKSGVyZSwgd2UncmUgbG9va2luZyBhdCB3aGV0aGVyIHByb2cgYW5kIG1hdGggcHJlZGljdCBudW1iZXIgb2YgYXdhcmRzCgpgYGB7cn0KcCA8LSByZWFkLmNzdigiaHR0cHM6Ly9zdGF0cy5pZHJlLnVjbGEuZWR1L3N0YXQvZGF0YS9wb2lzc29uX3NpbS5jc3YiKQpwIDwtIHdpdGhpbihwLCB7CiAgcHJvZyA8LSBmYWN0b3IocHJvZywgbGV2ZWxzPTE6MywgbGFiZWxzPWMoIkdlbmVyYWwiLCAiQWNhZGVtaWMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVm9jYXRpb25hbCIpKQogIGlkIDwtIGZhY3RvcihpZCkKfSkKc3VtbWFyeShwKQpgYGAKCmBgYHtyfQpnZ3Bsb3QocCwgYWVzKG51bV9hd2FyZHMsIGZpbGwgPSBwcm9nKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPS41LCBwb3NpdGlvbj0iZG9kZ2UiKSArIHRoZW1lX2NsYXNzaWMoKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KG0xIDwtIGdsbShudW1fYXdhcmRzIH4gcHJvZyArIG1hdGgsIGZhbWlseT0icG9pc3NvbiIsIGRhdGE9cCkpCmBgYAoKQ2FtZXJvbiBhbmQgVHJpdmVkaSAoMjAwOSkgcmVjb21tZW5kZWQgdXNpbmcgcm9idXN0IHN0YW5kYXJkIGVycm9ycyBmb3IKdGhlIHBhcmFtZXRlciBlc3RpbWF0ZXMgdG8gY29udHJvbCBmb3IgbWlsZCB2aW9sYXRpb24gb2YgdGhlCmRpc3RyaWJ1dGlvbiBhc3N1bXB0aW9uIHRoYXQgdGhlIHZhcmlhbmNlIGVxdWFscyB0aGUgbWVhbi4KCmBgYHtyfQpjb3YubTEgPC0gdmNvdkhDKG0xLCB0eXBlPSJIQzAiKQpzdGQuZXJyIDwtIHNxcnQoZGlhZyhjb3YubTEpKQpyLmVzdCA8LSBjYmluZChFc3RpbWF0ZT0gY29lZihtMSksICJSb2J1c3QgU0UiID0gc3RkLmVyciwKIlByKD58enwpIiA9IDIgKiBwbm9ybShhYnMoY29lZihtMSkvc3RkLmVyciksIGxvd2VyLnRhaWw9RkFMU0UpLApMTCA9IGNvZWYobTEpIC0gMS45NiAqIHN0ZC5lcnIsClVMID0gY29lZihtMSkgKyAxLjk2ICogc3RkLmVycikKCnIuZXN0CmBgYAoKIyMjIyBQbG90IHByZWRpY3Rpb25zCgpgYGB7cn0KIyMgY2FsY3VsYXRlIGFuZCBzdG9yZSBwcmVkaWN0ZWQgdmFsdWVzCnAkcGhhdCA8LSBwcmVkaWN0KG0xLCB0eXBlPSJyZXNwb25zZSIpCgojIyBvcmRlciBieSBwcm9ncmFtIGFuZCB0aGVuIGJ5IG1hdGgKcCA8LSBwW3dpdGgocCwgb3JkZXIocHJvZywgbWF0aCkpLCBdCgojIyBjcmVhdGUgdGhlIHBsb3QKZ2dwbG90KHAsIGFlcyh4ID0gbWF0aCwgeSA9IHBoYXQsIGNvbG91ciA9IHByb2cpKSArCiAgZ2VvbV9wb2ludChhZXMoeSA9IG51bV9hd2FyZHMpLCBhbHBoYT0uNSwgcG9zaXRpb249cG9zaXRpb25faml0dGVyKGg9LjIpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAxKSArIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh4ID0gIk1hdGggU2NvcmUiLCB5ID0gIkV4cGVjdGVkIG51bWJlciBvZiBhd2FyZHMiKQpgYGAKCiMjIyMgV3JpdGUgdXAKCj4gQmVpbmcgaW4gYW4gYWNhZGVtaWMgcHJvZ3JhbSB3YXMgYXNzb2NpYXRlZCB3aXRoIGEgc2lnbmlmaWNhbnQKPiBpbmNyZWFzZSBpbiB0aGUgbnVtYmVyIG9mIGF3YXJkcywgYiA9IDEuMDg0LCBTRSA9IDAuMzU4LCB6ID0gMy4wMjUsIHAKPiA9IDAuMDAyNDgsIGluZGljYXRpbmcgdGhhdCBzdHVkZW50cyBpbiBhY2FkZW1pYyBwcm9ncmFtcyB0ZW5kIHRvCj4gcmVjZWl2ZSBtb3JlIGF3YXJkcyB0aGFuIHRob3NlIGluIGdlbmVyYWwgcHJvZ3JhbXMsIGhvbGRpbmcgbWF0aAo+IHNjb3JlcyBjb25zdGFudC4KPgo+IEJlaW5nIGluIGEgdm9jYXRpb25hbCBwcm9ncmFtIGRpZCBub3Qgc2lnbmlmaWNhbnRseSBwcmVkaWN0IHRoZSBudW1iZXIKPiBvZiBhd2FyZHMgY29tcGFyZWQgdG8gYmVpbmcgaW4gYSBnZW5lcmFsIHByb2dyYW0sIGIgPSAwLjM3MCwgU0UgPQo+IDAuNDQxLCB6ID0gMC44MzgsIHAgPSAwLjQwMTc5LCBzdWdnZXN0aW5nIG5vIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4KPiB0aGUgZXhwZWN0ZWQgY291bnQgb2YgYXdhcmRzIGJldHdlZW4gdm9jYXRpb25hbCBhbmQgZ2VuZXJhbCBwcm9ncmFtCj4gc3R1ZGVudHMsIGFnYWluIGhvbGRpbmcgbWF0aCBzY29yZXMgY29uc3RhbnQuCj4KPiBNYXRoIHNjb3JlcyB3ZXJlIGFsc28gYSBzaWduaWZpY2FudCBwcmVkaWN0b3Igb2YgdGhlIG51bWJlciBvZiBhd2FyZHMsCj4gYiA9IDAuMDcwMTUsIFNFID0gMC4wMTA2MCwgeiA9IDYuNjE5LCBwIFw8IDAuMDAwMSwgc3VnZ2VzdGluZyB0aGF0LAo+IHJlZ2FyZGxlc3Mgb2YgcHJvZ3JhbSB0eXBlLCBhbiBpbmNyZWFzZSBpbiBtYXRoIHNjb3JlcyBpcyBhc3NvY2lhdGVkCj4gd2l0aCBhbiBpbmNyZWFzZSBpbiB0aGUgZXhwZWN0ZWQgbnVtYmVyIG9mIGF3YXJkcy4KCiMjIE9yZGluYWwgcmVncmVzc2lvbgoKYGBge3J9CmxpYnJhcnkoTUFTUykgIyBGb3Igb3JkaW5hbCBsb2dpc3RpYyByZWdyZXNzaW9uIChwb2xyKQoKIyBHZW5lcmF0ZSBleGFtcGxlIGRhdGEKc2V0LnNlZWQoMTIzKQpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgU3R1ZHlIb3VycyA9IHJub3JtKDEwMCwgbWVhbiA9IDUsIHNkID0gMiksICMgU2ltdWxhdGluZyBzdHVkeSBob3VycwogIFBlcmZvcm1hbmNlID0gb3JkZXJlZChzYW1wbGUoMTo1LCAxMDAsIHJlcGxhY2UgPSBUUlVFKSwgbGV2ZWxzID0gYygxLCAyLCAzLCA0LCA1KSwgbGFiZWxzID0gYygiUG9vciIsICJCZWxvdyBBdmVyYWdlIiwgIkF2ZXJhZ2UiLCAiQWJvdmUgQXZlcmFnZSIsICJFeGNlbGxlbnQiKSkKKQoKIyBGaXQgYW4gb3JkaW5hbCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsCm1vZGVsIDwtIHBvbHIoUGVyZm9ybWFuY2UgfiBTdHVkeUhvdXJzLCBkYXRhID0gZGF0YSkKCnN1bW1hcnkobW9kZWwpCgpgYGAKCiMjIyMgR2V0IHAtdmFsdWVzCgpgYGB7cn0KIyMgc3RvcmUgdGFibGUKKGN0YWJsZSA8LSBjb2VmKHN1bW1hcnkobW9kZWwpKSkKCiMjIGNhbGN1bGF0ZSBhbmQgc3RvcmUgcCB2YWx1ZXMKcCA8LSBwbm9ybShhYnMoY3RhYmxlWywgInQgdmFsdWUiXSksIGxvd2VyLnRhaWwgPSBGQUxTRSkgKiAyCgojIyBjb21iaW5lZCB0YWJsZQooY3RhYmxlIDwtIGNiaW5kKGN0YWJsZSwgInAgdmFsdWUiID0gcCkpCmBgYAoKVGhlIHNpZ25pZmljYW5jZSBvZiAiQWJvdmUgQXZlcmFnZVx8RXhjZWxsZW50IiBzdWdnZXN0cyB0aGF0IHN0dWR5IGhvdXJzCmJlY29tZSBtb3JlIGluZmx1ZW50aWFsIGluIGRldGVybWluaW5nIHdoZXRoZXIgc3R1ZGVudHMgYWNoaWV2ZSBBYm92ZQpBdmVyYWdlIHZzIEV4Y2VsbGVudC4KCiMjIyMgUGxvdCBvcmRpbmFsCgpgYGB7cn0KCiMgQ3JlYXRlIGEgc2VxdWVuY2Ugb2Ygc3R1ZHkgaG91cnMgZm9yIHByZWRpY3Rpb24KbmV3X2RhdGEgPC0gZGF0YS5mcmFtZShTdHVkeUhvdXJzID0gc2VxKG1pbihkYXRhJFN0dWR5SG91cnMpLCBtYXgoZGF0YSRTdHVkeUhvdXJzKSwgbGVuZ3RoLm91dCA9IDEwMCkpCgojIFByZWRpY3QgcHJvYmFiaWxpdGllcyBmb3IgZWFjaCBwZXJmb3JtYW5jZSBjYXRlZ29yeQpwcmVkaWN0ZWRfcHJvYnMgPC0gYXMuZGF0YS5mcmFtZShwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gbmV3X2RhdGEsIHR5cGUgPSAicHJvYnMiKSkKCiMgUmVuYW1lIHRoZSBjb2x1bW5zIGZvciBjbGFyaXR5CmNvbG5hbWVzKHByZWRpY3RlZF9wcm9icykgPC0gYygiUG9vciIsICJCZWxvdyBBdmVyYWdlIiwgIkF2ZXJhZ2UiLCAiQWJvdmUgQXZlcmFnZSIsICJFeGNlbGxlbnQiKQoKIyBDcmVhdGUgYSB2aXN1YWxpemF0aW9uCmdncGxvdChwcmVkaWN0ZWRfcHJvYnMsIGFlcyh4ID0gbmV3X2RhdGEkU3R1ZHlIb3VycykpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBgUG9vcmAsIGNvbG9yID0gIlBvb3IiKSwgc2l6ZSA9IDEpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBgQmVsb3cgQXZlcmFnZWAsIGNvbG9yID0gIkJlbG93IEF2ZXJhZ2UiKSwgc2l6ZSA9IDEpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBgQXZlcmFnZWAsIGNvbG9yID0gIkF2ZXJhZ2UiKSwgc2l6ZSA9IDEpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBgQWJvdmUgQXZlcmFnZWAsIGNvbG9yID0gIkFib3ZlIEF2ZXJhZ2UiKSwgc2l6ZSA9IDEpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBgRXhjZWxsZW50YCwgY29sb3IgPSAiRXhjZWxsZW50IiksIHNpemUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJPcmRpbmFsIExvZ2lzdGljIFJlZ3Jlc3Npb246IFByZWRpY3RpbmcgU3R1ZGVudCBQZXJmb3JtYW5jZSIsCiAgICAgICB4ID0gIlN0dWR5IEhvdXJzIiwKICAgICAgIHkgPSAiUHJlZGljdGVkIFByb2JhYmlsaXR5IikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJQb29yIiA9ICIjMWY3N2I0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJlbG93IEF2ZXJhZ2UiID0gIiNmZjdmMGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXZlcmFnZSIgPSAiIzJjYTAyYyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBYm92ZSBBdmVyYWdlIiA9ICIjZDYyNzI4IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV4Y2VsbGVudCIgPSAiIzk0NjdiZCIpLAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIlBlcmZvcm1hbmNlIExldmVsIiwKICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygiRXhjZWxsZW50IiwiQWJvdmUgQXZlcmFnZSIsICJBdmVyYWdlIiwgIkJlbG93IEF2ZXJhZ2UiLCAiUG9vciIpKSArICMgRXhwbGljaXRseSBzZXQgdGhlIG9yZGVyCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKCgo=