library(tidyverse)
library(nflfastR)
# Define the range of years you want to load (e.g., 2003 to 2023)
years <- 1999:2024

# Initialize an empty list to store the play-by-play data for each year
pbp_list <- list()

# Loop through each year and load the play-by-play data
for (year in years) {
  # Load the data and add the year column
  pbp_list[[as.character(year)]] <- load_pbp(year) %>%
    mutate(year = year)
}
Warning: URL 'https://objects.githubusercontent.com/github-production-release-asset-2e65be/452908115/bed379f4-234b-4ba6-99e8-e2bd2d143a44?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=releaseassetproduction%2F20240919%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20240919T190029Z&X-Amz-Expires=300&X-Amz-Signature=848e0a3834a16aca63449de978e697267d6774dee764a885fee276519c2df0a8&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%3Dplay_by_play_2017.rds&response-content-type=application%2Foctet-stream': status was 'Failure when receiving data from the peer'Warning: Failed to readRDS from <]8;;https://github.com/nflverse/nflverse-data/releases/download/pbp/play_by_play_2017.rdshttps://github.com/nflverse/nflverse-data/releases/download/pbp/play_by_play_2017.rds]8;;>
# Combine all the yearly play-by-play data into one data frame using bind_rows
pbp_data <- bind_rows(pbp_list)

let’s build an easy expected model based only on yards to go.

first, let’s look at all the different play types

play_type_counts <- pbp_data %>%
  group_by(play_type) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

play_type_counts

let’s looks at the playtypes by year:

# Summarize the number of plays by play_type and year
play_type_year_summary <- pbp_data %>%
  group_by(play_type, year) %>%
  summarise(count = n(), .groups = 'drop')


# Reshape the data to have years as columns
play_type_year_pivot <- play_type_year_summary %>%
  pivot_wider(names_from = year, values_from = count, values_fill = 0)

print(play_type_year_pivot)

Let’s quickly look at a visual to see if there are any evident trends

library(ggplot2)
library(ggrepel)

# Pivot the data to long format so that we can plot it easily with ggplot2
play_type_long <- play_type_year_pivot %>%
  pivot_longer(cols = -play_type, names_to = "year", values_to = "count") %>%
  mutate(year = as.numeric(year))  # Convert year to numeric for the plot

# Create the line plot
ggplot(play_type_long, aes(x = year, y = count, color = play_type)) +
  geom_line(size = 1) +  # Line size for better visibility
  labs(title = "Number of Plays by Play Type Over the Years",
       x = "Year",
       y = "Number of Plays",
       color = "Play Type") +  # Labels and title
  theme_minimal() +  # Clean theme
  theme(
    legend.position = "right",  # Place legend on the right
    plot.title = element_text(hjust = 0.5)  # Center the title
  )

Now let’s look at the

# Summarize the number of plays by play_type and year
touchdown_year_summary <- pbp_data %>%
  filter(play_type == "pass" | play_type =="run" | play_type== "punt" | play_type=="kickoff") %>%
  group_by(yardline_100, touchdown) %>%
  summarise(count = n(), .groups = 'drop')

# Reshape the data to have years as columns
play_touchdown_year_pivot <- touchdown_year_summary %>%
  pivot_wider(names_from = yardline_100, values_from = count, values_fill = 0)

play_touchdown_year_pivot <- play_touchdown_year_pivot %>%
  filter(!is.na(touchdown))

print(play_touchdown_year_pivot)
NA

Let’s calculate probabilities

# Calculate the total number of plays and the number of touchdowns for each yardline
probability_data <- play_touchdown_year_pivot %>%
  pivot_longer(cols = -touchdown, names_to = "yardline_100", values_to = "count") %>%
  mutate(yardline_100 = as.numeric(gsub("X", "", yardline_100))) %>%
  group_by(yardline_100) %>%
  summarise(
    total_plays = sum(count),
    touchdowns = sum(count[touchdown == 1]),
    .groups = 'drop'
  ) %>%
  mutate(probability_td = touchdowns / total_plays) %>%
  select(yardline_100, probability_td)

# Create a new dataframe with probabilities for scoring (1) and not scoring (0)
probability_scores <- probability_data %>%
  mutate(probability_no_td = 1 - probability_td) %>%
  select(yardline_100, probability_td, probability_no_td)

# View the resulting probability scores
print(probability_scores)

Let’s join in the probability data

combined_data <- pbp_data %>%
  left_join(probability_scores, by = "yardline_100") 

Next let’s create a “James Expected TD” column

combined_data <- combined_data %>%
  mutate(
    James_xTD = ifelse(play_type %in% c("pass", "run", "punt", "kickoff"), probability_td, NA)
      )

Let’s summarize by player

expected_td_summary <-combined_data %>%
  group_by(fantasy_player_name) %>%
  summarise(count = n(),
            touchdowns = sum(touchdown),
            James_xTD = sum(James_xTD), .groups = 'drop')

expected_td_summary_by_year <-combined_data %>%
  group_by(fantasy_player_name, year) %>%
  summarise(touchdowns = sum(touchdown),
            James_xTD = sum(James_xTD), .groups = 'drop')

Okay, now let’s regress James_xTF on Touchdowns to see how well the model performs:

# Perform linear regression
model <- lm(touchdowns ~ James_xTD, data = expected_td_summary)

# Summary of the model to check the details of regression output
summary(model)

Call:
lm(formula = touchdowns ~ James_xTD, data = expected_td_summary)

Residuals:
    Min      1Q  Median      3Q     Max 
-44.759  -1.454  -1.099  -0.014  72.583 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 1.086054   0.129503   8.386   <2e-16 ***
James_xTD   0.914747   0.008156 112.149   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.521 on 3025 degrees of freedom
  (611 observations deleted due to missingness)
Multiple R-squared:  0.8061,    Adjusted R-squared:  0.8061 
F-statistic: 1.258e+04 on 1 and 3025 DF,  p-value: < 2.2e-16

Now let’s visualize:

# Plotting the regression line along with the data points
ggplot(expected_td_summary, aes(x = James_xTD, y = touchdowns)) +
  geom_point(aes(color = touchdowns), alpha = 0.5) +  # Data points with semi-transparency
  geom_smooth(method = "lm", se = TRUE, color = "blue") +  # Linear regression line with confidence interval
  labs(title = "Linear Regression of Touchdowns on James_xTD",
       x = "James_xTD",
       y = "Touchdowns") +
  theme_minimal()  # Clean theme

# Load the ggfortify library for diagnostics
library(ggfortify)

# Generate diagnostic plots
autoplot(model)

We can see, the residual variance is not equal….and possibly violating normality assumptions…. let’s try again breaking the data down by year:


# Perform linear regression
model <- lm(touchdowns ~ James_xTD, data = expected_td_summary_by_year)

# Summary of the model to check the details of regression output
summary(model)

Call:
lm(formula = touchdowns ~ James_xTD, data = expected_td_summary_by_year)

Residuals:
     Min       1Q   Median       3Q      Max 
-11.7570  -0.7924  -0.5391   0.4250  18.2975 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 0.527055   0.021956    24.0   <2e-16 ***
James_xTD   0.845996   0.005701   148.4   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.013 on 11889 degrees of freedom
  (1738 observations deleted due to missingness)
Multiple R-squared:  0.6494,    Adjusted R-squared:  0.6494 
F-statistic: 2.202e+04 on 1 and 11889 DF,  p-value: < 2.2e-16

Now let’s visualize:

# Plotting the regression line along with the data points
ggplot(expected_td_summary_by_year, aes(x = James_xTD, y = touchdowns)) +
  geom_point(aes(color = touchdowns), alpha = 0.5) +  # Data points with semi-transparency
  geom_smooth(method = "lm", se = TRUE, color = "blue") +  # Linear regression line with confidence interval
  labs(title = "Linear Regression of Touchdowns on James_xTD",
       x = "James_xTD",
       y = "Touchdowns") +
  theme_minimal()  # Clean theme

# Load the ggfortify library for diagnostics
library(ggfortify)

# Generate diagnostic plots
autoplot(model)

expected_td_summary_by_year <- expected_td_summary_by_year %>%
  mutate(
   xTD_TD_deficit = touchdowns - James_xTD
  ) %>%
  arrange(desc(xTD_TD_deficit))

expected_td_summary_by_year
expected_td_summary_by_year_2024 <- expected_td_summary_by_year %>%
  filter(year==2024)

expected_td_summary_by_year_2024 <- expected_td_summary_by_year_2024 %>%
  filter(!is.na(xTD_TD_deficit))

expected_td_summary_by_year_2024

Let’s look at just 2024 so far

library(DT)
# Prepare the top 10 and bottom 10 with rounding
top_10 <- expected_td_summary_by_year %>%
  arrange(desc(xTD_TD_deficit)) %>%
  head(10) %>%
  mutate(
    James_xTD = sprintf("%.2f", James_xTD),
    xTD_TD_deficit = sprintf("%.2f", xTD_TD_deficit)
  )

# Prepare and format bottom 10 Unluckiest Players
bottom_10 <- expected_td_summary_by_year %>%
  arrange(xTD_TD_deficit) %>%
  head(10) %>%
  mutate(
    James_xTD = sprintf("%.2f", James_xTD),
    xTD_TD_deficit = sprintf("%.2f", xTD_TD_deficit)
  )

# Prepare and format top 10 Best Seasons xTD
top_10_xTD <- expected_td_summary_by_year %>%
  arrange(desc(James_xTD)) %>%
  head(10) %>%
  mutate(
    James_xTD = sprintf("%.2f", James_xTD),
    xTD_TD_deficit = sprintf("%.2f", xTD_TD_deficit)
  )

# Prepare and format bottom 10 Worst Seasons xTD
bottom_10_xTD <- expected_td_summary_by_year %>%
  arrange(James_xTD) %>%
  head(10) %>%
  mutate(
    James_xTD = sprintf("%.2f", James_xTD),
    xTD_TD_deficit = sprintf("%.2f", xTD_TD_deficit)
  )

Okay, let’s visualize a little better, first lets create some data frames for just want we want to look at:

# Rename columns for all data frames
top_10 <- top_10 %>%
  rename(
    Player = fantasy_player_name,
    Year = year,
    Touchdowns = touchdowns,
    James_xTD = James_xTD,  # Assuming you want to keep this name, otherwise change as needed
    "Expected TD - TD Diff" = xTD_TD_deficit
  )

bottom_10 <- bottom_10 %>%
  rename(
    Player = fantasy_player_name,
    Year = year,
    Touchdowns = touchdowns,
    James_xTD = James_xTD,  # Change as needed
    "Expected TD - TD Diff" = xTD_TD_deficit
  )

top_10_xTD <- top_10_xTD %>%
  rename(
    Player = fantasy_player_name,
    Year = year,
    Touchdowns = touchdowns,
    James_xTD = James_xTD,  # Change as needed
    "Expected TD - TD Diff" = xTD_TD_deficit
  )

let’s rename the columns

# Create a datatable for the Luckiest Players
luckiest_table <- datatable(
  top_10,  # Assuming 'top_10' contains the top 10 Luckiest players
  options = list(
    pageLength = 10,
    dom = 't',  # Keep the table only; no controls
    ordering = FALSE
  ),
  caption = htmltools::tags$caption(
    style = 'caption-side: top; font-weight: bold; text-align: center; font-size: 16px;',
    'Top 10 Luckiest Players since 1999')
) %>%
  formatStyle(
    columns = c('Player', 'Year', 'Touchdowns', 'James_xTD', 'Expected TD - TD Diff'),
    fontWeight = 'normal',
    fontSize = '14px',
    backgroundColor = 'transparent',
    borderBottom = '1px solid #DDD'
  )

# Create a datatable for the Unluckiest Players
unluckiest_table <- datatable(
  bottom_10,  # Assuming 'bottom_10' contains the bottom 10 Unluckiest players
  options = list(
    pageLength = 10,
    dom = 't',  # Keep the table only; no controls
    ordering = FALSE
  ),
  caption = htmltools::tags$caption(
    style = 'caption-side: top; font-weight: bold; text-align: center; font-size: 16px;',
    'Top 10 Unluckiest Players Since 1999')
) %>%
  formatStyle(
    columns = c('Player', 'Year', 'Touchdowns', 'James_xTD', 'Expected TD - TD Diff'),
    fontWeight = 'normal',
    fontSize = '14px',
    backgroundColor = 'transparent',
    borderBottom = '1px solid #DDD'
  )

#Best Seasons xTD
# Create a datatable for the Unluckiest Players
best_table <- datatable(
  top_10_xTD,  # Assuming 'bottom_10' contains the bottom 10 Unluckiest players
  options = list(
    pageLength = 10,
    dom = 't',  # Keep the table only; no controls
    ordering = FALSE
  ),
  caption = htmltools::tags$caption(
    style = 'caption-side: top; font-weight: bold; text-align: center; font-size: 16px;',
    'Top 10 xTD Seasons by Players Since 1999')
) %>%
  formatStyle(
    columns = c('Player', 'Year', 'Touchdowns', 'James_xTD', 'Expected TD - TD Diff'),
    fontWeight = 'normal',
    fontSize = '14px',
    backgroundColor = 'transparent',
    borderBottom = '1px solid #DDD'
  )

# Render the tables in R Markdown or an R HTML widget
luckiest_table
unluckiest_table
best_table
NA
expected_td_summary_team <- combined_data %>%
  group_by(posteam) %>%
  summarise(
    touchdowns = sum(touchdown, na.rm = TRUE),  # Ensure NAs are ignored
    James_xTD = sum(James_xTD, na.rm = TRUE),   # Ignore NAs
    .groups = 'drop'
  ) %>% 
  mutate(
    xTD_TD_deficit = touchdowns - James_xTD,  # Calculate the deficit
    James_xTD = sprintf("%.2f", James_xTD),   # Format James_xTD to 2 decimal places
    xTD_TD_deficit = sprintf("%.2f", xTD_TD_deficit)  # Format the deficit
  )

# Group by posteam and year, and calculate sum, handling NAs
expected_td_summary_by_year_team <- combined_data %>%
  group_by(posteam, year) %>%
  summarise(
    touchdowns = sum(touchdown, na.rm = TRUE),  # Ensure NAs are ignored
    James_xTD = sum(James_xTD, na.rm = TRUE),   # Ignore NAs
    .groups = 'drop'
  ) %>%
  filter(!is.na(posteam)) %>%  # Ensure posteam is not NA
  mutate(
    xTD_TD_deficit = touchdowns - James_xTD,  # Calculate the deficit
    James_xTD = sprintf("%.2f", James_xTD),   # Format James_xTD to 2 decimal places
    xTD_TD_deficit = sprintf("%.2f", xTD_TD_deficit)  # Format the deficit
  )

# View the result
expected_td_summary_by_year_team
NA
expected_td_summary_by_year_team_2024 <- expected_td_summary_by_year_team %>%
  filter(year==2024) %>%
  arrange(desc(xTD_TD_deficit))

expected_td_summary_by_year_team_2024
NA
expected_td_summary_by_year_team_2024 <- expected_td_summary_by_year_team %>%
  filter(year==2024) %>%
  arrange(desc(xTD_TD_deficit))

expected_td_summary_by_year_team_2024
LS0tDQp0aXRsZTogU2ltcGxlICJEdW1iIiBFeHBlY3RlZCBUb3VjaGRvd25zIE1vZGVsDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobmZsZmFzdFIpDQpgYGANCg0KYGBge3J9DQojIERlZmluZSB0aGUgcmFuZ2Ugb2YgeWVhcnMgeW91IHdhbnQgdG8gbG9hZCAoZS5nLiwgMjAwMyB0byAyMDIzKQ0KeWVhcnMgPC0gMTk5OToyMDI0DQoNCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBwbGF5LWJ5LXBsYXkgZGF0YSBmb3IgZWFjaCB5ZWFyDQpwYnBfbGlzdCA8LSBsaXN0KCkNCg0KIyBMb29wIHRocm91Z2ggZWFjaCB5ZWFyIGFuZCBsb2FkIHRoZSBwbGF5LWJ5LXBsYXkgZGF0YQ0KZm9yICh5ZWFyIGluIHllYXJzKSB7DQogICMgTG9hZCB0aGUgZGF0YSBhbmQgYWRkIHRoZSB5ZWFyIGNvbHVtbg0KICBwYnBfbGlzdFtbYXMuY2hhcmFjdGVyKHllYXIpXV0gPC0gbG9hZF9wYnAoeWVhcikgJT4lDQogICAgbXV0YXRlKHllYXIgPSB5ZWFyKQ0KfQ0KDQojIENvbWJpbmUgYWxsIHRoZSB5ZWFybHkgcGxheS1ieS1wbGF5IGRhdGEgaW50byBvbmUgZGF0YSBmcmFtZSB1c2luZyBiaW5kX3Jvd3MNCnBicF9kYXRhIDwtIGJpbmRfcm93cyhwYnBfbGlzdCkNCmBgYA0KbGV0J3MgYnVpbGQgYW4gZWFzeSBleHBlY3RlZCBtb2RlbCBiYXNlZCBvbmx5IG9uIHlhcmRzIHRvIGdvLg0KDQpmaXJzdCwgbGV0J3MgbG9vayBhdCBhbGwgdGhlIGRpZmZlcmVudCBwbGF5IHR5cGVzDQoNCmBgYHtyfQ0KcGxheV90eXBlX2NvdW50cyA8LSBwYnBfZGF0YSAlPiUNCiAgZ3JvdXBfYnkocGxheV90eXBlKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGNvdW50KSkNCg0KcGxheV90eXBlX2NvdW50cw0KYGBgDQpsZXQncyBsb29rcyBhdCB0aGUgcGxheXR5cGVzIGJ5IHllYXI6DQpgYGB7cn0NCiMgU3VtbWFyaXplIHRoZSBudW1iZXIgb2YgcGxheXMgYnkgcGxheV90eXBlIGFuZCB5ZWFyDQpwbGF5X3R5cGVfeWVhcl9zdW1tYXJ5IDwtIHBicF9kYXRhICU+JQ0KICBncm91cF9ieShwbGF5X3R5cGUsIHllYXIpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpDQoNCg0KIyBSZXNoYXBlIHRoZSBkYXRhIHRvIGhhdmUgeWVhcnMgYXMgY29sdW1ucw0KcGxheV90eXBlX3llYXJfcGl2b3QgPC0gcGxheV90eXBlX3llYXJfc3VtbWFyeSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHllYXIsIHZhbHVlc19mcm9tID0gY291bnQsIHZhbHVlc19maWxsID0gMCkNCg0KcHJpbnQocGxheV90eXBlX3llYXJfcGl2b3QpDQpgYGANCkxldCdzIHF1aWNrbHkgbG9vayBhdCBhIHZpc3VhbCB0byBzZWUgaWYgdGhlcmUgYXJlIGFueSBldmlkZW50IHRyZW5kcw0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdncmVwZWwpDQoNCiMgUGl2b3QgdGhlIGRhdGEgdG8gbG9uZyBmb3JtYXQgc28gdGhhdCB3ZSBjYW4gcGxvdCBpdCBlYXNpbHkgd2l0aCBnZ3Bsb3QyDQpwbGF5X3R5cGVfbG9uZyA8LSBwbGF5X3R5cGVfeWVhcl9waXZvdCAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtcGxheV90eXBlLCBuYW1lc190byA9ICJ5ZWFyIiwgdmFsdWVzX3RvID0gImNvdW50IikgJT4lDQogIG11dGF0ZSh5ZWFyID0gYXMubnVtZXJpYyh5ZWFyKSkgICMgQ29udmVydCB5ZWFyIHRvIG51bWVyaWMgZm9yIHRoZSBwbG90DQoNCiMgQ3JlYXRlIHRoZSBsaW5lIHBsb3QNCmdncGxvdChwbGF5X3R5cGVfbG9uZywgYWVzKHggPSB5ZWFyLCB5ID0gY291bnQsIGNvbG9yID0gcGxheV90eXBlKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsgICMgTGluZSBzaXplIGZvciBiZXR0ZXIgdmlzaWJpbGl0eQ0KICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBQbGF5cyBieSBQbGF5IFR5cGUgT3ZlciB0aGUgWWVhcnMiLA0KICAgICAgIHggPSAiWWVhciIsDQogICAgICAgeSA9ICJOdW1iZXIgb2YgUGxheXMiLA0KICAgICAgIGNvbG9yID0gIlBsYXkgVHlwZSIpICsgICMgTGFiZWxzIGFuZCB0aXRsZQ0KICB0aGVtZV9taW5pbWFsKCkgKyAgIyBDbGVhbiB0aGVtZQ0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCAgIyBQbGFjZSBsZWdlbmQgb24gdGhlIHJpZ2h0DQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkgICMgQ2VudGVyIHRoZSB0aXRsZQ0KICApDQpgYGANCk5vdyBsZXQncyBsb29rIGF0IHRoZSANCg0KYGBge3J9DQojIFN1bW1hcml6ZSB0aGUgbnVtYmVyIG9mIHBsYXlzIGJ5IHBsYXlfdHlwZSBhbmQgeWVhcg0KdG91Y2hkb3duX3llYXJfc3VtbWFyeSA8LSBwYnBfZGF0YSAlPiUNCiAgZmlsdGVyKHBsYXlfdHlwZSA9PSAicGFzcyIgfCBwbGF5X3R5cGUgPT0icnVuIiB8IHBsYXlfdHlwZT09ICJwdW50IiB8IHBsYXlfdHlwZT09ImtpY2tvZmYiKSAlPiUNCiAgZ3JvdXBfYnkoeWFyZGxpbmVfMTAwLCB0b3VjaGRvd24pICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpDQoNCiMgUmVzaGFwZSB0aGUgZGF0YSB0byBoYXZlIHllYXJzIGFzIGNvbHVtbnMNCnBsYXlfdG91Y2hkb3duX3llYXJfcGl2b3QgPC0gdG91Y2hkb3duX3llYXJfc3VtbWFyeSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHlhcmRsaW5lXzEwMCwgdmFsdWVzX2Zyb20gPSBjb3VudCwgdmFsdWVzX2ZpbGwgPSAwKQ0KDQpwbGF5X3RvdWNoZG93bl95ZWFyX3Bpdm90IDwtIHBsYXlfdG91Y2hkb3duX3llYXJfcGl2b3QgJT4lDQogIGZpbHRlcighaXMubmEodG91Y2hkb3duKSkNCg0KcHJpbnQocGxheV90b3VjaGRvd25feWVhcl9waXZvdCkNCg0KYGBgDQpMZXQncyBjYWxjdWxhdGUgcHJvYmFiaWxpdGllcw0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgcGxheXMgYW5kIHRoZSBudW1iZXIgb2YgdG91Y2hkb3ducyBmb3IgZWFjaCB5YXJkbGluZQ0KcHJvYmFiaWxpdHlfZGF0YSA8LSBwbGF5X3RvdWNoZG93bl95ZWFyX3Bpdm90ICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IC10b3VjaGRvd24sIG5hbWVzX3RvID0gInlhcmRsaW5lXzEwMCIsIHZhbHVlc190byA9ICJjb3VudCIpICU+JQ0KICBtdXRhdGUoeWFyZGxpbmVfMTAwID0gYXMubnVtZXJpYyhnc3ViKCJYIiwgIiIsIHlhcmRsaW5lXzEwMCkpKSAlPiUNCiAgZ3JvdXBfYnkoeWFyZGxpbmVfMTAwKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIHRvdGFsX3BsYXlzID0gc3VtKGNvdW50KSwNCiAgICB0b3VjaGRvd25zID0gc3VtKGNvdW50W3RvdWNoZG93biA9PSAxXSksDQogICAgLmdyb3VwcyA9ICdkcm9wJw0KICApICU+JQ0KICBtdXRhdGUocHJvYmFiaWxpdHlfdGQgPSB0b3VjaGRvd25zIC8gdG90YWxfcGxheXMpICU+JQ0KICBzZWxlY3QoeWFyZGxpbmVfMTAwLCBwcm9iYWJpbGl0eV90ZCkNCg0KIyBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdpdGggcHJvYmFiaWxpdGllcyBmb3Igc2NvcmluZyAoMSkgYW5kIG5vdCBzY29yaW5nICgwKQ0KcHJvYmFiaWxpdHlfc2NvcmVzIDwtIHByb2JhYmlsaXR5X2RhdGEgJT4lDQogIG11dGF0ZShwcm9iYWJpbGl0eV9ub190ZCA9IDEgLSBwcm9iYWJpbGl0eV90ZCkgJT4lDQogIHNlbGVjdCh5YXJkbGluZV8xMDAsIHByb2JhYmlsaXR5X3RkLCBwcm9iYWJpbGl0eV9ub190ZCkNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgcHJvYmFiaWxpdHkgc2NvcmVzDQpwcmludChwcm9iYWJpbGl0eV9zY29yZXMpDQpgYGANCg0KTGV0J3Mgam9pbiBpbiB0aGUgcHJvYmFiaWxpdHkgZGF0YQ0KDQpgYGB7cn0NCmNvbWJpbmVkX2RhdGEgPC0gcGJwX2RhdGEgJT4lDQogIGxlZnRfam9pbihwcm9iYWJpbGl0eV9zY29yZXMsIGJ5ID0gInlhcmRsaW5lXzEwMCIpIA0KYGBgDQoNCk5leHQgbGV0J3MgY3JlYXRlIGEgIkphbWVzIEV4cGVjdGVkIFREIiBjb2x1bW4NCmBgYHtyfQ0KY29tYmluZWRfZGF0YSA8LSBjb21iaW5lZF9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgSmFtZXNfeFREID0gaWZlbHNlKHBsYXlfdHlwZSAlaW4lIGMoInBhc3MiLCAicnVuIiwgInB1bnQiLCAia2lja29mZiIpLCBwcm9iYWJpbGl0eV90ZCwgTkEpDQogICAgICApDQpgYGANCg0KTGV0J3Mgc3VtbWFyaXplIGJ5IHBsYXllcg0KYGBge3J9DQpleHBlY3RlZF90ZF9zdW1tYXJ5IDwtY29tYmluZWRfZGF0YSAlPiUNCiAgZ3JvdXBfYnkoZmFudGFzeV9wbGF5ZXJfbmFtZSkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwNCiAgICAgICAgICAgIHRvdWNoZG93bnMgPSBzdW0odG91Y2hkb3duKSwNCiAgICAgICAgICAgIEphbWVzX3hURCA9IHN1bShKYW1lc194VEQpLCAuZ3JvdXBzID0gJ2Ryb3AnKQ0KDQpleHBlY3RlZF90ZF9zdW1tYXJ5X2J5X3llYXIgPC1jb21iaW5lZF9kYXRhICU+JQ0KICBncm91cF9ieShmYW50YXN5X3BsYXllcl9uYW1lLCB5ZWFyKSAlPiUNCiAgc3VtbWFyaXNlKHRvdWNoZG93bnMgPSBzdW0odG91Y2hkb3duKSwNCiAgICAgICAgICAgIEphbWVzX3hURCA9IHN1bShKYW1lc194VEQpLCAuZ3JvdXBzID0gJ2Ryb3AnKQ0KYGBgDQoNCg0KDQpPa2F5LCBub3cgbGV0J3MgcmVncmVzcyBKYW1lc194VEYgb24gVG91Y2hkb3ducyB0byBzZWUgaG93IHdlbGwgdGhlIG1vZGVsIHBlcmZvcm1zOg0KYGBge3J9DQojIFBlcmZvcm0gbGluZWFyIHJlZ3Jlc3Npb24NCm1vZGVsIDwtIGxtKHRvdWNoZG93bnMgfiBKYW1lc194VEQsIGRhdGEgPSBleHBlY3RlZF90ZF9zdW1tYXJ5KQ0KDQojIFN1bW1hcnkgb2YgdGhlIG1vZGVsIHRvIGNoZWNrIHRoZSBkZXRhaWxzIG9mIHJlZ3Jlc3Npb24gb3V0cHV0DQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQpOb3cgbGV0J3MgdmlzdWFsaXplOg0KYGBge3J9DQojIFBsb3R0aW5nIHRoZSByZWdyZXNzaW9uIGxpbmUgYWxvbmcgd2l0aCB0aGUgZGF0YSBwb2ludHMNCmdncGxvdChleHBlY3RlZF90ZF9zdW1tYXJ5LCBhZXMoeCA9IEphbWVzX3hURCwgeSA9IHRvdWNoZG93bnMpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gdG91Y2hkb3ducyksIGFscGhhID0gMC41KSArICAjIERhdGEgcG9pbnRzIHdpdGggc2VtaS10cmFuc3BhcmVuY3kNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFLCBjb2xvciA9ICJibHVlIikgKyAgIyBMaW5lYXIgcmVncmVzc2lvbiBsaW5lIHdpdGggY29uZmlkZW5jZSBpbnRlcnZhbA0KICBsYWJzKHRpdGxlID0gIkxpbmVhciBSZWdyZXNzaW9uIG9mIFRvdWNoZG93bnMgb24gSmFtZXNfeFREIiwNCiAgICAgICB4ID0gIkphbWVzX3hURCIsDQogICAgICAgeSA9ICJUb3VjaGRvd25zIikgKw0KICB0aGVtZV9taW5pbWFsKCkgICMgQ2xlYW4gdGhlbWUNCmBgYA0KYGBge3J9DQojIExvYWQgdGhlIGdnZm9ydGlmeSBsaWJyYXJ5IGZvciBkaWFnbm9zdGljcw0KbGlicmFyeShnZ2ZvcnRpZnkpDQoNCiMgR2VuZXJhdGUgZGlhZ25vc3RpYyBwbG90cw0KYXV0b3Bsb3QobW9kZWwpDQpgYGANCldlIGNhbiBzZWUsIHRoZSByZXNpZHVhbCB2YXJpYW5jZSBpcyBub3QgZXF1YWwuLi4uYW5kIHBvc3NpYmx5IHZpb2xhdGluZyBub3JtYWxpdHkgYXNzdW1wdGlvbnMuLi4uIGxldCdzIHRyeSBhZ2FpbiBicmVha2luZyB0aGUgZGF0YSBkb3duIGJ5IHllYXI6DQoNCmBgYHtyfQ0KDQojIFBlcmZvcm0gbGluZWFyIHJlZ3Jlc3Npb24NCm1vZGVsIDwtIGxtKHRvdWNoZG93bnMgfiBKYW1lc194VEQsIGRhdGEgPSBleHBlY3RlZF90ZF9zdW1tYXJ5X2J5X3llYXIpDQoNCiMgU3VtbWFyeSBvZiB0aGUgbW9kZWwgdG8gY2hlY2sgdGhlIGRldGFpbHMgb2YgcmVncmVzc2lvbiBvdXRwdXQNCnN1bW1hcnkobW9kZWwpDQpgYGANCk5vdyBsZXQncyB2aXN1YWxpemU6DQpgYGB7cn0NCiMgUGxvdHRpbmcgdGhlIHJlZ3Jlc3Npb24gbGluZSBhbG9uZyB3aXRoIHRoZSBkYXRhIHBvaW50cw0KZ2dwbG90KGV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhciwgYWVzKHggPSBKYW1lc194VEQsIHkgPSB0b3VjaGRvd25zKSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHRvdWNoZG93bnMpLCBhbHBoYSA9IDAuNSkgKyAgIyBEYXRhIHBvaW50cyB3aXRoIHNlbWktdHJhbnNwYXJlbmN5DQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3IgPSAiYmx1ZSIpICsgICMgTGluZWFyIHJlZ3Jlc3Npb24gbGluZSB3aXRoIGNvbmZpZGVuY2UgaW50ZXJ2YWwNCiAgbGFicyh0aXRsZSA9ICJMaW5lYXIgUmVncmVzc2lvbiBvZiBUb3VjaGRvd25zIG9uIEphbWVzX3hURCIsDQogICAgICAgeCA9ICJKYW1lc194VEQiLA0KICAgICAgIHkgPSAiVG91Y2hkb3ducyIpICsNCiAgdGhlbWVfbWluaW1hbCgpICAjIENsZWFuIHRoZW1lDQpgYGANCmBgYHtyfQ0KIyBMb2FkIHRoZSBnZ2ZvcnRpZnkgbGlicmFyeSBmb3IgZGlhZ25vc3RpY3MNCmxpYnJhcnkoZ2dmb3J0aWZ5KQ0KDQojIEdlbmVyYXRlIGRpYWdub3N0aWMgcGxvdHMNCmF1dG9wbG90KG1vZGVsKQ0KYGBgDQpgYGB7cn0NCmxvZ19tb2RlbCA8LSBnbG0odG91Y2hkb3duIH4gSmFtZXNfeFRELCBkYXRhID0gY29tYmluZWRfZGF0YSwgZmFtaWx5ID0gYmlub21pYWwpDQoNCiMgU3VtbWFyeSBvZiB0aGUgbW9kZWwgdG8gY2hlY2sgdGhlIGRldGFpbHMgb2YgcmVncmVzc2lvbiBvdXRwdXQNCnN1bW1hcnkobG9nX21vZGVsKQ0KDQoNCg0KDQoNCmBgYHtyfQ0KZXhwZWN0ZWRfdGRfc3VtbWFyeV9ieV95ZWFyIDwtIGV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhciAlPiUNCiAgbXV0YXRlKA0KICAgeFREX1REX2RlZmljaXQgPSB0b3VjaGRvd25zIC0gSmFtZXNfeFREDQogICkgJT4lDQogIGFycmFuZ2UoZGVzYyh4VERfVERfZGVmaWNpdCkpDQoNCmV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhcg0KYGBgDQoNCkxldCdzIGxvb2sgYXQganVzdCAyMDI0IHNvIGZhcg0KYGBge3J9DQpleHBlY3RlZF90ZF9zdW1tYXJ5X2J5X3llYXJfMjAyNCA8LSBleHBlY3RlZF90ZF9zdW1tYXJ5X2J5X3llYXIgJT4lDQogIGZpbHRlcih5ZWFyPT0yMDI0KQ0KDQpleHBlY3RlZF90ZF9zdW1tYXJ5X2J5X3llYXJfMjAyNCA8LSBleHBlY3RlZF90ZF9zdW1tYXJ5X2J5X3llYXJfMjAyNCAlPiUNCiAgZmlsdGVyKCFpcy5uYSh4VERfVERfZGVmaWNpdCkpDQoNCmV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhcl8yMDI0DQpgYGANCk9rYXksIGxldCdzIHZpc3VhbGl6ZSBhIGxpdHRsZSBiZXR0ZXIsIGZpcnN0IGxldHMgY3JlYXRlIHNvbWUgZGF0YSBmcmFtZXMgZm9yIGp1c3Qgd2FudCB3ZSB3YW50IHRvIGxvb2sgYXQ6DQoNCmBgYHtyfQ0KbGlicmFyeShEVCkNCiMgUHJlcGFyZSB0aGUgdG9wIDEwIGFuZCBib3R0b20gMTAgd2l0aCByb3VuZGluZw0KdG9wXzEwIDwtIGV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhciAlPiUNCiAgYXJyYW5nZShkZXNjKHhURF9URF9kZWZpY2l0KSkgJT4lDQogIGhlYWQoMTApICU+JQ0KICBtdXRhdGUoDQogICAgSmFtZXNfeFREID0gc3ByaW50ZigiJS4yZiIsIEphbWVzX3hURCksDQogICAgeFREX1REX2RlZmljaXQgPSBzcHJpbnRmKCIlLjJmIiwgeFREX1REX2RlZmljaXQpDQogICkNCg0KIyBQcmVwYXJlIGFuZCBmb3JtYXQgYm90dG9tIDEwIFVubHVja2llc3QgUGxheWVycw0KYm90dG9tXzEwIDwtIGV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhciAlPiUNCiAgYXJyYW5nZSh4VERfVERfZGVmaWNpdCkgJT4lDQogIGhlYWQoMTApICU+JQ0KICBtdXRhdGUoDQogICAgSmFtZXNfeFREID0gc3ByaW50ZigiJS4yZiIsIEphbWVzX3hURCksDQogICAgeFREX1REX2RlZmljaXQgPSBzcHJpbnRmKCIlLjJmIiwgeFREX1REX2RlZmljaXQpDQogICkNCg0KIyBQcmVwYXJlIGFuZCBmb3JtYXQgdG9wIDEwIEJlc3QgU2Vhc29ucyB4VEQNCnRvcF8xMF94VEQgPC0gZXhwZWN0ZWRfdGRfc3VtbWFyeV9ieV95ZWFyICU+JQ0KICBhcnJhbmdlKGRlc2MoSmFtZXNfeFREKSkgJT4lDQogIGhlYWQoMTApICU+JQ0KICBtdXRhdGUoDQogICAgSmFtZXNfeFREID0gc3ByaW50ZigiJS4yZiIsIEphbWVzX3hURCksDQogICAgeFREX1REX2RlZmljaXQgPSBzcHJpbnRmKCIlLjJmIiwgeFREX1REX2RlZmljaXQpDQogICkNCg0KIyBQcmVwYXJlIGFuZCBmb3JtYXQgYm90dG9tIDEwIFdvcnN0IFNlYXNvbnMgeFREDQpib3R0b21fMTBfeFREIDwtIGV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhciAlPiUNCiAgYXJyYW5nZShKYW1lc194VEQpICU+JQ0KICBoZWFkKDEwKSAlPiUNCiAgbXV0YXRlKA0KICAgIEphbWVzX3hURCA9IHNwcmludGYoIiUuMmYiLCBKYW1lc194VEQpLA0KICAgIHhURF9URF9kZWZpY2l0ID0gc3ByaW50ZigiJS4yZiIsIHhURF9URF9kZWZpY2l0KQ0KICApDQpgYGANCg0KbGV0J3MgcmVuYW1lIHRoZSBjb2x1bW5zDQpgYGB7cn0NCiMgUmVuYW1lIGNvbHVtbnMgZm9yIGFsbCBkYXRhIGZyYW1lcw0KdG9wXzEwIDwtIHRvcF8xMCAlPiUNCiAgcmVuYW1lKA0KICAgIFBsYXllciA9IGZhbnRhc3lfcGxheWVyX25hbWUsDQogICAgWWVhciA9IHllYXIsDQogICAgVG91Y2hkb3ducyA9IHRvdWNoZG93bnMsDQogICAgSmFtZXNfeFREID0gSmFtZXNfeFRELCAgIyBBc3N1bWluZyB5b3Ugd2FudCB0byBrZWVwIHRoaXMgbmFtZSwgb3RoZXJ3aXNlIGNoYW5nZSBhcyBuZWVkZWQNCiAgICAiRXhwZWN0ZWQgVEQgLSBURCBEaWZmIiA9IHhURF9URF9kZWZpY2l0DQogICkNCg0KYm90dG9tXzEwIDwtIGJvdHRvbV8xMCAlPiUNCiAgcmVuYW1lKA0KICAgIFBsYXllciA9IGZhbnRhc3lfcGxheWVyX25hbWUsDQogICAgWWVhciA9IHllYXIsDQogICAgVG91Y2hkb3ducyA9IHRvdWNoZG93bnMsDQogICAgSmFtZXNfeFREID0gSmFtZXNfeFRELCAgIyBDaGFuZ2UgYXMgbmVlZGVkDQogICAgIkV4cGVjdGVkIFREIC0gVEQgRGlmZiIgPSB4VERfVERfZGVmaWNpdA0KICApDQoNCnRvcF8xMF94VEQgPC0gdG9wXzEwX3hURCAlPiUNCiAgcmVuYW1lKA0KICAgIFBsYXllciA9IGZhbnRhc3lfcGxheWVyX25hbWUsDQogICAgWWVhciA9IHllYXIsDQogICAgVG91Y2hkb3ducyA9IHRvdWNoZG93bnMsDQogICAgSmFtZXNfeFREID0gSmFtZXNfeFRELCAgIyBDaGFuZ2UgYXMgbmVlZGVkDQogICAgIkV4cGVjdGVkIFREIC0gVEQgRGlmZiIgPSB4VERfVERfZGVmaWNpdA0KICApDQpgYGANCg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgZGF0YXRhYmxlIGZvciB0aGUgTHVja2llc3QgUGxheWVycw0KbHVja2llc3RfdGFibGUgPC0gZGF0YXRhYmxlKA0KICB0b3BfMTAsICAjIEFzc3VtaW5nICd0b3BfMTAnIGNvbnRhaW5zIHRoZSB0b3AgMTAgTHVja2llc3QgcGxheWVycw0KICBvcHRpb25zID0gbGlzdCgNCiAgICBwYWdlTGVuZ3RoID0gMTAsDQogICAgZG9tID0gJ3QnLCAgIyBLZWVwIHRoZSB0YWJsZSBvbmx5OyBubyBjb250cm9scw0KICAgIG9yZGVyaW5nID0gRkFMU0UNCiAgKSwNCiAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKA0KICAgIHN0eWxlID0gJ2NhcHRpb24tc2lkZTogdG9wOyBmb250LXdlaWdodDogYm9sZDsgdGV4dC1hbGlnbjogY2VudGVyOyBmb250LXNpemU6IDE2cHg7JywNCiAgICAnVG9wIDEwIEx1Y2tpZXN0IFBsYXllcnMgc2luY2UgMTk5OScpDQopICU+JQ0KICBmb3JtYXRTdHlsZSgNCiAgICBjb2x1bW5zID0gYygnUGxheWVyJywgJ1llYXInLCAnVG91Y2hkb3ducycsICdKYW1lc194VEQnLCAnRXhwZWN0ZWQgVEQgLSBURCBEaWZmJyksDQogICAgZm9udFdlaWdodCA9ICdub3JtYWwnLA0KICAgIGZvbnRTaXplID0gJzE0cHgnLA0KICAgIGJhY2tncm91bmRDb2xvciA9ICd0cmFuc3BhcmVudCcsDQogICAgYm9yZGVyQm90dG9tID0gJzFweCBzb2xpZCAjREREJw0KICApDQoNCiMgQ3JlYXRlIGEgZGF0YXRhYmxlIGZvciB0aGUgVW5sdWNraWVzdCBQbGF5ZXJzDQp1bmx1Y2tpZXN0X3RhYmxlIDwtIGRhdGF0YWJsZSgNCiAgYm90dG9tXzEwLCAgIyBBc3N1bWluZyAnYm90dG9tXzEwJyBjb250YWlucyB0aGUgYm90dG9tIDEwIFVubHVja2llc3QgcGxheWVycw0KICBvcHRpb25zID0gbGlzdCgNCiAgICBwYWdlTGVuZ3RoID0gMTAsDQogICAgZG9tID0gJ3QnLCAgIyBLZWVwIHRoZSB0YWJsZSBvbmx5OyBubyBjb250cm9scw0KICAgIG9yZGVyaW5nID0gRkFMU0UNCiAgKSwNCiAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKA0KICAgIHN0eWxlID0gJ2NhcHRpb24tc2lkZTogdG9wOyBmb250LXdlaWdodDogYm9sZDsgdGV4dC1hbGlnbjogY2VudGVyOyBmb250LXNpemU6IDE2cHg7JywNCiAgICAnVG9wIDEwIFVubHVja2llc3QgUGxheWVycyBTaW5jZSAxOTk5JykNCikgJT4lDQogIGZvcm1hdFN0eWxlKA0KICAgIGNvbHVtbnMgPSBjKCdQbGF5ZXInLCAnWWVhcicsICdUb3VjaGRvd25zJywgJ0phbWVzX3hURCcsICdFeHBlY3RlZCBURCAtIFREIERpZmYnKSwNCiAgICBmb250V2VpZ2h0ID0gJ25vcm1hbCcsDQogICAgZm9udFNpemUgPSAnMTRweCcsDQogICAgYmFja2dyb3VuZENvbG9yID0gJ3RyYW5zcGFyZW50JywNCiAgICBib3JkZXJCb3R0b20gPSAnMXB4IHNvbGlkICNEREQnDQogICkNCg0KI0Jlc3QgU2Vhc29ucyB4VEQNCiMgQ3JlYXRlIGEgZGF0YXRhYmxlIGZvciB0aGUgVW5sdWNraWVzdCBQbGF5ZXJzDQpiZXN0X3RhYmxlIDwtIGRhdGF0YWJsZSgNCiAgdG9wXzEwX3hURCwgICMgQXNzdW1pbmcgJ2JvdHRvbV8xMCcgY29udGFpbnMgdGhlIGJvdHRvbSAxMCBVbmx1Y2tpZXN0IHBsYXllcnMNCiAgb3B0aW9ucyA9IGxpc3QoDQogICAgcGFnZUxlbmd0aCA9IDEwLA0KICAgIGRvbSA9ICd0JywgICMgS2VlcCB0aGUgdGFibGUgb25seTsgbm8gY29udHJvbHMNCiAgICBvcmRlcmluZyA9IEZBTFNFDQogICksDQogIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbigNCiAgICBzdHlsZSA9ICdjYXB0aW9uLXNpZGU6IHRvcDsgZm9udC13ZWlnaHQ6IGJvbGQ7IHRleHQtYWxpZ246IGNlbnRlcjsgZm9udC1zaXplOiAxNnB4OycsDQogICAgJ1RvcCAxMCB4VEQgU2Vhc29ucyBieSBQbGF5ZXJzIFNpbmNlIDE5OTknKQ0KKSAlPiUNCiAgZm9ybWF0U3R5bGUoDQogICAgY29sdW1ucyA9IGMoJ1BsYXllcicsICdZZWFyJywgJ1RvdWNoZG93bnMnLCAnSmFtZXNfeFREJywgJ0V4cGVjdGVkIFREIC0gVEQgRGlmZicpLA0KICAgIGZvbnRXZWlnaHQgPSAnbm9ybWFsJywNCiAgICBmb250U2l6ZSA9ICcxNHB4JywNCiAgICBiYWNrZ3JvdW5kQ29sb3IgPSAndHJhbnNwYXJlbnQnLA0KICAgIGJvcmRlckJvdHRvbSA9ICcxcHggc29saWQgI0RERCcNCiAgKQ0KDQojIFJlbmRlciB0aGUgdGFibGVzIGluIFIgTWFya2Rvd24gb3IgYW4gUiBIVE1MIHdpZGdldA0KbHVja2llc3RfdGFibGUNCnVubHVja2llc3RfdGFibGUNCmJlc3RfdGFibGUNCg0KYGBgDQoNCmBgYHtyfQ0KZXhwZWN0ZWRfdGRfc3VtbWFyeV90ZWFtIDwtIGNvbWJpbmVkX2RhdGEgJT4lDQogIGdyb3VwX2J5KHBvc3RlYW0pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgdG91Y2hkb3ducyA9IHN1bSh0b3VjaGRvd24sIG5hLnJtID0gVFJVRSksICAjIEVuc3VyZSBOQXMgYXJlIGlnbm9yZWQNCiAgICBKYW1lc194VEQgPSBzdW0oSmFtZXNfeFRELCBuYS5ybSA9IFRSVUUpLCAgICMgSWdub3JlIE5Bcw0KICAgIC5ncm91cHMgPSAnZHJvcCcNCiAgKSAlPiUgDQogIG11dGF0ZSgNCiAgICB4VERfVERfZGVmaWNpdCA9IHRvdWNoZG93bnMgLSBKYW1lc194VEQsICAjIENhbGN1bGF0ZSB0aGUgZGVmaWNpdA0KICAgIEphbWVzX3hURCA9IHNwcmludGYoIiUuMmYiLCBKYW1lc194VEQpLCAgICMgRm9ybWF0IEphbWVzX3hURCB0byAyIGRlY2ltYWwgcGxhY2VzDQogICAgeFREX1REX2RlZmljaXQgPSBzcHJpbnRmKCIlLjJmIiwgeFREX1REX2RlZmljaXQpICAjIEZvcm1hdCB0aGUgZGVmaWNpdA0KICApDQoNCiMgR3JvdXAgYnkgcG9zdGVhbSBhbmQgeWVhciwgYW5kIGNhbGN1bGF0ZSBzdW0sIGhhbmRsaW5nIE5Bcw0KZXhwZWN0ZWRfdGRfc3VtbWFyeV9ieV95ZWFyX3RlYW0gPC0gY29tYmluZWRfZGF0YSAlPiUNCiAgZ3JvdXBfYnkocG9zdGVhbSwgeWVhcikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICB0b3VjaGRvd25zID0gc3VtKHRvdWNoZG93biwgbmEucm0gPSBUUlVFKSwgICMgRW5zdXJlIE5BcyBhcmUgaWdub3JlZA0KICAgIEphbWVzX3hURCA9IHN1bShKYW1lc194VEQsIG5hLnJtID0gVFJVRSksICAgIyBJZ25vcmUgTkFzDQogICAgLmdyb3VwcyA9ICdkcm9wJw0KICApICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHBvc3RlYW0pKSAlPiUgICMgRW5zdXJlIHBvc3RlYW0gaXMgbm90IE5BDQogIG11dGF0ZSgNCiAgICB4VERfVERfZGVmaWNpdCA9IHRvdWNoZG93bnMgLSBKYW1lc194VEQsICAjIENhbGN1bGF0ZSB0aGUgZGVmaWNpdA0KICAgIEphbWVzX3hURCA9IHNwcmludGYoIiUuMmYiLCBKYW1lc194VEQpLCAgICMgRm9ybWF0IEphbWVzX3hURCB0byAyIGRlY2ltYWwgcGxhY2VzDQogICAgeFREX1REX2RlZmljaXQgPSBzcHJpbnRmKCIlLjJmIiwgeFREX1REX2RlZmljaXQpICAjIEZvcm1hdCB0aGUgZGVmaWNpdA0KICApDQoNCiMgVmlldyB0aGUgcmVzdWx0DQpleHBlY3RlZF90ZF9zdW1tYXJ5X2J5X3llYXJfdGVhbQ0KDQpgYGANCg0KYGBge3J9DQpleHBlY3RlZF90ZF9zdW1tYXJ5X2J5X3llYXJfdGVhbV8yMDI0IDwtIGV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhcl90ZWFtICU+JQ0KICBmaWx0ZXIoeWVhcj09MjAyNCkgJT4lDQogIGFycmFuZ2UoZGVzYyh4VERfVERfZGVmaWNpdCkpDQoNCmV4cGVjdGVkX3RkX3N1bW1hcnlfYnlfeWVhcl90ZWFtXzIwMjQNCg0KYGBgDQoNCg==