Introduction

Our aim is to predict house values. Before we begin to do any analysis, we should always check whether the dataset has missing value or not, we do so by typing:

taiwan_real_estate <- read.csv("real_estates.csv",row.names=1)
attach(taiwan_real_estate)
any(is.na(taiwan_real_estate))
## [1] FALSE

Let’s take a look at structure of the data set:

glimpse(taiwan_real_estate)
## Rows: 414
## Columns: 6
## $ house.age                           <dbl> 32.0, 19.5, 13.3, 13.3, 5.0, 7.1, …
## $ distance.to.the.nearest.MRT.station <dbl> 84.87882, 306.59470, 561.98450, 56…
## $ number.of.convenience.stores        <int> 10, 9, 5, 5, 5, 3, 7, 6, 1, 3, 1, …
## $ latitude                            <dbl> 24.98298, 24.98034, 24.98746, 24.9…
## $ longitude                           <dbl> 121.5402, 121.5395, 121.5439, 121.…
## $ house.price.of.unit.area            <dbl> 37.9, 42.2, 47.3, 54.8, 43.1, 32.1…

Let’s simplify variables’ names:

taiwan_real_estate <- taiwan_real_estate %>%
rename(house_age_years = house.age, price_twd_msq = house.price.of.unit.area,
       n_convenience = number.of.convenience.stores, 
       dist_to_mrt_m = distance.to.the.nearest.MRT.station)

We can also perform binning for “house_age_years”:

#perform binning with specific number of bins
taiwan_real_estate<-taiwan_real_estate %>% mutate(house_age_cat = cut(house_age_years, breaks=c(0,15,30,45),include.lowest = T,
                                                                        right = F))

Descriptive Statistics

Prepare a heatmap with correlation coefficients on it:

library(corrplot)
## corrplot 0.92 loaded
M<-cor(taiwan_real_estate[,1:6])
corrplot(M, method = 'number') # colorful number

Draw a scatter plot of n_convenience vs. price_twd_msq:

ggplot(taiwan_real_estate, aes(x = n_convenience, y = price_twd_msq)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "blue") +
  labs(title = "Scatter plot of n_convenience vs. price_twd_msq with Trend Line",
       x = "Number of Convenience Stores",
       y = "House Price of Unit Area")
## `geom_smooth()` using formula = 'y ~ x'

Draw a scatter plot of house_age_years vs. price_twd_msq:

ggplot(taiwan_real_estate, aes(x = house_age_years, y = price_twd_msq)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE, color = "blue") +
  labs(title = "Scatter plot of house_age_years vs. price_twd_msq with Trend Line",
       x = "House Age (years)",
       y = "House Price of Unit Area")
## `geom_smooth()` using formula = 'y ~ x'

Draw a scatter plot of distance to nearest MRT station vs. price_twd_msq:

Plot a histogram of price_twd_msq with 10 bins, facet the plot so each house age group gets its own panel:

ggplot(taiwan_real_estate, aes(x = price_twd_msq)) +
  geom_histogram(bins = 10, fill = "blue", color = "black") +
  facet_wrap(~house_age_cat) +
  labs(title = "Histogram of Price per Square Meter Faceted by House Age Group",
       x = "House Price of Unit Area",
       y = "Frequency")

Summarize to calculate the mean, sd, median etc. house price/area by house age:

summary_stats <- taiwan_real_estate %>%
  group_by(house_age_cat) %>%
  summarise(
    mean_price = mean(price_twd_msq, na.rm = TRUE),
    sd_price = sd(price_twd_msq, na.rm = TRUE),
    median_price = median(price_twd_msq, na.rm = TRUE),
    min_price = min(price_twd_msq, na.rm = TRUE),
    max_price = max(price_twd_msq, na.rm = TRUE),
    count = n()
  )
print(summary_stats)
## # A tibble: 3 × 7
##   house_age_cat mean_price sd_price median_price min_price max_price count
##   <fct>              <dbl>    <dbl>        <dbl>     <dbl>     <dbl> <int>
## 1 [0,15)              41.8     14.2         42.6       7.6     118.    190
## 2 [15,30)             32.6     11.4         32.9      11.2      59.6   129
## 3 [30,45]             37.7     12.8         38.3      12.2      78.3    95

Simple model

Run a linear regression of price_twd_msq vs. best, but only 1 predictor (We will compare 2 of them for analysis):

model_n_convenience <- lm(price_twd_msq ~ n_convenience, data = taiwan_real_estate)
summary_n_convenience <- summary(model_n_convenience)

model_dist_to_mrt_m <- lm(price_twd_msq ~ dist_to_mrt_m, data = taiwan_real_estate)
summary_dist_to_mrt_m <- summary(model_dist_to_mrt_m)

comparison <- data.frame(
  Model = c("n_convenience", "dist_to_mrt_m"),
  Coefficient = c(summary_n_convenience$coefficients[2, 1], summary_dist_to_mrt_m$coefficients[2, 1]),
  Std_Error = c(summary_n_convenience$coefficients[2, 2], summary_dist_to_mrt_m$coefficients[2, 2]),
  t_value = c(summary_n_convenience$coefficients[2, 3], summary_dist_to_mrt_m$coefficients[2, 3]),
  R_squared = c(summary_n_convenience$r.squared, summary_dist_to_mrt_m$r.squared),
  Adj_R_squared = c(summary_n_convenience$adj.r.squared, summary_dist_to_mrt_m$adj.r.squared)
)

print(comparison) # The best will be dist_to_mrt_m
##           Model  Coefficient    Std_Error   t_value R_squared Adj_R_squared
## 1 n_convenience  2.637653463 0.1868289689  14.11801 0.3260466     0.3244108
## 2 dist_to_mrt_m -0.007262052 0.0003925495 -18.49971 0.4537543     0.4524284

We start by displaying the statistical summary of the model using the R function summary():

summary(model_dist_to_mrt_m) # Printing that table
## 
## Call:
## lm(formula = price_twd_msq ~ dist_to_mrt_m, data = taiwan_real_estate)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -35.396  -6.007  -1.195   4.831  73.483 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   45.8514271  0.6526105   70.26   <2e-16 ***
## dist_to_mrt_m -0.0072621  0.0003925  -18.50   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 10.07 on 412 degrees of freedom
## Multiple R-squared:  0.4538, Adjusted R-squared:  0.4524 
## F-statistic: 342.2 on 1 and 412 DF,  p-value: < 2.2e-16

You can access lots of different aspects of the regression object. To see what’s inside, use names():

names(model_dist_to_mrt_m)
##  [1] "coefficients"  "residuals"     "effects"       "rank"         
##  [5] "fitted.values" "assign"        "qr"            "df.residual"  
##  [9] "xlevels"       "call"          "terms"         "model"

Inside the model we can see that there are the necessary elements within the model to analyze and interpret the regression results.

Considering the summary results above and based on the R-squared value (0.4538), the dist_to_mrt_m predictor model explains about 45.38% of the variation in price per square meter, indicating moderate prediction accuracy.

Model diagnostics:

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

Let’s briefly go through each chart from top to bottom and left to right to describe shorthand what we see.

Residuals vs Fitted: we see that almost most points are scattered around the line, indicating possible nonlinearity or heteroscedasticity.

Q-Q Residuals: we can easily notice that the beginning and the end have deviations from the diagonal line, indicating non-normality of the residuals distribution.

Scale-Location: in this graph, you can see that the points are scattered quite randomly, although there are some right on the line. Despite these some points, we can say that we have a constant variance.

Residuals vs Leverage: here we can see a few points with high leverage and large residuals, in particular point 271, which is a significant outlier and has a large impact on the model. Such points can highly distort the model results and require attention.

To summarize, the graphs show possible problems with heteroskedasticity and non-normality of residuals, as well as indicate the presence of outliers that may affect the model.

Create the diagnostic plots using ggfortify:

library(ggfortify)
autoplot(model_dist_to_mrt_m)

Outliers and high levarage points:

plot(model_dist_to_mrt_m, 5)

Influential values:

# Cook's distance
plot(model_dist_to_mrt_m, 4)

or just plot all of diagnostic plots together:

autoplot(model_dist_to_mrt_m, which = 1:6, label.size = 3)

Discussion:

Cook’s Distance: We can see in the graph that points 271, 250, and 149 have the highest Cook’s distance value, indicating that they have a significant impact on model fit.

Cook’s Distance vs Leverage: Again, points 271, 149 and 250 stand out as having high values of leverage and Cook’s distance, confirming their significant influence on the model.

It is noticeable in the graphs that points 271, 250 and 149 have high values of Cook’s distance and leverage, indicating their significant influence on the model.

Multiple Regression Model

Test and training set

We begin by splitting the dataset into two parts, training set and testing set. In this example we will randomly take 75% row in this dataset and put it into the training set, and other 25% row in the testing set:

smp_size<-floor(0.75*nrow(taiwan_real_estate))
set.seed(12)
train_ind<-sample(seq_len(nrow(taiwan_real_estate)), size=smp_size)
train<-taiwan_real_estate[train_ind, ]
test<-taiwan_real_estate[-train_ind, ]
  • 1st comment: floor() is used to return the largest integer value which is not greater than an individual number, or expression.

  • 2nd comment: set.seed() is used to set the seed of R’s random number generator, this function is used so results from this example can be recreated easily.

Now we have our training set and testing set.

Variable selection methods

Generally, selecting variables for linear regression is a debatable topic.

There are many methods for variable selecting, namely, forward stepwise selection, backward stepwise selection, etc, some are valid, some are heavily criticized.

I recommend this document: https://www.stat.cmu.edu/~cshalizi/mreg/15/lectures/26/lecture-26.pdf and Gung’s comment: https://stats.stackexchange.com/questions/20836/algorithms-for-automatic-model-selection/20856#20856 if you want to learn more about variable selection process.

If our goal is prediction, it is safer to include all predictors in our model, removing variables without knowing the science behind it usually does more harm than good!!!

We begin to create our multiple linear regression model:

model2 <- lm(price_twd_msq ~ ., data = train)
summary(model2)
## 
## Call:
## lm(formula = price_twd_msq ~ ., data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -34.009  -4.953  -1.296   4.461  75.042 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -2.861e+03  7.542e+03  -0.379 0.704717    
## house_age_years      -4.209e-01  1.223e-01  -3.442 0.000659 ***
## dist_to_mrt_m        -4.558e-03  8.681e-04  -5.251 2.86e-07 ***
## n_convenience         9.826e-01  2.287e-01   4.297 2.34e-05 ***
## latitude              2.505e+02  5.375e+01   4.660 4.74e-06 ***
## longitude            -2.755e+01  6.032e+01  -0.457 0.648221    
## house_age_cat[15,30) -1.089e+00  1.916e+00  -0.568 0.570124    
## house_age_cat[30,45]  5.789e+00  3.577e+00   1.618 0.106655    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.331 on 302 degrees of freedom
## Multiple R-squared:  0.5676, Adjusted R-squared:  0.5575 
## F-statistic: 56.62 on 7 and 302 DF,  p-value: < 2.2e-16

We can observe from the important aspects that our model explains about 56.76% of the variation of price_twd_msq. The influence of house_age_years, dist_to_mrt_m, n_convenience and latitude variables are significant while the influence of longitude variables and house age categories are insignificant. The standard error of the residuals shows the average deviation of the predicted values from the actual values.

Looking at model summary, we see that variables Intercept, longitude, house_age_cat[15,30) and house_age_cat[30,45] are insignificant, so let’s estimate the model without those variables:

model3 <- lm(price_twd_msq ~ house_age_years + dist_to_mrt_m + n_convenience + latitude, data = train)
summary(model3)
## 
## Call:
## lm(formula = price_twd_msq ~ house_age_years + dist_to_mrt_m + 
##     n_convenience + latitude, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -34.782  -5.448  -1.616   4.269  75.310 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)     -6.079e+03  1.356e+03  -4.481 1.05e-05 ***
## house_age_years -2.546e-01  4.762e-02  -5.347 1.76e-07 ***
## dist_to_mrt_m   -4.692e-03  5.988e-04  -7.837 7.78e-14 ***
## n_convenience    1.006e+00  2.303e-01   4.367 1.73e-05 ***
## latitude         2.452e+02  5.432e+01   4.514 9.10e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.459 on 305 degrees of freedom
## Multiple R-squared:  0.5513, Adjusted R-squared:  0.5454 
## F-statistic: 93.67 on 4 and 305 DF,  p-value: < 2.2e-16

Evaluating multi-collinearity

There are many standards researchers apply for deciding whether a VIF is too large. In some domains, a VIF over 2 is worthy of suspicion. Others set the bar higher, at 5 or 10. Others still will say you shouldn’t pay attention to these at all. Ultimately, the main thing to consider is that small effects are more likely to be “drowned out” by higher VIFs, but this may just be a natural, unavoidable fact with your model.

## house_age_years   dist_to_mrt_m   n_convenience        latitude 
##        1.021076        1.965314        1.623361        1.513424
## Variables with VIF > 5 may indicate multicollinearity issues.

Finally we test our model on test dataset:

## Mean Absolute Error (MAE):  5.752221
## Mean Squared Error (MSE):  54.56405
## Root Mean Squared Error (RMSE):  7.386748

We can observe that the model tells us relatively accurately about prices and their approximate changes, but there are some outliers, especially for low price values, where the predicted values differ significantly from the actual values. Overall, we see a positive correlation between the predicted and actual values, indicating that the model is capturing the underlying trend in the data quite well.

We have acceptable prediction accuracy, however, there are outliers that indicate the need to further improve our model.

Variable selection using best subset regression

Best subset and stepwise (forward, backward, both) techniques of variable selection can be used to come up with the best linear regression model for the dependent variable medv.

## Subset selection object
## Call: regsubsets.formula(price_twd_msq ~ ., data = train, nvmax = NULL)
## 7 Variables  (and intercept)
##                      Forced in Forced out
## house_age_years          FALSE      FALSE
## dist_to_mrt_m            FALSE      FALSE
## n_convenience            FALSE      FALSE
## latitude                 FALSE      FALSE
## longitude                FALSE      FALSE
## house_age_cat[15,30)     FALSE      FALSE
## house_age_cat[30,45]     FALSE      FALSE
## 1 subsets of each size up to 7
## Selection Algorithm: exhaustive
##          house_age_years dist_to_mrt_m n_convenience latitude longitude
## 1  ( 1 ) " "             "*"           " "           " "      " "      
## 2  ( 1 ) " "             "*"           "*"           " "      " "      
## 3  ( 1 ) "*"             "*"           " "           "*"      " "      
## 4  ( 1 ) "*"             "*"           "*"           "*"      " "      
## 5  ( 1 ) "*"             "*"           "*"           "*"      " "      
## 6  ( 1 ) "*"             "*"           "*"           "*"      " "      
## 7  ( 1 ) "*"             "*"           "*"           "*"      "*"      
##          house_age_cat[15,30) house_age_cat[30,45]
## 1  ( 1 ) " "                  " "                 
## 2  ( 1 ) " "                  " "                 
## 3  ( 1 ) " "                  " "                 
## 4  ( 1 ) " "                  " "                 
## 5  ( 1 ) " "                  "*"                 
## 6  ( 1 ) "*"                  "*"                 
## 7  ( 1 ) "*"                  "*"

## Start:  AIC=1638.47
## price_twd_msq ~ 1
## 
##                   Df Sum of Sq   RSS    AIC
## + dist_to_mrt_m    1   27754.0 33057 1451.5
## + n_convenience    1   18298.4 42512 1529.5
## + longitude        1   17543.7 43267 1535.0
## + latitude         1   16685.2 44125 1541.0
## + house_age_cat    2    5118.7 55692 1615.2
## + house_age_years  1    2037.6 58773 1629.9
## <none>                         60811 1638.5
## 
## Step:  AIC=1451.52
## price_twd_msq ~ dist_to_mrt_m
## 
##                   Df Sum of Sq   RSS    AIC
## + n_convenience    1    1821.6 31235 1435.9
## + latitude         1    1793.8 31263 1436.2
## + house_age_years  1    1773.7 31283 1436.4
## + house_age_cat    2    1666.5 31390 1439.5
## <none>                         33057 1451.5
## + longitude        1      27.3 33029 1453.3
## 
## Step:  AIC=1435.94
## price_twd_msq ~ dist_to_mrt_m + n_convenience
## 
##                   Df Sum of Sq   RSS    AIC
## + house_age_years  1    2123.9 29111 1416.1
## + house_age_cat    2    2113.2 29122 1418.2
## + latitude         1    1388.9 29846 1423.8
## <none>                         31235 1435.9
## + longitude        1       6.3 31229 1437.9
## 
## Step:  AIC=1416.11
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years
## 
##                 Df Sum of Sq   RSS    AIC
## + latitude       1   1822.97 27288 1398.1
## + house_age_cat  2    871.12 28240 1410.7
## <none>                       29111 1416.1
## + longitude      1     32.98 29078 1417.8
## 
## Step:  AIC=1398.07
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years + 
##     latitude
## 
##                 Df Sum of Sq   RSS    AIC
## + house_age_cat  2    972.81 26315 1390.8
## <none>                       27288 1398.1
## + longitude      1      8.02 27280 1400.0
## 
## Step:  AIC=1390.81
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years + 
##     latitude + house_age_cat
## 
##             Df Sum of Sq   RSS    AIC
## <none>                   26315 1390.8
## + longitude  1    18.161 26297 1392.6
## Start:  AIC=1392.6
## price_twd_msq ~ house_age_years + dist_to_mrt_m + n_convenience + 
##     latitude + longitude + house_age_cat
## 
##                   Df Sum of Sq   RSS    AIC
## - longitude        1     18.16 26315 1390.8
## <none>                         26297 1392.6
## - house_age_cat    2    982.95 27280 1400.0
## - house_age_years  1   1031.51 27329 1402.5
## - n_convenience    1   1607.54 27905 1409.0
## - latitude         1   1891.31 28189 1412.1
## - dist_to_mrt_m    1   2400.71 28698 1417.7
## 
## Step:  AIC=1390.81
## price_twd_msq ~ house_age_years + dist_to_mrt_m + n_convenience + 
##     latitude + house_age_cat
## 
##                   Df Sum of Sq   RSS    AIC
## <none>                         26315 1390.8
## - house_age_cat    2     972.8 27288 1398.1
## - house_age_years  1    1035.7 27351 1400.8
## - n_convenience    1    1625.4 27941 1407.4
## - latitude         1    1924.7 28240 1410.7
## - dist_to_mrt_m    1    4348.8 30664 1436.2
## Start:  AIC=1638.47
## price_twd_msq ~ 1
## 
##                   Df Sum of Sq   RSS    AIC
## + dist_to_mrt_m    1   27754.0 33057 1451.5
## + n_convenience    1   18298.4 42512 1529.5
## + longitude        1   17543.7 43267 1535.0
## + latitude         1   16685.2 44125 1541.0
## + house_age_cat    2    5118.7 55692 1615.2
## + house_age_years  1    2037.6 58773 1629.9
## <none>                         60811 1638.5
## 
## Step:  AIC=1451.52
## price_twd_msq ~ dist_to_mrt_m
## 
##                   Df Sum of Sq   RSS    AIC
## + n_convenience    1    1821.6 31235 1435.9
## + latitude         1    1793.8 31263 1436.2
## + house_age_years  1    1773.7 31283 1436.4
## + house_age_cat    2    1666.5 31390 1439.5
## <none>                         33057 1451.5
## + longitude        1      27.3 33029 1453.3
## - dist_to_mrt_m    1   27754.0 60811 1638.5
## 
## Step:  AIC=1435.94
## price_twd_msq ~ dist_to_mrt_m + n_convenience
## 
##                   Df Sum of Sq   RSS    AIC
## + house_age_years  1    2123.9 29111 1416.1
## + house_age_cat    2    2113.2 29122 1418.2
## + latitude         1    1388.9 29846 1423.8
## <none>                         31235 1435.9
## + longitude        1       6.3 31229 1437.9
## - n_convenience    1    1821.6 33057 1451.5
## - dist_to_mrt_m    1   11277.2 42512 1529.5
## 
## Step:  AIC=1416.11
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years
## 
##                   Df Sum of Sq   RSS    AIC
## + latitude         1    1823.0 27288 1398.1
## + house_age_cat    2     871.1 28240 1410.7
## <none>                         29111 1416.1
## + longitude        1      33.0 29078 1417.8
## - house_age_years  1    2123.9 31235 1435.9
## - n_convenience    1    2171.8 31283 1436.4
## - dist_to_mrt_m    1   10564.4 39676 1510.1
## 
## Step:  AIC=1398.07
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years + 
##     latitude
## 
##                   Df Sum of Sq   RSS    AIC
## + house_age_cat    2     972.8 26315 1390.8
## <none>                         27288 1398.1
## + longitude        1       8.0 27280 1400.0
## - n_convenience    1    1706.1 28994 1414.9
## - latitude         1    1823.0 29111 1416.1
## - house_age_years  1    2557.9 29846 1423.8
## - dist_to_mrt_m    1    5494.4 32783 1452.9
## 
## Step:  AIC=1390.81
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years + 
##     latitude + house_age_cat
## 
##                   Df Sum of Sq   RSS    AIC
## <none>                         26315 1390.8
## + longitude        1      18.2 26297 1392.6
## - house_age_cat    2     972.8 27288 1398.1
## - house_age_years  1    1035.7 27351 1400.8
## - n_convenience    1    1625.4 27941 1407.4
## - latitude         1    1924.7 28240 1410.7
## - dist_to_mrt_m    1    4348.8 30664 1436.2
## 
## Call:
## lm(formula = price_twd_msq ~ dist_to_mrt_m + n_convenience + 
##     house_age_years + latitude + house_age_cat, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -33.913  -5.098  -1.227   4.551  75.387 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -6.250e+03  1.337e+03  -4.673 4.46e-06 ***
## dist_to_mrt_m        -4.274e-03  6.040e-04  -7.076 1.03e-11 ***
## n_convenience         9.871e-01  2.282e-01   4.326 2.06e-05 ***
## house_age_years      -4.217e-01  1.221e-01  -3.453 0.000633 ***
## latitude              2.521e+02  5.356e+01   4.708 3.82e-06 ***
## house_age_cat[15,30) -1.024e+00  1.909e+00  -0.536 0.592043    
## house_age_cat[30,45]  5.851e+00  3.570e+00   1.639 0.102303    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.319 on 303 degrees of freedom
## Multiple R-squared:  0.5673, Adjusted R-squared:  0.5587 
## F-statistic:  66.2 on 6 and 303 DF,  p-value: < 2.2e-16
## 
## Call:
## lm(formula = price_twd_msq ~ house_age_years + dist_to_mrt_m + 
##     n_convenience + latitude + house_age_cat, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -33.913  -5.098  -1.227   4.551  75.387 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -6.250e+03  1.337e+03  -4.673 4.46e-06 ***
## house_age_years      -4.217e-01  1.221e-01  -3.453 0.000633 ***
## dist_to_mrt_m        -4.274e-03  6.040e-04  -7.076 1.03e-11 ***
## n_convenience         9.871e-01  2.282e-01   4.326 2.06e-05 ***
## latitude              2.521e+02  5.356e+01   4.708 3.82e-06 ***
## house_age_cat[15,30) -1.024e+00  1.909e+00  -0.536 0.592043    
## house_age_cat[30,45]  5.851e+00  3.570e+00   1.639 0.102303    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.319 on 303 degrees of freedom
## Multiple R-squared:  0.5673, Adjusted R-squared:  0.5587 
## F-statistic:  66.2 on 6 and 303 DF,  p-value: < 2.2e-16
## 
## Call:
## lm(formula = price_twd_msq ~ dist_to_mrt_m + n_convenience + 
##     house_age_years + latitude + house_age_cat, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -33.913  -5.098  -1.227   4.551  75.387 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -6.250e+03  1.337e+03  -4.673 4.46e-06 ***
## dist_to_mrt_m        -4.274e-03  6.040e-04  -7.076 1.03e-11 ***
## n_convenience         9.871e-01  2.282e-01   4.326 2.06e-05 ***
## house_age_years      -4.217e-01  1.221e-01  -3.453 0.000633 ***
## latitude              2.521e+02  5.356e+01   4.708 3.82e-06 ***
## house_age_cat[15,30) -1.024e+00  1.909e+00  -0.536 0.592043    
## house_age_cat[30,45]  5.851e+00  3.570e+00   1.639 0.102303    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.319 on 303 degrees of freedom
## Multiple R-squared:  0.5673, Adjusted R-squared:  0.5587 
## F-statistic:  66.2 on 6 and 303 DF,  p-value: < 2.2e-16

Variable selection using stepwise regression

## Start:  AIC=1638.47
## price_twd_msq ~ 1
## 
##                   Df Sum of Sq   RSS    AIC
## + dist_to_mrt_m    1   27754.0 33057 1451.5
## + n_convenience    1   18298.4 42512 1529.5
## + longitude        1   17543.7 43267 1535.0
## + latitude         1   16685.2 44125 1541.0
## + house_age_cat    2    5118.7 55692 1615.2
## + house_age_years  1    2037.6 58773 1629.9
## <none>                         60811 1638.5
## 
## Step:  AIC=1451.52
## price_twd_msq ~ dist_to_mrt_m
## 
##                   Df Sum of Sq   RSS    AIC
## + n_convenience    1    1821.6 31235 1435.9
## + latitude         1    1793.8 31263 1436.2
## + house_age_years  1    1773.7 31283 1436.4
## + house_age_cat    2    1666.5 31390 1439.5
## <none>                         33057 1451.5
## + longitude        1      27.3 33029 1453.3
## 
## Step:  AIC=1435.94
## price_twd_msq ~ dist_to_mrt_m + n_convenience
## 
##                   Df Sum of Sq   RSS    AIC
## + house_age_years  1    2123.9 29111 1416.1
## + house_age_cat    2    2113.2 29122 1418.2
## + latitude         1    1388.9 29846 1423.8
## <none>                         31235 1435.9
## + longitude        1       6.3 31229 1437.9
## 
## Step:  AIC=1416.11
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years
## 
##                 Df Sum of Sq   RSS    AIC
## + latitude       1   1822.97 27288 1398.1
## + house_age_cat  2    871.12 28240 1410.7
## <none>                       29111 1416.1
## + longitude      1     32.98 29078 1417.8
## 
## Step:  AIC=1398.07
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years + 
##     latitude
## 
##                 Df Sum of Sq   RSS    AIC
## + house_age_cat  2    972.81 26315 1390.8
## <none>                       27288 1398.1
## + longitude      1      8.02 27280 1400.0
## 
## Step:  AIC=1390.81
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years + 
##     latitude + house_age_cat
## 
##             Df Sum of Sq   RSS    AIC
## <none>                   26315 1390.8
## + longitude  1    18.161 26297 1392.6
## 
## Call:
## lm(formula = price_twd_msq ~ dist_to_mrt_m + n_convenience + 
##     house_age_years + latitude + house_age_cat, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -33.913  -5.098  -1.227   4.551  75.387 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -6.250e+03  1.337e+03  -4.673 4.46e-06 ***
## dist_to_mrt_m        -4.274e-03  6.040e-04  -7.076 1.03e-11 ***
## n_convenience         9.871e-01  2.282e-01   4.326 2.06e-05 ***
## house_age_years      -4.217e-01  1.221e-01  -3.453 0.000633 ***
## latitude              2.521e+02  5.356e+01   4.708 3.82e-06 ***
## house_age_cat[15,30) -1.024e+00  1.909e+00  -0.536 0.592043    
## house_age_cat[30,45]  5.851e+00  3.570e+00   1.639 0.102303    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.319 on 303 degrees of freedom
## Multiple R-squared:  0.5673, Adjusted R-squared:  0.5587 
## F-statistic:  66.2 on 6 and 303 DF,  p-value: < 2.2e-16
## Start:  AIC=1392.6
## price_twd_msq ~ house_age_years + dist_to_mrt_m + n_convenience + 
##     latitude + longitude + house_age_cat
## 
##                   Df Sum of Sq   RSS    AIC
## - longitude        1     18.16 26315 1390.8
## <none>                         26297 1392.6
## - house_age_cat    2    982.95 27280 1400.0
## - house_age_years  1   1031.51 27329 1402.5
## - n_convenience    1   1607.54 27905 1409.0
## - latitude         1   1891.31 28189 1412.1
## - dist_to_mrt_m    1   2400.71 28698 1417.7
## 
## Step:  AIC=1390.81
## price_twd_msq ~ house_age_years + dist_to_mrt_m + n_convenience + 
##     latitude + house_age_cat
## 
##                   Df Sum of Sq   RSS    AIC
## <none>                         26315 1390.8
## - house_age_cat    2     972.8 27288 1398.1
## - house_age_years  1    1035.7 27351 1400.8
## - n_convenience    1    1625.4 27941 1407.4
## - latitude         1    1924.7 28240 1410.7
## - dist_to_mrt_m    1    4348.8 30664 1436.2
## 
## Call:
## lm(formula = price_twd_msq ~ house_age_years + dist_to_mrt_m + 
##     n_convenience + latitude + house_age_cat, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -33.913  -5.098  -1.227   4.551  75.387 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -6.250e+03  1.337e+03  -4.673 4.46e-06 ***
## house_age_years      -4.217e-01  1.221e-01  -3.453 0.000633 ***
## dist_to_mrt_m        -4.274e-03  6.040e-04  -7.076 1.03e-11 ***
## n_convenience         9.871e-01  2.282e-01   4.326 2.06e-05 ***
## latitude              2.521e+02  5.356e+01   4.708 3.82e-06 ***
## house_age_cat[15,30) -1.024e+00  1.909e+00  -0.536 0.592043    
## house_age_cat[30,45]  5.851e+00  3.570e+00   1.639 0.102303    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.319 on 303 degrees of freedom
## Multiple R-squared:  0.5673, Adjusted R-squared:  0.5587 
## F-statistic:  66.2 on 6 and 303 DF,  p-value: < 2.2e-16
## Start:  AIC=1638.47
## price_twd_msq ~ 1
## 
##                   Df Sum of Sq   RSS    AIC
## + dist_to_mrt_m    1   27754.0 33057 1451.5
## + n_convenience    1   18298.4 42512 1529.5
## + longitude        1   17543.7 43267 1535.0
## + latitude         1   16685.2 44125 1541.0
## + house_age_cat    2    5118.7 55692 1615.2
## + house_age_years  1    2037.6 58773 1629.9
## <none>                         60811 1638.5
## 
## Step:  AIC=1451.52
## price_twd_msq ~ dist_to_mrt_m
## 
##                   Df Sum of Sq   RSS    AIC
## + n_convenience    1    1821.6 31235 1435.9
## + latitude         1    1793.8 31263 1436.2
## + house_age_years  1    1773.7 31283 1436.4
## + house_age_cat    2    1666.5 31390 1439.5
## <none>                         33057 1451.5
## + longitude        1      27.3 33029 1453.3
## - dist_to_mrt_m    1   27754.0 60811 1638.5
## 
## Step:  AIC=1435.94
## price_twd_msq ~ dist_to_mrt_m + n_convenience
## 
##                   Df Sum of Sq   RSS    AIC
## + house_age_years  1    2123.9 29111 1416.1
## + house_age_cat    2    2113.2 29122 1418.2
## + latitude         1    1388.9 29846 1423.8
## <none>                         31235 1435.9
## + longitude        1       6.3 31229 1437.9
## - n_convenience    1    1821.6 33057 1451.5
## - dist_to_mrt_m    1   11277.2 42512 1529.5
## 
## Step:  AIC=1416.11
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years
## 
##                   Df Sum of Sq   RSS    AIC
## + latitude         1    1823.0 27288 1398.1
## + house_age_cat    2     871.1 28240 1410.7
## <none>                         29111 1416.1
## + longitude        1      33.0 29078 1417.8
## - house_age_years  1    2123.9 31235 1435.9
## - n_convenience    1    2171.8 31283 1436.4
## - dist_to_mrt_m    1   10564.4 39676 1510.1
## 
## Step:  AIC=1398.07
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years + 
##     latitude
## 
##                   Df Sum of Sq   RSS    AIC
## + house_age_cat    2     972.8 26315 1390.8
## <none>                         27288 1398.1
## + longitude        1       8.0 27280 1400.0
## - n_convenience    1    1706.1 28994 1414.9
## - latitude         1    1823.0 29111 1416.1
## - house_age_years  1    2557.9 29846 1423.8
## - dist_to_mrt_m    1    5494.4 32783 1452.9
## 
## Step:  AIC=1390.81
## price_twd_msq ~ dist_to_mrt_m + n_convenience + house_age_years + 
##     latitude + house_age_cat
## 
##                   Df Sum of Sq   RSS    AIC
## <none>                         26315 1390.8
## + longitude        1      18.2 26297 1392.6
## - house_age_cat    2     972.8 27288 1398.1
## - house_age_years  1    1035.7 27351 1400.8
## - n_convenience    1    1625.4 27941 1407.4
## - latitude         1    1924.7 28240 1410.7
## - dist_to_mrt_m    1    4348.8 30664 1436.2

Comparing competing models

##                df      AIC
## model_forward   8 2272.556
## model.backward  8 2272.556
## model.step      8 2272.556
## 
## Call:
## lm(formula = price_twd_msq ~ dist_to_mrt_m + n_convenience + 
##     house_age_years + latitude + house_age_cat, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -33.913  -5.098  -1.227   4.551  75.387 
## 
## Coefficients:
##                        Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          -6.250e+03  1.337e+03  -4.673 4.46e-06 ***
## dist_to_mrt_m        -4.274e-03  6.040e-04  -7.076 1.03e-11 ***
## n_convenience         9.871e-01  2.282e-01   4.326 2.06e-05 ***
## house_age_years      -4.217e-01  1.221e-01  -3.453 0.000633 ***
## latitude              2.521e+02  5.356e+01   4.708 3.82e-06 ***
## house_age_cat[15,30) -1.024e+00  1.909e+00  -0.536 0.592043    
## house_age_cat[30,45]  5.851e+00  3.570e+00   1.639 0.102303    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.319 on 303 degrees of freedom
## Multiple R-squared:  0.5673, Adjusted R-squared:  0.5587 
## F-statistic:  66.2 on 6 and 303 DF,  p-value: < 2.2e-16

From Best subset regression and stepwise selection (forward, backward, both), we see that all methods consistently selected the same model with the predictors dist_to_mrt_m, n_convenience, house_age_years, latitude, and house_age_cat[15,30), indicating their significant influence on the price per square meter.

Models are compared based on adjusted r square, AIC, BIC criteria for in-sample performance and mean square prediction error (MSPE) for out-of-sample performance.

Finally, we can check the Out-of-sample Prediction or test error (MSPE):

Cross Validation

Please check how function ?cv.glm works.

We will just extract from this object created by cv.glm command - the raw cross-validation estimate of prediction error.

## Linear Regression 
## 
## 310 samples
##   5 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 280, 281, 279, 279, 279, 278, ... 
## Resampling results:
## 
##   RMSE      Rsquared   MAE     
##   9.064416  0.5838067  6.474888
## 
## Tuning parameter 'intercept' was held constant at a value of TRUE
## Cross-validation estimate of prediction error:  82.16364
## Manual cross-validation estimate of prediction error:  89.47849
## Based on AIC criteria, the best model is Forward
## We need to check out-of-sample MSPE for both models. Based on out-of-sample prediction error, model Forward is the best.

To summarize, we can say that based on the AIC criterion and MSPE prediction error estimation, for both of them the forward selection model was chosen as the best model in our conditions.

These results are satisfactory and in line with our expectations, given the research and work done on the data.

LS0tDQp0aXRsZTogIkRlc2NyaXB0aXZlIFN0YXRpc3RpY3MiDQphdXRob3I6ICJNeWt5dGEgWmluY2hlbmtvIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBmb250c2l6ZTogMTBwdA0KICAgIHRvYzogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgZGZfcHJpbnQ6IGRlZmF1bHQNCiAgICB0b2NfZGVwdGg6IDUNCnN1YnRpdGxlOiBSZWdyZXNzaW9uIEFuYWx5c2lzDQplZGl0b3Jfb3B0aW9uczoNCiAgbWFya2Rvd246DQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShsZWFwcykNCmxpYnJhcnkoTUFTUykNCmxpYnJhcnkoYm9vdCkNCmxpYnJhcnkoY2FyZXQpDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpkb3dubG9hZC5maWxlKCJodHRwczovL2dpdGh1Yi5jb20va2ZsaXNpa293c2tpL2RzL2Jsb2IvbWFzdGVyL1JlYWwlMjBlc3RhdGUuY3N2P3Jhdz10cnVlIiwgZGVzdGZpbGUgPSJyZWFsX2VzdGF0ZXMuY3N2Iixtb2RlPSJ3YiIpDQpgYGANCg0KIyMgSW50cm9kdWN0aW9uDQoNCk91ciBhaW0gaXMgdG8gcHJlZGljdCBob3VzZSB2YWx1ZXMuIEJlZm9yZSB3ZSBiZWdpbiB0byBkbyBhbnkgYW5hbHlzaXMsIHdlIHNob3VsZCBhbHdheXMgY2hlY2sgd2hldGhlciB0aGUgZGF0YXNldCBoYXMgbWlzc2luZyB2YWx1ZSBvciBub3QsIHdlIGRvIHNvIGJ5IHR5cGluZzoNCg0KYGBge3IgZGF0YXNldCwgd2FybmluZz1GQUxTRX0NCnRhaXdhbl9yZWFsX2VzdGF0ZSA8LSByZWFkLmNzdigicmVhbF9lc3RhdGVzLmNzdiIscm93Lm5hbWVzPTEpDQphdHRhY2godGFpd2FuX3JlYWxfZXN0YXRlKQ0KYW55KGlzLm5hKHRhaXdhbl9yZWFsX2VzdGF0ZSkpDQpgYGANCg0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgc2V0Og0KDQpgYGB7ciBkYXRhc2V0Miwgd2FybmluZz1UUlVFfQ0KZ2xpbXBzZSh0YWl3YW5fcmVhbF9lc3RhdGUpDQpgYGANCg0KTGV0J3Mgc2ltcGxpZnkgdmFyaWFibGVzJyBuYW1lczoNCg0KYGBge3J9DQp0YWl3YW5fcmVhbF9lc3RhdGUgPC0gdGFpd2FuX3JlYWxfZXN0YXRlICU+JQ0KcmVuYW1lKGhvdXNlX2FnZV95ZWFycyA9IGhvdXNlLmFnZSwgcHJpY2VfdHdkX21zcSA9IGhvdXNlLnByaWNlLm9mLnVuaXQuYXJlYSwNCiAgICAgICBuX2NvbnZlbmllbmNlID0gbnVtYmVyLm9mLmNvbnZlbmllbmNlLnN0b3JlcywgDQogICAgICAgZGlzdF90b19tcnRfbSA9IGRpc3RhbmNlLnRvLnRoZS5uZWFyZXN0Lk1SVC5zdGF0aW9uKQ0KYGBgDQoNCldlIGNhbiBhbHNvIHBlcmZvcm0gYmlubmluZyBmb3IgImhvdXNlX2FnZV95ZWFycyI6DQoNCmBgYHtyfQ0KI3BlcmZvcm0gYmlubmluZyB3aXRoIHNwZWNpZmljIG51bWJlciBvZiBiaW5zDQp0YWl3YW5fcmVhbF9lc3RhdGU8LXRhaXdhbl9yZWFsX2VzdGF0ZSAlPiUgbXV0YXRlKGhvdXNlX2FnZV9jYXQgPSBjdXQoaG91c2VfYWdlX3llYXJzLCBicmVha3M9YygwLDE1LDMwLDQ1KSxpbmNsdWRlLmxvd2VzdCA9IFQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaWdodCA9IEYpKQ0KYGBgDQoNCiMjIERlc2NyaXB0aXZlIFN0YXRpc3RpY3MNCg0KUHJlcGFyZSBhIGhlYXRtYXAgd2l0aCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgb24gaXQ6DQoNCmBgYHtyfQ0KbGlicmFyeShjb3JycGxvdCkNCk08LWNvcih0YWl3YW5fcmVhbF9lc3RhdGVbLDE6Nl0pDQpjb3JycGxvdChNLCBtZXRob2QgPSAnbnVtYmVyJykgIyBjb2xvcmZ1bCBudW1iZXINCmBgYA0KDQoNCkRyYXcgYSBzY2F0dGVyIHBsb3Qgb2Ygbl9jb252ZW5pZW5jZSB2cy4gcHJpY2VfdHdkX21zcToNCg0KYGBge3J9DQpnZ3Bsb3QodGFpd2FuX3JlYWxfZXN0YXRlLCBhZXMoeCA9IG5fY29udmVuaWVuY2UsIHkgPSBwcmljZV90d2RfbXNxKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlNjYXR0ZXIgcGxvdCBvZiBuX2NvbnZlbmllbmNlIHZzLiBwcmljZV90d2RfbXNxIHdpdGggVHJlbmQgTGluZSIsDQogICAgICAgeCA9ICJOdW1iZXIgb2YgQ29udmVuaWVuY2UgU3RvcmVzIiwNCiAgICAgICB5ID0gIkhvdXNlIFByaWNlIG9mIFVuaXQgQXJlYSIpDQpgYGANCg0KRHJhdyBhIHNjYXR0ZXIgcGxvdCBvZiBob3VzZV9hZ2VfeWVhcnMgdnMuIHByaWNlX3R3ZF9tc3E6DQoNCmBgYHtyfQ0KZ2dwbG90KHRhaXdhbl9yZWFsX2VzdGF0ZSwgYWVzKHggPSBob3VzZV9hZ2VfeWVhcnMsIHkgPSBwcmljZV90d2RfbXNxKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlNjYXR0ZXIgcGxvdCBvZiBob3VzZV9hZ2VfeWVhcnMgdnMuIHByaWNlX3R3ZF9tc3Egd2l0aCBUcmVuZCBMaW5lIiwNCiAgICAgICB4ID0gIkhvdXNlIEFnZSAoeWVhcnMpIiwNCiAgICAgICB5ID0gIkhvdXNlIFByaWNlIG9mIFVuaXQgQXJlYSIpDQpgYGANCg0KRHJhdyBhIHNjYXR0ZXIgcGxvdCBvZiBkaXN0YW5jZSB0byBuZWFyZXN0IE1SVCBzdGF0aW9uIHZzLiBwcmljZV90d2RfbXNxOg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KHRhaXdhbl9yZWFsX2VzdGF0ZSwgYWVzKHk9cHJpY2VfdHdkX21zcSx4PWRpc3RfdG9fbXJ0X20pKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD1sbSxzZT1GQUxTRSkNCmBgYA0KDQpQbG90IGEgaGlzdG9ncmFtIG9mIHByaWNlX3R3ZF9tc3Egd2l0aCAxMCBiaW5zLCBmYWNldCB0aGUgcGxvdCBzbyBlYWNoIGhvdXNlIGFnZSBncm91cCBnZXRzIGl0cyBvd24gcGFuZWw6DQoNCmBgYHtyfQ0KZ2dwbG90KHRhaXdhbl9yZWFsX2VzdGF0ZSwgYWVzKHggPSBwcmljZV90d2RfbXNxKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAsIGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gImJsYWNrIikgKw0KICBmYWNldF93cmFwKH5ob3VzZV9hZ2VfY2F0KSArDQogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIFByaWNlIHBlciBTcXVhcmUgTWV0ZXIgRmFjZXRlZCBieSBIb3VzZSBBZ2UgR3JvdXAiLA0KICAgICAgIHggPSAiSG91c2UgUHJpY2Ugb2YgVW5pdCBBcmVhIiwNCiAgICAgICB5ID0gIkZyZXF1ZW5jeSIpDQpgYGANCg0KDQpTdW1tYXJpemUgdG8gY2FsY3VsYXRlIHRoZSBtZWFuLCBzZCwgbWVkaWFuIGV0Yy4gaG91c2UgcHJpY2UvYXJlYSBieSBob3VzZSBhZ2U6DQoNCmBgYHtyfQ0Kc3VtbWFyeV9zdGF0cyA8LSB0YWl3YW5fcmVhbF9lc3RhdGUgJT4lDQogIGdyb3VwX2J5KGhvdXNlX2FnZV9jYXQpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVhbl9wcmljZSA9IG1lYW4ocHJpY2VfdHdkX21zcSwgbmEucm0gPSBUUlVFKSwNCiAgICBzZF9wcmljZSA9IHNkKHByaWNlX3R3ZF9tc3EsIG5hLnJtID0gVFJVRSksDQogICAgbWVkaWFuX3ByaWNlID0gbWVkaWFuKHByaWNlX3R3ZF9tc3EsIG5hLnJtID0gVFJVRSksDQogICAgbWluX3ByaWNlID0gbWluKHByaWNlX3R3ZF9tc3EsIG5hLnJtID0gVFJVRSksDQogICAgbWF4X3ByaWNlID0gbWF4KHByaWNlX3R3ZF9tc3EsIG5hLnJtID0gVFJVRSksDQogICAgY291bnQgPSBuKCkNCiAgKQ0KcHJpbnQoc3VtbWFyeV9zdGF0cykNCmBgYA0KDQojIyBTaW1wbGUgbW9kZWwNCg0KUnVuIGEgbGluZWFyIHJlZ3Jlc3Npb24gb2YgcHJpY2VfdHdkX21zcSB2cy4gYmVzdCwgYnV0IG9ubHkgMSBwcmVkaWN0b3IgKFdlIHdpbGwgY29tcGFyZSAyIG9mIHRoZW0gZm9yIGFuYWx5c2lzKToNCg0KYGBge3J9DQptb2RlbF9uX2NvbnZlbmllbmNlIDwtIGxtKHByaWNlX3R3ZF9tc3EgfiBuX2NvbnZlbmllbmNlLCBkYXRhID0gdGFpd2FuX3JlYWxfZXN0YXRlKQ0Kc3VtbWFyeV9uX2NvbnZlbmllbmNlIDwtIHN1bW1hcnkobW9kZWxfbl9jb252ZW5pZW5jZSkNCg0KbW9kZWxfZGlzdF90b19tcnRfbSA8LSBsbShwcmljZV90d2RfbXNxIH4gZGlzdF90b19tcnRfbSwgZGF0YSA9IHRhaXdhbl9yZWFsX2VzdGF0ZSkNCnN1bW1hcnlfZGlzdF90b19tcnRfbSA8LSBzdW1tYXJ5KG1vZGVsX2Rpc3RfdG9fbXJ0X20pDQoNCmNvbXBhcmlzb24gPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJuX2NvbnZlbmllbmNlIiwgImRpc3RfdG9fbXJ0X20iKSwNCiAgQ29lZmZpY2llbnQgPSBjKHN1bW1hcnlfbl9jb252ZW5pZW5jZSRjb2VmZmljaWVudHNbMiwgMV0sIHN1bW1hcnlfZGlzdF90b19tcnRfbSRjb2VmZmljaWVudHNbMiwgMV0pLA0KICBTdGRfRXJyb3IgPSBjKHN1bW1hcnlfbl9jb252ZW5pZW5jZSRjb2VmZmljaWVudHNbMiwgMl0sIHN1bW1hcnlfZGlzdF90b19tcnRfbSRjb2VmZmljaWVudHNbMiwgMl0pLA0KICB0X3ZhbHVlID0gYyhzdW1tYXJ5X25fY29udmVuaWVuY2UkY29lZmZpY2llbnRzWzIsIDNdLCBzdW1tYXJ5X2Rpc3RfdG9fbXJ0X20kY29lZmZpY2llbnRzWzIsIDNdKSwNCiAgUl9zcXVhcmVkID0gYyhzdW1tYXJ5X25fY29udmVuaWVuY2Ukci5zcXVhcmVkLCBzdW1tYXJ5X2Rpc3RfdG9fbXJ0X20kci5zcXVhcmVkKSwNCiAgQWRqX1Jfc3F1YXJlZCA9IGMoc3VtbWFyeV9uX2NvbnZlbmllbmNlJGFkai5yLnNxdWFyZWQsIHN1bW1hcnlfZGlzdF90b19tcnRfbSRhZGouci5zcXVhcmVkKQ0KKQ0KDQpwcmludChjb21wYXJpc29uKSAjIFRoZSBiZXN0IHdpbGwgYmUgZGlzdF90b19tcnRfbQ0KYGBgDQoNCldlIHN0YXJ0IGJ5IGRpc3BsYXlpbmcgdGhlIHN0YXRpc3RpY2FsIHN1bW1hcnkgb2YgdGhlIG1vZGVsIHVzaW5nIHRoZSBSIGZ1bmN0aW9uIHN1bW1hcnkoKToNCg0KYGBge3J9DQpzdW1tYXJ5KG1vZGVsX2Rpc3RfdG9fbXJ0X20pICMgUHJpbnRpbmcgdGhhdCB0YWJsZQ0KYGBgDQoNCg0KWW91IGNhbiBhY2Nlc3MgbG90cyBvZiBkaWZmZXJlbnQgYXNwZWN0cyBvZiB0aGUgcmVncmVzc2lvbiBvYmplY3QuIFRvIHNlZSB3aGF04oCZcyBpbnNpZGUsIHVzZSBuYW1lcygpOg0KDQpgYGB7cn0NCm5hbWVzKG1vZGVsX2Rpc3RfdG9fbXJ0X20pDQpgYGANCkluc2lkZSB0aGUgbW9kZWwgd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSB0aGUgbmVjZXNzYXJ5IGVsZW1lbnRzIHdpdGhpbiB0aGUgbW9kZWwgdG8gYW5hbHl6ZSBhbmQgaW50ZXJwcmV0IHRoZSByZWdyZXNzaW9uIHJlc3VsdHMuDQoNCkNvbnNpZGVyaW5nIHRoZSBzdW1tYXJ5IHJlc3VsdHMgYWJvdmUgYW5kIGJhc2VkIG9uIHRoZSBSLXNxdWFyZWQgdmFsdWUgKDAuNDUzOCksIHRoZSBkaXN0X3RvX21ydF9tIHByZWRpY3RvciBtb2RlbCBleHBsYWlucyBhYm91dCA0NS4zOCUgb2YgdGhlIHZhcmlhdGlvbiBpbiBwcmljZSBwZXIgc3F1YXJlIG1ldGVyLCBpbmRpY2F0aW5nIG1vZGVyYXRlIHByZWRpY3Rpb24gYWNjdXJhY3kuDQoNCk1vZGVsIGRpYWdub3N0aWNzOg0KDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMiwgMikpDQpwbG90KG1vZGVsX2Rpc3RfdG9fbXJ0X20pDQpgYGANCjxjZW50ZXI+DQoNCioqTGV0J3MgYnJpZWZseSBnbyB0aHJvdWdoIGVhY2ggY2hhcnQgZnJvbSB0b3AgdG8gYm90dG9tIGFuZCBsZWZ0IHRvIHJpZ2h0IHRvIGRlc2NyaWJlIHNob3J0aGFuZCB3aGF0IHdlIHNlZS4qKg0KDQo8L2NlbnRlcj4NCg0KUmVzaWR1YWxzIHZzIEZpdHRlZDogd2Ugc2VlIHRoYXQgYWxtb3N0IG1vc3QgcG9pbnRzIGFyZSBzY2F0dGVyZWQgYXJvdW5kIHRoZSBsaW5lLCBpbmRpY2F0aW5nIHBvc3NpYmxlIG5vbmxpbmVhcml0eSBvciBoZXRlcm9zY2VkYXN0aWNpdHkuDQoNClEtUSBSZXNpZHVhbHM6IHdlIGNhbiBlYXNpbHkgbm90aWNlIHRoYXQgdGhlIGJlZ2lubmluZyBhbmQgdGhlIGVuZCBoYXZlIGRldmlhdGlvbnMgZnJvbSB0aGUgZGlhZ29uYWwgbGluZSwgaW5kaWNhdGluZyBub24tbm9ybWFsaXR5IG9mIHRoZSByZXNpZHVhbHMgZGlzdHJpYnV0aW9uLg0KDQpTY2FsZS1Mb2NhdGlvbjogaW4gdGhpcyBncmFwaCwgeW91IGNhbiBzZWUgdGhhdCB0aGUgcG9pbnRzIGFyZSBzY2F0dGVyZWQgcXVpdGUgcmFuZG9tbHksIGFsdGhvdWdoIHRoZXJlIGFyZSBzb21lIHJpZ2h0IG9uIHRoZSBsaW5lLiBEZXNwaXRlIHRoZXNlIHNvbWUgcG9pbnRzLCB3ZSBjYW4gc2F5IHRoYXQgd2UgaGF2ZSBhIGNvbnN0YW50IHZhcmlhbmNlLg0KDQpSZXNpZHVhbHMgdnMgTGV2ZXJhZ2U6IGhlcmUgd2UgY2FuIHNlZSBhIGZldyBwb2ludHMgd2l0aCBoaWdoIGxldmVyYWdlIGFuZCBsYXJnZSByZXNpZHVhbHMsIGluIHBhcnRpY3VsYXIgcG9pbnQgMjcxLCB3aGljaCBpcyBhIHNpZ25pZmljYW50IG91dGxpZXIgYW5kIGhhcyBhIGxhcmdlIGltcGFjdCBvbiB0aGUgbW9kZWwuIFN1Y2ggcG9pbnRzIGNhbiBoaWdobHkgZGlzdG9ydCB0aGUgbW9kZWwgcmVzdWx0cyBhbmQgcmVxdWlyZSBhdHRlbnRpb24uDQoNClRvIHN1bW1hcml6ZSwgdGhlIGdyYXBocyBzaG93IHBvc3NpYmxlIHByb2JsZW1zIHdpdGggaGV0ZXJvc2tlZGFzdGljaXR5IGFuZCBub24tbm9ybWFsaXR5IG9mIHJlc2lkdWFscywgYXMgd2VsbCBhcyBpbmRpY2F0ZSB0aGUgcHJlc2VuY2Ugb2Ygb3V0bGllcnMgdGhhdCBtYXkgYWZmZWN0IHRoZSBtb2RlbC4NCg0KQ3JlYXRlIHRoZSBkaWFnbm9zdGljIHBsb3RzIHVzaW5nIGdnZm9ydGlmeToNCg0KYGBge3J9DQpsaWJyYXJ5KGdnZm9ydGlmeSkNCmF1dG9wbG90KG1vZGVsX2Rpc3RfdG9fbXJ0X20pDQpgYGANCg0KT3V0bGllcnMgYW5kIGhpZ2ggbGV2YXJhZ2UgcG9pbnRzOg0KDQpgYGB7cn0NCnBsb3QobW9kZWxfZGlzdF90b19tcnRfbSwgNSkNCmBgYA0KDQoNCkluZmx1ZW50aWFsIHZhbHVlczoNCg0KYGBge3J9DQojIENvb2sncyBkaXN0YW5jZQ0KcGxvdChtb2RlbF9kaXN0X3RvX21ydF9tLCA0KQ0KYGBgDQoNCm9yIGp1c3QgcGxvdCBhbGwgb2YgZGlhZ25vc3RpYyBwbG90cyB0b2dldGhlcjoNCg0KYGBge3J9DQphdXRvcGxvdChtb2RlbF9kaXN0X3RvX21ydF9tLCB3aGljaCA9IDE6NiwgbGFiZWwuc2l6ZSA9IDMpDQpgYGANCg0KDQpEaXNjdXNzaW9uOg0KDQpDb29rJ3MgRGlzdGFuY2U6IFdlIGNhbiBzZWUgaW4gdGhlIGdyYXBoIHRoYXQgcG9pbnRzIDI3MSwgMjUwLCBhbmQgMTQ5IGhhdmUgdGhlIGhpZ2hlc3QgQ29vaydzIGRpc3RhbmNlIHZhbHVlLCBpbmRpY2F0aW5nIHRoYXQgdGhleSBoYXZlIGEgc2lnbmlmaWNhbnQgaW1wYWN0IG9uIG1vZGVsIGZpdC4NCg0KQ29vaydzIERpc3RhbmNlIHZzIExldmVyYWdlOiBBZ2FpbiwgcG9pbnRzIDI3MSwgMTQ5IGFuZCAyNTAgc3RhbmQgb3V0IGFzIGhhdmluZyBoaWdoIHZhbHVlcyBvZiBsZXZlcmFnZSBhbmQgQ29vaydzIGRpc3RhbmNlLCBjb25maXJtaW5nIHRoZWlyIHNpZ25pZmljYW50IGluZmx1ZW5jZSBvbiB0aGUgbW9kZWwuDQoNCkl0IGlzIG5vdGljZWFibGUgaW4gdGhlIGdyYXBocyB0aGF0IHBvaW50cyAyNzEsIDI1MCBhbmQgMTQ5IGhhdmUgaGlnaCB2YWx1ZXMgb2YgQ29vaydzIGRpc3RhbmNlIGFuZCBsZXZlcmFnZSwgaW5kaWNhdGluZyB0aGVpciBzaWduaWZpY2FudCBpbmZsdWVuY2Ugb24gdGhlIG1vZGVsLg0KDQojIyBNdWx0aXBsZSBSZWdyZXNzaW9uIE1vZGVsDQoNCiMjIyBUZXN0IGFuZCB0cmFpbmluZyBzZXQgDQoNCldlIGJlZ2luIGJ5IHNwbGl0dGluZyB0aGUgZGF0YXNldCBpbnRvIHR3byBwYXJ0cywgdHJhaW5pbmcgc2V0IGFuZCB0ZXN0aW5nIHNldC4gSW4gdGhpcyBleGFtcGxlIHdlIHdpbGwgcmFuZG9tbHkgdGFrZSA3NSUgcm93IGluIHRoaXMgZGF0YXNldCBhbmQgcHV0IGl0IGludG8gdGhlIHRyYWluaW5nIHNldCwgYW5kIG90aGVyIDI1JSByb3cgaW4gdGhlIHRlc3Rpbmcgc2V0Og0KDQpgYGB7ciB0ZXN0X3RyYWlufQ0Kc21wX3NpemU8LWZsb29yKDAuNzUqbnJvdyh0YWl3YW5fcmVhbF9lc3RhdGUpKQ0Kc2V0LnNlZWQoMTIpDQp0cmFpbl9pbmQ8LXNhbXBsZShzZXFfbGVuKG5yb3codGFpd2FuX3JlYWxfZXN0YXRlKSksIHNpemU9c21wX3NpemUpDQp0cmFpbjwtdGFpd2FuX3JlYWxfZXN0YXRlW3RyYWluX2luZCwgXQ0KdGVzdDwtdGFpd2FuX3JlYWxfZXN0YXRlWy10cmFpbl9pbmQsIF0NCmBgYA0KDQotICAgMXN0IGNvbW1lbnQ6IGZsb29yKCkgaXMgdXNlZCB0byByZXR1cm4gdGhlIGxhcmdlc3QgaW50ZWdlciB2YWx1ZSB3aGljaCBpcyBub3QgZ3JlYXRlciB0aGFuIGFuIGluZGl2aWR1YWwgbnVtYmVyLCBvciBleHByZXNzaW9uLg0KDQotICAgMm5kIGNvbW1lbnQ6IHNldC5zZWVkKCkgaXMgdXNlZCB0byBzZXQgdGhlIHNlZWQgb2YgUidzIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yLCB0aGlzIGZ1bmN0aW9uIGlzIHVzZWQgc28gcmVzdWx0cyBmcm9tIHRoaXMgZXhhbXBsZSBjYW4gYmUgcmVjcmVhdGVkIGVhc2lseS4NCg0KTm93IHdlIGhhdmUgb3VyIHRyYWluaW5nIHNldCBhbmQgdGVzdGluZyBzZXQuIA0KDQojIyMgVmFyaWFibGUgc2VsZWN0aW9uIG1ldGhvZHMNCg0KR2VuZXJhbGx5LCBzZWxlY3RpbmcgdmFyaWFibGVzIGZvciBsaW5lYXIgcmVncmVzc2lvbiBpcyBhIGRlYmF0YWJsZSB0b3BpYy4NCg0KVGhlcmUgYXJlIG1hbnkgbWV0aG9kcyBmb3IgdmFyaWFibGUgc2VsZWN0aW5nLCBuYW1lbHksIGZvcndhcmQgc3RlcHdpc2Ugc2VsZWN0aW9uLCBiYWNrd2FyZCBzdGVwd2lzZSBzZWxlY3Rpb24sIGV0Yywgc29tZSBhcmUgdmFsaWQsIHNvbWUgYXJlIGhlYXZpbHkgY3JpdGljaXplZC4NCg0KSSByZWNvbW1lbmQgdGhpcyBkb2N1bWVudDogPGh0dHBzOi8vd3d3LnN0YXQuY211LmVkdS9+Y3NoYWxpemkvbXJlZy8xNS9sZWN0dXJlcy8yNi9sZWN0dXJlLTI2LnBkZj4gYW5kIEd1bmcncyBjb21tZW50OiA8aHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMjA4MzYvYWxnb3JpdGhtcy1mb3ItYXV0b21hdGljLW1vZGVsLXNlbGVjdGlvbi8yMDg1NiMyMDg1Nj4gaWYgeW91IHdhbnQgdG8gbGVhcm4gbW9yZSBhYm91dCB2YXJpYWJsZSBzZWxlY3Rpb24gcHJvY2Vzcy4NCg0KWyoqSWYgb3VyIGdvYWwgaXMgcHJlZGljdGlvbioqXXsudWx9LCBpdCBpcyBzYWZlciB0byBpbmNsdWRlIGFsbCBwcmVkaWN0b3JzIGluIG91ciBtb2RlbCwgcmVtb3ZpbmcgdmFyaWFibGVzIHdpdGhvdXQga25vd2luZyB0aGUgc2NpZW5jZSBiZWhpbmQgaXQgdXN1YWxseSBkb2VzIG1vcmUgaGFybSB0aGFuIGdvb2QhISENCg0KV2UgYmVnaW4gdG8gY3JlYXRlIG91ciBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbDoNCg0KYGBge3J9DQptb2RlbDIgPC0gbG0ocHJpY2VfdHdkX21zcSB+IC4sIGRhdGEgPSB0cmFpbikNCnN1bW1hcnkobW9kZWwyKQ0KYGBgDQpXZSBjYW4gb2JzZXJ2ZSBmcm9tIHRoZSBpbXBvcnRhbnQgYXNwZWN0cyB0aGF0IG91ciBtb2RlbCBleHBsYWlucyBhYm91dCA1Ni43NiUgb2YgdGhlIHZhcmlhdGlvbiBvZiBwcmljZV90d2RfbXNxLiBUaGUgaW5mbHVlbmNlIG9mIGhvdXNlX2FnZV95ZWFycywgZGlzdF90b19tcnRfbSwgbl9jb252ZW5pZW5jZSBhbmQgbGF0aXR1ZGUgdmFyaWFibGVzIGFyZSBzaWduaWZpY2FudCB3aGlsZSB0aGUgaW5mbHVlbmNlIG9mIGxvbmdpdHVkZSB2YXJpYWJsZXMgYW5kIGhvdXNlIGFnZSBjYXRlZ29yaWVzIGFyZSBpbnNpZ25pZmljYW50LiBUaGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIHJlc2lkdWFscyBzaG93cyB0aGUgYXZlcmFnZSBkZXZpYXRpb24gb2YgdGhlIHByZWRpY3RlZCB2YWx1ZXMgZnJvbSB0aGUgYWN0dWFsIHZhbHVlcy4NCg0KTG9va2luZyBhdCBtb2RlbCBzdW1tYXJ5LCB3ZSBzZWUgdGhhdCB2YXJpYWJsZXMgSW50ZXJjZXB0LCBsb25naXR1ZGUsIGhvdXNlX2FnZV9jYXRbMTUsMzApIGFuZCBob3VzZV9hZ2VfY2F0WzMwLDQ1XSBhcmUgaW5zaWduaWZpY2FudCwgc28gbGV0J3MgZXN0aW1hdGUgdGhlIG1vZGVsIHdpdGhvdXQgdGhvc2UgdmFyaWFibGVzOg0KDQpgYGB7cn0NCm1vZGVsMyA8LSBsbShwcmljZV90d2RfbXNxIH4gaG91c2VfYWdlX3llYXJzICsgZGlzdF90b19tcnRfbSArIG5fY29udmVuaWVuY2UgKyBsYXRpdHVkZSwgZGF0YSA9IHRyYWluKQ0Kc3VtbWFyeShtb2RlbDMpDQpgYGANCg0KDQojIyMgRXZhbHVhdGluZyBtdWx0aS1jb2xsaW5lYXJpdHkNCg0KDQpUaGVyZSBhcmUgbWFueSBzdGFuZGFyZHMgcmVzZWFyY2hlcnMgYXBwbHkgZm9yIGRlY2lkaW5nIHdoZXRoZXIgYSBWSUYgaXMgdG9vIGxhcmdlLiBJbiBzb21lIGRvbWFpbnMsIGEgVklGIG92ZXIgMiBpcyB3b3J0aHkgb2Ygc3VzcGljaW9uLiBPdGhlcnMgc2V0IHRoZSBiYXIgaGlnaGVyLCBhdCA1IG9yIDEwLiBPdGhlcnMgc3RpbGwgd2lsbCBzYXkgeW91IHNob3VsZG4ndCBwYXkgYXR0ZW50aW9uIHRvIHRoZXNlIGF0IGFsbC4gVWx0aW1hdGVseSwgdGhlIG1haW4gdGhpbmcgdG8gY29uc2lkZXIgaXMgdGhhdCBzbWFsbCBlZmZlY3RzIGFyZSBtb3JlIGxpa2VseSB0byBiZSAiZHJvd25lZCBvdXQiIGJ5IGhpZ2hlciBWSUZzLCBidXQgdGhpcyBtYXkganVzdCBiZSBhIG5hdHVyYWwsIHVuYXZvaWRhYmxlIGZhY3Qgd2l0aCB5b3VyIG1vZGVsLg0KDQpgYGB7ciBtdWx0aWNvbGxpbmVhcml0eSwgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KdmlmX3ZhbHVlcyA8LSB2aWYobW9kZWwzKQ0KcHJpbnQodmlmX3ZhbHVlcykNCg0KY2F0KCJWYXJpYWJsZXMgd2l0aCBWSUYgPiA1IG1heSBpbmRpY2F0ZSBtdWx0aWNvbGxpbmVhcml0eSBpc3N1ZXMuXG4iKQ0KYGBgDQoNCg0KRmluYWxseSB3ZSB0ZXN0IG91ciBtb2RlbCBvbiB0ZXN0IGRhdGFzZXQ6DQoNCmBgYHtyIG11bHRpcGxlX2RpYWdub3N0aWNzLCBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG1vZGVsMywgbmV3ZGF0YSA9IHRlc3QpDQoNCm1hZSA8LSBtZWFuKGFicyhwcmVkaWN0aW9ucyAtIHRlc3QkcHJpY2VfdHdkX21zcSkpDQptc2UgPC0gbWVhbigocHJlZGljdGlvbnMgLSB0ZXN0JHByaWNlX3R3ZF9tc3EpXjIpDQpybXNlIDwtIHNxcnQobXNlKQ0KDQpjYXQoIk1lYW4gQWJzb2x1dGUgRXJyb3IgKE1BRSk6ICIsIG1hZSwgIlxuIikNCmNhdCgiTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpOiAiLCBtc2UsICJcbiIpDQpjYXQoIlJvb3QgTWVhbiBTcXVhcmVkIEVycm9yIChSTVNFKTogIiwgcm1zZSwgIlxuIikNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gdGVzdCRwcmljZV90d2RfbXNxLCB5ID0gcHJlZGljdGlvbnMpLCBjb2xvciA9ICdibHVlJykgKw0KICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGNvbG9yID0gJ3JlZCcsIGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsNCiAgbGFicyh0aXRsZSA9ICJQcmVkaWN0ZWQgdnMgQWN0dWFsIFByaWNlcyIsDQogICAgICAgeCA9ICJBY3R1YWwgUHJpY2VzIChUV0QgcGVyIG1eMikiLA0KICAgICAgIHkgPSAiUHJlZGljdGVkIFByaWNlcyAoVFdEIHBlciBtXjIpIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpXZSBjYW4gb2JzZXJ2ZSB0aGF0IHRoZSBtb2RlbCB0ZWxscyB1cyByZWxhdGl2ZWx5IGFjY3VyYXRlbHkgYWJvdXQgcHJpY2VzIGFuZCB0aGVpciBhcHByb3hpbWF0ZSBjaGFuZ2VzLCBidXQgdGhlcmUgYXJlIHNvbWUgb3V0bGllcnMsIGVzcGVjaWFsbHkgZm9yIGxvdyBwcmljZSB2YWx1ZXMsIHdoZXJlIHRoZSBwcmVkaWN0ZWQgdmFsdWVzIGRpZmZlciBzaWduaWZpY2FudGx5IGZyb20gdGhlIGFjdHVhbCB2YWx1ZXMuDQpPdmVyYWxsLCB3ZSBzZWUgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwcmVkaWN0ZWQgYW5kIGFjdHVhbCB2YWx1ZXMsIGluZGljYXRpbmcgdGhhdCB0aGUgbW9kZWwgaXMgY2FwdHVyaW5nIHRoZSB1bmRlcmx5aW5nIHRyZW5kIGluIHRoZSBkYXRhIHF1aXRlIHdlbGwuDQoNCldlIGhhdmUgYWNjZXB0YWJsZSBwcmVkaWN0aW9uIGFjY3VyYWN5LCBob3dldmVyLCB0aGVyZSBhcmUgb3V0bGllcnMgdGhhdCBpbmRpY2F0ZSB0aGUgbmVlZCB0byBmdXJ0aGVyIGltcHJvdmUgb3VyIG1vZGVsLg0KDQoNCiMjIFZhcmlhYmxlIHNlbGVjdGlvbiB1c2luZyBiZXN0IHN1YnNldCByZWdyZXNzaW9uDQoNCipCZXN0IHN1YnNldCBhbmQgc3RlcHdpc2UgKGZvcndhcmQsIGJhY2t3YXJkLCBib3RoKSB0ZWNobmlxdWVzIG9mIHZhcmlhYmxlIHNlbGVjdGlvbiBjYW4gYmUgdXNlZCB0byBjb21lIHVwIHdpdGggdGhlIGJlc3QgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgbWVkdi4qDQoNCmBgYHtyIG1vZGVsX3N1YnNldCwgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KYmVzdF9zdWJzZXQgPC0gcmVnc3Vic2V0cyhwcmljZV90d2RfbXNxIH4gLiwgZGF0YSA9IHRyYWluLCBudm1heCA9IE5VTEwpDQpzdW1tYXJ5X2Jlc3Rfc3Vic2V0IDwtIHN1bW1hcnkoYmVzdF9zdWJzZXQpDQoNCnByaW50KHN1bW1hcnlfYmVzdF9zdWJzZXQpDQpwbG90KGJlc3Rfc3Vic2V0LCBzY2FsZSA9ICJhZGpyMiIpDQpgYGANCg0KYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KIyBmb3J3YXJkDQpzdGVwX2ZvcndhcmQgPC0gc3RlcEFJQyhsbShwcmljZV90d2RfbXNxIH4gMSwgZGF0YSA9IHRyYWluKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBzY29wZSA9IGxpc3QobG93ZXIgPSB+MSwgdXBwZXIgPSB+IGhvdXNlX2FnZV95ZWFycyArIGRpc3RfdG9fbXJ0X20gKyBuX2NvbnZlbmllbmNlICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUgKyBob3VzZV9hZ2VfY2F0KSwgDQogICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiZm9yd2FyZCIpDQoNCiMgYmFja3dhcmQNCnN0ZXBfYmFja3dhcmQgPC0gc3RlcEFJQyhsbShwcmljZV90d2RfbXNxIH4gaG91c2VfYWdlX3llYXJzICsgZGlzdF90b19tcnRfbSArIG5fY29udmVuaWVuY2UgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSArIGhvdXNlX2FnZV9jYXQsIGRhdGEgPSB0cmFpbiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJiYWNrd2FyZCIpDQoNCiMgYm90aA0Kc3RlcF9ib3RoIDwtIHN0ZXBBSUMobG0ocHJpY2VfdHdkX21zcSB+IDEsIGRhdGEgPSB0cmFpbiksIA0KICAgICAgICAgICAgICAgICAgICAgc2NvcGUgPSBsaXN0KGxvd2VyID0gfjEsIHVwcGVyID0gfiBob3VzZV9hZ2VfeWVhcnMgKyBkaXN0X3RvX21ydF9tICsgbl9jb252ZW5pZW5jZSArIGxhdGl0dWRlICsgbG9uZ2l0dWRlICsgaG91c2VfYWdlX2NhdCksIA0KICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImJvdGgiKQ0KDQpzdW1tYXJ5KHN0ZXBfZm9yd2FyZCkNCnN1bW1hcnkoc3RlcF9iYWNrd2FyZCkNCnN1bW1hcnkoc3RlcF9ib3RoKQ0KYGBgDQoNCiMjIFZhcmlhYmxlIHNlbGVjdGlvbiB1c2luZyBzdGVwd2lzZSByZWdyZXNzaW9uDQoNCmBgYHtyIG1vZGVsX3N0ZXB3aXNlLCBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpudWxsbW9kZWwgPC0gbG0ocHJpY2VfdHdkX21zcSB+IDEsIGRhdGEgPSB0cmFpbikNCmZ1bGxtb2RlbCA8LSBsbShwcmljZV90d2RfbXNxIH4gaG91c2VfYWdlX3llYXJzICsgZGlzdF90b19tcnRfbSArIG5fY29udmVuaWVuY2UgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSArIGhvdXNlX2FnZV9jYXQsIGRhdGEgPSB0cmFpbikNCg0KI2ZvcndhcmQgc2VsZWN0aW9uDQptb2RlbF9mb3J3YXJkIDwtIHN0ZXBBSUMobnVsbG1vZGVsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBzY29wZSA9IGxpc3QobG93ZXIgPSBudWxsbW9kZWwsIHVwcGVyID0gZnVsbG1vZGVsKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImZvcndhcmQiKQ0Kc3VtbWFyeShtb2RlbF9mb3J3YXJkKQ0KYGBgDQoNCmBgYHtyIG1vZGVsX3N0ZXB3aXNlMiwgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KI0JhY2t3YXJkIHNlbGVjdGlvbg0KbW9kZWwuYmFja3dhcmQgPC0gc3RlcEFJQyhmdWxsbW9kZWwsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiYmFja3dhcmQiKQ0Kc3VtbWFyeShtb2RlbC5iYWNrd2FyZCkNCmBgYA0KDQpgYGB7ciBtb2RlbF9zdGVwd2lzZV9ib3RoLCBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQojc3RlcHdpc2Ugc2VsZWN0aW9uDQptb2RlbC5zdGVwIDwtIHN0ZXBBSUMobnVsbG1vZGVsLCANCiAgICAgICAgICAgICAgICAgICAgICBzY29wZSA9IGxpc3QobG93ZXIgPSBudWxsbW9kZWwsIHVwcGVyID0gZnVsbG1vZGVsKSwgDQogICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImJvdGgiKQ0KYGBgDQoNCiMjIENvbXBhcmluZyBjb21wZXRpbmcgbW9kZWxzDQoNCmBgYHtyIGNvbXBhcmlzb24sIGVjaG89RkFMU0UsIHdhcm5pbmc9VFJVRX0NCkFJQyhtb2RlbF9mb3J3YXJkLG1vZGVsLmJhY2t3YXJkLG1vZGVsLnN0ZXApDQpzdW1tYXJ5KG1vZGVsLnN0ZXApDQpgYGANCg0KRnJvbSAqKkJlc3Qgc3Vic2V0IHJlZ3Jlc3Npb24qKiBhbmQgc3RlcHdpc2Ugc2VsZWN0aW9uIChmb3J3YXJkLCBiYWNrd2FyZCwgYm90aCksIHdlIHNlZSB0aGF0IGFsbCBtZXRob2RzIGNvbnNpc3RlbnRseSBzZWxlY3RlZCB0aGUgc2FtZSBtb2RlbCB3aXRoIHRoZSBwcmVkaWN0b3JzIGRpc3RfdG9fbXJ0X20sIG5fY29udmVuaWVuY2UsIGhvdXNlX2FnZV95ZWFycywgbGF0aXR1ZGUsIGFuZCBob3VzZV9hZ2VfY2F0WzE1LDMwKSwgaW5kaWNhdGluZyB0aGVpciBzaWduaWZpY2FudCBpbmZsdWVuY2Ugb24gdGhlIHByaWNlIHBlciBzcXVhcmUgbWV0ZXIuDQoNCmBgYHtyIHN0ZXBfZGlhZ25vc3RpY3MsIGVjaG89RkFMU0UsIHdhcm5pbmc9VFJVRX0NCiNNb2RlbCBEaWFnbm9zdGljcw0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChtb2RlbC5zdGVwKQ0KYGBgDQoNCk1vZGVscyBhcmUgY29tcGFyZWQgYmFzZWQgb24gYWRqdXN0ZWQgciBzcXVhcmUsIEFJQywgQklDIGNyaXRlcmlhIGZvciBpbi1zYW1wbGUgcGVyZm9ybWFuY2UgYW5kIG1lYW4gc3F1YXJlIHByZWRpY3Rpb24gZXJyb3IgKE1TUEUpIGZvciBvdXQtb2Ytc2FtcGxlIHBlcmZvcm1hbmNlLg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KcGFyKG1mcm93ID0gYygxLDEpKQ0KI0luLXNhbXBsZSBwZXJmb3JtYW5jZQ0KI01TRQ0KbXNlX2ZvcndhcmQgPC0gbWVhbihyZXNpZHVhbHMobW9kZWxfZm9yd2FyZCleMikNCm1zZV9iYWNrd2FyZCA8LSBtZWFuKHJlc2lkdWFscyhtb2RlbC5iYWNrd2FyZCleMikNCm1zZV9zdGVwIDwtIG1lYW4ocmVzaWR1YWxzKG1vZGVsLnN0ZXApXjIpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9VFJVRX0NCiNSLXNxdWFyZWQNCnJfc3F1YXJlZF9mb3J3YXJkIDwtIHN1bW1hcnkobW9kZWxfZm9yd2FyZCkkci5zcXVhcmVkDQpyX3NxdWFyZWRfYmFja3dhcmQgPC0gc3VtbWFyeShtb2RlbC5iYWNrd2FyZCkkci5zcXVhcmVkDQpyX3NxdWFyZWRfc3RlcCA8LSBzdW1tYXJ5KG1vZGVsLnN0ZXApJHIuc3F1YXJlZA0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQojQWRqdXN0ZWQgciBzcXVhcmUNCmFkal9yX3NxdWFyZWRfZm9yd2FyZCA8LSBzdW1tYXJ5KG1vZGVsX2ZvcndhcmQpJGFkai5yLnNxdWFyZWQNCmFkal9yX3NxdWFyZWRfYmFja3dhcmQgPC0gc3VtbWFyeShtb2RlbC5iYWNrd2FyZCkkYWRqLnIuc3F1YXJlZA0KYWRqX3Jfc3F1YXJlZF9zdGVwIDwtIHN1bW1hcnkobW9kZWwuc3RlcCkkYWRqLnIuc3F1YXJlZA0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQojQUlDIA0KYWljX2ZvcndhcmQgPC0gQUlDKG1vZGVsX2ZvcndhcmQpDQphaWNfYmFja3dhcmQgPC0gQUlDKG1vZGVsLmJhY2t3YXJkKQ0KYWljX3N0ZXAgPC0gQUlDKG1vZGVsLnN0ZXApDQoNCiNCSUMNCkJJQyA8LSBmdW5jdGlvbihtb2RlbCkgew0KICBuIDwtIGxlbmd0aChtb2RlbCRmaXR0ZWQudmFsdWVzKQ0KICBrIDwtIGxlbmd0aChjb2VmZmljaWVudHMobW9kZWwpKQ0KICBsb2dMIDwtIGxvZ0xpayhtb2RlbCkNCiAgYmljIDwtIC0yICogYXMubnVtZXJpYyhsb2dMKSArIGsgKiBsb2cobikNCiAgcmV0dXJuKGJpYykNCn0NCg0KYmljX2ZvcndhcmQgPC0gQklDKG1vZGVsX2ZvcndhcmQpDQpiaWNfYmFja3dhcmQgPC0gQklDKG1vZGVsLmJhY2t3YXJkKQ0KYmljX3N0ZXAgPC0gQklDKG1vZGVsLnN0ZXApDQpgYGANCg0KRmluYWxseSwgd2UgY2FuIGNoZWNrIHRoZSBPdXQtb2Ytc2FtcGxlIFByZWRpY3Rpb24gb3IgdGVzdCBlcnJvciAoTVNQRSk6DQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpwcmVkX2ZvcndhcmQgPC0gcHJlZGljdChtb2RlbF9mb3J3YXJkLCBuZXdkYXRhID0gdGVzdCkNCnByZWRfYmFja3dhcmQgPC0gcHJlZGljdChtb2RlbC5iYWNrd2FyZCwgbmV3ZGF0YSA9IHRlc3QpDQpwcmVkX3N0ZXAgPC0gcHJlZGljdChtb2RlbC5zdGVwLCBuZXdkYXRhID0gdGVzdCkNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KbXNwZV9mb3J3YXJkIDwtIG1lYW4oKHByZWRfZm9yd2FyZCAtIHRlc3QkcHJpY2VfdHdkX21zcSleMikNCm1zcGVfYmFja3dhcmQgPC0gbWVhbigocHJlZF9iYWNrd2FyZCAtIHRlc3QkcHJpY2VfdHdkX21zcSleMikNCm1zcGVfc3RlcCA8LSBtZWFuKChwcmVkX3N0ZXAgLSB0ZXN0JHByaWNlX3R3ZF9tc3EpXjIpDQpgYGANCg0KDQojIyBDcm9zcyBWYWxpZGF0aW9uDQoNClBsZWFzZSBjaGVjayBob3cgZnVuY3Rpb24gKio/Y3YuZ2xtKiogd29ya3MuDQoNCldlIHdpbGwganVzdCBleHRyYWN0IGZyb20gdGhpcyBvYmplY3QgY3JlYXRlZCBieSBjdi5nbG0gY29tbWFuZCAtIHRoZSByYXcgY3Jvc3MtdmFsaWRhdGlvbiBlc3RpbWF0ZSBvZiBwcmVkaWN0aW9uIGVycm9yLg0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpjb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkNCg0KbW9kZWxfY2FyZXQgPC0gdHJhaW4ocHJpY2VfdHdkX21zcSB+IGRpc3RfdG9fbXJ0X20gKyBuX2NvbnZlbmllbmNlICsgaG91c2VfYWdlX3llYXJzICsgbGF0aXR1ZGUgKyBob3VzZV9hZ2VfY2F0LCANCiAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbiwgDQogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAibG0iLCANCiAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGNvbnRyb2wpDQoNCnByaW50KG1vZGVsX2NhcmV0KQ0KDQpjdl9lcnJvcl9jYXJldCA8LSBtb2RlbF9jYXJldCRyZXN1bHRzJFJNU0VeMg0KY2F0KCJDcm9zcy12YWxpZGF0aW9uIGVzdGltYXRlIG9mIHByZWRpY3Rpb24gZXJyb3I6ICIsIGN2X2Vycm9yX2NhcmV0LCAiXG4iKQ0KYGBgDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KSyA8LSAxMA0KZm9sZHMgPC0gY3JlYXRlRm9sZHModHJhaW4kcHJpY2VfdHdkX21zcSwgayA9IEssIGxpc3QgPSBUUlVFKQ0KDQptc2UgPC0gZnVuY3Rpb24ocHJlZGljdGlvbnMsIGFjdHVhbHMpIHsNCiAgbWVhbigocHJlZGljdGlvbnMgLSBhY3R1YWxzKV4yKQ0KfQ0KDQpjdl9lcnJvcnMgPC0gbnVtZXJpYyhLKQ0KDQpmb3IgKGkgaW4gMTpLKSB7DQogIGZvbGRfaW5kaWNlcyA8LSBmb2xkc1tbaV1dDQogIHRyYWluX2ZvbGQgPC0gdHJhaW5bLWZvbGRfaW5kaWNlcywgXQ0KICB2YWxpZGF0aW9uX2ZvbGQgPC0gdHJhaW5bZm9sZF9pbmRpY2VzLCBdDQogIA0KICBtb2RlbCA8LSBsbShwcmljZV90d2RfbXNxIH4gZGlzdF90b19tcnRfbSArIG5fY29udmVuaWVuY2UgKyBob3VzZV9hZ2VfeWVhcnMgKyBsYXRpdHVkZSArIGhvdXNlX2FnZV9jYXQsIGRhdGEgPSB0cmFpbl9mb2xkKQ0KICANCiAgcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IHZhbGlkYXRpb25fZm9sZCkNCiAgDQogIGN2X2Vycm9yc1tpXSA8LSBtc2UocHJlZGljdGlvbnMsIHZhbGlkYXRpb25fZm9sZCRwcmljZV90d2RfbXNxKQ0KfQ0KDQpjdl9lcnJvcl9tYW51YWwgPC0gbWVhbihjdl9lcnJvcnMpDQpjYXQoIk1hbnVhbCBjcm9zcy12YWxpZGF0aW9uIGVzdGltYXRlIG9mIHByZWRpY3Rpb24gZXJyb3I6ICIsIGN2X2Vycm9yX21hbnVhbCwgIlxuIikNCmBgYA0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9VFJVRX0NCmNvbXBhcmlzb24gPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJGb3J3YXJkIiwgIkJhY2t3YXJkIiwgIlN0ZXB3aXNlIiksDQogIFJfU3F1YXJlZCA9IGMocl9zcXVhcmVkX2ZvcndhcmQsIHJfc3F1YXJlZF9iYWNrd2FyZCwgcl9zcXVhcmVkX3N0ZXApLA0KICBBZGpfUl9TcXVhcmVkID0gYyhhZGpfcl9zcXVhcmVkX2ZvcndhcmQsIGFkal9yX3NxdWFyZWRfYmFja3dhcmQsIGFkal9yX3NxdWFyZWRfc3RlcCksDQogIEFJQyA9IGMoYWljX2ZvcndhcmQsIGFpY19iYWNrd2FyZCwgYWljX3N0ZXApLA0KICBCSUMgPSBjKGJpY19mb3J3YXJkLCBiaWNfYmFja3dhcmQsIGJpY19zdGVwKSwNCiAgTVNFID0gYyhtc2VfZm9yd2FyZCwgbXNlX2JhY2t3YXJkLCBtc2Vfc3RlcCksDQogIE1TUEUgPSBjKG1zcGVfZm9yd2FyZCwgbXNwZV9iYWNrd2FyZCwgbXNwZV9zdGVwKQ0KKQ0KYGBgDQoNCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9VFJVRX0NCiMg0J7QsdC90L7QstC70LXQvdC40LUg0YLQsNCx0LvQuNGG0Ysg0YHRgNCw0LLQvdC10L3QuNGPINC00LvRjyDQv9C+0LvRg9GH0LXQvdC40Y8g0LzQvtC00LXQu9C4INGBINC90LDQuNC80LXQvdGM0YjQuNC8INC30L3QsNGH0LXQvdC40LXQvCBBSUMNCmJlc3RfbW9kZWxfQUlDIDwtIGNvbXBhcmlzb25bd2hpY2gubWluKGNvbXBhcmlzb24kQUlDKSwgIk1vZGVsIl0NCg0KY2F0KCJCYXNlZCBvbiBBSUMgY3JpdGVyaWEsIHRoZSBiZXN0IG1vZGVsIGlzIiwgYmVzdF9tb2RlbF9BSUMsICJcbiIpDQpgYGANCg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KIyDQntCx0L3QvtCy0LvQtdC90LjQtSDRgtCw0LHQu9C40YbRiyDRgdGA0LDQstC90LXQvdC40Y8g0LTQu9GPINC/0L7Qu9GD0YfQtdC90LjRjyDQvNC+0LTQtdC70Lgg0YEg0L3QsNC40LzQtdC90YzRiNC40Lwg0LfQvdCw0YfQtdC90LjQtdC8IE1TUEUNCmJlc3RfbW9kZWxfTVNQRSA8LSBjb21wYXJpc29uW3doaWNoLm1pbihjb21wYXJpc29uJE1TUEUpLCAiTW9kZWwiXQ0KDQpjYXQoIldlIG5lZWQgdG8gY2hlY2sgb3V0LW9mLXNhbXBsZSBNU1BFIGZvciBib3RoIG1vZGVscy4gQmFzZWQgb24gb3V0LW9mLXNhbXBsZSBwcmVkaWN0aW9uIGVycm9yLCBtb2RlbCIsIGJlc3RfbW9kZWxfTVNQRSwgImlzIHRoZSBiZXN0LlxuIikNCmBgYA0KDQoNClRvIHN1bW1hcml6ZSwgd2UgY2FuIHNheSB0aGF0IGJhc2VkIG9uIHRoZSBBSUMgY3JpdGVyaW9uIGFuZCBNU1BFIHByZWRpY3Rpb24gZXJyb3IgZXN0aW1hdGlvbiwgZm9yIGJvdGggb2YgdGhlbSB0aGUgZm9yd2FyZCBzZWxlY3Rpb24gbW9kZWwgd2FzIGNob3NlbiBhcyB0aGUgYmVzdCBtb2RlbCBpbiBvdXIgY29uZGl0aW9ucy4NCg0KVGhlc2UgcmVzdWx0cyBhcmUgc2F0aXNmYWN0b3J5IGFuZCBpbiBsaW5lIHdpdGggb3VyIGV4cGVjdGF0aW9ucywgZ2l2ZW4gdGhlIHJlc2VhcmNoIGFuZCB3b3JrIGRvbmUgb24gdGhlIGRhdGEu