About

In this lab, we will focus on non-linear regression modeling, and linear programming. Linear regression modeling, as discussed in the previous lab, works with simple and multiple linear regression models. Sometimes the relationships are not best represented by linear models, and a non-linear regression modeling is required. The general concept remains the same; minimizing the error between the observed/actual values and the values predicted/fitted by the model. Linear programming on the other hands seek to find the optimal solution to a problem with multivariables and multiconstraints described by linear relationships, as opposed to non-linear relationships. The latter would be a non-linear programming, a case not covered in this class.

In this lab, we will perform a non-linear regression modeling on the cost of servers, and setup a linear programming model to solve the marketing use case discussed in class.

Setup

Remember to always set your working directory to the source file location. Go to ‘Session’, scroll down to ‘Set Working Directory’, and click ‘To Source File Location’. Read carefully the below and follow the instructions to complete the tasks and answer any questions. Submit your work to RPubs as detailed in previous notes.

Note

For your assignment you may be using different data sets than what is included here. Always read carefully the instructions on Sakai. For clarity, tasks/questions to be completed/answered are highlighted in red color (visisble only in preview mode) and numbered according to their particular placement in the task section. Quite often you will need to add your own code chunk.

Execute all code chunks, preview, publish, and submit link on Sakai.


Task 1: Non-Linear Regression Modeling

First, we must read the file ‘ServersCost.csv’ into R, and extract the two columns of interest.

mydata <- read.csv("data/serverscost.csv", header=TRUE)
head(mydata)
servers = mydata$servers
cost = mydata$cost

We start by creating a simple linear regression model. Next, we plot the points to visually inspect the data and unravel any potential relationships.

linear_model = lm(cost ~ servers)
summary(linear_model)

Call:
lm(formula = cost ~ servers)

Residuals:
     Min       1Q   Median       3Q      Max 
-10646.2  -8646.2   -544.7   7066.0  12858.8 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)   
(Intercept)  14747.2     4035.5   3.654  0.00181 **
servers         48.0      336.9   0.142  0.88828   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 8687 on 18 degrees of freedom
Multiple R-squared:  0.001127,  Adjusted R-squared:  -0.05437 
F-statistic: 0.0203 on 1 and 18 DF,  p-value: 0.8883
# add linear line based on regression model
plot(servers,cost, pch=16) # the pch option is to accentuate the points
abline(linear_model, col="blue", lwd=2)

The blue line here represents the model based predicted data, and the black dots are the actual data points. Clearly, from the qualitative visual inspection and the quantitative \(R^2\) and \(AdjustedR^2\) the linear model is far from being a good fit or predictor. Next we will use a nonlinear quadratic model to see how the model can be improved.

A linear model is of the form \(y\) ~ \(x_0 + x_1 +....x_n\) where \(y\) is the dependent variable and the \(x_n\) are the independent variables. For the non-linear quadratic regression model we are looking for an equation of the form \(y\) ~ \(x + x^2\)

# First it is best to define a new variable which is the squared value of servers
servers2 = servers^2
# The model formula is based on the form y = x + x^2
quad_model = lm(cost ~ servers + servers2)
summary(quad_model)

Call:
lm(formula = cost ~ servers + servers2)

Residuals:
    Min      1Q  Median      3Q     Max 
-2897.8 -1553.4  -513.2  1152.4  4752.7 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 35417.77    1742.64   20.32 2.30e-13 ***
servers     -5589.43     382.19  -14.62 4.62e-11 ***
servers2      268.45      17.68   15.19 2.55e-11 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2342 on 17 degrees of freedom
Multiple R-squared:  0.9314,    Adjusted R-squared:  0.9233 
F-statistic: 115.4 on 2 and 17 DF,  p-value: 1.282e-10

From the summary, we see the R-squared value has increased dramatically from the linear model, indicating a big improvement. That is not the only value we must check though. Let’s inspect visually how the model based data compare to the actual data.

First, we must calculate the predicted/fitted values. Then, we can plot the predicted points next to the actual values.

# Compute the predicted values based on the quad model using the R-function predict()
predicted2 = predict(quad_model,data=mydata)
# Plot cost versus servers based on actual values using a striked symbol
plot(servers,cost, pch=16) 
# The par (parameter setting) command is needed to overlay the predicted model based values without the labels and annotations
par(new=TRUE, xaxt="n", yaxt="n", ann=FALSE) 
# Use the red color for the quadratic model
plot(predicted2, col="red", pch=16) 

It is easy to observe now that the predicted values are more in line with the observed values.

A common misconception is that the higher order the non-linear model is, the better predictive it is. Remember from the previous lab and class sessions the need to distinguish between \(R^2\) which is a measure of how good fitting the model is and \(AdjustedR^2\) which is a measure of how good predicting the model is. Lets try a cubic model and see how it performs. The model takes now the form \(y\) ~ \(x + x^2 + x^3\).

##### 1A) Fill-in the code chunk below to derive a cubic non-linear regression model, and display the summary statistics.

#First define the additional new variable. the cubic of servers, needed for your model
servers3 = servers^3
# The model formula is of the form x + x^2 + x^3.  For consistency best to call your new model cubic_model.
cubic_model =  lm(cost ~ servers + servers2 + servers3)
summary(cubic_model)

Call:
lm(formula = cost ~ servers + servers2 + servers3)

Residuals:
    Min      1Q  Median      3Q     Max 
-2871.0 -1435.1  -473.6  1271.8  4600.3 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 36133.696   2625.976  13.760 2.77e-10 ***
servers     -5954.738   1056.596  -5.636 3.72e-05 ***
servers2      310.895    115.431   2.693    0.016 *  
servers3       -1.347      3.619  -0.372    0.715    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2404 on 16 degrees of freedom
Multiple R-squared:  0.932, Adjusted R-squared:  0.9193 
F-statistic: 73.11 on 3 and 16 DF,  p-value: 1.478e-09

We can next visually inspect the goodness of the quadratic model and the cubic model.

##### 1B) Graph a plot of cost versus servers based on actual data. Overlay the predicted values based on the cubic model, and on the quadratic model. Follow the sequence of commands described in the code chunk below.

# compute the predicted values based on the cubic model. For consistency with the previous example best to call your model predicted3.
predicted3 = predict( cubic_model, data=mydata)
plot (servers, cost, pch=16)

# Plot cost versus servers based on actual values
plot(servers,  cost, pch=16)
# The par (parameter setting) command is needed to overlay the predicted model based values without the labels and annotations
par(new=TRUE, xaxt="n", yaxt="n", ann=FALSE)
# Use the color `green`  to plot the predicted points for the cubic model
plot(predicted3, col="green", pch=16)   
# The par (parameter setting) command is needed again to overlay the predicted model based values without the labels and annotations
par(new=TRUE, xaxt="n", yaxt="n", ann=FALSE) 
# Use the color `red` to plot the predicted poiints for the quadratic model
plot(predicted2, col="red", pch=16) 

From the graphs it should be hard to tell which of the two models, the quadratic or the cubic, is a better fit and predictor. A good way to quantify which model is best in predicting, is to look at the \(AdjustedR^2\).

##### 1C) List here the R2 and Adjusted R2 for all three models linear, quandratic, and cubic. Identify which model is a better predictor and which model is a better fit. Explain why. Linear: Multiple R^2: 0.001127 Linear Adjusted R^2: -0.05437 Quadratic: Multiple R^2: 0.9314 Quadratic Adjusted R^2: 0.9233 Cubic: Multiple R^2: 0.932 Cubic Adjusted R^2: 0.9193 Cubic Adjusted R^2 because its adjusted and has the most input data ———-

Task 2: Linear Programming & Optimization

For this task, we need to install an optimization package in R.

# Require will load the package only if not installed 
# Dependencies = TRUE makes sure that dependencies are install
if(!require("lpSolveAPI",quietly = TRUE))
  install.packages("lpSolveAPI",dependencies = TRUE, repos = "https://cloud.r-project.org")

Solving Marketing Model

We will solve for the marketing use case discussed in class. First create the linear programming model object in R. This is the starting point. An object is like a container and will eventually contain all the definitions for objective function, constraints and optimized results.

# We start with `0` constraint and `2` decision variables. The object name `lpmark` is discretionary.
lpmark <- make.lp(0, 2) 

Next we need to define the type of optimization, set the objective function, and add the constraints to our model object. This is done by using different commands applicable only to the created linear programming model object.

# Define type of optimization as maximum and to avoid the unnecessary screen outputs in the worksheet dump the screen output into a variable called `dump`
dump = lp.control(lpmark, sense="max")  
# Set the objective function with the proper coefficients associated with the decision variables
set.objfn(lpmark, c(275.691, 48.341))
# add a constraint for the maximum allowed budget of $350K
add.constraint(lpmark, c(1, 1), "<=", 350000)
add.constraint(lpmark, c(1, 0), ">=", 150000)
add.constraint(lpmark, c(0, 1), ">=", 750000)
add.constraint(lpmark, c(2, -1), "=", 0)
add.constraint(lpmark, c(1, 0), ">=", 0)
add.constraint(lpmark, c(0, 1), ">=", 0)

##### 2A) Insert in the above code chunk the remaining five constraints corresponding to the marketing model. Follow the guidelines as described in the class PPT slides.

Finally we can explore, solve the model, and report results using additional commands part of the lpSolveAPI package and applicable only to our created lpmark linear programming object.

# View the problem formulation in tabular/matrix form
lpmark
Model name: 
               C1       C2            
Maximize  275.691   48.341            
R1              1        1  <=  350000
R2              1        0  >=  150000
R3              0        1  >=  750000
R4              2       -1   =       0
R5              1        0  >=       0
R6              0        1  >=       0
Kind          Std      Std            
Type         Real     Real            
Upper         Inf      Inf            
Lower           0        0            
# Solve 
solve(lpmark) 
[1] 2
# Display the objective function optimum value
get.objective(lpmark)
[1] -1e+30
# Display the decision variables optimum values
get.variables(lpmark) 
[1]  0.000000e+00 6.953276e-310

##### 2B) Clearly mark the optimum values for sales, radio, and tv ads. Show how the optimum solution satisfy all six constraints by substituting for the decision variables and numerically validating each case. Ideal sales $43,443,517 radio ads: $116,666.70 tv ads: 23,333.30 Radio:116,666.70 + TV:233,333.30 = 350,000 is less than or equal to 350,000 116,666.70 invested in radio > 15000 233,333.30 invested in tv > 75,000 233,333.30 = 2 * 116,666.70 twice as much tv as on radio

LS0tCnRpdGxlOiAiQlNBRDM0MyBGYWxsIDIwMTggTGFiIFdvcmtzaGVldCAwNyIKYXV0aG9yOiAiS2FyZWVtIEJhemFyYWEiCmRhdGU6ICIxMC8zMS8xOCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CnN1YnRpdGxlOiBOb24tTGluZWFyIFJlZ3Jlc3Npb24gJiBMaW5lYXIgUHJvZ3JhbW1pbmcgKGJzYWQtbGFiMDcpCi0tLQoKIyMjIEFib3V0CgpJbiB0aGlzIGxhYiwgd2Ugd2lsbCBmb2N1cyBvbiBub24tbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxpbmcsIGFuZCBsaW5lYXIgcHJvZ3JhbW1pbmcuIExpbmVhciByZWdyZXNzaW9uIG1vZGVsaW5nLCBhcyBkaXNjdXNzZWQgaW4gdGhlIHByZXZpb3VzIGxhYiwgd29ya3Mgd2l0aCBzaW1wbGUgYW5kIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscy4gU29tZXRpbWVzIHRoZSByZWxhdGlvbnNoaXBzIGFyZSBub3QgYmVzdCByZXByZXNlbnRlZCBieSBsaW5lYXIgbW9kZWxzLCBhbmQgYSBub24tbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxpbmcgaXMgcmVxdWlyZWQuIFRoZSBnZW5lcmFsIGNvbmNlcHQgcmVtYWlucyB0aGUgc2FtZTsgbWluaW1pemluZyB0aGUgZXJyb3IgYmV0d2VlbiB0aGUgb2JzZXJ2ZWQvYWN0dWFsIHZhbHVlcyBhbmQgdGhlIHZhbHVlcyBwcmVkaWN0ZWQvZml0dGVkIGJ5IHRoZSBtb2RlbC4gTGluZWFyIHByb2dyYW1taW5nIG9uIHRoZSBvdGhlciBoYW5kcyBzZWVrIHRvIGZpbmQgdGhlIG9wdGltYWwgc29sdXRpb24gdG8gYSBwcm9ibGVtIHdpdGggbXVsdGl2YXJpYWJsZXMgYW5kIG11bHRpY29uc3RyYWludHMgZGVzY3JpYmVkIGJ5IGxpbmVhciByZWxhdGlvbnNoaXBzLCBhcyBvcHBvc2VkIHRvIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwcy4gVGhlIGxhdHRlciB3b3VsZCBiZSBhIG5vbi1saW5lYXIgcHJvZ3JhbW1pbmcsIGEgY2FzZSBub3QgY292ZXJlZCBpbiB0aGlzIGNsYXNzLgoKSW4gdGhpcyBsYWIsIHdlIHdpbGwgcGVyZm9ybSBhIG5vbi1saW5lYXIgcmVncmVzc2lvbiBtb2RlbGluZyBvbiB0aGUgY29zdCBvZiBzZXJ2ZXJzLCBhbmQgc2V0dXAgYSBsaW5lYXIgcHJvZ3JhbW1pbmcgbW9kZWwgdG8gc29sdmUgdGhlIG1hcmtldGluZyB1c2UgY2FzZSBkaXNjdXNzZWQgaW4gY2xhc3MuCgojIyMgU2V0dXAKClJlbWVtYmVyIHRvIGFsd2F5cyBzZXQgeW91ciB3b3JraW5nIGRpcmVjdG9yeSB0byB0aGUgc291cmNlIGZpbGUgbG9jYXRpb24uIEdvIHRvICdTZXNzaW9uJywgc2Nyb2xsIGRvd24gdG8gJ1NldCBXb3JraW5nIERpcmVjdG9yeScsIGFuZCBjbGljayAnVG8gU291cmNlIEZpbGUgTG9jYXRpb24nLiBSZWFkIGNhcmVmdWxseSB0aGUgYmVsb3cgYW5kIGZvbGxvdyB0aGUgaW5zdHJ1Y3Rpb25zIHRvIGNvbXBsZXRlIHRoZSB0YXNrcyBhbmQgYW5zd2VyIGFueSBxdWVzdGlvbnMuICBTdWJtaXQgeW91ciB3b3JrIHRvIFJQdWJzIGFzIGRldGFpbGVkIGluIHByZXZpb3VzIG5vdGVzLiAKCiMjIyBOb3RlCgpGb3IgeW91ciBhc3NpZ25tZW50IHlvdSBtYXkgYmUgdXNpbmcgZGlmZmVyZW50IGRhdGEgc2V0cyB0aGFuIHdoYXQgaXMgaW5jbHVkZWQgaGVyZS4gQWx3YXlzIHJlYWQgY2FyZWZ1bGx5IHRoZSBpbnN0cnVjdGlvbnMgb24gU2FrYWkuICBGb3IgY2xhcml0eSwgdGFza3MvcXVlc3Rpb25zIHRvIGJlIGNvbXBsZXRlZC9hbnN3ZXJlZCBhcmUgaGlnaGxpZ2h0ZWQgaW4gcmVkIGNvbG9yICh2aXNpc2JsZSBvbmx5IGluIHByZXZpZXcgbW9kZSkgYW5kIG51bWJlcmVkIGFjY29yZGluZyB0byB0aGVpciBwYXJ0aWN1bGFyIHBsYWNlbWVudCBpbiB0aGUgdGFzayBzZWN0aW9uLiAgUXVpdGUgb2Z0ZW4geW91IHdpbGwgbmVlZCB0byBhZGQgeW91ciBvd24gY29kZSBjaHVuay4KCkV4ZWN1dGUgYWxsIGNvZGUgY2h1bmtzLCBwcmV2aWV3LCBwdWJsaXNoLCBhbmQgc3VibWl0IGxpbmsgb24gU2FrYWkuCgotLS0tLS0tLS0tLS0tLQoKCiMjIyBUYXNrIDE6IE5vbi1MaW5lYXIgUmVncmVzc2lvbiBNb2RlbGluZwoKRmlyc3QsIHdlIG11c3QgcmVhZCB0aGUgZmlsZSAnU2VydmVyc0Nvc3QuY3N2JyBpbnRvIFIsIGFuZCBleHRyYWN0IHRoZSB0d28gY29sdW1ucyBvZiBpbnRlcmVzdC4gCgpgYGB7cn0KbXlkYXRhIDwtIHJlYWQuY3N2KCJkYXRhL3NlcnZlcnNjb3N0LmNzdiIsIGhlYWRlcj1UUlVFKQpoZWFkKG15ZGF0YSkKYGBgCgpgYGB7cn0Kc2VydmVycyA9IG15ZGF0YSRzZXJ2ZXJzCmNvc3QgPSBteWRhdGEkY29zdApgYGAKCldlIHN0YXJ0IGJ5IGNyZWF0aW5nIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBOZXh0LCB3ZSBwbG90IHRoZSBwb2ludHMgdG8gdmlzdWFsbHkgaW5zcGVjdCB0aGUgZGF0YSBhbmQgdW5yYXZlbCBhbnkgcG90ZW50aWFsIHJlbGF0aW9uc2hpcHMuIAoKYGBge3J9CmxpbmVhcl9tb2RlbCA9IGxtKGNvc3QgfiBzZXJ2ZXJzKQpzdW1tYXJ5KGxpbmVhcl9tb2RlbCkKYGBgCgpgYGB7cn0KIyBhZGQgbGluZWFyIGxpbmUgYmFzZWQgb24gcmVncmVzc2lvbiBtb2RlbApwbG90KHNlcnZlcnMsY29zdCwgcGNoPTE2KSAjIHRoZSBwY2ggb3B0aW9uIGlzIHRvIGFjY2VudHVhdGUgdGhlIHBvaW50cwphYmxpbmUobGluZWFyX21vZGVsLCBjb2w9ImJsdWUiLCBsd2Q9MikKYGBgCgpUaGUgYmx1ZSBsaW5lIGhlcmUgcmVwcmVzZW50cyB0aGUgbW9kZWwgYmFzZWQgcHJlZGljdGVkIGRhdGEsICBhbmQgdGhlIGJsYWNrIGRvdHMgYXJlIHRoZSBhY3R1YWwgZGF0YSBwb2ludHMuIENsZWFybHksIGZyb20gdGhlIHF1YWxpdGF0aXZlIHZpc3VhbCBpbnNwZWN0aW9uIGFuZCB0aGUgcXVhbnRpdGF0aXZlICRSXjIkIGFuZCAkQWRqdXN0ZWRSXjIkIHRoZSBsaW5lYXIgbW9kZWwgaXMgZmFyIGZyb20gYmVpbmcgYSBnb29kIGZpdCBvciBwcmVkaWN0b3IuIE5leHQgd2Ugd2lsbCB1c2UgYSBub25saW5lYXIgcXVhZHJhdGljIG1vZGVsIHRvIHNlZSBob3cgdGhlIG1vZGVsIGNhbiBiZSBpbXByb3ZlZC4KCkEgbGluZWFyIG1vZGVsIGlzIG9mIHRoZSBmb3JtICR5JCB+ICR4XzAgKyB4XzEgKy4uLi54X24kIHdoZXJlICR5JCBpcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGFuZCB0aGUgJHhfbiQgYXJlIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIEZvciB0aGUgbm9uLWxpbmVhciBxdWFkcmF0aWMgcmVncmVzc2lvbiBtb2RlbCB3ZSBhcmUgbG9va2luZyBmb3IgYW4gZXF1YXRpb24gb2YgdGhlIGZvcm0gJHkkIH4gJHggKyB4XjIkCgpgYGB7cn0KIyBGaXJzdCBpdCBpcyBiZXN0IHRvIGRlZmluZSBhIG5ldyB2YXJpYWJsZSB3aGljaCBpcyB0aGUgc3F1YXJlZCB2YWx1ZSBvZiBzZXJ2ZXJzCnNlcnZlcnMyID0gc2VydmVyc14yCgojIFRoZSBtb2RlbCBmb3JtdWxhIGlzIGJhc2VkIG9uIHRoZSBmb3JtIHkgPSB4ICsgeF4yCnF1YWRfbW9kZWwgPSBsbShjb3N0IH4gc2VydmVycyArIHNlcnZlcnMyKQpzdW1tYXJ5KHF1YWRfbW9kZWwpCmBgYApGcm9tIHRoZSBzdW1tYXJ5LCB3ZSBzZWUgdGhlIFItc3F1YXJlZCB2YWx1ZSBoYXMgaW5jcmVhc2VkIGRyYW1hdGljYWxseSBmcm9tIHRoZSBsaW5lYXIgbW9kZWwsIGluZGljYXRpbmcgYSBiaWcgaW1wcm92ZW1lbnQuIFRoYXQgaXMgbm90IHRoZSBvbmx5IHZhbHVlIHdlIG11c3QgY2hlY2sgdGhvdWdoLiBMZXQncyBpbnNwZWN0IHZpc3VhbGx5IGhvdyB0aGUgbW9kZWwgYmFzZWQgZGF0YSBjb21wYXJlIHRvIHRoZSBhY3R1YWwgZGF0YS4KCkZpcnN0LCB3ZSBtdXN0IGNhbGN1bGF0ZSB0aGUgcHJlZGljdGVkL2ZpdHRlZCB2YWx1ZXMuIFRoZW4sIHdlIGNhbiBwbG90IHRoZSBwcmVkaWN0ZWQgcG9pbnRzIG5leHQgdG8gdGhlIGFjdHVhbCB2YWx1ZXMuCmBgYHtyfQojIENvbXB1dGUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgYmFzZWQgb24gdGhlIHF1YWQgbW9kZWwgdXNpbmcgdGhlIFItZnVuY3Rpb24gcHJlZGljdCgpCnByZWRpY3RlZDIgPSBwcmVkaWN0KHF1YWRfbW9kZWwsZGF0YT1teWRhdGEpCgojIFBsb3QgY29zdCB2ZXJzdXMgc2VydmVycyBiYXNlZCBvbiBhY3R1YWwgdmFsdWVzIHVzaW5nIGEgc3RyaWtlZCBzeW1ib2wKcGxvdChzZXJ2ZXJzLGNvc3QsIHBjaD0xNikgCgojIFRoZSBwYXIgKHBhcmFtZXRlciBzZXR0aW5nKSBjb21tYW5kIGlzIG5lZWRlZCB0byBvdmVybGF5IHRoZSBwcmVkaWN0ZWQgbW9kZWwgYmFzZWQgdmFsdWVzIHdpdGhvdXQgdGhlIGxhYmVscyBhbmQgYW5ub3RhdGlvbnMKcGFyKG5ldz1UUlVFLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGFubj1GQUxTRSkgCgojIFVzZSB0aGUgcmVkIGNvbG9yIGZvciB0aGUgcXVhZHJhdGljIG1vZGVsCnBsb3QocHJlZGljdGVkMiwgY29sPSJyZWQiLCBwY2g9MTYpIApgYGAKCkl0IGlzIGVhc3kgdG8gb2JzZXJ2ZSBub3cgdGhhdCB0aGUgcHJlZGljdGVkIHZhbHVlcyBhcmUgbW9yZSBpbiBsaW5lIHdpdGggdGhlIG9ic2VydmVkIHZhbHVlcy4KCkEgY29tbW9uIG1pc2NvbmNlcHRpb24gaXMgdGhhdCB0aGUgaGlnaGVyIG9yZGVyIHRoZSBub24tbGluZWFyIG1vZGVsIGlzLCB0aGUgYmV0dGVyIHByZWRpY3RpdmUgaXQgaXMuIFJlbWVtYmVyIGZyb20gdGhlIHByZXZpb3VzIGxhYiBhbmQgY2xhc3Mgc2Vzc2lvbnMgdGhlIG5lZWQgdG8gZGlzdGluZ3Vpc2ggYmV0d2VlbiAkUl4yJCB3aGljaCBpcyBhIG1lYXN1cmUgb2YgaG93IGdvb2QgZml0dGluZyB0aGUgbW9kZWwgaXMgYW5kICRBZGp1c3RlZFJeMiQgd2hpY2ggaXMgYSBtZWFzdXJlIG9mIGhvdyBnb29kIHByZWRpY3RpbmcgdGhlIG1vZGVsIGlzLiBMZXRzIHRyeSBhIGN1YmljIG1vZGVsIGFuZCBzZWUgaG93IGl0IHBlcmZvcm1zLiBUaGUgbW9kZWwgdGFrZXMgbm93IHRoZSBmb3JtICR5JCB+ICR4ICsgeF4yICsgeF4zJC4gCgo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4KIyMjIyMgMUEpIEZpbGwtaW4gdGhlIGNvZGUgY2h1bmsgYmVsb3cgdG8gZGVyaXZlIGEgY3ViaWMgbm9uLWxpbmVhciByZWdyZXNzaW9uIG1vZGVsLCBhbmQgZGlzcGxheSB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzLgo8L3NwYW4+CgpgYGB7cn0KI0ZpcnN0IGRlZmluZSB0aGUgYWRkaXRpb25hbCBuZXcgdmFyaWFibGUuIHRoZSBjdWJpYyBvZiBzZXJ2ZXJzLCBuZWVkZWQgZm9yIHlvdXIgbW9kZWwKc2VydmVyczMgPSBzZXJ2ZXJzXjMKIyBUaGUgbW9kZWwgZm9ybXVsYSBpcyBvZiB0aGUgZm9ybSB4ICsgeF4yICsgeF4zLiAgRm9yIGNvbnNpc3RlbmN5IGJlc3QgdG8gY2FsbCB5b3VyIG5ldyBtb2RlbCBjdWJpY19tb2RlbC4KY3ViaWNfbW9kZWwgPSAgbG0oY29zdCB+IHNlcnZlcnMgKyBzZXJ2ZXJzMiArIHNlcnZlcnMzKQpzdW1tYXJ5KGN1YmljX21vZGVsKQpgYGAKCldlIGNhbiBuZXh0IHZpc3VhbGx5IGluc3BlY3QgdGhlIGdvb2RuZXNzIG9mIHRoZSBxdWFkcmF0aWMgbW9kZWwgYW5kIHRoZSBjdWJpYyBtb2RlbC4KCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPgojIyMjIyAxQikgR3JhcGggYSBwbG90IG9mIGNvc3QgdmVyc3VzIHNlcnZlcnMgYmFzZWQgb24gYWN0dWFsIGRhdGEuIE92ZXJsYXkgdGhlIHByZWRpY3RlZCB2YWx1ZXMgYmFzZWQgb24gdGhlIGN1YmljIG1vZGVsLCBhbmQgb24gdGhlIHF1YWRyYXRpYyBtb2RlbC4gRm9sbG93IHRoZSBzZXF1ZW5jZSBvZiBjb21tYW5kcyBkZXNjcmliZWQgaW4gdGhlIGNvZGUgY2h1bmsgYmVsb3cuIAo8L3NwYW4+CgpgYGB7cn0KIyBjb21wdXRlIHRoZSBwcmVkaWN0ZWQgdmFsdWVzIGJhc2VkIG9uIHRoZSBjdWJpYyBtb2RlbC4gRm9yIGNvbnNpc3RlbmN5IHdpdGggdGhlIHByZXZpb3VzIGV4YW1wbGUgYmVzdCB0byBjYWxsIHlvdXIgbW9kZWwgcHJlZGljdGVkMy4KcHJlZGljdGVkMyA9IHByZWRpY3QoIGN1YmljX21vZGVsLCBkYXRhPW15ZGF0YSkKcGxvdCAoc2VydmVycywgY29zdCwgcGNoPTE2KQojIFBsb3QgY29zdCB2ZXJzdXMgc2VydmVycyBiYXNlZCBvbiBhY3R1YWwgdmFsdWVzCnBsb3Qoc2VydmVycywgIGNvc3QsIHBjaD0xNikKCiMgVGhlIHBhciAocGFyYW1ldGVyIHNldHRpbmcpIGNvbW1hbmQgaXMgbmVlZGVkIHRvIG92ZXJsYXkgdGhlIHByZWRpY3RlZCBtb2RlbCBiYXNlZCB2YWx1ZXMgd2l0aG91dCB0aGUgbGFiZWxzIGFuZCBhbm5vdGF0aW9ucwpwYXIobmV3PVRSVUUsIHhheHQ9Im4iLCB5YXh0PSJuIiwgYW5uPUZBTFNFKQojIFVzZSB0aGUgY29sb3IgYGdyZWVuYCAgdG8gcGxvdCB0aGUgcHJlZGljdGVkIHBvaW50cyBmb3IgdGhlIGN1YmljIG1vZGVsCnBsb3QocHJlZGljdGVkMywgY29sPSJncmVlbiIsIHBjaD0xNikgICAKIyBUaGUgcGFyIChwYXJhbWV0ZXIgc2V0dGluZykgY29tbWFuZCBpcyBuZWVkZWQgYWdhaW4gdG8gb3ZlcmxheSB0aGUgcHJlZGljdGVkIG1vZGVsIGJhc2VkIHZhbHVlcyB3aXRob3V0IHRoZSBsYWJlbHMgYW5kIGFubm90YXRpb25zCnBhcihuZXc9VFJVRSwgeGF4dD0ibiIsIHlheHQ9Im4iLCBhbm49RkFMU0UpIAojIFVzZSB0aGUgY29sb3IgYHJlZGAgdG8gcGxvdCB0aGUgcHJlZGljdGVkIHBvaWludHMgZm9yIHRoZSBxdWFkcmF0aWMgbW9kZWwKcGxvdChwcmVkaWN0ZWQyLCBjb2w9InJlZCIsIHBjaD0xNikgCmBgYAoKRnJvbSB0aGUgZ3JhcGhzIGl0IHNob3VsZCBiZSBoYXJkIHRvIHRlbGwgd2hpY2ggb2YgdGhlIHR3byBtb2RlbHMsIHRoZSBxdWFkcmF0aWMgb3IgdGhlIGN1YmljLCBpcyBhIGJldHRlciBmaXQgYW5kIHByZWRpY3Rvci4KQSBnb29kIHdheSB0byBxdWFudGlmeSB3aGljaCBtb2RlbCBpcyBiZXN0IGluIHByZWRpY3RpbmcsIGlzIHRvIGxvb2sgYXQgdGhlICRBZGp1c3RlZFJeMiQuCgo8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4KIyMjIyMgMUMpIExpc3QgaGVyZSB0aGUgUjIgYW5kIEFkanVzdGVkIFIyIGZvciBhbGwgdGhyZWUgbW9kZWxzIGxpbmVhciwgcXVhbmRyYXRpYywgYW5kIGN1YmljLiAgSWRlbnRpZnkgd2hpY2ggbW9kZWwgaXMgYSBiZXR0ZXIgcHJlZGljdG9yIGFuZCB3aGljaCBtb2RlbCBpcyBhIGJldHRlciBmaXQuIEV4cGxhaW4gd2h5Lgo8L3NwYW4+CkxpbmVhcjogTXVsdGlwbGUgUl4yOiAwLjAwMTEyNwpMaW5lYXIgQWRqdXN0ZWQgUl4yOiAtMC4wNTQzNyAKUXVhZHJhdGljOiBNdWx0aXBsZSBSXjI6IDAuOTMxNApRdWFkcmF0aWMgQWRqdXN0ZWQgUl4yOiAwLjkyMzMgCkN1YmljOiBNdWx0aXBsZSBSXjI6IDAuOTMyCkN1YmljIEFkanVzdGVkIFJeMjogMC45MTkzCkN1YmljIEFkanVzdGVkIFJeMiBiZWNhdXNlIGl0cyBhZGp1c3RlZCBhbmQgaGFzIHRoZSBtb3N0IGlucHV0IGRhdGEgCi0tLS0tLS0tLS0KCiMjIyBUYXNrIDI6IExpbmVhciBQcm9ncmFtbWluZyAmIE9wdGltaXphdGlvbgoKRm9yIHRoaXMgdGFzaywgd2UgbmVlZCB0byBpbnN0YWxsIGFuIG9wdGltaXphdGlvbiBwYWNrYWdlIGluIFIuIAoKYGBge3J9CgojIFJlcXVpcmUgd2lsbCBsb2FkIHRoZSBwYWNrYWdlIG9ubHkgaWYgbm90IGluc3RhbGxlZCAKIyBEZXBlbmRlbmNpZXMgPSBUUlVFIG1ha2VzIHN1cmUgdGhhdCBkZXBlbmRlbmNpZXMgYXJlIGluc3RhbGwKaWYoIXJlcXVpcmUoImxwU29sdmVBUEkiLHF1aWV0bHkgPSBUUlVFKSkKICBpbnN0YWxsLnBhY2thZ2VzKCJscFNvbHZlQVBJIixkZXBlbmRlbmNpZXMgPSBUUlVFLCByZXBvcyA9ICJodHRwczovL2Nsb3VkLnItcHJvamVjdC5vcmciKQoKYGBgCgoKIyMjIyBTb2x2aW5nIE1hcmtldGluZyBNb2RlbAoKV2Ugd2lsbCBzb2x2ZSBmb3IgdGhlIG1hcmtldGluZyB1c2UgY2FzZSBkaXNjdXNzZWQgaW4gY2xhc3MuIEZpcnN0IGNyZWF0ZSB0aGUgbGluZWFyIHByb2dyYW1taW5nIG1vZGVsIG9iamVjdCBpbiBSLiAgVGhpcyBpcyB0aGUgc3RhcnRpbmcgcG9pbnQuICBBbiBvYmplY3QgaXMgbGlrZSBhIGNvbnRhaW5lciBhbmQgd2lsbCBldmVudHVhbGx5IGNvbnRhaW4gYWxsIHRoZSBkZWZpbml0aW9ucyBmb3Igb2JqZWN0aXZlIGZ1bmN0aW9uLCBjb25zdHJhaW50cyBhbmQgb3B0aW1pemVkIHJlc3VsdHMuCgpgYGB7cn0KIyBXZSBzdGFydCB3aXRoIGAwYCBjb25zdHJhaW50IGFuZCBgMmAgZGVjaXNpb24gdmFyaWFibGVzLiBUaGUgb2JqZWN0IG5hbWUgYGxwbWFya2AgaXMgZGlzY3JldGlvbmFyeS4KbHBtYXJrIDwtIG1ha2UubHAoMCwgMikgCmBgYAoKTmV4dCB3ZSBuZWVkIHRvIGRlZmluZSB0aGUgdHlwZSBvZiBvcHRpbWl6YXRpb24sICBzZXQgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiwgYW5kIGFkZCB0aGUgY29uc3RyYWludHMgdG8gb3VyIG1vZGVsIG9iamVjdC4gIFRoaXMgaXMgZG9uZSBieSB1c2luZyBkaWZmZXJlbnQgY29tbWFuZHMgYXBwbGljYWJsZSBvbmx5IHRvIHRoZSBjcmVhdGVkIGxpbmVhciBwcm9ncmFtbWluZyBtb2RlbCBvYmplY3QuCgpgYGB7cn0KIyBEZWZpbmUgdHlwZSBvZiBvcHRpbWl6YXRpb24gYXMgbWF4aW11bSBhbmQgdG8gYXZvaWQgdGhlIHVubmVjZXNzYXJ5IHNjcmVlbiBvdXRwdXRzIGluIHRoZSB3b3Jrc2hlZXQgZHVtcCB0aGUgc2NyZWVuIG91dHB1dCBpbnRvIGEgdmFyaWFibGUgY2FsbGVkIGBkdW1wYApkdW1wID0gbHAuY29udHJvbChscG1hcmssIHNlbnNlPSJtYXgiKSAgCiMgU2V0IHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gd2l0aCB0aGUgcHJvcGVyIGNvZWZmaWNpZW50cyBhc3NvY2lhdGVkIHdpdGggdGhlIGRlY2lzaW9uIHZhcmlhYmxlcwpzZXQub2JqZm4obHBtYXJrLCBjKDI3NS42OTEsIDQ4LjM0MSkpCgojIGFkZCBhIGNvbnN0cmFpbnQgZm9yIHRoZSBtYXhpbXVtIGFsbG93ZWQgYnVkZ2V0IG9mICQzNTBLCmFkZC5jb25zdHJhaW50KGxwbWFyaywgYygxLCAxKSwgIjw9IiwgMzUwMDAwKQphZGQuY29uc3RyYWludChscG1hcmssIGMoMSwgMCksICI+PSIsIDE1MDAwMCkKYWRkLmNvbnN0cmFpbnQobHBtYXJrLCBjKDAsIDEpLCAiPj0iLCA3NTAwMDApCmFkZC5jb25zdHJhaW50KGxwbWFyaywgYygyLCAtMSksICI9IiwgMCkKYWRkLmNvbnN0cmFpbnQobHBtYXJrLCBjKDEsIDApLCAiPj0iLCAwKQphZGQuY29uc3RyYWludChscG1hcmssIGMoMCwgMSksICI+PSIsIDApCgpgYGAKCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPgojIyMjIyAyQSkgSW5zZXJ0IGluIHRoZSBhYm92ZSBjb2RlIGNodW5rIHRoZSByZW1haW5pbmcgZml2ZSBjb25zdHJhaW50cyBjb3JyZXNwb25kaW5nIHRvIHRoZSBtYXJrZXRpbmcgbW9kZWwuIEZvbGxvdyB0aGUgZ3VpZGVsaW5lcyBhcyBkZXNjcmliZWQgaW4gdGhlIGNsYXNzIFBQVCBzbGlkZXMuCjwvc3Bhbj4KCkZpbmFsbHkgd2UgY2FuIGV4cGxvcmUsIHNvbHZlIHRoZSBtb2RlbCwgYW5kIHJlcG9ydCByZXN1bHRzIHVzaW5nIGFkZGl0aW9uYWwgY29tbWFuZHMgcGFydCBvZiB0aGUgYGxwU29sdmVBUElgIHBhY2thZ2UgYW5kIGFwcGxpY2FibGUgb25seSB0byBvdXIgY3JlYXRlZCBgbHBtYXJrYCBsaW5lYXIgcHJvZ3JhbW1pbmcgb2JqZWN0LgoKYGBge3J9CiMgVmlldyB0aGUgcHJvYmxlbSBmb3JtdWxhdGlvbiBpbiB0YWJ1bGFyL21hdHJpeCBmb3JtCmxwbWFyawoKIyBTb2x2ZSAKc29sdmUobHBtYXJrKSAKCiMgRGlzcGxheSB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIG9wdGltdW0gdmFsdWUKZ2V0Lm9iamVjdGl2ZShscG1hcmspCgojIERpc3BsYXkgdGhlIGRlY2lzaW9uIHZhcmlhYmxlcyBvcHRpbXVtIHZhbHVlcwpnZXQudmFyaWFibGVzKGxwbWFyaykgCmBgYAoKPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+CiMjIyMjIDJCKSBDbGVhcmx5IG1hcmsgdGhlIG9wdGltdW0gdmFsdWVzIGZvciBzYWxlcywgcmFkaW8sIGFuZCB0diBhZHMuIFNob3cgaG93IHRoZSBvcHRpbXVtIHNvbHV0aW9uIHNhdGlzZnkgYWxsIHNpeCBjb25zdHJhaW50cyBieSBzdWJzdGl0dXRpbmcgZm9yIHRoZSBkZWNpc2lvbiB2YXJpYWJsZXMgYW5kIG51bWVyaWNhbGx5IHZhbGlkYXRpbmcgZWFjaCBjYXNlLgo8L3NwYW4+CklkZWFsIHNhbGVzICQ0Myw0NDMsNTE3IApyYWRpbyBhZHM6ICQxMTYsNjY2LjcwIAp0diBhZHM6IDIzLDMzMy4zMCAKUmFkaW86MTE2LDY2Ni43MCArIFRWOjIzMywzMzMuMzAgPSAzNTAsMDAwIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byAzNTAsMDAwIAoxMTYsNjY2LjcwIGludmVzdGVkIGluIHJhZGlvID4gMTUwMDAgCjIzMywzMzMuMzAgaW52ZXN0ZWQgaW4gdHYgPiA3NSwwMDAKMjMzLDMzMy4zMCA9IDIgKiAxMTYsNjY2LjcwIHR3aWNlIGFzIG11Y2ggdHYgYXMgb24gcmFkaW8KCg==