We are going to investigate polynomial regression today. To start, we first define what polynomial regression is, \[ y = \beta_0 + \beta_1 x + \beta_2 x^2 + \beta_3 x^3 + \cdots + \beta_p x^p + \epsilon. \] The greater the degree of the polynomial \(p\) is, the richer the model is. However, it may not be one’s best interest to keep increasing the degree of the polynomial

Polynomial Regression

We will use the 04cars dataset, which is the 2004 New Car and Truck Data, available from http://ww2.amstat.org/publications/jse/jse_data_archive.htm. For convenience, the file 04cars.rda is available for download. The .rda file has been cleaned and prepared for direct application in R. Please make sure you have the data file stored in the working directory before continuing.

The following objects are masked from dat (pos = 3):

    All-Wheel_Drive, City_MPG, Dealer_Cost, Engine_Size,
    Highway_MPG, Horsepower, Length, Make_Model, Minivan,
    Number_of_Cylinders, Pickup, Rear-Wheel_Drive, Retail_Price,
    SportsCar, SportUtility, Wagon, Weight, Wheel_Base, Width

The following objects are masked from dat (pos = 4):

    All-Wheel_Drive, City_MPG, Dealer_Cost, Engine_Size,
    Highway_MPG, Horsepower, Length, Make_Model, Minivan,
    Number_of_Cylinders, Pickup, Rear-Wheel_Drive, Retail_Price,
    SportsCar, SportUtility, Wagon, Weight, Wheel_Base, Width

We will be focusing on two of the variables only, Retail_Price and Horsepower. Let’s take a look at the data points.

rr

plot(Horsepower, Retail_Price, pch=16)

The scatterplot matches with our intuition, as we expect the retail price of a vehicle to be higher if it has a larger horsepower. However, we may not be certain whether this is a linear relationship or a quadratic relationship.

In such case, our remedy is polynomial regression. To start, we will first look at a degree 1 and 2 examples.

The degree 1 polynomial fit is exactly the same linear regression that we have studied two weeks ago. The degree 2 polynomial is a quandratic fit to the data. The quadratic fit is slightly better one can argue. In fact, we can verify the claim using \(R^2\) of the 2 fits.

rr

summary(fit.d1)$r.squared
[1] 0.683838

rr

summary(fit.d2)$r.squared
[1] 0.7240974

However, as we talked about earlier, \(R^2\) is not a good measure, when we compare two models of different model complexity. In fact, if one increases the degree of polynomial, \(R^2\) increases as well.

We can also take a look at the actual fits of the polynomials ranging from degree 1 to 10.

rr

plot(Horsepower, Retail_Price, pch = 16)
pts <- seq(0, 600, length.out=100)
for (d in 1:10){
    fit <- lm(Retail_Price ~ poly(Horsepower, d, raw=TRUE))
    val <- predict(fit, data.frame(Horsepower = pts))
    lines(pts, val, col=rainbow(10)[d], lwd = 2)
}

Obviously, this shows that even though polynomial models of higher degree return larger \(R^2\) for the fit, the higher degree model will overfit the data. One can see that the high degree polynomials are very volatile. It does perform well in fitting the data we have at hand, but it will make large errors in predicting data not currently in the dataset.

So how do we choose the degree of our polynomial regression? A common approach is to stop at a low degree which \(R^2\) does not increase much further. For example, from our example, we see that \(R^2\) increase from about 0.68 to over 0.72 at degree 2, which is actually the biggest single step gain. But then one might also argue that degree 8 has achieved 0.76 in \(R^2\). If we take a look at degree 8 polynomial below, we can see that it is apparently overfitting.

Model Selection

The degree selection may sound vague. Trying to determine it graphically is sometimes hard. There are other methods in determining the right model. Here we will take cross validation for example, and go over the concept step by step.

The term cross validation usually comes with a parameter. A \(k\)-fold cross validation is one that divides the available dataset into \(k\) piles. If a dataset has \(n\) points, then each pile has \(n/k\) data points, assuming there’s no remainders. In our case, \(n = 428\), common choices for \(k\) are 5 or 10. Let’s do a 5-fold cross validation.

Each round, one pile is going to be used as the validation set, and the rest being training set. Training set consists of the ones we use in fitting, while we use validation set to evaluate the model in each round. Rotating through all the \(k=5\) piles gives us \(k=5\) rounds in total, which also gives us \(k=5\) evaluations of the model we have chosen. Usually an average is taken as the final measure of how well the model performs. Let’s implement this in R for the linear model.

rr

k <- 5
n <- 428
val.size <- floor(n/k)
cv.mse <- rep(0, k)
for (round in 1:k){
  if (round == k){
    val.ind <- ((round-1)*val.size + 1):n
  }else{
    val.ind <- ((round-1)*val.size + 1):(round*val.size)
  }
  y <- Retail_Price[-val.ind]
  x <- Horsepower[-val.ind]
  fit <- lm(y ~ x)
  y.hat <- predict(fit, data.frame(x = Horsepower[val.ind]))
  cv.mse[round] <- sum((Retail_Price[val.ind] - y.hat)^2)/length(val.ind)
}
mean(cv.mse)
[1] 132433176

Notice that we took mean squared error (MSE) as our evaluation criterion. Now let’s see how the measure changes with the degree of polynomials. We first put the cross validation procedure into a function.

rr

cv.degree.d <- function(k, n, d){
  val.size <- floor(n/k)
  cv.mse <- rep(0, k)
  for (round in 1:k){
    if (round == k){
      val.ind <- ((round-1)*val.size + 1):n
    }else{
      val.ind <- ((round-1)*val.size + 1):(round*val.size)
    }
    y <- Retail_Price[-val.ind]
    x <- Horsepower[-val.ind]
    fit <- lm(y ~ poly(x, d, raw=TRUE))
    y.hat <- predict(fit, data.frame(x = Horsepower[val.ind]))
    cv.mse[round] <- sum((Retail_Price[val.ind] - y.hat)^2)/length(val.ind)
  }
  return (mean(cv.mse))
}

We now have the necessary tools to take a look at the MSE of polynomial model of degrees from 1 to 9.

As shown with the plot, the cross validation MSE achieves minimum at degree 3. In other words, we should stop at degree 3 polynomial as the optimal degree. Increasing the degree further leads to overfitting.

This is known as the mean variance tradeoff. Assume the underlying model takes the form \[ y = f(x) + \epsilon, \] where \(\epsilon\) has mean 0 and variance \(\sigma^2\). Then if we fit a model with \(\hat f(x)\). We have the mean squared error as the following, \[\mathbb{E} \left[ \left( y - \hat f(x) \right)^2 \right] = \mathbb{E} \left[ \left( f(x) + \epsilon - \hat f(x) \right)^2 \right] = \mathbb{E} \left[ \left( f(x) - \hat f(x) \right)^2 + \epsilon \left( f(x) - \hat f(x) \right) + \epsilon^2 \right] = \text{Var} \left( \hat f(x) \right) + \left[ \mathbb{E} \left( f(x) - \hat f(x) \right) \right]^2 + \sigma^2.\] The part \(\text{Var}\left( \hat f(x) \right)\) is the variance of our fitted model, whereas the \(\left[ \mathbb{E} \left( f(x) - \hat f(x) \right) \right]^2\) is the bias squared. The \(\sigma^2\) is the irreducible error. The more complex the model \(\hat f(x)\) is, it will be able to reduce the bias better. However, the more complex model will lead to a larger variance.

LS0tCnRpdGxlOiAiTWF0aCAxODkvMjg5IExhYiA5IgphdXRob3I6ICJKaWFxaSBHdW8iCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQogIApXZSBhcmUgZ29pbmcgdG8gaW52ZXN0aWdhdGUgcG9seW5vbWlhbCByZWdyZXNzaW9uIHRvZGF5LiBUbyBzdGFydCwgd2UgZmlyc3QgZGVmaW5lIHdoYXQgcG9seW5vbWlhbCByZWdyZXNzaW9uIGlzLAokJCB5ID0gXGJldGFfMCArIFxiZXRhXzEgeCArIFxiZXRhXzIgeF4yICsgXGJldGFfMyB4XjMgKyBcY2RvdHMgKyBcYmV0YV9wIHhecCArIFxlcHNpbG9uLiAkJApUaGUgZ3JlYXRlciB0aGUgZGVncmVlIG9mIHRoZSBwb2x5bm9taWFsICRwJCBpcywgdGhlIHJpY2hlciB0aGUgbW9kZWwgaXMuIEhvd2V2ZXIsIGl0IG1heSBub3QgYmUgb25lJ3MgYmVzdCBpbnRlcmVzdCB0byBrZWVwIGluY3JlYXNpbmcgdGhlIGRlZ3JlZSBvZiB0aGUgcG9seW5vbWlhbAoKIyBQb2x5bm9taWFsIFJlZ3Jlc3Npb24KCldlIHdpbGwgdXNlIHRoZSAqMDRjYXJzKiBkYXRhc2V0LCB3aGljaCBpcyB0aGUgMjAwNCBOZXcgQ2FyIGFuZCBUcnVjayBEYXRhLCBhdmFpbGFibGUgZnJvbSBodHRwOi8vd3cyLmFtc3RhdC5vcmcvcHVibGljYXRpb25zL2pzZS9qc2VfZGF0YV9hcmNoaXZlLmh0bS4gRm9yIGNvbnZlbmllbmNlLCB0aGUgZmlsZSAqMDRjYXJzLnJkYSogaXMgYXZhaWxhYmxlIGZvciBkb3dubG9hZC4gVGhlICoucmRhKiBmaWxlIGhhcyBiZWVuIGNsZWFuZWQgYW5kIHByZXBhcmVkIGZvciBkaXJlY3QgYXBwbGljYXRpb24gaW4gUi4gUGxlYXNlIG1ha2Ugc3VyZSB5b3UgaGF2ZSB0aGUgZGF0YSBmaWxlIHN0b3JlZCBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkgYmVmb3JlIGNvbnRpbnVpbmcuIApgYGB7cn0KbG9hZCgiMDRjYXJzLnJkYSIpCmhlYWQoZGF0KQphdHRhY2goZGF0KQpgYGAKV2Ugd2lsbCBiZSBmb2N1c2luZyBvbiB0d28gb2YgdGhlIHZhcmlhYmxlcyBvbmx5LCAqUmV0YWlsX1ByaWNlKiBhbmQgKkhvcnNlcG93ZXIqLiBMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YSBwb2ludHMuCmBgYHtyfQpwbG90KEhvcnNlcG93ZXIsIFJldGFpbF9QcmljZSwgcGNoPTE2KQpgYGAKVGhlIHNjYXR0ZXJwbG90IG1hdGNoZXMgd2l0aCBvdXIgaW50dWl0aW9uLCBhcyB3ZSBleHBlY3QgdGhlIHJldGFpbCBwcmljZSBvZiBhIHZlaGljbGUgdG8gYmUgaGlnaGVyIGlmIGl0IGhhcyBhIGxhcmdlciBob3JzZXBvd2VyLiBIb3dldmVyLCB3ZSBtYXkgbm90IGJlIGNlcnRhaW4gd2hldGhlciB0aGlzIGlzIGEgbGluZWFyIHJlbGF0aW9uc2hpcCBvciBhIHF1YWRyYXRpYyByZWxhdGlvbnNoaXAuIAoKSW4gc3VjaCBjYXNlLCBvdXIgcmVtZWR5IGlzIHBvbHlub21pYWwgcmVncmVzc2lvbi4gVG8gc3RhcnQsIHdlIHdpbGwgZmlyc3QgbG9vayBhdCBhIGRlZ3JlZSAxIGFuZCAyIGV4YW1wbGVzLgpgYGB7cn0KZml0LmQxIDwtIGxtKFJldGFpbF9QcmljZSB+IEhvcnNlcG93ZXIpCmZpdC5kMiA8LSBsbShSZXRhaWxfUHJpY2UgfiBwb2x5KEhvcnNlcG93ZXIsIDIsIHJhdz1UUlVFKSkKcHRzIDwtIHNlcSgwLCA2MDAsIGxlbmd0aC5vdXQ9MTAwKQp2YWwuZDEgPC0gcHJlZGljdChmaXQuZDEsIGRhdGEuZnJhbWUoSG9yc2Vwb3dlcj1wdHMpKQp2YWwuZDIgPC0gcHJlZGljdChmaXQuZDIsIGRhdGEuZnJhbWUoSG9yc2Vwb3dlcj1wdHMpKQpwbG90KEhvcnNlcG93ZXIsIFJldGFpbF9QcmljZSwgcGNoPTE2KQpsaW5lcyhwdHMsIHZhbC5kMSwgY29sPSJibHVlIiwgbHdkPTIpCmxpbmVzKHB0cywgdmFsLmQyLCBjb2w9InJlZCIsIGx3ZD0yKQpgYGAKVGhlIGRlZ3JlZSAxIHBvbHlub21pYWwgZml0IGlzIGV4YWN0bHkgdGhlIHNhbWUgbGluZWFyIHJlZ3Jlc3Npb24gdGhhdCB3ZSBoYXZlIHN0dWRpZWQgdHdvIHdlZWtzIGFnby4gVGhlIGRlZ3JlZSAyIHBvbHlub21pYWwgaXMgYSBxdWFuZHJhdGljIGZpdCB0byB0aGUgZGF0YS4gVGhlIHF1YWRyYXRpYyBmaXQgaXMgc2xpZ2h0bHkgYmV0dGVyIG9uZSBjYW4gYXJndWUuIEluIGZhY3QsIHdlIGNhbiB2ZXJpZnkgdGhlIGNsYWltIHVzaW5nICRSXjIkIG9mIHRoZSAyIGZpdHMuCmBgYHtyfQpzdW1tYXJ5KGZpdC5kMSkkci5zcXVhcmVkCnN1bW1hcnkoZml0LmQyKSRyLnNxdWFyZWQKYGBgCgpIb3dldmVyLCBhcyB3ZSB0YWxrZWQgYWJvdXQgZWFybGllciwgJFJeMiQgaXMgbm90IGEgZ29vZCBtZWFzdXJlLCB3aGVuIHdlIGNvbXBhcmUgdHdvIG1vZGVscyBvZiBkaWZmZXJlbnQgbW9kZWwgY29tcGxleGl0eS4gSW4gZmFjdCwgaWYgb25lIGluY3JlYXNlcyB0aGUgZGVncmVlIG9mIHBvbHlub21pYWwsICRSXjIkIGluY3JlYXNlcyBhcyB3ZWxsLgpgYGB7cn0KZC5tYXggPSAzMApSLnNxdWFyZSA9IHJlcChOQSwgZC5tYXgpCmZvciAoZCBpbiAxOmQubWF4KXsKCWZpdCA9IGxtKFJldGFpbF9QcmljZSB+IHBvbHkoSG9yc2Vwb3dlciwgZCwgcmF3PVRSVUUpKQoJUi5zcXVhcmVbZF0gPSBzdW1tYXJ5KGZpdCkkci5zcXVhcmVkCn0KcGxvdCgxOmQubWF4LCBSLnNxdWFyZSwgeGxhYiA9ICJEZWdyZWUiLCB5bGFiID0gIlItc3F1YXJlZCIsIGx3ZCA9IDIsIGNvbCA9ICJibHVlIiwgcGNoID0gNSkKbGluZXMoMTpkLm1heCwgUi5zcXVhcmUsIHR5cGU9J2wnLCBsd2QgPSAyLCBjb2wgPSAiYmx1ZSIpCmBgYApXZSBjYW4gYWxzbyB0YWtlIGEgbG9vayBhdCB0aGUgYWN0dWFsIGZpdHMgb2YgdGhlIHBvbHlub21pYWxzIHJhbmdpbmcgZnJvbSBkZWdyZWUgMSB0byAxMC4KYGBge3J9CnBsb3QoSG9yc2Vwb3dlciwgUmV0YWlsX1ByaWNlLCBwY2ggPSAxNikKcHRzIDwtIHNlcSgwLCA2MDAsIGxlbmd0aC5vdXQ9MTAwKQpmb3IgKGQgaW4gMToxMCl7CglmaXQgPC0gbG0oUmV0YWlsX1ByaWNlIH4gcG9seShIb3JzZXBvd2VyLCBkLCByYXc9VFJVRSkpCgl2YWwgPC0gcHJlZGljdChmaXQsIGRhdGEuZnJhbWUoSG9yc2Vwb3dlciA9IHB0cykpCglsaW5lcyhwdHMsIHZhbCwgY29sPXJhaW5ib3coMTApW2RdLCBsd2QgPSAyKQp9CmBgYApPYnZpb3VzbHksIHRoaXMgc2hvd3MgdGhhdCBldmVuIHRob3VnaCBwb2x5bm9taWFsIG1vZGVscyBvZiBoaWdoZXIgZGVncmVlIHJldHVybiBsYXJnZXIgJFJeMiQgZm9yIHRoZSBmaXQsIHRoZSBoaWdoZXIgZGVncmVlIG1vZGVsIHdpbGwgb3ZlcmZpdCB0aGUgZGF0YS4gT25lIGNhbiBzZWUgdGhhdCB0aGUgaGlnaCBkZWdyZWUgcG9seW5vbWlhbHMgYXJlIHZlcnkgdm9sYXRpbGUuIEl0IGRvZXMgcGVyZm9ybSB3ZWxsIGluIGZpdHRpbmcgdGhlIGRhdGEgd2UgaGF2ZSBhdCBoYW5kLCBidXQgaXQgd2lsbCBtYWtlIGxhcmdlIGVycm9ycyBpbiBwcmVkaWN0aW5nIGRhdGEgbm90IGN1cnJlbnRseSBpbiB0aGUgZGF0YXNldC4KClNvIGhvdyBkbyB3ZSBjaG9vc2UgdGhlIGRlZ3JlZSBvZiBvdXIgcG9seW5vbWlhbCByZWdyZXNzaW9uPyBBIGNvbW1vbiBhcHByb2FjaCBpcyB0byBzdG9wIGF0IGEgbG93IGRlZ3JlZSB3aGljaCAkUl4yJCBkb2VzIG5vdCBpbmNyZWFzZSBtdWNoIGZ1cnRoZXIuIEZvciBleGFtcGxlLCBmcm9tIG91ciBleGFtcGxlLCB3ZSBzZWUgdGhhdCAkUl4yJCBpbmNyZWFzZSBmcm9tIGFib3V0IDAuNjggdG8gb3ZlciAwLjcyIGF0IGRlZ3JlZSAyLCB3aGljaCBpcyBhY3R1YWxseSB0aGUgYmlnZ2VzdCBzaW5nbGUgc3RlcCBnYWluLiBCdXQgdGhlbiBvbmUgbWlnaHQgYWxzbyBhcmd1ZSB0aGF0IGRlZ3JlZSA4IGhhcyBhY2hpZXZlZCAwLjc2IGluICRSXjIkLiBJZiB3ZSB0YWtlIGEgbG9vayBhdCBkZWdyZWUgOCBwb2x5bm9taWFsIGJlbG93LCB3ZSBjYW4gc2VlIHRoYXQgaXQgaXMgYXBwYXJlbnRseSBvdmVyZml0dGluZy4KYGBge3J9CnBsb3QoSG9yc2Vwb3dlciwgUmV0YWlsX1ByaWNlLCBwY2ggPSAxNikKcHRzIDwtIHNlcSgwLCA2MDAsIGxlbmd0aC5vdXQ9MTAwKQpmaXQuZDggPC0gbG0oUmV0YWlsX1ByaWNlIH4gcG9seShIb3JzZXBvd2VyLCA4LCByYXc9VFJVRSkpCnZhbC5kOCA8LSBwcmVkaWN0KGZpdC5kOCwgZGF0YS5mcmFtZShIb3JzZXBvd2VyID0gcHRzKSkKbGluZXMocHRzLCB2YWwuZDgsIGNvbD0icmVkIiwgbHdkID0gMikKYGBgCgojIE1vZGVsIFNlbGVjdGlvbgpUaGUgZGVncmVlIHNlbGVjdGlvbiBtYXkgc291bmQgdmFndWUuIFRyeWluZyB0byBkZXRlcm1pbmUgaXQgZ3JhcGhpY2FsbHkgaXMgc29tZXRpbWVzIGhhcmQuIFRoZXJlIGFyZSBvdGhlciBtZXRob2RzIGluIGRldGVybWluaW5nIHRoZSByaWdodCBtb2RlbC4gSGVyZSB3ZSB3aWxsIHRha2UgY3Jvc3MgdmFsaWRhdGlvbiBmb3IgZXhhbXBsZSwgYW5kIGdvIG92ZXIgdGhlIGNvbmNlcHQgc3RlcCBieSBzdGVwLgoKVGhlIHRlcm0gY3Jvc3MgdmFsaWRhdGlvbiB1c3VhbGx5IGNvbWVzIHdpdGggYSBwYXJhbWV0ZXIuIEEgJGskLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiBpcyBvbmUgdGhhdCBkaXZpZGVzIHRoZSBhdmFpbGFibGUgZGF0YXNldCBpbnRvICRrJCBwaWxlcy4gSWYgYSBkYXRhc2V0IGhhcyAkbiQgcG9pbnRzLCB0aGVuIGVhY2ggcGlsZSBoYXMgJG4vayQgZGF0YSBwb2ludHMsIGFzc3VtaW5nIHRoZXJlJ3Mgbm8gcmVtYWluZGVycy4gSW4gb3VyIGNhc2UsICRuID0gNDI4JCwgY29tbW9uIGNob2ljZXMgZm9yICRrJCBhcmUgNSBvciAxMC4gTGV0J3MgZG8gYSA1LWZvbGQgY3Jvc3MgdmFsaWRhdGlvbi4KCkVhY2ggcm91bmQsIG9uZSBwaWxlIGlzIGdvaW5nIHRvIGJlIHVzZWQgYXMgdGhlIHZhbGlkYXRpb24gc2V0LCBhbmQgdGhlIHJlc3QgYmVpbmcgdHJhaW5pbmcgc2V0LiBUcmFpbmluZyBzZXQgY29uc2lzdHMgb2YgdGhlIG9uZXMgd2UgdXNlIGluIGZpdHRpbmcsIHdoaWxlIHdlIHVzZSB2YWxpZGF0aW9uIHNldCB0byBldmFsdWF0ZSB0aGUgbW9kZWwgaW4gZWFjaCByb3VuZC4gUm90YXRpbmcgdGhyb3VnaCBhbGwgdGhlICRrPTUkIHBpbGVzIGdpdmVzIHVzICRrPTUkIHJvdW5kcyBpbiB0b3RhbCwgd2hpY2ggYWxzbyBnaXZlcyB1cyAkaz01JCBldmFsdWF0aW9ucyBvZiB0aGUgbW9kZWwgd2UgaGF2ZSBjaG9zZW4uIFVzdWFsbHkgYW4gYXZlcmFnZSBpcyB0YWtlbiBhcyB0aGUgZmluYWwgbWVhc3VyZSBvZiBob3cgd2VsbCB0aGUgbW9kZWwgcGVyZm9ybXMuIExldCdzIGltcGxlbWVudCB0aGlzIGluIFIgZm9yIHRoZSBsaW5lYXIgbW9kZWwuCmBgYHtyfQprIDwtIDUKbiA8LSA0MjgKdmFsLnNpemUgPC0gZmxvb3Iobi9rKQpjdi5tc2UgPC0gcmVwKDAsIGspCmZvciAocm91bmQgaW4gMTprKXsKICBpZiAocm91bmQgPT0gayl7CiAgICB2YWwuaW5kIDwtICgocm91bmQtMSkqdmFsLnNpemUgKyAxKTpuCiAgfWVsc2V7CiAgICB2YWwuaW5kIDwtICgocm91bmQtMSkqdmFsLnNpemUgKyAxKToocm91bmQqdmFsLnNpemUpCiAgfQogIHkgPC0gUmV0YWlsX1ByaWNlWy12YWwuaW5kXQogIHggPC0gSG9yc2Vwb3dlclstdmFsLmluZF0KICBmaXQgPC0gbG0oeSB+IHgpCiAgeS5oYXQgPC0gcHJlZGljdChmaXQsIGRhdGEuZnJhbWUoeCA9IEhvcnNlcG93ZXJbdmFsLmluZF0pKQogIGN2Lm1zZVtyb3VuZF0gPC0gc3VtKChSZXRhaWxfUHJpY2VbdmFsLmluZF0gLSB5LmhhdCleMikvbGVuZ3RoKHZhbC5pbmQpCn0KbWVhbihjdi5tc2UpCmBgYAoKTm90aWNlIHRoYXQgd2UgdG9vayBtZWFuIHNxdWFyZWQgZXJyb3IgKE1TRSkgYXMgb3VyIGV2YWx1YXRpb24gY3JpdGVyaW9uLiBOb3cgbGV0J3Mgc2VlIGhvdyB0aGUgbWVhc3VyZSBjaGFuZ2VzIHdpdGggdGhlIGRlZ3JlZSBvZiBwb2x5bm9taWFscy4gV2UgZmlyc3QgcHV0IHRoZSBjcm9zcyB2YWxpZGF0aW9uIHByb2NlZHVyZSBpbnRvIGEgZnVuY3Rpb24uCmBgYHtyfQpjdi5kZWdyZWUuZCA8LSBmdW5jdGlvbihrLCBuLCBkKXsKICB2YWwuc2l6ZSA8LSBmbG9vcihuL2spCiAgY3YubXNlIDwtIHJlcCgwLCBrKQogIGZvciAocm91bmQgaW4gMTprKXsKICAgIGlmIChyb3VuZCA9PSBrKXsKICAgICAgdmFsLmluZCA8LSAoKHJvdW5kLTEpKnZhbC5zaXplICsgMSk6bgogICAgfWVsc2V7CiAgICAgIHZhbC5pbmQgPC0gKChyb3VuZC0xKSp2YWwuc2l6ZSArIDEpOihyb3VuZCp2YWwuc2l6ZSkKICAgIH0KICAgIHkgPC0gUmV0YWlsX1ByaWNlWy12YWwuaW5kXQogICAgeCA8LSBIb3JzZXBvd2VyWy12YWwuaW5kXQogICAgZml0IDwtIGxtKHkgfiBwb2x5KHgsIGQsIHJhdz1UUlVFKSkKICAgIHkuaGF0IDwtIHByZWRpY3QoZml0LCBkYXRhLmZyYW1lKHggPSBIb3JzZXBvd2VyW3ZhbC5pbmRdKSkKICAgIGN2Lm1zZVtyb3VuZF0gPC0gc3VtKChSZXRhaWxfUHJpY2VbdmFsLmluZF0gLSB5LmhhdCleMikvbGVuZ3RoKHZhbC5pbmQpCiAgfQogIHJldHVybiAobWVhbihjdi5tc2UpKQp9CmBgYApXZSBub3cgaGF2ZSB0aGUgbmVjZXNzYXJ5IHRvb2xzIHRvIHRha2UgYSBsb29rIGF0IHRoZSBNU0Ugb2YgcG9seW5vbWlhbCBtb2RlbCBvZiBkZWdyZWVzIGZyb20gMSB0byA5LiAKYGBge3J9CmsgPC0gNQpuIDwtIDQyOApkLm1heCA8LSA5Cm1zZSA8LSByZXAoMCwgZC5tYXgpCmZvciAoZCBpbiAxOmQubWF4KXsKICBtc2VbZF0gPC0gY3YuZGVncmVlLmQoaywgbiwgZCkKfQpwbG90KDE6ZC5tYXgsIG1zZSwgeGxhYiA9ICJEZWdyZWUiLCB5bGFiID0gIk1TRSIsIGx3ZCA9IDIsIGNvbCA9ICJibHVlIiwgcGNoID0gNSkKbGluZXMoMTpkLm1heCwgbXNlLCB0eXBlPSdsJywgbHdkID0gMiwgY29sID0gImJsdWUiKQpgYGAKQXMgc2hvd24gd2l0aCB0aGUgcGxvdCwgdGhlIGNyb3NzIHZhbGlkYXRpb24gTVNFIGFjaGlldmVzIG1pbmltdW0gYXQgZGVncmVlIDMuIEluIG90aGVyIHdvcmRzLCB3ZSBzaG91bGQgc3RvcCBhdCBkZWdyZWUgMyBwb2x5bm9taWFsIGFzIHRoZSBvcHRpbWFsIGRlZ3JlZS4gSW5jcmVhc2luZyB0aGUgZGVncmVlIGZ1cnRoZXIgbGVhZHMgdG8gb3ZlcmZpdHRpbmcuCgpUaGlzIGlzIGtub3duIGFzIHRoZSBtZWFuIHZhcmlhbmNlIHRyYWRlb2ZmLiBBc3N1bWUgdGhlIHVuZGVybHlpbmcgbW9kZWwgdGFrZXMgdGhlIGZvcm0gCiQkIHkgPSBmKHgpICsgXGVwc2lsb24sICQkCndoZXJlICRcZXBzaWxvbiQgaGFzIG1lYW4gMCBhbmQgdmFyaWFuY2UgJFxzaWdtYV4yJC4gVGhlbiBpZiB3ZSBmaXQgYSBtb2RlbCB3aXRoICRcaGF0IGYoeCkkLiBXZSBoYXZlIHRoZSBtZWFuIHNxdWFyZWQgZXJyb3IgYXMgdGhlIGZvbGxvd2luZywKJCRcbWF0aGJie0V9IFxsZWZ0WyBcbGVmdCggeSAtIFxoYXQgZih4KSBccmlnaHQpXjIgXHJpZ2h0XSA9IFxtYXRoYmJ7RX0gXGxlZnRbIFxsZWZ0KCBmKHgpICsgXGVwc2lsb24gLSBcaGF0IGYoeCkgXHJpZ2h0KV4yIFxyaWdodF0gPSBcbWF0aGJie0V9IFxsZWZ0WyBcbGVmdCggZih4KSAtIFxoYXQgZih4KSBccmlnaHQpXjIgKyBcZXBzaWxvbiBcbGVmdCggZih4KSAtIFxoYXQgZih4KSBccmlnaHQpICsgXGVwc2lsb25eMiAgXHJpZ2h0XSA9IFx0ZXh0e1Zhcn0gXGxlZnQoIFxoYXQgZih4KSBccmlnaHQpICsgXGxlZnRbIFxtYXRoYmJ7RX0gXGxlZnQoIGYoeCkgLSBcaGF0IGYoeCkgXHJpZ2h0KSBccmlnaHRdXjIgKyBcc2lnbWFeMi4kJApUaGUgcGFydCAkXHRleHR7VmFyfVxsZWZ0KCBcaGF0IGYoeCkgXHJpZ2h0KSQgaXMgdGhlIHZhcmlhbmNlIG9mIG91ciBmaXR0ZWQgbW9kZWwsIHdoZXJlYXMgdGhlICRcbGVmdFsgXG1hdGhiYntFfSBcbGVmdCggZih4KSAtIFxoYXQgZih4KSBccmlnaHQpIFxyaWdodF1eMiQgaXMgdGhlIGJpYXMgc3F1YXJlZC4gVGhlICRcc2lnbWFeMiQgaXMgdGhlIGlycmVkdWNpYmxlIGVycm9yLiBUaGUgbW9yZSBjb21wbGV4IHRoZSBtb2RlbCAkXGhhdCBmKHgpJCBpcywgaXQgd2lsbCBiZSBhYmxlIHRvIHJlZHVjZSB0aGUgYmlhcyBiZXR0ZXIuIEhvd2V2ZXIsIHRoZSBtb3JlIGNvbXBsZXggbW9kZWwgd2lsbCBsZWFkIHRvIGEgbGFyZ2VyIHZhcmlhbmNlLg==