Example data: 634 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(ggplot2) == FALSE){
  install.packages("ggplot2")
}
library(ggplot2)

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("cia", 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,]

Graded Response Model (2PL-ish version) using WLSMV probit model

grm2Psyntax = "
IADL =~ cia1 + cia2 + cia3 + cia4 + cia5 + cia6 + cia7
"

grm2Pestimates = cfa(model = grm2Psyntax, data = iadlData, ordered = c("cia1", "cia2", "cia3", "cia4", "cia5", "cia6", "cia7"), std.lv = TRUE, parameterization="theta")
summary(grm2Pestimates, fit.measures = TRUE, rsquare = TRUE, standardized = TRUE)

Parameter estimates under WLSMV Theta Parameterization (Probit) for the 2PL version of polytomous responses

Factor loadings:

Latent Variables: Estimate Std.Err z-value P(>|z|) Std.lv Std.all IADL =~
cia1 3.450 0.301 11.470 0.000 3.450 0.960 cia2 3.620 0.507 7.139 0.000 3.620 0.964 cia3 3.026 0.292 10.361 0.000 3.026 0.949 cia4 3.310 0.300 11.030 0.000 3.310 0.957 cia5 2.360 0.190 12.412 0.000 2.360 0.921 cia6 1.943 0.182 10.680 0.000 1.943 0.889 cia7 1.044 0.135 7.724 0.000 1.044 0.722

Thresholds:
               Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
cia1|t1          -4.946    0.385  -12.838    0.000   -4.946   -1.377
cia1|t2          -3.636    0.324  -11.233    0.000   -3.636   -1.012
cia1|t3          -0.900    0.219   -4.111    0.000   -0.900   -0.251
cia2|t1          -5.502    0.658   -8.356    0.000   -5.502   -1.465
cia2|t2          -4.797    0.605   -7.928    0.000   -4.797   -1.277
cia2|t3          -2.973    0.463   -6.421    0.000   -2.973   -0.792
cia3|t1          -4.426    0.362  -12.226    0.000   -4.426   -1.389
cia3|t2          -3.702    0.331  -11.175    0.000   -3.702   -1.162
cia3|t3          -2.050    0.262   -7.821    0.000   -2.050   -0.643
cia4|t1          -4.490    0.355  -12.647    0.000   -4.490   -1.298
cia4|t2          -3.100    0.282  -10.977    0.000   -3.100   -0.897
cia4|t3          -1.098    0.225   -4.881    0.000   -1.098   -0.317
cia5|t1          -4.098    0.281  -14.607    0.000   -4.098   -1.599
cia5|t2          -2.195    0.190  -11.578    0.000   -2.195   -0.856
cia5|t3          -0.618    0.151   -4.089    0.000   -0.618   -0.241
cia6|t1          -3.605    0.255  -14.125    0.000   -3.605   -1.650
cia6|t2          -2.558    0.203  -12.628    0.000   -2.558   -1.171
cia6|t3          -1.531    0.171   -8.974    0.000   -1.531   -0.701
cia7|t1          -3.311    0.268  -12.345    0.000   -3.311   -2.291
cia7|t2          -2.695    0.199  -13.556    0.000   -2.695   -1.864
cia7|t3          -1.788    0.145  -12.304    0.000   -1.788   -1.237

Calculations

Logit = 1.7*probit, or Probit = Logit/1.7

IFA model: Probit(y=1) = –threshold + loading(Theta)

Threshold = expected probit of (y=0) for someone with Theta=0

When *-1, threshold \(\rightarrow\) intercept: expected probit for (y=1) instead

Loading = regression of item probit on Theta

For 4-category responses, the sub-models look like this: Probit(y= 0 vs 123) = -threshold$1 + loading(Theta) Probit(y= 01 vs 23) = -threshold$2 + loading(Theta) Probit y= 012 vs 3) = -threshold$3 + loading(Theta)

IRT model: Probit(y) = a(theta – difficulty) a = discrimination (rescaled slope) = loading b = difficulty (location on latent metric) = threshold/loading

For 4-category responses, the sub-models look like this: $1 Probit(y= 0 vs 123) = a(theta – difficulty$1) $2 Probit(y= 01 vs 23) = a(theta – difficulty$2) $3 Probit(y= 012 vs 3) = a(theta – difficulty$3)

Model Fit

LOCAL FIT VIA RAW RESIDUAL CORRELATIONS LEFTOVER POLYCHORIC CORRELATION (HOW FAR OFF FROM DATA)

With raw residuals, the scale is that of correlation (different from normalized)

resid(object = grm2Pestimates)

Item 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[1], ")")
    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 = grm2Pestimates, varname = "cia4", sds = 3)

Graded Response Model (1PL-ish version) using WLSMV probit model

We can also set the item discriminations equal for a 1PL-like model:

grm1Psyntax = "
IADL =~ a*cia1 + a*cia2 + a*cia3 + a*cia4 + a*cia5 + a*cia6 + a*cia7
"

grm1Pestimates = cfa(model = grm1Psyntax, data = iadlData, ordered = c("cia1", "cia2", "cia3", "cia4", "cia5", "cia6", "cia7"), std.lv = TRUE, parameterization="theta")
summary(grm1Pestimates, fit.measures = TRUE, rsquare = TRUE, standardized = TRUE)

Similarly, we can use the anova() function to compare model fit:

anova(grm1Pestimates, grm2Pestimates)
LS0tCnRpdGxlOiAiUFNRRiA2MjQ5OiBFeGFtcGxlIDUgKElSVC9JRkEpIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpFeGFtcGxlIGRhdGE6IDYzNCBvbGRlciBhZHVsdHMgKGFnZSA4MC0xMDApIHNlbGYtcmVwb3J0aW5nIG9uIDcgaXRlbXMgYXNzZXNzaW5nIHRoZSBJbnN0cnVtZW50YWwgQWN0aXZpdGllcyBvZiBEYWlseSBMaXZpbmcgKElBREwpIGFzIGZvbGxvd3M6CgoxLiAgSG91c2V3b3JrIChjbGVhbmluZyBhbmQgbGF1bmRyeSk6IDE9NjQlCjIuICBCZWRtYWtpbmc6IDE9ODQlCjMuICBDb29raW5nOiAxPTc3JQo0LiAgRXZlcnlkYXkgc2hvcHBpbmc6IDE9NjYlCjUuICBHZXR0aW5nIHRvIHBsYWNlcyBvdXRzaWRlIG9mIHdhbGtpbmcgZGlzdGFuY2U6IDE9NjUlCjYuICBIYW5kbGluZyBiYW5raW5nIGFuZCBvdGhlciBidXNpbmVzczogMT03MyVcCjcuICBVc2luZyB0aGUgdGVsZXBob25lIDE9OTQlCgpUd28gdmVyc2lvbnMgb2YgYSByZXNwb25zZSBmb3JtYXQgd2VyZSBhdmFpbGFibGU6CgpCaW5hcnkgLVw+IDAgPSDigJxuZWVkcyBoZWxw4oCdLCAxID0g4oCcZG9lcyBub3QgbmVlZCBoZWxw4oCdCgpDYXRlZ29yaWNhbCAtXD4gMCA9IOKAnGNhbuKAmXQgZG8gaXTigJ0sIDE94oCdYmlnIHByb2JsZW1z4oCdLCAyPeKAnXNvbWUgcHJvYmxlbXPigJ0sIDM94oCdbm8gcHJvYmxlbXPigJ0KCkhpZ2hlciBzY29yZXMgaW5kaWNhdGUgZ3JlYXRlciBmdW5jdGlvbi4gV2Ugd2lsbCBsb29rIGF0IGVhY2ggcmVzcG9uc2UgZm9ybWF0IGluIHR1cm4uCgojIyMgUGFja2FnZSBJbnN0YWxsYXRpb24gYW5kIExvYWRpbmcKCmBgYHtyIHNldHVwLCBpbmNsdWRlPVRSVUV9CmlmIChyZXF1aXJlKGxhdmFhbikgPT0gRkFMU0UpewogIGluc3RhbGwucGFja2FnZXMoImxhdmFhbiIpCn0KbGlicmFyeShsYXZhYW4pCgppZiAocmVxdWlyZShnZ3Bsb3QyKSA9PSBGQUxTRSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCn0KbGlicmFyeShnZ3Bsb3QyKQpgYGAKCiMjIyBEYXRhIEltcG9ydCBpbnRvIFIKClRoZSBkYXRhIGFyZSBpbiBhIHRleHQgZmlsZSBuYW1lZCBgYWRsLmRhdGAgb3JpZ25hbGx5IHVzZWQgaW4gTXBsdXMgKHNvIG5vIGNvbHVtbiBuYW1lcyB3ZXJlIGluY2x1ZGVkIGF0IHRoZSB0b3Agb2YgdGhlIGZpbGUpLiBUaGUgZmlsZSBjb250YWlucyBtb3JlIGl0ZW1zIHRoYW4gd2Ugd2lsbCB1c2UsIHNvIHdlIHNlbGVjdCBvbmx5IGl0ZW1zIGFib3ZlIGZyb20gdGhlIHdob2xlIGZpbGUuCgpgYGB7ciBkYXRhLCBpbmNsdWRlPVRSVUV9CgojcmVhZCBpbiBkYXRhIGZpbGUgKE1wbHVzIGZpbGUgZm9ybWF0IHNvIGhhdmluZyB0byBsYWJlbCBjb2x1bW5zKQphZGxEYXRhID0gcmVhZC50YWJsZShmaWxlID0gImFkbC5kYXQiLCBoZWFkZXIgPSBGQUxTRSwgbmEuc3RyaW5ncyA9ICIuIiwgY29sLm5hbWVzID0gYygiY2FzZSIsIHBhc3RlMCgiZHBhIiwgMToxNCksIHBhc3RlMCgiZGlhIiwgMTo3KSwgcGFzdGUwKCJjcGEiLCAxOjE0KSwgcGFzdGUwKCJjaWEiLCAxOjcpKSkKCiNzZWxlY3QgU2l0dWF0aW9ucyBpdGVtcyBhbmQgUGVyc29uSUQgdmFyaWFibGVzCmlhZGxEYXRhSW5pdCA9IGFkbERhdGFbYyhwYXN0ZTAoImNpYSIsIDE6NykpXQoKI3JlbW92ZSBjYXNlcyB3aXRoIGFsbCBpdGVtcyBtaXNzaW5nCnJlbW92ZUNhc2VzID0gd2hpY2goYXBwbHkoWCA9IGlhZGxEYXRhSW5pdCwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24gKHgpeyBsZW5ndGgod2hpY2goaXMubmEoeCkpKX0pID09IDcpCgppYWRsRGF0YSA9IGlhZGxEYXRhSW5pdFstcmVtb3ZlQ2FzZXMsXQpgYGAKCiMjIyBHcmFkZWQgUmVzcG9uc2UgTW9kZWwgKDJQTC1pc2ggdmVyc2lvbikgdXNpbmcgV0xTTVYgcHJvYml0IG1vZGVsCgpgYGB7ciBncm0ycCwgaW5jbHVkZT1UUlVFfQpncm0yUHN5bnRheCA9ICIKSUFETCA9fiBjaWExICsgY2lhMiArIGNpYTMgKyBjaWE0ICsgY2lhNSArIGNpYTYgKyBjaWE3CiIKCmdybTJQZXN0aW1hdGVzID0gY2ZhKG1vZGVsID0gZ3JtMlBzeW50YXgsIGRhdGEgPSBpYWRsRGF0YSwgb3JkZXJlZCA9IGMoImNpYTEiLCAiY2lhMiIsICJjaWEzIiwgImNpYTQiLCAiY2lhNSIsICJjaWE2IiwgImNpYTciKSwgc3RkLmx2ID0gVFJVRSwgcGFyYW1ldGVyaXphdGlvbj0idGhldGEiKQpzdW1tYXJ5KGdybTJQZXN0aW1hdGVzLCBmaXQubWVhc3VyZXMgPSBUUlVFLCByc3F1YXJlID0gVFJVRSwgc3RhbmRhcmRpemVkID0gVFJVRSkKCmBgYAoKIyMjIyBQYXJhbWV0ZXIgZXN0aW1hdGVzIHVuZGVyIFdMU01WIFRoZXRhIFBhcmFtZXRlcml6YXRpb24gKFByb2JpdCkgZm9yIHRoZSAyUEwgdmVyc2lvbiBvZiBwb2x5dG9tb3VzIHJlc3BvbnNlcwoKIyMjIyMgRmFjdG9yIGxvYWRpbmdzOgoKTGF0ZW50IFZhcmlhYmxlczogRXN0aW1hdGUgU3RkLkVyciB6LXZhbHVlIFAoXD5cfHpcfCkgU3RkLmx2IFN0ZC5hbGwgSUFETCA9XH5cCmNpYTEgMy40NTAgMC4zMDEgMTEuNDcwIDAuMDAwIDMuNDUwIDAuOTYwIGNpYTIgMy42MjAgMC41MDcgNy4xMzkgMC4wMDAgMy42MjAgMC45NjQgY2lhMyAzLjAyNiAwLjI5MiAxMC4zNjEgMC4wMDAgMy4wMjYgMC45NDkgY2lhNCAzLjMxMCAwLjMwMCAxMS4wMzAgMC4wMDAgMy4zMTAgMC45NTcgY2lhNSAyLjM2MCAwLjE5MCAxMi40MTIgMC4wMDAgMi4zNjAgMC45MjEgY2lhNiAxLjk0MyAwLjE4MiAxMC42ODAgMC4wMDAgMS45NDMgMC44ODkgY2lhNyAxLjA0NCAwLjEzNSA3LjcyNCAwLjAwMCAxLjA0NCAwLjcyMgoKIyMjIyMgVGhyZXNob2xkczoKCmBgYCAgICAgICAgIAogICAgICAgICAgICAgICBFc3RpbWF0ZSAgU3RkLkVyciAgei12YWx1ZSAgUCg+fHp8KSAgIFN0ZC5sdiAgU3RkLmFsbApjaWExfHQxICAgICAgICAgIC00Ljk0NiAgICAwLjM4NSAgLTEyLjgzOCAgICAwLjAwMCAgIC00Ljk0NiAgIC0xLjM3NwpjaWExfHQyICAgICAgICAgIC0zLjYzNiAgICAwLjMyNCAgLTExLjIzMyAgICAwLjAwMCAgIC0zLjYzNiAgIC0xLjAxMgpjaWExfHQzICAgICAgICAgIC0wLjkwMCAgICAwLjIxOSAgIC00LjExMSAgICAwLjAwMCAgIC0wLjkwMCAgIC0wLjI1MQpjaWEyfHQxICAgICAgICAgIC01LjUwMiAgICAwLjY1OCAgIC04LjM1NiAgICAwLjAwMCAgIC01LjUwMiAgIC0xLjQ2NQpjaWEyfHQyICAgICAgICAgIC00Ljc5NyAgICAwLjYwNSAgIC03LjkyOCAgICAwLjAwMCAgIC00Ljc5NyAgIC0xLjI3NwpjaWEyfHQzICAgICAgICAgIC0yLjk3MyAgICAwLjQ2MyAgIC02LjQyMSAgICAwLjAwMCAgIC0yLjk3MyAgIC0wLjc5MgpjaWEzfHQxICAgICAgICAgIC00LjQyNiAgICAwLjM2MiAgLTEyLjIyNiAgICAwLjAwMCAgIC00LjQyNiAgIC0xLjM4OQpjaWEzfHQyICAgICAgICAgIC0zLjcwMiAgICAwLjMzMSAgLTExLjE3NSAgICAwLjAwMCAgIC0zLjcwMiAgIC0xLjE2MgpjaWEzfHQzICAgICAgICAgIC0yLjA1MCAgICAwLjI2MiAgIC03LjgyMSAgICAwLjAwMCAgIC0yLjA1MCAgIC0wLjY0MwpjaWE0fHQxICAgICAgICAgIC00LjQ5MCAgICAwLjM1NSAgLTEyLjY0NyAgICAwLjAwMCAgIC00LjQ5MCAgIC0xLjI5OApjaWE0fHQyICAgICAgICAgIC0zLjEwMCAgICAwLjI4MiAgLTEwLjk3NyAgICAwLjAwMCAgIC0zLjEwMCAgIC0wLjg5NwpjaWE0fHQzICAgICAgICAgIC0xLjA5OCAgICAwLjIyNSAgIC00Ljg4MSAgICAwLjAwMCAgIC0xLjA5OCAgIC0wLjMxNwpjaWE1fHQxICAgICAgICAgIC00LjA5OCAgICAwLjI4MSAgLTE0LjYwNyAgICAwLjAwMCAgIC00LjA5OCAgIC0xLjU5OQpjaWE1fHQyICAgICAgICAgIC0yLjE5NSAgICAwLjE5MCAgLTExLjU3OCAgICAwLjAwMCAgIC0yLjE5NSAgIC0wLjg1NgpjaWE1fHQzICAgICAgICAgIC0wLjYxOCAgICAwLjE1MSAgIC00LjA4OSAgICAwLjAwMCAgIC0wLjYxOCAgIC0wLjI0MQpjaWE2fHQxICAgICAgICAgIC0zLjYwNSAgICAwLjI1NSAgLTE0LjEyNSAgICAwLjAwMCAgIC0zLjYwNSAgIC0xLjY1MApjaWE2fHQyICAgICAgICAgIC0yLjU1OCAgICAwLjIwMyAgLTEyLjYyOCAgICAwLjAwMCAgIC0yLjU1OCAgIC0xLjE3MQpjaWE2fHQzICAgICAgICAgIC0xLjUzMSAgICAwLjE3MSAgIC04Ljk3NCAgICAwLjAwMCAgIC0xLjUzMSAgIC0wLjcwMQpjaWE3fHQxICAgICAgICAgIC0zLjMxMSAgICAwLjI2OCAgLTEyLjM0NSAgICAwLjAwMCAgIC0zLjMxMSAgIC0yLjI5MQpjaWE3fHQyICAgICAgICAgIC0yLjY5NSAgICAwLjE5OSAgLTEzLjU1NiAgICAwLjAwMCAgIC0yLjY5NSAgIC0xLjg2NApjaWE3fHQzICAgICAgICAgIC0xLjc4OCAgICAwLjE0NSAgLTEyLjMwNCAgICAwLjAwMCAgIC0xLjc4OCAgIC0xLjIzNwpgYGAKCiMjIyMgQ2FsY3VsYXRpb25zCgpMb2dpdCA9IDEuN1wqcHJvYml0LCBvciBQcm9iaXQgPSBMb2dpdC8xLjcKCklGQSBtb2RlbDogUHJvYml0KHk9MSkgPSDigJN0aHJlc2hvbGQgKyBsb2FkaW5nKFRoZXRhKQoKVGhyZXNob2xkID0gZXhwZWN0ZWQgcHJvYml0IG9mICh5PTApIGZvciBzb21lb25lIHdpdGggVGhldGE9MAoKV2hlbiBcKi0xLCB0aHJlc2hvbGQgJFxyaWdodGFycm93JCBpbnRlcmNlcHQ6IGV4cGVjdGVkIHByb2JpdCBmb3IgKHk9MSkgaW5zdGVhZAoKTG9hZGluZyA9IHJlZ3Jlc3Npb24gb2YgaXRlbSBwcm9iaXQgb24gVGhldGEKCkZvciA0LWNhdGVnb3J5IHJlc3BvbnNlcywgdGhlIHN1Yi1tb2RlbHMgbG9vayBsaWtlIHRoaXM6IFByb2JpdCh5PSAwIHZzIDEyMykgPSAtdGhyZXNob2xkXCQxICsgbG9hZGluZyhUaGV0YSkgUHJvYml0KHk9IDAxIHZzIDIzKSA9IC10aHJlc2hvbGRcJDIgKyBsb2FkaW5nKFRoZXRhKSBQcm9iaXQgeT0gMDEyIHZzIDMpID0gLXRocmVzaG9sZFwkMyArIGxvYWRpbmcoVGhldGEpCgpJUlQgbW9kZWw6IFByb2JpdCh5KSA9IGEodGhldGEg4oCTIGRpZmZpY3VsdHkpIGEgPSBkaXNjcmltaW5hdGlvbiAocmVzY2FsZWQgc2xvcGUpID0gbG9hZGluZyBiID0gZGlmZmljdWx0eSAobG9jYXRpb24gb24gbGF0ZW50IG1ldHJpYykgPSB0aHJlc2hvbGQvbG9hZGluZwoKRm9yIDQtY2F0ZWdvcnkgcmVzcG9uc2VzLCB0aGUgc3ViLW1vZGVscyBsb29rIGxpa2UgdGhpczogXCQxIFByb2JpdCh5PSAwIHZzIDEyMykgPSBhKHRoZXRhIOKAkyBkaWZmaWN1bHR5XCQxKSBcJDIgUHJvYml0KHk9IDAxIHZzIDIzKSA9IGEodGhldGEg4oCTIGRpZmZpY3VsdHlcJDIpIFwkMyBQcm9iaXQoeT0gMDEyIHZzIDMpID0gYSh0aGV0YSDigJMgZGlmZmljdWx0eVwkMykKCiMjIyMgTW9kZWwgRml0CgpMT0NBTCBGSVQgVklBIFJBVyBSRVNJRFVBTCBDT1JSRUxBVElPTlMgTEVGVE9WRVIgUE9MWUNIT1JJQyBDT1JSRUxBVElPTiAoSE9XIEZBUiBPRkYgRlJPTSBEQVRBKQoKV2l0aCByYXcgcmVzaWR1YWxzLCB0aGUgc2NhbGUgaXMgdGhhdCBvZiBjb3JyZWxhdGlvbiAoZGlmZmVyZW50IGZyb20gbm9ybWFsaXplZCkKCmBgYHtyIGdybTJwZml0LCBpbmNsdWRlPVRSVUV9CnJlc2lkKG9iamVjdCA9IGdybTJQZXN0aW1hdGVzKQpgYGAKCiMjIyMgSXRlbSBQbG90cwoKYGBge3IgaXRlbXBsb3QsIGluY2x1ZGU9VFJVRX0KbGF2YWFuQ2F0SXRlbVBsb3QgPSBmdW5jdGlvbihsYXZPYmplY3QsIHZhcm5hbWUsIHNkcyA9IDMpewogIG91dHB1dCA9IGluc3BlY3Qob2JqZWN0ID0gbGF2T2JqZWN0LCB3aGF0ID0gImVzdCIpCiAgaWYgKCF2YXJuYW1lICVpbiUgcm93bmFtZXMob3V0cHV0JGxhbWJkYSkpIHN0b3AocGFzdGUodmFybmFtZSwgIm5vdCBmb3VuZCBpbiBsYXZhYW4gb2JqZWN0IikpCiAgaWYgKGRpbShvdXRwdXQkbGFtYmRhKVsyXT4xKSBzdG9wKCJwbG90cyBvbmx5IGdpdmVuIGZvciBvbmUgZmFjdG9yIG1vZGVscyIpCiAgCiAgaXRlbWxvYWRpbmcgPSBvdXRwdXQkbGFtYmRhW3doaWNoKHJvd25hbWVzKG91dHB1dCRsYW1iZGEpID09IHZhcm5hbWUpLDFdCiAgaXRlbXRocmVzaG9sZHMgPSBvdXRwdXQkdGF1W2dyZXAocGF0dGVybiA9IHZhcm5hbWUsIHggPSByb3duYW1lcyhvdXRwdXQkdGF1KSldCiAgCiAgZmFjdG9ybmFtZSA9IGNvbG5hbWVzKG91dHB1dCRsYW1iZGEpCiAgZmFjdG9ybWVhbiA9IG91dHB1dCRhbHBoYVt3aGljaChyb3duYW1lcyhvdXRwdXQkYWxwaGEpID09IGZhY3Rvcm5hbWUpXQogIGZhY3RvcnZhciA9IG91dHB1dCRwc2lbd2hpY2gocm93bmFtZXMob3V0cHV0JHBzaSkgPT0gZmFjdG9ybmFtZSldCiAgCiAgZmFjdG9ybWluID0gZmFjdG9ybWVhbiAtIDMqc3FydChmYWN0b3J2YXIpCiAgZmFjdG9ybWF4ID0gZmFjdG9ybWVhbiArIDMqc3FydChmYWN0b3J2YXIpCiAgCiAgZmFjdG9yWCA9IHNlcShmYWN0b3JtaW4sIGZhY3Rvcm1heCwgLjAxKQogIGl0ZW1sb2MgPSB3aGljaChsYXZPYmplY3RARGF0YUBvdiRuYW1lID09IHZhcm5hbWUpICAgICAgCiAgaXRlbWxldmVscyA9IHVubGlzdChzdHJzcGxpdCh4ID0gbGF2T2JqZWN0QERhdGFAb3YkbG5hbVtpdGVtbG9jXSwgc3BsaXQgPSAiXFx8IikpICAKICBpZiAobGVuZ3RoKGl0ZW10aHJlc2hvbGRzKT4xKXsKCiAgICAKICAgIHBsb3RkYXRhID0gTlVMTAogICAgcGxvdGRhdGEyID0gTlVMTAogICAgaXRlbVkgPSBOVUxMCiAgICBpdGVtWTIgPSBOVUxMCiAgICBpdGVtWCA9IE5VTEwKICAgIGl0ZW1UZXh0ID0gTlVMTAogICAgZm9yIChsZXZlbCBpbiAxOmxlbmd0aChpdGVtdGhyZXNob2xkcykpewogICAgICAKICAgICAgaXRlbVkgPSBwbm9ybShxID0gLTEqaXRlbXRocmVzaG9sZHNbbGV2ZWxdICsgaXRlbWxvYWRpbmcqZmFjdG9yWCkKICAgICAgaXRlbVkyID0gY2JpbmQoaXRlbVkyLCBwbm9ybShxID0gLTEqaXRlbXRocmVzaG9sZHNbbGV2ZWxdICsgaXRlbWxvYWRpbmcqZmFjdG9yWCkpCiAgICAgIGl0ZW1UZXh0ID0gcGFzdGUwKCJQKCIsIHZhcm5hbWUsICIgPiAiLCBpdGVtbGV2ZWxzW2xldmVsXSwgIikiKQogICAgICBpdGVtVGV4dDIgPSBwYXN0ZTAoIlAoIiwgdmFybmFtZSwgIiA9ICIsIGl0ZW1sZXZlbHNbbGV2ZWxdLCAiKSIpCiAgICAgIHBsb3RkYXRhID0gcmJpbmQocGxvdGRhdGEsIGRhdGEuZnJhbWUoZmFjdG9yID0gZmFjdG9yWCwgcHJvYiA9IGl0ZW1ZLCBwbG90ID0gaXRlbVRleHQpKQogICAgICAKICAgICAgaWYgKGxldmVsID09IDEpewogICAgICAgIHBsb3RkYXRhMiA9IGRhdGEuZnJhbWUoZmFjdG9yID0gZmFjdG9yWCwgcGxvdCA9IGl0ZW1UZXh0MiwgcHJvYiA9IG1hdHJpeCgxLCBucm93ID0gZGltKGl0ZW1ZMilbMV0sIG5jb2w9MSkgLSBpdGVtWTJbLGxldmVsXSkKICAgICAgfSBlbHNlIGlmIChsZXZlbCA9PSBsZW5ndGgoaXRlbXRocmVzaG9sZHMpKXsKICAgICAgICBwbG90ZGF0YTIgPSByYmluZChwbG90ZGF0YTIsIGRhdGEuZnJhbWUoZmFjdG9yID0gZmFjdG9yWCwgcGxvdCA9IGl0ZW1UZXh0MiwgcHJvYiA9IGl0ZW1ZMlssbGV2ZWwtMV0gLSBpdGVtWTJbLGxldmVsXSkpCiAgICAgICAgcGxvdGRhdGEyID0gcmJpbmQocGxvdGRhdGEyLCBkYXRhLmZyYW1lKGZhY3RvciA9IGZhY3RvclgsIHBsb3QgPSBwYXN0ZTAoIlAoIiwgdmFybmFtZSwgIiA9ICIsIGl0ZW1sZXZlbHNbbGV2ZWwrMV0sICIpIiksIHByb2IgPSBpdGVtWTJbLGxldmVsXSkpICAgICAgICAgICAgICAgICAgCiAgICAgIH0gZWxzZSB7CiAgICAgICAgcGxvdGRhdGEyID0gcmJpbmQocGxvdGRhdGEyLCBkYXRhLmZyYW1lKGZhY3RvciA9IGZhY3RvclgsIHBsb3QgPSBpdGVtVGV4dDIsIHByb2IgPSBpdGVtWTJbLGxldmVsLTFdIC0gaXRlbVkyWyxsZXZlbF0pKQogICAgICB9CiAgICAgIAogICAgfQogICAgCiAgICBuYW1lcyhwbG90ZGF0YSkgPSBjKGZhY3Rvcm5hbWUgLCAiUHJvYmFiaWxpdHkiLCAiQ3VtdWxhdGl2ZSIpCiAgICBnZ3Bsb3QoZGF0YSA9IHBsb3RkYXRhLCBhZXNfc3RyaW5nKHggPSBmYWN0b3JuYW1lLCB5ID0gIlByb2JhYmlsaXR5IiwgY29sb3VyID0gIkN1bXVsYXRpdmUiKSkgKyBnZW9tX2xpbmUoc2l6ZSA9IDIpCiAgICAKICAgIG5hbWVzKHBsb3RkYXRhMikgPSBjKGZhY3Rvcm5hbWUsICJSZXNwb25zZSIsICJQcm9iYWJpbGl0eSIpCiAgICBnZ3Bsb3QoZGF0YSA9IHBsb3RkYXRhMiwgYWVzX3N0cmluZyh4ID0gZmFjdG9ybmFtZSwgeSA9ICJQcm9iYWJpbGl0eSIsIGNvbG91ciA9ICJSZXNwb25zZSIpKSArIGdlb21fbGluZShzaXplID0gMikKICB9IGVsc2UgewogICAgCiAgICBpdGVtWSA9IHBub3JtKHEgPSAtMSppdGVtdGhyZXNob2xkc1sxXSArIGl0ZW1sb2FkaW5nKmZhY3RvclgpCiAgICBpdGVtVGV4dDIgPSBwYXN0ZTAoIlAoIiwgdmFybmFtZSwgIiA9ICIsIGl0ZW1sZXZlbHNbMV0sICIpIikKICAgIHBsb3RkYXRhID0gZGF0YS5mcmFtZShmYWN0b3IgPSBmYWN0b3JYLCBwcm9iID0gaXRlbVksIHBsb3QgPSBpdGVtVGV4dDIpCiAgICAKICAgIG5hbWVzKHBsb3RkYXRhKSA9IGMoZmFjdG9ybmFtZSAsICJQcm9iYWJpbGl0eSIsICJSZXNwb25zZSIpCiAgICBnZ3Bsb3QoZGF0YSA9IHBsb3RkYXRhLCBhZXNfc3RyaW5nKHggPSBmYWN0b3JuYW1lLCB5ID0gIlByb2JhYmlsaXR5IiwgY29sb3VyID0gIlJlc3BvbnNlIikpICsgZ2VvbV9saW5lKHNpemUgPSAyKQogICAgCiAgfQp9CgpsYXZhYW5DYXRJdGVtUGxvdChsYXZPYmplY3QgPSBncm0yUGVzdGltYXRlcywgdmFybmFtZSA9ICJjaWE0Iiwgc2RzID0gMykKYGBgCgojIyMgR3JhZGVkIFJlc3BvbnNlIE1vZGVsICgxUEwtaXNoIHZlcnNpb24pIHVzaW5nIFdMU01WIHByb2JpdCBtb2RlbAoKV2UgY2FuIGFsc28gc2V0IHRoZSBpdGVtIGRpc2NyaW1pbmF0aW9ucyBlcXVhbCBmb3IgYSAxUEwtbGlrZSBtb2RlbDoKCmBgYHtyIGdybTFwLCBpbmNsdWRlPVRSVUV9CmdybTFQc3ludGF4ID0gIgpJQURMID1+IGEqY2lhMSArIGEqY2lhMiArIGEqY2lhMyArIGEqY2lhNCArIGEqY2lhNSArIGEqY2lhNiArIGEqY2lhNwoiCgpncm0xUGVzdGltYXRlcyA9IGNmYShtb2RlbCA9IGdybTFQc3ludGF4LCBkYXRhID0gaWFkbERhdGEsIG9yZGVyZWQgPSBjKCJjaWExIiwgImNpYTIiLCAiY2lhMyIsICJjaWE0IiwgImNpYTUiLCAiY2lhNiIsICJjaWE3IiksIHN0ZC5sdiA9IFRSVUUsIHBhcmFtZXRlcml6YXRpb249InRoZXRhIikKc3VtbWFyeShncm0xUGVzdGltYXRlcywgZml0Lm1lYXN1cmVzID0gVFJVRSwgcnNxdWFyZSA9IFRSVUUsIHN0YW5kYXJkaXplZCA9IFRSVUUpCgpgYGAKClNpbWlsYXJseSwgd2UgY2FuIHVzZSB0aGUgYGFub3ZhKClgIGZ1bmN0aW9uIHRvIGNvbXBhcmUgbW9kZWwgZml0OgoKYGBge3IgY29tcGFyZSwgaW5jbHVkZT1UUlVFfQphbm92YShncm0xUGVzdGltYXRlcywgZ3JtMlBlc3RpbWF0ZXMpCmBgYAo=