# 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:
totalprice (the market total price (in Euros) of the
apartment including garage(s) and storage room(s))
area (the total living area of the apartment in
square meters)
zone (a factor indicating the neighborhood where the
apartment is located with levels
Z11, Z21, Z31, Z32, Z34, Z35, Z36, Z37, Z38, Z41, Z42, Z43, Z44, Z45, Z46, Z47, Z48, Z49, Z52, Z53, Z56, Z61, and Z62)
category (a factor indicating the condition of the
apartment with levels 2A, 2B, 3A, 3B, 4A, 4B, and
5A ordered so that 2A is the best and
5A is the worst)
age (age of the apartment in years)
floor (floor on which the apartment is
located)
rooms (total number of rooms including bedrooms,
dining room, and kitchen)
out (a factor indicating the percent of the
apartment exposed to the elements: The levels
E100, E75, E50, and E25, correspond to complete exposure,
75% exposure, 50% exposure, and
25% exposure, respectively.)
conservation (is an ordered factor indicating the
state of conservation of the apartment. The levels
1A, 2A, 2B, and 3A are ordered from best to worst
conservation.)
toilets (the number of bathrooms)
garage (the number of garages)
elevator (indicates the absence (0) or presence (1)
of elevators.)
streetcategory (an ordered factor from best to worst
indicating the category of the street with levels
S2, S3, S4, and S5)
heating (a factor indicating the type of heating
with levels 1A, 3A, 3B, and 4A which correspond to: no
heating, low-standard private heating, high-standard private heating,
and central heating, respectively.)
storage (the number of storage rooms outside of the
apartment)
Complete the parts below
(10 pts) Quiz-Project 3 Pr.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
- 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.
- 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
- (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).
- 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.
- (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()
Step 2: Create Model (E) by transforming the response variable using
log
Step 3: Apply Backward Elimination using p-value of 5% for
removal
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 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==