Abstract

This research was divised to provide the reader with insight as to which offensive statistcis are most significant in relation to total offensive points scored by a team. Those statistics will then be used as significant variables in order to predict offensive point outputs by teams in the years to come via a created linear regression model.

Upon creating a model that was not only accurate but simplistic enough (using a reasonable amount of significant statistics), we then checked to make sure it passed all 5 linear regression assumptions. Upon passing the assumptions the model was ready for prediction. The 8 significant variables included in it were: TotYds (total yards of offense), FL (fumbles lost), PTD (passing touchdowns), YPA (yards per pass attempt), P1std (passing first downs), RuTD (rushing touchdowns), Ru1stD (rushing first downs), and Pen (penalties). When put into context it makes sense these are some of the most significant variables as they are all critical statistics that either depict signs of a juggernaut offense that can keep the chains moving or a weak offense that struggles to get first downs and constantly turns the ball over.

With this 8 variable model we were able to construct a function that upon inputting values for each of these 8 significant variables was able to give a rough projection of average points scored per team as well as average points scored per game.

Introduction

Problem

Points per game in the NFL has been on a steady upward trend basically since the leagues inception. While it’s easy to say that “Players have just become more skilled now than they were back then” and there certainly can be some truth to this statement, there’s defintely much more to it. Therefore we’ll be taking a deep dive into NFL offensive averages per game by season to hopefully uncover which of these statistics have helped play the biggest role in putting more points up on the scoreboard for us viewers.

Focus

To dive into this data we’ll be using Pro Football Reference’s Team Offense League Averages Per Team Game dataset from the years 1949-2020, for completeness of recorded statistics. The statistics range from total combined offensive stats per game, to passing/rushing splits. Additionally statistics regarding turnovers as well as penalties are taken into account. Therefore there will be no statistical stone left unturned throughout this analysis.

Methodology

To analyze this data we’ll be using a combination of visualization as well as statistical techniques. We’ll be plotting the primary statistics with avergae points per game to see if they have any distinct positive or negative correlation with each other. Additionally we will be running a regression on the statistical dataset, and then we’ll create and test multiple other fits/models to see if we are able to construct one that combines accuracy of prediction as well as simplicity of variables used into one. The variables of our dataset as well as a description of them are listed below.

Variable Name

  • PtsFor

  • Tot Yds

  • Ply

  • YPP

  • TO

  • FL

  • Tot1std

  • Cmp

  • Att

  • PYds

  • PTD

  • Int

  • YPA

  • P1stD

  • RuAtt

  • RuYds

  • RuTD

  • YPC

  • Ru1stD

  • Pen

  • PenYds

  • 1stPy

Variable Type

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Numeric

Variable Description

Points For (Per team)

Total Yds (Rush+Receiving)

Total Plays (Per team)

Yards Per Play

Turnovers

Fumbles Lost

Total First Downs

Completions

Pass Attempts

Pass Yards

Pass Touchdowns

Interceptions

Yards Per Attempt (Passing)

Passing 1st Downs

Rush Attempts

Rush Yards

Rush Touchdowns

Yards Per Carry

Rushing First Downs

Penalties Committed (Accepted by opposition)

Penalty Yardage

Penalties Resulting in First Downs

Data Cleaning

library(readxl)
NFL <- read_xlsx("NFLFinalProj.xlsx", col_names = TRUE, na = "NA")

head(NFL, 50)

To begin we’ll remove columns 1,2, and 3 respectively which correspond to rank, year, and number of teams in the league. These stats are of no use to our set as rank has no effect here, it’s just ordering the years. Additionally since we are analyzing which statistics correspond to more points being scored on average the year is of no use to us either, as it provides no football significance. Finally although the number of teams in the league has expanded over the years, we are analyzing leaguewide data, so as long as all teams are included the total number isn’t of much concern.

clean1 <- NFL[,-c(1:3)]

head(clean1, 50)

Next we’ll be omitting columns 23-28 as they’ve only begun recording these statistics regarding drive length, scoring percentage, turnover percentage, average number of plays, average number of yards, and average points per drive since roughly 2000. Given the data goes back to 1922 we’d be missing out on a plethora of years in our sample.

clean2 <- clean1[,-c(23:28)]

head(clean2, 50)

To continue we’ll run a na.omit command on our remaining data, to clean out any remaining NA values that are still within our data.

FinalNFL <- na.omit(clean2)
head(FinalNFL, 50)

Now that are data is fully cleaned we see our data consists of the NFL Team Offense League Averages Per Team Game for the seasons 1949-2020, thus the final cleaning of the data rid the set of the years 1922-1948 due to lack of data regarding them.

Nontheless we now have 72 years of NFL average offensive statistics to find out which variables contribute most to a team putting up points.

Packages Used

library(readxl) #Used to read data in via excel file
library(ggplot2) # Used to help creaate and organize both box and scatterplots
library(reshape2) # Used to plot and fit boxplots of all variables appropriately
library(leaps) # Used to help determine model's most significant variables
library(car) # Used as a part of the prediction function created

Exploratory Data Analysis

library(ggplot2)
library(reshape2)

NFLPoints <- melt(data = FinalNFL)
## No id variables; using all as measure variables
ggplot(data = NFLPoints, aes(x=variable, y=value)) + geom_boxplot() +facet_wrap(~variable, scales = "free")

As we can see all the data here presented within the variables appears to be normally distributed with only a few of them containing an outlier or two.

ggplot(FinalNFL, aes(x=TotYds, y=PtsFor)) + geom_point(color="green")

ggplot(FinalNFL, aes(x=PTD, y=PtsFor)) + geom_point(color="blue")

ggplot(FinalNFL, aes(x=Pen, y=PtsFor)) + geom_point(color="red")

ggplot(FinalNFL, aes(x=YPA, y=PtsFor)) + geom_point(color="cyan")

It wouldn’t be feasible to show a scatter plot for all 21 variables as it would occupy too much space or they would all be too small to get a fair visual on. However from these four randomly chosen variables from the dataset you can see that they all follow a linear pattern/trend thus they are appropriate to use in a linear regression model. This additionally is a precursor for the rest of the variables too, as they also follow linear trends. Thus we can safely attempt to use a linear regression model to fit this data.

Creating the Model

To begin we’ll run a regression on our data using all 21 variables.

options(scipen=4)
MLR <- lm(formula = PtsFor ~ ., data=FinalNFL)
summary(MLR)
## 
## Call:
## lm(formula = PtsFor ~ ., data = FinalNFL)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.77090 -0.22923  0.02343  0.20991  0.93498 
## 
## Coefficients:
##               Estimate Std. Error t value     Pr(>|t|)    
## (Intercept) 31.8203560 14.6514728   2.172       0.0346 *  
## TotYds       0.2695916  0.9698411   0.278       0.7822    
## Ply          0.1428058  0.3893455   0.367       0.7153    
## YPP          0.0002126  1.9739941   0.000       0.9999    
## TO           1.5333739  1.6149700   0.949       0.3469    
## FL          -3.3832308  1.5835883  -2.136       0.0376 *  
## Tot1stD     -0.4237103  1.1353244  -0.373       0.7106    
## Cmp          0.0606371  0.2159394   0.281       0.7800    
## Att         -0.2042333  0.3119737  -0.655       0.5157    
## PYds        -0.1813786  0.9762030  -0.186       0.8534    
## PTD          5.6853297  0.8715506   6.523 0.0000000334 ***
## Int         -1.7924614  1.5883578  -1.128       0.2645    
## YPA         -2.0366638  1.6082082  -1.266       0.2112    
## P1stD       -0.6570512  1.1667418  -0.563       0.5758    
## RuAtt       -0.5731412  0.4728462  -1.212       0.2312    
## RuYds       -0.1151497  0.9710158  -0.119       0.9061    
## RuTD         4.5086568  1.0407345   4.332 0.0000711003 ***
## YPC         -3.2024224  2.0073370  -1.595       0.1169    
## Ru1stD      -0.2422972  1.1145780  -0.217       0.8288    
## Pen         -0.3169721  0.2332310  -1.359       0.1802    
## PenYds      -0.0231319  0.0318336  -0.727       0.4708    
## `1stPy`      0.6263404  1.2596146   0.497       0.6212    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4214 on 50 degrees of freedom
## Multiple R-squared:  0.9329, Adjusted R-squared:  0.9047 
## F-statistic:  33.1 on 21 and 50 DF,  p-value: < 2.2e-16

Although this model provides accurate predictions, as evidenced by the r-squared/adjusted r-squared values, 21 varaiables for one regression model is a rather large number. Thus we’ll run a step test on the data to see if we can find any models that maintain a similar level of accuracy while using a smaller amount of significant variables.

summary(step)
## 
## Call:
## lm(formula = PtsFor ~ TotYds + FL + Tot1stD + PYds + PTD + YPA + 
##     RuAtt + RuTD + YPC + Pen + `1stPy`, data = FinalNFL)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.74547 -0.25041  0.04895  0.22151  1.05136 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 28.09525    7.42370   3.785 0.000358 ***
## TotYds       0.15007    0.06230   2.409 0.019092 *  
## FL          -1.74502    0.57895  -3.014 0.003773 ** 
## Tot1stD     -0.74372    0.17363  -4.283 6.75e-05 ***
## PYds        -0.07923    0.05792  -1.368 0.176440    
## PTD          5.43341    0.61023   8.904 1.43e-12 ***
## YPA         -1.81473    0.38195  -4.751 1.30e-05 ***
## RuAtt       -0.40866    0.23109  -1.768 0.082074 .  
## RuTD         4.55816    0.80361   5.672 4.30e-07 ***
## YPC         -2.77170    1.73684  -1.596 0.115781    
## Pen         -0.43326    0.13696  -3.163 0.002447 ** 
## `1stPy`      0.94169    0.45796   2.056 0.044112 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.3965 on 60 degrees of freedom
## Multiple R-squared:  0.9287, Adjusted R-squared:  0.9157 
## F-statistic: 71.07 on 11 and 60 DF,  p-value: < 2.2e-16

The step test was able to provide us with a nearly identical model accuracy wise, while only using 11 of 21 variables. However for simplicity purposes we will continue to test further to see if we can find a model that once again uses less variables. However, the models lack of variables doesn’t allow for model accuracy to be compromised.

Therefore the next 3 models I randomly chose using my knowledge of football I’ve obtained throughout my many years of watching, to see if I could create the most accurate model yet.

randlm1 <- lm(formula = PtsFor ~ TotYds+Ply+TO+Att+PTD+RuTD+Pen+PenYds , data=FinalNFL)
summary(randlm1)
## 
## Call:
## lm(formula = PtsFor ~ TotYds + Ply + TO + Att + PTD + RuTD + 
##     Pen + PenYds, data = FinalNFL)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.20473 -0.32844  0.01517  0.27351  1.20777 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  5.983787   2.628269   2.277   0.0262 *  
## TotYds       0.002304   0.009925   0.232   0.8172    
## Ply          0.005953   0.064663   0.092   0.9269    
## TO          -0.415252   0.263968  -1.573   0.1207    
## Att          0.126701   0.057660   2.197   0.0317 *  
## PTD          5.884595   0.773575   7.607 1.74e-10 ***
## RuTD         4.124290   0.951266   4.336 5.34e-05 ***
## Pen         -0.524228   0.247492  -2.118   0.0381 *  
## PenYds       0.048013   0.026735   1.796   0.0773 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4777 on 63 degrees of freedom
## Multiple R-squared:  0.8913, Adjusted R-squared:  0.8776 
## F-statistic:  64.6 on 8 and 63 DF,  p-value: < 2.2e-16
randlm2 <- lm(formula = PtsFor ~ TotYds+Ply+YPP+Tot1stD+Cmp+YPA+YPC , data=FinalNFL)
summary(randlm2)
## 
## Call:
## lm(formula = PtsFor ~ TotYds + Ply + YPP + Tot1stD + Cmp + YPA + 
##     YPC, data = FinalNFL)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.52911 -0.47881 -0.06348  0.29589  2.24158 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 32.63530   19.33200   1.688 0.096250 .  
## TotYds       0.22093    0.06476   3.411 0.001124 ** 
## Ply         -0.51053    0.30589  -1.669 0.100001    
## YPP          5.87291    3.12933   1.877 0.065116 .  
## Tot1stD     -0.70463    0.27426  -2.569 0.012533 *  
## Cmp         -0.55277    0.14331  -3.857 0.000269 ***
## YPA         -6.33532    1.88559  -3.360 0.001318 ** 
## YPC         -4.75050    1.74464  -2.723 0.008330 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.7843 on 64 degrees of freedom
## Multiple R-squared:  0.7025, Adjusted R-squared:   0.67 
## F-statistic: 21.59 on 7 and 64 DF,  p-value: 1.205e-14
randlm3 <- lm(formula = PtsFor ~ TotYds+Ply+PTD+RuTD+FL+Int+Pen , data=FinalNFL)
summary(randlm3)
## 
## Call:
## lm(formula = PtsFor ~ TotYds + Ply + PTD + RuTD + FL + Int + 
##     Pen, data = FinalNFL)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.39555 -0.25000  0.02328  0.22411  1.12568 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  7.018159   2.370464   2.961  0.00430 ** 
## TotYds       0.003016   0.008361   0.361  0.71954    
## Ply          0.052600   0.062418   0.843  0.40254    
## PTD          6.796104   0.625071  10.873 3.52e-16 ***
## RuTD         3.361831   0.762273   4.410 4.04e-05 ***
## FL          -1.839797   0.635373  -2.896  0.00517 ** 
## Int          0.222068   0.477314   0.465  0.64333    
## Pen         -0.169123   0.144473  -1.171  0.24609    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4762 on 64 degrees of freedom
## Multiple R-squared:  0.8903, Adjusted R-squared:  0.8783 
## F-statistic: 74.21 on 7 and 64 DF,  p-value: < 2.2e-16

Unfortunately all three of these models randomly created by me had significantly lower r-squared values than what we’ve seen in the initial two models. Thus we must continue to search for the ideal model combination.

Using the leaps package we’ll have a handful more models given to us, allowing us to analyze and decide if any of them have an adequate enough combination of accuracy and simplicity, so we can use for our prediction model.

library(leaps)
best <- regsubsets(PtsFor~., data=FinalNFL)
plot(best, scale="adjr2", main="Best-fit: Adjusted R square")

best.sum<- summary(best)
out.table <- data.frame(best.sum$outmat, best.sum$adjr2, best.sum$rss, best.sum$cp, best.sum$bic)
out.table

Looking through each of these 8 models, we see that fit8 is the best of the group. It has a similar r-squared to our first two models tested. Additionally, it uses only 8 of 21 variables within it. Thus we will test/summarize this model to make sure it is fully adequate to be tested.

BestModel <- lm(PtsFor ~ TotYds+FL+PTD+YPA+P1stD+RuTD+Ru1stD+Pen, data = FinalNFL)
summary(BestModel)
## 
## Call:
## lm(formula = PtsFor ~ TotYds + FL + PTD + YPA + P1stD + RuTD + 
##     Ru1stD + Pen, data = FinalNFL)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.84121 -0.28561  0.03454  0.27031  0.94456 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 15.02499    1.96410   7.650 1.46e-10 ***
## TotYds       0.05313    0.01235   4.301 6.03e-05 ***
## FL          -1.64012    0.42781  -3.834 0.000294 ***
## PTD          5.81081    0.54924  10.580 1.33e-15 ***
## YPA         -1.51224    0.36113  -4.188 8.93e-05 ***
## P1stD       -0.44992    0.16037  -2.805 0.006675 ** 
## RuTD         4.40183    0.91018   4.836 8.88e-06 ***
## Ru1stD      -0.78746    0.19202  -4.101 0.000120 ***
## Pen         -0.39789    0.11611  -3.427 0.001080 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4023 on 63 degrees of freedom
## Multiple R-squared:  0.9229, Adjusted R-squared:  0.9131 
## F-statistic: 94.31 on 8 and 63 DF,  p-value: < 2.2e-16

After reading the summary we see that everything from fit8, now titled BestModel, checked out. Therefore it is now time to check and make sure that this model meets all the assumptions, so that it is alright to go forward with creating a prediction model.

plot(BestModel)

Meeting Assumptions

We now must make sure that our model meets all 5 assumptions of multiple linear regression.

  • Assumption 1 - the probability distribition of the random errors (epsilon′s) has a mean of 0.
    • There is no direct way to check this condition, the least squares regression equation ensures this is true forthe sample by its calculations - we will need to assume this is an ok assumption for the population.
  • Assumption 2 - the probability distribution of the random errors (epsilon′s) has a constant variance.
    • The residual versus fit plot shows a random scatter pattern except for a few data values.It may be that our model does not fit best with these types of extreme values. We may want to consider splitting the dataset into 2 populations thus creating models for each.
  • Assumption 3: the probability distribution of the random errors (epsilon) follows a normal distribution.
    • The normal probability plot of the residuals appears to follow a relatively linear path. We don’t have enoughevidence to say the random errors are not normally distributed.
  • Assumption 4: the random errors (epsilon) are independent (little to no autocorrelation).
library(car)
## Loading required package: carData
dwt <- durbinWatsonTest(BestModel)
dwt
##  lag Autocorrelation D-W Statistic p-value
##    1       0.1851193      1.612345   0.032
##  Alternative hypothesis: rho != 0

Since the p-value (0.06) is greater than any reasonable alpha level, there is evidence to fail to reject thenull hypothesis. There is insufficient evidence to suggest that there is autocorrelation.

  • Assumption 5: Little to no multicollinearity.
    • Given that no pair of variables share a correlation coefficient over .8, in addition to having no VIF values over 5, thus there’s not sufficient evidence to indicate multicollinearity.

We see that our chosen model has met all assumptions and thus is ready to be used for prediction! The function created below has inputs of the 8 significant variables/parameters used by this fit. The function runs a regression using the model and its inputted values, and the output provides us not only what the average team total per game would be in the NFL with those inputs, but also what the entire game total of points would be if those were the season averages of our inputted statistics as well.

library(car)
PredPtsFor <- function(TotYds, FL, PTD, YPA, P1stD, RuTD, Ru1stD, Pen){
  inputs <- data.frame(TotYds, FL, PTD, YPA, P1stD, RuTD, Ru1stD, Pen)
  PointsPred <- predict(BestModel, newdata = inputs)
  PointsForNew <- as.integer(PointsPred)
  
  cat("Teams are projected to score on average",PointsForNew, "points per game this season.\n" )
  
  cat("Games are projected to have a total score of",2*PointsForNew, "points this season." )
}
PredPtsFor(300, .9, 1.6, 6.3, 13, 1.2, 7, 4.8)
## Teams are projected to score on average 21 points per game this season.
## Games are projected to have a total score of 42 points this season.
PredPtsFor(360, .6, 1.9, 7.1, 15, 1.4, 9, 5.5)
## Teams are projected to score on average 23 points per game this season.
## Games are projected to have a total score of 46 points this season.
PredPtsFor(347, .8, 2.1, 6.8, 14, 1.4, 10, 4)
## Teams are projected to score on average 24 points per game this season.
## Games are projected to have a total score of 48 points this season.

Summary/Conclusion

Findings

To summarize we took a set of total league offensive statistics of the NFL from the years 1922-2020. We then cleaned this dataset/narrowed it down to the seasons spanning 1949-2020. With this newly cleaned dataset we embarked to see which offensive related statistics were most significant in causing the gradual increase in offensive points scored per game in the NFL year-over-year. To do so we created and tested numerous linear regression models until we were able to arrive at a model that was able to perfectly balance accuracy with simplicity. The significant variables in this model were TotYds, FL, PTD, YPA, P1stD, RuTD, Ru1stD, and Pen. Before using this model for prediction, all assumptions were checked and subsequently met, allowing us to proceed with our prediction process as our model was deemed statistically significant. With all of this in mind using this model should give you a rough idea about what the offensive landscape in the NFL should look like for the upcoming season(s) given the values of the statistics that are inputted.

This research isn’t necessarily groundbreaking or world changing, however, it certainly has a niche. Whether it be coaches or analysts using a model like this to determine what areas a team needs to shore up or improve upon in order to put more points up on the board. Or if it’s just a casual fan inputting stats into these of their favorite team to see how many points they’re projected to score this week/how many points the opposition is projected to score this week.

Where to Look Next?

The next best, and most realistic step to take with this data would be to run smaller sample sized regressions using some of the more advanced statistics that were introduced in the late 90’s to the data. These statistics include drive duration, average number of plays per drive, average total yardage per drive, and much more. If the regression models consistently show over the next few years that any of these models have a great deal of significance they could then be added to our current prediction model to enhance it’s accuracy even more.

Personal Information

James Weinblatt is a senior Mathematical Science major here at Clemson University. He’ll be receving his bachelor of science degree next week at graduation and then continue his search for an employment opportunity back home in New York.

This project combines two of James’s favorite things; sports as well as analyzing data. He plans to show this project to possible employers to exemplify his coding/ analysis skills. In hopes to receive an entry level position at a company.

Additionally James hopes that anyone else who happens to be looking at this project enjoys its contents and is able to learn something they find valuable within it.

Full Code

library(readxl)
NFL <- read_xlsx("NFLFinalProj.xlsx", col_names = TRUE, na = "NA")
#Reading data into R
head(NFL, 50)

clean1 <- NFL[,-c(1:3)]

head(clean1, 50)

clean2 <- clean1[,-c(23:28)]

head(clean2, 50)

FinalNFL <- na.omit(clean2)
head(FinalNFL, 50)
# 3Steps of data cleaning to get our final set of data to be used

library(readxl) #Used to read data in via excel file
library(ggplot2) # Used to help creaate and organize both box and scatterplots
library(reshape2) # Used to plot and fit boxplots of all variables appropriately
library(leaps) # Used to help determine model's most significant variables
library(car) # Used as a part of the prediction function created

NFLPoints <- melt(data = FinalNFL)

ggplot(data = NFLPoints, aes(x=variable, y=value)) + geom_boxplot() +facet_wrap(~variable, scales = "free")
#Creates individual boxplots of each variable/statistic

ggplot(FinalNFL, aes(x=TotYds, y=PtsFor)) + geom_point(color="green")
ggplot(FinalNFL, aes(x=PTD, y=PtsFor)) + geom_point(color="blue")
ggplot(FinalNFL, aes(x=Pen, y=PtsFor)) + geom_point(color="red")
ggplot(FinalNFL, aes(x=YPA, y=PtsFor)) + geom_point(color="cyan")

# scatter plots of a few statistics to exemplify all variables present in the dataset are appropriate for a linear model/follow a linear trend.

options(scipen=4)
MLR <- lm(formula = PtsFor ~ ., data=FinalNFL)
summary(MLR)
#linear regression with all variables included

step <- step(MLR, direction = "both", test="F")
summary(step)
#step test to try and drop less significant variables from the model

randlm1 <- lm(formula = PtsFor ~ TotYds+Ply+TO+Att+PTD+RuTD+Pen+PenYds , data=FinalNFL)
summary(randlm1)

randlm2 <- lm(formula = PtsFor ~ TotYds+Ply+YPP+Tot1stD+Cmp+YPA+YPC , data=FinalNFL)
summary(randlm2)

randlm3 <- lm(formula = PtsFor ~ TotYds+Ply+PTD+RuTD+FL+Int+Pen , data=FinalNFL)
summary(randlm3)
#3 chosen at random models to be tested, by me using my own "football knowledge"

best <- regsubsets(PtsFor~., data=FinalNFL)
plot(best, scale="adjr2", main="Best-fit: Adjusted R square")

best.sum<- summary(best)
out.table <- data.frame(best.sum$outmat, best.sum$adjr2, best.sum$rss, best.sum$cp, best.sum$bic)
out.table
# 8 additional models created to see if any are as accurate yet more simple than any of the previous ones

BestModel <- lm(PtsFor ~ TotYds+FL+PTD+YPA+P1stD+RuTD+Ru1stD+Pen, data = FinalNFL)
summary(BestModel)

plot(BestModel)
# graphs that help with verifying MLR assumptions 1-3

dwt <- durbinWatsonTest(BestModel)
dwt
# test to check assumption 4 of MLR

PredPtsFor <- function(TotYds, FL, PTD, YPA, P1stD, RuTD, Ru1stD, Pen){
  inputs <- data.frame(TotYds, FL, PTD, YPA, P1stD, RuTD, Ru1stD, Pen)
  PointsPred <- predict(BestModel, newdata = inputs)
  PointsForNew <- as.integer(PointsPred)
  
  cat("Teams are projected to score on average",PointsForNew, "points per game this season.\n" )
  
  cat("Games are projected to have a total score of",2*PointsForNew, "points this season." )
  
  #function created that runs a regression using our chosen model upon plugging in values for our 8 significant statistics
}

PredPtsFor(300, .9, 1.6, 6.3, 13, 1.2, 7, 4.8)

PredPtsFor(360, .6, 1.9, 7.1, 15, 1.4, 9, 5.5)

PredPtsFor(347, .8, 2.1, 6.8, 14, 1.4, 10, 4)
LS0tCnRpdGxlOiAiTkZMIFNjb3JpbmcgSGlzdG9yeTogV2hhdCBBZmZlY3RzIEl0PyIKYXV0aG9yOiAiSmFtZXMgV2VpbmJsYXR0IgpkYXRlOiAiNC8yMi8yMDIxIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBudW1iZXJfc2VjdGlvbnM6IEZBTFNFCiAgICBkZl9wcmludDogcGFnZWQKICAgIHRoZW1lOiB1bml0ZWQKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKLS0tCiFbXShORkxQcm9qLnBuZyl7d2lkdGg9NTAlIGFsaWduPWNlbnRlcn0KCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IEFic3RyYWN0IDwvc3Bhbj4KClRoaXMgcmVzZWFyY2ggd2FzIGRpdmlzZWQgdG8gcHJvdmlkZSB0aGUgcmVhZGVyIHdpdGggaW5zaWdodCBhcyB0byB3aGljaCBvZmZlbnNpdmUgc3RhdGlzdGNpcyBhcmUgbW9zdCBzaWduaWZpY2FudCBpbiByZWxhdGlvbiB0byB0b3RhbCBvZmZlbnNpdmUgcG9pbnRzIHNjb3JlZCBieSBhIHRlYW0uIFRob3NlIHN0YXRpc3RpY3Mgd2lsbCB0aGVuIGJlIHVzZWQgYXMgc2lnbmlmaWNhbnQgdmFyaWFibGVzIGluIG9yZGVyIHRvIHByZWRpY3Qgb2ZmZW5zaXZlIHBvaW50IG91dHB1dHMgYnkgdGVhbXMgaW4gdGhlIHllYXJzIHRvIGNvbWUgdmlhIGEgY3JlYXRlZCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbC4KClVwb24gY3JlYXRpbmcgYSBtb2RlbCB0aGF0IHdhcyBub3Qgb25seSBhY2N1cmF0ZSBidXQgc2ltcGxpc3RpYyBlbm91Z2ggKHVzaW5nIGEgcmVhc29uYWJsZSBhbW91bnQgb2Ygc2lnbmlmaWNhbnQgc3RhdGlzdGljcyksIHdlIHRoZW4gY2hlY2tlZCB0byBtYWtlIHN1cmUgaXQgcGFzc2VkIGFsbCA1IGxpbmVhciByZWdyZXNzaW9uIGFzc3VtcHRpb25zLiBVcG9uIHBhc3NpbmcgdGhlIGFzc3VtcHRpb25zIHRoZSBtb2RlbCB3YXMgcmVhZHkgZm9yIHByZWRpY3Rpb24uIFRoZSA4IHNpZ25pZmljYW50IHZhcmlhYmxlcyBpbmNsdWRlZCBpbiBpdCB3ZXJlOiBUb3RZZHMgKHRvdGFsIHlhcmRzIG9mIG9mZmVuc2UpLCBGTCAoZnVtYmxlcyBsb3N0KSwgUFREIChwYXNzaW5nIHRvdWNoZG93bnMpLCBZUEEgKHlhcmRzIHBlciBwYXNzIGF0dGVtcHQpLCBQMXN0ZCAocGFzc2luZyBmaXJzdCBkb3ducyksIFJ1VEQgKHJ1c2hpbmcgdG91Y2hkb3ducyksIFJ1MXN0RCAocnVzaGluZyBmaXJzdCBkb3ducyksIGFuZCBQZW4gKHBlbmFsdGllcykuIFdoZW4gcHV0IGludG8gY29udGV4dCBpdCBtYWtlcyBzZW5zZSB0aGVzZSBhcmUgc29tZSBvZiB0aGUgbW9zdCBzaWduaWZpY2FudCB2YXJpYWJsZXMgYXMgdGhleSBhcmUgYWxsIGNyaXRpY2FsIHN0YXRpc3RpY3MgdGhhdCBlaXRoZXIgZGVwaWN0IHNpZ25zIG9mIGEganVnZ2VybmF1dCBvZmZlbnNlIHRoYXQgY2FuIGtlZXAgdGhlIGNoYWlucyBtb3Zpbmcgb3IgYSB3ZWFrIG9mZmVuc2UgdGhhdCBzdHJ1Z2dsZXMgdG8gZ2V0IGZpcnN0IGRvd25zIGFuZCBjb25zdGFudGx5IHR1cm5zIHRoZSBiYWxsIG92ZXIuIAoKV2l0aCB0aGlzIDggdmFyaWFibGUgbW9kZWwgd2Ugd2VyZSBhYmxlIHRvIGNvbnN0cnVjdCBhIGZ1bmN0aW9uIHRoYXQgdXBvbiBpbnB1dHRpbmcgdmFsdWVzIGZvciBlYWNoIG9mIHRoZXNlIDggc2lnbmlmaWNhbnQgdmFyaWFibGVzIHdhcyBhYmxlIHRvIGdpdmUgYSByb3VnaCBwcm9qZWN0aW9uIG9mIGF2ZXJhZ2UgcG9pbnRzIHNjb3JlZCBwZXIgdGVhbSBhcyB3ZWxsIGFzIGF2ZXJhZ2UgcG9pbnRzIHNjb3JlZCBwZXIgZ2FtZS4KCgojIyA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZTsiPiBJbnRyb2R1Y3Rpb24gPC9zcGFuPgoKCjxmb250IHNpemUgPSI1IiBjb2xvciA9ImJsdWUiPlByb2JsZW08L2ZvbnQ+CgpQb2ludHMgcGVyIGdhbWUgaW4gdGhlIE5GTCBoYXMgYmVlbiBvbiBhIHN0ZWFkeSB1cHdhcmQgdHJlbmQgYmFzaWNhbGx5IHNpbmNlIHRoZSBsZWFndWVzIGluY2VwdGlvbi4gV2hpbGUgaXQncyBlYXN5IHRvIHNheSB0aGF0ICJQbGF5ZXJzIGhhdmUganVzdCBiZWNvbWUgbW9yZSBza2lsbGVkIG5vdyB0aGFuIHRoZXkgd2VyZSBiYWNrIHRoZW4iIGFuZCB0aGVyZSBjZXJ0YWlubHkgY2FuIGJlIHNvbWUgdHJ1dGggdG8gdGhpcyBzdGF0ZW1lbnQsIHRoZXJlJ3MgZGVmaW50ZWx5IG11Y2ggbW9yZSB0byBpdC4gVGhlcmVmb3JlIHdlJ2xsIGJlIHRha2luZyBhIGRlZXAgZGl2ZSBpbnRvIE5GTCBvZmZlbnNpdmUgYXZlcmFnZXMgcGVyIGdhbWUgYnkgc2Vhc29uIHRvIGhvcGVmdWxseSB1bmNvdmVyIHdoaWNoIG9mIHRoZXNlIHN0YXRpc3RpY3MgaGF2ZSBoZWxwZWQgcGxheSB0aGUgYmlnZ2VzdCByb2xlIGluIHB1dHRpbmcgbW9yZSBwb2ludHMgdXAgb24gdGhlIHNjb3JlYm9hcmQgZm9yIHVzIHZpZXdlcnMuCgo8Zm9udCBzaXplID0iNSIgY29sb3IgPSJibHVlIj5Gb2N1czwvZm9udD4KClRvIGRpdmUgaW50byB0aGlzIGRhdGEgd2UnbGwgYmUgdXNpbmcgUHJvIEZvb3RiYWxsIFJlZmVyZW5jZSdzICBbVGVhbSBPZmZlbnNlIExlYWd1ZSBBdmVyYWdlcyBQZXIgVGVhbSBHYW1lXShodHRwczovL3d3dy5wcm8tZm9vdGJhbGwtcmVmZXJlbmNlLmNvbS95ZWFycy9ORkwvaW5kZXguaHRtKSBkYXRhc2V0IGZyb20gdGhlIHllYXJzIDE5NDktMjAyMCwgZm9yIGNvbXBsZXRlbmVzcyBvZiByZWNvcmRlZCBzdGF0aXN0aWNzLiBUaGUgc3RhdGlzdGljcyByYW5nZSBmcm9tIHRvdGFsIGNvbWJpbmVkIG9mZmVuc2l2ZSBzdGF0cyBwZXIgZ2FtZSwgdG8gcGFzc2luZy9ydXNoaW5nIHNwbGl0cy4gQWRkaXRpb25hbGx5IHN0YXRpc3RpY3MgcmVnYXJkaW5nIHR1cm5vdmVycyBhcyB3ZWxsIGFzIHBlbmFsdGllcyBhcmUgdGFrZW4gaW50byBhY2NvdW50LiBUaGVyZWZvcmUgdGhlcmUgd2lsbCBiZSBubyBzdGF0aXN0aWNhbCBzdG9uZSBsZWZ0IHVudHVybmVkIHRocm91Z2hvdXQgdGhpcyBhbmFseXNpcy4KCgoKPGZvbnQgc2l6ZSA9IjUiIGNvbG9yID0iYmx1ZSI+TWV0aG9kb2xvZ3k8L2ZvbnQ+CgpUbyBhbmFseXplIHRoaXMgZGF0YSB3ZSdsbCBiZSB1c2luZyBhIGNvbWJpbmF0aW9uIG9mIHZpc3VhbGl6YXRpb24gYXMgd2VsbCBhcyBzdGF0aXN0aWNhbCB0ZWNobmlxdWVzLiBXZSdsbCBiZSBwbG90dGluZyB0aGUgcHJpbWFyeSBzdGF0aXN0aWNzIHdpdGggYXZlcmdhZSBwb2ludHMgcGVyIGdhbWUgdG8gc2VlIGlmIHRoZXkgaGF2ZSBhbnkgZGlzdGluY3QgcG9zaXRpdmUgb3IgbmVnYXRpdmUgY29ycmVsYXRpb24gd2l0aCBlYWNoIG90aGVyLiBBZGRpdGlvbmFsbHkgd2Ugd2lsbCBiZSBydW5uaW5nIGEgcmVncmVzc2lvbiBvbiB0aGUgc3RhdGlzdGljYWwgZGF0YXNldCwgYW5kIHRoZW4gd2UnbGwgY3JlYXRlIGFuZCB0ZXN0IG11bHRpcGxlIG90aGVyIGZpdHMvbW9kZWxzIHRvIHNlZSBpZiB3ZSBhcmUgYWJsZSB0byBjb25zdHJ1Y3Qgb25lIHRoYXQgY29tYmluZXMgYWNjdXJhY3kgb2YgcHJlZGljdGlvbiBhcyB3ZWxsIGFzIHNpbXBsaWNpdHkgb2YgdmFyaWFibGVzIHVzZWQgaW50byBvbmUuIFRoZSB2YXJpYWJsZXMgb2Ygb3VyIGRhdGFzZXQgYXMgd2VsbCBhcyBhIGRlc2NyaXB0aW9uIG9mIHRoZW0gYXJlIGxpc3RlZCBiZWxvdy4KCjo6Ojo6Ojo6Ojo6Ojo6IHsuY29sdW1ucyBhbGlnbj1yaWdodCB0b3RhbHdpZHRoPTVlbX0KOjo6IHsuY29sdW1uIHdpZHRoPSIzMyUiIGFsaWduPWxlZnR9CgpWYXJpYWJsZSBOYW1lCgoqIFB0c0ZvcgoKKiBUb3QgWWRzCgoqIFBseQoKKiBZUFAKCiogVE8KCiogRkwKCiogVG90MXN0ZAoKKiBDbXAKCiogQXR0CgoqIFBZZHMKCiogUFRECgoqIEludAoKKiBZUEEKCiogUDFzdEQKCiogUnVBdHQKCiogUnVZZHMKCiogUnVURAoKKiBZUEMKCiogUnUxc3RECgoqIFBlbgoKKiBQZW5ZZHMKCiogMXN0UHkKOjo6Cjo6OnsuY29sdW1uIHdpZHRoPSIzMyUiIGFsaWduPWxlZnR9CgpWYXJpYWJsZSBUeXBlCgogIE51bWVyaWMKCiAgTnVtZXJpYwoKICBOdW1lcmljCgogIE51bWVyaWMKCiAgTnVtZXJpYwoKICBOdW1lcmljCgogIE51bWVyaWMKCiAgTnVtZXJpYwoKICBOdW1lcmljCgogIE51bWVyaWMKCiAgTnVtZXJpYwoKICBOdW1lcmljCgogIE51bWVyaWMKCiAgTnVtZXJpYwogIAogIE51bWVyaWMKCiAgTnVtZXJpYwoKICBOdW1lcmljCgogIE51bWVyaWMKCiAgTnVtZXJpYwoKICBOdW1lcmljCgogIE51bWVyaWMKICAKICBOdW1lcmljCjo6Ogo6Ojogey5jb2x1bW4gd2lkdGg9IjMzJSIgYWxpZ249bGVmdH0KClZhcmlhYmxlIERlc2NyaXB0aW9uCgpQb2ludHMgRm9yIChQZXIgdGVhbSkKClRvdGFsIFlkcyAoUnVzaCtSZWNlaXZpbmcpCgpUb3RhbCBQbGF5cyAoUGVyIHRlYW0pCgpZYXJkcyBQZXIgUGxheQoKVHVybm92ZXJzCgpGdW1ibGVzIExvc3QKClRvdGFsIEZpcnN0IERvd25zCgpDb21wbGV0aW9ucyAKClBhc3MgQXR0ZW1wdHMKClBhc3MgWWFyZHMKClBhc3MgVG91Y2hkb3ducwoKSW50ZXJjZXB0aW9ucwoKWWFyZHMgUGVyIEF0dGVtcHQgKFBhc3NpbmcpCgpQYXNzaW5nIDFzdCBEb3ducwoKUnVzaCBBdHRlbXB0cwoKUnVzaCBZYXJkcwoKUnVzaCBUb3VjaGRvd25zCgpZYXJkcyBQZXIgQ2FycnkKClJ1c2hpbmcgRmlyc3QgRG93bnMKClBlbmFsdGllcyBDb21taXR0ZWQgKEFjY2VwdGVkIGJ5IG9wcG9zaXRpb24pCgpQZW5hbHR5IFlhcmRhZ2UKClBlbmFsdGllcyBSZXN1bHRpbmcgaW4gRmlyc3QgRG93bnMKICAKOjo6Cjo6Ojo6Ojo6Ojo6Ojo6CgoKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IERhdGEgQ2xlYW5pbmcgPC9zcGFuPgoKYGBge3J9CgpsaWJyYXJ5KHJlYWR4bCkKTkZMIDwtIHJlYWRfeGxzeCgiTkZMRmluYWxQcm9qLnhsc3giLCBjb2xfbmFtZXMgPSBUUlVFLCBuYSA9ICJOQSIpCgpoZWFkKE5GTCwgNTApCgpgYGAKCgpUbyBiZWdpbiB3ZSdsbCByZW1vdmUgY29sdW1ucyAxLDIsIGFuZCAzIHJlc3BlY3RpdmVseSB3aGljaCBjb3JyZXNwb25kIHRvIHJhbmssIHllYXIsIGFuZCBudW1iZXIgb2YgdGVhbXMgaW4gdGhlIGxlYWd1ZS4gVGhlc2Ugc3RhdHMgYXJlIG9mIG5vIHVzZSB0byBvdXIgc2V0IGFzIHJhbmsgaGFzIG5vIGVmZmVjdCBoZXJlLCBpdCdzIGp1c3Qgb3JkZXJpbmcgdGhlIHllYXJzLiBBZGRpdGlvbmFsbHkgc2luY2Ugd2UgYXJlIGFuYWx5emluZyB3aGljaCBzdGF0aXN0aWNzIGNvcnJlc3BvbmQgdG8gbW9yZSBwb2ludHMgYmVpbmcgc2NvcmVkIG9uIGF2ZXJhZ2UgdGhlIHllYXIgaXMgb2Ygbm8gdXNlIHRvIHVzIGVpdGhlciwgYXMgaXQgcHJvdmlkZXMgbm8gZm9vdGJhbGwgc2lnbmlmaWNhbmNlLiBGaW5hbGx5IGFsdGhvdWdoIHRoZSBudW1iZXIgb2YgdGVhbXMgaW4gdGhlIGxlYWd1ZSBoYXMgZXhwYW5kZWQgb3ZlciB0aGUgeWVhcnMsIHdlIGFyZSBhbmFseXppbmcgbGVhZ3Vld2lkZSBkYXRhLCBzbyBhcyBsb25nIGFzIGFsbCB0ZWFtcyBhcmUgaW5jbHVkZWQgdGhlIHRvdGFsIG51bWJlciBpc24ndCBvZiBtdWNoIGNvbmNlcm4uCgoKYGBge3J9CgpjbGVhbjEgPC0gTkZMWywtYygxOjMpXQoKaGVhZChjbGVhbjEsIDUwKQoKYGBgCgoKTmV4dCB3ZSdsbCBiZSBvbWl0dGluZyBjb2x1bW5zIDIzLTI4IGFzIHRoZXkndmUgb25seSBiZWd1biByZWNvcmRpbmcgdGhlc2Ugc3RhdGlzdGljcyByZWdhcmRpbmcgZHJpdmUgbGVuZ3RoLCBzY29yaW5nIHBlcmNlbnRhZ2UsIHR1cm5vdmVyIHBlcmNlbnRhZ2UsIGF2ZXJhZ2UgbnVtYmVyIG9mIHBsYXlzLCBhdmVyYWdlIG51bWJlciBvZiB5YXJkcywgYW5kIGF2ZXJhZ2UgcG9pbnRzIHBlciBkcml2ZSBzaW5jZSByb3VnaGx5IDIwMDAuIEdpdmVuIHRoZSBkYXRhIGdvZXMgYmFjayB0byAxOTIyIHdlJ2QgYmUgbWlzc2luZyBvdXQgb24gYSBwbGV0aG9yYSBvZiB5ZWFycyBpbiBvdXIgc2FtcGxlLgoKCmBgYHtyfQpjbGVhbjIgPC0gY2xlYW4xWywtYygyMzoyOCldCgpoZWFkKGNsZWFuMiwgNTApCmBgYAoKClRvIGNvbnRpbnVlIHdlJ2xsIHJ1biBhIG5hLm9taXQgY29tbWFuZCBvbiBvdXIgcmVtYWluaW5nIGRhdGEsIHRvIGNsZWFuIG91dCBhbnkgcmVtYWluaW5nIE5BIHZhbHVlcyB0aGF0IGFyZSBzdGlsbCB3aXRoaW4gb3VyIGRhdGEuCgoKYGBge3J9CkZpbmFsTkZMIDwtIG5hLm9taXQoY2xlYW4yKQpoZWFkKEZpbmFsTkZMLCA1MCkKYGBgCgoKTm93IHRoYXQgYXJlIGRhdGEgaXMgZnVsbHkgY2xlYW5lZCB3ZSBzZWUgb3VyIGRhdGEgY29uc2lzdHMgb2YgdGhlIE5GTCBUZWFtIE9mZmVuc2UgTGVhZ3VlIEF2ZXJhZ2VzIFBlciBUZWFtIEdhbWUgZm9yIHRoZSBzZWFzb25zIDE5NDktMjAyMCwgdGh1cyB0aGUgZmluYWwgY2xlYW5pbmcgb2YgdGhlIGRhdGEgcmlkIHRoZSBzZXQgb2YgdGhlIHllYXJzIDE5MjItMTk0OCBkdWUgdG8gbGFjayBvZiBkYXRhIHJlZ2FyZGluZyB0aGVtLgoKTm9udGhlbGVzcyB3ZSBub3cgaGF2ZSA3MiB5ZWFycyBvZiBORkwgYXZlcmFnZSBvZmZlbnNpdmUgc3RhdGlzdGljcyB0byBmaW5kIG91dCB3aGljaCB2YXJpYWJsZXMgY29udHJpYnV0ZSBtb3N0IHRvIGEgdGVhbSBwdXR0aW5nIHVwIHBvaW50cy4KCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IFBhY2thZ2VzIFVzZWQgPC9zcGFuPiAKCgpgYGB7ciBldmFsPUZBTFNFfQoKbGlicmFyeShyZWFkeGwpICNVc2VkIHRvIHJlYWQgZGF0YSBpbiB2aWEgZXhjZWwgZmlsZQpsaWJyYXJ5KGdncGxvdDIpICMgVXNlZCB0byBoZWxwIGNyZWFhdGUgYW5kIG9yZ2FuaXplIGJvdGggYm94IGFuZCBzY2F0dGVycGxvdHMKbGlicmFyeShyZXNoYXBlMikgIyBVc2VkIHRvIHBsb3QgYW5kIGZpdCBib3hwbG90cyBvZiBhbGwgdmFyaWFibGVzIGFwcHJvcHJpYXRlbHkKbGlicmFyeShsZWFwcykgIyBVc2VkIHRvIGhlbHAgZGV0ZXJtaW5lIG1vZGVsJ3MgbW9zdCBzaWduaWZpY2FudCB2YXJpYWJsZXMKbGlicmFyeShjYXIpICMgVXNlZCBhcyBhIHBhcnQgb2YgdGhlIHByZWRpY3Rpb24gZnVuY3Rpb24gY3JlYXRlZAoKCmBgYAoKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgPC9zcGFuPiAKCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQoKTkZMUG9pbnRzIDwtIG1lbHQoZGF0YSA9IEZpbmFsTkZMKQoKZ2dwbG90KGRhdGEgPSBORkxQb2ludHMsIGFlcyh4PXZhcmlhYmxlLCB5PXZhbHVlKSkgKyBnZW9tX2JveHBsb3QoKSArZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikKCmBgYAoKQXMgd2UgY2FuIHNlZSBhbGwgdGhlIGRhdGEgaGVyZSBwcmVzZW50ZWQgd2l0aGluIHRoZSB2YXJpYWJsZXMgYXBwZWFycyB0byBiZSBub3JtYWxseSBkaXN0cmlidXRlZCB3aXRoIG9ubHkgYSBmZXcgb2YgdGhlbSBjb250YWluaW5nIGFuIG91dGxpZXIgb3IgdHdvLgoKYGBge3J9CmdncGxvdChGaW5hbE5GTCwgYWVzKHg9VG90WWRzLCB5PVB0c0ZvcikpICsgZ2VvbV9wb2ludChjb2xvcj0iZ3JlZW4iKQpnZ3Bsb3QoRmluYWxORkwsIGFlcyh4PVBURCwgeT1QdHNGb3IpKSArIGdlb21fcG9pbnQoY29sb3I9ImJsdWUiKQpnZ3Bsb3QoRmluYWxORkwsIGFlcyh4PVBlbiwgeT1QdHNGb3IpKSArIGdlb21fcG9pbnQoY29sb3I9InJlZCIpCmdncGxvdChGaW5hbE5GTCwgYWVzKHg9WVBBLCB5PVB0c0ZvcikpICsgZ2VvbV9wb2ludChjb2xvcj0iY3lhbiIpCmBgYAoKSXQgd291bGRuJ3QgYmUgZmVhc2libGUgdG8gc2hvdyBhIHNjYXR0ZXIgcGxvdCBmb3IgYWxsIDIxIHZhcmlhYmxlcyBhcyBpdCB3b3VsZCBvY2N1cHkgdG9vIG11Y2ggc3BhY2Ugb3IgdGhleSB3b3VsZCBhbGwgYmUgdG9vIHNtYWxsIHRvIGdldCBhIGZhaXIgdmlzdWFsIG9uLiBIb3dldmVyIGZyb20gdGhlc2UgZm91ciByYW5kb21seSBjaG9zZW4gdmFyaWFibGVzIGZyb20gdGhlIGRhdGFzZXQgeW91IGNhbiBzZWUgdGhhdCB0aGV5IGFsbCBmb2xsb3cgYSBsaW5lYXIgcGF0dGVybi90cmVuZCB0aHVzIHRoZXkgYXJlIGFwcHJvcHJpYXRlIHRvIHVzZSBpbiBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBUaGlzIGFkZGl0aW9uYWxseSBpcyBhIHByZWN1cnNvciBmb3IgdGhlIHJlc3Qgb2YgdGhlIHZhcmlhYmxlcyB0b28sIGFzIHRoZXkgYWxzbyBmb2xsb3cgbGluZWFyIHRyZW5kcy4gVGh1cyB3ZSBjYW4gc2FmZWx5IGF0dGVtcHQgdG8gdXNlIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdG8gZml0IHRoaXMgZGF0YS4KCjxmb250IHNpemUgPSI1IiBjb2xvciA9ImJsdWUiPkNyZWF0aW5nIHRoZSBNb2RlbDwvZm9udD4KClRvIGJlZ2luIHdlJ2xsIHJ1biBhIHJlZ3Jlc3Npb24gb24gb3VyIGRhdGEgdXNpbmcgYWxsIDIxIHZhcmlhYmxlcy4KCgpgYGB7cn0Kb3B0aW9ucyhzY2lwZW49NCkKTUxSIDwtIGxtKGZvcm11bGEgPSBQdHNGb3IgfiAuLCBkYXRhPUZpbmFsTkZMKQpzdW1tYXJ5KE1MUikKYGBgCgoKQWx0aG91Z2ggdGhpcyBtb2RlbCBwcm92aWRlcyBhY2N1cmF0ZSBwcmVkaWN0aW9ucywgYXMgZXZpZGVuY2VkIGJ5IHRoZSByLXNxdWFyZWQvYWRqdXN0ZWQgci1zcXVhcmVkIHZhbHVlcywgMjEgdmFyYWlhYmxlcyBmb3Igb25lIHJlZ3Jlc3Npb24gbW9kZWwgaXMgYSByYXRoZXIgbGFyZ2UgbnVtYmVyLiBUaHVzIHdlJ2xsIHJ1biBhIHN0ZXAgdGVzdCBvbiB0aGUgZGF0YSB0byBzZWUgaWYgd2UgY2FuIGZpbmQgYW55IG1vZGVscyB0aGF0IG1haW50YWluIGEgc2ltaWxhciBsZXZlbCBvZiBhY2N1cmFjeSB3aGlsZSB1c2luZyBhIHNtYWxsZXIgYW1vdW50IG9mIHNpZ25pZmljYW50IHZhcmlhYmxlcy4KCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpzdGVwIDwtIHN0ZXAoTUxSLCBkaXJlY3Rpb24gPSAiYm90aCIsIHRlc3Q9IkYiKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KHN0ZXApCmBgYAoKClRoZSBzdGVwIHRlc3Qgd2FzIGFibGUgdG8gcHJvdmlkZSB1cyB3aXRoIGEgbmVhcmx5IGlkZW50aWNhbCBtb2RlbCBhY2N1cmFjeSB3aXNlLCB3aGlsZSBvbmx5IHVzaW5nIDExIG9mIDIxIHZhcmlhYmxlcy4gSG93ZXZlciBmb3Igc2ltcGxpY2l0eSBwdXJwb3NlcyB3ZSB3aWxsIGNvbnRpbnVlIHRvIHRlc3QgZnVydGhlciB0byBzZWUgaWYgd2UgY2FuIGZpbmQgYSBtb2RlbCB0aGF0IG9uY2UgYWdhaW4gdXNlcyBsZXNzIHZhcmlhYmxlcy4gSG93ZXZlciwgdGhlIG1vZGVscyBsYWNrIG9mIHZhcmlhYmxlcyBkb2Vzbid0IGFsbG93IGZvciBtb2RlbCBhY2N1cmFjeSB0byBiZSBjb21wcm9taXNlZC4KClRoZXJlZm9yZSB0aGUgbmV4dCAzIG1vZGVscyBJIHJhbmRvbWx5IGNob3NlIHVzaW5nIG15IGtub3dsZWRnZSBvZiBmb290YmFsbCBJJ3ZlIG9idGFpbmVkIHRocm91Z2hvdXQgbXkgbWFueSB5ZWFycyBvZiB3YXRjaGluZywgdG8gc2VlIGlmIEkgY291bGQgY3JlYXRlIHRoZSBtb3N0IGFjY3VyYXRlIG1vZGVsIHlldC4KCgpgYGB7cn0KcmFuZGxtMSA8LSBsbShmb3JtdWxhID0gUHRzRm9yIH4gVG90WWRzK1BseStUTytBdHQrUFREK1J1VEQrUGVuK1BlbllkcyAsIGRhdGE9RmluYWxORkwpCnN1bW1hcnkocmFuZGxtMSkKCnJhbmRsbTIgPC0gbG0oZm9ybXVsYSA9IFB0c0ZvciB+IFRvdFlkcytQbHkrWVBQK1RvdDFzdEQrQ21wK1lQQStZUEMgLCBkYXRhPUZpbmFsTkZMKQpzdW1tYXJ5KHJhbmRsbTIpCgpyYW5kbG0zIDwtIGxtKGZvcm11bGEgPSBQdHNGb3IgfiBUb3RZZHMrUGx5K1BURCtSdVREK0ZMK0ludCtQZW4gLCBkYXRhPUZpbmFsTkZMKQpzdW1tYXJ5KHJhbmRsbTMpCmBgYAoKClVuZm9ydHVuYXRlbHkgYWxsIHRocmVlIG9mIHRoZXNlIG1vZGVscyByYW5kb21seSBjcmVhdGVkIGJ5IG1lIGhhZCBzaWduaWZpY2FudGx5IGxvd2VyIHItc3F1YXJlZCB2YWx1ZXMgdGhhbiB3aGF0IHdlJ3ZlIHNlZW4gaW4gdGhlIGluaXRpYWwgdHdvIG1vZGVscy4gVGh1cyB3ZSBtdXN0IGNvbnRpbnVlIHRvIHNlYXJjaCBmb3IgdGhlIGlkZWFsIG1vZGVsIGNvbWJpbmF0aW9uLgoKVXNpbmcgdGhlIGxlYXBzIHBhY2thZ2Ugd2UnbGwgaGF2ZSBhIGhhbmRmdWwgbW9yZSBtb2RlbHMgZ2l2ZW4gdG8gdXMsIGFsbG93aW5nIHVzIHRvIGFuYWx5emUgYW5kIGRlY2lkZSBpZiBhbnkgb2YgdGhlbSBoYXZlIGFuIGFkZXF1YXRlIGVub3VnaCBjb21iaW5hdGlvbiBvZiBhY2N1cmFjeSBhbmQgc2ltcGxpY2l0eSwgc28gd2UgY2FuIHVzZSBmb3Igb3VyIHByZWRpY3Rpb24gbW9kZWwuCgoKYGBge3J9CmxpYnJhcnkobGVhcHMpCmJlc3QgPC0gcmVnc3Vic2V0cyhQdHNGb3J+LiwgZGF0YT1GaW5hbE5GTCkKcGxvdChiZXN0LCBzY2FsZT0iYWRqcjIiLCBtYWluPSJCZXN0LWZpdDogQWRqdXN0ZWQgUiBzcXVhcmUiKQoKYmVzdC5zdW08LSBzdW1tYXJ5KGJlc3QpCm91dC50YWJsZSA8LSBkYXRhLmZyYW1lKGJlc3Quc3VtJG91dG1hdCwgYmVzdC5zdW0kYWRqcjIsIGJlc3Quc3VtJHJzcywgYmVzdC5zdW0kY3AsIGJlc3Quc3VtJGJpYykKb3V0LnRhYmxlCmBgYAoKCkxvb2tpbmcgdGhyb3VnaCBlYWNoIG9mIHRoZXNlIDggbW9kZWxzLCB3ZSBzZWUgdGhhdCBmaXQ4IGlzIHRoZSBiZXN0IG9mIHRoZSBncm91cC4gSXQgaGFzIGEgc2ltaWxhciByLXNxdWFyZWQgdG8gb3VyIGZpcnN0IHR3byBtb2RlbHMgdGVzdGVkLiBBZGRpdGlvbmFsbHksIGl0IHVzZXMgb25seSA4IG9mIDIxIHZhcmlhYmxlcyB3aXRoaW4gaXQuIFRodXMgd2Ugd2lsbCB0ZXN0L3N1bW1hcml6ZSB0aGlzIG1vZGVsIHRvIG1ha2Ugc3VyZSBpdCBpcyBmdWxseSBhZGVxdWF0ZSB0byBiZSB0ZXN0ZWQuCgoKYGBge3J9CkJlc3RNb2RlbCA8LSBsbShQdHNGb3IgfiBUb3RZZHMrRkwrUFREK1lQQStQMXN0RCtSdVREK1J1MXN0RCtQZW4sIGRhdGEgPSBGaW5hbE5GTCkKc3VtbWFyeShCZXN0TW9kZWwpCmBgYAoKCkFmdGVyIHJlYWRpbmcgdGhlIHN1bW1hcnkgd2Ugc2VlIHRoYXQgZXZlcnl0aGluZyBmcm9tIGZpdDgsIG5vdyB0aXRsZWQgQmVzdE1vZGVsLCBjaGVja2VkIG91dC4gVGhlcmVmb3JlIGl0IGlzIG5vdyB0aW1lIHRvIGNoZWNrIGFuZCBtYWtlIHN1cmUgdGhhdCB0aGlzIG1vZGVsIG1lZXRzIGFsbCB0aGUgYXNzdW1wdGlvbnMsIHNvIHRoYXQgaXQgaXMgYWxyaWdodCB0byBnbyBmb3J3YXJkIHdpdGggY3JlYXRpbmcgYSBwcmVkaWN0aW9uIG1vZGVsLgoKCmBgYHtyfQpwbG90KEJlc3RNb2RlbCkKYGBgCgo8Zm9udCBzaXplID0iNSIgY29sb3IgPSJibHVlIj5NZWV0aW5nIEFzc3VtcHRpb25zPC9mb250PgoKV2Ugbm93IG11c3QgbWFrZSBzdXJlIHRoYXQgb3VyIG1vZGVsIG1lZXRzIGFsbCA1IGFzc3VtcHRpb25zIG9mIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uLgoKKiBBc3N1bXB0aW9uIDEgLSB0aGUgcHJvYmFiaWxpdHkgZGlzdHJpYml0aW9uIG9mIHRoZSByYW5kb20gZXJyb3JzIChlcHNpbG9u4oCycykgaGFzIGEgbWVhbiBvZiAwLgogICogVGhlcmUgaXMgbm8gZGlyZWN0IHdheSB0byBjaGVjayB0aGlzIGNvbmRpdGlvbiwgdGhlIGxlYXN0IHNxdWFyZXMgcmVncmVzc2lvbiBlcXVhdGlvbiBlbnN1cmVzIHRoaXMgaXMgdHJ1ZSBmb3J0aGUgc2FtcGxlIGJ5IGl0cyBjYWxjdWxhdGlvbnMgLSB3ZSB3aWxsIG5lZWQgdG8gYXNzdW1lIHRoaXMgaXMgYW4gb2sgYXNzdW1wdGlvbiBmb3IgdGhlIHBvcHVsYXRpb24uCiAgCiogQXNzdW1wdGlvbiAyIC0gdGhlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmFuZG9tIGVycm9ycyAoZXBzaWxvbuKAsnMpIGhhcyBhIGNvbnN0YW50IHZhcmlhbmNlLgogICogVGhlIHJlc2lkdWFsIHZlcnN1cyBmaXQgcGxvdCBzaG93cyBhIHJhbmRvbSBzY2F0dGVyIHBhdHRlcm4gZXhjZXB0IGZvciBhIGZldyBkYXRhIHZhbHVlcy5JdCBtYXkgYmUgdGhhdCBvdXIgbW9kZWwgZG9lcyBub3QgZml0IGJlc3Qgd2l0aCB0aGVzZSB0eXBlcyBvZiBleHRyZW1lIHZhbHVlcy4gV2UgbWF5IHdhbnQgdG8gY29uc2lkZXIgc3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gMiBwb3B1bGF0aW9ucyB0aHVzIGNyZWF0aW5nIG1vZGVscyBmb3IgZWFjaC4gCiAgCiogQXNzdW1wdGlvbiAzOiB0aGUgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIG9mIHRoZSByYW5kb20gZXJyb3JzIChlcHNpbG9uKSBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4KICAqIFRoZSBub3JtYWwgcHJvYmFiaWxpdHkgcGxvdCBvZiB0aGUgcmVzaWR1YWxzIGFwcGVhcnMgdG8gZm9sbG93IGEgcmVsYXRpdmVseSBsaW5lYXIgcGF0aC4gV2UgZG9u4oCZdCBoYXZlIGVub3VnaGV2aWRlbmNlIHRvIHNheSB0aGUgcmFuZG9tIGVycm9ycyBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLgogIAoqIEFzc3VtcHRpb24gNDogdGhlIHJhbmRvbSBlcnJvcnMgKGVwc2lsb24pIGFyZSBpbmRlcGVuZGVudCAobGl0dGxlIHRvIG5vIGF1dG9jb3JyZWxhdGlvbikuCmBgYHtyfQpsaWJyYXJ5KGNhcikKZHd0IDwtIGR1cmJpbldhdHNvblRlc3QoQmVzdE1vZGVsKQpkd3QKYGBgCiBTaW5jZSB0aGUgcC12YWx1ZSAoMC4wNikgaXMgZ3JlYXRlciB0aGFuIGFueSByZWFzb25hYmxlIGFscGhhIGxldmVsLCB0aGVyZSBpcyBldmlkZW5jZSB0byBmYWlsIHRvIHJlamVjdCB0aGVudWxsIGh5cG90aGVzaXMuIFRoZXJlIGlzIGluc3VmZmljaWVudCBldmlkZW5jZSB0byBzdWdnZXN0IHRoYXQgdGhlcmUgaXMgYXV0b2NvcnJlbGF0aW9uLgogIAoqIEFzc3VtcHRpb24gNTogTGl0dGxlIHRvIG5vIG11bHRpY29sbGluZWFyaXR5LgogICogR2l2ZW4gdGhhdCBubyBwYWlyIG9mIHZhcmlhYmxlcyBzaGFyZSBhIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG92ZXIgLjgsIGluIGFkZGl0aW9uIHRvIGhhdmluZyBubyBWSUYgdmFsdWVzIG92ZXIgNSwgdGh1cyB0aGVyZSdzIG5vdCBzdWZmaWNpZW50IGV2aWRlbmNlIHRvIGluZGljYXRlIG11bHRpY29sbGluZWFyaXR5LgoKV2Ugc2VlIHRoYXQgb3VyIGNob3NlbiBtb2RlbCBoYXMgbWV0IGFsbCBhc3N1bXB0aW9ucyBhbmQgdGh1cyBpcyByZWFkeSB0byBiZSB1c2VkIGZvciBwcmVkaWN0aW9uISBUaGUgZnVuY3Rpb24gY3JlYXRlZCBiZWxvdyBoYXMgaW5wdXRzIG9mIHRoZSA4IHNpZ25pZmljYW50IHZhcmlhYmxlcy9wYXJhbWV0ZXJzIHVzZWQgYnkgdGhpcyBmaXQuIFRoZSBmdW5jdGlvbiBydW5zIGEgcmVncmVzc2lvbiB1c2luZyB0aGUgbW9kZWwgYW5kIGl0cyBpbnB1dHRlZCB2YWx1ZXMsIGFuZCB0aGUgb3V0cHV0IHByb3ZpZGVzIHVzIG5vdCBvbmx5IHdoYXQgdGhlIGF2ZXJhZ2UgdGVhbSB0b3RhbCBwZXIgZ2FtZSB3b3VsZCBiZSBpbiB0aGUgTkZMIHdpdGggdGhvc2UgaW5wdXRzLCBidXQgYWxzbyB3aGF0IHRoZSBlbnRpcmUgZ2FtZSB0b3RhbCBvZiBwb2ludHMgd291bGQgYmUgaWYgdGhvc2Ugd2VyZSB0aGUgc2Vhc29uIGF2ZXJhZ2VzIG9mIG91ciAKaW5wdXR0ZWQgc3RhdGlzdGljcyBhcyB3ZWxsLgoKCmBgYHtyfQpsaWJyYXJ5KGNhcikKUHJlZFB0c0ZvciA8LSBmdW5jdGlvbihUb3RZZHMsIEZMLCBQVEQsIFlQQSwgUDFzdEQsIFJ1VEQsIFJ1MXN0RCwgUGVuKXsKICBpbnB1dHMgPC0gZGF0YS5mcmFtZShUb3RZZHMsIEZMLCBQVEQsIFlQQSwgUDFzdEQsIFJ1VEQsIFJ1MXN0RCwgUGVuKQogIFBvaW50c1ByZWQgPC0gcHJlZGljdChCZXN0TW9kZWwsIG5ld2RhdGEgPSBpbnB1dHMpCiAgUG9pbnRzRm9yTmV3IDwtIGFzLmludGVnZXIoUG9pbnRzUHJlZCkKICAKICBjYXQoIlRlYW1zIGFyZSBwcm9qZWN0ZWQgdG8gc2NvcmUgb24gYXZlcmFnZSIsUG9pbnRzRm9yTmV3LCAicG9pbnRzIHBlciBnYW1lIHRoaXMgc2Vhc29uLlxuIiApCiAgCiAgY2F0KCJHYW1lcyBhcmUgcHJvamVjdGVkIHRvIGhhdmUgYSB0b3RhbCBzY29yZSBvZiIsMipQb2ludHNGb3JOZXcsICJwb2ludHMgdGhpcyBzZWFzb24uIiApCn0KCmBgYApgYGB7cn0KUHJlZFB0c0ZvcigzMDAsIC45LCAxLjYsIDYuMywgMTMsIDEuMiwgNywgNC44KQpgYGAKYGBge3J9ClByZWRQdHNGb3IoMzYwLCAuNiwgMS45LCA3LjEsIDE1LCAxLjQsIDksIDUuNSkKYGBgCmBgYHtyfQpQcmVkUHRzRm9yKDM0NywgLjgsIDIuMSwgNi44LCAxNCwgMS40LCAxMCwgNCkKYGBgCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjpibHVlOyI+IFN1bW1hcnkvQ29uY2x1c2lvbiA8L3NwYW4+IAoKPGZvbnQgc2l6ZSA9IjUiIGNvbG9yID0iYmx1ZSI+RmluZGluZ3M8L2ZvbnQ+CgpUbyBzdW1tYXJpemUgd2UgdG9vayBhIHNldCBvZiB0b3RhbCBsZWFndWUgb2ZmZW5zaXZlIHN0YXRpc3RpY3Mgb2YgdGhlIE5GTCBmcm9tIHRoZSB5ZWFycyAxOTIyLTIwMjAuIFdlIHRoZW4gY2xlYW5lZCB0aGlzIGRhdGFzZXQvbmFycm93ZWQgaXQgZG93biB0byB0aGUgc2Vhc29ucyBzcGFubmluZyAxOTQ5LTIwMjAuIFdpdGggdGhpcyBuZXdseSBjbGVhbmVkIGRhdGFzZXQgd2UgZW1iYXJrZWQgdG8gc2VlIHdoaWNoIG9mZmVuc2l2ZSByZWxhdGVkIHN0YXRpc3RpY3Mgd2VyZSBtb3N0IHNpZ25pZmljYW50IGluIGNhdXNpbmcgdGhlIGdyYWR1YWwgaW5jcmVhc2UgaW4gb2ZmZW5zaXZlIHBvaW50cyBzY29yZWQgcGVyIGdhbWUgaW4gdGhlIE5GTCB5ZWFyLW92ZXIteWVhci4gVG8gZG8gc28gd2UgY3JlYXRlZCBhbmQgdGVzdGVkIG51bWVyb3VzIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyB1bnRpbCB3ZSB3ZXJlIGFibGUgdG8gYXJyaXZlIGF0IGEgbW9kZWwgdGhhdCB3YXMgYWJsZSB0byBwZXJmZWN0bHkgYmFsYW5jZSBhY2N1cmFjeSB3aXRoIHNpbXBsaWNpdHkuIFRoZSBzaWduaWZpY2FudCB2YXJpYWJsZXMgaW4gdGhpcyBtb2RlbCB3ZXJlIFRvdFlkcywgRkwsIFBURCwgWVBBLCBQMXN0RCwgUnVURCwgUnUxc3RELCBhbmQgUGVuLiBCZWZvcmUgdXNpbmcgdGhpcyBtb2RlbCBmb3IgcHJlZGljdGlvbiwgYWxsIGFzc3VtcHRpb25zIHdlcmUgY2hlY2tlZCBhbmQgc3Vic2VxdWVudGx5IG1ldCwgYWxsb3dpbmcgdXMgdG8gcHJvY2VlZCB3aXRoIG91ciBwcmVkaWN0aW9uIHByb2Nlc3MgYXMgb3VyIG1vZGVsIHdhcyBkZWVtZWQgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gV2l0aCBhbGwgb2YgdGhpcyBpbiBtaW5kIHVzaW5nIHRoaXMgbW9kZWwgc2hvdWxkIGdpdmUgeW91IGEgcm91Z2ggaWRlYSBhYm91dCB3aGF0IHRoZSBvZmZlbnNpdmUgbGFuZHNjYXBlIGluIHRoZSBORkwgc2hvdWxkIGxvb2sgbGlrZSBmb3IgdGhlIHVwY29taW5nIHNlYXNvbihzKSBnaXZlbiB0aGUgdmFsdWVzIG9mIHRoZSBzdGF0aXN0aWNzIHRoYXQgYXJlIGlucHV0dGVkLgoKVGhpcyByZXNlYXJjaCBpc24ndCBuZWNlc3NhcmlseSBncm91bmRicmVha2luZyBvciB3b3JsZCBjaGFuZ2luZywgaG93ZXZlciwgaXQgY2VydGFpbmx5IGhhcyBhIG5pY2hlLiBXaGV0aGVyIGl0IGJlIGNvYWNoZXMgb3IgYW5hbHlzdHMgdXNpbmcgYSBtb2RlbCBsaWtlIHRoaXMgdG8gZGV0ZXJtaW5lIHdoYXQgYXJlYXMgYSB0ZWFtIG5lZWRzIHRvIHNob3JlIHVwIG9yIGltcHJvdmUgdXBvbiBpbiBvcmRlciB0byBwdXQgbW9yZSBwb2ludHMgdXAgb24gdGhlIGJvYXJkLiBPciBpZiBpdCdzIGp1c3QgYSBjYXN1YWwgZmFuIGlucHV0dGluZyBzdGF0cyBpbnRvIHRoZXNlIG9mIHRoZWlyIGZhdm9yaXRlIHRlYW0gdG8gc2VlIGhvdyBtYW55IHBvaW50cyB0aGV5J3JlIHByb2plY3RlZCB0byBzY29yZSB0aGlzIHdlZWsvaG93IG1hbnkgcG9pbnRzIHRoZSBvcHBvc2l0aW9uIGlzIHByb2plY3RlZCB0byBzY29yZSB0aGlzIHdlZWsuCgo8Zm9udCBzaXplID0iNSIgY29sb3IgPSJibHVlIj5XaGVyZSB0byBMb29rIE5leHQ/PC9mb250PgoKVGhlIG5leHQgYmVzdCwgYW5kIG1vc3QgcmVhbGlzdGljIHN0ZXAgdG8gdGFrZSB3aXRoIHRoaXMgZGF0YSB3b3VsZCBiZSB0byBydW4gc21hbGxlciBzYW1wbGUgc2l6ZWQgcmVncmVzc2lvbnMgdXNpbmcgc29tZSBvZiB0aGUgbW9yZSBhZHZhbmNlZCBzdGF0aXN0aWNzIHRoYXQgd2VyZSBpbnRyb2R1Y2VkIGluIHRoZSBsYXRlIDkwJ3MgdG8gdGhlIGRhdGEuIFRoZXNlIHN0YXRpc3RpY3MgaW5jbHVkZSBkcml2ZSBkdXJhdGlvbiwgYXZlcmFnZSBudW1iZXIgb2YgcGxheXMgcGVyIGRyaXZlLCBhdmVyYWdlIHRvdGFsIHlhcmRhZ2UgcGVyIGRyaXZlLCBhbmQgbXVjaCBtb3JlLiBJZiB0aGUgcmVncmVzc2lvbiBtb2RlbHMgY29uc2lzdGVudGx5IHNob3cgb3ZlciB0aGUgbmV4dCBmZXcgeWVhcnMgdGhhdCBhbnkgb2YgdGhlc2UgbW9kZWxzIGhhdmUgYSBncmVhdCBkZWFsIG9mIHNpZ25pZmljYW5jZSB0aGV5IGNvdWxkIHRoZW4gYmUgYWRkZWQgdG8gb3VyIGN1cnJlbnQgcHJlZGljdGlvbiBtb2RlbCB0byBlbmhhbmNlIGl0J3MgYWNjdXJhY3kgZXZlbiBtb3JlLgoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij4gUGVyc29uYWwgSW5mb3JtYXRpb24gPC9zcGFuPgoKCjo6Ojo6Ojo6Ojo6Ojo6IHsuY29sdW1ucyBhbGlnbj1yaWdodCB0b3RhbHdpZHRoPThlbX0KOjo6IHsuY29sdW1uIHdpZHRoPSI0MCUifQohW10oSU1HXzQzMjUuanBlZykKOjo6Cjo6OnsuY29sdW1uIHdpZHRoPSIxMCUiIGFsaWduPWNlbnRlcn0KOjo6Cjo6OiB7LmNvbHVtbiB3aWR0aD0iNTAlIiBhbGlnbj1sZWZ0fQogIEphbWVzIFdlaW5ibGF0dCBpcyBhIHNlbmlvciBNYXRoZW1hdGljYWwgU2NpZW5jZSBtYWpvciBoZXJlIGF0IENsZW1zb24gVW5pdmVyc2l0eS4gSGUnbGwgYmUgcmVjZXZpbmcgaGlzIGJhY2hlbG9yIG9mIHNjaWVuY2UgZGVncmVlIG5leHQgd2VlayBhdCBncmFkdWF0aW9uIGFuZCB0aGVuIGNvbnRpbnVlIGhpcyBzZWFyY2ggZm9yIGFuIGVtcGxveW1lbnQgb3Bwb3J0dW5pdHkgYmFjayBob21lIGluIE5ldyBZb3JrLiAKICAKICBUaGlzIHByb2plY3QgY29tYmluZXMgdHdvIG9mIEphbWVzJ3MgZmF2b3JpdGUgdGhpbmdzOyBzcG9ydHMgYXMgd2VsbCBhcyBhbmFseXppbmcgZGF0YS4gSGUgcGxhbnMgdG8gc2hvdyB0aGlzIHByb2plY3QgdG8gcG9zc2libGUgZW1wbG95ZXJzIHRvIGV4ZW1wbGlmeSBoaXMgY29kaW5nLyBhbmFseXNpcyBza2lsbHMuIEluIGhvcGVzIHRvIHJlY2VpdmUgYW4gZW50cnkgbGV2ZWwgcG9zaXRpb24gYXQgYSBjb21wYW55LgogIAogIEFkZGl0aW9uYWxseSBKYW1lcyBob3BlcyB0aGF0IGFueW9uZSBlbHNlIHdobyBoYXBwZW5zIHRvIGJlIGxvb2tpbmcgYXQgdGhpcyBwcm9qZWN0IGVuam95cyBpdHMgY29udGVudHMgYW5kIGlzIGFibGUgdG8gbGVhcm4gc29tZXRoaW5nIHRoZXkgZmluZCB2YWx1YWJsZSB3aXRoaW4gaXQuCjo6Ogo6Ojo6Ojo6Ojo6Ojo6OgoKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWU7Ij4gUmVmZXJlbmNlcyA8L3NwYW4+IAoKPGh0dHBzOi8vd3d3LnByby1mb290YmFsbC1yZWZlcmVuY2UuY29tL3llYXJzL05GTC9pbmRleC5odG0+CgojIyA8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZTsiPiBGdWxsIENvZGUgPC9zcGFuPiAKCmBgYHtyIGV2YWwgPSBGQUxTRX0gCmxpYnJhcnkocmVhZHhsKQpORkwgPC0gcmVhZF94bHN4KCJORkxGaW5hbFByb2oueGxzeCIsIGNvbF9uYW1lcyA9IFRSVUUsIG5hID0gIk5BIikKI1JlYWRpbmcgZGF0YSBpbnRvIFIKaGVhZChORkwsIDUwKQoKY2xlYW4xIDwtIE5GTFssLWMoMTozKV0KCmhlYWQoY2xlYW4xLCA1MCkKCmNsZWFuMiA8LSBjbGVhbjFbLC1jKDIzOjI4KV0KCmhlYWQoY2xlYW4yLCA1MCkKCkZpbmFsTkZMIDwtIG5hLm9taXQoY2xlYW4yKQpoZWFkKEZpbmFsTkZMLCA1MCkKIyAzU3RlcHMgb2YgZGF0YSBjbGVhbmluZyB0byBnZXQgb3VyIGZpbmFsIHNldCBvZiBkYXRhIHRvIGJlIHVzZWQKCmxpYnJhcnkocmVhZHhsKSAjVXNlZCB0byByZWFkIGRhdGEgaW4gdmlhIGV4Y2VsIGZpbGUKbGlicmFyeShnZ3Bsb3QyKSAjIFVzZWQgdG8gaGVscCBjcmVhYXRlIGFuZCBvcmdhbml6ZSBib3RoIGJveCBhbmQgc2NhdHRlcnBsb3RzCmxpYnJhcnkocmVzaGFwZTIpICMgVXNlZCB0byBwbG90IGFuZCBmaXQgYm94cGxvdHMgb2YgYWxsIHZhcmlhYmxlcyBhcHByb3ByaWF0ZWx5CmxpYnJhcnkobGVhcHMpICMgVXNlZCB0byBoZWxwIGRldGVybWluZSBtb2RlbCdzIG1vc3Qgc2lnbmlmaWNhbnQgdmFyaWFibGVzCmxpYnJhcnkoY2FyKSAjIFVzZWQgYXMgYSBwYXJ0IG9mIHRoZSBwcmVkaWN0aW9uIGZ1bmN0aW9uIGNyZWF0ZWQKCk5GTFBvaW50cyA8LSBtZWx0KGRhdGEgPSBGaW5hbE5GTCkKCmdncGxvdChkYXRhID0gTkZMUG9pbnRzLCBhZXMoeD12YXJpYWJsZSwgeT12YWx1ZSkpICsgZ2VvbV9ib3hwbG90KCkgK2ZhY2V0X3dyYXAofnZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZSIpCiNDcmVhdGVzIGluZGl2aWR1YWwgYm94cGxvdHMgb2YgZWFjaCB2YXJpYWJsZS9zdGF0aXN0aWMKCmdncGxvdChGaW5hbE5GTCwgYWVzKHg9VG90WWRzLCB5PVB0c0ZvcikpICsgZ2VvbV9wb2ludChjb2xvcj0iZ3JlZW4iKQpnZ3Bsb3QoRmluYWxORkwsIGFlcyh4PVBURCwgeT1QdHNGb3IpKSArIGdlb21fcG9pbnQoY29sb3I9ImJsdWUiKQpnZ3Bsb3QoRmluYWxORkwsIGFlcyh4PVBlbiwgeT1QdHNGb3IpKSArIGdlb21fcG9pbnQoY29sb3I9InJlZCIpCmdncGxvdChGaW5hbE5GTCwgYWVzKHg9WVBBLCB5PVB0c0ZvcikpICsgZ2VvbV9wb2ludChjb2xvcj0iY3lhbiIpCgojIHNjYXR0ZXIgcGxvdHMgb2YgYSBmZXcgc3RhdGlzdGljcyB0byBleGVtcGxpZnkgYWxsIHZhcmlhYmxlcyBwcmVzZW50IGluIHRoZSBkYXRhc2V0IGFyZSBhcHByb3ByaWF0ZSBmb3IgYSBsaW5lYXIgbW9kZWwvZm9sbG93IGEgbGluZWFyIHRyZW5kLgoKb3B0aW9ucyhzY2lwZW49NCkKTUxSIDwtIGxtKGZvcm11bGEgPSBQdHNGb3IgfiAuLCBkYXRhPUZpbmFsTkZMKQpzdW1tYXJ5KE1MUikKI2xpbmVhciByZWdyZXNzaW9uIHdpdGggYWxsIHZhcmlhYmxlcyBpbmNsdWRlZAoKc3RlcCA8LSBzdGVwKE1MUiwgZGlyZWN0aW9uID0gImJvdGgiLCB0ZXN0PSJGIikKc3VtbWFyeShzdGVwKQojc3RlcCB0ZXN0IHRvIHRyeSBhbmQgZHJvcCBsZXNzIHNpZ25pZmljYW50IHZhcmlhYmxlcyBmcm9tIHRoZSBtb2RlbAoKcmFuZGxtMSA8LSBsbShmb3JtdWxhID0gUHRzRm9yIH4gVG90WWRzK1BseStUTytBdHQrUFREK1J1VEQrUGVuK1BlbllkcyAsIGRhdGE9RmluYWxORkwpCnN1bW1hcnkocmFuZGxtMSkKCnJhbmRsbTIgPC0gbG0oZm9ybXVsYSA9IFB0c0ZvciB+IFRvdFlkcytQbHkrWVBQK1RvdDFzdEQrQ21wK1lQQStZUEMgLCBkYXRhPUZpbmFsTkZMKQpzdW1tYXJ5KHJhbmRsbTIpCgpyYW5kbG0zIDwtIGxtKGZvcm11bGEgPSBQdHNGb3IgfiBUb3RZZHMrUGx5K1BURCtSdVREK0ZMK0ludCtQZW4gLCBkYXRhPUZpbmFsTkZMKQpzdW1tYXJ5KHJhbmRsbTMpCiMzIGNob3NlbiBhdCByYW5kb20gbW9kZWxzIHRvIGJlIHRlc3RlZCwgYnkgbWUgdXNpbmcgbXkgb3duICJmb290YmFsbCBrbm93bGVkZ2UiCgpiZXN0IDwtIHJlZ3N1YnNldHMoUHRzRm9yfi4sIGRhdGE9RmluYWxORkwpCnBsb3QoYmVzdCwgc2NhbGU9ImFkanIyIiwgbWFpbj0iQmVzdC1maXQ6IEFkanVzdGVkIFIgc3F1YXJlIikKCmJlc3Quc3VtPC0gc3VtbWFyeShiZXN0KQpvdXQudGFibGUgPC0gZGF0YS5mcmFtZShiZXN0LnN1bSRvdXRtYXQsIGJlc3Quc3VtJGFkanIyLCBiZXN0LnN1bSRyc3MsIGJlc3Quc3VtJGNwLCBiZXN0LnN1bSRiaWMpCm91dC50YWJsZQojIDggYWRkaXRpb25hbCBtb2RlbHMgY3JlYXRlZCB0byBzZWUgaWYgYW55IGFyZSBhcyBhY2N1cmF0ZSB5ZXQgbW9yZSBzaW1wbGUgdGhhbiBhbnkgb2YgdGhlIHByZXZpb3VzIG9uZXMKCkJlc3RNb2RlbCA8LSBsbShQdHNGb3IgfiBUb3RZZHMrRkwrUFREK1lQQStQMXN0RCtSdVREK1J1MXN0RCtQZW4sIGRhdGEgPSBGaW5hbE5GTCkKc3VtbWFyeShCZXN0TW9kZWwpCgpwbG90KEJlc3RNb2RlbCkKIyBncmFwaHMgdGhhdCBoZWxwIHdpdGggdmVyaWZ5aW5nIE1MUiBhc3N1bXB0aW9ucyAxLTMKCmR3dCA8LSBkdXJiaW5XYXRzb25UZXN0KEJlc3RNb2RlbCkKZHd0CiMgdGVzdCB0byBjaGVjayBhc3N1bXB0aW9uIDQgb2YgTUxSCgpQcmVkUHRzRm9yIDwtIGZ1bmN0aW9uKFRvdFlkcywgRkwsIFBURCwgWVBBLCBQMXN0RCwgUnVURCwgUnUxc3RELCBQZW4pewogIGlucHV0cyA8LSBkYXRhLmZyYW1lKFRvdFlkcywgRkwsIFBURCwgWVBBLCBQMXN0RCwgUnVURCwgUnUxc3RELCBQZW4pCiAgUG9pbnRzUHJlZCA8LSBwcmVkaWN0KEJlc3RNb2RlbCwgbmV3ZGF0YSA9IGlucHV0cykKICBQb2ludHNGb3JOZXcgPC0gYXMuaW50ZWdlcihQb2ludHNQcmVkKQogIAogIGNhdCgiVGVhbXMgYXJlIHByb2plY3RlZCB0byBzY29yZSBvbiBhdmVyYWdlIixQb2ludHNGb3JOZXcsICJwb2ludHMgcGVyIGdhbWUgdGhpcyBzZWFzb24uXG4iICkKICAKICBjYXQoIkdhbWVzIGFyZSBwcm9qZWN0ZWQgdG8gaGF2ZSBhIHRvdGFsIHNjb3JlIG9mIiwyKlBvaW50c0Zvck5ldywgInBvaW50cyB0aGlzIHNlYXNvbi4iICkKICAKICAjZnVuY3Rpb24gY3JlYXRlZCB0aGF0IHJ1bnMgYSByZWdyZXNzaW9uIHVzaW5nIG91ciBjaG9zZW4gbW9kZWwgdXBvbiBwbHVnZ2luZyBpbiB2YWx1ZXMgZm9yIG91ciA4IHNpZ25pZmljYW50IHN0YXRpc3RpY3MKfQoKUHJlZFB0c0ZvcigzMDAsIC45LCAxLjYsIDYuMywgMTMsIDEuMiwgNywgNC44KQoKUHJlZFB0c0ZvcigzNjAsIC42LCAxLjksIDcuMSwgMTUsIDEuNCwgOSwgNS41KQoKUHJlZFB0c0ZvcigzNDcsIC44LCAyLjEsIDYuOCwgMTQsIDEuNCwgMTAsIDQpCmBgYAo=