Notebook Instructions


About

  • In this lab, we will focus on linear and non-linear programming.

  • Linear programming, as discussed in the previous lab, works with simple and multiple linear regression techniques; sometimes the variables have completely direct or completely non-direct relationships and these techniques can model them.

  • Sometimes, however, the variables do not predict each other in a linear way. For example, looking at the stock market vs. time, we know that generally the market was booming before the crash, then the market crashed and the great depression hit, and slowly the market started to rise again.

  • This pattern is not linear, and in fact a non-linear programming technique can be used to model it and predict the value of the market based on the year.

  • In this lab, we will explore topics like optimization, solve a marketing model, and perform linear and non-linear regression on the cost of servers.

Load Packages in R/RStudio

We are going to use tidyverse a collection of R packages designed for data science.

Loading required package: lpSolveAPI
there is no package called <U+393C><U+3E31>lpSolveAPI<U+393C><U+3E32>Installing package into <U+393C><U+3E31>C:/Users/fasha/OneDrive/Documents/R/win-library/3.4<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.4/lpSolveAPI_5.5.2.0-17.zip'
Content type 'application/zip' length 1044053 bytes (1019 KB)
downloaded 1019 KB
package ‘lpSolveAPI’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\fasha\AppData\Local\Temp\RtmpgvPb8H\downloaded_packages
Loading required package: tidyverse
-- Attaching packages --------------------------------------- tidyverse 1.2.1 --
v ggplot2 2.2.1     v purrr   0.2.4
v tibble  1.4.2     v dplyr   0.7.4
v tidyr   0.8.0     v stringr 1.2.0
v readr   1.1.1     v forcats 0.2.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
Loading required package: rvest
Loading required package: xml2

Attaching package: <U+393C><U+3E31>rvest<U+393C><U+3E32>

The following object is masked from <U+393C><U+3E31>package:purrr<U+393C><U+3E32>:

    pluck

The following object is masked from <U+393C><U+3E31>package:readr<U+393C><U+3E32>:

    guess_encoding

Task 1: Linear Programming - Solving Marketing Model


1A) Create the model object in R.

lprec <- make.lp(0, 2) 

Set the constrains and objective function for the model.

  • Set for maximum
lp.control(lprec, sense="max")  
$anti.degen
[1] "fixedvars" "stalling" 

$basis.crash
[1] "none"

$bb.depthlimit
[1] -50

$bb.floorfirst
[1] "automatic"

$bb.rule
[1] "pseudononint" "greedy"       "dynamic"      "rcostfixing" 

$break.at.first
[1] FALSE

$break.at.value
[1] 1e+30

$epsilon
      epsb       epsd      epsel     epsint epsperturb   epspivot 
     1e-10      1e-09      1e-12      1e-07      1e-05      2e-07 

$improve
[1] "dualfeas" "thetagap"

$infinite
[1] 1e+30

$maxpivot
[1] 250

$mip.gap
absolute relative 
   1e-11    1e-11 

$negrange
[1] -1e+06

$obj.in.basis
[1] TRUE

$pivoting
[1] "devex"    "adaptive"

$presolve
[1] "none"

$scalelimit
[1] 5

$scaling
[1] "geometric"   "equilibrate" "integers"   

$sense
[1] "maximize"

$simplextype
[1] "dual"   "primal"

$timeout
[1] 0

$verbose
[1] "neutral"
set.objfn(lprec, c(275.691, 48.341))

1B) Add constrains

add.constraint(lprec, c(1, 1), "<=", 350000)
add.constraint(lprec, c(1, 0), ">=", 15000)
add.constraint(lprec, c(0, 1), ">=", 75000)
add.constraint(lprec, c(2, -1), "=", 0)

View the problem formulation in tabular/matrix form to confirm that the model was created correctly.

lprec
Model name: 
               C1       C2            
Maximize  275.691   48.341            
R1              1        1  <=  350000
R2              1        0  >=   15000
R3              0        1  >=   75000
R4              2       -1   =       0
Kind          Std      Std            
Type         Real     Real            
Upper         Inf      Inf            
Lower           0        0            

1C) Solve the optimization problem

# solve 
solve(lprec) 
[1] 0

Display the objective function optimum value

get.objective(lprec)
[1] 43443517

Display the variables optimum values

get.variables(lprec) 
[1] 116666.7 233333.3

Task 2: Regression Analysis - Linear Regression


2A) Read the csv file into R Studio and display the dataset.

  • Name your dataset ‘mydata’ so it easy to work with.

  • Commands: read_csv() head()

mydata <- read.csv(file="data/ServersCost.csv")
mydata
head(mydata)

Extract the assigned features (columns) to perform some analytics.

servers <- mydata$servers
cost <- mydata$cost

2B) Create a correlation table for your to compare the correlations between all variables. What can you tell about the correlation between the variables.

cor(mydata)
           servers       cost
servers 1.00000000 0.03356606
cost    0.03356606 1.00000000

The two variables are positively correlated which means they move together. However it’s a small positive correlation.

2C) Create a plot for the dependent (y) and independent (x) variables. Note any patterns or relation between the two variables describe the trend line.

  • The blue line here represents the linear model we created and the black dots are the data points.

Commands: p <- qplot( x = INDEPENDENT, y = DEPENDENT, data = mydata) + geom_point()

p <- qplot( x = servers, y = cost, data = mydata) + geom_point()
p

Commmand: p + geom_smooth(method = “lm”)

Add a trend line plot using the a linear model

p + geom_smooth(method = "lm")

The points make a U shape going from a negative slope to a positive one. The trend line shows the best fit for the data. However, this data doesn’t have a very good fit and doesn’t go through a single point.

2D) Create a linear regression model by identifying the dependent variable (y) and independent variable (x_n)

  • Commands: linear_model <- lm( DEPENDENT ~ INDEPENDENT )
linear_model <- lm( cost ~ servers ) 
linear_model

Call:
lm(formula = cost ~ servers)

Coefficients:
(Intercept)      servers  
      14747           48  

Use the regression model to create a report. Note the R-Squared and Adjusted R-Squared values, determine if this is a good or bad fit for your data?

  • Commands: summary( linear_model )
 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

## R-squared is .001127 and the adjusted is -0.05437. This shows that this is not a very good fit for the data. It’s better the higher the number becomes and this is a low number.

Task 3: Regression Analysis - Non-linear Regression


3A) Create a non-linear quadratic regression model by identifying the dependent variable (y) and independent variables (x). Transforms the independent variable by squaring it and adding to the model.

  • The Quadratic model formula is: y = x + x^2
  • Commands: quad_model <- lm(y ~ x + x_squared)
  • Commands: To squared a variable use (^) such as x^2
x = mydata$servers
x2 = mydata$servers^2
y = x + x2 
quad_model <- lm(y ~ x + x2)
quad_model

Call:
lm(formula = y ~ x + x2)

Coefficients:
(Intercept)            x           x2  
  7.944e-14    1.000e+00    1.000e+00  

Use the quadratic model to create a report. Note the R-Squared and Adjusted R-Squared values, determine if this is a good or bad fit for your data?

  • Commands: summary( quad_model )
summary( quad_model )
essentially perfect fit: summary may be unreliable

Call:
lm(formula = y ~ x + x2)

Residuals:
       Min         1Q     Median         3Q        Max 
-8.356e-14 -8.148e-15  9.260e-16  9.139e-15  8.532e-14 

Coefficients:
             Estimate Std. Error   t value Pr(>|t|)    
(Intercept) 7.944e-14  2.285e-14 3.477e+00  0.00288 ** 
x           1.000e+00  5.010e-15 1.996e+14  < 2e-16 ***
x2          1.000e+00  2.318e-16 4.315e+15  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.071e-14 on 17 degrees of freedom
Multiple R-squared:      1, Adjusted R-squared:      1 
F-statistic: 1.8e+32 on 2 and 17 DF,  p-value: < 2.2e-16

Both the r-squared and adjusted are 1 which would lead people to believe that this is a perfect fit.

3B) Compute the predicted values based on the quadratic model.

Commands: predicted_2 <- predict( quad_model, data = mydata )

servers2 = servers^2
quad_model = lm(cost ~ servers + servers2 )
quad_model

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

Coefficients:
(Intercept)      servers     servers2  
    35417.8      -5589.4        268.4  
predicted2 = predict(quad_model,data=mydata)
predicted2
        1         2         3         4         5         6         7         8 
30096.790 25312.706 21065.520 17355.233 14181.844 11545.354  9445.762  7883.068 
        9        10        11        12        13        14        15        16 
 6857.273  6368.376  6416.377  7001.277  8123.076  9781.772 11977.367 14709.861 
       17        18        19        20 
17979.252 21785.543 26128.731 31008.818 

Create a plot using the quadratic model predicted values in color red. Noted the shape, looking at the plot is this a good or bad fit for your data?

Commands: qplot( x = DEPENDENT, y = INDEPENDENT/PREDICTED, colour = “red” )

qplot( x = servers, y = predicted2, colour = "red" )

The shape is a perfect U which seems to be a better fit for the data as it seems more evenly distributed.

3C) Create a non-linear cubic regression model by identifying the dependent variable (y) and independent variables (x). Transforms the independent variable by squaring it to second (x^2) and third )x^3) degrees and adding them to the model.

  • The Cubic model formula is: y = x + x^2 + x^3
  • Commands: cubic_model <- lm(y ~ x + x_squared + x_cubic)
  • Commands: To squared a variable use (^) such as x^2, x^3
servers <- mydata$servers
servers2 <- mydata$servers^2
servers3 <- mydata$servers^3
servers4 <- mydata$servers^4
servers5 <- mydata$servers^5
cubic_model <- lm(cost ~ servers + servers2 + servers3)
cubic_model

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

Coefficients:
(Intercept)      servers     servers2     servers3  
  36133.696    -5954.738      310.895       -1.347  

Use the cubic model to create a report. Note the R-Squared and Adjusted R-Squared values, determine if this is a good or bad fit for your data?

  • Commands: summary( cubic_model )
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

R-squared is .932 and the adjusted is 0.9193 which is pretty close to 1 which leads us to believe that this a good fit for the data.

3D) Compute the predicted values based on the cubic model.

Commands: predicted3 <- predict( cubic_model, data = mydata )

predicted3 <- predict( cubic_model, data = mydata )
predicted3
        1         2         3         4         5         6         7         8 
30488.507 25457.022 21031.159 17202.831 13963.954 11306.443  9222.212  7703.177 
        9        10        11        12        13        14        15        16 
 6741.253  6328.355  6456.398  7117.297  8302.966 10005.322 12216.278 14927.751 
       17        18        19        20 
18131.654 21819.904 25984.414 30617.101 

Create a plot using the cubic model predicted values in color green. Noted the shape, looking at the plot is this a good or bad fit for your data? Is this model better than the previous?

Commands: qplot( x = DEPENDENT, y = INDEPENDENT/PREDICTED, colour = “red” )

qplot( x = servers, y = predicted3, colour = "green" )

This model seems evenly distributed which makes it look like it will be a good fit for the data.

3E) Overlay the all models on top of the data. Which model seems to fit the best in your opinion? Justify your answer.

variables: LINEAR_MODEL , PREDICTED_QUADRATIC, PREDICTED_CUBIC

# Black = Actual Data
plot(servers, cost, pch = 16) 
# Blue = Linear Line based on Linear Regression Model
abline(linear_model, col = "blue", lwd = 2) 
# Red = Quadratic Model based on Quadratric Regression found above
# Needed to overlay new points without the labels and annotations
par(new = TRUE, xaxt = "n", yaxt = "n", ann = FALSE) 
plot(predicted2, col = "red", pch = 16) 
# Green = Cubic Model based on Cubic Regression found above
# Overlay new points without the labels and annotations 
par(new = TRUE, xaxt = "n", yaxt = "n", ann = FALSE) 
plot(predicted3, col = "green", pch = 16)

The quadratic model looks the best because the trend line goes directly through two points where as it only goes directly through one poin for the cubic graph and none for the linear.

LS0tDQp0aXRsZTogIkJ1c2luZXNzIEFuYWx5dGljcyBMYWIgV29ya3NoZWV0IDA2Ig0KYXV0aG9yOiAiQXNobGV5IEtyZW56Ig0KZGF0ZTogIk1hcmNoIDI5LCAyMDE4Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQpzdWJ0aXRsZTogQ01FIEdyb3VwIEZvdW5kYXRpb24gQnVzaW5lc3MgQW5hbHl0aWNzIExhYg0KLS0tDQoNCi0tLS0tLS0tLS0tLS0NCg0KIyMgTm90ZWJvb2sgSW5zdHJ1Y3Rpb25zDQoNCi0tLS0tLS0tLS0tLS0NCg0KIyMjIEFib3V0DQoNCiogSW4gdGhpcyBsYWIsIHdlIHdpbGwgZm9jdXMgb24gbGluZWFyIGFuZCBub24tbGluZWFyIHByb2dyYW1taW5nLiANCg0KKiBMaW5lYXIgcHJvZ3JhbW1pbmcsIGFzIGRpc2N1c3NlZCBpbiB0aGUgcHJldmlvdXMgbGFiLCB3b3JrcyB3aXRoIHNpbXBsZSBhbmQgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gdGVjaG5pcXVlczsgc29tZXRpbWVzIHRoZSB2YXJpYWJsZXMgaGF2ZSBjb21wbGV0ZWx5IGRpcmVjdCBvciBjb21wbGV0ZWx5IG5vbi1kaXJlY3QgcmVsYXRpb25zaGlwcyBhbmQgdGhlc2UgdGVjaG5pcXVlcyBjYW4gbW9kZWwgdGhlbS4NCg0KKiBTb21ldGltZXMsIGhvd2V2ZXIsIHRoZSB2YXJpYWJsZXMgZG8gbm90IHByZWRpY3QgZWFjaCBvdGhlciBpbiBhIGxpbmVhciB3YXkuIEZvciBleGFtcGxlLCBsb29raW5nIGF0IHRoZSBzdG9jayBtYXJrZXQgdnMuIHRpbWUsIHdlIGtub3cgdGhhdCBnZW5lcmFsbHkgdGhlIG1hcmtldCB3YXMgYm9vbWluZyBiZWZvcmUgdGhlIGNyYXNoLCB0aGVuIHRoZSBtYXJrZXQgY3Jhc2hlZCBhbmQgdGhlIGdyZWF0IGRlcHJlc3Npb24gaGl0LCBhbmQgc2xvd2x5IHRoZSBtYXJrZXQgc3RhcnRlZCB0byByaXNlIGFnYWluLiANCg0KKiBUaGlzIHBhdHRlcm4gaXMgbm90IGxpbmVhciwgYW5kIGluIGZhY3QgYSBub24tbGluZWFyIHByb2dyYW1taW5nIHRlY2huaXF1ZSBjYW4gYmUgdXNlZCB0byBtb2RlbCBpdCBhbmQgcHJlZGljdCB0aGUgdmFsdWUgb2YgdGhlIG1hcmtldCBiYXNlZCBvbiB0aGUgeWVhci4gDQoNCiogSW4gdGhpcyBsYWIsIHdlIHdpbGwgZXhwbG9yZSB0b3BpY3MgbGlrZSBvcHRpbWl6YXRpb24sIHNvbHZlIGEgbWFya2V0aW5nIG1vZGVsLCBhbmQgcGVyZm9ybSBsaW5lYXIgYW5kIG5vbi1saW5lYXIgcmVncmVzc2lvbiBvbiB0aGUgY29zdCBvZiBzZXJ2ZXJzLg0KDQoNCiMjIyBMb2FkIFBhY2thZ2VzIGluIFIvUlN0dWRpbyANCg0KV2UgYXJlIGdvaW5nIHRvIHVzZSB0aWR5dmVyc2UgYSBjb2xsZWN0aW9uIG9mIFIgcGFja2FnZXMgZGVzaWduZWQgZm9yIGRhdGEgc2NpZW5jZS4gDQoNCiogSW5mbzogaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8NCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCg0KIyBIZXJlIHdlIGFyZSBjaGVja2luZyBpZiB0aGUgcGFja2FnZSBpcyBpbnN0YWxsZWQNCmlmKCFyZXF1aXJlKCJscFNvbHZlQVBJIikpew0KICANCiAgIyBJZiB0aGUgcGFja2FnZSBpcyBub3QgaW4gdGhlIHN5c3RlbSB0aGVuIGl0IHdpbGwgYmUgaW5zdGFsbA0KICBpbnN0YWxsLnBhY2thZ2VzKCJscFNvbHZlQVBJIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgDQogICMgSGVyZSB3ZSBhcmUgbG9hZGluZyB0aGUgcGFja2FnZQ0KICBsaWJyYXJ5KCJscFNvbHZlQVBJIikNCn0NCg0KYGBgDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQoNCiMgSGVyZSB3ZSBhcmUgY2hlY2tpbmcgaWYgdGhlIHBhY2thZ2UgaXMgaW5zdGFsbGVkDQppZighcmVxdWlyZSgidGlkeXZlcnNlIikpew0KICANCiAgIyBJZiB0aGUgcGFja2FnZSBpcyBub3QgaW4gdGhlIHN5c3RlbSB0aGVuIGl0IHdpbGwgYmUgaW5zdGFsbA0KICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICANCiAgIyBIZXJlIHdlIGFyZSBsb2FkaW5nIHRoZSBwYWNrYWdlDQogIGxpYnJhcnkoInRpZHl2ZXJzZSIpDQp9DQoNCiMgSGVyZSB3ZSBhcmUgY2hlY2tpbmcgaWYgdGhlIHBhY2thZ2UgaXMgaW5zdGFsbGVkDQppZighcmVxdWlyZSgicnZlc3QiKSl7DQogIA0KICAjIElmIHRoZSBwYWNrYWdlIGlzIG5vdCBpbiB0aGUgc3lzdGVtIHRoZW4gaXQgd2lsbCBiZSBpbnN0YWxsDQogIGluc3RhbGwucGFja2FnZXMoInJ2ZXN0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgDQogICMgSGVyZSB3ZSBhcmUgbG9hZGluZyB0aGUgcGFja2FnZQ0KICBsaWJyYXJ5KCJydmVzdCIpDQp9DQoNCmBgYA0KDQotLS0tLS0tLS0tLS0tDQoNCiMjIFRhc2sgMTogTGluZWFyIFByb2dyYW1taW5nIC0gU29sdmluZyBNYXJrZXRpbmcgTW9kZWwNCg0KLS0tLS0tLS0tLS0tLQ0KDQojIyMgMUEpIENyZWF0ZSB0aGUgbW9kZWwgb2JqZWN0IGluIFIuDQoNCmBgYHtyfQ0KDQpscHJlYyA8LSBtYWtlLmxwKDAsIDIpIA0KDQpgYGANCg0KIyMjIyBTZXQgdGhlIGNvbnN0cmFpbnMgYW5kIG9iamVjdGl2ZSBmdW5jdGlvbiBmb3IgdGhlIG1vZGVsLg0KDQoqIFNldCBmb3IgbWF4aW11bQ0KYGBge3J9DQoNCmxwLmNvbnRyb2wobHByZWMsIHNlbnNlPSJtYXgiKSAgDQpzZXQub2JqZm4obHByZWMsIGMoMjc1LjY5MSwgNDguMzQxKSkNCg0KYGBgDQoNCiMjIyAxQikgQWRkIGNvbnN0cmFpbnMNCg0KYGBge3J9DQoNCmFkZC5jb25zdHJhaW50KGxwcmVjLCBjKDEsIDEpLCAiPD0iLCAzNTAwMDApDQphZGQuY29uc3RyYWludChscHJlYywgYygxLCAwKSwgIj49IiwgMTUwMDApDQphZGQuY29uc3RyYWludChscHJlYywgYygwLCAxKSwgIj49IiwgNzUwMDApDQphZGQuY29uc3RyYWludChscHJlYywgYygyLCAtMSksICI9IiwgMCkNCg0KYGBgDQoNCiMjIyMgVmlldyB0aGUgcHJvYmxlbSBmb3JtdWxhdGlvbiBpbiB0YWJ1bGFyL21hdHJpeCBmb3JtIHRvIGNvbmZpcm0gdGhhdCB0aGUgbW9kZWwgd2FzIGNyZWF0ZWQgY29ycmVjdGx5Lg0KDQpgYGB7cn0NCg0KbHByZWMNCg0KYGBgDQoNCiMjIyAxQykgU29sdmUgdGhlIG9wdGltaXphdGlvbiBwcm9ibGVtDQpgYGB7cn0NCiMgc29sdmUgDQpzb2x2ZShscHJlYykgDQoNCmBgYA0KDQojIyMjIERpc3BsYXkgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBvcHRpbXVtIHZhbHVlDQpgYGB7cn0NCg0KZ2V0Lm9iamVjdGl2ZShscHJlYykNCg0KYGBgDQoNCiMjIyMgRGlzcGxheSB0aGUgdmFyaWFibGVzIG9wdGltdW0gdmFsdWVzDQpgYGB7cn0NCg0KZ2V0LnZhcmlhYmxlcyhscHJlYykgDQoNCmBgYA0KDQotLS0tLS0tLS0tLS0tDQoNCiMjIFRhc2sgMjogUmVncmVzc2lvbiBBbmFseXNpcyAtIExpbmVhciBSZWdyZXNzaW9uDQoNCi0tLS0tLS0tLS0tLS0NCg0KKiBBIGxpbmVhciBtb2RlbCBpcyBvZiB0aGUgZm9ybSB5ID0geDAgKyB4MSArIC4uLisgeF9uDQoNCiMjIyAyQSkgUmVhZCB0aGUgY3N2IGZpbGUgaW50byBSIFN0dWRpbyBhbmQgZGlzcGxheSB0aGUgZGF0YXNldC4gDQoNCiogTmFtZSB5b3VyIGRhdGFzZXQgJ215ZGF0YScgc28gaXQgZWFzeSB0byB3b3JrIHdpdGguDQoNCiogQ29tbWFuZHM6IHJlYWRfY3N2KCkgaGVhZCgpDQoNCmBgYHtyfQ0KbXlkYXRhIDwtIHJlYWQuY3N2KGZpbGU9ImRhdGEvU2VydmVyc0Nvc3QuY3N2IikNCm15ZGF0YQ0KYGBgDQpgYGB7cn0NCmhlYWQobXlkYXRhKQ0KYGBgDQoNCg0KIyMjIyBFeHRyYWN0IHRoZSBhc3NpZ25lZCBmZWF0dXJlcyAoY29sdW1ucykgdG8gcGVyZm9ybSBzb21lIGFuYWx5dGljcy4gDQpgYGB7cn0NCnNlcnZlcnMgPC0gbXlkYXRhJHNlcnZlcnMNCmNvc3QgPC0gbXlkYXRhJGNvc3QNCmBgYA0KDQojIyMgMkIpIENyZWF0ZSBhIGNvcnJlbGF0aW9uIHRhYmxlIGZvciB5b3VyIHRvIGNvbXBhcmUgdGhlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIGFsbCB2YXJpYWJsZXMuIFdoYXQgY2FuIHlvdSB0ZWxsIGFib3V0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMuICANCg0KYGBge3J9DQpjb3IobXlkYXRhKQ0KYGBgDQojIyBUaGUgdHdvIHZhcmlhYmxlcyBhcmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdoaWNoIG1lYW5zIHRoZXkgbW92ZSB0b2dldGhlci4gSG93ZXZlciBpdCdzIGEgc21hbGwgcG9zaXRpdmUgY29ycmVsYXRpb24uIA0KDQojIyMgMkMpIENyZWF0ZSBhIHBsb3QgZm9yIHRoZSBkZXBlbmRlbnQgKHkpIGFuZCBpbmRlcGVuZGVudCAoeCkgdmFyaWFibGVzLiBOb3RlIGFueSBwYXR0ZXJucyBvciByZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzIGRlc2NyaWJlIHRoZSB0cmVuZCBsaW5lLg0KDQoqIFRoZSBibHVlIGxpbmUgaGVyZSByZXByZXNlbnRzIHRoZSBsaW5lYXIgbW9kZWwgd2UgY3JlYXRlZCBhbmQgdGhlIGJsYWNrIGRvdHMgYXJlIHRoZSBkYXRhIHBvaW50cy4gDQoNCkNvbW1hbmRzOiBwIDwtIHFwbG90KCB4ID0gSU5ERVBFTkRFTlQsIHkgPSBERVBFTkRFTlQsIGRhdGEgPSBteWRhdGEpICsgZ2VvbV9wb2ludCgpDQoNCmBgYHtyfQ0KcCA8LSBxcGxvdCggeCA9IHNlcnZlcnMsIHkgPSBjb3N0LCBkYXRhID0gbXlkYXRhKSArIGdlb21fcG9pbnQoKQ0KcA0KYGBgDQoNCkNvbW1tYW5kOiBwICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikNCg0KIyMjIyBBZGQgYSB0cmVuZCBsaW5lIHBsb3QgdXNpbmcgdGhlIGEgbGluZWFyIG1vZGVsDQpgYGB7cn0NCnAgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQ0KYGBgDQojIyBUaGUgcG9pbnRzIG1ha2UgYSBVIHNoYXBlIGdvaW5nIGZyb20gYSBuZWdhdGl2ZSBzbG9wZSB0byBhIHBvc2l0aXZlIG9uZS4gVGhlIHRyZW5kIGxpbmUgc2hvd3MgdGhlIGJlc3QgZml0IGZvciB0aGUgZGF0YS4gSG93ZXZlciwgdGhpcyBkYXRhIGRvZXNuJ3QgaGF2ZSBhIHZlcnkgZ29vZCBmaXQgYW5kIGRvZXNuJ3QgZ28gdGhyb3VnaCBhIHNpbmdsZSBwb2ludC4gDQoNCiMjIyAyRCkgQ3JlYXRlIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYnkgaWRlbnRpZnlpbmcgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSAoeSkgYW5kIGluZGVwZW5kZW50IHZhcmlhYmxlICh4X24pDQoNCiogQ29tbWFuZHM6IGxpbmVhcl9tb2RlbCA8LSBsbSggREVQRU5ERU5UIH4gSU5ERVBFTkRFTlQgKSANCg0KYGBge3J9DQoNCmxpbmVhcl9tb2RlbCA8LSBsbSggY29zdCB+IHNlcnZlcnMgKSANCmxpbmVhcl9tb2RlbA0KDQpgYGANCg0KIyMjIyBVc2UgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgdG8gY3JlYXRlIGEgcmVwb3J0LiBOb3RlIHRoZSBSLVNxdWFyZWQgYW5kIEFkanVzdGVkIFItU3F1YXJlZCB2YWx1ZXMsIGRldGVybWluZSBpZiB0aGlzIGlzIGEgZ29vZCBvciBiYWQgZml0IGZvciB5b3VyIGRhdGE/DQoNCiogQ29tbWFuZHM6IHN1bW1hcnkoIGxpbmVhcl9tb2RlbCApDQoNCmBgYHtyfQ0KIHN1bW1hcnkoIGxpbmVhcl9tb2RlbCApDQpgYGANCiMjIFItc3F1YXJlZCBpcyAuMDAxMTI3IGFuZCB0aGUgYWRqdXN0ZWQgaXMgLTAuMDU0MzcuIFRoaXMgc2hvd3MgdGhhdCB0aGlzIGlzIG5vdCBhIHZlcnkgZ29vZCBmaXQgZm9yIHRoZSBkYXRhLiBJdCdzIGJldHRlciB0aGUgaGlnaGVyIHRoZSBudW1iZXIgYmVjb21lcyBhbmQgdGhpcyBpcyBhIGxvdyBudW1iZXIuIA0KLS0tLS0tLS0tLS0tLQ0KDQojIyBUYXNrIDM6IFJlZ3Jlc3Npb24gQW5hbHlzaXMgLSBOb24tbGluZWFyIFJlZ3Jlc3Npb24NCg0KLS0tLS0tLS0tLS0tLQ0KDQoqIFdlIHVzZSBhIHRyYW5zZm9ybWF0aW9uIGFuZCB1c2UgYSBub25saW5lYXIgcXVhZHJhdGljIG1vZGVsIHRvIHNlZSBob3cgdGhlIG1vZGVsIGZpdHMgdG8gdGhlIGRhdGEuDQoNCiogQSBxdWFkcmF0aWMgbW9kZWwgdHJhbnNmb3JtcyB0aGUgcHJlZGljdG9yIGJ5IHNxdWFyaW5nIGl0IGFuZCBhZGRpbmcgdG8gdGhlIG1vZGVsLiANCiogUXVhZHJhdGljIE1vZGVsOiB5ID0geCArIHheMg0KDQojIyMgM0EpIENyZWF0ZSBhIG5vbi1saW5lYXIgcXVhZHJhdGljIHJlZ3Jlc3Npb24gbW9kZWwgYnkgaWRlbnRpZnlpbmcgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSAoeSkgYW5kIGluZGVwZW5kZW50IHZhcmlhYmxlcyAoeCkuIFRyYW5zZm9ybXMgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlIGJ5IHNxdWFyaW5nIGl0IGFuZCBhZGRpbmcgdG8gdGhlIG1vZGVsLiANCg0KKiBUaGUgUXVhZHJhdGljIG1vZGVsIGZvcm11bGEgaXM6IHkgPSB4ICsgeF4yDQoqIENvbW1hbmRzOiBxdWFkX21vZGVsIDwtIGxtKHkgfiB4ICsgeF9zcXVhcmVkKQ0KKiBDb21tYW5kczogVG8gc3F1YXJlZCBhIHZhcmlhYmxlIHVzZSAoXikgc3VjaCBhcyAgeF4yDQoNCmBgYHtyfQ0KeCA9IG15ZGF0YSRzZXJ2ZXJzDQp4MiA9IG15ZGF0YSRzZXJ2ZXJzXjINCnkgPSB4ICsgeDIgDQpxdWFkX21vZGVsIDwtIGxtKHkgfiB4ICsgeDIpDQpxdWFkX21vZGVsDQpgYGANCg0KIyMjIyBVc2UgdGhlIHF1YWRyYXRpYyBtb2RlbCB0byBjcmVhdGUgYSByZXBvcnQuIE5vdGUgdGhlIFItU3F1YXJlZCBhbmQgQWRqdXN0ZWQgUi1TcXVhcmVkIHZhbHVlcywgZGV0ZXJtaW5lIGlmIHRoaXMgaXMgYSBnb29kIG9yIGJhZCBmaXQgZm9yIHlvdXIgZGF0YT8NCg0KKiBDb21tYW5kczogc3VtbWFyeSggcXVhZF9tb2RlbCApDQoNCmBgYHtyfQ0Kc3VtbWFyeSggcXVhZF9tb2RlbCApDQpgYGANCg0KIyMgQm90aCB0aGUgci1zcXVhcmVkIGFuZCBhZGp1c3RlZCBhcmUgMSB3aGljaCB3b3VsZCBsZWFkIHBlb3BsZSB0byBiZWxpZXZlIHRoYXQgdGhpcyBpcyBhIHBlcmZlY3QgZml0LiANCg0KIyMjIDNCKSBDb21wdXRlIHRoZSBwcmVkaWN0ZWQgdmFsdWVzIGJhc2VkIG9uIHRoZSBxdWFkcmF0aWMgbW9kZWwuDQoNCkNvbW1hbmRzOiBwcmVkaWN0ZWRfMiA8LSBwcmVkaWN0KCBxdWFkX21vZGVsLCBkYXRhID0gbXlkYXRhICkNCg0KYGBge3J9DQpzZXJ2ZXJzMiA9IHNlcnZlcnNeMg0KDQpxdWFkX21vZGVsID0gbG0oY29zdCB+IHNlcnZlcnMgKyBzZXJ2ZXJzMiApDQpxdWFkX21vZGVsDQoNCnByZWRpY3RlZDIgPSBwcmVkaWN0KHF1YWRfbW9kZWwsZGF0YT1teWRhdGEpDQpwcmVkaWN0ZWQyDQoNCmBgYA0KDQojIyMjIENyZWF0ZSBhIHBsb3QgdXNpbmcgdGhlIHF1YWRyYXRpYyBtb2RlbCBwcmVkaWN0ZWQgdmFsdWVzIGluIGNvbG9yIHJlZC4gTm90ZWQgdGhlIHNoYXBlLCBsb29raW5nIGF0IHRoZSBwbG90IGlzIHRoaXMgYSBnb29kIG9yIGJhZCBmaXQgZm9yIHlvdXIgZGF0YT8NCg0KQ29tbWFuZHM6IHFwbG90KCB4ID0gREVQRU5ERU5ULCB5ID0gSU5ERVBFTkRFTlQvUFJFRElDVEVELCBjb2xvdXIgPSAicmVkIiApDQoNCmBgYHtyfQ0KcXBsb3QoIHggPSBzZXJ2ZXJzLCB5ID0gcHJlZGljdGVkMiwgY29sb3VyID0gInJlZCIgKQ0KDQpgYGANCiMjIFRoZSBzaGFwZSBpcyBhIHBlcmZlY3QgVSB3aGljaCBzZWVtcyB0byBiZSBhIGJldHRlciBmaXQgZm9yIHRoZSBkYXRhIGFzIGl0IHNlZW1zIG1vcmUgZXZlbmx5IGRpc3RyaWJ1dGVkLiANCg0KIyMjIDNDKSBDcmVhdGUgYSBub24tbGluZWFyIGN1YmljIHJlZ3Jlc3Npb24gbW9kZWwgYnkgaWRlbnRpZnlpbmcgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSAoeSkgYW5kIGluZGVwZW5kZW50IHZhcmlhYmxlcyAoeCkuIFRyYW5zZm9ybXMgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlIGJ5IHNxdWFyaW5nIGl0IHRvIHNlY29uZCAoeF4yKSBhbmQgdGhpcmQgKXheMykgZGVncmVlcyBhbmQgYWRkaW5nIHRoZW0gdG8gdGhlIG1vZGVsLiANCg0KKiBUaGUgQ3ViaWMgbW9kZWwgZm9ybXVsYSBpczogeSA9IHggKyB4XjIgKyB4XjMNCiogQ29tbWFuZHM6IGN1YmljX21vZGVsIDwtIGxtKHkgfiB4ICsgeF9zcXVhcmVkICsgeF9jdWJpYykNCiogQ29tbWFuZHM6IFRvIHNxdWFyZWQgYSB2YXJpYWJsZSB1c2UgKF4pIHN1Y2ggYXMgIHheMiwgeF4zDQoNCmBgYHtyfQ0Kc2VydmVycyA8LSBteWRhdGEkc2VydmVycw0Kc2VydmVyczIgPC0gbXlkYXRhJHNlcnZlcnNeMg0Kc2VydmVyczMgPC0gbXlkYXRhJHNlcnZlcnNeMw0Kc2VydmVyczQgPC0gbXlkYXRhJHNlcnZlcnNeNA0Kc2VydmVyczUgPC0gbXlkYXRhJHNlcnZlcnNeNQ0KDQpjdWJpY19tb2RlbCA8LSBsbShjb3N0IH4gc2VydmVycyArIHNlcnZlcnMyICsgc2VydmVyczMpDQpjdWJpY19tb2RlbA0KYGBgDQoNCiMjIyMgVXNlIHRoZSBjdWJpYyBtb2RlbCB0byBjcmVhdGUgYSByZXBvcnQuIE5vdGUgdGhlIFItU3F1YXJlZCBhbmQgQWRqdXN0ZWQgUi1TcXVhcmVkIHZhbHVlcywgZGV0ZXJtaW5lIGlmIHRoaXMgaXMgYSBnb29kIG9yIGJhZCBmaXQgZm9yIHlvdXIgZGF0YT8NCg0KKiBDb21tYW5kczogc3VtbWFyeSggY3ViaWNfbW9kZWwgKQ0KDQpgYGB7cn0NCnN1bW1hcnkoIGN1YmljX21vZGVsICkNCmBgYA0KIyMgUi1zcXVhcmVkIGlzIC45MzIgYW5kIHRoZSBhZGp1c3RlZCBpcyAwLjkxOTMgd2hpY2ggaXMgcHJldHR5IGNsb3NlIHRvIDEgd2hpY2ggbGVhZHMgdXMgdG8gYmVsaWV2ZSB0aGF0IHRoaXMgYSBnb29kIGZpdCBmb3IgdGhlIGRhdGEuIA0KDQojIyMgM0QpIENvbXB1dGUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgYmFzZWQgb24gdGhlIGN1YmljIG1vZGVsLg0KDQpDb21tYW5kczogcHJlZGljdGVkMyA8LSBwcmVkaWN0KCBjdWJpY19tb2RlbCwgZGF0YSA9IG15ZGF0YSApDQoNCmBgYHtyfQ0KcHJlZGljdGVkMyA8LSBwcmVkaWN0KCBjdWJpY19tb2RlbCwgZGF0YSA9IG15ZGF0YSApDQpwcmVkaWN0ZWQzDQpgYGANCg0KIyMjIyBDcmVhdGUgYSBwbG90IHVzaW5nIHRoZSBjdWJpYyBtb2RlbCBwcmVkaWN0ZWQgdmFsdWVzIGluIGNvbG9yIGdyZWVuLiBOb3RlZCB0aGUgc2hhcGUsIGxvb2tpbmcgYXQgdGhlIHBsb3QgaXMgdGhpcyBhIGdvb2Qgb3IgYmFkIGZpdCBmb3IgeW91ciBkYXRhPyBJcyB0aGlzIG1vZGVsIGJldHRlciB0aGFuIHRoZSBwcmV2aW91cz8NCg0KQ29tbWFuZHM6IHFwbG90KCB4ID0gREVQRU5ERU5ULCB5ID0gSU5ERVBFTkRFTlQvUFJFRElDVEVELCBjb2xvdXIgPSAicmVkIiApDQoNCmBgYHtyfQ0KcXBsb3QoIHggPSBzZXJ2ZXJzLCB5ID0gcHJlZGljdGVkMywgY29sb3VyID0gImdyZWVuIiApDQoNCmBgYA0KDQojIyBUaGlzIG1vZGVsIHNlZW1zIGV2ZW5seSBkaXN0cmlidXRlZCB3aGljaCBtYWtlcyBpdCBsb29rIGxpa2UgaXQgd2lsbCBiZSBhIGdvb2QgZml0IGZvciB0aGUgZGF0YS4gDQoNCiMjIyAzRSkgT3ZlcmxheSB0aGUgYWxsIG1vZGVscyBvbiB0b3Agb2YgdGhlIGRhdGEuIFdoaWNoIG1vZGVsIHNlZW1zIHRvIGZpdCB0aGUgYmVzdCBpbiB5b3VyIG9waW5pb24/IEp1c3RpZnkgeW91ciBhbnN3ZXIuIA0KDQp2YXJpYWJsZXM6IExJTkVBUl9NT0RFTCAsIFBSRURJQ1RFRF9RVUFEUkFUSUMsIFBSRURJQ1RFRF9DVUJJQw0KDQpgYGB7cn0NCg0KIyBCbGFjayA9IEFjdHVhbCBEYXRhDQpwbG90KHNlcnZlcnMsIGNvc3QsIHBjaCA9IDE2KSANCiMgQmx1ZSA9IExpbmVhciBMaW5lIGJhc2VkIG9uIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsDQphYmxpbmUobGluZWFyX21vZGVsLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpIA0KDQojIFJlZCA9IFF1YWRyYXRpYyBNb2RlbCBiYXNlZCBvbiBRdWFkcmF0cmljIFJlZ3Jlc3Npb24gZm91bmQgYWJvdmUNCiMgTmVlZGVkIHRvIG92ZXJsYXkgbmV3IHBvaW50cyB3aXRob3V0IHRoZSBsYWJlbHMgYW5kIGFubm90YXRpb25zDQpwYXIobmV3ID0gVFJVRSwgeGF4dCA9ICJuIiwgeWF4dCA9ICJuIiwgYW5uID0gRkFMU0UpIA0KcGxvdChwcmVkaWN0ZWQyLCBjb2wgPSAicmVkIiwgcGNoID0gMTYpIA0KDQojIEdyZWVuID0gQ3ViaWMgTW9kZWwgYmFzZWQgb24gQ3ViaWMgUmVncmVzc2lvbiBmb3VuZCBhYm92ZQ0KIyBPdmVybGF5IG5ldyBwb2ludHMgd2l0aG91dCB0aGUgbGFiZWxzIGFuZCBhbm5vdGF0aW9ucyANCnBhcihuZXcgPSBUUlVFLCB4YXh0ID0gIm4iLCB5YXh0ID0gIm4iLCBhbm4gPSBGQUxTRSkgDQpwbG90KHByZWRpY3RlZDMsIGNvbCA9ICJncmVlbiIsIHBjaCA9IDE2KQ0KDQpgYGANCiMjIFRoZSBxdWFkcmF0aWMgbW9kZWwgbG9va3MgdGhlIGJlc3QgYmVjYXVzZSB0aGUgdHJlbmQgbGluZSBnb2VzIGRpcmVjdGx5IHRocm91Z2ggdHdvIHBvaW50cyB3aGVyZSBhcyBpdCBvbmx5IGdvZXMgZGlyZWN0bHkgdGhyb3VnaCBvbmUgcG9pbiBmb3IgdGhlIGN1YmljIGdyYXBoIGFuZCBub25lIGZvciB0aGUgbGluZWFyLiANCg==