Chess Scandal Evaluation

Data Analysis

Run libraries

library(devtools)
library(DBI)
library(tidyverse)
library(RSQLite)
library(DescTools)
library(DT)
library(Datatablethemes)
library(scales)
library(stringi)
library(downloadthis)
library(ggplot2)
library(lubridate)
library(hrbrthemes)
library(patchwork)
library(viridis)
library(ggridges)
library(RColorBrewer)
library(gplots)
library(neuralnet)
library(caret)
library(fastDummies)
library(e1071)

Import and format data

I will now install/load the required libraries which are used to develop the graphics and functions used in this project. I will then import this turbine dataset with the readcsv() function.

library(devtools)
library(DBI)
library(tidyverse)
library(RSQLite)
library(DescTools)
library(DT)
library(Datatablethemes)
library(stringi)
library(downloadthis)
Chess_project <- read_csv(file = "10480_games_with_centipawn_metrics.csv")

EDA

Description:

  • This data is of 10,000 chess games played with the centipawn loss, which is used to measure the accuracy score. The Author Tom Paulat chose to create this data set as there’s rumors of a chess scandal going around with a certain player that won’t be named. The goal is to predict the expected player rating whether they are playing as white or black. I will use simple forecasting techniques, which includes the use of ACPL to help achieve the goal.
dim(Chess_project)
naniar::gg_miss_var(Chess_project)

Chess_project <- Chess_project %>% 
  select(-...1, -PGN, -`White Expected Rating by ACPL`, -`Black Expected Rating by ACPL`) %>% 
  drop_na(Result)

naniar::gg_miss_var(Chess_project)

glimpse(Chess_project)
## [1] 10622    20
## Rows: 10,478
## Columns: 16
## $ Date                 <chr> "3/28/2018", "3/29/2018", "3/29/2018", "3/30/2018…
## $ `Event Name`         <chr> "Philadelphia op 12th", "Philadelphia op 12th", "…
## $ `Event Rounds`       <dbl> 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9…
## $ Round                <chr> "1", "2", "3", "4", "5", "6", "7", "8", "9", "1",…
## $ `White Name`         <chr> "Niemann, Hans Moke", "Niemann, Hans Moke", "Goro…
## $ `Black Name`         <chr> "Oberoi, Shelev", "Popilski, Gil", "Niemann, Hans…
## $ Result               <dbl> 0, 1, 2, 0, 2, 1, 0, 1, 0, 1, 2, 1, 1, 2, 1, 1, 0…
## $ `White ELO`          <dbl> 2302, 2302, 2496, 2509, 2302, 2134, 2302, 2162, 2…
## $ `Black ELO`          <dbl> 1924, 2502, 2302, 2302, 2405, 2302, 2145, 2302, 2…
## $ Moves                <dbl> 39, 36, 50, 38, 39, 46, 44, 46, 56, 38, 57, 60, 6…
## $ `White Av CP Loss`   <dbl> 22, 12, 46, 22, 23, 34, 24, 50, 32, 16, 37, 11, 1…
## $ `Black Av CP Loss`   <dbl> 46, 13, 28, 38, 7, 34, 45, 50, 49, 12, 21, 11, 21…
## $ `Evaluations List`   <chr> "[47, 43, 44, -9, 17, 10, 5, 0, 12, -30, -13, -51…
## $ `White CP Loss List` <chr> "[4, 53, 7, 5, 42, 38, 0, 13, 135, 0, 0, 5, 0, 22…
## $ `Black CP Loss List` <chr> "[1, 26, 0, 12, 17, 10, 19, 14, 35, 12, 10, 11, 1…
## $ `Analysis Depth`     <dbl> 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2…
  • Initially we have 20 variables & 10,622 observations, however, the id & PNG column are useless so I drop them
  • Next I wanted to ensure there’s no N/A’s in the Result column as we want to know the outcome of the game. Also, it fixes the date column where some of the code didn’t get pulled correctly. We can view the before & after in above in ggplot

Summary Statistics & Preliminary Graphs

summary(Chess_project)
##      Date            Event Name         Event Rounds       Round          
##  Length:10478       Length:10478       Min.   : 1.000   Length:10478      
##  Class :character   Class :character   1st Qu.: 9.000   Class :character  
##  Mode  :character   Mode  :character   Median : 9.000   Mode  :character  
##                                        Mean   : 9.408                     
##                                        3rd Qu.:11.000                     
##                                        Max.   :22.000                     
##                                        NA's   :12                         
##   White Name         Black Name            Result         White ELO   
##  Length:10478       Length:10478       Min.   :0.0000   Min.   : 893  
##  Class :character   Class :character   1st Qu.:0.0000   1st Qu.:2439  
##  Mode  :character   Mode  :character   Median :1.0000   Median :2558  
##                                        Mean   :0.8846   Mean   :2535  
##                                        3rd Qu.:1.0000   3rd Qu.:2688  
##                                        Max.   :2.0000   Max.   :2882  
##                                                                       
##    Black ELO        Moves        White Av CP Loss Black Av CP Loss
##  Min.   : 700   Min.   :  0.00   Min.   :  0.00   Min.   :  0.0   
##  1st Qu.:2430   1st Qu.: 33.00   1st Qu.: 15.00   1st Qu.: 15.0   
##  Median :2553   Median : 41.00   Median : 23.00   Median : 25.0   
##  Mean   :2526   Mean   : 44.28   Mean   : 26.84   Mean   : 28.5   
##  3rd Qu.:2686   3rd Qu.: 54.00   3rd Qu.: 35.00   3rd Qu.: 39.0   
##  Max.   :2882   Max.   :182.00   Max.   :458.00   Max.   :476.0   
##                                                                   
##  Evaluations List   White CP Loss List Black CP Loss List Analysis Depth
##  Length:10478       Length:10478       Length:10478       Min.   :20    
##  Class :character   Class :character   Class :character   1st Qu.:20    
##  Mode  :character   Mode  :character   Mode  :character   Median :20    
##                                                           Mean   :20    
##                                                           3rd Qu.:20    
##                                                           Max.   :22    
##                                                           NA's   :1
t(t(unique(Chess_project$Round)))[72,]
## [1] "?"
Chess_project$Date <- as.Date(Chess_project$Date, "%m/%d/%y")
Chess_project$Round <- as.numeric(Chess_project$Round)
Chess_project$Result <- as.factor(Chess_project$Result)

summary(Chess_project)
##       Date             Event Name         Event Rounds        Round       
##  Min.   :2019-01-01   Length:10478       Min.   : 1.000   Min.   : 1.000  
##  1st Qu.:2020-03-26   Class :character   1st Qu.: 9.000   1st Qu.: 3.000  
##  Median :2020-06-30   Mode  :character   Median : 9.000   Median : 5.000  
##  Mean   :2020-06-21                      Mean   : 9.408   Mean   : 5.275  
##  3rd Qu.:2020-09-21                      3rd Qu.:11.000   3rd Qu.: 7.800  
##  Max.   :2020-12-31                      Max.   :22.000   Max.   :15.800  
##                                          NA's   :12       NA's   :29      
##   White Name         Black Name        Result     White ELO      Black ELO   
##  Length:10478       Length:10478       0:3703   Min.   : 893   Min.   : 700  
##  Class :character   Class :character   1:4281   1st Qu.:2439   1st Qu.:2430  
##  Mode  :character   Mode  :character   2:2494   Median :2558   Median :2553  
##                                                 Mean   :2535   Mean   :2526  
##                                                 3rd Qu.:2688   3rd Qu.:2686  
##                                                 Max.   :2882   Max.   :2882  
##                                                                              
##      Moves        White Av CP Loss Black Av CP Loss Evaluations List  
##  Min.   :  0.00   Min.   :  0.00   Min.   :  0.0    Length:10478      
##  1st Qu.: 33.00   1st Qu.: 15.00   1st Qu.: 15.0    Class :character  
##  Median : 41.00   Median : 23.00   Median : 25.0    Mode  :character  
##  Mean   : 44.28   Mean   : 26.84   Mean   : 28.5                      
##  3rd Qu.: 54.00   3rd Qu.: 35.00   3rd Qu.: 39.0                      
##  Max.   :182.00   Max.   :458.00   Max.   :476.0                      
##                                                                       
##  White CP Loss List Black CP Loss List Analysis Depth
##  Length:10478       Length:10478       Min.   :20    
##  Class :character   Class :character   1st Qu.:20    
##  Mode  :character   Mode  :character   Median :20    
##                                        Mean   :20    
##                                        3rd Qu.:20    
##                                        Max.   :22    
##                                        NA's   :1
  • Notice all of the list variables are coded as character so, later in data wrangling will fix that by converting them into numeric lists.

  • ‘Date’ needs to be reformatted to POSIXct, ‘Round’ is a character format because ‘?’ is not treated as N/A, ’Result needs to be formatted as a factor.

Histograms

## White ELO
bw <- Chess_project %>% 
  select(`White ELO`) %>% 
  filter(!is.na(`White ELO`))

bw <- 2 * IQR(bw$`White ELO`) / length(bw$`White ELO`)^(1/3)

g1 <- Chess_project %>%
  ggplot(aes(x = `White ELO`)) +
  geom_histogram(binwidth = bw,
                 fill = 'green',
                 color = 'black',
                 show.legend = F,
                 alpha = .5) +
  labs(title = "(g1) Distribution of White ELO",
       x = "White ELO",
       y = "Frequency") +
  hrbrthemes::theme_ft_rc()+
  theme(axis.title = element_text(color = "white"),
        axis.text = element_text(color = "white"))

## Black ELO
bw <- Chess_project %>% 
  select(`Black ELO`) %>% 
  filter(!is.na(`Black ELO`))

bw <- 2 * IQR(bw$`Black ELO`) / length(bw$`Black ELO`)^(1/3)

g2 <- Chess_project %>%
  ggplot(aes(x = `Black ELO`)) +
  geom_histogram(binwidth = bw,
                 fill = 'green',
                 color = 'black',
                 show.legend = F,
                 alpha = .5) +
  labs(title = "(g2) Distribution of Black ELO",
       x = "Black ELO",
       y = "Frequency") +
  hrbrthemes::theme_ft_rc()+
  theme(axis.title = element_text(color = "white"),
        axis.text = element_text(color = "white"))

## Combining
(g1 /g2) +
  plot_annotation(theme = theme(plot.title = element_text(size = 18,
                                                          colour = "black"))) +
  theme(text = element_text('mono'))

  • Both histograms are skewed to the left because most of chess players at these tournaments are highly rated in terms of ELO.

  • Therefore, if we were going to impute any values we should use the median as its less subjective to outliers

  • The mean is less than the median confirming our suspicion. See table below

White ELO Black ELO
Mean 2,535 2,526
Median 2,558 2,553

Box plots

## White AVG CP Loss
g3 <- Chess_project %>%
  ggplot(aes(x = `White Av CP Loss`)) +
  geom_boxplot(show.legend = F,
               alpha = .5,
               fill = "#FF8C00",
               color = "#0DDBCC") +
  labs(title = "(g3) White Av CP Loss",
       x = "White Av CP Loss") +
  hrbrthemes::theme_modern_rc()+
  theme(axis.title = element_text(color = "#0DDBCC"),
        axis.text = element_text(color = "#0DDBCC"),
        plot.title = element_text(color = "#FF8C00"))

## Black AVG CP Loss
g4 <- Chess_project %>%
  ggplot(aes(x = `Black Av CP Loss`)) +
  geom_boxplot(show.legend = F,
               alpha = .5,
               fill = "#FF8C00",
               color = "#0DDBCC") +
  labs(title = "(g4) Black Av CP Loss",
       x = "Black Av CP Loss") +
  hrbrthemes::theme_modern_rc()+
  theme(axis.title = element_text(color = "#0DDBCC"),
        axis.text = element_text(color = "#0DDBCC"),
        plot.title = element_text(color = "#FF8C00"))

## Total Moves
g5 <- Chess_project %>%
  ggplot(aes(x = Moves)) +
  geom_boxplot(show.legend = F,
               alpha = .5,
               fill = "#FF8C00",
               color = "#0DDBCC") +
  labs(title = "(g5) Moves",
       x = "Moves") +
  hrbrthemes::theme_modern_rc()+
  theme(axis.title = element_text(color = "#0DDBCC"),
        axis.text = element_text(color = "#0DDBCC"),
        plot.title = element_text(color = "#FF8C00"))

## Combine
((g3 | g4) / g5) +
  plot_annotation(theme = theme(plot.title = element_text(size = 18,
                                                          colour = "black"))) +
  theme(text = element_text('mono'))

## Total Moves aggregated by Result of game
Chess_project %>% 
  mutate(Result = recode(Result,
                         "0" = "Black win",
                         "1" = "Draw",
                         "2" = "White win")) %>% 
  ggplot(aes(Moves, Result, fill = Result)) +
  geom_boxplot(alpha = 0.5, color = "white") +
  stat_summary(fun.y = mean, geom = "text", label = "X", color = "white")+
  scale_fill_manual(values = c("Black win" = "#ffe042",
                               "Draw" = "#e71989",
                               "White win" = "#00e1d9")) +
  labs(title = "(g6) Total moves made",
       subtitle = "disagregated by result of game",
       x = "Total Moves",
       y = "Result of game") +
  hrbrthemes::theme_modern_rc()+
  theme(legend.position = "none")+
  annotate("text", x=38, y=1, label= "42", color = "white")+
  annotate("text", x=36, y=2, label= "40", color = "white")+
  annotate("text", x=39, y=3, label= "43", color = "white")

# ggsave(file = "Total moves made aggregated by result of game.png",
#        units = c("in"),
#        width = 8,
#        height = 5.5,
#        dpi=700,
#        g6)
  • The first thing to notice is that all of the box plots have a lot of outliers, therefore if we were to impute any values for these variables, median would be the best choice.

  • Next, we can see that for both g3 & g4 the AVG CP Loss for most players is quite low. This is to be expected as lot players are Grandmasters and should have a lower centipawn loss

  • For graph g6 we can see that there all positively skewed meaning, the mean is greater than the median. From this we can conclude that 75% of matches end no matter the result in around 52 moves or less. However, there is a long right tail of outliers where the game ends with many more moves.

## Line plots

## White ELO VS White AVG CP Loss
g7 <- Chess_project %>%
  ggplot(aes(x = `White ELO`,
             y = `White Av CP Loss`)) +
  geom_point(color = "#2b6be4", size = 2,
             alpha = 0.7) +
  geom_smooth(color = "#00008b")+
  labs(title = "(g7) White ELO vs. AVG CP Loss",
       subtitle = "with smoothed linear model",
       x = "White ELO",
       y = "White AVG CP Loss") +
  ggthemes::theme_solarized()

## Black ELO Vs Black AVG CP Loss
g8 <- Chess_project %>%
  ggplot(aes(x = `Black ELO`,
             y = `Black Av CP Loss`)) +
  geom_point(color = "#2b6be4", size = 2,
             alpha = 0.7) +
  geom_smooth(color = "#00008b")+
  labs(title = "(g8) Black ELO vs. AVG CP Loss",
       subtitle = "with smoothed linear model",
       x = "Black ELO",
       y = "Black AVG CP Loss") +
  ggthemes::theme_solarized()

## Combine graphs
(g7 /g8) +
  plot_annotation(theme = theme(plot.title = element_text(size = 18,
                                                          colour = "black"))) +
  theme(text = element_text('mono'))

## White ELO vs Black AVG CP Loss
g9 <- Chess_project %>%
  ggplot(aes(x = `White ELO`,
             y = `Black Av CP Loss`)) +
  geom_point(color = "#2b6be4", size = 2,
             alpha = 0.7) +
  geom_smooth(color = "#00008b")+
  labs(title = "(g7) White ELO vs. Black AVG CP Loss",
       subtitle = "with smoothed linear model",
       x = "White ELO",
       y = "Black AVG CP Loss") +
  ggthemes::theme_solarized()

## Black ELO vs White AVG CP Loss
g10 <- Chess_project %>%
  ggplot(aes(x = `Black ELO`,
             y = `White Av CP Loss`)) +
  geom_point(color = "#2b6be4", size = 2,
             alpha = 0.7) +
  geom_smooth(color = "#00008b")+
  labs(title = "(g7) Black ELO vs. White AVG CP Loss",
       subtitle = "with smoothed linear model",
       x = "Black ELO",
       y = "White AVG CP Loss") +
  ggthemes::theme_solarized()

## Combine Graph
(g9 /g10) +
  plot_annotation(theme = theme(plot.title = element_text(size = 18,
                                                          colour = "black"))) +
  theme(text = element_text('mono'))

## Combine with result of game
Chess_project %>%
  mutate(Result = recode(Result,
                         "0" = "Black win",
                         "1" = "Draw",
                         "2" = "White win")) %>% 
  ggplot(aes(x = `White ELO`,
             y = `White Av CP Loss`,
             color = Result)) +
  geom_point(size = 5,
             alpha = 0.3) +
  geom_line(size = 1)+
  labs(title = "(g11) White ELO vs. AVG CP Loss",
       subtitle = "Aggregated by Result of game",
       x = "White ELO",
       y = "White AVG CP Loss") +
  ggthemes::theme_solarized()

## Checking to make sure the avg cp Loss matches (Need to run code below for this to work)
# Chess_project %>% 
#   left_join(., 
#             Chess_project %>% 
#   group_by(`White CP Loss List`) %>% 
#   summarise(White_AVG = round(mean(`White CP Loss List`[[1]]), digits = 0)),
#   by = "White CP Loss List") %>%
#   select(`White Av CP Loss`, White_AVG) %>% 
#   mutate(example = case_when(
#     `White Av CP Loss` == White_AVG ~ "Yes",
#     TRUE ~ "No"
#   )) %>% 
#   filter(example == "Yes")

## Could use mutate but the group by gets rid of 4 rows because there are duplicates

# Chess_project %>%
#   group_by(`White CP Loss List`) %>% 
#   mutate(White_AVG = round(mean(`White CP Loss List`[[1]]), digits = 0))
  • We can see that when comparing the avg CP loss & ELO of the same color there is a slight decrease in avg CP loss the higher the ELO, which should be the case considering you should make less mistakes the higher your ELO is

  • The opposite can be said for comparing the colors as there doesn’t seem to be much correlation and results in a relatively flat line.

  • The last graph shows there is a big outlier where black won because white had a high avg CP loss. Should be the case if white makes that many mistakes.

Data Wrangling

There isn’t much data wrangling to be done however, when exploring the chess data I noticed that the list variables were not coded correctly as they were in a character format. To fix this I used the gsub() function to reformat the variables to a list but they were now read as a character list. I then use a for() loop to correctly format them as a numeric list. This ensures if I want to pull these lists, I can perform numeric computations with out any issues. See code below!

Chess_project <- Chess_project %>% 
  mutate(`Evaluations List` = gsub(`Evaluations List`, pattern = "[", fixed = T, replacement = "")) %>%
  mutate(`Evaluations List` = gsub(`Evaluations List`, pattern = ",", fixed = T, replacement = "")) %>% 
  mutate(`Evaluations List` = str_extract_all(`Evaluations List`, "\\(?[0-9,.-]+\\)?")) %>% 
  mutate(`White CP Loss List` = gsub(`White CP Loss List`, pattern = "[", fixed = T, replacement = "")) %>%
  mutate(`White CP Loss List` = gsub(`White CP Loss List`, pattern = ",", fixed = T, replacement = "")) %>% 
  mutate(`White CP Loss List` = str_extract_all(`White CP Loss List`, "\\(?[0-9,.-]+\\)?")) %>% 
  mutate(`Black CP Loss List` = gsub(`Black CP Loss List`, pattern = "[", fixed = T, replacement = "")) %>%
  mutate(`Black CP Loss List` = gsub(`Black CP Loss List`, pattern = ",", fixed = T, replacement = "")) %>% 
  mutate(`Black CP Loss List` = str_extract_all(`Black CP Loss List`, "\\(?[0-9,.-]+\\)?"))

## need to perform on specific columns
for (i in 1:length(Chess_project$`Evaluations List`)) {
  Chess_project$`Evaluations List`[[i]] <- as.numeric(Chess_project$`Evaluations List`[[i]])
}
for (i in 1:length(Chess_project$`White CP Loss List`)) {
  Chess_project$`White CP Loss List`[[i]] <- as.numeric(Chess_project$`White CP Loss List`[[i]])
}
for (i in 1:length(Chess_project$`Black CP Loss List`)) {
  Chess_project$`Black CP Loss List`[[i]] <- as.numeric(Chess_project$`Black CP Loss List`[[i]])
}

glimpse(Chess_project)
## Rows: 10,478
## Columns: 16
## $ Date                 <date> 2020-03-28, 2020-03-29, 2020-03-29, 2020-03-30, …
## $ `Event Name`         <chr> "Philadelphia op 12th", "Philadelphia op 12th", "…
## $ `Event Rounds`       <dbl> 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9…
## $ Round                <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 1…
## $ `White Name`         <chr> "Niemann, Hans Moke", "Niemann, Hans Moke", "Goro…
## $ `Black Name`         <chr> "Oberoi, Shelev", "Popilski, Gil", "Niemann, Hans…
## $ Result               <fct> 0, 1, 2, 0, 2, 1, 0, 1, 0, 1, 2, 1, 1, 2, 1, 1, 0…
## $ `White ELO`          <dbl> 2302, 2302, 2496, 2509, 2302, 2134, 2302, 2162, 2…
## $ `Black ELO`          <dbl> 1924, 2502, 2302, 2302, 2405, 2302, 2145, 2302, 2…
## $ Moves                <dbl> 39, 36, 50, 38, 39, 46, 44, 46, 56, 38, 57, 60, 6…
## $ `White Av CP Loss`   <dbl> 22, 12, 46, 22, 23, 34, 24, 50, 32, 16, 37, 11, 1…
## $ `Black Av CP Loss`   <dbl> 46, 13, 28, 38, 7, 34, 45, 50, 49, 12, 21, 11, 21…
## $ `Evaluations List`   <list> <47, 43, 44, -9, 17, 10, 5, 0, 12, -30, -13, -51…
## $ `White CP Loss List` <list> <4, 53, 7, 5, 42, 38, 0, 13, 135, 0, 0, 5, 0, 22…
## $ `Black CP Loss List` <list> <1, 26, 0, 12, 17, 10, 19, 14, 35, 12, 10, 11, 1…
## $ `Analysis Depth`     <dbl> 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2…
# mean(Chess_project$`White CP Loss List`[[1]])

Next I want to visualize players with the top 6 most played games to get a better understanding of who occurs most in the data set. See chart below!

## g1
Chess_project %>% 
  count(`White Name`) %>% 
  arrange(desc(n)) %>% 
  rename(Count = n) %>% 
  mutate(`White Name` = fct_reorder(`White Name`, Count)) %>% 
  head() %>% 
  ggplot() +
  aes(x = `White Name`, 
      y = Count, 
      fill = `White Name`, 
      label = Count) +
  geom_bar(stat='identity', 
           alpha = 0.5,
           show.legend = F,
           color = "black") +
  scale_fill_brewer(palette = "YlGnBu", direction = 1) +
  theme(legend.position = "right", 
        axis.text.x = element_text(angle = 45, hjust = 0.8)) +
  labs(title = "(g1) Barplot of Amount of Games Played",
       x = "Player",
       y = "How many games played by player") +
  ggthemes::theme_solarized() +
  geom_text(nudge_y = 2, 
            vjust = 3,
            color = "black",
            size = 4.3) +
  theme(axis.text.x = element_text(size = 12, angle = 45, hjust = 1))

To conclude the data wrangling lets create a correlation Matrix Plot on our numeric variables to see which variables we want to keep or drop before we perform any models on our data. See plot Below!

## Need library(viridis)
corrmatrix <- as.data.frame(Chess_project)

colfunc <- colorRampPalette(brewer.pal(9,"BrBG"))
heatmap.2(cor(Filter(is.numeric, corrmatrix), use = "complete.obs"), Rowv = FALSE, 
          Colv = FALSE, dendrogram = "none", lwid=c(.1,2), lhei=c(.1,2), 
          col = colfunc(15), 
          cellnote = round(cor(Filter(is.numeric, corrmatrix), use = "complete.obs"),2),
          notecol = "black", key = FALSE, trace = 'none', margins = c(10,10))

  • From this we can really only conclude that we should drop the numeric lists because there will be multicollinearity between them and the AVG CP loss

  • We can move on to Variance Analysis

Analysis of Variance

Will use an anova test as we have three or more categories when talking about the results variable, which is all we really care about. See test below!

Will start by testing total moves made in a match to the result of the match.

  • (H0) - Null hypothesis: the means of total moves made are the same between the results of the match.

  • (H1) - Alternative: The difference is real and there is a difference in the means

  • Alpha Value: 0.05

Chess_project <- Chess_project %>% 
  select(-`Evaluations List`, -`White CP Loss List`, -`Black CP Loss List`)

Chess_project %>% 
  mutate(Result = recode(Result,
                         "0" = "Black win",
                         "1" = "Draw",
                         "2" = "White win")) %>% 
  select(Result, Moves) %>% 
  group_by(Result) %>% 
  summarise(mean_moves = mean(Moves)) %>% 
  arrange(mean_moves)
## # A tibble: 3 × 2
##   Result    mean_moves
##   <fct>          <dbl>
## 1 Draw            42.8
## 2 Black win       45.0
## 3 White win       45.8
Chess_project %>% 
  mutate(Result = recode(Result,
                         "0" = "Black win",
                         "1" = "Draw",
                         "2" = "White win")) %>% 
  select(Result, Moves) %>% 
  aov(Moves ~ Result, data = .) %>% 
  summary() # Honestly significant difference
##                Df  Sum Sq Mean Sq F value  Pr(>F)    
## Result          2   16884    8442   26.39 3.7e-12 ***
## Residuals   10475 3351206     320                    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
  • We can conclude that the difference is significant & we can reject (H0)

  • We have proven that at least one of these is different from the other two but we haven’t defined which one.

Chess_project %>% 
  mutate(Result = recode(Result,
                         "0" = "Black win",
                         "1" = "Draw",
                         "2" = "White win")) %>% 
  select(Result, Moves) %>% 
  aov(Moves ~ Result, data = .) %>% 
  TukeyHSD() # Honestly significant difference
##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = Moves ~ Result, data = .)
## 
## $Result
##                           diff        lwr       upr     p adj
## Draw-Black win      -2.1514386 -3.0923492 -1.210528 0.0000003
## White win-Black win  0.8585284 -0.2275301  1.944587 0.1526681
## White win-Draw       3.0099670  1.9538273  4.066107 0.0000000
  • Draw-Black Win & Draw-White Win are both significant, meaning there is a difference in the means.

  • White Win-Black Win is not significant meaning the means are similar

Fitting a Neural Network

pre-processing

Chess_project <- Chess_project %>% 
  mutate(Result = recode(Result,
                         "0" = "Black_win",
                         "1" = "Draw",
                         "2" = "White_win")) %>% 
  rename(White_ELO = `White ELO`) %>% 
  rename(Black_ELO = `Black ELO`) %>% 
  rename(Event_Rounds = `Event Rounds`) %>% 
  rename(White_Av_CP_Loss = `White Av CP Loss`) %>% 
  rename(Black_Av_CP_Loss = `Black Av CP Loss`)

Chess_project <- dummy_cols(Chess_project, 
                        select_columns = c("Result"),
                        remove_first_dummy = TRUE,
                        remove_selected_columns = TRUE)

Checking for highley skewed predictors (less than -1 or greater than 1)

num.cols <- colnames(select_if(Chess_project, is.numeric))

Chess_project <- Chess_project %>% 
  drop_na()

for (i in num.cols) {
  print(i)
  print(skewness(Chess_project[[i]]))
}
## [1] "Event_Rounds"
## [1] -0.4466047
## [1] "Round"
## [1] 0.3724137
## [1] "White_ELO"
## [1] -1.373876
## [1] "Black_ELO"
## [1] -1.77574
## [1] "Moves"
## [1] 1.048866
## [1] "White_Av_CP_Loss"
## [1] 3.558416
## [1] "Black_Av_CP_Loss"
## [1] 2.976917
## [1] "Analysis Depth"
## [1] NaN
## [1] "Result_Draw"
## [1] 0.3714695
## [1] "Result_White_win"
## [1] 1.227654
Chess_project <- Chess_project %>% 
  select(-`Analysis Depth`)

Apply a log transformation to highly skewed predictors

Chess_project$Black_ELO <- log(Chess_project$Black_ELO + 1)
Chess_project$Moves <- log(Chess_project$Moves + 1)
Chess_project$White_Av_CP_Loss <- log(Chess_project$White_Av_CP_Loss + 1)
Chess_project$Black_Av_CP_Loss <- log(Chess_project$Black_Av_CP_Loss + 1)
Chess_project <- Chess_project %>% 
  select_if(., is.numeric)

partition the data

# Could set seed with set.seed()
train.index <- sample(rownames(Chess_project), nrow(Chess_project) * 0.7)
chess.train <- Chess_project[train.index, ]
valid.index <- setdiff(rownames(Chess_project), train.index)
chess.valid <- Chess_project[valid.index, ]

convert all variables (INCLUDING QUANTITATIVE OUTCOME) to a 0-1 scale

chess.train.norm <- chess.train
chess.valid.norm <- chess.valid

cols <- colnames(chess.train)
for (i in cols) {
  chess.valid.norm[[i]] <- 
    (chess.valid.norm[[i]] - min(chess.train[[i]])) / (max(chess.train[[i]]) - min(chess.train[[i]]))
  chess.train.norm[[i]] <- 
    (chess.train.norm[[i]] - min(chess.train[[i]])) / (max(chess.train[[i]]) - min(chess.train[[i]]))
}
summary(chess.train.norm)
##   Event_Rounds        Round          White_ELO        Black_ELO     
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.3810   1st Qu.:0.1351   1st Qu.:0.7778   1st Qu.:0.8803  
##  Median :0.3810   Median :0.2703   Median :0.8376   Median :0.9154  
##  Mean   :0.4009   Mean   :0.2877   Mean   :0.8257   Mean   :0.9036  
##  3rd Qu.:0.4762   3rd Qu.:0.4257   3rd Qu.:0.9030   3rd Qu.:0.9502  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
##      Moves        White_Av_CP_Loss Black_Av_CP_Loss  Result_Draw    
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.:0.6769   1st Qu.:0.2923   1st Qu.:0.3302   1st Qu.:0.0000  
##  Median :0.7220   Median :0.3778   Median :0.4260   Median :0.0000  
##  Mean   :0.7176   Mean   :0.3765   Mean   :0.4168   Mean   :0.4127  
##  3rd Qu.:0.7692   3rd Qu.:0.4633   3rd Qu.:0.5060   3rd Qu.:1.0000  
##  Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  
##  Result_White_win
##  Min.   :0.0000  
##  1st Qu.:0.0000  
##  Median :0.0000  
##  Mean   :0.2341  
##  3rd Qu.:0.0000  
##  Max.   :1.0000
summary(chess.valid.norm)
##   Event_Rounds        Round          White_ELO        Black_ELO      
##  Min.   :0.0000   Min.   :0.0000   Min.   :0.1403   Min.   :0.05715  
##  1st Qu.:0.3810   1st Qu.:0.1351   1st Qu.:0.7755   1st Qu.:0.87882  
##  Median :0.3810   Median :0.2703   Median :0.8346   Median :0.91403  
##  Mean   :0.4012   Mean   :0.2925   Mean   :0.8248   Mean   :0.90324  
##  3rd Qu.:0.4762   3rd Qu.:0.4730   3rd Qu.:0.9005   3rd Qu.:0.95021  
##  Max.   :1.0000   Max.   :0.9730   Max.   :1.0000   Max.   :1.00000  
##      Moves        White_Av_CP_Loss  Black_Av_CP_Loss   Result_Draw    
##  Min.   :0.0000   Min.   :-0.2923   Min.   :-0.2167   Min.   :0.0000  
##  1st Qu.:0.6712   1st Qu.: 0.2923   1st Qu.: 0.3422   1st Qu.:0.0000  
##  Median :0.7175   Median : 0.3778   Median : 0.4260   Median :0.0000  
##  Mean   :0.7132   Mean   : 0.3787   Mean   : 0.4196   Mean   :0.3992  
##  3rd Qu.:0.7657   3rd Qu.: 0.4691   3rd Qu.: 0.5110   3rd Qu.:1.0000  
##  Max.   :0.9472   Max.   : 0.8311   Max.   : 0.8088   Max.   :1.0000  
##  Result_White_win
##  Min.   :0.0000  
##  1st Qu.:0.0000  
##  Median :0.0000  
##  Mean   :0.2485  
##  3rd Qu.:0.0000  
##  Max.   :1.0000

Neural net with 2 hidden layers of 3 nodes

chess.nn.3.3 <- neuralnet(White_ELO ~ .,                  # categorical outcome ~ predictors
                           data = chess.train.norm,   # data for training model
                           linear.output = FALSE,      # assume relationship is nonlinear
                           hidden = c(3,3))            # 2 hidden layers of 3 nodes each
plot(chess.nn.3.3, rep = "best")

predict.nn.3.3 <- predict(chess.nn.3.3, chess.valid.norm)

Accuracy on Validation set

convert back to original scale

min_White_ELO <- min(chess.train$White_ELO)
max_White_ELO <- max(chess.train$White_ELO)

actpred <- data.frame(actual = chess.valid$White_ELO, 
                      predicted = min_White_ELO + predict.nn.3.3*(max_White_ELO - min_White_ELO))
RMSE(actpred$predicted, actpred$actual)
## [1] 136.7458
head(predict.nn.3.3)
##           [,1]
## [1,] 0.6339028
## [2,] 0.7424240
## [3,] 0.7297766
## [4,] 0.6922631
## [5,] 0.6294488
## [6,] 0.7131989
head(actpred)
##   actual predicted
## 1   2496  2153.833
## 2   2302  2369.681
## 3   2162  2344.526
## 4   2302  2269.911
## 5   1853  2144.974
## 6   2313  2311.553

Excel File

Chess Scandal Evaluation

LS0tDQphdXRob3I6ICJFcmlrIFJhbmRhbGwiDQpkYXRlOiAiMjAyMi0xMC0yNCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHBhcGVyDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCiMgKipDaGVzcyBTY2FuZGFsIEV2YWx1YXRpb24qKiB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMgKipEYXRhIEFuYWx5c2lzKioNCg0KIyMjIyAqKlJ1biBsaWJyYXJpZXMqKg0KDQpgYGB7cn0NCmxpYnJhcnkoZGV2dG9vbHMpDQpsaWJyYXJ5KERCSSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShSU1FMaXRlKQ0KbGlicmFyeShEZXNjVG9vbHMpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShEYXRhdGFibGV0aGVtZXMpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkoc3RyaW5naSkNCmxpYnJhcnkoZG93bmxvYWR0aGlzKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGhyYnJ0aGVtZXMpDQpsaWJyYXJ5KHBhdGNod29yaykNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkoZ2dyaWRnZXMpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZ3Bsb3RzKQ0KbGlicmFyeShuZXVyYWxuZXQpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShmYXN0RHVtbWllcykNCmxpYnJhcnkoZTEwNzEpDQpgYGANCg0KIyMjICoqSW1wb3J0IGFuZCBmb3JtYXQgZGF0YSoqDQoNCiMjIyMgVGhzIGRhdGFzZXQgaXMgMTAsMDAwIENoZXNzIEdhbWVzIHdpdGggQ2VudGlwYXduIExvc3MuIEkgZG93bmxvYWRlZCB0aGlzIGRhdGEgc2V0IGZyb20gS2FnZ2xlLmNvbS4gSGVyZSBpcyB0aGUgTGluazogPGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvdG9tcGF1bGF0LzEwMDAwLWNoZXNzLWdhbWVzLXdpdGgtY2VudGlwYXduLWxvc3M+DQoNCiMjIyMgSSB3aWxsIG5vdyBpbnN0YWxsL2xvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcyB3aGljaCBhcmUgdXNlZCB0byBkZXZlbG9wIHRoZSBncmFwaGljcyBhbmQgZnVuY3Rpb25zIHVzZWQgaW4gdGhpcyBwcm9qZWN0LiBJIHdpbGwgdGhlbiBpbXBvcnQgdGhpcyB0dXJiaW5lIGRhdGFzZXQgd2l0aCB0aGUgcmVhZGNzdigpIGZ1bmN0aW9uLg0KDQotLS0NCmxpYnJhcnkoZGV2dG9vbHMpDQpsaWJyYXJ5KERCSSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShSU1FMaXRlKQ0KbGlicmFyeShEZXNjVG9vbHMpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShEYXRhdGFibGV0aGVtZXMpDQpsaWJyYXJ5KHN0cmluZ2kpDQpsaWJyYXJ5KGRvd25sb2FkdGhpcykNCi0tLQ0KDQpgYGB7cn0NCkNoZXNzX3Byb2plY3QgPC0gcmVhZF9jc3YoZmlsZSA9ICIxMDQ4MF9nYW1lc193aXRoX2NlbnRpcGF3bl9tZXRyaWNzLmNzdiIpDQpgYGANCg0KIyMjICoqRURBKioNCg0KIyMjIyBEZXNjcmlwdGlvbjoNCg0KLSAgIA0KDQogICAgIyMjIyMgVGhpcyBkYXRhIGlzIG9mIDEwLDAwMCBjaGVzcyBnYW1lcyBwbGF5ZWQgd2l0aCB0aGUgY2VudGlwYXduIGxvc3MsIHdoaWNoIGlzIHVzZWQgdG8gbWVhc3VyZSB0aGUgYWNjdXJhY3kgc2NvcmUuIFRoZSBBdXRob3IgVG9tIFBhdWxhdCBjaG9zZSB0byBjcmVhdGUgdGhpcyBkYXRhIHNldCBhcyB0aGVyZSdzIHJ1bW9ycyBvZiBhIGNoZXNzIHNjYW5kYWwgZ29pbmcgYXJvdW5kIHdpdGggYSBjZXJ0YWluIHBsYXllciB0aGF0IHdvbid0IGJlIG5hbWVkLiBUaGUgZ29hbCBpcyB0byBwcmVkaWN0IHRoZSBleHBlY3RlZCBwbGF5ZXIgcmF0aW5nIHdoZXRoZXIgdGhleSBhcmUgcGxheWluZyBhcyB3aGl0ZSBvciBibGFjay4gSSB3aWxsIHVzZSBzaW1wbGUgZm9yZWNhc3RpbmcgdGVjaG5pcXVlcywgd2hpY2ggaW5jbHVkZXMgdGhlIHVzZSBvZiBBQ1BMIHRvIGhlbHAgYWNoaWV2ZSB0aGUgZ29hbC4NCg0KYGBge3IgcmVzdWx0cz0naG9sZCd9DQpkaW0oQ2hlc3NfcHJvamVjdCkNCm5hbmlhcjo6Z2dfbWlzc192YXIoQ2hlc3NfcHJvamVjdCkNCg0KQ2hlc3NfcHJvamVjdCA8LSBDaGVzc19wcm9qZWN0ICU+JSANCiAgc2VsZWN0KC0uLi4xLCAtUEdOLCAtYFdoaXRlIEV4cGVjdGVkIFJhdGluZyBieSBBQ1BMYCwgLWBCbGFjayBFeHBlY3RlZCBSYXRpbmcgYnkgQUNQTGApICU+JSANCiAgZHJvcF9uYShSZXN1bHQpDQoNCm5hbmlhcjo6Z2dfbWlzc192YXIoQ2hlc3NfcHJvamVjdCkNCmdsaW1wc2UoQ2hlc3NfcHJvamVjdCkNCmBgYA0KDQotICAgDQoNCiAgICAjIyMjIyBJbml0aWFsbHkgd2UgaGF2ZSAyMCB2YXJpYWJsZXMgJiAxMCw2MjIgb2JzZXJ2YXRpb25zLCBob3dldmVyLCB0aGUgaWQgJiBQTkcgY29sdW1uIGFyZSB1c2VsZXNzIHNvIEkgZHJvcCB0aGVtDQoNCi0gICANCg0KICAgICMjIyMjIE5leHQgSSB3YW50ZWQgdG8gZW5zdXJlIHRoZXJlJ3Mgbm8gTi9BJ3MgaW4gdGhlIFJlc3VsdCBjb2x1bW4gYXMgd2Ugd2FudCB0byBrbm93IHRoZSBvdXRjb21lIG9mIHRoZSBnYW1lLiBBbHNvLCBpdCBmaXhlcyB0aGUgZGF0ZSBjb2x1bW4gd2hlcmUgc29tZSBvZiB0aGUgY29kZSBkaWRuJ3QgZ2V0IHB1bGxlZCBjb3JyZWN0bHkuIFdlIGNhbiB2aWV3IHRoZSBiZWZvcmUgJiBhZnRlciBpbiBhYm92ZSBpbiBnZ3Bsb3QNCg0KIyMjIyBTdW1tYXJ5IFN0YXRpc3RpY3MgJiBQcmVsaW1pbmFyeSBHcmFwaHMNCg0KYGBge3J9DQpzdW1tYXJ5KENoZXNzX3Byb2plY3QpDQoNCnQodCh1bmlxdWUoQ2hlc3NfcHJvamVjdCRSb3VuZCkpKVs3MixdDQoNCkNoZXNzX3Byb2plY3QkRGF0ZSA8LSBhcy5EYXRlKENoZXNzX3Byb2plY3QkRGF0ZSwgIiVtLyVkLyV5IikNCkNoZXNzX3Byb2plY3QkUm91bmQgPC0gYXMubnVtZXJpYyhDaGVzc19wcm9qZWN0JFJvdW5kKQ0KQ2hlc3NfcHJvamVjdCRSZXN1bHQgPC0gYXMuZmFjdG9yKENoZXNzX3Byb2plY3QkUmVzdWx0KQ0KDQpzdW1tYXJ5KENoZXNzX3Byb2plY3QpDQpgYGANCg0KLSAgIA0KDQogICAgIyMjIyBOb3RpY2UgYWxsIG9mIHRoZSBsaXN0IHZhcmlhYmxlcyBhcmUgY29kZWQgYXMgY2hhcmFjdGVyIHNvLCBsYXRlciBpbiBkYXRhIHdyYW5nbGluZyB3aWxsIGZpeCB0aGF0IGJ5IGNvbnZlcnRpbmcgdGhlbSBpbnRvIG51bWVyaWMgbGlzdHMuDQoNCi0gICANCg0KICAgICMjIyMgJ0RhdGUnIG5lZWRzIHRvIGJlIHJlZm9ybWF0dGVkIHRvIFBPU0lYY3QsICdSb3VuZCcgaXMgYSBjaGFyYWN0ZXIgZm9ybWF0IGJlY2F1c2UgJz8nIGlzIG5vdCB0cmVhdGVkIGFzIE4vQSwgJ1Jlc3VsdCBuZWVkcyB0byBiZSBmb3JtYXR0ZWQgYXMgYSBmYWN0b3IuDQoNCiMjIyMgKipIaXN0b2dyYW1zKioNCg0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTB9DQojIyBXaGl0ZSBFTE8NCmJ3IDwtIENoZXNzX3Byb2plY3QgJT4lIA0KICBzZWxlY3QoYFdoaXRlIEVMT2ApICU+JSANCiAgZmlsdGVyKCFpcy5uYShgV2hpdGUgRUxPYCkpDQoNCmJ3IDwtIDIgKiBJUVIoYnckYFdoaXRlIEVMT2ApIC8gbGVuZ3RoKGJ3JGBXaGl0ZSBFTE9gKV4oMS8zKQ0KDQpnMSA8LSBDaGVzc19wcm9qZWN0ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBgV2hpdGUgRUxPYCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSBidywNCiAgICAgICAgICAgICAgICAgZmlsbCA9ICdncmVlbicsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gJ2JsYWNrJywNCiAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGLA0KICAgICAgICAgICAgICAgICBhbHBoYSA9IC41KSArDQogIGxhYnModGl0bGUgPSAiKGcxKSBEaXN0cmlidXRpb24gb2YgV2hpdGUgRUxPIiwNCiAgICAgICB4ID0gIldoaXRlIEVMTyIsDQogICAgICAgeSA9ICJGcmVxdWVuY3kiKSArDQogIGhyYnJ0aGVtZXM6OnRoZW1lX2Z0X3JjKCkrDQogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIndoaXRlIikpDQoNCiMjIEJsYWNrIEVMTw0KYncgPC0gQ2hlc3NfcHJvamVjdCAlPiUgDQogIHNlbGVjdChgQmxhY2sgRUxPYCkgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGBCbGFjayBFTE9gKSkNCg0KYncgPC0gMiAqIElRUihidyRgQmxhY2sgRUxPYCkgLyBsZW5ndGgoYnckYEJsYWNrIEVMT2ApXigxLzMpDQoNCmcyIDwtIENoZXNzX3Byb2plY3QgJT4lDQogIGdncGxvdChhZXMoeCA9IGBCbGFjayBFTE9gKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IGJ3LA0KICAgICAgICAgICAgICAgICBmaWxsID0gJ2dyZWVuJywNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAnYmxhY2snLA0KICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYsDQogICAgICAgICAgICAgICAgIGFscGhhID0gLjUpICsNCiAgbGFicyh0aXRsZSA9ICIoZzIpIERpc3RyaWJ1dGlvbiBvZiBCbGFjayBFTE8iLA0KICAgICAgIHggPSAiQmxhY2sgRUxPIiwNCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIpICsNCiAgaHJicnRoZW1lczo6dGhlbWVfZnRfcmMoKSsNCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ3aGl0ZSIpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiKSkNCg0KIyMgQ29tYmluaW5nDQooZzEgL2cyKSArDQogIHBsb3RfYW5ub3RhdGlvbih0aGVtZSA9IHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIpKSkgKw0KICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KCdtb25vJykpDQpgYGANCg0KLSAgIA0KDQogICAgIyMjIyBCb3RoIGhpc3RvZ3JhbXMgYXJlIHNrZXdlZCB0byB0aGUgbGVmdCBiZWNhdXNlIG1vc3Qgb2YgY2hlc3MgcGxheWVycyBhdCB0aGVzZSB0b3VybmFtZW50cyBhcmUgaGlnaGx5IHJhdGVkIGluIHRlcm1zIG9mIEVMTy4NCg0KLSAgIA0KDQogICAgIyMjIyBUaGVyZWZvcmUsIGlmIHdlIHdlcmUgZ29pbmcgdG8gaW1wdXRlIGFueSB2YWx1ZXMgd2Ugc2hvdWxkIHVzZSB0aGUgbWVkaWFuIGFzIGl0cyBsZXNzIHN1YmplY3RpdmUgdG8gb3V0bGllcnMNCg0KLSAgIA0KDQogICAgIyMjIyBUaGUgbWVhbiBpcyBsZXNzIHRoYW4gdGhlIG1lZGlhbiBjb25maXJtaW5nIG91ciBzdXNwaWNpb24uIFNlZSB0YWJsZSBiZWxvdw0KDQp8ICAgICAgICAgICAgfCBXaGl0ZSBFTE8gfCBCbGFjayBFTE8gfA0KfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLXwNCnwgKipNZWFuKiogICB8IDIsNTM1ICAgICB8IDIsNTI2ICAgICB8DQp8ICoqTWVkaWFuKiogfCAyLDU1OCAgICAgfCAyLDU1MyAgICAgfA0KDQojIyMjICoqQm94IHBsb3RzKioNCg0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTAsIHJlc3VsdHM9J2hvbGQnfQ0KIyMgV2hpdGUgQVZHIENQIExvc3MNCmczIDwtIENoZXNzX3Byb2plY3QgJT4lDQogIGdncGxvdChhZXMoeCA9IGBXaGl0ZSBBdiBDUCBMb3NzYCkpICsNCiAgZ2VvbV9ib3hwbG90KHNob3cubGVnZW5kID0gRiwNCiAgICAgICAgICAgICAgIGFscGhhID0gLjUsDQogICAgICAgICAgICAgICBmaWxsID0gIiNGRjhDMDAiLA0KICAgICAgICAgICAgICAgY29sb3IgPSAiIzBEREJDQyIpICsNCiAgbGFicyh0aXRsZSA9ICIoZzMpIFdoaXRlIEF2IENQIExvc3MiLA0KICAgICAgIHggPSAiV2hpdGUgQXYgQ1AgTG9zcyIpICsNCiAgaHJicnRoZW1lczo6dGhlbWVfbW9kZXJuX3JjKCkrDQogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiIzBEREJDQyIpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiIzBEREJDQyIpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiNGRjhDMDAiKSkNCg0KIyMgQmxhY2sgQVZHIENQIExvc3MNCmc0IDwtIENoZXNzX3Byb2plY3QgJT4lDQogIGdncGxvdChhZXMoeCA9IGBCbGFjayBBdiBDUCBMb3NzYCkpICsNCiAgZ2VvbV9ib3hwbG90KHNob3cubGVnZW5kID0gRiwNCiAgICAgICAgICAgICAgIGFscGhhID0gLjUsDQogICAgICAgICAgICAgICBmaWxsID0gIiNGRjhDMDAiLA0KICAgICAgICAgICAgICAgY29sb3IgPSAiIzBEREJDQyIpICsNCiAgbGFicyh0aXRsZSA9ICIoZzQpIEJsYWNrIEF2IENQIExvc3MiLA0KICAgICAgIHggPSAiQmxhY2sgQXYgQ1AgTG9zcyIpICsNCiAgaHJicnRoZW1lczo6dGhlbWVfbW9kZXJuX3JjKCkrDQogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiIzBEREJDQyIpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiIzBEREJDQyIpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiNGRjhDMDAiKSkNCg0KIyMgVG90YWwgTW92ZXMNCmc1IDwtIENoZXNzX3Byb2plY3QgJT4lDQogIGdncGxvdChhZXMoeCA9IE1vdmVzKSkgKw0KICBnZW9tX2JveHBsb3Qoc2hvdy5sZWdlbmQgPSBGLA0KICAgICAgICAgICAgICAgYWxwaGEgPSAuNSwNCiAgICAgICAgICAgICAgIGZpbGwgPSAiI0ZGOEMwMCIsDQogICAgICAgICAgICAgICBjb2xvciA9ICIjMEREQkNDIikgKw0KICBsYWJzKHRpdGxlID0gIihnNSkgTW92ZXMiLA0KICAgICAgIHggPSAiTW92ZXMiKSArDQogIGhyYnJ0aGVtZXM6OnRoZW1lX21vZGVybl9yYygpKw0KICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiMwRERCQ0MiKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiMwRERCQ0MiKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICIjRkY4QzAwIikpDQoNCiMjIENvbWJpbmUNCigoZzMgfCBnNCkgLyBnNSkgKw0KICBwbG90X2Fubm90YXRpb24odGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siKSkpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dCgnbW9ubycpKQ0KDQojIyBUb3RhbCBNb3ZlcyBhZ2dyZWdhdGVkIGJ5IFJlc3VsdCBvZiBnYW1lDQpDaGVzc19wcm9qZWN0ICU+JSANCiAgbXV0YXRlKFJlc3VsdCA9IHJlY29kZShSZXN1bHQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIjAiID0gIkJsYWNrIHdpbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIjEiID0gIkRyYXciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICIyIiA9ICJXaGl0ZSB3aW4iKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKE1vdmVzLCBSZXN1bHQsIGZpbGwgPSBSZXN1bHQpKSArDQogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNSwgY29sb3IgPSAid2hpdGUiKSArDQogIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGdlb20gPSAidGV4dCIsIGxhYmVsID0gIlgiLCBjb2xvciA9ICJ3aGl0ZSIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJCbGFjayB3aW4iID0gIiNmZmUwNDIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEcmF3IiA9ICIjZTcxOTg5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiV2hpdGUgd2luIiA9ICIjMDBlMWQ5IikpICsNCiAgbGFicyh0aXRsZSA9ICIoZzYpIFRvdGFsIG1vdmVzIG1hZGUiLA0KICAgICAgIHN1YnRpdGxlID0gImRpc2FncmVnYXRlZCBieSByZXN1bHQgb2YgZ2FtZSIsDQogICAgICAgeCA9ICJUb3RhbCBNb3ZlcyIsDQogICAgICAgeSA9ICJSZXN1bHQgb2YgZ2FtZSIpICsNCiAgaHJicnRoZW1lczo6dGhlbWVfbW9kZXJuX3JjKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrDQogIGFubm90YXRlKCJ0ZXh0IiwgeD0zOCwgeT0xLCBsYWJlbD0gIjQyIiwgY29sb3IgPSAid2hpdGUiKSsNCiAgYW5ub3RhdGUoInRleHQiLCB4PTM2LCB5PTIsIGxhYmVsPSAiNDAiLCBjb2xvciA9ICJ3aGl0ZSIpKw0KICBhbm5vdGF0ZSgidGV4dCIsIHg9MzksIHk9MywgbGFiZWw9ICI0MyIsIGNvbG9yID0gIndoaXRlIikNCg0KIyBnZ3NhdmUoZmlsZSA9ICJUb3RhbCBtb3ZlcyBtYWRlIGFnZ3JlZ2F0ZWQgYnkgcmVzdWx0IG9mIGdhbWUucG5nIiwNCiMgICAgICAgIHVuaXRzID0gYygiaW4iKSwNCiMgICAgICAgIHdpZHRoID0gOCwNCiMgICAgICAgIGhlaWdodCA9IDUuNSwNCiMgICAgICAgIGRwaT03MDAsDQojICAgICAgICBnNikNCmBgYA0KDQotICAgIyMjIyBUaGUgZmlyc3QgdGhpbmcgdG8gbm90aWNlIGlzIHRoYXQgYWxsIG9mIHRoZSBib3ggcGxvdHMgaGF2ZSBhIGxvdCBvZiBvdXRsaWVycywgdGhlcmVmb3JlIGlmIHdlIHdlcmUgdG8gaW1wdXRlIGFueSB2YWx1ZXMgZm9yIHRoZXNlIHZhcmlhYmxlcywgbWVkaWFuIHdvdWxkIGJlIHRoZSBiZXN0IGNob2ljZS4gDQotICAgIyMjIyBOZXh0LCB3ZSBjYW4gc2VlIHRoYXQgZm9yIGJvdGggZzMgJiBnNCB0aGUgQVZHIENQIExvc3MgZm9yIG1vc3QgcGxheWVycyBpcyBxdWl0ZSBsb3cuIFRoaXMgaXMgdG8gYmUgZXhwZWN0ZWQgYXMgbG90IHBsYXllcnMgYXJlIEdyYW5kbWFzdGVycyBhbmQgc2hvdWxkIGhhdmUgYSBsb3dlciBjZW50aXBhd24gbG9zcyANCi0gICAjIyMjIEZvciBncmFwaCBnNiB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYWxsIHBvc2l0aXZlbHkgc2tld2VkIG1lYW5pbmcsIHRoZSBtZWFuIGlzIGdyZWF0ZXIgdGhhbiB0aGUgbWVkaWFuLiBGcm9tIHRoaXMgd2UgY2FuIGNvbmNsdWRlIHRoYXQgNzUlIG9mIG1hdGNoZXMgZW5kIG5vIG1hdHRlciB0aGUgcmVzdWx0IGluIGFyb3VuZCA1MiBtb3ZlcyBvciBsZXNzLiBIb3dldmVyLCB0aGVyZSBpcyBhIGxvbmcgcmlnaHQgdGFpbCBvZiBvdXRsaWVycyB3aGVyZSB0aGUgZ2FtZSBlbmRzIHdpdGggbWFueSBtb3JlIG1vdmVzLiANCg0KYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTB9DQojIyBMaW5lIHBsb3RzDQoNCiMjIFdoaXRlIEVMTyBWUyBXaGl0ZSBBVkcgQ1AgTG9zcw0KZzcgPC0gQ2hlc3NfcHJvamVjdCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gYFdoaXRlIEVMT2AsDQogICAgICAgICAgICAgeSA9IGBXaGl0ZSBBdiBDUCBMb3NzYCkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjMmI2YmU0Iiwgc2l6ZSA9IDIsDQogICAgICAgICAgICAgYWxwaGEgPSAwLjcpICsNCiAgZ2VvbV9zbW9vdGgoY29sb3IgPSAiIzAwMDA4YiIpKw0KICBsYWJzKHRpdGxlID0gIihnNykgV2hpdGUgRUxPIHZzLiBBVkcgQ1AgTG9zcyIsDQogICAgICAgc3VidGl0bGUgPSAid2l0aCBzbW9vdGhlZCBsaW5lYXIgbW9kZWwiLA0KICAgICAgIHggPSAiV2hpdGUgRUxPIiwNCiAgICAgICB5ID0gIldoaXRlIEFWRyBDUCBMb3NzIikgKw0KICBnZ3RoZW1lczo6dGhlbWVfc29sYXJpemVkKCkNCg0KIyMgQmxhY2sgRUxPIFZzIEJsYWNrIEFWRyBDUCBMb3NzDQpnOCA8LSBDaGVzc19wcm9qZWN0ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBgQmxhY2sgRUxPYCwNCiAgICAgICAgICAgICB5ID0gYEJsYWNrIEF2IENQIExvc3NgKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIiMyYjZiZTQiLCBzaXplID0gMiwNCiAgICAgICAgICAgICBhbHBoYSA9IDAuNykgKw0KICBnZW9tX3Ntb290aChjb2xvciA9ICIjMDAwMDhiIikrDQogIGxhYnModGl0bGUgPSAiKGc4KSBCbGFjayBFTE8gdnMuIEFWRyBDUCBMb3NzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJ3aXRoIHNtb290aGVkIGxpbmVhciBtb2RlbCIsDQogICAgICAgeCA9ICJCbGFjayBFTE8iLA0KICAgICAgIHkgPSAiQmxhY2sgQVZHIENQIExvc3MiKSArDQogIGdndGhlbWVzOjp0aGVtZV9zb2xhcml6ZWQoKQ0KDQojIyBDb21iaW5lIGdyYXBocw0KKGc3IC9nOCkgKw0KICBwbG90X2Fubm90YXRpb24odGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siKSkpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dCgnbW9ubycpKQ0KYGBgDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwfQ0KIyMgV2hpdGUgRUxPIHZzIEJsYWNrIEFWRyBDUCBMb3NzDQpnOSA8LSBDaGVzc19wcm9qZWN0ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBgV2hpdGUgRUxPYCwNCiAgICAgICAgICAgICB5ID0gYEJsYWNrIEF2IENQIExvc3NgKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIiMyYjZiZTQiLCBzaXplID0gMiwNCiAgICAgICAgICAgICBhbHBoYSA9IDAuNykgKw0KICBnZW9tX3Ntb290aChjb2xvciA9ICIjMDAwMDhiIikrDQogIGxhYnModGl0bGUgPSAiKGc3KSBXaGl0ZSBFTE8gdnMuIEJsYWNrIEFWRyBDUCBMb3NzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJ3aXRoIHNtb290aGVkIGxpbmVhciBtb2RlbCIsDQogICAgICAgeCA9ICJXaGl0ZSBFTE8iLA0KICAgICAgIHkgPSAiQmxhY2sgQVZHIENQIExvc3MiKSArDQogIGdndGhlbWVzOjp0aGVtZV9zb2xhcml6ZWQoKQ0KDQojIyBCbGFjayBFTE8gdnMgV2hpdGUgQVZHIENQIExvc3MNCmcxMCA8LSBDaGVzc19wcm9qZWN0ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBgQmxhY2sgRUxPYCwNCiAgICAgICAgICAgICB5ID0gYFdoaXRlIEF2IENQIExvc3NgKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIiMyYjZiZTQiLCBzaXplID0gMiwNCiAgICAgICAgICAgICBhbHBoYSA9IDAuNykgKw0KICBnZW9tX3Ntb290aChjb2xvciA9ICIjMDAwMDhiIikrDQogIGxhYnModGl0bGUgPSAiKGc3KSBCbGFjayBFTE8gdnMuIFdoaXRlIEFWRyBDUCBMb3NzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJ3aXRoIHNtb290aGVkIGxpbmVhciBtb2RlbCIsDQogICAgICAgeCA9ICJCbGFjayBFTE8iLA0KICAgICAgIHkgPSAiV2hpdGUgQVZHIENQIExvc3MiKSArDQogIGdndGhlbWVzOjp0aGVtZV9zb2xhcml6ZWQoKQ0KDQojIyBDb21iaW5lIEdyYXBoDQooZzkgL2cxMCkgKw0KICBwbG90X2Fubm90YXRpb24odGhlbWUgPSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siKSkpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dCgnbW9ubycpKQ0KYGBgDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwfQ0KIyMgQ29tYmluZSB3aXRoIHJlc3VsdCBvZiBnYW1lDQpDaGVzc19wcm9qZWN0ICU+JQ0KICBtdXRhdGUoUmVzdWx0ID0gcmVjb2RlKFJlc3VsdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMCIgPSAiQmxhY2sgd2luIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMSIgPSAiRHJhdyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIjIiID0gIldoaXRlIHdpbiIpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGBXaGl0ZSBFTE9gLA0KICAgICAgICAgICAgIHkgPSBgV2hpdGUgQXYgQ1AgTG9zc2AsDQogICAgICAgICAgICAgY29sb3IgPSBSZXN1bHQpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDUsDQogICAgICAgICAgICAgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxKSsNCiAgbGFicyh0aXRsZSA9ICIoZzExKSBXaGl0ZSBFTE8gdnMuIEFWRyBDUCBMb3NzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJBZ2dyZWdhdGVkIGJ5IFJlc3VsdCBvZiBnYW1lIiwNCiAgICAgICB4ID0gIldoaXRlIEVMTyIsDQogICAgICAgeSA9ICJXaGl0ZSBBVkcgQ1AgTG9zcyIpICsNCiAgZ2d0aGVtZXM6OnRoZW1lX3NvbGFyaXplZCgpDQoNCiMjIENoZWNraW5nIHRvIG1ha2Ugc3VyZSB0aGUgYXZnIGNwIExvc3MgbWF0Y2hlcyAoTmVlZCB0byBydW4gY29kZSBiZWxvdyBmb3IgdGhpcyB0byB3b3JrKQ0KIyBDaGVzc19wcm9qZWN0ICU+JSANCiMgICBsZWZ0X2pvaW4oLiwgDQojICAgICAgICAgICAgIENoZXNzX3Byb2plY3QgJT4lIA0KIyAgIGdyb3VwX2J5KGBXaGl0ZSBDUCBMb3NzIExpc3RgKSAlPiUgDQojICAgc3VtbWFyaXNlKFdoaXRlX0FWRyA9IHJvdW5kKG1lYW4oYFdoaXRlIENQIExvc3MgTGlzdGBbWzFdXSksIGRpZ2l0cyA9IDApKSwNCiMgICBieSA9ICJXaGl0ZSBDUCBMb3NzIExpc3QiKSAlPiUNCiMgICBzZWxlY3QoYFdoaXRlIEF2IENQIExvc3NgLCBXaGl0ZV9BVkcpICU+JSANCiMgICBtdXRhdGUoZXhhbXBsZSA9IGNhc2Vfd2hlbigNCiMgICAgIGBXaGl0ZSBBdiBDUCBMb3NzYCA9PSBXaGl0ZV9BVkcgfiAiWWVzIiwNCiMgICAgIFRSVUUgfiAiTm8iDQojICAgKSkgJT4lIA0KIyAgIGZpbHRlcihleGFtcGxlID09ICJZZXMiKQ0KDQojIyBDb3VsZCB1c2UgbXV0YXRlIGJ1dCB0aGUgZ3JvdXAgYnkgZ2V0cyByaWQgb2YgNCByb3dzIGJlY2F1c2UgdGhlcmUgYXJlIGR1cGxpY2F0ZXMNCg0KIyBDaGVzc19wcm9qZWN0ICU+JQ0KIyAgIGdyb3VwX2J5KGBXaGl0ZSBDUCBMb3NzIExpc3RgKSAlPiUgDQojICAgbXV0YXRlKFdoaXRlX0FWRyA9IHJvdW5kKG1lYW4oYFdoaXRlIENQIExvc3MgTGlzdGBbWzFdXSksIGRpZ2l0cyA9IDApKQ0KYGBgDQoNCi0gICAjIyMjIFdlIGNhbiBzZWUgdGhhdCB3aGVuIGNvbXBhcmluZyB0aGUgYXZnIENQIGxvc3MgJiBFTE8gb2YgdGhlIHNhbWUgY29sb3IgdGhlcmUgaXMgYSBzbGlnaHQgZGVjcmVhc2UgaW4gYXZnIENQIGxvc3MgdGhlIGhpZ2hlciB0aGUgRUxPLCB3aGljaCBzaG91bGQgYmUgdGhlIGNhc2UgY29uc2lkZXJpbmcgeW91IHNob3VsZCBtYWtlIGxlc3MgbWlzdGFrZXMgdGhlIGhpZ2hlciB5b3VyIEVMTyBpcyANCi0gICAjIyMjIFRoZSBvcHBvc2l0ZSBjYW4gYmUgc2FpZCBmb3IgY29tcGFyaW5nIHRoZSBjb2xvcnMgYXMgdGhlcmUgZG9lc24ndCBzZWVtIHRvIGJlIG11Y2ggY29ycmVsYXRpb24gYW5kIHJlc3VsdHMgaW4gYSByZWxhdGl2ZWx5IGZsYXQgbGluZS4NCi0gICAjIyMjIFRoZSBsYXN0IGdyYXBoIHNob3dzIHRoZXJlIGlzIGEgYmlnIG91dGxpZXIgd2hlcmUgYmxhY2sgd29uIGJlY2F1c2Ugd2hpdGUgaGFkIGEgaGlnaCBhdmcgQ1AgbG9zcy4gU2hvdWxkIGJlIHRoZSBjYXNlIGlmIHdoaXRlIG1ha2VzIHRoYXQgbWFueSBtaXN0YWtlcy4NCg0KIyMjICoqRGF0YSBXcmFuZ2xpbmcqKg0KDQojIyMjIFRoZXJlIGlzbid0IG11Y2ggZGF0YSB3cmFuZ2xpbmcgdG8gYmUgZG9uZSBob3dldmVyLCB3aGVuIGV4cGxvcmluZyB0aGUgY2hlc3MgZGF0YSBJIG5vdGljZWQgdGhhdCB0aGUgbGlzdCB2YXJpYWJsZXMgd2VyZSBub3QgY29kZWQgY29ycmVjdGx5IGFzIHRoZXkgd2VyZSBpbiBhIGNoYXJhY3RlciBmb3JtYXQuIFRvIGZpeCB0aGlzIEkgdXNlZCB0aGUgZ3N1YigpIGZ1bmN0aW9uIHRvIHJlZm9ybWF0IHRoZSB2YXJpYWJsZXMgdG8gYSBsaXN0IGJ1dCB0aGV5IHdlcmUgbm93IHJlYWQgYXMgYSBjaGFyYWN0ZXIgbGlzdC4gSSB0aGVuIHVzZSBhIGZvcigpIGxvb3AgdG8gY29ycmVjdGx5IGZvcm1hdCB0aGVtIGFzIGEgbnVtZXJpYyBsaXN0LiBUaGlzIGVuc3VyZXMgaWYgSSB3YW50IHRvIHB1bGwgdGhlc2UgbGlzdHMsIEkgY2FuIHBlcmZvcm0gbnVtZXJpYyBjb21wdXRhdGlvbnMgd2l0aCBvdXQgYW55IGlzc3Vlcy4gU2VlIGNvZGUgYmVsb3chIA0KDQpgYGB7cn0NCkNoZXNzX3Byb2plY3QgPC0gQ2hlc3NfcHJvamVjdCAlPiUgDQogIG11dGF0ZShgRXZhbHVhdGlvbnMgTGlzdGAgPSBnc3ViKGBFdmFsdWF0aW9ucyBMaXN0YCwgcGF0dGVybiA9ICJbIiwgZml4ZWQgPSBULCByZXBsYWNlbWVudCA9ICIiKSkgJT4lDQogIG11dGF0ZShgRXZhbHVhdGlvbnMgTGlzdGAgPSBnc3ViKGBFdmFsdWF0aW9ucyBMaXN0YCwgcGF0dGVybiA9ICIsIiwgZml4ZWQgPSBULCByZXBsYWNlbWVudCA9ICIiKSkgJT4lIA0KICBtdXRhdGUoYEV2YWx1YXRpb25zIExpc3RgID0gc3RyX2V4dHJhY3RfYWxsKGBFdmFsdWF0aW9ucyBMaXN0YCwgIlxcKD9bMC05LC4tXStcXCk/IikpICU+JSANCiAgbXV0YXRlKGBXaGl0ZSBDUCBMb3NzIExpc3RgID0gZ3N1YihgV2hpdGUgQ1AgTG9zcyBMaXN0YCwgcGF0dGVybiA9ICJbIiwgZml4ZWQgPSBULCByZXBsYWNlbWVudCA9ICIiKSkgJT4lDQogIG11dGF0ZShgV2hpdGUgQ1AgTG9zcyBMaXN0YCA9IGdzdWIoYFdoaXRlIENQIExvc3MgTGlzdGAsIHBhdHRlcm4gPSAiLCIsIGZpeGVkID0gVCwgcmVwbGFjZW1lbnQgPSAiIikpICU+JSANCiAgbXV0YXRlKGBXaGl0ZSBDUCBMb3NzIExpc3RgID0gc3RyX2V4dHJhY3RfYWxsKGBXaGl0ZSBDUCBMb3NzIExpc3RgLCAiXFwoP1swLTksLi1dK1xcKT8iKSkgJT4lIA0KICBtdXRhdGUoYEJsYWNrIENQIExvc3MgTGlzdGAgPSBnc3ViKGBCbGFjayBDUCBMb3NzIExpc3RgLCBwYXR0ZXJuID0gIlsiLCBmaXhlZCA9IFQsIHJlcGxhY2VtZW50ID0gIiIpKSAlPiUNCiAgbXV0YXRlKGBCbGFjayBDUCBMb3NzIExpc3RgID0gZ3N1YihgQmxhY2sgQ1AgTG9zcyBMaXN0YCwgcGF0dGVybiA9ICIsIiwgZml4ZWQgPSBULCByZXBsYWNlbWVudCA9ICIiKSkgJT4lIA0KICBtdXRhdGUoYEJsYWNrIENQIExvc3MgTGlzdGAgPSBzdHJfZXh0cmFjdF9hbGwoYEJsYWNrIENQIExvc3MgTGlzdGAsICJcXCg/WzAtOSwuLV0rXFwpPyIpKQ0KDQojIyBuZWVkIHRvIHBlcmZvcm0gb24gc3BlY2lmaWMgY29sdW1ucw0KZm9yIChpIGluIDE6bGVuZ3RoKENoZXNzX3Byb2plY3QkYEV2YWx1YXRpb25zIExpc3RgKSkgew0KICBDaGVzc19wcm9qZWN0JGBFdmFsdWF0aW9ucyBMaXN0YFtbaV1dIDwtIGFzLm51bWVyaWMoQ2hlc3NfcHJvamVjdCRgRXZhbHVhdGlvbnMgTGlzdGBbW2ldXSkNCn0NCmZvciAoaSBpbiAxOmxlbmd0aChDaGVzc19wcm9qZWN0JGBXaGl0ZSBDUCBMb3NzIExpc3RgKSkgew0KICBDaGVzc19wcm9qZWN0JGBXaGl0ZSBDUCBMb3NzIExpc3RgW1tpXV0gPC0gYXMubnVtZXJpYyhDaGVzc19wcm9qZWN0JGBXaGl0ZSBDUCBMb3NzIExpc3RgW1tpXV0pDQp9DQpmb3IgKGkgaW4gMTpsZW5ndGgoQ2hlc3NfcHJvamVjdCRgQmxhY2sgQ1AgTG9zcyBMaXN0YCkpIHsNCiAgQ2hlc3NfcHJvamVjdCRgQmxhY2sgQ1AgTG9zcyBMaXN0YFtbaV1dIDwtIGFzLm51bWVyaWMoQ2hlc3NfcHJvamVjdCRgQmxhY2sgQ1AgTG9zcyBMaXN0YFtbaV1dKQ0KfQ0KDQpnbGltcHNlKENoZXNzX3Byb2plY3QpDQogIA0KDQojIG1lYW4oQ2hlc3NfcHJvamVjdCRgV2hpdGUgQ1AgTG9zcyBMaXN0YFtbMV1dKQ0KYGBgDQoNCiMjIyMgTmV4dCBJIHdhbnQgdG8gdmlzdWFsaXplIHBsYXllcnMgd2l0aCB0aGUgdG9wIDYgbW9zdCBwbGF5ZWQgZ2FtZXMgdG8gZ2V0IGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2Ygd2hvIG9jY3VycyBtb3N0IGluIHRoZSBkYXRhIHNldC4gU2VlIGNoYXJ0IGJlbG93IQ0KDQpgYGB7ciBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMH0NCiMjIGcxDQpDaGVzc19wcm9qZWN0ICU+JSANCiAgY291bnQoYFdoaXRlIE5hbWVgKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lIA0KICByZW5hbWUoQ291bnQgPSBuKSAlPiUgDQogIG11dGF0ZShgV2hpdGUgTmFtZWAgPSBmY3RfcmVvcmRlcihgV2hpdGUgTmFtZWAsIENvdW50KSkgJT4lIA0KICBoZWFkKCkgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGFlcyh4ID0gYFdoaXRlIE5hbWVgLCANCiAgICAgIHkgPSBDb3VudCwgDQogICAgICBmaWxsID0gYFdoaXRlIE5hbWVgLCANCiAgICAgIGxhYmVsID0gQ291bnQpICsNCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknLCANCiAgICAgICAgICAgYWxwaGEgPSAwLjUsDQogICAgICAgICAgIHNob3cubGVnZW5kID0gRiwNCiAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiWWxHbkJ1IiwgZGlyZWN0aW9uID0gMSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCANCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAwLjgpKSArDQogIGxhYnModGl0bGUgPSAiKGcxKSBCYXJwbG90IG9mIEFtb3VudCBvZiBHYW1lcyBQbGF5ZWQiLA0KICAgICAgIHggPSAiUGxheWVyIiwNCiAgICAgICB5ID0gIkhvdyBtYW55IGdhbWVzIHBsYXllZCBieSBwbGF5ZXIiKSArDQogIGdndGhlbWVzOjp0aGVtZV9zb2xhcml6ZWQoKSArDQogIGdlb21fdGV4dChudWRnZV95ID0gMiwgDQogICAgICAgICAgICB2anVzdCA9IDMsDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICBzaXplID0gNC4zKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQojIyMjIFRvIGNvbmNsdWRlIHRoZSBkYXRhIHdyYW5nbGluZyBsZXRzIGNyZWF0ZSBhIGNvcnJlbGF0aW9uIE1hdHJpeCBQbG90IG9uIG91ciBudW1lcmljIHZhcmlhYmxlcyB0byBzZWUgd2hpY2ggdmFyaWFibGVzIHdlIHdhbnQgdG8ga2VlcCBvciBkcm9wIGJlZm9yZSB3ZSBwZXJmb3JtIGFueSBtb2RlbHMgb24gb3VyIGRhdGEuIFNlZSBwbG90IEJlbG93IQ0KDQpgYGB7ciBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMH0NCiMjIE5lZWQgbGlicmFyeSh2aXJpZGlzKQ0KY29ycm1hdHJpeCA8LSBhcy5kYXRhLmZyYW1lKENoZXNzX3Byb2plY3QpDQoNCmNvbGZ1bmMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDksIkJyQkciKSkNCmhlYXRtYXAuMihjb3IoRmlsdGVyKGlzLm51bWVyaWMsIGNvcnJtYXRyaXgpLCB1c2UgPSAiY29tcGxldGUub2JzIiksIFJvd3YgPSBGQUxTRSwgDQogICAgICAgICAgQ29sdiA9IEZBTFNFLCBkZW5kcm9ncmFtID0gIm5vbmUiLCBsd2lkPWMoLjEsMiksIGxoZWk9YyguMSwyKSwgDQogICAgICAgICAgY29sID0gY29sZnVuYygxNSksIA0KICAgICAgICAgIGNlbGxub3RlID0gcm91bmQoY29yKEZpbHRlcihpcy5udW1lcmljLCBjb3JybWF0cml4KSwgdXNlID0gImNvbXBsZXRlLm9icyIpLDIpLA0KICAgICAgICAgIG5vdGVjb2wgPSAiYmxhY2siLCBrZXkgPSBGQUxTRSwgdHJhY2UgPSAnbm9uZScsIG1hcmdpbnMgPSBjKDEwLDEwKSkNCmBgYA0KDQotICAgIyMjIyBGcm9tIHRoaXMgd2UgY2FuIHJlYWxseSBvbmx5IGNvbmNsdWRlIHRoYXQgd2Ugc2hvdWxkIGRyb3AgdGhlIG51bWVyaWMgbGlzdHMgYmVjYXVzZSB0aGVyZSB3aWxsIGJlIG11bHRpY29sbGluZWFyaXR5IGJldHdlZW4gdGhlbSBhbmQgdGhlIEFWRyBDUCBsb3NzDQotICAgIyMjIyBXZSBjYW4gbW92ZSBvbiB0byBWYXJpYW5jZSBBbmFseXNpcw0KDQojIyMgKipBbmFseXNpcyBvZiBWYXJpYW5jZSoqDQoNCiMjIyMgV2lsbCB1c2UgYW4gYW5vdmEgdGVzdCBhcyB3ZSBoYXZlIHRocmVlIG9yIG1vcmUgY2F0ZWdvcmllcyB3aGVuIHRhbGtpbmcgYWJvdXQgdGhlIHJlc3VsdHMgdmFyaWFibGUsIHdoaWNoIGlzIGFsbCB3ZSByZWFsbHkgY2FyZSBhYm91dC4gU2VlIHRlc3QgYmVsb3chDQoNCiMjIyMgV2lsbCBzdGFydCBieSB0ZXN0aW5nIHRvdGFsIG1vdmVzIG1hZGUgaW4gYSBtYXRjaCB0byB0aGUgcmVzdWx0IG9mIHRoZSBtYXRjaC4NCi0gICAjIyMjIChIMCkgLSBOdWxsIGh5cG90aGVzaXM6IHRoZSBtZWFucyBvZiB0b3RhbCBtb3ZlcyBtYWRlIGFyZSB0aGUgc2FtZSBiZXR3ZWVuIHRoZSByZXN1bHRzIG9mIHRoZSBtYXRjaC4NCi0gICAjIyMjIChIMSkgLSBBbHRlcm5hdGl2ZTogVGhlIGRpZmZlcmVuY2UgaXMgcmVhbCBhbmQgdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGluIHRoZSBtZWFucw0KLSAgICMjIyMgQWxwaGEgVmFsdWU6IDAuMDUNCg0KYGBge3J9DQpDaGVzc19wcm9qZWN0IDwtIENoZXNzX3Byb2plY3QgJT4lIA0KICBzZWxlY3QoLWBFdmFsdWF0aW9ucyBMaXN0YCwgLWBXaGl0ZSBDUCBMb3NzIExpc3RgLCAtYEJsYWNrIENQIExvc3MgTGlzdGApDQoNCkNoZXNzX3Byb2plY3QgJT4lIA0KICBtdXRhdGUoUmVzdWx0ID0gcmVjb2RlKFJlc3VsdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMCIgPSAiQmxhY2sgd2luIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMSIgPSAiRHJhdyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIjIiID0gIldoaXRlIHdpbiIpKSAlPiUgDQogIHNlbGVjdChSZXN1bHQsIE1vdmVzKSAlPiUgDQogIGdyb3VwX2J5KFJlc3VsdCkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9tb3ZlcyA9IG1lYW4oTW92ZXMpKSAlPiUgDQogIGFycmFuZ2UobWVhbl9tb3ZlcykNCg0KQ2hlc3NfcHJvamVjdCAlPiUgDQogIG11dGF0ZShSZXN1bHQgPSByZWNvZGUoUmVzdWx0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICIwIiA9ICJCbGFjayB3aW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICIxIiA9ICJEcmF3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMiIgPSAiV2hpdGUgd2luIikpICU+JSANCiAgc2VsZWN0KFJlc3VsdCwgTW92ZXMpICU+JSANCiAgYW92KE1vdmVzIH4gUmVzdWx0LCBkYXRhID0gLikgJT4lIA0KICBzdW1tYXJ5KCkgIyBIb25lc3RseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlDQpgYGANCg0KLSAgICMjIyMgV2UgY2FuIGNvbmNsdWRlIHRoYXQgdGhlIGRpZmZlcmVuY2UgaXMgc2lnbmlmaWNhbnQgJiB3ZSBjYW4gcmVqZWN0IChIMCkNCi0gICAjIyMjIFdlIGhhdmUgcHJvdmVuIHRoYXQgYXQgbGVhc3Qgb25lIG9mIHRoZXNlIGlzIGRpZmZlcmVudCBmcm9tIHRoZSBvdGhlciB0d28gYnV0IHdlIGhhdmVuJ3QgZGVmaW5lZCB3aGljaCBvbmUuIA0KDQpgYGB7cn0NCkNoZXNzX3Byb2plY3QgJT4lIA0KICBtdXRhdGUoUmVzdWx0ID0gcmVjb2RlKFJlc3VsdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMCIgPSAiQmxhY2sgd2luIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMSIgPSAiRHJhdyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIjIiID0gIldoaXRlIHdpbiIpKSAlPiUgDQogIHNlbGVjdChSZXN1bHQsIE1vdmVzKSAlPiUgDQogIGFvdihNb3ZlcyB+IFJlc3VsdCwgZGF0YSA9IC4pICU+JSANCiAgVHVrZXlIU0QoKSAjIEhvbmVzdGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2UNCmBgYA0KLSAgICMjIyMgRHJhdy1CbGFjayBXaW4gJiBEcmF3LVdoaXRlIFdpbiBhcmUgYm90aCBzaWduaWZpY2FudCwgbWVhbmluZyB0aGVyZSBpcyBhIGRpZmZlcmVuY2UgaW4gdGhlIG1lYW5zLg0KLSAgICMjIyMgV2hpdGUgV2luLUJsYWNrIFdpbiBpcyBub3Qgc2lnbmlmaWNhbnQgbWVhbmluZyB0aGUgbWVhbnMgYXJlIHNpbWlsYXINCg0KIyMjICoqRml0dGluZyBhIE5ldXJhbCBOZXR3b3JrKioNCg0KIyMjIyBwcmUtcHJvY2Vzc2luZw0KDQpgYGB7cn0NCkNoZXNzX3Byb2plY3QgPC0gQ2hlc3NfcHJvamVjdCAlPiUgDQogIG11dGF0ZShSZXN1bHQgPSByZWNvZGUoUmVzdWx0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICIwIiA9ICJCbGFja193aW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICIxIiA9ICJEcmF3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiMiIgPSAiV2hpdGVfd2luIikpICU+JSANCiAgcmVuYW1lKFdoaXRlX0VMTyA9IGBXaGl0ZSBFTE9gKSAlPiUgDQogIHJlbmFtZShCbGFja19FTE8gPSBgQmxhY2sgRUxPYCkgJT4lIA0KICByZW5hbWUoRXZlbnRfUm91bmRzID0gYEV2ZW50IFJvdW5kc2ApICU+JSANCiAgcmVuYW1lKFdoaXRlX0F2X0NQX0xvc3MgPSBgV2hpdGUgQXYgQ1AgTG9zc2ApICU+JSANCiAgcmVuYW1lKEJsYWNrX0F2X0NQX0xvc3MgPSBgQmxhY2sgQXYgQ1AgTG9zc2ApDQoNCkNoZXNzX3Byb2plY3QgPC0gZHVtbXlfY29scyhDaGVzc19wcm9qZWN0LCANCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdF9jb2x1bW5zID0gYygiUmVzdWx0IiksDQogICAgICAgICAgICAgICAgICAgICAgICByZW1vdmVfZmlyc3RfZHVtbXkgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlX3NlbGVjdGVkX2NvbHVtbnMgPSBUUlVFKQ0KYGBgDQoNCiMjIyMgQ2hlY2tpbmcgZm9yIGhpZ2hsZXkgc2tld2VkIHByZWRpY3RvcnMgKGxlc3MgdGhhbiAtMSBvciBncmVhdGVyIHRoYW4gMSkNCg0KYGBge3J9DQoNCm51bS5jb2xzIDwtIGNvbG5hbWVzKHNlbGVjdF9pZihDaGVzc19wcm9qZWN0LCBpcy5udW1lcmljKSkNCg0KQ2hlc3NfcHJvamVjdCA8LSBDaGVzc19wcm9qZWN0ICU+JSANCiAgZHJvcF9uYSgpDQoNCmZvciAoaSBpbiBudW0uY29scykgew0KICBwcmludChpKQ0KICBwcmludChza2V3bmVzcyhDaGVzc19wcm9qZWN0W1tpXV0pKQ0KfQ0KDQpDaGVzc19wcm9qZWN0IDwtIENoZXNzX3Byb2plY3QgJT4lIA0KICBzZWxlY3QoLWBBbmFseXNpcyBEZXB0aGApDQpgYGANCg0KIyMjIyBBcHBseSBhIGxvZyB0cmFuc2Zvcm1hdGlvbiB0byBoaWdobHkgc2tld2VkIHByZWRpY3RvcnMNCg0KYGBge3J9DQpDaGVzc19wcm9qZWN0JEJsYWNrX0VMTyA8LSBsb2coQ2hlc3NfcHJvamVjdCRCbGFja19FTE8gKyAxKQ0KQ2hlc3NfcHJvamVjdCRNb3ZlcyA8LSBsb2coQ2hlc3NfcHJvamVjdCRNb3ZlcyArIDEpDQpDaGVzc19wcm9qZWN0JFdoaXRlX0F2X0NQX0xvc3MgPC0gbG9nKENoZXNzX3Byb2plY3QkV2hpdGVfQXZfQ1BfTG9zcyArIDEpDQpDaGVzc19wcm9qZWN0JEJsYWNrX0F2X0NQX0xvc3MgPC0gbG9nKENoZXNzX3Byb2plY3QkQmxhY2tfQXZfQ1BfTG9zcyArIDEpDQpgYGANCg0KYGBge3J9DQpDaGVzc19wcm9qZWN0IDwtIENoZXNzX3Byb2plY3QgJT4lIA0KICBzZWxlY3RfaWYoLiwgaXMubnVtZXJpYykNCmBgYA0KDQojIyMjIHBhcnRpdGlvbiB0aGUgZGF0YQ0KDQpgYGB7cn0NCiMgQ291bGQgc2V0IHNlZWQgd2l0aCBzZXQuc2VlZCgpDQp0cmFpbi5pbmRleCA8LSBzYW1wbGUocm93bmFtZXMoQ2hlc3NfcHJvamVjdCksIG5yb3coQ2hlc3NfcHJvamVjdCkgKiAwLjcpDQpjaGVzcy50cmFpbiA8LSBDaGVzc19wcm9qZWN0W3RyYWluLmluZGV4LCBdDQp2YWxpZC5pbmRleCA8LSBzZXRkaWZmKHJvd25hbWVzKENoZXNzX3Byb2plY3QpLCB0cmFpbi5pbmRleCkNCmNoZXNzLnZhbGlkIDwtIENoZXNzX3Byb2plY3RbdmFsaWQuaW5kZXgsIF0NCmBgYA0KDQojIyMjIGNvbnZlcnQgYWxsIHZhcmlhYmxlcyAoSU5DTFVESU5HIFFVQU5USVRBVElWRSBPVVRDT01FKSB0byBhIDAtMSBzY2FsZQ0KDQpgYGB7cn0NCmNoZXNzLnRyYWluLm5vcm0gPC0gY2hlc3MudHJhaW4NCmNoZXNzLnZhbGlkLm5vcm0gPC0gY2hlc3MudmFsaWQNCg0KY29scyA8LSBjb2xuYW1lcyhjaGVzcy50cmFpbikNCmZvciAoaSBpbiBjb2xzKSB7DQogIGNoZXNzLnZhbGlkLm5vcm1bW2ldXSA8LSANCiAgICAoY2hlc3MudmFsaWQubm9ybVtbaV1dIC0gbWluKGNoZXNzLnRyYWluW1tpXV0pKSAvIChtYXgoY2hlc3MudHJhaW5bW2ldXSkgLSBtaW4oY2hlc3MudHJhaW5bW2ldXSkpDQogIGNoZXNzLnRyYWluLm5vcm1bW2ldXSA8LSANCiAgICAoY2hlc3MudHJhaW4ubm9ybVtbaV1dIC0gbWluKGNoZXNzLnRyYWluW1tpXV0pKSAvIChtYXgoY2hlc3MudHJhaW5bW2ldXSkgLSBtaW4oY2hlc3MudHJhaW5bW2ldXSkpDQp9DQpzdW1tYXJ5KGNoZXNzLnRyYWluLm5vcm0pDQpzdW1tYXJ5KGNoZXNzLnZhbGlkLm5vcm0pDQpgYGANCg0KIyMjIyBOZXVyYWwgbmV0IHdpdGggMiBoaWRkZW4gbGF5ZXJzIG9mIDMgbm9kZXMNCg0KYGBge3J9DQpjaGVzcy5ubi4zLjMgPC0gbmV1cmFsbmV0KFdoaXRlX0VMTyB+IC4sICAgICAgICAgICAgICAgICAgIyBjYXRlZ29yaWNhbCBvdXRjb21lIH4gcHJlZGljdG9ycw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGNoZXNzLnRyYWluLm5vcm0sICAgIyBkYXRhIGZvciB0cmFpbmluZyBtb2RlbA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZWFyLm91dHB1dCA9IEZBTFNFLCAgICAgICMgYXNzdW1lIHJlbGF0aW9uc2hpcCBpcyBub25saW5lYXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZGRlbiA9IGMoMywzKSkgICAgICAgICAgICAjIDIgaGlkZGVuIGxheWVycyBvZiAzIG5vZGVzIGVhY2gNCnBsb3QoY2hlc3Mubm4uMy4zLCByZXAgPSAiYmVzdCIpDQoNCnByZWRpY3Qubm4uMy4zIDwtIHByZWRpY3QoY2hlc3Mubm4uMy4zLCBjaGVzcy52YWxpZC5ub3JtKQ0KYGBgDQoNCiMjIyAqKkFjY3VyYWN5IG9uIFZhbGlkYXRpb24gc2V0KioNCg0KIyMjIyBjb252ZXJ0IGJhY2sgdG8gb3JpZ2luYWwgc2NhbGUNCg0KYGBge3J9DQptaW5fV2hpdGVfRUxPIDwtIG1pbihjaGVzcy50cmFpbiRXaGl0ZV9FTE8pDQptYXhfV2hpdGVfRUxPIDwtIG1heChjaGVzcy50cmFpbiRXaGl0ZV9FTE8pDQoNCmFjdHByZWQgPC0gZGF0YS5mcmFtZShhY3R1YWwgPSBjaGVzcy52YWxpZCRXaGl0ZV9FTE8sIA0KICAgICAgICAgICAgICAgICAgICAgIHByZWRpY3RlZCA9IG1pbl9XaGl0ZV9FTE8gKyBwcmVkaWN0Lm5uLjMuMyoobWF4X1doaXRlX0VMTyAtIG1pbl9XaGl0ZV9FTE8pKQ0KUk1TRShhY3RwcmVkJHByZWRpY3RlZCwgYWN0cHJlZCRhY3R1YWwpDQpoZWFkKHByZWRpY3Qubm4uMy4zKQ0KaGVhZChhY3RwcmVkKQ0KYGBgDQoNCiMjICoqRXhjZWwgRmlsZSoqDQoNCiMjIyMgKipDaGVzcyBTY2FuZGFsIEV2YWx1YXRpb24qKg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KQ2hlc3NfcHJvamVjdCA8LSByZWFkX2NzdihmaWxlID0gIjEwNDgwX2dhbWVzX3dpdGhfY2VudGlwYXduX21ldHJpY3MuY3N2IikNCg0KQ2hlc3NfcHJvamVjdCAlPiUgDQogIGRvd25sb2FkX3RoaXMoDQogICAgb3V0cHV0X25hbWUgPSAiQ2hlc3NfcHJvamVjdCIsDQogICAgb3V0cHV0X2V4dGVuc2lvbiA9ICIueGxzeCIsDQogICAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIENoZXNzX3Byb2plY3QgaW4gRXhjZWwiLA0KICAgIGJ1dHRvbl90eXBlID0gImRlZmF1bHQiLA0KICAgIGhhc19pY29uID0gVFJVRSwNCiAgICBpY29uID0gImZhIGZhLXNhdmUiKQ0KYGBgDQo=