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 .
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)
}
})
}
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)
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
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
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\) .
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).
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.
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
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))
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
Show Answer
s <- 0.01 #define a small step
dfmin <- dfplus <- df #let us copy the datasets
dfplus$age <- df$age + s #we add a small step to age in one of the datasets
dfmin$age <- df$age - s #we substract a small step to age in one of the datasets
ysplus <- predict(m1, newdata = dfplus) #we calculate the predicted values based on our model parameters and new values of covariates
ysmin <- predict(m1, newdata = dfmin) #we calculate the predicted values based on our model parameters and new values of covariates
ME_age <- (ysplus - ysmin)/(2 * s) #calculate the MEs
AME_age <- mean(ME_age) #calculate the AME
AME_age
#> [1] 1.053627
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.
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
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))
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
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.
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!
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]
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
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
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
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
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.
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
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]
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
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
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
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
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)
}
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
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.
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
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.
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??
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.
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.
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.
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