Задача 1

Податоците од задачата се:

satisfaction = c(
  65, 34, 54, 47, 100, 100,
  130, 141, 76, 116, 111, 191,
  67, 130, 48, 105, 62, 104
)
level = as.factor(rep(c(rep("low", 2), rep("medium", 2), rep("high", 2)), 3))
housing = as.factor(c(rep("tower", 6), rep("apartment", 6), rep("house", 6)))
contact = as.factor(rep(c("low", "high"), 9))

sat_df = data.frame(housing, contact, level, satisfaction)
sat_df

(a)

Онаму каде што имало повисок контакт, имало и поголемо задоволство.

contact = c("low", "high")
low_sat_sum = c(
  sum(sat_df[sat_df$contact == "low" & sat_df$level == "low", ]$satisfaction),
  sum(sat_df[sat_df$contact == "high" & sat_df$level == "low", ]$satisfaction)
)

medium_sat_sum = c(
  sum(sat_df[sat_df$contact == "low" & sat_df$level == "medium", ]$satisfaction),
  sum(sat_df[sat_df$contact == "high" & sat_df$level == "medium", ]$satisfaction)
)

high_sat_sum = c(
  sum(sat_df[sat_df$contact == "low" & sat_df$level == "high", ]$satisfaction),
  sum(sat_df[sat_df$contact == "high" & sat_df$level == "high", ]$satisfaction)
)

low_sat_pct = low_sat_sum / (low_sat_sum + medium_sat_sum + high_sat_sum)
medium_sat_pct = medium_sat_sum / (low_sat_sum + medium_sat_sum + high_sat_sum)
high_sat_pct = high_sat_sum / (low_sat_sum + medium_sat_sum + high_sat_sum)

data.frame(contact, low_sat_pct, medium_sat_pct, high_sat_pct)

Најголемо задоволство имало во tower.

housing = c("tower", "apartment", "house")
low_sat_sum = c(
  sum(sat_df[sat_df$housing == "tower" & sat_df$level == "low", ]$satisfaction),
  sum(sat_df[sat_df$housing == "apartment" & sat_df$level == "low", ]$satisfaction),
  sum(sat_df[sat_df$housing == "house" & sat_df$level == "low", ]$satisfaction)
)

medium_sat_sum = c(
  sum(sat_df[sat_df$housing == "tower" & sat_df$level == "medium", ]$satisfaction),
  sum(sat_df[sat_df$housing == "apartment" & sat_df$level == "medium", ]$satisfaction),
  sum(sat_df[sat_df$housing == "house" & sat_df$level == "medium", ]$satisfaction)
)

high_sat_sum = c(
  sum(sat_df[sat_df$housing == "tower" & sat_df$level == "high", ]$satisfaction),
  sum(sat_df[sat_df$housing == "apartment" & sat_df$level == "high", ]$satisfaction),
  sum(sat_df[sat_df$housing == "house" & sat_df$level == "high", ]$satisfaction)
)

low_sat_pct = low_sat_sum / (low_sat_sum + medium_sat_sum + high_sat_sum)
medium_sat_pct = medium_sat_sum / (low_sat_sum + medium_sat_sum + high_sat_sum)
high_sat_pct = high_sat_sum / (low_sat_sum + medium_sat_sum + high_sat_sum)

data.frame(housing, low_sat_pct, medium_sat_pct, high_sat_pct)

Во tower имало најнизок контакт, а во house имало највисок контакт.

housing = c("tower", "apartment", "house")
low_contact_sum = c(
  sum(sat_df[sat_df$housing == "tower" & sat_df$contact == "low", ]$satisfaction),
  sum(sat_df[sat_df$housing == "apartment" & sat_df$contact == "low", ]$satisfaction),
  sum(sat_df[sat_df$housing == "house" & sat_df$contact == "low", ]$satisfaction)
)

high_contact_sum = c(
  sum(sat_df[sat_df$housing == "tower" & sat_df$contact == "high", ]$satisfaction),
  sum(sat_df[sat_df$housing == "apartment" & sat_df$contact == "high", ]$satisfaction),
  sum(sat_df[sat_df$housing == "house" & sat_df$contact == "high", ]$satisfaction)
)

low_contact_pct = low_contact_sum / (low_contact_sum + high_contact_sum)
high_contact_pct = high_contact_sum / (low_contact_sum + high_contact_sum)

data.frame(housing, low_contact_pct, high_contact_pct)

(b)

library(nnet)

low_satisfaction = sat_df[sat_df$level == "low", ]$satisfaction
medium_satisfaction = sat_df[sat_df$level == "medium", ]$satisfaction
high_satisfaction = sat_df[sat_df$level == "high", ]$satisfaction
satisfaction = cbind(low_satisfaction, medium_satisfaction, high_satisfaction)

contact = sat_df[sat_df$level == "low", ]$contact
housing = sat_df[sat_df$level == "low", ]$housing

multinom_sat_df = data.frame(contact, housing, satisfaction)
multinom_sat_df
multinom_model = multinom(
  satisfaction ~ contact * housing, 
  data=multinom_sat_df
)
# weights:  21 (12 variable)
initial  value 1846.767257 
iter  10 value 1800.128659
final  value 1799.293647 
converged
summary(multinom_model)
Call:
multinom(formula = satisfaction ~ contact * housing, data = multinom_sat_df)

Coefficients:
                    (Intercept) contactlow housinghouse housingtower
medium_satisfaction  -0.1951677  -0.341634  -0.01840665    0.5189502
high_satisfaction     0.3035139  -0.461520  -0.52665690    0.7752913
                    contactlow:housinghouse contactlow:housingtower
medium_satisfaction               0.2217172              -0.1675522
high_satisfaction                 0.6071035              -0.1865006

Std. Errors:
                    (Intercept) contactlow housinghouse housingtower
medium_satisfaction   0.1253510  0.1912147    0.1814635    0.2576842
high_satisfaction     0.1110307  0.1703794    0.1721496    0.2274631
                    contactlow:housinghouse contactlow:housingtower
medium_satisfaction               0.2992288               0.3480726
high_satisfaction                 0.2781928               0.3063093

Residual Deviance: 3598.587 
AIC: 3622.587 

(c)

Да, ординален модел би одговарал во ситуацијава, бидејќи нивоата на задоволство се подредени. Мораме да ги обработиме податоците за да можеме да креираме ординален модел.

library(MASS)

contact = c()
housing = c()
satisfaction = c()

for (idx in 1:nrow(multinom_sat_df)) {
  row = multinom_sat_df[idx, ]
  for (sat in c("low", "medium", "high")) {
    col_name = paste(sat, "satisfaction", sep="_")
    cnt = row[[col_name]]
    contact = append(contact, rep(row$contact, cnt))
    housing = append(housing, rep(row$housing, cnt))
    satisfaction = append(satisfaction, rep(sat, cnt))
  }
}

satisfaction = factor(
  satisfaction, 
  ordered=TRUE, 
  levels=c("low", "medium", "high")
)

ordered_sat_df = data.frame(housing, contact, satisfaction)
ordered_sat_df

polr_model = polr(
  satisfaction ~ housing * contact, 
  data=ordered_sat_df
)
summary(polr_model)

Re-fitting to get Hessian
Call:
polr(formula = satisfaction ~ housing * contact, data = ordered_sat_df)

Coefficients:
                   Value Std. Error t value
housing          0.08112     0.1769  0.4585
contact         -0.32911     0.2234 -1.4731
housing:contact  0.07621     0.1133  0.6728

Intercepts:
            Value   Std. Error t value
low|medium  -0.8064  0.3409    -2.3655
medium|high  0.2937  0.3403     0.8629

Residual Deviance: 3633.392 
AIC: 3643.392 

(d)

За номиналниот модел (тој е делумно подобар според резултатите, иако нема некоја разлика), можеме да забележиме дека резидуалите се доста ниски (помали од 0.0001, што е навистина добро).

multinom_sat_df["sums"] = 
  multinom_sat_df["low_satisfaction"] + 
  multinom_sat_df["medium_satisfaction"] + 
  multinom_sat_df["high_satisfaction"]

fitted_values = fitted(multinom_model)
multinom_sat_df["low_residual"] = 
  as.vector(fitted_values[, 1]) * multinom_sat_df["sums"] - 
  multinom_sat_df["low_satisfaction"]
multinom_sat_df["medium_residual"] = 
  as.vector(fitted_values[, 2]) * multinom_sat_df["sums"] - 
  multinom_sat_df["medium_satisfaction"]
multinom_sat_df["high_residual"] = 
  as.vector(fitted_values[, 3]) * multinom_sat_df["sums"]- 
  multinom_sat_df["high_satisfaction"]

Задача 3

Податоците од задалата се:

cnt = c(28, 45, 29, 26, 4, 12, 5, 2, 41, 44, 20, 20, 12, 7, 3, 1)
response = rep(c("progressive", "no_change", "partial", "complete"), 4)
sex = rep(c(rep("male", 4), rep("female", 4)), 2)
treatment = c(rep("sequential", 8), rep("alternating", 8))

tumor_df = data.frame(sex, treatment, response, cnt)
tumor_df

(a)

Ги обработуваме податоците слично како во втората задача:

sex = c()
treatment = c()
response = c()

for (idx in 1:nrow(tumor_df)) {
  row = tumor_df[idx, ]
  sex = append(sex, rep(row$sex, row$cnt))
  treatment = append(treatment, rep(row$treatment, row$cnt))
  response = append(response, rep(row$response, row$cnt))
}

sex = as.factor(sex)
treatment = as.factor(treatment)
response = factor(
  response, 
  levels=c("progressive", "no_change", "partial", "complete"),
  ordered=TRUE
)
ordered_tumor_df = data.frame(sex, treatment, response)
ordered_tumor_df

Ова е моделот:

tumor_model = polr(
  response ~ sex + treatment, 
  data=ordered_tumor_df, 
  Hess=TRUE
)
summary(tumor_model)
Call:
polr(formula = response ~ sex + treatment, data = ordered_tumor_df, 
    Hess = TRUE)

Coefficients:
                     Value Std. Error t value
sexmale             0.5414     0.2872   1.885
treatmentsequential 0.5807     0.2121   2.737

Intercepts:
                      Value   Std. Error t value
progressive|no_change -0.1960  0.2893    -0.6774
no_change|partial      1.3713  0.3000     4.5706
partial|complete       2.4221  0.3224     7.5119

Residual Deviance: 789.0566 
AIC: 799.0566 

(b)

Гледајќи ги резидуалите, false postives и false negatives за секоја класа, и правејќи го хи-квадрат тестот за точните и предвидените фреквенции, заклучуваме дека моделот не ги учи добро податоците.

y_pred = c(predict(tumor_model))
y_true = c(ordered_tumor_df$response)

correct_freqs = c(0, 0, 0, 0)
predicted_freqs = c(0, 0, 0, 0)
residuals = c(0, 0, 0, 0)
fps = c(0, 0, 0, 0)
fns = c(0, 0, 0, 0)

for (i in 1:length(y_true)) {
  correct_freqs[y_true[i]] = correct_freqs[y_true[i]] + 1
  predicted_freqs[y_pred[i]] = predicted_freqs[y_pred[i]] + 1
  if (y_pred[i] != y_true[i]) {
    residuals[y_pred[i]] = residuals[y_pred[i]] + 1
    residuals[y_true[i]] = residuals[y_true[i]] + 1
    fps[y_pred[i]] = fps[y_pred[i]] + 1
    fns[y_true[i]] = fns[y_true[i]] + 1
  }
}

data.frame(correct_freqs, predicted_freqs, residuals, fps, fns)
chisq.test(correct_freqs, predicted_freqs)
Chi-squared approximation may be incorrect

    Pearson's Chi-squared test

data:  correct_freqs and predicted_freqs
X-squared = 8, df = 6, p-value = 0.2381

(c)

Од големата p-вредност тестот на Валд сугерира дека има разлика во различните третмани.

library(lmtest)
treatment_model = polr(response ~ treatment, data=ordered_sat_df, Hess=TRUE)
waldtest(treatment_model)
Wald test

Model 1: response ~ treatment
Model 2: response ~ 1
  Res.Df Df  Chisq Pr(>Chisq)   
1    295                        
2    296 -1 7.2424    0.00712 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(d)

И тестот на Wald и обичното summary на моделот каде што можеме да ги споредиме AIC вредностите сугерираат дека моделот кој што зема предвид treatment е подобар и ја поддржуваат тезата дека има разлика помеѓу различните третмани.

no_treatment = polr(response ~ sex, data=ordered_sat_df, Hess=TRUE)
summary(no_treatment)
Call:
polr(formula = response ~ sex, data = ordered_sat_df, Hess = TRUE)

Coefficients:
         Value Std. Error t value
sexmale 0.5219     0.2871   1.818

Intercepts:
                      Value   Std. Error t value
progressive|no_change -0.4932  0.2682    -1.8385
no_change|partial      1.0407  0.2729     3.8137
partial|complete       2.0790  0.2945     7.0600

Residual Deviance: 796.6268 
AIC: 804.6268 
waldtest(no_treatment)
Wald test

Model 1: response ~ sex
Model 2: response ~ 1
  Res.Df Df  Chisq Pr(>Chisq)  
1    295                       
2    296 -1 3.3048    0.06908 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
yes_treatment = polr(
  response ~ sex + treatment, 
  data=ordered_sat_df, 
  Hess=TRUE
)
summary(yes_treatment)
Call:
polr(formula = response ~ sex + treatment, data = ordered_sat_df, 
    Hess = TRUE)

Coefficients:
                     Value Std. Error t value
sexmale             0.5414     0.2872   1.885
treatmentsequential 0.5807     0.2121   2.737

Intercepts:
                      Value   Std. Error t value
progressive|no_change -0.1960  0.2893    -0.6774
no_change|partial      1.3713  0.3000     4.5706
partial|complete       2.4221  0.3224     7.5119

Residual Deviance: 789.0566 
AIC: 799.0566 
waldtest(yes_treatment)
Wald test

Model 1: response ~ sex + treatment
Model 2: response ~ 1
  Res.Df Df  Chisq Pr(>Chisq)   
1    294                        
2    296 -2 10.738   0.004659 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
LS0tDQp0aXRsZTogItCT0LvQsNCy0LAgODog0J3QvtC80LjQvdCw0LvQvdCwINC4INC+0YDQtNC40L3QsNC70L3QsCDQu9C+0LPQuNGB0YLQuNGH0LrQsCDRgNC10LPRgNC10YHQuNGY0LAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojINCX0LDQtNCw0YfQsCAxDQoNCtCf0L7QtNCw0YLQvtGG0LjRgtC1INC+0LQg0LfQsNC00LDRh9Cw0YLQsCDRgdC1Og0KDQpgYGB7cn0NCnNhdGlzZmFjdGlvbiA9IGMoDQogIDY1LCAzNCwgNTQsIDQ3LCAxMDAsIDEwMCwNCiAgMTMwLCAxNDEsIDc2LCAxMTYsIDExMSwgMTkxLA0KICA2NywgMTMwLCA0OCwgMTA1LCA2MiwgMTA0DQopDQpsZXZlbCA9IGFzLmZhY3RvcihyZXAoYyhyZXAoImxvdyIsIDIpLCByZXAoIm1lZGl1bSIsIDIpLCByZXAoImhpZ2giLCAyKSksIDMpKQ0KaG91c2luZyA9IGFzLmZhY3RvcihjKHJlcCgidG93ZXIiLCA2KSwgcmVwKCJhcGFydG1lbnQiLCA2KSwgcmVwKCJob3VzZSIsIDYpKSkNCmNvbnRhY3QgPSBhcy5mYWN0b3IocmVwKGMoImxvdyIsICJoaWdoIiksIDkpKQ0KDQpzYXRfZGYgPSBkYXRhLmZyYW1lKGhvdXNpbmcsIGNvbnRhY3QsIGxldmVsLCBzYXRpc2ZhY3Rpb24pDQpzYXRfZGYNCmBgYA0KDQojIyMgKGEpDQoNCtCe0L3QsNC80YMg0LrQsNC00LUg0YjRgtC+INC40LzQsNC70L4g0L/QvtCy0LjRgdC+0Log0LrQvtC90YLQsNC60YIsINC40LzQsNC70L4g0Lgg0L/QvtCz0L7Qu9C10LzQviDQt9Cw0LTQvtCy0L7Qu9GB0YLQstC+Lg0KDQpgYGB7cn0NCmNvbnRhY3QgPSBjKCJsb3ciLCAiaGlnaCIpDQpsb3dfc2F0X3N1bSA9IGMoDQogIHN1bShzYXRfZGZbc2F0X2RmJGNvbnRhY3QgPT0gImxvdyIgJiBzYXRfZGYkbGV2ZWwgPT0gImxvdyIsIF0kc2F0aXNmYWN0aW9uKSwNCiAgc3VtKHNhdF9kZltzYXRfZGYkY29udGFjdCA9PSAiaGlnaCIgJiBzYXRfZGYkbGV2ZWwgPT0gImxvdyIsIF0kc2F0aXNmYWN0aW9uKQ0KKQ0KDQptZWRpdW1fc2F0X3N1bSA9IGMoDQogIHN1bShzYXRfZGZbc2F0X2RmJGNvbnRhY3QgPT0gImxvdyIgJiBzYXRfZGYkbGV2ZWwgPT0gIm1lZGl1bSIsIF0kc2F0aXNmYWN0aW9uKSwNCiAgc3VtKHNhdF9kZltzYXRfZGYkY29udGFjdCA9PSAiaGlnaCIgJiBzYXRfZGYkbGV2ZWwgPT0gIm1lZGl1bSIsIF0kc2F0aXNmYWN0aW9uKQ0KKQ0KDQpoaWdoX3NhdF9zdW0gPSBjKA0KICBzdW0oc2F0X2RmW3NhdF9kZiRjb250YWN0ID09ICJsb3ciICYgc2F0X2RmJGxldmVsID09ICJoaWdoIiwgXSRzYXRpc2ZhY3Rpb24pLA0KICBzdW0oc2F0X2RmW3NhdF9kZiRjb250YWN0ID09ICJoaWdoIiAmIHNhdF9kZiRsZXZlbCA9PSAiaGlnaCIsIF0kc2F0aXNmYWN0aW9uKQ0KKQ0KDQpsb3dfc2F0X3BjdCA9IGxvd19zYXRfc3VtIC8gKGxvd19zYXRfc3VtICsgbWVkaXVtX3NhdF9zdW0gKyBoaWdoX3NhdF9zdW0pDQptZWRpdW1fc2F0X3BjdCA9IG1lZGl1bV9zYXRfc3VtIC8gKGxvd19zYXRfc3VtICsgbWVkaXVtX3NhdF9zdW0gKyBoaWdoX3NhdF9zdW0pDQpoaWdoX3NhdF9wY3QgPSBoaWdoX3NhdF9zdW0gLyAobG93X3NhdF9zdW0gKyBtZWRpdW1fc2F0X3N1bSArIGhpZ2hfc2F0X3N1bSkNCg0KZGF0YS5mcmFtZShjb250YWN0LCBsb3dfc2F0X3BjdCwgbWVkaXVtX3NhdF9wY3QsIGhpZ2hfc2F0X3BjdCkNCmBgYA0KDQrQndCw0ZjQs9C+0LvQtdC80L4g0LfQsNC00L7QstC+0LvRgdGC0LLQviDQuNC80LDQu9C+INCy0L4gdG93ZXIuDQoNCmBgYHtyfQ0KaG91c2luZyA9IGMoInRvd2VyIiwgImFwYXJ0bWVudCIsICJob3VzZSIpDQpsb3dfc2F0X3N1bSA9IGMoDQogIHN1bShzYXRfZGZbc2F0X2RmJGhvdXNpbmcgPT0gInRvd2VyIiAmIHNhdF9kZiRsZXZlbCA9PSAibG93IiwgXSRzYXRpc2ZhY3Rpb24pLA0KICBzdW0oc2F0X2RmW3NhdF9kZiRob3VzaW5nID09ICJhcGFydG1lbnQiICYgc2F0X2RmJGxldmVsID09ICJsb3ciLCBdJHNhdGlzZmFjdGlvbiksDQogIHN1bShzYXRfZGZbc2F0X2RmJGhvdXNpbmcgPT0gImhvdXNlIiAmIHNhdF9kZiRsZXZlbCA9PSAibG93IiwgXSRzYXRpc2ZhY3Rpb24pDQopDQoNCm1lZGl1bV9zYXRfc3VtID0gYygNCiAgc3VtKHNhdF9kZltzYXRfZGYkaG91c2luZyA9PSAidG93ZXIiICYgc2F0X2RmJGxldmVsID09ICJtZWRpdW0iLCBdJHNhdGlzZmFjdGlvbiksDQogIHN1bShzYXRfZGZbc2F0X2RmJGhvdXNpbmcgPT0gImFwYXJ0bWVudCIgJiBzYXRfZGYkbGV2ZWwgPT0gIm1lZGl1bSIsIF0kc2F0aXNmYWN0aW9uKSwNCiAgc3VtKHNhdF9kZltzYXRfZGYkaG91c2luZyA9PSAiaG91c2UiICYgc2F0X2RmJGxldmVsID09ICJtZWRpdW0iLCBdJHNhdGlzZmFjdGlvbikNCikNCg0KaGlnaF9zYXRfc3VtID0gYygNCiAgc3VtKHNhdF9kZltzYXRfZGYkaG91c2luZyA9PSAidG93ZXIiICYgc2F0X2RmJGxldmVsID09ICJoaWdoIiwgXSRzYXRpc2ZhY3Rpb24pLA0KICBzdW0oc2F0X2RmW3NhdF9kZiRob3VzaW5nID09ICJhcGFydG1lbnQiICYgc2F0X2RmJGxldmVsID09ICJoaWdoIiwgXSRzYXRpc2ZhY3Rpb24pLA0KICBzdW0oc2F0X2RmW3NhdF9kZiRob3VzaW5nID09ICJob3VzZSIgJiBzYXRfZGYkbGV2ZWwgPT0gImhpZ2giLCBdJHNhdGlzZmFjdGlvbikNCikNCg0KbG93X3NhdF9wY3QgPSBsb3dfc2F0X3N1bSAvIChsb3dfc2F0X3N1bSArIG1lZGl1bV9zYXRfc3VtICsgaGlnaF9zYXRfc3VtKQ0KbWVkaXVtX3NhdF9wY3QgPSBtZWRpdW1fc2F0X3N1bSAvIChsb3dfc2F0X3N1bSArIG1lZGl1bV9zYXRfc3VtICsgaGlnaF9zYXRfc3VtKQ0KaGlnaF9zYXRfcGN0ID0gaGlnaF9zYXRfc3VtIC8gKGxvd19zYXRfc3VtICsgbWVkaXVtX3NhdF9zdW0gKyBoaWdoX3NhdF9zdW0pDQoNCmRhdGEuZnJhbWUoaG91c2luZywgbG93X3NhdF9wY3QsIG1lZGl1bV9zYXRfcGN0LCBoaWdoX3NhdF9wY3QpDQpgYGANCg0K0JLQviB0b3dlciDQuNC80LDQu9C+INC90LDRmNC90LjQt9C+0Log0LrQvtC90YLQsNC60YIsINCwINCy0L4gaG91c2Ug0LjQvNCw0LvQviDQvdCw0ZjQstC40YHQvtC6INC60L7QvdGC0LDQutGCLg0KDQpgYGB7cn0NCmhvdXNpbmcgPSBjKCJ0b3dlciIsICJhcGFydG1lbnQiLCAiaG91c2UiKQ0KbG93X2NvbnRhY3Rfc3VtID0gYygNCiAgc3VtKHNhdF9kZltzYXRfZGYkaG91c2luZyA9PSAidG93ZXIiICYgc2F0X2RmJGNvbnRhY3QgPT0gImxvdyIsIF0kc2F0aXNmYWN0aW9uKSwNCiAgc3VtKHNhdF9kZltzYXRfZGYkaG91c2luZyA9PSAiYXBhcnRtZW50IiAmIHNhdF9kZiRjb250YWN0ID09ICJsb3ciLCBdJHNhdGlzZmFjdGlvbiksDQogIHN1bShzYXRfZGZbc2F0X2RmJGhvdXNpbmcgPT0gImhvdXNlIiAmIHNhdF9kZiRjb250YWN0ID09ICJsb3ciLCBdJHNhdGlzZmFjdGlvbikNCikNCg0KaGlnaF9jb250YWN0X3N1bSA9IGMoDQogIHN1bShzYXRfZGZbc2F0X2RmJGhvdXNpbmcgPT0gInRvd2VyIiAmIHNhdF9kZiRjb250YWN0ID09ICJoaWdoIiwgXSRzYXRpc2ZhY3Rpb24pLA0KICBzdW0oc2F0X2RmW3NhdF9kZiRob3VzaW5nID09ICJhcGFydG1lbnQiICYgc2F0X2RmJGNvbnRhY3QgPT0gImhpZ2giLCBdJHNhdGlzZmFjdGlvbiksDQogIHN1bShzYXRfZGZbc2F0X2RmJGhvdXNpbmcgPT0gImhvdXNlIiAmIHNhdF9kZiRjb250YWN0ID09ICJoaWdoIiwgXSRzYXRpc2ZhY3Rpb24pDQopDQoNCmxvd19jb250YWN0X3BjdCA9IGxvd19jb250YWN0X3N1bSAvIChsb3dfY29udGFjdF9zdW0gKyBoaWdoX2NvbnRhY3Rfc3VtKQ0KaGlnaF9jb250YWN0X3BjdCA9IGhpZ2hfY29udGFjdF9zdW0gLyAobG93X2NvbnRhY3Rfc3VtICsgaGlnaF9jb250YWN0X3N1bSkNCg0KZGF0YS5mcmFtZShob3VzaW5nLCBsb3dfY29udGFjdF9wY3QsIGhpZ2hfY29udGFjdF9wY3QpDQpgYGANCg0KIyMjIChiKQ0KDQpgYGB7cn0NCmxpYnJhcnkobm5ldCkNCg0KbG93X3NhdGlzZmFjdGlvbiA9IHNhdF9kZltzYXRfZGYkbGV2ZWwgPT0gImxvdyIsIF0kc2F0aXNmYWN0aW9uDQptZWRpdW1fc2F0aXNmYWN0aW9uID0gc2F0X2RmW3NhdF9kZiRsZXZlbCA9PSAibWVkaXVtIiwgXSRzYXRpc2ZhY3Rpb24NCmhpZ2hfc2F0aXNmYWN0aW9uID0gc2F0X2RmW3NhdF9kZiRsZXZlbCA9PSAiaGlnaCIsIF0kc2F0aXNmYWN0aW9uDQpzYXRpc2ZhY3Rpb24gPSBjYmluZChsb3dfc2F0aXNmYWN0aW9uLCBtZWRpdW1fc2F0aXNmYWN0aW9uLCBoaWdoX3NhdGlzZmFjdGlvbikNCg0KY29udGFjdCA9IHNhdF9kZltzYXRfZGYkbGV2ZWwgPT0gImxvdyIsIF0kY29udGFjdA0KaG91c2luZyA9IHNhdF9kZltzYXRfZGYkbGV2ZWwgPT0gImxvdyIsIF0kaG91c2luZw0KDQptdWx0aW5vbV9zYXRfZGYgPSBkYXRhLmZyYW1lKGNvbnRhY3QsIGhvdXNpbmcsIHNhdGlzZmFjdGlvbikNCm11bHRpbm9tX3NhdF9kZg0KbXVsdGlub21fbW9kZWwgPSBtdWx0aW5vbSgNCiAgc2F0aXNmYWN0aW9uIH4gY29udGFjdCAqIGhvdXNpbmcsIA0KICBkYXRhPW11bHRpbm9tX3NhdF9kZg0KKQ0Kc3VtbWFyeShtdWx0aW5vbV9tb2RlbCkNCmBgYA0KDQojIyMgKGMpDQoNCtCU0LAsINC+0YDQtNC40L3QsNC70LXQvSDQvNC+0LTQtdC7INCx0Lgg0L7QtNCz0L7QstCw0YDQsNC7INCy0L4g0YHQuNGC0YPQsNGG0LjRmNCw0LLQsCwg0LHQuNC00LXRmNGc0Lgg0L3QuNCy0L7QsNGC0LAg0L3QsCDQt9Cw0LTQvtCy0L7Qu9GB0YLQstC+INGB0LUg0L/QvtC00YDQtdC00LXQvdC4LiDQnNC+0YDQsNC80LUg0LTQsCDQs9C4INC+0LHRgNCw0LHQvtGC0LjQvNC1INC/0L7QtNCw0YLQvtGG0LjRgtC1INC30LAg0LTQsCDQvNC+0LbQtdC80LUg0LTQsCDQutGA0LXQuNGA0LDQvNC1INC+0YDQtNC40L3QsNC70LXQvSDQvNC+0LTQtdC7Lg0KDQpgYGB7cn0NCmxpYnJhcnkoTUFTUykNCg0KY29udGFjdCA9IGMoKQ0KaG91c2luZyA9IGMoKQ0Kc2F0aXNmYWN0aW9uID0gYygpDQoNCmZvciAoaWR4IGluIDE6bnJvdyhtdWx0aW5vbV9zYXRfZGYpKSB7DQogIHJvdyA9IG11bHRpbm9tX3NhdF9kZltpZHgsIF0NCiAgZm9yIChzYXQgaW4gYygibG93IiwgIm1lZGl1bSIsICJoaWdoIikpIHsNCiAgICBjb2xfbmFtZSA9IHBhc3RlKHNhdCwgInNhdGlzZmFjdGlvbiIsIHNlcD0iXyIpDQogICAgY250ID0gcm93W1tjb2xfbmFtZV1dDQogICAgY29udGFjdCA9IGFwcGVuZChjb250YWN0LCByZXAocm93JGNvbnRhY3QsIGNudCkpDQogICAgaG91c2luZyA9IGFwcGVuZChob3VzaW5nLCByZXAocm93JGhvdXNpbmcsIGNudCkpDQogICAgc2F0aXNmYWN0aW9uID0gYXBwZW5kKHNhdGlzZmFjdGlvbiwgcmVwKHNhdCwgY250KSkNCiAgfQ0KfQ0KDQpzYXRpc2ZhY3Rpb24gPSBmYWN0b3IoDQogIHNhdGlzZmFjdGlvbiwgDQogIG9yZGVyZWQ9VFJVRSwgDQogIGxldmVscz1jKCJsb3ciLCAibWVkaXVtIiwgImhpZ2giKQ0KKQ0KDQpvcmRlcmVkX3NhdF9kZiA9IGRhdGEuZnJhbWUoaG91c2luZywgY29udGFjdCwgc2F0aXNmYWN0aW9uKQ0Kb3JkZXJlZF9zYXRfZGYNCg0KcG9scl9tb2RlbCA9IHBvbHIoDQogIHNhdGlzZmFjdGlvbiB+IGhvdXNpbmcgKiBjb250YWN0LCANCiAgZGF0YT1vcmRlcmVkX3NhdF9kZg0KKQ0Kc3VtbWFyeShwb2xyX21vZGVsKQ0KYGBgDQoNCiMjIyAoZCkNCg0K0JfQsCDQvdC+0LzQuNC90LDQu9C90LjQvtGCINC80L7QtNC10LsgKNGC0L7RmCDQtSDQtNC10LvRg9C80L3QviDQv9C+0LTQvtCx0LDRgCDRgdC/0L7RgNC10LQg0YDQtdC30YPQu9GC0LDRgtC40YLQtSwg0LjQsNC60L4g0L3QtdC80LAg0L3QtdC60L7RmNCwINGA0LDQt9C70LjQutCwKSwg0LzQvtC20LXQvNC1INC00LAg0LfQsNCx0LXQu9C10LbQuNC80LUg0LTQtdC60LAg0YDQtdC30LjQtNGD0LDQu9C40YLQtSDRgdC1INC00L7RgdGC0LAg0L3QuNGB0LrQuCAo0L/QvtC80LDQu9C4INC+0LQgMC4wMDAxLCDRiNGC0L4g0LUg0L3QsNCy0LjRgdGC0LjQvdCwINC00L7QsdGA0L4pLg0KDQpgYGB7cn0NCm11bHRpbm9tX3NhdF9kZlsic3VtcyJdID0gDQogIG11bHRpbm9tX3NhdF9kZlsibG93X3NhdGlzZmFjdGlvbiJdICsgDQogIG11bHRpbm9tX3NhdF9kZlsibWVkaXVtX3NhdGlzZmFjdGlvbiJdICsgDQogIG11bHRpbm9tX3NhdF9kZlsiaGlnaF9zYXRpc2ZhY3Rpb24iXQ0KDQpmaXR0ZWRfdmFsdWVzID0gZml0dGVkKG11bHRpbm9tX21vZGVsKQ0KbXVsdGlub21fc2F0X2RmWyJsb3dfcmVzaWR1YWwiXSA9IA0KICBhcy52ZWN0b3IoZml0dGVkX3ZhbHVlc1ssIDFdKSAqIG11bHRpbm9tX3NhdF9kZlsic3VtcyJdIC0gDQogIG11bHRpbm9tX3NhdF9kZlsibG93X3NhdGlzZmFjdGlvbiJdDQptdWx0aW5vbV9zYXRfZGZbIm1lZGl1bV9yZXNpZHVhbCJdID0gDQogIGFzLnZlY3RvcihmaXR0ZWRfdmFsdWVzWywgMl0pICogbXVsdGlub21fc2F0X2RmWyJzdW1zIl0gLSANCiAgbXVsdGlub21fc2F0X2RmWyJtZWRpdW1fc2F0aXNmYWN0aW9uIl0NCm11bHRpbm9tX3NhdF9kZlsiaGlnaF9yZXNpZHVhbCJdID0gDQogIGFzLnZlY3RvcihmaXR0ZWRfdmFsdWVzWywgM10pICogbXVsdGlub21fc2F0X2RmWyJzdW1zIl0tIA0KICBtdWx0aW5vbV9zYXRfZGZbImhpZ2hfc2F0aXNmYWN0aW9uIl0NCg0KbXVsdGlub21fc2F0X2RmDQpgYGANCg0KIyDQl9Cw0LTQsNGH0LAgMw0KDQrQn9C+0LTQsNGC0L7RhtC40YLQtSDQvtC0INC30LDQtNCw0LvQsNGC0LAg0YHQtToNCg0KYGBge3J9DQpjbnQgPSBjKDI4LCA0NSwgMjksIDI2LCA0LCAxMiwgNSwgMiwgNDEsIDQ0LCAyMCwgMjAsIDEyLCA3LCAzLCAxKQ0KcmVzcG9uc2UgPSByZXAoYygicHJvZ3Jlc3NpdmUiLCAibm9fY2hhbmdlIiwgInBhcnRpYWwiLCAiY29tcGxldGUiKSwgNCkNCnNleCA9IHJlcChjKHJlcCgibWFsZSIsIDQpLCByZXAoImZlbWFsZSIsIDQpKSwgMikNCnRyZWF0bWVudCA9IGMocmVwKCJzZXF1ZW50aWFsIiwgOCksIHJlcCgiYWx0ZXJuYXRpbmciLCA4KSkNCg0KdHVtb3JfZGYgPSBkYXRhLmZyYW1lKHNleCwgdHJlYXRtZW50LCByZXNwb25zZSwgY250KQ0KdHVtb3JfZGYNCmBgYA0KDQojIyMgKGEpDQoNCtCT0Lgg0L7QsdGA0LDQsdC+0YLRg9Cy0LDQvNC1INC/0L7QtNCw0YLQvtGG0LjRgtC1INGB0LvQuNGH0L3QviDQutCw0LrQviDQstC+INCy0YLQvtGA0LDRgtCwINC30LDQtNCw0YfQsDoNCg0KYGBge3J9DQpzZXggPSBjKCkNCnRyZWF0bWVudCA9IGMoKQ0KcmVzcG9uc2UgPSBjKCkNCg0KZm9yIChpZHggaW4gMTpucm93KHR1bW9yX2RmKSkgew0KICByb3cgPSB0dW1vcl9kZltpZHgsIF0NCiAgc2V4ID0gYXBwZW5kKHNleCwgcmVwKHJvdyRzZXgsIHJvdyRjbnQpKQ0KICB0cmVhdG1lbnQgPSBhcHBlbmQodHJlYXRtZW50LCByZXAocm93JHRyZWF0bWVudCwgcm93JGNudCkpDQogIHJlc3BvbnNlID0gYXBwZW5kKHJlc3BvbnNlLCByZXAocm93JHJlc3BvbnNlLCByb3ckY250KSkNCn0NCg0Kc2V4ID0gYXMuZmFjdG9yKHNleCkNCnRyZWF0bWVudCA9IGFzLmZhY3Rvcih0cmVhdG1lbnQpDQpyZXNwb25zZSA9IGZhY3RvcigNCiAgcmVzcG9uc2UsIA0KICBsZXZlbHM9YygicHJvZ3Jlc3NpdmUiLCAibm9fY2hhbmdlIiwgInBhcnRpYWwiLCAiY29tcGxldGUiKSwNCiAgb3JkZXJlZD1UUlVFDQopDQpvcmRlcmVkX3R1bW9yX2RmID0gZGF0YS5mcmFtZShzZXgsIHRyZWF0bWVudCwgcmVzcG9uc2UpDQpvcmRlcmVkX3R1bW9yX2RmDQpgYGANCg0K0J7QstCwINC1INC80L7QtNC10LvQvtGCOg0KDQpgYGB7cn0NCnR1bW9yX21vZGVsID0gcG9scigNCiAgcmVzcG9uc2UgfiBzZXggKyB0cmVhdG1lbnQsIA0KICBkYXRhPW9yZGVyZWRfdHVtb3JfZGYsIA0KICBIZXNzPVRSVUUNCikNCnN1bW1hcnkodHVtb3JfbW9kZWwpDQpgYGANCg0KIyMjIChiKQ0KDQrQk9C70LXQtNCw0ZjRnNC4INCz0Lgg0YDQtdC30LjQtNGD0LDQu9C40YLQtSwgZmFsc2UgcG9zdGl2ZXMg0LggZmFsc2UgbmVnYXRpdmVzINC30LAg0YHQtdC60L7RmNCwINC60LvQsNGB0LAsINC4INC/0YDQsNCy0LXRmNGc0Lgg0LPQviDRhdC4LdC60LLQsNC00YDQsNGCINGC0LXRgdGC0L7RgiDQt9CwINGC0L7Rh9C90LjRgtC1INC4INC/0YDQtdC00LLQuNC00LXQvdC40YLQtSDRhNGA0LXQutCy0LXQvdGG0LjQuCwg0LfQsNC60LvRg9GH0YPQstCw0LzQtSDQtNC10LrQsCDQvNC+0LTQtdC70L7RgiDQvdC1INCz0Lgg0YPRh9C4INC00L7QsdGA0L4g0L/QvtC00LDRgtC+0YbQuNGC0LUuDQoNCmBgYHtyfQ0KeV9wcmVkID0gYyhwcmVkaWN0KHR1bW9yX21vZGVsKSkNCnlfdHJ1ZSA9IGMob3JkZXJlZF90dW1vcl9kZiRyZXNwb25zZSkNCg0KY29ycmVjdF9mcmVxcyA9IGMoMCwgMCwgMCwgMCkNCnByZWRpY3RlZF9mcmVxcyA9IGMoMCwgMCwgMCwgMCkNCnJlc2lkdWFscyA9IGMoMCwgMCwgMCwgMCkNCmZwcyA9IGMoMCwgMCwgMCwgMCkNCmZucyA9IGMoMCwgMCwgMCwgMCkNCg0KZm9yIChpIGluIDE6bGVuZ3RoKHlfdHJ1ZSkpIHsNCiAgY29ycmVjdF9mcmVxc1t5X3RydWVbaV1dID0gY29ycmVjdF9mcmVxc1t5X3RydWVbaV1dICsgMQ0KICBwcmVkaWN0ZWRfZnJlcXNbeV9wcmVkW2ldXSA9IHByZWRpY3RlZF9mcmVxc1t5X3ByZWRbaV1dICsgMQ0KICBpZiAoeV9wcmVkW2ldICE9IHlfdHJ1ZVtpXSkgew0KICAgIHJlc2lkdWFsc1t5X3ByZWRbaV1dID0gcmVzaWR1YWxzW3lfcHJlZFtpXV0gKyAxDQogICAgcmVzaWR1YWxzW3lfdHJ1ZVtpXV0gPSByZXNpZHVhbHNbeV90cnVlW2ldXSArIDENCiAgICBmcHNbeV9wcmVkW2ldXSA9IGZwc1t5X3ByZWRbaV1dICsgMQ0KICAgIGZuc1t5X3RydWVbaV1dID0gZm5zW3lfdHJ1ZVtpXV0gKyAxDQogIH0NCn0NCg0KZGF0YS5mcmFtZShjb3JyZWN0X2ZyZXFzLCBwcmVkaWN0ZWRfZnJlcXMsIHJlc2lkdWFscywgZnBzLCBmbnMpDQpjaGlzcS50ZXN0KGNvcnJlY3RfZnJlcXMsIHByZWRpY3RlZF9mcmVxcykNCmBgYA0KDQojIyMgKGMpDQoNCtCe0LQg0LPQvtC70LXQvNCw0YLQsCBwLdCy0YDQtdC00L3QvtGB0YIg0YLQtdGB0YLQvtGCINC90LAg0JLQsNC70LQg0YHRg9Cz0LXRgNC40YDQsCDQtNC10LrQsCDQuNC80LAg0YDQsNC30LvQuNC60LAg0LLQviDRgNCw0LfQu9C40YfQvdC40YLQtSDRgtGA0LXRgtC80LDQvdC4Lg0KDQpgYGB7cn0NCmxpYnJhcnkobG10ZXN0KQ0KdHJlYXRtZW50X21vZGVsID0gcG9scihyZXNwb25zZSB+IHRyZWF0bWVudCwgZGF0YT1vcmRlcmVkX3NhdF9kZiwgSGVzcz1UUlVFKQ0Kd2FsZHRlc3QodHJlYXRtZW50X21vZGVsKQ0KYGBgDQoNCiMjIyAoZCkNCg0K0Jgg0YLQtdGB0YLQvtGCINC90LAgV2FsZCDQuCDQvtCx0LjRh9C90L7RgtC+IHN1bW1hcnkg0L3QsCDQvNC+0LTQtdC70L7RgiDQutCw0LTQtSDRiNGC0L4g0LzQvtC20LXQvNC1INC00LAg0LPQuCDRgdC/0L7RgNC10LTQuNC80LUgQUlDINCy0YDQtdC00L3QvtGB0YLQuNGC0LUg0YHRg9Cz0LXRgNC40YDQsNCw0YIg0LTQtdC60LAg0LzQvtC00LXQu9C+0YIg0LrQvtGYINGI0YLQviDQt9C10LzQsCDQv9GA0LXQtNCy0LjQtCB0cmVhdG1lbnQg0LUg0L/QvtC00L7QsdCw0YAg0Lgg0ZjQsCDQv9C+0LTQtNGA0LbRg9Cy0LDQsNGCINGC0LXQt9Cw0YLQsCDQtNC10LrQsCDQuNC80LAg0YDQsNC30LvQuNC60LAg0L/QvtC80LXRk9GDINGA0LDQt9C70LjRh9C90LjRgtC1INGC0YDQtdGC0LzQsNC90LguDQoNCmBgYHtyfQ0Kbm9fdHJlYXRtZW50ID0gcG9scihyZXNwb25zZSB+IHNleCwgZGF0YT1vcmRlcmVkX3NhdF9kZiwgSGVzcz1UUlVFKQ0Kc3VtbWFyeShub190cmVhdG1lbnQpDQp3YWxkdGVzdChub190cmVhdG1lbnQpDQoNCnllc190cmVhdG1lbnQgPSBwb2xyKA0KICByZXNwb25zZSB+IHNleCArIHRyZWF0bWVudCwgDQogIGRhdGE9b3JkZXJlZF9zYXRfZGYsIA0KICBIZXNzPVRSVUUNCikNCnN1bW1hcnkoeWVzX3RyZWF0bWVudCkNCndhbGR0ZXN0KHllc190cmVhdG1lbnQpDQpgYGANCg==