Introduction

In the NFL, teams can score points in three ways:

  1. Safety: Two points
  1. Field Goal: Three points
  1. Touchdown: Six points

A field goal attempt occurs when a team attempts to kick the ball through goal placed at both ends of the playing field. If the ball passes between the uprights, the team scores 3 points. Otherwise, the other team’s offense starts where the kicking team was on the field. If a team misses a field goal, not only do they lose out on three points, but the opponent has a higher probability of scoring points!

There are two types of stadium roofs that teams play in: open or closed. One common comment about stadiums with closed roofs (called domes) is teams have an easier time scoring since the weather doesn’t have any influence on the result of a play. We want to examine the data to determine if there is any legitimacy of these types of comments for field goal attempts.

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:

  1. team: The team attempting the field goal

  2. kicker: The player who attempted the field goa

  3. distance: How far away the uprights are from the player attempting the field goal

  4. roof_type: One of two different values:

  1. fg_result: If the field goal attempt was successful
  1. 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

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)
  )
Logistic regression model: $\log\left(\frac{p}{1-p}\right) = \beta_0 + \beta_1x_1 + \beta^I_2$

Logistic regression model: \(\log\left(\frac{p}{1-p}\right) = \beta_0 + \beta_1x_1 + \beta^I_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)
  )
Mixed effect regression: $\log\left(\frac{p_i}{1-p_i}\right) = \beta_0 + \beta_1x_1 + \beta^I_2 + u_i$

Mixed effect regression: \(\log\left(\frac{p_i}{1-p_i}\right) = \beta_0 + \beta_1x_1 + \beta^I_2 + u_i\)

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
Odds of success for a kicker accounting for distance and location

Odds of success for a kicker accounting for distance and location

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