#--------------
library(randomForest)
library(e1071) #SVM and Naive Bayes
library(rpart)
library('ggplot2') # visualization
library('ggthemes') # visualization
library('scales') # visualization
library('dplyr') # data manipulation
library('mice') # imputation
library(class)
source("./titanicHelper.R")
d<-getCleanTitanicData()
train<-d[[1]]
test<-d[[2]]
#-------------SVM----------------------
svmModel<- svm(Survived~ Pclass + Sex + Age + SibSp
+ Parch + Fare + Embarked + Title
+ FsizeD + Child + Mother, data = train, prob.model=TRUE)
svmPred<-predict(svmModel, newdata = data.frame(test[,c(1,3:19)]))
prepareSubmission(svmPred,"svm_submission.csv")
#-------RANDOM FOREST------------------
rf <- randomForest(train[,c('Pclass','Age','Sex','Parch','SibSp','Fare',
'Embarked','Title','FsizeD','Child','Mother')],
as.factor(train$Survived), ntree=100, importance=TRUE)
rfPred<-predict(rf,test[,c('Pclass','Age','Sex','Parch','SibSp','Fare',
'Embarked','Title','FsizeD','Child','Mother')])
prepareSubmission(rfPred,"rf_submission.csv")
#-------K NEAREST NEIGHBORS-------------
knnTrainX<-train[,c('Pclass','Age','Sex','Parch','SibSp','Fare','Embarked','FsizeD') ]
knnTrainY<-train[,2]
knnTestX<-test[,c('Pclass','Age','Sex','Parch','SibSp','Fare','Embarked','FsizeD')]
knnTestY<-test[,2]
#knn Clean
knnTrainX$Sex <- sapply(as.character(knnTrainX$Sex), switch, 'male' = 0, 'female' = 1)
knnTestX$Sex <- sapply(as.character(knnTestX$Sex), switch, 'male' = 0, 'female' = 1)
knnTrainX$Embarked <- sapply(as.character(knnTrainX$Embarked), switch, 'C' = 0, 'Q' = 1, 'S' = 2)
knnTestX$Embarked <- sapply(as.character(knnTestX$Embarked), switch, 'C' = 0, 'Q' = 1, 'S' = 2)
knnTrainX$FsizeD <- sapply(as.character(knnTrainX$FsizeD), switch, 'large' = 2, 'small' = 1, 'singleton' = 0)
knnTestX$FsizeD <- sapply(as.character(knnTestX$FsizeD), switch, 'large' = 2, 'small' = 1, 'singleton' = 0)
knnPred <- knn(train=knnTrainX, test=knnTestX, cl = knnTrainY, k=35)
prepareSubmission(knnPred, "knn_submission.csv")
#-------NAIVE BAYES-------------
#Naive Bayes does not function well with this dataset. It is likely because we can't
#really tell if the assumptions of conditional independence of the feature vectors work.
nb<-naiveBayes(x = train[,c('Pclass','Age','Sex')], y=train[,'Survived'])
nbtestX<-test[,c('Pclass','Age','Sex')]
nbPred<-predict(nb,newdata = nbtestX, type = "class")
prepareSubmission(nbPred, "nb_submission.csv")
#---------ENSEMBLE----------------------
knnPred<-sapply(as.character(knnPred), switch, '0'=0, '1'=1)
rfPred<-sapply(as.character(rfPred), switch, '0'=0, '1'=1)
svmPred<-sapply(as.character(svmPred), switch, '0'=0, '1'=1)
nbPred<-sapply(as.character(nbPred), switch, '0'=0, '1'=1)
ensemblePred<-round((2*knnPred+2*rfPred+2*svmPred+1*nbPred)/7)
prepareSubmission(ensemblePred, "ensemble_submission2.csv")
Code in TitanicHelper.R File
readData<-function()
{
train <- read.csv("train.csv", stringsAsFactors=FALSE)
test <- read.csv("test.csv", stringsAsFactors=FALSE)
full <- bind_rows(train, test)
return (full)
}
cleanTitle<-function(full)
{
full$Title <- gsub('(.*, )|(\\..*)', '', full$Name)
uncommon <- c('Dona', 'Lady', 'the Countess','Capt', 'Col', 'Don',
'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer')
full$Title[full$Title == 'Mlle'] <- 'Miss'
full$Title[full$Title == 'Ms'] <- 'Miss'
full$Title[full$Title == 'Mme'] <- 'Mrs'
full$Title[full$Title %in% uncommon] <- 'Rare Title'
return(full)
}
calcFamilySize<-function(full)
{
full$Surname <- sapply(full$Name,
function(x) strsplit(x, split = '[,.]')[[1]][1])
full$Fsize <- full$SibSp + full$Parch + 1
full$Family <- paste(full$Surname, full$Fsize, sep='_')
full$FsizeD[full$Fsize == 1] <- 'singleton'
full$FsizeD[full$Fsize < 5 & full$Fsize > 1] <- 'small'
full$FsizeD[full$Fsize > 4] <- 'large'
return(full)
}
cleanFareEmbarked<-function(full)
{
full$Embarked[c(62, 830)] <- 'C'
full$Fare[1044] <- median(full[full$Pclass == '3' & full$Embarked == 'S', ]$Fare, na.rm = TRUE)
return(full)
}
imputeAge<-function(full)
{
set.seed(129)
mice_mod <- mice(full[, !names(full) %in% c('PassengerId','Name','Ticket','Cabin','Family','Surname','Survived')], method='rf')
mice_output <- complete(mice_mod)
full$Age <- mice_output$Age
return(full)
}
createChildMother<-function(full)
{
full$Child[full$Age < 18] <- 'Child'
full$Child[full$Age >= 18] <- 'Adult'
full$Mother <- 'Not Mother'
full$Mother[full$Sex == 'female' & full$Parch > 0 & full$Age > 18 & full$Title != 'Miss'] <- 'Mother'
full$Child <- factor(full$Child)
full$Mother <- factor(full$Mother)
return(full)
}
getCleanTitanicData<-function()
{
full<-readData()
full<-cleanFareEmbarked(calcFamilySize(cleanTitle(full)))
factor_vars <- c('Survived','PassengerId','Pclass','Sex','Embarked',
'Title','Surname','Family','FsizeD')
full[factor_vars] <- lapply(full[factor_vars], function(x) as.factor(x))
full<-imputeAge(full)
full<-createChildMother(full)
# Split the data back into a train set and a test set
train <- data.frame(full[1:891,])
test <- data.frame(full[892:1309,])
return (list("train"=train,"test"=test))
}
#Prepare Submission
prepareSubmission<-function(pred,filename)
{
submission<-data.frame()
PassengerID<-892:1309
submission<-data.frame("PassengerID" = PassengerID, "Survived"=pred)
write.csv(submission,filename, row.names=FALSE)
}
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoZTEwNzEpICNTVk0gYW5kIE5haXZlIEJheWVzDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeSgnZ2dwbG90MicpICMgdmlzdWFsaXphdGlvbg0KbGlicmFyeSgnZ2d0aGVtZXMnKSAjIHZpc3VhbGl6YXRpb24NCmxpYnJhcnkoJ3NjYWxlcycpICMgdmlzdWFsaXphdGlvbg0KbGlicmFyeSgnZHBseXInKSAjIGRhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KCdtaWNlJykgIyBpbXB1dGF0aW9uDQpsaWJyYXJ5KGNsYXNzKQ0KDQpzb3VyY2UoIi4vdGl0YW5pY0hlbHBlci5SIikNCmQ8LWdldENsZWFuVGl0YW5pY0RhdGEoKQ0KdHJhaW48LWRbWzFdXQ0KdGVzdDwtZFtbMl1dDQoNCiMtLS0tLS0tLS0tLS0tU1ZNLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0Kc3ZtTW9kZWw8LSBzdm0oU3Vydml2ZWR+ICBQY2xhc3MgKyBTZXggKyBBZ2UgKyBTaWJTcCANCiAgICAgICAgICAgICAgICAgICAgICAgICsgUGFyY2ggKyBGYXJlICsgRW1iYXJrZWQgKyBUaXRsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgKyBGc2l6ZUQgKyBDaGlsZCArIE1vdGhlciwgZGF0YSA9IHRyYWluLCBwcm9iLm1vZGVsPVRSVUUpDQpzdm1QcmVkPC1wcmVkaWN0KHN2bU1vZGVsLCBuZXdkYXRhID0gZGF0YS5mcmFtZSh0ZXN0WyxjKDEsMzoxOSldKSkNCnByZXBhcmVTdWJtaXNzaW9uKHN2bVByZWQsInN2bV9zdWJtaXNzaW9uLmNzdiIpDQoNCiMtLS0tLS0tUkFORE9NIEZPUkVTVC0tLS0tLS0tLS0tLS0tLS0tLQ0KcmYgPC0gcmFuZG9tRm9yZXN0KHRyYWluWyxjKCdQY2xhc3MnLCdBZ2UnLCdTZXgnLCdQYXJjaCcsJ1NpYlNwJywnRmFyZScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0VtYmFya2VkJywnVGl0bGUnLCdGc2l6ZUQnLCdDaGlsZCcsJ01vdGhlcicpXSwNCiAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3IodHJhaW4kU3Vydml2ZWQpLCBudHJlZT0xMDAsIGltcG9ydGFuY2U9VFJVRSkNCnJmUHJlZDwtcHJlZGljdChyZix0ZXN0WyxjKCdQY2xhc3MnLCdBZ2UnLCdTZXgnLCdQYXJjaCcsJ1NpYlNwJywnRmFyZScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0VtYmFya2VkJywnVGl0bGUnLCdGc2l6ZUQnLCdDaGlsZCcsJ01vdGhlcicpXSkNCnByZXBhcmVTdWJtaXNzaW9uKHJmUHJlZCwicmZfc3VibWlzc2lvbi5jc3YiKQ0KDQojLS0tLS0tLUsgTkVBUkVTVCBORUlHSEJPUlMtLS0tLS0tLS0tLS0tDQprbm5UcmFpblg8LXRyYWluWyxjKCdQY2xhc3MnLCdBZ2UnLCdTZXgnLCdQYXJjaCcsJ1NpYlNwJywnRmFyZScsJ0VtYmFya2VkJywnRnNpemVEJykgXQ0Ka25uVHJhaW5ZPC10cmFpblssMl0NCmtublRlc3RYPC10ZXN0WyxjKCdQY2xhc3MnLCdBZ2UnLCdTZXgnLCdQYXJjaCcsJ1NpYlNwJywnRmFyZScsJ0VtYmFya2VkJywnRnNpemVEJyldDQprbm5UZXN0WTwtdGVzdFssMl0NCiNrbm4gQ2xlYW4NCmtublRyYWluWCRTZXggPC0gc2FwcGx5KGFzLmNoYXJhY3Rlcihrbm5UcmFpblgkU2V4KSwgc3dpdGNoLCAnbWFsZScgPSAwLCAnZmVtYWxlJyA9IDEpDQprbm5UZXN0WCRTZXggPC0gc2FwcGx5KGFzLmNoYXJhY3Rlcihrbm5UZXN0WCRTZXgpLCBzd2l0Y2gsICdtYWxlJyA9IDAsICdmZW1hbGUnID0gMSkNCmtublRyYWluWCRFbWJhcmtlZCA8LSBzYXBwbHkoYXMuY2hhcmFjdGVyKGtublRyYWluWCRFbWJhcmtlZCksIHN3aXRjaCwgJ0MnID0gMCwgJ1EnID0gMSwgJ1MnID0gMikNCmtublRlc3RYJEVtYmFya2VkIDwtIHNhcHBseShhcy5jaGFyYWN0ZXIoa25uVGVzdFgkRW1iYXJrZWQpLCBzd2l0Y2gsICdDJyA9IDAsICdRJyA9IDEsICdTJyA9IDIpDQprbm5UcmFpblgkRnNpemVEIDwtIHNhcHBseShhcy5jaGFyYWN0ZXIoa25uVHJhaW5YJEZzaXplRCksIHN3aXRjaCwgJ2xhcmdlJyA9IDIsICdzbWFsbCcgPSAxLCAnc2luZ2xldG9uJyA9IDApDQprbm5UZXN0WCRGc2l6ZUQgPC0gc2FwcGx5KGFzLmNoYXJhY3Rlcihrbm5UZXN0WCRGc2l6ZUQpLCBzd2l0Y2gsICdsYXJnZScgPSAyLCAnc21hbGwnID0gMSwgJ3NpbmdsZXRvbicgPSAwKQ0KDQprbm5QcmVkIDwtIGtubih0cmFpbj1rbm5UcmFpblgsIHRlc3Q9a25uVGVzdFgsIGNsID0ga25uVHJhaW5ZLCBrPTM1KQ0KcHJlcGFyZVN1Ym1pc3Npb24oa25uUHJlZCwgImtubl9zdWJtaXNzaW9uLmNzdiIpDQoNCiMtLS0tLS0tTkFJVkUgQkFZRVMtLS0tLS0tLS0tLS0tDQojTmFpdmUgQmF5ZXMgZG9lcyBub3QgZnVuY3Rpb24gd2VsbCB3aXRoIHRoaXMgZGF0YXNldC4gSXQgaXMgbGlrZWx5IGJlY2F1c2Ugd2UgY2FuJ3QNCiNyZWFsbHkgdGVsbCBpZiB0aGUgYXNzdW1wdGlvbnMgb2YgY29uZGl0aW9uYWwgaW5kZXBlbmRlbmNlIG9mIHRoZSBmZWF0dXJlIHZlY3RvcnMgd29yay4NCg0KbmI8LW5haXZlQmF5ZXMoeCA9IHRyYWluWyxjKCdQY2xhc3MnLCdBZ2UnLCdTZXgnKV0sIHk9dHJhaW5bLCdTdXJ2aXZlZCddKQ0KbmJ0ZXN0WDwtdGVzdFssYygnUGNsYXNzJywnQWdlJywnU2V4JyldDQpuYlByZWQ8LXByZWRpY3QobmIsbmV3ZGF0YSA9IG5idGVzdFgsIHR5cGUgPSAiY2xhc3MiKQ0KcHJlcGFyZVN1Ym1pc3Npb24obmJQcmVkLCAibmJfc3VibWlzc2lvbi5jc3YiKQ0KDQojLS0tLS0tLS0tRU5TRU1CTEUtLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQprbm5QcmVkPC1zYXBwbHkoYXMuY2hhcmFjdGVyKGtublByZWQpLCBzd2l0Y2gsICcwJz0wLCAnMSc9MSkNCnJmUHJlZDwtc2FwcGx5KGFzLmNoYXJhY3RlcihyZlByZWQpLCBzd2l0Y2gsICcwJz0wLCAnMSc9MSkNCnN2bVByZWQ8LXNhcHBseShhcy5jaGFyYWN0ZXIoc3ZtUHJlZCksIHN3aXRjaCwgJzAnPTAsICcxJz0xKQ0KbmJQcmVkPC1zYXBwbHkoYXMuY2hhcmFjdGVyKG5iUHJlZCksIHN3aXRjaCwgJzAnPTAsICcxJz0xKQ0KZW5zZW1ibGVQcmVkPC1yb3VuZCgoMiprbm5QcmVkKzIqcmZQcmVkKzIqc3ZtUHJlZCsxKm5iUHJlZCkvNykNCnByZXBhcmVTdWJtaXNzaW9uKGVuc2VtYmxlUHJlZCwgImVuc2VtYmxlX3N1Ym1pc3Npb24yLmNzdiIpDQoNCmBgYA0KDQoNCg0KDQojI0NvZGUgaW4gVGl0YW5pY0hlbHBlci5SIEZpbGUNCg0KYGBge3J9DQpyZWFkRGF0YTwtZnVuY3Rpb24oKQ0Kew0KICB0cmFpbiA8LSByZWFkLmNzdigidHJhaW4uY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkNCiAgdGVzdCAgPC0gcmVhZC5jc3YoInRlc3QuY3N2IiwgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpDQogIGZ1bGwgIDwtIGJpbmRfcm93cyh0cmFpbiwgdGVzdCkNCiAgcmV0dXJuIChmdWxsKQ0KfQ0KDQpjbGVhblRpdGxlPC1mdW5jdGlvbihmdWxsKQ0Kew0KICBmdWxsJFRpdGxlIDwtIGdzdWIoJyguKiwgKXwoXFwuLiopJywgJycsIGZ1bGwkTmFtZSkNCiAgDQogIHVuY29tbW9uIDwtIGMoJ0RvbmEnLCAnTGFkeScsICd0aGUgQ291bnRlc3MnLCdDYXB0JywgJ0NvbCcsICdEb24nLCANCiAgICAgICAgICAgICAgICAgICdEcicsICdNYWpvcicsICdSZXYnLCAnU2lyJywgJ0pvbmtoZWVyJykNCiAgDQogIGZ1bGwkVGl0bGVbZnVsbCRUaXRsZSA9PSAnTWxsZSddICAgICAgICA8LSAnTWlzcycgDQogIGZ1bGwkVGl0bGVbZnVsbCRUaXRsZSA9PSAnTXMnXSAgICAgICAgICA8LSAnTWlzcycNCiAgZnVsbCRUaXRsZVtmdWxsJFRpdGxlID09ICdNbWUnXSAgICAgICAgIDwtICdNcnMnIA0KICBmdWxsJFRpdGxlW2Z1bGwkVGl0bGUgJWluJSB1bmNvbW1vbl0gIDwtICdSYXJlIFRpdGxlJw0KICByZXR1cm4oZnVsbCkNCn0NCg0KY2FsY0ZhbWlseVNpemU8LWZ1bmN0aW9uKGZ1bGwpDQp7DQogIGZ1bGwkU3VybmFtZSA8LSBzYXBwbHkoZnVsbCROYW1lLCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgc3Ryc3BsaXQoeCwgc3BsaXQgPSAnWywuXScpW1sxXV1bMV0pDQogIGZ1bGwkRnNpemUgPC0gZnVsbCRTaWJTcCArIGZ1bGwkUGFyY2ggKyAxDQogIGZ1bGwkRmFtaWx5IDwtIHBhc3RlKGZ1bGwkU3VybmFtZSwgZnVsbCRGc2l6ZSwgc2VwPSdfJykNCiAgZnVsbCRGc2l6ZURbZnVsbCRGc2l6ZSA9PSAxXSA8LSAnc2luZ2xldG9uJw0KICBmdWxsJEZzaXplRFtmdWxsJEZzaXplIDwgNSAmIGZ1bGwkRnNpemUgPiAxXSA8LSAnc21hbGwnDQogIGZ1bGwkRnNpemVEW2Z1bGwkRnNpemUgPiA0XSA8LSAnbGFyZ2UnDQogIHJldHVybihmdWxsKQ0KfQ0KDQpjbGVhbkZhcmVFbWJhcmtlZDwtZnVuY3Rpb24oZnVsbCkNCnsNCiAgZnVsbCRFbWJhcmtlZFtjKDYyLCA4MzApXSA8LSAnQycNCiAgZnVsbCRGYXJlWzEwNDRdIDwtIG1lZGlhbihmdWxsW2Z1bGwkUGNsYXNzID09ICczJyAmIGZ1bGwkRW1iYXJrZWQgPT0gJ1MnLCBdJEZhcmUsIG5hLnJtID0gVFJVRSkNCiAgcmV0dXJuKGZ1bGwpDQp9DQoNCmltcHV0ZUFnZTwtZnVuY3Rpb24oZnVsbCkNCnsNCiAgc2V0LnNlZWQoMTI5KQ0KICBtaWNlX21vZCA8LSBtaWNlKGZ1bGxbLCAhbmFtZXMoZnVsbCkgJWluJSBjKCdQYXNzZW5nZXJJZCcsJ05hbWUnLCdUaWNrZXQnLCdDYWJpbicsJ0ZhbWlseScsJ1N1cm5hbWUnLCdTdXJ2aXZlZCcpXSwgbWV0aG9kPSdyZicpIA0KICBtaWNlX291dHB1dCA8LSBjb21wbGV0ZShtaWNlX21vZCkNCiAgZnVsbCRBZ2UgPC0gbWljZV9vdXRwdXQkQWdlDQogIHJldHVybihmdWxsKQ0KfQ0KDQpjcmVhdGVDaGlsZE1vdGhlcjwtZnVuY3Rpb24oZnVsbCkNCnsNCiAgZnVsbCRDaGlsZFtmdWxsJEFnZSA8IDE4XSA8LSAnQ2hpbGQnDQogIGZ1bGwkQ2hpbGRbZnVsbCRBZ2UgPj0gMThdIDwtICdBZHVsdCcNCiAgZnVsbCRNb3RoZXIgPC0gJ05vdCBNb3RoZXInDQogIGZ1bGwkTW90aGVyW2Z1bGwkU2V4ID09ICdmZW1hbGUnICYgZnVsbCRQYXJjaCA+IDAgJiBmdWxsJEFnZSA+IDE4ICYgZnVsbCRUaXRsZSAhPSAnTWlzcyddIDwtICdNb3RoZXInDQogIGZ1bGwkQ2hpbGQgIDwtIGZhY3RvcihmdWxsJENoaWxkKQ0KICBmdWxsJE1vdGhlciA8LSBmYWN0b3IoZnVsbCRNb3RoZXIpDQogIHJldHVybihmdWxsKQ0KfQ0KZ2V0Q2xlYW5UaXRhbmljRGF0YTwtZnVuY3Rpb24oKQ0Kew0KICBmdWxsPC1yZWFkRGF0YSgpDQogIGZ1bGw8LWNsZWFuRmFyZUVtYmFya2VkKGNhbGNGYW1pbHlTaXplKGNsZWFuVGl0bGUoZnVsbCkpKQ0KICBmYWN0b3JfdmFycyA8LSBjKCdTdXJ2aXZlZCcsJ1Bhc3NlbmdlcklkJywnUGNsYXNzJywnU2V4JywnRW1iYXJrZWQnLA0KICAgICAgICAgICAgICAgICAgICdUaXRsZScsJ1N1cm5hbWUnLCdGYW1pbHknLCdGc2l6ZUQnKQ0KICBmdWxsW2ZhY3Rvcl92YXJzXSA8LSBsYXBwbHkoZnVsbFtmYWN0b3JfdmFyc10sIGZ1bmN0aW9uKHgpIGFzLmZhY3Rvcih4KSkNCiAgZnVsbDwtaW1wdXRlQWdlKGZ1bGwpDQogIGZ1bGw8LWNyZWF0ZUNoaWxkTW90aGVyKGZ1bGwpDQogICMgU3BsaXQgdGhlIGRhdGEgYmFjayBpbnRvIGEgdHJhaW4gc2V0IGFuZCBhIHRlc3Qgc2V0DQogIHRyYWluIDwtIGRhdGEuZnJhbWUoZnVsbFsxOjg5MSxdKQ0KICB0ZXN0IDwtIGRhdGEuZnJhbWUoZnVsbFs4OTI6MTMwOSxdKQ0KICByZXR1cm4gKGxpc3QoInRyYWluIj10cmFpbiwidGVzdCI9dGVzdCkpDQp9DQoNCg0KI1ByZXBhcmUgU3VibWlzc2lvbg0KcHJlcGFyZVN1Ym1pc3Npb248LWZ1bmN0aW9uKHByZWQsZmlsZW5hbWUpDQp7DQogIHN1Ym1pc3Npb248LWRhdGEuZnJhbWUoKQ0KICBQYXNzZW5nZXJJRDwtODkyOjEzMDkNCiAgc3VibWlzc2lvbjwtZGF0YS5mcmFtZSgiUGFzc2VuZ2VySUQiID0gUGFzc2VuZ2VySUQsICJTdXJ2aXZlZCI9cHJlZCkNCiAgd3JpdGUuY3N2KHN1Ym1pc3Npb24sZmlsZW5hbWUsIHJvdy5uYW1lcz1GQUxTRSkNCn0NCmBgYA0KDQoNCg==