For SEM Workshop HW #1 Code, skip to Katrina Data: SEM HW 1

This is a short introduction to using Lavaan for mediation and SEM models. I’m using a very basic SEM model with a latent variable and single outcome.

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 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.

1.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.

set.seed(123) # Standardizes the numbers generated by rnorm
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)

1.1 Mediation using the Baron & Kenny method

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

1.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.

1.2 Mediation using lavaan

1.2.1 Structure of lavaan models

  • ~ regressions (observed variables)
  • =~ refers to latent variable
  • := defines new path coefficients (e.g., indirect effect := a*b)
  • (a, b, c): To label each path, add an * plus whatever name you want. For example, if I want to remember that path a is X, I’d put a*X. This is totally optional.
library(lavaan)

model <- "

  # regressions 
  M.coffee ~ a*X.hours   # Path a: X.hours predicts coffee 
  Y.wake ~ b*M.coffee    # Path b: Coffee predicts wakefulness
  Y.wake ~ c*X.hours     # Path c (aka direct effect): X.hours predicts wakefulness

  # indirect effect
  ind := a*b             # Path ab: the product of path a (M~X) and path b (Y~X)

  # total effect: direct + indirect
  total := c + (a*b)     # overall effect of X.hours on Y.wake, accounting for both the direct                                # effect and indirect.
"
med <-sem(model = model, data = Meddata)

summary(med,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                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  M.coffee ~                                                            
    X.hours    (a)    0.663    0.076    8.766    0.000    0.663    0.659
  Y.wake ~                                                              
    M.coffee   (b)    0.424    0.097    4.347    0.000    0.424    0.519
    X.hours    (c)   -0.112    0.098   -1.141    0.254   -0.112   -0.136

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .M.coffee         23.086    3.265    7.071    0.000   23.086    0.565
   .Y.wake           21.945    3.104    7.071    0.000   21.945    0.805

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

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    ind               0.281    0.072    3.894    0.000    0.281    0.342
    total             0.169    0.080    2.103    0.035    0.169    0.206

Model Fit - The chi-square is 0 with 0 dfs, indicating that the model is fully saturated. The model perfectly fits the data.

Path A: More hours since dawn (X) predicts higher rate of coffee consumption (M).

Path B: More coffee consumption (M) means more wakefulness (Y).

Path C: Direct effect of hours since dawn (X) on wakefulness (Y) is not significant.

Indirect effect: When accounting for coffee consumption (M), hours since dawn (X) on wakefulness (Y) is significant.

Total effect: When accounting for coffee consumption (M), hours since dawn (X) on wakefulness (Y) is significant.

R-squared for M.coffee = 0.435, so 43.5% of the variance in coffee consumption is explained by hours since dawn.

R-squared for Y.wake = 0.195, so 19.5% of the variance in wakefulness is explained by coffee consumption and hours since dawn combined.

semPaths(object = med, whatLabels = "par") # "std" = standardized parameter estimates

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


2 Adapting code for SEM

Basic SEM Components.
Basic SEM Components.

SEM Question: How do hours since dawn and coffee consumption (mediator) affect a latent construct of wakefulness, as measured by alertness, focus, and mood?

# Adding variables that represent wakefulness. These will define our new latent variable
Y_alertness <- 0.6*Y.wake + rnorm(N, 0, 2)  
Y_focus <- 0.7*Y.wake + rnorm(N, 0, 2)      
Y_mood <- 0.5*Y.wake + rnorm(N, 0, 2)       

# Combine into the dataset
Meddata_sem <- data.frame(X.hours, M.coffee, Y_alertness, Y_focus, Y_mood)
sem_model <- '
  # latent variable for wakefulness
  Y.wake_latent =~ Y_alertness + Y_focus + Y_mood
  
  # mediation path
  M.coffee ~ a*X.hours
  Y.wake_latent ~ b*M.coffee
  Y.wake_latent ~ c*X.hours

  # indirect effect of X.hours on wakefulness through coffee
  ab := a*b
  total := c + (a*b)
'


fit_sem <- sem(sem_model, data = Meddata_sem)


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

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                        10

  Number of observations                           100

Model Test User Model:
                                                      
  Test statistic                                 5.455
  Degrees of freedom                                 4
  P-value (Chi-square)                           0.244

Model Test Baseline Model:

  Test statistic                               244.930
  Degrees of freedom                                10
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    0.994
  Tucker-Lewis Index (TLI)                       0.985

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)              -1018.423
  Loglikelihood unrestricted model (H1)      -1015.696
                                                      
  Akaike (AIC)                                2056.847
  Bayesian (BIC)                              2082.899
  Sample-size adjusted Bayesian (SABIC)       2051.316

Root Mean Square Error of Approximation:

  RMSEA                                          0.060
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.172
  P-value H_0: RMSEA <= 0.050                    0.362
  P-value H_0: RMSEA >= 0.080                    0.475

Standardized Root Mean Square Residual:

  SRMR                                           0.029

Parameter Estimates:

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

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  Y.wake_latent =~                                                      
    Y_alertness       1.000                               2.888    0.784
    Y_focus           1.212    0.131    9.287    0.000    3.502    0.896
    Y_mood            0.970    0.107    9.064    0.000    2.802    0.857

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  M.coffee ~                                                            
    X.hours    (a)    0.663    0.076    8.766    0.000    0.663    0.659
  Y.wake_latent ~                                                       
    M.coffee   (b)    0.233    0.061    3.795    0.000    0.081    0.516
    X.hours    (c)   -0.092    0.059   -1.545    0.122   -0.032   -0.202

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Y_alertness       5.228    0.926    5.646    0.000    5.228    0.385
   .Y_focus           3.003    0.901    3.335    0.001    3.003    0.197
   .Y_mood            2.837    0.650    4.362    0.000    2.837    0.265
   .M.coffee         23.086    3.265    7.071    0.000   23.086    0.565
   .Y.wake_latent     6.929    1.558    4.447    0.000    0.831    0.831

R-Square:
                   Estimate
    Y_alertness       0.615
    Y_focus           0.803
    Y_mood            0.735
    M.coffee          0.435
    Y.wake_latent     0.169

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    ab                0.154    0.044    3.483    0.000    0.053    0.340
    total             0.063    0.048    1.310    0.190    0.022    0.138

Model Fit: chi-square represents how well the model fits the data. The fact that it’s not significant means there’s no significant difference between the predicted vs actual data.

Latent Variables: these estimates represent the factor loadings. So, how well do these variables indicate wakefulness? Note that “alertness” is fixed at 1.00 to act as a reference for the other loadings and our latent variable (wake) is measured on the same scale as alertness. They’re all significant and strong indicators.

Path A: Hours since dawn (X) predicts coffee consumption (M).

Path B: Coffee consumption (M) predicts wakefulness (Y).

Path C: No significant direct relationship between hours since dawn (X) and wakefulness (Y)

Indirect Effect: Significant indirect effect of hours (X) on wakefulness (Y) through coffee (M)

Total Effect: When we combine the direct and indirect effects, the total effect is basically not significant (p = 0.098). So, it seems that hours (X) has a marginal/not significant effect on wake (Y) and the relationship is primarily driven by coffee (M).

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

3 Katrina Data: SEM HW 1

MPlus Code

VARIABLE:
      NAMES ARE y1-y23 rc mm pc as;
      USEVARIABLES ARE rc mm pc as;

 ANALYSIS:
      TYPE IS GENERAL;
      ESTIMATOR IS ML;
      ITERATIONS = 1000;
      CONVERGENCE = 0.00005;
      BOOTSTRAP = 5000;

  MODEL:
      mm on rc;
      pc on rc;
      as on rc mm pc;
      mm WITH pc;

  Model indirect: as IND mm rc;
                  as IND pc rc;

Katrina dataset variables

  • IV = Religious commitment (rc)
  • M1 = Meaning making (mm)
  • M2 = Perceived control (pc)
  • DV = Acute stress (ac)

Sample code for multiple mediation

multipleMediation <- ’ store the model

Y ~ b1 * M1 + b2 * M2 + c * X DV ~ Mediator1 + Mediator2 + IV

M1 ~ a1 * X A path 1 (first mediation)

M2 ~ a2 * X A path 2 (second mediation)

M1 ~~ M2 relationship between mediators

indirect1 := a1 * b1 first indirect effect (X and M1)

indirect2 := a2 * b2 second indirect effect (X and M2)

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

In the results table below, the “Std.all” column matches the MPlus output.

katrina_data <- read_sav("/Users/kareenadelrosario/Downloads/Katrina.sav")
colnames(katrina_data)
 [1] "asds1_1_1"  "asds1_2_1"  "asds1_3_1"  "asds1_4_1"  "asds1_5_1"  "asds1_6_1" 
 [7] "asds1_7_1"  "asds1_8_1"  "asds1_9_1"  "asds1_10_1" "asds1_11_1" "asds1_12_1"
[13] "asds1_13_1" "asds1_14_1" "asds1_15_1" "asds1_16_1" "asds1_17_1" "asds1_18_1"
[19] "asds1_19_1" "RACE"       "PRC"        "PRP"        "SC"         "RC"        
[25] "MM"         "PC"         "AS"        
library(lavaan)

sem_katrina <- '

  # mediation path
  MM ~ a1*RC
  PC ~ a2*RC
  AS ~ c*RC + b1*MM + b2*PC

  MM~~PC
  
  # indirect effect of X.hours on wakefulness through coffee
  indirect1 := a1*b1
  indirect2 := a2*b2
  total := c + (a1*b2) + (a2*b2)
  

'
medsem <- sem(model = sem_katrina, data = katrina_data, se="bootstrap")

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

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                         9

  Number of observations                           132

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

Model Test Baseline Model:

  Test statistic                                30.956
  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)               -999.398
  Loglikelihood unrestricted model (H1)       -999.398
                                                      
  Akaike (AIC)                                2016.796
  Bayesian (BIC)                              2042.741
  Sample-size adjusted Bayesian (SABIC)       2014.274

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             995

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  MM ~                                                                  
    RC        (a1)   -0.035    0.028   -1.280    0.200   -0.035   -0.118
  PC ~                                                                  
    RC        (a2)    0.114    0.069    1.657    0.097    0.114    0.152
  AS ~                                                                  
    RC         (c)    0.478    0.524    0.912    0.362    0.478    0.068
    MM        (b1)    6.631    1.811    3.661    0.000    6.631    0.283
    PC        (b2)   -1.816    0.793   -2.289    0.022   -1.816   -0.194

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
 .MM ~~                                                                 
   .PC               -0.346    0.142   -2.436    0.015   -0.346   -0.216

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .MM                0.643    0.076    8.483    0.000    0.643    0.986
   .PC                3.994    0.350   11.419    0.000    3.994    0.977
   .AS              308.962   30.681   10.070    0.000  308.962    0.861

R-Square:
                   Estimate
    MM                0.014
    PC                0.023
    AS                0.139

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    indirect1        -0.234    0.198   -1.180    0.238   -0.234   -0.033
    indirect2        -0.207    0.163   -1.266    0.206   -0.207   -0.030
    total             0.336    0.529    0.634    0.526    0.336    0.062
mediationPlot(medsem, indirect = TRUE, whatLabels = "std", base_size = 5)

LS0tCnRpdGxlOiAnSW50cm9kdWN0aW9uIHRvIExhdmFhbjogTWVkaWF0aW9uIGFuZCBTRU0nCmF1dGhvcjogIkthcmVlbmEgZGVsIFJvc2FyaW8iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIHRvYzogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IGZhbHNlCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCioqRm9yIFNFTSBXb3Jrc2hvcCBIVyAjMSBDb2RlLCBza2lwIHRvIEthdHJpbmEgRGF0YTogU0VNIEhXIDEqKgoKYGBgez1odG1sfQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKCmgxLnRpdGxlIHsKICBjb2xvcjogRGFya0JsdWU7Cn0KaDEgeyAvKiBIZWFkZXIgMSAqLwogIGNvbG9yOiBEYXJrQmx1ZTsKfQpoMiB7IC8qIEhlYWRlciAyICovCiAgY29sb3I6IERhcmtCbHVlOwp9CmgzIHsgLyogSGVhZGVyIDMgKi8KICBjb2xvcjogRGFya0JsdWU7Cgo8L3N0eWxlPgpgYGAKCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BLAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgdGlkeSA9ICdzdHlsZXInLAogICAgICAgICAgICAgICAgICAgICAgZXJyb3IgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICBoaWdobGlnaHQgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgIHByb21wdCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgUi5vcHRpb25zID0gbGlzdCh3aWR0aCA9IDkwKSkKb3B0aW9ucyhzY2lwZW4gPSA5OTkpCgpwcmludF9wIDwtIGZ1bmN0aW9uKHApIHsKICBwcmludChyb3VuZChwLCA0KSwgcXVvdGUgPSBGQUxTRSkKfQpgYGAKCipUaGlzIGlzIGEgc2hvcnQgaW50cm9kdWN0aW9uIHRvIHVzaW5nIExhdmFhbiBmb3IgbWVkaWF0aW9uIGFuZCBTRU0gbW9kZWxzLiBJJ20gdXNpbmcgYSB2ZXJ5IGJhc2ljIFNFTSBtb2RlbCB3aXRoIGEgbGF0ZW50IHZhcmlhYmxlIGFuZCBzaW5nbGUgb3V0Y29tZS4qCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KCnBrZ3MgPC0gYygidGlkeXZlcnNlIiwgCiAgICAgICAgICAiZHBseXIiLCAKICAgICAgICAgICJoYXZlbiIsIAogICAgICAgICAgImZvcmVpZ24iLCAKICAgICAgICAgICJsbWU0IiwgCiAgICAgICAgICAibmxtZSIsIAogICAgICAgICAgImxzciIsIAogICAgICAgICAgImVtbWVhbnMiLCAKICAgICAgICAgICJhZmV4IiwgCiAgICAgICAgICAia25pdHIiLCAKICAgICAgICAgICJrYWJsZUV4dHJhIiwgCiAgICAgICAgICAiY2FyIiwKICAgICAgICAgICJtZWRpYXRpb24iLAogICAgICAgICAgInJvY2tjaGFsayIsCiAgICAgICAgICAibXVsdGlsZXZlbCIsCiAgICAgICAgICAiYmRhIiwKICAgICAgICAgICJndmxtYSIsCiAgICAgICAgICAic3RhcmdhemVyIiwKICAgICAgICAgICJRdWFudFBzeWMiLAogICAgICAgICAgInBlcXVvZCIsCiAgICAgICAgICAiTUFTUyIsCiAgICAgICAgICAidGV4cmVnIiwKICAgICAgICAgICJwd3IiLAogICAgICAgICAgImVmZmVjdHNpemUiLAogICAgICAgICAgInNlbVBsb3QiLAogICAgICAgICAgImxtdGVzdCIsCiAgICAgICAgICAic2VtcHRvb2xzIiwKICAgICAgICAgICJjb25mbGljdGVkIiwKICAgICAgICAgICJubmV0IiwKICAgICAgICAgICJvcmRpbmFsIiwKICAgICAgICAgICJEZXNjVG9vbHMiKQoKCnBhY2thZ2VzIDwtIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKQpwX3RvX2luc3RhbGwgPC0gcGtnc1shKHBrZ3MgJWluJSBwYWNrYWdlcyldCgppZihsZW5ndGgocF90b19pbnN0YWxsKSA+IDApewogIGluc3RhbGwucGFja2FnZXMocF90b19pbnN0YWxsKQp9CgpsYXBwbHkocGtncywgbGlicmFyeSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQoKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImNhcmRpb21vb24vc2VtTWVkaWF0aW9uIikKbGlicmFyeShzZW1NZWRpYXRpb24pCgojIHRlbGwgUiB3aGljaCBwYWNrYWdlIHRvIHVzZSBmb3IgZnVuY3Rpb25zIHRoYXQgYXJlIGluIG11bHRpcGxlIHBhY2thZ2VzCnRoZXNlX2Z1bmN0aW9ucyA8LSBjKCJtdXRhdGUiLCAic2VsZWN0IiwgInN1bW1hcml6ZSIsICJmaWx0ZXIiKQpsYXBwbHkodGhlc2VfZnVuY3Rpb25zLCBjb25mbGljdF9wcmVmZXIsICJkcGx5ciIpCgpjb25mbGljdF9wcmVmZXIoIm11dGF0ZSIsICJkcGx5ciIpCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIikKY29uZmxpY3RfcHJlZmVyKCJzdW1tYXJpemUiLCAiZHBseXIiKQoKCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBNZWRpYXRpb24gQW5hbHlzZXMKClRoZSBCYXJvbiAmIEtlbm55IG1ldGhvZCBpcyBhbW9uZyB0aGUgb3JpZ2luYWwgbWV0aG9kcyBmb3IgdGVzdGluZyBmb3IKbWVkaWF0aW9uIGJ1dCB0ZW5kcyB0byBoYXZlIGxvdyBzdGF0aXN0aWNhbCBwb3dlci4gV2UncmUgY292ZXJpbmcgaXQKaGVyZSBiZWNhdXNlIGl0IHByb3ZpZGVzIGEgdmVyeSBjbGVhciBhcHByb2FjaCB0byBlc3RhYmxpc2hpbmcKcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHZhcmlhYmxlcyBhbmQgaXMgc3RpbGwgb2NjYXNpb25hbGx5IHJlcXVlc3RlZCBieQpyZXZpZXdlcnMuIAoKTWVkaWF0aW9uIHRlc3RzIHdoZXRoZXIgdGhlIGVmZmVjdHMgb2YgWCAodGhlIGluZGVwZW5kZW50IHZhcmlhYmxlKSBvbiBZCih0aGUgZGVwZW5kZW50IHZhcmlhYmxlKSBvcGVyYXRlIHRocm91Z2ggYSB0aGlyZCB2YXJpYWJsZSwgTSAodGhlCm1lZGlhdG9yKS4gSW4gdGhpcyB3YXksIG1lZGlhdG9ycyBleHBsYWluIHRoZSBjYXVzYWwgcmVsYXRpb25zaGlwCmJldHdlZW4gdHdvIHZhcmlhYmxlcyBvciAiaG93IiB0aGUgcmVsYXRpb25zaGlwIHdvcmtzLgoKIVtCYXNpYyBNZWRpYXRpb24gTW9kZWwuXShtZWRpYXRpb25fbW9kZWwucG5nKQoKYGBgICAgICAgICAgCmMgID0gdGhlIHRvdGFsIGVmZmVjdCBvZiBYIG9uIFkgd2l0aCBubyBjb25zaWRlcmF0aW9uIG9mIG1lZGlhdG9yIHZhcmlhYmxlcwpjJyA9IHRoZSBkaXJlY3QgZWZmZWN0IG9mIFggb24gWSBhZnRlciBjb250cm9sbGluZyBmb3IgTSAKYWIgPSB0aGUgaW5kaXJlY3QgZWZmZWN0IG9mIFggb24gWSB0aHJvdWdoIE0KCmBgYAoKVGhlIGFib3ZlIHNob3dzIHRoZSBzdGFuZGFyZCBtZWRpYXRpb24gbW9kZWwuICoqRnVsbCBtZWRpYXRpb24qKgpvY2N1cnMgd2hlbiB0aGUgZWZmZWN0IG9mIFggb24gWSBkaXNhcHBlYXJzIHdpdGggTSBpbiB0aGUgbW9kZWwuCioqUGFydGlhbCBtZWRpYXRpb24qKiBvY2N1cnMgd2hlbiB0aGUgZWZmZWN0IG9mIFggb24gWSBkZWNyZWFzZXMgYnkgYQpub250cml2aWFsIGFtb3VudCAodGhlIGFjdHVhbCBhbW91bnQgaXMgdXAgZm9yIGRlYmF0ZSkgd2l0aCBNIGluIHRoZQptb2RlbC4KCiMjIyBFeGFtcGxlIE1lZGlhdGlvbiBEYXRhCgpJbiB0aGlzIGV4YW1wbGUgd2UnbGwgc2F5IHdlIGFyZSBpbnRlcmVzdGVkIGluIHdoZXRoZXIgdGhlICoqbnVtYmVyIG9mCmhvdXJzIHNpbmNlIGRhd24gKFgpKiogYWZmZWN0IHRoZSAqKnN1YmplY3RpdmUgcmF0aW5ncyBvZiB3YWtlZnVsbmVzcwooWSkqKiAxMDAgZ3JhZHVhdGUgc3R1ZGVudHMgdGhyb3VnaCB0aGUgKipjb25zdW1wdGlvbiBvZiBjb2ZmZWUgKE0pLioqCgpOb3RlIHRoYXQgd2UgYXJlIGludGVudGlvbmFsbHkgY3JlYXRpbmcgYSBtZWRpYXRpb24gZWZmZWN0IGhlcmUgKGJlY2F1c2UKc3RhdGlzdGljcyBpcyBhbHdheXMgbW9yZSBmdW4gaWYgd2UgaGF2ZSBzb21ldGhpbmcgdG8gZmluZCkgYW5kIHdlIGRvIHNvCmJlbG93IGJ5IGNyZWF0aW5nIE0gc28gdGhhdCBpdCBpcyByZWxhdGVkIHRvIFggYW5kIFkgc28gdGhhdCBpdCBpcwpyZWxhdGVkIHRvIE0uIFRoaXMgY3JlYXRlcyB0aGUgY2F1c2FsIGNoYWluIGZvciBvdXIgYW5hbHlzaXMgdG8gcGFyc2UuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0Kc2V0LnNlZWQoMTIzKSAjIFN0YW5kYXJkaXplcyB0aGUgbnVtYmVycyBnZW5lcmF0ZWQgYnkgcm5vcm0KTiA8LSAxMDAgIyBOdW1iZXIgb2YgcGFydGljaXBhbnRzOyBncmFkdWF0ZSBzdHVkZW50cwpYLmhvdXJzIDwtIHJub3JtKE4sIDE3NSwgNykgIyBJVjsgaG91cnMgc2luY2UgZGF3bgpNLmNvZmZlZSA8LSAwLjcqWC5ob3VycyArIHJub3JtKE4sIDAsIDUpICMgU3VzcGVjdGVkIG1lZGlhdG9yOyBjb2ZmZWUgY29uc3VtcHRpb24gClkud2FrZSA8LSAwLjQqTS5jb2ZmZWUgKyBybm9ybShOLCAwLCA1KSAjIERWOyB3YWtlZnVsbmVzcwpNZWRkYXRhIDwtIGRhdGEuZnJhbWUoWC5ob3VycywgTS5jb2ZmZWUsIFkud2FrZSkKYGBgCgojIyBNZWRpYXRpb24gdXNpbmcgdGhlIEJhcm9uICYgS2VubnkgbWV0aG9kCgpUaGlzIGlzIHRoZSBvcmlnaW5hbCA0LXN0ZXAgbWV0aG9kIHVzZWQgdG8gZGVzY3JpYmUgYSBtZWRpYXRpb24gZWZmZWN0LgpTdGVwcyAxIGFuZCAyIHVzZSBiYXNpYyBsaW5lYXIgcmVncmVzc2lvbiB3aGlsZSBzdGVwcyAzIGFuZCA0IHVzZQptdWx0aXBsZSByZWdyZXNzaW9uLgoKTm93LCBsZXQncyBhcHBseSB0aGUgQmFyb24gJiBLZW5ueSBtZXRob2QgdG8gb3VyIGRhdGE6CgpUaGUgU3RlcHM6CgoxLiAgRXN0aW1hdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIFggb24gWSAoaG91cnMgc2luY2UgZGF3biBvbiBkZWdyZWUKICAgIG9mIHdha2VmdWxuZXNzKQogICAgLSAgIFBhdGggImMiIG11c3QgYmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSAwOyBtdXN0IGhhdmUgYQogICAgICAgICoqdG90YWwgZWZmZWN0IGJldHdlZW4gdGhlIElWICYgRFYqKgogICAgLSAgICoqWSBcfiBYID0gU0lHTklGSUNBTlQqKgoyLiAgRXN0aW1hdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIFggb24gTSAoaG91cnMgc2luY2UgZGF3biBvbiBjb2ZmZWUKICAgIGNvbnN1bXB0aW9uKQogICAgLSAgIFBhdGggImEiIG11c3QgYmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSAwOyAqKklWIGFuZAogICAgICAgIG1lZGlhdG9yIG11c3QgYmUgcmVsYXRlZC4qKgogICAgLSAgICoqTSBcfiBYID0gU0lHTklGSUNBTlQqKgozLiAgRXN0aW1hdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIE0gb24gWSBjb250cm9sbGluZyBmb3IgWCAoY29mZmVlCiAgICBjb25zdW1wdGlvbiBvbiB3YWtlZnVsbmVzcywgY29udHJvbGxpbmcgZm9yIGhvdXJzIHNpbmNlIGRhd24pCiAgICAtICAgUGF0aCAiYiIgbXVzdCBiZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIDA7ICoqbWVkaWF0b3IgYW5kCiAgICAgICAgRFYgbXVzdCBiZSByZWxhdGVkLioqCiAgICAtICAgKipZIFx+IE0gKyBYID0gU0lHTklGSUNBTlQqKgo0LiAgRXN0aW1hdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIFkgb24gWCBjb250cm9sbGluZyBmb3IgTQogICAgKHdha2VmdWxuZXNzIG9uIGhvdXJzIHNpbmNlIGRhd24sIGNvbnRyb2xsaW5nIGZvciBjb2ZmZWUKICAgIGNvbnN1bXB0aW9uKQogICAgLSAgIFNob3VsZCBiZSAqKm5vbi1zaWduaWZpY2FudCoqIGFuZCBuZWFybHkgMC4KICAgIC0gICAqKlggXH4gWSArIE0gPSBOT04tU0lHTklGSUNBTlQqKgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CiMxLiBUb3RhbCBFZmZlY3QKZml0IDwtIGxtKFkud2FrZSB+IFguaG91cnMsIGRhdGE9TWVkZGF0YSkKCiMyLiBQYXRoIEEgKFggb24gTSkKZml0YSA8LSBsbShNLmNvZmZlZSB+IFguaG91cnMsIGRhdGE9TWVkZGF0YSkKCiMzLiBQYXRoIEIgKE0gb24gWSwgY29udHJvbGxpbmcgZm9yIFgpCmZpdGIgPC0gbG0oWS53YWtlIH4gTS5jb2ZmZWUgKyBYLmhvdXJzLCBkYXRhPU1lZGRhdGEpCgojNC4gUmV2ZXJzZWQgUGF0aCBDIChZIG9uIFgsIGNvbnRyb2xsaW5nIGZvciBNKQpmaXRjIDwtIGxtKFguaG91cnMgfiBZLndha2UgKyBNLmNvZmZlZSwgZGF0YT1NZWRkYXRhKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdhc2lzJ30KI1N1bW1hcnkgVGFibGUKc3RhcmdhemVyOjpzdGFyZ2F6ZXIoZml0LCBmaXRhLCBmaXRiLCBmaXRjLCB0eXBlPSJodG1sIiwgZGlnaXRzID0gMiwgZm9udC5zaXplID0gImZvb3Rub3Rlc2l6ZSIsdGl0bGUgPSAiQmFyb24gYW5kIEtlbm55IE1ldGhvZCIpCmBgYAoKIyMjIEludGVycHJldGluZyBCYXJvbiAmIEtlbm55IFJlc3VsdHMKCi0gICBIZXJlIHdlIGZpbmQgdGhhdCBvdXIgdG90YWwgZWZmZWN0IG1vZGVsIHNob3dzIGEgc2lnbmlmaWNhbnQKICAgICoqcG9zaXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gaG91cnMgc2luY2UgZGF3biAoWCkgYW5kIHdha2VmdWxuZXNzCiAgICAoWSkqKi4KLSAgIE91ciBQYXRoIEEgbW9kZWwgc2hvd3MgdGhhdCAqKmhvdXJzIHNpbmNlIGRhd24gKFgpIGlzIGFsc28KICAgIHBvc2l0aXZlbHkgcmVsYXRlZCB0byBjb2ZmZWUgY29uc3VtcHRpb24gKE0pKiouCi0gICBPdXIgUGF0aCBCIG1vZGVsIHRoZW4gc2hvd3MgdGhhdCAqKmNvZmZlZSBjb25zdW1wdGlvbiAoTSkgcG9zaXRpdmVseQogICAgcHJlZGljdHMgd2FrZWZ1bG5lc3MgKFkpIHdoZW4gY29udHJvbGxpbmcgZm9yIGhvdXJzIHNpbmNlIGRhd24KICAgIChYKSoqLgotICAgRmluYWxseSwgKip3YWtlZnVsbmVzcyAoWSkgZG9lcyBub3QgcHJlZGljdCBob3VycyBzaW5jZSBkYXduIChYKQogICAgd2hlbiBjb250cm9sbGluZyBmb3IgY29mZmVlIGNvbnN1bXB0aW9uIChNKSoqLgoKU2luY2UgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGhvdXJzIHNpbmNlIGRhd24gYW5kIHdha2VmdWxuZXNzIGlzIG5vCmxvbmdlciBzaWduaWZpY2FudCB3aGVuIGNvbnRyb2xsaW5nIGZvciBjb2ZmZWUgY29uc3VtcHRpb24sIHRoaXMKc3VnZ2VzdHMgdGhhdCBjb2ZmZWUgY29uc3VtcHRpb24gZG9lcyBpbiBmYWN0IG1lZGlhdGUgdGhpcyByZWxhdGlvbnNoaXAuCkhvd2V2ZXIsIHRoaXMgbWV0aG9kIGFsb25lIGRvZXMgbm90IGFsbG93IGZvciBhIGZvcm1hbCB0ZXN0IG9mIHRoZQppbmRpcmVjdCBlZmZlY3Qgc28gd2UgZG9uJ3Qga25vdyBpZiB0aGUgY2hhbmdlIGluIHRoaXMgcmVsYXRpb25zaGlwIGlzCnRydWx5IG1lYW5pbmdmdWwuCgojIyBNZWRpYXRpb24gdXNpbmcgbGF2YWFuCgojIyMgU3RydWN0dXJlIG9mIGxhdmFhbiBtb2RlbHMKCiAgLSAqKn4qKiByZWdyZXNzaW9ucyAob2JzZXJ2ZWQgdmFyaWFibGVzKQogIC0gKio9fioqIHJlZmVycyB0byBsYXRlbnQgdmFyaWFibGUKICAtICoqOj0qKiBkZWZpbmVzIG5ldyBwYXRoIGNvZWZmaWNpZW50cyAoZS5nLiwgaW5kaXJlY3QgZWZmZWN0IDo9IGEqYikKICAtICoqKGEsIGIsIGMpKio6IFRvIGxhYmVsIGVhY2ggcGF0aCwgYWRkIGFuICogcGx1cyB3aGF0ZXZlciBuYW1lIHlvdSB3YW50LiBGb3IgZXhhbXBsZSwgaWYgSSB3YW50IHRvIHJlbWVtYmVyIHRoYXQgcGF0aCBhIGlzIFgsIEknZCBwdXQgYSpYLiBUaGlzIGlzIHRvdGFsbHkgb3B0aW9uYWwuCgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkobGF2YWFuKQoKbW9kZWwgPC0gIgoKICAjIHJlZ3Jlc3Npb25zIAogIE0uY29mZmVlIH4gYSpYLmhvdXJzICAgIyBQYXRoIGE6IFguaG91cnMgcHJlZGljdHMgY29mZmVlIAogIFkud2FrZSB+IGIqTS5jb2ZmZWUgICAgIyBQYXRoIGI6IENvZmZlZSBwcmVkaWN0cyB3YWtlZnVsbmVzcwogIFkud2FrZSB+IGMqWC5ob3VycyAgICAgIyBQYXRoIGMgKGFrYSBkaXJlY3QgZWZmZWN0KTogWC5ob3VycyBwcmVkaWN0cyB3YWtlZnVsbmVzcwoKICAjIGluZGlyZWN0IGVmZmVjdAogIGluZCA6PSBhKmIgICAgICAgICAgICAgIyBQYXRoIGFiOiB0aGUgcHJvZHVjdCBvZiBwYXRoIGEgKE1+WCkgYW5kIHBhdGggYiAoWX5YKQoKICAjIHRvdGFsIGVmZmVjdDogZGlyZWN0ICsgaW5kaXJlY3QKICB0b3RhbCA6PSBjICsgKGEqYikgICAgICMgb3ZlcmFsbCBlZmZlY3Qgb2YgWC5ob3VycyBvbiBZLndha2UsIGFjY291bnRpbmcgZm9yIGJvdGggdGhlIGRpcmVjdCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBlZmZlY3QgYW5kIGluZGlyZWN0LgoiCgpgYGAKCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWVkIDwtc2VtKG1vZGVsID0gbW9kZWwsIGRhdGEgPSBNZWRkYXRhKQoKc3VtbWFyeShtZWQsc3RhbmRhcmRpemVkPVQsZml0Lm1lYXN1cmVzPVQscnNxdWFyZT1UKQpgYGAKCioqTW9kZWwgRml0KioKLSBUaGUgY2hpLXNxdWFyZSBpcyAwIHdpdGggMCBkZnMsIGluZGljYXRpbmcgdGhhdCB0aGUgbW9kZWwgaXMgZnVsbHkgc2F0dXJhdGVkLiBUaGUgbW9kZWwgcGVyZmVjdGx5IGZpdHMgdGhlIGRhdGEuCgoqKlBhdGggQToqKiBNb3JlIGhvdXJzIHNpbmNlIGRhd24gKFgpIHByZWRpY3RzIGhpZ2hlciByYXRlIG9mIGNvZmZlZSBjb25zdW1wdGlvbiAoTSkuCgoqKlBhdGggQjoqKiBNb3JlIGNvZmZlZSBjb25zdW1wdGlvbiAoTSkgbWVhbnMgbW9yZSB3YWtlZnVsbmVzcyAoWSkuCgoqKlBhdGggQzoqKiBEaXJlY3QgZWZmZWN0IG9mIGhvdXJzIHNpbmNlIGRhd24gKFgpIG9uIHdha2VmdWxuZXNzIChZKSBpcyBub3Qgc2lnbmlmaWNhbnQuCgoqKkluZGlyZWN0IGVmZmVjdDoqKiBXaGVuIGFjY291bnRpbmcgZm9yIGNvZmZlZSBjb25zdW1wdGlvbiAoTSksIGhvdXJzIHNpbmNlIGRhd24gKFgpIG9uIHdha2VmdWxuZXNzIChZKSBpcyBzaWduaWZpY2FudC4KCioqVG90YWwgZWZmZWN0OioqIFdoZW4gYWNjb3VudGluZyBmb3IgY29mZmVlIGNvbnN1bXB0aW9uIChNKSwgaG91cnMgc2luY2UgZGF3biAoWCkgb24gd2FrZWZ1bG5lc3MgKFkpIGlzIHNpZ25pZmljYW50LgoKKipSLXNxdWFyZWQgZm9yIE0uY29mZmVlID0gMC40MzUqKiwgc28gNDMuNSUgb2YgdGhlIHZhcmlhbmNlIGluIGNvZmZlZSBjb25zdW1wdGlvbiBpcyBleHBsYWluZWQgYnkgaG91cnMgc2luY2UgZGF3bi4KCioqUi1zcXVhcmVkIGZvciBZLndha2UgPSAwLjE5NSoqLCBzbyAxOS41JSBvZiB0aGUgdmFyaWFuY2UgaW4gd2FrZWZ1bG5lc3MgaXMgZXhwbGFpbmVkIGJ5IGNvZmZlZSBjb25zdW1wdGlvbiBhbmQgaG91cnMgc2luY2UgZGF3biBjb21iaW5lZC4KCmBgYHtyfQpzZW1QYXRocyhvYmplY3QgPSBtZWQsIHdoYXRMYWJlbHMgPSAicGFyIikgIyAic3RkIiA9IHN0YW5kYXJkaXplZCBwYXJhbWV0ZXIgZXN0aW1hdGVzCmBgYAoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0KbWVkaWF0aW9uUGxvdChtZWQsIGluZGlyZWN0ID0gVFJVRSwgd2hhdExhYmVscyA9ICJlc3QiKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgQWRhcHRpbmcgY29kZSBmb3IgU0VNCgohW0Jhc2ljIFNFTSBDb21wb25lbnRzLl0oc2VtX21vZGVsLnBuZykKCgoKCioqU0VNIFF1ZXN0aW9uOioqIEhvdyBkbyBob3VycyBzaW5jZSBkYXduIGFuZCBjb2ZmZWUgY29uc3VtcHRpb24gKG1lZGlhdG9yKSBhZmZlY3QgYSBsYXRlbnQgY29uc3RydWN0IG9mIHdha2VmdWxuZXNzLCBhcyBtZWFzdXJlZCBieSBhbGVydG5lc3MsIGZvY3VzLCBhbmQgbW9vZD8KCmBgYHtyfQojIEFkZGluZyB2YXJpYWJsZXMgdGhhdCByZXByZXNlbnQgd2FrZWZ1bG5lc3MuIFRoZXNlIHdpbGwgZGVmaW5lIG91ciBuZXcgbGF0ZW50IHZhcmlhYmxlCllfYWxlcnRuZXNzIDwtIDAuNipZLndha2UgKyBybm9ybShOLCAwLCAyKSAgCllfZm9jdXMgPC0gMC43Klkud2FrZSArIHJub3JtKE4sIDAsIDIpICAgICAgCllfbW9vZCA8LSAwLjUqWS53YWtlICsgcm5vcm0oTiwgMCwgMikgICAgICAgCgojIENvbWJpbmUgaW50byB0aGUgZGF0YXNldApNZWRkYXRhX3NlbSA8LSBkYXRhLmZyYW1lKFguaG91cnMsIE0uY29mZmVlLCBZX2FsZXJ0bmVzcywgWV9mb2N1cywgWV9tb29kKQoKYGBgCgoKYGBge3J9CnNlbV9tb2RlbCA8LSAnCiAgIyBsYXRlbnQgdmFyaWFibGUgZm9yIHdha2VmdWxuZXNzCiAgWS53YWtlX2xhdGVudCA9fiBZX2FsZXJ0bmVzcyArIFlfZm9jdXMgKyBZX21vb2QKICAKICAjIG1lZGlhdGlvbiBwYXRoCiAgTS5jb2ZmZWUgfiBhKlguaG91cnMKICBZLndha2VfbGF0ZW50IH4gYipNLmNvZmZlZQogIFkud2FrZV9sYXRlbnQgfiBjKlguaG91cnMKCiAgIyBpbmRpcmVjdCBlZmZlY3Qgb2YgWC5ob3VycyBvbiB3YWtlZnVsbmVzcyB0aHJvdWdoIGNvZmZlZQogIGFiIDo9IGEqYgogIHRvdGFsIDo9IGMgKyAoYSpiKQonCgoKZml0X3NlbSA8LSBzZW0oc2VtX21vZGVsLCBkYXRhID0gTWVkZGF0YV9zZW0pCgoKc3VtbWFyeShmaXRfc2VtLCBzdGFuZGFyZGl6ZWQgPSBULGZpdC5tZWFzdXJlcz1ULHJzcXVhcmU9VCkKYGBgCgoqKk1vZGVsIEZpdDoqKiBjaGktc3F1YXJlIHJlcHJlc2VudHMgaG93IHdlbGwgdGhlIG1vZGVsIGZpdHMgdGhlIGRhdGEuIFRoZSBmYWN0IHRoYXQgaXQncyBub3Qgc2lnbmlmaWNhbnQgbWVhbnMgdGhlcmUncyBubyBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHByZWRpY3RlZCB2cyBhY3R1YWwgZGF0YS4KCioqTGF0ZW50IFZhcmlhYmxlczoqKiB0aGVzZSBlc3RpbWF0ZXMgcmVwcmVzZW50IHRoZSBmYWN0b3IgbG9hZGluZ3MuIFNvLCBob3cgd2VsbCBkbyB0aGVzZSB2YXJpYWJsZXMgaW5kaWNhdGUgd2FrZWZ1bG5lc3M/IE5vdGUgdGhhdCAiYWxlcnRuZXNzIiBpcyBmaXhlZCBhdCAxLjAwIHRvIGFjdCBhcyBhIHJlZmVyZW5jZSBmb3IgdGhlIG90aGVyIGxvYWRpbmdzIGFuZCBvdXIgbGF0ZW50IHZhcmlhYmxlICh3YWtlKSBpcyBtZWFzdXJlZCBvbiB0aGUgc2FtZSBzY2FsZSBhcyBhbGVydG5lc3MuIFRoZXkncmUgYWxsIHNpZ25pZmljYW50IGFuZCBzdHJvbmcgaW5kaWNhdG9ycy4KCioqUGF0aCBBOioqIEhvdXJzIHNpbmNlIGRhd24gKFgpIHByZWRpY3RzIGNvZmZlZSBjb25zdW1wdGlvbiAoTSkuCgoqKlBhdGggQjoqKiBDb2ZmZWUgY29uc3VtcHRpb24gKE0pIHByZWRpY3RzIHdha2VmdWxuZXNzIChZKS4KCioqUGF0aCBDOioqIE5vIHNpZ25pZmljYW50IGRpcmVjdCByZWxhdGlvbnNoaXAgYmV0d2VlbiBob3VycyBzaW5jZSBkYXduIChYKSBhbmQgd2FrZWZ1bG5lc3MgKFkpCgoqKkluZGlyZWN0IEVmZmVjdDoqKiBTaWduaWZpY2FudCBpbmRpcmVjdCBlZmZlY3Qgb2YgaG91cnMgKFgpIG9uIHdha2VmdWxuZXNzIChZKSB0aHJvdWdoIGNvZmZlZSAoTSkKCioqVG90YWwgRWZmZWN0OioqIFdoZW4gd2UgY29tYmluZSB0aGUgZGlyZWN0IGFuZCBpbmRpcmVjdCBlZmZlY3RzLCB0aGUgdG90YWwgZWZmZWN0IGlzIGJhc2ljYWxseSBub3Qgc2lnbmlmaWNhbnQgKHAgPSAwLjA5OCkuIFNvLCBpdCBzZWVtcyB0aGF0IGhvdXJzIChYKSBoYXMgYSBtYXJnaW5hbC9ub3Qgc2lnbmlmaWNhbnQgZWZmZWN0IG9uIHdha2UgKFkpIGFuZCB0aGUgcmVsYXRpb25zaGlwIGlzIHByaW1hcmlseSBkcml2ZW4gYnkgY29mZmVlIChNKS4KCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9Cm1lZGlhdGlvblBsb3QoZml0X3NlbSwgaW5kaXJlY3QgPSBUUlVFLCB3aGF0TGFiZWxzID0gImVzdCIpCmBgYAoKCiMgS2F0cmluYSBEYXRhOiBTRU0gSFcgMQoKKk1QbHVzIENvZGUqCgogICAgVkFSSUFCTEU6CiAgICAgICAgICBOQU1FUyBBUkUgeTEteTIzIHJjIG1tIHBjIGFzOwogICAgICAgICAgVVNFVkFSSUFCTEVTIEFSRSByYyBtbSBwYyBhczsKCiAgICAgQU5BTFlTSVM6CiAgICAgICAgICBUWVBFIElTIEdFTkVSQUw7CiAgICAgICAgICBFU1RJTUFUT1IgSVMgTUw7CiAgICAgICAgICBJVEVSQVRJT05TID0gMTAwMDsKICAgICAgICAgIENPTlZFUkdFTkNFID0gMC4wMDAwNTsKICAgICAgICAgIEJPT1RTVFJBUCA9IDUwMDA7CgogICAgICBNT0RFTDoKICAgICAgICAgIG1tIG9uIHJjOwogICAgICAgICAgcGMgb24gcmM7CiAgICAgICAgICBhcyBvbiByYyBtbSBwYzsKICAgICAgICAgIG1tIFdJVEggcGM7CgogICAgICBNb2RlbCBpbmRpcmVjdDogYXMgSU5EIG1tIHJjOwogICAgICAgICAgICAgICAgICAgICAgYXMgSU5EIHBjIHJjOwoKKkthdHJpbmEgZGF0YXNldCB2YXJpYWJsZXMqCgogIC0gSVYgPSBSZWxpZ2lvdXMgY29tbWl0bWVudCAocmMpCiAgLSBNMSA9IE1lYW5pbmcgbWFraW5nIChtbSkKICAtIE0yID0gUGVyY2VpdmVkIGNvbnRyb2wgKHBjKQogIC0gRFYgPSBBY3V0ZSBzdHJlc3MgKGFjKQoKKlNhbXBsZSBjb2RlIGZvciBtdWx0aXBsZSBtZWRpYXRpb24qCgogIG11bHRpcGxlTWVkaWF0aW9uIDwtICcgKipzdG9yZSB0aGUgbW9kZWwqKgogIAogIFkgfiBiMSAqIE0xICsgYjIgKiBNMiArIGMgKiBYICoqRFYgfiBNZWRpYXRvcjEgKyBNZWRpYXRvcjIgKyBJVioqCiAgCiAgTTEgfiBhMSAqIFggKipBIHBhdGggMSAoZmlyc3QgbWVkaWF0aW9uKSoqCiAgCiAgTTIgfiBhMiAqIFggKipBIHBhdGggMiAoc2Vjb25kIG1lZGlhdGlvbikqKgoKICBNMSB+fiBNMiAqKnJlbGF0aW9uc2hpcCBiZXR3ZWVuIG1lZGlhdG9ycyoqCiAgCiAgaW5kaXJlY3QxIDo9IGExICogYjEgKipmaXJzdCBpbmRpcmVjdCBlZmZlY3QgKFggYW5kIE0xKSoqCiAgCiAgaW5kaXJlY3QyIDo9IGEyICogYjIgKipzZWNvbmQgaW5kaXJlY3QgZWZmZWN0IChYIGFuZCBNMikqKgogIAogIHRvdGFsIDo9IGMgKyAoYTEgKiBiMSkgKyAoYTIgKiBiMikgKip0b3RhbCBlZmZlY3QqKgonCgpJbiB0aGUgcmVzdWx0cyB0YWJsZSBiZWxvdywgdGhlICJTdGQuYWxsIiBjb2x1bW4gbWF0Y2hlcyB0aGUgTVBsdXMgb3V0cHV0LgoKYGBge3J9CmthdHJpbmFfZGF0YSA8LSByZWFkX3NhdigiL1VzZXJzL2thcmVlbmFkZWxyb3NhcmlvL0Rvd25sb2Fkcy9LYXRyaW5hLnNhdiIpCmNvbG5hbWVzKGthdHJpbmFfZGF0YSkKbGlicmFyeShsYXZhYW4pCgpzZW1fa2F0cmluYSA8LSAnCgogICMgbWVkaWF0aW9uIHBhdGgKICBNTSB+IGExKlJDCiAgUEMgfiBhMipSQwogIEFTIH4gYypSQyArIGIxKk1NICsgYjIqUEMKCiAgTU1+flBDCiAgCiAgIyBpbmRpcmVjdCBlZmZlY3Qgb2YgWC5ob3VycyBvbiB3YWtlZnVsbmVzcyB0aHJvdWdoIGNvZmZlZQogIGluZGlyZWN0MSA6PSBhMSpiMQogIGluZGlyZWN0MiA6PSBhMipiMgogIHRvdGFsIDo9IGMgKyAoYTEqYjIpICsgKGEyKmIyKQogIAoKJwptZWRzZW0gPC0gc2VtKG1vZGVsID0gc2VtX2thdHJpbmEsIGRhdGEgPSBrYXRyaW5hX2RhdGEsIHNlPSJib290c3RyYXAiKQoKc3VtbWFyeShtZWRzZW0sIHN0YW5kYXJkaXplZCA9IFQsZml0Lm1lYXN1cmVzPVQscnNxdWFyZT1UKQpgYGAKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9Cm1lZGlhdGlvblBsb3QobWVkc2VtLCBpbmRpcmVjdCA9IFRSVUUsIHdoYXRMYWJlbHMgPSAic3RkIiwgYmFzZV9zaXplID0gNSkKYGBgCg==