Going to try working with Statsbomb Data. First, will need to install Statsbombr which is their parsing package.

Will need to do this through Github, as this won’t be in the packages under tools. Will have to use the devtools library in order to achieve this using the code below:

(For full Statsbomb R Walkthrough, navigate through the browser here: https://statsbomb.com/wp-content/uploads/2021/11/Working-with-R.pdf)

library(tidyverse)
library(ggplot2)
library(devtools)
Warning: package ‘devtools’ was built under R version 4.2.3Loading required package: usethis
devtools::install_github("statsbomb/SDMTools") 
Skipping install of 'SDMTools' from a github remote, the SHA1 (4f193c85) has not changed since last install.
  Use `force = TRUE` to force installation
devtools::install_github("statsbomb/StatsBombR")
Skipping install of 'StatsBombR' from a github remote, the SHA1 (f0503b80) has not changed since last install.
  Use `force = TRUE` to force installation
devtools::install_github("Torvaney/ggsoccer")
Skipping install of 'ggsoccer' from a github remote, the SHA1 (3fe317f3) has not changed since last install.
  Use `force = TRUE` to force installation

Next we need to load the soccer libraries now that we’ve downloaded them

library(StatsBombR)
Loading required package: stringi
Warning: package ‘stringi’ was built under R version 4.2.2Loading required package: rvest
Warning: package ‘rvest’ was built under R version 4.2.2
Attaching package: ‘rvest’

The following object is masked from ‘package:readr’:

    guess_encoding

Loading required package: RCurl
Warning: package ‘RCurl’ was built under R version 4.2.3
Attaching package: ‘RCurl’

The following object is masked from ‘package:tidyr’:

    complete

Loading required package: doParallel
Warning: package ‘doParallel’ was built under R version 4.2.3Loading required package: foreach
Warning: package ‘foreach’ was built under R version 4.2.2
Attaching package: ‘foreach’

The following objects are masked from ‘package:purrr’:

    accumulate, when

Loading required package: iterators
Warning: package ‘iterators’ was built under R version 4.2.2Loading required package: parallel
Loading required package: httr
Warning: package ‘httr’ was built under R version 4.2.3Loading required package: jsonlite
Warning: package ‘jsonlite’ was built under R version 4.2.3
Attaching package: ‘jsonlite’

The following object is masked from ‘package:purrr’:

    flatten

Loading required package: sp
Warning: package ‘sp’ was built under R version 4.2.3Warning: replacing previous import ‘foreach::when’ by ‘purrr::when’ when loading ‘StatsBombR’Warning: replacing previous import ‘jsonlite::flatten’ by ‘purrr::flatten’ when loading ‘StatsBombR’Warning: replacing previous import ‘foreach::accumulate’ by ‘purrr::accumulate’ when loading ‘StatsBombR’
library(ggsoccer)

Filter the competition we want to look at

freecomp <- FreeCompetitions()
[1] "Whilst we are keen to share data and facilitate research, we also urge you to be responsible with the data. Please credit StatsBomb as your data source when using the data and visit https://statsbomb.com/media-pack/ to obtain our logos for public use."
competitions <- FreeCompetitions() %>%
  filter(competition_id == 43, season_id==106)
[1] "Whilst we are keen to share data and facilitate research, we also urge you to be responsible with the data. Please credit StatsBomb as your data source when using the data and visit https://statsbomb.com/media-pack/ to obtain our logos for public use."
str(competitions)
'data.frame':   1 obs. of  12 variables:
 $ competition_id           : int 43
 $ season_id                : int 106
 $ country_name             : chr "International"
 $ competition_name         : chr "FIFA World Cup"
 $ competition_gender       : chr "male"
 $ competition_youth        : logi FALSE
 $ competition_international: logi TRUE
 $ season_name              : chr "2022"
 $ match_updated            : chr "2023-11-05T04:23:26.649917"
 $ match_updated_360        : chr "2023-11-21T15:37:11.589616"
 $ match_available_360      : chr "2023-11-21T15:37:11.589616"
 $ match_available          : chr "2023-11-05T04:23:26.649917"
write.csv(freecomp, "C:\\Users\\jmo5660\\School\\Statsbomb\\competitions.csv", row.names = FALSE)

Now we want to look at matches in the season

matches <- FreeMatches(Competitions = competitions
)
[1] "Whilst we are keen to share data and facilitate research, we also urge you to be responsible with the data. Please credit StatsBomb as your data source when using the data and visit https://statsbomb.com/media-pack/ to obtain our logos for public use."

Now let’s look at the event data

non_clean_events <- free_allevents(MatchesDF = matches, Parallel = T)
[1] "Whilst we are keen to share data and facilitate research, we also urge you to be responsible with the data. Please credit StatsBomb as your data source when using the data and visit https://statsbomb.com/media-pack/ to obtain our logos for public use."
# Convert nested lists to character strings
compatible_columns <-sapply(non_clean_events, function(col) is.atomic(col) && !is.list(col))
non_clean_events2 <- non_clean_events[, compatible_columns, drop=FALSE]


write.csv(non_clean_events2, "C:\\Users\\jmo5660\\School\\Statsbomb\\all_unclean_free_event_data_2022_FifaWorldCup.csv", row.names = FALSE)

#clean the events based on the provided statsbomb function
events = allclean(non_clean_events)
Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(id)`Joining with `by = join_by(period, match_id)`
compatible_columns <-sapply(events, function(col) is.atomic(col) && !is.list(col))
subset_df <- events[, compatible_columns, drop=FALSE]


# Assuming you have loaded the StatsBomb event data into a dataframe called 'events'

# Convert timestamp column to numeric format
events$timestamp <- as.numeric(events$timestamp)
Warning: NAs introduced by coercion
# Sort the events dataframe by the timestamp column
events <- events[order(events$timestamp), ]

# Create a new column to store the duration of each possession
events$possession_duration <- NA

# Initialize variables
current_possession <- 1
possession_start_time <- events$timestamp[1]

# Iterate through the events dataframe
for (i in 2:length(events$timestamp)) {
  # Check if possession changes (based on possession_team.id)
  if (events$possession_team.id[i] != events$possession_team.id[i-1]) {
    # Calculate the duration of the previous possession
    possession_duration <- events$timestamp[i-1] - possession_start_time
    # Update the duration column for the previous possession
    events$possession_duration[events$possession_team.id == current_possession] <- possession_duration
    
    # Reset variables for the new possession
    current_possession <- events$possession_team.id[i]
    possession_start_time <- events$timestamp[i]
  }
}

# Calculate the duration of the last possession (if exists)
possession_duration <- events$timestamp[length(events$timestamp)] - possession_start_time
events$possession_duration[events$possession_team.id == current_possession] <- possession_duration



write.csv(subset_df, "C:\\Users\\jmo5660\\School\\Statsbomb\\clean_events.csv")

Player Summary Statistics (Need to do a join on all summaries)

summary <- events %>%
  group_by(events$player.name) %>%
  summarise(
    "Carries" = sum(type.name == "Carry", na.rm = TRUE),
    "Time of Possession" = sum(duration, na.rm = TRUE),
    "Shots" = sum(type.name == "Shot", na.rm = TRUE),
    "Passes" = sum(type.name == "Pass", na.rm = TRUE),
    "Pressures" = sum(type.name == "Pressure", na.rm = TRUE),
    "Fouls_Won" = sum(type.name == "Foul Won", na.rm = TRUE),
    "Interceptions" = sum(type.name == "Interception", na.rm = TRUE),
    "Dispossessed" = sum(type.name == "Dispossessed", na.rm = TRUE),
    "Blocks" = sum(type.name == "Block", na.rm = TRUE),
    "xG" = sum(shot.statsbomb_xg, na.rm=TRUE),
    "Total_Passes_Length" = sum(pass.length, na.rm=TRUE),
    "Nutmegs" = sum(dribble.nutmeg == "TRUE", na.rm=TRUE),
    "Assists" = sum(pass.shot_assist == "TRUE", na.rm = TRUE),
    "Matches_Played" = n_distinct(match_id),
    "Interceptions_Won" = sum(interception.outcome.name == "Won", na.rm=TRUE),
    "Duel_Won" = sum(duel.outcome.name == "Success In Play", na.rm=TRUE),
    "Duel_Lost" = sum(duel.outcome.name == "Lost In Play", na.rm=TRUE),
    "Complete_Dribbles" = sum(dribble.outcome.name == "Complete", na.rm=TRUE),
    "Incomplete_Dribbles" = sum(dribble.outcome.name == "Incomplete", na.rm=TRUE),
  )



summary_goal_outcome <- events %>%
  group_by(player.name) %>%
  summarise(
    Goals = sum(shot.outcome.name == "Goal", na.rm = TRUE),
    `Shots Saved to Post` = sum(shot.outcome.name == "Saved to Post", na.rm = TRUE),
    Shots_Blocked = sum(shot.outcome.name == "Blocked", na.rm = TRUE),
    `Shots Off T` = sum(shot.outcome.name == "Off T", na.rm = TRUE),
    Shots_Hit_Post = sum(shot.outcome.name == "Post", na.rm = TRUE),
    Shots_Saved = sum(shot.outcome.name == "Saved", na.rm = TRUE),
    `Shots Saved Off Target` = sum(shot.outcome.name == "Saved Off Target", na.rm = TRUE),
    Shots_Wayward = sum(shot.outcome.name == "Wayward", na.rm = TRUE)
  )


joined_player_summary <- inner_join(summary, summary_goal_outcome, by = c("events$player.name" = "player.name"))%>% 
  mutate(across(c("Dispossessed", "Carries"), as.numeric),
         dispossessed_per_carry = Dispossessed / Carries) %>% 
  mutate(interception_per_pressure = Interceptions / Pressures)%>% 
  mutate(fouls_won_per_Carry = Fouls_Won / Carries)%>% 
  mutate(blocks_per_pressure = Blocks / Pressures)%>%
  mutate(goals_per_shot = Goals / Shots) %>%
  mutate(Average_Pass_Length = Total_Passes_Length/Passes) %>%
  mutate(blocks_per_match_played = Blocks/Matches_Played) %>%
  mutate(interceptions_per_match = Interceptions/Matches_Played) %>%
  mutate(interception_won_per_pressure = Interceptions_Won / Pressures)%>% 
  mutate(interceptions_won_per_match = Interceptions_Won/Matches_Played) %>%
  mutate(G_xG_Diff = Goals - xG) %>%
  mutate(Duel_Win_Percentage = Duel_Won/(Duel_Won+Duel_Lost)) %>%
  mutate(Complete_Dribble_Percentage = Complete_Dribbles/(Complete_Dribbles+Incomplete_Dribbles))
           

write.csv(joined_player_summary, "C:\\Users\\jmo5660\\School\\Statsbomb\\joined_player_summary.csv", row.names = FALSE)

Team Stats – mimics player stats

summary4 <- events %>%
  group_by(events$team.name) %>%
  summarise("Carries" = sum(type.name=="Carry", na.rm=TRUE))


summary5 <- events %>%
  group_by(events$team.name) %>%
  summarise("Time of Possession" = sum(duration, na.rm=TRUE))

summary6 <- events %>%
  group_by(events$team.name) %>%
  summarise("Shots" = sum(type.name=="Shot", na.rm=TRUE))

summary_team_Pass <- events %>%
  group_by(events$team.name) %>%
  summarise("Passes" = sum(type.name=="Pass", na.rm=TRUE))

summary_team_Pressure <- events %>%
  group_by(events$team.name) %>%
  summarise("Pressures" = sum(type.name=="Pressure", na.rm=TRUE))

summary_foulwon <- events %>%
  group_by(events$team.name) %>%
  summarise("Fouls Won" = sum(type.name=="Foul Won", na.rm=TRUE))

summary_interception <- events %>%
  group_by(events$team.name) %>%
  summarise("Interceptions" = sum(type.name=="Interception", na.rm=TRUE))

joined_team_summary <- inner_join(summary4, summary5, by = "events$team.name") %>%
  inner_join(summary6, by = "events$team.name") %>%
  inner_join(summary_team_Pass, by = "events$team.name")  %>%
  inner_join(summary_team_Pressure, by = "events$team.name")%>%
  inner_join(summary_foulwon, by = "events$team.name")%>%
  inner_join(summary_interception, by = "events$team.name")

write.csv(joined_team_summary, "C:\\Users\\jmo5660\\School\\Statsbomb\\joined_team_summary.csv", row.names = FALSE)

Let’s look to see what correlations there are in our dataset – first for players

lm_model <- lm(Interceptions~Pressures, data=joined_player_summary)

summary(lm_model)

Call:
lm(formula = Interceptions ~ Pressures, data = joined_player_summary)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.0808 -1.1029 -0.4027  0.6919 10.2713 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 0.350357   0.117399   2.984  0.00294 ** 
Pressures   0.068411   0.003535  19.352  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.088 on 679 degrees of freedom
Multiple R-squared:  0.3555,    Adjusted R-squared:  0.3545 
F-statistic: 374.5 on 1 and 679 DF,  p-value: < 2.2e-16
plot(joined_player_summary$Pressures, joined_player_summary$Interceptions, xlab="Pressures", ylab="Interceptions", main="Linear Regression for Pressures and Interceptions")
abline(lm_model, col="red")


# Obtain residuals
residuals <- residuals(lm_model)

# Plot the residuals
plot(predict(lm_model), residuals, pch = 16, xlab = "Fitted values", ylab = "Residuals",
     main = "Residuals Plot: Actual vs. Fitted")


# Plot the standardized residuals
std_residuals <- rstandard(lm_model)
plot(predict(lm_model), std_residuals, pch = 16, xlab = "Fitted values", ylab = "Standardized residuals",
     main = "Residuals Plot: Standardized")


library(car)
Warning: package ‘car’ was built under R version 4.2.3Loading required package: carData
Warning: package ‘carData’ was built under R version 4.2.3
Attaching package: ‘car’

The following object is masked from ‘package:dplyr’:

    recode

The following object is masked from ‘package:purrr’:

    some
# Normal probability plot
qqPlot(residuals, main = "Normal Probability Plot: Residuals")
[1]  77 162

Now for Teams

lm_model2 <- lm(Interceptions~Pressures, data=joined_team_summary)

summary(lm_model2)

Call:
lm(formula = Interceptions ~ Pressures, data = joined_team_summary)

Residuals:
     Min       1Q   Median       3Q      Max 
-13.8709  -5.2709  -0.1027   5.0379  20.8449 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 1.069205   3.770672   0.284    0.779    
Pressures   0.080758   0.006799  11.879 7.19e-13 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 7.695 on 30 degrees of freedom
Multiple R-squared:  0.8247,    Adjusted R-squared:  0.8188 
F-statistic: 141.1 on 1 and 30 DF,  p-value: 7.189e-13
plot(joined_team_summary$Pressures, joined_team_summary$Interceptions, xlab="Pressures", ylab="Interceptions", main="Linear Regression for Pressures and Interceptions")
abline(lm_model2, col="red")

Let’s Try to Produce a Carry Map for Christian Pulisic – first let’s filter his data out of the events

library(dplyr)
library(cowplot)
Warning: package ‘cowplot’ was built under R version 4.2.3
Attaching package: ‘cowplot’

The following object is masked from ‘package:lubridate’:

    stamp
# Filter carries for "Christian Pulisic"
pulisic_carries <- events %>%
  filter(player.name == "Christian Pulisic", type.name == "Carry")

# Create a new dataframe with selected columns
pulisic_carry_data <- pulisic_carries %>%
  select(player.name, type.name, location.x, location.y, carry.end_location.x, carry.end_location.y)

Now let’s create the Christian Pulisic Carry map – will need to re-scale the ggsoccer coordinates to statsbomb


plot<-ggplot(pulisic_carry_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = carry.end_location.x, yend =  carry.end_location.y),
               colour = "navy",
               arrow = arrow(length = unit(0.15, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Simple Pulisic  World Cup 2022 Carry Map", 
          "Orange Analytics")
ggplot2::annotate("text", x = Inf, y = -Inf, label = "Data: StatsBomb", hjust = 1, vjust = -1, size = 3, color = "gray50")
mapping: x = ~x, y = ~y 
geom_text: na.rm = FALSE
stat_identity: na.rm = FALSE
position_identity 
logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Now let’s do it again for Pulisic Passes Let’s Try to Produce a Carry Map for Christian Pulisic – first let’s filter his data out of the events


# Filter passes for "Christian Pulisic"
pulisic_passes <- events %>%
  filter(player.name == "Christian Pulisic", type.name == "Pass")

# Create a new dataframe with selected columns
pulisic_pass_data <- pulisic_passes %>%
  select(player.name, type.name, location.x, location.y, pass.end_location.x, pass.end_location.y)

Now let’s create the Christian Pulisic Carry map – will need to re-scale the ggsoccer coordinates from optima to statsbomb

plot <- ggplot(pulisic_pass_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = pass.end_location.x, yend =  pass.end_location.y),
               colour = "navy",
               arrow = arrow(length = unit(0.17, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Simple Pulisic  World Cup 2022 Passmap", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Let’s try to look at pressures for one of the World Cup Darlings…Amrabat

# Filter passes for "Christian Pulisic"
amrabat_pressures <- events %>%
  filter(player.name == "Sofyan Amrabat", type.name == "Pressure")

# Create a new dataframe with selected columns
amrabat_pressure_data <- amrabat_pressures %>%
  select(player.name, type.name, location.x, location.y)

plot <- ggplot(amrabat_pressure_data) +
  annotate_pitch(dimensions = pitch_statsbomb,
                 colour = "white",
                 fill   = "springgreen4",
                 limits = TRUE) +
  geom_point(aes(x = location.x, y = location.y),
             colour = "maroon",
             size = 3) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Amrabat Pressure Map",
          "Fifa World Cup 2022 Statsbomb Data")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Ziyech

# Filter pressures for Ziyech
ziyech_pressures <- events %>%
  filter(player.name == "Hakim Ziyech", type.name == "Pressure")

# Create a new dataframe with selected columns
ziyech_pressure_data <- ziyech_pressures %>%
  select(player.name, type.name, location.x, location.y)

plot<- ggplot(ziyech_pressure_data) +
  annotate_pitch(dimensions = pitch_statsbomb,
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_point(aes(x = location.x, y = location.y),
             colour = "maroon",
             size = 3) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Ziyech Pressure Map",
          "Fifa World Cup 2022 Statsbomb Data")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Let’s take a good look at Azzedine Ounahi

# Filter pressures for Ounahi
Ounahi_pressures <- events %>%
  filter(player.name == "Azzedine Ounahi", type.name == "Pressure")

# Create a new dataframe with selected columns
Ounahi_pressure_data <- Ounahi_pressures %>%
  select(player.name, type.name, location.x, location.y)

plot<- ggplot(Ounahi_pressure_data) +
  annotate_pitch(dimensions = pitch_statsbomb,
                 colour = "white",
                 fill   = "springgreen4",
                 limits = TRUE) +
  geom_point(aes(x = location.x, y = location.y),
             colour = "maroon",
             size = 3) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Azzedine Ounahi Pressure Map",
          "Fifa World Cup 2022 Statsbomb Data")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Azzedine Ounahi – passes

# Filter passses for Ounahi
Ounahi_passes <- events %>%
  filter(player.name == "Azzedine Ounahi", type.name == "Pass")

# Create a new dataframe with selected columns
Ounahi_pass_data <- Ounahi_passes %>%
  select(player.name, type.name, location.x, location.y, pass.end_location.x, pass.end_location.y)

plot <- ggplot(Ounahi_pass_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = TRUE) +
  geom_segment(aes(x = location.x, y = location.y, xend = pass.end_location.x, yend =  pass.end_location.y),
               colour = "maroon",
               arrow = arrow(length = unit(0.17, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Azzedine Ounahi World Cup 2022 Passing Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Azzedine Ounahi – Carries

# Filter passses for Ounahi
Ounahi_Carries <- events %>%
  filter(player.name == "Azzedine Ounahi", type.name == "Carry")

# Create a new dataframe with selected columns
Ounahi_carry_data <- Ounahi_Carries %>%
  select(player.name, type.name, location.x, location.y, carry.end_location.x, carry.end_location.y)

plot <- ggplot(Ounahi_carry_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = carry.end_location.x, yend =  carry.end_location.y),
               colour = "maroon",
               arrow = arrow(length = unit(0.17, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Azzedine Ounahi World Cup 2022 Carry Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot



# Filter carries for "Weston McKennie"
Mckkenie_carries <- events %>%
  filter(player.name == "Weston McKennie", type.name == "Carry")

# Create a new dataframe with selected columns
Mckennie_carry_data <- Mckkenie_carries %>%
  select(player.name, type.name, location.x, location.y, carry.end_location.x, carry.end_location.y)

Now let’s create the Christian Pulisic Carry map – will need to re-scale the ggsoccer coordinates to statsbomb


plot<-ggplot(Mckennie_carry_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = carry.end_location.x, yend =  carry.end_location.y),
               colour = "navy",
               arrow = arrow(length = unit(0.15, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Weston McKennie World Cup 2022 Carry Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Now let’s do it again for Pulisic Passes Let’s Try to Produce a Carry Map for Christian Pulisic – first let’s filter his data out of the events


# Filter passes for "Weston McKennie"
mckkenie_passes <- events %>%
  filter(player.name == "Weston McKennie", type.name == "Pass")

# Create a new dataframe with selected columns
mckkenie_pass_data <- mckkenie_passes %>%
  select(player.name, type.name, location.x, location.y, pass.end_location.x, pass.end_location.y)

Now let’s create the mckkenie passing map – will need to re-scale the ggsoccer coordinates from optima to statsbomb

plot <- ggplot(mckkenie_pass_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = pass.end_location.x, yend =  pass.end_location.y),
               colour = "navy",
               arrow = arrow(length = unit(0.17, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Weston Mckennie 2022 World Cup Passing Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

# Filter passes for "Victor Nelsson"
Nelsson_pressures <- events %>%
  filter(player.name == "Victor Nelsson", type.name == "Pressure")

# Create a new dataframe with selected columns
Nelsson_pressure_data <- Nelsson_pressures %>%
  select(player.name, type.name, location.x, location.y)

plot <- ggplot(Nelsson_pressure_data) +
  annotate_pitch(dimensions = pitch_statsbomb,
                 colour = "white",
                 fill   = "springgreen4",
                 limits = TRUE) +
  geom_point(aes(x = location.x, y = location.y),
             colour = "pink",
             size = 3) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Victor Nelson Pressure Map",
          "Fifa World Cup 2022 Statsbomb Data")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

# Filter passes for "Victor Nelsson"
Nelsson_passes<- events %>%
  filter(player.name == "Victor Nelsson", type.name == "Pass")

# Create a new dataframe with selected columns
Nelsson_passing_data <- Nelsson_passes %>%
  select(player.name, type.name, location.x, location.y, pass.end_location.x, pass.end_location.y)

plot <- ggplot(Nelsson_passing_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = pass.end_location.x, yend =  pass.end_location.y),
               colour = "pink",
               arrow = arrow(length = unit(0.17, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Victor Nelsson 2022 World Cup Passing Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Let’s Look at Enzo Fernandez

# Filter passes for "Enzo Fernandez"
player_passes<- events %>%
  filter(player.name == "Enzo Fernandez", type.name == "Pass")

# Create a new dataframe with selected columns
player_passing_data <- player_passes %>%
  select(player.name, type.name, location.x, location.y, pass.end_location.x, pass.end_location.y)

plot <- ggplot(player_passing_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = pass.end_location.x, yend =  pass.end_location.y),
               colour = "light blue",
               arrow = arrow(length = unit(0.17, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Enzo Fernandez 2022 World Cup Passing Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

# Filter passes for "Enzo Fernandez"
player_carries<- events %>%
  filter(player.name == "Enzo Fernandez", type.name == "Carry")

# Create a new dataframe with selected columns
player_carry_data <- player_carries %>%
  select(player.name, type.name, location.x, location.y, carry.end_location.x, carry.end_location.y)

plot <- ggplot(player_carry_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = carry.end_location.x, yend =  carry.end_location.y),
               colour = "light blue",
               arrow = arrow(length = unit(0.17, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Enzo Fernandez 2022 World Cup Carry Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Let’s Look at Tyler Adams

# Filter passes for "Tyler Adams"
player_passes<- events %>%
  filter(player.name == "Tyler Adams", type.name == "Pass")

# Create a new dataframe with selected columns
player_passing_data <- player_passes %>%
  select(player.name, type.name, location.x, location.y, pass.end_location.x, pass.end_location.y)

plot <- ggplot(player_passing_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_segment(aes(x = location.x, y = location.y, xend = pass.end_location.x, yend =  pass.end_location.y),
               colour = "light blue",
               arrow = arrow(length = unit(0.17, "cm"),
                             type = "closed")) +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Tyler Adams 2022 World Cup Passing Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

Let’s Look at Enzo Fernandez

# Filter passes for "Tyler Adams"
player_pressures<- events %>%
  filter(player.name == "Tyler Adams", type.name == "Pressure")

# Create a new dataframe with selected columns
player_pressure_data <- player_pressures %>%
  select(player.name, type.name, location.x, location.y, pass.end_location.x, pass.end_location.y)

plot <- ggplot(player_pressure_data) +
  annotate_pitch(dimensions = pitch_statsbomb, 
                 colour = "white",
                 fill   = "springgreen4",
                 limits = FALSE) +
  geom_point(aes(x = location.x, y = location.y),
               colour = "navy") +
  theme_pitch() +
  direction_label(colour = "white")+
  theme(panel.background = element_rect(fill = "springgreen4")) +
  ggtitle("Tyler Adams 2022 World Cup Pressure Map", 
          "Orange Analytics")

logo <- ggdraw() +
  draw_image("C:\\Users\\jmo5660\\School\\Statsbomb\\StatsBomb Logo\\1. Colour positive\\SB - Icon Lockup - Colour positive.png",x = 0.01, y = 0.001, width = 0.15, height = 0.15)

# Combine the plot and the logo
final_plot <- plot + annotation_custom(ggplotGrob(logo))

# Display the final plot
final_plot

DQotLS0NCnRpdGxlOiAiU3RhdHNib21iIERhdGEgLS0+IFBhc3NpbmcgTWFwL1ByZXNzdXJlIE1hcCB2aXN1YWxpemF0aW9ucyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkdvaW5nIHRvIHRyeSB3b3JraW5nIHdpdGggU3RhdHNib21iIERhdGEuIEZpcnN0LCB3aWxsIG5lZWQgdG8gaW5zdGFsbCBTdGF0c2JvbWJyIHdoaWNoIGlzIHRoZWlyIHBhcnNpbmcgcGFja2FnZS4NCg0KV2lsbCBuZWVkIHRvIGRvIHRoaXMgdGhyb3VnaCBHaXRodWIsIGFzIHRoaXMgd29uJ3QgYmUgaW4gdGhlIHBhY2thZ2VzIHVuZGVyIHRvb2xzLiBXaWxsIGhhdmUgdG8gdXNlIHRoZSBkZXZ0b29scyBsaWJyYXJ5IGluIG9yZGVyIHRvIGFjaGlldmUgdGhpcyB1c2luZyB0aGUgY29kZSBiZWxvdzoNCg0KKEZvciBmdWxsIFN0YXRzYm9tYiBSIFdhbGt0aHJvdWdoLCBuYXZpZ2F0ZSB0aHJvdWdoIHRoZSBicm93c2VyIGhlcmU6IGh0dHBzOi8vc3RhdHNib21iLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAyMS8xMS9Xb3JraW5nLXdpdGgtUi5wZGYpDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRldnRvb2xzKQ0KDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInN0YXRzYm9tYi9TRE1Ub29scyIpIA0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJzdGF0c2JvbWIvU3RhdHNCb21iUiIpDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIlRvcnZhbmV5L2dnc29jY2VyIikNCmBgYA0KDQpOZXh0IHdlIG5lZWQgdG8gbG9hZCB0aGUgc29jY2VyIGxpYnJhcmllcyBub3cgdGhhdCB3ZSd2ZSBkb3dubG9hZGVkIHRoZW0NCmBgYHtyfQ0KbGlicmFyeShTdGF0c0JvbWJSKQ0KbGlicmFyeShnZ3NvY2NlcikNCmBgYA0KDQpGaWx0ZXIgdGhlIGNvbXBldGl0aW9uIHdlIHdhbnQgdG8gbG9vayBhdCANCmBgYHtyfQ0KZnJlZWNvbXAgPC0gRnJlZUNvbXBldGl0aW9ucygpDQoNCg0KY29tcGV0aXRpb25zIDwtIEZyZWVDb21wZXRpdGlvbnMoKSAlPiUNCiAgZmlsdGVyKGNvbXBldGl0aW9uX2lkID09IDQzLCBzZWFzb25faWQ9PTEwNikNCg0Kc3RyKGNvbXBldGl0aW9ucykNCg0Kd3JpdGUuY3N2KGZyZWVjb21wLCAiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcY29tcGV0aXRpb25zLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQpOb3cgd2Ugd2FudCB0byBsb29rIGF0IG1hdGNoZXMgaW4gdGhlIHNlYXNvbg0KYGBge3J9DQptYXRjaGVzIDwtIEZyZWVNYXRjaGVzKENvbXBldGl0aW9ucyA9IGNvbXBldGl0aW9ucw0KKQ0KDQpgYGANCk5vdyBsZXQncyBsb29rIGF0IHRoZSBldmVudCBkYXRhDQpgYGB7cn0NCm5vbl9jbGVhbl9ldmVudHMgPC0gZnJlZV9hbGxldmVudHMoTWF0Y2hlc0RGID0gbWF0Y2hlcywgUGFyYWxsZWwgPSBUKQ0KDQojIENvbnZlcnQgbmVzdGVkIGxpc3RzIHRvIGNoYXJhY3RlciBzdHJpbmdzDQpjb21wYXRpYmxlX2NvbHVtbnMgPC1zYXBwbHkobm9uX2NsZWFuX2V2ZW50cywgZnVuY3Rpb24oY29sKSBpcy5hdG9taWMoY29sKSAmJiAhaXMubGlzdChjb2wpKQ0Kbm9uX2NsZWFuX2V2ZW50czIgPC0gbm9uX2NsZWFuX2V2ZW50c1ssIGNvbXBhdGlibGVfY29sdW1ucywgZHJvcD1GQUxTRV0NCg0KDQp3cml0ZS5jc3Yobm9uX2NsZWFuX2V2ZW50czIsICJDOlxcVXNlcnNcXGptbzU2NjBcXFNjaG9vbFxcU3RhdHNib21iXFxhbGxfdW5jbGVhbl9mcmVlX2V2ZW50X2RhdGFfMjAyMl9GaWZhV29ybGRDdXAuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQoNCiNjbGVhbiB0aGUgZXZlbnRzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBzdGF0c2JvbWIgZnVuY3Rpb24NCmV2ZW50cyA9IGFsbGNsZWFuKG5vbl9jbGVhbl9ldmVudHMpDQpjb21wYXRpYmxlX2NvbHVtbnMgPC1zYXBwbHkoZXZlbnRzLCBmdW5jdGlvbihjb2wpIGlzLmF0b21pYyhjb2wpICYmICFpcy5saXN0KGNvbCkpDQpzdWJzZXRfZGYgPC0gZXZlbnRzWywgY29tcGF0aWJsZV9jb2x1bW5zLCBkcm9wPUZBTFNFXQ0KDQoNCiMgQXNzdW1pbmcgeW91IGhhdmUgbG9hZGVkIHRoZSBTdGF0c0JvbWIgZXZlbnQgZGF0YSBpbnRvIGEgZGF0YWZyYW1lIGNhbGxlZCAnZXZlbnRzJw0KDQojIENvbnZlcnQgdGltZXN0YW1wIGNvbHVtbiB0byBudW1lcmljIGZvcm1hdA0KZXZlbnRzJHRpbWVzdGFtcCA8LSBhcy5udW1lcmljKGV2ZW50cyR0aW1lc3RhbXApDQoNCiMgU29ydCB0aGUgZXZlbnRzIGRhdGFmcmFtZSBieSB0aGUgdGltZXN0YW1wIGNvbHVtbg0KZXZlbnRzIDwtIGV2ZW50c1tvcmRlcihldmVudHMkdGltZXN0YW1wKSwgXQ0KDQojIENyZWF0ZSBhIG5ldyBjb2x1bW4gdG8gc3RvcmUgdGhlIGR1cmF0aW9uIG9mIGVhY2ggcG9zc2Vzc2lvbg0KZXZlbnRzJHBvc3Nlc3Npb25fZHVyYXRpb24gPC0gTkENCg0KIyBJbml0aWFsaXplIHZhcmlhYmxlcw0KY3VycmVudF9wb3NzZXNzaW9uIDwtIDENCnBvc3Nlc3Npb25fc3RhcnRfdGltZSA8LSBldmVudHMkdGltZXN0YW1wWzFdDQoNCiMgSXRlcmF0ZSB0aHJvdWdoIHRoZSBldmVudHMgZGF0YWZyYW1lDQpmb3IgKGkgaW4gMjpsZW5ndGgoZXZlbnRzJHRpbWVzdGFtcCkpIHsNCiAgIyBDaGVjayBpZiBwb3NzZXNzaW9uIGNoYW5nZXMgKGJhc2VkIG9uIHBvc3Nlc3Npb25fdGVhbS5pZCkNCiAgaWYgKGV2ZW50cyRwb3NzZXNzaW9uX3RlYW0uaWRbaV0gIT0gZXZlbnRzJHBvc3Nlc3Npb25fdGVhbS5pZFtpLTFdKSB7DQogICAgIyBDYWxjdWxhdGUgdGhlIGR1cmF0aW9uIG9mIHRoZSBwcmV2aW91cyBwb3NzZXNzaW9uDQogICAgcG9zc2Vzc2lvbl9kdXJhdGlvbiA8LSBldmVudHMkdGltZXN0YW1wW2ktMV0gLSBwb3NzZXNzaW9uX3N0YXJ0X3RpbWUNCiAgICAjIFVwZGF0ZSB0aGUgZHVyYXRpb24gY29sdW1uIGZvciB0aGUgcHJldmlvdXMgcG9zc2Vzc2lvbg0KICAgIGV2ZW50cyRwb3NzZXNzaW9uX2R1cmF0aW9uW2V2ZW50cyRwb3NzZXNzaW9uX3RlYW0uaWQgPT0gY3VycmVudF9wb3NzZXNzaW9uXSA8LSBwb3NzZXNzaW9uX2R1cmF0aW9uDQogICAgDQogICAgIyBSZXNldCB2YXJpYWJsZXMgZm9yIHRoZSBuZXcgcG9zc2Vzc2lvbg0KICAgIGN1cnJlbnRfcG9zc2Vzc2lvbiA8LSBldmVudHMkcG9zc2Vzc2lvbl90ZWFtLmlkW2ldDQogICAgcG9zc2Vzc2lvbl9zdGFydF90aW1lIDwtIGV2ZW50cyR0aW1lc3RhbXBbaV0NCiAgfQ0KfQ0KDQojIENhbGN1bGF0ZSB0aGUgZHVyYXRpb24gb2YgdGhlIGxhc3QgcG9zc2Vzc2lvbiAoaWYgZXhpc3RzKQ0KcG9zc2Vzc2lvbl9kdXJhdGlvbiA8LSBldmVudHMkdGltZXN0YW1wW2xlbmd0aChldmVudHMkdGltZXN0YW1wKV0gLSBwb3NzZXNzaW9uX3N0YXJ0X3RpbWUNCmV2ZW50cyRwb3NzZXNzaW9uX2R1cmF0aW9uW2V2ZW50cyRwb3NzZXNzaW9uX3RlYW0uaWQgPT0gY3VycmVudF9wb3NzZXNzaW9uXSA8LSBwb3NzZXNzaW9uX2R1cmF0aW9uDQoNCg0KDQp3cml0ZS5jc3Yoc3Vic2V0X2RmLCAiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcY2xlYW5fZXZlbnRzLmNzdiIpDQpgYGANClBsYXllciBTdW1tYXJ5IFN0YXRpc3RpY3MgKE5lZWQgdG8gZG8gYSBqb2luIG9uIGFsbCBzdW1tYXJpZXMpDQpgYGB7cn0NCnN1bW1hcnkgPC0gZXZlbnRzICU+JQ0KICBncm91cF9ieShldmVudHMkcGxheWVyLm5hbWUpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgIkNhcnJpZXMiID0gc3VtKHR5cGUubmFtZSA9PSAiQ2FycnkiLCBuYS5ybSA9IFRSVUUpLA0KICAgICJUaW1lIG9mIFBvc3Nlc3Npb24iID0gc3VtKGR1cmF0aW9uLCBuYS5ybSA9IFRSVUUpLA0KICAgICJTaG90cyIgPSBzdW0odHlwZS5uYW1lID09ICJTaG90IiwgbmEucm0gPSBUUlVFKSwNCiAgICAiUGFzc2VzIiA9IHN1bSh0eXBlLm5hbWUgPT0gIlBhc3MiLCBuYS5ybSA9IFRSVUUpLA0KICAgICJQcmVzc3VyZXMiID0gc3VtKHR5cGUubmFtZSA9PSAiUHJlc3N1cmUiLCBuYS5ybSA9IFRSVUUpLA0KICAgICJGb3Vsc19Xb24iID0gc3VtKHR5cGUubmFtZSA9PSAiRm91bCBXb24iLCBuYS5ybSA9IFRSVUUpLA0KICAgICJJbnRlcmNlcHRpb25zIiA9IHN1bSh0eXBlLm5hbWUgPT0gIkludGVyY2VwdGlvbiIsIG5hLnJtID0gVFJVRSksDQogICAgIkRpc3Bvc3Nlc3NlZCIgPSBzdW0odHlwZS5uYW1lID09ICJEaXNwb3NzZXNzZWQiLCBuYS5ybSA9IFRSVUUpLA0KICAgICJCbG9ja3MiID0gc3VtKHR5cGUubmFtZSA9PSAiQmxvY2siLCBuYS5ybSA9IFRSVUUpLA0KICAgICJ4RyIgPSBzdW0oc2hvdC5zdGF0c2JvbWJfeGcsIG5hLnJtPVRSVUUpLA0KICAgICJUb3RhbF9QYXNzZXNfTGVuZ3RoIiA9IHN1bShwYXNzLmxlbmd0aCwgbmEucm09VFJVRSksDQogICAgIk51dG1lZ3MiID0gc3VtKGRyaWJibGUubnV0bWVnID09ICJUUlVFIiwgbmEucm09VFJVRSksDQogICAgIkFzc2lzdHMiID0gc3VtKHBhc3Muc2hvdF9hc3Npc3QgPT0gIlRSVUUiLCBuYS5ybSA9IFRSVUUpLA0KICAgICJNYXRjaGVzX1BsYXllZCIgPSBuX2Rpc3RpbmN0KG1hdGNoX2lkKSwNCiAgICAiSW50ZXJjZXB0aW9uc19Xb24iID0gc3VtKGludGVyY2VwdGlvbi5vdXRjb21lLm5hbWUgPT0gIldvbiIsIG5hLnJtPVRSVUUpLA0KICAgICJEdWVsX1dvbiIgPSBzdW0oZHVlbC5vdXRjb21lLm5hbWUgPT0gIlN1Y2Nlc3MgSW4gUGxheSIsIG5hLnJtPVRSVUUpLA0KICAgICJEdWVsX0xvc3QiID0gc3VtKGR1ZWwub3V0Y29tZS5uYW1lID09ICJMb3N0IEluIFBsYXkiLCBuYS5ybT1UUlVFKSwNCiAgICAiQ29tcGxldGVfRHJpYmJsZXMiID0gc3VtKGRyaWJibGUub3V0Y29tZS5uYW1lID09ICJDb21wbGV0ZSIsIG5hLnJtPVRSVUUpLA0KICAgICJJbmNvbXBsZXRlX0RyaWJibGVzIiA9IHN1bShkcmliYmxlLm91dGNvbWUubmFtZSA9PSAiSW5jb21wbGV0ZSIsIG5hLnJtPVRSVUUpLA0KICApDQoNCg0KDQpzdW1tYXJ5X2dvYWxfb3V0Y29tZSA8LSBldmVudHMgJT4lDQogIGdyb3VwX2J5KHBsYXllci5uYW1lKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIEdvYWxzID0gc3VtKHNob3Qub3V0Y29tZS5uYW1lID09ICJHb2FsIiwgbmEucm0gPSBUUlVFKSwNCiAgICBgU2hvdHMgU2F2ZWQgdG8gUG9zdGAgPSBzdW0oc2hvdC5vdXRjb21lLm5hbWUgPT0gIlNhdmVkIHRvIFBvc3QiLCBuYS5ybSA9IFRSVUUpLA0KICAgIFNob3RzX0Jsb2NrZWQgPSBzdW0oc2hvdC5vdXRjb21lLm5hbWUgPT0gIkJsb2NrZWQiLCBuYS5ybSA9IFRSVUUpLA0KICAgIGBTaG90cyBPZmYgVGAgPSBzdW0oc2hvdC5vdXRjb21lLm5hbWUgPT0gIk9mZiBUIiwgbmEucm0gPSBUUlVFKSwNCiAgICBTaG90c19IaXRfUG9zdCA9IHN1bShzaG90Lm91dGNvbWUubmFtZSA9PSAiUG9zdCIsIG5hLnJtID0gVFJVRSksDQogICAgU2hvdHNfU2F2ZWQgPSBzdW0oc2hvdC5vdXRjb21lLm5hbWUgPT0gIlNhdmVkIiwgbmEucm0gPSBUUlVFKSwNCiAgICBgU2hvdHMgU2F2ZWQgT2ZmIFRhcmdldGAgPSBzdW0oc2hvdC5vdXRjb21lLm5hbWUgPT0gIlNhdmVkIE9mZiBUYXJnZXQiLCBuYS5ybSA9IFRSVUUpLA0KICAgIFNob3RzX1dheXdhcmQgPSBzdW0oc2hvdC5vdXRjb21lLm5hbWUgPT0gIldheXdhcmQiLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KDQpqb2luZWRfcGxheWVyX3N1bW1hcnkgPC0gaW5uZXJfam9pbihzdW1tYXJ5LCBzdW1tYXJ5X2dvYWxfb3V0Y29tZSwgYnkgPSBjKCJldmVudHMkcGxheWVyLm5hbWUiID0gInBsYXllci5uYW1lIikpJT4lIA0KICBtdXRhdGUoYWNyb3NzKGMoIkRpc3Bvc3Nlc3NlZCIsICJDYXJyaWVzIiksIGFzLm51bWVyaWMpLA0KICAgICAgICAgZGlzcG9zc2Vzc2VkX3Blcl9jYXJyeSA9IERpc3Bvc3Nlc3NlZCAvIENhcnJpZXMpICU+JSANCiAgbXV0YXRlKGludGVyY2VwdGlvbl9wZXJfcHJlc3N1cmUgPSBJbnRlcmNlcHRpb25zIC8gUHJlc3N1cmVzKSU+JSANCiAgbXV0YXRlKGZvdWxzX3dvbl9wZXJfQ2FycnkgPSBGb3Vsc19Xb24gLyBDYXJyaWVzKSU+JSANCiAgbXV0YXRlKGJsb2Nrc19wZXJfcHJlc3N1cmUgPSBCbG9ja3MgLyBQcmVzc3VyZXMpJT4lDQogIG11dGF0ZShnb2Fsc19wZXJfc2hvdCA9IEdvYWxzIC8gU2hvdHMpICU+JQ0KICBtdXRhdGUoQXZlcmFnZV9QYXNzX0xlbmd0aCA9IFRvdGFsX1Bhc3Nlc19MZW5ndGgvUGFzc2VzKSAlPiUNCiAgbXV0YXRlKGJsb2Nrc19wZXJfbWF0Y2hfcGxheWVkID0gQmxvY2tzL01hdGNoZXNfUGxheWVkKSAlPiUNCiAgbXV0YXRlKGludGVyY2VwdGlvbnNfcGVyX21hdGNoID0gSW50ZXJjZXB0aW9ucy9NYXRjaGVzX1BsYXllZCkgJT4lDQogIG11dGF0ZShpbnRlcmNlcHRpb25fd29uX3Blcl9wcmVzc3VyZSA9IEludGVyY2VwdGlvbnNfV29uIC8gUHJlc3N1cmVzKSU+JSANCiAgbXV0YXRlKGludGVyY2VwdGlvbnNfd29uX3Blcl9tYXRjaCA9IEludGVyY2VwdGlvbnNfV29uL01hdGNoZXNfUGxheWVkKSAlPiUNCiAgbXV0YXRlKEdfeEdfRGlmZiA9IEdvYWxzIC0geEcpICU+JQ0KICBtdXRhdGUoRHVlbF9XaW5fUGVyY2VudGFnZSA9IER1ZWxfV29uLyhEdWVsX1dvbitEdWVsX0xvc3QpKSAlPiUNCiAgbXV0YXRlKENvbXBsZXRlX0RyaWJibGVfUGVyY2VudGFnZSA9IENvbXBsZXRlX0RyaWJibGVzLyhDb21wbGV0ZV9EcmliYmxlcytJbmNvbXBsZXRlX0RyaWJibGVzKSkNCiAgICAgICAgICAgDQoNCndyaXRlLmNzdihqb2luZWRfcGxheWVyX3N1bW1hcnksICJDOlxcVXNlcnNcXGptbzU2NjBcXFNjaG9vbFxcU3RhdHNib21iXFxqb2luZWRfcGxheWVyX3N1bW1hcnkuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQoNCg0KYGBgDQoNClRlYW0gU3RhdHMgLS0gbWltaWNzIHBsYXllciBzdGF0cw0KYGBge3J9DQpzdW1tYXJ5NCA8LSBldmVudHMgJT4lDQogIGdyb3VwX2J5KGV2ZW50cyR0ZWFtLm5hbWUpICU+JQ0KICBzdW1tYXJpc2UoIkNhcnJpZXMiID0gc3VtKHR5cGUubmFtZT09IkNhcnJ5IiwgbmEucm09VFJVRSkpDQoNCg0Kc3VtbWFyeTUgPC0gZXZlbnRzICU+JQ0KICBncm91cF9ieShldmVudHMkdGVhbS5uYW1lKSAlPiUNCiAgc3VtbWFyaXNlKCJUaW1lIG9mIFBvc3Nlc3Npb24iID0gc3VtKGR1cmF0aW9uLCBuYS5ybT1UUlVFKSkNCg0Kc3VtbWFyeTYgPC0gZXZlbnRzICU+JQ0KICBncm91cF9ieShldmVudHMkdGVhbS5uYW1lKSAlPiUNCiAgc3VtbWFyaXNlKCJTaG90cyIgPSBzdW0odHlwZS5uYW1lPT0iU2hvdCIsIG5hLnJtPVRSVUUpKQ0KDQpzdW1tYXJ5X3RlYW1fUGFzcyA8LSBldmVudHMgJT4lDQogIGdyb3VwX2J5KGV2ZW50cyR0ZWFtLm5hbWUpICU+JQ0KICBzdW1tYXJpc2UoIlBhc3NlcyIgPSBzdW0odHlwZS5uYW1lPT0iUGFzcyIsIG5hLnJtPVRSVUUpKQ0KDQpzdW1tYXJ5X3RlYW1fUHJlc3N1cmUgPC0gZXZlbnRzICU+JQ0KICBncm91cF9ieShldmVudHMkdGVhbS5uYW1lKSAlPiUNCiAgc3VtbWFyaXNlKCJQcmVzc3VyZXMiID0gc3VtKHR5cGUubmFtZT09IlByZXNzdXJlIiwgbmEucm09VFJVRSkpDQoNCnN1bW1hcnlfZm91bHdvbiA8LSBldmVudHMgJT4lDQogIGdyb3VwX2J5KGV2ZW50cyR0ZWFtLm5hbWUpICU+JQ0KICBzdW1tYXJpc2UoIkZvdWxzIFdvbiIgPSBzdW0odHlwZS5uYW1lPT0iRm91bCBXb24iLCBuYS5ybT1UUlVFKSkNCg0Kc3VtbWFyeV9pbnRlcmNlcHRpb24gPC0gZXZlbnRzICU+JQ0KICBncm91cF9ieShldmVudHMkdGVhbS5uYW1lKSAlPiUNCiAgc3VtbWFyaXNlKCJJbnRlcmNlcHRpb25zIiA9IHN1bSh0eXBlLm5hbWU9PSJJbnRlcmNlcHRpb24iLCBuYS5ybT1UUlVFKSkNCg0Kam9pbmVkX3RlYW1fc3VtbWFyeSA8LSBpbm5lcl9qb2luKHN1bW1hcnk0LCBzdW1tYXJ5NSwgYnkgPSAiZXZlbnRzJHRlYW0ubmFtZSIpICU+JQ0KICBpbm5lcl9qb2luKHN1bW1hcnk2LCBieSA9ICJldmVudHMkdGVhbS5uYW1lIikgJT4lDQogIGlubmVyX2pvaW4oc3VtbWFyeV90ZWFtX1Bhc3MsIGJ5ID0gImV2ZW50cyR0ZWFtLm5hbWUiKSAgJT4lDQogIGlubmVyX2pvaW4oc3VtbWFyeV90ZWFtX1ByZXNzdXJlLCBieSA9ICJldmVudHMkdGVhbS5uYW1lIiklPiUNCiAgaW5uZXJfam9pbihzdW1tYXJ5X2ZvdWx3b24sIGJ5ID0gImV2ZW50cyR0ZWFtLm5hbWUiKSU+JQ0KICBpbm5lcl9qb2luKHN1bW1hcnlfaW50ZXJjZXB0aW9uLCBieSA9ICJldmVudHMkdGVhbS5uYW1lIikNCg0Kd3JpdGUuY3N2KGpvaW5lZF90ZWFtX3N1bW1hcnksICJDOlxcVXNlcnNcXGptbzU2NjBcXFNjaG9vbFxcU3RhdHNib21iXFxqb2luZWRfdGVhbV9zdW1tYXJ5LmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCkxldCdzIGxvb2sgdG8gc2VlIHdoYXQgY29ycmVsYXRpb25zIHRoZXJlIGFyZSBpbiBvdXIgZGF0YXNldCAtLSBmaXJzdCBmb3IgcGxheWVycw0KYGBge3J9DQpsbV9tb2RlbCA8LSBsbShJbnRlcmNlcHRpb25zflByZXNzdXJlcywgZGF0YT1qb2luZWRfcGxheWVyX3N1bW1hcnkpDQoNCnN1bW1hcnkobG1fbW9kZWwpDQoNCnBsb3Qoam9pbmVkX3BsYXllcl9zdW1tYXJ5JFByZXNzdXJlcywgam9pbmVkX3BsYXllcl9zdW1tYXJ5JEludGVyY2VwdGlvbnMsIHhsYWI9IlByZXNzdXJlcyIsIHlsYWI9IkludGVyY2VwdGlvbnMiLCBtYWluPSJMaW5lYXIgUmVncmVzc2lvbiBmb3IgUHJlc3N1cmVzIGFuZCBJbnRlcmNlcHRpb25zIikNCmFibGluZShsbV9tb2RlbCwgY29sPSJyZWQiKQ0KDQojIE9idGFpbiByZXNpZHVhbHMNCnJlc2lkdWFscyA8LSByZXNpZHVhbHMobG1fbW9kZWwpDQoNCiMgUGxvdCB0aGUgcmVzaWR1YWxzDQpwbG90KHByZWRpY3QobG1fbW9kZWwpLCByZXNpZHVhbHMsIHBjaCA9IDE2LCB4bGFiID0gIkZpdHRlZCB2YWx1ZXMiLCB5bGFiID0gIlJlc2lkdWFscyIsDQogICAgIG1haW4gPSAiUmVzaWR1YWxzIFBsb3Q6IEFjdHVhbCB2cy4gRml0dGVkIikNCg0KIyBQbG90IHRoZSBzdGFuZGFyZGl6ZWQgcmVzaWR1YWxzDQpzdGRfcmVzaWR1YWxzIDwtIHJzdGFuZGFyZChsbV9tb2RlbCkNCnBsb3QocHJlZGljdChsbV9tb2RlbCksIHN0ZF9yZXNpZHVhbHMsIHBjaCA9IDE2LCB4bGFiID0gIkZpdHRlZCB2YWx1ZXMiLCB5bGFiID0gIlN0YW5kYXJkaXplZCByZXNpZHVhbHMiLA0KICAgICBtYWluID0gIlJlc2lkdWFscyBQbG90OiBTdGFuZGFyZGl6ZWQiKQ0KDQpsaWJyYXJ5KGNhcikNCiMgTm9ybWFsIHByb2JhYmlsaXR5IHBsb3QNCnFxUGxvdChyZXNpZHVhbHMsIG1haW4gPSAiTm9ybWFsIFByb2JhYmlsaXR5IFBsb3Q6IFJlc2lkdWFscyIpDQpgYGANCk5vdyBmb3IgVGVhbXMNCmBgYHtyfQ0KbG1fbW9kZWwyIDwtIGxtKEludGVyY2VwdGlvbnN+UHJlc3N1cmVzLCBkYXRhPWpvaW5lZF90ZWFtX3N1bW1hcnkpDQoNCnN1bW1hcnkobG1fbW9kZWwyKQ0KDQpwbG90KGpvaW5lZF90ZWFtX3N1bW1hcnkkUHJlc3N1cmVzLCBqb2luZWRfdGVhbV9zdW1tYXJ5JEludGVyY2VwdGlvbnMsIHhsYWI9IlByZXNzdXJlcyIsIHlsYWI9IkludGVyY2VwdGlvbnMiLCBtYWluPSJMaW5lYXIgUmVncmVzc2lvbiBmb3IgUHJlc3N1cmVzIGFuZCBJbnRlcmNlcHRpb25zIikNCmFibGluZShsbV9tb2RlbDIsIGNvbD0icmVkIikNCg0KYGBgDQoNCkxldCdzIFRyeSB0byBQcm9kdWNlIGEgQ2FycnkgTWFwIGZvciBDaHJpc3RpYW4gUHVsaXNpYyAtLSBmaXJzdCBsZXQncyBmaWx0ZXIgaGlzIGRhdGEgb3V0IG9mIHRoZSBldmVudHMNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoY293cGxvdCkNCg0KIyBGaWx0ZXIgY2FycmllcyBmb3IgIkNocmlzdGlhbiBQdWxpc2ljIg0KcHVsaXNpY19jYXJyaWVzIDwtIGV2ZW50cyAlPiUNCiAgZmlsdGVyKHBsYXllci5uYW1lID09ICJDaHJpc3RpYW4gUHVsaXNpYyIsIHR5cGUubmFtZSA9PSAiQ2FycnkiKQ0KDQojIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgd2l0aCBzZWxlY3RlZCBjb2x1bW5zDQpwdWxpc2ljX2NhcnJ5X2RhdGEgPC0gcHVsaXNpY19jYXJyaWVzICU+JQ0KICBzZWxlY3QocGxheWVyLm5hbWUsIHR5cGUubmFtZSwgbG9jYXRpb24ueCwgbG9jYXRpb24ueSwgY2FycnkuZW5kX2xvY2F0aW9uLngsIGNhcnJ5LmVuZF9sb2NhdGlvbi55KQ0KDQpgYGANCg0KTm93IGxldCdzIGNyZWF0ZSB0aGUgQ2hyaXN0aWFuIFB1bGlzaWMgQ2FycnkgbWFwIC0tIHdpbGwgbmVlZCB0byByZS1zY2FsZSB0aGUgZ2dzb2NjZXIgY29vcmRpbmF0ZXMgdG8gc3RhdHNib21iDQpgYGB7cn0NCg0KcGxvdDwtZ2dwbG90KHB1bGlzaWNfY2FycnlfZGF0YSkgKw0KICBhbm5vdGF0ZV9waXRjaChkaW1lbnNpb25zID0gcGl0Y2hfc3RhdHNib21iLCANCiAgICAgICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwNCiAgICAgICAgICAgICAgICAgZmlsbCAgID0gInNwcmluZ2dyZWVuNCIsDQogICAgICAgICAgICAgICAgIGxpbWl0cyA9IEZBTFNFKSArDQogIGdlb21fc2VnbWVudChhZXMoeCA9IGxvY2F0aW9uLngsIHkgPSBsb2NhdGlvbi55LCB4ZW5kID0gY2FycnkuZW5kX2xvY2F0aW9uLngsIHllbmQgPSAgY2FycnkuZW5kX2xvY2F0aW9uLnkpLA0KICAgICAgICAgICAgICAgY29sb3VyID0gIm5hdnkiLA0KICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMTUsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImNsb3NlZCIpKSArDQogIHRoZW1lX3BpdGNoKCkgKw0KICBkaXJlY3Rpb25fbGFiZWwoY29sb3VyID0gIndoaXRlIikrDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJzcHJpbmdncmVlbjQiKSkgKw0KICBnZ3RpdGxlKCJTaW1wbGUgUHVsaXNpYyAgV29ybGQgQ3VwIDIwMjIgQ2FycnkgTWFwIiwgDQogICAgICAgICAgIk9yYW5nZSBBbmFseXRpY3MiKQ0KZ2dwbG90Mjo6YW5ub3RhdGUoInRleHQiLCB4ID0gSW5mLCB5ID0gLUluZiwgbGFiZWwgPSAiRGF0YTogU3RhdHNCb21iIiwgaGp1c3QgPSAxLCB2anVzdCA9IC0xLCBzaXplID0gMywgY29sb3IgPSAiZ3JheTUwIikNCg0KbG9nbyA8LSBnZ2RyYXcoKSArDQogIGRyYXdfaW1hZ2UoIkM6XFxVc2Vyc1xcam1vNTY2MFxcU2Nob29sXFxTdGF0c2JvbWJcXFN0YXRzQm9tYiBMb2dvXFwxLiBDb2xvdXIgcG9zaXRpdmVcXFNCIC0gSWNvbiBMb2NrdXAgLSBDb2xvdXIgcG9zaXRpdmUucG5nIix4ID0gMC4wMSwgeSA9IDAuMDAxLCB3aWR0aCA9IDAuMTUsIGhlaWdodCA9IDAuMTUpDQoNCiMgQ29tYmluZSB0aGUgcGxvdCBhbmQgdGhlIGxvZ28NCmZpbmFsX3Bsb3QgPC0gcGxvdCArIGFubm90YXRpb25fY3VzdG9tKGdncGxvdEdyb2IobG9nbykpDQoNCiMgRGlzcGxheSB0aGUgZmluYWwgcGxvdA0KZmluYWxfcGxvdA0KYGBgDQoNCg0KTm93IGxldCdzIGRvIGl0IGFnYWluIGZvciBQdWxpc2ljIFBhc3Nlcw0KTGV0J3MgVHJ5IHRvIFByb2R1Y2UgYSBDYXJyeSBNYXAgZm9yIENocmlzdGlhbiBQdWxpc2ljIC0tIGZpcnN0IGxldCdzIGZpbHRlciBoaXMgZGF0YSBvdXQgb2YgdGhlIGV2ZW50cw0KYGBge3J9DQoNCiMgRmlsdGVyIHBhc3NlcyBmb3IgIkNocmlzdGlhbiBQdWxpc2ljIg0KcHVsaXNpY19wYXNzZXMgPC0gZXZlbnRzICU+JQ0KICBmaWx0ZXIocGxheWVyLm5hbWUgPT0gIkNocmlzdGlhbiBQdWxpc2ljIiwgdHlwZS5uYW1lID09ICJQYXNzIikNCg0KIyBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdpdGggc2VsZWN0ZWQgY29sdW1ucw0KcHVsaXNpY19wYXNzX2RhdGEgPC0gcHVsaXNpY19wYXNzZXMgJT4lDQogIHNlbGVjdChwbGF5ZXIubmFtZSwgdHlwZS5uYW1lLCBsb2NhdGlvbi54LCBsb2NhdGlvbi55LCBwYXNzLmVuZF9sb2NhdGlvbi54LCBwYXNzLmVuZF9sb2NhdGlvbi55KQ0KDQpgYGANCg0KTm93IGxldCdzIGNyZWF0ZSB0aGUgQ2hyaXN0aWFuIFB1bGlzaWMgQ2FycnkgbWFwIC0tIHdpbGwgbmVlZCB0byByZS1zY2FsZSB0aGUgZ2dzb2NjZXIgY29vcmRpbmF0ZXMgZnJvbSBvcHRpbWEgdG8gc3RhdHNib21iDQpgYGB7cn0NCnBsb3QgPC0gZ2dwbG90KHB1bGlzaWNfcGFzc19kYXRhKSArDQogIGFubm90YXRlX3BpdGNoKGRpbWVuc2lvbnMgPSBwaXRjaF9zdGF0c2JvbWIsIA0KICAgICAgICAgICAgICAgICBjb2xvdXIgPSAid2hpdGUiLA0KICAgICAgICAgICAgICAgICBmaWxsICAgPSAic3ByaW5nZ3JlZW40IiwNCiAgICAgICAgICAgICAgICAgbGltaXRzID0gRkFMU0UpICsNCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gbG9jYXRpb24ueCwgeSA9IGxvY2F0aW9uLnksIHhlbmQgPSBwYXNzLmVuZF9sb2NhdGlvbi54LCB5ZW5kID0gIHBhc3MuZW5kX2xvY2F0aW9uLnkpLA0KICAgICAgICAgICAgICAgY29sb3VyID0gIm5hdnkiLA0KICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMTcsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImNsb3NlZCIpKSArDQogIHRoZW1lX3BpdGNoKCkgKw0KICBkaXJlY3Rpb25fbGFiZWwoY29sb3VyID0gIndoaXRlIikrDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJzcHJpbmdncmVlbjQiKSkgKw0KICBnZ3RpdGxlKCJTaW1wbGUgUHVsaXNpYyAgV29ybGQgQ3VwIDIwMjIgUGFzc21hcCIsIA0KICAgICAgICAgICJPcmFuZ2UgQW5hbHl0aWNzIikNCg0KbG9nbyA8LSBnZ2RyYXcoKSArDQogIGRyYXdfaW1hZ2UoIkM6XFxVc2Vyc1xcam1vNTY2MFxcU2Nob29sXFxTdGF0c2JvbWJcXFN0YXRzQm9tYiBMb2dvXFwxLiBDb2xvdXIgcG9zaXRpdmVcXFNCIC0gSWNvbiBMb2NrdXAgLSBDb2xvdXIgcG9zaXRpdmUucG5nIix4ID0gMC4wMSwgeSA9IDAuMDAxLCB3aWR0aCA9IDAuMTUsIGhlaWdodCA9IDAuMTUpDQoNCiMgQ29tYmluZSB0aGUgcGxvdCBhbmQgdGhlIGxvZ28NCmZpbmFsX3Bsb3QgPC0gcGxvdCArIGFubm90YXRpb25fY3VzdG9tKGdncGxvdEdyb2IobG9nbykpDQoNCiMgRGlzcGxheSB0aGUgZmluYWwgcGxvdA0KZmluYWxfcGxvdA0KYGBgDQoNCkxldCdzIHRyeSB0byBsb29rIGF0IHByZXNzdXJlcyBmb3Igb25lIG9mIHRoZSBXb3JsZCBDdXAgRGFybGluZ3MuLi5BbXJhYmF0DQpgYGB7cn0NCiMgRmlsdGVyIHBhc3NlcyBmb3IgIkNocmlzdGlhbiBQdWxpc2ljIg0KYW1yYWJhdF9wcmVzc3VyZXMgPC0gZXZlbnRzICU+JQ0KICBmaWx0ZXIocGxheWVyLm5hbWUgPT0gIlNvZnlhbiBBbXJhYmF0IiwgdHlwZS5uYW1lID09ICJQcmVzc3VyZSIpDQoNCiMgQ3JlYXRlIGEgbmV3IGRhdGFmcmFtZSB3aXRoIHNlbGVjdGVkIGNvbHVtbnMNCmFtcmFiYXRfcHJlc3N1cmVfZGF0YSA8LSBhbXJhYmF0X3ByZXNzdXJlcyAlPiUNCiAgc2VsZWN0KHBsYXllci5uYW1lLCB0eXBlLm5hbWUsIGxvY2F0aW9uLngsIGxvY2F0aW9uLnkpDQpgYGANCg0KYGBge3J9DQoNCnBsb3QgPC0gZ2dwbG90KGFtcmFiYXRfcHJlc3N1cmVfZGF0YSkgKw0KICBhbm5vdGF0ZV9waXRjaChkaW1lbnNpb25zID0gcGl0Y2hfc3RhdHNib21iLA0KICAgICAgICAgICAgICAgICBjb2xvdXIgPSAid2hpdGUiLA0KICAgICAgICAgICAgICAgICBmaWxsICAgPSAic3ByaW5nZ3JlZW40IiwNCiAgICAgICAgICAgICAgICAgbGltaXRzID0gVFJVRSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gbG9jYXRpb24ueCwgeSA9IGxvY2F0aW9uLnkpLA0KICAgICAgICAgICAgIGNvbG91ciA9ICJtYXJvb24iLA0KICAgICAgICAgICAgIHNpemUgPSAzKSArDQogIHRoZW1lX3BpdGNoKCkgKw0KICBkaXJlY3Rpb25fbGFiZWwoY29sb3VyID0gIndoaXRlIikrDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJzcHJpbmdncmVlbjQiKSkgKw0KICBnZ3RpdGxlKCJBbXJhYmF0IFByZXNzdXJlIE1hcCIsDQogICAgICAgICAgIkZpZmEgV29ybGQgQ3VwIDIwMjIgU3RhdHNib21iIERhdGEiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQoNCmBgYA0KDQpaaXllY2gNCmBgYHtyfQ0KIyBGaWx0ZXIgcHJlc3N1cmVzIGZvciBaaXllY2gNCnppeWVjaF9wcmVzc3VyZXMgPC0gZXZlbnRzICU+JQ0KICBmaWx0ZXIocGxheWVyLm5hbWUgPT0gIkhha2ltIFppeWVjaCIsIHR5cGUubmFtZSA9PSAiUHJlc3N1cmUiKQ0KDQojIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgd2l0aCBzZWxlY3RlZCBjb2x1bW5zDQp6aXllY2hfcHJlc3N1cmVfZGF0YSA8LSB6aXllY2hfcHJlc3N1cmVzICU+JQ0KICBzZWxlY3QocGxheWVyLm5hbWUsIHR5cGUubmFtZSwgbG9jYXRpb24ueCwgbG9jYXRpb24ueSkNCmBgYA0KDQpgYGB7cn0NCg0KcGxvdDwtIGdncGxvdCh6aXllY2hfcHJlc3N1cmVfZGF0YSkgKw0KICBhbm5vdGF0ZV9waXRjaChkaW1lbnNpb25zID0gcGl0Y2hfc3RhdHNib21iLA0KICAgICAgICAgICAgICAgICBjb2xvdXIgPSAid2hpdGUiLA0KICAgICAgICAgICAgICAgICBmaWxsICAgPSAic3ByaW5nZ3JlZW40IiwNCiAgICAgICAgICAgICAgICAgbGltaXRzID0gRkFMU0UpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxvY2F0aW9uLngsIHkgPSBsb2NhdGlvbi55KSwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibWFyb29uIiwNCiAgICAgICAgICAgICBzaXplID0gMykgKw0KICB0aGVtZV9waXRjaCgpICsNCiAgZGlyZWN0aW9uX2xhYmVsKGNvbG91ciA9ICJ3aGl0ZSIpKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAic3ByaW5nZ3JlZW40IikpICsNCiAgZ2d0aXRsZSgiWml5ZWNoIFByZXNzdXJlIE1hcCIsDQogICAgICAgICAgIkZpZmEgV29ybGQgQ3VwIDIwMjIgU3RhdHNib21iIERhdGEiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQoNCmBgYA0KDQpMZXQncyB0YWtlIGEgZ29vZCBsb29rIGF0IA0KQXp6ZWRpbmUgT3VuYWhpDQpgYGB7cn0NCiMgRmlsdGVyIHByZXNzdXJlcyBmb3IgT3VuYWhpDQpPdW5haGlfcHJlc3N1cmVzIDwtIGV2ZW50cyAlPiUNCiAgZmlsdGVyKHBsYXllci5uYW1lID09ICJBenplZGluZSBPdW5haGkiLCB0eXBlLm5hbWUgPT0gIlByZXNzdXJlIikNCg0KIyBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdpdGggc2VsZWN0ZWQgY29sdW1ucw0KT3VuYWhpX3ByZXNzdXJlX2RhdGEgPC0gT3VuYWhpX3ByZXNzdXJlcyAlPiUNCiAgc2VsZWN0KHBsYXllci5uYW1lLCB0eXBlLm5hbWUsIGxvY2F0aW9uLngsIGxvY2F0aW9uLnkpDQpgYGANCg0KYGBge3J9DQoNCnBsb3Q8LSBnZ3Bsb3QoT3VuYWhpX3ByZXNzdXJlX2RhdGEpICsNCiAgYW5ub3RhdGVfcGl0Y2goZGltZW5zaW9ucyA9IHBpdGNoX3N0YXRzYm9tYiwNCiAgICAgICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwNCiAgICAgICAgICAgICAgICAgZmlsbCAgID0gInNwcmluZ2dyZWVuNCIsDQogICAgICAgICAgICAgICAgIGxpbWl0cyA9IFRSVUUpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxvY2F0aW9uLngsIHkgPSBsb2NhdGlvbi55KSwNCiAgICAgICAgICAgICBjb2xvdXIgPSAibWFyb29uIiwNCiAgICAgICAgICAgICBzaXplID0gMykgKw0KICB0aGVtZV9waXRjaCgpICsNCiAgZGlyZWN0aW9uX2xhYmVsKGNvbG91ciA9ICJ3aGl0ZSIpKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAic3ByaW5nZ3JlZW40IikpICsNCiAgZ2d0aXRsZSgiQXp6ZWRpbmUgT3VuYWhpIFByZXNzdXJlIE1hcCIsDQogICAgICAgICAgIkZpZmEgV29ybGQgQ3VwIDIwMjIgU3RhdHNib21iIERhdGEiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQoNCmBgYA0KQXp6ZWRpbmUgT3VuYWhpIC0tIHBhc3Nlcw0KYGBge3J9DQojIEZpbHRlciBwYXNzc2VzIGZvciBPdW5haGkNCk91bmFoaV9wYXNzZXMgPC0gZXZlbnRzICU+JQ0KICBmaWx0ZXIocGxheWVyLm5hbWUgPT0gIkF6emVkaW5lIE91bmFoaSIsIHR5cGUubmFtZSA9PSAiUGFzcyIpDQoNCiMgQ3JlYXRlIGEgbmV3IGRhdGFmcmFtZSB3aXRoIHNlbGVjdGVkIGNvbHVtbnMNCk91bmFoaV9wYXNzX2RhdGEgPC0gT3VuYWhpX3Bhc3NlcyAlPiUNCiAgc2VsZWN0KHBsYXllci5uYW1lLCB0eXBlLm5hbWUsIGxvY2F0aW9uLngsIGxvY2F0aW9uLnksIHBhc3MuZW5kX2xvY2F0aW9uLngsIHBhc3MuZW5kX2xvY2F0aW9uLnkpDQpgYGANCg0KYGBge3J9DQoNCnBsb3QgPC0gZ2dwbG90KE91bmFoaV9wYXNzX2RhdGEpICsNCiAgYW5ub3RhdGVfcGl0Y2goZGltZW5zaW9ucyA9IHBpdGNoX3N0YXRzYm9tYiwgDQogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgIGZpbGwgICA9ICJzcHJpbmdncmVlbjQiLA0KICAgICAgICAgICAgICAgICBsaW1pdHMgPSBUUlVFKSArDQogIGdlb21fc2VnbWVudChhZXMoeCA9IGxvY2F0aW9uLngsIHkgPSBsb2NhdGlvbi55LCB4ZW5kID0gcGFzcy5lbmRfbG9jYXRpb24ueCwgeWVuZCA9ICBwYXNzLmVuZF9sb2NhdGlvbi55KSwNCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJtYXJvb24iLA0KICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMTcsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImNsb3NlZCIpKSArDQogIHRoZW1lX3BpdGNoKCkgKw0KICBkaXJlY3Rpb25fbGFiZWwoY29sb3VyID0gIndoaXRlIikrDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJzcHJpbmdncmVlbjQiKSkgKw0KICBnZ3RpdGxlKCJBenplZGluZSBPdW5haGkgV29ybGQgQ3VwIDIwMjIgUGFzc2luZyBNYXAiLCANCiAgICAgICAgICAiT3JhbmdlIEFuYWx5dGljcyIpDQoNCmxvZ28gPC0gZ2dkcmF3KCkgKw0KICBkcmF3X2ltYWdlKCJDOlxcVXNlcnNcXGptbzU2NjBcXFNjaG9vbFxcU3RhdHNib21iXFxTdGF0c0JvbWIgTG9nb1xcMS4gQ29sb3VyIHBvc2l0aXZlXFxTQiAtIEljb24gTG9ja3VwIC0gQ29sb3VyIHBvc2l0aXZlLnBuZyIseCA9IDAuMDEsIHkgPSAwLjAwMSwgd2lkdGggPSAwLjE1LCBoZWlnaHQgPSAwLjE1KQ0KDQojIENvbWJpbmUgdGhlIHBsb3QgYW5kIHRoZSBsb2dvDQpmaW5hbF9wbG90IDwtIHBsb3QgKyBhbm5vdGF0aW9uX2N1c3RvbShnZ3Bsb3RHcm9iKGxvZ28pKQ0KDQojIERpc3BsYXkgdGhlIGZpbmFsIHBsb3QNCmZpbmFsX3Bsb3QNCg0KYGBgDQoNCg0KQXp6ZWRpbmUgT3VuYWhpIC0tIENhcnJpZXMNCmBgYHtyfQ0KIyBGaWx0ZXIgcGFzc3NlcyBmb3IgT3VuYWhpDQpPdW5haGlfQ2FycmllcyA8LSBldmVudHMgJT4lDQogIGZpbHRlcihwbGF5ZXIubmFtZSA9PSAiQXp6ZWRpbmUgT3VuYWhpIiwgdHlwZS5uYW1lID09ICJDYXJyeSIpDQoNCiMgQ3JlYXRlIGEgbmV3IGRhdGFmcmFtZSB3aXRoIHNlbGVjdGVkIGNvbHVtbnMNCk91bmFoaV9jYXJyeV9kYXRhIDwtIE91bmFoaV9DYXJyaWVzICU+JQ0KICBzZWxlY3QocGxheWVyLm5hbWUsIHR5cGUubmFtZSwgbG9jYXRpb24ueCwgbG9jYXRpb24ueSwgY2FycnkuZW5kX2xvY2F0aW9uLngsIGNhcnJ5LmVuZF9sb2NhdGlvbi55KQ0KYGBgDQoNCmBgYHtyfQ0KDQpwbG90IDwtIGdncGxvdChPdW5haGlfY2FycnlfZGF0YSkgKw0KICBhbm5vdGF0ZV9waXRjaChkaW1lbnNpb25zID0gcGl0Y2hfc3RhdHNib21iLCANCiAgICAgICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwNCiAgICAgICAgICAgICAgICAgZmlsbCAgID0gInNwcmluZ2dyZWVuNCIsDQogICAgICAgICAgICAgICAgIGxpbWl0cyA9IEZBTFNFKSArDQogIGdlb21fc2VnbWVudChhZXMoeCA9IGxvY2F0aW9uLngsIHkgPSBsb2NhdGlvbi55LCB4ZW5kID0gY2FycnkuZW5kX2xvY2F0aW9uLngsIHllbmQgPSAgY2FycnkuZW5kX2xvY2F0aW9uLnkpLA0KICAgICAgICAgICAgICAgY29sb3VyID0gIm1hcm9vbiIsDQogICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4xNywgImNtIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiY2xvc2VkIikpICsNCiAgdGhlbWVfcGl0Y2goKSArDQogIGRpcmVjdGlvbl9sYWJlbChjb2xvdXIgPSAid2hpdGUiKSsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInNwcmluZ2dyZWVuNCIpKSArDQogIGdndGl0bGUoIkF6emVkaW5lIE91bmFoaSBXb3JsZCBDdXAgMjAyMiBDYXJyeSBNYXAiLCANCiAgICAgICAgICAiT3JhbmdlIEFuYWx5dGljcyIpDQoNCmxvZ28gPC0gZ2dkcmF3KCkgKw0KICBkcmF3X2ltYWdlKCJDOlxcVXNlcnNcXGptbzU2NjBcXFNjaG9vbFxcU3RhdHNib21iXFxTdGF0c0JvbWIgTG9nb1xcMS4gQ29sb3VyIHBvc2l0aXZlXFxTQiAtIEljb24gTG9ja3VwIC0gQ29sb3VyIHBvc2l0aXZlLnBuZyIseCA9IDAuMDEsIHkgPSAwLjAwMSwgd2lkdGggPSAwLjE1LCBoZWlnaHQgPSAwLjE1KQ0KDQojIENvbWJpbmUgdGhlIHBsb3QgYW5kIHRoZSBsb2dvDQpmaW5hbF9wbG90IDwtIHBsb3QgKyBhbm5vdGF0aW9uX2N1c3RvbShnZ3Bsb3RHcm9iKGxvZ28pKQ0KDQojIERpc3BsYXkgdGhlIGZpbmFsIHBsb3QNCmZpbmFsX3Bsb3QNCg0KYGBgDQoNCg0KYGBge3J9DQoNCg0KIyBGaWx0ZXIgY2FycmllcyBmb3IgIldlc3RvbiBNY0tlbm5pZSINCk1ja2tlbmllX2NhcnJpZXMgPC0gZXZlbnRzICU+JQ0KICBmaWx0ZXIocGxheWVyLm5hbWUgPT0gIldlc3RvbiBNY0tlbm5pZSIsIHR5cGUubmFtZSA9PSAiQ2FycnkiKQ0KDQojIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgd2l0aCBzZWxlY3RlZCBjb2x1bW5zDQpNY2tlbm5pZV9jYXJyeV9kYXRhIDwtIE1ja2tlbmllX2NhcnJpZXMgJT4lDQogIHNlbGVjdChwbGF5ZXIubmFtZSwgdHlwZS5uYW1lLCBsb2NhdGlvbi54LCBsb2NhdGlvbi55LCBjYXJyeS5lbmRfbG9jYXRpb24ueCwgY2FycnkuZW5kX2xvY2F0aW9uLnkpDQoNCmBgYA0KDQpOb3cgbGV0J3MgY3JlYXRlIHRoZSBDaHJpc3RpYW4gUHVsaXNpYyBDYXJyeSBtYXAgLS0gd2lsbCBuZWVkIHRvIHJlLXNjYWxlIHRoZSBnZ3NvY2NlciBjb29yZGluYXRlcyB0byBzdGF0c2JvbWINCmBgYHtyfQ0KDQpwbG90PC1nZ3Bsb3QoTWNrZW5uaWVfY2FycnlfZGF0YSkgKw0KICBhbm5vdGF0ZV9waXRjaChkaW1lbnNpb25zID0gcGl0Y2hfc3RhdHNib21iLCANCiAgICAgICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwNCiAgICAgICAgICAgICAgICAgZmlsbCAgID0gInNwcmluZ2dyZWVuNCIsDQogICAgICAgICAgICAgICAgIGxpbWl0cyA9IEZBTFNFKSArDQogIGdlb21fc2VnbWVudChhZXMoeCA9IGxvY2F0aW9uLngsIHkgPSBsb2NhdGlvbi55LCB4ZW5kID0gY2FycnkuZW5kX2xvY2F0aW9uLngsIHllbmQgPSAgY2FycnkuZW5kX2xvY2F0aW9uLnkpLA0KICAgICAgICAgICAgICAgY29sb3VyID0gIm5hdnkiLA0KICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMTUsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImNsb3NlZCIpKSArDQogIHRoZW1lX3BpdGNoKCkgKw0KICBkaXJlY3Rpb25fbGFiZWwoY29sb3VyID0gIndoaXRlIikrDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJzcHJpbmdncmVlbjQiKSkgKw0KICBnZ3RpdGxlKCJXZXN0b24gTWNLZW5uaWUgV29ybGQgQ3VwIDIwMjIgQ2FycnkgTWFwIiwgDQogICAgICAgICAgIk9yYW5nZSBBbmFseXRpY3MiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQpgYGANCg0KDQpOb3cgbGV0J3MgZG8gaXQgYWdhaW4gZm9yIFB1bGlzaWMgUGFzc2VzDQpMZXQncyBUcnkgdG8gUHJvZHVjZSBhIENhcnJ5IE1hcCBmb3IgQ2hyaXN0aWFuIFB1bGlzaWMgLS0gZmlyc3QgbGV0J3MgZmlsdGVyIGhpcyBkYXRhIG91dCBvZiB0aGUgZXZlbnRzDQpgYGB7cn0NCg0KIyBGaWx0ZXIgcGFzc2VzIGZvciAiV2VzdG9uIE1jS2VubmllIg0KbWNra2VuaWVfcGFzc2VzIDwtIGV2ZW50cyAlPiUNCiAgZmlsdGVyKHBsYXllci5uYW1lID09ICJXZXN0b24gTWNLZW5uaWUiLCB0eXBlLm5hbWUgPT0gIlBhc3MiKQ0KDQojIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgd2l0aCBzZWxlY3RlZCBjb2x1bW5zDQptY2trZW5pZV9wYXNzX2RhdGEgPC0gbWNra2VuaWVfcGFzc2VzICU+JQ0KICBzZWxlY3QocGxheWVyLm5hbWUsIHR5cGUubmFtZSwgbG9jYXRpb24ueCwgbG9jYXRpb24ueSwgcGFzcy5lbmRfbG9jYXRpb24ueCwgcGFzcy5lbmRfbG9jYXRpb24ueSkNCg0KYGBgDQoNCk5vdyBsZXQncyBjcmVhdGUgdGhlIG1ja2tlbmllIHBhc3NpbmcgbWFwIC0tIHdpbGwgbmVlZCB0byByZS1zY2FsZSB0aGUgZ2dzb2NjZXIgY29vcmRpbmF0ZXMgZnJvbSBvcHRpbWEgdG8gc3RhdHNib21iDQpgYGB7cn0NCnBsb3QgPC0gZ2dwbG90KG1ja2tlbmllX3Bhc3NfZGF0YSkgKw0KICBhbm5vdGF0ZV9waXRjaChkaW1lbnNpb25zID0gcGl0Y2hfc3RhdHNib21iLCANCiAgICAgICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwNCiAgICAgICAgICAgICAgICAgZmlsbCAgID0gInNwcmluZ2dyZWVuNCIsDQogICAgICAgICAgICAgICAgIGxpbWl0cyA9IEZBTFNFKSArDQogIGdlb21fc2VnbWVudChhZXMoeCA9IGxvY2F0aW9uLngsIHkgPSBsb2NhdGlvbi55LCB4ZW5kID0gcGFzcy5lbmRfbG9jYXRpb24ueCwgeWVuZCA9ICBwYXNzLmVuZF9sb2NhdGlvbi55KSwNCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJuYXZ5IiwNCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjE3LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJjbG9zZWQiKSkgKw0KICB0aGVtZV9waXRjaCgpICsNCiAgZGlyZWN0aW9uX2xhYmVsKGNvbG91ciA9ICJ3aGl0ZSIpKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAic3ByaW5nZ3JlZW40IikpICsNCiAgZ2d0aXRsZSgiV2VzdG9uIE1ja2VubmllIDIwMjIgV29ybGQgQ3VwIFBhc3NpbmcgTWFwIiwgDQogICAgICAgICAgIk9yYW5nZSBBbmFseXRpY3MiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQpgYGANCmBgYHtyfQ0KIyBGaWx0ZXIgcGFzc2VzIGZvciAiVmljdG9yIE5lbHNzb24iDQpOZWxzc29uX3ByZXNzdXJlcyA8LSBldmVudHMgJT4lDQogIGZpbHRlcihwbGF5ZXIubmFtZSA9PSAiVmljdG9yIE5lbHNzb24iLCB0eXBlLm5hbWUgPT0gIlByZXNzdXJlIikNCg0KIyBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdpdGggc2VsZWN0ZWQgY29sdW1ucw0KTmVsc3Nvbl9wcmVzc3VyZV9kYXRhIDwtIE5lbHNzb25fcHJlc3N1cmVzICU+JQ0KICBzZWxlY3QocGxheWVyLm5hbWUsIHR5cGUubmFtZSwgbG9jYXRpb24ueCwgbG9jYXRpb24ueSkNCmBgYA0KDQpgYGB7cn0NCg0KcGxvdCA8LSBnZ3Bsb3QoTmVsc3Nvbl9wcmVzc3VyZV9kYXRhKSArDQogIGFubm90YXRlX3BpdGNoKGRpbWVuc2lvbnMgPSBwaXRjaF9zdGF0c2JvbWIsDQogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgIGZpbGwgICA9ICJzcHJpbmdncmVlbjQiLA0KICAgICAgICAgICAgICAgICBsaW1pdHMgPSBUUlVFKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBsb2NhdGlvbi54LCB5ID0gbG9jYXRpb24ueSksDQogICAgICAgICAgICAgY29sb3VyID0gInBpbmsiLA0KICAgICAgICAgICAgIHNpemUgPSAzKSArDQogIHRoZW1lX3BpdGNoKCkgKw0KICBkaXJlY3Rpb25fbGFiZWwoY29sb3VyID0gIndoaXRlIikrDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJzcHJpbmdncmVlbjQiKSkgKw0KICBnZ3RpdGxlKCJWaWN0b3IgTmVsc29uIFByZXNzdXJlIE1hcCIsDQogICAgICAgICAgIkZpZmEgV29ybGQgQ3VwIDIwMjIgU3RhdHNib21iIERhdGEiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQpgYGANCmBgYHtyfQ0KIyBGaWx0ZXIgcGFzc2VzIGZvciAiVmljdG9yIE5lbHNzb24iDQpOZWxzc29uX3Bhc3NlczwtIGV2ZW50cyAlPiUNCiAgZmlsdGVyKHBsYXllci5uYW1lID09ICJWaWN0b3IgTmVsc3NvbiIsIHR5cGUubmFtZSA9PSAiUGFzcyIpDQoNCiMgQ3JlYXRlIGEgbmV3IGRhdGFmcmFtZSB3aXRoIHNlbGVjdGVkIGNvbHVtbnMNCk5lbHNzb25fcGFzc2luZ19kYXRhIDwtIE5lbHNzb25fcGFzc2VzICU+JQ0KICBzZWxlY3QocGxheWVyLm5hbWUsIHR5cGUubmFtZSwgbG9jYXRpb24ueCwgbG9jYXRpb24ueSwgcGFzcy5lbmRfbG9jYXRpb24ueCwgcGFzcy5lbmRfbG9jYXRpb24ueSkNCmBgYA0KDQpgYGB7cn0NCg0KcGxvdCA8LSBnZ3Bsb3QoTmVsc3Nvbl9wYXNzaW5nX2RhdGEpICsNCiAgYW5ub3RhdGVfcGl0Y2goZGltZW5zaW9ucyA9IHBpdGNoX3N0YXRzYm9tYiwgDQogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgIGZpbGwgICA9ICJzcHJpbmdncmVlbjQiLA0KICAgICAgICAgICAgICAgICBsaW1pdHMgPSBGQUxTRSkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHggPSBsb2NhdGlvbi54LCB5ID0gbG9jYXRpb24ueSwgeGVuZCA9IHBhc3MuZW5kX2xvY2F0aW9uLngsIHllbmQgPSAgcGFzcy5lbmRfbG9jYXRpb24ueSksDQogICAgICAgICAgICAgICBjb2xvdXIgPSAicGluayIsDQogICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4xNywgImNtIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiY2xvc2VkIikpICsNCiAgdGhlbWVfcGl0Y2goKSArDQogIGRpcmVjdGlvbl9sYWJlbChjb2xvdXIgPSAid2hpdGUiKSsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInNwcmluZ2dyZWVuNCIpKSArDQogIGdndGl0bGUoIlZpY3RvciBOZWxzc29uIDIwMjIgV29ybGQgQ3VwIFBhc3NpbmcgTWFwIiwgDQogICAgICAgICAgIk9yYW5nZSBBbmFseXRpY3MiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQpgYGANCkxldCdzIExvb2sgYXQgRW56byBGZXJuYW5kZXoNCmBgYHtyfQ0KIyBGaWx0ZXIgcGFzc2VzIGZvciAiRW56byBGZXJuYW5kZXoiDQpwbGF5ZXJfcGFzc2VzPC0gZXZlbnRzICU+JQ0KICBmaWx0ZXIocGxheWVyLm5hbWUgPT0gIkVuem8gRmVybmFuZGV6IiwgdHlwZS5uYW1lID09ICJQYXNzIikNCg0KIyBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdpdGggc2VsZWN0ZWQgY29sdW1ucw0KcGxheWVyX3Bhc3NpbmdfZGF0YSA8LSBwbGF5ZXJfcGFzc2VzICU+JQ0KICBzZWxlY3QocGxheWVyLm5hbWUsIHR5cGUubmFtZSwgbG9jYXRpb24ueCwgbG9jYXRpb24ueSwgcGFzcy5lbmRfbG9jYXRpb24ueCwgcGFzcy5lbmRfbG9jYXRpb24ueSkNCmBgYA0KDQpgYGB7cn0NCg0KcGxvdCA8LSBnZ3Bsb3QocGxheWVyX3Bhc3NpbmdfZGF0YSkgKw0KICBhbm5vdGF0ZV9waXRjaChkaW1lbnNpb25zID0gcGl0Y2hfc3RhdHNib21iLCANCiAgICAgICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwNCiAgICAgICAgICAgICAgICAgZmlsbCAgID0gInNwcmluZ2dyZWVuNCIsDQogICAgICAgICAgICAgICAgIGxpbWl0cyA9IEZBTFNFKSArDQogIGdlb21fc2VnbWVudChhZXMoeCA9IGxvY2F0aW9uLngsIHkgPSBsb2NhdGlvbi55LCB4ZW5kID0gcGFzcy5lbmRfbG9jYXRpb24ueCwgeWVuZCA9ICBwYXNzLmVuZF9sb2NhdGlvbi55KSwNCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodCBibHVlIiwNCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjE3LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJjbG9zZWQiKSkgKw0KICB0aGVtZV9waXRjaCgpICsNCiAgZGlyZWN0aW9uX2xhYmVsKGNvbG91ciA9ICJ3aGl0ZSIpKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAic3ByaW5nZ3JlZW40IikpICsNCiAgZ2d0aXRsZSgiRW56byBGZXJuYW5kZXogMjAyMiBXb3JsZCBDdXAgUGFzc2luZyBNYXAiLCANCiAgICAgICAgICAiT3JhbmdlIEFuYWx5dGljcyIpDQoNCmxvZ28gPC0gZ2dkcmF3KCkgKw0KICBkcmF3X2ltYWdlKCJDOlxcVXNlcnNcXGptbzU2NjBcXFNjaG9vbFxcU3RhdHNib21iXFxTdGF0c0JvbWIgTG9nb1xcMS4gQ29sb3VyIHBvc2l0aXZlXFxTQiAtIEljb24gTG9ja3VwIC0gQ29sb3VyIHBvc2l0aXZlLnBuZyIseCA9IDAuMDEsIHkgPSAwLjAwMSwgd2lkdGggPSAwLjE1LCBoZWlnaHQgPSAwLjE1KQ0KDQojIENvbWJpbmUgdGhlIHBsb3QgYW5kIHRoZSBsb2dvDQpmaW5hbF9wbG90IDwtIHBsb3QgKyBhbm5vdGF0aW9uX2N1c3RvbShnZ3Bsb3RHcm9iKGxvZ28pKQ0KDQojIERpc3BsYXkgdGhlIGZpbmFsIHBsb3QNCmZpbmFsX3Bsb3QNCmBgYA0KDQoNCmBgYHtyfQ0KIyBGaWx0ZXIgcGFzc2VzIGZvciAiRW56byBGZXJuYW5kZXoiDQpwbGF5ZXJfY2FycmllczwtIGV2ZW50cyAlPiUNCiAgZmlsdGVyKHBsYXllci5uYW1lID09ICJFbnpvIEZlcm5hbmRleiIsIHR5cGUubmFtZSA9PSAiQ2FycnkiKQ0KDQojIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgd2l0aCBzZWxlY3RlZCBjb2x1bW5zDQpwbGF5ZXJfY2FycnlfZGF0YSA8LSBwbGF5ZXJfY2FycmllcyAlPiUNCiAgc2VsZWN0KHBsYXllci5uYW1lLCB0eXBlLm5hbWUsIGxvY2F0aW9uLngsIGxvY2F0aW9uLnksIGNhcnJ5LmVuZF9sb2NhdGlvbi54LCBjYXJyeS5lbmRfbG9jYXRpb24ueSkNCmBgYA0KDQpgYGB7cn0NCg0KcGxvdCA8LSBnZ3Bsb3QocGxheWVyX2NhcnJ5X2RhdGEpICsNCiAgYW5ub3RhdGVfcGl0Y2goZGltZW5zaW9ucyA9IHBpdGNoX3N0YXRzYm9tYiwgDQogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgIGZpbGwgICA9ICJzcHJpbmdncmVlbjQiLA0KICAgICAgICAgICAgICAgICBsaW1pdHMgPSBGQUxTRSkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHggPSBsb2NhdGlvbi54LCB5ID0gbG9jYXRpb24ueSwgeGVuZCA9IGNhcnJ5LmVuZF9sb2NhdGlvbi54LCB5ZW5kID0gIGNhcnJ5LmVuZF9sb2NhdGlvbi55KSwNCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJsaWdodCBibHVlIiwNCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjE3LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJjbG9zZWQiKSkgKw0KICB0aGVtZV9waXRjaCgpICsNCiAgZGlyZWN0aW9uX2xhYmVsKGNvbG91ciA9ICJ3aGl0ZSIpKw0KICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAic3ByaW5nZ3JlZW40IikpICsNCiAgZ2d0aXRsZSgiRW56byBGZXJuYW5kZXogMjAyMiBXb3JsZCBDdXAgQ2FycnkgTWFwIiwgDQogICAgICAgICAgIk9yYW5nZSBBbmFseXRpY3MiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQoNCmBgYA0KDQpMZXQncyBMb29rIGF0IFR5bGVyIEFkYW1zDQpgYGB7cn0NCiMgRmlsdGVyIHBhc3NlcyBmb3IgIlR5bGVyIEFkYW1zIg0KcGxheWVyX3Bhc3NlczwtIGV2ZW50cyAlPiUNCiAgZmlsdGVyKHBsYXllci5uYW1lID09ICJUeWxlciBBZGFtcyIsIHR5cGUubmFtZSA9PSAiUGFzcyIpDQoNCiMgQ3JlYXRlIGEgbmV3IGRhdGFmcmFtZSB3aXRoIHNlbGVjdGVkIGNvbHVtbnMNCnBsYXllcl9wYXNzaW5nX2RhdGEgPC0gcGxheWVyX3Bhc3NlcyAlPiUNCiAgc2VsZWN0KHBsYXllci5uYW1lLCB0eXBlLm5hbWUsIGxvY2F0aW9uLngsIGxvY2F0aW9uLnksIHBhc3MuZW5kX2xvY2F0aW9uLngsIHBhc3MuZW5kX2xvY2F0aW9uLnkpDQpgYGANCg0KYGBge3J9DQoNCnBsb3QgPC0gZ2dwbG90KHBsYXllcl9wYXNzaW5nX2RhdGEpICsNCiAgYW5ub3RhdGVfcGl0Y2goZGltZW5zaW9ucyA9IHBpdGNoX3N0YXRzYm9tYiwgDQogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgIGZpbGwgICA9ICJzcHJpbmdncmVlbjQiLA0KICAgICAgICAgICAgICAgICBsaW1pdHMgPSBGQUxTRSkgKw0KICBnZW9tX3NlZ21lbnQoYWVzKHggPSBsb2NhdGlvbi54LCB5ID0gbG9jYXRpb24ueSwgeGVuZCA9IHBhc3MuZW5kX2xvY2F0aW9uLngsIHllbmQgPSAgcGFzcy5lbmRfbG9jYXRpb24ueSksDQogICAgICAgICAgICAgICBjb2xvdXIgPSAibGlnaHQgYmx1ZSIsDQogICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4xNywgImNtIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiY2xvc2VkIikpICsNCiAgdGhlbWVfcGl0Y2goKSArDQogIGRpcmVjdGlvbl9sYWJlbChjb2xvdXIgPSAid2hpdGUiKSsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInNwcmluZ2dyZWVuNCIpKSArDQogIGdndGl0bGUoIlR5bGVyIEFkYW1zIDIwMjIgV29ybGQgQ3VwIFBhc3NpbmcgTWFwIiwgDQogICAgICAgICAgIk9yYW5nZSBBbmFseXRpY3MiKQ0KDQpsb2dvIDwtIGdnZHJhdygpICsNCiAgZHJhd19pbWFnZSgiQzpcXFVzZXJzXFxqbW81NjYwXFxTY2hvb2xcXFN0YXRzYm9tYlxcU3RhdHNCb21iIExvZ29cXDEuIENvbG91ciBwb3NpdGl2ZVxcU0IgLSBJY29uIExvY2t1cCAtIENvbG91ciBwb3NpdGl2ZS5wbmciLHggPSAwLjAxLCB5ID0gMC4wMDEsIHdpZHRoID0gMC4xNSwgaGVpZ2h0ID0gMC4xNSkNCg0KIyBDb21iaW5lIHRoZSBwbG90IGFuZCB0aGUgbG9nbw0KZmluYWxfcGxvdCA8LSBwbG90ICsgYW5ub3RhdGlvbl9jdXN0b20oZ2dwbG90R3JvYihsb2dvKSkNCg0KIyBEaXNwbGF5IHRoZSBmaW5hbCBwbG90DQpmaW5hbF9wbG90DQpgYGANCg0KDQpMZXQncyBMb29rIGF0IEVuem8gRmVybmFuZGV6DQpgYGB7cn0NCiMgRmlsdGVyIHBhc3NlcyBmb3IgIlR5bGVyIEFkYW1zIg0KcGxheWVyX3ByZXNzdXJlczwtIGV2ZW50cyAlPiUNCiAgZmlsdGVyKHBsYXllci5uYW1lID09ICJUeWxlciBBZGFtcyIsIHR5cGUubmFtZSA9PSAiUHJlc3N1cmUiKQ0KDQojIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgd2l0aCBzZWxlY3RlZCBjb2x1bW5zDQpwbGF5ZXJfcHJlc3N1cmVfZGF0YSA8LSBwbGF5ZXJfcHJlc3N1cmVzICU+JQ0KICBzZWxlY3QocGxheWVyLm5hbWUsIHR5cGUubmFtZSwgbG9jYXRpb24ueCwgbG9jYXRpb24ueSwgcGFzcy5lbmRfbG9jYXRpb24ueCwgcGFzcy5lbmRfbG9jYXRpb24ueSkNCmBgYA0KDQpgYGB7cn0NCg0KcGxvdCA8LSBnZ3Bsb3QocGxheWVyX3ByZXNzdXJlX2RhdGEpICsNCiAgYW5ub3RhdGVfcGl0Y2goZGltZW5zaW9ucyA9IHBpdGNoX3N0YXRzYm9tYiwgDQogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgIGZpbGwgICA9ICJzcHJpbmdncmVlbjQiLA0KICAgICAgICAgICAgICAgICBsaW1pdHMgPSBGQUxTRSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gbG9jYXRpb24ueCwgeSA9IGxvY2F0aW9uLnkpLA0KICAgICAgICAgICAgICAgY29sb3VyID0gIm5hdnkiKSArDQogIHRoZW1lX3BpdGNoKCkgKw0KICBkaXJlY3Rpb25fbGFiZWwoY29sb3VyID0gIndoaXRlIikrDQogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJzcHJpbmdncmVlbjQiKSkgKw0KICBnZ3RpdGxlKCJUeWxlciBBZGFtcyAyMDIyIFdvcmxkIEN1cCBQcmVzc3VyZSBNYXAiLCANCiAgICAgICAgICAiT3JhbmdlIEFuYWx5dGljcyIpDQoNCmxvZ28gPC0gZ2dkcmF3KCkgKw0KICBkcmF3X2ltYWdlKCJDOlxcVXNlcnNcXGptbzU2NjBcXFNjaG9vbFxcU3RhdHNib21iXFxTdGF0c0JvbWIgTG9nb1xcMS4gQ29sb3VyIHBvc2l0aXZlXFxTQiAtIEljb24gTG9ja3VwIC0gQ29sb3VyIHBvc2l0aXZlLnBuZyIseCA9IDAuMDEsIHkgPSAwLjAwMSwgd2lkdGggPSAwLjE1LCBoZWlnaHQgPSAwLjE1KQ0KDQojIENvbWJpbmUgdGhlIHBsb3QgYW5kIHRoZSBsb2dvDQpmaW5hbF9wbG90IDwtIHBsb3QgKyBhbm5vdGF0aW9uX2N1c3RvbShnZ3Bsb3RHcm9iKGxvZ28pKQ0KDQojIERpc3BsYXkgdGhlIGZpbmFsIHBsb3QNCmZpbmFsX3Bsb3QNCmBgYA==