# Load necessary libraries
library(tidyverse)

# Import datasets (assuming they are in your current project directory)
season_stats <- read_csv("WomenSuperLeagueseason2022-23.csv")
Rows: 12 Columns: 16
── Column specification ───────────────────────────────
Delimiter: ","
chr  (1): Squad
dbl (14): Rk, MP, W, D, L, GF, GA, GD, Pts, Pts/MP,...
num  (1): Attendance

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
passing_stats <- read_csv("WomenSuperLeaguePassingstats2022-23.csv")
New names:
• `Cmp` -> `Cmp...5`
• `Att` -> `Att...6`
• `Cmp%` -> `Cmp%...7`
• `Cmp` -> `Cmp...10`
• `Att` -> `Att...11`
• `Cmp%` -> `Cmp%...12`
• `Cmp` -> `Cmp...13`
• `Att` -> `Att...14`
• `Cmp%` -> `Cmp%...15`
• `Cmp` -> `Cmp...16`
• `Att` -> `Att...17`
• `Cmp%` -> `Cmp%...18`
Rows: 12 Columns: 27
── Column specification ───────────────────────────────
Delimiter: ","
chr  (1): Squad
dbl (26): Rank, # Pl, 90s, Cmp...5, Att...6, Cmp%.....

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
pass_types <- read_csv("WomenSuperLeaguePassTypes2022-23.csv")
Rows: 12 Columns: 19
── Column specification ───────────────────────────────
Delimiter: ","
chr  (1): Squad
dbl (18): Rank, # Pl, 90s, Att, Live, Dead, FK, TB,...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Preview the data
head(season_stats)
head(passing_stats)
head(pass_types)
# Standardize column names to lowercase for consistency
season_stats <- season_stats %>% rename_all(tolower)
passing_stats <- passing_stats %>% rename_all(tolower)
pass_types <- pass_types %>% rename_all(tolower)

# Let's preview the squad names to ensure they align
unique(season_stats$squad)
 [1] "Club Crest\xa0Chelsea"           
 [2] "Club Crest\xa0Manchester Utd"    
 [3] "Club Crest\xa0Arsenal"           
 [4] "Club Crest\xa0Manchester City"   
 [5] "Club Crest\xa0Aston Villa"       
 [6] "Club Crest\xa0Everton"           
 [7] "Club Crest\xa0Liverpool"         
 [8] "Club Crest\xa0West Ham"          
 [9] "Club Crest\xa0Tottenham"         
[10] "Club Crest\xa0Leicester City WFC"
[11] "Club Crest\xa0Brighton"          
[12] "Club Crest\xa0Reading"           
unique(passing_stats$squad)
 [1] "Arsenal"            "Aston Villa"       
 [3] "Brighton"           "Chelsea"           
 [5] "Everton"            "Leicester City WFC"
 [7] "Liverpool"          "Manchester City"   
 [9] "Manchester Utd"     "Reading"           
[11] "Tottenham"          "West Ham"          
unique(pass_types$squad)
 [1] "Arsenal"            "Aston Villa"       
 [3] "Brighton"           "Chelsea"           
 [5] "Everton"            "Leicester City WFC"
 [7] "Liverpool"          "Manchester City"   
 [9] "Manchester Utd"     "Reading"           
[11] "Tottenham"          "West Ham"          
# Final cleanup to extract just the team name
season_stats <- season_stats %>%
  mutate(squad = str_extract(squad, "[A-Za-z ]+$"))

# Check the result
unique(season_stats$squad)
 [1] "Chelsea"            "Manchester Utd"    
 [3] "Arsenal"            "Manchester City"   
 [5] "Aston Villa"        "Everton"           
 [7] "Liverpool"          "West Ham"          
 [9] "Tottenham"          "Leicester City WFC"
[11] "Brighton"           "Reading"           
# Merge all datasets on 'squad'
merged_data <- season_stats %>%
  inner_join(passing_stats, by = "squad") %>%
  inner_join(pass_types, by = "squad")

# View the structure of the merged data
glimpse(merged_data)
Rows: 12
Columns: 60
$ rk          <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11…
$ squad       <chr> "Chelsea", "Manchester Utd", "Ars…
$ mp          <dbl> 22, 22, 22, 22, 22, 22, 22, 22, 2…
$ w           <dbl> 19, 18, 15, 15, 11, 9, 6, 6, 5, 5…
$ d           <dbl> 1, 2, 2, 2, 4, 3, 5, 3, 3, 1, 4, 2
$ l           <dbl> 2, 2, 5, 5, 7, 10, 11, 13, 14, 16…
$ gf          <dbl> 66, 56, 49, 50, 47, 29, 24, 23, 3…
$ ga          <dbl> 15, 12, 16, 25, 37, 36, 39, 44, 4…
$ gd          <dbl> 51, 44, 33, 25, 10, -7, -15, -21,…
$ pts         <dbl> 58, 56, 47, 47, 37, 30, 23, 21, 1…
$ `pts/mp`    <dbl> 2.64, 2.55, 2.14, 2.14, 1.68, 1.3…
$ xg          <dbl> 43.8, 35.4, 42.7, 48.0, 28.0, 20.…
$ xga         <dbl> 17.8, 13.0, 17.6, 17.2, 24.6, 31.…
$ xgd         <dbl> 26.0, 22.4, 25.1, 30.8, 3.3, -11.…
$ `xgd/90`    <dbl> 1.18, 1.02, 1.14, 1.40, 0.15, -0.…
$ attendance  <dbl> 5616, 9648, 13996, 6559, 4199, 25…
$ rank.x      <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11…
$ `# pl.x`    <dbl> 25, 24, 27, 24, 26, 23, 27, 26, 2…
$ `90s.x`     <dbl> 22, 22, 22, 22, 22, 22, 22, 22, 2…
$ cmp...5     <dbl> 9939, 10120, 9705, 10460, 8431, 8…
$ att...6     <dbl> 12582, 12518, 12030, 12748, 10922…
$ `cmp%...7`  <dbl> 79.0, 80.8, 80.7, 82.1, 77.2, 78.…
$ totdist     <dbl> 191912, 186350, 184631, 189947, 1…
$ prgdist     <dbl> 69839, 68111, 66769, 71843, 60024…
$ cmp...10    <dbl> 3509, 3904, 3540, 4203, 3562, 355…
$ att...11    <dbl> 4219, 4556, 4122, 4828, 4251, 421…
$ `cmp%...12` <dbl> 83.2, 85.7, 85.9, 87.1, 83.8, 84.…
$ cmp...13    <dbl> 5064, 4944, 4911, 5062, 3834, 443…
$ att...14    <dbl> 5924, 5665, 5656, 5779, 4575, 521…
$ `cmp%...15` <dbl> 85.5, 87.3, 86.8, 87.6, 83.8, 85.…
$ cmp...16    <dbl> 1216, 1044, 1119, 989, 882, 744, …
$ att...17    <dbl> 1961, 1741, 1793, 1665, 1603, 143…
$ `cmp%...18` <dbl> 62.0, 60.0, 62.4, 59.4, 55.0, 51.…
$ ast         <dbl> 44, 41, 31, 38, 33, 20, 15, 12, 2…
$ xag         <dbl> 29.8, 24.9, 29.2, 36.6, 19.0, 14.…
$ xa          <dbl> 33.2, 29.3, 31.8, 36.0, 20.1, 14.…
$ `a-xag`     <dbl> 14.2, 16.1, 1.8, 1.4, 14.0, 5.2, …
$ kp          <dbl> 286, 259, 295, 349, 202, 172, 144…
$ `3-jan`     <dbl> 814, 754, 817, 907, 691, 545, 441…
$ ppa         <dbl> 265, 256, 261, 276, 161, 128, 113…
$ crspa       <dbl> 45, 58, 45, 93, 44, 31, 42, 25, 3…
$ prgp        <dbl> 1004, 979, 1026, 1062, 831, 716, …
$ rank.y      <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11…
$ `# pl.y`    <dbl> 25, 24, 27, 24, 26, 23, 27, 26, 2…
$ `90s.y`     <dbl> 22, 22, 22, 22, 22, 22, 22, 22, 2…
$ att         <dbl> 12582, 12518, 12030, 12748, 10922…
$ live        <dbl> 11440, 11375, 10911, 11630, 9768,…
$ dead        <dbl> 1076, 1074, 1046, 1088, 1086, 112…
$ fk          <dbl> 239, 249, 217, 232, 224, 278, 204…
$ tb          <dbl> 17, 12, 15, 11, 6, 5, 4, 2, 7, 1,…
$ sw          <dbl> 43, 32, 50, 30, 38, 19, 21, 22, 4…
$ crs         <dbl> 380, 406, 375, 519, 425, 268, 307…
$ ti          <dbl> 546, 567, 536, 511, 553, 490, 578…
$ ck          <dbl> 128, 116, 156, 179, 115, 100, 82,…
$ `in`        <dbl> 66, 35, 51, 84, 46, 48, 48, 25, 2…
$ out         <dbl> 24, 29, 62, 23, 37, 10, 21, 33, 1…
$ str         <dbl> 5, 23, 12, 24, 18, 12, 3, 2, 18, …
$ cmp         <dbl> 9939, 10120, 9705, 10460, 8431, 8…
$ off         <dbl> 66, 69, 73, 30, 68, 32, 33, 46, 4…
$ blocks      <dbl> 226, 212, 214, 218, 268, 277, 200…
library(corrplot)
corrplot 0.95 loaded
# Select numeric columns of interest
eda_vars <- merged_data %>%
  select(squad, pts, xg, `cmp%...7`, kp, prgp, xag, xa, `3-jan`, ppa, crspa, att...6, prgdist)

cor_data <- eda_vars %>% select(-squad)

# Create and plot the correlation matrix
cor_matrix <- cor(cor_data, use = "complete.obs")
corrplot::corrplot(cor_matrix, method = "circle", type = "upper", tl.cex = 0.8)

# Fit a multiple linear regression model
lm_pts <- lm(pts ~ kp + prgp + xag + xg + `cmp%...7`, data = merged_data)

# View the summary (coefficients, R², p-values)
summary(lm_pts)

Call:
lm(formula = pts ~ kp + prgp + xag + xg + `cmp%...7`, data = merged_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.9704 -2.0161  0.2005  2.6937  5.0021 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept) 10.43087   43.67086   0.239   0.8192  
kp          -0.18506    0.17667  -1.047   0.3352  
prgp         0.13956    0.04537   3.076   0.0218 *
xag         -0.17197    2.17614  -0.079   0.9396  
xg           0.33561    1.04540   0.321   0.7591  
`cmp%...7`  -0.68601    0.77439  -0.886   0.4098  
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.893 on 6 degrees of freedom
Multiple R-squared:  0.9534,    Adjusted R-squared:  0.9145 
F-statistic: 24.53 on 5 and 6 DF,  p-value: 0.0006311
# Select key metrics for PCA
pca_vars <- merged_data %>% 
  select(kp, prgp, xag, xg, `cmp%...7`, prgdist)

# Run PCA (standardizing variables)
pca_res <- prcomp(pca_vars, scale. = TRUE)

# Summarize
summary(pca_res)
Importance of components:
                          PC1    PC2    PC3     PC4
Standard deviation     2.3789 0.5208 0.1913 0.14666
Proportion of Variance 0.9432 0.0452 0.0061 0.00358
Cumulative Proportion  0.9432 0.9884 0.9945 0.99806
                           PC5     PC6
Standard deviation     0.09579 0.04991
Proportion of Variance 0.00153 0.00042
Cumulative Proportion  0.99958 1.00000
# Scree plot to pick number of components
plot(pca_res, type = "l", main = "PCA Scree Plot")


# Biplot of PC1 vs PC2
biplot(pca_res, scale = 0)

# View PCA loadings (contributions of each variable to the principal components)
pca_res$rotation[, 1]  # PC1 loadings
        kp       prgp        xag         xg   cmp%...7 
-0.4106414 -0.4167559 -0.4132278 -0.4076769 -0.3860316 
   prgdist 
-0.4143792 
set.seed(42)
# Scale and cluster on the same PCA variables
scaled_vars <- scale(pca_vars)
km_res <- kmeans(scaled_vars, centers = 3)  # try 2–4 clusters to see what makes sense

# Add cluster assignment back to data
merged_data$cluster <- factor(km_res$cluster)

# Visualize cluster membership
library(ggplot2)
ggplot(merged_data, aes(x = prgp, y = xg, color = cluster, label = squad)) +
  geom_point(size = 4) +
  geom_text(vjust = -0.75, size = 3) +
  labs(title = "Team Clusters by Progressive Passes & xG",
       x = "Progressive Passes",
       y = "Expected Goals") +
  theme_minimal()

# Fit the regression model
lm_pts <- lm(pts ~ kp + prgp + xag + xg + `cmp%...7`, data = merged_data)

# Predict and plot
merged_data$predicted_pts <- predict(lm_pts)

ggplot(merged_data, aes(x = predicted_pts, y = pts)) +
  geom_point(size = 4, color = "#1D3557") +
  geom_smooth(method = "lm", se = FALSE, color = "#E63946", linetype = "dashed") +
  geom_text_repel(aes(label = squad), size = 4.5) +
  labs(title = "Actual vs Predicted Points",
       x = "Predicted Points (Linear Model)",
       y = "Actual Points",
       subtitle = "Based on key attacking metrics") +
  theme_minimal(base_size = 14)
`geom_smooth()` using formula = 'y ~ x'

#load libraries
library(ggplot2)
library(reshape2)

# Select key variables
cor_vars <- merged_data %>%
  select(pts, xg, xag, kp, prgp, `cmp%...7`, prgdist)

# Compute and reshape the correlation matrix
cor_matrix <- round(cor(cor_vars, use = "complete.obs"), 2)
cor_melted <- melt(cor_matrix)

# Plot with yellow midpoint
ggplot(cor_melted, aes(Var1, Var2, fill = value)) +
  geom_tile(color = "white", size = 0.8) +
  geom_text(aes(label = value), color = "black", size = 4) +
  scale_fill_gradient2(low = "#E63946", mid = "#FFD700", high = "#1D3557",
                       midpoint = 0.6, limits = c(0, 1), space = "Lab") +
  labs(title = "Correlation Heatmap of Key Performance Metrics",
       x = "", y = "", fill = "Correlation") +
  theme_minimal(base_size = 14) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1, face = "bold"),
    axis.text.y = element_text(face = "bold"),
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank()
  )

# Team Comparison Bar Chart: Key Passes
ggplot(merged_data, aes(x = reorder(squad, kp), y = kp, fill = cluster)) +
  geom_bar(stat = "identity", color = "black") +
  coord_flip() +
  scale_fill_manual(values = c("High-Performers" = "#E63946", "Mid-Tier" = "#F4A261", "Low-Performers" = "#457B9D")) +
  labs(title = "Key Passes by Team", x = "Team", y = "Key Passes") +
  theme_minimal(base_size = 14)

# Load PCA tools
library(ggplot2)
library(ggrepel)
library(factoextra)  
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
# Prepare PCA data
pca_vars <- merged_data %>%
  select(kp, prgp, xag, xg, `cmp%...7`, prgdist)

# Perform PCA with scaling
pca_res <- prcomp(pca_vars, scale. = TRUE)

# Create a biplot using factoextra
fviz_pca_biplot(
  pca_res,
  label = "var",          # show variable arrows
  habillage = merged_data$cluster,  # color by cluster
  addEllipses = TRUE,     # optional: adds confidence ellipses around clusters
  col.var = "black",      # arrow color
  repel = TRUE,           # prevent overlapping labels
  labelsize = 5,
  pointshape = 21,
  geom = "point",
  palette = c("#E63946", "#F4A261", "#457B9D")
) +
  labs(title = "PCA Biplot: Women's Super League Playing Styles") +
  theme_minimal(base_size = 14)
Too few points to calculate an ellipse

library(fmsb)

# Choose teams to compare
teams_to_compare <- c("Chelsea", "Aston Villa", "Reading")

# Select key metrics to display
radar_data <- merged_data %>%
  filter(squad %in% teams_to_compare) %>%
  select(squad, prgp, xg, xag, kp, `cmp%...7`) %>%
  column_to_rownames("squad")

# Normalize: add max and min rows for radar plot bounds
radar_max <- apply(radar_data, 2, max)
radar_min <- apply(radar_data, 2, min)
radar_plot_data <- rbind(radar_max, radar_min, radar_data)

# Plot
colors <- c("#E63946", "#F4A261", "#457B9D")
radarchart(radar_plot_data,
           axistype = 1,
           pcol = colors,
           pfcol = adjustcolor(colors, alpha.f = 0.25),
           plwd = 2,
           cglcol = "gray",
           cglty = 1,
           axislabcol = "gray",
           vlcex = 0.9,
           title = "Team Style Comparison Radar Chart")

legend("topright", legend = rownames(radar_data), col = colors, lty = 1, lwd = 2, bty = "n")

# Already fitted model: lm_pts
merged_data$predicted_pts <- predict(lm_pts)

ggplot(merged_data, aes(x = predicted_pts, y = pts)) +
  geom_point(size = 4, color = "#1D3557") +
  geom_smooth(method = "lm", se = FALSE, color = "#E63946", linetype = "dashed") +
  geom_text_repel(aes(label = squad), size = 4.5) +
  labs(title = "Actual vs Predicted Points",
       subtitle = paste("R-squared:", round(summary(lm_pts)$adj.r.squared, 2)),
       x = "Predicted Points (Regression Model)",
       y = "Actual Points") +
  theme_minimal(base_size = 14)
`geom_smooth()` using formula = 'y ~ x'

library(knitr)
library(kableExtra)

Attaching package: ‘kableExtra’

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

    group_rows
summary_table <- merged_data %>%
  select(squad, cluster, prgp, xg, kp, pts) %>%
  arrange(desc(pts))

# Display as a styled table
kable(summary_table, format = "markdown", digits = 1,
      col.names = c("Team", "Cluster", "Progressive Passes", "xG", "Key Passes", "Points")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = F)
Team Cluster Progressive Passes xG Key Passes Points
Chelsea High-Performers 1004 43.8 286 58
Manchester Utd High-Performers 979 35.4 259 56
Arsenal High-Performers 1026 42.7 295 47
Manchester City High-Performers 1062 48.0 349 47
Aston Villa Mid-Tier 831 28.0 202 37
Everton Mid-Tier 716 20.5 172 30
Liverpool Low-Performers 565 21.2 144 23
West Ham Low-Performers 505 17.4 112 21
Tottenham Mid-Tier 607 19.9 155 18
Leicester City WFC Low-Performers 550 16.0 168 16
Brighton Low-Performers 554 23.4 160 16
Reading Low-Performers 504 16.5 140 11
write.csv(summary_table, "WSL_summary_table.csv", row.names = FALSE)
# Print all results from above visuals 
# 1. Linear Regression Summary
cat("Linear Regression Summary: Predicting Points\n\n")
Linear Regression Summary: Predicting Points
summary(lm_pts)

Call:
lm(formula = pts ~ kp + prgp + xag + xg + `cmp%...7`, data = merged_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.9704 -2.0161  0.2005  2.6937  5.0021 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept) 10.43087   43.67086   0.239   0.8192  
kp          -0.18506    0.17667  -1.047   0.3352  
prgp         0.13956    0.04537   3.076   0.0218 *
xag         -0.17197    2.17614  -0.079   0.9396  
xg           0.33561    1.04540   0.321   0.7591  
`cmp%...7`  -0.68601    0.77439  -0.886   0.4098  
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.893 on 6 degrees of freedom
Multiple R-squared:  0.9534,    Adjusted R-squared:  0.9145 
F-statistic: 24.53 on 5 and 6 DF,  p-value: 0.0006311
# 2. PCA Loadings
cat("\nPCA Loadings for Principal Component 1 (Attacking Style Dimension)\n")

PCA Loadings for Principal Component 1 (Attacking Style Dimension)
print(round(pca_res$rotation[, 1], 3))
      kp     prgp      xag       xg cmp%...7  prgdist 
  -0.411   -0.417   -0.413   -0.408   -0.386   -0.414 
# 3. Cluster Membership by Team
cat("\nCluster Membership by Team\n")

Cluster Membership by Team
cluster_membership <- merged_data %>%
  select(squad, cluster, pts, prgp, xg) %>%
  arrange(cluster, desc(pts))
print(cluster_membership)

# 4. Cluster-Level Summary
cat("\nCluster-Level Summary: Average Team Statistics\n")

Cluster-Level Summary: Average Team Statistics
cluster_summary <- merged_data %>%
  group_by(cluster) %>%
  summarise(
    Average_Points = round(mean(pts), 1),
    Average_Progressive_Passes = round(mean(prgp), 1),
    Average_xG = round(mean(xg), 1),
    Average_Key_Passes = round(mean(kp), 1)
  )
print(cluster_summary)

# 5. Top Performing Teams by Key Metrics
cat("\nTop 3 Teams by Progressive Passes, Expected Goals (xG), and Key Passes\n")

Top 3 Teams by Progressive Passes, Expected Goals (xG), and Key Passes
top_prgp <- merged_data %>% select(squad, prgp) %>% arrange(desc(prgp)) %>% head(3)
top_xg   <- merged_data %>% select(squad, xg) %>% arrange(desc(xg)) %>% head(3)
top_kp   <- merged_data %>% select(squad, kp) %>% arrange(desc(kp)) %>% head(3)

cat("\nTop Teams by Progressive Passes:\n")

Top Teams by Progressive Passes:
print(top_prgp)

cat("\nTop Teams by Expected Goals (xG):\n")

Top Teams by Expected Goals (xG):
print(top_xg)

cat("\nTop Teams by Key Passes:\n")

Top Teams by Key Passes:
print(top_kp)

# The end
LS0tCnRpdGxlOiAiUHJvZ3Jlc3Npb24gV2lucyBHYW1lczogQSBUYWN0aWNhbCBBbmFseXNpcyBvZiBQbGF5aW5nIFN0eWxlcyBpbiB0aGUgV29tZW7igJlzIFN1cGVyIExlYWd1ZSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQoKIyBJbXBvcnQgZGF0YXNldHMgKGFzc3VtaW5nIHRoZXkgYXJlIGluIGN1cnJlbnQgcHJvamVjdCBkaXJlY3RvcnkpCnNlYXNvbl9zdGF0cyA8LSByZWFkX2NzdigiV29tZW5TdXBlckxlYWd1ZXNlYXNvbjIwMjItMjMuY3N2IikKcGFzc2luZ19zdGF0cyA8LSByZWFkX2NzdigiV29tZW5TdXBlckxlYWd1ZVBhc3NpbmdzdGF0czIwMjItMjMuY3N2IikKcGFzc190eXBlcyA8LSByZWFkX2NzdigiV29tZW5TdXBlckxlYWd1ZVBhc3NUeXBlczIwMjItMjMuY3N2IikKCiMgUHJldmlldyB0aGUgZGF0YQpoZWFkKHNlYXNvbl9zdGF0cykKaGVhZChwYXNzaW5nX3N0YXRzKQpoZWFkKHBhc3NfdHlwZXMpCmBgYApgYGB7cn0KIyBTdGFuZGFyZGl6ZSBjb2x1bW4gbmFtZXMgdG8gbG93ZXJjYXNlIGZvciBjb25zaXN0ZW5jeQpzZWFzb25fc3RhdHMgPC0gc2Vhc29uX3N0YXRzICU+JSByZW5hbWVfYWxsKHRvbG93ZXIpCnBhc3Npbmdfc3RhdHMgPC0gcGFzc2luZ19zdGF0cyAlPiUgcmVuYW1lX2FsbCh0b2xvd2VyKQpwYXNzX3R5cGVzIDwtIHBhc3NfdHlwZXMgJT4lIHJlbmFtZV9hbGwodG9sb3dlcikKCiMgTGV0J3MgcHJldmlldyB0aGUgc3F1YWQgbmFtZXMgdG8gZW5zdXJlIHRoZXkgYWxpZ24KdW5pcXVlKHNlYXNvbl9zdGF0cyRzcXVhZCkKdW5pcXVlKHBhc3Npbmdfc3RhdHMkc3F1YWQpCnVuaXF1ZShwYXNzX3R5cGVzJHNxdWFkKQpgYGAKYGBge3J9CiMgRmluYWwgY2xlYW51cCB0byBleHRyYWN0IGp1c3QgdGhlIHRlYW0gbmFtZQpzZWFzb25fc3RhdHMgPC0gc2Vhc29uX3N0YXRzICU+JQogIG11dGF0ZShzcXVhZCA9IHN0cl9leHRyYWN0KHNxdWFkLCAiW0EtWmEteiBdKyQiKSkKCiMgQ2hlY2sgdGhlIHJlc3VsdAp1bmlxdWUoc2Vhc29uX3N0YXRzJHNxdWFkKQpgYGAKCmBgYHtyfQojIE1lcmdlIGFsbCBkYXRhc2V0cyBvbiAnc3F1YWQnCm1lcmdlZF9kYXRhIDwtIHNlYXNvbl9zdGF0cyAlPiUKICBpbm5lcl9qb2luKHBhc3Npbmdfc3RhdHMsIGJ5ID0gInNxdWFkIikgJT4lCiAgaW5uZXJfam9pbihwYXNzX3R5cGVzLCBieSA9ICJzcXVhZCIpCgojIFZpZXcgdGhlIHN0cnVjdHVyZSBvZiB0aGUgbWVyZ2VkIGRhdGEKZ2xpbXBzZShtZXJnZWRfZGF0YSkKYGBgCgpgYGB7cn0KbGlicmFyeShjb3JycGxvdCkKYGBgCmBgYHtyfQojIFNlbGVjdCBudW1lcmljIGNvbHVtbnMgb2YgaW50ZXJlc3QKZWRhX3ZhcnMgPC0gbWVyZ2VkX2RhdGEgJT4lCiAgc2VsZWN0KHNxdWFkLCBwdHMsIHhnLCBgY21wJS4uLjdgLCBrcCwgcHJncCwgeGFnLCB4YSwgYDMtamFuYCwgcHBhLCBjcnNwYSwgYXR0Li4uNiwgcHJnZGlzdCkKCmNvcl9kYXRhIDwtIGVkYV92YXJzICU+JSBzZWxlY3QoLXNxdWFkKQoKIyBDcmVhdGUgYW5kIHBsb3QgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApjb3JfbWF0cml4IDwtIGNvcihjb3JfZGF0YSwgdXNlID0gImNvbXBsZXRlLm9icyIpCmNvcnJwbG90Ojpjb3JycGxvdChjb3JfbWF0cml4LCBtZXRob2QgPSAiY2lyY2xlIiwgdHlwZSA9ICJ1cHBlciIsIHRsLmNleCA9IDAuOCkKYGBgCmBgYHtyfQojIEZpdCBhIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsCmxtX3B0cyA8LSBsbShwdHMgfiBrcCArIHByZ3AgKyB4YWcgKyB4ZyArIGBjbXAlLi4uN2AsIGRhdGEgPSBtZXJnZWRfZGF0YSkKCiMgVmlldyB0aGUgc3VtbWFyeSAoY29lZmZpY2llbnRzLCBSwrIsIHAtdmFsdWVzKQpzdW1tYXJ5KGxtX3B0cykKYGBgCmBgYHtyfQojIFNlbGVjdCBrZXkgbWV0cmljcyBmb3IgUENBCnBjYV92YXJzIDwtIG1lcmdlZF9kYXRhICU+JSAKICBzZWxlY3Qoa3AsIHByZ3AsIHhhZywgeGcsIGBjbXAlLi4uN2AsIHByZ2Rpc3QpCgojIFJ1biBQQ0EgKHN0YW5kYXJkaXppbmcgdmFyaWFibGVzKQpwY2FfcmVzIDwtIHByY29tcChwY2FfdmFycywgc2NhbGUuID0gVFJVRSkKCiMgU3VtbWFyaXplCnN1bW1hcnkocGNhX3JlcykKCiMgU2NyZWVuIHBsb3QgdG8gcGljayBudW1iZXIgb2YgY29tcG9uZW50cwpwbG90KHBjYV9yZXMsIHR5cGUgPSAibCIsIG1haW4gPSAiUENBIFNjcmVlIFBsb3QiKQoKIyBCaXBsb3Qgb2YgUEMxIHZzIFBDMgpiaXBsb3QocGNhX3Jlcywgc2NhbGUgPSAwKQpgYGAKYGBge3J9CiMgVmlldyBQQ0EgbG9hZGluZ3MgKGNvbnRyaWJ1dGlvbnMgb2YgZWFjaCB2YXJpYWJsZSB0byB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMpCnBjYV9yZXMkcm90YXRpb25bLCAxXSAgIyBQQzEgbG9hZGluZ3MKYGBgCmBgYHtyfQpzZXQuc2VlZCg0MikKIyBTY2FsZSBhbmQgY2x1c3RlciBvbiB0aGUgc2FtZSBQQ0EgdmFyaWFibGVzCnNjYWxlZF92YXJzIDwtIHNjYWxlKHBjYV92YXJzKQprbV9yZXMgPC0ga21lYW5zKHNjYWxlZF92YXJzLCBjZW50ZXJzID0gMykgICMgdHJ5IDLigJM0IGNsdXN0ZXJzIHRvIHNlZSB3aGF0IG1ha2VzIHNlbnNlCgojIEFkZCBjbHVzdGVyIGFzc2lnbm1lbnQgYmFjayB0byBkYXRhCm1lcmdlZF9kYXRhJGNsdXN0ZXIgPC0gZmFjdG9yKGttX3JlcyRjbHVzdGVyKQoKIyBWaXN1YWxpemUgY2x1c3RlciBtZW1iZXJzaGlwCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KG1lcmdlZF9kYXRhLCBhZXMoeCA9IHByZ3AsIHkgPSB4ZywgY29sb3IgPSBjbHVzdGVyLCBsYWJlbCA9IHNxdWFkKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKICBnZW9tX3RleHQodmp1c3QgPSAtMC43NSwgc2l6ZSA9IDMpICsKICBsYWJzKHRpdGxlID0gIlRlYW0gQ2x1c3RlcnMgYnkgUHJvZ3Jlc3NpdmUgUGFzc2VzICYgeEciLAogICAgICAgeCA9ICJQcm9ncmVzc2l2ZSBQYXNzZXMiLAogICAgICAgeSA9ICJFeHBlY3RlZCBHb2FscyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7cn0KIyBGaXQgdGhlIHJlZ3Jlc3Npb24gbW9kZWwKbG1fcHRzIDwtIGxtKHB0cyB+IGtwICsgcHJncCArIHhhZyArIHhnICsgYGNtcCUuLi43YCwgZGF0YSA9IG1lcmdlZF9kYXRhKQoKIyBQcmVkaWN0IGFuZCBwbG90Cm1lcmdlZF9kYXRhJHByZWRpY3RlZF9wdHMgPC0gcHJlZGljdChsbV9wdHMpCgpnZ3Bsb3QobWVyZ2VkX2RhdGEsIGFlcyh4ID0gcHJlZGljdGVkX3B0cywgeSA9IHB0cykpICsKICBnZW9tX3BvaW50KHNpemUgPSA0LCBjb2xvciA9ICIjMUQzNTU3IikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gIiNFNjM5NDYiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IHNxdWFkKSwgc2l6ZSA9IDQuNSkgKwogIGxhYnModGl0bGUgPSAiQWN0dWFsIHZzIFByZWRpY3RlZCBQb2ludHMiLAogICAgICAgeCA9ICJQcmVkaWN0ZWQgUG9pbnRzIChMaW5lYXIgTW9kZWwpIiwKICAgICAgIHkgPSAiQWN0dWFsIFBvaW50cyIsCiAgICAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiBrZXkgYXR0YWNraW5nIG1ldHJpY3MiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkKYGBgCmBgYHtyfQojbG9hZCBsaWJyYXJpZXMKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQoKIyBTZWxlY3Qga2V5IHZhcmlhYmxlcwpjb3JfdmFycyA8LSBtZXJnZWRfZGF0YSAlPiUKICBzZWxlY3QocHRzLCB4ZywgeGFnLCBrcCwgcHJncCwgYGNtcCUuLi43YCwgcHJnZGlzdCkKCiMgQ29tcHV0ZSBhbmQgcmVzaGFwZSB0aGUgY29ycmVsYXRpb24gbWF0cml4CmNvcl9tYXRyaXggPC0gcm91bmQoY29yKGNvcl92YXJzLCB1c2UgPSAiY29tcGxldGUub2JzIiksIDIpCmNvcl9tZWx0ZWQgPC0gbWVsdChjb3JfbWF0cml4KQoKIyBQbG90IHdpdGggeWVsbG93IG1pZHBvaW50CmdncGxvdChjb3JfbWVsdGVkLCBhZXMoVmFyMSwgVmFyMiwgZmlsbCA9IHZhbHVlKSkgKwogIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdmFsdWUpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gIiNFNjM5NDYiLCBtaWQgPSAiI0ZGRDcwMCIsIGhpZ2ggPSAiIzFEMzU1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSAwLjYsIGxpbWl0cyA9IGMoMCwgMSksIHNwYWNlID0gIkxhYiIpICsKICBsYWJzKHRpdGxlID0gIkNvcnJlbGF0aW9uIEhlYXRtYXAgb2YgS2V5IFBlcmZvcm1hbmNlIE1ldHJpY3MiLAogICAgICAgeCA9ICIiLCB5ID0gIiIsIGZpbGwgPSAiQ29ycmVsYXRpb24iKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkKICApCmBgYAoKYGBge3J9CiMgVGVhbSBDb21wYXJpc29uIEJhciBDaGFydDogS2V5IFBhc3NlcwpnZ3Bsb3QobWVyZ2VkX2RhdGEsIGFlcyh4ID0gcmVvcmRlcihzcXVhZCwga3ApLCB5ID0ga3AsIGZpbGwgPSBjbHVzdGVyKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIpICsKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkhpZ2gtUGVyZm9ybWVycyIgPSAiI0U2Mzk0NiIsICJNaWQtVGllciIgPSAiI0Y0QTI2MSIsICJMb3ctUGVyZm9ybWVycyIgPSAiIzQ1N0I5RCIpKSArCiAgbGFicyh0aXRsZSA9ICJLZXkgUGFzc2VzIGJ5IFRlYW0iLCB4ID0gIlRlYW0iLCB5ID0gIktleSBQYXNzZXMiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxNCkKYGBgCmBgYHtyfQojIExvYWQgUENBIHRvb2xzCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGZhY3RvZXh0cmEpICAKCiMgUHJlcGFyZSBQQ0EgZGF0YQpwY2FfdmFycyA8LSBtZXJnZWRfZGF0YSAlPiUKICBzZWxlY3Qoa3AsIHByZ3AsIHhhZywgeGcsIGBjbXAlLi4uN2AsIHByZ2Rpc3QpCgojIFBlcmZvcm0gUENBIHdpdGggc2NhbGluZwpwY2FfcmVzIDwtIHByY29tcChwY2FfdmFycywgc2NhbGUuID0gVFJVRSkKCiMgQ3JlYXRlIGEgYmlwbG90IHVzaW5nIGZhY3RvZXh0cmEKZnZpel9wY2FfYmlwbG90KAogIHBjYV9yZXMsCiAgbGFiZWwgPSAidmFyIiwgICAgICAgICAgIyBzaG93IHZhcmlhYmxlIGFycm93cwogIGhhYmlsbGFnZSA9IG1lcmdlZF9kYXRhJGNsdXN0ZXIsICAjIGNvbG9yIGJ5IGNsdXN0ZXIKICBhZGRFbGxpcHNlcyA9IFRSVUUsICAgICAjIG9wdGlvbmFsOiBhZGRzIGNvbmZpZGVuY2UgZWxsaXBzZXMgYXJvdW5kIGNsdXN0ZXJzCiAgY29sLnZhciA9ICJibGFjayIsICAgICAgIyBhcnJvdyBjb2xvcgogIHJlcGVsID0gVFJVRSwgICAgICAgICAgICMgcHJldmVudCBvdmVybGFwcGluZyBsYWJlbHMKICBsYWJlbHNpemUgPSA1LAogIHBvaW50c2hhcGUgPSAyMSwKICBnZW9tID0gInBvaW50IiwKICBwYWxldHRlID0gYygiI0U2Mzk0NiIsICIjRjRBMjYxIiwgIiM0NTdCOUQiKQopICsKICBsYWJzKHRpdGxlID0gIlBDQSBCaXBsb3Q6IFdvbWVuJ3MgU3VwZXIgTGVhZ3VlIFBsYXlpbmcgU3R5bGVzIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpCmBgYApgYGB7cn0KbGlicmFyeShmbXNiKQoKIyBDaG9vc2UgdGVhbXMgdG8gY29tcGFyZQp0ZWFtc190b19jb21wYXJlIDwtIGMoIkNoZWxzZWEiLCAiQXN0b24gVmlsbGEiLCAiUmVhZGluZyIpCgojIFNlbGVjdCBrZXkgbWV0cmljcyB0byBkaXNwbGF5CnJhZGFyX2RhdGEgPC0gbWVyZ2VkX2RhdGEgJT4lCiAgZmlsdGVyKHNxdWFkICVpbiUgdGVhbXNfdG9fY29tcGFyZSkgJT4lCiAgc2VsZWN0KHNxdWFkLCBwcmdwLCB4ZywgeGFnLCBrcCwgYGNtcCUuLi43YCkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJzcXVhZCIpCgojIE5vcm1hbGl6ZTogYWRkIG1heCBhbmQgbWluIHJvd3MgZm9yIHJhZGFyIHBsb3QgYm91bmRzCnJhZGFyX21heCA8LSBhcHBseShyYWRhcl9kYXRhLCAyLCBtYXgpCnJhZGFyX21pbiA8LSBhcHBseShyYWRhcl9kYXRhLCAyLCBtaW4pCnJhZGFyX3Bsb3RfZGF0YSA8LSByYmluZChyYWRhcl9tYXgsIHJhZGFyX21pbiwgcmFkYXJfZGF0YSkKCiMgUGxvdApjb2xvcnMgPC0gYygiI0U2Mzk0NiIsICIjRjRBMjYxIiwgIiM0NTdCOUQiKQpyYWRhcmNoYXJ0KHJhZGFyX3Bsb3RfZGF0YSwKICAgICAgICAgICBheGlzdHlwZSA9IDEsCiAgICAgICAgICAgcGNvbCA9IGNvbG9ycywKICAgICAgICAgICBwZmNvbCA9IGFkanVzdGNvbG9yKGNvbG9ycywgYWxwaGEuZiA9IDAuMjUpLAogICAgICAgICAgIHBsd2QgPSAyLAogICAgICAgICAgIGNnbGNvbCA9ICJncmF5IiwKICAgICAgICAgICBjZ2x0eSA9IDEsCiAgICAgICAgICAgYXhpc2xhYmNvbCA9ICJncmF5IiwKICAgICAgICAgICB2bGNleCA9IDAuOSwKICAgICAgICAgICB0aXRsZSA9ICJUZWFtIFN0eWxlIENvbXBhcmlzb24gUmFkYXIgQ2hhcnQiKQoKbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IHJvd25hbWVzKHJhZGFyX2RhdGEpLCBjb2wgPSBjb2xvcnMsIGx0eSA9IDEsIGx3ZCA9IDIsIGJ0eSA9ICJuIikKYGBgCmBgYHtyfQojIEFscmVhZHkgZml0dGVkIG1vZGVsOiBsbV9wdHMKbWVyZ2VkX2RhdGEkcHJlZGljdGVkX3B0cyA8LSBwcmVkaWN0KGxtX3B0cykKCmdncGxvdChtZXJnZWRfZGF0YSwgYWVzKHggPSBwcmVkaWN0ZWRfcHRzLCB5ID0gcHRzKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQsIGNvbG9yID0gIiMxRDM1NTciKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiI0U2Mzk0NiIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gc3F1YWQpLCBzaXplID0gNC41KSArCiAgbGFicyh0aXRsZSA9ICJBY3R1YWwgdnMgUHJlZGljdGVkIFBvaW50cyIsCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJSLXNxdWFyZWQ6Iiwgcm91bmQoc3VtbWFyeShsbV9wdHMpJGFkai5yLnNxdWFyZWQsIDIpKSwKICAgICAgIHggPSAiUHJlZGljdGVkIFBvaW50cyAoUmVncmVzc2lvbiBNb2RlbCkiLAogICAgICAgeSA9ICJBY3R1YWwgUG9pbnRzIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpCmBgYApgYGB7cn0KbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQoKc3VtbWFyeV90YWJsZSA8LSBtZXJnZWRfZGF0YSAlPiUKICBzZWxlY3Qoc3F1YWQsIGNsdXN0ZXIsIHByZ3AsIHhnLCBrcCwgcHRzKSAlPiUKICBhcnJhbmdlKGRlc2MocHRzKSkKCiMgRGlzcGxheSBhcyBhIHN0eWxlZCB0YWJsZQprYWJsZShzdW1tYXJ5X3RhYmxlLCBmb3JtYXQgPSAibWFya2Rvd24iLCBkaWdpdHMgPSAxLAogICAgICBjb2wubmFtZXMgPSBjKCJUZWFtIiwgIkNsdXN0ZXIiLCAiUHJvZ3Jlc3NpdmUgUGFzc2VzIiwgInhHIiwgIktleSBQYXNzZXMiLCAiUG9pbnRzIikpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGKQpgYGAKYGBge3J9CndyaXRlLmNzdihzdW1tYXJ5X3RhYmxlLCAiV1NMX3N1bW1hcnlfdGFibGUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYApgYGB7cn0KIyBQcmludCBhbGwgcmVzdWx0cyBmcm9tIGFib3ZlIHZpc3VhbHMgCiMgMS4gTGluZWFyIFJlZ3Jlc3Npb24gU3VtbWFyeQpjYXQoIkxpbmVhciBSZWdyZXNzaW9uIFN1bW1hcnk6IFByZWRpY3RpbmcgUG9pbnRzXG5cbiIpCnN1bW1hcnkobG1fcHRzKQoKIyAyLiBQQ0EgTG9hZGluZ3MKY2F0KCJcblBDQSBMb2FkaW5ncyBmb3IgUHJpbmNpcGFsIENvbXBvbmVudCAxIChBdHRhY2tpbmcgU3R5bGUgRGltZW5zaW9uKVxuIikKcHJpbnQocm91bmQocGNhX3JlcyRyb3RhdGlvblssIDFdLCAzKSkKCiMgMy4gQ2x1c3RlciBNZW1iZXJzaGlwIGJ5IFRlYW0KY2F0KCJcbkNsdXN0ZXIgTWVtYmVyc2hpcCBieSBUZWFtXG4iKQpjbHVzdGVyX21lbWJlcnNoaXAgPC0gbWVyZ2VkX2RhdGEgJT4lCiAgc2VsZWN0KHNxdWFkLCBjbHVzdGVyLCBwdHMsIHByZ3AsIHhnKSAlPiUKICBhcnJhbmdlKGNsdXN0ZXIsIGRlc2MocHRzKSkKcHJpbnQoY2x1c3Rlcl9tZW1iZXJzaGlwKQoKIyA0LiBDbHVzdGVyLUxldmVsIFN1bW1hcnkKY2F0KCJcbkNsdXN0ZXItTGV2ZWwgU3VtbWFyeTogQXZlcmFnZSBUZWFtIFN0YXRpc3RpY3NcbiIpCmNsdXN0ZXJfc3VtbWFyeSA8LSBtZXJnZWRfZGF0YSAlPiUKICBncm91cF9ieShjbHVzdGVyKSAlPiUKICBzdW1tYXJpc2UoCiAgICBBdmVyYWdlX1BvaW50cyA9IHJvdW5kKG1lYW4ocHRzKSwgMSksCiAgICBBdmVyYWdlX1Byb2dyZXNzaXZlX1Bhc3NlcyA9IHJvdW5kKG1lYW4ocHJncCksIDEpLAogICAgQXZlcmFnZV94RyA9IHJvdW5kKG1lYW4oeGcpLCAxKSwKICAgIEF2ZXJhZ2VfS2V5X1Bhc3NlcyA9IHJvdW5kKG1lYW4oa3ApLCAxKQogICkKcHJpbnQoY2x1c3Rlcl9zdW1tYXJ5KQoKIyA1LiBUb3AgUGVyZm9ybWluZyBUZWFtcyBieSBLZXkgTWV0cmljcwpjYXQoIlxuVG9wIDMgVGVhbXMgYnkgUHJvZ3Jlc3NpdmUgUGFzc2VzLCBFeHBlY3RlZCBHb2FscyAoeEcpLCBhbmQgS2V5IFBhc3Nlc1xuIikKCnRvcF9wcmdwIDwtIG1lcmdlZF9kYXRhICU+JSBzZWxlY3Qoc3F1YWQsIHByZ3ApICU+JSBhcnJhbmdlKGRlc2MocHJncCkpICU+JSBoZWFkKDMpCnRvcF94ZyAgIDwtIG1lcmdlZF9kYXRhICU+JSBzZWxlY3Qoc3F1YWQsIHhnKSAlPiUgYXJyYW5nZShkZXNjKHhnKSkgJT4lIGhlYWQoMykKdG9wX2twICAgPC0gbWVyZ2VkX2RhdGEgJT4lIHNlbGVjdChzcXVhZCwga3ApICU+JSBhcnJhbmdlKGRlc2Moa3ApKSAlPiUgaGVhZCgzKQoKY2F0KCJcblRvcCBUZWFtcyBieSBQcm9ncmVzc2l2ZSBQYXNzZXM6XG4iKQpwcmludCh0b3BfcHJncCkKCmNhdCgiXG5Ub3AgVGVhbXMgYnkgRXhwZWN0ZWQgR29hbHMgKHhHKTpcbiIpCnByaW50KHRvcF94ZykKCmNhdCgiXG5Ub3AgVGVhbXMgYnkgS2V5IFBhc3NlczpcbiIpCnByaW50KHRvcF9rcCkKCiMgVGhlIGVuZApgYGAK