The shinking of the Titanic is one of the most infamous shipwrecks in history. While there was some element of luck involved in surviving related to the passenger class,gender,age etc.In this project I am going to build a predictive model with the passenger data(training data) from kaggle that answers the question:“what sorts of people were more likely to survive?”

The Data Science project cycle works as OSMEN framework where the first step comes as:

  1. Obtaining The Data:

The first step of a Data Science project is Obtaining or Gathering the data.Now as the data is already available in the website I am going to set the directory to the folder where the data is downloaded.

setwd("C:/Users/user/Desktop/Folder/TITANIC")

Now it is time to load the dataset into the interface with the ‘read.csv()’ function where ‘header=T’ tells R that the first line contains name of the columns and ‘stringAsfactors= FALSE’ tells R to consider the strings as string variables.

titanic.train<- read.csv(file = "train.csv", stringsAsFactors = FALSE, header = TRUE)
titanic.test<- read.csv(file = "test.csv", stringsAsFactors = FALSE, header = TRUE)

Next I am going to have look how the dataset looks like with the ‘header()’ funcion as:

head(titanic.train)
head(titanic.test)

For each of the passenger Id,‘survival’ refres as 0=No,1=YES, ‘Pclass’ defines Ticket class, ‘Age’ in years as Age is fractional if less than 1. If the age is estimated, is it in the form of xx.5,

‘sibsip’ defines no of siblings/spouses aboard the Titanic as sibsp: The dataset defines family relations in this way… Sibling = brother, sister, stepbrother, stepsister Spouse = husband, wife (mistresses and fiancés were ignored),

‘parch’ defines no of parents/children aboard the Titanic as parch: The dataset defines family relations in this way… Parent = mother, father Child = daughter, son, stepdaughter, stepson Some children travelled only with a nanny, therefore parch=0 for them,

‘embarked’ defines port of Embarkation as C=Cherbourg,Q= Queenstown, S= Southampton.

Now clearly we can see that the test datatset does not have the ‘Survived’ column, exactly what we have to predict.So the Submisson file will have the passengerId of test dataset and whether they survived or not.Let’s continue to the next step.

2)Scrubbing of the Dataset: Cleaning and filtering of the dataset.

Let’s combine the traing and testing dataset that will reduce the time as well as the task much more easier.As we know the test data set does not have the survived column so we have to include that column, so let’s fill the survived column with ‘NA’.Now to avoid the confusion in identifying the datasets while splitting let’s insert a new column in both the test and the traing dataset.let’s call the new column as ‘IsTrainset’ with ‘TRUE’ string in Training data and ‘FALSE’ string in the Test data.Now let’s combine them with ‘rbind’ function to start the scrubbing process.

titanic.test$Survived<- NA
titanic.train$IsTrainset<- TRUE
titanic.test$IsTrainset<- FALSE
titanic.combined<- rbind(titanic.train,titanic.test)
str(titanic.combined)
'data.frame':   1309 obs. of  13 variables:
 $ PassengerId: chr  "s" "2" "3" "4" ...
 $ 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        : chr  "male" "female" "female" "female" ...
 $ 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" ...
 $ IsTrainset : logi  TRUE TRUE TRUE TRUE TRUE TRUE ...

a)The first step to data cleaning is removing unwanted observations from your dataset.This includes ‘duplicate or irrelevant observations’— as we can see from the ‘titanic.combined’ dataframe that each unique passengerId all the 13 variables are given. so no need of checking for duplicate data and irrelevant observations.

b)The next comes as ‘Fixing Structural errors’— as we can see all the 13 variables has unqiue justified name hence thereis zero chance of the typos and inconsistent capitalization and mislabeled classes.

c)the 3rd step is ‘Filter Unwanted Outliers’— Outliers can cause problems with certain types of models. For example, linear regression models are less robust to outliers than decision tree models.We can’t stress this enough: as we do not have a good reason for removing an outlier, such as suspicious measurements that are unlikely to be real data. so this step can also be skipped.

d)The most important step is ‘Handle missing data’— The 2 most commonly recommended ways of dealing with missing data is

1)Dropping observations that have missing values.

2)Imputing the missing values based on other observations.

now,The best way to handle missing data for categorical features is to simply label them as ’Missing’! and For missing numeric data, you should flag and fill the values.By using this technique of flagging and filling, you are essentially allowing the algorithm to estimate the optimal constant for missingness, instead of just filling it in with the mean. to find out the missing varibles we will use the function’is.na’ on different variables as we can see

table(is.na(titanic.combined))

FALSE  TRUE 
16335   682 

now let’s look into each column as

table(is.na(titanic.combined$Age))

FALSE  TRUE 
 1046   263 

263 values out of 1309 were missing this whole time, that’s a whopping 20%! so let’s take a look at the combined dataframe’s age variable to see what we’re up against:

summary(titanic.combined$Age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
   0.17   21.00   28.00   29.88   39.00   80.00     263 

So let’s grow a tree on the subset of the data with the age values available, and then replace those that are missing: rpart has a great advantage in that it can use surrogate variables when it encounters an NA value.

install.packages('rpart')
library(rpart)
age.fit<- rpart(Age~ Pclass + Sex + SibSp + Parch + Fare + Embarked, data=titanic.combined[!is.na(titanic.combined$Age),],method="anova")
titanic.combined$Age[is.na(titanic.combined$Age)] <- predict(age.fit, titanic.combined[is.na(titanic.combined$Age),])
table(is.na(titanic.combined$Age))

FALSE 
 1309 

now we can see all the missing values of age column is replaced by the predicted values from the decision tree. There is some mising value that we are not able find out with the ‘is.na’, probably that beacuse of ’ ’.So let’s find out

table(titanic.combined$Embarked)

      C   Q   S 
  2 270 123 914 

now as we can see two observations do not have the port of embarktion specified that’s why they are blank. While a blank wouldn’t be a problem for our model like an NA would be, since we’re cleaning anyhow, let’s get rid of it. Because it’s so few observations and such a large majority boarded in Southampton, let’s just replace those two with “S”. First we need to find out who they are though! We can use which for this:

which(titanic.combined$Embarked== '')
[1]  62 830

This gives us the indexes of the blank fields. Then we simply replace those two, and encode it as a factor:

titanic.combined$Embarked[c(62,830)] = "S"
titanic.combined$Embarked <- factor(titanic.combined$Embarked)

The other variable with missing values are Fare so let’s take a look and find the observation and replace it with the mean of fare.

summary(titanic.combined$Fare)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  0.000   7.896  14.454  33.295  31.275 512.329       1 
which(is.na(titanic.combined$Fare))
[1] 1044
titanic.combined$Fare[1044]<- median(titanic.combined$Fare,na.rm=TRUE)

Okay. Our dataframe is now cleared of all NAs.

3)Explore and vizualize the dataset to find patterns and Feature Engineering:

Feature engineering has been described as easily the most important factor in determining the success or failure of your predictive model.In the Titanic competition it could mean chopping, and combining different attributes that we were given.

the ticket number, cabin, and name were all unique to each passenger; perhaps parts of those text strings could be extracted to build a new predictive attribute. Let’s start with the name field.

 titanic.combined$Name <- as.character(titanic.combined$Name)
 titanic.combined$Name[1]
[1] "Braund, Mr. Owen Harris"

Nicely, we see that there is a comma right after the person’s last name, and a full stop after their title. We can easily use the function strsplit, which stands for string split, to break apart our original name over these two symbols. Let’s try it out on Mr. Braund:

strsplit(titanic.combined$Name[1], split = '[,.]')
[[1]]
[1] "Braund"       " Mr"          " Owen Harris"

Let’s go a level deeper into the indexing mess and extract the title.

strsplit(titanic.combined$Name[1], split = '[,.]')[[1]][2]
[1] " Mr"

We feed sapply our vector of names and our function that we just came up with. It runs through the rows of the vector of names, and sends each name to the function.The results of all these string splits are all combined up into a vector as output from the sapply function, which we then store to a new column in our original dataframe, called Title.

titanic.combined$Title <- sapply(titanic.combined$Name, FUN=function(x) {strsplit(x, split='[,.]')[[1]][2]})
titanic.combined$Title <- sub(' ', '', titanic.combined$Title)
table(titanic.combined$Title)

        Capt          Col          Don         Dona           Dr     Jonkheer 
           1            4            1            1            8            1 
        Lady        Major       Master         Miss         Mlle          Mme 
           1            2           61          260            2            1 
          Mr          Mrs           Ms          Rev          Sir the Countess 
         757          197            2            8            1            1 

There are a few very rare titles in here that won’t give our model much to work with, so let’s combine a few of the most unusual ones.

titanic.combined$Title[titanic.combined$Title %in% c('Mme', 'Mlle')] <- 'Mlle'
titanic.combined$Title[titanic.combined$Title %in% c('Capt', 'Don', 'Major', 'Sir')] <- 'Sir'
titanic.combined$Title[titanic.combined$Title %in% c('Dona', 'Lady', 'the Countess', 'Jonkheer')] <- 'Lady'

suppose when we combine two titles the ‘%in%’ function checks if any of the existing titles in the entire Title column match either of them and then the c() function stores to the operator. Our final step is to change the variable type back to a factor, as these are essentially categories that we have created:

titanic.combined$Title <- factor(titanic.combined$Title)

We’re done with the passenger’s title now. What else can we think up? Well, there’s those two variables SibSb and Parch that indicate the number of family members the passenger is travelling with. Seems reasonable to assume that a large family might have trouble tracking down little Johnny as they all scramble to get off the sinking ship, so let’s combine the two variables into a new one, FamilySize:

titanic.combined$FamilySize <- titanic.combined$SibSp + titanic.combined$Parch + 1

We just add the number of siblings, spouses, parents and children the passenger had with them, and plus one for their own existence of course, and have a new variable indicating the size of the family they travelled with.

we just thought about a large family having issues getting to lifeboats together, but maybe specific families had more trouble than others? now suppose there are three Johnsons in a family with size 3, and another three probably unrelated Johnsons all travelling solo.Combining the Surname with the family size though should remedy this concern.

titanic.combined$Surname <- sapply(titanic.combined$Name, FUN=function(x) {strsplit(x, split='[,.]')[[1]][1]})
titanic.combined$FamilyID <- paste(as.character(titanic.combined$FamilySize), titanic.combined$Surname, sep="")

But those three single Johnsons would all have the same Family ID. let’s knock out any family size of two or less and call it a “small” family. This would fix the Johnson problem too.

titanic.combined$FamilyID[titanic.combined$FamilySize <= 2] <- 'Small'
table(titanic.combined$FamilyID)

           11Sage           3Abbott         3Appleton         3Beckwith 
               11                 3                 1                 2 
          3Boulos           3Bourke            3Brown         3Caldwell 
                3                 3                 4                 3 
         3Christy          3Collyer          3Compton          3Cornell 
                2                 3                 3                 1 
          3Coutts           3Crosby           3Danbom           3Davies 
                3                 3                 3                 5 
           3Dodge          3Douglas             3Drew            3Elias 
                3                 1                 3                 3 
      3Frauenthal        3Frolicher 3Frolicher-Stehli        3Goldsmith 
                1                 1                 2                 3 
      3Gustafsson       3Hamalainen           3Hansen             3Hart 
                2                 2                 1                 3 
            3Hays          3Hickman         3Hiltunen         3Hirvonen 
                2                 3                 1                 1 
        3Jefferys          3Johnson             3Kink    3Kink-Heilmann 
                2                 3                 2                 2 
          3Klasen         3Lahtinen           3Mallet            3McCoy 
                3                 2                 3                 3 
         3Minahan         3Moubarek            3Nakid         3Navratil 
                1                 3                 3                 3 
          3Newell           3Newsom         3Nicholls          3Peacock 
                1                 1                 1                 3 
           3Peter            3Quick         3Richards          3Rosblom 
                3                 3                 2                 3 
          3Samaan        3Sandstrom           3Silven          3Spedden 
                3                 3                 1                 3 
           3Strom          3Taussig           3Thayer           3Thomas 
                1                 3                 3                 1 
           3Touma     3van Billiard         3Van Impe    3Vander Planke 
                3                 3                 3                 2 
           3Wells             3Wick          3Widener          4Allison 
                3                 3                 3                 4 
       4Backstrom          4Baclini           4Becker           4Carter 
                1                 4                 4                 4 
        4Davidson             4Dean           4Herman          4Hocking 
                1                 4                 4                 2 
       4Jacobsohn         4Johnston          4Laroche           4Renouf 
                1                 4                 4                 1 
   4Vander Planke             4West             5Ford          5Hocking 
                1                 4                 5                 1 
   5Kink-Heilmann          5Lefebre          5Palsson          5Ryerson 
                1                 5                 5                 5 
         6Fortune           6Panula             6Rice         6Richards 
                6                 6                 6                 1 
           6Skoog        7Andersson          7Asplund          8Goodwin 
                6                 9                 7                 8 
            Small 
             1025 

There’s plenty of FamilyIDs with only one or two members, even though we wanted only family sizes of 3 or more. Perhaps some families had different last names, but whatever the case,let’s store the values in a dataframe and subset this dataframe to show only those unexpectedly small FamilyID groups.

famIDs <- data.frame(table(titanic.combined$FamilyID))
famIDs <- famIDs[famIDs$Freq <= 2,]

We then need to overwrite any family IDs in our dataset for groups that were not correctly identified and finally convert it to a factor:

titanic.combined$FamilyID[titanic.combined$FamilyID %in% famIDs$Var1] <- 'Small'
titanic.combined$FamilyID <- factor(titanic.combined$FamilyID)

with this the feature engineering part is now completed.

  1. Modeling the Dataset: applying proper machine learning algoritm as per requirement.

Now we can approach the prediction modeling by applying simple decision trees but they are biased to favour factors with many levels.This way the decision node can chop and change the data into the best way possible combination for purity of the following nodes.The bias towards many-levelled factors won’t go away either, and the overfitting problem will rise.Random Forest algorithm has a few restrictions that we did not have with our decision trees that is Instead of looking at the entire pool of available variables, Random Forests take only a subset of them, typically the square root of the number available.This way, many of the trees won’t even have the gender variable available at the first split, and might not even see it until several nodes deep.

Random Forests in R can only digest factors with up to 32 levels. Our FamilyID variable had almost double that.we’ll copy the FamilyID column to a new variable, FamilyID2, and then convert it from a factor back into a character string with as.character(). We can then increase our cut-off to be a “Small” family from 2 to 3 people. Then we just convert it back to a factor and we’re done:

 titanic.combined$FamilyID2 <- titanic.combined$FamilyID
 titanic.combined$FamilyID2 <- as.character(titanic.combined$FamilyID2)
 titanic.combined$FamilyID2[titanic.combined$FamilySize <= 3] <- 'Small'
 titanic.combined$FamilyID2 <- factor(titanic.combined$FamilyID2)
 table(titanic.combined$FamilyID2)

    11Sage   4Allison   4Baclini    4Becker    4Carter      4Dean    4Herman 
        11          4          4          4          4          4          4 
 4Johnston   4Laroche      4West      5Ford   5Lefebre   5Palsson   5Ryerson 
         4          4          4          5          5          5          5 
  6Fortune    6Panula      6Rice     6Skoog 7Andersson   7Asplund   8Goodwin 
         6          6          6          6          9          7          8 
     Small 
      1194 

Now we are at 22 levels so we’re good to split the test and train sets and delete the survived variable.

titanic.train<- titanic.combined[titanic.combined$IsTrainset== TRUE,]
titanic.test<-titanic.combined[titanic.combined$IsTrainset==FALSE,]
install.packages("dplyr")
library(dplyr)
titanic.test<- select(titanic.test,-Survived)

Now let’s grow a Random Forest. Install and load the package randomForest:

install.packages("randomForest")
library(randomForest)

the randomforest has randomness as discussed earlier so it’s a good practice to set a seed to get the reproducible results fo the next time we code up.he number inside isn’t important, you just need to ensure you use the same seed number each time so that the same random numbers are generated inside the Random Forest function.

set.seed(415)

Instead of specifying method=“class” as with rpart, we force the model to predict our classification by temporarily changing our target variable to a factor with only two levels using as.factor(). The importance=TRUE argument allows us to inspect variable importance as we’ll see, and the ntree argument specifies how many trees we want to grow.

fit<- randomForest(as.factor(Survived)~ Pclass+Sex+Age+SibSp+Parch+Fare+Embarked+Title+FamilySize+FamilyID2,data = titanic.train,importance=TRUE, ntree=2000)

The number of trees we want to grow depends on the size of the dataset,we can grow a large number of trees and not worry too much about their complexity, it will still run pretty fast.So let’s look at what variables were important:

varImpPlot(fit)

Random Forests doesn’t just waste those “out-of-bag” (OOB) observations, it uses them to see how well each tree performs on unseen data.

The prediction function works similarly to decision trees as all 2000 trees need to make their classifications and then discuss who’s right:

 Prediction <- predict(fit, titanic.test)
 submit <- data.frame(PassengerId = titanic.test$PassengerId, Survived = Prediction)
 write.csv(submit, file = "First_randomForest.csv", row.names = FALSE)

5)Interpret the Results: this step is all about telling the story of how the model works and explaining each of the steps involved.

   The Job was to predict if a passenger in the Titanic survived or not with 0,1 values for the variable. The score is the percentage of passengers you correctly predict.So after submitting this pediction file 'First_randomForest.csv' this model got the score of 0.78468.
   In the leaderboard This model got the rank of 5122 which is in the top 28% among the leaderboard.
LS0tDQp0aXRsZTogIk15IGZpcnN0IERhdGEgU2NpZW5jZSBQcm9qZWN0IHdpdGggUiBOb3RlYm9vayBmb3IgVGl0YW5pYyBEYXRhc2V0IGZyb20ga2FnZ2xlIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NClRoZSBzaGlua2luZyBvZiB0aGUgIFRpdGFuaWMgaXMgb25lIG9mIHRoZSBtb3N0IGluZmFtb3VzIHNoaXB3cmVja3MgaW4gaGlzdG9yeS4gV2hpbGUgdGhlcmUgd2FzIHNvbWUgZWxlbWVudCBvZiBsdWNrIGludm9sdmVkIGluIHN1cnZpdmluZyByZWxhdGVkIHRvIHRoZSBwYXNzZW5nZXIgY2xhc3MsZ2VuZGVyLGFnZSBldGMuSW4gdGhpcyBwcm9qZWN0IEkgYW0gZ29pbmcgdG8gYnVpbGQgYSBwcmVkaWN0aXZlIG1vZGVsIHdpdGggdGhlIHBhc3NlbmdlciBkYXRhKHRyYWluaW5nIGRhdGEpIGZyb20ga2FnZ2xlIHRoYXQgYW5zd2VycyB0aGUgcXVlc3Rpb246IndoYXQgc29ydHMgb2YgcGVvcGxlIHdlcmUgbW9yZSBsaWtlbHkgdG8gc3Vydml2ZT8iDQoNClRoZSBEYXRhIFNjaWVuY2UgcHJvamVjdCBjeWNsZSB3b3JrcyBhcyBPU01FTiBmcmFtZXdvcmsgd2hlcmUgdGhlIGZpcnN0IHN0ZXAgY29tZXMgYXM6DQoNCg0KMSkgT2J0YWluaW5nIFRoZSBEYXRhOiANCg0KVGhlIGZpcnN0IHN0ZXAgb2YgYSBEYXRhIFNjaWVuY2UgcHJvamVjdCBpcyBPYnRhaW5pbmcgb3IgR2F0aGVyaW5nIHRoZSBkYXRhLk5vdyBhcyB0aGUgZGF0YSBpcyBhbHJlYWR5IGF2YWlsYWJsZSBpbiB0aGUgd2Vic2l0ZSBJIGFtIGdvaW5nIHRvIHNldCB0aGUgZGlyZWN0b3J5IHRvIHRoZSBmb2xkZXIgd2hlcmUgdGhlIGRhdGEgaXMgZG93bmxvYWRlZC4NCmBgYHtyfQ0Kc2V0d2QoIkM6L1VzZXJzL3VzZXIvRGVza3RvcC9Gb2xkZXIvVElUQU5JQyIpDQpgYGANCk5vdyBpdCBpcyB0aW1lIHRvIGxvYWQgdGhlIGRhdGFzZXQgaW50byB0aGUgaW50ZXJmYWNlIHdpdGggdGhlICdyZWFkLmNzdigpJyBmdW5jdGlvbiB3aGVyZSAnaGVhZGVyPVQnIHRlbGxzIFIgdGhhdCB0aGUgZmlyc3QgbGluZSBjb250YWlucyBuYW1lIG9mIHRoZSBjb2x1bW5zIGFuZCAnc3RyaW5nQXNmYWN0b3JzPSBGQUxTRScgdGVsbHMgUiB0byBjb25zaWRlciB0aGUgc3RyaW5ncyBhcyBzdHJpbmcgdmFyaWFibGVzLiANCmBgYHtyfQ0KdGl0YW5pYy50cmFpbjwtIHJlYWQuY3N2KGZpbGUgPSAidHJhaW4uY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLCBoZWFkZXIgPSBUUlVFKQ0KdGl0YW5pYy50ZXN0PC0gcmVhZC5jc3YoZmlsZSA9ICJ0ZXN0LmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgaGVhZGVyID0gVFJVRSkNCmBgYA0KTmV4dCBJIGFtIGdvaW5nIHRvIGhhdmUgbG9vayBob3cgdGhlIGRhdGFzZXQgbG9va3MgbGlrZSB3aXRoIHRoZSAnaGVhZGVyKCknIGZ1bmNpb24gYXM6DQpgYGB7cn0NCmhlYWQodGl0YW5pYy50cmFpbikNCmBgYA0KYGBge3J9DQpoZWFkKHRpdGFuaWMudGVzdCkNCmBgYA0KRm9yIGVhY2ggb2YgdGhlIHBhc3NlbmdlciBJZCwnc3Vydml2YWwnIHJlZnJlcyBhcyAwPU5vLDE9WUVTLCANCidQY2xhc3MnIGRlZmluZXMgVGlja2V0IGNsYXNzLCAnQWdlJyBpbiB5ZWFycyBhcyBBZ2UgaXMgZnJhY3Rpb25hbCBpZiBsZXNzIHRoYW4gMS4gSWYgdGhlIGFnZSBpcyBlc3RpbWF0ZWQsIGlzIGl0IGluIHRoZSBmb3JtIG9mIHh4LjUsDQoNCg0KJ3NpYnNpcCcgZGVmaW5lcyBubyBvZiBzaWJsaW5ncy9zcG91c2VzIGFib2FyZCB0aGUgVGl0YW5pYyBhcyBzaWJzcDogVGhlIGRhdGFzZXQgZGVmaW5lcyBmYW1pbHkgcmVsYXRpb25zIGluIHRoaXMgd2F5Li4uDQpTaWJsaW5nID0gYnJvdGhlciwgc2lzdGVyLCBzdGVwYnJvdGhlciwgc3RlcHNpc3Rlcg0KU3BvdXNlID0gaHVzYmFuZCwgd2lmZSAobWlzdHJlc3NlcyBhbmQgZmlhbmPDqXMgd2VyZSBpZ25vcmVkKSwNCg0KDQoncGFyY2gnIGRlZmluZXMgbm8gb2YgcGFyZW50cy9jaGlsZHJlbiBhYm9hcmQgdGhlIFRpdGFuaWMgYXMgcGFyY2g6IFRoZSBkYXRhc2V0IGRlZmluZXMgZmFtaWx5IHJlbGF0aW9ucyBpbiB0aGlzIHdheS4uLg0KUGFyZW50ID0gbW90aGVyLCBmYXRoZXINCkNoaWxkID0gZGF1Z2h0ZXIsIHNvbiwgc3RlcGRhdWdodGVyLCBzdGVwc29uDQpTb21lIGNoaWxkcmVuIHRyYXZlbGxlZCBvbmx5IHdpdGggYSBuYW5ueSwgdGhlcmVmb3JlIHBhcmNoPTAgZm9yIHRoZW0sDQoNCidlbWJhcmtlZCcgZGVmaW5lcyBwb3J0IG9mIEVtYmFya2F0aW9uIGFzIEM9Q2hlcmJvdXJnLFE9IFF1ZWVuc3Rvd24sIFM9IFNvdXRoYW1wdG9uLg0KDQpOb3cgY2xlYXJseSB3ZSBjYW4gc2VlIHRoYXQgdGhlIHRlc3QgZGF0YXRzZXQgZG9lcyBub3QgaGF2ZSB0aGUgJ1N1cnZpdmVkJyBjb2x1bW4sIGV4YWN0bHkgd2hhdCB3ZSBoYXZlIHRvIHByZWRpY3QuU28gdGhlIFN1Ym1pc3NvbiBmaWxlIHdpbGwgaGF2ZSB0aGUgcGFzc2VuZ2VySWQgb2YgdGVzdCBkYXRhc2V0IGFuZCB3aGV0aGVyIHRoZXkgc3Vydml2ZWQgb3Igbm90LkxldCdzIGNvbnRpbnVlIHRvIHRoZSBuZXh0IHN0ZXAuDQogDQogMilTY3J1YmJpbmcgb2YgdGhlIERhdGFzZXQ6IENsZWFuaW5nIGFuZCBmaWx0ZXJpbmcgb2YgdGhlIGRhdGFzZXQuDQogICANCiAgTGV0J3MgY29tYmluZSB0aGUgdHJhaW5nIGFuZCB0ZXN0aW5nIGRhdGFzZXQgdGhhdCB3aWxsIHJlZHVjZSB0aGUgdGltZSBhcyB3ZWxsIGFzIHRoZSB0YXNrIG11Y2ggbW9yZSBlYXNpZXIuQXMgd2Uga25vdyB0aGUgdGVzdCBkYXRhIHNldCBkb2VzIG5vdCBoYXZlIHRoZSBzdXJ2aXZlZCBjb2x1bW4gc28gd2UgaGF2ZSB0byBpbmNsdWRlIHRoYXQgY29sdW1uLCBzbyBsZXQncyBmaWxsIHRoZSBzdXJ2aXZlZCBjb2x1bW4gd2l0aCAnTkEnLk5vdyB0byBhdm9pZCB0aGUgY29uZnVzaW9uIGluIGlkZW50aWZ5aW5nIHRoZSBkYXRhc2V0cyB3aGlsZSBzcGxpdHRpbmcgbGV0J3MgaW5zZXJ0IGEgbmV3IGNvbHVtbiBpbiBib3RoIHRoZSB0ZXN0IGFuZCB0aGUgdHJhaW5nIGRhdGFzZXQubGV0J3MgY2FsbCB0aGUgbmV3IGNvbHVtbiBhcyAnSXNUcmFpbnNldCcgd2l0aCAnVFJVRScgc3RyaW5nIGluIFRyYWluaW5nIGRhdGEgYW5kICdGQUxTRScgc3RyaW5nIGluIHRoZSBUZXN0IGRhdGEuTm93IGxldCdzIGNvbWJpbmUgdGhlbSB3aXRoICdyYmluZCcgZnVuY3Rpb24gdG8gc3RhcnQgdGhlIHNjcnViYmluZyBwcm9jZXNzLg0KYGBge3J9DQp0aXRhbmljLnRlc3QkU3Vydml2ZWQ8LSBOQQ0KdGl0YW5pYy50cmFpbiRJc1RyYWluc2V0PC0gVFJVRQ0KdGl0YW5pYy50ZXN0JElzVHJhaW5zZXQ8LSBGQUxTRQ0KdGl0YW5pYy5jb21iaW5lZDwtIHJiaW5kKHRpdGFuaWMudHJhaW4sdGl0YW5pYy50ZXN0KQ0Kc3RyKHRpdGFuaWMuY29tYmluZWQpDQpgYGANCiAgICANCmEpVGhlIGZpcnN0IHN0ZXAgdG8gZGF0YSBjbGVhbmluZyBpcyByZW1vdmluZyB1bndhbnRlZCBvYnNlcnZhdGlvbnMgZnJvbSB5b3VyIGRhdGFzZXQuVGhpcyBpbmNsdWRlcyAnZHVwbGljYXRlIG9yIGlycmVsZXZhbnQgb2JzZXJ2YXRpb25zJy0tLSBhcyB3ZSBjYW4gc2VlIGZyb20gdGhlICd0aXRhbmljLmNvbWJpbmVkJyBkYXRhZnJhbWUgdGhhdCBlYWNoIHVuaXF1ZSBwYXNzZW5nZXJJZCBhbGwgdGhlIDEzIHZhcmlhYmxlcyBhcmUgZ2l2ZW4uIHNvIG5vIG5lZWQgb2YgY2hlY2tpbmcgZm9yIGR1cGxpY2F0ZSBkYXRhIGFuZCBpcnJlbGV2YW50IG9ic2VydmF0aW9ucy4NCg0KYilUaGUgbmV4dCBjb21lcyBhcyAnRml4aW5nIFN0cnVjdHVyYWwgZXJyb3JzJy0tLSBhcyB3ZSBjYW4gc2VlIGFsbCB0aGUgMTMgdmFyaWFibGVzIGhhcyB1bnFpdWUganVzdGlmaWVkIG5hbWUgaGVuY2UgdGhlcmVpcyB6ZXJvIGNoYW5jZSBvZiB0aGUgdHlwb3MgYW5kIGluY29uc2lzdGVudCBjYXBpdGFsaXphdGlvbiBhbmQgbWlzbGFiZWxlZCBjbGFzc2VzLg0KDQpjKXRoZSAzcmQgc3RlcCBpcyAnRmlsdGVyIFVud2FudGVkIE91dGxpZXJzJy0tLSBPdXRsaWVycyBjYW4gY2F1c2UgcHJvYmxlbXMgd2l0aCBjZXJ0YWluIHR5cGVzIG9mIG1vZGVscy4gRm9yIGV4YW1wbGUsIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyBhcmUgbGVzcyByb2J1c3QgdG8gb3V0bGllcnMgdGhhbiBkZWNpc2lvbiB0cmVlIG1vZGVscy5XZSBjYW7igJl0IHN0cmVzcyB0aGlzIGVub3VnaDogYXMgd2UgZG8gbm90IGhhdmUgYSBnb29kIHJlYXNvbiBmb3IgcmVtb3ZpbmcgYW4gb3V0bGllciwgc3VjaCBhcyBzdXNwaWNpb3VzIG1lYXN1cmVtZW50cyB0aGF0IGFyZSB1bmxpa2VseSB0byBiZSByZWFsIGRhdGEuIHNvIHRoaXMgc3RlcCBjYW4gYWxzbyBiZSBza2lwcGVkLg0KDQpkKVRoZSBtb3N0IGltcG9ydGFudCBzdGVwIGlzICdIYW5kbGUgbWlzc2luZyBkYXRhJy0tLSBUaGUgMiBtb3N0IGNvbW1vbmx5IHJlY29tbWVuZGVkIHdheXMgb2YgZGVhbGluZyB3aXRoIG1pc3NpbmcgZGF0YSBpcyANCiANCiAxKURyb3BwaW5nIG9ic2VydmF0aW9ucyB0aGF0IGhhdmUgbWlzc2luZyB2YWx1ZXMuDQoNCiAyKUltcHV0aW5nIHRoZSBtaXNzaW5nIHZhbHVlcyBiYXNlZCBvbiBvdGhlciBvYnNlcnZhdGlvbnMuDQogDQogbm93LFRoZSBiZXN0IHdheSB0byBoYW5kbGUgbWlzc2luZyBkYXRhIGZvciBjYXRlZ29yaWNhbCBmZWF0dXJlcyBpcyB0byBzaW1wbHkgbGFiZWwgdGhlbSBhcyDigJlNaXNzaW5n4oCZISBhbmQgRm9yIG1pc3NpbmcgbnVtZXJpYyBkYXRhLCB5b3Ugc2hvdWxkIGZsYWcgYW5kIGZpbGwgdGhlIHZhbHVlcy5CeSB1c2luZyB0aGlzIHRlY2huaXF1ZSBvZiBmbGFnZ2luZyBhbmQgZmlsbGluZywgeW91IGFyZSBlc3NlbnRpYWxseSBhbGxvd2luZyB0aGUgYWxnb3JpdGhtIHRvIGVzdGltYXRlIHRoZSBvcHRpbWFsIGNvbnN0YW50IGZvciBtaXNzaW5nbmVzcywgaW5zdGVhZCBvZiBqdXN0IGZpbGxpbmcgaXQgaW4gd2l0aCB0aGUgbWVhbi4NCiB0byBmaW5kIG91dCB0aGUgbWlzc2luZyB2YXJpYmxlcyB3ZSB3aWxsIHVzZSB0aGUgZnVuY3Rpb24naXMubmEnIG9uIGRpZmZlcmVudCB2YXJpYWJsZXMgYXMgd2UgY2FuIHNlZQ0KYGBge3J9DQp0YWJsZShpcy5uYSh0aXRhbmljLmNvbWJpbmVkKSkNCmBgYA0KIG5vdyBsZXQncyBsb29rIGludG8gZWFjaCBjb2x1bW4gYXMNCmBgYHtyfQ0KdGFibGUoaXMubmEodGl0YW5pYy5jb21iaW5lZCRBZ2UpKQ0KYGBgDQoyNjMgdmFsdWVzIG91dCBvZiAxMzA5IHdlcmUgbWlzc2luZyB0aGlzIHdob2xlIHRpbWUsIHRoYXTigJlzIGEgd2hvcHBpbmcgMjAlISBzbyBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgY29tYmluZWQgZGF0YWZyYW1l4oCZcyBhZ2UgdmFyaWFibGUgdG8gc2VlIHdoYXQgd2XigJlyZSB1cCBhZ2FpbnN0Og0KYGBge3J9DQpzdW1tYXJ5KHRpdGFuaWMuY29tYmluZWQkQWdlKQ0KYGBgDQogU28gbGV04oCZcyBncm93IGEgdHJlZSBvbiB0aGUgc3Vic2V0IG9mIHRoZSBkYXRhIHdpdGggdGhlIGFnZSB2YWx1ZXMgYXZhaWxhYmxlLCBhbmQgdGhlbiByZXBsYWNlIHRob3NlIHRoYXQgYXJlIG1pc3Npbmc6IHJwYXJ0IGhhcyBhIGdyZWF0IGFkdmFudGFnZSBpbiB0aGF0IGl0IGNhbiB1c2Ugc3Vycm9nYXRlIHZhcmlhYmxlcyB3aGVuIGl0IGVuY291bnRlcnMgYW4gTkEgdmFsdWUuDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoJ3JwYXJ0JykNCmxpYnJhcnkocnBhcnQpDQpgYGANCg0KYGBge3J9DQphZ2UuZml0PC0gcnBhcnQoQWdlfiBQY2xhc3MgKyBTZXggKyBTaWJTcCArIFBhcmNoICsgRmFyZSArIEVtYmFya2VkLCBkYXRhPXRpdGFuaWMuY29tYmluZWRbIWlzLm5hKHRpdGFuaWMuY29tYmluZWQkQWdlKSxdLG1ldGhvZD0iYW5vdmEiKQ0KdGl0YW5pYy5jb21iaW5lZCRBZ2VbaXMubmEodGl0YW5pYy5jb21iaW5lZCRBZ2UpXSA8LSBwcmVkaWN0KGFnZS5maXQsIHRpdGFuaWMuY29tYmluZWRbaXMubmEodGl0YW5pYy5jb21iaW5lZCRBZ2UpLF0pDQp0YWJsZShpcy5uYSh0aXRhbmljLmNvbWJpbmVkJEFnZSkpDQpgYGANCm5vdyB3ZSBjYW4gc2VlIGFsbCB0aGUgbWlzc2luZyB2YWx1ZXMgb2YgYWdlIGNvbHVtbiBpcyByZXBsYWNlZCBieSB0aGUgcHJlZGljdGVkIHZhbHVlcyBmcm9tIHRoZSBkZWNpc2lvbiB0cmVlLg0KIFRoZXJlIGlzIHNvbWUgbWlzaW5nIHZhbHVlIHRoYXQgd2UgYXJlIG5vdCBhYmxlIGZpbmQgb3V0IHdpdGggdGhlICdpcy5uYScsIHByb2JhYmx5IHRoYXQgYmVhY3VzZSBvZiAnICcuU28gbGV0J3MgZmluZCBvdXQNCmBgYHtyfQ0KdGFibGUodGl0YW5pYy5jb21iaW5lZCRFbWJhcmtlZCkNCmBgYA0KIG5vdyBhcyB3ZSBjYW4gc2VlIHR3byBvYnNlcnZhdGlvbnMgZG8gbm90IGhhdmUgdGhlIHBvcnQgb2YgZW1iYXJrdGlvbiBzcGVjaWZpZWQgdGhhdCdzIHdoeSB0aGV5IGFyZSBibGFuay4gDQogICAgV2hpbGUgYSBibGFuayB3b3VsZG7igJl0IGJlIGEgcHJvYmxlbSBmb3Igb3VyIG1vZGVsIGxpa2UgYW4gTkEgd291bGQgYmUsIHNpbmNlIHdl4oCZcmUgY2xlYW5pbmcgYW55aG93LCBsZXTigJlzIGdldCByaWQgb2YgaXQuIEJlY2F1c2UgaXTigJlzIHNvIGZldyBvYnNlcnZhdGlvbnMgYW5kIHN1Y2ggYSBsYXJnZSBtYWpvcml0eSBib2FyZGVkIGluIFNvdXRoYW1wdG9uLCBsZXTigJlzIGp1c3QgcmVwbGFjZSB0aG9zZSB0d28gd2l0aCDigJxT4oCdLiBGaXJzdCB3ZSBuZWVkIHRvIGZpbmQgb3V0IHdobyB0aGV5IGFyZSB0aG91Z2ghIFdlIGNhbiB1c2Ugd2hpY2ggZm9yIHRoaXM6DQpgYGB7cn0NCndoaWNoKHRpdGFuaWMuY29tYmluZWQkRW1iYXJrZWQ9PSAnJykNCmBgYA0KIFRoaXMgZ2l2ZXMgdXMgdGhlIGluZGV4ZXMgb2YgdGhlIGJsYW5rIGZpZWxkcy4gVGhlbiB3ZSBzaW1wbHkgcmVwbGFjZSB0aG9zZSB0d28sIGFuZCBlbmNvZGUgaXQgYXMgYSBmYWN0b3I6DQpgYGB7cn0NCnRpdGFuaWMuY29tYmluZWQkRW1iYXJrZWRbYyg2Miw4MzApXSA9ICJTIg0KdGl0YW5pYy5jb21iaW5lZCRFbWJhcmtlZCA8LSBmYWN0b3IodGl0YW5pYy5jb21iaW5lZCRFbWJhcmtlZCkNCmBgYA0KIFRoZSBvdGhlciB2YXJpYWJsZSB3aXRoIG1pc3NpbmcgdmFsdWVzIGFyZSBGYXJlIHNvIGxldCdzIHRha2UgYSBsb29rIGFuZCBmaW5kIHRoZSBvYnNlcnZhdGlvbiBhbmQgcmVwbGFjZSBpdCB3aXRoIHRoZSBtZWFuIG9mIGZhcmUuDQpgYGB7cn0NCnN1bW1hcnkodGl0YW5pYy5jb21iaW5lZCRGYXJlKQ0KYGBgDQoNCmBgYHtyfQ0Kd2hpY2goaXMubmEodGl0YW5pYy5jb21iaW5lZCRGYXJlKSkNCmBgYA0KYGBge3J9DQp0aXRhbmljLmNvbWJpbmVkJEZhcmVbMTA0NF08LSBtZWRpYW4odGl0YW5pYy5jb21iaW5lZCRGYXJlLG5hLnJtPVRSVUUpDQpgYGANCiBPa2F5LiBPdXIgZGF0YWZyYW1lIGlzIG5vdyBjbGVhcmVkIG9mIGFsbCBOQXMuDQogDQogDQozKUV4cGxvcmUgYW5kIHZpenVhbGl6ZSB0aGUgZGF0YXNldCB0byBmaW5kIHBhdHRlcm5zIGFuZCBGZWF0dXJlIEVuZ2luZWVyaW5nOg0KDQpGZWF0dXJlIGVuZ2luZWVyaW5nIGhhcyBiZWVuIGRlc2NyaWJlZCBhcyBlYXNpbHkgdGhlIG1vc3QgaW1wb3J0YW50IGZhY3RvciBpbiBkZXRlcm1pbmluZyB0aGUgc3VjY2VzcyBvciBmYWlsdXJlIG9mIHlvdXIgcHJlZGljdGl2ZSBtb2RlbC5JbiB0aGUgVGl0YW5pYyBjb21wZXRpdGlvbiBpdCBjb3VsZCBtZWFuIGNob3BwaW5nLCBhbmQgY29tYmluaW5nIGRpZmZlcmVudCBhdHRyaWJ1dGVzIHRoYXQgd2Ugd2VyZSBnaXZlbi4NCiAgDQogIHRoZSB0aWNrZXQgbnVtYmVyLCBjYWJpbiwgYW5kIG5hbWUgd2VyZSBhbGwgdW5pcXVlIHRvIGVhY2ggcGFzc2VuZ2VyOyBwZXJoYXBzIHBhcnRzIG9mIHRob3NlIHRleHQgc3RyaW5ncyBjb3VsZCBiZSBleHRyYWN0ZWQgdG8gYnVpbGQgYSBuZXcgcHJlZGljdGl2ZSBhdHRyaWJ1dGUuIExldOKAmXMgc3RhcnQgd2l0aCB0aGUgbmFtZSBmaWVsZC4NCmBgYHtyfQ0KIHRpdGFuaWMuY29tYmluZWQkTmFtZSA8LSBhcy5jaGFyYWN0ZXIodGl0YW5pYy5jb21iaW5lZCROYW1lKQ0KIHRpdGFuaWMuY29tYmluZWQkTmFtZVsxXQ0KYGBgDQogIE5pY2VseSwgd2Ugc2VlIHRoYXQgdGhlcmUgaXMgYSBjb21tYSByaWdodCBhZnRlciB0aGUgcGVyc29u4oCZcyBsYXN0IG5hbWUsIGFuZCBhIGZ1bGwgc3RvcCBhZnRlciB0aGVpciB0aXRsZS4gV2UgY2FuIGVhc2lseSB1c2UgdGhlIGZ1bmN0aW9uIHN0cnNwbGl0LCB3aGljaCBzdGFuZHMgZm9yIHN0cmluZyBzcGxpdCwgdG8gYnJlYWsgYXBhcnQgb3VyIG9yaWdpbmFsIG5hbWUgb3ZlciB0aGVzZSB0d28gc3ltYm9scy4gTGV04oCZcyB0cnkgaXQgb3V0IG9uIE1yLiBCcmF1bmQ6DQpgYGB7cn0NCnN0cnNwbGl0KHRpdGFuaWMuY29tYmluZWQkTmFtZVsxXSwgc3BsaXQgPSAnWywuXScpDQpgYGANCiAgIExldOKAmXMgZ28gYSBsZXZlbCBkZWVwZXIgaW50byB0aGUgaW5kZXhpbmcgbWVzcyBhbmQgZXh0cmFjdCB0aGUgdGl0bGUuDQpgYGB7cn0NCnN0cnNwbGl0KHRpdGFuaWMuY29tYmluZWQkTmFtZVsxXSwgc3BsaXQgPSAnWywuXScpW1sxXV1bMl0NCmBgYA0KV2UgZmVlZCBzYXBwbHkgb3VyIHZlY3RvciBvZiBuYW1lcyBhbmQgb3VyIGZ1bmN0aW9uIHRoYXQgd2UganVzdCBjYW1lIHVwIHdpdGguIEl0IHJ1bnMgdGhyb3VnaCB0aGUgcm93cyBvZiB0aGUgdmVjdG9yIG9mIG5hbWVzLCBhbmQgc2VuZHMgZWFjaCBuYW1lIHRvIHRoZSBmdW5jdGlvbi5UaGUgcmVzdWx0cyBvZiBhbGwgdGhlc2Ugc3RyaW5nIHNwbGl0cyBhcmUgYWxsIGNvbWJpbmVkIHVwIGludG8gYSB2ZWN0b3IgYXMgb3V0cHV0IGZyb20gdGhlIHNhcHBseSBmdW5jdGlvbiwgd2hpY2ggd2UgdGhlbiBzdG9yZSB0byBhIG5ldyBjb2x1bW4gaW4gb3VyIG9yaWdpbmFsIGRhdGFmcmFtZSwgY2FsbGVkIFRpdGxlLg0KYGBge3J9DQp0aXRhbmljLmNvbWJpbmVkJFRpdGxlIDwtIHNhcHBseSh0aXRhbmljLmNvbWJpbmVkJE5hbWUsIEZVTj1mdW5jdGlvbih4KSB7c3Ryc3BsaXQoeCwgc3BsaXQ9J1ssLl0nKVtbMV1dWzJdfSkNCnRpdGFuaWMuY29tYmluZWQkVGl0bGUgPC0gc3ViKCcgJywgJycsIHRpdGFuaWMuY29tYmluZWQkVGl0bGUpDQp0YWJsZSh0aXRhbmljLmNvbWJpbmVkJFRpdGxlKQ0KYGBgDQoNClRoZXJlIGFyZSBhIGZldyB2ZXJ5IHJhcmUgdGl0bGVzIGluIGhlcmUgdGhhdCB3b27igJl0IGdpdmUgb3VyIG1vZGVsIG11Y2ggdG8gd29yayB3aXRoLCBzbyBsZXTigJlzIGNvbWJpbmUgYSBmZXcgb2YgdGhlIG1vc3QgdW51c3VhbCBvbmVzLiANCmBgYHtyfQ0KdGl0YW5pYy5jb21iaW5lZCRUaXRsZVt0aXRhbmljLmNvbWJpbmVkJFRpdGxlICVpbiUgYygnTW1lJywgJ01sbGUnKV0gPC0gJ01sbGUnDQp0aXRhbmljLmNvbWJpbmVkJFRpdGxlW3RpdGFuaWMuY29tYmluZWQkVGl0bGUgJWluJSBjKCdDYXB0JywgJ0RvbicsICdNYWpvcicsICdTaXInKV0gPC0gJ1NpcicNCnRpdGFuaWMuY29tYmluZWQkVGl0bGVbdGl0YW5pYy5jb21iaW5lZCRUaXRsZSAlaW4lIGMoJ0RvbmEnLCAnTGFkeScsICd0aGUgQ291bnRlc3MnLCAnSm9ua2hlZXInKV0gPC0gJ0xhZHknDQpgYGANCnN1cHBvc2Ugd2hlbiB3ZSBjb21iaW5lIHR3byB0aXRsZXMgdGhlICclaW4lJyBmdW5jdGlvbiBjaGVja3MgaWYgYW55IG9mIHRoZSBleGlzdGluZyB0aXRsZXMgaW4gdGhlIGVudGlyZSBUaXRsZSBjb2x1bW4gbWF0Y2ggZWl0aGVyIG9mIHRoZW0gYW5kIHRoZW4gdGhlIGMoKSBmdW5jdGlvbiBzdG9yZXMgdG8gdGhlIG9wZXJhdG9yLg0KICAgT3VyIGZpbmFsIHN0ZXAgaXMgdG8gY2hhbmdlIHRoZSB2YXJpYWJsZSB0eXBlIGJhY2sgdG8gYSBmYWN0b3IsIGFzIHRoZXNlIGFyZSBlc3NlbnRpYWxseSBjYXRlZ29yaWVzIHRoYXQgd2UgaGF2ZSBjcmVhdGVkOg0KYGBge3J9DQp0aXRhbmljLmNvbWJpbmVkJFRpdGxlIDwtIGZhY3Rvcih0aXRhbmljLmNvbWJpbmVkJFRpdGxlKQ0KYGBgDQogICANCiAgIFdl4oCZcmUgZG9uZSB3aXRoIHRoZSBwYXNzZW5nZXLigJlzIHRpdGxlIG5vdy4gV2hhdCBlbHNlIGNhbiB3ZSB0aGluayB1cD8gV2VsbCwgdGhlcmXigJlzIHRob3NlIHR3byB2YXJpYWJsZXMgU2liU2IgYW5kIFBhcmNoIHRoYXQgaW5kaWNhdGUgdGhlIG51bWJlciBvZiBmYW1pbHkgbWVtYmVycyB0aGUgcGFzc2VuZ2VyIGlzIHRyYXZlbGxpbmcgd2l0aC4gU2VlbXMgcmVhc29uYWJsZSB0byBhc3N1bWUgdGhhdCBhIGxhcmdlIGZhbWlseSBtaWdodCBoYXZlIHRyb3VibGUgdHJhY2tpbmcgZG93biBsaXR0bGUgSm9obm55IGFzIHRoZXkgYWxsIHNjcmFtYmxlIHRvIGdldCBvZmYgdGhlIHNpbmtpbmcgc2hpcCwgc28gbGV04oCZcyBjb21iaW5lIHRoZSB0d28gdmFyaWFibGVzIGludG8gYSBuZXcgb25lLCBGYW1pbHlTaXplOg0KYGBge3J9DQp0aXRhbmljLmNvbWJpbmVkJEZhbWlseVNpemUgPC0gdGl0YW5pYy5jb21iaW5lZCRTaWJTcCArIHRpdGFuaWMuY29tYmluZWQkUGFyY2ggKyAxDQpgYGANCiAgIFdlIGp1c3QgYWRkIHRoZSBudW1iZXIgb2Ygc2libGluZ3MsIHNwb3VzZXMsIHBhcmVudHMgYW5kIGNoaWxkcmVuIHRoZSBwYXNzZW5nZXIgaGFkIHdpdGggdGhlbSwgYW5kIHBsdXMgb25lIGZvciB0aGVpciBvd24gZXhpc3RlbmNlIG9mIGNvdXJzZSwgYW5kIGhhdmUgYSBuZXcgdmFyaWFibGUgaW5kaWNhdGluZyB0aGUgc2l6ZSBvZiB0aGUgZmFtaWx5IHRoZXkgdHJhdmVsbGVkIHdpdGguDQogICANCiAgIHdlIGp1c3QgdGhvdWdodCBhYm91dCBhIGxhcmdlIGZhbWlseSBoYXZpbmcgaXNzdWVzIGdldHRpbmcgdG8gbGlmZWJvYXRzIHRvZ2V0aGVyLCBidXQgbWF5YmUgc3BlY2lmaWMgZmFtaWxpZXMgaGFkIG1vcmUgdHJvdWJsZSB0aGFuIG90aGVycz8gbm93IHN1cHBvc2UgdGhlcmUgYXJlIHRocmVlIEpvaG5zb25zIGluIGEgZmFtaWx5IHdpdGggc2l6ZSAzLCBhbmQgYW5vdGhlciB0aHJlZSBwcm9iYWJseSB1bnJlbGF0ZWQgSm9obnNvbnMgYWxsIHRyYXZlbGxpbmcgc29sby5Db21iaW5pbmcgdGhlIFN1cm5hbWUgd2l0aCB0aGUgZmFtaWx5IHNpemUgdGhvdWdoIHNob3VsZCByZW1lZHkgdGhpcyBjb25jZXJuLg0KYGBge3J9DQp0aXRhbmljLmNvbWJpbmVkJFN1cm5hbWUgPC0gc2FwcGx5KHRpdGFuaWMuY29tYmluZWQkTmFtZSwgRlVOPWZ1bmN0aW9uKHgpIHtzdHJzcGxpdCh4LCBzcGxpdD0nWywuXScpW1sxXV1bMV19KQ0KdGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRCA8LSBwYXN0ZShhcy5jaGFyYWN0ZXIodGl0YW5pYy5jb21iaW5lZCRGYW1pbHlTaXplKSwgdGl0YW5pYy5jb21iaW5lZCRTdXJuYW1lLCBzZXA9IiIpDQpgYGANCiAgIEJ1dCB0aG9zZSB0aHJlZSBzaW5nbGUgSm9obnNvbnMgd291bGQgYWxsIGhhdmUgdGhlIHNhbWUgRmFtaWx5IElELiAgbGV04oCZcyBrbm9jayBvdXQgYW55IGZhbWlseSBzaXplIG9mIHR3byBvciBsZXNzIGFuZCBjYWxsIGl0IGEg4oCcc21hbGzigJ0gZmFtaWx5LiBUaGlzIHdvdWxkIGZpeCB0aGUgSm9obnNvbiBwcm9ibGVtIHRvby4NCmBgYHtyfQ0KdGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRFt0aXRhbmljLmNvbWJpbmVkJEZhbWlseVNpemUgPD0gMl0gPC0gJ1NtYWxsJw0KdGFibGUodGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRCkNCmBgYA0KICAgVGhlcmXigJlzIHBsZW50eSBvZiBGYW1pbHlJRHMgd2l0aCBvbmx5IG9uZSBvciB0d28gbWVtYmVycywgZXZlbiB0aG91Z2ggd2Ugd2FudGVkIG9ubHkgZmFtaWx5IHNpemVzIG9mIDMgb3IgbW9yZS4gUGVyaGFwcyBzb21lIGZhbWlsaWVzIGhhZCBkaWZmZXJlbnQgbGFzdCBuYW1lcywgYnV0IHdoYXRldmVyIHRoZSBjYXNlLGxldCdzIHN0b3JlIHRoZSB2YWx1ZXMgaW4gYSBkYXRhZnJhbWUgYW5kIHN1YnNldCB0aGlzIGRhdGFmcmFtZSB0byBzaG93IG9ubHkgdGhvc2UgdW5leHBlY3RlZGx5IHNtYWxsIEZhbWlseUlEIGdyb3Vwcy4NCmBgYHtyfQ0KZmFtSURzIDwtIGRhdGEuZnJhbWUodGFibGUodGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRCkpDQpmYW1JRHMgPC0gZmFtSURzW2ZhbUlEcyRGcmVxIDw9IDIsXQ0KYGBgDQogICBXZSB0aGVuIG5lZWQgdG8gb3ZlcndyaXRlIGFueSBmYW1pbHkgSURzIGluIG91ciBkYXRhc2V0IGZvciBncm91cHMgdGhhdCB3ZXJlIG5vdCBjb3JyZWN0bHkgaWRlbnRpZmllZCBhbmQgZmluYWxseSBjb252ZXJ0IGl0IHRvIGEgZmFjdG9yOg0KYGBge3J9DQp0aXRhbmljLmNvbWJpbmVkJEZhbWlseUlEW3RpdGFuaWMuY29tYmluZWQkRmFtaWx5SUQgJWluJSBmYW1JRHMkVmFyMV0gPC0gJ1NtYWxsJw0KdGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRCA8LSBmYWN0b3IodGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRCkNCmBgYA0KICAgd2l0aCB0aGlzIHRoZSBmZWF0dXJlIGVuZ2luZWVyaW5nIHBhcnQgaXMgbm93IGNvbXBsZXRlZC4NCiAgIA0KICAgDQo0KSBNb2RlbGluZyB0aGUgRGF0YXNldDogYXBwbHlpbmcgcHJvcGVyIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdG0gYXMgcGVyIHJlcXVpcmVtZW50Lg0KDQpOb3cgd2UgY2FuIGFwcHJvYWNoIHRoZSBwcmVkaWN0aW9uIG1vZGVsaW5nIGJ5IGFwcGx5aW5nIHNpbXBsZSBkZWNpc2lvbiB0cmVlcyBidXQgdGhleSBhcmUgYmlhc2VkIHRvIGZhdm91ciBmYWN0b3JzIHdpdGggbWFueSBsZXZlbHMuVGhpcyB3YXkgdGhlIGRlY2lzaW9uIG5vZGUgY2FuIGNob3AgYW5kIGNoYW5nZSB0aGUgZGF0YSBpbnRvIHRoZSBiZXN0IHdheSBwb3NzaWJsZSBjb21iaW5hdGlvbiBmb3IgcHVyaXR5IG9mIHRoZSBmb2xsb3dpbmcgbm9kZXMuVGhlIGJpYXMgdG93YXJkcyBtYW55LWxldmVsbGVkIGZhY3RvcnMgd29u4oCZdCBnbyBhd2F5IGVpdGhlciwgYW5kIHRoZSBvdmVyZml0dGluZyBwcm9ibGVtIHdpbGwgcmlzZS5SYW5kb20gRm9yZXN0IGFsZ29yaXRobSBoYXMgYSBmZXcgcmVzdHJpY3Rpb25zIHRoYXQgd2UgZGlkIG5vdCBoYXZlIHdpdGggb3VyIGRlY2lzaW9uIHRyZWVzIHRoYXQgaXMgSW5zdGVhZCBvZiBsb29raW5nIGF0IHRoZSBlbnRpcmUgcG9vbCBvZiBhdmFpbGFibGUgdmFyaWFibGVzLCBSYW5kb20gRm9yZXN0cyB0YWtlIG9ubHkgYSBzdWJzZXQgb2YgdGhlbSwgdHlwaWNhbGx5IHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgbnVtYmVyIGF2YWlsYWJsZS5UaGlzIHdheSwgbWFueSBvZiB0aGUgdHJlZXMgd29u4oCZdCBldmVuIGhhdmUgdGhlIGdlbmRlciB2YXJpYWJsZSBhdmFpbGFibGUgYXQgdGhlIGZpcnN0IHNwbGl0LCBhbmQgbWlnaHQgbm90IGV2ZW4gc2VlIGl0IHVudGlsIHNldmVyYWwgbm9kZXMgZGVlcC4NCg0KICBSYW5kb20gRm9yZXN0cyBpbiBSIGNhbiBvbmx5IGRpZ2VzdCBmYWN0b3JzIHdpdGggdXAgdG8gMzIgbGV2ZWxzLiBPdXIgRmFtaWx5SUQgdmFyaWFibGUgaGFkIGFsbW9zdCBkb3VibGUgdGhhdC53ZeKAmWxsIGNvcHkgdGhlIEZhbWlseUlEIGNvbHVtbiB0byBhIG5ldyB2YXJpYWJsZSwgRmFtaWx5SUQyLCBhbmQgdGhlbiBjb252ZXJ0IGl0IGZyb20gYSBmYWN0b3IgYmFjayBpbnRvIGEgY2hhcmFjdGVyIHN0cmluZyB3aXRoIGFzLmNoYXJhY3RlcigpLiBXZSBjYW4gdGhlbiBpbmNyZWFzZSBvdXIgY3V0LW9mZiB0byBiZSBhIOKAnFNtYWxs4oCdIGZhbWlseSBmcm9tIDIgdG8gMyBwZW9wbGUuIFRoZW4gd2UganVzdCBjb252ZXJ0IGl0IGJhY2sgdG8gYSBmYWN0b3IgYW5kIHdl4oCZcmUgZG9uZToNCmBgYHtyfQ0KIHRpdGFuaWMuY29tYmluZWQkRmFtaWx5SUQyIDwtIHRpdGFuaWMuY29tYmluZWQkRmFtaWx5SUQNCiB0aXRhbmljLmNvbWJpbmVkJEZhbWlseUlEMiA8LSBhcy5jaGFyYWN0ZXIodGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRDIpDQogdGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRDJbdGl0YW5pYy5jb21iaW5lZCRGYW1pbHlTaXplIDw9IDNdIDwtICdTbWFsbCcNCiB0aXRhbmljLmNvbWJpbmVkJEZhbWlseUlEMiA8LSBmYWN0b3IodGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRDIpDQogdGFibGUodGl0YW5pYy5jb21iaW5lZCRGYW1pbHlJRDIpDQpgYGANCk5vdyB3ZSBhcmUgYXQgMjIgbGV2ZWxzIHNvIHdl4oCZcmUgZ29vZCB0byBzcGxpdCB0aGUgdGVzdCBhbmQgdHJhaW4gc2V0cyBhbmQgZGVsZXRlIHRoZSBzdXJ2aXZlZCB2YXJpYWJsZS4NCmBgYHtyfQ0KdGl0YW5pYy50cmFpbjwtIHRpdGFuaWMuY29tYmluZWRbdGl0YW5pYy5jb21iaW5lZCRJc1RyYWluc2V0PT0gVFJVRSxdDQp0aXRhbmljLnRlc3Q8LXRpdGFuaWMuY29tYmluZWRbdGl0YW5pYy5jb21iaW5lZCRJc1RyYWluc2V0PT1GQUxTRSxdDQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQpsaWJyYXJ5KGRwbHlyKQ0KdGl0YW5pYy50ZXN0PC0gc2VsZWN0KHRpdGFuaWMudGVzdCwtU3Vydml2ZWQpDQpgYGANCiANCiBOb3cgbGV0J3MgZ3JvdyBhIFJhbmRvbSBGb3Jlc3QuIEluc3RhbGwgYW5kIGxvYWQgdGhlIHBhY2thZ2UgcmFuZG9tRm9yZXN0Og0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQpgYGANCiB0aGUgcmFuZG9tZm9yZXN0IGhhcyByYW5kb21uZXNzIGFzIGRpc2N1c3NlZCBlYXJsaWVyIHNvIGl0J3MgYSBnb29kIHByYWN0aWNlIHRvIHNldCBhIHNlZWQgdG8gZ2V0IHRoZSByZXByb2R1Y2libGUgcmVzdWx0cyBmbyB0aGUgbmV4dCB0aW1lIHdlIGNvZGUgdXAuaGUgbnVtYmVyIGluc2lkZSBpc27igJl0IGltcG9ydGFudCwgeW91IGp1c3QgbmVlZCB0byBlbnN1cmUgeW91IHVzZSB0aGUgc2FtZSBzZWVkIG51bWJlciBlYWNoIHRpbWUgc28gdGhhdCB0aGUgc2FtZSByYW5kb20gbnVtYmVycyBhcmUgZ2VuZXJhdGVkIGluc2lkZSB0aGUgUmFuZG9tIEZvcmVzdCBmdW5jdGlvbi4NCmBgYHtyfQ0Kc2V0LnNlZWQoNDE1KQ0KYGBgDQogSW5zdGVhZCBvZiBzcGVjaWZ5aW5nIG1ldGhvZD0iY2xhc3MiIGFzIHdpdGggcnBhcnQsIHdlIGZvcmNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IG91ciBjbGFzc2lmaWNhdGlvbiBieSB0ZW1wb3JhcmlseSBjaGFuZ2luZyBvdXIgdGFyZ2V0IHZhcmlhYmxlIHRvIGEgZmFjdG9yIHdpdGggb25seSB0d28gbGV2ZWxzIHVzaW5nIGFzLmZhY3RvcigpLiBUaGUgaW1wb3J0YW5jZT1UUlVFIGFyZ3VtZW50IGFsbG93cyB1cyB0byBpbnNwZWN0IHZhcmlhYmxlIGltcG9ydGFuY2UgYXMgd2XigJlsbCBzZWUsIGFuZCB0aGUgbnRyZWUgYXJndW1lbnQgc3BlY2lmaWVzIGhvdyBtYW55IHRyZWVzIHdlIHdhbnQgdG8gZ3Jvdy4NCmBgYHtyfQ0KZml0PC0gcmFuZG9tRm9yZXN0KGFzLmZhY3RvcihTdXJ2aXZlZCl+IFBjbGFzcytTZXgrQWdlK1NpYlNwK1BhcmNoK0ZhcmUrRW1iYXJrZWQrVGl0bGUrRmFtaWx5U2l6ZStGYW1pbHlJRDIsZGF0YSA9IHRpdGFuaWMudHJhaW4saW1wb3J0YW5jZT1UUlVFLCBudHJlZT0yMDAwKQ0KYGBgDQogDQogVGhlIG51bWJlciBvZiB0cmVlcyB3ZSB3YW50IHRvIGdyb3cgZGVwZW5kcyBvbiB0aGUgc2l6ZSBvZiB0aGUgZGF0YXNldCx3ZSBjYW4gZ3JvdyBhIGxhcmdlIG51bWJlciBvZiB0cmVlcyBhbmQgbm90IHdvcnJ5IHRvbyBtdWNoIGFib3V0IHRoZWlyIGNvbXBsZXhpdHksIGl0IHdpbGwgc3RpbGwgcnVuIHByZXR0eSBmYXN0LlNvIGxldOKAmXMgbG9vayBhdCB3aGF0IHZhcmlhYmxlcyB3ZXJlIGltcG9ydGFudDoNCiANCmBgYHtyfQ0KdmFySW1wUGxvdChmaXQpDQpgYGANCiANCiBSYW5kb20gRm9yZXN0cyBkb2VzbuKAmXQganVzdCB3YXN0ZSB0aG9zZSDigJxvdXQtb2YtYmFn4oCdIChPT0IpIG9ic2VydmF0aW9ucywgaXQgdXNlcyB0aGVtIHRvIHNlZSBob3cgd2VsbCBlYWNoIHRyZWUgcGVyZm9ybXMgb24gdW5zZWVuIGRhdGEuDQogDQogVGhlIHByZWRpY3Rpb24gZnVuY3Rpb24gd29ya3Mgc2ltaWxhcmx5IHRvIGRlY2lzaW9uIHRyZWVzIGFzIGFsbCAyMDAwIHRyZWVzIG5lZWQgdG8gbWFrZSB0aGVpciBjbGFzc2lmaWNhdGlvbnMgYW5kIHRoZW4gZGlzY3VzcyB3aG/igJlzIHJpZ2h0Og0KYGBge3J9DQogUHJlZGljdGlvbiA8LSBwcmVkaWN0KGZpdCwgdGl0YW5pYy50ZXN0KQ0KIHN1Ym1pdCA8LSBkYXRhLmZyYW1lKFBhc3NlbmdlcklkID0gdGl0YW5pYy50ZXN0JFBhc3NlbmdlcklkLCBTdXJ2aXZlZCA9IFByZWRpY3Rpb24pDQogd3JpdGUuY3N2KHN1Ym1pdCwgZmlsZSA9ICJGaXJzdF9yYW5kb21Gb3Jlc3QuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KNSlJbnRlcnByZXQgdGhlIFJlc3VsdHM6IHRoaXMgc3RlcCBpcyBhbGwgYWJvdXQgdGVsbGluZyB0aGUgc3Rvcnkgb2YgaG93IHRoZSBtb2RlbCB3b3JrcyBhbmQgZXhwbGFpbmluZyBlYWNoIG9mIHRoZSBzdGVwcyBpbnZvbHZlZC4NCg0KICAgICAgIFRoZSBKb2Igd2FzIHRvIHByZWRpY3QgaWYgYSBwYXNzZW5nZXIgaW4gdGhlIFRpdGFuaWMgc3Vydml2ZWQgb3Igbm90IHdpdGggMCwxIHZhbHVlcyBmb3IgdGhlIHZhcmlhYmxlLiBUaGUgc2NvcmUgaXMgdGhlIHBlcmNlbnRhZ2Ugb2YgcGFzc2VuZ2VycyB5b3UgY29ycmVjdGx5IHByZWRpY3QuU28gYWZ0ZXIgc3VibWl0dGluZyB0aGlzIHBlZGljdGlvbiBmaWxlICdGaXJzdF9yYW5kb21Gb3Jlc3QuY3N2JyB0aGlzIG1vZGVsIGdvdCB0aGUgc2NvcmUgb2YgMC43ODQ2OC4NCiAgICAgICBJbiB0aGUgbGVhZGVyYm9hcmQgVGhpcyBtb2RlbCBnb3QgdGhlIHJhbmsgb2YgNTEyMiB3aGljaCBpcyBpbiB0aGUgdG9wIDI4JSBhbW9uZyB0aGUgbGVhZGVyYm9hcmQuDQoNCg==