Example 4.1 Munich Rent Index — Diagnostics for Heteroscedastic Errors

Importing the Munich Rent Index data:

source("import_data/munich_rent_index.R")

Adjust the model:

\[ \text{rent}_{i} = \beta_{0} + \beta_{1} \text{area}_{i} + \beta_{2} \text{yearco}_{i} + \beta_{3} \text{yearco2}_{i} + \beta_{4} \text{yearco3}_{i} + \epsilon_{i} \]

munich_rent_poly_yearc <- poly(munich_rent_index$yearc, 3)
munich_rent_index[, c("yearco", "yearco2", "yearco3")] <- munich_rent_poly_yearc
munich_regression <- lm(rent ~ area + yearco + yearco2 + yearco3, data = munich_rent_index)
summary(munich_regression)

Call:
lm(formula = rent ~ area + yearco + yearco2 + yearco3, data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-758.18  -89.06   -8.24   83.59 1039.08 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  113.3754     8.2222  13.789   <2e-16 ***
area           5.1364     0.1156  44.425   <2e-16 ***
yearco      3016.5227   150.2419  20.078   <2e-16 ***
yearco2     1747.8691   148.1177  11.801   <2e-16 ***
yearco3      -11.0154   146.0896  -0.075     0.94    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 146.1 on 3077 degrees of freedom
Multiple R-squared:  0.4433,    Adjusted R-squared:  0.4425 
F-statistic: 612.5 on 4 and 3077 DF,  p-value: < 2.2e-16

And we visualize:

par(mfrow = c(3, 2))

area_seq <- seq(min(munich_rent_index$area), max(munich_rent_index$area), length.out = 100)
yearc_seq <- seq(min(munich_rent_index$yearc), max(munich_rent_index$yearc), length.out = 100)

munich_beta <- munich_regression$coefficients

area_effect <- function(area){
  munich_beta[2] * area
}

yearc_effect <- function(yearc){
  yearc_poly <- predict(munich_rent_poly_yearc, yearc)
  munich_beta[3] * yearc_poly[, 1] + munich_beta[4] * yearc_poly[, 2] + munich_beta[5] * yearc_poly[, 3]
}

partial_area <- munich_rent_index$rent - munich_beta[1] - yearc_effect(munich_rent_index$yearc)
plot(
  munich_rent_index$area,
  partial_area,
  main = 'effect of area incl. partial residuals',
  xlab = 'area in sqm',
  ylab = 'effect of area'
)
lines(area_seq, area_effect(area_seq), col = 'red', lwd = 2)

partial_year <- munich_rent_index$rent - munich_beta[1] - area_effect(munich_rent_index$area)
plot(
  munich_rent_index$yearc,
  partial_year,
  main = 'effect of year of construction incl. partial residuals',
  xlab = 'year of construction',
  ylab = 'effect of year of construction'
)
lines(yearc_seq, yearc_effect(yearc_seq), col = 'red', lwd = 2)

studentized_residuals <- rstudent(munich_regression)
plot(
  munich_regression$fitted.values,
  studentized_residuals,
  main = 'studentized residuals vs. estimated rent',
  xlab = 'estimated rent',
  ylab = 'studentized residuals'
)
plot(
  munich_rent_index$area,
  studentized_residuals,
  main = 'studentized residuals vs. area',
  xlab = 'area in sqm',
  ylab = 'studentized residuals'
)
plot(
  munich_rent_index$yearc,
  studentized_residuals,
  main = 'studentized residuals vs. year of construction',
  xlab = 'year of construction',
  ylab = 'studentized residuals'
)

Example 4.2 Munich Rent Index — Breusch–Pagan Test

Assuming the variance model:

\[ {\sigma_{i}}^{2} = \sigma_{i} \cdot h \left ( \alpha_{0} + \alpha_{1} \text{areao}_{i} + \alpha_{2} \text{areao2}_{i} + \alpha_{3} \text{areao3}_{i} + \alpha_{4} \text{yearco}_{i} + \alpha_{5} \text{yearco2}_{i} + \alpha_{6} \text{yearco3}_{i} \right ) \]

We add the polynomial of degree 3 of \(\text{area}\):

munich_rent_poly_area <- poly(munich_rent_index$area, 3)
munich_rent_index[, c("areao", "areao2", "areao3")] <- munich_rent_poly_area

We calculate the Breusch and Pagan Test:

\[ {\sigma_{i}}^{2} = \sigma_{i} \cdot h \left ( \alpha_{0} + \alpha_{1} {z}_{i1} + \cdots + \alpha_{q} {z}_{iq} \right ) \]

\[ \begin{matrix} H_{0}: \alpha_{1} = \cdots = \alpha_{q} = 0 & \text{against} & H_{1}: \alpha_{j} \neq 0 \text{ for at least one } j \end{matrix} \]

\[ g_{i} = \frac{{\hat{\epsilon_{i}}}^{2}}{{\hat{\sigma}}_{ML}^{2}} \]

\[ T = \frac{1}{2} \sum_{i=1}^{2} \left ( \hat{g}_{i} - \bar{g} \right )^{2} \]

We calculate using R package lmtest:

library(lmtest)
bptest(
  munich_regression,
  ~ areao + areao2 + areao3 + yearco + yearco2 + yearco3,
  data = munich_rent_index
)

    studentized Breusch-Pagan test

data:  munich_regression
BP = 558.7, df = 6, p-value < 2.2e-16

Example 4.3 Munich Rent Index — Two-Stage Estimation

First step: Classical linear model

Using the model of Example 4.1:

summary(munich_regression)

Call:
lm(formula = rent ~ area + yearco + yearco2 + yearco3, data = munich_rent_index)

Residuals:
    Min      1Q  Median      3Q     Max 
-758.18  -89.06   -8.24   83.59 1039.08 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  113.3754     8.2222  13.789   <2e-16 ***
area           5.1364     0.1156  44.425   <2e-16 ***
yearco      3016.5227   150.2419  20.078   <2e-16 ***
yearco2     1747.8691   148.1177  11.801   <2e-16 ***
yearco3      -11.0154   146.0896  -0.075     0.94    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 146.1 on 3077 degrees of freedom
Multiple R-squared:  0.4433,    Adjusted R-squared:  0.4425 
F-statistic: 612.5 on 4 and 3077 DF,  p-value: < 2.2e-16

Second step: Auxiliary regression

Estimate the regression model of example

\[ \log({\hat{\epsilon}_{i}}^{2}) = \alpha_{0} + \alpha_{1} \text{areao}_{i} + \alpha_{2} \text{areao2}_{i} + \alpha_{3} \text{areao3}_{i} + \alpha_{4} \text{yearco}_{i} + \alpha_{5} \text{yearco2}_{i} + \alpha_{6} \text{yearco3}_{i} + \nu_{i} \]

par(mfrow = c(2, 2))

log_sq_residuals <- log(residuals(munich_regression)^2)
variance_model <- lm(
  log_sq_residuals ~ areao + areao2 + areao3 + yearco + yearco2 + yearco3,
  data = munich_rent_index
)
summary(variance_model)

Call:
lm(formula = log_sq_residuals ~ areao + areao2 + areao3 + yearco + 
    yearco2 + yearco3, data = munich_rent_index)

Residuals:
     Min       1Q   Median       3Q      Max 
-14.1548  -0.9571   0.5289   1.4981   4.2336 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   8.47901    0.03988 212.594  < 2e-16 ***
areao        34.91018    2.30869  15.121  < 2e-16 ***
areao2       -6.91661    2.22960  -3.102  0.00194 ** 
areao3        2.10026    2.21900   0.946  0.34397    
yearco      -13.85151    2.28931  -6.051 1.62e-09 ***
yearco2       6.72398    2.25083   2.987  0.00284 ** 
yearco3      11.08694    2.21764   4.999 6.07e-07 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.214 on 3075 degrees of freedom
Multiple R-squared:  0.1125,    Adjusted R-squared:  0.1108 
F-statistic: 64.96 on 6 and 3075 DF,  p-value: < 2.2e-16
plot(
  munich_rent_index$area,
  log_sq_residuals,
  main = 'log. squared residuals vs. area',
  xlab = 'log. squared residuals',
  ylab = 'area in sqm'
)

plot(
  munich_rent_index$yearc,
  log_sq_residuals,
  main = 'log. squared residuals vs. year of construction',
  xlab = 'log. squared residuals',
  ylab = 'year of construction'
)

variance_beta <- variance_model$coefficients

area_variance_effect <- function(area){
  i_poly_area <- predict(munich_rent_poly_area, area)
  variance_beta[2] * i_poly_area[, 1] + variance_beta[3] * i_poly_area[, 2] + variance_beta[4] * i_poly_area[, 3]
}

yearc_variance_effect <- function(yearc){
  yearc_poly <- predict(munich_rent_poly_yearc, yearc)
  variance_beta[5] * yearc_poly[, 1] + variance_beta[6] * yearc_poly[, 2] + variance_beta[7] * yearc_poly[, 3]
}

plot(
  munich_rent_index$area,
  log_sq_residuals - variance_beta[1] - yearc_variance_effect(munich_rent_index$yearc),
  main = 'residual regression: effect of area',
  xlab = 'effect of area',
  ylab = 'area in sqm'
)
lines(area_seq, area_variance_effect(area_seq), col = 'red', lwd = 2)

plot(
  munich_rent_index$yearc,
  log_sq_residuals - variance_beta[1] - area_variance_effect(munich_rent_index$area),
  main = 'residual regression: effect of year of construction',
  xlab = 'effect of year of construction',
  ylab = 'year of construction'
)
lines(yearc_seq, yearc_variance_effect(yearc_seq), col = 'red', lwd = 2)

Third step: Weighted linear model

We calculate the Weight:

weight <- 1 / exp(variance_model$fitted.values)
head(weight)
           1            2            3            4            5            6 
3.548163e-04 5.247798e-05 1.127534e-03 7.297107e-04 1.226543e-04 2.707725e-04 

And now the adjustment:

weighted_linear_model <- lm(
  rent ~ area + yearco + yearco2 + yearco3,
  data = munich_rent_index,
  weights = weight
)
summary(weighted_linear_model)

Call:
lm(formula = rent ~ area + yearco + yearco2 + yearco3, data = munich_rent_index, 
    weights = weight)

Weighted Residuals:
    Min      1Q  Median      3Q     Max 
-6.8645 -1.3168 -0.1547  1.3007  8.5157 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  124.3734     5.8569  21.235   <2e-16 ***
area           4.9643     0.1004  49.430   <2e-16 ***
yearco      2703.4555   147.4826  18.331   <2e-16 ***
yearco2     1403.5794   129.5723  10.832   <2e-16 ***
yearco3      -73.4991   132.1618  -0.556    0.578    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 1.855 on 3077 degrees of freedom
Multiple R-squared:  0.4947,    Adjusted R-squared:  0.4941 
F-statistic: 753.2 on 4 and 3077 DF,  p-value: < 2.2e-16

And we compare the covariance matrix of both model:

vcov(munich_regression)
             (Intercept)        area       yearco      yearco2       yearco3
(Intercept)   67.6043077 -0.90063685  -273.475196   190.606709   -11.1223265
area          -0.9006368  0.01336757     4.059016    -2.829052     0.1650815
yearco      -273.4751960  4.05901572 22572.641836  -859.031732    50.1264169
yearco2      190.6067095 -2.82905229  -859.031732 21938.863890   -34.9371040
yearco3      -11.1223265  0.16508151    50.126417   -34.937104 21342.1747138
vcov(weighted_linear_model)
             (Intercept)        area       yearco      yearco2       yearco3
(Intercept)   34.3033928 -0.54385319  -238.469903   175.705533    70.5099712
area          -0.5438532  0.01008615     2.273448    -1.993453    -0.4420123
yearco      -238.4699026  2.27344774 21751.123356 -4179.319393  2680.1687402
yearco2      175.7055331 -1.99345269 -4179.319393 16788.973668 -1332.6752253
yearco3       70.5099712 -0.44201233  2680.168740 -1332.675225 17466.7502213

And the confidence intervals:

linear_confint <- confint(munich_regression)
linear_confint
                  2.5 %      97.5 %
(Intercept)   97.253848  129.496900
area           4.909675    5.363069
yearco      2721.938054 3311.107407
yearco2     1457.449437 2038.288772
yearco3     -297.458491  275.427613
weighted_confint <- confint(weighted_linear_model)
weighted_confint
                  2.5 %      97.5 %
(Intercept)  112.889518  135.857217
area           4.767364    5.161197
yearco      2414.281147 2992.629893
yearco2     1149.522483 1657.636326
yearco3     -332.633438  185.635293

Example 4.4 Munich Rent Index — White-Estimator

We use R to obtain the covariance matrix and the confidence intervals:

library(sandwich)
robust_se <- sqrt(diag(vcovHC(munich_regression, type = "HC1")))
coefs <- munich_regression$coefficients
z <- qnorm(1 - 0.05 / 2)
lower <- coefs - z * robust_se
upper <- coefs + z * robust_se
confint_robust <- data.frame(lower, upper)
colnames(confint_robust) <- colnames(linear_confint)
confint_robust

Example 4.5 Prices of Used Cars — Weighted Regression

LS0tCnRpdGxlOiAiRXh0ZW5zaW9ucyBvZiB0aGUgQ2xhc3NpY2FsIExpbmVhciBNb2RlbCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgRXhhbXBsZSA0LjEgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIERpYWdub3N0aWNzIGZvciBIZXRlcm9zY2VkYXN0aWMgRXJyb3JzCgpJbXBvcnRpbmcgdGhlIE11bmljaCBSZW50IEluZGV4IGRhdGE6CgpgYGB7cn0Kc291cmNlKCJpbXBvcnRfZGF0YS9tdW5pY2hfcmVudF9pbmRleC5SIikKYGBgCgpBZGp1c3QgdGhlIG1vZGVsOgoKJCQKXHRleHR7cmVudH1fe2l9ID0gXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2FyZWF9X3tpfSArIFxiZXRhX3syfSBcdGV4dHt5ZWFyY299X3tpfSArIFxiZXRhX3szfSBcdGV4dHt5ZWFyY28yfV97aX0gKyBcYmV0YV97NH0gXHRleHR7eWVhcmNvM31fe2l9ICsgXGVwc2lsb25fe2l9CiQkCgpgYGB7cn0KbXVuaWNoX3JlbnRfcG9seV95ZWFyYyA8LSBwb2x5KG11bmljaF9yZW50X2luZGV4JHllYXJjLCAzKQptdW5pY2hfcmVudF9pbmRleFssIGMoInllYXJjbyIsICJ5ZWFyY28yIiwgInllYXJjbzMiKV0gPC0gbXVuaWNoX3JlbnRfcG9seV95ZWFyYwptdW5pY2hfcmVncmVzc2lvbiA8LSBsbShyZW50IH4gYXJlYSArIHllYXJjbyArIHllYXJjbzIgKyB5ZWFyY28zLCBkYXRhID0gbXVuaWNoX3JlbnRfaW5kZXgpCnN1bW1hcnkobXVuaWNoX3JlZ3Jlc3Npb24pCmBgYAoKQW5kIHdlIHZpc3VhbGl6ZToKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyLCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMywgMikpCgphcmVhX3NlcSA8LSBzZXEobWluKG11bmljaF9yZW50X2luZGV4JGFyZWEpLCBtYXgobXVuaWNoX3JlbnRfaW5kZXgkYXJlYSksIGxlbmd0aC5vdXQgPSAxMDApCnllYXJjX3NlcSA8LSBzZXEobWluKG11bmljaF9yZW50X2luZGV4JHllYXJjKSwgbWF4KG11bmljaF9yZW50X2luZGV4JHllYXJjKSwgbGVuZ3RoLm91dCA9IDEwMCkKCm11bmljaF9iZXRhIDwtIG11bmljaF9yZWdyZXNzaW9uJGNvZWZmaWNpZW50cwoKYXJlYV9lZmZlY3QgPC0gZnVuY3Rpb24oYXJlYSl7CiAgbXVuaWNoX2JldGFbMl0gKiBhcmVhCn0KCnllYXJjX2VmZmVjdCA8LSBmdW5jdGlvbih5ZWFyYyl7CiAgeWVhcmNfcG9seSA8LSBwcmVkaWN0KG11bmljaF9yZW50X3BvbHlfeWVhcmMsIHllYXJjKQogIG11bmljaF9iZXRhWzNdICogeWVhcmNfcG9seVssIDFdICsgbXVuaWNoX2JldGFbNF0gKiB5ZWFyY19wb2x5WywgMl0gKyBtdW5pY2hfYmV0YVs1XSAqIHllYXJjX3BvbHlbLCAzXQp9CgpwYXJ0aWFsX2FyZWEgPC0gbXVuaWNoX3JlbnRfaW5kZXgkcmVudCAtIG11bmljaF9iZXRhWzFdIC0geWVhcmNfZWZmZWN0KG11bmljaF9yZW50X2luZGV4JHllYXJjKQpwbG90KAogIG11bmljaF9yZW50X2luZGV4JGFyZWEsCiAgcGFydGlhbF9hcmVhLAogIG1haW4gPSAnZWZmZWN0IG9mIGFyZWEgaW5jbC4gcGFydGlhbCByZXNpZHVhbHMnLAogIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogIHlsYWIgPSAnZWZmZWN0IG9mIGFyZWEnCikKbGluZXMoYXJlYV9zZXEsIGFyZWFfZWZmZWN0KGFyZWFfc2VxKSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCgpwYXJ0aWFsX3llYXIgPC0gbXVuaWNoX3JlbnRfaW5kZXgkcmVudCAtIG11bmljaF9iZXRhWzFdIC0gYXJlYV9lZmZlY3QobXVuaWNoX3JlbnRfaW5kZXgkYXJlYSkKcGxvdCgKICBtdW5pY2hfcmVudF9pbmRleCR5ZWFyYywKICBwYXJ0aWFsX3llYXIsCiAgbWFpbiA9ICdlZmZlY3Qgb2YgeWVhciBvZiBjb25zdHJ1Y3Rpb24gaW5jbC4gcGFydGlhbCByZXNpZHVhbHMnLAogIHhsYWIgPSAneWVhciBvZiBjb25zdHJ1Y3Rpb24nLAogIHlsYWIgPSAnZWZmZWN0IG9mIHllYXIgb2YgY29uc3RydWN0aW9uJwopCmxpbmVzKHllYXJjX3NlcSwgeWVhcmNfZWZmZWN0KHllYXJjX3NlcSksIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKc3R1ZGVudGl6ZWRfcmVzaWR1YWxzIDwtIHJzdHVkZW50KG11bmljaF9yZWdyZXNzaW9uKQpwbG90KAogIG11bmljaF9yZWdyZXNzaW9uJGZpdHRlZC52YWx1ZXMsCiAgc3R1ZGVudGl6ZWRfcmVzaWR1YWxzLAogIG1haW4gPSAnc3R1ZGVudGl6ZWQgcmVzaWR1YWxzIHZzLiBlc3RpbWF0ZWQgcmVudCcsCiAgeGxhYiA9ICdlc3RpbWF0ZWQgcmVudCcsCiAgeWxhYiA9ICdzdHVkZW50aXplZCByZXNpZHVhbHMnCikKcGxvdCgKICBtdW5pY2hfcmVudF9pbmRleCRhcmVhLAogIHN0dWRlbnRpemVkX3Jlc2lkdWFscywKICBtYWluID0gJ3N0dWRlbnRpemVkIHJlc2lkdWFscyB2cy4gYXJlYScsCiAgeGxhYiA9ICdhcmVhIGluIHNxbScsCiAgeWxhYiA9ICdzdHVkZW50aXplZCByZXNpZHVhbHMnCikKcGxvdCgKICBtdW5pY2hfcmVudF9pbmRleCR5ZWFyYywKICBzdHVkZW50aXplZF9yZXNpZHVhbHMsCiAgbWFpbiA9ICdzdHVkZW50aXplZCByZXNpZHVhbHMgdnMuIHllYXIgb2YgY29uc3RydWN0aW9uJywKICB4bGFiID0gJ3llYXIgb2YgY29uc3RydWN0aW9uJywKICB5bGFiID0gJ3N0dWRlbnRpemVkIHJlc2lkdWFscycKKQpgYGAKCiMjIEV4YW1wbGUgNC4yIE11bmljaCBSZW50IEluZGV4IOKAlCBCcmV1c2No4oCTUGFnYW4gVGVzdAoKQXNzdW1pbmcgdGhlIHZhcmlhbmNlIG1vZGVsOgoKJCQKe1xzaWdtYV97aX19XnsyfSA9IFxzaWdtYV97aX0gXGNkb3QgaCBcbGVmdCAoIFxhbHBoYV97MH0gKyBcYWxwaGFfezF9IFx0ZXh0e2FyZWFvfV97aX0gKyBcYWxwaGFfezJ9IFx0ZXh0e2FyZWFvMn1fe2l9ICsgXGFscGhhX3szfSBcdGV4dHthcmVhbzN9X3tpfSArIFxhbHBoYV97NH0gXHRleHR7eWVhcmNvfV97aX0gKyBcYWxwaGFfezV9IFx0ZXh0e3llYXJjbzJ9X3tpfSArIFxhbHBoYV97Nn0gXHRleHR7eWVhcmNvM31fe2l9IFxyaWdodCApCiQkCgpXZSBhZGQgdGhlIHBvbHlub21pYWwgb2YgZGVncmVlIDMgb2YgJFx0ZXh0e2FyZWF9JDoKCmBgYHtyfQptdW5pY2hfcmVudF9wb2x5X2FyZWEgPC0gcG9seShtdW5pY2hfcmVudF9pbmRleCRhcmVhLCAzKQptdW5pY2hfcmVudF9pbmRleFssIGMoImFyZWFvIiwgImFyZWFvMiIsICJhcmVhbzMiKV0gPC0gbXVuaWNoX3JlbnRfcG9seV9hcmVhCmBgYAoKV2UgY2FsY3VsYXRlIHRoZSBCcmV1c2NoIGFuZCBQYWdhbiBUZXN0OgoKJCQKe1xzaWdtYV97aX19XnsyfSA9IFxzaWdtYV97aX0gXGNkb3QgaCBcbGVmdCAoIFxhbHBoYV97MH0gKyBcYWxwaGFfezF9IHt6fV97aTF9ICsgXGNkb3RzICsgXGFscGhhX3txfSB7en1fe2lxfSBccmlnaHQgKQokJAoKJCQKXGJlZ2lue21hdHJpeH0KICBIX3swfTogXGFscGhhX3sxfSA9IFxjZG90cyA9IFxhbHBoYV97cX0gPSAwICYgXHRleHR7YWdhaW5zdH0gJiBIX3sxfTogXGFscGhhX3tqfSBcbmVxIDAgXHRleHR7IGZvciBhdCBsZWFzdCBvbmUgfSBqClxlbmR7bWF0cml4fQokJAoKJCQKZ197aX0gPSBcZnJhY3t7XGhhdHtcZXBzaWxvbl97aX19fV57Mn19e3tcaGF0e1xzaWdtYX19X3tNTH1eezJ9fQokJAoKJCQKVCA9IFxmcmFjezF9ezJ9IFxzdW1fe2k9MX1eezJ9IFxsZWZ0ICggXGhhdHtnfV97aX0gLSBcYmFye2d9IFxyaWdodCApXnsyfQokJAoKV2UgY2FsY3VsYXRlIHVzaW5nIFIgcGFja2FnZSBgbG10ZXN0YDoKCmBgYHtyfQpsaWJyYXJ5KGxtdGVzdCkKYnB0ZXN0KAogIG11bmljaF9yZWdyZXNzaW9uLAogIH4gYXJlYW8gKyBhcmVhbzIgKyBhcmVhbzMgKyB5ZWFyY28gKyB5ZWFyY28yICsgeWVhcmNvMywKICBkYXRhID0gbXVuaWNoX3JlbnRfaW5kZXgKKQpgYGAKCiMjIEV4YW1wbGUgNC4zIE11bmljaCBSZW50IEluZGV4IOKAlCBUd28tU3RhZ2UgRXN0aW1hdGlvbgoKIyMjIEZpcnN0IHN0ZXA6IENsYXNzaWNhbCBsaW5lYXIgbW9kZWwKClVzaW5nIHRoZSBtb2RlbCBvZiBFeGFtcGxlIDQuMToKCmBgYHtyfQpzdW1tYXJ5KG11bmljaF9yZWdyZXNzaW9uKQpgYGAKCiMjIyBTZWNvbmQgc3RlcDogQXV4aWxpYXJ5IHJlZ3Jlc3Npb24KCkVzdGltYXRlIHRoZSByZWdyZXNzaW9uIG1vZGVsIG9mIGV4YW1wbGUKCiQkClxsb2coe1xoYXR7XGVwc2lsb259X3tpfX1eezJ9KSA9IFxhbHBoYV97MH0gKyBcYWxwaGFfezF9IFx0ZXh0e2FyZWFvfV97aX0gKyBcYWxwaGFfezJ9IFx0ZXh0e2FyZWFvMn1fe2l9ICsgXGFscGhhX3szfSBcdGV4dHthcmVhbzN9X3tpfSArIFxhbHBoYV97NH0gXHRleHR7eWVhcmNvfV97aX0gKyBcYWxwaGFfezV9IFx0ZXh0e3llYXJjbzJ9X3tpfSArIFxhbHBoYV97Nn0gXHRleHR7eWVhcmNvM31fe2l9ICsgXG51X3tpfQokJAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04LCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMiwgMikpCgpsb2dfc3FfcmVzaWR1YWxzIDwtIGxvZyhyZXNpZHVhbHMobXVuaWNoX3JlZ3Jlc3Npb24pXjIpCnZhcmlhbmNlX21vZGVsIDwtIGxtKAogIGxvZ19zcV9yZXNpZHVhbHMgfiBhcmVhbyArIGFyZWFvMiArIGFyZWFvMyArIHllYXJjbyArIHllYXJjbzIgKyB5ZWFyY28zLAogIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleAopCnN1bW1hcnkodmFyaWFuY2VfbW9kZWwpCgpwbG90KAogIG11bmljaF9yZW50X2luZGV4JGFyZWEsCiAgbG9nX3NxX3Jlc2lkdWFscywKICBtYWluID0gJ2xvZy4gc3F1YXJlZCByZXNpZHVhbHMgdnMuIGFyZWEnLAogIHhsYWIgPSAnbG9nLiBzcXVhcmVkIHJlc2lkdWFscycsCiAgeWxhYiA9ICdhcmVhIGluIHNxbScKKQoKcGxvdCgKICBtdW5pY2hfcmVudF9pbmRleCR5ZWFyYywKICBsb2dfc3FfcmVzaWR1YWxzLAogIG1haW4gPSAnbG9nLiBzcXVhcmVkIHJlc2lkdWFscyB2cy4geWVhciBvZiBjb25zdHJ1Y3Rpb24nLAogIHhsYWIgPSAnbG9nLiBzcXVhcmVkIHJlc2lkdWFscycsCiAgeWxhYiA9ICd5ZWFyIG9mIGNvbnN0cnVjdGlvbicKKQoKdmFyaWFuY2VfYmV0YSA8LSB2YXJpYW5jZV9tb2RlbCRjb2VmZmljaWVudHMKCmFyZWFfdmFyaWFuY2VfZWZmZWN0IDwtIGZ1bmN0aW9uKGFyZWEpewogIGlfcG9seV9hcmVhIDwtIHByZWRpY3QobXVuaWNoX3JlbnRfcG9seV9hcmVhLCBhcmVhKQogIHZhcmlhbmNlX2JldGFbMl0gKiBpX3BvbHlfYXJlYVssIDFdICsgdmFyaWFuY2VfYmV0YVszXSAqIGlfcG9seV9hcmVhWywgMl0gKyB2YXJpYW5jZV9iZXRhWzRdICogaV9wb2x5X2FyZWFbLCAzXQp9Cgp5ZWFyY192YXJpYW5jZV9lZmZlY3QgPC0gZnVuY3Rpb24oeWVhcmMpewogIHllYXJjX3BvbHkgPC0gcHJlZGljdChtdW5pY2hfcmVudF9wb2x5X3llYXJjLCB5ZWFyYykKICB2YXJpYW5jZV9iZXRhWzVdICogeWVhcmNfcG9seVssIDFdICsgdmFyaWFuY2VfYmV0YVs2XSAqIHllYXJjX3BvbHlbLCAyXSArIHZhcmlhbmNlX2JldGFbN10gKiB5ZWFyY19wb2x5WywgM10KfQoKcGxvdCgKICBtdW5pY2hfcmVudF9pbmRleCRhcmVhLAogIGxvZ19zcV9yZXNpZHVhbHMgLSB2YXJpYW5jZV9iZXRhWzFdIC0geWVhcmNfdmFyaWFuY2VfZWZmZWN0KG11bmljaF9yZW50X2luZGV4JHllYXJjKSwKICBtYWluID0gJ3Jlc2lkdWFsIHJlZ3Jlc3Npb246IGVmZmVjdCBvZiBhcmVhJywKICB4bGFiID0gJ2VmZmVjdCBvZiBhcmVhJywKICB5bGFiID0gJ2FyZWEgaW4gc3FtJwopCmxpbmVzKGFyZWFfc2VxLCBhcmVhX3ZhcmlhbmNlX2VmZmVjdChhcmVhX3NlcSksIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKcGxvdCgKICBtdW5pY2hfcmVudF9pbmRleCR5ZWFyYywKICBsb2dfc3FfcmVzaWR1YWxzIC0gdmFyaWFuY2VfYmV0YVsxXSAtIGFyZWFfdmFyaWFuY2VfZWZmZWN0KG11bmljaF9yZW50X2luZGV4JGFyZWEpLAogIG1haW4gPSAncmVzaWR1YWwgcmVncmVzc2lvbjogZWZmZWN0IG9mIHllYXIgb2YgY29uc3RydWN0aW9uJywKICB4bGFiID0gJ2VmZmVjdCBvZiB5ZWFyIG9mIGNvbnN0cnVjdGlvbicsCiAgeWxhYiA9ICd5ZWFyIG9mIGNvbnN0cnVjdGlvbicKKQpsaW5lcyh5ZWFyY19zZXEsIHllYXJjX3ZhcmlhbmNlX2VmZmVjdCh5ZWFyY19zZXEpLCBjb2wgPSAncmVkJywgbHdkID0gMikKYGBgCgojIyMgVGhpcmQgc3RlcDogV2VpZ2h0ZWQgbGluZWFyIG1vZGVsCgpXZSBjYWxjdWxhdGUgdGhlIFdlaWdodDoKCmBgYHtyfQp3ZWlnaHQgPC0gMSAvIGV4cCh2YXJpYW5jZV9tb2RlbCRmaXR0ZWQudmFsdWVzKQpoZWFkKHdlaWdodCkKYGBgCgpBbmQgbm93IHRoZSBhZGp1c3RtZW50OgoKYGBge3J9CndlaWdodGVkX2xpbmVhcl9tb2RlbCA8LSBsbSgKICByZW50IH4gYXJlYSArIHllYXJjbyArIHllYXJjbzIgKyB5ZWFyY28zLAogIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCwKICB3ZWlnaHRzID0gd2VpZ2h0CikKc3VtbWFyeSh3ZWlnaHRlZF9saW5lYXJfbW9kZWwpCmBgYAoKQW5kIHdlIGNvbXBhcmUgdGhlIGNvdmFyaWFuY2UgbWF0cml4IG9mIGJvdGggbW9kZWw6CgpgYGB7cn0KdmNvdihtdW5pY2hfcmVncmVzc2lvbikKdmNvdih3ZWlnaHRlZF9saW5lYXJfbW9kZWwpCmBgYAoKQW5kIHRoZSBjb25maWRlbmNlIGludGVydmFsczoKCmBgYHtyfQpsaW5lYXJfY29uZmludCA8LSBjb25maW50KG11bmljaF9yZWdyZXNzaW9uKQpsaW5lYXJfY29uZmludAp3ZWlnaHRlZF9jb25maW50IDwtIGNvbmZpbnQod2VpZ2h0ZWRfbGluZWFyX21vZGVsKQp3ZWlnaHRlZF9jb25maW50CmBgYAoKIyMgRXhhbXBsZSA0LjQgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIFdoaXRlLUVzdGltYXRvcgoKV2UgdXNlIFIgdG8gb2J0YWluIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBhbmQgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzOgoKYGBge3J9CmxpYnJhcnkoc2FuZHdpY2gpCnJvYnVzdF9zZSA8LSBzcXJ0KGRpYWcodmNvdkhDKG11bmljaF9yZWdyZXNzaW9uLCB0eXBlID0gIkhDMSIpKSkKY29lZnMgPC0gbXVuaWNoX3JlZ3Jlc3Npb24kY29lZmZpY2llbnRzCnogPC0gcW5vcm0oMSAtIDAuMDUgLyAyKQpsb3dlciA8LSBjb2VmcyAtIHogKiByb2J1c3Rfc2UKdXBwZXIgPC0gY29lZnMgKyB6ICogcm9idXN0X3NlCmNvbmZpbnRfcm9idXN0IDwtIGRhdGEuZnJhbWUobG93ZXIsIHVwcGVyKQpjb2xuYW1lcyhjb25maW50X3JvYnVzdCkgPC0gY29sbmFtZXMobGluZWFyX2NvbmZpbnQpCmNvbmZpbnRfcm9idXN0CmBgYAoKIyMgRXhhbXBsZSA0LjUgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgV2VpZ2h0ZWQgUmVncmVzc2lvbgo=