Loading and check data
library(ggplot2)
library(dplyr)
library(scales)
library(mice)
library(ggthemes)
library(randomForest)
Lets read the dataset
setwd("/Users/vidyasagarbhargava/Desktop/Kaggle")
train<-read.csv("train.csv")
test<-read.csv("test.csv")
full<-bind_rows(train, test)
Unequal factor levels: coercing to characterUnequal factor levels: coercing to characterUnequal factor levels: coercing to characterUnequal factor levels: coercing to character
str(full)
'data.frame': 1309 obs. of 12 variables:
$ PassengerId: int 1 2 3 4 5 6 7 8 9 10 ...
$ Survived : int 0 1 1 1 0 0 0 0 1 1 ...
$ Pclass : int 3 1 3 1 3 3 1 3 3 2 ...
$ Name : chr "Braund, Mr. Owen Harris" "Cumings, Mrs. John Bradley (Florence Briggs Thayer)" "Heikkinen, Miss. Laina" "Futrelle, Mrs. Jacques Heath (Lily May Peel)" ...
$ Sex : Factor w/ 2 levels "female","male": 2 1 1 1 2 2 2 2 1 1 ...
$ Age : num 22 38 26 35 35 NA 54 2 27 14 ...
$ SibSp : int 1 1 0 1 0 0 0 3 0 1 ...
$ Parch : int 0 0 0 0 0 0 0 1 2 0 ...
$ Ticket : chr "A/5 21171" "PC 17599" "STON/O2. 3101282" "113803" ...
$ Fare : num 7.25 71.28 7.92 53.1 8.05 ...
$ Cabin : chr "" "C85" "" "C123" ...
$ Embarked : chr "S" "C" "S" "S" ...
Getting Started with feature engineering…
1.What is in Name? passenger title is contained within the passenger name variable and we can use surname to represent families.
#grab title from passenger name
full$Title<-gsub('(.*, )|(\\..*)', '', full$Name)
# Show title counts by sex
table(full$Sex, full$Title)
Capt Col Don Dona Dr Jonkheer Lady Major Master Miss Mlle Mme Mr Mrs Ms Rev Sir the Countess
female 0 0 0 1 1 0 1 0 0 260 2 1 0 197 2 0 0 1
male 1 4 1 0 7 1 0 2 61 0 0 0 757 0 0 8 1 0
# Titles with very low cell counts to be combined to "rare" level
rare_title <- c('Dona', 'Lady', 'the Countess','Capt', 'Col', 'Don',
'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer')
# Also reassign mlle, ms, and mme accordingly
full$Title[full$Title == 'Mlle'] <- 'Miss'
full$Title[full$Title == 'Ms'] <- 'Miss'
full$Title[full$Title == 'Mme'] <- 'Mrs'
full$Title[full$Title %in% rare_title] <- 'Rare Title'
# Show title counts by sex again
table(full$Sex, full$Title)
Master Miss Mr Mrs Rare Title
female 0 264 0 198 4
male 61 0 757 0 25
2.Do family sink or swim together?
# Create a family size variable including the passenger themselves
full$Fsize <- full$SibSp + full$Parch + 1
# Create a family variable
full$Family <- paste(full$Surname, full$Fsize, sep='_')
What does our family size variable look like? To help us understand how it may relate to survival, let’s plot it among the training data.
# Use ggplot2 to visualize the relationship between family size & survival
ggplot(full[1:891,], aes(x = Fsize, fill = factor(Survived))) +
geom_bar(stat='count', position='dodge') +
scale_x_continuous(breaks=c(1:11)) +
labs(x = 'Family Size') +
theme_few()

We can see that there’s a survival penalty to singletons and those with family sizes above 4. We can collapse this variable into three levels which will be helpful since there are comparatively fewer large families. Let’s create a discretized family size variable.
# Discretize family size
full$FsizeD[full$Fsize == 1] <- 'singleton'
full$FsizeD[full$Fsize < 5 & full$Fsize > 1] <- 'small'
full$FsizeD[full$Fsize > 4] <- 'large'
# Show family size by survival using a mosaic plot
mosaicplot(table(full$FsizeD, full$Survived), main='Family Size by Survival', shade=TRUE)

3.Passenger Cabin
full$Cabin[1:28]
[1] "" "C85" "" "C123" "" "" "E46" "" "" "" "G6" "C103"
[13] "" "" "" "" "" "" "" "" "" "D56" "" "A6"
[25] "" "" "" "C23 C25 C27"
strsplit(full$Cabin[2], NULL)[[1]]
[1] "C" "8" "5"
Now we’re ready to start exploring missing data and rectifying it through imputation. There are a number of different ways we could go about doing this. Given the small size of the dataset, we probably should not opt for deleting either entire observations (rows) or variables (columns) containing missing values. We’re left with the option of either replacing missing values with a sensible values given the distribution of the data, e.g., the mean, median or mode. Finally, we could go with prediction. We’ll use both of the two latter methods and I’ll rely on some data visualization to guide our decisions.
Sensible value imputation |
# Passengers 62 and 830 are missing Embarkment
full[c(62, 830), 'Embarked']
[1] "" ""
cat(paste('We will infer their values for **embarkment** based on present data that we can imagine may be relevant: **passenger class** and **fare**. We see that they paid<b> $', full[c(62, 830), 'Fare'][[1]][1], '</b>and<b> $', full[c(62, 830), 'Fare'][[1]][2], '</b>respectively and their classes are<b>', full[c(62, 830), 'Pclass'][[1]][1], '</b>and<b>', full[c(62, 830), 'Pclass'][[1]][2], '</b>. So from where did they embark?'))
We will infer their values for **embarkment** based on present data that we can imagine may be relevant: **passenger class** and **fare**. We see that they paid<b> $ 80 </b>and<b> $ NA </b>respectively and their classes are<b> 1 </b>and<b> NA </b>. So from where did they embark?
# Get rid of our missing passenger IDs
embark_fare <- full %>%
filter(PassengerId != 62 & PassengerId != 830)
# Use ggplot2 to visualize embarkment, passenger class, & median fare
ggplot(embark_fare, aes(x = Embarked, y = Fare, fill = factor(Pclass))) +
geom_boxplot() +
geom_hline(aes(yintercept=80),
colour='red', linetype='dashed', lwd=2) +
scale_y_continuous(labels=dollar_format()) +
theme_few()

# Since their fare was $80 for 1st class, they most likely embarked from 'C'
full$Embarked[c(62, 830)] <- 'C'
This is a third class passenger who departed from Southampton (‘S’). Let’s visualize Fares among all others sharing their class and embarkment (n = 494).
ggplot(full[full$Pclass == '3' & full$Embarked == 'S', ],
aes(x = Fare)) +
geom_density(fill = '#99d6ff', alpha=0.4) +
geom_vline(aes(xintercept=median(Fare, na.rm=T)),
colour='red', linetype='dashed', lwd=1) +
scale_x_continuous(labels=dollar_format()) +
theme_few()

From this visualization, it seems quite reasonable to replace the NA Fare value with median for their class and embarkment which is $8.05.
# Replace missing fare value with median fare for class/embarkment
full$Fare[1044] <- median(full[full$Pclass == '3' & full$Embarked == 'S', ]$Fare, na.rm = TRUE)
Finally, as we noted earlier, there are quite a few missing Age values in our data. We are going to get a bit more fancy in imputing missing age values. Why? Because we can. We will create a model predicting ages based on other variables.
# Show number of missing Age values
sum(is.na(full$Age))
[1] 263
We could definitely use rpart (recursive partitioning for regression) to predict missing ages, but I’m going to use the mice package for this task just for something different. You can read more about multiple imputation using chained equations in r here (PDF). Since we haven’t done it yet, I’ll first factorize the factor variables and then perform mice imputation.
# Make variables factors into factors
factor_vars <- c('PassengerId','Pclass','Sex','Embarked',
'Title','Surname','Family','FsizeD')
full[factor_vars] <- lapply(full[factor_vars], function(x) as.factor(x))
# Set a random seed
set.seed(129)
# Perform mice imputation, excluding certain less-than-useful variables:
mice_mod <- mice(full[, !names(full) %in% c('PassengerId','Name','Ticket','Cabin','Family','Surname','Survived')], method='rf')
iter imp variable
1 1 Age Deck
1 2 Age Deck
1 3 Age Deck
1 4 Age Deck
1 5 Age Deck
2 1 Age Deck
2 2 Age Deck
2 3 Age Deck
2 4 Age Deck
2 5 Age Deck
3 1 Age Deck
3 2 Age Deck
3 3 Age Deck
3 4 Age Deck
3 5 Age Deck
4 1 Age Deck
4 2 Age Deck
4 3 Age Deck
4 4 Age Deck
4 5 Age Deck
5 1 Age Deck
5 2 Age Deck
5 3 Age Deck
5 4 Age Deck
5 5 Age Deck
# Save the complete output
mice_output <- complete(mice_mod)
Let’s compare the results we get with the original distribution of passenger ages to ensure that nothing has gone completely awry.
# Plot age distributions
par(mfrow=c(1,2))
hist(full$Age, freq=F, main='Age: Original Data',
col='darkgreen', ylim=c(0,0.04))
hist(mice_output$Age, freq=F, main='Age: MICE Output',
col='lightgreen', ylim=c(0,0.04))

Things look good, so let’s replace our age vector in the original data with the output from the mice model.
# Replace Age variable from the mice model.
full$Age <- mice_output$Age
# Show new number of missing Age values
sum(is.na(full$Age))
[1] 0
We’ve finished imputing values for all variables that we care about for now! Now that we have a complete Age variable, there are just a few finishing touches I’d like to make. We can use Age to do just a bit more feature engineering …
Now that we know everyone’s age, we can create a couple of new age-dependent variables: Child and Mother. A child will simply be someone under 18 years of age and a mother is a passenger who is 1) female, 2) is over 18, 3) has more than 0 children (no kidding!), and 4) does not have the title Miss.
#First we'll look at the relationship between age & survival
ggplot(full[1:891,], aes(Age, fill = factor(Survived))) +
geom_histogram() +
# I include Sex since we know (a priori) it's a significant predictor
facet_grid(.~Sex) +
theme_few()

# Create the column child, and indicate whether child or adult
full$Child[full$Age < 18] <- 'Child'
full$Child[full$Age >= 18] <- 'Adult'
# Show counts
table(full$Child, full$Survived)
Looks like being a child doesn’t hurt, but it’s not going to necessarily save you either! We will finish off our feature engineering by creating the Mother variable. Maybe we can hope that mothers are more likely to have survived on the Titanic.
# Adding Mother variable
full$Mother <- 'Not Mother'
full$Mother[full$Sex == 'female' & full$Parch > 0 & full$Age > 18 & full$Title != 'Miss'] <- 'Mother'
# Show counts
table(full$Mother, full$Survived)
# Finish by factorizing our two new factor variables
full$Child <- factor(full$Child)
full$Mother <- factor(full$Mother)
All of the variables we care about should be taken care of and there should be no missing data. I’m going to double check just to be sure:
md.pattern(full)
Wow! We have finally finished treating all of the relevant missing values in the Titanic dataset which has included some fancy imputation with mice. We have also successfully created several new variables which we hope will help us build a model which reliably predicts survival.
At last we’re ready to predict who survives among passengers of the Titanic based on variables that we carefully curated and treated for missing values. For this, we will rely on the randomForest classification algorithm; we spent all that time on imputation, after all.
Split into trianing and test data sets
# Split the data back into a train set and a test set
train <- full[1:891,]
test <- full[892:1309,]
Building model
# Set a random seed
set.seed(754)
# Build the model (note: not all possible variables are used)
rf_model <- randomForest(factor(Survived) ~ Pclass + Sex + Age + SibSp + Parch +
Fare + Embarked + Title +
FsizeD + Child + Mother,
data = train)
# Show model error
plot(rf_model, ylim=c(0,0.36))
legend('topright', colnames(rf_model$err.rate), col=1:3, fill=1:3)
The black line shows the overall error rate which falls below 20%. The red and green lines show the error rate for ‘died’ and ‘survived’ respectively. We can see that right now we’re much more successful predicting death than we are survival. What does that say about me, I wonder?
Let’s look at relative variable importance by plotting the mean decrease in Gini calculated across all trees.
# Get importance
importance <- importance(rf_model)
varImportance <- data.frame(Variables = row.names(importance),
Importance = round(importance[ ,'MeanDecreaseGini'],2))
# Create a rank variable based on importance
rankImportance <- varImportance %>%
mutate(Rank = paste0('#',dense_rank(desc(Importance))))
# Use ggplot2 to visualize the relative importance of variables
ggplot(rankImportance, aes(x = reorder(Variables, Importance),
y = Importance, fill = Importance)) +
geom_bar(stat='identity') +
geom_text(aes(x = Variables, y = 0.5, label = Rank),
hjust=0, vjust=0.55, size = 4, colour = 'red') +
labs(x = 'Variables') +
coord_flip() +
theme_few()
Whoa, glad we made our title variable! It has the highest relative importance out of all of our predictor variables. I think I’m most surprised to see that passenger class fell to 5, but maybe that’s just bias coming from watching the movie Titanic too many times as a kid.
We’re ready for the final step — making our prediction! When we finish here, we could iterate through the preceding steps making tweaks as we go or fit the data using different models or use different combinations of variables to achieve better predictions. But this is a good starting (and stopping) point for me now.
# Predict using the test set
prediction <- predict(rf_model, test)
# Save the solution to a dataframe with two columns: PassengerId and Survived (prediction)
solution <- data.frame(PassengerID = test$PassengerId, Survived = prediction)
# Write the solution to file
write.csv(solution, file = 'rf_mod_Solution.csv', row.names = F)
LS0tCnRpdGxlOiAiRXhwbG9yaW5nIFRpdGFuaWMgRGF0YXNldCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpMb2FkaW5nIGFuZCBjaGVjayBkYXRhCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KG1pY2UpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpgYGAKCkxldHMgcmVhZCB0aGUgZGF0YXNldApgYGB7cn0Kc2V0d2QoIi9Vc2Vycy92aWR5YXNhZ2FyYmhhcmdhdmEvRGVza3RvcC9LYWdnbGUiKQp0cmFpbjwtcmVhZC5jc3YoInRyYWluLmNzdiIpCnRlc3Q8LXJlYWQuY3N2KCJ0ZXN0LmNzdiIpCmZ1bGw8LWJpbmRfcm93cyh0cmFpbiwgdGVzdCkKc3RyKGZ1bGwpCmBgYApHZXR0aW5nIFN0YXJ0ZWQgd2l0aCBmZWF0dXJlIGVuZ2luZWVyaW5nLi4uCgoxLldoYXQgaXMgaW4gTmFtZT8KcGFzc2VuZ2VyIHRpdGxlIGlzIGNvbnRhaW5lZCB3aXRoaW4gdGhlIHBhc3NlbmdlciBuYW1lIHZhcmlhYmxlIGFuZCB3ZSBjYW4gdXNlIHN1cm5hbWUgdG8gcmVwcmVzZW50IGZhbWlsaWVzLgoKYGBge3J9CiNncmFiIHRpdGxlIGZyb20gcGFzc2VuZ2VyIG5hbWUKZnVsbCRUaXRsZTwtZ3N1YignKC4qLCApfChcXC4uKiknLCAnJywgZnVsbCROYW1lKQojIFNob3cgdGl0bGUgY291bnRzIGJ5IHNleAp0YWJsZShmdWxsJFNleCwgZnVsbCRUaXRsZSkKCmBgYApgYGB7cn0KIyBUaXRsZXMgd2l0aCB2ZXJ5IGxvdyBjZWxsIGNvdW50cyB0byBiZSBjb21iaW5lZCB0byAicmFyZSIgbGV2ZWwKcmFyZV90aXRsZSA8LSBjKCdEb25hJywgJ0xhZHknLCAndGhlIENvdW50ZXNzJywnQ2FwdCcsICdDb2wnLCAnRG9uJywgCiAgICAgICAgICAgICAgICAnRHInLCAnTWFqb3InLCAnUmV2JywgJ1NpcicsICdKb25raGVlcicpCiMgQWxzbyByZWFzc2lnbiBtbGxlLCBtcywgYW5kIG1tZSBhY2NvcmRpbmdseQpmdWxsJFRpdGxlW2Z1bGwkVGl0bGUgPT0gJ01sbGUnXSAgICAgICAgPC0gJ01pc3MnIApmdWxsJFRpdGxlW2Z1bGwkVGl0bGUgPT0gJ01zJ10gICAgICAgICAgPC0gJ01pc3MnCmZ1bGwkVGl0bGVbZnVsbCRUaXRsZSA9PSAnTW1lJ10gICAgICAgICA8LSAnTXJzJyAKZnVsbCRUaXRsZVtmdWxsJFRpdGxlICVpbiUgcmFyZV90aXRsZV0gIDwtICdSYXJlIFRpdGxlJwojIFNob3cgdGl0bGUgY291bnRzIGJ5IHNleCBhZ2Fpbgp0YWJsZShmdWxsJFNleCwgZnVsbCRUaXRsZSkKYGBgCgpgYGB7cn0KIyBGaW5hbGx5LCBncmFiIHN1cm5hbWUgZnJvbSBwYXNzZW5nZXIgbmFtZQpmdWxsJFN1cm5hbWUgPC0gc2FwcGx5KGZ1bGwkTmFtZSwgIAogICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgc3Ryc3BsaXQoeCwgc3BsaXQgPSAnWywuXScpW1sxXV1bMV0pCmBgYAoKCgoyLkRvIGZhbWlseSBzaW5rIG9yIHN3aW0gdG9nZXRoZXI/CgpgYGB7cn0KIyBDcmVhdGUgYSBmYW1pbHkgc2l6ZSB2YXJpYWJsZSBpbmNsdWRpbmcgdGhlIHBhc3NlbmdlciB0aGVtc2VsdmVzCmZ1bGwkRnNpemUgPC0gZnVsbCRTaWJTcCArIGZ1bGwkUGFyY2ggKyAxCgojIENyZWF0ZSBhIGZhbWlseSB2YXJpYWJsZSAKZnVsbCRGYW1pbHkgPC0gcGFzdGUoZnVsbCRTdXJuYW1lLCBmdWxsJEZzaXplLCBzZXA9J18nKQpgYGAKCldoYXQgZG9lcyBvdXIgZmFtaWx5IHNpemUgdmFyaWFibGUgbG9vayBsaWtlPyBUbyBoZWxwIHVzIHVuZGVyc3RhbmQgaG93IGl0IG1heSByZWxhdGUgdG8gc3Vydml2YWwsIGxldOKAmXMgcGxvdCBpdCBhbW9uZyB0aGUgdHJhaW5pbmcgZGF0YS4KYGBge3J9CiMgVXNlIGdncGxvdDIgdG8gdmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBmYW1pbHkgc2l6ZSAmIHN1cnZpdmFsCmdncGxvdChmdWxsWzE6ODkxLF0sIGFlcyh4ID0gRnNpemUsIGZpbGwgPSBmYWN0b3IoU3Vydml2ZWQpKSkgKwogIGdlb21fYmFyKHN0YXQ9J2NvdW50JywgcG9zaXRpb249J2RvZGdlJykgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9YygxOjExKSkgKwogIGxhYnMoeCA9ICdGYW1pbHkgU2l6ZScpICsKICB0aGVtZV9mZXcoKQpgYGAKCiBXZSBjYW4gc2VlIHRoYXQgdGhlcmXigJlzIGEgc3Vydml2YWwgcGVuYWx0eSB0byBzaW5nbGV0b25zIGFuZCB0aG9zZSB3aXRoIGZhbWlseSBzaXplcyBhYm92ZSA0LiBXZSBjYW4gY29sbGFwc2UgdGhpcyB2YXJpYWJsZSBpbnRvIHRocmVlIGxldmVscyB3aGljaCB3aWxsIGJlIGhlbHBmdWwgc2luY2UgdGhlcmUgYXJlIGNvbXBhcmF0aXZlbHkgZmV3ZXIgbGFyZ2UgZmFtaWxpZXMuIExldOKAmXMgY3JlYXRlIGEgZGlzY3JldGl6ZWQgZmFtaWx5IHNpemUgdmFyaWFibGUuCiAKYGBge3J9CiMgRGlzY3JldGl6ZSBmYW1pbHkgc2l6ZQpmdWxsJEZzaXplRFtmdWxsJEZzaXplID09IDFdIDwtICdzaW5nbGV0b24nCmZ1bGwkRnNpemVEW2Z1bGwkRnNpemUgPCA1ICYgZnVsbCRGc2l6ZSA+IDFdIDwtICdzbWFsbCcKZnVsbCRGc2l6ZURbZnVsbCRGc2l6ZSA+IDRdIDwtICdsYXJnZScKCiMgU2hvdyBmYW1pbHkgc2l6ZSBieSBzdXJ2aXZhbCB1c2luZyBhIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3QodGFibGUoZnVsbCRGc2l6ZUQsIGZ1bGwkU3Vydml2ZWQpLCBtYWluPSdGYW1pbHkgU2l6ZSBieSBTdXJ2aXZhbCcsIHNoYWRlPVRSVUUpCmBgYAoKMy5QYXNzZW5nZXIgQ2FiaW4KCmBgYHtyfQojIFRoaXMgdmFyaWFibGUgYXBwZWFycyB0byBoYXZlIGEgbG90IG9mIG1pc3NpbmcgdmFsdWVzCmZ1bGwkQ2FiaW5bMToyOF0KYGBgCgpgYGB7cn0KIyBUaGUgZmlyc3QgY2hhcmFjdGVyIGlzIHRoZSBkZWNrLiBGb3IgZXhhbXBsZToKc3Ryc3BsaXQoZnVsbCRDYWJpblsyXSwgTlVMTClbWzFdXQpgYGAKCgoKYGBge3J9CiMgQ3JlYXRlIGEgRGVjayB2YXJpYWJsZS4gR2V0IHBhc3NlbmdlciBkZWNrIEEgLSBGOgpmdWxsJERlY2s8LWZhY3RvcihzYXBwbHkoZnVsbCRDYWJpbiwgZnVuY3Rpb24oeCkgc3Ryc3BsaXQoeCwgTlVMTClbWzFdXVsxXSkpCmBgYAoKLS0tCk1pc3NpbmduZXNzCi0tLQoKTm93IHdl4oCZcmUgcmVhZHkgdG8gc3RhcnQgZXhwbG9yaW5nIG1pc3NpbmcgZGF0YSBhbmQgcmVjdGlmeWluZyBpdCB0aHJvdWdoIGltcHV0YXRpb24uIFRoZXJlIGFyZSBhIG51bWJlciBvZiBkaWZmZXJlbnQgd2F5cyB3ZSBjb3VsZCBnbyBhYm91dCBkb2luZyB0aGlzLiBHaXZlbiB0aGUgc21hbGwgc2l6ZSBvZiB0aGUgZGF0YXNldCwgd2UgcHJvYmFibHkgc2hvdWxkIG5vdCBvcHQgZm9yIGRlbGV0aW5nIGVpdGhlciBlbnRpcmUgb2JzZXJ2YXRpb25zIChyb3dzKSBvciB2YXJpYWJsZXMgKGNvbHVtbnMpIGNvbnRhaW5pbmcgbWlzc2luZyB2YWx1ZXMuIFdl4oCZcmUgbGVmdCB3aXRoIHRoZSBvcHRpb24gb2YgZWl0aGVyIHJlcGxhY2luZyBtaXNzaW5nIHZhbHVlcyB3aXRoIGEgc2Vuc2libGUgdmFsdWVzIGdpdmVuIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGRhdGEsIGUuZy4sIHRoZSBtZWFuLCBtZWRpYW4gb3IgbW9kZS4gRmluYWxseSwgd2UgY291bGQgZ28gd2l0aCBwcmVkaWN0aW9uLiBXZeKAmWxsIHVzZSBib3RoIG9mIHRoZSB0d28gbGF0dGVyIG1ldGhvZHMgYW5kIEnigJlsbCByZWx5IG9uIHNvbWUgZGF0YSB2aXN1YWxpemF0aW9uIHRvIGd1aWRlIG91ciBkZWNpc2lvbnMuCgotLS0tClNlbnNpYmxlIHZhbHVlIGltcHV0YXRpb24KLS0tLQpgYGB7cn0KIyBQYXNzZW5nZXJzIDYyIGFuZCA4MzAgYXJlIG1pc3NpbmcgRW1iYXJrbWVudApmdWxsW2MoNjIsIDgzMCksICdFbWJhcmtlZCddCmBgYApgYGB7cn0KY2F0KHBhc3RlKCdXZSB3aWxsIGluZmVyIHRoZWlyIHZhbHVlcyBmb3IgKiplbWJhcmttZW50KiogYmFzZWQgb24gcHJlc2VudCBkYXRhIHRoYXQgd2UgY2FuIGltYWdpbmUgbWF5IGJlIHJlbGV2YW50OiAqKnBhc3NlbmdlciBjbGFzcyoqIGFuZCAqKmZhcmUqKi4gV2Ugc2VlIHRoYXQgdGhleSBwYWlkPGI+ICQnLCBmdWxsW2MoNjIsIDgzMCksICdGYXJlJ11bWzFdXVsxXSwgJzwvYj5hbmQ8Yj4gJCcsIGZ1bGxbYyg2MiwgODMwKSwgJ0ZhcmUnXVtbMV1dWzJdLCAnPC9iPnJlc3BlY3RpdmVseSBhbmQgdGhlaXIgY2xhc3NlcyBhcmU8Yj4nLCBmdWxsW2MoNjIsIDgzMCksICdQY2xhc3MnXVtbMV1dWzFdLCAnPC9iPmFuZDxiPicsIGZ1bGxbYyg2MiwgODMwKSwgJ1BjbGFzcyddW1sxXV1bMl0sICc8L2I+LiBTbyBmcm9tIHdoZXJlIGRpZCB0aGV5IGVtYmFyaz8nKSkKYGBgCgpgYGB7cn0KIyBHZXQgcmlkIG9mIG91ciBtaXNzaW5nIHBhc3NlbmdlciBJRHMKZW1iYXJrX2ZhcmUgPC0gZnVsbCAlPiUKICBmaWx0ZXIoUGFzc2VuZ2VySWQgIT0gNjIgJiBQYXNzZW5nZXJJZCAhPSA4MzApCgojIFVzZSBnZ3Bsb3QyIHRvIHZpc3VhbGl6ZSBlbWJhcmttZW50LCBwYXNzZW5nZXIgY2xhc3MsICYgbWVkaWFuIGZhcmUKZ2dwbG90KGVtYmFya19mYXJlLCBhZXMoeCA9IEVtYmFya2VkLCB5ID0gRmFyZSwgZmlsbCA9IGZhY3RvcihQY2xhc3MpKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PTgwKSwgCiAgICBjb2xvdXI9J3JlZCcsIGxpbmV0eXBlPSdkYXNoZWQnLCBsd2Q9MikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9ZG9sbGFyX2Zvcm1hdCgpKSArCiAgdGhlbWVfZmV3KCkKYGBgCgpgYGB7cn0KIyBTaW5jZSB0aGVpciBmYXJlIHdhcyAkODAgZm9yIDFzdCBjbGFzcywgdGhleSBtb3N0IGxpa2VseSBlbWJhcmtlZCBmcm9tICdDJwpmdWxsJEVtYmFya2VkW2MoNjIsIDgzMCldIDwtICdDJwpgYGAKCgpgYGB7cn0KIyBTaG93IHJvdyAxMDQ0CmZ1bGxbMTA0NCwgXQpgYGAKCgpUaGlzIGlzIGEgdGhpcmQgY2xhc3MgcGFzc2VuZ2VyIHdobyBkZXBhcnRlZCBmcm9tIFNvdXRoYW1wdG9uICjigJhT4oCZKS4gTGV04oCZcyB2aXN1YWxpemUgRmFyZXMgYW1vbmcgYWxsIG90aGVycyBzaGFyaW5nIHRoZWlyIGNsYXNzIGFuZCBlbWJhcmttZW50IChuID0gNDk0KS4KYGBge3J9CmdncGxvdChmdWxsW2Z1bGwkUGNsYXNzID09ICczJyAmIGZ1bGwkRW1iYXJrZWQgPT0gJ1MnLCBdLCAKICBhZXMoeCA9IEZhcmUpKSArCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAnIzk5ZDZmZicsIGFscGhhPTAuNCkgKyAKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PW1lZGlhbihGYXJlLCBuYS5ybT1UKSksCiAgICBjb2xvdXI9J3JlZCcsIGxpbmV0eXBlPSdkYXNoZWQnLCBsd2Q9MSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9ZG9sbGFyX2Zvcm1hdCgpKSArCiAgdGhlbWVfZmV3KCkKYGBgCgpGcm9tIHRoaXMgdmlzdWFsaXphdGlvbiwgaXQgc2VlbXMgcXVpdGUgcmVhc29uYWJsZSB0byByZXBsYWNlIHRoZSBOQSBGYXJlIHZhbHVlIHdpdGggbWVkaWFuIGZvciB0aGVpciBjbGFzcyBhbmQgZW1iYXJrbWVudCB3aGljaCBpcyAkOC4wNS4KCmBgYHtyfQojIFJlcGxhY2UgbWlzc2luZyBmYXJlIHZhbHVlIHdpdGggbWVkaWFuIGZhcmUgZm9yIGNsYXNzL2VtYmFya21lbnQKZnVsbCRGYXJlWzEwNDRdIDwtIG1lZGlhbihmdWxsW2Z1bGwkUGNsYXNzID09ICczJyAmIGZ1bGwkRW1iYXJrZWQgPT0gJ1MnLCBdJEZhcmUsIG5hLnJtID0gVFJVRSkKYGBgCgotLS0KUHJlZGljdGl2ZSBJbXB1dGF0aW9uCi0tLQpGaW5hbGx5LCBhcyB3ZSBub3RlZCBlYXJsaWVyLCB0aGVyZSBhcmUgcXVpdGUgYSBmZXcgbWlzc2luZyBBZ2UgdmFsdWVzIGluIG91ciBkYXRhLiBXZSBhcmUgZ29pbmcgdG8gZ2V0IGEgYml0IG1vcmUgZmFuY3kgaW4gaW1wdXRpbmcgbWlzc2luZyBhZ2UgdmFsdWVzLiBXaHk/IEJlY2F1c2Ugd2UgY2FuLiBXZSB3aWxsIGNyZWF0ZSBhIG1vZGVsIHByZWRpY3RpbmcgYWdlcyBiYXNlZCBvbiBvdGhlciB2YXJpYWJsZXMuCgpgYGB7cn0KIyBTaG93IG51bWJlciBvZiBtaXNzaW5nIEFnZSB2YWx1ZXMKc3VtKGlzLm5hKGZ1bGwkQWdlKSkKYGBgCgpXZSBjb3VsZCBkZWZpbml0ZWx5IHVzZSBycGFydCAocmVjdXJzaXZlIHBhcnRpdGlvbmluZyBmb3IgcmVncmVzc2lvbikgdG8gcHJlZGljdCBtaXNzaW5nIGFnZXMsIGJ1dCBJ4oCZbSBnb2luZyB0byB1c2UgdGhlICBtaWNlIHBhY2thZ2UgZm9yIHRoaXMgdGFzayBqdXN0IGZvciBzb21ldGhpbmcgZGlmZmVyZW50LiAKWW91IGNhbiByZWFkIG1vcmUgYWJvdXQgbXVsdGlwbGUgaW1wdXRhdGlvbiB1c2luZyBjaGFpbmVkIGVxdWF0aW9ucyBpbiByIGhlcmUgKFBERikuIFNpbmNlIHdlIGhhdmVu4oCZdCBkb25lIGl0IHlldCwgSeKAmWxsIGZpcnN0IGZhY3Rvcml6ZSB0aGUgZmFjdG9yIHZhcmlhYmxlcyBhbmQgdGhlbiBwZXJmb3JtIG1pY2UKaW1wdXRhdGlvbi4KCgpgYGB7cn0KIyBNYWtlIHZhcmlhYmxlcyBmYWN0b3JzIGludG8gZmFjdG9ycwpmYWN0b3JfdmFycyA8LSBjKCdQYXNzZW5nZXJJZCcsJ1BjbGFzcycsJ1NleCcsJ0VtYmFya2VkJywKICAgICAgICAgICAgICAgICAnVGl0bGUnLCdTdXJuYW1lJywnRmFtaWx5JywnRnNpemVEJykKCmZ1bGxbZmFjdG9yX3ZhcnNdIDwtIGxhcHBseShmdWxsW2ZhY3Rvcl92YXJzXSwgZnVuY3Rpb24oeCkgYXMuZmFjdG9yKHgpKQoKIyBTZXQgYSByYW5kb20gc2VlZApzZXQuc2VlZCgxMjkpCgojIFBlcmZvcm0gbWljZSBpbXB1dGF0aW9uLCBleGNsdWRpbmcgY2VydGFpbiBsZXNzLXRoYW4tdXNlZnVsIHZhcmlhYmxlczoKbWljZV9tb2QgPC0gbWljZShmdWxsWywgIW5hbWVzKGZ1bGwpICVpbiUgYygnUGFzc2VuZ2VySWQnLCdOYW1lJywnVGlja2V0JywnQ2FiaW4nLCdGYW1pbHknLCdTdXJuYW1lJywnU3Vydml2ZWQnKV0sIG1ldGhvZD0ncmYnKSAKYGBgCgpgYGB7cn0KIyBTYXZlIHRoZSBjb21wbGV0ZSBvdXRwdXQgCm1pY2Vfb3V0cHV0IDwtIGNvbXBsZXRlKG1pY2VfbW9kKQpgYGAKCgoKTGV04oCZcyBjb21wYXJlIHRoZSByZXN1bHRzIHdlIGdldCB3aXRoIHRoZSBvcmlnaW5hbCBkaXN0cmlidXRpb24gb2YgcGFzc2VuZ2VyIGFnZXMgdG8gZW5zdXJlIHRoYXQgbm90aGluZyBoYXMgZ29uZSBjb21wbGV0ZWx5IGF3cnkuCmBgYHtyfQojIFBsb3QgYWdlIGRpc3RyaWJ1dGlvbnMKcGFyKG1mcm93PWMoMSwyKSkKaGlzdChmdWxsJEFnZSwgZnJlcT1GLCBtYWluPSdBZ2U6IE9yaWdpbmFsIERhdGEnLCAKICBjb2w9J2RhcmtncmVlbicsIHlsaW09YygwLDAuMDQpKQpoaXN0KG1pY2Vfb3V0cHV0JEFnZSwgZnJlcT1GLCBtYWluPSdBZ2U6IE1JQ0UgT3V0cHV0JywgCiAgY29sPSdsaWdodGdyZWVuJywgeWxpbT1jKDAsMC4wNCkpCmBgYAoKClRoaW5ncyBsb29rIGdvb2QsIHNvIGxldOKAmXMgcmVwbGFjZSBvdXIgYWdlIHZlY3RvciBpbiB0aGUgb3JpZ2luYWwgZGF0YSB3aXRoIHRoZSBvdXRwdXQgZnJvbSB0aGUgbWljZSBtb2RlbC4KCgpgYGB7cn0KIyBSZXBsYWNlIEFnZSB2YXJpYWJsZSBmcm9tIHRoZSBtaWNlIG1vZGVsLgpmdWxsJEFnZSA8LSBtaWNlX291dHB1dCRBZ2UKCiMgU2hvdyBuZXcgbnVtYmVyIG9mIG1pc3NpbmcgQWdlIHZhbHVlcwpzdW0oaXMubmEoZnVsbCRBZ2UpKQpgYGAKV2XigJl2ZSBmaW5pc2hlZCBpbXB1dGluZyB2YWx1ZXMgZm9yIGFsbCB2YXJpYWJsZXMgdGhhdCB3ZSBjYXJlIGFib3V0IGZvciBub3chIE5vdyB0aGF0IHdlIGhhdmUgYSBjb21wbGV0ZSBBZ2UgdmFyaWFibGUsIHRoZXJlIGFyZSBqdXN0IGEgZmV3IGZpbmlzaGluZyB0b3VjaGVzIEnigJlkIGxpa2UgdG8gbWFrZS4gV2UgY2FuCnVzZSBBZ2UgdG8gZG8ganVzdCBhIGJpdCBtb3JlIGZlYXR1cmUgZW5naW5lZXJpbmcg4oCmCgotLS0KRmVhdHVyZSBFbmdpbmVlcmluZyBSb3VuZCAyCi0tLQpOb3cgdGhhdCB3ZSBrbm93IGV2ZXJ5b25l4oCZcyBhZ2UsIHdlIGNhbiBjcmVhdGUgYSBjb3VwbGUgb2YgbmV3IGFnZS1kZXBlbmRlbnQgdmFyaWFibGVzOiBDaGlsZCBhbmQgTW90aGVyLiAKQSBjaGlsZCB3aWxsIHNpbXBseSBiZSBzb21lb25lIHVuZGVyIDE4IHllYXJzIG9mIGFnZSBhbmQgYSBtb3RoZXIgaXMgYQpwYXNzZW5nZXIgd2hvIGlzIDEpIGZlbWFsZSwgMikgaXMgb3ZlciAxOCwgMykgaGFzIG1vcmUgdGhhbiAwIGNoaWxkcmVuIChubyBraWRkaW5nISksIGFuZCA0KSBkb2VzIG5vdCBoYXZlIHRoZSB0aXRsZSBNaXNzLgoKYGBge3J9CiMgRmlyc3Qgd2UnbGwgbG9vayBhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYWdlICYgc3Vydml2YWwKCmdncGxvdChmdWxsWzE6ODkxLF0sIGFlcyhBZ2UsIGZpbGwgPSBmYWN0b3IoU3Vydml2ZWQpKSkgKyAKICBnZW9tX2hpc3RvZ3JhbSgpICsgCiAgIyBJIGluY2x1ZGUgU2V4IHNpbmNlIHdlIGtub3cgKGEgcHJpb3JpKSBpdCdzIGEgc2lnbmlmaWNhbnQgcHJlZGljdG9yCiAgZmFjZXRfZ3JpZCguflNleCkgKyAKICB0aGVtZV9mZXcoKQpgYGAKCgpgYGB7cn0KIyBDcmVhdGUgdGhlIGNvbHVtbiBjaGlsZCwgYW5kIGluZGljYXRlIHdoZXRoZXIgY2hpbGQgb3IgYWR1bHQKZnVsbCRDaGlsZFtmdWxsJEFnZSA8IDE4XSA8LSAnQ2hpbGQnCmZ1bGwkQ2hpbGRbZnVsbCRBZ2UgPj0gMThdIDwtICdBZHVsdCcKCiMgU2hvdyBjb3VudHMKdGFibGUoZnVsbCRDaGlsZCwgZnVsbCRTdXJ2aXZlZCkKYGBgCgoKTG9va3MgbGlrZSBiZWluZyBhIGNoaWxkIGRvZXNu4oCZdCBodXJ0LCBidXQgaXTigJlzIG5vdCBnb2luZyB0byBuZWNlc3NhcmlseSBzYXZlIHlvdSBlaXRoZXIhIFdlIHdpbGwgZmluaXNoIG9mZiBvdXIgZmVhdHVyZSBlbmdpbmVlcmluZyBieSBjcmVhdGluZyB0aGUgTW90aGVyIHZhcmlhYmxlLiBNYXliZSB3ZSBjYW4KaG9wZSB0aGF0IG1vdGhlcnMgYXJlIG1vcmUgbGlrZWx5IHRvIGhhdmUgc3Vydml2ZWQgb24gdGhlIFRpdGFuaWMuCgoKYGBge3J9CiMgQWRkaW5nIE1vdGhlciB2YXJpYWJsZQpmdWxsJE1vdGhlciA8LSAnTm90IE1vdGhlcicKZnVsbCRNb3RoZXJbZnVsbCRTZXggPT0gJ2ZlbWFsZScgJiBmdWxsJFBhcmNoID4gMCAmIGZ1bGwkQWdlID4gMTggJiBmdWxsJFRpdGxlICE9ICdNaXNzJ10gPC0gJ01vdGhlcicKCiMgU2hvdyBjb3VudHMKdGFibGUoZnVsbCRNb3RoZXIsIGZ1bGwkU3Vydml2ZWQpCmBgYAoKYGBge3J9CiMgRmluaXNoIGJ5IGZhY3Rvcml6aW5nIG91ciB0d28gbmV3IGZhY3RvciB2YXJpYWJsZXMKZnVsbCRDaGlsZCAgPC0gZmFjdG9yKGZ1bGwkQ2hpbGQpCmZ1bGwkTW90aGVyIDwtIGZhY3RvcihmdWxsJE1vdGhlcikKYGBgCgpBbGwgb2YgdGhlIHZhcmlhYmxlcyB3ZSBjYXJlIGFib3V0IHNob3VsZCBiZSB0YWtlbiBjYXJlIG9mIGFuZCB0aGVyZSBzaG91bGQgYmUgbm8gbWlzc2luZyBkYXRhLiBJ4oCZbSBnb2luZyB0byBkb3VibGUgY2hlY2sganVzdCB0byBiZSBzdXJlOgoKYGBge3J9Cm1kLnBhdHRlcm4oZnVsbCkKYGBgCldvdyEgV2UgaGF2ZSBmaW5hbGx5IGZpbmlzaGVkIHRyZWF0aW5nIGFsbCBvZiB0aGUgcmVsZXZhbnQgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIFRpdGFuaWMgZGF0YXNldCB3aGljaCBoYXMgaW5jbHVkZWQgc29tZSBmYW5jeSBpbXB1dGF0aW9uIHdpdGggbWljZS4gV2UgaGF2ZSBhbHNvIHN1Y2Nlc3NmdWxseSBjcmVhdGVkCnNldmVyYWwgbmV3IHZhcmlhYmxlcyB3aGljaCB3ZSBob3BlIHdpbGwgaGVscCB1cyBidWlsZCBhIG1vZGVsIHdoaWNoIHJlbGlhYmx5IHByZWRpY3RzIHN1cnZpdmFsLgoKLS0tClByZWRpY3Rpb24KLS0tCkF0IGxhc3Qgd2XigJlyZSByZWFkeSB0byBwcmVkaWN0IHdobyBzdXJ2aXZlcyBhbW9uZyBwYXNzZW5nZXJzIG9mIHRoZSBUaXRhbmljIGJhc2VkIG9uIHZhcmlhYmxlcyB0aGF0IHdlIGNhcmVmdWxseSBjdXJhdGVkIGFuZCB0cmVhdGVkIGZvciBtaXNzaW5nIHZhbHVlcy4gRm9yIHRoaXMsIHdlIHdpbGwgcmVseSBvbiB0aGUKcmFuZG9tRm9yZXN0IGNsYXNzaWZpY2F0aW9uIGFsZ29yaXRobTsgd2Ugc3BlbnQgYWxsIHRoYXQgdGltZSBvbiBpbXB1dGF0aW9uLCBhZnRlciBhbGwuCgpTcGxpdCBpbnRvIHRyaWFuaW5nIGFuZCB0ZXN0IGRhdGEgc2V0cwoKYGBge3J9CiMgU3BsaXQgdGhlIGRhdGEgYmFjayBpbnRvIGEgdHJhaW4gc2V0IGFuZCBhIHRlc3Qgc2V0CnRyYWluIDwtIGZ1bGxbMTo4OTEsXQp0ZXN0IDwtIGZ1bGxbODkyOjEzMDksXQpgYGAKCkJ1aWxkaW5nIG1vZGVsCmBgYHtyfQojIFNldCBhIHJhbmRvbSBzZWVkCnNldC5zZWVkKDc1NCkKCiMgQnVpbGQgdGhlIG1vZGVsIChub3RlOiBub3QgYWxsIHBvc3NpYmxlIHZhcmlhYmxlcyBhcmUgdXNlZCkKcmZfbW9kZWwgPC0gcmFuZG9tRm9yZXN0KGZhY3RvcihTdXJ2aXZlZCkgfiBQY2xhc3MgKyBTZXggKyBBZ2UgKyBTaWJTcCArIFBhcmNoICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmFyZSArIEVtYmFya2VkICsgVGl0bGUgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGc2l6ZUQgKyBDaGlsZCArIE1vdGhlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW4pCgojIFNob3cgbW9kZWwgZXJyb3IKcGxvdChyZl9tb2RlbCwgeWxpbT1jKDAsMC4zNikpCmxlZ2VuZCgndG9wcmlnaHQnLCBjb2xuYW1lcyhyZl9tb2RlbCRlcnIucmF0ZSksIGNvbD0xOjMsIGZpbGw9MTozKQpgYGAKCgpUaGUgYmxhY2sgbGluZSBzaG93cyB0aGUgb3ZlcmFsbCBlcnJvciByYXRlIHdoaWNoIGZhbGxzIGJlbG93IDIwJS4gVGhlIHJlZCBhbmQgZ3JlZW4gbGluZXMgc2hvdyB0aGUgZXJyb3IgcmF0ZSBmb3Ig4oCYZGllZOKAmSBhbmQg4oCYc3Vydml2ZWTigJkgcmVzcGVjdGl2ZWx5LiBXZSBjYW4gc2VlIHRoYXQgcmlnaHQgbm93IHdl4oCZcmUKbXVjaCBtb3JlIHN1Y2Nlc3NmdWwgcHJlZGljdGluZyBkZWF0aCB0aGFuIHdlIGFyZSBzdXJ2aXZhbC4gV2hhdCBkb2VzIHRoYXQgc2F5IGFib3V0IG1lLCBJIHdvbmRlcj8KCgotLS0tClZhcmlhYmxlIEltcG9ydGFuY2UKLS0tLQpMZXTigJlzIGxvb2sgYXQgcmVsYXRpdmUgdmFyaWFibGUgaW1wb3J0YW5jZSBieSBwbG90dGluZyB0aGUgbWVhbiBkZWNyZWFzZSBpbiBHaW5pIGNhbGN1bGF0ZWQgYWNyb3NzIGFsbCB0cmVlcy4KCmBgYHtyfQojIEdldCBpbXBvcnRhbmNlCmltcG9ydGFuY2UgICAgPC0gaW1wb3J0YW5jZShyZl9tb2RlbCkKdmFySW1wb3J0YW5jZSA8LSBkYXRhLmZyYW1lKFZhcmlhYmxlcyA9IHJvdy5uYW1lcyhpbXBvcnRhbmNlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbXBvcnRhbmNlID0gcm91bmQoaW1wb3J0YW5jZVsgLCdNZWFuRGVjcmVhc2VHaW5pJ10sMikpCgojIENyZWF0ZSBhIHJhbmsgdmFyaWFibGUgYmFzZWQgb24gaW1wb3J0YW5jZQpyYW5rSW1wb3J0YW5jZSA8LSB2YXJJbXBvcnRhbmNlICU+JQogIG11dGF0ZShSYW5rID0gcGFzdGUwKCcjJyxkZW5zZV9yYW5rKGRlc2MoSW1wb3J0YW5jZSkpKSkKCiMgVXNlIGdncGxvdDIgdG8gdmlzdWFsaXplIHRoZSByZWxhdGl2ZSBpbXBvcnRhbmNlIG9mIHZhcmlhYmxlcwpnZ3Bsb3QocmFua0ltcG9ydGFuY2UsIGFlcyh4ID0gcmVvcmRlcihWYXJpYWJsZXMsIEltcG9ydGFuY2UpLCAKICAgIHkgPSBJbXBvcnRhbmNlLCBmaWxsID0gSW1wb3J0YW5jZSkpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgCiAgZ2VvbV90ZXh0KGFlcyh4ID0gVmFyaWFibGVzLCB5ID0gMC41LCBsYWJlbCA9IFJhbmspLAogICAgaGp1c3Q9MCwgdmp1c3Q9MC41NSwgc2l6ZSA9IDQsIGNvbG91ciA9ICdyZWQnKSArCiAgbGFicyh4ID0gJ1ZhcmlhYmxlcycpICsKICBjb29yZF9mbGlwKCkgKyAKICB0aGVtZV9mZXcoKQpgYGAKCldob2EsIGdsYWQgd2UgbWFkZSBvdXIgdGl0bGUgdmFyaWFibGUhIEl0IGhhcyB0aGUgaGlnaGVzdCByZWxhdGl2ZSBpbXBvcnRhbmNlIG91dCBvZiBhbGwgb2Ygb3VyIHByZWRpY3RvciB2YXJpYWJsZXMuIEkgdGhpbmsgSeKAmW0gbW9zdCBzdXJwcmlzZWQgdG8gc2VlIHRoYXQgcGFzc2VuZ2VyIGNsYXNzIGZlbGwgdG8KNSwgYnV0IG1heWJlIHRoYXTigJlzIGp1c3QgYmlhcyBjb21pbmcgZnJvbSB3YXRjaGluZyB0aGUgbW92aWUgVGl0YW5pYyB0b28gbWFueSB0aW1lcyBhcyBhIGtpZC4KCgpXZeKAmXJlIHJlYWR5IGZvciB0aGUgZmluYWwgc3RlcCDigJQgbWFraW5nIG91ciBwcmVkaWN0aW9uISBXaGVuIHdlIGZpbmlzaCBoZXJlLCB3ZSBjb3VsZCBpdGVyYXRlIHRocm91Z2ggdGhlIHByZWNlZGluZyBzdGVwcyBtYWtpbmcgdHdlYWtzIGFzIHdlIGdvIG9yIGZpdCB0aGUgZGF0YSB1c2luZyBkaWZmZXJlbnQKbW9kZWxzIG9yIHVzZSBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIHZhcmlhYmxlcyB0byBhY2hpZXZlIGJldHRlciBwcmVkaWN0aW9ucy4gQnV0IHRoaXMgaXMgYSBnb29kIHN0YXJ0aW5nIChhbmQgc3RvcHBpbmcpIHBvaW50IGZvciBtZSBub3cuCgpgYGB7cn0KIyBQcmVkaWN0IHVzaW5nIHRoZSB0ZXN0IHNldApwcmVkaWN0aW9uIDwtIHByZWRpY3QocmZfbW9kZWwsIHRlc3QpCgojIFNhdmUgdGhlIHNvbHV0aW9uIHRvIGEgZGF0YWZyYW1lIHdpdGggdHdvIGNvbHVtbnM6IFBhc3NlbmdlcklkIGFuZCBTdXJ2aXZlZCAocHJlZGljdGlvbikKc29sdXRpb24gPC0gZGF0YS5mcmFtZShQYXNzZW5nZXJJRCA9IHRlc3QkUGFzc2VuZ2VySWQsIFN1cnZpdmVkID0gcHJlZGljdGlvbikKCiMgV3JpdGUgdGhlIHNvbHV0aW9uIHRvIGZpbGUKd3JpdGUuY3N2KHNvbHV0aW9uLCBmaWxlID0gJ3JmX21vZF9Tb2x1dGlvbi5jc3YnLCByb3cubmFtZXMgPSBGKQpgYGAKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCg==