1. Introduction

1.1 Objective

In the spirit of the past November elections, this paper will attempt to predict the outcome of Republican primaries based on the demographic features of each county. Various classfication methods are used to predict the winner of each county.

1.2 Data

The data explored in this study is extracted from Kaggle 2016 US Presidential Election in Primary Results. There are two datasets: primary results that contains votes by each county for each candidate and county facts that contains demographic data such as percentage of senior citizens and bachelors degree holders and median household income per county. The analysis is only based on Republican candidates performances here.

2. Data Exploration

Load all relevant libraries.

# load libraries
library(readr)
library(magrittr)
library(dplyr)
library(ggplot2)
library(caret)
library(C50)

After loading the 2 datasets, a new dataframe is created by filtering for only Republican votes and creating a new data frame where the winner of each county is defined as the candidate with the maximum percentage of votes. Then, another dataset is extracted from county demogrpahics, leaving variables such as percentage of non-native English speakers and retail sales per capita. In this dataset, we have only included demographc attributes such as percent of white population, population density, poverty rate and percent of veterans per county. Finally, the two datasets, primary results and county facts, are joined based on county name and state.

# load 2 datasets
county_facts <- read_csv("~/Downloads/county_facts.csv")
primary_results <- read_csv("~/Downloads/primary_results.csv.zip")
# winners as candidate with max vote percent from each county 
votes <- primary_results %>%  
            filter(party == "Republican") %>% 
            group_by(state_abbreviation, county) %>% 
            summarize(winner = candidate[which.max(fraction_votes)],
                      frac_vote = max(fraction_votes),
                      votes = max(votes))
# extracting only relevant attributes for county
county_facts %<>%
  select(state_abbreviation = state_abbreviation, county = area_name, 
         income = INC110213, hispanic = RHI725214, white= RHI825214, 
         college = EDU685213, density = POP060210, over65 = AGE775214,
         female=SEX255214, poverty_level = PVY020213, veterans =VET605213) %>% 
  mutate(county = gsub(" County", "", county))
#join on state abb & county
repubvotes <- inner_join(votes, county, by = c("state_abbreviation","county"))
Error in is.data.frame(y) : object 'county' not found

There are only 2 counties which recorded a win for Ben Carson, so a new dataset is created excluding Ben Carson results.

table(repubvotes$winner)

  Ben Carson Donald Trump  John Kasich  Marco Rubio     Ted Cruz 
           2         1945           59           27          596 
repub_votes <- repubvotes %>%
  filter(winner=="Donald Trump" | winner=="Ted Cruz" | winner=="Marco Rubio"| winner=="John Kasich")

Unfortunately it is important to note that as evident from the above values, the data is not uniformly distributed between 4 target variables.

Lets explore some county attributes and the wining candidate’s performances before we delve into some classfication methods.

By visualizing the candidates performance by Income and Education, we can observe that Donald Trump and Ted Cruz seem to have won in counties with lowe percent of college educated persons earning lower medium income while Marco Rubio has outperformed all candidates in highly educated counties.

qplot(income,college, data = repub_votes, geom="point",
      color = winner,
      main = "Candidate Performance by Income and Education", xlab = "household median income", ylab = "college")

In the below boxplot, we can see that Donald Trump and Ted Cruz have done well in communites with few percentage of white population indicating these candidates ability to appeal to other ethnicities.

qplot(winner, white, data=repub_votes, geom="boxplot", fill=winner, main="Candidate Performance by % of White Pop", xlab="candidate", ylab="% of white population")

When we plot candidate performance by age, once again we notice that Trump has a wider appeal than his rivals.

qplot(winner, over65, data=repub_votes, geom="boxplot", fill=winner, main="Candidate Performance by Senior Citizens", xlab="candidate",ylab="% of people 65 or over")


3. Classification Methods & Results

First, we will remove state abbreviation and county from the dataset. The target variable (variable predicted) is winner with 4 levels. There are 1945 occurences where Donald Trump became the winner followed by Ted Cruz winning in 596 counties.

repub <- subset(repub_votes, select = -c(state_abbreviation,county))
names(repub)
 [1] "winner"        "frac_vote"     "votes"         "income"        "hispanic"      "white"        
 [7] "college"       "density"       "over65"        "female"        "poverty_level" "veterans"     
repub$winner <- as.factor(repub$winner)
summary(repub$winner)
Donald Trump  John Kasich  Marco Rubio     Ted Cruz 
        1945           59           27          596 

We will use 4 classification methods to predict the winner of each county.

3.1 Decision Tress

Decision tree is built by repeatedly dividing data into groups based on demographic features. The attribute on which to divide is based on a statistical technique called, information gain, that determines which attribute split will divide the data in a clean manner.

We will use 5 fold cross validation to train the model. A training test is created that contains 75% (or 1972 records) of data from the repub dataset. The remaning will data is used for testing the model results.

# stratified sampling to create a data partition 
set.seed(40)
TrainingDataIndex <- createDataPartition(repub$winner, p=0.75, list = FALSE)
train <- repub[TrainingDataIndex,]
# 25% of data is used to train the model
test <- repub[-TrainingDataIndex,]
# decision tree built wit c5.0 algorithm
DecTreeModel <- train(winner ~ ., data = train, 
                      method = "C5.0",
                      trControl= trainControl(method = "cv", number = 5),
                      na.action = na.omit)

Lets see how well the model classified each county winner using test data.

DTPredictions <-predict(DecTreeModel, test, na.action = na.pass)
dt_cf <- confusionMatrix(DTPredictions, test$winner)
dt_cf
Confusion Matrix and Statistics

              Reference
Prediction     Donald Trump John Kasich Marco Rubio Ted Cruz
  Donald Trump          447          12           4       57
  John Kasich             3           2           0        0
  Marco Rubio             1           0           1        2
  Ted Cruz               35           0           1       90

Overall Statistics
                                          
               Accuracy : 0.8244          
                 95% CI : (0.7931, 0.8528)
    No Information Rate : 0.742           
    P-Value [Acc > NIR] : 3.388e-07       
                                          
                  Kappa : 0.5216          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Donald Trump Class: John Kasich Class: Marco Rubio Class: Ted Cruz
Sensitivity                       0.9198           0.142857           0.166667          0.6040
Specificity                       0.5680           0.995320           0.995378          0.9289
Pos Pred Value                    0.8596           0.400000           0.250000          0.7143
Neg Pred Value                    0.7111           0.981538           0.992320          0.8885
Prevalence                        0.7420           0.021374           0.009160          0.2275
Detection Rate                    0.6824           0.003053           0.001527          0.1374
Detection Prevalence              0.7939           0.007634           0.006107          0.1924
Balanced Accuracy                 0.7439           0.569088           0.581022          0.7664

The decision tree model classified class variable winner with about 82% accuracy. In 57 counties, the model misclassified the winner as Donald Trump when Ted Cruz won those counties. The confusion matrix also shows that the model misclassified Ted Cruz as the winner in 35 instances when Donald Trump won those counties. Sensitivity is another important metric that measures the true positive rate, i.e. when a candidate actually wins a county, how well does model predict that same candidate won that county. Sensitivity for Donald Trump is highest and this could be because our class variables are not uniformly distributed.

3.2 Naive Bayes

Naive Bayes method calculates probabilities given certain event occuring. This method assumes that predictor variables are independent from each other. For instance, the model assumes that median household income and poverty rate are independent from each other as a predictor of which candidate wins the county.

NBModel <- train(train[,-1], train$winner, 
                 method = "nb",
                 trControl= trainControl(method = "cv", number = 5))

Below is the confusion matrix using test data to evaluate Naive Bayes method in predicting the winner of the county elections. The model seems to have a lesser accuracy 75% than that from Decision Trees. For instance, the model predicted that Donald Trump was the winning candidate in 89 counties but Ted Cruz truned out to be the actual winner. In 20 counties, the Naive Bayes model misclassified target variable predicting that Marco Rubio would win when Donald Trump won those counties. Further analysis on the sensitivity shows that the true positive rate has decreased for Trump by about 1% in absolute terms and has increased for Marco Rubio to 67%. Although the true positive rate has gone up for Marco Rubio it is important to note that the model misclassified Rubio in 31 counties when he only won 6 counties in total.

NBPredictions <-predict(NBModel, test)
confusionMatrix(NBPredictions, test$winner)
Confusion Matrix and Statistics

              Reference
Prediction     Donald Trump John Kasich Marco Rubio Ted Cruz
  Donald Trump          439          12           1       89
  John Kasich            15           2           0        5
  Marco Rubio            20           0           4       11
  Ted Cruz               12           0           1       44

Overall Statistics
                                          
               Accuracy : 0.7466          
                 95% CI : (0.7114, 0.7795)
    No Information Rate : 0.742           
    P-Value [Acc > NIR] : 0.4144          
                                          
                  Kappa : 0.3078          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Donald Trump Class: John Kasich Class: Marco Rubio Class: Ted Cruz
Sensitivity                       0.9033           0.142857           0.666667         0.29530
Specificity                       0.3964           0.968799           0.952234         0.97431
Pos Pred Value                    0.8115           0.090909           0.114286         0.77193
Neg Pred Value                    0.5877           0.981043           0.996774         0.82441
Prevalence                        0.7420           0.021374           0.009160         0.22748
Detection Rate                    0.6702           0.003053           0.006107         0.06718
Detection Prevalence              0.8260           0.033588           0.053435         0.08702
Balanced Accuracy                 0.6499           0.555828           0.809450         0.63481

3.3 Support Vector Machines (SVM)

Support Vector Machine (SVM) classification plots each data point in a coordinate plane with each feautre being the value of a particular coordinate. Then the model performs classification by finding the hyperplane between two classes.

When we perform SVM model to predict the county winner, the model does not make any predictions for Marco Rubio, partly because there are so few data points for Rubio. In terms of accuracy, the SVM model displays 82% accuracy which is significantly improved from Naive Bayes model. However, the model incorrectly classifies Donald Trump as the winner in 13 instance when indeed John Kasich won those 13 counties. The true positive rate of SVM model has improved for Trump correctly classifiying Trump as the winner in 97% of instances he actually won.

set.seed(60)
SVMMmodel <- train(winner ~., train,
                 method='svmPoly',
                 trControl=trainControl(method = "cv", number = 5))
SVMPredictions <-predict(SVMMmodel, test)
confusionMatrix(SVMPredictions, test$winner)
Confusion Matrix and Statistics

              Reference
Prediction     Donald Trump John Kasich Marco Rubio Ted Cruz
  Donald Trump          474          13           6       89
  John Kasich             0           1           0        0
  Marco Rubio             0           0           0        0
  Ted Cruz               12           0           0       60

Overall Statistics
                                         
               Accuracy : 0.8168         
                 95% CI : (0.785, 0.8457)
    No Information Rate : 0.742          
    P-Value [Acc > NIR] : 3.682e-06      
                                         
                  Kappa : 0.4196         
 Mcnemar's Test P-Value : NA             

Statistics by Class:

                     Class: Donald Trump Class: John Kasich Class: Marco Rubio Class: Ted Cruz
Sensitivity                       0.9753           0.071429            0.00000          0.4027
Specificity                       0.3609           1.000000            1.00000          0.9763
Pos Pred Value                    0.8144           1.000000                NaN          0.8333
Neg Pred Value                    0.8356           0.980122            0.99084          0.8473
Prevalence                        0.7420           0.021374            0.00916          0.2275
Detection Rate                    0.7237           0.001527            0.00000          0.0916
Detection Prevalence              0.8885           0.001527            0.00000          0.1099
Balanced Accuracy                 0.6681           0.535714            0.50000          0.6895

3.4 Neural Networks

Neural networks mimic the structure and learning process of neurons in the brains. They group feature vectors into classes, takes new input data and find out which label fits best.

NNModel <- train(train[,-1], train$winner,
                 method = "nnet",
                 trControl= trainControl(method = "cv", number = 5))

When we evaluate the NN model with test data, the model does not make any recommendations for either Marco Rubio or Ted Cruz. The model contains a relatively low accuracy at about 74% compared to other models.

NNPredictions <-predict(NNModel, test, na.action = na.pass)
confusionMatrix(NNPredictions, test$winner)
Confusion Matrix and Statistics

              Reference
Prediction     Donald Trump John Kasich Marco Rubio Ted Cruz
  Donald Trump          486          13           6      149
  John Kasich             0           1           0        0
  Marco Rubio             0           0           0        0
  Ted Cruz                0           0           0        0

Overall Statistics
                                          
               Accuracy : 0.7435          
                 95% CI : (0.7083, 0.7766)
    No Information Rate : 0.742           
    P-Value [Acc > NIR] : 0.4851          
                                          
                  Kappa : 0.0101          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Donald Trump Class: John Kasich Class: Marco Rubio Class: Ted Cruz
Sensitivity                     1.000000           0.071429            0.00000          0.0000
Specificity                     0.005917           1.000000            1.00000          1.0000
Pos Pred Value                  0.743119           1.000000                NaN             NaN
Neg Pred Value                  1.000000           0.980122            0.99084          0.7725
Prevalence                      0.741985           0.021374            0.00916          0.2275
Detection Rate                  0.741985           0.001527            0.00000          0.0000
Detection Prevalence            0.998473           0.001527            0.00000          0.0000
Balanced Accuracy               0.502959           0.535714            0.50000          0.5000

4. Conclusion

This study attempts to predict the results of 2016 Republican primary elections in each county based on four classificiation methods. Based on the accuracy of all four models, we can conclude that Decision Trees model produced the highest accuracy of 82% while neural networks produced the lowest accuracy of 74%. The decision tree model correctly classified the runner up in Republican primaries, Ted Cruz in 60% of the counties that he won, the highest sensitivity produced among all models. In an ideal classification problem, the training and testing dataset would contain an equal number of size in each class. It is important to highlight that the training tends to pull the classifier towards the dominant class Donald Trump in making predictions.

These model performances for all four candidates could have been improved by choosing a higher value for k in k-cross fold validation. Recall that all models in above methods use 5-fold cross validation to estimate the accuracy of the model. We could also even out the number of records for each candidate, however this would create a small dataset to perform classification models on.


5. Future Recommendations

Additionally, we hope to use ensemble methods such as boosting, bagging and stacking to improve the accuracy of the models explored above. It may also be useful to perform k means and Kohonen clusering methods on county demographics to determine a holisitic view of voting trends. This would provide a good context to understand which voting groups tend to lean towards Marco Rubio versus Ted Cruz and how are voting demographcs different between Trump and Ted Cruz.


6. References

1: Hammer, Ben. “2016 US Election.” Kaggle. N.p., n.d. Web. https://www.kaggle.com/benhamner/2016-us-election

2: Papiu, Alexandru. “Predictions in the Republican Primary.” N.p., n.d. Web.

https://www.kaggle.com/apapiu/d/benhamner/2016-us-election/predictions-in-the-republican-primary

LS0tCnRpdGxlOiAiUHJlZGljdGluZyB0aGUgUmVzdWx0cyBvZiBSZXB1YmxpY2FuIFByaW1hcmllcyIKYXV0aG9yOiAiSW1hbGkgTmFkeWEgV2FuaWdhc3VuZGFyYSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIDEuIEludHJvZHVjdGlvbgoKIyMjIyAxLjEgT2JqZWN0aXZlCgpJbiB0aGUgc3Bpcml0IG9mIHRoZSBwYXN0IE5vdmVtYmVyIGVsZWN0aW9ucywgdGhpcyBwYXBlciB3aWxsIGF0dGVtcHQgdG8gcHJlZGljdCB0aGUgb3V0Y29tZSBvZiBSZXB1YmxpY2FuIHByaW1hcmllcyBiYXNlZCBvbiB0aGUgZGVtb2dyYXBoaWMgZmVhdHVyZXMgb2YgZWFjaCBjb3VudHkuIFZhcmlvdXMgY2xhc3NmaWNhdGlvbiBtZXRob2RzIGFyZSB1c2VkIHRvIHByZWRpY3QgdGhlIHdpbm5lciBvZiBlYWNoIGNvdW50eS4KCiMjIyMgMS4yIERhdGEKClRoZSBkYXRhIGV4cGxvcmVkIGluIHRoaXMgc3R1ZHkgaXMgZXh0cmFjdGVkIGZyb20gS2FnZ2xlIDIwMTYgVVMgUHJlc2lkZW50aWFsIEVsZWN0aW9uIGluIFByaW1hcnkgUmVzdWx0cy4gVGhlcmUgYXJlIHR3byBkYXRhc2V0czogX19wcmltYXJ5IHJlc3VsdHNfXyB0aGF0IGNvbnRhaW5zIHZvdGVzIGJ5IGVhY2ggY291bnR5IGZvciBlYWNoIGNhbmRpZGF0ZSBhbmQgX19jb3VudHkgZmFjdHNfXyB0aGF0IGNvbnRhaW5zIGRlbW9ncmFwaGljIGRhdGEgc3VjaCBhcyBwZXJjZW50YWdlIG9mIHNlbmlvciBjaXRpemVucyBhbmQgYmFjaGVsb3JzIGRlZ3JlZSBob2xkZXJzIGFuZCBtZWRpYW4gaG91c2Vob2xkIGluY29tZSBwZXIgY291bnR5LiBUaGUgYW5hbHlzaXMgaXMgb25seSBiYXNlZCBvbiBSZXB1YmxpY2FuIGNhbmRpZGF0ZXMgcGVyZm9ybWFuY2VzIGhlcmUuIAoKIyMjIyAxLjMgUmVsYXRlZCBXb3JrcwoKVGhlcmUgd2FzIG9uZSByZWxhdGVkIHdvcmsgaW4gdGhlIEthZ2dsZSBrZXJuZWxzIGJ5IEFsZXhhbmRyYSBQYXBpdS4KClBhcGl1IGNyZWF0ZXMgYSBkYXRhIGZyYW1lIHVzaW5nIG9ubHkgdGhlIHJlc3VsdHMgYmFzZWQgb24gdGhlIFJlcHVibGljYW4gcHJpbWFyaWVzIHRvIGVzdGFibGlzaCBjb3JyZWxhdGlvbnMgYW5kIGF0dGVtcHRzIHRvIHByZWRpY3QgdGhlIHdpbm5lciBvZiBlYWNoIGNvdW50eSB1c2luZyBSYW5kb20gRm9yZXN0IGNsYXNzaWZpY2F0aW9uIGFsZ29yaXRobS4gSGlzIGRhdGEgY2xlYW5pbmcgbWV0aG9kcyBoYXZlIGJlZW4gYWRvcHRlZCBpbiB0aGlzIGFuYWx5c2lzIGFzIHRoZSBvcmlnaW5hbCBkYXRhc2V0cyBjb250YWluZWQgc2V2ZXJhbCBhdHRyaWJ1dGVzLiAKCioqKgojIyMgMi4gRGF0YSBFeHBsb3JhdGlvbgoKTG9hZCBhbGwgcmVsZXZhbnQgbGlicmFyaWVzLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgbG9hZCBsaWJyYXJpZXMKbGlicmFyeShyZWFkcikKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShwbHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoQzUwKQpsaWJyYXJ5KGtsYVIpCmxpYnJhcnkoTUFTUykKbGlicmFyeShlMTA3MSkKbGlicmFyeShubmV0KQpgYGAKCkFmdGVyIGxvYWRpbmcgdGhlIDIgZGF0YXNldHMsIGEgbmV3IGRhdGFmcmFtZSBpcyBjcmVhdGVkIGJ5IGZpbHRlcmluZyBmb3Igb25seSBSZXB1YmxpY2FuIHZvdGVzIGFuZCBjcmVhdGluZyBhIG5ldyBkYXRhIGZyYW1lIHdoZXJlIHRoZSB3aW5uZXIgb2YgZWFjaCBjb3VudHkgaXMgZGVmaW5lZCBhcyB0aGUgY2FuZGlkYXRlIHdpdGggdGhlIG1heGltdW0gcGVyY2VudGFnZSBvZiB2b3Rlcy4gClRoZW4sIGFub3RoZXIgZGF0YXNldCBpcyBleHRyYWN0ZWQgZnJvbSBjb3VudHkgZGVtb2dycGFoaWNzLCBsZWF2aW5nIHZhcmlhYmxlcyBzdWNoIGFzIHBlcmNlbnRhZ2Ugb2Ygbm9uLW5hdGl2ZSBFbmdsaXNoIHNwZWFrZXJzIGFuZCByZXRhaWwgc2FsZXMgcGVyIGNhcGl0YS4gSW4gdGhpcyBkYXRhc2V0LCB3ZSBoYXZlIG9ubHkgaW5jbHVkZWQgZGVtb2dyYXBoYyBhdHRyaWJ1dGVzIHN1Y2ggYXMgcGVyY2VudCBvZiB3aGl0ZSBwb3B1bGF0aW9uLCBwb3B1bGF0aW9uIGRlbnNpdHksIHBvdmVydHkgcmF0ZSBhbmQgcGVyY2VudCBvZiB2ZXRlcmFucyBwZXIgY291bnR5LiAKRmluYWxseSwgdGhlIHR3byBkYXRhc2V0cywgcHJpbWFyeSByZXN1bHRzIGFuZCBjb3VudHkgZmFjdHMsIGFyZSBqb2luZWQgYmFzZWQgb24gY291bnR5IG5hbWUgYW5kIHN0YXRlLiAgCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyBsb2FkIDIgZGF0YXNldHMKY291bnR5X2ZhY3RzIDwtIHJlYWRfY3N2KCJ+L0Rvd25sb2Fkcy9jb3VudHlfZmFjdHMuY3N2IikKcHJpbWFyeV9yZXN1bHRzIDwtIHJlYWRfY3N2KCJ+L0Rvd25sb2Fkcy9wcmltYXJ5X3Jlc3VsdHMuY3N2LnppcCIpCgojIHdpbm5lcnMgYXMgY2FuZGlkYXRlIHdpdGggbWF4IHZvdGUgcGVyY2VudCBmcm9tIGVhY2ggY291bnR5IAp2b3RlcyA8LSBwcmltYXJ5X3Jlc3VsdHMgJT4lICAKICAgICAgICAgICAgZmlsdGVyKHBhcnR5ID09ICJSZXB1YmxpY2FuIikgJT4lIAogICAgICAgICAgICBncm91cF9ieShzdGF0ZV9hYmJyZXZpYXRpb24sIGNvdW50eSkgJT4lIAogICAgICAgICAgICBzdW1tYXJpemUod2lubmVyID0gY2FuZGlkYXRlW3doaWNoLm1heChmcmFjdGlvbl92b3RlcyldLAogICAgICAgICAgICAgICAgICAgICAgZnJhY192b3RlID0gbWF4KGZyYWN0aW9uX3ZvdGVzKSwKICAgICAgICAgICAgICAgICAgICAgIHZvdGVzID0gbWF4KHZvdGVzKSkKCiMgZXh0cmFjdGluZyBvbmx5IHJlbGV2YW50IGF0dHJpYnV0ZXMgZm9yIGNvdW50eQpjb3VudHkgPC0gY291bnR5X2ZhY3RzICU8PiUKICAgICAgICAgIHNlbGVjdChzdGF0ZV9hYmJyZXZpYXRpb24gPSBzdGF0ZV9hYmJyZXZpYXRpb24sIGNvdW50eSA9IGFyZWFfbmFtZSwgCiAgICAgICAgICAgIGluY29tZSA9IElOQzExMDIxMywgaGlzcGFuaWMgPSBSSEk3MjUyMTQsIHdoaXRlPSBSSEk4MjUyMTQsIAogICAgICAgICAgICBjb2xsZWdlID0gRURVNjg1MjEzLCBkZW5zaXR5ID0gUE9QMDYwMjEwLCBvdmVyNjUgPSBBR0U3NzUyMTQsCiAgICAgICAgICAgIGZlbWFsZT1TRVgyNTUyMTQsIHBvdmVydHlfbGV2ZWwgPSBQVlkwMjAyMTMsIHZldGVyYW5zID1WRVQ2MDUyMTMpICU+JSAKICAgICAgICAgIG11dGF0ZShjb3VudHkgPSBnc3ViKCIgQ291bnR5IiwgIiIsIGNvdW50eSkpCgojam9pbiBvbiBzdGF0ZSBhYmIgJiBjb3VudHkKcmVwdWJ2b3RlcyA8LSBpbm5lcl9qb2luKHZvdGVzLCBjb3VudHksIGJ5ID0gYygic3RhdGVfYWJicmV2aWF0aW9uIiwiY291bnR5IikpCmBgYAoKClRoZXJlIGFyZSBvbmx5IDIgY291bnRpZXMgd2hpY2ggcmVjb3JkZWQgYSB3aW4gZm9yIEJlbiBDYXJzb24sIHNvIGEgbmV3IGRhdGFzZXQgaXMgY3JlYXRlZCBleGNsdWRpbmcgQmVuIENhcnNvbiByZXN1bHRzLgoKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CnRhYmxlKHJlcHVidm90ZXMkd2lubmVyKQpyZXB1Yl92b3RlcyA8LSByZXB1YnZvdGVzICU+JQogIGZpbHRlcih3aW5uZXI9PSJEb25hbGQgVHJ1bXAiIHwgd2lubmVyPT0iVGVkIENydXoiIHwgd2lubmVyPT0iTWFyY28gUnViaW8ifCB3aW5uZXI9PSJKb2huIEthc2ljaCIpCmBgYAoKVW5mb3J0dW5hdGVseSBpdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IGFzIGV2aWRlbnQgZnJvbSB0aGUgYWJvdmUgdmFsdWVzLCB0aGUgZGF0YSBpcyBub3QgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkIGJldHdlZW4gNCB0YXJnZXQgdmFyaWFibGVzLgoKTGV0cyBleHBsb3JlIHNvbWUgY291bnR5IGF0dHJpYnV0ZXMgYW5kIHRoZSB3aW5pbmcgY2FuZGlkYXRlJ3MgcGVyZm9ybWFuY2VzIGJlZm9yZSB3ZSBkZWx2ZSBpbnRvIHNvbWUgY2xhc3NmaWNhdGlvbiBtZXRob2RzLgoKQnkgdmlzdWFsaXppbmcgdGhlIGNhbmRpZGF0ZXMgcGVyZm9ybWFuY2UgYnkgSW5jb21lIGFuZCBFZHVjYXRpb24sIHdlIGNhbiBvYnNlcnZlIHRoYXQgRG9uYWxkIFRydW1wIGFuZCBUZWQgQ3J1eiBzZWVtIHRvIGhhdmUgd29uIGluIGNvdW50aWVzIHdpdGggbG93ZSBwZXJjZW50IG9mIGNvbGxlZ2UgZWR1Y2F0ZWQgcGVyc29ucyBlYXJuaW5nIGxvd2VyIG1lZGl1bSBpbmNvbWUgd2hpbGUgTWFyY28gUnViaW8gaGFzIG91dHBlcmZvcm1lZCBhbGwgY2FuZGlkYXRlcyBpbiBoaWdobHkgZWR1Y2F0ZWQgY291bnRpZXMuIAoKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTcsIHdhcm5pbmc9VFJVRX0KcXBsb3QoaW5jb21lLGNvbGxlZ2UsIGRhdGEgPSByZXB1Yl92b3RlcywgZ2VvbT0icG9pbnQiLAogICAgICBjb2xvciA9IHdpbm5lciwKICAgICAgbWFpbiA9ICJDYW5kaWRhdGUgUGVyZm9ybWFuY2UgYnkgSW5jb21lIGFuZCBFZHVjYXRpb24iLCB4bGFiID0gImhvdXNlaG9sZCBtZWRpYW4gaW5jb21lIiwgeWxhYiA9ICJjb2xsZWdlIikKYGBgCgpJbiB0aGUgYmVsb3cgYm94cGxvdCwgd2UgY2FuIHNlZSB0aGF0IERvbmFsZCBUcnVtcCBhbmQgVGVkIENydXogaGF2ZSBkb25lIHdlbGwgaW4gY29tbXVuaXRlcyB3aXRoIGZldyBwZXJjZW50YWdlIG9mIHdoaXRlIHBvcHVsYXRpb24gaW5kaWNhdGluZyB0aGVzZSBjYW5kaWRhdGVzIGFiaWxpdHkgdG8gYXBwZWFsIHRvIG90aGVyIGV0aG5pY2l0aWVzLgoKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTV9CnFwbG90KHdpbm5lciwgd2hpdGUsIGRhdGE9cmVwdWJfdm90ZXMsIGdlb209ImJveHBsb3QiLCBmaWxsPXdpbm5lciwgbWFpbj0iQ2FuZGlkYXRlIFBlcmZvcm1hbmNlIGJ5ICUgb2YgV2hpdGUgUG9wIiwgeGxhYj0iY2FuZGlkYXRlIiwgeWxhYj0iJSBvZiB3aGl0ZSBwb3B1bGF0aW9uIikKYGBgCgpXaGVuIHdlIHBsb3QgY2FuZGlkYXRlIHBlcmZvcm1hbmNlIGJ5IGFnZSwgb25jZSBhZ2FpbiB3ZSBub3RpY2UgdGhhdCBUcnVtcCBoYXMgYSB3aWRlciBhcHBlYWwgdGhhbiBoaXMgcml2YWxzLgoKYGBge3IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTV9CnFwbG90KHdpbm5lciwgb3ZlcjY1LCBkYXRhPXJlcHViX3ZvdGVzLCBnZW9tPSJib3hwbG90IiwgZmlsbD13aW5uZXIsIG1haW49IkNhbmRpZGF0ZSBQZXJmb3JtYW5jZSBieSBTZW5pb3IgQ2l0aXplbnMiLCB4bGFiPSJjYW5kaWRhdGUiLHlsYWI9IiUgb2YgcGVvcGxlIDY1IG9yIG92ZXIiKQpgYGAKCioqKgojIyMgMy4gQ2xhc3NpZmljYXRpb24gTWV0aG9kcyAmIFJlc3VsdHMKCkZpcnN0LCB3ZSB3aWxsIHJlbW92ZSBzdGF0ZSBhYmJyZXZpYXRpb24gYW5kIGNvdW50eSBmcm9tIHRoZSBkYXRhc2V0LiBUaGUgdGFyZ2V0IHZhcmlhYmxlICh2YXJpYWJsZSBwcmVkaWN0ZWQpIGlzIF93aW5uZXJfIHdpdGggNCBsZXZlbHMuIFRoZXJlIGFyZSAxOTQ1IG9jY3VyZW5jZXMgd2hlcmUgRG9uYWxkIFRydW1wIGJlY2FtZSB0aGUgd2lubmVyIGZvbGxvd2VkIGJ5IFRlZCBDcnV6IHdpbm5pbmcgaW4gNTk2IGNvdW50aWVzLgoKYGBge3J9CnJlcHViIDwtIHN1YnNldChyZXB1Yl92b3Rlcywgc2VsZWN0ID0gLWMoc3RhdGVfYWJicmV2aWF0aW9uLGNvdW50eSkpCm5hbWVzKHJlcHViKQpyZXB1YiR3aW5uZXIgPC0gYXMuZmFjdG9yKHJlcHViJHdpbm5lcikKc3VtbWFyeShyZXB1YiR3aW5uZXIpCmBgYAoKV2Ugd2lsbCB1c2UgNCBjbGFzc2lmaWNhdGlvbiBtZXRob2RzIHRvIHByZWRpY3QgdGhlIHdpbm5lciBvZiBlYWNoIGNvdW50eS4gCgojIyMjIDMuMSBEZWNpc2lvbiBUcmVzcwoKRGVjaXNpb24gdHJlZSBpcyBidWlsdCBieSByZXBlYXRlZGx5IGRpdmlkaW5nIGRhdGEgaW50byBncm91cHMgYmFzZWQgb24gZGVtb2dyYXBoaWMgZmVhdHVyZXMuIFRoZSBhdHRyaWJ1dGUgb24gd2hpY2ggdG8gZGl2aWRlIGlzIGJhc2VkIG9uIGEgc3RhdGlzdGljYWwgdGVjaG5pcXVlIGNhbGxlZCwgaW5mb3JtYXRpb24gZ2FpbiwgdGhhdCBkZXRlcm1pbmVzIHdoaWNoIGF0dHJpYnV0ZSBzcGxpdCB3aWxsIGRpdmlkZSB0aGUgZGF0YSBpbiBhIGNsZWFuIG1hbm5lci4gCgpXZSB3aWxsIHVzZSA1IGZvbGQgY3Jvc3MgdmFsaWRhdGlvbiB0byB0cmFpbiB0aGUgbW9kZWwuIEEgdHJhaW5pbmcgdGVzdCBpcyBjcmVhdGVkIHRoYXQgY29udGFpbnMgNzUlIChvciAxOTcyIHJlY29yZHMpIG9mIGRhdGEgZnJvbSB0aGUgX3JlcHViXyBkYXRhc2V0LiBUaGUgcmVtYW5pbmcgd2lsbCBkYXRhIGlzIHVzZWQgZm9yIHRlc3RpbmcgdGhlIG1vZGVsIHJlc3VsdHMuIAoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CiMgc3RyYXRpZmllZCBzYW1wbGluZyB0byBjcmVhdGUgYSBkYXRhIHBhcnRpdGlvbiAKc2V0LnNlZWQoNDApClRyYWluaW5nRGF0YUluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24ocmVwdWIkd2lubmVyLCBwPTAuNzUsIGxpc3QgPSBGQUxTRSkKdHJhaW4gPC0gcmVwdWJbVHJhaW5pbmdEYXRhSW5kZXgsXQoKIyAyNSUgb2YgZGF0YSBpcyB1c2VkIHRvIHRyYWluIHRoZSBtb2RlbAp0ZXN0IDwtIHJlcHViWy1UcmFpbmluZ0RhdGFJbmRleCxdCgojIGRlY2lzaW9uIHRyZWUgYnVpbHQgd2l0IGM1LjAgYWxnb3JpdGhtCkRlY1RyZWVNb2RlbCA8LSB0cmFpbih3aW5uZXIgfiAuLCBkYXRhID0gdHJhaW4sIAogICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIkM1LjAiLAogICAgICAgICAgICAgICAgICAgICAgdHJDb250cm9sPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gNSksCiAgICAgICAgICAgICAgICAgICAgICBuYS5hY3Rpb24gPSBuYS5vbWl0KQpgYGAKCkxldHMgc2VlIGhvdyB3ZWxsIHRoZSBtb2RlbCBjbGFzc2lmaWVkIGVhY2ggY291bnR5IHdpbm5lciB1c2luZyB0ZXN0IGRhdGEuCgpgYGB7cn0KRFRQcmVkaWN0aW9ucyA8LXByZWRpY3QoRGVjVHJlZU1vZGVsLCB0ZXN0LCBuYS5hY3Rpb24gPSBuYS5wYXNzKQpkdF9jZiA8LSBjb25mdXNpb25NYXRyaXgoRFRQcmVkaWN0aW9ucywgdGVzdCR3aW5uZXIpCmR0X2NmCmBgYAoKVGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgY2xhc3NpZmllZCBjbGFzcyB2YXJpYWJsZSBfd2lubmVyXyB3aXRoIGFib3V0IDgyJSBhY2N1cmFjeS4gSW4gNTcgY291bnRpZXMsIHRoZSBtb2RlbCBtaXNjbGFzc2lmaWVkIHRoZSB3aW5uZXIgYXMgRG9uYWxkIFRydW1wIHdoZW4gVGVkIENydXogd29uIHRob3NlIGNvdW50aWVzLiBUaGUgY29uZnVzaW9uIG1hdHJpeCBhbHNvIHNob3dzIHRoYXQgdGhlIG1vZGVsIG1pc2NsYXNzaWZpZWQgVGVkIENydXogYXMgdGhlIHdpbm5lciBpbiAzNSBpbnN0YW5jZXMgd2hlbiBEb25hbGQgVHJ1bXAgd29uIHRob3NlIGNvdW50aWVzLgpTZW5zaXRpdml0eSBpcyBhbm90aGVyIGltcG9ydGFudCBtZXRyaWMgdGhhdCBtZWFzdXJlcyB0aGUgdHJ1ZSBwb3NpdGl2ZSByYXRlLCBpLmUuIHdoZW4gYSBjYW5kaWRhdGUgYWN0dWFsbHkgd2lucyBhIGNvdW50eSwgaG93IHdlbGwgZG9lcyBtb2RlbCBwcmVkaWN0IHRoYXQgc2FtZSBjYW5kaWRhdGUgd29uIHRoYXQgY291bnR5LiBTZW5zaXRpdml0eSBmb3IgRG9uYWxkIFRydW1wIGlzIGhpZ2hlc3QgYW5kIHRoaXMgY291bGQgYmUgYmVjYXVzZSBvdXIgY2xhc3MgdmFyaWFibGVzIGFyZSBub3QgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkLgoKIyMjIyAzLjIgTmFpdmUgQmF5ZXMKCk5haXZlIEJheWVzIG1ldGhvZCBjYWxjdWxhdGVzIHByb2JhYmlsaXRpZXMgZ2l2ZW4gY2VydGFpbiBldmVudCBvY2N1cmluZy4gVGhpcyBtZXRob2QgYXNzdW1lcyB0aGF0IHByZWRpY3RvciB2YXJpYWJsZXMgYXJlIGluZGVwZW5kZW50IGZyb20gZWFjaCBvdGhlci4gRm9yIGluc3RhbmNlLCB0aGUgbW9kZWwgYXNzdW1lcyB0aGF0IG1lZGlhbiBob3VzZWhvbGQgaW5jb21lIGFuZCBwb3ZlcnR5IHJhdGUgYXJlIGluZGVwZW5kZW50IGZyb20gZWFjaCBvdGhlciBhcyBhIHByZWRpY3RvciBvZiB3aGljaCBjYW5kaWRhdGUgd2lucyB0aGUgY291bnR5LiAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSJoaWRlIn0KTkJNb2RlbCA8LSB0cmFpbih0cmFpblssLTFdLCB0cmFpbiR3aW5uZXIsIAogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJuYiIsCiAgICAgICAgICAgICAgICAgdHJDb250cm9sPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gNSkpCmBgYAoKQmVsb3cgaXMgdGhlIGNvbmZ1c2lvbiBtYXRyaXggdXNpbmcgdGVzdCBkYXRhIHRvIGV2YWx1YXRlIE5haXZlIEJheWVzIG1ldGhvZCBpbiBwcmVkaWN0aW5nIHRoZSB3aW5uZXIgb2YgdGhlIGNvdW50eSBlbGVjdGlvbnMuClRoZSBtb2RlbCBzZWVtcyB0byBoYXZlIGEgbGVzc2VyIGFjY3VyYWN5IDc1JSB0aGFuIHRoYXQgZnJvbSBEZWNpc2lvbiBUcmVlcy4gRm9yIGluc3RhbmNlLCB0aGUgbW9kZWwgcHJlZGljdGVkIHRoYXQgRG9uYWxkIFRydW1wIHdhcyB0aGUgd2lubmluZyBjYW5kaWRhdGUgaW4gODkgY291bnRpZXMgYnV0IFRlZCBDcnV6IHRydW5lZCBvdXQgdG8gYmUgdGhlIGFjdHVhbCB3aW5uZXIuIEluIDIwIGNvdW50aWVzLCB0aGUgTmFpdmUgQmF5ZXMgbW9kZWwgbWlzY2xhc3NpZmllZCB0YXJnZXQgdmFyaWFibGUgcHJlZGljdGluZyB0aGF0IE1hcmNvIFJ1YmlvIHdvdWxkIHdpbiB3aGVuIERvbmFsZCBUcnVtcCB3b24gdGhvc2UgY291bnRpZXMuIApGdXJ0aGVyIGFuYWx5c2lzIG9uIHRoZSBzZW5zaXRpdml0eSBzaG93cyB0aGF0IHRoZSB0cnVlIHBvc2l0aXZlIHJhdGUgaGFzIGRlY3JlYXNlZCBmb3IgVHJ1bXAgYnkgYWJvdXQgMSUgaW4gYWJzb2x1dGUgdGVybXMgYW5kIGhhcyBpbmNyZWFzZWQgZm9yIE1hcmNvIFJ1YmlvIHRvIDY3JS4gQWx0aG91Z2ggdGhlIHRydWUgcG9zaXRpdmUgcmF0ZSBoYXMgZ29uZSB1cCBmb3IgTWFyY28gUnViaW8gaXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB0aGUgbW9kZWwgbWlzY2xhc3NpZmllZCBSdWJpbyBpbiAzMSBjb3VudGllcyB3aGVuIGhlIG9ubHkgd29uIDYgY291bnRpZXMgaW4gdG90YWwuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KTkJQcmVkaWN0aW9ucyA8LXByZWRpY3QoTkJNb2RlbCwgdGVzdCkKY29uZnVzaW9uTWF0cml4KE5CUHJlZGljdGlvbnMsIHRlc3Qkd2lubmVyKQpgYGAKCgojIyMjIDMuMyBTdXBwb3J0IFZlY3RvciBNYWNoaW5lcyAoU1ZNKQoKU3VwcG9ydCBWZWN0b3IgTWFjaGluZSAoU1ZNKSBjbGFzc2lmaWNhdGlvbiBwbG90cyBlYWNoIGRhdGEgcG9pbnQgaW4gYSBjb29yZGluYXRlIHBsYW5lIHdpdGggZWFjaCBmZWF1dHJlIGJlaW5nIHRoZSB2YWx1ZSBvZiBhIHBhcnRpY3VsYXIgY29vcmRpbmF0ZS4gVGhlbiB0aGUgbW9kZWwgcGVyZm9ybXMgY2xhc3NpZmljYXRpb24gYnkgZmluZGluZyB0aGUgaHlwZXJwbGFuZSBiZXR3ZWVuIHR3byBjbGFzc2VzLiAKCldoZW4gd2UgcGVyZm9ybSBTVk0gbW9kZWwgdG8gcHJlZGljdCB0aGUgY291bnR5IHdpbm5lciwgdGhlIG1vZGVsIGRvZXMgbm90IG1ha2UgYW55IHByZWRpY3Rpb25zIGZvciBNYXJjbyBSdWJpbywgcGFydGx5IGJlY2F1c2UgdGhlcmUgYXJlIHNvIGZldyBkYXRhIHBvaW50cyBmb3IgUnViaW8uCkluIHRlcm1zIG9mIGFjY3VyYWN5LCB0aGUgU1ZNIG1vZGVsIGRpc3BsYXlzIDgyJSBhY2N1cmFjeSB3aGljaCBpcyBzaWduaWZpY2FudGx5IGltcHJvdmVkIGZyb20gTmFpdmUgQmF5ZXMgbW9kZWwuIEhvd2V2ZXIsIHRoZSBtb2RlbCBpbmNvcnJlY3RseSBjbGFzc2lmaWVzIERvbmFsZCBUcnVtcCBhcyB0aGUgd2lubmVyIGluIDEzIGluc3RhbmNlIHdoZW4gaW5kZWVkIEpvaG4gS2FzaWNoIHdvbiB0aG9zZSAxMyBjb3VudGllcy4gVGhlIHRydWUgcG9zaXRpdmUgcmF0ZSBvZiBTVk0gbW9kZWwgaGFzIGltcHJvdmVkIGZvciBUcnVtcCBjb3JyZWN0bHkgY2xhc3NpZml5aW5nIFRydW1wIGFzIHRoZSB3aW5uZXIgaW4gOTclIG9mIGluc3RhbmNlcyBoZSBhY3R1YWxseSB3b24uCgpgYGB7cn0Kc2V0LnNlZWQoNjApClNWTU1tb2RlbCA8LSB0cmFpbih3aW5uZXIgfi4sIHRyYWluLAogICAgICAgICAgICAgICAgIG1ldGhvZD0nc3ZtUG9seScsCiAgICAgICAgICAgICAgICAgdHJDb250cm9sPXRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSA1KSkKU1ZNUHJlZGljdGlvbnMgPC1wcmVkaWN0KFNWTU1tb2RlbCwgdGVzdCkKY29uZnVzaW9uTWF0cml4KFNWTVByZWRpY3Rpb25zLCB0ZXN0JHdpbm5lcikKYGBgCgoKIyMjIyAzLjQgTmV1cmFsIE5ldHdvcmtzCgpOZXVyYWwgbmV0d29ya3MgbWltaWMgdGhlIHN0cnVjdHVyZSBhbmQgbGVhcm5pbmcgcHJvY2VzcyBvZiBuZXVyb25zIGluIHRoZSBicmFpbnMuIFRoZXkgZ3JvdXAgZmVhdHVyZSB2ZWN0b3JzIGludG8gY2xhc3NlcywgdGFrZXMgbmV3IGlucHV0IGRhdGEgYW5kIGZpbmQgb3V0IHdoaWNoIGxhYmVsIGZpdHMgYmVzdC4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSJoaWRlIiwgZXZhbD1GQUxTRX0KTk5Nb2RlbCA8LSB0cmFpbih0cmFpblssLTFdLCB0cmFpbiR3aW5uZXIsCiAgICAgICAgICAgICAgICAgbWV0aG9kID0gIm5uZXQiLAogICAgICAgICAgICAgICAgIHRyQ29udHJvbD0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDUpKQpgYGAKCldoZW4gd2UgZXZhbHVhdGUgdGhlIE5OIG1vZGVsIHdpdGggdGVzdCBkYXRhLCB0aGUgbW9kZWwgZG9lcyBub3QgbWFrZSBhbnkgcmVjb21tZW5kYXRpb25zIGZvciBlaXRoZXIgTWFyY28gUnViaW8gb3IgVGVkIENydXouIFRoZSBtb2RlbCBjb250YWlucyBhIHJlbGF0aXZlbHkgbG93IGFjY3VyYWN5IGF0IGFib3V0IDc0JSBjb21wYXJlZCB0byBvdGhlciBtb2RlbHMuCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KTk5QcmVkaWN0aW9ucyA8LXByZWRpY3QoTk5Nb2RlbCwgdGVzdCwgbmEuYWN0aW9uID0gbmEucGFzcykKY29uZnVzaW9uTWF0cml4KE5OUHJlZGljdGlvbnMsIHRlc3Qkd2lubmVyKQpgYGAKCioqKgojIyMgNC4gQ29uY2x1c2lvbgoKVGhpcyBzdHVkeSBhdHRlbXB0cyB0byBwcmVkaWN0IHRoZSByZXN1bHRzIG9mIDIwMTYgUmVwdWJsaWNhbiBwcmltYXJ5IGVsZWN0aW9ucyBpbiBlYWNoIGNvdW50eSBiYXNlZCBvbiBmb3VyIGNsYXNzaWZpY2lhdGlvbiBtZXRob2RzLiBCYXNlZCBvbiB0aGUgYWNjdXJhY3kgb2YgYWxsIGZvdXIgbW9kZWxzLCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCBEZWNpc2lvbiBUcmVlcyBtb2RlbCBwcm9kdWNlZCB0aGUgaGlnaGVzdCBhY2N1cmFjeSBvZiA4MiUgd2hpbGUgbmV1cmFsIG5ldHdvcmtzIHByb2R1Y2VkIHRoZSBsb3dlc3QgYWNjdXJhY3kgb2YgNzQlLiBUaGUgZGVjaXNpb24gdHJlZSBtb2RlbCBjb3JyZWN0bHkgY2xhc3NpZmllZCB0aGUgcnVubmVyIHVwIGluIFJlcHVibGljYW4gcHJpbWFyaWVzLCBUZWQgQ3J1eiBpbiA2MCUgb2YgdGhlIGNvdW50aWVzIHRoYXQgaGUgd29uLCB0aGUgaGlnaGVzdCBzZW5zaXRpdml0eSBwcm9kdWNlZCBhbW9uZyBhbGwgbW9kZWxzLiBJbiBhbiBpZGVhbCBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtLCB0aGUgdHJhaW5pbmcgYW5kIHRlc3RpbmcgZGF0YXNldCB3b3VsZCBjb250YWluIGFuIGVxdWFsIG51bWJlciBvZiBzaXplIGluIGVhY2ggY2xhc3MuIEl0IGlzIGltcG9ydGFudCB0byBoaWdobGlnaHQgdGhhdCB0aGUgdHJhaW5pbmcgdGVuZHMgdG8gcHVsbCB0aGUgY2xhc3NpZmllciB0b3dhcmRzIHRoZSBkb21pbmFudCBjbGFzcyBEb25hbGQgVHJ1bXAgaW4gbWFraW5nIHByZWRpY3Rpb25zLgoKVGhlc2UgbW9kZWwgcGVyZm9ybWFuY2VzIGZvciBhbGwgZm91ciBjYW5kaWRhdGVzIGNvdWxkIGhhdmUgYmVlbiBpbXByb3ZlZCBieSBjaG9vc2luZyBhIGhpZ2hlciB2YWx1ZSBmb3IgayBpbiBrLWNyb3NzIGZvbGQgdmFsaWRhdGlvbi4gUmVjYWxsIHRoYXQgYWxsIG1vZGVscyBpbiBhYm92ZSBtZXRob2RzIHVzZSA1LWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiB0byBlc3RpbWF0ZSB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsLiBXZSBjb3VsZCBhbHNvIGV2ZW4gb3V0IHRoZSBudW1iZXIgb2YgcmVjb3JkcyBmb3IgZWFjaCBjYW5kaWRhdGUsIGhvd2V2ZXIgdGhpcyB3b3VsZCBjcmVhdGUgYSBzbWFsbCBkYXRhc2V0IHRvIHBlcmZvcm0gY2xhc3NpZmljYXRpb24gbW9kZWxzIG9uLgoKKioqCiMjIyA1LiBGdXR1cmUgUmVjb21tZW5kYXRpb25zCgpBZGRpdGlvbmFsbHksIHdlIGhvcGUgdG8gdXNlIGVuc2VtYmxlIG1ldGhvZHMgc3VjaCBhcyBib29zdGluZywgYmFnZ2luZyBhbmQgc3RhY2tpbmcgdG8gaW1wcm92ZSB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVscyBleHBsb3JlZCBhYm92ZS4gSXQgbWF5IGFsc28gYmUgdXNlZnVsIHRvIHBlcmZvcm0gayBtZWFucyBhbmQgS29ob25lbiBjbHVzZXJpbmcgbWV0aG9kcyBvbiBjb3VudHkgZGVtb2dyYXBoaWNzIHRvIGRldGVybWluZSBhIGhvbGlzaXRpYyB2aWV3IG9mIHZvdGluZyB0cmVuZHMuIFRoaXMgd291bGQgcHJvdmlkZSBhIGdvb2QgY29udGV4dCB0byB1bmRlcnN0YW5kIHdoaWNoIHZvdGluZyBncm91cHMgdGVuZCB0byBsZWFuIHRvd2FyZHMgTWFyY28gUnViaW8gdmVyc3VzIFRlZCBDcnV6IGFuZCBob3cgYXJlIHZvdGluZyBkZW1vZ3JhcGhjcyBkaWZmZXJlbnQgYmV0d2VlbiBUcnVtcCBhbmQgVGVkIENydXouIAoKKioqCiMjIyA2LiBSZWZlcmVuY2VzCgoxOiBIYW1tZXIsIEJlbi4gIjIwMTYgVVMgRWxlY3Rpb24uIiBLYWdnbGUuIE4ucC4sIG4uZC4gV2ViLgpodHRwczovL3d3dy5rYWdnbGUuY29tL2JlbmhhbW5lci8yMDE2LXVzLWVsZWN0aW9uCgoyOiBQYXBpdSwgQWxleGFuZHJ1LiAiUHJlZGljdGlvbnMgaW4gdGhlIFJlcHVibGljYW4gUHJpbWFyeS4iIE4ucC4sIG4uZC4gV2ViLgoKaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9hcGFwaXUvZC9iZW5oYW1uZXIvMjAxNi11cy1lbGVjdGlvbi9wcmVkaWN0aW9ucy1pbi10aGUtcmVwdWJsaWNhbi1wcmltYXJ5CgoK