# 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.

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:


ggplot(data = totalprice, aes(x = IQR)) +
 geom_density(fill = "blue") +
 theme_bw()
Error in ggplot(data = totalprice, aes(x = IQR)) : 
  object 'totalprice' not found

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:

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", "toilets", "rooms")
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.

model.be <- lm(totalprice ~ ., data = VIT2005)

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

Model:
totalprice ~ area + zone + category + age + floor + rooms + out + 
    conservation + toilets + garage + elevator + streetcategory + 
    heating + storage
               Df  Sum of Sq        RSS    AIC F value    Pr(>F)    
<none>                       8.5891e+10 4412.6                      
area            1 4.4519e+10 1.3041e+11 4501.7 87.5972 < 2.2e-16 ***
zone           22 1.1171e+11 1.9760e+11 4550.3  9.9910 < 2.2e-16 ***
category        6 9.5199e+09 9.5411e+10 4423.5  3.1219  0.006303 ** 
age             1 3.7563e+06 8.5895e+10 4410.6  0.0074  0.931591    
floor           1 3.8440e+07 8.5929e+10 4410.7  0.0756  0.783639    
rooms           1 1.0656e+09 8.6956e+10 4413.3  2.0967  0.149472    
out             3 3.8946e+09 8.9785e+10 4416.3  2.5544  0.057135 .  
conservation    3 1.0031e+09 8.6894e+10 4409.2  0.6579  0.579069    
toilets         1 4.7971e+09 9.0688e+10 4422.5  9.4389  0.002477 ** 
garage          1 1.4771e+10 1.0066e+11 4445.2 29.0627 2.328e-07 ***
elevator        1 5.4265e+09 9.1317e+10 4424.0 10.6772  0.001314 ** 
streetcategory  3 3.5550e+09 8.9446e+10 4415.5  2.3316  0.076019 .  
heating         3 4.3202e+09 9.0211e+10 4417.3  2.8335  0.039877 *  
storage         1 1.6433e+09 8.7534e+10 4414.8  3.2334  0.073937 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(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 <- update(model.be, .~. - age)
drop1(model.be, test = "F")
Single term deletions

Model:
totalprice ~ area + zone + category + floor + rooms + out + conservation + 
    toilets + garage + elevator + streetcategory + heating + 
    storage
               Df  Sum of Sq        RSS    AIC F value    Pr(>F)    
<none>                       8.5895e+10 4410.6                      
area            1 4.4894e+10 1.3079e+11 4500.3 88.8523 < 2.2e-16 ***
zone           22 1.1220e+11 1.9810e+11 4548.8 10.0942 < 2.2e-16 ***
category        6 9.6605e+09 9.5555e+10 4421.9  3.1866 0.0054598 ** 
floor           1 3.6864e+07 8.5931e+10 4408.7  0.0730 0.7874016    
rooms           1 1.0655e+09 8.6960e+10 4411.3  2.1088 0.1482969    
out             3 4.0439e+09 8.9938e+10 4414.7  2.6678 0.0493562 *  
conservation    3 1.3497e+09 8.7244e+10 4408.0  0.8905 0.4473466    
toilets         1 4.7993e+09 9.0694e+10 4420.5  9.4987 0.0023994 ** 
garage          1 1.4767e+10 1.0066e+11 4443.2 29.2260 2.153e-07 ***
elevator        1 5.6740e+09 9.1569e+10 4422.6 11.2298 0.0009919 ***
streetcategory  3 3.5550e+09 8.9450e+10 4413.5  2.3453 0.0746761 .  
heating         3 4.3716e+09 9.0266e+10 4415.5  2.8840 0.0373395 *  
storage         1 1.6950e+09 8.7590e+10 4412.9  3.3547 0.0687625 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
update(model.be, area = FALSE)
In lm.fit(x, y, offset = offset, singular.ok = singular.ok, ...) :
 extra argument 㤼㸱area㤼㸲 will be disregarded

Call:
lm(formula = totalprice ~ area + zone + category + floor + rooms + 
    out + conservation + toilets + garage + elevator + streetcategory + 
    heating + storage, data = VIT2005, area = FALSE)

Coefficients:
     (Intercept)              area           zoneZ21           zoneZ31           zoneZ32           zoneZ34           zoneZ35           zoneZ36           zoneZ37           zoneZ38           zoneZ41           zoneZ42           zoneZ43  
         78583.7            1348.9           93618.0           77753.4           40574.2           16315.0           60875.7           36073.0           74093.5           21941.1           52112.2           81135.2           36420.4  
         zoneZ44           zoneZ45           zoneZ46           zoneZ47           zoneZ48           zoneZ49           zoneZ52           zoneZ53           zoneZ56           zoneZ61           zoneZ62        category2B        category3A  
         18260.2           15305.1            8484.9          -10403.1           34476.9           29796.0            2877.1           18527.6           23027.2           16272.6           15309.2            -889.6          -22657.6  
      category3B        category4A        category4B        category5A             floor             rooms            outE25            outE50            outE75    conservation2A    conservation2B    conservation3A           toilets  
        -26110.9          -32763.8          -41086.2          -69865.5             230.1            5158.0           35706.5           -4451.9            3090.2           -4938.7           -8117.1           -8949.4           17542.2  
          garage          elevator  streetcategoryS3  streetcategoryS4  streetcategoryS5         heating3A         heating3B         heating4A           storage  
         24874.9           19530.8           13161.3            9543.9           -4119.2          -12243.9           -5427.9             820.0            8530.4  

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

Which one is to be dropped next?

Answer: conservation

model.be <- update(model.be, .~. - age)
drop1(model.be, test = "F")
update(model.be, conservation = FALSE)

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

Answer: rooms

model.be <- update(model.be, .~. - age)
drop1(model.be, test = "F")
update(model.be, rooms = FALSE)

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

Answer:streetcategory

model.be <- update(model.be, .~. - age)
drop1(model.be, test = "F")
update(model.be, streetcategory = FALSE)

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

Answer: storage

model.be <- update(model.be, .~. - age)
drop1(model.be, test = "F")
update(model.be, storage = FALSE)

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)
totalprice ~ area + zone + category + floor + rooms + out + conservation + 
    toilets + garage + elevator + streetcategory + heating + 
    storage
modelA <- lm(formula(model.be), data = VIT2005)

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

“area”, “zone”, “category”, “out”, “toilets”, “garage”, “elevator”, “streetcorner”, and “heating”.

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

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

Compute \(CV_n\), the leave-one-out cross validation error, for modelA. Set the seed to 5 and compute \(CV_5\), the five-fold cross validation error, for modelA. The cross validation error for a generalized linear model can be computed using the cv.glm() from the boot package. Using the function glm() without passing a family argument is the same as using the function lm(). R Code 12.3 provides a template of how to use the cv.glm() function. Note that \(CV_n\) is returned with cv.error$delta[1]. To compute \(CV_5\), pass the value 5 to the argument K inside the cv.glm() function.


mod.glm <- glm(y ~ x1 + x2, data = modelA)
Error in as.data.frame.default(data) : 
  cannot coerce class ‘"lm"’ to a data.frame
  1. (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:

The proportion of total variability is 0.9138.

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

# # Fro 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
          R2       R2.adj          AIC          BIC           SE 
9.175750e-01 8.947869e-01 5.031290e+03 5.197130e+03 2.247804e+04 

Observation: The total proportion of variability 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)

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

Yes, there is a large curve in this 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

Observation: A log transformation is suggested for the response totalprice in Model (A).

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)

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")
Single term deletions

Model:
logtotalprice ~ area + zone + category + age + floor + rooms + 
    out + conservation + toilets + garage + elevator + streetcategory + 
    heating + storage
               Df Sum of Sq     RSS      AIC F value    Pr(>F)    
<none>                      0.97895 -1080.46                      
area            1   0.45808 1.43703  -998.78 79.0794 8.788e-16 ***
zone           22   1.25852 2.23747  -944.25  9.8756 < 2.2e-16 ***
category        6   0.10125 1.08020 -1071.00  2.9132  0.009938 ** 
age             1   0.00061 0.97956 -1082.32  0.1045  0.746909    
floor           1   0.00185 0.98080 -1082.05  0.3191  0.572909    
rooms           1   0.00422 0.98317 -1081.52  0.7279  0.394766    
out             3   0.06721 1.04616 -1071.98  3.8675  0.010426 *  
conservation    3   0.01134 0.99029 -1083.95  0.6523  0.582546    
toilets         1   0.08460 1.06356 -1064.39 14.6057  0.000186 ***
garage          1   0.14304 1.12199 -1052.73 24.6935 1.636e-06 ***
elevator        1   0.14529 1.12424 -1052.29 25.0826 1.373e-06 ***
streetcategory  3   0.03040 1.00935 -1079.79  1.7492  0.158880    
heating         3   0.04007 1.01902 -1077.71  2.3060  0.078550 .  
storage         1   0.03280 1.01175 -1075.27  5.6625  0.018445 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

and so on.

LS0tDQp0aXRsZTogIlNUQVQyNzAgLSBQcm9qZWN0IDMgLSBNb2RlbGluZyB3aXRoIFJlZ3Jlc3Npb24iDQphdXRob3I6ICJNYXR0aGV3IERhbW1lciINCmRhdGU6ICJTcHJpbmcgMjAyMSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KDQoNCmBgYHtyIHBhY2thZ2VzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMgbG9hZCB0aGUgcGFja2FnZXMgZm9yIGdyYXBoaW5nIGFuZCBkYXRhIHdyYW5nbGluZw0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShQQVNXUjIpDQpsaWJyYXJ5KGNhcikgIyBpbnN0YWxsIHRoZSBjYXIgcGFja2FnZSBmaXJzdCwgaWYgbm90IGFscmVhZHkgaW5zdGFsbGVkDQpsaWJyYXJ5KGRwbHlyKSANCmxpYnJhcnkobGF0dGljZSkNCmxpYnJhcnkoYm9vdCkNCmxpYnJhcnkoTUFTUykNCmBgYA0KDQoNCk5vdGU6IElmIHlvdSBgUm1kYCBmaWxlIHN1Ym1pc3Npb24ga25pdHMgeW91IHdpbGwgcmVjZWl2ZSB0b3RhbCBvZiAqKig1IHBvaW50cykqKg0KDQoNCiMjIENhc2UgU3R1ZHk6IFJlYWwgRXN0YXRlDQpEYXRhIGFuZCBpZGVhcyBmb3IgdGhpcyBjYXNlIHN0dWR5IGNvbWUgZnJvbSAoKk1pbGl0aW5vIGV0IGFsLiwgMjAwNCopLg0KDQoqKlByb2JsZW0gMTYsIHBhZ2UgODk5KiogKCoqbm90IGVudGlyZSBwcm9ibGVtKiosIG9ubHkgc3BlY2lmaWVkIHBhcnRzIGJlbG93KQ0KDQpUaGUgZ29hbCBvZiB0aGlzIGNhc2Ugc3R1ZHkgaXMgdG8gd2FsayB0aGUgdXNlciB0aHJvdWdoIHRoZSBjcmVhdGlvbiBvZiBhIHBhcnNpbW9uaW91cw0KbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdGhhdCBjYW4gYmUgdXNlZCB0byBwcmVkaWN0IHRoZSB0b3RhbCBwcmljZSAodG90YWxwcmljZSkgb2YNCmFwYXJ0bWVudHMgYnkgdGhlaXIgaGVkb25pYyAoc3RydWN0dXJhbCkgY2hhcmFjdGVyaXN0aWNzLiBUaGUgZGF0YSBmcmFtZSBgVklUMjAwNWAgY29udGFpbnMNCnNldmVyYWwgdmFyaWFibGVzLCBhbmQgZnVydGhlciBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YSBjYW4gYmUgZm91bmQgaW4gdGhlIGhlbHAgZmlsZSAobGlzdGVkIGJlbG93KS4NCg0KQSBkYXRhIGZyYW1lIHdpdGggMjE4IG9ic2VydmF0aW9ucyBvbiB0aGUgZm9sbG93aW5nIDE1IHZhcmlhYmxlczoNCg0KKiBgdG90YWxwcmljZWAgKHRoZSBtYXJrZXQgdG90YWwgcHJpY2UgKGluIEV1cm9zKSBvZiB0aGUgYXBhcnRtZW50IGluY2x1ZGluZyBnYXJhZ2UocykgYW5kIHN0b3JhZ2Ugcm9vbShzKSkNCg0KKiBgYXJlYWAgKHRoZSB0b3RhbCBsaXZpbmcgYXJlYSBvZiB0aGUgYXBhcnRtZW50IGluIHNxdWFyZSBtZXRlcnMpDQoNCiogYHpvbmVgIChhIGZhY3RvciBpbmRpY2F0aW5nIHRoZSBuZWlnaGJvcmhvb2Qgd2hlcmUgdGhlIGFwYXJ0bWVudCBpcyBsb2NhdGVkIHdpdGggbGV2ZWxzIGBaMTEsIFoyMSwgWjMxLCBaMzIsIFozNCwgWjM1LCBaMzYsIFozNywgWjM4LCBaNDEsIFo0MiwgWjQzLCBaNDQsIFo0NSwgWjQ2LCBaNDcsIFo0OCwgWjQ5LCBaNTIsIFo1MywgWjU2LCBaNjEsIGFuZCBaNjJgKQ0KDQoqIGBjYXRlZ29yeWAgKGEgZmFjdG9yIGluZGljYXRpbmcgdGhlIGNvbmRpdGlvbiBvZiB0aGUgYXBhcnRtZW50IHdpdGggbGV2ZWxzIGAyQSwgMkIsIDNBLCAzQiwgNEEsIDRCYCwgYW5kIGA1QWAgb3JkZXJlZCBzbyB0aGF0IGAyQWAgaXMgdGhlIGJlc3QgYW5kIGA1QWAgaXMgdGhlIHdvcnN0KQ0KDQoqIGBhZ2VgIChhZ2Ugb2YgdGhlIGFwYXJ0bWVudCBpbiB5ZWFycykNCg0KKiBgZmxvb3JgIChmbG9vciBvbiB3aGljaCB0aGUgYXBhcnRtZW50IGlzIGxvY2F0ZWQpDQoNCiogYHJvb21zYCAodG90YWwgbnVtYmVyIG9mIHJvb21zIGluY2x1ZGluZyBiZWRyb29tcywgZGluaW5nIHJvb20sIGFuZCBraXRjaGVuKQ0KDQoqIGBvdXRgIChhIGZhY3RvciBpbmRpY2F0aW5nIHRoZSBwZXJjZW50IG9mIHRoZSBhcGFydG1lbnQgZXhwb3NlZCB0byB0aGUgZWxlbWVudHM6IFRoZSBsZXZlbHMgYEUxMDAsIEU3NSwgRTUwLCBhbmQgRTI1YCwgY29ycmVzcG9uZCB0byBjb21wbGV0ZSBleHBvc3VyZSwgYDc1YCUgZXhwb3N1cmUsIGA1MGAlIGV4cG9zdXJlLCBhbmQgYDI1YCUgZXhwb3N1cmUsIHJlc3BlY3RpdmVseS4pDQoNCiogYGNvbnNlcnZhdGlvbmAgKGlzIGFuIG9yZGVyZWQgZmFjdG9yIGluZGljYXRpbmcgdGhlIHN0YXRlIG9mIGNvbnNlcnZhdGlvbiBvZiB0aGUgYXBhcnRtZW50LiBUaGUgbGV2ZWxzIGAxQSwgMkEsIDJCLCBhbmQgM0FgIGFyZSBvcmRlcmVkIGZyb20gYmVzdCB0byB3b3JzdCBjb25zZXJ2YXRpb24uKQ0KDQoqIGB0b2lsZXRzYCAodGhlIG51bWJlciBvZiBiYXRocm9vbXMpDQoNCiogYGdhcmFnZWAgKHRoZSBudW1iZXIgb2YgZ2FyYWdlcykNCg0KKiBgZWxldmF0b3JgIChpbmRpY2F0ZXMgdGhlIGFic2VuY2UgKDApIG9yIHByZXNlbmNlICgxKSBvZiBlbGV2YXRvcnMuKQ0KDQoqIGBzdHJlZXRjYXRlZ29yeWAgKGFuIG9yZGVyZWQgZmFjdG9yIGZyb20gYmVzdCB0byB3b3JzdCBpbmRpY2F0aW5nIHRoZSBjYXRlZ29yeSBvZiB0aGUgc3RyZWV0IHdpdGggbGV2ZWxzIGBTMiwgUzMsIFM0LCBhbmQgUzVgKQ0KDQoqIGBoZWF0aW5nYCAoYSBmYWN0b3IgaW5kaWNhdGluZyB0aGUgdHlwZSBvZiBoZWF0aW5nIHdpdGggbGV2ZWxzIGAxQSwgM0EsIDNCLCBhbmQgNEFgIHdoaWNoIGNvcnJlc3BvbmQgdG86IG5vIGhlYXRpbmcsIGxvdy1zdGFuZGFyZCBwcml2YXRlIGhlYXRpbmcsIGhpZ2gtc3RhbmRhcmQgcHJpdmF0ZSBoZWF0aW5nLCBhbmQgY2VudHJhbCBoZWF0aW5nLCByZXNwZWN0aXZlbHkuKQ0KDQoqIGBzdG9yYWdlYCAodGhlIG51bWJlciBvZiBzdG9yYWdlIHJvb21zIG91dHNpZGUgb2YgdGhlIGFwYXJ0bWVudCkNCg0KDQojIyMgQ29tcGxldGUgdGhlIHBhcnRzIGJlbG93DQoNCioqKDEwIHB0cykqKiAqKlF1aXotUHJvamVjdCAzIFByLjEqKg0KDQooYSkgQ2hhcmFjdGVyaXplIHRoZSBzaGFwZSwgY2VudGVyLCBhbmQgc3ByZWFkIG9mIHRoZSB2YXJpYWJsZSBgdG90YWxwcmljZWAuDQoNCkhpbnQ6IFVzZSBgZ2dwbG90YCBmdW5jdGlvbiBmcm9tIGBnZ3Bsb3QyYCBwYWNrYWdlIHRvIGdyYXBoIHRoZSBgdG90YWxwcmljZWAgZGVuc2l0eSBmdW5jdGlvbi4gVXNlIGBtZWRpYW5gIGFuZCBgSVFSYCB0byBmaW5kIHRoZSBtZWRpYW4gYW5kIElRUiBmb3IgdGhlIGB0b3RhbHByaWNlYC4NCg0KKipTb2x1dGlvbjoqKiANCkhpbnQ6IFVzZSB0aGUgdGVtcGxhdGUgZm9yIHRoZSBncmFwaDoNCg0KYGdncGxvdChkYXRhID0gWU9VUiBEQVRBLCBhZXMoeCA9IHZhcmlhYmxlIHRvIHBsb3QpKSArDQogZ2VvbV9kZW5zaXR5KGZpbGwgPSAieW91ciBmYXZvcml0ZSBjb2xvciIpICsNCiB0aGVtZV9idygpYA0KDQpZT1VSIENPREUgSEVSRToNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHRvdGFscHJpY2UsIGFlcyh4ID0gSVFSKSkgKw0KIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiKSArDQogdGhlbWVfYncoKQ0KDQpNRCA8LSBtZWRpYW4oVklUMjAwNSR0b3RhbHByaWNlKQ0KIA0KaXFyIDwtIElRUihWSVQyMDA1JHRvdGFscHJpY2UpDQoNCmMoTUQsIGlxcikNCmBgYA0KDQoqKk9ic2VydmF0aW9uOioqIFRoZSBkaXN0cmlidXRpb24gb2YgYHRvdGFscHJpY2VgIGlzIC4uLiB3aXRoIGEgbWVkaWFuIG9mIGAuLi5gIGFuZCBhbiBgSVFSYCBvZiBgLi4uYA0KDQoNCg0KDQoNCioqKDEwIHB0cykqKiAqKlF1aXotUHJvamVjdCAzIFByLjIqKg0KDQooYikgVXNlIGBzY2F0dGVycGxvdE1hdHJpeCgpYCBmcm9tIGBjYXJgIHBhY2thZ2Ugb3IgYHBhaXJzKClgIHRvIGV4cGxvcmUgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0b3RhbHByaWNlIGFuZCB0aGUgbnVtZXJpY2FsIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBgYXJlYSwgYWdlLCBmbG9vciwgcm9vbXMsIHRvaWxldHMsIGdhcmFnZSwgZWxldmF0b3JgLCBhbmQgYHN0b3JhZ2VgLg0KDQpIaW50OiBUbyB1c2UgYHNjYXR0ZXJwbG90TWF0cml4YCB0eXBlIA0KDQpgc2NhdHRlcnBsb3RNYXRyaXgoIH4gdG90YWxwcmljZSArIHZhcjEgKyB2YXIyICsgLi4uICsgdmFyIG4sIGRhdGEgPSBWSVQyMDA1KWAsIGRvIG5vdCB1c2UgbW9yZSB0aGFuIDUgdmFyaWFibGVzIHRvIHByb2R1Y2UgaW5wdXQgdGhhdCBmaXRzIHRoZSBzY3JlZW4gYW5kIGNhbiBiZSByZXZpZXdlZC4gVXNlIHRoZSBjb21tYW5kIGFzIG1hbnkgdGltZXMgYXMgeW91IG5lZWQgdG8gcmV2aWV3IGhvdyBgdG90YWxwcmljZSBgIGNvcnJlbGF0ZXMgd2l0aCBvdGhlciB2YXJpYWJsZXMgaW4gdGhlIGRhdGEuDQoNCioqU29sdXRpb246KiogDQoNCllPVVIgQ09ERSBIRVJFOg0KDQoNCmBgYHtyfQ0KIyBlLmcuIG1hdHJpeCBjYW4gYmUgcHJvZHVjZWQgd2l0aA0KIyBzY2F0dGVycGxvdE1hdHJpeCggfiB0b3RhbHByaWNlICsgYXJlYSArIGFnZSArIGZsb29yICsgcm9vbXMsIGRhdGEgPSBWSVQyMDA1KQ0KDQoNCg0KIyBvciB1c2UgDQpwYWlycyh+IHRvdGFscHJpY2UgKyBhcmVhICsgYWdlICsgZmxvb3IgKyByb29tcywgZGF0YSA9IFZJVDIwMDUpDQoNCnBhaXJzKH4gdG90YWxwcmljZSArIHRvaWxldHMgKyBnYXJhZ2UgKyBlbGV2YXRvciArIHN0b3JhZ2UsIGRhdGEgPSBWSVQyMDA1KQ0KYGBgDQoNCk9ic2VydmF0aW9uOiBUaGUgdmFyaWFibGUgYHRvdGFscHJpY2VgIGFwcGVhcnMgdG8gaGF2ZSBhIG1vZGVyYXRlIGxpbmVhciByZWxhdGlvbnNoaXAgd2l0aCBgYXJlYWAuDQoNCg0KKGMpICoqVG90YWwgb2YgKDU1IHB0cykqKiAqKlF1aXotUHJvamVjdCAzKiogUHIuMyB0byA4LCAxMCBwdHMgZm9yIGVhY2ggY29ycmVjdGx5IHJlbW92ZWQgdmFyaWFibGUsIDUgcHRzIHRvIGZpbmQgdGhlIGNvcnJlbGF0aW9uLiANCg0KQ29tcHV0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBgdG90YWxwcmljZWAgYW5kIGFsbCBvZiB0aGUgb3RoZXIgbnVtZXJpY2FsIHZhcmlhYmxlcy4gTGlzdCB0aGUgKip0aHJlZSoqIHZhcmlhYmxlcyBpbiBvcmRlciBhbG9uZyB3aXRoIHRoZWlyIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyB0aGF0IGhhdmUgdGhlIGhpZ2hlc3QgY29ycmVsYXRpb24gd2l0aCB0b3RhbHByaWNlLg0KDQojIyMjICoqTW9kZWwgKEEpKio6IFVzZSBiYWNrd2FyZCBlbGltaW5hdGlvbiB0byBkZXZlbG9wIGEgbW9kZWwgdGhhdCBwcmVkaWN0cyB0b3RhbHByaWNlIHVzaW5nIHRoZSBkYXRhIGZyYW1lIGBWSVQyMDA1YC4gVXNlIGEg4oCcUC12YWx1ZS10byByZW1vdmXigJ0gb2YgYDVgJS4gU3RvcmUgdGhlIGZpbmFsIG1vZGVsIGluIHRoZSBvYmplY3QgYG1vZGVsQWAuDQoNCg0KKiooNSBwdHMpKiogKipRdWl6LVByb2plY3QgMyBQci4zKiogDQoNClRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgYXJlOg0KDQpgYGB7cn0NCk5VTSA8LSBjKCJhcmVhIiwgInRvaWxldHMiLCAicm9vbXMiKQ0KQ09SIDwtIGNvcihWSVQyMDA1WywgInRvdGFscHJpY2UiXSwgVklUMjAwNVssIE5VTV0pDQpDT1INCmBgYA0KDQoqKk9ic2VydmF0aW9uOioqIFRoZSBoaWdoZXN0IHRocmVlIGNvcnJlbGF0aW9ucyB3aXRoIGB0b3RhbHByaWNlYCBvY2N1ciB3aXRoIGBhcmVhYCAoMC44MDkyKSwgYHRvaWxldHNgICgwLjY4NzYpLCBhbmQgYHJvb21zYCgwLjUyNTYpLg0KDQoqKk1vZGVsIChBKSoqIFRoZSBmdW5jdGlvbnMgYGRyb3AxKClgIGFuZCBgdXBkYXRlKClgIGFyZSB1c2VkIHRvIGNyZWF0ZSBhIG1vZGVsIHVzaW5nIGJhY2t3YXJkDQplbGltaW5hdGlvbi4NCg0KDQoqKkhpbnQ6KiogYGRyb3AxKG1vZGVsXy5iZV9uYW1lLCB0ZXN0ID0gIkYiKWAgdGVzdCBmb3Igc2lnbmlmaWNhbmNlIG9mIGFsbCBpbmRpdmlkdWFsIHByZWRpY3RvcnMgZ2l2ZW4gYWxsIG90aGVycyBhcmUgYWxyZWFkeSBpbiB0aGUgbW9kZWwuDQoNCmBgYHtyfQ0KIyB1c2UgYSBtb2RlbCB3aGVyZSB0b3RhbHByaWNlIGlzIHJlZ3Jlc3NlZCBvbiBhbGwgb3RoZXIgdmFyaWFibGVzDQptb2RlbC5iZSA8LSBsbSh0b3RhbHByaWNlIH4gLiwgZGF0YSA9IFZJVDIwMDUpDQoNCiMgDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCmBgYA0KDQoqKigxMCBwdHMpKiogKHBhcnQgYy4xKSAqKlF1aXotUHJvamVjdCAzIFByLjQqKiANCg0KV2hpY2ggb25lIGFwcGVhcnMgbW9zdCAqKmluc2lnbmlmaWNhbnQqKiAoYmlnZ2VzdCBgUC12YWx1ZWApPyAgRHJvcCBpdCBmcm9tIHRoZSBtb2RlbC4gDQoNCkUuZy4gSWYgYGFnZWAgaXMgbW9zdCBpbnNpZ25pZmljYW50LCB1c2UgdGhlIGB1cGRhdGVgIGZ1bmN0aW9uIHRvIHVwZGF0ZSB0aGUgbW9kZWwgd2l0aCB0aGUgZm9sbG93aW5nIGNvZGU6DQoNCmBtb2RlbC5iZSA8LSB1cGRhdGUobW9kZWwuYmUsIC5+LiAtIGFnZSlgIC0gdGhlIGZpcnN0IGRvdCBtZWFucyB3ZSB1c2UgdGhlIHNhbWUgcmVzcG9uc2UgdmFyaWFibGUsIHRoZSBzZWNvbmQgYWxsIHByZXZpb3VzbHkgaW5jbHVkZWQgcHJlZGljdG9ycyBtaW51cyBgYWdlYC4gVGhlIGFnYWluIHVzZSBgZHJvcDFgIGFuZCBzbyBvbiwgdW50aWwgYWxsIHJlbWFpbmluZyB2YXJpYWJsZXMgYXJlIG9mIHNpZ25pZmljYW5jZSBgMC4wNWAgYXMgc3BlY2lmaWVkIGluIHRoZSBkaXJlY3Rpb25zIG9mIHRoZSBwcm9ibGVtIA0KDQpZT1VSIENPREUgSEVSRToNCmBgYHtyfQ0KbW9kZWwuYmUgPC0gdXBkYXRlKG1vZGVsLmJlLCAufi4gLSBhZ2UpDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCnVwZGF0ZShtb2RlbC5iZSwgZmxvb3IgPSBGQUxTRSkNCmBgYA0KDQoqKigxMCBwdHMpKiogKHBhcnQgYy4yKSAqKlF1aXotUHJvamVjdCAzIFByLjUqKiANCg0KV2hpY2ggb25lIGlzIHRvIGJlIGRyb3BwZWQgbmV4dD8NCg0KQW5zd2VyOiBjb25zZXJ2YXRpb24NCg0KYGBge3J9DQptb2RlbC5iZSA8LSB1cGRhdGUobW9kZWwuYmUsIC5+LiAtIGFnZSkNCmRyb3AxKG1vZGVsLmJlLCB0ZXN0ID0gIkYiKQ0KdXBkYXRlKG1vZGVsLmJlLCBjb25zZXJ2YXRpb24gPSBGQUxTRSkNCmBgYA0KDQoqKigxMCBwdHMpKiogKHBhcnQgYy4zKSAqKlF1aXotUHJvamVjdCAzIFByLjYqKg0KV2hpY2ggb25lIGlzIHRvIGJlIGRyb3BwZWQgbmV4dD8NCg0KQW5zd2VyOiByb29tcw0KDQpgYGB7cn0NCm1vZGVsLmJlIDwtIHVwZGF0ZShtb2RlbC5iZSwgLn4uIC0gYWdlKQ0KZHJvcDEobW9kZWwuYmUsIHRlc3QgPSAiRiIpDQp1cGRhdGUobW9kZWwuYmUsIHJvb21zID0gRkFMU0UpDQoNCmBgYA0KDQoqKigxMCBwdHMpKiogKHBhcnQgYy40KSAqKlF1aXotUHJvamVjdCAzIFByLjcqKg0KV2hpY2ggb25lIGlzIHRvIGJlIGRyb3BwZWQgbmV4dD8NCg0KQW5zd2VyOnN0cmVldGNhdGVnb3J5IA0KDQpgYGB7cn0NCm1vZGVsLmJlIDwtIHVwZGF0ZShtb2RlbC5iZSwgLn4uIC0gYWdlKQ0KZHJvcDEobW9kZWwuYmUsIHRlc3QgPSAiRiIpDQp1cGRhdGUobW9kZWwuYmUsIHN0cmVldGNhdGVnb3J5ID0gRkFMU0UpDQoNCmBgYA0KDQoqKigxMCBwdHMpKiogKHBhcnQgYy41KSAqKlF1aXotUHJvamVjdCAzIFByLjgqKg0KV2hpY2ggb25lIGlzIHRvIGJlIGRyb3BwZWQgbmV4dD8NCg0KQW5zd2VyOiBzdG9yYWdlDQoNCmBgYHtyfQ0KbW9kZWwuYmUgPC0gdXBkYXRlKG1vZGVsLmJlLCAufi4gLSBhZ2UpDQpkcm9wMShtb2RlbC5iZSwgdGVzdCA9ICJGIikNCnVwZGF0ZShtb2RlbC5iZSwgc3RvcmFnZSA9IEZBTFNFKQ0KYGBgDQoNCg0KSWYgYWxsIHZhcmlhYmxlIGFyZSBzaWduaWZpY2FudCBjcmVhdGUgdGhlIG9iamVjdCBob2xkaW5nIHRoZSBvcHRpbWFsIG1vZGVsIGFzIHBlciB0aGUgZGVzY3JpcHRpb24gb2YgdGhlIHByb2JsZW0gdXNpbmcgdGhlIGNvZGU6DQoNCmBgYHtyfQ0KZm9ybXVsYShtb2RlbC5iZSkNCg0KbW9kZWxBIDwtIGxtKGZvcm11bGEobW9kZWwuYmUpLCBkYXRhID0gVklUMjAwNSkNCg0KYGBgDQoNCioqUXVlc3Rpb24gMTogKDUgcHRzKSoqICoqUXVpei1Qcm9qZWN0IDMgUHIuOSoqIFdoaWNoIHZhcmlhYmxlIGFyZSBsZWZ0IGluIHRoZSBtb2RlbCwgbGlzdCB0aGVtPw0KDQoiYXJlYSIsICJ6b25lIiwgImNhdGVnb3J5IiwgIm91dCIsICJ0b2lsZXRzIiwgImdhcmFnZSIsICJlbGV2YXRvciIsICJzdHJlZXRjb3JuZXIiLCBhbmQgImhlYXRpbmciLg0KDQpPYnNlcnZhdGlvbjogQmFja3dhcmQgZWxpbWluYXRpb24gc3VnZ2VzdHMgdXNpbmcgdGhlIHZhcmlhYmxlcyBgYXJlYWAsIGB6b25lYCwgYGNhdGVnb3J5YCwgYG91dGAsIGB0b2lsZXRzYCwgYGdhcmFnZWAsIGBlbGV2YXRvcmAsIGBzdHJlZXRjb3JuZXJgLCBhbmQgYGhlYXRpbmdgIHRvIGJlc3QgcHJlZGljdCBgdG90YWxwcmljZWAuDQoNCg0KDQooaSkgKiooNSBwdHMpKiogKipRdWl6LVByb2plY3QgMyBQci4xMCoqDQoNCkNvbXB1dGUgJENWX24kLCB0aGUgbGVhdmUtb25lLW91dCBjcm9zcyB2YWxpZGF0aW9uIGVycm9yLCBmb3IgYG1vZGVsQWAuIA0KU2V0IHRoZSBzZWVkIHRvIGA1YCBhbmQgY29tcHV0ZSAkQ1ZfNSQsIHRoZSAqKmZpdmUtZm9sZCoqIGNyb3NzIHZhbGlkYXRpb24gZXJyb3IsIGZvciBgbW9kZWxBYC4gDQpUaGUgY3Jvc3MgdmFsaWRhdGlvbiBlcnJvciBmb3IgYSBnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWwgY2FuIGJlIGNvbXB1dGVkIHVzaW5nIHRoZQ0KYGN2LmdsbSgpYCBmcm9tIHRoZSBgYm9vdGAgcGFja2FnZS4gDQpVc2luZyB0aGUgZnVuY3Rpb24gYGdsbSgpYCB3aXRob3V0IHBhc3NpbmcgYSBmYW1pbHkgYXJndW1lbnQgaXMgdGhlIHNhbWUgYXMgdXNpbmcgdGhlIGZ1bmN0aW9uIGBsbSgpYC4gYFIgQ29kZSAxMi4zYCBwcm92aWRlcyBhIHRlbXBsYXRlIG9mIGhvdyB0byB1c2UgdGhlIGBjdi5nbG0oKWAgZnVuY3Rpb24uIE5vdGUgdGhhdCAkQ1ZfbiQgaXMgcmV0dXJuZWQgd2l0aA0KYGN2LmVycm9yJGRlbHRhWzFdYC4gVG8gY29tcHV0ZSAkQ1ZfNSQsIHBhc3MgdGhlIHZhbHVlIGA1YCB0byB0aGUgYXJndW1lbnQgYEtgIGluc2lkZSB0aGUgYGN2LmdsbSgpYCBmdW5jdGlvbi4NCg0KDQpgYGB7ciBSIENvZGUgMTIuM30NCiMgIyB0aGUgY29kZSB3aWxsIG5vdCB3b3JrLCBpdCBpcyBvbmx5IHRlbXBsYXRlDQojIG1vZC5nbG0gPC0gZ2xtKHkgfiB4MSArIHgyLCBkYXRhID0gREYpDQojIGxpYnJhcnkoYm9vdCkNCiMgDQojIGN2LmVycm9yIDwtIGN2LmdsbShkYXRhID0gREYsIGdsbWZpdCA9IG1vZC5nbG0pDQojIGN2LmVycm9yJGRlbHRhWzFdDQoNCg0KbW9kLmdsbSA8LSBnbG0oeSB+IHgxICsgeDIsIGRhdGEgPSBtb2RlbEEpDQpsaWJyYXJ5KGJvb3QpDQoNCmN2LmVycm9yIDwtIGN2LmdsbShkYXRhID0gbW9kZWxBLCBnbG1maXQgPSBtb2QuZ2xtLCA1KQ0KY3YuZXJyb3IkZGVsdGFbMV0NCg0KYGBgDQoNCihpaSkgKiooNSBwdHMpKiogKipRdWl6LVByb2plY3QgMyBQci4xMSoqDQoNCkNvbXB1dGUgJFJeMiQsICRSXjJfYSQsIHRoZSBgQUlDYCwgYW5kIHRoZSBgQklDYCBmb3IgKipNb2RlbCAoQSkqKi4gV2hhdCBpcyB0aGUgcHJvcG9ydGlvbiBvZiB0b3RhbCB2YXJpYWJpbGl0eSBleHBsYWluZWQgYnkgKipNb2RlbCAoQSkqKj8gDQoNCg0KKipZb3VyIFNvbHV0aW9uOioqDQoNCg0KVGhlIHByb3BvcnRpb24gb2YgdG90YWwgdmFyaWFiaWxpdHkgaXMgMC45MTM4Lg0KDQoNCihpKQ0KDQpgYGB7cn0NCm1vZGVsQWcgPC0gZ2xtKGZvcm11bGEobW9kZWwuYmUpLCBkYXRhID0gVklUMjAwNSkNCg0KIyAjIEZybyBjdi5nbG0gLSB0aGUgZGVmYXVsdCBpcyB0byBzZXQgSyBlcXVhbCB0byB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBkYXRhIHdoaWNoIGdpdmVzIHRoZSB1c3VhbCBsZWF2ZS1vbmUtb3V0IGNyb3NzLXZhbGlkYXRpb24uDQojIA0Kc2V0LnNlZWQoNSkgIyB1c2UgZm9yIHJlcGxpY2F0aW9uIHB1cnBvc2VzDQpjdi5lcnJvck4gPC0gY3YuZ2xtKGRhdGEgPSBWSVQyMDA1LCBnbG1maXQgPSAgbW9kZWxBZykNCg0KQ1ZOYSA8LSBjdi5lcnJvck4kZGVsdGFbMV0NCkNWTmENCmBgYA0KIA0KDQpgYGB7cn0NCm1vZGVsQWcgPC0gZ2xtKGZvcm11bGEobW9kZWwuYmUpLCBkYXRhID0gVklUMjAwNSkNCg0KIyBVTkNPTU1FTlQgdGhlIGNvZGUgYmVsb3cgYW5kIHJ1biBpdA0KDQogc2V0LnNlZWQoNSkgIyB1c2UgZm9yIHJlcGxpY2F0aW9uIHB1cnBvc2VzDQogY3YuZXJyb3I1IDwtIGN2LmdsbShkYXRhID0gVklUMjAwNSwgZ2xtZml0ID0gbW9kZWxBZywgSyA9IDUpDQogQ1Y1YSA8LSBjdi5lcnJvcjUkZGVsdGFbMV0NCiBDVjVhDQoNCmBgYA0KDQpPYnNlcnZhdGlvbjogVGhlICRDVl9uID0gLi4uJCBmb3IgTW9kZWwgKEEpLCBhbmQgJENWXzUgPS4uLiQgZm9yIE1vZGVsIChBKS4NCg0KDQooaWkpIFNpbmNlIHRoaXMgcHJvYmxlbSBhbmQgYSBmZXcgbW9yZSB3aWxsIHJlcXVlc3QgbWFueSBnb29kbmVzcyBvZiBmaXQgc3RhdGlzdGljcywgYSBmdW5jdGlvbg0KY2FsbGVkIGBtZ29mKClgIGlzIHdyaXR0ZW4gdG8gY29tcHV0ZSB0aGUgcmVxdWVzdGVkIHZhbHVlcy4gVXNlIGl0IGFzIHNob3duIGJlbG93Lg0KDQpgYGB7cn0NCg0KbWdvZiA8LSBmdW5jdGlvbihtb2RlbCA9IG1vZGVsLCBkYXRhID0gREYsIC4uLil7DQogIFIyYSA8LSBzdW1tYXJ5KG1vZGVsKSRhZGouci5zcXVhcmVkDQogIFIyIDwtIHN1bW1hcnkobW9kZWwpJHIuc3F1YXJlZA0KICBhaWMgPC0gQUlDKG1vZGVsKQ0KICBiaWMgPC0gQUlDKG1vZGVsLCBrID0gbG9nKG5yb3coZGF0YSkpKQ0KICBzZSA8LSBzdW1tYXJ5KG1vZGVsKSRzaWdtYQ0KICBmb3JtIDwtIGZvcm11bGEobW9kZWwpDQogIEFOUyA8LSBjKFIyID0gUjIsIFIyLmFkaiA9IFIyYSwgQUlDID0gYWljLCBCSUMgPSBiaWMsIFNFID0gc2UpDQogIEFOUw0KfQ0KDQpNR09GIDwtIG1nb2YobW9kZWwgPSBtb2RlbEEsIGRhdGEgPSBWSVQyMDA1KQ0KTUdPRg0KYGBgDQoNCk9ic2VydmF0aW9uOiBUaGUgdG90YWwgcHJvcG9ydGlvbiBvZiB2YXJpYWJpbGl0eSBleHBsYWluZWQgYnkgbW9kZWxBIGlzIGAwLjkxMzhgLg0KDQoNCihkKSAqKigxMCBwdHMpKiogKipRdWl6LVByb2plY3QgMyBQci4xMioqIA0KDQpFeHBsb3JlIHRoZSByZXNpZHVhbHMgb2YgdGhlIE1vZGVscyAoQSkgdXNpbmcgdGhlIGZ1bmN0aW9uIGByZXNpZHVhbFBsb3QoKWAgb3IgYHJlc2lkdWFsUGxvdHMoKWAgZnJvbSB0aGUgcGFja2FnZSBgY2FyYC4gQ29tbWVudCBvbiB0aGUgcmVzdWx0cy4gKCoqRGlhZ25vc3RpY3M6IENoZWNraW5nIHRoZSBtb2RlbCBhc3N1bXB0aW9ucyoqKQ0KDQpgYGB7cn0NCiMgIyB1bmNvbW1lbnQgdG8gcnVuIHRoZSByZXNpZHVhbCBwbG90IGZvciB0aGUgbW9kZWxBDQpyZXNpZHVhbFBsb3QobW9kZWxBLCBtYWluID0gIk1vZGVsIEEiKQ0KI1RoaXMgcGxvdCB3b3VsZCBiZSB1c2VkIHRvIGZpbmQgb3V0bGllcnMgYW5kIHVuZXF1YWwgZXJyb3IgdmFyaWFuY2VzIHdoaWNoIGl0IGRvZXMgcXVpdGUgd2VsbCB3aXRoIHBvaW50cyBwbG90dGVkIGFsbCBhcm91bmQgaXQuDQpgYGANCg0KKiooZCkgUXVlc3Rpb24gMjoqKiBJcyB0aGVyZSBjdXJ2YXR1cmUgb2YgdGhlIHJlc2lkdWFscyBvbiB0aGUgYWJvdmUgcGxvdD8NCg0KWWVzLCB0aGVyZSBpcyBhIGxhcmdlIGN1cnZlIGluIHRoaXMgcGxvdC4NCg0KT2JzZXJ2YXRpb246IFRoZSByZXNpZHVhbHMgdmVyc3VzIHRoZSBmaXR0ZWQgdmFsdWVzIGZvciBNb2RlbCAoQSkgaGF2ZSBhIGRlZmluaXRlIGN1cnZhdHVyZSBpbmRpY2F0aW5nIHRoZSBtb2RlbCBpcyBub3QgcXVpdGUgYWRlcXVhdGUuDQoNCg0KKipFeHRyYSBDcmVkaXQqKiAoMTAgcHRzKSAqKlF1aXotUHJvamVjdCAzIFByLjEzKioNCg0KKiooZSkqKiBVc2UgdGhlIGZ1bmN0aW9uIGBib3hDb3goKWAgZnJvbSBjYXIgdG8gZmluZCBhIHN1aXRhYmxlIHRyYW5zZm9ybWF0aW9uIGZvciB0b3RhbHByaWNlLg0KQnVpbGQgTW9kZWwgKEUpIFVzZSBiYWNrd2FyZCBlbGltaW5hdGlvbiB0byBkZXZlbG9wIGEgbW9kZWwgdGhhdCBwcmVkaWN0cyBgbG9nKHRvdGFscHJpY2UpYCB1c2luZyB0aGUgZGF0YSBmcmFtZSBgVklUMjAwNWAuIFVzZSBhIOKAnFAtdmFsdWUtdG8gcmVtb3Zl4oCdIG9mIDUlLiBTdG9yZSB0aGUgZmluYWwgbW9kZWwgaW4gdGhlIG9iamVjdCBgbW9kZWxFYC4NCg0KRm9yIGRldGFpbHMgb24gYGJveENveCgpYCBzZWUgcGFnZSA4NTYgaW4gdGV4dA0KDQpgYGB7cn0NCmJveENveChtb2RlbEEsIGxhbWJkYSA9IHNlcSgtMC41LCAwLjUsIGxlbmd0aCA9IDIwMCkpDQpgYGANCg0KDQoNCiMjIyMgT2JzZXJ2YXRpb246IEEgYGxvZ2AgdHJhbnNmb3JtYXRpb24gaXMgc3VnZ2VzdGVkIGZvciB0aGUgcmVzcG9uc2UgYHRvdGFscHJpY2VgIGluICoqTW9kZWwgKEEpKiouDQoNCg0KKipFeHRyYSBDcmVkaXQqKiAoMjAgcHRzKSAqKlF1aXotUHJvamVjdCAzIFByLjE0KiogLSA0IHB0cyBmb3IgZXhjbHVkaW5nIHRoZSBjb3JyZWN0IHZhcmlhYmxlIGF0IGVhY2ggc3RlcC4NCg0KIyMjIyAqKk1vZGVsIChFKSoqOiBVc2UgYmFja3dhcmQgZWxpbWluYXRpb24gdG8gZGV2ZWxvcCBhIG1vZGVsIHRoYXQgcHJlZGljdHMgYGxvZyh0b3RhbHByaWNlKWAgdXNpbmcgdGhlIGRhdGEgZnJhbWUgYFZJVDIwMDVgLiBVc2UgYSDigJxQLXZhbHVlLXRvIHJlbW92ZeKAnSBvZiBgNWAlLiBTdG9yZSB0aGUgZmluYWwgbW9kZWwgaW4gdGhlIG9iamVjdCBgbW9kZWxFYC4NCg0KKipNb2RlbCAoRSkqKiBUaGUgZnVuY3Rpb25zIGBkcm9wMSgpYCBhbmQgYHVwZGF0ZSgpYCBhcmUgdXNlZCB0byBjcmVhdGUgYSBtb2RlbCB1c2luZyBiYWNrd2FyZA0KZWxpbWluYXRpb24uIChhcyBzaG93biBhdCB0aGUgYmVnaW5uaW5nKQ0KDQpgYGB7cn0NCiMgdHJhbnNmb3JtIHRoZSByZXNwb25zZSB2YXJpYWJsZSB1c2luZyBuYXR1cmFsIGxvZyB0cmFuc2Zvcm1hdGlvbg0KDQpWSVRORVcgPC0gVklUMjAwNSAlPiUgbXV0YXRlKGxvZ3RvdGFscHJpY2UgPSBsb2codG90YWxwcmljZSkpDQoNCiMgYnVpbGQgdGhlIGZpdHRlZCBmdWxsIG1vZGVsDQptb2RlbC5iZSA8LSBsbShsb2d0b3RhbHByaWNlIH4gLiwgZGF0YSA9IFZJVE5FV1sgLC0xXSkgIyBleGNsdWRlIHRoZSB0b3RhbHByaWNlIHZhcmlhYmxlIHNpbmNlIGl0IGlzIG5vIGxvbmdlciByZXNwb25zZSB2YXJpYWJsZQ0KZHJvcDEobW9kZWwuYmUsIHRlc3QgPSAiRiIpDQoNCmBgYA0KDQphbmQgc28gb24uDQoNCg==