But I Regress:

Using R to model data in different ways

Data Preprocessing

This next section just deals with data filtering and cleanup to make it more usable for the analysis we’re about to perform.

dataset <- read.csv("HEALTH_PHMC_24102017160155249.csv") # Importing the data
names(dataset) # See different features that the dataset has
 [1] "VAR"        "Variable"   "UNIT"       "Measure"    "COU"       
 [6] "Country"    "YEA"        "Year"       "Value"      "Flag.Codes"
[11] "Flags"     
dataset <- dplyr::filter(dataset, # Filters by rows containing
                         Measure == "Million US$ at exchange rate")
dataset <- dplyr::select(dataset, year = Year, country = Country,
                         value = Value) # Filters by column
total.revenue <- aggregate(dataset$value, by=list(dataset$year), FUN=sum)
total.revenue <- total.revenue[1:35,]
total.revenue[1] <- 1:35
names(total.revenue) <- c("year", "value") # names columns for easier reference
names(total.revenue)
[1] "year"  "value"

Linear Regression

Linear regression is one of the most basic forms of regression. It uses the equation \[ y = mx + b \] Where \(m\) is the slope of the line and \(b\) is where the line crosses the \(y\) axis (also known as the \(y\)-intercept) \(m\) can be found by the equation \[ m = \frac{y_{2} - y_{1}}{x_{2} - x_{1}} \] Where \(x_{1}\), \(y_{1}\) are the coordinates of point 1 and \(x_{2}\), \(y_{2}\) are the coordinates of point 2. With linear regression, we can have R do this for us. It is a very basic form of machine learning.

# Below we will be making a 'linear machine' or lm.
regressor <- lm(formula = value ~ year, data = total.revenue)
y.test <- data.frame("year" = 1:35, "value" = 0)
y.pred <- predict(regressor, newdata = y.test)
y.test[2] <- NULL
y.pred <- data.frame("year" = 1:35, "value" = y.pred)

Now we can use ggplot2, a toolset available to \(R\) analysts based on “The Grammar of Graphics” concept, to plot these two values.

ggplot(data = total.revenue) + aes(x = year, y = value) +
  geom_point(color = "Blue") + 
  geom_point(data = y.pred, aes(x = year, y = value), 
             color = "Green") + 
  ggtitle("Linear Regression Model")

Notice how the green dots and the blue dots both have a similar trend, but only really come into contact with each other for a few years? The model we just made is okay, but it could be better. The reason our group used Linear Regression for our presentation is because it is better for creating future predictions than the other models we looked at.

Polynomial regression

Polynomial regression is very similar to linear regression. But instead of using the equation \(y = mx + b\), we use the equation \[ y = \sum_{i=0}^{n}a_{i}x^{i} \] This may look slightly complicated, but really what it’s saying is \[ y = a_{0}x^{0}+a_{1}x^{1} + a_{2}x^{2} + ... +a_{n}x^{n} \] The best part about this kind of regression is that just like linear regression, we can have \(R\) do it for us. And since our data is ready to go, we can jump right in!

total.poly <- total.revenue
for (i in 2:4){ # This generates some polynomials for us. Fun!
  total.poly[, i+1] <- total.poly[, 1]^i
}
polyreg <- lm(formula = value ~ ., data = total.poly)
poly.test <- total.poly
poly.test[2] <- 0
poly.pred <- predict(polyreg, newdata = poly.test)
poly.pred <- data.frame("year" = 1:35, "value" = poly.pred)
ggplot(data = total.revenue) + aes(x = year, y = value) +
  geom_point(color = "Blue") + 
  geom_point(data = poly.pred, aes(x = year, y = value), 
             color = "Green") + 
  ggtitle("Polynomial Regression Model")

That looks a bit better, doesn’t it? Now, because we are basically guessing at paramaters here, there is a danger in using this form (and many other forms) of regression. It is called overfitting, and can be a serious issue.
Overfitting is basically making a guess that is too good. Above, we are making an educated guess that we think the data could fit a polynomial regression model and then we apply parameters that we choose and calculate. This will be something that is discussed further in statistics, but for now know that it is a thing and can happen when modeling.

Support Vector Regression

Support Vector Regression is basically a way to model the data that uses the size of the error \(\epsilon\) as a maximum deviation away from our actual data point(Smola and Scholkopf 2003, 2). That is to say, we want to minimize the line distance from our model to the actual data. In theory, this is pretty deep, but in practice it is very easy.
For this analysis in \(R\) we need to install the package e1071. After that it’s done in a very similar way to linear and polynomial regressions.

svreg = svm(formula = value ~ year,
                data = total.revenue,
                type = 'eps-regression',
                kernel = 'radial')
svr.pred <- predict(svreg, newdata = y.test)
svr.pred <- data.frame("year" = 1:35, "value" = svr.pred)
ggplot(data = total.revenue) + aes(x = year, y = value) +
  geom_point(color = "Blue") + 
  geom_point(data = svr.pred, aes(x = year, y = value), 
             color = "Green") + ggtitle("SVR Model")

We’re getting even closer, and there’s less danger of overfitting our model.

Decision Tree / Random Forest Regression

Decision Trees

Now we’re moving on to using decision trees to make our models. Basically, a decision tree is a type of machine learning that utilizes “… a tree (and a type of directed, acyclic graph) in which the nodes represent decisions (a square box), random transitions (a circular box) or terminal nodes, and the edges or branches are binary (yes/no, true/false) representing possible paths from one node to another”(Lan 2017). It can be used for regression (which is what we’ll be using it for today), or classification (basically, sorting data into like groups).
This can be done in \(R\) quite easily as well, in a similar fashion to what we did above. First, we must install in import the rpart library, and then it’s the same old story:

tree.reg = rpart(formula = value ~ year,
                  data = total.revenue,
                  control = rpart.control(minsplit = 1))
tree.pred <- predict(tree.reg, newdata = y.test)
tree.pred <- data.frame("year" = 1:35, "value" = tree.pred)
ggplot(data = total.revenue) + aes(x = year, y = value) +
  geom_point(color = "Blue") + 
  geom_point(data = tree.pred, aes(x = year, y = value), 
             color = "Green") + ggtitle("Decision Tree Regression Model")

Well that’s pretty awful, isn’t it? But you can see above we used a value of minsplit = 1. That tells \(R\) to use a minimum of one decision “branch”, and that’s why we have a straight line at the average. Let’s try that again.

two.tree.reg = rpart(formula = value ~ year,
                  data = total.revenue,
                  control = rpart.control(minsplit = 2))
two.tree.pred <- predict(two.tree.reg, newdata = y.test)
two.tree.pred <- data.frame("year" = 1:35, "value" = two.tree.pred)
ggplot(data = total.revenue) + aes(x = year, y = value) +
  geom_point(color = "Blue") + 
  geom_point(data = two.tree.pred, aes(x = year, y = value), 
             color = "Green") + 
  ggtitle("Decision Tree Regression Model V2")

Better, but it’s still not the best model. The reason for this is that we’re using a single tree to decide our regressor. The way that decision tree regression works is that it just takes a “cluster” (a group of similar data points) and then averages them all together. With the 35 data points that we have, it can only find 4 clusters and as a result there are only “mini-models” of data that we can use. The way to combat this is to use random forest regression.

Taking a look at the decision tree

plot(two.tree.reg, main = "Decision Tree V2")

What this plot shows is how the computer grouped the data in the decision tree. Each “branch” is a different cluster of data points.

Random Forests

Random forest regression uses the same idea as decision trees, but it makes many seperate decision trees and then combines them into one decision forest. The random aspect is simply that it slightly changes the way the decision tree is calculated each time. To do this in \(R\), simply install and load the package randomForest.

forest.reg = randomForest(x = total.revenue[-2],
                         y = total.revenue$value,
                         ntree = 500)
forest.pred <- predict(forest.reg, newdata = y.test)
forest.pred <- data.frame("year" = 1:35, "value" = forest.pred)
ggplot(data = total.revenue) + aes(x = year, y = value) +
  geom_point(color = "Blue") + 
  geom_point(data = forest.pred, aes(x = year, y = value), 
             color = "Green") + ggtitle("Random Forest Regression Model")

This is by far the closest fitting model we have gotten, and from this we can make great predictions about any year we have. If we increase the value of ntree we can get an even better model. The best part of this is that it allows us to do very little work on the front end to get a great-fitting model.

Conclusion

The above models are just a few of the tools available to any data analyst. There are other ways to do regression, but hopefully this will provide you with enough information to whet your curiosity. Feel free to check out the source R code at my github.
LicenseFacebooktwitterLinkedin

References

Lan, Haihan. 2017. “Decision Trees and Random Forests for Classification and Regression.” https://medium.com/towards-data-science/decision-trees-and-random-forests-for-classification-and-regression-pt-1-dbb65a458df.

OECD. 2017. “Pharmaceutical Market.” http://dx.doi.org/10.1787/data-00545-en.

Smola, Alex J., and Bernhard Scholkopf. 2003. “A Tutorial on Support Vector Regression.” http://alex.smola.org/papers/2003/SmoSch03b.pdf.

LS0tCnRpdGxlOiAiUmVncmVzc2lvbiBFeGFtcGxlcyIKYXV0aG9yOiBEYW5pZWwgQnJvd24KYmlibGlvZ3JhcGh5OiByZWYuYmliCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgQnV0IEkgUmVncmVzczoKIyMgVXNpbmcgUiB0byBtb2RlbCBkYXRhIGluIGRpZmZlcmVudCB3YXlzCiMjIFRhYmxlIG9mIENvbnRlbnRzCltEYXRhIFByZXByb2Nlc3NpbmddKCNEYXRhLVByZXByb2Nlc3NpbmcpICAKW0xpbmVhciBSZWdyZXNzaW9uXSgjTGluZWFyLVJlZ3Jlc3Npb24pICAKW1BvbHlub21pYWwgUmVncmVzc2lvbl0oI1BvbHlub21pYWwtUmVncmVzc2lvbikgIApbU3VwcG9ydCBWZWN0b3IgUmVncmVzc2lvbl0oI1N1cHBvcnQtVmVjdG9yLVJlZ3Jlc3Npb24pICAKW0RlY2lzb24gVHJlZSAvIFJhbmRvbSBGb3Jlc3QgUmVncmVzc2lvbl0oI0RlY2lzaW9uLVRyZWUtLy1SYW5kb20tRm9yZXN0LVJlZ3Jlc3Npb24pICAKW0RlY2lzaW9uIFRyZWVzXSgjRGVjaXNpb24tVHJlZXMpICAKW1JhbmRvbSBGb3Jlc3RzXSgjUmFuZG9tLUZvcmVzdHMpICAKW0NvbmNsdXNpb25dKCNDb25jbHVzaW9uKSAgCltSZWZlcmVuY2VzXSgjUmVmZXJlbmNlcykgIAoKKk5vdGU6KiBhbGwgZGF0YSB3YXMgcmV0cmlldmVkIGZyb20gdGhlIE9FQ0RbQE9FQ0QxN10gd2Vic2l0ZS4KYGBge3IgZWNobz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KGUxMDcxKQpsaWJyYXJ5KHJwYXJ0KQpgYGAKIyMgRGF0YSBQcmVwcm9jZXNzaW5nClRoaXMgbmV4dCBzZWN0aW9uIGp1c3QgZGVhbHMgd2l0aCBkYXRhIGZpbHRlcmluZyBhbmQgY2xlYW51cCB0byBtYWtlIGl0IG1vcmUgdXNhYmxlIGZvciB0aGUgYW5hbHlzaXMgd2UncmUgYWJvdXQgdG8gcGVyZm9ybS4KYGBge3J9CmRhdGFzZXQgPC0gcmVhZC5jc3YoIkhFQUxUSF9QSE1DXzI0MTAyMDE3MTYwMTU1MjQ5LmNzdiIpICMgSW1wb3J0aW5nIHRoZSBkYXRhCm5hbWVzKGRhdGFzZXQpICMgU2VlIGRpZmZlcmVudCBmZWF0dXJlcyB0aGF0IHRoZSBkYXRhc2V0IGhhcwpkYXRhc2V0IDwtIGRwbHlyOjpmaWx0ZXIoZGF0YXNldCwgIyBGaWx0ZXJzIGJ5IHJvd3MgY29udGFpbmluZwogICAgICAgICAgICAgICAgICAgICAgICAgTWVhc3VyZSA9PSAiTWlsbGlvbiBVUyQgYXQgZXhjaGFuZ2UgcmF0ZSIpCmRhdGFzZXQgPC0gZHBseXI6OnNlbGVjdChkYXRhc2V0LCB5ZWFyID0gWWVhciwgY291bnRyeSA9IENvdW50cnksCiAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IFZhbHVlKSAjIEZpbHRlcnMgYnkgY29sdW1uCgp0b3RhbC5yZXZlbnVlIDwtIGFnZ3JlZ2F0ZShkYXRhc2V0JHZhbHVlLCBieT1saXN0KGRhdGFzZXQkeWVhciksIEZVTj1zdW0pCnRvdGFsLnJldmVudWUgPC0gdG90YWwucmV2ZW51ZVsxOjM1LF0KdG90YWwucmV2ZW51ZVsxXSA8LSAxOjM1Cm5hbWVzKHRvdGFsLnJldmVudWUpIDwtIGMoInllYXIiLCAidmFsdWUiKSAjIG5hbWVzIGNvbHVtbnMgZm9yIGVhc2llciByZWZlcmVuY2UKbmFtZXModG90YWwucmV2ZW51ZSkKYGBgCiMjIExpbmVhciBSZWdyZXNzaW9uCkxpbmVhciByZWdyZXNzaW9uIGlzIG9uZSBvZiB0aGUgbW9zdCBiYXNpYyBmb3JtcyBvZiByZWdyZXNzaW9uLgpJdCB1c2VzIHRoZSBlcXVhdGlvbgpcWwp5ID0gbXggKyBiClxdCldoZXJlICRtJCBpcyB0aGUgc2xvcGUgb2YgdGhlIGxpbmUgYW5kICRiJCBpcyB3aGVyZSB0aGUgbGluZSBjcm9zc2VzCnRoZSAkeSQgYXhpcyAoYWxzbyBrbm93biBhcyB0aGUgJHkkLWludGVyY2VwdCkKJG0kIGNhbiBiZSBmb3VuZCBieSB0aGUgZXF1YXRpb24KXFsKbSA9IFxmcmFje3lfezJ9IC0geV97MX19e3hfezJ9IC0geF97MX19ClxdCldoZXJlICR4X3sxfSQsICR5X3sxfSQgYXJlIHRoZSBjb29yZGluYXRlcyBvZiBwb2ludCAxIGFuZCAkeF97Mn0kLCAkeV97Mn0kCmFyZSB0aGUgY29vcmRpbmF0ZXMgb2YgcG9pbnQgMi4KV2l0aCBsaW5lYXIgcmVncmVzc2lvbiwgd2UgY2FuIGhhdmUgUiBkbyB0aGlzIGZvciB1cy4gSXQgaXMgYSB2ZXJ5IGJhc2ljIGZvcm0gb2YKKiptYWNoaW5lIGxlYXJuaW5nKiouCmBgYHtyfQojIEJlbG93IHdlIHdpbGwgYmUgbWFraW5nIGEgJ2xpbmVhciBtYWNoaW5lJyBvciBsbS4KcmVncmVzc29yIDwtIGxtKGZvcm11bGEgPSB2YWx1ZSB+IHllYXIsIGRhdGEgPSB0b3RhbC5yZXZlbnVlKQp5LnRlc3QgPC0gZGF0YS5mcmFtZSgieWVhciIgPSAxOjM1LCAidmFsdWUiID0gMCkKeS5wcmVkIDwtIHByZWRpY3QocmVncmVzc29yLCBuZXdkYXRhID0geS50ZXN0KQp5LnRlc3RbMl0gPC0gTlVMTAp5LnByZWQgPC0gZGF0YS5mcmFtZSgieWVhciIgPSAxOjM1LCAidmFsdWUiID0geS5wcmVkKQpgYGAKTm93IHdlIGNhbiB1c2UgKmdncGxvdDIqLCBhIHRvb2xzZXQgYXZhaWxhYmxlIHRvICRSJCBhbmFseXN0cyBiYXNlZCBvbiAiVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MiIGNvbmNlcHQsIHRvIHBsb3QgdGhlc2UgdHdvIHZhbHVlcy4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHRvdGFsLnJldmVudWUpICsgYWVzKHggPSB5ZWFyLCB5ID0gdmFsdWUpICsKICBnZW9tX3BvaW50KGNvbG9yID0gIkJsdWUiKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IHkucHJlZCwgYWVzKHggPSB5ZWFyLCB5ID0gdmFsdWUpLCAKICAgICAgICAgICAgIGNvbG9yID0gIkdyZWVuIikgKyAKICBnZ3RpdGxlKCJMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCIpCmBgYAoKTm90aWNlIGhvdyB0aGUgZ3JlZW4gZG90cyBhbmQgdGhlIGJsdWUgZG90cyBib3RoIGhhdmUgYSBzaW1pbGFyIHRyZW5kLCBidXQgb25seSByZWFsbHkgY29tZSBpbnRvIGNvbnRhY3Qgd2l0aCBlYWNoIG90aGVyIGZvciBhIGZldyB5ZWFycz8gVGhlIG1vZGVsIHdlIGp1c3QgbWFkZSBpcyBva2F5LCBidXQgaXQgY291bGQgYmUgYmV0dGVyLiBUaGUgcmVhc29uIG91ciBncm91cCB1c2VkIExpbmVhciBSZWdyZXNzaW9uIGZvciBvdXIgcHJlc2VudGF0aW9uIGlzIGJlY2F1c2UgaXQgaXMgYmV0dGVyIGZvciBjcmVhdGluZyAqZnV0dXJlKiBwcmVkaWN0aW9ucyB0aGFuIHRoZSBvdGhlciBtb2RlbHMgd2UgbG9va2VkIGF0LgoKIyMgUG9seW5vbWlhbCByZWdyZXNzaW9uClBvbHlub21pYWwgcmVncmVzc2lvbiBpcyB2ZXJ5IHNpbWlsYXIgdG8gbGluZWFyIHJlZ3Jlc3Npb24uIEJ1dCBpbnN0ZWFkIG9mIHVzaW5nIHRoZSBlcXVhdGlvbiAkeSA9IG14ICsgYiQsIHdlIHVzZSB0aGUgZXF1YXRpb24gClxbCnkgPSBcc3VtX3tpPTB9XntufWFfe2l9eF57aX0KXF0KVGhpcyBtYXkgbG9vayBzbGlnaHRseSBjb21wbGljYXRlZCwgYnV0IHJlYWxseSB3aGF0IGl0J3Mgc2F5aW5nIGlzClxbCnkgPSBhX3swfXheezB9K2FfezF9eF57MX0gKyBhX3syfXheezJ9ICsgLi4uICthX3tufXhee259ClxdClRoZSBiZXN0IHBhcnQgYWJvdXQgdGhpcyBraW5kIG9mIHJlZ3Jlc3Npb24gaXMgdGhhdCBqdXN0IGxpa2UgbGluZWFyIHJlZ3Jlc3Npb24sIHdlIGNhbiBoYXZlICRSJCBkbyBpdCBmb3IgdXMuIEFuZCBzaW5jZSBvdXIgZGF0YSBpcyByZWFkeSB0byBnbywgd2UgY2FuIGp1bXAgcmlnaHQgaW4hCgpgYGB7cn0KdG90YWwucG9seSA8LSB0b3RhbC5yZXZlbnVlCmZvciAoaSBpbiAyOjQpeyAjIFRoaXMgZ2VuZXJhdGVzIHNvbWUgcG9seW5vbWlhbHMgZm9yIHVzLiBGdW4hCiAgdG90YWwucG9seVssIGkrMV0gPC0gdG90YWwucG9seVssIDFdXmkKfQpwb2x5cmVnIDwtIGxtKGZvcm11bGEgPSB2YWx1ZSB+IC4sIGRhdGEgPSB0b3RhbC5wb2x5KQpwb2x5LnRlc3QgPC0gdG90YWwucG9seQpwb2x5LnRlc3RbMl0gPC0gMAoKcG9seS5wcmVkIDwtIHByZWRpY3QocG9seXJlZywgbmV3ZGF0YSA9IHBvbHkudGVzdCkKcG9seS5wcmVkIDwtIGRhdGEuZnJhbWUoInllYXIiID0gMTozNSwgInZhbHVlIiA9IHBvbHkucHJlZCkKCmdncGxvdChkYXRhID0gdG90YWwucmV2ZW51ZSkgKyBhZXMoeCA9IHllYXIsIHkgPSB2YWx1ZSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiQmx1ZSIpICsgCiAgZ2VvbV9wb2ludChkYXRhID0gcG9seS5wcmVkLCBhZXMoeCA9IHllYXIsIHkgPSB2YWx1ZSksIAogICAgICAgICAgICAgY29sb3IgPSAiR3JlZW4iKSArIAogIGdndGl0bGUoIlBvbHlub21pYWwgUmVncmVzc2lvbiBNb2RlbCIpCmBgYApUaGF0IGxvb2tzIGEgYml0IGJldHRlciwgZG9lc24ndCBpdD8KTm93LCBiZWNhdXNlIHdlIGFyZSBiYXNpY2FsbHkgZ3Vlc3NpbmcgYXQgcGFyYW1hdGVycyBoZXJlLCB0aGVyZSBpcyBhIGRhbmdlciBpbiB1c2luZyB0aGlzIGZvcm0gKGFuZCBtYW55IG90aGVyIGZvcm1zKSBvZiByZWdyZXNzaW9uLiBJdCBpcyBjYWxsZWQgKipvdmVyZml0dGluZyoqLCBhbmQgY2FuIGJlIGEgc2VyaW91cyBpc3N1ZS4gIApPdmVyZml0dGluZyBpcyBiYXNpY2FsbHkgbWFraW5nIGEgZ3Vlc3MgdGhhdCBpcyAqdG9vIGdvb2QqLiBBYm92ZSwgd2UgYXJlIG1ha2luZyBhbiBlZHVjYXRlZCBndWVzcyB0aGF0IHdlIHRoaW5rIHRoZSBkYXRhIGNvdWxkIGZpdCBhIHBvbHlub21pYWwgcmVncmVzc2lvbiBtb2RlbCBhbmQgdGhlbiB3ZSBhcHBseSBwYXJhbWV0ZXJzIHRoYXQgd2UgY2hvb3NlIGFuZCBjYWxjdWxhdGUuIFRoaXMgd2lsbCBiZSBzb21ldGhpbmcgdGhhdCBpcyBkaXNjdXNzZWQgZnVydGhlciBpbiBzdGF0aXN0aWNzLCBidXQgZm9yIG5vdyBrbm93IHRoYXQgaXQgaXMgYSB0aGluZyBhbmQgY2FuIGhhcHBlbiB3aGVuIG1vZGVsaW5nLgoKIyMgU3VwcG9ydCBWZWN0b3IgUmVncmVzc2lvbgpTdXBwb3J0IFZlY3RvciBSZWdyZXNzaW9uIGlzIGJhc2ljYWxseSBhIHdheSB0byBtb2RlbCB0aGUgZGF0YSB0aGF0IHVzZXMgdGhlIHNpemUgb2YgdGhlIGVycm9yICRcZXBzaWxvbiQgYXMgYSBtYXhpbXVtIGRldmlhdGlvbiBhd2F5IGZyb20gb3VyIGFjdHVhbCBkYXRhIHBvaW50W0BhanMwMywgcHAuIDJdLiBUaGF0IGlzIHRvIHNheSwgd2Ugd2FudCB0byBtaW5pbWl6ZSB0aGUgbGluZSBkaXN0YW5jZSBmcm9tIG91ciBtb2RlbCB0byB0aGUgYWN0dWFsIGRhdGEuIEluIHRoZW9yeSwgdGhpcyBpcyBwcmV0dHkgZGVlcCwgYnV0IGluIHByYWN0aWNlIGl0IGlzIHZlcnkgZWFzeS4gIApGb3IgdGhpcyBhbmFseXNpcyBpbiAkUiQgd2UgbmVlZCB0byBpbnN0YWxsIHRoZSBwYWNrYWdlICplMTA3MSouIEFmdGVyIHRoYXQgaXQncyBkb25lIGluIGEgdmVyeSBzaW1pbGFyIHdheSB0byBsaW5lYXIgYW5kIHBvbHlub21pYWwgcmVncmVzc2lvbnMuCmBgYHtyfQpzdnJlZyA9IHN2bShmb3JtdWxhID0gdmFsdWUgfiB5ZWFyLAogICAgICAgICAgICAgICAgZGF0YSA9IHRvdGFsLnJldmVudWUsCiAgICAgICAgICAgICAgICB0eXBlID0gJ2Vwcy1yZWdyZXNzaW9uJywKICAgICAgICAgICAgICAgIGtlcm5lbCA9ICdyYWRpYWwnKQpzdnIucHJlZCA8LSBwcmVkaWN0KHN2cmVnLCBuZXdkYXRhID0geS50ZXN0KQoKc3ZyLnByZWQgPC0gZGF0YS5mcmFtZSgieWVhciIgPSAxOjM1LCAidmFsdWUiID0gc3ZyLnByZWQpCgpnZ3Bsb3QoZGF0YSA9IHRvdGFsLnJldmVudWUpICsgYWVzKHggPSB5ZWFyLCB5ID0gdmFsdWUpICsKICBnZW9tX3BvaW50KGNvbG9yID0gIkJsdWUiKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IHN2ci5wcmVkLCBhZXMoeCA9IHllYXIsIHkgPSB2YWx1ZSksIAogICAgICAgICAgICAgY29sb3IgPSAiR3JlZW4iKSArIGdndGl0bGUoIlNWUiBNb2RlbCIpCmBgYApXZSdyZSBnZXR0aW5nIGV2ZW4gY2xvc2VyLCBhbmQgdGhlcmUncyBsZXNzIGRhbmdlciBvZiBvdmVyZml0dGluZyBvdXIgbW9kZWwuCgojIyBEZWNpc2lvbiBUcmVlIC8gUmFuZG9tIEZvcmVzdCBSZWdyZXNzaW9uCiMjIyBEZWNpc2lvbiBUcmVlcwpOb3cgd2UncmUgbW92aW5nIG9uIHRvIHVzaW5nIGRlY2lzaW9uIHRyZWVzIHRvIG1ha2Ugb3VyIG1vZGVscy4gQmFzaWNhbGx5LCBhIGRlY2lzaW9uIHRyZWUgaXMgYSB0eXBlIG9mIG1hY2hpbmUgbGVhcm5pbmcgdGhhdCB1dGlsaXplcyAiLi4uIGEgdHJlZSAoYW5kIGEgdHlwZSBvZiBkaXJlY3RlZCwgYWN5Y2xpYyBncmFwaCkgaW4gd2hpY2ggdGhlIG5vZGVzIHJlcHJlc2VudCBkZWNpc2lvbnMgKGEgc3F1YXJlIGJveCksIHJhbmRvbSB0cmFuc2l0aW9ucyAoYSBjaXJjdWxhciBib3gpIG9yIHRlcm1pbmFsIG5vZGVzLCBhbmQgdGhlIGVkZ2VzIG9yIGJyYW5jaGVzIGFyZSBiaW5hcnkgKHllcy9ubywgdHJ1ZS9mYWxzZSkgcmVwcmVzZW50aW5nIHBvc3NpYmxlIHBhdGhzIGZyb20gb25lIG5vZGUgdG8gYW5vdGhlciJbQGhsMTddLiBJdCBjYW4gYmUgdXNlZCBmb3IgcmVncmVzc2lvbiAod2hpY2ggaXMgd2hhdCB3ZSdsbCBiZSB1c2luZyBpdCBmb3IgdG9kYXkpLCBvciBjbGFzc2lmaWNhdGlvbiAoYmFzaWNhbGx5LCBzb3J0aW5nIGRhdGEgaW50byBsaWtlIGdyb3VwcykuICAKVGhpcyBjYW4gYmUgZG9uZSBpbiAkUiQgcXVpdGUgZWFzaWx5IGFzIHdlbGwsIGluIGEgc2ltaWxhciBmYXNoaW9uIHRvIHdoYXQgd2UgZGlkIGFib3ZlLiBGaXJzdCwgd2UgbXVzdCBpbnN0YWxsIGluIGltcG9ydCB0aGUgKnJwYXJ0KiBsaWJyYXJ5LCBhbmQgdGhlbiBpdCdzIHRoZSBzYW1lIG9sZCBzdG9yeToKCmBgYHtyfQp0cmVlLnJlZyA9IHJwYXJ0KGZvcm11bGEgPSB2YWx1ZSB+IHllYXIsCiAgICAgICAgICAgICAgICAgIGRhdGEgPSB0b3RhbC5yZXZlbnVlLAogICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChtaW5zcGxpdCA9IDEpKQp0cmVlLnByZWQgPC0gcHJlZGljdCh0cmVlLnJlZywgbmV3ZGF0YSA9IHkudGVzdCkKCnRyZWUucHJlZCA8LSBkYXRhLmZyYW1lKCJ5ZWFyIiA9IDE6MzUsICJ2YWx1ZSIgPSB0cmVlLnByZWQpCgpnZ3Bsb3QoZGF0YSA9IHRvdGFsLnJldmVudWUpICsgYWVzKHggPSB5ZWFyLCB5ID0gdmFsdWUpICsKICBnZW9tX3BvaW50KGNvbG9yID0gIkJsdWUiKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IHRyZWUucHJlZCwgYWVzKHggPSB5ZWFyLCB5ID0gdmFsdWUpLCAKICAgICAgICAgICAgIGNvbG9yID0gIkdyZWVuIikgKyBnZ3RpdGxlKCJEZWNpc2lvbiBUcmVlIFJlZ3Jlc3Npb24gTW9kZWwiKQpgYGAKV2VsbCB0aGF0J3MgcHJldHR5IGF3ZnVsLCBpc24ndCBpdD8gQnV0IHlvdSBjYW4gc2VlIGFib3ZlIHdlIHVzZWQgYSB2YWx1ZSBvZiAqbWluc3BsaXQgPSAxKi4gVGhhdCB0ZWxscyAkUiQgdG8gdXNlIGEgbWluaW11bSBvZiBvbmUgZGVjaXNpb24gImJyYW5jaCIsIGFuZCB0aGF0J3Mgd2h5IHdlIGhhdmUgYSBzdHJhaWdodCBsaW5lIGF0IHRoZSBhdmVyYWdlLiBMZXQncyB0cnkgdGhhdCBhZ2Fpbi4KYGBge3J9CnR3by50cmVlLnJlZyA9IHJwYXJ0KGZvcm11bGEgPSB2YWx1ZSB+IHllYXIsCiAgICAgICAgICAgICAgICAgIGRhdGEgPSB0b3RhbC5yZXZlbnVlLAogICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChtaW5zcGxpdCA9IDIpKQp0d28udHJlZS5wcmVkIDwtIHByZWRpY3QodHdvLnRyZWUucmVnLCBuZXdkYXRhID0geS50ZXN0KQoKdHdvLnRyZWUucHJlZCA8LSBkYXRhLmZyYW1lKCJ5ZWFyIiA9IDE6MzUsICJ2YWx1ZSIgPSB0d28udHJlZS5wcmVkKQoKZ2dwbG90KGRhdGEgPSB0b3RhbC5yZXZlbnVlKSArIGFlcyh4ID0geWVhciwgeSA9IHZhbHVlKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICJCbHVlIikgKyAKICBnZW9tX3BvaW50KGRhdGEgPSB0d28udHJlZS5wcmVkLCBhZXMoeCA9IHllYXIsIHkgPSB2YWx1ZSksIAogICAgICAgICAgICAgY29sb3IgPSAiR3JlZW4iKSArIAogIGdndGl0bGUoIkRlY2lzaW9uIFRyZWUgUmVncmVzc2lvbiBNb2RlbCBWMiIpCmBgYApCZXR0ZXIsIGJ1dCBpdCdzIHN0aWxsIG5vdCB0aGUgYmVzdCBtb2RlbC4gVGhlIHJlYXNvbiBmb3IgdGhpcyBpcyB0aGF0IHdlJ3JlIHVzaW5nIGEgc2luZ2xlIHRyZWUgdG8gZGVjaWRlIG91ciByZWdyZXNzb3IuIFRoZSB3YXkgdGhhdCBkZWNpc2lvbiB0cmVlIHJlZ3Jlc3Npb24gd29ya3MgaXMgdGhhdCBpdCBqdXN0IHRha2VzIGEgImNsdXN0ZXIiIChhIGdyb3VwIG9mIHNpbWlsYXIgZGF0YSBwb2ludHMpIGFuZCB0aGVuIGF2ZXJhZ2VzIHRoZW0gYWxsIHRvZ2V0aGVyLiBXaXRoIHRoZSAzNSBkYXRhIHBvaW50cyB0aGF0IHdlIGhhdmUsIGl0IGNhbiBvbmx5IGZpbmQgNCBjbHVzdGVycyBhbmQgYXMgYSByZXN1bHQgdGhlcmUgYXJlIG9ubHkgIm1pbmktbW9kZWxzIiBvZiBkYXRhIHRoYXQgd2UgY2FuIHVzZS4gVGhlIHdheSB0byBjb21iYXQgdGhpcyBpcyB0byB1c2UgKipyYW5kb20gZm9yZXN0KiogcmVncmVzc2lvbi4gIAoKIyMjIyBUYWtpbmcgYSBsb29rIGF0IHRoZSBkZWNpc2lvbiB0cmVlCmBgYHtyfQpwbG90KHR3by50cmVlLnJlZywgbWFpbiA9ICJEZWNpc2lvbiBUcmVlIFYyIikKYGBgCldoYXQgdGhpcyBwbG90IHNob3dzIGlzIGhvdyB0aGUgY29tcHV0ZXIgZ3JvdXBlZCB0aGUgZGF0YSBpbiB0aGUgZGVjaXNpb24gdHJlZS4KRWFjaCAiYnJhbmNoIiBpcyBhIGRpZmZlcmVudCBjbHVzdGVyIG9mIGRhdGEgcG9pbnRzLgoKIyMjIFJhbmRvbSBGb3Jlc3RzICAKUmFuZG9tIGZvcmVzdCByZWdyZXNzaW9uIHVzZXMgdGhlIHNhbWUgaWRlYSBhcyBkZWNpc2lvbiB0cmVlcywgYnV0IGl0IG1ha2VzIG1hbnkgc2VwZXJhdGUgZGVjaXNpb24gdHJlZXMgYW5kIHRoZW4gY29tYmluZXMgdGhlbSBpbnRvIG9uZSBkZWNpc2lvbiBmb3Jlc3QuIFRoZSByYW5kb20gYXNwZWN0IGlzIHNpbXBseSB0aGF0IGl0IHNsaWdodGx5IGNoYW5nZXMgdGhlIHdheSB0aGUgZGVjaXNpb24gdHJlZSBpcyBjYWxjdWxhdGVkIGVhY2ggdGltZS4gVG8gZG8gdGhpcyBpbiAkUiQsIHNpbXBseSBpbnN0YWxsIGFuZCBsb2FkIHRoZSBwYWNrYWdlICpyYW5kb21Gb3Jlc3QqLgoKYGBge3J9CmZvcmVzdC5yZWcgPSByYW5kb21Gb3Jlc3QoeCA9IHRvdGFsLnJldmVudWVbLTJdLAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHRvdGFsLnJldmVudWUkdmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDUwMCkKZm9yZXN0LnByZWQgPC0gcHJlZGljdChmb3Jlc3QucmVnLCBuZXdkYXRhID0geS50ZXN0KQoKZm9yZXN0LnByZWQgPC0gZGF0YS5mcmFtZSgieWVhciIgPSAxOjM1LCAidmFsdWUiID0gZm9yZXN0LnByZWQpCgpnZ3Bsb3QoZGF0YSA9IHRvdGFsLnJldmVudWUpICsgYWVzKHggPSB5ZWFyLCB5ID0gdmFsdWUpICsKICBnZW9tX3BvaW50KGNvbG9yID0gIkJsdWUiKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IGZvcmVzdC5wcmVkLCBhZXMoeCA9IHllYXIsIHkgPSB2YWx1ZSksIAogICAgICAgICAgICAgY29sb3IgPSAiR3JlZW4iKSArIGdndGl0bGUoIlJhbmRvbSBGb3Jlc3QgUmVncmVzc2lvbiBNb2RlbCIpCmBgYApUaGlzIGlzIGJ5IGZhciB0aGUgY2xvc2VzdCBmaXR0aW5nIG1vZGVsIHdlIGhhdmUgZ290dGVuLCBhbmQgZnJvbSB0aGlzIHdlIGNhbiBtYWtlIGdyZWF0IHByZWRpY3Rpb25zIGFib3V0IGFueSB5ZWFyIHdlIGhhdmUuIElmIHdlIGluY3JlYXNlIHRoZSB2YWx1ZSBvZiAqbnRyZWUqIHdlIGNhbiBnZXQgYW4gZXZlbiBiZXR0ZXIgbW9kZWwuIFRoZSBiZXN0IHBhcnQgb2YgdGhpcyBpcyB0aGF0IGl0IGFsbG93cyB1cyB0byBkbyB2ZXJ5IGxpdHRsZSB3b3JrIG9uIHRoZSBmcm9udCBlbmQgdG8gZ2V0IGEgZ3JlYXQtZml0dGluZyBtb2RlbC4KCiMjIENvbmNsdXNpb24KVGhlIGFib3ZlIG1vZGVscyBhcmUganVzdCBhIGZldyBvZiB0aGUgdG9vbHMgYXZhaWxhYmxlIHRvIGFueSBkYXRhIGFuYWx5c3QuIFRoZXJlIGFyZSBvdGhlciB3YXlzIHRvIGRvIHJlZ3Jlc3Npb24sIGJ1dCBob3BlZnVsbHkgdGhpcyB3aWxsIHByb3ZpZGUgeW91IHdpdGggZW5vdWdoIGluZm9ybWF0aW9uIHRvIHdoZXQgeW91ciBjdXJpb3NpdHkuIEZlZWwgZnJlZSB0byBjaGVjayBvdXQgdGhlIHNvdXJjZSBSIGNvZGUgYXQgbXkgW2dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2RhbmllbGJyb3duanIvcmVncmVzc2lvbikuICAKWyFbTGljZW5zZV0oaHR0cHM6Ly9pbWcuc2hpZWxkcy5pby9iYWRnZS9MSUNFTlNFLU1JVC1yZWQuc3ZnKV0oLi9MaWNlbnNlKVshW0ZhY2Vib29rXShodHRwczovL2ltZy5zaGllbGRzLmlvL2JhZGdlL2ZhY2Vib29rLURhbmllbC1yZWQuc3ZnP3N0eWxlPXNvY2lhbCldKGh0dHBzOi8vd3d3LmZhY2Vib29rLmNvbS9jaGFzZWFmdGVyc3RhcnQyMDA2KVshW3R3aXR0ZXJdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvdHdpdHRlci1jaGFzZWFmdGVyc3RhcnQtcmVkLnN2Zz9zdHlsZT1zb2NpYWwpXShodHRwczovL3R3aXR0ZXIuY29tL0NoYXNlQWZ0ZXJTdGFydClbIVtMaW5rZWRpbl0oaHR0cHM6Ly9pbWcuc2hpZWxkcy5pby9iYWRnZS9MaW5rZWRpbi1EYW5pZWwtcmVkLnN2Zz9zdHlsZT1zb2NpYWwpXShodHRwOi8vdGlueS5jYy9kYW5pZWxicm93bikgIAoKIyBSZWZlcmVuY2Vz