Part 1: Linear Regression
####In this section, I manually compute the slope and intercept of a
simple linear regression model to demonstrate understanding of the
mathematical foundation of OLS. I also calculate the correlation
coefficient and verify the relationship between correlation and the
regression slope. The lm() function is then used to confirm the results
and obtain additional statistics such as R-squared and p-values. A
custom regression function is implemented using the matrix formula,
showing the algebraic structure behind multiple regression.
Understanding regression
## Example: Space Shuttle Launch Data
launch <- read.csv("challenger.csv")
# estimate beta manually
b <- cov(launch$temperature, launch$distress_ct) / var(launch$temperature)
b
# estimate alpha manually
a <- mean(launch$distress_ct) - b * mean(launch$temperature)
a
# calculate the correlation of launch data
r <- cov(launch$temperature, launch$distress_ct) /
(sd(launch$temperature) * sd(launch$distress_ct))
r
cor(launch$temperature, launch$distress_ct)
# computing the slope using correlation
r * (sd(launch$distress_ct) / sd(launch$temperature))
# confirming the regression line using the lm function (not in text)
model <- lm(distress_ct ~ temperature, data = launch)
model
summary(model)
# creating a simple multiple regression function
reg <- function(y, x) {
x <- as.matrix(x)
x <- cbind(Intercept = 1, x)
b <- solve(t(x) %*% x) %*% t(x) %*% y
colnames(b) <- "estimate"
print(b)
}
# examine the launch data
str(launch)
# test regression model with simple linear regression
reg(y = launch$distress_ct, x = launch[2])
# use regression model with multiple regression
reg(y = launch$distress_ct, x = launch[2:4])
# confirming the multiple regression result using the lm function (not in text)
model <- lm(distress_ct ~ temperature + field_check_pressure + flight_num, data = launch)
model
Predicting Medical Expenses
## Step 2: Exploring and preparing the data ----
insurance <- read.csv("insurance.csv", stringsAsFactors = TRUE)
str(insurance)
# summarize the charges variable
summary(insurance$expenses)
# histogram of insurance charges
hist(insurance$expenses)
# table of region
table(insurance$region)
# exploring relationships among features: correlation matrix
cor(insurance[c("age", "bmi", "children", "expenses")])
# visualing relationships among features: scatterplot matrix
pairs(insurance[c("age", "bmi", "children", "expenses")])
## Step 3: Training a model on the data ----
ins_model <- lm(expenses ~ age + children + bmi + sex + smoker + region,
data = insurance)
ins_model <- lm(expenses ~ ., data = insurance) # this is equivalent to above
# see the estimated beta coefficients
ins_model
Step 5: Improving model performance
# add a higher-order "age" term
insurance$age2 <- insurance$age^2
# add an indicator for BMI >= 30
insurance$bmi30 <- ifelse(insurance$bmi >= 30, 1, 0)
# create final model
ins_model2 <- lm(expenses ~ age + age2 + children + bmi + sex +
bmi30*smoker + region, data = insurance)
summary(ins_model2)
# making predictions with the regression model
insurance$pred <- predict(ins_model2, insurance)
cor(insurance$pred, insurance$expenses)
plot(insurance$pred, insurance$expenses)
abline(a = 0, b = 1, col = "red", lwd = 3, lty = 2)
predict(ins_model2,
data.frame(age = 30, age2 = 30^2, children = 2,
bmi = 30, sex = "male", bmi30 = 1,
smoker = "no", region = "northeast"))
predict(ins_model2,
data.frame(age = 30, age2 = 30^2, children = 2,
bmi = 30, sex = "female", bmi30 = 1,
smoker = "no", region = "northeast"))
predict(ins_model2,
data.frame(age = 30, age2 = 30^2, children = 0,
bmi = 30, sex = "female", bmi30 = 1,
smoker = "no", region = "northeast"))
Part 2: Regression Trees and Model Trees
####I begin with exploratory data analysis to understand variable
distributions and relationships. A multiple linear regression model is
built to estimate how demographic and health-related factors influence
medical expenses. To improve the model, I add a quadratic age term, a
BMI indicator variable, and an interaction between BMI and smoking
status to capture nonlinear and combined effects. Model performance is
evaluated using correlation and prediction accuracy
Understanding regression trees and model trees
Example: Calculating SDR
# set up the data
tee <- c(1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7)
at1 <- c(1, 1, 1, 2, 2, 3, 4, 5, 5)
at2 <- c(6, 6, 7, 7, 7, 7)
bt1 <- c(1, 1, 1, 2, 2, 3, 4)
bt2 <- c(5, 5, 6, 6, 7, 7, 7, 7)
# compute the SDR
sdr_a <- sd(tee) - (length(at1) / length(tee) * sd(at1) + length(at2) / length(tee) * sd(at2))
sdr_b <- sd(tee) - (length(bt1) / length(tee) * sd(bt1) + length(bt2) / length(tee) * sd(bt2))
# compare the SDR for each split
sdr_a
sdr_b
Exercise No 3: Estimating Wine Quality
####The dataset is split into training and testing sets to evaluate
out-of-sample performance. A regression tree is trained using rpart, and
performance is measured using correlation and Mean Absolute Error (MAE).
To improve predictions, a Cubist model tree is implemented, combining
decision rules with linear regression at the leaves. The results are
compared to assess which approach provides better predictive
accuracy.
Step 2: Exploring and preparing the data
wine <- read.csv("whitewines.csv")
# examine the wine data
str(wine)
# the distribution of quality ratings
hist(wine$quality)
# summary statistics of the wine data
summary(wine)
wine_train <- wine[1:3750, ]
wine_test <- wine[3751:4898, ]
Step 3: Training a model on the data
# regression tree using rpart
library(rpart)
m.rpart <- rpart(quality ~ ., data = wine_train)
# get basic information about the tree
m.rpart
# get more detailed information about the tree
summary(m.rpart)
install.packages("rpart.plot")
# use the rpart.plot package to create a visualization
library(rpart.plot)
# a basic decision tree diagram
rpart.plot(m.rpart, digits = 3)
# a few adjustments to the diagram
rpart.plot(m.rpart, digits = 4, fallen.leaves = TRUE, type = 3, extra = 101)
LS0tDQp0aXRsZTogIkNoYXB0ZXIgNjogUmVncmVzc2lvbiBNZXRob2RzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQojIyMjIFBhcnQgMTogTGluZWFyIFJlZ3Jlc3Npb24NCg0KIyMjI0luIHRoaXMgc2VjdGlvbiwgSSBtYW51YWxseSBjb21wdXRlIHRoZSBzbG9wZSBhbmQgaW50ZXJjZXB0IG9mIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIGRlbW9uc3RyYXRlIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG1hdGhlbWF0aWNhbCBmb3VuZGF0aW9uIG9mIE9MUy4gSSBhbHNvIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYW5kIHZlcmlmeSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY29ycmVsYXRpb24gYW5kIHRoZSByZWdyZXNzaW9uIHNsb3BlLiBUaGUgbG0oKSBmdW5jdGlvbiBpcyB0aGVuIHVzZWQgdG8gY29uZmlybSB0aGUgcmVzdWx0cyBhbmQgb2J0YWluIGFkZGl0aW9uYWwgc3RhdGlzdGljcyBzdWNoIGFzIFItc3F1YXJlZCBhbmQgcC12YWx1ZXMuIEEgY3VzdG9tIHJlZ3Jlc3Npb24gZnVuY3Rpb24gaXMgaW1wbGVtZW50ZWQgdXNpbmcgdGhlIG1hdHJpeCBmb3JtdWxhLCBzaG93aW5nIHRoZSBhbGdlYnJhaWMgc3RydWN0dXJlIGJlaGluZCBtdWx0aXBsZSByZWdyZXNzaW9uLg0KDQojIyBVbmRlcnN0YW5kaW5nIHJlZ3Jlc3Npb24NCg0KDQoNCmBgYHtyfQ0KIyMgRXhhbXBsZTogU3BhY2UgU2h1dHRsZSBMYXVuY2ggRGF0YQ0KbGF1bmNoIDwtIHJlYWQuY3N2KCJjaGFsbGVuZ2VyLmNzdiIpDQpgYGANCg0KDQpgYGB7cn0NCiMgZXN0aW1hdGUgYmV0YSBtYW51YWxseQ0KYiA8LSBjb3YobGF1bmNoJHRlbXBlcmF0dXJlLCBsYXVuY2gkZGlzdHJlc3NfY3QpIC8gdmFyKGxhdW5jaCR0ZW1wZXJhdHVyZSkNCmINCmBgYA0KDQoNCmBgYHtyfQ0KIyBlc3RpbWF0ZSBhbHBoYSBtYW51YWxseQ0KYSA8LSBtZWFuKGxhdW5jaCRkaXN0cmVzc19jdCkgLSBiICogbWVhbihsYXVuY2gkdGVtcGVyYXR1cmUpDQphDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIG9mIGxhdW5jaCBkYXRhDQpyIDwtIGNvdihsYXVuY2gkdGVtcGVyYXR1cmUsIGxhdW5jaCRkaXN0cmVzc19jdCkgLw0KICAgICAgIChzZChsYXVuY2gkdGVtcGVyYXR1cmUpICogc2QobGF1bmNoJGRpc3RyZXNzX2N0KSkNCnINCmBgYA0KDQoNCmBgYHtyfQ0KY29yKGxhdW5jaCR0ZW1wZXJhdHVyZSwgbGF1bmNoJGRpc3RyZXNzX2N0KQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KIyBjb21wdXRpbmcgdGhlIHNsb3BlIHVzaW5nIGNvcnJlbGF0aW9uDQpyICogKHNkKGxhdW5jaCRkaXN0cmVzc19jdCkgLyBzZChsYXVuY2gkdGVtcGVyYXR1cmUpKQ0KYGBgDQoNCg0KYGBge3J9DQojIGNvbmZpcm1pbmcgdGhlIHJlZ3Jlc3Npb24gbGluZSB1c2luZyB0aGUgbG0gZnVuY3Rpb24gKG5vdCBpbiB0ZXh0KQ0KbW9kZWwgPC0gbG0oZGlzdHJlc3NfY3QgfiB0ZW1wZXJhdHVyZSwgZGF0YSA9IGxhdW5jaCkNCm1vZGVsDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc3VtbWFyeShtb2RlbCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjcmVhdGluZyBhIHNpbXBsZSBtdWx0aXBsZSByZWdyZXNzaW9uIGZ1bmN0aW9uDQpyZWcgPC0gZnVuY3Rpb24oeSwgeCkgew0KICB4IDwtIGFzLm1hdHJpeCh4KQ0KICB4IDwtIGNiaW5kKEludGVyY2VwdCA9IDEsIHgpDQogIGIgPC0gc29sdmUodCh4KSAlKiUgeCkgJSolIHQoeCkgJSolIHkNCiAgY29sbmFtZXMoYikgPC0gImVzdGltYXRlIg0KICBwcmludChiKQ0KfQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KIyBleGFtaW5lIHRoZSBsYXVuY2ggZGF0YQ0Kc3RyKGxhdW5jaCkNCmBgYA0KDQoNCg0KYGBge3J9DQojIHRlc3QgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbg0KcmVnKHkgPSBsYXVuY2gkZGlzdHJlc3NfY3QsIHggPSBsYXVuY2hbMl0pDQpgYGANCg0KDQpgYGB7cn0NCiMgdXNlIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBtdWx0aXBsZSByZWdyZXNzaW9uDQpyZWcoeSA9IGxhdW5jaCRkaXN0cmVzc19jdCwgeCA9IGxhdW5jaFsyOjRdKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY29uZmlybWluZyB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbiByZXN1bHQgdXNpbmcgdGhlIGxtIGZ1bmN0aW9uIChub3QgaW4gdGV4dCkNCm1vZGVsIDwtIGxtKGRpc3RyZXNzX2N0IH4gdGVtcGVyYXR1cmUgKyBmaWVsZF9jaGVja19wcmVzc3VyZSArIGZsaWdodF9udW0sIGRhdGEgPSBsYXVuY2gpDQptb2RlbA0KYGBgDQoNCg0KIyMgUHJlZGljdGluZyBNZWRpY2FsIEV4cGVuc2VzDQoNCmBgYHtyfQ0KIyMgU3RlcCAyOiBFeHBsb3JpbmcgYW5kIHByZXBhcmluZyB0aGUgZGF0YSAtLS0tDQppbnN1cmFuY2UgPC0gcmVhZC5jc3YoImluc3VyYW5jZS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gVFJVRSkNCnN0cihpbnN1cmFuY2UpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBzdW1tYXJpemUgdGhlIGNoYXJnZXMgdmFyaWFibGUNCnN1bW1hcnkoaW5zdXJhbmNlJGV4cGVuc2VzKQ0KYGBgDQoNCg0KYGBge3J9DQojIGhpc3RvZ3JhbSBvZiBpbnN1cmFuY2UgY2hhcmdlcw0KaGlzdChpbnN1cmFuY2UkZXhwZW5zZXMpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyB0YWJsZSBvZiByZWdpb24NCnRhYmxlKGluc3VyYW5jZSRyZWdpb24pDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBleHBsb3JpbmcgcmVsYXRpb25zaGlwcyBhbW9uZyBmZWF0dXJlczogY29ycmVsYXRpb24gbWF0cml4DQpjb3IoaW5zdXJhbmNlW2MoImFnZSIsICJibWkiLCAiY2hpbGRyZW4iLCAiZXhwZW5zZXMiKV0pDQpgYGANCg0KDQpgYGB7cn0NCiMgdmlzdWFsaW5nIHJlbGF0aW9uc2hpcHMgYW1vbmcgZmVhdHVyZXM6IHNjYXR0ZXJwbG90IG1hdHJpeA0KcGFpcnMoaW5zdXJhbmNlW2MoImFnZSIsICJibWkiLCAiY2hpbGRyZW4iLCAiZXhwZW5zZXMiKV0pDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIyBTdGVwIDM6IFRyYWluaW5nIGEgbW9kZWwgb24gdGhlIGRhdGEgLS0tLQ0KaW5zX21vZGVsIDwtIGxtKGV4cGVuc2VzIH4gYWdlICsgY2hpbGRyZW4gKyBibWkgKyBzZXggKyBzbW9rZXIgKyByZWdpb24sDQogICAgICAgICAgICAgICAgZGF0YSA9IGluc3VyYW5jZSkNCmluc19tb2RlbCA8LSBsbShleHBlbnNlcyB+IC4sIGRhdGEgPSBpbnN1cmFuY2UpICMgdGhpcyBpcyBlcXVpdmFsZW50IHRvIGFib3ZlDQoNCiMgc2VlIHRoZSBlc3RpbWF0ZWQgYmV0YSBjb2VmZmljaWVudHMNCmluc19tb2RlbA0KYGBgDQoNCg0KIyMgU3RlcCA0OiBFdmFsdWF0aW5nIG1vZGVsIHBlcmZvcm1hbmNlDQoNCmBgYHtyfQ0KIyBzZWUgbW9yZSBkZXRhaWwgYWJvdXQgdGhlIGVzdGltYXRlZCBiZXRhIGNvZWZmaWNpZW50cw0Kc3VtbWFyeShpbnNfbW9kZWwpDQpgYGANCg0KDQojIyBTdGVwIDU6IEltcHJvdmluZyBtb2RlbCBwZXJmb3JtYW5jZQ0KDQoNCg0KYGBge3J9DQojIGFkZCBhIGhpZ2hlci1vcmRlciAiYWdlIiB0ZXJtDQppbnN1cmFuY2UkYWdlMiA8LSBpbnN1cmFuY2UkYWdlXjINCmBgYA0KDQoNCg0KYGBge3J9DQojIGFkZCBhbiBpbmRpY2F0b3IgZm9yIEJNSSA+PSAzMA0KaW5zdXJhbmNlJGJtaTMwIDwtIGlmZWxzZShpbnN1cmFuY2UkYm1pID49IDMwLCAxLCAwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY3JlYXRlIGZpbmFsIG1vZGVsDQppbnNfbW9kZWwyIDwtIGxtKGV4cGVuc2VzIH4gYWdlICsgYWdlMiArIGNoaWxkcmVuICsgYm1pICsgc2V4ICsNCiAgICAgICAgICAgICAgICAgICBibWkzMCpzbW9rZXIgKyByZWdpb24sIGRhdGEgPSBpbnN1cmFuY2UpDQpgYGANCg0KDQpgYGB7cn0NCnN1bW1hcnkoaW5zX21vZGVsMikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBtYWtpbmcgcHJlZGljdGlvbnMgd2l0aCB0aGUgcmVncmVzc2lvbiBtb2RlbA0KaW5zdXJhbmNlJHByZWQgPC0gcHJlZGljdChpbnNfbW9kZWwyLCBpbnN1cmFuY2UpDQpjb3IoaW5zdXJhbmNlJHByZWQsIGluc3VyYW5jZSRleHBlbnNlcykNCmBgYA0KDQoNCg0KYGBge3J9DQpwbG90KGluc3VyYW5jZSRwcmVkLCBpbnN1cmFuY2UkZXhwZW5zZXMpDQphYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSAicmVkIiwgbHdkID0gMywgbHR5ID0gMikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCnByZWRpY3QoaW5zX21vZGVsMiwNCiAgICAgICAgZGF0YS5mcmFtZShhZ2UgPSAzMCwgYWdlMiA9IDMwXjIsIGNoaWxkcmVuID0gMiwNCiAgICAgICAgICAgICAgICAgICBibWkgPSAzMCwgc2V4ID0gIm1hbGUiLCBibWkzMCA9IDEsDQogICAgICAgICAgICAgICAgICAgc21va2VyID0gIm5vIiwgcmVnaW9uID0gIm5vcnRoZWFzdCIpKQ0KYGBgDQoNCg0KYGBge3J9DQpwcmVkaWN0KGluc19tb2RlbDIsDQogICAgICAgIGRhdGEuZnJhbWUoYWdlID0gMzAsIGFnZTIgPSAzMF4yLCBjaGlsZHJlbiA9IDIsDQogICAgICAgICAgICAgICAgICAgYm1pID0gMzAsIHNleCA9ICJmZW1hbGUiLCBibWkzMCA9IDEsDQogICAgICAgICAgICAgICAgICAgc21va2VyID0gIm5vIiwgcmVnaW9uID0gIm5vcnRoZWFzdCIpKQ0KYGBgDQoNCg0KYGBge3J9DQpwcmVkaWN0KGluc19tb2RlbDIsDQogICAgICAgIGRhdGEuZnJhbWUoYWdlID0gMzAsIGFnZTIgPSAzMF4yLCBjaGlsZHJlbiA9IDAsDQogICAgICAgICAgICAgICAgICAgYm1pID0gMzAsIHNleCA9ICJmZW1hbGUiLCBibWkzMCA9IDEsDQogICAgICAgICAgICAgICAgICAgc21va2VyID0gIm5vIiwgcmVnaW9uID0gIm5vcnRoZWFzdCIpKQ0KYGBgDQoNCg0KDQojIyMjIFBhcnQgMjogUmVncmVzc2lvbiBUcmVlcyBhbmQgTW9kZWwgVHJlZXMNCg0KIyMjI0kgYmVnaW4gd2l0aCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIHRvIHVuZGVyc3RhbmQgdmFyaWFibGUgZGlzdHJpYnV0aW9ucyBhbmQgcmVsYXRpb25zaGlwcy4gQSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpcyBidWlsdCB0byBlc3RpbWF0ZSBob3cgZGVtb2dyYXBoaWMgYW5kIGhlYWx0aC1yZWxhdGVkIGZhY3RvcnMgaW5mbHVlbmNlIG1lZGljYWwgZXhwZW5zZXMuIFRvIGltcHJvdmUgdGhlIG1vZGVsLCBJIGFkZCBhIHF1YWRyYXRpYyBhZ2UgdGVybSwgYSBCTUkgaW5kaWNhdG9yIHZhcmlhYmxlLCBhbmQgYW4gaW50ZXJhY3Rpb24gYmV0d2VlbiBCTUkgYW5kIHNtb2tpbmcgc3RhdHVzIHRvIGNhcHR1cmUgbm9ubGluZWFyIGFuZCBjb21iaW5lZCBlZmZlY3RzLiBNb2RlbCBwZXJmb3JtYW5jZSBpcyBldmFsdWF0ZWQgdXNpbmcgY29ycmVsYXRpb24gYW5kIHByZWRpY3Rpb24gYWNjdXJhY3kNCg0KIyMgVW5kZXJzdGFuZGluZyByZWdyZXNzaW9uIHRyZWVzIGFuZCBtb2RlbCB0cmVlcw0KDQojIyBFeGFtcGxlOiBDYWxjdWxhdGluZyBTRFINCg0KYGBge3J9DQojIHNldCB1cCB0aGUgZGF0YQ0KdGVlIDwtIGMoMSwgMSwgMSwgMiwgMiwgMywgNCwgNSwgNSwgNiwgNiwgNywgNywgNywgNykNCmF0MSA8LSBjKDEsIDEsIDEsIDIsIDIsIDMsIDQsIDUsIDUpDQphdDIgPC0gYyg2LCA2LCA3LCA3LCA3LCA3KQ0KYnQxIDwtIGMoMSwgMSwgMSwgMiwgMiwgMywgNCkNCmJ0MiA8LSBjKDUsIDUsIDYsIDYsIDcsIDcsIDcsIDcpDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIGNvbXB1dGUgdGhlIFNEUg0Kc2RyX2EgPC0gc2QodGVlKSAtIChsZW5ndGgoYXQxKSAvIGxlbmd0aCh0ZWUpICogc2QoYXQxKSArIGxlbmd0aChhdDIpIC8gbGVuZ3RoKHRlZSkgKiBzZChhdDIpKQ0Kc2RyX2IgPC0gc2QodGVlKSAtIChsZW5ndGgoYnQxKSAvIGxlbmd0aCh0ZWUpICogc2QoYnQxKSArIGxlbmd0aChidDIpIC8gbGVuZ3RoKHRlZSkgKiBzZChidDIpKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY29tcGFyZSB0aGUgU0RSIGZvciBlYWNoIHNwbGl0DQpzZHJfYQ0Kc2RyX2INCmBgYA0KDQoNCg0KIyMgRXhlcmNpc2UgTm8gMzogRXN0aW1hdGluZyBXaW5lIFF1YWxpdHkNCg0KIyMjI1RoZSBkYXRhc2V0IGlzIHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyB0byBldmFsdWF0ZSBvdXQtb2Ytc2FtcGxlIHBlcmZvcm1hbmNlLiBBIHJlZ3Jlc3Npb24gdHJlZSBpcyB0cmFpbmVkIHVzaW5nIHJwYXJ0LCBhbmQgcGVyZm9ybWFuY2UgaXMgbWVhc3VyZWQgdXNpbmcgY29ycmVsYXRpb24gYW5kIE1lYW4gQWJzb2x1dGUgRXJyb3IgKE1BRSkuIFRvIGltcHJvdmUgcHJlZGljdGlvbnMsIGEgQ3ViaXN0IG1vZGVsIHRyZWUgaXMgaW1wbGVtZW50ZWQsIGNvbWJpbmluZyBkZWNpc2lvbiBydWxlcyB3aXRoIGxpbmVhciByZWdyZXNzaW9uIGF0IHRoZSBsZWF2ZXMuIFRoZSByZXN1bHRzIGFyZSBjb21wYXJlZCB0byBhc3Nlc3Mgd2hpY2ggYXBwcm9hY2ggcHJvdmlkZXMgYmV0dGVyIHByZWRpY3RpdmUgYWNjdXJhY3kuDQoNCg0KIyMgU3RlcCAyOiBFeHBsb3JpbmcgYW5kIHByZXBhcmluZyB0aGUgZGF0YQ0KDQpgYGB7cn0NCndpbmUgPC0gcmVhZC5jc3YoIndoaXRld2luZXMuY3N2IikNCmBgYA0KDQoNCg0KYGBge3J9DQojIGV4YW1pbmUgdGhlIHdpbmUgZGF0YQ0Kc3RyKHdpbmUpDQpgYGANCg0KDQpgYGB7cn0NCiMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBxdWFsaXR5IHJhdGluZ3MNCmhpc3Qod2luZSRxdWFsaXR5KQ0KYGBgDQoNCg0KYGBge3J9DQojIHN1bW1hcnkgc3RhdGlzdGljcyBvZiB0aGUgd2luZSBkYXRhDQpzdW1tYXJ5KHdpbmUpDQpgYGANCg0KDQoNCmBgYHtyfQ0Kd2luZV90cmFpbiA8LSB3aW5lWzE6Mzc1MCwgXQ0Kd2luZV90ZXN0IDwtIHdpbmVbMzc1MTo0ODk4LCBdDQpgYGANCg0KDQoNCiMjIFN0ZXAgMzogVHJhaW5pbmcgYSBtb2RlbCBvbiB0aGUgZGF0YQ0KDQpgYGB7cn0NCiMgcmVncmVzc2lvbiB0cmVlIHVzaW5nIHJwYXJ0DQpsaWJyYXJ5KHJwYXJ0KQ0KbS5ycGFydCA8LSBycGFydChxdWFsaXR5IH4gLiwgZGF0YSA9IHdpbmVfdHJhaW4pDQpgYGANCg0KDQpgYGB7cn0NCiMgZ2V0IGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IHRoZSB0cmVlDQptLnJwYXJ0DQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBnZXQgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdHJlZQ0Kc3VtbWFyeShtLnJwYXJ0KQ0KYGBgDQoNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmBgYA0KDQoNCmBgYHtyfQ0KIyB1c2UgdGhlIHJwYXJ0LnBsb3QgcGFja2FnZSB0byBjcmVhdGUgYSB2aXN1YWxpemF0aW9uDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpgYGANCg0KDQpgYGB7cn0NCiMgYSBiYXNpYyBkZWNpc2lvbiB0cmVlIGRpYWdyYW0NCnJwYXJ0LnBsb3QobS5ycGFydCwgZGlnaXRzID0gMykNCmBgYA0KDQoNCmBgYHtyfQ0KIyBhIGZldyBhZGp1c3RtZW50cyB0byB0aGUgZGlhZ3JhbQ0KcnBhcnQucGxvdChtLnJwYXJ0LCBkaWdpdHMgPSA0LCBmYWxsZW4ubGVhdmVzID0gVFJVRSwgdHlwZSA9IDMsIGV4dHJhID0gMTAxKQ0KYGBgDQoNCg0KIyMgU3RlcCA0OiBFdmFsdWF0ZSBtb2RlbCBwZXJmb3JtYW5jDQoNCmBgYHtyfQ0KIyBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHRlc3RpbmcgZGF0YXNldA0KcC5ycGFydCA8LSBwcmVkaWN0KG0ucnBhcnQsIHdpbmVfdGVzdCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjb21wYXJlIHRoZSBkaXN0cmlidXRpb24gb2YgcHJlZGljdGVkIHZhbHVlcyB2cy4gYWN0dWFsIHZhbHVlcw0Kc3VtbWFyeShwLnJwYXJ0KQ0Kc3VtbWFyeSh3aW5lX3Rlc3QkcXVhbGl0eSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjb21wYXJlIHRoZSBjb3JyZWxhdGlvbg0KY29yKHAucnBhcnQsIHdpbmVfdGVzdCRxdWFsaXR5KQ0KYGBgDQoNCg0KYGBge3J9DQojIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiBhYnNvbHV0ZSBlcnJvcg0KTUFFIDwtIGZ1bmN0aW9uKGFjdHVhbCwgcHJlZGljdGVkKSB7DQogIG1lYW4oYWJzKGFjdHVhbCAtIHByZWRpY3RlZCkpICANCn0NCmBgYA0KDQoNCg0KYGBge3J9DQojIG1lYW4gYWJzb2x1dGUgZXJyb3IgYmV0d2VlbiBwcmVkaWN0ZWQgYW5kIGFjdHVhbCB2YWx1ZXMNCk1BRShwLnJwYXJ0LCB3aW5lX3Rlc3QkcXVhbGl0eSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBtZWFuIGFic29sdXRlIGVycm9yIGJldHdlZW4gYWN0dWFsIHZhbHVlcyBhbmQgbWVhbiB2YWx1ZQ0KbWVhbih3aW5lX3RyYWluJHF1YWxpdHkpICMgcmVzdWx0ID0gNS44Nw0KTUFFKDUuODcsIHdpbmVfdGVzdCRxdWFsaXR5KQ0KYGBgDQoNCg0KIyMgU3RlcCA1OiBJbXByb3ZpbmcgbW9kZWwgcGVyZm9ybWFuY2UNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygicGx5ciIpDQojaW5zdGFsbC5wYWNrYWdlcygiQ3ViaXN0IikNCmBgYA0KDQoNCmBgYHtyfQ0KIyB0cmFpbiBhIEN1YmlzdCBNb2RlbCBUcmVlDQpsaWJyYXJ5KEN1YmlzdCkNCm0uY3ViaXN0IDwtIGN1YmlzdCh4ID0gd2luZV90cmFpblstMTJdLCB5ID0gd2luZV90cmFpbiRxdWFsaXR5KQ0KYGBgDQoNCg0KYGBge3J9DQojIGRpc3BsYXkgYmFzaWMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIG1vZGVsIHRyZWUNCm0uY3ViaXN0DQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBkaXNwbGF5IHRoZSB0cmVlIGl0c2VsZg0Kc3VtbWFyeShtLmN1YmlzdCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3IgdGhlIG1vZGVsDQpwLmN1YmlzdCA8LSBwcmVkaWN0KG0uY3ViaXN0LCB3aW5lX3Rlc3QpDQpgYGANCg0KDQpgYGB7cn0NCiMgc3VtbWFyeSBzdGF0aXN0aWNzIGFib3V0IHRoZSBwcmVkaWN0aW9ucw0Kc3VtbWFyeShwLmN1YmlzdCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgYW5kIHRydWUgdmFsdWVzDQpjb3IocC5jdWJpc3QsIHdpbmVfdGVzdCRxdWFsaXR5KQ0KYGBgDQoNCg0KYGBge3J9DQojIG1lYW4gYWJzb2x1dGUgZXJyb3Igb2YgcHJlZGljdGVkIGFuZCB0cnVlIHZhbHVlcw0KIyAodXNlcyBhIGN1c3RvbSBmdW5jdGlvbiBkZWZpbmVkIGFib3ZlKQ0KTUFFKHdpbmVfdGVzdCRxdWFsaXR5LCBwLmN1YmlzdCkgDQpgYGANCg0KDQoNCg==