Part 1

Intro

Real Life Applications of ML

  1. Supervised Classification: Whether a 1st year college student will graduate
  2. Supervised Regression: The avg. rating of a professor for a class
  3. Unsupervised Clustering: What songs to queue in a music streaming app
  4. Unsupervised Dimension Reduction: 5 Most important attributes to profile a customer

College Student Classification

Application:

Applying Machine learning to predict whether a 1st year college student will graduate allows for universities to:

  • Identify key features that support successful students
  • Support individuals with lower probabilities of success

Supervised or Unsupervised:

Since universities measure and retain information about the binary outcome (Y/N) of whether a college student graduates, this would be a Supervised Classification problem.

Target Variable:

The target variable is a Binary Y/N of whether the student graduates or not

Potential Feature Variables:
  • High school GPA
  • Household Income
  • Declared or Undeclared Degree (Y/N)
  • Anticipated Degree
  • Geographic Location

Data Collection:

Colleges already keep extensive information about students (and likely buy it from third party sources). Anticipated barriers lie in accessing the data/gaining data permissions.

Ethical Concerns:

So many… Especially regarding public colleges and universities (versus private or in regular business). - Could be used (and likely is used) to optimize college selection for students, reducing opportunity for low-opportunity students (rather than helping them) - Anticipated lawsuits for seeming discriminatory against certain segments in admission process —

Professor Rating Regression

Application:

Applying Machine Learning to predict the average rating (out of 5) of college professors for the classes they teach:

  • Determine certain classes or trends leading to higher or lower avg. ratings
  • Provide strategic feedback to professors to increase their rating among students

Supervised or Unsupervised:

Since the model would predict a numerical value that can be checked against actual results, this falls within Supervised Regression territory.

Target Variable:

The target variable is Average Rating of a professor at the end of a semester for a class

Potential Feature Variables:
  • Number of students in class
  • Class name
  • Department
  • Online/In Person
  • Tenure (Y/N)
  • Years of experience
Data Collection:

Again, colleges already maintain information about classes and professors, including professor ratings by semester.

Ethical Concerns:

The biggest ethical concern lies in professors utilizing this model to change their teaching style for better ratings (and higher pay) without considering the value that they bring to their students (AKA optimizing for rating, rather than teaching). —

Clustering Songs for Queue

Application:

Applying Machine Learning to ascertain clusters of songs to be placed in the queue of a user’s music streaming app:

  • Better queue recommendations/clusters lead to higher customer satisfaction

Supervised or Unsupervised:
  • Since this model aims to group songs based on user information, and since there is no true answer to measure against, this serves as an Unsupervised Clustering model.

Target Variable:

Unsupervised models do not use target variables

Potential Feature Variables:
  • Time of day
  • Day of week/month
  • Previous songs queued/played by user
  • Liked artists
  • Liked albums/music types

Data Collection:

Ideally, this would be developed as an employee within one of these companies. Streaming apps collect extensive user data, which can be leveraged within the model.

Ethical Concerns:

While users agree to data collection within the terms of use of streaming apps, they still find direct use of their information off-putting. Not so much an ethical concern, but a concern of the brand’s PR (think of the famous Target pregnancy model)


Dimension Reduction for Customer Profiling

Application:

I work in an insurance company, where customer information includes over one hundred variables. This model reduces the # of variables to be considered for customer profiling within the business.:

  • Identify and strip away customer features deemed unimportant
  • Simplify customer profiling for future modeling

Supervised or Unsupervised:

No target variable and no true answer, this would be an Unsupervised Dimension Reduction model.

Target Variable:

No target variable

Potential Feature Variables:

Literally hundreds of variables, including…

  • Age
  • Quoted Bodily Injury Limites
  • Prior Insurance
  • Number of Automobiles vs. # of Drivers (also used for fraud detection)
  • Geographic Location

Data Collection:

Assuming working within an insurance company. Data collection ability lies on the user’s ability to gain access to the data (or find people who can make it happen). This can take months, depending on the data required.

Ethical Concerns:

Insurance is heavily regulated,and customer profiling often fits in the “grey” are of legal vs not legal..


Part 2

Data Setup & Loading

Setting global chunk options

knitr::opts_chunk$set(
  comment = '', fig.width = 6, fig.height = 6,
  warning = FALSE, error = FALSE, message = FALSE,
  include = TRUE, echo = TRUE, strip.white = TRUE, highlight = TRUE, results = TRUE
)

Loading Libraries

packages = c('tidyverse','tidymodels','here','kknn')
lapply(packages, library, character.only = TRUE)

Lab Questions

1. Is this a supervised or unsupervised learning problem? Why?
  • Since the target variable is contained within the data, and since the model aims to predict a numerical value, this is a Supervised Regression model

2. There are 16 variables in this data set. Which variable is the response variable and which variables are the predictor variables (aka features)?
  • Target Var: cmedv (Median value of owner-occupied homes in USD 1,000’s)
  • Predictor Vars:
    • lon: longitude of census tract
    • lat: latitude of census tract
    • crim: per capita crime rate by town
    • zn: proportion of residential land zoned for lots over 25,000 sq.ft
    • indus: proportion of non-retail business acres per town
    • chas: Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
    • nox: nitric oxides concentration (parts per 10 million) –> aka air pollution
    • rm: average number of rooms per dwelling
    • age: proportion of owner-occupied units built prior to 1940
    • dis: weighted distances to five Boston employment centers
    • rad: index of accessibility to radial highways
    • tax: full-value property-tax rate per USD 10,000
    • ptratio: pupil-teacher ratio by town
    • lstat: percentage of lower status of the population


3. Given the type of variable cmedv is, is this a regression or classification problem?
  • As stated before, this is a Regression problem


4. Fill in the blanks to import the Boston housing data set (boston.csv). Are there any missing values? What is the minimum and maximum values of cmedv? What is the average cmedv value?
path <- here('Data','boston.csv')
boston <- readr::read_csv(path)
head(boston)



5. Fill in the blanks to split the data into a training set and test set using a 70-30% split. Be sure to include the set.seed(123) so that your train and test sets are the same size as mine.
set.seed(123)
split <- initial_split(boston, prop = 0.7, strata = cmedv)
train <- training(split)
test <- testing(split)



6. How many observations are in the training set and test set?

There are 352 observations in the “train” data set and 154 observations in the “test” data set.

7. Compare the distribution of cmedv between the training set and test set. Do they appear to have the same distribution or do they differ significantly?
train %>% 
  mutate(id = 'train') %>% 
  bind_rows(test %>% mutate(id = 'test')) %>%
  ggplot(aes(cmedv, color = id)) +
  geom_density()



8. Fill in the blanks to fit a linear regression model using the rm feature variable to predict cmedv and compute the RMSE on the test data. What is the test set RMSE?
# fit model
lm1 <- linear_reg() %>%
  fit( cmedv ~ rm, data = train)

# compute the RMSE on the test data
prd1 <- lm1 %>%
  predict(test) %>%
  bind_cols(test %>% select(cmedv)) %>%
  rmse(truth = cmedv, estimate = .pred)

prd1

The test set RMSE is $6,8301 when using rm (average number of rooms) to estimate cmedv



9. Fill in the blanks to fit a linear regression model using all available features to predict cmedv and compute the RMSE on the test data. What is the test set RMSE? Is this better than the previous model’s performance?
# fit model
features <- colnames(boston[names(boston) %in% 'cmedv' == FALSE])

lm2 <- linear_reg() %>%
  fit( cmedv ~ ., data = train[,c("cmedv",features)])

# compute the RMSE on the test data
lm2 %>%
  predict(test) %>%
  bind_cols(test %>% select(cmedv)) %>%
  rmse(truth = cmedv, estimate = .pred)

The test set RMSE is $4,829 when using all features to estimate cmedv. This represents a nearly $2,000 increase in the model’s accuracy.

10. Fit a K-nearest neighbor model that uses all available features to predict cmedv and compute the RMSE on the test data. What is the test set RMSE? Is this better than the previous two models’ performances?
# fit model
knn <- nearest_neighbor() %>%
  set_engine("kknn") %>%
  set_mode("regression") %>%
  fit( cmedv ~ ., data = train[,c("cmedv",features)])

# compute the RMSE on the test data
knn %>%
  predict(test) %>%
  bind_cols(test %>% select(cmedv)) %>%
  rmse(truth = cmedv, estimate = .pred)

Using K-Nearest-Neighbor, the test set RMSE is $3,357 (with all features included). The kknn models maintains a $1,500 increase in accuracy compared to linear regression using all features, and a $3,500 increase in accruacy compared to linear regression using only the rm factor.

LS0tDQp0aXRsZTogIk1vZHVsZSA4IExhYiINCnN1YnRpdGxlOiAiSW50cm8gdG8gTWFjaGluZSBMZWFybmluZyINCmF1dGhvcjogIlJvYW4gWmFwcGFudGkiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCg0KDQpoZWFkZXItaW5jbHVkZXM6DQotIFx1c2VwYWNrYWdle3RpdGxlc2VjfQ0KLS0tDQo8YnI+DQoNCiMjIFBhcnQgMSB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KIyMjIEludHJvDQoNCiMjIyMgUmVhbCBMaWZlIEFwcGxpY2F0aW9ucyBvZiBNTA0KDQoxLiAqKlN1cGVydmlzZWQgQ2xhc3NpZmljYXRpb24qKjogV2hldGhlciBhIDFzdCB5ZWFyIGNvbGxlZ2Ugc3R1ZGVudCB3aWxsIGdyYWR1YXRlIDxicj4NCjIuICoqU3VwZXJ2aXNlZCBSZWdyZXNzaW9uKio6IFRoZSBhdmcuIHJhdGluZyBvZiBhIHByb2Zlc3NvciBmb3IgYSBjbGFzcyA8YnI+DQozLiAqKlVuc3VwZXJ2aXNlZCBDbHVzdGVyaW5nKio6IFdoYXQgc29uZ3MgdG8gcXVldWUgaW4gYSBtdXNpYyBzdHJlYW1pbmcgYXBwIDxicj4NCjQuICoqVW5zdXBlcnZpc2VkIERpbWVuc2lvbiBSZWR1Y3Rpb24qKjogNSBNb3N0IGltcG9ydGFudCBhdHRyaWJ1dGVzIHRvIHByb2ZpbGUgYSBjdXN0b21lciAgDQoNCi0tLQ0KDQojIyMgQ29sbGVnZSBTdHVkZW50IENsYXNzaWZpY2F0aW9uDQojIyMjIyMgKipBcHBsaWNhdGlvbioqOg0KICBBcHBseWluZyBNYWNoaW5lIGxlYXJuaW5nIHRvIHByZWRpY3Qgd2hldGhlciBhIDFzdCB5ZWFyIGNvbGxlZ2Ugc3R1ZGVudCB3aWxsIA0KICBncmFkdWF0ZSBhbGxvd3MgZm9yIHVuaXZlcnNpdGllcyB0bzo8YnI+DQoNCiAgLSBJZGVudGlmeSBrZXkgZmVhdHVyZXMgdGhhdCBzdXBwb3J0IHN1Y2Nlc3NmdWwgc3R1ZGVudHMNCiAgLSBTdXBwb3J0IGluZGl2aWR1YWxzIHdpdGggbG93ZXIgcHJvYmFiaWxpdGllcyBvZiBzdWNjZXNzPGJyPjxicj4NCg0KIyMjIyMjICoqU3VwZXJ2aXNlZCBvciBVbnN1cGVydmlzZWQqKjoNCiAgU2luY2UgdW5pdmVyc2l0aWVzIG1lYXN1cmUgYW5kIHJldGFpbiBpbmZvcm1hdGlvbiBhYm91dCB0aGUgYmluYXJ5IG91dGNvbWUgKFkvTikgb2YNCiAgd2hldGhlciBhIGNvbGxlZ2Ugc3R1ZGVudCBncmFkdWF0ZXMsIHRoaXMgd291bGQgYmUgYSBTdXBlcnZpc2VkIENsYXNzaWZpY2F0aW9uIHByb2JsZW0uDQogIDxicj48YnI+DQogICAgDQojIyMjIyMgKipUYXJnZXQgVmFyaWFibGUqKjogDQogIFRoZSB0YXJnZXQgdmFyaWFibGUgaXMgYSBCaW5hcnkgWS9OIG9mIHdoZXRoZXIgdGhlIHN0dWRlbnQgZ3JhZHVhdGVzIG9yIG5vdCA8YnI+PGJyPg0KICANCiMjIyMjIyAqKlBvdGVudGlhbCBGZWF0dXJlIFZhcmlhYmxlcyoqOg0KICAtIEhpZ2ggc2Nob29sIEdQQQ0KICAtIEhvdXNlaG9sZCBJbmNvbWUNCiAgLSBEZWNsYXJlZCBvciBVbmRlY2xhcmVkIERlZ3JlZSAoWS9OKQ0KICAtIEFudGljaXBhdGVkIERlZ3JlZQ0KICAtIEdlb2dyYXBoaWMgTG9jYXRpb24gIA0KICA8YnI+DQogIA0KIyMjIyMjICoqRGF0YSBDb2xsZWN0aW9uKio6IA0KICBDb2xsZWdlcyBhbHJlYWR5IGtlZXAgZXh0ZW5zaXZlIGluZm9ybWF0aW9uIGFib3V0IHN0dWRlbnRzIChhbmQgbGlrZWx5IGJ1eSBpdCBmcm9tIHRoaXJkDQogIHBhcnR5IHNvdXJjZXMpLiBBbnRpY2lwYXRlZCBiYXJyaWVycyBsaWUgaW4gYWNjZXNzaW5nIHRoZSBkYXRhL2dhaW5pbmcgZGF0YSBwZXJtaXNzaW9ucy4NCiAgPGJyPjxicj4NCg0KIyMjIyMjICoqRXRoaWNhbCBDb25jZXJucyoqOiANCiAgU28gbWFueS4uLiBFc3BlY2lhbGx5IHJlZ2FyZGluZyBwdWJsaWMgY29sbGVnZXMgYW5kIHVuaXZlcnNpdGllcyAodmVyc3VzIHByaXZhdGUgb3IgaW4NCiAgcmVndWxhciBidXNpbmVzcykuDQogIC0gQ291bGQgYmUgdXNlZCAoYW5kIGxpa2VseSBpcyB1c2VkKSB0byBvcHRpbWl6ZSBjb2xsZWdlIHNlbGVjdGlvbiBmb3Igc3R1ZGVudHMsIHJlZHVjaW5nDQogIG9wcG9ydHVuaXR5IGZvciBsb3ctb3Bwb3J0dW5pdHkgc3R1ZGVudHMgKHJhdGhlciB0aGFuIGhlbHBpbmcgdGhlbSkNCiAgLSBBbnRpY2lwYXRlZCBsYXdzdWl0cyBmb3Igc2VlbWluZyBkaXNjcmltaW5hdG9yeSBhZ2FpbnN0IGNlcnRhaW4gc2VnbWVudHMgaW4gYWRtaXNzaW9uDQogIHByb2Nlc3MNCi0tLQ0KDQoNCiMjIyBQcm9mZXNzb3IgUmF0aW5nIFJlZ3Jlc3Npb24NCiMjIyMjIyAqKkFwcGxpY2F0aW9uKio6DQogIEFwcGx5aW5nIE1hY2hpbmUgTGVhcm5pbmcgdG8gcHJlZGljdCB0aGUgYXZlcmFnZSByYXRpbmcgKG91dCBvZiA1KSBvZiBjb2xsZWdlIHByb2Zlc3NvcnMNCiAgZm9yIHRoZSBjbGFzc2VzIHRoZXkgdGVhY2g6PGJyPg0KDQogIC0gRGV0ZXJtaW5lIGNlcnRhaW4gY2xhc3NlcyBvciB0cmVuZHMgbGVhZGluZyB0byBoaWdoZXIgb3IgbG93ZXIgYXZnLiByYXRpbmdzDQogIC0gUHJvdmlkZSBzdHJhdGVnaWMgZmVlZGJhY2sgdG8gcHJvZmVzc29ycyB0byBpbmNyZWFzZSB0aGVpciByYXRpbmcgYW1vbmcgc3R1ZGVudHM8YnI+PGJyPg0KDQojIyMjIyMgKipTdXBlcnZpc2VkIG9yIFVuc3VwZXJ2aXNlZCoqOg0KICBTaW5jZSB0aGUgbW9kZWwgd291bGQgcHJlZGljdCBhIG51bWVyaWNhbCB2YWx1ZSB0aGF0IGNhbiBiZSBjaGVja2VkIGFnYWluc3QgYWN0dWFsIHJlc3VsdHMsDQogIHRoaXMgZmFsbHMgd2l0aGluIFN1cGVydmlzZWQgUmVncmVzc2lvbiB0ZXJyaXRvcnkuDQogIDxicj48YnI+DQogICAgDQojIyMjIyMgKipUYXJnZXQgVmFyaWFibGUqKjogDQogIFRoZSB0YXJnZXQgdmFyaWFibGUgaXMgQXZlcmFnZSBSYXRpbmcgb2YgYSBwcm9mZXNzb3IgYXQgdGhlIGVuZCBvZiBhIHNlbWVzdGVyIGZvciBhIGNsYXNzIDxicj48YnI+DQogIA0KIyMjIyMjICoqUG90ZW50aWFsIEZlYXR1cmUgVmFyaWFibGVzKio6DQogIC0gTnVtYmVyIG9mIHN0dWRlbnRzIGluIGNsYXNzDQogIC0gQ2xhc3MgbmFtZQ0KICAtIERlcGFydG1lbnQNCiAgLSBPbmxpbmUvSW4gUGVyc29uDQogIC0gVGVudXJlIChZL04pDQogIC0gWWVhcnMgb2YgZXhwZXJpZW5jZQ0KICA8YnI+DQogIA0KIyMjIyMjICoqRGF0YSBDb2xsZWN0aW9uKio6IA0KICBBZ2FpbiwgY29sbGVnZXMgYWxyZWFkeSBtYWludGFpbiBpbmZvcm1hdGlvbiBhYm91dCBjbGFzc2VzIGFuZCBwcm9mZXNzb3JzLCBpbmNsdWRpbmcNCiAgcHJvZmVzc29yIHJhdGluZ3MgYnkgc2VtZXN0ZXIuDQogIDxicj48YnI+DQoNCiMjIyMjIyAqKkV0aGljYWwgQ29uY2VybnMqKjogDQogIFRoZSBiaWdnZXN0IGV0aGljYWwgY29uY2VybiBsaWVzIGluIHByb2Zlc3NvcnMgdXRpbGl6aW5nIHRoaXMgbW9kZWwgdG8gY2hhbmdlIHRoZWlyIA0KICB0ZWFjaGluZyBzdHlsZSBmb3IgYmV0dGVyIHJhdGluZ3MgKGFuZCBoaWdoZXIgcGF5KSB3aXRob3V0IGNvbnNpZGVyaW5nIHRoZSB2YWx1ZSB0aGF0DQogIHRoZXkgYnJpbmcgdG8gdGhlaXIgc3R1ZGVudHMgKEFLQSBvcHRpbWl6aW5nIGZvciByYXRpbmcsIHJhdGhlciB0aGFuIHRlYWNoaW5nKS4NCi0tLQ0KDQoNCiMjIyBDbHVzdGVyaW5nIFNvbmdzIGZvciBRdWV1ZQ0KIyMjIyMjICoqQXBwbGljYXRpb24qKjoNCiAgQXBwbHlpbmcgTWFjaGluZSBMZWFybmluZyB0byBhc2NlcnRhaW4gY2x1c3RlcnMgb2Ygc29uZ3MgdG8gYmUgcGxhY2VkIGluIHRoZSBxdWV1ZQ0KICBvZiBhIHVzZXIncyBtdXNpYyBzdHJlYW1pbmcgYXBwOjxicj4NCg0KICAtIEJldHRlciBxdWV1ZSByZWNvbW1lbmRhdGlvbnMvY2x1c3RlcnMgbGVhZCB0byBoaWdoZXIgY3VzdG9tZXIgc2F0aXNmYWN0aW9uDQogIDxicj48YnI+DQoNCiMjIyMjIyAqKlN1cGVydmlzZWQgb3IgVW5zdXBlcnZpc2VkKio6DQogIC0gU2luY2UgdGhpcyBtb2RlbCBhaW1zIHRvIGdyb3VwIHNvbmdzIGJhc2VkIG9uIHVzZXIgaW5mb3JtYXRpb24sIGFuZCBzaW5jZSB0aGVyZSBpcyBubw0KICB0cnVlIGFuc3dlciB0byBtZWFzdXJlIGFnYWluc3QsIHRoaXMgc2VydmVzIGFzIGFuIFVuc3VwZXJ2aXNlZCBDbHVzdGVyaW5nIG1vZGVsLiANCiAgPGJyPjxicj4NCiAgICANCiMjIyMjIyAqKlRhcmdldCBWYXJpYWJsZSoqOiANCiAgVW5zdXBlcnZpc2VkIG1vZGVscyBkbyBub3QgdXNlIHRhcmdldCB2YXJpYWJsZXMgPGJyPjxicj4NCiAgDQojIyMjIyMgKipQb3RlbnRpYWwgRmVhdHVyZSBWYXJpYWJsZXMqKjoNCiAgLSBUaW1lIG9mIGRheQ0KICAtIERheSBvZiB3ZWVrL21vbnRoDQogIC0gUHJldmlvdXMgc29uZ3MgcXVldWVkL3BsYXllZCBieSB1c2VyDQogIC0gTGlrZWQgYXJ0aXN0cw0KICAtIExpa2VkIGFsYnVtcy9tdXNpYyB0eXBlcyAgDQogIDxicj4NCiAgDQojIyMjIyMgKipEYXRhIENvbGxlY3Rpb24qKjogDQogIElkZWFsbHksIHRoaXMgd291bGQgYmUgZGV2ZWxvcGVkIGFzIGFuIGVtcGxveWVlIHdpdGhpbiBvbmUgb2YgdGhlc2UgY29tcGFuaWVzLiBTdHJlYW1pbmcNCiAgYXBwcyBjb2xsZWN0IGV4dGVuc2l2ZSB1c2VyIGRhdGEsIHdoaWNoIGNhbiBiZSBsZXZlcmFnZWQgd2l0aGluIHRoZSBtb2RlbC4NCiAgPGJyPjxicj4NCg0KIyMjIyMjICoqRXRoaWNhbCBDb25jZXJucyoqOiANCiAgV2hpbGUgdXNlcnMgYWdyZWUgdG8gZGF0YSBjb2xsZWN0aW9uIHdpdGhpbiB0aGUgdGVybXMgb2YgdXNlIG9mIHN0cmVhbWluZyBhcHBzLCB0aGV5IHN0aWxsDQogIGZpbmQgZGlyZWN0IHVzZSBvZiB0aGVpciBpbmZvcm1hdGlvbiBvZmYtcHV0dGluZy4gTm90IHNvIG11Y2ggYW4gZXRoaWNhbCBjb25jZXJuLCBidXQgYSANCiAgY29uY2VybiBvZiB0aGUgYnJhbmQncyBQUiAodGhpbmsgb2YgdGhlIGZhbW91cyBUYXJnZXQgcHJlZ25hbmN5IG1vZGVsKQ0KDQotLS0NCg0KDQojIyMgRGltZW5zaW9uIFJlZHVjdGlvbiBmb3IgQ3VzdG9tZXIgUHJvZmlsaW5nDQojIyMjIyMgKipBcHBsaWNhdGlvbioqOg0KICBJIHdvcmsgaW4gYW4gaW5zdXJhbmNlIGNvbXBhbnksIHdoZXJlIGN1c3RvbWVyIGluZm9ybWF0aW9uIGluY2x1ZGVzIG92ZXIgb25lIGh1bmRyZWQgdmFyaWFibGVzLg0KICBUaGlzIG1vZGVsIHJlZHVjZXMgdGhlICMgb2YgdmFyaWFibGVzIHRvIGJlIGNvbnNpZGVyZWQgZm9yIGN1c3RvbWVyIHByb2ZpbGluZyB3aXRoaW4gdGhlIGJ1c2luZXNzLjo8YnI+DQoNCiAgLSBJZGVudGlmeSBhbmQgc3RyaXAgYXdheSBjdXN0b21lciBmZWF0dXJlcyBkZWVtZWQgdW5pbXBvcnRhbnQNCiAgLSBTaW1wbGlmeSBjdXN0b21lciBwcm9maWxpbmcgZm9yIGZ1dHVyZSBtb2RlbGluZzxicj48YnI+DQoNCiMjIyMjIyAqKlN1cGVydmlzZWQgb3IgVW5zdXBlcnZpc2VkKio6DQogIE5vIHRhcmdldCB2YXJpYWJsZSBhbmQgbm8gdHJ1ZSBhbnN3ZXIsIHRoaXMgd291bGQgYmUgYW4gVW5zdXBlcnZpc2VkIERpbWVuc2lvbiBSZWR1Y3Rpb24gbW9kZWwuDQogIDxicj48YnI+DQogICAgDQojIyMjIyMgKipUYXJnZXQgVmFyaWFibGUqKjogDQogIE5vIHRhcmdldCB2YXJpYWJsZQ0KICA8YnI+PGJyPg0KICANCiMjIyMjIyAqKlBvdGVudGlhbCBGZWF0dXJlIFZhcmlhYmxlcyoqOg0KDQpMaXRlcmFsbHkgKmh1bmRyZWRzKiBvZiB2YXJpYWJsZXMsIGluY2x1ZGluZy4uLg0KDQogIC0gQWdlDQogIC0gUXVvdGVkIEJvZGlseSBJbmp1cnkgTGltaXRlcw0KICAtIFByaW9yIEluc3VyYW5jZQ0KICAtIE51bWJlciBvZiBBdXRvbW9iaWxlcyB2cy4gIyBvZiBEcml2ZXJzIChhbHNvIHVzZWQgZm9yIGZyYXVkIGRldGVjdGlvbikNCiAgLSBHZW9ncmFwaGljIExvY2F0aW9uICANCiAgPGJyPg0KICANCiMjIyMjIyAqKkRhdGEgQ29sbGVjdGlvbioqOiANCiAgQXNzdW1pbmcgd29ya2luZyB3aXRoaW4gYW4gaW5zdXJhbmNlIGNvbXBhbnkuIERhdGEgY29sbGVjdGlvbiBhYmlsaXR5IGxpZXMgb24gdGhlIHVzZXIncyANCiAgYWJpbGl0eSB0byBnYWluIGFjY2VzcyB0byB0aGUgZGF0YSAob3IgZmluZCBwZW9wbGUgd2hvIGNhbiBtYWtlIGl0IGhhcHBlbikuIFRoaXMgY2FuIHRha2UgbW9udGhzLA0KICBkZXBlbmRpbmcgb24gdGhlIGRhdGEgcmVxdWlyZWQuDQogIDxicj48YnI+DQoNCiMjIyMjIyAqKkV0aGljYWwgQ29uY2VybnMqKjogDQogIEluc3VyYW5jZSBpcyBoZWF2aWx5IHJlZ3VsYXRlZCxhbmQgY3VzdG9tZXIgcHJvZmlsaW5nIG9mdGVuIGZpdHMgaW4gdGhlICJncmV5IiBhcmUgb2YgbGVnYWwNCiAgdnMgbm90IGxlZ2FsLi4NCiAgDQogIC0gV2l0aG91dCBnb2luZyBpbnRvIHRvbyBtdWNoIGRldGFpbCwgaG93IHRvIGJhbGFuY2UgcHJvZml0YWJpbGl0eSB2cyBtb3JhbGl0eT8gDQogIC0gV2hhdCdzIGdvb2QgZm9yIHRoZSB0b3RhbCBjdXN0b21lciBiYXNlIHZzLiBjZXJ0YWluIGN1c3RvbWVyIHNlZ21lbnRzPw0KICAtIFJlZCBsaW5pbmc6IDxodHRwczovL3d3dy5pbnN1cmFuY2UudXMvYXJ0aWNsZXMvYmxvZy93aGF0LWlzLXJlZGxpbmluZy13aXRoLWF1dG8taW5zdXJhbmNlPg0KDQotLS0NCg0KDQojIyBQYXJ0IDIgey50YWJzZXQgLnRhYnNldC1waWxsc30NCiMjIyBEYXRhIFNldHVwICYgTG9hZGluZw0KDQpTZXR0aW5nIGdsb2JhbCBjaHVuayBvcHRpb25zDQpgYGB7ciBHbG9iYWwgU2V0dXAsIHNldHVwLCBpbmNsdWRlPVRSVUV9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGNvbW1lbnQgPSAnJywgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDYsDQogIHdhcm5pbmcgPSBGQUxTRSwgZXJyb3IgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLA0KICBpbmNsdWRlID0gVFJVRSwgZWNobyA9IFRSVUUsIHN0cmlwLndoaXRlID0gVFJVRSwgaGlnaGxpZ2h0ID0gVFJVRSwgcmVzdWx0cyA9IFRSVUUNCikNCmBgYA0KDQoNCkxvYWRpbmcgTGlicmFyaWVzDQpgYGB7ciBMaWJyYXJpZXMsIHJlc3VsdHMgPSAnaGlkZSd9DQpwYWNrYWdlcyA9IGMoJ3RpZHl2ZXJzZScsJ3RpZHltb2RlbHMnLCdoZXJlJywna2tubicpDQpsYXBwbHkocGFja2FnZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCmBgYA0KDQojIyMgTGFiIFF1ZXN0aW9ucw0KDQojIyMjIyMgKioxLiBJcyB0aGlzIGEgc3VwZXJ2aXNlZCBvciB1bnN1cGVydmlzZWQgbGVhcm5pbmcgcHJvYmxlbT8gV2h5PyoqDQogIC0gU2luY2UgdGhlIHRhcmdldCB2YXJpYWJsZSBpcyBjb250YWluZWQgd2l0aGluIHRoZSBkYXRhLCBhbmQgc2luY2UgdGhlIG1vZGVsIGFpbXMgdG8gcHJlZGljdA0KICAgIGEgbnVtZXJpY2FsIHZhbHVlLCB0aGlzIGlzIGEgU3VwZXJ2aXNlZCBSZWdyZXNzaW9uIG1vZGVsDQogICAgPGJyPjxicj4NCiAgICANCiMjIyMjIyAqKjIuIFRoZXJlIGFyZSAxNiB2YXJpYWJsZXMgaW4gdGhpcyBkYXRhIHNldC4gV2hpY2ggdmFyaWFibGUgaXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGFuZCB3aGljaCB2YXJpYWJsZXMgYXJlIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIChha2EgZmVhdHVyZXMpPyoqDQoNCiAgLSAqVGFyZ2V0IFZhcjoqIGNtZWR2IChNZWRpYW4gdmFsdWUgb2Ygb3duZXItb2NjdXBpZWQgaG9tZXMgaW4gVVNEIDEsMDAwJ3MpDQogIC0gKlByZWRpY3RvciBWYXJzOiogIA0KICAgIC0gbG9uOiBsb25naXR1ZGUgb2YgY2Vuc3VzIHRyYWN0DQogICAgLSBsYXQ6IGxhdGl0dWRlIG9mIGNlbnN1cyB0cmFjdA0KICAgIC0gY3JpbTogcGVyIGNhcGl0YSBjcmltZSByYXRlIGJ5IHRvd24NCiAgICAtIHpuOiBwcm9wb3J0aW9uIG9mIHJlc2lkZW50aWFsIGxhbmQgem9uZWQgZm9yIGxvdHMgb3ZlciAyNSwwMDAgc3EuZnQNCiAgICAtIGluZHVzOiBwcm9wb3J0aW9uIG9mIG5vbi1yZXRhaWwgYnVzaW5lc3MgYWNyZXMgcGVyIHRvd24NCiAgICAtIGNoYXM6IENoYXJsZXMgUml2ZXIgZHVtbXkgdmFyaWFibGUgKD0gMSBpZiB0cmFjdCBib3VuZHMgcml2ZXI7IDAgb3RoZXJ3aXNlKQ0KICAgIC0gbm94OiBuaXRyaWMgb3hpZGVzIGNvbmNlbnRyYXRpb24gKHBhcnRzIHBlciAxMCBtaWxsaW9uKSDigJM+IGFrYSBhaXIgcG9sbHV0aW9uDQogICAgLSBybTogYXZlcmFnZSBudW1iZXIgb2Ygcm9vbXMgcGVyIGR3ZWxsaW5nDQogICAgLSBhZ2U6IHByb3BvcnRpb24gb2Ygb3duZXItb2NjdXBpZWQgdW5pdHMgYnVpbHQgcHJpb3IgdG8gMTk0MA0KICAgIC0gZGlzOiB3ZWlnaHRlZCBkaXN0YW5jZXMgdG8gZml2ZSBCb3N0b24gZW1wbG95bWVudCBjZW50ZXJzDQogICAgLSByYWQ6IGluZGV4IG9mIGFjY2Vzc2liaWxpdHkgdG8gcmFkaWFsIGhpZ2h3YXlzDQogICAgLSB0YXg6IGZ1bGwtdmFsdWUgcHJvcGVydHktdGF4IHJhdGUgcGVyIFVTRCAxMCwwMDANCiAgICAtIHB0cmF0aW86IHB1cGlsLXRlYWNoZXIgcmF0aW8gYnkgdG93bg0KICAgIC0gbHN0YXQ6IHBlcmNlbnRhZ2Ugb2YgbG93ZXIgc3RhdHVzIG9mIHRoZSBwb3B1bGF0aW9uDQoNCjxicj4NCg0KIyMjIyMjICoqMy4gR2l2ZW4gdGhlIHR5cGUgb2YgdmFyaWFibGUgY21lZHYgaXMsIGlzIHRoaXMgYSByZWdyZXNzaW9uIG9yIGNsYXNzaWZpY2F0aW9uIHByb2JsZW0/KioNCi0gQXMgc3RhdGVkIGJlZm9yZSwgdGhpcyBpcyBhIFJlZ3Jlc3Npb24gcHJvYmxlbQ0KDQo8YnI+DQoNCiMjIyMjIyAqKjQuIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBpbXBvcnQgdGhlIEJvc3RvbiBob3VzaW5nIGRhdGEgc2V0IChib3N0b24uY3N2KS4gQXJlIHRoZXJlIGFueSBtaXNzaW5nIHZhbHVlcz8gV2hhdCBpcyB0aGUgbWluaW11bSBhbmQgbWF4aW11bSB2YWx1ZXMgb2YgY21lZHY/IFdoYXQgaXMgdGhlIGF2ZXJhZ2UgY21lZHYgdmFsdWU/KioNCmBgYHtyIExvYWQgRGF0YSwgcmVzdWx0cyA9ICdoaWRlJ30NCnBhdGggPC0gaGVyZSgnRGF0YScsJ2Jvc3Rvbi5jc3YnKQ0KYm9zdG9uIDwtIHJlYWRyOjpyZWFkX2NzdihwYXRoKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChib3N0b24pDQpgYGANCg0KPGJyPjxicj4NCg0KIyMjIyMjICoqNS4gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIHNwbGl0IHRoZSBkYXRhIGludG8gYSB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0IHVzaW5nIGEgNzAtMzAlIHNwbGl0LiBCZSBzdXJlIHRvIGluY2x1ZGUgdGhlIHNldC5zZWVkKDEyMykgc28gdGhhdCB5b3VyIHRyYWluIGFuZCB0ZXN0IHNldHMgYXJlIHRoZSBzYW1lIHNpemUgYXMgbWluZS4qKg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpzcGxpdCA8LSBpbml0aWFsX3NwbGl0KGJvc3RvbiwgcHJvcCA9IDAuNywgc3RyYXRhID0gY21lZHYpDQp0cmFpbiA8LSB0cmFpbmluZyhzcGxpdCkNCnRlc3QgPC0gdGVzdGluZyhzcGxpdCkNCmBgYA0KDQo8YnI+PGJyPg0KDQojIyMjIyMgKio2LiBIb3cgbWFueSBvYnNlcnZhdGlvbnMgYXJlIGluIHRoZSB0cmFpbmluZyBzZXQgYW5kIHRlc3Qgc2V0PyoqDQpUaGVyZSBhcmUgKipgciBucm93KHRyYWluKWAqKiBvYnNlcnZhdGlvbnMgaW4gdGhlICJ0cmFpbiIgZGF0YSBzZXQgYW5kICoqYHIgbnJvdyh0ZXN0KWAqKiBvYnNlcnZhdGlvbnMgDQppbiB0aGUgInRlc3QiIGRhdGEgc2V0Lg0KPGJyPjxicj4NCg0KIyMjIyMjICoqNy4gQ29tcGFyZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGNtZWR2IGJldHdlZW4gdGhlIHRyYWluaW5nIHNldCBhbmQgdGVzdCBzZXQuIERvIHRoZXkgYXBwZWFyIHRvIGhhdmUgdGhlIHNhbWUgZGlzdHJpYnV0aW9uIG9yIGRvIHRoZXkgZGlmZmVyIHNpZ25pZmljYW50bHk/KioNCmBgYHtyfQ0KdHJhaW4gJT4lIA0KICBtdXRhdGUoaWQgPSAndHJhaW4nKSAlPiUgDQogIGJpbmRfcm93cyh0ZXN0ICU+JSBtdXRhdGUoaWQgPSAndGVzdCcpKSAlPiUNCiAgZ2dwbG90KGFlcyhjbWVkdiwgY29sb3IgPSBpZCkpICsNCiAgZ2VvbV9kZW5zaXR5KCkNCmBgYA0KDQo8YnI+PGJyPg0KDQojIyMjIyMgKio4LiBGaWxsIGluIHRoZSBibGFua3MgdG8gZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgdGhlIHJtIGZlYXR1cmUgdmFyaWFibGUgdG8gcHJlZGljdCBjbWVkdiBhbmQgY29tcHV0ZSB0aGUgUk1TRSBvbiB0aGUgdGVzdCBkYXRhLiBXaGF0IGlzIHRoZSB0ZXN0IHNldCBSTVNFPyoqDQpgYGB7cn0NCiMgZml0IG1vZGVsDQpsbTEgPC0gbGluZWFyX3JlZygpICU+JQ0KICBmaXQoIGNtZWR2IH4gcm0sIGRhdGEgPSB0cmFpbikNCg0KIyBjb21wdXRlIHRoZSBSTVNFIG9uIHRoZSB0ZXN0IGRhdGENCnByZDEgPC0gbG0xICU+JQ0KICBwcmVkaWN0KHRlc3QpICU+JQ0KICBiaW5kX2NvbHModGVzdCAlPiUgc2VsZWN0KGNtZWR2KSkgJT4lDQogIHJtc2UodHJ1dGggPSBjbWVkdiwgZXN0aW1hdGUgPSAucHJlZCkNCg0KcHJkMQ0KYGBgDQpUaGUgdGVzdCBzZXQgUk1TRSBpcyAqKiQ2LDgzMDEqKiB3aGVuIHVzaW5nIHJtIChhdmVyYWdlIG51bWJlciBvZiByb29tcykgdG8gZXN0aW1hdGUgY21lZHYNCg0KPGJyPjxicj4NCg0KIyMjIyMjICoqOS4gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIGZpdCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIGFsbCBhdmFpbGFibGUgZmVhdHVyZXMgdG8gcHJlZGljdCBjbWVkdiBhbmQgY29tcHV0ZSB0aGUgUk1TRSBvbiB0aGUgdGVzdCBkYXRhLiBXaGF0IGlzIHRoZSB0ZXN0IHNldCBSTVNFPyBJcyB0aGlzIGJldHRlciB0aGFuIHRoZSBwcmV2aW91cyBtb2RlbOKAmXMgcGVyZm9ybWFuY2U/KioNCmBgYHtyfQ0KIyBmaXQgbW9kZWwNCmZlYXR1cmVzIDwtIGNvbG5hbWVzKGJvc3RvbltuYW1lcyhib3N0b24pICVpbiUgJ2NtZWR2JyA9PSBGQUxTRV0pDQoNCmxtMiA8LSBsaW5lYXJfcmVnKCkgJT4lDQogIGZpdCggY21lZHYgfiAuLCBkYXRhID0gdHJhaW5bLGMoImNtZWR2IixmZWF0dXJlcyldKQ0KDQojIGNvbXB1dGUgdGhlIFJNU0Ugb24gdGhlIHRlc3QgZGF0YQ0KbG0yICU+JQ0KICBwcmVkaWN0KHRlc3QpICU+JQ0KICBiaW5kX2NvbHModGVzdCAlPiUgc2VsZWN0KGNtZWR2KSkgJT4lDQogIHJtc2UodHJ1dGggPSBjbWVkdiwgZXN0aW1hdGUgPSAucHJlZCkNCmBgYA0KVGhlIHRlc3Qgc2V0IFJNU0UgaXMgKiokNCw4MjkqKiB3aGVuIHVzaW5nIGFsbCBmZWF0dXJlcyB0byBlc3RpbWF0ZSBjbWVkdi4gVGhpcyByZXByZXNlbnRzIGEgbmVhcmx5ICokMiwwMDAqIGluY3JlYXNlIGluIHRoZSBtb2RlbCdzIGFjY3VyYWN5LiANCjxicj48YnI+DQoNCiMjIyMjIyAqKjEwLiBGaXQgYSBLLW5lYXJlc3QgbmVpZ2hib3IgbW9kZWwgdGhhdCB1c2VzIGFsbCBhdmFpbGFibGUgZmVhdHVyZXMgdG8gcHJlZGljdCBjbWVkdiBhbmQgY29tcHV0ZSB0aGUgUk1TRSBvbiB0aGUgdGVzdCBkYXRhLiBXaGF0IGlzIHRoZSB0ZXN0IHNldCBSTVNFPyBJcyB0aGlzIGJldHRlciB0aGFuIHRoZSBwcmV2aW91cyB0d28gbW9kZWxz4oCZIHBlcmZvcm1hbmNlcz8qKg0KYGBge3J9DQojIGZpdCBtb2RlbA0Ka25uIDwtIG5lYXJlc3RfbmVpZ2hib3IoKSAlPiUNCiAgc2V0X2VuZ2luZSgia2tubiIpICU+JQ0KICBzZXRfbW9kZSgicmVncmVzc2lvbiIpICU+JQ0KICBmaXQoIGNtZWR2IH4gLiwgZGF0YSA9IHRyYWluWyxjKCJjbWVkdiIsZmVhdHVyZXMpXSkNCg0KIyBjb21wdXRlIHRoZSBSTVNFIG9uIHRoZSB0ZXN0IGRhdGENCmtubiAlPiUNCiAgcHJlZGljdCh0ZXN0KSAlPiUNCiAgYmluZF9jb2xzKHRlc3QgJT4lIHNlbGVjdChjbWVkdikpICU+JQ0KICBybXNlKHRydXRoID0gY21lZHYsIGVzdGltYXRlID0gLnByZWQpDQpgYGANClVzaW5nIEstTmVhcmVzdC1OZWlnaGJvciwgdGhlIHRlc3Qgc2V0IFJNU0UgaXMgKiokMywzNTcqKiAod2l0aCBhbGwgZmVhdHVyZXMgaW5jbHVkZWQpLiBUaGUga2tubiBtb2RlbHMgbWFpbnRhaW5zIGEgKiokMSw1MDAqKiBpbmNyZWFzZSBpbiBhY2N1cmFjeSBjb21wYXJlZCB0byBsaW5lYXIgcmVncmVzc2lvbiB1c2luZyBhbGwgZmVhdHVyZXMsIGFuZCBhICoqJDMsNTAwKiogaW5jcmVhc2UgaW4gYWNjcnVhY3kgY29tcGFyZWQgdG8gbGluZWFyIHJlZ3Jlc3Npb24gdXNpbmcgb25seSB0aGUgcm0gZmFjdG9yLiAgIA0KDQoNCg0KDQoNCg==