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))
sdr_b
[1] 1.392751
Exercise No 3: Estimating Wine Quality
Step 2: Exploring and preparing the data
wine <- read.csv("whitewines.csv")
Error in file(file, "rt") : cannot open the connection
# 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 ...

# summary statistics of the wine data
summary(wine)
fixed.acidity volatile.acidity citric.acid residual.sugar chlorides
Min. : 3.800 Min. :0.0800 Min. :0.0000 Min. : 0.600 Min. :0.00900
1st Qu.: 6.300 1st Qu.:0.2100 1st Qu.:0.2700 1st Qu.: 1.700 1st Qu.:0.03600
Median : 6.800 Median :0.2600 Median :0.3200 Median : 5.200 Median :0.04300
Mean : 6.855 Mean :0.2782 Mean :0.3342 Mean : 6.391 Mean :0.04577
3rd Qu.: 7.300 3rd Qu.:0.3200 3rd Qu.:0.3900 3rd Qu.: 9.900 3rd Qu.:0.05000
Max. :14.200 Max. :1.1000 Max. :1.6600 Max. :65.800 Max. :0.34600
free.sulfur.dioxide total.sulfur.dioxide density pH sulphates
Min. : 2.00 Min. : 9.0 Min. :0.9871 Min. :2.720 Min. :0.2200
1st Qu.: 23.00 1st Qu.:108.0 1st Qu.:0.9917 1st Qu.:3.090 1st Qu.:0.4100
Median : 34.00 Median :134.0 Median :0.9937 Median :3.180 Median :0.4700
Mean : 35.31 Mean :138.4 Mean :0.9940 Mean :3.188 Mean :0.4898
3rd Qu.: 46.00 3rd Qu.:167.0 3rd Qu.:0.9961 3rd Qu.:3.280 3rd Qu.:0.5500
Max. :289.00 Max. :440.0 Max. :1.0390 Max. :3.820 Max. :1.0800
alcohol quality
Min. : 8.00 Min. :3.000
1st Qu.: 9.50 1st Qu.:5.000
Median :10.40 Median :6.000
Mean :10.51 Mean :5.878
3rd Qu.:11.40 3rd Qu.:6.000
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.0008330 0.02447515
2 0.05098911 1 0.8449895 0.8481054 0.02336586
3 0.02796998 2 0.7940004 0.8029105 0.02268910
4 0.01970128 3 0.7660304 0.7776164 0.02147106
5 0.01265926 4 0.7463291 0.7577502 0.02069972
6 0.01007193 5 0.7336698 0.7540073 0.02060305
7 0.01000000 6 0.7235979 0.7485112 0.02048703
Variable importance
alcohol density volatile.acidity chlorides
34 21 15 11
total.sulfur.dioxide free.sulfur.dioxide residual.sugar sulphates
7 6 3 1
citric.acid
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")
trying URL 'http://rspm/default/__linux__/focal/latest/src/contrib/rpart.plot_3.1.4.tar.gz'
Content type 'application/x-gzip' length 993302 bytes (970 KB)
==================================================
downloaded 970 KB
The downloaded source packages are in
‘/tmp/RtmpSBhFhd/downloaded_packages’
# use the rpart.plot package to create a visualization
library(rpart.plot)


Step 5: Improving model performance
install.packages("plyr")
trying URL 'http://rspm/default/__linux__/focal/latest/src/contrib/plyr_1.8.9.tar.gz'
Content type 'application/x-gzip' length 823755 bytes (804 KB)
==================================================
downloaded 804 KB
The downloaded source packages are in
‘/tmp/RtmpGGgkl7/downloaded_packages’
# 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
Call:
cubist.default(x = wine_train[-12], y = wine_train$quality)
Number of samples: 3750
Number of predictors: 11
Number of committees: 1
Number of rules: 7
# display the tree itself
summary(m.cubist)
Call:
cubist.default(x = wine_train[-12], y = wine_train$quality)
Cubist [Release 2.07 GPL Edition] Tue Feb 3 01:25:38 2026
---------------------------------
Target attribute `outcome'
*** Ignoring cases with unknown or N/A target value
Read 1599 cases (12 attributes) from undefined.data
Model:
Rule 1: [630 cases, mean 5.3, range 3 to 8, est err 0.4]
if
alcohol <= 9.8
then
outcome = 5 - 0.79 volatile.acidity - 0.099 alcohol
+ 0.052 fixed.acidity - 0.31 citric.acid + 0.33 sulphates
+ 0.29 pH - 0.0031 free.sulfur.dioxide
- 0.0007 total.sulfur.dioxide - 0.4 chlorides
Rule 2: [589 cases, mean 5.3, range 3 to 8, est err 0.4]
if
sulphates <= 0.92
alcohol <= 9.8
then
outcome = 5.5 + 1.28 sulphates - 0.9 volatile.acidity - 0.33 citric.acid
+ 0.029 fixed.acidity - 0.033 alcohol
- 0.0008 total.sulfur.dioxide - 0.0023 free.sulfur.dioxide
- 0.4 chlorides - 0.1 pH
Rule 3: [80 cases, mean 5.3, range 3 to 7, est err 0.7]
if
volatile.acidity > 0.31
total.sulfur.dioxide <= 14
sulphates <= 0.63
alcohol > 9.8
then
outcome = 0.5 + 0.549 alcohol - 1.61 volatile.acidity + 0.36 sulphates
- 0.18 pH - 0.0005 total.sulfur.dioxide - 0.07 citric.acid
+ 0.001 free.sulfur.dioxide
Rule 4: [340 cases, mean 5.6, range 4 to 7, est err 0.5]
if
volatile.acidity > 0.31
total.sulfur.dioxide > 14
sulphates <= 0.63
alcohol > 9.8
then
outcome = 5.1 + 2.85 sulphates + 0.19 alcohol - 0.74 citric.acid
- 0.69 volatile.acidity - 0.74 pH
- 0.0027 total.sulfur.dioxide + 0.0013 free.sulfur.dioxide
Rule 5: [407 cases, mean 6.1, range 3 to 8, est err 0.6]
if
volatile.acidity > 0.31
sulphates > 0.63
alcohol > 9.8
then
outcome = 7.6 + 0.309 alcohol - 0.0073 total.sulfur.dioxide - 1.12 pH
- 0.81 volatile.acidity - 0.079 fixed.acidity + 0.22 sulphates
+ 0.002 free.sulfur.dioxide
Rule 6: [71 cases, mean 6.2, range 5 to 8, est err 0.5]
if
volatile.acidity <= 0.31
sulphates <= 0.73
alcohol > 9.8
then
outcome = 131.4 + 4.85 volatile.acidity - 124 density - 1.35 pH
+ 0.056 fixed.acidity + 0.54 sulphates + 0.036 alcohol
+ 0.021 residual.sugar
Rule 7: [85 cases, mean 6.5, range 5 to 8, est err 0.4]
if
volatile.acidity <= 0.31
sulphates > 0.73
then
outcome = 17 + 0.39 alcohol + 0.113 fixed.acidity
+ 0.25 volatile.acidity - 16 density + 0.14 sulphates
Evaluation on training data (1599 cases):
Average |error| 0.4
Relative |error| 0.62
Correlation coefficient 0.62
Attribute usage:
Conds Model
96% 100% alcohol
71% 100% sulphates
45% 100% volatile.acidity
19% 93% total.sulfur.dioxide
96% pH
93% free.sulfur.dioxide
81% fixed.acidity
74% citric.acid
55% chlorides
7% density
3% residual.sugar
Time: 0.0 secs
# generate predictions for the model
p.cubist <- predict(m.cubist, wine_test)
# summary statistics about the predictions
summary(p.cubist)
Min. 1st Qu. Median Mean 3rd Qu. Max.
5.865 5.865 5.865 5.865 5.865 5.865
# correlation between the predicted and true values
cor(p.cubist, wine_test$quality)
[1] NA
# mean absolute error of predicted and true values
# (uses a custom function defined above)
MAE(wine_test$quality, p.cubist)
[1] NA
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBUcmVlcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIyBSZWdyZXNzaW9uIFRyZWVzIGFuZCBNb2RlbCBUcmVlcwoKIyMgVW5kZXJzdGFuZGluZyByZWdyZXNzaW9uIHRyZWVzIGFuZCBtb2RlbCB0cmVlcwoKIyMgRXhhbXBsZTogQ2FsY3VsYXRpbmcgU0RSCgpgYGB7cn0KIyBzZXQgdXAgdGhlIGRhdGEKdGVlIDwtIGMoMSwgMSwgMSwgMiwgMiwgMywgNCwgNSwgNSwgNiwgNiwgNywgNywgNywgNykKYXQxIDwtIGMoMSwgMSwgMSwgMiwgMiwgMywgNCwgNSwgNSkKYXQyIDwtIGMoNiwgNiwgNywgNywgNywgNykKYnQxIDwtIGMoMSwgMSwgMSwgMiwgMiwgMywgNCkKYnQyIDwtIGMoNSwgNSwgNiwgNiwgNywgNywgNywgNykKYGBgCgoKCgpgYGB7cn0KIyBjb21wdXRlIHRoZSBTRFIKc2RyX2EgPC0gc2QodGVlKSAtIChsZW5ndGgoYXQxKSAvIGxlbmd0aCh0ZWUpICogc2QoYXQxKSArIGxlbmd0aChhdDIpIC8gbGVuZ3RoKHRlZSkgKiBzZChhdDIpKQpzZHJfYiA8LSBzZCh0ZWUpIC0gKGxlbmd0aChidDEpIC8gbGVuZ3RoKHRlZSkgKiBzZChidDEpICsgbGVuZ3RoKGJ0MikgLyBsZW5ndGgodGVlKSAqIHNkKGJ0MikpCmBgYAoKCgpgYGB7cn0KIyBjb21wYXJlIHRoZSBTRFIgZm9yIGVhY2ggc3BsaXQKc2RyX2EKc2RyX2IKYGBgCgoKCiMjIEV4ZXJjaXNlIE5vIDM6IEVzdGltYXRpbmcgV2luZSBRdWFsaXR5CgoKIyMgU3RlcCAyOiBFeHBsb3JpbmcgYW5kIHByZXBhcmluZyB0aGUgZGF0YQoKYGBge3J9CndpbmUgPC0gcmVhZC5jc3YoIndoaXRld2luZXMuY3N2IikKYGBgCgoKCmBgYHtyfQojIGV4YW1pbmUgdGhlIHdpbmUgZGF0YQpzdHIod2luZSkKYGBgCgoKYGBge3J9CiMgdGhlIGRpc3RyaWJ1dGlvbiBvZiBxdWFsaXR5IHJhdGluZ3MKaGlzdCh3aW5lJHF1YWxpdHkpCmBgYAoKCmBgYHtyfQojIHN1bW1hcnkgc3RhdGlzdGljcyBvZiB0aGUgd2luZSBkYXRhCnN1bW1hcnkod2luZSkKYGBgCgoKCmBgYHtyfQp3aW5lX3RyYWluIDwtIHdpbmVbMTozNzUwLCBdCndpbmVfdGVzdCA8LSB3aW5lWzM3NTE6NDg5OCwgXQpgYGAKCgoKIyMgU3RlcCAzOiBUcmFpbmluZyBhIG1vZGVsIG9uIHRoZSBkYXRhCgpgYGB7cn0KIyByZWdyZXNzaW9uIHRyZWUgdXNpbmcgcnBhcnQKbGlicmFyeShycGFydCkKbS5ycGFydCA8LSBycGFydChxdWFsaXR5IH4gLiwgZGF0YSA9IHdpbmVfdHJhaW4pCmBgYAoKCmBgYHtyfQojIGdldCBiYXNpYyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdHJlZQptLnJwYXJ0CmBgYAoKCgpgYGB7cn0KIyBnZXQgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdHJlZQpzdW1tYXJ5KG0ucnBhcnQpCmBgYAoKCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikKYGBgCgoKYGBge3J9CiMgdXNlIHRoZSBycGFydC5wbG90IHBhY2thZ2UgdG8gY3JlYXRlIGEgdmlzdWFsaXphdGlvbgpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmBgYAoKCmBgYHtyfQojIGEgYmFzaWMgZGVjaXNpb24gdHJlZSBkaWFncmFtCnJwYXJ0LnBsb3QobS5ycGFydCwgZGlnaXRzID0gMykKYGBgCgoKYGBge3J9CiMgYSBmZXcgYWRqdXN0bWVudHMgdG8gdGhlIGRpYWdyYW0KcnBhcnQucGxvdChtLnJwYXJ0LCBkaWdpdHMgPSA0LCBmYWxsZW4ubGVhdmVzID0gVFJVRSwgdHlwZSA9IDMsIGV4dHJhID0gMTAxKQpgYGAKCgojIyBTdGVwIDQ6IEV2YWx1YXRlIG1vZGVsIHBlcmZvcm1hbmMKCmBgYHtyfQojIGdlbmVyYXRlIHByZWRpY3Rpb25zIGZvciB0aGUgdGVzdGluZyBkYXRhc2V0CnAucnBhcnQgPC0gcHJlZGljdChtLnJwYXJ0LCB3aW5lX3Rlc3QpCmBgYAoKCmBgYHtyfQojIGNvbXBhcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwcmVkaWN0ZWQgdmFsdWVzIHZzLiBhY3R1YWwgdmFsdWVzCnN1bW1hcnkocC5ycGFydCkKc3VtbWFyeSh3aW5lX3Rlc3QkcXVhbGl0eSkKYGBgCgoKYGBge3J9CiMgY29tcGFyZSB0aGUgY29ycmVsYXRpb24KY29yKHAucnBhcnQsIHdpbmVfdGVzdCRxdWFsaXR5KQpgYGAKCgpgYGB7cn0KIyBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IKTUFFIDwtIGZ1bmN0aW9uKGFjdHVhbCwgcHJlZGljdGVkKSB7CiAgbWVhbihhYnMoYWN0dWFsIC0gcHJlZGljdGVkKSkgIAp9CmBgYAoKCgpgYGB7cn0KIyBtZWFuIGFic29sdXRlIGVycm9yIGJldHdlZW4gcHJlZGljdGVkIGFuZCBhY3R1YWwgdmFsdWVzCk1BRShwLnJwYXJ0LCB3aW5lX3Rlc3QkcXVhbGl0eSkKYGBgCgoKYGBge3J9CiMgbWVhbiBhYnNvbHV0ZSBlcnJvciBiZXR3ZWVuIGFjdHVhbCB2YWx1ZXMgYW5kIG1lYW4gdmFsdWUKbWVhbih3aW5lX3RyYWluJHF1YWxpdHkpICMgcmVzdWx0ID0gNS44NwpNQUUoNS44Nywgd2luZV90ZXN0JHF1YWxpdHkpCmBgYAoKCiMjIFN0ZXAgNTogSW1wcm92aW5nIG1vZGVsIHBlcmZvcm1hbmNlCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygicGx5ciIpCmluc3RhbGwucGFja2FnZXMoIkN1YmlzdCIpCmBgYAoKCmBgYHtyfQojIHRyYWluIGEgQ3ViaXN0IE1vZGVsIFRyZWUKbGlicmFyeShDdWJpc3QpCm0uY3ViaXN0IDwtIGN1YmlzdCh4ID0gd2luZV90cmFpblstMTJdLCB5ID0gd2luZV90cmFpbiRxdWFsaXR5KQpgYGAKCgpgYGB7cn0KIyBkaXNwbGF5IGJhc2ljIGluZm9ybWF0aW9uIGFib3V0IHRoZSBtb2RlbCB0cmVlCm0uY3ViaXN0CmBgYAoKCgpgYGB7cn0KIyBkaXNwbGF5IHRoZSB0cmVlIGl0c2VsZgpzdW1tYXJ5KG0uY3ViaXN0KQpgYGAKCgpgYGB7cn0KIyBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3IgdGhlIG1vZGVsCnAuY3ViaXN0IDwtIHByZWRpY3QobS5jdWJpc3QsIHdpbmVfdGVzdCkKYGBgCgoKYGBge3J9CiMgc3VtbWFyeSBzdGF0aXN0aWNzIGFib3V0IHRoZSBwcmVkaWN0aW9ucwpzdW1tYXJ5KHAuY3ViaXN0KQpgYGAKCgpgYGB7cn0KIyBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgYW5kIHRydWUgdmFsdWVzCmNvcihwLmN1YmlzdCwgd2luZV90ZXN0JHF1YWxpdHkpCmBgYAoKCmBgYHtyfQojIG1lYW4gYWJzb2x1dGUgZXJyb3Igb2YgcHJlZGljdGVkIGFuZCB0cnVlIHZhbHVlcwojICh1c2VzIGEgY3VzdG9tIGZ1bmN0aW9uIGRlZmluZWQgYWJvdmUpCk1BRSh3aW5lX3Rlc3QkcXVhbGl0eSwgcC5jdWJpc3QpIApgYGAKCgoK