# load the packages for graphing and data wrangling
library(ggplot2)
library(PASWR2)
library(car) # install the car package first, if not already installed
library(dplyr) 
library(lattice)
library(boot)
library(MASS)

Note: If you Rmd file submission knits you will receive total of (5 points)

Case Study: Real Estate

Data and ideas for this case study come from (Militino et al., 2004).

Problem 16, page 899 (not entire problem, only specified parts below)

The goal of this case study is to walk the user through the creation of a parsimonious multiple linear regression model that can be used to predict the total price (totalprice) of apartments by their hedonic (structural) characteristics. The data frame VIT2005 contains several variables, and further description of the data can be found in the help file (listed below).

A data frame with 218 observations on the following 15 variables:

Complete the parts below

(10 pts) Quiz-Project 3 Pr.1

  1. Characterize the shape, center, and spread of the variable totalprice.

Hint: Use ggplot function from ggplot2 package to graph the totalprice density function. Use median and IQR to find the median and IQR for the totalprice. Fill in the blank spaces in the observation below:

Solution: Hint: Use the template for the graph:

ggplot(data = YOUR DATA, aes(x = variable to plot)) + geom_density(fill = "your favorite color") + theme_bw()

YOUR CODE HERE:

library(ggplot2) # Plot the density function of ‘totalprice’ ggplot(data = VIT2005, aes(x = totalprice)) + geom_density(fill = “lightblue”) + theme_bw() + ggtitle(“Density Plot of Total Price”)

Calculate the median and IQR for ‘totalprice’

median_totalprice <- median(VIT2005\(totalprice) iqr_totalprice <- IQR(VIT2005\)totalprice)

median_totalprice iqr_totalprice



# MD <- median(VIT2005$totalprice)
# 
# iqr <- IQR(VIT2005$totalprice)
# 
# c(MD, iqr)

Observation: The distribution of totalprice is … with a median of ... and an IQR of ...

(10 pts) Quiz-Project 3 Pr.2

  1. Use scatterplotMatrix() from car package or pairs() to explore the relationships between totalprice and the numerical explanatory variables area, age, floor, rooms, toilets, garage, elevator, and storage.

Hint: To use scatterplotMatrix type scatterplotMatrix( ~ totalprice + var1 + var2 + ... + var n, data = VIT2005), do not use more than 5 variables to produce input that fits the screen and can be reviewed. Use the command as many times as you need to review how totalprice correlates with other variables in the data.

Solution:

YOUR CODE HERE: library(car)

scatterplotMatrix( ~ totalprice + area + age + floor + rooms, data = VIT2005, smooth = FALSE)

scatterplotMatrix( ~ totalprice + toilets + garage + elevator + storage, data = VIT2005, smooth = FALSE)

# e.g. matrix can be produced with
scatterplotMatrix( ~ totalprice + area + age + floor + rooms, data = VIT2005)

scatterplotMatrix( ~ totalprice + elevator + storage + toilets, data = VIT2005 )

# or use 
# pairs(~ totalprice + area + age + floor + rooms, data = VIT2005)
# 
# pairs(~ totalprice + toilets + garage + elevator + storage, data = VIT2005)

Observation: The variable totalprice appears to have a moderate linear relationship with area.

  1. Total of (55 pts) Quiz-Project 3 Pr.3 to 8, 10 pts for each correctly removed variable, 5 pts to find the correlation.

Compute the correlation between totalprice and all of the other numerical variables. List the three variables in order along with their correlation coefficients that have the highest correlation with totalprice.

Model (A): Use backward elimination to develop a model that predicts totalprice using the data frame VIT2005. Use a “P-value-to remove” of 5%. Store the final model in the object modelA.

(5 pts) Quiz-Project 3 Pr.3

The correlation coefficients are:

NUM <- c("area", "age", "floor", "rooms", "toilets", "garage","elevator", "storage")
COR <- cor(VIT2005[, "totalprice"], VIT2005[, NUM])
COR

Observation: The highest three correlations with totalprice occur with area (0.8092), toilets (0.6876), and rooms(0.5256).

Model (A) The functions drop1() and update() are used to create a model using backward elimination.

Hint: drop1(model_.be_name, test = "F") test for significance of all individual predictors given all others are already in the model.

# use a model where totalprice is regressed on all other variables
model.be <- lm(totalprice ~ ., data = VIT2005)

# 
drop1(model.be, test = "F")

(10 pts) (part c.1) Quiz-Project 3 Pr.4

Which one appears most insignificant (biggest P-value)? Drop it from the model.

E.g. If age is most insignificant, use the update function to update the model with the following code:

model.be <- update(model.be, .~. - age) - the first dot means we use the same response variable, the second all previously included predictors minus age. The again use drop1 and so on, until all remaining variables are of significance 0.05 as specified in the directions of the problem

YOUR CODE HERE: model.be <- lm(totalprice ~ area + age + floor + rooms + toilets + garage + elevator + storage, data = VIT2005)

drop1(model.be, test = “F”) model.be <- update(model.be, . ~ . - age) drop1(model.be, test = “F”) model.be <- update(model.be, . ~ . - floor) drop1(model.be, test = “F”)

model.be <- update(model.be, . ~ . - rooms)

drop1(model.be, test = “F”)

model.be <- update(model.be, . ~ . - storage)

drop1(model.be, test = “F”)

model.be <- update(model.be, . ~ . - elevator)

drop1(model.be, test = “F”)

totalprice ~ area + toilets + garage

model.be <- update(model.be, .~. - age)
drop1(model.be, test = "F")
Single term deletions

Model:
totalprice ~ area + rooms + toilets + garage + elevator + storage
         Df  Sum of Sq        RSS    AIC  F value    Pr(>F)
<none>                 2.3894e+11 4551.7                   
area      1 1.4652e+11 3.8546e+11 4653.9 129.3900 < 2.2e-16
rooms     1 4.4164e+05 2.3894e+11 4549.7   0.0004 0.9842626
toilets   1 1.2991e+10 2.5193e+11 4561.2  11.4725 0.0008425
garage    1 3.2312e+10 2.7125e+11 4577.3  28.5338 2.376e-07
elevator  1 1.5731e+10 2.5467e+11 4563.6  13.8914 0.0002487
storage   1 7.0112e+09 2.4595e+11 4556.0   6.1915 0.0136104
            
<none>      
area     ***
rooms       
toilets  ***
garage   ***
elevator ***
storage  *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(10 pts) (part c.2) Quiz-Project 3 Pr.5

Which one is to be dropped next?

Answer: model.be <- update(model.be, . ~ . - floor) drop1(model.be, test = “F”)

(10 pts) (part c.3) Quiz-Project 3 Pr.6 Which one is to be dropped next?

Answer: model.be <- update(model.be, . ~ . - rooms)

drop1(model.be, test = “F”)

(10 pts) (part c.4) Quiz-Project 3 Pr.7 Which one is to be dropped next?

Answer:

(10 pts) (part c.5) Quiz-Project 3 Pr.8 Which one is to be dropped next?

Answer: model.be <- update(model.be, . ~ . - storage)

If all variable are significant create the object holding the optimal model as per the description of the problem using the code:

formula(model.be)

modelA <- lm(formula(model.be), data = VIT2005)

Question 1: (5 pts) Quiz-Project 3 Pr.9 Which variables are left in the model, list them?

Observation: Backward elimination suggests using the variables area, zone, category, out, toilets, garage, elevator, streetcorner, and heating to best predict totalprice. area

zone

category

out

toilets

garage

elevator

streetcorner

heating

  1. (5 pts) Quiz-Project 3 Pr.10

Set the seed for reproducibility

set.seed(5)

Assuming modelA is already fitted, e.g.:

modelA <- lm(totalprice ~ area + toilets + garage + elevator, data = VIT2005)

Load the necessary library

library(boot)

Compute leave-one-out cross-validation error (CV_n)

cv_error_n <- cv.glm(data = VIT2005, glmfit = modelA) cv_error_n$delta[1]

Compute five-fold cross-validation error (CV_5)

cv_error_5 <- cv.glm(data = VIT2005, glmfit = modelA, K = 5) cv_error_5$delta[1]


(ii) **(5 pts)** **Quiz-Project 3 Pr.11**

Compute $R^2$, $R^2_a$, the `AIC`, and the `BIC` for **Model (A)**. What is the proportion of total variability explained by **Model (A)**?

**Your Solution:**
# Fit the model (if not already fitted)
# modelA <- lm(totalprice ~ area + toilets + garage + elevator, data = VIT2005)

# Compute R-squared (R^2)
R2 <- summary(modelA)$r.squared

# Compute Adjusted R-squared (R^2_a)
R2_a <- summary(modelA)$adj.r.squared

# Compute AIC
AIC_value <- AIC(modelA)

# Compute BIC
BIC_value <- BIC(modelA)

# Proportion of total variability explained by Model (A)
proportion_variability_explained <- R2

# Print results
cat("R-squared (R^2):", R2, "\n")
cat("Adjusted R-squared (R^2_a):", R2_a, "\n")
cat("AIC:", AIC_value, "\n")
cat("BIC:", BIC_value, "\n")
cat("Proportion of total variability explained by Model (A):", proportion_variability_explained, "\n")

(i) 


<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-output-begin eyJkYXRhIjoiXG48IS0tIHJuYi1zb3VyY2UtYmVnaW4gZXlKa1lYUmhJam9pWUdCZ2NseHViVzlrWld4Qlp5QThMU0JuYkcwb1ptOXliWFZzWVNodGIyUmxiQzVpWlNrc0lHUmhkR0VnUFNCV1NWUXlNREExS1Z4dVhHNGpJQ01nUm05eUlHTjJMbWRzYlNBdElIUm9aU0JrWldaaGRXeDBJR2x6SUhSdklITmxkQ0JMSUdWeGRXRnNJSFJ2SUhSb1pTQnVkVzFpWlhJZ2IyWWdiMkp6WlhKMllYUnBiMjV6SUdsdUlHUmhkR0VnZDJocFkyZ2daMmwyWlhNZ2RHaGxJSFZ6ZFdGc0lHeGxZWFpsTFc5dVpTMXZkWFFnWTNKdmMzTXRkbUZzYVdSaGRHbHZiaTVjYmlNZ1hHNXpaWFF1YzJWbFpDZzFLU0FqSUhWelpTQm1iM0lnY21Wd2JHbGpZWFJwYjI0Z2NIVnljRzl6WlhOY2JtTjJMbVZ5Y205eVRpQThMU0JqZGk1bmJHMG9aR0YwWVNBOUlGWkpWREl3TURVc0lHZHNiV1pwZENBOUlDQnRiMlJsYkVGbktWeHVYRzVEVms1aElEd3RJR04yTG1WeWNtOXlUaVJrWld4MFlWc3hYVnh1UTFaT1lWeHVZR0JnSW4wPSAtLT5cblxuYGBgclxubW9kZWxBZyA8LSBnbG0oZm9ybXVsYShtb2RlbC5iZSksIGRhdGEgPSBWSVQyMDA1KVxuXG4jICMgRm9yIGN2LmdsbSAtIHRoZSBkZWZhdWx0IGlzIHRvIHNldCBLIGVxdWFsIHRvIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGRhdGEgd2hpY2ggZ2l2ZXMgdGhlIHVzdWFsIGxlYXZlLW9uZS1vdXQgY3Jvc3MtdmFsaWRhdGlvbi5cbiMgXG5zZXQuc2VlZCg1KSAjIHVzZSBmb3IgcmVwbGljYXRpb24gcHVycG9zZXNcbmN2LmVycm9yTiA8LSBjdi5nbG0oZGF0YSA9IFZJVDIwMDUsIGdsbWZpdCA9ICBtb2RlbEFnKVxuXG5DVk5hIDwtIGN2LmVycm9yTiRkZWx0YVsxXVxuQ1ZOYVxuYGBgXG5cbjwhLS0gcm5iLXNvdXJjZS1lbmQgLS0+XG4ifQ== -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxubW9kZWxBZyA8LSBnbG0oZm9ybXVsYShtb2RlbC5iZSksIGRhdGEgPSBWSVQyMDA1KVxuXG4jICMgRm9yIGN2LmdsbSAtIHRoZSBkZWZhdWx0IGlzIHRvIHNldCBLIGVxdWFsIHRvIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGRhdGEgd2hpY2ggZ2l2ZXMgdGhlIHVzdWFsIGxlYXZlLW9uZS1vdXQgY3Jvc3MtdmFsaWRhdGlvbi5cbiMgXG5zZXQuc2VlZCg1KSAjIHVzZSBmb3IgcmVwbGljYXRpb24gcHVycG9zZXNcbmN2LmVycm9yTiA8LSBjdi5nbG0oZGF0YSA9IFZJVDIwMDUsIGdsbWZpdCA9ICBtb2RlbEFnKVxuXG5DVk5hIDwtIGN2LmVycm9yTiRkZWx0YVsxXVxuQ1ZOYVxuYGBgIn0= -->

```r
modelAg <- glm(formula(model.be), data = VIT2005)

# # For cv.glm - the default is to set K equal to the number of observations in data which gives the usual leave-one-out cross-validation.
# 
set.seed(5) # use for replication purposes
cv.errorN <- cv.glm(data = VIT2005, glmfit =  modelAg)

CVNa <- cv.errorN$delta[1]
CVNa
modelAg <- glm(formula(model.be), data = VIT2005)

# UNCOMMENT the code below and run it

# set.seed(5) # use for replication purposes
# cv.error5 <- cv.glm(data = VIT2005, glmfit = modelAg, K = 5)
# CV5a <- cv.error5$delta[1]
# CV5a

Observation: The \(CV_n = ...\) for Model (A), and \(CV_5 =...\) for Model (A).

  1. Since this problem and a few more will request many goodness of fit statistics, a function called mgof() is written to compute the requested values. Use it as shown below.

mgof <- function(model = model, data = DF, ...){
  R2a <- summary(model)$adj.r.squared
  R2 <- summary(model)$r.squared
  aic <- AIC(model)
  bic <- AIC(model, k = log(nrow(data)))
  se <- summary(model)$sigma
  form <- formula(model)
  ANS <- c(R2 = R2, R2.adj = R2a, AIC = aic, BIC = bic, SE = se)
  ANS
}

MGOF <- mgof(model = modelA, data = VIT2005)
MGOF

Observation: The total proportion of variability (\(R^2\)) explained by modelA is 0.9138.

  1. (10 pts) Quiz-Project 3 Pr.12

Explore the residuals of the Models (A) using the function residualPlot() or residualPlots() from the package car. Comment on the results. (Diagnostics: Checking the model assumptions)

# uncomment to run the residual plot for the modelA
# residualPlot(modelA, main = "Model A")

Load the necessary library

library(car)

Plot the residuals for Model A

residualPlot(modelA, main = “Residual Plot for Model A”)

(d) Question 2: Is there curvature of the residuals on the above plot?

Observation: The residuals versus the fitted values for Model (A) have a definite curvature indicating the model is not quite adequate.

Extra Credit (10 pts) Quiz-Project 3 Pr.13

(e) Use the function boxCox() from car to find a suitable transformation for totalprice. Build Model (E) Use backward elimination to develop a model that predicts log(totalprice) using the data frame VIT2005. Use a “P-value-to remove” of 5%. Store the final model in the object modelE.

For details on boxCox() see page 856 in text

boxCox(modelA, lambda = seq(-0.5, 0.5, length = 200))

Observation:

A log transformation is suggested for the response totalprice in Model (A). # Load necessary libraries library(car)

Step 1: Find the Suitable Transformation using boxCox()

Applying the Box-Cox transformation for modelA to determine lambda

boxCox(modelA, lambda = seq(-0.5, 0.5, length = 200))

Step 2: Create Model (E) by transforming the response variable using log

Log transformation of totalprice

modelE <- lm(log(totalprice) ~ area + age + floor + rooms + toilets + garage + elevator + storage, data = VIT2005)

Step 3: Apply Backward Elimination using p-value of 5% for removal

Start with the full model and use step() to perform backward elimination

modelE_final <- step(modelE, direction = “backward”, k = log(nrow(VIT2005)))

Display the final model after backward elimination

summary(modelE_final)

Extra Credit (20 pts) Quiz-Project 3 Pr.14 - 4 pts for excluding the correct variable at each step.

Model (E):

Use backward elimination to develop a model that predicts log(totalprice) using the data frame VIT2005. Use a “P-value-to remove” of 5%. Store the final model in the object modelE.

Model (E) The functions drop1() and update() are used to create a model using backward elimination. (as shown at the beginning)

Load necessary libraries

library(car)

Step 1: Log transformation of totalprice

modelE <- lm(log(totalprice) ~ area + age + floor + rooms + toilets + garage + elevator + storage, data = VIT2005)

Step 2: Backward elimination using drop1() and update()

Initial model

summary(modelE)

Step 3: Perform backward elimination

Drop variable with highest p-value > 0.05

drop1(modelE, test = “F”) # Check for p-values

Suppose ‘storage’ has the highest p-value (greater than 0.05) in the initial model

modelE <- update(modelE, . ~ . - storage)

Check the model again

drop1(modelE, test = “F”)

Suppose ‘age’ has the next highest p-value > 0.05

modelE <- update(modelE, . ~ . - age)

Check the model again

drop1(modelE, test = “F”)

Suppose ‘floor’ has the next highest p-value > 0.05

modelE <- update(modelE, . ~ . - floor)

Check the model again

drop1(modelE, test = “F”)

Continue this process until all variables have p-values < 0.05

The final model (modelE) should have predictors with significant p-values

Step 4: Final Model

summary(modelE)

# transform the response variable using natural log transformation

VITNEW <- VIT2005 %>% mutate(logtotalprice = log(totalprice))

# build the fitted full model
model.be <- lm(logtotalprice ~ ., data = VITNEW[ ,-1]) # exclude the totalprice variable since it is no longer response variable
drop1(model.be, test = "F")

and so on.

LS0tDQp0aXRsZTogIlNUQVQyNzAgLSBQcm9qZWN0IDMgLSBNb2RlbGluZyB3aXRoIFJlZ3Jlc3Npb24iDQphdXRob3I6ICJBcnRodXIgQ29sZW1hbiINCmRhdGU6ICdgciBTeXMuRGF0ZSgpYCcNCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCmBgYHtyIHBhY2thZ2VzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMgbG9hZCB0aGUgcGFja2FnZXMgZm9yIGdyYXBoaW5nIGFuZCBkYXRhIHdyYW5nbGluZw0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShQQVNXUjIpDQpsaWJyYXJ5KGNhcikgIyBpbnN0YWxsIHRoZSBjYXIgcGFja2FnZSBmaXJzdCwgaWYgbm90IGFscmVhZHkgaW5zdGFsbGVkDQpsaWJyYXJ5KGRwbHlyKSANCmxpYnJhcnkobGF0dGljZSkNCmxpYnJhcnkoYm9vdCkNCmxpYnJhcnkoTUFTUykNCmBgYA0KDQoqKk5vdGU6KiogSWYgeW91IGBSbWRgIGZpbGUgc3VibWlzc2lvbiBrbml0cyB5b3Ugd2lsbCByZWNlaXZlIHRvdGFsIG9mICoqKDUgcG9pbnRzKSoqDQoNCiMjIENhc2UgU3R1ZHk6IFJlYWwgRXN0YXRlDQoNCkRhdGEgYW5kIGlkZWFzIGZvciB0aGlzIGNhc2Ugc3R1ZHkgY29tZSBmcm9tICgqTWlsaXRpbm8gZXQgYWwuLCAyMDA0KikuDQoNCioqUHJvYmxlbSAxNiwgcGFnZSA4OTkqKiAoKipub3QgZW50aXJlIHByb2JsZW0qKiwgb25seSBzcGVjaWZpZWQgcGFydHMgYmVsb3cpDQoNClRoZSBnb2FsIG9mIHRoaXMgY2FzZSBzdHVkeSBpcyB0byB3YWxrIHRoZSB1c2VyIHRocm91Z2ggdGhlIGNyZWF0aW9uIG9mIGEgcGFyc2ltb25pb3VzIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRoYXQgY2FuIGJlIHVzZWQgdG8gcHJlZGljdCB0aGUgdG90YWwgcHJpY2UgKHRvdGFscHJpY2UpIG9mIGFwYXJ0bWVudHMgYnkgdGhlaXIgaGVkb25pYyAoc3RydWN0dXJhbCkgY2hhcmFjdGVyaXN0aWNzLiBUaGUgZGF0YSBmcmFtZSBgVklUMjAwNWAgY29udGFpbnMgc2V2ZXJhbCB2YXJpYWJsZXMsIGFuZCBmdXJ0aGVyIGRlc2NyaXB0aW9uIG9mIHRoZSBkYXRhIGNhbiBiZSBmb3VuZCBpbiB0aGUgaGVscCBmaWxlIChsaXN0ZWQgYmVsb3cpLg0KDQpBIGRhdGEgZnJhbWUgd2l0aCAyMTggb2JzZXJ2YXRpb25zIG9uIHRoZSBmb2xsb3dpbmcgMTUgdmFyaWFibGVzOg0KDQotICAgYHRvdGFscHJpY2VgICh0aGUgbWFya2V0IHRvdGFsIHByaWNlIChpbiBFdXJvcykgb2YgdGhlIGFwYXJ0bWVudCBpbmNsdWRpbmcgZ2FyYWdlKHMpIGFuZCBzdG9yYWdlIHJvb20ocykpDQoNCi0gICBgYXJlYWAgKHRoZSB0b3RhbCBsaXZpbmcgYXJlYSBvZiB0aGUgYXBhcnRtZW50IGluIHNxdWFyZSBtZXRlcnMpDQoNCi0gICBgem9uZWAgKGEgZmFjdG9yIGluZGljYXRpbmcgdGhlIG5laWdoYm9yaG9vZCB3aGVyZSB0aGUgYXBhcnRtZW50IGlzIGxvY2F0ZWQgd2l0aCBsZXZlbHMgYFoxMSwgWjIxLCBaMzEsIFozMiwgWjM0LCBaMzUsIFozNiwgWjM3LCBaMzgsIFo0MSwgWjQyLCBaNDMsIFo0NCwgWjQ1LCBaNDYsIFo0NywgWjQ4LCBaNDksIFo1MiwgWjUzLCBaNTYsIFo2MSwgYW5kIFo2MmApDQoNCi0gICBgY2F0ZWdvcnlgIChhIGZhY3RvciBpbmRpY2F0aW5nIHRoZSBjb25kaXRpb24gb2YgdGhlIGFwYXJ0bWVudCB3aXRoIGxldmVscyBgMkEsIDJCLCAzQSwgM0IsIDRBLCA0QmAsIGFuZCBgNUFgIG9yZGVyZWQgc28gdGhhdCBgMkFgIGlzIHRoZSBiZXN0IGFuZCBgNUFgIGlzIHRoZSB3b3JzdCkNCg0KLSAgIGBhZ2VgIChhZ2Ugb2YgdGhlIGFwYXJ0bWVudCBpbiB5ZWFycykNCg0KLSAgIGBmbG9vcmAgKGZsb29yIG9uIHdoaWNoIHRoZSBhcGFydG1lbnQgaXMgbG9jYXRlZCkNCg0KLSAgIGByb29tc2AgKHRvdGFsIG51bWJlciBvZiByb29tcyBpbmNsdWRpbmcgYmVkcm9vbXMsIGRpbmluZyByb29tLCBhbmQga2l0Y2hlbikNCg0KLSAgIGBvdXRgIChhIGZhY3RvciBpbmRpY2F0aW5nIHRoZSBwZXJjZW50IG9mIHRoZSBhcGFydG1lbnQgZXhwb3NlZCB0byB0aGUgZWxlbWVudHM6IFRoZSBsZXZlbHMgYEUxMDAsIEU3NSwgRTUwLCBhbmQgRTI1YCwgY29ycmVzcG9uZCB0byBjb21wbGV0ZSBleHBvc3VyZSwgYDc1YCUgZXhwb3N1cmUsIGA1MGAlIGV4cG9zdXJlLCBhbmQgYDI1YCUgZXhwb3N1cmUsIHJlc3BlY3RpdmVseS4pDQoNCi0gICBgY29uc2VydmF0aW9uYCAoaXMgYW4gb3JkZXJlZCBmYWN0b3IgaW5kaWNhdGluZyB0aGUgc3RhdGUgb2YgY29uc2VydmF0aW9uIG9mIHRoZSBhcGFydG1lbnQuIFRoZSBsZXZlbHMgYDFBLCAyQSwgMkIsIGFuZCAzQWAgYXJlIG9yZGVyZWQgZnJvbSBiZXN0IHRvIHdvcnN0IGNvbnNlcnZhdGlvbi4pDQoNCi0gICBgdG9pbGV0c2AgKHRoZSBudW1iZXIgb2YgYmF0aHJvb21zKQ0KDQotICAgYGdhcmFnZWAgKHRoZSBudW1iZXIgb2YgZ2FyYWdlcykNCg0KLSAgIGBlbGV2YXRvcmAgKGluZGljYXRlcyB0aGUgYWJzZW5jZSAoMCkgb3IgcHJlc2VuY2UgKDEpIG9mIGVsZXZhdG9ycy4pDQoNCi0gICBgc3RyZWV0Y2F0ZWdvcnlgIChhbiBvcmRlcmVkIGZhY3RvciBmcm9tIGJlc3QgdG8gd29yc3QgaW5kaWNhdGluZyB0aGUgY2F0ZWdvcnkgb2YgdGhlIHN0cmVldCB3aXRoIGxldmVscyBgUzIsIFMzLCBTNCwgYW5kIFM1YCkNCg0KLSAgIGBoZWF0aW5nYCAoYSBmYWN0b3IgaW5kaWNhdGluZyB0aGUgdHlwZSBvZiBoZWF0aW5nIHdpdGggbGV2ZWxzIGAxQSwgM0EsIDNCLCBhbmQgNEFgIHdoaWNoIGNvcnJlc3BvbmQgdG86IG5vIGhlYXRpbmcsIGxvdy1zdGFuZGFyZCBwcml2YXRlIGhlYXRpbmcsIGhpZ2gtc3RhbmRhcmQgcHJpdmF0ZSBoZWF0aW5nLCBhbmQgY2VudHJhbCBoZWF0aW5nLCByZXNwZWN0aXZlbHkuKQ0KDQotICAgYHN0b3JhZ2VgICh0aGUgbnVtYmVyIG9mIHN0b3JhZ2Ugcm9vbXMgb3V0c2lkZSBvZiB0aGUgYXBhcnRtZW50KQ0KDQojIyMgQ29tcGxldGUgdGhlIHBhcnRzIGJlbG93DQoNCioqKDEwIHB0cykqKiAqKlF1aXotUHJvamVjdCAzIFByLjEqKg0KDQooYSkgQ2hhcmFjdGVyaXplIHRoZSBzaGFwZSwgY2VudGVyLCBhbmQgc3ByZWFkIG9mIHRoZSB2YXJpYWJsZSBgdG90YWxwcmljZWAuDQoNCkhpbnQ6IFVzZSBgZ2dwbG90YCBmdW5jdGlvbiBmcm9tIGBnZ3Bsb3QyYCBwYWNrYWdlIHRvIGdyYXBoIHRoZSBgdG90YWxwcmljZWAgZGVuc2l0eSBmdW5jdGlvbi4gVXNlIGBtZWRpYW5gIGFuZCBgSVFSYCB0byBmaW5kIHRoZSBtZWRpYW4gYW5kIElRUiBmb3IgdGhlIGB0b3RhbHByaWNlYC4gRmlsbCBpbiB0aGUgYmxhbmsgc3BhY2VzIGluIHRoZSBvYnNlcnZhdGlvbiBiZWxvdzoNCg0KKipTb2x1dGlvbjoqKiBIaW50OiBVc2UgdGhlIHRlbXBsYXRlIGZvciB0aGUgZ3JhcGg6DQoNCmBnZ3Bsb3QoZGF0YSA9IFlPVVIgREFUQSwgYWVzKHggPSB2YXJpYWJsZSB0byBwbG90KSkgKyAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAieW91ciBmYXZvcml0ZSBjb2xvciIpICsgIHRoZW1lX2J3KClgDQoNCllPVVIgQ09ERSBIRVJFOg0KDQpsaWJyYXJ5KGdncGxvdDIpDQojIFBsb3QgdGhlIGRlbnNpdHkgZnVuY3Rpb24gb2YgJ3RvdGFscHJpY2UnDQpnZ3Bsb3QoZGF0YSA9IFZJVDIwMDUsIGFlcyh4ID0gdG90YWxwcmljZSkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAibGlnaHRibHVlIikgKw0KICB0aGVtZV9idygpICsNCiAgZ2d0aXRsZSgiRGVuc2l0eSBQbG90IG9mIFRvdGFsIFByaWNlIikNCg0KIyBDYWxjdWxhdGUgdGhlIG1lZGlhbiBhbmQgSVFSIGZvciAndG90YWxwcmljZScNCm1lZGlhbl90b3RhbHByaWNlIDwtIG1lZGlhbihWSVQyMDA1JHRvdGFscHJpY2UpDQppcXJfdG90YWxwcmljZSA8LSBJUVIoVklUMjAwNSR0b3RhbHByaWNlKQ0KDQptZWRpYW5fdG90YWxwcmljZQ0KaXFyX3RvdGFscHJpY2UNCg0KYGBge3J9DQoNCg0KIyBNRCA8LSBtZWRpYW4oVklUMjAwNSR0b3RhbHByaWNlKQ0KIyANCiMgaXFyIDwtIElRUihWSVQyMDA1JHRvdGFscHJpY2UpDQojIA0KIyBjKE1ELCBpcXIpDQpgYGANCg0KKipPYnNlcnZhdGlvbjoqKiBUaGUgZGlzdHJpYnV0aW9uIG9mIGB0b3RhbHByaWNlYCBpcyAuLi4gd2l0aCBhIG1lZGlhbiBvZiBgLi4uYCBhbmQgYW4gYElRUmAgb2YgYC4uLmANCg0KKiooMTAgcHRzKSoqICoqUXVpei1Qcm9qZWN0IDMgUHIuMioqDQoNCihiKSBVc2UgYHNjYXR0ZXJwbG90TWF0cml4KClgIGZyb20gYGNhcmAgcGFja2FnZSBvciBgcGFpcnMoKWAgdG8gZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRvdGFscHJpY2UgYW5kIHRoZSBudW1lcmljYWwgZXhwbGFuYXRvcnkgdmFyaWFibGVzIGBhcmVhLCBhZ2UsIGZsb29yLCByb29tcywgdG9pbGV0cywgZ2FyYWdlLCBlbGV2YXRvcmAsIGFuZCBgc3RvcmFnZWAuDQoNCioqSGludDoqKiBUbyB1c2UgYHNjYXR0ZXJwbG90TWF0cml4YCB0eXBlIGBzY2F0dGVycGxvdE1hdHJpeCggfiB0b3RhbHByaWNlICsgdmFyMSArIHZhcjIgKyAuLi4gKyB2YXIgbiwgZGF0YSA9IFZJVDIwMDUpYCwgZG8gbm90IHVzZSBtb3JlIHRoYW4gNSB2YXJpYWJsZXMgdG8gcHJvZHVjZSBpbnB1dCB0aGF0IGZpdHMgdGhlIHNjcmVlbiBhbmQgY2FuIGJlIHJldmlld2VkLiBVc2UgdGhlIGNvbW1hbmQgYXMgbWFueSB0aW1lcyBhcyB5b3UgbmVlZCB0byByZXZpZXcgaG93IGB0b3RhbHByaWNlYCBjb3JyZWxhdGVzIHdpdGggb3RoZXIgdmFyaWFibGVzIGluIHRoZSBkYXRhLg0KDQoqKlNvbHV0aW9uOioqDQoNCllPVVIgQ09ERSBIRVJFOg0KbGlicmFyeShjYXIpDQoNCnNjYXR0ZXJwbG90TWF0cml4KCB+IHRvdGFscHJpY2UgKyBhcmVhICsgYWdlICsgZmxvb3IgKyByb29tcywgZGF0YSA9IFZJVDIwMDUsIHNtb290aCA9IEZBTFNFKQ0KDQpzY2F0dGVycGxvdE1hdHJpeCggfiB0b3RhbHByaWNlICsgdG9pbGV0cyArIGdhcmFnZSArIGVsZXZhdG9yICsgc3RvcmFnZSwgZGF0YSA9IFZJVDIwMDUsIHNtb290aCA9IEZBTFNFKQ0KDQoNCmBgYHtyfQ0KIyBlLmcuIG1hdHJpeCBjYW4gYmUgcHJvZHVjZWQgd2l0aA0Kc2NhdHRlcnBsb3RNYXRyaXgoIH4gdG90YWxwcmljZSArIGFyZWEgKyBhZ2UgKyBmbG9vciArIHJvb21zLCBkYXRhID0gVklUMjAwNSkNCg0Kc2NhdHRlcnBsb3RNYXRyaXgoIH4gdG90YWxwcmljZSArIGVsZXZhdG9yICsgc3RvcmFnZSArIHRvaWxldHMsIGRhdGEgPSBWSVQyMDA1ICkNCg0KIyBvciB1c2UgDQojIHBhaXJzKH4gdG90YWxwcmljZSArIGFyZWEgKyBhZ2UgKyBmbG9vciArIHJvb21zLCBkYXRhID0gVklUMjAwNSkNCiMgDQojIHBhaXJzKH4gdG90YWxwcmljZSArIHRvaWxldHMgKyBnYXJhZ2UgKyBlbGV2YXRvciArIHN0b3JhZ2UsIGRhdGEgPSBWSVQyMDA1KQ0KYGBgDQoNCk9ic2VydmF0aW9uOiBUaGUgdmFyaWFibGUgYHRvdGFscHJpY2VgIGFwcGVhcnMgdG8gaGF2ZSBhIG1vZGVyYXRlIGxpbmVhciByZWxhdGlvbnNoaXAgd2l0aCBgYXJlYWAuDQoNCihjKSAqKlRvdGFsIG9mICg1NSBwdHMpKiogKipRdWl6LVByb2plY3QgMyoqIFByLjMgdG8gOCwgMTAgcHRzIGZvciBlYWNoIGNvcnJlY3RseSByZW1vdmVkIHZhcmlhYmxlLCA1IHB0cyB0byBmaW5kIHRoZSBjb3JyZWxhdGlvbi4NCg0KQ29tcHV0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBgdG90YWxwcmljZWAgYW5kIGFsbCBvZiB0aGUgb3RoZXIgbnVtZXJpY2FsIHZhcmlhYmxlcy4gTGlzdCB0aGUgKip0aHJlZSoqIHZhcmlhYmxlcyBpbiBvcmRlciBhbG9uZyB3aXRoIHRoZWlyIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyB0aGF0IGhhdmUgdGhlIGhpZ2hlc3QgY29ycmVsYXRpb24gd2l0aCB0b3RhbHByaWNlLg0KDQojIyMjICoqTW9kZWwgKEEpKio6IFVzZSBiYWNrd2FyZCBlbGltaW5hdGlvbiB0byBkZXZlbG9wIGEgbW9kZWwgdGhhdCBwcmVkaWN0cyB0b3RhbHByaWNlIHVzaW5nIHRoZSBkYXRhIGZyYW1lIGBWSVQyMDA1YC4gVXNlIGEgIlAtdmFsdWUtdG8gcmVtb3ZlIiBvZiBgNWAlLiBTdG9yZSB0aGUgZmluYWwgbW9kZWwgaW4gdGhlIG9iamVjdCBgbW9kZWxBYC4NCg0KKiooNSBwdHMpKiogKipRdWl6LVByb2plY3QgMyBQci4zKioNCg0KVGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBhcmU6DQoNCmBgYHtyfQ0KTlVNIDwtIGMoImFyZWEiLCAiYWdlIiwgImZsb29yIiwgInJvb21zIiwgInRvaWxldHMiLCAiZ2FyYWdlIiwiZWxldmF0b3IiLCAic3RvcmFnZSIpDQpDT1IgPC0gY29yKFZJVDIwMDVbLCAidG90YWxwcmljZSJdLCBWSVQyMDA1WywgTlVNXSkNCkNPUg0KYGBgDQoNCioqT2JzZXJ2YXRpb246KiogVGhlIGhpZ2hlc3QgdGhyZWUgY29ycmVsYXRpb25zIHdpdGggYHRvdGFscHJpY2VgIG9jY3VyIHdpdGggYGFyZWFgICgwLjgwOTIpLCBgdG9pbGV0c2AgKDAuNjg3NiksIGFuZCBgcm9vbXNgKDAuNTI1NikuDQoNCioqTW9kZWwgKEEpKiogVGhlIGZ1bmN0aW9ucyBgZHJvcDEoKWAgYW5kIGB1cGRhdGUoKWAgYXJlIHVzZWQgdG8gY3JlYXRlIGEgbW9kZWwgdXNpbmcgYmFja3dhcmQgZWxpbWluYXRpb24uDQoNCioqSGludDoqKiBgZHJvcDEobW9kZWxfLmJlX25hbWUsIHRlc3QgPSAiRiIpYCB0ZXN0IGZvciBzaWduaWZpY2FuY2Ugb2YgYWxsIGluZGl2aWR1YWwgcHJlZGljdG9ycyBnaXZlbiBhbGwgb3RoZXJzIGFyZSBhbHJlYWR5IGluIHRoZSBtb2RlbC4NCg0KYGBge3J9DQojIHVzZSBhIG1vZGVsIHdoZXJlIHRvdGFscHJpY2UgaXMgcmVncmVzc2VkIG9uIGFsbCBvdGhlciB2YXJpYWJsZXMNCm1vZGVsLmJlIDwtIGxtKHRvdGFscHJpY2UgfiAuLCBkYXRhID0gVklUMjAwNSkNCg0KIyANCmRyb3AxKG1vZGVsLmJlLCB0ZXN0ID0gIkYiKQ0KYGBgDQoNCioqKDEwIHB0cykqKiAocGFydCBjLjEpICoqUXVpei1Qcm9qZWN0IDMgUHIuNCoqDQoNCldoaWNoIG9uZSBhcHBlYXJzIG1vc3QgKippbnNpZ25pZmljYW50KiogKGJpZ2dlc3QgYFAtdmFsdWVgKT8gRHJvcCBpdCBmcm9tIHRoZSBtb2RlbC4NCg0KRS5nLiBJZiBgYWdlYCBpcyBtb3N0IGluc2lnbmlmaWNhbnQsIHVzZSB0aGUgYHVwZGF0ZWAgZnVuY3Rpb24gdG8gdXBkYXRlIHRoZSBtb2RlbCB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZToNCg0KYG1vZGVsLmJlIDwtIHVwZGF0ZShtb2RlbC5iZSwgLn4uIC0gYWdlKWAgLSB0aGUgZmlyc3QgZG90IG1lYW5zIHdlIHVzZSB0aGUgc2FtZSByZXNwb25zZSB2YXJpYWJsZSwgdGhlIHNlY29uZCBhbGwgcHJldmlvdXNseSBpbmNsdWRlZCBwcmVkaWN0b3JzIG1pbnVzIGBhZ2VgLiBUaGUgYWdhaW4gdXNlIGBkcm9wMWAgYW5kIHNvIG9uLCB1bnRpbCBhbGwgcmVtYWluaW5nIHZhcmlhYmxlcyBhcmUgb2Ygc2lnbmlmaWNhbmNlIGAwLjA1YCBhcyBzcGVjaWZpZWQgaW4gdGhlIGRpcmVjdGlvbnMgb2YgdGhlIHByb2JsZW0NCg0KWU9VUiBDT0RFIEhFUkU6DQptb2RlbC5iZSA8LSBsbSh0b3RhbHByaWNlIH4gYXJlYSArIGFnZSArIGZsb29yICsgcm9vbXMgKyB0b2lsZXRzICsgZ2FyYWdlICsgZWxldmF0b3IgKyBzdG9yYWdlLCBkYXRhID0gVklUMjAwNSkNCg0KZHJvcDEobW9kZWwuYmUsIHRlc3QgPSAiRiIpDQptb2RlbC5iZSA8LSB1cGRhdGUobW9kZWwuYmUsIC4gfiAuIC0gYWdlKQ0KZHJvcDEobW9kZWwuYmUsIHRlc3QgPSAiRiIpDQptb2RlbC5iZSA8LSB1cGRhdGUobW9kZWwuYmUsIC4gfiAuIC0gZmxvb3IpDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCg0KbW9kZWwuYmUgPC0gdXBkYXRlKG1vZGVsLmJlLCAuIH4gLiAtIHJvb21zKQ0KDQoNCmRyb3AxKG1vZGVsLmJlLCB0ZXN0ID0gIkYiKQ0KDQptb2RlbC5iZSA8LSB1cGRhdGUobW9kZWwuYmUsIC4gfiAuIC0gc3RvcmFnZSkNCg0KDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCg0KDQptb2RlbC5iZSA8LSB1cGRhdGUobW9kZWwuYmUsIC4gfiAuIC0gZWxldmF0b3IpDQoNCg0KZHJvcDEobW9kZWwuYmUsIHRlc3QgPSAiRiIpDQoNCnRvdGFscHJpY2UgfiBhcmVhICsgdG9pbGV0cyArIGdhcmFnZQ0KDQoNCmBgYHtyfQ0KbW9kZWwuYmUgPC0gdXBkYXRlKG1vZGVsLmJlLCAufi4gLSBhZ2UpDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCmBgYA0KDQoqKigxMCBwdHMpKiogKHBhcnQgYy4yKSAqKlF1aXotUHJvamVjdCAzIFByLjUqKg0KDQpXaGljaCBvbmUgaXMgdG8gYmUgZHJvcHBlZCBuZXh0Pw0KDQpBbnN3ZXI6DQptb2RlbC5iZSA8LSB1cGRhdGUobW9kZWwuYmUsIC4gfiAuIC0gZmxvb3IpDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCg0KYGBge3J9DQoNCg0KYGBgDQoNCioqKDEwIHB0cykqKiAocGFydCBjLjMpICoqUXVpei1Qcm9qZWN0IDMgUHIuNioqIFdoaWNoIG9uZSBpcyB0byBiZSBkcm9wcGVkIG5leHQ/DQoNCkFuc3dlcjoNCm1vZGVsLmJlIDwtIHVwZGF0ZShtb2RlbC5iZSwgLiB+IC4gLSByb29tcykNCg0KDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCmBgYHtyfQ0KDQoNCmBgYA0KDQoqKigxMCBwdHMpKiogKHBhcnQgYy40KSAqKlF1aXotUHJvamVjdCAzIFByLjcqKiBXaGljaCBvbmUgaXMgdG8gYmUgZHJvcHBlZCBuZXh0Pw0KDQpBbnN3ZXI6DQoNCmBgYHtyfQ0KDQoNCmBgYA0KDQoqKigxMCBwdHMpKiogKHBhcnQgYy41KSAqKlF1aXotUHJvamVjdCAzIFByLjgqKiBXaGljaCBvbmUgaXMgdG8gYmUgZHJvcHBlZCBuZXh0Pw0KDQpBbnN3ZXI6DQptb2RlbC5iZSA8LSB1cGRhdGUobW9kZWwuYmUsIC4gfiAuIC0gc3RvcmFnZSkNCg0KYGBge3J9DQoNCmBgYA0KDQpJZiBhbGwgdmFyaWFibGUgYXJlIHNpZ25pZmljYW50IGNyZWF0ZSB0aGUgb2JqZWN0IGhvbGRpbmcgdGhlIG9wdGltYWwgbW9kZWwgYXMgcGVyIHRoZSBkZXNjcmlwdGlvbiBvZiB0aGUgcHJvYmxlbSB1c2luZyB0aGUgY29kZToNCg0KYGBge3J9DQpmb3JtdWxhKG1vZGVsLmJlKQ0KDQptb2RlbEEgPC0gbG0oZm9ybXVsYShtb2RlbC5iZSksIGRhdGEgPSBWSVQyMDA1KQ0KDQpgYGANCg0KKipRdWVzdGlvbiAxOiAoNSBwdHMpKiogKipRdWl6LVByb2plY3QgMyBQci45KiogV2hpY2ggdmFyaWFibGVzIGFyZSBsZWZ0IGluIHRoZSBtb2RlbCwgbGlzdCB0aGVtPw0KDQpPYnNlcnZhdGlvbjogQmFja3dhcmQgZWxpbWluYXRpb24gc3VnZ2VzdHMgdXNpbmcgdGhlIHZhcmlhYmxlcyBgYXJlYWAsIGB6b25lYCwgYGNhdGVnb3J5YCwgYG91dGAsIGB0b2lsZXRzYCwgYGdhcmFnZWAsIGBlbGV2YXRvcmAsIGBzdHJlZXRjb3JuZXJgLCBhbmQgYGhlYXRpbmdgIHRvIGJlc3QgcHJlZGljdCBgdG90YWxwcmljZWAuDQphcmVhDQoNCnpvbmUNCg0KY2F0ZWdvcnkNCg0Kb3V0DQoNCnRvaWxldHMNCg0KZ2FyYWdlDQoNCmVsZXZhdG9yDQoNCnN0cmVldGNvcm5lcg0KDQpoZWF0aW5nDQoNCg0KKGkpICoqKDUgcHRzKSoqICoqUXVpei1Qcm9qZWN0IDMgUHIuMTAqKg0KDQoNCiMgU2V0IHRoZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDUpDQoNCiMgQXNzdW1pbmcgbW9kZWxBIGlzIGFscmVhZHkgZml0dGVkLCBlLmcuOg0KIyBtb2RlbEEgPC0gbG0odG90YWxwcmljZSB+IGFyZWEgKyB0b2lsZXRzICsgZ2FyYWdlICsgZWxldmF0b3IsIGRhdGEgPSBWSVQyMDA1KQ0KDQojIExvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJ5DQpsaWJyYXJ5KGJvb3QpDQoNCiMgQ29tcHV0ZSBsZWF2ZS1vbmUtb3V0IGNyb3NzLXZhbGlkYXRpb24gZXJyb3IgKENWX24pDQpjdl9lcnJvcl9uIDwtIGN2LmdsbShkYXRhID0gVklUMjAwNSwgZ2xtZml0ID0gbW9kZWxBKQ0KY3ZfZXJyb3JfbiRkZWx0YVsxXQ0KDQojIENvbXB1dGUgZml2ZS1mb2xkIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IgKENWXzUpDQpjdl9lcnJvcl81IDwtIGN2LmdsbShkYXRhID0gVklUMjAwNSwgZ2xtZml0ID0gbW9kZWxBLCBLID0gNSkNCmN2X2Vycm9yXzUkZGVsdGFbMV0NCg0KDQpgYGANCg0KKGlpKSAqKig1IHB0cykqKiAqKlF1aXotUHJvamVjdCAzIFByLjExKioNCg0KQ29tcHV0ZSAkUl4yJCwgJFJeMl9hJCwgdGhlIGBBSUNgLCBhbmQgdGhlIGBCSUNgIGZvciAqKk1vZGVsIChBKSoqLiBXaGF0IGlzIHRoZSBwcm9wb3J0aW9uIG9mIHRvdGFsIHZhcmlhYmlsaXR5IGV4cGxhaW5lZCBieSAqKk1vZGVsIChBKSoqPw0KDQoqKllvdXIgU29sdXRpb246KioNCiMgRml0IHRoZSBtb2RlbCAoaWYgbm90IGFscmVhZHkgZml0dGVkKQ0KIyBtb2RlbEEgPC0gbG0odG90YWxwcmljZSB+IGFyZWEgKyB0b2lsZXRzICsgZ2FyYWdlICsgZWxldmF0b3IsIGRhdGEgPSBWSVQyMDA1KQ0KDQojIENvbXB1dGUgUi1zcXVhcmVkIChSXjIpDQpSMiA8LSBzdW1tYXJ5KG1vZGVsQSkkci5zcXVhcmVkDQoNCiMgQ29tcHV0ZSBBZGp1c3RlZCBSLXNxdWFyZWQgKFJeMl9hKQ0KUjJfYSA8LSBzdW1tYXJ5KG1vZGVsQSkkYWRqLnIuc3F1YXJlZA0KDQojIENvbXB1dGUgQUlDDQpBSUNfdmFsdWUgPC0gQUlDKG1vZGVsQSkNCg0KIyBDb21wdXRlIEJJQw0KQklDX3ZhbHVlIDwtIEJJQyhtb2RlbEEpDQoNCiMgUHJvcG9ydGlvbiBvZiB0b3RhbCB2YXJpYWJpbGl0eSBleHBsYWluZWQgYnkgTW9kZWwgKEEpDQpwcm9wb3J0aW9uX3ZhcmlhYmlsaXR5X2V4cGxhaW5lZCA8LSBSMg0KDQojIFByaW50IHJlc3VsdHMNCmNhdCgiUi1zcXVhcmVkIChSXjIpOiIsIFIyLCAiXG4iKQ0KY2F0KCJBZGp1c3RlZCBSLXNxdWFyZWQgKFJeMl9hKToiLCBSMl9hLCAiXG4iKQ0KY2F0KCJBSUM6IiwgQUlDX3ZhbHVlLCAiXG4iKQ0KY2F0KCJCSUM6IiwgQklDX3ZhbHVlLCAiXG4iKQ0KY2F0KCJQcm9wb3J0aW9uIG9mIHRvdGFsIHZhcmlhYmlsaXR5IGV4cGxhaW5lZCBieSBNb2RlbCAoQSk6IiwgcHJvcG9ydGlvbl92YXJpYWJpbGl0eV9leHBsYWluZWQsICJcbiIpDQoNCihpKSANCg0KYGBge3J9DQptb2RlbEFnIDwtIGdsbShmb3JtdWxhKG1vZGVsLmJlKSwgZGF0YSA9IFZJVDIwMDUpDQoNCiMgIyBGb3IgY3YuZ2xtIC0gdGhlIGRlZmF1bHQgaXMgdG8gc2V0IEsgZXF1YWwgdG8gdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZGF0YSB3aGljaCBnaXZlcyB0aGUgdXN1YWwgbGVhdmUtb25lLW91dCBjcm9zcy12YWxpZGF0aW9uLg0KIyANCnNldC5zZWVkKDUpICMgdXNlIGZvciByZXBsaWNhdGlvbiBwdXJwb3Nlcw0KY3YuZXJyb3JOIDwtIGN2LmdsbShkYXRhID0gVklUMjAwNSwgZ2xtZml0ID0gIG1vZGVsQWcpDQoNCkNWTmEgPC0gY3YuZXJyb3JOJGRlbHRhWzFdDQpDVk5hDQpgYGANCg0KYGBge3J9DQptb2RlbEFnIDwtIGdsbShmb3JtdWxhKG1vZGVsLmJlKSwgZGF0YSA9IFZJVDIwMDUpDQoNCiMgVU5DT01NRU5UIHRoZSBjb2RlIGJlbG93IGFuZCBydW4gaXQNCg0KIyBzZXQuc2VlZCg1KSAjIHVzZSBmb3IgcmVwbGljYXRpb24gcHVycG9zZXMNCiMgY3YuZXJyb3I1IDwtIGN2LmdsbShkYXRhID0gVklUMjAwNSwgZ2xtZml0ID0gbW9kZWxBZywgSyA9IDUpDQojIENWNWEgPC0gY3YuZXJyb3I1JGRlbHRhWzFdDQojIENWNWENCg0KYGBgDQoNCk9ic2VydmF0aW9uOiBUaGUgJENWX24gPSAuLi4kIGZvciBNb2RlbCAoQSksIGFuZCAkQ1ZfNSA9Li4uJCBmb3IgTW9kZWwgKEEpLg0KDQooaWkpIFNpbmNlIHRoaXMgcHJvYmxlbSBhbmQgYSBmZXcgbW9yZSB3aWxsIHJlcXVlc3QgbWFueSBnb29kbmVzcyBvZiBmaXQgc3RhdGlzdGljcywgYSBmdW5jdGlvbiBjYWxsZWQgYG1nb2YoKWAgaXMgd3JpdHRlbiB0byBjb21wdXRlIHRoZSByZXF1ZXN0ZWQgdmFsdWVzLiAqKlVzZSBpdCBhcyBzaG93biBiZWxvdyoqLg0KDQpgYGB7cn0NCg0KbWdvZiA8LSBmdW5jdGlvbihtb2RlbCA9IG1vZGVsLCBkYXRhID0gREYsIC4uLil7DQogIFIyYSA8LSBzdW1tYXJ5KG1vZGVsKSRhZGouci5zcXVhcmVkDQogIFIyIDwtIHN1bW1hcnkobW9kZWwpJHIuc3F1YXJlZA0KICBhaWMgPC0gQUlDKG1vZGVsKQ0KICBiaWMgPC0gQUlDKG1vZGVsLCBrID0gbG9nKG5yb3coZGF0YSkpKQ0KICBzZSA8LSBzdW1tYXJ5KG1vZGVsKSRzaWdtYQ0KICBmb3JtIDwtIGZvcm11bGEobW9kZWwpDQogIEFOUyA8LSBjKFIyID0gUjIsIFIyLmFkaiA9IFIyYSwgQUlDID0gYWljLCBCSUMgPSBiaWMsIFNFID0gc2UpDQogIEFOUw0KfQ0KDQpNR09GIDwtIG1nb2YobW9kZWwgPSBtb2RlbEEsIGRhdGEgPSBWSVQyMDA1KQ0KTUdPRg0KYGBgDQoNCk9ic2VydmF0aW9uOiBUaGUgdG90YWwgcHJvcG9ydGlvbiBvZiB2YXJpYWJpbGl0eSAoJFJeMiQpIGV4cGxhaW5lZCBieSBtb2RlbEEgaXMgYDAuOTEzOGAuDQoNCihkKSAqKigxMCBwdHMpKiogKipRdWl6LVByb2plY3QgMyBQci4xMioqDQoNCkV4cGxvcmUgdGhlIHJlc2lkdWFscyBvZiB0aGUgTW9kZWxzIChBKSB1c2luZyB0aGUgZnVuY3Rpb24gYHJlc2lkdWFsUGxvdCgpYCBvciBgcmVzaWR1YWxQbG90cygpYCBmcm9tIHRoZSBwYWNrYWdlIGBjYXJgLiBDb21tZW50IG9uIHRoZSByZXN1bHRzLiAoKipEaWFnbm9zdGljczogQ2hlY2tpbmcgdGhlIG1vZGVsIGFzc3VtcHRpb25zKiopDQoNCmBgYHtyfQ0KIyB1bmNvbW1lbnQgdG8gcnVuIHRoZSByZXNpZHVhbCBwbG90IGZvciB0aGUgbW9kZWxBDQojIHJlc2lkdWFsUGxvdChtb2RlbEEsIG1haW4gPSAiTW9kZWwgQSIpDQpgYGANCiMgTG9hZCB0aGUgbmVjZXNzYXJ5IGxpYnJhcnkNCmxpYnJhcnkoY2FyKQ0KDQojIFBsb3QgdGhlIHJlc2lkdWFscyBmb3IgTW9kZWwgQQ0KcmVzaWR1YWxQbG90KG1vZGVsQSwgbWFpbiA9ICJSZXNpZHVhbCBQbG90IGZvciBNb2RlbCBBIikNCg0KKiooZCkgUXVlc3Rpb24gMjoqKiBJcyB0aGVyZSBjdXJ2YXR1cmUgb2YgdGhlIHJlc2lkdWFscyBvbiB0aGUgYWJvdmUgcGxvdD8NCg0KT2JzZXJ2YXRpb246IFRoZSByZXNpZHVhbHMgdmVyc3VzIHRoZSBmaXR0ZWQgdmFsdWVzIGZvciBNb2RlbCAoQSkgaGF2ZSBhIGRlZmluaXRlIGN1cnZhdHVyZSBpbmRpY2F0aW5nIHRoZSBtb2RlbCBpcyBub3QgcXVpdGUgYWRlcXVhdGUuDQoNCioqRXh0cmEgQ3JlZGl0KiogKDEwIHB0cykgKipRdWl6LVByb2plY3QgMyBQci4xMyoqDQoNCioqKGUpKiogVXNlIHRoZSBmdW5jdGlvbiBgYm94Q294KClgIGZyb20gY2FyIHRvIGZpbmQgYSBzdWl0YWJsZSB0cmFuc2Zvcm1hdGlvbiBmb3IgdG90YWxwcmljZS4gQnVpbGQgTW9kZWwgKEUpIFVzZSBiYWNrd2FyZCBlbGltaW5hdGlvbiB0byBkZXZlbG9wIGEgbW9kZWwgdGhhdCBwcmVkaWN0cyBgbG9nKHRvdGFscHJpY2UpYCB1c2luZyB0aGUgZGF0YSBmcmFtZSBgVklUMjAwNWAuIFVzZSBhICJQLXZhbHVlLXRvIHJlbW92ZSIgb2YgNSUuIFN0b3JlIHRoZSBmaW5hbCBtb2RlbCBpbiB0aGUgb2JqZWN0IGBtb2RlbEVgLg0KDQpGb3IgZGV0YWlscyBvbiBgYm94Q294KClgIHNlZSBwYWdlIDg1NiBpbiB0ZXh0DQoNCmBgYHtyfQ0KYm94Q294KG1vZGVsQSwgbGFtYmRhID0gc2VxKC0wLjUsIDAuNSwgbGVuZ3RoID0gMjAwKSkNCmBgYA0KDQojIyMjIE9ic2VydmF0aW9uOg0KDQpBIGBsb2dgIHRyYW5zZm9ybWF0aW9uIGlzIHN1Z2dlc3RlZCBmb3IgdGhlIHJlc3BvbnNlIGB0b3RhbHByaWNlYCBpbiAqKk1vZGVsIChBKSoqLg0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmxpYnJhcnkoY2FyKQ0KDQojIFN0ZXAgMTogRmluZCB0aGUgU3VpdGFibGUgVHJhbnNmb3JtYXRpb24gdXNpbmcgYm94Q294KCkNCiMgQXBwbHlpbmcgdGhlIEJveC1Db3ggdHJhbnNmb3JtYXRpb24gZm9yIG1vZGVsQSB0byBkZXRlcm1pbmUgbGFtYmRhDQpib3hDb3gobW9kZWxBLCBsYW1iZGEgPSBzZXEoLTAuNSwgMC41LCBsZW5ndGggPSAyMDApKQ0KDQojIFN0ZXAgMjogQ3JlYXRlIE1vZGVsIChFKSBieSB0cmFuc2Zvcm1pbmcgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIHVzaW5nIGxvZw0KIyBMb2cgdHJhbnNmb3JtYXRpb24gb2YgdG90YWxwcmljZQ0KbW9kZWxFIDwtIGxtKGxvZyh0b3RhbHByaWNlKSB+IGFyZWEgKyBhZ2UgKyBmbG9vciArIHJvb21zICsgdG9pbGV0cyArIGdhcmFnZSArIGVsZXZhdG9yICsgc3RvcmFnZSwgZGF0YSA9IFZJVDIwMDUpDQoNCiMgU3RlcCAzOiBBcHBseSBCYWNrd2FyZCBFbGltaW5hdGlvbiB1c2luZyBwLXZhbHVlIG9mIDUlIGZvciByZW1vdmFsDQojIFN0YXJ0IHdpdGggdGhlIGZ1bGwgbW9kZWwgYW5kIHVzZSBzdGVwKCkgdG8gcGVyZm9ybSBiYWNrd2FyZCBlbGltaW5hdGlvbg0KbW9kZWxFX2ZpbmFsIDwtIHN0ZXAobW9kZWxFLCBkaXJlY3Rpb24gPSAiYmFja3dhcmQiLCBrID0gbG9nKG5yb3coVklUMjAwNSkpKQ0KDQojIERpc3BsYXkgdGhlIGZpbmFsIG1vZGVsIGFmdGVyIGJhY2t3YXJkIGVsaW1pbmF0aW9uDQpzdW1tYXJ5KG1vZGVsRV9maW5hbCkNCg0KKipFeHRyYSBDcmVkaXQqKiAoMjAgcHRzKSAqKlF1aXotUHJvamVjdCAzIFByLjE0KiogLSA0IHB0cyBmb3IgZXhjbHVkaW5nIHRoZSBjb3JyZWN0IHZhcmlhYmxlIGF0IGVhY2ggc3RlcC4NCg0KIyMjIyAqKk1vZGVsIChFKSoqOg0KDQpVc2UgYmFja3dhcmQgZWxpbWluYXRpb24gdG8gZGV2ZWxvcCBhIG1vZGVsIHRoYXQgcHJlZGljdHMgYGxvZyh0b3RhbHByaWNlKWAgdXNpbmcgdGhlIGRhdGEgZnJhbWUgYFZJVDIwMDVgLiBVc2UgYSAiUC12YWx1ZS10byByZW1vdmUiIG9mIGA1YCUuIFN0b3JlIHRoZSBmaW5hbCBtb2RlbCBpbiB0aGUgb2JqZWN0IGBtb2RlbEVgLg0KDQoqKk1vZGVsIChFKSoqIFRoZSBmdW5jdGlvbnMgYGRyb3AxKClgIGFuZCBgdXBkYXRlKClgIGFyZSB1c2VkIHRvIGNyZWF0ZSBhIG1vZGVsIHVzaW5nIGJhY2t3YXJkIGVsaW1pbmF0aW9uLiAoYXMgc2hvd24gYXQgdGhlIGJlZ2lubmluZykNCg0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMNCmxpYnJhcnkoY2FyKQ0KDQojIFN0ZXAgMTogTG9nIHRyYW5zZm9ybWF0aW9uIG9mIHRvdGFscHJpY2UNCm1vZGVsRSA8LSBsbShsb2codG90YWxwcmljZSkgfiBhcmVhICsgYWdlICsgZmxvb3IgKyByb29tcyArIHRvaWxldHMgKyBnYXJhZ2UgKyBlbGV2YXRvciArIHN0b3JhZ2UsIGRhdGEgPSBWSVQyMDA1KQ0KDQojIFN0ZXAgMjogQmFja3dhcmQgZWxpbWluYXRpb24gdXNpbmcgZHJvcDEoKSBhbmQgdXBkYXRlKCkNCg0KIyBJbml0aWFsIG1vZGVsDQpzdW1tYXJ5KG1vZGVsRSkNCg0KIyBTdGVwIDM6IFBlcmZvcm0gYmFja3dhcmQgZWxpbWluYXRpb24NCiMgRHJvcCB2YXJpYWJsZSB3aXRoIGhpZ2hlc3QgcC12YWx1ZSA+IDAuMDUNCmRyb3AxKG1vZGVsRSwgdGVzdCA9ICJGIikgICMgQ2hlY2sgZm9yIHAtdmFsdWVzDQoNCiMgU3VwcG9zZSAnc3RvcmFnZScgaGFzIHRoZSBoaWdoZXN0IHAtdmFsdWUgKGdyZWF0ZXIgdGhhbiAwLjA1KSBpbiB0aGUgaW5pdGlhbCBtb2RlbA0KbW9kZWxFIDwtIHVwZGF0ZShtb2RlbEUsIC4gfiAuIC0gc3RvcmFnZSkNCg0KIyBDaGVjayB0aGUgbW9kZWwgYWdhaW4NCmRyb3AxKG1vZGVsRSwgdGVzdCA9ICJGIikNCg0KIyBTdXBwb3NlICdhZ2UnIGhhcyB0aGUgbmV4dCBoaWdoZXN0IHAtdmFsdWUgPiAwLjA1DQptb2RlbEUgPC0gdXBkYXRlKG1vZGVsRSwgLiB+IC4gLSBhZ2UpDQoNCiMgQ2hlY2sgdGhlIG1vZGVsIGFnYWluDQpkcm9wMShtb2RlbEUsIHRlc3QgPSAiRiIpDQoNCiMgU3VwcG9zZSAnZmxvb3InIGhhcyB0aGUgbmV4dCBoaWdoZXN0IHAtdmFsdWUgPiAwLjA1DQptb2RlbEUgPC0gdXBkYXRlKG1vZGVsRSwgLiB+IC4gLSBmbG9vcikNCg0KIyBDaGVjayB0aGUgbW9kZWwgYWdhaW4NCmRyb3AxKG1vZGVsRSwgdGVzdCA9ICJGIikNCg0KIyBDb250aW51ZSB0aGlzIHByb2Nlc3MgdW50aWwgYWxsIHZhcmlhYmxlcyBoYXZlIHAtdmFsdWVzIDwgMC4wNQ0KIyBUaGUgZmluYWwgbW9kZWwgKG1vZGVsRSkgc2hvdWxkIGhhdmUgcHJlZGljdG9ycyB3aXRoIHNpZ25pZmljYW50IHAtdmFsdWVzDQoNCiMgU3RlcCA0OiBGaW5hbCBNb2RlbA0Kc3VtbWFyeShtb2RlbEUpDQoNCmBgYHtyfQ0KIyB0cmFuc2Zvcm0gdGhlIHJlc3BvbnNlIHZhcmlhYmxlIHVzaW5nIG5hdHVyYWwgbG9nIHRyYW5zZm9ybWF0aW9uDQoNClZJVE5FVyA8LSBWSVQyMDA1ICU+JSBtdXRhdGUobG9ndG90YWxwcmljZSA9IGxvZyh0b3RhbHByaWNlKSkNCg0KIyBidWlsZCB0aGUgZml0dGVkIGZ1bGwgbW9kZWwNCm1vZGVsLmJlIDwtIGxtKGxvZ3RvdGFscHJpY2UgfiAuLCBkYXRhID0gVklUTkVXWyAsLTFdKSAjIGV4Y2x1ZGUgdGhlIHRvdGFscHJpY2UgdmFyaWFibGUgc2luY2UgaXQgaXMgbm8gbG9uZ2VyIHJlc3BvbnNlIHZhcmlhYmxlDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCg0KYGBgDQoNCmFuZCBzbyBvbi4NCg==