Part 1: Linear 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
[1] -0.04753968
# estimate alpha manually
a <- mean(launch$distress_ct) - b * mean(launch$temperature)
a
[1] 3.698413
# calculate the correlation of launch data
r <- cov(launch$temperature, launch$distress_ct) /
       (sd(launch$temperature) * sd(launch$distress_ct))
r
[1] -0.5111264
cor(launch$temperature, launch$distress_ct)
[1] -0.5111264
# computing the slope using correlation
r * (sd(launch$distress_ct) / sd(launch$temperature))
[1] -0.04753968
# confirming the regression line using the lm function (not in text)
model <- lm(distress_ct ~ temperature, data = launch)
model

Call:
lm(formula = distress_ct ~ temperature, data = launch)

Coefficients:
(Intercept)  temperature  
    3.69841     -0.04754  
summary(model)

Call:
lm(formula = distress_ct ~ temperature, data = launch)

Residuals:
    Min      1Q  Median      3Q     Max 
-0.5608 -0.3944 -0.0854  0.1056  1.8671 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)   
(Intercept)  3.69841    1.21951   3.033  0.00633 **
temperature -0.04754    0.01744  -2.725  0.01268 * 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5774 on 21 degrees of freedom
Multiple R-squared:  0.2613,    Adjusted R-squared:  0.2261 
F-statistic: 7.426 on 1 and 21 DF,  p-value: 0.01268
# 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)
'data.frame':   23 obs. of  4 variables:
 $ distress_ct         : int  0 1 0 0 0 0 0 0 1 1 ...
 $ temperature         : int  66 70 69 68 67 72 73 70 57 63 ...
 $ field_check_pressure: int  50 50 50 50 50 50 100 100 200 200 ...
 $ flight_num          : int  1 2 3 4 5 6 7 8 9 10 ...
# test regression model with simple linear regression
reg(y = launch$distress_ct, x = launch[2])
               estimate
Intercept    3.69841270
temperature -0.04753968
# use regression model with multiple regression
reg(y = launch$distress_ct, x = launch[2:4])
                         estimate
Intercept             3.527093383
temperature          -0.051385940
field_check_pressure  0.001757009
flight_num            0.014292843
# 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

Call:
lm(formula = distress_ct ~ temperature + field_check_pressure + 
    flight_num, data = launch)

Coefficients:
         (Intercept)           temperature  field_check_pressure            flight_num  
            3.527093             -0.051386              0.001757              0.014293  

Predicting Medical Expenses

## Step 2: Exploring and preparing the data ----
insurance <- read.csv("insurance.csv", stringsAsFactors = TRUE)
str(insurance)
'data.frame':   1338 obs. of  7 variables:
 $ age     : int  19 18 28 33 32 31 46 37 37 60 ...
 $ sex     : Factor w/ 2 levels "female","male": 1 2 2 2 2 1 1 1 2 1 ...
 $ bmi     : num  27.9 33.8 33 22.7 28.9 25.7 33.4 27.7 29.8 25.8 ...
 $ children: int  0 1 3 0 0 0 1 3 2 0 ...
 $ smoker  : Factor w/ 2 levels "no","yes": 2 1 1 1 1 1 1 1 1 1 ...
 $ region  : Factor w/ 4 levels "northeast","northwest",..: 4 3 3 2 2 3 3 2 1 2 ...
 $ expenses: num  16885 1726 4449 21984 3867 ...

summary(model)

Call:
lm(formula = distress_ct ~ temperature + field_check_pressure + 
    flight_num, data = launch)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.65003 -0.24414 -0.11219  0.01279  1.67530 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)  
(Intercept)           3.527093   1.307024   2.699   0.0142 *
temperature          -0.051386   0.018341  -2.802   0.0114 *
field_check_pressure  0.001757   0.003402   0.517   0.6115  
flight_num            0.014293   0.035138   0.407   0.6887  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.565 on 19 degrees of freedom
Multiple R-squared:   0.36, Adjusted R-squared:  0.259 
F-statistic: 3.563 on 3 and 19 DF,  p-value: 0.03371
# summarize the charges variable
summary(insurance$expenses)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1122    4740    9382   13270   16640   63770 
# histogram of insurance charges
hist(insurance$expenses)

# table of region
table(insurance$region)

northeast northwest southeast southwest 
      324       325       364       325 

#vif

# exploring relationships among features: correlation matrix
cor(insurance[c("age", "bmi", "children", "expenses")])
               age        bmi   children   expenses
age      1.0000000 0.10934101 0.04246900 0.29900819
bmi      0.1093410 1.00000000 0.01264471 0.19857626
children 0.0424690 0.01264471 1.00000000 0.06799823
expenses 0.2990082 0.19857626 0.06799823 1.00000000
# 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

Call:
lm(formula = expenses ~ ., data = insurance)

Coefficients:
    (Intercept)              age          sexmale              bmi         children        smokeryes  
      8.367e-11       -1.613e-12        4.110e-13        6.429e-13        1.558e-12       -4.786e-11  
regionnorthwest  regionsoutheast  regionsouthwest             age2            bmi30             pred  
     -4.927e-13        3.777e-12       -2.305e-12        5.350e-15       -4.001e-12        1.000e+00  

Step 4: Evaluating model performance

# see more detail about the estimated beta coefficients
summary(ins_model)

Call:
lm(formula = expenses ~ ., data = insurance)

Residuals:
     Min       1Q   Median       3Q      Max 
-11302.7  -2850.9   -979.6   1383.9  29981.7 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)     -11941.6      987.8 -12.089  < 2e-16 ***
age                256.8       11.9  21.586  < 2e-16 ***
sexmale           -131.3      332.9  -0.395 0.693255    
bmi                339.3       28.6  11.864  < 2e-16 ***
children           475.7      137.8   3.452 0.000574 ***
smokeryes        23847.5      413.1  57.723  < 2e-16 ***
regionnorthwest   -352.8      476.3  -0.741 0.458976    
regionsoutheast  -1035.6      478.7  -2.163 0.030685 *  
regionsouthwest   -959.3      477.9  -2.007 0.044921 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6062 on 1329 degrees of freedom
Multiple R-squared:  0.7509,    Adjusted R-squared:  0.7494 
F-statistic: 500.9 on 8 and 1329 DF,  p-value: < 2.2e-16

#If the absolute value of T is greater than 2 is significiant, if the p value is greater than 0.05 is significant. #Gender is not sginificant because the valuabes are more than the significants values #bmi is significant but the lowest one in the data frame #Being a smoke person is the most signigicant in terms of the medical expenses. #The analogy of the accuracy is the R squared. in this case is 75%. The data is not actual, so we cannot consider this as accurate.

Step 5: Improving model performance

# add a higher-order "age" term
insurance$age2 <- insurance$age^2

#Adding the value AGE. #In the case of bmi the age of 30 is very important. #Equal greater or lower to 30.Creating a new categorical. converting numerical into categorical.

# 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)

Call:
lm(formula = expenses ~ age + age2 + children + bmi + sex + bmi30 * 
    smoker + region, data = insurance)

Residuals:
     Min       1Q   Median       3Q      Max 
-17297.1  -1656.0  -1262.7   -727.8  24161.6 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)       139.0053  1363.1359   0.102 0.918792    
age               -32.6181    59.8250  -0.545 0.585690    
age2                3.7307     0.7463   4.999 6.54e-07 ***
children          678.6017   105.8855   6.409 2.03e-10 ***
bmi               119.7715    34.2796   3.494 0.000492 ***
sexmale          -496.7690   244.3713  -2.033 0.042267 *  
bmi30            -997.9355   422.9607  -2.359 0.018449 *  
smokeryes       13404.5952   439.9591  30.468  < 2e-16 ***
regionnorthwest  -279.1661   349.2826  -0.799 0.424285    
regionsoutheast  -828.0345   351.6484  -2.355 0.018682 *  
regionsouthwest -1222.1619   350.5314  -3.487 0.000505 ***
bmi30:smokeryes 19810.1534   604.6769  32.762  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4445 on 1326 degrees of freedom
Multiple R-squared:  0.8664,    Adjusted R-squared:  0.8653 
F-statistic: 781.7 on 11 and 1326 DF,  p-value: < 2.2e-16
# making predictions with the regression model
insurance$pred <- predict(ins_model2, insurance)
cor(insurance$pred, insurance$expenses)
[1] 0.9307999

#Sort of accuracy of how accurate is predctions vs expernses. #Correlation of what I predict

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"))
       1 
5973.774 

#That is how much is going to pay with all the variables that they put in the code.

#New scenario

predict(ins_model2,
        data.frame(age = 50, age2 = 50^2, children = 3,
                   bmi = 50, sex = "female", bmi30 = 1,
                   smoker = "no", region = "northeast"))
      1 
14861.4 

#New scenario #Case 1

predict(ins_model2,
        data.frame(age = 22, age2 = 22^2, children = 3,
                   bmi = 24, sex = "female", bmi30 = 0,
                   smoker = "no", region = "northwest"))
       1 
5858.241 

#New scenario #Case 2.

predict(ins_model2,
        data.frame(age = 22, age2 = 22^2, children = 1,
                   bmi = 27, sex = "male", bmi30 = 0,
                   smoker = "yes", region = "southeast"))
       1 
17219.31 
predict(ins_model2,
        data.frame(age = 30, age2 = 30^2, children = 2,
                   bmi = 30, sex = "female", bmi30 = 1,
                   smoker = "no", region = "northeast"))
       1 
6470.543 

#They are going to pay more if they are female.

predict(ins_model2,
        data.frame(age = 30, age2 = 30^2, children = 0,
                   bmi = 30, sex = "female", bmi30 = 1,
                   smoker = "no", region = "northeast"))
      1 
5113.34 

Part 2: Regression Trees and Model Trees

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
[1] 1.202815
sdr_b
[1] 1.392751

Exercise No 3: Estimating Wine Quality

Step 2: Exploring and preparing the data

wine <- read.csv("whitewines.csv")
# examine the wine data
str(wine)
'data.frame':   4898 obs. of  12 variables:
 $ fixed.acidity       : num  6.7 5.7 5.9 5.3 6.4 7 7.9 6.6 7 6.5 ...
 $ volatile.acidity    : num  0.62 0.22 0.19 0.47 0.29 0.14 0.12 0.38 0.16 0.37 ...
 $ citric.acid         : num  0.24 0.2 0.26 0.1 0.21 0.41 0.49 0.28 0.3 0.33 ...
 $ residual.sugar      : num  1.1 16 7.4 1.3 9.65 0.9 5.2 2.8 2.6 3.9 ...
 $ chlorides           : num  0.039 0.044 0.034 0.036 0.041 0.037 0.049 0.043 0.043 0.027 ...
 $ free.sulfur.dioxide : num  6 41 33 11 36 22 33 17 34 40 ...
 $ total.sulfur.dioxide: num  62 113 123 74 119 95 152 67 90 130 ...
 $ density             : num  0.993 0.999 0.995 0.991 0.993 ...
 $ pH                  : num  3.41 3.22 3.49 3.48 2.99 3.25 3.18 3.21 2.88 3.28 ...
 $ sulphates           : num  0.32 0.46 0.42 0.54 0.34 0.43 0.47 0.47 0.47 0.39 ...
 $ alcohol             : num  10.4 8.9 10.1 11.2 10.9 ...
 $ quality             : int  5 6 6 4 6 6 6 6 6 7 ...
# the distribution of quality ratings
hist(wine$quality)

# summary statistics of the wine data
summary(wine)
 fixed.acidity    volatile.acidity  citric.acid     residual.sugar     chlorides       free.sulfur.dioxide
 Min.   : 3.800   Min.   :0.0800   Min.   :0.0000   Min.   : 0.600   Min.   :0.00900   Min.   :  2.00     
 1st Qu.: 6.300   1st Qu.:0.2100   1st Qu.:0.2700   1st Qu.: 1.700   1st Qu.:0.03600   1st Qu.: 23.00     
 Median : 6.800   Median :0.2600   Median :0.3200   Median : 5.200   Median :0.04300   Median : 34.00     
 Mean   : 6.855   Mean   :0.2782   Mean   :0.3342   Mean   : 6.391   Mean   :0.04577   Mean   : 35.31     
 3rd Qu.: 7.300   3rd Qu.:0.3200   3rd Qu.:0.3900   3rd Qu.: 9.900   3rd Qu.:0.05000   3rd Qu.: 46.00     
 Max.   :14.200   Max.   :1.1000   Max.   :1.6600   Max.   :65.800   Max.   :0.34600   Max.   :289.00     
 total.sulfur.dioxide    density             pH          sulphates         alcohol         quality     
 Min.   :  9.0        Min.   :0.9871   Min.   :2.720   Min.   :0.2200   Min.   : 8.00   Min.   :3.000  
 1st Qu.:108.0        1st Qu.:0.9917   1st Qu.:3.090   1st Qu.:0.4100   1st Qu.: 9.50   1st Qu.:5.000  
 Median :134.0        Median :0.9937   Median :3.180   Median :0.4700   Median :10.40   Median :6.000  
 Mean   :138.4        Mean   :0.9940   Mean   :3.188   Mean   :0.4898   Mean   :10.51   Mean   :5.878  
 3rd Qu.:167.0        3rd Qu.:0.9961   3rd Qu.:3.280   3rd Qu.:0.5500   3rd Qu.:11.40   3rd Qu.:6.000  
 Max.   :440.0        Max.   :1.0390   Max.   :3.820   Max.   :1.0800   Max.   :14.20   Max.   :9.000  
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
n= 3750 

node), split, n, deviance, yval
      * denotes terminal node

 1) root 3750 2945.53200 5.870933  
   2) alcohol< 10.85 2372 1418.86100 5.604975  
     4) volatile.acidity>=0.2275 1611  821.30730 5.432030  
       8) volatile.acidity>=0.3025 688  278.97670 5.255814 *
       9) volatile.acidity< 0.3025 923  505.04230 5.563380 *
     5) volatile.acidity< 0.2275 761  447.36400 5.971091 *
   3) alcohol>=10.85 1378 1070.08200 6.328737  
     6) free.sulfur.dioxide< 10.5 84   95.55952 5.369048 *
     7) free.sulfur.dioxide>=10.5 1294  892.13600 6.391036  
      14) alcohol< 11.76667 629  430.11130 6.173291  
        28) volatile.acidity>=0.465 11   10.72727 4.545455 *
        29) volatile.acidity< 0.465 618  389.71680 6.202265 *
      15) alcohol>=11.76667 665  403.99400 6.596992 *
# get more detailed information about the tree
summary(m.rpart)
Call:
rpart(formula = quality ~ ., data = wine_train)
  n= 3750 

          CP nsplit rel error    xerror       xstd
1 0.15501053      0 1.0000000 1.0003241 0.02446341
2 0.05098911      1 0.8449895 0.8456302 0.02333695
3 0.02796998      2 0.7940004 0.8030360 0.02269799
4 0.01970128      3 0.7660304 0.7778053 0.02148777
5 0.01265926      4 0.7463291 0.7589862 0.02071159
6 0.01007193      5 0.7336698 0.7501439 0.02057169
7 0.01000000      6 0.7235979 0.7474330 0.02054184

Variable importance
             alcohol              density     volatile.acidity            chlorides total.sulfur.dioxide 
                  34                   21                   15                   11                    7 
 free.sulfur.dioxide       residual.sugar            sulphates          citric.acid 
                   6                    3                    1                    1 

Node number 1: 3750 observations,    complexity param=0.1550105
  mean=5.870933, MSE=0.7854751 
  left son=2 (2372 obs) right son=3 (1378 obs)
  Primary splits:
      alcohol              < 10.85    to the left,  improve=0.15501050, (0 missing)
      density              < 0.992035 to the right, improve=0.10915940, (0 missing)
      chlorides            < 0.0395   to the right, improve=0.07682258, (0 missing)
      total.sulfur.dioxide < 158.5    to the right, improve=0.04089663, (0 missing)
      citric.acid          < 0.235    to the left,  improve=0.03636458, (0 missing)
  Surrogate splits:
      density              < 0.991995 to the right, agree=0.869, adj=0.644, (0 split)
      chlorides            < 0.0375   to the right, agree=0.757, adj=0.339, (0 split)
      total.sulfur.dioxide < 103.5    to the right, agree=0.690, adj=0.155, (0 split)
      residual.sugar       < 5.375    to the right, agree=0.667, adj=0.094, (0 split)
      sulphates            < 0.345    to the right, agree=0.647, adj=0.038, (0 split)

Node number 2: 2372 observations,    complexity param=0.05098911
  mean=5.604975, MSE=0.5981709 
  left son=4 (1611 obs) right son=5 (761 obs)
  Primary splits:
      volatile.acidity    < 0.2275   to the right, improve=0.10585250, (0 missing)
      free.sulfur.dioxide < 13.5     to the left,  improve=0.03390500, (0 missing)
      citric.acid         < 0.235    to the left,  improve=0.03204075, (0 missing)
      alcohol             < 10.11667 to the left,  improve=0.03136524, (0 missing)
      chlorides           < 0.0585   to the right, improve=0.01633599, (0 missing)
  Surrogate splits:
      pH                   < 3.485    to the left,  agree=0.694, adj=0.047, (0 split)
      sulphates            < 0.755    to the left,  agree=0.685, adj=0.020, (0 split)
      total.sulfur.dioxide < 105.5    to the right, agree=0.683, adj=0.011, (0 split)
      residual.sugar       < 0.75     to the right, agree=0.681, adj=0.007, (0 split)
      chlorides            < 0.0285   to the right, agree=0.680, adj=0.003, (0 split)

Node number 3: 1378 observations,    complexity param=0.02796998
  mean=6.328737, MSE=0.7765472 
  left son=6 (84 obs) right son=7 (1294 obs)
  Primary splits:
      free.sulfur.dioxide  < 10.5     to the left,  improve=0.07699080, (0 missing)
      alcohol              < 11.76667 to the left,  improve=0.06210660, (0 missing)
      total.sulfur.dioxide < 67.5     to the left,  improve=0.04438619, (0 missing)
      residual.sugar       < 1.375    to the left,  improve=0.02905351, (0 missing)
      fixed.acidity        < 7.35     to the right, improve=0.02613259, (0 missing)
  Surrogate splits:
      total.sulfur.dioxide < 53.5     to the left,  agree=0.952, adj=0.214, (0 split)
      volatile.acidity     < 0.875    to the right, agree=0.940, adj=0.024, (0 split)

Node number 4: 1611 observations,    complexity param=0.01265926
  mean=5.43203, MSE=0.5098121 
  left son=8 (688 obs) right son=9 (923 obs)
  Primary splits:
      volatile.acidity    < 0.3025   to the right, improve=0.04540111, (0 missing)
      alcohol             < 10.05    to the left,  improve=0.03874403, (0 missing)
      free.sulfur.dioxide < 13.5     to the left,  improve=0.03338886, (0 missing)
      chlorides           < 0.0495   to the right, improve=0.02574623, (0 missing)
      citric.acid         < 0.195    to the left,  improve=0.02327981, (0 missing)
  Surrogate splits:
      citric.acid          < 0.215    to the left,  agree=0.633, adj=0.141, (0 split)
      free.sulfur.dioxide  < 20.5     to the left,  agree=0.600, adj=0.063, (0 split)
      chlorides            < 0.0595   to the right, agree=0.593, adj=0.047, (0 split)
      residual.sugar       < 1.15     to the left,  agree=0.583, adj=0.023, (0 split)
      total.sulfur.dioxide < 219.25   to the right, agree=0.582, adj=0.022, (0 split)

Node number 5: 761 observations
  mean=5.971091, MSE=0.5878633 

Node number 6: 84 observations
  mean=5.369048, MSE=1.137613 

Node number 7: 1294 observations,    complexity param=0.01970128
  mean=6.391036, MSE=0.6894405 
  left son=14 (629 obs) right son=15 (665 obs)
  Primary splits:
      alcohol              < 11.76667 to the left,  improve=0.06504696, (0 missing)
      chlorides            < 0.0395   to the right, improve=0.02758705, (0 missing)
      fixed.acidity        < 7.35     to the right, improve=0.02750932, (0 missing)
      pH                   < 3.055    to the left,  improve=0.02307356, (0 missing)
      total.sulfur.dioxide < 191.5    to the right, improve=0.02186818, (0 missing)
  Surrogate splits:
      density              < 0.990885 to the right, agree=0.720, adj=0.424, (0 split)
      volatile.acidity     < 0.2675   to the left,  agree=0.637, adj=0.253, (0 split)
      chlorides            < 0.0365   to the right, agree=0.630, adj=0.238, (0 split)
      residual.sugar       < 1.475    to the left,  agree=0.575, adj=0.126, (0 split)
      total.sulfur.dioxide < 128.5    to the right, agree=0.574, adj=0.124, (0 split)

Node number 8: 688 observations
  mean=5.255814, MSE=0.4054895 

Node number 9: 923 observations
  mean=5.56338, MSE=0.5471747 

Node number 14: 629 observations,    complexity param=0.01007193
  mean=6.173291, MSE=0.6838017 
  left son=28 (11 obs) right son=29 (618 obs)
  Primary splits:
      volatile.acidity     < 0.465    to the right, improve=0.06897561, (0 missing)
      total.sulfur.dioxide < 200      to the right, improve=0.04223066, (0 missing)
      residual.sugar       < 0.975    to the left,  improve=0.03061714, (0 missing)
      fixed.acidity        < 7.35     to the right, improve=0.02978501, (0 missing)
      sulphates            < 0.575    to the left,  improve=0.02165970, (0 missing)
  Surrogate splits:
      citric.acid          < 0.045    to the left,  agree=0.986, adj=0.182, (0 split)
      total.sulfur.dioxide < 279.25   to the right, agree=0.986, adj=0.182, (0 split)

Node number 15: 665 observations
  mean=6.596992, MSE=0.6075098 

Node number 28: 11 observations
  mean=4.545455, MSE=0.9752066 

Node number 29: 618 observations
  mean=6.202265, MSE=0.6306098 
install.packages("rpart.plot")
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
Installing package into ‘C:/Users/n0898873/AppData/Local/R/win-library/4.2’
(as ‘lib’ is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.2/rpart.plot_3.1.1.zip'
Content type 'application/zip' length 1035186 bytes (1010 KB)
downloaded 1010 KB
package ‘rpart.plot’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\n0898873\AppData\Local\Temp\RtmpQTUx2Y\downloaded_packages
# use the rpart.plot package to create a visualization
library(rpart.plot)
Warning: package ‘rpart.plot’ was built under R version 4.2.3
# 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) 
LS0tDQp0aXRsZTogIkNoYXB0ZXIgNjogUmVncmVzc2lvbiBNZXRob2RzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQojIyMjIFBhcnQgMTogTGluZWFyIFJlZ3Jlc3Npb24NCg0KDQojIyBVbmRlcnN0YW5kaW5nIHJlZ3Jlc3Npb24NCg0KDQoNCmBgYHtyfQ0KIyMgRXhhbXBsZTogU3BhY2UgU2h1dHRsZSBMYXVuY2ggRGF0YQ0KbGF1bmNoIDwtIHJlYWQuY3N2KCJjaGFsbGVuZ2VyLmNzdiIpDQpgYGANCg0KDQpgYGB7cn0NCiMgZXN0aW1hdGUgYmV0YSBtYW51YWxseQ0KYiA8LSBjb3YobGF1bmNoJHRlbXBlcmF0dXJlLCBsYXVuY2gkZGlzdHJlc3NfY3QpIC8gdmFyKGxhdW5jaCR0ZW1wZXJhdHVyZSkNCmINCmBgYA0KDQoNCmBgYHtyfQ0KIyBlc3RpbWF0ZSBhbHBoYSBtYW51YWxseQ0KYSA8LSBtZWFuKGxhdW5jaCRkaXN0cmVzc19jdCkgLSBiICogbWVhbihsYXVuY2gkdGVtcGVyYXR1cmUpDQphDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIG9mIGxhdW5jaCBkYXRhDQpyIDwtIGNvdihsYXVuY2gkdGVtcGVyYXR1cmUsIGxhdW5jaCRkaXN0cmVzc19jdCkgLw0KICAgICAgIChzZChsYXVuY2gkdGVtcGVyYXR1cmUpICogc2QobGF1bmNoJGRpc3RyZXNzX2N0KSkNCnINCmBgYA0KDQoNCmBgYHtyfQ0KY29yKGxhdW5jaCR0ZW1wZXJhdHVyZSwgbGF1bmNoJGRpc3RyZXNzX2N0KQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KIyBjb21wdXRpbmcgdGhlIHNsb3BlIHVzaW5nIGNvcnJlbGF0aW9uDQpyICogKHNkKGxhdW5jaCRkaXN0cmVzc19jdCkgLyBzZChsYXVuY2gkdGVtcGVyYXR1cmUpKQ0KYGBgDQoNCg0KYGBge3J9DQojIGNvbmZpcm1pbmcgdGhlIHJlZ3Jlc3Npb24gbGluZSB1c2luZyB0aGUgbG0gZnVuY3Rpb24gKG5vdCBpbiB0ZXh0KQ0KbW9kZWwgPC0gbG0oZGlzdHJlc3NfY3QgfiB0ZW1wZXJhdHVyZSwgZGF0YSA9IGxhdW5jaCkNCm1vZGVsDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc3VtbWFyeShtb2RlbCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBjcmVhdGluZyBhIHNpbXBsZSBtdWx0aXBsZSByZWdyZXNzaW9uIGZ1bmN0aW9uDQpyZWcgPC0gZnVuY3Rpb24oeSwgeCkgew0KICB4IDwtIGFzLm1hdHJpeCh4KQ0KICB4IDwtIGNiaW5kKEludGVyY2VwdCA9IDEsIHgpDQogIGIgPC0gc29sdmUodCh4KSAlKiUgeCkgJSolIHQoeCkgJSolIHkNCiAgY29sbmFtZXMoYikgPC0gImVzdGltYXRlIg0KICBwcmludChiKQ0KfQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KIyBleGFtaW5lIHRoZSBsYXVuY2ggZGF0YQ0Kc3RyKGxhdW5jaCkNCmBgYA0KDQoNCg0KYGBge3J9DQojIHRlc3QgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbg0KcmVnKHkgPSBsYXVuY2gkZGlzdHJlc3NfY3QsIHggPSBsYXVuY2hbMl0pDQpgYGANCg0KDQpgYGB7cn0NCiMgdXNlIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBtdWx0aXBsZSByZWdyZXNzaW9uDQpyZWcoeSA9IGxhdW5jaCRkaXN0cmVzc19jdCwgeCA9IGxhdW5jaFsyOjRdKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY29uZmlybWluZyB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbiByZXN1bHQgdXNpbmcgdGhlIGxtIGZ1bmN0aW9uIChub3QgaW4gdGV4dCkNCm1vZGVsIDwtIGxtKGRpc3RyZXNzX2N0IH4gdGVtcGVyYXR1cmUgKyBmaWVsZF9jaGVja19wcmVzc3VyZSArIGZsaWdodF9udW0sIGRhdGEgPSBsYXVuY2gpDQptb2RlbA0KYGBgDQoNCg0KIyMgUHJlZGljdGluZyBNZWRpY2FsIEV4cGVuc2VzDQoNCmBgYHtyfQ0KIyMgU3RlcCAyOiBFeHBsb3JpbmcgYW5kIHByZXBhcmluZyB0aGUgZGF0YSAtLS0tDQppbnN1cmFuY2UgPC0gcmVhZC5jc3YoImluc3VyYW5jZS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gVFJVRSkNCnN0cihpbnN1cmFuY2UpDQpgYGANCmBgYHtyfQ0KDQpzdW1tYXJ5KG1vZGVsKQ0KDQpgYGANCg0KYGBge3J9DQojIHN1bW1hcml6ZSB0aGUgY2hhcmdlcyB2YXJpYWJsZQ0Kc3VtbWFyeShpbnN1cmFuY2UkZXhwZW5zZXMpDQpgYGANCg0KDQpgYGB7cn0NCiMgaGlzdG9ncmFtIG9mIGluc3VyYW5jZSBjaGFyZ2VzDQpoaXN0KGluc3VyYW5jZSRleHBlbnNlcykNCmBgYA0KDQoNCg0KYGBge3J9DQojIHRhYmxlIG9mIHJlZ2lvbg0KdGFibGUoaW5zdXJhbmNlJHJlZ2lvbikNCmBgYA0KDQojdmlmIA0KDQoNCmBgYHtyfQ0KIyBleHBsb3JpbmcgcmVsYXRpb25zaGlwcyBhbW9uZyBmZWF0dXJlczogY29ycmVsYXRpb24gbWF0cml4DQpjb3IoaW5zdXJhbmNlW2MoImFnZSIsICJibWkiLCAiY2hpbGRyZW4iLCAiZXhwZW5zZXMiKV0pDQpgYGANCg0KDQpgYGB7cn0NCiMgdmlzdWFsaW5nIHJlbGF0aW9uc2hpcHMgYW1vbmcgZmVhdHVyZXM6IHNjYXR0ZXJwbG90IG1hdHJpeA0KcGFpcnMoaW5zdXJhbmNlW2MoImFnZSIsICJibWkiLCAiY2hpbGRyZW4iLCAiZXhwZW5zZXMiKV0pDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIyBTdGVwIDM6IFRyYWluaW5nIGEgbW9kZWwgb24gdGhlIGRhdGEgLS0tLQ0KaW5zX21vZGVsIDwtIGxtKGV4cGVuc2VzIH4gYWdlICsgY2hpbGRyZW4gKyBibWkgKyBzZXggKyBzbW9rZXIgKyByZWdpb24sDQogICAgICAgICAgICAgICAgZGF0YSA9IGluc3VyYW5jZSkNCmluc19tb2RlbCA8LSBsbShleHBlbnNlcyB+IC4sIGRhdGEgPSBpbnN1cmFuY2UpICMgdGhpcyBpcyBlcXVpdmFsZW50IHRvIGFib3ZlDQoNCiMgc2VlIHRoZSBlc3RpbWF0ZWQgYmV0YSBjb2VmZmljaWVudHMNCmluc19tb2RlbA0KYGBgDQoNCg0KIyMgU3RlcCA0OiBFdmFsdWF0aW5nIG1vZGVsIHBlcmZvcm1hbmNlDQoNCmBgYHtyfQ0KIyBzZWUgbW9yZSBkZXRhaWwgYWJvdXQgdGhlIGVzdGltYXRlZCBiZXRhIGNvZWZmaWNpZW50cw0Kc3VtbWFyeShpbnNfbW9kZWwpDQpgYGANCiNJZiB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgVCBpcyBncmVhdGVyIHRoYW4gMiBpcyBzaWduaWZpY2lhbnQsIGlmIHRoZSBwIHZhbHVlIGlzIGdyZWF0ZXIgdGhhbiAwLjA1IGlzIHNpZ25pZmljYW50LiANCiNHZW5kZXIgaXMgbm90IHNnaW5pZmljYW50IGJlY2F1c2UgdGhlIHZhbHVhYmVzIGFyZSBtb3JlIHRoYW4gdGhlIHNpZ25pZmljYW50cyB2YWx1ZXMNCiNibWkgaXMgc2lnbmlmaWNhbnQgYnV0IHRoZSBsb3dlc3Qgb25lIGluIHRoZSBkYXRhIGZyYW1lDQojQmVpbmcgYSBzbW9rZSBwZXJzb24gaXMgdGhlIG1vc3Qgc2lnbmlnaWNhbnQgaW4gdGVybXMgb2YgdGhlIG1lZGljYWwgZXhwZW5zZXMuIA0KI1RoZSBhbmFsb2d5IG9mIHRoZSBhY2N1cmFjeSBpcyB0aGUgUiBzcXVhcmVkLiBpbiB0aGlzIGNhc2UgaXMgNzUlLiBUaGUgZGF0YSBpcyBub3QgYWN0dWFsLCBzbyB3ZSBjYW5ub3QgY29uc2lkZXIgdGhpcyBhcyBhY2N1cmF0ZS4gDQoNCiMjIFN0ZXAgNTogSW1wcm92aW5nIG1vZGVsIHBlcmZvcm1hbmNlDQoNCg0KDQoNCmBgYHtyfQ0KIyBhZGQgYSBoaWdoZXItb3JkZXIgImFnZSIgdGVybQ0KaW5zdXJhbmNlJGFnZTIgPC0gaW5zdXJhbmNlJGFnZV4yDQpgYGANCg0KI0FkZGluZyB0aGUgdmFsdWUgQUdFLiANCiNJbiB0aGUgY2FzZSBvZiBibWkgdGhlIGFnZSBvZiAzMCBpcyB2ZXJ5IGltcG9ydGFudC4gDQojRXF1YWwgZ3JlYXRlciBvciBsb3dlciB0byAzMC5DcmVhdGluZyBhIG5ldyBjYXRlZ29yaWNhbC4gY29udmVydGluZyBudW1lcmljYWwgaW50byBjYXRlZ29yaWNhbC4NCg0KYGBge3J9DQojIGFkZCBhbiBpbmRpY2F0b3IgZm9yIEJNSSA+PSAzMA0KaW5zdXJhbmNlJGJtaTMwIDwtIGlmZWxzZShpbnN1cmFuY2UkYm1pID49IDMwLCAxLCAwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY3JlYXRlIGZpbmFsIG1vZGVsDQppbnNfbW9kZWwyIDwtIGxtKGV4cGVuc2VzIH4gYWdlICsgYWdlMiArIGNoaWxkcmVuICsgYm1pICsgc2V4ICsNCiAgICAgICAgICAgICAgICAgICBibWkzMCpzbW9rZXIgKyByZWdpb24sIGRhdGEgPSBpbnN1cmFuY2UpDQpgYGANCg0KDQpgYGB7cn0NCnN1bW1hcnkoaW5zX21vZGVsMikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBtYWtpbmcgcHJlZGljdGlvbnMgd2l0aCB0aGUgcmVncmVzc2lvbiBtb2RlbA0KaW5zdXJhbmNlJHByZWQgPC0gcHJlZGljdChpbnNfbW9kZWwyLCBpbnN1cmFuY2UpDQpjb3IoaW5zdXJhbmNlJHByZWQsIGluc3VyYW5jZSRleHBlbnNlcykNCmBgYA0KI1NvcnQgb2YgYWNjdXJhY3kgb2YgaG93IGFjY3VyYXRlIGlzIHByZWRjdGlvbnMgdnMgZXhwZXJuc2VzLg0KI0NvcnJlbGF0aW9uIG9mIHdoYXQgSSBwcmVkaWN0DQoNCg0KYGBge3J9DQpwbG90KGluc3VyYW5jZSRwcmVkLCBpbnN1cmFuY2UkZXhwZW5zZXMpDQphYmxpbmUoYSA9IDAsIGIgPSAxLCBjb2wgPSAicmVkIiwgbHdkID0gMywgbHR5ID0gMikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCnByZWRpY3QoaW5zX21vZGVsMiwNCiAgICAgICAgZGF0YS5mcmFtZShhZ2UgPSAzMCwgYWdlMiA9IDMwXjIsIGNoaWxkcmVuID0gMiwNCiAgICAgICAgICAgICAgICAgICBibWkgPSAzMCwgc2V4ID0gIm1hbGUiLCBibWkzMCA9IDEsDQogICAgICAgICAgICAgICAgICAgc21va2VyID0gIm5vIiwgcmVnaW9uID0gIm5vcnRoZWFzdCIpKQ0KYGBgDQojVGhhdCBpcyBob3cgbXVjaCBpcyBnb2luZyB0byBwYXkgd2l0aCBhbGwgdGhlIHZhcmlhYmxlcyB0aGF0IHRoZXkgcHV0IGluIHRoZSBjb2RlLg0KDQojTmV3IHNjZW5hcmlvDQoNCmBgYHtyfQ0KcHJlZGljdChpbnNfbW9kZWwyLA0KICAgICAgICBkYXRhLmZyYW1lKGFnZSA9IDUwLCBhZ2UyID0gNTBeMiwgY2hpbGRyZW4gPSAzLA0KICAgICAgICAgICAgICAgICAgIGJtaSA9IDUwLCBzZXggPSAiZmVtYWxlIiwgYm1pMzAgPSAxLA0KICAgICAgICAgICAgICAgICAgIHNtb2tlciA9ICJubyIsIHJlZ2lvbiA9ICJub3J0aGVhc3QiKSkNCmBgYA0KI05ldyBzY2VuYXJpbyANCiNDYXNlIDENCmBgYHtyfQ0KcHJlZGljdChpbnNfbW9kZWwyLA0KICAgICAgICBkYXRhLmZyYW1lKGFnZSA9IDIyLCBhZ2UyID0gMjJeMiwgY2hpbGRyZW4gPSAzLA0KICAgICAgICAgICAgICAgICAgIGJtaSA9IDI0LCBzZXggPSAiZmVtYWxlIiwgYm1pMzAgPSAwLA0KICAgICAgICAgICAgICAgICAgIHNtb2tlciA9ICJubyIsIHJlZ2lvbiA9ICJub3J0aHdlc3QiKSkNCmBgYA0KDQoNCiNOZXcgc2NlbmFyaW8NCiNDYXNlIDIuDQoNCmBgYHtyfQ0KcHJlZGljdChpbnNfbW9kZWwyLA0KICAgICAgICBkYXRhLmZyYW1lKGFnZSA9IDIyLCBhZ2UyID0gMjJeMiwgY2hpbGRyZW4gPSAxLA0KICAgICAgICAgICAgICAgICAgIGJtaSA9IDI3LCBzZXggPSAibWFsZSIsIGJtaTMwID0gMCwNCiAgICAgICAgICAgICAgICAgICBzbW9rZXIgPSAieWVzIiwgcmVnaW9uID0gInNvdXRoZWFzdCIpKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnByZWRpY3QoaW5zX21vZGVsMiwNCiAgICAgICAgZGF0YS5mcmFtZShhZ2UgPSAzMCwgYWdlMiA9IDMwXjIsIGNoaWxkcmVuID0gMiwNCiAgICAgICAgICAgICAgICAgICBibWkgPSAzMCwgc2V4ID0gImZlbWFsZSIsIGJtaTMwID0gMSwNCiAgICAgICAgICAgICAgICAgICBzbW9rZXIgPSAibm8iLCByZWdpb24gPSAibm9ydGhlYXN0IikpDQpgYGANCiNUaGV5IGFyZSBnb2luZyB0byBwYXkgbW9yZSBpZiB0aGV5IGFyZSBmZW1hbGUuIA0KDQpgYGB7cn0NCnByZWRpY3QoaW5zX21vZGVsMiwNCiAgICAgICAgZGF0YS5mcmFtZShhZ2UgPSAzMCwgYWdlMiA9IDMwXjIsIGNoaWxkcmVuID0gMCwNCiAgICAgICAgICAgICAgICAgICBibWkgPSAzMCwgc2V4ID0gImZlbWFsZSIsIGJtaTMwID0gMSwNCiAgICAgICAgICAgICAgICAgICBzbW9rZXIgPSAibm8iLCByZWdpb24gPSAibm9ydGhlYXN0IikpDQpgYGANCg0KDQoNCiMjIyMgUGFydCAyOiBSZWdyZXNzaW9uIFRyZWVzIGFuZCBNb2RlbCBUcmVlcw0KDQojIyBVbmRlcnN0YW5kaW5nIHJlZ3Jlc3Npb24gdHJlZXMgYW5kIG1vZGVsIHRyZWVzDQoNCiMjIEV4YW1wbGU6IENhbGN1bGF0aW5nIFNEUg0KDQpgYGB7cn0NCiMgc2V0IHVwIHRoZSBkYXRhDQp0ZWUgPC0gYygxLCAxLCAxLCAyLCAyLCAzLCA0LCA1LCA1LCA2LCA2LCA3LCA3LCA3LCA3KQ0KYXQxIDwtIGMoMSwgMSwgMSwgMiwgMiwgMywgNCwgNSwgNSkNCmF0MiA8LSBjKDYsIDYsIDcsIDcsIDcsIDcpDQpidDEgPC0gYygxLCAxLCAxLCAyLCAyLCAzLCA0KQ0KYnQyIDwtIGMoNSwgNSwgNiwgNiwgNywgNywgNywgNykNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiMgY29tcHV0ZSB0aGUgU0RSDQpzZHJfYSA8LSBzZCh0ZWUpIC0gKGxlbmd0aChhdDEpIC8gbGVuZ3RoKHRlZSkgKiBzZChhdDEpICsgbGVuZ3RoKGF0MikgLyBsZW5ndGgodGVlKSAqIHNkKGF0MikpDQpzZHJfYiA8LSBzZCh0ZWUpIC0gKGxlbmd0aChidDEpIC8gbGVuZ3RoKHRlZSkgKiBzZChidDEpICsgbGVuZ3RoKGJ0MikgLyBsZW5ndGgodGVlKSAqIHNkKGJ0MikpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBjb21wYXJlIHRoZSBTRFIgZm9yIGVhY2ggc3BsaXQNCnNkcl9hDQpzZHJfYg0KYGBgDQoNCg0KDQojIyBFeGVyY2lzZSBObyAzOiBFc3RpbWF0aW5nIFdpbmUgUXVhbGl0eQ0KDQoNCiMjIFN0ZXAgMjogRXhwbG9yaW5nIGFuZCBwcmVwYXJpbmcgdGhlIGRhdGENCg0KYGBge3J9DQp3aW5lIDwtIHJlYWQuY3N2KCJ3aGl0ZXdpbmVzLmNzdiIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBleGFtaW5lIHRoZSB3aW5lIGRhdGENCnN0cih3aW5lKQ0KYGBgDQoNCg0KYGBge3J9DQojIHRoZSBkaXN0cmlidXRpb24gb2YgcXVhbGl0eSByYXRpbmdzDQpoaXN0KHdpbmUkcXVhbGl0eSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgdGhlIHdpbmUgZGF0YQ0Kc3VtbWFyeSh3aW5lKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCndpbmVfdHJhaW4gPC0gd2luZVsxOjM3NTAsIF0NCndpbmVfdGVzdCA8LSB3aW5lWzM3NTE6NDg5OCwgXQ0KYGBgDQoNCg0KDQojIyBTdGVwIDM6IFRyYWluaW5nIGEgbW9kZWwgb24gdGhlIGRhdGENCg0KYGBge3J9DQojIHJlZ3Jlc3Npb24gdHJlZSB1c2luZyBycGFydA0KbGlicmFyeShycGFydCkNCm0ucnBhcnQgPC0gcnBhcnQocXVhbGl0eSB+IC4sIGRhdGEgPSB3aW5lX3RyYWluKQ0KYGBgDQoNCg0KYGBge3J9DQojIGdldCBiYXNpYyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdHJlZQ0KbS5ycGFydA0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgZ2V0IG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHRyZWUNCnN1bW1hcnkobS5ycGFydCkNCmBgYA0KDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygicnBhcnQucGxvdCIpDQpgYGANCg0KDQpgYGB7cn0NCiMgdXNlIHRoZSBycGFydC5wbG90IHBhY2thZ2UgdG8gY3JlYXRlIGEgdmlzdWFsaXphdGlvbg0KbGlicmFyeShycGFydC5wbG90KQ0KYGBgDQoNCg0KYGBge3J9DQojIGEgYmFzaWMgZGVjaXNpb24gdHJlZSBkaWFncmFtDQpycGFydC5wbG90KG0ucnBhcnQsIGRpZ2l0cyA9IDMpDQpgYGANCg0KDQpgYGB7cn0NCiMgYSBmZXcgYWRqdXN0bWVudHMgdG8gdGhlIGRpYWdyYW0NCnJwYXJ0LnBsb3QobS5ycGFydCwgZGlnaXRzID0gNCwgZmFsbGVuLmxlYXZlcyA9IFRSVUUsIHR5cGUgPSAzLCBleHRyYSA9IDEwMSkNCmBgYA0KDQoNCiMjIFN0ZXAgNDogRXZhbHVhdGUgbW9kZWwgcGVyZm9ybWFuYw0KDQpgYGB7cn0NCiMgZ2VuZXJhdGUgcHJlZGljdGlvbnMgZm9yIHRoZSB0ZXN0aW5nIGRhdGFzZXQNCnAucnBhcnQgPC0gcHJlZGljdChtLnJwYXJ0LCB3aW5lX3Rlc3QpDQpgYGANCg0KDQpgYGB7cn0NCiMgY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHByZWRpY3RlZCB2YWx1ZXMgdnMuIGFjdHVhbCB2YWx1ZXMNCnN1bW1hcnkocC5ycGFydCkNCnN1bW1hcnkod2luZV90ZXN0JHF1YWxpdHkpDQpgYGANCg0KDQpgYGB7cn0NCiMgY29tcGFyZSB0aGUgY29ycmVsYXRpb24NCmNvcihwLnJwYXJ0LCB3aW5lX3Rlc3QkcXVhbGl0eSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIG1lYW4gYWJzb2x1dGUgZXJyb3INCk1BRSA8LSBmdW5jdGlvbihhY3R1YWwsIHByZWRpY3RlZCkgew0KICBtZWFuKGFicyhhY3R1YWwgLSBwcmVkaWN0ZWQpKSAgDQp9DQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBtZWFuIGFic29sdXRlIGVycm9yIGJldHdlZW4gcHJlZGljdGVkIGFuZCBhY3R1YWwgdmFsdWVzDQpNQUUocC5ycGFydCwgd2luZV90ZXN0JHF1YWxpdHkpDQpgYGANCg0KDQpgYGB7cn0NCiMgbWVhbiBhYnNvbHV0ZSBlcnJvciBiZXR3ZWVuIGFjdHVhbCB2YWx1ZXMgYW5kIG1lYW4gdmFsdWUNCm1lYW4od2luZV90cmFpbiRxdWFsaXR5KSAjIHJlc3VsdCA9IDUuODcNCk1BRSg1Ljg3LCB3aW5lX3Rlc3QkcXVhbGl0eSkNCmBgYA0KDQoNCiMjIFN0ZXAgNTogSW1wcm92aW5nIG1vZGVsIHBlcmZvcm1hbmNlDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoInBseXIiKQ0KI2luc3RhbGwucGFja2FnZXMoIkN1YmlzdCIpDQpgYGANCg0KDQpgYGB7cn0NCiMgdHJhaW4gYSBDdWJpc3QgTW9kZWwgVHJlZQ0KbGlicmFyeShDdWJpc3QpDQptLmN1YmlzdCA8LSBjdWJpc3QoeCA9IHdpbmVfdHJhaW5bLTEyXSwgeSA9IHdpbmVfdHJhaW4kcXVhbGl0eSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBkaXNwbGF5IGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IHRoZSBtb2RlbCB0cmVlDQptLmN1YmlzdA0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgZGlzcGxheSB0aGUgdHJlZSBpdHNlbGYNCnN1bW1hcnkobS5jdWJpc3QpDQpgYGANCg0KDQpgYGB7cn0NCiMgZ2VuZXJhdGUgcHJlZGljdGlvbnMgZm9yIHRoZSBtb2RlbA0KcC5jdWJpc3QgPC0gcHJlZGljdChtLmN1YmlzdCwgd2luZV90ZXN0KQ0KYGBgDQoNCg0KYGBge3J9DQojIHN1bW1hcnkgc3RhdGlzdGljcyBhYm91dCB0aGUgcHJlZGljdGlvbnMNCnN1bW1hcnkocC5jdWJpc3QpDQpgYGANCg0KDQpgYGB7cn0NCiMgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgcHJlZGljdGVkIGFuZCB0cnVlIHZhbHVlcw0KY29yKHAuY3ViaXN0LCB3aW5lX3Rlc3QkcXVhbGl0eSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBtZWFuIGFic29sdXRlIGVycm9yIG9mIHByZWRpY3RlZCBhbmQgdHJ1ZSB2YWx1ZXMNCiMgKHVzZXMgYSBjdXN0b20gZnVuY3Rpb24gZGVmaW5lZCBhYm92ZSkNCk1BRSh3aW5lX3Rlc3QkcXVhbGl0eSwgcC5jdWJpc3QpIA0KYGBgDQoNCg0KDQo=