library(ggplot2)
library(tidyverse)
library(graphics)
library(mdsr) # install package if not installed
library(discrim) # install package if not installed
library(klaR) # install package if not installed
library(kknn) # install package if not installed
library(utils) # install package if not installed
library(sp) # install package if not installed
library(fs)

Note: If you Rmd file submission knits you will receive total of (5 points Extra Credit)

Directions: Complete Task 1 and one of the Task 2 or 3!

Task 1 (Total 60 pts):

High-earners in the 1994 United States Census

A marketing analyst might be interested in finding factors that can be used to predict whether a potential customer is a high-earner. The 1994 United States Census provides information that can inform such a model, with records from 32,561 adults that include a binary variable indicating whether each person makes greater or less than $50,000 (more than $80,000 today after accounting for inflation). This is our response variable.

A bit of data preparation

library(tidyverse)
library(mdsr)
url <-
"http://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data"

census <- read_csv(
  url,
  col_names = c(
    "age", "workclass", "fnlwgt", "education", 
    "education_1", "marital_status", "occupation", "relationship", 
    "race", "sex", "capital_gain", "capital_loss", "hours_per_week", 
    "native_country", "income"
  )
) %>%
  mutate(income = factor(income), income_ind = as.numeric(income == ">50K")) # create indicator variable income_ind (0 - low, 1 - high earner)
Rows: 32561 Columns: 15── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (9): workclass, education, marital_status, occupation, relationship, race, sex, native_country, income
dbl (6): age, fnlwgt, education_1, capital_gain, capital_loss, hours_per_week
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# look at the structure of the data
glimpse(census)
Rows: 32,561
Columns: 16
$ age            <dbl> 39, 50, 38, 53, 28, 37, 49, 52, 31, 42, 37, 30, 23, 32, 40, 34, 25, 32, 38, 43, 40, 54, 35, 43, 59, 56, 19, 54, 39…
$ workclass      <chr> "State-gov", "Self-emp-not-inc", "Private", "Private", "Private", "Private", "Private", "Self-emp-not-inc", "Priva…
$ fnlwgt         <dbl> 77516, 83311, 215646, 234721, 338409, 284582, 160187, 209642, 45781, 159449, 280464, 141297, 122272, 205019, 12177…
$ education      <chr> "Bachelors", "Bachelors", "HS-grad", "11th", "Bachelors", "Masters", "9th", "HS-grad", "Masters", "Bachelors", "So…
$ education_1    <dbl> 13, 13, 9, 7, 13, 14, 5, 9, 14, 13, 10, 13, 13, 12, 11, 4, 9, 9, 7, 14, 16, 9, 5, 7, 9, 13, 9, 10, 9, 9, 12, 10, 1…
$ marital_status <chr> "Never-married", "Married-civ-spouse", "Divorced", "Married-civ-spouse", "Married-civ-spouse", "Married-civ-spouse…
$ occupation     <chr> "Adm-clerical", "Exec-managerial", "Handlers-cleaners", "Handlers-cleaners", "Prof-specialty", "Exec-managerial", …
$ relationship   <chr> "Not-in-family", "Husband", "Not-in-family", "Husband", "Wife", "Wife", "Not-in-family", "Husband", "Not-in-family…
$ race           <chr> "White", "White", "White", "Black", "Black", "White", "Black", "White", "White", "White", "Black", "Asian-Pac-Isla…
$ sex            <chr> "Male", "Male", "Male", "Male", "Female", "Female", "Female", "Male", "Female", "Male", "Male", "Male", "Female", …
$ capital_gain   <dbl> 2174, 0, 0, 0, 0, 0, 0, 0, 14084, 5178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ capital_loss   <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2042, 0, 0, 0, 0, 0, 0, 0, 0, 1408, 0, 0, 0, …
$ hours_per_week <dbl> 40, 13, 40, 40, 40, 40, 16, 45, 50, 40, 80, 40, 30, 50, 40, 45, 35, 40, 50, 45, 60, 20, 40, 40, 40, 40, 40, 60, 80…
$ native_country <chr> "United-States", "United-States", "United-States", "United-States", "Cuba", "United-States", "Jamaica", "United-St…
$ income         <fct> <=50K, <=50K, <=50K, <=50K, <=50K, <=50K, <=50K, >50K, >50K, >50K, >50K, >50K, <=50K, <=50K, >50K, <=50K, <=50K, <…
$ income_ind     <dbl> 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
ggplot(census, aes(x = education, fill = income)) +
  geom_bar(position = "fill") +
  coord_flip() +
  labs(y = "Proportion", title = "Proportion of High vs Low Earners by Education")

a) (10 pts) Split the data set into two pieces by separating the rows at random. A sample of 70% of the rows will become the training data set, with the remaining 30% set aside as the testing (or “hold-out”) data set. Use set.seed(364) in the beginning of your code. How many records are in the testing set? 9768 Hint: One possible way to do this is to use the function initial_split(prop = 0.7) and call the functions training() and testing().

library(tidymodels)
set.seed(364)

# Perform the split
census_split <- initial_split(census, prop = 0.7)
train <- training(census_split)
test <- testing(census_split)

# Check how many rows are in the test set
nrow(test)
[1] 9769

Note: You should get around 24% of those in the sample make more than $50k. Thus, the accuracy of the null model is about 76%, since we can get that many right by just predicting that everyone makes less than $50k.

# if your training set is called `train` this code will produce the correct percentage
pi_bar <- train %>%
  count(income) %>%
  mutate(pct = n / sum(n)) %>%
  filter(income == ">50K") %>%
  pull(pct)

print(c("Percent >50K", pi_bar)) 
[1] "Percent >50K"      "0.241751491751492"

Pro Tip: Always benchmark your predictive models against a reasonable null model.

b) (10 pts) Use KNN algorithm to classify the earners (<=50K, >50K, low/high) for High-earners in the 1994 United States Census data above. Select only the quantitative variables age,education_1, capital_gain, capital_loss, hours_per_week. Use mode = "classification" and k=1(use the closest neighbor) in the nearest_neighbor function arguments. Print the confusion matrix. State the accuracy.

Hint: See Programming exercises Week 13 for details about KNN implementation.

library(kknn)

# Select only the numeric columns (excluding 'fnlwgt') and 'income' as the target variable
train_q <- train %>% dplyr::select(income, where(is.numeric), -fnlwgt)

# Define the KNN classifier (k=1, classification task)
knn_model <- kknn(income ~ age + education_1 + capital_gain + capital_loss + hours_per_week, 
                  train = train_q, 
                  test = train_q, 
                  k = 1, 
                  distance = 2, # Euclidean distance by default
                  kernel = "rectangular") # Rectangular kernel (default)
Warning: variable 'income' is absent, its contrast will be ignored
# Predict the income using the KNN classifier
train_q$income_knn <- predict(knn_model)

# Print the confusion matrix
confusion_matrix <- table(train_q$income, train_q$income_knn)
print(confusion_matrix)
       
        <=50K  >50K
  <=50K 15426  1856
  >50K   1627  3883
# Calculate accuracy
accuracy <- sum(diag(confusion_matrix)) / sum(confusion_matrix)
print(paste("Accuracy is:", round(accuracy, 4)))
[1] "Accuracy is: 0.8472"

Accuracy is:0.8472


# Find the Accuracy = (true positive and true negative)/total or use the `accuracy()` function.

Load necessary libraries

library(tidyverse)

Define the formula for the logistic regression model

form <- as.formula( “income_ind ~ age + education_num + sex + marital_status” )

Fit logistic regression model using glm function

logit_model <- glm(form, data = train, family = binomial)

Summary of the logistic regression model to see coefficients

summary(logit_model)

Predict probabilities for the test set

logit_pred_prob <- predict(logit_model, newdata = test, type = “response”)

Assign 1/0 based on a threshold of 0.5 for high-earner prediction

pred_y_logit <- as.numeric(logit_pred_prob > 0.5)

Create confusion matrix comparing predicted values and actual values

confusion_logit <- table(pred_y_logit, test$income_ind)

Calculate accuracy

accuracy_logit <- (confusion_logit[1, 1] + confusion_logit[2, 2]) / sum(confusion_logit) print(paste(“Accuracy is:”, accuracy_logit))

`# Fit the logistic regression model with all chosen predictors form <- as.formula( “income_ind ~ age + education_1 + sex + marital_status” )

logit_model <- glm(form, data = train, family = binomial)

Summarize the logistic regression model to see which predictors are significant

summary(logit_model)

Fit the logistic regression model

logreg <- glm(income_ind ~ age + sex + education_1, family = “binomial”, data = train)

Load broom package to use tidy function

library(broom)

Summarize the model using tidy()

tidy_logreg <- tidy(logreg)

LS0tDQp0aXRsZTogIlNUQVQyNzAgLSBGaW5hbCBQcm9qZWN0Ig0KYXV0aG9yOiAiQXJ0aHVyIENvbGVtYW4iDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQpgYGB7ciBwYWNrYWdlcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9DQoNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShncmFwaGljcykNCmxpYnJhcnkobWRzcikgIyBpbnN0YWxsIHBhY2thZ2UgaWYgbm90IGluc3RhbGxlZA0KbGlicmFyeShkaXNjcmltKSAjIGluc3RhbGwgcGFja2FnZSBpZiBub3QgaW5zdGFsbGVkDQpsaWJyYXJ5KGtsYVIpICMgaW5zdGFsbCBwYWNrYWdlIGlmIG5vdCBpbnN0YWxsZWQNCmxpYnJhcnkoa2tubikgIyBpbnN0YWxsIHBhY2thZ2UgaWYgbm90IGluc3RhbGxlZA0KbGlicmFyeSh1dGlscykgIyBpbnN0YWxsIHBhY2thZ2UgaWYgbm90IGluc3RhbGxlZA0KbGlicmFyeShzcCkgIyBpbnN0YWxsIHBhY2thZ2UgaWYgbm90IGluc3RhbGxlZA0KbGlicmFyeShmcykNCg0KYGBgDQoNCioqTm90ZToqKiBJZiB5b3UgYFJtZGAgZmlsZSBzdWJtaXNzaW9uIGtuaXRzIHlvdSB3aWxsIHJlY2VpdmUgdG90YWwgb2YgKiooNSBwb2ludHMgRXh0cmEgQ3JlZGl0KSoqDQoNCj4gKipEaXJlY3Rpb25zOioqIENvbXBsZXRlIFRhc2sgMSBhbmQgKipvbmUqKiBvZiB0aGUgVGFzayAyICoqb3IqKiAzIQ0KDQojIyBUYXNrIDEgKFRvdGFsIDYwIHB0cyk6DQoNCiMjIyBIaWdoLWVhcm5lcnMgaW4gdGhlIDE5OTQgVW5pdGVkIFN0YXRlcyBDZW5zdXMNCg0KQSBtYXJrZXRpbmcgYW5hbHlzdCBtaWdodCBiZSBpbnRlcmVzdGVkIGluIGZpbmRpbmcgZmFjdG9ycyB0aGF0IGNhbiBiZSB1c2VkIHRvIHByZWRpY3Qgd2hldGhlciBhIHBvdGVudGlhbCBjdXN0b21lciBpcyBhIGhpZ2gtZWFybmVyLiBUaGUgYDE5OTRgIFVuaXRlZCBTdGF0ZXMgQ2Vuc3VzIHByb3ZpZGVzIGluZm9ybWF0aW9uIHRoYXQgY2FuIGluZm9ybSBzdWNoIGEgbW9kZWwsIHdpdGggcmVjb3JkcyBmcm9tIGAzMiw1NjFgIGFkdWx0cyB0aGF0IGluY2x1ZGUgYSBiaW5hcnkgdmFyaWFibGUgaW5kaWNhdGluZyB3aGV0aGVyIGVhY2ggcGVyc29uIG1ha2VzIGdyZWF0ZXIgb3IgbGVzcyB0aGFuIGAkNTAsMDAwYCAobW9yZSB0aGFuIGAkODAsMDAwYCB0b2RheSBhZnRlciBhY2NvdW50aW5nIGZvciBpbmZsYXRpb24pLiBUaGlzIGlzIG91ciByZXNwb25zZSB2YXJpYWJsZS4NCg0KIyMjIyBBIGJpdCBvZiBkYXRhIHByZXBhcmF0aW9uDQoNCmBgYHtyIHByZXBhcmUgZGF0YX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShtZHNyKQ0KdXJsIDwtDQoiaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMvYWR1bHQvYWR1bHQuZGF0YSINCg0KY2Vuc3VzIDwtIHJlYWRfY3N2KA0KICB1cmwsDQogIGNvbF9uYW1lcyA9IGMoDQogICAgImFnZSIsICJ3b3JrY2xhc3MiLCAiZm5sd2d0IiwgImVkdWNhdGlvbiIsIA0KICAgICJlZHVjYXRpb25fMSIsICJtYXJpdGFsX3N0YXR1cyIsICJvY2N1cGF0aW9uIiwgInJlbGF0aW9uc2hpcCIsIA0KICAgICJyYWNlIiwgInNleCIsICJjYXBpdGFsX2dhaW4iLCAiY2FwaXRhbF9sb3NzIiwgImhvdXJzX3Blcl93ZWVrIiwgDQogICAgIm5hdGl2ZV9jb3VudHJ5IiwgImluY29tZSINCiAgKQ0KKSAlPiUNCiAgbXV0YXRlKGluY29tZSA9IGZhY3RvcihpbmNvbWUpLCBpbmNvbWVfaW5kID0gYXMubnVtZXJpYyhpbmNvbWUgPT0gIj41MEsiKSkgIyBjcmVhdGUgaW5kaWNhdG9yIHZhcmlhYmxlIGluY29tZV9pbmQgKDAgLSBsb3csIDEgLSBoaWdoIGVhcm5lcikNCg0KDQojIGxvb2sgYXQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YQ0KZ2xpbXBzZShjZW5zdXMpDQoNCmdncGxvdChjZW5zdXMsIGFlcyh4ID0gZWR1Y2F0aW9uLCBmaWxsID0gaW5jb21lKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHkgPSAiUHJvcG9ydGlvbiIsIHRpdGxlID0gIlByb3BvcnRpb24gb2YgSGlnaCB2cyBMb3cgRWFybmVycyBieSBFZHVjYXRpb24iKQ0KDQpgYGANCg0KKiphKSAoMTAgcHRzKSoqIFNwbGl0IHRoZSBkYXRhIHNldCBpbnRvIHR3byBwaWVjZXMgYnkgc2VwYXJhdGluZyB0aGUgcm93cyBhdCByYW5kb20uIEEgc2FtcGxlIG9mIDcwJSBvZiB0aGUgcm93cyB3aWxsIGJlY29tZSB0aGUgYHRyYWluaW5nYCBkYXRhIHNldCwgd2l0aCB0aGUgcmVtYWluaW5nIDMwJSBzZXQgYXNpZGUgYXMgdGhlIGB0ZXN0aW5nYCAob3IgImhvbGQtb3V0IikgZGF0YSBzZXQuIFVzZSBgc2V0LnNlZWQoMzY0KWAgaW4gdGhlIGJlZ2lubmluZyBvZiB5b3VyIGNvZGUuIEhvdyBtYW55IHJlY29yZHMgYXJlIGluIHRoZSB0ZXN0aW5nIHNldD8NCjk3NjgNCioqSGludDoqKiBPbmUgcG9zc2libGUgd2F5IHRvIGRvIHRoaXMgaXMgdG8gdXNlIHRoZSBmdW5jdGlvbiBgaW5pdGlhbF9zcGxpdChwcm9wID0gMC43KWAgYW5kIGNhbGwgdGhlIGZ1bmN0aW9ucyBgdHJhaW5pbmcoKWAgYW5kIGB0ZXN0aW5nKClgLg0KDQpgYGB7ciBzcGxpdCBkYXRhIHNldH0NCmxpYnJhcnkodGlkeW1vZGVscykNCnNldC5zZWVkKDM2NCkNCg0KIyBQZXJmb3JtIHRoZSBzcGxpdA0KY2Vuc3VzX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQoY2Vuc3VzLCBwcm9wID0gMC43KQ0KdHJhaW4gPC0gdHJhaW5pbmcoY2Vuc3VzX3NwbGl0KQ0KdGVzdCA8LSB0ZXN0aW5nKGNlbnN1c19zcGxpdCkNCg0KIyBDaGVjayBob3cgbWFueSByb3dzIGFyZSBpbiB0aGUgdGVzdCBzZXQNCm5yb3codGVzdCkNCg0KYGBgDQoNCioqTm90ZToqKiBZb3Ugc2hvdWxkIGdldCBhcm91bmQgMjQlIG9mIHRob3NlIGluIHRoZSBzYW1wbGUgbWFrZSBtb3JlIHRoYW4gYCQ1MGtgLiBUaHVzLCB0aGUgYWNjdXJhY3kgb2YgdGhlICpudWxsIG1vZGVsKiBpcyBhYm91dCBgNzYlYCwgc2luY2Ugd2UgY2FuIGdldCB0aGF0IG1hbnkgcmlnaHQgYnkganVzdCBwcmVkaWN0aW5nIHRoYXQgZXZlcnlvbmUgbWFrZXMgbGVzcyB0aGFuIGAkNTBrYC4NCg0KYGBge3J9DQojIGlmIHlvdXIgdHJhaW5pbmcgc2V0IGlzIGNhbGxlZCBgdHJhaW5gIHRoaXMgY29kZSB3aWxsIHByb2R1Y2UgdGhlIGNvcnJlY3QgcGVyY2VudGFnZQ0KcGlfYmFyIDwtIHRyYWluICU+JQ0KICBjb3VudChpbmNvbWUpICU+JQ0KICBtdXRhdGUocGN0ID0gbiAvIHN1bShuKSkgJT4lDQogIGZpbHRlcihpbmNvbWUgPT0gIj41MEsiKSAlPiUNCiAgcHVsbChwY3QpDQoNCnByaW50KGMoIlBlcmNlbnQgPjUwSyIsIHBpX2JhcikpIA0KYGBgDQoNCg0KKipQcm8gVGlwOioqIEFsd2F5cyBiZW5jaG1hcmsgeW91ciBwcmVkaWN0aXZlIG1vZGVscyBhZ2FpbnN0IGEgcmVhc29uYWJsZSBudWxsIG1vZGVsLg0KDQoqKmIpICgxMCBwdHMpKiogVXNlIGBLTk5gIGFsZ29yaXRobSB0byBjbGFzc2lmeSB0aGUgZWFybmVycyAoYDw9NTBLLCA+NTBLYCwgbG93L2hpZ2gpIGZvciBIaWdoLWVhcm5lcnMgaW4gdGhlIDE5OTQgVW5pdGVkIFN0YXRlcyBDZW5zdXMgZGF0YSBhYm92ZS4gU2VsZWN0IG9ubHkgdGhlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgYGFnZSxlZHVjYXRpb25fMSwgY2FwaXRhbF9nYWluLCBjYXBpdGFsX2xvc3MsIGhvdXJzX3Blcl93ZWVrYC4gVXNlIGBtb2RlID0gImNsYXNzaWZpY2F0aW9uImAgYW5kIGBrPTFgKHVzZSB0aGUgY2xvc2VzdCBuZWlnaGJvcikgaW4gdGhlIGBuZWFyZXN0X25laWdoYm9yYCBmdW5jdGlvbiBhcmd1bWVudHMuIFByaW50IHRoZSBjb25mdXNpb24gbWF0cml4LiBTdGF0ZSB0aGUgYWNjdXJhY3kuDQoNCioqSGludDoqKiBTZWUgUHJvZ3JhbW1pbmcgZXhlcmNpc2VzIFdlZWsgMTMgZm9yIGRldGFpbHMgYWJvdXQgYEtOTmAgaW1wbGVtZW50YXRpb24uDQoNCmBgYHtyfQ0KbGlicmFyeShra25uKQ0KDQojIFNlbGVjdCBvbmx5IHRoZSBudW1lcmljIGNvbHVtbnMgKGV4Y2x1ZGluZyAnZm5sd2d0JykgYW5kICdpbmNvbWUnIGFzIHRoZSB0YXJnZXQgdmFyaWFibGUNCnRyYWluX3EgPC0gdHJhaW4gJT4lIGRwbHlyOjpzZWxlY3QoaW5jb21lLCB3aGVyZShpcy5udW1lcmljKSwgLWZubHdndCkNCg0KIyBEZWZpbmUgdGhlIEtOTiBjbGFzc2lmaWVyIChrPTEsIGNsYXNzaWZpY2F0aW9uIHRhc2spDQprbm5fbW9kZWwgPC0ga2tubihpbmNvbWUgfiBhZ2UgKyBlZHVjYXRpb25fMSArIGNhcGl0YWxfZ2FpbiArIGNhcGl0YWxfbG9zcyArIGhvdXJzX3Blcl93ZWVrLCANCiAgICAgICAgICAgICAgICAgIHRyYWluID0gdHJhaW5fcSwgDQogICAgICAgICAgICAgICAgICB0ZXN0ID0gdHJhaW5fcSwgDQogICAgICAgICAgICAgICAgICBrID0gMSwgDQogICAgICAgICAgICAgICAgICBkaXN0YW5jZSA9IDIsICMgRXVjbGlkZWFuIGRpc3RhbmNlIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgICAgIGtlcm5lbCA9ICJyZWN0YW5ndWxhciIpICMgUmVjdGFuZ3VsYXIga2VybmVsIChkZWZhdWx0KQ0KDQojIFByZWRpY3QgdGhlIGluY29tZSB1c2luZyB0aGUgS05OIGNsYXNzaWZpZXINCnRyYWluX3EkaW5jb21lX2tubiA8LSBwcmVkaWN0KGtubl9tb2RlbCkNCg0KIyBQcmludCB0aGUgY29uZnVzaW9uIG1hdHJpeA0KY29uZnVzaW9uX21hdHJpeCA8LSB0YWJsZSh0cmFpbl9xJGluY29tZSwgdHJhaW5fcSRpbmNvbWVfa25uKQ0KcHJpbnQoY29uZnVzaW9uX21hdHJpeCkNCg0KIyBDYWxjdWxhdGUgYWNjdXJhY3kNCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZ1c2lvbl9tYXRyaXgpKSAvIHN1bShjb25mdXNpb25fbWF0cml4KQ0KcHJpbnQocGFzdGUoIkFjY3VyYWN5IGlzOiIsIHJvdW5kKGFjY3VyYWN5LCA0KSkpDQoNCg0KYGBgDQoNCkFjY3VyYWN5IGlzOjAuODQ3Mg0KDQpgYGB7cn0NCg0KIyBGaW5kIHRoZSBBY2N1cmFjeSA9ICh0cnVlIHBvc2l0aXZlIGFuZCB0cnVlIG5lZ2F0aXZlKS90b3RhbCBvciB1c2UgdGhlIGBhY2N1cmFjeSgpYCBmdW5jdGlvbi4NCg0KDQpgYGANCg0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIERlZmluZSB0aGUgZm9ybXVsYSBmb3IgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCmZvcm0gPC0gYXMuZm9ybXVsYSgNCiAgImluY29tZV9pbmQgfiBhZ2UgKyBlZHVjYXRpb25fbnVtICsgc2V4ICsgbWFyaXRhbF9zdGF0dXMiDQopDQoNCiMgRml0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgZ2xtIGZ1bmN0aW9uDQpsb2dpdF9tb2RlbCA8LSBnbG0oZm9ybSwgZGF0YSA9IHRyYWluLCBmYW1pbHkgPSBiaW5vbWlhbCkNCg0KIyBTdW1tYXJ5IG9mIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRvIHNlZSBjb2VmZmljaWVudHMNCnN1bW1hcnkobG9naXRfbW9kZWwpDQoNCiMgUHJlZGljdCBwcm9iYWJpbGl0aWVzIGZvciB0aGUgdGVzdCBzZXQNCmxvZ2l0X3ByZWRfcHJvYiA8LSBwcmVkaWN0KGxvZ2l0X21vZGVsLCBuZXdkYXRhID0gdGVzdCwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCiMgQXNzaWduIDEvMCBiYXNlZCBvbiBhIHRocmVzaG9sZCBvZiAwLjUgZm9yIGhpZ2gtZWFybmVyIHByZWRpY3Rpb24NCnByZWRfeV9sb2dpdCA8LSBhcy5udW1lcmljKGxvZ2l0X3ByZWRfcHJvYiA+IDAuNSkNCg0KIyBDcmVhdGUgY29uZnVzaW9uIG1hdHJpeCBjb21wYXJpbmcgcHJlZGljdGVkIHZhbHVlcyBhbmQgYWN0dWFsIHZhbHVlcw0KY29uZnVzaW9uX2xvZ2l0IDwtIHRhYmxlKHByZWRfeV9sb2dpdCwgdGVzdCRpbmNvbWVfaW5kKQ0KDQojIFByaW50IGNvbmZ1c2lvbiBtYXRyaXgNCnByaW50KGNvbmZ1c2lvbl9sb2dpdCkNCg0KIyBDYWxjdWxhdGUgYWNjdXJhY3kNCmFjY3VyYWN5X2xvZ2l0IDwtIChjb25mdXNpb25fbG9naXRbMSwgMV0gKyBjb25mdXNpb25fbG9naXRbMiwgMl0pIC8gc3VtKGNvbmZ1c2lvbl9sb2dpdCkNCnByaW50KHBhc3RlKCJBY2N1cmFjeSBpczoiLCBhY2N1cmFjeV9sb2dpdCkpDQoNCmAjIEZpdCB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB3aXRoIGFsbCBjaG9zZW4gcHJlZGljdG9ycw0KZm9ybSA8LSBhcy5mb3JtdWxhKA0KICAiaW5jb21lX2luZCB+IGFnZSArIGVkdWNhdGlvbl8xICsgc2V4ICsgbWFyaXRhbF9zdGF0dXMiDQopDQoNCmxvZ2l0X21vZGVsIDwtIGdsbShmb3JtLCBkYXRhID0gdHJhaW4sIGZhbWlseSA9IGJpbm9taWFsKQ0KDQojIFN1bW1hcml6ZSB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0byBzZWUgd2hpY2ggcHJlZGljdG9ycyBhcmUgc2lnbmlmaWNhbnQNCnN1bW1hcnkobG9naXRfbW9kZWwpDQoNCiMgRml0IHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsDQpsb2dyZWcgPC0gZ2xtKGluY29tZV9pbmQgfiBhZ2UgKyBzZXggKyBlZHVjYXRpb25fMSwgZmFtaWx5ID0gImJpbm9taWFsIiwgZGF0YSA9IHRyYWluKQ0KDQojIExvYWQgYnJvb20gcGFja2FnZSB0byB1c2UgdGlkeSBmdW5jdGlvbg0KbGlicmFyeShicm9vbSkNCg0KIyBTdW1tYXJpemUgdGhlIG1vZGVsIHVzaW5nIHRpZHkoKQ0KdGlkeV9sb2dyZWcgPC0gdGlkeShsb2dyZWcpDQoNCiMgUHJpbnQgdGhlIHRpZHkgb3V0cHV0IHRvIHNlZSB0aGUgY29lZmZpY2llbnRzLCBzdGFuZGFyZCBlcnJvcnMsIGFuZCBwLXZhbHVlcw0KcHJpbnQodGlkeV9sb2dyZWcpDQoNCmBgYA0KDQoqKkZZSToqKiBUaGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYW5kIHByZWRpY3RlZCB2YWx1ZXMgZm9yIGBpbmNvbWVfaW5kYCBjYW4gYmUgZm91bmQgdXNpbmcgdGhlIGNvZGUsIHVuY29tbWVudCB0aGUgY29kZSBsaW5lcyB0byB1c2UgKGhpZ2hsaWdodCBjaGluayBhbmQgcHJlc3MgKipDVFJMICsgU0hJRlQgKyBDKiopOg0KDQpgYCMgUHJlZGljdCBwcm9iYWJpbGl0aWVzIHVzaW5nIHRoZSBsb2dyZWcgbW9kZWwgb24gdGhlIHRlc3Qgc2V0DQpsb2dpdF9wcmVkX3Byb2JfdGVzdCA8LSBwcmVkaWN0KGxvZ3JlZywgbmV3ZGF0YSA9IHRlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQ0KDQojIENvbnZlcnQgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgdG8gY2xhc3MgcHJlZGljdGlvbnMgKDEgZm9yIGhpZ2ggZWFybmVyLCAwIGZvciBsb3cgZWFybmVyKQ0KcHJlZF95X3Rlc3QgPC0gYXMubnVtZXJpYyhsb2dpdF9wcmVkX3Byb2JfdGVzdCA+IDAuNSkNCg0KIyBDcmVhdGUgY29uZnVzaW9uIG1hdHJpeCBjb21wYXJpbmcgcHJlZGljdGVkIGFuZCBhY3R1YWwgdmFsdWVzIGZyb20gdGhlIHRlc3Qgc2V0DQpjb25mdXNpb25fdGVzdCA8LSB0YWJsZShwcmVkX3lfdGVzdCwgdGVzdCRpbmNvbWVfaW5kKQ0KDQojIFByaW50IGNvbmZ1c2lvbiBtYXRyaXgNCnByaW50KGNvbmZ1c2lvbl90ZXN0KQ0KDQojIENhbGN1bGF0ZSBhY2N1cmFjeSBieSBjb21wYXJpbmcgcHJlZGljdGVkIHZhbHVlcyB3aXRoIGFjdHVhbCB2YWx1ZXMgZnJvbSB0aGUgdGVzdCBzZXQNCmFjY3VyYWN5X3Rlc3QgPC0gbWVhbihwcmVkX3lfdGVzdCA9PSB0ZXN0JGluY29tZV9pbmQsIG5hLnJtID0gVFJVRSkNCg0KIyBQcmludCBhY2N1cmFjeQ0KcHJpbnQocGFzdGUoIkFjY3VyYWN5IG9uIHRoZSB0ZXN0IHNldCBpczoiLCBhY2N1cmFjeV90ZXN0KSkNCg0KDQoNCmBgYA0KDQoqKmUpICgxMCBwdHMpKiogQXNzZXNzaW5nIHRoZSBMb2dpdCBtb2RlbCBmcm9tICoqcGFydCBkKSoqIHVzaW5nIHRoZSB0ZXN0IHNldCBzYXZlZCBpbiBgdGVzdGAgUi1vYmplY3QuDQoNCldoYXQgaXMgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbD8NCg0KYGBge3J9DQojIEZpdCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIHRoZSB0cmFpbmluZyBzZXQNCmxvZ3JlZyA8LSBnbG0oaW5jb21lX2luZCB+IGFnZSArIHNleCArIGVkdWNhdGlvbl8xLCBmYW1pbHkgPSAiYmlub21pYWwiLCBkYXRhID0gdHJhaW4pDQoNCiMgRGlzcGxheSBtb2RlbCBzdW1tYXJ5DQpzdW1tYXJ5KGxvZ3JlZykNCg0KDQoNCmBgYA0KDQoqKmYpICgxMCBwdHMpKiogV2hpY2ggb25lIG9mIHRoZSBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgYWNoaWV2ZWQgdGhlIGhpZ2hlc3QgYWNjdXJhY3k/DQpUaGUgS05OIG1vZGVsIGFjaGlldmVkIHRoZSBoaWdoZXN0IGFjY3VyYWN5IG9mIDg0LjcyJQ0KIyMgVEFTSyAyIChUb3RhbCA0MCBwdHMsIDIwIHB0cyBlYWNoIHBhcnQgYSAmIGIgYmVsb3cpOg0KDQpMZXQgdXMgY29uc2lkZXIgdGhlIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBwcm9jZXNzIG9mIGlkZW50aWZ5aW5nIGRpZmZlcmVudCB0eXBlcyBvZiBjYXJzLiBUaGUgVW5pdGVkIFN0YXRlcyBEZXBhcnRtZW50IG9mIEVuZXJneSBtYWludGFpbnMgYXV0b21vYmlsZSBjaGFyYWN0ZXJpc3RpY3MgZm9yIHRob3VzYW5kcyBvZiBjYXJzOiBgbWlsZXMgcGVyIGdhbGxvbmAsIGBlbmdpbmUgc2l6ZWAsIGBudW1iZXIgb2YgY3lsaW5kZXJzYCwgYG51bWJlciBvZiBnZWFyc2AsIGV0Yy4NCg0KSGVyZSwgd2UgdXNlIHRoZSBgMjAxNiBGRUd1aWRlLnhsc3hgIGZpbGUgdGhhdCBjb250YWlucyBmdWVsIGVjb25vbXkgcmF0aW5nIGZvciB0aGUgMjAxNiBtb2RlbCB5ZWFyLg0KDQpOZXh0LCB3ZSB3aWxsIHVzZSB0aGUgYHJlYWR4bGAgcGFja2FnZSB0byByZWFkIHRoaXMgZmlsZSBpbnRvIGBSYCwgY2xlYW4gdXAgc29tZSBvZiB0aGUgcmVzdWx0aW5nIHZhcmlhYmxlIG5hbWVzLCBzZWxlY3QgYSBzbWFsbCBzdWJzZXQgb2YgdGhlIHZhcmlhYmxlcywgYW5kIGZpbHRlciBmb3IgZGlzdGluY3QgbW9kZWxzIG9mIFRveW90YSB2ZWhpY2xlcy4gVGhlIHJlc3VsdGluZyBkYXRhIHNldCBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCBgNzVgIGRpZmZlcmVudCBtb2RlbHMgdGhhdCBUb3lvdGEgcHJvZHVjZXMuDQoNClN0b3JlIHRoZSBkYXRhIGZpbGUgIjIwMTYgRkVHdWlkZS54bHN4IiBpbiBhIHN1YmZvbGRlciBieSB0aGUgbmFtZSAqKmBkYXRhYCoqIGluIHlvdXIgd29ya2luZyBkaXJlY3RvcnkuDQoNCioqTm90ZToqKiBZb3UgbWF5IG5lZWQgdG8gYWRqdXN0IHRoZSBjb2RlIGJlbG93IHRvIHNwZWNpZnkgdGhlICIyMDE2IEZFR3VpZGUueGxzeCIgZmlsZSBsb2NhdGlvbiBpZiB5b3Ugb3B0IG91dCB0byBzdG9yZSB0aGUgZGF0YSBmaWxlIGluIGRpZmZlcmVudCBsb2NhdGlvbi4NCg0KYGBge3IgTW9kZWxzfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoamFuaXRvcikNCmxpYnJhcnkoYXBlKQ0KDQojIExvYWQgYW5kIGNsZWFuIGRhdGEgZm9yIEhvbmRhDQpmaWxlbmFtZSA8LSAiZGF0YS8yMDE2IEZFR3VpZGUueGxzeCINCg0KIyBSZWFkIHRoZSBkYXRhIGFuZCBjbGVhbiBjb2x1bW4gbmFtZXMNCmNhcnMgPC0gcmVhZF9leGNlbChmaWxlbmFtZSkgJT4lIA0KICBjbGVhbl9uYW1lcygpICAjIENsZWFuIGNvbHVtbiBuYW1lcw0KDQojIENoZWNrIGNvbHVtbiBuYW1lcw0KZ2xpbXBzZShjYXJzKQ0KDQojIEFmdGVyIGNoZWNraW5nIHRoZSBjb2x1bW4gbmFtZXMsIHVwZGF0ZSB0aGUgc2VsZWN0IGZ1bmN0aW9uIHdpdGggY29ycmVjdCBuYW1lcw0KY2FycyA8LSBjYXJzICU+JQ0KICBzZWxlY3QoDQogICAgbWFrZSA9IG1mcl9uYW1lLCAgICAgICAgICAgICMgQ29ycmVjdCBjb2x1bW4gbmFtZSBmb3IgbWFudWZhY3R1cmVyDQogICAgbW9kZWwgPSBjYXJsaW5lLCAgICAgICAgICAgICMgQ29ycmVjdCBjb2x1bW4gbmFtZSBmb3IgY2FyIG1vZGVsDQogICAgZGlzcGxhY2VtZW50ID0gZW5nX2Rpc3BsLCAgICMgQ29ycmVjdCBjb2x1bW4gbmFtZSBmb3IgZW5naW5lIGRpc3BsYWNlbWVudA0KICAgIG51bWJlcl9jeWwgPSBjeWxpbmRlcnMsICAgICMgQ29ycmVjdCBjb2x1bW4gbmFtZSBmb3IgbnVtYmVyIG9mIGN5bGluZGVycw0KICAgIG51bWJlcl9nZWFycyA9IGdlYXJzLCAgICAgICMgQ29ycmVjdCBjb2x1bW4gbmFtZSBmb3IgbnVtYmVyIG9mIGdlYXJzDQogICAgY2l0eV9tcGcgPSBjaXR5X2ZlX2d1aWRlX2NvbnZlbnRpb25hbF9mdWVsLCAgIyBDb3JyZWN0IGNvbHVtbiBuYW1lIGZvciBjaXR5IG1wZw0KICAgIGh3eV9tcGcgPSBod3lfZmVfZ3VpZGVfY29udmVudGlvbmFsX2Z1ZWwgICAgICMgQ29ycmVjdCBjb2x1bW4gbmFtZSBmb3IgaGlnaHdheSBtcGcNCiAgKSAlPiUNCiAgZGlzdGluY3QobW9kZWwsIC5rZWVwX2FsbCA9IFRSVUUpICU+JSANCiAgZmlsdGVyKG1ha2UgPT0gIkhvbmRhIikgICMgRmlsdGVyIGZvciBIb25kYSBtb2RlbHMgb25seQ0KDQojIFZpZXcgdGhlIGNsZWFuZWQgZGF0YQ0KZ2xpbXBzZShjYXJzKQ0KDQoNCg0KDQpgYGANCg0KQXMgYSBsYXJnZSBhdXRvbWFrZXIsIFRveW90YSBoYXMgYSBkaXZlcnNlIGxpbmV1cCBvZiBjYXJzLCB0cnVja3MsIFNVVnMsIGFuZCBoeWJyaWQgdmVoaWNsZXMuIENhbiB3ZSB1c2UgdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHRvIGNhdGVnb3JpemUgdGhlc2UgdmVoaWNsZXMgaW4gYSBzZW5zaWJsZSB3YXkgd2l0aCBvbmx5IHRoZSBkYXRhIHdlIGhhdmUgYmVlbiBnaXZlbj8NCg0KRm9yIGFuIGluZGl2aWR1YWwgcXVhbnRpdGF0aXZlIHZhcmlhYmxlLCBpdCBpcyBlYXN5IHRvIG1lYXN1cmUgaG93IGZhciBhcGFydCBhbnkgdHdvIGNhcnMgYXJlOiBUYWtlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG51bWVyaWNhbCB2YWx1ZXMuIFRoZSBkaWZmZXJlbnQgdmFyaWFibGVzIGFyZSwgaG93ZXZlciwgb24gZGlmZmVyZW50IHNjYWxlcyBhbmQgaW4gZGlmZmVyZW50IHVuaXRzLiBGb3IgZXhhbXBsZSwgYGdlYXJzYCByYW5nZXMgb25seSBmcm9tIGAxYCB0byBgOGAsIHdoaWxlIGBjaXR5X21wZ2AgZ29lcyBmcm9tIGAxM2AgdG8gYDU4YC4gVGhpcyBtZWFucyB0aGF0IHNvbWUgZGVjaXNpb24gbmVlZHMgdG8gYmUgbWFkZSBhYm91dCByZXNjYWxpbmcgdGhlIHZhcmlhYmxlcyBzbyB0aGF0IHRoZSBkaWZmZXJlbmNlcyBhbG9uZyBlYWNoIHZhcmlhYmxlIHJlYXNvbmFibHkgcmVmbGVjdCBob3cgZGlmZmVyZW50IHRoZSByZXNwZWN0aXZlIGNhcnMgYXJlLiBUaGVyZSBpcyBtb3JlIHRoYW4gb25lIHdheSB0byBkbyB0aGlzLCBhbmQgaW4gZmFjdCwgdGhlcmUgaXMgbm8gdW5pdmVyc2FsbHkgImJlc3QiIHNvbHV0aW9uLS0tdGhlIGJlc3Qgc29sdXRpb24gd2lsbCBhbHdheXMgZGVwZW5kIG9uIHRoZSBkYXRhIGFuZCB5b3VyIGRvbWFpbiBleHBlcnRpc2UuIFRoZSBgZGlzdCgpYCBmdW5jdGlvbiB0YWtlcyBhIHNpbXBsZSBhbmQgcHJhZ21hdGljIHBvaW50IG9mIHZpZXc6ICoqRWFjaCB2YXJpYWJsZSBpcyBlcXVhbGx5IGltcG9ydGFudCoqLg0KDQpUaGUgb3V0cHV0IG9mIGBkaXN0KClgIGdpdmVzIHRoZSBkaXN0YW5jZSBmcm9tIGVhY2ggaW5kaXZpZHVhbCBjYXIgdG8gZXZlcnkgb3RoZXIgY2FyLg0KDQpgYGB7cn0NCiMgQXNzdW1pbmcgY2FycyBkYXRhc2V0IGlzIGFscmVhZHkgbG9hZGVkDQoNCiMgU2NhbGUgdGhlIGRhdGEgKGp1c3QgdGhlIG51bWVyaWMgdmFyaWFibGVzIGZvciBjbHVzdGVyaW5nKQ0KY2Fyc19zY2FsZWQgPC0gc2NhbGUoY2Fyc1ssIGMoImRpc3BsYWNlbWVudCIsICJudW1iZXJfY3lsIiwgIm51bWJlcl9nZWFycyIsICJjaXR5X21wZyIsICJod3lfbXBnIildKQ0KDQojIENvbXB1dGUgdGhlIGRpc3RhbmNlIG1hdHJpeA0KY2FyX2RpZmZzIDwtIGRpc3QoY2Fyc19zY2FsZWQpDQoNCiMgQ29udmVydCB0aGUgZGlzdGFuY2UgbWF0cml4IGludG8gYSByZWd1bGFyIG1hdHJpeCBhbmQgcm91bmQgaXQNCmNhcl9tYXQgPC0gY2FyX2RpZmZzICU+JSBhcy5tYXRyaXgoKQ0KDQojIERpc3BsYXkgYW5kIHJvdW5kIHRoZSBmaXJzdCA2eDYgc3VibWF0cml4DQpjYXJfbWF0WzE6NiwgMTo2XSAlPiUgcm91bmQoZGlnaXRzID0gMikNCg0KYGBgDQoNCkNyZWF0ZSBkaXN0YW5jZSBtYXRyaXggb2JqZWN0IGZyb20gdGhlIGBjYXJfZGlmZnNgDQoNCmBgYHtyfQ0KY2FyX21hdCA8LSBjYXJfZGlmZnMgJT4lIGFzLm1hdHJpeCgpIA0KY2FyX21hdFsxOjYsIDE6Nl0gJT4lIHJvdW5kKGRpZ2l0cyA9IDIpDQpgYGANCg0KYGBge3IgY2x1c3RlcmluZywgd2FybmluZyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gMTQsIGZpZy53aWR0aCA9IDh9DQojaW5zdGFsbCBpZiBub3QgaW5zdGFsbGVkDQojIGluc3RhbGwucGFja2FnZXMoImFwZSIpDQoNCmxpYnJhcnkoYXBlKSANCmNhcl9kaWZmcyAlPiUgDQogIGhjbHVzdCgpICU+JSANCiAgYXMucGh5bG8oKSAlPiUgDQogIHBsb3QoY2V4ID0gMC45LCBsYWJlbC5vZmZzZXQgPSAxKQ0KDQpgYGANCg0KQ2hvb3NlICoqb25lKiogb2YgdGhlIGNhciBtYWtlcnM6IGBHZW5lcmFsIE1vdG9ycywgTmlzc2FuLCBGb3JkIE1vdG9yIENvbXBhbnksIEhvbmRhLCBNZXJjZWRlcy1CZW56LCBCTVcsIEtpYWAgLSBwcmVmZXJhYmx5IG1ha2VyIHRoYXQgeW91IGFyZSBmYW1pbGlhciB3aXRoIHRoZSBtb2RlbHMgYnV0IG5vdCBuZWNlc3NhcmlseS4NCg0KKiphKSAoVG90YWwgMjAgcHRzKSoqIENyZWF0ZSBhIHRyZWUgY29uc3RydWN0ZWQgYnkgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdGhhdCByZWxhdGVzIGNhcm1ha2VyIGNhciBtb2RlbHMgdG8gb25lIGFub3RoZXIuDQoNCioqSGludDoqKiBZb3UgY2FuIHVzZSB0aGUgY29kZSBhYm92ZSBhbmQgbWFrZSBuZWNlc3NhcnkgbW9kaWZpY2F0aW9uLg0KDQoqKllPVVIgQ09ERSBIRVJFOioqDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoamFuaXRvcikNCmxpYnJhcnkoYXBlKQ0KDQojIExvYWQgYW5kIGNsZWFuIGRhdGEgZm9yIEhvbmRhDQpmaWxlbmFtZSA8LSAiZGF0YS8yMDE2IEZFR3VpZGUueGxzeCINCg0KY2FycyA8LSByZWFkX2V4Y2VsKGZpbGVuYW1lKSAlPiUgDQogIGNsZWFuX25hbWVzKCkgJT4lDQogIHNlbGVjdCgNCiAgICBtYWtlID0gbWZyX25hbWUsDQogICAgbW9kZWwgPSBjYXJsaW5lLA0KICAgIGRpc3BsYWNlbWVudCA9IGVuZ19kaXNwbCwNCiAgICBudW1iZXJfY3lsID0gY3lsaW5kZXJzLA0KICAgIG51bWJlcl9nZWFycyA9IGdlYXJzLA0KICAgIGNpdHlfbXBnID0gY2l0eV9mZV9ndWlkZV9jb252ZW50aW9uYWxfZnVlbCwNCiAgICBod3lfbXBnID0gaHd5X2ZlX2d1aWRlX2NvbnZlbnRpb25hbF9mdWVsDQogICkgJT4lDQogIGRpc3RpbmN0KG1vZGVsLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUNCiAgZmlsdGVyKG1ha2UgPT0gIkhvbmRhIikgICMgRmlsdGVyaW5nIGZvciBIb25kYSBvbmx5DQoNCiMgVmlldyB0aGUgY2xlYW5lZCBkYXRhDQpnbGltcHNlKGNhcnMpDQoNCiMgU2NhbGluZyB0aGUgZGF0YSB0byBub3JtYWxpemUgdGhlIGZlYXR1cmVzDQpjYXJzX3NjYWxlZCA8LSBjYXJzICU+JQ0KICBzZWxlY3QoZGlzcGxhY2VtZW50LCBudW1iZXJfY3lsLCBudW1iZXJfZ2VhcnMsIGNpdHlfbXBnLCBod3lfbXBnKSAlPiUNCiAgc2NhbGUoKQ0KDQojIENvbXB1dGUgdGhlIGRpc3RhbmNlIG1hdHJpeA0KY2FyX2RpZmZzIDwtIGRpc3QoY2Fyc19zY2FsZWQpDQoNCiMgQ3JlYXRlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nDQpoYyA8LSBoY2x1c3QoY2FyX2RpZmZzKQ0KDQojIFBsb3QgdGhlIGRlbmRyb2dyYW0gKHRyZWUpDQpwbG90KGhjLCBtYWluID0gIkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nIG9mIEhvbmRhIENhciBNb2RlbHMiLCB4bGFiID0gIkNhciBNb2RlbHMiLCBzdWIgPSAiIiwgY2V4ID0gMC42LCBsYWJlbHMgPSBjYXJzJG1vZGVsKQ0KDQpgYGANCg0KKipiKSAoVG90YWwgMjAgcHRzKSoqIEF0dGVtcHQgdG8gaW50ZXJwcmV0IHRoZSB0cmVlLCBob3cgdGhlIG1vZGVscyBpbiBzYW1lIGNsdXN0ZXIgYXJlIHNpbWlsYXIgYW5kIGhvdyBjbHVzdGVycyBkaWZmZXIuDQoNCioqWU9VUiBDT01NRU5UUzoqKg0KDQojIyBUQVNLIDMgKFRvdGFsIDQwIHB0cywgMjAgcHRzIGVhY2ggcGFydCBhICYgYiBiZWxvdykgSy1tZWFucyBjbHVzdGVyaW5nIChHZW9zcGF0aWFsIGRhdGEgZXhhbXBsZSkNCg0KQW5vdGhlciB3YXkgdG8gZ3JvdXAgc2ltaWxhciBjYXNlcyBpcyB0byBhc3NpZ24gZWFjaCBjYXNlIHRvIG9uZSBvZiBzZXZlcmFsIGRpc3RpbmN0IGdyb3VwcywgYnV0IHdpdGhvdXQgY29uc3RydWN0aW5nIGEgaGllcmFyY2h5LiBUaGUgb3V0cHV0IGlzIG5vdCBhIHRyZWUgYnV0IGEgY2hvaWNlIG9mIGdyb3VwIHRvIHdoaWNoIGVhY2ggY2FzZSBiZWxvbmdzLiAoVGhlcmUgY2FuIGJlIG1vcmUgZGV0YWlsIHRoYW4gdGhpczsgZm9yIGluc3RhbmNlLCBhIHByb2JhYmlsaXR5IGZvciBlYWNoIGdyb3VwIHRoYXQgYSBzcGVjaWZpYyBjYXNlIGJlbG9uZ3MgdG8gdGhlIGdyb3VwLikgVGhpcyBpcyBsaWtlIGNsYXNzaWZpY2F0aW9uIGV4Y2VwdCB0aGF0IGhlcmUgKip0aGVyZSBpcyBubyByZXNwb25zZSB2YXJpYWJsZSoqLg0KDQoqKkdlb3NwYXRpYWwgZGF0YSBleGFtcGxlOioqDQoNCkNvbnNpZGVyIHRoZSBjaXRpZXMgb2YgdGhlIHdvcmxkIChpbiBgV29ybGRDaXRpZXNgLCBpbiB0aGUgYG1kc3JgIHBhY2thZ2UpLiBDaXRpZXMgY2FuIGJlIGRpZmZlcmVudCBhbmQgc2ltaWxhciBpbiBtYW55IHdheXM6IHBvcHVsYXRpb24sIGFnZSBzdHJ1Y3R1cmUsIHB1YmxpYyB0cmFuc3BvcnRhdGlvbiBhbmQgcm9hZHMsIGJ1aWxkaW5nIHNwYWNlIHBlciBwZXJzb24sIGV0Yy4gVGhlIGNob2ljZSBvZiAqZmVhdHVyZXMqIChvciB2YXJpYWJsZXMpIGRlcGVuZHMgb24gdGhlIHB1cnBvc2UgeW91IGhhdmUgZm9yIG1ha2luZyB0aGUgZ3JvdXBpbmcuDQoNCk91ciBwdXJwb3NlIGlzIHRvIHNob3cgeW91IHRoYXQgY2x1c3RlcmluZyB2aWEgbWFjaGluZSBsZWFybmluZyBjYW4gYWN0dWFsbHkgaWRlbnRpZnkgZ2VudWluZSBwYXR0ZXJucyBpbiB0aGUgZGF0YS4NCg0KV2Ugd2lsbCBjaG9vc2UgZmVhdHVyZXMgdGhhdCBhcmUgdXR0ZXJseSBmYW1pbGlhcjogdGhlICoqbGF0aXR1ZGUqKiBhbmQgKipsb25naXR1ZGUqKiBvZiBlYWNoIGNpdHkuDQoNCllvdSBhbHJlYWR5IGtub3cgYWJvdXQgdGhlIGxvY2F0aW9uIG9mIGNpdGllcy4gVGhleSBhcmUgb24gbGFuZC4gQW5kIHlvdSBrbm93IGFib3V0IHRoZSBvcmdhbml6YXRpb24gb2YgbGFuZCBvbiBlYXJ0aDogbW9zdCBsYW5kIGZhbGxzIGluIG9uZSBvZiB0aGUgbGFyZ2UgY2x1c3RlcnMgY2FsbGVkIGNvbnRpbmVudHMuDQoNCkJ1dCB0aGUgYFdvcmxkQ2l0aWVzYCBkYXRhIGRvZXNuJ3QgaGF2ZSBhbnkgbm90aW9uIG9mIGNvbnRpbmVudHMuIFBlcmhhcHMgaXQgaXMgcG9zc2libGUgdGhhdCB0aGlzIGZlYXR1cmUsIHdoaWNoIHlvdSBsb25nIGFnbyBpbnRlcm5hbGl6ZWQsIGNhbiBiZSBsZWFybmVkIGJ5IGEgY29tcHV0ZXIgdGhhdCBoYXMgbmV2ZXIgZXZlbiB0YWtlbiBncmFkZS1zY2hvb2wgZ2VvZ3JhcGh5Lg0KDQpDb25zaWRlciB0aGUgYDQsMDAwYCBiaWdnZXN0IGNpdGllcyBpbiB0aGUgd29ybGQgYW5kIHRoZWlyIGxvbmdpdHVkZXMgYW5kIGxhdGl0dWRlcy4NCg0KYGBge3Igc2VsZWN0Q2l0aWVzfQ0KQmlnQ2l0aWVzIDwtIHdvcmxkX2NpdGllcyAlPiUgYXJyYW5nZShkZXNjKHBvcHVsYXRpb24pKSAlPiUgDQogIGhlYWQoNDAwMCkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KGxvbmdpdHVkZSwgbGF0aXR1ZGUpDQpnbGltcHNlKEJpZ0NpdGllcykNCmBgYA0KDQpOb3RlIHRoYXQgaW4gdGhlc2UgZGF0YSwgdGhlcmUgaXMgbm8gYW5jaWxsYXJ5IGluZm9ybWF0aW9uLS0tbm90IGV2ZW4gdGhlIG5hbWUgb2YgdGhlIGNpdHkuIEhvd2V2ZXIsIHRoZSBgay1tZWFuc2AgY2x1c3RlcmluZyBhbGdvcml0aG0gd2lsbCBzZXBhcmF0ZSB0aGVzZSBgNCwwMDBgIHBvaW50cy0tLWVhY2ggb2Ygd2hpY2ggaXMgbG9jYXRlZCBpbiBhIHR3by1kaW1lbnNpb25hbCBwbGFuZS0tLWludG8gYGtgIGNsdXN0ZXJzIGJhc2VkIG9uIHRoZWlyIGxvY2F0aW9ucyBhbG9uZS4NCg0KYGBge3J9DQoNCnNldC5zZWVkKDE1KQ0KIyBpbnN0YWxsIHRoZSBwYWNrYWdlIGZpcnN0IGlmIG5vdCBpbnN0YWxsZWQNCiNpbnN0YWxsLnBhY2thZ2VzKCJtY2x1c3QiKQ0KbGlicmFyeShtY2x1c3QpIA0KDQojIGZvcm0gNiBjbHVzdGVyIGl0ZXJhdGl2ZWx5DQpjaXR5X2NsdXN0cyA8LSBCaWdDaXRpZXMgJT4lDQprbWVhbnMoY2VudGVycyA9IDYpICU+JSBmaXR0ZWQoImNsYXNzZXMiKSAlPiUgYXMuY2hhcmFjdGVyKCkNCg0KIyBmb3JtIDYgY2x1c3RlciBpdGVyYXRpdmVseSwgYnkgZm9ybWluZyBpbml0aWFsbHkgMTAgcmFuZG9tIHNldHMNCmttIDwtIGttZWFucyhCaWdDaXRpZXMsIGNlbnRlcnMgPSA2LCBuc3RhcnQgPSAxMCkNCiMgaW5zcGVjdCB0aGUgc3RydWN0dXJlIG9mIHRoZSBrbWVhbnMgb3V0cHV0IGNsdXN0ZXIgb2JqZWN0DQpzdHIoa20pDQoNCiMgYWNjZXNzIHR3byBpbXBvcnRhbnQgZmVhdHVyZXMgb2YgY2x1c3RlciwgdGhlaXIgc2l6ZSBhbmQgY2VudGVycw0Ka20kc2l6ZQ0Ka20kY2VudGVycw0KDQpCaWdDaXRpZXMgPC0gQmlnQ2l0aWVzICU+JSBtdXRhdGUoY2x1c3RlciA9IGNpdHlfY2x1c3RzKSANCg0KIyBncmFwaCB0aGUgY2x1c3RlcnMsIHVzaW5nIHRoZSBjbHVzdGVyIHZhcmlhYmxlIHRvIHBpY2sgdGhlIGNvbG9yIGluIHN0YW5kYXJkIGNhcnRlc2lhbiBjb29yZGluYXRlIHN5c3RlbQ0KQmlnQ2l0aWVzICU+JSBnZ3Bsb3QoYWVzKHggPSBsb25naXR1ZGUsIHkgPSBsYXRpdHVkZSkpICsNCmdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2x1c3RlciksIGFscGhhID0gMC41KQ0KDQpgYGANCg0KKiphKSBUb3RhbCAoMTAgcHRzKSoqIFdoYXQgZGlkIHRoZSBhYm92ZSBjbHVzdGVyaW5nIGFsZ29yaXRobSBzZWVtcyB0byBoYXZlIGlkZW50aWZpZWQ/DQoNCioqQW5zd2VyOioqDQoNCioqYikgVG90YWwgKDMwIHB0cykqKg0KDQoqKlByb2plY3Rpb25zOioqIFRoZSBFYXJ0aCBoYXBwZW5zIHRvIGJlIGFuIG9ibGF0ZSBzcGhlcm9pZC0tLWEgdGhyZWUtZGltZW5zaW9uYWwgZmxhdHRlbmVkIHNwaGVyZS4gWWV0IHdlIHdvdWxkIGxpa2UgdG8gY3JlYXRlIHR3by1kaW1lbnNpb25hbCByZXByZXNlbnRhdGlvbnMgb2YgdGhlIEVhcnRoIHRoYXQgZml0IG9uIHBhZ2VzIG9yIGNvbXB1dGVyIHNjcmVlbnMuIFRoZSBwcm9jZXNzIG9mIGNvbnZlcnRpbmcgbG9jYXRpb25zIGluIGEgdGhyZWUtZGltZW5zaW9uYWwgKmdlb2dyYXBoaWMgY29vcmRpbmF0ZSBzeXN0ZW0qIHRvIGEgdHdvLWRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIGlzIGNhbGxlZCAqcHJvamVjdGlvbiouDQoNCkEgKmNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbSAoQ1JTKSogaXMgbmVlZGVkIHRvIGtlZXAgdHJhY2sgb2YgZ2VvZ3JhcGhpYyBsb2NhdGlvbnMuIEV2ZXJ5IHNwYXRpYWxseS1hd2FyZSBvYmplY3QgaW4gYFJgIGNhbiBoYXZlIGEgcHJvamVjdGlvbiBzdHJpbmcsIGVuY29kZWQgdXNpbmcgdGhlIGBQUk9KLjRgIG1hcCBwcm9qZWN0aW9uIGxpYnJhcnkuIFRoZXNlIGNhbiBiZSByZXRyaWV2ZWQgKG9yIHNldCkgdXNpbmcgdGhlIGBwcm9qNHN0cmluZygpYCBjb21tYW5kLg0KDQpUaGVyZSBhcmUgbWFueSAqQ1JTKnMsIGJ1dCBhIGZldyBhcmUgbW9zdCBjb21tb24uIEEgc2V0IG9mIEVQU0cgKEV1cm9wZWFuIFBldHJvbGV1bSBTdXJ2ZXkgR3JvdXApIGNvZGVzIHByb3ZpZGVzIGEgc2hvcnRoYW5kIGZvciB0aGUgZnVsbCBQUk9KLjQgc3RyaW5ncyAobGlrZSB0aGUgb25lIHNob3duIGFib3ZlKS4gVGhlIG1vc3QgY29tbW9ubHktdXNlZCBhcmU6DQoNCioqRVBTRzo0MzI2KiogQWxzbyBrbm93biBhcyBXR1M4NCwgdGhpcyBpcyB0aGUgc3RhbmRhcmQgZm9yIEdQUyBzeXN0ZW1zIGFuZCBHb29nbGUgRWFydGguDQoNCioqRVBTRzozODU3KiogQSBNZXJjYXRvciBwcm9qZWN0aW9uIHVzZWQgaW4gbWFwcyB0aWxlczQgYnkgR29vZ2xlIE1hcHMsIE9wZW4gU3RyZWV0IE1hcHMsIGV0Yy4NCg0KKipFUFNHOjQyNjkqKiBOQUQ4MywgbW9zdCBjb21tb25seSB1c2VkIGJ5IFUuUy4gZmVkZXJhbCBhZ2VuY2llcy4NCg0KYFItQ29kZWAgYmVsb3cgdXNlcyAqKkVQU0c6NDMyNioqLiBVc2UgdGhlIG90aGVyICoqdHdvIHN0YW5kYXJkcyoqICoqRVBTRzozODU3KiosICoqRVBTRzo0MjY5KioNCg0KVXNlIGBLLW1lYW5zYCBhbGdvcml0aG0gZm9yIGVhY2ggb2YgdGhlIHRocmVlICgzKSBwcm9qZWN0aW9ucyBhYm92ZSBhbmQgY29tcGFyZSB0aGUgdGhyZWUgcHJvamVjdGlvbnMgdG8gdGhlIHN0YW5kYXJkIGNhcnRlc2lhbiBjb29yZGluYXRlcyB1c2VkIGluIHRoZSBleGFtcGxlLiBXaGljaCBvbmUgaXMgYmVzdCBpbiBpZGVudGlmeWluZyB0aGUgY29udGluZW50cz8NCg0KKipOb3RlOioqIEdyYXBoaW5nIHRoZSBjbHVzdGVycyBpbiBlYWNoIHByb2plY3Rpb24gaXMgd29ydGggKioxMCBwdHMqKg0KDQpgYGB7ciBTcGF0aWFsIE9iamVjdH0NCg0KIyBhc3NpZ24gdGhlIEJpZ0NpdGllcyBkYXRhLmZyYW1lIHRvIGEgd29ya2luZyBkYXRhLmZyYW1lIG9iamVjdCBkIA0KZCA8LSBCaWdDaXRpZXMgI29yIEJpZ0NpdGllc1ssYygnbG9uZ2l0dWRlJywgJ2xhdGl0dWRlJyldDQoNCiMgY3JlYXRlIHNwYXRpYWwgb2JqZWN0IGZyb20gZA0KY29vcmRpbmF0ZXMoZCkgPC0gMToyDQoNCiMgU2V0IFdHUyA4NCAoRVBTRzo0MzI2KSBzdGFuZGFyZCBmb3IgcHJvamVjdGluZyBsb25naXR1ZGUgbGF0aXR1ZGUgY29vcmRpbmF0ZXMNCnByb2o0c3RyaW5nKGQpIDwtIENSUygiK2luaXQ9ZXBzZzo0MzI2IikNCg0KIyBjb29yZGluYXRlIHJlZmVyZW5jZSBzeXN0ZW0gdXNpbmcgdGhlIEVQU0c6NDMyNiBzdGFuZGFyZA0KQ1JTLm5ldyA8LSBDUlMoIitpbml0PWVwc2c6NDMyNiIpDQoNCiMgdGhlIGQgb2JqZWN0IGluIHRoZSBuZXcgQ1JTLCB5b3UgbWF5IHByaW50IG91dCBmZXcgcmVjb3JkcyB0byBzZWUgaG93IGl0IGxvb2tzIGluIHRoZSBuZXcgQ1JTDQpkLm5ldyA8LSBzcFRyYW5zZm9ybShkLCBDUlMubmV3KQ0KDQojIGp1c3QgZm9yIGluZm9ybWF0aW9uIHJldmlldyB0aGUgDQpwcm9qNHN0cmluZyhkLm5ldykgJT4lIHN0cndyYXAoKQ0KDQoNCiMgZm9ybSA2IGNsdXN0ZXIgaXRlcmF0aXZlbHkNCmNpdHlfY2x1c3RzIDwtIGFzLmRhdGEuZnJhbWUoZC5uZXcpICU+JQ0Ka21lYW5zKGNlbnRlcnMgPSA2KSAlPiUgZml0dGVkKCJjbGFzc2VzIikgJT4lIGFzLmNoYXJhY3RlcigpDQoNCiMgYWRkIGEgdmFyaWFibGUgZm9yIHRoZSBuZXdseSBmb3JtZWQgY2x1c3RlcnMNCmRmLm5ldyA8LSBhcy5kYXRhLmZyYW1lKGQubmV3KSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBjaXR5X2NsdXN0cywgbG9uZ2l0dWRlID0gY29vcmRzLngxLCBsYXRpdHVkZSA9IGNvb3Jkcy54MikNCg0KIyBncmFwaCB0aGUgY2x1c3RlcnMsIHVzaW5nIHRoZSBjbHVzdGVyIHZhcmlhYmxlIHRvIHBpY2sgdGhlIGNvbG9yDQpkZi5uZXcgJT4lIGdncGxvdChhZXMoeCA9IGNvb3Jkcy54MSwgeSA9IGNvb3Jkcy54MikpICsNCmdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2x1c3RlciksIGFscGhhID0gMC41KSArDQpzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikNCg0KYGBgDQoNCioqWU9VUiBDT0RFIEhFUkU6KioNCg0KYGBge3J9DQoNCmBgYA0K