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==