Packages

Loading packages:

library(lme4)
library(MASS)
library(stargazer)
library(dplyr)
library(ggplot2)
library(ggeffects)
library(nnet)

Data

Loading the data:

load('/Users/davidhamad/Documents/Cand.Scient.Pol/2. Semester/Statistical models beyond linear regression - applied statistics for political scientists/9-10) Categorical outcomes/kap6.rda')

Rename

Renaming for convenience:

df <- kap6 %>%
mutate(Education = Uddannelse,
Income = Indtaegt,
Female = Kvinde,
Burden = Belastning,
Immigrant = Innvandrer)

Exercise 1

Q1: Begin by choosing a reference category. Which one did you choose? Why? You can use the functions as.factor() and relevel().

A1: I chose Socialdemokraterne as the reference category because it is a large category with a familiar baseline.

table(df$Parti)

                      Andet            Dansk Folkeparti Det Konservative Folkeparti 
                         17                         143                          65 
       Det Radikale Venstre                Enhedslisten         Kristendemokraterne 
                        134                          77                           8 
           Liberal Alliance          Socialdemokraterne     Socialistisk Folkeparti 
                         48                         268                         108 
                    Venstre 
                        311 
df <- df %>%
  mutate(Parti = as.factor(Parti),
         Parti = relevel(Parti, ref = "Socialdemokraterne"))
table(df$Parti)

         Socialdemokraterne                       Andet            Dansk Folkeparti 
                        268                          17                         143 
Det Konservative Folkeparti        Det Radikale Venstre                Enhedslisten 
                         65                         134                          77 
        Kristendemokraterne            Liberal Alliance     Socialistisk Folkeparti 
                          8                          48                         108 
                    Venstre 
                        311 

Q2: Estimate a multinomial model where choice of party is a function of education and income.

A2: The coefficients on income support the redistribution hypothesis. The bourgeois parties (V, KF, LA) have positive coefficients, meaning that higher income increases the probability of voting for these parties rather than S. Education is also interesting. It seems that education is especially important when voting for RV (and also SF).

mod1.nom <- multinom(Parti ~ Education + Income, data = df)
# weights:  40 (27 variable)
initial  value 2516.725507 
iter  10 value 2297.906803
iter  20 value 2182.108328
iter  30 value 2111.031390
final  value 2110.013593 
converged
summary(mod1.nom)
Call:
multinom(formula = Parti ~ Education + Income, data = df)

Coefficients:
                            (Intercept)   Education      Income
Andet                       -3.47649926  0.02586823  0.05825070
Dansk Folkeparti            -0.02936041 -0.03946825 -0.02262418
Det Konservative Folkeparti -2.49096556  0.02823281  0.13025930
Det Radikale Venstre        -3.14790162  0.11733117  0.12734286
Enhedslisten                -1.51281720  0.02578023 -0.01401907
Kristendemokraterne         -3.00310057 -0.01311761 -0.05207996
Liberal Alliance            -3.09991119  0.03387547  0.14070260
Socialistisk Folkeparti     -1.82459370  0.07611650 -0.01328518
Venstre                     -1.06960446  0.02104517  0.15310841

Std. Errors:
                            (Intercept)  Education     Income
Andet                         0.8716862 0.05979221 0.09979420
Dansk Folkeparti              0.3071891 0.02289756 0.04053397
Det Konservative Folkeparti   0.4756282 0.03150253 0.05332830
Det Radikale Venstre          0.4264347 0.02693849 0.04309100
Enhedslisten                  0.4198162 0.02998338 0.05049790
Kristendemokraterne           1.0260423 0.07701172 0.13630737
Liberal Alliance              0.5904935 0.03844719 0.06496080
Socialistisk Folkeparti       0.3920117 0.02703611 0.04384018
Venstre                       0.2869829 0.01951165 0.03339975

Residual Deviance: 4220.027 
AIC: 4274.027 

Q3: The model effectively estimates the effect of income at equal levels of education. Which party has the voters with the highest/lowest revenue? In your opinion, does our knowledge of respondents’ income help us understand how voters discriminate between parties?

A3: Looking at the income coefficients (all relative to S), four parties have voters with significantly higher incomes than S voters: V (β = 0.15), LA (β = 0.14) KF (β = 0.13), and RV (β = 0.13). All four are statistically significant. The parties with the lowest-income voters are K, DF, Ø, and SF, all with small negative coefficients, But none of these are statistically significant, meaning we cannot distinguish their voters’ income from S voters in this data. Income therefore helps us discriminate the bourgeois bloc (V, KF, LA) from S, and it interestingly also separates RV from S.

coefs <- summary(mod1.nom)$coefficients
ses   <- summary(mod1.nom)$standard.errors

z <- coefs / ses
p <- (1 - pnorm(abs(z))) * 2

data.frame(
  coef = round(coefs[, "Income"], 3),
  se   = round(ses[, "Income"], 3),
  z    = round(z[, "Income"], 2),
  p    = round(p[, "Income"], 4),
  sig  = ifelse(p[, "Income"] < 0.001, "***",
         ifelse(p[, "Income"] < 0.01,  "**",
         ifelse(p[, "Income"] < 0.05,  "*",
         ifelse(p[, "Income"] < 0.1,   ".", ""))))
) |> 
  (\(x) x[order(x$coef), ])()

Q4: Interpret the marginal effects (by exponentiating the slope coefficients). Which parties are similar to your reference category with respect to the effect of income? For the party-pairs that are significantly different, what is the effect of income on the respondents’ choice of party?

A4: Exponentiating the income coefficients converts the log-odds into odds ratios, which show how a one-unit increase in income changes the odds of voting for a given party rather than for Socialdemokraterne. Five parties are not significantly different from S with respect to the effect of income: Kristendemokraterne, Dansk Folkeparti, Enhedslisten, Socialistisk Folkeparti, and Andet. Substantively, this means that the voters of these parties have income profiles similar to S voters, once education is controlled for. Three of these (V, LA, KF) are classic bourgeois parties, and the income effect fits the redistribution hypothesis. Higher-income voters prefer parties that favor less redistribution. The fourth, RV, is more surprising, but it attracts high-income voters. This is consistent with its appeal running through the educational-cultural dimension rather than the economic one.

or <- exp(coefs[, "Income"])
data.frame(
  coef       = round(coefs[, "Income"], 3),
  odds_ratio = round(or, 3),
  pct_change = round((or - 1) * 100, 1),
  p          = ifelse(p[, "Income"] < 0.0001, "<0.0001", 
                      format(round(p[, "Income"], 4), nsmall = 4)),
  sig        = ifelse(p[, "Income"] < 0.001, "***",
                ifelse(p[, "Income"] < 0.01,  "**",
                ifelse(p[, "Income"] < 0.05,  "*",
                ifelse(p[, "Income"] < 0.1,   ".", ""))))
) |> 
  (\(x) x[order(x$coef), ])()

Q5: Make predictions for a scenario where a respondent is rich (Income = 9) by filling in the equation for Dansk Folkeparti (DF), Venstre (V) and Socialistisk Folkeparti (SF). What do you find?

A5: For a rich respondent (Income = 9) with average education, the predicted probabilities for the three parties of interest are: Venstre 33.9%, Dansk Folkeparti 8.9%, and Socialistisk Folkeparti 7.3%. Venstre is clearly the most likely choice. V voters have the highest-income profile of any party. DF and SF are both well below Venstre, and interestingly they are fairly close to each other, despite being on opposite ends of the cultural spectrum.

scenario_rich <- data.frame(
  Education = mean(df$Education, na.rm = TRUE),
  Income = 9
)
preds_rich <- predict(mod1.nom, newdata = scenario_rich, type = "probs")
round(preds_rich, 3)
         Socialdemokraterne                       Andet            Dansk Folkeparti 
                      0.189                       0.014                       0.089 
Det Konservative Folkeparti        Det Radikale Venstre                Enhedslisten 
                      0.073                       0.119                       0.051 
        Kristendemokraterne            Liberal Alliance     Socialistisk Folkeparti 
                      0.005                       0.047                       0.073 
                    Venstre 
                      0.339 
preds_rich[c("Dansk Folkeparti", "Venstre", "Socialistisk Folkeparti")]
       Dansk Folkeparti                 Venstre Socialistisk Folkeparti 
             0.08938308              0.33904848              0.07327798 

Q6 + Q7: Reestimate your model with a new reference category? What happened? Based on your new model fit, make predictions for the same scenarios. What did you find? Why?

A6 + A7: I reestimated the model with Venstre as the reference category. The coefficients change. However, when I compute predictions for the same rich-voter scenario under both models and subtract them, all differences are zero. The reference category only affects how coefficients are presented, not what the model estimates.

df_v <- df %>%
  mutate(Parti = relevel(Parti, ref = "Venstre"))
mod2.nom <- multinom(Parti ~ Education + Income, data = df_v, trace = FALSE)
summary(mod2.nom)$coefficients
                            (Intercept)    Education      Income
Socialdemokraterne            1.0696836 -0.021043714 -0.15311519
Andet                        -2.4070773  0.004844270 -0.09487471
Dansk Folkeparti              1.0402804 -0.060511852 -0.17573784
Det Konservative Folkeparti  -1.4214332  0.007194775 -0.02285379
Det Radikale Venstre         -2.0781289  0.096283116 -0.02578075
Enhedslisten                 -0.4430975  0.004728908 -0.16713024
Kristendemokraterne          -1.9334603 -0.034279840 -0.20497515
Liberal Alliance             -2.0300525  0.012823998 -0.01243127
Socialistisk Folkeparti      -0.7549107  0.055072451 -0.16640355
preds_rich_s <- predict(mod1.nom, newdata = scenario_rich, type = "probs")
preds_rich_v <- predict(mod2.nom, newdata = scenario_rich, type = "probs")
round(preds_rich_s - preds_rich_v[names(preds_rich_s)], 4)
         Socialdemokraterne                       Andet            Dansk Folkeparti 
                          0                           0                           0 
Det Konservative Folkeparti        Det Radikale Venstre                Enhedslisten 
                          0                           0                           0 
        Kristendemokraterne            Liberal Alliance     Socialistisk Folkeparti 
                          0                           0                           0 
                    Venstre 
                          0 

Exercise 2

Q1: Calculate the regression coefficients in an intercept-only model by hand.

A1: The log-odds show the log-ratio of each party’s vote share relative to S. Venstre is the only party with more votes than S, so it has a positive value (+0.15). All other parties have fewer votes than S and therefore have negative values. S itself has a value of 0 because it is the reference category.

tab <- df %>%
  filter(!is.na(Parti)) %>%
  group_by(Parti) %>%
  reframe(n = n()) %>%
  mutate(
    N = sum(n),
    p = n / N,
    p_ref = p[Parti == "Socialdemokraterne"],
    odds_vs_S = p / p_ref,
    logodds = log(odds_vs_S)
  ) %>%
  arrange(logodds)
tab

Q2: Calculate the parameters of a multinomial model of party choice with a binary predictor by hand. The data contains a predictor that flags the respondent’s gender (Kvinde / Female).

A2: Among men, V is the only party with positive odds against S, so V is the most popular party for men relative to S. Women are more likely to vote SF and Ø, relative to S.

df0 <- df %>% filter(!is.na(Parti), Female == 0)
df1 <- df %>% filter(!is.na(Parti), Female == 1) 
calc_logodds <- function(d, ref = "Socialdemokraterne") {
  d %>%
    group_by(Parti) %>%
    reframe(n = n()) %>%
    mutate(
      p = n / sum(n),
      odds_vs_ref = p / p[Parti == ref],
      logodds = log(odds_vs_ref)
    )
}
men_tab    <- calc_logodds(df0)
women_tab  <- calc_logodds(df1)
combined <- men_tab %>%
  dplyr::select(Parti, logodds_men = logodds, odds_men = odds_vs_ref) %>%
  left_join(
    women_tab %>% dplyr::select(Parti, logodds_women = logodds, odds_women = odds_vs_ref),
    by = "Parti"
  ) %>%
  mutate(
    intercept_a = logodds_men,
    slope_b     = log(odds_women / odds_men)
  ) %>%
  filter(Parti != "Socialdemokraterne")
combined %>% dplyr::select(Parti, intercept_a, slope_b) %>% arrange(intercept_a)

Q3: How would you explain the slope coefficient to your neighbour/classmate?

A3: The slope tells you how the odds of voting for a party (rather than for Socialdemokraterne) change when we move from a man to a woman. A negative slope means women are less likely than men to vote for that party rather than S. A positive slope means women are more likely. To get a more intuitive number, we exponentiate the slope: exp(b) gives the odds-ratio. For example, Liberal Alliance has a slope of −1.61, so exp(−1.61) = 0.20, meaning women have only a fifth of the odds that men have of voting LA rather than S.


Exercise 3

Q1: Recode the variable in R into an ordinal variable.

A1: See the recoding below.

df <- df %>%
  mutate(Burden_rev = 10 - Burden,
         Burden_ord = ordered(Burden_rev, levels = 0:10))
class(df$Burden_ord)
[1] "ordered" "factor" 
levels(df$Burden_ord)
 [1] "0"  "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"
table(df$Burden_ord)

  0   1   2   3   4   5   6   7   8   9  10 
 23  36 161 193 148 389 124 161 116  52  72 

Q2: Fit an ordinal model where agreement to the statement is a function of the respondent’s income, education and immigrant background.

A2: The model shows three negative and statistically significant effects: higher income, higher education and immigrant background all decrease the probability of being in a higher Burden category; that is, they reduce the likelihood of viewing immigration as a burden. The 10 cutpoints separate the 11 ordered categories on the latent scale.

mod.ord <- polr(Burden_ord ~ Income + Education + Immigrant, Hess = T, df)
summary(mod.ord)
Call:
polr(formula = Burden_ord ~ Income + Education + Immigrant, data = df, 
    Hess = T)

Coefficients:
             Value Std. Error t value
Income    -0.06457    0.01773  -3.643
Education -0.08159    0.01060  -7.695
Immigrant -0.53010    0.15567  -3.405

Intercepts:
     Value    Std. Error t value 
0|1   -5.7690   0.2768   -20.8454
1|2   -4.7739   0.2167   -22.0302
2|3   -3.3085   0.1803   -18.3485
3|4   -2.4726   0.1699   -14.5523
4|5   -1.9814   0.1650   -12.0057
5|6   -0.8658   0.1570    -5.5146
6|7   -0.4622   0.1563    -2.9571
7|8    0.2229   0.1591     1.4011
8|9    0.9805   0.1706     5.7486
9|10   1.5980   0.1895     8.4351

Residual Deviance: 5526.293 
AIC: 5552.293 
(203 observations deleted due to missingness)

Q3: Present the results in stargazer.

A3: See the results below.

stargazer(mod.ord, 
          ord.intercepts = TRUE,
          type = "text")

========================================
                 Dependent variable:    
             ---------------------------
                     Burden_ord         
----------------------------------------
Income                -0.065***         
                       (0.018)          
                                        
Education             -0.082***         
                       (0.011)          
                                        
Immigrant             -0.530***         
                       (0.156)          
                                        
0| 1                  -5.769***         
                       (0.277)          
                                        
1| 2                  -4.774***         
                       (0.217)          
                                        
2| 3                  -3.308***         
                       (0.180)          
                                        
3| 4                  -2.473***         
                       (0.170)          
                                        
4| 5                  -1.981***         
                       (0.165)          
                                        
5| 6                  -0.866***         
                       (0.157)          
                                        
6| 7                  -0.462***         
                       (0.156)          
                                        
7| 8                    0.223           
                       (0.159)          
                                        
8| 9                  0.980***          
                       (0.171)          
                                        
9| 10                 1.598***          
                       (0.189)          
                                        
----------------------------------------
Observations            1,299           
========================================
Note:        *p<0.1; **p<0.05; ***p<0.01

Q4: What is the marginal effect of income? Can you make a sentence that states the finding?

A4: A one-decile increase in income lowers the odds of being in a higher Burden category by approximately 6.3% (OR = 0.937), holding education and immigrant background constant. Substantively, wealthier respondents are less likely to see immigration as a burden.

exp(coef(mod.ord)["Income"])
   Income 
0.9374682 

Q5: Calculate the probability that an observation is below the first – then the second – cutpoint in the model.

A5: For an average respondent (mean income, mean education, non-immigrant), the predicted probability of being in the lowest category (Burden_rev = 0, i.e. not viewing immigration as a burden at all) is approximately X%. The probability of being in either of the two lowest categories is approximately Y%.

b_inc <- coef(mod.ord)["Income"]
b_edu <- coef(mod.ord)["Education"]
b_imm <- coef(mod.ord)["Immigrant"]
tau1 <- mod.ord$zeta["0|1"]
tau2 <- mod.ord$zeta["1|2"]
xb <- b_inc * mean(df$Income, na.rm = TRUE) +
      b_edu * mean(df$Education, na.rm = TRUE) +
      b_imm * 0
xb
   Income 
-1.441242 
logodds_tau1 <- tau1 - xb
logodds_tau2 <- tau2 - xb
p_below_tau1 <- plogis(logodds_tau1)
p_below_tau2 <- plogis(logodds_tau2)
p_below_tau1
      0|1 
0.0130248 
p_below_tau2
       1|2 
0.03446845 

Q6: Using the same scenario, calculate the probability that an observation falls between cutpoints “0|1” and “1|2” by subtracting one cut point from the other.

A6: Subtracting the probability of being below cutpoint 0|1 from the probability of being below cutpoint 1|2 isolates the probability of being in category 1 specifically. For an average respondent, this gives 3.6%. So a typical Dane has approximately a 3.6% probability of placing themselves in category 1 on the Burden scale.

p_kat1 <- p_below_tau2 - p_below_tau1
p_kat1
       1|2 
0.02144365 

Q7: Check your results by having R calculate the predicted probabilities. Remember to keep your scenario constant.

A7: See the predicted probabilities below.

scenario <- data.frame(
  Income    = mean(df$Income, na.rm = TRUE),
  Education = mean(df$Education, na.rm = TRUE),
  Immigrant = 0
)
predict(mod.ord, newdata = scenario, type = "probs")
         0          1          2          3          4          5          6          7          8 
0.01302480 0.02144365 0.09939390 0.12895222 0.10534712 0.27186722 0.08688645 0.11387419 0.07767918 
         9         10 
0.03584824 0.04568303 

Q8: Calculate the cumulative sum of the probabilities.

A8: Calling cumsum() on the predicted probabilities gives the cumulative probability. So the first cumsum value (0.046) matches my hand-calculated. The difference is just perspective. Predict() shows the probability of each specific category, while cumsum() shows the probability of being in that category or any lower one.

predict(mod.ord, newdata = scenario, type = "probs") %>% cumsum()
         0          1          2          3          4          5          6          7          8 
0.01302480 0.03446845 0.13386235 0.26281457 0.36816169 0.64002891 0.72691536 0.84078955 0.91846873 
         9         10 
0.95431697 1.00000000 

Exercise 4

Q1: Create 10 binary variables (one for each of the cut points in the “Burden” variable), then run 10 binomial regressions.

A1: These 10 binary logits test the parallel regressions assumption. Income and Education are stable across cutpoints, so the assumption holds for them. Immigrant varies more and gets stronger at higher cutpoints, so the assumption is questionable for that variable.

binary_models <- list()
for (k in 0:9) {
  df$y_temp <- as.numeric(df$Burden_rev < (k + 1))
  binary_models[[paste0("<", k+1)]] <- glm(
    y_temp ~ Income + Education + Immigrant,
    data = df,
    family = "binomial"
  )
}
coef_table <- sapply(binary_models, function(m) coef(m)[c("Income", "Education", "Immigrant")])
round(coef_table, 3)
             <1    <2    <3    <4    <5    <6    <7    <8    <9   <10
Income    0.056 0.026 0.052 0.082 0.070 0.062 0.068 0.062 0.048 0.087
Education 0.038 0.068 0.100 0.088 0.098 0.082 0.077 0.063 0.064 0.065
Immigrant 0.895 0.651 0.735 0.642 0.623 0.418 0.439 0.278 0.177 0.697

Q2: Display the 10 binary models and the ordinal model from the previous exercise in a stargazer() results table. Are the regression coefficients very different?

A2: Income and Education coefficients in the binary models are close to those in the ordinal model, supporting parallel regressions. Immigrant varies much more (from 0.18 to 0.90) and the ordinal model’s value of 0.53 is essentially an average of these which is masking that the effect is much weaker at lower cutpoints and much stronger at upper cutpoints. The assumption is therefore questionable for immigrant.

stargazer(binary_models[1:5],
          type = "text",
          column.labels = paste0("<", 1:5),
          model.numbers = FALSE)

===================================================================
                                 Dependent variable:               
                  -------------------------------------------------
                                       y_temp                      
                     <1        <2        <3        <4        <5    
-------------------------------------------------------------------
Income              0.056     0.026    0.052*   0.082***  0.070*** 
                   (0.082)   (0.052)   (0.029)   (0.023)   (0.022) 
                                                                   
Education           0.038    0.068**  0.100***  0.088***  0.098*** 
                   (0.049)   (0.032)   (0.019)   (0.015)   (0.014) 
                                                                   
Immigrant          0.895*    0.651*   0.735***  0.642***  0.623*** 
                   (0.526)   (0.353)   (0.211)   (0.185)   (0.182) 
                                                                   
Constant          -5.135*** -4.345*** -3.542*** -2.699*** -2.261***
                   (0.787)   (0.507)   (0.301)   (0.232)   (0.211) 
                                                                   
-------------------------------------------------------------------
Observations        1,299     1,299     1,299     1,299     1,299  
Log Likelihood    -105.448  -222.875  -534.369  -741.870  -820.797 
Akaike Inf. Crit.  218.896   453.751  1,076.738 1,491.741 1,649.593
===================================================================
Note:                                   *p<0.1; **p<0.05; ***p<0.01
stargazer(binary_models[6:10],
          type = "text",
          column.labels = paste0("<", 6:10),
          model.numbers = FALSE)

=================================================================
                                Dependent variable:              
                  -----------------------------------------------
                                      y_temp                     
                     <6        <7        <8        <9      <10   
-----------------------------------------------------------------
Income            0.062***  0.068***   0.062**   0.048    0.087* 
                   (0.022)   (0.023)   (0.028)  (0.037)  (0.048) 
                                                                 
Education         0.082***  0.077***  0.063***  0.064*** 0.065** 
                   (0.013)   (0.014)   (0.016)  (0.021)  (0.027) 
                                                                 
Immigrant          0.418**   0.439**    0.278    0.177    0.697  
                   (0.197)   (0.217)   (0.256)  (0.334)  (0.528) 
                                                                 
Constant          -0.858*** -0.424**   0.473**  1.302*** 1.671***
                   (0.189)   (0.195)   (0.223)  (0.286)  (0.361) 
                                                                 
-----------------------------------------------------------------
Observations        1,299     1,299     1,299    1,299    1,299  
Log Likelihood    -806.452  -727.047  -556.568  -364.433 -238.816
Akaike Inf. Crit. 1,620.903 1,462.093 1,121.137 736.867  485.632 
=================================================================
Note:                                 *p<0.1; **p<0.05; ***p<0.01
stargazer(mod.ord,
          type = "text",
          ord.intercepts = TRUE)

========================================
                 Dependent variable:    
             ---------------------------
                     Burden_ord         
----------------------------------------
Income                -0.065***         
                       (0.018)          
                                        
Education             -0.082***         
                       (0.011)          
                                        
Immigrant             -0.530***         
                       (0.156)          
                                        
0| 1                  -5.769***         
                       (0.277)          
                                        
1| 2                  -4.774***         
                       (0.217)          
                                        
2| 3                  -3.308***         
                       (0.180)          
                                        
3| 4                  -2.473***         
                       (0.170)          
                                        
4| 5                  -1.981***         
                       (0.165)          
                                        
5| 6                  -0.866***         
                       (0.157)          
                                        
6| 7                  -0.462***         
                       (0.156)          
                                        
7| 8                    0.223           
                       (0.159)          
                                        
8| 9                  0.980***          
                       (0.171)          
                                        
9| 10                 1.598***          
                       (0.189)          
                                        
----------------------------------------
Observations            1,299           
========================================
Note:        *p<0.1; **p<0.05; ***p<0.01

Q3: Create a coefplot for each of the 10 coefficients on income. To do so, you will want to make a small data frame with one observation per model and variables reporting the slope coefficient, its standard error and the cutvalue associated with each model. What do you see?

A3: The coefficient on income is stable across the 10 cutpoints. Confidence intervals overlap substantially, especially in the middle of the scale where most data are concentrated. The wider intervals at cutpoints 1, 9 and 10 reflect the smaller number of observations in the tails of the Burden distribution, not a real change in the underlying effect. The plot supports the parallel regressions assumption for income: the effect is essentially constant across the Burden scale, justifying its use in the ordered logit model.

income_df <- data.frame(
  cutpoint = 1:10,
  estimate = sapply(binary_models, function(m) coef(m)["Income"]),
  se       = sapply(binary_models, function(m) summary(m)$coefficients["Income", "Std. Error"])
)
income_df$lower <- income_df$estimate - 1.96 * income_df$se
income_df$upper <- income_df$estimate + 1.96 * income_df$se
ggplot(income_df, aes(x = cutpoint, y = estimate)) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
  geom_errorbar(aes(ymin = lower, ymax = upper), width = 0.2) +
  geom_point(size = 3) +
  scale_x_continuous(breaks = 1:10) +
  labs(x = "Cutpoint",
       y = "Income coefficient",
       title = "Effect of income across cutpoints") +
  theme_minimal()


Q4: Create a similar coefplot for the intercepts/cutpoints of the model. There is no need to backtransform them.

A4: The intercepts increase monotonically. There are no reversals, confirming that the categories of Burden are genuinely ordered. Together, this supports the second assumption of the ordinal model: the categories are both ordered and separable.

intercept_df <- data.frame(
  cutpoint = 1:10,
  estimate = sapply(binary_models, function(m) coef(m)["(Intercept)"]),
  se       = sapply(binary_models, function(m) summary(m)$coefficients["(Intercept)", "Std. Error"])
)
intercept_df$lower <- intercept_df$estimate - 1.96 * intercept_df$se
intercept_df$upper <- intercept_df$estimate + 1.96 * intercept_df$se
ggplot(intercept_df, aes(x = cutpoint, y = estimate)) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
  geom_errorbar(aes(ymin = lower, ymax = upper), width = 0.2) +
  geom_point(size = 3) +
  scale_x_continuous(breaks = 1:10) +
  labs(x = "Cutpoint",
       y = "Intercept (log-odds)",
       title = "Intercepts across cutpoints") +
  theme_minimal()

LS0tCnRpdGxlOiAiTW9kZWxzIGZvciBjYXRlZ29yaWNhbCBkYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7Y3NzLCBlY2hvPUZBTFNFfQpib2R5e3RleHQtYWxpZ246IEp1c3RpZnk7fQpoMS50aXRsZSB7Ym9yZGVyLWJvdHRvbTogM3B4IHNvbGlkIGJsYWNrOyBwYWRkaW5nLWJvdHRvbTogMTBweDt9CmBgYApgYGB7ciwgZWNobz1GQUxTRX0Kc2V0d2QoJy9Vc2Vycy9kYXZpZGhhbWFkL0RvY3VtZW50cy9DYW5kLlNjaWVudC5Qb2wvMi4gU2VtZXN0ZXIvU3RhdGlzdGljYWwgbW9kZWxzIGJleW9uZCBsaW5lYXIgcmVncmVzc2lvbiAtIGFwcGxpZWQgc3RhdGlzdGljcyBmb3IgcG9saXRpY2FsIHNjaWVudGlzdHMvOS0xMCkgQ2F0ZWdvcmljYWwgb3V0Y29tZXMnKQpgYGAKCiMjIyBQYWNrYWdlcwpMb2FkaW5nIHBhY2thZ2VzOgoKYGBge3J9CmxpYnJhcnkobG1lNCkKbGlicmFyeShNQVNTKQpsaWJyYXJ5KHN0YXJnYXplcikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdnZWZmZWN0cykKbGlicmFyeShubmV0KQpgYGAKCi0tLQoKIyMjIERhdGEKTG9hZGluZyB0aGUgZGF0YToKCmBgYHtyfQpsb2FkKCcvVXNlcnMvZGF2aWRoYW1hZC9Eb2N1bWVudHMvQ2FuZC5TY2llbnQuUG9sLzIuIFNlbWVzdGVyL1N0YXRpc3RpY2FsIG1vZGVscyBiZXlvbmQgbGluZWFyIHJlZ3Jlc3Npb24gLSBhcHBsaWVkIHN0YXRpc3RpY3MgZm9yIHBvbGl0aWNhbCBzY2llbnRpc3RzLzktMTApIENhdGVnb3JpY2FsIG91dGNvbWVzL2thcDYucmRhJykKYGBgCgotLS0KCiMjIyBSZW5hbWUKUmVuYW1pbmcgZm9yIGNvbnZlbmllbmNlOgoKYGBge3J9CmRmIDwtIGthcDYgJT4lCm11dGF0ZShFZHVjYXRpb24gPSBVZGRhbm5lbHNlLApJbmNvbWUgPSBJbmR0YWVndCwKRmVtYWxlID0gS3ZpbmRlLApCdXJkZW4gPSBCZWxhc3RuaW5nLApJbW1pZ3JhbnQgPSBJbm52YW5kcmVyKQpgYGAKCi0tLQoKIyMjIEV4ZXJjaXNlIDEKCioqUTE6KiogQmVnaW4gYnkgY2hvb3NpbmcgYSByZWZlcmVuY2UgY2F0ZWdvcnkuIFdoaWNoIG9uZSBkaWQgeW91IGNob29zZT8gV2h5PyBZb3UgY2FuIHVzZSB0aGUgZnVuY3Rpb25zIGFzLmZhY3RvcigpIGFuZCByZWxldmVsKCkuCgoqKkExOioqIEkgY2hvc2UgU29jaWFsZGVtb2tyYXRlcm5lIGFzIHRoZSByZWZlcmVuY2UgY2F0ZWdvcnkgYmVjYXVzZSBpdCBpcyBhIGxhcmdlIGNhdGVnb3J5IHdpdGggYSBmYW1pbGlhciBiYXNlbGluZS4KCmBgYHtyfQp0YWJsZShkZiRQYXJ0aSkKYGBgCmBgYHtyfQpkZiA8LSBkZiAlPiUKICBtdXRhdGUoUGFydGkgPSBhcy5mYWN0b3IoUGFydGkpLAogICAgICAgICBQYXJ0aSA9IHJlbGV2ZWwoUGFydGksIHJlZiA9ICJTb2NpYWxkZW1va3JhdGVybmUiKSkKYGBgCgpgYGB7cn0KdGFibGUoZGYkUGFydGkpCmBgYAotLS0KCioqUTI6KiogRXN0aW1hdGUgYSBtdWx0aW5vbWlhbCBtb2RlbCB3aGVyZSBjaG9pY2Ugb2YgcGFydHkgaXMgYSBmdW5jdGlvbiBvZiBlZHVjYXRpb24gYW5kIGluY29tZS4KCioqQTI6KiogVGhlIGNvZWZmaWNpZW50cyBvbiBpbmNvbWUgc3VwcG9ydCB0aGUgcmVkaXN0cmlidXRpb24gaHlwb3RoZXNpcy4gVGhlIGJvdXJnZW9pcyBwYXJ0aWVzIChWLCBLRiwgTEEpIGhhdmUgcG9zaXRpdmUgY29lZmZpY2llbnRzLCBtZWFuaW5nIHRoYXQgaGlnaGVyIGluY29tZSBpbmNyZWFzZXMgdGhlIHByb2JhYmlsaXR5IG9mIHZvdGluZyBmb3IgdGhlc2UgcGFydGllcyByYXRoZXIgdGhhbiBTLiBFZHVjYXRpb24gaXMgYWxzbyBpbnRlcmVzdGluZy4gSXQgc2VlbXMgdGhhdCBlZHVjYXRpb24gaXMgZXNwZWNpYWxseSBpbXBvcnRhbnQgd2hlbiB2b3RpbmcgZm9yIFJWIChhbmQgYWxzbyBTRikuCgpgYGB7cn0KbW9kMS5ub20gPC0gbXVsdGlub20oUGFydGkgfiBFZHVjYXRpb24gKyBJbmNvbWUsIGRhdGEgPSBkZikKYGBgCgpgYGB7cn0Kc3VtbWFyeShtb2QxLm5vbSkKYGBgCgotLS0KCioqUTM6KiogVGhlIG1vZGVsIGVmZmVjdGl2ZWx5IGVzdGltYXRlcyB0aGUgZWZmZWN0IG9mIGluY29tZSBhdCBlcXVhbCBsZXZlbHMgb2YgZWR1Y2F0aW9uLiBXaGljaCBwYXJ0eSBoYXMgdGhlCnZvdGVycyB3aXRoIHRoZSBoaWdoZXN0L2xvd2VzdCByZXZlbnVlPyBJbiB5b3VyIG9waW5pb24sIGRvZXMgb3VyIGtub3dsZWRnZSBvZiByZXNwb25kZW50c+KAmSBpbmNvbWUKaGVscCB1cyB1bmRlcnN0YW5kIGhvdyB2b3RlcnMgZGlzY3JpbWluYXRlIGJldHdlZW4gcGFydGllcz8KCioqQTM6KiogTG9va2luZyBhdCB0aGUgaW5jb21lIGNvZWZmaWNpZW50cyAoYWxsIHJlbGF0aXZlIHRvIFMpLCBmb3VyIHBhcnRpZXMgaGF2ZSB2b3RlcnMgd2l0aCBzaWduaWZpY2FudGx5IGhpZ2hlciBpbmNvbWVzIHRoYW4gUyB2b3RlcnM6IFYgKM6yID0gMC4xNSksIExBICjOsiA9IDAuMTQpIEtGICjOsiA9IDAuMTMpLCBhbmQgUlYgKM6yID0gMC4xMykuIEFsbCBmb3VyIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBUaGUgcGFydGllcyB3aXRoIHRoZSBsb3dlc3QtaW5jb21lIHZvdGVycyBhcmUgSywgREYsIMOYLCBhbmQgU0YsIGFsbCB3aXRoIHNtYWxsIG5lZ2F0aXZlIGNvZWZmaWNpZW50cywgQnV0IG5vbmUgb2YgdGhlc2UgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsIG1lYW5pbmcgd2UgY2Fubm90IGRpc3Rpbmd1aXNoIHRoZWlyIHZvdGVycycgaW5jb21lIGZyb20gUyB2b3RlcnMgaW4gdGhpcyBkYXRhLiBJbmNvbWUgdGhlcmVmb3JlIGhlbHBzIHVzIGRpc2NyaW1pbmF0ZSB0aGUgYm91cmdlb2lzIGJsb2MgKFYsIEtGLCBMQSkgZnJvbSBTLCBhbmQgaXQgaW50ZXJlc3RpbmdseSBhbHNvIHNlcGFyYXRlcyBSViBmcm9tIFMuCgpgYGB7cn0KY29lZnMgPC0gc3VtbWFyeShtb2QxLm5vbSkkY29lZmZpY2llbnRzCnNlcyAgIDwtIHN1bW1hcnkobW9kMS5ub20pJHN0YW5kYXJkLmVycm9ycwoKeiA8LSBjb2VmcyAvIHNlcwpwIDwtICgxIC0gcG5vcm0oYWJzKHopKSkgKiAyCgpkYXRhLmZyYW1lKAogIGNvZWYgPSByb3VuZChjb2Vmc1ssICJJbmNvbWUiXSwgMyksCiAgc2UgICA9IHJvdW5kKHNlc1ssICJJbmNvbWUiXSwgMyksCiAgeiAgICA9IHJvdW5kKHpbLCAiSW5jb21lIl0sIDIpLAogIHAgICAgPSByb3VuZChwWywgIkluY29tZSJdLCA0KSwKICBzaWcgID0gaWZlbHNlKHBbLCAiSW5jb21lIl0gPCAwLjAwMSwgIioqKiIsCiAgICAgICAgIGlmZWxzZShwWywgIkluY29tZSJdIDwgMC4wMSwgICIqKiIsCiAgICAgICAgIGlmZWxzZShwWywgIkluY29tZSJdIDwgMC4wNSwgICIqIiwKICAgICAgICAgaWZlbHNlKHBbLCAiSW5jb21lIl0gPCAwLjEsICAgIi4iLCAiIikpKSkKKSB8PiAKICAoXCh4KSB4W29yZGVyKHgkY29lZiksIF0pKCkKYGBgCgotLS0KCioqUTQ6KiogSW50ZXJwcmV0IHRoZSBtYXJnaW5hbCBlZmZlY3RzIChieSBleHBvbmVudGlhdGluZyB0aGUgc2xvcGUgY29lZmZpY2llbnRzKS4gV2hpY2ggcGFydGllcyBhcmUgc2ltaWxhciB0byB5b3VyIHJlZmVyZW5jZSBjYXRlZ29yeSB3aXRoIHJlc3BlY3QgdG8gdGhlIGVmZmVjdCBvZiBpbmNvbWU/IEZvciB0aGUgcGFydHktcGFpcnMgdGhhdCBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQsIHdoYXQgaXMgdGhlIGVmZmVjdCBvZiBpbmNvbWUgb24gdGhlIHJlc3BvbmRlbnRz4oCZIGNob2ljZSBvZiBwYXJ0eT8KCioqQTQ6KiogRXhwb25lbnRpYXRpbmcgdGhlIGluY29tZSBjb2VmZmljaWVudHMgY29udmVydHMgdGhlIGxvZy1vZGRzIGludG8gb2RkcyByYXRpb3MsIHdoaWNoIHNob3cgaG93IGEgb25lLXVuaXQgaW5jcmVhc2UgaW4gaW5jb21lIGNoYW5nZXMgdGhlIG9kZHMgb2Ygdm90aW5nIGZvciBhIGdpdmVuIHBhcnR5IHJhdGhlciB0aGFuIGZvciBTb2NpYWxkZW1va3JhdGVybmUuIEZpdmUgcGFydGllcyBhcmUgbm90IHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gUyB3aXRoIHJlc3BlY3QgdG8gdGhlIGVmZmVjdCBvZiBpbmNvbWU6IEtyaXN0ZW5kZW1va3JhdGVybmUsIERhbnNrIEZvbGtlcGFydGksIEVuaGVkc2xpc3RlbiwgU29jaWFsaXN0aXNrIEZvbGtlcGFydGksIGFuZCBBbmRldC4gU3Vic3RhbnRpdmVseSwgdGhpcyBtZWFucyB0aGF0IHRoZSB2b3RlcnMgb2YgdGhlc2UgcGFydGllcyBoYXZlIGluY29tZSBwcm9maWxlcyBzaW1pbGFyIHRvIFMgdm90ZXJzLCBvbmNlIGVkdWNhdGlvbiBpcyBjb250cm9sbGVkIGZvci4gVGhyZWUgb2YgdGhlc2UgKFYsIExBLCBLRikgYXJlIGNsYXNzaWMgYm91cmdlb2lzIHBhcnRpZXMsIGFuZCB0aGUgaW5jb21lIGVmZmVjdCBmaXRzIHRoZSByZWRpc3RyaWJ1dGlvbiBoeXBvdGhlc2lzLiBIaWdoZXItaW5jb21lIHZvdGVycyBwcmVmZXIgcGFydGllcyB0aGF0IGZhdm9yIGxlc3MgcmVkaXN0cmlidXRpb24uIFRoZSBmb3VydGgsIFJWLCBpcyBtb3JlIHN1cnByaXNpbmcsIGJ1dCBpdCBhdHRyYWN0cyBoaWdoLWluY29tZSB2b3RlcnMuIFRoaXMgaXMgY29uc2lzdGVudCB3aXRoIGl0cyBhcHBlYWwgcnVubmluZyB0aHJvdWdoIHRoZSBlZHVjYXRpb25hbC1jdWx0dXJhbCBkaW1lbnNpb24gcmF0aGVyIHRoYW4gdGhlIGVjb25vbWljIG9uZS4KCmBgYHtyfQpvciA8LSBleHAoY29lZnNbLCAiSW5jb21lIl0pCmBgYAoKYGBge3J9CmRhdGEuZnJhbWUoCiAgY29lZiAgICAgICA9IHJvdW5kKGNvZWZzWywgIkluY29tZSJdLCAzKSwKICBvZGRzX3JhdGlvID0gcm91bmQob3IsIDMpLAogIHBjdF9jaGFuZ2UgPSByb3VuZCgob3IgLSAxKSAqIDEwMCwgMSksCiAgcCAgICAgICAgICA9IGlmZWxzZShwWywgIkluY29tZSJdIDwgMC4wMDAxLCAiPDAuMDAwMSIsIAogICAgICAgICAgICAgICAgICAgICAgZm9ybWF0KHJvdW5kKHBbLCAiSW5jb21lIl0sIDQpLCBuc21hbGwgPSA0KSksCiAgc2lnICAgICAgICA9IGlmZWxzZShwWywgIkluY29tZSJdIDwgMC4wMDEsICIqKioiLAogICAgICAgICAgICAgICAgaWZlbHNlKHBbLCAiSW5jb21lIl0gPCAwLjAxLCAgIioqIiwKICAgICAgICAgICAgICAgIGlmZWxzZShwWywgIkluY29tZSJdIDwgMC4wNSwgICIqIiwKICAgICAgICAgICAgICAgIGlmZWxzZShwWywgIkluY29tZSJdIDwgMC4xLCAgICIuIiwgIiIpKSkpCikgfD4gCiAgKFwoeCkgeFtvcmRlcih4JGNvZWYpLCBdKSgpCmBgYAoKLS0tCgoqKlE1OioqIE1ha2UgcHJlZGljdGlvbnMgZm9yIGEgc2NlbmFyaW8gd2hlcmUgYSByZXNwb25kZW50IGlzIHJpY2ggKEluY29tZSA9IDkpIGJ5IGZpbGxpbmcgaW4gdGhlIGVxdWF0aW9uIGZvcgpEYW5zayBGb2xrZXBhcnRpIChERiksIFZlbnN0cmUgKFYpIGFuZCBTb2NpYWxpc3Rpc2sgRm9sa2VwYXJ0aSAoU0YpLiBXaGF0IGRvIHlvdSBmaW5kPwoKKipBNToqKiBGb3IgYSByaWNoIHJlc3BvbmRlbnQgKEluY29tZSA9IDkpIHdpdGggYXZlcmFnZSBlZHVjYXRpb24sIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmb3IgdGhlIHRocmVlIHBhcnRpZXMgb2YgaW50ZXJlc3QgYXJlOiBWZW5zdHJlIDMzLjklLCBEYW5zayBGb2xrZXBhcnRpIDguOSUsIGFuZCBTb2NpYWxpc3Rpc2sgRm9sa2VwYXJ0aSA3LjMlLiBWZW5zdHJlIGlzIGNsZWFybHkgdGhlIG1vc3QgbGlrZWx5IGNob2ljZS4gViB2b3RlcnMgaGF2ZSB0aGUgaGlnaGVzdC1pbmNvbWUgcHJvZmlsZSBvZiBhbnkgcGFydHkuIERGIGFuZCBTRiBhcmUgYm90aCB3ZWxsIGJlbG93IFZlbnN0cmUsIGFuZCBpbnRlcmVzdGluZ2x5IHRoZXkgYXJlIGZhaXJseSBjbG9zZSB0byBlYWNoIG90aGVyLCBkZXNwaXRlIGJlaW5nIG9uIG9wcG9zaXRlIGVuZHMgb2YgdGhlIGN1bHR1cmFsIHNwZWN0cnVtLgoKYGBge3J9CnNjZW5hcmlvX3JpY2ggPC0gZGF0YS5mcmFtZSgKICBFZHVjYXRpb24gPSBtZWFuKGRmJEVkdWNhdGlvbiwgbmEucm0gPSBUUlVFKSwKICBJbmNvbWUgPSA5CikKYGBgCgpgYGB7cn0KcHJlZHNfcmljaCA8LSBwcmVkaWN0KG1vZDEubm9tLCBuZXdkYXRhID0gc2NlbmFyaW9fcmljaCwgdHlwZSA9ICJwcm9icyIpCnJvdW5kKHByZWRzX3JpY2gsIDMpCmBgYAoKYGBge3J9CnByZWRzX3JpY2hbYygiRGFuc2sgRm9sa2VwYXJ0aSIsICJWZW5zdHJlIiwgIlNvY2lhbGlzdGlzayBGb2xrZXBhcnRpIildCmBgYAoKLS0tCgoqKlE2ICsgUTc6KiogUmVlc3RpbWF0ZSB5b3VyIG1vZGVsIHdpdGggYSBuZXcgcmVmZXJlbmNlIGNhdGVnb3J5PyBXaGF0IGhhcHBlbmVkPyBCYXNlZCBvbiB5b3VyIG5ldyBtb2RlbCBmaXQsIG1ha2UgcHJlZGljdGlvbnMgZm9yIHRoZSBzYW1lIHNjZW5hcmlvcy4gV2hhdCBkaWQgeW91IGZpbmQ/IFdoeT8KCioqQTYgKyBBNzoqKiBJIHJlZXN0aW1hdGVkIHRoZSBtb2RlbCB3aXRoIFZlbnN0cmUgYXMgdGhlIHJlZmVyZW5jZSBjYXRlZ29yeS4gVGhlIGNvZWZmaWNpZW50cyBjaGFuZ2UuIEhvd2V2ZXIsIHdoZW4gSSBjb21wdXRlIHByZWRpY3Rpb25zIGZvciB0aGUgc2FtZSByaWNoLXZvdGVyIHNjZW5hcmlvIHVuZGVyIGJvdGggbW9kZWxzIGFuZCBzdWJ0cmFjdCB0aGVtLCBhbGwgZGlmZmVyZW5jZXMgYXJlIHplcm8uIFRoZSByZWZlcmVuY2UgY2F0ZWdvcnkgb25seSBhZmZlY3RzIGhvdyBjb2VmZmljaWVudHMgYXJlIHByZXNlbnRlZCwgbm90IHdoYXQgdGhlIG1vZGVsIGVzdGltYXRlcy4KCmBgYHtyfQpkZl92IDwtIGRmICU+JQogIG11dGF0ZShQYXJ0aSA9IHJlbGV2ZWwoUGFydGksIHJlZiA9ICJWZW5zdHJlIikpCmBgYAoKYGBge3J9Cm1vZDIubm9tIDwtIG11bHRpbm9tKFBhcnRpIH4gRWR1Y2F0aW9uICsgSW5jb21lLCBkYXRhID0gZGZfdiwgdHJhY2UgPSBGQUxTRSkKYGBgCgpgYGB7cn0Kc3VtbWFyeShtb2QyLm5vbSkkY29lZmZpY2llbnRzCmBgYApgYGB7cn0KcHJlZHNfcmljaF9zIDwtIHByZWRpY3QobW9kMS5ub20sIG5ld2RhdGEgPSBzY2VuYXJpb19yaWNoLCB0eXBlID0gInByb2JzIikKYGBgCgpgYGB7cn0KcHJlZHNfcmljaF92IDwtIHByZWRpY3QobW9kMi5ub20sIG5ld2RhdGEgPSBzY2VuYXJpb19yaWNoLCB0eXBlID0gInByb2JzIikKYGBgCgpgYGB7cn0Kcm91bmQocHJlZHNfcmljaF9zIC0gcHJlZHNfcmljaF92W25hbWVzKHByZWRzX3JpY2hfcyldLCA0KQpgYGAKCi0tLQoKIyMjIEV4ZXJjaXNlIDIKCioqUTE6KiogQ2FsY3VsYXRlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBpbiBhbiBpbnRlcmNlcHQtb25seSBtb2RlbCBieSBoYW5kLgoKKipBMToqKiBUaGUgbG9nLW9kZHMgc2hvdyB0aGUgbG9nLXJhdGlvIG9mIGVhY2ggcGFydHkncyB2b3RlIHNoYXJlIHJlbGF0aXZlIHRvIFMuIFZlbnN0cmUgaXMgdGhlIG9ubHkgcGFydHkgd2l0aCBtb3JlIHZvdGVzIHRoYW4gUywgc28gaXQgaGFzIGEgcG9zaXRpdmUgdmFsdWUgKCswLjE1KS4gQWxsIG90aGVyIHBhcnRpZXMgaGF2ZSBmZXdlciB2b3RlcyB0aGFuIFMgYW5kIHRoZXJlZm9yZSBoYXZlIG5lZ2F0aXZlIHZhbHVlcy4gUyBpdHNlbGYgaGFzIGEgdmFsdWUgb2YgMCBiZWNhdXNlIGl0IGlzIHRoZSByZWZlcmVuY2UgY2F0ZWdvcnkuCgpgYGB7cn0KdGFiIDwtIGRmICU+JQogIGZpbHRlcighaXMubmEoUGFydGkpKSAlPiUKICBncm91cF9ieShQYXJ0aSkgJT4lCiAgcmVmcmFtZShuID0gbigpKSAlPiUKICBtdXRhdGUoCiAgICBOID0gc3VtKG4pLAogICAgcCA9IG4gLyBOLAogICAgcF9yZWYgPSBwW1BhcnRpID09ICJTb2NpYWxkZW1va3JhdGVybmUiXSwKICAgIG9kZHNfdnNfUyA9IHAgLyBwX3JlZiwKICAgIGxvZ29kZHMgPSBsb2cob2Rkc192c19TKQogICkgJT4lCiAgYXJyYW5nZShsb2dvZGRzKQpgYGAKCmBgYHtyfQp0YWIKYGBgCgotLS0KCioqUTI6KiogQ2FsY3VsYXRlIHRoZSBwYXJhbWV0ZXJzIG9mIGEgbXVsdGlub21pYWwgbW9kZWwgb2YgcGFydHkgY2hvaWNlIHdpdGggYSBiaW5hcnkgcHJlZGljdG9yIGJ5IGhhbmQuIFRoZQpkYXRhIGNvbnRhaW5zIGEgcHJlZGljdG9yIHRoYXQgZmxhZ3MgdGhlIHJlc3BvbmRlbnTigJlzIGdlbmRlciAoS3ZpbmRlIC8gRmVtYWxlKS4KCioqQTI6KiogQW1vbmcgbWVuLCBWIGlzIHRoZSBvbmx5IHBhcnR5IHdpdGggcG9zaXRpdmUgb2RkcyBhZ2FpbnN0IFMsIHNvIFYgaXMgdGhlIG1vc3QgcG9wdWxhciBwYXJ0eSBmb3IgbWVuIHJlbGF0aXZlIHRvIFMuIFdvbWVuIGFyZSBtb3JlIGxpa2VseSB0byB2b3RlIFNGIGFuZCDDmCwgcmVsYXRpdmUgdG8gUy4KCmBgYHtyfQpkZjAgPC0gZGYgJT4lIGZpbHRlcighaXMubmEoUGFydGkpLCBGZW1hbGUgPT0gMCkKZGYxIDwtIGRmICU+JSBmaWx0ZXIoIWlzLm5hKFBhcnRpKSwgRmVtYWxlID09IDEpIApgYGAKCmBgYHtyfQpjYWxjX2xvZ29kZHMgPC0gZnVuY3Rpb24oZCwgcmVmID0gIlNvY2lhbGRlbW9rcmF0ZXJuZSIpIHsKICBkICU+JQogICAgZ3JvdXBfYnkoUGFydGkpICU+JQogICAgcmVmcmFtZShuID0gbigpKSAlPiUKICAgIG11dGF0ZSgKICAgICAgcCA9IG4gLyBzdW0obiksCiAgICAgIG9kZHNfdnNfcmVmID0gcCAvIHBbUGFydGkgPT0gcmVmXSwKICAgICAgbG9nb2RkcyA9IGxvZyhvZGRzX3ZzX3JlZikKICAgICkKfQpgYGAKCmBgYHtyfQptZW5fdGFiICAgIDwtIGNhbGNfbG9nb2RkcyhkZjApCndvbWVuX3RhYiAgPC0gY2FsY19sb2dvZGRzKGRmMSkKYGBgCgpgYGB7cn0KY29tYmluZWQgPC0gbWVuX3RhYiAlPiUKICBkcGx5cjo6c2VsZWN0KFBhcnRpLCBsb2dvZGRzX21lbiA9IGxvZ29kZHMsIG9kZHNfbWVuID0gb2Rkc192c19yZWYpICU+JQogIGxlZnRfam9pbigKICAgIHdvbWVuX3RhYiAlPiUgZHBseXI6OnNlbGVjdChQYXJ0aSwgbG9nb2Rkc193b21lbiA9IGxvZ29kZHMsIG9kZHNfd29tZW4gPSBvZGRzX3ZzX3JlZiksCiAgICBieSA9ICJQYXJ0aSIKICApICU+JQogIG11dGF0ZSgKICAgIGludGVyY2VwdF9hID0gbG9nb2Rkc19tZW4sCiAgICBzbG9wZV9iICAgICA9IGxvZyhvZGRzX3dvbWVuIC8gb2Rkc19tZW4pCiAgKSAlPiUKICBmaWx0ZXIoUGFydGkgIT0gIlNvY2lhbGRlbW9rcmF0ZXJuZSIpCmBgYAoKYGBge3J9CmNvbWJpbmVkICU+JSBkcGx5cjo6c2VsZWN0KFBhcnRpLCBpbnRlcmNlcHRfYSwgc2xvcGVfYikgJT4lIGFycmFuZ2UoaW50ZXJjZXB0X2EpCmBgYAoKLS0tCgoqKlEzOioqIEhvdyB3b3VsZCB5b3UgZXhwbGFpbiB0aGUgc2xvcGUgY29lZmZpY2llbnQgdG8geW91ciBuZWlnaGJvdXIvY2xhc3NtYXRlPwoKKipBMzoqKiBUaGUgc2xvcGUgdGVsbHMgeW91IGhvdyB0aGUgb2RkcyBvZiB2b3RpbmcgZm9yIGEgcGFydHkgKHJhdGhlciB0aGFuIGZvciBTb2NpYWxkZW1va3JhdGVybmUpIGNoYW5nZSB3aGVuIHdlIG1vdmUgZnJvbSBhIG1hbiB0byBhIHdvbWFuLiBBIG5lZ2F0aXZlIHNsb3BlIG1lYW5zIHdvbWVuIGFyZSBsZXNzIGxpa2VseSB0aGFuIG1lbiB0byB2b3RlIGZvciB0aGF0IHBhcnR5IHJhdGhlciB0aGFuIFMuIEEgcG9zaXRpdmUgc2xvcGUgbWVhbnMgd29tZW4gYXJlIG1vcmUgbGlrZWx5LiBUbyBnZXQgYSBtb3JlIGludHVpdGl2ZSBudW1iZXIsIHdlIGV4cG9uZW50aWF0ZSB0aGUgc2xvcGU6IGV4cChiKSBnaXZlcyB0aGUgb2Rkcy1yYXRpby4gRm9yIGV4YW1wbGUsIExpYmVyYWwgQWxsaWFuY2UgaGFzIGEgc2xvcGUgb2Yg4oiSMS42MSwgc28gZXhwKOKIkjEuNjEpID0gMC4yMCwgbWVhbmluZyB3b21lbiBoYXZlIG9ubHkgYSBmaWZ0aCBvZiB0aGUgb2RkcyB0aGF0IG1lbiBoYXZlIG9mIHZvdGluZyBMQSByYXRoZXIgdGhhbiBTLgoKLS0tCgojIyMgRXhlcmNpc2UgMwoKKipRMToqKiBSZWNvZGUgdGhlIHZhcmlhYmxlIGluIFIgaW50byBhbiBvcmRpbmFsIHZhcmlhYmxlLiAKCioqQTE6KiogU2VlIHRoZSByZWNvZGluZyBiZWxvdy4KCmBgYHtyfQpkZiA8LSBkZiAlPiUKICBtdXRhdGUoQnVyZGVuX3JldiA9IDEwIC0gQnVyZGVuLAogICAgICAgICBCdXJkZW5fb3JkID0gb3JkZXJlZChCdXJkZW5fcmV2LCBsZXZlbHMgPSAwOjEwKSkKYGBgCgpgYGB7cn0KY2xhc3MoZGYkQnVyZGVuX29yZCkKbGV2ZWxzKGRmJEJ1cmRlbl9vcmQpCnRhYmxlKGRmJEJ1cmRlbl9vcmQpCmBgYAoKLS0tCgoqKlEyOioqIEZpdCBhbiBvcmRpbmFsIG1vZGVsIHdoZXJlIGFncmVlbWVudCB0byB0aGUgc3RhdGVtZW50IGlzIGEgZnVuY3Rpb24gb2YgdGhlIHJlc3BvbmRlbnTigJlzIGluY29tZSwKZWR1Y2F0aW9uIGFuZCBpbW1pZ3JhbnQgYmFja2dyb3VuZC4KCioqQTI6KiogVGhlIG1vZGVsIHNob3dzIHRocmVlIG5lZ2F0aXZlIGFuZCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGVmZmVjdHM6IGhpZ2hlciBpbmNvbWUsIGhpZ2hlciBlZHVjYXRpb24gYW5kIGltbWlncmFudCBiYWNrZ3JvdW5kIGFsbCBkZWNyZWFzZSB0aGUgcHJvYmFiaWxpdHkgb2YgYmVpbmcgaW4gYSBoaWdoZXIgQnVyZGVuIGNhdGVnb3J5OyB0aGF0IGlzLCB0aGV5IHJlZHVjZSB0aGUgbGlrZWxpaG9vZCBvZiB2aWV3aW5nIGltbWlncmF0aW9uIGFzIGEgYnVyZGVuLiBUaGUgMTAgY3V0cG9pbnRzIHNlcGFyYXRlIHRoZSAxMSBvcmRlcmVkIGNhdGVnb3JpZXMgb24gdGhlIGxhdGVudCBzY2FsZS4KCgpgYGB7cn0KbW9kLm9yZCA8LSBwb2xyKEJ1cmRlbl9vcmQgfiBJbmNvbWUgKyBFZHVjYXRpb24gKyBJbW1pZ3JhbnQsIEhlc3MgPSBULCBkZikKYGBgCgpgYGB7cn0Kc3VtbWFyeShtb2Qub3JkKQpgYGAKCi0tLQoKKipRMzoqKiBQcmVzZW50IHRoZSByZXN1bHRzIGluIHN0YXJnYXplci4KCioqQTM6KiogU2VlIHRoZSByZXN1bHRzIGJlbG93LgoKYGBge3J9CnN0YXJnYXplcihtb2Qub3JkLCAKICAgICAgICAgIG9yZC5pbnRlcmNlcHRzID0gVFJVRSwKICAgICAgICAgIHR5cGUgPSAidGV4dCIpCmBgYAoKLS0tCgoqKlE0OioqIFdoYXQgaXMgdGhlIG1hcmdpbmFsIGVmZmVjdCBvZiBpbmNvbWU/IENhbiB5b3UgbWFrZSBhIHNlbnRlbmNlIHRoYXQgc3RhdGVzIHRoZSBmaW5kaW5nPwoKKipBNDoqKiBBIG9uZS1kZWNpbGUgaW5jcmVhc2UgaW4gaW5jb21lIGxvd2VycyB0aGUgb2RkcyBvZiBiZWluZyBpbiBhIGhpZ2hlciBCdXJkZW4gY2F0ZWdvcnkgYnkgYXBwcm94aW1hdGVseSA2LjMlIChPUiA9IDAuOTM3KSwgaG9sZGluZyBlZHVjYXRpb24gYW5kIGltbWlncmFudCBiYWNrZ3JvdW5kIGNvbnN0YW50LiBTdWJzdGFudGl2ZWx5LCB3ZWFsdGhpZXIgcmVzcG9uZGVudHMgYXJlIGxlc3MgbGlrZWx5IHRvIHNlZSBpbW1pZ3JhdGlvbiBhcyBhIGJ1cmRlbi4KCmBgYHtyfQpleHAoY29lZihtb2Qub3JkKVsiSW5jb21lIl0pCmBgYAoKLS0tCgoqKlE1OioqIENhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhbiBvYnNlcnZhdGlvbiBpcyBiZWxvdyB0aGUgZmlyc3Qg4oCTIHRoZW4gdGhlIHNlY29uZCDigJMgY3V0cG9pbnQgaW4gdGhlIG1vZGVsLgoKKipBNToqKiBGb3IgYW4gYXZlcmFnZSByZXNwb25kZW50IChtZWFuIGluY29tZSwgbWVhbiBlZHVjYXRpb24sIG5vbi1pbW1pZ3JhbnQpLCB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXR5IG9mIGJlaW5nIGluIHRoZSBsb3dlc3QgY2F0ZWdvcnkgKEJ1cmRlbl9yZXYgPSAwLCBpLmUuIG5vdCB2aWV3aW5nIGltbWlncmF0aW9uIGFzIGEgYnVyZGVuIGF0IGFsbCkgaXMgYXBwcm94aW1hdGVseSBYJS4gVGhlIHByb2JhYmlsaXR5IG9mIGJlaW5nIGluIGVpdGhlciBvZiB0aGUgdHdvIGxvd2VzdCBjYXRlZ29yaWVzIGlzIGFwcHJveGltYXRlbHkgWSUuCgpgYGB7cn0KYl9pbmMgPC0gY29lZihtb2Qub3JkKVsiSW5jb21lIl0KYl9lZHUgPC0gY29lZihtb2Qub3JkKVsiRWR1Y2F0aW9uIl0KYl9pbW0gPC0gY29lZihtb2Qub3JkKVsiSW1taWdyYW50Il0KYGBgCgpgYGB7cn0KdGF1MSA8LSBtb2Qub3JkJHpldGFbIjB8MSJdCnRhdTIgPC0gbW9kLm9yZCR6ZXRhWyIxfDIiXQpgYGAKCmBgYHtyfQp4YiA8LSBiX2luYyAqIG1lYW4oZGYkSW5jb21lLCBuYS5ybSA9IFRSVUUpICsKICAgICAgYl9lZHUgKiBtZWFuKGRmJEVkdWNhdGlvbiwgbmEucm0gPSBUUlVFKSArCiAgICAgIGJfaW1tICogMApgYGAKCmBgYHtyfQp4YgpgYGAKCmBgYHtyfQpsb2dvZGRzX3RhdTEgPC0gdGF1MSAtIHhiCmxvZ29kZHNfdGF1MiA8LSB0YXUyIC0geGIKYGBgCgpgYGB7cn0KcF9iZWxvd190YXUxIDwtIHBsb2dpcyhsb2dvZGRzX3RhdTEpCnBfYmVsb3dfdGF1MiA8LSBwbG9naXMobG9nb2Rkc190YXUyKQpgYGAKCmBgYHtyfQpwX2JlbG93X3RhdTEKcF9iZWxvd190YXUyCmBgYAoKLS0tCgoqKlE2OioqIFVzaW5nIHRoZSBzYW1lIHNjZW5hcmlvLCBjYWxjdWxhdGUgdGhlIHByb2JhYmlsaXR5IHRoYXQgYW4gb2JzZXJ2YXRpb24gZmFsbHMgYmV0d2VlbiBjdXRwb2ludHMg4oCcMHwx4oCdIGFuZArigJwxfDLigJ0gYnkgc3VidHJhY3Rpbmcgb25lIGN1dCBwb2ludCBmcm9tIHRoZSBvdGhlci4KCioqQTY6KiogU3VidHJhY3RpbmcgdGhlIHByb2JhYmlsaXR5IG9mIGJlaW5nIGJlbG93IGN1dHBvaW50IDB8MSBmcm9tIHRoZSBwcm9iYWJpbGl0eSBvZiBiZWluZyBiZWxvdyBjdXRwb2ludCAxfDIgaXNvbGF0ZXMgdGhlIHByb2JhYmlsaXR5IG9mIGJlaW5nIGluIGNhdGVnb3J5IDEgc3BlY2lmaWNhbGx5LiBGb3IgYW4gYXZlcmFnZSByZXNwb25kZW50LCB0aGlzIGdpdmVzIDMuNiUuIFNvIGEgdHlwaWNhbCBEYW5lIGhhcyBhcHByb3hpbWF0ZWx5IGEgMy42JSBwcm9iYWJpbGl0eSBvZiBwbGFjaW5nIHRoZW1zZWx2ZXMgaW4gY2F0ZWdvcnkgMSBvbiB0aGUgQnVyZGVuIHNjYWxlLgoKYGBge3J9CnBfa2F0MSA8LSBwX2JlbG93X3RhdTIgLSBwX2JlbG93X3RhdTEKYGBgCgpgYGB7cn0KcF9rYXQxCmBgYAoKLS0tCgoqKlE3OioqIENoZWNrIHlvdXIgcmVzdWx0cyBieSBoYXZpbmcgUiBjYWxjdWxhdGUgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzLiBSZW1lbWJlciB0byBrZWVwIHlvdXIgc2NlbmFyaW8KY29uc3RhbnQuCgoqKkE3OioqIFNlZSB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYmVsb3cuCgpgYGB7cn0Kc2NlbmFyaW8gPC0gZGF0YS5mcmFtZSgKICBJbmNvbWUgICAgPSBtZWFuKGRmJEluY29tZSwgbmEucm0gPSBUUlVFKSwKICBFZHVjYXRpb24gPSBtZWFuKGRmJEVkdWNhdGlvbiwgbmEucm0gPSBUUlVFKSwKICBJbW1pZ3JhbnQgPSAwCikKYGBgCgpgYGB7cn0KcHJlZGljdChtb2Qub3JkLCBuZXdkYXRhID0gc2NlbmFyaW8sIHR5cGUgPSAicHJvYnMiKQpgYGAKCi0tLQoKKipRODoqKiBDYWxjdWxhdGUgdGhlIGN1bXVsYXRpdmUgc3VtIG9mIHRoZSBwcm9iYWJpbGl0aWVzLgoKKipBODoqKiBDYWxsaW5nIGN1bXN1bSgpIG9uIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBnaXZlcyB0aGUgY3VtdWxhdGl2ZSBwcm9iYWJpbGl0eS4gU28gdGhlIGZpcnN0IGN1bXN1bSB2YWx1ZSAoMC4wNDYpIG1hdGNoZXMgbXkgaGFuZC1jYWxjdWxhdGVkLiBUaGUgZGlmZmVyZW5jZSBpcyBqdXN0IHBlcnNwZWN0aXZlLiBQcmVkaWN0KCkgc2hvd3MgdGhlIHByb2JhYmlsaXR5IG9mIGVhY2ggc3BlY2lmaWMgY2F0ZWdvcnksIHdoaWxlIGN1bXN1bSgpIHNob3dzIHRoZSBwcm9iYWJpbGl0eSBvZiBiZWluZyBpbiB0aGF0IGNhdGVnb3J5IG9yIGFueSBsb3dlciBvbmUuCgoKYGBge3J9CnByZWRpY3QobW9kLm9yZCwgbmV3ZGF0YSA9IHNjZW5hcmlvLCB0eXBlID0gInByb2JzIikgJT4lIGN1bXN1bSgpCmBgYAoKLS0tCgojIyMgRXhlcmNpc2UgNAoKKipRMToqKiBDcmVhdGUgMTAgYmluYXJ5IHZhcmlhYmxlcyAob25lIGZvciBlYWNoIG9mIHRoZSBjdXQgcG9pbnRzIGluIHRoZSDigJxCdXJkZW7igJ0gdmFyaWFibGUpLCB0aGVuIHJ1biAxMCBiaW5vbWlhbApyZWdyZXNzaW9ucy4KCioqQTE6KiogVGhlc2UgMTAgYmluYXJ5IGxvZ2l0cyB0ZXN0IHRoZSBwYXJhbGxlbCByZWdyZXNzaW9ucyBhc3N1bXB0aW9uLiBJbmNvbWUgYW5kIEVkdWNhdGlvbiBhcmUgc3RhYmxlIGFjcm9zcyBjdXRwb2ludHMsIHNvIHRoZSBhc3N1bXB0aW9uIGhvbGRzIGZvciB0aGVtLiBJbW1pZ3JhbnQgdmFyaWVzIG1vcmUgYW5kIGdldHMgc3Ryb25nZXIgYXQgaGlnaGVyIGN1dHBvaW50cywgc28gdGhlIGFzc3VtcHRpb24gaXMgcXVlc3Rpb25hYmxlIGZvciB0aGF0IHZhcmlhYmxlLgoKYGBge3J9CmJpbmFyeV9tb2RlbHMgPC0gbGlzdCgpCmBgYAoKYGBge3J9CmZvciAoayBpbiAwOjkpIHsKICBkZiR5X3RlbXAgPC0gYXMubnVtZXJpYyhkZiRCdXJkZW5fcmV2IDwgKGsgKyAxKSkKICBiaW5hcnlfbW9kZWxzW1twYXN0ZTAoIjwiLCBrKzEpXV0gPC0gZ2xtKAogICAgeV90ZW1wIH4gSW5jb21lICsgRWR1Y2F0aW9uICsgSW1taWdyYW50LAogICAgZGF0YSA9IGRmLAogICAgZmFtaWx5ID0gImJpbm9taWFsIgogICkKfQpgYGAKCmBgYHtyfQpjb2VmX3RhYmxlIDwtIHNhcHBseShiaW5hcnlfbW9kZWxzLCBmdW5jdGlvbihtKSBjb2VmKG0pW2MoIkluY29tZSIsICJFZHVjYXRpb24iLCAiSW1taWdyYW50IildKQpgYGAKCmBgYHtyfQpyb3VuZChjb2VmX3RhYmxlLCAzKQpgYGAKCi0tLQoKKipRMjoqKiBEaXNwbGF5IHRoZSAxMCBiaW5hcnkgbW9kZWxzIGFuZCB0aGUgb3JkaW5hbCBtb2RlbCBmcm9tIHRoZSBwcmV2aW91cyBleGVyY2lzZSBpbiBhIHN0YXJnYXplcigpIHJlc3VsdHMKdGFibGUuIEFyZSB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgdmVyeSBkaWZmZXJlbnQ/CgoqKkEyOioqIEluY29tZSBhbmQgRWR1Y2F0aW9uIGNvZWZmaWNpZW50cyBpbiB0aGUgYmluYXJ5IG1vZGVscyBhcmUgY2xvc2UgdG8gdGhvc2UgaW4gdGhlIG9yZGluYWwgbW9kZWwsIHN1cHBvcnRpbmcgcGFyYWxsZWwgcmVncmVzc2lvbnMuIEltbWlncmFudCB2YXJpZXMgbXVjaCBtb3JlIChmcm9tIDAuMTggdG8gMC45MCkgYW5kIHRoZSBvcmRpbmFsIG1vZGVsJ3MgdmFsdWUgb2YgMC41MyBpcyBlc3NlbnRpYWxseSBhbiBhdmVyYWdlIG9mIHRoZXNlIHdoaWNoIGlzIG1hc2tpbmcgdGhhdCB0aGUgZWZmZWN0IGlzIG11Y2ggd2Vha2VyIGF0IGxvd2VyIGN1dHBvaW50cyBhbmQgbXVjaCBzdHJvbmdlciBhdCB1cHBlciBjdXRwb2ludHMuIFRoZSBhc3N1bXB0aW9uIGlzIHRoZXJlZm9yZSBxdWVzdGlvbmFibGUgZm9yIGltbWlncmFudC4KCmBgYHtyfQpzdGFyZ2F6ZXIoYmluYXJ5X21vZGVsc1sxOjVdLAogICAgICAgICAgdHlwZSA9ICJ0ZXh0IiwKICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBwYXN0ZTAoIjwiLCAxOjUpLAogICAgICAgICAgbW9kZWwubnVtYmVycyA9IEZBTFNFKQpgYGAKCmBgYHtyfQpzdGFyZ2F6ZXIoYmluYXJ5X21vZGVsc1s2OjEwXSwKICAgICAgICAgIHR5cGUgPSAidGV4dCIsCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gcGFzdGUwKCI8IiwgNjoxMCksCiAgICAgICAgICBtb2RlbC5udW1iZXJzID0gRkFMU0UpCmBgYAoKYGBge3J9CnN0YXJnYXplcihtb2Qub3JkLAogICAgICAgICAgdHlwZSA9ICJ0ZXh0IiwKICAgICAgICAgIG9yZC5pbnRlcmNlcHRzID0gVFJVRSkKYGBgCgotLS0KCioqUTM6KiogQ3JlYXRlIGEgY29lZnBsb3QgZm9yIGVhY2ggb2YgdGhlIDEwIGNvZWZmaWNpZW50cyBvbiBpbmNvbWUuIFRvIGRvIHNvLCB5b3Ugd2lsbCB3YW50IHRvIG1ha2UgYSBzbWFsbCBkYXRhCmZyYW1lIHdpdGggb25lIG9ic2VydmF0aW9uIHBlciBtb2RlbCBhbmQgdmFyaWFibGVzIHJlcG9ydGluZyB0aGUgc2xvcGUgY29lZmZpY2llbnQsIGl0cyBzdGFuZGFyZCBlcnJvciBhbmQKdGhlIGN1dHZhbHVlIGFzc29jaWF0ZWQgd2l0aCBlYWNoIG1vZGVsLiBXaGF0IGRvIHlvdSBzZWU/CgoqKkEzOioqIFRoZSBjb2VmZmljaWVudCBvbiBpbmNvbWUgaXMgc3RhYmxlIGFjcm9zcyB0aGUgMTAgY3V0cG9pbnRzLiBDb25maWRlbmNlIGludGVydmFscyBvdmVybGFwIHN1YnN0YW50aWFsbHksIGVzcGVjaWFsbHkgaW4gdGhlIG1pZGRsZSBvZiB0aGUgc2NhbGUgd2hlcmUgbW9zdCBkYXRhIGFyZSBjb25jZW50cmF0ZWQuIFRoZSB3aWRlciBpbnRlcnZhbHMgYXQgY3V0cG9pbnRzIDEsIDkgYW5kIDEwIHJlZmxlY3QgdGhlIHNtYWxsZXIgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiB0aGUgdGFpbHMgb2YgdGhlIEJ1cmRlbiBkaXN0cmlidXRpb24sIG5vdCBhIHJlYWwgY2hhbmdlIGluIHRoZSB1bmRlcmx5aW5nIGVmZmVjdC4gVGhlIHBsb3Qgc3VwcG9ydHMgdGhlIHBhcmFsbGVsIHJlZ3Jlc3Npb25zIGFzc3VtcHRpb24gZm9yIGluY29tZTogdGhlIGVmZmVjdCBpcyBlc3NlbnRpYWxseSBjb25zdGFudCBhY3Jvc3MgdGhlIEJ1cmRlbiBzY2FsZSwganVzdGlmeWluZyBpdHMgdXNlIGluIHRoZSBvcmRlcmVkIGxvZ2l0IG1vZGVsLgoKYGBge3J9CmluY29tZV9kZiA8LSBkYXRhLmZyYW1lKAogIGN1dHBvaW50ID0gMToxMCwKICBlc3RpbWF0ZSA9IHNhcHBseShiaW5hcnlfbW9kZWxzLCBmdW5jdGlvbihtKSBjb2VmKG0pWyJJbmNvbWUiXSksCiAgc2UgICAgICAgPSBzYXBwbHkoYmluYXJ5X21vZGVscywgZnVuY3Rpb24obSkgc3VtbWFyeShtKSRjb2VmZmljaWVudHNbIkluY29tZSIsICJTdGQuIEVycm9yIl0pCikKYGBgCgpgYGB7cn0KaW5jb21lX2RmJGxvd2VyIDwtIGluY29tZV9kZiRlc3RpbWF0ZSAtIDEuOTYgKiBpbmNvbWVfZGYkc2UKaW5jb21lX2RmJHVwcGVyIDwtIGluY29tZV9kZiRlc3RpbWF0ZSArIDEuOTYgKiBpbmNvbWVfZGYkc2UKYGBgCgpgYGB7cn0KZ2dwbG90KGluY29tZV9kZiwgYWVzKHggPSBjdXRwb2ludCwgeSA9IGVzdGltYXRlKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXk1MCIpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbG93ZXIsIHltYXggPSB1cHBlciksIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjEwKSArCiAgbGFicyh4ID0gIkN1dHBvaW50IiwKICAgICAgIHkgPSAiSW5jb21lIGNvZWZmaWNpZW50IiwKICAgICAgIHRpdGxlID0gIkVmZmVjdCBvZiBpbmNvbWUgYWNyb3NzIGN1dHBvaW50cyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgotLS0KCioqUTQ6KiogQ3JlYXRlIGEgc2ltaWxhciBjb2VmcGxvdCBmb3IgdGhlIGludGVyY2VwdHMvY3V0cG9pbnRzIG9mIHRoZSBtb2RlbC4gVGhlcmUgaXMgbm8gbmVlZCB0byBiYWNrdHJhbnNmb3JtCnRoZW0uCgoqKkE0OioqIFRoZSBpbnRlcmNlcHRzIGluY3JlYXNlIG1vbm90b25pY2FsbHkuIFRoZXJlIGFyZSBubyByZXZlcnNhbHMsIGNvbmZpcm1pbmcgdGhhdCB0aGUgY2F0ZWdvcmllcyBvZiBCdXJkZW4gYXJlIGdlbnVpbmVseSBvcmRlcmVkLiBUb2dldGhlciwgdGhpcyBzdXBwb3J0cyB0aGUgc2Vjb25kIGFzc3VtcHRpb24gb2YgdGhlIG9yZGluYWwgbW9kZWw6IHRoZSBjYXRlZ29yaWVzIGFyZSBib3RoIG9yZGVyZWQgYW5kIHNlcGFyYWJsZS4KCmBgYHtyfQppbnRlcmNlcHRfZGYgPC0gZGF0YS5mcmFtZSgKICBjdXRwb2ludCA9IDE6MTAsCiAgZXN0aW1hdGUgPSBzYXBwbHkoYmluYXJ5X21vZGVscywgZnVuY3Rpb24obSkgY29lZihtKVsiKEludGVyY2VwdCkiXSksCiAgc2UgICAgICAgPSBzYXBwbHkoYmluYXJ5X21vZGVscywgZnVuY3Rpb24obSkgc3VtbWFyeShtKSRjb2VmZmljaWVudHNbIihJbnRlcmNlcHQpIiwgIlN0ZC4gRXJyb3IiXSkKKQpgYGAKCmBgYHtyfQppbnRlcmNlcHRfZGYkbG93ZXIgPC0gaW50ZXJjZXB0X2RmJGVzdGltYXRlIC0gMS45NiAqIGludGVyY2VwdF9kZiRzZQppbnRlcmNlcHRfZGYkdXBwZXIgPC0gaW50ZXJjZXB0X2RmJGVzdGltYXRlICsgMS45NiAqIGludGVyY2VwdF9kZiRzZQpgYGAKCmBgYHtyfQpnZ3Bsb3QoaW50ZXJjZXB0X2RmLCBhZXMoeCA9IGN1dHBvaW50LCB5ID0gZXN0aW1hdGUpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheTUwIikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBsb3dlciwgeW1heCA9IHVwcGVyKSwgd2lkdGggPSAwLjIpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6MTApICsKICBsYWJzKHggPSAiQ3V0cG9pbnQiLAogICAgICAgeSA9ICJJbnRlcmNlcHQgKGxvZy1vZGRzKSIsCiAgICAgICB0aXRsZSA9ICJJbnRlcmNlcHRzIGFjcm9zcyBjdXRwb2ludHMiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYA==