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"      
 [3] "gameDuration"       "seasonId"          
 [5] "winner"             "firstBlood"        
 [7] "firstTower"         "firstInhibitor"    
 [9] "firstBaron"         "firstDragon"       
[11] "firstRiftHerald"    "t1_champ1id"       
[13] "t1_champ1_sum1"     "t1_champ1_sum2"    
[15] "t1_champ2id"        "t1_champ2_sum1"    
[17] "t1_champ2_sum2"     "t1_champ3id"       
[19] "t1_champ3_sum1"     "t1_champ3_sum2"    
[21] "t1_champ4id"        "t1_champ4_sum1"    
[23] "t1_champ4_sum2"     "t1_champ5id"       
[25] "t1_champ5_sum1"     "t1_champ5_sum2"    
[27] "t1_towerKills"      "t1_inhibitorKills" 
[29] "t1_baronKills"      "t1_dragonKills"    
[31] "t1_riftHeraldKills" "t1_ban1"           
[33] "t1_ban2"            "t1_ban3"           
[35] "t1_ban4"            "t1_ban5"           
[37] "t2_champ1id"        "t2_champ1_sum1"    
[39] "t2_champ1_sum2"     "t2_champ2id"       
[41] "t2_champ2_sum1"     "t2_champ2_sum2"    
[43] "t2_champ3id"        "t2_champ3_sum1"    
[45] "t2_champ3_sum2"     "t2_champ4id"       
[47] "t2_champ4_sum1"     "t2_champ4_sum2"    
[49] "t2_champ5id"        "t2_champ5_sum1"    
[51] "t2_champ5_sum2"     "t2_towerKills"     
[53] "t2_inhibitorKills"  "t2_baronKills"     
[55] "t2_dragonKills"     "t2_riftHeraldKills"
[57] "t2_ban1"            "t2_ban2"           
[59] "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"      
 [3] "gameDuration"       "seasonId"          
 [5] "winner"             "firstBlood"        
 [7] "firstTower"         "firstInhibitor"    
 [9] "firstBaron"         "firstDragon"       
[11] "firstRiftHerald"    "t1_towerKills"     
[13] "t1_inhibitorKills"  "t1_baronKills"     
[15] "t1_dragonKills"     "t1_riftHeraldKills"
[17] "t2_towerKills"      "t2_inhibitorKills" 
[19] "t2_baronKills"      "t2_dragonKills"    
[21] "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 
 Min.   :3.215e+09   Min.   :1.497e+12   Min.   : 477  
 1st Qu.:3.292e+09   1st Qu.:1.502e+12   1st Qu.:1632  
 Median :3.320e+09   Median :1.504e+12   Median :1895  
 Mean   :3.306e+09   Mean   :1.503e+12   Mean   :1930  
 3rd Qu.:3.327e+09   3rd Qu.:1.504e+12   3rd Qu.:2196  
 Max.   :3.332e+09   Max.   :1.505e+12   Max.   :4728  
    seasonId winner    firstBlood firstTower firstInhibitor
 Min.   :9   1:22867   1:23151    1:23248    1:23054       
 1st Qu.:9   2:22347   2:22063    2:21966    2:22160       
 Median :9                                                 
 Mean   :9                                                 
 3rd Qu.:9                                                 
 Max.   :9                                                 
 firstBaron firstDragon firstRiftHerald t1_towerKills   
 0:14617    0:  434     0:21922         Min.   : 0.000  
 1:14469    1:22258     1:11933         1st Qu.: 3.000  
 2:16128    2:22522     2:11359         Median : 7.000  
                                        Mean   : 6.173  
                                        3rd Qu.:10.000  
                                        Max.   :11.000  
 t1_inhibitorKills t1_baronKills    t1_dragonKills 
 Min.   : 0.000    Min.   :0.0000   Min.   :0.000  
 1st Qu.: 0.000    1st Qu.:0.0000   1st Qu.:0.000  
 Median : 1.000    Median :0.0000   Median :1.000  
 Mean   : 1.159    Mean   :0.4173   Mean   :1.483  
 3rd Qu.: 2.000    3rd Qu.:1.0000   3rd Qu.:2.000  
 Max.   :10.000    Max.   :5.0000   Max.   :6.000  
 t1_riftHeraldKills t2_towerKills    t2_inhibitorKills
 Min.   :0.0000     Min.   : 0.000   Min.   : 0.000   
 1st Qu.:0.0000     1st Qu.: 2.000   1st Qu.: 0.000   
 Median :0.0000     Median : 7.000   Median : 1.000   
 Mean   :0.2639     Mean   : 6.014   Mean   : 1.122   
 3rd Qu.:1.0000     3rd Qu.:10.000   3rd Qu.: 2.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 6.6e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.66 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.198 seconds (Warm-up)
Chain 1:                24.632 seconds (Sampling)
Chain 1:                24.83 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: 0.193 seconds (Warm-up)
Chain 2:                24.084 seconds (Sampling)
Chain 2:                24.277 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 1.6e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.16 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.225 seconds (Warm-up)
Chain 3:                23.984 seconds (Sampling)
Chain 3:                24.209 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.4e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.14 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.226 seconds (Warm-up)
Chain 4:                23.894 seconds (Sampling)
Chain 4:                24.12 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.3e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.13 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: 3.402 seconds (Warm-up)
Chain 1:                0.138 seconds (Sampling)
Chain 1:                3.54 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.1e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.11 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.386 seconds (Warm-up)
Chain 2:                0.212 seconds (Sampling)
Chain 2:                2.598 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 1.1e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.11 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: 3.215 seconds (Warm-up)
Chain 3:                0.133 seconds (Sampling)
Chain 3:                3.348 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.1e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.11 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: 2.956 seconds (Warm-up)
Chain 4:                0.13 seconds (Sampling)
Chain 4:                3.086 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.5e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.25 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.243 seconds (Warm-up)
Chain 1:                23.706 seconds (Sampling)
Chain 1:                23.949 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.5e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.15 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.22 seconds (Warm-up)
Chain 2:                23.406 seconds (Sampling)
Chain 2:                23.626 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 1.5e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.15 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.235 seconds (Warm-up)
Chain 3:                23.581 seconds (Sampling)
Chain 3:                23.816 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.237 seconds (Warm-up)
Chain 4:                23.737 seconds (Sampling)
Chain 4:                23.974 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 2e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.2 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.273 seconds (Warm-up)
Chain 1:                17.357 seconds (Sampling)
Chain 1:                17.63 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 1.5e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.15 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.234 seconds (Warm-up)
Chain 2:                17.045 seconds (Sampling)
Chain 2:                17.279 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 2e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.2 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.249 seconds (Warm-up)
Chain 3:                16.572 seconds (Sampling)
Chain 3:                16.821 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 1.5e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.15 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.231 seconds (Warm-up)
Chain 4:                16.656 seconds (Sampling)
Chain 4:                16.887 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.7
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  17400
t1_dragonKills:t2_dragonKills 0.0  1.0  20373
sigma                         0.0  1.0  18046
mean_PPD                      0.0  1.0  17889
log-posterior                 0.0  1.0   9182

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"))
`stat_bin()` using `bins = 30`. Pick better value with
`binwidth`.

mcmc_hist(posterior_samples2, pars = c("t1_baronKills:t2_baronKills"))
`stat_bin()` using `bins = 30`. Pick better value with
`binwidth`.

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.5e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.15 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 seconds (Warm-up)
Chain 1:                16.219 seconds (Sampling)
Chain 1:                17.219 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 2.2e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.22 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.142 seconds (Warm-up)
Chain 2:                20.153 seconds (Sampling)
Chain 2:                21.295 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 2.3e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.23 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.895 seconds (Warm-up)
Chain 3:                15.819 seconds (Sampling)
Chain 3:                16.714 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 2e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.2 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.88 seconds (Warm-up)
Chain 4:                15.902 seconds (Sampling)
Chain 4:                16.782 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.0001821    0.9999929    0.9999398    0.9998713    1.0001601 
firstDragon2  firstBaron1  firstBaron2        sigma 
   1.0001982    0.9998699    0.9999778    0.9999692 
neff_ratio(main)
 (Intercept)      winner2  firstBlood2  firstTower2 firstDragon1 
     0.51960      1.05495      1.28565      1.17810      0.50175 
firstDragon2  firstBaron1  firstBaron2        sigma 
     0.49995      0.98030      0.97875      1.05095 

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 5.2e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.52 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.402 seconds (Warm-up)
Chain 1:                23.404 seconds (Sampling)
Chain 1:                24.806 seconds (Total)
Chain 1: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 2).
Chain 2: 
Chain 2: Gradient evaluation took 2.2e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.22 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.461 seconds (Warm-up)
Chain 2:                23.609 seconds (Sampling)
Chain 2:                25.07 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.524 seconds (Warm-up)
Chain 3:                23.227 seconds (Sampling)
Chain 3:                24.751 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 2.4e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.24 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.53 seconds (Warm-up)
Chain 4:                22.993 seconds (Sampling)
Chain 4:                24.523 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 
        1.0002256         1.0002342         0.9998792 
      firstTower2      firstDragon1      firstDragon2 
        0.9999455         1.0003390         1.0003823 
      firstBaron1       firstBaron2     t1_towerKills 
        1.0002441         0.9999394         0.9999148 
t1_inhibitorKills     t1_baronKills    t1_dragonKills 
        1.0000205         1.0005001         1.0000165 
    t2_towerKills t2_inhibitorKills     t2_baronKills 
        1.0000909         0.9998952         0.9999024 
   t2_dragonKills             sigma 
        0.9999282         1.0000341 
neff_ratio(ext_main)
      (Intercept)           winner2       firstBlood2 
          0.59525           0.91430           2.09050 
      firstTower2      firstDragon1      firstDragon2 
          1.52335           0.49105           0.49450 
      firstBaron1       firstBaron2     t1_towerKills 
          0.70805           0.74320           0.77175 
t1_inhibitorKills     t1_baronKills    t1_dragonKills 
          1.02380           0.68870           0.96135 
    t2_towerKills t2_inhibitorKills     t2_baronKills 
          0.80965           1.10315           0.75605 
   t2_dragonKills             sigma 
          0.99900           1.19210 

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 2.2e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.22 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.415 seconds (Warm-up)
Chain 1:                24.649 seconds (Sampling)
Chain 1:                26.064 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: 1.797 seconds (Warm-up)
Chain 2:                24.903 seconds (Sampling)
Chain 2:                26.7 seconds (Total)
Chain 2: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 3).
Chain 3: 
Chain 3: Gradient evaluation took 2.3e-05 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.23 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.53 seconds (Warm-up)
Chain 3:                25.085 seconds (Sampling)
Chain 3:                26.615 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'continuous' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 2e-05 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.2 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.312 seconds (Warm-up)
Chain 4:                24.619 seconds (Sampling)
Chain 4:                25.931 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.0001372                     1.0002078 
                 firstDragon2                   firstBaron1 
                    1.0001533                     1.0003050 
                  firstBaron2                 t1_baronKills 
                    1.0002689                     1.0002779 
               t1_dragonKills                 t2_baronKills 
                    1.0000343                     1.0004493 
               t2_dragonKills   t1_baronKills:t2_baronKills 
                    1.0002614                     1.0004149 
t1_dragonKills:t2_dragonKills                         sigma 
                    0.9999161                     0.9999662 

Rhat looks satisfactory for all predictors

neff_ratio(ext_main_int)
                  (Intercept)                  firstDragon1 
                      0.59695                       0.51615 
                 firstDragon2                   firstBaron1 
                      0.51000                       0.56870 
                  firstBaron2                 t1_baronKills 
                      0.56995                       0.55110 
               t1_dragonKills                 t2_baronKills 
                      0.72620                       0.54695 
               t2_dragonKills   t1_baronKills:t2_baronKills 
                      0.71520                       0.68670 
t1_dragonKills:t2_dragonKills                         sigma 
                      0.87645                       1.14675 

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)

cv_extend <- prediction_summary_cv(
  model = ext_main, data = lol_data, k = 10)

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

Loo Diagnostics

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

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 \]

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

LS0tDQp0aXRsZTogIkxlYWd1ZSBvZiBMZWdlbmRzIEJheWVzaWFuIFJlZ3Jlc3Npb24gQW5hbHlzaXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBUaGUgRGF0YQ0KDQpUaGUgZGF0YXNldCBJIGhhdmUgY2hvc2VuIHRvIGNvbmR1Y3QgbXkgcmVncmVzc2lvbiBhbmFseXNpcyBpcyB0aGUgWyhMb0wpIExlYWd1ZSBvZiBMZWdlbmRzIFJhbmtlZCBHYW1lcyBkYXRhc2V0XShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2RhdGFzbmFlay9sZWFndWUtb2YtbGVnZW5kcy8/c2VsZWN0PWdhbWVzLmNzdikuIFRoaXMgZGF0YSBsb29rcyBhdCByYW5rZWQgTGVhZ3VlIG9mIExlZ2VuZHMgZ2FtZXMgcGxheWVkIGR1cmluZyBzZWFzb24gOSBpbiB0aGUgRVVXIHJlZ2lvbi4gVGhpcyBkYXRhc2V0IHdhcyBjb2xsZWN0ZWQgdXNpbmcgdGhlIFJpb3QgR2FtZXMgQVBJLCB3aGljaCBtYWtlcyBpdCBlYXN5IHRvIGxvb2sgdXAgYW5kIGNvbGxlY3QgaW5mb3JtYXRpb24gb24gYSB1c2VycyByYW5rZWQgaGlzdG9yeSBhbmQgY29sbGVjdCB0aGVpciBnYW1lcy4NCg0KYGBge3Igc2V0dXAsIHJlc3VsdHM9RiwgbWVzc2FnZT1GQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIExvYWQgcGFja2FnZXMNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocnN0YW5hcm0pDQpsaWJyYXJ5KGJheWVzcGxvdCkNCmxpYnJhcnkoYmF5ZXNydWxlcykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0aWR5YmF5ZXMpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShicm9vbS5taXhlZCkNCmxpYnJhcnkoaW50ZXJhY3Rpb25zKQ0KYGBgDQoNClRvIHByZWZhY2UgdGhpcyBhbmFseXNpcywgb25lIG11c3QgZmlyc3QgdW5kZXJzdGFuZCB0aGUgZ2FtZSBvZiBMZWFndWUgb2YgTGVnZW5kcywgb3IgTE9MIGZvciBzaG9ydC4gSW4gTE9MLCB0d28gNSBwZXJzb24gdGVhbXMgZmlnaHQgdG8gZGVzdHJveSBlYWNoIG90aGVycyBuZXh1cyBhbmQgZ2V0IHRvIGNob29zZSBhbmQgYmFuIGEgY2hhbXBpb24sIGVhY2ggd2l0aCBhIGRpZmZlcmVudCBzZXQgb2Ygc2tpbGxzIGFuZCBhYmlsaXRpZXMsIGFuZCAyIHN1bW1vbmVyIHNwZWxscyB0byBkbyBzby4gQWxvbmcgdGhlIHdheSwgdGhlIHBsYXllcnMgbXVzdCBib3RoIGZpZ2h0IGVhY2ggb3RoZXIsIGRlc3Ryb3kgb2JqZWN0aXZlcyBzdWNoIGFzIHRvd2VycyBhbmQgaW5oaWJpdG9ycywgYW5kIHN0cmF0ZWdpemUgZWZmZWN0aXZlbHkgdG8gcmVhY2ggdGhlIG9wcG9uZW50J3MgbmV4dXMuIFRoZXJlIGFyZSBhbHNvIHNldmVyYWwgbW9uc3RlcnMgdGhhdCB0ZWFtcyBjYW4gZGVmZWF0IHRvIGJ1ZmYgdGhlbSBhcyB3ZWxsLCBzdWNoIGFzIFJpZnQgSGVyYWxkLCBCYXJvbiBOYXNob3IsIGFuZCBhbiBhc3NvcnRlZCBzZXQgb2YgRHJhZ29ucy4NCg0KV2Ugd2lsbCB1c2UgYGxvbF9kYXRhYCB0byBidWlsZCB2YXJpb3VzIG1vZGVscyBvZiBMZWFndWUgb2YgTGVnZW5kcyBgZ2FtZUR1cmF0aW9uYC4gVGhyb3VnaG91dCwgd2UnbGwgdXRpbGl6ZSB3ZWFrbHkgaW5mb3JtYXRpdmUgcHJpb3JzIGFuZCBhIGJhc2ljIHVuZGVyc3RhbmRpbmcgdGhhdCBMT0wgZ2FtZXMgdXN1YWxseSBhcmUgWzMwIG1pbnV0ZXNdKGh0dHBzOi8vd3d3LmxlYWd1ZW9mZ3JhcGhzLmNvbS9zdGF0cy9nYW1lLWR1cmF0aW9ucykgYnV0IGNhbiByYW5nZSBmcm9tIDI1LTM1IG1pbnV0ZXMuIFdlIHdpbGwgYXNzZXMgYW4gYXJyYXkgb2YgZGlmZmVyZW50IHByZWRpY3RvcnMgYW5kIGNvbWJpbmF0aW9ucyB0byBkZXRlcm1pbmUgd2hhdCBjYW4gcHJvdmlkZSB1cyB0aGUgYmVzdCBmaXQgZm9yIHByZWRpY3RpbmcgdGhlIGdhbWUgZHVyYXRpb24gb2YgYSBMZWFndWUgb2YgTGVnZW5kcyBnYW1lLiBBIGJhc2UgY3JpdGVyaWEgZm9yIHRoZXNlIGdhbWVzIGlzIHRoZXJlIGF0IGxlYXN0IG5lZWRzIHRvIGJlIG9uZSB0b3dlciwgb25lIGluaGliaXRvciwgYW5kIG9uZSBjaGFtcGlvbiBraWxsIGZvciBlYWNoIGdhbWUuIFRoaXMgaXMgdG8gbWl0aWdhdGUgdGhlIGFtb3VudCBvZiBnYW1lcyB0aGF0IHRlYW1zIG1heSBoYXZlIGZvcmZlaXRlZCBkdWUgdG8gYSBwbGF5ZXIgbGVhdmluZyBvciB0cm9sbGluZy4NCg0KYGBge3J9DQpsb2xfZGF0YSA8LSByZWFkLmNzdigiZ2FtZXMuY3N2IiwNCiAgICAgICAgICAgICAgICAgICAgc2VwPSIsIiwNCiAgICAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncz1jKCIiLCIgIiwiTkEiLCJOL0EiKQ0KKQ0KaGVhZChsb2xfZGF0YSkNCmBgYA0KDQojIyBTZXR0aW5nIFVwIHRoZSBEYXRhDQoNClRyYW5zZm9ybWluZyBkYXRhIHRvIGZpdCBiYXNlbGluZSBvZiBhdCBsZWFzdCBvbmUga2lsbCwgdG93ZXIsIGFuZCBpbmhpYml0b3INCg0KYGBge3J9DQpsb2xfZGF0YSA8LSBsb2xfZGF0YVshKGxvbF9kYXRhJGZpcnN0Qmxvb2QgPT0gMCB8IGxvbF9kYXRhJGZpcnN0VG93ZXIgPT0gMCB8IGxvbF9kYXRhJGZpcnN0SW5oaWJpdG9yID09IDApLCBdDQpgYGANCg0KYGBge3J9DQpucm93KGxvbF9kYXRhKQ0KYGBgDQoNCmBgYHtyfQ0KY29sbmFtZXMobG9sX2RhdGEpDQpgYGANCg0KV2UgY2FuIGNsZWFuIHVwIG91ciBkYXRhIGJ5IG9taXR0aW5nIHVubmVjZXNzYXJ5IGNvbHVtbnMNCg0KYGBge3J9DQpsb2xfZGF0YSA8LSBzdWJzZXQobG9sX2RhdGEsIHNlbGVjdCA9IC1jKHQyX2JhbjEsIHQyX2JhbjIsIHQyX2JhbjMsIHQyX2JhbjQsIHQyX2JhbjUsIHQxX2JhbjEsIHQxX2JhbjIsIHQxX2JhbjMsIHQxX2JhbjQsIHQxX2JhbjUsIHQxX2NoYW1wMV9zdW0xLCB0MV9jaGFtcDJfc3VtMSwgdDFfY2hhbXAzX3N1bTEsIHQxX2NoYW1wNF9zdW0xLCB0MV9jaGFtcDVfc3VtMSwgdDJfY2hhbXA1X3N1bTEsIHQyX2NoYW1wMV9zdW0xLCB0Ml9jaGFtcDJfc3VtMSwgdDJfY2hhbXAzX3N1bTEsIHQyX2NoYW1wNF9zdW0xLCB0Ml9jaGFtcDVfc3VtMSwgdDFfY2hhbXAxX3N1bTIsIHQxX2NoYW1wMl9zdW0yLCB0MV9jaGFtcDNfc3VtMiwgdDFfY2hhbXA0X3N1bTIsIHQxX2NoYW1wNV9zdW0yLCB0Ml9jaGFtcDFfc3VtMiwgdDJfY2hhbXAxX3N1bTIsIHQyX2NoYW1wMl9zdW0yLCB0Ml9jaGFtcDNfc3VtMiwgdDJfY2hhbXA0X3N1bTIsIHQyX2NoYW1wNV9zdW0yLCB0MV9jaGFtcDFpZCwgdDFfY2hhbXAyaWQsIHQxX2NoYW1wM2lkLCB0MV9jaGFtcDRpZCwgdDFfY2hhbXA1aWQsIHQyX2NoYW1wMWlkLCB0Ml9jaGFtcDJpZCwgdDJfY2hhbXAzaWQsIHQyX2NoYW1wNGlkLCB0Ml9jaGFtcDVpZCkpDQoNCmNvbG5hbWVzKGxvbF9kYXRhKQ0KYGBgDQoNCldlIGNhbiBhbHNvIGNvdmVydCBzb21lIG9mIHRoZSBjb2x1bW5zIHRvIGZhY3RvcnMgZm9yIHRlYW0gMSBhbmQgdGVhbSAyIGZvciBlYXNlIG9mIHZpc3VhbGl6YXRpb24gYW5kIGludGVycHJldGF0aW9uDQoNCmBgYHtyfQ0KbG9sX2RhdGEkd2lubmVyIDwtIGFzLmZhY3Rvcihsb2xfZGF0YSR3aW5uZXIpDQpsb2xfZGF0YSRmaXJzdEJsb29kIDwtIGFzLmZhY3Rvcihsb2xfZGF0YSRmaXJzdEJsb29kKQ0KbG9sX2RhdGEkZmlyc3RUb3dlciA8LSBhcy5mYWN0b3IobG9sX2RhdGEkZmlyc3RUb3dlcikNCmxvbF9kYXRhJGZpcnN0SW5oaWJpdG9yIDwtIGFzLmZhY3Rvcihsb2xfZGF0YSRmaXJzdEluaGliaXRvcikNCmxvbF9kYXRhJGZpcnN0QmFyb24gPC0gYXMuZmFjdG9yKGxvbF9kYXRhJGZpcnN0QmFyb24pDQpsb2xfZGF0YSRmaXJzdERyYWdvbiA8LSBhcy5mYWN0b3IobG9sX2RhdGEkZmlyc3REcmFnb24pDQpsb2xfZGF0YSRmaXJzdFJpZnRIZXJhbGQgPC0gYXMuZmFjdG9yKGxvbF9kYXRhJGZpcnN0UmlmdEhlcmFsZCkNCmBgYA0KDQpgYGB7cn0NCmhlYWQobG9sX2RhdGEpDQpgYGANCg0KIyMgU3VtbWFyeSBBbmFseXNpcw0KDQpIZXJlIGFyZSBzb21lIHZpc3VhbGl6YXRpb25zIGFuZCBzdGF0aXN0aWNzIHRvIGhlbHAgYmV0dGVyIHVuZGVyc3RhbmQgb3VyIGRhdGEgYW5kIHdoYXQgd2UgYXJlIHdvcmtpbmcgd2l0aC4NCg0KYGBge3J9DQpzdW1tYXJ5KGxvbF9kYXRhKQ0KYGBgDQoNClNvbWUgY29uY2x1c2lvbnMgd2UgY2FuIG1ha2UgaGVyZToNCg0KLSAgIFRoZSBtZWFuIGdhbWUgdGltZSBpcyBhcm91bmQgMTkzMCBzZWNvbmRzIChyb3VnaGx5IDMyIG1pbnV0ZXMpDQoNCi0gICBHYW1lIGR1cmF0aW9uIGxpZXMgbW9zdGx5IGJldHdlZW4gMTYzMiBhbmQgMjE5Ng0KDQotICAgVGVhbSAxIGhhcyB3b24gbW9yZSBnYW1lcywgaGFzIG1vcmUgZmlyc3QgYmxvb2RzLCB0b3dlcnMsIGluaGliaXRvcnMsIGFuZCByaWZ0IGhlcmFsZHMNCg0KLSAgIFRlYW0gMiBoYXMgbW9yZSBmaXJzdCBiYXJvbnMgYW5kIGRyYWdvbnMuDQoNCi0gICBUZWFtIDEncyB0b3dlciwgaW5oaWJpdG9yLCBhbmQgaGVyYWxkIGtpbGxzIGFyZSBoaWdoZXIgdGhhbiBUZWFtIDIncyB3aGljaCBpcyByZWZsZWN0ZWQgZnJvbSB0aGVtIGdldHRpbmcgdGhlIGZpcnN0IG9mIGVhY2ggbW9yZSBvZnRlbiB0aGFuIG5vdC4NCg0KLSAgIFRlYW0gMidzIGRyYWdvbiBhbmQgYmFyb24ga2lsbHMgYXJlIGhpZ2hlciB0aGFuIFRlYW0gMSdzIHdoaWNoIGlzIHJlZmxlY3RlZCBmcm9tIHRoZW0gZ2V0dGluZyB0aGUgZmlyc3Qgb2YgZWFjaCBtb3JlIG9mdGVuIHRoYW4gbm90Lg0KDQojIyBWaXN1YWxpemF0aW9ucw0KDQpXZSBjYW4gdXNlIGdncGxvdCBkbyB2aXN1YWxpemUgc29tZSBvZiBvdXIgZmluZGluZ3MNCg0KIyMjIFJlc3BvbnNlIERpc3RyaWJ1dGlvbg0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSBnYW1lRHVyYXRpb24pKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNjAsIGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKHggPSAiR2FtZSBEdXJhdGlvbiAoc2Vjb25kcykiLCB5ID0gIkZyZXF1ZW5jeSIsIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBHYW1lIER1cmF0aW9uIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpHYW1lIGR1cmF0aW9uIHNlZW1zIHRvIGJlIGEgYml0IHJpZ2h0LXNrZXdlZA0KDQojIyMgRmlyc3QgQ29tcGFyaXNvbnMNCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gd2lubmVyLCBmaWxsID0gd2lubmVyKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT05KSkgKw0KICAgICAgbGFicyh5ID0gIldpbm5lcnMiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IGZpcnN0Qmxvb2QsIGZpbGwgPSBmaXJzdEJsb29kKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT05KSkgKw0KICAgICAgbGFicyh5ID0gIkZpcnN0IEJsb29kcyIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gZmlyc3RUb3dlciwgZmlsbCA9IGZpcnN0VG93ZXIpKSArIA0KICAgICAgZ2VvbV9iYXIoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTkpKSArDQogICAgICBsYWJzKHkgPSAiRmlyc3QgVG93ZXJzIikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSBmaXJzdEluaGliaXRvciwgZmlsbCA9IGZpcnN0SW5oaWJpdG9yKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT05KSkgKw0KICAgICAgbGFicyh5ID0gIkZpcnN0IEluaGliaXRvcnMiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IGZpcnN0RHJhZ29uLCBmaWxsID0gZmlyc3REcmFnb24pKSArIA0KICAgICAgZ2VvbV9iYXIoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTkpKSArDQogICAgICBsYWJzKHkgPSAiRmlyc3QgRHJhZ29ucyIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gZmlyc3RSaWZ0SGVyYWxkLCBmaWxsID0gZmlyc3RSaWZ0SGVyYWxkKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT05KSkgKw0KICAgICAgbGFicyh5ID0gIkZpcnN0IFJpZnQgSGVyYWxkcyIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gZmlyc3RCYXJvbiwgZmlsbCA9IGZpcnN0QmFyb24pKSArIA0KICAgICAgZ2VvbV9iYXIoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTkpKSArDQogICAgICBsYWJzKHkgPSAiRmlyc3QgQmFyb25zIikNCmBgYA0KDQpUaGVzZSBhbGwgYXJlIGNvbnNpc3RlbnQgd2l0aCBvdXIgcHJldmlvdXMgc3VtbWFyeSBmaW5kaW5ncyBhYm91dCBmaXJzdCBjYXRlZ29yaWVzDQoNCiMjIyBUb3RhbCBPYmplY3RpdmVzIENvbXBhcmlzb25zDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IHQxX3Rvd2VyS2lsbHMpKSArIA0KICAgICAgZ2VvbV9iYXIoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICAgICAgbGFicyh5ID0gIkNvdW50IikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSB0Ml90b3dlcktpbGxzKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgICAgIGxhYnMoeSA9ICJDb3VudCIpDQpgYGANCg0KYGBge3J9DQp0b3dlcl9raWxscyA8LSBkYXRhLmZyYW1lKFRlYW0gPSBjKCJUMSIsICJUMiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBLaWxscyA9IGMoc3VtKGxvbF9kYXRhJHQxX3Rvd2VyS2lsbHMsIG5hLnJtID0gVFJVRSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGxvbF9kYXRhJHQyX3Rvd2VyS2lsbHMsIG5hLnJtID0gVFJVRSkpKQ0KDQpnZ3Bsb3QodG93ZXJfa2lsbHMsIGFlcyh4ID0gVGVhbSwgeSA9IEtpbGxzLCBmaWxsID0gVGVhbSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh4ID0gIlRlYW0iLCB5ID0gIlRvdGFsIFRvd2VyIEtpbGxzIiwgdGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBUb3dlciBLaWxscyBiZXR3ZWVuIFQxIGFuZCBUMiIpDQpgYGANCg0KTW9yZSBvZnRlbiB0aGFuIG5vdCwgYm90aCB0ZWFtcyB0ZW5kIHRvIGtpbGwgXD42IHRvd2VycyBwZXIgZ2FtZQ0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSB0MV9pbmhpYml0b3JLaWxscykpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogICAgICBsYWJzKHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IHQyX2luaGliaXRvcktpbGxzKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgICAgIGxhYnMoeSA9ICJDb3VudCIpDQpgYGANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIGluaGliaXRvciBraWxscyBmb3IgZWFjaCB0ZWFtDQppbmhpYml0b3Jfa2lsbHMgPC0gZGF0YS5mcmFtZShUZWFtID0gYygiVDEiLCAiVDIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEtpbGxzID0gYyhzdW0obG9sX2RhdGEkdDFfaW5oaWJpdG9yS2lsbHMsIG5hLnJtID0gVFJVRSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShsb2xfZGF0YSR0Ml9pbmhpYml0b3JLaWxscywgbmEucm0gPSBUUlVFKSkpDQoNCmdncGxvdChpbmhpYml0b3Jfa2lsbHMsIGFlcyh4ID0gVGVhbSwgeSA9IEtpbGxzLCBmaWxsID0gVGVhbSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh4ID0gIlRlYW0iLCB5ID0gIlRvdGFsIEluaGliaXRvciBLaWxscyIsIHRpdGxlID0gIkNvbXBhcmlzb24gb2YgSW5oaWJpdG9yIEtpbGxzIGJldHdlZW4gVDEgYW5kIFQyIikNCmBgYA0KDQpHYW1lcyB1c3VhbGx5IGVuZCB3aXRoIDAgdG8gMiBpbmhpYml0b3JzIGJlaW5nIGRlc3Ryb3llZCwgbGlrZWx5IGR1ZSB0byBsYXRlIGdhbWUgZm9yZmVpdHMgb3Igc3RlYW0gcm9sbHMgKHdoaWNoIGlzIG9mdGVuIHRoZSBjYXNlIGluIG1hbnkgZ2FtZXMpDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbF9kYXRhLCBhZXMoeCA9IHQxX2RyYWdvbktpbGxzKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgICAgIGxhYnMoeSA9ICJDb3VudCIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gdDJfZHJhZ29uS2lsbHMpKSArIA0KICAgICAgZ2VvbV9iYXIoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkgKw0KICAgICAgbGFicyh5ID0gIkNvdW50IikNCmBgYA0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgZHJhZ29uIGtpbGxzIGZvciBlYWNoIHRlYW0NCmRyYWdvbl9raWxscyA8LSBkYXRhLmZyYW1lKFRlYW0gPSBjKCJUMSIsICJUMiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgS2lsbHMgPSBjKHN1bShsb2xfZGF0YSR0MV9kcmFnb25LaWxscywgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGxvbF9kYXRhJHQyX2RyYWdvbktpbGxzLCBuYS5ybSA9IFRSVUUpKSkNCg0KZ2dwbG90KGRyYWdvbl9raWxscywgYWVzKHggPSBUZWFtLCB5ID0gS2lsbHMsIGZpbGwgPSBUZWFtKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBsYWJzKHggPSAiVGVhbSIsIHkgPSAiVG90YWwgRHJhZ29uIEtpbGxzIiwgdGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBEcmFnb24gS2lsbHMgYmV0d2VlbiBUMSBhbmQgVDIiKQ0KYGBgDQoNCkRyYWdvbiBraWxscyBjb21mb3J0YWJseSBsaWUgd2l0aGluIHRoZSAwLTIgcmFuZ2UNCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gdDFfcmlmdEhlcmFsZEtpbGxzKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgICAgIGxhYnMoeSA9ICJDb3VudCIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gdDJfcmlmdEhlcmFsZEtpbGxzKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgICAgIGxhYnMoeSA9ICJDb3VudCIpDQpgYGANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHJpZnQgaGVyYWxkIGtpbGxzIGZvciBlYWNoIHRlYW0NCnJpZnRIZXJhbGRfa2lsbHMgPC0gZGF0YS5mcmFtZShUZWFtID0gYygiVDEiLCAiVDIiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBLaWxscyA9IGMoc3VtKGxvbF9kYXRhJHQxX3JpZnRIZXJhbGRLaWxscywgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShsb2xfZGF0YSR0Ml9yaWZ0SGVyYWxkS2lsbHMsIG5hLnJtID0gVFJVRSkpKQ0KDQpnZ3Bsb3QocmlmdEhlcmFsZF9raWxscywgYWVzKHggPSBUZWFtLCB5ID0gS2lsbHMsIGZpbGwgPSBUZWFtKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBsYWJzKHggPSAiVGVhbSIsIHkgPSAiVG90YWwgUmlmdCBIZXJhbGQgS2lsbHMiLCB0aXRsZSA9ICJDb21wYXJpc29uIG9mIFJpZnQgSGVyYWxkIEtpbGxzIGJldHdlZW4gVDEgYW5kIFQyIikNCmBgYA0KDQpSaWZ0IGhlcmFsZCBvbmx5IHNwYXducyBmcm9tIDk6NTAgLSAxOTo0NSBkdXJpbmcgdGhlIGdhbWUgYW5kIGlzIG1vc3Qgb2Z0ZW4gb25seSB0YWtlbiBvbmNlIGlmIGF0IGFsbA0KDQpgYGB7cn0NCmdncGxvdChsb2xfZGF0YSwgYWVzKHggPSB0MV9iYXJvbktpbGxzKSkgKyANCiAgICAgIGdlb21fYmFyKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpICsNCiAgICAgIGxhYnMoeSA9ICJDb3VudCIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobG9sX2RhdGEsIGFlcyh4ID0gdDJfYmFyb25LaWxscykpICsgDQogICAgICBnZW9tX2JhcigpICsgDQogICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTApKSArDQogICAgICBsYWJzKHkgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBiYXJvbiBraWxscyBmb3IgZWFjaCB0ZWFtDQpiYXJvbl9raWxscyA8LSBkYXRhLmZyYW1lKFRlYW0gPSBjKCJUMSIsICJUMiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBLaWxscyA9IGMoc3VtKGxvbF9kYXRhJHQxX2Jhcm9uS2lsbHMsIG5hLnJtID0gVFJVRSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtKGxvbF9kYXRhJHQyX2Jhcm9uS2lsbHMsIG5hLnJtID0gVFJVRSkpKQ0KDQpnZ3Bsb3QoYmFyb25fa2lsbHMsIGFlcyh4ID0gVGVhbSwgeSA9IEtpbGxzLCBmaWxsID0gVGVhbSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh4ID0gIlRlYW0iLCB5ID0gIlRvdGFsIEJhcm9uIEtpbGxzIiwgdGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBCYXJvbiBLaWxscyBiZXR3ZWVuIFQxIGFuZCBUMiIpDQpgYGANCg0KQmFyb24gc3Bhd25zIGFmdGVyIHRoZSByaWZ0IGhlcmFsZCBhbmQgdXN1YWxseSBpcyBnYW1lIG92ZXIgaWYgdGFrZW4gYW5kIHVzZWQgcHJvcGVybHksIGhlbmNlIHdoeSB0aGUgYW1vdW50IHRha2VuIGlzIHVzdWFsbHkgMCBvciAxIGFzIHNvbWV0aW1lcyBnYW1lcyBlbmQgYmVmb3JlIGl0IGNhbiBiZSB0YWtlbg0KDQojIyBCYXNlIE1vZGVsIGFuZCBQcmlvciBNb2RlbGluZw0KDQpMZXRzIHN0YXJ0IG9mZiB3aXRoIGEgYmFzZSBtb2RlbCB0byBzZWUgaWYgd2UgY2FuIGdldCBpbnNpZ2h0IGZyb20ganVzdCB0aGUgd2lubmVyIG9mIHRoZSBtYXRjaA0KDQpgYGB7cn0NCmJhc2UgPC0gc3Rhbl9nbG0oZ2FtZUR1cmF0aW9uIH4gd2lubmVyLA0KICBkYXRhID0gbG9sX2RhdGEsIGZhbWlseSA9IGdhdXNzaWFuLCANCiAgcHJpb3JfaW50ZXJjZXB0ID0gbm9ybWFsKDE4MDAsIDE1MCwgYXV0b3NjYWxlID0gVFJVRSksDQogIHByaW9yID0gbm9ybWFsKDAsIDIuNSwgYXV0b3NjYWxlID0gVFJVRSksIA0KICBwcmlvcl9hdXggPSBleHBvbmVudGlhbCgxLCBhdXRvc2NhbGUgPSBUUlVFKSwNCiAgY2hhaW5zID0gNCwgaXRlciA9IDUwMDAqMiwgc2VlZCA9IDg0NzM1KQ0KYGBgDQoNCmBgYHtyfQ0KcHJpb3Jfc3VtbWFyeShiYXNlKSANCmBgYA0KDQpBIHByaW9yIHByZWRpY3RpdmUgY2hlY2sgY2FuIGJlIHVzZWQgdG8gc2VlIHdoYXQgd2UgY2FuIHBvdGVudGlhbGx5IGV4cGVjdCBmcm9tIHRoZSBkYXRhIGFsb25lIGFuZCB3aGF0IHRoZSBzdGFuIG1vZGVsIHZpZXdlZCBhcyBhcHByb3ByaWF0ZSBmb3Igb3VyIHByZWRpY3Rpb25zDQoNCmBgYHtyfQ0KYmFzZV9wcmlvcnMgPC0gdXBkYXRlKGJhc2UsIHByaW9yX1BEID0gVFJVRSkNCiMgMjAwIHByaW9yIG1vZGVsIGxpbmVzDQpsb2xfZGF0YSAlPiUNCiAgYWRkX2ZpdHRlZF9kcmF3cyhiYXNlLCBuID0gMjAwKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gd2lubmVyLCB5ID0gZ2FtZUR1cmF0aW9uKSkgKw0KICAgIGdlb21fbGluZShhZXMoeSA9IC52YWx1ZSwgZ3JvdXAgPSAuZHJhdyksIGFscGhhID0gMC4wNSkNCg0KIyA0IHByaW9yIHNpbXVsYXRlZCBkYXRhc2V0cw0Kc2V0LnNlZWQoMykNCmxvbF9kYXRhICU+JQ0KICBhZGRfcHJlZGljdGVkX2RyYXdzKGJhc2UsIG4gPSA0KSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gd2lubmVyLCB5ID0gZ2FtZUR1cmF0aW9uKSkgKw0KICAgIGdlb21fcG9pbnQoYWVzKHkgPSAucHJlZGljdGlvbiwgZ3JvdXAgPSAuZHJhdykpICsgDQogICAgZmFjZXRfd3JhcCh+IC5kcmF3KQ0KYGBgDQoNClByaW9yIGRpc3RyaWJ1dGlvbiBleHBlY3RzIHRoZSBnYW1lcyB0byByYW5nZSBmcm9tIDE5MTAgLSAxOTQ1IHNlY29uZHMgYXMgZXhwZWN0ZWQNCg0KR2FtZSBkdXJhdGlvbiBsaWVzIGJldHdlZW4gNTAwLTM1MDAgc2Vjb25kcyB3aGljaCBmYWxscyBpbiBsaW5lIHdpdGggb3VyIHN1bW1hcnkNCg0KUHJpb3IgZGlzdHJpYnV0aW9uIGV4cGVjdHMgZ2FtZXMgdG8gbGFzdCBsb25nZXIgaWYgdGVhbSAyIHdpbnMgYXMgY29tcGFyZWQgdG8gdGVhbSAxDQoNCmBgYHtyfQ0KbWNtY190cmFjZShiYXNlLCBzaXplID0gLjEpDQpgYGANCg0KUGxvdHMgbG9vayBmYXN0IG1peGluZyBhbmQgY29uc2lzdGVudA0KDQpgYGB7cn0NCnJoYXQoYmFzZSkNCmBgYA0KDQpSaGF0IGNsb3NlIHRvIDEgYW5kIG5vdCBcPjEuMDUsIGdvb2QNCg0KYGBge3J9DQpuZWZmX3JhdGlvKGJhc2UpDQpgYGANCg0KbmVmZiByYXRpbyBpcyBhIGxpdHRsZSBoaWdoIGJ1dCBzdGlsbCBhY2NlcHRhYmxlLCBcPi4xMCB3aGljaCBpcyBnb29kDQoNCmBgYHtyfQ0KdGlkeShiYXNlLCBlZmZlY3RzID0gYygiZml4ZWQiLCAiYXV4IiksDQogICAgIGNvbmYuaW50ID0gVFJVRSwgY29uZi5sZXZlbCA9IDAuOTUpDQpgYGANCg0KT3ZlcmFsbCB0aGUgbW9kZWwgY291bGQgd29yaywgYnV0IG1vcmUgaW52ZXN0aWdhdGlvbiBzaG91bGQgYmUgZG9uZSB0byBpbXByb3ZlIHdoYXQgd2UgaGF2ZQ0KDQojIyBJbnRlcmFjdGlvbiBJbnZlc3RpZ2F0aW9uDQoNCkxldHMgc2VlIGlmIHRoZXJlIGFyZSBzb21lIGludGVyYWN0aW9ucyB3ZSBjYW4gZXhwZWN0DQoNClNwZWFraW5nIGZyb20gZXhwZXJpZW5jZSBpbiBwbGF5aW5nIHRoZSBnYW1lIG15c2VsZiwgSSBwcmVkaWN0IHRoZSBkcmFnb24gYW5kIGJhcm9uIGtpbGxzIHdpbGwgaGF2ZSBhIGJpZyBpbXBhY3Qgb24gdGhlIGdhbWUgZHVyYXRpb24gZHVlIHRvIHRoZSBidWZmcyB0aGV5IGdpdmUgZWFjaCBtZW1iZXIgb2YgdGhlIHRlYW0gYmVpbmcgY3J1Y2lhbCB0byB3aW5uaW5nDQoNCmBgYHtyfQ0KaW50IDwtIHN0YW5fZ2xtKGdhbWVEdXJhdGlvbiB+IHQxX2RyYWdvbktpbGxzOnQyX2RyYWdvbktpbGxzLA0KICBkYXRhID0gbG9sX2RhdGEsIGZhbWlseSA9IGdhdXNzaWFuLCANCiAgcHJpb3JfaW50ZXJjZXB0ID0gbm9ybWFsKDE4MDAsIDE1MCwgYXV0b3NjYWxlID0gVFJVRSksDQogIHByaW9yID0gbm9ybWFsKDAsIDIuNSwgYXV0b3NjYWxlID0gVFJVRSksIA0KICBwcmlvcl9hdXggPSBleHBvbmVudGlhbCgxLCBhdXRvc2NhbGUgPSBUUlVFKSwNCiAgY2hhaW5zID0gNCwgaXRlciA9IDUwMDAqMiwgc2VlZCA9IDg0NzM1KQ0KYGBgDQoNCmBgYHtyfQ0KaW50MiA8LSBzdGFuX2dsbShnYW1lRHVyYXRpb24gfiB0MV9iYXJvbktpbGxzOnQyX2Jhcm9uS2lsbHMsDQogIGRhdGEgPSBsb2xfZGF0YSwgZmFtaWx5ID0gZ2F1c3NpYW4sIA0KICBwcmlvcl9pbnRlcmNlcHQgPSBub3JtYWwoMTgwMCwgMTUwLCBhdXRvc2NhbGUgPSBUUlVFKSwNCiAgcHJpb3IgPSBub3JtYWwoMCwgMi41LCBhdXRvc2NhbGUgPSBUUlVFKSwgDQogIHByaW9yX2F1eCA9IGV4cG9uZW50aWFsKDEsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBjaGFpbnMgPSA0LCBpdGVyID0gNTAwMCoyLCBzZWVkID0gODQ3MzUpDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5KGludCkNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkoaW50MikNCmBgYA0KDQpTdW1tYXJ5IHN0YXRzIGxvb2sgcHJvbWlzaW5nIHdpdGggYm90aCB2YWx1ZXMgbm90IGNvbnRhaW5pbmcgMCBmb3IgdGhlaXIgOTUlIENJIHNob3dpbmcgdGhleSBhcmUgc2lnbmlmaWNhbnQgYW5kIGNhbiBiZSB1c2VmdWwNCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIHBvc3RlcmlvciBzYW1wbGVzDQpwb3N0ZXJpb3Jfc2FtcGxlcyA8LSBhcy5tYXRyaXgoaW50KQ0KcG9zdGVyaW9yX3NhbXBsZXMyIDwtIGFzLm1hdHJpeChpbnQyKQ0KDQojIFBsb3QgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gb2YgdGhlIGludGVyYWN0aW9uIHRlcm0NCm1jbWNfaGlzdChwb3N0ZXJpb3Jfc2FtcGxlcywgcGFycyA9IGMoInQxX2RyYWdvbktpbGxzOnQyX2RyYWdvbktpbGxzIikpDQoNCm1jbWNfaGlzdChwb3N0ZXJpb3Jfc2FtcGxlczIsIHBhcnMgPSBjKCJ0MV9iYXJvbktpbGxzOnQyX2Jhcm9uS2lsbHMiKSkNCmBgYA0KDQpUaGUgcHJlZGljdG9ycyBkb24ndCBsb29rIHNrZXdlZCBlaXRoZXIsIGxvb2tzIGdvb2QuDQoNCldlIGNhbiBjYW5jbHVkZSB0aGF0IHRoZXJlIG1heSBiZSBhIHBvdGVudGlhbCBpbnRlcmFjdGlvbiBiZXR3ZWVuIGRyYWdvbiBraWxscyBhbmQgYmFyb24ga2lsbHMgZm9yIGVhY2ggdGVhbQ0KDQpXZSBjYW4gdXRpbGl6ZSB0aGlzIGluZm8gbGF0ZXIgb24gdXBvbiBidWlsZGluZyBhIGJldHRlciBtb2RlbA0KDQojIyBNYWluIE1vZGVsDQoNCkxldCdzIGFzc2VzcyBob3cgbXVjaCBnZXR0aW5nIGEganVtcCBzdGFydCBpbiB0aGUgZ2FtZSBkb2VzIGZvciBkZXRlcm1pbmluZyB0aGUgbGVuZ3RoIG9mIHRoZSBnYW1lDQoNCmBgYHtyfQ0KbWFpbiA8LSBzdGFuX2dsbShnYW1lRHVyYXRpb24gfiB3aW5uZXIgKyBmaXJzdEJsb29kICsgZmlyc3RUb3dlciArIGZpcnN0RHJhZ29uICsgZmlyc3RCYXJvbiwNCiAgZGF0YSA9IGxvbF9kYXRhLCBmYW1pbHkgPSBnYXVzc2lhbiwgDQogIHByaW9yX2ludGVyY2VwdCA9IG5vcm1hbCgxODAwLCAxNTAsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBwcmlvciA9IG5vcm1hbCgwLCAyLjUsIGF1dG9zY2FsZSA9IFRSVUUpLCANCiAgcHJpb3JfYXV4ID0gZXhwb25lbnRpYWwoMSwgYXV0b3NjYWxlID0gVFJVRSksDQogIGNoYWlucyA9IDQsIGl0ZXIgPSA1MDAwKjIsIHNlZWQgPSA4NDczNSkNCmBgYA0KDQpgYGB7cn0NCnByaW9yX3N1bW1hcnkobWFpbikgDQpgYGANCg0KYGBge3J9DQptY21jX3RyYWNlKG1haW4sIHNpemUgPSAuMSkNCmBgYA0KDQpDaGFpbnMgbG9vayBub3JtYWwNCg0KYGBge3J9DQpyaGF0KG1haW4pDQpgYGANCg0KYGBge3J9DQpuZWZmX3JhdGlvKG1haW4pDQpgYGANCg0KUmhhdCBsb29rcyBnb29kLCBzb21lIG9mIHRoZSBuZWZmIHJhdGlvIHZhbHVlcyBhcmUgc3RpbGwgaGlnaCBidXQgYWNjZXB0YWJsZSBmb3Igbm93DQoNCmBgYHtyfQ0KdGlkeShtYWluLCBlZmZlY3RzID0gYygiZml4ZWQiLCAiYXV4IiksDQogICAgIGNvbmYuaW50ID0gVFJVRSwgY29uZi5sZXZlbCA9IDAuOTUpDQpgYGANCg0KRnJvbSB0aGUgdGlkeSBvdXRwdXQsIGl0IGxvb2tzIGxpa2Ugd2lubmVyLCBmaXJzdEJsb29kLCBhbmQgZmlyc3RUb3dlciBsb3NlIHRoZWlyIHNpZ25pZmljYW5jZSB3aGVuIGFkZGluZyBvdGhlciBwcmVkaWN0b3JzIHNpbmNlIHRoZWlyIDk1JSBDSSByYW5nZSBpbmNsdWRlcyAwLiBXZSdsbCB1c2UgdGhpcyBpbmZvIGxhdGVyIGZvciByZWZpbmluZyBvdXIgbW9kZWwNCg0KYGBge3J9DQpuZXdkYXRhIDwtIGRhdGEuZnJhbWUod2lubmVyID0gZmFjdG9yKDEsIGxldmVscyA9IGxldmVscyhsb2xfZGF0YSR3aW5uZXIpKSwNCiAgICAgICAgICAgICAgICAgICAgICBmaXJzdEJsb29kID0gZmFjdG9yKDIsIGxldmVscyA9IGxldmVscyhsb2xfZGF0YSRmaXJzdEJsb29kKSksDQogICAgICAgICAgICAgICAgICAgICAgZmlyc3RUb3dlciA9IGZhY3RvcigyLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkZmlyc3RUb3dlcikpLA0KICAgICAgICAgICAgICAgICAgICAgIGZpcnN0RHJhZ29uID0gZmFjdG9yKDIsIGxldmVscyA9IGxldmVscyhsb2xfZGF0YSRmaXJzdERyYWdvbikpLA0KICAgICAgICAgICAgICAgICAgICAgIGZpcnN0QmFyb24gPSBmYWN0b3IoMiwgbGV2ZWxzID0gbGV2ZWxzKGxvbF9kYXRhJGZpcnN0QmFyb24pKSkNCg0KbWFpbl9wcmVkaWN0IDwtIHBvc3Rlcmlvcl9wcmVkaWN0KA0KICBtYWluLCANCiAgbmV3ZGF0YSA9IG5ld2RhdGEpDQptY21jX2FyZWFzKG1haW5fcHJlZGljdCkgKyAgeGxhYigiR2FtZSBEdXJhdGlvbiIpICsNCiAgZ2d0aXRsZSgnUHJlZGljdGl2ZSBkaXN0cmlidXRpb24gb2YgYSBMZWFndWUgb2YgTGVnZW5kcyBnYW1lIHdoZXJlIGZpcnN0IGJsb29kLCB0b3dlciwgZHJhZ29uLCBiYXJvbiB3YXMgdGVhbSAyIGFuZCB0ZWFtIDEgd29uJykgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSkNCmBgYA0KDQpIZXJlJ3MgYW4gZXhhbXBsZSBvdXRwdXQgb2Ygd2hlcmUgYSBnYW1lIGR1cmF0aW9uIG1heSBsaWUgaWYgdGVhbSAxIHdpbnMgZGVzcGl0ZSBiZWluZyBiZWhpbmQgZnJvbSB0aGUgc3RhcnQuIFRoZSBnYW1lIGlzIGEgbGl0dGxlIGxhdGVyIHRoYW4gbm9ybWFsLCBidXQgdG8gYmUgZXhwZWN0ZWQgYXMgdGhlIHRlYW0gd291bGQgbmVlZCB0aW1lIHRvIG1ha2UgdGhlIGNvbWViYWNrIGluIHRoZSBmaXJzdCBwbGFjZS4gSG93ZXZlciwgSSBiZWxpZXZlIHRoaXMgaXMgdW5kZXJzaG9vdGluZyB0aGUgdHJ1ZSB0aW1lIHdlIGNvdWxkIGV4cGVjdCBmcm9tIHRoaXMgc2NlbmFyaW8uIFdlJ2xsIHZlcmlmeSBpZiB0aGlzIGlzIHRydWUgYWZ0ZXIgcmVmaW5pbmcgb3VyIG1vZGVsDQoNCiMjIE1vcmUgUHJlZGljdG9ycw0KDQpXZSBjYW4gYWRkIHNvbWUgbnVtZXJpY2FsIHZhcmlhYmxlcyB0byBzZWUgaG93IG11Y2ggYWRkaW5nIHRoZSBhbW91bnQgb2YgZWFjaCBvYmplY3RpdmUgYSB0ZWFtIGhhcyB0YWtlbiB0byBzZWUgaG93IGl0IGFmZmVjdHMgdGhlIGdhbWUgZHVyYXRpb24NCg0KYGBge3J9DQpleHRfbWFpbiA8LSBzdGFuX2dsbShnYW1lRHVyYXRpb24gfiB3aW5uZXIgKyBmaXJzdEJsb29kICsgZmlyc3RUb3dlciArIGZpcnN0RHJhZ29uICsgZmlyc3RCYXJvbiArIHQxX3Rvd2VyS2lsbHMgKyB0MV9pbmhpYml0b3JLaWxscyArIHQxX2Jhcm9uS2lsbHMgKyB0MV9kcmFnb25LaWxscyArIHQyX3Rvd2VyS2lsbHMgKyB0Ml9pbmhpYml0b3JLaWxscyArIHQyX2Jhcm9uS2lsbHMgKyB0Ml9kcmFnb25LaWxscywNCiAgZGF0YSA9IGxvbF9kYXRhLCBmYW1pbHkgPSBnYXVzc2lhbiwgDQogIHByaW9yX2ludGVyY2VwdCA9IG5vcm1hbCgxODAwLCAxNTAsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBwcmlvciA9IG5vcm1hbCgwLCAyLjUsIGF1dG9zY2FsZSA9IFRSVUUpLCANCiAgcHJpb3JfYXV4ID0gZXhwb25lbnRpYWwoMSwgYXV0b3NjYWxlID0gVFJVRSksDQogIGNoYWlucyA9IDQsIGl0ZXIgPSA1MDAwKjIsIHNlZWQgPSA4NDczNSkNCmBgYA0KDQpgYGB7cn0NCnByaW9yX3N1bW1hcnkoZXh0X21haW4pIA0KYGBgDQoNCmBgYHtyfQ0KbWNtY190cmFjZShleHRfbWFpbiwgc2l6ZSA9IC4xKQ0KYGBgDQoNClRyYWNlcyBsb29rIGdvb2QNCg0KYGBge3J9DQpyaGF0KGV4dF9tYWluKQ0KYGBgDQoNCmBgYHtyfQ0KbmVmZl9yYXRpbyhleHRfbWFpbikNCmBgYA0KDQpSaGF0IGxvb2tzIGdvb2QsIHNvbWUgbmVmZiByYXRpb3MgKGkuZS4gZmlyc3RCbG9vZCwgZmlyc3RUb3dlcikgYXJlIG5vdyB0b28gaGlnaCB0byBrZWVwIGFuZCB3aWxsIG5lZWQgdG8gYmUgaGFuZGxlZCBsYXRlcg0KDQpgYGB7cn0NCnRpZHkoZXh0X21haW4sIGVmZmVjdHMgPSBjKCJmaXhlZCIsICJhdXgiKSwNCiAgICAgY29uZi5pbnQgPSBUUlVFLCBjb25mLmxldmVsID0gMC45NSkNCmBgYA0KDQpIZXJlLCB0aGUgcHJlZGljdG9ycyB3aW5uZXIsIGZpcnN0Qmxvb2QsIGFuZCBmaXJzdFRvd2VyIGFyZSBub3Qgc2lnbmlmaWNhbnQgZHVlIHRvIHRoZWlyIDk1JSBDSSBpbmNsdWRpbmcgMA0KDQpgYGB7cn0NCiMgZGF0YWZyYW1lIHdpdGggdGhlIHNwZWNpZmllZCB2YWx1ZXMNCm5ld2RhdGFfZXh0IDwtIGRhdGEuZnJhbWUoDQogIHdpbm5lciA9IGZhY3RvcigxLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkd2lubmVyKSksDQogIGZpcnN0Qmxvb2QgPSBmYWN0b3IoMiwgbGV2ZWxzID0gbGV2ZWxzKGxvbF9kYXRhJGZpcnN0Qmxvb2QpKSwNCiAgZmlyc3RUb3dlciA9IGZhY3RvcigyLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkZmlyc3RUb3dlcikpLA0KICBmaXJzdERyYWdvbiA9IGZhY3RvcigyLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkZmlyc3REcmFnb24pKSwNCiAgZmlyc3RCYXJvbiA9IGZhY3RvcigyLCBsZXZlbHMgPSBsZXZlbHMobG9sX2RhdGEkZmlyc3RCYXJvbikpLA0KICB0MV90b3dlcktpbGxzID0gNywNCiAgdDFfaW5oaWJpdG9yS2lsbHMgPSAyLA0KICB0MV9iYXJvbktpbGxzID0gMiwNCiAgdDFfZHJhZ29uS2lsbHMgPSAyLA0KICB0Ml90b3dlcktpbGxzID0gMywNCiAgdDJfaW5oaWJpdG9yS2lsbHMgPSAyLA0KICB0Ml9iYXJvbktpbGxzID0gMSwNCiAgdDJfZHJhZ29uS2lsbHMgPSAzDQopDQoNCmV4dF9tYWluX3ByZWRpY3QgPC0gcG9zdGVyaW9yX3ByZWRpY3QoZXh0X21haW4sIG5ld2RhdGEgPSBuZXdkYXRhX2V4dCkNCg0KbWNtY19hcmVhcyhleHRfbWFpbl9wcmVkaWN0KSArICANCiAgeGxhYigiR2FtZSBEdXJhdGlvbiIpICsNCiAgZ2d0aXRsZSgnUHJlZGljdGl2ZSBkaXN0cmlidXRpb24gb2YgYSBMZWFndWUgb2YgTGVnZW5kcyBnYW1lIHdoZXJlIGZpcnN0IGJsb29kLCB0b3dlciwgZHJhZ29uLCBiYXJvbiB3YXMgdGVhbSAyIGFuZCB0ZWFtIDEgd29uLCB3aXRoIGFkZGl0aW9uYWwgcHJlZGljdG9ycycpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpDQpgYGANCg0KQWZ0ZXIgYWRkaW5nIGEgZmV3IG1vcmUgdmFyaWFibGVzIGludG8gcGxheSB3ZSBjYW4gc2VlIHRoZSBnYW1lIGdldHMgbG9uZ2VyIChhbmQgZXhwZWN0ZWRseSBzbykuIFRlYW0gMSBjb21lcyBmcm9tIGJlaGluZCBidXQgZW5kcyB1cCBnZXR0aW5nIDIgYmFyb25zIGFuZCA3IHRvd2VycyBtYWtpbmcgdXMgYmVsaWV2ZSB0aGlzIGdhbWUgd2lsbCBoYXZlIHRvIGdvIG9uIGZvciBhIHdoaWxlIGZvciB0aGUgY29tZWJhY2sgdG8gdHJ1bHkgYmUgY29tcGxldGUNCg0KIyMgUmVmaW5lZCBNb2RlbA0KDQpMZXQncyBub3cgcmVtb3ZlIHRoZSBoaWdoIG5lZmYgcmF0aW8gdGVybXMgZnJvbSB0aGUgZXh0ZW5kZWQgbW9kZWwgKGkuZS4gXD4gMSkgYW5kIGluY2x1ZGUgdGhlIGludGVyYWN0aW9uIHRlcm1zIHdlIGRlZW1lZCB1c2VmdWwgYmVmb3JlaGFuZA0KDQpgYGB7cn0NCmV4dF9tYWluX2ludCA8LSBzdGFuX2dsbShnYW1lRHVyYXRpb24gfiBmaXJzdERyYWdvbiArIGZpcnN0QmFyb24gKyB0MV9iYXJvbktpbGxzICsgdDFfZHJhZ29uS2lsbHMgKyB0Ml9iYXJvbktpbGxzICsgdDJfZHJhZ29uS2lsbHMgKyB0MV9iYXJvbktpbGxzOnQyX2Jhcm9uS2lsbHMgKyB0MV9kcmFnb25LaWxsczp0Ml9kcmFnb25LaWxscywNCiAgZGF0YSA9IGxvbF9kYXRhLCBmYW1pbHkgPSBnYXVzc2lhbiwgDQogIHByaW9yX2ludGVyY2VwdCA9IG5vcm1hbCgxODAwLCAxNTAsIGF1dG9zY2FsZSA9IFRSVUUpLA0KICBwcmlvciA9IG5vcm1hbCgwLCAyLjUsIGF1dG9zY2FsZSA9IFRSVUUpLCANCiAgcHJpb3JfYXV4ID0gZXhwb25lbnRpYWwoMSwgYXV0b3NjYWxlID0gVFJVRSksDQogIGNoYWlucyA9IDQsIGl0ZXIgPSA1MDAwKjIsIHNlZWQgPSA4NDczNSkNCmBgYA0KDQpgYGB7cn0NCnByaW9yX3N1bW1hcnkoZXh0X21haW5faW50KQ0KYGBgDQoNCmBgYHtyfQ0KbWNtY190cmFjZShleHRfbWFpbl9pbnQsIHNpemUgPSAuMSkNCmBgYA0KDQpTbyBmYXIgc28gZ29vZA0KDQpgYGB7cn0NCnJoYXQoZXh0X21haW5faW50KQ0KYGBgDQoNClJoYXQgbG9va3Mgc2F0aXNmYWN0b3J5IGZvciBhbGwgcHJlZGljdG9ycw0KDQpgYGB7cn0NCm5lZmZfcmF0aW8oZXh0X21haW5faW50KQ0KYGBgDQoNCk5lZmYgcmF0aW9zIGFyZSBtdWNoIG1vcmUgcmVhc29uYWJsZSBub3cgYW5kIGEgYmlnIGltcHJvdmVtZW50IGZyb20gcHJpb3IgbW9kZWxzIHdpdGggbm8gdmFsdWUgYmVpbmcgXD4gLjkwDQoNCmBgYHtyfQ0KdGlkeShleHRfbWFpbl9pbnQsIGVmZmVjdHMgPSBjKCJmaXhlZCIsICJhdXgiKSwNCiAgICAgY29uZi5pbnQgPSBUUlVFLCBjb25mLmxldmVsID0gMC45NSkNCmBgYA0KDQpFdmVyeSB2YWx1ZSBsb29rcyBzaWduaWZpY2FudCBmcm9tIHRoZSA5NSUgQ0kgKGV2ZW4gdGhlIGludGVyYWN0aW9uIHRlcm1zISEpIGR1ZSB0byBub25lIG9mIHRoZW0gaW5jbHVkaW5nIDAuDQoNCmBgYHtyfQ0KIyBkYXRhZnJhbWUgd2l0aCB0aGUgc3BlY2lmaWVkIHZhbHVlcw0KbmV3ZGF0YV9leHRfaW50IDwtIGRhdGEuZnJhbWUoDQogIGZpcnN0RHJhZ29uID0gZmFjdG9yKDIsIGxldmVscyA9IGxldmVscyhsb2xfZGF0YSRmaXJzdERyYWdvbikpLA0KICBmaXJzdEJhcm9uID0gZmFjdG9yKDIsIGxldmVscyA9IGxldmVscyhsb2xfZGF0YSRmaXJzdEJhcm9uKSksDQogIHQxX2Jhcm9uS2lsbHMgPSAyLA0KICB0MV9kcmFnb25LaWxscyA9IDIsDQogIHQyX2Jhcm9uS2lsbHMgPSAxLA0KICB0Ml9kcmFnb25LaWxscyA9IDMNCikNCg0KZXh0X21haW5faW50X3ByZWRpY3QgPC0gcG9zdGVyaW9yX3ByZWRpY3QoZXh0X21haW5faW50LCBuZXdkYXRhID0gbmV3ZGF0YV9leHRfaW50KQ0KDQptY21jX2FyZWFzKGV4dF9tYWluX2ludF9wcmVkaWN0KSArICANCiAgeGxhYigiR2FtZSBEdXJhdGlvbiIpICsNCiAgZ2d0aXRsZSgnUHJlZGljdGl2ZSBkaXN0cmlidXRpb24gb2YgYSBMZWFndWUgb2YgTGVnZW5kcyBnYW1lIHdoZXJlIGZpcnN0IGRyYWdvbiwgYmFyb24gd2FzIHRlYW0gMiBhbmQgdGVhbSAxIGhhZCBtb3JlIGJhcm9uIGtpbGxzIHdpdGggaW50ZXJhY3Rpb24nKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQ0KYGBgDQoNClRoZSBnYW1lcyBzZWVtIHRvIGRyYWcgb24gZXZlbiBsb25nZXIgd2l0aCB0aGlzIG5ldyBtb2RlbC4gVGhpcyBpcyBleHBlY3RlZCBzaW5jZSB0aGUgZHJhZ29uIGFuZCBiYXJvbiByZXNwYXduIHRpbWVzIGFyZSBxdWl0ZSBsb25nIHNvLCBhcyBzYWlkIGJlZm9yZSwgdGhlIGNvbWViYWNrIHdvdWxkIHJlYWxpc3RpY2FsbHkgdGFrZSBsb25nZXIgdGhhbiBhdmVyYWdlLg0KDQpMZXRzIGRvIHNvbWUgbW9kZWwgY29tcGFyaXNvbnMgdG8gdmVyaWZ5IHdoaWNoIG1vZGVsIGlzIGJlc3QNCg0KIyMgTW9kZWwgQ29tcGFyaXNvbnMNCg0KIyMjIFBQIENoZWNrDQoNCmBgYHtyfQ0KcHBfY2hlY2sobWFpbiwgbnJlcHMgPSA1MCkgKyB4bGFiKCJHYW1lIER1cmF0aW9uIikgKw0KICBnZ3RpdGxlKCdNYWluIGVmZmVjdHMgbW9kZWwnKQ0KYGBgDQoNClRoZSBtYWluIG1vZGVsIGdldHMgYSBkZWNlbnQgYW1vdW50IG9mIGFyZWEsIHlldCBpdCBzdGFydHMgbW92aW5nIHRvbyBmYXIgcmlnaHQgYW5kIHVuZGVyc2hvb3RzIGF0IHRoZSBwZWFrDQoNCmBgYHtyfQ0KcHBfY2hlY2soZXh0X21haW4sIG5yZXBzID0gNTApICsgeGxhYigiR2FtZSBEdXJhdGlvbiIpICsNCiAgZ2d0aXRsZSgnRXh0ZW5kZWQgTWFpbiBlZmZlY3RzIG1vZGVsJykNCmBgYA0KDQpUaGUgZXh0ZW5kZWQgbW9kZWwgaXMgb2JqZWN0aXZlbHkgYmV0dGVyIHRoYW4gdGhlIG1haW4gbW9kZWwsIGJ1dCBzdGlsbCB1bmRlcnNob290cyBhcmVhIGF0IHRoZSBwZWFrDQoNCmBgYHtyfQ0KcHBfY2hlY2soZXh0X21haW5faW50LCBucmVwcyA9IDUwKSArIHhsYWIoIkdhbWUgRHVyYXRpb24iKSArDQogIGdndGl0bGUoJ1JlZmluZWQgSW50ZXJhY3Rpb24gZWZmZWN0cyBtb2RlbCcpDQpgYGANCg0KVGhlIGludGVyYWN0aW9uIG1vZGVsIGlzIHZlcnkgc2ltaWxhciB0byB0aGUgZXh0ZW5kZWQgbW9kZWwgd2hpY2ggaXMgYmV0dGVyIHRoYW4gdGhlIG1haW4gbW9kZWwuDQoNClRoZSBwcGNoZWNrcyBzaG93IG1vc3Qgb2YgdGhlIGFyZSBiZWluZyBjb3ZlcmVkIGZvciB0aGUgbGFzdCAyIG1vZGVscyB3aXRoIHRoZSBpbnRlcmFjdGlvbiBtb2RlbCBkb2luZyBiZXR0ZXIgYXJvdW5kIHRoZSBwZWFrIGJ5IGEgc21hbGwgbWFyZ2luDQoNCiMjIyAxMCBGb2xkIENyb3NzLVZhbGlkYXRpb25zDQoNCmBgYHtyfQ0KdGVzdF9zYW1wbGUgPC0gbG9sX2RhdGEgJT4lIGhlYWQoMTAwMDApDQpucm93KHRlc3Rfc2FtcGxlKQ0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoODQ3MzUpDQoNCnBfbWFpbiA8LSBwcmVkaWN0aW9uX3N1bW1hcnkobW9kZWwgPSBtYWluLCBkYXRhID0gdGVzdF9zYW1wbGUpDQpwX2V4dF9tYWluIDwtIHByZWRpY3Rpb25fc3VtbWFyeShtb2RlbCA9IGV4dF9tYWluLCBkYXRhID0gdGVzdF9zYW1wbGUpDQpwX2V4dF9tYWluX2ludCA8LSBwcmVkaWN0aW9uX3N1bW1hcnkobW9kZWwgPSBleHRfbWFpbl9pbnQsIGRhdGEgPSB0ZXN0X3NhbXBsZSkNCmBgYA0KDQpgYGB7cn0NCnJiaW5kKHBfbWFpbiwgcF9leHRfbWFpbiwgcF9leHRfbWFpbl9pbnQpDQpgYGANCg0KVGhlc2UgYXJlIHRoZSByYXcgTUFFIHZhbHVlcyBiYXNlZCBvbiBhIHNhbXBsZSBvZiAxMDAwMCBvYnNlcnZhdGlvbnMgZnJvbSB0aGUgZGF0YSwgd2UgY2FuIHVzZSB0aGVzZSB0byBkZXRlcm1pbmUgdGhlIGJlaGF2aW9yIGFuZCBiaWFzIG9mIGVhY2ggbW9kZWwgYmFzZWQgb24gdGhlaXIgY3Jvc3MtdmFsaWRhdGlvbiByZXN1bHRzDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoODQ3MzUpDQoNCmN2X21haW4gPC0gcHJlZGljdGlvbl9zdW1tYXJ5X2N2KA0KICBtb2RlbCA9IG1haW4sIGRhdGEgPSBsb2xfZGF0YSwgayA9IDEwKQ0KDQpjdl9leHRlbmQgPC0gcHJlZGljdGlvbl9zdW1tYXJ5X2N2KA0KICBtb2RlbCA9IGV4dF9tYWluLCBkYXRhID0gbG9sX2RhdGEsIGsgPSAxMCkNCg0KY3ZfaW50ZXJhY3QgPC0gcHJlZGljdGlvbl9zdW1tYXJ5X2N2KA0KICBtb2RlbCA9IGV4dF9tYWluX2ludCwgZGF0YSA9IGxvbF9kYXRhLCBrID0gMTApDQpgYGANCg0KYGBge3J9DQpyYmluZChjdl9tYWluJGN2LCBjdl9leHRlbmQkY3YsIGN2X2ludGVyYWN0JGN2KQ0KYGBgDQoNCiMjIyBMb28gRGlhZ25vc3RpY3MNCg0KYGBge3J9DQpzZXQuc2VlZCgzNDUyMSkNCm1haW5fZWxwZCA8LSBsb28obWFpbikNCmV4dF9tYWluX2VscGQgPC0gbG9vKGV4dF9tYWluKQ0KZXh0X21haW5faW50ZXJhY3RfZWxwZCA8LSBsb28oZXh0X21haW5faW50KQ0KDQptYWluX2VscGQkZXN0aW1hdGVzDQpleHRfbWFpbl9lbHBkJGVzdGltYXRlcw0KZXh0X21haW5faW50ZXJhY3RfZWxwZCRlc3RpbWF0ZXMNCmBgYA0KDQpgYGB7cn0NCmMobWFpbl9lbHBkJGVzdGltYXRlc1sxXSwgZXh0X21haW5fZWxwZCRlc3RpbWF0ZXNbMV0sIGV4dF9tYWluX2ludGVyYWN0X2VscGQkZXN0aW1hdGVzWzFdKQ0KDQpsb29fY29tcGFyZShtYWluX2VscGQsIGV4dF9tYWluX2VscGQsIGV4dF9tYWluX2ludGVyYWN0X2VscGQpDQpgYGANCg0KVGhlIGV4dGVuZGVkIG1vZGVsIGZhaXJzIGJldHRlciB0aGFuIHRoZSBpbnRlcmFjdGlvbiBtb2RlbCB3aGVuIGNvbXBhcmluZyB0aGVpciBFTFBEIGFuZCBNQUUuIFRoZSBpbnRlcmFjdGlvbiBtb2RlbCBkb2VzIDQzMzcuNiBwb2ludHMgIndvcnNlIiBmb3IgRUxQRCBhbmQgaXMgYXJvdW5kIDE2IHBvaW50cyBoaWdoZXIgaW4gaXRzIE1BRS4gSG93ZXZlciwgc29tZXRoaW5nIHRvIGNvbnNpZGVyIGhlcmUgaXMgdGhlIGlkZWEgb2YgW292ZXJmaXR0aW5nXShodHRwczovL3N0YXRpc3RpY3NieWppbS5jb20vcmVncmVzc2lvbi9vdmVyZml0dGluZy1yZWdyZXNzaW9uLW1vZGVscy8pIGFuZCBob3cgbW9yZSBwcmVkaWN0b3JzIGFuZCBhZmZlY3QgdGhlIG1vZGVsIG92ZXJhbGwgYW5kIHNrZXcgb3VyIG1ldHJpY3MuDQoNCkRlc3BpdGUgdGhlIGludGVyYWN0aW9uIG1vZGVsIGNvdmVyaW5nIG1vcmUgYXJlYSwgdGhlIFtNQUVdKGh0dHBzOi8vc3RlcGhlbmFsbHdyaWdodC5jb20vZ29vZC1tYWUtc2NvcmUvKSBhbmQgRUxQRCBhcmUgYm90aCB3b3JzZS4gVGhpcyBtYXkgYmUgZHVlIHRvIHRoZXNlIHZhbHVlcyBiZWluZyBpbmZsYXRlZCBieSB0aGUgbGFyZ2VyIG51bWJlciBvZiBwcmVkaWN0b3JzIGluIHRoZSBleHRlbmRlZCBtb2RlbC4gRnVydGhlcm1vcmUsIHRoZSBleHRlbmRlZCBtb2RlbCBjb250YWlucyBudW1lcm91cyB2YWx1ZXMgd2l0aCBoaWdoZXIgdGhhbiBub3JtYWwgTmVmZiBSYXRpb3Mgd2hpY2ggaXMgYSBbY2F1c2UgZm9yIGNvbmNlcm5dKGh0dHBzOi8vc3RhdHMuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzI5NjA1OS9lZmZlY3RpdmUtc2FtcGxlLXNpemUtZ3JlYXRlci10aGFuLWFjdHVhbC1zYW1wbGUtc2l6ZSkgYWJvdXQgdGhlIHZhbGlkaXR5IG9mIHRoZSBtb2RlbCBhbG9uZyB3aXRoIHByZWRpY3RvcnMgdGhhdCB3ZXJlIG5vdCBzaWduaWZpY2FudC4NCg0KRGVzcGl0ZSBoYXZpbmcgbGVzcyBwcmVkaWN0b3JzLCB0aGUgaW50ZXJhY3Rpb24gbW9kZWwgZ2FybmVycyBtb3JlIGFyZWEgaW4gaXRzIHByZWRpY3RpdmUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiB3aGlsZSBoYXZpbmcgYSBzaW1pbGFyIE1BRSBhbmQgZ29vZCBFTFBEIHNjb3JlIHRvbyAoLTMwOTg1OS43IHZzIDMxNDE5Ny4zKSB3aGljaCBpcyBhIG1pbm9yIChcfjElKSBkaWZmZXJlbmNlIGdpdmVuIHRoZSBzaXplIG9mIHRoZXNlIHZhbHVlcy4gSG93ZXZlciwgdGhlc2UgaW50ZXJhY3Rpb24gdGVybXMgY291bGQgYWxzbyBiZSBoYXZpbmcgYW4gYWR2ZXJzZSBlZmZlY3Qgb24gdGhlIG1vZGVsIGxlYWRpbmcgdG8gdGhlc2UgZ3JlYXRlciBlcnJvcnMuDQoNCiMjIyBPdmVyZml0dGluZw0KDQpgYGB7cn0NCnJiaW5kKHBfbWFpbiwgcF9leHRfbWFpbiwgcF9leHRfbWFpbl9pbnQpIA0KcmJpbmQoY3ZfbWFpbiRjdiwgY3ZfZXh0ZW5kJGN2LCBjdl9pbnRlcmFjdCRjdikNCmBgYA0KDQpCYXNlZCBvbiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByYXcgTUFFIHZhbHVlcyBhbmQgdGhlIGNyb3NzLXZhbGlkYXRpb24gTUFFIHZhbHVlcywgb3ZlcmZpdHRpbmcgZG9lcyBub3Qgc2VlbSB0byBwb3NlIGEgdGhyZWF0IHRvIHRoZXNlIG1vZGVscywgaG93ZXZlciB3ZSBjYW4gb25seSB0cnVseSBrbm93IHRoaXMgaWYgd2UgYXJlIGdpdmVuIG5ldyBkYXRhIGVudGlyZWx5IHRvIHRlc3QgdGhlIG1vZGVscy4gRnVydGhlcm1vcmUsIHRoZSBleHRlbmRlZCBtb2RlbCBkb2VzIGhhdmUgYSBsYXJnZXIgKHRob3VnaCBtb3N0bHkgbmVnbGlnaWJsZSBkdWUgdG8gaG93IHNtYWxsIGl0IGlzKSBkaWZmZXJlbmNlIGZyb20gdGhlIGNyb3NzLXZhbGlkYXRpb24gTUFFIGNvbXBhcmVkIHRvIHRoZSBvdGhlciBtb2RlbHMuDQoNCldoYXQgaXMgdW5kb3VidGVkbHkgY2xlYXIgaXMgdGhhdCBhZGRpbmcgbnVtZXJpY2FsIHByZWRpY3RvcnMgdG8gdGhlIG1vZGVsIGRvZXMgaGVscCB0aGUgcHJlZGljdGlvbnMgb3ZlcmFsbCBhbmQgaXMgc2hvd24gd2l0aCBib3RoIHRoZSBleHRlbmRlZCBhbmQgaW50ZXJhY3Rpb24gbW9kZWwgZmFpcmluZyBiZXR0ZXIgdGhhbiB0aGUgbWFpbiBtb2RlbCBpbiBldmVyeSBhc3BlY3QuDQoNCiMjIFJlZ3Jlc3Npb24gSW5mZXJlbmNlDQoNCkxldCdzIHNlZSBob3cgdGhlIDIgYmVzdCBtb2RlbHMgZmFpciB3aGVuIGNvbmR1Y3RpbmcgYSBxdWljayBoeXBvdGhlc2lzIHRlc3QgdG8gc2VlIHRoZSBQb3N0ZXJpb3IgcHJvYmFiaWxpdHkgb2YgYSBnYW1lIGFyb3VuZCA0MCBtaW51dGVzIGxvbmcNCg0KV2UnbGwgdXNlIHRoZSBwcmVkaWN0aXZlIHBvc3RlcmlvciBtb2RlbHMgd2UgY3JlYXRlZCBhbmQgdmlzdWFsaXplZCBlYXJsaWVyIHRvIGNvbmR1Y3QgdGhlIHRlc3RzDQoNCiQkDQpIXzA6IFxwaSBcZ2VxIDI1MDANCiQkDQoNCiQkDQpIX2E6IFxwaSA8IDI1MDANCiQkDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHRoZSBwb3N0ZXJpb3Igc2FtcGxlcw0KcG9zdGVyaW9yX3NhbXBsZXMgPC0gZXh0X21haW5faW50X3ByZWRpY3QNCg0KcF9IMCA8LSBtZWFuKHBvc3Rlcmlvcl9zYW1wbGVzID49IDI1MDApDQoNCnBfSGEgPC0gbWVhbihwb3N0ZXJpb3Jfc2FtcGxlcyA8IDI1MDApDQoNCiMgUHJpbnQgdGhlIHJlc3VsdHMNCmNhdCgiUG9zdGVyaW9yIHByb2JhYmlsaXR5IG9mIEgwICjPgCDiiaUgMjUwMCk6IiwgcF9IMCkNCmNhdCgiUG9zdGVyaW9yIHByb2JhYmlsaXR5IG9mIEhhICjPgCA8IDI1MDApOiIsIHBfSGEpDQpgYGANCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIHBvc3RlcmlvciBzYW1wbGVzDQpwb3N0ZXJpb3Jfc2FtcGxlcyA8LSBleHRfbWFpbl9wcmVkaWN0DQoNCnBfSDAgPC0gbWVhbihwb3N0ZXJpb3Jfc2FtcGxlcyA+PSAyNTAwKQ0KDQpwX0hhIDwtIG1lYW4ocG9zdGVyaW9yX3NhbXBsZXMgPCAyNTAwKQ0KDQojIFByaW50IHRoZSByZXN1bHRzDQpjYXQoIlBvc3RlcmlvciBwcm9iYWJpbGl0eSBvZiBIMCAoz4Ag4omlIDI1MDApOiIsIHBfSDApDQpjYXQoIlBvc3RlcmlvciBwcm9iYWJpbGl0eSBvZiBIYSAoz4AgPCAyNTAwKToiLCBwX0hhKQ0KYGBgDQoNClRob3VnaCB0aGUgdHdvIG1vZGVscyBzZWVtaW5nbHkgcHJlZGljdCB0aGUgYWN0dWFsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSB3ZWxsLCB0aGUgMiBtb2RlbHMgaGF2ZSB2ZXJ5IGRpZmZlcmVudCBvdXRjb21lcyB3aGVuIGNvbmR1Y3RpbmcgdGhlIHRlc3QuDQoNClRoZSBleHRlbmRlZCBtb2RlbCBmYXZvcnMgdGhlIGFsdGVybmF0ZSBoeXBvdGhlc2lzIHdoaWxlIHRoZSBpbnRlcmFjdGlvbiBtb2RlbCBjbGVhcmx5IGZhdm9ycyB0aGUgbnVsbCBoeXBvdGhlc2lzLiBUaGlzIGJvdGggY29pbmNpZGVzIHdpdGggd2hhdCB3ZSBzYXcgYmVmb3JlIGZyb20gdGhlIHZpc3VhbGl6YXRpb25zIGFuZCB0ZWxscyB1cyB0aGF0IHRoZSBleHRlbmRlZCBtb2RlbCBmYXZvcnMgZ2FtZXMgdGhhdCBhcmUgc2hvcnRlciB3aGlsZSB0aGUgaW50ZXJhY3Rpb24gbW9kZWwgZXhwZWN0cyBnYW1lcyBsaWtlIHRoaXMgdG8gdGFrZSBsb25nZXIuDQoNClRoaXMgaXMgYWxzbyBzb21ldGhpbmcgb25lIHNob3VsZCBjb25zaWRlciB3aGVuIGNob29zaW5nIHRoZSBtb2RlbC4gV2hldGhlciBvciBub3QgdGhlIHByZWRpY3Rpb25zIHRoZW1zZWx2ZXMgc2VlbSByZWFsaXN0aWMgZ2l2ZW4gdGhlIHNjZW5hcmlvL2NpcmN1bXN0YW5jZXMuDQoNClRvIGJhY2sgdXAgd2hhdCBJJ20gc2F5aW5nLCBCYXJvbiBOYXNob3Igc3Bhd25zIGF0IDIwIG1pbnV0ZXMgYW5kIHJlc3Bhd25zIGV2ZXJ5IDYgbWludXRlcy4gU2luY2UgMyBCYXJvbnMgYXJlIGtpbGxlZCBpbiBib3RoIHNjZW5hcmlvcywgdGhlIGdhbWUgd291bGQgaGF2ZSB0byBiZSBhdCBsZWFzdCAzOCBtaW51dGVzIGFuZCBvbmx5IGlmIHRoZSB0ZWFtcyBraWxsIHRoZSBCYXJvbiBJTU1FRElBVEVMWSAod2hpY2ggaXMgdXN1YWxseSBuZXZlciB0aGUgY2FzZSkuIFRoZXJlZm9yZSwgb25lIHdvdWxkIGV4cGVjdCBtYWpvcml0eSBvZiB0aGUgYXJlYSB0byBsaWUgd2l0aGluIHRoZSBcfjQwIG1pbnV0ZSByYW5nZSBhdCB0aGUgdmVyeSBsZWFzdCBmb3Igd2hlbiB0aGUgZ2FtZSB3b3VsZCBlbmQuDQoNCiMjIENvbmNsdXNpb24NCg0KSW4gY29uY2x1c2lvbiwgaXQgaXMgYSBjaG9pY2UgYmV0d2VlbiB3aGV0aGVyIG9uZSB3YW50cyB0byB0YWtlIHRoZSBjaGFuY2Ugb2YgcGxheWluZyB3aXRoIGEgbW9kZWwgdGhhdCBtYXkgYmUgc3VzY2VwdGlibGUgdG8gb3ZlcmZpdHRpbmcgZm9yIHRoZSBzYWtlIG9mIHBvdGVudGlhbGx5IGxlc3MgZXJyb3Igb3IgYSBzbGlnaHRseSBoaWdoZXIgZXJyb3IgbW9kZWwgdGhhdCBoYXMgaW50ZXJhY3Rpb25zIGJ1dCB3b3VsZCBiZSBsZXNzIGxpa2VseSB0byBiZSBzdXNjZXB0aWJsZSB0byBvdmVyZml0dGluZy4NCg0KUGVyc29uYWxseSwgSSBzYXkgdGhhdCB0aGUgcmVmaW5lZCBpbnRlcmFjdGlvbiBtb2RlbCBpcyB0aGUgYmVzdCBtb2RlbCB0byB1c2UgZm9yIHByZWRpY3RpbmcgdGhlIGdhbWUgZHVyYXRpb24gb2YgYSBMT0wgbWF0Y2ggZ2l2ZW4gb3VyIGRhdGEgZHVlIHRvIGl0cyBjb21wYXJhYmxlIE1BRSBhbG9uZyB3aXRoIGJldHRlciBwb3N0ZXJpb3IgcHJlZGljdGl2ZSBhYmlsaXRpZXMgYW5kIGxhY2sgb2YgZXZpZGVuY2UgZm9yIG92ZXJmaXR0aW5nLiBGdXJ0aGVybW9yZSwgYmFzZWQgb24gbXkgZXhwZXJpZW5jZSBhbmQgZ2FtZSBydWxlcywgdGhlIHByZWRpY3RpdmUgdmFsdWVzIGl0IGNvbWVzIHVwIHdpdGggZW5kIHVwIGJlaW5nIG11Y2ggbW9yZSByZWFsaXN0aWMgaW4gdGhlIGdyYW5kIHNjaGVtZSBvZiB0aGluZ3MgYW5kIHdpdGggb3RoZXIgZmFjdG9ycyBzdWNoIGFzIHJlc3Bhd24gdGltZSBjb25zaWRlcmVkLg0KDQojIyBSZWZlcmVuY2VzDQoNCi0gICA8aHR0cHM6Ly93d3cuYmF5ZXNydWxlc2Jvb2suY29tLz4NCg0KLSAgIDxodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8yOTYwNTkvZWZmZWN0aXZlLXNhbXBsZS1zaXplLWdyZWF0ZXItdGhhbi1hY3R1YWwtc2FtcGxlLXNpemU+DQoNCi0gICA8aHR0cHM6Ly9zdGVwaGVuYWxsd3JpZ2h0LmNvbS9nb29kLW1hZS1zY29yZS8+DQoNCi0gICA8aHR0cHM6Ly9kaXNjb3Vyc2UubWMtc3Rhbi5vcmcvdC91bmRlcnN0YW5kaW5nLWxvb2ljLzEzNDA5LzY+DQoNCi0gICA8aHR0cHM6Ly9kaXNjb3Vyc2UubWMtc3Rhbi5vcmcvdC9wcm9qcHJlZC1lbHBkLWdvZXMtZG93bi1hbmQtcm1zZS1nb2VzLXVwLWFmdGVyLXgtdmFyaWFibGVzLzEzMTUzPg0KDQotICAgPGh0dHBzOi8vc3RhdHMuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzMxMzU2NC9ob3ctZG9lcy1iYXllc2lhbi1hbmFseXNpcy1tYWtlLWFjY3VyYXRlLXByZWRpY3Rpb25zLXVzaW5nLXN1YmplY3RpdmVseS1jaG9zZW4tcD4NCg0KLSAgIFtodHRwczovL21lZGl1bS5jb20vXEBvb2VtbWE4My9pbnRlcnByZXRhdGlvbi1vZi1ldmFsdWF0aW9uLW1ldHJpY3MtZm9yLXJlZ3Jlc3Npb24tYW5hbHlzaXMtbWFlLW1zZS1ybXNlLW1hcGUtci1zcXVhcmVkLWFuZC01NjkzYjYxYTk4MzNdKGh0dHBzOi8vbWVkaXVtLmNvbS9Ab29lbW1hODMvaW50ZXJwcmV0YXRpb24tb2YtZXZhbHVhdGlvbi1tZXRyaWNzLWZvci1yZWdyZXNzaW9uLWFuYWx5c2lzLW1hZS1tc2Utcm1zZS1tYXBlLXItc3F1YXJlZC1hbmQtNTY5M2I2MWE5ODMzKXsudXJpfQ0KDQotICAgPGh0dHBzOi8vc3RhdGlzdGljc2J5amltLmNvbS9yZWdyZXNzaW9uL292ZXJmaXR0aW5nLXJlZ3Jlc3Npb24tbW9kZWxzLz4NCg0KLSAgIDxodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy85MDUzL2hvdy1kb2VzLWNyb3NzLXZhbGlkYXRpb24tb3ZlcmNvbWUtdGhlLW92ZXJmaXR0aW5nLXByb2JsZW0+