Krister Martinez
6

library(ISLR)
str(Hitters)
'data.frame':   322 obs. of  20 variables:
 $ AtBat    : int  293 315 479 496 321 594 185 298 323 401 ...
 $ Hits     : int  66 81 130 141 87 169 37 73 81 92 ...
 $ HmRun    : int  1 7 18 20 10 4 1 0 6 17 ...
 $ Runs     : int  30 24 66 65 39 74 23 24 26 49 ...
 $ RBI      : int  29 38 72 78 42 51 8 24 32 66 ...
 $ Walks    : int  14 39 76 37 30 35 21 7 8 65 ...
 $ Years    : int  1 14 3 11 2 11 2 3 2 13 ...
 $ CAtBat   : int  293 3449 1624 5628 396 4408 214 509 341 5206 ...
 $ CHits    : int  66 835 457 1575 101 1133 42 108 86 1332 ...
 $ CHmRun   : int  1 69 63 225 12 19 1 0 6 253 ...
 $ CRuns    : int  30 321 224 828 48 501 30 41 32 784 ...
 $ CRBI     : int  29 414 266 838 46 336 9 37 34 890 ...
 $ CWalks   : int  14 375 263 354 33 194 24 12 8 866 ...
 $ League   : Factor w/ 2 levels "A","N": 1 2 1 2 2 1 2 1 2 1 ...
 $ Division : Factor w/ 2 levels "E","W": 1 2 2 1 1 2 1 2 2 1 ...
 $ PutOuts  : int  446 632 880 200 805 282 76 121 143 0 ...
 $ Assists  : int  33 43 82 11 40 421 127 283 290 0 ...
 $ Errors   : int  20 10 14 3 4 25 7 9 19 0 ...
 $ Salary   : num  NA 475 480 500 91.5 750 70 100 75 1100 ...
 $ NewLeague: Factor w/ 2 levels "A","N": 1 2 1 2 2 1 1 1 2 1 ...
Hitters_Fixed = na.omit(Hitters )
str(Hitters_Fixed)
'data.frame':   263 obs. of  20 variables:
 $ AtBat    : int  315 479 496 321 594 185 298 323 401 574 ...
 $ Hits     : int  81 130 141 87 169 37 73 81 92 159 ...
 $ HmRun    : int  7 18 20 10 4 1 0 6 17 21 ...
 $ Runs     : int  24 66 65 39 74 23 24 26 49 107 ...
 $ RBI      : int  38 72 78 42 51 8 24 32 66 75 ...
 $ Walks    : int  39 76 37 30 35 21 7 8 65 59 ...
 $ Years    : int  14 3 11 2 11 2 3 2 13 10 ...
 $ CAtBat   : int  3449 1624 5628 396 4408 214 509 341 5206 4631 ...
 $ CHits    : int  835 457 1575 101 1133 42 108 86 1332 1300 ...
 $ CHmRun   : int  69 63 225 12 19 1 0 6 253 90 ...
 $ CRuns    : int  321 224 828 48 501 30 41 32 784 702 ...
 $ CRBI     : int  414 266 838 46 336 9 37 34 890 504 ...
 $ CWalks   : int  375 263 354 33 194 24 12 8 866 488 ...
 $ League   : Factor w/ 2 levels "A","N": 2 1 2 2 1 2 1 2 1 1 ...
 $ Division : Factor w/ 2 levels "E","W": 2 2 1 1 2 1 2 2 1 1 ...
 $ PutOuts  : int  632 880 200 805 282 76 121 143 0 238 ...
 $ Assists  : int  43 82 11 40 421 127 283 290 0 445 ...
 $ Errors   : int  10 14 3 4 25 7 9 19 0 22 ...
 $ Salary   : num  475 480 500 91.5 750 ...
 $ NewLeague: Factor w/ 2 levels "A","N": 2 1 2 2 1 1 1 2 1 1 ...
 - attr(*, "na.action")= 'omit' Named int [1:59] 1 16 19 23 31 33 37 39 40 42 ...
  ..- attr(*, "names")= chr [1:59] "-Andy Allanson" "-Billy Beane" "-Bruce Bochte" "-Bob Boone" ...
head(Hitters_Fixed)

Apply the Best Subset Selection method to predict “Salary” (i.e., Salary is the outcome variable) based on all the predictors with the exemption of the three categorical predictors: League, Division, and NewLeague. Thus, do NOT include these three predictors when you apply the method. You must apply the Best Subset Selection method based on adjusted R squared

library(leaps)

Preparing the data by excluding the categorical variables


Hitters_Fixed <- Hitters_Fixed[, !(names(Hitters_Fixed) %in% c("League", "Division", "NewLeague"))]

Running regsubset from leaps


subset_fit <- regsubsets(Salary ~ ., data=Hitters_Fixed, nvmax=ncol(Hitters_Fixed)-1, method="exhaustive")

summary(subset_fit)
Subset selection object
Call: regsubsets.formula(Salary ~ ., data = Hitters_Fixed, nvmax = ncol(Hitters_Fixed) - 
    1, method = "exhaustive")
16 Variables  (and intercept)
        Forced in Forced out
AtBat       FALSE      FALSE
Hits        FALSE      FALSE
HmRun       FALSE      FALSE
Runs        FALSE      FALSE
RBI         FALSE      FALSE
Walks       FALSE      FALSE
Years       FALSE      FALSE
CAtBat      FALSE      FALSE
CHits       FALSE      FALSE
CHmRun      FALSE      FALSE
CRuns       FALSE      FALSE
CRBI        FALSE      FALSE
CWalks      FALSE      FALSE
PutOuts     FALSE      FALSE
Assists     FALSE      FALSE
Errors      FALSE      FALSE
1 subsets of each size up to 16
Selection Algorithm: exhaustive
          AtBat Hits HmRun Runs RBI Walks Years CAtBat CHits CHmRun CRuns CRBI CWalks PutOuts Assists Errors
1  ( 1 )  " "   " "  " "   " "  " " " "   " "   " "    " "   " "    " "   "*"  " "    " "     " "     " "   
2  ( 1 )  " "   "*"  " "   " "  " " " "   " "   " "    " "   " "    " "   "*"  " "    " "     " "     " "   
3  ( 1 )  " "   "*"  " "   " "  " " " "   " "   " "    " "   " "    " "   "*"  " "    "*"     " "     " "   
4  ( 1 )  "*"   "*"  " "   " "  " " " "   " "   " "    " "   " "    " "   "*"  " "    "*"     " "     " "   
5  ( 1 )  "*"   "*"  " "   " "  " " "*"   " "   " "    " "   " "    " "   "*"  " "    "*"     " "     " "   
6  ( 1 )  "*"   "*"  " "   " "  " " "*"   " "   " "    " "   " "    "*"   " "  "*"    "*"     " "     " "   
7  ( 1 )  "*"   "*"  " "   " "  " " "*"   " "   " "    " "   "*"    "*"   " "  "*"    "*"     " "     " "   
8  ( 1 )  "*"   "*"  " "   " "  " " "*"   " "   "*"    " "   " "    "*"   "*"  "*"    "*"     " "     " "   
9  ( 1 )  "*"   "*"  " "   " "  " " "*"   " "   "*"    " "   " "    "*"   "*"  "*"    "*"     "*"     " "   
10  ( 1 ) "*"   "*"  " "   "*"  " " "*"   " "   "*"    " "   " "    "*"   "*"  "*"    "*"     "*"     " "   
11  ( 1 ) "*"   "*"  " "   "*"  " " "*"   " "   "*"    " "   " "    "*"   "*"  "*"    "*"     "*"     "*"   
12  ( 1 ) "*"   "*"  "*"   "*"  " " "*"   " "   "*"    " "   " "    "*"   "*"  "*"    "*"     "*"     "*"   
13  ( 1 ) "*"   "*"  "*"   "*"  " " "*"   " "   "*"    "*"   " "    "*"   "*"  "*"    "*"     "*"     "*"   
14  ( 1 ) "*"   "*"  "*"   "*"  " " "*"   "*"   "*"    " "   "*"    "*"   "*"  "*"    "*"     "*"     "*"   
15  ( 1 ) "*"   "*"  "*"   "*"  " " "*"   "*"   "*"    "*"   "*"    "*"   "*"  "*"    "*"     "*"     "*"   
16  ( 1 ) "*"   "*"  "*"   "*"  "*" "*"   "*"   "*"    "*"   "*"    "*"   "*"  "*"    "*"     "*"     "*"   

First, we are going to use Adjusted R Squared to compare the equations

summary (subset_fit)$adjr2
 [1] 0.3188503 0.4208024 0.4450753 0.4621594 0.4805047 0.4855144 0.4983282 0.5036515 0.5086408 0.5077531 0.5064485 0.5049397 0.5031603 0.5012591 0.4992625
[16] 0.4972271

Adjusted R squared can be transformed into a % (similar to R squared). Let’s do it:


adj_r2_percent_HF = summary (subset_fit)$adjr2 * 100
adj_r2_percent_HF
 [1] 31.88503 42.08024 44.50753 46.21594 48.05047 48.55144 49.83282 50.36515 50.86408 50.77531 50.64485 50.49397 50.31603 50.12591 49.92625 49.72271

which.max(adj_r2_percent_HF)
[1] 9
adj_r2_percent_HF
 [1] 31.88503 42.08024 44.50753 46.21594 48.05047 48.55144 49.83282 50.36515 50.86408 50.77531 50.64485 50.49397 50.31603 50.12591 49.92625 49.72271
adj_r2_percent_HF[2:length(adj_r2_percent_HF)] - adj_r2_percent_HF [1: (length(adj_r2_percent_HF)-1)]
 [1] 10.19521101  2.42729256  1.70840877  1.83452551  0.50097404  1.28137897  0.53232663  0.49893771 -0.08877784 -0.13045059 -0.15088969 -0.17793131
[13] -0.19011938 -0.19966419 -0.20353608

chosen_HF_number= which (adj_r2_percent_HF[2:length(adj_r2_percent_HF)] - adj_r2_percent_HF [1: (length(adj_r2_percent_HF)-1)] < 1) [1]

chosen_HF_number
[1] 5
coef (subset_fit, 5)
(Intercept)       AtBat        Hits       Walks        CRBI     PutOuts 
 25.2819915  -2.0349977   8.1842739   3.9059431   0.6417565   0.2645828 

Write the equation that resulted from applying the Best Subset Selection method based on adjusted R squared (alternative 2).

Predicted Salary = 25.282 - 2.035 * AtBat + 8.184 * Hits + 3.906 * Walks + 0.642 * CRBI + 0.0265 * PutOuts

Does the equation that you obtained allow you to eliminate at least 50% of the variation existing in the values of Salary? Justify.

model_HF <- lm(Salary ~ AtBat + Hits + Walks + CRBI + PutOuts, data=Hitters_Fixed)
summary(model_HF)

Call:
lm(formula = Salary ~ AtBat + Hits + Walks + CRBI + PutOuts, 
    data = Hitters_Fixed)

Residuals:
    Min      1Q  Median      3Q     Max 
-935.75 -170.61  -29.91  119.62 2109.44 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 25.28199   62.36927   0.405 0.685550    
AtBat       -2.03500    0.53330  -3.816 0.000170 ***
Hits         8.18427    1.67910   4.874 1.91e-06 ***
Walks        3.90594    1.22837   3.180 0.001655 ** 
CRBI         0.64176    0.06549   9.799  < 2e-16 ***
PutOuts      0.26458    0.07600   3.481 0.000586 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 325.1 on 257 degrees of freedom
Multiple R-squared:  0.4904,    Adjusted R-squared:  0.4805 
F-statistic: 49.47 on 5 and 257 DF,  p-value: < 2.2e-16

The Adjusted R-squared that I obtained from the summary of the regression model is 0.4805 which means that 48% of the variation in salary is explain by the model. so 48 is less than 50 so the answer is no it is only 48%

Apply the Best Subset Selection method again, but this time using RSE (alternative 2). Consider a reduction in RSE practically significant if it represents at least a %1 reduction. Show all your work (I need to see every code chunk you wrote and the output you got).

We continue:..

Creating a vector to store the variable p predictors


p = 1:16

Computing RSE for each equation


rse_values_HF = sqrt((summary (subset_fit)$rss)/(nrow(Hitters_Fixed)-p-1))

rse_values_HF
 [1] 372.3163 343.3240 336.0530 330.8397 325.1484 323.5768 319.5219 317.8222 316.2207 316.5063 316.9254 317.4095 317.9794 318.5872 319.2242 319.8724

IF we need to make the decision about the “best” model based on RSE, we observe the values of RSE to identify when it stops decreasing. We find where the minimum value of RSE happens for this purpose


which.min(rse_values_HF)
[1] 9
percent_decrease_rse_HF= (rse_values_HF [1: (length(rse_values_HF) -1 ) ]- rse_values_HF[2:length(rse_values_HF)]) / rse_values_HF [1: (length(rse_values_HF) -1 )]

percent_decrease_rse_HF
 [1]  0.0778701317  0.0211781819  0.0155134931  0.0172025098  0.0048334185  0.0125315306  0.0053196761  0.0050387768 -0.0009029828 -0.0013241756
[11] -0.0015274450 -0.0017954551 -0.0019114602 -0.0019996834 -0.0020303020

which (percent_decrease_rse_HF < 0.01)[1]
[1] 5

Here are the predictors

coef (subset_fit, 5)
(Intercept)       AtBat        Hits       Walks        CRBI     PutOuts 
 25.2819915  -2.0349977   8.1842739   3.9059431   0.6417565   0.2645828 

I obtained the same equation as the one in part 1a

After you apply the method, if you get an equation different from the one you obtained in 1a, write this new equation. Otherwise, you answer this: “I obtained the same equation as the one in part 1a”

For the equation you obtained in 1 a), assume that Assumption 2 regarding the validity of the linear regression analysis is satisfied. Run an analysis to check for the validity of all other assumptions. Show all your work AND comment on your findings.

plot(predict (model_HF), residuals (model_HF))

# abline() allows us to draw a horizontal line at y=0 (because 0 is the mean of the residuals)

abline(h=0)

Interpretation: In this case, we can clearly see that the residuals show a nonlinear pattern. The data points seem to follow a parabolic shape; therefore, a quadratic model might be more appropriate than a linear model ( assumption 1 is not satisfied). We had already observed this nonlinear pattern when we did the original scatter plot. However, this graph shows the non-linear pattern more clearly.

When assumption 1 is not satisfied, it is really hard to evaluate assumption 4. So, NO need to assess assumption 4 in this case.

To check assumption 3 (whether the residuals follow a normal distribution), we are going to conduct a hypothesis test: the Shapiro test to check for normality

Ho: The residuals follow a Normal distribution Ha: The residuals do NOT follow a Normal distribution

shapiro.test(residuals (model_HF))

    Shapiro-Wilk normality test

data:  residuals(model_HF)
W = 0.90272, p-value = 5.266e-12

PV= 5.266e-12, which is a lot smaller than alpha (0.05). Thus, we reject Ho and support Ha. The data is giving us evidence that the residuals do NOT follow a Normal distribution. Assumption 3 is not satisfied either!

Apply Linear Regression to predict “Salary” based on these predictors: Hits, League, and Division. Show all your work.

Hitters_Fixed <- na.omit(Hitters)
model_2_HF = lm(Salary~Hits + League + Division , data = Hitters_Fixed)
summary(model_2_HF)

Call:
lm(formula = Salary ~ Hits + League + Division, data = Hitters_Fixed)

Residuals:
    Min      1Q  Median      3Q     Max 
-844.52 -264.61  -59.75  169.95 1958.07 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  118.1472    78.3232   1.508  0.13266    
Hits           4.3350     0.5574   7.778 1.75e-13 ***
LeagueN       46.7805    50.1133   0.933  0.35143    
DivisionW   -140.7468    49.6199  -2.837  0.00492 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 400.8 on 259 degrees of freedom
Multiple R-squared:  0.2196,    Adjusted R-squared:  0.2105 
F-statistic: 24.29 on 3 and 259 DF,  p-value: 6.985e-14
str(Hitters_Fixed)
'data.frame':   263 obs. of  20 variables:
 $ AtBat    : int  315 479 496 321 594 185 298 323 401 574 ...
 $ Hits     : int  81 130 141 87 169 37 73 81 92 159 ...
 $ HmRun    : int  7 18 20 10 4 1 0 6 17 21 ...
 $ Runs     : int  24 66 65 39 74 23 24 26 49 107 ...
 $ RBI      : int  38 72 78 42 51 8 24 32 66 75 ...
 $ Walks    : int  39 76 37 30 35 21 7 8 65 59 ...
 $ Years    : int  14 3 11 2 11 2 3 2 13 10 ...
 $ CAtBat   : int  3449 1624 5628 396 4408 214 509 341 5206 4631 ...
 $ CHits    : int  835 457 1575 101 1133 42 108 86 1332 1300 ...
 $ CHmRun   : int  69 63 225 12 19 1 0 6 253 90 ...
 $ CRuns    : int  321 224 828 48 501 30 41 32 784 702 ...
 $ CRBI     : int  414 266 838 46 336 9 37 34 890 504 ...
 $ CWalks   : int  375 263 354 33 194 24 12 8 866 488 ...
 $ League   : Factor w/ 2 levels "A","N": 2 1 2 2 1 2 1 2 1 1 ...
 $ Division : Factor w/ 2 levels "E","W": 2 2 1 1 2 1 2 2 1 1 ...
 $ PutOuts  : int  632 880 200 805 282 76 121 143 0 238 ...
 $ Assists  : int  43 82 11 40 421 127 283 290 0 445 ...
 $ Errors   : int  10 14 3 4 25 7 9 19 0 22 ...
 $ Salary   : num  475 480 500 91.5 750 ...
 $ NewLeague: Factor w/ 2 levels "A","N": 2 1 2 2 1 1 1 2 1 1 ...
 - attr(*, "na.action")= 'omit' Named int [1:59] 1 16 19 23 31 33 37 39 40 42 ...
  ..- attr(*, "names")= chr [1:59] "-Andy Allanson" "-Billy Beane" "-Bruce Bochte" "-Bob Boone" ...

Predicted Salary = 118.1472 + 4.3350 * Hits - 140.7468 * Division

#a) Based on your results, write the equation that you consider should be used to predict players’ salaries.

Predicted Salary = 118.1472 + 4.3350 * Hits - 140.7468 * Division

#b) Use this equation to predict the Salary for a player who connected 120 hits and played in the National League in the West division in 1986. Show your work.

#levels(Hitters_Fixed$Division)
contrasts(Hitters_Fixed$Division)
  W
E 0
W 1
pred_Salary_2b = 118.1472 + 4.3350 * 120 - 140.7468 * 1
pred_Salary_2b
[1] 497.6004

The predicted salary is $497.60 for a player in the West Division with 120 hits.

#c) Does this equation improve when you add the “NewLeague” predictor? Justify your answer and show all your work.

model_2c_HF = lm(Salary~Hits + League + Division+NewLeague , data = Hitters_Fixed)
summary(model_2c_HF)

Call:
lm(formula = Salary ~ Hits + League + Division + NewLeague, data = Hitters_Fixed)

Residuals:
    Min      1Q  Median      3Q     Max 
-843.75 -265.76  -59.39  171.08 1961.07 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  118.317     78.469   1.508  0.13283    
Hits           4.345      0.560   7.760 1.99e-13 ***
LeagueN       67.617     99.084   0.682  0.49559    
DivisionW   -140.672     49.711  -2.830  0.00502 ** 
NewLeagueN   -24.012     98.445  -0.244  0.80749    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 401.6 on 258 degrees of freedom
Multiple R-squared:  0.2197,    Adjusted R-squared:  0.2076 
F-statistic: 18.17 on 4 and 258 DF,  p-value: 3.683e-13

The p value .80749 for NewLeague is greater than alpha, therefore, there is NO statistically significant difference. In this case, we can determine NewLeague predictor does not improve the equation.

# Load necessary library
library(ggplot2)

# Histogram of Salary
ggplot(Hitters_Fixed, aes(x = Salary)) +
  geom_histogram(binwidth = 500, fill = "blue", color = "black") +
  ggtitle("Distribution of Salaries") +
  xlab("Salary") +
  ylab("Frequency")

# Scatterplot of Salary vs Hits
ggplot(Hitters_Fixed, aes(x = Hits, y = Salary)) +
  geom_point(aes(color = Division), alpha = 0.7) +  # Color points by Division to add another dimension of analysis
  geom_smooth(method = "lm", se = FALSE, color = "red") +
  ggtitle("Relationship between Hits and Salary") +
  xlab("Hits") +
  ylab("Salary") +
  theme_minimal()

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KS3Jpc3RlciBNYXJ0aW5leiA8YnI+DQo2IA0KDQpgYGB7cn0NCmxpYnJhcnkoSVNMUikNCmBgYA0KDQpgYGB7cn0NCnN0cihIaXR0ZXJzKQ0KYGBgDQoNCmBgYHtyfQ0KSGl0dGVyc19GaXhlZCA9IG5hLm9taXQoSGl0dGVycyApDQpgYGANCg0KYGBge3J9DQpzdHIoSGl0dGVyc19GaXhlZCkNCmBgYA0KYGBge3J9DQpoZWFkKEhpdHRlcnNfRml4ZWQpDQpgYGANCg0KDQpBcHBseSB0aGUgQmVzdCBTdWJzZXQgU2VsZWN0aW9uIG1ldGhvZCB0byBwcmVkaWN0IOKAnFNhbGFyeeKAnSAoaS5lLiwgU2FsYXJ5IGlzIHRoZSBvdXRjb21lIHZhcmlhYmxlKSBiYXNlZCBvbiBhbGwgdGhlIHByZWRpY3RvcnMgd2l0aCB0aGUgZXhlbXB0aW9uIG9mIHRoZSB0aHJlZSBjYXRlZ29yaWNhbCBwcmVkaWN0b3JzOiBMZWFndWUsIERpdmlzaW9uLCBhbmQgTmV3TGVhZ3VlLiBUaHVzLCBkbyBOT1QgaW5jbHVkZSB0aGVzZSB0aHJlZSBwcmVkaWN0b3JzIHdoZW4geW91IGFwcGx5IHRoZSBtZXRob2QuIFlvdSBtdXN0IGFwcGx5IHRoZSBCZXN0IFN1YnNldCBTZWxlY3Rpb24gbWV0aG9kIGJhc2VkIG9uIGFkanVzdGVkIFIgc3F1YXJlZA0KDQpgYGB7cn0NCmxpYnJhcnkobGVhcHMpDQpgYGANCg0KUHJlcGFyaW5nIHRoZSBkYXRhIGJ5IGV4Y2x1ZGluZyB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzDQoNCmBgYHtyfQ0KDQpIaXR0ZXJzX0ZpeGVkIDwtIEhpdHRlcnNfRml4ZWRbLCAhKG5hbWVzKEhpdHRlcnNfRml4ZWQpICVpbiUgYygiTGVhZ3VlIiwgIkRpdmlzaW9uIiwgIk5ld0xlYWd1ZSIpKV0NCg0KYGBgDQoNClJ1bm5pbmcgcmVnc3Vic2V0IGZyb20gbGVhcHMNCg0KYGBge3J9DQoNCnN1YnNldF9maXQgPC0gcmVnc3Vic2V0cyhTYWxhcnkgfiAuLCBkYXRhPUhpdHRlcnNfRml4ZWQsIG52bWF4PW5jb2woSGl0dGVyc19GaXhlZCktMSwgbWV0aG9kPSJleGhhdXN0aXZlIikNCg0Kc3VtbWFyeShzdWJzZXRfZml0KQ0KDQpgYGANCg0KRmlyc3QsIHdlIGFyZSBnb2luZyB0byB1c2UgQWRqdXN0ZWQgUiBTcXVhcmVkIHRvIGNvbXBhcmUgdGhlIGVxdWF0aW9ucw0KDQpgYGB7cn0NCnN1bW1hcnkgKHN1YnNldF9maXQpJGFkanIyDQoNCmBgYA0KDQpBZGp1c3RlZCBSIHNxdWFyZWQgY2FuIGJlIHRyYW5zZm9ybWVkIGludG8gYSAlIChzaW1pbGFyIHRvIFIgc3F1YXJlZCkuIExldCdzIGRvIGl0Og0KDQpgYGB7cn0NCg0KYWRqX3IyX3BlcmNlbnRfSEYgPSBzdW1tYXJ5IChzdWJzZXRfZml0KSRhZGpyMiAqIDEwMA0KYWRqX3IyX3BlcmNlbnRfSEYNCg0KYGBgDQoNCmBgYHtyfQ0KDQp3aGljaC5tYXgoYWRqX3IyX3BlcmNlbnRfSEYpDQoNCmFkal9yMl9wZXJjZW50X0hGDQoNCmBgYA0KDQpgYGB7cn0NCmFkal9yMl9wZXJjZW50X0hGWzI6bGVuZ3RoKGFkal9yMl9wZXJjZW50X0hGKV0gLSBhZGpfcjJfcGVyY2VudF9IRiBbMTogKGxlbmd0aChhZGpfcjJfcGVyY2VudF9IRiktMSldDQpgYGANCg0KYGBge3J9DQoNCmNob3Nlbl9IRl9udW1iZXI9IHdoaWNoIChhZGpfcjJfcGVyY2VudF9IRlsyOmxlbmd0aChhZGpfcjJfcGVyY2VudF9IRildIC0gYWRqX3IyX3BlcmNlbnRfSEYgWzE6IChsZW5ndGgoYWRqX3IyX3BlcmNlbnRfSEYpLTEpXSA8IDEpIFsxXQ0KDQpjaG9zZW5fSEZfbnVtYmVyDQoNCmBgYA0KDQpgYGB7cn0NCmNvZWYgKHN1YnNldF9maXQsIDUpDQpgYGANCg0KDQpXcml0ZSB0aGUgZXF1YXRpb24gdGhhdCByZXN1bHRlZCBmcm9tIGFwcGx5aW5nIHRoZSBCZXN0IFN1YnNldCBTZWxlY3Rpb24gbWV0aG9kIGJhc2VkIG9uIGFkanVzdGVkIFIgc3F1YXJlZCAoYWx0ZXJuYXRpdmUgMikuDQoNClByZWRpY3RlZCBTYWxhcnkgPSAyNS4yODIgLSAyLjAzNSAqIEF0QmF0ICsgOC4xODQgKiBIaXRzICsgMy45MDYgKiBXYWxrcyArIDAuNjQyICogQ1JCSSArIDAuMDI2NSAqIFB1dE91dHMNCg0KDQpEb2VzIHRoZSBlcXVhdGlvbiB0aGF0IHlvdSBvYnRhaW5lZCBhbGxvdyB5b3UgdG8gZWxpbWluYXRlIGF0IGxlYXN0IDUwJSBvZiB0aGUgdmFyaWF0aW9uIGV4aXN0aW5nIGluIHRoZSB2YWx1ZXMgb2YgU2FsYXJ5PyBKdXN0aWZ5Lg0KDQpgYGB7cn0NCm1vZGVsX0hGIDwtIGxtKFNhbGFyeSB+IEF0QmF0ICsgSGl0cyArIFdhbGtzICsgQ1JCSSArIFB1dE91dHMsIGRhdGE9SGl0dGVyc19GaXhlZCkNCnN1bW1hcnkobW9kZWxfSEYpDQoNCmBgYA0KDQpUaGUgQWRqdXN0ZWQgUi1zcXVhcmVkIHRoYXQgSSBvYnRhaW5lZCBmcm9tIHRoZSBzdW1tYXJ5IG9mIHRoZSByZWdyZXNzaW9uIG1vZGVsIGlzIDAuNDgwNSB3aGljaCBtZWFucyB0aGF0IDQ4JSBvZiB0aGUgdmFyaWF0aW9uIGluIHNhbGFyeSBpcyBleHBsYWluIGJ5IHRoZSBtb2RlbC4gc28gNDggaXMgbGVzcyB0aGFuIDUwIHNvIHRoZSBhbnN3ZXIgaXMgbm8gaXQgaXMgb25seSA0OCUNCg0KQXBwbHkgdGhlIEJlc3QgU3Vic2V0IFNlbGVjdGlvbiBtZXRob2QgYWdhaW4sIGJ1dCB0aGlzIHRpbWUgdXNpbmcgUlNFIChhbHRlcm5hdGl2ZSAyKS4gQ29uc2lkZXIgYSByZWR1Y3Rpb24gaW4gUlNFIHByYWN0aWNhbGx5IHNpZ25pZmljYW50IGlmIGl0IHJlcHJlc2VudHMgYXQgbGVhc3QgYSAlMSByZWR1Y3Rpb24uIFNob3cgYWxsIHlvdXIgd29yayAoSSBuZWVkIHRvIHNlZSBldmVyeSBjb2RlIGNodW5rIHlvdSB3cm90ZSBhbmQgdGhlIG91dHB1dCB5b3UgZ290KS4NCg0KDQpXZSBjb250aW51ZTouLg0KDQpDcmVhdGluZyBhIHZlY3RvciB0byBzdG9yZSB0aGUgdmFyaWFibGUgcCBwcmVkaWN0b3JzDQoNCmBgYHtyfQ0KDQpwID0gMToxNg0KDQpgYGANCg0KQ29tcHV0aW5nIFJTRSBmb3IgZWFjaCBlcXVhdGlvbg0KDQpgYGB7cn0NCg0KcnNlX3ZhbHVlc19IRiA9IHNxcnQoKHN1bW1hcnkgKHN1YnNldF9maXQpJHJzcykvKG5yb3coSGl0dGVyc19GaXhlZCktcC0xKSkNCg0KcnNlX3ZhbHVlc19IRg0KDQpgYGANCg0KSUYgd2UgbmVlZCB0byBtYWtlIHRoZSBkZWNpc2lvbiBhYm91dCB0aGUgImJlc3QiIG1vZGVsIGJhc2VkIG9uIFJTRSwgd2Ugb2JzZXJ2ZSB0aGUgdmFsdWVzIG9mIFJTRSB0byBpZGVudGlmeSB3aGVuIGl0IHN0b3BzIGRlY3JlYXNpbmcuIFdlIGZpbmQgd2hlcmUgdGhlIG1pbmltdW0gdmFsdWUgb2YgUlNFIGhhcHBlbnMgZm9yIHRoaXMgcHVycG9zZQ0KDQpgYGB7cn0NCg0Kd2hpY2gubWluKHJzZV92YWx1ZXNfSEYpDQoNCmBgYA0KYGBge3J9DQpwZXJjZW50X2RlY3JlYXNlX3JzZV9IRj0gKHJzZV92YWx1ZXNfSEYgWzE6IChsZW5ndGgocnNlX3ZhbHVlc19IRikgLTEgKSBdLSByc2VfdmFsdWVzX0hGWzI6bGVuZ3RoKHJzZV92YWx1ZXNfSEYpXSkgLyByc2VfdmFsdWVzX0hGIFsxOiAobGVuZ3RoKHJzZV92YWx1ZXNfSEYpIC0xICldDQoNCnBlcmNlbnRfZGVjcmVhc2VfcnNlX0hGDQpgYGANCg0KYGBge3J9DQoNCndoaWNoIChwZXJjZW50X2RlY3JlYXNlX3JzZV9IRiA8IDAuMDEpWzFdDQoNCmBgYA0KSGVyZSBhcmUgdGhlIHByZWRpY3RvcnMNCg0KYGBge3J9DQpjb2VmIChzdWJzZXRfZml0LCA1KQ0KYGBgDQoNCkkgb2J0YWluZWQgdGhlIHNhbWUgZXF1YXRpb24gYXMgdGhlIG9uZSBpbiBwYXJ0IDFhDQoNCkFmdGVyIHlvdSBhcHBseSB0aGUgbWV0aG9kLCBpZiB5b3UgZ2V0IGFuIGVxdWF0aW9uIGRpZmZlcmVudCBmcm9tIHRoZSBvbmUgeW91IG9idGFpbmVkIGluIDFhLCB3cml0ZSB0aGlzIG5ldyBlcXVhdGlvbi4gT3RoZXJ3aXNlLCB5b3UgYW5zd2VyIHRoaXM6ICJJIG9idGFpbmVkIHRoZSBzYW1lIGVxdWF0aW9uIGFzIHRoZSBvbmUgaW4gcGFydCAxYSINCg0KDQpGb3IgdGhlIGVxdWF0aW9uIHlvdSBvYnRhaW5lZCBpbiAxIGEpLCBhc3N1bWUgdGhhdCBBc3N1bXB0aW9uIDIgcmVnYXJkaW5nIHRoZSB2YWxpZGl0eSBvZiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMgaXMgc2F0aXNmaWVkLiBSdW4gYW4gYW5hbHlzaXMgdG8gY2hlY2sgZm9yIHRoZSB2YWxpZGl0eSBvZiBhbGwgb3RoZXIgYXNzdW1wdGlvbnMuIFNob3cgYWxsIHlvdXIgd29yayBBTkQgY29tbWVudCBvbiB5b3VyIGZpbmRpbmdzLg0KDQpgYGB7cn0NCnBsb3QocHJlZGljdCAobW9kZWxfSEYpLCByZXNpZHVhbHMgKG1vZGVsX0hGKSkNCg0KIyBhYmxpbmUoKSBhbGxvd3MgdXMgdG8gZHJhdyBhIGhvcml6b250YWwgbGluZSBhdCB5PTAgKGJlY2F1c2UgMCBpcyB0aGUgbWVhbiBvZiB0aGUgcmVzaWR1YWxzKQ0KDQphYmxpbmUoaD0wKQ0KYGBgDQoNCg0KSW50ZXJwcmV0YXRpb246IEluIHRoaXMgY2FzZSwgd2UgY2FuIGNsZWFybHkgc2VlIHRoYXQgdGhlIHJlc2lkdWFscyBzaG93IGEgbm9ubGluZWFyIHBhdHRlcm4uIFRoZSBkYXRhIHBvaW50cyBzZWVtIHRvIGZvbGxvdyBhIHBhcmFib2xpYyBzaGFwZTsgdGhlcmVmb3JlLCBhIHF1YWRyYXRpYyBtb2RlbCBtaWdodCBiZSBtb3JlIGFwcHJvcHJpYXRlIHRoYW4gYSBsaW5lYXIgbW9kZWwgKCBfX2Fzc3VtcHRpb24gMSBpcyBub3Qgc2F0aXNmaWVkX18pLiBXZSBoYWQgYWxyZWFkeSBvYnNlcnZlZCB0aGlzIG5vbmxpbmVhciBwYXR0ZXJuIHdoZW4gd2UgZGlkIHRoZSBvcmlnaW5hbCBzY2F0dGVyIHBsb3QuIEhvd2V2ZXIsIHRoaXMgZ3JhcGggc2hvd3MgdGhlIG5vbi1saW5lYXIgcGF0dGVybiBtb3JlIGNsZWFybHkuDQoNCldoZW4gYXNzdW1wdGlvbiAxIGlzIG5vdCBzYXRpc2ZpZWQsIGl0IGlzIHJlYWxseSBoYXJkIHRvIGV2YWx1YXRlIGFzc3VtcHRpb24gNC4gU28sIE5PIG5lZWQgIHRvIGFzc2VzcyBhc3N1bXB0aW9uIDQgaW4gdGhpcyBjYXNlLg0KDQoNClRvIGNoZWNrIGFzc3VtcHRpb24gMyAod2hldGhlciB0aGUgcmVzaWR1YWxzIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24pLCB3ZSBhcmUgZ29pbmcgdG8gY29uZHVjdCBhIGh5cG90aGVzaXMgdGVzdDogdGhlIFNoYXBpcm8gdGVzdCB0byBjaGVjayBmb3Igbm9ybWFsaXR5DQoNCkhvOiBUaGUgcmVzaWR1YWxzIGZvbGxvdyBhIE5vcm1hbCBkaXN0cmlidXRpb24NCkhhOiBUaGUgcmVzaWR1YWxzIGRvIE5PVCBmb2xsb3cgYSBOb3JtYWwgZGlzdHJpYnV0aW9uDQoNCmBgYHtyfQ0Kc2hhcGlyby50ZXN0KHJlc2lkdWFscyAobW9kZWxfSEYpKQ0KYGBgDQpQVj0gNS4yNjZlLTEyLCB3aGljaCBpcyBhIGxvdCBzbWFsbGVyIHRoYW4gYWxwaGEgKDAuMDUpLiBUaHVzLCB3ZSByZWplY3QgSG8gYW5kIHN1cHBvcnQgSGEuIFRoZSBkYXRhIGlzIGdpdmluZyB1cyBldmlkZW5jZSB0aGF0IHRoZSByZXNpZHVhbHMgZG8gTk9UIGZvbGxvdyBhIE5vcm1hbCBkaXN0cmlidXRpb24uIEFzc3VtcHRpb24gMyBpcyBub3Qgc2F0aXNmaWVkIGVpdGhlciENCg0KDQoNCkFwcGx5IExpbmVhciBSZWdyZXNzaW9uIHRvIHByZWRpY3Qg4oCcU2FsYXJ54oCdIGJhc2VkIG9uIHRoZXNlIHByZWRpY3RvcnM6IEhpdHMsIExlYWd1ZSwgYW5kIERpdmlzaW9uLiBTaG93IGFsbCB5b3VyIHdvcmsuDQoNCg0KYGBge3J9DQpIaXR0ZXJzX0ZpeGVkIDwtIG5hLm9taXQoSGl0dGVycykNCg0KYGBgDQoNCg0KYGBge3J9DQptb2RlbF8yX0hGID0gbG0oU2FsYXJ5fkhpdHMgKyBMZWFndWUgKyBEaXZpc2lvbiAsIGRhdGEgPSBIaXR0ZXJzX0ZpeGVkKQ0Kc3VtbWFyeShtb2RlbF8yX0hGKQ0KDQpgYGANCmBgYHtyfQ0Kc3RyKEhpdHRlcnNfRml4ZWQpDQpgYGANCg0KUHJlZGljdGVkIFNhbGFyeSA9IDExOC4xNDcyICsgNC4zMzUwICogSGl0cyAtIDE0MC43NDY4ICogRGl2aXNpb24NCg0KI2EpIA0KQmFzZWQgb24geW91ciByZXN1bHRzLCB3cml0ZSB0aGUgZXF1YXRpb24gdGhhdCB5b3UgY29uc2lkZXIgc2hvdWxkIGJlIHVzZWQgdG8gcHJlZGljdCBwbGF5ZXJzJyBzYWxhcmllcy4NCg0KUHJlZGljdGVkIFNhbGFyeSA9IDExOC4xNDcyICsgNC4zMzUwICogSGl0cyAtIDE0MC43NDY4ICogRGl2aXNpb24NCg0KI2IpIA0KVXNlIHRoaXMgZXF1YXRpb24gdG8gcHJlZGljdCB0aGUgU2FsYXJ5IGZvciBhIHBsYXllciB3aG8gY29ubmVjdGVkIDEyMCBoaXRzIGFuZCBwbGF5ZWQgaW4gdGhlIE5hdGlvbmFsIExlYWd1ZSBpbiB0aGUgV2VzdCBkaXZpc2lvbiBpbiAxOTg2LiBTaG93IHlvdXIgd29yay4NCg0KYGBge3J9DQojbGV2ZWxzKEhpdHRlcnNfRml4ZWQkRGl2aXNpb24pDQpjb250cmFzdHMoSGl0dGVyc19GaXhlZCREaXZpc2lvbikNCmBgYA0KYGBge3J9DQpwcmVkX1NhbGFyeV8yYiA9IDExOC4xNDcyICsgNC4zMzUwICogMTIwIC0gMTQwLjc0NjggKiAxDQpwcmVkX1NhbGFyeV8yYg0KYGBgDQoNClRoZSBwcmVkaWN0ZWQgc2FsYXJ5IGlzICQ0OTcuNjAgZm9yIGEgcGxheWVyIGluIHRoZSBXZXN0IERpdmlzaW9uIHdpdGggMTIwIGhpdHMuDQoNCiNjKSANCkRvZXMgdGhpcyBlcXVhdGlvbiBpbXByb3ZlIHdoZW4geW91IGFkZCB0aGUgIOKAnE5ld0xlYWd1ZeKAnSBwcmVkaWN0b3I/IEp1c3RpZnkgeW91ciBhbnN3ZXIgYW5kIHNob3cgYWxsIHlvdXIgd29yay4gDQoNCmBgYHtyfQ0KbW9kZWxfMmNfSEYgPSBsbShTYWxhcnl+SGl0cyArIExlYWd1ZSArIERpdmlzaW9uK05ld0xlYWd1ZSAsIGRhdGEgPSBIaXR0ZXJzX0ZpeGVkKQ0Kc3VtbWFyeShtb2RlbF8yY19IRikNCmBgYA0KDQpUaGUgcCB2YWx1ZSAuODA3NDkgZm9yIE5ld0xlYWd1ZSBpcyBncmVhdGVyIHRoYW4gYWxwaGEsIHRoZXJlZm9yZSwgdGhlcmUgaXMgTk8gc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlLiANCkluIHRoaXMgY2FzZSwgd2UgY2FuIGRldGVybWluZSBOZXdMZWFndWUgcHJlZGljdG9yIGRvZXMgbm90IGltcHJvdmUgdGhlIGVxdWF0aW9uLg0KDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJ5DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgSGlzdG9ncmFtIG9mIFNhbGFyeQ0KZ2dwbG90KEhpdHRlcnNfRml4ZWQsIGFlcyh4ID0gU2FsYXJ5KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUwMCwgZmlsbCA9ICJibHVlIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBTYWxhcmllcyIpICsNCiAgeGxhYigiU2FsYXJ5IikgKw0KICB5bGFiKCJGcmVxdWVuY3kiKQ0KDQpgYGANCg0KDQpgYGB7cn0NCiMgU2NhdHRlcnBsb3Qgb2YgU2FsYXJ5IHZzIEhpdHMNCmdncGxvdChIaXR0ZXJzX0ZpeGVkLCBhZXMoeCA9IEhpdHMsIHkgPSBTYWxhcnkpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gRGl2aXNpb24pLCBhbHBoYSA9IDAuNykgKyAgIyBDb2xvciBwb2ludHMgYnkgRGl2aXNpb24gdG8gYWRkIGFub3RoZXIgZGltZW5zaW9uIG9mIGFuYWx5c2lzDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gInJlZCIpICsNCiAgZ2d0aXRsZSgiUmVsYXRpb25zaGlwIGJldHdlZW4gSGl0cyBhbmQgU2FsYXJ5IikgKw0KICB4bGFiKCJIaXRzIikgKw0KICB5bGFiKCJTYWxhcnkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KDQo=