# references 
# xda package : https://www.r-bloggers.com/introducing-xda-r-package-for-exploratory-data-analysis/
# eda         : http://r4ds.had.co.nz/exploratory-data-analysis.html
library(data.table)
library(dplyr)
library(magrittr)
library(tidyr)
library(stringi)
library(stringr)
library(ggplot2)
library(caret)
library(doMC)
train <- fread("/Users/CA/Downloads/titanic/train.csv", na.strings = "")
test <- fread("/Users/CA/Downloads/titanic/test.csv", na.strings = "")
gender <- fread("/Users/CA/Downloads/titanic/gender_submission.csv", na.strings = "")
head(train)
library(xda)
numSummary(train)
charSummary(train)
# missing value 는 Cabin / Embarked 와 Age 에서 발생하고 있고, 
# Age 는 연령 추정으로, Embarked 는 별도 모름으로, Cabin 은 없음으로 처리하면 좋겠다. 
train %<>% replace_na(list(Embarked = "unknown"))
# 당장 factor type 으로 변환은 Survived, Sex, Embarked, Pclass 이고, 
# Ticket 과 Cabin 은 추가로 살펴봐야 한다. 
train %<>% mutate_each_(funs(factor(.)), c("Sex", "Embarked", "Pclass", "Survived"))
charSummary(train)
# ticket 은 문자 + 숫자로 이뤄져 있으며, 숫자부분은 중복데이터가 발견되고 있다. 
train %<>% 
  separate(col = Ticket, into = c("Ticket.prefix", "Ticket.no"), sep = " ", fill = "left", extra = "drop") %<>%
  replace_na(list(Ticket.prefix = "none"))
train <- transform(train, Ticket.prefix = stri_replace_all_fixed(Ticket.prefix, ".", ""))
charSummary(train)
# ticket no 가 동일한 것들을 디벼보자 
dup_ticket_passengers <- train %>% group_by(Ticket.no) %>% filter(n() > 1)
# ticket no 가 동일한 승객들은, 일행임을 알 수 있다. 이는 추후 연령 추정이나, 최종 생존 예측때 사용될 수 있으니, 
# ticket no 자체를 하나의 factor 로 등록하자, 1개이상의 중복 티켓번호는 각각을 티켓번호로 level 부여, 
# 중복되지 않은 놈들은 그냥 다 퉁쳐서 하나로 level 부여 
group <- subset(dup_ticket_passengers, select = c(PassengerId, Ticket.no))
colnames(group) <- c("PassengerId", "group_no")
group$Ticket.no <- NULL
train <- merge(train, group, by = "PassengerId", all.x = T)
train %<>% replace_na(list(group_no = "none"))
train$group_no <- factor(train$group_no)
train$Ticket.no <- NULL
# cabin 을 디벼보자
library(stringi)
train %<>% replace_na(list(Cabin = "X"))
train <- transform(train, load_count = stri_count(Cabin, regex="\\S+"), Cabin.prefix = stri_sub(Cabin, 0, 1))
train %<>% replace_na(list(load_count = 0)) %>% mutate(Cabin.prefix = ifelse(Cabin.prefix %in% c("G", "T"), "ETC", Cabin.prefix))
table(train$Cabin.prefix)

  1   2   3   4   5   6   9 ETC 
 15  47  59  33  32  13 687   5 
train$Cabin <-NULL
# family_size 를 생성하자
train <- transform(train, family_size = SibSp + Parch)
table(train$family_size)

  0   1   2   3   4   5   6   7  10 
537 161 102  29  15  22  12   6   7 
# fare 를 디벼보자
# numSummary 를 보면 outlier 갯수가 많지만.... 일단 버리지 말고, categorizing 하자. 
plot(density(train$Fare))

plot(density(log(train$Fare)))

train <- transform(train, log_fare = ifelse(round(log(Fare),0) <= 1, 1, round(log(Fare),0)))
train$log_fare <- factor(train$log_fare)
# 1. missing value 처리 : Cabin / Embarked -> unknown
# 2. Cabin  => Cabin.prefix , Load.count
# 3. Ticket => Ticket.prefix , Group.no
# 4. Fare => Log Transformation => log_fare ( 1 ~ 6 )
# 5. Factor type 으로 변경 : Sex , Embarked, PClass, Survived, Cabin.prefix, Ticket.Prefix, Group.no, log_fare
# 6. Family Size 추가 = SibSp + Parch
#
# 7. Name 디비기
#
# 8. Age 추정
# name 을 디벼보자, 이름, 호칭, 성 으로 구성되는 듯 하다. 
name_seperator <- Vectorize(function(name) {
  splitted <- strsplit(name, ",")[[1]][2]
  return(str_trim(str_split(splitted, "\\.")[[1]][1]))
})
train %<>% mutate(Name.call = name_seperator(Name))
table(train$Name.call)

        Capt          Col          Don           Dr     Jonkheer         Lady        Major       Master         Miss         Mlle          Mme 
           1            2            1            7            1            1            2           40          182            2            1 
          Mr          Mrs           Ms          Rev          Sir the Countess 
         517          125            1            6            1            1 
# 호칭 레벨이 너무 많아지는 것을 막기위해, 10개 이하는 etc 로 구분한다. 
name_seperator <- Vectorize(function(name) {
  splitted <- strsplit(name, ",")[[1]][2]
  call <- str_trim(str_split(splitted, "\\.")[[1]][1])
  
  
  return(ifelse(call %in% c('Master', 'Miss', 'Mr', 'Mrs'), call, 'Etc'))
})
train %<>% mutate(Name.call = name_seperator(Name))
table(train$Name.call)

   Etc Master   Miss     Mr    Mrs 
    27     40    182    517    125 
train$Name <- NULL
par(mfrow = c(2,2))
ggplot(train,aes(x=log_fare,group=Survived,fill=Survived))+geom_bar(position="identity",alpha=0.5)+theme_bw()

ggplot(train,aes(x=Cabin.prefix,group=Survived,fill=Survived))+geom_bar(position="identity",alpha=0.5)+theme_bw()

ggplot(train,aes(x=Ticket.prefix,group=Survived,fill=Survived))+geom_bar(position="identity",alpha=0.5)+theme_bw()

ggplot(train,aes(x=Name.call,group=Survived,fill=Survived))+geom_bar(position="identity",alpha=0.5)+theme_bw()

numSummary(train)
charSummary(train)
# Age 추정 
# Age 가 있는 놈들로 모델링해서, missing 인 놈들을 채운다. 
train <- transform(train, Age = ifelse(is.na(Age), -1, ifelse(round(Age / 10,0) >= 6, 6, round(Age / 10,0))))
has_age <- subset(train, Age > 0)
none_age <- setdiff(train, has_age)
has_age$PassengerId <- NULL
has_age.train <- sample_frac(has_age, 0.5)
has_age.test <- setdiff(has_age, has_age.train)
age.fit <- glm(Age ~ . , data = has_age.train)
age.fit.steps <- step(age.fit, direction = "backward", trace = 0)
summary(age.fit.steps)

Call:
glm(formula = Age ~ Pclass + Sex + SibSp + Name.call, data = has_age.train)

Deviance Residuals: 
   Min      1Q  Median      3Q     Max  
-2.316  -0.886  -0.091   0.599   3.114  

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)       3.0096     0.6082    4.95  1.2e-06 ***
Pclass2          -1.0289     0.1574   -6.54  2.4e-10 ***
Pclass3          -1.2337     0.1415   -8.72  < 2e-16 ***
Sexmale           1.9891     0.7264    2.74   0.0065 ** 
SibSp            -0.2261     0.0931   -2.43   0.0157 *  
Name.callMaster  -2.1945     0.6941   -3.16   0.0017 ** 
Name.callMiss     0.4203     0.6258    0.67   0.5023    
Name.callMr      -0.8788     0.4109   -2.14   0.0332 *  
Name.callMrs      1.5325     0.6233    2.46   0.0145 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for gaussian family taken to be 1.1)

    Null deviance: 517.89  on 334  degrees of freedom
Residual deviance: 359.07  on 326  degrees of freedom
AIC: 993.9

Number of Fisher Scoring iterations: 2
age.fit <- glm(
  formula = Age ~ Survived + Pclass + Sex + SibSp + Name.call, 
  data = has_age.train, 
  family = "gaussian") 
has_age.test$pred <- predict(age.fit, has_age.test, type = "response")
plot(has_age.test$Age, has_age.test$pred)

has_age <- subset(train, Age != -1)
none_age <- setdiff(train, has_age)

has_age$PassengerId <- NULL
has_age$Ticket.prefix <- NULL
has_age$group_no <- NULL
has_age$Cabin.prefix <- NULL

has_age.train <- sample_frac(has_age, 0.3)
has_age.test <- setdiff(has_age, has_age.train)

has_age.train$Age <- factor(has_age.train$Age)


#registerDoMC(cores = 4)

fitControl <- trainControl(## 10-fold CV
                           method = "repeatedcv",
                           number = 10,
                           ## repeated ten times
                           repeats = 10)

gbmGrid <-  expand.grid(interaction.depth = c(1,3,5,9), 
                        n.trees = (1:10)*10, 
                        shrinkage = 0.05,
                        n.minobsinnode = 10)

set.seed(825)
age.fit.gbm <- train(Age ~ ., data = subset(has_age.train, select = -c(Survived)), 
                 method = "gbm", 
                 metric = "Accuracy",
                 trControl = fitControl,
                 tuneGrid = gbmGrid,
                 verbose = FALSE)
# The final values used for the model were n.trees = 20, interaction.depth = 3, shrinkage = 0.05 and n.minobsinnode = 10.
plot(age.fit.gbm)
plot(age.fit.gbm, metric = "Accuracy")

has_age.test$pred <- predict(age.fit.gbm, has_age.test)
table(has_age.test$Age, has_age.test$pred)
has_age <- subset(train, Age > 0)
none_age <- setdiff(train, has_age)
has_age$PassengerId <- NULL
has_age$Ticket.prefix <- NULL
has_age$group_no <- NULL
has_age$Cabin.prefix <- NULL
has_age.train <- sample_frac(has_age, 0.5)
has_age.test <- setdiff(has_age, has_age.train)
has_age.train$Age <- factor(has_age.train$Age)
library(C50)
fit.c50 <- C5.0( x = subset(has_age.train, select = -c(Survived, Age)),
                 y = has_age.train$Age, 
                 # an integer specifying the number of boosting iterations. A value of one indicates that a single model is used.
                 # C5.0 uses adaptive boosting 
                 trials = 10,  
                 control = C5.0Control(earlyStopping = F))
print(fit.c50)

Call:
C5.0.default(x = subset(has_age.train, select = -c(Survived, Age)), y = has_age.train$Age, trials = 10, control = C5.0Control(earlyStopping = F))

Classification Tree
Number of samples: 335 
Number of predictors: 10 

Number of boosting iterations: 10 
Average tree size: 26.9 

Non-standard options: attempt to group attributes, early stopping for boosting
has_age.test$pred <- predict(fit.c50, has_age.test)
plot(has_age.test$Age, has_age.test$pred)

table(has_age.test$Age, has_age.test$pred)
   
     1  2  3  4  5  6
  1  5  6  1  0  0  0
  2  6 36 14  8  9  1
  3  0 22 17  7  5  5
  4  0 14 13  8 13  6
  5  0  3  7  4  9  5
  6  0  3  1  6  3  2
# Random Search
control <- trainControl(method="repeatedcv", number=10, repeats=3, search="random")
set.seed(1234)
mtry <- sqrt(ncol(has_age.train))
rf_random <- train(Age ~., data=has_age.train, method="rf", metric="Kappa", tuneLength=15, trControl=control)
Loading required package: randomForest
randomForest 4.6-12
Type rfNews() to see new features/changes/bug fixes.

Attaching package: 'randomForest'

The following object is masked from 'package:ggplot2':

    margin

The following object is masked from 'package:dplyr':

    combine
print(rf_random)
Random Forest 

335 samples
 11 predictor
  6 classes: '1', '2', '3', '4', '5', '6' 

No pre-processing
Resampling: Cross-Validated (10 fold, repeated 3 times) 
Summary of sample sizes: 302, 302, 300, 301, 301, 302, ... 
Resampling results across tuning parameters:

  mtry  Accuracy  Kappa
   1    0.376     0.114
   3    0.431     0.229
   5    0.411     0.214
   6    0.413     0.215
   7    0.404     0.206
   8    0.396     0.197
   9    0.407     0.212
  15    0.414     0.222
  16    0.412     0.220
  18    0.408     0.217
  20    0.402     0.209
  21    0.399     0.207

Kappa was used to select the optimal model using  the largest value.
The final value used for the model was mtry = 3.
plot(rf_random)

non_age <- subset(train, Age == -1)
non_age$Age <- predict(age.fit.gbm, non_age)
train$Age <- factor(train$Age)
predicted_ages <- subset(non_age, select = c(PassengerId, Age))
train %<>% anti_join(predicted_ages, by = "PassengerId") %>% bind_rows(predicted_ages)
Unequal factor levels: coercing to character
train <- na.omit(train)
ggplot(train,aes(x=Age,group=Survived,fill=Survived))+geom_bar(position="identity",alpha=0.5)+theme_bw()

registerDoMC(cores = 4)

fitControl <- trainControl(## 10-fold CV
                           method = "repeatedcv",
                           number = 10,
                           ## repeated ten times
                           repeats = 10)

gbmGrid <-  expand.grid(interaction.depth = c(1,3,5,7,9), 
                        n.trees = (1:10)*50, 
                        shrinkage = 0.01,
                        n.minobsinnode = 5)

set.seed(825)
Survived.fit.gbm <- train(Survived ~ ., data = train, 
                 method = "gbm", 
                 metric = "Accuracy",
                 trControl = fitControl,
                 tuneGrid = gbmGrid,
                 verbose = FALSE)
# The final values used for the model were n.trees = 450, interaction.depth = 3, shrinkage = 0.01 and n.minobsinnode = 5.
plot(Survived.fit.gbm)

library(data.table)
library(dplyr)
library(magrittr)
library(tidyr)
library(stringi)
library(stringr)
library(xda)
library(caret)
library(doMC)
# test 데이터에 대해, 위에 데이터 가공을 하는 함수를 만들자.
name_seperator <- Vectorize(function(name) {
  splitted <- strsplit(name, ",")[[1]][2]
  call <- str_trim(str_split(splitted, "\\.")[[1]][1])
  
  return(ifelse(call %in% c('Master', 'Miss', 'Mr', 'Mrs'), call, 'Etc'))
})
tt <- function(test) {
  test %<>% 
    replace_na(list(Embarked = "unknown")) %>%
    mutate_each_(funs(factor(.)), c("Sex", "Embarked", "Pclass")) %>%
    separate(col = Ticket, into = c("Ticket.prefix", "Ticket.no"), sep = " ", fill = "left", extra = "drop") %>%
    replace_na(list(Ticket.prefix = "none", Fare = 0)) %>%
    mutate(Ticket.prefix = stri_replace_all_fixed(Ticket.prefix, ".", "")) %>% 
    mutate(Ticket.prefix = ifelse(Ticket.prefix %in% c("CA", "PC"), Ticket.prefix ,"ETC"))
    
  dup_ticket_passengers <- test %>% group_by(Ticket.no) %>% filter(n() > 1)
  group <- subset(dup_ticket_passengers, select = c(PassengerId, Ticket.no))
  colnames(group) <- c("PassengerId", "group_no")
  group$Ticket.no <- NULL
  
  test %<>% 
    merge(group, by = "PassengerId", all.x = T) %>%
    replace_na(list(group_no = "none")) %>% 
    mutate_each_(funs(factor(.)), c("group_no")) -> test
  
  test %<>% 
    replace_na(list(Cabin = "X")) %>% 
    mutate(load_count = stri_count(Cabin, regex="\\S+"), Cabin.prefix = stri_sub(Cabin, 0, 1)) %>%
    replace_na(list(load_count = 0)) %>% 
    mutate(Cabin.prefix = ifelse(Cabin.prefix %in% c("G", "T"), "ETC", Cabin.prefix))
  
  test <- transform(test, family_size = SibSp + Parch)
  
  test <- transform(test, log_fare = ifelse(round(log(Fare),0) <= 1, 1, round(log(Fare),0)))
  test$log_fare <- factor(test$log_fare)
  
  test %<>% 
    mutate(Name.call = name_seperator(Name)) %>%
    mutate_each_(funs(factor(.)), c("Name.call"))
  
  test <- transform(test, Age = ifelse(is.na(Age), -1, ifelse(round(Age / 10,0) >= 6, 6, round(Age / 10,0))))
  test %<>% select(-c(Name, Cabin, Ticket.no, Fare))
  
  return(test)
}
train_age_preiction_model <- function(train) {
  train <- subset(train, Age != -1)
  train$Age <- factor(train$Age)
  
  train %<>% select(-c(PassengerId, Ticket.prefix, group_no, Cabin.prefix, Survived))
  fitControl <- trainControl(method = "repeatedcv", number = 10, repeats = 10)
  
  gbmGrid <-  expand.grid(interaction.depth = c(1,3,5,9), 
                          n.trees = (1:10)*10, 
                          shrinkage = 0.05,
                          n.minobsinnode = 5)
  
  set.seed(1234)
  age.fit.gbm <- train(Age ~ ., data = train, 
                   method = "gbm", 
                   metric = "Accuracy",
                   trControl = fitControl,
                   tuneGrid = gbmGrid,
                   verbose = T)
  
  return(age.fit.gbm)
}
train_survival_prediction_model <- function(train) {
  train <- subset(train, Age != -1)
  train$Age <- factor(train$Age)
  train$Survived <- factor(train$Survived)
  
  train %<>% select(-c(group_no))
  
  fitControl <- trainControl(method = "repeatedcv",
                             number = 10,
                             repeats = 10)
  
  gbmGrid <-  expand.grid(interaction.depth = c(1,3,5), 
                          n.trees = (1:10)*50, 
                          shrinkage = 0.05,
                          n.minobsinnode = 5)
  
  set.seed(825)
  Survived.fit.gbm <- train(Survived ~ ., data = train, 
                   method = "gbm", 
                   metric = "Accuracy",
                   trControl = fitControl,
                   tuneGrid = gbmGrid,
                   verbose = T)
  
  return(Survived.fit.gbm)
}
predict_age <- function(test, age.fit) {
      
  non_age <- subset(test, Age == -1)
  non_age$Age <- predict(age.fit, non_age)
  
  test$Age <- factor(test$Age)
  test %<>% anti_join(non_age, by = "PassengerId") %>% bind_rows(non_age)
  test$Age <- factor(test$Age)
  return(test)
}
train <- fread("/Users/CA/Downloads/titanic/train.csv", na.strings = "")
test <- fread("/Users/CA/Downloads/titanic/test.csv", na.strings = "")

refined_train_data <- tt(train)
refined_test_data <- tt(test)

age.fit <- train_age_preiction_model(refined_train_data)
survival.fit <- train_survival_prediction_model(refined_train_data)

refined_test_data          <- predict_age(refined_test_data, age.fit)
refined_test_data$pred     <- predict(survival.fit, refined_test_data)
refined_test_data <- merge(refined_test_data, gender, by = "PassengerId")
column names 'Survived.x', 'Prediction.x', 'Survived.y', 'Prediction.y' are duplicated in the result
confusionMatrix(gender$Survived, refined_test_data$pred)
Confusion Matrix and Statistics

          Reference
Prediction   0   1
         0 248  18
         1   5 147
                                        
               Accuracy : 0.945         
                 95% CI : (0.919, 0.965)
    No Information Rate : 0.605         
    P-Value [Acc > NIR] : <2e-16        
                                        
                  Kappa : 0.883         
 Mcnemar's Test P-Value : 0.0123        
                                        
            Sensitivity : 0.980         
            Specificity : 0.891         
         Pos Pred Value : 0.932         
         Neg Pred Value : 0.967         
             Prevalence : 0.605         
         Detection Rate : 0.593         
   Detection Prevalence : 0.636         
      Balanced Accuracy : 0.936         
                                        
       'Positive' Class : 0             
                                        
LS0tCnRpdGxlOiAi7YOA7J207YOA64uJIOyDneyhtOyekCDsmIjsuKEgKCDsnpHshLEg7KSRICkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQojIHJlZmVyZW5jZXMgCiMgeGRhIHBhY2thZ2UgOiBodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS9pbnRyb2R1Y2luZy14ZGEtci1wYWNrYWdlLWZvci1leHBsb3JhdG9yeS1kYXRhLWFuYWx5c2lzLwojIGVkYSAgICAgICAgIDogaHR0cDovL3I0ZHMuaGFkLmNvLm56L2V4cGxvcmF0b3J5LWRhdGEtYW5hbHlzaXMuaHRtbApgYGAKCmBgYHtyfQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5naSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZG9NQykKCnRyYWluIDwtIGZyZWFkKCIvVXNlcnMvQ0EvRG93bmxvYWRzL3RpdGFuaWMvdHJhaW4uY3N2IiwgbmEuc3RyaW5ncyA9ICIiKQp0ZXN0IDwtIGZyZWFkKCIvVXNlcnMvQ0EvRG93bmxvYWRzL3RpdGFuaWMvdGVzdC5jc3YiLCBuYS5zdHJpbmdzID0gIiIpCmdlbmRlciA8LSBmcmVhZCgiL1VzZXJzL0NBL0Rvd25sb2Fkcy90aXRhbmljL2dlbmRlcl9zdWJtaXNzaW9uLmNzdiIsIG5hLnN0cmluZ3MgPSAiIikKCmhlYWQodHJhaW4pCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KHhkYSkKbnVtU3VtbWFyeSh0cmFpbikKYGBgCgpgYGB7cn0KY2hhclN1bW1hcnkodHJhaW4pCmBgYAoKYGBge3J9CiMgbWlzc2luZyB2YWx1ZSDripQgQ2FiaW4gLyBFbWJhcmtlZCDsmYAgQWdlIOyXkOyEnCDrsJzsg53tlZjqs6Ag7J6I6rOgLCAKIyBBZ2Ug64qUIOyXsOuguSDstpTsoJXsnLzroZwsIEVtYmFya2VkIOuKlCDrs4Trj4Qg66qo66aE7Jy866GcLCBDYWJpbiDsnYAg7JeG7J2M7Jy866GcIOyymOumrO2VmOuptCDsoovqsqDri6QuIAp0cmFpbiAlPD4lIHJlcGxhY2VfbmEobGlzdChFbWJhcmtlZCA9ICJ1bmtub3duIikpCmBgYAoKCmBgYHtyfQojIOuLueyepSBmYWN0b3IgdHlwZSDsnLzroZwg67OA7ZmY7J2AIFN1cnZpdmVkLCBTZXgsIEVtYmFya2VkLCBQY2xhc3Mg7J206rOgLCAKIyBUaWNrZXQg6rO8IENhYmluIOydgCDstpTqsIDroZwg7IK07Y6067SQ7JW8IO2VnOuLpC4gCnRyYWluICU8PiUgbXV0YXRlX2VhY2hfKGZ1bnMoZmFjdG9yKC4pKSwgYygiU2V4IiwgIkVtYmFya2VkIiwgIlBjbGFzcyIsICJTdXJ2aXZlZCIpKQpjaGFyU3VtbWFyeSh0cmFpbikKYGBgCgpgYGB7cn0KIyB0aWNrZXQg7J2AIOusuOyekCArIOyIq+yekOuhnCDsnbTrpITsoLgg7J6I7Jy866mwLCDsiKvsnpDrtoDrtoTsnYAg7KSR67O1642w7J207YSw6rCAIOuwnOqyrOuQmOqzoCDsnojri6QuIAp0cmFpbiAlPD4lIAogIHNlcGFyYXRlKGNvbCA9IFRpY2tldCwgaW50byA9IGMoIlRpY2tldC5wcmVmaXgiLCAiVGlja2V0Lm5vIiksIHNlcCA9ICIgIiwgZmlsbCA9ICJsZWZ0IiwgZXh0cmEgPSAiZHJvcCIpICU8PiUKICByZXBsYWNlX25hKGxpc3QoVGlja2V0LnByZWZpeCA9ICJub25lIikpCnRyYWluIDwtIHRyYW5zZm9ybSh0cmFpbiwgVGlja2V0LnByZWZpeCA9IHN0cmlfcmVwbGFjZV9hbGxfZml4ZWQoVGlja2V0LnByZWZpeCwgIi4iLCAiIikpCmNoYXJTdW1tYXJ5KHRyYWluKQpgYGAKCmBgYHtyfQojIHRpY2tldCBubyDqsIAg64+Z7J287ZWcIOqyg+uTpOydhCDrlJTrsrzrs7TsnpAgCmR1cF90aWNrZXRfcGFzc2VuZ2VycyA8LSB0cmFpbiAlPiUgZ3JvdXBfYnkoVGlja2V0Lm5vKSAlPiUgZmlsdGVyKG4oKSA+IDEpCiMgdGlja2V0IG5vIOqwgCDrj5nsnbztlZwg7Iq56rCd65Ok7J2ALCDsnbztlonsnoTsnYQg7JWMIOyImCDsnojri6QuIOydtOuKlCDstpTtm4Qg7Jew66C5IOy2lOygleydtOuCmCwg7LWc7KKFIOyDneyhtCDsmIjsuKHrlYwg7IKs7Jqp65CgIOyImCDsnojsnLzri4gsIAojIHRpY2tldCBubyDsnpDssrTrpbwg7ZWY64KY7J2YIGZhY3RvciDroZwg65Ox66Gd7ZWY7J6QLCAx6rCc7J207IOB7J2YIOykkeuztSDti7DsvJPrsojtmLjripQg6rCB6rCB7J2EIO2LsOy8k+uyiO2YuOuhnCBsZXZlbCDrtoDsl6wsIAojIOykkeuzteuQmOyngCDslYrsnYAg64aI65Ok7J2AIOq3uOuDpSDri6Qg7YmB7LOQ7IScIO2VmOuCmOuhnCBsZXZlbCDrtoDsl6wgCmdyb3VwIDwtIHN1YnNldChkdXBfdGlja2V0X3Bhc3NlbmdlcnMsIHNlbGVjdCA9IGMoUGFzc2VuZ2VySWQsIFRpY2tldC5ubykpCmNvbG5hbWVzKGdyb3VwKSA8LSBjKCJQYXNzZW5nZXJJZCIsICJncm91cF9ubyIpCmdyb3VwJFRpY2tldC5ubyA8LSBOVUxMCgp0cmFpbiA8LSBtZXJnZSh0cmFpbiwgZ3JvdXAsIGJ5ID0gIlBhc3NlbmdlcklkIiwgYWxsLnggPSBUKQp0cmFpbiAlPD4lIHJlcGxhY2VfbmEobGlzdChncm91cF9ubyA9ICJub25lIikpCnRyYWluJGdyb3VwX25vIDwtIGZhY3Rvcih0cmFpbiRncm91cF9ubykKdHJhaW4kVGlja2V0Lm5vIDwtIE5VTEwKYGBgCgpgYGB7cn0KIyBjYWJpbiDsnYQg65SU67K867O07J6QCmxpYnJhcnkoc3RyaW5naSkKdHJhaW4gJTw+JSByZXBsYWNlX25hKGxpc3QoQ2FiaW4gPSAiWCIpKQp0cmFpbiA8LSB0cmFuc2Zvcm0odHJhaW4sIGxvYWRfY291bnQgPSBzdHJpX2NvdW50KENhYmluLCByZWdleD0iXFxTKyIpLCBDYWJpbi5wcmVmaXggPSBzdHJpX3N1YihDYWJpbiwgMCwgMSkpCnRyYWluICU8PiUgcmVwbGFjZV9uYShsaXN0KGxvYWRfY291bnQgPSAwKSkgJT4lIG11dGF0ZShDYWJpbi5wcmVmaXggPSBpZmVsc2UoQ2FiaW4ucHJlZml4ICVpbiUgYygiRyIsICJUIiksICJFVEMiLCBDYWJpbi5wcmVmaXgpKQoKCnRhYmxlKHRyYWluJENhYmluLnByZWZpeCkKdHJhaW4kQ2FiaW4gPC1OVUxMCmBgYAoKYGBge3J9CiMgZmFtaWx5X3NpemUg66W8IOyDneyEse2VmOyekAp0cmFpbiA8LSB0cmFuc2Zvcm0odHJhaW4sIGZhbWlseV9zaXplID0gU2liU3AgKyBQYXJjaCkKdGFibGUodHJhaW4kZmFtaWx5X3NpemUpCmBgYAoKYGBge3J9CiMgZmFyZSDrpbwg65SU67K867O07J6QCiMgbnVtU3VtbWFyeSDrpbwg67O066m0IG91dGxpZXIg6rCv7IiY6rCAIOunjuyngOunjC4uLi4g7J2864uoIOuyhOumrOyngCDrp5Dqs6AsIGNhdGVnb3JpemluZyDtlZjsnpAuIAoKCnBsb3QoZGVuc2l0eSh0cmFpbiRGYXJlKSkKcGxvdChkZW5zaXR5KGxvZyh0cmFpbiRGYXJlKSkpCgp0cmFpbiA8LSB0cmFuc2Zvcm0odHJhaW4sIGxvZ19mYXJlID0gaWZlbHNlKHJvdW5kKGxvZyhGYXJlKSwwKSA8PSAxLCAxLCByb3VuZChsb2coRmFyZSksMCkpKQp0cmFpbiRsb2dfZmFyZSA8LSBmYWN0b3IodHJhaW4kbG9nX2ZhcmUpCmBgYAoKYGBge3J9CiMgMS4gbWlzc2luZyB2YWx1ZSDsspjrpqwgOiBDYWJpbiAvIEVtYmFya2VkIC0+IHVua25vd24KIyAyLiBDYWJpbiAgPT4gQ2FiaW4ucHJlZml4ICwgTG9hZC5jb3VudAojIDMuIFRpY2tldCA9PiBUaWNrZXQucHJlZml4ICwgR3JvdXAubm8KIyA0LiBGYXJlID0+IExvZyBUcmFuc2Zvcm1hdGlvbiA9PiBsb2dfZmFyZSAoIDEgfiA2ICkKIyA1LiBGYWN0b3IgdHlwZSDsnLzroZwg67OA6rK9IDogU2V4ICwgRW1iYXJrZWQsIFBDbGFzcywgU3Vydml2ZWQsIENhYmluLnByZWZpeCwgVGlja2V0LlByZWZpeCwgR3JvdXAubm8sIGxvZ19mYXJlCiMgNi4gRmFtaWx5IFNpemUg7LaU6rCAID0gU2liU3AgKyBQYXJjaAojCiMgNy4gTmFtZSDrlJTruYTquLAKIwojIDguIEFnZSDstpTsoJUKYGBgCgpgYGB7cn0KIyBuYW1lIOydhCDrlJTrsrzrs7TsnpAsIOydtOumhCwg7Zi47LmtLCDshLEg7Jy866GcIOq1rOyEseuQmOuKlCDrk68g7ZWY64ukLiAKbmFtZV9zZXBlcmF0b3IgPC0gVmVjdG9yaXplKGZ1bmN0aW9uKG5hbWUpIHsKICBzcGxpdHRlZCA8LSBzdHJzcGxpdChuYW1lLCAiLCIpW1sxXV1bMl0KICByZXR1cm4oc3RyX3RyaW0oc3RyX3NwbGl0KHNwbGl0dGVkLCAiXFwuIilbWzFdXVsxXSkpCn0pCnRyYWluICU8PiUgbXV0YXRlKE5hbWUuY2FsbCA9IG5hbWVfc2VwZXJhdG9yKE5hbWUpKQp0YWJsZSh0cmFpbiROYW1lLmNhbGwpCmBgYAoKYGBge3J9CiMg7Zi47LmtIOugiOuyqOydtCDrhIjrrLQg66eO7JWE7KeA64qUIOqyg+ydhCDrp4nquLDsnITtlbQsIDEw6rCcIOydtO2VmOuKlCBldGMg66GcIOq1rOu2hO2VnOuLpC4gCm5hbWVfc2VwZXJhdG9yIDwtIFZlY3Rvcml6ZShmdW5jdGlvbihuYW1lKSB7CiAgc3BsaXR0ZWQgPC0gc3Ryc3BsaXQobmFtZSwgIiwiKVtbMV1dWzJdCiAgY2FsbCA8LSBzdHJfdHJpbShzdHJfc3BsaXQoc3BsaXR0ZWQsICJcXC4iKVtbMV1dWzFdKQogIAogIAogIHJldHVybihpZmVsc2UoY2FsbCAlaW4lIGMoJ01hc3RlcicsICdNaXNzJywgJ01yJywgJ01ycycpLCBjYWxsLCAnRXRjJykpCn0pCnRyYWluICU8PiUgbXV0YXRlKE5hbWUuY2FsbCA9IG5hbWVfc2VwZXJhdG9yKE5hbWUpKQp0YWJsZSh0cmFpbiROYW1lLmNhbGwpCgp0cmFpbiROYW1lIDwtIE5VTEwKYGBgCgpgYGB7cn0KcGFyKG1mcm93ID0gYygyLDIpKQoKZ2dwbG90KHRyYWluLGFlcyh4PWxvZ19mYXJlLGdyb3VwPVN1cnZpdmVkLGZpbGw9U3Vydml2ZWQpKStnZW9tX2Jhcihwb3NpdGlvbj0iaWRlbnRpdHkiLGFscGhhPTAuNSkrdGhlbWVfYncoKQpnZ3Bsb3QodHJhaW4sYWVzKHg9Q2FiaW4ucHJlZml4LGdyb3VwPVN1cnZpdmVkLGZpbGw9U3Vydml2ZWQpKStnZW9tX2Jhcihwb3NpdGlvbj0iaWRlbnRpdHkiLGFscGhhPTAuNSkrdGhlbWVfYncoKQpnZ3Bsb3QodHJhaW4sYWVzKHg9VGlja2V0LnByZWZpeCxncm91cD1TdXJ2aXZlZCxmaWxsPVN1cnZpdmVkKSkrZ2VvbV9iYXIocG9zaXRpb249ImlkZW50aXR5IixhbHBoYT0wLjUpK3RoZW1lX2J3KCkKZ2dwbG90KHRyYWluLGFlcyh4PU5hbWUuY2FsbCxncm91cD1TdXJ2aXZlZCxmaWxsPVN1cnZpdmVkKSkrZ2VvbV9iYXIocG9zaXRpb249ImlkZW50aXR5IixhbHBoYT0wLjUpK3RoZW1lX2J3KCkKYGBgCgpgYGB7cn0KbnVtU3VtbWFyeSh0cmFpbikKY2hhclN1bW1hcnkodHJhaW4pCmBgYAoKYGBge3J9CiMgQWdlIOy2lOyglSAKIyBBZ2Ug6rCAIOyeiOuKlCDrhojrk6TroZwg66qo642466eB7ZW07IScLCBtaXNzaW5nIOyduCDrhojrk6TsnYQg7LGE7Jq064ukLiAKdHJhaW4gPC0gdHJhbnNmb3JtKHRyYWluLCBBZ2UgPSBpZmVsc2UoaXMubmEoQWdlKSwgLTEsIGlmZWxzZShyb3VuZChBZ2UgLyAxMCwwKSA+PSA2LCA2LCByb3VuZChBZ2UgLyAxMCwwKSkpKQpgYGAKCmBgYHtyfQpoYXNfYWdlIDwtIHN1YnNldCh0cmFpbiwgQWdlID4gMCkKbm9uZV9hZ2UgPC0gc2V0ZGlmZih0cmFpbiwgaGFzX2FnZSkKCmhhc19hZ2UkUGFzc2VuZ2VySWQgPC0gTlVMTAoKaGFzX2FnZS50cmFpbiA8LSBzYW1wbGVfZnJhYyhoYXNfYWdlLCAwLjUpCmhhc19hZ2UudGVzdCA8LSBzZXRkaWZmKGhhc19hZ2UsIGhhc19hZ2UudHJhaW4pCgoKYWdlLmZpdCA8LSBnbG0oQWdlIH4gLiAsIGRhdGEgPSBoYXNfYWdlLnRyYWluKQphZ2UuZml0LnN0ZXBzIDwtIHN0ZXAoYWdlLmZpdCwgZGlyZWN0aW9uID0gImJhY2t3YXJkIiwgdHJhY2UgPSAwKQpzdW1tYXJ5KGFnZS5maXQuc3RlcHMpCmBgYAoKYGBge3J9CmFnZS5maXQgPC0gZ2xtKAogIGZvcm11bGEgPSBBZ2UgfiBTdXJ2aXZlZCArIFBjbGFzcyArIFNleCArIFNpYlNwICsgTmFtZS5jYWxsLCAKICBkYXRhID0gaGFzX2FnZS50cmFpbiwgCiAgZmFtaWx5ID0gImdhdXNzaWFuIikgCgpoYXNfYWdlLnRlc3QkcHJlZCA8LSBwcmVkaWN0KGFnZS5maXQsIGhhc19hZ2UudGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpCnBsb3QoaGFzX2FnZS50ZXN0JEFnZSwgaGFzX2FnZS50ZXN0JHByZWQpCmBgYAoKYGBge3J9Cmhhc19hZ2UgPC0gc3Vic2V0KHRyYWluLCBBZ2UgIT0gLTEpCm5vbmVfYWdlIDwtIHNldGRpZmYodHJhaW4sIGhhc19hZ2UpCgpoYXNfYWdlJFBhc3NlbmdlcklkIDwtIE5VTEwKaGFzX2FnZSRUaWNrZXQucHJlZml4IDwtIE5VTEwKaGFzX2FnZSRncm91cF9ubyA8LSBOVUxMCmhhc19hZ2UkQ2FiaW4ucHJlZml4IDwtIE5VTEwKCmhhc19hZ2UudHJhaW4gPC0gc2FtcGxlX2ZyYWMoaGFzX2FnZSwgMC4zKQpoYXNfYWdlLnRlc3QgPC0gc2V0ZGlmZihoYXNfYWdlLCBoYXNfYWdlLnRyYWluKQoKaGFzX2FnZS50cmFpbiRBZ2UgPC0gZmFjdG9yKGhhc19hZ2UudHJhaW4kQWdlKQoKCiNyZWdpc3RlckRvTUMoY29yZXMgPSA0KQoKZml0Q29udHJvbCA8LSB0cmFpbkNvbnRyb2woIyMgMTAtZm9sZCBDVgogICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmVwZWF0ZWRjdiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAjIyByZXBlYXRlZCB0ZW4gdGltZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwZWF0cyA9IDEwKQoKZ2JtR3JpZCA8LSAgZXhwYW5kLmdyaWQoaW50ZXJhY3Rpb24uZGVwdGggPSBjKDEsMyw1LDkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgbi50cmVlcyA9ICgxOjEwKSoxMCwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNocmlua2FnZSA9IDAuMDUsCiAgICAgICAgICAgICAgICAgICAgICAgIG4ubWlub2JzaW5ub2RlID0gMTApCgpzZXQuc2VlZCg4MjUpCmFnZS5maXQuZ2JtIDwtIHRyYWluKEFnZSB+IC4sIGRhdGEgPSBzdWJzZXQoaGFzX2FnZS50cmFpbiwgc2VsZWN0ID0gLWMoU3Vydml2ZWQpKSwgCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdibSIsIAogICAgICAgICAgICAgICAgIG1ldHJpYyA9ICJBY2N1cmFjeSIsCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gZml0Q29udHJvbCwKICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGdibUdyaWQsCiAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQoKCmBgYAoKYGBge3J9CiMgVGhlIGZpbmFsIHZhbHVlcyB1c2VkIGZvciB0aGUgbW9kZWwgd2VyZSBuLnRyZWVzID0gMjAsIGludGVyYWN0aW9uLmRlcHRoID0gMywgc2hyaW5rYWdlID0gMC4wNSBhbmQgbi5taW5vYnNpbm5vZGUgPSAxMC4KcGxvdChhZ2UuZml0LmdibSkKcGxvdChhZ2UuZml0LmdibSwgbWV0cmljID0gIkFjY3VyYWN5IikKCmhhc19hZ2UudGVzdCRwcmVkIDwtIHByZWRpY3QoYWdlLmZpdC5nYm0sIGhhc19hZ2UudGVzdCkKdGFibGUoaGFzX2FnZS50ZXN0JEFnZSwgaGFzX2FnZS50ZXN0JHByZWQpCmBgYAoKYGBge3J9Cmhhc19hZ2UgPC0gc3Vic2V0KHRyYWluLCBBZ2UgPiAwKQpub25lX2FnZSA8LSBzZXRkaWZmKHRyYWluLCBoYXNfYWdlKQoKaGFzX2FnZSRQYXNzZW5nZXJJZCA8LSBOVUxMCmhhc19hZ2UkVGlja2V0LnByZWZpeCA8LSBOVUxMCmhhc19hZ2UkZ3JvdXBfbm8gPC0gTlVMTApoYXNfYWdlJENhYmluLnByZWZpeCA8LSBOVUxMCgpoYXNfYWdlLnRyYWluIDwtIHNhbXBsZV9mcmFjKGhhc19hZ2UsIDAuNSkKaGFzX2FnZS50ZXN0IDwtIHNldGRpZmYoaGFzX2FnZSwgaGFzX2FnZS50cmFpbikKCmhhc19hZ2UudHJhaW4kQWdlIDwtIGZhY3RvcihoYXNfYWdlLnRyYWluJEFnZSkKCmxpYnJhcnkoQzUwKQpmaXQuYzUwIDwtIEM1LjAoIHggPSBzdWJzZXQoaGFzX2FnZS50cmFpbiwgc2VsZWN0ID0gLWMoU3Vydml2ZWQsIEFnZSkpLAogICAgICAgICAgICAgICAgIHkgPSBoYXNfYWdlLnRyYWluJEFnZSwgCiAgICAgICAgICAgICAgICAgIyBhbiBpbnRlZ2VyIHNwZWNpZnlpbmcgdGhlIG51bWJlciBvZiBib29zdGluZyBpdGVyYXRpb25zLiBBIHZhbHVlIG9mIG9uZSBpbmRpY2F0ZXMgdGhhdCBhIHNpbmdsZSBtb2RlbCBpcyB1c2VkLgogICAgICAgICAgICAgICAgICMgQzUuMCB1c2VzIGFkYXB0aXZlIGJvb3N0aW5nIAogICAgICAgICAgICAgICAgIHRyaWFscyA9IDEwLCAgCiAgICAgICAgICAgICAgICAgY29udHJvbCA9IEM1LjBDb250cm9sKGVhcmx5U3RvcHBpbmcgPSBGKSkKcHJpbnQoZml0LmM1MCkKaGFzX2FnZS50ZXN0JHByZWQgPC0gcHJlZGljdChmaXQuYzUwLCBoYXNfYWdlLnRlc3QpCnBsb3QoaGFzX2FnZS50ZXN0JEFnZSwgaGFzX2FnZS50ZXN0JHByZWQpCmBgYAoKYGBge3J9CnRhYmxlKGhhc19hZ2UudGVzdCRBZ2UsIGhhc19hZ2UudGVzdCRwcmVkKQpgYGAKCmBgYHtyfQojIFJhbmRvbSBTZWFyY2gKY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kPSJyZXBlYXRlZGN2IiwgbnVtYmVyPTEwLCByZXBlYXRzPTMsIHNlYXJjaD0icmFuZG9tIikKc2V0LnNlZWQoMTIzNCkKbXRyeSA8LSBzcXJ0KG5jb2woaGFzX2FnZS50cmFpbikpCnJmX3JhbmRvbSA8LSB0cmFpbihBZ2Ugfi4sIGRhdGE9aGFzX2FnZS50cmFpbiwgbWV0aG9kPSJyZiIsIG1ldHJpYz0iS2FwcGEiLCB0dW5lTGVuZ3RoPTE1LCB0ckNvbnRyb2w9Y29udHJvbCkKcHJpbnQocmZfcmFuZG9tKQpwbG90KHJmX3JhbmRvbSkKYGBgCgpgYGB7cn0Kbm9uX2FnZSA8LSBzdWJzZXQodHJhaW4sIEFnZSA9PSAtMSkKbm9uX2FnZSRBZ2UgPC0gcHJlZGljdChhZ2UuZml0LmdibSwgbm9uX2FnZSkKCnRyYWluJEFnZSA8LSBmYWN0b3IodHJhaW4kQWdlKQpwcmVkaWN0ZWRfYWdlcyA8LSBzdWJzZXQobm9uX2FnZSwgc2VsZWN0ID0gYyhQYXNzZW5nZXJJZCwgQWdlKSkKdHJhaW4gJTw+JSBhbnRpX2pvaW4ocHJlZGljdGVkX2FnZXMsIGJ5ID0gIlBhc3NlbmdlcklkIikgJT4lIGJpbmRfcm93cyhwcmVkaWN0ZWRfYWdlcykKYGBgCgpgYGB7cn0KdHJhaW4gPC0gbmEub21pdCh0cmFpbikKCmdncGxvdCh0cmFpbixhZXMoeD1BZ2UsZ3JvdXA9U3Vydml2ZWQsZmlsbD1TdXJ2aXZlZCkpK2dlb21fYmFyKHBvc2l0aW9uPSJpZGVudGl0eSIsYWxwaGE9MC41KSt0aGVtZV9idygpCmBgYAoKYGBge3J9CnJlZ2lzdGVyRG9NQyhjb3JlcyA9IDQpCgpmaXRDb250cm9sIDwtIHRyYWluQ29udHJvbCgjIyAxMC1mb2xkIENWCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICMjIHJlcGVhdGVkIHRlbiB0aW1lcwogICAgICAgICAgICAgICAgICAgICAgICAgICByZXBlYXRzID0gMTApCgpnYm1HcmlkIDwtICBleHBhbmQuZ3JpZChpbnRlcmFjdGlvbi5kZXB0aCA9IGMoMSwzLDUsNyw5KSwgCiAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXMgPSAoMToxMCkqNTAsIAogICAgICAgICAgICAgICAgICAgICAgICBzaHJpbmthZ2UgPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICBuLm1pbm9ic2lubm9kZSA9IDUpCgpzZXQuc2VlZCg4MjUpClN1cnZpdmVkLmZpdC5nYm0gPC0gdHJhaW4oU3Vydml2ZWQgfiAuLCBkYXRhID0gdHJhaW4sIAogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJnYm0iLCAKICAgICAgICAgICAgICAgICBtZXRyaWMgPSAiQWNjdXJhY3kiLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGZpdENvbnRyb2wsCiAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBnYm1HcmlkLAogICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKYGBgCgpgYGB7cn0KIyBUaGUgZmluYWwgdmFsdWVzIHVzZWQgZm9yIHRoZSBtb2RlbCB3ZXJlIG4udHJlZXMgPSA0NTAsIGludGVyYWN0aW9uLmRlcHRoID0gMywgc2hyaW5rYWdlID0gMC4wMSBhbmQgbi5taW5vYnNpbm5vZGUgPSA1LgpwbG90KFN1cnZpdmVkLmZpdC5nYm0pCmBgYAoKYGBge3J9CmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShkcGx5cikKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoeGRhKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGRvTUMpCgojIHRlc3Qg642w7J207YSw7JeQIOuMgO2VtCwg7JyE7JeQIOuNsOydtO2EsCDqsIDqs7XsnYQg7ZWY64qUIO2VqOyImOulvCDrp4zrk6TsnpAuCm5hbWVfc2VwZXJhdG9yIDwtIFZlY3Rvcml6ZShmdW5jdGlvbihuYW1lKSB7CiAgc3BsaXR0ZWQgPC0gc3Ryc3BsaXQobmFtZSwgIiwiKVtbMV1dWzJdCiAgY2FsbCA8LSBzdHJfdHJpbShzdHJfc3BsaXQoc3BsaXR0ZWQsICJcXC4iKVtbMV1dWzFdKQogIAogIHJldHVybihpZmVsc2UoY2FsbCAlaW4lIGMoJ01hc3RlcicsICdNaXNzJywgJ01yJywgJ01ycycpLCBjYWxsLCAnRXRjJykpCn0pCgp0dCA8LSBmdW5jdGlvbih0ZXN0KSB7CiAgdGVzdCAlPD4lIAogICAgcmVwbGFjZV9uYShsaXN0KEVtYmFya2VkID0gInVua25vd24iKSkgJT4lCiAgICBtdXRhdGVfZWFjaF8oZnVucyhmYWN0b3IoLikpLCBjKCJTZXgiLCAiRW1iYXJrZWQiLCAiUGNsYXNzIikpICU+JQogICAgc2VwYXJhdGUoY29sID0gVGlja2V0LCBpbnRvID0gYygiVGlja2V0LnByZWZpeCIsICJUaWNrZXQubm8iKSwgc2VwID0gIiAiLCBmaWxsID0gImxlZnQiLCBleHRyYSA9ICJkcm9wIikgJT4lCiAgICByZXBsYWNlX25hKGxpc3QoVGlja2V0LnByZWZpeCA9ICJub25lIiwgRmFyZSA9IDApKSAlPiUKICAgIG11dGF0ZShUaWNrZXQucHJlZml4ID0gc3RyaV9yZXBsYWNlX2FsbF9maXhlZChUaWNrZXQucHJlZml4LCAiLiIsICIiKSkgJT4lIAogICAgbXV0YXRlKFRpY2tldC5wcmVmaXggPSBpZmVsc2UoVGlja2V0LnByZWZpeCAlaW4lIGMoIkNBIiwgIlBDIiksIFRpY2tldC5wcmVmaXggLCJFVEMiKSkKICAgIAogIGR1cF90aWNrZXRfcGFzc2VuZ2VycyA8LSB0ZXN0ICU+JSBncm91cF9ieShUaWNrZXQubm8pICU+JSBmaWx0ZXIobigpID4gMSkKICBncm91cCA8LSBzdWJzZXQoZHVwX3RpY2tldF9wYXNzZW5nZXJzLCBzZWxlY3QgPSBjKFBhc3NlbmdlcklkLCBUaWNrZXQubm8pKQogIGNvbG5hbWVzKGdyb3VwKSA8LSBjKCJQYXNzZW5nZXJJZCIsICJncm91cF9ubyIpCiAgZ3JvdXAkVGlja2V0Lm5vIDwtIE5VTEwKICAKICB0ZXN0ICU8PiUgCiAgICBtZXJnZShncm91cCwgYnkgPSAiUGFzc2VuZ2VySWQiLCBhbGwueCA9IFQpICU+JQogICAgcmVwbGFjZV9uYShsaXN0KGdyb3VwX25vID0gIm5vbmUiKSkgJT4lIAogICAgbXV0YXRlX2VhY2hfKGZ1bnMoZmFjdG9yKC4pKSwgYygiZ3JvdXBfbm8iKSkgLT4gdGVzdAogIAogIHRlc3QgJTw+JSAKICAgIHJlcGxhY2VfbmEobGlzdChDYWJpbiA9ICJYIikpICU+JSAKICAgIG11dGF0ZShsb2FkX2NvdW50ID0gc3RyaV9jb3VudChDYWJpbiwgcmVnZXg9IlxcUysiKSwgQ2FiaW4ucHJlZml4ID0gc3RyaV9zdWIoQ2FiaW4sIDAsIDEpKSAlPiUKICAgIHJlcGxhY2VfbmEobGlzdChsb2FkX2NvdW50ID0gMCkpICU+JSAKICAgIG11dGF0ZShDYWJpbi5wcmVmaXggPSBpZmVsc2UoQ2FiaW4ucHJlZml4ICVpbiUgYygiRyIsICJUIiksICJFVEMiLCBDYWJpbi5wcmVmaXgpKQogIAogIHRlc3QgPC0gdHJhbnNmb3JtKHRlc3QsIGZhbWlseV9zaXplID0gU2liU3AgKyBQYXJjaCkKICAKICB0ZXN0IDwtIHRyYW5zZm9ybSh0ZXN0LCBsb2dfZmFyZSA9IGlmZWxzZShyb3VuZChsb2coRmFyZSksMCkgPD0gMSwgMSwgcm91bmQobG9nKEZhcmUpLDApKSkKICB0ZXN0JGxvZ19mYXJlIDwtIGZhY3Rvcih0ZXN0JGxvZ19mYXJlKQogIAogIHRlc3QgJTw+JSAKICAgIG11dGF0ZShOYW1lLmNhbGwgPSBuYW1lX3NlcGVyYXRvcihOYW1lKSkgJT4lCiAgICBtdXRhdGVfZWFjaF8oZnVucyhmYWN0b3IoLikpLCBjKCJOYW1lLmNhbGwiKSkKICAKICB0ZXN0IDwtIHRyYW5zZm9ybSh0ZXN0LCBBZ2UgPSBpZmVsc2UoaXMubmEoQWdlKSwgLTEsIGlmZWxzZShyb3VuZChBZ2UgLyAxMCwwKSA+PSA2LCA2LCByb3VuZChBZ2UgLyAxMCwwKSkpKQoKICB0ZXN0ICU8PiUgc2VsZWN0KC1jKE5hbWUsIENhYmluLCBUaWNrZXQubm8sIEZhcmUpKQogIAogIHJldHVybih0ZXN0KQp9Cgp0cmFpbl9hZ2VfcHJlaWN0aW9uX21vZGVsIDwtIGZ1bmN0aW9uKHRyYWluKSB7CiAgdHJhaW4gPC0gc3Vic2V0KHRyYWluLCBBZ2UgIT0gLTEpCiAgdHJhaW4kQWdlIDwtIGZhY3Rvcih0cmFpbiRBZ2UpCiAgCiAgdHJhaW4gJTw+JSBzZWxlY3QoLWMoUGFzc2VuZ2VySWQsIFRpY2tldC5wcmVmaXgsIGdyb3VwX25vLCBDYWJpbi5wcmVmaXgsIFN1cnZpdmVkKSkKCiAgZml0Q29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gInJlcGVhdGVkY3YiLCBudW1iZXIgPSAxMCwgcmVwZWF0cyA9IDEwKQogIAogIGdibUdyaWQgPC0gIGV4cGFuZC5ncmlkKGludGVyYWN0aW9uLmRlcHRoID0gYygxLDMsNSw5KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbi50cmVlcyA9ICgxOjEwKSoxMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2hyaW5rYWdlID0gMC4wNSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuLm1pbm9ic2lubm9kZSA9IDUpCiAgCiAgc2V0LnNlZWQoMTIzNCkKICBhZ2UuZml0LmdibSA8LSB0cmFpbihBZ2UgfiAuLCBkYXRhID0gdHJhaW4sIAogICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdibSIsIAogICAgICAgICAgICAgICAgICAgbWV0cmljID0gIkFjY3VyYWN5IiwKICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGZpdENvbnRyb2wsCiAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGdibUdyaWQsCiAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVCkKICAKICByZXR1cm4oYWdlLmZpdC5nYm0pCn0KCnRyYWluX3N1cnZpdmFsX3ByZWRpY3Rpb25fbW9kZWwgPC0gZnVuY3Rpb24odHJhaW4pIHsKICB0cmFpbiA8LSBzdWJzZXQodHJhaW4sIEFnZSAhPSAtMSkKICB0cmFpbiRBZ2UgPC0gZmFjdG9yKHRyYWluJEFnZSkKICB0cmFpbiRTdXJ2aXZlZCA8LSBmYWN0b3IodHJhaW4kU3Vydml2ZWQpCiAgCiAgdHJhaW4gJTw+JSBzZWxlY3QoLWMoZ3JvdXBfbm8pKQogIAogIGZpdENvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBlYXRzID0gMTApCiAgCiAgZ2JtR3JpZCA8LSAgZXhwYW5kLmdyaWQoaW50ZXJhY3Rpb24uZGVwdGggPSBjKDEsMyw1KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbi50cmVlcyA9ICgxOjEwKSo1MCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2hyaW5rYWdlID0gMC4wNSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuLm1pbm9ic2lubm9kZSA9IDUpCiAgCiAgc2V0LnNlZWQoODI1KQogIFN1cnZpdmVkLmZpdC5nYm0gPC0gdHJhaW4oU3Vydml2ZWQgfiAuLCBkYXRhID0gdHJhaW4sIAogICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImdibSIsIAogICAgICAgICAgICAgICAgICAgbWV0cmljID0gIkFjY3VyYWN5IiwKICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGZpdENvbnRyb2wsCiAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGdibUdyaWQsCiAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVCkKICAKICByZXR1cm4oU3Vydml2ZWQuZml0LmdibSkKfQoKcHJlZGljdF9hZ2UgPC0gZnVuY3Rpb24odGVzdCwgYWdlLmZpdCkgewogICAgICAKICBub25fYWdlIDwtIHN1YnNldCh0ZXN0LCBBZ2UgPT0gLTEpCiAgbm9uX2FnZSRBZ2UgPC0gcHJlZGljdChhZ2UuZml0LCBub25fYWdlKQogIAogIHRlc3QkQWdlIDwtIGZhY3Rvcih0ZXN0JEFnZSkKICB0ZXN0ICU8PiUgYW50aV9qb2luKG5vbl9hZ2UsIGJ5ID0gIlBhc3NlbmdlcklkIikgJT4lIGJpbmRfcm93cyhub25fYWdlKQogIHRlc3QkQWdlIDwtIGZhY3Rvcih0ZXN0JEFnZSkKICByZXR1cm4odGVzdCkKfQpgYGAKCgoKYGBge3J9CnRyYWluIDwtIGZyZWFkKCIvVXNlcnMvQ0EvRG93bmxvYWRzL3RpdGFuaWMvdHJhaW4uY3N2IiwgbmEuc3RyaW5ncyA9ICIiKQp0ZXN0IDwtIGZyZWFkKCIvVXNlcnMvQ0EvRG93bmxvYWRzL3RpdGFuaWMvdGVzdC5jc3YiLCBuYS5zdHJpbmdzID0gIiIpCgpyZWZpbmVkX3RyYWluX2RhdGEgPC0gdHQodHJhaW4pCnJlZmluZWRfdGVzdF9kYXRhIDwtIHR0KHRlc3QpCgphZ2UuZml0IDwtIHRyYWluX2FnZV9wcmVpY3Rpb25fbW9kZWwocmVmaW5lZF90cmFpbl9kYXRhKQpzdXJ2aXZhbC5maXQgPC0gdHJhaW5fc3Vydml2YWxfcHJlZGljdGlvbl9tb2RlbChyZWZpbmVkX3RyYWluX2RhdGEpCgpyZWZpbmVkX3Rlc3RfZGF0YSAgICAgICAgICA8LSBwcmVkaWN0X2FnZShyZWZpbmVkX3Rlc3RfZGF0YSwgYWdlLmZpdCkKcmVmaW5lZF90ZXN0X2RhdGEkcHJlZCAgICAgPC0gcHJlZGljdChzdXJ2aXZhbC5maXQsIHJlZmluZWRfdGVzdF9kYXRhKQpgYGAKCmBgYHtyfQpyZWZpbmVkX3Rlc3RfZGF0YSA8LSBtZXJnZShyZWZpbmVkX3Rlc3RfZGF0YSwgZ2VuZGVyLCBieSA9ICJQYXNzZW5nZXJJZCIpCmNvbmZ1c2lvbk1hdHJpeChnZW5kZXIkU3Vydml2ZWQsIHJlZmluZWRfdGVzdF9kYXRhJHByZWQpCmBgYA==