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, 
               janitor, summarytools, skimr, DataExplorer, inspectdf, Hmisc,
               lavaan, semTools, semoutput, semPlot, sjPlot, psych, readr, dplyr,
               GPArotation, psychTools, semptools,tinytex)

LISREL

In the Beginning there was LISREL, and it was good… Structural equation modeling (SEM) is a family of statistical methods for analyzing the structure of a variance/covariance matrix among a set of variables. SEM has its origins in the work of Karl Joreskog who developed the LISREL model and software during the 1970s. LISREL is the acronym for LInear Structural RELations.

# creating an example w 5 latents w 3 indicators and mediation

# Lambda matrices:
lambdax <- matrix(0,9,3)
lambdax[1:3,1] <-1
lambdax[4:6,2] <-1
lambdax[7:9,3] <-1

lambday <- matrix(0,6,2)
lambday[1:3,1] <- 1
lambday[4:6,2] <- 1

# Phi and Psi matrices:
LatX <- matrix(1,3,3) #phi
LatY <- diag(1,2,2)   #psy

# Gamma matrix:
Gamma <- matrix(0,2,3)
Gamma[1,1] <- 1
Gamma[1,2] <- 1
Gamma[1,3] <- 1
Gamma[2,2] <- 1

# Beta matrix:
Beta <- matrix(0,2,2)
Beta[2,1] <- 1

# Theta matrices:
thd <- diag(1,nrow(lambdax))
the <- diag(1,nrow(lambday))

# Combine matrices into a model:
mod3 <- lisrelModel(LX=lambdax, LY=lambday, TD=thd, TE=the,
                    PH=LatX, GA=Gamma, BE=Beta, PS=LatY)

The LISREL model as path diagram

# Plot path diagram using semPlot::semPaths sizelat=4, sizeman=3
semPaths(mod3, as.expression=c("nodes","edges"), sizeMan = 4,
         sizeLat = 5, style="lisrel", rotation = 2,
         residScale=8, edge.color="black", mar=c(2,2,2,2),
         edge.label.cex = 0.9, label.cex=1.5)
Figure 1. The LISREL model for a hypothetical example.

Figure 1. The LISREL model for a hypothetical example.

Figure 1 is the classic graphicical representation of the full LISREL model. Squares denote observed variables (indicators) and circles represent latent variables (constructs). In this example there are 5 latent variables; 3 of them are exogenous, \(\xi_1, \xi_2, \xi_3\) (xi’s on the left) and 2 are endogenous, \(\eta_1, \eta_2\) (eta’s, on the right).

The arrows in the figure imply hypothesized causal relations between variables and represent parameters to be estimated. Arrows connecting squares to circles represent the measurement portion of the model. Each latent variable in Figure 1 has 3 indicators, or observed variables hypothesized to measure the underlying construct of interest. For example \(\xi_1\) has indicators \(x_1, x_2\) and \(x_3\). The arrows connecting these variables are labeled with \(\lambda_{ij}\) (lambda) coefficients representing factor loadings. The subscript numbering system is such that i refers to the variable being pointed to and j indicates the variable where the arrow originates, and in this case they identify the parameter by its row and column position in the \(\mathbf\Lambda\) matrix discussed below. Each indicator variable (square) also has an arrow pointing to it that represents a measurement error variance parameter \(\theta_{ii}\) (theta).

Arrows between circles represent the structural portion of the model, that is the hypothesized relations between latent variables. The exogenous latent variables \(\xi_1, \xi_2, \xi_3\) are connected by two-headed arrows implying that they are correlated; these arrows are labeled with \(\phi_{ij}\) (phi) coefficients. \(\xi_1, \xi_2,\) and \(\xi_3\) all have arrows pointing to \(\eta_1\), indicating that they are hypothesized to predict variance in \(\eta_1\). Also note that \(\eta_1\) points to \(\eta_2\) indicating that \(\eta_1\) is hypothesized as a mediator between \(\xi_1, \xi_2, \xi_3\) and \(\eta_2\). There is also a direct effect of \(\xi_2\) on \(\eta_2\). Arrows between \(\xi\) variables and \(\eta\) variables are labeled with \(\gamma_{ij}\) coefficients. Arrows between \(\eta\) variables are labeled with \(\beta_{ij}\) coefficients. Both \(\eta\) variables also have arrows pointing to them that represent unexplained variance and these are are labeled with \(\psi\) (psi) coefficients. The various coefficients \(\lambda, \theta, \gamma, \beta, \phi, \psi\) are elements of specific matrices discussed below.

The LISREL model in matrix notation

The LISREL model consists of two parts, a measurement model and a structural model. This is the structural portion of the model showing how the variances of the endogenous latent variables (constructs) are related: \[ \mathbf{\eta = \beta \eta + \Gamma \xi + \zeta} \tag{1} \]

where \(\eta\) (eta) is a m x 1 vector of endogenous latent constructs, \(\xi\) (xi) is a n x 1 vector of exogenous latent constructs, and \(\zeta\) (zeta) is a m x 1 vector of residual variances for the endogenous latent constructs. \(\beta\) (beta) specifies the causal relationships among latent endogenous constructs (\(\eta\)’s); size = m x m. \(\Gamma\) (gamma) specifies the causal influences of latent exogenous constructs (\(\xi\)’s) on the latent endogenous constructs (\(\eta\)’s); size = m x n.

This is the measurement model for the variances of the endogenous observed y variables (indicators): \[ \mathbf{y = \Lambda_y \eta + \Theta_\epsilon}\tag{2} \]

where y is a p x 1 vector of observed endogenous indicators, (p = the number of observed endogenous indicators) and m = the number of latent endogenous constructs. \(\Lambda_y\) (lambda y) specifies which y variables are indicators of which endogenous constructs (\(\eta\)’s); size = p x m. \(\Theta_\epsilon\) (theta epsilon) specifies the residual variances/covariances among the y variables. The diagonal elements are error variances and the off-diagonal elements are covariances between residuals. Size = p x p, usually specify as diagonal.

This is the measurement model for the variances of the exogenous observed x variables: \[ \mathbf{x = \Lambda_x \xi + \Theta_\delta}\tag{3} \] where x is a q x 1 vector of observed exogenous indicators, (q = the number of observed exogenous indicators) and n = the number of latent exogenous constructs. \(\Lambda_x\) (lambda x) specifies which x variables are indicators of which exogenous constructs (\(\xi\)’s); size = q x n. \(\Theta_\delta\) (theta delta) specifies the residual variances/covariances among the x variables. The diagonal elements are error variances and the off-diagonal elements are covariances between residuals. Size = q x q, usually specify as diagonal.

Other Matrices in the LISREL Model: \(\Phi\) (phi) – specifies the variances and covariances among latent exogenous constructs (ξ’s); size = n x n. \(\Psi\) (psi) matrix – specifies the variances and covariances among the latent residual terms (\(\zeta\)’s); size = m x m.

Assessing Model Fit

Listing the p observed \(y\) variables first, followed by the q observed \(x\) variables, the covariances of these (p + q = 15) variables in Figure 1 are contained in the \(\mathbf\Sigma\) matrix which may be partitioned as:

\[\mathbf\Sigma = \left[ \begin{array}{c|c} covariances_y & covariances_{y,x} \\ \hline covariances_{x,y} & covariances_x \end{array} \right]\]

In the full LISREL model the partitions of \(\mathbf\Sigma\) are estimated as:

\[\mathbf\Sigma^* = \left[ \begin{array}{c|c} \Lambda_y [(I-\beta)^{-1} (\Gamma\Phi\Gamma' + \Psi)(I-\beta)^{-1'}]\Lambda'_y + \Theta_\epsilon & \Lambda_y(I-\beta)^{-1} \Gamma \Phi \Lambda'_x \\ \hline \Lambda_x \Phi\Gamma'(I-\beta)^{-1'}\Lambda'_y & \Lambda_x\Phi\Lambda'_x + \Theta_\delta \end{array} \right] \tag{4}\]

How well \(\mathbf\Sigma^*\) reproduces \(\mathbf\Sigma\) is the basis for assessing the fit of the model to the data. The differences between observed and model implied covariances form the basis for various fit indices such as \(\chi^2\), GFI, CFI, TLI, RMSEA, and SRSR.


Further Developments

Other SEM programs have been developed since LISREL. These include EQS, Amos, Mplus, and the lavaan package in R. Most of these reparameterize the LISREL model treating all variables as endogenous. This means that the structural model (Eq. 1) simplifies to: \[ \mathbf{\eta = \beta \eta + \Psi}\tag{5} \]

and Eq. 2 and Eq. 3 are combined into a single equation linking observed and latent variables: \[ \mathbf{y = \Lambda \eta + \Theta} \tag{6} \]

Matrices in Detail

Let’s look at the parameter matrices in Eq. 5 and Eq.6 in more detail using the model in Figure 1 as an example. The \(\mathbf\Lambda\) matrix contains 15 rows, one for each indicator variable (9 x’s and 6 y’s) and 5 columns, one for each latent variable, \(\eta_1\) to \(\eta_5\). The elements (\(\lambda_{ij}\)) are factor loadings, quantifying the relation between each indicator variable and the latent variable (\(\eta_j\)) hypothesized to underlie it. Estimating some \(\lambda_{ij}\) coefficients while fixing others to 0.0, allows us to specify the measurement model.

\[\mathbf\Lambda = \begin{bmatrix} \lambda_{1,1} & \lambda_{1,2} & \cdots & \lambda_{1,5} \\ \lambda_{2,1} & \lambda_{2,2} & \cdots & \lambda_{2,5} \\ \vdots & \vdots & \ddots & \vdots \\ \lambda_{15,1} & \lambda_{15,2} & \cdots & \lambda_{15,5} \end{bmatrix}\]

The \(\mathbf\beta\) matrix is a 5 x 5 indicating the influence of the column \(\eta\) variable on the row \(\eta\) variable. The diagonal elements are set to 0.0.

\[\mathbf\beta = \begin{bmatrix} 0 & \beta_{1,2} & \cdots & \beta_{1,5} \\ \beta_{2,1} & 0 & \cdots & \beta_{2,5} \\ \vdots & \vdots & \ddots & \vdots \\ \beta_{5,1} & \beta_{5,2} & \cdots & 0 \end{bmatrix}\]

In the reparameterization, the \(\mathbf\psi\) matrix is a 5 x 5 and contains the variances and covariances among exogenous latent variables (originally in \(\mathbf\Phi\)), and the residual variances (\(\zeta\)s) for the endogenous latent variables. All elements are denoted as \(\psi\) coefficients. \[\mathbf\Psi = \begin{bmatrix} \psi_{1,1} & \psi_{1,2} & \cdots & \psi_{1,5} \\ \psi_{2,1} & \psi_{2,2} & \cdots & \psi_{2,5} \\ \vdots & \vdots & \ddots & \vdots \\ \psi_{5,1} & \psi_{5,2} & \cdots & \psi_{5,5} \end{bmatrix}\]

The residual variances of the 15 indicator variables are in the \(\Theta\) matrix which is typically specified as a diagonal matrix: \[\mathbf\Theta = \begin{bmatrix} \theta_{1,1} & 0 & \cdots & 0 \\ 0 & \theta_{2,2} & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & \theta_{15,15} \end{bmatrix}\]

Using the lavaan package for SEM

Specifying models in lavaan is pretty straightforward. There are different operators for specifying various relations among variables. Regressions use ~ with the outcome variable on the left and the predictors on the right. Latent variables are defined using the =~ operator with the latent variable on the left and the indicator variables on the right. The := operator is used to define parameters constructed from other parameters; specifying indirect effects for example. Correlations (covariances) between variables are indicated with the ~~ operator. In the examples below I show the how to specify and run various models in lavaan. One nice feature is that we don’t always need the raw data file; we can work from the covariance matrix, or correlation matrix plus a vector of standard deviations and the sample size. I’m going to work through a few examples to demonstrate bits of SEM theory and features of lavaan and its support packages. I created this document in R Markdown.

1. Path Analysis Examples

1a: Path analysis with observed variables

This is an example of path analysis. Illness (ill) is regressed onto Stress (str) and Fitness (fit), which are regressed onto Hardiness (hardy) and Exercise (exer), respectively. Here we are providing a correlation matrix and vector of standard deviations. lavaan will convert these into a covariance matrix for analysis. We must also provide N = 373 when specifying the model below.

Reading in data:

# reading in corr matrix
corr <- '
1.00
-.03 1.00
 .39  .07 1.00
-.05 -.23 -.13 1.00
-.08 -.16 -.29  .34 1.00'
# add variable names and convert to full correlation matrix
corrfull <- lavaan::getCov(corr, names=c("exer","hardy","fit","str","ill"))
# add SDs and convert to full covariance matrix
covfull <- lavaan::cor2cov(corrfull, sds=c(66.50, 38.00, 36.80, 67.00, 62.48))
# observed covariance matrix
#covfull

Specifying model in lavaan. Note we can define the hypothesized indirect effects. A convenient way to do this is to name the parameters and then use these names to create products. Here “g1” and “g2” may be considered as \(\gamma\) coefficients and “b1” and “b2” may be considered as \(\beta\) coefficients in the original LISREL model. Indirect effects are defined using the := operator.

Specify the model:

# specify the path model
roth <- '
fit ~ g1*exer
str ~ g2*hardy
ill ~ b1*fit + b2*str
# indirect effects
exer_ill  := g1*b1
hardy_ill := g2*b2
# allowing resids of two parallel mediators to correlate
fit ~~ str '

Running the model:

# fit the path model
roth.fit <- lavaan::sem(roth, sample.cov=covfull, sample.nobs=373, optim.method=list("BFGS"))

The fit of the model to the data is reasonable according to the following fit indices: CFI = .980, TLI = .955, RMSEA = .046, and SRMR = .038. The \(\chi^2\) = 7.135 with df = 4, and p = .129. The results are summarized in Figure 2 which presents standardized coefficients. On the left we see that the correlation between hardiness and exercise is -.03 (the arrow is dashed to indicate that this value was not estimated but was given in the data. The model explains 5% of the variance in stress (note the residual arrow is 1 - .05) and 15% of the variance in fittness (the residual arrow is 1 - .15). The unexplained variance in each of these variables is allowed to correlate (-.10) meaning that they share the influence of omitted variables. The model explains about 17% of the variance in illness (1 - .83). The indirect effect of hardiness on illness is obtained by multiplying the paths from hardy to str and from str to ill (-.22 x .31 = -.068, p < .05). The indirect effect of exercise on illness is similarly obtained (.39 x -.25 = -.096, p < .05).

Plotting results as path diagram:

#--making path diagram obs only
fig2 <- semPlot::semPaths(roth.fit, whatLabels = "std", layout = "tree2", 
         rotation = 2, style = "lisrel", optimizeLatRes = TRUE, 
         structural = FALSE, layoutSplit = FALSE,
         intercepts = FALSE, residuals = T, 
         curve = 3, curvature = 3, nCharNodes = 8, 
         sizeLat = 5, sizeMan = 8, sizeMan2 = 6, 
         edge.label.cex = 0.9, label.cex=1.1, residScale=10, 
         edge.color = "black", edge.label.position = .40, DoNotPlot=T)

#--working with semptools to modify path diagrams
my_position_list <- c("hardy ~~ exer" = .50, "str ~~ fit"=.50)
fig2_1 <- fig2 |> set_edge_label_position(my_position_list) |>
  mark_sig(roth.fit, alpha = c("(n.s.)" = 1.00, "*" = .05)) #adding *, ns
plot(fig2_1)
Figure 2. Path analysis using observed variables.

Figure 2. Path analysis using observed variables.

Full output:

I used the summary() function to write lavaan results to the console. There is a lot of output to review. The first bit is model fit statistics. Next we see the regression models involving the observed variables. The column labeled Estimate gives the unstandardized coefficients, and the column Std.all gives the completely standardized coefficients. The covariance/correlation between the residuals of the two regressions is shown next, followed by estimated variances. Variables prefixed with a “.” indicate residual terms. Then we see the \(R^2\) values indicating the proportion of variance explained in each endogenous variable. Finally, under the heading “Defined Parameters” we see the indirect effects as we specified in our model statement above.
summary(roth.fit, fit.measures=T, standardized=T, rsquare=T)
## lavaan 0.6.15 ended normally after 15 iterations
## 
##   Estimator                                         ML
##   Optimization method                             BFGS
##   Number of model parameters                         8
## 
##   Number of observations                           373
## 
## Model Test User Model:
##                                                       
##   Test statistic                                 7.135
##   Degrees of freedom                                 4
##   P-value (Chi-square)                           0.129
## 
## Model Test Baseline Model:
## 
##   Test statistic                               165.608
##   Degrees of freedom                                 9
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.980
##   Tucker-Lewis Index (TLI)                       0.955
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)              -5962.553
##   Loglikelihood unrestricted model (H1)      -5958.985
##                                                       
##   Akaike (AIC)                               11941.105
##   Bayesian (BIC)                             11972.478
##   Sample-size adjusted Bayesian (SABIC)      11947.096
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.046
##   90 Percent confidence interval - lower         0.000
##   90 Percent confidence interval - upper         0.099
##   P-value H_0: RMSEA <= 0.050                    0.474
##   P-value H_0: RMSEA >= 0.080                    0.171
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.038
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Expected
##   Information saturated (h1) model          Structured
## 
## Regressions:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   fit ~                                                                 
##     exer      (g1)    0.213    0.026    8.107    0.000    0.213    0.385
##   str ~                                                                 
##     hardy     (g2)   -0.390    0.088   -4.411    0.000   -0.390   -0.222
##   ill ~                                                                 
##     fit       (b1)   -0.424    0.080   -5.290    0.000   -0.424   -0.250
##     str       (b2)    0.287    0.044    6.506    0.000    0.287    0.308
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##  .fit ~~                                                                
##    .str            -228.048  114.706   -1.988    0.047 -228.048   -0.103
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .fit            1145.182   83.856   13.657    0.000 1145.182    0.852
##    .str            4240.134  310.485   13.657    0.000 4240.134    0.951
##    .ill            3203.954  234.610   13.657    0.000 3203.954    0.829
## 
## R-Square:
##                    Estimate
##     fit               0.148
##     str               0.049
##     ill               0.171
## 
## Defined Parameters:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##     exer_ill         -0.090    0.020   -4.430    0.000   -0.090   -0.096
##     hardy_ill        -0.112    0.031   -3.651    0.000   -0.112   -0.068

1b: Path analysis incorporating measurement error

Here we can specify the amount of measurement error variance in our observed variables. This involves 3 steps: first we have to define a latent variable for each of the observed variables. Second, we re-specify the regressions using the latent variables. Finally, we fix theta values equal to 10% of the observed variances in the indicators. This is equivalent to setting reliability to .90 for all observed variables.

Specify the model:

roth2 <- '
exerf  =~ exer
hardyf =~ hardy
fitf   =~ fit
strf   =~ str
illf   =~ ill
# factor regs
fitf ~ g1*exerf
strf ~ g2*hardyf
illf ~ b1*fitf + b2*strf
#indirect effects
exer_ill  := g1*b1
hardy_ill := g2*b2
# setting measurement error variances to 10% obs variance
exer  ~~ 422.225*exer
hardy ~~ 144.400*hardy
fit   ~~ 135.424*fit
str   ~~ 448.900*str
ill   ~~ 390.375*ill
# allowing resids of two parallel mediators to correlate
fitf ~~ strf '

Running the model:

# fit a path model w m.e.
#--had to change optimization method to "BFGS" from default "MLMINB" for single indicators to work
roth2.fit <- lavaan::sem(roth2, sample.cov=covfull, sample.nobs=373, optim.method=list("BFGS"))

The fit of the model to the data is reasonable according to the following fit indices: CFI = .980, TLI = .949, RMSEA = .046, and SRMR = .036. The \(\chi^2\) = 7.163 with df = 4, and p = .128. The results are summarized in Figure 3 which presents standardized coefficients. Note that the structural parameters are a bit larger than their counterparts in Figure 2 because they have been corrected for attenuation by taking the reliability of the observed variables into account. The standard errors for these parameters are also increased due to measurement error (compare full output of the two models). The indirect effect of hardiness on illness is obtained by multiplying the paths from hardyf to strf and from strf to illf (-.25 x .34 = -.085, p < .05). The indirect effect of exercise on illness is similarly obtained (.43 x -.27 = -.116, p < .05).

Plotting results as path diagram with latent and observed variables:

#--making path diagram w m.e.---------------------mar(bot,lft,top,rgt)
fig3 <-semPlot::semPaths(roth2.fit, whatLabels = "std", layout = "tree2", 
         rotation = 2, style = "lisrel", optimizeLatRes = TRUE, 
         structural = FALSE, layoutSplit = FALSE,
         intercepts = FALSE, residuals = T, 
         curve = 3, curvature = 3, nCharNodes = 8, 
         sizeLat = 8, sizeMan = 8, sizeMan2 = 6, 
         edge.label.cex = 0.9, label.cex=1.1, residScale=10, mar=c(2,3,2,3), 
         edge.color = "black", edge.label.position = .40, DoNotPlot=T)

#--working with semptools to modify path diagrams
#--my specifications--
my_rotate_resid_list <- c(strf = -120,
                          fitf = -45,
                          illf =  45)
my_curve_list <- c("strf ~~ fitf" = 2)
#--making multiple adjustments w piping |>
fig3_1 <- fig3 |> set_curve(my_curve_list) |>
  rotate_resid(my_rotate_resid_list) |>
  mark_sig(roth2.fit, alpha = c("(n.s.)" = 1.00, "*" = .05))
plot(fig3_1)  
Figure 3. Path analysis using single indicator variables with specified measurement error variance

Figure 3. Path analysis using single indicator variables with specified measurement error variance

Full output:

Note there is a new section labeled “Latent Variables” which shows the operator =~. The regression equations and indirect effects are now specified using the latent variables. The \(R^2\) values for the observed variables are approximately .90 reflecting fact that we fixed their measurement error variances to 10% of their original variances.
summary(roth2.fit, fit.measures=T, standardized=T, rsquare=T)
## lavaan 0.6.15 ended normally after 848 iterations
## 
##   Estimator                                         ML
##   Optimization method                             BFGS
##   Number of model parameters                        11
## 
##   Number of observations                           373
## 
## Model Test User Model:
##                                                       
##   Test statistic                                 7.163
##   Degrees of freedom                                 4
##   P-value (Chi-square)                           0.128
## 
## Model Test Baseline Model:
## 
##   Test statistic                               165.944
##   Degrees of freedom                                10
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.980
##   Tucker-Lewis Index (TLI)                       0.949
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)              -9942.302
##   Loglikelihood unrestricted model (H1)      -9938.720
##                                                       
##   Akaike (AIC)                               19906.603
##   Bayesian (BIC)                             19949.740
##   Sample-size adjusted Bayesian (SABIC)      19914.841
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.046
##   90 Percent confidence interval - lower         0.000
##   90 Percent confidence interval - upper         0.100
##   P-value H_0: RMSEA <= 0.050                    0.472
##   P-value H_0: RMSEA >= 0.080                    0.172
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.036
## 
## 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
##   exerf =~                                                              
##     exer              1.000                              63.152    0.951
##   hardyf =~                                                             
##     hardy             1.000                              35.996    0.949
##   fitf =~                                                               
##     fit               1.000                              34.780    0.948
##   strf =~                                                               
##     str               1.000                              63.149    0.948
##   illf =~                                                               
##     ill               1.000                              58.922    0.948
## 
## Regressions:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   fitf ~                                                                
##     exerf     (g1)    0.234    0.029    8.056    0.000    0.426    0.426
##   strf ~                                                                
##     hardyf    (g2)   -0.438    0.098   -4.473    0.000   -0.250   -0.250
##   illf ~                                                                
##     fitf      (b1)   -0.460    0.089   -5.151    0.000   -0.272   -0.272
##     strf      (b2)    0.319    0.049    6.464    0.000    0.342    0.342
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##  .fitf ~~                                                               
##    .strf           -227.060  114.409   -1.985    0.047   -0.118   -0.118
##   exerf ~~                                                              
##     hardyf          -61.709  130.363   -0.473    0.636   -0.027   -0.027
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .exer            422.225                             422.225    0.096
##    .hardy           144.400                             144.400    0.100
##    .fit             135.424                             135.424    0.101
##    .str             448.900                             448.900    0.101
##    .ill             390.375                             390.375    0.101
##     exerf          3988.169  322.952   12.349    0.000    1.000    1.000
##     hardyf         1295.729  105.454   12.287    0.000    1.000    1.000
##    .fitf            990.594   84.130   11.774    0.000    0.819    0.819
##    .strf           3739.301  308.673   12.114    0.000    0.938    0.938
##    .illf           2745.361  235.005   11.682    0.000    0.791    0.791
## 
## R-Square:
##                    Estimate
##     exer              0.904
##     hardy             0.900
##     fit               0.899
##     str               0.899
##     ill               0.899
##     fitf              0.181
##     strf              0.062
##     illf              0.209
## 
## Defined Parameters:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##     exer_ill         -0.108    0.025   -4.364    0.000   -0.116   -0.116
##     hardy_ill        -0.140    0.038   -3.694    0.000   -0.085   -0.085

Residuals Analysis:

Displays residual correlations (observed - predicted) and z-scores indicating the magnitude of the residuals. Typically, z values > |2.54| are interpreted as significant and indicate where the model is doing a poor job of reproducing the observed correlations (and variances). Note that for this example all the residuals are small but one is significantly different from 0.0 indicating that the model is under-estimating the variance in str.
lavResiduals(roth2.fit)
## $type
## [1] "cor.bentler"
## 
## $cov
##         exer  hardy    fit    str    ill
## exer   0.000                            
## hardy -0.006  0.000                     
## fit    0.007  0.080  0.004              
## str   -0.056 -0.007 -0.040  0.009       
## ill    0.022 -0.086 -0.016  0.011  0.008
## 
## $cov.z
##         exer  hardy    fit    str    ill
## exer   0.000                            
## hardy -2.090  0.000                     
## fit    1.371  1.713  1.078              
## str   -1.119 -1.201 -1.844  3.852       
## ill    0.485 -1.828 -2.038  1.801  2.011
## 
## $summary
##                            cov
## srmr                     0.036
## srmr.se                  0.010
## srmr.exactfit.z          1.343
## srmr.exactfit.pvalue     0.090
## usrmr                    0.029
## usrmr.se                 0.017
## usrmr.ci.lower           0.000
## usrmr.ci.upper           0.057
## usrmr.closefit.h0.value  0.050
## usrmr.closefit.z        -1.230
## usrmr.closefit.pvalue    0.891

2. Confirmatory Factor Analysis

The SF-36 questionnaire measures self-reported functional status. Its 36 items are combined to form 8 subscales yielding two summary measures: physical and mental health. The physical health measure includes four subscales: physical functioning (10 items), role-physical (4 items), bodily pain (2 items), and general health (5 items). The mental health measure includes the other four subscales: vitality (4 items), social functioning (2 items), role-emotional (3 items), and mental health (5 items).

This example uses data from 249 women (age 31 to 87, median 64) who completed the SF-36 following heart surgery. The subscale variable names are: MENHLTH, SOCFUNC, ROLEEMO, VITAL, PHSFUNC, ROLPHYS, BODPAIN, and GENHLTH. A Confirmatory Factor Analysis with 2 correlated factors is run using the lavaan package in which each subscale loads on only one hypothesized factor. I’m working with a raw data file this time so I can display the covariance/correlation matrix graphically. Figure 4 was produced Using the psych package. It combines correlation values, scatter-plots, and histograms.

2.1 Importing Data:

In this example I am using the rio package to import an SPSS data file. One inconvenience when importing SPSS data files is that the variable labels are recognized by some packages but not others. In this example the variable labels actually mess up the tables and path diagrams so I deleted them from the data file.

## Import Data
hrtsrg <- rio::import(here("data", "WOMEN_HRTSRG_noLabels.SAV"))
attach(hrtsrg)
data <- data.frame(MENHLTH, SOCFUNC, ROLEEMO, VITAL,   PHSFUNC, ROLPHYS, BODPAIN, GENHLTH)

2.2 Bivariate Summary Display

Using the psych package to get this rich figure.

psych::pairs.panels(data, lm=T, cex.cor=.9, jiggle=T, factor=3,
                    show.points = T, ellipses=T, pch=".")
Figure 4. Bivariate summary of SF-36 subscales. Histograms are on the diagonal, scatter-plots showing bivariate regression line and centroid are below the diagnonal, correlations are above the diagonal.

Figure 4. Bivariate summary of SF-36 subscales. Histograms are on the diagonal, scatter-plots showing bivariate regression line and centroid are below the diagnonal, correlations are above the diagonal.

2.3 Specify & run the model:

# specify the 2-factor CFA model
model <- '
# latent factors
MH =~  MENHLTH + SOCFUNC + ROLEEMO + VITAL
PH =~  PHSFUNC + ROLPHYS + BODPAIN + GENHLTH'
# fit the model
fit <- lavaan::cfa(model = model, data = data, mimic = "lavaan", 
           estimator = "ML", missing = "listwise", check.post=T,
           std.lv = TRUE, std.ov = FALSE, test = "standard", 
           se = "standard", bootstrap = 1000)

The fit of the hypothesized model to the data is not great according to the following fit indices: CFI = .871, TLI = .810, RMSEA = .150, and SRMR = .070. The \(\chi^2\) = 125.111 with df = 19, and p < .001. Despite the questionable fit, we can comment on the model parameter estimates. The results are summarized in Figure 5 which presents standardized coefficients. First, all the factor loadings (\(\lambda\)’s) are significant. Second, the \(\theta\) values show the proportion of measurement error variance in each subscale when considered as an indicator of the underlying latent factor (PH or MH). Note that because all parameters are standardized \(\lambda^2_{ij}\) + \(\theta_{ii}\) = 1.0 for any given subscale. In general, the \(\lambda^2_{ij}\) values may be interpreted as reliability, or the proportion of variance that an indicator variable shares with the underlying latent factor it is hypothesized to measure. Finally, notice the large correlation between the factors \(\phi\) = .85, suggesting that the PH and MH constructs may not be distinct from one another. Modification indices below offer some insight into why the hypothesized CFA model fit is not great.

Plotting path diagram:

factors <- fit@pta$vnames$lv[[1]]
size <- .75
fig5 <- semPaths(fit, latents = factors, whatLabels = "std", layout = "tree2", 
         rotation = 2, style = "lisrel", optimizeLatRes = TRUE, 
         structural = FALSE, layoutSplit = FALSE,
         intercepts = FALSE, residuals = T, 
         curve = 1, curvature = 3, nCharNodes = 8, 
         sizeLat = 11 * size, sizeMan = 11 * size, sizeMan2 = 4 * size, 
         edge.label.cex = 1.2 * size, residScale=12 *size,
         edge.color = "black", edge.label.position = .40, DoNotPlot=T)
fig5_1 <- fig5 |> mark_sig(fit, alpha = c("(n.s.)" = 1.00, "*" = .05)) #adding *, ns
plot(fig5_1)
Figure 5. CFA showing eight subscales of the SF-36 loading onto the mental health factor (MH) and the physical health factor (PH)

Figure 5. CFA showing eight subscales of the SF-36 loading onto the mental health factor (MH) and the physical health factor (PH)

Full output:

summary(fit, fit.measures=T, standardized=T, rsquare=T)
## lavaan 0.6.15 ended normally after 22 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                        17
## 
##   Number of observations                           249
## 
## Model Test User Model:
##                                                       
##   Test statistic                               125.511
##   Degrees of freedom                                19
##   P-value (Chi-square)                           0.000
## 
## Model Test Baseline Model:
## 
##   Test statistic                               853.850
##   Degrees of freedom                                28
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.871
##   Tucker-Lewis Index (TLI)                       0.810
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)              -4928.803
##   Loglikelihood unrestricted model (H1)      -4866.048
##                                                       
##   Akaike (AIC)                                9891.606
##   Bayesian (BIC)                              9951.403
##   Sample-size adjusted Bayesian (SABIC)       9897.512
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.150
##   90 Percent confidence interval - lower         0.126
##   90 Percent confidence interval - upper         0.176
##   P-value H_0: RMSEA <= 0.050                    0.000
##   P-value H_0: RMSEA >= 0.080                    1.000
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.070
## 
## 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
##   MH =~                                                                 
##     MENHLTH           3.345    0.253   13.224    0.000    3.345    0.756
##     SOCFUNC           1.730    0.121   14.252    0.000    1.730    0.798
##     ROLEEMO           2.438    0.201   12.153    0.000    2.438    0.711
##     VITAL             2.436    0.193   12.596    0.000    2.436    0.730
##   PH =~                                                                 
##     PHSFUNC           3.160    0.307   10.308    0.000    3.160    0.643
##     ROLPHYS           2.818    0.234   12.046    0.000    2.818    0.728
##     BODPAIN           1.470    0.150    9.822    0.000    1.470    0.618
##     GENHLTH           2.442    0.254    9.617    0.000    2.442    0.607
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   MH ~~                                                                 
##     PH                0.846    0.038   22.119    0.000    0.846    0.846
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .MENHLTH           8.387    0.957    8.762    0.000    8.387    0.428
##    .SOCFUNC           1.713    0.214    8.013    0.000    1.713    0.364
##    .ROLEEMO           5.826    0.624    9.339    0.000    5.826    0.495
##    .VITAL             5.210    0.571    9.121    0.000    5.210    0.468
##    .PHSFUNC          14.202    1.515    9.375    0.000   14.202    0.587
##    .ROLPHYS           7.064    0.855    8.258    0.000    7.064    0.471
##    .BODPAIN           3.500    0.365    9.599    0.000    3.500    0.618
##    .GENHLTH          10.210    1.054    9.686    0.000   10.210    0.631
##     MH                1.000                               1.000    1.000
##     PH                1.000                               1.000    1.000
## 
## R-Square:
##                    Estimate
##     MENHLTH           0.572
##     SOCFUNC           0.636
##     ROLEEMO           0.505
##     VITAL             0.532
##     PHSFUNC           0.413
##     ROLPHYS           0.529
##     BODPAIN           0.382
##     GENHLTH           0.369

2.4 Modification Indices

The modification indices indicate where additional parameters could be added to improve the model fit. The values are estimates of how much the \(\chi^2\) statistic would decrease if the parameter were to be estimated rather than fixed to 0.0. For example, allowing GENHLTH to cross-load onto the MH factor would result in the largest improvement in model fit (32.527). Allowing the residual variances between MENHLTH and PHSFUNC to correlate would also result in improvement in fit (26.178). The assumptions of CFA theory are that indicators load on only one factor and that measurement error variances are uncorrelated, so adding these parameters would violate these assumptions. We are left to conclude that the nice clean two-factor model is not supported; the correlational structure of the SF-36 subscales is more complex than hypothesized.

#summary(fit, fit.measures=T, standardized=T, rsquare=T)
modificationIndices(fit, sort. = TRUE, minimum.value = 3)
##        lhs op     rhs     mi    epc sepc.lv sepc.all sepc.nox
## 23      MH =~ GENHLTH 32.527  4.270   4.270    1.062    1.062
## 31 MENHLTH ~~ PHSFUNC 26.178 -4.306  -4.306   -0.395   -0.395
## 24      PH =~ MENHLTH 25.898 -3.608  -3.608   -0.816   -0.816
## 20      MH =~ PHSFUNC 21.195 -4.291  -4.291   -0.873   -0.873
## 29 MENHLTH ~~ ROLEEMO 20.032  2.690   2.690    0.385    0.385
## 41 ROLEEMO ~~   VITAL 17.170 -1.890  -1.890   -0.343   -0.343
## 54 ROLPHYS ~~ GENHLTH 16.933 -3.053  -3.053   -0.360   -0.360
## 50 PHSFUNC ~~ ROLPHYS 14.352  3.468   3.468    0.346    0.346
## 32 MENHLTH ~~ ROLPHYS 13.047 -2.280  -2.280   -0.296   -0.296
## 34 MENHLTH ~~ GENHLTH 12.376  2.468   2.468    0.267    0.267
## 43 ROLEEMO ~~ ROLPHYS 11.784  1.741   1.741    0.271    0.271
## 49   VITAL ~~ GENHLTH  9.706  1.689   1.689    0.232    0.232
## 27      PH =~   VITAL  8.440  1.558   1.558    0.467    0.467
## 51 PHSFUNC ~~ BODPAIN  4.492  1.180   1.180    0.167    0.167
## 46   VITAL ~~ PHSFUNC  3.711  1.252   1.252    0.146    0.146

3. Path Analysis with multiple-indicator latent variables

This example uses the lavaan package to conduct structural equation modeling and some support packages (semPlot, semoutput, semTools, & sjPlot) for displaying results. The model below deals with mediation among latent variables. This example uses data from Kelloway’s book (1998, p.122). The model predicts perceived workplace risk and willingness to participate in occupational safety programs. The two predictor variables are organizational climate regarding safety and accident history. Perceived risk is hypothesized as a mediating variable between climate and participation, and between accident history and participation. The results do not support the mediation hypothesis.

Variables in the model:

-Exogenous latent variables: Perceived Safety Climate (CLIMATE), Accident History (ACCHIST)
-Exogenous indicator variables: plant, super & cow are measures of organizational climate regarding safety [plant manager commitment (plant), supervisor commitment (super), coworker commitment (cow)] own accident history (dir), witnessed accidents (vic)
-Endogenous latent variables: Perceived Risk in the Workplace (PERCRISK), Willingness to Participate in Safety Programs (WILLPART)
-Endogenous indicator variables: perceived risk to self (risk), perceived risk to others (riskc) p1, p2, & p3 are parcels of a multi-item scale measuring willingness to participate.

Here is my code for specifying the model and running it in lavaan. This example code also shows how to specify parameter names {a1,a2,b,c1,c2} and use them to define the indirect and total effects. The indirect and total effects are printed at the bottom of the Summary Output under Path Regressions. To see the indirect and total effects as displayed in the console, click on the Full Output tab and scroll to the bottom section: ‘Defined Parameters’.

Reading in data:

# Reading correlation matrix
corr <- '
 1.00
 .76 1.00
 .78  .68  1.00
 -.05  .01  -.05 1.00
 .10  .09   .08  .52 1.00
 .01  .08   .14 -.28 -.22 1.00
 .20  .18   .29 -.44 -.29  .54 1.00
 .22  .25   .29 -.42 -.31  .57  .65 1.00
 .002 .002 -.05  .42  .22 -.13 -.26 -.20 1.00
 .12  .03   .11  .26  .30 -.12 -.20 -.17  .47 1.00 '

# add variable names and convert to full correlation matrix
corrfull <- getCov(corr, names=c("p1","p2","p3","risk","riskc","plant","super","cow","dir","vic"))
# add SDs and convert to full covariance matrix
covfull <- lavaan::cor2cov(corrfull, sds=c(2.7, 3.1, 2.9, 7.3, 1.5, 1.4, 1.2, 1.2, 1.7, 0.8))

3.1 Correlation Matrix

Using sjPlot to create this nice table.

# Uses sjPlot to print a nice looking correlation table
sjPlot::tab_corr(corrfull, na.deletion = "listwise", digits = 2, triangle = "lower",
         title = "Correlations among observed variables (N = 115)",
         string.diag=c('1','1','1','1','1','1','1','1','1','1'))
Correlations among observed variables (N = 115)
  p1 p2 p3 risk riskc plant super cow dir vic
p1 1                  
p2 0.76 1                
p3 0.78 0.68 1              
risk -0.05 0.01 -0.05 1            
riskc 0.10 0.09 0.08 0.52 1          
plant 0.01 0.08 0.14 -0.28 -0.22 1        
super 0.20 0.18 0.29 -0.44 -0.29 0.54 1      
cow 0.22 0.25 0.29 -0.42 -0.31 0.57 0.65 1    
dir 0.00 0.00 -0.05 0.42 0.22 -0.13 -0.26 -0.20 1  
vic 0.12 0.03 0.11 0.26 0.30 -0.12 -0.20 -0.17 0.47 1
Computed correlation used pearson-method with listwise-deletion.

Specify & run the model:

# specify latent path model w direct & indirect effects (partially mediated model)
kell_p1 <- '
# defining latent variables
 CLIMATE  =~ plant + super + cow
 ACCHIST  =~ dir + vic
 PERCRISK =~ risk + riskc
 WILLPART =~ p1 + p2 + p3
# defining structual relations and assigning parameter names {a1,a2,b,c1,c2}
 PERCRISK ~ a1*CLIMATE + a2*ACCHIST
 WILLPART ~ b*PERCRISK + c1*CLIMATE + c2*ACCHIST
# defining indirect effects
 CLIMATE_a1b := a1*b
 ACCHIST_a2b := a2*b
# defining total effects
 tot1_CLIMATE := c1 + (a1*b)
 tot2_ACCHIST := c2 + (a2*b) '
# fit latent path model w direct & indirect effects
kell_p1.fit <- lavaan::sem(kell_p1, sample.cov=covfull, std.lv=F, sample.nobs=115)

3.2 Results Tabs

Multiple parts of lavaan output are created as tabs using semoutput package.

Summary Output

The “Regression Paths” section lists the indirect and total effects as well as their significance tests. Note that both the indirect effects of CLIMATE and ACCHIST on WILLPART are not significant and that only the total effect of CLIMATE on WILLPART is significant.

sem_tables(kell_p1.fit)
Model Significance
Sample.Size Chi.Square df p.value
115 31.159 29 0.358
Model Fit Measures
CFI RMSEA RMSEA.Lower RMSEA.Upper SRMR AIC BIC
0.995 0.025 0 0.077 0.049 4364.955 4436.323
Factor Loadings
Standardized
Latent Factor Indicator Loadings sig p Lower.CI Upper.CI SE z
CLIMATE plant 0.657 *** 0 0.532 0.782 0.064 10.326
CLIMATE super 0.802 *** 0 0.704 0.901 0.050 16.023
CLIMATE cow 0.832 *** 0 0.738 0.926 0.048 17.351
ACCHIST dir 0.777 *** 0 0.571 0.983 0.105 7.400
ACCHIST vic 0.605 *** 0 0.415 0.795 0.097 6.257
PERCRISK risk 0.844 *** 0 0.694 0.993 0.076 11.083
PERCRISK riskc 0.616 *** 0 0.464 0.769 0.078 7.926
WILLPART p1 0.921 *** 0 0.865 0.977 0.029 32.216
WILLPART p2 0.819 *** 0 0.745 0.894 0.038 21.595
WILLPART p3 0.846 *** 0 0.777 0.914 0.035 24.068
Regression Paths
Standardized
Predictor DV Path Values SE z sig p Lower.CI Upper.CI
CLIMATE PERCRISK -0.448 0.108 -4.158 *** 0.000 -0.659 -0.237
ACCHIST PERCRISK 0.440 0.119 3.682 *** 0.000 0.206 0.674
PERCRISK WILLPART 0.226 0.198 1.142 0.253 -0.162 0.614
CLIMATE WILLPART 0.444 0.142 3.129 ** 0.002 0.166 0.722
ACCHIST WILLPART 0.058 0.165 0.352 0.725 -0.265 0.381
a1*b CLIMATE_a1b -0.101 0.096 -1.055 0.292 -0.289 0.087
a2*b ACCHIST_a2b 0.099 0.092 1.085 0.278 -0.080 0.279
c1+(a1*b) tot1_CLIMATE 0.343 0.110 3.108 ** 0.002 0.127 0.559
c2+(a2*b) tot2_ACCHIST 0.157 0.126 1.248 0.212 -0.090 0.404
Latent Factor Correlations
Factor 1 Factor 2 r sig p Lower.CI Upper.CI SE
CLIMATE ACCHIST -0.346 ** 0.003 -0.574 -0.119 0.116
Latent Factor Variance/Residual Variance
Factor 1 Factor 2 var var.std sig p
CLIMATE CLIMATE 0.838 1.000 *** 0.000
ACCHIST ACCHIST 1.729 1.000 ** 0.002
PERCRISK PERCRISK 17.657 0.470
0.011
WILLPART WILLPART 5.343 0.871 *** 0.000
R-Squared Values
Variable R-Squared
PERCRISK 0.530229
WILLPART 0.128877

Diagram Output

The fit of the model to the data is reasonable according to the following fit indices: CFI = .995, TLI = .992, RMSEA = .025, and SRMR = .049. The \(\chi^2\) = 31.159 with df = 29, and p = .358. The results are summarized in Figure 6 which presents standardized coefficients.

The model explains 53% of the variance in PERCRISK, and 13% of the variance in WILLPART. ACCHIST and CLIMATE are negatively correlated and they have significant and similar effects on PERCRISK. CLIMATE has a significant effect on WILLPART. The effect of ACCHIST on WILLPART is not significant. There is no support for PERCRISK as a mediator as its effect on WILLPART is not significant.

The dashed arrows identify the “marker” variable that is used to define the metric of each latent variable. These are fixed to 1.00 in the unstandardized solution.
#--making path diagram w m.e.---------------------mar(bot,lft,top,rgt)
fig6 <-semPlot::semPaths(kell_p1.fit, whatLabels = "std", layout = "tree2", 
         rotation = 2, style = "lisrel", optimizeLatRes = TRUE, 
         structural = FALSE, layoutSplit = FALSE,
         intercepts = FALSE, residuals = T, 
         curve = 3, curvature = 3, nCharNodes = 8, 
         sizeLat = 9, sizeMan = 8, sizeMan2 = 6, 
         edge.label.cex = 0.9, label.cex=1, residScale=10, mar=c(2,3,2,3), 
         edge.color = "black", edge.label.position = .40, DoNotPlot=T)
#--working with semptools to modify path diagrams
#--making adjustments w piping |>
fig6_1 <- fig6 |> mark_sig(kell_p1.fit, alpha = c("(n.s.)" = 1.00, "*" = .05))
plot(fig6_1)  
Figure 6. Path analysis with multiple-indicator latent variables.

Figure 6. Path analysis with multiple-indicator latent variables.

Full Output

Summary

Using summary() function to write lavaan results to the console. There is a lot of output to review. The first bit is model fit statistics. Then we see the loadings of the observed variables onto their respective latent variables (factors). The column labeled Estimate gives the unstandardized coefficients, and the column Std.all gives the completely standardized coefficients. Next we see the regression model involving the latent variables. The variances are listed next. Note that for variable names that start with a “.” the values are estimated residual variances. For variables that do not have a “.” prefix (CLIMATE & ACCHIST) the values are estimated variances. Next we see the R-square values indicating the proportion of variance explained in the observed variables by the latent variables and the proportion of variance explained in the two endogenous factors PERCRISK and WILLPART by the structural model. Finally, we see the indirect and total effects as we specified in our model statement above.
summary(kell_p1.fit, fit.measures = TRUE, standardized = TRUE, ci=F, rsquare=T)
## lavaan 0.6.15 ended normally after 87 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                        26
## 
##   Number of observations                           115
## 
## Model Test User Model:
##                                                       
##   Test statistic                                31.159
##   Degrees of freedom                                29
##   P-value (Chi-square)                           0.358
## 
## Model Test Baseline Model:
## 
##   Test statistic                               487.719
##   Degrees of freedom                                45
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.995
##   Tucker-Lewis Index (TLI)                       0.992
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)              -2156.477
##   Loglikelihood unrestricted model (H1)      -2140.898
##                                                       
##   Akaike (AIC)                                4364.955
##   Bayesian (BIC)                              4436.323
##   Sample-size adjusted Bayesian (SABIC)       4354.142
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.025
##   90 Percent confidence interval - lower         0.000
##   90 Percent confidence interval - upper         0.077
##   P-value H_0: RMSEA <= 0.050                    0.726
##   P-value H_0: RMSEA >= 0.080                    0.039
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.049
## 
## 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
##   CLIMATE =~                                                            
##     plant             1.000                               0.916    0.657
##     super             1.047    0.155    6.767    0.000    0.959    0.802
##     cow               1.086    0.159    6.839    0.000    0.994    0.832
##   ACCHIST =~                                                            
##     dir               1.000                               1.315    0.777
##     vic               0.366    0.100    3.655    0.000    0.482    0.605
##   PERCRISK =~                                                           
##     risk              1.000                               6.131    0.844
##     riskc             0.150    0.030    4.972    0.000    0.921    0.616
##   WILLPART =~                                                           
##     p1                1.000                               2.477    0.921
##     p2                1.021    0.091   11.174    0.000    2.529    0.819
##     p3                0.986    0.084   11.672    0.000    2.442    0.846
## 
## Regressions:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   PERCRISK ~                                                            
##     CLIMATE   (a1)   -2.999    0.827   -3.628    0.000   -0.448   -0.448
##     ACCHIST   (a2)    2.050    0.712    2.878    0.004    0.440    0.440
##   WILLPART ~                                                            
##     PERCRISK   (b)    0.091    0.083    1.107    0.268    0.226    0.226
##     CLIMATE   (c1)    1.201    0.426    2.818    0.005    0.444    0.444
##     ACCHIST   (c2)    0.109    0.312    0.350    0.726    0.058    0.058
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   CLIMATE ~~                                                            
##     ACCHIST          -0.417    0.165   -2.530    0.011   -0.346   -0.346
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .plant             1.105    0.171    6.457    0.000    1.105    0.569
##    .super             0.508    0.109    4.666    0.000    0.508    0.356
##    .cow               0.439    0.108    4.066    0.000    0.439    0.308
##    .dir               1.136    0.462    2.457    0.014    1.136    0.396
##    .vic               0.402    0.079    5.082    0.000    0.402    0.634
##    .risk             15.240    6.656    2.290    0.022   15.240    0.288
##    .riskc             1.383    0.232    5.965    0.000    1.383    0.620
##    .p1                1.093    0.361    3.026    0.002    1.093    0.151
##    .p2                3.131    0.541    5.789    0.000    3.131    0.329
##    .p3                2.375    0.451    5.269    0.000    2.375    0.285
##     CLIMATE           0.838    0.229    3.659    0.000    1.000    1.000
##     ACCHIST           1.729    0.558    3.097    0.002    1.000    1.000
##    .PERCRISK         17.657    6.964    2.535    0.011    0.470    0.470
##    .WILLPART          5.343    0.926    5.767    0.000    0.871    0.871
## 
## R-Square:
##                    Estimate
##     plant             0.431
##     super             0.644
##     cow               0.692
##     dir               0.604
##     vic               0.366
##     risk              0.712
##     riskc             0.380
##     p1                0.849
##     p2                0.671
##     p3                0.715
##     PERCRISK          0.530
##     WILLPART          0.129
## 
## Defined Parameters:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##     CLIMATE_a1b      -0.274    0.263   -1.042    0.298   -0.101   -0.101
##     ACCHIST_a2b       0.187    0.177    1.058    0.290    0.099    0.099
##     tot1_CLIMATE      0.927    0.331    2.800    0.005    0.343    0.343
##     tot2_ACCHIST      0.296    0.247    1.200    0.230    0.157    0.157

Residual Correlation Matrix

The values below are the residual correlations (i.e., observed correlation - model estimated correlation). They are summarized by the SRMR which is .049.

sem_residuals(kell_p1.fit)
plant super cow dir vic risk riskc p1 p2 p3
plant
super 0.01
cow 0.02 -0.02
dir 0.05 -0.04 0.02
vic 0.02 -0.03 0.00 0.00
risk 0.05 -0.03 0.00 0.03 -0.04
riskc 0.02 0.01 0.00 -0.06 0.08 0.00
p1 -0.16 -0.01 0.00 -0.03 0.10 -0.05 0.10
p2 -0.08 -0.01 0.05 -0.02 0.01 0.01 0.09 0.01
p3 -0.02 0.09 0.09 -0.08 0.09 -0.05 0.08 0.00 -0.01

Residuals Analysis

Displays residual correlations and z-scores indicating the magnitude of the residuals. Typically, z values > |2.54| are interpreted as significant and indicate where the model is doing a poor job of reproducing the observed correlations.

lavResiduals(kell_p1.fit)
## $type
## [1] "cor.bentler"
## 
## $cov
##        plant  super    cow    dir    vic   risk  riskc     p1     p2     p3
## plant  0.000                                                               
## super  0.013  0.000                                                        
## cow    0.024 -0.018  0.000                                                 
## dir    0.047 -0.044  0.024  0.000                                          
## vic    0.018 -0.032  0.004  0.000  0.000                                   
## risk   0.053 -0.034  0.001  0.030 -0.044  0.000                            
## riskc  0.023  0.007 -0.002 -0.065  0.078  0.000  0.000                     
## p1    -0.164 -0.013 -0.001 -0.026  0.098 -0.045  0.103  0.000              
## p2    -0.075 -0.010  0.053 -0.023  0.011  0.014  0.093  0.005  0.000       
## p3    -0.020  0.094  0.087 -0.075  0.090 -0.046  0.083  0.001 -0.013  0.000
## 
## $cov.z
##        plant  super    cow    dir    vic   risk  riskc     p1     p2     p3
## plant  0.000                                                               
## super  0.542  0.000                                                        
## cow    1.156 -2.262  0.000                                                 
## dir    0.749 -0.926  0.566  0.000                                          
## vic    0.244 -0.516  0.073  0.000  0.000                                   
## risk   1.025 -0.947  0.038  1.668 -1.942  0.000                            
## riskc  0.362  0.137 -0.045 -1.362  1.409  0.000  0.000                     
## p1    -2.584 -0.276 -0.022 -0.669  1.494 -1.404  1.507  0.000              
## p2    -1.083 -0.161  0.974 -0.414  0.150  0.273  1.254  1.894  0.000       
## p3    -0.300  1.670  1.644 -1.454  1.264 -0.953  1.136  0.470 -2.188  0.000
## 
## $summary
##                            cov
## srmr                     0.049
## srmr.se                  0.008
## srmr.exactfit.z          0.509
## srmr.exactfit.pvalue     0.305
## usrmr                    0.019
## usrmr.se                 0.016
## usrmr.ci.lower          -0.007
## usrmr.ci.upper           0.045
## usrmr.closefit.h0.value  0.050
## usrmr.closefit.z        -1.933
## usrmr.closefit.pvalue    0.973

Modification Indices

modificationIndices(kell_p1.fit, sort. = TRUE, minimum.value = 3)
##          lhs op   rhs    mi    epc sepc.lv sepc.all sepc.nox
## 71     plant ~~    p1 5.186 -0.353  -0.353   -0.321   -0.321
## 90       dir ~~  risk 5.066  2.518   2.518    0.605    0.605
## 95       vic ~~  risk 4.563 -1.018  -1.018   -0.411   -0.411
## 74     super ~~   cow 4.558 -0.353  -0.353   -0.746   -0.746
## 109       p2 ~~    p3 4.417 -2.488  -2.488   -0.912   -0.912
## 91       dir ~~ riskc 4.029 -0.383  -0.383   -0.306   -0.306
## 107       p1 ~~    p2 3.851  2.689   2.689    1.453    1.453
## 96       vic ~~ riskc 3.561  0.162   0.162    0.217    0.217
## 58  WILLPART =~ plant 3.549 -0.090  -0.224   -0.160   -0.160
## 101     risk ~~    p1 3.503 -1.485  -1.485   -0.364   -0.364
## 41   CLIMATE =~    p3 3.422  0.388   0.355    0.123    0.123

Reliability Analysis

The semTools package has a function compRelSEM that reads the lavaan output and returns the reliability estimates for each latent variable. Setting the options ‘tau.eq=T’ and ‘obs.var=T’ produces equivalent to Cronbach’s reliability coefficient alpha. Setting ‘tau.eq=F’ produces the omega coefficient of reliability.

relests<-semTools::compRelSEM(kell_p1.fit, tau.eq=T, obs.var=T, return.total=T)
The reliability of CLIMATE is 0.8047043.
The reliability of ACCHIST is 0.5317361.
The reliability of PERCRISK is 0.340306.
The reliability of WILLPART is 0.8923033.




Concluding Remarks

So, these examples are some of the cool analyses you can do in lavaan. Other examples include multiple-group models and latent growth-curve models. There are various R packages for converting lavaan output into tables and path diagrams in Rmarkdown: semTools, semoutput, semPlot, sjPlot, psych, GPArotation, psychTools, and semptools. Some of these packages produce html documents, others produce Word and PDF documents. When rendering Rmarkdown files to Word documents, there does not seem to be a easy way to include equation numbers. Also, adding partition lines in a matrix seems to work for html documents but not for Word documents.

LS0tDQp0aXRsZTogIkFuIEludHJvZHVjdGlvbiB0byBTdHJ1Y3R1cmFsIEVxdWF0aW9uIE1vZGVsaW5nIg0KYXV0aG9yOiAiSmFzb24gQmVja3N0ZWFkIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgdG9jX2RlcHRoOiAzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNCiMjIFNldHVwDQpSZXF1aXJlZCBQYWNrYWdlcw0KYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQoNCmlmICghcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpDQpsaWJyYXJ5KHBhY21hbikNCiMgTG9hZCBpbnN0YWxsZWQgcGFja2FnZXMsIGluc3RhbGwgYW5kIGxvYWQgdGhlIHBhY2thZ2UgaWYgbm90IGluc3RhbGxlZA0KcGFjbWFuOjpwX2xvYWQoaGVyZSwgcmlvLCBjb25mbGljdGVkLCB0aWR5dmVyc2UsIA0KICAgICAgICAgICAgICAgamFuaXRvciwgc3VtbWFyeXRvb2xzLCBza2ltciwgRGF0YUV4cGxvcmVyLCBpbnNwZWN0ZGYsIEhtaXNjLA0KICAgICAgICAgICAgICAgbGF2YWFuLCBzZW1Ub29scywgc2Vtb3V0cHV0LCBzZW1QbG90LCBzalBsb3QsIHBzeWNoLCByZWFkciwgZHBseXIsDQogICAgICAgICAgICAgICBHUEFyb3RhdGlvbiwgcHN5Y2hUb29scywgc2VtcHRvb2xzLHRpbnl0ZXgpDQpgYGANCg0KIyMgTElTUkVMDQoNCipJbiB0aGUgQmVnaW5uaW5nIHRoZXJlIHdhcyBMSVNSRUwsIGFuZCBpdCB3YXMgZ29vZC4uLiogU3RydWN0dXJhbCBlcXVhdGlvbiBtb2RlbGluZyAoU0VNKSBpcyBhIGZhbWlseSBvZiBzdGF0aXN0aWNhbCBtZXRob2RzIGZvciBhbmFseXppbmcgdGhlIHN0cnVjdHVyZSBvZiBhIHZhcmlhbmNlL2NvdmFyaWFuY2UgbWF0cml4IGFtb25nIGEgc2V0IG9mIHZhcmlhYmxlcy4gU0VNIGhhcyBpdHMgb3JpZ2lucyBpbiB0aGUgd29yayBvZiBLYXJsIEpvcmVza29nIHdobyBkZXZlbG9wZWQgdGhlIExJU1JFTCBtb2RlbCBhbmQgc29mdHdhcmUgZHVyaW5nIHRoZSAxOTcwcy4gTElTUkVMIGlzIHRoZSBhY3JvbnltIGZvciAqKkxJKipuZWFyICoqUyoqdHJ1Y3R1cmFsICoqUkVMKiphdGlvbnMuDQoNCmBgYHtyIG1vZGVsLCBlY2hvPVR9DQojIGNyZWF0aW5nIGFuIGV4YW1wbGUgdyA1IGxhdGVudHMgdyAzIGluZGljYXRvcnMgYW5kIG1lZGlhdGlvbg0KDQojIExhbWJkYSBtYXRyaWNlczoNCmxhbWJkYXggPC0gbWF0cml4KDAsOSwzKQ0KbGFtYmRheFsxOjMsMV0gPC0xDQpsYW1iZGF4WzQ6NiwyXSA8LTENCmxhbWJkYXhbNzo5LDNdIDwtMQ0KDQpsYW1iZGF5IDwtIG1hdHJpeCgwLDYsMikNCmxhbWJkYXlbMTozLDFdIDwtIDENCmxhbWJkYXlbNDo2LDJdIDwtIDENCg0KIyBQaGkgYW5kIFBzaSBtYXRyaWNlczoNCkxhdFggPC0gbWF0cml4KDEsMywzKSAjcGhpDQpMYXRZIDwtIGRpYWcoMSwyLDIpICAgI3BzeQ0KDQojIEdhbW1hIG1hdHJpeDoNCkdhbW1hIDwtIG1hdHJpeCgwLDIsMykNCkdhbW1hWzEsMV0gPC0gMQ0KR2FtbWFbMSwyXSA8LSAxDQpHYW1tYVsxLDNdIDwtIDENCkdhbW1hWzIsMl0gPC0gMQ0KDQojIEJldGEgbWF0cml4Og0KQmV0YSA8LSBtYXRyaXgoMCwyLDIpDQpCZXRhWzIsMV0gPC0gMQ0KDQojIFRoZXRhIG1hdHJpY2VzOg0KdGhkIDwtIGRpYWcoMSxucm93KGxhbWJkYXgpKQ0KdGhlIDwtIGRpYWcoMSxucm93KGxhbWJkYXkpKQ0KDQojIENvbWJpbmUgbWF0cmljZXMgaW50byBhIG1vZGVsOg0KbW9kMyA8LSBsaXNyZWxNb2RlbChMWD1sYW1iZGF4LCBMWT1sYW1iZGF5LCBURD10aGQsIFRFPXRoZSwNCiAgICAgICAgICAgICAgICAgICAgUEg9TGF0WCwgR0E9R2FtbWEsIEJFPUJldGEsIFBTPUxhdFkpDQoNCmBgYA0KDQojIyMgVGhlIExJU1JFTCBtb2RlbCBhcyBwYXRoIGRpYWdyYW0NCg0KYGBge3IgcGF0aGRpYWdyYW0sIGVjaG89VCwgZmlnLndpZHRoPTcsZmlnLmhlaWdodD02LCBmaWcuY2FwPSJGaWd1cmUgMS4gVGhlIExJU1JFTCBtb2RlbCBmb3IgYSBoeXBvdGhldGljYWwgZXhhbXBsZS4ifQ0KIyBQbG90IHBhdGggZGlhZ3JhbSB1c2luZyBzZW1QbG90OjpzZW1QYXRocyBzaXplbGF0PTQsIHNpemVtYW49Mw0Kc2VtUGF0aHMobW9kMywgYXMuZXhwcmVzc2lvbj1jKCJub2RlcyIsImVkZ2VzIiksIHNpemVNYW4gPSA0LA0KICAgICAgICAgc2l6ZUxhdCA9IDUsIHN0eWxlPSJsaXNyZWwiLCByb3RhdGlvbiA9IDIsDQogICAgICAgICByZXNpZFNjYWxlPTgsIGVkZ2UuY29sb3I9ImJsYWNrIiwgbWFyPWMoMiwyLDIsMiksDQogICAgICAgICBlZGdlLmxhYmVsLmNleCA9IDAuOSwgbGFiZWwuY2V4PTEuNSkNCmBgYA0KDQp8IEZpZ3VyZSAxIGlzIHRoZSBjbGFzc2ljIGdyYXBoaWNpY2FsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBmdWxsIExJU1JFTCBtb2RlbC4gU3F1YXJlcyBkZW5vdGUgb2JzZXJ2ZWQgdmFyaWFibGVzIChpbmRpY2F0b3JzKSBhbmQgY2lyY2xlcyByZXByZXNlbnQgbGF0ZW50IHZhcmlhYmxlcyAoY29uc3RydWN0cykuIEluIHRoaXMgZXhhbXBsZSB0aGVyZSBhcmUgNSBsYXRlbnQgdmFyaWFibGVzOyAzIG9mIHRoZW0gYXJlIGV4b2dlbm91cywgJFx4aV8xLCBceGlfMiwgXHhpXzMkICh4aSdzIG9uIHRoZSBsZWZ0KSBhbmQgMiBhcmUgZW5kb2dlbm91cywgJFxldGFfMSwgXGV0YV8yJCAoZXRhJ3MsIG9uIHRoZSByaWdodCkuIA0KfA0KfCBUaGUgYXJyb3dzIGluIHRoZSBmaWd1cmUgaW1wbHkgaHlwb3RoZXNpemVkIGNhdXNhbCByZWxhdGlvbnMgYmV0d2VlbiB2YXJpYWJsZXMgYW5kIHJlcHJlc2VudCBwYXJhbWV0ZXJzIHRvIGJlIGVzdGltYXRlZC4gQXJyb3dzIGNvbm5lY3Rpbmcgc3F1YXJlcyB0byBjaXJjbGVzIHJlcHJlc2VudCB0aGUgKm1lYXN1cmVtZW50IHBvcnRpb24qIG9mIHRoZSBtb2RlbC4gRWFjaCBsYXRlbnQgdmFyaWFibGUgaW4gRmlndXJlIDEgaGFzIDMgaW5kaWNhdG9ycywgb3Igb2JzZXJ2ZWQgdmFyaWFibGVzIGh5cG90aGVzaXplZCB0byBtZWFzdXJlIHRoZSB1bmRlcmx5aW5nIGNvbnN0cnVjdCBvZiBpbnRlcmVzdC4gRm9yIGV4YW1wbGUgJFx4aV8xJCBoYXMgaW5kaWNhdG9ycyAkeF8xLCB4XzIkIGFuZCAkeF8zJC4gVGhlIGFycm93cyBjb25uZWN0aW5nIHRoZXNlIHZhcmlhYmxlcyBhcmUgbGFiZWxlZCB3aXRoICRcbGFtYmRhX3tpan0kIChsYW1iZGEpIGNvZWZmaWNpZW50cyByZXByZXNlbnRpbmcgZmFjdG9yIGxvYWRpbmdzLiBUaGUgc3Vic2NyaXB0IG51bWJlcmluZyBzeXN0ZW0gaXMgc3VjaCB0aGF0ICppKiByZWZlcnMgdG8gdGhlIHZhcmlhYmxlIGJlaW5nIHBvaW50ZWQgdG8gYW5kICpqKiBpbmRpY2F0ZXMgdGhlIHZhcmlhYmxlIHdoZXJlIHRoZSBhcnJvdyBvcmlnaW5hdGVzLCBhbmQgaW4gdGhpcyBjYXNlIHRoZXkgaWRlbnRpZnkgdGhlIHBhcmFtZXRlciBieSBpdHMgcm93IGFuZCBjb2x1bW4gcG9zaXRpb24gaW4gdGhlICRcbWF0aGJmXExhbWJkYSQgbWF0cml4IGRpc2N1c3NlZCBiZWxvdy4gRWFjaCBpbmRpY2F0b3IgdmFyaWFibGUgKHNxdWFyZSkgYWxzbyBoYXMgYW4gYXJyb3cgcG9pbnRpbmcgdG8gaXQgdGhhdCByZXByZXNlbnRzIGEgbWVhc3VyZW1lbnQgZXJyb3IgdmFyaWFuY2UgcGFyYW1ldGVyICRcdGhldGFfe2lpfSQgKHRoZXRhKS4NCg0KfA0KfCBBcnJvd3MgYmV0d2VlbiBjaXJjbGVzIHJlcHJlc2VudCB0aGUgKnN0cnVjdHVyYWwgcG9ydGlvbiogb2YgdGhlIG1vZGVsLCB0aGF0IGlzIHRoZSBoeXBvdGhlc2l6ZWQgcmVsYXRpb25zIGJldHdlZW4gbGF0ZW50IHZhcmlhYmxlcy4gVGhlIGV4b2dlbm91cyBsYXRlbnQgdmFyaWFibGVzICRceGlfMSwgXHhpXzIsIFx4aV8zJCBhcmUgY29ubmVjdGVkIGJ5ICp0d28taGVhZGVkIGFycm93cyogaW1wbHlpbmcgdGhhdCB0aGV5IGFyZSBjb3JyZWxhdGVkOyB0aGVzZSBhcnJvd3MgYXJlIGxhYmVsZWQgd2l0aCAkXHBoaV97aWp9JCAocGhpKSBjb2VmZmljaWVudHMuICRceGlfMSwgXHhpXzIsJCBhbmQgJFx4aV8zJCBhbGwgaGF2ZSBhcnJvd3MgcG9pbnRpbmcgdG8gJFxldGFfMSQsIGluZGljYXRpbmcgdGhhdCB0aGV5IGFyZSBoeXBvdGhlc2l6ZWQgdG8gcHJlZGljdCB2YXJpYW5jZSBpbiAkXGV0YV8xJC4gQWxzbyBub3RlIHRoYXQgJFxldGFfMSQgcG9pbnRzIHRvICRcZXRhXzIkIGluZGljYXRpbmcgdGhhdCAkXGV0YV8xJCBpcyBoeXBvdGhlc2l6ZWQgYXMgYSAqbWVkaWF0b3IqIGJldHdlZW4gJFx4aV8xLCBceGlfMiwgXHhpXzMkIGFuZCAkXGV0YV8yJC4gVGhlcmUgaXMgYWxzbyBhIGRpcmVjdCBlZmZlY3Qgb2YgJFx4aV8yJCBvbiAkXGV0YV8yJC4gQXJyb3dzIGJldHdlZW4gJFx4aSQgdmFyaWFibGVzIGFuZCAkXGV0YSQgdmFyaWFibGVzIGFyZSBsYWJlbGVkIHdpdGggJFxnYW1tYV97aWp9JCBjb2VmZmljaWVudHMuIEFycm93cyBiZXR3ZWVuICRcZXRhJCB2YXJpYWJsZXMgYXJlIGxhYmVsZWQgd2l0aCAkXGJldGFfe2lqfSQgY29lZmZpY2llbnRzLiBCb3RoICRcZXRhJCB2YXJpYWJsZXMgYWxzbyBoYXZlIGFycm93cyBwb2ludGluZyB0byB0aGVtIHRoYXQgcmVwcmVzZW50IHVuZXhwbGFpbmVkIHZhcmlhbmNlIGFuZCB0aGVzZSBhcmUgYXJlIGxhYmVsZWQgd2l0aCAkXHBzaSQgKHBzaSkgY29lZmZpY2llbnRzLiBUaGUgdmFyaW91cyBjb2VmZmljaWVudHMgJFxsYW1iZGEsIFx0aGV0YSwgXGdhbW1hLCBcYmV0YSwgXHBoaSwgXHBzaSQgYXJlIGVsZW1lbnRzIG9mIHNwZWNpZmljIG1hdHJpY2VzIGRpc2N1c3NlZCBiZWxvdy4gDQoNCiMjIyBUaGUgTElTUkVMIG1vZGVsIGluIG1hdHJpeCBub3RhdGlvbg0KDQpUaGUgTElTUkVMIG1vZGVsIGNvbnNpc3RzIG9mIHR3byBwYXJ0cywgYSBtZWFzdXJlbWVudCBtb2RlbCBhbmQgYSBzdHJ1Y3R1cmFsIG1vZGVsLiBUaGlzIGlzIHRoZSBzdHJ1Y3R1cmFsIHBvcnRpb24gb2YgdGhlIG1vZGVsIHNob3dpbmcgaG93IHRoZSB2YXJpYW5jZXMgb2YgdGhlIGVuZG9nZW5vdXMgbGF0ZW50IHZhcmlhYmxlcyAoY29uc3RydWN0cykgYXJlIHJlbGF0ZWQ6DQokJA0KXG1hdGhiZntcZXRhID0gXGJldGEgXGV0YSArIFxHYW1tYSBceGkgKyBcemV0YX0gXHRhZ3sxfQ0KJCQNCg0Kd2hlcmUgJFxldGEkIChldGEpIGlzIGEgKm0qIHggMSB2ZWN0b3Igb2YgZW5kb2dlbm91cyBsYXRlbnQgY29uc3RydWN0cywgJFx4aSQgKHhpKSBpcyBhICpuKiB4IDEgdmVjdG9yIG9mIGV4b2dlbm91cyBsYXRlbnQgY29uc3RydWN0cywgYW5kICRcemV0YSQgKHpldGEpIGlzIGEgKm0qIHggMSB2ZWN0b3Igb2YgcmVzaWR1YWwgdmFyaWFuY2VzIGZvciB0aGUgZW5kb2dlbm91cyBsYXRlbnQgY29uc3RydWN0cy4gJFxiZXRhJCAoYmV0YSkgc3BlY2lmaWVzIHRoZSBjYXVzYWwgcmVsYXRpb25zaGlwcyBhbW9uZyBsYXRlbnQgZW5kb2dlbm91cyBjb25zdHJ1Y3RzICgkXGV0YSQncyk7IHNpemUgPSAqbSogeCAqbSouICRcR2FtbWEkIChnYW1tYSkgc3BlY2lmaWVzIHRoZSBjYXVzYWwgaW5mbHVlbmNlcyBvZiBsYXRlbnQgZXhvZ2Vub3VzIGNvbnN0cnVjdHMgKCRceGkkJ3MpIG9uIHRoZSBsYXRlbnQgZW5kb2dlbm91cyBjb25zdHJ1Y3RzICgkXGV0YSQncyk7IHNpemUgPSAqbSogeCAqbiouDQoNCg0KVGhpcyBpcyB0aGUgbWVhc3VyZW1lbnQgbW9kZWwgZm9yIHRoZSB2YXJpYW5jZXMgb2YgdGhlIGVuZG9nZW5vdXMgb2JzZXJ2ZWQgKnkqIHZhcmlhYmxlcyAoaW5kaWNhdG9ycyk6DQokJA0KXG1hdGhiZnt5ID0gXExhbWJkYV95IFxldGEgKyBcVGhldGFfXGVwc2lsb259XHRhZ3syfQ0KJCQNCg0Kd2hlcmUgKip5KiogaXMgYSAqcCogeCAxIHZlY3RvciBvZiBvYnNlcnZlZCBlbmRvZ2Vub3VzIGluZGljYXRvcnMsICgqcCogPSB0aGUgbnVtYmVyIG9mIG9ic2VydmVkIGVuZG9nZW5vdXMgaW5kaWNhdG9ycykgYW5kICptKiA9IHRoZSBudW1iZXIgb2YgbGF0ZW50IGVuZG9nZW5vdXMgY29uc3RydWN0cy4gJFxMYW1iZGFfeSQgKGxhbWJkYSB5KSBzcGVjaWZpZXMgd2hpY2ggKnkqIHZhcmlhYmxlcyBhcmUgaW5kaWNhdG9ycyBvZiB3aGljaCBlbmRvZ2Vub3VzIGNvbnN0cnVjdHMgKCRcZXRhJCdzKTsgc2l6ZSA9ICpwKiB4ICptKi4gJFxUaGV0YV9cZXBzaWxvbiQgKHRoZXRhIGVwc2lsb24pIHNwZWNpZmllcyB0aGUgcmVzaWR1YWwgdmFyaWFuY2VzL2NvdmFyaWFuY2VzIGFtb25nIHRoZSAqeSogdmFyaWFibGVzLiBUaGUgZGlhZ29uYWwgZWxlbWVudHMgYXJlIGVycm9yIHZhcmlhbmNlcyBhbmQgdGhlIG9mZi1kaWFnb25hbCBlbGVtZW50cyBhcmUgY292YXJpYW5jZXMgYmV0d2VlbiByZXNpZHVhbHMuIFNpemUgPSAqcCogeCAqcCosIHVzdWFsbHkgc3BlY2lmeSBhcyBkaWFnb25hbC4gDQoNCg0KVGhpcyBpcyB0aGUgbWVhc3VyZW1lbnQgbW9kZWwgZm9yIHRoZSB2YXJpYW5jZXMgb2YgdGhlIGV4b2dlbm91cyBvYnNlcnZlZCAqeCogdmFyaWFibGVzOg0KJCQNClxtYXRoYmZ7eCA9IFxMYW1iZGFfeCBceGkgKyBcVGhldGFfXGRlbHRhfVx0YWd7M30NCiQkDQp3aGVyZSAqKngqKiBpcyBhICpxKiB4IDEgdmVjdG9yIG9mIG9ic2VydmVkIGV4b2dlbm91cyBpbmRpY2F0b3JzLCAoKnEqID0gdGhlIG51bWJlciBvZiBvYnNlcnZlZCBleG9nZW5vdXMgaW5kaWNhdG9ycykgYW5kICpuKiA9IHRoZSBudW1iZXIgb2YgbGF0ZW50IGV4b2dlbm91cyBjb25zdHJ1Y3RzLiAkXExhbWJkYV94JCAobGFtYmRhIHgpIHNwZWNpZmllcyB3aGljaCAqeCogdmFyaWFibGVzIGFyZSBpbmRpY2F0b3JzIG9mIHdoaWNoIGV4b2dlbm91cyBjb25zdHJ1Y3RzICgkXHhpJCdzKTsgc2l6ZSA9ICpxKiB4ICpuKi4gJFxUaGV0YV9cZGVsdGEkICh0aGV0YSBkZWx0YSkgc3BlY2lmaWVzIHRoZSByZXNpZHVhbCB2YXJpYW5jZXMvY292YXJpYW5jZXMgYW1vbmcgdGhlICp4KiB2YXJpYWJsZXMuIFRoZSBkaWFnb25hbCBlbGVtZW50cyBhcmUgZXJyb3IgdmFyaWFuY2VzIGFuZCB0aGUgb2ZmLWRpYWdvbmFsIGVsZW1lbnRzIGFyZSBjb3ZhcmlhbmNlcyBiZXR3ZWVuIHJlc2lkdWFscy4gU2l6ZSA9ICpxKiB4ICpxKiwgdXN1YWxseSBzcGVjaWZ5IGFzIGRpYWdvbmFsLg0KDQoNCipPdGhlciBNYXRyaWNlcyBpbiB0aGUgTElTUkVMIE1vZGVsOiogICRcUGhpJCAocGhpKSDigJMgc3BlY2lmaWVzIHRoZSB2YXJpYW5jZXMgYW5kIGNvdmFyaWFuY2VzIGFtb25nIGxhdGVudCBleG9nZW5vdXMgY29uc3RydWN0cyAozr4ncyk7IHNpemUgPSAqbiogeCAqbiouICAkXFBzaSQgKHBzaSkgbWF0cml4IOKAkyBzcGVjaWZpZXMgdGhlIHZhcmlhbmNlcyBhbmQgY292YXJpYW5jZXMgYW1vbmcgdGhlIGxhdGVudCByZXNpZHVhbCB0ZXJtcyAoJFx6ZXRhJCdzKTsgc2l6ZSA9ICptKiB4ICptKi4NCg0KfA0KIyMjIEFzc2Vzc2luZyBNb2RlbCBGaXQNCg0KTGlzdGluZyB0aGUgKnAqIG9ic2VydmVkICR5JCB2YXJpYWJsZXMgZmlyc3QsIGZvbGxvd2VkIGJ5IHRoZSAqcSogb2JzZXJ2ZWQgJHgkIHZhcmlhYmxlcywgdGhlIGNvdmFyaWFuY2VzIG9mIHRoZXNlICgqcCogKyAqcSogPSAxNSkgdmFyaWFibGVzIGluIEZpZ3VyZSAxIGFyZSBjb250YWluZWQgaW4gdGhlICRcbWF0aGJmXFNpZ21hJCBtYXRyaXggd2hpY2ggbWF5IGJlIHBhcnRpdGlvbmVkIGFzOg0KDQokJFxtYXRoYmZcU2lnbWEgPQ0KXGxlZnRbDQogIFxiZWdpbnthcnJheX17Y3xjfQ0KICAgY292YXJpYW5jZXNfeSAmIGNvdmFyaWFuY2VzX3t5LHh9IFxcDQogIFxobGluZQ0KICAgY292YXJpYW5jZXNfe3gseX0gJiBjb3ZhcmlhbmNlc194DQpcZW5ke2FycmF5fSBccmlnaHRdJCQNCg0KSW4gdGhlIGZ1bGwgTElTUkVMIG1vZGVsIHRoZSBwYXJ0aXRpb25zIG9mICRcbWF0aGJmXFNpZ21hJCBhcmUgZXN0aW1hdGVkIGFzOg0KDQokJFxtYXRoYmZcU2lnbWFeKiA9IFxsZWZ0Ww0KICBcYmVnaW57YXJyYXl9e2N8Y30NCiAgXExhbWJkYV95IFsoSS1cYmV0YSleey0xfSAoXEdhbW1hXFBoaVxHYW1tYScgKyBcUHNpKShJLVxiZXRhKV57LTEnfV1cTGFtYmRhJ195ICsgXFRoZXRhX1xlcHNpbG9uICYgXExhbWJkYV95KEktXGJldGEpXnstMX0gXEdhbW1hIFxQaGkgXExhbWJkYSdfeCBcXA0KICBcaGxpbmUgDQogIFxMYW1iZGFfeCBcUGhpXEdhbW1hJyhJLVxiZXRhKV57LTEnfVxMYW1iZGEnX3kgJiBcTGFtYmRhX3hcUGhpXExhbWJkYSdfeCArIFxUaGV0YV9cZGVsdGENClxlbmR7YXJyYXl9IA0KXHJpZ2h0XSBcdGFnezR9JCQNCg0KSG93IHdlbGwgJFxtYXRoYmZcU2lnbWFeKiQgcmVwcm9kdWNlcyAkXG1hdGhiZlxTaWdtYSQgaXMgdGhlIGJhc2lzIGZvciBhc3Nlc3NpbmcgdGhlIGZpdCBvZiB0aGUgbW9kZWwgdG8gdGhlIGRhdGEuIFRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIG9ic2VydmVkIGFuZCBtb2RlbCBpbXBsaWVkIGNvdmFyaWFuY2VzIGZvcm0gdGhlIGJhc2lzIGZvciB2YXJpb3VzIGZpdCBpbmRpY2VzIHN1Y2ggYXMgJFxjaGleMiQsIEdGSSwgQ0ZJLCBUTEksIFJNU0VBLCBhbmQgU1JTUi4NCg0KfA0KfCANCiMjIEZ1cnRoZXIgRGV2ZWxvcG1lbnRzIA0KT3RoZXIgU0VNIHByb2dyYW1zIGhhdmUgYmVlbiBkZXZlbG9wZWQgc2luY2UgTElTUkVMLiBUaGVzZSBpbmNsdWRlIEVRUywgQW1vcywgTXBsdXMsIGFuZCB0aGUgYGxhdmFhbmAgcGFja2FnZSBpbiBSLiBNb3N0IG9mIHRoZXNlIHJlcGFyYW1ldGVyaXplIHRoZSBMSVNSRUwgbW9kZWwgdHJlYXRpbmcgYWxsIHZhcmlhYmxlcyBhcyBlbmRvZ2Vub3VzLiBUaGlzIG1lYW5zIHRoYXQgdGhlIHN0cnVjdHVyYWwgbW9kZWwgKEVxLiAxKSBzaW1wbGlmaWVzIHRvOg0KJCQNClxtYXRoYmZ7XGV0YSA9IFxiZXRhIFxldGEgKyBcUHNpfVx0YWd7NX0NCiQkDQoNCg0KYW5kIEVxLiAyIGFuZCBFcS4gMyBhcmUgY29tYmluZWQgaW50byBhIHNpbmdsZSBlcXVhdGlvbiBsaW5raW5nIG9ic2VydmVkIGFuZCBsYXRlbnQgdmFyaWFibGVzOg0KJCQNClxtYXRoYmZ7eSA9IFxMYW1iZGEgXGV0YSArIFxUaGV0YX0gXHRhZ3s2fQ0KJCQNCg0KIyMjIE1hdHJpY2VzIGluIERldGFpbA0KTGV0J3MgbG9vayBhdCB0aGUgcGFyYW1ldGVyIG1hdHJpY2VzIGluIEVxLiA1IGFuZCBFcS42IGluIG1vcmUgZGV0YWlsIHVzaW5nIHRoZSBtb2RlbCBpbiBGaWd1cmUgMSBhcyBhbiBleGFtcGxlLiBUaGUgJFxtYXRoYmZcTGFtYmRhJCBtYXRyaXggY29udGFpbnMgMTUgcm93cywgb25lIGZvciBlYWNoIGluZGljYXRvciB2YXJpYWJsZSAoOSAqeConcyBhbmQgNiAqeSoncykgYW5kIDUgY29sdW1ucywgb25lIGZvciBlYWNoIGxhdGVudCB2YXJpYWJsZSwgJFxldGFfMSQgdG8gJFxldGFfNSQuIFRoZSBlbGVtZW50cyAoJFxsYW1iZGFfe2lqfSQpIGFyZSBmYWN0b3IgbG9hZGluZ3MsIHF1YW50aWZ5aW5nIHRoZSByZWxhdGlvbiBiZXR3ZWVuIGVhY2ggaW5kaWNhdG9yIHZhcmlhYmxlIGFuZCB0aGUgbGF0ZW50IHZhcmlhYmxlICgkXGV0YV9qJCkgaHlwb3RoZXNpemVkIHRvIHVuZGVybGllIGl0LiBFc3RpbWF0aW5nIHNvbWUgJFxsYW1iZGFfe2lqfSQgY29lZmZpY2llbnRzIHdoaWxlIGZpeGluZyBvdGhlcnMgdG8gMC4wLCBhbGxvd3MgdXMgdG8gc3BlY2lmeSB0aGUgbWVhc3VyZW1lbnQgbW9kZWwuDQoNCiQkXG1hdGhiZlxMYW1iZGEgPQ0KIFxiZWdpbntibWF0cml4fQ0KICBcbGFtYmRhX3sxLDF9ICYgXGxhbWJkYV97MSwyfSAmIFxjZG90cyAmIFxsYW1iZGFfezEsNX0gXFwNCiAgXGxhbWJkYV97MiwxfSAmIFxsYW1iZGFfezIsMn0gJiBcY2RvdHMgJiBcbGFtYmRhX3syLDV9IFxcDQogIFx2ZG90cyAgJiBcdmRvdHMgICYgXGRkb3RzICYgXHZkb3RzICBcXA0KICBcbGFtYmRhX3sxNSwxfSAmIFxsYW1iZGFfezE1LDJ9ICYgXGNkb3RzICYgXGxhbWJkYV97MTUsNX0NCiBcZW5ke2JtYXRyaXh9JCQNCg0KVGhlICRcbWF0aGJmXGJldGEkIG1hdHJpeCBpcyBhIDUgeCA1IGluZGljYXRpbmcgdGhlIGluZmx1ZW5jZSBvZiB0aGUgY29sdW1uICRcZXRhJCB2YXJpYWJsZSBvbiB0aGUgcm93ICRcZXRhJCB2YXJpYWJsZS4gVGhlIGRpYWdvbmFsIGVsZW1lbnRzIGFyZSBzZXQgdG8gMC4wLg0KDQokJFxtYXRoYmZcYmV0YSA9DQogXGJlZ2lue2JtYXRyaXh9DQogIDAgJiBcYmV0YV97MSwyfSAmIFxjZG90cyAmIFxiZXRhX3sxLDV9IFxcDQogIFxiZXRhX3syLDF9ICYgMCAmIFxjZG90cyAmIFxiZXRhX3syLDV9IFxcDQogIFx2ZG90cyAgJiBcdmRvdHMgICYgXGRkb3RzICYgXHZkb3RzICBcXA0KICBcYmV0YV97NSwxfSAmIFxiZXRhX3s1LDJ9ICYgXGNkb3RzICYgMA0KIFxlbmR7Ym1hdHJpeH0kJA0KDQpJbiB0aGUgcmVwYXJhbWV0ZXJpemF0aW9uLCB0aGUgJFxtYXRoYmZccHNpJCBtYXRyaXggaXMgYSA1IHggNSBhbmQgY29udGFpbnMgdGhlIHZhcmlhbmNlcyBhbmQgY292YXJpYW5jZXMgYW1vbmcgZXhvZ2Vub3VzIGxhdGVudCB2YXJpYWJsZXMgKG9yaWdpbmFsbHkgaW4gJFxtYXRoYmZcUGhpJCksIGFuZCB0aGUgcmVzaWR1YWwgdmFyaWFuY2VzICgkXHpldGEkcykgZm9yIHRoZSBlbmRvZ2Vub3VzIGxhdGVudCB2YXJpYWJsZXMuIEFsbCBlbGVtZW50cyBhcmUgZGVub3RlZCBhcyAkXHBzaSQgY29lZmZpY2llbnRzLg0KJCRcbWF0aGJmXFBzaSA9DQogXGJlZ2lue2JtYXRyaXh9DQogIFxwc2lfezEsMX0gJiBccHNpX3sxLDJ9ICYgXGNkb3RzICYgXHBzaV97MSw1fSBcXA0KICBccHNpX3syLDF9ICYgXHBzaV97MiwyfSAmIFxjZG90cyAmIFxwc2lfezIsNX0gXFwNCiAgXHZkb3RzICAmIFx2ZG90cyAgJiBcZGRvdHMgJiBcdmRvdHMgIFxcDQogIFxwc2lfezUsMX0gJiBccHNpX3s1LDJ9ICYgXGNkb3RzICYgXHBzaV97NSw1fQ0KIFxlbmR7Ym1hdHJpeH0kJA0KDQpUaGUgcmVzaWR1YWwgdmFyaWFuY2VzIG9mIHRoZSAxNSBpbmRpY2F0b3IgdmFyaWFibGVzIGFyZSBpbiB0aGUgJFxUaGV0YSQgbWF0cml4IHdoaWNoIGlzIHR5cGljYWxseSBzcGVjaWZpZWQgYXMgYSBkaWFnb25hbCBtYXRyaXg6DQokJFxtYXRoYmZcVGhldGEgPQ0KIFxiZWdpbntibWF0cml4fQ0KICBcdGhldGFfezEsMX0gJiAwICYgXGNkb3RzICYgMCBcXA0KICAwICYgXHRoZXRhX3syLDJ9ICYgXGNkb3RzICYgMCBcXA0KICBcdmRvdHMgICYgXHZkb3RzICAmIFxkZG90cyAmIFx2ZG90cyAgXFwNCiAgMCAmIDAgJiBcY2RvdHMgJiBcdGhldGFfezE1LDE1fQ0KIFxlbmR7Ym1hdHJpeH0kJA0KIA0KDQojIyBVc2luZyB0aGUgbGF2YWFuIHBhY2thZ2UgZm9yIFNFTQ0KU3BlY2lmeWluZyBtb2RlbHMgaW4gYGxhdmFhbmAgaXMgcHJldHR5IHN0cmFpZ2h0Zm9yd2FyZC4gVGhlcmUgYXJlIGRpZmZlcmVudCBvcGVyYXRvcnMgZm9yIHNwZWNpZnlpbmcgdmFyaW91cyByZWxhdGlvbnMgYW1vbmcgdmFyaWFibGVzLiBSZWdyZXNzaW9ucyB1c2UgYH5gIHdpdGggdGhlIG91dGNvbWUgdmFyaWFibGUgb24gdGhlIGxlZnQgYW5kIHRoZSBwcmVkaWN0b3JzIG9uIHRoZSByaWdodC4gTGF0ZW50IHZhcmlhYmxlcyBhcmUgZGVmaW5lZCB1c2luZyB0aGUgYD1+YCBvcGVyYXRvciB3aXRoIHRoZSBsYXRlbnQgdmFyaWFibGUgb24gdGhlIGxlZnQgYW5kIHRoZSBpbmRpY2F0b3IgdmFyaWFibGVzIG9uIHRoZSByaWdodC4gVGhlIGA6PWAgb3BlcmF0b3IgaXMgdXNlZCB0byBkZWZpbmUgcGFyYW1ldGVycyBjb25zdHJ1Y3RlZCBmcm9tIG90aGVyIHBhcmFtZXRlcnM7IHNwZWNpZnlpbmcgaW5kaXJlY3QgZWZmZWN0cyBmb3IgZXhhbXBsZS4gQ29ycmVsYXRpb25zIChjb3ZhcmlhbmNlcykgYmV0d2VlbiB2YXJpYWJsZXMgYXJlIGluZGljYXRlZCB3aXRoICB0aGUgYH5+YCBvcGVyYXRvci4gSW4gdGhlIGV4YW1wbGVzIGJlbG93IEkgc2hvdyB0aGUgaG93IHRvIHNwZWNpZnkgYW5kIHJ1biB2YXJpb3VzIG1vZGVscyBpbiBgbGF2YWFuYC4gT25lIG5pY2UgZmVhdHVyZSBpcyB0aGF0IHdlIGRvbid0IGFsd2F5cyBuZWVkIHRoZSByYXcgZGF0YSBmaWxlOyB3ZSBjYW4gd29yayBmcm9tIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCwgb3IgY29ycmVsYXRpb24gbWF0cml4IHBsdXMgYSB2ZWN0b3Igb2Ygc3RhbmRhcmQgZGV2aWF0aW9ucyBhbmQgdGhlIHNhbXBsZSBzaXplLiBJJ20gZ29pbmcgdG8gd29yayB0aHJvdWdoIGEgZmV3IGV4YW1wbGVzIHRvIGRlbW9uc3RyYXRlIGJpdHMgb2YgU0VNIHRoZW9yeSBhbmQgZmVhdHVyZXMgb2YgYGxhdmFhbmAgYW5kIGl0cyBzdXBwb3J0IHBhY2thZ2VzLiBJIGNyZWF0ZWQgdGhpcyBkb2N1bWVudCBpbiBSIE1hcmtkb3duLg0KDQojIyAxLiBQYXRoIEFuYWx5c2lzIEV4YW1wbGVzDQojIyMgMWE6IFBhdGggYW5hbHlzaXMgd2l0aCBvYnNlcnZlZCB2YXJpYWJsZXMNClRoaXMgaXMgYW4gZXhhbXBsZSBvZiBwYXRoIGFuYWx5c2lzLiBJbGxuZXNzIChpbGwpIGlzIHJlZ3Jlc3NlZCBvbnRvIFN0cmVzcyAoc3RyKSBhbmQgRml0bmVzcyAoZml0KSwgd2hpY2ggYXJlIHJlZ3Jlc3NlZCBvbnRvIEhhcmRpbmVzcyAoaGFyZHkpIGFuZCBFeGVyY2lzZSAoZXhlciksIHJlc3BlY3RpdmVseS4gSGVyZSB3ZSBhcmUgcHJvdmlkaW5nIGEgY29ycmVsYXRpb24gbWF0cml4IGFuZCB2ZWN0b3Igb2Ygc3RhbmRhcmQgZGV2aWF0aW9ucy4gYGxhdmFhbmAgd2lsbCBjb252ZXJ0IHRoZXNlIGludG8gYSBjb3ZhcmlhbmNlIG1hdHJpeCBmb3IgYW5hbHlzaXMuIFdlIG11c3QgYWxzbyBwcm92aWRlIE4gPSAzNzMgd2hlbiBzcGVjaWZ5aW5nIHRoZSBtb2RlbCBiZWxvdy4NCg0KUmVhZGluZyBpbiBkYXRhOg0KYGBge3IsIGVjaG89VH0NCiMgcmVhZGluZyBpbiBjb3JyIG1hdHJpeA0KY29yciA8LSAnDQoxLjAwDQotLjAzIDEuMDANCiAuMzkgIC4wNyAxLjAwDQotLjA1IC0uMjMgLS4xMyAxLjAwDQotLjA4IC0uMTYgLS4yOSAgLjM0IDEuMDAnDQojIGFkZCB2YXJpYWJsZSBuYW1lcyBhbmQgY29udmVydCB0byBmdWxsIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29ycmZ1bGwgPC0gbGF2YWFuOjpnZXRDb3YoY29yciwgbmFtZXM9YygiZXhlciIsImhhcmR5IiwiZml0Iiwic3RyIiwiaWxsIikpDQojIGFkZCBTRHMgYW5kIGNvbnZlcnQgdG8gZnVsbCBjb3ZhcmlhbmNlIG1hdHJpeA0KY292ZnVsbCA8LSBsYXZhYW46OmNvcjJjb3YoY29ycmZ1bGwsIHNkcz1jKDY2LjUwLCAzOC4wMCwgMzYuODAsIDY3LjAwLCA2Mi40OCkpDQojIG9ic2VydmVkIGNvdmFyaWFuY2UgbWF0cml4DQojY292ZnVsbA0KYGBgDQoNCg0KKipTcGVjaWZ5aW5nIG1vZGVsIGluIGxhdmFhbioqLiBOb3RlIHdlIGNhbiBkZWZpbmUgdGhlIGh5cG90aGVzaXplZCBpbmRpcmVjdCBlZmZlY3RzLiBBIGNvbnZlbmllbnQgd2F5IHRvIGRvIHRoaXMgaXMgdG8gbmFtZSB0aGUgcGFyYW1ldGVycyBhbmQgdGhlbiB1c2UgdGhlc2UgbmFtZXMgdG8gY3JlYXRlIHByb2R1Y3RzLiBIZXJlICJnMSIgYW5kICJnMiIgbWF5IGJlIGNvbnNpZGVyZWQgYXMgJFxnYW1tYSQgY29lZmZpY2llbnRzIGFuZCAiYjEiIGFuZCAiYjIiIG1heSBiZSBjb25zaWRlcmVkIGFzICRcYmV0YSQgY29lZmZpY2llbnRzIGluIHRoZSBvcmlnaW5hbCBMSVNSRUwgbW9kZWwuIEluZGlyZWN0IGVmZmVjdHMgYXJlIGRlZmluZWQgdXNpbmcgdGhlIGA6PWAgb3BlcmF0b3IuDQoNClNwZWNpZnkgdGhlIG1vZGVsOg0KYGBge3IsIGVjaG89VH0NCiMgc3BlY2lmeSB0aGUgcGF0aCBtb2RlbA0Kcm90aCA8LSAnDQpmaXQgfiBnMSpleGVyDQpzdHIgfiBnMipoYXJkeQ0KaWxsIH4gYjEqZml0ICsgYjIqc3RyDQojIGluZGlyZWN0IGVmZmVjdHMNCmV4ZXJfaWxsICA6PSBnMSpiMQ0KaGFyZHlfaWxsIDo9IGcyKmIyDQojIGFsbG93aW5nIHJlc2lkcyBvZiB0d28gcGFyYWxsZWwgbWVkaWF0b3JzIHRvIGNvcnJlbGF0ZQ0KZml0IH5+IHN0ciAnDQpgYGANCg0KUnVubmluZyB0aGUgbW9kZWw6DQpgYGB7ciwgZWNobz1UfQ0KIyBmaXQgdGhlIHBhdGggbW9kZWwNCnJvdGguZml0IDwtIGxhdmFhbjo6c2VtKHJvdGgsIHNhbXBsZS5jb3Y9Y292ZnVsbCwgc2FtcGxlLm5vYnM9MzczLCBvcHRpbS5tZXRob2Q9bGlzdCgiQkZHUyIpKQ0KYGBgDQoNClRoZSBmaXQgb2YgdGhlIG1vZGVsIHRvIHRoZSBkYXRhIGlzIHJlYXNvbmFibGUgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgZml0IGluZGljZXM6IENGSSA9IC45ODAsIFRMSSA9IC45NTUsIFJNU0VBID0gLjA0NiwgYW5kIFNSTVIgPSAuMDM4LiBUaGUgJFxjaGleMiQgPSA3LjEzNSB3aXRoICpkZiogPSA0LCBhbmQgKnAqID0gLjEyOS4gVGhlIHJlc3VsdHMgYXJlIHN1bW1hcml6ZWQgaW4gRmlndXJlIDIgd2hpY2ggcHJlc2VudHMgc3RhbmRhcmRpemVkIGNvZWZmaWNpZW50cy4gT24gdGhlIGxlZnQgd2Ugc2VlIHRoYXQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gaGFyZGluZXNzIGFuZCBleGVyY2lzZSBpcyAtLjAzICh0aGUgYXJyb3cgaXMgZGFzaGVkIHRvIGluZGljYXRlIHRoYXQgdGhpcyB2YWx1ZSB3YXMgbm90IGVzdGltYXRlZCBidXQgd2FzIGdpdmVuIGluIHRoZSBkYXRhLiBUaGUgbW9kZWwgZXhwbGFpbnMgNSUgb2YgdGhlIHZhcmlhbmNlIGluIHN0cmVzcyAobm90ZSB0aGUgcmVzaWR1YWwgYXJyb3cgaXMgMSAtIC4wNSkgYW5kIDE1JSBvZiB0aGUgdmFyaWFuY2UgaW4gZml0dG5lc3MgKHRoZSByZXNpZHVhbCBhcnJvdyBpcyAxIC0gLjE1KS4gVGhlIHVuZXhwbGFpbmVkIHZhcmlhbmNlIGluIGVhY2ggb2YgdGhlc2UgdmFyaWFibGVzIGlzIGFsbG93ZWQgdG8gY29ycmVsYXRlICgtLjEwKSBtZWFuaW5nIHRoYXQgdGhleSBzaGFyZSB0aGUgaW5mbHVlbmNlIG9mIG9taXR0ZWQgdmFyaWFibGVzLiBUaGUgbW9kZWwgZXhwbGFpbnMgYWJvdXQgMTclIG9mIHRoZSB2YXJpYW5jZSBpbiBpbGxuZXNzICgxIC0gLjgzKS4gVGhlIGluZGlyZWN0IGVmZmVjdCBvZiBoYXJkaW5lc3Mgb24gaWxsbmVzcyBpcyBvYnRhaW5lZCBieSBtdWx0aXBseWluZyB0aGUgcGF0aHMgZnJvbSBoYXJkeSB0byBzdHIgYW5kIGZyb20gc3RyIHRvIGlsbCAoLS4yMiB4IC4zMSA9IC0uMDY4LCAqcCogPCAuMDUpLiBUaGUgaW5kaXJlY3QgZWZmZWN0IG9mIGV4ZXJjaXNlIG9uIGlsbG5lc3MgaXMgc2ltaWxhcmx5IG9idGFpbmVkICguMzkgeCAtLjI1ID0gLS4wOTYsICpwKiA8IC4wNSkuDQoNClBsb3R0aW5nIHJlc3VsdHMgYXMgcGF0aCBkaWFncmFtOg0KDQpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5jYXA9IkZpZ3VyZSAyLiBQYXRoIGFuYWx5c2lzIHVzaW5nIG9ic2VydmVkIHZhcmlhYmxlcy4iLCBlY2hvPVQsIGZpZy5hbGlnbj0nY2VudGVyJ30NCiMtLW1ha2luZyBwYXRoIGRpYWdyYW0gb2JzIG9ubHkNCmZpZzIgPC0gc2VtUGxvdDo6c2VtUGF0aHMocm90aC5maXQsIHdoYXRMYWJlbHMgPSAic3RkIiwgbGF5b3V0ID0gInRyZWUyIiwgDQogICAgICAgICByb3RhdGlvbiA9IDIsIHN0eWxlID0gImxpc3JlbCIsIG9wdGltaXplTGF0UmVzID0gVFJVRSwgDQogICAgICAgICBzdHJ1Y3R1cmFsID0gRkFMU0UsIGxheW91dFNwbGl0ID0gRkFMU0UsDQogICAgICAgICBpbnRlcmNlcHRzID0gRkFMU0UsIHJlc2lkdWFscyA9IFQsIA0KICAgICAgICAgY3VydmUgPSAzLCBjdXJ2YXR1cmUgPSAzLCBuQ2hhck5vZGVzID0gOCwgDQogICAgICAgICBzaXplTGF0ID0gNSwgc2l6ZU1hbiA9IDgsIHNpemVNYW4yID0gNiwgDQogICAgICAgICBlZGdlLmxhYmVsLmNleCA9IDAuOSwgbGFiZWwuY2V4PTEuMSwgcmVzaWRTY2FsZT0xMCwgDQogICAgICAgICBlZGdlLmNvbG9yID0gImJsYWNrIiwgZWRnZS5sYWJlbC5wb3NpdGlvbiA9IC40MCwgRG9Ob3RQbG90PVQpDQoNCiMtLXdvcmtpbmcgd2l0aCBzZW1wdG9vbHMgdG8gbW9kaWZ5IHBhdGggZGlhZ3JhbXMNCm15X3Bvc2l0aW9uX2xpc3QgPC0gYygiaGFyZHkgfn4gZXhlciIgPSAuNTAsICJzdHIgfn4gZml0Ij0uNTApDQpmaWcyXzEgPC0gZmlnMiB8PiBzZXRfZWRnZV9sYWJlbF9wb3NpdGlvbihteV9wb3NpdGlvbl9saXN0KSB8Pg0KICBtYXJrX3NpZyhyb3RoLmZpdCwgYWxwaGEgPSBjKCIobi5zLikiID0gMS4wMCwgIioiID0gLjA1KSkgI2FkZGluZyAqLCBucw0KcGxvdChmaWcyXzEpDQpgYGANCg0KYGBge2NzcywgZWNobz1GQUxTRX0NCi5zY3JvbGwtNTAwIHsNCiAgbWF4LWhlaWdodDogNTAwcHg7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIGJhY2tncm91bmQtY29sb3I6IGluaGVyaXQ7DQp9DQpgYGANCg0KRnVsbCBvdXRwdXQ6DQoNCnwgSSB1c2VkIHRoZSBgc3VtbWFyeSgpYCBmdW5jdGlvbiB0byB3cml0ZSBgbGF2YWFuYCByZXN1bHRzIHRvIHRoZSBjb25zb2xlLiBUaGVyZSBpcyBhIGxvdCBvZiBvdXRwdXQgdG8gcmV2aWV3LiBUaGUgZmlyc3QgYml0IGlzIG1vZGVsIGZpdCBzdGF0aXN0aWNzLiBOZXh0IHdlIHNlZSB0aGUgcmVncmVzc2lvbiBtb2RlbHMgaW52b2x2aW5nIHRoZSBvYnNlcnZlZCB2YXJpYWJsZXMuIFRoZSBjb2x1bW4gbGFiZWxlZCAqRXN0aW1hdGUqIGdpdmVzIHRoZSB1bnN0YW5kYXJkaXplZCBjb2VmZmljaWVudHMsIGFuZCB0aGUgY29sdW1uICpTdGQuYWxsKiBnaXZlcyB0aGUgY29tcGxldGVseSBzdGFuZGFyZGl6ZWQgY29lZmZpY2llbnRzLiBUaGUgY292YXJpYW5jZS9jb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSByZXNpZHVhbHMgb2YgdGhlIHR3byByZWdyZXNzaW9ucyBpcyBzaG93biBuZXh0LCBmb2xsb3dlZCBieSBlc3RpbWF0ZWQgdmFyaWFuY2VzLiBWYXJpYWJsZXMgcHJlZml4ZWQgd2l0aCBhICIuIiBpbmRpY2F0ZSByZXNpZHVhbCB0ZXJtcy4gVGhlbiB3ZSBzZWUgdGhlICRSXjIkIHZhbHVlcyBpbmRpY2F0aW5nIHRoZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBpbiBlYWNoIGVuZG9nZW5vdXMgdmFyaWFibGUuIEZpbmFsbHksIHVuZGVyIHRoZSBoZWFkaW5nICJEZWZpbmVkIFBhcmFtZXRlcnMiIHdlIHNlZSB0aGUgaW5kaXJlY3QgZWZmZWN0cyBhcyB3ZSBzcGVjaWZpZWQgaW4gb3VyIG1vZGVsIHN0YXRlbWVudCBhYm92ZS4gDQoNCmBgYHtyLCBjbGFzcy5vdXRwdXQ9InNjcm9sbC01MDAifQ0Kc3VtbWFyeShyb3RoLmZpdCwgZml0Lm1lYXN1cmVzPVQsIHN0YW5kYXJkaXplZD1ULCByc3F1YXJlPVQpDQpgYGANCg0KIyMjIDFiOiBQYXRoIGFuYWx5c2lzIGluY29ycG9yYXRpbmcgbWVhc3VyZW1lbnQgZXJyb3INCkhlcmUgd2UgY2FuIHNwZWNpZnkgdGhlIGFtb3VudCBvZiBtZWFzdXJlbWVudCBlcnJvciB2YXJpYW5jZSBpbiBvdXIgb2JzZXJ2ZWQgdmFyaWFibGVzLiBUaGlzIGludm9sdmVzIDMgc3RlcHM6IGZpcnN0IHdlIGhhdmUgdG8gZGVmaW5lIGEgbGF0ZW50IHZhcmlhYmxlIGZvciBlYWNoIG9mIHRoZSBvYnNlcnZlZCB2YXJpYWJsZXMuIFNlY29uZCwgd2UgcmUtc3BlY2lmeSB0aGUgcmVncmVzc2lvbnMgdXNpbmcgdGhlIGxhdGVudCB2YXJpYWJsZXMuIEZpbmFsbHksIHdlIGZpeCB0aGV0YSB2YWx1ZXMgZXF1YWwgdG8gMTAlIG9mIHRoZSBvYnNlcnZlZCB2YXJpYW5jZXMgaW4gdGhlIGluZGljYXRvcnMuIFRoaXMgaXMgZXF1aXZhbGVudCB0byBzZXR0aW5nIHJlbGlhYmlsaXR5IHRvIC45MCBmb3IgYWxsIG9ic2VydmVkIHZhcmlhYmxlcy4NCg0KU3BlY2lmeSB0aGUgbW9kZWw6DQpgYGB7ciwgZWNobz1UfQ0Kcm90aDIgPC0gJw0KZXhlcmYgID1+IGV4ZXINCmhhcmR5ZiA9fiBoYXJkeQ0KZml0ZiAgID1+IGZpdA0Kc3RyZiAgID1+IHN0cg0KaWxsZiAgID1+IGlsbA0KIyBmYWN0b3IgcmVncw0KZml0ZiB+IGcxKmV4ZXJmDQpzdHJmIH4gZzIqaGFyZHlmDQppbGxmIH4gYjEqZml0ZiArIGIyKnN0cmYNCiNpbmRpcmVjdCBlZmZlY3RzDQpleGVyX2lsbCAgOj0gZzEqYjENCmhhcmR5X2lsbCA6PSBnMipiMg0KIyBzZXR0aW5nIG1lYXN1cmVtZW50IGVycm9yIHZhcmlhbmNlcyB0byAxMCUgb2JzIHZhcmlhbmNlDQpleGVyICB+fiA0MjIuMjI1KmV4ZXINCmhhcmR5IH5+IDE0NC40MDAqaGFyZHkNCmZpdCAgIH5+IDEzNS40MjQqZml0DQpzdHIgICB+fiA0NDguOTAwKnN0cg0KaWxsICAgfn4gMzkwLjM3NSppbGwNCiMgYWxsb3dpbmcgcmVzaWRzIG9mIHR3byBwYXJhbGxlbCBtZWRpYXRvcnMgdG8gY29ycmVsYXRlDQpmaXRmIH5+IHN0cmYgJw0KYGBgDQoNClJ1bm5pbmcgdGhlIG1vZGVsOg0KYGBge3IsIGVjaG89VH0NCiMgZml0IGEgcGF0aCBtb2RlbCB3IG0uZS4NCiMtLWhhZCB0byBjaGFuZ2Ugb3B0aW1pemF0aW9uIG1ldGhvZCB0byAiQkZHUyIgZnJvbSBkZWZhdWx0ICJNTE1JTkIiIGZvciBzaW5nbGUgaW5kaWNhdG9ycyB0byB3b3JrDQpyb3RoMi5maXQgPC0gbGF2YWFuOjpzZW0ocm90aDIsIHNhbXBsZS5jb3Y9Y292ZnVsbCwgc2FtcGxlLm5vYnM9MzczLCBvcHRpbS5tZXRob2Q9bGlzdCgiQkZHUyIpKQ0KYGBgDQoNCg0KVGhlIGZpdCBvZiB0aGUgbW9kZWwgdG8gdGhlIGRhdGEgaXMgcmVhc29uYWJsZSBhY2NvcmRpbmcgdG8gdGhlIGZvbGxvd2luZyBmaXQgaW5kaWNlczogQ0ZJID0gLjk4MCwgVExJID0gLjk0OSwgUk1TRUEgPSAuMDQ2LCBhbmQgU1JNUiA9IC4wMzYuIFRoZSAkXGNoaV4yJCA9IDcuMTYzIHdpdGggKmRmKiA9IDQsIGFuZCAqcCogPSAuMTI4LiBUaGUgcmVzdWx0cyBhcmUgc3VtbWFyaXplZCBpbiBGaWd1cmUgMyB3aGljaCBwcmVzZW50cyBzdGFuZGFyZGl6ZWQgY29lZmZpY2llbnRzLiAqKk5vdGUqKiB0aGF0IHRoZSBzdHJ1Y3R1cmFsIHBhcmFtZXRlcnMgYXJlIGEgYml0IGxhcmdlciB0aGFuIHRoZWlyIGNvdW50ZXJwYXJ0cyBpbiBGaWd1cmUgMiBiZWNhdXNlIHRoZXkgaGF2ZSBiZWVuIGNvcnJlY3RlZCBmb3IgYXR0ZW51YXRpb24gYnkgdGFraW5nIHRoZSByZWxpYWJpbGl0eSBvZiB0aGUgb2JzZXJ2ZWQgdmFyaWFibGVzIGludG8gYWNjb3VudC4gVGhlIHN0YW5kYXJkIGVycm9ycyBmb3IgdGhlc2UgcGFyYW1ldGVycyBhcmUgYWxzbyBpbmNyZWFzZWQgZHVlIHRvIG1lYXN1cmVtZW50IGVycm9yIChjb21wYXJlIGZ1bGwgb3V0cHV0IG9mIHRoZSB0d28gbW9kZWxzKS4gVGhlIGluZGlyZWN0IGVmZmVjdCBvZiBoYXJkaW5lc3Mgb24gaWxsbmVzcyBpcyBvYnRhaW5lZCBieSBtdWx0aXBseWluZyB0aGUgcGF0aHMgZnJvbSBoYXJkeWYgdG8gc3RyZiBhbmQgZnJvbSBzdHJmIHRvIGlsbGYgKC0uMjUgeCAuMzQgPSAtLjA4NSwgKnAqIDwgLjA1KS4gVGhlIGluZGlyZWN0IGVmZmVjdCBvZiBleGVyY2lzZSBvbiBpbGxuZXNzIGlzIHNpbWlsYXJseSBvYnRhaW5lZCAoLjQzIHggLS4yNyA9IC0uMTE2LCAqcCogPCAuMDUpLg0KDQpQbG90dGluZyByZXN1bHRzIGFzIHBhdGggZGlhZ3JhbSB3aXRoIGxhdGVudCBhbmQgb2JzZXJ2ZWQgdmFyaWFibGVzOg0KDQpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5jYXA9IkZpZ3VyZSAzLiBQYXRoIGFuYWx5c2lzIHVzaW5nIHNpbmdsZSBpbmRpY2F0b3IgdmFyaWFibGVzIHdpdGggc3BlY2lmaWVkIG1lYXN1cmVtZW50IGVycm9yIHZhcmlhbmNlIiwgZWNobz1ULCBmaWcuYWxpZ249J2NlbnRlcid9DQojLS1tYWtpbmcgcGF0aCBkaWFncmFtIHcgbS5lLi0tLS0tLS0tLS0tLS0tLS0tLS0tLW1hcihib3QsbGZ0LHRvcCxyZ3QpDQpmaWczIDwtc2VtUGxvdDo6c2VtUGF0aHMocm90aDIuZml0LCB3aGF0TGFiZWxzID0gInN0ZCIsIGxheW91dCA9ICJ0cmVlMiIsIA0KICAgICAgICAgcm90YXRpb24gPSAyLCBzdHlsZSA9ICJsaXNyZWwiLCBvcHRpbWl6ZUxhdFJlcyA9IFRSVUUsIA0KICAgICAgICAgc3RydWN0dXJhbCA9IEZBTFNFLCBsYXlvdXRTcGxpdCA9IEZBTFNFLA0KICAgICAgICAgaW50ZXJjZXB0cyA9IEZBTFNFLCByZXNpZHVhbHMgPSBULCANCiAgICAgICAgIGN1cnZlID0gMywgY3VydmF0dXJlID0gMywgbkNoYXJOb2RlcyA9IDgsIA0KICAgICAgICAgc2l6ZUxhdCA9IDgsIHNpemVNYW4gPSA4LCBzaXplTWFuMiA9IDYsIA0KICAgICAgICAgZWRnZS5sYWJlbC5jZXggPSAwLjksIGxhYmVsLmNleD0xLjEsIHJlc2lkU2NhbGU9MTAsIG1hcj1jKDIsMywyLDMpLCANCiAgICAgICAgIGVkZ2UuY29sb3IgPSAiYmxhY2siLCBlZGdlLmxhYmVsLnBvc2l0aW9uID0gLjQwLCBEb05vdFBsb3Q9VCkNCg0KIy0td29ya2luZyB3aXRoIHNlbXB0b29scyB0byBtb2RpZnkgcGF0aCBkaWFncmFtcw0KIy0tbXkgc3BlY2lmaWNhdGlvbnMtLQ0KbXlfcm90YXRlX3Jlc2lkX2xpc3QgPC0gYyhzdHJmID0gLTEyMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZml0ZiA9IC00NSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgaWxsZiA9ICA0NSkNCm15X2N1cnZlX2xpc3QgPC0gYygic3RyZiB+fiBmaXRmIiA9IDIpDQojLS1tYWtpbmcgbXVsdGlwbGUgYWRqdXN0bWVudHMgdyBwaXBpbmcgfD4NCmZpZzNfMSA8LSBmaWczIHw+IHNldF9jdXJ2ZShteV9jdXJ2ZV9saXN0KSB8Pg0KICByb3RhdGVfcmVzaWQobXlfcm90YXRlX3Jlc2lkX2xpc3QpIHw+DQogIG1hcmtfc2lnKHJvdGgyLmZpdCwgYWxwaGEgPSBjKCIobi5zLikiID0gMS4wMCwgIioiID0gLjA1KSkNCnBsb3QoZmlnM18xKSAgDQpgYGANCg0KRnVsbCBvdXRwdXQ6DQoNCnwgTm90ZSB0aGVyZSBpcyBhIG5ldyBzZWN0aW9uIGxhYmVsZWQgIkxhdGVudCBWYXJpYWJsZXMiIHdoaWNoIHNob3dzIHRoZSBvcGVyYXRvciBgPX5gLiBUaGUgcmVncmVzc2lvbiBlcXVhdGlvbnMgYW5kIGluZGlyZWN0IGVmZmVjdHMgYXJlIG5vdyBzcGVjaWZpZWQgdXNpbmcgdGhlIGxhdGVudCB2YXJpYWJsZXMuIFRoZSAkUl4yJCB2YWx1ZXMgZm9yIHRoZSBvYnNlcnZlZCB2YXJpYWJsZXMgYXJlIGFwcHJveGltYXRlbHkgLjkwIHJlZmxlY3RpbmcgZmFjdCB0aGF0IHdlIGZpeGVkIHRoZWlyIG1lYXN1cmVtZW50IGVycm9yIHZhcmlhbmNlcyB0byAxMCUgb2YgdGhlaXIgb3JpZ2luYWwgdmFyaWFuY2VzLg0KDQpgYGB7ciwgY2xhc3Mub3V0cHV0PSJzY3JvbGwtNTAwIn0NCnN1bW1hcnkocm90aDIuZml0LCBmaXQubWVhc3VyZXM9VCwgc3RhbmRhcmRpemVkPVQsIHJzcXVhcmU9VCkNCmBgYA0KUmVzaWR1YWxzIEFuYWx5c2lzOg0KDQp8IERpc3BsYXlzIHJlc2lkdWFsIGNvcnJlbGF0aW9ucyAob2JzZXJ2ZWQgLSBwcmVkaWN0ZWQpIGFuZCB6LXNjb3JlcyBpbmRpY2F0aW5nIHRoZSBtYWduaXR1ZGUgb2YgdGhlIHJlc2lkdWFscy4gVHlwaWNhbGx5LCB6IHZhbHVlcyA+IHwyLjU0fCBhcmUgaW50ZXJwcmV0ZWQgYXMgc2lnbmlmaWNhbnQgYW5kIGluZGljYXRlIHdoZXJlIHRoZSBtb2RlbCBpcyBkb2luZyBhIHBvb3Igam9iIG9mIHJlcHJvZHVjaW5nIHRoZSBvYnNlcnZlZCBjb3JyZWxhdGlvbnMgKGFuZCB2YXJpYW5jZXMpLiBOb3RlIHRoYXQgZm9yIHRoaXMgZXhhbXBsZSBhbGwgdGhlIHJlc2lkdWFscyBhcmUgc21hbGwgYnV0IG9uZSBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIDAuMCBpbmRpY2F0aW5nIHRoYXQgdGhlIG1vZGVsIGlzIHVuZGVyLWVzdGltYXRpbmcgdGhlIHZhcmlhbmNlIGluIHN0ci4NCmBgYHtyLGNsYXNzLm91dHB1dD0ic2Nyb2xsLTUwMCJ9DQpsYXZSZXNpZHVhbHMocm90aDIuZml0KQ0KYGBgDQoNCnwgDQojIyAyLiBDb25maXJtYXRvcnkgRmFjdG9yIEFuYWx5c2lzDQoNClRoZSAqKlNGLTM2KiogcXVlc3Rpb25uYWlyZSBtZWFzdXJlcyAqKnNlbGYtcmVwb3J0ZWQgZnVuY3Rpb25hbCBzdGF0dXMqKi4gSXRzIDM2IGl0ZW1zIGFyZSBjb21iaW5lZCB0byBmb3JtIDggc3Vic2NhbGVzIHlpZWxkaW5nICp0d28gc3VtbWFyeSBtZWFzdXJlczoqIHBoeXNpY2FsIGFuZCBtZW50YWwgaGVhbHRoLiBUaGUgKnBoeXNpY2FsIGhlYWx0aCBtZWFzdXJlKiBpbmNsdWRlcyBmb3VyIHN1YnNjYWxlczogcGh5c2ljYWwgZnVuY3Rpb25pbmcgKDEwIGl0ZW1zKSwgcm9sZS1waHlzaWNhbCAoNCBpdGVtcyksIGJvZGlseSBwYWluICgyIGl0ZW1zKSwgYW5kIGdlbmVyYWwgaGVhbHRoICg1IGl0ZW1zKS4gVGhlICptZW50YWwgaGVhbHRoIG1lYXN1cmUqIGluY2x1ZGVzIHRoZSBvdGhlciBmb3VyIHN1YnNjYWxlczogdml0YWxpdHkgKDQgaXRlbXMpLCBzb2NpYWwgZnVuY3Rpb25pbmcgKDIgaXRlbXMpLCByb2xlLWVtb3Rpb25hbCAoMyBpdGVtcyksIGFuZCBtZW50YWwgaGVhbHRoICg1IGl0ZW1zKS4NCiANCnwgKipUaGlzIGV4YW1wbGUqKiB1c2VzIGRhdGEgZnJvbSAyNDkgd29tZW4gKGFnZSAzMSB0byA4NywgbWVkaWFuIDY0KSB3aG8gY29tcGxldGVkIHRoZSBTRi0zNiBmb2xsb3dpbmcgaGVhcnQgc3VyZ2VyeS4gVGhlIHN1YnNjYWxlIHZhcmlhYmxlIG5hbWVzIGFyZTogTUVOSExUSCwgU09DRlVOQywgUk9MRUVNTywgVklUQUwsIFBIU0ZVTkMsIFJPTFBIWVMsIEJPRFBBSU4sIGFuZCBHRU5ITFRILiBBIENvbmZpcm1hdG9yeSBGYWN0b3IgQW5hbHlzaXMgd2l0aCAyIGNvcnJlbGF0ZWQgZmFjdG9ycyBpcyBydW4gdXNpbmcgdGhlIGBsYXZhYW5gIHBhY2thZ2UgaW4gd2hpY2ggZWFjaCBzdWJzY2FsZSBsb2FkcyBvbiBvbmx5IG9uZSBoeXBvdGhlc2l6ZWQgZmFjdG9yLiBJJ20gd29ya2luZyB3aXRoIGEgcmF3IGRhdGEgZmlsZSB0aGlzIHRpbWUgc28gSSBjYW4gZGlzcGxheSB0aGUgY292YXJpYW5jZS9jb3JyZWxhdGlvbiBtYXRyaXggZ3JhcGhpY2FsbHkuIEZpZ3VyZSA0IHdhcyBwcm9kdWNlZCBVc2luZyB0aGUgYHBzeWNoYCBwYWNrYWdlLiBJdCBjb21iaW5lcyBjb3JyZWxhdGlvbiB2YWx1ZXMsIHNjYXR0ZXItcGxvdHMsIGFuZCBoaXN0b2dyYW1zLg0KDQojIyMgMi4xIEltcG9ydGluZyBEYXRhOg0KSW4gdGhpcyBleGFtcGxlIEkgYW0gdXNpbmcgdGhlIGByaW9gIHBhY2thZ2UgdG8gaW1wb3J0IGFuIFNQU1MgZGF0YSBmaWxlLiBPbmUgaW5jb252ZW5pZW5jZSB3aGVuIGltcG9ydGluZyBTUFNTIGRhdGEgZmlsZXMgaXMgdGhhdCB0aGUgdmFyaWFibGUgbGFiZWxzIGFyZSByZWNvZ25pemVkIGJ5IHNvbWUgcGFja2FnZXMgYnV0IG5vdCBvdGhlcnMuIEluIHRoaXMgZXhhbXBsZSB0aGUgdmFyaWFibGUgbGFiZWxzIGFjdHVhbGx5IG1lc3MgdXAgdGhlIHRhYmxlcyBhbmQgcGF0aCBkaWFncmFtcyBzbyBJIGRlbGV0ZWQgdGhlbSBmcm9tIHRoZSBkYXRhIGZpbGUuDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBlY2hvPVR9DQojIyBJbXBvcnQgRGF0YQ0KaHJ0c3JnIDwtIHJpbzo6aW1wb3J0KGhlcmUoImRhdGEiLCAiV09NRU5fSFJUU1JHX25vTGFiZWxzLlNBViIpKQ0KYXR0YWNoKGhydHNyZykNCmRhdGEgPC0gZGF0YS5mcmFtZShNRU5ITFRILCBTT0NGVU5DLCBST0xFRU1PLCBWSVRBTCwgICBQSFNGVU5DLCBST0xQSFlTLCBCT0RQQUlOLCBHRU5ITFRIKQ0KDQpgYGANCg0KIyMjIDIuMiBCaXZhcmlhdGUgU3VtbWFyeSBEaXNwbGF5DQpVc2luZyB0aGUgYHBzeWNoYCBwYWNrYWdlIHRvIGdldCB0aGlzIHJpY2ggZmlndXJlLg0KYGBge3IsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTcsIGVjaG89VCwgZmlnLmNhcD0iRmlndXJlIDQuIEJpdmFyaWF0ZSBzdW1tYXJ5IG9mIFNGLTM2IHN1YnNjYWxlcy4gSGlzdG9ncmFtcyBhcmUgb24gdGhlIGRpYWdvbmFsLCBzY2F0dGVyLXBsb3RzIHNob3dpbmcgYml2YXJpYXRlIHJlZ3Jlc3Npb24gbGluZSBhbmQgY2VudHJvaWQgYXJlIGJlbG93IHRoZSBkaWFnbm9uYWwsIGNvcnJlbGF0aW9ucyBhcmUgYWJvdmUgdGhlIGRpYWdvbmFsLiJ9DQpwc3ljaDo6cGFpcnMucGFuZWxzKGRhdGEsIGxtPVQsIGNleC5jb3I9LjksIGppZ2dsZT1ULCBmYWN0b3I9MywNCiAgICAgICAgICAgICAgICAgICAgc2hvdy5wb2ludHMgPSBULCBlbGxpcHNlcz1ULCBwY2g9Ii4iKQ0KYGBgDQoNCiMjIyAyLjMgU3BlY2lmeSAmIHJ1biB0aGUgbW9kZWw6DQoNCmBgYHtyLCBlY2hvPVR9DQojIHNwZWNpZnkgdGhlIDItZmFjdG9yIENGQSBtb2RlbA0KbW9kZWwgPC0gJw0KIyBsYXRlbnQgZmFjdG9ycw0KTUggPX4gIE1FTkhMVEggKyBTT0NGVU5DICsgUk9MRUVNTyArIFZJVEFMDQpQSCA9fiAgUEhTRlVOQyArIFJPTFBIWVMgKyBCT0RQQUlOICsgR0VOSExUSCcNCiMgZml0IHRoZSBtb2RlbA0KZml0IDwtIGxhdmFhbjo6Y2ZhKG1vZGVsID0gbW9kZWwsIGRhdGEgPSBkYXRhLCBtaW1pYyA9ICJsYXZhYW4iLCANCiAgICAgICAgICAgZXN0aW1hdG9yID0gIk1MIiwgbWlzc2luZyA9ICJsaXN0d2lzZSIsIGNoZWNrLnBvc3Q9VCwNCiAgICAgICAgICAgc3RkLmx2ID0gVFJVRSwgc3RkLm92ID0gRkFMU0UsIHRlc3QgPSAic3RhbmRhcmQiLCANCiAgICAgICAgICAgc2UgPSAic3RhbmRhcmQiLCBib290c3RyYXAgPSAxMDAwKQ0KYGBgDQoNClRoZSBmaXQgb2YgdGhlIGh5cG90aGVzaXplZCBtb2RlbCB0byB0aGUgZGF0YSBpcyBub3QgZ3JlYXQgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgZml0IGluZGljZXM6IENGSSA9IC44NzEsIFRMSSA9IC44MTAsIFJNU0VBID0gLjE1MCwgYW5kIFNSTVIgPSAuMDcwLiBUaGUgJFxjaGleMiQgPSAxMjUuMTExIHdpdGggKmRmKiA9IDE5LCBhbmQgKnAqIDwgLjAwMS4gRGVzcGl0ZSB0aGUgcXVlc3Rpb25hYmxlIGZpdCwgd2UgY2FuIGNvbW1lbnQgb24gdGhlIG1vZGVsIHBhcmFtZXRlciBlc3RpbWF0ZXMuIFRoZSByZXN1bHRzIGFyZSBzdW1tYXJpemVkIGluIEZpZ3VyZSA1IHdoaWNoIHByZXNlbnRzIHN0YW5kYXJkaXplZCBjb2VmZmljaWVudHMuIEZpcnN0LCBhbGwgdGhlIGZhY3RvciBsb2FkaW5ncyAoJFxsYW1iZGEkJ3MpIGFyZSBzaWduaWZpY2FudC4gU2Vjb25kLCB0aGUgJFx0aGV0YSQgdmFsdWVzIHNob3cgdGhlIHByb3BvcnRpb24gb2YgbWVhc3VyZW1lbnQgZXJyb3IgdmFyaWFuY2UgaW4gZWFjaCBzdWJzY2FsZSB3aGVuIGNvbnNpZGVyZWQgYXMgYW4gaW5kaWNhdG9yIG9mIHRoZSB1bmRlcmx5aW5nIGxhdGVudCBmYWN0b3IgKFBIIG9yIE1IKS4gTm90ZSB0aGF0IGJlY2F1c2UgYWxsIHBhcmFtZXRlcnMgYXJlIHN0YW5kYXJkaXplZCAkXGxhbWJkYV4yX3tpan0kICsgJFx0aGV0YV97aWl9JCA9IDEuMCBmb3IgYW55IGdpdmVuIHN1YnNjYWxlLiBJbiBnZW5lcmFsLCB0aGUgJFxsYW1iZGFeMl97aWp9JCB2YWx1ZXMgbWF5IGJlIGludGVycHJldGVkIGFzIHJlbGlhYmlsaXR5LCBvciB0aGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSB0aGF0IGFuIGluZGljYXRvciB2YXJpYWJsZSBzaGFyZXMgd2l0aCB0aGUgdW5kZXJseWluZyBsYXRlbnQgZmFjdG9yIGl0IGlzIGh5cG90aGVzaXplZCB0byBtZWFzdXJlLiBGaW5hbGx5LCBub3RpY2UgdGhlIGxhcmdlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGZhY3RvcnMgJFxwaGkkID0gLjg1LCBzdWdnZXN0aW5nIHRoYXQgdGhlIFBIIGFuZCBNSCBjb25zdHJ1Y3RzIG1heSBub3QgYmUgZGlzdGluY3QgZnJvbSBvbmUgYW5vdGhlci4gTW9kaWZpY2F0aW9uIGluZGljZXMgYmVsb3cgb2ZmZXIgc29tZSBpbnNpZ2h0IGludG8gd2h5IHRoZSBoeXBvdGhlc2l6ZWQgQ0ZBIG1vZGVsIGZpdCBpcyBub3QgZ3JlYXQuIA0KDQpQbG90dGluZyBwYXRoIGRpYWdyYW06DQoNCmBgYHtyLCBlY2hvPVQsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTgsIGZpZy5jYXA9IkZpZ3VyZSA1LiBDRkEgc2hvd2luZyBlaWdodCBzdWJzY2FsZXMgb2YgdGhlIFNGLTM2IGxvYWRpbmcgb250byB0aGUgbWVudGFsIGhlYWx0aCBmYWN0b3IgKE1IKSBhbmQgdGhlIHBoeXNpY2FsIGhlYWx0aCBmYWN0b3IgKFBIKSJ9DQpmYWN0b3JzIDwtIGZpdEBwdGEkdm5hbWVzJGx2W1sxXV0NCnNpemUgPC0gLjc1DQpmaWc1IDwtIHNlbVBhdGhzKGZpdCwgbGF0ZW50cyA9IGZhY3RvcnMsIHdoYXRMYWJlbHMgPSAic3RkIiwgbGF5b3V0ID0gInRyZWUyIiwgDQogICAgICAgICByb3RhdGlvbiA9IDIsIHN0eWxlID0gImxpc3JlbCIsIG9wdGltaXplTGF0UmVzID0gVFJVRSwgDQogICAgICAgICBzdHJ1Y3R1cmFsID0gRkFMU0UsIGxheW91dFNwbGl0ID0gRkFMU0UsDQogICAgICAgICBpbnRlcmNlcHRzID0gRkFMU0UsIHJlc2lkdWFscyA9IFQsIA0KICAgICAgICAgY3VydmUgPSAxLCBjdXJ2YXR1cmUgPSAzLCBuQ2hhck5vZGVzID0gOCwgDQogICAgICAgICBzaXplTGF0ID0gMTEgKiBzaXplLCBzaXplTWFuID0gMTEgKiBzaXplLCBzaXplTWFuMiA9IDQgKiBzaXplLCANCiAgICAgICAgIGVkZ2UubGFiZWwuY2V4ID0gMS4yICogc2l6ZSwgcmVzaWRTY2FsZT0xMiAqc2l6ZSwNCiAgICAgICAgIGVkZ2UuY29sb3IgPSAiYmxhY2siLCBlZGdlLmxhYmVsLnBvc2l0aW9uID0gLjQwLCBEb05vdFBsb3Q9VCkNCmZpZzVfMSA8LSBmaWc1IHw+IG1hcmtfc2lnKGZpdCwgYWxwaGEgPSBjKCIobi5zLikiID0gMS4wMCwgIioiID0gLjA1KSkgI2FkZGluZyAqLCBucw0KcGxvdChmaWc1XzEpDQoNCmBgYA0KfCANCkZ1bGwgb3V0cHV0Og0KYGBge3IsIGNsYXNzLm91dHB1dD0ic2Nyb2xsLTUwMCJ9DQpzdW1tYXJ5KGZpdCwgZml0Lm1lYXN1cmVzPVQsIHN0YW5kYXJkaXplZD1ULCByc3F1YXJlPVQpDQpgYGANCg0KfA0KDQojIyMgMi40IE1vZGlmaWNhdGlvbiBJbmRpY2VzDQpUaGUgbW9kaWZpY2F0aW9uIGluZGljZXMgaW5kaWNhdGUgd2hlcmUgYWRkaXRpb25hbCBwYXJhbWV0ZXJzIGNvdWxkIGJlIGFkZGVkIHRvIGltcHJvdmUgdGhlIG1vZGVsIGZpdC4gVGhlIHZhbHVlcyBhcmUgZXN0aW1hdGVzIG9mIGhvdyBtdWNoIHRoZSAkXGNoaV4yJCBzdGF0aXN0aWMgd291bGQgZGVjcmVhc2UgaWYgdGhlIHBhcmFtZXRlciB3ZXJlIHRvIGJlIGVzdGltYXRlZCByYXRoZXIgdGhhbiBmaXhlZCB0byAwLjAuIEZvciBleGFtcGxlLCBhbGxvd2luZyBHRU5ITFRIIHRvIGNyb3NzLWxvYWQgb250byB0aGUgTUggZmFjdG9yIHdvdWxkIHJlc3VsdCBpbiB0aGUgbGFyZ2VzdCBpbXByb3ZlbWVudCBpbiBtb2RlbCBmaXQgKDMyLjUyNykuIEFsbG93aW5nIHRoZSByZXNpZHVhbCB2YXJpYW5jZXMgYmV0d2VlbiBNRU5ITFRIIGFuZCBQSFNGVU5DIHRvIGNvcnJlbGF0ZSB3b3VsZCBhbHNvIHJlc3VsdCBpbiBpbXByb3ZlbWVudCBpbiBmaXQgKDI2LjE3OCkuIFRoZSBhc3N1bXB0aW9ucyBvZiBDRkEgdGhlb3J5IGFyZSB0aGF0IGluZGljYXRvcnMgbG9hZCBvbiBvbmx5IG9uZSBmYWN0b3IgYW5kIHRoYXQgbWVhc3VyZW1lbnQgZXJyb3IgdmFyaWFuY2VzIGFyZSB1bmNvcnJlbGF0ZWQsIHNvIGFkZGluZyB0aGVzZSBwYXJhbWV0ZXJzIHdvdWxkIHZpb2xhdGUgdGhlc2UgYXNzdW1wdGlvbnMuIFdlIGFyZSBsZWZ0IHRvIGNvbmNsdWRlIHRoYXQgdGhlIG5pY2UgY2xlYW4gdHdvLWZhY3RvciBtb2RlbCBpcyBub3Qgc3VwcG9ydGVkOyB0aGUgY29ycmVsYXRpb25hbCBzdHJ1Y3R1cmUgb2YgdGhlIFNGLTM2IHN1YnNjYWxlcyBpcyBtb3JlIGNvbXBsZXggdGhhbiBoeXBvdGhlc2l6ZWQuIA0KDQpgYGB7ciwgY2xhc3Mub3V0cHV0PSJzY3JvbGwtNTAwIn0NCiNzdW1tYXJ5KGZpdCwgZml0Lm1lYXN1cmVzPVQsIHN0YW5kYXJkaXplZD1ULCByc3F1YXJlPVQpDQptb2RpZmljYXRpb25JbmRpY2VzKGZpdCwgc29ydC4gPSBUUlVFLCBtaW5pbXVtLnZhbHVlID0gMykNCmBgYA0KfA0KfA0KIyMgMy4gUGF0aCBBbmFseXNpcyB3aXRoIG11bHRpcGxlLWluZGljYXRvciBsYXRlbnQgdmFyaWFibGVzDQpUaGlzIGV4YW1wbGUgdXNlcyB0aGUgYGxhdmFhbmAgcGFja2FnZSB0byBjb25kdWN0IHN0cnVjdHVyYWwgZXF1YXRpb24gbW9kZWxpbmcgYW5kIHNvbWUgc3VwcG9ydCBwYWNrYWdlcyAoYHNlbVBsb3RgLCBgc2Vtb3V0cHV0YCwgYHNlbVRvb2xzYCwgJiBgc2pQbG90YCkgZm9yIGRpc3BsYXlpbmcgcmVzdWx0cy4gVGhlIG1vZGVsIGJlbG93IGRlYWxzIHdpdGggKiptZWRpYXRpb24qKiBhbW9uZyBsYXRlbnQgdmFyaWFibGVzLiBUaGlzIGV4YW1wbGUgdXNlcyBkYXRhIGZyb20gS2VsbG93YXkncyBib29rICgxOTk4LCBwLjEyMikuIFRoZSBtb2RlbCBwcmVkaWN0cyAqcGVyY2VpdmVkIHdvcmtwbGFjZSByaXNrKiBhbmQgKndpbGxpbmduZXNzIHRvIHBhcnRpY2lwYXRlIGluIG9jY3VwYXRpb25hbCBzYWZldHkgcHJvZ3JhbXMqLiBUaGUgdHdvIHByZWRpY3RvciB2YXJpYWJsZXMgYXJlICpvcmdhbml6YXRpb25hbCBjbGltYXRlIHJlZ2FyZGluZyBzYWZldHkqIGFuZCAqYWNjaWRlbnQgaGlzdG9yeSouIFBlcmNlaXZlZCByaXNrIGlzIGh5cG90aGVzaXplZCBhcyBhIG1lZGlhdGluZyB2YXJpYWJsZSBiZXR3ZWVuIGNsaW1hdGUgYW5kIHBhcnRpY2lwYXRpb24sIGFuZCBiZXR3ZWVuIGFjY2lkZW50IGhpc3RvcnkgYW5kIHBhcnRpY2lwYXRpb24uIFRoZSByZXN1bHRzIGRvICoqbm90Kiogc3VwcG9ydCB0aGUgbWVkaWF0aW9uIGh5cG90aGVzaXMuDQoNCipWYXJpYWJsZXMgaW4gdGhlIG1vZGVsOioNCiAgDQp8IC1FeG9nZW5vdXMgbGF0ZW50IHZhcmlhYmxlczogUGVyY2VpdmVkIFNhZmV0eSBDbGltYXRlIChDTElNQVRFKSwgQWNjaWRlbnQgSGlzdG9yeSAoQUNDSElTVCkNCg0KfCAtRXhvZ2Vub3VzIGluZGljYXRvciB2YXJpYWJsZXM6IHBsYW50LCBzdXBlciAmIGNvdyBhcmUgbWVhc3VyZXMgb2Ygb3JnYW5pemF0aW9uYWwgY2xpbWF0ZSByZWdhcmRpbmcgc2FmZXR5IFtwbGFudCBtYW5hZ2VyIGNvbW1pdG1lbnQgKHBsYW50KSwgc3VwZXJ2aXNvciBjb21taXRtZW50IChzdXBlciksIGNvd29ya2VyIGNvbW1pdG1lbnQgKGNvdyldIG93biBhY2NpZGVudCBoaXN0b3J5IChkaXIpLCB3aXRuZXNzZWQgYWNjaWRlbnRzICh2aWMpDQoNCnwgLUVuZG9nZW5vdXMgbGF0ZW50IHZhcmlhYmxlczogUGVyY2VpdmVkIFJpc2sgaW4gdGhlIFdvcmtwbGFjZSAoUEVSQ1JJU0spLCBXaWxsaW5nbmVzcyB0byBQYXJ0aWNpcGF0ZSBpbiBTYWZldHkgUHJvZ3JhbXMgKFdJTExQQVJUKQ0KDQp8IC1FbmRvZ2Vub3VzIGluZGljYXRvciB2YXJpYWJsZXM6IHBlcmNlaXZlZCByaXNrIHRvIHNlbGYgKHJpc2spLCBwZXJjZWl2ZWQgcmlzayB0byBvdGhlcnMgKHJpc2tjKSBwMSwgcDIsICYgcDMgYXJlIHBhcmNlbHMgb2YgYSBtdWx0aS1pdGVtIHNjYWxlIG1lYXN1cmluZyB3aWxsaW5nbmVzcyB0byBwYXJ0aWNpcGF0ZS4NCnwgDQp8IA0KSGVyZSBpcyBteSBjb2RlIGZvciBzcGVjaWZ5aW5nIHRoZSBtb2RlbCBhbmQgcnVubmluZyBpdCBpbiBsYXZhYW4uIFRoaXMgZXhhbXBsZSBjb2RlIGFsc28gc2hvd3MgaG93IHRvIHNwZWNpZnkgcGFyYW1ldGVyIG5hbWVzIHthMSxhMixiLGMxLGMyfSBhbmQgdXNlIHRoZW0gdG8gZGVmaW5lIHRoZSBpbmRpcmVjdCBhbmQgdG90YWwgZWZmZWN0cy4gVGhlIGluZGlyZWN0IGFuZCB0b3RhbCBlZmZlY3RzIGFyZSBwcmludGVkIGF0IHRoZSBib3R0b20gb2YgdGhlICoqU3VtbWFyeSBPdXRwdXQqKiB1bmRlciBQYXRoIFJlZ3Jlc3Npb25zLiAqVG8gc2VlIHRoZSBpbmRpcmVjdCBhbmQgdG90YWwgZWZmZWN0cyBhcyBkaXNwbGF5ZWQgaW4gdGhlIGNvbnNvbGUsIGNsaWNrIG9uIHRoZSBGdWxsIE91dHB1dCB0YWIgYW5kIHNjcm9sbCB0byB0aGUgYm90dG9tIHNlY3Rpb246ICdEZWZpbmVkIFBhcmFtZXRlcnMnKi4NCg0KUmVhZGluZyBpbiBkYXRhOg0KDQpgYGB7ciwgZWNobz10fQ0KIyBSZWFkaW5nIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29yciA8LSAnDQogMS4wMA0KIC43NiAxLjAwDQogLjc4ICAuNjggIDEuMDANCiAtLjA1ICAuMDEgIC0uMDUgMS4wMA0KIC4xMCAgLjA5ICAgLjA4ICAuNTIgMS4wMA0KIC4wMSAgLjA4ICAgLjE0IC0uMjggLS4yMiAxLjAwDQogLjIwICAuMTggICAuMjkgLS40NCAtLjI5ICAuNTQgMS4wMA0KIC4yMiAgLjI1ICAgLjI5IC0uNDIgLS4zMSAgLjU3ICAuNjUgMS4wMA0KIC4wMDIgLjAwMiAtLjA1ICAuNDIgIC4yMiAtLjEzIC0uMjYgLS4yMCAxLjAwDQogLjEyICAuMDMgICAuMTEgIC4yNiAgLjMwIC0uMTIgLS4yMCAtLjE3ICAuNDcgMS4wMCAnDQoNCiMgYWRkIHZhcmlhYmxlIG5hbWVzIGFuZCBjb252ZXJ0IHRvIGZ1bGwgY29ycmVsYXRpb24gbWF0cml4DQpjb3JyZnVsbCA8LSBnZXRDb3YoY29yciwgbmFtZXM9YygicDEiLCJwMiIsInAzIiwicmlzayIsInJpc2tjIiwicGxhbnQiLCJzdXBlciIsImNvdyIsImRpciIsInZpYyIpKQ0KIyBhZGQgU0RzIGFuZCBjb252ZXJ0IHRvIGZ1bGwgY292YXJpYW5jZSBtYXRyaXgNCmNvdmZ1bGwgPC0gbGF2YWFuOjpjb3IyY292KGNvcnJmdWxsLCBzZHM9YygyLjcsIDMuMSwgMi45LCA3LjMsIDEuNSwgMS40LCAxLjIsIDEuMiwgMS43LCAwLjgpKQ0KDQpgYGANCiMjIDMuMSBDb3JyZWxhdGlvbiBNYXRyaXgNClVzaW5nIGBzalBsb3RgIHRvIGNyZWF0ZSB0aGlzIG5pY2UgdGFibGUuDQpgYGB7cn0NCiMgVXNlcyBzalBsb3QgdG8gcHJpbnQgYSBuaWNlIGxvb2tpbmcgY29ycmVsYXRpb24gdGFibGUNCnNqUGxvdDo6dGFiX2NvcnIoY29ycmZ1bGwsIG5hLmRlbGV0aW9uID0gImxpc3R3aXNlIiwgZGlnaXRzID0gMiwgdHJpYW5nbGUgPSAibG93ZXIiLA0KICAgICAgICAgdGl0bGUgPSAiQ29ycmVsYXRpb25zIGFtb25nIG9ic2VydmVkIHZhcmlhYmxlcyAoTiA9IDExNSkiLA0KICAgICAgICAgc3RyaW5nLmRpYWc9YygnMScsJzEnLCcxJywnMScsJzEnLCcxJywnMScsJzEnLCcxJywnMScpKQ0KYGBgDQoNCnwNCnwNClNwZWNpZnkgJiBydW4gdGhlIG1vZGVsOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0NCiMgc3BlY2lmeSBsYXRlbnQgcGF0aCBtb2RlbCB3IGRpcmVjdCAmIGluZGlyZWN0IGVmZmVjdHMgKHBhcnRpYWxseSBtZWRpYXRlZCBtb2RlbCkNCmtlbGxfcDEgPC0gJw0KIyBkZWZpbmluZyBsYXRlbnQgdmFyaWFibGVzDQogQ0xJTUFURSAgPX4gcGxhbnQgKyBzdXBlciArIGNvdw0KIEFDQ0hJU1QgID1+IGRpciArIHZpYw0KIFBFUkNSSVNLID1+IHJpc2sgKyByaXNrYw0KIFdJTExQQVJUID1+IHAxICsgcDIgKyBwMw0KIyBkZWZpbmluZyBzdHJ1Y3R1YWwgcmVsYXRpb25zIGFuZCBhc3NpZ25pbmcgcGFyYW1ldGVyIG5hbWVzIHthMSxhMixiLGMxLGMyfQ0KIFBFUkNSSVNLIH4gYTEqQ0xJTUFURSArIGEyKkFDQ0hJU1QNCiBXSUxMUEFSVCB+IGIqUEVSQ1JJU0sgKyBjMSpDTElNQVRFICsgYzIqQUNDSElTVA0KIyBkZWZpbmluZyBpbmRpcmVjdCBlZmZlY3RzDQogQ0xJTUFURV9hMWIgOj0gYTEqYg0KIEFDQ0hJU1RfYTJiIDo9IGEyKmINCiMgZGVmaW5pbmcgdG90YWwgZWZmZWN0cw0KIHRvdDFfQ0xJTUFURSA6PSBjMSArIChhMSpiKQ0KIHRvdDJfQUNDSElTVCA6PSBjMiArIChhMipiKSAnDQojIGZpdCBsYXRlbnQgcGF0aCBtb2RlbCB3IGRpcmVjdCAmIGluZGlyZWN0IGVmZmVjdHMNCmtlbGxfcDEuZml0IDwtIGxhdmFhbjo6c2VtKGtlbGxfcDEsIHNhbXBsZS5jb3Y9Y292ZnVsbCwgc3RkLmx2PUYsIHNhbXBsZS5ub2JzPTExNSkNCmBgYA0KDQojIyAzLjIgUmVzdWx0cyBUYWJzIHsudGFic2V0fQ0KDQpNdWx0aXBsZSBwYXJ0cyBvZiBgbGF2YWFuYCBvdXRwdXQgYXJlIGNyZWF0ZWQgYXMgdGFicyB1c2luZyBgc2Vtb3V0cHV0YCBwYWNrYWdlLg0KDQojIyMgU3VtbWFyeSBPdXRwdXQNCnwgDQpUaGUgIlJlZ3Jlc3Npb24gUGF0aHMiIHNlY3Rpb24gbGlzdHMgdGhlIGluZGlyZWN0IGFuZCB0b3RhbCBlZmZlY3RzIGFzIHdlbGwgYXMgdGhlaXIgc2lnbmlmaWNhbmNlIHRlc3RzLiBOb3RlIHRoYXQgYm90aCB0aGUgaW5kaXJlY3QgZWZmZWN0cyBvZiBDTElNQVRFIGFuZCBBQ0NISVNUIG9uIFdJTExQQVJUIGFyZSBub3Qgc2lnbmlmaWNhbnQgYW5kIHRoYXQgb25seSB0aGUgdG90YWwgZWZmZWN0IG9mIENMSU1BVEUgb24gV0lMTFBBUlQgaXMgc2lnbmlmaWNhbnQuIA0KDQpgYGB7ciByZXN1bHRzPSdhc2lzJ30NCnNlbV90YWJsZXMoa2VsbF9wMS5maXQpDQpgYGANCg0KIyMjIERpYWdyYW0gT3V0cHV0IHsuYWN0aXZlfQ0KVGhlIGZpdCBvZiB0aGUgbW9kZWwgdG8gdGhlIGRhdGEgaXMgcmVhc29uYWJsZSBhY2NvcmRpbmcgdG8gdGhlIGZvbGxvd2luZyBmaXQgaW5kaWNlczogQ0ZJID0gLjk5NSwgVExJID0gLjk5MiwgUk1TRUEgPSAuMDI1LCBhbmQgU1JNUiA9IC4wNDkuIFRoZSAkXGNoaV4yJCA9IDMxLjE1OSB3aXRoICpkZiogPSAyOSwgYW5kICpwKiA9IC4zNTguIFRoZSByZXN1bHRzIGFyZSBzdW1tYXJpemVkIGluIEZpZ3VyZSA2IHdoaWNoIHByZXNlbnRzIHN0YW5kYXJkaXplZCBjb2VmZmljaWVudHMuDQoNCnwgDQpUaGUgbW9kZWwgZXhwbGFpbnMgNTMlIG9mIHRoZSB2YXJpYW5jZSBpbiBQRVJDUklTSywgYW5kIDEzJSBvZiB0aGUgdmFyaWFuY2UgaW4gV0lMTFBBUlQuIEFDQ0hJU1QgYW5kIENMSU1BVEUgYXJlIG5lZ2F0aXZlbHkgY29ycmVsYXRlZCBhbmQgdGhleSBoYXZlIHNpZ25pZmljYW50IGFuZCBzaW1pbGFyIGVmZmVjdHMgb24gUEVSQ1JJU0suIENMSU1BVEUgaGFzIGEgc2lnbmlmaWNhbnQgZWZmZWN0IG9uIFdJTExQQVJULiBUaGUgZWZmZWN0IG9mIEFDQ0hJU1Qgb24gV0lMTFBBUlQgaXMgbm90IHNpZ25pZmljYW50LiBUaGVyZSBpcyBubyBzdXBwb3J0IGZvciBQRVJDUklTSyBhcyBhIG1lZGlhdG9yIGFzIGl0cyBlZmZlY3Qgb24gV0lMTFBBUlQgaXMgbm90IHNpZ25pZmljYW50Lg0KDQp8IFRoZSBkYXNoZWQgYXJyb3dzIGlkZW50aWZ5IHRoZSAibWFya2VyIiB2YXJpYWJsZSB0aGF0IGlzIHVzZWQgdG8gZGVmaW5lIHRoZSBtZXRyaWMgb2YgZWFjaCBsYXRlbnQgdmFyaWFibGUuIFRoZXNlIGFyZSBmaXhlZCB0byAxLjAwIGluIHRoZSB1bnN0YW5kYXJkaXplZCBzb2x1dGlvbi4NCg0KDQpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5jYXA9IkZpZ3VyZSA2LiBQYXRoIGFuYWx5c2lzIHdpdGggbXVsdGlwbGUtaW5kaWNhdG9yIGxhdGVudCB2YXJpYWJsZXMuIiwgZWNobz1UfQ0KIy0tbWFraW5nIHBhdGggZGlhZ3JhbSB3IG0uZS4tLS0tLS0tLS0tLS0tLS0tLS0tLS1tYXIoYm90LGxmdCx0b3Ascmd0KQ0KZmlnNiA8LXNlbVBsb3Q6OnNlbVBhdGhzKGtlbGxfcDEuZml0LCB3aGF0TGFiZWxzID0gInN0ZCIsIGxheW91dCA9ICJ0cmVlMiIsIA0KICAgICAgICAgcm90YXRpb24gPSAyLCBzdHlsZSA9ICJsaXNyZWwiLCBvcHRpbWl6ZUxhdFJlcyA9IFRSVUUsIA0KICAgICAgICAgc3RydWN0dXJhbCA9IEZBTFNFLCBsYXlvdXRTcGxpdCA9IEZBTFNFLA0KICAgICAgICAgaW50ZXJjZXB0cyA9IEZBTFNFLCByZXNpZHVhbHMgPSBULCANCiAgICAgICAgIGN1cnZlID0gMywgY3VydmF0dXJlID0gMywgbkNoYXJOb2RlcyA9IDgsIA0KICAgICAgICAgc2l6ZUxhdCA9IDksIHNpemVNYW4gPSA4LCBzaXplTWFuMiA9IDYsIA0KICAgICAgICAgZWRnZS5sYWJlbC5jZXggPSAwLjksIGxhYmVsLmNleD0xLCByZXNpZFNjYWxlPTEwLCBtYXI9YygyLDMsMiwzKSwgDQogICAgICAgICBlZGdlLmNvbG9yID0gImJsYWNrIiwgZWRnZS5sYWJlbC5wb3NpdGlvbiA9IC40MCwgRG9Ob3RQbG90PVQpDQojLS13b3JraW5nIHdpdGggc2VtcHRvb2xzIHRvIG1vZGlmeSBwYXRoIGRpYWdyYW1zDQojLS1tYWtpbmcgYWRqdXN0bWVudHMgdyBwaXBpbmcgfD4NCmZpZzZfMSA8LSBmaWc2IHw+IG1hcmtfc2lnKGtlbGxfcDEuZml0LCBhbHBoYSA9IGMoIihuLnMuKSIgPSAxLjAwLCAiKiIgPSAuMDUpKQ0KcGxvdChmaWc2XzEpICANCmBgYA0KDQoNCiMjIyBGdWxsIE91dHB1dA0KIyMjIyBTdW1tYXJ5DQpVc2luZyBzdW1tYXJ5KCkgZnVuY3Rpb24gdG8gd3JpdGUgbGF2YWFuIHJlc3VsdHMgdG8gdGhlIGNvbnNvbGUuIFRoZXJlIGlzIGEgbG90IG9mIG91dHB1dCB0byByZXZpZXcuIFRoZSBmaXJzdCBiaXQgaXMgbW9kZWwgZml0IHN0YXRpc3RpY3MuIFRoZW4gd2Ugc2VlIHRoZSBsb2FkaW5ncyBvZiB0aGUgb2JzZXJ2ZWQgdmFyaWFibGVzIG9udG8gdGhlaXIgcmVzcGVjdGl2ZSBsYXRlbnQgdmFyaWFibGVzIChmYWN0b3JzKS4gVGhlIGNvbHVtbiBsYWJlbGVkICpFc3RpbWF0ZSogZ2l2ZXMgdGhlIHVuc3RhbmRhcmRpemVkIGNvZWZmaWNpZW50cywgYW5kIHRoZSBjb2x1bW4gKlN0ZC5hbGwqIGdpdmVzIHRoZSBjb21wbGV0ZWx5IHN0YW5kYXJkaXplZCBjb2VmZmljaWVudHMuIE5leHQgd2Ugc2VlIHRoZSByZWdyZXNzaW9uIG1vZGVsIGludm9sdmluZyB0aGUgbGF0ZW50IHZhcmlhYmxlcy4gVGhlIHZhcmlhbmNlcyBhcmUgbGlzdGVkIG5leHQuIE5vdGUgdGhhdCBmb3IgdmFyaWFibGUgbmFtZXMgdGhhdCBzdGFydCB3aXRoIGEgIi4iIHRoZSB2YWx1ZXMgYXJlIGVzdGltYXRlZCByZXNpZHVhbCB2YXJpYW5jZXMuIEZvciB2YXJpYWJsZXMgdGhhdCBkbyBub3QgaGF2ZSBhICIuIiBwcmVmaXggKENMSU1BVEUgJiBBQ0NISVNUKSB0aGUgdmFsdWVzIGFyZSBlc3RpbWF0ZWQgdmFyaWFuY2VzLiBOZXh0IHdlIHNlZSB0aGUgUi1zcXVhcmUgdmFsdWVzIGluZGljYXRpbmcgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGluIHRoZSBvYnNlcnZlZCB2YXJpYWJsZXMgYnkgdGhlIGxhdGVudCB2YXJpYWJsZXMgYW5kIHRoZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBpbiB0aGUgdHdvIGVuZG9nZW5vdXMgZmFjdG9ycyBQRVJDUklTSyAgYW5kIFdJTExQQVJUIGJ5IHRoZSBzdHJ1Y3R1cmFsIG1vZGVsLiBGaW5hbGx5LCB3ZSBzZWUgdGhlIGluZGlyZWN0IGFuZCB0b3RhbCBlZmZlY3RzIGFzIHdlIHNwZWNpZmllZCBpbiBvdXIgbW9kZWwgc3RhdGVtZW50IGFib3ZlLiANCmBgYHtjc3MsIGVjaG89RkFMU0V9DQouc2Nyb2xsLTUwMCB7DQogIG1heC1oZWlnaHQ6IDUwMHB4Ow0KICBvdmVyZmxvdy15OiBhdXRvOw0KICBiYWNrZ3JvdW5kLWNvbG9yOiBpbmhlcml0Ow0KfQ0KYGBgDQoNCmBgYHtyLCBjbGFzcy5vdXRwdXQ9InNjcm9sbC01MDAifQ0Kc3VtbWFyeShrZWxsX3AxLmZpdCwgZml0Lm1lYXN1cmVzID0gVFJVRSwgc3RhbmRhcmRpemVkID0gVFJVRSwgY2k9RiwgcnNxdWFyZT1UKQ0KYGBgDQojIyMgUmVzaWR1YWwgQ29ycmVsYXRpb24gTWF0cml4DQpUaGUgdmFsdWVzIGJlbG93IGFyZSB0aGUgcmVzaWR1YWwgY29ycmVsYXRpb25zIChpLmUuLCBvYnNlcnZlZCBjb3JyZWxhdGlvbiAtIG1vZGVsIGVzdGltYXRlZCBjb3JyZWxhdGlvbikuIFRoZXkgYXJlIHN1bW1hcml6ZWQgYnkgdGhlIFNSTVIgd2hpY2ggaXMgLjA0OS4gDQpgYGB7cn0NCnNlbV9yZXNpZHVhbHMoa2VsbF9wMS5maXQpDQpgYGANCg0KIyMjIFJlc2lkdWFscyBBbmFseXNpcw0KRGlzcGxheXMgcmVzaWR1YWwgY29ycmVsYXRpb25zIGFuZCB6LXNjb3JlcyBpbmRpY2F0aW5nIHRoZSBtYWduaXR1ZGUgb2YgdGhlIHJlc2lkdWFscy4gVHlwaWNhbGx5LCB6IHZhbHVlcyA+IHwyLjU0fCBhcmUgaW50ZXJwcmV0ZWQgYXMgc2lnbmlmaWNhbnQgYW5kIGluZGljYXRlIHdoZXJlIHRoZSBtb2RlbCBpcyBkb2luZyBhIHBvb3Igam9iIG9mIHJlcHJvZHVjaW5nIHRoZSBvYnNlcnZlZCBjb3JyZWxhdGlvbnMuDQoNCmBgYHtyLGNsYXNzLm91dHB1dD0ic2Nyb2xsLTUwMCJ9DQpsYXZSZXNpZHVhbHMoa2VsbF9wMS5maXQpDQpgYGANCg0KIyMjIE1vZGlmaWNhdGlvbiBJbmRpY2VzDQpgYGB7ciwgY2xhc3Mub3V0cHV0PSJzY3JvbGwtNTAwIn0NCm1vZGlmaWNhdGlvbkluZGljZXMoa2VsbF9wMS5maXQsIHNvcnQuID0gVFJVRSwgbWluaW11bS52YWx1ZSA9IDMpDQpgYGANCiMjIyBSZWxpYWJpbGl0eSBBbmFseXNpcw0KVGhlIGBzZW1Ub29sc2AgcGFja2FnZSBoYXMgYSBmdW5jdGlvbiBgY29tcFJlbFNFTWAgdGhhdCByZWFkcyB0aGUgYGxhdmFhbmAgb3V0cHV0IGFuZCByZXR1cm5zIHRoZSByZWxpYWJpbGl0eSBlc3RpbWF0ZXMgZm9yIGVhY2ggbGF0ZW50IHZhcmlhYmxlLiBTZXR0aW5nIHRoZSBvcHRpb25zICd0YXUuZXE9VCcgYW5kICdvYnMudmFyPVQnIHByb2R1Y2VzIGVxdWl2YWxlbnQgdG8gQ3JvbmJhY2gncyByZWxpYWJpbGl0eSBjb2VmZmljaWVudCBhbHBoYS4gU2V0dGluZyAndGF1LmVxPUYnIHByb2R1Y2VzIHRoZSBvbWVnYSBjb2VmZmljaWVudCBvZiByZWxpYWJpbGl0eS4NCmBgYHtyLCByZXN1bHRzPSdob2xkJ30NCnJlbGVzdHM8LXNlbVRvb2xzOjpjb21wUmVsU0VNKGtlbGxfcDEuZml0LCB0YXUuZXE9VCwgb2JzLnZhcj1ULCByZXR1cm4udG90YWw9VCkNCmBgYA0KfCBUaGUgcmVsaWFiaWxpdHkgb2YgQ0xJTUFURSBpcyBgciByZWxlc3RzIFsxXWAuDQp8IFRoZSByZWxpYWJpbGl0eSBvZiBBQ0NISVNUIGlzIGByIHJlbGVzdHMgWzJdYC4NCnwgVGhlIHJlbGlhYmlsaXR5IG9mIFBFUkNSSVNLIGlzIGByIHJlbGVzdHMgWzNdYC4NCnwgVGhlIHJlbGlhYmlsaXR5IG9mIFdJTExQQVJUIGlzIGByIHJlbGVzdHMgWzRdYC4NCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCnwgDQp8IA0KfCANCnwgDQojIyBDb25jbHVkaW5nIFJlbWFya3MNClNvLCB0aGVzZSBleGFtcGxlcyBhcmUgc29tZSBvZiB0aGUgY29vbCBhbmFseXNlcyB5b3UgY2FuIGRvIGluIGBsYXZhYW5gLiBPdGhlciBleGFtcGxlcyBpbmNsdWRlIG11bHRpcGxlLWdyb3VwIG1vZGVscyBhbmQgbGF0ZW50IGdyb3d0aC1jdXJ2ZSBtb2RlbHMuIFRoZXJlIGFyZSB2YXJpb3VzIFIgcGFja2FnZXMgZm9yIGNvbnZlcnRpbmcgYGxhdmFhbmAgb3V0cHV0IGludG8gdGFibGVzIGFuZCBwYXRoIGRpYWdyYW1zIGluIFJtYXJrZG93bjogYHNlbVRvb2xzYCwgYHNlbW91dHB1dGAsIGBzZW1QbG90YCwgYHNqUGxvdGAsIGBwc3ljaGAsIGBHUEFyb3RhdGlvbmAsIGBwc3ljaFRvb2xzYCwgYW5kIGBzZW1wdG9vbHNgLiBTb21lIG9mIHRoZXNlIHBhY2thZ2VzIHByb2R1Y2UgaHRtbCBkb2N1bWVudHMsIG90aGVycyBwcm9kdWNlIFdvcmQgYW5kIFBERiBkb2N1bWVudHMuIFdoZW4gcmVuZGVyaW5nIFJtYXJrZG93biBmaWxlcyB0byBXb3JkIGRvY3VtZW50cywgdGhlcmUgZG9lcyBub3Qgc2VlbSB0byBiZSBhIGVhc3kgd2F5IHRvIGluY2x1ZGUgZXF1YXRpb24gbnVtYmVycy4gQWxzbywgYWRkaW5nIHBhcnRpdGlvbiBsaW5lcyBpbiBhIG1hdHJpeCBzZWVtcyB0byB3b3JrIGZvciBodG1sIGRvY3VtZW50cyBidXQgbm90IGZvciBXb3JkIGRvY3VtZW50cy4gDQoNCg==