CONTENT



INTRODUCTION





The sinking of the RMS Titanic is one of the most infamous shipwrecks in history. On April 15, 1912, during her maiden voyage, the Titanic sank after colliding with an iceberg, killing 1502 out of 2224 passengers and crew. This sensational tragedy shocked the international community and led to better safety regulations for ships.

One of the reasons that the shipwreck led to such loss of life was that there were not enough lifeboats for the passengers and crew. Although there was some element of luck involved in surviving the sinking, some groups of people were more likely to survive than others, such as women, children, and the upper-class.

Some of the images of the disaster are shown below:



 

 



In this I done the analysis on titanic dataset and applied predictive analysis to predict the survival of pessengers.



IMPORTING LIBRARIES AND LOADING DATA


# Importing Packages
library(ggplot2)       # Visualization
library(ggthemes)      # Visualization
library(scales)        # Visualization
library(dplyr)         # Data Manipulation
library(mice)          # Imputation
library(randomForest)  # Classification Algorithm


# Loading Data
train <- read.csv("train.csv", stringsAsFactors = F)
test <- read.csv("test.csv", stringsAsFactors = F)
full <- bind_rows(train, test)     # Bind training and test data



DATA INSPECTION


glimpse(full)
Observations: 1,309
Variables: 12
$ PassengerId <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 1...
$ Survived    <int> 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1...
$ Pclass      <int> 3, 1, 3, 1, 3, 3, 1, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 2, 3, 3, 2, 2...
$ Name        <chr> "Braund, Mr. Owen Harris", "Cumings, Mrs. John Bradley (Florence...
$ Sex         <chr> "male", "female", "female", "female", "male", "male", "male", "m...
$ Age         <dbl> 22, 38, 26, 35, 35, NA, 54, 2, 27, 14, 4, 58, 20, 39, 14, 55, 2,...
$ SibSp       <int> 1, 1, 0, 1, 0, 0, 0, 3, 0, 1, 1, 0, 0, 1, 0, 0, 4, 0, 1, 0, 0, 0...
$ Parch       <int> 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 5, 0, 0, 1, 0, 0, 0, 0, 0...
$ Ticket      <chr> "A/5 21171", "PC 17599", "STON/O2. 3101282", "113803", "373450",...
$ Fare        <dbl> 7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 8.4583, 51.8625, 21.07...
$ Cabin       <chr> "", "C85", "", "C123", "", "", "E46", "", "", "", "G6", "C103", ...
$ Embarked    <chr> "S", "C", "S", "S", "S", "Q", "S", "S", "S", "C", "S", "S", "S",...


Here we see that we have 1309 observations of 12 variables with their class type and the first few observations.

Variables description:

  • PassengerID : ID of the Passenger.
  • Survived : No (0) Yes (1).
  • Pclass : Passenger’s class.
  • Name : Name of the Passenger.
  • Sex : Gender of the Passenger.
  • SibSp : Number of Siblings / Spouses aboard.
  • Parch : Number of Parents / Children aboard.
  • Ticket : Ticket number.
  • Fare : Fare of the Ticket.
  • Cabin : Cabin number.
  • Embarked : Port of embarkation.



FEATURE ENGINEERING


WHAT’S IN A NAME?


In this data we can break variable Name into meaningful variables. First we extract Passenger Title from the Passenger Name.

# Grab Title from the Passenger Names
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
  female    0   0   0    1   1        0    1     0      0  260    2   1   0 197   2   0
  male      1   4   1    0   7        1    0     2     61    0    0   0 757   0   0   8
        
         Sir the Countess
  female   0            1
  male     1            0
# Title with very low cell counts to be combined to "rare" Level
rareTitle <- c("Dona", "Lady","the Countess", "Capt", "Col", "Don", "Dr", "Major", "Rev", "Sir", "Jonkheer")
# Also reassign "Mlle", "Mme" and "Ms" accordingly
full$Title[full$Title == "Mlle"] <- "Miss"
full$Title[full$Title == "Ms"] <- "Miss"
full$Title[full$Title == "Mme"] <- "Mrs"
full$Title[full$Title %in% rareTitle] <- "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


Finally we will extract Surnames from the Passenger Names.

full$Surnames <- sapply(full$Name, function(x) strsplit(x, split = "[., ]")[[1]][1])
count <-  nlevels(factor(full$Surnames))
count
[1] 866


We have 866 unique surnames.


DO FAMILIES SINK OR SWIM TOGETHER?


Now we will create the Family Size variable based on number of siblings / spouse(s) (may be someone has more than one spouse!!) and number of parents / children.

# Create a Family Size variable including the passenger themselves
full$Fsize <- full$SibSp + full$Parch + 1
# Create a Family variable
full$Family <- paste(full$Surnames, full$Fsize, sep = "_")


Let us look upon the variable Family Size and how it may relates to Survival with the help of visualization.

# Relationship between Family Size and Survival (Training Data)
ggplot(data = full[1:891,], aes(x = Fsize, fill = factor(Survived))) + 
  geom_bar(stat = "count", position = "dodge") + 
  scale_x_continuous(breaks = c(1:11)) + 
  ylim(c(0,400)) +
  labs(x = "Family Size") + 
  theme_bw()


Here we see that sigletons are more in comparision then others and they survived more and died too as compare to others.

Now we create discretized family size variable.

# Discretized family size
full$FsizeD[full$Fsize == 1] <- "Singleton"
full$FsizeD[full$Fsize < 5 & full$Fsize > 1] <- "Small"
full$FsizeD[full$Fsize >= 5] <- "Large"
# Show Family size by Survival using a Mosaic plot
mosaicplot(table(full$FsizeD, full$Survived), main = "Family Size by Survival", shade = TRUE)

This plot shows the relationship between Family size and Survival.


TREATING FEW MORE VARIABLES…


Now we will treate variable Passenger Cabin which has potentially useful information such as their Deck.

Let’s have a look….

# This variable appears to have a lot of missing values
full$Cabin[1:28]
 [1] ""            "C85"         ""            "C123"        ""            ""           
 [7] "E46"         ""            ""            ""            "G6"          "C103"       
[13] ""            ""            ""            ""            ""            ""           
[19] ""            ""            ""            "D56"         ""            "A6"         
[25] ""            ""            ""            "C23 C25 C27"
# The first letter is the "Deck". For Example:
strsplit(full$Cabin[2], NULL)[[1]]
[1] "C" "8" "5"
# Create a Deck variable. Get passenger deck A - F:
full$Deck <- factor(sapply(full$Cabin, function(x) strsplit(x, NULL)[[1]][1]))



DATA CLEANING


Now we will treate missing values. As this dataset size is very small so we will neither remove any missing values nor any column. Instead we will replace missing values with the sensible values given the distribution of the data, e.g., mean, median or mode. We will also use prediction for the same.


SENSIBLE VALUE IMPUTATION


# Lets look into the data to see how many missing values are there:
miss <- function(x){
  sum = 0
  for(i in 1:ncol(x))
  {
    cat("In column",colnames(x[i]),"total NA values are:",colSums(is.na(x[i])),"\n")
  }
}
miss(full)
In column PassengerId total NA values are: 0 
In column Survived total NA values are: 418 
In column Pclass total NA values are: 0 
In column Name total NA values are: 0 
In column Sex total NA values are: 0 
In column Age total NA values are: 263 
In column SibSp total NA values are: 0 
In column Parch total NA values are: 0 
In column Ticket total NA values are: 0 
In column Fare total NA values are: 1 
In column Cabin total NA values are: 0 
In column Embarked total NA values are: 0 
In column Title total NA values are: 0 
In column Surnames total NA values are: 0 
In column Fsize total NA values are: 0 
In column Family total NA values are: 0 
In column FsizeD total NA values are: 0 
In column Deck total NA values are: 1014 
blank <- function(x){
  sum = 0
  for(i in 1:ncol(x))
  {
    cat("In column",colnames(x[i]),"total blank values are:",colSums(x[i]==""),"\n")
  }
}
blank(full)
In column PassengerId total blank values are: 0 
In column Survived total blank values are: NA 
In column Pclass total blank values are: 0 
In column Name total blank values are: 0 
In column Sex total blank values are: 0 
In column Age total blank values are: NA 
In column SibSp total blank values are: 0 
In column Parch total blank values are: 0 
In column Ticket total blank values are: 0 
In column Fare total blank values are: NA 
In column Cabin total blank values are: 1014 
In column Embarked total blank values are: 2 
In column Title total blank values are: 0 
In column Surnames total blank values are: 0 
In column Fsize total blank values are: 0 
In column Family total blank values are: 0 
In column FsizeD total blank values are: 0 
In column Deck total blank values are: NA 

Now variable of interest are Embarked, Survived, Age, Fare and Deck.


# Now examine which passenger has missing Embarked 
full$PassengerId[full$Embarked == ""]
[1]  62 830

Passenger 62 and 830 has missing Embarked.

# Now we in which class they belong and how much they paid fare:
full$Pclass[full$PassengerId == 62]
[1] 1
full$Fare[full$PassengerId == 62]
[1] 80
full$Pclass[full$PassengerId == 830]
[1] 1
full$Fare[full$PassengerId == 830]
[1] 80


Here we see that both passengers are in class 1 and paid fare 80. So from where did they embark?

# Get rid of our missing passenger id 
embarkFare <- full %>%
  filter(PassengerId != 62 & PassengerId != 830)
# Use ggplot2 to visualize embarkment, passenger class, & median fare
ggplot(data = embarkFare, 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_bw()


From this plot we see that median fare for the first class passenger departing from C (Charbourg) Embarked coincides nicely with the $80 paid by the passengers whose Embarked is missing. So we can safely replace the NA with C.

# Replacing NA with C for Embarked - Deficient Passengers
full$Embarked[c(62, 830)] <- "C"


# Now examine which passenger has missing Fare
full$PassengerId[is.na(full$Fare)]
[1] 1044

Passenger 1044 has missing Fare.

# Let's see the detail of this passenger
full[1044, ]


This is the third class passenger who departed from Southampton (S).

# Let's visualize Fares among the "3rd" class and "S" embarked
ggplot(data = 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_bw()


From this plot it seems quite reasonable to replace NA Fare value with median for their class and embarkment which is $8.05.

# Replace missing fare value median fare for class / embarked
full$Fare[1044] <- median(full[full$Pclass == 3 & full$Embarked == "S", ]$Fare, na.rm = T)


PREDICTIVE IMPUTATION


Finally, we will treat the variable Age as it contains 263 missing values. We will create a module which will predict ages based on other variables.

# Make variables factors into factors
factorVars <- c("PassengerId", "Pclass", "Sex", "Embarked", "Title", "Surnames", "Family", "FsizeD")
full[factorVars] <- lapply(full[factorVars], function(x) as.factor(x))
# Set a random seed
set.seed(129)
# Perform mice imputation, excluding certain less - than - useful variables
miceMod <- mice(full[, !names(full) %in% c("PassengerId", "Name", "Ticket", "Cabin", "Family", "Survived", "Surnames")], 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
Number of logged events: 50
# Save the complete output
miceOutput <- complete(miceMod)


Let’s compare the results we get with the original distribution of passenger ages to ensure that nothing is gone completely awry.

# Plot age distribution
par(mfrow = c(1, 2))
hist(full$Age, freq = F, main = "Age: Original Data", col = "darkgreen", ylim = c(0, 0.04))
hist(miceOutput$Age, freq = F, main = "Age: MICE Output", col = "lightgreen", ylim = c(0, 0.04))


Our model is similar to the original Age. So we can replace Age variable with our mice output Age variable.

# Replace Age variable with mice model
full$Age <- miceOutput$Age
# Show new number of missing Age values
sum(is.na(full$Age))
[1] 0


Now we have finished imputation for variables we are interested. Now we will do bit more of feature engineering.


FEATURE ENGINEERING : ROUND 2


Now we will create few age - dependent variables like Child and Mother. A child may be simply be someoneunder 18 years of age and a mother is a passenger who is:

  • Female
  • Is over 18
  • Has more than 0 children
  • Does not have “Miss” title
# First we will look at the relationship between age and survival
ggplot(data = full[1:891, ], aes(x = Age, fill = factor(Survived))) + 
  geom_histogram() + 
  facet_grid(.~Sex) + 
  theme_bw()


# Create a 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)
       
          0   1
  Adult 482 272
  Child  67  70


Here we see that Children has 50% - 50% for survival. Now we will create variable Mother and hope that they more likely to survive on 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)
            
               0   1
  Mother      16  39
  Not Mother 533 303
# Factorizing the Child and Mother variables
full$Child <- factor(full$Child)
full$Mother <- factor(full$Mother)


We should double check there will be no missing values in the dataset.

md.pattern(full)
    PassengerId Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked Title
204           1      1    1   1   1     1     1      1    1     1        1     1
687           1      1    1   1   1     1     1      1    1     1        1     1
91            1      1    1   1   1     1     1      1    1     1        1     1
327           1      1    1   1   1     1     1      1    1     1        1     1
              0      0    0   0   0     0     0      0    0     0        0     0
    Surnames Fsize Family FsizeD Child Mother Survived Deck     
204        1     1      1      1     1      1        1    1    0
687        1     1      1      1     1      1        1    0    1
91         1     1      1      1     1      1        0    1    1
327        1     1      1      1     1      1        0    0    2
           0     0      0      0     0      0      418 1014 1432

We finally finished treating missing values in the titanic dataset. We also created some new variables which will help us in building model which reliably predicts survival.



PREDICTION


Now we will predict who survives among passengers of the Titanic from the variables we treated and created. We will use randomForrest classification algorithm for our prediction.


SPLIT INTO TRAIN AND TEST DATASETS


# Split the data back into a train set and a test set
train <- full[1:891,]
test <- full[892:1309,]


BUILDING THE MODEL


Now we will build our model by using randomForrest on the train set.

# Set a random seed
set.seed(754)
# Build the model (note: not all possble variables are used)
model <- randomForest(factor(Survived) ~ Pclass + Sex + Age + SibSp + Parch + Fare + Embarked + Title + FsizeD + Child + Mother, data = train)
# Show model error
plot(model, ylim = c(0, 0.36))
legend("topright", colnames(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.


VARIABLE IMPORTANCE


Let’s look at relative variable importance by plotting the mean decrease in Gini calculated across all trees.

# Get importance
importance    <- importance(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_bw()


From this we see that the variable Title is very important.


PREDICTION

# Predict using the test set
prediction <- predict(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 = "Solution.csv", row.names = F)
# Count
table(solution$Survived)

  0   1 
266 152 


CONCLUSION

This is the analysis on Titanic dataset in which we predicted 266 died out of 418 passengers in the test set. So total passengers who died on Titanic are 815 out of 1309 passengers.

LS0tDQp0aXRsZTogPGNlbnRlcj48c3BhbiBzdHlsZSA9ICJjb2xvcjpyZWQiPjxiPlRJVEFOSUMgREFUQVNFVCBBTkFMWVNJUzwvYj48L3NwYW4+PC9jZW50ZXI+DQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogIlNoaXZhbSBBZ3Jhd2FsIg0KZGF0ZTogIjQgSnVseSAyMDE4Ig0KLS0tDQoNCjxicj48YnI+DQoNCiMjIDxpIHN0eWxlID0gImNvbG9yOnB1cnBsZSI+KipDT05URU5UKio8L2k+DQoqICoqSW50cm9kdWN0aW9uKioNCiogKipJbXBvcnRpbmcgTGlicmFyaWVzIGFuZCBMb2FkaW5nIERhdGEqKg0KKiAqKkRhdGEgSW5zcGVjdGlvbioqDQoqICoqRmVhdHVyZSBFbmdpbmVlcmluZyoqDQoqICoqRGF0YSBDbGVhbmluZyoqDQoqICoqUHJlZGljdGlvbioqDQoqICoqQ29uY2x1dGlvbioqDQoNCjxicj48YnI+DQoNCiMjIyMgPGkgc3R5bGUgPSAiY29sb3I6b3JhbmdlIj4qKklOVFJPRFVDVElPTioqPC9pPg0KDQo8YnI+PGJyPg0KDQo8aW1nIHNyYz0ic2hpcC5qcGciIGFsdD0iIiBzdHlsZSA9ICJ3aWR0aDoxMDAlIj4NCg0KPGJyPjxicj4NCg0KPHA+VGhlIHNpbmtpbmcgb2YgdGhlIFJNUyBUaXRhbmljIGlzIG9uZSBvZiB0aGUgbW9zdCBpbmZhbW91cyBzaGlwd3JlY2tzIGluIGhpc3RvcnkuICBPbiBBcHJpbCAxNSwgMTkxMiwgZHVyaW5nIGhlciBtYWlkZW4gdm95YWdlLCB0aGUgVGl0YW5pYyBzYW5rIGFmdGVyIGNvbGxpZGluZyB3aXRoIGFuIGljZWJlcmcsIGtpbGxpbmcgMTUwMiBvdXQgb2YgMjIyNCBwYXNzZW5nZXJzIGFuZCBjcmV3LiBUaGlzIHNlbnNhdGlvbmFsIHRyYWdlZHkgc2hvY2tlZCB0aGUgaW50ZXJuYXRpb25hbCBjb21tdW5pdHkgYW5kIGxlZCB0byBiZXR0ZXIgc2FmZXR5IHJlZ3VsYXRpb25zIGZvciBzaGlwcy48L3A+DQo8cD5PbmUgb2YgdGhlIHJlYXNvbnMgdGhhdCB0aGUgc2hpcHdyZWNrIGxlZCB0byBzdWNoIGxvc3Mgb2YgbGlmZSB3YXMgdGhhdCB0aGVyZSB3ZXJlIG5vdCBlbm91Z2ggbGlmZWJvYXRzIGZvciB0aGUgcGFzc2VuZ2VycyBhbmQgY3Jldy4gQWx0aG91Z2ggdGhlcmUgd2FzIHNvbWUgZWxlbWVudCBvZiBsdWNrIGludm9sdmVkIGluIHN1cnZpdmluZyB0aGUgc2lua2luZywgc29tZSBncm91cHMgb2YgcGVvcGxlIHdlcmUgbW9yZSBsaWtlbHkgdG8gc3Vydml2ZSB0aGFuIG90aGVycywgc3VjaCBhcyB3b21lbiwgY2hpbGRyZW4sIGFuZCB0aGUgdXBwZXItY2xhc3MuPC9wPg0KPHA+U29tZSBvZiB0aGUgaW1hZ2VzIG9mIHRoZSBkaXNhc3RlciBhcmUgc2hvd24gYmVsb3c6PC9wPg0KDQo8YnI+PGJyPg0KDQo8aW1nIHNyYz0icGljMS5qcGciIGFsdD0iIiBzdHlsZSA9ICJ3aWR0aDo0NSUiPg0KJm5ic3A7DQo8aW1nIHNyYz0icGljMi5qcGciIGFsdD0iIiBzdHlsZSA9ICJ3aWR0aDo0NSU7IGZsb2F0OiByaWdodCI+DQo8YnI+PGJyPg0KPGltZyBzcmM9InRpdGFuaWMuanBnIiBhbHQ9IiIgc3R5bGUgPSAid2lkdGg6NDUlIj4NCiZuYnNwOw0KPGltZyBzcmM9InBpYzMuanBnIiBhbHQ9IiIgc3R5bGUgPSAid2lkdGg6NDUlOyBmbG9hdDogcmlnaHQiPg0KPGJyPjxicj4NCjxjZW50ZXI+PGltZyBzcmM9InRpdGFuaWMxLmpwZyIgYWx0PSIiIHN0eWxlID0gIndpZHRoOiA1MCUiPjwvY2VudGVyPg0KDQo8YnI+PGJyPg0KDQo8cD4gSW4gdGhpcyBJIGRvbmUgdGhlIGFuYWx5c2lzIG9uIHRpdGFuaWMgZGF0YXNldCBhbmQgYXBwbGllZCBwcmVkaWN0aXZlIGFuYWx5c2lzIHRvIHByZWRpY3QgdGhlIHN1cnZpdmFsIG9mIHBlc3NlbmdlcnMuIDwvcD4NCg0KPGJyPjxicj4NCg0KIyMjIyA8aSBzdHlsZSA9ICJjb2xvcjpvcmFuZ2UiPioqSU1QT1JUSU5HIExJQlJBUklFUyBBTkQgTE9BRElORyBEQVRBKio8L2k+DQoNCjxicj4NCg0KYGBge3J9DQojIEltcG9ydGluZyBQYWNrYWdlcw0KDQpsaWJyYXJ5KGdncGxvdDIpICAgICAgICMgVmlzdWFsaXphdGlvbg0KbGlicmFyeShnZ3RoZW1lcykgICAgICAjIFZpc3VhbGl6YXRpb24NCmxpYnJhcnkoc2NhbGVzKSAgICAgICAgIyBWaXN1YWxpemF0aW9uDQpsaWJyYXJ5KGRwbHlyKSAgICAgICAgICMgRGF0YSBNYW5pcHVsYXRpb24NCmxpYnJhcnkobWljZSkgICAgICAgICAgIyBJbXB1dGF0aW9uDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkgICMgQ2xhc3NpZmljYXRpb24gQWxnb3JpdGhtDQpgYGANCg0KPGJyPg0KDQpgYGB7cn0NCiMgTG9hZGluZyBEYXRhDQoNCnRyYWluIDwtIHJlYWQuY3N2KCJ0cmFpbi5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRikNCnRlc3QgPC0gcmVhZC5jc3YoInRlc3QuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpDQoNCmZ1bGwgPC0gYmluZF9yb3dzKHRyYWluLCB0ZXN0KSAgICAgIyBCaW5kIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGENCmBgYA0KDQo8YnI+PGJyPg0KDQojIyMjIDxpIHN0eWxlID0gImNvbG9yOm9yYW5nZSI+KipEQVRBIElOU1BFQ1RJT04qKjwvaT4NCg0KPGJyPg0KDQpgYGB7cn0NCmdsaW1wc2UoZnVsbCkNCmBgYA0KDQo8YnI+DQoNCjxwPiBIZXJlIHdlIHNlZSB0aGF0IHdlIGhhdmUgKioxMzA5Kiogb2JzZXJ2YXRpb25zIG9mICoqMTIqKiB2YXJpYWJsZXMgd2l0aCB0aGVpciBjbGFzcyB0eXBlIGFuZCB0aGUgZmlyc3QgZmV3IG9ic2VydmF0aW9ucy4gPHA+IA0KPHA+IFZhcmlhYmxlcyBkZXNjcmlwdGlvbjo8L3A+DQoNCiogKipQYXNzZW5nZXJJRCoqIDogSUQgb2YgdGhlIFBhc3Nlbmdlci4NCiogKipTdXJ2aXZlZCoqIDogTm8gKDApIFllcyAoMSkuDQoqICoqUGNsYXNzKiogOiBQYXNzZW5nZXIncyBjbGFzcy4NCiogKipOYW1lKiogOiBOYW1lIG9mIHRoZSBQYXNzZW5nZXIuDQoqICoqU2V4KiogOiBHZW5kZXIgb2YgdGhlIFBhc3Nlbmdlci4NCiogKipTaWJTcCoqIDogTnVtYmVyIG9mIFNpYmxpbmdzIC8gU3BvdXNlcyBhYm9hcmQuDQoqICoqUGFyY2gqKiA6IE51bWJlciBvZiBQYXJlbnRzIC8gQ2hpbGRyZW4gYWJvYXJkLg0KKiAqKlRpY2tldCoqIDogVGlja2V0IG51bWJlci4NCiogKipGYXJlKiogOiBGYXJlIG9mIHRoZSBUaWNrZXQuDQoqICoqQ2FiaW4qKiA6IENhYmluIG51bWJlci4NCiogKipFbWJhcmtlZCoqIDogUG9ydCBvZiBlbWJhcmthdGlvbi4NCg0KPGJyPjxicj4NCg0KIyMjIyA8aSBzdHlsZSA9ICJjb2xvcjpvcmFuZ2UiPioqRkVBVFVSRSBFTkdJTkVFUklORyoqPC9pPg0KDQo8YnI+DQoNCjxzcGFuIHN0eWxlID0gICJjb2xvcjpicm93biI+KipXSEFUJ1MgSU4gQSBOQU1FPyoqPC9zcGFuPg0KDQo8YnI+DQoNCjxwPiBJbiB0aGlzIGRhdGEgd2UgY2FuIGJyZWFrIHZhcmlhYmxlICoqTmFtZSoqIGludG8gbWVhbmluZ2Z1bCB2YXJpYWJsZXMuIEZpcnN0IHdlIGV4dHJhY3QgKipQYXNzZW5nZXIgVGl0bGUqKiBmcm9tIHRoZSAqKlBhc3NlbmdlciBOYW1lKiouIDwvcD4NCg0KYGBge3J9DQojIEdyYWIgVGl0bGUgZnJvbSB0aGUgUGFzc2VuZ2VyIE5hbWVzDQoNCmZ1bGwkVGl0bGUgPC0gZ3N1YigiKC4qLCApfChcXC4uKikiLCIiLGZ1bGwkTmFtZSkNCmBgYA0KDQpgYGB7cn0NCiMgU2hvdyBUaXRsZSBDb3VudHMgYnkgU2V4DQoNCnRhYmxlKGZ1bGwkU2V4LCBmdWxsJFRpdGxlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUaXRsZSB3aXRoIHZlcnkgbG93IGNlbGwgY291bnRzIHRvIGJlIGNvbWJpbmVkIHRvICJyYXJlIiBMZXZlbA0KDQpyYXJlVGl0bGUgPC0gYygiRG9uYSIsICJMYWR5IiwidGhlIENvdW50ZXNzIiwgIkNhcHQiLCAiQ29sIiwgIkRvbiIsICJEciIsICJNYWpvciIsICJSZXYiLCAiU2lyIiwgIkpvbmtoZWVyIikNCmBgYA0KDQpgYGB7cn0NCiMgQWxzbyByZWFzc2lnbiAiTWxsZSIsICJNbWUiIGFuZCAiTXMiIGFjY29yZGluZ2x5DQoNCmZ1bGwkVGl0bGVbZnVsbCRUaXRsZSA9PSAiTWxsZSJdIDwtICJNaXNzIg0KZnVsbCRUaXRsZVtmdWxsJFRpdGxlID09ICJNcyJdIDwtICJNaXNzIg0KZnVsbCRUaXRsZVtmdWxsJFRpdGxlID09ICJNbWUiXSA8LSAiTXJzIg0KZnVsbCRUaXRsZVtmdWxsJFRpdGxlICVpbiUgcmFyZVRpdGxlXSA8LSAiUmFyZSBUaXRsZSINCmBgYA0KDQpgYGB7cn0NCiMgU2hvdyBUaXRsZSBDb3VudHMgYnkgU2V4IGFnYWluDQoNCnRhYmxlKGZ1bGwkU2V4LCBmdWxsJFRpdGxlKQ0KYGBgDQoNCjxicj4NCg0KPHA+IEZpbmFsbHkgd2Ugd2lsbCBleHRyYWN0ICoqU3VybmFtZXMqKiBmcm9tIHRoZSAqKlBhc3NlbmdlciBOYW1lcyoqLiA8L3A+DQoNCmBgYHtyfQ0KZnVsbCRTdXJuYW1lcyA8LSBzYXBwbHkoZnVsbCROYW1lLCBmdW5jdGlvbih4KSBzdHJzcGxpdCh4LCBzcGxpdCA9ICJbLiwgXSIpW1sxXV1bMV0pDQpgYGANCg0KYGBge3J9DQpjb3VudCA8LSAgbmxldmVscyhmYWN0b3IoZnVsbCRTdXJuYW1lcykpDQpjb3VudA0KYGBgDQoNCjxicj4NCg0KPHA+IFdlIGhhdmUgKio4NjYqKiB1bmlxdWUgc3VybmFtZXMuIDwvcD4NCg0KPGJyPg0KDQo8c3BhbiBzdHlsZSA9ICAiY29sb3I6YnJvd24iPioqRE8gRkFNSUxJRVMgU0lOSyBPUiBTV0lNIFRPR0VUSEVSPyoqPC9zcGFuPg0KDQo8YnI+DQoNCjxwPiBOb3cgd2Ugd2lsbCBjcmVhdGUgdGhlICoqRmFtaWx5IFNpemUqKiB2YXJpYWJsZSBiYXNlZCBvbiBudW1iZXIgb2Ygc2libGluZ3MgLyBzcG91c2UocykgKG1heSBiZSBzb21lb25lIGhhcyBtb3JlIHRoYW4gb25lIHNwb3VzZSEhKSBhbmQgbnVtYmVyIG9mIHBhcmVudHMgLyBjaGlsZHJlbi4gPC9wPg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgRmFtaWx5IFNpemUgdmFyaWFibGUgaW5jbHVkaW5nIHRoZSBwYXNzZW5nZXIgdGhlbXNlbHZlcw0KDQpmdWxsJEZzaXplIDwtIGZ1bGwkU2liU3AgKyBmdWxsJFBhcmNoICsgMQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSBGYW1pbHkgdmFyaWFibGUNCg0KZnVsbCRGYW1pbHkgPC0gcGFzdGUoZnVsbCRTdXJuYW1lcywgZnVsbCRGc2l6ZSwgc2VwID0gIl8iKQ0KYGBgDQoNCjxicj4NCg0KPHA+IExldCB1cyBsb29rIHVwb24gdGhlIHZhcmlhYmxlICoqRmFtaWx5IFNpemUqKiBhbmQgaG93IGl0IG1heSByZWxhdGVzIHRvICoqU3Vydml2YWwqKiB3aXRoIHRoZSBoZWxwIG9mIHZpc3VhbGl6YXRpb24uIDwvcD4NCg0KYGBge3J9DQojIFJlbGF0aW9uc2hpcCBiZXR3ZWVuIEZhbWlseSBTaXplIGFuZCBTdXJ2aXZhbCAoVHJhaW5pbmcgRGF0YSkNCg0KZ2dwbG90KGRhdGEgPSBmdWxsWzE6ODkxLF0sIGFlcyh4ID0gRnNpemUsIGZpbGwgPSBmYWN0b3IoU3Vydml2ZWQpKSkgKyANCiAgZ2VvbV9iYXIoc3RhdCA9ICJjb3VudCIsIHBvc2l0aW9uID0gImRvZGdlIikgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMToxMSkpICsgDQogIHlsaW0oYygwLDQwMCkpICsNCiAgbGFicyh4ID0gIkZhbWlseSBTaXplIikgKyANCiAgdGhlbWVfYncoKQ0KYGBgDQoNCjxicj4NCg0KPHA+IEhlcmUgd2Ugc2VlIHRoYXQgKipzaWdsZXRvbnMqKiBhcmUgbW9yZSBpbiBjb21wYXJpc2lvbiB0aGVuIG90aGVycyBhbmQgdGhleSBzdXJ2aXZlZCBtb3JlIGFuZCBkaWVkIHRvbyBhcyBjb21wYXJlIHRvIG90aGVycy4gPC9wPg0KPHA+IE5vdyB3ZSBjcmVhdGUgKipkaXNjcmV0aXplZCBmYW1pbHkgc2l6ZSoqIHZhcmlhYmxlLiA8L3A+IA0KDQpgYGB7cn0NCiMgRGlzY3JldGl6ZWQgZmFtaWx5IHNpemUNCmZ1bGwkRnNpemVEW2Z1bGwkRnNpemUgPT0gMV0gPC0gIlNpbmdsZXRvbiINCmZ1bGwkRnNpemVEW2Z1bGwkRnNpemUgPCA1ICYgZnVsbCRGc2l6ZSA+IDFdIDwtICJTbWFsbCINCmZ1bGwkRnNpemVEW2Z1bGwkRnNpemUgPj0gNV0gPC0gIkxhcmdlIg0KYGBgDQoNCmBgYHtyfQ0KIyBTaG93IEZhbWlseSBzaXplIGJ5IFN1cnZpdmFsIHVzaW5nIGEgTW9zYWljIHBsb3QNCg0KbW9zYWljcGxvdCh0YWJsZShmdWxsJEZzaXplRCwgZnVsbCRTdXJ2aXZlZCksIG1haW4gPSAiRmFtaWx5IFNpemUgYnkgU3Vydml2YWwiLCBzaGFkZSA9IFRSVUUpDQpgYGANCg0KPHA+IFRoaXMgcGxvdCBzaG93cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gKipGYW1pbHkgc2l6ZSBhbmQgU3Vydml2YWwqKi4gPC9wPg0KDQo8YnI+DQoNCjxzcGFuIHN0eWxlID0gICJjb2xvcjpicm93biI+KipUUkVBVElORyBGRVcgTU9SRSBWQVJJQUJMRVMuLi4qKjwvc3Bhbj4NCg0KPGJyPg0KDQo8cD4gTm93IHdlIHdpbGwgdHJlYXRlIHZhcmlhYmxlICoqUGFzc2VuZ2VyIENhYmluKiogd2hpY2ggaGFzIHBvdGVudGlhbGx5IHVzZWZ1bCBpbmZvcm1hdGlvbiBzdWNoIGFzIHRoZWlyICoqRGVjayoqLiA8L3A+DQo8cD4gTGV0J3MgaGF2ZSBhIGxvb2suLi4uPC9wPg0KDQpgYGB7cn0NCiMgVGhpcyB2YXJpYWJsZSBhcHBlYXJzIHRvIGhhdmUgYSBsb3Qgb2YgbWlzc2luZyB2YWx1ZXMNCg0KZnVsbCRDYWJpblsxOjI4XQ0KYGBgDQoNCmBgYHtyfQ0KIyBUaGUgZmlyc3QgbGV0dGVyIGlzIHRoZSAiRGVjayIuIEZvciBFeGFtcGxlOg0KDQpzdHJzcGxpdChmdWxsJENhYmluWzJdLCBOVUxMKVtbMV1dDQpgYGANCg0KYGBge3J9DQojIENyZWF0ZSBhIERlY2sgdmFyaWFibGUuIEdldCBwYXNzZW5nZXIgZGVjayBBIC0gRjoNCg0KZnVsbCREZWNrIDwtIGZhY3RvcihzYXBwbHkoZnVsbCRDYWJpbiwgZnVuY3Rpb24oeCkgc3Ryc3BsaXQoeCwgTlVMTClbWzFdXVsxXSkpDQpgYGANCg0KPGJyPjxicj4NCg0KIyMjIyA8aSBzdHlsZSA9ICJjb2xvcjpvcmFuZ2UiPioqREFUQSBDTEVBTklORyoqPC9pPg0KDQo8YnI+DQoNCjxwPiBOb3cgd2Ugd2lsbCB0cmVhdGUgbWlzc2luZyB2YWx1ZXMuIEFzIHRoaXMgZGF0YXNldCBzaXplIGlzIHZlcnkgc21hbGwgc28gd2Ugd2lsbCBuZWl0aGVyIHJlbW92ZSBhbnkgbWlzc2luZyB2YWx1ZXMgbm9yIGFueSBjb2x1bW4uIEluc3RlYWQgd2Ugd2lsbCByZXBsYWNlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIHNlbnNpYmxlIHZhbHVlcyBnaXZlbiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhLCBlLmcuLCBtZWFuLCBtZWRpYW4gb3IgbW9kZS4gV2Ugd2lsbCBhbHNvIHVzZSBwcmVkaWN0aW9uIGZvciB0aGUgc2FtZS4gPC9wPg0KDQo8YnI+DQoNCjxzcGFuIHN0eWxlID0gICJjb2xvcjpicm93biI+KipTRU5TSUJMRSBWQUxVRSBJTVBVVEFUSU9OKio8L3NwYW4+DQoNCjxicj4NCg0KYGBge3J9DQojIExldHMgbG9vayBpbnRvIHRoZSBkYXRhIHRvIHNlZSBob3cgbWFueSBtaXNzaW5nIHZhbHVlcyBhcmUgdGhlcmU6DQoNCm1pc3MgPC0gZnVuY3Rpb24oeCl7DQogIHN1bSA9IDANCiAgZm9yKGkgaW4gMTpuY29sKHgpKQ0KICB7DQogICAgY2F0KCJJbiBjb2x1bW4iLGNvbG5hbWVzKHhbaV0pLCJ0b3RhbCBOQSB2YWx1ZXMgYXJlOiIsY29sU3Vtcyhpcy5uYSh4W2ldKSksIlxuIikNCiAgfQ0KfQ0KbWlzcyhmdWxsKQ0KYGBgDQoNCmBgYHtyfQ0KYmxhbmsgPC0gZnVuY3Rpb24oeCl7DQogIHN1bSA9IDANCiAgZm9yKGkgaW4gMTpuY29sKHgpKQ0KICB7DQogICAgY2F0KCJJbiBjb2x1bW4iLGNvbG5hbWVzKHhbaV0pLCJ0b3RhbCBibGFuayB2YWx1ZXMgYXJlOiIsY29sU3Vtcyh4W2ldPT0iIiksIlxuIikNCiAgfQ0KfQ0KYmxhbmsoZnVsbCkNCmBgYA0KDQo8cD4gTm93IHZhcmlhYmxlIG9mIGludGVyZXN0IGFyZSAqKkVtYmFya2VkKiosICoqU3Vydml2ZWQqKiwgKipBZ2UqKiwgKipGYXJlKiogYW5kICoqRGVjayoqLiA8L3A+DQoNCjxicj4NCg0KYGBge3J9DQojIE5vdyBleGFtaW5lIHdoaWNoIHBhc3NlbmdlciBoYXMgbWlzc2luZyBFbWJhcmtlZCANCg0KZnVsbCRQYXNzZW5nZXJJZFtmdWxsJEVtYmFya2VkID09ICIiXQ0KYGBgDQoNCjxwPiBQYXNzZW5nZXIgKio2MioqIGFuZCAqKjgzMCoqIGhhcyBtaXNzaW5nIEVtYmFya2VkLiA8L3A+DQoNCmBgYHtyfQ0KIyBOb3cgd2UgaW4gd2hpY2ggY2xhc3MgdGhleSBiZWxvbmcgYW5kIGhvdyBtdWNoIHRoZXkgcGFpZCBmYXJlOg0KZnVsbCRQY2xhc3NbZnVsbCRQYXNzZW5nZXJJZCA9PSA2Ml0NCmZ1bGwkRmFyZVtmdWxsJFBhc3NlbmdlcklkID09IDYyXQ0KDQpmdWxsJFBjbGFzc1tmdWxsJFBhc3NlbmdlcklkID09IDgzMF0NCmZ1bGwkRmFyZVtmdWxsJFBhc3NlbmdlcklkID09IDgzMF0NCmBgYA0KDQo8YnI+DQoNCjxwPiBIZXJlIHdlIHNlZSB0aGF0IGJvdGggcGFzc2VuZ2VycyBhcmUgaW4gY2xhc3MgKioxKiogYW5kIHBhaWQgZmFyZSAqKjgwKiouIFNvIGZyb20gd2hlcmUgZGlkIHRoZXkgZW1iYXJrPyA8L2I+ICANCg0KYGBge3J9DQojIEdldCByaWQgb2Ygb3VyIG1pc3NpbmcgcGFzc2VuZ2VyIGlkIA0KDQplbWJhcmtGYXJlIDwtIGZ1bGwgJT4lDQogIGZpbHRlcihQYXNzZW5nZXJJZCAhPSA2MiAmIFBhc3NlbmdlcklkICE9IDgzMCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBVc2UgZ2dwbG90MiB0byB2aXN1YWxpemUgZW1iYXJrbWVudCwgcGFzc2VuZ2VyIGNsYXNzLCAmIG1lZGlhbiBmYXJlDQoNCmdncGxvdChkYXRhID0gZW1iYXJrRmFyZSwgYWVzKHggPSBFbWJhcmtlZCwgeSA9IEZhcmUsIGZpbGwgPSBmYWN0b3IoUGNsYXNzKSkpICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSA4MCksIA0KICAgICAgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIsIGx3ZCA9IDIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcl9mb3JtYXQoKSkgKyANCiAgdGhlbWVfYncoKQ0KYGBgDQoNCjxicj4NCg0KPHA+IEZyb20gdGhpcyBwbG90IHdlIHNlZSB0aGF0IG1lZGlhbiBmYXJlIGZvciB0aGUgZmlyc3QgY2xhc3MgcGFzc2VuZ2VyIGRlcGFydGluZyBmcm9tICoqQyAoQ2hhcmJvdXJnKSoqIEVtYmFya2VkIGNvaW5jaWRlcyBuaWNlbHkgd2l0aCB0aGUgKiokODAqKiBwYWlkIGJ5IHRoZSBwYXNzZW5nZXJzIHdob3NlIEVtYmFya2VkIGlzIG1pc3NpbmcuIFNvIHdlIGNhbiBzYWZlbHkgcmVwbGFjZSB0aGUgKipOQSoqIHdpdGggKipDKiouIDwvcD4NCg0KDQpgYGB7cn0NCiMgUmVwbGFjaW5nIE5BIHdpdGggQyBmb3IgRW1iYXJrZWQgLSBEZWZpY2llbnQgUGFzc2VuZ2Vycw0KDQpmdWxsJEVtYmFya2VkW2MoNjIsIDgzMCldIDwtICJDIg0KYGBgDQoNCjxicj4NCg0KYGBge3J9DQojIE5vdyBleGFtaW5lIHdoaWNoIHBhc3NlbmdlciBoYXMgbWlzc2luZyBGYXJlDQoNCmZ1bGwkUGFzc2VuZ2VySWRbaXMubmEoZnVsbCRGYXJlKV0NCmBgYA0KDQo8cD4gUGFzc2VuZ2VyICoqMTA0NCoqIGhhcyBtaXNzaW5nIEZhcmUuIDwvcD4NCg0KYGBge3J9DQojIExldCdzIHNlZSB0aGUgZGV0YWlsIG9mIHRoaXMgcGFzc2VuZ2VyDQoNCmZ1bGxbMTA0NCwgXQ0KYGBgDQoNCjxicj4NCg0KPHA+IFRoaXMgaXMgdGhlIHRoaXJkIGNsYXNzIHBhc3NlbmdlciB3aG8gZGVwYXJ0ZWQgZnJvbSBTb3V0aGFtcHRvbiAoUykuIDwvcD4NCg0KYGBge3J9DQojIExldCdzIHZpc3VhbGl6ZSBGYXJlcyBhbW9uZyB0aGUgIjNyZCIgY2xhc3MgYW5kICJTIiBlbWJhcmtlZA0KDQpnZ3Bsb3QoZGF0YSA9IGZ1bGxbZnVsbCRQY2xhc3MgPT0gMyAmIGZ1bGwkRW1iYXJrZWQgPT0gIlMiLCBdLCBhZXMoeCA9IEZhcmUpKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICIjOTlkNmZmIiwgYWxwaGEgPSAwLjQpICsgDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRpYW4oRmFyZSwgbmEucm0gPSBUKSksIA0KICAgICAgICAgICAgIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBsd2QgPSAxKSArDQogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBkb2xsYXJfZm9ybWF0KCkpICsgDQogIHRoZW1lX2J3KCkNCmBgYA0KDQo8YnI+DQoNCjxwPiBGcm9tIHRoaXMgcGxvdCBpdCBzZWVtcyBxdWl0ZSByZWFzb25hYmxlIHRvIHJlcGxhY2UgKipOQSoqIEZhcmUgdmFsdWUgd2l0aCBtZWRpYW4gZm9yIHRoZWlyIGNsYXNzIGFuZCBlbWJhcmttZW50IHdoaWNoIGlzICoqJDguMDUqKi4gPC9wPg0KDQpgYGB7cn0NCiMgUmVwbGFjZSBtaXNzaW5nIGZhcmUgdmFsdWUgbWVkaWFuIGZhcmUgZm9yIGNsYXNzIC8gZW1iYXJrZWQNCg0KZnVsbCRGYXJlWzEwNDRdIDwtIG1lZGlhbihmdWxsW2Z1bGwkUGNsYXNzID09IDMgJiBmdWxsJEVtYmFya2VkID09ICJTIiwgXSRGYXJlLCBuYS5ybSA9IFQpDQpgYGANCg0KPGJyPg0KDQo8c3BhbiBzdHlsZSA9ICAiY29sb3I6YnJvd24iPioqUFJFRElDVElWRSBJTVBVVEFUSU9OKio8L3NwYW4+DQoNCjxicj4NCg0KPHA+IEZpbmFsbHksIHdlIHdpbGwgdHJlYXQgdGhlIHZhcmlhYmxlICoqQWdlKiogYXMgaXQgY29udGFpbnMgKioyNjMqKiBtaXNzaW5nIHZhbHVlcy4gV2Ugd2lsbCBjcmVhdGUgYSBtb2R1bGUgd2hpY2ggd2lsbCBwcmVkaWN0IGFnZXMgYmFzZWQgb24gb3RoZXIgdmFyaWFibGVzLiA8L3A+DQoNCmBgYHtyfQ0KIyBNYWtlIHZhcmlhYmxlcyBmYWN0b3JzIGludG8gZmFjdG9ycw0KDQpmYWN0b3JWYXJzIDwtIGMoIlBhc3NlbmdlcklkIiwgIlBjbGFzcyIsICJTZXgiLCAiRW1iYXJrZWQiLCAiVGl0bGUiLCAiU3VybmFtZXMiLCAiRmFtaWx5IiwgIkZzaXplRCIpDQoNCmZ1bGxbZmFjdG9yVmFyc10gPC0gbGFwcGx5KGZ1bGxbZmFjdG9yVmFyc10sIGZ1bmN0aW9uKHgpIGFzLmZhY3Rvcih4KSkNCmBgYA0KDQpgYGB7cn0NCiMgU2V0IGEgcmFuZG9tIHNlZWQNCg0Kc2V0LnNlZWQoMTI5KQ0KYGBgDQoNCmBgYHtyfQ0KIyBQZXJmb3JtIG1pY2UgaW1wdXRhdGlvbiwgZXhjbHVkaW5nIGNlcnRhaW4gbGVzcyAtIHRoYW4gLSB1c2VmdWwgdmFyaWFibGVzDQoNCm1pY2VNb2QgPC0gbWljZShmdWxsWywgIW5hbWVzKGZ1bGwpICVpbiUgYygiUGFzc2VuZ2VySWQiLCAiTmFtZSIsICJUaWNrZXQiLCAiQ2FiaW4iLCAiRmFtaWx5IiwgIlN1cnZpdmVkIiwgIlN1cm5hbWVzIildLCBtZXRob2QgPSAicmYiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTYXZlIHRoZSBjb21wbGV0ZSBvdXRwdXQNCg0KbWljZU91dHB1dCA8LSBjb21wbGV0ZShtaWNlTW9kKQ0KYGBgDQoNCg0KPGJyPg0KDQo8cD4gTGV0J3MgY29tcGFyZSB0aGUgcmVzdWx0cyB3ZSBnZXQgd2l0aCB0aGUgb3JpZ2luYWwgZGlzdHJpYnV0aW9uIG9mIHBhc3NlbmdlciBhZ2VzIHRvIGVuc3VyZSB0aGF0IG5vdGhpbmcgaXMgZ29uZSBjb21wbGV0ZWx5IGF3cnkuIDwvcD4NCg0KYGBge3J9DQojIFBsb3QgYWdlIGRpc3RyaWJ1dGlvbg0KDQpwYXIobWZyb3cgPSBjKDEsIDIpKQ0KaGlzdChmdWxsJEFnZSwgZnJlcSA9IEYsIG1haW4gPSAiQWdlOiBPcmlnaW5hbCBEYXRhIiwgY29sID0gImRhcmtncmVlbiIsIHlsaW0gPSBjKDAsIDAuMDQpKQ0KaGlzdChtaWNlT3V0cHV0JEFnZSwgZnJlcSA9IEYsIG1haW4gPSAiQWdlOiBNSUNFIE91dHB1dCIsIGNvbCA9ICJsaWdodGdyZWVuIiwgeWxpbSA9IGMoMCwgMC4wNCkpDQpgYGANCg0KPGJyPg0KDQo8cD4gT3VyIG1vZGVsIGlzIHNpbWlsYXIgdG8gdGhlIG9yaWdpbmFsICoqQWdlKiouIFNvIHdlIGNhbiByZXBsYWNlICoqQWdlKiogdmFyaWFibGUgd2l0aCBvdXIgbWljZSBvdXRwdXQgKipBZ2UqKiB2YXJpYWJsZS4gPC9wPg0KDQpgYGB7cn0NCiMgUmVwbGFjZSBBZ2UgdmFyaWFibGUgd2l0aCBtaWNlIG1vZGVsDQoNCmZ1bGwkQWdlIDwtIG1pY2VPdXRwdXQkQWdlDQpgYGANCg0KYGBge3J9DQojIFNob3cgbmV3IG51bWJlciBvZiBtaXNzaW5nIEFnZSB2YWx1ZXMNCg0Kc3VtKGlzLm5hKGZ1bGwkQWdlKSkNCmBgYA0KDQo8YnI+DQoNCjxwPiBOb3cgd2UgaGF2ZSBmaW5pc2hlZCBpbXB1dGF0aW9uIGZvciB2YXJpYWJsZXMgd2UgYXJlIGludGVyZXN0ZWQuIE5vdyB3ZSB3aWxsIGRvIGJpdCBtb3JlIG9mIGZlYXR1cmUgZW5naW5lZXJpbmcuIDwvcD4NCg0KPGJyPg0KDQo8c3BhbiBzdHlsZSA9ICAiY29sb3I6YnJvd24iPioqRkVBVFVSRSBFTkdJTkVFUklORyA6IFJPVU5EIDIqKjwvc3Bhbj4NCg0KPGJyPg0KDQo8cD4gTm93IHdlIHdpbGwgY3JlYXRlIGZldyBhZ2UgLSBkZXBlbmRlbnQgdmFyaWFibGVzIGxpa2UgKipDaGlsZCoqIGFuZCAqKk1vdGhlcioqLiBBIGNoaWxkIG1heSBiZSBzaW1wbHkgYmUgc29tZW9uZXVuZGVyIDE4IHllYXJzIG9mIGFnZSBhbmQgYSBtb3RoZXIgaXMgYSBwYXNzZW5nZXIgd2hvIGlzOjwvcD4NCiogRmVtYWxlDQoqIElzIG92ZXIgMTgNCiogSGFzIG1vcmUgdGhhbiAwIGNoaWxkcmVuDQoqIERvZXMgbm90IGhhdmUgIk1pc3MiIHRpdGxlDQoNCmBgYHtyfQ0KIyBGaXJzdCB3ZSB3aWxsIGxvb2sgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFnZSBhbmQgc3Vydml2YWwNCg0KZ2dwbG90KGRhdGEgPSBmdWxsWzE6ODkxLCBdLCBhZXMoeCA9IEFnZSwgZmlsbCA9IGZhY3RvcihTdXJ2aXZlZCkpKSArIA0KICBnZW9tX2hpc3RvZ3JhbSgpICsgDQogIGZhY2V0X2dyaWQoLn5TZXgpICsgDQogIHRoZW1lX2J3KCkNCmBgYA0KDQo8YnI+DQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSBjb2x1bW4gY2hpbGQsIGFuZCBpbmRpY2F0ZSB3aGV0aGVyIGNoaWxkIG9yIGFkdWx0DQoNCmZ1bGwkQ2hpbGRbZnVsbCRBZ2UgPCAxOF0gPC0gIkNoaWxkIg0KZnVsbCRDaGlsZFtmdWxsJEFnZSA+PSAxOF0gPC0gIkFkdWx0Ig0KYGBgDQoNCmBgYHtyfQ0KIyBTaG93IGNvdW50cw0KDQp0YWJsZShmdWxsJENoaWxkLCBmdWxsJFN1cnZpdmVkKQ0KYGBgDQoNCjxicj4NCg0KPHA+IEhlcmUgd2Ugc2VlIHRoYXQgQ2hpbGRyZW4gaGFzIDUwJSAtIDUwJSBmb3Igc3Vydml2YWwuIE5vdyB3ZSB3aWxsIGNyZWF0ZSB2YXJpYWJsZSAqKk1vdGhlcioqIGFuZCBob3BlIHRoYXQgdGhleSBtb3JlIGxpa2VseSB0byBzdXJ2aXZlIG9uICoqVGl0YW5pYyoqLiA8L3A+DQoNCmBgYHtyfQ0KIyBBZGRpbmcgTW90aGVyIHZhcmlhYmxlDQoNCmZ1bGwkTW90aGVyIDwtICJOb3QgTW90aGVyIg0KZnVsbCRNb3RoZXJbZnVsbCRTZXggPT0gImZlbWFsZSIgJiBmdWxsJFBhcmNoID4gMCAmIGZ1bGwkQWdlID4gMTggJiBmdWxsJFRpdGxlICE9ICJNaXNzIl0gPC0gIk1vdGhlciINCmBgYA0KDQpgYGB7cn0NCiMgU2hvdyBjb3VudHMgDQoNCnRhYmxlKGZ1bGwkTW90aGVyLCBmdWxsJFN1cnZpdmVkKQ0KYGBgDQoNCmBgYHtyfQ0KIyBGYWN0b3JpemluZyB0aGUgQ2hpbGQgYW5kIE1vdGhlciB2YXJpYWJsZXMNCg0KZnVsbCRDaGlsZCA8LSBmYWN0b3IoZnVsbCRDaGlsZCkNCmZ1bGwkTW90aGVyIDwtIGZhY3RvcihmdWxsJE1vdGhlcikNCmBgYA0KDQo8YnI+DQoNCjxwPiBXZSBzaG91bGQgZG91YmxlIGNoZWNrIHRoZXJlIHdpbGwgYmUgbm8gbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQuIDwvcD4NCg0KYGBge3J9DQptZC5wYXR0ZXJuKGZ1bGwpDQpgYGANCg0KPHA+IFdlIGZpbmFsbHkgZmluaXNoZWQgdHJlYXRpbmcgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIHRpdGFuaWMgZGF0YXNldC4gV2UgYWxzbyBjcmVhdGVkIHNvbWUgbmV3IHZhcmlhYmxlcyB3aGljaCB3aWxsIGhlbHAgdXMgaW4gYnVpbGRpbmcgbW9kZWwgd2hpY2ggcmVsaWFibHkgcHJlZGljdHMgc3Vydml2YWwuIDwvcD4NCg0KPGJyPjxicj4NCg0KIyMjIyA8aSBzdHlsZSA9ICJjb2xvcjpvcmFuZ2UiPioqUFJFRElDVElPTioqPC9pPg0KDQo8YnI+DQoNCjxwPiBOb3cgd2Ugd2lsbCBwcmVkaWN0IHdobyBzdXJ2aXZlcyBhbW9uZyBwYXNzZW5nZXJzIG9mIHRoZSBUaXRhbmljIGZyb20gdGhlIHZhcmlhYmxlcyB3ZSB0cmVhdGVkIGFuZCBjcmVhdGVkLiBXZSB3aWxsIHVzZSAqKnJhbmRvbUZvcnJlc3QqKiBjbGFzc2lmaWNhdGlvbiBhbGdvcml0aG0gZm9yIG91ciBwcmVkaWN0aW9uLiA8L3A+DQoNCjxicj4NCg0KPHNwYW4gc3R5bGUgPSAgImNvbG9yOmJyb3duIj4qKlNQTElUIElOVE8gVFJBSU4gQU5EIFRFU1QgREFUQVNFVFMqKjwvc3Bhbj4NCg0KPGJyPg0KDQpgYGB7cn0NCiMgU3BsaXQgdGhlIGRhdGEgYmFjayBpbnRvIGEgdHJhaW4gc2V0IGFuZCBhIHRlc3Qgc2V0DQoNCnRyYWluIDwtIGZ1bGxbMTo4OTEsXQ0KdGVzdCA8LSBmdWxsWzg5MjoxMzA5LF0NCmBgYA0KDQo8YnI+DQoNCjxzcGFuIHN0eWxlID0gICJjb2xvcjpicm93biI+KipCVUlMRElORyBUSEUgTU9ERUwqKjwvc3Bhbj4NCg0KPGJyPg0KDQo8cD4gTm93IHdlIHdpbGwgYnVpbGQgb3VyIG1vZGVsIGJ5IHVzaW5nICoqcmFuZG9tRm9ycmVzdCoqIG9uIHRoZSAqKnRyYWluKiogc2V0LiA8L3A+DQoNCmBgYHtyfQ0KIyBTZXQgYSByYW5kb20gc2VlZA0Kc2V0LnNlZWQoNzU0KQ0KYGBgDQoNCmBgYHtyfQ0KIyBCdWlsZCB0aGUgbW9kZWwgKG5vdGU6IG5vdCBhbGwgcG9zc2JsZSB2YXJpYWJsZXMgYXJlIHVzZWQpDQoNCm1vZGVsIDwtIHJhbmRvbUZvcmVzdChmYWN0b3IoU3Vydml2ZWQpIH4gUGNsYXNzICsgU2V4ICsgQWdlICsgU2liU3AgKyBQYXJjaCArIEZhcmUgKyBFbWJhcmtlZCArIFRpdGxlICsgRnNpemVEICsgQ2hpbGQgKyBNb3RoZXIsIGRhdGEgPSB0cmFpbikNCmBgYA0KDQpgYGB7cn0NCiMgU2hvdyBtb2RlbCBlcnJvcg0KDQpwbG90KG1vZGVsLCB5bGltID0gYygwLCAwLjM2KSkNCmxlZ2VuZCgidG9wcmlnaHQiLCBjb2xuYW1lcyhtb2RlbCRlcnIucmF0ZSksIGNvbCA9IDE6MywgZmlsbCA9IDE6MykNCmBgYA0KDQo8YnI+DQoNCjxwPiBUaGUgYmxhY2sgbGluZSBzaG93cyB0aGUgb3ZlcmFsbCBlcnJvciByYXRlIHdoaWNoIGZhbGxzIGJlbG93IDIwJS4gVGhlIHJlZCBhbmQgZ3JlZW4gbGluZXMgc2hvdyB0aGUgZXJyb3IgcmF0ZSBmb3IgJ2RpZWQnIGFuZCAnc3Vydml2ZWQnIHJlc3BlY3RpdmVseS4gPC9wPg0KDQo8YnI+DQoNCjxzcGFuIHN0eWxlID0gICJjb2xvcjpicm93biI+KipWQVJJQUJMRSBJTVBPUlRBTkNFKio8L3NwYW4+DQoNCjxicj4NCg0KPHA+IExldCdzIGxvb2sgYXQgcmVsYXRpdmUgdmFyaWFibGUgaW1wb3J0YW5jZSBieSBwbG90dGluZyB0aGUgbWVhbiBkZWNyZWFzZSBpbiBHaW5pIGNhbGN1bGF0ZWQgYWNyb3NzIGFsbCB0cmVlcy4gPC9wPg0KDQpgYGB7cn0NCiMgR2V0IGltcG9ydGFuY2UNCg0KaW1wb3J0YW5jZSAgICA8LSBpbXBvcnRhbmNlKG1vZGVsKQ0KdmFySW1wb3J0YW5jZSA8LSBkYXRhLmZyYW1lKFZhcmlhYmxlcyA9IHJvdy5uYW1lcyhpbXBvcnRhbmNlKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgSW1wb3J0YW5jZSA9IHJvdW5kKGltcG9ydGFuY2VbICwnTWVhbkRlY3JlYXNlR2luaSddLDIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSByYW5rIHZhcmlhYmxlIGJhc2VkIG9uIGltcG9ydGFuY2UNCg0KcmFua0ltcG9ydGFuY2UgPC0gdmFySW1wb3J0YW5jZSAlPiUNCiAgbXV0YXRlKFJhbmsgPSBwYXN0ZTAoJyMnLGRlbnNlX3JhbmsoZGVzYyhJbXBvcnRhbmNlKSkpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBVc2UgZ2dwbG90MiB0byB2aXN1YWxpemUgdGhlIHJlbGF0aXZlIGltcG9ydGFuY2Ugb2YgdmFyaWFibGVzDQpnZ3Bsb3QocmFua0ltcG9ydGFuY2UsIGFlcyh4ID0gcmVvcmRlcihWYXJpYWJsZXMsIEltcG9ydGFuY2UpLCANCiAgICB5ID0gSW1wb3J0YW5jZSwgZmlsbCA9IEltcG9ydGFuY2UpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKyANCiAgZ2VvbV90ZXh0KGFlcyh4ID0gVmFyaWFibGVzLCB5ID0gMC41LCBsYWJlbCA9IFJhbmspLA0KICAgIGhqdXN0PTAsIHZqdXN0PTAuNTUsIHNpemUgPSA0LCBjb2xvdXIgPSAncmVkJykgKw0KICBsYWJzKHggPSAnVmFyaWFibGVzJykgKw0KICBjb29yZF9mbGlwKCkgKyANCiAgdGhlbWVfYncoKQ0KYGBgDQoNCjxicj4NCg0KPHA+IEZyb20gdGhpcyB3ZSBzZWUgdGhhdCB0aGUgdmFyaWFibGUgKipUaXRsZSoqIGlzIHZlcnkgaW1wb3J0YW50LiA8L3A+DQoNCjxicj4NCg0KPHNwYW4gc3R5bGUgPSAgImNvbG9yOmJyb3duIj4qKlBSRURJQ1RJT04qKjwvc3Bhbj4NCg0KYGBge3J9DQojIFByZWRpY3QgdXNpbmcgdGhlIHRlc3Qgc2V0DQoNCnByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbCwgdGVzdCkNCmBgYA0KDQpgYGB7cn0NCiMgU2F2ZSB0aGUgc29sdXRpb24gdG8gYSBkYXRhZnJhbWUgd2l0aCB0d28gY29sdW1uczogUGFzc2VuZ2VySWQgYW5kIFN1cnZpdmVkIChwcmVkaWN0aW9uKQ0KDQpzb2x1dGlvbiA8LSBkYXRhLmZyYW1lKFBhc3NlbmdlcklEID0gdGVzdCRQYXNzZW5nZXJJZCwgU3Vydml2ZWQgPSBwcmVkaWN0aW9uKQ0KYGBgDQoNCmBgYHtyfQ0KIyBXcml0ZSB0aGUgc29sdXRpb24gdG8gZmlsZQ0KDQp3cml0ZS5jc3Yoc29sdXRpb24sIGZpbGUgPSAiU29sdXRpb24uY3N2Iiwgcm93Lm5hbWVzID0gRikNCmBgYA0KDQpgYGB7cn0NCiMgQ291bnQNCg0KdGFibGUoc29sdXRpb24kU3Vydml2ZWQpDQpgYGANCg0KDQo8YnI+DQoNCiMjIyMgPGkgc3R5bGUgPSAiY29sb3I6b3JhbmdlIj4qKkNPTkNMVVNJT04qKjwvaT4NCg0KPHA+IFRoaXMgaXMgdGhlIGFuYWx5c2lzIG9uIFRpdGFuaWMgZGF0YXNldCBpbiB3aGljaCB3ZSBwcmVkaWN0ZWQgKioyNjYqKiBkaWVkIG91dCBvZiAqKjQxOCoqIHBhc3NlbmdlcnMgaW4gdGhlIHRlc3Qgc2V0LiBTbyB0b3RhbCBwYXNzZW5nZXJzIHdobyBkaWVkIG9uIFRpdGFuaWMgYXJlICoqODE1Kiogb3V0IG9mICoqMTMwOSoqIHBhc3NlbmdlcnMuIDwvcD4NCg0K