Using Multi-Level Regression Models to look at cereal rating differences

Jacqueline Nosrati

Overview

In this week’s assignment I will use nutrition data of cereals from different cereal manufacturers to look at cereal ratings and whether the amounts of sugars and number of calories affect cereal ratings.

Load packages

library(tidyverse)
library(tidyr)
library(readr)
library(dplyr)
library(visreg)
library(nlme)
library(sjmisc)
library(ggplot2)
library(radiant.data)
library(pander)
library(magrittr)
library(haven)
library(lmerTest)
library(texreg)

Load data set

Cereal<- read_csv("C:/Users/Papa/Desktop/Soc 712 -R/cereal.csv", col_names = TRUE)
head(Cereal)
Cereal2 <- select(Cereal, name, mfr, calories, sugars, rating)
head(Cereal2)

For the analysis the following variables were selected cereal name, manufacturer, calories, sugars, and rating.

Recode mfr (manufacturer) variable into factor

In the manufacturer variable A stands for American Home Food Products, G for General Mills, K for Kellogs, N for Nabisco,P for Post, Q for Quaker Oats and R for Ralston Purina. We convert this variable into a factor.

Cereal3<-Cereal2 %>% mutate (n_manufacturer = factor(ifelse(mfr == "A", 1,
                ifelse(mfr == "G", 2,
                ifelse(mfr == "K", 3,
                ifelse(mfr == "N", 4,
                ifelse(mfr == "P", 5,
                ifelse(mfr == "Q", 6,
                ifelse(mfr == "R", 7, "error")))))))))
head(Cereal3)
length(unique(Cereal3$n_manufacturer))
[1] 7
Cereal3 %>% 
  group_by(n_manufacturer) %>% 
  summarise(n_man = n())
manufacturers <- Cereal3 %>% 
  group_by(n_manufacturer) %>% 
  summarise(mean_r = mean(rating, na.rm = TRUE), mean_c = mean(calories, na.rm = TRUE), mean_s = mean(sugars, na.rm = TRUE))
head(manufacturers)

We generated the average rating, calories and sugars amounts per manufacturer.

Complete Pooling Models

cpooling <- lm(rating ~ calories, data = Cereal3)
summary(cpooling)

Call:
lm(formula = rating ~ calories, data = Cereal3)

Residuals:
     Min       1Q   Median       3Q      Max 
-18.7201  -7.9317  -0.6678   5.9902  23.4161 

Coefficients:
            Estimate Std. Error t value             Pr(>|t|)    
(Intercept) 95.78802    6.55057  14.623 < 0.0000000000000002 ***
calories    -0.49701    0.06031  -8.241     0.00000000000414 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 10.24 on 75 degrees of freedom
Multiple R-squared:  0.4752,    Adjusted R-squared:  0.4682 
F-statistic: 67.92 on 1 and 75 DF,  p-value: 0.00000000000414

The output for the first complete pooling model shows that the average cereal rating (without accounting for differences among cereal manufacturers) is 95.79 if a cereal had 0 calories and that for each additional calorie the cereal rating decreases on average by 0.5. We see that the results are statistically significant at a 99% confidence level. To get a better look at rating differences we add for the next complete pooling model the amount of sugars into the model.

cpooling2 <- lm(rating ~ calories + sugars, data = Cereal3)
summary(cpooling2)

Call:
lm(formula = rating ~ calories + sugars, data = Cereal3)

Residuals:
    Min      1Q  Median      3Q     Max 
-15.643  -6.339  -1.221   4.823  23.413 

Coefficients:
            Estimate Std. Error t value             Pr(>|t|)    
(Intercept) 84.11417    5.44513  15.448 < 0.0000000000000002 ***
calories    -0.27644    0.05755  -4.804        0.00000793294 ***
sugars      -1.71939    0.25225  -6.816        0.00000000216 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8.083 on 74 degrees of freedom
Multiple R-squared:  0.6776,    Adjusted R-squared:  0.6689 
F-statistic: 77.78 on 2 and 74 DF,  p-value: < 0.00000000000000022

The output for the second complete pooling model shows that the average cereal rating (without accounting for differences among cereal manufacturers) is 84.11 if a cereal had 0 calories and 0 grams sugar and that for each additional calorie the cereal rating decreases on average by 0.28 and for each additional sugar gram the cereal rating decreases on average by 1.72. Again, we see that the results are statistically significant at a 99% confidence level. Finally, we will look at rating differences with an interaction between calories and sugar amounts for the complete pooling model

cpooling3 <- lm(rating ~ calories*sugars, data = Cereal3)
summary(cpooling3)

Call:
lm(formula = rating ~ calories * sugars, data = Cereal3)

Residuals:
     Min       1Q   Median       3Q      Max 
-18.1761  -5.3793   0.1491   4.8486  15.7521 

Coefficients:
                  Estimate Std. Error t value             Pr(>|t|)    
(Intercept)     101.659340   7.560213  13.447 < 0.0000000000000002 ***
calories         -0.454542   0.078219  -5.811          0.000000151 ***
sugars           -5.038979   1.075503  -4.685          0.000012641 ***
calories:sugars   0.031056   0.009812   3.165              0.00226 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 7.631 on 73 degrees of freedom
Multiple R-squared:  0.7165,    Adjusted R-squared:  0.7049 
F-statistic: 61.51 on 3 and 73 DF,  p-value: < 0.00000000000000022

In the third complete pooling model we see that on their own calories and sugar amounts are still statistically significant at a 99% confidence level, however the interaction between the two indepedent variables is only statistically significant at a 95% confidence level.

Comparing the 3 complete pooling models

htmlreg(list(cpooling,cpooling2, cpooling3))
Statistical models
Model 1 Model 2 Model 3
(Intercept) 95.79*** 84.11*** 101.66***
(6.55) (5.45) (7.56)
calories -0.50*** -0.28*** -0.45***
(0.06) (0.06) (0.08)
sugars -1.72*** -5.04***
(0.25) (1.08)
calories:sugars 0.03**
(0.01)
R2 0.48 0.68 0.72
Adj. R2 0.47 0.67 0.70
Num. obs. 77 77 77
RMSE 10.24 8.08 7.63
p < 0.001, p < 0.01, p < 0.05

When we compare the three complete pooling models we see that the third model is the best fitting model with an R2 of 0.72 (which is the highest amongst the three models).

No Pooling Model

dcoef <- Cereal3 %>% 
    group_by(n_manufacturer) %>% 
    do(mod = lm(rating ~ calories + sugars, data = .))
coef <- dcoef %>% do(data.frame(intc = coef(.$mod)[1]))
ggplot(coef, aes(x = intc)) + geom_histogram()

Now we use the No pooling model to look at manufacturer differences in ratings. We see that large differences in cereal ratings among different manufacturers. The distribution looks somewhat normally distributed.

dcoef <- Cereal3 %>% 
    group_by(n_manufacturer) %>% 
    do(mod = lm(rating ~ calories, data = .))
coef <- dcoef %>% do(data.frame(calories = coef(.$mod)[2]))
ggplot(coef, aes(x = calories)) + geom_histogram()

When looking at rating differences with respect to calories we now see a slightly right skewed distribution as the number of calories increase.

Random Intercept Model

m1_crating <- lme(rating ~ calories, data = Cereal3, random = ~1|n_manufacturer, method = "ML")
summary(m1_crating)
Linear mixed-effects model fit by maximum likelihood
 Data: Cereal3 
       AIC      BIC    logLik
  568.5035 577.8787 -280.2517

Random effects:
 Formula: ~1 | n_manufacturer
        (Intercept) Residual
StdDev:    6.374507 8.503316

Fixed effects: rating ~ calories 
               Value Std.Error DF   t-value p-value
(Intercept) 91.86110  6.322652 69 14.528886       0
calories    -0.44305  0.054472 69 -8.133517       0
 Correlation: 
         (Intr)
calories -0.899

Standardized Within-Group Residuals:
        Min          Q1         Med          Q3         Max 
-1.76923784 -0.74835203  0.05835578  0.72828773  2.78544717 

Number of Observations: 77
Number of Groups: 7 

Random slope Model

m2_crating <- lme(rating ~  sugars, data = Cereal3, random = ~1|n_manufacturer, method = "ML")
summary(m2_crating)
Linear mixed-effects model fit by maximum likelihood
 Data: Cereal3 
       AIC      BIC    logLik
  550.0402 559.4154 -271.0201

Random effects:
 Formula: ~1 | n_manufacturer
        (Intercept) Residual
StdDev:    5.674263 7.540553

Fixed effects: rating ~ sugars 
               Value Std.Error DF   t-value p-value
(Intercept) 58.96186 2.7849719 69  21.17144       0
sugars      -2.17261 0.2126363 69 -10.21750       0
 Correlation: 
       (Intr)
sugars -0.469

Standardized Within-Group Residuals:
        Min          Q1         Med          Q3         Max 
-1.76232047 -0.66734610  0.08421924  0.50447067  4.42116697 

Number of Observations: 77
Number of Groups: 7 
m3_crating <- lme(rating ~ calories + sugars, data = Cereal3, random = ~1|n_manufacturer, method = "ML")
summary(m3_crating)
Linear mixed-effects model fit by maximum likelihood
 Data: Cereal3 
       AIC      BIC    logLik
  527.6888 539.4078 -258.8444

Random effects:
 Formula: ~1 | n_manufacturer
        (Intercept) Residual
StdDev:    5.195241  6.40292

Fixed effects: rating ~ calories + sugars 
               Value Std.Error DF   t-value p-value
(Intercept) 81.93156  5.038979 68 16.259557       0
calories    -0.25440  0.048437 68 -5.252119       0
sugars      -1.58308  0.213238 68 -7.424006       0
 Correlation: 
         (Intr) calors
calories -0.868       
sugars    0.262 -0.520

Standardized Within-Group Residuals:
        Min          Q1         Med          Q3         Max 
-1.85332392 -0.62335188 -0.06762691  0.59790039  3.57112139 

Number of Observations: 77
Number of Groups: 7 
m4_crating <- lme(rating ~ calories*sugars, data = Cereal3, random = ~ sugars|n_manufacturer, method = "ML")
summary(m4_crating)
Linear mixed-effects model fit by maximum likelihood
 Data: Cereal3 
       AIC      BIC    logLik
  518.1851 536.9356 -251.0926

Random effects:
 Formula: ~sugars | n_manufacturer
 Structure: General positive-definite, Log-Cholesky parametrization
            StdDev        Corr  
(Intercept) 5.44074510787 (Intr)
sugars      0.00005841409 0     
Residual    5.72159189529       

Fixed effects: rating ~ calories * sugars 
                    Value Std.Error DF   t-value p-value
(Intercept)     100.89399  6.624025 67 15.231522  0.0000
calories         -0.44511  0.064296 67 -6.922701  0.0000
sugars           -5.05465  0.877583 67 -5.759745  0.0000
calories:sugars   0.03222  0.007927 67  4.064667  0.0001
 Correlation: 
                (Intr) calors sugars
calories        -0.924              
sugars          -0.651  0.639       
calories:sugars  0.708 -0.734 -0.976

Standardized Within-Group Residuals:
       Min         Q1        Med         Q3        Max 
-2.0230039 -0.7014219  0.1141536  0.6372830  2.3697122 

Number of Observations: 77
Number of Groups: 7 

Final Model Comparison

htmlreg(list(m1_crating, m2_crating, m3_crating, m4_crating))
Statistical models
Model 1 Model 2 Model 3 Model 4
(Intercept) 91.86*** 58.96*** 81.93*** 100.89***
(6.32) (2.78) (5.04) (6.62)
calories -0.44*** -0.25*** -0.45***
(0.05) (0.05) (0.06)
sugars -2.17*** -1.58*** -5.05***
(0.21) (0.21) (0.88)
calories:sugars 0.03***
(0.01)
AIC 568.50 550.04 527.69 518.19
BIC 577.88 559.42 539.41 536.94
Log Likelihood -280.25 -271.02 -258.84 -251.09
Num. obs. 77 77 77 77
Num. groups 7 7 7 7
p < 0.001, p < 0.01, p < 0.05

Comparing the models, we see that the last model that accounts for manufacturer differences is the best fit model since it has the lowest AIC and BIC (with 518.19 and 536.94, respectively). Looking at the m4_crating model we see that there is a difference of 5.44 in cereal ratings when keeping the amoung of sugars constant at the higher-level (manufacturer level). We also see that there is a 5.72 rating difference after keeping cereal manufacturer and sugar amount constant.

LS0tDQp0aXRsZTogIldlZWsgOCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjKipVc2luZyBNdWx0aS1MZXZlbCBSZWdyZXNzaW9uIE1vZGVscyB0byBsb29rIGF0IGNlcmVhbCByYXRpbmcgZGlmZmVyZW5jZXMgKioNCg0KIyMjI0phY3F1ZWxpbmUgTm9zcmF0aQ0KDQojIyNPdmVydmlldw0KDQpJbiB0aGlzIHdlZWsncyBhc3NpZ25tZW50IEkgd2lsbCB1c2UgbnV0cml0aW9uIGRhdGEgb2YgY2VyZWFscyBmcm9tIGRpZmZlcmVudCBjZXJlYWwgbWFudWZhY3R1cmVycyB0byBsb29rIGF0IGNlcmVhbCByYXRpbmdzIGFuZCB3aGV0aGVyIHRoZSBhbW91bnRzIG9mIHN1Z2FycyBhbmQgbnVtYmVyIG9mIGNhbG9yaWVzIGFmZmVjdCBjZXJlYWwgcmF0aW5ncy4NCg0KIyMjTG9hZCBwYWNrYWdlcw0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh2aXNyZWcpDQpsaWJyYXJ5KG5sbWUpDQpsaWJyYXJ5KHNqbWlzYykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmFkaWFudC5kYXRhKQ0KbGlicmFyeShwYW5kZXIpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShoYXZlbikNCmxpYnJhcnkobG1lclRlc3QpDQpsaWJyYXJ5KHRleHJlZykNCmBgYA0KDQojIyNMb2FkIGRhdGEgc2V0DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpDZXJlYWw8LSByZWFkX2NzdigiQzovVXNlcnMvUGFwYS9EZXNrdG9wL1NvYyA3MTIgLVIvY2VyZWFsLmNzdiIsIGNvbF9uYW1lcyA9IFRSVUUpDQpoZWFkKENlcmVhbCkNCmBgYA0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KQ2VyZWFsMiA8LSBzZWxlY3QoQ2VyZWFsLCBuYW1lLCBtZnIsIGNhbG9yaWVzLCBzdWdhcnMsIHJhdGluZykNCmhlYWQoQ2VyZWFsMikNCmBgYA0KDQpGb3IgdGhlIGFuYWx5c2lzIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIHdlcmUgc2VsZWN0ZWQgY2VyZWFsIG5hbWUsIG1hbnVmYWN0dXJlciwgY2Fsb3JpZXMsIHN1Z2FycywgYW5kIHJhdGluZy4NCg0KIyMjUmVjb2RlIG1mciAobWFudWZhY3R1cmVyKSB2YXJpYWJsZSBpbnRvIGZhY3Rvcg0KDQpJbiB0aGUgbWFudWZhY3R1cmVyIHZhcmlhYmxlIEEgc3RhbmRzIGZvciBBbWVyaWNhbiBIb21lIEZvb2QgUHJvZHVjdHMsIEcgZm9yIEdlbmVyYWwgTWlsbHMsIEsgZm9yIEtlbGxvZ3MsIE4gZm9yIE5hYmlzY28sUCBmb3IgUG9zdCwgUSBmb3IgUXVha2VyIE9hdHMgYW5kIFIgZm9yIFJhbHN0b24gUHVyaW5hLiBXZSBjb252ZXJ0IHRoaXMgdmFyaWFibGUgaW50byBhIGZhY3Rvci4gDQogDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCkNlcmVhbDM8LUNlcmVhbDIgJT4lIG11dGF0ZSAobl9tYW51ZmFjdHVyZXIgPSBmYWN0b3IoaWZlbHNlKG1mciA9PSAiQSIsIDEsDQogICAgICAgICAgICAgICAgaWZlbHNlKG1mciA9PSAiRyIsIDIsDQogICAgICAgICAgICAgICAgaWZlbHNlKG1mciA9PSAiSyIsIDMsDQogICAgICAgICAgICAgICAgaWZlbHNlKG1mciA9PSAiTiIsIDQsDQogICAgICAgICAgICAgICAgaWZlbHNlKG1mciA9PSAiUCIsIDUsDQogICAgICAgICAgICAgICAgaWZlbHNlKG1mciA9PSAiUSIsIDYsDQogICAgICAgICAgICAgICAgaWZlbHNlKG1mciA9PSAiUiIsIDcsICJlcnJvciIpKSkpKSkpKSkNCmhlYWQoQ2VyZWFsMykNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxlbmd0aCh1bmlxdWUoQ2VyZWFsMyRuX21hbnVmYWN0dXJlcikpDQoNCkNlcmVhbDMgJT4lIA0KICBncm91cF9ieShuX21hbnVmYWN0dXJlcikgJT4lIA0KICBzdW1tYXJpc2Uobl9tYW4gPSBuKCkpDQpgYGANCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptYW51ZmFjdHVyZXJzIDwtIENlcmVhbDMgJT4lIA0KICBncm91cF9ieShuX21hbnVmYWN0dXJlcikgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9yID0gbWVhbihyYXRpbmcsIG5hLnJtID0gVFJVRSksIG1lYW5fYyA9IG1lYW4oY2Fsb3JpZXMsIG5hLnJtID0gVFJVRSksIG1lYW5fcyA9IG1lYW4oc3VnYXJzLCBuYS5ybSA9IFRSVUUpKQ0KaGVhZChtYW51ZmFjdHVyZXJzKQ0KYGBgDQoNCldlIGdlbmVyYXRlZCB0aGUgYXZlcmFnZSByYXRpbmcsIGNhbG9yaWVzIGFuZCBzdWdhcnMgYW1vdW50cyBwZXIgbWFudWZhY3R1cmVyLg0KDQojIyNDb21wbGV0ZSBQb29saW5nIE1vZGVscw0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjcG9vbGluZyA8LSBsbShyYXRpbmcgfiBjYWxvcmllcywgZGF0YSA9IENlcmVhbDMpDQpzdW1tYXJ5KGNwb29saW5nKQ0KYGBgDQoNClRoZSBvdXRwdXQgZm9yIHRoZSBmaXJzdCBjb21wbGV0ZSBwb29saW5nIG1vZGVsIHNob3dzIHRoYXQgdGhlIGF2ZXJhZ2UgY2VyZWFsIHJhdGluZyAod2l0aG91dCBhY2NvdW50aW5nIGZvciBkaWZmZXJlbmNlcyBhbW9uZyBjZXJlYWwgbWFudWZhY3R1cmVycykgaXMgOTUuNzkgaWYgYSBjZXJlYWwgaGFkIDAgY2Fsb3JpZXMgYW5kIHRoYXQgZm9yIGVhY2ggYWRkaXRpb25hbCBjYWxvcmllIHRoZSBjZXJlYWwgcmF0aW5nIGRlY3JlYXNlcyBvbiBhdmVyYWdlIGJ5IDAuNS4gV2Ugc2VlIHRoYXQgdGhlIHJlc3VsdHMgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYXQgYSA5OSUgY29uZmlkZW5jZSBsZXZlbC4gVG8gZ2V0IGEgYmV0dGVyIGxvb2sgYXQgcmF0aW5nIGRpZmZlcmVuY2VzIHdlIGFkZCBmb3IgdGhlIG5leHQgY29tcGxldGUgcG9vbGluZyBtb2RlbCB0aGUgYW1vdW50IG9mIHN1Z2FycyBpbnRvIHRoZSBtb2RlbC4gDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY3Bvb2xpbmcyIDwtIGxtKHJhdGluZyB+IGNhbG9yaWVzICsgc3VnYXJzLCBkYXRhID0gQ2VyZWFsMykNCnN1bW1hcnkoY3Bvb2xpbmcyKQ0KYGBgDQoNClRoZSBvdXRwdXQgZm9yIHRoZSBzZWNvbmQgY29tcGxldGUgcG9vbGluZyBtb2RlbCBzaG93cyB0aGF0IHRoZSBhdmVyYWdlIGNlcmVhbCByYXRpbmcgKHdpdGhvdXQgYWNjb3VudGluZyBmb3IgZGlmZmVyZW5jZXMgYW1vbmcgY2VyZWFsIG1hbnVmYWN0dXJlcnMpIGlzIDg0LjExIGlmIGEgY2VyZWFsIGhhZCAwIGNhbG9yaWVzIGFuZCAwIGdyYW1zIHN1Z2FyIGFuZCB0aGF0IGZvciBlYWNoIGFkZGl0aW9uYWwgY2Fsb3JpZSB0aGUgY2VyZWFsIHJhdGluZyBkZWNyZWFzZXMgb24gYXZlcmFnZSBieSAwLjI4IGFuZCBmb3IgZWFjaCBhZGRpdGlvbmFsIHN1Z2FyIGdyYW0gdGhlIGNlcmVhbCByYXRpbmcgZGVjcmVhc2VzIG9uIGF2ZXJhZ2UgYnkgMS43Mi4gQWdhaW4sIHdlIHNlZSB0aGF0IHRoZSByZXN1bHRzIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGF0IGEgOTklIGNvbmZpZGVuY2UgbGV2ZWwuIEZpbmFsbHksIHdlIHdpbGwgbG9vayBhdCByYXRpbmcgZGlmZmVyZW5jZXMgd2l0aCBhbiBpbnRlcmFjdGlvbiBiZXR3ZWVuIGNhbG9yaWVzIGFuZCBzdWdhciBhbW91bnRzIGZvciB0aGUgY29tcGxldGUgcG9vbGluZyBtb2RlbCANCg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmNwb29saW5nMyA8LSBsbShyYXRpbmcgfiBjYWxvcmllcypzdWdhcnMsIGRhdGEgPSBDZXJlYWwzKQ0Kc3VtbWFyeShjcG9vbGluZzMpDQpgYGANCg0KSW4gdGhlIHRoaXJkIGNvbXBsZXRlIHBvb2xpbmcgbW9kZWwgd2Ugc2VlIHRoYXQgb24gdGhlaXIgb3duIGNhbG9yaWVzIGFuZCBzdWdhciBhbW91bnRzIGFyZSBzdGlsbCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGF0IGEgOTklIGNvbmZpZGVuY2UgbGV2ZWwsIGhvd2V2ZXIgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gdGhlIHR3byBpbmRlcGVkZW50IHZhcmlhYmxlcyBpcyBvbmx5IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYXQgYSA5NSUgY29uZmlkZW5jZSBsZXZlbC4NCg0KDQojIyNDb21wYXJpbmcgdGhlIDMgY29tcGxldGUgcG9vbGluZyBtb2RlbHMNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQ0KaHRtbHJlZyhsaXN0KGNwb29saW5nLGNwb29saW5nMiwgY3Bvb2xpbmczKSkNCmBgYA0KDQpXaGVuIHdlIGNvbXBhcmUgdGhlIHRocmVlIGNvbXBsZXRlIHBvb2xpbmcgbW9kZWxzIHdlIHNlZSB0aGF0IHRoZSB0aGlyZCBtb2RlbCBpcyB0aGUgYmVzdCBmaXR0aW5nIG1vZGVsIHdpdGggYW4gUl4yXiBvZiAwLjcyICh3aGljaCBpcyB0aGUgaGlnaGVzdCBhbW9uZ3N0IHRoZSB0aHJlZSBtb2RlbHMpLg0KDQojIyMgTm8gUG9vbGluZyBNb2RlbA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRjb2VmIDwtIENlcmVhbDMgJT4lIA0KICAgIGdyb3VwX2J5KG5fbWFudWZhY3R1cmVyKSAlPiUgDQogICAgZG8obW9kID0gbG0ocmF0aW5nIH4gY2Fsb3JpZXMgKyBzdWdhcnMsIGRhdGEgPSAuKSkNCmNvZWYgPC0gZGNvZWYgJT4lIGRvKGRhdGEuZnJhbWUoaW50YyA9IGNvZWYoLiRtb2QpWzFdKSkNCmdncGxvdChjb2VmLCBhZXMoeCA9IGludGMpKSArIGdlb21faGlzdG9ncmFtKCkNCmBgYA0KDQpOb3cgd2UgdXNlIHRoZSBObyBwb29saW5nIG1vZGVsIHRvIGxvb2sgYXQgbWFudWZhY3R1cmVyIGRpZmZlcmVuY2VzIGluIHJhdGluZ3MuIFdlIHNlZSB0aGF0IGxhcmdlIGRpZmZlcmVuY2VzIGluIGNlcmVhbCByYXRpbmdzIGFtb25nIGRpZmZlcmVudCBtYW51ZmFjdHVyZXJzLiBUaGUgZGlzdHJpYnV0aW9uIGxvb2tzIHNvbWV3aGF0IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiANCg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmRjb2VmIDwtIENlcmVhbDMgJT4lIA0KICAgIGdyb3VwX2J5KG5fbWFudWZhY3R1cmVyKSAlPiUgDQogICAgZG8obW9kID0gbG0ocmF0aW5nIH4gY2Fsb3JpZXMsIGRhdGEgPSAuKSkNCmNvZWYgPC0gZGNvZWYgJT4lIGRvKGRhdGEuZnJhbWUoY2Fsb3JpZXMgPSBjb2VmKC4kbW9kKVsyXSkpDQpnZ3Bsb3QoY29lZiwgYWVzKHggPSBjYWxvcmllcykpICsgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBgDQoNCldoZW4gbG9va2luZyBhdCByYXRpbmcgZGlmZmVyZW5jZXMgd2l0aCByZXNwZWN0IHRvIGNhbG9yaWVzIHdlIG5vdyBzZWUgYSBzbGlnaHRseSByaWdodCBza2V3ZWQgZGlzdHJpYnV0aW9uIGFzIHRoZSBudW1iZXIgb2YgY2Fsb3JpZXMgaW5jcmVhc2UuDQoNCiMjI1JhbmRvbSBJbnRlcmNlcHQgTW9kZWwNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbTFfY3JhdGluZyA8LSBsbWUocmF0aW5nIH4gY2Fsb3JpZXMsIGRhdGEgPSBDZXJlYWwzLCByYW5kb20gPSB+MXxuX21hbnVmYWN0dXJlciwgbWV0aG9kID0gIk1MIikNCnN1bW1hcnkobTFfY3JhdGluZykNCmBgYA0KDQoNCiMjI1JhbmRvbSBzbG9wZSBNb2RlbA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm0yX2NyYXRpbmcgPC0gbG1lKHJhdGluZyB+ICBzdWdhcnMsIGRhdGEgPSBDZXJlYWwzLCByYW5kb20gPSB+MXxuX21hbnVmYWN0dXJlciwgbWV0aG9kID0gIk1MIikNCnN1bW1hcnkobTJfY3JhdGluZykNCmBgYA0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbTNfY3JhdGluZyA8LSBsbWUocmF0aW5nIH4gY2Fsb3JpZXMgKyBzdWdhcnMsIGRhdGEgPSBDZXJlYWwzLCByYW5kb20gPSB+MXxuX21hbnVmYWN0dXJlciwgbWV0aG9kID0gIk1MIikNCnN1bW1hcnkobTNfY3JhdGluZykNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm00X2NyYXRpbmcgPC0gbG1lKHJhdGluZyB+IGNhbG9yaWVzKnN1Z2FycywgZGF0YSA9IENlcmVhbDMsIHJhbmRvbSA9IH4gc3VnYXJzfG5fbWFudWZhY3R1cmVyLCBtZXRob2QgPSAiTUwiKQ0Kc3VtbWFyeShtNF9jcmF0aW5nKQ0KYGBgDQoNCiMjI0ZpbmFsIE1vZGVsIENvbXBhcmlzb24NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2FzaXMnfQ0KaHRtbHJlZyhsaXN0KG0xX2NyYXRpbmcsIG0yX2NyYXRpbmcsIG0zX2NyYXRpbmcsIG00X2NyYXRpbmcpKQ0KYGBgDQoNCkNvbXBhcmluZyB0aGUgbW9kZWxzLCB3ZSBzZWUgdGhhdCB0aGUgbGFzdCBtb2RlbCB0aGF0IGFjY291bnRzIGZvciBtYW51ZmFjdHVyZXIgZGlmZmVyZW5jZXMgaXMgdGhlIGJlc3QgZml0IG1vZGVsIHNpbmNlIGl0IGhhcyB0aGUgbG93ZXN0IEFJQyBhbmQgQklDICh3aXRoIDUxOC4xOSBhbmQgNTM2Ljk0LCByZXNwZWN0aXZlbHkpLiBMb29raW5nIGF0IHRoZSBtNF9jcmF0aW5nIG1vZGVsIHdlIHNlZSB0aGF0IHRoZXJlIGlzIGEgZGlmZmVyZW5jZSBvZiA1LjQ0IGluIGNlcmVhbCByYXRpbmdzIHdoZW4ga2VlcGluZyB0aGUgYW1vdW5nIG9mIHN1Z2FycyBjb25zdGFudCBhdCB0aGUgaGlnaGVyLWxldmVsIChtYW51ZmFjdHVyZXIgbGV2ZWwpLiBXZSBhbHNvIHNlZSB0aGF0IHRoZXJlIGlzIGEgNS43MiByYXRpbmcgZGlmZmVyZW5jZSBhZnRlciBrZWVwaW5nIGNlcmVhbCBtYW51ZmFjdHVyZXIgYW5kIHN1Z2FyIGFtb3VudCBjb25zdGFudC4NCg0K