library(tidyverse)
library(openintro)
## Warning: package 'openintro' was built under R version 4.3.3
## Warning: package 'usdata' was built under R version 4.3.3
The file UniversalBank.rds contains data on 5000
customers. The data include customer demographic information (age,
income, etc.), the customer’s relationship with the bank (mortgage,
securities account, etc.), and the customer response to the last
personal loan campaign (Personal Loan). Among these 5000 customers, only
480 (= 9.6%) accepted the personal loan that was offered to them in the
earlier campaign.
Data Description:
| ID |
Customer ID |
| Age |
Customer’s age in completed years |
| Experience |
# years of professional experience |
| Income |
Annual income of the customer ($000) |
| ZIPCode |
Home Address ZIP code |
| Family |
Family size of the customer |
| CCAvg |
Avg. spending on credit cards per month ($000) |
| Education_1 |
Education Level = 1 if Undergrad; 0 otherwise |
| Education_2 |
Education Level = 1 if Graduate; 0 otherwise |
| Education_3 |
Education Level = 1 if Advanced/Professional; 0
otherwise |
| Mortgage |
Value of house mortgage if any. ($000) |
| PersonalLoan |
Did this customer accept the personal loan offered in
the last campaign? |
| SecuritiesAccount |
Does the customer have a securities account with the
bank? |
| CDAccount |
Does the customer have a certificate of deposit (CD)
account with the bank? |
| Online |
Does the customer use internet banking facilities? |
| CreditCard |
Does the customer use a credit card issued by
UniversalBank? |
Question 1
Read and preprocess the data:
- Read the data file and assign into an
R object.
- Drop ID and ZIPcode from the dataset.
- Convert binary categorical variables into factor variables.
- Handle missing values, if there is any.
- Normalize all numerical variables.
Answer to Question 1
# Inserting the code here
bank_data <- readRDS("~/Documents/UNH/Semester-3/SupervisedMachineLearning/test/UniversalBank.rds")
# Dropping 'ID' and 'ZIPCode'
bank_data <- bank_data %>% select(-ID, -ZIPCode)
# Converting binary categorical variables to factors
binary_vars <- c("PersonalLoan", "SecuritiesAccount", "CDAccount", "Online", "CreditCard")
bank_data[binary_vars] <- lapply(bank_data[binary_vars], factor)
# Checking for missing values
sum(is.na(bank_data))
## [1] 0
# Removing rows with missing values if any exist
bank_data <- na.omit(bank_data)
# Identifying numerical variables
numerical_vars <- c("Age", "Experience", "Income", "Family", "CCAvg", "Mortgage")
# Normalizing the numerical variables to scale between 0 and 1
bank_data[numerical_vars] <- lapply(bank_data[numerical_vars], function(x) (x - min(x)) / (max(x) - min(x)))
Question 2
Partition the data into training (60%) and testing (40%) sets. Use
top 60% as training sample and remaining as test data.
Answer to Question 2
# Insert the code here
# Setingthe seed for reproducibility
set.seed(123)
# Defining the split index (60% training data)
train_indices <- sample(1:nrow(bank_data), size = 0.6 * nrow(bank_data))
# Splitting the data
train_data <- bank_data[train_indices, ] # 60% training data
test_data <- bank_data[-train_indices, ] # 40% testing data
# Checking the size of train and test data
nrow(train_data) # Should be 60% of the original data
## [1] 3000
nrow(test_data) # Should be 40% of the original data
## [1] 2000
Question 3
Perform a k-NN classification with all predictors except ID and ZIP
code using:
a. k = 1.
b. k = 2.
c. k = 3.
d. k = 4.
For each k=1,2,3,4, compute accuracy based on test dataset. Whick k
is the best?
Answer to Question 3
# Insert the code here
# Loading the necessary package
library(class)
# Define the target variable (PersonalLoan) for both training and test datasets
train_labels <- train_data$PersonalLoan
test_labels <- test_data$PersonalLoan
# Exclude the target variable from training and test datasets (use only predictors)
train_predictors <- train_data %>% select(-PersonalLoan)
test_predictors <- test_data %>% select(-PersonalLoan)
# Convert to matrix for knn function
train_matrix <- as.matrix(train_predictors)
test_matrix <- as.matrix(test_predictors)
# Function to compute accuracy
compute_accuracy <- function(k) {
# Perform k-NN classification
knn_predictions <- knn(train_matrix, test_matrix, cl = train_labels, k = k)
# Calculate accuracy
accuracy <- sum(knn_predictions == test_labels) / length(test_labels)
return(accuracy)
}
# Compute accuracy for k = 1, 2, 3, 4
accuracy_k1 <- compute_accuracy(1)
accuracy_k2 <- compute_accuracy(2)
accuracy_k3 <- compute_accuracy(3)
accuracy_k4 <- compute_accuracy(4)
# Print the accuracies
accuracy_k1 # Accuracy for k = 1
## [1] 0.962
accuracy_k2 # Accuracy for k = 2
## [1] 0.9565
accuracy_k3 # Accuracy for k = 3
## [1] 0.96
accuracy_k4 # Accuracy for k = 4
## [1] 0.955
Question 4
Consider the following customer:
Age = 40, Experience = 10, Income = 84, Family = 2, CCAvg = 2,
Education_1 = 0, Education_2 = 1, Education_3 = 0, Mortgage = 0,
SecuritiesAccount = 0, CDAccount = 0, Online = 1, and CreditCard =
1.
Perform a k-NN classification with all predictors except ID and ZIP
code using th best k. How would this customer be classified?
Answer to Question 4
# Insert the yor code here
# Defining the new customer data
new_customer <- data.frame(
Age = 40,
Experience = 10,
Income = 84,
Family = 2,
CCAvg = 2,
Education_1 = 0,
Education_2 = 1,
Education_3 = 0,
Mortgage = 0,
SecuritiesAccount = 0,
CDAccount = 0,
Online = 1,
CreditCard = 1
)
# Normalizing the new customer data using the same scaling as the training data
normalize <- function(x, min_val, max_val) {
return ((x - min_val) / (max_val - min_val))
}
# Normalizing the new customer's numeric values based on the training data's range
numerical_vars <- c("Age", "Experience", "Income", "Family", "CCAvg", "Mortgage")
for (var in numerical_vars) {
new_customer[[var]] <- normalize(new_customer[[var]], min(train_data[[var]]), max(train_data[[var]]))
}
# Converting categorical variables of the new customer to factors (to match the training data)
binary_vars <- c("SecuritiesAccount", "CDAccount", "Online", "CreditCard", "Education_1", "Education_2", "Education_3")
new_customer[binary_vars] <- lapply(new_customer[binary_vars], factor)
# Performing k-NN classification for the new customer (using the best k = 1 from previous analysis)
knn_prediction <- knn(train_matrix, as.matrix(new_customer), cl = train_labels, k = 1)
# Printing the predicted class for the new customer
knn_prediction
## [1] 1
## Levels: 0 1
LS0tCnRpdGxlOiAiRUNPTiAzMjAwOiBIb21ld29yayAxIgphdXRob3I6ICJTYXR5YSBOYXJheWFuYSBQYW5kYSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IG9wZW5pbnRybzo6bGFiX3JlcG9ydAotLS0KCmBgYHtyIGxvYWQtcGFja2FnZXMsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG9wZW5pbnRybykKbGlicmFyeShkcGx5cikKYGBgCgpUaGUgZmlsZSBgVW5pdmVyc2FsQmFuay5yZHNgIGNvbnRhaW5zIGRhdGEgb24gNTAwMCBjdXN0b21lcnMuIFRoZSBkYXRhIGluY2x1ZGUgY3VzdG9tZXIgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24gKGFnZSwgaW5jb21lLCBldGMuKSwgdGhlIGN1c3RvbWVy4oCZcyByZWxhdGlvbnNoaXAgd2l0aCB0aGUgYmFuayAobW9ydGdhZ2UsIHNlY3VyaXRpZXMgYWNjb3VudCwgZXRjLiksIGFuZCB0aGUgY3VzdG9tZXIgcmVzcG9uc2UgdG8gdGhlIGxhc3QgcGVyc29uYWwgbG9hbiBjYW1wYWlnbiAoUGVyc29uYWwgTG9hbikuIEFtb25nIHRoZXNlIDUwMDAgY3VzdG9tZXJzLCBvbmx5IDQ4MCAoPSA5LjYlKSBhY2NlcHRlZCB0aGUgcGVyc29uYWwgbG9hbiB0aGF0IHdhcyBvZmZlcmVkIHRvIHRoZW0gaW4gdGhlIGVhcmxpZXIgY2FtcGFpZ24uCgpEYXRhIERlc2NyaXB0aW9uOgoKfCBWYXJpYWJsZSAgICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgSUQgICAgICAgICAgICAgICAgICB8IEN1c3RvbWVyIElEICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBBZ2UgICAgICAgICAgICAgICAgIHwgQ3VzdG9tZXIncyBhZ2UgaW4gY29tcGxldGVkIHllYXJzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IEV4cGVyaWVuY2UgICAgICAgICAgfCAjIHllYXJzIG9mIHByb2Zlc3Npb25hbCBleHBlcmllbmNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgSW5jb21lICAgICAgICAgICAgICB8IEFubnVhbCBpbmNvbWUgb2YgdGhlIGN1c3RvbWVyICgkMDAwKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBaSVBDb2RlICAgICAgICAgICAgIHwgSG9tZSBBZGRyZXNzIFpJUCBjb2RlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IEZhbWlseSAgICAgICAgICAgICAgfCBGYW1pbHkgc2l6ZSBvZiB0aGUgY3VzdG9tZXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgQ0NBdmcgICAgICAgICAgICAgICB8IEF2Zy4gc3BlbmRpbmcgb24gY3JlZGl0IGNhcmRzIHBlciBtb250aCAoJDAwMCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBFZHVjYXRpb25fMSAgICAgICAgIHwgRWR1Y2F0aW9uIExldmVsID0gMSBpZiBVbmRlcmdyYWQ7IDAgb3RoZXJ3aXNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IEVkdWNhdGlvbl8yICAgICAgICAgfCBFZHVjYXRpb24gTGV2ZWwgPSAxIGlmIEdyYWR1YXRlOyAwIG90aGVyd2lzZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgRWR1Y2F0aW9uXzMgICAgICAgICB8IEVkdWNhdGlvbiBMZXZlbCA9IDEgaWYgQWR2YW5jZWQvUHJvZmVzc2lvbmFsOyAwIG90aGVyd2lzZSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBNb3J0Z2FnZSAgICAgICAgICAgIHwgVmFsdWUgb2YgaG91c2UgbW9ydGdhZ2UgaWYgYW55LiAoJDAwMCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IFBlcnNvbmFsTG9hbiAgICAgICAgfCBEaWQgdGhpcyBjdXN0b21lciBhY2NlcHQgdGhlIHBlcnNvbmFsIGxvYW4gb2ZmZXJlZCBpbiB0aGUgbGFzdCBjYW1wYWlnbj8gICAgICAgICAgICB8CnwgU2VjdXJpdGllc0FjY291bnQgICB8IERvZXMgdGhlIGN1c3RvbWVyIGhhdmUgYSBzZWN1cml0aWVzIGFjY291bnQgd2l0aCB0aGUgYmFuaz8gICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBDREFjY291bnQgICAgICAgICAgIHwgRG9lcyB0aGUgY3VzdG9tZXIgaGF2ZSBhIGNlcnRpZmljYXRlIG9mIGRlcG9zaXQgKENEKSBhY2NvdW50IHdpdGggdGhlIGJhbms/ICAgICAgICAgfAp8IE9ubGluZSAgICAgICAgICAgICAgfCBEb2VzIHRoZSBjdXN0b21lciB1c2UgaW50ZXJuZXQgYmFua2luZyBmYWNpbGl0aWVzPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgQ3JlZGl0Q2FyZCAgICAgICAgICB8IERvZXMgdGhlIGN1c3RvbWVyIHVzZSBhIGNyZWRpdCBjYXJkIGlzc3VlZCBieSBVbml2ZXJzYWxCYW5rPyAgICAgICAgICAgICAgICAgICAgICAgIHwKCiMjIyBRdWVzdGlvbiAxCgpSZWFkIGFuZCBwcmVwcm9jZXNzIHRoZSBkYXRhOiAKCmEuIFJlYWQgdGhlIGRhdGEgZmlsZSBhbmQgYXNzaWduIGludG8gYW4gYFJgIG9iamVjdC4gIApiLiBEcm9wIElEIGFuZCBaSVBjb2RlIGZyb20gdGhlIGRhdGFzZXQuICAKYy4gQ29udmVydCBiaW5hcnkgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGludG8gZmFjdG9yIHZhcmlhYmxlcy4gIApkLiBIYW5kbGUgbWlzc2luZyB2YWx1ZXMsIGlmIHRoZXJlIGlzIGFueS4gIAplLiBOb3JtYWxpemUgYWxsIG51bWVyaWNhbCB2YXJpYWJsZXMuICAKCgojIyMgX0Fuc3dlciB0byBRdWVzdGlvbiAxXwoKYGBge3IgUTF9CiMgSW5zZXJ0aW5nIHRoZSBjb2RlIGhlcmUKCmJhbmtfZGF0YSA8LSByZWFkUkRTKCJ+L0RvY3VtZW50cy9VTkgvU2VtZXN0ZXItMy9TdXBlcnZpc2VkTWFjaGluZUxlYXJuaW5nL3Rlc3QvVW5pdmVyc2FsQmFuay5yZHMiKQoKIyBEcm9wcGluZyAnSUQnIGFuZCAnWklQQ29kZScKYmFua19kYXRhIDwtIGJhbmtfZGF0YSAlPiUgc2VsZWN0KC1JRCwgLVpJUENvZGUpCgojIENvbnZlcnRpbmcgYmluYXJ5IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB0byBmYWN0b3JzCmJpbmFyeV92YXJzIDwtIGMoIlBlcnNvbmFsTG9hbiIsICJTZWN1cml0aWVzQWNjb3VudCIsICJDREFjY291bnQiLCAiT25saW5lIiwgIkNyZWRpdENhcmQiKQoKYmFua19kYXRhW2JpbmFyeV92YXJzXSA8LSBsYXBwbHkoYmFua19kYXRhW2JpbmFyeV92YXJzXSwgZmFjdG9yKQoKIyBDaGVja2luZyBmb3IgbWlzc2luZyB2YWx1ZXMKc3VtKGlzLm5hKGJhbmtfZGF0YSkpCgojIFJlbW92aW5nIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBpZiBhbnkgZXhpc3QKYmFua19kYXRhIDwtIG5hLm9taXQoYmFua19kYXRhKQoKIyBJZGVudGlmeWluZyBudW1lcmljYWwgdmFyaWFibGVzCm51bWVyaWNhbF92YXJzIDwtIGMoIkFnZSIsICJFeHBlcmllbmNlIiwgIkluY29tZSIsICJGYW1pbHkiLCAiQ0NBdmciLCAiTW9ydGdhZ2UiKQoKIyBOb3JtYWxpemluZyB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyB0byBzY2FsZSBiZXR3ZWVuIDAgYW5kIDEKYmFua19kYXRhW251bWVyaWNhbF92YXJzXSA8LSBsYXBwbHkoYmFua19kYXRhW251bWVyaWNhbF92YXJzXSwgZnVuY3Rpb24oeCkgKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkpCgpgYGAKCgojIyMgUXVlc3Rpb24gMgoKUGFydGl0aW9uIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgKDYwJSkgYW5kIHRlc3RpbmcgKDQwJSkgc2V0cy4gVXNlIHRvcCA2MCUgYXMgdHJhaW5pbmcgc2FtcGxlIGFuZCByZW1haW5pbmcgYXMgdGVzdCBkYXRhLgoKIyMjIF9BbnN3ZXIgdG8gUXVlc3Rpb24gMl8KCmBgYHtyIFEyfQojIEluc2VydCB0aGUgY29kZSBoZXJlCgojIFNldGluZ3RoZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTIzKQoKIyBEZWZpbmluZyB0aGUgc3BsaXQgaW5kZXggKDYwJSB0cmFpbmluZyBkYXRhKQp0cmFpbl9pbmRpY2VzIDwtIHNhbXBsZSgxOm5yb3coYmFua19kYXRhKSwgc2l6ZSA9IDAuNiAqIG5yb3coYmFua19kYXRhKSkKCiMgU3BsaXR0aW5nIHRoZSBkYXRhCnRyYWluX2RhdGEgPC0gYmFua19kYXRhW3RyYWluX2luZGljZXMsIF0gICMgNjAlIHRyYWluaW5nIGRhdGEKdGVzdF9kYXRhIDwtIGJhbmtfZGF0YVstdHJhaW5faW5kaWNlcywgXSAgICMgNDAlIHRlc3RpbmcgZGF0YQoKIyBDaGVja2luZyB0aGUgc2l6ZSBvZiB0cmFpbiBhbmQgdGVzdCBkYXRhCm5yb3codHJhaW5fZGF0YSkgICMgU2hvdWxkIGJlIDYwJSBvZiB0aGUgb3JpZ2luYWwgZGF0YQpucm93KHRlc3RfZGF0YSkgICAjIFNob3VsZCBiZSA0MCUgb2YgdGhlIG9yaWdpbmFsIGRhdGEKCgpgYGAKCiMjIyBRdWVzdGlvbiAzCgpQZXJmb3JtIGEgay1OTiBjbGFzc2lmaWNhdGlvbiB3aXRoIGFsbCBwcmVkaWN0b3JzIGV4Y2VwdCBJRCBhbmQgWklQIGNvZGUgdXNpbmc6ICAKYS4gayA9IDEuICAKYi4gayA9IDIuICAKYy4gayA9IDMuICAKZC4gayA9IDQuICAgCgpGb3IgZWFjaCBrPTEsMiwzLDQsIGNvbXB1dGUgYWNjdXJhY3kgYmFzZWQgb24gdGVzdCBkYXRhc2V0LiBXaGljayBrIGlzIHRoZSBiZXN0PwoKIyMjIF9BbnN3ZXIgdG8gUXVlc3Rpb24gM18KCmBgYHtyIFEzfQojIEluc2VydCB0aGUgY29kZSBoZXJlCiMgTG9hZGluZyB0aGUgIG5lY2Vzc2FyeSBwYWNrYWdlCmxpYnJhcnkoY2xhc3MpCgojIERlZmluZSB0aGUgdGFyZ2V0IHZhcmlhYmxlIChQZXJzb25hbExvYW4pIGZvciBib3RoIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGFzZXRzCnRyYWluX2xhYmVscyA8LSB0cmFpbl9kYXRhJFBlcnNvbmFsTG9hbgp0ZXN0X2xhYmVscyA8LSB0ZXN0X2RhdGEkUGVyc29uYWxMb2FuCgojIEV4Y2x1ZGUgdGhlIHRhcmdldCB2YXJpYWJsZSBmcm9tIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGFzZXRzICh1c2Ugb25seSBwcmVkaWN0b3JzKQp0cmFpbl9wcmVkaWN0b3JzIDwtIHRyYWluX2RhdGEgJT4lIHNlbGVjdCgtUGVyc29uYWxMb2FuKQp0ZXN0X3ByZWRpY3RvcnMgPC0gdGVzdF9kYXRhICU+JSBzZWxlY3QoLVBlcnNvbmFsTG9hbikKCiMgQ29udmVydCB0byBtYXRyaXggZm9yIGtubiBmdW5jdGlvbgp0cmFpbl9tYXRyaXggPC0gYXMubWF0cml4KHRyYWluX3ByZWRpY3RvcnMpCnRlc3RfbWF0cml4IDwtIGFzLm1hdHJpeCh0ZXN0X3ByZWRpY3RvcnMpCgojIEZ1bmN0aW9uIHRvIGNvbXB1dGUgYWNjdXJhY3kKY29tcHV0ZV9hY2N1cmFjeSA8LSBmdW5jdGlvbihrKSB7CiAgIyBQZXJmb3JtIGstTk4gY2xhc3NpZmljYXRpb24KICBrbm5fcHJlZGljdGlvbnMgPC0ga25uKHRyYWluX21hdHJpeCwgdGVzdF9tYXRyaXgsIGNsID0gdHJhaW5fbGFiZWxzLCBrID0gaykKICAKICAjIENhbGN1bGF0ZSBhY2N1cmFjeQogIGFjY3VyYWN5IDwtIHN1bShrbm5fcHJlZGljdGlvbnMgPT0gdGVzdF9sYWJlbHMpIC8gbGVuZ3RoKHRlc3RfbGFiZWxzKQogIHJldHVybihhY2N1cmFjeSkKfQoKIyBDb21wdXRlIGFjY3VyYWN5IGZvciBrID0gMSwgMiwgMywgNAphY2N1cmFjeV9rMSA8LSBjb21wdXRlX2FjY3VyYWN5KDEpCmFjY3VyYWN5X2syIDwtIGNvbXB1dGVfYWNjdXJhY3koMikKYWNjdXJhY3lfazMgPC0gY29tcHV0ZV9hY2N1cmFjeSgzKQphY2N1cmFjeV9rNCA8LSBjb21wdXRlX2FjY3VyYWN5KDQpCgojIFByaW50IHRoZSBhY2N1cmFjaWVzCmFjY3VyYWN5X2sxICAjIEFjY3VyYWN5IGZvciBrID0gMQphY2N1cmFjeV9rMiAgIyBBY2N1cmFjeSBmb3IgayA9IDIKYWNjdXJhY3lfazMgICMgQWNjdXJhY3kgZm9yIGsgPSAzCmFjY3VyYWN5X2s0ICAjIEFjY3VyYWN5IGZvciBrID0gNAoKCmBgYAoKIyMjIFF1ZXN0aW9uIDQKCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgY3VzdG9tZXI6IAoKQWdlID0gNDAsIEV4cGVyaWVuY2UgPSAxMCwgSW5jb21lID0gODQsIEZhbWlseSA9IDIsIENDQXZnID0gMiwgRWR1Y2F0aW9uXzEgPSAwLCBFZHVjYXRpb25fMiA9IDEsIEVkdWNhdGlvbl8zID0gMCwgTW9ydGdhZ2UgPSAwLCBTZWN1cml0aWVzQWNjb3VudCA9IDAsIENEQWNjb3VudCA9IDAsIE9ubGluZSA9IDEsIGFuZCBDcmVkaXRDYXJkID0gMS4KClBlcmZvcm0gYSBrLU5OIGNsYXNzaWZpY2F0aW9uIHdpdGggYWxsIHByZWRpY3RvcnMgZXhjZXB0IElEIGFuZCBaSVAgY29kZSB1c2luZyB0aCBiZXN0IGsuIEhvdyB3b3VsZCB0aGlzIGN1c3RvbWVyIGJlIGNsYXNzaWZpZWQ/CgojIyMgX0Fuc3dlciB0byBRdWVzdGlvbiA0XwoKYGBge3IgUTR9CiMgSW5zZXJ0IHRoZSB5b3IgY29kZSBoZXJlCgojIERlZmluaW5nIHRoZSBuZXcgY3VzdG9tZXIgZGF0YQpuZXdfY3VzdG9tZXIgPC0gZGF0YS5mcmFtZSgKICBBZ2UgPSA0MCwKICBFeHBlcmllbmNlID0gMTAsCiAgSW5jb21lID0gODQsCiAgRmFtaWx5ID0gMiwKICBDQ0F2ZyA9IDIsCiAgRWR1Y2F0aW9uXzEgPSAwLAogIEVkdWNhdGlvbl8yID0gMSwKICBFZHVjYXRpb25fMyA9IDAsCiAgTW9ydGdhZ2UgPSAwLAogIFNlY3VyaXRpZXNBY2NvdW50ID0gMCwKICBDREFjY291bnQgPSAwLAogIE9ubGluZSA9IDEsCiAgQ3JlZGl0Q2FyZCA9IDEKKQoKIyBOb3JtYWxpemluZyB0aGUgbmV3IGN1c3RvbWVyIGRhdGEgdXNpbmcgdGhlIHNhbWUgc2NhbGluZyBhcyB0aGUgdHJhaW5pbmcgZGF0YQpub3JtYWxpemUgPC0gZnVuY3Rpb24oeCwgbWluX3ZhbCwgbWF4X3ZhbCkgewogIHJldHVybiAoKHggLSBtaW5fdmFsKSAvIChtYXhfdmFsIC0gbWluX3ZhbCkpCn0KCiMgTm9ybWFsaXppbmcgdGhlIG5ldyBjdXN0b21lcidzIG51bWVyaWMgdmFsdWVzIGJhc2VkIG9uIHRoZSB0cmFpbmluZyBkYXRhJ3MgcmFuZ2UKbnVtZXJpY2FsX3ZhcnMgPC0gYygiQWdlIiwgIkV4cGVyaWVuY2UiLCAiSW5jb21lIiwgIkZhbWlseSIsICJDQ0F2ZyIsICJNb3J0Z2FnZSIpCmZvciAodmFyIGluIG51bWVyaWNhbF92YXJzKSB7CiAgbmV3X2N1c3RvbWVyW1t2YXJdXSA8LSBub3JtYWxpemUobmV3X2N1c3RvbWVyW1t2YXJdXSwgbWluKHRyYWluX2RhdGFbW3Zhcl1dKSwgbWF4KHRyYWluX2RhdGFbW3Zhcl1dKSkKfQoKIyBDb252ZXJ0aW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBvZiB0aGUgbmV3IGN1c3RvbWVyIHRvIGZhY3RvcnMgKHRvIG1hdGNoIHRoZSB0cmFpbmluZyBkYXRhKQpiaW5hcnlfdmFycyA8LSBjKCJTZWN1cml0aWVzQWNjb3VudCIsICJDREFjY291bnQiLCAiT25saW5lIiwgIkNyZWRpdENhcmQiLCAiRWR1Y2F0aW9uXzEiLCAiRWR1Y2F0aW9uXzIiLCAiRWR1Y2F0aW9uXzMiKQpuZXdfY3VzdG9tZXJbYmluYXJ5X3ZhcnNdIDwtIGxhcHBseShuZXdfY3VzdG9tZXJbYmluYXJ5X3ZhcnNdLCBmYWN0b3IpCgojIFBlcmZvcm1pbmcgay1OTiBjbGFzc2lmaWNhdGlvbiBmb3IgdGhlIG5ldyBjdXN0b21lciAodXNpbmcgdGhlIGJlc3QgayA9IDEgZnJvbSBwcmV2aW91cyBhbmFseXNpcykKa25uX3ByZWRpY3Rpb24gPC0ga25uKHRyYWluX21hdHJpeCwgYXMubWF0cml4KG5ld19jdXN0b21lciksIGNsID0gdHJhaW5fbGFiZWxzLCBrID0gMSkKCiMgUHJpbnRpbmcgdGhlIHByZWRpY3RlZCBjbGFzcyBmb3IgdGhlIG5ldyBjdXN0b21lcgprbm5fcHJlZGljdGlvbgoKCmBgYAoK