Project Proposal

The primary goal of this project was to identify prospective students who will most likely enroll as freshman in the upcoming school year for a mid-size university that enrolls 2400 to 2800 new freshman each year.

The dataset was built over a period of several months in consultation with Enrollment Management. The data contained many missing values that were dealt with in a few different ways depending on the variable. In total, it consisted of 106767 missing values across 56237 observations.

The results of this analysis can be used in the future to determine probabilities of enrollment for a given student.

Required Packages

install.packages("randomForest")
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.4/randomForest_4.6-14.zip'
Content type 'application/zip' length 180935 bytes (176 KB)
downloaded 176 KB
package ‘randomForest’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\morga\AppData\Local\Temp\Rtmpgd5hdy\downloaded_packages
library(tidyverse)
library(lubridate)
library(dummy)
library(rpart)
library(randomForest)
package <U+393C><U+3E31>randomForest<U+393C><U+3E32> was built under R version 3.4.4
library(plyr)
library(dplyr)
library(caret)
library(survival)
library(splines)
library(parallel)

Data Import

setwd("C:/Users/morga/OneDrive/Documents/Predictive Analytics & Data Mining - BAN 6025")
school_data <- read_csv("inq05_samp.csv")
#school_data_dict <- read_csv("inq05_data_dict.csv")

Data Cleaning

Dropping Variables

I chose to drop some variables present in my dataset. These included ACADEMIC_INTEREST_1, ACADEMIC_INTEREST_2, IRSCHOOL, and LEVEL_YEAR. I dropped the first of those three because they were replaced by INT1RAT, INT2RAT, and hscrat respectively. This controls for population at each of these levels. I dropped LEVEL_YEAR because this variable only consists of one level since we are considering only one year’s worth of applicants. I also dropped the variables ETHN_CODE and sex since ethnicity and sex cannot be used by universities during admissions decisions.

dropped_vars <- c("ACADEMIC_INTEREST_1", "ACADEMIC_INTEREST_2", "IRSCHOOL", 
                  "ETHN_CODE", "sex", "LEVEL_YEAR")
full_set <- school_data %>%
   select(-one_of(dropped_vars))

For mailq, telecq, and interest, I operated under the assumption that they should not be treated as purely numeric but rather as categorical. For instance, the difference between a 2 and a 3 when rating interest in the school is not the same as the difference between a 4 and a 5 Therefore I ran a chi-square test on each of the variables and considered their p-values. Since each of the chi-square tests showed statistical significance across groups, I chose to convert each of the three variables to factors.

ordered_factor_vars <- c("mailq", "telecq", "interest")
for (i in ordered_factor_vars){
  full_set[[i]] <- factor(full_set[[i]])
  print(chisq.test(table(full_set[[i]], full_set$Enroll)))
}

    Pearson's Chi-squared test

data:  table(full_set[[i]], full_set$Enroll)
X-squared = 693.63, df = 4, p-value < 2.2e-16


    Pearson's Chi-squared test

data:  table(full_set[[i]], full_set$Enroll)
X-squared = 1965.6, df = 3, p-value < 2.2e-16


    Pearson's Chi-squared test

data:  table(full_set[[i]], full_set$Enroll)
X-squared = 2963.2, df = 3, p-value < 2.2e-16

I converted the time variable CONTACT_DATE1 to a proper time format using the lubridate package as such:

full_set$CONTACT_DATE1 <- dmy(full_set$CONTACT_DATE1)

I chose to drop the CONTACT_CODE1 variable due to its high number of categories and low proportion of students across each category.

full_set <- full_set %>%
  select(-CONTACT_CODE1)

I converted the indicator variable for Enroll from being “Y” and “N” to a 1 and 0 respectively.

full_set$Instate[full_set$Instate == "Y"] <- 1
full_set$Instate[full_set$Instate == "N"] <- 0
full_set$Instate <- as.numeric(full_set$Instate)

For RECR_CODE, I used my best judgment and chose to convert values of 000, N, and A to be NA. There were 3815 such values.

full_set$RECR_CODE[full_set$RECR_CODE %in% c("000", "N", "A")] <- NA

This left 3816 NA values for the RECR_CODE variable. Under the assumption that these were not missing at random, I chose to generate bins for each possible value of RECR_CODE, with NA values having their own respective bin. I ran a chisquare test across the different values of RECR_CODE to see if I was justified in doing this.

chisq.test(table(full_set$RECR_CODE, full_set$Enroll))

    Pearson's Chi-squared test

data:  table(full_set$RECR_CODE, full_set$Enroll)
X-squared = 394.06, df = 7, p-value < 2.2e-16

The low p-value led me to feel reassured in doing so. Although the values for RECR_CODE extend up to 8, I chose to treat each value as having a separate category in order to maintain the rows containing NA observations in my dataset for my later model implementations.

full_set$RECR_CODE[is.na(full_set$RECR_CODE)] <- "missing"
full_set$RECR_CODE <- factor(full_set$RECR_CODE)

Note that I imputed these values to “missing” rather than purely NA. I did this so that these values might receive their own factor level when I generate dummy variables for each category later in the analysis.

NA Values

SAT Score

There were 38786 missing SAT score values in the dataset. Since these were likely not missing at random (those who choose not to disclose their SAT scores likely scored poorly), I decided to bin the SAT scores by proportionality, with NA values receiving their own bin. I binned based on the quintiles of the observed SAT scores, thus generating 5 bins for the actually reported SAT scores and one additional bin for the NA values. I chose the value of 5 bins arbitrarily.

sat_splits <- c(0, quantile(full_set$satscore, c(.2, .4, .6, .8), 
                       na.rm = TRUE), Inf)
full_set <- cbind(full_set,  sat_group = cut(full_set$satscore,
                                         breaks = sat_splits))
levels(full_set$sat_group) <- c(levels(full_set$sat_group), "missing")
full_set$sat_group[is.na(full_set$sat_group)] <- "missing"
## Removing initial satscore variable
full_set <- full_set %>%
  select(-satscore)

Telecq

There were 43190 missing telephone interest scores in the dataset. Since these were likely not missing at random (somebody who hangs up the phone is more likely to not be interested than interested in enrollment), I chose to provide a separate bin for those observations with missing telecq values.

levels(full_set$telecq) <- c("Missing", levels(full_set$telecq))
full_set$telecq[is.na(full_set$telecq)] <- "Missing"
full_set$telecq <- factor(full_set$telecq)

Average Income

There were 12801 missing income values in the dataset. I assumed that these were missing at random and that average income did not have an immediate impact on enrollment at the university. I therefore imputed the mean for these values.

avg_avg_income <- mean(full_set$avg_income, na.rm = TRUE)
full_set$avg_income[is.na(full_set$avg_income)] <- avg_avg_income

Distance

There were 11905 missing distance values in the dataset. I operated under the assumption that these were missing because the school might not know exact distance to a given prospective student’s hometown. I therefore chose to put bin the distance values similarly to how I binned SAT scores, binning by quintiles, with NA values getting a separate bin.

distance_splits <- c(0, quantile(full_set$distance, c(0.2, 0.4, 0.6, 0.8), 
                            na.rm = TRUE), Inf)
full_set <- cbind(full_set, distance_group = cut(full_set$distance, 
                                     breaks = distance_splits))
levels(full_set$distance_group) <- c(levels(full_set$distance_group), "missing")
full_set$distance_group[is.na(full_set$distance_group)] <- "missing"
## Removing initial distance variable
full_set <- full_set %>%
  select(-distance)

These imputations and binnings left 0 NA observations remaining in the dataset.

Dummy Variable Generation

I used the dummy package to generate dummy variables for each of my subsequent factor variables. This gave each level within a factor its own dummy variable. For example, SAT scores, since they are comprised of 6 bins, are now split across 6 dummy variables.

full_set <- cbind(full_set, dummy(full_set))
## Dropping initial factor variables used to generate the dummies
full_set <- full_set %>%
  select(-c(mailq, telecq, interest, sat_group, distance_group, RECR_CODE))

Splitting into Training and Validation

Note that for the potential students in the dataset, only 5% actually enrolled. Since I am focusing my analysis on discovering which factors seeem to be influencing students’ decisions to enroll in the university, I chose to weight the model so that it errs to Type I errors rather than Type II. I would prefer to predict that a student enrolled who actually didn’t rather than not predict a student to enroll who actually went on to enroll. To increase the relative proportion of enrollment within my dataset, I chose to subset the data so that I am taking all observations in the enrolled category, and a smaller proportion of the observations in the not enrolled category.

I then split this subsetted data into 70% training and 30% validation sets.

enrolled_set <- full_set %>%
  filter(Enroll == 1)
non_enrolled_set <- full_set %>%
  filter(Enroll == 0)
# Only selecting 20% of values among those who didn't enroll so that the split 
# of enrolled:not enrolled is approximately 20:80
set.seed(123)
sample_non_enrolled_set <- sample(c(TRUE, FALSE), nrow(non_enrolled_set),
                                  replace = T, prob = c(0.15, 0.85))
included_non_enrolled <- non_enrolled_set[sample_non_enrolled_set, ]
not_included_non_enrolled <- non_enrolled_set[!sample_non_enrolled_set, ]
balanced_full_set <- rbind(enrolled_set, included_non_enrolled)
## creating training-validation sets; in future, want to explore bootstrap
## methodologies, k-fold techniques, and Leave-one-out cross-validation
## THESE WILL NOT BE USED UNTIL MODEL BUILDING
sample <- sample(c(TRUE, FALSE), nrow(balanced_full_set),
                 replace = T, prob = c(0.7, 0.3))
train <- balanced_full_set[sample, ]
validation <- balanced_full_set[!sample, ]

Models

The variable of interest is the binary enrollment variable. Given that this is a classification problem, I implemented multiple logistic regression, decision tree, random forest, and boosting models.

Multiple Logistic Regression

I generated null and full models so that I could implement a stepwise selection methodology to ultimately pick my final logistic model. When implementing the full model, I left out the variable mailq_4 since there were only 0’s present among my training dataset for that variable. I also left out TOTAL_CONTACTS since it is linearly dependent with the other contacts variables. Given that I have a dummy variable for each possible value of satscore and distance, I left out one indicator for each respectively, again to prevent perfect linear dependencies among my x variables.

# Null model
logit_null <- glm(Enroll ~ 1, data = train, family = binomial(link = "logit"))
# Full model
logit_full <- glm(Enroll ~ CONTACT_DATE1 + SELF_INIT_CNTCTS 
                  + TRAVEL_INIT_CNTCTS + SOLICITED_CNTCTS + REFERRAL_CNTCTS 
                  + CAMPUS_VISIT + premiere + stuemail + init_span + int1rat 
                  + int2rat + hscrat + avg_income + Instate + RECR_CODE_2 
                  + RECR_CODE_3 + RECR_CODE_4 + RECR_CODE_5 
                  + RECR_CODE_6 + RECR_CODE_7 + RECR_CODE_8 + RECR_CODE_missing 
                  + mailq_2 + mailq_3  + mailq_5 
                  + telecq_Missing + telecq_2 + telecq_3 + interest_0 
                  + interest_2 + interest_3 + sat_group_.920.1.04e.03.
                  + sat_group_.1.04e.03.1.14e.03. 
                  + sat_group_.1.14e.03.1.24e.03. + sat_group_.1.24e.03.Inf.  
                  + sat_group_missing + distance_group_.0.101. 
                  + distance_group_.160.290. 
                  + distance_group_.290.620. + distance_group_.620.Inf. 
                  + distance_group_missing, data = train, 
                  family = binomial(link = "logit"))
step(logit_null, scope = list(upper = logit_full),
     direction = "both", test = "Chisq", data = train)
logit_final <- glm(formula = Enroll ~ SELF_INIT_CNTCTS + hscrat + 
                     sat_group_missing + stuemail + premiere + telecq_3 + 
                     int2rat + CAMPUS_VISIT + init_span + 
                     sat_group_.1.14e.03.1.24e.03. + 
                     sat_group_.1.04e.03.1.14e.03. + RECR_CODE_5 + mailq_5 + 
                     TRAVEL_INIT_CNTCTS + int1rat + sat_group_.1.24e.03.Inf. + 
                     sat_group_.920.1.04e.03. + telecq_Missing + RECR_CODE_2 + 
                     interest_0 + distance_group_missing + REFERRAL_CNTCTS + 
                     Instate + avg_income + telecq_2, 
                   family = binomial(link = "logit"),
                   data = train)
test_predicted_logit_final <- predict(logit_final, newdata = validation,
                                      type = "response")
logit_conf_matrix <- table(validation$Enroll, 
                           test_predicted_logit_final > 0.5) %>%
  prop.table()
rownames(logit_conf_matrix) <- c("Didn't Enroll", "Enrolled")
colnames(logit_conf_matrix) <- c("Predicted Non-enrollment", 
                                  "Predicted Enrollment")

Decision Tree

tree_fit <- rpart(Enroll ~ ., data = train, method = "class")
test_predicted_tree <- predict(tree_fit, validation, type = "class")
tree_conf_matrix <- table(validation$Enroll, test_predicted_tree) %>%
  prop.table()
rownames(tree_conf_matrix) <- c("Didn't Enroll", "Enrolled")
colnames(tree_conf_matrix) <- c("Predicted Non-enrollment", 
                                  "Predicted Enrollment")

Random Forest

I had to first convert the target variable Enroll to a factor so that the randomForest() function would know to implement CART when generating the decision trees.

train$Enroll <- as.factor(train$Enroll)
forest_fit <- randomForest(Enroll ~ ., data = train, ntree = 500)
test_predicted_forest <- predict(forest_fit, validation, type = "class")
forest_conf_matrix <- table(validation$Enroll, test_predicted_forest) %>% 
  prop.table()
rownames(forest_conf_matrix) <- c("Didn't Enroll", "Enrolled")
colnames(forest_conf_matrix) <- c("Predicted Non-enrollment", 
                                  "Predicted Enrollment")

Gradient Boosting

fit_control <- trainControl(method = "cv", number = 10)
tune_grid <- expand.grid(interaction.depth = 2, n.trees = 500, shrinkage = 0.1,
                         n.minobsinnode = 10)                            
boost_fit <- train(Enroll ~ ., data = train, method = "gbm", 
                   trControl = fit_control, verbose = FALSE, 
                   tuneGrid = tune_grid)
1 package is needed for this model and is not installed. (gbm). Would you like to try to install it now?
1: yes
2: no

My logistic regression had a misclassification rate of 6.7%. My decision tree had a misclassification rate of 6.8%. My random forest had a misclassification rate of 5.75%. And lastly, my gradient boosting model had a misclassification rate of 5.78%.

More interesting from the models is actually the percentage of students who enrolled that the model actually predicted correctly, since this goal is more in line with that of the project. The logistic regression correctly identified 91.67% of the students who enrolled. The decision tree correctly identified 98.32% of the students who enrolled. The random forest correctly identified 94.05% of the students who enrolled. And lastly, my gradient boosing model correctly identified 93.15% of students who enrolled.

Executive Summary

The results of my analysis show that a simple decision tree does the best job modeling whether or not a student will enroll. Overall it correctly classified 92.2% percent of all potential students as to whether or not they enrolled. More importantly, it predicted 98% percent of the students who actually enrolled correctly.

Given that the purpose of this analysis was to most accurately identify those students who would enroll in the school, I subsetted the data so that I was taking all students who ultimately enrolled, but only a proportion of the students who did not enroll. On this subset, decision tree performed the best. This seems odd from a statistical perspective, but given that the random forest and the boosting model were tasked with lowering the overall misclassification rate (which they successfully did), they actually lost predictive power when selecting the students who were going to enroll in exchange for more accurately correctly identifying students who were not going to enroll.

Therefore my final model is the decision tree.

The foremost variable in determining whether or not a student would enroll was the number of self-initiated contacts. If the student reached out to 3 or more people, then that was the greatest indicator that that student would enroll. Of those who did not reach out to 3 or more people, if they also did not provide their SAT scores, then they did not enroll in the fall. This is true for all 3923 such potential students.

Of those who self-initiated contact with three or more people, the next greatest differentiator was their high school’s historical enrollment rate. If the high school typically sent less than 1.2% of its students to the university, then that student was much less likely to enroll in the school.

The least effective node in the model was the one where students did not self initiate three or more contacts, were not missing their sat scores, had a student email, and whose high schools sent more than 3.9% to the university. For those falling into this category, our model only accurately assessed 191/(116 + 191) of these students correctly.

The node where we identified the most students to not enroll who actually ended up enrolling was the node consisting of students who self-initiated 3 or more contacts and whose high schools sent fewer than 1.2% of its students to the university. There were ten students falling into this category who ultimately enrolled that were classified as not enrolling.

This model will enable the university to more accurately assess the factors that ultimately drive students to enroll. The number of self-initiated contacts, whether the student had submitted the SAT, whether the student had an email, and the rate of enrollment from each student’s high school all played important rolls in whether or not a student ultimately enrolled.

From a business perspective, the university likely wants to know which students are on the fringes of enrollment. The university can then divy out remaining scholarship funds accordingly in order to encourage certain students on the edge to enroll. Based on this analysis, the school knows to target scholarship funds to those who chose not to submit their sat scores, who have emails, and whose high schools typically don’t send many students to the university.

If the university wants to improve its global footprint, then it should target the same group of students with more advertising and events in their respective geographic areas, thereby increasing the number of students enrolling from different areas of the country and likely boosting public perception of the university.

Perhaps most surprising from the analysis is that whether or not a student is in-state did not have a major impact on whether or not they would enroll in the university. This likely indicates that the school in question does not provide in-state tuition and is therefore probably a small liberal arts school.

LS0tDQp0aXRsZTogIkFuYWx5c2lzIG9mIFVuaXZlcnNpdHkgRW5yb2xsbWVudCINCmF1dGhvcjogIk1vcmdhbiBIb3dhcmQiDQpkYXRlOiAiT2N0b2JlciA4LCAyMDE3Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQojIFByb2plY3QgUHJvcG9zYWwgey50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQpUaGUgcHJpbWFyeSBnb2FsIG9mIHRoaXMgcHJvamVjdCB3YXMgdG8gaWRlbnRpZnkgcHJvc3BlY3RpdmUgc3R1ZGVudHMgd2hvDQp3aWxsIG1vc3QgbGlrZWx5IGVucm9sbCBhcyBmcmVzaG1hbiBpbiB0aGUgdXBjb21pbmcgc2Nob29sIHllYXIgZm9yIGEgbWlkLXNpemUNCnVuaXZlcnNpdHkgdGhhdCBlbnJvbGxzIDI0MDAgdG8gMjgwMCBuZXcgZnJlc2htYW4gZWFjaCB5ZWFyLg0KDQpUaGUgZGF0YXNldCB3YXMgYnVpbHQgb3ZlciBhIHBlcmlvZCBvZiBzZXZlcmFsIG1vbnRocyBpbiBjb25zdWx0YXRpb24gd2l0aA0KRW5yb2xsbWVudCBNYW5hZ2VtZW50LiAgVGhlIGRhdGEgY29udGFpbmVkIG1hbnkgbWlzc2luZyB2YWx1ZXMgdGhhdCB3ZXJlIGRlYWx0DQp3aXRoIGluIGEgZmV3IGRpZmZlcmVudCB3YXlzIGRlcGVuZGluZyBvbiB0aGUgdmFyaWFibGUuICBJbiB0b3RhbCwgaXQgY29uc2lzdGVkDQpvZiAxMDY3NjcgbWlzc2luZyB2YWx1ZXMgYWNyb3NzIGByIG5yb3coZnVsbF9zZXQpYCANCm9ic2VydmF0aW9ucy4NCg0KVGhlIHJlc3VsdHMgb2YgdGhpcyBhbmFseXNpcyBjYW4gYmUgdXNlZCBpbiB0aGUgZnV0dXJlIHRvIGRldGVybWluZQ0KcHJvYmFiaWxpdGllcyBvZiBlbnJvbGxtZW50IGZvciBhIGdpdmVuIHN0dWRlbnQuDQoNCg0KIyMgUmVxdWlyZWQgUGFja2FnZXMNCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0NCmluc3RhbGwucGFja2FnZXMoImdibSIpDQppbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZHVtbXkpDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQpsaWJyYXJ5KHBseXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoc3Vydml2YWwpDQpsaWJyYXJ5KHNwbGluZXMpDQpsaWJyYXJ5KHBhcmFsbGVsKQ0KYGBgDQoNCiMjIERhdGEgSW1wb3J0DQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzID0gImhpZGUifQ0Kc2V0d2QoIkM6L1VzZXJzL21vcmdhL09uZURyaXZlL0RvY3VtZW50cy9QcmVkaWN0aXZlIEFuYWx5dGljcyAmIERhdGEgTWluaW5nIC0gQkFOIDYwMjUiKQ0Kc2Nob29sX2RhdGEgPC0gcmVhZF9jc3YoImlucTA1X3NhbXAuY3N2IikNCiNzY2hvb2xfZGF0YV9kaWN0IDwtIHJlYWRfY3N2KCJpbnEwNV9kYXRhX2RpY3QuY3N2IikNCmBgYA0KDQoNCiMjIERhdGEgQ2xlYW5pbmcgey50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQojIyMgRHJvcHBpbmcgVmFyaWFibGVzDQoNCkkgY2hvc2UgdG8gZHJvcCBzb21lIHZhcmlhYmxlcyBwcmVzZW50IGluIG15IGRhdGFzZXQuICBUaGVzZSBpbmNsdWRlZA0KPGNvZGU+QUNBREVNSUNfSU5URVJFU1RfMTwvY29kZT4sIDxjb2RlPkFDQURFTUlDX0lOVEVSRVNUXzI8L2NvZGU+LCANCjxjb2RlPklSU0NIT09MPC9jb2RlPiwgYW5kIDxjb2RlPkxFVkVMX1lFQVI8L2NvZGU+LiAgSSBkcm9wcGVkIHRoZSBmaXJzdCBvZiANCnRob3NlIHRocmVlIGJlY2F1c2UgdGhleSB3ZXJlIHJlcGxhY2VkIGJ5IDxjb2RlPklOVDFSQVQ8L2NvZGU+LCANCjxjb2RlPklOVDJSQVQ8L2NvZGU+LCBhbmQgPGNvZGU+aHNjcmF0PC9jb2RlPiByZXNwZWN0aXZlbHkuICBUaGlzIGNvbnRyb2xzIGZvcg0KcG9wdWxhdGlvbiBhdCBlYWNoIG9mIHRoZXNlIGxldmVscy4gIEkgZHJvcHBlZCA8Y29kZT5MRVZFTF9ZRUFSPC9jb2RlPiBiZWNhdXNlDQp0aGlzIHZhcmlhYmxlIG9ubHkgY29uc2lzdHMgb2Ygb25lIGxldmVsIHNpbmNlIHdlIGFyZSBjb25zaWRlcmluZyBvbmx5IG9uZSANCnllYXIncyB3b3J0aCBvZiBhcHBsaWNhbnRzLiAgSSBhbHNvIGRyb3BwZWQgdGhlIHZhcmlhYmxlcyA8Y29kZT5FVEhOX0NPREU8L2NvZGU+DQphbmQgPGNvZGU+c2V4PC9jb2RlPiBzaW5jZSBldGhuaWNpdHkgYW5kIHNleCBjYW5ub3QgYmUgdXNlZCBieSB1bml2ZXJzaXRpZXMgDQpkdXJpbmcgYWRtaXNzaW9ucyBkZWNpc2lvbnMuDQoNCmBgYHtyfQ0KZHJvcHBlZF92YXJzIDwtIGMoIkFDQURFTUlDX0lOVEVSRVNUXzEiLCAiQUNBREVNSUNfSU5URVJFU1RfMiIsICJJUlNDSE9PTCIsIA0KICAgICAgICAgICAgICAgICAgIkVUSE5fQ09ERSIsICJzZXgiLCAiTEVWRUxfWUVBUiIpDQoNCmZ1bGxfc2V0IDwtIHNjaG9vbF9kYXRhICU+JQ0KICAgc2VsZWN0KC1vbmVfb2YoZHJvcHBlZF92YXJzKSkNCmBgYA0KDQoNCkZvciA8Y29kZT5tYWlscTwvY29kZT4sIDxjb2RlPnRlbGVjcTwvY29kZT4sIGFuZCA8Y29kZT5pbnRlcmVzdDwvY29kZT4sIEkgDQpvcGVyYXRlZCB1bmRlciB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZXkgc2hvdWxkIG5vdCBiZSB0cmVhdGVkIGFzIHB1cmVseSBudW1lcmljDQpidXQgcmF0aGVyIGFzIGNhdGVnb3JpY2FsLiAgRm9yIGluc3RhbmNlLCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGEgMiBhbmQgYSAzDQp3aGVuIHJhdGluZyBpbnRlcmVzdCBpbiB0aGUgc2Nob29sIGlzIG5vdCB0aGUgc2FtZSBhcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGENCjQgYW5kIGEgNSAgVGhlcmVmb3JlIEkgcmFuIGEgY2hpLXNxdWFyZSB0ZXN0IG9uIGVhY2ggb2YgdGhlIHZhcmlhYmxlcyBhbmQgDQpjb25zaWRlcmVkIHRoZWlyIHAtdmFsdWVzLiAgU2luY2UgZWFjaCBvZiB0aGUgY2hpLXNxdWFyZSB0ZXN0cyBzaG93ZWQgDQpzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgYWNyb3NzIGdyb3VwcywgSSBjaG9zZSB0byBjb252ZXJ0IGVhY2ggb2YgdGhlIHRocmVlDQp2YXJpYWJsZXMgdG8gZmFjdG9ycy4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpvcmRlcmVkX2ZhY3Rvcl92YXJzIDwtIGMoIm1haWxxIiwgInRlbGVjcSIsICJpbnRlcmVzdCIpDQoNCg0KZm9yIChpIGluIG9yZGVyZWRfZmFjdG9yX3ZhcnMpew0KICBmdWxsX3NldFtbaV1dIDwtIGZhY3RvcihmdWxsX3NldFtbaV1dKQ0KICBwcmludChjaGlzcS50ZXN0KHRhYmxlKGZ1bGxfc2V0W1tpXV0sIGZ1bGxfc2V0JEVucm9sbCkpKQ0KfQ0KYGBgDQoNCkkgY29udmVydGVkIHRoZSB0aW1lIHZhcmlhYmxlIDxjb2RlPkNPTlRBQ1RfREFURTE8L2NvZGU+IHRvIGEgcHJvcGVyIHRpbWUgZm9ybWF0DQp1c2luZyB0aGUgPGNvZGU+bHVicmlkYXRlPC9jb2RlPiBwYWNrYWdlIGFzIHN1Y2g6DQoNCmBgYHtyfQ0KZnVsbF9zZXQkQ09OVEFDVF9EQVRFMSA8LSBkbXkoZnVsbF9zZXQkQ09OVEFDVF9EQVRFMSkNCmBgYA0KDQpJIGNob3NlIHRvIGRyb3AgdGhlIDxjb2RlPkNPTlRBQ1RfQ09ERTE8L2NvZGU+IHZhcmlhYmxlIGR1ZSB0byBpdHMgaGlnaCBudW1iZXINCm9mIGNhdGVnb3JpZXMgYW5kIGxvdyBwcm9wb3J0aW9uIG9mIHN0dWRlbnRzIGFjcm9zcyBlYWNoIGNhdGVnb3J5Lg0KDQpgYGB7cn0NCmZ1bGxfc2V0IDwtIGZ1bGxfc2V0ICU+JQ0KICBzZWxlY3QoLUNPTlRBQ1RfQ09ERTEpDQpgYGANCg0KSSBjb252ZXJ0ZWQgdGhlIGluZGljYXRvciB2YXJpYWJsZSBmb3IgPGNvZGU+RW5yb2xsPC9jb2RlPiBmcm9tIGJlaW5nICJZIiBhbmQgDQoiTiIgdG8gYSAxIGFuZCAwIHJlc3BlY3RpdmVseS4NCg0KYGBge3J9DQpmdWxsX3NldCRJbnN0YXRlW2Z1bGxfc2V0JEluc3RhdGUgPT0gIlkiXSA8LSAxDQpmdWxsX3NldCRJbnN0YXRlW2Z1bGxfc2V0JEluc3RhdGUgPT0gIk4iXSA8LSAwDQpmdWxsX3NldCRJbnN0YXRlIDwtIGFzLm51bWVyaWMoZnVsbF9zZXQkSW5zdGF0ZSkNCmBgYA0KDQpGb3IgPGNvZGU+UkVDUl9DT0RFPC9jb2RlPiwgSSB1c2VkIG15IGJlc3QganVkZ21lbnQgYW5kIGNob3NlIHRvIGNvbnZlcnQgdmFsdWVzDQpvZiAwMDAsIE4sIGFuZCBBIHRvIGJlIE5BLiAgVGhlcmUgd2VyZSAzODE1IHN1Y2ggdmFsdWVzLg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCmZ1bGxfc2V0JFJFQ1JfQ09ERVtmdWxsX3NldCRSRUNSX0NPREUgJWluJSBjKCIwMDAiLCAiTiIsICJBIildIDwtIE5BDQpgYGANCg0KVGhpcyBsZWZ0IDM4MTYgTkEgdmFsdWVzIGZvciB0aGUgDQo8Y29kZT5SRUNSX0NPREU8L2NvZGU+IHZhcmlhYmxlLiAgVW5kZXIgdGhlIGFzc3VtcHRpb24gdGhhdCB0aGVzZSB3ZXJlIG5vdCANCm1pc3NpbmcgYXQgcmFuZG9tLCBJIGNob3NlIHRvIGdlbmVyYXRlIGJpbnMgZm9yIGVhY2ggcG9zc2libGUgdmFsdWUgb2YgDQo8Y29kZT5SRUNSX0NPREU8L2NvZGU+LCB3aXRoIE5BIHZhbHVlcyBoYXZpbmcgdGhlaXIgb3duIHJlc3BlY3RpdmUgYmluLiAgSSByYW4NCmEgY2hpc3F1YXJlIHRlc3QgYWNyb3NzIHRoZSBkaWZmZXJlbnQgdmFsdWVzIG9mIDxjb2RlPlJFQ1JfQ09ERTwvY29kZT4gdG8gc2VlDQppZiBJIHdhcyBqdXN0aWZpZWQgaW4gZG9pbmcgdGhpcy4NCg0KYGBge3J9DQpjaGlzcS50ZXN0KHRhYmxlKGZ1bGxfc2V0JFJFQ1JfQ09ERSwgZnVsbF9zZXQkRW5yb2xsKSkNCg0KYGBgDQoNClRoZSBsb3cgcC12YWx1ZSBsZWQgbWUgdG8gZmVlbCByZWFzc3VyZWQgaW4gZG9pbmcgc28uICBBbHRob3VnaCB0aGUgdmFsdWVzIGZvcg0KPGNvZGU+UkVDUl9DT0RFPC9jb2RlPiBleHRlbmQgdXAgdG8gOCwgSSBjaG9zZSB0byB0cmVhdCBlYWNoIHZhbHVlIGFzIGhhdmluZyBhDQpzZXBhcmF0ZSBjYXRlZ29yeSBpbiBvcmRlciB0byBtYWludGFpbiB0aGUgcm93cyBjb250YWluaW5nIE5BIG9ic2VydmF0aW9ucyBpbiBteQ0KZGF0YXNldCBmb3IgbXkgbGF0ZXIgbW9kZWwgaW1wbGVtZW50YXRpb25zLg0KDQpgYGB7cn0NCmZ1bGxfc2V0JFJFQ1JfQ09ERVtpcy5uYShmdWxsX3NldCRSRUNSX0NPREUpXSA8LSAibWlzc2luZyINCg0KZnVsbF9zZXQkUkVDUl9DT0RFIDwtIGZhY3RvcihmdWxsX3NldCRSRUNSX0NPREUpDQpgYGANCg0KDQpOb3RlIHRoYXQgSSBpbXB1dGVkIHRoZXNlIHZhbHVlcyB0byAibWlzc2luZyIgcmF0aGVyIHRoYW4gcHVyZWx5IE5BLiAgSSBkaWQgdGhpcw0Kc28gdGhhdCB0aGVzZSB2YWx1ZXMgbWlnaHQgcmVjZWl2ZSB0aGVpciBvd24gZmFjdG9yIGxldmVsIHdoZW4gSSBnZW5lcmF0ZQ0KZHVtbXkgdmFyaWFibGVzIGZvciBlYWNoIGNhdGVnb3J5IGxhdGVyIGluIHRoZSBhbmFseXNpcy4NCg0KIyMjIE5BIFZhbHVlcw0KDQojIyMjIFNBVCBTY29yZQ0KDQpUaGVyZSB3ZXJlIDM4Nzg2IG1pc3NpbmcgU0FUIHNjb3JlIHZhbHVlcyBpbiB0aGUgDQpkYXRhc2V0LiAgU2luY2UgdGhlc2Ugd2VyZSBsaWtlbHkgbm90IG1pc3NpbmcgYXQgcmFuZG9tICh0aG9zZSB3aG8gY2hvb3NlIG5vdCB0bw0KZGlzY2xvc2UgdGhlaXIgU0FUIHNjb3JlcyBsaWtlbHkgc2NvcmVkIHBvb3JseSksIEkgZGVjaWRlZCB0byBiaW4gdGhlIFNBVA0Kc2NvcmVzIGJ5IHByb3BvcnRpb25hbGl0eSwgd2l0aCBOQSB2YWx1ZXMgcmVjZWl2aW5nIHRoZWlyIG93biBiaW4uICBJIGJpbm5lZA0KYmFzZWQgb24gdGhlIHF1aW50aWxlcyBvZiB0aGUgb2JzZXJ2ZWQgU0FUIHNjb3JlcywgdGh1cyBnZW5lcmF0aW5nIDUgYmlucyBmb3INCnRoZSBhY3R1YWxseSByZXBvcnRlZCBTQVQgc2NvcmVzIGFuZCBvbmUgYWRkaXRpb25hbCBiaW4gZm9yIHRoZSBOQSB2YWx1ZXMuICBJDQpjaG9zZSB0aGUgdmFsdWUgb2YgNSBiaW5zIGFyYml0cmFyaWx5Lg0KDQpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQ0Kc2F0X3NwbGl0cyA8LSBjKDAsIHF1YW50aWxlKGZ1bGxfc2V0JHNhdHNjb3JlLCBjKC4yLCAuNCwgLjYsIC44KSwgDQogICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSksIEluZikNCg0KDQpmdWxsX3NldCA8LSBjYmluZChmdWxsX3NldCwgIHNhdF9ncm91cCA9IGN1dChmdWxsX3NldCRzYXRzY29yZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2F0X3NwbGl0cykpDQoNCmxldmVscyhmdWxsX3NldCRzYXRfZ3JvdXApIDwtIGMobGV2ZWxzKGZ1bGxfc2V0JHNhdF9ncm91cCksICJtaXNzaW5nIikNCg0KZnVsbF9zZXQkc2F0X2dyb3VwW2lzLm5hKGZ1bGxfc2V0JHNhdF9ncm91cCldIDwtICJtaXNzaW5nIg0KDQojIyBSZW1vdmluZyBpbml0aWFsIHNhdHNjb3JlIHZhcmlhYmxlDQoNCmZ1bGxfc2V0IDwtIGZ1bGxfc2V0ICU+JQ0KICBzZWxlY3QoLXNhdHNjb3JlKQ0KYGBgDQoNCiMjIyMgVGVsZWNxDQoNClRoZXJlIHdlcmUgNDMxOTAgbWlzc2luZyB0ZWxlcGhvbmUgaW50ZXJlc3Qgc2NvcmVzIGluDQp0aGUgZGF0YXNldC4gIFNpbmNlIHRoZXNlIHdlcmUgbGlrZWx5IG5vdCBtaXNzaW5nIGF0IHJhbmRvbSAoc29tZWJvZHkgd2hvIGhhbmdzDQp1cCB0aGUgcGhvbmUgaXMgbW9yZSBsaWtlbHkgdG8gbm90IGJlIGludGVyZXN0ZWQgdGhhbiBpbnRlcmVzdGVkIGluIGVucm9sbG1lbnQpLA0KSSBjaG9zZSB0byBwcm92aWRlIGEgc2VwYXJhdGUgYmluIGZvciB0aG9zZSBvYnNlcnZhdGlvbnMgd2l0aCBtaXNzaW5nIA0KPGNvZGU+dGVsZWNxPC9jb2RlPiB2YWx1ZXMuDQoNCmBgYHtyfQ0KbGV2ZWxzKGZ1bGxfc2V0JHRlbGVjcSkgPC0gYygiTWlzc2luZyIsIGxldmVscyhmdWxsX3NldCR0ZWxlY3EpKQ0KDQpmdWxsX3NldCR0ZWxlY3FbaXMubmEoZnVsbF9zZXQkdGVsZWNxKV0gPC0gIk1pc3NpbmciDQoNCmZ1bGxfc2V0JHRlbGVjcSA8LSBmYWN0b3IoZnVsbF9zZXQkdGVsZWNxKQ0KYGBgDQoNCiMjIyMgQXZlcmFnZSBJbmNvbWUNCg0KVGhlcmUgd2VyZSAxMjgwMSBtaXNzaW5nIGluY29tZSB2YWx1ZXMgaW4gdGhlIA0KZGF0YXNldC4gIEkgYXNzdW1lZCB0aGF0IHRoZXNlIHdlcmUgbWlzc2luZyBhdCByYW5kb20gYW5kIHRoYXQgYXZlcmFnZSBpbmNvbWUNCmRpZCBub3QgaGF2ZSBhbiBpbW1lZGlhdGUgaW1wYWN0IG9uIGVucm9sbG1lbnQgYXQgdGhlIHVuaXZlcnNpdHkuICBJIHRoZXJlZm9yZQ0KaW1wdXRlZCB0aGUgbWVhbiBmb3IgdGhlc2UgdmFsdWVzLg0KDQpgYGB7cn0NCmF2Z19hdmdfaW5jb21lIDwtIG1lYW4oZnVsbF9zZXQkYXZnX2luY29tZSwgbmEucm0gPSBUUlVFKQ0KDQpmdWxsX3NldCRhdmdfaW5jb21lW2lzLm5hKGZ1bGxfc2V0JGF2Z19pbmNvbWUpXSA8LSBhdmdfYXZnX2luY29tZQ0KYGBgDQoNCiMjIyMgRGlzdGFuY2UNCg0KVGhlcmUgd2VyZSAxMTkwNSBtaXNzaW5nIGRpc3RhbmNlIHZhbHVlcyBpbiB0aGUgDQpkYXRhc2V0LiAgSSBvcGVyYXRlZCB1bmRlciB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZXNlIHdlcmUgbWlzc2luZyBiZWNhdXNlIHRoZSANCnNjaG9vbCBtaWdodCBub3Qga25vdyBleGFjdCBkaXN0YW5jZSB0byBhIGdpdmVuIHByb3NwZWN0aXZlIHN0dWRlbnQncyBob21ldG93bi4NCkkgdGhlcmVmb3JlIGNob3NlIHRvIHB1dCBiaW4gdGhlIGRpc3RhbmNlIHZhbHVlcyBzaW1pbGFybHkgdG8gaG93IEkgYmlubmVkIFNBVCANCnNjb3JlcywgYmlubmluZyBieSBxdWludGlsZXMsIHdpdGggTkEgdmFsdWVzIGdldHRpbmcgYSBzZXBhcmF0ZSBiaW4uDQoNCmBgYHtyfQ0KZGlzdGFuY2Vfc3BsaXRzIDwtIGMoMCwgcXVhbnRpbGUoZnVsbF9zZXQkZGlzdGFuY2UsIGMoMC4yLCAwLjQsIDAuNiwgMC44KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSwgSW5mKQ0KDQpmdWxsX3NldCA8LSBjYmluZChmdWxsX3NldCwgZGlzdGFuY2VfZ3JvdXAgPSBjdXQoZnVsbF9zZXQkZGlzdGFuY2UsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGRpc3RhbmNlX3NwbGl0cykpDQoNCmxldmVscyhmdWxsX3NldCRkaXN0YW5jZV9ncm91cCkgPC0gYyhsZXZlbHMoZnVsbF9zZXQkZGlzdGFuY2VfZ3JvdXApLCAibWlzc2luZyIpDQoNCmZ1bGxfc2V0JGRpc3RhbmNlX2dyb3VwW2lzLm5hKGZ1bGxfc2V0JGRpc3RhbmNlX2dyb3VwKV0gPC0gIm1pc3NpbmciDQoNCiMjIFJlbW92aW5nIGluaXRpYWwgZGlzdGFuY2UgdmFyaWFibGUNCg0KZnVsbF9zZXQgPC0gZnVsbF9zZXQgJT4lDQogIHNlbGVjdCgtZGlzdGFuY2UpDQpgYGANCg0KVGhlc2UgaW1wdXRhdGlvbnMgYW5kIGJpbm5pbmdzIGxlZnQgYHIgc3VtKGlzLm5hKGZ1bGxfc2V0KSlgIE5BIG9ic2VydmF0aW9ucw0KcmVtYWluaW5nIGluIHRoZSBkYXRhc2V0Lg0KDQojIyMgRHVtbXkgVmFyaWFibGUgR2VuZXJhdGlvbg0KDQpJIHVzZWQgdGhlIDxjb2RlPmR1bW15PC9jb2RlPiBwYWNrYWdlIHRvIGdlbmVyYXRlIGR1bW15IHZhcmlhYmxlcyBmb3IgZWFjaA0Kb2YgbXkgc3Vic2VxdWVudCBmYWN0b3IgdmFyaWFibGVzLiAgVGhpcyBnYXZlIGVhY2ggbGV2ZWwgd2l0aGluIGEgZmFjdG9yIGl0cyBvd24NCmR1bW15IHZhcmlhYmxlLiAgRm9yIGV4YW1wbGUsIFNBVCBzY29yZXMsIHNpbmNlIHRoZXkgYXJlIGNvbXByaXNlZCBvZiA2IGJpbnMsDQphcmUgbm93IHNwbGl0IGFjcm9zcyA2IGR1bW15IHZhcmlhYmxlcy4NCg0KYGBge3J9DQpmdWxsX3NldCA8LSBjYmluZChmdWxsX3NldCwgZHVtbXkoZnVsbF9zZXQpKQ0KDQoNCiMjIERyb3BwaW5nIGluaXRpYWwgZmFjdG9yIHZhcmlhYmxlcyB1c2VkIHRvIGdlbmVyYXRlIHRoZSBkdW1taWVzDQoNCmZ1bGxfc2V0IDwtIGZ1bGxfc2V0ICU+JQ0KICBzZWxlY3QoLWMobWFpbHEsIHRlbGVjcSwgaW50ZXJlc3QsIHNhdF9ncm91cCwgZGlzdGFuY2VfZ3JvdXAsIFJFQ1JfQ09ERSkpDQpgYGANCg0KDQoNCiMjIFNwbGl0dGluZyBpbnRvIFRyYWluaW5nIGFuZCBWYWxpZGF0aW9uDQoNCk5vdGUgdGhhdCBmb3IgdGhlIHBvdGVudGlhbCBzdHVkZW50cyBpbiB0aGUgZGF0YXNldCwgb25seSA1JSBhY3R1YWxseSBlbnJvbGxlZC4NClNpbmNlIEkgYW0gZm9jdXNpbmcgbXkgYW5hbHlzaXMgb24gZGlzY292ZXJpbmcgd2hpY2ggZmFjdG9ycyBzZWVlbSB0byBiZSANCmluZmx1ZW5jaW5nIHN0dWRlbnRzJyBkZWNpc2lvbnMgdG8gZW5yb2xsIGluIHRoZSB1bml2ZXJzaXR5LCBJIGNob3NlIHRvIHdlaWdodA0KdGhlIG1vZGVsIHNvIHRoYXQgaXQgZXJycyB0byBUeXBlIEkgZXJyb3JzIHJhdGhlciB0aGFuIFR5cGUgSUkuICBJIHdvdWxkIHByZWZlcg0KdG8gcHJlZGljdCB0aGF0IGEgc3R1ZGVudCBlbnJvbGxlZCB3aG8gYWN0dWFsbHkgZGlkbid0IHJhdGhlciB0aGFuIG5vdA0KcHJlZGljdCBhIHN0dWRlbnQgdG8gZW5yb2xsIHdobyBhY3R1YWxseSB3ZW50IG9uIHRvIGVucm9sbC4NClRvIGluY3JlYXNlIHRoZSByZWxhdGl2ZSBwcm9wb3J0aW9uIG9mIGVucm9sbG1lbnQgd2l0aGluIG15IGRhdGFzZXQsIEkgY2hvc2UNCnRvIHN1YnNldCB0aGUgZGF0YSBzbyB0aGF0IEkgYW0gdGFraW5nIGFsbCBvYnNlcnZhdGlvbnMNCmluIHRoZSBlbnJvbGxlZCBjYXRlZ29yeSwgYW5kIGEgc21hbGxlciBwcm9wb3J0aW9uIG9mIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlDQpub3QgZW5yb2xsZWQgY2F0ZWdvcnkuDQoNCkkgdGhlbiBzcGxpdCB0aGlzIHN1YnNldHRlZCBkYXRhIGludG8gNzAlIHRyYWluaW5nIGFuZCAzMCUgdmFsaWRhdGlvbiBzZXRzLg0KDQpgYGB7cn0NCmVucm9sbGVkX3NldCA8LSBmdWxsX3NldCAlPiUNCiAgZmlsdGVyKEVucm9sbCA9PSAxKQ0KDQpub25fZW5yb2xsZWRfc2V0IDwtIGZ1bGxfc2V0ICU+JQ0KICBmaWx0ZXIoRW5yb2xsID09IDApDQoNCiMgT25seSBzZWxlY3RpbmcgMjAlIG9mIHZhbHVlcyBhbW9uZyB0aG9zZSB3aG8gZGlkbid0IGVucm9sbCBzbyB0aGF0IHRoZSBzcGxpdCANCiMgb2YgZW5yb2xsZWQ6bm90IGVucm9sbGVkIGlzIGFwcHJveGltYXRlbHkgMjA6ODANCg0Kc2V0LnNlZWQoMTIzKQ0Kc2FtcGxlX25vbl9lbnJvbGxlZF9zZXQgPC0gc2FtcGxlKGMoVFJVRSwgRkFMU0UpLCBucm93KG5vbl9lbnJvbGxlZF9zZXQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSBULCBwcm9iID0gYygwLjE1LCAwLjg1KSkNCg0KaW5jbHVkZWRfbm9uX2Vucm9sbGVkIDwtIG5vbl9lbnJvbGxlZF9zZXRbc2FtcGxlX25vbl9lbnJvbGxlZF9zZXQsIF0NCm5vdF9pbmNsdWRlZF9ub25fZW5yb2xsZWQgPC0gbm9uX2Vucm9sbGVkX3NldFshc2FtcGxlX25vbl9lbnJvbGxlZF9zZXQsIF0NCg0KYmFsYW5jZWRfZnVsbF9zZXQgPC0gcmJpbmQoZW5yb2xsZWRfc2V0LCBpbmNsdWRlZF9ub25fZW5yb2xsZWQpDQoNCiMjIGNyZWF0aW5nIHRyYWluaW5nLXZhbGlkYXRpb24gc2V0czsgaW4gZnV0dXJlLCB3YW50IHRvIGV4cGxvcmUgYm9vdHN0cmFwDQojIyBtZXRob2RvbG9naWVzLCBrLWZvbGQgdGVjaG5pcXVlcywgYW5kIExlYXZlLW9uZS1vdXQgY3Jvc3MtdmFsaWRhdGlvbg0KIyMgVEhFU0UgV0lMTCBOT1QgQkUgVVNFRCBVTlRJTCBNT0RFTCBCVUlMRElORw0KDQpzYW1wbGUgPC0gc2FtcGxlKGMoVFJVRSwgRkFMU0UpLCBucm93KGJhbGFuY2VkX2Z1bGxfc2V0KSwNCiAgICAgICAgICAgICAgICAgcmVwbGFjZSA9IFQsIHByb2IgPSBjKDAuNywgMC4zKSkNCg0KdHJhaW4gPC0gYmFsYW5jZWRfZnVsbF9zZXRbc2FtcGxlLCBdDQp2YWxpZGF0aW9uIDwtIGJhbGFuY2VkX2Z1bGxfc2V0WyFzYW1wbGUsIF0NCmBgYA0KDQojIyBNb2RlbHMNCg0KVGhlIHZhcmlhYmxlIG9mIGludGVyZXN0IGlzIHRoZSBiaW5hcnkgZW5yb2xsbWVudCB2YXJpYWJsZS4gIEdpdmVuIHRoYXQgdGhpcyBpcyBhIGNsYXNzaWZpY2F0aW9uIHByb2JsZW0sIEkgaW1wbGVtZW50ZWQgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiwgZGVjaXNpb24gdHJlZSwgcmFuZG9tIGZvcmVzdCwgYW5kIGJvb3N0aW5nIG1vZGVscy4gIA0KDQojIyMgTXVsdGlwbGUgTG9naXN0aWMgUmVncmVzc2lvbg0KSSBnZW5lcmF0ZWQgbnVsbCBhbmQgZnVsbCBtb2RlbHMgc28gdGhhdCBJIGNvdWxkIGltcGxlbWVudCBhIHN0ZXB3aXNlIHNlbGVjdGlvbg0KbWV0aG9kb2xvZ3kgdG8gdWx0aW1hdGVseSBwaWNrIG15IGZpbmFsIGxvZ2lzdGljIG1vZGVsLiAgV2hlbiBpbXBsZW1lbnRpbmcgdGhlIGZ1bGwgbW9kZWwsIEkgbGVmdCBvdXQgdGhlIHZhcmlhYmxlIDxjb2RlPm1haWxxXzQ8L2NvZGU+IHNpbmNlIHRoZXJlIHdlcmUgb25seSAwJ3MgcHJlc2VudCBhbW9uZyBteSB0cmFpbmluZyBkYXRhc2V0IGZvciB0aGF0IHZhcmlhYmxlLiAgSSBhbHNvIGxlZnQgb3V0IDxjb2RlPlRPVEFMX0NPTlRBQ1RTPC9jb2RlPiBzaW5jZSBpdCBpcyBsaW5lYXJseSBkZXBlbmRlbnQgd2l0aCB0aGUgb3RoZXIgY29udGFjdHMgdmFyaWFibGVzLiAgR2l2ZW4gdGhhdCBJIGhhdmUgYSBkdW1teSB2YXJpYWJsZSBmb3IgZWFjaCBwb3NzaWJsZSB2YWx1ZSBvZiA8Y29kZT5zYXRzY29yZTwvY29kZT4gYW5kIDxjb2RlPmRpc3RhbmNlPC9jb2RlPiwgSSBsZWZ0IG91dCBvbmUgaW5kaWNhdG9yIGZvciBlYWNoIHJlc3BlY3RpdmVseSwgYWdhaW4gdG8gcHJldmVudCBwZXJmZWN0IGxpbmVhciBkZXBlbmRlbmNpZXMgYW1vbmcgbXkgeCB2YXJpYWJsZXMuDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSJ9DQojIE51bGwgbW9kZWwNCg0KbG9naXRfbnVsbCA8LSBnbG0oRW5yb2xsIH4gMSwgZGF0YSA9IHRyYWluLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IikpDQoNCiMgRnVsbCBtb2RlbA0KDQpsb2dpdF9mdWxsIDwtIGdsbShFbnJvbGwgfiBDT05UQUNUX0RBVEUxICsgU0VMRl9JTklUX0NOVENUUyANCiAgICAgICAgICAgICAgICAgICsgVFJBVkVMX0lOSVRfQ05UQ1RTICsgU09MSUNJVEVEX0NOVENUUyArIFJFRkVSUkFMX0NOVENUUyANCiAgICAgICAgICAgICAgICAgICsgQ0FNUFVTX1ZJU0lUICsgcHJlbWllcmUgKyBzdHVlbWFpbCArIGluaXRfc3BhbiArIGludDFyYXQgDQogICAgICAgICAgICAgICAgICArIGludDJyYXQgKyBoc2NyYXQgKyBhdmdfaW5jb21lICsgSW5zdGF0ZSArIFJFQ1JfQ09ERV8yIA0KICAgICAgICAgICAgICAgICAgKyBSRUNSX0NPREVfMyArIFJFQ1JfQ09ERV80ICsgUkVDUl9DT0RFXzUgDQogICAgICAgICAgICAgICAgICArIFJFQ1JfQ09ERV82ICsgUkVDUl9DT0RFXzcgKyBSRUNSX0NPREVfOCArIFJFQ1JfQ09ERV9taXNzaW5nIA0KICAgICAgICAgICAgICAgICAgKyBtYWlscV8yICsgbWFpbHFfMyAgKyBtYWlscV81IA0KICAgICAgICAgICAgICAgICAgKyB0ZWxlY3FfTWlzc2luZyArIHRlbGVjcV8yICsgdGVsZWNxXzMgKyBpbnRlcmVzdF8wIA0KICAgICAgICAgICAgICAgICAgKyBpbnRlcmVzdF8yICsgaW50ZXJlc3RfMyArIHNhdF9ncm91cF8uOTIwLjEuMDRlLjAzLg0KICAgICAgICAgICAgICAgICAgKyBzYXRfZ3JvdXBfLjEuMDRlLjAzLjEuMTRlLjAzLiANCiAgICAgICAgICAgICAgICAgICsgc2F0X2dyb3VwXy4xLjE0ZS4wMy4xLjI0ZS4wMy4gKyBzYXRfZ3JvdXBfLjEuMjRlLjAzLkluZi4gIA0KICAgICAgICAgICAgICAgICAgKyBzYXRfZ3JvdXBfbWlzc2luZyArIGRpc3RhbmNlX2dyb3VwXy4wLjEwMS4gDQogICAgICAgICAgICAgICAgICArIGRpc3RhbmNlX2dyb3VwXy4xNjAuMjkwLiANCiAgICAgICAgICAgICAgICAgICsgZGlzdGFuY2VfZ3JvdXBfLjI5MC42MjAuICsgZGlzdGFuY2VfZ3JvdXBfLjYyMC5JbmYuIA0KICAgICAgICAgICAgICAgICAgKyBkaXN0YW5jZV9ncm91cF9taXNzaW5nLCBkYXRhID0gdHJhaW4sIA0KICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQ0KDQpzdGVwKGxvZ2l0X251bGwsIHNjb3BlID0gbGlzdCh1cHBlciA9IGxvZ2l0X2Z1bGwpLA0KICAgICBkaXJlY3Rpb24gPSAiYm90aCIsIHRlc3QgPSAiQ2hpc3EiLCBkYXRhID0gdHJhaW4pDQoNCmxvZ2l0X2ZpbmFsIDwtIGdsbShmb3JtdWxhID0gRW5yb2xsIH4gU0VMRl9JTklUX0NOVENUUyArIGhzY3JhdCArIA0KICAgICAgICAgICAgICAgICAgICAgc2F0X2dyb3VwX21pc3NpbmcgKyBzdHVlbWFpbCArIHByZW1pZXJlICsgdGVsZWNxXzMgKyANCiAgICAgICAgICAgICAgICAgICAgIGludDJyYXQgKyBDQU1QVVNfVklTSVQgKyBpbml0X3NwYW4gKyANCiAgICAgICAgICAgICAgICAgICAgIHNhdF9ncm91cF8uMS4xNGUuMDMuMS4yNGUuMDMuICsgDQogICAgICAgICAgICAgICAgICAgICBzYXRfZ3JvdXBfLjEuMDRlLjAzLjEuMTRlLjAzLiArIFJFQ1JfQ09ERV81ICsgbWFpbHFfNSArIA0KICAgICAgICAgICAgICAgICAgICAgVFJBVkVMX0lOSVRfQ05UQ1RTICsgaW50MXJhdCArIHNhdF9ncm91cF8uMS4yNGUuMDMuSW5mLiArIA0KICAgICAgICAgICAgICAgICAgICAgc2F0X2dyb3VwXy45MjAuMS4wNGUuMDMuICsgdGVsZWNxX01pc3NpbmcgKyBSRUNSX0NPREVfMiArIA0KICAgICAgICAgICAgICAgICAgICAgaW50ZXJlc3RfMCArIGRpc3RhbmNlX2dyb3VwX21pc3NpbmcgKyBSRUZFUlJBTF9DTlRDVFMgKyANCiAgICAgICAgICAgICAgICAgICAgIEluc3RhdGUgKyBhdmdfaW5jb21lICsgdGVsZWNxXzIsIA0KICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwNCiAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW4pDQoNCnRlc3RfcHJlZGljdGVkX2xvZ2l0X2ZpbmFsIDwtIHByZWRpY3QobG9naXRfZmluYWwsIG5ld2RhdGEgPSB2YWxpZGF0aW9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInJlc3BvbnNlIikNCg0KbG9naXRfY29uZl9tYXRyaXggPC0gdGFibGUodmFsaWRhdGlvbiRFbnJvbGwsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdF9wcmVkaWN0ZWRfbG9naXRfZmluYWwgPiAwLjUpICU+JQ0KICBwcm9wLnRhYmxlKCkNCg0Kcm93bmFtZXMobG9naXRfY29uZl9tYXRyaXgpIDwtIGMoIkRpZG4ndCBFbnJvbGwiLCAiRW5yb2xsZWQiKQ0KY29sbmFtZXMobG9naXRfY29uZl9tYXRyaXgpIDwtIGMoIlByZWRpY3RlZCBOb24tZW5yb2xsbWVudCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcmVkaWN0ZWQgRW5yb2xsbWVudCIpDQpgYGANCg0KIyMjIERlY2lzaW9uIFRyZWUNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQp0cmVlX2ZpdCA8LSBycGFydChFbnJvbGwgfiAuLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJjbGFzcyIpDQoNCnRlc3RfcHJlZGljdGVkX3RyZWUgPC0gcHJlZGljdCh0cmVlX2ZpdCwgdmFsaWRhdGlvbiwgdHlwZSA9ICJjbGFzcyIpDQoNCnRyZWVfY29uZl9tYXRyaXggPC0gdGFibGUodmFsaWRhdGlvbiRFbnJvbGwsIHRlc3RfcHJlZGljdGVkX3RyZWUpICU+JQ0KICBwcm9wLnRhYmxlKCkNCg0Kcm93bmFtZXModHJlZV9jb25mX21hdHJpeCkgPC0gYygiRGlkbid0IEVucm9sbCIsICJFbnJvbGxlZCIpDQpjb2xuYW1lcyh0cmVlX2NvbmZfbWF0cml4KSA8LSBjKCJQcmVkaWN0ZWQgTm9uLWVucm9sbG1lbnQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHJlZGljdGVkIEVucm9sbG1lbnQiKQ0KYGBgDQoNCiMjIyBSYW5kb20gRm9yZXN0DQoNCkkgaGFkIHRvIGZpcnN0IGNvbnZlcnQgdGhlIHRhcmdldCB2YXJpYWJsZSA8Y29kZT5FbnJvbGw8L2NvZGU+IHRvIGEgZmFjdG9yIHNvIHRoYXQgdGhlIDxjb2RlPnJhbmRvbUZvcmVzdCgpPC9jb2RlPiBmdW5jdGlvbiB3b3VsZCBrbm93IHRvIGltcGxlbWVudCBDQVJUIHdoZW4gZ2VuZXJhdGluZyB0aGUgZGVjaXNpb24gdHJlZXMuDQoNCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCnRyYWluJEVucm9sbCA8LSBhcy5mYWN0b3IodHJhaW4kRW5yb2xsKQ0KDQpmb3Jlc3RfZml0IDwtIHJhbmRvbUZvcmVzdChFbnJvbGwgfiAuLCBkYXRhID0gdHJhaW4sIG50cmVlID0gNTAwKQ0KDQp0ZXN0X3ByZWRpY3RlZF9mb3Jlc3QgPC0gcHJlZGljdChmb3Jlc3RfZml0LCB2YWxpZGF0aW9uLCB0eXBlID0gImNsYXNzIikNCg0KZm9yZXN0X2NvbmZfbWF0cml4IDwtIHRhYmxlKHZhbGlkYXRpb24kRW5yb2xsLCB0ZXN0X3ByZWRpY3RlZF9mb3Jlc3QpICU+JSANCiAgcHJvcC50YWJsZSgpDQoNCnJvd25hbWVzKGZvcmVzdF9jb25mX21hdHJpeCkgPC0gYygiRGlkbid0IEVucm9sbCIsICJFbnJvbGxlZCIpDQpjb2xuYW1lcyhmb3Jlc3RfY29uZl9tYXRyaXgpIDwtIGMoIlByZWRpY3RlZCBOb24tZW5yb2xsbWVudCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcmVkaWN0ZWQgRW5yb2xsbWVudCIpDQpgYGANCg0KIyMjIEdyYWRpZW50IEJvb3N0aW5nDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KZml0X2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKQ0KDQp0dW5lX2dyaWQgPC0gZXhwYW5kLmdyaWQoaW50ZXJhY3Rpb24uZGVwdGggPSAyLCBuLnRyZWVzID0gNTAwLCBzaHJpbmthZ2UgPSAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgbi5taW5vYnNpbm5vZGUgPSAxMCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNCmJvb3N0X2ZpdCA8LSB0cmFpbihFbnJvbGwgfiAuLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJnYm0iLCANCiAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRfY29udHJvbCwgdmVyYm9zZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IHR1bmVfZ3JpZCkNCg0KdGVzdF9ib29zdGluZyA8LSBwcmVkaWN0KGJvb3N0X2ZpdCwgdmFsaWRhdGlvbiwgdHlwZSA9ICJwcm9iIikNCg0KYm9vc3RfY29uZl9tYXRyaXggPC0gdGFibGUodmFsaWRhdGlvbiRFbnJvbGwsIHRlc3RfYm9vc3RpbmckYDFgID4gMC41KSAlPiUgDQogIHByb3AudGFibGUoKQ0KDQpyb3duYW1lcyhib29zdF9jb25mX21hdHJpeCkgPC0gYygiRGlkbid0IEVucm9sbCIsICJFbnJvbGxlZCIpDQpjb2xuYW1lcyhib29zdF9jb25mX21hdHJpeCkgPC0gYygiUHJlZGljdGVkIE5vbi1lbnJvbGxtZW50IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlByZWRpY3RlZCBFbnJvbGxtZW50IikNCmBgYA0KDQoNCmBgYHtyfQ0KbGlzdCgNCiAgbG9naXQgPSBsb2dpdF9jb25mX21hdHJpeCwNCiAgdHJlZSA9IHRyZWVfY29uZl9tYXRyaXgsDQogIGZvcmVzdCA9IGZvcmVzdF9jb25mX21hdHJpeCwNCiAgYm9vc3QgPSBib29zdF9jb25mX21hdHJpeA0KKQ0KYGBgDQoNCk15IGxvZ2lzdGljIHJlZ3Jlc3Npb24gaGFkIGEgbWlzY2xhc3NpZmljYXRpb24gcmF0ZSBvZiA2LjclLiAgTXkgZGVjaXNpb24gdHJlZSBoYWQgYSBtaXNjbGFzc2lmaWNhdGlvbiByYXRlIG9mIDYuOCUuICBNeSByYW5kb20gZm9yZXN0IGhhZCBhIG1pc2NsYXNzaWZpY2F0aW9uIHJhdGUgb2YgNS43NSUuICBBbmQgbGFzdGx5LCBteSBncmFkaWVudCBib29zdGluZyBtb2RlbCBoYWQgYSBtaXNjbGFzc2lmaWNhdGlvbiByYXRlIG9mIDUuNzglLg0KDQpNb3JlIGludGVyZXN0aW5nIGZyb20gdGhlIG1vZGVscyBpcyBhY3R1YWxseSB0aGUgcGVyY2VudGFnZSBvZiBzdHVkZW50cyB3aG8gZW5yb2xsZWQgdGhhdCB0aGUgbW9kZWwgYWN0dWFsbHkgcHJlZGljdGVkIGNvcnJlY3RseSwgc2luY2UgdGhpcyBnb2FsIGlzIG1vcmUgaW4gbGluZSB3aXRoIHRoYXQgb2YgdGhlIHByb2plY3QuICBUaGUgbG9naXN0aWMgcmVncmVzc2lvbiBjb3JyZWN0bHkgaWRlbnRpZmllZCA5MS42NyUgb2YgdGhlIHN0dWRlbnRzIHdobyBlbnJvbGxlZC4gIFRoZSBkZWNpc2lvbiB0cmVlIGNvcnJlY3RseSBpZGVudGlmaWVkIDk4LjMyJSBvZiB0aGUgc3R1ZGVudHMgd2hvIGVucm9sbGVkLiAgVGhlIHJhbmRvbSBmb3Jlc3QgY29ycmVjdGx5IGlkZW50aWZpZWQgOTQuMDUlIG9mIHRoZSBzdHVkZW50cyB3aG8gZW5yb2xsZWQuICBBbmQgbGFzdGx5LCBteSBncmFkaWVudCBib29zaW5nIG1vZGVsIGNvcnJlY3RseSBpZGVudGlmaWVkIDkzLjE1JSBvZiBzdHVkZW50cyB3aG8gZW5yb2xsZWQuDQoNCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5DQoNClRoZSByZXN1bHRzIG9mIG15IGFuYWx5c2lzIHNob3cgdGhhdCBhIHNpbXBsZSBkZWNpc2lvbiB0cmVlIGRvZXMgdGhlIGJlc3Qgam9iIG1vZGVsaW5nIHdoZXRoZXIgb3Igbm90IGEgc3R1ZGVudCB3aWxsIGVucm9sbC4gIE92ZXJhbGwgaXQgY29ycmVjdGx5IGNsYXNzaWZpZWQgOTIuMiUgcGVyY2VudCBvZiBhbGwgcG90ZW50aWFsIHN0dWRlbnRzIGFzIHRvIHdoZXRoZXIgb3Igbm90IHRoZXkgZW5yb2xsZWQuICBNb3JlIGltcG9ydGFudGx5LCBpdCBwcmVkaWN0ZWQgOTglIHBlcmNlbnQgb2YgdGhlIHN0dWRlbnRzIHdobyBhY3R1YWxseSBlbnJvbGxlZCBjb3JyZWN0bHkuDQoNCkdpdmVuIHRoYXQgdGhlIHB1cnBvc2Ugb2YgdGhpcyBhbmFseXNpcyB3YXMgdG8gbW9zdCBhY2N1cmF0ZWx5IGlkZW50aWZ5IHRob3NlIHN0dWRlbnRzIHdobyB3b3VsZCBlbnJvbGwgaW4gdGhlIHNjaG9vbCwgSSBzdWJzZXR0ZWQgdGhlIGRhdGEgc28gdGhhdCBJIHdhcyB0YWtpbmcgYWxsIHN0dWRlbnRzIHdobyB1bHRpbWF0ZWx5IGVucm9sbGVkLCBidXQgb25seSBhIHByb3BvcnRpb24gb2YgdGhlIHN0dWRlbnRzIHdobyBkaWQgbm90IGVucm9sbC4gIE9uIHRoaXMgc3Vic2V0LCBkZWNpc2lvbiB0cmVlIHBlcmZvcm1lZCB0aGUgYmVzdC4gIFRoaXMgc2VlbXMgb2RkIGZyb20gYSBzdGF0aXN0aWNhbCBwZXJzcGVjdGl2ZSwgYnV0IGdpdmVuIHRoYXQgdGhlIHJhbmRvbSBmb3Jlc3QgYW5kIHRoZSBib29zdGluZyBtb2RlbCB3ZXJlIHRhc2tlZCB3aXRoIGxvd2VyaW5nIHRoZSBvdmVyYWxsIG1pc2NsYXNzaWZpY2F0aW9uIHJhdGUgKHdoaWNoIHRoZXkgc3VjY2Vzc2Z1bGx5IGRpZCksIHRoZXkgYWN0dWFsbHkgbG9zdCBwcmVkaWN0aXZlIHBvd2VyIHdoZW4gc2VsZWN0aW5nIHRoZSBzdHVkZW50cyB3aG8gd2VyZSBnb2luZyB0byBlbnJvbGwgaW4gZXhjaGFuZ2UgZm9yIG1vcmUgYWNjdXJhdGVseSBjb3JyZWN0bHkgaWRlbnRpZnlpbmcgc3R1ZGVudHMgd2hvIHdlcmUgbm90IGdvaW5nIHRvIGVucm9sbC4NCg0KVGhlcmVmb3JlIG15IGZpbmFsIG1vZGVsIGlzIHRoZSBkZWNpc2lvbiB0cmVlLg0KDQpgYGB7cn0NCnBsb3QodHJlZV9maXQsIHVuaWZvcm0gPSBUUlVFLCBtYXJnaW4gPSAwLjIpDQp0ZXh0KHRyZWVfZml0LCB1c2UubiA9IFRSVUUsIGFsbCA9IFRSVUUpDQpgYGANCg0KVGhlIGZvcmVtb3N0IHZhcmlhYmxlIGluIGRldGVybWluaW5nIHdoZXRoZXIgb3Igbm90IGEgc3R1ZGVudCB3b3VsZCBlbnJvbGwgd2FzIHRoZSBudW1iZXIgb2Ygc2VsZi1pbml0aWF0ZWQgY29udGFjdHMuICBJZiB0aGUgc3R1ZGVudCByZWFjaGVkIG91dCB0byAzIG9yIG1vcmUgcGVvcGxlLCB0aGVuIHRoYXQgd2FzIHRoZSBncmVhdGVzdCBpbmRpY2F0b3IgdGhhdCB0aGF0IHN0dWRlbnQgd291bGQgZW5yb2xsLiAgT2YgdGhvc2Ugd2hvIGRpZCBub3QgcmVhY2ggb3V0IHRvIDMgb3IgbW9yZSBwZW9wbGUsIGlmIHRoZXkgYWxzbyBkaWQgbm90IHByb3ZpZGUgdGhlaXIgU0FUIHNjb3JlcywgdGhlbiB0aGV5IGRpZCBub3QgZW5yb2xsIGluIHRoZSBmYWxsLiAgVGhpcyBpcyB0cnVlIGZvciBhbGwgMzkyMyBzdWNoIHBvdGVudGlhbCBzdHVkZW50cy4gIA0KDQpPZiB0aG9zZSB3aG8gc2VsZi1pbml0aWF0ZWQgY29udGFjdCB3aXRoIHRocmVlIG9yIG1vcmUgcGVvcGxlLCB0aGUgbmV4dCBncmVhdGVzdCBkaWZmZXJlbnRpYXRvciB3YXMgdGhlaXIgaGlnaCBzY2hvb2wncyBoaXN0b3JpY2FsIGVucm9sbG1lbnQgcmF0ZS4gIElmIHRoZSBoaWdoIHNjaG9vbCB0eXBpY2FsbHkgc2VudCBsZXNzIHRoYW4gMS4yJSBvZiBpdHMgc3R1ZGVudHMgdG8gdGhlIHVuaXZlcnNpdHksIHRoZW4gdGhhdCBzdHVkZW50IHdhcyBtdWNoIGxlc3MgbGlrZWx5IHRvIGVucm9sbCBpbiB0aGUgc2Nob29sLiAgDQoNClRoZSBsZWFzdCBlZmZlY3RpdmUgbm9kZSBpbiB0aGUgbW9kZWwgd2FzIHRoZSBvbmUgd2hlcmUgc3R1ZGVudHMgZGlkIG5vdCBzZWxmIGluaXRpYXRlIHRocmVlIG9yIG1vcmUgY29udGFjdHMsIHdlcmUgbm90IG1pc3NpbmcgdGhlaXIgc2F0IHNjb3JlcywgaGFkIGEgc3R1ZGVudCBlbWFpbCwgYW5kIHdob3NlIGhpZ2ggc2Nob29scyBzZW50IG1vcmUgdGhhbiAzLjklIHRvIHRoZSB1bml2ZXJzaXR5LiAgRm9yIHRob3NlIGZhbGxpbmcgaW50byB0aGlzIGNhdGVnb3J5LCBvdXIgbW9kZWwgb25seSBhY2N1cmF0ZWx5IGFzc2Vzc2VkIDE5MS8oMTE2ICsgMTkxKSBvZiB0aGVzZSBzdHVkZW50cyBjb3JyZWN0bHkuICANCg0KVGhlIG5vZGUgd2hlcmUgd2UgaWRlbnRpZmllZCB0aGUgbW9zdCBzdHVkZW50cyB0byBub3QgZW5yb2xsIHdobyBhY3R1YWxseSBlbmRlZCB1cCBlbnJvbGxpbmcgd2FzIHRoZSBub2RlIGNvbnNpc3Rpbmcgb2Ygc3R1ZGVudHMgd2hvIHNlbGYtaW5pdGlhdGVkIDMgb3IgbW9yZSBjb250YWN0cyBhbmQgd2hvc2UgaGlnaCBzY2hvb2xzIHNlbnQgZmV3ZXIgdGhhbiAxLjIlIG9mIGl0cyBzdHVkZW50cyB0byB0aGUgdW5pdmVyc2l0eS4gIFRoZXJlIHdlcmUgdGVuIHN0dWRlbnRzIGZhbGxpbmcgaW50byB0aGlzIGNhdGVnb3J5IHdobyB1bHRpbWF0ZWx5IGVucm9sbGVkIHRoYXQgd2VyZSBjbGFzc2lmaWVkIGFzIG5vdCBlbnJvbGxpbmcuDQoNClRoaXMgbW9kZWwgd2lsbCBlbmFibGUgdGhlIHVuaXZlcnNpdHkgdG8gbW9yZSBhY2N1cmF0ZWx5IGFzc2VzcyB0aGUgZmFjdG9ycyB0aGF0IHVsdGltYXRlbHkgZHJpdmUgc3R1ZGVudHMgdG8gZW5yb2xsLiAgVGhlIG51bWJlciBvZiBzZWxmLWluaXRpYXRlZCBjb250YWN0cywgd2hldGhlciB0aGUgc3R1ZGVudCBoYWQgc3VibWl0dGVkIHRoZSBTQVQsIHdoZXRoZXIgdGhlIHN0dWRlbnQgaGFkIGFuIGVtYWlsLCBhbmQgdGhlIHJhdGUgb2YgZW5yb2xsbWVudCBmcm9tIGVhY2ggc3R1ZGVudCdzIGhpZ2ggc2Nob29sIGFsbCBwbGF5ZWQgaW1wb3J0YW50IHJvbGxzIGluIHdoZXRoZXIgb3Igbm90IGEgc3R1ZGVudCB1bHRpbWF0ZWx5IGVucm9sbGVkLg0KDQpGcm9tIGEgYnVzaW5lc3MgcGVyc3BlY3RpdmUsIHRoZSB1bml2ZXJzaXR5IGxpa2VseSB3YW50cyB0byBrbm93IHdoaWNoIHN0dWRlbnRzIGFyZSBvbiB0aGUgZnJpbmdlcyBvZiBlbnJvbGxtZW50LiAgVGhlIHVuaXZlcnNpdHkgY2FuIHRoZW4gZGl2eSBvdXQgcmVtYWluaW5nIHNjaG9sYXJzaGlwIGZ1bmRzIGFjY29yZGluZ2x5IGluIG9yZGVyIHRvIGVuY291cmFnZSBjZXJ0YWluIHN0dWRlbnRzIG9uIHRoZSBlZGdlIHRvIGVucm9sbC4gIEJhc2VkIG9uIHRoaXMgYW5hbHlzaXMsIHRoZSBzY2hvb2wga25vd3MgdG8gdGFyZ2V0IHNjaG9sYXJzaGlwIGZ1bmRzIHRvIHRob3NlIHdobyBjaG9zZSBub3QgdG8gc3VibWl0IHRoZWlyIHNhdCBzY29yZXMsIHdobyBoYXZlIGVtYWlscywgYW5kIHdob3NlIGhpZ2ggc2Nob29scyB0eXBpY2FsbHkgZG9uJ3Qgc2VuZCBtYW55IHN0dWRlbnRzIHRvIHRoZSB1bml2ZXJzaXR5Lg0KDQpJZiB0aGUgdW5pdmVyc2l0eSB3YW50cyB0byBpbXByb3ZlIGl0cyBnbG9iYWwgZm9vdHByaW50LCB0aGVuIGl0IHNob3VsZCB0YXJnZXQgdGhlIHNhbWUgZ3JvdXAgb2Ygc3R1ZGVudHMgd2l0aCBtb3JlIGFkdmVydGlzaW5nIGFuZCBldmVudHMgaW4gdGhlaXIgcmVzcGVjdGl2ZSBnZW9ncmFwaGljIGFyZWFzLCB0aGVyZWJ5IGluY3JlYXNpbmcgdGhlIG51bWJlciBvZiBzdHVkZW50cyBlbnJvbGxpbmcgZnJvbSBkaWZmZXJlbnQgYXJlYXMgb2YgdGhlIGNvdW50cnkgYW5kIGxpa2VseSBib29zdGluZyBwdWJsaWMgcGVyY2VwdGlvbiBvZiB0aGUgdW5pdmVyc2l0eS4NCg0KUGVyaGFwcyBtb3N0IHN1cnByaXNpbmcgZnJvbSB0aGUgYW5hbHlzaXMgaXMgdGhhdCB3aGV0aGVyIG9yIG5vdCBhIHN0dWRlbnQgaXMgaW4tc3RhdGUgZGlkIG5vdCBoYXZlIGEgbWFqb3IgaW1wYWN0IG9uIHdoZXRoZXIgb3Igbm90IHRoZXkgd291bGQgZW5yb2xsIGluIHRoZSB1bml2ZXJzaXR5LiAgVGhpcyBsaWtlbHkgaW5kaWNhdGVzIHRoYXQgdGhlIHNjaG9vbCBpbiBxdWVzdGlvbiBkb2VzIG5vdCBwcm92aWRlIGluLXN0YXRlIHR1aXRpb24gYW5kIGlzIHRoZXJlZm9yZSBwcm9iYWJseSBhIHNtYWxsIGxpYmVyYWwgYXJ0cyBzY2hvb2wu