setwd("/Users/patty/Documents/INFOSCI 310")

#read the CSV file into R data frame
player_stats <- read.csv("2022-2023_NBA_player_stats_formatted.csv", header = TRUE, sep = ",")

#view first few rows
head(player_stats)
if (!require("ggplot2")) install.packages("ggplot2")
library(ggplot2)

if (!require("dplyr")) install.packages("dplyr")
library(dplyr)
#simple linear regression on Points per game against field goal percentage

regression_fg <- lm(PTS ~ FG, data = player_stats)

summary(regression_fg)

Call:
lm(formula = PTS ~ FG, data = player_stats)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.5268 -0.4084  0.0488  0.3941  5.6626 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.18595    0.05537  -3.358 0.000828 ***
FG           2.78368    0.01379 201.921  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.8481 on 677 degrees of freedom
Multiple R-squared:  0.9837,    Adjusted R-squared:  0.9836 
F-statistic: 4.077e+04 on 1 and 677 DF,  p-value: < 2.2e-16
#Plotting PTS against FG%
ggplot(player_stats, aes(x = FG, y = PTS)) +
  geom_point() +
  geom_smooth(method = "lm", col = "blue") +
  labs(title = "Regression of PTS on FG%", x = "Field Goal Percentage", y = "Points Per Game (PTS")

regression_mp <- lm(PTS ~ MP, data = player_stats)
summary(regression_mp)

Call:
lm(formula = PTS ~ MP, data = player_stats)

Residuals:
     Min       1Q   Median       3Q      Max 
-19.1040  -1.7397  -0.0236   1.4267  14.9320 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -3.11156    0.28504  -10.92   <2e-16 ***
MP           0.61501    0.01319   46.65   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.233 on 677 degrees of freedom
Multiple R-squared:  0.7627,    Adjusted R-squared:  0.7623 
F-statistic:  2176 on 1 and 677 DF,  p-value: < 2.2e-16
#Plotting PTS against MP
ggplot(player_stats, aes(x = MP, y = PTS)) +
  geom_point() +
  geom_smooth(method = "lm", col = "red") +
  labs(title = "Regression of PTS on MP", x = "Minutes Player per Game (MP)", y = "Points Per Game (PTS)")

# Running the regression analyses
regression_fg <- lm(PTS ~ FG, data = player_stats)
regression_mp <- lm(PTS ~ MP, data = player_stats)

# Assuming we have the summaries stored
summary_fg <- summary(regression_fg)
summary_mp <- summary(regression_mp)

# Interpretation for Field Goal Percentage
cat("\nInterpretation for Field Goal Percentage (FG%):\n")

Interpretation for Field Goal Percentage (FG%):
cat("Slope (Coefficient for FG%):", summary_fg$coefficients["FG", "Estimate"], "\n")
Slope (Coefficient for FG%): 2.783681 
cat("This slope indicates that for each one percent increase in FG%, a player's points per game is expected to change by",
    summary_fg$coefficients["FG", "Estimate"], "points.\n")
This slope indicates that for each one percent increase in FG%, a player's points per game is expected to change by 2.783681 points.
cat("R-squared:", summary_fg$r.squared, "\n")
R-squared: 0.9836668 
cat("This R-squared value suggests that", round(summary_fg$r.squared * 100), "% of the variability in PTS can be explained by FG%.\n")
This R-squared value suggests that 98 % of the variability in PTS can be explained by FG%.
cat("p-value for FG%:", summary_fg$coefficients["FG", "Pr(>|t|)"], "\n")
p-value for FG%: 0 
if (summary_fg$coefficients["FG", "Pr(>|t|)"] < 0.05) {
  cat("This p-value is statistically significant, indicating a strong evidence that FG% has an effect on PTS.\n")
} else {
  cat("This p-value is not statistically significant, which suggests that the relationship between FG% and PTS may be due to chance.\n")
}
This p-value is statistically significant, indicating a strong evidence that FG% has an effect on PTS.
# Interpretation for Minutes Played
cat("\nInterpretation for Minutes Played (MP):\n")

Interpretation for Minutes Played (MP):
cat("Slope (Coefficient for MP):", summary_mp$coefficients["MP", "Estimate"], "\n")
Slope (Coefficient for MP): 0.6150149 
cat("This slope indicates that for each additional minute played, a player's points per game is expected to change by",
    summary_mp$coefficients["MP", "Estimate"], "points.\n")
This slope indicates that for each additional minute played, a player's points per game is expected to change by 0.6150149 points.
cat("R-squared:", summary_mp$r.squared, "\n")
R-squared: 0.7626856 
cat("This R-squared value suggests that", round(summary_mp$r.squared * 100), "% of the variability in PTS can be explained by MP.\n")
This R-squared value suggests that 76 % of the variability in PTS can be explained by MP.
cat("p-value for MP:", summary_mp$coefficients["MP", "Pr(>|t|)"], "\n")
p-value for MP: 1.236772e-213 
if (summary_mp$coefficients["MP", "Pr(>|t|)"] < 0.05) {
  cat("This p-value is statistically significant, which provides strong evidence that MP has an effect on PTS.\n")
} else {
  cat("This p-value is not statistically significant, suggesting that the relationship between MP and PTS may be due to chance.\n")
}
This p-value is statistically significant, which provides strong evidence that MP has an effect on PTS.
#Calc correlation between FG% and MP

correlation <- cor(player_stats$FG, player_stats$MP, use = "complete.obs")

#output corr
cat("Correlation between FG and MP is: ", correlation, "\n")
Correlation between FG and MP is:  0.8757879 
#Decide whether to perform multiple regression
if(abs(correlation) > 0.7) {
  cat("The correlation is high, indicating potential collinearity. It may be more appropriate to use multiple regression with caution, ensuring to check for multicollinearity. \n")
} else{
  cat("The correlation is not high, indicating that simple linear regression may sufficient. However, multiple regression could still be beneficial to understand the combined effect of both variables on Points Per Game. \n")
}
The correlation is high, indicating potential collinearity. It may be more appropriate to use multiple regression with caution, ensuring to check for multicollinearity. 
#Multiple Regression model
mrm <- lm(PTS ~ FG + MP, data = player_stats)

#summary
summary(mrm)

Call:
lm(formula = PTS ~ FG + MP, data = player_stats)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.4563 -0.4077  0.0409  0.3854  5.7385 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.30164    0.08016  -3.763 0.000182 ***
FG           2.73396    0.02850  95.936  < 2e-16 ***
MP           0.01424    0.00715   1.992 0.046772 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.8463 on 676 degrees of freedom
Multiple R-squared:  0.9838,    Adjusted R-squared:  0.9837 
F-statistic: 2.048e+04 on 2 and 676 DF,  p-value: < 2.2e-16
# Assuming the multiple regression model is stored in multiple_regression_model variable

# Get the summary of the model
model_summary <- summary(mrm)


#coefficients and their significance
cat("Coefficients and Significance:\n")
Coefficients and Significance:
if (model_summary$coefficients["FG", "Pr(>|t|)"] < 0.05) {
  cat("FG% is statistically significant with a p-value of", model_summary$coefficients["FG", "Pr(>|t|)"], "\n")
} else {
  cat("FG% is not statistically significant with a p-value of", model_summary$coefficients["FG", "Pr(>|t|)"], "\n")
}
FG% is statistically significant with a p-value of 0 
if (model_summary$coefficients["MP", "Pr(>|t|)"] < 0.05) {
  cat("MP is statistically significant with a p-value of", model_summary$coefficients["MP", "Pr(>|t|)"], "\n")
} else {
  cat("MP is not statistically significant with a p-value of", model_summary$coefficients["MP", "Pr(>|t|)"], "\n")
}
MP is statistically significant with a p-value of 0.04677218 
#model fit
cat("\nModel Fit:\n")

Model Fit:
cat("The R-squared value is", model_summary$r.squared, "which means that", round(model_summary$r.squared * 100), "% of the variability in PTS is explained by the model.\n")
The R-squared value is 0.9837621 which means that 98 % of the variability in PTS is explained by the model.
cat("The adjusted R-squared value is", model_summary$adj.r.squared, "which adjusts for the number of predictors in the model.\n")
The adjusted R-squared value is 0.983714 which adjusts for the number of predictors in the model.
#Residual Error
cat("\nResidual Error:\n")

Residual Error:
cat("The Residual Standard Error (RSE) is", sigma(mrm), "points. This value indicates the typical size of the residuals.\n")
The Residual Standard Error (RSE) is 0.8462535 points. This value indicates the typical size of the residuals.
# Insights R-squared
cat("\nInsights from R-squared:\n")

Insights from R-squared:
cat("A higher R-squared value would suggest a better fit of the model to the data. However, it does not indicate whether the model is appropriate or whether every predictor is significant.\n")
A higher R-squared value would suggest a better fit of the model to the data. However, it does not indicate whether the model is appropriate or whether every predictor is significant.
# Residual error difference between different models

cat("\nResidual Error Difference Between Different Models:\n")

Residual Error Difference Between Different Models:
cat("The RSE for the FG% model is", sigma(regression_fg), "and for the MP model is", sigma(regression_mp), ".\n")
The RSE for the FG% model is 0.8481066 and for the MP model is 3.232782 .
cat("Comparing these with the RSE for the multiple regression model may give insights into whether combining the variables improves the prediction accuracy.\n")
Comparing these with the RSE for the multiple regression model may give insights into whether combining the variables improves the prediction accuracy.
cat("In conclusion, the multiple regression model because it offers a more complex and informative picture of how FG% and MP interact to influecne points per game. This apprach is in line with our project motivation to delve deeper into the dynamics of scoring in the NBA and provides actionable insights that can influence player training, game strategty, and team performance analysis.")
In conclusion, the multiple regression model because it offers a more complex and informative picture of how FG% and MP interact to influecne points per game. This apprach is in line with our project motivation to delve deeper into the dynamics of scoring in the NBA and provides actionable insights that can influence player training, game strategty, and team performance analysis.
#install packages
if (!require("Metrics")) install.packages("Metrics")
library(Metrics)
#set a seed for reproducibility
set.seed(123)

#Randomly sample indices for the training set
train_indicies <- sample(1:nrow(player_stats), size = 0.8 * nrow(player_stats))

#create the training set
train_data <- player_stats[train_indicies, ]

#Create the test by excluding
test_data <- player_stats[-train_indicies, ]

#Check the number of rows in each set
cat("Training set rows:", nrow(train_data), "\n")
Training set rows: 543 
cat("Test set rows:", nrow(test_data), "\n")
Test set rows: 136 
predictors <- setdiff(names(train_data), c("Player", "Tm", "PTS"))

#create the formula for the model by pasting all predictor variables
formula <- as.formula(paste("PTS ~", paste(predictors, collapse = " + ")))

#Fit the multiple linear regression model
model <- lm(formula, data = train_data)

summary(model)

Call:
lm(formula = formula, data = train_data)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.203475 -0.058444  0.002787  0.037992  0.205385 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -2.228e-02  3.169e-02  -0.703 0.482325    
Rk          -1.913e-05  2.073e-05  -0.923 0.356400    
PosPF        8.829e-03  1.208e-02   0.731 0.465076    
PosPF-SF     5.028e-02  5.326e-02   0.944 0.345674    
PosPG       -1.057e-02  1.553e-02  -0.680 0.496571    
PosSF       -5.241e-04  1.384e-02  -0.038 0.969802    
PosSG       -3.328e-03  1.420e-02  -0.234 0.814778    
Age          1.875e-04  7.722e-04   0.243 0.808252    
G            3.872e-04  1.809e-04   2.140 0.032824 *  
GS          -9.585e-06  2.330e-04  -0.041 0.967200    
MP          -1.300e-03  1.271e-03  -1.023 0.306921    
FG           1.761e+00  7.324e-02  24.039  < 2e-16 ***
FGA          9.667e-02  6.551e-02   1.476 0.140678    
FG.1         9.460e-02  1.590e-01   0.595 0.552131    
X3P          1.191e+00  7.417e-02  16.058  < 2e-16 ***
X3PA        -7.491e-02  6.624e-02  -1.131 0.258595    
X3P.         2.227e-02  3.270e-02   0.681 0.496176    
X2P          2.537e-01  7.437e-02   3.411 0.000697 ***
X2PA        -1.027e-01  6.544e-02  -1.570 0.117053    
X2P.        -1.138e-01  4.561e-02  -2.495 0.012925 *  
eFG.         4.342e-02  1.464e-01   0.297 0.766887    
FT           9.739e-01  1.523e-02  63.934  < 2e-16 ***
FTA          1.875e-02  1.274e-02   1.472 0.141654    
FT.         -3.179e-03  1.826e-02  -0.174 0.861855    
ORB         -2.910e-02  6.399e-02  -0.455 0.649433    
DRB         -2.599e-02  6.418e-02  -0.405 0.685676    
TRB          2.424e-02  6.395e-02   0.379 0.704846    
AST         -1.956e-03  3.936e-03  -0.497 0.619553    
STL          9.901e-03  1.280e-02   0.774 0.439581    
BLK          1.521e-02  1.315e-02   1.156 0.248118    
TOV          1.140e-02  1.019e-02   1.119 0.263619    
PF          -7.280e-03  7.578e-03  -0.961 0.337167    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.07187 on 511 degrees of freedom
Multiple R-squared:  0.9999,    Adjusted R-squared:  0.9999 
F-statistic: 1.577e+05 on 31 and 511 DF,  p-value: < 2.2e-16
#predicting on the test set
test_predictons <- predict(model, newdata = test_data)

#computing residuals
residuals <- test_data$PTS - test_predictons

#Computing RSE
rse <- sqrt(mean(residuals^2))

#Computing R squared
sst <- sum((test_data$PTS - mean(test_data$PTS))^2)
ssr <- sum(residuals^2)
r_squared <- 1 - (ssr / sst)

#output
cat("Performance on the Test Data:\n")
Performance on the Test Data:
cat("Residual Standard Error (RSE):", rse, "\n")
Residual Standard Error (RSE): 0.07540592 
cat("R-squared:", r_squared, "\n")
R-squared: 0.9998292 
#Evaluate

#predict on training set
train_predictions <- predict(model, newdata = train_data)

#compute residuals for training set
train_residuals <- train_data$PTS - train_predictions

#compute RSE for training set
train_rse <- sqrt(mean(train_residuals^2))

#compute r squared for the training set
train_sst <- sum((train_data$PTS - mean(train_data$PTS))^2)

train_ssr <- sum(train_residuals^2)

train_r_squared <- 1 - (train_ssr / train_sst)

#compare with test set metrics
cat("Training Data Performance:\n")
Training Data Performance:
cat("R-squared:", train_r_squared, "\n")
R-squared: 0.9998955 
cat("Residual Standard Error (RSE):", train_rse, "\n")
Residual Standard Error (RSE): 0.06971688 
cat("\nTest Data Performance:\n")

Test Data Performance:
cat("R-squared:", r_squared, "\n")
R-squared: 0.9998292 
cat("Residual Standard Error (RSE):", rse, "\n")
Residual Standard Error (RSE): 0.07540592 
cat("\nBoth the training and test data show high r-squared values, indicating that the model is highly accurage in explaining the variance of the dependent variable.\n")

Both the training and test data show high r-squared values, indicating that the model is highly accurage in explaining the variance of the dependent variable.
cat("\nThe consistency of high R-squared and low RSE values between training and test datasets suggest that the model is generalizing well to unseen data. which implies there is no significant overfitting.\n")

The consistency of high R-squared and low RSE values between training and test datasets suggest that the model is generalizing well to unseen data. which implies there is no significant overfitting.
cat("\nR-squared values this high are quite unusual and might indicate overfitting. Which cna happen when the model captures noise in the training set as if it were a part of the underlying pattern.\n")

R-squared values this high are quite unusual and might indicate overfitting. Which cna happen when the model captures noise in the training set as if it were a part of the underlying pattern.
cat("\nIn summary, the model appears to perform extraordinarily well on both sets. While this is a generally postive outcome, its important to critically asses the models complexity, the quality of the data and the potential for overfitting.\n")

In summary, the model appears to perform extraordinarily well on both sets. While this is a generally postive outcome, its important to critically asses the models complexity, the quality of the data and the potential for overfitting.
LS0tDQp0aXRsZTogIkhXNCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCg0KYGBge3J9DQpzZXR3ZCgiL1VzZXJzL3BhdHR5L0RvY3VtZW50cy9JTkZPU0NJIDMxMCIpDQoNCiNyZWFkIHRoZSBDU1YgZmlsZSBpbnRvIFIgZGF0YSBmcmFtZQ0KcGxheWVyX3N0YXRzIDwtIHJlYWQuY3N2KCIyMDIyLTIwMjNfTkJBX3BsYXllcl9zdGF0c19mb3JtYXR0ZWQuY3N2IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiKQ0KDQojdmlldyBmaXJzdCBmZXcgcm93cw0KaGVhZChwbGF5ZXJfc3RhdHMpDQpgYGANCmBgYHtyfQ0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQppZiAoIXJlcXVpcmUoImRwbHlyIikpIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KYGBge3J9DQojc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG9uIFBvaW50cyBwZXIgZ2FtZSBhZ2FpbnN0IGZpZWxkIGdvYWwgcGVyY2VudGFnZQ0KDQpyZWdyZXNzaW9uX2ZnIDwtIGxtKFBUUyB+IEZHLCBkYXRhID0gcGxheWVyX3N0YXRzKQ0KDQpzdW1tYXJ5KHJlZ3Jlc3Npb25fZmcpDQoNCiNQbG90dGluZyBQVFMgYWdhaW5zdCBGRyUNCmdncGxvdChwbGF5ZXJfc3RhdHMsIGFlcyh4ID0gRkcsIHkgPSBQVFMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbCA9ICJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlJlZ3Jlc3Npb24gb2YgUFRTIG9uIEZHJSIsIHggPSAiRmllbGQgR29hbCBQZXJjZW50YWdlIiwgeSA9ICJQb2ludHMgUGVyIEdhbWUgKFBUUyIpDQpgYGANCg0KDQpgYGB7cn0NCnJlZ3Jlc3Npb25fbXAgPC0gbG0oUFRTIH4gTVAsIGRhdGEgPSBwbGF5ZXJfc3RhdHMpDQpzdW1tYXJ5KHJlZ3Jlc3Npb25fbXApDQoNCiNQbG90dGluZyBQVFMgYWdhaW5zdCBNUA0KZ2dwbG90KHBsYXllcl9zdGF0cywgYWVzKHggPSBNUCwgeSA9IFBUUykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJSZWdyZXNzaW9uIG9mIFBUUyBvbiBNUCIsIHggPSAiTWludXRlcyBQbGF5ZXIgcGVyIEdhbWUgKE1QKSIsIHkgPSAiUG9pbnRzIFBlciBHYW1lIChQVFMpIikNCmBgYA0KDQpgYGB7cn0NCiMgUnVubmluZyB0aGUgcmVncmVzc2lvbiBhbmFseXNlcw0KcmVncmVzc2lvbl9mZyA8LSBsbShQVFMgfiBGRywgZGF0YSA9IHBsYXllcl9zdGF0cykNCnJlZ3Jlc3Npb25fbXAgPC0gbG0oUFRTIH4gTVAsIGRhdGEgPSBwbGF5ZXJfc3RhdHMpDQoNCiMgQXNzdW1pbmcgd2UgaGF2ZSB0aGUgc3VtbWFyaWVzIHN0b3JlZA0Kc3VtbWFyeV9mZyA8LSBzdW1tYXJ5KHJlZ3Jlc3Npb25fZmcpDQpzdW1tYXJ5X21wIDwtIHN1bW1hcnkocmVncmVzc2lvbl9tcCkNCg0KIyBJbnRlcnByZXRhdGlvbiBmb3IgRmllbGQgR29hbCBQZXJjZW50YWdlDQpjYXQoIlxuSW50ZXJwcmV0YXRpb24gZm9yIEZpZWxkIEdvYWwgUGVyY2VudGFnZSAoRkclKTpcbiIpDQoNCmNhdCgiU2xvcGUgKENvZWZmaWNpZW50IGZvciBGRyUpOiIsIHN1bW1hcnlfZmckY29lZmZpY2llbnRzWyJGRyIsICJFc3RpbWF0ZSJdLCAiXG4iKQ0KDQpjYXQoIlRoaXMgc2xvcGUgaW5kaWNhdGVzIHRoYXQgZm9yIGVhY2ggb25lIHBlcmNlbnQgaW5jcmVhc2UgaW4gRkclLCBhIHBsYXllcidzIHBvaW50cyBwZXIgZ2FtZSBpcyBleHBlY3RlZCB0byBjaGFuZ2UgYnkiLA0KICAgIHN1bW1hcnlfZmckY29lZmZpY2llbnRzWyJGRyIsICJFc3RpbWF0ZSJdLCAicG9pbnRzLlxuIikNCg0KY2F0KCJSLXNxdWFyZWQ6Iiwgc3VtbWFyeV9mZyRyLnNxdWFyZWQsICJcbiIpDQoNCmNhdCgiVGhpcyBSLXNxdWFyZWQgdmFsdWUgc3VnZ2VzdHMgdGhhdCIsIHJvdW5kKHN1bW1hcnlfZmckci5zcXVhcmVkICogMTAwKSwgIiUgb2YgdGhlIHZhcmlhYmlsaXR5IGluIFBUUyBjYW4gYmUgZXhwbGFpbmVkIGJ5IEZHJS5cbiIpDQoNCmNhdCgicC12YWx1ZSBmb3IgRkclOiIsIHN1bW1hcnlfZmckY29lZmZpY2llbnRzWyJGRyIsICJQcig+fHR8KSJdLCAiXG4iKQ0KaWYgKHN1bW1hcnlfZmckY29lZmZpY2llbnRzWyJGRyIsICJQcig+fHR8KSJdIDwgMC4wNSkgew0KICBjYXQoIlRoaXMgcC12YWx1ZSBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LCBpbmRpY2F0aW5nIGEgc3Ryb25nIGV2aWRlbmNlIHRoYXQgRkclIGhhcyBhbiBlZmZlY3Qgb24gUFRTLlxuIikNCn0gZWxzZSB7DQogIGNhdCgiVGhpcyBwLXZhbHVlIGlzIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LCB3aGljaCBzdWdnZXN0cyB0aGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBGRyUgYW5kIFBUUyBtYXkgYmUgZHVlIHRvIGNoYW5jZS5cbiIpDQp9DQoNCiMgSW50ZXJwcmV0YXRpb24gZm9yIE1pbnV0ZXMgUGxheWVkDQpjYXQoIlxuSW50ZXJwcmV0YXRpb24gZm9yIE1pbnV0ZXMgUGxheWVkIChNUCk6XG4iKQ0KDQpjYXQoIlNsb3BlIChDb2VmZmljaWVudCBmb3IgTVApOiIsIHN1bW1hcnlfbXAkY29lZmZpY2llbnRzWyJNUCIsICJFc3RpbWF0ZSJdLCAiXG4iKQ0KDQpjYXQoIlRoaXMgc2xvcGUgaW5kaWNhdGVzIHRoYXQgZm9yIGVhY2ggYWRkaXRpb25hbCBtaW51dGUgcGxheWVkLCBhIHBsYXllcidzIHBvaW50cyBwZXIgZ2FtZSBpcyBleHBlY3RlZCB0byBjaGFuZ2UgYnkiLA0KICAgIHN1bW1hcnlfbXAkY29lZmZpY2llbnRzWyJNUCIsICJFc3RpbWF0ZSJdLCAicG9pbnRzLlxuIikNCg0KY2F0KCJSLXNxdWFyZWQ6Iiwgc3VtbWFyeV9tcCRyLnNxdWFyZWQsICJcbiIpDQoNCmNhdCgiVGhpcyBSLXNxdWFyZWQgdmFsdWUgc3VnZ2VzdHMgdGhhdCIsIHJvdW5kKHN1bW1hcnlfbXAkci5zcXVhcmVkICogMTAwKSwgIiUgb2YgdGhlIHZhcmlhYmlsaXR5IGluIFBUUyBjYW4gYmUgZXhwbGFpbmVkIGJ5IE1QLlxuIikNCg0KY2F0KCJwLXZhbHVlIGZvciBNUDoiLCBzdW1tYXJ5X21wJGNvZWZmaWNpZW50c1siTVAiLCAiUHIoPnx0fCkiXSwgIlxuIikNCmlmIChzdW1tYXJ5X21wJGNvZWZmaWNpZW50c1siTVAiLCAiUHIoPnx0fCkiXSA8IDAuMDUpIHsNCiAgY2F0KCJUaGlzIHAtdmFsdWUgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCwgd2hpY2ggcHJvdmlkZXMgc3Ryb25nIGV2aWRlbmNlIHRoYXQgTVAgaGFzIGFuIGVmZmVjdCBvbiBQVFMuXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCJUaGlzIHAtdmFsdWUgaXMgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsIHN1Z2dlc3RpbmcgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gTVAgYW5kIFBUUyBtYXkgYmUgZHVlIHRvIGNoYW5jZS5cbiIpDQp9DQoNCmBgYA0KYGBge3J9DQojQ2FsYyBjb3JyZWxhdGlvbiBiZXR3ZWVuIEZHJSBhbmQgTVANCg0KY29ycmVsYXRpb24gPC0gY29yKHBsYXllcl9zdGF0cyRGRywgcGxheWVyX3N0YXRzJE1QLCB1c2UgPSAiY29tcGxldGUub2JzIikNCg0KI291dHB1dCBjb3JyDQpjYXQoIkNvcnJlbGF0aW9uIGJldHdlZW4gRkcgYW5kIE1QIGlzOiAiLCBjb3JyZWxhdGlvbiwgIlxuIikNCg0KI0RlY2lkZSB3aGV0aGVyIHRvIHBlcmZvcm0gbXVsdGlwbGUgcmVncmVzc2lvbg0KaWYoYWJzKGNvcnJlbGF0aW9uKSA+IDAuNykgew0KICBjYXQoIlRoZSBjb3JyZWxhdGlvbiBpcyBoaWdoLCBpbmRpY2F0aW5nIHBvdGVudGlhbCBjb2xsaW5lYXJpdHkuIEl0IG1heSBiZSBtb3JlIGFwcHJvcHJpYXRlIHRvIHVzZSBtdWx0aXBsZSByZWdyZXNzaW9uIHdpdGggY2F1dGlvbiwgZW5zdXJpbmcgdG8gY2hlY2sgZm9yIG11bHRpY29sbGluZWFyaXR5LiBcbiIpDQp9IGVsc2V7DQogIGNhdCgiVGhlIGNvcnJlbGF0aW9uIGlzIG5vdCBoaWdoLCBpbmRpY2F0aW5nIHRoYXQgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1heSBzdWZmaWNpZW50LiBIb3dldmVyLCBtdWx0aXBsZSByZWdyZXNzaW9uIGNvdWxkIHN0aWxsIGJlIGJlbmVmaWNpYWwgdG8gdW5kZXJzdGFuZCB0aGUgY29tYmluZWQgZWZmZWN0IG9mIGJvdGggdmFyaWFibGVzIG9uIFBvaW50cyBQZXIgR2FtZS4gXG4iKQ0KfQ0KYGBgDQpgYGB7cn0NCiNNdWx0aXBsZSBSZWdyZXNzaW9uIG1vZGVsDQptcm0gPC0gbG0oUFRTIH4gRkcgKyBNUCwgZGF0YSA9IHBsYXllcl9zdGF0cykNCg0KI3N1bW1hcnkNCnN1bW1hcnkobXJtKQ0KYGBgDQoNCmBgYHtyfQ0KIyBBc3N1bWluZyB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCBpcyBzdG9yZWQgaW4gbXVsdGlwbGVfcmVncmVzc2lvbl9tb2RlbCB2YXJpYWJsZQ0KDQojIEdldCB0aGUgc3VtbWFyeSBvZiB0aGUgbW9kZWwNCm1vZGVsX3N1bW1hcnkgPC0gc3VtbWFyeShtcm0pDQoNCg0KI2NvZWZmaWNpZW50cyBhbmQgdGhlaXIgc2lnbmlmaWNhbmNlDQpjYXQoIkNvZWZmaWNpZW50cyBhbmQgU2lnbmlmaWNhbmNlOlxuIikNCmlmIChtb2RlbF9zdW1tYXJ5JGNvZWZmaWNpZW50c1siRkciLCAiUHIoPnx0fCkiXSA8IDAuMDUpIHsNCiAgY2F0KCJGRyUgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCB3aXRoIGEgcC12YWx1ZSBvZiIsIG1vZGVsX3N1bW1hcnkkY29lZmZpY2llbnRzWyJGRyIsICJQcig+fHR8KSJdLCAiXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCJGRyUgaXMgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgd2l0aCBhIHAtdmFsdWUgb2YiLCBtb2RlbF9zdW1tYXJ5JGNvZWZmaWNpZW50c1siRkciLCAiUHIoPnx0fCkiXSwgIlxuIikNCn0NCg0KDQppZiAobW9kZWxfc3VtbWFyeSRjb2VmZmljaWVudHNbIk1QIiwgIlByKD58dHwpIl0gPCAwLjA1KSB7DQogIGNhdCgiTVAgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCB3aXRoIGEgcC12YWx1ZSBvZiIsIG1vZGVsX3N1bW1hcnkkY29lZmZpY2llbnRzWyJNUCIsICJQcig+fHR8KSJdLCAiXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCJNUCBpcyBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCB3aXRoIGEgcC12YWx1ZSBvZiIsIG1vZGVsX3N1bW1hcnkkY29lZmZpY2llbnRzWyJNUCIsICJQcig+fHR8KSJdLCAiXG4iKQ0KfQ0KDQojbW9kZWwgZml0DQpjYXQoIlxuTW9kZWwgRml0OlxuIikNCmNhdCgiVGhlIFItc3F1YXJlZCB2YWx1ZSBpcyIsIG1vZGVsX3N1bW1hcnkkci5zcXVhcmVkLCAid2hpY2ggbWVhbnMgdGhhdCIsIHJvdW5kKG1vZGVsX3N1bW1hcnkkci5zcXVhcmVkICogMTAwKSwgIiUgb2YgdGhlIHZhcmlhYmlsaXR5IGluIFBUUyBpcyBleHBsYWluZWQgYnkgdGhlIG1vZGVsLlxuIikNCmNhdCgiVGhlIGFkanVzdGVkIFItc3F1YXJlZCB2YWx1ZSBpcyIsIG1vZGVsX3N1bW1hcnkkYWRqLnIuc3F1YXJlZCwgIndoaWNoIGFkanVzdHMgZm9yIHRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyBpbiB0aGUgbW9kZWwuXG4iKQ0KDQojUmVzaWR1YWwgRXJyb3INCmNhdCgiXG5SZXNpZHVhbCBFcnJvcjpcbiIpDQpjYXQoIlRoZSBSZXNpZHVhbCBTdGFuZGFyZCBFcnJvciAoUlNFKSBpcyIsIHNpZ21hKG1ybSksICJwb2ludHMuIFRoaXMgdmFsdWUgaW5kaWNhdGVzIHRoZSB0eXBpY2FsIHNpemUgb2YgdGhlIHJlc2lkdWFscy5cbiIpDQoNCiMgSW5zaWdodHMgUi1zcXVhcmVkDQpjYXQoIlxuSW5zaWdodHMgZnJvbSBSLXNxdWFyZWQ6XG4iKQ0KY2F0KCJBIGhpZ2hlciBSLXNxdWFyZWQgdmFsdWUgd291bGQgc3VnZ2VzdCBhIGJldHRlciBmaXQgb2YgdGhlIG1vZGVsIHRvIHRoZSBkYXRhLiBIb3dldmVyLCBpdCBkb2VzIG5vdCBpbmRpY2F0ZSB3aGV0aGVyIHRoZSBtb2RlbCBpcyBhcHByb3ByaWF0ZSBvciB3aGV0aGVyIGV2ZXJ5IHByZWRpY3RvciBpcyBzaWduaWZpY2FudC5cbiIpDQoNCiMgUmVzaWR1YWwgZXJyb3IgZGlmZmVyZW5jZSBiZXR3ZWVuIGRpZmZlcmVudCBtb2RlbHMNCg0KY2F0KCJcblJlc2lkdWFsIEVycm9yIERpZmZlcmVuY2UgQmV0d2VlbiBEaWZmZXJlbnQgTW9kZWxzOlxuIikNCmNhdCgiVGhlIFJTRSBmb3IgdGhlIEZHJSBtb2RlbCBpcyIsIHNpZ21hKHJlZ3Jlc3Npb25fZmcpLCAiYW5kIGZvciB0aGUgTVAgbW9kZWwgaXMiLCBzaWdtYShyZWdyZXNzaW9uX21wKSwgIi5cbiIpDQpjYXQoIkNvbXBhcmluZyB0aGVzZSB3aXRoIHRoZSBSU0UgZm9yIHRoZSBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsIG1heSBnaXZlIGluc2lnaHRzIGludG8gd2hldGhlciBjb21iaW5pbmcgdGhlIHZhcmlhYmxlcyBpbXByb3ZlcyB0aGUgcHJlZGljdGlvbiBhY2N1cmFjeS5cbiIpDQoNCmBgYA0KDQpgYGB7cn0NCmNhdCgiSW4gY29uY2x1c2lvbiwgdGhlIG11bHRpcGxlIHJlZ3Jlc3Npb24gbW9kZWwgYmVjYXVzZSBpdCBvZmZlcnMgYSBtb3JlIGNvbXBsZXggYW5kIGluZm9ybWF0aXZlIHBpY3R1cmUgb2YgaG93IEZHJSBhbmQgTVAgaW50ZXJhY3QgdG8gaW5mbHVlY25lIHBvaW50cyBwZXIgZ2FtZS4gVGhpcyBhcHByYWNoIGlzIGluIGxpbmUgd2l0aCBvdXIgcHJvamVjdCBtb3RpdmF0aW9uIHRvIGRlbHZlIGRlZXBlciBpbnRvIHRoZSBkeW5hbWljcyBvZiBzY29yaW5nIGluIHRoZSBOQkEgYW5kIHByb3ZpZGVzIGFjdGlvbmFibGUgaW5zaWdodHMgdGhhdCBjYW4gaW5mbHVlbmNlIHBsYXllciB0cmFpbmluZywgZ2FtZSBzdHJhdGVndHksIGFuZCB0ZWFtIHBlcmZvcm1hbmNlIGFuYWx5c2lzLiIpDQpgYGANCmBgYHtyfQ0KI2luc3RhbGwgcGFja2FnZXMNCmlmICghcmVxdWlyZSgiTWV0cmljcyIpKSBpbnN0YWxsLnBhY2thZ2VzKCJNZXRyaWNzIikNCmxpYnJhcnkoTWV0cmljcykNCg0KYGBgDQoNCmBgYHtyfQ0KI3NldCBhIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoMTIzKQ0KDQojUmFuZG9tbHkgc2FtcGxlIGluZGljZXMgZm9yIHRoZSB0cmFpbmluZyBzZXQNCnRyYWluX2luZGljaWVzIDwtIHNhbXBsZSgxOm5yb3cocGxheWVyX3N0YXRzKSwgc2l6ZSA9IDAuOCAqIG5yb3cocGxheWVyX3N0YXRzKSkNCg0KI2NyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0DQp0cmFpbl9kYXRhIDwtIHBsYXllcl9zdGF0c1t0cmFpbl9pbmRpY2llcywgXQ0KDQojQ3JlYXRlIHRoZSB0ZXN0IGJ5IGV4Y2x1ZGluZw0KdGVzdF9kYXRhIDwtIHBsYXllcl9zdGF0c1stdHJhaW5faW5kaWNpZXMsIF0NCg0KI0NoZWNrIHRoZSBudW1iZXIgb2Ygcm93cyBpbiBlYWNoIHNldA0KY2F0KCJUcmFpbmluZyBzZXQgcm93czoiLCBucm93KHRyYWluX2RhdGEpLCAiXG4iKQ0KY2F0KCJUZXN0IHNldCByb3dzOiIsIG5yb3codGVzdF9kYXRhKSwgIlxuIikNCg0KYGBgDQpgYGB7cn0NCnByZWRpY3RvcnMgPC0gc2V0ZGlmZihuYW1lcyh0cmFpbl9kYXRhKSwgYygiUGxheWVyIiwgIlRtIiwgIlBUUyIpKQ0KDQojY3JlYXRlIHRoZSBmb3JtdWxhIGZvciB0aGUgbW9kZWwgYnkgcGFzdGluZyBhbGwgcHJlZGljdG9yIHZhcmlhYmxlcw0KZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlKCJQVFMgfiIsIHBhc3RlKHByZWRpY3RvcnMsIGNvbGxhcHNlID0gIiArICIpKSkNCg0KI0ZpdCB0aGUgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwNCm1vZGVsIDwtIGxtKGZvcm11bGEsIGRhdGEgPSB0cmFpbl9kYXRhKQ0KDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQpgYGB7cn0NCiNwcmVkaWN0aW5nIG9uIHRoZSB0ZXN0IHNldA0KdGVzdF9wcmVkaWN0b25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpDQoNCiNjb21wdXRpbmcgcmVzaWR1YWxzDQpyZXNpZHVhbHMgPC0gdGVzdF9kYXRhJFBUUyAtIHRlc3RfcHJlZGljdG9ucw0KDQojQ29tcHV0aW5nIFJTRQ0KcnNlIDwtIHNxcnQobWVhbihyZXNpZHVhbHNeMikpDQoNCiNDb21wdXRpbmcgUiBzcXVhcmVkDQpzc3QgPC0gc3VtKCh0ZXN0X2RhdGEkUFRTIC0gbWVhbih0ZXN0X2RhdGEkUFRTKSleMikNCnNzciA8LSBzdW0ocmVzaWR1YWxzXjIpDQpyX3NxdWFyZWQgPC0gMSAtIChzc3IgLyBzc3QpDQoNCiNvdXRwdXQNCmNhdCgiUGVyZm9ybWFuY2Ugb24gdGhlIFRlc3QgRGF0YTpcbiIpDQpjYXQoIlJlc2lkdWFsIFN0YW5kYXJkIEVycm9yIChSU0UpOiIsIHJzZSwgIlxuIikNCmNhdCgiUi1zcXVhcmVkOiIsIHJfc3F1YXJlZCwgIlxuIikNCg0KYGBgDQpgYGB7cn0NCiNFdmFsdWF0ZQ0KDQojcHJlZGljdCBvbiB0cmFpbmluZyBzZXQNCnRyYWluX3ByZWRpY3Rpb25zIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSB0cmFpbl9kYXRhKQ0KDQojY29tcHV0ZSByZXNpZHVhbHMgZm9yIHRyYWluaW5nIHNldA0KdHJhaW5fcmVzaWR1YWxzIDwtIHRyYWluX2RhdGEkUFRTIC0gdHJhaW5fcHJlZGljdGlvbnMNCg0KI2NvbXB1dGUgUlNFIGZvciB0cmFpbmluZyBzZXQNCnRyYWluX3JzZSA8LSBzcXJ0KG1lYW4odHJhaW5fcmVzaWR1YWxzXjIpKQ0KDQojY29tcHV0ZSByIHNxdWFyZWQgZm9yIHRoZSB0cmFpbmluZyBzZXQNCnRyYWluX3NzdCA8LSBzdW0oKHRyYWluX2RhdGEkUFRTIC0gbWVhbih0cmFpbl9kYXRhJFBUUykpXjIpDQoNCnRyYWluX3NzciA8LSBzdW0odHJhaW5fcmVzaWR1YWxzXjIpDQoNCnRyYWluX3Jfc3F1YXJlZCA8LSAxIC0gKHRyYWluX3NzciAvIHRyYWluX3NzdCkNCg0KI2NvbXBhcmUgd2l0aCB0ZXN0IHNldCBtZXRyaWNzDQpjYXQoIlRyYWluaW5nIERhdGEgUGVyZm9ybWFuY2U6XG4iKQ0KY2F0KCJSLXNxdWFyZWQ6IiwgdHJhaW5fcl9zcXVhcmVkLCAiXG4iKQ0KY2F0KCJSZXNpZHVhbCBTdGFuZGFyZCBFcnJvciAoUlNFKToiLCB0cmFpbl9yc2UsICJcbiIpDQoNCmNhdCgiXG5UZXN0IERhdGEgUGVyZm9ybWFuY2U6XG4iKQ0KDQpjYXQoIlItc3F1YXJlZDoiLCByX3NxdWFyZWQsICJcbiIpDQpjYXQoIlJlc2lkdWFsIFN0YW5kYXJkIEVycm9yIChSU0UpOiIsIHJzZSwgIlxuIikNCg0KYGBgDQpgYGB7cn0NCmNhdCgiXG5Cb3RoIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBkYXRhIHNob3cgaGlnaCByLXNxdWFyZWQgdmFsdWVzLCBpbmRpY2F0aW5nIHRoYXQgdGhlIG1vZGVsIGlzIGhpZ2hseSBhY2N1cmFnZSBpbiBleHBsYWluaW5nIHRoZSB2YXJpYW5jZSBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLlxuIikNCg0KY2F0KCJcblRoZSBjb25zaXN0ZW5jeSBvZiBoaWdoIFItc3F1YXJlZCBhbmQgbG93IFJTRSB2YWx1ZXMgYmV0d2VlbiB0cmFpbmluZyBhbmQgdGVzdCBkYXRhc2V0cyBzdWdnZXN0IHRoYXQgdGhlIG1vZGVsIGlzIGdlbmVyYWxpemluZyB3ZWxsIHRvIHVuc2VlbiBkYXRhLiB3aGljaCBpbXBsaWVzIHRoZXJlIGlzIG5vIHNpZ25pZmljYW50IG92ZXJmaXR0aW5nLlxuIikNCg0KY2F0KCJcblItc3F1YXJlZCB2YWx1ZXMgdGhpcyBoaWdoIGFyZSBxdWl0ZSB1bnVzdWFsIGFuZCBtaWdodCBpbmRpY2F0ZSBvdmVyZml0dGluZy4gV2hpY2ggY25hIGhhcHBlbiB3aGVuIHRoZSBtb2RlbCBjYXB0dXJlcyBub2lzZSBpbiB0aGUgdHJhaW5pbmcgc2V0IGFzIGlmIGl0IHdlcmUgYSBwYXJ0IG9mIHRoZSB1bmRlcmx5aW5nIHBhdHRlcm4uXG4iKQ0KDQpjYXQoIlxuSW4gc3VtbWFyeSwgdGhlIG1vZGVsIGFwcGVhcnMgdG8gcGVyZm9ybSBleHRyYW9yZGluYXJpbHkgd2VsbCBvbiBib3RoIHNldHMuIFdoaWxlIHRoaXMgaXMgYSBnZW5lcmFsbHkgcG9zdGl2ZSBvdXRjb21lLCBpdHMgaW1wb3J0YW50IHRvIGNyaXRpY2FsbHkgYXNzZXMgdGhlIG1vZGVscyBjb21wbGV4aXR5LCB0aGUgcXVhbGl0eSBvZiB0aGUgZGF0YSBhbmQgdGhlIHBvdGVudGlhbCBmb3Igb3ZlcmZpdHRpbmcuXG4iKQ0KDQpgYGANCg0K