Author: Daniel R. Brown, Jr.

ALY6020 Predictive Analytics

Professor Stewart Huang

February 17th, 2018

library(mi)
library(tidyverse)
library(rpart)

Looking at MPG data using various methods of analysis

In this discussion I will be looking at the mpg.data dataset from UCI’s Machine Learning repository(Lichman 2013) ## Dataset importing and data preprocessing The dataset that we will be looking at today contains vehicle data from the years 1970 to 1982. The data provides 9 attributes, two of which being model and year. Because multiple cars were made in multiple years, to create a unique label for each row I combined model and year into a factor variable called model.year using the as.factor() and paste() functions.

mpg.data <- as.data.frame(read.table("auto-mpg.txt"))
names(mpg.data) <- c("mpg", "cylinders", "displacement", "horsepower", "weight",
                     "acceleration", "year", "origin", "model")
model.year <- as.factor(paste(mpg.data$year, mpg.data$model, sep = " "))
mpg.data$horsepower <- as.numeric(mpg.data$horsepower)
mpg.data$model.year <- model.year
mpg.data$model <- NULL
mpg.data$year <- NULL
mpg.data$origin <- as.factor(mpg.data$origin)
mpg.data$cylinders <- as.factor(mpg.data$cylinders)
remove(model.year)
mpg.data <- na.omit(mpg.data)

Next I can split the data into training and test sets for performing analysis on them using the tried-but-true rand_sample() function that I made earlier in this course

rand_sample <- function(x, fraction = 0.75, seed = 123){
  set.seed(seed)
  size <- nrow(x)
  training_size <- fraction * size
  results <- sort(sample(size, training_size))
  return(results)
}
mpg.train <- mpg.data[rand_sample(mpg.data), ]
mpg.test <- mpg.data[-rand_sample(mpg.data), ]

Multiple Linear Regression

Let’s start off by performing some standard multiple linear regression on this bad boy

regressor1 <- lm(formula = mpg ~ cylinders + displacement + horsepower + weight 
                + acceleration + origin, data = mpg.train)
summary(regressor1)

Call:
lm(formula = mpg ~ cylinders + displacement + horsepower + weight + 
    acceleration + origin, data = mpg.train)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.8886  -2.2927  -0.3991   2.1572  13.1365 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  33.6335749  3.9726375   8.466 1.39e-15 ***
cylinders4    9.8726336  2.9051401   3.398 0.000775 ***
cylinders5   12.3660384  4.0704850   3.038 0.002604 ** 
cylinders6    6.0155001  3.1164031   1.930 0.054572 .  
cylinders8    8.5857033  3.4920116   2.459 0.014544 *  
displacement  0.0044192  0.0110508   0.400 0.689529    
horsepower   -0.0667207  0.0189282  -3.525 0.000494 ***
weight       -0.0042418  0.0009653  -4.394 1.57e-05 ***
acceleration -0.0433673  0.1361534  -0.319 0.750327    
origin2       0.5000341  0.7746055   0.646 0.519104    
origin3       3.3162630  0.7702200   4.306 2.30e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.942 on 283 degrees of freedom
Multiple R-squared:  0.7633,    Adjusted R-squared:  0.7549 
F-statistic: 91.25 on 10 and 283 DF,  p-value: < 2.2e-16

If we set a our Signif. code threshold at 95% (call this the limit for statistical significance), we can start eliminating coefficients. The highest is acceleration, so we can start by removing that.

regressor2 <- lm(formula = mpg ~ cylinders + displacement + horsepower + weight 
                + origin, data = mpg.train)
summary(regressor2)

Call:
lm(formula = mpg ~ cylinders + displacement + horsepower + weight + 
    origin, data = mpg.train)

Residuals:
     Min       1Q   Median       3Q      Max 
-11.0329  -2.3205  -0.3886   2.2092  12.9491 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  32.9905500  3.4160264   9.658  < 2e-16 ***
cylinders4    9.7957802  2.8905194   3.389 0.000801 ***
cylinders5   12.2867261  4.0564293   3.029 0.002680 ** 
cylinders6    5.9532702  3.1053487   1.917 0.056228 .  
cylinders8    8.5393912  3.4834595   2.451 0.014832 *  
displacement  0.0047530  0.0109836   0.433 0.665536    
horsepower   -0.0629842  0.0148313  -4.247 2.94e-05 ***
weight       -0.0043829  0.0008564  -5.118 5.71e-07 ***
origin2       0.4940332  0.7731504   0.639 0.523346    
origin3       3.3167358  0.7689991   4.313 2.22e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.936 on 284 degrees of freedom
Multiple R-squared:  0.7632,    Adjusted R-squared:  0.7557 
F-statistic: 101.7 on 9 and 284 DF,  p-value: < 2.2e-16

Now we can pull displacement

regressor3 <- lm(formula = mpg ~ cylinders + horsepower + weight + origin,
                 data = mpg.train)
summary(regressor3)

Call:
lm(formula = mpg ~ cylinders + horsepower + weight + origin, 
    data = mpg.train)

Residuals:
     Min       1Q   Median       3Q      Max 
-11.0849  -2.3032  -0.3701   2.1809  12.9751 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) 32.7158270  3.3517263   9.761  < 2e-16 ***
cylinders4  10.0120888  2.8429076   3.522 0.000499 ***
cylinders5  12.4948934  4.0220573   3.107 0.002083 ** 
cylinders6   6.3986156  2.9256755   2.187 0.029551 *  
cylinders8   9.2595564  3.0558400   3.030 0.002669 ** 
horsepower  -0.0601953  0.0133388  -4.513 9.37e-06 ***
weight      -0.0041990  0.0007425  -5.655 3.78e-08 ***
origin2      0.3909378  0.7344792   0.532 0.594957    
origin3      3.2236380  0.7372386   4.373 1.72e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.931 on 285 degrees of freedom
Multiple R-squared:  0.763, Adjusted R-squared:  0.7564 
F-statistic: 114.7 on 8 and 285 DF,  p-value: < 2.2e-16

Because origin3 is statistically significant, I will keep the entire origin variable.

Now that we have our regressor, we can create our predictions

predictor <- predict(regressor3, newdata = mpg.test)

Now we can create a dataframe to look at the results

result.data <- data.frame(model.year = mpg.test$model.year, 
                          prediction = predictor, 
                          actual = mpg.test$mpg)
percent.diff <- abs(result.data$prediction - result.data$actual) / 
  result.data$actual * 100
result.data$percent.diff <- percent.diff
remove(percent.diff)
paste("Percent difference:", round(mean(result.data$percent.diff)))
[1] "Percent difference: 12"
result.data$prediction <- round(result.data$prediction, 2)
result.data$percent.diff <- round(result.data$percent.diff, 2)
print(result.data)

12 percent difference isn’t very good. Is there a better way to do this?

Let’s try a decision tree.

Decision tree

regressor4 <- rpart(formula = mpg ~ ., data = mpg.test)
dtpred <- predict(regressor4, data = mpg.test)
plot(regressor4, main = "Decision Tree Regression")

Now let’s evaluate how our model performed:

result.data2 <- data.frame(model.year = mpg.test$model.year, 
                          prediction = dtpred, 
                          actual = mpg.test$mpg)
percent.diff <- abs(result.data2$prediction - result.data2$actual) / 
  result.data2$actual * 100
result.data2$percent.diff <- percent.diff
remove(percent.diff)
paste("Percent difference:", round(mean(result.data2$percent.diff)))
[1] "Percent difference: 7"
result.data2$prediction <- round(result.data2$prediction, 2)
result.data2$percent.diff <- round(result.data2$percent.diff, 2)
print(result.data2)

This 7% result is much better.

Discussion

Multiple Linear Regression

I chose linear regression to start off with because I feel that it is one of the most common and basic methods of predictive analysis. “The linear regression model analyzes the relationship between the response or dependent variable and a set of independent or predictor variables. This relationship is expressed as an equation that predicts the response variable as a linear function of the parameters… [which] are adjusted so that a measure of fit is optimized”(Strickland 2014)

Basically, linear regression uses the old geometric formula that we’ve all been taught, \[ y = mx + b \]

to linearly fit a dependent variable (\(y\)) to an independent variable (\(x\)). This measure of fit discussed above is measured by a property known as the \(R^2\) value, and the fit is considered better the closer to 1 it is. During the linear regression portion, we had an \(R^2 \approx 0.75\), which is not very good - the data was not related linearly, and the linear regression model was a poor choice to use.

We can look at some data that I obtained during a freshman physics lab during my undergraduate degree program. Part of the assignment was to manually perform linear regression on the data, so I thought it would be interesting to pop the data into excel and take a look at it.

dataset <- read_csv("2d.csv")
Parsed with column specification:
cols(
  v = col_double(),
  m = col_double()
)
y.pred <- lm(m~., data = dataset)
plot(y.pred)

plot(x = dataset$v, y = dataset$m)

Decision Tree Learning

Decision trees “utilize a tree structure to model the relationships among features and the potential outcomes… this structure earned its name due to the fact that it mirrors how a literal tree begins at a wide trunk, which if followed upward, splits into narrower and narrower branches. In much the same way, a decision tree classifier uses a structure of branching decisions which channel examples into a final predicted class value”(Lantz 2015)

The main reason I picked a decision tree model is because I wanted to use a random forest model but R 3.4.3 Kite-eating-tree will not recognize the fact that I have Java installed and as a result I cannot use rJava or any of the associated packages that require it. I decided that the next best thing would be to use a decision tree and as a result chose that.

Strengths and weaknesses of each model

Linear Regression

Linear regression is a good model to use when you want fast, simple results. When you have one dependent and one independent variable, linear regression is a great method to use. There are other models that are closely related to linear regression, like polynomial regression, that can provide accurate results as well. Look at this sweet graph(Brown 2017)

It is not without its weaknesses. More complicated datasets, like the mpg dataset we looked at earlier, do not do well with linear regression. Noise data sets can confuse the models as well.

Decision tree

Decision trees are great at looking back at models. You can take prior data and create a well-made model that will capture all the data within before hand that can easily provide solid results at point within the model. Where the model can fail, however, is when it is used to forecast future results. So rich, so pretty, the best piece of graph in the whole damn city

Also, the models take some understanding as to how to tune them. The above graph captures the data only at one point - at about 23 years in, the model is perfect, but all other points are completely ignored. In this situation, you would be better off averaging all the data points to get your prediction.

A better result for the same data can be found below. V2

Conclusion

Using the two models of linear regression and decision trees we were able to predict the mpg of various classic cars. That being said, the linear regression model only performed 88% efficiently. The decision tree model performed better at 93% efficiently, and I predict that the use of a random forest model would only increase these results.

References

Brown, Daniel. 2017. “But I Regress: Using R to Model Data in Different Ways.” https://rpubs.com/dbrown/regression.

Lantz, Brett. 2015. Machine Learning with R. Birmingham, United Kingdom: Packt Publishing.

Lichman, M. 2013. “UCI Machine Learning Repository.” University of California, Irvine, School of Information; Computer Sciences. http://archive.ics.uci.edu/ml.

Strickland, Jeffrey S. 2014. Predictive Analytics Using R. Raleigh, NC, United States: Lulu, Inc.

LS0tCnRpdGxlOiAiRmluYWwgUHJvamVjdCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwpiaWJsaW9ncmFwaHk6IHJlZi5iaWIKLS0tCgojIyMjIEF1dGhvcjogRGFuaWVsIFIuIEJyb3duLCBKci4KIyMjIyBBTFk2MDIwIFByZWRpY3RpdmUgQW5hbHl0aWNzCiMjIyMgUHJvZmVzc29yIFN0ZXdhcnQgSHVhbmcKIyMjIyBGZWJydWFyeSAxN3RoLCAyMDE4CgpgYGB7ciBMaWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkobWkpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJwYXJ0KQpgYGAKCiMgTG9va2luZyBhdCBNUEcgZGF0YSB1c2luZyB2YXJpb3VzIG1ldGhvZHMgb2YgYW5hbHlzaXMKCkluIHRoaXMgZGlzY3Vzc2lvbiBJIHdpbGwgYmUgbG9va2luZyBhdCB0aGUgKiptcGcuZGF0YSoqIGRhdGFzZXQgZnJvbSBVQ0kncyBNYWNoaW5lIExlYXJuaW5nIHJlcG9zaXRvcnlbQGxpY2htMTNdCiMjIERhdGFzZXQgaW1wb3J0aW5nIGFuZCBkYXRhIHByZXByb2Nlc3NpbmcKVGhlIGRhdGFzZXQgdGhhdCB3ZSB3aWxsIGJlIGxvb2tpbmcgYXQgdG9kYXkgY29udGFpbnMgdmVoaWNsZSBkYXRhIGZyb20gdGhlIHllYXJzIDE5NzAgdG8gMTk4Mi4KVGhlIGRhdGEgcHJvdmlkZXMgOSBhdHRyaWJ1dGVzLCB0d28gb2Ygd2hpY2ggYmVpbmcgKm1vZGVsKiBhbmQgKnllYXIqLiBCZWNhdXNlIG11bHRpcGxlIGNhcnMgd2VyZSBtYWRlIGluIG11bHRpcGxlIHllYXJzLCB0byBjcmVhdGUgYSB1bmlxdWUgbGFiZWwgZm9yIGVhY2ggcm93IEkgY29tYmluZWQgKm1vZGVsKiBhbmQgKnllYXIqIGludG8gYSBmYWN0b3IgdmFyaWFibGUgY2FsbGVkICptb2RlbC55ZWFyKiB1c2luZyB0aGUgYGBgYXMuZmFjdG9yKClgYGAgYW5kIGBgYHBhc3RlKClgYGAgZnVuY3Rpb25zLgpgYGB7ciBkYXRhIHByZXByb2Nlc3Npbmd9Cm1wZy5kYXRhIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZSgiYXV0by1tcGcudHh0IikpCm5hbWVzKG1wZy5kYXRhKSA8LSBjKCJtcGciLCAiY3lsaW5kZXJzIiwgImRpc3BsYWNlbWVudCIsICJob3JzZXBvd2VyIiwgIndlaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICJhY2NlbGVyYXRpb24iLCAieWVhciIsICJvcmlnaW4iLCAibW9kZWwiKQptb2RlbC55ZWFyIDwtIGFzLmZhY3RvcihwYXN0ZShtcGcuZGF0YSR5ZWFyLCBtcGcuZGF0YSRtb2RlbCwgc2VwID0gIiAiKSkKbXBnLmRhdGEkaG9yc2Vwb3dlciA8LSBhcy5udW1lcmljKG1wZy5kYXRhJGhvcnNlcG93ZXIpCm1wZy5kYXRhJG1vZGVsLnllYXIgPC0gbW9kZWwueWVhcgptcGcuZGF0YSRtb2RlbCA8LSBOVUxMCm1wZy5kYXRhJHllYXIgPC0gTlVMTAptcGcuZGF0YSRvcmlnaW4gPC0gYXMuZmFjdG9yKG1wZy5kYXRhJG9yaWdpbikKbXBnLmRhdGEkY3lsaW5kZXJzIDwtIGFzLmZhY3RvcihtcGcuZGF0YSRjeWxpbmRlcnMpCnJlbW92ZShtb2RlbC55ZWFyKQptcGcuZGF0YSA8LSBuYS5vbWl0KG1wZy5kYXRhKQpgYGAKCk5leHQgSSBjYW4gc3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdCBzZXRzIGZvciBwZXJmb3JtaW5nIGFuYWx5c2lzIG9uIHRoZW0gdXNpbmcgdGhlIHRyaWVkLWJ1dC10cnVlIApgYGByYW5kX3NhbXBsZSgpYGBgIGZ1bmN0aW9uIHRoYXQgSSBtYWRlIGVhcmxpZXIgaW4gdGhpcyBjb3Vyc2UKYGBge3IgY3JlYXRlIGEgcmFuZG9tIHNhbXBsZSBmdW5jdGlvbn0KcmFuZF9zYW1wbGUgPC0gZnVuY3Rpb24oeCwgZnJhY3Rpb24gPSAwLjc1LCBzZWVkID0gMTIzKXsKICBzZXQuc2VlZChzZWVkKQogIHNpemUgPC0gbnJvdyh4KQogIHRyYWluaW5nX3NpemUgPC0gZnJhY3Rpb24gKiBzaXplCiAgcmVzdWx0cyA8LSBzb3J0KHNhbXBsZShzaXplLCB0cmFpbmluZ19zaXplKSkKICByZXR1cm4ocmVzdWx0cykKfQpgYGAKCmBgYHtyIHNwbGl0aW5nIHRoZSBkYXRhIGludG8gdGVzdCBhbmQgdHJhaW5pbmcgc2V0c30KbXBnLnRyYWluIDwtIG1wZy5kYXRhW3JhbmRfc2FtcGxlKG1wZy5kYXRhKSwgXQptcGcudGVzdCA8LSBtcGcuZGF0YVstcmFuZF9zYW1wbGUobXBnLmRhdGEpLCBdCmBgYAojIyBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbgpMZXQncyBzdGFydCBvZmYgYnkgcGVyZm9ybWluZyBzb21lIHN0YW5kYXJkICptdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiogb24gdGhpcyBiYWQgYm95CmBgYHtyIGxpbmVhciByZWdyZXNzaW9ufQpyZWdyZXNzb3IxIDwtIGxtKGZvcm11bGEgPSBtcGcgfiBjeWxpbmRlcnMgKyBkaXNwbGFjZW1lbnQgKyBob3JzZXBvd2VyICsgd2VpZ2h0IAogICAgICAgICAgICAgICAgKyBhY2NlbGVyYXRpb24gKyBvcmlnaW4sIGRhdGEgPSBtcGcudHJhaW4pCnN1bW1hcnkocmVncmVzc29yMSkKYGBgCklmIHdlIHNldCBhIG91ciBgYGBTaWduaWYuIGNvZGVgYGAgdGhyZXNob2xkIGF0IDk1JSAoY2FsbCB0aGlzIHRoZSBsaW1pdCBmb3Igc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlKSwgd2UgY2FuIHN0YXJ0IGVsaW1pbmF0aW5nIGNvZWZmaWNpZW50cy4KVGhlIGhpZ2hlc3QgaXMgYWNjZWxlcmF0aW9uLCBzbyB3ZSBjYW4gc3RhcnQgYnkgcmVtb3ZpbmcgdGhhdC4KYGBge3IgbG0gcHQgMn0KcmVncmVzc29yMiA8LSBsbShmb3JtdWxhID0gbXBnIH4gY3lsaW5kZXJzICsgZGlzcGxhY2VtZW50ICsgaG9yc2Vwb3dlciArIHdlaWdodCAKICAgICAgICAgICAgICAgICsgb3JpZ2luLCBkYXRhID0gbXBnLnRyYWluKQpzdW1tYXJ5KHJlZ3Jlc3NvcjIpCmBgYApOb3cgd2UgY2FuIHB1bGwgZGlzcGxhY2VtZW50CmBgYHtyIGxtIHB0IDN9CnJlZ3Jlc3NvcjMgPC0gbG0oZm9ybXVsYSA9IG1wZyB+IGN5bGluZGVycyArIGhvcnNlcG93ZXIgKyB3ZWlnaHQgKyBvcmlnaW4sCiAgICAgICAgICAgICAgICAgZGF0YSA9IG1wZy50cmFpbikKc3VtbWFyeShyZWdyZXNzb3IzKQpgYGAKQmVjYXVzZSBgYGBvcmlnaW4zYGBgIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsIEkgd2lsbCBrZWVwIHRoZSBlbnRpcmUgYGBgb3JpZ2luYGBgIHZhcmlhYmxlLgoKTm93IHRoYXQgd2UgaGF2ZSBvdXIgcmVncmVzc29yLCB3ZSBjYW4gY3JlYXRlIG91ciBwcmVkaWN0aW9ucwpgYGB7ciBjcmVhdGluZyBwcmVkaWN0aW9uc30KcHJlZGljdG9yIDwtIHByZWRpY3QocmVncmVzc29yMywgbmV3ZGF0YSA9IG1wZy50ZXN0KQpgYGAKTm93IHdlIGNhbiBjcmVhdGUgYSBkYXRhZnJhbWUgdG8gbG9vayBhdCB0aGUgcmVzdWx0cwpgYGB7cn0KcmVzdWx0LmRhdGEgPC0gZGF0YS5mcmFtZShtb2RlbC55ZWFyID0gbXBnLnRlc3QkbW9kZWwueWVhciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdGlvbiA9IHByZWRpY3RvciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWN0dWFsID0gbXBnLnRlc3QkbXBnKQpwZXJjZW50LmRpZmYgPC0gYWJzKHJlc3VsdC5kYXRhJHByZWRpY3Rpb24gLSByZXN1bHQuZGF0YSRhY3R1YWwpIC8gCiAgcmVzdWx0LmRhdGEkYWN0dWFsICogMTAwCnJlc3VsdC5kYXRhJHBlcmNlbnQuZGlmZiA8LSBwZXJjZW50LmRpZmYKcmVtb3ZlKHBlcmNlbnQuZGlmZikKcGFzdGUoIlBlcmNlbnQgZGlmZmVyZW5jZToiLCByb3VuZChtZWFuKHJlc3VsdC5kYXRhJHBlcmNlbnQuZGlmZikpKQpyZXN1bHQuZGF0YSRwcmVkaWN0aW9uIDwtIHJvdW5kKHJlc3VsdC5kYXRhJHByZWRpY3Rpb24sIDIpCnJlc3VsdC5kYXRhJHBlcmNlbnQuZGlmZiA8LSByb3VuZChyZXN1bHQuZGF0YSRwZXJjZW50LmRpZmYsIDIpCnByaW50KHJlc3VsdC5kYXRhKQpgYGAKMTIgcGVyY2VudCBkaWZmZXJlbmNlIGlzbid0IHZlcnkgZ29vZC4gSXMgdGhlcmUgYSBiZXR0ZXIgd2F5IHRvIGRvIHRoaXM/CgpMZXQncyB0cnkgYSBkZWNpc2lvbiB0cmVlLgoKIyMgRGVjaXNpb24gdHJlZQpgYGB7ciBjcmVhdGluZyBvdXIgZGVjaXNpb24gdHJlZX0KcmVncmVzc29yNCA8LSBycGFydChmb3JtdWxhID0gbXBnIH4gLiwgZGF0YSA9IG1wZy50ZXN0KQpkdHByZWQgPC0gcHJlZGljdChyZWdyZXNzb3I0LCBkYXRhID0gbXBnLnRlc3QpCnBsb3QocmVncmVzc29yNCwgbWFpbiA9ICJEZWNpc2lvbiBUcmVlIFJlZ3Jlc3Npb24iKQpgYGAKTm93IGxldCdzIGV2YWx1YXRlIGhvdyBvdXIgbW9kZWwgcGVyZm9ybWVkOgpgYGB7cn0KcmVzdWx0LmRhdGEyIDwtIGRhdGEuZnJhbWUobW9kZWwueWVhciA9IG1wZy50ZXN0JG1vZGVsLnllYXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHByZWRpY3Rpb24gPSBkdHByZWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGFjdHVhbCA9IG1wZy50ZXN0JG1wZykKcGVyY2VudC5kaWZmIDwtIGFicyhyZXN1bHQuZGF0YTIkcHJlZGljdGlvbiAtIHJlc3VsdC5kYXRhMiRhY3R1YWwpIC8gCiAgcmVzdWx0LmRhdGEyJGFjdHVhbCAqIDEwMApyZXN1bHQuZGF0YTIkcGVyY2VudC5kaWZmIDwtIHBlcmNlbnQuZGlmZgpyZW1vdmUocGVyY2VudC5kaWZmKQpwYXN0ZSgiUGVyY2VudCBkaWZmZXJlbmNlOiIsIHJvdW5kKG1lYW4ocmVzdWx0LmRhdGEyJHBlcmNlbnQuZGlmZikpKQpyZXN1bHQuZGF0YTIkcHJlZGljdGlvbiA8LSByb3VuZChyZXN1bHQuZGF0YTIkcHJlZGljdGlvbiwgMikKcmVzdWx0LmRhdGEyJHBlcmNlbnQuZGlmZiA8LSByb3VuZChyZXN1bHQuZGF0YTIkcGVyY2VudC5kaWZmLCAyKQpwcmludChyZXN1bHQuZGF0YTIpCmBgYApUaGlzIDclIHJlc3VsdCBpcyBtdWNoIGJldHRlci4KCiMgRGlzY3Vzc2lvbgojIyBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbgpJIGNob3NlIGxpbmVhciByZWdyZXNzaW9uIHRvIHN0YXJ0IG9mZiB3aXRoIGJlY2F1c2UgSSBmZWVsIHRoYXQgaXQgaXMgb25lIG9mIHRoZSBtb3N0IGNvbW1vbiBhbmQgYmFzaWMgbWV0aG9kcyBvZiBwcmVkaWN0aXZlIGFuYWx5c2lzLgoiVGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGFuYWx5emVzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcmVzcG9uc2Ugb3IgZGVwZW5kZW50IHZhcmlhYmxlIGFuZCBhIHNldCBvZiBpbmRlcGVuZGVudCBvciBwcmVkaWN0b3IgdmFyaWFibGVzLiBUaGlzIHJlbGF0aW9uc2hpcCBpcyBleHByZXNzZWQgYXMgYW4gZXF1YXRpb24gdGhhdCBwcmVkaWN0cyB0aGUgcmVzcG9uc2UgdmFyaWFibGUgYXMgYSBsaW5lYXIgZnVuY3Rpb24gb2YgdGhlIHBhcmFtZXRlcnMuLi4gW3doaWNoXSBhcmUgYWRqdXN0ZWQgc28gdGhhdCBhIG1lYXN1cmUgb2YgZml0IGlzIG9wdGltaXplZCJbQHN0cmljMTRdCgpCYXNpY2FsbHksIGxpbmVhciByZWdyZXNzaW9uIHVzZXMgdGhlIG9sZCBnZW9tZXRyaWMgZm9ybXVsYSB0aGF0IHdlJ3ZlIGFsbCBiZWVuIHRhdWdodCwgCiQkCnkgPSBteCArIGIKJCQKCnRvIGxpbmVhcmx5IGZpdCBhICpkZXBlbmRlbnQgdmFyaWFibGUqICgkeSQpIHRvIGFuICppbmRlcGVuZGVudCogdmFyaWFibGUgKCR4JCkuIFRoaXMgbWVhc3VyZSBvZiBmaXQgZGlzY3Vzc2VkIGFib3ZlIGlzIG1lYXN1cmVkIGJ5IGEgcHJvcGVydHkga25vd24gYXMgdGhlICRSXjIkIHZhbHVlLCBhbmQgdGhlIGZpdCBpcyBjb25zaWRlcmVkIGJldHRlciB0aGUgY2xvc2VyIHRvIDEgaXQgaXMuIER1cmluZyB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gcG9ydGlvbiwgd2UgaGFkIGFuICRSXjIgXGFwcHJveCAwLjc1JCwgd2hpY2ggaXMgbm90IHZlcnkgZ29vZCAtIHRoZSBkYXRhIHdhcyBub3QgcmVsYXRlZCBsaW5lYXJseSwgYW5kIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3YXMgYSBwb29yIGNob2ljZSB0byB1c2UuCgpXZSBjYW4gbG9vayBhdCBzb21lIGRhdGEgdGhhdCBJIG9idGFpbmVkIGR1cmluZyBhIGZyZXNobWFuIHBoeXNpY3MgbGFiIGR1cmluZyBteSB1bmRlcmdyYWR1YXRlIGRlZ3JlZSBwcm9ncmFtLgpQYXJ0IG9mIHRoZSBhc3NpZ25tZW50IHdhcyB0byBtYW51YWxseSBwZXJmb3JtIGxpbmVhciByZWdyZXNzaW9uIG9uIHRoZSBkYXRhLCBzbyBJIHRob3VnaHQgaXQgd291bGQgYmUgaW50ZXJlc3RpbmcgdG8gcG9wIHRoZSBkYXRhIGludG8gZXhjZWwgYW5kIHRha2UgYSBsb29rIGF0IGl0LgpgYGB7cn0KZGF0YXNldCA8LSByZWFkX2NzdigiMmQuY3N2IikKeS5wcmVkIDwtIGxtKG1+LiwgZGF0YSA9IGRhdGFzZXQpCnBsb3QoeS5wcmVkKQpgYGAKCmBgYHtyfQpwbG90KHggPSBkYXRhc2V0JHYsIHkgPSBkYXRhc2V0JG0pCmBgYAoKIyMgRGVjaXNpb24gVHJlZSBMZWFybmluZwpEZWNpc2lvbiB0cmVlcyAidXRpbGl6ZSBhIHRyZWUgc3RydWN0dXJlIHRvIG1vZGVsIHRoZSByZWxhdGlvbnNoaXBzIGFtb25nIGZlYXR1cmVzIGFuZCB0aGUgcG90ZW50aWFsIG91dGNvbWVzLi4uIHRoaXMgc3RydWN0dXJlIGVhcm5lZCBpdHMgbmFtZSBkdWUgdG8gdGhlIGZhY3QgdGhhdCBpdCBtaXJyb3JzIGhvdyBhIGxpdGVyYWwgdHJlZSBiZWdpbnMgYXQgYSB3aWRlIHRydW5rLCB3aGljaCBpZiBmb2xsb3dlZCB1cHdhcmQsIHNwbGl0cyBpbnRvIG5hcnJvd2VyIGFuZCBuYXJyb3dlciBicmFuY2hlcy4gSW4gbXVjaCB0aGUgc2FtZSB3YXksIGEgZGVjaXNpb24gdHJlZSBjbGFzc2lmaWVyIHVzZXMgYSBzdHJ1Y3R1cmUgb2YgYnJhbmNoaW5nIGRlY2lzaW9ucyB3aGljaCBjaGFubmVsIGV4YW1wbGVzIGludG8gYQpmaW5hbCBwcmVkaWN0ZWQgY2xhc3MgdmFsdWUiW0BsYW50ejE1XQoKVGhlIG1haW4gcmVhc29uIEkgcGlja2VkIGEgZGVjaXNpb24gdHJlZSBtb2RlbCBpcyBiZWNhdXNlIEkgd2FudGVkIHRvIHVzZSBhIHJhbmRvbSBmb3Jlc3QgbW9kZWwgYnV0IFIgMy40LjMgKktpdGUtZWF0aW5nLXRyZWUqIHdpbGwgbm90IHJlY29nbml6ZSB0aGUgZmFjdCB0aGF0IEkgaGF2ZSBKYXZhIGluc3RhbGxlZCBhbmQgYXMgYSByZXN1bHQgSSBjYW5ub3QgdXNlIGBgYHJKYXZhYGBgIG9yIGFueSBvZiB0aGUgYXNzb2NpYXRlZCBwYWNrYWdlcyB0aGF0IHJlcXVpcmUgaXQuIEkgZGVjaWRlZCB0aGF0IHRoZSBuZXh0IGJlc3QgdGhpbmcgd291bGQgYmUgdG8gdXNlIGEgZGVjaXNpb24gdHJlZSBhbmQgYXMgYSByZXN1bHQgY2hvc2UgdGhhdC4KCiMjIFN0cmVuZ3RocyBhbmQgd2Vha25lc3NlcyBvZiBlYWNoIG1vZGVsCiMjIyBMaW5lYXIgUmVncmVzc2lvbgpMaW5lYXIgcmVncmVzc2lvbiBpcyBhIGdvb2QgbW9kZWwgdG8gdXNlIHdoZW4geW91IHdhbnQgZmFzdCwgc2ltcGxlIHJlc3VsdHMuIFdoZW4geW91IGhhdmUgb25lIGRlcGVuZGVudCBhbmQgb25lIGluZGVwZW5kZW50IHZhcmlhYmxlLCBsaW5lYXIgcmVncmVzc2lvbiBpcyBhIGdyZWF0IG1ldGhvZCB0byB1c2UuIFRoZXJlIGFyZSBvdGhlciBtb2RlbHMgdGhhdCBhcmUgY2xvc2VseSByZWxhdGVkIHRvIGxpbmVhciByZWdyZXNzaW9uLCBsaWtlIHBvbHlub21pYWwgcmVncmVzc2lvbiwgdGhhdCBjYW4gcHJvdmlkZSBhY2N1cmF0ZSByZXN1bHRzIGFzIHdlbGwuIAohW0xvb2sgYXQgdGhpcyBzd2VldCBncmFwaF0oZG93bmxvYWQucG5nKVtAYnJvd24xN10KCkl0IGlzIG5vdCB3aXRob3V0IGl0cyB3ZWFrbmVzc2VzLiBNb3JlIGNvbXBsaWNhdGVkIGRhdGFzZXRzLCBsaWtlIHRoZSBgYGBtcGdgYGAgZGF0YXNldCB3ZSBsb29rZWQgYXQgZWFybGllciwgZG8gbm90IGRvIHdlbGwgd2l0aCBsaW5lYXIgcmVncmVzc2lvbi4gTm9pc2UgZGF0YSBzZXRzIGNhbiBjb25mdXNlIHRoZSBtb2RlbHMgYXMgd2VsbC4KCiMjIyBEZWNpc2lvbiB0cmVlCkRlY2lzaW9uIHRyZWVzIGFyZSBncmVhdCBhdCBsb29raW5nIGJhY2sgYXQgbW9kZWxzLiBZb3UgY2FuIHRha2UgcHJpb3IgZGF0YSBhbmQgY3JlYXRlIGEgd2VsbC1tYWRlIG1vZGVsIHRoYXQgd2lsbCBjYXB0dXJlIGFsbCB0aGUgZGF0YSB3aXRoaW4gYmVmb3JlIGhhbmQgdGhhdCBjYW4gZWFzaWx5IHByb3ZpZGUgc29saWQgcmVzdWx0cyBhdCBwb2ludCB3aXRoaW4gdGhlIG1vZGVsLiBXaGVyZSB0aGUgbW9kZWwgY2FuIGZhaWwsIGhvd2V2ZXIsIGlzIHdoZW4gaXQgaXMgdXNlZCB0byBmb3JlY2FzdCBmdXR1cmUgcmVzdWx0cy4gCiFbU28gcmljaCwgc28gcHJldHR5LCB0aGUgYmVzdCBwaWVjZSBvZiBncmFwaCBpbiB0aGUgd2hvbGUgZGFtbiBjaXR5XShkb3dubG9hZDIucG5nKQoKQWxzbywgdGhlIG1vZGVscyB0YWtlIHNvbWUgdW5kZXJzdGFuZGluZyBhcyB0byBob3cgdG8gdHVuZSB0aGVtLiBUaGUgYWJvdmUgZ3JhcGggY2FwdHVyZXMgdGhlIGRhdGEgb25seSBhdCBvbmUgcG9pbnQgLSBhdCBhYm91dCAyMyB5ZWFycyBpbiwgdGhlIG1vZGVsIGlzIHBlcmZlY3QsIGJ1dCBhbGwgb3RoZXIgcG9pbnRzIGFyZSBjb21wbGV0ZWx5IGlnbm9yZWQuIEluIHRoaXMgc2l0dWF0aW9uLCB5b3Ugd291bGQgYmUgYmV0dGVyIG9mZiBhdmVyYWdpbmcgYWxsIHRoZSBkYXRhIHBvaW50cyB0byBnZXQgeW91ciBwcmVkaWN0aW9uLgoKQSBiZXR0ZXIgcmVzdWx0IGZvciB0aGUgc2FtZSBkYXRhIGNhbiBiZSBmb3VuZCBiZWxvdy4KIVtWMl0oZG93bmxvYWQzLnBuZykKCiMgQ29uY2x1c2lvbgoKVXNpbmcgdGhlIHR3byBtb2RlbHMgb2YgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIGRlY2lzaW9uIHRyZWVzIHdlIHdlcmUgYWJsZSB0byBwcmVkaWN0IHRoZSBtcGcgb2YgdmFyaW91cyBjbGFzc2ljIGNhcnMuIFRoYXQgYmVpbmcgc2FpZCwgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIG9ubHkgcGVyZm9ybWVkIDg4JSBlZmZpY2llbnRseS4gVGhlIGRlY2lzaW9uIHRyZWUgbW9kZWwgcGVyZm9ybWVkIGJldHRlciBhdCA5MyUgZWZmaWNpZW50bHksIGFuZCBJIHByZWRpY3QgdGhhdCB0aGUgdXNlIG9mIGEgcmFuZG9tIGZvcmVzdCBtb2RlbCB3b3VsZCBvbmx5IGluY3JlYXNlIHRoZXNlIHJlc3VsdHMuCgojIFJlZmVyZW5jZXMK