1 Intro

In this tutorial I will try to explain the logic behind marginal effects.

I will provide you with practical examples. And give you some exercises.

To copy the code click the button in the upper right corner of the code-chunks.

Questions can be addressed to Jochem Tolsma.


1.1 Custom functions

  • package.check: Check if packages are installed (and install if not) in R (source).
fpackage.check <- function(packages) {
    lapply(packages, FUN = function(x) {
        if (!require(x, character.only = TRUE)) {
            install.packages(x, dependencies = TRUE)
            library(x, character.only = TRUE)
        }
    })
}

1.2 Packages

  • tidyverse: if you can’t base them, join them
  • miniGUI:
  • rgl: plotting stuf
  • Deriv: to let R calculate derivatives
  • margin: an R package that will calculate margins for you
  • mvtnorm: to simulate data (with correlated covariates)
  • boot: to calculate SE/CI of the marginal effects
packages = c("tidyverse", "miniGUI", "rgl", "Deriv", "margins", "mvtnorm", "boot", "DAMisc")

fpackage.check(packages)

1.3 Session info

This is my setup.

sessionInfo()
#> R version 4.2.0 (2022-04-22 ucrt)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 19042)
#> 
#> Matrix products: default
#> 
#> locale:
#> [1] LC_COLLATE=Dutch_Netherlands.utf8  LC_CTYPE=Dutch_Netherlands.utf8   
#> [3] LC_MONETARY=Dutch_Netherlands.utf8 LC_NUMERIC=C                      
#> [5] LC_TIME=Dutch_Netherlands.utf8    
#> 
#> attached base packages:
#> [1] tcltk     stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] DAMisc_1.7.2    boot_1.3-28     mvtnorm_1.1-3   margins_0.3.26  Deriv_4.1.3     miniGUI_0.8-1  
#>  [7] forcats_0.5.1   stringr_1.4.0   dplyr_1.0.9     purrr_0.3.4     readr_2.1.2     tidyr_1.2.0    
#> [13] tibble_3.1.7    ggplot2_3.4.0   tidyverse_1.3.1 rgl_0.109.2     knitr_1.39     
#> 
#> loaded via a namespace (and not attached):
#>   [1] VGAM_1.1-6          minqa_1.2.4         colorspace_2.0-3    ellipsis_0.3.2     
#>   [5] snakecase_0.11.0    base64enc_0.1-3     fs_1.5.2            optiscale_1.2.2    
#>   [9] rstudioapi_0.13     DT_0.23             fansi_1.0.3         lubridate_1.8.0    
#>  [13] xml2_1.3.3          codetools_0.2-18    splines_4.2.0       extrafont_0.18     
#>  [17] effects_4.2-1       jsonlite_1.8.0      nloptr_2.0.3        broom_0.8.0        
#>  [21] Rttf2pt1_1.3.10     dbplyr_2.2.0        png_0.1-7           compiler_4.2.0     
#>  [25] httr_1.4.3          backports_1.4.1     assertthat_0.2.1    Matrix_1.4-1       
#>  [29] fastmap_1.1.0       survey_4.1-1        cli_3.3.0           formatR_1.12       
#>  [33] htmltools_0.5.2     tools_4.2.0         coda_0.19-4         gtable_0.3.0       
#>  [37] glue_1.6.2          Rcpp_1.0.8.3        carData_3.0-5       cellranger_1.1.0   
#>  [41] jquerylib_0.1.4     vctrs_0.5.1         nlme_3.1-157        extrafontdb_1.0    
#>  [45] insight_0.17.1      xfun_0.31           lme4_1.1-29         rvest_1.0.2        
#>  [49] lifecycle_1.0.3     klippy_0.0.0.9500   srvyr_1.1.2         MASS_7.3-56        
#>  [53] scales_1.2.0        hms_1.1.1           parallel_4.2.0      RColorBrewer_1.1-3 
#>  [57] yaml_2.3.5          pbapply_1.5-0       pander_0.6.5        sass_0.4.1         
#>  [61] latticeExtra_0.6-29 stringi_1.7.6       rlang_1.0.6         pkgconfig_2.0.3    
#>  [65] evaluate_0.15       lattice_0.20-45     prediction_0.3.14   htmlwidgets_1.5.4  
#>  [69] tidyselect_1.1.2    plyr_1.8.7          magrittr_2.0.3      R6_2.5.1           
#>  [73] generics_0.1.2      clarkeTest_0.1.0    DBI_1.1.2           pillar_1.7.0       
#>  [77] haven_2.5.0         withr_2.5.0         jtools_2.2.1        survival_3.3-1     
#>  [81] abind_1.4-5         nnet_7.3-17         janitor_2.1.0       modelr_0.1.8       
#>  [85] crayon_1.5.1        car_3.0-13          utf8_1.2.2          tzdb_0.3.0         
#>  [89] rmarkdown_2.14      jpeg_0.1-9          grid_4.2.0          readxl_1.4.0       
#>  [93] data.table_1.14.2   AICcmodavg_2.3-1    reprex_2.0.1        digest_0.6.29      
#>  [97] xtable_1.8-4        stats4_4.2.0        munsell_0.5.0       unmarked_1.2.5     
#> [101] bslib_0.3.1         mitools_2.4

1.4 Background reading and theory

Ai and Norton, 2003. Interaction terms in logit and probit models : https://doi.org/10.1016/S0165-1765(03)00032-6

1.4.1 Marginal effect definition

What is a marginal effect? If you google, you will find this definition:

Marginal effects measure the impact that an instantaneous change in one variable has on the outcome variable while all other variables are held constant.

But I would say the definition depends on the measurement level of your independent variable.

For a dichotomous independent variable:

  • The change in your dependent variable if (ceteris paribus) your independent variable x1 changes from 0 to 1.

More formally:

\[ ME = f(X|x1=1) - f(X|x1=0), \]

where \(Y = f(X)\) and \(X\) is your set of covariates.

For a metric independent variable:

  • The change in your dependent variable, Y, at some value, x, of your independent variable x1, per (ceteris paribus) small change of x1 at x of s.

More formally:

\[ ME = \frac{f(X|x1=x + s) - f(X|x1=x - s)}{2s}, \] where \(s\) is an infinitesimal small number.

This is thus the slope (or more precisely the partial derivative) of \(f(X)\) at the point where \(x1\) is \(x\):

\[ ME = f^{'}(X|x1=x), \] and \[ f^{'}(X) = \displaystyle \frac{\partial f(X)}{\partial x1} \]

Where \(X\) is your set of covariates, \(x1\) is one of your covariates, \(x\) is a value of your covariate \(x1\).

1.4.2 Average Marginal Effect / Marginal Effect at Mean

Because you can calculate the ME at different values of your other covariates, you can calculate many MEs.

If you calculate the ME for all respondents in your dataset (and their observed values of covariates) and take the mean, you will end up with the AME, the Average Marginal Effect.
If you calculate the ME for covariate values set at their mean, you will end up with the MEM, the Marginal Effect at Mean (or MME, Mean Marginal Effect).

1.4.3 Marginal interaction effect

Sometimes this is incorrectly defined as:

\[ \displaystyle \frac{\partial f(X)}{\partial (x1x2)} \] Correctly would be:

\[ \displaystyle \frac{\partial f^2(X)}{\partial(x1)\partial(x2)} \]

Interpretation: How does the effect of \(x1\) on \(Y\) change with a change in \(x2\), conditional on \(X\).

Naturally, you have analogous definitions if \(x1\) and/or \(x2\) are dichotomous.


1.5 Simulated data

set.seed(227863)  # so we all have the same data

# we start with simulating some correlated covariates
sigma <- matrix(c(4, 2, 3, 2, 3, 1.5, 3, 1.5, 5), ncol = 3)
# sigma
df <- data.frame(rmvnorm(n = 500, mean = c(6, 8, 7), sigma = sigma))
colnames(df) <- c("age", "sex", "educ")

df %>%
    mutate(sex = ifelse(sex > mean(sex) + 0.3 * sd(sex), 0, 1), sex = as.factor(sex), sex = recode_factor(sex,
        `0` = "men", `1` = "women"), age = round(age, 2), educ = round(educ, 2)) -> df

# Let us add a linear dependent variable with the following formula
df %>%
    mutate(y_lin = 10 + 5 * as.numeric(sex) + 2.4 * age + 2 * educ + 0.5 * as.numeric(sex) * educ + -0.2 *
        age * educ + rnorm(500, 0, 0.5)) -> df

head(df)
#>    age   sex educ    y_lin
#> 1 6.87 women 9.34 51.13692
#> 2 6.34 women 4.64 42.90037
#> 3 5.87 women 7.96 48.96823
#> 4 1.29 women 4.01 33.57377
#> 5 6.66   men 5.66 36.26559
#> 6 9.88   men 9.96 43.77554


This was hopefully, all quite straightforward. But let us also simulate a dichotomous dependent variable.

Thus we would like to simulate a dichotomous dependent variable (0/1), from a Bernoulli distribution, with a logit link function. So we can illustrate marginal effects with logistic regression model.

The log-odds of the probability of an event is a linear combination of the independent variables

Please remember, the logistic function is:

\[ f(X) = P(Y=1|X) = n(X) = \pi_x = \frac{e^X}{(1 + e^X)} = \frac{1} {(1+e^{-X})} \]

We can easily calculate \(P(Y=1|X)\) in R if we know \(X\) via the function plogis(X).

And if we have \(P(Y=1|X)\) we can simulate an observed variable according to a binomial distribution:

\[Y_{di} \sim {\sf Binom}(1, P(Y=1|X))\]

df %>%
    mutate(y_di = rbinom(n = 500, size = 1, prob = plogis(-2.4 + 0.3 * as.numeric(sex) + 0.4 * age +
        0.3 * educ + 0.1 * as.numeric(sex) * educ + -0.1 * age * educ))) -> df

table(df$sex, df$y_di)
#>        
#>           0   1
#>   men   152  27
#>   women 141 180
head(df)
#>    age   sex educ    y_lin y_di
#> 1 6.87 women 9.34 51.13692    0
#> 2 6.34 women 4.64 42.90037    1
#> 3 5.87 women 7.96 48.96823    1
#> 4 1.29 women 4.01 33.57377    1
#> 5 6.66   men 5.66 36.26559    1
#> 6 9.88   men 9.96 43.77554    0

2 Linear model

Estimate the model

m1 <- lm(y_lin ~ sex + age + educ, data = df)
summary(m1)
#> 
#> Call:
#> lm(formula = y_lin ~ sex + age + educ, data = df)
#> 
#> Residuals:
#>     Min      1Q  Median      3Q     Max 
#> -7.5318 -0.5558  0.2797  0.9000  3.3035 
#> 
#> Coefficients:
#>             Estimate Std. Error t value Pr(>|t|)    
#> (Intercept) 19.22576    0.30004   64.08   <2e-16 ***
#> sexwomen     8.83186    0.15553   56.78   <2e-16 ***
#> age          1.05363    0.04703   22.40   <2e-16 ***
#> educ         1.71399    0.03890   44.07   <2e-16 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 1.455 on 496 degrees of freedom
#> Multiple R-squared:  0.9355, Adjusted R-squared:  0.9351 
#> F-statistic:  2399 on 3 and 496 DF,  p-value: < 2.2e-16


And plot the model.

colors <- ifelse(df$sex == "men", "red", "blue")

with(df, plot3d(educ, age, predict(m1), col = colors))

2.1 Marginal effects of age.

We know that \(Y = intercept + b_sex*sex + b_age*age + b_educ*educ\). We know that age is a continuous variable. Thus we calculate the derivative (no worries we will use the function D) of Y with respect to age.

ME_age <- D(expression(intercept + b_sex * sex + b_age * age + b_educ * educ), "age")
ME_age
#> b_age

Find the value of this coefficient:

# coef(m1)
b_intercept <- coef(m1)[1]
b_sex <- coef(m1)[2]
b_age <- coef(m1)[3]
b_educ <- coef(m1)[4]
b_age
#>      age 
#> 1.053627


Exercise: Calculate the average marginal effect for age by using the numerical approach to calculate the derivative

2.2 Marginal effects of sex.

Remember the definition?

ME_sex <- with(df, (b_intercept + b_sex * 1 + b_age * age + b_educ * educ) - (b_intercept + b_sex * 0 +
    b_age * age + b_educ * educ))
ME_sex
#>   [1] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [11] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [21] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [31] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [41] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [51] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [61] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [71] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [81] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#>  [91] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [101] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [111] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [121] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [131] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [141] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [151] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [161] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [171] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [181] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [191] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [201] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [211] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [221] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [231] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [241] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [251] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [261] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [271] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [281] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [291] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [301] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [311] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [321] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [331] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [341] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [351] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [361] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [371] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [381] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [391] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [401] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [411] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [421] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [431] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [441] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [451] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [461] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [471] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [481] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865
#> [491] 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865 8.831865

A bit shorter would be:

dfnews0 <- dfnews1 <- df
# construct two datasets
dfnews0$sex <- "men"
dfnews1$sex <- "women"
# let R calculate the predicted scores
mean(predict(m1, dfnews1) - predict(m1, dfnews0))
#> [1] 8.831865


So, lets calculate the AME: mean(ME_sex) = 8.8318647.

And what is the interpretation? Well, let us plot the values of the dependent variable, if we hold education constant and vary age, for men and women.

plot(c(-10:10), b_intercept + b_sex * 1 + b_age * c(-10:10) + b_educ * mean(df$educ), col = "red", type = "b",
    xlab = "age", ylab = "y_lin", xlim = c(-10, 10), ylim = c(0, 60))
par(new = TRUE)
plot(c(-10:10), b_intercept + b_sex * 0 + b_age * c(-10:10) + b_educ * mean(df$educ), col = "blue", type = "b",
    xlab = "", ylab = "", xlim = c(-10, 10), ylim = c(0, 60))


You see, that at all values of age, the difference between men and women is AME.

2.3 check with R

summary(margins(m1))  #SE via deltamethod
#>    factor    AME     SE       z      p  lower  upper
#>       age 1.0536 0.0470 22.4029 0.0000 0.9614 1.1458
#>      educ 1.7140 0.0389 44.0662 0.0000 1.6378 1.7902
#>  sexwomen 8.8319 0.1555 56.7852 0.0000 8.5270 9.1367
summary(margins(m1, vce = "bootstrap", iterations = 999))  #SE via bootstrapping
#>    factor    AME     SE       z      p  lower  upper
#>       age 1.0536 0.0538 19.5965 0.0000 0.9482 1.1590
#>      educ 1.7140 0.0471 36.4029 0.0000 1.6217 1.8063
#>  sexwomen 8.8319 0.1396 63.2743 0.0000 8.5583 9.1054

3 Logistic model

m1_d <- glm(y_di ~ sex + age + educ, data = df, family = binomial)
summary(m1_d)
#> 
#> Call:
#> glm(formula = y_di ~ sex + age + educ, family = binomial, data = df)
#> 
#> Deviance Residuals: 
#>     Min       1Q   Median       3Q      Max  
#> -2.0237  -0.9296  -0.4609   1.0013   2.1158  
#> 
#> Coefficients:
#>             Estimate Std. Error z value Pr(>|z|)    
#> (Intercept)  0.63124    0.48127   1.312 0.189656    
#> sexwomen     1.43639    0.25730   5.583 2.37e-08 ***
#> age         -0.28712    0.07645  -3.756 0.000173 ***
#> educ        -0.04639    0.06069  -0.764 0.444632    
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for binomial family taken to be 1)
#> 
#>     Null deviance: 678.28  on 499  degrees of freedom
#> Residual deviance: 560.37  on 496  degrees of freedom
#> AIC: 568.37
#> 
#> Number of Fisher Scoring iterations: 4


Let us have a look at the model:

# with(df, plot3d(sex, age, fitted.values(m1_d), col=colors))
with(df, plot3d(educ, age, fitted.values(m1_d), col = colors))

3.1 Marginal effects of age

Please remember, the logistic function is:

\[ f(X) = \frac{1} {(1+e^{-X})} \]

And the definition of the ME for a continuous variable \(x1\):

\[ ME = f^{'}(X|x1=x), \]

Thus we need to find \(f^{'}(X)\) with respect to \(x1\) (i.e., age).

You could do that yourself by using the chain-rule:

\[\displaystyle \frac{\partial f(X)}{\partial (x1)} = \displaystyle \frac{\partial f(X)}{\partial (X)}*\displaystyle \frac{\partial (X)}{\partial (x1)}\]

… or let R help you.

ME_age_f2 <- D(expression(1/(1 + exp(-1 * (b_intercept + b_sex * sex + b_age * age + b_educ * educ)))),
    "age")
ME_age_f2
#> exp(-1 * (b_intercept + b_sex * sex + b_age * age + b_educ * 
#>     educ)) * b_age/(1 + exp(-1 * (b_intercept + b_sex * sex + 
#>     b_age * age + b_educ * educ)))^2


Luckily for us, the derivative of plogis(X) is dlogis(X).

You wanna check? simply run: ?dlogis.

Retrieve the coefficients:

coef(m1_d)
#> (Intercept)    sexwomen         age        educ 
#>   0.6312358   1.4363873  -0.2871232  -0.0463920
b_intercept <- coef(m1_d)[1]
b_sex <- coef(m1_d)[2]
b_age <- coef(m1_d)[3]
b_educ <- coef(m1_d)[4]

And fill in the formula:

ME_age <- with(df, dlogis(b_intercept + b_sex * (sex == "women") + b_age * age + b_educ * educ) * b_age)
# shorter would be:
ME_age <- dlogis(predict(m1_d)) * b_age
head(ME_age)
#>           1           2           3           4           5           6 
#> -0.06976658 -0.07176243 -0.07177781 -0.04251901 -0.04164254 -0.01742796

So, you notice - hopefully - that that the ME_age is different for each respondent. Let us take the mean.

AME_age = -0.0546062.:

AME_age <- mean(ME_age)
AME_age
#> [1] -0.05460625

3.2 marginal effect sex.

Remember \(ME_{sex} = f(X|sex=1) - f(X|sex=0)\).

And once again simply fill in the formula.

ME_sex <- with(df, plogis(b_intercept + b_sex * 1 + b_age * age + b_educ * educ) - plogis(b_intercept +
    b_sex * 0 + b_age * age + b_educ * educ))
AME_sex <- mean(ME_sex)
AME_sex
#> [1] 0.290718

or,…

# new datasets
dfs0 <- dfs1 <- df
dfs0$sex <- "men"
dfs1$sex <- "women"
ME_sex <- plogis(predict(m1_d, dfs1)) - plogis(predict(m1_d, dfs0))
AME_sex <- mean(ME_sex)
AME_sex
#> [1] 0.290718

Let us plot the marginal effect at different values of the other covariates.

with(df, plot3d(educ, age, ME_sex, col = colors))

AME_sex = 0.290718.

3.3 Check with R.

# okay, now let's use R to calculate the marginal effects
summary(margins(m1_d))  #make sure that variable sex is defined as a factor not as a numeric variable. 
#>    factor     AME     SE       z      p   lower   upper
#>       age -0.0546 0.0138 -3.9608 0.0001 -0.0816 -0.0276
#>      educ -0.0088 0.0115 -0.7661 0.4436 -0.0314  0.0138
#>  sexwomen  0.2907 0.0485  5.9983 0.0000  0.1957  0.3857

Everything seems to be okay!


4 Linear model with interactions

Let us estimate a better fitting model.

m2 <- lm(y_lin ~ sex + age + educ + sex:educ + age:educ, data = df)
# note that we define the interaction within the formula. This is crucial!!
summary(m2)
#> 
#> Call:
#> lm(formula = y_lin ~ sex + age + educ + sex:educ + age:educ, 
#>     data = df)
#> 
#> Residuals:
#>      Min       1Q   Median       3Q      Max 
#> -1.30718 -0.33026  0.00721  0.32965  1.94917 
#> 
#> Coefficients:
#>                Estimate Std. Error t value Pr(>|t|)    
#> (Intercept)   14.866935   0.284848   52.19   <2e-16 ***
#> sexwomen       5.022406   0.197319   25.45   <2e-16 ***
#> age            2.396873   0.036563   65.56   <2e-16 ***
#> educ           2.527558   0.040563   62.31   <2e-16 ***
#> sexwomen:educ  0.497224   0.025560   19.45   <2e-16 ***
#> age:educ      -0.200642   0.004748  -42.26   <2e-16 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 0.5058 on 494 degrees of freedom
#> Multiple R-squared:  0.9922, Adjusted R-squared:  0.9922 
#> F-statistic: 1.263e+04 on 5 and 494 DF,  p-value: < 2.2e-16

Retrieve coefficients.

coef(m2)
#>   (Intercept)      sexwomen           age          educ sexwomen:educ      age:educ 
#>    14.8669349     5.0224058     2.3968728     2.5275580     0.4972235    -0.2006417
b_intercept <- coef(m2)[1]
b_sex <- coef(m2)[2]
b_age <- coef(m2)[3]
b_educ <- coef(m2)[4]
b_sex_educ <- coef(m2)[5]
b_age_educ <- coef(m2)[6]

4.1 marginal effect sex

Let’s use power of R.

ME_sex <- predict(m2, newdata = dfs1) - predict(m2, newdata = dfs0)
AME_sex <- mean(ME_sex)
AME_sex
#> [1] 8.484782

4.2 marginal effect age

ME_age_f <- expression(b_age + b_age_educ * educ)  #this is the derivative with respect to age
ME_age <- with(df, eval(ME_age_f))  #Do you see I do not fit all variables myself but use this convenient function eval()
AME_age <- mean(ME_age)
AME_age
#> [1] 0.9997207

4.3 Marginal effect education

ME_educ_f <- expression(b_educ + b_age_educ * age + b_sex_educ * (sex == "women"))
ME_educ <- with(df, eval(ME_educ_f))
AME_educ <- mean(ME_educ)
AME_educ
#> [1] 1.65055

4.4 Let us check with R

summary(margins(m2))
#>    factor    AME     SE        z      p  lower  upper
#>       age 0.9997 0.0164  61.0272 0.0000 0.9676 1.0318
#>      educ 1.6505 0.0136 121.6687 0.0000 1.6240 1.6771
#>  sexwomen 8.4848 0.0554 153.1992 0.0000 8.3762 8.5933

4.5 Marginal interaction effect educ*age

First calculate derivative to age and then to educ.

fx <- expression(intercept + b_sex * sex + b_age * age + b_educ * educ + b_sex_educ * sex * educ + b_age_educ *
    age * educ)
dfxdage <- D(fx, "age")
d2fxdagededuc <- D(dfxdage, "educ")
# check if the variable sex is in our expression we then need to be careful because it is factor.
# but it is not.
AME_age_educ <- mean(with(df, eval(d2fxdagededuc)))
AME_age_educ
#> [1] -0.2006417

And this is (of course) our regression coefficient for the interaction.

4.6 Marginal interaction effect sex*educ

First calculate derivative to sex and then to educ.

fx <- expression(intercept + b_sex * sex + b_age * age + b_educ * educ + b_sex_educ * sex * educ + b_age_educ *
    age * educ)
dfxdsex <- expression(intercept + b_sex * 1 + b_age * age + b_educ * educ + b_sex_educ * 1 * educ + b_age_educ *
    age * educ - (intercept + b_sex * 0 + b_age * age + b_educ * educ + b_sex_educ * 0 * educ + b_age_educ *
    age * educ))
dfxdsex
#> expression(intercept + b_sex * 1 + b_age * age + b_educ * educ + 
#>     b_sex_educ * 1 * educ + b_age_educ * age * educ - (intercept + 
#>     b_sex * 0 + b_age * age + b_educ * educ + b_sex_educ * 0 * 
#>     educ + b_age_educ * age * educ))
dfxdsex <- Simplify(dfxdsex)
dfxdsex
#> expression(b_sex + b_sex_educ * educ)
d2fxdsexdeduc <- D(dfxdsex, "educ")
d2fxdsexdeduc
#> b_sex_educ
# this is just a constant, namely our interaction coefficient
eval(d2fxdsexdeduc)
#> sexwomen:educ 
#>     0.4972235

5 Logistic with model interactions

Let’s estimate a better model

m2_d <- glm(y_di ~ sex + age + educ + sex:educ + age:educ, family = binomial, data = df)
summary(m2_d)
#> 
#> Call:
#> glm(formula = y_di ~ sex + age + educ + sex:educ + age:educ, 
#>     family = binomial, data = df)
#> 
#> Deviance Residuals: 
#>     Min       1Q   Median       3Q      Max  
#> -1.8203  -0.8882  -0.3042   0.9333   2.1433  
#> 
#> Coefficients:
#>               Estimate Std. Error z value Pr(>|z|)    
#> (Intercept)   -2.73893    1.38148  -1.983  0.04741 *  
#> sexwomen       0.95791    0.98451   0.973  0.33056    
#> age            0.46274    0.18541   2.496  0.01257 *  
#> educ           0.56442    0.21555   2.619  0.00883 ** 
#> sexwomen:educ  0.06869    0.13933   0.493  0.62202    
#> age:educ      -0.12350    0.02823  -4.375 1.21e-05 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for binomial family taken to be 1)
#> 
#>     Null deviance: 678.28  on 499  degrees of freedom
#> Residual deviance: 533.69  on 494  degrees of freedom
#> AIC: 545.69
#> 
#> Number of Fisher Scoring iterations: 5

Plotting the estimated model is very insightful.

with(df, plot3d(sex, age, fitted.values(m2_d), col = colors))
# with(df, plot3d(educ, age, fitted.values(m2_d), col=colors)) with(df, plot3d(educ[sex=='men'],
# age[sex=='men'], fitted.values(m2_d)[sex=='men'], col=colors[sex=='men'])) with(df,
# plot3d(educ[sex=='women'], age[sex=='women'], fitted.values(m2_d)[sex=='women'],
# col=colors[sex=='women']))

Retrieve coefficients.

coef(m2_d)
#>   (Intercept)      sexwomen           age          educ sexwomen:educ      age:educ 
#>   -2.73893262    0.95790989    0.46274191    0.56442210    0.06868779   -0.12350206
b_intercept <- coef(m2_d)[1]
b_sex <- coef(m2_d)[2]
b_age <- coef(m2_d)[3]
b_educ <- coef(m2_d)[4]
b_sex_educ <- coef(m2_d)[5]
b_age_educ <- coef(m2_d)[6]

5.1 marginal effect sex

Let’s use power of R

ME_sex <- plogis(predict(m2_d, newdata = dfs1)) - plogis(predict(m2_d, newdata = dfs0))
AME_sex <- mean(ME_sex)
AME_sex
#> [1] 0.2700221

5.2 marginal effect age

ME_age <- with(df, dlogis(predict(m2_d)) * (b_age + b_age_educ * educ))
AME_age <- mean(ME_age)
AME_age
#> [1] -0.06071402

5.3 marginal effect educ

ME_educ <- with(df, dlogis(predict(m2_d)) * (b_educ + b_sex_educ * (sex == "women") + b_age_educ * age))
AME_educ <- mean(ME_educ)
AME_educ
#> [1] -0.009992943

5.4 check with R

summary(margins(m2_d))
#>    factor     AME     SE       z      p   lower   upper
#>       age -0.0607 0.0134 -4.5156 0.0000 -0.0871 -0.0344
#>      educ -0.0100 0.0111 -0.9018 0.3672 -0.0317  0.0117
#>  sexwomen  0.2700 0.0483  5.5892 0.0000  0.1753  0.3647

5.5 Marginal interaction effects.

Warning, calculating the derivatives may be difficult depending on f(x), and your mathematical background ;-)

We need the double derivative of \(f(x)\). We will call this function ddlogis.

# f(X) = P(Y=1|X) = n(X) = exp(X) / (1 + exp(X)) = 1 / (1+exp(-X))
nX <- expression(1/(1 + exp(-X)))
# similar as dlogis
dnXdx <- D(nX, "X")
# higher order derivative
d2nxdxdx <- D(dnXdx, "X")
# d2nxdxdx
ddlogis <- function(X) {
    -(exp(-X)/(1 + exp(-X))^2 - exp(-X) * (2 * (exp(-X) * (1 + exp(-X))))/((1 + exp(-X))^2)^2)
}

5.6 marginal interaction effect educ*age

MEeducage <- with(df, ddlogis(predict(m2_d)) * (b_educ + b_age_educ * age) * (b_age + b_age_educ * educ) +
    dlogis(predict(m2_d)) * (b_age_educ))

AMEeducage <- mean(MEeducage)
AMEeducage
#> [1] -0.01726894

5.6.1 Numerical approach

s <- 0.001  #define a small step

# define datasets
dfplusplus <- dfplusmin <- dfminplus <- dfminmin <- df
# add the small step to the variables
dfplusmin$age <- dfplusplus$age <- df$age + s
dfminmin$age <- dfminplus$age <- df$age - s
dfplusplus$educ <- dfminplus$educ <- df$educ + s
dfplusmin$educ <- dfminmin$educ <- df$educ - s

# calculate the predicted probabilities
p11 <- plogis(predict(m2_d, dfplusplus))
p10 <- plogis(predict(m2_d, dfplusmin))
p01 <- plogis(predict(m2_d, dfminplus))
p00 <- plogis(predict(m2_d, dfminmin))

# and the marginal effects. be aware of all the brackets. :-(
am <- (((p11 - p01)/(2 * s)) - ((p10 - p00)/(2 * s)))/(2 * s)
mean(am)
#> [1] -0.01711588


Exercise: Suppose that all estimated interaction terms are 0 in model m2_d what would be the marginal effect of the interaction effect of education with age according to the above? Thus what would be: MEeducage.

5.7 marginal interaction effect sex*educ

First calculate derivative to sex and then to educ

dfxdsex <- with(df, plogis(predict(m2_d, dfs1)) - plogis(predict(m2_d, dfs0)))

MEsexeduc <- with(df, dlogis(predict(m2_d, dfs1)) * (b_educ + b_sex_educ * 1 + b_age_educ * age) - dlogis(predict(m2_d,
    dfs0)) * (b_educ + b_sex_educ * 0 + b_age_educ * age))

AMEsexeduc <- mean(MEsexeduc)
AMEsexeduc
#> [1] -0.00386123

5.7.1 Numerical approach

s <- 0.001  #define a small step

# define datasets
dfplusplus <- dfplusmin <- dfminplus <- dfminmin <- df
# add the small step to the variables
dfplusmin$sex <- dfplusplus$sex <- "women"
dfminmin$sex <- dfminplus$sex <- "men"
dfplusplus$educ <- dfminplus$educ <- df$educ + s
dfplusmin$educ <- dfminmin$educ <- df$educ - s

# calculate the predicted probabilities
p11 <- plogis(predict(m2_d, dfplusplus))
p10 <- plogis(predict(m2_d, dfplusmin))
p01 <- plogis(predict(m2_d, dfminplus))
p00 <- plogis(predict(m2_d, dfminmin))

# and the marginal effects. be aware of all the brackets. :-(
am <- ((p11 - p01) - (p10 - p00))/(2 * s)
mean(am)
#> [1] -0.00386123

Let us plot these interaction effects against the fitted values.

plot(fitted.values(m2_d), MEsexeduc, col = colors)


You see that at some points the interaction is negative and at some it is positive.

5.8 Check with R2

results <- intEff(m2_d, vars = c("age", "educ"), data = df)
mean(results$byobs$int$int_eff)
#> [1] -0.01726894

quite close.

results <- intEff(m2_d, vars = c("sex", "educ"), data = df)
mean(results$byobs$int$int_eff)
#> [1] 0.04301771

This is not the same as above, probably because function cannot handle double interactions??


6 Take home message

  • When you use a non-linear model, the interpretation of marginal effects is difficult.
  • Only when you have a non-linear model, the use of marginal effects makes sense.
  • MEM: How useful are they?
  • The absence or presence of an estimated interaction term does not say much about the presence or absence of a (marginal) interaction effect when your link function is not linear.
  • R does not provide packages/functions that calculate marginal effects for interaction effects for non-linear models.

7 SE of marginal effects.

Well, you made it this far in the tutorial. The goal was to show you the underlying idea and interpretation of marginal effects. Now, that you understand, you can apply the standard packages in R to calculate the marginal effects for you, like margin. There is, however, one caveat. These packages are not able to calculate (correct) marginal effects of interaction effects and not all non-linear models are supported. This means you sometimes have to calculate the marginal effects yourself. Which can be a challenge but as you see, is doable. But if you calculate the ME yourself you might also want to know the standard error (SE) or confidence-interval (CI) of the marginal effect.

There are two common approaches:

  • Delta method
  • Bootstrapping

The Delta method is default in most packages but it is quite crude. Bootstrapping is slow but quite easy to implement yourself. For more background reading on bootstrapping you could start here:

Davison, A.C. and Hinkley, D.V. (1997) Bootstrap Methods and Their Application. Cambridge University Press.

7.1 Example 1: Linear without interactions

# we need to define a function in which we calculate our marginal effects. This is quite easy
# because it is simply copy paste from the above.

bootFunc <- function(data, i) {
    df <- data[i, ]  #these are the df we will calculate our statistics of interest
    m1 <- lm(y_lin ~ sex + age + educ, data = df)  #we first estimate the model on the generated datasets
    c(coef(m1)[2], coef(m1)[3], coef(m1)[4])  #we save our statistics of interest in a vector
}

b <- boot(df, bootFunc, R = 999)  #we feet the boot function our original dataset and our function
b  #names are not very nice. Well, you will manage. 

# boot.ci(b, index=1) #you could ask for the CI summary(margins(m1, vce='bootstrap',
# iterations=999)) #SE via bootstrapping. Please notice the order of the variabels is messed up.
# #you could check with if margins gives the same results. Spoiler alert: YES!
#> 
#> ORDINARY NONPARAMETRIC BOOTSTRAP
#> 
#> 
#> Call:
#> boot(data = df, statistic = bootFunc, R = 999)
#> 
#> 
#> Bootstrap Statistics :
#>     original        bias    std. error
#> t1* 8.831865 -0.0045442056  0.13365235
#> t2* 1.053627 -0.0014882377  0.05228616
#> t3* 1.713989 -0.0006844387  0.04880639


Okay, our estimates of the AME and the SE are very close. Our boot function is a lot faster than the function implemented in margin. Don’t know why.

7.2 Example 2: Logistic model with interactions

ddlogis <- function(X) {
    -(exp(-X)/(1 + exp(-X))^2 - exp(-X) * (2 * (exp(-X) * (1 + exp(-X))))/((1 + exp(-X))^2)^2)
}  #dont forget we need this one as well. 

bootFunc <- function(data, i) {
    df <- data[i, ]  #bootrap datasets

    m2_d <- glm(y_di ~ sex + age + educ + sex:educ + age:educ, family = binomial, data = df)  #estimate model

    # retrieve coefficients
    b_intercept <- coef(m2_d)[1]
    b_sex <- coef(m2_d)[2]
    b_age <- coef(m2_d)[3]
    b_educ <- coef(m2_d)[4]
    b_sex_educ <- coef(m2_d)[5]
    b_age_educ <- coef(m2_d)[6]

    # AME sex
    newdata_sex0 <- df
    newdata_sex0$sex <- "men"  #use the correct factor levels!!
    newdata_sex1 <- df
    newdata_sex1$sex <- "women"  #use the correct factor levels!!
    ME_sex <- plogis(predict(m2_d, newdata = newdata_sex1)) - plogis(predict(m2_d, newdata = newdata_sex0))
    AME_sex <- mean(ME_sex)

    # AME age
    ME_age <- with(df, dlogis(predict(m2_d)) * (b_age + b_age_educ * educ))
    AME_age <- mean(ME_age)

    # AME educ
    ME_educ <- with(df, dlogis(predict(m2_d)) * (b_educ + b_sex_educ * (sex == "women") + b_age_educ *
        age))
    AME_educ <- mean(ME_educ)

    # AME interaction effect sex*educ
    MEsexeduc <- with(df, dlogis(predict(m2_d, dfs1)) * (b_educ + b_sex_educ * 1 + b_age_educ * age) -
        dlogis(predict(m2_d, dfs0)) * (b_educ + b_sex_educ * 0 + b_age_educ * age))
    AMEsexeduc <- mean(MEsexeduc)

    # AME interaction effect age*educ
    MEeducage <- with(df, ddlogis(predict(m2_d)) * (b_educ + b_age_educ * age) * (b_age + b_age_educ *
        educ) + dlogis(predict(m2_d)) * (b_age_educ))
    AMEeducage <- mean(MEeducage)

    c(AME_sex, AME_age, AME_educ, AMEsexeduc, AMEeducage)  #save results
}

b <- boot(df, bootFunc, R = 999)


b <- summary(b)
rownames(b) <- c("AME_sex", "AME_age", "AME_educ", "AME_sex*educ", "AME_educ*age")
b
#> 
#> Number of bootstrap replications R = 999 
#>                original    bootBias    bootSE    bootMed
#> AME_sex       0.2700221 -3.4979e-03 0.0484563  0.2666374
#> AME_age      -0.0607140 -4.4300e-04 0.0137299 -0.0611598
#> AME_educ     -0.0099929 -7.8897e-05 0.0115793 -0.0103263
#> AME_sex*educ -0.0038612  1.0225e-02 0.0233875  0.0051893
#> AME_educ*age -0.0172689 -4.7421e-04 0.0041145 -0.0176206


Copyright © 2022 Jochem Tolsma

LS0tDQp0aXRsZTogIlR1dG9yaWFsIG1hcmdpbmFsIGVmZmVjdHMiDQphdXRob3I6ICdbSm9jaGVtIFRvbHNtYV0oaHR0cHM6Ly93d3cuam9jaGVtdG9sc21hLm5sKSAtIFJhZGJvdWQgVW5pdmVyc2l0eSAvIFVuaXZlcnNpdHkgb2YgR3JvbmluZ2VuLCB0aGUgTmV0aGVybGFuZHMnDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCLCAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6ICB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGhpZ2hsaWdodDogZGVmYXVsdA0KICAgIA0KLS0tDQoNCg0KYGBge3IgZ2xvYmFsc2V0dGluZ3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQodGlkeS5vcHRzPWxpc3Qod2lkdGguY3V0b2ZmPTEwMCksdGlkeT1UUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSxjb21tZW50ID0gIiM+IiwgY2FjaGU9VFJVRSwgZWNobz1UUlVFLCBjbGFzcy5zb3VyY2U9YygidGVzdCIpLCBjbGFzcy5vdXRwdXQ9YygidGVzdDIiKSkNCm9wdGlvbnMod2lkdGggPSAxMDApDQpsaWJyYXJ5KHJnbCkNCnJnbDo6c2V0dXBLbml0cigpDQprbml0cjo6a25pdF9ob29rcyRzZXQod2ViZ2wgPSBob29rX3dlYmdsKQ0KYGBgDQoNCmBgYHtyIGNvbG9yaXplLCBlY2hvPUZBTFNFfQ0KY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHsNCiAgaWYgKGtuaXRyOjppc19sYXRleF9vdXRwdXQoKSkgew0KICAgIHNwcmludGYoIlxcdGV4dGNvbG9yeyVzfXslc30iLCBjb2xvciwgeCkNCiAgfSBlbHNlIGlmIChrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSkgew0KICAgIHNwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwgDQogICAgICAgICAgICB4KQ0KICB9IGVsc2UgeA0KfQ0KDQpgYGANCg0KYGBge3Iga2xpcHB5LCBlY2hvPUZBTFNFLCBpbmNsdWRlPVRSVUV9DQprbGlwcHk6OmtsaXBweShwb3NpdGlvbiA9IGMoJ3RvcCcsICdyaWdodCcpKQ0KI2tsaXBweTo6a2xpcHB5KGNvbG9yID0gJ2RhcmtyZWQnKQ0KI2tsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnKQ0KYGBgDQoNCmBgYHtjc3Mgc3R5bGUgc2V0dGluZ3MsIGVjaG8gPSBGQUxTRX0NCi50ZXN0IHsNCiAgbWF4LWhlaWdodDogMzAwcHg7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIG92ZXJmbG93LXg6IGF1dG87DQogIG1hcmdpbjogMHB4Ow0KfQ0KDQoudGVzdDIgew0KICBtYXgtaGVpZ2h0OiAzMDBweDsNCiAgb3ZlcmZsb3cteTogYXV0bzsNCiAgb3ZlcmZsb3cteDogYXV0bzsNCiAgbWFyZ2luOiAwcHg7DQogICNiYWNrZ3JvdW5kLWNvbG9yOiB3aGl0ZTsNCiAgY29sb3I6IHJnYigyMDEsIDc2LCA3Nik7DQp9DQoNCg0KaDEsIC5oMSwgaDIsIC5oMiwgaDMsIC5oMyB7DQogIG1hcmdpbi10b3A6IDI0cHg7DQp9DQoNCg0KDQpibG9ja3F1b3RlIHsNCiAgICBwYWRkaW5nOiAxMHB4IDIwcHg7DQogICAgbWFyZ2luOiAwIDAgMjBweDsNCiAgICBmb250LXNpemU6IDE0cHg7DQogICAgYm9yZGVyLWxlZnQ6IDVweCBzb2xpZCAjZWVlOw0KICAgIGJhY2tncm91bmQtY29sb3I6IHJnYigyNTUsMjU1LDIyNCwxKTsNCn0NCg0KDQouYnV0dG9uMSB7DQogIGJhY2tncm91bmQtY29sb3I6IGdyZXk7IC8qIFJlZCAqLyANCiAgYm9yZGVyOiAycHggc29saWQgYmxhY2s7DQogIGNvbG9yOiB3aGl0ZTsNCiAgcGFkZGluZzogMTVweCAzMnB4Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIHRleHQtZGVjb3JhdGlvbjogbm9uZTsNCiAgZGlzcGxheTogaW5saW5lLWJsb2NrOw0KICBmb250LXNpemU6IDE2cHg7DQogIG1hcmdpbjogNHB4IDJweDsNCiAgY3Vyc29yOiBwb2ludGVyOw0KICAvKiBib3JkZXItcmFkaXVzOiAxMnB4OyAqLw0KICB3aWR0aDogMTAwJTsNCn0NCg0KLmJ1dHRvbjE6aG92ZXIgew0KICBib3gtc2hhZG93OiAwIDEycHggMTZweCAwIHJnYmEoMCwwLDAsMC4yNCksIDAgMTdweCA1MHB4IDAgcmdiYSgwLDAsMCwwLjE5KTsNCn0NCg0KLmJ1dHRvbjE6YWN0aXZlIHsNCiAgYm9yZGVyOiAycHggc29saWQgcmVkOw0KfQ0KDQoNCg0KYGBgDQoNCg0KDQoNCi0tLQ0KDQoNCiMgIEludHJvDQoNCg0KSW4gdGhpcyB0dXRvcmlhbCBJIHdpbGwgdHJ5IHRvIGV4cGxhaW4gdGhlIGxvZ2ljIGJlaGluZCBtYXJnaW5hbCBlZmZlY3RzLiAgDQoNCkkgd2lsbCBwcm92aWRlIHlvdSB3aXRoIHByYWN0aWNhbCBleGFtcGxlcy4gQW5kIGdpdmUgeW91IHNvbWUgZXhlcmNpc2VzLiANCg0KVG8gY29weSB0aGUgY29kZSBjbGljayB0aGUgYnV0dG9uIGluIHRoZSB1cHBlciByaWdodCBjb3JuZXIgb2YgdGhlIGNvZGUtY2h1bmtzLg0KDQpRdWVzdGlvbnMgY2FuIGJlIGFkZHJlc3NlZCB0byBbSm9jaGVtIFRvbHNtYV0obWFpbHRvOmpvY2hlbS50b2xzbWFAcnUubmwpLg0KDQotLS0gIA0KDQoNCg0KIyMgQ3VzdG9tIGZ1bmN0aW9ucw0KDQotIGBwYWNrYWdlLmNoZWNrYDogQ2hlY2sgaWYgcGFja2FnZXMgYXJlIGluc3RhbGxlZCAoYW5kIGluc3RhbGwgaWYgbm90KSBpbiBSIChbc291cmNlXShodHRwczovL3ZiYWxpZ2EuZ2l0aHViLmlvL3ZlcmlmeS10aGF0LXItcGFja2FnZXMtYXJlLWluc3RhbGxlZC1hbmQtbG9hZGVkLykpLiANCg0KDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJywgZWNobz1UUlVFfQ0KZnBhY2thZ2UuY2hlY2sgPC0gZnVuY3Rpb24ocGFja2FnZXMpIHsNCiAgbGFwcGx5KHBhY2thZ2VzLCBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgaWYgKCFyZXF1aXJlKHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIHsNCiAgICAgIGluc3RhbGwucGFja2FnZXMoeCwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgICAgIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KICAgIH0NCiAgfSkNCn0NCg0KDQpgYGANCg0KLS0tICANCg0KIyMgUGFja2FnZXMNCg0KDQotIGB0aWR5dmVyc2VgOiBpZiB5b3UgY2FuJ3QgYmFzZSB0aGVtLCBqb2luIHRoZW0gIA0KLSBgbWluaUdVSWA6IA0KLSBgcmdsYDogcGxvdHRpbmcgc3R1ZiAgIA0KLSBgRGVyaXZgOiB0byBsZXQgUiBjYWxjdWxhdGUgZGVyaXZhdGl2ZXMgIA0KLSBgbWFyZ2luYDogYW4gUiBwYWNrYWdlIHRoYXQgd2lsbCBjYWxjdWxhdGUgbWFyZ2lucyBmb3IgeW91ICANCi0gYG12dG5vcm1gOiB0byBzaW11bGF0ZSBkYXRhICh3aXRoIGNvcnJlbGF0ZWQgY292YXJpYXRlcykgIA0KLSBgYm9vdGA6IHRvIGNhbGN1bGF0ZSBTRS9DSSBvZiB0aGUgbWFyZ2luYWwgZWZmZWN0cw0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsIGVjaG89VFJVRX0NCnBhY2thZ2VzID0gYygidGlkeXZlcnNlIiwgIm1pbmlHVUkiLCAicmdsIiwgIkRlcml2IiwgIm1hcmdpbnMiLCAibXZ0bm9ybSIsICJib290IiwgIkRBTWlzYyIpDQoNCmZwYWNrYWdlLmNoZWNrKHBhY2thZ2VzKQ0KDQpgYGANCg0KDQojIyBTZXNzaW9uIGluZm8NCg0KVGhpcyBpcyBteSBzZXR1cC4gDQoNCmBgYHtyfQ0Kc2Vzc2lvbkluZm8oKQ0KYGBgDQoNCi0tLSANCg0KIyMgQmFja2dyb3VuZCByZWFkaW5nIGFuZCB0aGVvcnkNCg0KQWkgYW5kIE5vcnRvbiwgMjAwMy4gSW50ZXJhY3Rpb24gdGVybXMgaW4gbG9naXQgYW5kIHByb2JpdCBtb2RlbHMgOiBodHRwczovL2RvaS5vcmcvMTAuMTAxNi9TMDE2NS0xNzY1KDAzKTAwMDMyLTYNCg0KDQojIyMgTWFyZ2luYWwgZWZmZWN0IGRlZmluaXRpb24gIHsjZGVmc30NCg0KV2hhdCBpcyBhIG1hcmdpbmFsIGVmZmVjdD8gSWYgeW91IGdvb2dsZSwgeW91IHdpbGwgZmluZCB0aGlzIGRlZmluaXRpb246IA0KDQo+IE1hcmdpbmFsIGVmZmVjdHMgbWVhc3VyZSB0aGUgaW1wYWN0IHRoYXQgYW4gaW5zdGFudGFuZW91cyBjaGFuZ2UgaW4gb25lIHZhcmlhYmxlIGhhcyBvbiB0aGUgb3V0Y29tZSB2YXJpYWJsZSB3aGlsZSBhbGwgb3RoZXIgdmFyaWFibGVzIGFyZSBoZWxkIGNvbnN0YW50LiANCg0KQnV0IEkgd291bGQgc2F5IHRoZSBkZWZpbml0aW9uIGRlcGVuZHMgb24gdGhlIG1lYXN1cmVtZW50IGxldmVsIG9mIHlvdXIgaW5kZXBlbmRlbnQgdmFyaWFibGUuIA0KDQpGb3IgYSBkaWNob3RvbW91cyBpbmRlcGVuZGVudCB2YXJpYWJsZTogDQoNCiogYHIgY29sb3JpemUoIlRoZSBjaGFuZ2UgaW4geW91ciBkZXBlbmRlbnQgdmFyaWFibGUgaWYgKGNldGVyaXMgcGFyaWJ1cykgeW91ciBpbmRlcGVuZGVudCB2YXJpYWJsZSB4MSBjaGFuZ2VzIGZyb20gMCB0byAxIiwgInJlZCIpYC4gDQoNCk1vcmUgZm9ybWFsbHk6IA0KDQokJCBNRSA9IGYoWHx4MT0xKSAtIGYoWHx4MT0wKSwgJCQgDQoNCndoZXJlICRZID0gZihYKSQgYW5kICRYJCBpcyB5b3VyIHNldCBvZiBjb3ZhcmlhdGVzLiANCg0KRm9yIGEgbWV0cmljIGluZGVwZW5kZW50IHZhcmlhYmxlOiAgDQoNCiogYHIgY29sb3JpemUoIlRoZSBjaGFuZ2UgaW4geW91ciBkZXBlbmRlbnQgdmFyaWFibGUsIFksIGF0IHNvbWUgdmFsdWUsIHgsICBvZiB5b3VyIGluZGVwZW5kZW50IHZhcmlhYmxlIHgxLCBwZXIgKGNldGVyaXMgcGFyaWJ1cykgc21hbGwgY2hhbmdlIG9mIHgxIGF0IHggb2Ygcy4iLCAicmVkIilgDQoNCk1vcmUgZm9ybWFsbHk6IA0KDQokJCBNRSA9IFxmcmFje2YoWHx4MT14ICsgcykgLSBmKFh8eDE9eCAtIHMpfXsyc30sICQkDQp3aGVyZSAkcyQgaXMgYW4gaW5maW5pdGVzaW1hbCBzbWFsbCBudW1iZXIuIA0KDQpUaGlzIGlzIHRodXMgdGhlIHNsb3BlIChvciBtb3JlIHByZWNpc2VseSB0aGUgcGFydGlhbCBkZXJpdmF0aXZlKSBvZiAkZihYKSQgYXQgdGhlIHBvaW50IHdoZXJlICR4MSQgaXMgJHgkOiANCg0KJCQgTUUgPSBmXnsnfShYfHgxPXgpLCAkJA0KYW5kICQkIGZeeyd9KFgpICA9IFxkaXNwbGF5c3R5bGUgXGZyYWN7XHBhcnRpYWwgZihYKX17XHBhcnRpYWwgeDF9ICQkDQoNCg0KV2hlcmUgJFgkIGlzIHlvdXIgc2V0IG9mIGNvdmFyaWF0ZXMsICR4MSQgaXMgb25lIG9mIHlvdXIgY292YXJpYXRlcywgJHgkIGlzIGEgdmFsdWUgb2YgeW91ciBjb3ZhcmlhdGUgJHgxJC4gDQoNCg0KIyMjIEF2ZXJhZ2UgTWFyZ2luYWwgRWZmZWN0IC8gTWFyZ2luYWwgRWZmZWN0IGF0IE1lYW4NCg0KQmVjYXVzZSB5b3UgY2FuIGNhbGN1bGF0ZSB0aGUgTUUgYXQgZGlmZmVyZW50IHZhbHVlcyBvZiB5b3VyIG90aGVyIGNvdmFyaWF0ZXMsIHlvdSBjYW4gY2FsY3VsYXRlIG1hbnkgTUVzLiANCg0KSWYgeW91IGNhbGN1bGF0ZSB0aGUgTUUgZm9yIGFsbCByZXNwb25kZW50cyBpbiB5b3VyIGRhdGFzZXQgKGFuZCB0aGVpciBvYnNlcnZlZCB2YWx1ZXMgb2YgY292YXJpYXRlcykgYW5kIHRha2UgdGhlIG1lYW4sIHlvdSB3aWxsIGVuZCB1cCB3aXRoIHRoZSAqKkFNRSoqLCB0aGUgQXZlcmFnZSBNYXJnaW5hbCBFZmZlY3QuICANCklmIHlvdSBjYWxjdWxhdGUgdGhlIE1FIGZvciBjb3ZhcmlhdGUgdmFsdWVzIHNldCBhdCB0aGVpciBtZWFuLCB5b3Ugd2lsbCBlbmQgdXAgd2l0aCB0aGUgKipNRU0qKiwgdGhlIE1hcmdpbmFsIEVmZmVjdCBhdCBNZWFuIChvciBNTUUsIE1lYW4gTWFyZ2luYWwgRWZmZWN0KS4gDQoNCiMjIyBNYXJnaW5hbCBpbnRlcmFjdGlvbiBlZmZlY3QNCg0KU29tZXRpbWVzIHRoaXMgaXMgaW5jb3JyZWN0bHkgZGVmaW5lZCBhczoNCg0KJCQgXGRpc3BsYXlzdHlsZSBcZnJhY3tccGFydGlhbCBmKFgpfXtccGFydGlhbCAoeDF4Mil9ICQkDQpDb3JyZWN0bHkgd291bGQgYmU6DQoNCiQkIFxkaXNwbGF5c3R5bGUgXGZyYWN7XHBhcnRpYWwgZl4yKFgpfXtccGFydGlhbCh4MSlccGFydGlhbCh4Mil9ICQkDQoNCmByIGNvbG9yaXplICgiSW50ZXJwcmV0YXRpb246IEhvdyBkb2VzIHRoZSBlZmZlY3Qgb2YgJHgxJCBvbiAkWSQgY2hhbmdlIHdpdGggYSBjaGFuZ2UgaW4gJHgyJCwgY29uZGl0aW9uYWwgb24gJFgkLiIsICJyZWQiKWAgDQoNCk5hdHVyYWxseSwgeW91IGhhdmUgYW5hbG9nb3VzIGRlZmluaXRpb25zIGlmICR4MSQgYW5kL29yICR4MiQgYXJlIGRpY2hvdG9tb3VzLiANCg0KLS0tICANCg0KDQoNCiMjIFNpbXVsYXRlZCBkYXRhICANCg0KYGBge3J9DQpzZXQuc2VlZCgyMjc4NjMpICMgc28gd2UgYWxsIGhhdmUgdGhlIHNhbWUgZGF0YQ0KDQojIHdlIHN0YXJ0IHdpdGggc2ltdWxhdGluZyBzb21lIGNvcnJlbGF0ZWQgY292YXJpYXRlcw0Kc2lnbWEgPC0gbWF0cml4KGMoNCwyLDMsIDIsMywxLjUsIDMsMS41LDUpLCBuY29sPTMpDQojc2lnbWENCmRmIDwtIGRhdGEuZnJhbWUocm12bm9ybShuPTUwMCwgbWVhbj1jKDYsOCw3KSwgc2lnbWE9c2lnbWEpKQ0KY29sbmFtZXMoZGYpIDwtIGMoImFnZSIsICJzZXgiLCAiZWR1YyIpDQoNCmRmICU+JSANCiAgbXV0YXRlKHNleCA9IGlmZWxzZShzZXggPiBtZWFuKHNleCkgKyAwLjMqc2Qoc2V4KSwgMCwxKSwgDQogICAgICAgICBzZXggPSBhcy5mYWN0b3Ioc2V4KSwNCiAgICAgICAgIHNleCA9IHJlY29kZV9mYWN0b3Ioc2V4LCAnMCc9Im1lbiIsICcxJz0id29tZW4iKSwgDQogICAgICAgICBhZ2UgPSByb3VuZChhZ2UsIDIpLCANCiAgICAgICAgIGVkdWMgPSByb3VuZChlZHVjLCAyKSkgLT4gZGYNCg0KIyBMZXQgdXMgYWRkIGEgbGluZWFyIGRlcGVuZGVudCB2YXJpYWJsZSB3aXRoIHRoZSBmb2xsb3dpbmcgZm9ybXVsYQ0KZGYgJT4lIA0KICBtdXRhdGUoeV9saW4gPSAxMCArIDUqYXMubnVtZXJpYyhzZXgpICsgMi40KmFnZSArIDIqZWR1YyArIDAuNSphcy5udW1lcmljKHNleCkqZWR1YyArIC0wLjIqYWdlKmVkdWMgKyBybm9ybSg1MDAsIDAsIC41KSkgLT4gZGYNCg0KaGVhZChkZikNCg0KYGBgDQo8YnI+DQoNClRoaXMgd2FzIGhvcGVmdWxseSwgYWxsIHF1aXRlIHN0cmFpZ2h0Zm9yd2FyZC4gQnV0IGxldCB1cyBhbHNvIHNpbXVsYXRlIGEgZGljaG90b21vdXMgZGVwZW5kZW50IHZhcmlhYmxlLiANCg0KDQpUaHVzIHdlIHdvdWxkIGxpa2UgdG8gc2ltdWxhdGUgYSBkaWNob3RvbW91cyBkZXBlbmRlbnQgdmFyaWFibGUgKDAvMSksIGZyb20gYSBCZXJub3VsbGkgZGlzdHJpYnV0aW9uLCB3aXRoIGEgbG9naXQgbGluayBmdW5jdGlvbi4gU28gd2UgY2FuIGlsbHVzdHJhdGUgbWFyZ2luYWwgZWZmZWN0cyB3aXRoIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuIA0KDQpUaGUgbG9nLW9kZHMgb2YgdGhlIHByb2JhYmlsaXR5IG9mIGFuIGV2ZW50IGlzIGEgbGluZWFyIGNvbWJpbmF0aW9uIG9mIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMNCg0KUGxlYXNlIHJlbWVtYmVyLCB0aGUgbG9naXN0aWMgZnVuY3Rpb24gaXM6ICANCg0KJCQgZihYKSA9IFAoWT0xfFgpID0gbihYKSA9IFxwaV94ID0gXGZyYWN7ZV5YfXsoMSArIGVeWCl9ICA9IFxmcmFjezF9IHsoMStlXnstWH0pfSAkJA0KDQoNCldlIGNhbiBlYXNpbHkgY2FsY3VsYXRlICRQKFk9MXxYKSQgaW4gUiBpZiB3ZSBrbm93ICRYJCB2aWEgdGhlIGZ1bmN0aW9uIGBwbG9naXMoWClgLiAgDQoNCjwhLS0tIA0KZG8gaSBuZWVkIHRoaXM6ICBhcHBseSB0aGUgbGluayBmdW5jdGlvbiAoaW52ZXJzZSBsb2dpc3RpYyBmdW5jdGlvbiAvIGxvZ2l0IC8gaW4gUjogcWxvZ2lzKHBpX3gpKTogbG4ocGlfeCAvIDEgLSBwaV94KSA9IFgNCi0tLT4gDQoNCkFuZCBpZiB3ZSBoYXZlICRQKFk9MXxYKSQgd2UgY2FuIHNpbXVsYXRlIGFuIG9ic2VydmVkIHZhcmlhYmxlIGFjY29yZGluZyB0byBhIGJpbm9taWFsIGRpc3RyaWJ1dGlvbjogDQoNCiQkWV97ZGl9IFxzaW0ge1xzZiBCaW5vbX0oMSwgUChZPTF8WCkpJCQNCg0KYGBge3J9DQoNCmRmICU+JQ0KICBtdXRhdGUoeV9kaSA9IHJiaW5vbShuPTUwMCwgc2l6ZT0xLCBwcm9iPXBsb2dpcygtMi40ICsgLjMqYXMubnVtZXJpYyhzZXgpICsgLjQqYWdlICsgLjMqZWR1YyArIC4xKmFzLm51bWVyaWMoc2V4KSplZHVjICsgLTAuMSphZ2UqZWR1YykpKSAtPiBkZg0KDQp0YWJsZShkZiRzZXgsIGRmJHlfZGkpDQpoZWFkKGRmKQ0KYGBgDQoNCi0tLSAgDQoNCg0KIyBMaW5lYXIgbW9kZWwNCg0KRXN0aW1hdGUgdGhlIG1vZGVsICANCg0KYGBge3IgfQ0KDQptMSA8LSBsbSh5X2xpbiB+IHNleCArIGFnZSArIGVkdWMsIGRhdGE9ZGYpDQpzdW1tYXJ5KG0xKQ0KDQpgYGANCg0KPGJyPg0KQW5kIHBsb3QgdGhlIG1vZGVsLg0KYGBge3IgLCB3ZWJnbD1UUlVFfQ0KDQpjb2xvcnMgPC0gaWZlbHNlKGRmJHNleD09Im1lbiIsICJyZWQiLCAiYmx1ZSIpDQoNCndpdGgoZGYsIHBsb3QzZChlZHVjLCBhZ2UsIHByZWRpY3QobTEpLCBjb2w9Y29sb3JzKSkNCmBgYA0KDQoNCiMjIE1hcmdpbmFsIGVmZmVjdHMgb2YgYWdlLiANCg0KV2Uga25vdyB0aGF0ICRZID0gaW50ZXJjZXB0ICsgYl9zZXgqc2V4ICsgYl9hZ2UqYWdlICsgYl9lZHVjKmVkdWMkLiANCldlIGtub3cgdGhhdCBhZ2UgaXMgYSBjb250aW51b3VzIHZhcmlhYmxlLiANClRodXMgd2UgY2FsY3VsYXRlIHRoZSBkZXJpdmF0aXZlIChubyB3b3JyaWVzIHdlIHdpbGwgdXNlIHRoZSBmdW5jdGlvbiBgRGApIG9mIFkgd2l0aCByZXNwZWN0IHRvIGFnZS4gDQoNCmBgYHtyfQ0KTUVfYWdlIDwtIEQoZXhwcmVzc2lvbihpbnRlcmNlcHQgKyBiX3NleCpzZXggKyBiX2FnZSphZ2UgKyBiX2VkdWMqZWR1YyksICJhZ2UiKSANCk1FX2FnZQ0KYGBgDQpGaW5kIHRoZSB2YWx1ZSBvZiB0aGlzIGNvZWZmaWNpZW50OiANCg0KYGBge3J9DQoNCiNjb2VmKG0xKQ0KYl9pbnRlcmNlcHQgPC0gY29lZihtMSlbMV0NCmJfc2V4IDwtIGNvZWYobTEpWzJdDQpiX2FnZSA8LSBjb2VmKG0xKVszXQ0KYl9lZHVjIDwtIGNvZWYobTEpWzRdDQpiX2FnZQ0KYGBgDQo8YnI+IA0KDQo+IEV4ZXJjaXNlOiBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgbWFyZ2luYWwgZWZmZWN0IGZvciBhZ2UgYnkgdXNpbmcgdGhlIG51bWVyaWNhbCBhcHByb2FjaCB0byBjYWxjdWxhdGUgdGhlIGRlcml2YXRpdmUNCg0KDQo8IS0tLSBpbmNsdWRlIGEgYnV0dG9uIGhlcmUgLS0tPiANCg0KPHNjcmlwdD4NCmZ1bmN0aW9uIG15RnVuY3Rpb24oKSB7DQoNCiAgICAgICAgICAgIHZhciBidG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgibXlCdXR0b24iKTsNCiAgICAgICAgICAgIC8vdG8gbWFrZSBpdCBmYW5jaWVyDQogICAgICAgICAgICBpZiAoYnRuLnZhbHVlID09ICJDbGljayB0byBIaWRlIEFuc3dlciIpIHsNCiAgICAgICAgICAgICAgICBidG4udmFsdWUgPSAiU2hvdyBBbnN3ZXIiOw0KICAgICAgICAgICAgICAgIGJ0bi5pbm5lckhUTUwgPSAiU2hvdyBBbnN3ZXIiOw0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgZWxzZSB7DQogICAgICAgICAgICAgICAgYnRuLnZhbHVlID0gIkNsaWNrIHRvIEhpZGUgQW5zd2VyIjsNCiAgICAgICAgICAgICAgICBidG4uaW5uZXJIVE1MID0gIkhpZGUgQW5zd2VyIjsNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIC8vdGhpcyBpcyB3aGF0IHlvdSdyZSBsb29raW5nIGZvcg0KICAgICAgICAgICAgdmFyIHggPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgibXlESVYiKTsNCiAgICAgICAgICAgIGlmICh4LnN0eWxlLmRpc3BsYXkgPT09ICJub25lIikgew0KICAgICAgICAgICAgICAgIHguc3R5bGUuZGlzcGxheSA9ICJibG9jayI7DQogICAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICAgIHguc3R5bGUuZGlzcGxheSA9ICJub25lIjsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgICAgIA0KPC9zY3JpcHQ+DQoNCg0KDQo8YnV0dG9uIGNsYXNzPWJ1dHRvbjEgb25jbGljaz0ibXlGdW5jdGlvbigpIiBpZD0ibXlCdXR0b24iIHZhbHVlPSJDbGljayB0byBTaG93IEFuc3dlciI+U2hvdyBBbnN3ZXI8L2J1dHRvbj4NCg0KPGRpdiBzdHlsZT0iZGlzcGxheTpub25lOyIgaWQ9Im15RElWIj4NCjxicj4NCg0KDQoNCmBgYHtyfQ0KcyA8LSAuMDEgI2RlZmluZSBhIHNtYWxsIHN0ZXANCg0KZGZtaW4gPC0gZGZwbHVzIDwtIGRmICNsZXQgdXMgY29weSB0aGUgZGF0YXNldHMNCmRmcGx1cyRhZ2UgPC0gZGYkYWdlICsgcyAjd2UgYWRkIGEgc21hbGwgc3RlcCB0byBhZ2UgaW4gb25lIG9mIHRoZSBkYXRhc2V0cw0KZGZtaW4kYWdlIDwtIGRmJGFnZSAtIHMgI3dlIHN1YnN0cmFjdCBhIHNtYWxsIHN0ZXAgdG8gYWdlIGluIG9uZSBvZiB0aGUgZGF0YXNldHMNCg0KeXNwbHVzIDwtIHByZWRpY3QobTEsIG5ld2RhdGE9ZGZwbHVzKSAjd2UgY2FsY3VsYXRlIHRoZSBwcmVkaWN0ZWQgdmFsdWVzIGJhc2VkIG9uIG91ciBtb2RlbCBwYXJhbWV0ZXJzIGFuZCBuZXcgdmFsdWVzIG9mIGNvdmFyaWF0ZXMNCnlzbWluIDwtIHByZWRpY3QobTEsIG5ld2RhdGE9ZGZtaW4pICN3ZSBjYWxjdWxhdGUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgYmFzZWQgb24gb3VyIG1vZGVsIHBhcmFtZXRlcnMgYW5kIG5ldyB2YWx1ZXMgb2YgY292YXJpYXRlcw0KDQpNRV9hZ2UgPC0gKHlzcGx1cyAtIHlzbWluKSAvICgyKnMpICNjYWxjdWxhdGUgdGhlIE1Fcw0KDQpBTUVfYWdlIDwtIG1lYW4oTUVfYWdlKSAjY2FsY3VsYXRlIHRoZSBBTUUNCg0KQU1FX2FnZQ0KYGBgDQo8L2Rpdj4NCg0KIyMgTWFyZ2luYWwgZWZmZWN0cyBvZiBzZXguDQoNClJlbWVtYmVyIHRoZSBkZWZpbml0aW9uPyANCg0KYGBge3J9DQpNRV9zZXggPC0gd2l0aChkZiwgKGJfaW50ZXJjZXB0ICsgYl9zZXgqMSArIGJfYWdlKmFnZSArIGJfZWR1YyplZHVjKSAtIChiX2ludGVyY2VwdCArIGJfc2V4KjAgKyBiX2FnZSphZ2UgKyBiX2VkdWMqZWR1YykpDQpNRV9zZXgNCmBgYA0KDQpBIGJpdCBzaG9ydGVyIHdvdWxkIGJlOiANCmBgYHtyfQ0KZGZuZXdzMCA8LSBkZm5ld3MxIDwtIGRmIA0KI2NvbnN0cnVjdCB0d28gZGF0YXNldHMNCmRmbmV3czAkc2V4IDwtICJtZW4iDQpkZm5ld3MxJHNleCA8LSAid29tZW4iDQojbGV0IFIgY2FsY3VsYXRlIHRoZSBwcmVkaWN0ZWQgc2NvcmVzDQptZWFuKHByZWRpY3QobTEsIGRmbmV3czEpIC0gcHJlZGljdChtMSwgZGZuZXdzMCkpDQpgYGANCjxicj4gDQpTbywgbGV0cyBjYWxjdWxhdGUgdGhlIEFNRTogYG1lYW4oTUVfc2V4KWAgPSBgciBtZWFuKE1FX3NleClgLiANCg0KQW5kIHdoYXQgaXMgdGhlIGludGVycHJldGF0aW9uPyBXZWxsLCBsZXQgdXMgcGxvdCB0aGUgdmFsdWVzIG9mIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUsIGlmIHdlIGhvbGQgZWR1Y2F0aW9uIGNvbnN0YW50IGFuZCB2YXJ5IGFnZSwgZm9yIGByIGNvbG9yaXplKCJtZW4iLCAicmVkIilgIGFuZCBgciBjb2xvcml6ZSgid29tZW4iLCAiYmx1ZSIpYC4gDQoNCmBgYHtyfQ0KcGxvdChjKC0xMDoxMCksIGJfaW50ZXJjZXB0ICsgYl9zZXgqMSArIGJfYWdlKmMoLTEwOjEwKSArIGJfZWR1YyptZWFuKGRmJGVkdWMpLCBjb2w9InJlZCIsIHR5cGU9ICJiIiwgeGxhYj0iYWdlIiwgeWxhYj0ieV9saW4iLCB4bGltPWMoLTEwLDEwKSwgeWxpbT1jKDAsIDYwKSkNCnBhcihuZXc9VFJVRSkNCnBsb3QoYygtMTA6MTApLCBiX2ludGVyY2VwdCArIGJfc2V4KjAgKyBiX2FnZSpjKC0xMDoxMCkgKyBiX2VkdWMqbWVhbihkZiRlZHVjKSwgY29sPSJibHVlIiwgdHlwZT0gImIiLCB4bGFiPSIiLCB5bGFiPSIiLCB4bGltPWMoLTEwLDEwKSwgeWxpbT1jKDAsIDYwKSkNCg0KYGBgDQo8YnI+IA0KDQpZb3Ugc2VlLCB0aGF0IGF0IGFsbCB2YWx1ZXMgb2YgYWdlLCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGByIGNvbG9yaXplKCJtZW4iLCAicmVkIilgIGFuZCBgciBjb2xvcml6ZSgid29tZW4iLCAiYmx1ZSIpYCBpcyBBTUUuICANCg0KIyMgY2hlY2sgd2l0aCBSDQoNCmBgYHtyfQ0Kc3VtbWFyeShtYXJnaW5zKG0xKSkgI1NFIHZpYSBkZWx0YW1ldGhvZA0Kc3VtbWFyeShtYXJnaW5zKG0xLCB2Y2U9ImJvb3RzdHJhcCIsIGl0ZXJhdGlvbnM9OTk5KSkgI1NFIHZpYSBib290c3RyYXBwaW5nDQpgYGANCg0KLS0tICANCg0KIyBMb2dpc3RpYyBtb2RlbA0KDQpgYGB7cn0NCm0xX2QgPC0gZ2xtKHlfZGkgfiBzZXggKyBhZ2UgKyBlZHVjLCBkYXRhPWRmLCBmYW1pbHk9Ymlub21pYWwpDQpzdW1tYXJ5KG0xX2QpDQpgYGANCjxicj4NCg0KTGV0IHVzIGhhdmUgYSBsb29rIGF0IHRoZSBtb2RlbDogDQoNCmBgYHtyLCB3ZWJnbD1UUlVFfQ0KDQojd2l0aChkZiwgcGxvdDNkKHNleCwgYWdlLCBmaXR0ZWQudmFsdWVzKG0xX2QpLCBjb2w9Y29sb3JzKSkNCndpdGgoZGYsIHBsb3QzZChlZHVjLCBhZ2UsIGZpdHRlZC52YWx1ZXMobTFfZCksIGNvbD1jb2xvcnMpKQ0KYGBgDQoNCg0KIyMgTWFyZ2luYWwgZWZmZWN0cyBvZiBhZ2UgIA0KDQpQbGVhc2UgcmVtZW1iZXIsIHRoZSBsb2dpc3RpYyBmdW5jdGlvbiBpczogIA0KDQokJCBmKFgpID0gIFxmcmFjezF9IHsoMStlXnstWH0pfSAkJA0KDQoNCkFuZCB0aGUgZGVmaW5pdGlvbiBvZiB0aGUgTUUgZm9yIGEgY29udGludW91cyB2YXJpYWJsZSAkeDEkOiANCg0KDQokJCBNRSA9IGZeeyd9KFh8eDE9eCksICQkDQoNClRodXMgd2UgbmVlZCB0byBmaW5kICRmXnsnfShYKSQgd2l0aCByZXNwZWN0IHRvICR4MSQgKGkuZS4sIGFnZSkuIA0KDQpZb3UgY291bGQgZG8gdGhhdCB5b3Vyc2VsZiBieSB1c2luZyB0aGUgY2hhaW4tcnVsZTogDQoNCiQkXGRpc3BsYXlzdHlsZSBcZnJhY3tccGFydGlhbCBmKFgpfXtccGFydGlhbCAoeDEpfSA9IFxkaXNwbGF5c3R5bGUgXGZyYWN7XHBhcnRpYWwgZihYKX17XHBhcnRpYWwgKFgpfSpcZGlzcGxheXN0eWxlIFxmcmFje1xwYXJ0aWFsIChYKX17XHBhcnRpYWwgKHgxKX0kJA0KDQouLi4gb3IgbGV0IFIgaGVscCB5b3UuIA0KDQpgYGB7cn0NCk1FX2FnZV9mMiA8LSBEKGV4cHJlc3Npb24oMSAvICgxICsgZXhwKC0xKihiX2ludGVyY2VwdCArIGJfc2V4KnNleCArIGJfYWdlKmFnZSArIGJfZWR1YyplZHVjKSkpKSwgImFnZSIpIA0KTUVfYWdlX2YyDQoNCmBgYA0KPGJyPg0KDQpMdWNraWx5IGZvciB1cywgdGhlIGRlcml2YXRpdmUgb2YgYHBsb2dpcyhYKWAgaXMgYGRsb2dpcyhYKWAuIA0KDQpZb3Ugd2FubmEgY2hlY2s/IHNpbXBseSBydW46IGA/ZGxvZ2lzYC4gDQoNCg0KUmV0cmlldmUgdGhlIGNvZWZmaWNpZW50czogDQoNCmBgYHtyfQ0KY29lZihtMV9kKQ0KYl9pbnRlcmNlcHQgPC0gY29lZihtMV9kKVsxXQ0KYl9zZXggPC0gY29lZihtMV9kKVsyXQ0KYl9hZ2UgPC0gY29lZihtMV9kKVszXQ0KYl9lZHVjIDwtIGNvZWYobTFfZClbNF0NCmBgYA0KQW5kIGZpbGwgaW4gdGhlIGZvcm11bGE6IA0KDQpgYGB7cn0NCk1FX2FnZSA8LSB3aXRoKGRmLCBkbG9naXMoYl9pbnRlcmNlcHQgKyBiX3NleCooc2V4PT0id29tZW4iKSsgYl9hZ2UqYWdlICsgYl9lZHVjKmVkdWMpKmJfYWdlKQ0KI3Nob3J0ZXIgd291bGQgYmU6IA0KTUVfYWdlIDwtIGRsb2dpcyhwcmVkaWN0KG0xX2QpKSpiX2FnZQ0KaGVhZChNRV9hZ2UpDQpgYGANClNvLCB5b3Ugbm90aWNlIC0gaG9wZWZ1bGx5IC0gdGhhdCB0aGF0IHRoZSBNRV9hZ2UgaXMgZGlmZmVyZW50IGZvciBlYWNoIHJlc3BvbmRlbnQuIExldCB1cyB0YWtlIHRoZSBtZWFuLiANCg0KDQoqKkFNRV9hZ2UqKiA9IGByIG1lYW4oTUVfYWdlKWAuOg0KDQpgYGB7cn0NCkFNRV9hZ2UgPC0gbWVhbihNRV9hZ2UpDQpBTUVfYWdlDQpgYGANCg0KDQojIyBtYXJnaW5hbCBlZmZlY3Qgc2V4LiANCg0KUmVtZW1iZXIgJE1FX3tzZXh9ID0gZihYfHNleD0xKSAtIGYoWHxzZXg9MCkkLiANCg0KQW5kIG9uY2UgYWdhaW4gc2ltcGx5IGZpbGwgaW4gdGhlIGZvcm11bGEuIA0KDQpgYGB7cn0NCk1FX3NleCA8LSB3aXRoKGRmLCBwbG9naXMoYl9pbnRlcmNlcHQgKyBiX3NleCoxKyBiX2FnZSphZ2UgKyBiX2VkdWMqZWR1YykgLSBwbG9naXMoYl9pbnRlcmNlcHQgKyBiX3NleCowKyBiX2FnZSphZ2UgKyBiX2VkdWMqZWR1YykpDQpBTUVfc2V4IDwtIG1lYW4oTUVfc2V4KQ0KQU1FX3NleA0KYGBgDQoNCm9yLC4uLg0KYGBge3J9DQojbmV3IGRhdGFzZXRzDQpkZnMwIDwtIGRmczEgPC0gZGYNCmRmczAkc2V4IDwtICJtZW4iDQpkZnMxJHNleCA8LSAid29tZW4iDQpNRV9zZXggPC0gcGxvZ2lzKHByZWRpY3QobTFfZCwgZGZzMSkpIC0gcGxvZ2lzKHByZWRpY3QobTFfZCwgZGZzMCkpDQpBTUVfc2V4IDwtIG1lYW4oTUVfc2V4KQ0KQU1FX3NleA0KYGBgDQoNCkxldCB1cyBwbG90IHRoZSBtYXJnaW5hbCBlZmZlY3QgYXQgZGlmZmVyZW50IHZhbHVlcyBvZiB0aGUgb3RoZXIgY292YXJpYXRlcy4gDQoNCmBgYHtyICwgd2ViZ2w9VFJVRX0NCndpdGgoZGYsIHBsb3QzZChlZHVjLCBhZ2UsIE1FX3NleCwgY29sPWNvbG9ycykpDQpgYGANCg0KQU1FX3NleCA9IGByIEFNRV9zZXhgLiANCg0KIyMgQ2hlY2sgd2l0aCBSLiANCg0KYGBge3J9DQojb2theSwgbm93IGxldCdzIHVzZSBSIHRvIGNhbGN1bGF0ZSB0aGUgbWFyZ2luYWwgZWZmZWN0cw0Kc3VtbWFyeShtYXJnaW5zKG0xX2QpKSAjbWFrZSBzdXJlIHRoYXQgdmFyaWFibGUgc2V4IGlzIGRlZmluZWQgYXMgYSBmYWN0b3Igbm90IGFzIGEgbnVtZXJpYyB2YXJpYWJsZS4gDQpgYGANCkV2ZXJ5dGhpbmcgc2VlbXMgdG8gYmUgb2theSEgDQoNCi0tLSAgIA0KDQojIExpbmVhciBtb2RlbCB3aXRoIGludGVyYWN0aW9ucw0KDQpMZXQgdXMgZXN0aW1hdGUgYSBiZXR0ZXIgZml0dGluZyBtb2RlbC4gDQoNCmBgYHtyfQ0KbTIgPC0gbG0oeV9saW4gfiBzZXggKyBhZ2UgKyBlZHVjICsgc2V4OmVkdWMgKyBhZ2U6ZWR1YywgZGF0YT1kZikNCiNub3RlIHRoYXQgd2UgZGVmaW5lIHRoZSBpbnRlcmFjdGlvbiB3aXRoaW4gdGhlIGZvcm11bGEuIFRoaXMgaXMgY3J1Y2lhbCEhDQpzdW1tYXJ5KG0yKQ0KYGBgDQoNClJldHJpZXZlIGNvZWZmaWNpZW50cy4gDQoNCmBgYHtyfQ0KY29lZihtMikNCmJfaW50ZXJjZXB0IDwtIGNvZWYobTIpWzFdDQpiX3NleCA8LSBjb2VmKG0yKVsyXQ0KYl9hZ2UgPC0gY29lZihtMilbM10NCmJfZWR1YyA8LSBjb2VmKG0yKVs0XQ0KYl9zZXhfZWR1YyA8LSBjb2VmKG0yKVs1XQ0KYl9hZ2VfZWR1YyA8LSBjb2VmKG0yKVs2XQ0KYGBgIA0KDQojIyBtYXJnaW5hbCBlZmZlY3Qgc2V4DQoNCkxldCdzIHVzZSBwb3dlciBvZiBSLiANCg0KYGBge3J9DQpNRV9zZXggPC0gcHJlZGljdChtMiwgbmV3ZGF0YT1kZnMxKSAtIHByZWRpY3QobTIsIG5ld2RhdGE9ZGZzMCkNCkFNRV9zZXggPC0gbWVhbihNRV9zZXgpDQpBTUVfc2V4DQpgYGANCg0KDQojIyBtYXJnaW5hbCBlZmZlY3QgYWdlICANCg0KYGBge3J9DQpNRV9hZ2VfZiA8LSBleHByZXNzaW9uKGJfYWdlICsgYl9hZ2VfZWR1YyplZHVjKSAjdGhpcyBpcyB0aGUgZGVyaXZhdGl2ZSB3aXRoIHJlc3BlY3QgdG8gYWdlDQpNRV9hZ2UgPC0gd2l0aChkZiwgZXZhbChNRV9hZ2VfZikpICNEbyB5b3Ugc2VlIEkgZG8gbm90IGZpdCBhbGwgdmFyaWFibGVzIG15c2VsZiBidXQgdXNlIHRoaXMgY29udmVuaWVudCBmdW5jdGlvbiBldmFsKCkNCkFNRV9hZ2UgPC0gbWVhbihNRV9hZ2UpDQpBTUVfYWdlDQpgYGANCg0KIyMgTWFyZ2luYWwgZWZmZWN0IGVkdWNhdGlvbiAgDQoNCmBgYHtyfQ0KTUVfZWR1Y19mIDwtIGV4cHJlc3Npb24oYl9lZHVjICsgYl9hZ2VfZWR1YyphZ2UgKyBiX3NleF9lZHVjKihzZXg9PSJ3b21lbiIpKQ0KTUVfZWR1YyA8LSB3aXRoKGRmLCBldmFsKE1FX2VkdWNfZikpDQpBTUVfZWR1YyA8LSBtZWFuKE1FX2VkdWMpDQpBTUVfZWR1Yw0KYGBgDQoNCiMjIExldCB1cyBjaGVjayB3aXRoIFINCg0KYGBge3J9DQpzdW1tYXJ5KG1hcmdpbnMobTIpKQ0KYGBgDQoNCg0KDQojIyBNYXJnaW5hbCBpbnRlcmFjdGlvbiBlZmZlY3QgZWR1YyphZ2UNCg0KRmlyc3QgY2FsY3VsYXRlIGRlcml2YXRpdmUgdG8gYWdlIGFuZCB0aGVuIHRvIGVkdWMuIA0KDQpgYGB7cn0NCg0KZnggPC0gZXhwcmVzc2lvbihpbnRlcmNlcHQgKyBiX3NleCpzZXggKyBiX2FnZSphZ2UgKyBiX2VkdWMqZWR1YyArIGJfc2V4X2VkdWMqc2V4KmVkdWMgKyBiX2FnZV9lZHVjKmFnZSplZHVjKQ0KZGZ4ZGFnZSA8LSBEKGZ4LCAiYWdlIikNCmQyZnhkYWdlZGVkdWMgPC0gRChkZnhkYWdlLCAiZWR1YyIpDQojY2hlY2sgaWYgdGhlIHZhcmlhYmxlIHNleCBpcyBpbiBvdXIgZXhwcmVzc2lvbiB3ZSB0aGVuIG5lZWQgdG8gYmUgY2FyZWZ1bCBiZWNhdXNlIGl0IGlzIGZhY3Rvci4gYnV0IGl0IGlzIG5vdC4gDQpBTUVfYWdlX2VkdWMgPC0gbWVhbih3aXRoKGRmLCBldmFsKGQyZnhkYWdlZGVkdWMpICkpDQpBTUVfYWdlX2VkdWMNCmBgYA0KQW5kIHRoaXMgaXMgKG9mIGNvdXJzZSkgb3VyIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgZm9yIHRoZSBpbnRlcmFjdGlvbi4gDQoNCiMjIE1hcmdpbmFsIGludGVyYWN0aW9uIGVmZmVjdCBzZXgqZWR1Yw0KDQpGaXJzdCBjYWxjdWxhdGUgZGVyaXZhdGl2ZSB0byBzZXggYW5kIHRoZW4gdG8gZWR1Yy4gDQoNCmBgYHtyfQ0KZnggPC0gZXhwcmVzc2lvbihpbnRlcmNlcHQgKyBiX3NleCpzZXggKyBiX2FnZSphZ2UgKyBiX2VkdWMqZWR1YyArIGJfc2V4X2VkdWMqc2V4KmVkdWMgKyBiX2FnZV9lZHVjKmFnZSplZHVjKQ0KZGZ4ZHNleCA8LSBleHByZXNzaW9uKGludGVyY2VwdCArIGJfc2V4KjEgKyBiX2FnZSphZ2UgKyBiX2VkdWMqZWR1YyArIGJfc2V4X2VkdWMqMSplZHVjICsgYl9hZ2VfZWR1YyphZ2UqZWR1YyAtKGludGVyY2VwdCArIGJfc2V4KjAgKyBiX2FnZSphZ2UgKyBiX2VkdWMqZWR1YyArIGJfc2V4X2VkdWMqMCplZHVjICsgYl9hZ2VfZWR1YyphZ2UqZWR1YykpDQpkZnhkc2V4DQpkZnhkc2V4IDwtIFNpbXBsaWZ5KGRmeGRzZXgpDQpkZnhkc2V4DQpkMmZ4ZHNleGRlZHVjIDwtIEQoZGZ4ZHNleCwgImVkdWMiKQ0KZDJmeGRzZXhkZWR1Yw0KI3RoaXMgaXMganVzdCBhIGNvbnN0YW50LCBuYW1lbHkgb3VyIGludGVyYWN0aW9uIGNvZWZmaWNpZW50DQpldmFsKGQyZnhkc2V4ZGVkdWMpDQpgYGANCg0KLS0tICANCg0KDQojIExvZ2lzdGljIHdpdGggbW9kZWwgaW50ZXJhY3Rpb25zDQoNCkxldCdzIGVzdGltYXRlIGEgYmV0dGVyIG1vZGVsDQoNCmBgYHtyfQ0KbTJfZCA8LSBnbG0oeV9kaSB+IHNleCArIGFnZSArIGVkdWMgKyBzZXg6ZWR1YyArIGFnZTplZHVjLCBmYW1pbHk9Ymlub21pYWwsIGRhdGE9ZGYpDQpzdW1tYXJ5KG0yX2QpDQpgYGANClBsb3R0aW5nIHRoZSBlc3RpbWF0ZWQgbW9kZWwgaXMgdmVyeSBpbnNpZ2h0ZnVsLiANCg0KYGBge3IsIHJlc3VsdHM9ImhvbGQiLCB3ZWJnbD1UUlVFIH0NCndpdGgoZGYsIHBsb3QzZChzZXgsIGFnZSwgZml0dGVkLnZhbHVlcyhtMl9kKSwgY29sPWNvbG9ycykpDQojd2l0aChkZiwgcGxvdDNkKGVkdWMsIGFnZSwgZml0dGVkLnZhbHVlcyhtMl9kKSwgY29sPWNvbG9ycykpDQojd2l0aChkZiwgcGxvdDNkKGVkdWNbc2V4PT0ibWVuIl0sIGFnZVtzZXg9PSJtZW4iXSwgZml0dGVkLnZhbHVlcyhtMl9kKVtzZXg9PSJtZW4iXSwgY29sPWNvbG9yc1tzZXg9PSJtZW4iXSkpDQojd2l0aChkZiwgcGxvdDNkKGVkdWNbc2V4PT0id29tZW4iXSwgYWdlW3NleD09IndvbWVuIl0sIGZpdHRlZC52YWx1ZXMobTJfZClbc2V4PT0id29tZW4iXSwgY29sPWNvbG9yc1tzZXg9PSJ3b21lbiJdKSkNCmBgYA0KDQpSZXRyaWV2ZSBjb2VmZmljaWVudHMuIA0KDQpgYGB7cn0NCmNvZWYobTJfZCkNCmJfaW50ZXJjZXB0IDwtIGNvZWYobTJfZClbMV0NCmJfc2V4IDwtIGNvZWYobTJfZClbMl0NCmJfYWdlIDwtIGNvZWYobTJfZClbM10NCmJfZWR1YyA8LSBjb2VmKG0yX2QpWzRdDQpiX3NleF9lZHVjIDwtIGNvZWYobTJfZClbNV0NCmJfYWdlX2VkdWMgPC0gY29lZihtMl9kKVs2XQ0KYGBgDQoNCiMjIG1hcmdpbmFsIGVmZmVjdCBzZXgNCg0KTGV0J3MgdXNlIHBvd2VyIG9mIFINCg0KYGBge3J9DQpNRV9zZXggPC0gcGxvZ2lzKHByZWRpY3QobTJfZCwgbmV3ZGF0YT1kZnMxKSkgLSBwbG9naXMocHJlZGljdChtMl9kLCBuZXdkYXRhPWRmczApKQ0KQU1FX3NleCA8LSBtZWFuKE1FX3NleCkNCkFNRV9zZXgNCmBgYA0KDQojIyBtYXJnaW5hbCBlZmZlY3QgYWdlICANCg0KYGBge3J9DQpNRV9hZ2UgPC0gd2l0aChkZiwgZGxvZ2lzKHByZWRpY3QobTJfZCkpKihiX2FnZSArIGJfYWdlX2VkdWMqZWR1YykpDQpBTUVfYWdlIDwtIG1lYW4oTUVfYWdlKQ0KQU1FX2FnZQ0KYGBgDQoNCiMjIG1hcmdpbmFsIGVmZmVjdCBlZHVjIA0KDQpgYGB7cn0NCk1FX2VkdWMgPC0gd2l0aChkZiwgZGxvZ2lzKHByZWRpY3QobTJfZCkpKihiX2VkdWMgKyBiX3NleF9lZHVjKihzZXg9PSJ3b21lbiIpICsgYl9hZ2VfZWR1YyphZ2UpKSANCkFNRV9lZHVjIDwtIG1lYW4oTUVfZWR1YykNCkFNRV9lZHVjDQpgYGANCg0KIyMgY2hlY2sgd2l0aCBSDQoNCmBgYHtyfQ0Kc3VtbWFyeShtYXJnaW5zKG0yX2QpKQ0KDQpgYGANCg0KDQojIyBNYXJnaW5hbCBpbnRlcmFjdGlvbiBlZmZlY3RzLg0KDQpgciBjb2xvcml6ZSgiV2FybmluZywgY2FsY3VsYXRpbmcgdGhlIGRlcml2YXRpdmVzIG1heSBiZSBkaWZmaWN1bHQgZGVwZW5kaW5nIG9uIGYoeCksIGFuZCB5b3VyIG1hdGhlbWF0aWNhbCBiYWNrZ3JvdW5kIDstKSIsICJyZWQiKWANCg0KV2UgbmVlZCB0aGUgZG91YmxlIGRlcml2YXRpdmUgb2YgJGYoeCkkLiANCldlIHdpbGwgY2FsbCB0aGlzIGZ1bmN0aW9uIGBkZGxvZ2lzYC4NCg0KYGBge3J9DQojIGYoWCkgPSBQKFk9MXxYKSA9IG4oWCkgPSBleHAoWCkgLyAoMSArIGV4cChYKSkgID0gMSAvICgxK2V4cCgtWCkpDQpuWCA8LSBleHByZXNzaW9uKDEgLyAoMStleHAoLVgpKSkgDQojc2ltaWxhciBhcyBkbG9naXMNCmRuWGR4IDwtIEQoblgsICJYIikNCiNoaWdoZXIgb3JkZXIgZGVyaXZhdGl2ZSANCmQybnhkeGR4IDwtIEQoZG5YZHgsICJYIikNCiNkMm54ZHhkeA0KZGRsb2dpcyA8LSBmdW5jdGlvbihYKXstKGV4cCgtWCkvKDEgKyBleHAoLVgpKV4yIC0gZXhwKC1YKSAqICgyICogKGV4cCgtWCkgKiAoMSArIGV4cCgtWCkpKSkvKCgxICsgICAgIGV4cCgtWCkpXjIpXjIpfQ0KYGBgDQoNCiMjIG1hcmdpbmFsIGludGVyYWN0aW9uIGVmZmVjdCBlZHVjKmFnZQ0KDQpgYGB7cn0NCk1FZWR1Y2FnZSA8LSB3aXRoKGRmLCBkZGxvZ2lzKHByZWRpY3QobTJfZCkpKihiX2VkdWMgKyBiX2FnZV9lZHVjKmFnZSkqKGJfYWdlICsgYl9hZ2VfZWR1YyAqIGVkdWMpICsgZGxvZ2lzKHByZWRpY3QobTJfZCkpKihiX2FnZV9lZHVjKSkNCg0KQU1FZWR1Y2FnZSA8LSBtZWFuKE1FZWR1Y2FnZSkNCkFNRWVkdWNhZ2UNCmBgYA0KIyMjIE51bWVyaWNhbCBhcHByb2FjaA0KDQoNCmBgYHtyfQ0KcyA8LSAuMDAxICNkZWZpbmUgYSBzbWFsbCBzdGVwDQoNCiNkZWZpbmUgZGF0YXNldHMNCmRmcGx1c3BsdXMgPC0gZGZwbHVzbWluIDwtIGRmbWlucGx1cyA8LSBkZm1pbm1pbiA8LSBkZiANCiNhZGQgdGhlIHNtYWxsIHN0ZXAgdG8gdGhlIHZhcmlhYmxlcw0KZGZwbHVzbWluJGFnZSA8LSBkZnBsdXNwbHVzJGFnZSA8LSBkZiRhZ2UgKyBzIA0KZGZtaW5taW4kYWdlIDwtIGRmbWlucGx1cyRhZ2UgPC0gZGYkYWdlIC0gcw0KZGZwbHVzcGx1cyRlZHVjIDwtIGRmbWlucGx1cyRlZHVjIDwtIGRmJGVkdWMgKyBzDQpkZnBsdXNtaW4kZWR1YyA8LSBkZm1pbm1pbiRlZHVjIDwtIGRmJGVkdWMgLSBzDQoNCiNjYWxjdWxhdGUgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzDQpwMTEgPC0gcGxvZ2lzKHByZWRpY3QobTJfZCwgZGZwbHVzcGx1cykpDQpwMTAgPC0gcGxvZ2lzKHByZWRpY3QobTJfZCwgZGZwbHVzbWluKSkNCnAwMSA8LSBwbG9naXMocHJlZGljdChtMl9kLCBkZm1pbnBsdXMpKQ0KcDAwIDwtIHBsb2dpcyhwcmVkaWN0KG0yX2QsIGRmbWlubWluKSkNCg0KI2FuZCB0aGUgbWFyZ2luYWwgZWZmZWN0cy4gYmUgYXdhcmUgb2YgYWxsIHRoZSBicmFja2V0cy4gOi0oDQphbSA8LSAoKChwMTEgLSBwMDEpLygyKnMpKSAtICgocDEwIC0gcDAwKS8oMipzKSkpIC8gKDIqcykNCm1lYW4oYW0pDQpgYGANCg0KDQoNCjxicj4NCg0KPiBFeGVyY2lzZTogU3VwcG9zZSB0aGF0IGFsbCBlc3RpbWF0ZWQgaW50ZXJhY3Rpb24gdGVybXMgYXJlIDAgaW4gbW9kZWwgbTJfZCB3aGF0IHdvdWxkIGJlIHRoZSBtYXJnaW5hbCBlZmZlY3Qgb2YgdGhlIGludGVyYWN0aW9uIGVmZmVjdCBvZiBlZHVjYXRpb24gd2l0aCBhZ2UgYWNjb3JkaW5nIHRvIHRoZSBhYm92ZT8gVGh1cyB3aGF0IHdvdWxkIGJlOiBNRWVkdWNhZ2UuIA0KDQoNCiMjIG1hcmdpbmFsIGludGVyYWN0aW9uIGVmZmVjdCBzZXgqZWR1Yw0KDQpGaXJzdCBjYWxjdWxhdGUgZGVyaXZhdGl2ZSB0byBzZXggYW5kIHRoZW4gdG8gZWR1Yw0KDQpgYGB7cn0NCmRmeGRzZXggPC0gd2l0aChkZiwgcGxvZ2lzKHByZWRpY3QobTJfZCwgZGZzMSkpIC0gcGxvZ2lzKHByZWRpY3QobTJfZCwgZGZzMCkpKQ0KDQpNRXNleGVkdWMgIDwtIHdpdGgoZGYsIGRsb2dpcyhwcmVkaWN0KG0yX2QsIGRmczEpKSooYl9lZHVjICsgYl9zZXhfZWR1YyoxICsgYl9hZ2VfZWR1YyphZ2UpIC0gZGxvZ2lzKHByZWRpY3QobTJfZCwgZGZzMCkpKihiX2VkdWMgKyBiX3NleF9lZHVjKjAgKyBiX2FnZV9lZHVjKmFnZSkpICANCg0KQU1Fc2V4ZWR1YyA8LSBtZWFuKE1Fc2V4ZWR1YykNCkFNRXNleGVkdWMNCg0KYGBgIA0KIyMjIE51bWVyaWNhbCBhcHByb2FjaA0KDQoNCmBgYHtyfQ0KcyA8LSAuMDAxICNkZWZpbmUgYSBzbWFsbCBzdGVwDQoNCiNkZWZpbmUgZGF0YXNldHMNCmRmcGx1c3BsdXMgPC0gZGZwbHVzbWluIDwtIGRmbWlucGx1cyA8LSBkZm1pbm1pbiA8LSBkZiANCiNhZGQgdGhlIHNtYWxsIHN0ZXAgdG8gdGhlIHZhcmlhYmxlcw0KZGZwbHVzbWluJHNleCA8LSBkZnBsdXNwbHVzJHNleCA8LSAid29tZW4iDQpkZm1pbm1pbiRzZXggPC0gZGZtaW5wbHVzJHNleCA8LSAibWVuIg0KZGZwbHVzcGx1cyRlZHVjIDwtIGRmbWlucGx1cyRlZHVjIDwtIGRmJGVkdWMgKyBzDQpkZnBsdXNtaW4kZWR1YyA8LSBkZm1pbm1pbiRlZHVjIDwtIGRmJGVkdWMgLSBzDQoNCiNjYWxjdWxhdGUgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzDQpwMTEgPC0gcGxvZ2lzKHByZWRpY3QobTJfZCwgZGZwbHVzcGx1cykpDQpwMTAgPC0gcGxvZ2lzKHByZWRpY3QobTJfZCwgZGZwbHVzbWluKSkNCnAwMSA8LSBwbG9naXMocHJlZGljdChtMl9kLCBkZm1pbnBsdXMpKQ0KcDAwIDwtIHBsb2dpcyhwcmVkaWN0KG0yX2QsIGRmbWlubWluKSkNCg0KI2FuZCB0aGUgbWFyZ2luYWwgZWZmZWN0cy4gYmUgYXdhcmUgb2YgYWxsIHRoZSBicmFja2V0cy4gOi0oDQphbSA8LSAoKHAxMSAtIHAwMSkgLSAocDEwIC0gcDAwKSkgLyAoMipzKQ0KbWVhbihhbSkNCg0KYGBgDQpMZXQgdXMgcGxvdCB0aGVzZSBpbnRlcmFjdGlvbiBlZmZlY3RzIGFnYWluc3QgdGhlIGZpdHRlZCB2YWx1ZXMuIA0KDQpgYGB7cn0NCnBsb3QoZml0dGVkLnZhbHVlcyhtMl9kKSwgTUVzZXhlZHVjLCBjb2w9Y29sb3JzKQ0KYGBgDQo8YnI+DQpZb3Ugc2VlIHRoYXQgYXQgc29tZSBwb2ludHMgdGhlIGludGVyYWN0aW9uIGlzIG5lZ2F0aXZlIGFuZCBhdCBzb21lIGl0IGlzIHBvc2l0aXZlLg0KDQoNCiMjIENoZWNrIHdpdGggUjIgDQoNCg0KYGBge3J9DQpyZXN1bHRzPC0gaW50RWZmKG0yX2QsIHZhcnM9YygiYWdlIiwgImVkdWMiKSwgZGF0YT1kZikNCm1lYW4ocmVzdWx0cyRieW9icyRpbnQkaW50X2VmZikNCmBgYA0KcXVpdGUgY2xvc2UuIA0KDQpgYGB7cn0NCnJlc3VsdHM8LSBpbnRFZmYobTJfZCwgdmFycz1jKCJzZXgiLCAiZWR1YyIpLCBkYXRhPWRmKQ0KbWVhbihyZXN1bHRzJGJ5b2JzJGludCRpbnRfZWZmKQ0KDQpgYGANClRoaXMgaXMgbm90IHRoZSBzYW1lIGFzIGFib3ZlLCBwcm9iYWJseSBiZWNhdXNlIGZ1bmN0aW9uIGNhbm5vdCBoYW5kbGUgZG91YmxlIGludGVyYWN0aW9ucz8/ICANCg0KDQotLS0gIA0KDQojIFRha2UgaG9tZSBtZXNzYWdlDQoNCiogV2hlbiB5b3UgdXNlIGEgbm9uLWxpbmVhciBtb2RlbCwgdGhlIGludGVycHJldGF0aW9uIG9mIG1hcmdpbmFsIGVmZmVjdHMgaXMgZGlmZmljdWx0LiAgDQoqIE9ubHkgd2hlbiB5b3UgaGF2ZSBhIG5vbi1saW5lYXIgbW9kZWwsIHRoZSB1c2Ugb2YgbWFyZ2luYWwgZWZmZWN0cyBtYWtlcyBzZW5zZS4gIA0KKiBNRU06IEhvdyB1c2VmdWwgYXJlIHRoZXk/ICANCiogVGhlIGFic2VuY2Ugb3IgcHJlc2VuY2Ugb2YgYW4gZXN0aW1hdGVkIGludGVyYWN0aW9uIHRlcm0gZG9lcyBub3Qgc2F5IG11Y2ggYWJvdXQgdGhlIHByZXNlbmNlIG9yIGFic2VuY2Ugb2YgYSAobWFyZ2luYWwpIGludGVyYWN0aW9uIGVmZmVjdCB3aGVuIHlvdXIgbGluayBmdW5jdGlvbiBpcyBub3QgbGluZWFyLiANCiogUiBkb2VzIG5vdCBwcm92aWRlIHBhY2thZ2VzL2Z1bmN0aW9ucyB0aGF0IGNhbGN1bGF0ZSBtYXJnaW5hbCBlZmZlY3RzIGZvciBpbnRlcmFjdGlvbiBlZmZlY3RzIGZvciBub24tbGluZWFyIG1vZGVscy4gDQoNCi0tLSAgDQoNCiMgU0Ugb2YgbWFyZ2luYWwgZWZmZWN0cy4gDQoNCldlbGwsIHlvdSBtYWRlIGl0IHRoaXMgZmFyIGluIHRoZSB0dXRvcmlhbC4gVGhlIGdvYWwgd2FzIHRvIHNob3cgeW91IHRoZSB1bmRlcmx5aW5nIGlkZWEgYW5kIGludGVycHJldGF0aW9uIG9mIG1hcmdpbmFsIGVmZmVjdHMuIE5vdywgdGhhdCB5b3UgdW5kZXJzdGFuZCwgeW91IGNhbiBhcHBseSB0aGUgc3RhbmRhcmQgcGFja2FnZXMgaW4gUiB0byBjYWxjdWxhdGUgdGhlIG1hcmdpbmFsIGVmZmVjdHMgZm9yIHlvdSwgbGlrZSBgbWFyZ2luYC4gDQpUaGVyZSBpcywgaG93ZXZlciwgb25lIGNhdmVhdC4gVGhlc2UgcGFja2FnZXMgYXJlIG5vdCBhYmxlIHRvIGNhbGN1bGF0ZSAoY29ycmVjdCkgbWFyZ2luYWwgZWZmZWN0cyBvZiBpbnRlcmFjdGlvbiBlZmZlY3RzIGFuZCBub3QgYWxsIG5vbi1saW5lYXIgbW9kZWxzIGFyZSBzdXBwb3J0ZWQuIFRoaXMgbWVhbnMgeW91IHNvbWV0aW1lcyBoYXZlIHRvIGNhbGN1bGF0ZSB0aGUgbWFyZ2luYWwgZWZmZWN0cyB5b3Vyc2VsZi4gV2hpY2ggY2FuIGJlIGEgY2hhbGxlbmdlIGJ1dCBhcyB5b3Ugc2VlLCBpcyBkb2FibGUuIEJ1dCBpZiB5b3UgY2FsY3VsYXRlIHRoZSBNRSB5b3Vyc2VsZiB5b3UgbWlnaHQgYWxzbyB3YW50IHRvIGtub3cgdGhlIHN0YW5kYXJkIGVycm9yIChTRSkgb3IgY29uZmlkZW5jZS1pbnRlcnZhbCAoQ0kpIG9mIHRoZSBtYXJnaW5hbCBlZmZlY3QuIA0KDQpUaGVyZSBhcmUgdHdvIGNvbW1vbiBhcHByb2FjaGVzOiAgDQoNCi0gRGVsdGEgbWV0aG9kICANCi0gQm9vdHN0cmFwcGluZyAgDQoNClRoZSBEZWx0YSBtZXRob2QgaXMgZGVmYXVsdCBpbiBtb3N0IHBhY2thZ2VzIGJ1dCBpdCBpcyBxdWl0ZSBjcnVkZS4gDQpCb290c3RyYXBwaW5nIGlzIHNsb3cgYnV0IHF1aXRlIGVhc3kgdG8gaW1wbGVtZW50IHlvdXJzZWxmLiBGb3IgbW9yZSBiYWNrZ3JvdW5kIHJlYWRpbmcgb24gYm9vdHN0cmFwcGluZyB5b3UgY291bGQgc3RhcnQgaGVyZTogDQoNCkRhdmlzb24sIEEuQy4gYW5kIEhpbmtsZXksIEQuVi4gKDE5OTcpICpCb290c3RyYXAgTWV0aG9kcyBhbmQgVGhlaXIgQXBwbGljYXRpb24uKiBDYW1icmlkZ2UgVW5pdmVyc2l0eSBQcmVzcy4NCg0KDQoNCiMjIEV4YW1wbGUgMTogTGluZWFyIHdpdGhvdXQgaW50ZXJhY3Rpb25zDQoNCmBgYHtyLCByZXN1bHRzPSdob2xkJ30NCiN3ZSBuZWVkIHRvIGRlZmluZSBhIGZ1bmN0aW9uIGluIHdoaWNoIHdlIGNhbGN1bGF0ZSBvdXIgbWFyZ2luYWwgZWZmZWN0cy4gVGhpcyBpcyBxdWl0ZSBlYXN5IGJlY2F1c2UgaXQgaXMgc2ltcGx5IGNvcHkgcGFzdGUgZnJvbSB0aGUgYWJvdmUuIA0KDQpib290RnVuYyA8LSBmdW5jdGlvbihkYXRhLCBpKXsNCiAgZGYgPC0gZGF0YVtpLF0gI3RoZXNlIGFyZSB0aGUgZGYgd2Ugd2lsbCBjYWxjdWxhdGUgb3VyIHN0YXRpc3RpY3Mgb2YgaW50ZXJlc3QNCiAgbTEgPC0gbG0oeV9saW4gfiBzZXggKyBhZ2UgKyBlZHVjLCBkYXRhPWRmKSAjd2UgZmlyc3QgZXN0aW1hdGUgdGhlIG1vZGVsIG9uIHRoZSBnZW5lcmF0ZWQgZGF0YXNldHMNCiAgYyhjb2VmKG0xKVsyXSwgY29lZihtMSlbM10sIGNvZWYobTEpWzRdKSAjd2Ugc2F2ZSBvdXIgc3RhdGlzdGljcyBvZiBpbnRlcmVzdCBpbiBhIHZlY3Rvcg0KfQ0KDQpiIDwtIGJvb3QoZGYsIGJvb3RGdW5jLCBSID0gOTk5KSAjd2UgZmVldCB0aGUgYm9vdCBmdW5jdGlvbiBvdXIgb3JpZ2luYWwgZGF0YXNldCBhbmQgb3VyIGZ1bmN0aW9uDQpiICNuYW1lcyBhcmUgbm90IHZlcnkgbmljZS4gV2VsbCwgeW91IHdpbGwgbWFuYWdlLiANCg0KI2Jvb3QuY2koYiwgaW5kZXg9MSkgI3lvdSBjb3VsZCBhc2sgZm9yIHRoZSBDSQ0KI3N1bW1hcnkobWFyZ2lucyhtMSwgdmNlPSJib290c3RyYXAiLCBpdGVyYXRpb25zPTk5OSkpICNTRSB2aWEgYm9vdHN0cmFwcGluZy4gUGxlYXNlIG5vdGljZSB0aGUgb3JkZXIgb2YgdGhlIHZhcmlhYmVscyBpcyBtZXNzZWQgdXAuICN5b3UgY291bGQgY2hlY2sgd2l0aCBpZiBtYXJnaW5zIGdpdmVzIHRoZSBzYW1lIHJlc3VsdHMuIFNwb2lsZXIgYWxlcnQ6IFlFUyENCmBgYA0KPGJyPiANCg0KT2theSwgb3VyIGVzdGltYXRlcyBvZiB0aGUgQU1FIGFuZCB0aGUgU0UgYXJlIHZlcnkgY2xvc2UuIE91ciBib290IGZ1bmN0aW9uIGlzIGEgbG90IGZhc3RlciB0aGFuIHRoZSBmdW5jdGlvbiBpbXBsZW1lbnRlZCBpbiBgbWFyZ2luYC4gRG9uJ3Qga25vdyB3aHkuIA0KDQojIyBFeGFtcGxlIDI6IExvZ2lzdGljIG1vZGVsIHdpdGggaW50ZXJhY3Rpb25zDQpgYGB7cn0NCg0KZGRsb2dpcyA8LSBmdW5jdGlvbihYKXstKGV4cCgtWCkvKDEgKyBleHAoLVgpKV4yIC0gZXhwKC1YKSAqICgyICogKGV4cCgtWCkgKiAoMSArIGV4cCgtWCkpKSkvKCgxICsgICAgIGV4cCgtWCkpXjIpXjIpfSAjZG9udCBmb3JnZXQgd2UgbmVlZCB0aGlzIG9uZSBhcyB3ZWxsLiANCg0KYm9vdEZ1bmMgPC0gZnVuY3Rpb24oZGF0YSwgaSl7DQogIGRmIDwtIGRhdGFbaSxdICNib290cmFwIGRhdGFzZXRzDQogIA0KICBtMl9kIDwtIGdsbSh5X2RpIH4gc2V4ICsgYWdlICsgZWR1YyArIHNleDplZHVjICsgYWdlOmVkdWMsIGZhbWlseT1iaW5vbWlhbCwgZGF0YT1kZikgI2VzdGltYXRlIG1vZGVsDQogIA0KICAjcmV0cmlldmUgY29lZmZpY2llbnRzDQogIGJfaW50ZXJjZXB0IDwtIGNvZWYobTJfZClbMV0NCiAgYl9zZXggPC0gY29lZihtMl9kKVsyXQ0KICBiX2FnZSA8LSBjb2VmKG0yX2QpWzNdDQogIGJfZWR1YyA8LSBjb2VmKG0yX2QpWzRdDQogIGJfc2V4X2VkdWMgPC0gY29lZihtMl9kKVs1XQ0KICBiX2FnZV9lZHVjIDwtIGNvZWYobTJfZClbNl0NCiAgDQogICNBTUUgc2V4DQogIG5ld2RhdGFfc2V4MCA8LSBkZg0KICBuZXdkYXRhX3NleDAkc2V4IDwtICJtZW4iICN1c2UgdGhlIGNvcnJlY3QgZmFjdG9yIGxldmVscyEhDQogIG5ld2RhdGFfc2V4MSA8LSBkZg0KICBuZXdkYXRhX3NleDEkc2V4IDwtICJ3b21lbiIgI3VzZSB0aGUgY29ycmVjdCBmYWN0b3IgbGV2ZWxzISENCiAgTUVfc2V4IDwtIHBsb2dpcyhwcmVkaWN0KG0yX2QsIG5ld2RhdGE9bmV3ZGF0YV9zZXgxKSkgLSBwbG9naXMocHJlZGljdChtMl9kLCBuZXdkYXRhPW5ld2RhdGFfc2V4MCkpDQogIEFNRV9zZXggPC0gbWVhbihNRV9zZXgpDQogIA0KICAjQU1FIGFnZQ0KICBNRV9hZ2UgPC0gd2l0aChkZiwgZGxvZ2lzKHByZWRpY3QobTJfZCkpKihiX2FnZSArIGJfYWdlX2VkdWMqZWR1YykpDQogIEFNRV9hZ2UgPC0gbWVhbihNRV9hZ2UpDQogIA0KICAjQU1FIGVkdWMNCiAgTUVfZWR1YyA8LSB3aXRoKGRmLCBkbG9naXMocHJlZGljdChtMl9kKSkqKGJfZWR1YyArIGJfc2V4X2VkdWMqKHNleD09IndvbWVuIikgKyBiX2FnZV9lZHVjKmFnZSkpIA0KICBBTUVfZWR1YyA8LSBtZWFuKE1FX2VkdWMpDQoNCiAgI0FNRSBpbnRlcmFjdGlvbiBlZmZlY3Qgc2V4KmVkdWMNCiAgTUVzZXhlZHVjICA8LSB3aXRoKGRmLCBkbG9naXMocHJlZGljdChtMl9kLCBkZnMxKSkqKGJfZWR1YyArIGJfc2V4X2VkdWMqMSArIGJfYWdlX2VkdWMqYWdlKSAtIGRsb2dpcyhwcmVkaWN0KG0yX2QsIGRmczApKSooYl9lZHVjICsgYl9zZXhfZWR1YyowICsgYl9hZ2VfZWR1YyphZ2UpKSAgDQogIEFNRXNleGVkdWMgPC0gbWVhbihNRXNleGVkdWMpDQogIA0KICAjQU1FIGludGVyYWN0aW9uIGVmZmVjdCBhZ2UqZWR1Yw0KICBNRWVkdWNhZ2UgPC0gd2l0aChkZiwgZGRsb2dpcyhwcmVkaWN0KG0yX2QpKSooYl9lZHVjICsgYl9hZ2VfZWR1YyphZ2UpKihiX2FnZSArIGJfYWdlX2VkdWMgKiBlZHVjKSArIGRsb2dpcyhwcmVkaWN0KG0yX2QpKSooYl9hZ2VfZWR1YykpDQogIEFNRWVkdWNhZ2UgPC0gbWVhbihNRWVkdWNhZ2UpDQoNCiAgDQogIGMoQU1FX3NleCwgQU1FX2FnZSwgQU1FX2VkdWMsIEFNRXNleGVkdWMsIEFNRWVkdWNhZ2UpICNzYXZlIHJlc3VsdHMNCn0NCg0KYiA8LSBib290KGRmLCBib290RnVuYywgUiA9IDk5OSkNCg0KDQpiPC0gc3VtbWFyeShiKQ0Kcm93bmFtZXMoYikgPC0gYygiQU1FX3NleCIsICJBTUVfYWdlIiwgIkFNRV9lZHVjIiwgIkFNRV9zZXgqZWR1YyIsICJBTUVfZWR1YyphZ2UiKQ0KYg0KYGBgDQoNCg0KDQoNCjxzdHlsZT4NCi5jZW50ZXIgew0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGNvbG9yOiByZWQ7DQp9DQo8L3N0eWxlPg0KDQo8aHI+DQo8YnI+DQo8cCBjbGFzcz0iY2VudGVyIj5Db3B5cmlnaHQgJmNvcHk7IDIwMjIgSm9jaGVtIFRvbHNtYTwvcD4NCg0K