Overview

In this notebook, I use first base hitting statistics to practice linear regression in R.
The goal is to see how different baseball performance variables relate to 2023 payroll salary.

The response variable is:

  • Payroll.Salary2023

Some possible explanatory variables are:

  • RBI
  • AVG
  • HR
  • OBP
  • OPS

Question 1: Read and explore the data

Answer

First, I read in the data file and check the structure of the dataset.

# Load the first base statistics data
firstbase <- read.csv("firstbasestats.csv")

# View the structure of the dataset
str(firstbase)
'data.frame':   23 obs. of  15 variables:
 $ Player            : chr  "Freddie Freeman" "Jose Abreu" "Nate Lowe" "Paul Goldschmidt" ...
 $ Pos               : chr  "1B" "1B" "1B" "1B" ...
 $ Team              : chr  "LAD" "CHW" "TEX" "STL" ...
 $ GP                : int  159 157 157 151 160 140 160 145 146 143 ...
 $ AB                : int  612 601 593 561 638 551 583 555 545 519 ...
 $ H                 : int  199 183 179 178 175 152 141 139 132 124 ...
 $ X2B               : int  47 40 26 41 35 27 25 28 40 23 ...
 $ HR                : int  21 15 27 35 32 20 36 22 8 18 ...
 $ RBI               : int  100 75 76 115 97 84 94 85 53 63 ...
 $ AVG               : num  0.325 0.305 0.302 0.317 0.274 0.276 0.242 0.251 0.242 0.239 ...
 $ OBP               : num  0.407 0.379 0.358 0.404 0.339 0.34 0.327 0.305 0.288 0.319 ...
 $ SLG               : num  0.511 0.446 0.492 0.578 0.48 0.437 0.477 0.423 0.36 0.391 ...
 $ OPS               : num  0.918 0.824 0.851 0.981 0.818 0.777 0.804 0.729 0.647 0.71 ...
 $ WAR               : num  5.77 4.19 3.21 7.86 3.85 3.07 5.05 1.32 -0.33 1.87 ...
 $ Payroll.Salary2023: num  27000000 19500000 4050000 26000000 14500000 ...

The dataset contains player information, team information, hitting statistics, and salary data.
The salary column, Payroll.Salary2023, will be used as the response variable in the regression models.

# Summary statistics for the dataset
summary(firstbase)
       Player          Pos            Team          GP              AB              H        
 Length   :23   Length   :23   Length   :23   Min.   :  5.0   Min.   : 14.0   Min.   :  3.0  
 N.unique :23   N.unique : 1   N.unique :18   1st Qu.:105.5   1st Qu.:309.0   1st Qu.: 74.5  
 N.blank  : 0   N.blank  : 0   N.blank  : 0   Median :131.0   Median :465.0   Median :115.0  
 Min.nchar: 9   Min.nchar: 2   Min.nchar: 2   Mean   :120.2   Mean   :426.9   Mean   :110.0  
 Max.nchar:21   Max.nchar: 2   Max.nchar: 3   3rd Qu.:152.0   3rd Qu.:558.0   3rd Qu.:146.5  
                                              Max.   :160.0   Max.   :638.0   Max.   :199.0  
      X2B              HR             RBI              AVG              OBP              SLG        
 Min.   : 1.00   Min.   : 0.00   Min.   :  1.00   Min.   :0.2020   Min.   :0.2140   Min.   :0.2860  
 1st Qu.:13.50   1st Qu.: 8.00   1st Qu.: 27.00   1st Qu.:0.2180   1st Qu.:0.3030   1st Qu.:0.3505  
 Median :23.00   Median :18.00   Median : 63.00   Median :0.2420   Median :0.3210   Median :0.4230  
 Mean   :22.39   Mean   :17.09   Mean   : 59.43   Mean   :0.2499   Mean   :0.3242   Mean   :0.4106  
 3rd Qu.:28.00   3rd Qu.:24.50   3rd Qu.: 84.50   3rd Qu.:0.2750   3rd Qu.:0.3395   3rd Qu.:0.4690  
 Max.   :47.00   Max.   :36.00   Max.   :115.00   Max.   :0.3250   Max.   :0.4070   Max.   :0.5780  
      OPS              WAR         Payroll.Salary2023
 Min.   :0.5000   Min.   :-1.470   Min.   :  720000  
 1st Qu.:0.6445   1st Qu.: 0.190   1st Qu.:  739200  
 Median :0.7290   Median : 1.310   Median : 4050000  
 Mean   :0.7346   Mean   : 1.788   Mean   : 6972744  
 3rd Qu.:0.8175   3rd Qu.: 3.140   3rd Qu.: 8150000  
 Max.   :0.9810   Max.   : 7.860   Max.   :27000000  

The summary output gives a quick view of the minimum, median, mean, and maximum values for the numeric variables. This is useful before fitting regression models because it helps identify the range of each variable.


Question 2: Build a linear regression model with one variable

Answer

For the first model, I use RBI as the only predictor of Payroll.Salary2023.

# Simple linear regression model using RBI
model1 <- lm(Payroll.Salary2023 ~ RBI, data = firstbase)

# View model results
summary(model1)

Call:
lm(formula = Payroll.Salary2023 ~ RBI, data = firstbase)

Residuals:
      Min        1Q    Median        3Q       Max 
-10250331  -5220790   -843455   2386848  13654950 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)   
(Intercept) -2363744    2866320  -0.825  0.41883   
RBI           157088      42465   3.699  0.00133 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6516000 on 21 degrees of freedom
Multiple R-squared:  0.3945,    Adjusted R-squared:  0.3657 
F-statistic: 13.68 on 1 and 21 DF,  p-value: 0.001331

The regression equation has the form:

\[ \widehat{Salary} = b_0 + b_1(RBI) \]

From this model, RBI has a positive coefficient. This means that players with more RBI tend to have higher predicted salaries.

The p-value for RBI is small, so RBI appears to be a statistically significant predictor in this simple model.

Sum of Squared Errors for Model 1

# Sum of squared errors for model 1
SSE_model1 <- sum(model1$residuals^2)

SSE_model1
[1] 8.914926e+14

The Sum of Squared Errors measures how far the predicted values are from the actual salary values. A smaller SSE means the model fits the data better.


Question 3: Build a regression model with two variables

Answer

Next, I use both AVG and RBI to predict salary.

# Multiple linear regression model using AVG and RBI
model2 <- lm(Payroll.Salary2023 ~ AVG + RBI, data = firstbase)

# View model results
summary(model2)

Call:
lm(formula = Payroll.Salary2023 ~ AVG + RBI, data = firstbase)

Residuals:
     Min       1Q   Median       3Q      Max 
-9097952 -4621582   -33233  3016541 10260245 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)  
(Intercept) -18083756    9479036  -1.908   0.0709 .
AVG          74374031   42934155   1.732   0.0986 .
RBI            108850      49212   2.212   0.0388 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6226000 on 20 degrees of freedom
Multiple R-squared:  0.4735,    Adjusted R-squared:  0.4209 
F-statistic: 8.994 on 2 and 20 DF,  p-value: 0.001636

This model uses two explanatory variables instead of one.
The equation has the form:

\[ \widehat{Salary} = b_0 + b_1(AVG) + b_2(RBI) \]

In this model, RBI remains an important variable. AVG also has a positive coefficient, which suggests that higher batting average is associated with higher salary, although its significance should be judged using the p-value.

Sum of Squared Errors for Model 2

# Sum of squared errors for model 2
SSE_model2 <- sum(model2$residuals^2)

SSE_model2
[1] 7.751841e+14

Compared with Model 1, this model has a lower SSE. This means adding AVG improved the model fit.


Question 4: Build a model with several hitting variables

Answer

Now I include several offensive statistics in the regression model.

# Multiple linear regression model using several hitting statistics
model3 <- lm(Payroll.Salary2023 ~ HR + RBI + AVG + OBP + OPS, data = firstbase)

# View model results
summary(model3)

Call:
lm(formula = Payroll.Salary2023 ~ HR + RBI + AVG + OBP + OPS, 
    data = firstbase)

Residuals:
     Min       1Q   Median       3Q      Max 
-9611440 -3338119    64016  4472451  9490309 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)  
(Intercept) -31107858   11738494  -2.650   0.0168 *
HR            -341069     552069  -0.618   0.5449  
RBI            115786     113932   1.016   0.3237  
AVG         -63824769  104544645  -0.611   0.5496  
OBP          27054948  131210166   0.206   0.8391  
OPS          60181012   95415131   0.631   0.5366  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6023000 on 17 degrees of freedom
Multiple R-squared:  0.5811,    Adjusted R-squared:  0.4579 
F-statistic: 4.717 on 5 and 17 DF,  p-value: 0.006951

This model includes home runs, RBI, batting average, on-base percentage, and OPS.
The purpose is to see whether using more hitting statistics improves the prediction of salary.

Sum of Squared Errors for Model 3

# Sum of squared errors for model 3
SSE_model3 <- sum(model3$residuals^2)

SSE_model3
[1] 6.167793e+14

Model 3 has a lower SSE than the earlier models, so it fits the training data better.
However, some variables may not be statistically significant once they are included together. This can happen when the predictors are correlated with each other.


Question 5: Remove HR and compare the model

Answer

Since HR is not very significant in the larger model, I remove it and fit another model.

# Regression model after removing HR
model4 <- lm(Payroll.Salary2023 ~ RBI + AVG + OBP + OPS, data = firstbase)

# View model results
summary(model4)

Call:
lm(formula = Payroll.Salary2023 ~ RBI + AVG + OBP + OPS, data = firstbase)

Residuals:
     Min       1Q   Median       3Q      Max 
-9399551 -3573842    98921  3979339  9263512 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)  
(Intercept) -29466887   11235931  -2.623   0.0173 *
RBI             71495      87015   0.822   0.4220  
AVG         -11035457   59192453  -0.186   0.8542  
OBP          86360720   87899074   0.982   0.3389  
OPS           9464546   47788458   0.198   0.8452  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5919000 on 18 degrees of freedom
Multiple R-squared:  0.5717,    Adjusted R-squared:  0.4765 
F-statistic: 6.007 on 4 and 18 DF,  p-value: 0.00298

This model checks whether removing HR makes the model simpler without losing too much explanatory power.

# Sum of squared errors for model 4
SSE_model4 <- sum(model4$residuals^2)

SSE_model4
[1] 6.30627e+14

The adjusted R-squared and residual standard error can be used to compare this model to Model 3. A simpler model is often better if it performs almost as well.


Question 6: Check correlations

Answer

Before choosing a final model, I check correlations between variables.

# Correlation between RBI and salary
cor(firstbase$RBI, firstbase$Payroll.Salary2023)
[1] 0.6281239
# Correlation between AVG and OBP
cor(firstbase$AVG, firstbase$OBP)
[1] 0.8028894

The correlation between RBI and salary is positive, which supports the earlier regression result.
The correlation between AVG and OBP is also strong, meaning these variables may overlap in the information they provide.

To see all numeric correlations, I first remove the character columns.

# Create a numeric-only version of the dataset
firstbase_numeric <- firstbase[, -(1:3)]

# Correlation matrix
cor(firstbase_numeric)
                          GP        AB         H       X2B        HR       RBI       AVG       OBP
GP                 1.0000000 0.9779421 0.9056508 0.8446267 0.7432552 0.8813917 0.4430808 0.4841583
AB                 0.9779421 1.0000000 0.9516701 0.8924632 0.7721339 0.9125839 0.5126292 0.5026125
H                  0.9056508 0.9516701 1.0000000 0.9308318 0.7155225 0.9068893 0.7393167 0.6560021
X2B                0.8446267 0.8924632 0.9308318 1.0000000 0.5889699 0.8485911 0.6613085 0.5466537
HR                 0.7432552 0.7721339 0.7155225 0.5889699 1.0000000 0.8929048 0.3444242 0.4603408
RBI                0.8813917 0.9125839 0.9068893 0.8485911 0.8929048 1.0000000 0.5658479 0.5704463
AVG                0.4430808 0.5126292 0.7393167 0.6613085 0.3444242 0.5658479 1.0000000 0.8028894
OBP                0.4841583 0.5026125 0.6560021 0.5466537 0.4603408 0.5704463 0.8028894 1.0000000
SLG                0.6875270 0.7471949 0.8211406 0.7211259 0.8681501 0.8824090 0.7254274 0.7617499
OPS                0.6504483 0.6980141 0.8069779 0.6966830 0.7638721 0.8156612 0.7989005 0.8987390
WAR                0.5645243 0.6211558 0.7688712 0.6757470 0.6897677 0.7885666 0.7855945 0.7766375
Payroll.Salary2023 0.4614889 0.5018820 0.6249911 0.6450730 0.5317619 0.6281239 0.5871543 0.7025979
                         SLG       OPS       WAR Payroll.Salary2023
GP                 0.6875270 0.6504483 0.5645243          0.4614889
AB                 0.7471949 0.6980141 0.6211558          0.5018820
H                  0.8211406 0.8069779 0.7688712          0.6249911
X2B                0.7211259 0.6966830 0.6757470          0.6450730
HR                 0.8681501 0.7638721 0.6897677          0.5317619
RBI                0.8824090 0.8156612 0.7885666          0.6281239
AVG                0.7254274 0.7989005 0.7855945          0.5871543
OBP                0.7617499 0.8987390 0.7766375          0.7025979
SLG                1.0000000 0.9686752 0.8611140          0.6974086
OPS                0.9686752 1.0000000 0.8799893          0.7394981
WAR                0.8611140 0.8799893 1.0000000          0.8086359
Payroll.Salary2023 0.6974086 0.7394981 0.8086359          1.0000000

From the correlation matrix, WAR, OPS, and OBP appear to have strong positive relationships with salary.
There are also strong correlations among several hitting variables, so I should avoid making the final model too complicated.


Question 7: Remove AVG and compare the model

Answer

Because AVG and OBP are highly correlated, I remove AVG and keep RBI, OBP, and OPS.

# Regression model after removing AVG
model5 <- lm(Payroll.Salary2023 ~ RBI + OBP + OPS, data = firstbase)

# View model results
summary(model5)

Call:
lm(formula = Payroll.Salary2023 ~ RBI + OBP + OPS, data = firstbase)

Residuals:
     Min       1Q   Median       3Q      Max 
-9465449 -3411234   259746  4102864  8876798 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)  
(Intercept) -29737007   10855411  -2.739    0.013 *
RBI             72393      84646   0.855    0.403  
OBP          82751360   83534224   0.991    0.334  
OPS           7598051   45525575   0.167    0.869  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5767000 on 19 degrees of freedom
Multiple R-squared:  0.5709,    Adjusted R-squared:  0.5031 
F-statistic: 8.426 on 3 and 19 DF,  p-value: 0.000913

This model is simpler than the previous model and avoids using both AVG and OBP together.

# Sum of squared errors for model 5
SSE_model5 <- sum(model5$residuals^2)

SSE_model5
[1] 6.318447e+14

The model still explains a useful amount of variation in salary, but OPS is not very significant in this version.


Question 8: Final model selection

Answer

Since OPS is not very significant in the previous model, I remove OPS and keep RBI and OBP.

# Final selected model using RBI and OBP
model6 <- lm(Payroll.Salary2023 ~ RBI + OBP, data = firstbase)

# View model results
summary(model6)

Call:
lm(formula = Payroll.Salary2023 ~ RBI + OBP, data = firstbase)

Residuals:
     Min       1Q   Median       3Q      Max 
-9045497 -3487008   139497  4084739  9190185 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)   
(Intercept) -28984802    9632560  -3.009  0.00693 **
RBI             84278      44634   1.888  0.07360 . 
OBP          95468873   33385182   2.860  0.00969 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5625000 on 20 degrees of freedom
Multiple R-squared:  0.5703,    Adjusted R-squared:  0.5273 
F-statistic: 13.27 on 2 and 20 DF,  p-value: 0.0002149

This model is my final model because it is simpler and still has good explanatory power.

The final model uses:

  • RBI
  • OBP

The model equation is:

\[ \widehat{Salary} = b_0 + b_1(RBI) + b_2(OBP) \]

Based on the model output, OBP is statistically significant. RBI is also useful in the model, although its p-value is higher than OBP’s.


Question 9: Make predictions using the test data

Answer

Now I read in the test data and use the final model to predict salary.

# Read in the test data
firstbaseTest <- read.csv("firstbasestats_test.csv")

# View the structure of the test data
str(firstbaseTest)
'data.frame':   2 obs. of  15 variables:
 $ Player            : chr  "Matt Olson" "Josh Bell"
 $ Pos               : chr  "1B" "1B"
 $ Team              : chr  "ATL" "SD"
 $ GP                : int  162 156
 $ AB                : int  616 552
 $ H                 : int  148 147
 $ X2B               : int  44 29
 $ HR                : int  34 17
 $ RBI               : int  103 71
 $ AVG               : num  0.24 0.266
 $ OBP               : num  0.325 0.362
 $ SLG               : num  0.477 0.422
 $ OPS               : num  0.802 0.784
 $ WAR               : num  3.29 3.5
 $ Payroll.Salary2023: num  21000000 16500000

The test data contains two first basemen. I use the final model, model6, to predict their 2023 salaries.

# Make predictions on the test set
predictTest <- predict(model6, newdata = firstbaseTest)

predictTest
       1        2 
10723186 11558647 

The predicted salaries from the model are approximately:

  • Matt Olson: about $10.72 million
  • Josh Bell: about $11.56 million

These predictions are based only on RBI and OBP.


Question 10: Compute test-set R-squared

Answer

To evaluate the model on the test data, I compute the Sum of Squared Errors and compare it with the total variation in the test salaries.

# Sum of squared errors on the test set
SSE_test <- sum((firstbaseTest$Payroll.Salary2023 - predictTest)^2)

# Total sum of squares using the training salary mean
SST_test <- sum((firstbaseTest$Payroll.Salary2023 - mean(firstbase$Payroll.Salary2023))^2)

# Test-set R-squared
R_squared_test <- 1 - SSE_test / SST_test

R_squared_test
[1] 0.5477734

The test-set R-squared is about 0.548.
This means the model explains about 54.8% of the variation in the test salary values.


Final Conclusion

In this activity, I practiced building and comparing linear regression models in R.

The best final model I selected was:

model6 <- lm(Payroll.Salary2023 ~ RBI + OBP, data = firstbase)
summary(model6)

Call:
lm(formula = Payroll.Salary2023 ~ RBI + OBP, data = firstbase)

Residuals:
     Min       1Q   Median       3Q      Max 
-9045497 -3487008   139497  4084739  9190185 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)   
(Intercept) -28984802    9632560  -3.009  0.00693 **
RBI             84278      44634   1.888  0.07360 . 
OBP          95468873   33385182   2.860  0.00969 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5625000 on 20 degrees of freedom
Multiple R-squared:  0.5703,    Adjusted R-squared:  0.5273 
F-statistic: 13.27 on 2 and 20 DF,  p-value: 0.0002149

I chose this model because it is simpler than the larger models and still gives a reasonable fit.
The model suggests that RBI and OBP are useful predictors of salary for first basemen in this dataset.

The prediction results show that the model can be applied to new players, but the predictions are not perfect. Salary depends on many other factors besides hitting statistics, such as age, contract history, team budget, defense, market value, and previous seasons.

LS0tCnRpdGxlOiAiSW50cm8gdG8gTGluZWFyIFJlZ3Jlc3Npb246IEZpcnN0IEJhc2UgSGl0dGluZyBTdGF0cyIKYXV0aG9yOiAiSnVsaW8gSGVybmFuZGV6IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyBPdmVydmlldwoKSW4gdGhpcyBub3RlYm9vaywgSSB1c2UgZmlyc3QgYmFzZSBoaXR0aW5nIHN0YXRpc3RpY3MgdG8gcHJhY3RpY2UgbGluZWFyIHJlZ3Jlc3Npb24gaW4gUi4gIApUaGUgZ29hbCBpcyB0byBzZWUgaG93IGRpZmZlcmVudCBiYXNlYmFsbCBwZXJmb3JtYW5jZSB2YXJpYWJsZXMgcmVsYXRlIHRvIDIwMjMgcGF5cm9sbCBzYWxhcnkuCgpUaGUgcmVzcG9uc2UgdmFyaWFibGUgaXM6CgotIGBQYXlyb2xsLlNhbGFyeTIwMjNgCgpTb21lIHBvc3NpYmxlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBhcmU6CgotIGBSQklgCi0gYEFWR2AKLSBgSFJgCi0gYE9CUGAKLSBgT1BTYAoKLS0tCgojIFF1ZXN0aW9uIDE6IFJlYWQgYW5kIGV4cGxvcmUgdGhlIGRhdGEKCiMjIEFuc3dlcgoKRmlyc3QsIEkgcmVhZCBpbiB0aGUgZGF0YSBmaWxlIGFuZCBjaGVjayB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0LgoKYGBge3J9CiMgTG9hZCB0aGUgZmlyc3QgYmFzZSBzdGF0aXN0aWNzIGRhdGEKZmlyc3RiYXNlIDwtIHJlYWQuY3N2KCJmaXJzdGJhc2VzdGF0cy5jc3YiKQoKIyBWaWV3IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQKc3RyKGZpcnN0YmFzZSkKYGBgCgpUaGUgZGF0YXNldCBjb250YWlucyBwbGF5ZXIgaW5mb3JtYXRpb24sIHRlYW0gaW5mb3JtYXRpb24sIGhpdHRpbmcgc3RhdGlzdGljcywgYW5kIHNhbGFyeSBkYXRhLiAgClRoZSBzYWxhcnkgY29sdW1uLCBgUGF5cm9sbC5TYWxhcnkyMDIzYCwgd2lsbCBiZSB1c2VkIGFzIHRoZSByZXNwb25zZSB2YXJpYWJsZSBpbiB0aGUgcmVncmVzc2lvbiBtb2RlbHMuCgpgYGB7cn0KIyBTdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHRoZSBkYXRhc2V0CnN1bW1hcnkoZmlyc3RiYXNlKQpgYGAKClRoZSBzdW1tYXJ5IG91dHB1dCBnaXZlcyBhIHF1aWNrIHZpZXcgb2YgdGhlIG1pbmltdW0sIG1lZGlhbiwgbWVhbiwgYW5kIG1heGltdW0gdmFsdWVzIGZvciB0aGUgbnVtZXJpYyB2YXJpYWJsZXMuIFRoaXMgaXMgdXNlZnVsIGJlZm9yZSBmaXR0aW5nIHJlZ3Jlc3Npb24gbW9kZWxzIGJlY2F1c2UgaXQgaGVscHMgaWRlbnRpZnkgdGhlIHJhbmdlIG9mIGVhY2ggdmFyaWFibGUuCgotLS0KCiMgUXVlc3Rpb24gMjogQnVpbGQgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3aXRoIG9uZSB2YXJpYWJsZQoKIyMgQW5zd2VyCgpGb3IgdGhlIGZpcnN0IG1vZGVsLCBJIHVzZSBgUkJJYCBhcyB0aGUgb25seSBwcmVkaWN0b3Igb2YgYFBheXJvbGwuU2FsYXJ5MjAyM2AuCgpgYGB7cn0KIyBTaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgUkJJCm1vZGVsMSA8LSBsbShQYXlyb2xsLlNhbGFyeTIwMjMgfiBSQkksIGRhdGEgPSBmaXJzdGJhc2UpCgojIFZpZXcgbW9kZWwgcmVzdWx0cwpzdW1tYXJ5KG1vZGVsMSkKYGBgCgpUaGUgcmVncmVzc2lvbiBlcXVhdGlvbiBoYXMgdGhlIGZvcm06CgpcWwpcd2lkZWhhdHtTYWxhcnl9ID0gYl8wICsgYl8xKFJCSSkKXF0KCkZyb20gdGhpcyBtb2RlbCwgUkJJIGhhcyBhIHBvc2l0aXZlIGNvZWZmaWNpZW50LiBUaGlzIG1lYW5zIHRoYXQgcGxheWVycyB3aXRoIG1vcmUgUkJJIHRlbmQgdG8gaGF2ZSBoaWdoZXIgcHJlZGljdGVkIHNhbGFyaWVzLgoKVGhlIHAtdmFsdWUgZm9yIGBSQklgIGlzIHNtYWxsLCBzbyBSQkkgYXBwZWFycyB0byBiZSBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgcHJlZGljdG9yIGluIHRoaXMgc2ltcGxlIG1vZGVsLgoKIyMgU3VtIG9mIFNxdWFyZWQgRXJyb3JzIGZvciBNb2RlbCAxCgpgYGB7cn0KIyBTdW0gb2Ygc3F1YXJlZCBlcnJvcnMgZm9yIG1vZGVsIDEKU1NFX21vZGVsMSA8LSBzdW0obW9kZWwxJHJlc2lkdWFsc14yKQoKU1NFX21vZGVsMQpgYGAKClRoZSBTdW0gb2YgU3F1YXJlZCBFcnJvcnMgbWVhc3VyZXMgaG93IGZhciB0aGUgcHJlZGljdGVkIHZhbHVlcyBhcmUgZnJvbSB0aGUgYWN0dWFsIHNhbGFyeSB2YWx1ZXMuIEEgc21hbGxlciBTU0UgbWVhbnMgdGhlIG1vZGVsIGZpdHMgdGhlIGRhdGEgYmV0dGVyLgoKLS0tCgojIFF1ZXN0aW9uIDM6IEJ1aWxkIGEgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHR3byB2YXJpYWJsZXMKCiMjIEFuc3dlcgoKTmV4dCwgSSB1c2UgYm90aCBgQVZHYCBhbmQgYFJCSWAgdG8gcHJlZGljdCBzYWxhcnkuCgpgYGB7cn0KIyBNdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBBVkcgYW5kIFJCSQptb2RlbDIgPC0gbG0oUGF5cm9sbC5TYWxhcnkyMDIzIH4gQVZHICsgUkJJLCBkYXRhID0gZmlyc3RiYXNlKQoKIyBWaWV3IG1vZGVsIHJlc3VsdHMKc3VtbWFyeShtb2RlbDIpCmBgYAoKVGhpcyBtb2RlbCB1c2VzIHR3byBleHBsYW5hdG9yeSB2YXJpYWJsZXMgaW5zdGVhZCBvZiBvbmUuICAKVGhlIGVxdWF0aW9uIGhhcyB0aGUgZm9ybToKClxbClx3aWRlaGF0e1NhbGFyeX0gPSBiXzAgKyBiXzEoQVZHKSArIGJfMihSQkkpClxdCgpJbiB0aGlzIG1vZGVsLCBSQkkgcmVtYWlucyBhbiBpbXBvcnRhbnQgdmFyaWFibGUuIEFWRyBhbHNvIGhhcyBhIHBvc2l0aXZlIGNvZWZmaWNpZW50LCB3aGljaCBzdWdnZXN0cyB0aGF0IGhpZ2hlciBiYXR0aW5nIGF2ZXJhZ2UgaXMgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciBzYWxhcnksIGFsdGhvdWdoIGl0cyBzaWduaWZpY2FuY2Ugc2hvdWxkIGJlIGp1ZGdlZCB1c2luZyB0aGUgcC12YWx1ZS4KCiMjIFN1bSBvZiBTcXVhcmVkIEVycm9ycyBmb3IgTW9kZWwgMgoKYGBge3J9CiMgU3VtIG9mIHNxdWFyZWQgZXJyb3JzIGZvciBtb2RlbCAyClNTRV9tb2RlbDIgPC0gc3VtKG1vZGVsMiRyZXNpZHVhbHNeMikKClNTRV9tb2RlbDIKYGBgCgpDb21wYXJlZCB3aXRoIE1vZGVsIDEsIHRoaXMgbW9kZWwgaGFzIGEgbG93ZXIgU1NFLiBUaGlzIG1lYW5zIGFkZGluZyBBVkcgaW1wcm92ZWQgdGhlIG1vZGVsIGZpdC4KCi0tLQoKIyBRdWVzdGlvbiA0OiBCdWlsZCBhIG1vZGVsIHdpdGggc2V2ZXJhbCBoaXR0aW5nIHZhcmlhYmxlcwoKIyMgQW5zd2VyCgpOb3cgSSBpbmNsdWRlIHNldmVyYWwgb2ZmZW5zaXZlIHN0YXRpc3RpY3MgaW4gdGhlIHJlZ3Jlc3Npb24gbW9kZWwuCgpgYGB7cn0KIyBNdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBzZXZlcmFsIGhpdHRpbmcgc3RhdGlzdGljcwptb2RlbDMgPC0gbG0oUGF5cm9sbC5TYWxhcnkyMDIzIH4gSFIgKyBSQkkgKyBBVkcgKyBPQlAgKyBPUFMsIGRhdGEgPSBmaXJzdGJhc2UpCgojIFZpZXcgbW9kZWwgcmVzdWx0cwpzdW1tYXJ5KG1vZGVsMykKYGBgCgpUaGlzIG1vZGVsIGluY2x1ZGVzIGhvbWUgcnVucywgUkJJLCBiYXR0aW5nIGF2ZXJhZ2UsIG9uLWJhc2UgcGVyY2VudGFnZSwgYW5kIE9QUy4gIApUaGUgcHVycG9zZSBpcyB0byBzZWUgd2hldGhlciB1c2luZyBtb3JlIGhpdHRpbmcgc3RhdGlzdGljcyBpbXByb3ZlcyB0aGUgcHJlZGljdGlvbiBvZiBzYWxhcnkuCgojIyBTdW0gb2YgU3F1YXJlZCBFcnJvcnMgZm9yIE1vZGVsIDMKCmBgYHtyfQojIFN1bSBvZiBzcXVhcmVkIGVycm9ycyBmb3IgbW9kZWwgMwpTU0VfbW9kZWwzIDwtIHN1bShtb2RlbDMkcmVzaWR1YWxzXjIpCgpTU0VfbW9kZWwzCmBgYAoKTW9kZWwgMyBoYXMgYSBsb3dlciBTU0UgdGhhbiB0aGUgZWFybGllciBtb2RlbHMsIHNvIGl0IGZpdHMgdGhlIHRyYWluaW5nIGRhdGEgYmV0dGVyLiAgCkhvd2V2ZXIsIHNvbWUgdmFyaWFibGVzIG1heSBub3QgYmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBvbmNlIHRoZXkgYXJlIGluY2x1ZGVkIHRvZ2V0aGVyLiBUaGlzIGNhbiBoYXBwZW4gd2hlbiB0aGUgcHJlZGljdG9ycyBhcmUgY29ycmVsYXRlZCB3aXRoIGVhY2ggb3RoZXIuCgotLS0KCiMgUXVlc3Rpb24gNTogUmVtb3ZlIEhSIGFuZCBjb21wYXJlIHRoZSBtb2RlbAoKIyMgQW5zd2VyCgpTaW5jZSBIUiBpcyBub3QgdmVyeSBzaWduaWZpY2FudCBpbiB0aGUgbGFyZ2VyIG1vZGVsLCBJIHJlbW92ZSBpdCBhbmQgZml0IGFub3RoZXIgbW9kZWwuCgpgYGB7cn0KIyBSZWdyZXNzaW9uIG1vZGVsIGFmdGVyIHJlbW92aW5nIEhSCm1vZGVsNCA8LSBsbShQYXlyb2xsLlNhbGFyeTIwMjMgfiBSQkkgKyBBVkcgKyBPQlAgKyBPUFMsIGRhdGEgPSBmaXJzdGJhc2UpCgojIFZpZXcgbW9kZWwgcmVzdWx0cwpzdW1tYXJ5KG1vZGVsNCkKYGBgCgpUaGlzIG1vZGVsIGNoZWNrcyB3aGV0aGVyIHJlbW92aW5nIEhSIG1ha2VzIHRoZSBtb2RlbCBzaW1wbGVyIHdpdGhvdXQgbG9zaW5nIHRvbyBtdWNoIGV4cGxhbmF0b3J5IHBvd2VyLgoKYGBge3J9CiMgU3VtIG9mIHNxdWFyZWQgZXJyb3JzIGZvciBtb2RlbCA0ClNTRV9tb2RlbDQgPC0gc3VtKG1vZGVsNCRyZXNpZHVhbHNeMikKClNTRV9tb2RlbDQKYGBgCgpUaGUgYWRqdXN0ZWQgUi1zcXVhcmVkIGFuZCByZXNpZHVhbCBzdGFuZGFyZCBlcnJvciBjYW4gYmUgdXNlZCB0byBjb21wYXJlIHRoaXMgbW9kZWwgdG8gTW9kZWwgMy4gQSBzaW1wbGVyIG1vZGVsIGlzIG9mdGVuIGJldHRlciBpZiBpdCBwZXJmb3JtcyBhbG1vc3QgYXMgd2VsbC4KCi0tLQoKIyBRdWVzdGlvbiA2OiBDaGVjayBjb3JyZWxhdGlvbnMKCiMjIEFuc3dlcgoKQmVmb3JlIGNob29zaW5nIGEgZmluYWwgbW9kZWwsIEkgY2hlY2sgY29ycmVsYXRpb25zIGJldHdlZW4gdmFyaWFibGVzLgoKYGBge3J9CiMgQ29ycmVsYXRpb24gYmV0d2VlbiBSQkkgYW5kIHNhbGFyeQpjb3IoZmlyc3RiYXNlJFJCSSwgZmlyc3RiYXNlJFBheXJvbGwuU2FsYXJ5MjAyMykKCiMgQ29ycmVsYXRpb24gYmV0d2VlbiBBVkcgYW5kIE9CUApjb3IoZmlyc3RiYXNlJEFWRywgZmlyc3RiYXNlJE9CUCkKYGBgCgpUaGUgY29ycmVsYXRpb24gYmV0d2VlbiBSQkkgYW5kIHNhbGFyeSBpcyBwb3NpdGl2ZSwgd2hpY2ggc3VwcG9ydHMgdGhlIGVhcmxpZXIgcmVncmVzc2lvbiByZXN1bHQuICAKVGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gQVZHIGFuZCBPQlAgaXMgYWxzbyBzdHJvbmcsIG1lYW5pbmcgdGhlc2UgdmFyaWFibGVzIG1heSBvdmVybGFwIGluIHRoZSBpbmZvcm1hdGlvbiB0aGV5IHByb3ZpZGUuCgpUbyBzZWUgYWxsIG51bWVyaWMgY29ycmVsYXRpb25zLCBJIGZpcnN0IHJlbW92ZSB0aGUgY2hhcmFjdGVyIGNvbHVtbnMuCgpgYGB7cn0KIyBDcmVhdGUgYSBudW1lcmljLW9ubHkgdmVyc2lvbiBvZiB0aGUgZGF0YXNldApmaXJzdGJhc2VfbnVtZXJpYyA8LSBmaXJzdGJhc2VbLCAtKDE6MyldCgojIENvcnJlbGF0aW9uIG1hdHJpeApjb3IoZmlyc3RiYXNlX251bWVyaWMpCmBgYAoKRnJvbSB0aGUgY29ycmVsYXRpb24gbWF0cml4LCBgV0FSYCwgYE9QU2AsIGFuZCBgT0JQYCBhcHBlYXIgdG8gaGF2ZSBzdHJvbmcgcG9zaXRpdmUgcmVsYXRpb25zaGlwcyB3aXRoIHNhbGFyeS4gIApUaGVyZSBhcmUgYWxzbyBzdHJvbmcgY29ycmVsYXRpb25zIGFtb25nIHNldmVyYWwgaGl0dGluZyB2YXJpYWJsZXMsIHNvIEkgc2hvdWxkIGF2b2lkIG1ha2luZyB0aGUgZmluYWwgbW9kZWwgdG9vIGNvbXBsaWNhdGVkLgoKLS0tCgojIFF1ZXN0aW9uIDc6IFJlbW92ZSBBVkcgYW5kIGNvbXBhcmUgdGhlIG1vZGVsCgojIyBBbnN3ZXIKCkJlY2F1c2UgQVZHIGFuZCBPQlAgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCBJIHJlbW92ZSBBVkcgYW5kIGtlZXAgUkJJLCBPQlAsIGFuZCBPUFMuCgpgYGB7cn0KIyBSZWdyZXNzaW9uIG1vZGVsIGFmdGVyIHJlbW92aW5nIEFWRwptb2RlbDUgPC0gbG0oUGF5cm9sbC5TYWxhcnkyMDIzIH4gUkJJICsgT0JQICsgT1BTLCBkYXRhID0gZmlyc3RiYXNlKQoKIyBWaWV3IG1vZGVsIHJlc3VsdHMKc3VtbWFyeShtb2RlbDUpCmBgYAoKVGhpcyBtb2RlbCBpcyBzaW1wbGVyIHRoYW4gdGhlIHByZXZpb3VzIG1vZGVsIGFuZCBhdm9pZHMgdXNpbmcgYm90aCBBVkcgYW5kIE9CUCB0b2dldGhlci4KCmBgYHtyfQojIFN1bSBvZiBzcXVhcmVkIGVycm9ycyBmb3IgbW9kZWwgNQpTU0VfbW9kZWw1IDwtIHN1bShtb2RlbDUkcmVzaWR1YWxzXjIpCgpTU0VfbW9kZWw1CmBgYAoKVGhlIG1vZGVsIHN0aWxsIGV4cGxhaW5zIGEgdXNlZnVsIGFtb3VudCBvZiB2YXJpYXRpb24gaW4gc2FsYXJ5LCBidXQgT1BTIGlzIG5vdCB2ZXJ5IHNpZ25pZmljYW50IGluIHRoaXMgdmVyc2lvbi4KCi0tLQoKIyBRdWVzdGlvbiA4OiBGaW5hbCBtb2RlbCBzZWxlY3Rpb24KCiMjIEFuc3dlcgoKU2luY2UgT1BTIGlzIG5vdCB2ZXJ5IHNpZ25pZmljYW50IGluIHRoZSBwcmV2aW91cyBtb2RlbCwgSSByZW1vdmUgT1BTIGFuZCBrZWVwIFJCSSBhbmQgT0JQLgoKYGBge3J9CiMgRmluYWwgc2VsZWN0ZWQgbW9kZWwgdXNpbmcgUkJJIGFuZCBPQlAKbW9kZWw2IDwtIGxtKFBheXJvbGwuU2FsYXJ5MjAyMyB+IFJCSSArIE9CUCwgZGF0YSA9IGZpcnN0YmFzZSkKCiMgVmlldyBtb2RlbCByZXN1bHRzCnN1bW1hcnkobW9kZWw2KQpgYGAKClRoaXMgbW9kZWwgaXMgbXkgZmluYWwgbW9kZWwgYmVjYXVzZSBpdCBpcyBzaW1wbGVyIGFuZCBzdGlsbCBoYXMgZ29vZCBleHBsYW5hdG9yeSBwb3dlci4KClRoZSBmaW5hbCBtb2RlbCB1c2VzOgoKLSBgUkJJYAotIGBPQlBgCgpUaGUgbW9kZWwgZXF1YXRpb24gaXM6CgpcWwpcd2lkZWhhdHtTYWxhcnl9ID0gYl8wICsgYl8xKFJCSSkgKyBiXzIoT0JQKQpcXQoKQmFzZWQgb24gdGhlIG1vZGVsIG91dHB1dCwgT0JQIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuIFJCSSBpcyBhbHNvIHVzZWZ1bCBpbiB0aGUgbW9kZWwsIGFsdGhvdWdoIGl0cyBwLXZhbHVlIGlzIGhpZ2hlciB0aGFuIE9CUCdzLgoKLS0tCgojIFF1ZXN0aW9uIDk6IE1ha2UgcHJlZGljdGlvbnMgdXNpbmcgdGhlIHRlc3QgZGF0YQoKIyMgQW5zd2VyCgpOb3cgSSByZWFkIGluIHRoZSB0ZXN0IGRhdGEgYW5kIHVzZSB0aGUgZmluYWwgbW9kZWwgdG8gcHJlZGljdCBzYWxhcnkuCgpgYGB7cn0KIyBSZWFkIGluIHRoZSB0ZXN0IGRhdGEKZmlyc3RiYXNlVGVzdCA8LSByZWFkLmNzdigiZmlyc3RiYXNlc3RhdHNfdGVzdC5jc3YiKQoKIyBWaWV3IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIHRlc3QgZGF0YQpzdHIoZmlyc3RiYXNlVGVzdCkKYGBgCgpUaGUgdGVzdCBkYXRhIGNvbnRhaW5zIHR3byBmaXJzdCBiYXNlbWVuLiBJIHVzZSB0aGUgZmluYWwgbW9kZWwsIGBtb2RlbDZgLCB0byBwcmVkaWN0IHRoZWlyIDIwMjMgc2FsYXJpZXMuCgpgYGB7cn0KIyBNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldApwcmVkaWN0VGVzdCA8LSBwcmVkaWN0KG1vZGVsNiwgbmV3ZGF0YSA9IGZpcnN0YmFzZVRlc3QpCgpwcmVkaWN0VGVzdApgYGAKClRoZSBwcmVkaWN0ZWQgc2FsYXJpZXMgZnJvbSB0aGUgbW9kZWwgYXJlIGFwcHJveGltYXRlbHk6CgotIE1hdHQgT2xzb246IGFib3V0ICQxMC43MiBtaWxsaW9uCi0gSm9zaCBCZWxsOiBhYm91dCAkMTEuNTYgbWlsbGlvbgoKVGhlc2UgcHJlZGljdGlvbnMgYXJlIGJhc2VkIG9ubHkgb24gUkJJIGFuZCBPQlAuCgotLS0KCiMgUXVlc3Rpb24gMTA6IENvbXB1dGUgdGVzdC1zZXQgUi1zcXVhcmVkCgojIyBBbnN3ZXIKClRvIGV2YWx1YXRlIHRoZSBtb2RlbCBvbiB0aGUgdGVzdCBkYXRhLCBJIGNvbXB1dGUgdGhlIFN1bSBvZiBTcXVhcmVkIEVycm9ycyBhbmQgY29tcGFyZSBpdCB3aXRoIHRoZSB0b3RhbCB2YXJpYXRpb24gaW4gdGhlIHRlc3Qgc2FsYXJpZXMuCgpgYGB7cn0KIyBTdW0gb2Ygc3F1YXJlZCBlcnJvcnMgb24gdGhlIHRlc3Qgc2V0ClNTRV90ZXN0IDwtIHN1bSgoZmlyc3RiYXNlVGVzdCRQYXlyb2xsLlNhbGFyeTIwMjMgLSBwcmVkaWN0VGVzdCleMikKCiMgVG90YWwgc3VtIG9mIHNxdWFyZXMgdXNpbmcgdGhlIHRyYWluaW5nIHNhbGFyeSBtZWFuClNTVF90ZXN0IDwtIHN1bSgoZmlyc3RiYXNlVGVzdCRQYXlyb2xsLlNhbGFyeTIwMjMgLSBtZWFuKGZpcnN0YmFzZSRQYXlyb2xsLlNhbGFyeTIwMjMpKV4yKQoKIyBUZXN0LXNldCBSLXNxdWFyZWQKUl9zcXVhcmVkX3Rlc3QgPC0gMSAtIFNTRV90ZXN0IC8gU1NUX3Rlc3QKClJfc3F1YXJlZF90ZXN0CmBgYAoKVGhlIHRlc3Qtc2V0IFItc3F1YXJlZCBpcyBhYm91dCAwLjU0OC4gIApUaGlzIG1lYW5zIHRoZSBtb2RlbCBleHBsYWlucyBhYm91dCA1NC44JSBvZiB0aGUgdmFyaWF0aW9uIGluIHRoZSB0ZXN0IHNhbGFyeSB2YWx1ZXMuCgotLS0KCiMgRmluYWwgQ29uY2x1c2lvbgoKSW4gdGhpcyBhY3Rpdml0eSwgSSBwcmFjdGljZWQgYnVpbGRpbmcgYW5kIGNvbXBhcmluZyBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMgaW4gUi4KClRoZSBiZXN0IGZpbmFsIG1vZGVsIEkgc2VsZWN0ZWQgd2FzOgoKYGBge3J9Cm1vZGVsNiA8LSBsbShQYXlyb2xsLlNhbGFyeTIwMjMgfiBSQkkgKyBPQlAsIGRhdGEgPSBmaXJzdGJhc2UpCnN1bW1hcnkobW9kZWw2KQpgYGAKCkkgY2hvc2UgdGhpcyBtb2RlbCBiZWNhdXNlIGl0IGlzIHNpbXBsZXIgdGhhbiB0aGUgbGFyZ2VyIG1vZGVscyBhbmQgc3RpbGwgZ2l2ZXMgYSByZWFzb25hYmxlIGZpdC4gIApUaGUgbW9kZWwgc3VnZ2VzdHMgdGhhdCBSQkkgYW5kIE9CUCBhcmUgdXNlZnVsIHByZWRpY3RvcnMgb2Ygc2FsYXJ5IGZvciBmaXJzdCBiYXNlbWVuIGluIHRoaXMgZGF0YXNldC4KClRoZSBwcmVkaWN0aW9uIHJlc3VsdHMgc2hvdyB0aGF0IHRoZSBtb2RlbCBjYW4gYmUgYXBwbGllZCB0byBuZXcgcGxheWVycywgYnV0IHRoZSBwcmVkaWN0aW9ucyBhcmUgbm90IHBlcmZlY3QuIFNhbGFyeSBkZXBlbmRzIG9uIG1hbnkgb3RoZXIgZmFjdG9ycyBiZXNpZGVzIGhpdHRpbmcgc3RhdGlzdGljcywgc3VjaCBhcyBhZ2UsIGNvbnRyYWN0IGhpc3RvcnksIHRlYW0gYnVkZ2V0LCBkZWZlbnNlLCBtYXJrZXQgdmFsdWUsIGFuZCBwcmV2aW91cyBzZWFzb25zLgo=