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 4: Evaluating model performance

# see more detail about the estimated beta coefficients
summary(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)

Step 4: Evaluate model performanc

# generate predictions for the testing dataset
p.rpart <- predict(m.rpart, wine_test)
# compare the distribution of predicted values vs. actual values
summary(p.rpart)
summary(wine_test$quality)
# compare the correlation
cor(p.rpart, wine_test$quality)
# function to calculate the mean absolute error
MAE <- function(actual, predicted) {
  mean(abs(actual - predicted))  
}
# mean absolute error between predicted and actual values
MAE(p.rpart, wine_test$quality)
# mean absolute error between actual values and mean value
mean(wine_train$quality) # result = 5.87
MAE(5.87, wine_test$quality)

Step 5: Improving model performance

#install.packages("plyr")
#install.packages("Cubist")
# train a Cubist Model Tree
library(Cubist)
m.cubist <- cubist(x = wine_train[-12], y = wine_train$quality)
# display basic information about the model tree
m.cubist
# display the tree itself
summary(m.cubist)
# generate predictions for the model
p.cubist <- predict(m.cubist, wine_test)
# summary statistics about the predictions
summary(p.cubist)
# correlation between the predicted and true values
cor(p.cubist, wine_test$quality)
# mean absolute error of predicted and true values
# (uses a custom function defined above)
MAE(wine_test$quality, p.cubist) 
LS0tDQp0aXRsZTogIkNoYXB0ZXIgNjogUmVncmVzc2lvbiBNZXRob2RzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQojIyMjIFBhcnQgMTogTGluZWFyIFJlZ3Jlc3Npb24NCg0KIyMjI0luIHRoaXMgc2VjdGlvbiwgSSBtYW51YWxseSBjb21wdXRlIHRoZSBzbG9wZSBhbmQgaW50ZXJjZXB0IG9mIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIGRlbW9uc3RyYXRlIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG1hdGhlbWF0aWNhbCBmb3VuZGF0aW9uIG9mIE9MUy4gSSBhbHNvIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYW5kIHZlcmlmeSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY29ycmVsYXRpb24gYW5kIHRoZSByZWdyZXNzaW9uIHNsb3BlLiBUaGUgbG0oKSBmdW5jdGlvbiBpcyB0aGVuIHVzZWQgdG8gY29uZmlybSB0aGUgcmVzdWx0cyBhbmQgb2J0YWluIGFkZGl0aW9uYWwgc3RhdGlzdGljcyBzdWNoIGFzIFItc3F1YXJlZCBhbmQgcC12YWx1ZXMuIEEgY3VzdG9tIHJlZ3Jlc3Npb24gZnVuY3Rpb24gaXMgaW1wbGVtZW50ZWQgdXNpbmcgdGhlIG1hdHJpeCBmb3JtdWxhLCBzaG93aW5nIHRoZSBhbGdlYnJhaWMgc3RydWN0dXJlIGJlaGluZCBtdWx0aXBsZSByZWdyZXNzaW9uLg0KDQojIyBVbmRlcnN0YW5kaW5nIHJlZ3Jlc3Npb24NCg0KDQoNCmBgYHtyfQ0KIyMgRXhhbXBsZTogU3BhY2UgU2h1dHRsZSBMYXVuY2ggRGF0YQ0KbGF1bmNoIDwtIHJlYWQuY3N2KCJjaGFsbGVuZ2VyLmNzdiIpDQpgYGANCg0KDQpgYGB7cn0NCiMgZXN0aW1hdGUgYmV0YSBtYW51YWxseQ0KYiA8LSBjb3YobGF1bmNoJHRlbXBlcmF0dXJlLCBsYXVuY2gkZGlzdHJlc3NfY3QpIC8gdmFyKGxhdW5jaCR0ZW1wZXJhdHVyZSkNCmINCmBgYA0KDQoNCmBgYHtyfQ0KIyBlc3RpbWF0ZSBhbHBoYSBtYW51YWxseQ0KYSA8LSBtZWFuKGxhdW5jaCRkaXN0cmVzc19jdCkgLSBiICogbWVhbihsYXVuY2gkdGVtcGVyYXR1cmUpDQphDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIG9mIGxhdW5jaCBkYXRhDQpyIDwtIGNvdihsYXVuY2gkdGVtcGVyYXR1cmUsIGxhdW5jaCRkaXN0cmVzc19jdCkgLw0KICAgICAgIChzZChsYXVuY2gkdGVtcGVyYXR1cmUpICogc2QobGF1bmNoJGRpc3RyZXNzX2N0KSkNCnINCmBgYA0KDQoNCmBgYHtyfQ0KY29yKGxhdW5jaCR0ZW1wZXJhdHVyZSwgbGF1bmNoJGRpc3RyZXNzX2N0KQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KIyBjb21wdXRpbmcgdGhlIHNsb3BlIHVzaW5nIGNvcnJlbGF0aW9uDQpyICogKHNkKGxhdW5jaCRkaXN0cmVzc19jdCkgLyBzZChsYXVuY2gkdGVtcGVyYXR1cmUpKQ0KYGBgDQoNCg0KYGBge3J9DQojIGNvbmZpcm1pbmcgdGhlIHJlZ3Jlc3Npb24gbGluZSB1c2luZyB0aGUgbG0gZnVuY3Rpb24gKG5vdCBpbiB0ZXh0KQ0KbW9kZWwgPC0gbG0oZGlzdHJlc3NfY3QgfiB0ZW1wZXJhdHVyZSwgZGF0YSA9IGxhdW5jaCkNCm1vZGVsDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc3VtbWFyeShtb2RlbCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjcmVhdGluZyBhIHNpbXBsZSBtdWx0aXBsZSByZWdyZXNzaW9uIGZ1bmN0aW9uDQpyZWcgPC0gZnVuY3Rpb24oeSwgeCkgew0KICB4IDwtIGFzLm1hdHJpeCh4KQ0KICB4IDwtIGNiaW5kKEludGVyY2VwdCA9IDEsIHgpDQogIGIgPC0gc29sdmUodCh4KSAlKiUgeCkgJSolIHQoeCkgJSolIHkNCiAgY29sbmFtZXMoYikgPC0gImVzdGltYXRlIg0KICBwcmludChiKQ0KfQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KIyBleGFtaW5lIHRoZSBsYXVuY2ggZGF0YQ0Kc3RyKGxhdW5jaCkNCmBgYA0KDQoNCg0KYGBge3J9DQojIHRlc3QgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbg0KcmVnKHkgPSBsYXVuY2gkZGlzdHJlc3NfY3QsIHggPSBsYXVuY2hbMl0pDQpgYGANCg0KDQpgYGB7cn0NCiMgdXNlIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBtdWx0aXBsZSByZWdyZXNzaW9uDQpyZWcoeSA9IGxhdW5jaCRkaXN0cmVzc19jdCwgeCA9IGxhdW5jaFsyOjRdKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY29uZmlybWluZyB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbiByZXN1bHQgdXNpbmcgdGhlIGxtIGZ1bmN0aW9uIChub3QgaW4gdGV4dCkNCm1vZGVsIDwtIGxtKGRpc3RyZXNzX2N0IH4gdGVtcGVyYXR1cmUgKyBmaWVsZF9jaGVja19wcmVzc3VyZSArIGZsaWdodF9udW0sIGRhdGEgPSBsYXVuY2gpDQptb2RlbA0KYGBgDQoNCg0KIyMgUHJlZGljdGluZyBNZWRpY2FsIEV4cGVuc2VzDQoNCmBgYHtyfQ0KIyMgU3RlcCAyOiBFeHBsb3JpbmcgYW5kIHByZXBhcmluZyB0aGUgZGF0YSAtLS0tDQppbnN1cmFuY2UgPC0gcmVhZC5jc3YoImluc3VyYW5jZS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gVFJVRSkNCnN0cihpbnN1cmFuY2UpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBzdW1tYXJpemUgdGhlIGNoYXJnZXMgdmFyaWFibGUNCnN1bW1hcnkoaW5zdXJhbmNlJGV4cGVuc2VzKQ0KYGBgDQoNCg0KYGBge3J9DQojIGhpc3RvZ3JhbSBvZiBpbnN1cmFuY2UgY2hhcmdlcw0KaGlzdChpbnN1cmFuY2UkZXhwZW5zZXMpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyB0YWJsZSBvZiByZWdpb24NCnRhYmxlKGluc3VyYW5jZSRyZWdpb24pDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBleHBsb3JpbmcgcmVsYXRpb25zaGlwcyBhbW9uZyBmZWF0dXJlczogY29ycmVsYXRpb24gbWF0cml4DQpjb3IoaW5zdXJhbmNlW2MoImFnZSIsICJibWkiLCAiY2hpbGRyZW4iLCAiZXhwZW5zZXMiKV0pDQpgYGANCg0KDQpgYGB7cn0NCiMgdmlzdWFsaW5nIHJlbGF0aW9uc2hpcHMgYW1vbmcgZmVhdHVyZXM6IHNjYXR0ZXJwbG90IG1hdHJpeA0KcGFpcnMoaW5zdXJhbmNlW2MoImFnZSIsICJibWkiLCAiY2hpbGRyZW4iLCAiZXhwZW5zZXMiKV0pDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIyBTdGVwIDM6IFRyYWluaW5nIGEgbW9kZWwgb24gdGhlIGRhdGEgLS0tLQ0KaW5zX21vZGVsIDwtIGxtKGV4cGVuc2VzIH4gYWdlICsgY2hpbGRyZW4gKyBibWkgKyBzZXggKyBzbW9rZXIgKyByZWdpb24sDQogICAgICAgICAgICAgICAgZGF0YSA9IGluc3VyYW5jZSkNCmluc19tb2RlbCA8LSBsbShleHBlbnNlcyB+IC4sIGRhdGEgPSBpbnN1cmFuY2UpICMgdGhpcyBpcyBlcXVpdmFsZW50IHRvIGFib3ZlDQoNCiMgc2VlIHRoZSBlc3RpbWF0ZWQgYmV0YSBjb2VmZmljaWVudHMNCmluc19tb2RlbA0KYGBgDQoNCg0KIyMgU3RlcCA0OiBFdmFsdWF0aW5nIG1vZGVsIHBlcmZvcm1hbmNlDQoNCmBgYHtyfQ0KIyBzZWUgbW9yZSBkZXRhaWwgYWJvdXQgdGhlIGVzdGltYXRlZCBiZXRhIGNvZWZmaWNpZW50cw0Kc3VtbWFyeShpbnNfbW9kZWwpDQpgYGANCg0KDQojIyBTdGVwIDU6IEltcHJvdmluZyBtb2RlbCBwZXJmb3JtYW5jZQ0KDQoNCg0KYGBge3J9DQojIGFkZCBhIGhpZ2hlci1vcmRlciAiYWdlIiB0ZXJtDQppbnN1cmFuY2UkYWdlMiA8LSBpbnN1cmFuY2UkYWdlXjINCmBgYA0KDQoNCg0KYGBge3J9DQojIGFkZCBhbiBpbmRpY2F0b3IgZm9yIEJNSSA+PSAzMA0KaW5zdXJhbmNlJGJtaTMwIDwtIGlmZWxzZShpbnN1cmFuY2UkYm1pID49IDMwLCAxLCAwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY3JlYXRlIGZpbmFsIG1vZGVsDQppbnNfbW9kZWwyIDwtIGxtKGV4cGVuc2VzIH4gYWdlICsgYWdlMiArIGNoaWxkcmVuICsgYm1pICsgc2V4ICsNCiAgICAgICAgICAgICAgICAgICBibWkzMCpzbW9rZXIgKyByZWdpb24sIGRhdGEgPSBpbnN1cmFuY2UpDQpgYGANCg0KDQpgYGB7cn0NCnN1bW1hcnkoaW5zX21vZGVsMikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBtYWtpbmcgcHJlZGljdGlvbnMgd2l0aCB0aGUgcmVncmVzc2lvbiBtb2RlbA0KaW5zdXJhbmNlJHByZWQgPC0gcHJlZGljdChpbnNfbW9kZWwyLCBpbnN1cmFuY2UpDQpjb3IoaW5zdXJhbmNlJHByZWQsIGluc3VyYW5jZSRleHBlbnNlcykNCmBgYA0KDQoNCg0KYGBge3J9DQpwbG90KGluc3VyYW5jZSRwcmVkLCBpbnN1cmFuY2UkZXhwZW5zZXMpDQphYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSAicmVkIiwgbHdkID0gMywgbHR5ID0gMikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCnByZWRpY3QoaW5zX21vZGVsMiwNCiAgICAgICAgZGF0YS5mcmFtZShhZ2UgPSAzMCwgYWdlMiA9IDMwXjIsIGNoaWxkcmVuID0gMiwNCiAgICAgICAgICAgICAgICAgICBibWkgPSAzMCwgc2V4ID0gIm1hbGUiLCBibWkzMCA9IDEsDQogICAgICAgICAgICAgICAgICAgc21va2VyID0gIm5vIiwgcmVnaW9uID0gIm5vcnRoZWFzdCIpKQ0KYGBgDQoNCg0KYGBge3J9DQpwcmVkaWN0KGluc19tb2RlbDIsDQogICAgICAgIGRhdGEuZnJhbWUoYWdlID0gMzAsIGFnZTIgPSAzMF4yLCBjaGlsZHJlbiA9IDIsDQogICAgICAgICAgICAgICAgICAgYm1pID0gMzAsIHNleCA9ICJmZW1hbGUiLCBibWkzMCA9IDEsDQogICAgICAgICAgICAgICAgICAgc21va2VyID0gIm5vIiwgcmVnaW9uID0gIm5vcnRoZWFzdCIpKQ0KYGBgDQoNCg0KYGBge3J9DQpwcmVkaWN0KGluc19tb2RlbDIsDQogICAgICAgIGRhdGEuZnJhbWUoYWdlID0gMzAsIGFnZTIgPSAzMF4yLCBjaGlsZHJlbiA9IDAsDQogICAgICAgICAgICAgICAgICAgYm1pID0gMzAsIHNleCA9ICJmZW1hbGUiLCBibWkzMCA9IDEsDQogICAgICAgICAgICAgICAgICAgc21va2VyID0gIm5vIiwgcmVnaW9uID0gIm5vcnRoZWFzdCIpKQ0KYGBgDQoNCg0KDQojIyMjIFBhcnQgMjogUmVncmVzc2lvbiBUcmVlcyBhbmQgTW9kZWwgVHJlZXMNCg0KIyMjI0kgYmVnaW4gd2l0aCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIHRvIHVuZGVyc3RhbmQgdmFyaWFibGUgZGlzdHJpYnV0aW9ucyBhbmQgcmVsYXRpb25zaGlwcy4gQSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpcyBidWlsdCB0byBlc3RpbWF0ZSBob3cgZGVtb2dyYXBoaWMgYW5kIGhlYWx0aC1yZWxhdGVkIGZhY3RvcnMgaW5mbHVlbmNlIG1lZGljYWwgZXhwZW5zZXMuIFRvIGltcHJvdmUgdGhlIG1vZGVsLCBJIGFkZCBhIHF1YWRyYXRpYyBhZ2UgdGVybSwgYSBCTUkgaW5kaWNhdG9yIHZhcmlhYmxlLCBhbmQgYW4gaW50ZXJhY3Rpb24gYmV0d2VlbiBCTUkgYW5kIHNtb2tpbmcgc3RhdHVzIHRvIGNhcHR1cmUgbm9ubGluZWFyIGFuZCBjb21iaW5lZCBlZmZlY3RzLiBNb2RlbCBwZXJmb3JtYW5jZSBpcyBldmFsdWF0ZWQgdXNpbmcgY29ycmVsYXRpb24gYW5kIHByZWRpY3Rpb24gYWNjdXJhY3kNCg0KIyMgVW5kZXJzdGFuZGluZyByZWdyZXNzaW9uIHRyZWVzIGFuZCBtb2RlbCB0cmVlcw0KDQojIyBFeGFtcGxlOiBDYWxjdWxhdGluZyBTRFINCg0KYGBge3J9DQojIHNldCB1cCB0aGUgZGF0YQ0KdGVlIDwtIGMoMSwgMSwgMSwgMiwgMiwgMywgNCwgNSwgNSwgNiwgNiwgNywgNywgNywgNykNCmF0MSA8LSBjKDEsIDEsIDEsIDIsIDIsIDMsIDQsIDUsIDUpDQphdDIgPC0gYyg2LCA2LCA3LCA3LCA3LCA3KQ0KYnQxIDwtIGMoMSwgMSwgMSwgMiwgMiwgMywgNCkNCmJ0MiA8LSBjKDUsIDUsIDYsIDYsIDcsIDcsIDcsIDcpDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIGNvbXB1dGUgdGhlIFNEUg0Kc2RyX2EgPC0gc2QodGVlKSAtIChsZW5ndGgoYXQxKSAvIGxlbmd0aCh0ZWUpICogc2QoYXQxKSArIGxlbmd0aChhdDIpIC8gbGVuZ3RoKHRlZSkgKiBzZChhdDIpKQ0Kc2RyX2IgPC0gc2QodGVlKSAtIChsZW5ndGgoYnQxKSAvIGxlbmd0aCh0ZWUpICogc2QoYnQxKSArIGxlbmd0aChidDIpIC8gbGVuZ3RoKHRlZSkgKiBzZChidDIpKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY29tcGFyZSB0aGUgU0RSIGZvciBlYWNoIHNwbGl0DQpzZHJfYQ0Kc2RyX2INCmBgYA0KDQoNCg0KIyMgRXhlcmNpc2UgTm8gMzogRXN0aW1hdGluZyBXaW5lIFF1YWxpdHkNCg0KIyMjI1RoZSBkYXRhc2V0IGlzIHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyB0byBldmFsdWF0ZSBvdXQtb2Ytc2FtcGxlIHBlcmZvcm1hbmNlLiBBIHJlZ3Jlc3Npb24gdHJlZSBpcyB0cmFpbmVkIHVzaW5nIHJwYXJ0LCBhbmQgcGVyZm9ybWFuY2UgaXMgbWVhc3VyZWQgdXNpbmcgY29ycmVsYXRpb24gYW5kIE1lYW4gQWJzb2x1dGUgRXJyb3IgKE1BRSkuIFRvIGltcHJvdmUgcHJlZGljdGlvbnMsIGEgQ3ViaXN0IG1vZGVsIHRyZWUgaXMgaW1wbGVtZW50ZWQsIGNvbWJpbmluZyBkZWNpc2lvbiBydWxlcyB3aXRoIGxpbmVhciByZWdyZXNzaW9uIGF0IHRoZSBsZWF2ZXMuIFRoZSByZXN1bHRzIGFyZSBjb21wYXJlZCB0byBhc3Nlc3Mgd2hpY2ggYXBwcm9hY2ggcHJvdmlkZXMgYmV0dGVyIHByZWRpY3RpdmUgYWNjdXJhY3kuDQoNCg0KIyMgU3RlcCAyOiBFeHBsb3JpbmcgYW5kIHByZXBhcmluZyB0aGUgZGF0YQ0KDQpgYGB7cn0NCndpbmUgPC0gcmVhZC5jc3YoIndoaXRld2luZXMuY3N2IikNCmBgYA0KDQoNCg0KYGBge3J9DQojIGV4YW1pbmUgdGhlIHdpbmUgZGF0YQ0Kc3RyKHdpbmUpDQpgYGANCg0KDQpgYGB7cn0NCiMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBxdWFsaXR5IHJhdGluZ3MNCmhpc3Qod2luZSRxdWFsaXR5KQ0KYGBgDQoNCg0KYGBge3J9DQojIHN1bW1hcnkgc3RhdGlzdGljcyBvZiB0aGUgd2luZSBkYXRhDQpzdW1tYXJ5KHdpbmUpDQpgYGANCg0KDQoNCmBgYHtyfQ0Kd2luZV90cmFpbiA8LSB3aW5lWzE6Mzc1MCwgXQ0Kd2luZV90ZXN0IDwtIHdpbmVbMzc1MTo0ODk4LCBdDQpgYGANCg0KDQoNCiMjIFN0ZXAgMzogVHJhaW5pbmcgYSBtb2RlbCBvbiB0aGUgZGF0YQ0KDQpgYGB7cn0NCiMgcmVncmVzc2lvbiB0cmVlIHVzaW5nIHJwYXJ0DQpsaWJyYXJ5KHJwYXJ0KQ0KbS5ycGFydCA8LSBycGFydChxdWFsaXR5IH4gLiwgZGF0YSA9IHdpbmVfdHJhaW4pDQpgYGANCg0KDQpgYGB7cn0NCiMgZ2V0IGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IHRoZSB0cmVlDQptLnJwYXJ0DQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBnZXQgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdHJlZQ0Kc3VtbWFyeShtLnJwYXJ0KQ0KYGBgDQoNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmBgYA0KDQoNCmBgYHtyfQ0KIyB1c2UgdGhlIHJwYXJ0LnBsb3QgcGFja2FnZSB0byBjcmVhdGUgYSB2aXN1YWxpemF0aW9uDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQpgYGANCg0KDQpgYGB7cn0NCiMgYSBiYXNpYyBkZWNpc2lvbiB0cmVlIGRpYWdyYW0NCnJwYXJ0LnBsb3QobS5ycGFydCwgZGlnaXRzID0gMykNCmBgYA0KDQoNCmBgYHtyfQ0KIyBhIGZldyBhZGp1c3RtZW50cyB0byB0aGUgZGlhZ3JhbQ0KcnBhcnQucGxvdChtLnJwYXJ0LCBkaWdpdHMgPSA0LCBmYWxsZW4ubGVhdmVzID0gVFJVRSwgdHlwZSA9IDMsIGV4dHJhID0gMTAxKQ0KYGBgDQoNCg0KIyMgU3RlcCA0OiBFdmFsdWF0ZSBtb2RlbCBwZXJmb3JtYW5jDQoNCmBgYHtyfQ0KIyBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHRlc3RpbmcgZGF0YXNldA0KcC5ycGFydCA8LSBwcmVkaWN0KG0ucnBhcnQsIHdpbmVfdGVzdCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjb21wYXJlIHRoZSBkaXN0cmlidXRpb24gb2YgcHJlZGljdGVkIHZhbHVlcyB2cy4gYWN0dWFsIHZhbHVlcw0Kc3VtbWFyeShwLnJwYXJ0KQ0Kc3VtbWFyeSh3aW5lX3Rlc3QkcXVhbGl0eSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjb21wYXJlIHRoZSBjb3JyZWxhdGlvbg0KY29yKHAucnBhcnQsIHdpbmVfdGVzdCRxdWFsaXR5KQ0KYGBgDQoNCg0KYGBge3J9DQojIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiBhYnNvbHV0ZSBlcnJvcg0KTUFFIDwtIGZ1bmN0aW9uKGFjdHVhbCwgcHJlZGljdGVkKSB7DQogIG1lYW4oYWJzKGFjdHVhbCAtIHByZWRpY3RlZCkpICANCn0NCmBgYA0KDQoNCg0KYGBge3J9DQojIG1lYW4gYWJzb2x1dGUgZXJyb3IgYmV0d2VlbiBwcmVkaWN0ZWQgYW5kIGFjdHVhbCB2YWx1ZXMNCk1BRShwLnJwYXJ0LCB3aW5lX3Rlc3QkcXVhbGl0eSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBtZWFuIGFic29sdXRlIGVycm9yIGJldHdlZW4gYWN0dWFsIHZhbHVlcyBhbmQgbWVhbiB2YWx1ZQ0KbWVhbih3aW5lX3RyYWluJHF1YWxpdHkpICMgcmVzdWx0ID0gNS44Nw0KTUFFKDUuODcsIHdpbmVfdGVzdCRxdWFsaXR5KQ0KYGBgDQoNCg0KIyMgU3RlcCA1OiBJbXByb3ZpbmcgbW9kZWwgcGVyZm9ybWFuY2UNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygicGx5ciIpDQojaW5zdGFsbC5wYWNrYWdlcygiQ3ViaXN0IikNCmBgYA0KDQoNCmBgYHtyfQ0KIyB0cmFpbiBhIEN1YmlzdCBNb2RlbCBUcmVlDQpsaWJyYXJ5KEN1YmlzdCkNCm0uY3ViaXN0IDwtIGN1YmlzdCh4ID0gd2luZV90cmFpblstMTJdLCB5ID0gd2luZV90cmFpbiRxdWFsaXR5KQ0KYGBgDQoNCg0KYGBge3J9DQojIGRpc3BsYXkgYmFzaWMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIG1vZGVsIHRyZWUNCm0uY3ViaXN0DQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBkaXNwbGF5IHRoZSB0cmVlIGl0c2VsZg0Kc3VtbWFyeShtLmN1YmlzdCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3IgdGhlIG1vZGVsDQpwLmN1YmlzdCA8LSBwcmVkaWN0KG0uY3ViaXN0LCB3aW5lX3Rlc3QpDQpgYGANCg0KDQpgYGB7cn0NCiMgc3VtbWFyeSBzdGF0aXN0aWNzIGFib3V0IHRoZSBwcmVkaWN0aW9ucw0Kc3VtbWFyeShwLmN1YmlzdCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgYW5kIHRydWUgdmFsdWVzDQpjb3IocC5jdWJpc3QsIHdpbmVfdGVzdCRxdWFsaXR5KQ0KYGBgDQoNCg0KYGBge3J9DQojIG1lYW4gYWJzb2x1dGUgZXJyb3Igb2YgcHJlZGljdGVkIGFuZCB0cnVlIHZhbHVlcw0KIyAodXNlcyBhIGN1c3RvbSBmdW5jdGlvbiBkZWZpbmVkIGFib3ZlKQ0KTUFFKHdpbmVfdGVzdCRxdWFsaXR5LCBwLmN1YmlzdCkgDQpgYGANCg0KDQoNCg==