The Data

The dataset I have chosen to conduct my regression analysis is the (LoL) League of Legends Ranked Games dataset. This data looks at ranked League of Legends games played during season 9 in the EUW region. This dataset was collected using the Riot Games API, which makes it easy to look up and collect information on a users ranked history and collect their games.

# Load packages
library(ggplot2)
library(rstanarm)
library(bayesplot)
library(bayesrules)
library(tidyverse)
library(tidybayes)
library(dplyr)
library(broom.mixed)
library(interactions)

To preface this analysis, one must first understand the game of League of Legends, or LOL for short. In LOL, two 5 person teams fight to destroy each others nexus and get to choose and ban a champion, each with a different set of skills and abilities, and 2 summoner spells to do so. Along the way, the players must both fight each other, destroy objectives such as towers and inhibitors, and strategize effectively to reach the opponent’s nexus. There are also several monsters that teams can defeat to buff them as well, such as Rift Herald, Baron Nashor, and an assorted set of Dragons.

We will use lol_data to build various models of League of Legends gameDuration. Throughout, we’ll utilize weakly informative priors and a basic understanding that LOL games usually are 30 minutes but can range from 25-35 minutes. We will asses an array of different predictors and combinations to determine what can provide us the best fit for predicting the game duration of a League of Legends game. A base criteria for these games is there at least needs to be one tower, one inhibitor, and one champion kill for each game. This is to mitigate the amount of games that teams may have forfeited due to a player leaving or trolling.

lol_data <- read.csv("games.csv",
                    sep=",",
                    na.strings=c(""," ","NA","N/A")
)
head(lol_data)

Setting Up the Data

Transforming data to fit baseline of at least one kill, tower, and inhibitor

lol_data <- lol_data[!(lol_data$firstBlood == 0 | lol_data$firstTower == 0 | lol_data$firstInhibitor == 0), ]
nrow(lol_data)
[1] 45214
colnames(lol_data)
 [1] "gameId"             "creationTime"       "gameDuration"      
 [4] "seasonId"           "winner"             "firstBlood"        
 [7] "firstTower"         "firstInhibitor"     "firstBaron"        
[10] "firstDragon"        "firstRiftHerald"    "t1_champ1id"       
[13] "t1_champ1_sum1"     "t1_champ1_sum2"     "t1_champ2id"       
[16] "t1_champ2_sum1"     "t1_champ2_sum2"     "t1_champ3id"       
[19] "t1_champ3_sum1"     "t1_champ3_sum2"     "t1_champ4id"       
[22] "t1_champ4_sum1"     "t1_champ4_sum2"     "t1_champ5id"       
[25] "t1_champ5_sum1"     "t1_champ5_sum2"     "t1_towerKills"     
[28] "t1_inhibitorKills"  "t1_baronKills"      "t1_dragonKills"    
[31] "t1_riftHeraldKills" "t1_ban1"            "t1_ban2"           
[34] "t1_ban3"            "t1_ban4"            "t1_ban5"           
[37] "t2_champ1id"        "t2_champ1_sum1"     "t2_champ1_sum2"    
[40] "t2_champ2id"        "t2_champ2_sum1"     "t2_champ2_sum2"    
[43] "t2_champ3id"        "t2_champ3_sum1"     "t2_champ3_sum2"    
[46] "t2_champ4id"        "t2_champ4_sum1"     "t2_champ4_sum2"    
[49] "t2_champ5id"        "t2_champ5_sum1"     "t2_champ5_sum2"    
[52] "t2_towerKills"      "t2_inhibitorKills"  "t2_baronKills"     
[55] "t2_dragonKills"     "t2_riftHeraldKills" "t2_ban1"           
[58] "t2_ban2"            "t2_ban3"            "t2_ban4"           
[61] "t2_ban5"           

We can clean up our data by omitting unnecessary columns

lol_data <- subset(lol_data, select = -c(t2_ban1, t2_ban2, t2_ban3, t2_ban4, t2_ban5, t1_ban1, t1_ban2, t1_ban3, t1_ban4, t1_ban5, t1_champ1_sum1, t1_champ2_sum1, t1_champ3_sum1, t1_champ4_sum1, t1_champ5_sum1, t2_champ5_sum1, t2_champ1_sum1, t2_champ2_sum1, t2_champ3_sum1, t2_champ4_sum1, t2_champ5_sum1, t1_champ1_sum2, t1_champ2_sum2, t1_champ3_sum2, t1_champ4_sum2, t1_champ5_sum2, t2_champ1_sum2, t2_champ1_sum2, t2_champ2_sum2, t2_champ3_sum2, t2_champ4_sum2, t2_champ5_sum2, t1_champ1id, t1_champ2id, t1_champ3id, t1_champ4id, t1_champ5id, t2_champ1id, t2_champ2id, t2_champ3id, t2_champ4id, t2_champ5id))

colnames(lol_data)
 [1] "gameId"             "creationTime"       "gameDuration"      
 [4] "seasonId"           "winner"             "firstBlood"        
 [7] "firstTower"         "firstInhibitor"     "firstBaron"        
[10] "firstDragon"        "firstRiftHerald"    "t1_towerKills"     
[13] "t1_inhibitorKills"  "t1_baronKills"      "t1_dragonKills"    
[16] "t1_riftHeraldKills" "t2_towerKills"      "t2_inhibitorKills" 
[19] "t2_baronKills"      "t2_dragonKills"     "t2_riftHeraldKills"

We can also covert some of the columns to factors for team 1 and team 2 for ease of visualization and interpretation

lol_data$winner <- as.factor(lol_data$winner)
lol_data$firstBlood <- as.factor(lol_data$firstBlood)
lol_data$firstTower <- as.factor(lol_data$firstTower)
lol_data$firstInhibitor <- as.factor(lol_data$firstInhibitor)
lol_data$firstBaron <- as.factor(lol_data$firstBaron)
lol_data$firstDragon <- as.factor(lol_data$firstDragon)
lol_data$firstRiftHerald <- as.factor(lol_data$firstRiftHerald)
head(lol_data)

Summary Analysis

Here are some visualizations and statistics to help better understand our data and what we are working with.

summary(lol_data)
     gameId           creationTime        gameDuration     seasonId
 Min.   :3.215e+09   Min.   :1.497e+12   Min.   : 477   Min.   :9  
 1st Qu.:3.292e+09   1st Qu.:1.502e+12   1st Qu.:1632   1st Qu.:9  
 Median :3.320e+09   Median :1.504e+12   Median :1895   Median :9  
 Mean   :3.306e+09   Mean   :1.503e+12   Mean   :1930   Mean   :9  
 3rd Qu.:3.327e+09   3rd Qu.:1.504e+12   3rd Qu.:2196   3rd Qu.:9  
 Max.   :3.332e+09   Max.   :1.505e+12   Max.   :4728   Max.   :9  
 winner    firstBlood firstTower firstInhibitor firstBaron firstDragon
 1:22867   1:23151    1:23248    1:23054        0:14617    0:  434    
 2:22347   2:22063    2:21966    2:22160        1:14469    1:22258    
                                                2:16128    2:22522    
                                                                      
                                                                      
                                                                      
 firstRiftHerald t1_towerKills    t1_inhibitorKills t1_baronKills   
 0:21922         Min.   : 0.000   Min.   : 0.000    Min.   :0.0000  
 1:11933         1st Qu.: 3.000   1st Qu.: 0.000    1st Qu.:0.0000  
 2:11359         Median : 7.000   Median : 1.000    Median :0.0000  
                 Mean   : 6.173   Mean   : 1.159    Mean   :0.4173  
                 3rd Qu.:10.000   3rd Qu.: 2.000    3rd Qu.:1.0000  
                 Max.   :11.000   Max.   :10.000    Max.   :5.0000  
 t1_dragonKills  t1_riftHeraldKills t2_towerKills    t2_inhibitorKills
 Min.   :0.000   Min.   :0.0000     Min.   : 0.000   Min.   : 0.000   
 1st Qu.:0.000   1st Qu.:0.0000     1st Qu.: 2.000   1st Qu.: 0.000   
 Median :1.000   Median :0.0000     Median : 7.000   Median : 1.000   
 Mean   :1.483   Mean   :0.2639     Mean   : 6.014   Mean   : 1.122   
 3rd Qu.:2.000   3rd Qu.:1.0000     3rd Qu.:10.000   3rd Qu.: 2.000   
 Max.   :6.000   Max.   :1.0000     Max.   :11.000   Max.   :10.000   
 t2_baronKills    t2_dragonKills  t2_riftHeraldKills
 Min.   :0.0000   Min.   :0.000   Min.   :0.0000    
 1st Qu.:0.0000   1st Qu.:0.000   1st Qu.:0.0000    
 Median :0.0000   Median :1.000   Median :0.0000    
 Mean   :0.4641   Mean   :1.506   Mean   :0.2512    
 3rd Qu.:1.0000   3rd Qu.:2.000   3rd Qu.:1.0000    
 Max.   :4.0000   Max.   :6.000   Max.   :1.0000    

Some conclusions we can make here:

Visualizations

We can use ggplot do visualize some of our findings

Response Distribution

ggplot(lol_data, aes(x = gameDuration)) +
  geom_histogram(binwidth = 60, fill = "blue", color = "black") +
  labs(x = "Game Duration (seconds)", y = "Frequency", title = "Distribution of Game Duration") +
  theme_minimal()

Game duration seems to be a bit right-skewed

First Comparisons

ggplot(lol_data, aes(x = winner, fill = winner)) + 
      geom_bar() + 
      theme(text = element_text(size=9)) +
      labs(y = "Winners")

ggplot(lol_data, aes(x = firstBlood, fill = firstBlood)) + 
      geom_bar() + 
      theme(text = element_text(size=9)) +
      labs(y = "First Bloods")

ggplot(lol_data, aes(x = firstTower, fill = firstTower)) + 
      geom_bar() + 
      theme(text = element_text(size=9)) +
      labs(y = "First Towers")

ggplot(lol_data, aes(x = firstInhibitor, fill = firstInhibitor)) + 
      geom_bar() + 
      theme(text = element_text(size=9)) +
      labs(y = "First Inhibitors")

ggplot(lol_data, aes(x = firstDragon, fill = firstDragon)) + 
      geom_bar() + 
      theme(text = element_text(size=9)) +
      labs(y = "First Dragons")

ggplot(lol_data, aes(x = firstRiftHerald, fill = firstRiftHerald)) + 
      geom_bar() + 
      theme(text = element_text(size=9)) +
      labs(y = "First Rift Heralds")

ggplot(lol_data, aes(x = firstBaron, fill = firstBaron)) + 
      geom_bar() + 
      theme(text = element_text(size=9)) +
      labs(y = "First Barons")

These all are consistent with our previous summary findings about first categories

Total Objectives Comparisons

ggplot(lol_data, aes(x = t1_towerKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

ggplot(lol_data, aes(x = t2_towerKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

tower_kills <- data.frame(Team = c("T1", "T2"),
                          Kills = c(sum(lol_data$t1_towerKills, na.rm = TRUE), 
                                    sum(lol_data$t2_towerKills, na.rm = TRUE)))

ggplot(tower_kills, aes(x = Team, y = Kills, fill = Team)) +
  geom_bar(stat = "identity") +
  labs(x = "Team", y = "Total Tower Kills", title = "Comparison of Tower Kills between T1 and T2")

More often than not, both teams tend to kill >6 towers per game

ggplot(lol_data, aes(x = t1_inhibitorKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

ggplot(lol_data, aes(x = t2_inhibitorKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

# Calculate the total number of inhibitor kills for each team
inhibitor_kills <- data.frame(Team = c("T1", "T2"),
                              Kills = c(sum(lol_data$t1_inhibitorKills, na.rm = TRUE), 
                                        sum(lol_data$t2_inhibitorKills, na.rm = TRUE)))

ggplot(inhibitor_kills, aes(x = Team, y = Kills, fill = Team)) +
  geom_bar(stat = "identity") +
  labs(x = "Team", y = "Total Inhibitor Kills", title = "Comparison of Inhibitor Kills between T1 and T2")

Games usually end with 0 to 2 inhibitors being destroyed, likely due to late game forfeits or steam rolls (which is often the case in many games)

ggplot(lol_data, aes(x = t1_dragonKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

ggplot(lol_data, aes(x = t2_dragonKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

# Calculate the total number of dragon kills for each team
dragon_kills <- data.frame(Team = c("T1", "T2"),
                           Kills = c(sum(lol_data$t1_dragonKills, na.rm = TRUE), 
                                     sum(lol_data$t2_dragonKills, na.rm = TRUE)))

ggplot(dragon_kills, aes(x = Team, y = Kills, fill = Team)) +
  geom_bar(stat = "identity") +
  labs(x = "Team", y = "Total Dragon Kills", title = "Comparison of Dragon Kills between T1 and T2")

Dragon kills comfortably lie within the 0-2 range

ggplot(lol_data, aes(x = t1_riftHeraldKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

ggplot(lol_data, aes(x = t2_riftHeraldKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

# Calculate the total number of rift herald kills for each team
riftHerald_kills <- data.frame(Team = c("T1", "T2"),
                               Kills = c(sum(lol_data$t1_riftHeraldKills, na.rm = TRUE), 
                                         sum(lol_data$t2_riftHeraldKills, na.rm = TRUE)))

ggplot(riftHerald_kills, aes(x = Team, y = Kills, fill = Team)) +
  geom_bar(stat = "identity") +
  labs(x = "Team", y = "Total Rift Herald Kills", title = "Comparison of Rift Herald Kills between T1 and T2")

Rift herald only spawns from 9:50 - 19:45 during the game and is most often only taken once if at all

ggplot(lol_data, aes(x = t1_baronKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

ggplot(lol_data, aes(x = t2_baronKills)) + 
      geom_bar() + 
      theme(text = element_text(size=10)) +
      labs(y = "Count")

# Calculate the total number of baron kills for each team
baron_kills <- data.frame(Team = c("T1", "T2"),
                          Kills = c(sum(lol_data$t1_baronKills, na.rm = TRUE), 
                                    sum(lol_data$t2_baronKills, na.rm = TRUE)))

ggplot(baron_kills, aes(x = Team, y = Kills, fill = Team)) +
  geom_bar(stat = "identity") +
  labs(x = "Team", y = "Total Baron Kills", title = "Comparison of Baron Kills between T1 and T2")

Baron spawns after the rift herald and usually is game over if taken and used properly, hence why the amount taken is usually 0 or 1 as sometimes games end before it can be taken

Base Model and Prior Modeling

Lets start off with a base model to see if we can get insight from just the winner of the match

base <- stan_glm(gameDuration ~ winner,
  data = lol_data, family = gaussian, 
  prior_intercept = normal(1800, 150, autoscale = TRUE),
  prior = normal(0, 2.5, autoscale = TRUE), 
  prior_aux = exponential(1, autoscale = TRUE),
  chains = 4, iter = 5000*2, seed = 84735)

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 8e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.8 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 1: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 1: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 1: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 1: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 1: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 1: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 1: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 1: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 1: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 1: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 1: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 0.248 seconds (Warm-up)
Chain 1:                13.035 seconds (Sampling)
Chain 1:                13.283 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.6e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.16 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 2: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 2: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 2: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 2: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 2: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 2: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 2: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 2: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 2: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 2: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 2: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 0.227 seconds (Warm-up)
Chain 2:                13.029 seconds (Sampling)
Chain 2:                13.256 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 2.2e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.22 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 3: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 3: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 3: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 3: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 3: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 3: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 3: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 3: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 3: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 3: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 3: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 0.239 seconds (Warm-up)
Chain 3:                13.007 seconds (Sampling)
Chain 3:                13.246 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.6e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.16 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 4: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 4: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 4: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 4: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 4: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 4: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 4: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 4: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 4: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 4: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 4: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 0.239 seconds (Warm-up)
Chain 4:                12.989 seconds (Sampling)
Chain 4:                13.228 seconds (Total)
Chain 4: 
prior_summary(base) 
Priors for model 'base' 
------
Intercept (after predictors centered)
  Specified prior:
    ~ normal(location = 1800, scale = 150)
  Adjusted prior:
    ~ normal(location = 1800, scale = 64093)

Coefficients
  Specified prior:
    ~ normal(location = 0, scale = 2.5)
  Adjusted prior:
    ~ normal(location = 0, scale = 2137)

Auxiliary (sigma)
  Specified prior:
    ~ exponential(rate = 1)
  Adjusted prior:
    ~ exponential(rate = 0.0023)
------
See help('prior_summary.stanreg') for more details

A prior predictive check can be used to see what we can potentially expect from the data alone and what the stan model viewed as appropriate for our predictions

base_priors <- update(base, prior_PD = TRUE)

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 1.2e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.12 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 1: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 1: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 1: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 1: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 1: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 1: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 1: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 1: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 1: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 1: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 1: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 4.083 seconds (Warm-up)
Chain 1:                0.161 seconds (Sampling)
Chain 1:                4.244 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.7e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.17 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 2: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 2: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 2: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 2: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 2: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 2: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 2: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 2: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 2: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 2: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 2: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 2.508 seconds (Warm-up)
Chain 2:                0.148 seconds (Sampling)
Chain 2:                2.656 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 1.2e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.12 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 3: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 3: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 3: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 3: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 3: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 3: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 3: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 3: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 3: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 3: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 3: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 4.038 seconds (Warm-up)
Chain 3:                0.161 seconds (Sampling)
Chain 3:                4.199 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.2e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.12 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 4: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 4: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 4: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 4: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 4: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 4: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 4: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 4: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 4: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 4: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 4: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 4.794 seconds (Warm-up)
Chain 4:                0.159 seconds (Sampling)
Chain 4:                4.953 seconds (Total)
Chain 4: 
# 200 prior model lines
lol_data %>%
  add_fitted_draws(base, n = 200) %>%
  ggplot(aes(x = winner, y = gameDuration)) +
    geom_line(aes(y = .value, group = .draw), alpha = 0.05)
Warning: `fitted_draws` and `add_fitted_draws` are deprecated as their names were confusing.
- Use [add_]epred_draws() to get the expectation of the posterior predictive.
- Use [add_]linpred_draws() to get the distribution of the linear predictor.
- For example, you used [add_]fitted_draws(..., scale = "response"), which
  means you most likely want [add_]epred_draws(...).
NOTE: When updating to the new functions, note that the `model` parameter is now
  named `object` and the `n` parameter is now named `ndraws`.

# 4 prior simulated datasets
set.seed(3)
lol_data %>%
  add_predicted_draws(base, n = 4) %>%
  ggplot(aes(x = winner, y = gameDuration)) +
    geom_point(aes(y = .prediction, group = .draw)) + 
    facet_wrap(~ .draw)
Warning: 
In add_predicted_draws(): The `n` argument is a deprecated alias for `ndraws`.
Use the `ndraws` argument instead.
See help("tidybayes-deprecated").

Prior distribution expects the games to range from 1910 - 1945 seconds as expected

Game duration lies between 500-3500 seconds which falls in line with our summary

Prior distribution expects games to last longer if team 2 wins as compared to team 1

mcmc_trace(base, size = .1)

Plots look fast mixing and consistent

rhat(base)
(Intercept)     winner2       sigma 
  0.9998587   0.9999295   0.9999225 

Rhat close to 1 and not >1.05, good

neff_ratio(base)
(Intercept)     winner2       sigma 
    0.92220     0.95970     0.95575 

neff ratio is a little high but still acceptable, >.10 which is good

tidy(base, effects = c("fixed", "aux"),
     conf.int = TRUE, conf.level = 0.95)

Overall the model could work, but more investigation should be done to improve what we have

Interaction Investigation

Lets see if there are some interactions we can expect

Speaking from experience in playing the game myself, I predict the dragon and baron kills will have a big impact on the game duration due to the buffs they give each member of the team being crucial to winning

int <- stan_glm(gameDuration ~ t1_dragonKills:t2_dragonKills,
  data = lol_data, family = gaussian, 
  prior_intercept = normal(1800, 150, autoscale = TRUE),
  prior = normal(0, 2.5, autoscale = TRUE), 
  prior_aux = exponential(1, autoscale = TRUE),
  chains = 4, iter = 5000*2, seed = 84735)

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 2.4e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.24 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 1: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 1: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 1: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 1: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 1: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 1: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 1: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 1: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 1: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 1: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 1: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 0.266 seconds (Warm-up)
Chain 1:                13.025 seconds (Sampling)
Chain 1:                13.291 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.6e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.16 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 2: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 2: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 2: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 2: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 2: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 2: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 2: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 2: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 2: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 2: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 2: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 0.259 seconds (Warm-up)
Chain 2:                13.003 seconds (Sampling)
Chain 2:                13.262 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 1.7e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.17 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 3: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 3: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 3: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 3: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 3: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 3: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 3: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 3: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 3: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 3: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 3: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 0.251 seconds (Warm-up)
Chain 3:                12.961 seconds (Sampling)
Chain 3:                13.212 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.7e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.17 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 4: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 4: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 4: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 4: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 4: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 4: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 4: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 4: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 4: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 4: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 4: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 0.234 seconds (Warm-up)
Chain 4:                13.145 seconds (Sampling)
Chain 4:                13.379 seconds (Total)
Chain 4: 
int2 <- stan_glm(gameDuration ~ t1_baronKills:t2_baronKills,
  data = lol_data, family = gaussian, 
  prior_intercept = normal(1800, 150, autoscale = TRUE),
  prior = normal(0, 2.5, autoscale = TRUE), 
  prior_aux = exponential(1, autoscale = TRUE),
  chains = 4, iter = 5000*2, seed = 84735)

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 1.8e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.18 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 1: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 1: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 1: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 1: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 1: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 1: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 1: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 1: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 1: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 1: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 1: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 0.256 seconds (Warm-up)
Chain 1:                13.03 seconds (Sampling)
Chain 1:                13.286 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.7e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.17 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 2: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 2: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 2: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 2: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 2: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 2: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 2: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 2: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 2: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 2: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 2: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 0.241 seconds (Warm-up)
Chain 2:                12.952 seconds (Sampling)
Chain 2:                13.193 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 1.8e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.18 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 3: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 3: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 3: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 3: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 3: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 3: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 3: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 3: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 3: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 3: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 3: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 0.26 seconds (Warm-up)
Chain 3:                13.004 seconds (Sampling)
Chain 3:                13.264 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.6e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.16 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 4: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 4: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 4: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 4: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 4: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 4: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 4: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 4: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 4: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 4: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 4: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 0.233 seconds (Warm-up)
Chain 4:                12.956 seconds (Sampling)
Chain 4:                13.189 seconds (Total)
Chain 4: 
summary(int)

Model Info:
 function:     stan_glm
 family:       gaussian [identity]
 formula:      gameDuration ~ t1_dragonKills:t2_dragonKills
 algorithm:    sampling
 sample:       20000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 45214
 predictors:   2

Estimates:
                                mean   sd     10%    50%    90% 
(Intercept)                   1728.5    2.0 1725.9 1728.5 1731.1
t1_dragonKills:t2_dragonKills  147.5    0.9  146.3  147.5  148.6
sigma                          340.5    1.1  339.0  340.5  341.9

Fit Diagnostics:
           mean   sd     10%    50%    90% 
mean_PPD 1929.8    2.3 1926.9 1929.8 1932.7

The mean_ppd is the sample average posterior predictive distribution of the outcome variable (for details see help('summary.stanreg')).

MCMC diagnostics
                              mcse Rhat n_eff
(Intercept)                   0.0  1.0  18279
t1_dragonKills:t2_dragonKills 0.0  1.0  21144
sigma                         0.0  1.0  18662
mean_PPD                      0.0  1.0  18790
log-posterior                 0.0  1.0   9402

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
summary(int2)

Model Info:
 function:     stan_glm
 family:       gaussian [identity]
 formula:      gameDuration ~ t1_baronKills:t2_baronKills
 algorithm:    sampling
 sample:       20000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 45214
 predictors:   2

Estimates:
                              mean   sd     10%    50%    90% 
(Intercept)                 1884.0    1.9 1881.6 1884.0 1886.4
t1_baronKills:t2_baronKills  466.7    4.8  460.4  466.6  472.8
sigma                        390.0    1.3  388.4  390.0  391.7

Fit Diagnostics:
           mean   sd     10%    50%    90% 
mean_PPD 1929.8    2.6 1926.5 1929.8 1933.1

The mean_ppd is the sample average posterior predictive distribution of the outcome variable (for details see help('summary.stanreg')).

MCMC diagnostics
                            mcse Rhat n_eff
(Intercept)                 0.0  1.0  17022
t1_baronKills:t2_baronKills 0.0  1.0  20014
sigma                       0.0  1.0  18691
mean_PPD                    0.0  1.0  18228
log-posterior               0.0  1.0   9914

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).

Summary stats look promising with both values not containing 0 for their 95% CI showing they are significant and can be useful

# Extract the posterior samples
posterior_samples <- as.matrix(int)
posterior_samples2 <- as.matrix(int2)

# Plot the posterior distribution of the interaction term
mcmc_hist(posterior_samples, pars = c("t1_dragonKills:t2_dragonKills"))


mcmc_hist(posterior_samples2, pars = c("t1_baronKills:t2_baronKills"))

The predictors don’t look skewed either, looks good.

We can canclude that there may be a potential interaction between dragon kills and baron kills for each team

We can utilize this info later on upon building a better model

Main Model

Let’s assess how much getting a jump start in the game does for determining the length of the game

main <- stan_glm(gameDuration ~ winner + firstBlood + firstTower + firstDragon + firstBaron,
  data = lol_data, family = gaussian, 
  prior_intercept = normal(1800, 150, autoscale = TRUE),
  prior = normal(0, 2.5, autoscale = TRUE), 
  prior_aux = exponential(1, autoscale = TRUE),
  chains = 4, iter = 5000*2, seed = 84735)

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 1.8e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.18 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 1: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 1: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 1: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 1: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 1: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 1: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 1: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 1: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 1: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 1: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 1: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 1.141 seconds (Warm-up)
Chain 1:                14.816 seconds (Sampling)
Chain 1:                15.957 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 2e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.2 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 2: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 2: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 2: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 2: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 2: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 2: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 2: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 2: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 2: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 2: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 2: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 1.289 seconds (Warm-up)
Chain 2:                14.216 seconds (Sampling)
Chain 2:                15.505 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 1.8e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.18 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 3: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 3: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 3: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 3: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 3: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 3: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 3: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 3: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 3: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 3: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 3: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 1.089 seconds (Warm-up)
Chain 3:                14.186 seconds (Sampling)
Chain 3:                15.275 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.8e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.18 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 4: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 4: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 4: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 4: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 4: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 4: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 4: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 4: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 4: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 4: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 4: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 1.117 seconds (Warm-up)
Chain 4:                14.54 seconds (Sampling)
Chain 4:                15.657 seconds (Total)
Chain 4: 
prior_summary(main) 
Priors for model 'main' 
------
Intercept (after predictors centered)
  Specified prior:
    ~ normal(location = 1800, scale = 150)
  Adjusted prior:
    ~ normal(location = 1800, scale = 64093)

Coefficients
  Specified prior:
    ~ normal(location = [0,0,0,...], scale = [2.5,2.5,2.5,...])
  Adjusted prior:
    ~ normal(location = [0,0,0,...], scale = [2136.54,2137.02,2137.26,...])

Auxiliary (sigma)
  Specified prior:
    ~ exponential(rate = 1)
  Adjusted prior:
    ~ exponential(rate = 0.0023)
------
See help('prior_summary.stanreg') for more details
mcmc_trace(main, size = .1)

Chains look normal

rhat(main)
 (Intercept)      winner2  firstBlood2  firstTower2 firstDragon1 
   1.0001252    1.0000866    0.9999479    0.9999554    1.0000860 
firstDragon2  firstBaron1  firstBaron2        sigma 
   1.0001157    1.0000559    0.9999221    1.0000304 
neff_ratio(main)
 (Intercept)      winner2  firstBlood2  firstTower2 firstDragon1 
     0.56465      1.06070      1.40485      1.23750      0.54275 
firstDragon2  firstBaron1  firstBaron2        sigma 
     0.54175      1.05630      1.05235      1.12255 

Rhat looks good, some of the neff ratio values are still high but acceptable for now

tidy(main, effects = c("fixed", "aux"),
     conf.int = TRUE, conf.level = 0.95)

From the tidy output, it looks like winner, firstBlood, and firstTower lose their significance when adding other predictors since their 95% CI range includes 0. We’ll use this info later for refining our model

newdata <- data.frame(winner = factor(1, levels = levels(lol_data$winner)),
                      firstBlood = factor(2, levels = levels(lol_data$firstBlood)),
                      firstTower = factor(2, levels = levels(lol_data$firstTower)),
                      firstDragon = factor(2, levels = levels(lol_data$firstDragon)),
                      firstBaron = factor(2, levels = levels(lol_data$firstBaron)))

main_predict <- posterior_predict(
  main, 
  newdata = newdata)
mcmc_areas(main_predict) +  xlab("Game Duration") +
  ggtitle('Predictive distribution of a League of Legends game where first blood, tower, dragon, baron was team 2 and team 1 won') +
  theme(plot.title = element_text(size = 7))

Here’s an example output of where a game duration may lie if team 1 wins despite being behind from the start. The game is a little later than normal, but to be expected as the team would need time to make the comeback in the first place. However, I believe this is undershooting the true time we could expect from this scenario. We’ll verify if this is true after refining our model

More Predictors

We can add some numerical variables to see how much adding the amount of each objective a team has taken to see how it affects the game duration

ext_main <- stan_glm(gameDuration ~ winner + firstBlood + firstTower + firstDragon + firstBaron + t1_towerKills + t1_inhibitorKills + t1_baronKills + t1_dragonKills + t2_towerKills + t2_inhibitorKills + t2_baronKills + t2_dragonKills,
  data = lol_data, family = gaussian, 
  prior_intercept = normal(1800, 150, autoscale = TRUE),
  prior = normal(0, 2.5, autoscale = TRUE), 
  prior_aux = exponential(1, autoscale = TRUE),
  chains = 4, iter = 5000*2, seed = 84735)

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 1.9e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.19 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 1: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 1: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 1: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 1: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 1: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 1: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 1: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 1: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 1: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 1: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 1: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 1.543 seconds (Warm-up)
Chain 1:                14.226 seconds (Sampling)
Chain 1:                15.769 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.9e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.19 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 2: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 2: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 2: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 2: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 2: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 2: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 2: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 2: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 2: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 2: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 2: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 1.671 seconds (Warm-up)
Chain 2:                14.491 seconds (Sampling)
Chain 2:                16.162 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 2.1e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.21 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 3: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 3: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 3: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 3: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 3: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 3: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 3: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 3: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 3: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 3: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 3: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 1.822 seconds (Warm-up)
Chain 3:                14.543 seconds (Sampling)
Chain 3:                16.365 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.6e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.16 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 4: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 4: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 4: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 4: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 4: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 4: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 4: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 4: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 4: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 4: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 4: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 1.593 seconds (Warm-up)
Chain 4:                14.438 seconds (Sampling)
Chain 4:                16.031 seconds (Total)
Chain 4: 
prior_summary(ext_main) 
Priors for model 'ext_main' 
------
Intercept (after predictors centered)
  Specified prior:
    ~ normal(location = 1800, scale = 150)
  Adjusted prior:
    ~ normal(location = 1800, scale = 64093)

Coefficients
  Specified prior:
    ~ normal(location = [0,0,0,...], scale = [2.5,2.5,2.5,...])
  Adjusted prior:
    ~ normal(location = [0,0,0,...], scale = [2136.54,2137.02,2137.26,...])

Auxiliary (sigma)
  Specified prior:
    ~ exponential(rate = 1)
  Adjusted prior:
    ~ exponential(rate = 0.0023)
------
See help('prior_summary.stanreg') for more details
mcmc_trace(ext_main, size = .1)

Traces look good

rhat(ext_main)
      (Intercept)           winner2       firstBlood2       firstTower2 
        0.9999612         0.9999395         0.9999895         0.9998515 
     firstDragon1      firstDragon2       firstBaron1       firstBaron2 
        1.0000056         1.0000145         1.0001244         1.0001247 
    t1_towerKills t1_inhibitorKills     t1_baronKills    t1_dragonKills 
        0.9999698         1.0000633         1.0001761         0.9998990 
    t2_towerKills t2_inhibitorKills     t2_baronKills    t2_dragonKills 
        0.9998515         0.9999585         1.0003134         0.9999253 
            sigma 
        0.9998820 
neff_ratio(ext_main)
      (Intercept)           winner2       firstBlood2       firstTower2 
          0.58290           0.79920           2.05680           1.44595 
     firstDragon1      firstDragon2       firstBaron1       firstBaron2 
          0.48310           0.48375           0.66920           0.65460 
    t1_towerKills t1_inhibitorKills     t1_baronKills    t1_dragonKills 
          0.71785           1.02960           0.65910           0.94240 
    t2_towerKills t2_inhibitorKills     t2_baronKills    t2_dragonKills 
          0.74235           1.01930           0.65770           0.91395 
            sigma 
          1.16840 

Rhat looks good, some neff ratios (i.e. firstBlood, firstTower) are now too high to keep and will need to be handled later

tidy(ext_main, effects = c("fixed", "aux"),
     conf.int = TRUE, conf.level = 0.95)

Here, the predictors winner, firstBlood, and firstTower are not significant due to their 95% CI including 0

# dataframe with the specified values
newdata_ext <- data.frame(
  winner = factor(1, levels = levels(lol_data$winner)),
  firstBlood = factor(2, levels = levels(lol_data$firstBlood)),
  firstTower = factor(2, levels = levels(lol_data$firstTower)),
  firstDragon = factor(2, levels = levels(lol_data$firstDragon)),
  firstBaron = factor(2, levels = levels(lol_data$firstBaron)),
  t1_towerKills = 7,
  t1_inhibitorKills = 2,
  t1_baronKills = 2,
  t1_dragonKills = 2,
  t2_towerKills = 3,
  t2_inhibitorKills = 2,
  t2_baronKills = 1,
  t2_dragonKills = 3
)

ext_main_predict <- posterior_predict(ext_main, newdata = newdata_ext)

mcmc_areas(ext_main_predict) +  
  xlab("Game Duration") +
  ggtitle('Predictive distribution of a League of Legends game where first blood, tower, dragon, baron was team 2 and team 1 won, with additional predictors') +
  theme(plot.title = element_text(size = 8))

After adding a few more variables into play we can see the game gets longer (and expectedly so). Team 1 comes from behind but ends up getting 2 barons and 7 towers making us believe this game will have to go on for a while for the comeback to truly be complete

Refined Model

Let’s now remove the high neff ratio terms from the extended model (i.e. > 1) and include the interaction terms we deemed useful beforehand

ext_main_int <- stan_glm(gameDuration ~ firstDragon + firstBaron + t1_baronKills + t1_dragonKills + t2_baronKills + t2_dragonKills + t1_baronKills:t2_baronKills + t1_dragonKills:t2_dragonKills,
  data = lol_data, family = gaussian, 
  prior_intercept = normal(1800, 150, autoscale = TRUE),
  prior = normal(0, 2.5, autoscale = TRUE), 
  prior_aux = exponential(1, autoscale = TRUE),
  chains = 4, iter = 5000*2, seed = 84735)

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 1.7e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.17 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 1: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 1: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 1: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 1: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 1: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 1: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 1: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 1: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 1: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 1: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 1: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 1.363 seconds (Warm-up)
Chain 1:                14.396 seconds (Sampling)
Chain 1:                15.759 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.6e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.16 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 2: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 2: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 2: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 2: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 2: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 2: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 2: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 2: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 2: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 2: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 2: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 1.365 seconds (Warm-up)
Chain 2:                14.24 seconds (Sampling)
Chain 2:                15.605 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 1.4e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.14 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 3: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 3: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 3: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 3: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 3: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 3: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 3: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 3: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 3: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 3: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 3: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 1.761 seconds (Warm-up)
Chain 3:                14.227 seconds (Sampling)
Chain 3:                15.988 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.8e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.18 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 10000 [  0%]  (Warmup)
Chain 4: Iteration: 1000 / 10000 [ 10%]  (Warmup)
Chain 4: Iteration: 2000 / 10000 [ 20%]  (Warmup)
Chain 4: Iteration: 3000 / 10000 [ 30%]  (Warmup)
Chain 4: Iteration: 4000 / 10000 [ 40%]  (Warmup)
Chain 4: Iteration: 5000 / 10000 [ 50%]  (Warmup)
Chain 4: Iteration: 5001 / 10000 [ 50%]  (Sampling)
Chain 4: Iteration: 6000 / 10000 [ 60%]  (Sampling)
Chain 4: Iteration: 7000 / 10000 [ 70%]  (Sampling)
Chain 4: Iteration: 8000 / 10000 [ 80%]  (Sampling)
Chain 4: Iteration: 9000 / 10000 [ 90%]  (Sampling)
Chain 4: Iteration: 10000 / 10000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 1.409 seconds (Warm-up)
Chain 4:                14.229 seconds (Sampling)
Chain 4:                15.638 seconds (Total)
Chain 4: 
prior_summary(ext_main_int)
Priors for model 'ext_main_int' 
------
Intercept (after predictors centered)
  Specified prior:
    ~ normal(location = 1800, scale = 150)
  Adjusted prior:
    ~ normal(location = 1800, scale = 64093)

Coefficients
  Specified prior:
    ~ normal(location = [0,0,0,...], scale = [2.5,2.5,2.5,...])
  Adjusted prior:
    ~ normal(location = [0,0,0,...], scale = [2136.66,2136.42,2289.92,...])

Auxiliary (sigma)
  Specified prior:
    ~ exponential(rate = 1)
  Adjusted prior:
    ~ exponential(rate = 0.0023)
------
See help('prior_summary.stanreg') for more details
mcmc_trace(ext_main_int, size = .1)

So far so good

rhat(ext_main_int)
                  (Intercept)                  firstDragon1 
                    1.0003440                     1.0003428 
                 firstDragon2                   firstBaron1 
                    1.0003775                     1.0000760 
                  firstBaron2                 t1_baronKills 
                    1.0002465                     1.0000761 
               t1_dragonKills                 t2_baronKills 
                    0.9998796                     1.0002153 
               t2_dragonKills   t1_baronKills:t2_baronKills 
                    0.9999051                     1.0002525 
t1_dragonKills:t2_dragonKills                         sigma 
                    1.0000077                     1.0000297 

Rhat looks satisfactory for all predictors

neff_ratio(ext_main_int)
                  (Intercept)                  firstDragon1 
                      0.58795                       0.50540 
                 firstDragon2                   firstBaron1 
                      0.50105                       0.56190 
                  firstBaron2                 t1_baronKills 
                      0.53910                       0.53865 
               t1_dragonKills                 t2_baronKills 
                      0.67185                       0.53005 
               t2_dragonKills   t1_baronKills:t2_baronKills 
                      0.67705                       0.66965 
t1_dragonKills:t2_dragonKills                         sigma 
                      0.82210                       1.12835 

Neff ratios are much more reasonable now and a big improvement from prior models with no value being > .90

tidy(ext_main_int, effects = c("fixed", "aux"),
     conf.int = TRUE, conf.level = 0.95)

Every value looks significant from the 95% CI (even the interaction terms!!) due to none of them including 0.

# dataframe with the specified values
newdata_ext_int <- data.frame(
  firstDragon = factor(2, levels = levels(lol_data$firstDragon)),
  firstBaron = factor(2, levels = levels(lol_data$firstBaron)),
  t1_baronKills = 2,
  t1_dragonKills = 2,
  t2_baronKills = 1,
  t2_dragonKills = 3
)

ext_main_int_predict <- posterior_predict(ext_main_int, newdata = newdata_ext_int)

mcmc_areas(ext_main_int_predict) +  
  xlab("Game Duration") +
  ggtitle('Predictive distribution of a League of Legends game where first dragon, baron was team 2 and team 1 had more baron kills with interaction') +
  theme(plot.title = element_text(size = 8))

The games seem to drag on even longer with this new model. This is expected since the dragon and baron respawn times are quite long so, as said before, the comeback would realistically take longer than average.

Lets do some model comparisons to verify which model is best

Model Comparisons

PP Check

pp_check(main, nreps = 50) + xlab("Game Duration") +
  ggtitle('Main effects model')

The main model gets a decent amount of area, yet it starts moving too far right and undershoots at the peak

pp_check(ext_main, nreps = 50) + xlab("Game Duration") +
  ggtitle('Extended Main effects model')

The extended model is objectively better than the main model, but still undershoots area at the peak

pp_check(ext_main_int, nreps = 50) + xlab("Game Duration") +
  ggtitle('Refined Interaction effects model')

The interaction model is very similar to the extended model which is better than the main model.

The ppchecks show most of the are being covered for the last 2 models with the interaction model doing better around the peak by a small margin

10 Fold Cross-Validations

test_sample <- lol_data %>% head(10000)
nrow(test_sample)
[1] 10000
set.seed(84735)

p_main <- prediction_summary(model = main, data = test_sample)
p_ext_main <- prediction_summary(model = ext_main, data = test_sample)
p_ext_main_int <- prediction_summary(model = ext_main_int, data = test_sample)
rbind(p_main, p_ext_main, p_ext_main_int)

These are the raw MAE values based on a sample of 10000 observations from the data, we can use these to determine the behavior and bias of each model based on their cross-validation results

set.seed(84735)

cv_main <- prediction_summary_cv(
  model = main, data = lol_data, k = 10)
rbind(cv_main$cv, cv_extend$cv, cv_interact$cv)

Loo Diagnostics

set.seed(34521)
main_elpd <- loo(main)
ext_main_elpd <- loo(ext_main)
ext_main_interact_elpd <- loo(ext_main_int)

main_elpd$estimates
              Estimate          SE
elpd_loo -3.323804e+05 174.6839030
p_loo     9.093789e+00   0.1196568
looic     6.647608e+05 349.3678059
ext_main_elpd$estimates
             Estimate          SE
elpd_loo -309859.5959 173.9985196
p_loo         19.2336   0.2843918
looic     619719.1918 347.9970392
ext_main_interact_elpd$estimates
              Estimate          SE
elpd_loo -314197.32595 179.0596124
p_loo         13.56236   0.2282117
looic     628394.65191 358.1192249
c(main_elpd$estimates[1], ext_main_elpd$estimates[1], ext_main_interact_elpd$estimates[1])
[1] -332380.4 -309859.6 -314197.3
loo_compare(main_elpd, ext_main_elpd, ext_main_interact_elpd)
             elpd_diff se_diff 
ext_main          0.0       0.0
ext_main_int  -4337.7      96.8
main         -22520.8     178.3

The extended model fairs better than the interaction model when comparing their ELPD and MAE. The interaction model does 4337.6 points “worse” for ELPD and is around 16 points higher in its MAE. However, something to consider here is the idea of overfitting and how more predictors and affect the model overall and skew our metrics.

Despite the interaction model covering more area, the MAE and ELPD are both worse. This may be due to these values being inflated by the larger number of predictors in the extended model. Furthermore, the extended model contains numerous values with higher than normal Neff Ratios which is a cause for concern about the validity of the model along with predictors that were not significant.

Despite having less predictors, the interaction model garners more area in its predictive posterior distribution while having a similar MAE and good ELPD score too (-309859.7 vs 314197.3) which is a minor (~1%) difference given the size of these values. However, these interaction terms could also be having an adverse effect on the model leading to these greater errors.

Overfitting

rbind(p_main, p_ext_main, p_ext_main_int) 
rbind(cv_main$cv, cv_extend$cv, cv_interact$cv)

Based on the difference between the raw MAE values and the cross-validation MAE values, overfitting does not seem to pose a threat to these models, however we can only truly know this if we are given new data entirely to test the models. Furthermore, the extended model does have a larger (though mostly negligible due to how small it is) difference from the cross-validation MAE compared to the other models.

What is undoubtedly clear is that adding numerical predictors to the model does help the predictions overall and is shown with both the extended and interaction model fairing better than the main model in every aspect.

Regression Inference

Let’s see how the 2 best models fair when conducting a quick hypothesis test to see the Posterior probability of a game around 40 minutes long

We’ll use the predictive posterior models we created and visualized earlier to conduct the tests

\[ H_0: \pi \geq 2500 \]

\[ H_a: \pi < 2500 \]

# Extract the posterior samples
posterior_samples <- ext_main_int_predict

p_H0 <- mean(posterior_samples >= 2500)

p_Ha <- mean(posterior_samples < 2500)

# Print the results
cat("Posterior probability of H0 (π ≥ 2500):", p_H0)
Posterior probability of H0 (π ≥ 2500): 0.9364
cat("Posterior probability of Ha (π < 2500):", p_Ha)
Posterior probability of Ha (π < 2500): 0.0636
# Extract the posterior samples
posterior_samples <- ext_main_predict

p_H0 <- mean(posterior_samples >= 2500)

p_Ha <- mean(posterior_samples < 2500)

# Print the results
cat("Posterior probability of H0 (π ≥ 2500):", p_H0)
Posterior probability of H0 (π ≥ 2500): 0.42085
cat("Posterior probability of Ha (π < 2500):", p_Ha)
Posterior probability of Ha (π < 2500): 0.57915

Though the two models seemingly predict the actual distribution of the data well, the 2 models have very different outcomes when conducting the test.

The extended model favors the alternate hypothesis while the interaction model clearly favors the null hypothesis. This both coincides with what we saw before from the visualizations and tells us that the extended model favors games that are shorter while the interaction model expects games like this to take longer.

This is also something one should consider when choosing the model. Whether or not the predictions themselves seem realistic given the scenario/circumstances.

To back up what I’m saying, Baron Nashor spawns at 20 minutes and respawns every 6 minutes. Since 3 Barons are killed in both scenarios, the game would have to be at least 38 minutes and only if the teams kill the Baron IMMEDIATELY (which is usually never the case). Therefore, one would expect majority of the area to lie within the ~40 minute range at the very least for when the game would end.

Conclusion

In conclusion, it is a choice between whether one wants to take the chance of playing with a model that may be susceptible to overfitting for the sake of potentially less error or a slightly higher error model that has interactions but would be less likely to be susceptible to overfitting.

Personally, I say that the refined interaction model is the best model to use for predicting the game duration of a LOL match given our data due to its comparable MAE along with better posterior predictive abilities and lack of evidence for overfitting. Furthermore, based on my experience and game rules, the predictive values it comes up with end up being much more realistic in the grand scheme of things and with other factors such as respawn time considered.

References

Honor Pledge

On my honor, I have neither received nor given any unauthorized assistance on this project

Signed: Thomas Christo (tjc260)

LS0tDQp0aXRsZTogIkZpbmFsIFByb2plY3QgTE9MIg0KZGF0ZTogIkR1ZTogV2VkbmVzZGF5IDEyLzE5IGF0IDExOjU5YW0sIENhbnZhcyBzdWJtaXNzaW9uIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgVGhlIERhdGENCg0KVGhlIGRhdGFzZXQgSSBoYXZlIGNob3NlbiB0byBjb25kdWN0IG15IHJlZ3Jlc3Npb24gYW5hbHlzaXMgaXMgdGhlIFsoTG9MKSBMZWFndWUgb2YgTGVnZW5kcyBSYW5rZWQgR2FtZXMgZGF0YXNldF0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9kYXRhc25hZWsvbGVhZ3VlLW9mLWxlZ2VuZHMvP3NlbGVjdD1nYW1lcy5jc3YpLiBUaGlzIGRhdGEgbG9va3MgYXQgcmFua2VkIExlYWd1ZSBvZiBMZWdlbmRzIGdhbWVzIHBsYXllZCBkdXJpbmcgc2Vhc29uIDkgaW4gdGhlIEVVVyByZWdpb24uIFRoaXMgZGF0YXNldCB3YXMgY29sbGVjdGVkIHVzaW5nIHRoZSBSaW90IEdhbWVzIEFQSSwgd2hpY2ggbWFrZXMgaXQgZWFzeSB0byBsb29rIHVwIGFuZCBjb2xsZWN0IGluZm9ybWF0aW9uIG9uIGEgdXNlcnMgcmFua2VkIGhpc3RvcnkgYW5kIGNvbGxlY3QgdGhlaXIgZ2FtZXMuDQoNCmBgYHtyIHNldHVwLCByZXN1bHRzPUYsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBMb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHJzdGFuYXJtKQ0KbGlicmFyeShiYXllc3Bsb3QpDQpsaWJyYXJ5KGJheWVzcnVsZXMpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkodGlkeWJheWVzKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoYnJvb20ubWl4ZWQpDQpsaWJyYXJ5KGludGVyYWN0aW9ucykNCmBgYA0KDQpUbyBwcmVmYWNlIHRoaXMgYW5hbHlzaXMsIG9uZSBtdXN0IGZpcnN0IHVuZGVyc3RhbmQgdGhlIGdhbWUgb2YgTGVhZ3VlIG9mIExlZ2VuZHMsIG9yIExPTCBmb3Igc2hvcnQuIEluIExPTCwgdHdvIDUgcGVyc29uIHRlYW1zIGZpZ2h0IHRvIGRlc3Ryb3kgZWFjaCBvdGhlcnMgbmV4dXMgYW5kIGdldCB0byBjaG9vc2UgYW5kIGJhbiBhIGNoYW1waW9uLCBlYWNoIHdpdGggYSBkaWZmZXJlbnQgc2V0IG9mIHNraWxscyBhbmQgYWJpbGl0aWVzLCBhbmQgMiBzdW1tb25lciBzcGVsbHMgdG8gZG8gc28uIEFsb25nIHRoZSB3YXksIHRoZSBwbGF5ZXJzIG11c3QgYm90aCBmaWdodCBlYWNoIG90aGVyLCBkZXN0cm95IG9iamVjdGl2ZXMgc3VjaCBhcyB0b3dlcnMgYW5kIGluaGliaXRvcnMsIGFuZCBzdHJhdGVnaXplIGVmZmVjdGl2ZWx5IHRvIHJlYWNoIHRoZSBvcHBvbmVudCdzIG5leHVzLiBUaGVyZSBhcmUgYWxzbyBzZXZlcmFsIG1vbnN0ZXJzIHRoYXQgdGVhbXMgY2FuIGRlZmVhdCB0byBidWZmIHRoZW0gYXMgd2VsbCwgc3VjaCBhcyBSaWZ0IEhlcmFsZCwgQmFyb24gTmFzaG9yLCBhbmQgYW4gYXNzb3J0ZWQgc2V0IG9mIERyYWdvbnMuDQoNCldlIHdpbGwgdXNlIGBsb2xfZGF0YWAgdG8gYnVpbGQgdmFyaW91cyBtb2RlbHMgb2YgTGVhZ3VlIG9mIExlZ2VuZHMgYGdhbWVEdXJhdGlvbmAuIFRocm91Z2hvdXQsIHdlJ2xsIHV0aWxpemUgd2Vha2x5IGluZm9ybWF0aXZlIHByaW9ycyBhbmQgYSBiYXNpYyB1bmRlcnN0YW5kaW5nIHRoYXQgTE9MIGdhbWVzIHVzdWFsbHkgYXJlIFszMCBtaW51dGVzXShodHRwczovL3d3dy5sZWFndWVvZmdyYXBocy5jb20vc3RhdHMvZ2FtZS1kdXJhdGlvbnMpIGJ1dCBjYW4gcmFuZ2UgZnJvbSAyNS0zNSBtaW51dGVzLiBXZSB3aWxsIGFzc2VzIGFuIGFycmF5IG9mIGRpZmZlcmVudCBwcmVkaWN0b3JzIGFuZCBjb21iaW5hdGlvbnMgdG8gZGV0ZXJtaW5lIHdoYXQgY2FuIHByb3ZpZGUgdXMgdGhlIGJlc3QgZml0IGZvciBwcmVkaWN0aW5nIHRoZSBnYW1lIGR1cmF0aW9uIG9mIGEgTGVhZ3VlIG9mIExlZ2VuZHMgZ2FtZS4gQSBiYXNlIGNyaXRlcmlhIGZvciB0aGVzZSBnYW1lcyBpcyB0aGVyZSBhdCBsZWFzdCBuZWVkcyB0byBiZSBvbmUgdG93ZXIsIG9uZSBpbmhpYml0b3IsIGFuZCBvbmUgY2hhbXBpb24ga2lsbCBmb3IgZWFjaCBnYW1lLiBUaGlzIGlzIHRvIG1pdGlnYXRlIHRoZSBhbW91bnQgb2YgZ2FtZXMgdGhhdCB0ZWFtcyBtYXkgaGF2ZSBmb3JmZWl0ZWQgZHVlIHRvIGEgcGxheWVyIGxlYXZpbmcgb3IgdHJvbGxpbmcuDQoNCmBgYHtyfQ0KbG9sX2RhdGEgPC0gcmVhZC5jc3YoImdhbWVzLmNzdiIsDQogICAgICAgICAgICAgICAgICAgIHNlcD0iLCIsDQogICAgICAgICAgICAgICAgICAgIG5hLnN0cmluZ3M9YygiIiwiICIsIk5BIiwiTi9BIikNCikNCmhlYWQobG9sX2RhdGEpDQpgYGANCg0KIyMgU2V0dGluZyBVcCB0aGUgRGF0YQ0KDQpUcmFuc2Zvcm1pbmcgZGF0YSB0byBmaXQgYmFzZWxpbmUgb2YgYXQgbGVhc3Qgb25lIGtpbGwsIHRvd2VyLCBhbmQgaW5oaWJpdG9yDQoNCmBgYHtyfQ0KbG9sX2RhdGEgPC0gbG9sX2RhdGFbIShsb2xfZGF0YSRmaXJzdEJsb29kID09IDAgfCBsb2xfZGF0YSRmaXJzdFRvd2VyID09IDAgfCBsb2xfZGF0YSRmaXJzdEluaGliaXRvciA9PSAwKSwgXQ0KYGBgDQoNCmBgYHtyfQ0KbnJvdyhsb2xfZGF0YSkNCmBgYA0KDQpgYGB7cn0NCmNvbG5hbWVzKGxvbF9kYXRhKQ0KYGBgDQoNCldlIGNhbiBjbGVhbiB1cCBvdXIgZGF0YSBieSBvbWl0dGluZyB1bm5lY2Vzc2FyeSBjb2x1bW5zDQoNCmBgYHtyfQ0KbG9sX2RhdGEgPC0gc3Vic2V0KGxvbF9kYXRhLCBzZWxlY3QgPSAtYyh0Ml9iYW4xLCB0Ml9iYW4yLCB0Ml9iYW4zLCB0Ml9iYW40LCB0Ml9iYW41LCB0MV9iYW4xLCB0MV9iYW4yLCB0MV9iYW4zLCB0MV9iYW40LCB0MV9iYW41LCB0MV9jaGFtcDFfc3VtMSwgdDFfY2hhbXAyX3N1bTEsIHQxX2NoYW1wM19zdW0xLCB0MV9jaGFtcDRfc3VtMSwgdDFfY2hhbXA1X3N1bTEsIHQyX2NoYW1wNV9zdW0xLCB0Ml9jaGFtcDFfc3VtMSwgdDJfY2hhbXAyX3N1bTEsIHQyX2NoYW1wM19zdW0xLCB0Ml9jaGFtcDRfc3VtMSwgdDJfY2hhbXA1X3N1bTEsIHQxX2NoYW1wMV9zdW0yLCB0MV9jaGFtcDJfc3VtMiwgdDFfY2hhbXAzX3N1bTIsIHQxX2NoYW1wNF9zdW0yLCB0MV9jaGFtcDVfc3VtMiwgdDJfY2hhbXAxX3N1bTIsIHQyX2NoYW1wMV9zdW0yLCB0Ml9jaGFtcDJfc3VtMiwgdDJfY2hhbXAzX3N1bTIsIHQyX2NoYW1wNF9zdW0yLCB0Ml9jaGFtcDVfc3VtMiwgdDFfY2hhbXAxaWQsIHQxX2NoYW1wMmlkLCB0MV9jaGFtcDNpZCwgdDFfY2hhbXA0aWQsIHQxX2NoYW1wNWlkLCB0Ml9jaGFtcDFpZCwgdDJfY2hhbXAyaWQsIHQyX2NoYW1wM2lkLCB0Ml9jaGFtcDRpZCwgdDJfY2hhbXA1aWQpKQ0KDQpjb2xuYW1lcyhsb2xfZGF0YSkNCmBgYA0KDQpXZSBjYW4gYWxzbyBjb3ZlcnQgc29tZSBvZiB0aGUgY29sdW1ucyB0byBmYWN0b3JzIGZvciB0ZWFtIDEgYW5kIHRlYW0gMiBmb3IgZWFzZSBvZiB2aXN1YWxpemF0aW9uIGFuZCBpbnRlcnByZXRhdGlvbg0KDQpgYGB7cn0NCmxvbF9kYXRhJHdpbm5lciA8LSBhcy5mYWN0b3IobG9sX2RhdGEkd2lubmVyKQ0KbG9sX2RhdGEkZmlyc3RCbG9vZCA8LSBhcy5mYWN0b3IobG9sX2RhdGEkZmlyc3RCbG9vZCkNCmxvbF9kYXRhJGZpcnN0VG93ZXIgPC0gYXMuZmFjdG9yKGxvbF9kYXRhJGZpcnN0VG93ZXIpDQpsb2xfZGF0YSRmaXJzdEluaGliaXRvciA8LSBhcy5mYWN0b3IobG9sX2RhdGEkZmlyc3RJbmhpYml0b3IpDQpsb2xfZGF0YSRmaXJzdEJhcm9uIDwtIGFzLmZhY3Rvcihsb2xfZGF0YSRmaXJzdEJhcm9uKQ0KbG9sX2RhdGEkZmlyc3REcmFnb24gPC0gYXMuZmFjdG9yKGxvbF9kYXRhJGZpcnN0RHJhZ29uKQ0KbG9sX2RhdGEkZmlyc3RSaWZ0SGVyYWxkIDwtIGFzLmZhY3Rvcihsb2xfZGF0YSRmaXJzdFJpZnRIZXJhbGQpDQpgYGANCg0KYGBge3J9DQpoZWFkKGxvbF9kYXRhKQ0KYGBgDQoNCiMjIFN1bW1hcnkgQW5hbHlzaXMNCg0KSGVyZSBhcmUgc29tZSB2aXN1YWxpemF0aW9ucyBhbmQgc3RhdGlzdGljcyB0byBoZWxwIGJldHRlciB1bmRlcnN0YW5kIG91ciBkYXRhIGFuZCB3aGF0IHdlIGFyZSB3b3JraW5nIHdpdGguDQoNCmBgYHtyfQ0Kc3VtbWFyeShsb2xfZGF0YSkNCmBgYA0KDQpTb21lIGNvbmNsdXNpb25zIHdlIGNhbiBtYWtlIGhlcmU6DQoNCi0gICBUaGUgbWVhbiBnYW1lIHRpbWUgaXMgYXJvdW5kIDE5MzAgc2Vjb25kcyAocm91Z2hseSAzMiBtaW51dGVzKQ0KDQotICAgR2FtZSBkdXJhdGlvbiBsaWVzIG1vc3RseSBiZXR3ZWVuIDE2MzIgYW5kIDIxOTYNCg0KLSAgIFRlYW0gMSBoYXMgd29uIG1vcmUgZ2FtZXMsIGhhcyBtb3JlIGZpcnN0IGJsb29kcywgdG93ZXJzLCBpbmhpYml0b3JzLCBhbmQgcmlmdCBoZXJhbGRzDQoNCi0gICBUZWFtIDIgaGFzIG1vcmUgZmlyc3QgYmFyb25zIGFuZCBkcmFnb25zLg0KDQotICAgVGVhbSAxJ3MgdG93ZXIsIGluaGliaXRvciwgYW5kIGhlcmFsZCBraWxscyBhcmUgaGlnaGVyIHRoYW4gVGVhbSAyJ3Mgd2hpY2ggaXMgcmVmbGVjdGVkIGZyb20gdGhlbSBnZXR0aW5nIHRoZSBmaXJzdCBvZiBlYWNoIG1vcmUgb2Z0ZW4gdGhhbiBub3QuDQoNCi0gICBUZWFtIDIncyBkcmFnb24gYW5kIGJhcm9uIGtpbGxzIGFyZSBoaWdoZXIgdGhhbiBUZWFtIDEncyB3aGljaCBpcyByZWZsZWN0ZWQgZnJvbSB0aGVtIGdldHRpbmcgdGhlIGZpcnN0IG9mIGVhY2ggbW9yZSBvZnRlbiB0aGFuIG5vdC4NCg0KIyMgVmlzdWFsaXphdGlvbnMNCg0KV2UgY2FuIHVzZSBnZ3Bsb3QgZG8gdmlzdWFsaXplIHNvbWUgb2Ygb3VyIGZpbmRpbmdzDQoNCiMjIyBSZXNwb25zZSBEaXN0cmlidXRpb24NCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gZ2FtZUR1cmF0aW9uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDYwLCBmaWxsID0gImJsdWUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh4ID0gIkdhbWUgRHVyYXRpb24gKHNlY29uZHMpIiwgeSA9ICJGcmVxdWVuY3kiLCB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgR2FtZSBEdXJhdGlvbiIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KR2FtZSBkdXJhdGlvbiBzZWVtcyB0byBiZSBhIGJpdCByaWdodC1za2V3ZWQNCg0KIyMjIEZpcnN0IENvbXBhcmlzb25zDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IHdpbm5lciwgZmlsbCA9IHdpbm5lcikpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OSkpICsNCiAgICAgIGxhYnMoeSA9ICJXaW5uZXJzIikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSBmaXJzdEJsb29kLCBmaWxsID0gZmlyc3RCbG9vZCkpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OSkpICsNCiAgICAgIGxhYnMoeSA9ICJGaXJzdCBCbG9vZHMiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IGZpcnN0VG93ZXIsIGZpbGwgPSBmaXJzdFRvd2VyKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT05KSkgKw0KICAgICAgbGFicyh5ID0gIkZpcnN0IFRvd2VycyIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gZmlyc3RJbmhpYml0b3IsIGZpbGwgPSBmaXJzdEluaGliaXRvcikpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OSkpICsNCiAgICAgIGxhYnMoeSA9ICJGaXJzdCBJbmhpYml0b3JzIikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSBmaXJzdERyYWdvbiwgZmlsbCA9IGZpcnN0RHJhZ29uKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT05KSkgKw0KICAgICAgbGFicyh5ID0gIkZpcnN0IERyYWdvbnMiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IGZpcnN0UmlmdEhlcmFsZCwgZmlsbCA9IGZpcnN0UmlmdEhlcmFsZCkpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9OSkpICsNCiAgICAgIGxhYnMoeSA9ICJGaXJzdCBSaWZ0IEhlcmFsZHMiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IGZpcnN0QmFyb24sIGZpbGwgPSBmaXJzdEJhcm9uKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT05KSkgKw0KICAgICAgbGFicyh5ID0gIkZpcnN0IEJhcm9ucyIpDQpgYGANCg0KVGhlc2UgYWxsIGFyZSBjb25zaXN0ZW50IHdpdGggb3VyIHByZXZpb3VzIHN1bW1hcnkgZmluZGluZ3MgYWJvdXQgZmlyc3QgY2F0ZWdvcmllcw0KDQojIyMgVG90YWwgT2JqZWN0aXZlcyBDb21wYXJpc29ucw0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSB0MV90b3dlcktpbGxzKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgICAgIGxhYnMoeSA9ICJDb3VudCIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gdDJfdG93ZXJLaWxscykpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogICAgICBsYWJzKHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KdG93ZXJfa2lsbHMgPC0gZGF0YS5mcmFtZShUZWFtID0gYygiVDEiLCAiVDIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgS2lsbHMgPSBjKHN1bShsb2xfZGF0YSR0MV90b3dlcktpbGxzLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShsb2xfZGF0YSR0Ml90b3dlcktpbGxzLCBuYS5ybSA9IFRSVUUpKSkNCg0KZ2dwbG90KHRvd2VyX2tpbGxzLCBhZXMoeCA9IFRlYW0sIHkgPSBLaWxscywgZmlsbCA9IFRlYW0pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGxhYnMoeCA9ICJUZWFtIiwgeSA9ICJUb3RhbCBUb3dlciBLaWxscyIsIHRpdGxlID0gIkNvbXBhcmlzb24gb2YgVG93ZXIgS2lsbHMgYmV0d2VlbiBUMSBhbmQgVDIiKQ0KYGBgDQoNCk1vcmUgb2Z0ZW4gdGhhbiBub3QsIGJvdGggdGVhbXMgdGVuZCB0byBraWxsIFw+NiB0b3dlcnMgcGVyIGdhbWUNCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gdDFfaW5oaWJpdG9yS2lsbHMpKSArIA0KICAgICAgZ2VvbV9iYXIoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICAgICAgbGFicyh5ID0gIkNvdW50IikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSB0Ml9pbmhpYml0b3JLaWxscykpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogICAgICBsYWJzKHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBpbmhpYml0b3Iga2lsbHMgZm9yIGVhY2ggdGVhbQ0KaW5oaWJpdG9yX2tpbGxzIDwtIGRhdGEuZnJhbWUoVGVhbSA9IGMoIlQxIiwgIlQyIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBLaWxscyA9IGMoc3VtKGxvbF9kYXRhJHQxX2luaGliaXRvcktpbGxzLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0obG9sX2RhdGEkdDJfaW5oaWJpdG9yS2lsbHMsIG5hLnJtID0gVFJVRSkpKQ0KDQpnZ3Bsb3QoaW5oaWJpdG9yX2tpbGxzLCBhZXMoeCA9IFRlYW0sIHkgPSBLaWxscywgZmlsbCA9IFRlYW0pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGxhYnMoeCA9ICJUZWFtIiwgeSA9ICJUb3RhbCBJbmhpYml0b3IgS2lsbHMiLCB0aXRsZSA9ICJDb21wYXJpc29uIG9mIEluaGliaXRvciBLaWxscyBiZXR3ZWVuIFQxIGFuZCBUMiIpDQpgYGANCg0KR2FtZXMgdXN1YWxseSBlbmQgd2l0aCAwIHRvIDIgaW5oaWJpdG9ycyBiZWluZyBkZXN0cm95ZWQsIGxpa2VseSBkdWUgdG8gbGF0ZSBnYW1lIGZvcmZlaXRzIG9yIHN0ZWFtIHJvbGxzICh3aGljaCBpcyBvZnRlbiB0aGUgY2FzZSBpbiBtYW55IGdhbWVzKQ0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSB0MV9kcmFnb25LaWxscykpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogICAgICBsYWJzKHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IHQyX2RyYWdvbktpbGxzKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgICAgIGxhYnMoeSA9ICJDb3VudCIpDQpgYGANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIGRyYWdvbiBraWxscyBmb3IgZWFjaCB0ZWFtDQpkcmFnb25fa2lsbHMgPC0gZGF0YS5mcmFtZShUZWFtID0gYygiVDEiLCAiVDIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEtpbGxzID0gYyhzdW0obG9sX2RhdGEkdDFfZHJhZ29uS2lsbHMsIG5hLnJtID0gVFJVRSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShsb2xfZGF0YSR0Ml9kcmFnb25LaWxscywgbmEucm0gPSBUUlVFKSkpDQoNCmdncGxvdChkcmFnb25fa2lsbHMsIGFlcyh4ID0gVGVhbSwgeSA9IEtpbGxzLCBmaWxsID0gVGVhbSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh4ID0gIlRlYW0iLCB5ID0gIlRvdGFsIERyYWdvbiBLaWxscyIsIHRpdGxlID0gIkNvbXBhcmlzb24gb2YgRHJhZ29uIEtpbGxzIGJldHdlZW4gVDEgYW5kIFQyIikNCmBgYA0KDQpEcmFnb24ga2lsbHMgY29tZm9ydGFibHkgbGllIHdpdGhpbiB0aGUgMC0yIHJhbmdlDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IHQxX3JpZnRIZXJhbGRLaWxscykpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogICAgICBsYWJzKHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IHQyX3JpZnRIZXJhbGRLaWxscykpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogICAgICBsYWJzKHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiByaWZ0IGhlcmFsZCBraWxscyBmb3IgZWFjaCB0ZWFtDQpyaWZ0SGVyYWxkX2tpbGxzIDwtIGRhdGEuZnJhbWUoVGVhbSA9IGMoIlQxIiwgIlQyIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS2lsbHMgPSBjKHN1bShsb2xfZGF0YSR0MV9yaWZ0SGVyYWxkS2lsbHMsIG5hLnJtID0gVFJVRSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0obG9sX2RhdGEkdDJfcmlmdEhlcmFsZEtpbGxzLCBuYS5ybSA9IFRSVUUpKSkNCg0KZ2dwbG90KHJpZnRIZXJhbGRfa2lsbHMsIGFlcyh4ID0gVGVhbSwgeSA9IEtpbGxzLCBmaWxsID0gVGVhbSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh4ID0gIlRlYW0iLCB5ID0gIlRvdGFsIFJpZnQgSGVyYWxkIEtpbGxzIiwgdGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBSaWZ0IEhlcmFsZCBLaWxscyBiZXR3ZWVuIFQxIGFuZCBUMiIpDQpgYGANCg0KUmlmdCBoZXJhbGQgb25seSBzcGF3bnMgZnJvbSA5OjUwIC0gMTk6NDUgZHVyaW5nIHRoZSBnYW1lIGFuZCBpcyBtb3N0IG9mdGVuIG9ubHkgdGFrZW4gb25jZSBpZiBhdCBhbGwNCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gdDFfYmFyb25LaWxscykpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogICAgICBsYWJzKHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IHQyX2Jhcm9uS2lsbHMpKSArIA0KICAgICAgZ2VvbV9iYXIoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICAgICAgbGFicyh5ID0gIkNvdW50IikNCmBgYA0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgYmFyb24ga2lsbHMgZm9yIGVhY2ggdGVhbQ0KYmFyb25fa2lsbHMgPC0gZGF0YS5mcmFtZShUZWFtID0gYygiVDEiLCAiVDIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgS2lsbHMgPSBjKHN1bShsb2xfZGF0YSR0MV9iYXJvbktpbGxzLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShsb2xfZGF0YSR0Ml9iYXJvbktpbGxzLCBuYS5ybSA9IFRSVUUpKSkNCg0KZ2dwbG90KGJhcm9uX2tpbGxzLCBhZXMoeCA9IFRlYW0sIHkgPSBLaWxscywgZmlsbCA9IFRlYW0pKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGxhYnMoeCA9ICJUZWFtIiwgeSA9ICJUb3RhbCBCYXJvbiBLaWxscyIsIHRpdGxlID0gIkNvbXBhcmlzb24gb2YgQmFyb24gS2lsbHMgYmV0d2VlbiBUMSBhbmQgVDIiKQ0KYGBgDQoNCkJhcm9uIHNwYXducyBhZnRlciB0aGUgcmlmdCBoZXJhbGQgYW5kIHVzdWFsbHkgaXMgZ2FtZSBvdmVyIGlmIHRha2VuIGFuZCB1c2VkIHByb3Blcmx5LCBoZW5jZSB3aHkgdGhlIGFtb3VudCB0YWtlbiBpcyB1c3VhbGx5IDAgb3IgMSBhcyBzb21ldGltZXMgZ2FtZXMgZW5kIGJlZm9yZSBpdCBjYW4gYmUgdGFrZW4NCg0KIyMgQmFzZSBNb2RlbCBhbmQgUHJpb3IgTW9kZWxpbmcNCg0KTGV0cyBzdGFydCBvZmYgd2l0aCBhIGJhc2UgbW9kZWwgdG8gc2VlIGlmIHdlIGNhbiBnZXQgaW5zaWdodCBmcm9tIGp1c3QgdGhlIHdpbm5lciBvZiB0aGUgbWF0Y2gNCg0KYGBge3J9DQpiYXNlIDwtIHN0YW5fZ2xtKGdhbWVEdXJhdGlvbiB+IHdpbm5lciwNCiAgZGF0YSA9IGxvbF9kYXRhLCBmYW1pbHkgPSBnYXVzc2lhbiwgDQogIHByaW9yX2ludGVyY2VwdCA9IG5vcm1hbCgxODAwLCAxNTAsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBwcmlvciA9IG5vcm1hbCgwLCAyLjUsIGF1dG9zY2FsZSA9IFRSVUUpLCANCiAgcHJpb3JfYXV4ID0gZXhwb25lbnRpYWwoMSwgYXV0b3NjYWxlID0gVFJVRSksDQogIGNoYWlucyA9IDQsIGl0ZXIgPSA1MDAwKjIsIHNlZWQgPSA4NDczNSkNCmBgYA0KDQpgYGB7cn0NCnByaW9yX3N1bW1hcnkoYmFzZSkgDQpgYGANCg0KQSBwcmlvciBwcmVkaWN0aXZlIGNoZWNrIGNhbiBiZSB1c2VkIHRvIHNlZSB3aGF0IHdlIGNhbiBwb3RlbnRpYWxseSBleHBlY3QgZnJvbSB0aGUgZGF0YSBhbG9uZSBhbmQgd2hhdCB0aGUgc3RhbiBtb2RlbCB2aWV3ZWQgYXMgYXBwcm9wcmlhdGUgZm9yIG91ciBwcmVkaWN0aW9ucw0KDQpgYGB7cn0NCmJhc2VfcHJpb3JzIDwtIHVwZGF0ZShiYXNlLCBwcmlvcl9QRCA9IFRSVUUpDQojIDIwMCBwcmlvciBtb2RlbCBsaW5lcw0KbG9sX2RhdGEgJT4lDQogIGFkZF9maXR0ZWRfZHJhd3MoYmFzZSwgbiA9IDIwMCkgJT4lDQogIGdncGxvdChhZXMoeCA9IHdpbm5lciwgeSA9IGdhbWVEdXJhdGlvbikpICsNCiAgICBnZW9tX2xpbmUoYWVzKHkgPSAudmFsdWUsIGdyb3VwID0gLmRyYXcpLCBhbHBoYSA9IDAuMDUpDQoNCiMgNCBwcmlvciBzaW11bGF0ZWQgZGF0YXNldHMNCnNldC5zZWVkKDMpDQpsb2xfZGF0YSAlPiUNCiAgYWRkX3ByZWRpY3RlZF9kcmF3cyhiYXNlLCBuID0gNCkgJT4lDQogIGdncGxvdChhZXMoeCA9IHdpbm5lciwgeSA9IGdhbWVEdXJhdGlvbikpICsNCiAgICBnZW9tX3BvaW50KGFlcyh5ID0gLnByZWRpY3Rpb24sIGdyb3VwID0gLmRyYXcpKSArIA0KICAgIGZhY2V0X3dyYXAofiAuZHJhdykNCmBgYA0KDQpQcmlvciBkaXN0cmlidXRpb24gZXhwZWN0cyB0aGUgZ2FtZXMgdG8gcmFuZ2UgZnJvbSAxOTEwIC0gMTk0NSBzZWNvbmRzIGFzIGV4cGVjdGVkDQoNCkdhbWUgZHVyYXRpb24gbGllcyBiZXR3ZWVuIDUwMC0zNTAwIHNlY29uZHMgd2hpY2ggZmFsbHMgaW4gbGluZSB3aXRoIG91ciBzdW1tYXJ5DQoNClByaW9yIGRpc3RyaWJ1dGlvbiBleHBlY3RzIGdhbWVzIHRvIGxhc3QgbG9uZ2VyIGlmIHRlYW0gMiB3aW5zIGFzIGNvbXBhcmVkIHRvIHRlYW0gMQ0KDQpgYGB7cn0NCm1jbWNfdHJhY2UoYmFzZSwgc2l6ZSA9IC4xKQ0KYGBgDQoNClBsb3RzIGxvb2sgZmFzdCBtaXhpbmcgYW5kIGNvbnNpc3RlbnQNCg0KYGBge3J9DQpyaGF0KGJhc2UpDQpgYGANCg0KUmhhdCBjbG9zZSB0byAxIGFuZCBub3QgXD4xLjA1LCBnb29kDQoNCmBgYHtyfQ0KbmVmZl9yYXRpbyhiYXNlKQ0KYGBgDQoNCm5lZmYgcmF0aW8gaXMgYSBsaXR0bGUgaGlnaCBidXQgc3RpbGwgYWNjZXB0YWJsZSwgXD4uMTAgd2hpY2ggaXMgZ29vZA0KDQpgYGB7cn0NCnRpZHkoYmFzZSwgZWZmZWN0cyA9IGMoImZpeGVkIiwgImF1eCIpLA0KICAgICBjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjk1KQ0KYGBgDQoNCk92ZXJhbGwgdGhlIG1vZGVsIGNvdWxkIHdvcmssIGJ1dCBtb3JlIGludmVzdGlnYXRpb24gc2hvdWxkIGJlIGRvbmUgdG8gaW1wcm92ZSB3aGF0IHdlIGhhdmUNCg0KIyMgSW50ZXJhY3Rpb24gSW52ZXN0aWdhdGlvbg0KDQpMZXRzIHNlZSBpZiB0aGVyZSBhcmUgc29tZSBpbnRlcmFjdGlvbnMgd2UgY2FuIGV4cGVjdA0KDQpTcGVha2luZyBmcm9tIGV4cGVyaWVuY2UgaW4gcGxheWluZyB0aGUgZ2FtZSBteXNlbGYsIEkgcHJlZGljdCB0aGUgZHJhZ29uIGFuZCBiYXJvbiBraWxscyB3aWxsIGhhdmUgYSBiaWcgaW1wYWN0IG9uIHRoZSBnYW1lIGR1cmF0aW9uIGR1ZSB0byB0aGUgYnVmZnMgdGhleSBnaXZlIGVhY2ggbWVtYmVyIG9mIHRoZSB0ZWFtIGJlaW5nIGNydWNpYWwgdG8gd2lubmluZw0KDQpgYGB7cn0NCmludCA8LSBzdGFuX2dsbShnYW1lRHVyYXRpb24gfiB0MV9kcmFnb25LaWxsczp0Ml9kcmFnb25LaWxscywNCiAgZGF0YSA9IGxvbF9kYXRhLCBmYW1pbHkgPSBnYXVzc2lhbiwgDQogIHByaW9yX2ludGVyY2VwdCA9IG5vcm1hbCgxODAwLCAxNTAsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBwcmlvciA9IG5vcm1hbCgwLCAyLjUsIGF1dG9zY2FsZSA9IFRSVUUpLCANCiAgcHJpb3JfYXV4ID0gZXhwb25lbnRpYWwoMSwgYXV0b3NjYWxlID0gVFJVRSksDQogIGNoYWlucyA9IDQsIGl0ZXIgPSA1MDAwKjIsIHNlZWQgPSA4NDczNSkNCmBgYA0KDQpgYGB7cn0NCmludDIgPC0gc3Rhbl9nbG0oZ2FtZUR1cmF0aW9uIH4gdDFfYmFyb25LaWxsczp0Ml9iYXJvbktpbGxzLA0KICBkYXRhID0gbG9sX2RhdGEsIGZhbWlseSA9IGdhdXNzaWFuLCANCiAgcHJpb3JfaW50ZXJjZXB0ID0gbm9ybWFsKDE4MDAsIDE1MCwgYXV0b3NjYWxlID0gVFJVRSksDQogIHByaW9yID0gbm9ybWFsKDAsIDIuNSwgYXV0b3NjYWxlID0gVFJVRSksIA0KICBwcmlvcl9hdXggPSBleHBvbmVudGlhbCgxLCBhdXRvc2NhbGUgPSBUUlVFKSwNCiAgY2hhaW5zID0gNCwgaXRlciA9IDUwMDAqMiwgc2VlZCA9IDg0NzM1KQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShpbnQpDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5KGludDIpDQpgYGANCg0KU3VtbWFyeSBzdGF0cyBsb29rIHByb21pc2luZyB3aXRoIGJvdGggdmFsdWVzIG5vdCBjb250YWluaW5nIDAgZm9yIHRoZWlyIDk1JSBDSSBzaG93aW5nIHRoZXkgYXJlIHNpZ25pZmljYW50IGFuZCBjYW4gYmUgdXNlZnVsDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHRoZSBwb3N0ZXJpb3Igc2FtcGxlcw0KcG9zdGVyaW9yX3NhbXBsZXMgPC0gYXMubWF0cml4KGludCkNCnBvc3Rlcmlvcl9zYW1wbGVzMiA8LSBhcy5tYXRyaXgoaW50MikNCg0KIyBQbG90IHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIHRoZSBpbnRlcmFjdGlvbiB0ZXJtDQptY21jX2hpc3QocG9zdGVyaW9yX3NhbXBsZXMsIHBhcnMgPSBjKCJ0MV9kcmFnb25LaWxsczp0Ml9kcmFnb25LaWxscyIpKQ0KDQptY21jX2hpc3QocG9zdGVyaW9yX3NhbXBsZXMyLCBwYXJzID0gYygidDFfYmFyb25LaWxsczp0Ml9iYXJvbktpbGxzIikpDQpgYGANCg0KVGhlIHByZWRpY3RvcnMgZG9uJ3QgbG9vayBza2V3ZWQgZWl0aGVyLCBsb29rcyBnb29kLg0KDQpXZSBjYW4gY2FuY2x1ZGUgdGhhdCB0aGVyZSBtYXkgYmUgYSBwb3RlbnRpYWwgaW50ZXJhY3Rpb24gYmV0d2VlbiBkcmFnb24ga2lsbHMgYW5kIGJhcm9uIGtpbGxzIGZvciBlYWNoIHRlYW0NCg0KV2UgY2FuIHV0aWxpemUgdGhpcyBpbmZvIGxhdGVyIG9uIHVwb24gYnVpbGRpbmcgYSBiZXR0ZXIgbW9kZWwNCg0KIyMgTWFpbiBNb2RlbA0KDQpMZXQncyBhc3Nlc3MgaG93IG11Y2ggZ2V0dGluZyBhIGp1bXAgc3RhcnQgaW4gdGhlIGdhbWUgZG9lcyBmb3IgZGV0ZXJtaW5pbmcgdGhlIGxlbmd0aCBvZiB0aGUgZ2FtZQ0KDQpgYGB7cn0NCm1haW4gPC0gc3Rhbl9nbG0oZ2FtZUR1cmF0aW9uIH4gd2lubmVyICsgZmlyc3RCbG9vZCArIGZpcnN0VG93ZXIgKyBmaXJzdERyYWdvbiArIGZpcnN0QmFyb24sDQogIGRhdGEgPSBsb2xfZGF0YSwgZmFtaWx5ID0gZ2F1c3NpYW4sIA0KICBwcmlvcl9pbnRlcmNlcHQgPSBub3JtYWwoMTgwMCwgMTUwLCBhdXRvc2NhbGUgPSBUUlVFKSwNCiAgcHJpb3IgPSBub3JtYWwoMCwgMi41LCBhdXRvc2NhbGUgPSBUUlVFKSwgDQogIHByaW9yX2F1eCA9IGV4cG9uZW50aWFsKDEsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBjaGFpbnMgPSA0LCBpdGVyID0gNTAwMCoyLCBzZWVkID0gODQ3MzUpDQpgYGANCg0KYGBge3J9DQpwcmlvcl9zdW1tYXJ5KG1haW4pIA0KYGBgDQoNCmBgYHtyfQ0KbWNtY190cmFjZShtYWluLCBzaXplID0gLjEpDQpgYGANCg0KQ2hhaW5zIGxvb2sgbm9ybWFsDQoNCmBgYHtyfQ0KcmhhdChtYWluKQ0KYGBgDQoNCmBgYHtyfQ0KbmVmZl9yYXRpbyhtYWluKQ0KYGBgDQoNClJoYXQgbG9va3MgZ29vZCwgc29tZSBvZiB0aGUgbmVmZiByYXRpbyB2YWx1ZXMgYXJlIHN0aWxsIGhpZ2ggYnV0IGFjY2VwdGFibGUgZm9yIG5vdw0KDQpgYGB7cn0NCnRpZHkobWFpbiwgZWZmZWN0cyA9IGMoImZpeGVkIiwgImF1eCIpLA0KICAgICBjb25mLmludCA9IFRSVUUsIGNvbmYubGV2ZWwgPSAwLjk1KQ0KYGBgDQoNCkZyb20gdGhlIHRpZHkgb3V0cHV0LCBpdCBsb29rcyBsaWtlIHdpbm5lciwgZmlyc3RCbG9vZCwgYW5kIGZpcnN0VG93ZXIgbG9zZSB0aGVpciBzaWduaWZpY2FuY2Ugd2hlbiBhZGRpbmcgb3RoZXIgcHJlZGljdG9ycyBzaW5jZSB0aGVpciA5NSUgQ0kgcmFuZ2UgaW5jbHVkZXMgMC4gV2UnbGwgdXNlIHRoaXMgaW5mbyBsYXRlciBmb3IgcmVmaW5pbmcgb3VyIG1vZGVsDQoNCmBgYHtyfQ0KbmV3ZGF0YSA8LSBkYXRhLmZyYW1lKHdpbm5lciA9IGZhY3RvcigxLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkd2lubmVyKSksDQogICAgICAgICAgICAgICAgICAgICAgZmlyc3RCbG9vZCA9IGZhY3RvcigyLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkZmlyc3RCbG9vZCkpLA0KICAgICAgICAgICAgICAgICAgICAgIGZpcnN0VG93ZXIgPSBmYWN0b3IoMiwgbGV2ZWxzID0gbGV2ZWxzKGxvbF9kYXRhJGZpcnN0VG93ZXIpKSwNCiAgICAgICAgICAgICAgICAgICAgICBmaXJzdERyYWdvbiA9IGZhY3RvcigyLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkZmlyc3REcmFnb24pKSwNCiAgICAgICAgICAgICAgICAgICAgICBmaXJzdEJhcm9uID0gZmFjdG9yKDIsIGxldmVscyA9IGxldmVscyhsb2xfZGF0YSRmaXJzdEJhcm9uKSkpDQoNCm1haW5fcHJlZGljdCA8LSBwb3N0ZXJpb3JfcHJlZGljdCgNCiAgbWFpbiwgDQogIG5ld2RhdGEgPSBuZXdkYXRhKQ0KbWNtY19hcmVhcyhtYWluX3ByZWRpY3QpICsgIHhsYWIoIkdhbWUgRHVyYXRpb24iKSArDQogIGdndGl0bGUoJ1ByZWRpY3RpdmUgZGlzdHJpYnV0aW9uIG9mIGEgTGVhZ3VlIG9mIExlZ2VuZHMgZ2FtZSB3aGVyZSBmaXJzdCBibG9vZCwgdG93ZXIsIGRyYWdvbiwgYmFyb24gd2FzIHRlYW0gMiBhbmQgdGVhbSAxIHdvbicpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpDQpgYGANCg0KSGVyZSdzIGFuIGV4YW1wbGUgb3V0cHV0IG9mIHdoZXJlIGEgZ2FtZSBkdXJhdGlvbiBtYXkgbGllIGlmIHRlYW0gMSB3aW5zIGRlc3BpdGUgYmVpbmcgYmVoaW5kIGZyb20gdGhlIHN0YXJ0LiBUaGUgZ2FtZSBpcyBhIGxpdHRsZSBsYXRlciB0aGFuIG5vcm1hbCwgYnV0IHRvIGJlIGV4cGVjdGVkIGFzIHRoZSB0ZWFtIHdvdWxkIG5lZWQgdGltZSB0byBtYWtlIHRoZSBjb21lYmFjayBpbiB0aGUgZmlyc3QgcGxhY2UuIEhvd2V2ZXIsIEkgYmVsaWV2ZSB0aGlzIGlzIHVuZGVyc2hvb3RpbmcgdGhlIHRydWUgdGltZSB3ZSBjb3VsZCBleHBlY3QgZnJvbSB0aGlzIHNjZW5hcmlvLiBXZSdsbCB2ZXJpZnkgaWYgdGhpcyBpcyB0cnVlIGFmdGVyIHJlZmluaW5nIG91ciBtb2RlbA0KDQojIyBNb3JlIFByZWRpY3RvcnMNCg0KV2UgY2FuIGFkZCBzb21lIG51bWVyaWNhbCB2YXJpYWJsZXMgdG8gc2VlIGhvdyBtdWNoIGFkZGluZyB0aGUgYW1vdW50IG9mIGVhY2ggb2JqZWN0aXZlIGEgdGVhbSBoYXMgdGFrZW4gdG8gc2VlIGhvdyBpdCBhZmZlY3RzIHRoZSBnYW1lIGR1cmF0aW9uDQoNCmBgYHtyfQ0KZXh0X21haW4gPC0gc3Rhbl9nbG0oZ2FtZUR1cmF0aW9uIH4gd2lubmVyICsgZmlyc3RCbG9vZCArIGZpcnN0VG93ZXIgKyBmaXJzdERyYWdvbiArIGZpcnN0QmFyb24gKyB0MV90b3dlcktpbGxzICsgdDFfaW5oaWJpdG9yS2lsbHMgKyB0MV9iYXJvbktpbGxzICsgdDFfZHJhZ29uS2lsbHMgKyB0Ml90b3dlcktpbGxzICsgdDJfaW5oaWJpdG9yS2lsbHMgKyB0Ml9iYXJvbktpbGxzICsgdDJfZHJhZ29uS2lsbHMsDQogIGRhdGEgPSBsb2xfZGF0YSwgZmFtaWx5ID0gZ2F1c3NpYW4sIA0KICBwcmlvcl9pbnRlcmNlcHQgPSBub3JtYWwoMTgwMCwgMTUwLCBhdXRvc2NhbGUgPSBUUlVFKSwNCiAgcHJpb3IgPSBub3JtYWwoMCwgMi41LCBhdXRvc2NhbGUgPSBUUlVFKSwgDQogIHByaW9yX2F1eCA9IGV4cG9uZW50aWFsKDEsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBjaGFpbnMgPSA0LCBpdGVyID0gNTAwMCoyLCBzZWVkID0gODQ3MzUpDQpgYGANCg0KYGBge3J9DQpwcmlvcl9zdW1tYXJ5KGV4dF9tYWluKSANCmBgYA0KDQpgYGB7cn0NCm1jbWNfdHJhY2UoZXh0X21haW4sIHNpemUgPSAuMSkNCmBgYA0KDQpUcmFjZXMgbG9vayBnb29kDQoNCmBgYHtyfQ0KcmhhdChleHRfbWFpbikNCmBgYA0KDQpgYGB7cn0NCm5lZmZfcmF0aW8oZXh0X21haW4pDQpgYGANCg0KUmhhdCBsb29rcyBnb29kLCBzb21lIG5lZmYgcmF0aW9zIChpLmUuIGZpcnN0Qmxvb2QsIGZpcnN0VG93ZXIpIGFyZSBub3cgdG9vIGhpZ2ggdG8ga2VlcCBhbmQgd2lsbCBuZWVkIHRvIGJlIGhhbmRsZWQgbGF0ZXINCg0KYGBge3J9DQp0aWR5KGV4dF9tYWluLCBlZmZlY3RzID0gYygiZml4ZWQiLCAiYXV4IiksDQogICAgIGNvbmYuaW50ID0gVFJVRSwgY29uZi5sZXZlbCA9IDAuOTUpDQpgYGANCg0KSGVyZSwgdGhlIHByZWRpY3RvcnMgd2lubmVyLCBmaXJzdEJsb29kLCBhbmQgZmlyc3RUb3dlciBhcmUgbm90IHNpZ25pZmljYW50IGR1ZSB0byB0aGVpciA5NSUgQ0kgaW5jbHVkaW5nIDANCg0KYGBge3J9DQojIGRhdGFmcmFtZSB3aXRoIHRoZSBzcGVjaWZpZWQgdmFsdWVzDQpuZXdkYXRhX2V4dCA8LSBkYXRhLmZyYW1lKA0KICB3aW5uZXIgPSBmYWN0b3IoMSwgbGV2ZWxzID0gbGV2ZWxzKGxvbF9kYXRhJHdpbm5lcikpLA0KICBmaXJzdEJsb29kID0gZmFjdG9yKDIsIGxldmVscyA9IGxldmVscyhsb2xfZGF0YSRmaXJzdEJsb29kKSksDQogIGZpcnN0VG93ZXIgPSBmYWN0b3IoMiwgbGV2ZWxzID0gbGV2ZWxzKGxvbF9kYXRhJGZpcnN0VG93ZXIpKSwNCiAgZmlyc3REcmFnb24gPSBmYWN0b3IoMiwgbGV2ZWxzID0gbGV2ZWxzKGxvbF9kYXRhJGZpcnN0RHJhZ29uKSksDQogIGZpcnN0QmFyb24gPSBmYWN0b3IoMiwgbGV2ZWxzID0gbGV2ZWxzKGxvbF9kYXRhJGZpcnN0QmFyb24pKSwNCiAgdDFfdG93ZXJLaWxscyA9IDcsDQogIHQxX2luaGliaXRvcktpbGxzID0gMiwNCiAgdDFfYmFyb25LaWxscyA9IDIsDQogIHQxX2RyYWdvbktpbGxzID0gMiwNCiAgdDJfdG93ZXJLaWxscyA9IDMsDQogIHQyX2luaGliaXRvcktpbGxzID0gMiwNCiAgdDJfYmFyb25LaWxscyA9IDEsDQogIHQyX2RyYWdvbktpbGxzID0gMw0KKQ0KDQpleHRfbWFpbl9wcmVkaWN0IDwtIHBvc3Rlcmlvcl9wcmVkaWN0KGV4dF9tYWluLCBuZXdkYXRhID0gbmV3ZGF0YV9leHQpDQoNCm1jbWNfYXJlYXMoZXh0X21haW5fcHJlZGljdCkgKyAgDQogIHhsYWIoIkdhbWUgRHVyYXRpb24iKSArDQogIGdndGl0bGUoJ1ByZWRpY3RpdmUgZGlzdHJpYnV0aW9uIG9mIGEgTGVhZ3VlIG9mIExlZ2VuZHMgZ2FtZSB3aGVyZSBmaXJzdCBibG9vZCwgdG93ZXIsIGRyYWdvbiwgYmFyb24gd2FzIHRlYW0gMiBhbmQgdGVhbSAxIHdvbiwgd2l0aCBhZGRpdGlvbmFsIHByZWRpY3RvcnMnKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQ0KYGBgDQoNCkFmdGVyIGFkZGluZyBhIGZldyBtb3JlIHZhcmlhYmxlcyBpbnRvIHBsYXkgd2UgY2FuIHNlZSB0aGUgZ2FtZSBnZXRzIGxvbmdlciAoYW5kIGV4cGVjdGVkbHkgc28pLiBUZWFtIDEgY29tZXMgZnJvbSBiZWhpbmQgYnV0IGVuZHMgdXAgZ2V0dGluZyAyIGJhcm9ucyBhbmQgNyB0b3dlcnMgbWFraW5nIHVzIGJlbGlldmUgdGhpcyBnYW1lIHdpbGwgaGF2ZSB0byBnbyBvbiBmb3IgYSB3aGlsZSBmb3IgdGhlIGNvbWViYWNrIHRvIHRydWx5IGJlIGNvbXBsZXRlDQoNCiMjIFJlZmluZWQgTW9kZWwNCg0KTGV0J3Mgbm93IHJlbW92ZSB0aGUgaGlnaCBuZWZmIHJhdGlvIHRlcm1zIGZyb20gdGhlIGV4dGVuZGVkIG1vZGVsIChpLmUuIFw+IDEpIGFuZCBpbmNsdWRlIHRoZSBpbnRlcmFjdGlvbiB0ZXJtcyB3ZSBkZWVtZWQgdXNlZnVsIGJlZm9yZWhhbmQNCg0KYGBge3J9DQpleHRfbWFpbl9pbnQgPC0gc3Rhbl9nbG0oZ2FtZUR1cmF0aW9uIH4gZmlyc3REcmFnb24gKyBmaXJzdEJhcm9uICsgdDFfYmFyb25LaWxscyArIHQxX2RyYWdvbktpbGxzICsgdDJfYmFyb25LaWxscyArIHQyX2RyYWdvbktpbGxzICsgdDFfYmFyb25LaWxsczp0Ml9iYXJvbktpbGxzICsgdDFfZHJhZ29uS2lsbHM6dDJfZHJhZ29uS2lsbHMsDQogIGRhdGEgPSBsb2xfZGF0YSwgZmFtaWx5ID0gZ2F1c3NpYW4sIA0KICBwcmlvcl9pbnRlcmNlcHQgPSBub3JtYWwoMTgwMCwgMTUwLCBhdXRvc2NhbGUgPSBUUlVFKSwNCiAgcHJpb3IgPSBub3JtYWwoMCwgMi41LCBhdXRvc2NhbGUgPSBUUlVFKSwgDQogIHByaW9yX2F1eCA9IGV4cG9uZW50aWFsKDEsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBjaGFpbnMgPSA0LCBpdGVyID0gNTAwMCoyLCBzZWVkID0gODQ3MzUpDQpgYGANCg0KYGBge3J9DQpwcmlvcl9zdW1tYXJ5KGV4dF9tYWluX2ludCkNCmBgYA0KDQpgYGB7cn0NCm1jbWNfdHJhY2UoZXh0X21haW5faW50LCBzaXplID0gLjEpDQpgYGANCg0KU28gZmFyIHNvIGdvb2QNCg0KYGBge3J9DQpyaGF0KGV4dF9tYWluX2ludCkNCmBgYA0KDQpSaGF0IGxvb2tzIHNhdGlzZmFjdG9yeSBmb3IgYWxsIHByZWRpY3RvcnMNCg0KYGBge3J9DQpuZWZmX3JhdGlvKGV4dF9tYWluX2ludCkNCmBgYA0KDQpOZWZmIHJhdGlvcyBhcmUgbXVjaCBtb3JlIHJlYXNvbmFibGUgbm93IGFuZCBhIGJpZyBpbXByb3ZlbWVudCBmcm9tIHByaW9yIG1vZGVscyB3aXRoIG5vIHZhbHVlIGJlaW5nIFw+IC45MA0KDQpgYGB7cn0NCnRpZHkoZXh0X21haW5faW50LCBlZmZlY3RzID0gYygiZml4ZWQiLCAiYXV4IiksDQogICAgIGNvbmYuaW50ID0gVFJVRSwgY29uZi5sZXZlbCA9IDAuOTUpDQpgYGANCg0KRXZlcnkgdmFsdWUgbG9va3Mgc2lnbmlmaWNhbnQgZnJvbSB0aGUgOTUlIENJIChldmVuIHRoZSBpbnRlcmFjdGlvbiB0ZXJtcyEhKSBkdWUgdG8gbm9uZSBvZiB0aGVtIGluY2x1ZGluZyAwLg0KDQpgYGB7cn0NCiMgZGF0YWZyYW1lIHdpdGggdGhlIHNwZWNpZmllZCB2YWx1ZXMNCm5ld2RhdGFfZXh0X2ludCA8LSBkYXRhLmZyYW1lKA0KICBmaXJzdERyYWdvbiA9IGZhY3RvcigyLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkZmlyc3REcmFnb24pKSwNCiAgZmlyc3RCYXJvbiA9IGZhY3RvcigyLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkZmlyc3RCYXJvbikpLA0KICB0MV9iYXJvbktpbGxzID0gMiwNCiAgdDFfZHJhZ29uS2lsbHMgPSAyLA0KICB0Ml9iYXJvbktpbGxzID0gMSwNCiAgdDJfZHJhZ29uS2lsbHMgPSAzDQopDQoNCmV4dF9tYWluX2ludF9wcmVkaWN0IDwtIHBvc3Rlcmlvcl9wcmVkaWN0KGV4dF9tYWluX2ludCwgbmV3ZGF0YSA9IG5ld2RhdGFfZXh0X2ludCkNCg0KbWNtY19hcmVhcyhleHRfbWFpbl9pbnRfcHJlZGljdCkgKyAgDQogIHhsYWIoIkdhbWUgRHVyYXRpb24iKSArDQogIGdndGl0bGUoJ1ByZWRpY3RpdmUgZGlzdHJpYnV0aW9uIG9mIGEgTGVhZ3VlIG9mIExlZ2VuZHMgZ2FtZSB3aGVyZSBmaXJzdCBkcmFnb24sIGJhcm9uIHdhcyB0ZWFtIDIgYW5kIHRlYW0gMSBoYWQgbW9yZSBiYXJvbiBraWxscyB3aXRoIGludGVyYWN0aW9uJykgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkNCmBgYA0KDQpUaGUgZ2FtZXMgc2VlbSB0byBkcmFnIG9uIGV2ZW4gbG9uZ2VyIHdpdGggdGhpcyBuZXcgbW9kZWwuIFRoaXMgaXMgZXhwZWN0ZWQgc2luY2UgdGhlIGRyYWdvbiBhbmQgYmFyb24gcmVzcGF3biB0aW1lcyBhcmUgcXVpdGUgbG9uZyBzbywgYXMgc2FpZCBiZWZvcmUsIHRoZSBjb21lYmFjayB3b3VsZCByZWFsaXN0aWNhbGx5IHRha2UgbG9uZ2VyIHRoYW4gYXZlcmFnZS4NCg0KTGV0cyBkbyBzb21lIG1vZGVsIGNvbXBhcmlzb25zIHRvIHZlcmlmeSB3aGljaCBtb2RlbCBpcyBiZXN0DQoNCiMjIE1vZGVsIENvbXBhcmlzb25zDQoNCiMjIyBQUCBDaGVjaw0KDQpgYGB7cn0NCnBwX2NoZWNrKG1haW4sIG5yZXBzID0gNTApICsgeGxhYigiR2FtZSBEdXJhdGlvbiIpICsNCiAgZ2d0aXRsZSgnTWFpbiBlZmZlY3RzIG1vZGVsJykNCmBgYA0KDQpUaGUgbWFpbiBtb2RlbCBnZXRzIGEgZGVjZW50IGFtb3VudCBvZiBhcmVhLCB5ZXQgaXQgc3RhcnRzIG1vdmluZyB0b28gZmFyIHJpZ2h0IGFuZCB1bmRlcnNob290cyBhdCB0aGUgcGVhaw0KDQpgYGB7cn0NCnBwX2NoZWNrKGV4dF9tYWluLCBucmVwcyA9IDUwKSArIHhsYWIoIkdhbWUgRHVyYXRpb24iKSArDQogIGdndGl0bGUoJ0V4dGVuZGVkIE1haW4gZWZmZWN0cyBtb2RlbCcpDQpgYGANCg0KVGhlIGV4dGVuZGVkIG1vZGVsIGlzIG9iamVjdGl2ZWx5IGJldHRlciB0aGFuIHRoZSBtYWluIG1vZGVsLCBidXQgc3RpbGwgdW5kZXJzaG9vdHMgYXJlYSBhdCB0aGUgcGVhaw0KDQpgYGB7cn0NCnBwX2NoZWNrKGV4dF9tYWluX2ludCwgbnJlcHMgPSA1MCkgKyB4bGFiKCJHYW1lIER1cmF0aW9uIikgKw0KICBnZ3RpdGxlKCdSZWZpbmVkIEludGVyYWN0aW9uIGVmZmVjdHMgbW9kZWwnKQ0KYGBgDQoNClRoZSBpbnRlcmFjdGlvbiBtb2RlbCBpcyB2ZXJ5IHNpbWlsYXIgdG8gdGhlIGV4dGVuZGVkIG1vZGVsIHdoaWNoIGlzIGJldHRlciB0aGFuIHRoZSBtYWluIG1vZGVsLg0KDQpUaGUgcHBjaGVja3Mgc2hvdyBtb3N0IG9mIHRoZSBhcmUgYmVpbmcgY292ZXJlZCBmb3IgdGhlIGxhc3QgMiBtb2RlbHMgd2l0aCB0aGUgaW50ZXJhY3Rpb24gbW9kZWwgZG9pbmcgYmV0dGVyIGFyb3VuZCB0aGUgcGVhayBieSBhIHNtYWxsIG1hcmdpbg0KDQojIyMgMTAgRm9sZCBDcm9zcy1WYWxpZGF0aW9ucw0KDQpgYGB7cn0NCnRlc3Rfc2FtcGxlIDwtIGxvbF9kYXRhICU+JSBoZWFkKDEwMDAwKQ0KbnJvdyh0ZXN0X3NhbXBsZSkNCmBgYA0KDQpgYGB7cn0NCnNldC5zZWVkKDg0NzM1KQ0KDQpwX21haW4gPC0gcHJlZGljdGlvbl9zdW1tYXJ5KG1vZGVsID0gbWFpbiwgZGF0YSA9IHRlc3Rfc2FtcGxlKQ0KcF9leHRfbWFpbiA8LSBwcmVkaWN0aW9uX3N1bW1hcnkobW9kZWwgPSBleHRfbWFpbiwgZGF0YSA9IHRlc3Rfc2FtcGxlKQ0KcF9leHRfbWFpbl9pbnQgPC0gcHJlZGljdGlvbl9zdW1tYXJ5KG1vZGVsID0gZXh0X21haW5faW50LCBkYXRhID0gdGVzdF9zYW1wbGUpDQpgYGANCg0KYGBge3J9DQpyYmluZChwX21haW4sIHBfZXh0X21haW4sIHBfZXh0X21haW5faW50KQ0KYGBgDQoNClRoZXNlIGFyZSB0aGUgcmF3IE1BRSB2YWx1ZXMgYmFzZWQgb24gYSBzYW1wbGUgb2YgMTAwMDAgb2JzZXJ2YXRpb25zIGZyb20gdGhlIGRhdGEsIHdlIGNhbiB1c2UgdGhlc2UgdG8gZGV0ZXJtaW5lIHRoZSBiZWhhdmlvciBhbmQgYmlhcyBvZiBlYWNoIG1vZGVsIGJhc2VkIG9uIHRoZWlyIGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cw0KDQpgYGB7cn0NCnNldC5zZWVkKDg0NzM1KQ0KDQpjdl9tYWluIDwtIHByZWRpY3Rpb25fc3VtbWFyeV9jdigNCiAgbW9kZWwgPSBtYWluLCBkYXRhID0gbG9sX2RhdGEsIGsgPSAxMCkNCg0KY3ZfZXh0ZW5kIDwtIHByZWRpY3Rpb25fc3VtbWFyeV9jdigNCiAgbW9kZWwgPSBleHRfbWFpbiwgZGF0YSA9IGxvbF9kYXRhLCBrID0gMTApDQoNCmN2X2ludGVyYWN0IDwtIHByZWRpY3Rpb25fc3VtbWFyeV9jdigNCiAgbW9kZWwgPSBleHRfbWFpbl9pbnQsIGRhdGEgPSBsb2xfZGF0YSwgayA9IDEwKQ0KYGBgDQoNCmBgYHtyfQ0KcmJpbmQoY3ZfbWFpbiRjdiwgY3ZfZXh0ZW5kJGN2LCBjdl9pbnRlcmFjdCRjdikNCmBgYA0KDQojIyMgTG9vIERpYWdub3N0aWNzDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMzQ1MjEpDQptYWluX2VscGQgPC0gbG9vKG1haW4pDQpleHRfbWFpbl9lbHBkIDwtIGxvbyhleHRfbWFpbikNCmV4dF9tYWluX2ludGVyYWN0X2VscGQgPC0gbG9vKGV4dF9tYWluX2ludCkNCg0KbWFpbl9lbHBkJGVzdGltYXRlcw0KZXh0X21haW5fZWxwZCRlc3RpbWF0ZXMNCmV4dF9tYWluX2ludGVyYWN0X2VscGQkZXN0aW1hdGVzDQpgYGANCg0KYGBge3J9DQpjKG1haW5fZWxwZCRlc3RpbWF0ZXNbMV0sIGV4dF9tYWluX2VscGQkZXN0aW1hdGVzWzFdLCBleHRfbWFpbl9pbnRlcmFjdF9lbHBkJGVzdGltYXRlc1sxXSkNCg0KbG9vX2NvbXBhcmUobWFpbl9lbHBkLCBleHRfbWFpbl9lbHBkLCBleHRfbWFpbl9pbnRlcmFjdF9lbHBkKQ0KYGBgDQoNClRoZSBleHRlbmRlZCBtb2RlbCBmYWlycyBiZXR0ZXIgdGhhbiB0aGUgaW50ZXJhY3Rpb24gbW9kZWwgd2hlbiBjb21wYXJpbmcgdGhlaXIgRUxQRCBhbmQgTUFFLiBUaGUgaW50ZXJhY3Rpb24gbW9kZWwgZG9lcyA0MzM3LjYgcG9pbnRzICJ3b3JzZSIgZm9yIEVMUEQgYW5kIGlzIGFyb3VuZCAxNiBwb2ludHMgaGlnaGVyIGluIGl0cyBNQUUuIEhvd2V2ZXIsIHNvbWV0aGluZyB0byBjb25zaWRlciBoZXJlIGlzIHRoZSBpZGVhIG9mIFtvdmVyZml0dGluZ10oaHR0cHM6Ly9zdGF0aXN0aWNzYnlqaW0uY29tL3JlZ3Jlc3Npb24vb3ZlcmZpdHRpbmctcmVncmVzc2lvbi1tb2RlbHMvKSBhbmQgaG93IG1vcmUgcHJlZGljdG9ycyBhbmQgYWZmZWN0IHRoZSBtb2RlbCBvdmVyYWxsIGFuZCBza2V3IG91ciBtZXRyaWNzLg0KDQpEZXNwaXRlIHRoZSBpbnRlcmFjdGlvbiBtb2RlbCBjb3ZlcmluZyBtb3JlIGFyZWEsIHRoZSBbTUFFXShodHRwczovL3N0ZXBoZW5hbGx3cmlnaHQuY29tL2dvb2QtbWFlLXNjb3JlLykgYW5kIEVMUEQgYXJlIGJvdGggd29yc2UuIFRoaXMgbWF5IGJlIGR1ZSB0byB0aGVzZSB2YWx1ZXMgYmVpbmcgaW5mbGF0ZWQgYnkgdGhlIGxhcmdlciBudW1iZXIgb2YgcHJlZGljdG9ycyBpbiB0aGUgZXh0ZW5kZWQgbW9kZWwuIEZ1cnRoZXJtb3JlLCB0aGUgZXh0ZW5kZWQgbW9kZWwgY29udGFpbnMgbnVtZXJvdXMgdmFsdWVzIHdpdGggaGlnaGVyIHRoYW4gbm9ybWFsIE5lZmYgUmF0aW9zIHdoaWNoIGlzIGEgW2NhdXNlIGZvciBjb25jZXJuXShodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8yOTYwNTkvZWZmZWN0aXZlLXNhbXBsZS1zaXplLWdyZWF0ZXItdGhhbi1hY3R1YWwtc2FtcGxlLXNpemUpIGFib3V0IHRoZSB2YWxpZGl0eSBvZiB0aGUgbW9kZWwgYWxvbmcgd2l0aCBwcmVkaWN0b3JzIHRoYXQgd2VyZSBub3Qgc2lnbmlmaWNhbnQuDQoNCkRlc3BpdGUgaGF2aW5nIGxlc3MgcHJlZGljdG9ycywgdGhlIGludGVyYWN0aW9uIG1vZGVsIGdhcm5lcnMgbW9yZSBhcmVhIGluIGl0cyBwcmVkaWN0aXZlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gd2hpbGUgaGF2aW5nIGEgc2ltaWxhciBNQUUgYW5kIGdvb2QgRUxQRCBzY29yZSB0b28gKC0zMDk4NTkuNyB2cyAzMTQxOTcuMykgd2hpY2ggaXMgYSBtaW5vciAoXH4xJSkgZGlmZmVyZW5jZSBnaXZlbiB0aGUgc2l6ZSBvZiB0aGVzZSB2YWx1ZXMuIEhvd2V2ZXIsIHRoZXNlIGludGVyYWN0aW9uIHRlcm1zIGNvdWxkIGFsc28gYmUgaGF2aW5nIGFuIGFkdmVyc2UgZWZmZWN0IG9uIHRoZSBtb2RlbCBsZWFkaW5nIHRvIHRoZXNlIGdyZWF0ZXIgZXJyb3JzLg0KDQojIyMgT3ZlcmZpdHRpbmcNCg0KYGBge3J9DQpyYmluZChwX21haW4sIHBfZXh0X21haW4sIHBfZXh0X21haW5faW50KSANCnJiaW5kKGN2X21haW4kY3YsIGN2X2V4dGVuZCRjdiwgY3ZfaW50ZXJhY3QkY3YpDQpgYGANCg0KQmFzZWQgb24gdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcmF3IE1BRSB2YWx1ZXMgYW5kIHRoZSBjcm9zcy12YWxpZGF0aW9uIE1BRSB2YWx1ZXMsIG92ZXJmaXR0aW5nIGRvZXMgbm90IHNlZW0gdG8gcG9zZSBhIHRocmVhdCB0byB0aGVzZSBtb2RlbHMsIGhvd2V2ZXIgd2UgY2FuIG9ubHkgdHJ1bHkga25vdyB0aGlzIGlmIHdlIGFyZSBnaXZlbiBuZXcgZGF0YSBlbnRpcmVseSB0byB0ZXN0IHRoZSBtb2RlbHMuIEZ1cnRoZXJtb3JlLCB0aGUgZXh0ZW5kZWQgbW9kZWwgZG9lcyBoYXZlIGEgbGFyZ2VyICh0aG91Z2ggbW9zdGx5IG5lZ2xpZ2libGUgZHVlIHRvIGhvdyBzbWFsbCBpdCBpcykgZGlmZmVyZW5jZSBmcm9tIHRoZSBjcm9zcy12YWxpZGF0aW9uIE1BRSBjb21wYXJlZCB0byB0aGUgb3RoZXIgbW9kZWxzLg0KDQpXaGF0IGlzIHVuZG91YnRlZGx5IGNsZWFyIGlzIHRoYXQgYWRkaW5nIG51bWVyaWNhbCBwcmVkaWN0b3JzIHRvIHRoZSBtb2RlbCBkb2VzIGhlbHAgdGhlIHByZWRpY3Rpb25zIG92ZXJhbGwgYW5kIGlzIHNob3duIHdpdGggYm90aCB0aGUgZXh0ZW5kZWQgYW5kIGludGVyYWN0aW9uIG1vZGVsIGZhaXJpbmcgYmV0dGVyIHRoYW4gdGhlIG1haW4gbW9kZWwgaW4gZXZlcnkgYXNwZWN0Lg0KDQojIyBSZWdyZXNzaW9uIEluZmVyZW5jZQ0KDQpMZXQncyBzZWUgaG93IHRoZSAyIGJlc3QgbW9kZWxzIGZhaXIgd2hlbiBjb25kdWN0aW5nIGEgcXVpY2sgaHlwb3RoZXNpcyB0ZXN0IHRvIHNlZSB0aGUgUG9zdGVyaW9yIHByb2JhYmlsaXR5IG9mIGEgZ2FtZSBhcm91bmQgNDAgbWludXRlcyBsb25nDQoNCldlJ2xsIHVzZSB0aGUgcHJlZGljdGl2ZSBwb3N0ZXJpb3IgbW9kZWxzIHdlIGNyZWF0ZWQgYW5kIHZpc3VhbGl6ZWQgZWFybGllciB0byBjb25kdWN0IHRoZSB0ZXN0cw0KDQokJA0KSF8wOiBccGkgXGdlcSAyNTAwDQokJA0KDQokJA0KSF9hOiBccGkgPCAyNTAwDQokJA0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgcG9zdGVyaW9yIHNhbXBsZXMNCnBvc3Rlcmlvcl9zYW1wbGVzIDwtIGV4dF9tYWluX2ludF9wcmVkaWN0DQoNCnBfSDAgPC0gbWVhbihwb3N0ZXJpb3Jfc2FtcGxlcyA+PSAyNTAwKQ0KDQpwX0hhIDwtIG1lYW4ocG9zdGVyaW9yX3NhbXBsZXMgPCAyNTAwKQ0KDQojIFByaW50IHRoZSByZXN1bHRzDQpjYXQoIlBvc3RlcmlvciBwcm9iYWJpbGl0eSBvZiBIMCAoz4Ag4omlIDI1MDApOiIsIHBfSDApDQpjYXQoIlBvc3RlcmlvciBwcm9iYWJpbGl0eSBvZiBIYSAoz4AgPCAyNTAwKToiLCBwX0hhKQ0KYGBgDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHRoZSBwb3N0ZXJpb3Igc2FtcGxlcw0KcG9zdGVyaW9yX3NhbXBsZXMgPC0gZXh0X21haW5fcHJlZGljdA0KDQpwX0gwIDwtIG1lYW4ocG9zdGVyaW9yX3NhbXBsZXMgPj0gMjUwMCkNCg0KcF9IYSA8LSBtZWFuKHBvc3Rlcmlvcl9zYW1wbGVzIDwgMjUwMCkNCg0KIyBQcmludCB0aGUgcmVzdWx0cw0KY2F0KCJQb3N0ZXJpb3IgcHJvYmFiaWxpdHkgb2YgSDAgKM+AIOKJpSAyNTAwKToiLCBwX0gwKQ0KY2F0KCJQb3N0ZXJpb3IgcHJvYmFiaWxpdHkgb2YgSGEgKM+AIDwgMjUwMCk6IiwgcF9IYSkNCmBgYA0KDQpUaG91Z2ggdGhlIHR3byBtb2RlbHMgc2VlbWluZ2x5IHByZWRpY3QgdGhlIGFjdHVhbCBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEgd2VsbCwgdGhlIDIgbW9kZWxzIGhhdmUgdmVyeSBkaWZmZXJlbnQgb3V0Y29tZXMgd2hlbiBjb25kdWN0aW5nIHRoZSB0ZXN0Lg0KDQpUaGUgZXh0ZW5kZWQgbW9kZWwgZmF2b3JzIHRoZSBhbHRlcm5hdGUgaHlwb3RoZXNpcyB3aGlsZSB0aGUgaW50ZXJhY3Rpb24gbW9kZWwgY2xlYXJseSBmYXZvcnMgdGhlIG51bGwgaHlwb3RoZXNpcy4gVGhpcyBib3RoIGNvaW5jaWRlcyB3aXRoIHdoYXQgd2Ugc2F3IGJlZm9yZSBmcm9tIHRoZSB2aXN1YWxpemF0aW9ucyBhbmQgdGVsbHMgdXMgdGhhdCB0aGUgZXh0ZW5kZWQgbW9kZWwgZmF2b3JzIGdhbWVzIHRoYXQgYXJlIHNob3J0ZXIgd2hpbGUgdGhlIGludGVyYWN0aW9uIG1vZGVsIGV4cGVjdHMgZ2FtZXMgbGlrZSB0aGlzIHRvIHRha2UgbG9uZ2VyLg0KDQpUaGlzIGlzIGFsc28gc29tZXRoaW5nIG9uZSBzaG91bGQgY29uc2lkZXIgd2hlbiBjaG9vc2luZyB0aGUgbW9kZWwuIFdoZXRoZXIgb3Igbm90IHRoZSBwcmVkaWN0aW9ucyB0aGVtc2VsdmVzIHNlZW0gcmVhbGlzdGljIGdpdmVuIHRoZSBzY2VuYXJpby9jaXJjdW1zdGFuY2VzLg0KDQpUbyBiYWNrIHVwIHdoYXQgSSdtIHNheWluZywgQmFyb24gTmFzaG9yIHNwYXducyBhdCAyMCBtaW51dGVzIGFuZCByZXNwYXducyBldmVyeSA2IG1pbnV0ZXMuIFNpbmNlIDMgQmFyb25zIGFyZSBraWxsZWQgaW4gYm90aCBzY2VuYXJpb3MsIHRoZSBnYW1lIHdvdWxkIGhhdmUgdG8gYmUgYXQgbGVhc3QgMzggbWludXRlcyBhbmQgb25seSBpZiB0aGUgdGVhbXMga2lsbCB0aGUgQmFyb24gSU1NRURJQVRFTFkgKHdoaWNoIGlzIHVzdWFsbHkgbmV2ZXIgdGhlIGNhc2UpLiBUaGVyZWZvcmUsIG9uZSB3b3VsZCBleHBlY3QgbWFqb3JpdHkgb2YgdGhlIGFyZWEgdG8gbGllIHdpdGhpbiB0aGUgXH40MCBtaW51dGUgcmFuZ2UgYXQgdGhlIHZlcnkgbGVhc3QgZm9yIHdoZW4gdGhlIGdhbWUgd291bGQgZW5kLg0KDQojIyBDb25jbHVzaW9uDQoNCkluIGNvbmNsdXNpb24sIGl0IGlzIGEgY2hvaWNlIGJldHdlZW4gd2hldGhlciBvbmUgd2FudHMgdG8gdGFrZSB0aGUgY2hhbmNlIG9mIHBsYXlpbmcgd2l0aCBhIG1vZGVsIHRoYXQgbWF5IGJlIHN1c2NlcHRpYmxlIHRvIG92ZXJmaXR0aW5nIGZvciB0aGUgc2FrZSBvZiBwb3RlbnRpYWxseSBsZXNzIGVycm9yIG9yIGEgc2xpZ2h0bHkgaGlnaGVyIGVycm9yIG1vZGVsIHRoYXQgaGFzIGludGVyYWN0aW9ucyBidXQgd291bGQgYmUgbGVzcyBsaWtlbHkgdG8gYmUgc3VzY2VwdGlibGUgdG8gb3ZlcmZpdHRpbmcuDQoNClBlcnNvbmFsbHksIEkgc2F5IHRoYXQgdGhlIHJlZmluZWQgaW50ZXJhY3Rpb24gbW9kZWwgaXMgdGhlIGJlc3QgbW9kZWwgdG8gdXNlIGZvciBwcmVkaWN0aW5nIHRoZSBnYW1lIGR1cmF0aW9uIG9mIGEgTE9MIG1hdGNoIGdpdmVuIG91ciBkYXRhIGR1ZSB0byBpdHMgY29tcGFyYWJsZSBNQUUgYWxvbmcgd2l0aCBiZXR0ZXIgcG9zdGVyaW9yIHByZWRpY3RpdmUgYWJpbGl0aWVzIGFuZCBsYWNrIG9mIGV2aWRlbmNlIGZvciBvdmVyZml0dGluZy4gRnVydGhlcm1vcmUsIGJhc2VkIG9uIG15IGV4cGVyaWVuY2UgYW5kIGdhbWUgcnVsZXMsIHRoZSBwcmVkaWN0aXZlIHZhbHVlcyBpdCBjb21lcyB1cCB3aXRoIGVuZCB1cCBiZWluZyBtdWNoIG1vcmUgcmVhbGlzdGljIGluIHRoZSBncmFuZCBzY2hlbWUgb2YgdGhpbmdzIGFuZCB3aXRoIG90aGVyIGZhY3RvcnMgc3VjaCBhcyByZXNwYXduIHRpbWUgY29uc2lkZXJlZC4NCg0KIyMgUmVmZXJlbmNlcw0KDQotICAgPGh0dHBzOi8vd3d3LmJheWVzcnVsZXNib29rLmNvbS8+DQoNCi0gICA8aHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMjk2MDU5L2VmZmVjdGl2ZS1zYW1wbGUtc2l6ZS1ncmVhdGVyLXRoYW4tYWN0dWFsLXNhbXBsZS1zaXplPg0KDQotICAgPGh0dHBzOi8vc3RlcGhlbmFsbHdyaWdodC5jb20vZ29vZC1tYWUtc2NvcmUvPg0KDQotICAgPGh0dHBzOi8vZGlzY291cnNlLm1jLXN0YW4ub3JnL3QvdW5kZXJzdGFuZGluZy1sb29pYy8xMzQwOS82Pg0KDQotICAgPGh0dHBzOi8vZGlzY291cnNlLm1jLXN0YW4ub3JnL3QvcHJvanByZWQtZWxwZC1nb2VzLWRvd24tYW5kLXJtc2UtZ29lcy11cC1hZnRlci14LXZhcmlhYmxlcy8xMzE1Mz4NCg0KLSAgIDxodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8zMTM1NjQvaG93LWRvZXMtYmF5ZXNpYW4tYW5hbHlzaXMtbWFrZS1hY2N1cmF0ZS1wcmVkaWN0aW9ucy11c2luZy1zdWJqZWN0aXZlbHktY2hvc2VuLXA+DQoNCi0gICBbaHR0cHM6Ly9tZWRpdW0uY29tL1xAb29lbW1hODMvaW50ZXJwcmV0YXRpb24tb2YtZXZhbHVhdGlvbi1tZXRyaWNzLWZvci1yZWdyZXNzaW9uLWFuYWx5c2lzLW1hZS1tc2Utcm1zZS1tYXBlLXItc3F1YXJlZC1hbmQtNTY5M2I2MWE5ODMzXShodHRwczovL21lZGl1bS5jb20vQG9vZW1tYTgzL2ludGVycHJldGF0aW9uLW9mLWV2YWx1YXRpb24tbWV0cmljcy1mb3ItcmVncmVzc2lvbi1hbmFseXNpcy1tYWUtbXNlLXJtc2UtbWFwZS1yLXNxdWFyZWQtYW5kLTU2OTNiNjFhOTgzMyl7LnVyaX0NCg0KLSAgIDxodHRwczovL3N0YXRpc3RpY3NieWppbS5jb20vcmVncmVzc2lvbi9vdmVyZml0dGluZy1yZWdyZXNzaW9uLW1vZGVscy8+DQoNCi0gICA8aHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvOTA1My9ob3ctZG9lcy1jcm9zcy12YWxpZGF0aW9uLW92ZXJjb21lLXRoZS1vdmVyZml0dGluZy1wcm9ibGVtPg0KDQojIyBIb25vciBQbGVkZ2UNCg0KT24gbXkgaG9ub3IsIEkgaGF2ZSBuZWl0aGVyIHJlY2VpdmVkIG5vciBnaXZlbiBhbnkgdW5hdXRob3JpemVkIGFzc2lzdGFuY2Ugb24gdGhpcyBwcm9qZWN0DQoNClNpZ25lZDogVGhvbWFzIENocmlzdG8gKHRqYzI2MCkNCg==