Example data: 635 older adults (age 80-100) self-reporting on 7 items assessing the Instrumental Activities of Daily Living (IADL) as follows:

  1. Housework (cleaning and laundry): 1=64%
  2. Bedmaking: 1=84%
  3. Cooking: 1=77%
  4. Everyday shopping: 1=66%
  5. Getting to places outside of walking distance: 1=65%
  6. Handling banking and other business: 1=73%
  7. Using the telephone 1=94%

Two versions of a response format were available:

Binary -> 0 = “needs help”, 1 = “does not need help”

Categorical -> 0 = “can’t do it”, 1=”big problems”, 2=”some problems”, 3=”no problems”

Higher scores indicate greater function. We will look at each response format in turn.

Package Installation and Loading

if (require(lavaan) == FALSE){
  install.packages("lavaan")
}
library(lavaan)
if (require(mirt) == FALSE){
  install.packages("mirt")
}
library(mirt)

Data Import into R

The data are in a text file named adl.dat orignally used in Mplus (so no column names were included at the top of the file). The file contains more items than we will use, so we select only items above from the whole file.

#read in data file (Mplus file format so having to label columns)
adlData = read.table(file = "adl.dat", header = FALSE, na.strings = ".", col.names = c("case", paste0("dpa", 1:14), paste0("dia", 1:7), paste0("cpa", 1:14), paste0("cia", 1:7)))
#select Situations items and PersonID variables
iadlDataInit = adlData[c(paste0("dia", 1:7))]
#remove cases with all items missing
removeCases = which(apply(X = iadlDataInit, MARGIN = 1, FUN = function (x){ length(which(is.na(x)))}) == 7)
iadlData = iadlDataInit[-removeCases,]

Estimation with Marginal Maximum Likelihood

We will introduce the mirt package as a method for estimating IRT models. Overall, the package is very good, but typically is used for scaling purposes (measurement rather than use of latent variables in additional model equations). We use the package to demonstrate estimating IRT models using marginal maximum likelihood. If you wish to use the latent trait estimates in secondary analyses (which you would otherwise use SEM for simultaneously), there are additional steps to take to ensure the error associated with each score is carried over to the subsequent analysese

When all the items of the model are the same type, the mirt syntax is very short. The mirt() function is used to provide estimates, with options model=1 for all items measuring the same trait and itemtype="2PL" for the two-parameter logistic model ("Rasch" is used for the 1PL shorthand). The "Rasch" designation estimates a model where the loadings are all set to one and the factor/latent trait variance is estimated) – which is an equivalent model to the one estimated below but we seek to keep the latent trait standardized. We will estimate both simultaneously here:

mirt1PLsyntax = "
IADL = 1-7
CONSTRAIN = (1-7, a1)
COV = 1
"
model1PLmirt = mirt(data = iadlData, model = mirt1PLsyntax)

Iteration: 1, Log-Lik: -1878.487, Max-Change: 1.89501
Iteration: 2, Log-Lik: -1577.569, Max-Change: 1.18305
Iteration: 3, Log-Lik: -1505.605, Max-Change: 0.58836
Iteration: 4, Log-Lik: -1483.057, Max-Change: 0.34308
Iteration: 5, Log-Lik: -1474.066, Max-Change: 0.23870
Iteration: 6, Log-Lik: -1469.990, Max-Change: 0.16868
Iteration: 7, Log-Lik: -1466.436, Max-Change: 0.06185
Iteration: 8, Log-Lik: -1465.995, Max-Change: 0.04630
Iteration: 9, Log-Lik: -1465.692, Max-Change: 0.03439
Iteration: 10, Log-Lik: -1465.184, Max-Change: 0.01246
Iteration: 11, Log-Lik: -1465.079, Max-Change: 0.01223
Iteration: 12, Log-Lik: -1464.992, Max-Change: 0.01176
Iteration: 13, Log-Lik: -1464.662, Max-Change: 0.00863
Iteration: 14, Log-Lik: -1464.646, Max-Change: 0.00732
Iteration: 15, Log-Lik: -1464.632, Max-Change: 0.00642
Iteration: 16, Log-Lik: -1464.579, Max-Change: 0.00288
Iteration: 17, Log-Lik: -1464.576, Max-Change: 0.00252
Iteration: 18, Log-Lik: -1464.574, Max-Change: 0.00216
Iteration: 19, Log-Lik: -1464.569, Max-Change: 0.00417
Iteration: 20, Log-Lik: -1464.568, Max-Change: 0.00277
Iteration: 21, Log-Lik: -1464.567, Max-Change: 0.00138
Iteration: 22, Log-Lik: -1464.566, Max-Change: 0.00109
Iteration: 23, Log-Lik: -1464.566, Max-Change: 0.00161
Iteration: 24, Log-Lik: -1464.565, Max-Change: 0.00118
Iteration: 25, Log-Lik: -1464.565, Max-Change: 0.00168
Iteration: 26, Log-Lik: -1464.564, Max-Change: 0.00142
Iteration: 27, Log-Lik: -1464.564, Max-Change: 0.00099
Iteration: 28, Log-Lik: -1464.564, Max-Change: 0.00104
Iteration: 29, Log-Lik: -1464.563, Max-Change: 0.00102
Iteration: 30, Log-Lik: -1464.563, Max-Change: 0.00086
Iteration: 31, Log-Lik: -1464.563, Max-Change: 0.00082
Iteration: 32, Log-Lik: -1464.563, Max-Change: 0.00074
Iteration: 33, Log-Lik: -1464.563, Max-Change: 0.00070
Iteration: 34, Log-Lik: -1464.563, Max-Change: 0.00076
Iteration: 35, Log-Lik: -1464.563, Max-Change: 0.00065
Iteration: 36, Log-Lik: -1464.563, Max-Change: 0.00074
Iteration: 37, Log-Lik: -1464.563, Max-Change: 0.00051
Iteration: 38, Log-Lik: -1464.563, Max-Change: 0.00065
Iteration: 39, Log-Lik: -1464.563, Max-Change: 0.00048
Iteration: 40, Log-Lik: -1464.563, Max-Change: 0.00049
Iteration: 41, Log-Lik: -1464.563, Max-Change: 0.00039
Iteration: 42, Log-Lik: -1464.563, Max-Change: 0.00031
Iteration: 43, Log-Lik: -1464.563, Max-Change: 0.00010
Iteration: 44, Log-Lik: -1464.563, Max-Change: 0.00009
model2PLmirt = mirt(data = iadlData, model = 1, itemtype = "2PL")

Iteration: 1, Log-Lik: -1878.487, Max-Change: 1.36006
Iteration: 2, Log-Lik: -1572.925, Max-Change: 1.11015
Iteration: 3, Log-Lik: -1499.005, Max-Change: 0.91115
Iteration: 4, Log-Lik: -1474.055, Max-Change: 0.72471
Iteration: 5, Log-Lik: -1464.621, Max-Change: 0.54473
Iteration: 6, Log-Lik: -1460.408, Max-Change: 0.40623
Iteration: 7, Log-Lik: -1456.776, Max-Change: 0.16294
Iteration: 8, Log-Lik: -1456.384, Max-Change: 0.13300
Iteration: 9, Log-Lik: -1456.118, Max-Change: 0.10161
Iteration: 10, Log-Lik: -1455.651, Max-Change: 0.03754
Iteration: 11, Log-Lik: -1455.561, Max-Change: 0.02954
Iteration: 12, Log-Lik: -1455.486, Max-Change: 0.02638
Iteration: 13, Log-Lik: -1455.201, Max-Change: 0.01568
Iteration: 14, Log-Lik: -1455.180, Max-Change: 0.01315
Iteration: 15, Log-Lik: -1455.164, Max-Change: 0.01114
Iteration: 16, Log-Lik: -1455.117, Max-Change: 0.00739
Iteration: 17, Log-Lik: -1455.110, Max-Change: 0.00634
Iteration: 18, Log-Lik: -1455.105, Max-Change: 0.00517
Iteration: 19, Log-Lik: -1455.092, Max-Change: 0.00484
Iteration: 20, Log-Lik: -1455.089, Max-Change: 0.00409
Iteration: 21, Log-Lik: -1455.087, Max-Change: 0.00363
Iteration: 22, Log-Lik: -1455.084, Max-Change: 0.00367
Iteration: 23, Log-Lik: -1455.082, Max-Change: 0.00288
Iteration: 24, Log-Lik: -1455.081, Max-Change: 0.00267
Iteration: 25, Log-Lik: -1455.078, Max-Change: 0.00262
Iteration: 26, Log-Lik: -1455.078, Max-Change: 0.00195
Iteration: 27, Log-Lik: -1455.077, Max-Change: 0.00180
Iteration: 28, Log-Lik: -1455.075, Max-Change: 0.00088
Iteration: 29, Log-Lik: -1455.075, Max-Change: 0.00063
Iteration: 30, Log-Lik: -1455.075, Max-Change: 0.00065
Iteration: 31, Log-Lik: -1455.074, Max-Change: 0.00024
Iteration: 32, Log-Lik: -1455.074, Max-Change: 0.00014
Iteration: 33, Log-Lik: -1455.074, Max-Change: 0.00013
Iteration: 34, Log-Lik: -1455.074, Max-Change: 0.00073
Iteration: 35, Log-Lik: -1455.074, Max-Change: 0.00016
Iteration: 36, Log-Lik: -1455.074, Max-Change: 0.00048
Iteration: 37, Log-Lik: -1455.074, Max-Change: 0.00010

Unlike lavaan, mirt does not provide a nice formatting of parameters with the summary statment. Rather, we get parts of estimates through various pieces.

The model log-likelihood and summary information is given by the show() function:

show(model1PLmirt)

Call:
mirt(data = iadlData, model = mirt1PLsyntax)

Full-information item factor analysis with 1 factor(s).
Converged within 1e-04 tolerance after 44 EM iterations.
mirt version: 1.25 
M-step optimizer: BFGS 
EM acceleration: Ramsay 
Number of rectangular quadrature: 61

Log-likelihood = -1464.563
Estimated parameters: 8 
AIC = 2945.125; AICc = 2945.355
BIC = 2980.754; SABIC = 2955.355
show(model2PLmirt)

Call:
mirt(data = iadlData, model = 1, itemtype = "2PL")

Full-information item factor analysis with 1 factor(s).
Converged within 1e-04 tolerance after 37 EM iterations.
mirt version: 1.25 
M-step optimizer: BFGS 
EM acceleration: Ramsay 
Number of rectangular quadrature: 61

Log-likelihood = -1455.074
Estimated parameters: 14 
AIC = 2938.149; AICc = 2938.826
BIC = 3000.499; SABIC = 2956.051

Also note that the model log-likelihood information does not include a test of the model against an alternative, as does a typical CFA analysis in comparing the model fit of your model to one where all parameters were estimated. This is because the saturated model in IRT is different (for models where all items are binary, it is Multivariate Bernoulli) in that the statistics of interest come in the form of the proportion of people with a given response pattern.

To see estimates, use the coef() function. Here are the estimates for the 1PL model:

coef1PL = coef(model1PLmirt)
coef1PL
$dia1
      a1     d g u
par 4.39 1.637 0 1

$dia2
      a1     d g u
par 4.39 4.651 0 1

$dia3
      a1     d g u
par 4.39 3.509 0 1

$dia4
      a1     d g u
par 4.39 1.908 0 1

$dia5
      a1     d g u
par 4.39 1.881 0 1

$dia6
      a1     d g u
par 4.39 2.988 0 1

$dia7
      a1     d g u
par 4.39 7.467 0 1

$GroupPars
    MEAN_1 COV_11
par      0      1

The coef() function returns an R list of the parameters for each item along with the structural model parameters (the $GroupPars element), which shows the mean and variance of the latent variable. For each item, there are at least four parameters listed:

Note how the item discrimination (the a1 term) is equal for all items – this is done by convention in the 1PL model.

Putting the parameters into equation form, we have a slope/intercept form of the IRT model:

\[P(Y_{si} = 1 | \theta_s) = g_i + (u_i-g_i)\frac{\exp\left(d_i + a1_i \theta_s \right)}{1+\exp\left(d_i + a1_i \theta_s \right)}\]

Another commonly used parameterization of the IRT model is called discrimination/difficulty, given by:

\[P(Y_{si} = 1 | \theta_s) = g_i + (u_i-g_i)\frac{\exp\left(a1_i \left( \theta_s - b_i \right) \right)}{1+\exp\left(a1_i \left( \theta_s - b_i \right) \right)}\]

The two parameterizations are equivalent and one can be found by re-arranging terms of the other. To get the item difficulty from the slope/intercept parameterization:

\[b_i = -\frac{d_i}{a1_i}\]

For our results, we can use the lapply function to add the item difficulties:

getDifficulty = function(itemPar){
  parnames = colnames(itemPar)
  if ("a1" %in% parnames){
    itemPar = c(itemPar, -1*itemPar[2]/itemPar[1])
    names(itemPar) = c(parnames, "b")
    return(itemPar)
  } else {
    return(itemPar)
  }
}
lapply(X = coef1PL, FUN = getDifficulty)
$dia1
        a1          d          g          u          b 
 4.3895702  1.6372853  0.0000000  1.0000000 -0.3729944 

$dia2
       a1         d         g         u         b 
 4.389570  4.651017  0.000000  1.000000 -1.059561 

$dia3
        a1          d          g          u          b 
 4.3895702  3.5086672  0.0000000  1.0000000 -0.7993191 

$dia4
        a1          d          g          u          b 
 4.3895702  1.9084551  0.0000000  1.0000000 -0.4347704 

$dia5
        a1          d          g          u          b 
 4.3895702  1.8810667  0.0000000  1.0000000 -0.4285309 

$dia6
        a1          d          g          u          b 
 4.3895702  2.9876548  0.0000000  1.0000000 -0.6806258 

$dia7
       a1         d         g         u         b 
 4.389570  7.466592  0.000000  1.000000 -1.700985 

$GroupPars
    MEAN_1 COV_11
par      0      1
itemPar = coef1PL[[1]]

For the 2PL, we can use a similar method (here condensed to display the item difficulties):

coef2PL = lapply(X = coef(model2PLmirt), FUN = getDifficulty)
coef2PL
$dia1
       a1         d         g         u         b 
 4.373935  1.606529  0.000000  1.000000 -0.367296 

$dia2
       a1         d         g         u         b 
 5.058002  5.227547  0.000000  1.000000 -1.033520 

$dia3
        a1          d          g          u          b 
 4.3647262  3.4551388  0.0000000  1.0000000 -0.7916049 

$dia4
       a1         d         g         u         b 
 7.197112  2.944432  0.000000  1.000000 -0.409113 

$dia5
       a1         d         g         u         b 
 4.273968  1.807076  0.000000  1.000000 -0.422810 

$dia6
        a1          d          g          u          b 
 3.4634209  2.4201714  0.0000000  1.0000000 -0.6987806 

$dia7
       a1         d         g         u         b 
 3.303801  5.952140  0.000000  1.000000 -1.801604 

$GroupPars
    MEAN_1 COV_11
par      0      1

As the 1PL is nested within the 2PL, we can use a likelihood ratio test to see which model is preferred. The LRT tests the null hypothesis that all item discriminations are equal against an alternative that not all are equal:

anova(model1PLmirt, model2PLmirt)

Model 1: mirt(data = iadlData, model = mirt1PLsyntax)
Model 2: mirt(data = iadlData, model = 1, itemtype = "2PL")

       AIC     AICc    SABIC      BIC    logLik     X2  df     p
1 2945.125 2945.355 2955.355 2980.754 -1464.563    NaN NaN   NaN
2 2938.149 2938.826 2956.051 3000.499 -1455.074 18.977   6 0.004

Here, the test statistic was \(\chi_6 = 18.977\) with a p-value of .004. Therefore, we reject the null hypothesis of equal slopes and conclude the 2PL fits better than the 1PL model.

The LRT, however, assumes both models have a sufficient level of absolute fit to the data. One way to tell is the use of the M2() function, which provides model fit to the 2-way tables (think item-pair covariances). Because our data has some missing responses, we have to use the impute=10 option, imputing 10 values per missing response. Here is the value for the 1PL:

M2(obj = model1PLmirt, impute = 10)
M2(obj = model2PLmirt, impute=10)

The statistics given from the M2 function are similar to those used in CFA–these show approximate model fit indices such as RMSEA, SRMR, TLI, and CFI. From these, it appears the model fits approximately (CFI and TLI near 1 but relatively poor RMSEA). To find misfitting “residuals” we need complete data and the function M2() and the imputeMissing() functions are not working. So, here is an example with complete data and the 2PL:

model2PLmirtB = mirt(data = iadlData[complete.cases(iadlData),], model = 1, itemtype = "2PL")

Iteration: 1, Log-Lik: -1817.248, Max-Change: 1.46767
Iteration: 2, Log-Lik: -1517.948, Max-Change: 1.03243
Iteration: 3, Log-Lik: -1447.274, Max-Change: 1.00582
Iteration: 4, Log-Lik: -1424.072, Max-Change: 0.74506
Iteration: 5, Log-Lik: -1415.121, Max-Change: 0.55113
Iteration: 6, Log-Lik: -1411.156, Max-Change: 0.39934
Iteration: 7, Log-Lik: -1407.784, Max-Change: 0.17155
Iteration: 8, Log-Lik: -1407.439, Max-Change: 0.13233
Iteration: 9, Log-Lik: -1407.206, Max-Change: 0.10162
Iteration: 10, Log-Lik: -1406.798, Max-Change: 0.03483
Iteration: 11, Log-Lik: -1406.725, Max-Change: 0.02867
Iteration: 12, Log-Lik: -1406.663, Max-Change: 0.02278
Iteration: 13, Log-Lik: -1406.425, Max-Change: 0.01418
Iteration: 14, Log-Lik: -1406.410, Max-Change: 0.01160
Iteration: 15, Log-Lik: -1406.397, Max-Change: 0.00953
Iteration: 16, Log-Lik: -1406.352, Max-Change: 0.00510
Iteration: 17, Log-Lik: -1406.348, Max-Change: 0.00443
Iteration: 18, Log-Lik: -1406.344, Max-Change: 0.00389
Iteration: 19, Log-Lik: -1406.331, Max-Change: 0.00247
Iteration: 20, Log-Lik: -1406.330, Max-Change: 0.00214
Iteration: 21, Log-Lik: -1406.330, Max-Change: 0.00209
Iteration: 22, Log-Lik: -1406.327, Max-Change: 0.00080
Iteration: 23, Log-Lik: -1406.327, Max-Change: 0.00070
Iteration: 24, Log-Lik: -1406.327, Max-Change: 0.00071
Iteration: 25, Log-Lik: -1406.326, Max-Change: 0.00055
Iteration: 26, Log-Lik: -1406.326, Max-Change: 0.00021
Iteration: 27, Log-Lik: -1406.326, Max-Change: 0.00013
Iteration: 28, Log-Lik: -1406.326, Max-Change: 0.00075
Iteration: 29, Log-Lik: -1406.326, Max-Change: 0.00013
Iteration: 30, Log-Lik: -1406.326, Max-Change: 0.00060
Iteration: 31, Log-Lik: -1406.326, Max-Change: 0.00053
Iteration: 32, Log-Lik: -1406.326, Max-Change: 0.00019
Iteration: 33, Log-Lik: -1406.326, Max-Change: 0.00053
Iteration: 34, Log-Lik: -1406.326, Max-Change: 0.00015
Iteration: 35, Log-Lik: -1406.326, Max-Change: 0.00037
Iteration: 36, Log-Lik: -1406.326, Max-Change: 0.00017
Iteration: 37, Log-Lik: -1406.326, Max-Change: 0.00015
Iteration: 38, Log-Lik: -1406.326, Max-Change: 0.00041
Iteration: 39, Log-Lik: -1406.326, Max-Change: 0.00038
Iteration: 40, Log-Lik: -1406.326, Max-Change: 0.00015
Iteration: 41, Log-Lik: -1406.326, Max-Change: 0.00038
Iteration: 42, Log-Lik: -1406.326, Max-Change: 0.00008
M2(obj = model2PLmirtB)
M2(obj = model2PLmirtB, residmat = TRUE)
            dia1        dia2         dia3       dia4        dia5       dia6 dia7
dia1          NA          NA           NA         NA          NA         NA   NA
dia2  0.02663977          NA           NA         NA          NA         NA   NA
dia3  0.06240477  0.06313870           NA         NA          NA         NA   NA
dia4 -0.02758525 -0.02668132 -0.038895907         NA          NA         NA   NA
dia5 -0.02215675 -0.01360095 -0.080263677 0.04226633          NA         NA   NA
dia6 -0.03183329 -0.04414976 -0.029552955 0.02706127  0.03628025         NA   NA
dia7 -0.02704504 -0.01200917  0.007826515 0.01542917 -0.00691414 0.02537108   NA

Here we see the biggest descripancy of residual covariances is that for dia5 with dia3 at -.08.

Finally, we can see plots of our model (all shown for the 2PL model). First, the item characteristic curves

plot(model2PLmirt, item=1, type = "trace", theta_lim = c(-3,3))

Next we can see the test information plot:

plot(model2PLmirt, type = "info", theta_lim = c(-3,3))

We can see that our test information peaks around a theta of -.5, meaning scores near -.5 will be the most reliable.

Finally, we can use the fscores() function to get the estimated trait scores. Note, there are several types of scores available. The standard used for score reporting is method = "EAP", which are scores that use the expected value of the posterior distribution of the score. For doing secondary analyses, multiple “plausible” scores should be used with the option plausible.draws = # where # is the number of scores to draw. We plot the density of the test scores following estimating them with fscores():

theta = fscores(object = model2PLmirt, method = "EAP")
hist(theta)

The plot shows a number of people with scores at the maximum value – but very few around the most reliable portion of the test, -.5

Here, we will plot the scores along with the standard error of the scores to show the relationship:

theta2 = fscores(object = model2PLmirt, method = "EAP", full.scores.SE = TRUE)
plot(x = theta2[complete.cases(iadlData),1], y = theta2[complete.cases(iadlData),2], xlab = expression(theta), ylab = "SE", main = "Complete Data Theta vs. SE(Theta)")

For the cases with complete data, this is the best the SE gets. When plotting all the data, you can see the impact of missing data on SE: fewer observations means a higher SE for the trait.

plot(x = theta2[,1], y = theta2[,2], xlab = expression(theta), ylab = "SE", main = "All Data Theta vs. SE(Theta)")

We can also plot the item difficulty values to get a sense of the the scale is telling us about the trait:

itemDif = unlist(lapply(X = coef2PL, FUN = function(x) return(x[5])))
plot(x = 1:7, y = itemDif[1:7], type = "l", xlab = "Item", ylab = "Item Difficulty (b)")

Here are the items, again:

  1. Housework (cleaning and laundry): 1=64%
  2. Bedmaking: 1=84%
  3. Cooking: 1=77%
  4. Everyday shopping: 1=66%
  5. Getting to places outside of walking distance: 1=65%
  6. Handling banking and other business: 1=73%
  7. Using the telephone 1=94%

Note that no items are available to measure above-average abilities well! The item difficulty for most items covers values of Theta between −1.0 to −0.5.

Estimation in lavaan: Limited Information Only

lavaan only provides limited information estimates of IRT/IFA models, which are parallel (but not necessarily equal to) Mplus’ WLSMV methods. This is a limitation of lavaan, so if ML versions of estimates are needed, Mplus will have to be used. Alternatively, the mirt package in R estimates IRT models (and many other types), but has rather limited capabilities for SEM with the models used and does not include model functions for continous observed variables and provides very few SEM fit statistics.

We will compare the one-parameter vs. two-parameter models for Binary Responses using WLSMV Probit model. Beginning with the two-parameter model.

Two-Parameter Model

The two-parameter model is called the two-parameter logisitic model if the logit link function is used (2PL), otherwise it is called the two-parameter normal ogive (2PNO). The syntax is largely identical to that use for CFA, however, item intercepts (item ~ 1 in CFA) are now replaced by item thresholds (item | t# where # is the number of the threshold, in order, from 1 through the number of categories on the item minus one).

The normal ogive model provides a model for a categorical variable’s (\(Y\) “underlying” continuous analog).

model2PSyntax = "
# loadings/discrimination parameters:
IADL =~ dia1 + dia2 + dia3 + dia4 + dia5 + dia6 + dia7
# threshholds use the | operator and start at value 1 after t:
dia1 | t1; dia2 | t1; dia3 | t1; dia4 | t1; dia5 | t1; dia6 | t1; dia7 | t1; 
# factor mean:
IADL ~ 0;
# factor variance:
IADL ~~ 1*IADL
"
model2PEstimates = sem(model = model2PSyntax, data = iadlData, ordered = c("dia1", "dia2", "dia3", "dia4", "dia5", "dia6", "dia7"),
                       mimic = "Mplus", estimator = "WLSMV", std.lv = TRUE, parameterization = "theta")
lavaan WARNING: 1 bivariate tables have empty cells; to see them, use:
                  lavInspect(fit, "zero.cell.tables")
summary(model2PEstimates, fit.measures = TRUE, rsquare = TRUE, standardized = TRUE)
lavaan (0.5-23.1097) converged normally after  79 iterations

                                                  Used       Total
  Number of observations                           609         635

  Estimator                                       DWLS      Robust
  Minimum Function Test Statistic               34.035      50.039
  Degrees of freedom                                14          14
  P-value (Chi-square)                           0.002       0.000
  Scaling correction factor                                  0.714
  Shift parameter                                            2.361
    for simple second-order correction (WLSMV)

Model test baseline model:

  Minimum Function Test Statistic            18222.540   12045.449
  Degrees of freedom                                21          21
  P-value                                        0.000       0.000

User model versus baseline model:

  Comparative Fit Index (CFI)                    0.999       0.997
  Tucker-Lewis Index (TLI)                       0.998       0.996

  Robust Comparative Fit Index (CFI)                            NA
  Robust Tucker-Lewis Index (TLI)                               NA

Root Mean Square Error of Approximation:

  RMSEA                                          0.049       0.065
  90 Percent Confidence Interval          0.028  0.070       0.046  0.085
  P-value RMSEA <= 0.05                          0.512       0.091

  Robust RMSEA                                                  NA
  90 Percent Confidence Interval                                NA     NA

Standardized Root Mean Square Residual:

  SRMR                                           0.036       0.036

Weighted Root Mean Square Residual:

  WRMR                                           1.103       1.103

Parameter Estimates:

  Information                                 Expected
  Standard Errors                           Robust.sem

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  IADL =~                                                               
    dia1              2.730    0.332    8.214    0.000    2.730    0.939
    dia2              2.922    0.493    5.930    0.000    2.922    0.946
    dia3              2.785    0.381    7.307    0.000    2.785    0.941
    dia4              3.698    0.590    6.266    0.000    3.698    0.965
    dia5              2.449    0.291    8.416    0.000    2.449    0.926
    dia6              1.955    0.220    8.902    0.000    1.955    0.890
    dia7              1.511    0.278    5.425    0.000    1.511    0.834

Intercepts:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    IADL              0.000                               0.000    0.000
   .dia1              0.000                               0.000    0.000
   .dia2              0.000                               0.000    0.000
   .dia3              0.000                               0.000    0.000
   .dia4              0.000                               0.000    0.000
   .dia5              0.000                               0.000    0.000
   .dia6              0.000                               0.000    0.000
   .dia7              0.000                               0.000    0.000

Thresholds:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    dia1|t1          -1.020    0.185   -5.524    0.000   -1.020   -0.351
    dia2|t1          -3.080    0.480   -6.413    0.000   -3.080   -0.997
    dia3|t1          -2.220    0.305   -7.267    0.000   -2.220   -0.750
    dia4|t1          -1.581    0.306   -5.169    0.000   -1.581   -0.413
    dia5|t1          -1.092    0.176   -6.196    0.000   -1.092   -0.413
    dia6|t1          -1.439    0.169   -8.515    0.000   -1.439   -0.655
    dia7|t1          -2.936    0.374   -7.850    0.000   -2.936   -1.621

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    IADL              1.000                               1.000    1.000
   .dia1              1.000                               1.000    0.118
   .dia2              1.000                               1.000    0.105
   .dia3              1.000                               1.000    0.114
   .dia4              1.000                               1.000    0.068
   .dia5              1.000                               1.000    0.143
   .dia6              1.000                               1.000    0.207
   .dia7              1.000                               1.000    0.305

Scales y*:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    dia1              0.344                               0.344    1.000
    dia2              0.324                               0.324    1.000
    dia3              0.338                               0.338    1.000
    dia4              0.261                               0.261    1.000
    dia5              0.378                               0.378    1.000
    dia6              0.455                               0.455    1.000
    dia7              0.552                               0.552    1.000

R-Square:
                   Estimate
    dia1              0.882
    dia2              0.895
    dia3              0.886
    dia4              0.932
    dia5              0.857
    dia6              0.793
    dia7              0.695

We can also inspect the residual polychoric correlation matrix to investigate model misfit. Note these are the raw residuals, not a normalized or standardized version. Below that, the modification indices are displayed.

resid(model2PEstimates)
$type
[1] "raw"

$cov
     dia1   dia2   dia3   dia4   dia5   dia6   dia7  
dia1  0.000                                          
dia2  0.027  0.000                                   
dia3  0.037  0.029  0.000                            
dia4 -0.022 -0.042 -0.045  0.000                     
dia5 -0.030 -0.030 -0.100  0.030  0.000              
dia6 -0.043 -0.048 -0.039  0.026  0.028  0.000       
dia7 -0.110 -0.003  0.011  0.033 -0.031  0.065  0.000

$mean
dia1 dia2 dia3 dia4 dia5 dia6 dia7 
   0    0    0    0    0    0    0 

$th
      dia1|t1       dia2|t1       dia3|t1       dia4|t1       dia5|t1       dia6|t1       dia7|t1 
-1.142277e-08 -7.507156e-08  3.858897e-08  1.378347e-10  5.264907e-08 -4.490636e-08  2.146585e-08 
modificationindices(model2PEstimates, sort. = TRUE)
    lhs op  rhs    mi mi.scaled    epc sepc.lv sepc.all sepc.nox
53 dia4 ~~ dia5 9.157    12.828  0.889   0.889    0.088    0.088
50 dia3 ~~ dia5 8.877    12.435 -0.995  -0.995   -0.127   -0.127
39 dia1 ~~ dia3 7.766    10.879  0.719   0.719    0.084    0.084
49 dia3 ~~ dia4 4.328     6.062 -0.816  -0.816   -0.072   -0.072
44 dia2 ~~ dia3 4.060     5.688  0.581   0.581    0.064    0.064
45 dia2 ~~ dia4 2.852     3.995 -0.743  -0.743   -0.063   -0.063
43 dia1 ~~ dia7 2.334     3.270 -0.674  -0.674   -0.128   -0.128
54 dia4 ~~ dia6 2.312     3.239  0.422   0.422    0.050    0.050
42 dia1 ~~ dia6 2.094     2.933 -0.370  -0.370   -0.058   -0.058
47 dia2 ~~ dia6 1.986     2.781 -0.422  -0.422   -0.062   -0.062
38 dia1 ~~ dia2 1.879     2.632  0.421   0.421    0.047    0.047
40 dia1 ~~ dia4 1.862     2.608 -0.475  -0.475   -0.043   -0.043
41 dia1 ~~ dia5 1.801     2.523 -0.358  -0.358   -0.047   -0.047
58 dia6 ~~ dia7 1.644     2.303  0.338   0.338    0.085    0.085
51 dia3 ~~ dia6 1.640     2.298 -0.341  -0.341   -0.052   -0.052
56 dia5 ~~ dia6 1.588     2.224  0.260   0.260    0.045    0.045
46 dia2 ~~ dia5 1.091     1.529 -0.335  -0.335   -0.041   -0.041
57 dia5 ~~ dia7 0.227     0.319 -0.178  -0.178   -0.037   -0.037
55 dia4 ~~ dia7 0.127     0.179  0.254   0.254    0.037    0.037
52 dia3 ~~ dia7 0.052     0.073  0.081   0.081    0.015    0.015
48 dia2 ~~ dia7 0.005     0.007 -0.026  -0.026   -0.005   -0.005

From the modifiation indices, we see several trends we saw with the M2() residuals in R: that items 5 and 3 need some additional help (as do items 1 and 3 and items 5 and 4).

ICC Plots

lavaanCatItemPlot = function(lavObject, varname, sds = 3){
  output = inspect(object = lavObject, what = "est")
  if (!varname %in% rownames(output$lambda)) stop(paste(varname, "not found in lavaan object"))
  if (dim(output$lambda)[2]>1) stop("plots only given for one factor models")
  
  itemloading = output$lambda[which(rownames(output$lambda) == varname),1]
  itemthresholds = output$tau[grep(pattern = varname, x = rownames(output$tau))]
  
  factorname = colnames(output$lambda)
  factormean = output$alpha[which(rownames(output$alpha) == factorname)]
  factorvar = output$psi[which(rownames(output$psi) == factorname)]
  
  factormin = factormean - 3*sqrt(factorvar)
  factormax = factormean + 3*sqrt(factorvar)
  
  factorX = seq(factormin, factormax, .01)
  itemloc = which(lavObject@Data@ov$name == varname)      
  itemlevels = unlist(strsplit(x = lavObject@Data@ov$lnam[itemloc], split = "\\|"))  
  if (length(itemthresholds)>1){
    
    plotdata = NULL
    plotdata2 = NULL
    itemY = NULL
    itemY2 = NULL
    itemX = NULL
    itemText = NULL
    for (level in 1:length(itemthresholds)){
      
      itemY = pnorm(q = -1*itemthresholds[level] + itemloading*factorX)
      itemY2 = cbind(itemY2, pnorm(q = -1*itemthresholds[level] + itemloading*factorX))
      itemText = paste0("P(", varname, " > ", itemlevels[level], ")")
      itemText2 = paste0("P(", varname, " = ", itemlevels[level], ")")
      plotdata = rbind(plotdata, data.frame(factor = factorX, prob = itemY, plot = itemText))
      
      if (level == 1){
        plotdata2 = data.frame(factor = factorX, plot = itemText2, prob = matrix(1, nrow = dim(itemY2)[1], ncol=1) - itemY2[,level])
      } else if (level == length(itemthresholds)){
        plotdata2 = rbind(plotdata2, data.frame(factor = factorX, plot = itemText2, prob = itemY2[,level-1] - itemY2[,level]))
        plotdata2 = rbind(plotdata2, data.frame(factor = factorX, plot = paste0("P(", varname, " = ", itemlevels[level+1], ")"), prob = itemY2[,level]))                  
      } else {
        plotdata2 = rbind(plotdata2, data.frame(factor = factorX, plot = itemText2, prob = itemY2[,level-1] - itemY2[,level]))
      }
      
    }
    
    names(plotdata) = c(factorname , "Probability", "Cumulative")
    ggplot(data = plotdata, aes_string(x = factorname, y = "Probability", colour = "Cumulative")) + geom_line(size = 2)
    
    names(plotdata2) = c(factorname, "Response", "Probability")
    ggplot(data = plotdata2, aes_string(x = factorname, y = "Probability", colour = "Response")) + geom_line(size = 2)
  } else {
    
    itemY = pnorm(q = -1*itemthresholds[1] + itemloading*factorX)
    itemText2 = paste0("P(", varname, " = ", itemlevels[2], ")")
    plotdata = data.frame(factor = factorX, prob = itemY, plot = itemText2)
    
    names(plotdata) = c(factorname , "Probability", "Response")
    ggplot(data = plotdata, aes_string(x = factorname, y = "Probability", colour = "Response")) + geom_line(size = 2)
    
  }
}
lavaanCatItemPlot(lavObject = model2PEstimates, varname = "dia2", sds = 3)

Conversion to IRT Paramterization (Discrimination/Difficulty)

convertTheta2IRT = function(lavObject){
  if (!lavObject@Options$parameterization == "theta") stop("your model is not estimated with parameterization='theta'")
  
  output = inspect(object = lavObject, what = "est")
  if (ncol(output$lambda)>1) stop("IRT conversion is only valid for one dimensional factor models. Your model has more than one dimension.")
  
  a = output$lambda
  b = output$tau/output$lambda
  return(list(a = a, b=b))
}
convertTheta2IRT(lavObject = model2PEstimates)
$a
      IADL
dia1 2.730
dia2 2.922
dia3 2.785
dia4 3.698
dia5 2.449
dia6 1.955
dia7 1.511

$b
        thrshl
dia1|t1 -0.374
dia2|t1 -1.054
dia3|t1 -0.797
dia4|t1 -0.428
dia5|t1 -0.446
dia6|t1 -0.736
dia7|t1 -1.943

One-Parameter Normal Ogive Model

To estimate the 1PNO model in lavaan, we use the following syntax. The summary, residuals, and modification indices are displayed subsequently.

model1PSyntax = "
# loadings/discrimination parameters:
IADL =~ loading*dia1 + loading*dia2 + loading*dia3 + loading*dia4 + loading*dia5 + loading*dia6 + loading*dia7
# threshholds use the | operator and start at value 1 after t:
dia1 | t1; dia2 | t1; dia3 | t1; dia4 | t1; dia5 | t1; dia6 | t1; dia7 | t1; 
# factor mean:
IADL ~ 0;
# factor variance:
IADL ~~ 1*IADL
"
model1PEstimates = sem(model = model1PSyntax, data = iadlData, ordered = c("dia1", "dia2", "dia3", "dia4", "dia5", "dia6", "dia7"),
                       mimic = "Mplus", estimator = "WLSMV", std.lv = TRUE, parameterization = "theta")
lavaan WARNING: 1 bivariate tables have empty cells; to see them, use:
                  lavInspect(fit, "zero.cell.tables")
summary(model1PEstimates, fit.measures = TRUE, rsquare = TRUE, standardized = TRUE)
lavaan (0.5-23.1097) converged normally after  27 iterations

                                                  Used       Total
  Number of observations                           609         635

  Estimator                                       DWLS      Robust
  Minimum Function Test Statistic               62.072      64.108
  Degrees of freedom                                20          20
  P-value (Chi-square)                           0.000       0.000
  Scaling correction factor                                  1.037
  Shift parameter                                            4.227
    for simple second-order correction (WLSMV)

Model test baseline model:

  Minimum Function Test Statistic            18222.540   12045.449
  Degrees of freedom                                21          21
  P-value                                        0.000       0.000

User model versus baseline model:

  Comparative Fit Index (CFI)                    0.998       0.996
  Tucker-Lewis Index (TLI)                       0.998       0.996

  Robust Comparative Fit Index (CFI)                            NA
  Robust Tucker-Lewis Index (TLI)                               NA

Root Mean Square Error of Approximation:

  RMSEA                                          0.059       0.060
  90 Percent Confidence Interval          0.043  0.076       0.044  0.077
  P-value RMSEA <= 0.05                          0.175       0.141

  Robust RMSEA                                                  NA
  90 Percent Confidence Interval                                NA     NA

Standardized Root Mean Square Residual:

  SRMR                                           0.058       0.058

Weighted Root Mean Square Residual:

  WRMR                                           1.489       1.489

Parameter Estimates:

  Information                                 Expected
  Standard Errors                           Robust.sem

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  IADL =~                                                               
    dia1    (ldng)    2.630    0.166   15.830    0.000    2.630    0.935
    dia2    (ldng)    2.630    0.166   15.830    0.000    2.630    0.935
    dia3    (ldng)    2.630    0.166   15.830    0.000    2.630    0.935
    dia4    (ldng)    2.630    0.166   15.830    0.000    2.630    0.935
    dia5    (ldng)    2.630    0.166   15.830    0.000    2.630    0.935
    dia6    (ldng)    2.630    0.166   15.830    0.000    2.630    0.935
    dia7    (ldng)    2.630    0.166   15.830    0.000    2.630    0.935

Intercepts:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    IADL              0.000                               0.000    0.000
   .dia1              0.000                               0.000    0.000
   .dia2              0.000                               0.000    0.000
   .dia3              0.000                               0.000    0.000
   .dia4              0.000                               0.000    0.000
   .dia5              0.000                               0.000    0.000
   .dia6              0.000                               0.000    0.000
   .dia7              0.000                               0.000    0.000

Thresholds:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    dia1|t1          -0.987    0.158   -6.253    0.000   -0.987   -0.351
    dia2|t1          -2.807    0.190  -14.758    0.000   -2.807   -0.997
    dia3|t1          -2.111    0.177  -11.903    0.000   -2.111   -0.750
    dia4|t1          -1.161    0.161   -7.193    0.000   -1.161   -0.413
    dia5|t1          -1.161    0.160   -7.272    0.000   -1.161   -0.413
    dia6|t1          -1.844    0.172  -10.737    0.000   -1.844   -0.655
    dia7|t1          -4.560    0.291  -15.697    0.000   -4.560   -1.621

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    IADL              1.000                               1.000    1.000
   .dia1              1.000                               1.000    0.126
   .dia2              1.000                               1.000    0.126
   .dia3              1.000                               1.000    0.126
   .dia4              1.000                               1.000    0.126
   .dia5              1.000                               1.000    0.126
   .dia6              1.000                               1.000    0.126
   .dia7              1.000                               1.000    0.126

Scales y*:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
    dia1              0.355                               0.355    1.000
    dia2              0.355                               0.355    1.000
    dia3              0.355                               0.355    1.000
    dia4              0.355                               0.355    1.000
    dia5              0.355                               0.355    1.000
    dia6              0.355                               0.355    1.000
    dia7              0.355                               0.355    1.000

R-Square:
                   Estimate
    dia1              0.874
    dia2              0.874
    dia3              0.874
    dia4              0.874
    dia5              0.874
    dia6              0.874
    dia7              0.874
resid(object = model1PEstimates)
$type
[1] "raw"

$cov
     dia1   dia2   dia3   dia4   dia5   dia6   dia7  
dia1  0.000                                          
dia2  0.042  0.000                                   
dia3  0.047  0.045  0.000                            
dia4  0.011 -0.002 -0.010  0.000                     
dia5 -0.035 -0.028 -0.102  0.050  0.000              
dia6 -0.081 -0.080 -0.075  0.011 -0.022  0.000       
dia7 -0.201 -0.088 -0.078 -0.036 -0.133 -0.066  0.000

$mean
dia1 dia2 dia3 dia4 dia5 dia6 dia7 
   0    0    0    0    0    0    0 

$th
      dia1|t1       dia2|t1       dia3|t1       dia4|t1       dia5|t1       dia6|t1       dia7|t1 
-1.002357e-06 -9.511829e-08  1.171064e-06  4.800199e-07  4.800199e-07 -2.711188e-06 -3.423275e-06 
modificationindices(model1PEstimates, sort. = TRUE)
    lhs op  rhs     mi mi.scaled    epc sepc.lv sepc.all sepc.nox
23 dia7 ~~ dia7 13.706    13.222  1.863   1.863    0.235    0.235
59 dia4 ~~ dia5 10.157     9.799  0.462   0.462    0.058    0.058
22 dia6 ~~ dia6  9.921     9.571  0.869   0.869    0.110    0.110
56 dia3 ~~ dia5  7.506     7.241 -0.831  -0.831   -0.105   -0.105
49 dia1 ~~ dia7  6.714     6.477 -1.596  -1.596   -0.202   -0.202
20 dia4 ~~ dia4  6.692     6.456 -0.617  -0.617   -0.078   -0.078
45 dia1 ~~ dia3  6.172     5.954  0.413   0.413    0.052    0.052
48 dia1 ~~ dia6  5.665     5.465 -0.663  -0.663   -0.084   -0.084
50 dia2 ~~ dia3  5.065     4.886  0.397   0.397    0.050    0.050
57 dia3 ~~ dia6  4.638     4.474 -0.615  -0.615   -0.078   -0.078
53 dia2 ~~ dia6  4.314     4.161 -0.650  -0.650   -0.082   -0.082
63 dia5 ~~ dia7  3.514     3.390 -1.058  -1.058   -0.134   -0.134
44 dia1 ~~ dia2  2.742     2.645  0.350   0.350    0.044    0.044
54 dia2 ~~ dia7  2.415     2.329 -0.706  -0.706   -0.089   -0.089
58 dia3 ~~ dia7  1.835     1.770 -0.621  -0.621   -0.078   -0.078
47 dia1 ~~ dia5  1.616     1.559 -0.288  -0.288   -0.036   -0.036
64 dia6 ~~ dia7  1.298     1.253 -0.528  -0.528   -0.067   -0.067
52 dia2 ~~ dia5  0.707     0.682 -0.227  -0.227   -0.029   -0.029
18 dia2 ~~ dia2  0.660     0.636 -0.212  -0.212   -0.027   -0.027
62 dia5 ~~ dia6  0.647     0.624 -0.183  -0.183   -0.023   -0.023
17 dia1 ~~ dia1  0.598     0.577 -0.190  -0.190   -0.024   -0.024
19 dia3 ~~ dia3  0.496     0.479 -0.176  -0.176   -0.022   -0.022
46 dia1 ~~ dia4  0.261     0.252  0.094   0.094    0.012    0.012
60 dia4 ~~ dia6  0.252     0.243  0.098   0.098    0.012    0.012
55 dia3 ~~ dia4  0.149     0.144 -0.086  -0.086   -0.011   -0.011
61 dia4 ~~ dia7  0.134     0.130 -0.283  -0.283   -0.036   -0.036
21 dia5 ~~ dia5  0.081     0.078  0.071   0.071    0.009    0.009
51 dia2 ~~ dia4  0.006     0.006 -0.019  -0.019   -0.002   -0.002

To compare models, we use the anova() function, which uses the correct test for us. Here we see the 2PL is preferred to the 1PL, again.

anova(model1PEstimates, model2PEstimates)
Scaled Chi Square Difference Test (method = "satorra.2000")

                 Df AIC BIC  Chisq Chisq diff Df diff Pr(>Chisq)   
model2PEstimates 14         34.035                                 
model1PEstimates 20         62.072     18.013  5.0417    0.00303 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
LS0tCnRpdGxlOiAiRVBTWSA5MDYvQ0xEUCA5NDggRXhhbXBsZSA1IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpFeGFtcGxlIGRhdGE6IDYzNSBvbGRlciBhZHVsdHMgKGFnZSA4MC0xMDApIHNlbGYtcmVwb3J0aW5nIG9uIDcgaXRlbXMgYXNzZXNzaW5nIHRoZSBJbnN0cnVtZW50YWwgQWN0aXZpdGllcyBvZiBEYWlseSBMaXZpbmcgKElBREwpIGFzIGZvbGxvd3M6CgoxLiBIb3VzZXdvcmsgKGNsZWFuaW5nIGFuZCBsYXVuZHJ5KTogMT02NCUKMi4gQmVkbWFraW5nOiAxPTg0JQozLiBDb29raW5nOiAxPTc3JQo0LiBFdmVyeWRheSBzaG9wcGluZzogMT02NiUKNS4gR2V0dGluZyB0byBwbGFjZXMgb3V0c2lkZSBvZiB3YWxraW5nIGRpc3RhbmNlOiAxPTY1JQo2LiBIYW5kbGluZyBiYW5raW5nIGFuZCBvdGhlciBidXNpbmVzczogMT03MyUJCjcuIFVzaW5nIHRoZSB0ZWxlcGhvbmUgMT05NCUKClR3byB2ZXJzaW9ucyBvZiBhIHJlc3BvbnNlIGZvcm1hdCB3ZXJlIGF2YWlsYWJsZToKCkJpbmFyeSAtPiAwID0g4oCcbmVlZHMgaGVscOKAnSwgMSA9IOKAnGRvZXMgbm90IG5lZWQgaGVscOKAnQoKQ2F0ZWdvcmljYWwgLT4gMCA9IOKAnGNhbuKAmXQgZG8gaXTigJ0sIDE94oCdYmlnIHByb2JsZW1z4oCdLCAyPeKAnXNvbWUgcHJvYmxlbXPigJ0sIDM94oCdbm8gcHJvYmxlbXPigJ0KCkhpZ2hlciBzY29yZXMgaW5kaWNhdGUgZ3JlYXRlciBmdW5jdGlvbi4gV2Ugd2lsbCBsb29rIGF0IGVhY2ggcmVzcG9uc2UgZm9ybWF0IGluIHR1cm4uCgojIyMgUGFja2FnZSBJbnN0YWxsYXRpb24gYW5kIExvYWRpbmcgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1UUlVFfQppZiAocmVxdWlyZShsYXZhYW4pID09IEZBTFNFKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJsYXZhYW4iKQp9CmxpYnJhcnkobGF2YWFuKQoKaWYgKHJlcXVpcmUobWlydCkgPT0gRkFMU0UpewogIGluc3RhbGwucGFja2FnZXMoIm1pcnQiKQp9CmxpYnJhcnkobWlydCkKCmBgYAoKIyMjIERhdGEgSW1wb3J0IGludG8gUgpUaGUgZGF0YSBhcmUgaW4gYSB0ZXh0IGZpbGUgbmFtZWQgYGBgYWRsLmRhdGBgYCBvcmlnbmFsbHkgdXNlZCBpbiBNcGx1cyAoc28gbm8gY29sdW1uIG5hbWVzIHdlcmUgaW5jbHVkZWQgYXQgdGhlIHRvcCBvZiB0aGUgZmlsZSkuIFRoZSBmaWxlIGNvbnRhaW5zIG1vcmUgaXRlbXMgdGhhbiB3ZSB3aWxsIHVzZSwgc28gd2Ugc2VsZWN0IG9ubHkgaXRlbXMgYWJvdmUgZnJvbSB0aGUgd2hvbGUgZmlsZS4KCmBgYHtyIGRhdGEsIGluY2x1ZGU9VFJVRX0KCiNyZWFkIGluIGRhdGEgZmlsZSAoTXBsdXMgZmlsZSBmb3JtYXQgc28gaGF2aW5nIHRvIGxhYmVsIGNvbHVtbnMpCmFkbERhdGEgPSByZWFkLnRhYmxlKGZpbGUgPSAiYWRsLmRhdCIsIGhlYWRlciA9IEZBTFNFLCBuYS5zdHJpbmdzID0gIi4iLCBjb2wubmFtZXMgPSBjKCJjYXNlIiwgcGFzdGUwKCJkcGEiLCAxOjE0KSwgcGFzdGUwKCJkaWEiLCAxOjcpLCBwYXN0ZTAoImNwYSIsIDE6MTQpLCBwYXN0ZTAoImNpYSIsIDE6NykpKQoKI3NlbGVjdCBTaXR1YXRpb25zIGl0ZW1zIGFuZCBQZXJzb25JRCB2YXJpYWJsZXMKaWFkbERhdGFJbml0ID0gYWRsRGF0YVtjKHBhc3RlMCgiZGlhIiwgMTo3KSldCgojcmVtb3ZlIGNhc2VzIHdpdGggYWxsIGl0ZW1zIG1pc3NpbmcKcmVtb3ZlQ2FzZXMgPSB3aGljaChhcHBseShYID0gaWFkbERhdGFJbml0LCBNQVJHSU4gPSAxLCBGVU4gPSBmdW5jdGlvbiAoeCl7IGxlbmd0aCh3aGljaChpcy5uYSh4KSkpfSkgPT0gNykKCmlhZGxEYXRhID0gaWFkbERhdGFJbml0Wy1yZW1vdmVDYXNlcyxdCmBgYAoKIyMjIEVzdGltYXRpb24gd2l0aCBNYXJnaW5hbCBNYXhpbXVtIExpa2VsaWhvb2QKCldlIHdpbGwgaW50cm9kdWNlIHRoZSBgbWlydGAgcGFja2FnZSBhcyBhIG1ldGhvZCBmb3IgZXN0aW1hdGluZyBJUlQgbW9kZWxzLiBPdmVyYWxsLCB0aGUgcGFja2FnZSBpcyB2ZXJ5IGdvb2QsIGJ1dCB0eXBpY2FsbHkgaXMgdXNlZCBmb3Igc2NhbGluZyBwdXJwb3NlcyAobWVhc3VyZW1lbnQgcmF0aGVyIHRoYW4gdXNlIG9mIGxhdGVudCB2YXJpYWJsZXMgaW4gYWRkaXRpb25hbCBtb2RlbCBlcXVhdGlvbnMpLiBXZSB1c2UgdGhlIHBhY2thZ2UgdG8gZGVtb25zdHJhdGUgZXN0aW1hdGluZyBJUlQgbW9kZWxzIHVzaW5nIG1hcmdpbmFsIG1heGltdW0gbGlrZWxpaG9vZC4gSWYgeW91IHdpc2ggdG8gdXNlIHRoZSBsYXRlbnQgdHJhaXQgZXN0aW1hdGVzIGluIHNlY29uZGFyeSBhbmFseXNlcyAod2hpY2ggeW91IHdvdWxkIG90aGVyd2lzZSB1c2UgU0VNIGZvciBzaW11bHRhbmVvdXNseSksIHRoZXJlIGFyZSBhZGRpdGlvbmFsIHN0ZXBzIHRvIHRha2UgdG8gZW5zdXJlIHRoZSBlcnJvciBhc3NvY2lhdGVkIHdpdGggZWFjaCBzY29yZSBpcyBjYXJyaWVkIG92ZXIgdG8gdGhlIHN1YnNlcXVlbnQgYW5hbHlzZXNlCgpXaGVuIGFsbCB0aGUgaXRlbXMgb2YgdGhlIG1vZGVsIGFyZSB0aGUgc2FtZSB0eXBlLCB0aGUgYG1pcnRgIHN5bnRheCBpcyB2ZXJ5IHNob3J0LiBUaGUgYG1pcnQoKWAgZnVuY3Rpb24gaXMgdXNlZCB0byBwcm92aWRlIGVzdGltYXRlcywgd2l0aCBvcHRpb25zIGBtb2RlbD0xYCBmb3IgYWxsIGl0ZW1zIG1lYXN1cmluZyB0aGUgc2FtZSB0cmFpdCBhbmQgYGl0ZW10eXBlPSIyUEwiYCBmb3IgdGhlIHR3by1wYXJhbWV0ZXIgbG9naXN0aWMgbW9kZWwgKGAiUmFzY2giYCBpcyB1c2VkIGZvciB0aGUgYDFQTGAgc2hvcnRoYW5kKS4gVGhlIGAiUmFzY2giYCBkZXNpZ25hdGlvbiBlc3RpbWF0ZXMgYSBtb2RlbCB3aGVyZSB0aGUgbG9hZGluZ3MgYXJlIGFsbCBzZXQgdG8gb25lIGFuZCB0aGUgZmFjdG9yL2xhdGVudCB0cmFpdCB2YXJpYW5jZSBpcyBlc3RpbWF0ZWQpIC0tIHdoaWNoIGlzIGFuIGVxdWl2YWxlbnQgbW9kZWwgdG8gdGhlIG9uZSBlc3RpbWF0ZWQgYmVsb3cgYnV0IHdlIHNlZWsgdG8ga2VlcCB0aGUgbGF0ZW50IHRyYWl0IHN0YW5kYXJkaXplZC4gV2Ugd2lsbCBlc3RpbWF0ZSBib3RoIHNpbXVsdGFuZW91c2x5IGhlcmU6CgpgYGB7ciBtaXJ0MlBMLCBpbmNsdWRlPVRSVUV9Cm1pcnQxUExzeW50YXggPSAiCklBREwgPSAxLTcKQ09OU1RSQUlOID0gKDEtNywgYTEpCkNPViA9IDEKIgoKbW9kZWwxUExtaXJ0ID0gbWlydChkYXRhID0gaWFkbERhdGEsIG1vZGVsID0gbWlydDFQTHN5bnRheCkKCgptb2RlbDJQTG1pcnQgPSBtaXJ0KGRhdGEgPSBpYWRsRGF0YSwgbW9kZWwgPSAxLCBpdGVtdHlwZSA9ICIyUEwiKQpgYGAKClVubGlrZSBgbGF2YWFuYCwgYG1pcnRgIGRvZXMgbm90IHByb3ZpZGUgYSBuaWNlIGZvcm1hdHRpbmcgb2YgcGFyYW1ldGVycyB3aXRoIHRoZSBzdW1tYXJ5IHN0YXRtZW50LiBSYXRoZXIsIHdlIGdldCBwYXJ0cyBvZiBlc3RpbWF0ZXMgdGhyb3VnaCB2YXJpb3VzIHBpZWNlcy4KClRoZSBtb2RlbCBsb2ctbGlrZWxpaG9vZCBhbmQgc3VtbWFyeSBpbmZvcm1hdGlvbiBpcyBnaXZlbiBieSB0aGUgYHNob3coKWAgZnVuY3Rpb246CgpgYGB7ciBtaXJ0U3VtLCBpbmNsdWRlPVRSVUV9CnNob3cobW9kZWwxUExtaXJ0KQpzaG93KG1vZGVsMlBMbWlydCkKYGBgCgpBbHNvIG5vdGUgdGhhdCB0aGUgbW9kZWwgbG9nLWxpa2VsaWhvb2QgaW5mb3JtYXRpb24gZG9lcyBub3QgaW5jbHVkZSBhIHRlc3Qgb2YgdGhlIG1vZGVsIGFnYWluc3QgYW4gYWx0ZXJuYXRpdmUsIGFzIGRvZXMgYSB0eXBpY2FsIENGQSBhbmFseXNpcyBpbiBjb21wYXJpbmcgdGhlIG1vZGVsIGZpdCBvZiB5b3VyIG1vZGVsIHRvIG9uZSB3aGVyZSBhbGwgcGFyYW1ldGVycyB3ZXJlIGVzdGltYXRlZC4gVGhpcyBpcyBiZWNhdXNlIHRoZSBzYXR1cmF0ZWQgbW9kZWwgaW4gSVJUIGlzIGRpZmZlcmVudCAoZm9yIG1vZGVscyB3aGVyZSBhbGwgaXRlbXMgYXJlIGJpbmFyeSwgaXQgaXMgTXVsdGl2YXJpYXRlIEJlcm5vdWxsaSkgaW4gdGhhdCB0aGUgc3RhdGlzdGljcyBvZiBpbnRlcmVzdCBjb21lIGluIHRoZSBmb3JtIG9mIHRoZSBwcm9wb3J0aW9uIG9mIHBlb3BsZSB3aXRoIGEgZ2l2ZW4gX3Jlc3BvbnNlIHBhdHRlcm5fLgoKVG8gc2VlIGVzdGltYXRlcywgdXNlIHRoZSBgY29lZigpYCBmdW5jdGlvbi4gSGVyZSBhcmUgdGhlIGVzdGltYXRlcyBmb3IgdGhlIDFQTCBtb2RlbDoKCmBgYHtyIHNob3cxUEwsIGluY2x1ZGU9VFJVRX0KY29lZjFQTCA9IGNvZWYobW9kZWwxUExtaXJ0KQpjb2VmMVBMCmBgYAoKVGhlIGBjb2VmKClgIGZ1bmN0aW9uIHJldHVybnMgYW4gUiBsaXN0IG9mIHRoZSBwYXJhbWV0ZXJzIGZvciBlYWNoIGl0ZW0gYWxvbmcgd2l0aCB0aGUgc3RydWN0dXJhbCBtb2RlbCBwYXJhbWV0ZXJzICh0aGUgYCRHcm91cFBhcnNgIGVsZW1lbnQpLCB3aGljaCBzaG93cyB0aGUgbWVhbiBhbmQgdmFyaWFuY2Ugb2YgdGhlIGxhdGVudCB2YXJpYWJsZS4gRm9yIGVhY2ggaXRlbSwgdGhlcmUgYXJlIGF0IGxlYXN0IGZvdXIgcGFyYW1ldGVycyBsaXN0ZWQ6CgoqIGBhMWA6IHRoZSBmYWN0b3IgbG9hZGluZy9pdGVtIGRpc2NyaW1pbmF0aW9uL3Nsb3BlOiB0aGUgY2hhbmdlIGluIHRoZSBsb2ctb2RkcyBvZiAkUChZX3tzaX09MSkkIHBlciBvbmUgdW5pdCBjaGFuZ2UgaW4gJFx0aGV0YV9zJCAoYXBwcm94aW1hdGUgY2hhbmdlIGluIHRoZSAzLzRQTCBtb2RlbHMpCiogYGRgOiB0aGUgaXRlbSBpbnRlcmNlcHQgdGhlIGV4cGVjdGVkIHZhbHVlIG9mIHRoZSBsb2ctb2RkcyBvZiAkUChZX3tzaX09MSkkIGZvciAkXHRoZXRhX3MgPSAwJCAoYXBwcm94aW1hdGUgZXhwZWN0YXRpb24gaW4gdGhlIDMvNFBMIG1vZGVscykKKiBgZ2A6IHRoZSBsb3dlciBhc3ltcHRvdGUgb3IgZ3Vlc3NpbmcgcGFyYW1ldGVyIChmb3IgM1BMIHRoaXMgd2lsbCBiZSBub24temVybykKKiBgdWA6IHRoZSB1cHBlciBhc3ltcHRvdGUgKGZvciBgNFBMYCBvciwgaW4gbWlydCwgYSB0eXBlIG9mIGEgM1BMLCB0aGlzIHdpbGwgYmUgbm9uLXplcm8pCgpOb3RlIGhvdyB0aGUgaXRlbSBkaXNjcmltaW5hdGlvbiAodGhlIGBhMWAgdGVybSkgaXMgZXF1YWwgZm9yIGFsbCBpdGVtcyAtLSB0aGlzIGlzIGRvbmUgYnkgY29udmVudGlvbiBpbiB0aGUgMVBMIG1vZGVsLgoKUHV0dGluZyB0aGUgcGFyYW1ldGVycyBpbnRvIGVxdWF0aW9uIGZvcm0sIHdlIGhhdmUgYSBzbG9wZS9pbnRlcmNlcHQgZm9ybSBvZiB0aGUgSVJUIG1vZGVsOgoKJCRQKFlfe3NpfSA9IDEgfCBcdGhldGFfcykgPSBnX2kgKyAodV9pLWdfaSlcZnJhY3tcZXhwXGxlZnQoZF9pICsgYTFfaSBcdGhldGFfcyBccmlnaHQpfXsxK1xleHBcbGVmdChkX2kgKyBhMV9pIFx0aGV0YV9zIFxyaWdodCl9JCQKCkFub3RoZXIgY29tbW9ubHkgdXNlZCBwYXJhbWV0ZXJpemF0aW9uIG9mIHRoZSBJUlQgbW9kZWwgaXMgY2FsbGVkIGRpc2NyaW1pbmF0aW9uL2RpZmZpY3VsdHksIGdpdmVuIGJ5OgoKJCRQKFlfe3NpfSA9IDEgfCBcdGhldGFfcykgPSBnX2kgKyAodV9pLWdfaSlcZnJhY3tcZXhwXGxlZnQoYTFfaSBcbGVmdCggXHRoZXRhX3MgLSBiX2kgXHJpZ2h0KSBccmlnaHQpfXsxK1xleHBcbGVmdChhMV9pIFxsZWZ0KCBcdGhldGFfcyAtIGJfaSBccmlnaHQpIFxyaWdodCl9JCQKClRoZSB0d28gcGFyYW1ldGVyaXphdGlvbnMgYXJlIGVxdWl2YWxlbnQgYW5kIG9uZSBjYW4gYmUgZm91bmQgYnkgcmUtYXJyYW5naW5nIHRlcm1zIG9mIHRoZSBvdGhlci4gVG8gZ2V0IHRoZSBpdGVtIGRpZmZpY3VsdHkgZnJvbSB0aGUgc2xvcGUvaW50ZXJjZXB0IHBhcmFtZXRlcml6YXRpb246CgokJGJfaSA9IC1cZnJhY3tkX2l9e2ExX2l9JCQKCkZvciBvdXIgcmVzdWx0cywgd2UgY2FuIHVzZSB0aGUgYGxhcHBseWAgZnVuY3Rpb24gdG8gYWRkIHRoZSBpdGVtIGRpZmZpY3VsdGllczoKCmBgYHtyIG1pcnQxUExkaWZmaWN1bHR5LCBpbmNsdWRlPVRSVUV9CmdldERpZmZpY3VsdHkgPSBmdW5jdGlvbihpdGVtUGFyKXsKICBwYXJuYW1lcyA9IGNvbG5hbWVzKGl0ZW1QYXIpCiAgaWYgKCJhMSIgJWluJSBwYXJuYW1lcyl7CiAgICBpdGVtUGFyID0gYyhpdGVtUGFyLCAtMSppdGVtUGFyWzJdL2l0ZW1QYXJbMV0pCiAgICBuYW1lcyhpdGVtUGFyKSA9IGMocGFybmFtZXMsICJiIikKICAgIHJldHVybihpdGVtUGFyKQogIH0gZWxzZSB7CiAgICByZXR1cm4oaXRlbVBhcikKICB9Cn0KCmxhcHBseShYID0gY29lZjFQTCwgRlVOID0gZ2V0RGlmZmljdWx0eSkKCml0ZW1QYXIgPSBjb2VmMVBMW1sxXV0KYGBgCgpGb3IgdGhlIDJQTCwgd2UgY2FuIHVzZSBhIHNpbWlsYXIgbWV0aG9kIChoZXJlIGNvbmRlbnNlZCB0byBkaXNwbGF5IHRoZSBpdGVtIGRpZmZpY3VsdGllcyk6CmBgYHtyIG1vZGVsMlBMY29lZiwgaW5jbHVkZT1UUlVFfQpjb2VmMlBMID0gbGFwcGx5KFggPSBjb2VmKG1vZGVsMlBMbWlydCksIEZVTiA9IGdldERpZmZpY3VsdHkpCmNvZWYyUEwKYGBgCgpBcyB0aGUgMVBMIGlzIG5lc3RlZCB3aXRoaW4gdGhlIDJQTCwgd2UgY2FuIHVzZSBhIGxpa2VsaWhvb2QgcmF0aW8gdGVzdCB0byBzZWUgd2hpY2ggbW9kZWwgaXMgcHJlZmVycmVkLiBUaGUgTFJUIHRlc3RzIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCBhbGwgaXRlbSBkaXNjcmltaW5hdGlvbnMgYXJlIGVxdWFsIGFnYWluc3QgYW4gYWx0ZXJuYXRpdmUgdGhhdCBub3QgYWxsIGFyZSBlcXVhbDoKCmBgYHtyIG1pcnRMUlQsIGluY2x1ZGU9VFJVRX0KYW5vdmEobW9kZWwxUExtaXJ0LCBtb2RlbDJQTG1pcnQpCmBgYAoKSGVyZSwgdGhlIHRlc3Qgc3RhdGlzdGljIHdhcyAkXGNoaV82ID0gMTguOTc3JCB3aXRoIGEgcC12YWx1ZSBvZiAuMDA0LiBUaGVyZWZvcmUsIHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIGVxdWFsIHNsb3BlcyBhbmQgY29uY2x1ZGUgdGhlIDJQTCBmaXRzIGJldHRlciB0aGFuIHRoZSAxUEwgbW9kZWwuIAoKVGhlIExSVCwgaG93ZXZlciwgYXNzdW1lcyBib3RoIG1vZGVscyBoYXZlIGEgc3VmZmljaWVudCBsZXZlbCBvZiBhYnNvbHV0ZSBmaXQgdG8gdGhlIGRhdGEuIE9uZSB3YXkgdG8gdGVsbCBpcyB0aGUgdXNlIG9mIHRoZSBgTTIoKWAgZnVuY3Rpb24sIHdoaWNoIHByb3ZpZGVzIG1vZGVsIGZpdCB0byB0aGUgMi13YXkgdGFibGVzICh0aGluayBpdGVtLXBhaXIgY292YXJpYW5jZXMpLiBCZWNhdXNlIG91ciBkYXRhIGhhcyBzb21lIG1pc3NpbmcgcmVzcG9uc2VzLCB3ZSBoYXZlIHRvIHVzZSB0aGUgYGltcHV0ZT0xMGAgb3B0aW9uLCBpbXB1dGluZyAxMCB2YWx1ZXMgcGVyIG1pc3NpbmcgcmVzcG9uc2UuIEhlcmUgaXMgdGhlIHZhbHVlIGZvciB0aGUgMVBMOgoKYGBge3IgbW9kZWwxUExNMiwgaW5jbHVkZT1UUlVFfQpNMihvYmogPSBtb2RlbDFQTG1pcnQsIGltcHV0ZSA9IDEwKQpgYGAKCmBgYHtyIG1vZGVsMlBMTTIsIGluY2x1ZGU9VFJVRX0KTTIob2JqID0gbW9kZWwyUExtaXJ0LCBpbXB1dGU9MTApCmBgYAoKVGhlIHN0YXRpc3RpY3MgZ2l2ZW4gZnJvbSB0aGUgTTIgZnVuY3Rpb24gYXJlIHNpbWlsYXIgdG8gdGhvc2UgdXNlZCBpbiBDRkEtLXRoZXNlIHNob3cgYXBwcm94aW1hdGUgbW9kZWwgZml0IGluZGljZXMgc3VjaCBhcyBSTVNFQSwgU1JNUiwgVExJLCBhbmQgQ0ZJLiBGcm9tIHRoZXNlLCBpdCBhcHBlYXJzIHRoZSBtb2RlbCBmaXRzIGFwcHJveGltYXRlbHkgKENGSSBhbmQgVExJIG5lYXIgMSBidXQgcmVsYXRpdmVseSBwb29yIFJNU0VBKS4gVG8gZmluZCBtaXNmaXR0aW5nICJyZXNpZHVhbHMiIHdlIG5lZWQgY29tcGxldGUgZGF0YSBhbmQgdGhlIGZ1bmN0aW9uIGBNMigpYCBhbmQgdGhlIGBpbXB1dGVNaXNzaW5nKClgIGZ1bmN0aW9ucyBhcmUgbm90IHdvcmtpbmcuIFNvLCBoZXJlIGlzIGFuIGV4YW1wbGUgd2l0aCBjb21wbGV0ZSBkYXRhIGFuZCB0aGUgMlBMOgoKYGBge3IgbTJyZXNpZCwgaW5jbHVkZT1UUlVFfQptb2RlbDJQTG1pcnRCID0gbWlydChkYXRhID0gaWFkbERhdGFbY29tcGxldGUuY2FzZXMoaWFkbERhdGEpLF0sIG1vZGVsID0gMSwgaXRlbXR5cGUgPSAiMlBMIikKTTIob2JqID0gbW9kZWwyUExtaXJ0QikKCk0yKG9iaiA9IG1vZGVsMlBMbWlydEIsIHJlc2lkbWF0ID0gVFJVRSkKYGBgCkhlcmUgd2Ugc2VlIHRoZSBiaWdnZXN0IGRlc2NyaXBhbmN5IG9mIHJlc2lkdWFsIGNvdmFyaWFuY2VzIGlzIHRoYXQgZm9yIGRpYTUgd2l0aCBkaWEzIGF0IC0uMDguCgpGaW5hbGx5LCB3ZSBjYW4gc2VlIHBsb3RzIG9mIG91ciBtb2RlbCAoYWxsIHNob3duIGZvciB0aGUgMlBMIG1vZGVsKS4gRmlyc3QsIHRoZSBpdGVtIGNoYXJhY3RlcmlzdGljIGN1cnZlcwoKYGBge3IgcGxvdHMsIGluY2x1ZGU9VFJVRX0KcGxvdChtb2RlbDJQTG1pcnQsIGl0ZW09MSwgdHlwZSA9ICJ0cmFjZSIsIHRoZXRhX2xpbSA9IGMoLTMsMykpCmBgYAoKTmV4dCB3ZSBjYW4gc2VlIHRoZSB0ZXN0IGluZm9ybWF0aW9uIHBsb3Q6CmBgYHtyIHBsb3RzMiwgaW5jbHVkZT1UUlVFfQpwbG90KG1vZGVsMlBMbWlydCwgdHlwZSA9ICJpbmZvIiwgdGhldGFfbGltID0gYygtMywzKSkKYGBgCldlIGNhbiBzZWUgdGhhdCBvdXIgdGVzdCBpbmZvcm1hdGlvbiBwZWFrcyBhcm91bmQgYSB0aGV0YSBvZiAtLjUsIG1lYW5pbmcgc2NvcmVzIG5lYXIgLS41IHdpbGwgYmUgdGhlIG1vc3QgcmVsaWFibGUuCgpGaW5hbGx5LCB3ZSBjYW4gdXNlIHRoZSBgZnNjb3JlcygpYCBmdW5jdGlvbiB0byBnZXQgdGhlIGVzdGltYXRlZCB0cmFpdCBzY29yZXMuIE5vdGUsIHRoZXJlIGFyZSBzZXZlcmFsIHR5cGVzIG9mIHNjb3JlcyBhdmFpbGFibGUuIFRoZSBzdGFuZGFyZCB1c2VkIGZvciBzY29yZSByZXBvcnRpbmcgaXMgYG1ldGhvZCA9ICJFQVAiYCwgd2hpY2ggYXJlIHNjb3JlcyB0aGF0IHVzZSB0aGUgZXhwZWN0ZWQgdmFsdWUgb2YgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gb2YgdGhlIHNjb3JlLiBGb3IgZG9pbmcgc2Vjb25kYXJ5IGFuYWx5c2VzLCBtdWx0aXBsZSAicGxhdXNpYmxlIiBzY29yZXMgc2hvdWxkIGJlIHVzZWQgd2l0aCB0aGUgb3B0aW9uIGBwbGF1c2libGUuZHJhd3MgPSAjYCB3aGVyZSAjIGlzIHRoZSBudW1iZXIgb2Ygc2NvcmVzIHRvIGRyYXcuIFdlIHBsb3QgdGhlIGRlbnNpdHkgb2YgdGhlIHRlc3Qgc2NvcmVzIGZvbGxvd2luZyBlc3RpbWF0aW5nIHRoZW0gd2l0aCBgZnNjb3JlcygpYDoKCmBgYHtyIGZzY29yZXMsIGluY2x1ZGU9VFJVRX0KdGhldGEgPSBmc2NvcmVzKG9iamVjdCA9IG1vZGVsMlBMbWlydCwgbWV0aG9kID0gIkVBUCIpCmhpc3QodGhldGEpCmBgYApUaGUgcGxvdCBzaG93cyBhIG51bWJlciBvZiBwZW9wbGUgd2l0aCBzY29yZXMgYXQgdGhlIG1heGltdW0gdmFsdWUgLS0gYnV0IHZlcnkgZmV3IGFyb3VuZCB0aGUgbW9zdCByZWxpYWJsZSBwb3J0aW9uIG9mIHRoZSB0ZXN0LCAtLjUKCkhlcmUsIHdlIHdpbGwgcGxvdCB0aGUgc2NvcmVzIGFsb25nIHdpdGggdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBzY29yZXMgdG8gc2hvdyB0aGUgcmVsYXRpb25zaGlwOgoKYGBge3IgZnNjb3Jlc1NFLCBpbmNsdWRlPVRSVUV9CnRoZXRhMiA9IGZzY29yZXMob2JqZWN0ID0gbW9kZWwyUExtaXJ0LCBtZXRob2QgPSAiRUFQIiwgZnVsbC5zY29yZXMuU0UgPSBUUlVFKQpwbG90KHggPSB0aGV0YTJbY29tcGxldGUuY2FzZXMoaWFkbERhdGEpLDFdLCB5ID0gdGhldGEyW2NvbXBsZXRlLmNhc2VzKGlhZGxEYXRhKSwyXSwgeGxhYiA9IGV4cHJlc3Npb24odGhldGEpLCB5bGFiID0gIlNFIiwgbWFpbiA9ICJDb21wbGV0ZSBEYXRhIFRoZXRhIHZzLiBTRShUaGV0YSkiKQpgYGAKCkZvciB0aGUgY2FzZXMgd2l0aCBjb21wbGV0ZSBkYXRhLCB0aGlzIGlzIHRoZSBiZXN0IHRoZSBTRSBnZXRzLiBXaGVuIHBsb3R0aW5nIGFsbCB0aGUgZGF0YSwgeW91IGNhbiBzZWUgdGhlIGltcGFjdCBvZiBtaXNzaW5nIGRhdGEgb24gU0U6IGZld2VyIG9ic2VydmF0aW9ucyBtZWFucyBhIGhpZ2hlciBTRSBmb3IgdGhlIHRyYWl0LgoKYGBge3IgcGxvdFNFMiwgaW5jbHVkZT1UUlVFfQpwbG90KHggPSB0aGV0YTJbLDFdLCB5ID0gdGhldGEyWywyXSwgeGxhYiA9IGV4cHJlc3Npb24odGhldGEpLCB5bGFiID0gIlNFIiwgbWFpbiA9ICJBbGwgRGF0YSBUaGV0YSB2cy4gU0UoVGhldGEpIikKYGBgCgpXZSBjYW4gYWxzbyBwbG90IHRoZSBpdGVtIGRpZmZpY3VsdHkgdmFsdWVzIHRvIGdldCBhIHNlbnNlIG9mIHRoZSB0aGUgc2NhbGUgaXMgdGVsbGluZyB1cyBhYm91dCB0aGUgdHJhaXQ6CgpgYGB7ciBkaWZwbG90LCBpbmNsdWRlPVRSVUV9Cml0ZW1EaWYgPSB1bmxpc3QobGFwcGx5KFggPSBjb2VmMlBMLCBGVU4gPSBmdW5jdGlvbih4KSByZXR1cm4oeFs1XSkpKQpwbG90KHggPSAxOjcsIHkgPSBpdGVtRGlmWzE6N10sIHR5cGUgPSAibCIsIHhsYWIgPSAiSXRlbSIsIHlsYWIgPSAiSXRlbSBEaWZmaWN1bHR5IChiKSIpCmBgYApIZXJlIGFyZSB0aGUgaXRlbXMsIGFnYWluOgoKMS4gSG91c2V3b3JrIChjbGVhbmluZyBhbmQgbGF1bmRyeSk6IDE9NjQlCjIuIEJlZG1ha2luZzogMT04NCUKMy4gQ29va2luZzogMT03NyUKNC4gRXZlcnlkYXkgc2hvcHBpbmc6IDE9NjYlCjUuIEdldHRpbmcgdG8gcGxhY2VzIG91dHNpZGUgb2Ygd2Fsa2luZyBkaXN0YW5jZTogMT02NSUKNi4gSGFuZGxpbmcgYmFua2luZyBhbmQgb3RoZXIgYnVzaW5lc3M6IDE9NzMlCQo3LiBVc2luZyB0aGUgdGVsZXBob25lIDE9OTQlCgpOb3RlIHRoYXQgbm8gaXRlbXMgYXJlIGF2YWlsYWJsZSB0byBtZWFzdXJlIGFib3ZlLWF2ZXJhZ2UgYWJpbGl0aWVzIHdlbGwhIFRoZSBpdGVtIGRpZmZpY3VsdHkgZm9yIG1vc3QgaXRlbXMgY292ZXJzIHZhbHVlcyBvZiBUaGV0YSBiZXR3ZWVuIOKIkjEuMCB0byDiiJIwLjUuCgojIyMgRXN0aW1hdGlvbiBpbiBgbGF2YWFuYDogTGltaXRlZCBJbmZvcm1hdGlvbiBPbmx5CgpgbGF2YWFuYCBvbmx5IHByb3ZpZGVzIGxpbWl0ZWQgaW5mb3JtYXRpb24gZXN0aW1hdGVzIG9mIElSVC9JRkEgbW9kZWxzLCB3aGljaCBhcmUgcGFyYWxsZWwgKGJ1dCBub3QgbmVjZXNzYXJpbHkgZXF1YWwgdG8pIE1wbHVzJyBXTFNNViBtZXRob2RzLiBUaGlzIGlzIGEgbGltaXRhdGlvbiBvZiBgbGF2YWFuYCwgc28gaWYgTUwgdmVyc2lvbnMgb2YgZXN0aW1hdGVzIGFyZSBuZWVkZWQsIE1wbHVzIHdpbGwgaGF2ZSB0byBiZSB1c2VkLiBBbHRlcm5hdGl2ZWx5LCB0aGUgYG1pcnRgIHBhY2thZ2UgaW4gUiBlc3RpbWF0ZXMgSVJUIG1vZGVscyAoYW5kIG1hbnkgb3RoZXIgdHlwZXMpLCBidXQgaGFzIHJhdGhlciBsaW1pdGVkIGNhcGFiaWxpdGllcyBmb3IgU0VNIHdpdGggdGhlIG1vZGVscyB1c2VkIGFuZCBkb2VzIG5vdCBpbmNsdWRlIG1vZGVsIGZ1bmN0aW9ucyBmb3IgY29udGlub3VzIG9ic2VydmVkIHZhcmlhYmxlcyBhbmQgcHJvdmlkZXMgdmVyeSBmZXcgU0VNIGZpdCBzdGF0aXN0aWNzLiAKCldlIHdpbGwgY29tcGFyZSB0aGUgb25lLXBhcmFtZXRlciB2cy4gdHdvLXBhcmFtZXRlciBtb2RlbHMgZm9yIEJpbmFyeSBSZXNwb25zZXMgdXNpbmcgV0xTTVYgUHJvYml0IG1vZGVsLiBCZWdpbm5pbmcgd2l0aCB0aGUgdHdvLXBhcmFtZXRlciBtb2RlbC4KCiMjIyMgVHdvLVBhcmFtZXRlciBNb2RlbCAKClRoZSB0d28tcGFyYW1ldGVyIG1vZGVsIGlzIGNhbGxlZCB0aGUgdHdvLXBhcmFtZXRlciBsb2dpc2l0aWMgbW9kZWwgaWYgdGhlIGxvZ2l0IGxpbmsgZnVuY3Rpb24gaXMgdXNlZCAoMlBMKSwgb3RoZXJ3aXNlIGl0IGlzIGNhbGxlZCB0aGUgdHdvLXBhcmFtZXRlciBub3JtYWwgb2dpdmUgKDJQTk8pLiBUaGUgc3ludGF4IGlzIGxhcmdlbHkgaWRlbnRpY2FsIHRvIHRoYXQgdXNlIGZvciBDRkEsIGhvd2V2ZXIsIGl0ZW0gaW50ZXJjZXB0cyAoYGl0ZW0gfiAxYCBpbiBDRkEpIGFyZSBub3cgcmVwbGFjZWQgYnkgaXRlbSB0aHJlc2hvbGRzIChgaXRlbSB8IHQjYCB3aGVyZSBgI2AgaXMgdGhlIG51bWJlciBvZiB0aGUgdGhyZXNob2xkLCBpbiBvcmRlciwgZnJvbSAxIHRocm91Z2ggdGhlIG51bWJlciBvZiBjYXRlZ29yaWVzIG9uIHRoZSBpdGVtIG1pbnVzIG9uZSkuCgpUaGUgbm9ybWFsIG9naXZlIG1vZGVsIHByb3ZpZGVzIGEgbW9kZWwgZm9yIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUncyAoJFkkICJ1bmRlcmx5aW5nIiBjb250aW51b3VzIGFuYWxvZykuCgpgYGB7ciBsYXZhYW4yUEwsIGluY2x1ZGU9VFJVRX0KbW9kZWwyUFN5bnRheCA9ICIKCiMgbG9hZGluZ3MvZGlzY3JpbWluYXRpb24gcGFyYW1ldGVyczoKSUFETCA9fiBkaWExICsgZGlhMiArIGRpYTMgKyBkaWE0ICsgZGlhNSArIGRpYTYgKyBkaWE3CgojIHRocmVzaGhvbGRzIHVzZSB0aGUgfCBvcGVyYXRvciBhbmQgc3RhcnQgYXQgdmFsdWUgMSBhZnRlciB0OgpkaWExIHwgdDE7IGRpYTIgfCB0MTsgZGlhMyB8IHQxOyBkaWE0IHwgdDE7IGRpYTUgfCB0MTsgZGlhNiB8IHQxOyBkaWE3IHwgdDE7IAoKIyBmYWN0b3IgbWVhbjoKSUFETCB+IDA7CgojIGZhY3RvciB2YXJpYW5jZToKSUFETCB+fiAxKklBREwKCiIKCm1vZGVsMlBFc3RpbWF0ZXMgPSBzZW0obW9kZWwgPSBtb2RlbDJQU3ludGF4LCBkYXRhID0gaWFkbERhdGEsIG9yZGVyZWQgPSBjKCJkaWExIiwgImRpYTIiLCAiZGlhMyIsICJkaWE0IiwgImRpYTUiLCAiZGlhNiIsICJkaWE3IiksCiAgICAgICAgICAgICAgICAgICAgICAgbWltaWMgPSAiTXBsdXMiLCBlc3RpbWF0b3IgPSAiV0xTTVYiLCBzdGQubHYgPSBUUlVFLCBwYXJhbWV0ZXJpemF0aW9uID0gInRoZXRhIikKc3VtbWFyeShtb2RlbDJQRXN0aW1hdGVzLCBmaXQubWVhc3VyZXMgPSBUUlVFLCByc3F1YXJlID0gVFJVRSwgc3RhbmRhcmRpemVkID0gVFJVRSkKCmBgYApXZSBjYW4gYWxzbyBpbnNwZWN0IHRoZSByZXNpZHVhbCBwb2x5Y2hvcmljIGNvcnJlbGF0aW9uIG1hdHJpeCB0byBpbnZlc3RpZ2F0ZSBtb2RlbCBtaXNmaXQuIE5vdGUgdGhlc2UgYXJlIHRoZSByYXcgcmVzaWR1YWxzLCBub3QgYSBub3JtYWxpemVkIG9yIHN0YW5kYXJkaXplZCB2ZXJzaW9uLiBCZWxvdyB0aGF0LCB0aGUgbW9kaWZpY2F0aW9uIGluZGljZXMgYXJlIGRpc3BsYXllZC4KCmBgYHtyIGxhdmFhbjJQTDIsIGluY2x1ZGU9VFJVRX0KcmVzaWQobW9kZWwyUEVzdGltYXRlcykKbW9kaWZpY2F0aW9uaW5kaWNlcyhtb2RlbDJQRXN0aW1hdGVzLCBzb3J0LiA9IFRSVUUpCmBgYAoKRnJvbSB0aGUgbW9kaWZpYXRpb24gaW5kaWNlcywgd2Ugc2VlIHNldmVyYWwgdHJlbmRzIHdlIHNhdyB3aXRoIHRoZSBgTTIoKWAgcmVzaWR1YWxzIGluIFI6IHRoYXQgaXRlbXMgNSBhbmQgMyBuZWVkIHNvbWUgYWRkaXRpb25hbCBoZWxwIChhcyBkbyBpdGVtcyAxIGFuZCAzIGFuZCBpdGVtcyA1IGFuZCA0KS4KCiMjIyMgSUNDIFBsb3RzCgpgYGB7ciBwbG90SUNDcywgaW5jbHVkZT1UUlVFfQpsYXZhYW5DYXRJdGVtUGxvdCA9IGZ1bmN0aW9uKGxhdk9iamVjdCwgdmFybmFtZSwgc2RzID0gMyl7CiAgb3V0cHV0ID0gaW5zcGVjdChvYmplY3QgPSBsYXZPYmplY3QsIHdoYXQgPSAiZXN0IikKICBpZiAoIXZhcm5hbWUgJWluJSByb3duYW1lcyhvdXRwdXQkbGFtYmRhKSkgc3RvcChwYXN0ZSh2YXJuYW1lLCAibm90IGZvdW5kIGluIGxhdmFhbiBvYmplY3QiKSkKICBpZiAoZGltKG91dHB1dCRsYW1iZGEpWzJdPjEpIHN0b3AoInBsb3RzIG9ubHkgZ2l2ZW4gZm9yIG9uZSBmYWN0b3IgbW9kZWxzIikKICAKICBpdGVtbG9hZGluZyA9IG91dHB1dCRsYW1iZGFbd2hpY2gocm93bmFtZXMob3V0cHV0JGxhbWJkYSkgPT0gdmFybmFtZSksMV0KICBpdGVtdGhyZXNob2xkcyA9IG91dHB1dCR0YXVbZ3JlcChwYXR0ZXJuID0gdmFybmFtZSwgeCA9IHJvd25hbWVzKG91dHB1dCR0YXUpKV0KICAKICBmYWN0b3JuYW1lID0gY29sbmFtZXMob3V0cHV0JGxhbWJkYSkKICBmYWN0b3JtZWFuID0gb3V0cHV0JGFscGhhW3doaWNoKHJvd25hbWVzKG91dHB1dCRhbHBoYSkgPT0gZmFjdG9ybmFtZSldCiAgZmFjdG9ydmFyID0gb3V0cHV0JHBzaVt3aGljaChyb3duYW1lcyhvdXRwdXQkcHNpKSA9PSBmYWN0b3JuYW1lKV0KICAKICBmYWN0b3JtaW4gPSBmYWN0b3JtZWFuIC0gMypzcXJ0KGZhY3RvcnZhcikKICBmYWN0b3JtYXggPSBmYWN0b3JtZWFuICsgMypzcXJ0KGZhY3RvcnZhcikKICAKICBmYWN0b3JYID0gc2VxKGZhY3Rvcm1pbiwgZmFjdG9ybWF4LCAuMDEpCiAgaXRlbWxvYyA9IHdoaWNoKGxhdk9iamVjdEBEYXRhQG92JG5hbWUgPT0gdmFybmFtZSkgICAgICAKICBpdGVtbGV2ZWxzID0gdW5saXN0KHN0cnNwbGl0KHggPSBsYXZPYmplY3RARGF0YUBvdiRsbmFtW2l0ZW1sb2NdLCBzcGxpdCA9ICJcXHwiKSkgIAogIGlmIChsZW5ndGgoaXRlbXRocmVzaG9sZHMpPjEpewoKICAgIAogICAgcGxvdGRhdGEgPSBOVUxMCiAgICBwbG90ZGF0YTIgPSBOVUxMCiAgICBpdGVtWSA9IE5VTEwKICAgIGl0ZW1ZMiA9IE5VTEwKICAgIGl0ZW1YID0gTlVMTAogICAgaXRlbVRleHQgPSBOVUxMCiAgICBmb3IgKGxldmVsIGluIDE6bGVuZ3RoKGl0ZW10aHJlc2hvbGRzKSl7CiAgICAgIAogICAgICBpdGVtWSA9IHBub3JtKHEgPSAtMSppdGVtdGhyZXNob2xkc1tsZXZlbF0gKyBpdGVtbG9hZGluZypmYWN0b3JYKQogICAgICBpdGVtWTIgPSBjYmluZChpdGVtWTIsIHBub3JtKHEgPSAtMSppdGVtdGhyZXNob2xkc1tsZXZlbF0gKyBpdGVtbG9hZGluZypmYWN0b3JYKSkKICAgICAgaXRlbVRleHQgPSBwYXN0ZTAoIlAoIiwgdmFybmFtZSwgIiA+ICIsIGl0ZW1sZXZlbHNbbGV2ZWxdLCAiKSIpCiAgICAgIGl0ZW1UZXh0MiA9IHBhc3RlMCgiUCgiLCB2YXJuYW1lLCAiID0gIiwgaXRlbWxldmVsc1tsZXZlbF0sICIpIikKICAgICAgcGxvdGRhdGEgPSByYmluZChwbG90ZGF0YSwgZGF0YS5mcmFtZShmYWN0b3IgPSBmYWN0b3JYLCBwcm9iID0gaXRlbVksIHBsb3QgPSBpdGVtVGV4dCkpCiAgICAgIAogICAgICBpZiAobGV2ZWwgPT0gMSl7CiAgICAgICAgcGxvdGRhdGEyID0gZGF0YS5mcmFtZShmYWN0b3IgPSBmYWN0b3JYLCBwbG90ID0gaXRlbVRleHQyLCBwcm9iID0gbWF0cml4KDEsIG5yb3cgPSBkaW0oaXRlbVkyKVsxXSwgbmNvbD0xKSAtIGl0ZW1ZMlssbGV2ZWxdKQogICAgICB9IGVsc2UgaWYgKGxldmVsID09IGxlbmd0aChpdGVtdGhyZXNob2xkcykpewogICAgICAgIHBsb3RkYXRhMiA9IHJiaW5kKHBsb3RkYXRhMiwgZGF0YS5mcmFtZShmYWN0b3IgPSBmYWN0b3JYLCBwbG90ID0gaXRlbVRleHQyLCBwcm9iID0gaXRlbVkyWyxsZXZlbC0xXSAtIGl0ZW1ZMlssbGV2ZWxdKSkKICAgICAgICBwbG90ZGF0YTIgPSByYmluZChwbG90ZGF0YTIsIGRhdGEuZnJhbWUoZmFjdG9yID0gZmFjdG9yWCwgcGxvdCA9IHBhc3RlMCgiUCgiLCB2YXJuYW1lLCAiID0gIiwgaXRlbWxldmVsc1tsZXZlbCsxXSwgIikiKSwgcHJvYiA9IGl0ZW1ZMlssbGV2ZWxdKSkgICAgICAgICAgICAgICAgICAKICAgICAgfSBlbHNlIHsKICAgICAgICBwbG90ZGF0YTIgPSByYmluZChwbG90ZGF0YTIsIGRhdGEuZnJhbWUoZmFjdG9yID0gZmFjdG9yWCwgcGxvdCA9IGl0ZW1UZXh0MiwgcHJvYiA9IGl0ZW1ZMlssbGV2ZWwtMV0gLSBpdGVtWTJbLGxldmVsXSkpCiAgICAgIH0KICAgICAgCiAgICB9CiAgICAKICAgIG5hbWVzKHBsb3RkYXRhKSA9IGMoZmFjdG9ybmFtZSAsICJQcm9iYWJpbGl0eSIsICJDdW11bGF0aXZlIikKICAgIGdncGxvdChkYXRhID0gcGxvdGRhdGEsIGFlc19zdHJpbmcoeCA9IGZhY3Rvcm5hbWUsIHkgPSAiUHJvYmFiaWxpdHkiLCBjb2xvdXIgPSAiQ3VtdWxhdGl2ZSIpKSArIGdlb21fbGluZShzaXplID0gMikKICAgIAogICAgbmFtZXMocGxvdGRhdGEyKSA9IGMoZmFjdG9ybmFtZSwgIlJlc3BvbnNlIiwgIlByb2JhYmlsaXR5IikKICAgIGdncGxvdChkYXRhID0gcGxvdGRhdGEyLCBhZXNfc3RyaW5nKHggPSBmYWN0b3JuYW1lLCB5ID0gIlByb2JhYmlsaXR5IiwgY29sb3VyID0gIlJlc3BvbnNlIikpICsgZ2VvbV9saW5lKHNpemUgPSAyKQogIH0gZWxzZSB7CiAgICAKICAgIGl0ZW1ZID0gcG5vcm0ocSA9IC0xKml0ZW10aHJlc2hvbGRzWzFdICsgaXRlbWxvYWRpbmcqZmFjdG9yWCkKICAgIGl0ZW1UZXh0MiA9IHBhc3RlMCgiUCgiLCB2YXJuYW1lLCAiID0gIiwgaXRlbWxldmVsc1syXSwgIikiKQogICAgcGxvdGRhdGEgPSBkYXRhLmZyYW1lKGZhY3RvciA9IGZhY3RvclgsIHByb2IgPSBpdGVtWSwgcGxvdCA9IGl0ZW1UZXh0MikKICAgIAogICAgbmFtZXMocGxvdGRhdGEpID0gYyhmYWN0b3JuYW1lICwgIlByb2JhYmlsaXR5IiwgIlJlc3BvbnNlIikKICAgIGdncGxvdChkYXRhID0gcGxvdGRhdGEsIGFlc19zdHJpbmcoeCA9IGZhY3Rvcm5hbWUsIHkgPSAiUHJvYmFiaWxpdHkiLCBjb2xvdXIgPSAiUmVzcG9uc2UiKSkgKyBnZW9tX2xpbmUoc2l6ZSA9IDIpCiAgICAKICB9Cn0KCmxhdmFhbkNhdEl0ZW1QbG90KGxhdk9iamVjdCA9IG1vZGVsMlBFc3RpbWF0ZXMsIHZhcm5hbWUgPSAiZGlhMiIsIHNkcyA9IDMpCmBgYAoKIyMjIyBDb252ZXJzaW9uIHRvIElSVCBQYXJhbXRlcml6YXRpb24gKERpc2NyaW1pbmF0aW9uL0RpZmZpY3VsdHkpCgpgYGB7ciBjb252ZXJ0LCBpbmNsdWRlPVRSVUV9CmNvbnZlcnRUaGV0YTJJUlQgPSBmdW5jdGlvbihsYXZPYmplY3QpewogIGlmICghbGF2T2JqZWN0QE9wdGlvbnMkcGFyYW1ldGVyaXphdGlvbiA9PSAidGhldGEiKSBzdG9wKCJ5b3VyIG1vZGVsIGlzIG5vdCBlc3RpbWF0ZWQgd2l0aCBwYXJhbWV0ZXJpemF0aW9uPSd0aGV0YSciKQogIAogIG91dHB1dCA9IGluc3BlY3Qob2JqZWN0ID0gbGF2T2JqZWN0LCB3aGF0ID0gImVzdCIpCiAgaWYgKG5jb2wob3V0cHV0JGxhbWJkYSk+MSkgc3RvcCgiSVJUIGNvbnZlcnNpb24gaXMgb25seSB2YWxpZCBmb3Igb25lIGRpbWVuc2lvbmFsIGZhY3RvciBtb2RlbHMuIFlvdXIgbW9kZWwgaGFzIG1vcmUgdGhhbiBvbmUgZGltZW5zaW9uLiIpCiAgCiAgYSA9IG91dHB1dCRsYW1iZGEKICBiID0gb3V0cHV0JHRhdS9vdXRwdXQkbGFtYmRhCiAgcmV0dXJuKGxpc3QoYSA9IGEsIGI9YikpCn0KCmNvbnZlcnRUaGV0YTJJUlQobGF2T2JqZWN0ID0gbW9kZWwyUEVzdGltYXRlcykKYGBgCgojIyMjIE9uZS1QYXJhbWV0ZXIgTm9ybWFsIE9naXZlIE1vZGVsCgpUbyBlc3RpbWF0ZSB0aGUgMVBOTyBtb2RlbCBpbiBgbGF2YWFuYCwgd2UgdXNlIHRoZSBmb2xsb3dpbmcgc3ludGF4LiBUaGUgc3VtbWFyeSwgcmVzaWR1YWxzLCBhbmQgbW9kaWZpY2F0aW9uIGluZGljZXMgYXJlIGRpc3BsYXllZCBzdWJzZXF1ZW50bHkuIApgYGB7ciBsYXZhYW4xUEwsIGluY2x1ZGU9VFJVRX0KbW9kZWwxUFN5bnRheCA9ICIKCiMgbG9hZGluZ3MvZGlzY3JpbWluYXRpb24gcGFyYW1ldGVyczoKSUFETCA9fiBsb2FkaW5nKmRpYTEgKyBsb2FkaW5nKmRpYTIgKyBsb2FkaW5nKmRpYTMgKyBsb2FkaW5nKmRpYTQgKyBsb2FkaW5nKmRpYTUgKyBsb2FkaW5nKmRpYTYgKyBsb2FkaW5nKmRpYTcKCiMgdGhyZXNoaG9sZHMgdXNlIHRoZSB8IG9wZXJhdG9yIGFuZCBzdGFydCBhdCB2YWx1ZSAxIGFmdGVyIHQ6CmRpYTEgfCB0MTsgZGlhMiB8IHQxOyBkaWEzIHwgdDE7IGRpYTQgfCB0MTsgZGlhNSB8IHQxOyBkaWE2IHwgdDE7IGRpYTcgfCB0MTsgCgojIGZhY3RvciBtZWFuOgpJQURMIH4gMDsKCiMgZmFjdG9yIHZhcmlhbmNlOgpJQURMIH5+IDEqSUFETAoKIgoKbW9kZWwxUEVzdGltYXRlcyA9IHNlbShtb2RlbCA9IG1vZGVsMVBTeW50YXgsIGRhdGEgPSBpYWRsRGF0YSwgb3JkZXJlZCA9IGMoImRpYTEiLCAiZGlhMiIsICJkaWEzIiwgImRpYTQiLCAiZGlhNSIsICJkaWE2IiwgImRpYTciKSwKICAgICAgICAgICAgICAgICAgICAgICBtaW1pYyA9ICJNcGx1cyIsIGVzdGltYXRvciA9ICJXTFNNViIsIHN0ZC5sdiA9IFRSVUUsIHBhcmFtZXRlcml6YXRpb24gPSAidGhldGEiKQpzdW1tYXJ5KG1vZGVsMVBFc3RpbWF0ZXMsIGZpdC5tZWFzdXJlcyA9IFRSVUUsIHJzcXVhcmUgPSBUUlVFLCBzdGFuZGFyZGl6ZWQgPSBUUlVFKQpyZXNpZChvYmplY3QgPSBtb2RlbDFQRXN0aW1hdGVzKQptb2RpZmljYXRpb25pbmRpY2VzKG1vZGVsMVBFc3RpbWF0ZXMsIHNvcnQuID0gVFJVRSkKYGBgCgpUbyBjb21wYXJlIG1vZGVscywgd2UgdXNlIHRoZSBgYW5vdmEoKWAgZnVuY3Rpb24sIHdoaWNoIHVzZXMgdGhlIGNvcnJlY3QgdGVzdCBmb3IgdXMuIEhlcmUgd2Ugc2VlIHRoZSAyUEwgaXMgcHJlZmVycmVkIHRvIHRoZSAxUEwsIGFnYWluLgoKYGBge3IgbW9kZWxjb21wYXJlLCBpbmNsdWRlPVRSVUV9CmFub3ZhKG1vZGVsMVBFc3RpbWF0ZXMsIG1vZGVsMlBFc3RpbWF0ZXMpCmBgYA==