Introduction
This report details an Exploratory Data Analysis (EDA) and perform
feature engineering on the bank loan defaults dataset.
Problem Statements
and Objectives
Based on the dataset, we aim to address two primary practical
questions:
Classification Problem: What are the key
characteristics of a loan applicant that influence their likelihood of
defaulting on a loan?
- Analytical Question: Can we build a model to
predict whether a customer will
Default
(1) or not
Default
(0)?
Regression Problem: What customer and loan
characteristics are associated with the total loan Amount
requested?
- Analytical Question: Can we build a model to
predict the loan
Amount
a customer will request?
Data Description
Data Source and
Collection
The data is from the BankLoanDefaultDataset.csv
file. It
is a dataset containing information on 1,000 loan applicants.
SOURCE: Book - Applied Analytics through Case Studies Using SAS and
R, Deepti Gupta by APress, ISBN - 978-1-4842-3525-6
Dataset Overview
The dataset contains 1,000 observations and 16 variables. We will
first load the data and inspect its structure.
# load dataset
setwd("/Users/jeffery/Library/Mobile Documents/com~apple~CloudDocs/Documents/Documents - jMacP/WCUPA/Classes/Fall 2025/STA551/Homework/Data")
loan_data <- read.csv("BankLoanDefaultDataset.csv")
# set dimensions for displaying variable definitions
cat("Dimensions:", dim(loan_data)[1], "rows and", dim(loan_data)[2], "columns\n")
Dimensions: 1000 rows and 16 columns
'data.frame': 1000 obs. of 16 variables:
$ Default : int 0 0 0 1 1 0 0 0 0 1 ...
$ Checking_amount : int 988 458 158 300 63 1071 -192 172 585 189 ...
$ Term : int 15 15 14 25 24 20 13 16 20 19 ...
$ Credit_score : int 796 813 756 737 662 828 856 763 778 649 ...
$ Gender : chr "Female" "Female" "Female" "Female" ...
$ Marital_status : chr "Single" "Single" "Single" "Single" ...
$ Car_loan : int 1 1 0 0 0 1 1 1 1 1 ...
$ Personal_loan : int 0 0 1 0 0 0 0 0 0 0 ...
$ Home_loan : int 0 0 0 0 0 0 0 0 0 0 ...
$ Education_loan : int 0 0 0 1 1 0 0 0 0 0 ...
$ Emp_status : chr "employed" "employed" "employed" "employed" ...
$ Amount : int 1536 947 1678 1804 1184 475 626 1224 1162 786 ...
$ Saving_amount : int 3455 3600 3093 2449 2867 3282 3398 3022 3475 2711 ...
$ Emp_duration : int 12 25 43 0 4 12 11 12 12 0 ...
$ Age : int 38 36 34 29 30 32 38 36 36 29 ...
$ No_of_credit_acc: int 1 1 1 1 1 2 1 1 1 1 ...
Variables
The dataset includes a target variable, Default
, and 15
predictor variables.
Default |
Target Variable: 1 for default, 0 for non-default |
Checking_amount |
Amount in the checking account |
Term |
Loan term in months |
Credit_score |
Applicant’s credit score |
Gender |
Applicant’s gender (Male/Female) |
Marital_status |
Applicant’s marital status (Single/Married) |
Car_loan |
1 if applicant has a car loan, 0 otherwise |
Personal_loan |
1 if applicant has a personal loan, 0 otherwise |
Home_loan |
1 if applicant has a home loan, 0 otherwise |
Education_loan |
1 if applicant has an education loan, 0 otherwise |
Emp_status |
Employment status (Employed/Unemployed) |
Amount |
The loan amount requested |
Saving_amount |
Amount in the savings account |
Emp_duration |
Employment duration in months |
Age |
Applicant’s age in years |
No_of_credit_acc |
Number of credit accounts |
Exploratory Data
Analysis and Feature Engineering
We will now perform EDA to clean the data, show variable
relationships and prepare for modeling.
Data Cleaning
Missing Values and
Duplicates
First we check for any missing values or duplicated rows.
# check for missing values
cat("Total missing values:", sum(is.na(loan_data)), "\n")
Total missing values: 0
# check for duplicated rows
cat("Total duplicated rows:", sum(duplicated(loan_data)), "\n")
Total duplicated rows: 0
The data is clean with no missing values or duplicate entries.
Inspecting Data
Issues
The Checking_amount
column has negative values which
likely represent overdrafts. To simplify, we set negative values to
0.
# Show summary to identify the issue
summary(loan_data$Checking_amount)
Min. 1st Qu. Median Mean 3rd Qu. Max.
-665.0 164.8 351.5 362.4 553.5 1319.0
# Create a copy to modify
loan_data_cleaned <- loan_data
# Use base R indexing to find negative values and set them to 0
loan_data_cleaned$Checking_amount[loan_data_cleaned$Checking_amount < 0] <- 0
cat("\nSummary of Checking_amount after correction:\n")
Summary of Checking_amount after correction:
summary(loan_data_cleaned$Checking_amount)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0 164.8 351.5 377.4 553.5 1319.0
Individual Variable
Analysis
We will visualize the distribution of individual variables using
basic plots.
Numerical Variable
Distributions
# Set up a 2x2 plotting area
par(mfrow = c(2, 2))
# Basic histograms
hist(loan_data_cleaned$Credit_score, main = "Distribution of Credit Score", xlab = "Credit Score", col = "lightblue")
hist(loan_data_cleaned$Age, main = "Distribution of Age", xlab = "Age (years)", col = "green")
hist(loan_data_cleaned$Amount, main = "Distribution of Loan Amount", xlab = "Amount", col = "pink")
hist(loan_data_cleaned$Saving_amount, main = "Distribution of Saving Amount", xlab = "Saving Amount", col = "yellow")
# Reset plotting area to default
par(mfrow = c(1, 1))
Observations: Credit_score
and
Age
are approximately normally distributed. Financial
variables like Amount
and Saving_amount
are
skewed to the right.
Categorical
Variable Distributions
# Set up a 2x2 plotting area
par(mfrow = c(2, 2))
# Basic bar plots
barplot(table(loan_data_cleaned$Default), main = "Loan Default Counts", col=c("darkgreen", "darkred"))
barplot(table(loan_data_cleaned$Gender), main = "Gender Distribution", col=c("pink", "lightblue"))
barplot(table(loan_data_cleaned$Marital_status), main = "Marital Status", col=c("orange", "purple"))
barplot(table(loan_data_cleaned$Emp_status), main = "Employment Status", col=c("gray", "brown"))
# Reset plotting area
par(mfrow = c(1, 1))
Observations: There are more non-defaulters (0) than
defaulters (1).
Relationships between
Variables
Now we explore relationships between variables using simple
comparisons.
Relationship with
Default
We use boxplots to compare numerical variables between the default
and non-default applicants.
# Set up a 1x2 plotting area
par(mfrow = c(1, 2))
# basic boxplots
boxplot(Credit_score ~ Default, data = loan_data_cleaned,
main = "Credit Score by Default",
xlab = "Default (0=No, 1=Yes)", ylab = "Credit Score",
col = c("darkgreen", "darkred"))
boxplot(Amount ~ Default, data = loan_data_cleaned,
main = "Loan Amount by Default",
xlab = "Default (0=No, 1=Yes)", ylab = "Loan Amount",
col = c("darkgreen", "darkred"))
# Reset plotting area
par(mfrow = c(1, 1))
Observations: Customers who defaulted tend to have
lower credit scores.
Relationship with
Amount
To explore predictors for the regression problem, we can see how
Amount
relates to categorical variables.
# Set up a 1x2 plotting area
par(mfrow = c(1, 2))
# boxplots comparing Amount against categorical variables
boxplot(Amount ~ Emp_status, data = loan_data_cleaned,
main = "Loan Amount by Employment Status",
xlab = "Employment Status", ylab = "Loan Amount",
col = c("gray", "brown"))
boxplot(Amount ~ Marital_status, data = loan_data_cleaned,
main = "Loan Amount by Marital Status",
xlab = "Marital Status", ylab = "Loan Amount",
col = c("orange", "purple"))
# Reset plotting area
par(mfrow = c(1, 1))
Observations: The requested loan amount does not
appear to differ significantly between these groups, suggesting they may
not be strong predictors on their own for the regression problem.
Creating the Analytical
Dataset
We will now perform feature engineering to create the final dataset
for modeling.
Steps:
- Convert Categorical Variables:
Gender
,
Marital_status
, and Emp_status
will be
converted into factors, then into numerical dummy variables.
- Standardize Numerical Variables: We will scale
numerical predictors to have a mean of 0 and a standard deviation of
1.
# Start with the cleaned data
analytical_data <- loan_data_cleaned
# 1. Convert character columns to factors
analytical_data$Gender <- as.factor(analytical_data$Gender)
analytical_data$Marital_status <- as.factor(analytical_data$Marital_status)
analytical_data$Emp_status <- as.factor(analytical_data$Emp_status)
# 2. Identify numeric predictors to be scaled
vars_to_scale <- c("Checking_amount", "Term", "Credit_score", "Amount",
"Saving_amount", "Emp_duration", "Age", "No_of_credit_acc")
scaled_data <- scale(analytical_data[, vars_to_scale])
colnames(scaled_data) <- paste0(vars_to_scale, "_scaled") # Add suffix to scaled columns
# 3. One-hot encode categorical variables using model.matrix
dummy_vars <- model.matrix(~ Gender + Marital_status + Emp_status - 1, data = analytical_data)
# 4. Combine everything into the final analytical dataset
binary_vars <- analytical_data[, c("Car_loan", "Personal_loan", "Home_loan", "Education_loan", "Default")]
analytical_data_final <- cbind(scaled_data, dummy_vars, binary_vars)
# Display the first few rows and structure of the final dataset
cat("First 6 rows of the final analytical dataset:\n")
First 6 rows of the final analytical dataset:
head(as.data.frame(analytical_data_final))
Checking_amount_scaled Term_scaled Credit_score_scaled Amount_scaled
1 2.2191098 -0.8686751 0.45805485 1.0378255
2 0.2930295 -0.8686751 0.67725070 -0.8885616
3 -0.7972046 -1.1772630 -0.05770008 1.5022517
4 -0.2811605 2.2172044 -0.30268368 1.9143481
5 -1.1424454 1.9086164 -1.26972418 -0.1134279
6 2.5207412 0.6742647 0.87065880 -2.4322878
Saving_amount_scaled Emp_duration_scaled Age_scaled No_of_credit_acc_scaled
1 0.8120577 -0.9901873 1.6591037 -0.9355765
2 1.2390938 -0.6459033 1.1704853 -0.9355765
3 -0.2540600 -0.1692024 0.6818669 -0.9355765
4 -2.1506893 -1.3079880 -0.5396790 -0.9355765
5 -0.9196473 -1.2020544 -0.2953698 -0.9355765
6 0.3025595 -0.9901873 0.1932486 -0.3304170
GenderFemale GenderMale Marital_statusSingle Emp_statusunemployed Car_loan
1 1 0 1 0 1
2 1 0 1 0 1
3 1 0 1 0 0
4 1 0 1 0 0
5 1 0 1 1 0
6 0 1 0 0 1
Personal_loan Home_loan Education_loan Default
1 0 0 0 0
2 0 0 0 0
3 1 0 0 0
4 0 0 1 1
5 0 0 1 1
6 0 0 0 0
Wrapping Feature
Engineering Code
To make these steps reusable, we can wrap them in a function.
create_analytical_features <- function(raw_data) {
# Step 1: Clean data
data_cleaned <- raw_data
data_cleaned$Checking_amount[data_cleaned$Checking_amount < 0] <- 0
# Step 2: Convert to factors
data_cleaned$Gender <- as.factor(data_cleaned$Gender)
data_cleaned$Marital_status <- as.factor(data_cleaned$Marital_status)
data_cleaned$Emp_status <- as.factor(data_cleaned$Emp_status)
# Step 3: Scale numerical predictors
vars_to_scale <- c("Checking_amount", "Term", "Credit_score", "Amount",
"Saving_amount", "Emp_duration", "Age", "No_of_credit_acc")
scaled_data <- scale(data_cleaned[, vars_to_scale])
colnames(scaled_data) <- paste0(vars_to_scale, "_scaled")
# Step 4: Create dummy variables
dummy_vars <- model.matrix(~ Gender + Marital_status + Emp_status - 1, data = data_cleaned)
# Step 5: Combine all parts
binary_vars <- data_cleaned[, c("Car_loan", "Personal_loan", "Home_loan", "Education_loan", "Default")]
final_data <- cbind(scaled_data, dummy_vars, binary_vars)
return(as.data.frame(final_data))
}
# Example of using the function
analytical_data_from_function <- create_analytical_features(loan_data)
cat("First 3 rows of data processed by the function:\n")
First 3 rows of data processed by the function:
head(analytical_data_from_function, 3)
Checking_amount_scaled Term_scaled Credit_score_scaled Amount_scaled
1 2.2191098 -0.8686751 0.45805485 1.0378255
2 0.2930295 -0.8686751 0.67725070 -0.8885616
3 -0.7972046 -1.1772630 -0.05770008 1.5022517
Saving_amount_scaled Emp_duration_scaled Age_scaled No_of_credit_acc_scaled
1 0.8120577 -0.9901873 1.6591037 -0.9355765
2 1.2390938 -0.6459033 1.1704853 -0.9355765
3 -0.2540600 -0.1692024 0.6818669 -0.9355765
GenderFemale GenderMale Marital_statusSingle Emp_statusunemployed Car_loan
1 1 0 1 0 1
2 1 0 1 0 1
3 1 0 1 0 0
Personal_loan Home_loan Education_loan Default
1 0 0 0 0
2 0 0 0 0
3 1 0 0 0
Conclusion and Next
Steps
This concludes our analysis of the bank loan defaults dataset. Key
findings include the importance of Credit_score
in
predicting defaults and the nature of the Default
variable.
We performed feature engineering to scale numerical predictors and
encode categorical variables, resulting in a clean dataset ready for
part II. The entire process was wrapped in a function to ensure it can
be reused consistently. The project is now prepared for Part II which
will focus on building and evaluating predictive models.
LS0tCnRpdGxlOiAnUHJvamVjdCBPbmU6IEVEQSBhbmQgRmVhdHVyZSBFbmdpbmVlcmluZyBmb3IgTG9hbiBEZWZhdWx0IFByZWRpY3Rpb24nCmF1dGhvcjogJ0plZmZlcnkgRGVsdmEnCmRhdGU6ICJPY3RvYmVyIDEsIDIwMjUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ193aWR0aDogOAogICAgZmlnX2hlaWdodDogNQogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvY19jb2xsYXBzZWQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHNtb290aF9zY3JvbGw6IHllcwogICAgdGhlbWU6IGx1bWVuCiAgICBoaWdobGlnaHQ6IHRhbmdvCi0tLQoKYGBge2NzcywgZWNobyA9IEZBTFNFfQpoMS50aXRsZSB7CiAgZm9udC1zaXplOiAyNHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5hdXRob3IsIGg0LmRhdGUgewogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CmgxIHsKICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoMiB7CiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LXdlaWdodDogYm9sZDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CmgzIHsKICAgIGZvbnQtc2l6ZTogMTZweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgewogIGNvbnRlbnQ6ICIuIjsKfQpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogICBsaWJyYXJ5KGtuaXRyKQp9Cgprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQogICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCiMgSW50cm9kdWN0aW9uCgpUaGlzIHJlcG9ydCBkZXRhaWxzIGFuIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkgYW5kIHBlcmZvcm0gZmVhdHVyZSBlbmdpbmVlcmluZyBvbiB0aGUgYmFuayBsb2FuIGRlZmF1bHRzIGRhdGFzZXQuCgojIyBQcm9ibGVtIFN0YXRlbWVudHMgYW5kIE9iamVjdGl2ZXMKCkJhc2VkIG9uIHRoZSBkYXRhc2V0LCB3ZSBhaW0gdG8gYWRkcmVzcyB0d28gcHJpbWFyeSBwcmFjdGljYWwgcXVlc3Rpb25zOgoKMS4gICoqQ2xhc3NpZmljYXRpb24gUHJvYmxlbToqKiBXaGF0IGFyZSB0aGUga2V5IGNoYXJhY3RlcmlzdGljcyBvZiBhIGxvYW4gYXBwbGljYW50IHRoYXQgaW5mbHVlbmNlIHRoZWlyIGxpa2VsaWhvb2Qgb2YgZGVmYXVsdGluZyBvbiBhIGxvYW4/CgogICAgICAqICoqQW5hbHl0aWNhbCBRdWVzdGlvbjoqKiBDYW4gd2UgYnVpbGQgYSAgbW9kZWwgdG8gIHByZWRpY3Qgd2hldGhlciBhIGN1c3RvbWVyIHdpbGwgYERlZmF1bHRgICgxKSBvciBub3QgYERlZmF1bHRgICgwKT8KCjIuICAqKlJlZ3Jlc3Npb24gUHJvYmxlbToqKiBXaGF0IGN1c3RvbWVyIGFuZCBsb2FuIGNoYXJhY3RlcmlzdGljcyBhcmUgYXNzb2NpYXRlZCB3aXRoIHRoZSB0b3RhbCBsb2FuIGBBbW91bnRgIHJlcXVlc3RlZD8KCiAgICAgICogKipBbmFseXRpY2FsIFF1ZXN0aW9uOioqIENhbiB3ZSBidWlsZCBhIG1vZGVsIHRvIHByZWRpY3QgdGhlIGxvYW4gYEFtb3VudGAgYSBjdXN0b21lciB3aWxsIHJlcXVlc3Q/CgotLS0tLQoKIyBEYXRhIERlc2NyaXB0aW9uCgojIyBEYXRhIFNvdXJjZSBhbmQgQ29sbGVjdGlvbgoKVGhlIGRhdGEgaXMgZnJvbSB0aGUgYEJhbmtMb2FuRGVmYXVsdERhdGFzZXQuY3N2YCBmaWxlLiBJdCBpcyBhIGRhdGFzZXQgY29udGFpbmluZyBpbmZvcm1hdGlvbiBvbiAxLDAwMCBsb2FuIGFwcGxpY2FudHMuCgpTT1VSQ0U6IEJvb2sgLSBBcHBsaWVkIEFuYWx5dGljcyB0aHJvdWdoIENhc2UgU3R1ZGllcyBVc2luZyBTQVMgYW5kIFIsIERlZXB0aSBHdXB0YSBieSBBUHJlc3MsIElTQk4gLSA5NzgtMS00ODQyLTM1MjUtNgoKIyMgRGF0YXNldCBPdmVydmlldwoKVGhlIGRhdGFzZXQgY29udGFpbnMgMSwwMDAgb2JzZXJ2YXRpb25zIGFuZCAxNiB2YXJpYWJsZXMuIFdlIHdpbGwgZmlyc3QgbG9hZCB0aGUgZGF0YSBhbmQgaW5zcGVjdCBpdHMgc3RydWN0dXJlLgoKYGBge3IgbG9hZC1hbmQtaW5zcGVjdC1kYXRhfQojIGxvYWQgZGF0YXNldApzZXR3ZCgiL1VzZXJzL2plZmZlcnkvTGlicmFyeS9Nb2JpbGUgRG9jdW1lbnRzL2NvbX5hcHBsZX5DbG91ZERvY3MvRG9jdW1lbnRzL0RvY3VtZW50cyAtIGpNYWNQL1dDVVBBL0NsYXNzZXMvRmFsbCAyMDI1L1NUQTU1MS9Ib21ld29yay9EYXRhIikKbG9hbl9kYXRhIDwtIHJlYWQuY3N2KCJCYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdiIpCgojIHNldCBkaW1lbnNpb25zIGZvciBkaXNwbGF5aW5nIHZhcmlhYmxlIGRlZmluaXRpb25zCmNhdCgiRGltZW5zaW9uczoiLCBkaW0obG9hbl9kYXRhKVsxXSwgInJvd3MgYW5kIiwgZGltKGxvYW5fZGF0YSlbMl0sICJjb2x1bW5zXG4iKQpzdHIobG9hbl9kYXRhKQpgYGAKCiMjIFZhcmlhYmxlcwoKVGhlIGRhdGFzZXQgaW5jbHVkZXMgYSB0YXJnZXQgdmFyaWFibGUsIGBEZWZhdWx0YCwgYW5kIDE1IHByZWRpY3RvciB2YXJpYWJsZXMuCgp8IFZhcmlhYmxlIE5hbWUgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfC0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgYERlZmF1bHRgICAgICAgICAgfCBUYXJnZXQgVmFyaWFibGU6IDEgZm9yIGRlZmF1bHQsIDAgZm9yIG5vbi1kZWZhdWx0ICAgICAgICAgfAp8IGBDaGVja2luZ19hbW91bnRgIHwgQW1vdW50IGluIHRoZSBjaGVja2luZyBhY2NvdW50ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgVGVybWAgICAgICAgICAgICB8IExvYW4gdGVybSBpbiBtb250aHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYENyZWRpdF9zY29yZWAgICAgfCBBcHBsaWNhbnQncyBjcmVkaXQgc2NvcmUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGBHZW5kZXJgICAgICAgICAgIHwgQXBwbGljYW50J3MgZ2VuZGVyIChNYWxlL0ZlbWFsZSkgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgTWFyaXRhbF9zdGF0dXNgICB8IEFwcGxpY2FudCdzIG1hcml0YWwgc3RhdHVzIChTaW5nbGUvTWFycmllZCkgICAgICAgICAgICAgICB8CnwgYENhcl9sb2FuYCAgICAgICAgfCAxIGlmIGFwcGxpY2FudCBoYXMgYSBjYXIgbG9hbiwgMCBvdGhlcndpc2UgICAgICAgICAgICAgICAgfAp8IGBQZXJzb25hbF9sb2FuYCAgIHwgMSBpZiBhcHBsaWNhbnQgaGFzIGEgcGVyc29uYWwgbG9hbiwgMCBvdGhlcndpc2UgICAgICAgICAgIHwKfCBgSG9tZV9sb2FuYCAgICAgICB8IDEgaWYgYXBwbGljYW50IGhhcyBhIGhvbWUgbG9hbiwgMCBvdGhlcndpc2UgICAgICAgICAgICAgICB8CnwgYEVkdWNhdGlvbl9sb2FuYCAgfCAxIGlmIGFwcGxpY2FudCBoYXMgYW4gZWR1Y2F0aW9uIGxvYW4sIDAgb3RoZXJ3aXNlICAgICAgICAgfAp8IGBFbXBfc3RhdHVzYCAgICAgIHwgRW1wbG95bWVudCBzdGF0dXMgKEVtcGxveWVkL1VuZW1wbG95ZWQpICAgICAgICAgICAgICAgICAgIHwKfCBgQW1vdW50YCAgICAgICAgICB8IFRoZSBsb2FuIGFtb3VudCByZXF1ZXN0ZWQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYFNhdmluZ19hbW91bnRgICAgfCBBbW91bnQgaW4gdGhlIHNhdmluZ3MgYWNjb3VudCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGBFbXBfZHVyYXRpb25gICAgIHwgRW1wbG95bWVudCBkdXJhdGlvbiBpbiBtb250aHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgQWdlYCAgICAgICAgICAgICB8IEFwcGxpY2FudCdzIGFnZSBpbiB5ZWFycyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYE5vX29mX2NyZWRpdF9hY2NgfCBOdW1iZXIgb2YgY3JlZGl0IGFjY291bnRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAoKLS0tLS0KCiMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyBhbmQgRmVhdHVyZSBFbmdpbmVlcmluZwoKV2Ugd2lsbCBub3cgcGVyZm9ybSBFREEgdG8gY2xlYW4gdGhlIGRhdGEsIHNob3cgdmFyaWFibGUgcmVsYXRpb25zaGlwcyBhbmQgcHJlcGFyZSBmb3IgbW9kZWxpbmcuCgojIyBEYXRhIENsZWFuaW5nCgojIyMgTWlzc2luZyBWYWx1ZXMgYW5kIER1cGxpY2F0ZXMKCkZpcnN0IHdlIGNoZWNrIGZvciBhbnkgbWlzc2luZyB2YWx1ZXMgb3IgZHVwbGljYXRlZCByb3dzLgoKYGBge3IgY2hlY2stZHVwbGljYXRlc30KIyBjaGVjayBmb3IgbWlzc2luZyB2YWx1ZXMKY2F0KCJUb3RhbCBtaXNzaW5nIHZhbHVlczoiLCBzdW0oaXMubmEobG9hbl9kYXRhKSksICJcbiIpCgojIGNoZWNrIGZvciBkdXBsaWNhdGVkIHJvd3MKY2F0KCJUb3RhbCBkdXBsaWNhdGVkIHJvd3M6Iiwgc3VtKGR1cGxpY2F0ZWQobG9hbl9kYXRhKSksICJcbiIpCmBgYAoKVGhlIGRhdGEgaXMgY2xlYW4gd2l0aCBubyBtaXNzaW5nIHZhbHVlcyBvciBkdXBsaWNhdGUgZW50cmllcy4KCiMjIyBJbnNwZWN0aW5nIERhdGEgSXNzdWVzCgpUaGUgYENoZWNraW5nX2Ftb3VudGAgY29sdW1uIGhhcyBuZWdhdGl2ZSB2YWx1ZXMgd2hpY2ggbGlrZWx5IHJlcHJlc2VudCBvdmVyZHJhZnRzLiBUbyBzaW1wbGlmeSwgd2Ugc2V0IG5lZ2F0aXZlIHZhbHVlcyB0byAwLgoKYGBge3IgZml4LW5lZ2F0aXZlc30KIyBTaG93IHN1bW1hcnkgdG8gaWRlbnRpZnkgdGhlIGlzc3VlCnN1bW1hcnkobG9hbl9kYXRhJENoZWNraW5nX2Ftb3VudCkKCiMgQ3JlYXRlIGEgY29weSB0byBtb2RpZnkKbG9hbl9kYXRhX2NsZWFuZWQgPC0gbG9hbl9kYXRhCiMgVXNlIGJhc2UgUiBpbmRleGluZyB0byBmaW5kIG5lZ2F0aXZlIHZhbHVlcyBhbmQgc2V0IHRoZW0gdG8gMApsb2FuX2RhdGFfY2xlYW5lZCRDaGVja2luZ19hbW91bnRbbG9hbl9kYXRhX2NsZWFuZWQkQ2hlY2tpbmdfYW1vdW50IDwgMF0gPC0gMAoKY2F0KCJcblN1bW1hcnkgb2YgQ2hlY2tpbmdfYW1vdW50IGFmdGVyIGNvcnJlY3Rpb246XG4iKQpzdW1tYXJ5KGxvYW5fZGF0YV9jbGVhbmVkJENoZWNraW5nX2Ftb3VudCkKYGBgCgojIyBJbmRpdmlkdWFsIFZhcmlhYmxlIEFuYWx5c2lzCgpXZSB3aWxsIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGluZGl2aWR1YWwgdmFyaWFibGVzIHVzaW5nIGJhc2ljIHBsb3RzLgoKIyMjIE51bWVyaWNhbCBWYXJpYWJsZSBEaXN0cmlidXRpb25zCgpgYGB7ciBwbG90LW51bWVyaWMtZGlzdHJpYnV0aW9ucywgZmlnLmNhcD0iRGlzdHJpYnV0aW9ucyBvZiBLZXkgTnVtZXJpY2FsIFZhcmlhYmxlcyJ9CiMgU2V0IHVwIGEgMngyIHBsb3R0aW5nIGFyZWEKcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgQmFzaWMgaGlzdG9ncmFtcwpoaXN0KGxvYW5fZGF0YV9jbGVhbmVkJENyZWRpdF9zY29yZSwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgQ3JlZGl0IFNjb3JlIiwgeGxhYiA9ICJDcmVkaXQgU2NvcmUiLCBjb2wgPSAibGlnaHRibHVlIikKaGlzdChsb2FuX2RhdGFfY2xlYW5lZCRBZ2UsIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIEFnZSIsIHhsYWIgPSAiQWdlICh5ZWFycykiLCBjb2wgPSAiZ3JlZW4iKQpoaXN0KGxvYW5fZGF0YV9jbGVhbmVkJEFtb3VudCwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgTG9hbiBBbW91bnQiLCB4bGFiID0gIkFtb3VudCIsIGNvbCA9ICJwaW5rIikKaGlzdChsb2FuX2RhdGFfY2xlYW5lZCRTYXZpbmdfYW1vdW50LCBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBTYXZpbmcgQW1vdW50IiwgeGxhYiA9ICJTYXZpbmcgQW1vdW50IiwgY29sID0gInllbGxvdyIpCgojIFJlc2V0IHBsb3R0aW5nIGFyZWEgdG8gZGVmYXVsdApwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCioqT2JzZXJ2YXRpb25zOioqIGBDcmVkaXRfc2NvcmVgIGFuZCBgQWdlYCBhcmUgYXBwcm94aW1hdGVseSBub3JtYWxseSBkaXN0cmlidXRlZC4gRmluYW5jaWFsIHZhcmlhYmxlcyBsaWtlIGBBbW91bnRgIGFuZCBgU2F2aW5nX2Ftb3VudGAgYXJlIHNrZXdlZCB0byB0aGUgcmlnaHQuCgojIyMgQ2F0ZWdvcmljYWwgVmFyaWFibGUgRGlzdHJpYnV0aW9ucwoKYGBge3IgcGxvdC1jYXRlZ29yaWNhbC1kaXN0cmlidXRpb25zLCBmaWcuY2FwPSJEaXN0cmlidXRpb25zIG9mIENhdGVnb3JpY2FsICYgVGFyZ2V0IFZhcmlhYmxlcyJ9CiMgU2V0IHVwIGEgMngyIHBsb3R0aW5nIGFyZWEKcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgQmFzaWMgYmFyIHBsb3RzCmJhcnBsb3QodGFibGUobG9hbl9kYXRhX2NsZWFuZWQkRGVmYXVsdCksIG1haW4gPSAiTG9hbiBEZWZhdWx0IENvdW50cyIsIGNvbD1jKCJkYXJrZ3JlZW4iLCAiZGFya3JlZCIpKQpiYXJwbG90KHRhYmxlKGxvYW5fZGF0YV9jbGVhbmVkJEdlbmRlciksIG1haW4gPSAiR2VuZGVyIERpc3RyaWJ1dGlvbiIsIGNvbD1jKCJwaW5rIiwgImxpZ2h0Ymx1ZSIpKQpiYXJwbG90KHRhYmxlKGxvYW5fZGF0YV9jbGVhbmVkJE1hcml0YWxfc3RhdHVzKSwgbWFpbiA9ICJNYXJpdGFsIFN0YXR1cyIsIGNvbD1jKCJvcmFuZ2UiLCAicHVycGxlIikpCmJhcnBsb3QodGFibGUobG9hbl9kYXRhX2NsZWFuZWQkRW1wX3N0YXR1cyksIG1haW4gPSAiRW1wbG95bWVudCBTdGF0dXMiLCBjb2w9YygiZ3JheSIsICJicm93biIpKQoKIyBSZXNldCBwbG90dGluZyBhcmVhCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKKipPYnNlcnZhdGlvbnM6KiogVGhlcmUgYXJlIG1vcmUgbm9uLWRlZmF1bHRlcnMgKDApIHRoYW4gZGVmYXVsdGVycyAoMSkuCgojIyBSZWxhdGlvbnNoaXBzIGJldHdlZW4gVmFyaWFibGVzCgpOb3cgd2UgZXhwbG9yZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzIHVzaW5nIHNpbXBsZSBjb21wYXJpc29ucy4KCiMjIyBSZWxhdGlvbnNoaXAgd2l0aCBgRGVmYXVsdGAKCldlIHVzZSBib3hwbG90cyB0byBjb21wYXJlIG51bWVyaWNhbCB2YXJpYWJsZXMgYmV0d2VlbiB0aGUgZGVmYXVsdCBhbmQgbm9uLWRlZmF1bHQgYXBwbGljYW50cy4KCmBgYHtyIHBsb3QtYml2YXJpYXRlLWRlZmF1bHQsIGZpZy5jYXA9IkNyZWRpdCBTY29yZSBhbmQgTG9hbiBBbW91bnQgYnkgRGVmYXVsdCBTdGF0dXMifQojIFNldCB1cCBhIDF4MiBwbG90dGluZyBhcmVhCnBhcihtZnJvdyA9IGMoMSwgMikpCgojIGJhc2ljIGJveHBsb3RzCmJveHBsb3QoQ3JlZGl0X3Njb3JlIH4gRGVmYXVsdCwgZGF0YSA9IGxvYW5fZGF0YV9jbGVhbmVkLAogICAgICAgIG1haW4gPSAiQ3JlZGl0IFNjb3JlIGJ5IERlZmF1bHQiLAogICAgICAgIHhsYWIgPSAiRGVmYXVsdCAoMD1ObywgMT1ZZXMpIiwgeWxhYiA9ICJDcmVkaXQgU2NvcmUiLAogICAgICAgIGNvbCA9IGMoImRhcmtncmVlbiIsICJkYXJrcmVkIikpCgpib3hwbG90KEFtb3VudCB+IERlZmF1bHQsIGRhdGEgPSBsb2FuX2RhdGFfY2xlYW5lZCwKICAgICAgICBtYWluID0gIkxvYW4gQW1vdW50IGJ5IERlZmF1bHQiLAogICAgICAgIHhsYWIgPSAiRGVmYXVsdCAoMD1ObywgMT1ZZXMpIiwgeWxhYiA9ICJMb2FuIEFtb3VudCIsCiAgICAgICAgY29sID0gYygiZGFya2dyZWVuIiwgImRhcmtyZWQiKSkKICAgICAgICAKIyBSZXNldCBwbG90dGluZyBhcmVhCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKKipPYnNlcnZhdGlvbnM6KiogQ3VzdG9tZXJzIHdobyBkZWZhdWx0ZWQgdGVuZCB0byBoYXZlIGxvd2VyIGNyZWRpdCBzY29yZXMuCgojIyMgUmVsYXRpb25zaGlwIHdpdGggYEFtb3VudGAKClRvIGV4cGxvcmUgcHJlZGljdG9ycyBmb3IgdGhlIHJlZ3Jlc3Npb24gcHJvYmxlbSwgd2UgY2FuIHNlZSBob3cgYEFtb3VudGAgcmVsYXRlcyB0byBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuCgpgYGB7ciBwbG90LXJlbGF0aW9uc2hpcC1hbW91bnQsIGZpZy5jYXA9IkxvYW4gQW1vdW50IGJ5IEVtcGxveW1lbnQgYW5kIE1hcml0YWwgU3RhdHVzIn0KIyBTZXQgdXAgYSAxeDIgcGxvdHRpbmcgYXJlYQpwYXIobWZyb3cgPSBjKDEsIDIpKQoKIyBib3hwbG90cyBjb21wYXJpbmcgQW1vdW50IGFnYWluc3QgY2F0ZWdvcmljYWwgdmFyaWFibGVzCmJveHBsb3QoQW1vdW50IH4gRW1wX3N0YXR1cywgZGF0YSA9IGxvYW5fZGF0YV9jbGVhbmVkLAogICAgICAgIG1haW4gPSAiTG9hbiBBbW91bnQgYnkgRW1wbG95bWVudCBTdGF0dXMiLAogICAgICAgIHhsYWIgPSAiRW1wbG95bWVudCBTdGF0dXMiLCB5bGFiID0gIkxvYW4gQW1vdW50IiwKICAgICAgICBjb2wgPSBjKCJncmF5IiwgImJyb3duIikpCgpib3hwbG90KEFtb3VudCB+IE1hcml0YWxfc3RhdHVzLCBkYXRhID0gbG9hbl9kYXRhX2NsZWFuZWQsCiAgICAgICAgbWFpbiA9ICJMb2FuIEFtb3VudCBieSBNYXJpdGFsIFN0YXR1cyIsCiAgICAgICAgeGxhYiA9ICJNYXJpdGFsIFN0YXR1cyIsIHlsYWIgPSAiTG9hbiBBbW91bnQiLAogICAgICAgIGNvbCA9IGMoIm9yYW5nZSIsICJwdXJwbGUiKSkKICAgICAgICAKIyBSZXNldCBwbG90dGluZyBhcmVhCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKKipPYnNlcnZhdGlvbnM6KiogVGhlIHJlcXVlc3RlZCBsb2FuIGFtb3VudCBkb2VzIG5vdCBhcHBlYXIgdG8gZGlmZmVyIHNpZ25pZmljYW50bHkgYmV0d2VlbiB0aGVzZSBncm91cHMsIHN1Z2dlc3RpbmcgdGhleSBtYXkgbm90IGJlIHN0cm9uZyBwcmVkaWN0b3JzIG9uIHRoZWlyIG93biBmb3IgdGhlIHJlZ3Jlc3Npb24gcHJvYmxlbS4KCi0tLS0tCgojIENyZWF0aW5nIHRoZSBBbmFseXRpY2FsIERhdGFzZXQKCldlIHdpbGwgbm93IHBlcmZvcm0gZmVhdHVyZSBlbmdpbmVlcmluZyB0byBjcmVhdGUgdGhlIGZpbmFsIGRhdGFzZXQgZm9yIG1vZGVsaW5nLgoKKipTdGVwczoqKgoKMS4gICoqQ29udmVydCBDYXRlZ29yaWNhbCBWYXJpYWJsZXM6KiogYEdlbmRlcmAsIGBNYXJpdGFsX3N0YXR1c2AsIGFuZCBgRW1wX3N0YXR1c2Agd2lsbCBiZSBjb252ZXJ0ZWQgaW50byBmYWN0b3JzLCB0aGVuIGludG8gbnVtZXJpY2FsIGR1bW15IHZhcmlhYmxlcy4KMi4gICoqU3RhbmRhcmRpemUgTnVtZXJpY2FsIFZhcmlhYmxlczoqKiBXZSB3aWxsIHNjYWxlIG51bWVyaWNhbCBwcmVkaWN0b3JzIHRvIGhhdmUgYSBtZWFuIG9mIDAgYW5kIGEgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIDEuCgo8IS0tIGVuZCBsaXN0IC0tPgoKYGBge3IgY3JlYXRlLWFuYWx5dGljYWwtZGF0YX0KIyBTdGFydCB3aXRoIHRoZSBjbGVhbmVkIGRhdGEKYW5hbHl0aWNhbF9kYXRhIDwtIGxvYW5fZGF0YV9jbGVhbmVkCgojIDEuIENvbnZlcnQgY2hhcmFjdGVyIGNvbHVtbnMgdG8gZmFjdG9ycwphbmFseXRpY2FsX2RhdGEkR2VuZGVyIDwtIGFzLmZhY3RvcihhbmFseXRpY2FsX2RhdGEkR2VuZGVyKQphbmFseXRpY2FsX2RhdGEkTWFyaXRhbF9zdGF0dXMgPC0gYXMuZmFjdG9yKGFuYWx5dGljYWxfZGF0YSRNYXJpdGFsX3N0YXR1cykKYW5hbHl0aWNhbF9kYXRhJEVtcF9zdGF0dXMgPC0gYXMuZmFjdG9yKGFuYWx5dGljYWxfZGF0YSRFbXBfc3RhdHVzKQoKIyAyLiBJZGVudGlmeSBudW1lcmljIHByZWRpY3RvcnMgdG8gYmUgc2NhbGVkCnZhcnNfdG9fc2NhbGUgPC0gYygiQ2hlY2tpbmdfYW1vdW50IiwgIlRlcm0iLCAiQ3JlZGl0X3Njb3JlIiwgIkFtb3VudCIsCiAgICAgICAgICAgICAgICAgICAiU2F2aW5nX2Ftb3VudCIsICJFbXBfZHVyYXRpb24iLCAiQWdlIiwgIk5vX29mX2NyZWRpdF9hY2MiKQpzY2FsZWRfZGF0YSA8LSBzY2FsZShhbmFseXRpY2FsX2RhdGFbLCB2YXJzX3RvX3NjYWxlXSkKY29sbmFtZXMoc2NhbGVkX2RhdGEpIDwtIHBhc3RlMCh2YXJzX3RvX3NjYWxlLCAiX3NjYWxlZCIpICMgQWRkIHN1ZmZpeCB0byBzY2FsZWQgY29sdW1ucwoKIyAzLiBPbmUtaG90IGVuY29kZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgdXNpbmcgbW9kZWwubWF0cml4CmR1bW15X3ZhcnMgPC0gbW9kZWwubWF0cml4KH4gR2VuZGVyICsgTWFyaXRhbF9zdGF0dXMgKyBFbXBfc3RhdHVzIC0gMSwgZGF0YSA9IGFuYWx5dGljYWxfZGF0YSkKCiMgNC4gQ29tYmluZSBldmVyeXRoaW5nIGludG8gdGhlIGZpbmFsIGFuYWx5dGljYWwgZGF0YXNldApiaW5hcnlfdmFycyA8LSBhbmFseXRpY2FsX2RhdGFbLCBjKCJDYXJfbG9hbiIsICJQZXJzb25hbF9sb2FuIiwgIkhvbWVfbG9hbiIsICJFZHVjYXRpb25fbG9hbiIsICJEZWZhdWx0IildCmFuYWx5dGljYWxfZGF0YV9maW5hbCA8LSBjYmluZChzY2FsZWRfZGF0YSwgZHVtbXlfdmFycywgYmluYXJ5X3ZhcnMpCgojIERpc3BsYXkgdGhlIGZpcnN0IGZldyByb3dzIGFuZCBzdHJ1Y3R1cmUgb2YgdGhlIGZpbmFsIGRhdGFzZXQKY2F0KCJGaXJzdCA2IHJvd3Mgb2YgdGhlIGZpbmFsIGFuYWx5dGljYWwgZGF0YXNldDpcbiIpCmhlYWQoYXMuZGF0YS5mcmFtZShhbmFseXRpY2FsX2RhdGFfZmluYWwpKQpgYGAKCi0tLS0tCgojIFdyYXBwaW5nIEZlYXR1cmUgRW5naW5lZXJpbmcgQ29kZQoKVG8gbWFrZSB0aGVzZSBzdGVwcyByZXVzYWJsZSwgd2UgY2FuIHdyYXAgdGhlbSBpbiBhIGZ1bmN0aW9uLgoKYGBge3Igd3JhcC1mZWF0dXJlLWVuZ2luZWVyaW5nLWZ1bmN0aW9ufQpjcmVhdGVfYW5hbHl0aWNhbF9mZWF0dXJlcyA8LSBmdW5jdGlvbihyYXdfZGF0YSkgewogICMgU3RlcCAxOiBDbGVhbiBkYXRhCiAgZGF0YV9jbGVhbmVkIDwtIHJhd19kYXRhCiAgZGF0YV9jbGVhbmVkJENoZWNraW5nX2Ftb3VudFtkYXRhX2NsZWFuZWQkQ2hlY2tpbmdfYW1vdW50IDwgMF0gPC0gMAogIAogICMgU3RlcCAyOiBDb252ZXJ0IHRvIGZhY3RvcnMKICBkYXRhX2NsZWFuZWQkR2VuZGVyIDwtIGFzLmZhY3RvcihkYXRhX2NsZWFuZWQkR2VuZGVyKQogIGRhdGFfY2xlYW5lZCRNYXJpdGFsX3N0YXR1cyA8LSBhcy5mYWN0b3IoZGF0YV9jbGVhbmVkJE1hcml0YWxfc3RhdHVzKQogIGRhdGFfY2xlYW5lZCRFbXBfc3RhdHVzIDwtIGFzLmZhY3RvcihkYXRhX2NsZWFuZWQkRW1wX3N0YXR1cykKICAKICAjIFN0ZXAgMzogU2NhbGUgbnVtZXJpY2FsIHByZWRpY3RvcnMKICB2YXJzX3RvX3NjYWxlIDwtIGMoIkNoZWNraW5nX2Ftb3VudCIsICJUZXJtIiwgIkNyZWRpdF9zY29yZSIsICJBbW91bnQiLAogICAgICAgICAgICAgICAgICAgICAiU2F2aW5nX2Ftb3VudCIsICJFbXBfZHVyYXRpb24iLCAiQWdlIiwgIk5vX29mX2NyZWRpdF9hY2MiKQogIHNjYWxlZF9kYXRhIDwtIHNjYWxlKGRhdGFfY2xlYW5lZFssIHZhcnNfdG9fc2NhbGVdKQogIGNvbG5hbWVzKHNjYWxlZF9kYXRhKSA8LSBwYXN0ZTAodmFyc190b19zY2FsZSwgIl9zY2FsZWQiKQogIAogICMgU3RlcCA0OiBDcmVhdGUgZHVtbXkgdmFyaWFibGVzCiAgZHVtbXlfdmFycyA8LSBtb2RlbC5tYXRyaXgofiBHZW5kZXIgKyBNYXJpdGFsX3N0YXR1cyArIEVtcF9zdGF0dXMgLSAxLCBkYXRhID0gZGF0YV9jbGVhbmVkKQogIAogICMgU3RlcCA1OiBDb21iaW5lIGFsbCBwYXJ0cwogIGJpbmFyeV92YXJzIDwtIGRhdGFfY2xlYW5lZFssIGMoIkNhcl9sb2FuIiwgIlBlcnNvbmFsX2xvYW4iLCAiSG9tZV9sb2FuIiwgIkVkdWNhdGlvbl9sb2FuIiwgIkRlZmF1bHQiKV0KICBmaW5hbF9kYXRhIDwtIGNiaW5kKHNjYWxlZF9kYXRhLCBkdW1teV92YXJzLCBiaW5hcnlfdmFycykKICAKICByZXR1cm4oYXMuZGF0YS5mcmFtZShmaW5hbF9kYXRhKSkKfQoKIyBFeGFtcGxlIG9mIHVzaW5nIHRoZSBmdW5jdGlvbgphbmFseXRpY2FsX2RhdGFfZnJvbV9mdW5jdGlvbiA8LSBjcmVhdGVfYW5hbHl0aWNhbF9mZWF0dXJlcyhsb2FuX2RhdGEpCmNhdCgiRmlyc3QgMyByb3dzIG9mIGRhdGEgcHJvY2Vzc2VkIGJ5IHRoZSBmdW5jdGlvbjpcbiIpCmhlYWQoYW5hbHl0aWNhbF9kYXRhX2Zyb21fZnVuY3Rpb24sIDMpCmBgYAoKLS0tLS0KCiMgQ29uY2x1c2lvbiBhbmQgTmV4dCBTdGVwcwoKVGhpcyBjb25jbHVkZXMgb3VyIGFuYWx5c2lzIG9mIHRoZSBiYW5rIGxvYW4gZGVmYXVsdHMgZGF0YXNldC4gS2V5IGZpbmRpbmdzIGluY2x1ZGUgdGhlIGltcG9ydGFuY2Ugb2YgYENyZWRpdF9zY29yZWAgaW4gcHJlZGljdGluZyBkZWZhdWx0cyBhbmQgdGhlIG5hdHVyZSBvZiB0aGUgYERlZmF1bHRgIHZhcmlhYmxlLiBXZSBwZXJmb3JtZWQgZmVhdHVyZSBlbmdpbmVlcmluZyB0byBzY2FsZSBudW1lcmljYWwgcHJlZGljdG9ycyBhbmQgZW5jb2RlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgcmVzdWx0aW5nIGluIGEgY2xlYW4gZGF0YXNldCByZWFkeSBmb3IgcGFydCBJSS4gVGhlIGVudGlyZSBwcm9jZXNzIHdhcyB3cmFwcGVkIGluIGEgZnVuY3Rpb24gdG8gZW5zdXJlIGl0IGNhbiBiZSByZXVzZWQgY29uc2lzdGVudGx5LiBUaGUgcHJvamVjdCBpcyBub3cgcHJlcGFyZWQgZm9yIFBhcnQgSUkgd2hpY2ggd2lsbCBmb2N1cyBvbiBidWlsZGluZyBhbmQgZXZhbHVhdGluZyBwcmVkaWN0aXZlIG1vZGVscy4K