Import Libraries

library(readr)
library(dplyr)
library(ggplot2)
library(gridExtra)

Import and View Data

df <- read_csv("data/cleaned_data.csv")
head(df)

Analysis

1. What is His Win Percentage Overall?

# count of win, draw, loss
overall_win <- df %>%
  group_by(Result) %>%
  summarise(Count = n())

# vars for inline code
ngame <- summarise(df, n()) %>% as.character()
wins <- overall_win[3, 2] %>% as.character()
loss <- overall_win[2, 2] %>% as.character()
draw <- overall_win[1, 2] %>% as.character()
wr <- round(as.numeric(wins) / as.numeric(ngame) * 100, 2)

# plot
overall_win %>%
  ggplot(aes(x = reorder(Result, -Count), y = Count)) +
  geom_bar(stat = "identity", width = 0.6) +
  labs(
    title = "Comparison of number of wins, losses, and draws",
    x = "Result"
  ) +
  theme_light()

His win percentage is 42.24 %. Out of 3262 games, 1378 are wins, 576 are losses, and 1308 are draws. Most of his games end in either a win or a draw.

2. What is His Win Percentage Grouped by Color?

# ratio of win, draw, loss by color
winrate <- df %>%
  group_by(Color, Result) %>%
  summarise(count = n()) %>%
  mutate(Percentage = count / sum(count) * 100) %>%
  select(-count) 

# vars for inline code
w_wr <- round(winrate[6, 3], 2) %>% as.character()
b_wr <- round(winrate[3, 3], 2) %>% as.character()

# plot
winrate %>%
  ggplot(aes(x = Color, y = Percentage, fill = Result)) +
  geom_bar(stat = "identity", position = "fill", width = 0.6) +
  coord_flip() +
  labs(
    title = "Ratio of Wins, Losses & Draws Grouped by Color",
    y = "Win : Loss : Draw"
  ) +
  guides(fill = guide_legend(reverse = TRUE)) +
  theme_light()

His win-rate as white is 47.74 %, which is higher than his win-rate as black 36.7 %. Win to loss to draw ratio is roughly the same as white or black.

3. Which Openings Does He Play the Most Overall?

# openings and their game count
overall_op <- df %>%
  group_by(Opening) %>%
  summarise(N_games = n()) %>%
  arrange(desc(N_games))

# top 10 most played openings
head(overall_op, 10) %>%
  ggplot(aes(x = reorder(Opening, N_games), y = N_games)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(
    title = "Most Played Openings Overall",
    x = "Opening",
    y = "Number of Games"
  ) +
  theme_light()

4. Which Openings Does He Play the Most Grouped by Color?

# top 10 most played openings grouped by color
top_by_color <- df %>%
  group_by(Color, Opening) %>%
  summarise(N_games = n()) %>%
  top_n(n = 10, wt = N_games)

# top 10 most played openings by white
white <- head(top_by_color, 10) %>%
  ggplot(aes(x = reorder(Opening, N_games), y = N_games)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(
    title = "Most Played Openings as White",
    x = "Openings",
    y = "Number of Games"
  ) +
  theme_light()


# top 10 most played openings by black
black <- tail(top_by_color, 10) %>%
  ggplot(aes(x = reorder(Opening, N_games), y = N_games)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  labs(
    title = "Most Played Openings as Black",
    x = "Openings",
    y = "Number of Games"
  ) +
  theme_light()

grid.arrange(white, black)

It looks like the most played opening is the sicilian defense for both white and black. This might happen because there are many variations of the sicilian defense, but all of them are grouped together in the data.

5. Does He Lose More Against Higher Rated Opponents?

# results against/not-against higher rated opponents
vs_higher <-df %>%
  mutate(Vs_higher = if_else(Opponent_rating > Rating, "yes","no")) %>%
  group_by(Vs_higher, Result) %>%
  summarise(count = n()) %>%
  mutate(Percentage = count / sum(count) * 100) %>%
  select(-count) %>%
  arrange(desc(Vs_higher))

# vars for inline code
highdraw <- round(vs_higher[1, 3], 2) %>% as.character()
lowwin <- round(vs_higher[6, 3], 2) %>% as.character()

#plot
vs_higher %>%
  ggplot(aes(x = Vs_higher, y = Percentage, fill = Result)) +
  geom_bar(stat = "identity", width = 0.6) +
  coord_flip() +
  labs(
    title = "Ratio of Game Results Against & not Against Higher-Rated Opponents",
    x = "Against Higher-Rated Opponents?",
    y = "Win : Loss : Draw"
  ) +
  guides(fill = guide_legend(reverse = TRUE)) +
  theme_light()

Against higher-rated opponents, nearly half of his games end in a draw. More specifically 45.37 %. His win-rate is only slightly higher than his lose rate. On the other hand, 48.14 % of his games against lower-rated opponents are wins.

6. What is the Average Move Count in his Games?

# avg number of moves
avg_mv <- round(mean(df$Moves)) %>% as.character()

# move count distribution
df %>%
  ggplot(aes(x = Moves)) +
  geom_histogram(binwidth = 5) +
  labs(
    title = "Distribution of Number of Moves in a Game",
    x = "Number of Moves",
    y = "Count"
  ) +
  theme_light()

The average number of moves of his games is 48 moves.

7. Does the Number of Moves Effect Game Results?

# avg moves for win/draw/loss plot
df %>%
  group_by(Result) %>%
  summarise(Mean_moves = mean(Moves)) %>%
  ggplot(aes(x = reorder(Result, Mean_moves), y = Mean_moves)) +
  geom_bar(stat = "identity", width = 0.6) +
  labs(
    title = "Average move counts of different game results",
    x = "Results",
    y = "Average move count"
  ) +
  theme_light()

The average move count is roughly the same for wins, losses, and draws. Number of moves is unlikely to effect game results.

8. Which year did he perform the best in?

# highest rating of each year 2003-2020
df %>%
  group_by(Year) %>%
  filter(Year > 2002) %>%
  summarise(highest_rt = max(Rating, na.rm = TRUE)) %>%
  ggplot(aes(x = Year, y = highest_rt)) +
  geom_line() +
  scale_x_continuous(breaks = seq(2002, 2020, 2)) +
  labs(
    title = "Yearly Highest Rating from 2003 to mid 2020",
    y = "Rating"
  ) +
  theme_light()

max_rt <- max(df$Rating) %>% as.character()

This chart starts from 2003, the year he became a GM. His highest rating was 2816 back in 2015.

9. Who does he play with most often?

# top 10 most played opponents
reg_opp <- df %>%
  group_by(Opponent) %>%
  summarise(n = n()) %>%
  arrange(desc(n)) %>%
  head(10)

# top 10 barchart
reg_opp %>%
  ggplot(aes(x = reorder(Opponent, n), y = n)) +
  geom_bar(stat = "identity", width = 0.6) +
  coord_flip() +
  labs(
    title = "Top 10 Players He Plays with Most Often",
    x = "Opponent",
    y = "Number of games played"
  ) +
  theme_light()

10. What are Hikaru’s Game Results Like Against Them?

# w/d/l ratio 
df %>%
  group_by(Opponent, Result) %>%
  filter(Opponent %in% reg_opp$Opponent) %>%
  summarise(n = n()) %>%
  ggplot(aes(x = Opponent, y = n, fill = Result)) +
  geom_bar(stat = "identity",position = "fill", width = 0.6) +
  coord_flip() +
  labs(
    title = "Results Against Players He Plays With Most Often",
    x = "Opponent",
    y = "Win : Loss : Draw"
  ) +
  guides(fill = guide_legend(reverse = TRUE)) +
  theme_light()

Against the players he plays with most often, the result is usually a draw. His win-rates are higher than his lose-rates except against Magnus Carlsen, the world champion.

11. Does the Opening Played Effect the Game Result?

Performing a Cochran-Maentel-Haenszel Test

before doing the test, I filtered the data to include only openings that were played
atleast 20 times. The null hypothesis is that there is no relationship between the opening played, the piece color, and the game result. The alternative hypothesis is that there is a relationship. Alpha value is 0.05.

# openings with game number > 20
freq_op <- df %>%
  group_by(Opening) %>%
  summarise(count = n()) %>%
  filter(count > 20)

# colors, openings, results 
op_res <- df %>%
  select(Color, Opening, Result) %>%
  filter(Opening %in% freq_op$Opening)

# cochran-mantel-haenszel tes on op_res
tb <- table(op_res$Color, op_res$Opening, op_res$Result)
mantelhaen.test(tb)

    Cochran-Mantel-Haenszel test

data:  tb
Cochran-Mantel-Haenszel M^2 = 432.41, df = 29, p-value < 2.2e-16

The p-value is significantly lower than the alpha value. The null hypothesis is rejected.

12. Which Openings Have the Highest Win-Rate Overall?

# top 10 op overal
df %>%
  filter(Opening %in% op_res$Opening) %>%
  group_by(Opening, Result) %>%
  summarise(Count = n()) %>%
  mutate(Winrate = round(Count / sum(Count) * 100, 2)) %>%
  filter(Result == "Win") %>%
  select(-Count, -Result) %>%
  arrange(desc(Winrate)) %>%
  head(10) %>%
  ggplot(aes(x = reorder(Opening, Winrate), y = Winrate)) +
  geom_bar(stat = "Identity", width = 0.6) +
  coord_flip() +
  scale_y_continuous(limits = c(0, 100)) +
  labs(
    title = "Top 10 Win-Rates of Openings Overall",
    x = "Opening",
    y = "Win-rate in percentages(%)"
  ) +
  theme_light()

12. Which Openings Have the Highest Win-Rate Grouped by Color?

# top 10 white op
opw <- df %>%
  filter(Opening %in% op_res$Opening) %>%
  group_by(Color, Opening, Result) %>%
  summarise(Count = n()) %>%
  mutate(Winrate = round(Count / sum(Count) * 100, 2)) %>%
  filter(Result == "Win" & Color == "White") %>%
  select(-Count, -Result) %>%
  arrange(desc(Winrate)) %>%
  head(10) %>%
  ggplot(aes(x = reorder(Opening, Winrate), y = Winrate)) +
  geom_bar(stat = "Identity", width = 0.6) +
  coord_flip() +
  scale_y_continuous(limits = c(0, 100)) +
  labs(
    title = "Top 10 Win-rates of Openings Played as White",
    x = "Opening",
    y = "Win-Rate in Percentages(%)"
  ) +
  theme_light()


# top 10 black op
opb <- df %>%
  filter(Opening %in% op_res$Opening) %>%
  group_by(Color, Opening, Result) %>%
  summarise(Count = n()) %>%
  mutate(Winrate = round(Count / sum(Count) * 100, 2)) %>%
  filter(Result == "Win" & Color == "Black") %>%
  select(-Count, -Result) %>%
  arrange(desc(Winrate)) %>%
  head(10) %>%
  ggplot(aes(x = reorder(Opening, Winrate), y = Winrate)) +
  geom_bar(stat = "Identity", width = 0.6) +
  coord_flip() +
  scale_y_continuous(limits = c(0, 100)) +
  labs(
    title = "Top 10 Win-rates of Openings Played as Black",
    x = "Opening",
    y = "Win-Rate in Percentages(%)"
  ) +
  theme_light()

grid.arrange(opw, opb)

LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyBvZiBIaWthcnUgTmFrYW11cmEncyBHYW1lcyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogcGFwZXIKLS0tCgpgYGB7ciBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UpCmBgYAoKIyMgKipJbXBvcnQgTGlicmFyaWVzKioKYGBge3J9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShncmlkRXh0cmEpCmBgYAoKIyMgKipJbXBvcnQgYW5kIFZpZXcgRGF0YSoqCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRX0KZGYgPC0gcmVhZF9jc3YoImRhdGEvY2xlYW5lZF9kYXRhLmNzdiIpCmhlYWQoZGYpCmBgYAoKIyMgKipBbmFseXNpcyoqCiMjIyAqKjEuIFdoYXQgaXMgSGlzIFdpbiBQZXJjZW50YWdlIE92ZXJhbGw/KioKYGBge3J9CiMgY291bnQgb2Ygd2luLCBkcmF3LCBsb3NzCm92ZXJhbGxfd2luIDwtIGRmICU+JQogIGdyb3VwX2J5KFJlc3VsdCkgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbigpKQoKIyB2YXJzIGZvciBpbmxpbmUgY29kZQpuZ2FtZSA8LSBzdW1tYXJpc2UoZGYsIG4oKSkgJT4lIGFzLmNoYXJhY3RlcigpCndpbnMgPC0gb3ZlcmFsbF93aW5bMywgMl0gJT4lIGFzLmNoYXJhY3RlcigpCmxvc3MgPC0gb3ZlcmFsbF93aW5bMiwgMl0gJT4lIGFzLmNoYXJhY3RlcigpCmRyYXcgPC0gb3ZlcmFsbF93aW5bMSwgMl0gJT4lIGFzLmNoYXJhY3RlcigpCndyIDwtIHJvdW5kKGFzLm51bWVyaWMod2lucykgLyBhcy5udW1lcmljKG5nYW1lKSAqIDEwMCwgMikKCiMgcGxvdApvdmVyYWxsX3dpbiAlPiUKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKFJlc3VsdCwgLUNvdW50KSwgeSA9IENvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJDb21wYXJpc29uIG9mIG51bWJlciBvZiB3aW5zLCBsb3NzZXMsIGFuZCBkcmF3cyIsCiAgICB4ID0gIlJlc3VsdCIKICApICsKICB0aGVtZV9saWdodCgpCmBgYApIaXMgd2luIHBlcmNlbnRhZ2UgaXMgYHIgd3JgICUuIE91dCBvZiBgciBuZ2FtZWAgZ2FtZXMsIGByIHdpbnNgIGFyZSB3aW5zLCBgciBsb3NzYCBhcmUgbG9zc2VzLAphbmQgYHIgZHJhd2AgYXJlIGRyYXdzLiAgTW9zdCBvZiBoaXMgZ2FtZXMgZW5kIGluIGVpdGhlciBhIHdpbiBvciBhIGRyYXcuIAoKCiMjIyAqKjIuIFdoYXQgaXMgSGlzIFdpbiBQZXJjZW50YWdlIEdyb3VwZWQgYnkgQ29sb3I/KioKYGBge3J9CiMgcmF0aW8gb2Ygd2luLCBkcmF3LCBsb3NzIGJ5IGNvbG9yCndpbnJhdGUgPC0gZGYgJT4lCiAgZ3JvdXBfYnkoQ29sb3IsIFJlc3VsdCkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICBtdXRhdGUoUGVyY2VudGFnZSA9IGNvdW50IC8gc3VtKGNvdW50KSAqIDEwMCkgJT4lCiAgc2VsZWN0KC1jb3VudCkgCgojIHZhcnMgZm9yIGlubGluZSBjb2RlCndfd3IgPC0gcm91bmQod2lucmF0ZVs2LCAzXSwgMikgJT4lIGFzLmNoYXJhY3RlcigpCmJfd3IgPC0gcm91bmQod2lucmF0ZVszLCAzXSwgMikgJT4lIGFzLmNoYXJhY3RlcigpCgojIHBsb3QKd2lucmF0ZSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBDb2xvciwgeSA9IFBlcmNlbnRhZ2UsIGZpbGwgPSBSZXN1bHQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImZpbGwiLCB3aWR0aCA9IDAuNikgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicygKICAgIHRpdGxlID0gIlJhdGlvIG9mIFdpbnMsIExvc3NlcyAmIERyYXdzIEdyb3VwZWQgYnkgQ29sb3IiLAogICAgeSA9ICJXaW4gOiBMb3NzIDogRHJhdyIKICApICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpICsKICB0aGVtZV9saWdodCgpCmBgYApIaXMgd2luLXJhdGUgYXMgd2hpdGUgaXMgYHIgd193cmAgJSwgd2hpY2ggaXMgaGlnaGVyIHRoYW4gaGlzIHdpbi1yYXRlIGFzIGJsYWNrIGByIGJfd3JgICUuIApXaW4gdG8gbG9zcyB0byBkcmF3IHJhdGlvIGlzIHJvdWdobHkgdGhlIHNhbWUgYXMgd2hpdGUgb3IgYmxhY2suCgoKIyMjICoqMy4gV2hpY2ggT3BlbmluZ3MgRG9lcyBIZSBQbGF5IHRoZSBNb3N0IE92ZXJhbGw/KioKYGBge3J9CiMgb3BlbmluZ3MgYW5kIHRoZWlyIGdhbWUgY291bnQKb3ZlcmFsbF9vcCA8LSBkZiAlPiUKICBncm91cF9ieShPcGVuaW5nKSAlPiUKICBzdW1tYXJpc2UoTl9nYW1lcyA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKE5fZ2FtZXMpKQoKIyB0b3AgMTAgbW9zdCBwbGF5ZWQgb3BlbmluZ3MKaGVhZChvdmVyYWxsX29wLCAxMCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihPcGVuaW5nLCBOX2dhbWVzKSwgeSA9IE5fZ2FtZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJNb3N0IFBsYXllZCBPcGVuaW5ncyBPdmVyYWxsIiwKICAgIHggPSAiT3BlbmluZyIsCiAgICB5ID0gIk51bWJlciBvZiBHYW1lcyIKICApICsKICB0aGVtZV9saWdodCgpCmBgYAoKIyMjICoqNC4gV2hpY2ggT3BlbmluZ3MgRG9lcyBIZSBQbGF5IHRoZSBNb3N0IEdyb3VwZWQgYnkgQ29sb3I/KioKYGBge3J9CiMgdG9wIDEwIG1vc3QgcGxheWVkIG9wZW5pbmdzIGdyb3VwZWQgYnkgY29sb3IKdG9wX2J5X2NvbG9yIDwtIGRmICU+JQogIGdyb3VwX2J5KENvbG9yLCBPcGVuaW5nKSAlPiUKICBzdW1tYXJpc2UoTl9nYW1lcyA9IG4oKSkgJT4lCiAgdG9wX24obiA9IDEwLCB3dCA9IE5fZ2FtZXMpCgojIHRvcCAxMCBtb3N0IHBsYXllZCBvcGVuaW5ncyBieSB3aGl0ZQp3aGl0ZSA8LSBoZWFkKHRvcF9ieV9jb2xvciwgMTApICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoT3BlbmluZywgTl9nYW1lcyksIHkgPSBOX2dhbWVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAiTW9zdCBQbGF5ZWQgT3BlbmluZ3MgYXMgV2hpdGUiLAogICAgeCA9ICJPcGVuaW5ncyIsCiAgICB5ID0gIk51bWJlciBvZiBHYW1lcyIKICApICsKICB0aGVtZV9saWdodCgpCgoKIyB0b3AgMTAgbW9zdCBwbGF5ZWQgb3BlbmluZ3MgYnkgYmxhY2sKYmxhY2sgPC0gdGFpbCh0b3BfYnlfY29sb3IsIDEwKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKE9wZW5pbmcsIE5fZ2FtZXMpLCB5ID0gTl9nYW1lcykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicygKICAgIHRpdGxlID0gIk1vc3QgUGxheWVkIE9wZW5pbmdzIGFzIEJsYWNrIiwKICAgIHggPSAiT3BlbmluZ3MiLAogICAgeSA9ICJOdW1iZXIgb2YgR2FtZXMiCiAgKSArCiAgdGhlbWVfbGlnaHQoKQoKZ3JpZC5hcnJhbmdlKHdoaXRlLCBibGFjaykKYGBgCkl0IGxvb2tzIGxpa2UgdGhlIG1vc3QgcGxheWVkIG9wZW5pbmcgaXMgdGhlIHNpY2lsaWFuIGRlZmVuc2UgZm9yIGJvdGggd2hpdGUgYW5kIGJsYWNrLiBUaGlzIG1pZ2h0IGhhcHBlbiAKYmVjYXVzZSB0aGVyZSBhcmUgbWFueSB2YXJpYXRpb25zIG9mIHRoZSBzaWNpbGlhbiBkZWZlbnNlLCBidXQgYWxsIG9mIHRoZW0gYXJlIGdyb3VwZWQgdG9nZXRoZXIgaW4gdGhlIGRhdGEuCgoKIyMjICoqNS4gRG9lcyBIZSBMb3NlIE1vcmUgQWdhaW5zdCBIaWdoZXIgUmF0ZWQgT3Bwb25lbnRzPyoqCmBgYHtyfQojIHJlc3VsdHMgYWdhaW5zdC9ub3QtYWdhaW5zdCBoaWdoZXIgcmF0ZWQgb3Bwb25lbnRzCnZzX2hpZ2hlciA8LWRmICU+JQogIG11dGF0ZShWc19oaWdoZXIgPSBpZl9lbHNlKE9wcG9uZW50X3JhdGluZyA+IFJhdGluZywgInllcyIsIm5vIikpICU+JQogIGdyb3VwX2J5KFZzX2hpZ2hlciwgUmVzdWx0KSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIG11dGF0ZShQZXJjZW50YWdlID0gY291bnQgLyBzdW0oY291bnQpICogMTAwKSAlPiUKICBzZWxlY3QoLWNvdW50KSAlPiUKICBhcnJhbmdlKGRlc2MoVnNfaGlnaGVyKSkKCiMgdmFycyBmb3IgaW5saW5lIGNvZGUKaGlnaGRyYXcgPC0gcm91bmQodnNfaGlnaGVyWzEsIDNdLCAyKSAlPiUgYXMuY2hhcmFjdGVyKCkKbG93d2luIDwtIHJvdW5kKHZzX2hpZ2hlcls2LCAzXSwgMikgJT4lIGFzLmNoYXJhY3RlcigpCgojcGxvdAp2c19oaWdoZXIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gVnNfaGlnaGVyLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IFJlc3VsdCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjYpICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJSYXRpbyBvZiBHYW1lIFJlc3VsdHMgQWdhaW5zdCAmIG5vdCBBZ2FpbnN0IEhpZ2hlci1SYXRlZCBPcHBvbmVudHMiLAogICAgeCA9ICJBZ2FpbnN0IEhpZ2hlci1SYXRlZCBPcHBvbmVudHM/IiwKICAgIHkgPSAiV2luIDogTG9zcyA6IERyYXciCiAgKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArCiAgdGhlbWVfbGlnaHQoKQpgYGAKQWdhaW5zdCBoaWdoZXItcmF0ZWQgb3Bwb25lbnRzLCBuZWFybHkgaGFsZiBvZiBoaXMgZ2FtZXMgZW5kIGluIGEgZHJhdy4gTW9yZSBzcGVjaWZpY2FsbHkgCmByIGhpZ2hkcmF3YCAlLiBIaXMgd2luLXJhdGUgaXMgb25seSBzbGlnaHRseSBoaWdoZXIgdGhhbiBoaXMgbG9zZSByYXRlLiBPbiB0aGUgb3RoZXIgaGFuZCwgCmByIGxvd3dpbmAgJSBvZiBoaXMgZ2FtZXMgYWdhaW5zdCBsb3dlci1yYXRlZCBvcHBvbmVudHMgYXJlIHdpbnMuCgoKIyMjICoqNi4gV2hhdCBpcyB0aGUgQXZlcmFnZSBNb3ZlIENvdW50IGluIGhpcyBHYW1lcz8qKgpgYGB7cn0KIyBhdmcgbnVtYmVyIG9mIG1vdmVzCmF2Z19tdiA8LSByb3VuZChtZWFuKGRmJE1vdmVzKSkgJT4lIGFzLmNoYXJhY3RlcigpCgojIG1vdmUgY291bnQgZGlzdHJpYnV0aW9uCmRmICU+JQogIGdncGxvdChhZXMoeCA9IE1vdmVzKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgTnVtYmVyIG9mIE1vdmVzIGluIGEgR2FtZSIsCiAgICB4ID0gIk51bWJlciBvZiBNb3ZlcyIsCiAgICB5ID0gIkNvdW50IgogICkgKwogIHRoZW1lX2xpZ2h0KCkKYGBgClRoZSBhdmVyYWdlIG51bWJlciBvZiBtb3ZlcyBvZiBoaXMgZ2FtZXMgaXMgYHIgYXZnX212YCBtb3Zlcy4KCgojIyMgKio3LiBEb2VzIHRoZSBOdW1iZXIgb2YgTW92ZXMgRWZmZWN0IEdhbWUgUmVzdWx0cz8qKgpgYGB7cn0KIyBhdmcgbW92ZXMgZm9yIHdpbi9kcmF3L2xvc3MgcGxvdApkZiAlPiUKICBncm91cF9ieShSZXN1bHQpICU+JQogIHN1bW1hcmlzZShNZWFuX21vdmVzID0gbWVhbihNb3ZlcykpICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoUmVzdWx0LCBNZWFuX21vdmVzKSwgeSA9IE1lYW5fbW92ZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC42KSArCiAgbGFicygKICAgIHRpdGxlID0gIkF2ZXJhZ2UgbW92ZSBjb3VudHMgb2YgZGlmZmVyZW50IGdhbWUgcmVzdWx0cyIsCiAgICB4ID0gIlJlc3VsdHMiLAogICAgeSA9ICJBdmVyYWdlIG1vdmUgY291bnQiCiAgKSArCiAgdGhlbWVfbGlnaHQoKQpgYGAKVGhlIGF2ZXJhZ2UgbW92ZSBjb3VudCBpcyByb3VnaGx5IHRoZSBzYW1lIGZvciB3aW5zLCBsb3NzZXMsIGFuZCBkcmF3cy4gTnVtYmVyIG9mIG1vdmVzIGlzIAp1bmxpa2VseSB0byBlZmZlY3QgZ2FtZSByZXN1bHRzLgoKCiMjIyAqKjguIFdoaWNoIHllYXIgZGlkIGhlIHBlcmZvcm0gdGhlIGJlc3QgaW4/KioKYGBge3J9CiMgaGlnaGVzdCByYXRpbmcgb2YgZWFjaCB5ZWFyIDIwMDMtMjAyMApkZiAlPiUKICBncm91cF9ieShZZWFyKSAlPiUKICBmaWx0ZXIoWWVhciA+IDIwMDIpICU+JQogIHN1bW1hcmlzZShoaWdoZXN0X3J0ID0gbWF4KFJhdGluZywgbmEucm0gPSBUUlVFKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IGhpZ2hlc3RfcnQpKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMjAwMiwgMjAyMCwgMikpICsKICBsYWJzKAogICAgdGl0bGUgPSAiWWVhcmx5IEhpZ2hlc3QgUmF0aW5nIGZyb20gMjAwMyB0byBtaWQgMjAyMCIsCiAgICB5ID0gIlJhdGluZyIKICApICsKICB0aGVtZV9saWdodCgpCgptYXhfcnQgPC0gbWF4KGRmJFJhdGluZykgJT4lIGFzLmNoYXJhY3RlcigpCmBgYApUaGlzIGNoYXJ0IHN0YXJ0cyBmcm9tIDIwMDMsIHRoZSB5ZWFyIGhlIGJlY2FtZSBhIEdNLiBIaXMgaGlnaGVzdCByYXRpbmcgd2FzIGByIG1heF9ydGAgYmFjayBpbiAyMDE1LgoKCiMjIyAqKjkuIFdobyBkb2VzIGhlIHBsYXkgd2l0aCBtb3N0IG9mdGVuPyoqCmBgYHtyfQojIHRvcCAxMCBtb3N0IHBsYXllZCBvcHBvbmVudHMKcmVnX29wcCA8LSBkZiAlPiUKICBncm91cF9ieShPcHBvbmVudCkgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAgaGVhZCgxMCkKCiMgdG9wIDEwIGJhcmNoYXJ0CnJlZ19vcHAgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihPcHBvbmVudCwgbiksIHkgPSBuKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDAuNikgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicygKICAgIHRpdGxlID0gIlRvcCAxMCBQbGF5ZXJzIEhlIFBsYXlzIHdpdGggTW9zdCBPZnRlbiIsCiAgICB4ID0gIk9wcG9uZW50IiwKICAgIHkgPSAiTnVtYmVyIG9mIGdhbWVzIHBsYXllZCIKICApICsKICB0aGVtZV9saWdodCgpCmBgYAoKIyMjICoqMTAuIFdoYXQgYXJlIEhpa2FydSdzIEdhbWUgUmVzdWx0cyBMaWtlIEFnYWluc3QgVGhlbT8qKgpgYGB7cn0KIyB3L2QvbCByYXRpbyAKZGYgJT4lCiAgZ3JvdXBfYnkoT3Bwb25lbnQsIFJlc3VsdCkgJT4lCiAgZmlsdGVyKE9wcG9uZW50ICVpbiUgcmVnX29wcCRPcHBvbmVudCkgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JQogIGdncGxvdChhZXMoeCA9IE9wcG9uZW50LCB5ID0gbiwgZmlsbCA9IFJlc3VsdCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iixwb3NpdGlvbiA9ICJmaWxsIiwgd2lkdGggPSAwLjYpICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJSZXN1bHRzIEFnYWluc3QgUGxheWVycyBIZSBQbGF5cyBXaXRoIE1vc3QgT2Z0ZW4iLAogICAgeCA9ICJPcHBvbmVudCIsCiAgICB5ID0gIldpbiA6IExvc3MgOiBEcmF3IgogICkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgKwogIHRoZW1lX2xpZ2h0KCkKYGBgCkFnYWluc3QgdGhlIHBsYXllcnMgaGUgcGxheXMgd2l0aCBtb3N0IG9mdGVuLCB0aGUgcmVzdWx0IGlzIHVzdWFsbHkgYSBkcmF3LiBIaXMgd2luLXJhdGVzIGFyZSAKaGlnaGVyIHRoYW4gaGlzIGxvc2UtcmF0ZXMgZXhjZXB0IGFnYWluc3QgTWFnbnVzIENhcmxzZW4sIHRoZSB3b3JsZCBjaGFtcGlvbi4KCgojIyMgKioxMS4gRG9lcyB0aGUgT3BlbmluZyBQbGF5ZWQgRWZmZWN0IHRoZSBHYW1lIFJlc3VsdD8qKgojIyMjICoqUGVyZm9ybWluZyBhIENvY2hyYW4tTWFlbnRlbC1IYWVuc3plbCBUZXN0KioKYmVmb3JlIGRvaW5nIHRoZSB0ZXN0LCBJIGZpbHRlcmVkIHRoZSBkYXRhIHRvIGluY2x1ZGUgb25seSBvcGVuaW5ncyB0aGF0IHdlcmUgcGxheWVkICAKYXRsZWFzdCAyMCB0aW1lcy4gVGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0aGF0IHRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIAp0aGUgb3BlbmluZyBwbGF5ZWQsIHRoZSBwaWVjZSBjb2xvciwgYW5kIHRoZSBnYW1lIHJlc3VsdC4gVGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgCmlzIHRoYXQgdGhlcmUgaXMgYSByZWxhdGlvbnNoaXAuIEFscGhhIHZhbHVlIGlzIDAuMDUuCmBgYHtyfQojIG9wZW5pbmdzIHdpdGggZ2FtZSBudW1iZXIgPiAyMApmcmVxX29wIDwtIGRmICU+JQogIGdyb3VwX2J5KE9wZW5pbmcpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgZmlsdGVyKGNvdW50ID4gMjApCgojIGNvbG9ycywgb3BlbmluZ3MsIHJlc3VsdHMgCm9wX3JlcyA8LSBkZiAlPiUKICBzZWxlY3QoQ29sb3IsIE9wZW5pbmcsIFJlc3VsdCkgJT4lCiAgZmlsdGVyKE9wZW5pbmcgJWluJSBmcmVxX29wJE9wZW5pbmcpCgojIGNvY2hyYW4tbWFudGVsLWhhZW5zemVsIHRlcyBvbiBvcF9yZXMKdGIgPC0gdGFibGUob3BfcmVzJENvbG9yLCBvcF9yZXMkT3BlbmluZywgb3BfcmVzJFJlc3VsdCkKbWFudGVsaGFlbi50ZXN0KHRiKQpgYGAKVGhlIHAtdmFsdWUgaXMgc2lnbmlmaWNhbnRseSBsb3dlciB0aGFuIHRoZSBhbHBoYSB2YWx1ZS4gVGhlIG51bGwgaHlwb3RoZXNpcyBpcyByZWplY3RlZC4KCiMjIyAqKjEyLiBXaGljaCBPcGVuaW5ncyBIYXZlIHRoZSBIaWdoZXN0IFdpbi1SYXRlIE92ZXJhbGw/KioKYGBge3J9CiMgdG9wIDEwIG9wIG92ZXJhbApkZiAlPiUKICBmaWx0ZXIoT3BlbmluZyAlaW4lIG9wX3JlcyRPcGVuaW5nKSAlPiUKICBncm91cF9ieShPcGVuaW5nLCBSZXN1bHQpICU+JQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lCiAgbXV0YXRlKFdpbnJhdGUgPSByb3VuZChDb3VudCAvIHN1bShDb3VudCkgKiAxMDAsIDIpKSAlPiUKICBmaWx0ZXIoUmVzdWx0ID09ICJXaW4iKSAlPiUKICBzZWxlY3QoLUNvdW50LCAtUmVzdWx0KSAlPiUKICBhcnJhbmdlKGRlc2MoV2lucmF0ZSkpICU+JQogIGhlYWQoMTApICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoT3BlbmluZywgV2lucmF0ZSksIHkgPSBXaW5yYXRlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiSWRlbnRpdHkiLCB3aWR0aCA9IDAuNikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTAwKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJUb3AgMTAgV2luLVJhdGVzIG9mIE9wZW5pbmdzIE92ZXJhbGwiLAogICAgeCA9ICJPcGVuaW5nIiwKICAgIHkgPSAiV2luLXJhdGUgaW4gcGVyY2VudGFnZXMoJSkiCiAgKSArCiAgdGhlbWVfbGlnaHQoKQpgYGAKCiMjIyAqKjEyLiBXaGljaCBPcGVuaW5ncyBIYXZlIHRoZSBIaWdoZXN0IFdpbi1SYXRlIEdyb3VwZWQgYnkgQ29sb3I/KioKYGBge3J9CiMgdG9wIDEwIHdoaXRlIG9wCm9wdyA8LSBkZiAlPiUKICBmaWx0ZXIoT3BlbmluZyAlaW4lIG9wX3JlcyRPcGVuaW5nKSAlPiUKICBncm91cF9ieShDb2xvciwgT3BlbmluZywgUmVzdWx0KSAlPiUKICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpICU+JQogIG11dGF0ZShXaW5yYXRlID0gcm91bmQoQ291bnQgLyBzdW0oQ291bnQpICogMTAwLCAyKSkgJT4lCiAgZmlsdGVyKFJlc3VsdCA9PSAiV2luIiAmIENvbG9yID09ICJXaGl0ZSIpICU+JQogIHNlbGVjdCgtQ291bnQsIC1SZXN1bHQpICU+JQogIGFycmFuZ2UoZGVzYyhXaW5yYXRlKSkgJT4lCiAgaGVhZCgxMCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihPcGVuaW5nLCBXaW5yYXRlKSwgeSA9IFdpbnJhdGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJJZGVudGl0eSIsIHdpZHRoID0gMC42KSArCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxMDApKSArCiAgbGFicygKICAgIHRpdGxlID0gIlRvcCAxMCBXaW4tcmF0ZXMgb2YgT3BlbmluZ3MgUGxheWVkIGFzIFdoaXRlIiwKICAgIHggPSAiT3BlbmluZyIsCiAgICB5ID0gIldpbi1SYXRlIGluIFBlcmNlbnRhZ2VzKCUpIgogICkgKwogIHRoZW1lX2xpZ2h0KCkKCgojIHRvcCAxMCBibGFjayBvcApvcGIgPC0gZGYgJT4lCiAgZmlsdGVyKE9wZW5pbmcgJWluJSBvcF9yZXMkT3BlbmluZykgJT4lCiAgZ3JvdXBfYnkoQ29sb3IsIE9wZW5pbmcsIFJlc3VsdCkgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbigpKSAlPiUKICBtdXRhdGUoV2lucmF0ZSA9IHJvdW5kKENvdW50IC8gc3VtKENvdW50KSAqIDEwMCwgMikpICU+JQogIGZpbHRlcihSZXN1bHQgPT0gIldpbiIgJiBDb2xvciA9PSAiQmxhY2siKSAlPiUKICBzZWxlY3QoLUNvdW50LCAtUmVzdWx0KSAlPiUKICBhcnJhbmdlKGRlc2MoV2lucmF0ZSkpICU+JQogIGhlYWQoMTApICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoT3BlbmluZywgV2lucmF0ZSksIHkgPSBXaW5yYXRlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiSWRlbnRpdHkiLCB3aWR0aCA9IDAuNikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTAwKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJUb3AgMTAgV2luLXJhdGVzIG9mIE9wZW5pbmdzIFBsYXllZCBhcyBCbGFjayIsCiAgICB4ID0gIk9wZW5pbmciLAogICAgeSA9ICJXaW4tUmF0ZSBpbiBQZXJjZW50YWdlcyglKSIKICApICsKICB0aGVtZV9saWdodCgpCgpncmlkLmFycmFuZ2Uob3B3LCBvcGIpCmBgYAoK