🔙 Home

★ DRAFT ★
(A work in progress…)

What is “ordinal” data?

Data like reaction times is continuous because there are no discrete boundaries. RTs can be seconds, fractions of sections, fractions of milliseconds, or even finer grained than that.

Data like counts of words in corpora are discrete, but hypothetically infinite. You could have 0 attestations, 1, or one million… but you could not have 13.5 attestations of a word.

Binary data is also discrete, and can come from looking at hits vs misses (e.g., correct or incorrect responses) among other things. This data type is binary because there are only two values you can observe: 1 (hit) or 0 (miss).

But there is another type of data we use often, too. Ordinal data refers to data that is discrete but also ranked like in a Likert scale task. What makes this type of data unique is that it has a maximum and minimum endpoint, but also a limited number of (ordered) values in between. In this respect, binary data and ordinal data are similar, but when you have more than two possible responses, analysis can get quite complicated.

In particular, what makes analysis of ordinal data different from continuous, count, and binary data is that the perceptual size of each level may vary. That is, on a 7-point scale, the difference between a rating of 1 and a rating of 2 may not be the same distance as the difference between a rating of 2 and a rating of 3, or between a rating of 6 and a rating of 7. The distance between each level of the scale can vary across participants and items, so when we analyse this data, we really should take into account this dimension of variability.

That’s where the ordinal package comes in.

library(ordinal)
dataOrd <- read.csv("data/ordinal_data.csv",header=TRUE)
head(dataOrd)
  subject item  factor1 factor2 rating
1       1    1 Baseline  Absent      5
2       1    2 Baseline  Absent      6
3       1    3 Baseline  Absent      7
4       1    4 Baseline  Absent      7
5       1    5 Baseline  Absent      7
6       1    6 Baseline  Absent      7

When we graph our rating data in a box plot, we see there is likely to be a main effect of Factor 1 and possibly an interaction between Factor 1 and Factor 2. It’s hard to tell more at this point.

ggplot(dataOrd, aes(x=factor1,y=rating)) + geom_boxplot(aes(fill=factor2)) + scale_y_continuous(breaks=c(1:7)) + ggtitle("Latin square rating data")

ggplot(dataOrd, aes(x=factor1,y=rating)) + geom_boxplot(aes(fill=factor1)) + scale_y_continuous(breaks=c(1:7)) + ggtitle("Main effect of Factor 1")

ggplot(dataOrd, aes(x=factor2,y=rating)) + geom_boxplot(aes(fill=factor2)) + scale_y_continuous(breaks=c(1:7)) + ggtitle("Main effect of Factor 2")

Ordinal regressions

When we summarise a linear mixed effect model of this rating data, this is what we get.

lmer.rate.max <- lmer(rating ~ factor1*factor2 + (1|subject)+(1|item),data=dataOrd, REML=FALSE)
summary(lmer.rate.max)
Linear mixed model fit by maximum likelihood  ['lmerMod']
Formula: rating ~ factor1 * factor2 + (1 | subject) + (1 | item)
   Data: dataOrd

     AIC      BIC   logLik deviance df.resid 
  2892.6   2926.7  -1439.3   2878.6      953 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.4690 -0.8577 -0.0005  0.8743  3.6434 

Random effects:
 Groups   Name        Variance Std.Dev.
 subject  (Intercept) 0.071283 0.26699 
 item     (Intercept) 0.001825 0.04272 
 Residual             1.128254 1.06219 
Number of obs: 960, groups:  subject, 40; item, 24

Fixed effects:
                                Estimate Std. Error t value
(Intercept)                      6.02083    0.08099  74.342
factor1Treatment                -1.65833    0.09696 -17.102
factor2Present                  -0.07083    0.09696  -0.731
factor1Treatment:factor2Present -1.65417    0.13713 -12.063

Correlation of Fixed Effects:
            (Intr) fctr1T fctr2P
fctr1Trtmnt -0.599              
factr2Prsnt -0.599  0.500       
fctr1Trt:2P  0.423 -0.707 -0.707

The following model is an cumulative link mixed model, which operates in a very similar way to a linear mixed effect model on the surface, but takes into account the possibility that each level of the rating scale could be a different size or distance from its neighbors.

clmm.max <- clmm(as.factor(rating) ~ factor1*factor2 + (1|subject)+(1|item),data=dataOrd)
summary(clmm.max)
Cumulative Link Mixed Model fitted with the Laplace approximation

formula: as.factor(rating) ~ factor1 * factor2 + (1 | subject) + (1 |      item)
data:    dataOrd

 link  threshold nobs logLik   AIC     niter      max.grad cond.H 
 logit flexible  960  -1377.05 2776.10 1269(3796) 9.11e-04 6.4e+01

Random effects:
 Groups  Name        Variance Std.Dev.
 subject (Intercept) 0.17920  0.4233  
 item    (Intercept) 0.01514  0.1230  
Number of groups:  subject 40,  item 24 

Coefficients:
                                Estimate Std. Error z value Pr(>|z|)    
factor1Treatment                 -2.7711     0.1974 -14.040   <2e-16 ***
factor2Present                   -0.1735     0.1672  -1.038    0.299    
factor1Treatment:factor2Present  -2.3701     0.2564  -9.243   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Threshold coefficients:
    Estimate Std. Error z value
1|2  -6.7035     0.2714 -24.703
2|3  -5.4889     0.2400 -22.867
3|4  -4.1191     0.2131 -19.332
4|5  -2.6917     0.1826 -14.745
5|6  -0.9258     0.1480  -6.254
6|7   0.5231     0.1458   3.588

Just like you might with a LMER, you can do a model comparison to determine the contribution of each main effect and their interaction.

clmm.int <- clmm(as.factor(rating) ~ factor1+factor2 + (1|subject)+(1|item),data=dataOrd)
clmm.one <- clmm(as.factor(rating) ~ factor2 + (1|subject)+(1|item),data=dataOrd)
clmm.two <- clmm(as.factor(rating) ~ factor1 + (1|subject)+(1|item),data=dataOrd)
anova(clmm.max,clmm.int)
Likelihood ratio tests of cumulative link models:
 
         formula:                                                           link: threshold:
clmm.int as.factor(rating) ~ factor1 + factor2 + (1 | subject) + (1 | item) logit flexible  
clmm.max as.factor(rating) ~ factor1 * factor2 + (1 | subject) + (1 | item) logit flexible  

         no.par    AIC  logLik LR.stat df Pr(>Chisq)    
clmm.int     10 2863.5 -1421.8                          
clmm.max     11 2776.1 -1377.0  89.439  1  < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
anova(clmm.int,clmm.one)
Likelihood ratio tests of cumulative link models:
 
         formula:                                                           link: threshold:
clmm.one as.factor(rating) ~ factor2 + (1 | subject) + (1 | item)           logit flexible  
clmm.int as.factor(rating) ~ factor1 + factor2 + (1 | subject) + (1 | item) logit flexible  

         no.par    AIC  logLik LR.stat df Pr(>Chisq)    
clmm.one      9 3536.0 -1759.0                          
clmm.int     10 2863.5 -1421.8  674.43  1  < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
anova(clmm.int,clmm.two)
Likelihood ratio tests of cumulative link models:
 
         formula:                                                           link: threshold:
clmm.two as.factor(rating) ~ factor1 + (1 | subject) + (1 | item)           logit flexible  
clmm.int as.factor(rating) ~ factor1 + factor2 + (1 | subject) + (1 | item) logit flexible  

         no.par    AIC  logLik LR.stat df Pr(>Chisq)    
clmm.two      9 2970.1 -1476.0                          
clmm.int     10 2863.5 -1421.8  108.53  1  < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

These suggest both main effects and their interaction all contribute significantly to the fit of the model.

Visualising the ratings

If we visualise the ratings as probabilities, we can see subtle differences that aren’t visible in box plots or bar charts. In the graph below, the coloured lines indicate the estimated probability each of the conditions will be assigned a certain rating.

# script adapted from Jalal Al Tamimi
par(oma = c(0, 0, 0, 1))  # ,mfrow=c(1,1))
xlim = c(min(clmm.max$beta) - 0.5, max(clmm.max$beta) + 0.5)
ylim = c(0, 1)
plot(0, 0, xlim = xlim, ylim = ylim, type = "n", ylab = "Probability", xlab = "", 
    xaxt = "n", main = "Predicted curves")
axis(side = 1, labels = c("Treatment   \nPresent", "Baseline   \nAbsent", "Treatment   \nAbsent", 
    "Baseline   \nPresent"), at = c(clmm.max$beta, 0), las = 2, cex.axis = 0.7)
xs = seq(xlim[1], xlim[2], length.out = 100)
lines(xs, plogis(clmm.max$Theta[1] - xs), col = "red", lwd = 3)
lines(xs, plogis(clmm.max$Theta[2] - xs) - plogis(clmm.max$Theta[1] - xs), col = "orange", 
    lwd = 3)
lines(xs, plogis(clmm.max$Theta[3] - xs) - plogis(clmm.max$Theta[2] - xs), col = "yellow", 
    lwd = 3)
lines(xs, plogis(clmm.max$Theta[4] - xs) - plogis(clmm.max$Theta[3] - xs), col = "green", 
    lwd = 3)
lines(xs, plogis(clmm.max$Theta[5] - xs) - plogis(clmm.max$Theta[4] - xs), col = "blue", 
    lwd = 3)
lines(xs, plogis(clmm.max$Theta[6] - xs) - plogis(clmm.max$Theta[5] - xs), col = "purple", 
    lwd = 3)
lines(xs, 1 - (plogis(clmm.max$Theta[6] - xs)), col = "black", lwd = 3)
abline(v = c(0, clmm.max$beta[1:4]), lty = 3)
abline(h = 0, lty = "dashed")
abline(h = 1, lty = "dashed")
legend(par("usr")[2], par("usr")[4], bty = "n", xpd = NA, lty = 1, col = c("red", 
    "orange", "yellow", "green", "blue", "purple", "black"), legend = c("1", 
    "2", "3", "4", "5", "6", "7"), cex = 0.75)

In this graph, we can see the probability curves for each of the seven ratings. In particular, we can now see that the Baseline+Present condition and the Baseline+Absent condition are not rated in precisely the same manner: Baseline+Present is more likely to receive a rating of 7 and less like to receive a rating of 5 than Baseline+Absent. In some cases, when this difference ends up being significant, it will not be visible in a box plot but will be visible in this sort of probability curve plot.

LS0tCnRpdGxlOiAiRGVhbGluZyB3aXRoIG9yZGluYWwgZGF0YSIKYXV0aG9yOiAiTGF1cmVuIE0gQWNrZXJtYW4iCmRhdGU6ICJMYXN0IHVwZGF0ZWQ6IDAzIE1heSAyMDE4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDIKICAgIHRvY19mbG9hdDogeWVzCi0tLQpb8J+UmSBIb21lXShodHRwczovL3ZlcmJpbmdub3Vucy5naXRodWIuaW8vbm90ZWJvb2tzLykKCmBgYHtyIGVjaG89RkFMU0UsbWVzc2FnZT1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGxtZTQpCmxpYnJhcnkob3JkaW5hbCkKbGlicmFyeShmb3JtYXRSKQpgYGAKCj4g4piFIERSQUZUIOKYhSAgCj4gKEEgd29yayBpbiBwcm9ncmVzc+KApikKCiMgV2hhdCBpcyAib3JkaW5hbCIgZGF0YT8KCkRhdGEgbGlrZSByZWFjdGlvbiB0aW1lcyBpcyAqY29udGludW91cyogYmVjYXVzZSB0aGVyZSBhcmUgbm8gZGlzY3JldGUgYm91bmRhcmllcy4gUlRzIGNhbiBiZSBzZWNvbmRzLCBmcmFjdGlvbnMgb2Ygc2VjdGlvbnMsIGZyYWN0aW9ucyBvZiBtaWxsaXNlY29uZHMsIG9yIGV2ZW4gZmluZXIgZ3JhaW5lZCB0aGFuIHRoYXQuCgpEYXRhIGxpa2UgY291bnRzIG9mIHdvcmRzIGluIGNvcnBvcmEgYXJlICpkaXNjcmV0ZSosIGJ1dCBoeXBvdGhldGljYWxseSBpbmZpbml0ZS4gWW91IGNvdWxkIGhhdmUgMCBhdHRlc3RhdGlvbnMsIDEsIG9yIG9uZSBtaWxsaW9u4oCmIGJ1dCB5b3UgY291bGQgbm90IGhhdmUgMTMuNSBhdHRlc3RhdGlvbnMgb2YgYSB3b3JkLgoKKkJpbmFyeSogZGF0YSBpcyBhbHNvIGRpc2NyZXRlLCBhbmQgY2FuIGNvbWUgZnJvbSBsb29raW5nIGF0IGhpdHMgdnMgbWlzc2VzIChlLmcuLCBjb3JyZWN0IG9yIGluY29ycmVjdCByZXNwb25zZXMpIGFtb25nIG90aGVyIHRoaW5ncy4gVGhpcyBkYXRhIHR5cGUgaXMgYmluYXJ5IGJlY2F1c2UgdGhlcmUgYXJlIG9ubHkgdHdvIHZhbHVlcyB5b3UgY2FuIG9ic2VydmU6IDEgKGhpdCkgb3IgMCAobWlzcykuCgpCdXQgdGhlcmUgaXMgYW5vdGhlciB0eXBlIG9mIGRhdGEgd2UgdXNlIG9mdGVuLCB0b28uICpPcmRpbmFsKiBkYXRhIHJlZmVycyB0byBkYXRhIHRoYXQgaXMgZGlzY3JldGUgYnV0IGFsc28gcmFua2VkIGxpa2UgaW4gYSBMaWtlcnQgc2NhbGUgdGFzay4gV2hhdCBtYWtlcyB0aGlzIHR5cGUgb2YgZGF0YSB1bmlxdWUgaXMgdGhhdCBpdCBoYXMgYSBtYXhpbXVtIGFuZCBtaW5pbXVtIGVuZHBvaW50LCBidXQgYWxzbyBhIGxpbWl0ZWQgbnVtYmVyIG9mIChvcmRlcmVkKSB2YWx1ZXMgaW4gYmV0d2Vlbi4gSW4gdGhpcyByZXNwZWN0LCBiaW5hcnkgZGF0YSBhbmQgb3JkaW5hbCBkYXRhIGFyZSBzaW1pbGFyLCBidXQgd2hlbiB5b3UgaGF2ZSBtb3JlIHRoYW4gdHdvIHBvc3NpYmxlIHJlc3BvbnNlcywgYW5hbHlzaXMgY2FuIGdldCBxdWl0ZSBjb21wbGljYXRlZC4KCkluIHBhcnRpY3VsYXIsIHdoYXQgbWFrZXMgYW5hbHlzaXMgb2Ygb3JkaW5hbCBkYXRhIGRpZmZlcmVudCBmcm9tIGNvbnRpbnVvdXMsIGNvdW50LCBhbmQgYmluYXJ5IGRhdGEgaXMgdGhhdCB0aGUgcGVyY2VwdHVhbCBzaXplIG9mIGVhY2ggbGV2ZWwgbWF5IHZhcnkuIFRoYXQgaXMsIG9uIGEgNy1wb2ludCBzY2FsZSwgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIHJhdGluZyBvZiAxIGFuZCBhIHJhdGluZyBvZiAyIG1heSBub3QgYmUgdGhlIHNhbWUgZGlzdGFuY2UgYXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhIHJhdGluZyBvZiAyIGFuZCBhIHJhdGluZyBvZiAzLCBvciBiZXR3ZWVuIGEgcmF0aW5nIG9mIDYgYW5kIGEgcmF0aW5nIG9mIDcuIFRoZSBkaXN0YW5jZSBiZXR3ZWVuIGVhY2ggbGV2ZWwgb2YgdGhlIHNjYWxlIGNhbiB2YXJ5IGFjcm9zcyBwYXJ0aWNpcGFudHMgYW5kIGl0ZW1zLCBzbyB3aGVuIHdlIGFuYWx5c2UgdGhpcyBkYXRhLCB3ZSByZWFsbHkgc2hvdWxkIHRha2UgaW50byBhY2NvdW50IHRoaXMgZGltZW5zaW9uIG9mIHZhcmlhYmlsaXR5LgoKVGhhdCdzIHdoZXJlIHRoZSBgb3JkaW5hbGAgcGFja2FnZSBjb21lcyBpbi4KCmBgYHtyfQpsaWJyYXJ5KG9yZGluYWwpCgpkYXRhT3JkIDwtIHJlYWQuY3N2KCJkYXRhL29yZGluYWxfZGF0YS5jc3YiLGhlYWRlcj1UUlVFKQpoZWFkKGRhdGFPcmQpCmBgYAoKV2hlbiB3ZSBncmFwaCBvdXIgcmF0aW5nIGRhdGEgaW4gYSBib3ggcGxvdCwgd2Ugc2VlIHRoZXJlIGlzIGxpa2VseSB0byBiZSBhIG1haW4gZWZmZWN0IG9mICpGYWN0b3IgMSogYW5kIHBvc3NpYmx5IGFuIGludGVyYWN0aW9uIGJldHdlZW4gKkZhY3RvciAxKiBhbmQgKkZhY3RvciAyKi4gSXQncyBoYXJkIHRvIHRlbGwgbW9yZSBhdCB0aGlzIHBvaW50LgoKYGBge3J9CmdncGxvdChkYXRhT3JkLCBhZXMoeD1mYWN0b3IxLHk9cmF0aW5nKSkgKyBnZW9tX2JveHBsb3QoYWVzKGZpbGw9ZmFjdG9yMikpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1jKDE6NykpICsgZ2d0aXRsZSgiTGF0aW4gc3F1YXJlIHJhdGluZyBkYXRhIikKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGFPcmQsIGFlcyh4PWZhY3RvcjEseT1yYXRpbmcpKSArIGdlb21fYm94cGxvdChhZXMoZmlsbD1mYWN0b3IxKSkgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPWMoMTo3KSkgKyBnZ3RpdGxlKCJNYWluIGVmZmVjdCBvZiBGYWN0b3IgMSIpCmdncGxvdChkYXRhT3JkLCBhZXMoeD1mYWN0b3IyLHk9cmF0aW5nKSkgKyBnZW9tX2JveHBsb3QoYWVzKGZpbGw9ZmFjdG9yMikpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1jKDE6NykpICsgZ2d0aXRsZSgiTWFpbiBlZmZlY3Qgb2YgRmFjdG9yIDIiKQpgYGAKCiMgT3JkaW5hbCByZWdyZXNzaW9ucwoKV2hlbiB3ZSBzdW1tYXJpc2UgYSBsaW5lYXIgbWl4ZWQgZWZmZWN0IG1vZGVsIG9mIHRoaXMgcmF0aW5nIGRhdGEsIHRoaXMgaXMgd2hhdCB3ZSBnZXQuCgpgYGB7cn0KbG1lci5yYXRlLm1heCA8LSBsbWVyKHJhdGluZyB+IGZhY3RvcjEqZmFjdG9yMiArICgxfHN1YmplY3QpKygxfGl0ZW0pLGRhdGE9ZGF0YU9yZCwgUkVNTD1GQUxTRSkKc3VtbWFyeShsbWVyLnJhdGUubWF4KQpgYGAKClRoZSBmb2xsb3dpbmcgbW9kZWwgaXMgYW4gY3VtdWxhdGl2ZSBsaW5rIG1peGVkIG1vZGVsLCB3aGljaCBvcGVyYXRlcyBpbiBhIHZlcnkgc2ltaWxhciB3YXkgdG8gYSBsaW5lYXIgbWl4ZWQgZWZmZWN0IG1vZGVsIG9uIHRoZSBzdXJmYWNlLCBidXQgdGFrZXMgaW50byBhY2NvdW50IHRoZSBwb3NzaWJpbGl0eSB0aGF0IGVhY2ggbGV2ZWwgb2YgdGhlIHJhdGluZyBzY2FsZSBjb3VsZCBiZSBhIGRpZmZlcmVudCBzaXplIG9yIGRpc3RhbmNlIGZyb20gaXRzIG5laWdoYm9ycy4KCmBgYHtyfQpjbG1tLm1heCA8LSBjbG1tKGFzLmZhY3RvcihyYXRpbmcpIH4gZmFjdG9yMSpmYWN0b3IyICsgKDF8c3ViamVjdCkrKDF8aXRlbSksZGF0YT1kYXRhT3JkKQpzdW1tYXJ5KGNsbW0ubWF4KQpgYGAKCkp1c3QgbGlrZSB5b3UgbWlnaHQgd2l0aCBhIExNRVIsIHlvdSBjYW4gZG8gYSBtb2RlbCBjb21wYXJpc29uIHRvIGRldGVybWluZSB0aGUgY29udHJpYnV0aW9uIG9mIGVhY2ggbWFpbiBlZmZlY3QgYW5kIHRoZWlyIGludGVyYWN0aW9uLgoKYGBge3J9CmNsbW0uaW50IDwtIGNsbW0oYXMuZmFjdG9yKHJhdGluZykgfiBmYWN0b3IxK2ZhY3RvcjIgKyAoMXxzdWJqZWN0KSsoMXxpdGVtKSxkYXRhPWRhdGFPcmQpCmNsbW0ub25lIDwtIGNsbW0oYXMuZmFjdG9yKHJhdGluZykgfiBmYWN0b3IyICsgKDF8c3ViamVjdCkrKDF8aXRlbSksZGF0YT1kYXRhT3JkKQpjbG1tLnR3byA8LSBjbG1tKGFzLmZhY3RvcihyYXRpbmcpIH4gZmFjdG9yMSArICgxfHN1YmplY3QpKygxfGl0ZW0pLGRhdGE9ZGF0YU9yZCkKYGBgCmBgYHtyfQphbm92YShjbG1tLm1heCxjbG1tLmludCkKYGBgCgpgYGB7cn0KYW5vdmEoY2xtbS5pbnQsY2xtbS5vbmUpCmBgYAoKYGBge3J9CmFub3ZhKGNsbW0uaW50LGNsbW0udHdvKQpgYGAKClRoZXNlIHN1Z2dlc3QgYm90aCBtYWluIGVmZmVjdHMgYW5kIHRoZWlyIGludGVyYWN0aW9uIGFsbCBjb250cmlidXRlIHNpZ25pZmljYW50bHkgdG8gdGhlIGZpdCBvZiB0aGUgbW9kZWwuCgojIFZpc3VhbGlzaW5nIHRoZSByYXRpbmdzCgpJZiB3ZSB2aXN1YWxpc2UgdGhlIHJhdGluZ3MgYXMgcHJvYmFiaWxpdGllcywgd2UgY2FuIHNlZSBzdWJ0bGUgZGlmZmVyZW5jZXMgdGhhdCBhcmVuJ3QgdmlzaWJsZSBpbiBib3ggcGxvdHMgb3IgYmFyIGNoYXJ0cy4gSW4gdGhlIGdyYXBoIGJlbG93LCB0aGUgY29sb3VyZWQgbGluZXMgaW5kaWNhdGUgdGhlIGVzdGltYXRlZCBwcm9iYWJpbGl0eSBlYWNoIG9mIHRoZSBjb25kaXRpb25zIHdpbGwgYmUgYXNzaWduZWQgYSBjZXJ0YWluIHJhdGluZy4gCgpgYGB7ciwgZXZhbD1UUlVFLGNvbGxhcHNlPVRSVUUsdGlkeT1UUlVFfQojIHNjcmlwdCBhZGFwdGVkIGZyb20gSmFsYWwgQWwgVGFtaW1pCnBhcihvbWE9YygwLCAwLCAwLCAxKSkgIyAsbWZyb3c9YygxLDEpKQp4bGltID0gYyhtaW4oY2xtbS5tYXgkYmV0YSktLjUsbWF4KGNsbW0ubWF4JGJldGEpKy41KQp5bGltID0gYygwLDEpCnBsb3QoMCwwLHhsaW09eGxpbSwgeWxpbT15bGltLCB0eXBlPSJuIiwgeWxhYj0iUHJvYmFiaWxpdHkiLCB4bGFiPSIiLCB4YXh0ID0gIm4iLG1haW49IlByZWRpY3RlZCBjdXJ2ZXMiKQpheGlzKHNpZGUgPSAxLCBsYWJlbHMgPSBjKCJUcmVhdG1lbnQgICBcblByZXNlbnQiLCJCYXNlbGluZSAgIFxuQWJzZW50IiwiVHJlYXRtZW50ICAgXG5BYnNlbnQiLCJCYXNlbGluZSAgIFxuUHJlc2VudCIpLCAKICAgICBhdCA9IGMoY2xtbS5tYXgkYmV0YSwwKSxsYXM9MixjZXguYXhpcz0uNykKeHMgPSBzZXEoeGxpbVsxXSwgeGxpbVsyXSwgbGVuZ3RoLm91dD0xMDApCmxpbmVzKHhzLCBwbG9naXMoY2xtbS5tYXgkVGhldGFbMV0gLSB4cyksIGNvbD0ncmVkJyxsd2Q9MykKbGluZXMoeHMsIHBsb2dpcyhjbG1tLm1heCRUaGV0YVsyXSAtIHhzKS1wbG9naXMoY2xtbS5tYXgkVGhldGFbMV0gLSB4cyksIGNvbD0nb3JhbmdlJyxsd2Q9MykKbGluZXMoeHMsIHBsb2dpcyhjbG1tLm1heCRUaGV0YVszXSAtIHhzKS1wbG9naXMoY2xtbS5tYXgkVGhldGFbMl0gLSB4cyksIGNvbD0neWVsbG93Jyxsd2Q9MykKbGluZXMoeHMsIHBsb2dpcyhjbG1tLm1heCRUaGV0YVs0XSAtIHhzKS1wbG9naXMoY2xtbS5tYXgkVGhldGFbM10gLSB4cyksIGNvbD0nZ3JlZW4nLGx3ZD0zKQpsaW5lcyh4cywgcGxvZ2lzKGNsbW0ubWF4JFRoZXRhWzVdIC0geHMpLXBsb2dpcyhjbG1tLm1heCRUaGV0YVs0XSAtIHhzKSwgY29sPSdibHVlJyxsd2Q9MykKbGluZXMoeHMsIHBsb2dpcyhjbG1tLm1heCRUaGV0YVs2XSAtIHhzKS1wbG9naXMoY2xtbS5tYXgkVGhldGFbNV0gLSB4cyksIGNvbD0ncHVycGxlJyxsd2Q9MykKbGluZXMoeHMsIDEtKHBsb2dpcyhjbG1tLm1heCRUaGV0YVs2XSAtIHhzKSksIGNvbD0nYmxhY2snLGx3ZD0zKQphYmxpbmUodj1jKDAsY2xtbS5tYXgkYmV0YVsxOjRdKSxsdHk9MykKYWJsaW5lKGg9MCwgbHR5PSJkYXNoZWQiKQphYmxpbmUoaD0xLCBsdHk9ImRhc2hlZCIpCmxlZ2VuZChwYXIoJ3VzcicpWzJdLCBwYXIoJ3VzcicpWzRdLCBidHk9J24nLCB4cGQ9TkEsbHR5PTEsIGNvbD1jKCJyZWQiLCAib3JhbmdlIiwgInllbGxvdyIsICJncmVlbiIsICJibHVlIiwgInB1cnBsZSIsICJibGFjayIpLAogICAgICAgbGVnZW5kPWMoIjEiLCAiMiIsICIzIiwgIjQiLCAiNSIsICI2IiwgIjciKSxjZXg9MC43NSkKYGBgCgpJbiB0aGlzIGdyYXBoLCB3ZSBjYW4gc2VlIHRoZSBwcm9iYWJpbGl0eSBjdXJ2ZXMgZm9yIGVhY2ggb2YgdGhlIHNldmVuIHJhdGluZ3MuIEluIHBhcnRpY3VsYXIsIHdlIGNhbiBub3cgc2VlIHRoYXQgdGhlICpCYXNlbGluZStQcmVzZW50KiBjb25kaXRpb24gYW5kIHRoZSAqQmFzZWxpbmUrQWJzZW50KiBjb25kaXRpb24gYXJlIG5vdCByYXRlZCBpbiBwcmVjaXNlbHkgdGhlIHNhbWUgbWFubmVyOiAqQmFzZWxpbmUrUHJlc2VudCogaXMgbW9yZSBsaWtlbHkgdG8gcmVjZWl2ZSBhIHJhdGluZyBvZiA3IGFuZCBsZXNzIGxpa2UgdG8gcmVjZWl2ZSBhIHJhdGluZyBvZiA1IHRoYW4gKkJhc2VsaW5lK0Fic2VudCouIEluIHNvbWUgY2FzZXMsIHdoZW4gdGhpcyBkaWZmZXJlbmNlIGVuZHMgdXAgYmVpbmcgc2lnbmlmaWNhbnQsIGl0IHdpbGwgbm90IGJlIHZpc2libGUgaW4gYSBib3ggcGxvdCBidXQgd2lsbCBiZSB2aXNpYmxlIGluIHRoaXMgc29ydCBvZiBwcm9iYWJpbGl0eSBjdXJ2ZSBwbG90LgoK