Chapter 03 (page 121): 2, 9, 10, 12

Assignment 2

Q2: Carefully explain the differences between the KNN classifier and KNN regression methods.

KNN classification predicts categorical outcomes by looking at the classes of the K nearest neighbors and assigning the most common class. On the other hand, KNN regression predicts numerical outcomes by averaging the values of the K nearest neighbors. For example, KNN classification can determine whether a loan should be approved to applicant, while KNN regression can estimate a house price based on similar houses nearby.

Q9: This question involves the use of multiple linear regression on the Auto data set.

data(Auto)
attach(Auto)

(a) Produce a scatterplot matrix which includes all of the variables in the data set.

(b) Compute the matrix of correlations between the variables using the function cor(). You will need to exclude the name variable, cor() which is qualitative.

cor(Auto[, names(Auto) != "name"])
                    mpg  cylinders displacement horsepower     weight acceleration       year     origin
mpg           1.0000000 -0.7776175   -0.8051269 -0.7784268 -0.8322442    0.4233285  0.5805410  0.5652088
cylinders    -0.7776175  1.0000000    0.9508233  0.8429834  0.8975273   -0.5046834 -0.3456474 -0.5689316
displacement -0.8051269  0.9508233    1.0000000  0.8972570  0.9329944   -0.5438005 -0.3698552 -0.6145351
horsepower   -0.7784268  0.8429834    0.8972570  1.0000000  0.8645377   -0.6891955 -0.4163615 -0.4551715
weight       -0.8322442  0.8975273    0.9329944  0.8645377  1.0000000   -0.4168392 -0.3091199 -0.5850054
acceleration  0.4233285 -0.5046834   -0.5438005 -0.6891955 -0.4168392    1.0000000  0.2903161  0.2127458
year          0.5805410 -0.3456474   -0.3698552 -0.4163615 -0.3091199    0.2903161  1.0000000  0.1815277
origin        0.5652088 -0.5689316   -0.6145351 -0.4551715 -0.5850054    0.2127458  0.1815277  1.0000000

(c) Use the lm() function to perform a multiple linear regression with mpg as the response and all other variables except name as the predictors. Use the summary() function to print the results. Comment on the output.

lm.fit <- lm(mpg ~ .-name, data=Auto)
summary(lm.fit)

Call:
lm(formula = mpg ~ . - name, data = Auto)

Residuals:
    Min      1Q  Median      3Q     Max 
-9.5903 -2.1565 -0.1169  1.8690 13.0604 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -17.218435   4.644294  -3.707  0.00024 ***
cylinders     -0.493376   0.323282  -1.526  0.12780    
displacement   0.019896   0.007515   2.647  0.00844 ** 
horsepower    -0.016951   0.013787  -1.230  0.21963    
weight        -0.006474   0.000652  -9.929  < 2e-16 ***
acceleration   0.080576   0.098845   0.815  0.41548    
year           0.750773   0.050973  14.729  < 2e-16 ***
origin         1.426141   0.278136   5.127 4.67e-07 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.328 on 384 degrees of freedom
Multiple R-squared:  0.8215,    Adjusted R-squared:  0.8182 
F-statistic: 252.4 on 7 and 384 DF,  p-value: < 2.2e-16

For instance:
i. Is there a relationship between the predictors and the response?

Yes. There is a strong relationship between our predictors and the response variable mpg. The F-statistic is 252.4 with a p-value < 2.2e-16, indicating that at least one predictor between our variables is significantly associated with mpg. Additionally, our model explains about 82% of the variation in mpg, suggesting a very good fit.

  1. Which predictors appear to have a statistically significant relationship to the response?

displacement, weight, yearand origin have a statistically significant relationship to our response variable.

  1. What does the coefficient for the year variable suggest?

The coefficient for year is 0.7508, which suggests that, holding all other variables constant, a one-year increase in the year is associated with an average increase of about 0.75 MPG. This indicates that newer cars tend to be more fuel efficient than older cars.

(d) Use the plot() function to produce diagnostic plots of the linear regression fit. Comment on any problems you see with the fit. Do the residual plots suggest any unusually large outliers? Does the leverage plot identify any observations with unusually high leverage?

par(mfrow = c(2,2))
plot(lm.fit)

Residuals vs Fitted plot shows non-linear trend rather than flat straigt red line. this means that our model doesnt fully capture relationships between the variables. Points are also scattered accross the red line shows heteroscedasticity and plot points out few outliers such as observation: 327, 323 and 326.

Q-Q Residuals plot show mostly align dots around the line however there is a tail showing upward trend with same outliers.

Scale-Location plot confirms that the model violates the assumption of homoscedasticity once again.This means that the model’s prediction errors become increasingly volatile and less reliable at higher predicted mpg values. Additionally, the same three extreme outliers (327, 323 and 326) appear high above the rest of the data, showing they have unusually large standardized residuals.

Residuals vs Leverage plot shows that no single data point is forcing our model to give bad results. Point 14 is unusual compared to other data, but it fits the model’s pattern. Points 327 and 394 are far off the model’s predictions, but they do not have enough power to change the overall math.

(e) Use the * and : symbols to fit linear regression models with interaction effects. Do any interactions appear to be statistically significant?

I will consider displacement * acceleration interaction because a car with a massive engine (high displacement) usually accelerates quickly, but it burns a massive amount of fuel to do so. I will also utilize horsepower * weight interaction because thinking about big powerful truck they have massive weight and horsepower yet they are bad on fuel efficiency so I want to see if the same results apply in this dataset.

lm.fit.int <- lm(mpg ~ cylinders + displacement*acceleration + horsepower * weight + year + origin - name, data = Auto)
summary(lm.fit.int)

As it shows in our model output higher horsepower and heavier cars drastically cause lower mpg. Also the fuel cost of accelerating quickly is much higher when you are feeding a bigger engine versus a small engine. Our interactions helped tremendously in our model improvement.

(f) Try a few different transformations of the variables, such as log(X), √X, X2. Comment on your findings.

First I would pick a predictor variable and analyze it is relationship with the response variabe. Next I will try this transformations and see which one is best to capture that relationship between. For this example I will try horsepower against mpg but first lets see in scatter plot what are their relationship look like.

plot(Auto$horsepower, Auto$mpg,
     xlab = "Horsepower",
     ylab = "MPG",
     main = "MPG vs Horsepower",
     pch = 1)     

lm.fit.log <- lm(mpg ~ log(horsepower), data = Auto)
summary(lm.fit.log)

Call:
lm(formula = mpg ~ log(horsepower), data = Auto)

Residuals:
     Min       1Q   Median       3Q      Max 
-14.2299  -2.7818  -0.2322   2.6661  15.4695 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)     108.6997     3.0496   35.64   <2e-16 ***
log(horsepower) -18.5822     0.6629  -28.03   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.501 on 390 degrees of freedom
Multiple R-squared:  0.6683,    Adjusted R-squared:  0.6675 
F-statistic: 785.9 on 1 and 390 DF,  p-value: < 2.2e-16
lm.fit.sqrt <- lm(mpg ~ sqrt(horsepower), data = Auto)
summary(lm.fit.sqrt)

Call:
lm(formula = mpg ~ sqrt(horsepower), data = Auto)

Residuals:
     Min       1Q   Median       3Q      Max 
-13.9768  -3.2239  -0.2252   2.6881  16.1411 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)        58.705      1.349   43.52   <2e-16 ***
sqrt(horsepower)   -3.503      0.132  -26.54   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.665 on 390 degrees of freedom
Multiple R-squared:  0.6437,    Adjusted R-squared:  0.6428 
F-statistic: 704.6 on 1 and 390 DF,  p-value: < 2.2e-16
lm.fit.quad <- lm(mpg ~ I(horsepower^2), data = Auto)
summary(lm.fit.quad)

Call:
lm(formula = mpg ~ I(horsepower^2), data = Auto)

Residuals:
    Min      1Q  Median      3Q     Max 
-12.529  -3.798  -1.049   3.240  18.528 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)      3.047e+01  4.466e-01   68.22   <2e-16 ***
I(horsepower^2) -5.665e-04  2.827e-05  -20.04   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.485 on 390 degrees of freedom
Multiple R-squared:  0.5074,    Adjusted R-squared:  0.5061 
F-statistic: 401.7 on 1 and 390 DF,  p-value: < 2.2e-16
lm.fit.poly <- lm(mpg ~ horsepower + I(horsepower^2), data = Auto)
summary(lm.fit.poly)

Call:
lm(formula = mpg ~ horsepower + I(horsepower^2), data = Auto)

Residuals:
     Min       1Q   Median       3Q      Max 
-14.7135  -2.5943  -0.0859   2.2868  15.8961 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)     56.9000997  1.8004268   31.60   <2e-16 ***
horsepower      -0.4661896  0.0311246  -14.98   <2e-16 ***
I(horsepower^2)  0.0012305  0.0001221   10.08   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.374 on 389 degrees of freedom
Multiple R-squared:  0.6876,    Adjusted R-squared:  0.686 
F-statistic:   428 on 2 and 389 DF,  p-value: < 2.2e-16

Seems like polynomial model has the best R-squared value with 0.686 now lets look at all transformation models and see how they look like in our previous plot.

library(ggplot2)

ggplot(Auto, aes(x = horsepower, y = mpg)) +
    geom_point(color = "gray50", alpha = 0.5, size = 2) +
  
  stat_smooth(method = "lm", formula = y ~ log(x), 
              aes(color = "Log"), se = FALSE, size = 1) +
  
  
  stat_smooth(method = "lm", formula = y ~ sqrt(x), 
              aes(color = "Sqrt"), se = FALSE, size = 1) +
  
  stat_smooth(method = "lm", formula = y ~ x + I(x^2), 
              aes(color = "Quad"), se = FALSE, size = 1) +
  
  stat_smooth(method = "lm", formula = y ~ poly(x, 3), 
              aes(color = "Poly (Cubic)"), se = FALSE, size = 1) +
  
  labs(title = "Comparing Model Transformations: MPG vs Horsepower",
       x = "Horsepower",
       y = "Miles Per Gallon (MPG)",
       color = "Model Type") +
  scale_color_manual(values = c("Log" = "red", 
                                "Sqrt" = "blue", 
                                "Quad" = "green3", 
                                "Poly (Cubic)" = "purple")) +
  theme_minimal()

Q10: This question should be answered using the Carseats data set.

data("Carseats")
attach(Carseats)

(a) Fit a multiple regression model to predict Sales using Price, Urban, and US.

carseat_lm <- lm(Sales ~ Price + Urban + US, data = Carseats)
summary(carseat_lm)

Call:
lm(formula = Sales ~ Price + Urban + US, data = Carseats)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.9206 -1.6220 -0.0564  1.5786  7.0581 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 13.043469   0.651012  20.036  < 2e-16 ***
Price       -0.054459   0.005242 -10.389  < 2e-16 ***
UrbanYes    -0.021916   0.271650  -0.081    0.936    
USYes        1.200573   0.259042   4.635 4.86e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.472 on 396 degrees of freedom
Multiple R-squared:  0.2393,    Adjusted R-squared:  0.2335 
F-statistic: 41.52 on 3 and 396 DF,  p-value: < 2.2e-16

(b) Provide an interpretation of each coefficient in the model. Be careful—some of the variables in the model are qualitative!

Price (-0.054): holding other variables constant for every $1 increase in price, sales decrease by about 0.054 units.
UrbanYes (-0.022) holding other variables constant, stores in urban locations sell about 0.022 fewer units than non-urban stores. This effect is very small.
USYes (1.201) holding other variables constant, stores located in the US sell about 1.20 more units than non-US stores.

(c) Write out the model in equation form, being careful to handle the qualitative variables properly.

\(Sales=\beta_0 + \beta_1 * Price + \beta_2 * UrbanYes + \beta_3 * USYes\)

(d) For which of the predictors can you reject the null hypothesis H0 : βj =0?

Price and USYes have significant p-values, but UrbanYes doesnt have, therefore only UrbanYes fail to reject Null hypothesis.

(e) On the basis of your response to the previous question, fit a smaller model that only uses the predictors for which there is evidence of association with the outcome.

carseat_lm_reduced <- lm(Sales ~ Price + US, data = Carseats)
summary(carseat_lm_reduced)

Call:
lm(formula = Sales ~ Price + US, data = Carseats)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.9269 -1.6286 -0.0574  1.5766  7.0515 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 13.03079    0.63098  20.652  < 2e-16 ***
Price       -0.05448    0.00523 -10.416  < 2e-16 ***
USYes        1.19964    0.25846   4.641 4.71e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.469 on 397 degrees of freedom
Multiple R-squared:  0.2393,    Adjusted R-squared:  0.2354 
F-statistic: 62.43 on 2 and 397 DF,  p-value: < 2.2e-16

(f) How well do the models in (a) and (e) fit the data?

Both models fit the data with same performance. Both model’s Multiple R-squared: 0.2393 but Adjusted R-squared increased in model in (e) providing parsimonious fit with fewer variable.

(g) Using the model from (e), obtain 95% confidence intervals for the coefficient(s).

confint(carseat_lm_reduced)
                  2.5 %      97.5 %
(Intercept) 11.79032020 14.27126531
Price       -0.06475984 -0.04419543
USYes        0.69151957  1.70776632

(h) Is there evidence of outliers or high leverage observations in the model from (e)?

par(mfrow=c(2,2))
plot(carseat_lm_reduced)

plot(hatvalues(carseat_lm_reduced))

hat <- hatvalues(carseat_lm_reduced)

which.max(hat)
43 
43 

Observation 43 has highest hat value.

Q12: This problem involves simple linear regression without an intercept.

(a) Recall that the coefficient estimate ˆβ for the linear regression of Y onto X without an intercept is given by (3.38). Under what circumstance is the coefficient estimate for the regression of X onto Y the same as the coefficient estimate for the regression of Y onto X? 

Regression without an intercept, the coefficient estimates are

\[ \hat{\beta}_{Y|X} = \frac{\sum x_i y_i}{\sum x_i^2} \] >and

\[ \hat{\beta}_{X|Y} = \frac{\sum x_i y_i}{\sum y_i^2} \]

the Coefficient estimates will be equal when

\[ \frac{\sum x_i y_i}{\sum x_i^2} = \frac{\sum x_i y_i}{\sum y_i^2} \]

After canceling common terms our final formula look like below

\[ \sum x_i^2 = \sum y_i^2 \]

The coefficient estimates are the same when X and Y have the same sum of squared values

(b) Generate an example in R with n = 100 observations in which the coefficient estimate for the regression of X onto Y is different from the coefficient estimate for the regression of Y onto X.

set.seed(45)

x <- rnorm(100)
y <- 2*x + rnorm(100)

coef(lm(y ~ x - 1))
       x 
1.942817 
coef(lm(x ~ y - 1))
        y 
0.4254618 

I generated 100 observations with \(y=2x+ϵ\). The estimated coefficient for the regression of Y on X differed from the estimated coefficient for the regression of X on Y, demonstrating that the two regressions generally produce different estimates.

(c) Generate an example in R with n = 100 observations in which the coefficient estimate for the regression of X onto Y is the same as the coefficient estimate for the regression of Y onto X.

set.seed(45)

x <- rnorm(100)
y <- sample(c(-1,1), 100, replace = TRUE) * x

sum(x^2)
[1] 127.9706
sum(y^2)
[1] 127.9706
coef(lm(y ~ x - 1))
        x 
0.2549004 
coef(lm(x ~ y - 1))
        y 
0.2549004 

I generated Y by multiplying each value of X by either 1 or -1. As a result, X and Y had identical sums of squares, and the estimated coefficient for the regression of Y on X was the same as the estimated coefficient for the regression of X on Y

LS0tDQp0aXRsZTogIlNUQS02NDUzIFByZWRpY3RpdmUgTW9kZWxpbmciDQphdXRob3I6ICJFbXJhaCBBa2JhcyINCmRhdGU6ICIyMDI2LTA2LTEyIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KDQpDaGFwdGVyIDAzIChwYWdlIDEyMSk6IDIsIDksIDEwLCAxMg0KDQojIyBBc3NpZ25tZW50IDINCg0KX19RMjpfXyBDYXJlZnVsbHkgZXhwbGFpbiB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgS05OIGNsYXNzaWZpZXIgYW5kIEtOTg0KcmVncmVzc2lvbiBtZXRob2RzLg0KDQo+IEtOTiBjbGFzc2lmaWNhdGlvbiBwcmVkaWN0cyBjYXRlZ29yaWNhbCBvdXRjb21lcyBieSBsb29raW5nIGF0IHRoZSBjbGFzc2VzIG9mIHRoZSBLIG5lYXJlc3QgbmVpZ2hib3JzIGFuZCBhc3NpZ25pbmcgdGhlIG1vc3QgY29tbW9uIGNsYXNzLiBPbiB0aGUgb3RoZXIgaGFuZCwgS05OIHJlZ3Jlc3Npb24gcHJlZGljdHMgbnVtZXJpY2FsIG91dGNvbWVzIGJ5IGF2ZXJhZ2luZyB0aGUgdmFsdWVzIG9mIHRoZSBLIG5lYXJlc3QgbmVpZ2hib3JzLiBGb3IgZXhhbXBsZSwgS05OIGNsYXNzaWZpY2F0aW9uIGNhbiBkZXRlcm1pbmUgd2hldGhlciBhIGxvYW4gc2hvdWxkIGJlIGFwcHJvdmVkIHRvIGFwcGxpY2FudCwgd2hpbGUgS05OIHJlZ3Jlc3Npb24gY2FuIGVzdGltYXRlIGEgaG91c2UgcHJpY2UgYmFzZWQgb24gc2ltaWxhciBob3VzZXMgbmVhcmJ5Lg0KDQpfX1E5Ol9fIFRoaXMgcXVlc3Rpb24gaW52b2x2ZXMgdGhlIHVzZSBvZiBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBvbiB0aGUNCkF1dG8gZGF0YSBzZXQuXA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmRhdGEoQXV0bykNCmF0dGFjaChBdXRvKQ0KYGBgDQoNCl9fKGEpX18gUHJvZHVjZSBhIHNjYXR0ZXJwbG90IG1hdHJpeCB3aGljaCBpbmNsdWRlcyBhbGwgb2YgdGhlIHZhcmlhYmxlcw0KaW4gdGhlIGRhdGEgc2V0LlwNCg0KYGBge3J9DQpwYWlycyhBdXRvKQ0KYGBgDQoNCg0KX18oYilfXyBDb21wdXRlIHRoZSBtYXRyaXggb2YgY29ycmVsYXRpb25zIGJldHdlZW4gdGhlIHZhcmlhYmxlcyB1c2luZw0KdGhlIGZ1bmN0aW9uIGNvcigpLiBZb3Ugd2lsbCBuZWVkIHRvIGV4Y2x1ZGUgdGhlIG5hbWUgdmFyaWFibGUsIGNvcigpDQp3aGljaCBpcyBxdWFsaXRhdGl2ZS5cDQoNCmBgYHtyfQ0KY29yKEF1dG9bLCBuYW1lcyhBdXRvKSAhPSAibmFtZSJdKQ0KYGBgDQoNCg0KX18oYylfXyBVc2UgdGhlIGxtKCkgZnVuY3Rpb24gdG8gcGVyZm9ybSBhIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uDQp3aXRoIG1wZyBhcyB0aGUgcmVzcG9uc2UgYW5kIGFsbCBvdGhlciB2YXJpYWJsZXMgZXhjZXB0IG5hbWUgYXMNCnRoZSBwcmVkaWN0b3JzLiBVc2UgdGhlIHN1bW1hcnkoKSBmdW5jdGlvbiB0byBwcmludCB0aGUgcmVzdWx0cy4NCkNvbW1lbnQgb24gdGhlIG91dHB1dC4gXA0KDQpgYGB7cn0NCmxtLmZpdCA8LSBsbShtcGcgfiAuLW5hbWUsIGRhdGE9QXV0bykNCnN1bW1hcnkobG0uZml0KQ0KYGBgDQoNCg0KRm9yIGluc3RhbmNlOlwNCmkuIElzIHRoZXJlIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHByZWRpY3RvcnMgYW5kIHRoZSByZXNwb25zZT9cDQoNCj4gWWVzLiBUaGVyZSBpcyBhIHN0cm9uZyByZWxhdGlvbnNoaXAgYmV0d2VlbiBvdXIgcHJlZGljdG9ycyBhbmQgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGBtcGdgLiBUaGUgRi1zdGF0aXN0aWMgaXMgKioyNTIuNCoqIHdpdGggYSBwLXZhbHVlICoqPCAyLjJlLTE2KiosIGluZGljYXRpbmcgdGhhdCBhdCBsZWFzdCBvbmUgcHJlZGljdG9yIGJldHdlZW4gb3VyIHZhcmlhYmxlcyBpcyBzaWduaWZpY2FudGx5IGFzc29jaWF0ZWQgd2l0aCBgbXBnYC4gQWRkaXRpb25hbGx5LCBvdXIgbW9kZWwgZXhwbGFpbnMgYWJvdXQgODIlIG9mIHRoZSB2YXJpYXRpb24gaW4gYG1wZ2AsIHN1Z2dlc3RpbmcgYSB2ZXJ5IGdvb2QgZml0Lg0KDQoNCmlpLiBXaGljaCBwcmVkaWN0b3JzIGFwcGVhciB0byBoYXZlIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudA0KcmVsYXRpb25zaGlwIHRvIHRoZSByZXNwb25zZT9cDQoNCj4gYGRpc3BsYWNlbWVudGAsIGB3ZWlnaHRgLCBgeWVhcmBhbmQgYG9yaWdpbmAgaGF2ZSBhIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgcmVsYXRpb25zaGlwIHRvIG91ciByZXNwb25zZSB2YXJpYWJsZS4gDQoNCmlpaS4gV2hhdCBkb2VzIHRoZSBjb2VmZmljaWVudCBmb3IgdGhlIHllYXIgdmFyaWFibGUgc3VnZ2VzdD9cDQoNCj4gVGhlIGNvZWZmaWNpZW50IGZvciB5ZWFyIGlzICoqMC43NTA4KiosIHdoaWNoIHN1Z2dlc3RzIHRoYXQsIGhvbGRpbmcgYWxsIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudCwgYSBvbmUteWVhciBpbmNyZWFzZSBpbiB0aGUgYHllYXJgIGlzIGFzc29jaWF0ZWQgd2l0aCBhbiBhdmVyYWdlIGluY3JlYXNlIG9mIGFib3V0IDAuNzUgTVBHLiBUaGlzIGluZGljYXRlcyB0aGF0IG5ld2VyIGNhcnMgdGVuZCB0byBiZSBtb3JlIGZ1ZWwgZWZmaWNpZW50IHRoYW4gb2xkZXIgY2Fycy4NCg0KX18oZClfXyBVc2UgdGhlIHBsb3QoKSBmdW5jdGlvbiB0byBwcm9kdWNlIGRpYWdub3N0aWMgcGxvdHMgb2YgdGhlIGxpbmVhcg0KcmVncmVzc2lvbiBmaXQuIENvbW1lbnQgb24gYW55IHByb2JsZW1zIHlvdSBzZWUgd2l0aCB0aGUgZml0Lg0KRG8gdGhlIHJlc2lkdWFsIHBsb3RzIHN1Z2dlc3QgYW55IHVudXN1YWxseSBsYXJnZSBvdXRsaWVycz8gRG9lcw0KdGhlIGxldmVyYWdlIHBsb3QgaWRlbnRpZnkgYW55IG9ic2VydmF0aW9ucyB3aXRoIHVudXN1YWxseSBoaWdoDQpsZXZlcmFnZT9cDQoNCmBgYHtyfQ0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChsbS5maXQpDQpgYGANCg0KPiAqKlJlc2lkdWFscyB2cyBGaXR0ZWQqKiBwbG90IHNob3dzIG5vbi1saW5lYXIgdHJlbmQgcmF0aGVyIHRoYW4gZmxhdCBzdHJhaWd0IHJlZCBsaW5lLiB0aGlzIG1lYW5zIHRoYXQgb3VyIG1vZGVsIGRvZXNudCBmdWxseSBjYXB0dXJlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgdmFyaWFibGVzLiBQb2ludHMgYXJlIGFsc28gc2NhdHRlcmVkIGFjY3Jvc3MgdGhlIHJlZCBsaW5lIHNob3dzIGhldGVyb3NjZWRhc3RpY2l0eSBhbmQgcGxvdCBwb2ludHMgb3V0IGZldyBvdXRsaWVycyBzdWNoIGFzIG9ic2VydmF0aW9uOiAzMjcsIDMyMyBhbmQgMzI2Lg0KDQo+ICoqUS1RIFJlc2lkdWFscyoqIHBsb3Qgc2hvdyBtb3N0bHkgYWxpZ24gZG90cyBhcm91bmQgdGhlIGxpbmUgaG93ZXZlciB0aGVyZSBpcyBhIHRhaWwgc2hvd2luZyB1cHdhcmQgdHJlbmQgd2l0aCBzYW1lIG91dGxpZXJzLg0KDQo+ICoqU2NhbGUtTG9jYXRpb24qKiBwbG90IGNvbmZpcm1zIHRoYXQgdGhlIG1vZGVsIHZpb2xhdGVzIHRoZSBhc3N1bXB0aW9uIG9mIGhvbW9zY2VkYXN0aWNpdHkgb25jZSBhZ2Fpbi5UaGlzIG1lYW5zIHRoYXQgdGhlIG1vZGVsJ3MgcHJlZGljdGlvbiBlcnJvcnMgYmVjb21lIGluY3JlYXNpbmdseSB2b2xhdGlsZSBhbmQgbGVzcyByZWxpYWJsZSBhdCBoaWdoZXIgcHJlZGljdGVkIG1wZyB2YWx1ZXMuIEFkZGl0aW9uYWxseSwgdGhlIHNhbWUgdGhyZWUgZXh0cmVtZSBvdXRsaWVycyAoMzI3LCAzMjMgYW5kIDMyNikgYXBwZWFyIGhpZ2ggYWJvdmUgdGhlIHJlc3Qgb2YgdGhlIGRhdGEsIHNob3dpbmcgdGhleSBoYXZlIHVudXN1YWxseSBsYXJnZSBzdGFuZGFyZGl6ZWQgcmVzaWR1YWxzLg0KDQo+ICoqUmVzaWR1YWxzIHZzIExldmVyYWdlKiogcGxvdCBzaG93cyB0aGF0IG5vIHNpbmdsZSBkYXRhIHBvaW50IGlzIGZvcmNpbmcgb3VyIG1vZGVsIHRvIGdpdmUgYmFkIHJlc3VsdHMuIFBvaW50IDE0IGlzIHVudXN1YWwgY29tcGFyZWQgdG8gb3RoZXIgZGF0YSwgYnV0IGl0IGZpdHMgdGhlIG1vZGVsJ3MgcGF0dGVybi4gUG9pbnRzIDMyNyBhbmQgMzk0IGFyZSBmYXIgb2ZmIHRoZSBtb2RlbCdzIHByZWRpY3Rpb25zLCBidXQgdGhleSBkbyBub3QgaGF2ZSBlbm91Z2ggcG93ZXIgdG8gY2hhbmdlIHRoZSBvdmVyYWxsIG1hdGguDQoNCl9fKGUpX18gVXNlIHRoZSAqIGFuZCA6IHN5bWJvbHMgdG8gZml0IGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyB3aXRoDQppbnRlcmFjdGlvbiBlZmZlY3RzLiBEbyBhbnkgaW50ZXJhY3Rpb25zIGFwcGVhciB0byBiZSBzdGF0aXN0aWNhbGx5DQpzaWduaWZpY2FudD9cDQoNCj4gSSB3aWxsIGNvbnNpZGVyIGBkaXNwbGFjZW1lbnQgKiBhY2NlbGVyYXRpb25gIGludGVyYWN0aW9uIGJlY2F1c2UgYSBjYXIgd2l0aCBhIG1hc3NpdmUgZW5naW5lIChoaWdoIGRpc3BsYWNlbWVudCkgdXN1YWxseSBhY2NlbGVyYXRlcyBxdWlja2x5LCBidXQgaXQgYnVybnMgYSBtYXNzaXZlIGFtb3VudCBvZiBmdWVsIHRvIGRvIHNvLiAgSSB3aWxsIGFsc28gdXRpbGl6ZSBgaG9yc2Vwb3dlciAqIHdlaWdodGAgaW50ZXJhY3Rpb24gYmVjYXVzZSB0aGlua2luZyBhYm91dCBiaWcgcG93ZXJmdWwgdHJ1Y2sgdGhleSBoYXZlIG1hc3NpdmUgd2VpZ2h0IGFuZCBob3JzZXBvd2VyIHlldCB0aGV5IGFyZSBiYWQgb24gZnVlbCBlZmZpY2llbmN5IHNvIEkgd2FudCB0byBzZWUgaWYgdGhlIHNhbWUgcmVzdWx0cyBhcHBseSBpbiB0aGlzIGRhdGFzZXQuIA0KDQpgYGB7cn0NCmxtLmZpdC5pbnQgPC0gbG0obXBnIH4gY3lsaW5kZXJzICsgZGlzcGxhY2VtZW50KmFjY2VsZXJhdGlvbiArIGhvcnNlcG93ZXIgKiB3ZWlnaHQgKyB5ZWFyICsgb3JpZ2luIC0gbmFtZSwgZGF0YSA9IEF1dG8pDQpzdW1tYXJ5KGxtLmZpdC5pbnQpDQpgYGANCg0KPiBBcyBpdCBzaG93cyBpbiBvdXIgbW9kZWwgb3V0cHV0IGhpZ2hlciBob3JzZXBvd2VyIGFuZCBoZWF2aWVyIGNhcnMgZHJhc3RpY2FsbHkgY2F1c2UgbG93ZXIgbXBnLiBBbHNvIHRoZSBmdWVsIGNvc3Qgb2YgYWNjZWxlcmF0aW5nIHF1aWNrbHkgaXMgbXVjaCBoaWdoZXIgd2hlbiB5b3UgYXJlIGZlZWRpbmcgYSBiaWdnZXIgZW5naW5lIHZlcnN1cyBhIHNtYWxsIGVuZ2luZS4gT3VyIGludGVyYWN0aW9ucyBoZWxwZWQgdHJlbWVuZG91c2x5IGluIG91ciBtb2RlbCBpbXByb3ZlbWVudC4gDQoNCl9fKGYpX18gVHJ5IGEgZmV3IGRpZmZlcmVudCB0cmFuc2Zvcm1hdGlvbnMgb2YgdGhlIHZhcmlhYmxlcywgc3VjaCBhcw0KbG9nKFgpLCDiiJpYLCBYMi4gQ29tbWVudCBvbiB5b3VyIGZpbmRpbmdzLlwNCg0KPiBGaXJzdCBJIHdvdWxkIHBpY2sgYSBwcmVkaWN0b3IgdmFyaWFibGUgYW5kIGFuYWx5emUgaXQgaXMgcmVsYXRpb25zaGlwIHdpdGggdGhlIHJlc3BvbnNlIHZhcmlhYmUuIE5leHQgSSB3aWxsIHRyeSB0aGlzIHRyYW5zZm9ybWF0aW9ucyBhbmQgc2VlIHdoaWNoIG9uZSBpcyBiZXN0IHRvIGNhcHR1cmUgdGhhdCByZWxhdGlvbnNoaXAgYmV0d2Vlbi4gRm9yIHRoaXMgZXhhbXBsZSBJIHdpbGwgdHJ5IGBob3JzZXBvd2VyYCBhZ2FpbnN0IGBtcGdgIGJ1dCBmaXJzdCBsZXRzIHNlZSBpbiBzY2F0dGVyIHBsb3Qgd2hhdCBhcmUgdGhlaXIgcmVsYXRpb25zaGlwIGxvb2sgbGlrZS4NCg0KYGBge3J9DQpwbG90KEF1dG8kaG9yc2Vwb3dlciwgQXV0byRtcGcsDQogICAgIHhsYWIgPSAiSG9yc2Vwb3dlciIsDQogICAgIHlsYWIgPSAiTVBHIiwNCiAgICAgbWFpbiA9ICJNUEcgdnMgSG9yc2Vwb3dlciIsDQogICAgIHBjaCA9IDEpICAgICANCmBgYA0KDQoNCmBgYHtyfQ0KbG0uZml0LmxvZyA8LSBsbShtcGcgfiBsb2coaG9yc2Vwb3dlciksIGRhdGEgPSBBdXRvKQ0Kc3VtbWFyeShsbS5maXQubG9nKQ0KYGBgDQoNCmBgYHtyfQ0KbG0uZml0LnNxcnQgPC0gbG0obXBnIH4gc3FydChob3JzZXBvd2VyKSwgZGF0YSA9IEF1dG8pDQpzdW1tYXJ5KGxtLmZpdC5zcXJ0KQ0KYGBgDQoNCmBgYHtyfQ0KbG0uZml0LnF1YWQgPC0gbG0obXBnIH4gSShob3JzZXBvd2VyXjIpLCBkYXRhID0gQXV0bykNCnN1bW1hcnkobG0uZml0LnF1YWQpDQpgYGANCg0KYGBge3J9DQpsbS5maXQucG9seSA8LSBsbShtcGcgfiBob3JzZXBvd2VyICsgSShob3JzZXBvd2VyXjIpLCBkYXRhID0gQXV0bykNCnN1bW1hcnkobG0uZml0LnBvbHkpDQpgYGANCj4gU2VlbXMgbGlrZSBwb2x5bm9taWFsIG1vZGVsIGhhcyB0aGUgYmVzdCBSLXNxdWFyZWQgdmFsdWUgd2l0aCAqKjAuNjg2Kiogbm93IGxldHMgbG9vayBhdCBhbGwgdHJhbnNmb3JtYXRpb24gbW9kZWxzIGFuZCBzZWUgaG93IHRoZXkgbG9vayBsaWtlIGluIG91ciBwcmV2aW91cyBwbG90Lg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCg0KZ2dwbG90KEF1dG8sIGFlcyh4ID0gaG9yc2Vwb3dlciwgeSA9IG1wZykpICsNCiAgICBnZW9tX3BvaW50KGNvbG9yID0gImdyYXk1MCIsIGFscGhhID0gMC41LCBzaXplID0gMikgKw0KICANCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiBsb2coeCksIA0KICAgICAgICAgICAgICBhZXMoY29sb3IgPSAiTG9nIiksIHNlID0gRkFMU0UsIHNpemUgPSAxKSArDQogIA0KICANCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiBzcXJ0KHgpLCANCiAgICAgICAgICAgICAgYWVzKGNvbG9yID0gIlNxcnQiKSwgc2UgPSBGQUxTRSwgc2l6ZSA9IDEpICsNCiAgDQogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4geCArIEkoeF4yKSwgDQogICAgICAgICAgICAgIGFlcyhjb2xvciA9ICJRdWFkIiksIHNlID0gRkFMU0UsIHNpemUgPSAxKSArDQogIA0KICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHBvbHkoeCwgMyksIA0KICAgICAgICAgICAgICBhZXMoY29sb3IgPSAiUG9seSAoQ3ViaWMpIiksIHNlID0gRkFMU0UsIHNpemUgPSAxKSArDQogIA0KICBsYWJzKHRpdGxlID0gIkNvbXBhcmluZyBNb2RlbCBUcmFuc2Zvcm1hdGlvbnM6IE1QRyB2cyBIb3JzZXBvd2VyIiwNCiAgICAgICB4ID0gIkhvcnNlcG93ZXIiLA0KICAgICAgIHkgPSAiTWlsZXMgUGVyIEdhbGxvbiAoTVBHKSIsDQogICAgICAgY29sb3IgPSAiTW9kZWwgVHlwZSIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkxvZyIgPSAicmVkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTcXJ0IiA9ICJibHVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJRdWFkIiA9ICJncmVlbjMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBvbHkgKEN1YmljKSIgPSAicHVycGxlIikpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpfX1ExMDpfXyBUaGlzIHF1ZXN0aW9uIHNob3VsZCBiZSBhbnN3ZXJlZCB1c2luZyB0aGUgQ2Fyc2VhdHMgZGF0YSBzZXQuXA0KDQpgYGB7cn0NCmRhdGEoIkNhcnNlYXRzIikNCmF0dGFjaChDYXJzZWF0cykNCmBgYA0KDQoNCl9fKGEpX18gRml0IGEgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IFNhbGVzIHVzaW5nIFByaWNlLA0KVXJiYW4sIGFuZCBVUy5cDQoNCmBgYHtyfQ0KY2Fyc2VhdF9sbSA8LSBsbShTYWxlcyB+IFByaWNlICsgVXJiYW4gKyBVUywgZGF0YSA9IENhcnNlYXRzKQ0Kc3VtbWFyeShjYXJzZWF0X2xtKQ0KYGBgDQoNCl9fKGIpX18gUHJvdmlkZSBhbiBpbnRlcnByZXRhdGlvbiBvZiBlYWNoIGNvZWZmaWNpZW50IGluIHRoZSBtb2RlbC4gQmUNCmNhcmVmdWzigJRzb21lIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIG1vZGVsIGFyZSBxdWFsaXRhdGl2ZSFcDQoNCj4gKipQcmljZSAoLTAuMDU0KToqKiBob2xkaW5nIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudCBmb3IgZXZlcnkgJDEgaW5jcmVhc2UgaW4gcHJpY2UsIHNhbGVzIGRlY3JlYXNlIGJ5IGFib3V0IDAuMDU0IHVuaXRzLlwNCioqVXJiYW5ZZXMgKC0wLjAyMikqKiBob2xkaW5nIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudCwgc3RvcmVzIGluIHVyYmFuIGxvY2F0aW9ucyBzZWxsIGFib3V0IDAuMDIyIGZld2VyIHVuaXRzIHRoYW4gbm9uLXVyYmFuIHN0b3Jlcy4gVGhpcyBlZmZlY3QgaXMgdmVyeSBzbWFsbC5cDQoqKlVTWWVzICgxLjIwMSkqKiBob2xkaW5nIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudCwgc3RvcmVzIGxvY2F0ZWQgaW4gdGhlIFVTIHNlbGwgYWJvdXQgMS4yMCBtb3JlIHVuaXRzIHRoYW4gbm9uLVVTIHN0b3Jlcy4gDQoNCl9fKGMpX18gV3JpdGUgb3V0IHRoZSBtb2RlbCBpbiBlcXVhdGlvbiBmb3JtLCBiZWluZyBjYXJlZnVsIHRvIGhhbmRsZQ0KdGhlIHF1YWxpdGF0aXZlIHZhcmlhYmxlcyBwcm9wZXJseS5cDQoNCj4gJFNhbGVzPVxiZXRhXzAgKyBcYmV0YV8xICogUHJpY2UgKyBcYmV0YV8yICogVXJiYW5ZZXMgKyBcYmV0YV8zICogVVNZZXMkDQoNCl9fKGQpX18gRm9yIHdoaWNoIG9mIHRoZSBwcmVkaWN0b3JzIGNhbiB5b3UgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMNCkgwIDogzrJqID0wP1wNCg0KPiBgUHJpY2VgIGFuZCBgVVNZZXNgIGhhdmUgc2lnbmlmaWNhbnQgcC12YWx1ZXMsIGJ1dCBgVXJiYW5ZZXNgIGRvZXNudCBoYXZlLCB0aGVyZWZvcmUgb25seSBgVXJiYW5ZZXNgIGZhaWwgdG8gcmVqZWN0IE51bGwgaHlwb3RoZXNpcy4NCg0KX18oZSlfXyBPbiB0aGUgYmFzaXMgb2YgeW91ciByZXNwb25zZSB0byB0aGUgcHJldmlvdXMgcXVlc3Rpb24sIGZpdCBhDQpzbWFsbGVyIG1vZGVsIHRoYXQgb25seSB1c2VzIHRoZSBwcmVkaWN0b3JzIGZvciB3aGljaCB0aGVyZSBpcw0KZXZpZGVuY2Ugb2YgYXNzb2NpYXRpb24gd2l0aCB0aGUgb3V0Y29tZS5cDQoNCmBgYHtyfQ0KY2Fyc2VhdF9sbV9yZWR1Y2VkIDwtIGxtKFNhbGVzIH4gUHJpY2UgKyBVUywgZGF0YSA9IENhcnNlYXRzKQ0Kc3VtbWFyeShjYXJzZWF0X2xtX3JlZHVjZWQpDQpgYGANCg0KDQpfXyhmKV9fIEhvdyB3ZWxsIGRvIHRoZSBtb2RlbHMgaW4gKGEpIGFuZCAoZSkgZml0IHRoZSBkYXRhP1wNCg0KPiBCb3RoIG1vZGVscyBmaXQgdGhlIGRhdGEgd2l0aCBzYW1lIHBlcmZvcm1hbmNlLiBCb3RoIG1vZGVsJ3MgTXVsdGlwbGUgUi1zcXVhcmVkOiAgMC4yMzkzIGJ1dCBBZGp1c3RlZCBSLXNxdWFyZWQgaW5jcmVhc2VkIGluIG1vZGVsIGluIChlKSBwcm92aWRpbmcgcGFyc2ltb25pb3VzIGZpdCB3aXRoIGZld2VyIHZhcmlhYmxlLg0KDQpfXyhnKV9fIFVzaW5nIHRoZSBtb2RlbCBmcm9tIChlKSwgb2J0YWluIDk1JSBjb25maWRlbmNlIGludGVydmFscyBmb3INCnRoZSBjb2VmZmljaWVudChzKS5cDQoNCmBgYHtyfQ0KY29uZmludChjYXJzZWF0X2xtX3JlZHVjZWQpDQpgYGANCg0KDQpfXyhoKV9fIElzIHRoZXJlIGV2aWRlbmNlIG9mIG91dGxpZXJzIG9yIGhpZ2ggbGV2ZXJhZ2Ugb2JzZXJ2YXRpb25zIGluIHRoZQ0KbW9kZWwgZnJvbSAoZSk/XA0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsMikpDQpwbG90KGNhcnNlYXRfbG1fcmVkdWNlZCkNCmBgYA0KDQpgYGB7cn0NCnBsb3QoaGF0dmFsdWVzKGNhcnNlYXRfbG1fcmVkdWNlZCkpDQpgYGANCmBgYHtyfQ0KaGF0IDwtIGhhdHZhbHVlcyhjYXJzZWF0X2xtX3JlZHVjZWQpDQoNCndoaWNoLm1heChoYXQpDQpgYGANCj4gT2JzZXJ2YXRpb24gNDMgaGFzIGhpZ2hlc3QgaGF0IHZhbHVlLg0KDQpfX1ExMjpfXyBUaGlzIHByb2JsZW0gaW52b2x2ZXMgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIHdpdGhvdXQgYW4gaW50ZXJjZXB0LlwNCg0KX18oYSlfXyBSZWNhbGwgdGhhdCB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGUgy4bOsiBmb3IgdGhlIGxpbmVhciByZWdyZXNzaW9uIG9mDQpZIG9udG8gWCB3aXRob3V0IGFuIGludGVyY2VwdCBpcyBnaXZlbiBieSAoMy4zOCkuIFVuZGVyIHdoYXQNCmNpcmN1bXN0YW5jZSBpcyB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGUgZm9yIHRoZSByZWdyZXNzaW9uIG9mIFgNCm9udG8gWSB0aGUgc2FtZSBhcyB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGUgZm9yIHRoZSByZWdyZXNzaW9uIG9mDQpZIG9udG8gWD9cICANCg0KPlJlZ3Jlc3Npb24gd2l0aG91dCBhbiBpbnRlcmNlcHQsIHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMgYXJlIA0KDQokJA0KXGhhdHtcYmV0YX1fe1l8WH0gPSBcZnJhY3tcc3VtIHhfaSB5X2l9e1xzdW0geF9pXjJ9DQokJA0KPmFuZCANCg0KJCQNClxoYXR7XGJldGF9X3tYfFl9ID0gXGZyYWN7XHN1bSB4X2kgeV9pfXtcc3VtIHlfaV4yfQ0KJCQNCg0KPnRoZSBDb2VmZmljaWVudCBlc3RpbWF0ZXMgd2lsbCBiZSBlcXVhbCB3aGVuIA0KDQokJA0KXGZyYWN7XHN1bSB4X2kgeV9pfXtcc3VtIHhfaV4yfSA9IFxmcmFje1xzdW0geF9pIHlfaX17XHN1bSB5X2leMn0NCiQkDQoNCj4gQWZ0ZXIgY2FuY2VsaW5nIGNvbW1vbiB0ZXJtcyBvdXIgZmluYWwgZm9ybXVsYSBsb29rIGxpa2UgYmVsb3cNCg0KJCQNClxzdW0geF9pXjIgPSBcc3VtIHlfaV4yDQokJA0KDQo+IFRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZXMgYXJlIHRoZSBzYW1lIHdoZW4gWCBhbmQgWSBoYXZlIHRoZSBzYW1lIHN1bSBvZiBzcXVhcmVkIHZhbHVlcw0KDQoNCg0KX18oYilfXyBHZW5lcmF0ZSBhbiBleGFtcGxlIGluIFIgd2l0aCBuID0gMTAwIG9ic2VydmF0aW9ucyBpbiB3aGljaA0KdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlIGZvciB0aGUgcmVncmVzc2lvbiBvZiBYIG9udG8gWSBpcyBkaWZmZXJlbnQNCmZyb20gdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlIGZvciB0aGUgcmVncmVzc2lvbiBvZiBZIG9udG8gWC5cDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNDUpDQoNCnggPC0gcm5vcm0oMTAwKQ0KeSA8LSAyKnggKyBybm9ybSgxMDApDQoNCmNvZWYobG0oeSB+IHggLSAxKSkNCmNvZWYobG0oeCB+IHkgLSAxKSkNCmBgYA0KPiBJIGdlbmVyYXRlZCAxMDAgb2JzZXJ2YXRpb25zIHdpdGggJHk9Mngrz7UkLiBUaGUgZXN0aW1hdGVkIGNvZWZmaWNpZW50IGZvciB0aGUgcmVncmVzc2lvbiBvZiBZIG9uIFggZGlmZmVyZWQgZnJvbSB0aGUgZXN0aW1hdGVkIGNvZWZmaWNpZW50IGZvciB0aGUgcmVncmVzc2lvbiBvZiBYIG9uIFksIGRlbW9uc3RyYXRpbmcgdGhhdCB0aGUgdHdvIHJlZ3Jlc3Npb25zIGdlbmVyYWxseSBwcm9kdWNlIGRpZmZlcmVudCBlc3RpbWF0ZXMuDQoNCl9fKGMpX18gR2VuZXJhdGUgYW4gZXhhbXBsZSBpbiBSIHdpdGggbiA9IDEwMCBvYnNlcnZhdGlvbnMgaW4gd2hpY2gNCnRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZSBmb3IgdGhlIHJlZ3Jlc3Npb24gb2YgWCBvbnRvIFkgaXMgdGhlDQpzYW1lIGFzIHRoZSBjb2VmZmljaWVudCBlc3RpbWF0ZSBmb3IgdGhlIHJlZ3Jlc3Npb24gb2YgWSBvbnRvIFguXA0KDQpgYGB7cn0NCnNldC5zZWVkKDQ1KQ0KDQp4IDwtIHJub3JtKDEwMCkNCnkgPC0gc2FtcGxlKGMoLTEsMSksIDEwMCwgcmVwbGFjZSA9IFRSVUUpICogeA0KDQpzdW0oeF4yKQ0Kc3VtKHleMikNCg0KY29lZihsbSh5IH4geCAtIDEpKQ0KY29lZihsbSh4IH4geSAtIDEpKQ0KYGBgDQo+IEkgZ2VuZXJhdGVkIFkgYnkgbXVsdGlwbHlpbmcgZWFjaCB2YWx1ZSBvZiBYIGJ5IGVpdGhlciAxIG9yIC0xLiBBcyBhIHJlc3VsdCwgWCBhbmQgWSBoYWQgaWRlbnRpY2FsIHN1bXMgb2Ygc3F1YXJlcywgYW5kIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnQgZm9yIHRoZSByZWdyZXNzaW9uIG9mIFkgb24gWCB3YXMgdGhlIHNhbWUgYXMgdGhlIGVzdGltYXRlZCBjb2VmZmljaWVudCBmb3IgdGhlIHJlZ3Jlc3Npb24gb2YgWCBvbiBZDQo=