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=