Getting the data
The code chunk below will load all NFL plays from the last 5 years
and clean it to keep only field goal attempts made by players that
attempted at least 25 field goals.
We’ll start just by loading in the NFL plays data for the last 6
seasons and keeping just the field goal attempts
# Loading the most recent 6 seasons
pbp <- load_pbp((year(Sys.Date()) - 5):year(Sys.Date()))
FieldGoals <-
pbp |>
# Only keeping attempts without penalty during the regular season
filter(
field_goal_attempt == 1,
penalty==0,
season_type=="REG"
) |>
mutate(
# Changing roof_type to just indicate if the kick took place in or outdoors
roof_type = if_else(condition = roof %in% c("outdoors", "open"),
true = "outdoors",
false = "indoors"),
# If the field goal was successful or not
fg_result = if_else(condition = field_goal_result == "made",
true = 1,
false = 0)
) |>
# Changing the name to something simpler
rename(
kicker = kicker_player_name,
distance = kick_distance,
kicker_id = kicker_player_id
) |>
# Picking the variables we need
dplyr::select(
team = posteam, kicker, kicker_id, distance,
roof_type, fg_result, season
)
FieldGoals
## # A tibble: 5,862 Ă— 7
## team kicker kicker_id distance roof_type fg_result season
## <chr> <chr> <chr> <dbl> <chr> <dbl> <int>
## 1 SF R.Gould 00-0023252 52 outdoors 1 2020
## 2 ARI Z.Gonzalez 00-0033862 52 outdoors 0 2020
## 3 SF R.Gould 00-0023252 24 outdoors 1 2020
## 4 ARI Z.Gonzalez 00-0033862 56 outdoors 1 2020
## 5 ARI Z.Gonzalez 00-0033862 49 outdoors 0 2020
## 6 DET M.Prater 00-0023853 27 indoors 1 2020
## 7 CHI C.Santos 00-0031203 35 indoors 1 2020
## 8 DET M.Prater 00-0023853 32 indoors 1 2020
## 9 CHI C.Santos 00-0031203 28 indoors 1 2020
## 10 DET M.Prater 00-0023853 44 indoors 1 2020
## # ℹ 5,852 more rows
# Removing the pbp data set since it is large and not used again
rm(pbp)
The seven variables are:
team: The team attempting the field
goal
kicker: The player who attempted the field
goa
distance: How far away the uprights are from the
player attempting the field goal
roof_type: One of two different values:
- outdoors: a stadium that doesn’t have a roof or a stadium with the
roof open
- indoors: a stadium with a closed roof
- fg_result: If the field goal attempt was
successful
- fg_result = 1 -> the attempt was made
- fg_result = 0 -> the attempt was missed
- season: The season the FGA took place during
The code below will count how many field goals each player attempted
and only keep the field goals attempted by kickers with at least 25
attempts:
kicker_counts <-
FieldGoals |>
# Counting the number attempts for each player
count(kicker) |>
# Only keeping players with at least 25 kicks
filter(n >= 25)
kicker_counts |>
arrange(-n)
## # A tibble: 49 Ă— 2
## kicker n
## <chr> <int>
## 1 D.Carlson 204
## 2 C.Boswell 185
## 3 Y.Koo 181
## 4 J.Myers 180
## 5 M.Gay 177
## 6 B.McManus 176
## 7 J.Tucker 176
## 8 K.Fairbairn 175
## 9 N.Folk 175
## 10 J.Slye 172
## # ℹ 39 more rows
There are a total of 51 kickers that attempted at least 25 field
goals during the most recent six seasons. We’ll keep only the field
goals attempted by these kickers to remove any players that were not
accurate enough to be an NFL quality field goal kicker.
fg_df <-
FieldGoals |>
filter(kicker %in% kicker_counts$kicker)
tibble(fg_df)
## # A tibble: 5,535 Ă— 7
## team kicker kicker_id distance roof_type fg_result season
## <chr> <chr> <chr> <dbl> <chr> <dbl> <int>
## 1 SF R.Gould 00-0023252 52 outdoors 1 2020
## 2 ARI Z.Gonzalez 00-0033862 52 outdoors 0 2020
## 3 SF R.Gould 00-0023252 24 outdoors 1 2020
## 4 ARI Z.Gonzalez 00-0033862 56 outdoors 1 2020
## 5 ARI Z.Gonzalez 00-0033862 49 outdoors 0 2020
## 6 DET M.Prater 00-0023853 27 indoors 1 2020
## 7 CHI C.Santos 00-0031203 35 indoors 1 2020
## 8 DET M.Prater 00-0023853 32 indoors 1 2020
## 9 CHI C.Santos 00-0031203 28 indoors 1 2020
## 10 DET M.Prater 00-0023853 44 indoors 1 2020
## # ℹ 5,525 more rows
Our cleaned data has a total of 5,535 kick attempts! They will be
used to try to answer if it actually is easier to make a field goal
indoors.
Logistic Regression
We can estimate the probability a field goal is successful based on
distance and location (indoor/outdoor) using logistic regression.
The logistic regression model is:
\[\log\left(\frac{\pi(x)}{1-\pi(x)}\right)
= \beta_0 + \beta_1x_1 + \beta^I_2 + \beta_{12}^Ix_1\]
where
\(\beta_0\) is the log odds of a
successful attempt if it is 0 yards out and in an outdoor
stadium
\(\beta_1\) is the effect of
distance on the log odds of success for each 1 additional yard
out
\(\beta^I_2\) is the difference
in the log odds of a successful attempt being indoors vs
outdoors
\(\beta_{12}^I\) is the
interaction term between distance and roof type.
Summarized Data
# Summarizing the data first:
fg_sum <-
fg_df |>
# Count for each combo of distance, roof_type, and result
count(distance, roof_type, fg_result) |>
# Calculating the total number of attempts per distance
mutate(
.by = distance,
dist_n = sum(n)
) |>
# Calculating the success rate by distance and roof type
mutate(
.by = c(distance, roof_type),
dist_field_n = sum(n),
success_rate = n/dist_field_n
) |>
# Only keeping rows for the success rate and distance above 20
filter(
fg_result == 1,
dist_n > 20
) |>
dplyr::select(-fg_result, -dist_n, -n) |>
rename(attempts = dist_field_n)
fg_sum
## # A tibble: 80 Ă— 4
## distance roof_type attempts success_rate
## <dbl> <chr> <int> <dbl>
## 1 20 indoors 20 1
## 2 20 outdoors 41 0.976
## 3 21 indoors 23 1
## 4 21 outdoors 63 1
## 5 22 indoors 35 1
## 6 22 outdoors 73 0.973
## 7 23 indoors 50 1
## 8 23 outdoors 85 1
## 9 24 indoors 40 0.975
## 10 24 outdoors 79 1
## # ℹ 70 more rows
Fixed Effect Model
# Fitting a fixed effect logistic regression model with an interaction term
fg_fixed <-
glm(formula = success_rate ~ distance * roof_type,
# Swapping the order of indoor and outdoor so outdoor is the baseline
data = fg_sum |> mutate(roof_type = fct_rev(roof_type)),
weights = attempts,
family = binomial)
# Checking the model estimates using the tidy function and rounding them to 3 digits
library(broom)
tidy(fg_fixed) |>
mutate(across(.cols = where(is.numeric),
.fns = round,
digits = 3)) |>
gt::gt()
| term |
estimate |
std.error |
statistic |
p.value |
| (Intercept) |
6.205 |
0.274 |
22.614 |
0.000 |
| distance |
-0.105 |
0.006 |
-17.663 |
0.000 |
| roof_typeindoors |
-0.094 |
0.490 |
-0.192 |
0.848 |
| distance:roof_typeindoors |
0.008 |
0.010 |
0.743 |
0.457 |
When examining the necessity of each model term, we should always
start by examining the interaction term (we should always maintain a
hierarchical model). From the table above, the p-value for the
interaction term (distance:roof_typeindoors) is 0.457,
indicating the interaction term is not statistically significant and the
effect of distance on the probability a field goal attempt is successful
the same for an indoor or outdoor stadium.
Let’s reparameterize and refit the model without the interaction
term:
\[\log\left(\frac{\pi(x)}{1-\pi(x)}\right)
= \beta_0 + \beta_1x_1 + \beta^I_2\]
# Fitting a fixed effect logistic regression model without an interaction term
fg_fixed2 <-
glm(formula = success_rate ~ distance + roof_type,
data = fg_sum |> mutate(roof_type = fct_rev(roof_type)),
family = binomial,
weights = attempts)
# Checking the model estimates using the tidy function and rounding them to 3 digits
glm_estimates <-
tidy(fg_fixed2,
#exponentiate = T, # convert from log odds to odds
conf.int = T) |> # calculate a 95% confidence interval
# Rounding the numeric columns to 3 decimal places
mutate(
across(
.cols = where(is.numeric),
.fns = round,
digits = 3
)
)
glm_estimates |>
gt::gt()
| term |
estimate |
std.error |
statistic |
p.value |
conf.low |
conf.high |
| (Intercept) |
6.093 |
0.227 |
26.859 |
0.000 |
5.656 |
6.546 |
| distance |
-0.102 |
0.005 |
-20.975 |
0.000 |
-0.112 |
-0.093 |
| roof_typeindoors |
0.264 |
0.088 |
3.001 |
0.003 |
0.093 |
0.438 |
Now both the effect of distance and roof_type are significant!
The odds a kick is successful when attempted indoors is about 30%
higher if it is attempted at the same distance.
Interpreting the confidence interval effect, we are 95% confident
that the odds an indoor kick is successful is 10% to 55% higher compared
to an outdoor kick when attempted at the same distance.
The conclusions we can arrive from our model are only accurate if the
model fits the data reasonably well. Let’s look at the fit
statistics:
fit_stats_logit_fixed <-
glance(fg_fixed2) |>
mutate(p.val = pchisq(q = deviance, df = df.residual, lower.tail = F)) |>
select(nobs, deviance, df.residual, p.val)
gt::gt(fit_stats_logit_fixed)
| nobs |
deviance |
df.residual |
p.val |
| 80 |
94.8 |
77 |
0.08231 |
The p-value is for a hypothesis test with the null hypothesis stating
the model fits the data and the alternative hypothesis
is that the model does not fit the data. With a p-value
of 0.0823, there is some evidence that the model doesn’t fit the data,
but not strong evidence. We’ll continue on the assumption that the model
is a (some-what) reliable generalization about the relationship between
field goal success rate, distance, and roof type.
Let’s graph the results of the logistic regression:
# Adding the estimated probability of success at 59 yards away
pred_df <-
fg_sum |>
mutate(
est_success_rate = predict.glm(object = fg_fixed2,
newdata = fg_sum,
type = "response")
)
# Creating the graph
ggplot(
data = pred_df,
mapping = aes(
x = distance,
color = roof_type
)
) +
geom_point(
mapping = aes(y = success_rate)
) +
# Adding the line of estimated probabilities
geom_line(
mapping = aes(y = est_success_rate)
) +
# Adding indoor and outdoor and the end of their respective line
geom_text(
data = pred_df |> filter(distance == max(distance)),
mapping = aes(
y = est_success_rate,
label = roof_type
),
show.legend = F,
nudge_x = 2.3
) +
theme_bw() +
labs(
title = "Observed and Estimated Field Goal Success",
x = "Kicking Distance",
y = "Success Rate",
color = NULL
) +
scale_y_continuous(labels = scales::percent) +
scale_color_manual(values = c("#624a2e", "#567d46"),
labels = c("Indoors", "Outdoors")) +
scale_x_continuous(breaks = 2:5*10,
labels = paste(2:5*10, "yds")) +
theme(
plot.title = element_text(hjust = 0.5),
legend.position = c(0.1,0.2)
)
Mixed Effects Model
One big assumption most models have is that the rows in our data are
independent once we account for the explanatory variables (distance and
location in our data).
However, there is still one source of association between the rows
(each field goal attempt): the players attempting the field goals!
We can account for the kickers attempting the field goals by
including a random effect in the model:
\[\log\left(\frac{\pi(x)}{1-\pi(x)}\right)
= \beta_0 + \beta_1x_1 + \beta^I_2 + \beta_{12}^Ix_1 + u_i\]
where \(u_i\) is the
random effect of a specific kicker (kicker \(i\)) on the log odds a field goal attempt
is successful.
The difference between a fixed effect (like \(\beta_1\)) and a random effect (like \(u_i\)) is that we assume a probability
distribution for the random effect. Typically, we assume the random
effects are Normally distributed:
\[u_i \sim N(0, \sigma_u)\]
The code chunk below will estimate the mixed-effects
logistic regression model. It’s called a mixed-effects model because it
has both fixed effects and random effect(s).
# Fitting the mixed effects model using glmer in the lme4 package
library(lme4)
fg_mem <-
glmer(
fg_result ~ distance * roof_type + (1|kicker), # (1|kicker) -> random effect
data = fg_df |> mutate(roof_type = fct_rev(roof_type)),
nAGQ = 50, # How many quadrature points to use when estimating u_i
family = binomial,
control = glmerControl(optimizer = "bobyqa")
)
# Viewing the regression estimate table
summary(fg_mem)$coefficients |>
data.frame() |>
round(digits = 3) |>
rownames_to_column(var = "Term") |>
mutate(Estimate = exp(Estimate)) |>
rename(
SE = Std..Error,
`p-value` = Pr...z..,
`test stat` = z.value
) |>
gt::gt()
| Term |
Estimate |
SE |
test stat |
p-value |
| (Intercept) |
585.2271 |
0.271 |
23.471 |
0.000 |
| distance |
0.8967 |
0.006 |
-18.927 |
0.000 |
| roof_typeindoors |
0.8098 |
0.476 |
-0.443 |
0.657 |
| distance:roof_typeindoors |
1.0111 |
0.010 |
1.085 |
0.278 |
Like with the fixed effects model, the interaction term doesn’t
appear to be needed since the p-value is large. It will be removed and
the model reran:
fg_mem2 <-
glmer(
fg_result ~ distance + roof_type + (1|kicker),
data = fg_df |> mutate(roof_type = fct_rev(roof_type)),
nAGQ = 50,
family = binomial,
control = glmerControl(optimizer = "bobyqa")
)
summary(fg_mem2)$coefficients |>
data.frame() |>
round(digits = 3) |>
rownames_to_column(var = "Term") |>
mutate(Estimate = exp(Estimate)) |>
rename(
SE = Std..Error,
`p-value` = Pr...z..,
`test stat` = z.value
) |>
gt::gt()
| Term |
Estimate |
SE |
test stat |
p-value |
| (Intercept) |
499.6960 |
0.225 |
27.584 |
0.000 |
| distance |
0.8994 |
0.005 |
-22.248 |
0.000 |
| roof_typeindoors |
1.3458 |
0.091 |
3.273 |
0.001 |
Like with our fixed effects model, we have strong evidence that the
location has an effect on the probability a field goal attempt is
successful after accounting for the effect of distance and the person
attempting the field goal.
If a kick is attempted at the same distance by the same player, the
odds a kick is successful is about 35.7% higher if it
is attempted in an indoor stadium compared to an outdoor stadium.
Let’s visualize the difference using the mixed effect model. We’ll
start by creating a data frame that has each combo of distance, stadium
type, kicker, and the success probability estimated by the mixed effects
model.
# Creating a data frame that has a row for each combination of distance, roof_type, and kicker to use to estimate the probability a kick is successful:
pred_data <-
expand.grid(
distance = seq(20, 55, by = 0.250), # Distance by quarter yard increments
roof_type = unique(fg_df$roof_type), # Two types of roof
kicker = unique(fg_df$kicker) # kickers included in the model
)
# We can use the predict function to calculate the estimated probabilities
fg_predict <-
data.frame(
pred_data,
est_made = predict(fg_mem,
newdata = pred_data,
type = "response")
)
The graph below shows the median, middle 50%, and middle 90% of model
probabilities that a field goal attempt is successful.
# Calculating the 5 number summary of success for each distance and roof_type combo
avg_probs_dist <-
fg_predict |>
summarize(
.by = c(roof_type, distance),
avg_prob_made = mean(est_made),
min_prob = min(est_made),
Q1_prob = quantile(est_made, prob = 0.25),
Q3_prob = quantile(est_made, prob = 0.75),
max_prob = max(est_made),
quantile95 = quantile(est_made, prob = 0.95),
quantile05 = quantile(est_made, prob = 0.05)
)
# Now we'll plot the results:
ggplot(
data = avg_probs_dist,
mapping = aes(
x = distance,
y = avg_prob_made,
fill = roof_type
)
) +
# Adding the line for the mean estimated probability
geom_line(
mapping = aes(color = roof_type),
linewidth = 1
) +
# Adding a confidence region for Q1 and Q3
geom_ribbon(
mapping = aes(
ymin = Q1_prob,
ymax = Q3_prob
),
alpha = 0.5,
linetype = "dashed"
) +
annotate(
geom = "text",
label = "Shaded regions indicate the estimated\nmiddle 50% and 90% ranges",
x = 26,
y = 0.8
) +
geom_ribbon(
mapping = aes(
ymin = quantile95,
ymax = quantile05
),
alpha = 0.25,
linetype = "dotted"
) +
#facet_wrap(~roof_type) +
# Changing the fill and color pallete to match the previous color choices
scale_color_manual(
values = c("#624a2e", "#567d46"),
aesthetics = c("colour","fill")
) +
# Changing the y-axis to percentages
scale_y_continuous(labels = scales::percent) +
# Changing the x-axis to include yards
scale_x_continuous(
breaks = 2:6*10,
labels = paste(2:6*10, "yds")
) +
# Adding context
labs(
title = "Probability of a Successful Field Goal by Distance for
<span style='color:#624a2e;'>Indoor</span> and
<span style='color:#567d46;'>Outdoor</span> Stadiums",
subtitle = "Seasons 2019-2024, Min 25 Attempts per Kicker, Between 19-58 Yards",
x = "Field Goal Distance",
y = "Model Probability of Success",
caption = "data: nflfastR"
) +
# Moving the legend and centering the subtitle
theme(
legend.position = 'none',
plot.title = ggtext::element_markdown(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
Indoor kicks are more likely to be made, and the further the
distance, the larger the difference in the success rate! The difference
is most notable for attempts 45 yards and further.
Comparing Kickers
While it doesn’t help answer our original question “Is it easier to
attempt a field goal indoors”, we can use the random effects generated
from our mixed effects model to answer “Who is the best kicker over the
last 6 seasons, accounting for distance and stadium type?”
If the random effect is positive, the kicker is better than league
average and if the random effect is negative, the kicker is worse than
league average. The larger in absolute value, the better (or worse) the
player is.
# Let's look at the effectiveness of each player
kicker_eff <-
ranef(fg_mem) |>
data.frame() |>
select(kicker = grp, condval)
kicker_eff <-
left_join(
x = kicker_eff,
y = kicker_counts,
by = "kicker"
)
# The top and bottom 5 kickers
bind_rows(
.id = "type",
"best" = kicker_eff |> slice_max(condval, n = 5),
"worst" = kicker_eff |> filter(n > 50) |> slice_min(condval, n = 5)
) |>
arrange(-condval) |>
mutate(`odds of success` = exp(condval) |> round(2)) |>
select(kicker, attempts = n, `odds of success`) |>
gt::gt()
| kicker |
attempts |
odds of success |
| N.Folk |
175 |
1.45 |
| B.Aubrey |
102 |
1.35 |
| C.Boswell |
185 |
1.32 |
| C.Dicker |
123 |
1.32 |
| E.Pineiro |
119 |
1.28 |
| B.Grupe |
91 |
0.86 |
| C.Ryland |
75 |
0.86 |
| G.Joseph |
121 |
0.84 |
| M.Badgley |
95 |
0.80 |
| J.Moody |
70 |
0.78 |
According to the model, Chris Boswell has been the best kicker in the
league over the last six years, with the odds of a successful field goal
attempt 33% above the league average. Brandon Aubrey, Justin Tucker, and
Nick Folk are tied for second place, all having 27% odds of a successful
field goal attempt above league average.
The worst kicker in the league (with a minimum of 50 attempts) over
the last 6 seasons is Ryan Succop. The odds his field goal attempt is
successful is 20% below the league average. The worst 5 players are as
bad as the best five players are good. This makes sense since NFL
kickers aren’t given many chances, and if they start performing below
the league average, they won’t have a job for very long!
Let’s visualize kickers’ random effects for players who have at least
100 attempts:
# Loading needed packages
pacman::p_load(nflplotR)
gg_kicker_effects <-
kicker_eff |>
# Only keeping rows with at least 100 attempts
filter(n >= 100) |>
left_join(y = FieldGoals |> select(kicker, kicker_id) |> distinct(),
by = "kicker") |>
# blue = above average, red = below average and reordering the kickers
mutate(
bar_col = if_else(condval > 0, "#013369", "#D50A0A"),
kicker = fct_reorder(kicker, condval)
) |>
# Bar chart for kicker effects
ggplot(
mapping = aes(
x = fct_rev(kicker),
y = abs(condval),
fill = bar_col
)
) +
# Adding a line for the bars to sit on at 0
geom_hline(
linewidth = 1,
yintercept = 0
) +
geom_col(
color = "black"
) +
annotate(
geom = "text",
color = "#013369",
label = "More likely to make the attempt",
x = "C.Santos",
y = .3,
fontface = "bold"
) +
annotate(
geom = "text",
color = "#D50A0A",
label = "More likely to miss the attempt",
x = "W.Lutz",
y = 0.3,
fontface = "bold"
) +
scale_fill_identity() +
scale_y_continuous(
limits = c(0, 0.35),
breaks = (0):5/10,
expand = c(0,0),
labels = round(exp(0:5/10), 1)
) +
labs(
x = NULL,
y = "Odd of success",
title = "Odds of success for NFL Kickers",
subtitle = "2019 - 2024 regular season; Minumum 100 attempts"
) +
theme(
axis.text.x = element_text(
angle = 90,
hjust = 0.95,
vjust = 0.15
)
) +
geom_nfl_headshots(
mapping = aes(player_gsis = kicker_id),
width = 0.08,
height = 0.08,
vjust = 0
)
gg_kicker_effects
There are more players with a positive effect on the odds a field
goal is successful. A possible explanation is if a kicker has a below
average chance of success, they are cut from the team and are unlikely
to get more than 100 attempts and wouldn’t be included in the graph.
LS0tDQp0aXRsZTogIkdlbmVyYWxpemVkIE1peGVkIEVmZmVjdHMgTW9kZWxzPyINCmF1dGhvcjogIkNoYXB0ZXIgOSINCmRhdGU6ICJTVEFUIDUzNTAiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICAgIGZpX2NhcHRpb246IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GLCB3YXJuaW5nID0gRiwgbWVzc2FnZT1GfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBULA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDgsDQogICAgICAgICAgICAgICAgICAgICAgI2ZpZy5oZWlnaHQgPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRiwNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRikNCg0KIyBMb2FkaW5nIGluIHRoZSBuZWVkZWQgcGFja2FnZXMNCnBhY21hbjo6cF9sb2FkKHRpZHl2ZXJzZSwgY2FyLCBsbWU0LCBnZWVwYWNrLCBuZmxmYXN0UikNCg0KIyBDaGFuZ2luZyBkZWZhdWx0IHRoZW1lcw0KdGhlbWVfc2V0KHRoZW1lX2J3KCkpDQp0aGVtZV91cGRhdGUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCiMgQ2hhbmdpbmcgdGhlIGRlZmF1bHQgY2hvaWNlIGZvciBob3cgbWFueSBkZWNpbWFsIHBsYWNlcyBhcmUgZGlzcGxheWVkDQpvcHRpb25zKGRpZ2l0cyA9IDQpDQoNCmBgYA0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KSW4gdGhlIE5GTCwgdGVhbXMgY2FuIHNjb3JlIHBvaW50cyBpbiB0aHJlZSB3YXlzOg0KDQoxKSAqKlNhZmV0eSoqOiBUd28gcG9pbnRzDQotIE9jY3VycyBsZXNzIHRoYW4gMSUgb2YgcG9zc2Vzc2lvbnMNCg0KMikgKipGaWVsZCBHb2FsKio6IFRocmVlIHBvaW50cw0KLSBPY2N1cnMgYWJvdXQgNDAlIG9mIHRlYW1zJyBvZmZlbnNpdmUgcG9zc2Vzc2lvbnMNCg0KMykgKipUb3VjaGRvd24qKjogU2l4IHBvaW50cw0KLSBPY2N1cnMgYWJvdXQgMjAlIG9mIHRlYW1zJyBvZmZlbnNpdmUgcG9zc2Vzc2lvbnMNCg0KDQpBIGZpZWxkIGdvYWwgYXR0ZW1wdCBvY2N1cnMgd2hlbiBhIHRlYW0gYXR0ZW1wdHMgdG8ga2ljayB0aGUgYmFsbCB0aHJvdWdoIGdvYWwgcGxhY2VkIGF0IGJvdGggZW5kcyBvZiB0aGUgcGxheWluZyBmaWVsZC4gSWYgdGhlIGJhbGwgcGFzc2VzIGJldHdlZW4gdGhlIHVwcmlnaHRzLCB0aGUgdGVhbSBzY29yZXMgMyBwb2ludHMuIE90aGVyd2lzZSwgdGhlIG90aGVyIHRlYW0ncyBvZmZlbnNlIHN0YXJ0cyB3aGVyZSB0aGUga2lja2luZyB0ZWFtIHdhcyBvbiB0aGUgZmllbGQuIElmIGEgdGVhbSBtaXNzZXMgYSBmaWVsZCBnb2FsLCBub3Qgb25seSBkbyB0aGV5IGxvc2Ugb3V0IG9uIHRocmVlIHBvaW50cywgYnV0IHRoZSBvcHBvbmVudCBoYXMgYSBoaWdoZXIgcHJvYmFiaWxpdHkgb2Ygc2NvcmluZyBwb2ludHMhDQoNClRoZXJlIGFyZSB0d28gdHlwZXMgb2Ygc3RhZGl1bSByb29mcyB0aGF0IHRlYW1zIHBsYXkgaW46IG9wZW4gb3IgY2xvc2VkLiBPbmUgY29tbW9uIGNvbW1lbnQgYWJvdXQgc3RhZGl1bXMgd2l0aCBjbG9zZWQgcm9vZnMgKGNhbGxlZCAqZG9tZXMqKSBpcyB0ZWFtcyBoYXZlIGFuIGVhc2llciB0aW1lIHNjb3Jpbmcgc2luY2UgdGhlIHdlYXRoZXIgZG9lc24ndCBoYXZlIGFueSBpbmZsdWVuY2Ugb24gdGhlIHJlc3VsdCBvZiBhIHBsYXkuIFdlIHdhbnQgdG8gZXhhbWluZSB0aGUgZGF0YSB0byBkZXRlcm1pbmUgaWYgdGhlcmUgaXMgYW55IGxlZ2l0aW1hY3kgb2YgdGhlc2UgdHlwZXMgb2YgY29tbWVudHMgZm9yIGZpZWxkIGdvYWwgYXR0ZW1wdHMuIA0KDQoNCiMjIEdldHRpbmcgdGhlIGRhdGENCg0KVGhlIGNvZGUgY2h1bmsgYmVsb3cgd2lsbCBsb2FkIGFsbCBORkwgcGxheXMgZnJvbSB0aGUgbGFzdCA1IHllYXJzIGFuZCBjbGVhbiBpdCB0byBrZWVwIG9ubHkgZmllbGQgZ29hbCBhdHRlbXB0cyBtYWRlIGJ5IHBsYXllcnMgdGhhdCBhdHRlbXB0ZWQgYXQgbGVhc3QgMjUgZmllbGQgZ29hbHMuDQoNCg0KV2UnbGwgc3RhcnQganVzdCBieSBsb2FkaW5nIGluIHRoZSBORkwgcGxheXMgZGF0YSBmb3IgdGhlIGxhc3QgNiBzZWFzb25zIGFuZCBrZWVwaW5nIGp1c3QgdGhlIGZpZWxkIGdvYWwgYXR0ZW1wdHMNCmBgYHtyIGRhdGEgY3JlYXRpb259DQojIExvYWRpbmcgdGhlIG1vc3QgcmVjZW50IDYgc2Vhc29ucw0KcGJwIDwtIGxvYWRfcGJwKCh5ZWFyKFN5cy5EYXRlKCkpIC0gNSk6eWVhcihTeXMuRGF0ZSgpKSkNCg0KRmllbGRHb2FscyA8LSANCiAgcGJwICB8PiAgDQogIA0KICAjIE9ubHkga2VlcGluZyBhdHRlbXB0cyB3aXRob3V0IHBlbmFsdHkgZHVyaW5nIHRoZSByZWd1bGFyIHNlYXNvbg0KICBmaWx0ZXIoDQogICAgZmllbGRfZ29hbF9hdHRlbXB0ID09IDEsIA0KICAgIHBlbmFsdHk9PTAsIA0KICAgIHNlYXNvbl90eXBlPT0iUkVHIg0KICApIHw+IA0KICANCiAgbXV0YXRlKA0KICAgICMgQ2hhbmdpbmcgcm9vZl90eXBlIHRvIGp1c3QgaW5kaWNhdGUgaWYgdGhlIGtpY2sgdG9vayBwbGFjZSBpbiBvciBvdXRkb29ycw0KICAgIHJvb2ZfdHlwZSA9IGlmX2Vsc2UoY29uZGl0aW9uID0gcm9vZiAlaW4lIGMoIm91dGRvb3JzIiwgIm9wZW4iKSwgDQogICAgICAgICAgICAgICAgICAgICAgICB0cnVlID0gIm91dGRvb3JzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gImluZG9vcnMiKSwNCiAgICANCiAgICAjIElmIHRoZSBmaWVsZCBnb2FsIHdhcyBzdWNjZXNzZnVsIG9yIG5vdCAgIA0KICAgIGZnX3Jlc3VsdCA9IGlmX2Vsc2UoY29uZGl0aW9uID0gZmllbGRfZ29hbF9yZXN1bHQgPT0gIm1hZGUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHRydWUgPSAxLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlID0gMCkNCiAgKSB8PiANCiAgDQogICMgQ2hhbmdpbmcgdGhlIG5hbWUgdG8gc29tZXRoaW5nIHNpbXBsZXIgIA0KICByZW5hbWUoDQogICAga2lja2VyID0ga2lja2VyX3BsYXllcl9uYW1lLCANCiAgICBkaXN0YW5jZSA9IGtpY2tfZGlzdGFuY2UsIA0KICAgIGtpY2tlcl9pZCA9IGtpY2tlcl9wbGF5ZXJfaWQNCiAgKSB8PiANCiAgDQogICMgUGlja2luZyB0aGUgdmFyaWFibGVzIHdlIG5lZWQgIA0KICBkcGx5cjo6c2VsZWN0KA0KICAgIHRlYW0gPSBwb3N0ZWFtLCBraWNrZXIsIGtpY2tlcl9pZCwgZGlzdGFuY2UsIA0KICAgIHJvb2ZfdHlwZSwgZmdfcmVzdWx0LCBzZWFzb24NCiAgKQ0KDQpGaWVsZEdvYWxzDQoNCiMgUmVtb3ZpbmcgdGhlIHBicCBkYXRhIHNldCBzaW5jZSBpdCBpcyBsYXJnZSBhbmQgbm90IHVzZWQgYWdhaW4NCnJtKHBicCkNCg0KYGBgDQoNCg0KVGhlIHNldmVuIHZhcmlhYmxlcyBhcmU6DQoNCjEpICoqdGVhbSoqOiBUaGUgdGVhbSBhdHRlbXB0aW5nIHRoZSBmaWVsZCBnb2FsDQoNCjIpICoqa2lja2VyKio6IFRoZSBwbGF5ZXIgd2hvIGF0dGVtcHRlZCB0aGUgZmllbGQgZ29hDQoNCjMpICoqZGlzdGFuY2UqKjogSG93IGZhciBhd2F5IHRoZSB1cHJpZ2h0cyBhcmUgZnJvbSB0aGUgcGxheWVyIGF0dGVtcHRpbmcgdGhlIGZpZWxkIGdvYWwNCg0KNCkgKipyb29mX3R5cGUqKjogT25lIG9mIHR3byBkaWZmZXJlbnQgdmFsdWVzOg0KLSBvdXRkb29yczogYSBzdGFkaXVtIHRoYXQgZG9lc24ndCBoYXZlIGEgcm9vZiBvciBhIHN0YWRpdW0gd2l0aCB0aGUgcm9vZiBvcGVuDQotIGluZG9vcnM6IGEgc3RhZGl1bSB3aXRoIGEgY2xvc2VkIHJvb2YNCg0KNSkgKipmZ19yZXN1bHQqKjogSWYgdGhlIGZpZWxkIGdvYWwgYXR0ZW1wdCB3YXMgc3VjY2Vzc2Z1bA0KLSBmZ19yZXN1bHQgPSAxIC0+IHRoZSBhdHRlbXB0IHdhcyBtYWRlDQotIGZnX3Jlc3VsdCA9IDAgLT4gdGhlIGF0dGVtcHQgd2FzIG1pc3NlZA0KDQo2KSAqKnNlYXNvbioqOiBUaGUgc2Vhc29uIHRoZSBGR0EgdG9vayBwbGFjZSBkdXJpbmcNCg0KDQpUaGUgY29kZSBiZWxvdyB3aWxsIGNvdW50IGhvdyBtYW55IGZpZWxkIGdvYWxzIGVhY2ggcGxheWVyIGF0dGVtcHRlZCBhbmQgb25seSBrZWVwIHRoZSBmaWVsZCBnb2FscyBhdHRlbXB0ZWQgYnkga2lja2VycyB3aXRoIGF0IGxlYXN0IDI1IGF0dGVtcHRzOg0KDQpgYGB7ciBraWNrZXIgY291bnR9DQpraWNrZXJfY291bnRzIDwtIA0KICBGaWVsZEdvYWxzIHw+IA0KICAjIENvdW50aW5nIHRoZSBudW1iZXIgYXR0ZW1wdHMgZm9yIGVhY2ggcGxheWVyDQogIGNvdW50KGtpY2tlcikgfD4gDQogICMgT25seSBrZWVwaW5nIHBsYXllcnMgd2l0aCBhdCBsZWFzdCAyNSBraWNrcw0KICBmaWx0ZXIobiA+PSAyNSkgDQoNCg0KDQpraWNrZXJfY291bnRzIHw+IA0KICBhcnJhbmdlKC1uKQ0KDQpgYGANCg0KVGhlcmUgYXJlIGEgdG90YWwgb2YgNTEga2lja2VycyB0aGF0IGF0dGVtcHRlZCBhdCBsZWFzdCAyNSBmaWVsZCBnb2FscyBkdXJpbmcgdGhlIG1vc3QgcmVjZW50IHNpeCBzZWFzb25zLiBXZSdsbCBrZWVwIG9ubHkgdGhlIGZpZWxkIGdvYWxzIGF0dGVtcHRlZCBieSB0aGVzZSBraWNrZXJzIHRvIHJlbW92ZSBhbnkgcGxheWVycyB0aGF0IHdlcmUgbm90IGFjY3VyYXRlIGVub3VnaCB0byBiZSBhbiBORkwgcXVhbGl0eSBmaWVsZCBnb2FsIGtpY2tlci4NCg0KDQpgYGB7cn0NCmZnX2RmIDwtIA0KICBGaWVsZEdvYWxzIHw+IA0KICBmaWx0ZXIoa2lja2VyICVpbiUga2lja2VyX2NvdW50cyRraWNrZXIpIA0KDQp0aWJibGUoZmdfZGYpDQpgYGANCg0KT3VyIGNsZWFuZWQgZGF0YSBoYXMgYSB0b3RhbCBvZiBgciBzY2FsZXM6OmNvbW1hKG5yb3coZmdfZGYpKWAga2ljayBhdHRlbXB0cyEgVGhleSB3aWxsIGJlIHVzZWQgdG8gdHJ5IHRvIGFuc3dlciBpZiBpdCBhY3R1YWxseSBpcyBlYXNpZXIgdG8gbWFrZSBhIGZpZWxkIGdvYWwgaW5kb29ycy4NCg0KDQoNCiMjIExvZ2lzdGljIFJlZ3Jlc3Npb24NCg0KV2UgY2FuIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSBhIGZpZWxkIGdvYWwgaXMgc3VjY2Vzc2Z1bCBiYXNlZCBvbiBkaXN0YW5jZSBhbmQgbG9jYXRpb24gKGluZG9vci9vdXRkb29yKSB1c2luZyBsb2dpc3RpYyByZWdyZXNzaW9uLg0KDQpUaGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBpczoNCg0KJCRcbG9nXGxlZnQoXGZyYWN7XHBpKHgpfXsxLVxwaSh4KX1ccmlnaHQpID0gXGJldGFfMCArIFxiZXRhXzF4XzEgKyBcYmV0YV5JXzIgKyBcYmV0YV97MTJ9Xkl4XzEkJA0KDQp3aGVyZSANCg0KLSAkXGJldGFfMCQgaXMgdGhlIGxvZyBvZGRzIG9mIGEgc3VjY2Vzc2Z1bCBhdHRlbXB0IGlmIGl0IGlzIDAgeWFyZHMgb3V0IGFuZCBpbiBhbiBvdXRkb29yIHN0YWRpdW0NCg0KLSAkXGJldGFfMSQgaXMgdGhlIGVmZmVjdCBvZiBkaXN0YW5jZSBvbiB0aGUgbG9nIG9kZHMgb2Ygc3VjY2VzcyBmb3IgZWFjaCAxIGFkZGl0aW9uYWwgeWFyZCBvdXQNCg0KLSAkXGJldGFeSV8yJCBpcyB0aGUgZGlmZmVyZW5jZSBpbiB0aGUgbG9nIG9kZHMgb2YgYSBzdWNjZXNzZnVsIGF0dGVtcHQgYmVpbmcgaW5kb29ycyB2cyBvdXRkb29ycw0KDQotICRcYmV0YV97MTJ9XkkkIGlzIHRoZSBpbnRlcmFjdGlvbiB0ZXJtIGJldHdlZW4gZGlzdGFuY2UgYW5kIHJvb2YgdHlwZS4gDQoNCiMjIyMgU3VtbWFyaXplZCBEYXRhDQoNCg0KYGBge3IgZGlzdGFuY2Vfcm9vZl9ncmFwaH0NCiMgU3VtbWFyaXppbmcgdGhlIGRhdGEgZmlyc3Q6DQpmZ19zdW0gPC0gDQogIGZnX2RmIHw+IA0KICAjIENvdW50IGZvciBlYWNoIGNvbWJvIG9mIGRpc3RhbmNlLCByb29mX3R5cGUsIGFuZCByZXN1bHQNCiAgY291bnQoZGlzdGFuY2UsIHJvb2ZfdHlwZSwgZmdfcmVzdWx0KSB8PiANCiAgDQogICMgQ2FsY3VsYXRpbmcgdGhlIHRvdGFsIG51bWJlciBvZiBhdHRlbXB0cyBwZXIgZGlzdGFuY2UNCiAgbXV0YXRlKA0KICAgIC5ieSA9IGRpc3RhbmNlLA0KICAgIGRpc3RfbiA9IHN1bShuKQ0KICApIHw+IA0KICANCiAgIyBDYWxjdWxhdGluZyB0aGUgc3VjY2VzcyByYXRlIGJ5IGRpc3RhbmNlIGFuZCByb29mIHR5cGUNCiAgbXV0YXRlKA0KICAgIC5ieSA9IGMoZGlzdGFuY2UsIHJvb2ZfdHlwZSksDQogICAgZGlzdF9maWVsZF9uID0gc3VtKG4pLA0KICAgIHN1Y2Nlc3NfcmF0ZSA9IG4vZGlzdF9maWVsZF9uDQogICkgfD4gDQogIA0KICAjIE9ubHkga2VlcGluZyByb3dzIGZvciB0aGUgc3VjY2VzcyByYXRlIGFuZCBkaXN0YW5jZSBhYm92ZSAyMA0KICBmaWx0ZXIoDQogICAgZmdfcmVzdWx0ID09IDEsDQogICAgZGlzdF9uID4gMjANCiAgKSB8PiANCiAgDQogIGRwbHlyOjpzZWxlY3QoLWZnX3Jlc3VsdCwgLWRpc3RfbiwgLW4pIHw+IA0KICANCiAgcmVuYW1lKGF0dGVtcHRzID0gZGlzdF9maWVsZF9uKQ0KDQpmZ19zdW0NCmBgYA0KDQoNCiMjIyBGaXhlZCBFZmZlY3QgTW9kZWwNCg0KYGBge3IgZml4ZWQgbG9naXN0aWMgcmVncmVzc2lvbn0NCiMgRml0dGluZyBhIGZpeGVkIGVmZmVjdCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHdpdGggYW4gaW50ZXJhY3Rpb24gdGVybQ0KZmdfZml4ZWQgPC0gDQogIGdsbShmb3JtdWxhID0gc3VjY2Vzc19yYXRlIH4gZGlzdGFuY2UgKiByb29mX3R5cGUsDQogICAgICAjIFN3YXBwaW5nIHRoZSBvcmRlciBvZiBpbmRvb3IgYW5kIG91dGRvb3Igc28gb3V0ZG9vciBpcyB0aGUgYmFzZWxpbmUNCiAgICAgIGRhdGEgPSBmZ19zdW0gfD4gbXV0YXRlKHJvb2ZfdHlwZSA9IGZjdF9yZXYocm9vZl90eXBlKSksDQogICAgICB3ZWlnaHRzID0gYXR0ZW1wdHMsDQogICAgICBmYW1pbHkgPSBiaW5vbWlhbCkNCg0KDQojIENoZWNraW5nIHRoZSBtb2RlbCBlc3RpbWF0ZXMgdXNpbmcgdGhlIHRpZHkgZnVuY3Rpb24gYW5kIHJvdW5kaW5nIHRoZW0gdG8gMyBkaWdpdHMNCmxpYnJhcnkoYnJvb20pDQp0aWR5KGZnX2ZpeGVkKSB8PiANCiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHdoZXJlKGlzLm51bWVyaWMpLA0KICAgICAgICAgICAgICAgIC5mbnMgPSByb3VuZCwNCiAgICAgICAgICAgICAgICBkaWdpdHMgPSAzKSkgfD4gDQogIGd0OjpndCgpDQpgYGANCg0KDQpXaGVuIGV4YW1pbmluZyB0aGUgbmVjZXNzaXR5IG9mIGVhY2ggbW9kZWwgdGVybSwgd2Ugc2hvdWxkIGFsd2F5cyBzdGFydCBieSBleGFtaW5pbmcgdGhlIGludGVyYWN0aW9uIHRlcm0gKHdlIHNob3VsZCBhbHdheXMgbWFpbnRhaW4gYSBoaWVyYXJjaGljYWwgbW9kZWwpLiBGcm9tIHRoZSB0YWJsZSBhYm92ZSwgdGhlIHAtdmFsdWUgZm9yIHRoZSBpbnRlcmFjdGlvbiB0ZXJtIChgZGlzdGFuY2U6cm9vZl90eXBlaW5kb29yc2ApIGlzIGByIHJvdW5kKHRpZHkoZmdfZml4ZWQpWzQsICdwLnZhbHVlJ10sIDMpYCwgaW5kaWNhdGluZyB0aGUgaW50ZXJhY3Rpb24gdGVybSBpcyBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBhbmQgdGhlIGVmZmVjdCBvZiBkaXN0YW5jZSBvbiB0aGUgcHJvYmFiaWxpdHkgYSBmaWVsZCBnb2FsIGF0dGVtcHQgaXMgc3VjY2Vzc2Z1bCB0aGUgc2FtZSBmb3IgYW4gaW5kb29yIG9yIG91dGRvb3Igc3RhZGl1bS4gDQoNCkxldCdzIHJlcGFyYW1ldGVyaXplIGFuZCByZWZpdCB0aGUgbW9kZWwgd2l0aG91dCB0aGUgaW50ZXJhY3Rpb24gdGVybToNCg0KJCRcbG9nXGxlZnQoXGZyYWN7XHBpKHgpfXsxLVxwaSh4KX1ccmlnaHQpID0gXGJldGFfMCArIFxiZXRhXzF4XzEgKyBcYmV0YV5JXzIkJA0KDQpgYGB7ciBmaXhlZCBsb2dpc3RpYyByZWdyZXNzaW9uIG5vIGludGVyYWN0aW9ufQ0KIyBGaXR0aW5nIGEgZml4ZWQgZWZmZWN0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aG91dCBhbiBpbnRlcmFjdGlvbiB0ZXJtDQpmZ19maXhlZDIgPC0gDQogIGdsbShmb3JtdWxhID0gc3VjY2Vzc19yYXRlIH4gZGlzdGFuY2UgKyByb29mX3R5cGUsDQogICAgICBkYXRhID0gZmdfc3VtIHw+IG11dGF0ZShyb29mX3R5cGUgPSBmY3RfcmV2KHJvb2ZfdHlwZSkpLA0KICAgICAgZmFtaWx5ID0gYmlub21pYWwsDQogICAgICB3ZWlnaHRzID0gYXR0ZW1wdHMpDQoNCg0KIyBDaGVja2luZyB0aGUgbW9kZWwgZXN0aW1hdGVzIHVzaW5nIHRoZSB0aWR5IGZ1bmN0aW9uIGFuZCByb3VuZGluZyB0aGVtIHRvIDMgZGlnaXRzDQpnbG1fZXN0aW1hdGVzIDwtIA0KICB0aWR5KGZnX2ZpeGVkMiwgDQogICAgICAgI2V4cG9uZW50aWF0ZSA9IFQsICMgY29udmVydCBmcm9tIGxvZyBvZGRzIHRvIG9kZHMNCiAgICAgICBjb25mLmludCA9IFQpIHw+ICAjIGNhbGN1bGF0ZSBhIDk1JSBjb25maWRlbmNlIGludGVydmFsDQogICMgUm91bmRpbmcgdGhlIG51bWVyaWMgY29sdW1ucyB0byAzIGRlY2ltYWwgcGxhY2VzDQogIG11dGF0ZSgNCiAgICBhY3Jvc3MoDQogICAgICAuY29scyA9IHdoZXJlKGlzLm51bWVyaWMpLA0KICAgICAgLmZucyA9IHJvdW5kLA0KICAgICAgZGlnaXRzID0gMw0KICAgICkNCiAgKSANCg0KDQpnbG1fZXN0aW1hdGVzIHw+IA0KICBndDo6Z3QoKQ0KYGBgDQoNCk5vdyBib3RoIHRoZSBlZmZlY3Qgb2YgZGlzdGFuY2UgYW5kIHJvb2ZfdHlwZSBhcmUgc2lnbmlmaWNhbnQhDQoNClRoZSBvZGRzIGEga2ljayBpcyBzdWNjZXNzZnVsIHdoZW4gYXR0ZW1wdGVkIGluZG9vcnMgaXMgYWJvdXQgYHIgcGFzdGUwKHJvdW5kKGV4cChnbG1fZXN0aW1hdGVzWzMsMl0pIC0gMSwgMikqMTAwLCAiJSIpYCBoaWdoZXIgaWYgaXQgaXMgYXR0ZW1wdGVkIGF0IHRoZSBzYW1lIGRpc3RhbmNlLg0KDQpJbnRlcnByZXRpbmcgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZWZmZWN0LCB3ZSBhcmUgOTUlIGNvbmZpZGVudCB0aGF0IHRoZSBvZGRzIGFuIGluZG9vciBraWNrIGlzIHN1Y2Nlc3NmdWwgaXMgYHIgcGFzdGUwKHJvdW5kKGV4cChnbG1fZXN0aW1hdGVzWzMsNl0pIC0gMSwgMikqMTAwLCAiJSIpYCB0byBgciBwYXN0ZTAocm91bmQoZXhwKGdsbV9lc3RpbWF0ZXNbMyw3XSkgLSAxLCAyKSoxMDAsICIlIilgIGhpZ2hlciBjb21wYXJlZCB0byBhbiBvdXRkb29yIGtpY2sgd2hlbiBhdHRlbXB0ZWQgYXQgdGhlIHNhbWUgZGlzdGFuY2UuDQoNClRoZSBjb25jbHVzaW9ucyB3ZSBjYW4gYXJyaXZlIGZyb20gb3VyIG1vZGVsIGFyZSBvbmx5IGFjY3VyYXRlIGlmIHRoZSBtb2RlbCBmaXRzIHRoZSBkYXRhIHJlYXNvbmFibHkgd2VsbC4gTGV0J3MgbG9vayBhdCB0aGUgZml0IHN0YXRpc3RpY3M6DQoNCmBgYHtyfQ0KZml0X3N0YXRzX2xvZ2l0X2ZpeGVkIDwtIA0KICBnbGFuY2UoZmdfZml4ZWQyKSB8PiANCiAgbXV0YXRlKHAudmFsID0gcGNoaXNxKHEgPSBkZXZpYW5jZSwgZGYgPSBkZi5yZXNpZHVhbCwgbG93ZXIudGFpbCA9IEYpKSB8PiANCiAgc2VsZWN0KG5vYnMsIGRldmlhbmNlLCBkZi5yZXNpZHVhbCwgcC52YWwpDQoNCmd0OjpndChmaXRfc3RhdHNfbG9naXRfZml4ZWQpDQpgYGANCg0KVGhlIHAtdmFsdWUgaXMgZm9yIGEgaHlwb3RoZXNpcyB0ZXN0IHdpdGggdGhlIG51bGwgaHlwb3RoZXNpcyBzdGF0aW5nICoqdGhlIG1vZGVsIGZpdHMgdGhlIGRhdGEqKiBhbmQgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgaXMgdGhhdCAqKnRoZSBtb2RlbCBkb2VzIG5vdCBmaXQgdGhlIGRhdGEqKi4gV2l0aCBhIHAtdmFsdWUgb2YgYHIgZml0X3N0YXRzX2xvZ2l0X2ZpeGVkWzEsICdwLnZhbCddYCwgdGhlcmUgaXMgc29tZSBldmlkZW5jZSB0aGF0IHRoZSBtb2RlbCBkb2Vzbid0IGZpdCB0aGUgZGF0YSwgYnV0IG5vdCBzdHJvbmcgZXZpZGVuY2UuIFdlJ2xsIGNvbnRpbnVlIG9uIHRoZSBhc3N1bXB0aW9uIHRoYXQgdGhlIG1vZGVsIGlzIGEgKHNvbWUtd2hhdCkgcmVsaWFibGUgZ2VuZXJhbGl6YXRpb24gYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGZpZWxkIGdvYWwgc3VjY2VzcyByYXRlLCBkaXN0YW5jZSwgYW5kIHJvb2YgdHlwZS4NCg0KDQpMZXQncyBncmFwaCB0aGUgcmVzdWx0cyBvZiB0aGUgbG9naXN0aWMgcmVncmVzc2lvbjoNCg0KYGBge3IgZml4ZWQgZWZmZWN0IG1vZGVsIGdyYXBoLCBmaWcuY2FwID0gIkxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWw6ICRcXGxvZ1xcbGVmdChcXGZyYWN7cH17MS1wfVxccmlnaHQpID0gXFxiZXRhXzAgKyBcXGJldGFfMXhfMSArIFxcYmV0YV5JXzIkIn0NCiMgQWRkaW5nIHRoZSBlc3RpbWF0ZWQgcHJvYmFiaWxpdHkgb2Ygc3VjY2VzcyBhdCA1OSB5YXJkcyBhd2F5DQpwcmVkX2RmIDwtIA0KICBmZ19zdW0gfD4gDQogIG11dGF0ZSgNCiAgICBlc3Rfc3VjY2Vzc19yYXRlID0gcHJlZGljdC5nbG0ob2JqZWN0ID0gZmdfZml4ZWQyLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGZnX3N1bSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KICApDQoNCiMgQ3JlYXRpbmcgdGhlIGdyYXBoDQpnZ3Bsb3QoDQogIGRhdGEgPSBwcmVkX2RmLA0KICBtYXBwaW5nID0gYWVzKA0KICAgIHggPSBkaXN0YW5jZSwNCiAgICANCiAgICBjb2xvciA9IHJvb2ZfdHlwZQ0KICApDQopICsNCiAgDQogIGdlb21fcG9pbnQoDQogICAgbWFwcGluZyA9IGFlcyh5ID0gc3VjY2Vzc19yYXRlKQ0KICApICsgDQogIA0KICAjIEFkZGluZyB0aGUgbGluZSBvZiBlc3RpbWF0ZWQgcHJvYmFiaWxpdGllcw0KICBnZW9tX2xpbmUoDQogICAgbWFwcGluZyA9IGFlcyh5ID0gZXN0X3N1Y2Nlc3NfcmF0ZSkNCiAgKSArDQogIA0KICAjIEFkZGluZyBpbmRvb3IgYW5kIG91dGRvb3IgYW5kIHRoZSBlbmQgb2YgdGhlaXIgcmVzcGVjdGl2ZSBsaW5lDQogIGdlb21fdGV4dCgNCiAgICBkYXRhID0gcHJlZF9kZiB8PiBmaWx0ZXIoZGlzdGFuY2UgPT0gbWF4KGRpc3RhbmNlKSksDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHkgPSBlc3Rfc3VjY2Vzc19yYXRlLA0KICAgICAgbGFiZWwgPSByb29mX3R5cGUNCiAgICApLA0KICAgIHNob3cubGVnZW5kID0gRiwNCiAgICBudWRnZV94ID0gMi4zDQogICkgKw0KICANCiAgdGhlbWVfYncoKSArIA0KICANCiAgbGFicygNCiAgICB0aXRsZSA9ICJPYnNlcnZlZCBhbmQgRXN0aW1hdGVkIEZpZWxkIEdvYWwgU3VjY2VzcyIsDQogICAgeCA9ICJLaWNraW5nIERpc3RhbmNlIiwNCiAgICB5ID0gIlN1Y2Nlc3MgUmF0ZSIsDQogICAgY29sb3IgPSBOVUxMDQogICkgKyANCiAgDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsgDQogIA0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiIzYyNGEyZSIsICIjNTY3ZDQ2IiksDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJJbmRvb3JzIiwgIk91dGRvb3JzIikpICsNCiAgDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAyOjUqMTAsDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZSgyOjUqMTAsICJ5ZHMiKSkgKw0KICANCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjEsMC4yKQ0KICApDQpgYGANCg0KDQojIyMgTWl4ZWQgRWZmZWN0cyBNb2RlbA0KDQpPbmUgYmlnIGFzc3VtcHRpb24gbW9zdCBtb2RlbHMgaGF2ZSBpcyB0aGF0IHRoZSByb3dzIGluIG91ciBkYXRhIGFyZSBpbmRlcGVuZGVudCBvbmNlIHdlIGFjY291bnQgZm9yIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgKGRpc3RhbmNlIGFuZCBsb2NhdGlvbiBpbiBvdXIgZGF0YSkuIA0KDQpIb3dldmVyLCB0aGVyZSBpcyBzdGlsbCBvbmUgc291cmNlIG9mIGFzc29jaWF0aW9uIGJldHdlZW4gdGhlIHJvd3MgKGVhY2ggZmllbGQgZ29hbCBhdHRlbXB0KTogdGhlIHBsYXllcnMgYXR0ZW1wdGluZyB0aGUgZmllbGQgZ29hbHMhDQoNCldlIGNhbiBhY2NvdW50IGZvciB0aGUga2lja2VycyBhdHRlbXB0aW5nIHRoZSBmaWVsZCBnb2FscyBieSBpbmNsdWRpbmcgYSByYW5kb20gZWZmZWN0IGluIHRoZSBtb2RlbDoNCg0KDQokJFxsb2dcbGVmdChcZnJhY3tccGkoeCl9ezEtXHBpKHgpfVxyaWdodCkgPSBcYmV0YV8wICsgXGJldGFfMXhfMSArIFxiZXRhXklfMiArIFxiZXRhX3sxMn1eSXhfMSArIHVfaSQkDQoNCndoZXJlICR1X2kkIGlzIHRoZSAqKnJhbmRvbSoqIGVmZmVjdCBvZiBhIHNwZWNpZmljIGtpY2tlciAoa2lja2VyICRpJCkgb24gdGhlIGxvZyBvZGRzIGEgZmllbGQgZ29hbCBhdHRlbXB0IGlzIHN1Y2Nlc3NmdWwuDQoNClRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYSBmaXhlZCBlZmZlY3QgKGxpa2UgJFxiZXRhXzEkKSBhbmQgYSByYW5kb20gZWZmZWN0IChsaWtlICR1X2kkKSBpcyB0aGF0IHdlIGFzc3VtZSBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBmb3IgdGhlIHJhbmRvbSBlZmZlY3QuIFR5cGljYWxseSwgd2UgYXNzdW1lIHRoZSByYW5kb20gZWZmZWN0cyBhcmUgTm9ybWFsbHkgZGlzdHJpYnV0ZWQ6DQoNCiQkdV9pIFxzaW0gTigwLCBcc2lnbWFfdSkkJA0KDQpUaGUgY29kZSBjaHVuayBiZWxvdyB3aWxsIGVzdGltYXRlIHRoZSAqbWl4ZWQtZWZmZWN0cyogbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4gSXQncyBjYWxsZWQgYSBtaXhlZC1lZmZlY3RzIG1vZGVsIGJlY2F1c2UgaXQgaGFzIGJvdGggZml4ZWQgZWZmZWN0cyBhbmQgcmFuZG9tIGVmZmVjdChzKS4NCg0KYGBge3IgbWl4ZWQgZWZmZWN0IG1vZGVsIGVzdGltYXRpb259DQojIEZpdHRpbmcgdGhlIG1peGVkIGVmZmVjdHMgbW9kZWwgdXNpbmcgZ2xtZXIgaW4gdGhlIGxtZTQgcGFja2FnZQ0KbGlicmFyeShsbWU0KQ0KZmdfbWVtIDwtIA0KICBnbG1lcigNCiAgICBmZ19yZXN1bHQgfiBkaXN0YW5jZSAqIHJvb2ZfdHlwZSArICgxfGtpY2tlciksICMgKDF8a2lja2VyKSAtPiByYW5kb20gZWZmZWN0DQogICAgZGF0YSA9IGZnX2RmIHw+IG11dGF0ZShyb29mX3R5cGUgPSBmY3RfcmV2KHJvb2ZfdHlwZSkpLCANCiAgICBuQUdRID0gNTAsICAjIEhvdyBtYW55IHF1YWRyYXR1cmUgcG9pbnRzIHRvIHVzZSB3aGVuIGVzdGltYXRpbmcgdV9pDQogICAgZmFtaWx5ID0gYmlub21pYWwsIA0KICAgIGNvbnRyb2wgPSBnbG1lckNvbnRyb2wob3B0aW1pemVyID0gImJvYnlxYSIpDQogICkNCg0KIyBWaWV3aW5nIHRoZSByZWdyZXNzaW9uIGVzdGltYXRlIHRhYmxlDQpzdW1tYXJ5KGZnX21lbSkkY29lZmZpY2llbnRzIHw+IA0KICBkYXRhLmZyYW1lKCkgfD4gDQogIHJvdW5kKGRpZ2l0cyA9IDMpIHw+IA0KICByb3duYW1lc190b19jb2x1bW4odmFyID0gIlRlcm0iKSB8PiANCiAgbXV0YXRlKEVzdGltYXRlID0gZXhwKEVzdGltYXRlKSkgfD4gDQogIHJlbmFtZSgNCiAgICBTRSA9IFN0ZC4uRXJyb3IsDQogICAgYHAtdmFsdWVgID0gUHIuLi56Li4sDQogICAgYHRlc3Qgc3RhdGAgPSB6LnZhbHVlDQogICkgfD4gDQogIGd0OjpndCgpDQpgYGANCg0KTGlrZSB3aXRoIHRoZSBmaXhlZCBlZmZlY3RzIG1vZGVsLCB0aGUgaW50ZXJhY3Rpb24gdGVybSBkb2Vzbid0IGFwcGVhciB0byBiZSBuZWVkZWQgc2luY2UgdGhlIHAtdmFsdWUgaXMgbGFyZ2UuIEl0IHdpbGwgYmUgcmVtb3ZlZCBhbmQgdGhlIG1vZGVsIHJlcmFuOg0KDQpgYGB7ciBtaXhlZCBlZmZlY3QgbW9kZWwgbm8gaW50ZXJhY3Rpb259DQpmZ19tZW0yIDwtIA0KICBnbG1lcigNCiAgICBmZ19yZXN1bHQgfiBkaXN0YW5jZSArIHJvb2ZfdHlwZSArICgxfGtpY2tlciksIA0KICAgIGRhdGEgPSBmZ19kZiB8PiBtdXRhdGUocm9vZl90eXBlID0gZmN0X3Jldihyb29mX3R5cGUpKSwgDQogICAgbkFHUSA9IDUwLA0KICAgIGZhbWlseSA9IGJpbm9taWFsLCANCiAgICBjb250cm9sID0gZ2xtZXJDb250cm9sKG9wdGltaXplciA9ICJib2J5cWEiKQ0KICApDQoNCnN1bW1hcnkoZmdfbWVtMikkY29lZmZpY2llbnRzIHw+IA0KICBkYXRhLmZyYW1lKCkgfD4gDQogIHJvdW5kKGRpZ2l0cyA9IDMpIHw+IA0KICByb3duYW1lc190b19jb2x1bW4odmFyID0gIlRlcm0iKSB8PiANCiAgbXV0YXRlKEVzdGltYXRlID0gZXhwKEVzdGltYXRlKSkgfD4gDQogIHJlbmFtZSgNCiAgICBTRSA9IFN0ZC4uRXJyb3IsDQogICAgYHAtdmFsdWVgID0gUHIuLi56Li4sDQogICAgYHRlc3Qgc3RhdGAgPSB6LnZhbHVlDQogICkgfD4gDQogIGd0OjpndCgpDQpgYGANCg0KTGlrZSB3aXRoIG91ciBmaXhlZCBlZmZlY3RzIG1vZGVsLCB3ZSBoYXZlIHN0cm9uZyBldmlkZW5jZSB0aGF0IHRoZSBsb2NhdGlvbiBoYXMgYW4gZWZmZWN0IG9uIHRoZSBwcm9iYWJpbGl0eSBhIGZpZWxkIGdvYWwgYXR0ZW1wdCBpcyBzdWNjZXNzZnVsIGFmdGVyIGFjY291bnRpbmcgZm9yIHRoZSBlZmZlY3Qgb2YgZGlzdGFuY2UgYW5kIHRoZSBwZXJzb24gYXR0ZW1wdGluZyB0aGUgZmllbGQgZ29hbC4gDQoNCklmIGEga2ljayBpcyBhdHRlbXB0ZWQgYXQgdGhlIHNhbWUgZGlzdGFuY2UgYnkgdGhlIHNhbWUgcGxheWVyLCB0aGUgKipvZGRzKiogYSBraWNrIGlzIHN1Y2Nlc3NmdWwgaXMgYWJvdXQgMzUuNyUgaGlnaGVyIGlmIGl0IGlzIGF0dGVtcHRlZCBpbiBhbiBpbmRvb3Igc3RhZGl1bSBjb21wYXJlZCB0byBhbiBvdXRkb29yIHN0YWRpdW0uDQoNCg0KTGV0J3MgdmlzdWFsaXplIHRoZSBkaWZmZXJlbmNlIHVzaW5nIHRoZSBtaXhlZCBlZmZlY3QgbW9kZWwuIFdlJ2xsIHN0YXJ0IGJ5IGNyZWF0aW5nIGEgZGF0YSBmcmFtZSB0aGF0IGhhcyBlYWNoIGNvbWJvIG9mIGRpc3RhbmNlLCBzdGFkaXVtIHR5cGUsIGtpY2tlciwgYW5kIHRoZSBzdWNjZXNzIHByb2JhYmlsaXR5IGVzdGltYXRlZCBieSB0aGUgbWl4ZWQgZWZmZWN0cyBtb2RlbC4NCg0KYGBge3IgZXN0aW1hdGVkIHByb2JhYmlsaXRpZXMgZnJvbSBtaXhlZCBlZmZlY3QgbW9kZWx9DQojIENyZWF0aW5nIGEgZGF0YSBmcmFtZSB0aGF0IGhhcyBhIHJvdyBmb3IgZWFjaCBjb21iaW5hdGlvbiBvZiBkaXN0YW5jZSwgcm9vZl90eXBlLCBhbmQga2lja2VyIHRvIHVzZSB0byBlc3RpbWF0ZSB0aGUgcHJvYmFiaWxpdHkgYSBraWNrIGlzIHN1Y2Nlc3NmdWw6DQpwcmVkX2RhdGEgPC0gDQogIGV4cGFuZC5ncmlkKA0KICAgIGRpc3RhbmNlID0gc2VxKDIwLCA1NSwgYnkgPSAwLjI1MCksICAgIyBEaXN0YW5jZSBieSBxdWFydGVyIHlhcmQgaW5jcmVtZW50cw0KICAgIHJvb2ZfdHlwZSA9IHVuaXF1ZShmZ19kZiRyb29mX3R5cGUpLCAgIyBUd28gdHlwZXMgb2Ygcm9vZg0KICAgIGtpY2tlciA9IHVuaXF1ZShmZ19kZiRraWNrZXIpICAgICAgICAgIyBraWNrZXJzIGluY2x1ZGVkIGluIHRoZSBtb2RlbA0KICApICAgICAgICANCg0KDQojIFdlIGNhbiB1c2UgdGhlIHByZWRpY3QgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBlc3RpbWF0ZWQgcHJvYmFiaWxpdGllcyANCmZnX3ByZWRpY3QgPC0gDQogIGRhdGEuZnJhbWUoDQogICAgcHJlZF9kYXRhLCANCiAgICBlc3RfbWFkZSA9IHByZWRpY3QoZmdfbWVtLCANCiAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IHByZWRfZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmVzcG9uc2UiKQ0KICApDQpgYGANCg0KVGhlIGdyYXBoIGJlbG93IHNob3dzIHRoZSBtZWRpYW4sIG1pZGRsZSA1MCUsIGFuZCBtaWRkbGUgOTAlIG9mIG1vZGVsIHByb2JhYmlsaXRpZXMgdGhhdCBhIGZpZWxkIGdvYWwgYXR0ZW1wdCBpcyBzdWNjZXNzZnVsLiANCg0KYGBge3IgbWl4ZWQgZWZmZWN0IHBsb3QsIGZpZy53aWR0aCA9IDgsIGZpZy5jYXAgPSAiTWl4ZWQgZWZmZWN0IHJlZ3Jlc3Npb246ICRcXGxvZ1xcbGVmdChcXGZyYWN7cF9pfXsxLXBfaX1cXHJpZ2h0KSA9IFxcYmV0YV8wICsgXFxiZXRhXzF4XzEgKyBcXGJldGFeSV8yICsgdV9pJCJ9DQojIENhbGN1bGF0aW5nIHRoZSA1IG51bWJlciBzdW1tYXJ5IG9mIHN1Y2Nlc3MgZm9yIGVhY2ggZGlzdGFuY2UgYW5kIHJvb2ZfdHlwZSBjb21ibw0KYXZnX3Byb2JzX2Rpc3QgPC0gDQogIGZnX3ByZWRpY3QgfD4NCiAgc3VtbWFyaXplKA0KICAgIC5ieSA9IGMocm9vZl90eXBlLCBkaXN0YW5jZSksDQogICAgYXZnX3Byb2JfbWFkZSA9IG1lYW4oZXN0X21hZGUpLA0KICAgIG1pbl9wcm9iID0gbWluKGVzdF9tYWRlKSwNCiAgICBRMV9wcm9iID0gcXVhbnRpbGUoZXN0X21hZGUsIHByb2IgPSAwLjI1KSwNCiAgICBRM19wcm9iID0gcXVhbnRpbGUoZXN0X21hZGUsIHByb2IgPSAwLjc1KSwNCiAgICBtYXhfcHJvYiA9IG1heChlc3RfbWFkZSksDQogICAgcXVhbnRpbGU5NSA9IHF1YW50aWxlKGVzdF9tYWRlLCBwcm9iID0gMC45NSksDQogICAgcXVhbnRpbGUwNSA9IHF1YW50aWxlKGVzdF9tYWRlLCBwcm9iID0gMC4wNSkNCiAgKQ0KDQojIE5vdyB3ZSdsbCBwbG90IHRoZSByZXN1bHRzOg0KZ2dwbG90KA0KICBkYXRhID0gYXZnX3Byb2JzX2Rpc3QsDQogIG1hcHBpbmcgPSBhZXMoDQogICAgeCA9IGRpc3RhbmNlLCANCiAgICB5ID0gYXZnX3Byb2JfbWFkZSwgDQogICAgZmlsbCA9IHJvb2ZfdHlwZQ0KICApDQopICsgDQogIA0KICAjIEFkZGluZyB0aGUgbGluZSBmb3IgdGhlIG1lYW4gZXN0aW1hdGVkIHByb2JhYmlsaXR5DQogIGdlb21fbGluZSgNCiAgICBtYXBwaW5nID0gYWVzKGNvbG9yID0gcm9vZl90eXBlKSwNCiAgICBsaW5ld2lkdGggPSAxDQogICkgKyANCiAgDQogICMgQWRkaW5nIGEgY29uZmlkZW5jZSByZWdpb24gZm9yIFExIGFuZCBRMyANCiAgZ2VvbV9yaWJib24oDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHltaW4gPSBRMV9wcm9iLCANCiAgICAgIHltYXggPSBRM19wcm9iDQogICAgKSwgICANCiAgICBhbHBoYSA9IDAuNSwgDQogICAgbGluZXR5cGUgPSAiZGFzaGVkIg0KICApICsNCiAgDQogIGFubm90YXRlKA0KICAgIGdlb20gPSAidGV4dCIsDQogICAgbGFiZWwgPSAiU2hhZGVkIHJlZ2lvbnMgaW5kaWNhdGUgdGhlIGVzdGltYXRlZFxubWlkZGxlIDUwJSBhbmQgOTAlIHJhbmdlcyIsDQogICAgeCA9IDI2LA0KICAgIHkgPSAwLjgNCiAgKSArDQogIA0KICBnZW9tX3JpYmJvbigNCiAgICBtYXBwaW5nID0gYWVzKA0KICAgICAgeW1pbiA9IHF1YW50aWxlOTUsDQogICAgICB5bWF4ID0gcXVhbnRpbGUwNQ0KICAgICksDQogICAgYWxwaGEgPSAwLjI1LA0KICAgIGxpbmV0eXBlID0gImRvdHRlZCINCiAgKSArDQogIA0KICAjZmFjZXRfd3JhcCh+cm9vZl90eXBlKSArIA0KICANCiAgIyBDaGFuZ2luZyB0aGUgZmlsbCBhbmQgY29sb3IgcGFsbGV0ZSB0byBtYXRjaCB0aGUgcHJldmlvdXMgY29sb3IgY2hvaWNlcw0KICBzY2FsZV9jb2xvcl9tYW51YWwoDQogICAgdmFsdWVzID0gYygiIzYyNGEyZSIsICIjNTY3ZDQ2IiksIA0KICAgIGFlc3RoZXRpY3MgPSBjKCJjb2xvdXIiLCJmaWxsIikNCiAgKSArIA0KICANCiAgIyBDaGFuZ2luZyB0aGUgeS1heGlzIHRvIHBlcmNlbnRhZ2VzDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgDQogICMgQ2hhbmdpbmcgdGhlIHgtYXhpcyB0byBpbmNsdWRlIHlhcmRzDQogIHNjYWxlX3hfY29udGludW91cygNCiAgICBicmVha3MgPSAyOjYqMTAsDQogICAgbGFiZWxzID0gcGFzdGUoMjo2KjEwLCAieWRzIikNCiAgKSArIA0KICANCiAgIyBBZGRpbmcgY29udGV4dA0KICBsYWJzKA0KICAgIHRpdGxlID0gIlByb2JhYmlsaXR5IG9mIGEgU3VjY2Vzc2Z1bCBGaWVsZCBHb2FsIGJ5IERpc3RhbmNlIGZvciANCiAgICAgICAgICAgICA8c3BhbiBzdHlsZT0nY29sb3I6IzYyNGEyZTsnPkluZG9vcjwvc3Bhbj4gYW5kIA0KICAgICAgICAgICAgIDxzcGFuIHN0eWxlPSdjb2xvcjojNTY3ZDQ2Oyc+T3V0ZG9vcjwvc3Bhbj4gU3RhZGl1bXMiLA0KICAgIHN1YnRpdGxlID0gIlNlYXNvbnMgMjAxOS0yMDI0LCBNaW4gMjUgQXR0ZW1wdHMgcGVyIEtpY2tlciwgQmV0d2VlbiAxOS01OCBZYXJkcyIsDQogICAgeCA9ICJGaWVsZCBHb2FsIERpc3RhbmNlIiwNCiAgICB5ID0gIk1vZGVsIFByb2JhYmlsaXR5IG9mIFN1Y2Nlc3MiLA0KICAgIGNhcHRpb24gPSAiZGF0YTogbmZsZmFzdFIiDQogICkgKyANCiAgDQogICMgTW92aW5nIHRoZSBsZWdlbmQgYW5kIGNlbnRlcmluZyB0aGUgc3VidGl0bGUNCiAgdGhlbWUoDQogICAgbGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnLA0KICAgIHBsb3QudGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUpLA0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpDQogICkNCmBgYA0KDQpJbmRvb3Iga2lja3MgYXJlIG1vcmUgbGlrZWx5IHRvIGJlIG1hZGUsIGFuZCB0aGUgZnVydGhlciB0aGUgZGlzdGFuY2UsIHRoZSBsYXJnZXIgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIHN1Y2Nlc3MgcmF0ZSEgVGhlIGRpZmZlcmVuY2UgaXMgbW9zdCBub3RhYmxlIGZvciBhdHRlbXB0cyA0NSB5YXJkcyBhbmQgZnVydGhlci4NCg0KDQojIyMgQ29tcGFyaW5nIEtpY2tlcnMNCg0KV2hpbGUgaXQgZG9lc24ndCBoZWxwIGFuc3dlciBvdXIgb3JpZ2luYWwgcXVlc3Rpb24gIklzIGl0IGVhc2llciB0byBhdHRlbXB0IGEgZmllbGQgZ29hbCBpbmRvb3JzIiwgd2UgY2FuIHVzZSB0aGUgcmFuZG9tIGVmZmVjdHMgZ2VuZXJhdGVkIGZyb20gb3VyIG1peGVkIGVmZmVjdHMgbW9kZWwgdG8gYW5zd2VyICJXaG8gaXMgdGhlIGJlc3Qga2lja2VyIG92ZXIgdGhlIGxhc3QgNiBzZWFzb25zLCBhY2NvdW50aW5nIGZvciBkaXN0YW5jZSBhbmQgc3RhZGl1bSB0eXBlPyINCg0KDQpJZiB0aGUgcmFuZG9tIGVmZmVjdCBpcyBwb3NpdGl2ZSwgdGhlIGtpY2tlciBpcyBiZXR0ZXIgdGhhbiBsZWFndWUgYXZlcmFnZSBhbmQgaWYgdGhlIHJhbmRvbSBlZmZlY3QgaXMgbmVnYXRpdmUsIHRoZSBraWNrZXIgaXMgd29yc2UgdGhhbiBsZWFndWUgYXZlcmFnZS4gVGhlIGxhcmdlciBpbiBhYnNvbHV0ZSB2YWx1ZSwgdGhlIGJldHRlciAob3Igd29yc2UpIHRoZSBwbGF5ZXIgaXMuDQoNCmBgYHtyIGJlc3QgYW5kIHdvcnN0IDUga2lja2Vyc30NCiMgTGV0J3MgbG9vayBhdCB0aGUgZWZmZWN0aXZlbmVzcyBvZiBlYWNoIHBsYXllcg0Ka2lja2VyX2VmZiA8LSANCiAgcmFuZWYoZmdfbWVtKSB8PiANCiAgZGF0YS5mcmFtZSgpIHw+IA0KICBzZWxlY3Qoa2lja2VyID0gZ3JwLCBjb25kdmFsKQ0KDQpraWNrZXJfZWZmIDwtIA0KICBsZWZ0X2pvaW4oDQogICAgeCA9IGtpY2tlcl9lZmYsDQogICAgeSA9IGtpY2tlcl9jb3VudHMsDQogICAgYnkgPSAia2lja2VyIg0KICApDQoNCiMgVGhlIHRvcCBhbmQgYm90dG9tIDUga2lja2Vycw0KYmluZF9yb3dzKA0KICAuaWQgPSAidHlwZSIsDQogICJiZXN0IiAgPSBraWNrZXJfZWZmIHw+IHNsaWNlX21heChjb25kdmFsLCBuID0gNSksDQogICJ3b3JzdCIgPSBraWNrZXJfZWZmIHw+IGZpbHRlcihuID4gNTApIHw+IHNsaWNlX21pbihjb25kdmFsLCBuID0gNSkNCikgfD4gDQogIGFycmFuZ2UoLWNvbmR2YWwpIHw+IA0KICBtdXRhdGUoYG9kZHMgb2Ygc3VjY2Vzc2AgPSBleHAoY29uZHZhbCkgfD4gcm91bmQoMikpIHw+IA0KICBzZWxlY3Qoa2lja2VyLCBhdHRlbXB0cyA9IG4sIGBvZGRzIG9mIHN1Y2Nlc3NgKSB8PiANCiAgZ3Q6Omd0KCkNCg0KYGBgDQoNCg0KQWNjb3JkaW5nIHRvIHRoZSBtb2RlbCwgQ2hyaXMgQm9zd2VsbCBoYXMgYmVlbiB0aGUgYmVzdCBraWNrZXIgaW4gdGhlIGxlYWd1ZSBvdmVyIHRoZSBsYXN0IHNpeCB5ZWFycywgd2l0aCB0aGUgb2RkcyBvZiBhIHN1Y2Nlc3NmdWwgZmllbGQgZ29hbCBhdHRlbXB0IDMzJSBhYm92ZSB0aGUgbGVhZ3VlIGF2ZXJhZ2UuIEJyYW5kb24gQXVicmV5LCBKdXN0aW4gVHVja2VyLCBhbmQgTmljayBGb2xrIGFyZSB0aWVkIGZvciBzZWNvbmQgcGxhY2UsIGFsbCBoYXZpbmcgMjclIG9kZHMgb2YgYSBzdWNjZXNzZnVsIGZpZWxkIGdvYWwgYXR0ZW1wdCBhYm92ZSBsZWFndWUgYXZlcmFnZS4gDQoNCg0KVGhlIHdvcnN0IGtpY2tlciBpbiB0aGUgbGVhZ3VlICh3aXRoIGEgbWluaW11bSBvZiA1MCBhdHRlbXB0cykgb3ZlciB0aGUgbGFzdCA2IHNlYXNvbnMgaXMgUnlhbiBTdWNjb3AuIFRoZSBvZGRzIGhpcyBmaWVsZCBnb2FsIGF0dGVtcHQgaXMgc3VjY2Vzc2Z1bCBpcyAyMCUgYmVsb3cgdGhlIGxlYWd1ZSBhdmVyYWdlLiBUaGUgd29yc3QgNSBwbGF5ZXJzIGFyZSBhcyBiYWQgYXMgdGhlIGJlc3QgZml2ZSBwbGF5ZXJzIGFyZSBnb29kLiBUaGlzIG1ha2VzIHNlbnNlIHNpbmNlIE5GTCBraWNrZXJzIGFyZW4ndCBnaXZlbiBtYW55IGNoYW5jZXMsIGFuZCBpZiB0aGV5IHN0YXJ0IHBlcmZvcm1pbmcgYmVsb3cgdGhlIGxlYWd1ZSBhdmVyYWdlLCB0aGV5IHdvbid0IGhhdmUgYSBqb2IgZm9yIHZlcnkgbG9uZyENCg0KTGV0J3MgdmlzdWFsaXplIGtpY2tlcnMnIHJhbmRvbSBlZmZlY3RzIGZvciBwbGF5ZXJzIHdobyBoYXZlIGF0IGxlYXN0IDEwMCBhdHRlbXB0czoNCg0KYGBge3Iga2lja2VyIGdyYXBoLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLmNhcCA9ICJPZGRzIG9mIHN1Y2Nlc3MgZm9yIGEga2lja2VyIGFjY291bnRpbmcgZm9yIGRpc3RhbmNlIGFuZCBsb2NhdGlvbiJ9DQojIExvYWRpbmcgbmVlZGVkIHBhY2thZ2VzDQpwYWNtYW46OnBfbG9hZChuZmxwbG90UikNCg0KDQpnZ19raWNrZXJfZWZmZWN0cyA8LSANCiAga2lja2VyX2VmZiB8PiANCiAgIyBPbmx5IGtlZXBpbmcgcm93cyB3aXRoIGF0IGxlYXN0IDEwMCBhdHRlbXB0cw0KICBmaWx0ZXIobiA+PSAxMDApIHw+IA0KICANCiAgbGVmdF9qb2luKHkgPSBGaWVsZEdvYWxzIHw+IHNlbGVjdChraWNrZXIsIGtpY2tlcl9pZCkgfD4gZGlzdGluY3QoKSwNCiAgICAgICAgICAgIGJ5ID0gImtpY2tlciIpIHw+IA0KICANCiAgIyBibHVlID0gYWJvdmUgYXZlcmFnZSwgcmVkID0gYmVsb3cgYXZlcmFnZSBhbmQgcmVvcmRlcmluZyB0aGUga2lja2Vycw0KICBtdXRhdGUoDQogICAgYmFyX2NvbCA9IGlmX2Vsc2UoY29uZHZhbCA+IDAsICIjMDEzMzY5IiwgIiNENTBBMEEiKSwNCiAgICBraWNrZXIgPSBmY3RfcmVvcmRlcihraWNrZXIsIGNvbmR2YWwpDQogICkgfD4gDQogIA0KICAjIEJhciBjaGFydCBmb3Iga2lja2VyIGVmZmVjdHMNCiAgZ2dwbG90KA0KICAgIG1hcHBpbmcgPSBhZXMoDQogICAgICB4ID0gZmN0X3JldihraWNrZXIpLA0KICAgICAgeSA9IGFicyhjb25kdmFsKSwNCiAgICAgIGZpbGwgPSBiYXJfY29sDQogICAgKQ0KICApICsgDQogIA0KICAjIEFkZGluZyBhIGxpbmUgZm9yIHRoZSBiYXJzIHRvIHNpdCBvbiBhdCAwDQogIGdlb21faGxpbmUoDQogICAgbGluZXdpZHRoID0gMSwNCiAgICB5aW50ZXJjZXB0ID0gMA0KICApICsNCiAgDQogIGdlb21fY29sKA0KICAgIGNvbG9yID0gImJsYWNrIg0KICApICsNCiAgDQogIGFubm90YXRlKA0KICAgIGdlb20gPSAidGV4dCIsDQogICAgY29sb3IgPSAiIzAxMzM2OSIsDQogICAgbGFiZWwgPSAiTW9yZSBsaWtlbHkgdG8gbWFrZSB0aGUgYXR0ZW1wdCIsDQogICAgeCA9ICJDLlNhbnRvcyIsDQogICAgeSA9IC4zLA0KICAgIGZvbnRmYWNlID0gImJvbGQiDQogICkgKyANCiAgDQogIGFubm90YXRlKA0KICAgIGdlb20gPSAidGV4dCIsDQogICAgY29sb3IgPSAiI0Q1MEEwQSIsDQogICAgbGFiZWwgPSAiTW9yZSBsaWtlbHkgdG8gbWlzcyB0aGUgYXR0ZW1wdCIsDQogICAgeCA9ICJXLkx1dHoiLA0KICAgIHkgPSAwLjMsDQogICAgZm9udGZhY2UgPSAiYm9sZCINCiAgKSArDQogIA0KICBzY2FsZV9maWxsX2lkZW50aXR5KCkgKyANCiAgDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBsaW1pdHMgPSBjKDAsIDAuMzUpLA0KICAgIGJyZWFrcyA9ICgwKTo1LzEwLA0KICAgIGV4cGFuZCA9IGMoMCwwKSwNCiAgICBsYWJlbHMgPSByb3VuZChleHAoMDo1LzEwKSwgMSkNCiAgKSArIA0KICANCiAgbGFicygNCiAgICB4ID0gTlVMTCwNCiAgICB5ID0gIk9kZCBvZiBzdWNjZXNzIiwgDQogICAgdGl0bGUgPSAiT2RkcyBvZiBzdWNjZXNzIGZvciBORkwgS2lja2VycyIsDQogICAgc3VidGl0bGUgPSAiMjAxOSAtIDIwMjQgcmVndWxhciBzZWFzb247IE1pbnVtdW0gMTAwIGF0dGVtcHRzIg0KICApICsgDQogIA0KICB0aGVtZSgNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCgNCiAgICAgIGFuZ2xlID0gOTAsDQogICAgICBoanVzdCA9IDAuOTUsDQogICAgICB2anVzdCA9IDAuMTUNCiAgICApDQogICkgKw0KICBnZW9tX25mbF9oZWFkc2hvdHMoDQogICAgbWFwcGluZyA9IGFlcyhwbGF5ZXJfZ3NpcyA9IGtpY2tlcl9pZCksDQogICAgd2lkdGggPSAwLjA4LA0KICAgIGhlaWdodCA9IDAuMDgsDQogICAgdmp1c3QgPSAwDQogICkgIA0KDQoNCg0KZ2dfa2lja2VyX2VmZmVjdHMNCmBgYA0KDQpUaGVyZSBhcmUgbW9yZSBwbGF5ZXJzIHdpdGggYSBwb3NpdGl2ZSBlZmZlY3Qgb24gdGhlIG9kZHMgYSBmaWVsZCBnb2FsIGlzIHN1Y2Nlc3NmdWwuIEEgcG9zc2libGUgZXhwbGFuYXRpb24gaXMgaWYgYSBraWNrZXIgaGFzIGEgYmVsb3cgYXZlcmFnZSBjaGFuY2Ugb2Ygc3VjY2VzcywgdGhleSBhcmUgY3V0IGZyb20gdGhlIHRlYW0gYW5kIGFyZSB1bmxpa2VseSB0byBnZXQgbW9yZSB0aGFuIDEwMCBhdHRlbXB0cyBhbmQgd291bGRuJ3QgYmUgaW5jbHVkZWQgaW4gdGhlIGdyYXBoLg0KDQoNCg0KDQoNCg0K