【 選用資料集介紹 】



【 套件載入 】

# 載入所需的套件(Loading package)
library(dplyr)
library(plyr)
library(ggplot2)
library(readr)
library(reshape)
library(dplyr)
library(plyr)
library(ggplot2)
library(readr)
library(reshape)
library(ROCR)
library(caret)
library(randomForest)
library(pROC)
# 更改R的預設語系
Sys.setlocale("LC_ALL",'C')      
[1] "C/C/C/C/C/en_US.UTF-8"



【 敘述統計 】

# 讀取檔案
LOL <- read_csv("LOLgamedata.csv")
Parsed with column specification:
cols(
  .default = col_character(),
  gameId = col_double(),
  creationTime = col_double(),
  gameDuration = col_integer(),
  seasonId = col_integer(),
  winner = col_integer(),
  firstBlood = col_integer(),
  firstTower = col_integer(),
  firstInhibitor = col_integer(),
  firstBaron = col_integer(),
  firstDragon = col_integer(),
  firstRiftHerald = col_integer(),
  t1_towerKills = col_integer(),
  t1_inhibitorKills = col_integer(),
  t1_baronKills = col_integer(),
  t1_dragonKills = col_integer(),
  t1_riftHeraldKills = col_integer(),
  t2_towerKills = col_integer(),
  t2_inhibitorKills = col_integer(),
  t2_baronKills = col_integer(),
  t2_dragonKills = col_integer()
  # ... with 1 more columns
)
See spec(...) for full column specifications.
# 把英雄的出場次數做統計並且算出勝率
LOL$seasonId = NULL
hero = rbind(table(LOL$t1_champ1id), table(LOL$t1_champ2id), table(LOL$t1_champ3id), table(LOL$t1_champ4id),
table(LOL$t1_champ5id), table(LOL$t2_champ1id), table(LOL$t2_champ2id), table(LOL$t2_champ3id), table(LOL$t2_champ4id), table(LOL$t2_champ5id))
hero <- hero %>% t() %>% as.data.frame()
hero$red1 <- rowSums(subset(hero, ,c("V1","V2","V3","V4","V5")))
hero$blue1 <- rowSums(subset(hero, ,c("V6","V7","V8","V9","V10")))
hero$heroname <- rownames(hero)
# mdata <- melt(select(hero,c(1:10,13)),id = "heroname")
# rowSums(hero) 
# hero <- rbind(hero, Totals = colSums(hero))
barplot(hero[,11], names.arg = rownames(hero), horiz = TRUE, las = 1)

winlo = select(LOL,c(4, 11, 14, 17, 20, 23, 36, 39, 42, 45, 48))
winlo$rowname <- rownames(winlo)
m <- winlo %>% select(2 : 6)
colnames(m) <- c("v", "v", "v", "v", "v")
m <- cbind(m,winlo[,12])
mdata <- melt(m, id = "rowname")
ggplot(data = mdata, aes(x = value, colour = 'red')) + geom_histogram(stat = "count")
Ignoring unknown parameters: binwidth, bins, pad

m2 <- winlo %>% select(7 : 11)
colnames(m2) = c("v", "v", "v", "v", "v")
m2 <- cbind(m2, winlo[,12])
mdata2 <- melt(m2, id = "rowname")
ggplot(data = mdata2, aes(x = value, colour='red')) + geom_histogram(stat = "count")
Ignoring unknown parameters: binwidth, bins, pad

m3 <- winlo %>% select(2 : 11)
colnames(m3) = c("v", "v", "v", "v", "v", "v", "v", "v", "v", "v")
m3 <- cbind(m3,winlo[,12])
mdata3 <- melt(m3, id = "rowname")
ggplot(data = mdata3, aes(x = value, colour = 'red')) + geom_histogram(stat = "count")
Ignoring unknown parameters: binwidth, bins, pad



# 把英雄的出場次數做統計並且算出勝率
hero = as.data.frame(hero)
wint1 <- subset(winlo, winner == 1)
wint2 <- subset(winlo, winner == 2)
wincount1 <- Reduce(rbind, Map(function(count)table(wint1[,count]), c(2:6)))
losecount2 <- Reduce(rbind, Map(function(count)table(wint1[,count]), c(7:11)))
wincount2 <- Reduce(rbind, Map(function(count)table(wint2[,count]), c(7:11)))
losecount1 <- Reduce(rbind, Map(function(count)table(wint2[,count]), c(2:6)))
wincount1 <- wincount1 %>% as.data.frame() %>% t()
wincount2 <- wincount2 %>% as.data.frame() %>% t()
losecount1 <- losecount1 %>% as.data.frame() %>% t()
losecount2 <- losecount2 %>% as.data.frame() %>% t()
wincount <- cbind(wincount1,wincount2)
losecount <- cbind(losecount1,losecount2)
hero$win <- wincount %>% rowSums()
hero$lose <- losecount %>% rowSums()
rm(losecount1, losecount2, wincount1, wincount2, winlo, wint1, wint2)
hero$winrate <- hero$win / (hero$win + hero$lose)
max(hero$winrate)
[1] 0.5563472
hero %>% filter(heroname == "Yasuo" | heroname == "Zed" | heroname == "Janna" | heroname == "Darius" | heroname == "Cho'Gath" | heroname == "Udyr" | heroname == "Skarner" | heroname == "Ryze")
LOL %>% filter(t1_inhibitorKills >=10 | t2_inhibitorKills >= 10)



# 統計每隻角色被ban次數
bancount <- select(LOL, c(31:35,56:60))
bancount <- Reduce(rbind, Map(function(count)table(bancount[,count]) , c(1:10)))
bancount <- bancount %>% t() %>% rowSums() %>% as.data.frame()
bancount$name <- bancount %>% rownames()
drawdraw <- bancount %>% filter(.>=20000 | .<=100)
barplot(drawdraw[,1], names.arg = drawdraw$name, horiz = TRUE, las=1)



# 將每個腳色的種類和所帶的召喚師技能做統計
chtotag = select(LOL, c(61, 12, 13, 62, 15, 16, 63, 18, 19, 64, 21, 22, 65, 24, 25, 66, 37, 38, 67, 40, 41, 68, 43, 44, 69, 46, 47, 70, 49, 50))
# chtotag = rbind(table(select(chtotag,1,2)),table(select(chtotag,1,3)),table(select(chtotag,4,5)),table(select(chtotag,4,6)),table(select(chtotag,7,8)),table(select(chtotag,7,9)),table(select(chtotag,10,11)),table(select(chtotag,10,12)),table(select(chtotag,13,14)),table(select(chtotag,13,15)),table(select(chtotag,16,17)),table(select(chtotag,16,18)),table(select(chtotag,19,20)),table(select(chtotag,19,21)),table(select(chtotag,22,23)),table(select(chtotag,22,24)),table(select(chtotag,25,26)),table(select(chtotag,25,27)),table(select(chtotag,28,29)),table(select(chtotag,28,30)))
chtotag = cbind(table(chtotag[,c(1,2)]), table(chtotag[,c(1,3)]), table(chtotag[,c(4,5)]), table(chtotag[,c(4,6)]), table(chtotag[,c(7,8)]), table(chtotag[,c(7,9)]), table(chtotag[,c(10,11)]), table(chtotag[,c(10,12)]), table(chtotag[,c(13,14)]), table(chtotag[,c(13,15)]), table(chtotag[,c(16,17)]), table(chtotag[,c(16,18)]), table(chtotag[,c(19,20)]), table(chtotag[,c(19,21)]), table(chtotag[,c(22,23)]), table(chtotag[,c(22,24)]), table(chtotag[,c(25,26)]), table(chtotag[,c(25,27)]), table(chtotag[,c(28,29)]), table(chtotag[,c(28,30)]))
chtotag <- chtotag %>% as.data.frame()
# apply(chtotag, MARGIN = 2, FUN = table)
# for(i in 1:9){
#   for(j in 1:6)
#    chtotag[j,i]= sum(chtotag[j,i],chtotag[j,i+9],chtotag[j,i+18],chtotag[j,i+27],chtotag[j,i+36],chtotag[j,i+45],chtotag[j,i+54],chtotag[j,i+63],chtotag[j,i+72],chtotag[j,i+80],chtotag[j,i+82],chtotag[j,i+90],chtotag[j,i+99],chtotag[j,i+108],chtotag[j,i+117],chtotag[j,i+126],chtotag[j,i+135],chtotag[j,i+144],chtotag[j,i+153],chtotag[j,i+162],chtotag[j,i+171])
# }
# chtotag[,c(10:180)]=NULL



# 畫出腳色種類對於攜帶召喚師技能的熱圖
heatmap(as.matrix(chtotag[, c(1:9)]))

d <- density(LOL$t1_towerKills)
d2 <- density(LOL$t1_inhibitorKills)
d3 <- density(LOL$t1_baronKills)
d4 <- density(LOL$t1_dragonKills)
d5 <- density(LOL$firstRiftHerald)        # returns the density data 
plot(d)

plot(d2)

plot(d3)

plot(d4)

plot(d5)

p <- ggplot(LOL,aes(x=gameDuration,y=t1_towerKills))
p + geom_point()

p + geom_point(aes(color=factor(t1_inhibitorKills)))



# 畫出每個腳色對應殺龍次數的統計
redkilldrag <- select(LOL,c(11,14,17,20,23,29))           # %>% t() -> redkilldrag
bluekilldrag <-  select(LOL,c(36,39,42,45,48,54))         # %>% t() -> bluekilldrag
agg <- lapply(list(redkilldrag$t1_champ1id, redkilldrag$t1_champ2id, redkilldrag$t1_champ3id, redkilldrag$t1_champ4id, redkilldrag$t1_champ5id), function(num)aggregate(redkilldrag[,6],by = list(num), FUN = mean))
meandrapkill <- Reduce(cbind,Map(function(num)agg[[num]],c(1:5)))
hero$redmeandragkill <- meandrapkill[,c(2,4,6,8,10)] %>% rowSums()
agg2 <- lapply(list(bluekilldrag$t2_champ1id, bluekilldrag$t2_champ2id, bluekilldrag$t2_champ3id, bluekilldrag$t2_champ4id, bluekilldrag$t2_champ5id), function(num)aggregate(bluekilldrag[,6], by = list(num), FUN = mean))
meandrapkill2 <- Reduce(cbind, Map(function(num)agg2[[num]],c(1:5)))
hero$bluemeandragkill <- meandrapkill2[,c(2,4,6,8,10)] %>% rowSums()
# ggplot(redkilldrag,aes(t1_champ5id,fill=as.factor(t1_dragonKills)))+geom_bar(position ="stack")
# ggplot(bluekilldrag,aes(t2_champ5id,fill=as.factor(t2_dragonKills)))+geom_bar(position ="stack")
x <- ggplot(data = hero, aes(x = heroname, y = redmeandragkill)) + 
      geom_bar(stat = "identity",aes(fill = redmeandragkill))+ 
      coord_flip()
y <- ggplot(data = hero, aes(x=heroname, y = bluemeandragkill)) + 
      geom_bar(stat = "identity",aes(fill = bluemeandragkill))+ 
      coord_flip()
x

y
ggsave(plot = y, width = 10, height = 10, dpi = 300, filename = "tower.png")
ggsave(plot = x, width = 10, height = 10, dpi = 300, filename = "tower2.png")



# 畫出每個腳色對於拆塔次數的統計
y <- ggplot(data = hero, aes(x = heronames, y = redmeandragkill)) +
  geom_bar(stat = "identity",aes(colour = redmeandragkill,width = 1))+ 
  coord_flip()+
  theme(text = element_text(size = 10),
        axis.text.x = element_text(angle = 0, hjust = 2))+
  scale_fill_gradient(low = "white", high = "red")
Ignoring unknown aesthetics: width
  # scale_colour_gradient(low = "green", high = "yellow")
  
y=ggplot(data = hero, aes(x = heroname, y = redmeandragkill)) +
  geom_bar(stat = "identity",aes(fill = redmeandragkill,width = 1)) + 
  coord_flip()+
  theme(text = element_text(size = 10),
        axis.text.x = element_text(angle = 0, hjust = 2))+
  scale_fill_gradient(low = "white", high = "red")
Ignoring unknown aesthetics: width
y
ggsave(plot = y, width = 10, height = 10, dpi = 300, filename = "haha.png")

x <- ggplot(data = hero, aes(x = heroname, y = bluemeandragkill)) +
  geom_bar(stat = "identity", aes(fill = bluemeandragkill),width = 1)+ 
  coord_flip()+
  theme(text = element_text(size=10),
        axis.text.x = element_text(angle=0, hjust=2))
x
ggsave(plot = y, width = 10, height = 10, dpi = 300, filename = "haha.png")
ggsave(plot = x, width = 10, height = 10, dpi = 300, filename = "haha2.png")



# 欲瞭解當獲勝方為藍方時,閃現(flash)放在D鍵與F鍵的比例
summary(glm(winner ~ firstDragon, data= lol_train, family = "binomial"))

Call:
glm(formula = winner ~ firstDragon, family = "binomial", data = lol_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.4984  -0.8730  -0.8730   0.8873   1.5161  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -0.27006    0.08775  -3.078  0.00209 ** 
firstDragon1 -0.49825    0.08908  -5.593 2.23e-08 ***
firstDragon2  0.99897    0.08906  11.217  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 55288  on 39883  degrees of freedom
Residual deviance: 50130  on 39881  degrees of freedom
AIC: 50136

Number of Fisher Scoring iterations: 4
test1 <- subset(lol_train,lol_train$firstDragon!=0)
summary(glm(test1$winner ~ as.factor(test1$firstDragon),family = "binomial"))

Call:
glm(formula = test1$winner ~ as.factor(test1$firstDragon), family = "binomial")

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.4984  -0.8730  -0.8730   0.8873   1.5161  

Coefficients:
                              Estimate Std. Error z value Pr(>|z|)    
(Intercept)                   -0.76831    0.01535  -50.05   <2e-16 ***
as.factor(test1$firstDragon)2  1.49723    0.02159   69.34   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 54555  on 39354  degrees of freedom
Residual deviance: 49406  on 39353  degrees of freedom
AIC: 49410

Number of Fisher Scoring iterations: 4
summary(glm(lol_train$winner ~ lol_train$t1_champ1_sum1,family = "binomial"))

Call:
glm(formula = lol_train$winner ~ lol_train$t1_champ1_sum1, family = "binomial")

Deviance Residuals: 
   Min      1Q  Median      3Q     Max  
-1.202  -1.170  -1.150   1.185   1.247  

Coefficients:
                                 Estimate Std. Error z value Pr(>|z|)
(Intercept)                      -0.06454    0.08717  -0.740    0.459
lol_train$t1_champ1_sum1Cleanse  -0.09710    0.20017  -0.485    0.628
lol_train$t1_champ1_sum1Exhaust   0.01274    0.09496   0.134    0.893
lol_train$t1_champ1_sum1Flash     0.04615    0.08821   0.523    0.601
lol_train$t1_champ1_sum1Ghost     0.12308    0.12037   1.022    0.307
lol_train$t1_champ1_sum1Heal      0.06510    0.09337   0.697    0.486
lol_train$t1_champ1_sum1Ignite    0.04496    0.09459   0.475    0.635
lol_train$t1_champ1_sum1Smite     0.04236    0.09317   0.455    0.649
lol_train$t1_champ1_sum1Teleport  0.06080    0.09310   0.653    0.514

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 55288  on 39883  degrees of freedom
Residual deviance: 55285  on 39875  degrees of freedom
AIC: 55303

Number of Fisher Scoring iterations: 3
a <- lol[,c(5,13,16,19,22,25)]
summary(a)
 winner     t1_champ1_sum1   t1_champ2_sum1   t1_champ3_sum1   t1_champ4_sum1   t1_champ5_sum1 
 1:25211   Flash   :27295   Flash   :27144   Flash   :27117   Flash   :27104   Flash   :27085  
 2:24645   Teleport: 4784   Heal    : 4763   Heal    : 4737   Heal    : 4801   Teleport: 4899  
           Smite   : 4556   Smite   : 4626   Smite   : 4649   Smite   : 4519   Smite   : 4636  
           Heal    : 4439   Teleport: 4426   Teleport: 4529   Teleport: 4515   Heal    : 4352  
           Ignite  : 3700   Exhaust : 3770   Exhaust : 3808   Exhaust : 3863   Ignite  : 3794  
           Exhaust : 3544   Ignite  : 3697   Ignite  : 3656   Ignite  : 3668   Exhaust : 3634  
           (Other) : 1538   (Other) : 1430   (Other) : 1360   (Other) : 1386   (Other) : 1456  
b <- lol[,c(5,38,41,44,47,50)]
summary(b)
 winner     t2_champ1_sum1   t2_champ2_sum1   t2_champ3_sum1   t2_champ4_sum1   t2_champ5_sum1 
 1:25211   Flash   :27312   Flash   :27150   Flash   :27280   Flash   :27166   Flash   :26937  
 2:24645   Teleport: 4790   Heal    : 4704   Heal    : 4687   Heal    : 4757   Teleport: 4880  
           Smite   : 4451   Smite   : 4561   Teleport: 4576   Smite   : 4743   Smite   : 4624  
           Heal    : 4325   Teleport: 4533   Smite   : 4470   Teleport: 4456   Heal    : 4428  
           Ignite  : 3799   Exhaust : 3782   Exhaust : 3813   Exhaust : 3734   Ignite  : 3840  
           Exhaust : 3660   Ignite  : 3693   Ignite  : 3642   Ignite  : 3583   Exhaust : 3759  
           (Other) : 1519   (Other) : 1433   (Other) : 1388   (Other) : 1417   (Other) : 1388  
c <- a[a$winner=="1",]
d <- b[b$winner=="1",]
summary(c)
 winner     t1_champ1_sum1   t1_champ2_sum1   t1_champ3_sum1   t1_champ4_sum1   t1_champ5_sum1 
 1:25211   Flash   :13819   Flash   :13715   Flash   :13708   Flash   :13614   Flash   :13681  
 2:    0   Teleport: 2428   Heal    : 2392   Smite   : 2398   Heal    : 2482   Teleport: 2543  
           Smite   : 2296   Smite   : 2296   Heal    : 2381   Smite   : 2310   Smite   : 2334  
           Heal    : 2230   Teleport: 2215   Teleport: 2241   Teleport: 2251   Heal    : 2135  
           Ignite  : 1870   Exhaust : 1940   Exhaust : 1926   Exhaust : 1984   Ignite  : 1926  
           Exhaust : 1792   Ignite  : 1924   Ignite  : 1866   Ignite  : 1832   Exhaust : 1858  
           (Other) :  776   (Other) :  729   (Other) :  691   (Other) :  738   (Other) :  734  
summary(d)
 winner     t2_champ1_sum1   t2_champ2_sum1   t2_champ3_sum1   t2_champ4_sum1   t2_champ5_sum1 
 1:25211   Flash   :13769   Flash   :13727   Flash   :13857   Flash   :13659   Flash   :13572  
 2:    0   Teleport: 2475   Heal    : 2415   Teleport: 2361   Smite   : 2394   Teleport: 2450  
           Smite   : 2249   Smite   : 2295   Heal    : 2330   Heal    : 2363   Smite   : 2394  
           Heal    : 2241   Teleport: 2240   Smite   : 2181   Teleport: 2337   Heal    : 2269  
           Ignite  : 1855   Exhaust : 1926   Exhaust : 1938   Exhaust : 1954   Ignite  : 1939  
           Exhaust : 1841   Ignite  : 1875   Ignite  : 1856   Ignite  : 1810   Exhaust : 1882  
           (Other) :  781   (Other) :  733   (Other) :  688   (Other) :  694   (Other) :  705  
s1 <- 13819 + 13715 + 13708 + 13614 + 13681
s2 <- 13769 + 13727 + 13857 + 13659 + 13572
s1 / (25211 * 5)                                  # 獲勝方為藍方時,將閃現放在D鍵的比例          # 0.5437071
[1] 0.5437071
s2 / (25211 * 5)                                  # 獲勝方為藍方時,將閃現放在F鍵的比例          # 0.54408
[1] 0.54408



【 資料前處理 】



【 (處理後)資料集資訊 】



【 載入資料集(Loading dataset):[LOLgamedata.csv] 】

我們透過組員自身的遊戲經驗,挑出我們認為較顯著之資料欄位進行變數型態轉換

lol <- read.csv("LOLgamedata.csv",header = T)
# View(lol)
# 將欄位進行型態轉換(transfer our data as factor)
lol$winner <- as.factor(lol$winner)
lol$firstBlood <- as.factor(lol$firstBlood)
lol$firstTower <- as.factor(lol$firstTower)
lol$firstInhibitor <- as.factor(lol$firstInhibitor)
lol$firstBaron <- as.factor(lol$firstBaron)
lol$firstDragon <- as.factor(lol$firstDragon)
lol$firstRiftHerald <- as.factor(lol$firstRiftHerald)



【 創造變數 】

lol$tower_gap <- (lol$t1_towerKills - lol$t2_towerKills)                              # 勝負隊伍塔差
# lol_train$tower_gap <- (lol_train$t1_towerKills - lol_train$t2_towerKills)
# summary(glm(winner ~ lol_train$tower_gap, data= lol_train, family = "binomial"))
lol$dragon_gap <- (lol$t1_dragonKills - lol$t2_dragonKills)                           # 勝負隊伍殺小龍數量差異
lol$baron_gap <- (lol$t1_baronKills - lol$t2_baronKills)                              # 勝負隊伍殺巴隆數量差異
lol$inhibitorKills_gap <- (lol$t1_inhibitorKills - lol$t2_inhibitorKills)             # 勝負隊伍水晶兵營數量差異



【建模】 【邏輯式迴歸(glm) 】

# 切割資料:將資料切割成Training Set(lol_train), Testing Set(lol_test)
set.seed(2018)
train_idx <- sample(1:nrow(lol), size = 0.8 * nrow(lol), replace = F)
lol_train <- lol[train_idx,]                                          # 39884 obs. of 75 variables
lol_test <- lol[-train_idx,]                                          # 9972 obs. of 74 variables    
summary(glm(winner ~ firstBlood + firstTower + firstInhibitor + firstBaron + firstDragon + firstRiftHerald, data = lol_train, family = "binomial"))

Call:
glm(formula = winner ~ firstBlood + firstTower + firstInhibitor + 
    firstBaron + firstDragon + firstRiftHerald, family = "binomial", 
    data = lol_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.9057  -0.3409  -0.1809   0.3495   2.8710  

Coefficients:
                 Estimate Std. Error z value Pr(>|z|)    
(Intercept)      -0.72064    0.11624  -6.199 5.67e-10 ***
firstBlood2       0.28354    0.03497   8.108 5.14e-16 ***
firstTower2       0.82213    0.03631  22.644  < 2e-16 ***
firstInhibitor1  -2.02436    0.05042 -40.153  < 2e-16 ***
firstInhibitor2   1.91971    0.05078  37.805  < 2e-16 ***
firstBaron1      -0.91436    0.04685 -19.518  < 2e-16 ***
firstBaron2       1.18904    0.04549  26.141  < 2e-16 ***
firstDragon1     -0.26268    0.11836  -2.219   0.0265 *  
firstDragon2      0.54305    0.11849   4.583 4.58e-06 ***
firstRiftHerald1 -0.18285    0.04329  -4.224 2.40e-05 ***
firstRiftHerald2  0.16980    0.04365   3.890   0.0001 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 55288  on 39883  degrees of freedom
Residual deviance: 23077  on 39873  degrees of freedom
AIC: 23099

Number of Fisher Scoring iterations: 6
# 預測(glm prediction)(common sense)
model1 <- glm(winner ~ firstBlood + firstTower + firstInhibitor + firstBaron + firstDragon + firstRiftHerald, data = lol_train ,family = "binomial")
result <- predict(model1,newdata = lol_test, type = "response")
table(lol_test$winner, result>0.5) %>% {sum(diag(.))/sum(.)}         # ACC = 0.9044324
[1] 0.9044324
# 預測(glm prediction)(加入towel_gap)
model1 <- glm(winner ~ firstBlood + firstTower + firstInhibitor + firstBaron + firstDragon + firstRiftHerald + tower_gap, data = lol_train ,family = "binomial")
result <- predict(model1,newdata = lol_test, type = "response")
table(lol_test$winner, result>0.5) %>% {sum(diag(.))/sum(.)}         # ACC = 0.9736262
[1] 0.9736262
# 預測(glm prediction)(加入towel_gap,並拿掉不顯著之變數)
model1 <- glm(winner ~ firstTower + firstInhibitor + firstRiftHerald + tower_gap, data = lol_train ,family = "binomial")
result <- predict(model1,newdata = lol_test, type = "response")
table(lol_test$winner, result>0.5) %>% {sum(diag(.))/sum(.)}         # ACC = 0.9731247
[1] 0.9731247
# 預測(glm prediction)(加入towel_gap, dragon_gap, baron_gap, inhibitorKills_gap, gameDuration)
model1 <- glm(winner ~ firstBlood + firstTower + firstInhibitor + firstBaron + firstDragon + firstRiftHerald + tower_gap + dragon_gap + baron_gap + inhibitorKills_gap + gameDuration, data = lol_train ,family = "binomial")
result <- predict(model1,newdata = lol_test, type = "response")
table(lol_test$winner, result>0.5) %>% {sum(diag(.))/sum(.)}         # ACC = 0.9742278
[1] 0.9742278
summary(model1)

Call:
glm(formula = winner ~ firstBlood + firstTower + firstInhibitor + 
    firstBaron + firstDragon + firstRiftHerald + tower_gap + 
    dragon_gap + baron_gap + inhibitorKills_gap + gameDuration, 
    family = "binomial", data = lol_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-3.8432  -0.0338  -0.0023   0.0325   4.7229  

Coefficients:
                   Estimate Std. Error z value Pr(>|z|)    
(Intercept)         0.37382    0.27022   1.383 0.166540    
firstBlood2        -0.12787    0.07033  -1.818 0.069042 .  
firstTower2        -0.87222    0.07553 -11.548  < 2e-16 ***
firstInhibitor1     0.51610    0.13912   3.710 0.000207 ***
firstInhibitor2    -0.58941    0.14017  -4.205 2.61e-05 ***
firstBaron1         0.52945    0.13253   3.995 6.47e-05 ***
firstBaron2        -0.08496    0.13143  -0.646 0.518002    
firstDragon1        0.47420    0.26300   1.803 0.071388 .  
firstDragon2        0.59231    0.26366   2.246 0.024673 *  
firstRiftHerald1    0.22080    0.08751   2.523 0.011634 *  
firstRiftHerald2   -0.59453    0.08857  -6.713 1.91e-11 ***
tower_gap          -1.28958    0.02374 -54.313  < 2e-16 ***
dragon_gap          0.04511    0.02163   2.086 0.037020 *  
baron_gap          -0.62911    0.05491 -11.456  < 2e-16 ***
inhibitorKills_gap  0.10260    0.03776   2.717 0.006591 ** 
gameDuration       -0.01023    0.00544  -1.880 0.060134 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 55287.7  on 39883  degrees of freedom
Residual deviance:  5838.7  on 39868  degrees of freedom
AIC: 5870.7

Number of Fisher Scoring iterations: 9



【 邏輯式迴歸ROC(ROC curve of Logistic Regression) 】

pred <- prediction(result, lol_test$winner)
perf <- performance(pred, measure = "tpr", x.measure = "fpr")
auc <- performance(pred, "auc")
# 繪製ROC curve之圖形,並算出AUC
plot(perf, main = "ROC curve(Logistic Regression)", xlab = "Specificity(FPR)", ylab = "Sensitivity(TPR)")
abline(0, 1)
text(0.5, 0.5, as.character(auc@y.values[[1]]))           # AUC = 0.9966



【 邏輯式迴歸交叉驗證(Cross-Validation(CV) of Logistic Regession) 】

# Get k-fold CV confusion matrix for Logistic Regression model
# f: formula, d: data, k: number of folds, cutoff: cutoff point 0-1
k_fold_CV_logit = function(f, d, k, cutoff){
  numOfRec = nrow(d) # number of observations
  reponse_var = all.vars(f)[1] # name of the response variable
  # k indices used to split data into k parts
  sample_idx_k = rep(sample(1:k),round(numOfRec / k) + 1)[1:numOfRec]
  # k models for k subsets of data
  k_fits = Map( function(x) glm(f, d[sample_idx_k != x, ],
                                family = "binomial"), 1:k)
  # Predicted & actual classes for each hold-out subset
  predActualClass = Map(function(x){
    predictedProb = predict(k_fits[[x]], d[sample_idx_k == x,],
                            type = "response")
    predictedClass = ifelse(predictedProb > cutoff, 1, 0)
    return(data.frame("predictedClass" = predictedClass,
                      "actualClass" = d[sample_idx_k == x, reponse_var] ) )
  }, 1:k)
  # A data frame with all predicted & actual classes
  output_DF = Reduce(function(x, y) rbind(x, y), predActualClass)
  output_DF$predictedClass = factor(output_DF$predictedClass,
                                    levels=c(0,1),labels = c("No", "Yes"))
  return( table(output_DF$predictedClass, output_DF$actualClass))
}
Map(function(cutoff) k_fold_CV_logit(winner ~ firstBlood+firstTower+firstInhibitor+firstBaron+firstDragon+firstRiftHerald,
                                     lol[train_idx,], 10, cutoff), list(0.9, 0.8, 0.7, 0.6, 0.5, 0.45, 0.4, 0.3, 0.2, 0.1)) # 0.5
[[1]]
     
          1     2
  No  19504  7456
  Yes   618 12306

[[2]]
     
          1     2
  No  19129  4829
  Yes   993 14933

[[3]]
     
          1     2
  No  18822  3285
  Yes  1300 16477

[[4]]
     
          1     2
  No  18674  2637
  Yes  1448 17125

[[5]]
     
          1     2
  No  18233  1978
  Yes  1889 17784

[[6]]
     
          1     2
  No  17860  1664
  Yes  2262 18098

[[7]]
     
          1     2
  No  17692  1587
  Yes  2430 18175

[[8]]
     
          1     2
  No  16964  1335
  Yes  3158 18427

[[9]]
     
          1     2
  No  15230   975
  Yes  4892 18787

[[10]]
     
          1     2
  No  12439   526
  Yes  7683 19236



【 隨機森林(Random Forest) 】

# 試藉由組員自身遊戲經驗所找出之變數跑randomForest
set.seed(2018)
lol_tree <- randomForest(winner ~ firstBlood + firstTower + firstInhibitor + firstBaron + firstDragon + firstRiftHerald, lol_train, ntree = 500)
result_tree <- predict(lol_tree,newdata = lol_test)
lol_tree

Call:
 randomForest(formula = winner ~ firstBlood + firstTower + firstInhibitor +      firstBaron + firstDragon + firstRiftHerald, data = lol_train,      ntree = 500) 
               Type of random forest: classification
                     Number of trees: 500
No. of variables tried at each split: 2

        OOB estimate of  error rate: 9.36%
Confusion matrix:
      1     2 class.error
1 18342  1780  0.08846039
2  1954 17808  0.09887663
    # Confusion matrix:
    #       1     2 class.error
    # 1 18345  1777  0.08831130
    # 2  1973 17789  0.09983807
# 透過importance()來找出較重要之變數
importance(lol_tree)
                MeanDecreaseGini
firstBlood               49.4587
firstTower             1481.5744
firstInhibitor         8485.8823
firstBaron             2454.6815
firstDragon             718.0792
firstRiftHerald         210.8131
    #                 MeanDecreaseGini
    # firstTower            1059.24050
    # firstInhibitor        9253.06155
    # firstBaron            2451.38878
    # firstDragon            745.84541
    # firstRiftHerald         91.45326 (最低)
# 移除firstRiftHerald變數,再次進行建模
lol_tree <- randomForest(winner ~ firstTower+firstInhibitor+firstBaron+firstDragon+firstRiftHerald,lol_train, ntree=500)
result_tree <- predict(lol_tree,newdata = lol_test)
lol_tree

Call:
 randomForest(formula = winner ~ firstTower + firstInhibitor +      firstBaron + firstDragon + firstRiftHerald, data = lol_train,      ntree = 500) 
               Type of random forest: classification
                     Number of trees: 500
No. of variables tried at each split: 2

        OOB estimate of  error rate: 9.42%
Confusion matrix:
      1     2 class.error
1 18316  1806  0.08975251
2  1953 17809  0.09882603
    # Confusion matrix:
    #       1     2 class.error
    # 1 18322  1800  0.08945433
    # 2  1966 17796  0.09948386
# 預測(randomForest prediction)(加入towel_gap, dragon_gap, baron_gap, inhibitorKills_gap, gameDuration)
lol_tree <- randomForest(winner ~ firstBlood + firstTower + firstInhibitor + firstBaron + firstDragon + firstRiftHerald + tower_gap + dragon_gap + baron_gap + inhibitorKills_gap + gameDuration,lol_train, ntree = 500)
result_tree <- predict(lol_tree,newdata = lol_test)
lol_tree

Call:
 randomForest(formula = winner ~ firstBlood + firstTower + firstInhibitor +      firstBaron + firstDragon + firstRiftHerald + tower_gap +      dragon_gap + baron_gap + inhibitorKills_gap + gameDuration,      data = lol_train, ntree = 500) 
               Type of random forest: classification
                     Number of trees: 500
No. of variables tried at each split: 3

        OOB estimate of  error rate: 2.4%
Confusion matrix:
      1     2 class.error
1 19649   473  0.02350661
2   484 19278  0.02449145
    # Confusion matrix:
    #       1     2 class.error
    # 1 19655   467  0.02320843
    # 2   488 19274  0.02469386
summary(lol_tree)
                Length Class  Mode     
call                4  -none- call     
type                1  -none- character
predicted       39884  factor numeric  
err.rate         1500  -none- numeric  
confusion           6  -none- numeric  
votes           79768  matrix numeric  
oob.times       39884  -none- numeric  
classes             2  -none- character
importance         11  -none- numeric  
importanceSD        0  -none- NULL     
localImportance     0  -none- NULL     
proximity           0  -none- NULL     
ntree               1  -none- numeric  
mtry                1  -none- numeric  
forest             14  -none- list     
y               39884  factor numeric  
test                0  -none- NULL     
inbag               0  -none- NULL     
terms               3  terms  call     



【 隨機森林ROC(ROC curve of Random Forest) 】

# 繪製隨機森林的ROC曲線,繪製ROC curve之圖形,並算出AUC
rf.pred <- predict(lol_tree, lol_test, type = "prob")
rf.roc <- prediction(rf.pred[,2], lol_test$winner)
rf.auc <- performance(rf.roc, 'tpr', 'fpr')
# rf.auc
plot(rf.auc)
abline(0, 1)

# text(0.5, 0.5, as.character(rf.auc@y.values[[1]]))  



【 計算平均平方誤差MSE(Mean Square Error) 】

# mean((result-lol_test$winner)^2)
dim(lol_test)       #  9972    71
[1] 9972   75
dim(lol_train)      # 39884    71
[1] 39884    75
summary(model1)

Call:
glm(formula = winner ~ firstBlood + firstTower + firstInhibitor + 
    firstBaron + firstDragon + firstRiftHerald + tower_gap + 
    dragon_gap + baron_gap + inhibitorKills_gap + gameDuration, 
    family = "binomial", data = lol_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-3.8432  -0.0338  -0.0023   0.0325   4.7229  

Coefficients:
                   Estimate Std. Error z value Pr(>|z|)    
(Intercept)         0.37382    0.27022   1.383 0.166540    
firstBlood2        -0.12787    0.07033  -1.818 0.069042 .  
firstTower2        -0.87222    0.07553 -11.548  < 2e-16 ***
firstInhibitor1     0.51610    0.13912   3.710 0.000207 ***
firstInhibitor2    -0.58941    0.14017  -4.205 2.61e-05 ***
firstBaron1         0.52945    0.13253   3.995 6.47e-05 ***
firstBaron2        -0.08496    0.13143  -0.646 0.518002    
firstDragon1        0.47420    0.26300   1.803 0.071388 .  
firstDragon2        0.59231    0.26366   2.246 0.024673 *  
firstRiftHerald1    0.22080    0.08751   2.523 0.011634 *  
firstRiftHerald2   -0.59453    0.08857  -6.713 1.91e-11 ***
tower_gap          -1.28958    0.02374 -54.313  < 2e-16 ***
dragon_gap          0.04511    0.02163   2.086 0.037020 *  
baron_gap          -0.62911    0.05491 -11.456  < 2e-16 ***
inhibitorKills_gap  0.10260    0.03776   2.717 0.006591 ** 
gameDuration       -0.01023    0.00544  -1.880 0.060134 .  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 55287.7  on 39883  degrees of freedom
Residual deviance:  5838.7  on 39868  degrees of freedom
AIC: 5870.7

Number of Fisher Scoring iterations: 9
LS0tCnRpdGxlOiAiMTA3MeW3qOmHj+acn+acq+WwiOahiO+8mihMb0wpIExlYWd1ZSBvZiBMZWdlbmRzIFJhbmtlZCBHYW1lcyIKCmF1dGhvcjogIumZs+aMr+WYieOAgeaWvemHh+W9o+OAgealiueniei7kuOAgea4qeW9peerueOAgeS9leiqnuWpleOAgeadjuaoguetoOOAgealiuWHseWAq+OAgeWRqOm8jumIniIKCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCjxicj4KPGJyPgo8YnI+Cjxicj4KPGJyPgoqKuOAkCDpgbjnlKjos4fmlpnpm4bku4vntLkg44CRKioKCisg6YG455So6LOH5paZ6ZuG77yaKExvTCkgTGVhZ3VlIG9mIExlZ2VuZHMgUmFua2VkIEdhbWVzCgorIOizh+aWmeethuaVuO+8mjUxLDQ5MOethgoKKyDorormlbjlgIvmlbjvvJo2MeWAiwoKKyDos4fmlpnkvobmupDvvJpLYWdnbGUKCgo8YnI+Cjxicj4KKirjgJAg5aWX5Lu26LyJ5YWlIOOAkSoqCmBgYHtyfQojIOi8ieWFpeaJgOmcgOeahOWll+S7tihMb2FkaW5nIHBhY2thZ2UpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHJlc2hhcGUpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHJlc2hhcGUpCmxpYnJhcnkoUk9DUikKbGlicmFyeShjYXJldCkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkocFJPQykKCiMg5pu05pS5UueahOmgkOioreiqnuezuwpTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCdDJykgICAgICAKYGBgCgoKPGJyPgo8YnI+Cioq44CQIOaVmOi/sOe1seioiCDjgJEqKgpgYGB7cn0KIyDoroDlj5bmqpTmoYgKTE9MIDwtIHJlYWRfY3N2KCJMT0xnYW1lZGF0YS5jc3YiKQoKIyDmioroi7Hpm4TnmoTlh7rloLTmrKHmlbjlgZrntbHoqIjkuKbkuJTnrpflh7rli53njocKTE9MJHNlYXNvbklkID0gTlVMTApoZXJvID0gcmJpbmQodGFibGUoTE9MJHQxX2NoYW1wMWlkKSwgdGFibGUoTE9MJHQxX2NoYW1wMmlkKSwgdGFibGUoTE9MJHQxX2NoYW1wM2lkKSwgdGFibGUoTE9MJHQxX2NoYW1wNGlkKSwKdGFibGUoTE9MJHQxX2NoYW1wNWlkKSwgdGFibGUoTE9MJHQyX2NoYW1wMWlkKSwgdGFibGUoTE9MJHQyX2NoYW1wMmlkKSwgdGFibGUoTE9MJHQyX2NoYW1wM2lkKSwgdGFibGUoTE9MJHQyX2NoYW1wNGlkKSwgdGFibGUoTE9MJHQyX2NoYW1wNWlkKSkKaGVybyA8LSBoZXJvICU+JSB0KCkgJT4lIGFzLmRhdGEuZnJhbWUoKQpoZXJvJHJlZDEgPC0gcm93U3VtcyhzdWJzZXQoaGVybywgLGMoIlYxIiwiVjIiLCJWMyIsIlY0IiwiVjUiKSkpCmhlcm8kYmx1ZTEgPC0gcm93U3VtcyhzdWJzZXQoaGVybywgLGMoIlY2IiwiVjciLCJWOCIsIlY5IiwiVjEwIikpKQpoZXJvJGhlcm9uYW1lIDwtIHJvd25hbWVzKGhlcm8pCgojIG1kYXRhIDwtIG1lbHQoc2VsZWN0KGhlcm8sYygxOjEwLDEzKSksaWQgPSAiaGVyb25hbWUiKQojIHJvd1N1bXMoaGVybykgCiMgaGVybyA8LSByYmluZChoZXJvLCBUb3RhbHMgPSBjb2xTdW1zKGhlcm8pKQpiYXJwbG90KGhlcm9bLDExXSwgbmFtZXMuYXJnID0gcm93bmFtZXMoaGVybyksIGhvcml6ID0gVFJVRSwgbGFzID0gMSkKCndpbmxvID0gc2VsZWN0KExPTCxjKDQsIDExLCAxNCwgMTcsIDIwLCAyMywgMzYsIDM5LCA0MiwgNDUsIDQ4KSkKd2lubG8kcm93bmFtZSA8LSByb3duYW1lcyh3aW5sbykKCm0gPC0gd2lubG8gJT4lIHNlbGVjdCgyIDogNikKY29sbmFtZXMobSkgPC0gYygidiIsICJ2IiwgInYiLCAidiIsICJ2IikKbSA8LSBjYmluZChtLHdpbmxvWywxMl0pCm1kYXRhIDwtIG1lbHQobSwgaWQgPSAicm93bmFtZSIpCmdncGxvdChkYXRhID0gbWRhdGEsIGFlcyh4ID0gdmFsdWUsIGNvbG91ciA9ICdyZWQnKSkgKyBnZW9tX2hpc3RvZ3JhbShzdGF0ID0gImNvdW50IikKCm0yIDwtIHdpbmxvICU+JSBzZWxlY3QoNyA6IDExKQpjb2xuYW1lcyhtMikgPSBjKCJ2IiwgInYiLCAidiIsICJ2IiwgInYiKQptMiA8LSBjYmluZChtMiwgd2lubG9bLDEyXSkKbWRhdGEyIDwtIG1lbHQobTIsIGlkID0gInJvd25hbWUiKQpnZ3Bsb3QoZGF0YSA9IG1kYXRhMiwgYWVzKHggPSB2YWx1ZSwgY29sb3VyPSdyZWQnKSkgKyBnZW9tX2hpc3RvZ3JhbShzdGF0ID0gImNvdW50IikKCm0zIDwtIHdpbmxvICU+JSBzZWxlY3QoMiA6IDExKQpjb2xuYW1lcyhtMykgPSBjKCJ2IiwgInYiLCAidiIsICJ2IiwgInYiLCAidiIsICJ2IiwgInYiLCAidiIsICJ2IikKbTMgPC0gY2JpbmQobTMsd2lubG9bLDEyXSkKbWRhdGEzIDwtIG1lbHQobTMsIGlkID0gInJvd25hbWUiKQpnZ3Bsb3QoZGF0YSA9IG1kYXRhMywgYWVzKHggPSB2YWx1ZSwgY29sb3VyID0gJ3JlZCcpKSArIGdlb21faGlzdG9ncmFtKHN0YXQgPSAiY291bnQiKQpgYGAKCgo8YnI+Cjxicj4KYGBge3J9CiMg5oqK6Iux6ZuE55qE5Ye65aC05qyh5pW45YGa57Wx6KiI5Lim5LiU566X5Ye65Yud546HCmhlcm8gPSBhcy5kYXRhLmZyYW1lKGhlcm8pCgp3aW50MSA8LSBzdWJzZXQod2lubG8sIHdpbm5lciA9PSAxKQp3aW50MiA8LSBzdWJzZXQod2lubG8sIHdpbm5lciA9PSAyKQoKd2luY291bnQxIDwtIFJlZHVjZShyYmluZCwgTWFwKGZ1bmN0aW9uKGNvdW50KXRhYmxlKHdpbnQxWyxjb3VudF0pLCBjKDI6NikpKQpsb3NlY291bnQyIDwtIFJlZHVjZShyYmluZCwgTWFwKGZ1bmN0aW9uKGNvdW50KXRhYmxlKHdpbnQxWyxjb3VudF0pLCBjKDc6MTEpKSkKd2luY291bnQyIDwtIFJlZHVjZShyYmluZCwgTWFwKGZ1bmN0aW9uKGNvdW50KXRhYmxlKHdpbnQyWyxjb3VudF0pLCBjKDc6MTEpKSkKbG9zZWNvdW50MSA8LSBSZWR1Y2UocmJpbmQsIE1hcChmdW5jdGlvbihjb3VudCl0YWJsZSh3aW50MlssY291bnRdKSwgYygyOjYpKSkKCndpbmNvdW50MSA8LSB3aW5jb3VudDEgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgdCgpCndpbmNvdW50MiA8LSB3aW5jb3VudDIgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgdCgpCmxvc2Vjb3VudDEgPC0gbG9zZWNvdW50MSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSB0KCkKbG9zZWNvdW50MiA8LSBsb3NlY291bnQyICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHQoKQoKd2luY291bnQgPC0gY2JpbmQod2luY291bnQxLHdpbmNvdW50MikKbG9zZWNvdW50IDwtIGNiaW5kKGxvc2Vjb3VudDEsbG9zZWNvdW50MikKCmhlcm8kd2luIDwtIHdpbmNvdW50ICU+JSByb3dTdW1zKCkKaGVybyRsb3NlIDwtIGxvc2Vjb3VudCAlPiUgcm93U3VtcygpCgpybShsb3NlY291bnQxLCBsb3NlY291bnQyLCB3aW5jb3VudDEsIHdpbmNvdW50Miwgd2lubG8sIHdpbnQxLCB3aW50MikKaGVybyR3aW5yYXRlIDwtIGhlcm8kd2luIC8gKGhlcm8kd2luICsgaGVybyRsb3NlKQptYXgoaGVybyR3aW5yYXRlKQoKaGVybyAlPiUgZmlsdGVyKGhlcm9uYW1lID09ICJZYXN1byIgfCBoZXJvbmFtZSA9PSAiWmVkIiB8IGhlcm9uYW1lID09ICJKYW5uYSIgfCBoZXJvbmFtZSA9PSAiRGFyaXVzIiB8IGhlcm9uYW1lID09ICJDaG8nR2F0aCIgfCBoZXJvbmFtZSA9PSAiVWR5ciIgfCBoZXJvbmFtZSA9PSAiU2thcm5lciIgfCBoZXJvbmFtZSA9PSAiUnl6ZSIpCgpMT0wgJT4lIGZpbHRlcih0MV9pbmhpYml0b3JLaWxscyA+PTEwIHwgdDJfaW5oaWJpdG9yS2lsbHMgPj0gMTApCmBgYAoKCjxicj4KPGJyPgpgYGB7cn0KIyDntbHoqIjmr4/pmrvop5LoibLooqtiYW7mrKHmlbgKYmFuY291bnQgPC0gc2VsZWN0KExPTCwgYygzMTozNSw1Njo2MCkpCmJhbmNvdW50IDwtIFJlZHVjZShyYmluZCwgTWFwKGZ1bmN0aW9uKGNvdW50KXRhYmxlKGJhbmNvdW50Wyxjb3VudF0pICwgYygxOjEwKSkpCmJhbmNvdW50IDwtIGJhbmNvdW50ICU+JSB0KCkgJT4lIHJvd1N1bXMoKSAlPiUgYXMuZGF0YS5mcmFtZSgpCmJhbmNvdW50JG5hbWUgPC0gYmFuY291bnQgJT4lIHJvd25hbWVzKCkKZHJhd2RyYXcgPC0gYmFuY291bnQgJT4lIGZpbHRlciguPj0yMDAwMCB8IC48PTEwMCkKCmJhcnBsb3QoZHJhd2RyYXdbLDFdLCBuYW1lcy5hcmcgPSBkcmF3ZHJhdyRuYW1lLCBob3JpeiA9IFRSVUUsIGxhcz0xKQpgYGAKCjxicj4KPGJyPgpgYGB7cn0KIyDlsIfmr4/lgIvohbPoibLnmoTnqK7poZ7lkozmiYDluLbnmoTlj6zllprluKvmioDog73lgZrntbHoqIgKY2h0b3RhZyA9IHNlbGVjdChMT0wsIGMoNjEsIDEyLCAxMywgNjIsIDE1LCAxNiwgNjMsIDE4LCAxOSwgNjQsIDIxLCAyMiwgNjUsIDI0LCAyNSwgNjYsIDM3LCAzOCwgNjcsIDQwLCA0MSwgNjgsIDQzLCA0NCwgNjksIDQ2LCA0NywgNzAsIDQ5LCA1MCkpCgojIGNodG90YWcgPSByYmluZCh0YWJsZShzZWxlY3QoY2h0b3RhZywxLDIpKSx0YWJsZShzZWxlY3QoY2h0b3RhZywxLDMpKSx0YWJsZShzZWxlY3QoY2h0b3RhZyw0LDUpKSx0YWJsZShzZWxlY3QoY2h0b3RhZyw0LDYpKSx0YWJsZShzZWxlY3QoY2h0b3RhZyw3LDgpKSx0YWJsZShzZWxlY3QoY2h0b3RhZyw3LDkpKSx0YWJsZShzZWxlY3QoY2h0b3RhZywxMCwxMSkpLHRhYmxlKHNlbGVjdChjaHRvdGFnLDEwLDEyKSksdGFibGUoc2VsZWN0KGNodG90YWcsMTMsMTQpKSx0YWJsZShzZWxlY3QoY2h0b3RhZywxMywxNSkpLHRhYmxlKHNlbGVjdChjaHRvdGFnLDE2LDE3KSksdGFibGUoc2VsZWN0KGNodG90YWcsMTYsMTgpKSx0YWJsZShzZWxlY3QoY2h0b3RhZywxOSwyMCkpLHRhYmxlKHNlbGVjdChjaHRvdGFnLDE5LDIxKSksdGFibGUoc2VsZWN0KGNodG90YWcsMjIsMjMpKSx0YWJsZShzZWxlY3QoY2h0b3RhZywyMiwyNCkpLHRhYmxlKHNlbGVjdChjaHRvdGFnLDI1LDI2KSksdGFibGUoc2VsZWN0KGNodG90YWcsMjUsMjcpKSx0YWJsZShzZWxlY3QoY2h0b3RhZywyOCwyOSkpLHRhYmxlKHNlbGVjdChjaHRvdGFnLDI4LDMwKSkpCgpjaHRvdGFnID0gY2JpbmQodGFibGUoY2h0b3RhZ1ssYygxLDIpXSksIHRhYmxlKGNodG90YWdbLGMoMSwzKV0pLCB0YWJsZShjaHRvdGFnWyxjKDQsNSldKSwgdGFibGUoY2h0b3RhZ1ssYyg0LDYpXSksIHRhYmxlKGNodG90YWdbLGMoNyw4KV0pLCB0YWJsZShjaHRvdGFnWyxjKDcsOSldKSwgdGFibGUoY2h0b3RhZ1ssYygxMCwxMSldKSwgdGFibGUoY2h0b3RhZ1ssYygxMCwxMildKSwgdGFibGUoY2h0b3RhZ1ssYygxMywxNCldKSwgdGFibGUoY2h0b3RhZ1ssYygxMywxNSldKSwgdGFibGUoY2h0b3RhZ1ssYygxNiwxNyldKSwgdGFibGUoY2h0b3RhZ1ssYygxNiwxOCldKSwgdGFibGUoY2h0b3RhZ1ssYygxOSwyMCldKSwgdGFibGUoY2h0b3RhZ1ssYygxOSwyMSldKSwgdGFibGUoY2h0b3RhZ1ssYygyMiwyMyldKSwgdGFibGUoY2h0b3RhZ1ssYygyMiwyNCldKSwgdGFibGUoY2h0b3RhZ1ssYygyNSwyNildKSwgdGFibGUoY2h0b3RhZ1ssYygyNSwyNyldKSwgdGFibGUoY2h0b3RhZ1ssYygyOCwyOSldKSwgdGFibGUoY2h0b3RhZ1ssYygyOCwzMCldKSkKY2h0b3RhZyA8LSBjaHRvdGFnICU+JSBhcy5kYXRhLmZyYW1lKCkKCiMgYXBwbHkoY2h0b3RhZywgTUFSR0lOID0gMiwgRlVOID0gdGFibGUpCgojIGZvcihpIGluIDE6OSl7CiMgICBmb3IoaiBpbiAxOjYpCiMgICAgY2h0b3RhZ1tqLGldPSBzdW0oY2h0b3RhZ1tqLGldLGNodG90YWdbaixpKzldLGNodG90YWdbaixpKzE4XSxjaHRvdGFnW2osaSsyN10sY2h0b3RhZ1tqLGkrMzZdLGNodG90YWdbaixpKzQ1XSxjaHRvdGFnW2osaSs1NF0sY2h0b3RhZ1tqLGkrNjNdLGNodG90YWdbaixpKzcyXSxjaHRvdGFnW2osaSs4MF0sY2h0b3RhZ1tqLGkrODJdLGNodG90YWdbaixpKzkwXSxjaHRvdGFnW2osaSs5OV0sY2h0b3RhZ1tqLGkrMTA4XSxjaHRvdGFnW2osaSsxMTddLGNodG90YWdbaixpKzEyNl0sY2h0b3RhZ1tqLGkrMTM1XSxjaHRvdGFnW2osaSsxNDRdLGNodG90YWdbaixpKzE1M10sY2h0b3RhZ1tqLGkrMTYyXSxjaHRvdGFnW2osaSsxNzFdKQojIH0KCiMgY2h0b3RhZ1ssYygxMDoxODApXT1OVUxMCmBgYAoKCjxicj4KPGJyPgpgYGB7cn0KIyDnlavlh7rohbPoibLnqK7poZ7lsI3mlrzmlJzluLblj6zllprluKvmioDog73nmoTnhrHlnJYKaGVhdG1hcChhcy5tYXRyaXgoY2h0b3RhZ1ssIGMoMTo5KV0pKQpkIDwtIGRlbnNpdHkoTE9MJHQxX3Rvd2VyS2lsbHMpCmQyIDwtIGRlbnNpdHkoTE9MJHQxX2luaGliaXRvcktpbGxzKQpkMyA8LSBkZW5zaXR5KExPTCR0MV9iYXJvbktpbGxzKQpkNCA8LSBkZW5zaXR5KExPTCR0MV9kcmFnb25LaWxscykKZDUgPC0gZGVuc2l0eShMT0wkZmlyc3RSaWZ0SGVyYWxkKSAgICAgICAgIyByZXR1cm5zIHRoZSBkZW5zaXR5IGRhdGEgCgpwbG90KGQpCnBsb3QoZDIpCnBsb3QoZDMpCnBsb3QoZDQpCgpwbG90KGQ1KQoKcCA8LSBnZ3Bsb3QoTE9MLGFlcyh4PWdhbWVEdXJhdGlvbix5PXQxX3Rvd2VyS2lsbHMpKQpwICsgZ2VvbV9wb2ludCgpCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvcj1mYWN0b3IodDFfaW5oaWJpdG9yS2lsbHMpKSkKYGBgCgoKCjxicj4KPGJyPgpgYGB7cn0KIyDnlavlh7rmr4/lgIvohbPoibLlsI3mh4nmrrrpvo3mrKHmlbjnmoTntbHoqIgKcmVka2lsbGRyYWcgPC0gc2VsZWN0KExPTCxjKDExLDE0LDE3LDIwLDIzLDI5KSkgICAgICAgICAgICMgJT4lIHQoKSAtPiByZWRraWxsZHJhZwpibHVla2lsbGRyYWcgPC0gIHNlbGVjdChMT0wsYygzNiwzOSw0Miw0NSw0OCw1NCkpICAgICAgICAgIyAlPiUgdCgpIC0+IGJsdWVraWxsZHJhZwoKYWdnIDwtIGxhcHBseShsaXN0KHJlZGtpbGxkcmFnJHQxX2NoYW1wMWlkLCByZWRraWxsZHJhZyR0MV9jaGFtcDJpZCwgcmVka2lsbGRyYWckdDFfY2hhbXAzaWQsIHJlZGtpbGxkcmFnJHQxX2NoYW1wNGlkLCByZWRraWxsZHJhZyR0MV9jaGFtcDVpZCksIGZ1bmN0aW9uKG51bSlhZ2dyZWdhdGUocmVka2lsbGRyYWdbLDZdLGJ5ID0gbGlzdChudW0pLCBGVU4gPSBtZWFuKSkKbWVhbmRyYXBraWxsIDwtIFJlZHVjZShjYmluZCxNYXAoZnVuY3Rpb24obnVtKWFnZ1tbbnVtXV0sYygxOjUpKSkKaGVybyRyZWRtZWFuZHJhZ2tpbGwgPC0gbWVhbmRyYXBraWxsWyxjKDIsNCw2LDgsMTApXSAlPiUgcm93U3VtcygpCgphZ2cyIDwtIGxhcHBseShsaXN0KGJsdWVraWxsZHJhZyR0Ml9jaGFtcDFpZCwgYmx1ZWtpbGxkcmFnJHQyX2NoYW1wMmlkLCBibHVla2lsbGRyYWckdDJfY2hhbXAzaWQsIGJsdWVraWxsZHJhZyR0Ml9jaGFtcDRpZCwgYmx1ZWtpbGxkcmFnJHQyX2NoYW1wNWlkKSwgZnVuY3Rpb24obnVtKWFnZ3JlZ2F0ZShibHVla2lsbGRyYWdbLDZdLCBieSA9IGxpc3QobnVtKSwgRlVOID0gbWVhbikpCm1lYW5kcmFwa2lsbDIgPC0gUmVkdWNlKGNiaW5kLCBNYXAoZnVuY3Rpb24obnVtKWFnZzJbW251bV1dLGMoMTo1KSkpCmhlcm8kYmx1ZW1lYW5kcmFna2lsbCA8LSBtZWFuZHJhcGtpbGwyWyxjKDIsNCw2LDgsMTApXSAlPiUgcm93U3VtcygpCgojIGdncGxvdChyZWRraWxsZHJhZyxhZXModDFfY2hhbXA1aWQsZmlsbD1hcy5mYWN0b3IodDFfZHJhZ29uS2lsbHMpKSkrZ2VvbV9iYXIocG9zaXRpb24gPSJzdGFjayIpCiMgZ2dwbG90KGJsdWVraWxsZHJhZyxhZXModDJfY2hhbXA1aWQsZmlsbD1hcy5mYWN0b3IodDJfZHJhZ29uS2lsbHMpKSkrZ2VvbV9iYXIocG9zaXRpb24gPSJzdGFjayIpCgp4IDwtIGdncGxvdChkYXRhID0gaGVybywgYWVzKHggPSBoZXJvbmFtZSwgeSA9IHJlZG1lYW5kcmFna2lsbCkpICsgCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLGFlcyhmaWxsID0gcmVkbWVhbmRyYWdraWxsKSkrIAogICAgICBjb29yZF9mbGlwKCkKeSA8LSBnZ3Bsb3QoZGF0YSA9IGhlcm8sIGFlcyh4PWhlcm9uYW1lLCB5ID0gYmx1ZW1lYW5kcmFna2lsbCkpICsgCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLGFlcyhmaWxsID0gYmx1ZW1lYW5kcmFna2lsbCkpKyAKICAgICAgY29vcmRfZmxpcCgpCngKeQoKZ2dzYXZlKHBsb3QgPSB5LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwLCBmaWxlbmFtZSA9ICJ0b3dlci5wbmciKQpnZ3NhdmUocGxvdCA9IHgsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDAsIGZpbGVuYW1lID0gInRvd2VyMi5wbmciKQpgYGAKCgo8YnI+Cjxicj4KYGBge3J9CiMg55Wr5Ye65q+P5YCL6IWz6Imy5bCN5pa85ouG5aGU5qyh5pW455qE57Wx6KiICnkgPC0gZ2dwbG90KGRhdGEgPSBoZXJvLCBhZXMoeCA9IGhlcm9uYW1lcywgeSA9IHJlZG1lYW5kcmFna2lsbCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IixhZXMoY29sb3VyID0gcmVkbWVhbmRyYWdraWxsLHdpZHRoID0gMSkpKyAKICBjb29yZF9mbGlwKCkrCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAyKSkrCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gInJlZCIpCiAgIyBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gImdyZWVuIiwgaGlnaCA9ICJ5ZWxsb3ciKQogIAp5PWdncGxvdChkYXRhID0gaGVybywgYWVzKHggPSBoZXJvbmFtZSwgeSA9IHJlZG1lYW5kcmFna2lsbCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IixhZXMoZmlsbCA9IHJlZG1lYW5kcmFna2lsbCx3aWR0aCA9IDEpKSArIAogIGNvb3JkX2ZsaXAoKSsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDIpKSsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIikKeQpnZ3NhdmUocGxvdCA9IHksIHdpZHRoID0gMTAsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDAsIGZpbGVuYW1lID0gImhhaGEucG5nIikKCnggPC0gZ2dwbG90KGRhdGEgPSBoZXJvLCBhZXMoeCA9IGhlcm9uYW1lLCB5ID0gYmx1ZW1lYW5kcmFna2lsbCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgYWVzKGZpbGwgPSBibHVlbWVhbmRyYWdraWxsKSx3aWR0aCA9IDEpKyAKICBjb29yZF9mbGlwKCkrCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT0wLCBoanVzdD0yKSkKeAoKZ2dzYXZlKHBsb3QgPSB5LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwLCBmaWxlbmFtZSA9ICJoYWhhLnBuZyIpCmdnc2F2ZShwbG90ID0geCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCwgZmlsZW5hbWUgPSAiaGFoYTIucG5nIikKYGBgCgoKCjxicj4KPGJyPgpgYGB7cn0KIyDmrLLnnq3op6PnlbbnjbLli53mlrnngrrol43mlrnmmYLvvIzploPnj74oZmxhc2gp5pS+5ZyoROmNteiIh0bpjbXnmoTmr5TkvosKc3VtbWFyeShnbG0od2lubmVyIH4gZmlyc3REcmFnb24sIGRhdGE9IGxvbF90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIikpCnRlc3QxIDwtIHN1YnNldChsb2xfdHJhaW4sbG9sX3RyYWluJGZpcnN0RHJhZ29uIT0wKQpzdW1tYXJ5KGdsbSh0ZXN0MSR3aW5uZXIgfiBhcy5mYWN0b3IodGVzdDEkZmlyc3REcmFnb24pLGZhbWlseSA9ICJiaW5vbWlhbCIpKQoKc3VtbWFyeShnbG0obG9sX3RyYWluJHdpbm5lciB+IGxvbF90cmFpbiR0MV9jaGFtcDFfc3VtMSxmYW1pbHkgPSAiYmlub21pYWwiKSkKYSA8LSBsb2xbLGMoNSwxMywxNiwxOSwyMiwyNSldCnN1bW1hcnkoYSkKYiA8LSBsb2xbLGMoNSwzOCw0MSw0NCw0Nyw1MCldCnN1bW1hcnkoYikKYyA8LSBhW2Ekd2lubmVyPT0iMSIsXQpkIDwtIGJbYiR3aW5uZXI9PSIxIixdCnN1bW1hcnkoYykKc3VtbWFyeShkKQpzMSA8LSAxMzgxOSArIDEzNzE1ICsgMTM3MDggKyAxMzYxNCArIDEzNjgxCnMyIDwtIDEzNzY5ICsgMTM3MjcgKyAxMzg1NyArIDEzNjU5ICsgMTM1NzIKczEgLyAoMjUyMTEgKiA1KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIOeNsuWLneaWueeCuuiXjeaWueaZgu+8jOWwh+mWg+ePvuaUvuWcqETpjbXnmoTmr5TkvosgICAgICAgICAgIyAwLjU0MzcwNzEKczIgLyAoMjUyMTEgKiA1KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIOeNsuWLneaWueeCuuiXjeaWueaZgu+8jOWwh+mWg+ePvuaUvuWcqEbpjbXnmoTmr5TkvosgICAgICAgICAgIyAwLjU0NDA4CmBgYAoKCgo8YnI+Cjxicj4KKirjgJAg6LOH5paZ5YmN6JmV55CGIOOAkSoqCgorIOWwh+inkuiJsuWQjemChOacieWQhOWAi+inkuiJsueahOmhnuWeiyjkvovlpoJzdXBwb3J0562JKeaUvuWFpeizh+aWmembhueVtuS4re+8jOS9huaykuacieS/neeVmeWOn+acieaVuOWtl+eahOmDqOWIhu+8jOS7pemBv+WFjeizh+aWmembhumBjuaWvOa3t+S6guOAggoKKyDlsIdnYW1lRHVyYXRpb24o6YGK5oiy6YCy6KGM5pmC6ZaT55qE6YOo5YiGKeaPm+eul+aIkOS7peOAjOWIhumQmOOAjeeCuuWWruS9jeOAggoKKyDliKrpmaTkuI3lkIjnkIbnmoTlgLzvvIzkvovlpoLpppbmrrrngrowKOS7o+ihqOipsuWgtOmBiuaIsueEoeS6uuaLv+WIsOmmluauuiks56ys5LiA5bqn5aGU54K6MCjku6PooajoqbLloLTpgYrmiLLnhKHkurrmi7/liLDnrKzkuIDluqfloZQp5Lul5Y+K6YGK5oiy5pmC6ZaT5bCR5pa8MTXliIbpkJjnmoTos4fmlpnjgIIKCisg55Sx5pa85pys6LOH5paZ6ZuG54Sh5Lu75L2V56m65YC877yM5omA5Lul5rKS5YGa56m65YC855u46Zec6JmV55CG44CCCgo8YnI+Cjxicj4KKirjgJAgKOiZleeQhuW+jCnos4fmlpnpm4bos4foqIog44CRKioKCisg5Ymp5LiLNDksODU2562GKOWOn+WFiOaciTUxLDQ5MOethikg44CCCgorIOaWsOWinuiuiuaVuO+8muaWsOWinjEw5YCL5qyE5L2N77yM57i96KiI5YWxNzHlgIvmrITkvY0odDE66JeN5pa544CBdDI657SF5pa577yM6ZuZ5pa55ZCE6ZqK5YiG5YilNeS9jeeOqeWutu+8jOe4veioiOWFsTEw5L2N546p5a6277yM6K6K5pW46Kqq5piO5Zyo5q2k6JmV5LiN6LSF6L+wKe+8mgogICAgKyBnYW1lSWQ66YGK5oiy57eo6JmfCiAgICArIGNyZWF0aW9uVGltZTrpgYrmiLLlibXnq4vmmYLplpMKICAgICsgZ2FtZUR1cmF0aW9uOumBiuaIsuaZgumWkwogICAgKyBzZWFzb25JZDrnrKzlub7lraMKICAgICsgd2lubmVyOuWTquS4gOmaiui0jwogICAgKyBmaXJzdEJsb29kOuWTquS4gOmaiuaLv+WIsOmmluauugogICAgKyBmaXJzdFRvd2VyOuWTquS4gOmaiuaLv+WIsOmmluWhlAogICAgKyBmaXJzdEluaGliaXRvcjrlk6rkuIDpmormi7/liLDpppblhbXnh58KICAgICsgZmlyc3RCYXJvbjrlk6rkuIDpmormi7/liLDpppblt7Tpvo0KICAgICsgZmlyc3REcmFnb2465ZOq5LiA6ZqK5ou/5Yiw6aaW5bCP6b6NCiAgICArIGZpcnN0UmlmdEhlYXJsZDrlk6rkuIDpmormi7/liLDpppbpoJDnpLrogIUKICAgICsgdDFfY2hhbXAxaWQ66JeN5pa556ys5LiA5YCL6KeS6Imy5ZCN56ixCiAgICArIHQxX2NoYW1wMWlkX3RhZ3M66JeN5pa556ys5LiA5YCL6KeS6Imy56iu6aGeCiAgICArIHQxX2NoYW1wMV9zdW0x44CBc3VtMjrol43mlrnnrKzkuIDlgIvop5LoibLlj6zllprluKvmioDog70x44CBMgogICAgKyAuLi4KICAgICsgdDFfdG93ZXJLaWxsczrol43mlrnmi4bkuoblub7lgIvloZQKICAgICsgdDFfaW5oaWJpdG9yS2lsbHM66JeN5pa55ouG5LqG5bm+5YCL5YW154efCiAgICArIHQxX2Jhcm9uS2lsbHM66JeN5pa55ou/5LqG5bm+6Zq75be06b6NCiAgICArIHQxX2RyYWdvbktpbGxzOuiXjeaWueaLv+S6huW5vumau+Wwj+m+jQogICAgKyB0MV9yaWZ0SGVyYWxkS2lsbHM66JeN5pa55ou/5LqG5bm+6Zq76aCQ56S66ICFCiAgICArIHQxX2JhbjE66JeN5pa556ys5LiAYmFuCiAgICArIC4uLgogICAgKyB0MuWQjOS4igogICAgCisg5Yiq6Zmk6K6K5pW477ya5oqKc2Vhc29uSUTliKrmjonvvIzlm6Dngrrlhajpg6jpg73mmK85IAoKKyDmnIDlvozos4fmlpnpm4bnuL3oqIjliankuIs0OTg1Nuethijljp/lhYjmnIk1MTQ5MOethikg44CBNzDlgIvmrITkvY0KCjxicj4KPGJyPgoqKuOAkCDovInlhaXos4fmlpnpm4YoTG9hZGluZyBkYXRhc2V0Ke+8mltMT0xnYW1lZGF0YS5jc3ZdIOOAkSoqCgrmiJHlgJHpgI/pgY7ntYTlk6Hoh6rouqvnmoTpgYrmiLLntpPpqZfvvIzmjJHlh7rmiJHlgJHoqo3ngrrovIPpoa/okZfkuYvos4fmlpnmrITkvY3pgLLooYzorormlbjlnovmhYvovYnmj5sKYGBge3J9CmxvbCA8LSByZWFkLmNzdigiTE9MZ2FtZWRhdGEuY3N2IixoZWFkZXIgPSBUKQoKIyBWaWV3KGxvbCkKCiMg5bCH5qyE5L2N6YCy6KGM5Z6L5oWL6L2J5o+bKHRyYW5zZmVyIG91ciBkYXRhIGFzIGZhY3RvcikKbG9sJHdpbm5lciA8LSBhcy5mYWN0b3IobG9sJHdpbm5lcikKbG9sJGZpcnN0Qmxvb2QgPC0gYXMuZmFjdG9yKGxvbCRmaXJzdEJsb29kKQpsb2wkZmlyc3RUb3dlciA8LSBhcy5mYWN0b3IobG9sJGZpcnN0VG93ZXIpCmxvbCRmaXJzdEluaGliaXRvciA8LSBhcy5mYWN0b3IobG9sJGZpcnN0SW5oaWJpdG9yKQpsb2wkZmlyc3RCYXJvbiA8LSBhcy5mYWN0b3IobG9sJGZpcnN0QmFyb24pCmxvbCRmaXJzdERyYWdvbiA8LSBhcy5mYWN0b3IobG9sJGZpcnN0RHJhZ29uKQpsb2wkZmlyc3RSaWZ0SGVyYWxkIDwtIGFzLmZhY3Rvcihsb2wkZmlyc3RSaWZ0SGVyYWxkKQpgYGAKCjxicj4KPGJyPgoqKuOAkCDlibXpgKDorormlbgg44CRKioKYGBge3J9CmxvbCR0b3dlcl9nYXAgPC0gKGxvbCR0MV90b3dlcktpbGxzIC0gbG9sJHQyX3Rvd2VyS2lsbHMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyDli53osqDpmorkvI3loZTlt64KIyBsb2xfdHJhaW4kdG93ZXJfZ2FwIDwtIChsb2xfdHJhaW4kdDFfdG93ZXJLaWxscyAtIGxvbF90cmFpbiR0Ml90b3dlcktpbGxzKQojIHN1bW1hcnkoZ2xtKHdpbm5lciB+IGxvbF90cmFpbiR0b3dlcl9nYXAsIGRhdGE9IGxvbF90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIikpCmxvbCRkcmFnb25fZ2FwIDwtIChsb2wkdDFfZHJhZ29uS2lsbHMgLSBsb2wkdDJfZHJhZ29uS2lsbHMpICAgICAgICAgICAgICAgICAgICAgICAgICAgIyDli53osqDpmorkvI3mrrrlsI/pvo3mlbjph4/lt67nlbAKbG9sJGJhcm9uX2dhcCA8LSAobG9sJHQxX2Jhcm9uS2lsbHMgLSBsb2wkdDJfYmFyb25LaWxscykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIOWLneiyoOmaiuS8jeauuuW3tOmahuaVuOmHj+W3rueVsApsb2wkaW5oaWJpdG9yS2lsbHNfZ2FwIDwtIChsb2wkdDFfaW5oaWJpdG9yS2lsbHMgLSBsb2wkdDJfaW5oaWJpdG9yS2lsbHMpICAgICAgICAgICAgICMg5Yud6LKg6ZqK5LyN5rC05pm25YW154ef5pW46YeP5beu55WwCmBgYAoKCjxicj4KPGJyPgoqKuOAkOW7uuaooeOAkSoqCioq44CQ6YKP6Lyv5byP6L+05q24KGdsbSkg44CRKioKYGBge3J9CiMg5YiH5Ymy6LOH5paZ77ya5bCH6LOH5paZ5YiH5Ymy5oiQVHJhaW5pbmcgU2V0KGxvbF90cmFpbiksIFRlc3RpbmcgU2V0KGxvbF90ZXN0KQpzZXQuc2VlZCgyMDE4KQp0cmFpbl9pZHggPC0gc2FtcGxlKDE6bnJvdyhsb2wpLCBzaXplID0gMC44ICogbnJvdyhsb2wpLCByZXBsYWNlID0gRikKbG9sX3RyYWluIDwtIGxvbFt0cmFpbl9pZHgsXSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgMzk4ODQgb2JzLiBvZiA3NSB2YXJpYWJsZXMKbG9sX3Rlc3QgPC0gbG9sWy10cmFpbl9pZHgsXSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgOTk3MiBvYnMuIG9mIDc0IHZhcmlhYmxlcyAgICAKc3VtbWFyeShnbG0od2lubmVyIH4gZmlyc3RCbG9vZCArIGZpcnN0VG93ZXIgKyBmaXJzdEluaGliaXRvciArIGZpcnN0QmFyb24gKyBmaXJzdERyYWdvbiArIGZpcnN0UmlmdEhlcmFsZCwgZGF0YSA9IGxvbF90cmFpbiwgZmFtaWx5ID0gImJpbm9taWFsIikpCgojIOmgkOa4rChnbG0gcHJlZGljdGlvbikoY29tbW9uIHNlbnNlKQptb2RlbDEgPC0gZ2xtKHdpbm5lciB+IGZpcnN0Qmxvb2QgKyBmaXJzdFRvd2VyICsgZmlyc3RJbmhpYml0b3IgKyBmaXJzdEJhcm9uICsgZmlyc3REcmFnb24gKyBmaXJzdFJpZnRIZXJhbGQsIGRhdGEgPSBsb2xfdHJhaW4gLGZhbWlseSA9ICJiaW5vbWlhbCIpCnJlc3VsdCA8LSBwcmVkaWN0KG1vZGVsMSxuZXdkYXRhID0gbG9sX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQp0YWJsZShsb2xfdGVzdCR3aW5uZXIsIHJlc3VsdD4wLjUpICU+JSB7c3VtKGRpYWcoLikpL3N1bSguKX0gICAgICAgICAjIEFDQyA9IDAuOTA0NDMyNAoKIyDpoJDmuKwoZ2xtIHByZWRpY3Rpb24pKOWKoOWFpXRvd2VsX2dhcCkKbW9kZWwxIDwtIGdsbSh3aW5uZXIgfiBmaXJzdEJsb29kICsgZmlyc3RUb3dlciArIGZpcnN0SW5oaWJpdG9yICsgZmlyc3RCYXJvbiArIGZpcnN0RHJhZ29uICsgZmlyc3RSaWZ0SGVyYWxkICsgdG93ZXJfZ2FwLCBkYXRhID0gbG9sX3RyYWluICxmYW1pbHkgPSAiYmlub21pYWwiKQpyZXN1bHQgPC0gcHJlZGljdChtb2RlbDEsbmV3ZGF0YSA9IGxvbF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikKdGFibGUobG9sX3Rlc3Qkd2lubmVyLCByZXN1bHQ+MC41KSAlPiUge3N1bShkaWFnKC4pKS9zdW0oLil9ICAgICAgICAgIyBBQ0MgPSAwLjk3MzYyNjIKCiMg6aCQ5risKGdsbSBwcmVkaWN0aW9uKSjliqDlhaV0b3dlbF9nYXAs5Lim5ou/5o6J5LiN6aGv6JGX5LmL6K6K5pW4KQptb2RlbDEgPC0gZ2xtKHdpbm5lciB+IGZpcnN0VG93ZXIgKyBmaXJzdEluaGliaXRvciArIGZpcnN0UmlmdEhlcmFsZCArIHRvd2VyX2dhcCwgZGF0YSA9IGxvbF90cmFpbiAsZmFtaWx5ID0gImJpbm9taWFsIikKcmVzdWx0IDwtIHByZWRpY3QobW9kZWwxLG5ld2RhdGEgPSBsb2xfdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpCnRhYmxlKGxvbF90ZXN0JHdpbm5lciwgcmVzdWx0PjAuNSkgJT4lIHtzdW0oZGlhZyguKSkvc3VtKC4pfSAgICAgICAgICMgQUNDID0gMC45NzMxMjQ3CgojIOmgkOa4rChnbG0gcHJlZGljdGlvbiko5Yqg5YWldG93ZWxfZ2FwLCBkcmFnb25fZ2FwLCBiYXJvbl9nYXAsIGluaGliaXRvcktpbGxzX2dhcCwgZ2FtZUR1cmF0aW9uKQptb2RlbDEgPC0gZ2xtKHdpbm5lciB+IGZpcnN0Qmxvb2QgKyBmaXJzdFRvd2VyICsgZmlyc3RJbmhpYml0b3IgKyBmaXJzdEJhcm9uICsgZmlyc3REcmFnb24gKyBmaXJzdFJpZnRIZXJhbGQgKyB0b3dlcl9nYXAgKyBkcmFnb25fZ2FwICsgYmFyb25fZ2FwICsgaW5oaWJpdG9yS2lsbHNfZ2FwICsgZ2FtZUR1cmF0aW9uLCBkYXRhID0gbG9sX3RyYWluICxmYW1pbHkgPSAiYmlub21pYWwiKQpyZXN1bHQgPC0gcHJlZGljdChtb2RlbDEsbmV3ZGF0YSA9IGxvbF90ZXN0LCB0eXBlID0gInJlc3BvbnNlIikKdGFibGUobG9sX3Rlc3Qkd2lubmVyLCByZXN1bHQ+MC41KSAlPiUge3N1bShkaWFnKC4pKS9zdW0oLil9ICAgICAgICAgIyBBQ0MgPSAwLjk3NDIyNzgKCnN1bW1hcnkobW9kZWwxKQpgYGAKCjxicj4KPGJyPgoqKuOAkCDpgo/ovK/lvI/ov7TmrbhST0MoUk9DIGN1cnZlIG9mIExvZ2lzdGljIFJlZ3Jlc3Npb24pIOOAkSoqCmBgYHtyfQpwcmVkIDwtIHByZWRpY3Rpb24ocmVzdWx0LCBsb2xfdGVzdCR3aW5uZXIpCnBlcmYgPC0gcGVyZm9ybWFuY2UocHJlZCwgbWVhc3VyZSA9ICJ0cHIiLCB4Lm1lYXN1cmUgPSAiZnByIikKYXVjIDwtIHBlcmZvcm1hbmNlKHByZWQsICJhdWMiKQoKIyDnuaroo71ST0MgY3VydmXkuYvlnJblvaLvvIzkuKbnrpflh7pBVUMKcGxvdChwZXJmLCBtYWluID0gIlJPQyBjdXJ2ZShMb2dpc3RpYyBSZWdyZXNzaW9uKSIsIHhsYWIgPSAiU3BlY2lmaWNpdHkoRlBSKSIsIHlsYWIgPSAiU2Vuc2l0aXZpdHkoVFBSKSIpCmFibGluZSgwLCAxKQp0ZXh0KDAuNSwgMC41LCBhcy5jaGFyYWN0ZXIoYXVjQHkudmFsdWVzW1sxXV0pKSAgICAgICAgICAgIyBBVUMgPSAwLjk5NjYKYGBgCgo8YnI+Cjxicj4KKirjgJAg6YKP6Lyv5byP6L+05q245Lqk5Y+J6amX6K2JKENyb3NzLVZhbGlkYXRpb24oQ1YpIG9mIExvZ2lzdGljIFJlZ2Vzc2lvbikg44CRKioKYGBge3J9CiMgR2V0IGstZm9sZCBDViBjb25mdXNpb24gbWF0cml4IGZvciBMb2dpc3RpYyBSZWdyZXNzaW9uIG1vZGVsCiMgZjogZm9ybXVsYSwgZDogZGF0YSwgazogbnVtYmVyIG9mIGZvbGRzLCBjdXRvZmY6IGN1dG9mZiBwb2ludCAwLTEKa19mb2xkX0NWX2xvZ2l0ID0gZnVuY3Rpb24oZiwgZCwgaywgY3V0b2ZmKXsKICBudW1PZlJlYyA9IG5yb3coZCkgIyBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zCiAgcmVwb25zZV92YXIgPSBhbGwudmFycyhmKVsxXSAjIG5hbWUgb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlCiAgIyBrIGluZGljZXMgdXNlZCB0byBzcGxpdCBkYXRhIGludG8gayBwYXJ0cwogIHNhbXBsZV9pZHhfayA9IHJlcChzYW1wbGUoMTprKSxyb3VuZChudW1PZlJlYyAvIGspICsgMSlbMTpudW1PZlJlY10KICAjIGsgbW9kZWxzIGZvciBrIHN1YnNldHMgb2YgZGF0YQogIGtfZml0cyA9IE1hcCggZnVuY3Rpb24oeCkgZ2xtKGYsIGRbc2FtcGxlX2lkeF9rICE9IHgsIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiksIDE6aykKICAjIFByZWRpY3RlZCAmIGFjdHVhbCBjbGFzc2VzIGZvciBlYWNoIGhvbGQtb3V0IHN1YnNldAogIHByZWRBY3R1YWxDbGFzcyA9IE1hcChmdW5jdGlvbih4KXsKICAgIHByZWRpY3RlZFByb2IgPSBwcmVkaWN0KGtfZml0c1tbeF1dLCBkW3NhbXBsZV9pZHhfayA9PSB4LF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikKICAgIHByZWRpY3RlZENsYXNzID0gaWZlbHNlKHByZWRpY3RlZFByb2IgPiBjdXRvZmYsIDEsIDApCiAgICByZXR1cm4oZGF0YS5mcmFtZSgicHJlZGljdGVkQ2xhc3MiID0gcHJlZGljdGVkQ2xhc3MsCiAgICAgICAgICAgICAgICAgICAgICAiYWN0dWFsQ2xhc3MiID0gZFtzYW1wbGVfaWR4X2sgPT0geCwgcmVwb25zZV92YXJdICkgKQogIH0sIDE6aykKICAjIEEgZGF0YSBmcmFtZSB3aXRoIGFsbCBwcmVkaWN0ZWQgJiBhY3R1YWwgY2xhc3NlcwogIG91dHB1dF9ERiA9IFJlZHVjZShmdW5jdGlvbih4LCB5KSByYmluZCh4LCB5KSwgcHJlZEFjdHVhbENsYXNzKQogIG91dHB1dF9ERiRwcmVkaWN0ZWRDbGFzcyA9IGZhY3RvcihvdXRwdXRfREYkcHJlZGljdGVkQ2xhc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKDAsMSksbGFiZWxzID0gYygiTm8iLCAiWWVzIikpCiAgcmV0dXJuKCB0YWJsZShvdXRwdXRfREYkcHJlZGljdGVkQ2xhc3MsIG91dHB1dF9ERiRhY3R1YWxDbGFzcykpCn0KCk1hcChmdW5jdGlvbihjdXRvZmYpIGtfZm9sZF9DVl9sb2dpdCh3aW5uZXIgfiBmaXJzdEJsb29kK2ZpcnN0VG93ZXIrZmlyc3RJbmhpYml0b3IrZmlyc3RCYXJvbitmaXJzdERyYWdvbitmaXJzdFJpZnRIZXJhbGQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2xbdHJhaW5faWR4LF0sIDEwLCBjdXRvZmYpLCBsaXN0KDAuOSwgMC44LCAwLjcsIDAuNiwgMC41LCAwLjQ1LCAwLjQsIDAuMywgMC4yLCAwLjEpKSAjIDAuNQpgYGAKCgo8YnI+Cjxicj4KKirjgJAg6Zqo5qmf5qOu5p6XKFJhbmRvbSBGb3Jlc3QpIOOAkSoqCgorIOippuiXieeUsee1hOWToeiHqui6q+mBiuaIsue2k+mpl+aJgOaJvuWHuuS5i+iuiuaVuOi3kXJhbmRvbUZvcmVzdAogICAgCiAgICArIOiuiuaVuO+8mmZpcnN0Qmxvb2QsIGZpcnN0VG93ZXIsIGZpcnN0SW5oaWJpdG9yLCBmaXJzdEJhcm9uLCBmaXJzdERyYWdvbiwgZmlyc3RSaWZ0SGVyYWxkLCBsb2xfdHJhaW4KICAgIAogICAgKyBudHJlZSA9IDUwMAoKKyDpgI/pgY5pbXBvcnRhbmNlKCnkvobmib7lh7rovIPph43opoHkuYvorormlbgKCiAgICArIOeZvOePvmZpcnN0UmlmdEhlcmFsZOiuiuaVuOmHjeimgeW6puacgOS9ju+8jOaVheWYl+ippuenu+mZpGZpcnN0UmlmdEhlcmFsZOiuiuaVuO+8jOWGjeasoemAsuihjOW7uuaooQogICAgCiAgICArIOmHjeaWsOW7uuaooeW+jO+8jOeZvOePvumAj+mBjmltcG90YW5jZSgp5omA5oyR5Ye655qE6K6K5pW45omA6KiI566X5Ye655qE57WQ5p6c5Lim5rKS5pyJ5q+U6LyD5aW9CiAgICAKKyDnorrlrprmqKHlnovlu7rnva4KYGBge3J9CiMg6Kmm6JeJ55Sx57WE5ZOh6Ieq6Lqr6YGK5oiy57aT6amX5omA5om+5Ye65LmL6K6K5pW46LeRcmFuZG9tRm9yZXN0CnNldC5zZWVkKDIwMTgpCmxvbF90cmVlIDwtIHJhbmRvbUZvcmVzdCh3aW5uZXIgfiBmaXJzdEJsb29kICsgZmlyc3RUb3dlciArIGZpcnN0SW5oaWJpdG9yICsgZmlyc3RCYXJvbiArIGZpcnN0RHJhZ29uICsgZmlyc3RSaWZ0SGVyYWxkLCBsb2xfdHJhaW4sIG50cmVlID0gNTAwKQpyZXN1bHRfdHJlZSA8LSBwcmVkaWN0KGxvbF90cmVlLG5ld2RhdGEgPSBsb2xfdGVzdCkKbG9sX3RyZWUKICAgICMgQ29uZnVzaW9uIG1hdHJpeDoKICAgICMgICAgICAgMSAgICAgMiBjbGFzcy5lcnJvcgogICAgIyAxIDE4MzQ1ICAxNzc3ICAwLjA4ODMxMTMwCiAgICAjIDIgIDE5NzMgMTc3ODkgIDAuMDk5ODM4MDcKCiMg6YCP6YGOaW1wb3J0YW5jZSgp5L6G5om+5Ye66LyD6YeN6KaB5LmL6K6K5pW4CmltcG9ydGFuY2UobG9sX3RyZWUpCiAgICAjICAgICAgICAgICAgICAgICBNZWFuRGVjcmVhc2VHaW5pCiAgICAjIGZpcnN0VG93ZXIgICAgICAgICAgICAxMDU5LjI0MDUwCiAgICAjIGZpcnN0SW5oaWJpdG9yICAgICAgICA5MjUzLjA2MTU1CiAgICAjIGZpcnN0QmFyb24gICAgICAgICAgICAyNDUxLjM4ODc4CiAgICAjIGZpcnN0RHJhZ29uICAgICAgICAgICAgNzQ1Ljg0NTQxCiAgICAjIGZpcnN0UmlmdEhlcmFsZCAgICAgICAgIDkxLjQ1MzI2ICjmnIDkvY4pCgojIOenu+mZpGZpcnN0UmlmdEhlcmFsZOiuiuaVuO+8jOWGjeasoemAsuihjOW7uuaooQpsb2xfdHJlZSA8LSByYW5kb21Gb3Jlc3Qod2lubmVyIH4gZmlyc3RUb3dlcitmaXJzdEluaGliaXRvcitmaXJzdEJhcm9uK2ZpcnN0RHJhZ29uK2ZpcnN0UmlmdEhlcmFsZCxsb2xfdHJhaW4sIG50cmVlPTUwMCkKcmVzdWx0X3RyZWUgPC0gcHJlZGljdChsb2xfdHJlZSxuZXdkYXRhID0gbG9sX3Rlc3QpCmxvbF90cmVlCiAgICAjIENvbmZ1c2lvbiBtYXRyaXg6CiAgICAjICAgICAgIDEgICAgIDIgY2xhc3MuZXJyb3IKICAgICMgMSAxODMyMiAgMTgwMCAgMC4wODk0NTQzMwogICAgIyAyICAxOTY2IDE3Nzk2ICAwLjA5OTQ4Mzg2CgojIOmgkOa4rChyYW5kb21Gb3Jlc3QgcHJlZGljdGlvbiko5Yqg5YWldG93ZWxfZ2FwLCBkcmFnb25fZ2FwLCBiYXJvbl9nYXAsIGluaGliaXRvcktpbGxzX2dhcCwgZ2FtZUR1cmF0aW9uKQpsb2xfdHJlZSA8LSByYW5kb21Gb3Jlc3Qod2lubmVyIH4gZmlyc3RCbG9vZCArIGZpcnN0VG93ZXIgKyBmaXJzdEluaGliaXRvciArIGZpcnN0QmFyb24gKyBmaXJzdERyYWdvbiArIGZpcnN0UmlmdEhlcmFsZCArIHRvd2VyX2dhcCArIGRyYWdvbl9nYXAgKyBiYXJvbl9nYXAgKyBpbmhpYml0b3JLaWxsc19nYXAgKyBnYW1lRHVyYXRpb24sbG9sX3RyYWluLCBudHJlZSA9IDUwMCkKcmVzdWx0X3RyZWUgPC0gcHJlZGljdChsb2xfdHJlZSxuZXdkYXRhID0gbG9sX3Rlc3QpCmxvbF90cmVlCiAgICAjIENvbmZ1c2lvbiBtYXRyaXg6CiAgICAjICAgICAgIDEgICAgIDIgY2xhc3MuZXJyb3IKICAgICMgMSAxOTY1NSAgIDQ2NyAgMC4wMjMyMDg0MwogICAgIyAyICAgNDg4IDE5Mjc0ICAwLjAyNDY5Mzg2CnN1bW1hcnkobG9sX3RyZWUpCmBgYAoKPGJyPgo8YnI+Cioq44CQIOmaqOapn+ajruael1JPQyhST0MgY3VydmUgb2YgUmFuZG9tIEZvcmVzdCkg44CRKioKYGBge3J9CiMg57mq6KO96Zqo5qmf5qOu5p6X55qEUk9D5puy57ea77yM57mq6KO9Uk9DIGN1cnZl5LmL5ZyW5b2i77yM5Lim566X5Ye6QVVDCnJmLnByZWQgPC0gcHJlZGljdChsb2xfdHJlZSwgbG9sX3Rlc3QsIHR5cGUgPSAicHJvYiIpCnJmLnJvYyA8LSBwcmVkaWN0aW9uKHJmLnByZWRbLDJdLCBsb2xfdGVzdCR3aW5uZXIpCnJmLmF1YyA8LSBwZXJmb3JtYW5jZShyZi5yb2MsICd0cHInLCAnZnByJykKIyByZi5hdWMKcGxvdChyZi5hdWMpCmFibGluZSgwLCAxKQojIHRleHQoMC41LCAwLjUsIGFzLmNoYXJhY3RlcihyZi5hdWNAeS52YWx1ZXNbWzFdXSkpICAKYGBgCgo8YnI+Cjxicj4KKirjgJAg6KiI566X5bmz5Z2H5bmz5pa56Kqk5beuTVNFKE1lYW4gU3F1YXJlIEVycm9yKSDjgJEqKgpgYGB7cn0KIyBtZWFuKChyZXN1bHQtbG9sX3Rlc3Qkd2lubmVyKV4yKQpkaW0obG9sX3Rlc3QpICAgICAgICMgIDk5NzIgICAgNzEKZGltKGxvbF90cmFpbikgICAgICAjIDM5ODg0ICAgIDcxCnN1bW1hcnkobW9kZWwxKQpgYGA=