R script created by Junran Cao for independent study. July 2019.

Unless otherwise stated, all contents below are adapted from UCLA IDRE (see citation) as well as email correspondence with Siavash Jalal, statistical consultant at UCLA IDRE, on 9 July, 2019 pertaining to the interpretation of the estimated coefficients in the terms of probability.

Citation: FAQ: How do I interpret odds ratios in logistic regression? UCLA: Statistical Consulting Group. from https://stats.idre.ucla.edu/other/mult-pkg/faq/general/faq-how-do-i-interpret-odds-ratios-in-logistic-regression/ (accessed July 5, 2019)

I. Set up environment & load sample file

rm(list = ls())

library(tidyverse)

setwd("/Users/junran_cao/Desktop/Econometrics Method Papers")
dat <- read.csv("sample.csv", stringsAsFactors = FALSE)

II. Basics

The probability of an event or occurence happening is denoted as:

  p
  

The probability of that event not happening is therefore:

  1 - p
  

The odds is defined as the ratio of ‘success’ to ‘failure’ probabilities:

  p / (1 - p)
  

Suppose there are two such events (e.g. probabilities of going & not going to the movie for persons A and B), then the odds ratio is:

  [p / (1 - p)] / [q / (1 - q)]
  

And the log of odds or the odds ratio is called a logit:

  log[p / (1 - p)] 
    or,
  log[p / (1 - p)] / [q / (1 - q)]
  

In the context of logistic regression, the outcome variable by itself does not have a linear relationship with the predicator variables. Its logit transformation, however, does have a linear relationship with the predicator variables.

1. Probability to Odds

Example. Probability of success is 0.8 Then, the probability of failure is 1 - 0.8 = 0.2

Then, the odds of success is p(success) / p (failure) = 0.8 / 0.2 = 4 / 1 = 4 In words: “the odds of success are 4 to 1”

Transformation from probability to odds is a monotonic transformation.


p ranges from 0 to 1 odds range from 0 to +inf logit ranges from -inf to +inf ***

The logistic regression models the logit-transformed p as a linear relationship with the predicator variables:

  logit(p) := log(p / (1 - p)) = b0 + b1 * x1 + ... bk * xk

To convert back in terms of p:

  log(p / (1 - p)) = b0 + b1 * x1 + ... bk * xk
  p / (1 - p) = exp(b0 + b1 * x1 + ... bk * xk)
  (1 - p) / p = 1 / exp(b0 + b1 * x1 + ... bk * xk)
  1 / p = [1 + exp(b0 + b1 * x1 + ... bk * xk)] / exp(b0 + b1 * x1 + ... bk * xk)
  
  Therefore, p = exp(b0 + b1 * x1 + ... bk * xk) / [1 + exp(b0 + b1 * x1 + ... bk * xk)]
p <- seq(from = 0.001, to = 0.999, by = 0.005)
odds = p / (1 - p)

ggplot(data = dat, 
       aes(x = p, y = odds)) +
  geom_line()

2. Odds to Logit

This is also a monotonic transformation

logit = log(odds)

ggplot(data = dat, 
       aes(x = odds, y = logit)) +
  geom_line()

rm(list = ls.str(mode = 'numeric'))

Eq1 - no predicator variables

p := prob(hon == 1)

Equation 1 is:

  logit(p) = b0
  
eq1 <- glm(hon ~ 1,
           data = dat,
           family = "binomial")

summary(eq1)

Call:
glm(formula = hon ~ 1, family = "binomial", data = dat)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-0.7497  -0.7497  -0.7497  -0.7497   1.6772  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -1.1255     0.1644  -6.845 7.62e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 222.71  on 199  degrees of freedom
Residual deviance: 222.71  on 199  degrees of freedom
AIC: 224.71

Number of Fisher Scoring iterations: 4
paste("This means logit(p) equals", eq1$coefficients[1])
[1] "This means logit(p) equals -1.12545953870146"
paste("hence, p equals", exp(eq1$coefficients[1]) / (1 + exp(eq1$coefficients[1])), "which is the overall probability of being in honours class")
[1] "hence, p equals 0.245000000000525 which is the overall probability of being in honours class"

Frequency table for hon

dat %>% 
  group_by(hon) %>% 
  summarise(Freq. = n()) %>% 
  mutate(Percent = Freq. / nrow(.),
         Cum. = cumsum(100 * (Percent / sum(Percent)))) %>% 
  janitor::adorn_totals(where = "row") %>% 
  ungroup()
   hon Freq. Percent  Cum.
     0   151    75.5  75.5
     1    49    24.5 100.0
 Total   200   100.0 175.5

From frequency table

p = 49 / 200
odds = p / (1 - p)
logit = log(odds)

paste("p is", p)
[1] "p is 0.245"
paste("odds is", odds)
[1] "odds is 0.324503311258278"
paste("the logit is", logit)
[1] "the logit is -1.1254595387043"
paste("note the above logit equals to the intercept from eq1 which is", eq1$coefficients[1])
[1] "note the above logit equals to the intercept from eq1 which is -1.12545953870146"
rm(list = ls.str(mode = 'numeric'))

Eq2 - single dichotomous predicator variable

Equation 2 is:

  logit(p) = b0 + b1 * female
  
eq2 <- glm(hon ~ female,
           data = dat,
           family = "binomial")

summary(eq2)

Call:
glm(formula = hon ~ female, family = "binomial", data = dat)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-0.8337  -0.8337  -0.6431  -0.6431   1.8317  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -1.4709     0.2690  -5.469 4.53e-08 ***
female        0.5928     0.3414   1.736   0.0825 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 222.71  on 199  degrees of freedom
Residual deviance: 219.61  on 198  degrees of freedom
AIC: 223.61

Number of Fisher Scoring iterations: 4

Cross tabulation between hon and female

gmodels::CrossTable(dat$hon, dat$female,
                    prop.r = FALSE,
                    prop.c = FALSE,
                    prop.t = FALSE,
                    prop.chisq = FALSE)

 
   Cell Contents
|-------------------------|
|                       N |
|-------------------------|

 
Total Observations in Table:  200 

 
             | dat$female 
     dat$hon |         0 |         1 | Row Total | 
-------------|-----------|-----------|-----------|
           0 |        74 |        77 |       151 | 
-------------|-----------|-----------|-----------|
           1 |        17 |        32 |        49 | 
-------------|-----------|-----------|-----------|
Column Total |        91 |       109 |       200 | 
-------------|-----------|-----------|-----------|

 

Using above table

odds_m = (17 / 91) / (74 / 91)
odds_f = (32 / 109) / (77 / 109)
odds_ratio = odds_f / odds_m

paste("the odds of males in honours is", odds_m)
[1] "the odds of males in honours is 0.22972972972973"
paste("the odds of females in honours is", odds_f)
[1] "the odds of females in honours is 0.415584415584416"
paste("the odds ratio of female-to-male is therefore", odds_ratio)
[1] "the odds ratio of female-to-male is therefore 1.80901451489687"
paste("the interpretation of the female-to-male odds ratio is that the ODDS of getting into honours for females are", 100 * (odds_ratio - 1), "per cent higher than the ODDS for males")
[1] "the interpretation of the female-to-male odds ratio is that the ODDS of getting into honours for females are 80.9014514896868 per cent higher than the ODDS for males"

From regression output

paste("the intercept of", eq2$coefficients[1], "is the logit for male since male is the reference group (female == 0)", "This can be confirmed from the table at log(odds_m) equals", log(odds_m))
[1] "the intercept of -1.47085174914793 is the logit for male since male is the reference group (female == 0) This can be confirmed from the table at log(odds_m) equals -1.47085174914795"
paste("the coefficient for female", eq2$coefficients[2], "is the logit BETWEEN female == 1 & female == 0", "This can be confirmed from the table at log(odds_rato) equals", log(odds_ratio))
[1] "the coefficient for female 0.592782230095463 is the logit BETWEEN female == 1 & female == 0 This can be confirmed from the table at log(odds_rato) equals 0.592782230093996"
paste("note that we can get back to the female-to-male odds ratio from exp(female coefficient) at", exp(eq2$coefficients[2]), "which is the same as", odds_ratio)
[1] "note that we can get back to the female-to-male odds ratio from exp(female coefficient) at 1.80901451489952 which is the same as 1.80901451489687"

Question: What about converting all the way back to probability? That is, if all we know is the ODDS RATIO (between, say, female and male) can we get back to p and q, the probability of getting into honours for females and males respectively?

Response: (From Siavash Jalal’s email, 9 July 2019) Interpretation of the estimated coefficients in the terms of probability is not common in logistic regression.

We cannot simplify change in probability or percentage change in probability in terms of coefficients.

It is, however, possible to calculate the predicted probability for a given value of the predicator. That is, p = odds / (1 + odds) to get predicted probability.

Assume we have one predictor, then prob(y = 1 | x) = exp(b0 + b1 x) / [1 + exp(b0 + b1 x)].

We can also calculate the relative risk for given values of x1 and x2 as:

p1 / p2 = {exp(b0 + b1 x1) / [1 + exp(b0 + b1 x1)]} / {exp(b0 + b1 x2) / [1 + exp(b0 + b1 x2)]}

My notes:

Step 1: From the logistic regression output, we have the following estimated equation

  logit_honours = -1.4709 + 0.5928 * female; where, female = 0,1
  
logit_honours_0 <- coefficients(eq2)[1] + coef(eq2)[2] * 0
logit_honours_1 <- coefficients(eq2)[1] + coef(eq2)[2] * 1

paste("the logits of being in honours for males are", logit_honours_0)
[1] "the logits of being in honours for males are -1.47085174914793"
paste("the logits of being in honours for females are", logit_honours_1)
[1] "the logits of being in honours for females are -0.878069519052471"

Step 2: Convert to logits of being in honours to the probabilities of being in honours

prob_honours_0 <- exp(logit_honours_0) / (1 + exp(logit_honours_0))
prob_honours_1 <- exp(logit_honours_1) / (1 + exp(logit_honours_1))

paste("the probabilities of being in honours for males are", prob_honours_0)
[1] "the probabilities of being in honours for males are 0.18681318681319"
paste("the probabilities of being in honours for females are", prob_honours_1)
[1] "the probabilities of being in honours for females are 0.293577981651684"

Note that the following equation also works:

  prob = 0.5 * (1 + tanh(logit / 2))
  
prob_honours_0b <- 0.5 * (1 + tanh(logit_honours_0 / 2))
prob_honours_1b <- 0.5 * (1 + tanh(logit_honours_1 / 2))

paste("the probabilities of being in honours for males are", prob_honours_0b)
[1] "the probabilities of being in honours for males are 0.18681318681319"
paste("the probabilities of being in honours for females are", prob_honours_1b)
[1] "the probabilities of being in honours for females are 0.293577981651684"

Furthermore, R has an in-built function for this very purpose:

paste("the probabilities of being in honours for males are",
      predict(eq2, data.frame(female = 0), type = "response")
      )
[1] "the probabilities of being in honours for males are 0.18681318681319"
paste("the probabilities of being in honours for females are",
      predict(eq2, data.frame(female = 1), type = "response")
      )
[1] "the probabilities of being in honours for females are 0.293577981651684"

Step 3: For the marginal effects, let’s examine the effect on getting to honour class by changing a female student to a male student

In terms of logits

paste("the effect on honours in LOGIT from changing from female to male is", logit_honours_1 - logit_honours_0)
[1] "the effect on honours in LOGIT from changing from female to male is 0.592782230095463"
paste("note the difference - it's actually independent of 0 to 1 or 3 to 4 -  is exactly the estimated SLOPE COEFFICIENT", coefficients(eq2)[2])
[1] "note the difference - it's actually independent of 0 to 1 or 3 to 4 -  is exactly the estimated SLOPE COEFFICIENT 0.592782230095463"

i.e. A 1-unit change in x, changes the LOGIT by

  logit(.|x = 1) - logit(.|x = 0)
  = b0 + b1 * 1 - (b0 + b1 * 0)
  = b1

Now, in terms of probabilities, A 1-unit change in x, changes the PROB by (in this discrete case)

  prob(.| x = 1) - prob(. |x = 0)
  = [exp(b0 + b1 * 1) / (1 + exp(b0 + b1 * 1))] - [exp(b0 + b1 * 0) / (1 + exp(b0 + b1 * 0))]
  != b1 
  furthermore, != a constant 
  
  That is, delta(logit) = constant
  but, delta(prob) = f(x)
  
paste("the effect on honours in PROB from changing from female to male is", 
      (exp(logit_honours_1) / (1 + exp(logit_honours_1))) - (exp(logit_honours_0) / (1 + exp(logit_honours_0))))
[1] "the effect on honours in PROB from changing from female to male is 0.106764794838495"
rm(list = ls.str(mode = 'numeric'))

Eq3 - single continuous predicator variable

Equation 3 is:

  logit(p) = b0 + b1 * math
  
eq3 <- glm(hon ~ math,
           data = dat, 
           family = "binomial")

summary(eq3)

Call:
glm(formula = hon ~ math, family = "binomial", data = dat)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.0332  -0.6785  -0.3506  -0.1565   2.6143  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept) -9.79394    1.48174  -6.610 3.85e-11 ***
math         0.15634    0.02561   6.105 1.03e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 222.71  on 199  degrees of freedom
Residual deviance: 167.07  on 198  degrees of freedom
AIC: 171.07

Number of Fisher Scoring iterations: 5
paste("the interpretation of the intercept", eq3$coefficients[1], "is the logit of someone with a 0 in math score being in honours.", "Therefore, the odds of being in honours with math score 0 is", exp(eq3$coefficients[1]))
[1] "the interpretation of the intercept -9.79394211182874 is the logit of someone with a 0 in math score being in honours. Therefore, the odds of being in honours with math score 0 is 5.57885385598169e-05"
paste("To continue: the probability of being in honours with math score 0 is", exp(eq3$coefficients[1]) / (1  + exp(eq3$coefficients[1])))
[1] "To continue: the probability of being in honours with math score 0 is 5.57854263724066e-05"

At a math score of 55, the logistic regression fitted:

paste("logit(p | math == 55) = ", eq3$coefficients[1], "+", eq3$coefficients[2], "* 55")
[1] "logit(p | math == 55) =  -9.79394211182874 + 0.156340355582943 * 55"

Similarly at a math score of 54

paste("logit(p | math == 54) = ", eq3$coefficients[1], "+", eq3$coefficients[2], "* 54")
[1] "logit(p | math == 54) =  -9.79394211182874 + 0.156340355582943 * 54"

The difference from math == 54 to math == 55 is:

paste("logit(p | math == 55) - logit(p | math == 54) = ", (eq3$coefficients[1] + eq3$coefficients[2] * 55) - (eq3$coefficients[1] + eq3$coefficients[2] * 54), "which is the estimated slope coefficient for math")
[1] "logit(p | math == 55) - logit(p | math == 54) =  0.156340355582943 which is the estimated slope coefficient for math"
paste("In other words, b1 is the difference in logits due to a unit change in math score.")
[1] "In other words, b1 is the difference in logits due to a unit change in math score."
paste("For interpretation, we say a 1-unit increase in math leads to an expected change in the logit of", eq3$coefficients[2], "This is equivalent to an expected changes in the odds ratio of", exp(eq3$coefficients[2]), "That is, a 1-unit change in math increases the ODDS of being in honours by", 100 * (exp(eq3$coefficients[2]) - 1), "per cent. Note that this percentage is independent of math score")
[1] "For interpretation, we say a 1-unit increase in math leads to an expected change in the logit of 0.156340355582943 This is equivalent to an expected changes in the odds ratio of 1.1692240873208 That is, a 1-unit change in math increases the ODDS of being in honours by 16.9224087320799 per cent. Note that this percentage is independent of math score"

Question: And how does this 1-unit increase in math lead to expected change in the PROBABILITY of being in honours?

logit_honours_55 <- eq3$coefficients[1] + eq3$coefficients[2] * 55
logit_honours_54 <- eq3$coefficients[1] + eq3$coefficients[2] * 54

prob_honours_55 <- exp(logit_honours_55) / (1 + exp(logit_honours_55))
prob_honours_54 <- exp(logit_honours_54) / (1 + exp(logit_honours_54))

paste("the probabilities of being in honours for a student with a math score of 55 is", prob_honours_55)
[1] "the probabilities of being in honours for a student with a math score of 55 is 0.232326187508651"
paste("the probabilities of being in honours for a student with a math score of 54 is", prob_honours_54)
[1] "the probabilities of being in honours for a student with a math score of 54 is 0.205614972544512"

Double-check

paste("the probabilities of being in honours for a student with a math score of 55 is",
      predict(eq3, data.frame(math = 55), type = "response")
      )
[1] "the probabilities of being in honours for a student with a math score of 55 is 0.232326187508651"
paste("the probabilities of being in honours for a student with a math score of 54 is",
      predict(eq3, data.frame(math = 54), type = "response")
      )
[1] "the probabilities of being in honours for a student with a math score of 54 is 0.205614972544512"

Finally,

paste("the marginal probability of being in honours changes by", (exp(logit_honours_55) / (1 + exp(logit_honours_55))) - (exp(logit_honours_54) / (1 + exp(logit_honours_54))), "due to a change in math score from 54 to 55") 
[1] "the marginal probability of being in honours changes by 0.0267112149641393 due to a change in math score from 54 to 55"
      
paste("which is the same as", predict(eq3, data.frame(math = 55), type = "response") - predict(eq3, data.frame(math = 54), type = "response"))
[1] "which is the same as 0.0267112149641393"

Eq4 - multiple predicator variables and no interaction term

Equation 4 is:

logit(p) = b0 + b1 * math + b2 * female + b3 * read
eq4 <- glm(hon ~ math + female + read,
           data = dat,
           family = "binomial")

summary(eq4)

Call:
glm(formula = hon ~ math + female + read, family = "binomial", 
    data = dat)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.8305  -0.6327  -0.3300  -0.1258   2.3896  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept) -11.77025    1.71068  -6.880 5.97e-12 ***
math          0.12296    0.03128   3.931 8.44e-05 ***
female        0.97995    0.42163   2.324   0.0201 *  
read          0.05906    0.02655   2.224   0.0261 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 222.71  on 199  degrees of freedom
Residual deviance: 156.17  on 196  degrees of freedom
AIC: 164.17

Number of Fisher Scoring iterations: 5

Each estimated coefficient = expected change in the LOGIT of being in honours for a 1-unit increase in the corresponding predicator, holding all other predictors at constant values.

And each exp(estimated coefficient) = expected change in the ODDS RATIO of being in honours for a 1-unit increase in the corresponding predicator, holding all other predictors at constant values.

The expected changes in PROBABILITY must be calculated individually as it would change depending on the values of the predicators.

paste("Fixing values for math and read, the ODDS of being in honours for females over the ODDS of being in honours for males is", exp(eq4$coefficients[3]), "which means the ODDS for females are", 100 * (exp(eq4$coefficients[3]) - 1), "per cent higher than the ODDS for males")
[1] "Fixing values for math and read, the ODDS of being in honours for females over the ODDS of being in honours for males is 2.66431769455757 which means the ODDS for females are 166.431769455757 per cent higher than the ODDS for males"
paste("Fixing values for female and read, the ODDS of being in honours for a 1-unit increase in math is", exp(eq4$coefficients[2]), "which means the ODDS of being in honours increases by", 100 * (exp(eq4$coefficients[2]) - 1), "per cent due to a 1-unit increase in math")
[1] "Fixing values for female and read, the ODDS of being in honours for a 1-unit increase in math is 1.13083791729043 which means the ODDS of being in honours increases by 13.0837917290425 per cent due to a 1-unit increase in math"

Eq5 - multiple predicator variables with interaction term

Equation 5 is:

  logit(p) = b0 + b1 * female + b2 * math + b3 * female * math
  
eq5 <- glm(hon ~ female + math + female:math,
           data = dat,
           family = "binomial")

summary(eq5)

Call:
glm(formula = hon ~ female + math + female:math, family = "binomial", 
    data = dat)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.7623  -0.6725  -0.3421  -0.1450   2.6913  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept) -8.74584    2.12913  -4.108    4e-05 ***
female      -2.89986    3.09418  -0.937 0.348657    
math         0.12938    0.03588   3.606 0.000312 ***
female:math  0.06700    0.05346   1.253 0.210139    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 222.71  on 199  degrees of freedom
Residual deviance: 159.77  on 196  degrees of freedom
AIC: 167.77

Number of Fisher Scoring iterations: 5

In the presence of interaction term of female by math, we can no longer talk about the effect of female, holding all other variables at certain values. This is because it no longer makes sense to fix math and female x math at certain value and still allowing female to change from 0 to 1

The ‘equation’ for male (female == 0) is:

  logit(p) = b0 + b1 * female + b2 * math + b3 * female * math 
           = b0 + b2 * math

The ‘equation’ for female (female == 1) is:

  logit(p) = b0 + b1 * female + b2 * math + b3 * female * math 
           = (b0 + b1) + (b2 + b3) * math 
           
paste("for male, a 1-unit increase in math leads to a change in the logit of", eq5$coefficients[3])
[1] "for male, a 1-unit increase in math leads to a change in the logit of 0.129378054581833"
paste("this is a change in the odds ratio of", exp(eq5$coefficients[3]))
[1] "this is a change in the odds ratio of 1.13812031444246"
paste("for female, a 1-unit increase in math leads to a change in the logit of", eq5$coefficients[3] + eq5$coefficients[4])
[1] "for female, a 1-unit increase in math leads to a change in the logit of 0.196373186527463"
paste("this is a change in the odds ratio of", exp(eq5$coefficients[3] + eq5$coefficients[4]))
[1] "this is a change in the odds ratio of 1.21698098150797"
paste("the RATIO of the female-to-male odds ratio for math", exp(eq5$coefficients[3] + eq5$coefficients[4]) / exp(eq5$coefficients[3]), " = exp(coefficient(interaction term)) = ", exp(eq5$coefficients[4]))
[1] "the RATIO of the female-to-male odds ratio for math 1.06929027279875  = exp(coefficient(interaction term)) =  1.06929027279875"
LS0tCnRpdGxlOiAiVUNMQSBJbnN0aXR1dGUgZm9yIERpZ2l0YWwgUmVzZWFyY2ggJiBFZHVjYXRpb24ncyBGQVE6IEhvdyBkbyBJIGludGVycHJldCBvZGRzIHJhdGlvcyBpbiBsb2dpc3RpYyByZWdyZXNzaW9uPyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIFIgc2NyaXB0IGNyZWF0ZWQgYnkgSnVucmFuIENhbyBmb3IgaW5kZXBlbmRlbnQgc3R1ZHkuIEp1bHkgMjAxOS4KClVubGVzcyBvdGhlcndpc2Ugc3RhdGVkLCAqKmFsbCoqIGNvbnRlbnRzIGJlbG93IGFyZSBhZGFwdGVkIGZyb20gVUNMQSBJRFJFIChzZWUgY2l0YXRpb24pIGFzIHdlbGwgYXMgZW1haWwgY29ycmVzcG9uZGVuY2Ugd2l0aCBTaWF2YXNoIEphbGFsLCBzdGF0aXN0aWNhbCBjb25zdWx0YW50IGF0IFVDTEEgSURSRSwgb24gOSBKdWx5LCAyMDE5IHBlcnRhaW5pbmcgdG8gdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzIGluIHRoZSB0ZXJtcyBvZiBwcm9iYWJpbGl0eS4gCgoqKkNpdGF0aW9uOioqIApGQVE6IEhvdyBkbyBJIGludGVycHJldCBvZGRzIHJhdGlvcyBpbiBsb2dpc3RpYyByZWdyZXNzaW9uPyBVQ0xBOiBTdGF0aXN0aWNhbCBDb25zdWx0aW5nIEdyb3VwLgpmcm9tIGh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9vdGhlci9tdWx0LXBrZy9mYXEvZ2VuZXJhbC9mYXEtaG93LWRvLWktaW50ZXJwcmV0LW9kZHMtcmF0aW9zLWluLWxvZ2lzdGljLXJlZ3Jlc3Npb24vIChhY2Nlc3NlZCBKdWx5IDUsIDIwMTkpCgojIyMgSS4gU2V0IHVwIGVudmlyb25tZW50ICYgbG9hZCBzYW1wbGUgZmlsZQoKYGBge3J9CnJtKGxpc3QgPSBscygpKQoKbGlicmFyeSh0aWR5dmVyc2UpCgpzZXR3ZCgiL1VzZXJzL2p1bnJhbl9jYW8vRGVza3RvcC9FY29ub21ldHJpY3MgTWV0aG9kIFBhcGVycyIpCmRhdCA8LSByZWFkLmNzdigic2FtcGxlLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKYGBgCgojIyMgSUkuIEJhc2ljcwoKVGhlICoqcHJvYmFiaWxpdHkqKiBvZiBhbiBldmVudCBvciBvY2N1cmVuY2UgaGFwcGVuaW5nIGlzIGRlbm90ZWQgYXM6CgogICAgICBwCiAgICAgIApUaGUgcHJvYmFiaWxpdHkgb2YgdGhhdCBldmVudCBub3QgaGFwcGVuaW5nIGlzIHRoZXJlZm9yZToKCiAgICAgIDEgLSBwCiAgICAgIApUaGUgKipvZGRzKiogaXMgZGVmaW5lZCBhcyB0aGUgcmF0aW8gb2YgJ3N1Y2Nlc3MnIHRvICdmYWlsdXJlJyBwcm9iYWJpbGl0aWVzOgoKICAgICAgcCAvICgxIC0gcCkKICAgICAgClN1cHBvc2UgdGhlcmUgYXJlIHR3byBzdWNoIGV2ZW50cyAoZS5nLiBwcm9iYWJpbGl0aWVzIG9mIGdvaW5nICYgbm90IGdvaW5nIHRvIHRoZSBtb3ZpZSBmb3IgcGVyc29ucyBBIGFuZCBCKSwgdGhlbiB0aGUgKipvZGRzIHJhdGlvKiogaXM6CgogICAgICBbcCAvICgxIC0gcCldIC8gW3EgLyAoMSAtIHEpXQogICAgICAKQW5kIHRoZSBsb2cgb2Ygb2RkcyBvciB0aGUgb2RkcyByYXRpbyBpcyBjYWxsZWQgYSAqKmxvZ2l0Kio6CgogICAgICBsb2dbcCAvICgxIC0gcCldIAogICAgICAgIG9yLAogICAgICBsb2dbcCAvICgxIC0gcCldIC8gW3EgLyAoMSAtIHEpXQogICAgICAKSW4gdGhlIGNvbnRleHQgb2YgbG9naXN0aWMgcmVncmVzc2lvbiwgdGhlIG91dGNvbWUgdmFyaWFibGUgYnkgaXRzZWxmIGRvZXMgbm90IGhhdmUgYSBsaW5lYXIgcmVsYXRpb25zaGlwIHdpdGggdGhlIHByZWRpY2F0b3IgdmFyaWFibGVzLiBJdHMgbG9naXQgdHJhbnNmb3JtYXRpb24sIGhvd2V2ZXIsIGRvZXMgaGF2ZSBhIGxpbmVhciByZWxhdGlvbnNoaXAgd2l0aCB0aGUgcHJlZGljYXRvciB2YXJpYWJsZXMuCgojIyAxLiBQcm9iYWJpbGl0eSB0byBPZGRzCgpFeGFtcGxlLiBQcm9iYWJpbGl0eSBvZiBzdWNjZXNzIGlzIDAuOApUaGVuLCB0aGUgcHJvYmFiaWxpdHkgb2YgZmFpbHVyZSBpcyAxIC0gMC44ID0gMC4yCgpUaGVuLCB0aGUgb2RkcyBvZiBzdWNjZXNzIGlzIHAoc3VjY2VzcykgLyBwIChmYWlsdXJlKSA9IDAuOCAvIDAuMiA9IDQgLyAxID0gNApJbiB3b3JkczogInRoZSBvZGRzIG9mIHN1Y2Nlc3MgYXJlIDQgdG8gMSIKClRyYW5zZm9ybWF0aW9uIGZyb20gcHJvYmFiaWxpdHkgdG8gb2RkcyBpcyBhIG1vbm90b25pYyB0cmFuc2Zvcm1hdGlvbi4KCioqKgpwIHJhbmdlcyBmcm9tIDAgdG8gMQpvZGRzIHJhbmdlIGZyb20gMCB0byAraW5mCmxvZ2l0IHJhbmdlcyBmcm9tIC1pbmYgdG8gK2luZgoqKioKClRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyB0aGUgbG9naXQtdHJhbnNmb3JtZWQgcCBhcyBhIGxpbmVhciByZWxhdGlvbnNoaXAgd2l0aCB0aGUgcHJlZGljYXRvciB2YXJpYWJsZXM6CgogICAgICBsb2dpdChwKSA6PSBsb2cocCAvICgxIC0gcCkpID0gYjAgKyBiMSAqIHgxICsgLi4uIGJrICogeGsKClRvIGNvbnZlcnQgYmFjayBpbiB0ZXJtcyBvZiBwOgoKICAgICAgbG9nKHAgLyAoMSAtIHApKSA9IGIwICsgYjEgKiB4MSArIC4uLiBiayAqIHhrCiAgICAgIHAgLyAoMSAtIHApID0gZXhwKGIwICsgYjEgKiB4MSArIC4uLiBiayAqIHhrKQogICAgICAoMSAtIHApIC8gcCA9IDEgLyBleHAoYjAgKyBiMSAqIHgxICsgLi4uIGJrICogeGspCiAgICAgIDEgLyBwID0gWzEgKyBleHAoYjAgKyBiMSAqIHgxICsgLi4uIGJrICogeGspXSAvIGV4cChiMCArIGIxICogeDEgKyAuLi4gYmsgKiB4aykKICAgICAgCiAgICAgIFRoZXJlZm9yZSwgcCA9IGV4cChiMCArIGIxICogeDEgKyAuLi4gYmsgKiB4aykgLyBbMSArIGV4cChiMCArIGIxICogeDEgKyAuLi4gYmsgKiB4ayldCgpgYGB7cn0KcCA8LSBzZXEoZnJvbSA9IDAuMDAxLCB0byA9IDAuOTk5LCBieSA9IDAuMDA1KQpvZGRzID0gcCAvICgxIC0gcCkKCmdncGxvdChkYXRhID0gZGF0LCAKICAgICAgIGFlcyh4ID0gcCwgeSA9IG9kZHMpKSArCiAgZ2VvbV9saW5lKCkKYGBgCgojIyAyLiBPZGRzIHRvIExvZ2l0CgpUaGlzIGlzIGFsc28gYSBtb25vdG9uaWMgdHJhbnNmb3JtYXRpb24KCmBgYHtyfQpsb2dpdCA9IGxvZyhvZGRzKQoKZ2dwbG90KGRhdGEgPSBkYXQsIAogICAgICAgYWVzKHggPSBvZGRzLCB5ID0gbG9naXQpKSArCiAgZ2VvbV9saW5lKCkKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IGxzLnN0cihtb2RlID0gJ251bWVyaWMnKSkKYGBgCgojIEVxMSAtIG5vIHByZWRpY2F0b3IgdmFyaWFibGVzCgpwIDo9IHByb2IoaG9uID09IDEpCgpFcXVhdGlvbiAxIGlzOgoKICAgICAgbG9naXQocCkgPSBiMAogICAgICAKYGBge3J9CmVxMSA8LSBnbG0oaG9uIH4gMSwKICAgICAgICAgICBkYXRhID0gZGF0LAogICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIpCgpzdW1tYXJ5KGVxMSkKYGBgCgpgYGB7cn0KcGFzdGUoIlRoaXMgbWVhbnMgbG9naXQocCkgZXF1YWxzIiwgZXExJGNvZWZmaWNpZW50c1sxXSkKCnBhc3RlKCJoZW5jZSwgcCBlcXVhbHMiLCBleHAoZXExJGNvZWZmaWNpZW50c1sxXSkgLyAoMSArIGV4cChlcTEkY29lZmZpY2llbnRzWzFdKSksICJ3aGljaCBpcyB0aGUgb3ZlcmFsbCBwcm9iYWJpbGl0eSBvZiBiZWluZyBpbiBob25vdXJzIGNsYXNzIikKYGBgCgpGcmVxdWVuY3kgdGFibGUgZm9yIGBob25gCgpgYGB7cn0KZGF0ICU+JSAKICBncm91cF9ieShob24pICU+JSAKICBzdW1tYXJpc2UoRnJlcS4gPSBuKCkpICU+JSAKICBtdXRhdGUoUGVyY2VudCA9IEZyZXEuIC8gbnJvdyguKSwKICAgICAgICAgQ3VtLiA9IGN1bXN1bSgxMDAgKiAoUGVyY2VudCAvIHN1bShQZXJjZW50KSkpKSAlPiUgCiAgamFuaXRvcjo6YWRvcm5fdG90YWxzKHdoZXJlID0gInJvdyIpICU+JSAKICB1bmdyb3VwKCkKYGBgCgpGcm9tIGZyZXF1ZW5jeSB0YWJsZQoKYGBge3J9CnAgPSA0OSAvIDIwMApvZGRzID0gcCAvICgxIC0gcCkKbG9naXQgPSBsb2cob2RkcykKCnBhc3RlKCJwIGlzIiwgcCkKcGFzdGUoIm9kZHMgaXMiLCBvZGRzKQpwYXN0ZSgidGhlIGxvZ2l0IGlzIiwgbG9naXQpCgpwYXN0ZSgibm90ZSB0aGUgYWJvdmUgbG9naXQgZXF1YWxzIHRvIHRoZSBpbnRlcmNlcHQgZnJvbSBlcTEgd2hpY2ggaXMiLCBlcTEkY29lZmZpY2llbnRzWzFdKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMuc3RyKG1vZGUgPSAnbnVtZXJpYycpKQpgYGAKCiMgRXEyIC0gc2luZ2xlIGRpY2hvdG9tb3VzIHByZWRpY2F0b3IgdmFyaWFibGUKCkVxdWF0aW9uIDIgaXM6CgogICAgICBsb2dpdChwKSA9IGIwICsgYjEgKiBmZW1hbGUKICAgICAgCmBgYHtyfQplcTIgPC0gZ2xtKGhvbiB+IGZlbWFsZSwKICAgICAgICAgICBkYXRhID0gZGF0LAogICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIpCgpzdW1tYXJ5KGVxMikKYGBgCgpDcm9zcyB0YWJ1bGF0aW9uIGJldHdlZW4gYGhvbmAgYW5kIGBmZW1hbGVgCgpgYGB7cn0KZ21vZGVsczo6Q3Jvc3NUYWJsZShkYXQkaG9uLCBkYXQkZmVtYWxlLAogICAgICAgICAgICAgICAgICAgIHByb3AuciA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgIHByb3AuYyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgIHByb3AudCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgIHByb3AuY2hpc3EgPSBGQUxTRSkKYGBgCgpVc2luZyBhYm92ZSB0YWJsZSAKCmBgYHtyfQpvZGRzX20gPSAoMTcgLyA5MSkgLyAoNzQgLyA5MSkKb2Rkc19mID0gKDMyIC8gMTA5KSAvICg3NyAvIDEwOSkKb2Rkc19yYXRpbyA9IG9kZHNfZiAvIG9kZHNfbQoKcGFzdGUoInRoZSBvZGRzIG9mIG1hbGVzIGluIGhvbm91cnMgaXMiLCBvZGRzX20pCnBhc3RlKCJ0aGUgb2RkcyBvZiBmZW1hbGVzIGluIGhvbm91cnMgaXMiLCBvZGRzX2YpCgpwYXN0ZSgidGhlIG9kZHMgcmF0aW8gb2YgZmVtYWxlLXRvLW1hbGUgaXMgdGhlcmVmb3JlIiwgb2Rkc19yYXRpbykKcGFzdGUoInRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgZmVtYWxlLXRvLW1hbGUgb2RkcyByYXRpbyBpcyB0aGF0IHRoZSBPRERTIG9mIGdldHRpbmcgaW50byBob25vdXJzIGZvciBmZW1hbGVzIGFyZSIsIDEwMCAqIChvZGRzX3JhdGlvIC0gMSksICJwZXIgY2VudCBoaWdoZXIgdGhhbiB0aGUgT0REUyBmb3IgbWFsZXMiKQpgYGAKCkZyb20gcmVncmVzc2lvbiBvdXRwdXQKCmBgYHtyfQpwYXN0ZSgidGhlIGludGVyY2VwdCBvZiIsIGVxMiRjb2VmZmljaWVudHNbMV0sICJpcyB0aGUgbG9naXQgZm9yIG1hbGUgc2luY2UgbWFsZSBpcyB0aGUgcmVmZXJlbmNlIGdyb3VwIChmZW1hbGUgPT0gMCkiLCAiVGhpcyBjYW4gYmUgY29uZmlybWVkIGZyb20gdGhlIHRhYmxlIGF0IGxvZyhvZGRzX20pIGVxdWFscyIsIGxvZyhvZGRzX20pKQpgYGAKCmBgYHtyfQpwYXN0ZSgidGhlIGNvZWZmaWNpZW50IGZvciBmZW1hbGUiLCBlcTIkY29lZmZpY2llbnRzWzJdLCAiaXMgdGhlIGxvZ2l0IEJFVFdFRU4gZmVtYWxlID09IDEgJiBmZW1hbGUgPT0gMCIsICJUaGlzIGNhbiBiZSBjb25maXJtZWQgZnJvbSB0aGUgdGFibGUgYXQgbG9nKG9kZHNfcmF0bykgZXF1YWxzIiwgbG9nKG9kZHNfcmF0aW8pKQpgYGAKCmBgYHtyfQpwYXN0ZSgibm90ZSB0aGF0IHdlIGNhbiBnZXQgYmFjayB0byB0aGUgZmVtYWxlLXRvLW1hbGUgb2RkcyByYXRpbyBmcm9tIGV4cChmZW1hbGUgY29lZmZpY2llbnQpIGF0IiwgZXhwKGVxMiRjb2VmZmljaWVudHNbMl0pLCAid2hpY2ggaXMgdGhlIHNhbWUgYXMiLCBvZGRzX3JhdGlvKQpgYGAKCipRdWVzdGlvbjoqIFdoYXQgYWJvdXQgY29udmVydGluZyBhbGwgdGhlIHdheSBiYWNrIHRvIHByb2JhYmlsaXR5PyBUaGF0IGlzLCBpZiBhbGwgd2Uga25vdyBpcyB0aGUgT0REUyBSQVRJTyAoYmV0d2Vlbiwgc2F5LCBmZW1hbGUgYW5kIG1hbGUpIGNhbiB3ZSBnZXQgYmFjayB0byBwIGFuZCBxLCB0aGUgcHJvYmFiaWxpdHkgb2YgZ2V0dGluZyBpbnRvIGhvbm91cnMgZm9yIGZlbWFsZXMgYW5kIG1hbGVzIHJlc3BlY3RpdmVseT8KCipSZXNwb25zZToqICoqKEZyb20gU2lhdmFzaCBKYWxhbCdzIGVtYWlsLCA5IEp1bHkgMjAxOSkqKgpJbnRlcnByZXRhdGlvbiBvZiB0aGUgZXN0aW1hdGVkIGNvZWZmaWNpZW50cyBpbiB0aGUgdGVybXMgb2YgcHJvYmFiaWxpdHkgaXMgbm90IGNvbW1vbiBpbiBsb2dpc3RpYyByZWdyZXNzaW9uLiAKCldlIGNhbm5vdCBzaW1wbGlmeSBjaGFuZ2UgaW4gcHJvYmFiaWxpdHkgb3IgcGVyY2VudGFnZSBjaGFuZ2UgaW4gcHJvYmFiaWxpdHkgaW4gdGVybXMgb2YgY29lZmZpY2llbnRzLiAKCkl0IGlzLCBob3dldmVyLCBwb3NzaWJsZSB0byBjYWxjdWxhdGUgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBmb3IgYSBnaXZlbiB2YWx1ZSBvZiB0aGUgcHJlZGljYXRvci4KVGhhdCBpcywgcCA9IG9kZHMgLyAoMSArIG9kZHMpIHRvIGdldCBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkuIAoKQXNzdW1lIHdlIGhhdmUgb25lIHByZWRpY3RvciwgdGhlbiBwcm9iKHkgPSAxIHwgeCkgPSBleHAoYjAgKyBiMSB4KSAvIFsxICsgZXhwKGIwICsgYjEgeCldLiAKCldlIGNhbiBhbHNvIGNhbGN1bGF0ZSB0aGUgKipyZWxhdGl2ZSByaXNrKiogZm9yIGdpdmVuIHZhbHVlcyBvZiB4MSBhbmQgeDIgYXM6CgogICAgcDEgLyBwMiA9IHtleHAoYjAgKyBiMSB4MSkgLyBbMSArIGV4cChiMCArIGIxIHgxKV19IC8ge2V4cChiMCArIGIxIHgyKSAvIFsxICsgZXhwKGIwICsgYjEgeDIpXX0KCioqTXkgbm90ZXM6KioKClN0ZXAgMToKRnJvbSB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBvdXRwdXQsIHdlIGhhdmUgdGhlIGZvbGxvd2luZyBlc3RpbWF0ZWQgZXF1YXRpb24KCiAgICAgIGxvZ2l0X2hvbm91cnMgPSAtMS40NzA5ICsgMC41OTI4ICogZmVtYWxlOyB3aGVyZSwgZmVtYWxlID0gMCwxCiAgICAgIApgYGB7cn0KbG9naXRfaG9ub3Vyc18wIDwtIGNvZWZmaWNpZW50cyhlcTIpWzFdICsgY29lZihlcTIpWzJdICogMApsb2dpdF9ob25vdXJzXzEgPC0gY29lZmZpY2llbnRzKGVxMilbMV0gKyBjb2VmKGVxMilbMl0gKiAxCgpwYXN0ZSgidGhlIGxvZ2l0cyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBtYWxlcyBhcmUiLCBsb2dpdF9ob25vdXJzXzApCnBhc3RlKCJ0aGUgbG9naXRzIG9mIGJlaW5nIGluIGhvbm91cnMgZm9yIGZlbWFsZXMgYXJlIiwgbG9naXRfaG9ub3Vyc18xKQpgYGAKClN0ZXAgMjoKQ29udmVydCB0byBsb2dpdHMgb2YgYmVpbmcgaW4gaG9ub3VycyB0byB0aGUgcHJvYmFiaWxpdGllcyBvZiBiZWluZyBpbiBob25vdXJzCgpgYGB7cn0KcHJvYl9ob25vdXJzXzAgPC0gZXhwKGxvZ2l0X2hvbm91cnNfMCkgLyAoMSArIGV4cChsb2dpdF9ob25vdXJzXzApKQpwcm9iX2hvbm91cnNfMSA8LSBleHAobG9naXRfaG9ub3Vyc18xKSAvICgxICsgZXhwKGxvZ2l0X2hvbm91cnNfMSkpCgpwYXN0ZSgidGhlIHByb2JhYmlsaXRpZXMgb2YgYmVpbmcgaW4gaG9ub3VycyBmb3IgbWFsZXMgYXJlIiwgcHJvYl9ob25vdXJzXzApCnBhc3RlKCJ0aGUgcHJvYmFiaWxpdGllcyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBmZW1hbGVzIGFyZSIsIHByb2JfaG9ub3Vyc18xKQpgYGAKCk5vdGUgdGhhdCB0aGUgZm9sbG93aW5nIGVxdWF0aW9uIGFsc28gd29ya3M6CgogICAgICBwcm9iID0gMC41ICogKDEgKyB0YW5oKGxvZ2l0IC8gMikpCiAgICAgIApgYGB7cn0KcHJvYl9ob25vdXJzXzBiIDwtIDAuNSAqICgxICsgdGFuaChsb2dpdF9ob25vdXJzXzAgLyAyKSkKcHJvYl9ob25vdXJzXzFiIDwtIDAuNSAqICgxICsgdGFuaChsb2dpdF9ob25vdXJzXzEgLyAyKSkKCnBhc3RlKCJ0aGUgcHJvYmFiaWxpdGllcyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBtYWxlcyBhcmUiLCBwcm9iX2hvbm91cnNfMGIpCnBhc3RlKCJ0aGUgcHJvYmFiaWxpdGllcyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBmZW1hbGVzIGFyZSIsIHByb2JfaG9ub3Vyc18xYikKYGBgCgpGdXJ0aGVybW9yZSwgUiBoYXMgYW4gaW4tYnVpbHQgZnVuY3Rpb24gZm9yIHRoaXMgdmVyeSBwdXJwb3NlOgpgYGB7cn0KcGFzdGUoInRoZSBwcm9iYWJpbGl0aWVzIG9mIGJlaW5nIGluIGhvbm91cnMgZm9yIG1hbGVzIGFyZSIsCiAgICAgIHByZWRpY3QoZXEyLCBkYXRhLmZyYW1lKGZlbWFsZSA9IDApLCB0eXBlID0gInJlc3BvbnNlIikKICAgICAgKQoKcGFzdGUoInRoZSBwcm9iYWJpbGl0aWVzIG9mIGJlaW5nIGluIGhvbm91cnMgZm9yIGZlbWFsZXMgYXJlIiwKICAgICAgcHJlZGljdChlcTIsIGRhdGEuZnJhbWUoZmVtYWxlID0gMSksIHR5cGUgPSAicmVzcG9uc2UiKQogICAgICApCmBgYAoKU3RlcCAzOgpGb3IgdGhlIG1hcmdpbmFsIGVmZmVjdHMsIGxldCdzIGV4YW1pbmUgdGhlIGVmZmVjdCBvbiBnZXR0aW5nIHRvIGhvbm91ciBjbGFzcyBieSBjaGFuZ2luZyBhIGZlbWFsZSBzdHVkZW50IHRvIGEgbWFsZSBzdHVkZW50CgpJbiB0ZXJtcyBvZiBsb2dpdHMKYGBge3J9CnBhc3RlKCJ0aGUgZWZmZWN0IG9uIGhvbm91cnMgaW4gTE9HSVQgZnJvbSBjaGFuZ2luZyBmcm9tIGZlbWFsZSB0byBtYWxlIGlzIiwgbG9naXRfaG9ub3Vyc18xIC0gbG9naXRfaG9ub3Vyc18wKQoKcGFzdGUoIm5vdGUgdGhlIGRpZmZlcmVuY2UgLSBpdCdzIGFjdHVhbGx5IGluZGVwZW5kZW50IG9mIDAgdG8gMSBvciAzIHRvIDQgLSAgaXMgZXhhY3RseSB0aGUgZXN0aW1hdGVkIFNMT1BFIENPRUZGSUNJRU5UIiwgY29lZmZpY2llbnRzKGVxMilbMl0pCmBgYAoKaS5lLiBBIDEtdW5pdCBjaGFuZ2UgaW4geCwgY2hhbmdlcyB0aGUgTE9HSVQgYnkKCiAgICAgIGxvZ2l0KC58eCA9IDEpIC0gbG9naXQoLnx4ID0gMCkKICAgICAgPSBiMCArIGIxICogMSAtIChiMCArIGIxICogMCkKICAgICAgPSBiMQoKTm93LCBpbiB0ZXJtcyBvZiBwcm9iYWJpbGl0aWVzLCAKQSAxLXVuaXQgY2hhbmdlIGluIHgsIGNoYW5nZXMgdGhlIFBST0IgYnkgKGluIHRoaXMgZGlzY3JldGUgY2FzZSkKCiAgICAgIHByb2IoLnwgeCA9IDEpIC0gcHJvYiguIHx4ID0gMCkKICAgICAgPSBbZXhwKGIwICsgYjEgKiAxKSAvICgxICsgZXhwKGIwICsgYjEgKiAxKSldIC0gW2V4cChiMCArIGIxICogMCkgLyAoMSArIGV4cChiMCArIGIxICogMCkpXQogICAgICAhPSBiMSAKICAgICAgZnVydGhlcm1vcmUsICE9IGEgY29uc3RhbnQgCiAgICAgIAogICAgICBUaGF0IGlzLCBkZWx0YShsb2dpdCkgPSBjb25zdGFudAogICAgICBidXQsIGRlbHRhKHByb2IpID0gZih4KQogICAgICAKYGBge3J9CnBhc3RlKCJ0aGUgZWZmZWN0IG9uIGhvbm91cnMgaW4gUFJPQiBmcm9tIGNoYW5naW5nIGZyb20gZmVtYWxlIHRvIG1hbGUgaXMiLCAKICAgICAgKGV4cChsb2dpdF9ob25vdXJzXzEpIC8gKDEgKyBleHAobG9naXRfaG9ub3Vyc18xKSkpIC0gKGV4cChsb2dpdF9ob25vdXJzXzApIC8gKDEgKyBleHAobG9naXRfaG9ub3Vyc18wKSkpKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gbHMuc3RyKG1vZGUgPSAnbnVtZXJpYycpKQpgYGAKCiMgRXEzIC0gc2luZ2xlIGNvbnRpbnVvdXMgcHJlZGljYXRvciB2YXJpYWJsZQoKRXF1YXRpb24gMyBpczoKCiAgICAgIGxvZ2l0KHApID0gYjAgKyBiMSAqIG1hdGgKICAgICAgCmBgYHtyfQplcTMgPC0gZ2xtKGhvbiB+IG1hdGgsCiAgICAgICAgICAgZGF0YSA9IGRhdCwgCiAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIikKCnN1bW1hcnkoZXEzKQpgYGAKCmBgYHtyfQpwYXN0ZSgidGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBpbnRlcmNlcHQiLCBlcTMkY29lZmZpY2llbnRzWzFdLCAiaXMgdGhlIGxvZ2l0IG9mIHNvbWVvbmUgd2l0aCBhIDAgaW4gbWF0aCBzY29yZSBiZWluZyBpbiBob25vdXJzLiIsICJUaGVyZWZvcmUsIHRoZSBvZGRzIG9mIGJlaW5nIGluIGhvbm91cnMgd2l0aCBtYXRoIHNjb3JlIDAgaXMiLCBleHAoZXEzJGNvZWZmaWNpZW50c1sxXSkpCgpwYXN0ZSgiVG8gY29udGludWU6IHRoZSBwcm9iYWJpbGl0eSBvZiBiZWluZyBpbiBob25vdXJzIHdpdGggbWF0aCBzY29yZSAwIGlzIiwgZXhwKGVxMyRjb2VmZmljaWVudHNbMV0pIC8gKDEgICsgZXhwKGVxMyRjb2VmZmljaWVudHNbMV0pKSkKYGBgCgpBdCBhIG1hdGggc2NvcmUgb2YgNTUsIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIGZpdHRlZDoKCmBgYHtyfQpwYXN0ZSgibG9naXQocCB8IG1hdGggPT0gNTUpID0gIiwgZXEzJGNvZWZmaWNpZW50c1sxXSwgIisiLCBlcTMkY29lZmZpY2llbnRzWzJdLCAiKiA1NSIpCmBgYAoKU2ltaWxhcmx5IGF0IGEgbWF0aCBzY29yZSBvZiA1NAoKYGBge3J9CnBhc3RlKCJsb2dpdChwIHwgbWF0aCA9PSA1NCkgPSAiLCBlcTMkY29lZmZpY2llbnRzWzFdLCAiKyIsIGVxMyRjb2VmZmljaWVudHNbMl0sICIqIDU0IikKYGBgCgpUaGUgZGlmZmVyZW5jZSBmcm9tIG1hdGggPT0gNTQgdG8gbWF0aCA9PSA1NSBpczoKCmBgYHtyfQpwYXN0ZSgibG9naXQocCB8IG1hdGggPT0gNTUpIC0gbG9naXQocCB8IG1hdGggPT0gNTQpID0gIiwgKGVxMyRjb2VmZmljaWVudHNbMV0gKyBlcTMkY29lZmZpY2llbnRzWzJdICogNTUpIC0gKGVxMyRjb2VmZmljaWVudHNbMV0gKyBlcTMkY29lZmZpY2llbnRzWzJdICogNTQpLCAid2hpY2ggaXMgdGhlIGVzdGltYXRlZCBzbG9wZSBjb2VmZmljaWVudCBmb3IgbWF0aCIpCgpwYXN0ZSgiSW4gb3RoZXIgd29yZHMsIGIxIGlzIHRoZSBkaWZmZXJlbmNlIGluIGxvZ2l0cyBkdWUgdG8gYSB1bml0IGNoYW5nZSBpbiBtYXRoIHNjb3JlLiIpCgpwYXN0ZSgiRm9yIGludGVycHJldGF0aW9uLCB3ZSBzYXkgYSAxLXVuaXQgaW5jcmVhc2UgaW4gbWF0aCBsZWFkcyB0byBhbiBleHBlY3RlZCBjaGFuZ2UgaW4gdGhlIGxvZ2l0IG9mIiwgZXEzJGNvZWZmaWNpZW50c1syXSwgIlRoaXMgaXMgZXF1aXZhbGVudCB0byBhbiBleHBlY3RlZCBjaGFuZ2VzIGluIHRoZSBvZGRzIHJhdGlvIG9mIiwgZXhwKGVxMyRjb2VmZmljaWVudHNbMl0pLCAiVGhhdCBpcywgYSAxLXVuaXQgY2hhbmdlIGluIG1hdGggaW5jcmVhc2VzIHRoZSBPRERTIG9mIGJlaW5nIGluIGhvbm91cnMgYnkiLCAxMDAgKiAoZXhwKGVxMyRjb2VmZmljaWVudHNbMl0pIC0gMSksICJwZXIgY2VudC4gTm90ZSB0aGF0IHRoaXMgcGVyY2VudGFnZSBpcyBpbmRlcGVuZGVudCBvZiBtYXRoIHNjb3JlIikKYGBgCgoqUXVlc3Rpb246KiBBbmQgaG93IGRvZXMgdGhpcyAxLXVuaXQgaW5jcmVhc2UgaW4gbWF0aCBsZWFkIHRvIGV4cGVjdGVkIGNoYW5nZSBpbiB0aGUgUFJPQkFCSUxJVFkgb2YgYmVpbmcgaW4gaG9ub3Vycz8KCmBgYHtyfQpsb2dpdF9ob25vdXJzXzU1IDwtIGVxMyRjb2VmZmljaWVudHNbMV0gKyBlcTMkY29lZmZpY2llbnRzWzJdICogNTUKbG9naXRfaG9ub3Vyc181NCA8LSBlcTMkY29lZmZpY2llbnRzWzFdICsgZXEzJGNvZWZmaWNpZW50c1syXSAqIDU0Cgpwcm9iX2hvbm91cnNfNTUgPC0gZXhwKGxvZ2l0X2hvbm91cnNfNTUpIC8gKDEgKyBleHAobG9naXRfaG9ub3Vyc181NSkpCnByb2JfaG9ub3Vyc181NCA8LSBleHAobG9naXRfaG9ub3Vyc181NCkgLyAoMSArIGV4cChsb2dpdF9ob25vdXJzXzU0KSkKCnBhc3RlKCJ0aGUgcHJvYmFiaWxpdGllcyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBhIHN0dWRlbnQgd2l0aCBhIG1hdGggc2NvcmUgb2YgNTUgaXMiLCBwcm9iX2hvbm91cnNfNTUpCnBhc3RlKCJ0aGUgcHJvYmFiaWxpdGllcyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBhIHN0dWRlbnQgd2l0aCBhIG1hdGggc2NvcmUgb2YgNTQgaXMiLCBwcm9iX2hvbm91cnNfNTQpCmBgYAoKRG91YmxlLWNoZWNrCmBgYHtyfQpwYXN0ZSgidGhlIHByb2JhYmlsaXRpZXMgb2YgYmVpbmcgaW4gaG9ub3VycyBmb3IgYSBzdHVkZW50IHdpdGggYSBtYXRoIHNjb3JlIG9mIDU1IGlzIiwKICAgICAgcHJlZGljdChlcTMsIGRhdGEuZnJhbWUobWF0aCA9IDU1KSwgdHlwZSA9ICJyZXNwb25zZSIpCiAgICAgICkKCnBhc3RlKCJ0aGUgcHJvYmFiaWxpdGllcyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBhIHN0dWRlbnQgd2l0aCBhIG1hdGggc2NvcmUgb2YgNTQgaXMiLAogICAgICBwcmVkaWN0KGVxMywgZGF0YS5mcmFtZShtYXRoID0gNTQpLCB0eXBlID0gInJlc3BvbnNlIikKICAgICAgKQpgYGAKCkZpbmFsbHksCmBgYHtyfQpwYXN0ZSgidGhlIG1hcmdpbmFsIHByb2JhYmlsaXR5IG9mIGJlaW5nIGluIGhvbm91cnMgY2hhbmdlcyBieSIsIChleHAobG9naXRfaG9ub3Vyc181NSkgLyAoMSArIGV4cChsb2dpdF9ob25vdXJzXzU1KSkpIC0gKGV4cChsb2dpdF9ob25vdXJzXzU0KSAvICgxICsgZXhwKGxvZ2l0X2hvbm91cnNfNTQpKSksICJkdWUgdG8gYSBjaGFuZ2UgaW4gbWF0aCBzY29yZSBmcm9tIDU0IHRvIDU1IikgCiAgICAgIApwYXN0ZSgid2hpY2ggaXMgdGhlIHNhbWUgYXMiLCBwcmVkaWN0KGVxMywgZGF0YS5mcmFtZShtYXRoID0gNTUpLCB0eXBlID0gInJlc3BvbnNlIikgLSBwcmVkaWN0KGVxMywgZGF0YS5mcmFtZShtYXRoID0gNTQpLCB0eXBlID0gInJlc3BvbnNlIikpCmBgYAoKIyBFcTQgLSBtdWx0aXBsZSBwcmVkaWNhdG9yIHZhcmlhYmxlcyBhbmQgbm8gaW50ZXJhY3Rpb24gdGVybQoKRXF1YXRpb24gNCBpczoKCiAgICBsb2dpdChwKSA9IGIwICsgYjEgKiBtYXRoICsgYjIgKiBmZW1hbGUgKyBiMyAqIHJlYWQKCmBgYHtyfQplcTQgPC0gZ2xtKGhvbiB+IG1hdGggKyBmZW1hbGUgKyByZWFkLAogICAgICAgICAgIGRhdGEgPSBkYXQsCiAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIikKCnN1bW1hcnkoZXE0KQpgYGAKCkVhY2ggZXN0aW1hdGVkIGNvZWZmaWNpZW50ID0gZXhwZWN0ZWQgY2hhbmdlIGluIHRoZSBMT0dJVCBvZiBiZWluZyBpbiBob25vdXJzIGZvciBhIDEtdW5pdCBpbmNyZWFzZSBpbiB0aGUgY29ycmVzcG9uZGluZyBwcmVkaWNhdG9yLCBob2xkaW5nIGFsbCBvdGhlciBwcmVkaWN0b3JzIGF0IGNvbnN0YW50IHZhbHVlcy4KCkFuZCBlYWNoIGV4cChlc3RpbWF0ZWQgY29lZmZpY2llbnQpID0gZXhwZWN0ZWQgY2hhbmdlIGluIHRoZSBPRERTIFJBVElPIG9mIGJlaW5nIGluIGhvbm91cnMgZm9yIGEgMS11bml0IGluY3JlYXNlIGluIHRoZSBjb3JyZXNwb25kaW5nIHByZWRpY2F0b3IsIGhvbGRpbmcgYWxsIG90aGVyIHByZWRpY3RvcnMgYXQgY29uc3RhbnQgdmFsdWVzLgoKVGhlIGV4cGVjdGVkIGNoYW5nZXMgaW4gUFJPQkFCSUxJVFkgbXVzdCBiZSBjYWxjdWxhdGVkIGluZGl2aWR1YWxseSBhcyBpdCB3b3VsZCBjaGFuZ2UgZGVwZW5kaW5nIG9uIHRoZSB2YWx1ZXMgb2YgdGhlIHByZWRpY2F0b3JzLgoKYGBge3J9CnBhc3RlKCJGaXhpbmcgdmFsdWVzIGZvciBtYXRoIGFuZCByZWFkLCB0aGUgT0REUyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBmZW1hbGVzIG92ZXIgdGhlIE9ERFMgb2YgYmVpbmcgaW4gaG9ub3VycyBmb3IgbWFsZXMgaXMiLCBleHAoZXE0JGNvZWZmaWNpZW50c1szXSksICJ3aGljaCBtZWFucyB0aGUgT0REUyBmb3IgZmVtYWxlcyBhcmUiLCAxMDAgKiAoZXhwKGVxNCRjb2VmZmljaWVudHNbM10pIC0gMSksICJwZXIgY2VudCBoaWdoZXIgdGhhbiB0aGUgT0REUyBmb3IgbWFsZXMiKQpgYGAKCmBgYHtyfQpwYXN0ZSgiRml4aW5nIHZhbHVlcyBmb3IgZmVtYWxlIGFuZCByZWFkLCB0aGUgT0REUyBvZiBiZWluZyBpbiBob25vdXJzIGZvciBhIDEtdW5pdCBpbmNyZWFzZSBpbiBtYXRoIGlzIiwgZXhwKGVxNCRjb2VmZmljaWVudHNbMl0pLCAid2hpY2ggbWVhbnMgdGhlIE9ERFMgb2YgYmVpbmcgaW4gaG9ub3VycyBpbmNyZWFzZXMgYnkiLCAxMDAgKiAoZXhwKGVxNCRjb2VmZmljaWVudHNbMl0pIC0gMSksICJwZXIgY2VudCBkdWUgdG8gYSAxLXVuaXQgaW5jcmVhc2UgaW4gbWF0aCIpCmBgYAoKIyBFcTUgLSBtdWx0aXBsZSBwcmVkaWNhdG9yIHZhcmlhYmxlcyB3aXRoIGludGVyYWN0aW9uIHRlcm0KCkVxdWF0aW9uIDUgaXM6CgogICAgICBsb2dpdChwKSA9IGIwICsgYjEgKiBmZW1hbGUgKyBiMiAqIG1hdGggKyBiMyAqIGZlbWFsZSAqIG1hdGgKICAgICAgCmBgYHtyfQplcTUgPC0gZ2xtKGhvbiB+IGZlbWFsZSArIG1hdGggKyBmZW1hbGU6bWF0aCwKICAgICAgICAgICBkYXRhID0gZGF0LAogICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIpCgpzdW1tYXJ5KGVxNSkKYGBgCgpJbiB0aGUgcHJlc2VuY2Ugb2YgaW50ZXJhY3Rpb24gdGVybSBvZiBgZmVtYWxlYCBieSBgbWF0aGAsIHdlIGNhbiBubyBsb25nZXIgdGFsayBhYm91dCB0aGUgZWZmZWN0IG9mIGZlbWFsZSwgaG9sZGluZyBhbGwgb3RoZXIgdmFyaWFibGVzIGF0IGNlcnRhaW4gdmFsdWVzLiBUaGlzIGlzIGJlY2F1c2UgaXQgbm8gbG9uZ2VyIG1ha2VzIHNlbnNlIHRvIGZpeCBgbWF0aGAgYW5kIGBmZW1hbGUgeCBtYXRoYCBhdCBjZXJ0YWluIHZhbHVlIGFuZCBzdGlsbCBhbGxvd2luZyBgZmVtYWxlYCB0byBjaGFuZ2UgZnJvbSAwIHRvIDEKClRoZSAnZXF1YXRpb24nIGZvciBtYWxlIChmZW1hbGUgPT0gMCkgaXM6CgogICAgICBsb2dpdChwKSA9IGIwICsgYjEgKiBmZW1hbGUgKyBiMiAqIG1hdGggKyBiMyAqIGZlbWFsZSAqIG1hdGggCiAgICAgICAgICAgICAgID0gYjAgKyBiMiAqIG1hdGgKClRoZSAnZXF1YXRpb24nIGZvciBmZW1hbGUgKGZlbWFsZSA9PSAxKSBpczoKCiAgICAgIGxvZ2l0KHApID0gYjAgKyBiMSAqIGZlbWFsZSArIGIyICogbWF0aCArIGIzICogZmVtYWxlICogbWF0aCAKICAgICAgICAgICAgICAgPSAoYjAgKyBiMSkgKyAoYjIgKyBiMykgKiBtYXRoIAogICAgICAgICAgICAgICAKYGBge3J9CnBhc3RlKCJmb3IgbWFsZSwgYSAxLXVuaXQgaW5jcmVhc2UgaW4gbWF0aCBsZWFkcyB0byBhIGNoYW5nZSBpbiB0aGUgbG9naXQgb2YiLCBlcTUkY29lZmZpY2llbnRzWzNdKQoKcGFzdGUoInRoaXMgaXMgYSBjaGFuZ2UgaW4gdGhlIG9kZHMgcmF0aW8gb2YiLCBleHAoZXE1JGNvZWZmaWNpZW50c1szXSkpCmBgYAoKYGBge3J9CnBhc3RlKCJmb3IgZmVtYWxlLCBhIDEtdW5pdCBpbmNyZWFzZSBpbiBtYXRoIGxlYWRzIHRvIGEgY2hhbmdlIGluIHRoZSBsb2dpdCBvZiIsIGVxNSRjb2VmZmljaWVudHNbM10gKyBlcTUkY29lZmZpY2llbnRzWzRdKQoKcGFzdGUoInRoaXMgaXMgYSBjaGFuZ2UgaW4gdGhlIG9kZHMgcmF0aW8gb2YiLCBleHAoZXE1JGNvZWZmaWNpZW50c1szXSArIGVxNSRjb2VmZmljaWVudHNbNF0pKQpgYGAKCmBgYHtyfQpwYXN0ZSgidGhlIFJBVElPIG9mIHRoZSBmZW1hbGUtdG8tbWFsZSBvZGRzIHJhdGlvIGZvciBtYXRoIiwgZXhwKGVxNSRjb2VmZmljaWVudHNbM10gKyBlcTUkY29lZmZpY2llbnRzWzRdKSAvIGV4cChlcTUkY29lZmZpY2llbnRzWzNdKSwgIiA9IGV4cChjb2VmZmljaWVudChpbnRlcmFjdGlvbiB0ZXJtKSkgPSAiLCBleHAoZXE1JGNvZWZmaWNpZW50c1s0XSkpCmBgYAo=