Intro
Halo my name is Nunu.Today we’ll practice multiple linear regression using R. The dataset used in this Rpubs was uploaded to Kaggle by Aung Pyae. Linear Regression are used to explain the linear relationship between one dependent variable with ≥1 independent variables.
What are we working with?
we’re going to know the relationship between the weight of fish with other variables. So here’s the variables:
Species : The name of the fish species
Weight : Weight of the fish (g)
Height : Height (cm)
Width : Diagonal width (cm)
Length1 : Vertical length (cm)
Length2 : Diagonal length (cm)
Length3 : Cross length (cm)
Our dependent variable is Weight and the others are going to be our independent variable. The independent variable is something which is not affected by the experiment itself but which can be manipulated to affect the dependent variable (Kaliyadan and Kulkarni, 2019). The final analysis will assist fish market office management in setting business strategies to improve fish quality, especially in increasing fish weight.
Data Preparation
Load the required package.
library(dplyr)
library(GGally)
library(ggplot2)
library(reshape2)
library(ggeasy)
library(caret)
library(car)
library(lmtest)
library(MLmetrics)
Load the dataset, named fish_market
fish_market <- read.csv("C:/algoritma_FIX/W4_Regression/RM/LBB_4/Fish.csv")
rmarkdown::paged_table(fish_market)
str(fish_market)
'data.frame': 159 obs. of 7 variables:
$ ï..Species: chr "Bream" "Bream" "Bream" "Bream" ...
$ Weight : num 242 290 340 363 430 450 500 390 450 500 ...
$ Length1 : num 23.2 24 23.9 26.3 26.5 26.8 26.8 27.6 27.6 28.5 ...
$ Length2 : num 25.4 26.3 26.5 29 29 29.7 29.7 30 30 30.7 ...
$ Length3 : num 30 31.2 31.1 33.5 34 34.7 34.5 35 35.1 36.2 num :4.02 4.31 4.7 4.46 5.13
ï..Species Weight Length1 Length2 Length3
0
the data set has 159 rows and 7 columns. also contains 0 NA value. We have to change the type of species to factor.
fish_market <- fish_market %>%
mutate(species = as.factor(species))
Data Exploration
Check The Distribution
fish_hor <- melt(fish_market %>% select(-Weight), id = "species")
ggplot(fish_hor, aes(x=variable, y=value, fill=variable)) +
geom_boxplot()+
labs(x = " ", y = "Value")+
scale_fill_brewer(palette= "Set2") +
theme(legend.position = "None")

ggplot(fish_market, aes(y=Weight))+
geom_boxplot(fill = 4,
alpha = 0.5,
color = 1,
outlier.colour = 1)+
ggtitle(" Dependent variable ")+
labs(x = "Weight", y = "Value") +
theme_classic()+
theme(legend.position = "None")

From the boxplot above, we can see that only one independent variable is normally distributed (Width). While others tend to sskewed right. Each of Length variables (Length1, Length2, Length3) has an outlier, so does the dependent variable. Should we remove outliers before regression? Sometimes it’s best to keep outliers in our data.If the outliers are too influential to regression line, we have to take another action.
check the correlation
ggcorr(fish_market %>% select(-c(species)) ,
label = TRUE, label_size = 2.9, hjust = 1, layout.exp = 2)

By seeing the correlation plot we can define Length1, Length2, Length3 are all highly positively correlated to each other. If the correlation is too high it can lead to Multicollinearity in regression modeling. Which should be avoided by eliminate one of those variables that highly correlated to each others. What impact does the strong correlation between the two variables have on the regression analysis? we’ll know the answer later by keep all of the variables.
Modeling
Train-Test Split
fish_reg <- fish_market %>%
select(-species)
set.seed(123)
samplesize <- round(0.8 * nrow(fish_reg), 0)
index <- sample(seq_len(nrow(fish_reg)), size = samplesize)
data_train <- fish_reg[index, ]
data_test <- fish_reg[-index, ]
1. Linear Regression
fish_lm <- lm(Weight ~., data = data_train)
summary(fish_lm)
Call:
lm(formula = Weight ~ ., data = data_train)
Residuals:
Min 1Q Median 3Q Max
-262.28 -73.51 -23.75 68.23 413.26
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -503.86 32.84 -15.343 <2e-16 ***
Length1 64.49 47.97 1.344 0.1813
Length2 -23.55 49.75 -0.473 0.6368
Length3 -12.86 20.40 -0.630 0.5296
Height 24.06 10.17 2.365 0.0196 *
Width 13.76 24.72 0.557 0.5788
---
Signif. codes:
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 126.3 on 121 degrees of freedom
Multiple R-squared: 0.8832, Adjusted R-squared: 0.8783
F-statistic: 182.9 on 5 and 121 DF, p-value: < 2.2e-16
Let’s take a look on Pr(>|t|) column. We will take significance level of 0.05. It means if the value Pr(>|t|) is below 0.05, than we can asume that the variable has significant effect toward the model. The summary of fish_lm shown only one variable (Height) has significant effect toward our model. So with every increased value of one cm in Height will contibute to 24.06 increase in fish Weight.
Weight = -503.86+24.06(Height)
2. Step-Wise Regression
fish_lm_step <- step(fish_lm, direction = "backward")
Start: AIC=1234.88
Weight ~ Length1 + Length2 + Length3 + Height + Width
Df Sum of Sq RSS AIC
- Length2 1 3575 1933890 1233.1
- Width 1 4943 1935258 1233.2
- Length3 1 6340 1936656 1233.3
- Length1 1 28836 1959151 1234.8
<none> 1930316 1234.9
- Height 1 89252 2019567 1238.6
Step: AIC=1233.12
Weight ~ Length1 + Length3 + Height + Width
Df Sum of Sq RSS AIC
- Width 1 2783 1936674 1231.3
- Length3 1 12860 1946750 1232.0
<none> 1933890 1233.1
- Length1 1 68894 2002785 1235.6
- Height 1 95062 2028952 1237.2
Step: AIC=1231.3
Weight ~ Length1 + Length3 + Height
Df Sum of Sq RSS AIC
<none> 1936674 1231.3
- Length3 1 31263 1967937 1231.3
- Length1 1 151406 2088080 1238.9
- Height 1 299135 2235808 1247.5
summary(fish_lm_step)
Call:
lm(formula = Weight ~ Length1 + Length3 + Height, data = data_train)
Residuals:
Min 1Q Median 3Q Max
-267.50 -68.26 -24.57 66.06 402.36
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -500.765 31.375 -15.961 < 2e-16 ***
Length1 49.868 16.082 3.101 0.00239 **
Length3 -21.282 15.103 -1.409 0.16133
Height 27.898 6.401 4.359 2.73e-05 ***
---
Signif. codes:
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 125.5 on 123 degrees of freedom
Multiple R-squared: 0.8828, Adjusted R-squared: 0.8799
F-statistic: 308.7 on 3 and 123 DF, p-value: < 2.2e-16
This step-wise regression method will produce an optimum formula based on the lowest AIC value. The Akaike information criterion (AIC) is a mathematical method for evaluating how well a model fits the data it was generated from.
We can see that the step wise regression eliminates the Length2 and Width variables to produce the smallest AIC. The selected variables are Length1, Length2, Height. Length1 and Height have a significant effect to our model fish_lm_step (Pr(>|t|) is below 0.05). We can check the Adjusted R-Squared value from fish_lm and fish_lm_step. The first model with complete variables has adjusted R-squared of 0.8783 or fish_lm model can explain 87.83% of variance in Fish Weight (independent variable). While the step-wise regression has adjusted R-squared of 0.8799. There’s no big difference with fish_lm and fish_lm_step.
Weight = -500.765+49.868 (Length1)-40.938(Length3)+27.898(Height)
Evaluation
Mean absolute percentage error is commonly used for regression and model evaluation. This is the average of the absolute values of the percentage errors; it has the advantage of being dimensionless (Kennedy 2003). \[ MAPE = \frac{1}{n}\ \sum_{t=1}^{n}\ \lvert\frac{At-Ft}{At}\lvert\ \] ### 1. Linear Regression (fish_lm)
lm_pred <- predict(fish_lm, newdata = data_test %>% select(-Weight))
# MAPE of test dataset
MAPE(lm_pred, data_test$Weight)
[1] 2.262244
2. Step-Wise Regression (fish_lm_step)
lm_pred_step <- predict(fish_lm_step, newdata = data_test %>% select(-Weight))
# MAPE of test dataset
MAPE(lm_pred_step, data_test$Weight)
[1] 2.235405
The Step-Wise Regression (fish_lm_step) has a smaller MAPE, even only by a small margin. it indicates that fish_lm_step is better than fish_lm in predicting the data set. Next, we have to check the linear regression assumptions in fish_lm_step
Assumptions
There are four assumptions associated with a linear regression model: ### 1. Linearity The presence of a pattern may indicate a problem with some aspect of the linear model.
plot(fish_lm_step,1)

The residual and fit value that we have tend to be quadratic curve. This indicates that the linearity assumption is violated.
2. Normality of residuals
The normal probability plot of residuals should follow a straight line.
plot(fish_lm_step,2)

QQ plot of residuals can be used to check the normality assumption. From the QQ plot, not all of the data follow a straight line. so we can assume that the residual didnt follom the normality assumption. To make sure, we can check by formal test using Shapiro test.
shapiro.test(fish_lm_step$residuals)
The test rejects the hypothesis of normality when the p-value is less than or equal to 0.05. The null hypothesis is that the residuals follow normal distribution. It means that that our hypothesis is rejected. So or residuals are not following the normal distribution.
3. Heterocedasticity (Non-constant variance of error terms)
The residuals are assumed to have a constant variance.
plot(fish_lm_step, 3)

This plot shows if residuals are not spread equally along the ranges of predictors. It means our residuals doesnt have constant variance. To make sure, We can check with Breusch-Pagan test.
bptest(fish_lm_step)
studentized Breusch-Pagan test
data: fish_lm_step
BP = 60.461, df = 3, p-value = 4.684e-13
The null hypothesis is there is no heterocesdasticity. our test shown p-value < 0.05, we can conclude that heterocesdasticity is present in our model.
4. Multicollinearity
Multicollinearity is the occurrence of high intercorrelations among two or more independent variables in a multiple regression model. To check the multicollinearity, we can measure the varianec inflation factor (VIF). There’s no multicollinearity when VIF<10.
vif(fish_lm_step)
Length1 Length3 Height
217.499860 259.609031 6.147768
when we do correlation checking, we have 3 (Length1, Length2, Length3) variables that have a high correlation to each other. If the correlation is too high it can lead to Multicollinearity in regression modeling. In Step-wese regression, R eliminate the Length2 variable. But it still have multicollinearity in our model. So we have to eliminate another Length variable (Length1 or Length3)
Model Improvement
our final model doesn’t fulfill all of the linear regression assumptions. We can fix them by transform the data by sqrt and remove one variable that has high correlation to get rid of multicolinierity. i choose Length1 and Height as a final independent predictor. ### 1. Train-Test Split New Model
fish_imprv <- fish_market %>%
mutate_if(~is.numeric(.), ~sqrt(.)) %>%
select(Weight,Length1, Height)
set.seed(123)
data_train2 <- fish_imprv[index, ]
data_test2 <- fish_imprv[-index, ]
2. Regression New Model
fish_lm_imprv <- lm(Weight ~ Length1+Height, data = data_train2)
summary(fish_lm_imprv)
Call:
lm(formula = Weight ~ Length1 + Height, data = data_train2)
Residuals:
Min 1Q Median 3Q Max
-11.6387 -1.0081 -0.3917 1.5848 4.9890
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -27.6715 1.0467 -26.437 <2e-16 ***
Length1 6.8101 0.2888 23.580 <2e-16 ***
Height 3.7827 0.3828 9.881 <2e-16 ***
---
Signif. codes:
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.28 on 124 degrees of freedom
Multiple R-squared: 0.9406, Adjusted R-squared: 0.9396
F-statistic: 980.9 on 2 and 124 DF, p-value: < 2.2e-16
3. Evaluation New Model
lm_pred2 <- predict(fish_lm_imprv, newdata = data_test2 %>% select(-Weight))
# MAPE of test dataset
MAPE(lm_pred2^2,(data_test2$Weight)^2)
[1] 0.2202986
Our new model has R-squared of 93.96% and MAPE really drop to 0.2202986.
4. Assumptions New Model
#Linearity
plot(fish_lm_imprv,1)

There is still square pattern in our residual plot.
#Normality of residuals
shapiro.test(fish_lm_imprv$residuals)
Shapiro-Wilk normality test
data: fish_lm_imprv$residuals
W = 0.92149, p-value = 1.641e-06
p-value < 0.05, it means that residual isnt normally distributed.
#Heterocedasticity (Non-constant variance of error terms)
bptest(fish_lm_imprv)
studentized Breusch-Pagan test
data: fish_lm_imprv
BP = 4.932, df = 2, p-value = 0.08492
p-value > 0.05, it means that heterocesdasticity is not present.
#Multicollinearity
vif(fish_lm_imprv)
Length1 Height
1.99608 1.99608
all of VIF<10, there’s no Multicollinearity
Conclusion
Variables that affect weight of fish are length1 and height. we need to eliminate the variables Length2 and Length3 because the two variables have a large correlation. And to fulfill the assumptions of linear regression we need to transform data to sqrt. The final linear regression model has a MAPE value of 0.2202986 with Adjusted R-squared: 0.9396. it means the model is better than two previous models. For profitable business purposes, to increase the Weight of fish, fisherman needs to increase the Length1 and Height of each fish.
LS0tDQp0aXRsZTogIkZpc2ggTWFya2V0IEFuYWx5c2lzIHVzaW5nIExpbmVhciBSZWdyZXNzaW9uIg0KYXV0aG9yOiAia2h1c251bF8yMDEyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgSW50cm8NCg0KSGFsbyBteSBuYW1lIGlzIE51bnUuVG9kYXkgd2UnbGwgcHJhY3RpY2UgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gdXNpbmcgUi4gVGhlIGRhdGFzZXQgdXNlZCBpbiB0aGlzIFJwdWJzIHdhcyB1cGxvYWRlZCB0byBLYWdnbGUgYnkgKkF1bmcgUHlhZSouIExpbmVhciBSZWdyZXNzaW9uIGFyZSB1c2VkIHRvIGV4cGxhaW4gdGhlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBvbmUgZGVwZW5kZW50IHZhcmlhYmxlIHdpdGgg4omlMSBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIA0KDQojIyBXaGF0IGFyZSB3ZSB3b3JraW5nIHdpdGg/DQoNCndlJ3JlIGdvaW5nIHRvIGtub3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB3ZWlnaHQgb2YgZmlzaCB3aXRoIG90aGVyIHZhcmlhYmxlcy4gU28gaGVyZSdzIHRoZSB2YXJpYWJsZXM6DQoNClNwZWNpZXMgICAgIDogVGhlIG5hbWUgb2YgdGhlIGZpc2ggc3BlY2llcw0KDQpXZWlnaHQgICAgICA6IFdlaWdodCBvZiB0aGUgZmlzaCAoZykNCg0KSGVpZ2h0ICAgICAgOiBIZWlnaHQgKGNtKQ0KDQpXaWR0aCAgICAgICA6IERpYWdvbmFsIHdpZHRoIChjbSkNCg0KTGVuZ3RoMSAgICAgOiBWZXJ0aWNhbCBsZW5ndGggKGNtKQ0KDQpMZW5ndGgyICAgICA6IERpYWdvbmFsIGxlbmd0aCAoY20pDQoNCkxlbmd0aDMgICAgIDogQ3Jvc3MgbGVuZ3RoIChjbSkNCg0KT3VyIGRlcGVuZGVudCB2YXJpYWJsZSBpcyBXZWlnaHQgYW5kIHRoZSBvdGhlcnMgYXJlIGdvaW5nIHRvIGJlIG91ciBpbmRlcGVuZGVudCB2YXJpYWJsZS4gVGhlIGluZGVwZW5kZW50IHZhcmlhYmxlIGlzIHNvbWV0aGluZyB3aGljaCBpcyBub3QgYWZmZWN0ZWQgYnkgdGhlIGV4cGVyaW1lbnQgaXRzZWxmIGJ1dCB3aGljaCBjYW4gYmUgbWFuaXB1bGF0ZWQgdG8gYWZmZWN0IHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgKEthbGl5YWRhbiBhbmQgS3Vsa2FybmksIDIwMTkpLiBUaGUgZmluYWwgYW5hbHlzaXMgd2lsbCBhc3Npc3QgZmlzaCBtYXJrZXQgb2ZmaWNlIG1hbmFnZW1lbnQgaW4gc2V0dGluZyBidXNpbmVzcyBzdHJhdGVnaWVzIHRvIGltcHJvdmUgZmlzaCBxdWFsaXR5LCBlc3BlY2lhbGx5IGluIGluY3JlYXNpbmcgZmlzaCB3ZWlnaHQuDQoNCiMjIERhdGEgUHJlcGFyYXRpb24gDQoNCkxvYWQgdGhlIHJlcXVpcmVkIHBhY2thZ2UuDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KEdHYWxseSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVzaGFwZTIpIA0KbGlicmFyeShnZ2Vhc3kpIA0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShsbXRlc3QpDQpsaWJyYXJ5KE1MbWV0cmljcykNCmBgYA0KTG9hZCB0aGUgZGF0YXNldCwgbmFtZWQgKmZpc2hfbWFya2V0Kg0KYGBge3J9DQpmaXNoX21hcmtldCA8LSByZWFkLmNzdigiQzovYWxnb3JpdG1hX0ZJWC9XNF9SZWdyZXNzaW9uL1JNL0xCQl80L0Zpc2guY3N2IikNCnJtYXJrZG93bjo6cGFnZWRfdGFibGUoZmlzaF9tYXJrZXQpDQpgYGANCmBgYHtyfQ0Kc3RyKGZpc2hfbWFya2V0KQ0KY29sU3Vtcyhpcy5uYShmaXNoX21hcmtldCkpDQpgYGANCnRoZSBkYXRhIHNldCBoYXMgMTU5IHJvd3MgYW5kIDcgY29sdW1ucy4gYWxzbyBjb250YWlucyAwIE5BIHZhbHVlLiBXZSBoYXZlIHRvIGNoYW5nZSB0aGUgdHlwZSBvZiBzcGVjaWVzIHRvIGZhY3Rvci4gDQpgYGB7cn0NCmZpc2hfbWFya2V0IDwtIGZpc2hfbWFya2V0ICU+JSANCiAgcmVuYW1lKHNwZWNpZXMgPSDDry4uU3BlY2llcykgJT4lIA0KICBtdXRhdGUoc3BlY2llcyA9IGFzLmZhY3RvcihzcGVjaWVzKSkNCmBgYA0KDQojIyBEYXRhIEV4cGxvcmF0aW9uDQoNCiMjIyBDaGVjayBUaGUgRGlzdHJpYnV0aW9uDQpgYGB7cn0NCmZpc2hfaG9yIDwtIG1lbHQoZmlzaF9tYXJrZXQgJT4lIHNlbGVjdCgtV2VpZ2h0KSwgaWQgPSAic3BlY2llcyIpDQpnZ3Bsb3QoZmlzaF9ob3IsIGFlcyh4PXZhcmlhYmxlLCB5PXZhbHVlLCBmaWxsPXZhcmlhYmxlKSkgKw0KICBnZW9tX2JveHBsb3QoKSsNCiAgICBsYWJzKHggPSAiICIsIHkgPSAiVmFsdWUiKSsNCiAgZ2d0aXRsZSgiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmRlcGVuZGVudCB2YXJpYWJsZSAiKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9ICJTZXQyIikgKw0KICAgICAgdGhlbWVfY2xhc3NpYygpKw0KICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChmaXNoX21hcmtldCwgYWVzKHk9V2VpZ2h0KSkrDQogIGdlb21fYm94cGxvdChmaWxsID0gNCwgICAgICAgICAgIA0KICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUsICAgICAgICANCiAgICAgICAgICAgICAgIGNvbG9yID0gMSwgICAgICAgICAgDQogICAgICAgICAgICAgICBvdXRsaWVyLmNvbG91ciA9IDEpKw0KICBnZ3RpdGxlKCIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlcGVuZGVudCB2YXJpYWJsZSAiKSsNCiAgbGFicyh4ID0gIldlaWdodCIsIHkgPSAiVmFsdWUiKSArDQogICAgICB0aGVtZV9jbGFzc2ljKCkrDQogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpDQpgYGANCkZyb20gdGhlIGJveHBsb3QgYWJvdmUsIHdlIGNhbiBzZWUgdGhhdCBvbmx5IG9uZSBpbmRlcGVuZGVudCB2YXJpYWJsZSBpcyBub3JtYWxseSBkaXN0cmlidXRlZCAoV2lkdGgpLiBXaGlsZSBvdGhlcnMgdGVuZCB0byBzc2tld2VkIHJpZ2h0LiBFYWNoIG9mIExlbmd0aCB2YXJpYWJsZXMgKExlbmd0aDEsIExlbmd0aDIsIExlbmd0aDMpIGhhcyBhbiBvdXRsaWVyLCBzbyBkb2VzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUuIFNob3VsZCB3ZSByZW1vdmUgb3V0bGllcnMgYmVmb3JlIHJlZ3Jlc3Npb24/IFNvbWV0aW1lcyBpdOKAmXMgYmVzdCB0byBrZWVwIG91dGxpZXJzIGluIG91ciBkYXRhLklmIHRoZSBvdXRsaWVycyBhcmUgdG9vIGluZmx1ZW50aWFsIHRvIHJlZ3Jlc3Npb24gbGluZSwgd2UgaGF2ZSB0byB0YWtlIGFub3RoZXIgYWN0aW9uLiANCg0KDQojIyMgY2hlY2sgdGhlIGNvcnJlbGF0aW9uDQpgYGB7cn0NCmdnY29ycihmaXNoX21hcmtldCAlPiUgc2VsZWN0KC1jKHNwZWNpZXMpKSAsDQogICAgICAgbGFiZWwgPSBUUlVFLCBsYWJlbF9zaXplID0gMi45LCBoanVzdCA9IDEsIGxheW91dC5leHAgPSAyKQ0KYGBgDQpCeSBzZWVpbmcgdGhlIGNvcnJlbGF0aW9uIHBsb3Qgd2UgY2FuIGRlZmluZSAqTGVuZ3RoMSosICpMZW5ndGgyKiwgKkxlbmd0aDMqIGFyZSBhbGwgaGlnaGx5IHBvc2l0aXZlbHkgY29ycmVsYXRlZCB0byBlYWNoIG90aGVyLiBJZiB0aGUgY29ycmVsYXRpb24gaXMgdG9vIGhpZ2ggaXQgY2FuIGxlYWQgdG8gTXVsdGljb2xsaW5lYXJpdHkgaW4gcmVncmVzc2lvbiBtb2RlbGluZy4gV2hpY2ggc2hvdWxkIGJlIGF2b2lkZWQgYnkgZWxpbWluYXRlIG9uZSBvZiB0aG9zZSB2YXJpYWJsZXMgdGhhdCBoaWdobHkgY29ycmVsYXRlZCB0byBlYWNoIG90aGVycy4gV2hhdCBpbXBhY3QgZG9lcyB0aGUgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMgaGF2ZSBvbiB0aGUgcmVncmVzc2lvbiBhbmFseXNpcz8gIHdlJ2xsIGtub3cgdGhlIGFuc3dlciBsYXRlciBieSBrZWVwIGFsbCBvZiB0aGUgdmFyaWFibGVzLg0KDQoNCiMjIE1vZGVsaW5nIA0KDQojIyMgVHJhaW4tVGVzdCBTcGxpdA0KYGBge3J9DQpmaXNoX3JlZyA8LSBmaXNoX21hcmtldCAlPiUgDQogIHNlbGVjdCgtc3BlY2llcykNCnNldC5zZWVkKDEyMykNCnNhbXBsZXNpemUgPC0gcm91bmQoMC44ICogbnJvdyhmaXNoX3JlZyksIDApDQppbmRleCA8LSBzYW1wbGUoc2VxX2xlbihucm93KGZpc2hfcmVnKSksIHNpemUgPSBzYW1wbGVzaXplKQ0KZGF0YV90cmFpbiA8LSBmaXNoX3JlZ1tpbmRleCwgXQ0KZGF0YV90ZXN0IDwtIGZpc2hfcmVnWy1pbmRleCwgXQ0KYGBgDQoNCiMjIyAxLiBMaW5lYXIgUmVncmVzc2lvbg0KYGBge3J9DQpmaXNoX2xtIDwtIGxtKFdlaWdodCB+LiwgZGF0YSA9IGRhdGFfdHJhaW4pDQpzdW1tYXJ5KGZpc2hfbG0pDQpgYGANCkxldCdzIHRha2UgYSBsb29rIG9uICpQcig+fHR8KSogY29sdW1uLiBXZSB3aWxsIHRha2Ugc2lnbmlmaWNhbmNlIGxldmVsIG9mIDAuMDUuIEl0IG1lYW5zIGlmIHRoZSB2YWx1ZSAqUHIoPnx0fCkqIGlzIGJlbG93IDAuMDUsIHRoYW4gd2UgY2FuIGFzdW1lIHRoYXQgdGhlIHZhcmlhYmxlIGhhcyBzaWduaWZpY2FudCBlZmZlY3QgdG93YXJkIHRoZSBtb2RlbC4gVGhlIHN1bW1hcnkgb2YgZmlzaF9sbSBzaG93biBvbmx5IG9uZSB2YXJpYWJsZSAoKkhlaWdodCopIGhhcyBzaWduaWZpY2FudCBlZmZlY3QgdG93YXJkIG91ciBtb2RlbC4gU28gd2l0aCBldmVyeSBpbmNyZWFzZWQgdmFsdWUgb2Ygb25lIGNtIGluICpIZWlnaHQqIHdpbGwgY29udGlidXRlIHRvIDI0LjA2IGluY3JlYXNlIGluIGZpc2ggV2VpZ2h0Lg0KDQo+ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXZWlnaHQgPSAtNTAzLjg2KzI0LjA2KEhlaWdodCkNCg0KIyMjIDIuIFN0ZXAtV2lzZSBSZWdyZXNzaW9uDQoNCmBgYHtyfQ0KZmlzaF9sbV9zdGVwIDwtIHN0ZXAoZmlzaF9sbSwgZGlyZWN0aW9uID0gImJhY2t3YXJkIikNCnN1bW1hcnkoZmlzaF9sbV9zdGVwKQ0KYGBgDQpUaGlzIHN0ZXAtd2lzZSByZWdyZXNzaW9uIG1ldGhvZCB3aWxsIHByb2R1Y2UgYW4gb3B0aW11bSBmb3JtdWxhIGJhc2VkIG9uIHRoZSBsb3dlc3QgQUlDIHZhbHVlLiBUaGUgQWthaWtlIGluZm9ybWF0aW9uIGNyaXRlcmlvbiAoQUlDKSBpcyBhIG1hdGhlbWF0aWNhbCBtZXRob2QgZm9yIGV2YWx1YXRpbmcgaG93IHdlbGwgYSBtb2RlbCBmaXRzIHRoZSBkYXRhIGl0IHdhcyBnZW5lcmF0ZWQgZnJvbS4gDQoNCldlIGNhbiBzZWUgdGhhdCB0aGUgc3RlcCB3aXNlIHJlZ3Jlc3Npb24gZWxpbWluYXRlcyB0aGUgKkxlbmd0aDIqIGFuZCAqV2lkdGgqIHZhcmlhYmxlcyB0byBwcm9kdWNlIHRoZSBzbWFsbGVzdCBBSUMuIFRoZSBzZWxlY3RlZCB2YXJpYWJsZXMgYXJlIExlbmd0aDEsIExlbmd0aDIsIEhlaWdodC4gTGVuZ3RoMSBhbmQgSGVpZ2h0IGhhdmUgYSBzaWduaWZpY2FudCBlZmZlY3QgdG8gb3VyIG1vZGVsICpmaXNoX2xtX3N0ZXAqIChQcig+fHR8KSBpcyBiZWxvdyAwLjA1KS4gV2UgY2FuIGNoZWNrIHRoZSBBZGp1c3RlZCBSLVNxdWFyZWQgdmFsdWUgZnJvbSAqZmlzaF9sbSogYW5kICpmaXNoX2xtX3N0ZXAqLiBUaGUgZmlyc3QgbW9kZWwgd2l0aCBjb21wbGV0ZSB2YXJpYWJsZXMgaGFzIGFkanVzdGVkIFItc3F1YXJlZCBvZiAwLjg3ODMgb3IgZmlzaF9sbSBtb2RlbCBjYW4gZXhwbGFpbiA4Ny44MyUgb2YgdmFyaWFuY2UgaW4gRmlzaCBXZWlnaHQgKGluZGVwZW5kZW50IHZhcmlhYmxlKS4gV2hpbGUgdGhlIHN0ZXAtd2lzZSByZWdyZXNzaW9uIGhhcyBhZGp1c3RlZCBSLXNxdWFyZWQgb2YgMC44Nzk5LiBUaGVyZSdzIG5vIGJpZyBkaWZmZXJlbmNlIHdpdGggKmZpc2hfbG0qIGFuZCAqZmlzaF9sbV9zdGVwKi4NCg0KPiAgICAgICAgICAgICAgICAgICAgICAgICAgIFdlaWdodCA9IC01MDAuNzY1KzQ5Ljg2OCAoTGVuZ3RoMSktNDAuOTM4KExlbmd0aDMpKzI3Ljg5OChIZWlnaHQpDQoNCiMjIEV2YWx1YXRpb24NCk1lYW4gYWJzb2x1dGUgcGVyY2VudGFnZSBlcnJvciBpcyBjb21tb25seSB1c2VkICBmb3IgcmVncmVzc2lvbiBhbmQgbW9kZWwgZXZhbHVhdGlvbi4gVGhpcyBpcyB0aGUgYXZlcmFnZSBvZiB0aGUgYWJzb2x1dGUgdmFsdWVzIG9mIHRoZSBwZXJjZW50YWdlIGVycm9yczsgaXQgaGFzIHRoZSBhZHZhbnRhZ2Ugb2YgYmVpbmcgZGltZW5zaW9ubGVzcyAoS2VubmVkeSAyMDAzKS4NCiQkIE1BUEUgPSBcZnJhY3sxfXtufVwgXHN1bV97dD0xfV57bn1cIFxsdmVydFxmcmFje0F0LUZ0fXtBdH1cbHZlcnRcICAkJA0KIyMjIDEuIExpbmVhciBSZWdyZXNzaW9uIChmaXNoX2xtKQ0KYGBge3J9DQpsbV9wcmVkIDwtIHByZWRpY3QoZmlzaF9sbSwgbmV3ZGF0YSA9IGRhdGFfdGVzdCAlPiUgc2VsZWN0KC1XZWlnaHQpKQ0KDQojIE1BUEUgb2YgdGVzdCBkYXRhc2V0DQpNQVBFKGxtX3ByZWQsIGRhdGFfdGVzdCRXZWlnaHQpDQpgYGANCiMjIyAyLiBTdGVwLVdpc2UgUmVncmVzc2lvbiAoZmlzaF9sbV9zdGVwKQ0KYGBge3J9DQpsbV9wcmVkX3N0ZXAgPC0gcHJlZGljdChmaXNoX2xtX3N0ZXAsIG5ld2RhdGEgPSBkYXRhX3Rlc3QgJT4lIHNlbGVjdCgtV2VpZ2h0KSkNCg0KIyBNQVBFIG9mIHRlc3QgZGF0YXNldA0KTUFQRShsbV9wcmVkX3N0ZXAsIGRhdGFfdGVzdCRXZWlnaHQpDQpgYGANClRoZSBTdGVwLVdpc2UgUmVncmVzc2lvbiAoZmlzaF9sbV9zdGVwKSBoYXMgYSBzbWFsbGVyIE1BUEUsIGV2ZW4gb25seSBieSBhIHNtYWxsIG1hcmdpbi4gaXQgaW5kaWNhdGVzIHRoYXQgZmlzaF9sbV9zdGVwIGlzIGJldHRlciB0aGFuIGZpc2hfbG0gaW4gcHJlZGljdGluZyB0aGUgZGF0YSBzZXQuIE5leHQsIHdlIGhhdmUgdG8gY2hlY2sgdGhlIGxpbmVhciByZWdyZXNzaW9uIGFzc3VtcHRpb25zIGluICoqZmlzaF9sbV9zdGVwKioNCg0KIyMgQXNzdW1wdGlvbnMNClRoZXJlIGFyZSBmb3VyIGFzc3VtcHRpb25zIGFzc29jaWF0ZWQgd2l0aCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsOg0KIyMjIDEuIExpbmVhcml0eQ0KVGhlIHByZXNlbmNlIG9mIGEgcGF0dGVybiBtYXkgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggc29tZSBhc3BlY3Qgb2YgdGhlIGxpbmVhciBtb2RlbC4gDQpgYGB7cn0NCnBsb3QoZmlzaF9sbV9zdGVwLDEpDQpgYGANClRoZSByZXNpZHVhbCBhbmQgZml0IHZhbHVlIHRoYXQgd2UgaGF2ZSB0ZW5kIHRvIGJlIHF1YWRyYXRpYyBjdXJ2ZS4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgbGluZWFyaXR5IGFzc3VtcHRpb24gaXMgdmlvbGF0ZWQuIA0KDQojIyMgMi4gTm9ybWFsaXR5IG9mIHJlc2lkdWFscw0KVGhlIG5vcm1hbCBwcm9iYWJpbGl0eSBwbG90IG9mIHJlc2lkdWFscyBzaG91bGQgIGZvbGxvdyBhIHN0cmFpZ2h0IGxpbmUuDQpgYGB7cn0NCnBsb3QoZmlzaF9sbV9zdGVwLDIpDQpgYGANClFRIHBsb3Qgb2YgcmVzaWR1YWxzIGNhbiBiZSB1c2VkIHRvICBjaGVjayB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24uIEZyb20gdGhlIFFRIHBsb3QsIG5vdCBhbGwgb2YgdGhlIGRhdGEgZm9sbG93IGEgc3RyYWlnaHQgbGluZS4gc28gd2UgY2FuIGFzc3VtZSB0aGF0IHRoZSByZXNpZHVhbCBkaWRudCBmb2xsb20gdGhlIG5vcm1hbGl0eSBhc3N1bXB0aW9uLiBUbyBtYWtlIHN1cmUsIHdlIGNhbiBjaGVjayBieSBmb3JtYWwgdGVzdCB1c2luZyBTaGFwaXJvIHRlc3QuIA0KYGBge3J9DQpzaGFwaXJvLnRlc3QoZmlzaF9sbV9zdGVwJHJlc2lkdWFscykNCmBgYA0KVGhlIHRlc3QgcmVqZWN0cyB0aGUgaHlwb3RoZXNpcyBvZiBub3JtYWxpdHkgd2hlbiB0aGUgcC12YWx1ZSBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMC4wNS4gVGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0aGF0IHRoZSByZXNpZHVhbHMgZm9sbG93IG5vcm1hbCBkaXN0cmlidXRpb24uIEl0IG1lYW5zIHRoYXQgdGhhdCBvdXIgaHlwb3RoZXNpcyBpcyByZWplY3RlZC4gU28gIG9yIHJlc2lkdWFscyBhcmUgbm90IGZvbGxvd2luZyB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbi4NCg0KIyMjIDMuIEhldGVyb2NlZGFzdGljaXR5IChOb24tY29uc3RhbnQgdmFyaWFuY2Ugb2YgZXJyb3IgdGVybXMpDQpUaGUgcmVzaWR1YWxzIGFyZSBhc3N1bWVkIHRvIGhhdmUgYSBjb25zdGFudCB2YXJpYW5jZS4gDQpgYGB7cn0NCnBsb3QoZmlzaF9sbV9zdGVwLCAzKQ0KYGBgDQpUaGlzIHBsb3Qgc2hvd3MgaWYgcmVzaWR1YWxzIGFyZSBub3Qgc3ByZWFkIGVxdWFsbHkgYWxvbmcgdGhlIHJhbmdlcyBvZiBwcmVkaWN0b3JzLiBJdCBtZWFucyBvdXIgcmVzaWR1YWxzIGRvZXNudCBoYXZlIGNvbnN0YW50IHZhcmlhbmNlLiBUbyBtYWtlIHN1cmUsIFdlIGNhbiBjaGVjayB3aXRoIEJyZXVzY2gtUGFnYW4gdGVzdC4NCmBgYHtyfQ0KYnB0ZXN0KGZpc2hfbG1fc3RlcCkNCmBgYA0KVGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0aGVyZSBpcyBubyBoZXRlcm9jZXNkYXN0aWNpdHkuIG91ciB0ZXN0IHNob3duIHAtdmFsdWUgPCAwLjA1LCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCBoZXRlcm9jZXNkYXN0aWNpdHkgaXMgcHJlc2VudCBpbiBvdXIgbW9kZWwuDQoNCiMjIyA0LiBNdWx0aWNvbGxpbmVhcml0eQ0KTXVsdGljb2xsaW5lYXJpdHkgaXMgdGhlIG9jY3VycmVuY2Ugb2YgaGlnaCBpbnRlcmNvcnJlbGF0aW9ucyBhbW9uZyB0d28gb3IgbW9yZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgaW4gYSBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsLiBUbyBjaGVjayB0aGUgbXVsdGljb2xsaW5lYXJpdHksIHdlIGNhbiBtZWFzdXJlIHRoZSB2YXJpYW5lYyBpbmZsYXRpb24gZmFjdG9yIChWSUYpLiBUaGVyZSdzIG5vIG11bHRpY29sbGluZWFyaXR5IHdoZW4gVklGPDEwLg0KYGBge3J9DQp2aWYoZmlzaF9sbV9zdGVwKQ0KYGBgDQp3aGVuIHdlIGRvIGNvcnJlbGF0aW9uIGNoZWNraW5nLCB3ZSBoYXZlIDMgKExlbmd0aDEsIExlbmd0aDIsIExlbmd0aDMpIHZhcmlhYmxlcyB0aGF0IGhhdmUgYSBoaWdoIGNvcnJlbGF0aW9uIHRvIGVhY2ggb3RoZXIuIElmIHRoZSBjb3JyZWxhdGlvbiBpcyB0b28gaGlnaCBpdCBjYW4gbGVhZCB0byBNdWx0aWNvbGxpbmVhcml0eSBpbiByZWdyZXNzaW9uIG1vZGVsaW5nLiBJbiBTdGVwLXdlc2UgcmVncmVzc2lvbiwgUiBlbGltaW5hdGUgdGhlIExlbmd0aDIgdmFyaWFibGUuIEJ1dCBpdCBzdGlsbCBoYXZlIG11bHRpY29sbGluZWFyaXR5IGluIG91ciBtb2RlbC4gU28gd2UgaGF2ZSB0byBlbGltaW5hdGUgYW5vdGhlciBMZW5ndGggdmFyaWFibGUgKExlbmd0aDEgb3IgTGVuZ3RoMykNCg0KIyMgTW9kZWwgSW1wcm92ZW1lbnQNCm91ciBmaW5hbCBtb2RlbCBkb2VzbuKAmXQgZnVsZmlsbCBhbGwgb2YgdGhlIGxpbmVhciByZWdyZXNzaW9uIGFzc3VtcHRpb25zLiBXZSBjYW4gZml4IHRoZW0gYnkgdHJhbnNmb3JtIHRoZSBkYXRhIGJ5ICoqc3FydCoqIGFuZCByZW1vdmUgb25lIHZhcmlhYmxlIHRoYXQgaGFzIGhpZ2ggY29ycmVsYXRpb24gdG8gZ2V0IHJpZCBvZiBtdWx0aWNvbGluaWVyaXR5LiBpIGNob29zZSBMZW5ndGgxIGFuZCBIZWlnaHQgYXMgYSBmaW5hbCBpbmRlcGVuZGVudCBwcmVkaWN0b3IuDQojIyMgMS4gVHJhaW4tVGVzdCBTcGxpdCBOZXcgTW9kZWwNCmBgYHtyfQ0KZmlzaF9pbXBydiA8LSBmaXNoX21hcmtldCAgJT4lIA0KICBtdXRhdGVfaWYofmlzLm51bWVyaWMoLiksIH5zcXJ0KC4pKSAlPiUgDQogIHNlbGVjdChXZWlnaHQsTGVuZ3RoMSwgSGVpZ2h0KQ0Kc2V0LnNlZWQoMTIzKQ0KZGF0YV90cmFpbjIgPC0gZmlzaF9pbXBydltpbmRleCwgXQ0KZGF0YV90ZXN0MiA8LSBmaXNoX2ltcHJ2Wy1pbmRleCwgXQ0KDQpgYGANCiMjIyAyLiBSZWdyZXNzaW9uIE5ldyBNb2RlbA0KYGBge3J9DQpmaXNoX2xtX2ltcHJ2IDwtIGxtKFdlaWdodCB+IExlbmd0aDErSGVpZ2h0LCBkYXRhID0gZGF0YV90cmFpbjIpDQpzdW1tYXJ5KGZpc2hfbG1faW1wcnYpDQpgYGANCg0KIyMjIDMuIEV2YWx1YXRpb24gTmV3IE1vZGVsDQpgYGB7cn0NCmxtX3ByZWQyIDwtIHByZWRpY3QoZmlzaF9sbV9pbXBydiwgbmV3ZGF0YSA9IGRhdGFfdGVzdDIgJT4lIHNlbGVjdCgtV2VpZ2h0KSkNCg0KIyBNQVBFIG9mIHRlc3QgZGF0YXNldA0KTUFQRShsbV9wcmVkMl4yLChkYXRhX3Rlc3QyJFdlaWdodCleMikNCmBgYA0KT3VyIG5ldyBtb2RlbCBoYXMgUi1zcXVhcmVkIG9mIDkzLjk2JSBhbmQgTUFQRSByZWFsbHkgZHJvcCB0byAwLjIyMDI5ODYuDQoNCiMjIyA0LiBBc3N1bXB0aW9ucyBOZXcgTW9kZWwNCmBgYHtyfQ0KI0xpbmVhcml0eQ0KcGxvdChmaXNoX2xtX2ltcHJ2LDEpDQpgYGANClRoZXJlIGlzIHN0aWxsIHNxdWFyZSBwYXR0ZXJuIGluIG91ciByZXNpZHVhbCBwbG90Lg0KYGBge3J9DQojTm9ybWFsaXR5IG9mIHJlc2lkdWFscw0Kc2hhcGlyby50ZXN0KGZpc2hfbG1faW1wcnYkcmVzaWR1YWxzKQ0KYGBgDQogcC12YWx1ZSA8IDAuMDUsIGl0IG1lYW5zIHRoYXQgcmVzaWR1YWwgaXNudCBub3JtYWxseSBkaXN0cmlidXRlZC4NCmBgYHtyfQ0KI0hldGVyb2NlZGFzdGljaXR5IChOb24tY29uc3RhbnQgdmFyaWFuY2Ugb2YgZXJyb3IgdGVybXMpDQpicHRlc3QoZmlzaF9sbV9pbXBydikNCmBgYA0KIHAtdmFsdWUgPiAwLjA1LCBpdCBtZWFucyB0aGF0IGhldGVyb2Nlc2Rhc3RpY2l0eSBpcyBub3QgcHJlc2VudC4NCmBgYHtyfQ0KI011bHRpY29sbGluZWFyaXR5DQp2aWYoZmlzaF9sbV9pbXBydikNCmBgYA0KYWxsIG9mIFZJRjwxMCwgdGhlcmUncyBubyBNdWx0aWNvbGxpbmVhcml0eQ0KDQojIyBDb25jbHVzaW9uDQpWYXJpYWJsZXMgdGhhdCBhZmZlY3Qgd2VpZ2h0IG9mIGZpc2ggYXJlIGxlbmd0aDEgYW5kIGhlaWdodC4gd2UgbmVlZCB0byBlbGltaW5hdGUgdGhlIHZhcmlhYmxlcyBMZW5ndGgyIGFuZCBMZW5ndGgzIGJlY2F1c2UgdGhlIHR3byB2YXJpYWJsZXMgaGF2ZSBhIGxhcmdlIGNvcnJlbGF0aW9uLiBBbmQgdG8gZnVsZmlsbCB0aGUgYXNzdW1wdGlvbnMgb2YgbGluZWFyIHJlZ3Jlc3Npb24gd2UgbmVlZCB0byB0cmFuc2Zvcm0gZGF0YSB0byAqKnNxcnQqKi4gVGhlIGZpbmFsIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGhhcyBhIE1BUEUgdmFsdWUgb2YgMC4yMjAyOTg2IHdpdGggQWRqdXN0ZWQgUi1zcXVhcmVkOiAwLjkzOTYuIGl0IG1lYW5zIHRoZSBtb2RlbCBpcyBiZXR0ZXIgdGhhbiB0d28gcHJldmlvdXMgbW9kZWxzLiBGb3IgcHJvZml0YWJsZSBidXNpbmVzcyBwdXJwb3NlcywgdG8gaW5jcmVhc2UgdGhlIFdlaWdodCBvZiBmaXNoLCBmaXNoZXJtYW4gIG5lZWRzIHRvIGluY3JlYXNlIHRoZSBMZW5ndGgxIGFuZCBIZWlnaHQgb2YgZWFjaCBmaXNoLg0KDQo=