Introduction

This document shows you how to set up and run the example from my path analysis lecture using lavaan in R. Some additional packages (semPlot and semptools) are used for creating the path diagrams. This document was created in R Markdown. Note that there are some differences in the parameter estimates for Model 1C compared to those from the piece-wise OLS regression method used in the slides.

Setup

Required Packages

knitr::opts_chunk$set(echo = TRUE)

if (!require("pacman")) install.packages("pacman")
library(pacman)
# Load installed packages, install and load the package if not installed
pacman::p_load(here, rio, conflicted, tidyverse, gtsummary, broom,
               janitor, summarytools, skimr, DataExplorer, inspectdf, Hmisc,
               lavaan, semTools, semoutput, semPlot, sjPlot, psych, readr, dplyr,
               GPArotation, psychTools, semptools, tinytex, stargazer)

Reading in data:

# reading in corr matrix
corr <- '
1.000
.256 1.000
.536 .313 1.000
.505 .637 .653 1.000 '
# add variable names and convert to full correlation matrix
corrfull <- lavaan::getCov(corr, names=c("SES","MA","Mot","AA"))
# add SDs and convert to full covariance matrix
covfull <- lavaan::cor2cov(corrfull, sds=c(1,1,1,1))
# observed covariance matrix
#covfull

0. Separate Regressions

Using psych::lmCor to run the two OLS regressions with correlation matrix as input. Compare the parameter estimates to those we get from lavaan.

psych::lmCor(Mot ~ SES + MA, data=corrfull, n.obs=30, plot=T, main="First Regression Model")

## Call: psych::lmCor(y = Mot ~ SES + MA, data = corrfull, n.obs = 30, 
##     main = "First Regression Model", plot = T)
## 
## Multiple Regression from matrix input 
## 
##  DV =  Mot 
##     slope   se    t      p lower.ci upper.ci  VIF Vy.x
## SES  0.49 0.16 2.97 0.0061     0.15     0.82 1.07 0.26
## MA   0.19 0.16 1.15 0.2600    -0.15     0.52 1.07 0.06
## 
## Residual Standard Error =  0.85  with  27  degrees of freedom
## 
##  Multiple Regression
##        R   R2  Ruw R2uw Shrunken R2 SE of R2 overall F df1 df2       p
## Mot 0.57 0.32 0.54 0.29        0.27     0.12      6.36   2  27 0.00544
psych::lmCor(AA ~ SES + MA + Mot, data=corrfull, n.obs=30, plot=T, main="Second Regression Model")

## Call: psych::lmCor(y = AA ~ SES + MA + Mot, data = corrfull, n.obs = 30, 
##     main = "Second Regression Model", plot = T)
## 
## Multiple Regression from matrix input 
## 
##  DV =  AA 
##     slope   se    t       p lower.ci upper.ci  VIF Vy.x
## SES  0.16 0.14 1.16 0.26000    -0.12     0.44 1.42 0.08
## MA   0.46 0.12 3.79 0.00082     0.21     0.72 1.12 0.30
## Mot  0.42 0.14 3.01 0.00580     0.13     0.71 1.47 0.28
## 
## Residual Standard Error =  0.62  with  26  degrees of freedom
## 
##  Multiple Regression
##       R   R2  Ruw R2uw Shrunken R2 SE of R2 overall F df1 df2        p
## AA 0.81 0.65 0.79 0.62        0.61     0.08     16.23   3  26 3.79e-06

1. Just-identifed Model

1.1 Specifying the model: 1B

# specify the path model
mod1b <- '
Mot ~ a1*SES + a2*MA
AA  ~ c1*SES + c2*MA + b*Mot
#indirect & total effects
SES_Mot_AA := a1*b
MA_Mot_AA  := a2*b
SES_AA_tot := c1 + a1*b
MA_AA_tot  := c2 + a2*b
'

1.2 Running the model: 1B

# fit the path model
mod1b.fit <- lavaan::sem(mod1b, sample.cov=covfull, sample.nobs=30)

1.3 Plotting results as path diagram:

#--making path diagram w my locations specified in 'm'
m <- matrix(c("SES", NA, NA, NA,
               NA, "Mot", NA, "AA",
              "MA", NA, NA, NA), byrow=T, 3, 4)

fig1 <- semPlot::semPaths(mod1b.fit, layout=m, whatLabels = "std",
                          sizeMan=10, edge.label.cex=1.2,
                          nCharNodes=0, nCharEdges = 0,
                          rotation = 2, style = "lisrel", 
                          DoNotPlot=T, residScale=10,
                          edge.color = "black")

#--working with semptools to modify path diagram
my_curve_list        <- c("SES~~MA" = -3)
my_position_list     <- c("AA~SES" =.60, "AA~MA" = .50, "Mot~SES"=.40)
my_rotate_resid_list <- c(Mot=.90, AA=.90)
#using piping to apply my lists
fig1_1 <- fig1 |> set_curve(my_curve_list) |>
  set_edge_label_position(my_position_list)|>
  rotate_resid(my_rotate_resid_list) |>
  mark_sig(mod1b.fit, alpha = c("(n.s.)" = 1.00, "*" = .05)) #adding *, ns
plot(fig1_1)
Figure 1. Path diagram (Model 1B.

Figure 1. Path diagram (Model 1B.

1.4 Full output:

Model 1B is just-identified, so df = 0 and fit is perfect.

summary(mod1b.fit, fit.measures=T, standardized=T, rsquare=T)
## lavaan 0.6.16 ended normally after 1 iteration
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                         7
## 
##   Number of observations                            30
## 
## Model Test User Model:
##                                                       
##   Test statistic                                 0.000
##   Degrees of freedom                                 0
## 
## Model Test Baseline Model:
## 
##   Test statistic                                43.246
##   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)                -62.496
##   Loglikelihood unrestricted model (H1)        -62.496
##                                                       
##   Akaike (AIC)                                 138.993
##   Bayesian (BIC)                               148.801
##   Sample-size adjusted Bayesian (SABIC)        127.007
## 
## 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
##   Mot ~                                                                 
##     SES       (a1)    0.488    0.156    3.133    0.002    0.488    0.488
##     MA        (a2)    0.188    0.156    1.208    0.227    0.188    0.188
##   AA ~                                                                  
##     SES       (c1)    0.160    0.128    1.247    0.213    0.160    0.160
##     MA        (c2)    0.464    0.114    4.066    0.000    0.464    0.464
##     Mot        (b)    0.422    0.131    3.230    0.001    0.422    0.422
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .Mot               0.657    0.170    3.873    0.000    0.657    0.680
##    .AA                0.336    0.087    3.873    0.000    0.336    0.348
## 
## R-Square:
##                    Estimate
##     Mot               0.320
##     AA                0.652
## 
## Defined Parameters:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##     SES_Mot_AA        0.206    0.092    2.249    0.025    0.206    0.206
##     MA_Mot_AA         0.079    0.070    1.132    0.258    0.079    0.079
##     SES_AA_tot        0.366    0.129    2.829    0.005    0.366    0.366
##     MA_AA_tot         0.543    0.129    4.200    0.000    0.543    0.543
Note the indirect and total effects.

2. Revised Model

2.1 Specifying the model: 1C

# specify the path model
mod1c <- '
Mot ~ a1*SES
AA  ~ MA + b*Mot
#indirect effects
SES_Mot_AA := a1*b
'

2.2 Running the model: 1C

# fit the path model
mod1c.fit <- lavaan::sem(mod1c, sample.cov=covfull, sample.nobs=30)

2.3 Plotting results as path diagram:

Note that lavaan calculates the coefficients for MA and Mot as predictors of AA a bit differently than the piece-wise OLS regression method used in the lecture slides. Compare the values for ‘Estimates’ with those for ‘Std.all’ in the Full Output below. Also the \(R^2\) value is a bit different.

#--making path diagram w my locations specified in 'm'
m <- matrix(c("SES", NA, NA, NA,
               NA, "Mot", NA, "AA",
              "MA", NA, NA, NA), byrow=T, 3, 4)

fig2 <- semPlot::semPaths(mod1c.fit, layout=m, whatLabels = "std",
                          sizeMan=10, edge.label.cex=1.2,
                          nCharNodes=0, nCharEdges = 0,
                          rotation = 2, style = "lisrel", 
                          DoNotPlot=T, residScale=10,
                          edge.color = "black")

#--working with semptools to modify path diagram
my_curve_list        <- c("SES~~MA" = -3)
my_position_list     <- c("AA~MA" = .50, "Mot~SES"=.40)
my_rotate_resid_list <- c(Mot=.90, AA=.90)
#using piping to apply my lists
fig2_1 <- fig2 |> set_curve(my_curve_list) |>
  set_edge_label_position(my_position_list)|>
  rotate_resid(my_rotate_resid_list) |>
  mark_sig(mod1c.fit, alpha = c("(n.s.)" = 1.00, "*" = .05)) #adding *, ns
plot(fig2_1)
Figure 2. Path diagram (Model 1C.

Figure 2. Path diagram (Model 1C.

2.4 Full output:

summary(mod1c.fit, fit.measures=T, standardized=T, rsquare=T)
## lavaan 0.6.16 ended normally after 1 iteration
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                         5
## 
##   Number of observations                            30
## 
## Model Test User Model:
##                                                       
##   Test statistic                                 2.940
##   Degrees of freedom                                 2
##   P-value (Chi-square)                           0.230
## 
## Model Test Baseline Model:
## 
##   Test statistic                                43.246
##   Degrees of freedom                                 5
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.975
##   Tucker-Lewis Index (TLI)                       0.939
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)                -63.967
##   Loglikelihood unrestricted model (H1)        -62.496
##                                                       
##   Akaike (AIC)                                 137.933
##   Bayesian (BIC)                               144.939
##   Sample-size adjusted Bayesian (SABIC)        129.372
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.125
##   90 Percent confidence interval - lower         0.000
##   90 Percent confidence interval - upper         0.405
##   P-value H_0: RMSEA <= 0.050                    0.255
##   P-value H_0: RMSEA >= 0.080                    0.707
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.081
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Expected
##   Information saturated (h1) model          Structured
## 
## Regressions:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   Mot ~                                                                 
##     SES       (a1)    0.536    0.154    3.478    0.001    0.536    0.536
##   AA ~                                                                  
##     MA                0.480    0.112    4.300    0.000    0.480    0.501
##     Mot        (b)    0.503    0.112    4.509    0.000    0.503    0.526
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .Mot               0.689    0.178    3.873    0.000    0.689    0.713
##    .AA                0.354    0.091    3.873    0.000    0.354    0.400
## 
## R-Square:
##                    Estimate
##     Mot               0.287
##     AA                0.600
## 
## Defined Parameters:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##     SES_Mot_AA        0.270    0.098    2.754    0.006    0.270    0.282
The indirect effect of SES on AA via Mot is significant.

2.5 Residuals

All z values < |2.54|.

lavResiduals(mod1c.fit)
## $type
## [1] "cor.bentler"
## 
## $cov
##       Mot    AA   SES    MA
## Mot 0.000                  
## AA  0.084 0.085            
## SES 0.000 0.113 0.000      
## MA  0.176 0.088 0.000 0.000
## 
## $cov.z
##       Mot    AA   SES    MA
## Mot 0.000                  
## AA  1.208 1.208            
## SES 0.000 1.213 0.000      
## MA  1.208 1.208 0.000 0.000
## 
## $summary
##                            cov
## srmr                     0.081
## srmr.se                  0.039
## srmr.exactfit.z          0.650
## srmr.exactfit.pvalue     0.258
## usrmr                    0.067
## usrmr.se                 0.096
## usrmr.ci.lower          -0.091
## usrmr.ci.upper           0.226
## usrmr.closefit.h0.value  0.050
## usrmr.closefit.z         0.180
## usrmr.closefit.pvalue    0.429

3 Model Comparison

# comparing nested models via chisq likelihood ratio test
lavTestLRT(mod1b.fit, mod1c.fit, type = "chisq")
## 
## Chi-Squared Difference Test
## 
##           Df    AIC    BIC  Chisq Chisq diff  RMSEA Df diff Pr(>Chisq)
## mod1b.fit  0 138.99 148.80 0.0000                                     
## mod1c.fit  2 137.93 144.94 2.9404     2.9404 0.1252       2     0.2299

Note that the model comparison test is nonsignificant; the more parsimonious Model 1C fits the data as well as the just-identified Model 1B. The \(\chi^2\) value is a bit different than the hand calculation due to the differences in \(R^2\) values between OLS and lavaan.

LS0tDQp0aXRsZTogIlBhdGggQW5hbHlzaXMgTGVjdHVyZSBFeGFtcGxlIg0KYXV0aG9yOiAiSmFzb24gQmVja3N0ZWFkIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgdG9jX2RlcHRoOiAzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQojIyBJbnRyb2R1Y3Rpb24NClRoaXMgZG9jdW1lbnQgc2hvd3MgeW91IGhvdyB0byBzZXQgdXAgYW5kIHJ1biB0aGUgZXhhbXBsZSBmcm9tIG15IHBhdGggYW5hbHlzaXMgbGVjdHVyZSB1c2luZyBgbGF2YWFuYCBpbiBSLiBTb21lIGFkZGl0aW9uYWwgcGFja2FnZXMgKGBzZW1QbG90YCBhbmQgYHNlbXB0b29sc2ApIGFyZSB1c2VkIGZvciBjcmVhdGluZyB0aGUgcGF0aCBkaWFncmFtcy4gIFRoaXMgZG9jdW1lbnQgd2FzIGNyZWF0ZWQgaW4gUiBNYXJrZG93bi4gTm90ZSB0aGF0IHRoZXJlIGFyZSBzb21lIGRpZmZlcmVuY2VzIGluIHRoZSBwYXJhbWV0ZXIgZXN0aW1hdGVzIGZvciBNb2RlbCAxQyBjb21wYXJlZCB0byB0aG9zZSBmcm9tIHRoZSBwaWVjZS13aXNlIE9MUyByZWdyZXNzaW9uIG1ldGhvZCB1c2VkIGluIHRoZSBzbGlkZXMuIA0KDQojIyBTZXR1cA0KUmVxdWlyZWQgUGFja2FnZXMNCmBgYHtyIHNldHVwLCBtZXNzYWdlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KDQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KbGlicmFyeShwYWNtYW4pDQojIExvYWQgaW5zdGFsbGVkIHBhY2thZ2VzLCBpbnN0YWxsIGFuZCBsb2FkIHRoZSBwYWNrYWdlIGlmIG5vdCBpbnN0YWxsZWQNCnBhY21hbjo6cF9sb2FkKGhlcmUsIHJpbywgY29uZmxpY3RlZCwgdGlkeXZlcnNlLCBndHN1bW1hcnksIGJyb29tLA0KICAgICAgICAgICAgICAgamFuaXRvciwgc3VtbWFyeXRvb2xzLCBza2ltciwgRGF0YUV4cGxvcmVyLCBpbnNwZWN0ZGYsIEhtaXNjLA0KICAgICAgICAgICAgICAgbGF2YWFuLCBzZW1Ub29scywgc2Vtb3V0cHV0LCBzZW1QbG90LCBzalBsb3QsIHBzeWNoLCByZWFkciwgZHBseXIsDQogICAgICAgICAgICAgICBHUEFyb3RhdGlvbiwgcHN5Y2hUb29scywgc2VtcHRvb2xzLCB0aW55dGV4LCBzdGFyZ2F6ZXIpDQpgYGANCg0KIyMgUmVhZGluZyBpbiBkYXRhOg0KYGBge3IsIGVjaG89VH0NCiMgcmVhZGluZyBpbiBjb3JyIG1hdHJpeA0KY29yciA8LSAnDQoxLjAwMA0KLjI1NiAxLjAwMA0KLjUzNiAuMzEzIDEuMDAwDQouNTA1IC42MzcgLjY1MyAxLjAwMCAnDQojIGFkZCB2YXJpYWJsZSBuYW1lcyBhbmQgY29udmVydCB0byBmdWxsIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29ycmZ1bGwgPC0gbGF2YWFuOjpnZXRDb3YoY29yciwgbmFtZXM9YygiU0VTIiwiTUEiLCJNb3QiLCJBQSIpKQ0KIyBhZGQgU0RzIGFuZCBjb252ZXJ0IHRvIGZ1bGwgY292YXJpYW5jZSBtYXRyaXgNCmNvdmZ1bGwgPC0gbGF2YWFuOjpjb3IyY292KGNvcnJmdWxsLCBzZHM9YygxLDEsMSwxKSkNCiMgb2JzZXJ2ZWQgY292YXJpYW5jZSBtYXRyaXgNCiNjb3ZmdWxsDQpgYGANCiMjIDAuIFNlcGFyYXRlIFJlZ3Jlc3Npb25zDQpVc2luZyBgcHN5Y2g6OmxtQ29yYCB0byBydW4gdGhlIHR3byBPTFMgcmVncmVzc2lvbnMgd2l0aCBjb3JyZWxhdGlvbiBtYXRyaXggYXMgaW5wdXQuIENvbXBhcmUgdGhlIHBhcmFtZXRlciBlc3RpbWF0ZXMgdG8gdGhvc2Ugd2UgZ2V0IGZyb20gYGxhdmFhbmAuDQpgYGB7cn0NCnBzeWNoOjpsbUNvcihNb3QgfiBTRVMgKyBNQSwgZGF0YT1jb3JyZnVsbCwgbi5vYnM9MzAsIHBsb3Q9VCwgbWFpbj0iRmlyc3QgUmVncmVzc2lvbiBNb2RlbCIpDQpgYGANCg0KYGBge3J9DQpwc3ljaDo6bG1Db3IoQUEgfiBTRVMgKyBNQSArIE1vdCwgZGF0YT1jb3JyZnVsbCwgbi5vYnM9MzAsIHBsb3Q9VCwgbWFpbj0iU2Vjb25kIFJlZ3Jlc3Npb24gTW9kZWwiKQ0KYGBgDQoNCg0KIyMgMS4gSnVzdC1pZGVudGlmZWQgTW9kZWwNCiMjIDEuMSBTcGVjaWZ5aW5nIHRoZSBtb2RlbDogMUINCmBgYHtyLCBlY2hvPVR9DQojIHNwZWNpZnkgdGhlIHBhdGggbW9kZWwNCm1vZDFiIDwtICcNCk1vdCB+IGExKlNFUyArIGEyKk1BDQpBQSAgfiBjMSpTRVMgKyBjMipNQSArIGIqTW90DQojaW5kaXJlY3QgJiB0b3RhbCBlZmZlY3RzDQpTRVNfTW90X0FBIDo9IGExKmINCk1BX01vdF9BQSAgOj0gYTIqYg0KU0VTX0FBX3RvdCA6PSBjMSArIGExKmINCk1BX0FBX3RvdCAgOj0gYzIgKyBhMipiDQonDQpgYGANCg0KIyMgMS4yIFJ1bm5pbmcgdGhlIG1vZGVsOiAxQg0KYGBge3IsIGVjaG89VH0NCiMgZml0IHRoZSBwYXRoIG1vZGVsDQptb2QxYi5maXQgPC0gbGF2YWFuOjpzZW0obW9kMWIsIHNhbXBsZS5jb3Y9Y292ZnVsbCwgc2FtcGxlLm5vYnM9MzApDQpgYGANCg0KIyMgMS4zIFBsb3R0aW5nIHJlc3VsdHMgYXMgcGF0aCBkaWFncmFtOg0KYGBge3IsIGZpZy53aWR0aD03LCBmaWcuY2FwPSJGaWd1cmUgMS4gUGF0aCBkaWFncmFtIChNb2RlbCAxQi4iLCBlY2hvPVR9DQojLS1tYWtpbmcgcGF0aCBkaWFncmFtIHcgbXkgbG9jYXRpb25zIHNwZWNpZmllZCBpbiAnbScNCm0gPC0gbWF0cml4KGMoIlNFUyIsIE5BLCBOQSwgTkEsDQogICAgICAgICAgICAgICBOQSwgIk1vdCIsIE5BLCAiQUEiLA0KICAgICAgICAgICAgICAiTUEiLCBOQSwgTkEsIE5BKSwgYnlyb3c9VCwgMywgNCkNCg0KZmlnMSA8LSBzZW1QbG90OjpzZW1QYXRocyhtb2QxYi5maXQsIGxheW91dD1tLCB3aGF0TGFiZWxzID0gInN0ZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemVNYW49MTAsIGVkZ2UubGFiZWwuY2V4PTEuMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbkNoYXJOb2Rlcz0wLCBuQ2hhckVkZ2VzID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcm90YXRpb24gPSAyLCBzdHlsZSA9ICJsaXNyZWwiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgRG9Ob3RQbG90PVQsIHJlc2lkU2NhbGU9MTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGVkZ2UuY29sb3IgPSAiYmxhY2siKQ0KDQojLS13b3JraW5nIHdpdGggc2VtcHRvb2xzIHRvIG1vZGlmeSBwYXRoIGRpYWdyYW0NCm15X2N1cnZlX2xpc3QgICAgICAgIDwtIGMoIlNFU35+TUEiID0gLTMpDQpteV9wb3NpdGlvbl9saXN0ICAgICA8LSBjKCJBQX5TRVMiID0uNjAsICJBQX5NQSIgPSAuNTAsICJNb3R+U0VTIj0uNDApDQpteV9yb3RhdGVfcmVzaWRfbGlzdCA8LSBjKE1vdD0uOTAsIEFBPS45MCkNCiN1c2luZyBwaXBpbmcgdG8gYXBwbHkgbXkgbGlzdHMNCmZpZzFfMSA8LSBmaWcxIHw+IHNldF9jdXJ2ZShteV9jdXJ2ZV9saXN0KSB8Pg0KICBzZXRfZWRnZV9sYWJlbF9wb3NpdGlvbihteV9wb3NpdGlvbl9saXN0KXw+DQogIHJvdGF0ZV9yZXNpZChteV9yb3RhdGVfcmVzaWRfbGlzdCkgfD4NCiAgbWFya19zaWcobW9kMWIuZml0LCBhbHBoYSA9IGMoIihuLnMuKSIgPSAxLjAwLCAiKiIgPSAuMDUpKSAjYWRkaW5nICosIG5zDQpwbG90KGZpZzFfMSkNCmBgYA0KDQpgYGB7Y3NzLCBlY2hvPUZBTFNFfQ0KLnNjcm9sbC01MDAgew0KICBtYXgtaGVpZ2h0OiA1MDBweDsNCiAgb3ZlcmZsb3cteTogYXV0bzsNCiAgYmFja2dyb3VuZC1jb2xvcjogaW5oZXJpdDsNCn0NCmBgYA0KDQojIyAxLjQgRnVsbCBvdXRwdXQ6DQpNb2RlbCAxQiBpcyBqdXN0LWlkZW50aWZpZWQsIHNvIGRmID0gMCBhbmQgZml0IGlzIHBlcmZlY3QuDQpgYGB7ciwgY2xhc3Mub3V0cHV0PSJzY3JvbGwtNTAwIn0NCnN1bW1hcnkobW9kMWIuZml0LCBmaXQubWVhc3VyZXM9VCwgc3RhbmRhcmRpemVkPVQsIHJzcXVhcmU9VCkNCmBgYA0KfCBOb3RlIHRoZSBpbmRpcmVjdCBhbmQgdG90YWwgZWZmZWN0cy4NCg0KIyMgMi4gUmV2aXNlZCBNb2RlbA0KDQojIyAyLjEgU3BlY2lmeWluZyB0aGUgbW9kZWw6IDFDDQpgYGB7ciwgZWNobz1UfQ0KIyBzcGVjaWZ5IHRoZSBwYXRoIG1vZGVsDQptb2QxYyA8LSAnDQpNb3QgfiBhMSpTRVMNCkFBICB+IE1BICsgYipNb3QNCiNpbmRpcmVjdCBlZmZlY3RzDQpTRVNfTW90X0FBIDo9IGExKmINCicNCmBgYA0KDQojIyAyLjIgUnVubmluZyB0aGUgbW9kZWw6IDFDDQpgYGB7ciwgZWNobz1UfQ0KIyBmaXQgdGhlIHBhdGggbW9kZWwNCm1vZDFjLmZpdCA8LSBsYXZhYW46OnNlbShtb2QxYywgc2FtcGxlLmNvdj1jb3ZmdWxsLCBzYW1wbGUubm9icz0zMCkNCmBgYA0KDQojIyAyLjMgUGxvdHRpbmcgcmVzdWx0cyBhcyBwYXRoIGRpYWdyYW06DQpOb3RlIHRoYXQgYGxhdmFhbmAgY2FsY3VsYXRlcyB0aGUgY29lZmZpY2llbnRzIGZvciBNQSBhbmQgTW90IGFzIHByZWRpY3RvcnMgb2YgQUEgYSBiaXQgZGlmZmVyZW50bHkgdGhhbiB0aGUgcGllY2Utd2lzZSBPTFMgcmVncmVzc2lvbiBtZXRob2QgdXNlZCBpbiB0aGUgbGVjdHVyZSBzbGlkZXMuIENvbXBhcmUgdGhlIHZhbHVlcyBmb3IgJ0VzdGltYXRlcycgd2l0aCB0aG9zZSBmb3IgJ1N0ZC5hbGwnIGluIHRoZSBGdWxsIE91dHB1dCBiZWxvdy4gQWxzbyB0aGUgJFJeMiQgdmFsdWUgaXMgYSBiaXQgZGlmZmVyZW50LiANCmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmNhcD0iRmlndXJlIDIuIFBhdGggZGlhZ3JhbSAoTW9kZWwgMUMuIiwgZWNobz1UfQ0KIy0tbWFraW5nIHBhdGggZGlhZ3JhbSB3IG15IGxvY2F0aW9ucyBzcGVjaWZpZWQgaW4gJ20nDQptIDwtIG1hdHJpeChjKCJTRVMiLCBOQSwgTkEsIE5BLA0KICAgICAgICAgICAgICAgTkEsICJNb3QiLCBOQSwgIkFBIiwNCiAgICAgICAgICAgICAgIk1BIiwgTkEsIE5BLCBOQSksIGJ5cm93PVQsIDMsIDQpDQoNCmZpZzIgPC0gc2VtUGxvdDo6c2VtUGF0aHMobW9kMWMuZml0LCBsYXlvdXQ9bSwgd2hhdExhYmVscyA9ICJzdGQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplTWFuPTEwLCBlZGdlLmxhYmVsLmNleD0xLjIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5DaGFyTm9kZXM9MCwgbkNoYXJFZGdlcyA9IDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHJvdGF0aW9uID0gMiwgc3R5bGUgPSAibGlzcmVsIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIERvTm90UGxvdD1ULCByZXNpZFNjYWxlPTEwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBlZGdlLmNvbG9yID0gImJsYWNrIikNCg0KIy0td29ya2luZyB3aXRoIHNlbXB0b29scyB0byBtb2RpZnkgcGF0aCBkaWFncmFtDQpteV9jdXJ2ZV9saXN0ICAgICAgICA8LSBjKCJTRVN+fk1BIiA9IC0zKQ0KbXlfcG9zaXRpb25fbGlzdCAgICAgPC0gYygiQUF+TUEiID0gLjUwLCAiTW90flNFUyI9LjQwKQ0KbXlfcm90YXRlX3Jlc2lkX2xpc3QgPC0gYyhNb3Q9LjkwLCBBQT0uOTApDQojdXNpbmcgcGlwaW5nIHRvIGFwcGx5IG15IGxpc3RzDQpmaWcyXzEgPC0gZmlnMiB8PiBzZXRfY3VydmUobXlfY3VydmVfbGlzdCkgfD4NCiAgc2V0X2VkZ2VfbGFiZWxfcG9zaXRpb24obXlfcG9zaXRpb25fbGlzdCl8Pg0KICByb3RhdGVfcmVzaWQobXlfcm90YXRlX3Jlc2lkX2xpc3QpIHw+DQogIG1hcmtfc2lnKG1vZDFjLmZpdCwgYWxwaGEgPSBjKCIobi5zLikiID0gMS4wMCwgIioiID0gLjA1KSkgI2FkZGluZyAqLCBucw0KcGxvdChmaWcyXzEpDQpgYGANCg0KIyMgMi40IEZ1bGwgb3V0cHV0Og0KYGBge3IsIGNsYXNzLm91dHB1dD0ic2Nyb2xsLTUwMCJ9DQpzdW1tYXJ5KG1vZDFjLmZpdCwgZml0Lm1lYXN1cmVzPVQsIHN0YW5kYXJkaXplZD1ULCByc3F1YXJlPVQpDQpgYGANCnwgVGhlIGluZGlyZWN0IGVmZmVjdCBvZiBTRVMgb24gQUEgdmlhIE1vdCBpcyBzaWduaWZpY2FudC4NCg0KDQojIyAyLjUgUmVzaWR1YWxzDQpBbGwgeiB2YWx1ZXMgPCB8Mi41NHwuDQpgYGB7ciwgY2xhc3Mub3V0cHV0PSJzY3JvbGwtNTAwIn0NCmxhdlJlc2lkdWFscyhtb2QxYy5maXQpDQpgYGANCg0KDQojIyAzIE1vZGVsIENvbXBhcmlzb24NCmBgYHtyfQ0KIyBjb21wYXJpbmcgbmVzdGVkIG1vZGVscyB2aWEgY2hpc3EgbGlrZWxpaG9vZCByYXRpbyB0ZXN0DQpsYXZUZXN0TFJUKG1vZDFiLmZpdCwgbW9kMWMuZml0LCB0eXBlID0gImNoaXNxIikNCg0KYGBgDQpOb3RlIHRoYXQgdGhlIG1vZGVsIGNvbXBhcmlzb24gdGVzdCBpcyBub25zaWduaWZpY2FudDsgdGhlIG1vcmUgcGFyc2ltb25pb3VzIE1vZGVsIDFDIGZpdHMgdGhlIGRhdGEgYXMgd2VsbCBhcyB0aGUganVzdC1pZGVudGlmaWVkIE1vZGVsIDFCLiBUaGUgJFxjaGleMiQgdmFsdWUgaXMgYSBiaXQgZGlmZmVyZW50IHRoYW4gdGhlIGhhbmQgY2FsY3VsYXRpb24gZHVlIHRvIHRoZSBkaWZmZXJlbmNlcyBpbiAkUl4yJCB2YWx1ZXMgYmV0d2VlbiBPTFMgYW5kIGBsYXZhYW5gLiANCg0K