Daniel Farías | A01236327

Kathia Ruiz | A01571094

Naila Salinas | A00832702

Sofia Badillo | A02384253

# import dataset
data <- read.csv("C:\\Users\\danyb\\OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey\\Docs\\Documentos\\Business Intelligence\\Quinto Semestre\\Introduction to Econometrics\\real_estate_data.csv")

A. Exploratory Data Analysis

Data description

  • median_value - median value of owner-occupied homes in USD 1000’s
  • crime_rate - per capita crime rate by town
  • residential_land - proportion of residential land zoned for lots over 25,000 sq.ft
  • non_retail_business - proportion of non-retail business acres per town
  • river_view - Dummy variable (= 1 if neighborhood bounds river; 0 otherwise)
  • nitric_oxid - nitric oxides concentration (parts per 10 million)
  • rooms - average number of rooms per dwelling
  • age - proportion of owner-occupied units built prior to 1940
  • distance - weighted distances to five Boston employment centers
  • access_to_highways - index of accessibility to radial highways
  • property_tax_rate - full-value property-tax rate per USD 10,000
  • school - pupil-teacher ratio by town
  • low_status_pop percentage of lower status of the population

Identyfing missing values

# we explore de data set to see if there are missing values

# sum all the na in the hole dataset 
sum(is.na(data))
## [1] 0
# shows the sum of the na per variable
colSums(is.na(data))
##    medv   cmedv    crim      zn   indus    chas     nox      rm     age     dis 
##       0       0       0       0       0       0       0       0       0       0 
##     rad     tax ptratio       b   lstat 
##       0       0       0       0       0
# plot the na per variable
#gg_miss_var(data)

We can conclude that there are no NA or missing values in our dataset

Structure of the dataset

str(data)
## 'data.frame':    506 obs. of  15 variables:
##  $ medv   : num  24 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 ...
##  $ cmedv  : num  24 21.6 34.7 33.4 36.2 28.7 22.9 22.1 16.5 18.9 ...
##  $ crim   : num  0.00632 0.02731 0.02729 0.03237 0.06905 ...
##  $ zn     : num  18 0 0 0 0 0 12.5 12.5 12.5 12.5 ...
##  $ indus  : num  2.31 7.07 7.07 2.18 2.18 2.18 7.87 7.87 7.87 7.87 ...
##  $ chas   : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ nox    : num  0.538 0.469 0.469 0.458 0.458 0.458 0.524 0.524 0.524 0.524 ...
##  $ rm     : num  6.58 6.42 7.18 7 7.15 ...
##  $ age    : num  65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
##  $ dis    : num  4.09 4.97 4.97 6.06 6.06 ...
##  $ rad    : int  1 2 2 3 3 3 5 5 5 5 ...
##  $ tax    : int  296 242 242 222 222 222 311 311 311 311 ...
##  $ ptratio: num  15.3 17.8 17.8 18.7 18.7 18.7 15.2 15.2 15.2 15.2 ...
##  $ b      : num  397 397 393 395 397 ...
##  $ lstat  : num  4.98 9.14 4.03 2.94 5.33 ...

We have a dataset composed by 15 variables with 506 observations

Each variable stands for medv : num median_value crim : num crime_rate
zn : num residential_land indus : num non_retail_business chas : int river_view nox : num nitric_oxid rm : num rooms age : num age dis : num distance rad : int access_to_highways tax : int property_tax_rate ptratio : num school lstat : num low_status_pop

Trasformation of variables

As we see before in the data description of the data set, chas is river_view which is a dummy variable, and rad is access_to_highways that is a rate form 1 to 8 and 24. So we have to transform this variables. Also we are going to eliminate de variables “cmedv” and “b”

data$chas <- as.factor(data$chas)
data <- data[, !(names(data) %in% c("cmedv", "b"))]
#data$rad  <- as.factor(data$rad) 
str(data)
## 'data.frame':    506 obs. of  13 variables:
##  $ medv   : num  24 21.6 34.7 33.4 36.2 28.7 22.9 27.1 16.5 18.9 ...
##  $ crim   : num  0.00632 0.02731 0.02729 0.03237 0.06905 ...
##  $ zn     : num  18 0 0 0 0 0 12.5 12.5 12.5 12.5 ...
##  $ indus  : num  2.31 7.07 7.07 2.18 2.18 2.18 7.87 7.87 7.87 7.87 ...
##  $ chas   : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ nox    : num  0.538 0.469 0.469 0.458 0.458 0.458 0.524 0.524 0.524 0.524 ...
##  $ rm     : num  6.58 6.42 7.18 7 7.15 ...
##  $ age    : num  65.2 78.9 61.1 45.8 54.2 58.7 66.6 96.1 100 85.9 ...
##  $ dis    : num  4.09 4.97 4.97 6.06 6.06 ...
##  $ rad    : int  1 2 2 3 3 3 5 5 5 5 ...
##  $ tax    : int  296 242 242 222 222 222 311 311 311 311 ...
##  $ ptratio: num  15.3 17.8 17.8 18.7 18.7 18.7 15.2 15.2 15.2 15.2 ...
##  $ lstat  : num  4.98 9.14 4.03 2.94 5.33 ...

Descriptive statistics

# we have a summary of the data
summary(data)
##       medv            crim                zn             indus       chas   
##  Min.   : 5.00   Min.   : 0.00632   Min.   :  0.00   Min.   : 0.46   0:471  
##  1st Qu.:17.02   1st Qu.: 0.08205   1st Qu.:  0.00   1st Qu.: 5.19   1: 35  
##  Median :21.20   Median : 0.25651   Median :  0.00   Median : 9.69          
##  Mean   :22.53   Mean   : 3.61352   Mean   : 11.36   Mean   :11.14          
##  3rd Qu.:25.00   3rd Qu.: 3.67708   3rd Qu.: 12.50   3rd Qu.:18.10          
##  Max.   :50.00   Max.   :88.97620   Max.   :100.00   Max.   :27.74          
##       nox               rm             age              dis        
##  Min.   :0.3850   Min.   :3.561   Min.   :  2.90   Min.   : 1.130  
##  1st Qu.:0.4490   1st Qu.:5.886   1st Qu.: 45.02   1st Qu.: 2.100  
##  Median :0.5380   Median :6.208   Median : 77.50   Median : 3.207  
##  Mean   :0.5547   Mean   :6.285   Mean   : 68.57   Mean   : 3.795  
##  3rd Qu.:0.6240   3rd Qu.:6.623   3rd Qu.: 94.08   3rd Qu.: 5.188  
##  Max.   :0.8710   Max.   :8.780   Max.   :100.00   Max.   :12.127  
##       rad              tax           ptratio          lstat      
##  Min.   : 1.000   Min.   :187.0   Min.   :12.60   Min.   : 1.73  
##  1st Qu.: 4.000   1st Qu.:279.0   1st Qu.:17.40   1st Qu.: 6.95  
##  Median : 5.000   Median :330.0   Median :19.05   Median :11.36  
##  Mean   : 9.549   Mean   :408.2   Mean   :18.46   Mean   :12.65  
##  3rd Qu.:24.000   3rd Qu.:666.0   3rd Qu.:20.20   3rd Qu.:16.95  
##  Max.   :24.000   Max.   :711.0   Max.   :22.00   Max.   :37.97
describe(data)
## # A tibble: 12 × 26
##    described_variables     n    na    mean      sd se_mean     IQR skewness
##    <chr>               <int> <int>   <dbl>   <dbl>   <dbl>   <dbl>    <dbl>
##  1 medv                  506     0  22.5     9.20  0.409     7.98     1.11 
##  2 crim                  506     0   3.61    8.60  0.382     3.60     5.22 
##  3 zn                    506     0  11.4    23.3   1.04     12.5      2.23 
##  4 indus                 506     0  11.1     6.86  0.305    12.9      0.295
##  5 nox                   506     0   0.555   0.116 0.00515   0.175    0.729
##  6 rm                    506     0   6.28    0.703 0.0312    0.738    0.404
##  7 age                   506     0  68.6    28.1   1.25     49.0     -0.599
##  8 dis                   506     0   3.80    2.11  0.0936    3.09     1.01 
##  9 rad                   506     0   9.55    8.71  0.387    20        1.00 
## 10 tax                   506     0 408.    169.    7.49    387        0.670
## 11 ptratio               506     0  18.5     2.16  0.0962    2.8     -0.802
## 12 lstat                 506     0  12.7     7.14  0.317    10.0      0.906
## # ℹ 18 more variables: kurtosis <dbl>, p00 <dbl>, p01 <dbl>, p05 <dbl>,
## #   p10 <dbl>, p20 <dbl>, p25 <dbl>, p30 <dbl>, p40 <dbl>, p50 <dbl>,
## #   p60 <dbl>, p70 <dbl>, p75 <dbl>, p80 <dbl>, p90 <dbl>, p95 <dbl>,
## #   p99 <dbl>, p100 <dbl>
# descriptive statistics of median values
#data %>% group_by(US,Urban) %>% describe(median_values)

Data visualization

Wised Graphs

For the data visualization, it was decided to use the variables that we considered to have more relevance for the dependent variable based on the exploratory analysis previously carried out. Among the variables, we identified 4 that exhibited the most pronounced curvature and displayed interesting behavioral patterns.

ggplot(data, aes(x = rm, y = medv)) +
  geom_point() +
  labs(x = "number of rooms",
       y = "Median Value of homes (USD)",
       title = "Relation between value and number of rooms") +
  theme_minimal()

In this first graph we can see the relationship between the independent variable “rm” (rooms) and the dependent variable “medv” (median value). With this graph we can identify a strong positive linear correlation between the average number of rooms per dwelling and the median value of owner-occupied homes.

ggplot(data, aes(x = crim, y = medv)) +
  geom_point() +
  labs(x = "Per Capita Crime Rate",
       y = "Median value of homes (USD)",
       title = "Relation between value and per capita crime rate") +
  theme_minimal()

In this second graph we can see the relationship between the independent variable “crim” and the dependent variable “medv”. It can be recognized a weak negative linear correlation, along with an interesting and pronounced inclination towards the value of 0 in per capita crime rate by town and its impact on the median value of owner-occupied homes.

ggplot(data, aes(x = lstat, y = medv)) +
  geom_point() +
  labs(x = "percentage of lower status of the population",
       y = "Median value of homes",
       title = "Relation between value and percentage of lower status of the population") +
  theme_minimal()

This third graph shows the relationship between the independent variable “lstat” and the dependent variable “medv”. A strong negative linear correlation can be observed between the percentage of lower status of the population and the median value of owner-occupied homes.

 data %>% mutate(age_intervals=cut(age,breaks=c(0,20,40,60,80,100))) %>%
  ggplot(aes(x=reorder(age_intervals,medv),y=medv,fill=age_intervals)) +
  geom_bar(stat="identity") + coord_flip()+
  scale_fill_brewer(palette="greens")+
  labs(x="Ocuppied-Homes Age", y="Median Value (USD)", color="Ocuppied-Homes Age") +
  ggtitle("Median Value by Ocuppied-Homes Age")
## Warning in pal_name(palette, type): Unknown palette greens

In this fourth graph, it is displayed the relationship between the independent variable “age” and the dependent variable “medv”. Age intervals have been added to make the graph easier to understand. There is a trend that the older the occupied-homes, the higher the median value.

Despite seeing a very slight negative linear correlation in the graph of the relationship between “crim” and “medv”, we consider that it is not significant enough to be a variable to be used in our hypotheses. Moreover, the curious and pronounced tendency to value 0 in per capita crime made us discard it.

Histogram dependent variable

hist(data$medv,prob=TRUE,col='steelblue',main='Histogram with median value')
lines(density(data$medv),col=3,lwd=4)

plot_normality(data,medv)

We can see in these last graphs the result of having an original histogram and one with a log in the dependent variable. For the original histogram, it displays a weak left-skewed distribution. When we try to normalize it using log, it displays a weak right-skewed distribution.

Correlation plot

data_alt1<- data %>% select(-chas,-rad) ### create sub-dataset including quantiative variables.  
summary(data_alt1)
##       medv            crim                zn             indus      
##  Min.   : 5.00   Min.   : 0.00632   Min.   :  0.00   Min.   : 0.46  
##  1st Qu.:17.02   1st Qu.: 0.08205   1st Qu.:  0.00   1st Qu.: 5.19  
##  Median :21.20   Median : 0.25651   Median :  0.00   Median : 9.69  
##  Mean   :22.53   Mean   : 3.61352   Mean   : 11.36   Mean   :11.14  
##  3rd Qu.:25.00   3rd Qu.: 3.67708   3rd Qu.: 12.50   3rd Qu.:18.10  
##  Max.   :50.00   Max.   :88.97620   Max.   :100.00   Max.   :27.74  
##       nox               rm             age              dis        
##  Min.   :0.3850   Min.   :3.561   Min.   :  2.90   Min.   : 1.130  
##  1st Qu.:0.4490   1st Qu.:5.886   1st Qu.: 45.02   1st Qu.: 2.100  
##  Median :0.5380   Median :6.208   Median : 77.50   Median : 3.207  
##  Mean   :0.5547   Mean   :6.285   Mean   : 68.57   Mean   : 3.795  
##  3rd Qu.:0.6240   3rd Qu.:6.623   3rd Qu.: 94.08   3rd Qu.: 5.188  
##  Max.   :0.8710   Max.   :8.780   Max.   :100.00   Max.   :12.127  
##       tax           ptratio          lstat      
##  Min.   :187.0   Min.   :12.60   Min.   : 1.73  
##  1st Qu.:279.0   1st Qu.:17.40   1st Qu.: 6.95  
##  Median :330.0   Median :19.05   Median :11.36  
##  Mean   :408.2   Mean   :18.46   Mean   :12.65  
##  3rd Qu.:666.0   3rd Qu.:20.20   3rd Qu.:16.95  
##  Max.   :711.0   Max.   :22.00   Max.   :37.97
corrplot(cor(data_alt1),type='upper',order='hclust',addCoef.col='black')

In this correlation plot, we can conclude that the variables with more relation to the dependent variable could be “rooms (rm)” and “percentage of lower status of the population (lstat)”. The first one, having a positive relation, and the second one with a negative.

#Just following example code
qqnorm(data$medv)

#qqline(data$medv)
#Just following example code
shapiro.test(data$medv) 
## 
##  Shapiro-Wilk normality test
## 
## data:  data$medv
## W = 0.91718, p-value = 4.941e-16

Hypotheses Statement

Based on this analysis, the 3 hypotheses that will be testing are:

  1. It might be expected a positive relationship between rooms and the dependent median value.

  2. It might be expected a positive relationship between age and the dependent median value.

  3. It might be expected a positive relationship between percentage of lower status of the population and the dependent median value.

Regression Analysis

Model 1:

Multiple Linear Regression Model

model1<-lm(medv ~ crim+chas+nox+rm+age+rad+tax+lstat,data=data)
summary(model1)  
## 
## Call:
## lm(formula = medv ~ crim + chas + nox + rm + age + rad + tax + 
##     lstat, data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -19.6771  -3.3446  -0.9797   2.0143  29.2534 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  2.036012   3.505576   0.581 0.561644    
## crim        -0.078708   0.036433  -2.160 0.031221 *  
## chas1        3.703134   0.960410   3.856 0.000131 ***
## nox         -0.318563   3.549298  -0.090 0.928519    
## rm           4.851746   0.445229  10.897  < 2e-16 ***
## age          0.016024   0.013327   1.202 0.229772    
## rad          0.159515   0.070014   2.278 0.023130 *  
## tax         -0.012569   0.003716  -3.382 0.000775 ***
## lstat       -0.575404   0.056107 -10.256  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 5.347 on 497 degrees of freedom
## Multiple R-squared:  0.6673, Adjusted R-squared:  0.662 
## F-statistic: 124.6 on 8 and 497 DF,  p-value: < 2.2e-16
vif(model1)
##     crim     chas      nox       rm      age      rad      tax    lstat 
## 1.734473 1.050971 2.987547 1.728364 2.485337 6.563916 6.927621 2.835217
bptest(model1)
## 
##  studentized Breusch-Pagan test
## 
## data:  model1
## BP = 49.467, df = 8, p-value = 5.173e-08
cat("AIC:", AIC(model1),"\n")
## AIC: 3143.586
cat("RMSE:",RMSE(model1$fitted.values,data$medv))
## RMSE: 5.299483

In this first model, none of the variables were modified in order to generate a multiple linear regression model. Giving as results an AIC of 3143.586 and a RMSE of 5.299483.

Model 2:

Linear Regression with log

model2<-lm(log(medv) ~ crim+chas+nox+rm+age+rad+tax+lstat,data=data)
summary(model2) 
## 
## Call:
## lm(formula = log(medv) ~ crim + chas + nox + rm + age + rad + 
##     tax + lstat, data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.71931 -0.12338 -0.02445  0.10533  0.90302 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  2.8413906  0.1381198  20.572  < 2e-16 ***
## crim        -0.0094408  0.0014355  -6.577 1.22e-10 ***
## chas1        0.1420779  0.0378402   3.755 0.000194 ***
## nox         -0.1024244  0.1398424  -0.732 0.464253    
## rm           0.1248146  0.0175420   7.115 3.92e-12 ***
## age          0.0008360  0.0005251   1.592 0.111985    
## rad          0.0083819  0.0027586   3.039 0.002503 ** 
## tax         -0.0006225  0.0001464  -4.252 2.53e-05 ***
## lstat       -0.0310938  0.0022106 -14.066  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2107 on 497 degrees of freedom
## Multiple R-squared:  0.7385, Adjusted R-squared:  0.7343 
## F-statistic: 175.5 on 8 and 497 DF,  p-value: < 2.2e-16
vif(model2)
##     crim     chas      nox       rm      age      rad      tax    lstat 
## 1.734473 1.050971 2.987547 1.728364 2.485337 6.563916 6.927621 2.835217
bptest(model2)
## 
##  studentized Breusch-Pagan test
## 
## data:  model2
## BP = 51.872, df = 8, p-value = 1.782e-08
cat("AIC:", AIC(model2),"\n")
## AIC: -129.2103
cat("RMSE:",RMSE(model2$fitted.values,data$medv))
## RMSE: 21.4379

In the second model, the dependent variable was transformed to log, due to the slight tendency to the right that the original “medv” histogram has, in order to focus on the relevant data only. Giving as results an AIC of -129.2103 and a RMSE of 21.4379.

Model 3:

Polynomial Linear Regression

model3<-lm(log(medv) ~ crim+chas+nox+rm+I(rm^2)+age+rad+tax+lstat,data=data)
summary(model3) 
## 
## Call:
## lm(formula = log(medv) ~ crim + chas + nox + rm + I(rm^2) + age + 
##     rad + tax + lstat, data = data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.99689 -0.10900 -0.00926  0.10129  0.92524 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  6.6503136  0.4061304  16.375  < 2e-16 ***
## crim        -0.0105005  0.0013181  -7.967 1.13e-14 ***
## chas1        0.1249682  0.0346740   3.604 0.000345 ***
## nox         -0.1889397  0.1282811  -1.473 0.141424    
## rm          -1.0552257  0.1206464  -8.746  < 2e-16 ***
## I(rm^2)      0.0913242  0.0092539   9.869  < 2e-16 ***
## age          0.0006277  0.0004810   1.305 0.192461    
## rad          0.0073956  0.0025266   2.927 0.003578 ** 
## tax         -0.0005392  0.0001343  -4.016 6.84e-05 ***
## lstat       -0.0312611  0.0020232 -15.451  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1928 on 496 degrees of freedom
## Multiple R-squared:  0.7815, Adjusted R-squared:  0.7775 
## F-statistic: 197.1 on 9 and 496 DF,  p-value: < 2.2e-16
plot(effect("I(rm^2)",model3))

cat("AIC:", AIC(model3),"\n")
## AIC: -217.9258
cat("RMSE:",RMSE(model3$fitted.values,data$medv))
## RMSE: 21.42701

In this third model, as in the second one, the dependent variable was transformed to log, but also, the independent “rm” variable was elevated to the square, in order to generate a polynomial linear regression. Giving as results an AIC of -217.9258 and a RMSE of 21.42701.

Despite performing both AIC and RMSE indicators (for even more assurance), we decided to base our decision on AIC, and the lowest value among the 3 models to generate the LASSO and RIDGE methods with the intention of ensuring even more confidence in the data.

Validate Hypotheses Based on the results of our model,we can validate the hypotheses that we proposed.

For number 1, we can see that it is a positive relation, but also it is a negative relation between 4 and 6 rooms. After 6 rooms, the median value starts increasing.

For number 2, we can see that it is a positive relation, but this variable seems not to be statistically significant for the model or the prediction.

For number 3, we can confirm that it is a negative relation, and this variable is statistically significant.

Analyzing the results of the regression model, it could be interesting to study the relation between the per capita crime rate and the median value of homes, and the difference between the median value of homes if they bound river or they doesn’t. These are variables statistically significant that could be interesting to consider and use for prediction.

Lasso And Ridge regression models.

Lasso

set.seed(123)                                  ### sets the random seed for reproducibility of results
training.samples<-data$medv %>%
  createDataPartition(p=0.75,list=FALSE)       ### Lets consider 75% of the data to build a predictive model

train.data<-data[training.samples, ]  ### training data to fit the linear regression model 
test.data<-data[-training.samples, ]  ### testing data to test the linear regression model         
# LASSO regression via glmnet package can only take numerical observations. Then, the dataset is transformed to model.matrix() format. 
# Independent variables
x<-model.matrix(lm(log(medv) ~ crim+chas+nox+rm+I(rm^2)+age+rad+tax+lstat,data=train.data))[,-1] ### OLS model specification

# x<-model.matrix(Weekly_Sales~.,train.data)[,-1] ### matrix of independent variables X's
y<-train.data$medv ### dependent variable 

# In estimating LASSO regression it is important to define the lambda that minimizes the prediction error rate. 
# Cross-validation ensures that every data / observation from the original dataset (datains) has a chance of appearing in train and test datasets.
# Find the best lambda using cross-validation.
set.seed(123) 
cv.lasso<-cv.glmnet(x,y,alpha=1) # alpha = 1 for LASSO

# Display the best lambda value
cv.lasso$lambda.min                      ### lambda: a numeric value defining the amount of shrinkage. Why min? the higher the value of ?? , the more penalization there is
## [1] 0.003463957
# Fit the final model on the training data
lassomodel<-glmnet(x,y,alpha=1,lambda=cv.lasso$lambda.min)

# Display regression coefficients
coef(lassomodel)
## 10 x 1 sparse Matrix of class "dgCMatrix"
##                        s0
## (Intercept) 107.159703920
## crim         -0.094372253
## chas1         1.665556325
## nox          -3.791411782
## rm          -27.811138545
## I(rm^2)       2.541386933
## age           0.001948774
## rad           0.080581862
## tax          -0.010997326
## lstat        -0.472514556
# Make predictions on the test data
x.test<-model.matrix(lm(log(medv) ~ crim+chas+nox+rm+I(rm^2)+age+rad+tax+lstat,data=test.data))[,-1] ### OLS model specification
# x.test<-model.matrix(Weekly_Sales~.,test.data)[,-1]
lassopredictions <- lassomodel %>% predict(x.test) %>% as.vector()

# Model Accuracy
data.frame(
  RMSE = RMSE(lassopredictions, test.data$medv),
  Rsquare = R2(lassopredictions, test.data$medv))
##       RMSE   Rsquare
## 1 6.348638 0.6955881
### visualizing lasso regression results 
lbs_fun <- function(fit, offset_x=1, ...) {
  L <- length(fit$lambda)
  x <- log(fit$lambda[L])+ offset_x
  y <- fit$beta[, L]
  labs <- names(y)
  text(x, y, labels=labs, ...)
}

lasso<-glmnet(scale(x),y,alpha=1)

plot(lasso,xvar="lambda",label=T)
lbs_fun(lasso)
abline(v=cv.lasso$lambda.min,col="red",lty=2)
abline(v=cv.lasso$lambda.1se,col="blue",lty=2)

RIDGE

x<-model.matrix(lm(log(medv) ~ crim+chas+nox+rm+I(rm^2)+age+rad+tax+lstat,data=train.data))[,-1] ### OLS model specification

y<-train.data$medv ### dependent variable 


# Find the best lambda using cross-validation
set.seed(123)                           # x: independent variables | y: dependent variable 
cv.ridge <- cv.glmnet(x,y,alpha=0.1)      # alpha = 0 for RIDGE

# Display the best lambda value
cv.ridge$lambda.min                     # lambda: a numeric value defining the amount of shrinkage. Why min? the higher the value of ?? , the more penalization there is
## [1] 0.006490823
# Fit the final model on the training data
ridgemodel<-glmnet(x,y,alpha=0,lambda=cv.ridge$lambda.min)

# Display regression coefficients
coef(ridgemodel)
## 10 x 1 sparse Matrix of class "dgCMatrix"
##                        s0
## (Intercept)  98.326184204
## crim         -0.091979663
## chas1         1.738663587
## nox          -3.670704647
## rm          -25.054783990
## I(rm^2)       2.327573391
## age           0.003100005
## rad           0.087788439
## tax          -0.011395736
## lstat        -0.472553210
# Make predictions on the test data

x.test<-model.matrix(lm(log(medv) ~ crim+chas+nox+rm+I(rm^2)+age+rad+tax+lstat,data=test.data))[,-1]

ridgepredictions<-ridgemodel %>% predict(x.test) %>% as.vector()

# Model Accuracy
data.frame(
  RMSE = RMSE(ridgepredictions, test.data$medv),
  Rsquare = R2(ridgepredictions, test.data$medv)
)
##       RMSE  Rsquare
## 1 6.391347 0.691403
### visualizing ridge regression results 

ridge<-glmnet(scale(x),y,alpha=0)

plot(ridge, xvar = "lambda", label=T)
lbs_fun(ridge)
abline(v=cv.ridge$lambda.min, col = "red", lty=2)
abline(v=cv.ridge$lambda.1se, col="blue", lty=2)

tab <- matrix(c(17587,6324,6322,0.77,0.71,0.71), ncol=2, byrow=FALSE)
colnames(tab) <- c('RMSE','R2')
rownames(tab) <- c('Linear Regression','Lasso','Ridge')
tab <- as.table(tab)

Results discussion

We could see as a team that linear regression analysis can contribute to improve predictive analytics in different ways. It helps to understand de relationship that exists between one or more independent variables and the dependent variable. Knowing how to interpret the results of a linear regression, it is possible to evaluate these relationships, and the impact that one variable could have into the dependent variable.

It also helps to identify if there is a positive or negative relationship and which variables that have a significant impact on the dependent variable. Another contribution is the insights that can be generated from the analysis, that can be supported by the models created. It is a very useful tool in order to make informed and assertive predictions.

In our analysis, we employed both Lasso and Ridge regularization techniques to assess the relevance of input features in our model. While both methods provided valuable insights, we have chosen to prioritize the results obtained through Ridge regression. Our decision stems from the fact that, in the context of our specified model, the majority of attributes have been found to be relevant in relation to the hypotheses we established.

The weighted average error of 6.3913, when compared to the average actual median values of 21.20 in the dataset, suggests that our model’s predictions are reasonably accurate. This strengthens our confidence in the model’s performance.

Upon examining the Lasso results, we observed that variables such as “lstat,” “rm squared,” “tax,” and “rad” were identified as particularly relevant, given their higher associated coefficients. Moreover, the characteristic of Lasso to eliminate non-relevant variables, indicated by their dotted line (0 value), showcases its effectiveness in feature selection.

Overall, our analysis underscores the pivotal role of linear regression in enhancing predictive analytics. By comprehending the relationships between independent and dependent variables, we can gauge their impacts and enhance our understanding of the underlying dynamics. This knowledge equips us with the ability to make informed decisions and refine our predictive models effectively.

According to IBM, these are some key assumptions to be considered for success with linear-regression analysis:

For each variable: Consider the number of valid cases, mean and standard deviation.

For each model: Consider regression coefficients, correlation matrix, part and partial correlations, multiple R, R2, adjusted R2, change in R2, standard error of the estimate, analysis-of-variance table, predicted values and residuals. Also, consider 95-percent-confidence intervals for each regression coefficient, variance-covariance matrix, variance inflation factor, tolerance, Durbin-Watson test, distance measures (Mahalanobis, Cook and leverage values), DfBeta, DfFit, prediction intervals and case-wise diagnostic information.

Plots: Consider scatterplots, partial plots, histograms and normal probability plots.

Data: Dependent and independent variables should be quantitative. Categorical variables, such as religion, major field of study or region of residence, need to be recoded to binary (dummy) variables or other types of contrast variables.

Other assumptions: For each value of the independent variable, the distribution of the dependent variable must be normal. The variance of the distribution of the dependent variable should be constant for all values of the independent variable. The relationship between the dependent variable and each independent variable should be linear and all observations should be independent.

(About Linear Regression | IBM, 2023)

About Linear Regression | IBM. (2023). Ibm.com. https://www.ibm.com/topics/linear-regression

LS0tDQp0aXRsZTogIkFzc2lnbm1lbnQgMSINCmRhdGU6ICIyMDIzLTA4LTIyIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICANCi0tLQ0KRGFuaWVsIEZhcsOtYXMgfCBBMDEyMzYzMjcNCg0KS2F0aGlhIFJ1aXogfCBBMDE1NzEwOTQNCg0KTmFpbGEgU2FsaW5hcyB8IEEwMDgzMjcwMiANCg0KU29maWEgQmFkaWxsbyB8IEEwMjM4NDI1Mw0KDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQojaW5zdGFsbC5wYWNrYWdlcygidmlzZGF0IikNCiNpbnN0YWxsLnBhY2thZ2VzKCJmb3JlaWduIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uIA0KI2luc3RhbGwucGFja2FnZXMoImZvcmNhdHMiKSAgICAgICMgdG8gd29yayB3aXRoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcw0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAgICAgICMgZGF0YSB2aXN1YWxpemF0aW9uIA0KIyNpbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpICAgICAgICAjIHJlYWQgc3BlY2lmaWMgY3N2IGZpbGVzDQojaW5zdGFsbC5wYWNrYWdlcygiamFuaXRvciIpICAgICAgIyBkYXRhIGV4cGxvcmF0aW9uIGFuZCBjbGVhbmluZyANCiNpbnN0YWxsLnBhY2thZ2VzKCJIbWlzYyIpICAgICAgICAjIHNldmVyYWwgdXNlZnVsIGZ1bmN0aW9ucyBmb3IgZGF0YSBhbmFseXNpcyANCiNpbnN0YWxsLnBhY2thZ2VzKCJwc3ljaCIpICAgICAgICAjIGZ1bmN0aW9ucyBmb3IgbXVsdGl2YXJpYXRlIGFuYWx5c2lzIA0KI2luc3RhbGwucGFja2FnZXMoIm5hbmlhciIpICAgICAgICMgc3VtbWFyaWVzIGFuZCB2aXN1YWxpemF0aW9uIG9mIG1pc3NpbmcgdmFsdWVzIE5Bcw0KI2luc3RhbGwucGFja2FnZXMoImRsb29rciIpICAgICAgICMgc3VtbWFyaWVzIGFuZCB2aXN1YWxpemF0aW9uIG9mIG1pc3NpbmcgdmFsdWVzIE5Bcw0KI2luc3RhbGwucGFja2FnZXMoImNvcnJwbG90IikgICAgICMgY29ycmVsYXRpb24gcGxvdHMNCiNpbnN0YWxsLnBhY2thZ2VzKCJqdG9vbHMiKSAgICAgICAjIHByZXNlbnRhdGlvbiBvZiByZWdyZXNzaW9uIGFuYWx5c2lzIA0KI2luc3RhbGwucGFja2FnZXMoImxtdGVzdCIpICAgICAgICMgZGlhZ25vc3RpYyBjaGVja3MgLSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcyANCiNpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKSAgICAgICAgICAjIGRpYWdub3N0aWMgY2hlY2tzIC0gbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMNCiNpbnN0YWxsLnBhY2thZ2VzKCJvbHNyciIpICAgICAgICAjIGRpYWdub3N0aWMgY2hlY2tzIC0gbGluZWFyIHJlZ3Jlc3Npb24gYW5hbHlzaXMgDQojaW5zdGFsbC5wYWNrYWdlcygia2FibGVFeHRyYSIpICAgIyBIVE1MIHRhYmxlIGF0dHJpYnV0ZXMNCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KI2luc3RhbGwucGFja2FnZXMoImNhcmV0IikNCiNpbnN0YWxsLnBhY2thZ2VzKCJnbG1uZXQiKQ0KI2luc3RhbGwucGFja2FnZXMoImVmZmVjdHMiKQ0KDQpsaWJyYXJ5KGVmZmVjdHMpDQpsaWJyYXJ5KHZpc2RhdCkNCmxpYnJhcnkoZm9yZWlnbikNCmxpYnJhcnkoZHBseXIpICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uIA0KbGlicmFyeShmb3JjYXRzKSAgICAgICMgdG8gd29yayB3aXRoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcw0KbGlicmFyeShnZ3Bsb3QyKSAgICAgICMgZGF0YSB2aXN1YWxpemF0aW9uIA0KbGlicmFyeShyZWFkcikgICAgICAgICMgcmVhZCBzcGVjaWZpYyBjc3YgZmlsZXMNCmxpYnJhcnkoamFuaXRvcikgICAgICAjIGRhdGEgZXhwbG9yYXRpb24gYW5kIGNsZWFuaW5nIA0KbGlicmFyeShIbWlzYykgICAgICAgICMgc2V2ZXJhbCB1c2VmdWwgZnVuY3Rpb25zIGZvciBkYXRhIGFuYWx5c2lzIA0KbGlicmFyeShwc3ljaCkgICAgICAgICMgZnVuY3Rpb25zIGZvciBtdWx0aXZhcmlhdGUgYW5hbHlzaXMgDQpsaWJyYXJ5KG5hbmlhcikgICAgICAgIyBzdW1tYXJpZXMgYW5kIHZpc3VhbGl6YXRpb24gb2YgbWlzc2luZyB2YWx1ZXMgTkFzDQpsaWJyYXJ5KGRsb29rcikgICAgICAgIyBzdW1tYXJpZXMgYW5kIHZpc3VhbGl6YXRpb24gb2YgbWlzc2luZyB2YWx1ZXMgTkFzDQpsaWJyYXJ5KGNvcnJwbG90KSAgICAgIyBjb3JyZWxhdGlvbiBwbG90cw0KbGlicmFyeShqdG9vbHMpICAgICAgICMgcHJlc2VudGF0aW9uIG9mIHJlZ3Jlc3Npb24gYW5hbHlzaXMgDQpsaWJyYXJ5KGxtdGVzdCkgICAgICAgIyBkaWFnbm9zdGljIGNoZWNrcyAtIGxpbmVhciByZWdyZXNzaW9uIGFuYWx5c2lzIA0KbGlicmFyeShjYXIpICAgICAgICAgICMgZGlhZ25vc3RpYyBjaGVja3MgLSBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcw0KbGlicmFyeShrYWJsZUV4dHJhKSAgICMgSFRNTCB0YWJsZSBhdHRyaWJ1dGVzDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGdsbW5ldCkNCmBgYA0KDQpgYGB7cn0NCiMgaW1wb3J0IGRhdGFzZXQNCmRhdGEgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcZGFueWJcXE9uZURyaXZlIC0gSW5zdGl0dXRvIFRlY25vbG9naWNvIHkgZGUgRXN0dWRpb3MgU3VwZXJpb3JlcyBkZSBNb250ZXJyZXlcXERvY3NcXERvY3VtZW50b3NcXEJ1c2luZXNzIEludGVsbGlnZW5jZVxcUXVpbnRvIFNlbWVzdHJlXFxJbnRyb2R1Y3Rpb24gdG8gRWNvbm9tZXRyaWNzXFxyZWFsX2VzdGF0ZV9kYXRhLmNzdiIpDQpgYGANCg0KDQojIEEuIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgDQoNCiMjIERhdGEgZGVzY3JpcHRpb24NCg0KLSAqKm1lZGlhbl92YWx1ZSoqIC0JbWVkaWFuIHZhbHVlIG9mIG93bmVyLW9jY3VwaWVkIGhvbWVzIGluIFVTRCAxMDAwJ3MNCi0gKipjcmltZV9yYXRlKiogLQlwZXIgY2FwaXRhIGNyaW1lIHJhdGUgYnkgdG93bg0KLSAqKnJlc2lkZW50aWFsX2xhbmQqKiAtCXByb3BvcnRpb24gb2YgcmVzaWRlbnRpYWwgbGFuZCB6b25lZCBmb3IgbG90cyBvdmVyIDI1LDAwMCBzcS5mdA0KLSAqKm5vbl9yZXRhaWxfYnVzaW5lc3MqKiAtCXByb3BvcnRpb24gb2Ygbm9uLXJldGFpbCBidXNpbmVzcyBhY3JlcyBwZXIgdG93bg0KLSAqKnJpdmVyX3ZpZXcqKiAtCUR1bW15IHZhcmlhYmxlICg9IDEgaWYgbmVpZ2hib3Job29kIGJvdW5kcyByaXZlcjsgMCBvdGhlcndpc2UpDQotICoqbml0cmljX294aWQqKgktIG5pdHJpYyBveGlkZXMgY29uY2VudHJhdGlvbiAocGFydHMgcGVyIDEwIG1pbGxpb24pDQotICoqcm9vbXMqKgktIGF2ZXJhZ2UgbnVtYmVyIG9mIHJvb21zIHBlciBkd2VsbGluZw0KLSAqKmFnZSoqIC0JcHJvcG9ydGlvbiBvZiBvd25lci1vY2N1cGllZCB1bml0cyBidWlsdCBwcmlvciB0byAxOTQwDQotICoqZGlzdGFuY2UqKiAtCXdlaWdodGVkIGRpc3RhbmNlcyB0byBmaXZlIEJvc3RvbiBlbXBsb3ltZW50IGNlbnRlcnMNCi0gKiphY2Nlc3NfdG9faGlnaHdheXMqKiAtIAlpbmRleCBvZiBhY2Nlc3NpYmlsaXR5IHRvIHJhZGlhbCBoaWdod2F5cw0KLSAqKnByb3BlcnR5X3RheF9yYXRlKiogLSAJZnVsbC12YWx1ZSBwcm9wZXJ0eS10YXggcmF0ZSBwZXIgVVNEIDEwLDAwMA0KLSAqKnNjaG9vbCoqIC0JcHVwaWwtdGVhY2hlciByYXRpbyBieSB0b3duDQotICoqbG93X3N0YXR1c19wb3AqKglwZXJjZW50YWdlIG9mIGxvd2VyIHN0YXR1cyBvZiB0aGUgcG9wdWxhdGlvbg0KDQojIyBJZGVudHlmaW5nIG1pc3NpbmcgdmFsdWVzDQpgYGB7cn0NCiMgd2UgZXhwbG9yZSBkZSBkYXRhIHNldCB0byBzZWUgaWYgdGhlcmUgYXJlIG1pc3NpbmcgdmFsdWVzDQoNCiMgc3VtIGFsbCB0aGUgbmEgaW4gdGhlIGhvbGUgZGF0YXNldCANCnN1bShpcy5uYShkYXRhKSkNCg0KIyBzaG93cyB0aGUgc3VtIG9mIHRoZSBuYSBwZXIgdmFyaWFibGUNCmNvbFN1bXMoaXMubmEoZGF0YSkpDQoNCiMgcGxvdCB0aGUgbmEgcGVyIHZhcmlhYmxlDQojZ2dfbWlzc192YXIoZGF0YSkNCmBgYA0KDQpXZSBjYW4gY29uY2x1ZGUgdGhhdCB0aGVyZSBhcmUgbm8gTkEgb3IgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQNCg0KIyMgU3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0DQoNCmBgYHtyfQ0Kc3RyKGRhdGEpDQpgYGANCldlIGhhdmUgYSBkYXRhc2V0IGNvbXBvc2VkIGJ5IDE1IHZhcmlhYmxlcyB3aXRoIDUwNiBvYnNlcnZhdGlvbnMNCg0KRWFjaCB2YXJpYWJsZSBzdGFuZHMgZm9yIA0KICAqKm1lZHYqKiAgIDogbnVtICBtZWRpYW5fdmFsdWUNCiAgKipjcmltKiogICA6IG51bSAgY3JpbWVfcmF0ZSAgDQogICoqem4qKiAgICAgOiBudW0gIHJlc2lkZW50aWFsX2xhbmQNCiAgKippbmR1cyoqICA6IG51bSAgbm9uX3JldGFpbF9idXNpbmVzcw0KICAqKmNoYXMqKiAgIDogaW50ICByaXZlcl92aWV3DQogICoqbm94KiogICAgOiBudW0gIG5pdHJpY19veGlkDQogICoqcm0qKiAgICAgOiBudW0gIHJvb21zDQogICoqYWdlKiogICAgOiBudW0gIGFnZQ0KICAqKmRpcyoqICAgIDogbnVtICBkaXN0YW5jZQ0KICAqKnJhZCoqICAgIDogaW50ICBhY2Nlc3NfdG9faGlnaHdheXMNCiAgKip0YXgqKiAgICA6IGludCAgcHJvcGVydHlfdGF4X3JhdGUNCiAgKipwdHJhdGlvKiogOiBudW0gIHNjaG9vbA0KICAqKmxzdGF0KiogOiBudW0gIGxvd19zdGF0dXNfcG9wIA0KDQojIyBUcmFzZm9ybWF0aW9uIG9mIHZhcmlhYmxlcw0KDQpBcyB3ZSBzZWUgYmVmb3JlIGluIHRoZSBkYXRhIGRlc2NyaXB0aW9uIG9mIHRoZSBkYXRhIHNldCwgKmNoYXMqIGlzIHJpdmVyX3ZpZXcgd2hpY2ggaXMgYSBkdW1teSB2YXJpYWJsZSwgYW5kICpyYWQqIGlzIGFjY2Vzc190b19oaWdod2F5cyB0aGF0IGlzIGEgcmF0ZSBmb3JtIDEgdG8gOCBhbmQgMjQuIFNvIHdlIGhhdmUgdG8gdHJhbnNmb3JtIHRoaXMgdmFyaWFibGVzLiBBbHNvIHdlIGFyZSBnb2luZyB0byBlbGltaW5hdGUgZGUgdmFyaWFibGVzICJjbWVkdiIgYW5kICJiIg0KDQpgYGB7cn0NCmRhdGEkY2hhcyA8LSBhcy5mYWN0b3IoZGF0YSRjaGFzKQ0KZGF0YSA8LSBkYXRhWywgIShuYW1lcyhkYXRhKSAlaW4lIGMoImNtZWR2IiwgImIiKSldDQojZGF0YSRyYWQgIDwtIGFzLmZhY3RvcihkYXRhJHJhZCkgDQpzdHIoZGF0YSkNCmBgYA0KDQojIyBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzDQoNCmBgYHtyfQ0KIyB3ZSBoYXZlIGEgc3VtbWFyeSBvZiB0aGUgZGF0YQ0Kc3VtbWFyeShkYXRhKQ0KYGBgDQoNCmBgYHtyfQ0KZGVzY3JpYmUoZGF0YSkNCmBgYA0KDQpgYGB7cn0NCiMgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBvZiBtZWRpYW4gdmFsdWVzDQojZGF0YSAlPiUgZ3JvdXBfYnkoVVMsVXJiYW4pICU+JSBkZXNjcmliZShtZWRpYW5fdmFsdWVzKQ0KYGBgDQoNCiMgRGF0YSB2aXN1YWxpemF0aW9uDQojIyBXaXNlZCBHcmFwaHMNCg0KRm9yIHRoZSBkYXRhIHZpc3VhbGl6YXRpb24sIGl0IHdhcyBkZWNpZGVkIHRvIHVzZSB0aGUgdmFyaWFibGVzIHRoYXQgd2UgY29uc2lkZXJlZCB0byBoYXZlIG1vcmUgcmVsZXZhbmNlIGZvciB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGJhc2VkIG9uIHRoZSBleHBsb3JhdG9yeSBhbmFseXNpcyBwcmV2aW91c2x5IGNhcnJpZWQgb3V0LiBBbW9uZyB0aGUgdmFyaWFibGVzLCB3ZSBpZGVudGlmaWVkIDQgdGhhdCBleGhpYml0ZWQgdGhlIG1vc3QgcHJvbm91bmNlZCBjdXJ2YXR1cmUgYW5kIGRpc3BsYXllZCBpbnRlcmVzdGluZyBiZWhhdmlvcmFsIHBhdHRlcm5zLg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gcm0sIHkgPSBtZWR2KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHggPSAibnVtYmVyIG9mIHJvb21zIiwNCiAgICAgICB5ID0gIk1lZGlhbiBWYWx1ZSBvZiBob21lcyAoVVNEKSIsDQogICAgICAgdGl0bGUgPSAiUmVsYXRpb24gYmV0d2VlbiB2YWx1ZSBhbmQgbnVtYmVyIG9mIHJvb21zIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KSW4gdGhpcyBmaXJzdCBncmFwaCB3ZSBjYW4gc2VlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUgInJtIiAocm9vbXMpIGFuZCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlICJtZWR2IiAobWVkaWFuIHZhbHVlKS4gV2l0aCB0aGlzIGdyYXBoIHdlIGNhbiBpZGVudGlmeSBhIHN0cm9uZyBwb3NpdGl2ZSBsaW5lYXIgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgYXZlcmFnZSBudW1iZXIgb2Ygcm9vbXMgcGVyIGR3ZWxsaW5nIGFuZCB0aGUgbWVkaWFuIHZhbHVlIG9mIG93bmVyLW9jY3VwaWVkIGhvbWVzLg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gY3JpbSwgeSA9IG1lZHYpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGxhYnMoeCA9ICJQZXIgQ2FwaXRhIENyaW1lIFJhdGUiLA0KICAgICAgIHkgPSAiTWVkaWFuIHZhbHVlIG9mIGhvbWVzIChVU0QpIiwNCiAgICAgICB0aXRsZSA9ICJSZWxhdGlvbiBiZXR3ZWVuIHZhbHVlIGFuZCBwZXIgY2FwaXRhIGNyaW1lIHJhdGUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpJbiB0aGlzIHNlY29uZCBncmFwaCB3ZSBjYW4gc2VlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUgImNyaW0iIGFuZCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlICJtZWR2Ii4gSXQgY2FuIGJlIHJlY29nbml6ZWQgYSB3ZWFrIG5lZ2F0aXZlIGxpbmVhciBjb3JyZWxhdGlvbiwgYWxvbmcgd2l0aCBhbiBpbnRlcmVzdGluZyBhbmQgcHJvbm91bmNlZCBpbmNsaW5hdGlvbiB0b3dhcmRzIHRoZSB2YWx1ZSBvZiAwIGluIHBlciBjYXBpdGEgY3JpbWUgcmF0ZSBieSB0b3duIGFuZCBpdHMgaW1wYWN0IG9uIHRoZSBtZWRpYW4gdmFsdWUgb2Ygb3duZXItb2NjdXBpZWQgaG9tZXMuDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBsc3RhdCwgeSA9IG1lZHYpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGxhYnMoeCA9ICJwZXJjZW50YWdlIG9mIGxvd2VyIHN0YXR1cyBvZiB0aGUgcG9wdWxhdGlvbiIsDQogICAgICAgeSA9ICJNZWRpYW4gdmFsdWUgb2YgaG9tZXMiLA0KICAgICAgIHRpdGxlID0gIlJlbGF0aW9uIGJldHdlZW4gdmFsdWUgYW5kIHBlcmNlbnRhZ2Ugb2YgbG93ZXIgc3RhdHVzIG9mIHRoZSBwb3B1bGF0aW9uIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KVGhpcyB0aGlyZCBncmFwaCBzaG93cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlICJsc3RhdCIgYW5kIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgIm1lZHYiLiBBIHN0cm9uZyBuZWdhdGl2ZSBsaW5lYXIgY29ycmVsYXRpb24gY2FuIGJlIG9ic2VydmVkIGJldHdlZW4gdGhlIHBlcmNlbnRhZ2Ugb2YgbG93ZXIgc3RhdHVzIG9mIHRoZSBwb3B1bGF0aW9uIGFuZCB0aGUgbWVkaWFuIHZhbHVlIG9mIG93bmVyLW9jY3VwaWVkIGhvbWVzLg0KDQoNCmBgYHtyfQ0KIGRhdGEgJT4lIG11dGF0ZShhZ2VfaW50ZXJ2YWxzPWN1dChhZ2UsYnJlYWtzPWMoMCwyMCw0MCw2MCw4MCwxMDApKSkgJT4lDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKGFnZV9pbnRlcnZhbHMsbWVkdikseT1tZWR2LGZpbGw9YWdlX2ludGVydmFscykpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArIGNvb3JkX2ZsaXAoKSsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iZ3JlZW5zIikrDQogIGxhYnMoeD0iT2N1cHBpZWQtSG9tZXMgQWdlIiwgeT0iTWVkaWFuIFZhbHVlIChVU0QpIiwgY29sb3I9Ik9jdXBwaWVkLUhvbWVzIEFnZSIpICsNCiAgZ2d0aXRsZSgiTWVkaWFuIFZhbHVlIGJ5IE9jdXBwaWVkLUhvbWVzIEFnZSIpDQpgYGANCkluIHRoaXMgZm91cnRoIGdyYXBoLCBpdCBpcyBkaXNwbGF5ZWQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZSAiYWdlIiBhbmQgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSAibWVkdiIuIEFnZSBpbnRlcnZhbHMgaGF2ZSBiZWVuIGFkZGVkIHRvIG1ha2UgdGhlIGdyYXBoIGVhc2llciB0byB1bmRlcnN0YW5kLiBUaGVyZSBpcyBhIHRyZW5kIHRoYXQgdGhlIG9sZGVyIHRoZSBvY2N1cGllZC1ob21lcywgdGhlIGhpZ2hlciB0aGUgbWVkaWFuIHZhbHVlLg0KDQpEZXNwaXRlIHNlZWluZyBhIHZlcnkgc2xpZ2h0IG5lZ2F0aXZlIGxpbmVhciBjb3JyZWxhdGlvbiBpbiB0aGUgZ3JhcGggb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICJjcmltIiBhbmQgIm1lZHYiLCB3ZSBjb25zaWRlciB0aGF0IGl0IGlzIG5vdCBzaWduaWZpY2FudCBlbm91Z2ggdG8gYmUgYSB2YXJpYWJsZSB0byBiZSB1c2VkIGluIG91ciBoeXBvdGhlc2VzLiBNb3Jlb3ZlciwgdGhlIGN1cmlvdXMgYW5kIHByb25vdW5jZWQgdGVuZGVuY3kgdG8gdmFsdWUgMCBpbiBwZXIgY2FwaXRhIGNyaW1lIG1hZGUgdXMgZGlzY2FyZCBpdC4NCg0KIyMgSGlzdG9ncmFtIGRlcGVuZGVudCB2YXJpYWJsZSANCmBgYHtyfQ0KaGlzdChkYXRhJG1lZHYscHJvYj1UUlVFLGNvbD0nc3RlZWxibHVlJyxtYWluPSdIaXN0b2dyYW0gd2l0aCBtZWRpYW4gdmFsdWUnKQ0KbGluZXMoZGVuc2l0eShkYXRhJG1lZHYpLGNvbD0zLGx3ZD00KQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdF9ub3JtYWxpdHkoZGF0YSxtZWR2KQ0KYGBgDQoNCldlIGNhbiBzZWUgaW4gdGhlc2UgbGFzdCBncmFwaHMgdGhlIHJlc3VsdCBvZiBoYXZpbmcgYW4gb3JpZ2luYWwgaGlzdG9ncmFtIGFuZCBvbmUgd2l0aCBhIGxvZyBpbiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLiBGb3IgdGhlIG9yaWdpbmFsIGhpc3RvZ3JhbSwgaXQgZGlzcGxheXMgYSB3ZWFrIGxlZnQtc2tld2VkIGRpc3RyaWJ1dGlvbi4gV2hlbiB3ZSB0cnkgdG8gbm9ybWFsaXplIGl0IHVzaW5nIGxvZywgaXQgZGlzcGxheXMgYSB3ZWFrIHJpZ2h0LXNrZXdlZCBkaXN0cmlidXRpb24uDQoNCiMjIENvcnJlbGF0aW9uIHBsb3QNCg0KYGBge3J9DQpkYXRhX2FsdDE8LSBkYXRhICU+JSBzZWxlY3QoLWNoYXMsLXJhZCkgIyMjIGNyZWF0ZSBzdWItZGF0YXNldCBpbmNsdWRpbmcgcXVhbnRpYXRpdmUgdmFyaWFibGVzLiAgDQpzdW1tYXJ5KGRhdGFfYWx0MSkNCg0KY29ycnBsb3QoY29yKGRhdGFfYWx0MSksdHlwZT0ndXBwZXInLG9yZGVyPSdoY2x1c3QnLGFkZENvZWYuY29sPSdibGFjaycpDQpgYGANCg0KSW4gdGhpcyBjb3JyZWxhdGlvbiBwbG90LCB3ZSBjYW4gY29uY2x1ZGUgdGhhdCB0aGUgdmFyaWFibGVzIHdpdGggbW9yZSByZWxhdGlvbiB0byB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGNvdWxkIGJlICJyb29tcyAocm0pIiBhbmQgInBlcmNlbnRhZ2Ugb2YgbG93ZXIgc3RhdHVzIG9mIHRoZSBwb3B1bGF0aW9uIChsc3RhdCkiLiBUaGUgZmlyc3Qgb25lLCBoYXZpbmcgYSBwb3NpdGl2ZSByZWxhdGlvbiwgYW5kIHRoZSBzZWNvbmQgb25lIHdpdGggYSBuZWdhdGl2ZS4NCg0KYGBge3J9DQojSnVzdCBmb2xsb3dpbmcgZXhhbXBsZSBjb2RlDQpxcW5vcm0oZGF0YSRtZWR2KQ0KI3FxbGluZShkYXRhJG1lZHYpDQpgYGANCg0KYGBge3J9DQojSnVzdCBmb2xsb3dpbmcgZXhhbXBsZSBjb2RlDQpzaGFwaXJvLnRlc3QoZGF0YSRtZWR2KSANCmBgYA0KDQoNCg0KIyBIeXBvdGhlc2VzIFN0YXRlbWVudA0KDQpCYXNlZCBvbiB0aGlzIGFuYWx5c2lzLCB0aGUgMyBoeXBvdGhlc2VzIHRoYXQgd2lsbCBiZSB0ZXN0aW5nIGFyZToNCg0KDQoxLiBJdCBtaWdodCBiZSBleHBlY3RlZCBhIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHJvb21zIGFuZCB0aGUgZGVwZW5kZW50IG1lZGlhbiB2YWx1ZS4NCg0KMi4gSXQgbWlnaHQgYmUgZXhwZWN0ZWQgYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhZ2UgYW5kIHRoZSBkZXBlbmRlbnQgbWVkaWFuIHZhbHVlLg0KDQozLiBJdCBtaWdodCBiZSBleHBlY3RlZCBhIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHBlcmNlbnRhZ2Ugb2YgbG93ZXIgc3RhdHVzIG9mIHRoZSBwb3B1bGF0aW9uIGFuZCB0aGUgZGVwZW5kZW50IG1lZGlhbiB2YWx1ZS4NCg0KDQoNCg0KDQojIFJlZ3Jlc3Npb24gQW5hbHlzaXMNCiMjIE1vZGVsIDE6DQpNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbA0KYGBge3J9DQptb2RlbDE8LWxtKG1lZHYgfiBjcmltK2NoYXMrbm94K3JtK2FnZStyYWQrdGF4K2xzdGF0LGRhdGE9ZGF0YSkNCnN1bW1hcnkobW9kZWwxKSAgDQpgYGANCmBgYHtyfQ0KdmlmKG1vZGVsMSkNCmBgYA0KYGBge3J9DQpicHRlc3QobW9kZWwxKQ0KYGBgDQoNCmBgYHtyfQ0KY2F0KCJBSUM6IiwgQUlDKG1vZGVsMSksIlxuIikNCmBgYA0KDQpgYGB7cn0NCmNhdCgiUk1TRToiLFJNU0UobW9kZWwxJGZpdHRlZC52YWx1ZXMsZGF0YSRtZWR2KSkNCmBgYA0KSW4gdGhpcyBmaXJzdCBtb2RlbCwgbm9uZSBvZiB0aGUgdmFyaWFibGVzIHdlcmUgbW9kaWZpZWQgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgYSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbC4gR2l2aW5nIGFzIHJlc3VsdHMgYW4gQUlDIG9mICAzMTQzLjU4NiBhbmQgYSBSTVNFIG9mIDUuMjk5NDgzLg0KDQoNCiMjIE1vZGVsIDI6DQpMaW5lYXIgUmVncmVzc2lvbiB3aXRoIGxvZw0KYGBge3J9DQptb2RlbDI8LWxtKGxvZyhtZWR2KSB+IGNyaW0rY2hhcytub3grcm0rYWdlK3JhZCt0YXgrbHN0YXQsZGF0YT1kYXRhKQ0Kc3VtbWFyeShtb2RlbDIpIA0KYGBgDQoNCmBgYHtyfQ0KdmlmKG1vZGVsMikNCmBgYA0KDQpgYGB7cn0NCmJwdGVzdChtb2RlbDIpDQpgYGANCg0KYGBge3J9DQpjYXQoIkFJQzoiLCBBSUMobW9kZWwyKSwiXG4iKQ0KYGBgDQoNCmBgYHtyfQ0KY2F0KCJSTVNFOiIsUk1TRShtb2RlbDIkZml0dGVkLnZhbHVlcyxkYXRhJG1lZHYpKQ0KYGBgDQpJbiB0aGUgc2Vjb25kIG1vZGVsLCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIHdhcyB0cmFuc2Zvcm1lZCB0byBsb2csIGR1ZSB0byB0aGUgc2xpZ2h0IHRlbmRlbmN5IHRvIHRoZSByaWdodCB0aGF0IHRoZSBvcmlnaW5hbCAibWVkdiIgaGlzdG9ncmFtIGhhcywgaW4gb3JkZXIgdG8gZm9jdXMgb24gdGhlIHJlbGV2YW50IGRhdGEgb25seS4gR2l2aW5nIGFzIHJlc3VsdHMgYW4gQUlDIG9mICAtMTI5LjIxMDMgYW5kIGEgUk1TRSBvZiAyMS40Mzc5Lg0KDQoNCiMjIE1vZGVsIDM6DQpQb2x5bm9taWFsIExpbmVhciBSZWdyZXNzaW9uDQoNCmBgYHtyfQ0KbW9kZWwzPC1sbShsb2cobWVkdikgfiBjcmltK2NoYXMrbm94K3JtK0kocm1eMikrYWdlK3JhZCt0YXgrbHN0YXQsZGF0YT1kYXRhKQ0Kc3VtbWFyeShtb2RlbDMpIA0KYGBgDQoNCmBgYHtyfQ0KcGxvdChlZmZlY3QoIkkocm1eMikiLG1vZGVsMykpDQpgYGANCg0KYGBge3J9DQpjYXQoIkFJQzoiLCBBSUMobW9kZWwzKSwiXG4iKQ0KYGBgDQoNCmBgYHtyfQ0KY2F0KCJSTVNFOiIsUk1TRShtb2RlbDMkZml0dGVkLnZhbHVlcyxkYXRhJG1lZHYpKQ0KYGBgDQoNCkluIHRoaXMgdGhpcmQgbW9kZWwsIGFzIGluIHRoZSBzZWNvbmQgb25lLCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIHdhcyB0cmFuc2Zvcm1lZCB0byBsb2csIGJ1dCBhbHNvLCB0aGUgaW5kZXBlbmRlbnQgInJtIiB2YXJpYWJsZSB3YXMgZWxldmF0ZWQgdG8gdGhlIHNxdWFyZSwgaW4gb3JkZXIgdG8gZ2VuZXJhdGUgYSBwb2x5bm9taWFsIGxpbmVhciByZWdyZXNzaW9uLiBHaXZpbmcgYXMgcmVzdWx0cyBhbiBBSUMgb2YgLTIxNy45MjU4ICBhbmQgYSBSTVNFIG9mIDIxLjQyNzAxLg0KDQpEZXNwaXRlIHBlcmZvcm1pbmcgYm90aCBBSUMgYW5kIFJNU0UgaW5kaWNhdG9ycyAoZm9yIGV2ZW4gbW9yZSBhc3N1cmFuY2UpLCB3ZSBkZWNpZGVkIHRvIGJhc2Ugb3VyIGRlY2lzaW9uIG9uIEFJQywgYW5kIHRoZSBsb3dlc3QgdmFsdWUgYW1vbmcgdGhlIDMgbW9kZWxzIHRvIGdlbmVyYXRlIHRoZSBMQVNTTyBhbmQgUklER0UgbWV0aG9kcyB3aXRoIHRoZSBpbnRlbnRpb24gb2YgZW5zdXJpbmcgZXZlbiBtb3JlIGNvbmZpZGVuY2UgaW4gdGhlIGRhdGEuDQoNCioqVmFsaWRhdGUgSHlwb3RoZXNlcyoqDQpCYXNlZCBvbiB0aGUgcmVzdWx0cyBvZiBvdXIgbW9kZWwsd2UgY2FuIHZhbGlkYXRlIHRoZSBoeXBvdGhlc2VzIHRoYXQgd2UgcHJvcG9zZWQuDQoNCkZvciBudW1iZXIgMSwgd2UgY2FuIHNlZSB0aGF0IGl0IGlzIGEgcG9zaXRpdmUgcmVsYXRpb24sIGJ1dCBhbHNvIGl0IGlzIGEgbmVnYXRpdmUgcmVsYXRpb24gYmV0d2VlbiA0IGFuZCA2IHJvb21zLiBBZnRlciA2IHJvb21zLCB0aGUgbWVkaWFuIHZhbHVlIHN0YXJ0cyBpbmNyZWFzaW5nLg0KDQpGb3IgbnVtYmVyIDIsIHdlIGNhbiBzZWUgdGhhdCBpdCBpcyBhIHBvc2l0aXZlIHJlbGF0aW9uLCBidXQgdGhpcyB2YXJpYWJsZSBzZWVtcyBub3QgdG8gYmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBmb3IgdGhlIG1vZGVsIG9yIHRoZSBwcmVkaWN0aW9uLg0KDQpGb3IgbnVtYmVyIDMsIHdlIGNhbiBjb25maXJtIHRoYXQgaXQgaXMgYSBuZWdhdGl2ZSByZWxhdGlvbiwgYW5kIHRoaXMgdmFyaWFibGUgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4NCg0KQW5hbHl6aW5nIHRoZSByZXN1bHRzIG9mIHRoZSByZWdyZXNzaW9uIG1vZGVsLCBpdCBjb3VsZCBiZSBpbnRlcmVzdGluZyB0byBzdHVkeSB0aGUgcmVsYXRpb24gYmV0d2VlbiB0aGUgcGVyIGNhcGl0YSBjcmltZSByYXRlIGFuZCB0aGUgbWVkaWFuIHZhbHVlIG9mIGhvbWVzLCBhbmQgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWVkaWFuIHZhbHVlIG9mIGhvbWVzIGlmIHRoZXkgYm91bmQgcml2ZXIgb3IgdGhleSBkb2Vzbid0LiBUaGVzZSBhcmUgdmFyaWFibGVzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgdGhhdCBjb3VsZCBiZSBpbnRlcmVzdGluZyB0byBjb25zaWRlciBhbmQgdXNlIGZvciBwcmVkaWN0aW9uLg0KDQojIExhc3NvIEFuZCBSaWRnZSByZWdyZXNzaW9uIG1vZGVscy4NCiMjIExhc3NvDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyMgc2V0cyB0aGUgcmFuZG9tIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eSBvZiByZXN1bHRzDQp0cmFpbmluZy5zYW1wbGVzPC1kYXRhJG1lZHYgJT4lDQogIGNyZWF0ZURhdGFQYXJ0aXRpb24ocD0wLjc1LGxpc3Q9RkFMU0UpICAgICAgICMjIyBMZXRzIGNvbnNpZGVyIDc1JSBvZiB0aGUgZGF0YSB0byBidWlsZCBhIHByZWRpY3RpdmUgbW9kZWwNCg0KdHJhaW4uZGF0YTwtZGF0YVt0cmFpbmluZy5zYW1wbGVzLCBdICAjIyMgdHJhaW5pbmcgZGF0YSB0byBmaXQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIA0KdGVzdC5kYXRhPC1kYXRhWy10cmFpbmluZy5zYW1wbGVzLCBdICAjIyMgdGVzdGluZyBkYXRhIHRvIHRlc3QgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsICAgICAgICAgDQpgYGANCg0KYGBge3J9DQoNCiMgTEFTU08gcmVncmVzc2lvbiB2aWEgZ2xtbmV0IHBhY2thZ2UgY2FuIG9ubHkgdGFrZSBudW1lcmljYWwgb2JzZXJ2YXRpb25zLiBUaGVuLCB0aGUgZGF0YXNldCBpcyB0cmFuc2Zvcm1lZCB0byBtb2RlbC5tYXRyaXgoKSBmb3JtYXQuIA0KIyBJbmRlcGVuZGVudCB2YXJpYWJsZXMNCng8LW1vZGVsLm1hdHJpeChsbShsb2cobWVkdikgfiBjcmltK2NoYXMrbm94K3JtK0kocm1eMikrYWdlK3JhZCt0YXgrbHN0YXQsZGF0YT10cmFpbi5kYXRhKSlbLC0xXSAjIyMgT0xTIG1vZGVsIHNwZWNpZmljYXRpb24NCg0KIyB4PC1tb2RlbC5tYXRyaXgoV2Vla2x5X1NhbGVzfi4sdHJhaW4uZGF0YSlbLC0xXSAjIyMgbWF0cml4IG9mIGluZGVwZW5kZW50IHZhcmlhYmxlcyBYJ3MNCnk8LXRyYWluLmRhdGEkbWVkdiAjIyMgZGVwZW5kZW50IHZhcmlhYmxlIA0KDQojIEluIGVzdGltYXRpbmcgTEFTU08gcmVncmVzc2lvbiBpdCBpcyBpbXBvcnRhbnQgdG8gZGVmaW5lIHRoZSBsYW1iZGEgdGhhdCBtaW5pbWl6ZXMgdGhlIHByZWRpY3Rpb24gZXJyb3IgcmF0ZS4gDQojIENyb3NzLXZhbGlkYXRpb24gZW5zdXJlcyB0aGF0IGV2ZXJ5IGRhdGEgLyBvYnNlcnZhdGlvbiBmcm9tIHRoZSBvcmlnaW5hbCBkYXRhc2V0IChkYXRhaW5zKSBoYXMgYSBjaGFuY2Ugb2YgYXBwZWFyaW5nIGluIHRyYWluIGFuZCB0ZXN0IGRhdGFzZXRzLg0KIyBGaW5kIHRoZSBiZXN0IGxhbWJkYSB1c2luZyBjcm9zcy12YWxpZGF0aW9uLg0Kc2V0LnNlZWQoMTIzKSANCmN2Lmxhc3NvPC1jdi5nbG1uZXQoeCx5LGFscGhhPTEpICMgYWxwaGEgPSAxIGZvciBMQVNTTw0KDQojIERpc3BsYXkgdGhlIGJlc3QgbGFtYmRhIHZhbHVlDQpjdi5sYXNzbyRsYW1iZGEubWluICAgICAgICAgICAgICAgICAgICAgICMjIyBsYW1iZGE6IGEgbnVtZXJpYyB2YWx1ZSBkZWZpbmluZyB0aGUgYW1vdW50IG9mIHNocmlua2FnZS4gV2h5IG1pbj8gdGhlIGhpZ2hlciB0aGUgdmFsdWUgb2YgPz8gLCB0aGUgbW9yZSBwZW5hbGl6YXRpb24gdGhlcmUgaXMNCg0KIyBGaXQgdGhlIGZpbmFsIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBkYXRhDQpsYXNzb21vZGVsPC1nbG1uZXQoeCx5LGFscGhhPTEsbGFtYmRhPWN2Lmxhc3NvJGxhbWJkYS5taW4pDQoNCiMgRGlzcGxheSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cw0KY29lZihsYXNzb21vZGVsKQ0KDQojIE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3QgZGF0YQ0KeC50ZXN0PC1tb2RlbC5tYXRyaXgobG0obG9nKG1lZHYpIH4gY3JpbStjaGFzK25veCtybStJKHJtXjIpK2FnZStyYWQrdGF4K2xzdGF0LGRhdGE9dGVzdC5kYXRhKSlbLC0xXSAjIyMgT0xTIG1vZGVsIHNwZWNpZmljYXRpb24NCiMgeC50ZXN0PC1tb2RlbC5tYXRyaXgoV2Vla2x5X1NhbGVzfi4sdGVzdC5kYXRhKVssLTFdDQpsYXNzb3ByZWRpY3Rpb25zIDwtIGxhc3NvbW9kZWwgJT4lIHByZWRpY3QoeC50ZXN0KSAlPiUgYXMudmVjdG9yKCkNCg0KIyBNb2RlbCBBY2N1cmFjeQ0KZGF0YS5mcmFtZSgNCiAgUk1TRSA9IFJNU0UobGFzc29wcmVkaWN0aW9ucywgdGVzdC5kYXRhJG1lZHYpLA0KICBSc3F1YXJlID0gUjIobGFzc29wcmVkaWN0aW9ucywgdGVzdC5kYXRhJG1lZHYpKQ0KDQojIyMgdmlzdWFsaXppbmcgbGFzc28gcmVncmVzc2lvbiByZXN1bHRzIA0KbGJzX2Z1biA8LSBmdW5jdGlvbihmaXQsIG9mZnNldF94PTEsIC4uLikgew0KICBMIDwtIGxlbmd0aChmaXQkbGFtYmRhKQ0KICB4IDwtIGxvZyhmaXQkbGFtYmRhW0xdKSsgb2Zmc2V0X3gNCiAgeSA8LSBmaXQkYmV0YVssIExdDQogIGxhYnMgPC0gbmFtZXMoeSkNCiAgdGV4dCh4LCB5LCBsYWJlbHM9bGFicywgLi4uKQ0KfQ0KDQpsYXNzbzwtZ2xtbmV0KHNjYWxlKHgpLHksYWxwaGE9MSkNCg0KcGxvdChsYXNzbyx4dmFyPSJsYW1iZGEiLGxhYmVsPVQpDQpsYnNfZnVuKGxhc3NvKQ0KYWJsaW5lKHY9Y3YubGFzc28kbGFtYmRhLm1pbixjb2w9InJlZCIsbHR5PTIpDQphYmxpbmUodj1jdi5sYXNzbyRsYW1iZGEuMXNlLGNvbD0iYmx1ZSIsbHR5PTIpDQpgYGANCg0KDQojIyBSSURHRQ0KDQpgYGB7cn0NCg0KeDwtbW9kZWwubWF0cml4KGxtKGxvZyhtZWR2KSB+IGNyaW0rY2hhcytub3grcm0rSShybV4yKSthZ2UrcmFkK3RheCtsc3RhdCxkYXRhPXRyYWluLmRhdGEpKVssLTFdICMjIyBPTFMgbW9kZWwgc3BlY2lmaWNhdGlvbg0KDQp5PC10cmFpbi5kYXRhJG1lZHYgIyMjIGRlcGVuZGVudCB2YXJpYWJsZSANCg0KDQojIEZpbmQgdGhlIGJlc3QgbGFtYmRhIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24NCnNldC5zZWVkKDEyMykgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHg6IGluZGVwZW5kZW50IHZhcmlhYmxlcyB8IHk6IGRlcGVuZGVudCB2YXJpYWJsZSANCmN2LnJpZGdlIDwtIGN2LmdsbW5ldCh4LHksYWxwaGE9MC4xKSAgICAgICMgYWxwaGEgPSAwIGZvciBSSURHRQ0KDQojIERpc3BsYXkgdGhlIGJlc3QgbGFtYmRhIHZhbHVlDQpjdi5yaWRnZSRsYW1iZGEubWluICAgICAgICAgICAgICAgICAgICAgIyBsYW1iZGE6IGEgbnVtZXJpYyB2YWx1ZSBkZWZpbmluZyB0aGUgYW1vdW50IG9mIHNocmlua2FnZS4gV2h5IG1pbj8gdGhlIGhpZ2hlciB0aGUgdmFsdWUgb2YgPz8gLCB0aGUgbW9yZSBwZW5hbGl6YXRpb24gdGhlcmUgaXMNCg0KIyBGaXQgdGhlIGZpbmFsIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBkYXRhDQpyaWRnZW1vZGVsPC1nbG1uZXQoeCx5LGFscGhhPTAsbGFtYmRhPWN2LnJpZGdlJGxhbWJkYS5taW4pDQoNCiMgRGlzcGxheSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cw0KY29lZihyaWRnZW1vZGVsKQ0KDQojIE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3QgZGF0YQ0KDQp4LnRlc3Q8LW1vZGVsLm1hdHJpeChsbShsb2cobWVkdikgfiBjcmltK2NoYXMrbm94K3JtK0kocm1eMikrYWdlK3JhZCt0YXgrbHN0YXQsZGF0YT10ZXN0LmRhdGEpKVssLTFdDQoNCnJpZGdlcHJlZGljdGlvbnM8LXJpZGdlbW9kZWwgJT4lIHByZWRpY3QoeC50ZXN0KSAlPiUgYXMudmVjdG9yKCkNCg0KIyBNb2RlbCBBY2N1cmFjeQ0KZGF0YS5mcmFtZSgNCiAgUk1TRSA9IFJNU0UocmlkZ2VwcmVkaWN0aW9ucywgdGVzdC5kYXRhJG1lZHYpLA0KICBSc3F1YXJlID0gUjIocmlkZ2VwcmVkaWN0aW9ucywgdGVzdC5kYXRhJG1lZHYpDQopDQoNCiMjIyB2aXN1YWxpemluZyByaWRnZSByZWdyZXNzaW9uIHJlc3VsdHMgDQoNCnJpZGdlPC1nbG1uZXQoc2NhbGUoeCkseSxhbHBoYT0wKQ0KDQpwbG90KHJpZGdlLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsPVQpDQpsYnNfZnVuKHJpZGdlKQ0KYWJsaW5lKHY9Y3YucmlkZ2UkbGFtYmRhLm1pbiwgY29sID0gInJlZCIsIGx0eT0yKQ0KYWJsaW5lKHY9Y3YucmlkZ2UkbGFtYmRhLjFzZSwgY29sPSJibHVlIiwgbHR5PTIpDQoNCnRhYiA8LSBtYXRyaXgoYygxNzU4Nyw2MzI0LDYzMjIsMC43NywwLjcxLDAuNzEpLCBuY29sPTIsIGJ5cm93PUZBTFNFKQ0KY29sbmFtZXModGFiKSA8LSBjKCdSTVNFJywnUjInKQ0Kcm93bmFtZXModGFiKSA8LSBjKCdMaW5lYXIgUmVncmVzc2lvbicsJ0xhc3NvJywnUmlkZ2UnKQ0KdGFiIDwtIGFzLnRhYmxlKHRhYikNCg0KYGBgDQoNCiMgUmVzdWx0cyBkaXNjdXNzaW9uDQoNCldlIGNvdWxkIHNlZSBhcyBhIHRlYW0gdGhhdCBsaW5lYXIgcmVncmVzc2lvbiBhbmFseXNpcyBjYW4gY29udHJpYnV0ZSB0byBpbXByb3ZlIHByZWRpY3RpdmUgYW5hbHl0aWNzIGluIGRpZmZlcmVudCB3YXlzLiBJdCBoZWxwcyB0byB1bmRlcnN0YW5kIGRlIHJlbGF0aW9uc2hpcCB0aGF0IGV4aXN0cyBiZXR3ZWVuIG9uZSBvciBtb3JlIGluZGVwZW5kZW50IHZhcmlhYmxlcyBhbmQgdGhlIGRlcGVuZGVudCB2YXJpYWJsZS4gS25vd2luZyBob3cgdG8gaW50ZXJwcmV0IHRoZSByZXN1bHRzIG9mIGEgbGluZWFyIHJlZ3Jlc3Npb24sIGl0IGlzIHBvc3NpYmxlIHRvIGV2YWx1YXRlIHRoZXNlIHJlbGF0aW9uc2hpcHMsIGFuZCB0aGUgaW1wYWN0IHRoYXQgb25lIHZhcmlhYmxlIGNvdWxkIGhhdmUgaW50byB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLg0KDQpJdCBhbHNvIGhlbHBzIHRvIGlkZW50aWZ5IGlmIHRoZXJlIGlzIGEgcG9zaXRpdmUgb3IgbmVnYXRpdmUgcmVsYXRpb25zaGlwIGFuZCB3aGljaCB2YXJpYWJsZXMgdGhhdCBoYXZlIGEgc2lnbmlmaWNhbnQgaW1wYWN0IG9uIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUuIEFub3RoZXIgY29udHJpYnV0aW9uIGlzIHRoZSBpbnNpZ2h0cyB0aGF0IGNhbiBiZSBnZW5lcmF0ZWQgZnJvbSB0aGUgYW5hbHlzaXMsIHRoYXQgY2FuIGJlIHN1cHBvcnRlZCBieSB0aGUgbW9kZWxzIGNyZWF0ZWQuIEl0IGlzIGEgdmVyeSB1c2VmdWwgdG9vbCBpbiBvcmRlciB0byBtYWtlIGluZm9ybWVkIGFuZCBhc3NlcnRpdmUgcHJlZGljdGlvbnMuDQoNCkluIG91ciBhbmFseXNpcywgd2UgZW1wbG95ZWQgYm90aCBMYXNzbyBhbmQgUmlkZ2UgcmVndWxhcml6YXRpb24gdGVjaG5pcXVlcyB0byBhc3Nlc3MgdGhlIHJlbGV2YW5jZSBvZiBpbnB1dCBmZWF0dXJlcyBpbiBvdXIgbW9kZWwuIFdoaWxlIGJvdGggbWV0aG9kcyBwcm92aWRlZCB2YWx1YWJsZSBpbnNpZ2h0cywgd2UgaGF2ZSBjaG9zZW4gdG8gcHJpb3JpdGl6ZSB0aGUgcmVzdWx0cyBvYnRhaW5lZCB0aHJvdWdoIFJpZGdlIHJlZ3Jlc3Npb24uIE91ciBkZWNpc2lvbiBzdGVtcyBmcm9tIHRoZSBmYWN0IHRoYXQsIGluIHRoZSBjb250ZXh0IG9mIG91ciBzcGVjaWZpZWQgbW9kZWwsIHRoZSBtYWpvcml0eSBvZiBhdHRyaWJ1dGVzIGhhdmUgYmVlbiBmb3VuZCB0byBiZSByZWxldmFudCBpbiByZWxhdGlvbiB0byB0aGUgaHlwb3RoZXNlcyB3ZSBlc3RhYmxpc2hlZC4NCg0KVGhlIHdlaWdodGVkIGF2ZXJhZ2UgZXJyb3Igb2YgNi4zOTEzLCB3aGVuIGNvbXBhcmVkIHRvIHRoZSBhdmVyYWdlIGFjdHVhbCBtZWRpYW4gdmFsdWVzIG9mIDIxLjIwIGluIHRoZSBkYXRhc2V0LCBzdWdnZXN0cyB0aGF0IG91ciBtb2RlbCdzIHByZWRpY3Rpb25zIGFyZSByZWFzb25hYmx5IGFjY3VyYXRlLiBUaGlzIHN0cmVuZ3RoZW5zIG91ciBjb25maWRlbmNlIGluIHRoZSBtb2RlbCdzIHBlcmZvcm1hbmNlLg0KDQpVcG9uIGV4YW1pbmluZyB0aGUgTGFzc28gcmVzdWx0cywgd2Ugb2JzZXJ2ZWQgdGhhdCB2YXJpYWJsZXMgc3VjaCBhcyAibHN0YXQsIiAicm0gc3F1YXJlZCwiICJ0YXgsIiBhbmQgInJhZCIgd2VyZSBpZGVudGlmaWVkIGFzIHBhcnRpY3VsYXJseSByZWxldmFudCwgZ2l2ZW4gdGhlaXIgaGlnaGVyIGFzc29jaWF0ZWQgY29lZmZpY2llbnRzLiBNb3Jlb3ZlciwgdGhlIGNoYXJhY3RlcmlzdGljIG9mIExhc3NvIHRvIGVsaW1pbmF0ZSBub24tcmVsZXZhbnQgdmFyaWFibGVzLCBpbmRpY2F0ZWQgYnkgdGhlaXIgZG90dGVkIGxpbmUgKDAgdmFsdWUpLCBzaG93Y2FzZXMgaXRzIGVmZmVjdGl2ZW5lc3MgaW4gZmVhdHVyZSBzZWxlY3Rpb24uDQoNCk92ZXJhbGwsIG91ciBhbmFseXNpcyB1bmRlcnNjb3JlcyB0aGUgcGl2b3RhbCByb2xlIG9mIGxpbmVhciByZWdyZXNzaW9uIGluIGVuaGFuY2luZyBwcmVkaWN0aXZlIGFuYWx5dGljcy4gQnkgY29tcHJlaGVuZGluZyB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGluZGVwZW5kZW50IGFuZCBkZXBlbmRlbnQgdmFyaWFibGVzLCB3ZSBjYW4gZ2F1Z2UgdGhlaXIgaW1wYWN0cyBhbmQgZW5oYW5jZSBvdXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgdW5kZXJseWluZyBkeW5hbWljcy4gVGhpcyBrbm93bGVkZ2UgZXF1aXBzIHVzIHdpdGggdGhlIGFiaWxpdHkgdG8gbWFrZSBpbmZvcm1lZCBkZWNpc2lvbnMgYW5kIHJlZmluZSBvdXIgcHJlZGljdGl2ZSBtb2RlbHMgZWZmZWN0aXZlbHkuDQoNCkFjY29yZGluZyB0byBJQk0sIHRoZXNlIGFyZSBzb21lIGtleSBhc3N1bXB0aW9ucyB0byBiZSBjb25zaWRlcmVkIGZvciBzdWNjZXNzIHdpdGggbGluZWFyLXJlZ3Jlc3Npb24gYW5hbHlzaXM6DQoNCioqRm9yIGVhY2ggdmFyaWFibGU6KiogQ29uc2lkZXIgdGhlIG51bWJlciBvZiB2YWxpZCBjYXNlcywgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uLg0KDQoqKkZvciBlYWNoIG1vZGVsOioqIENvbnNpZGVyIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLCBjb3JyZWxhdGlvbiBtYXRyaXgsIHBhcnQgYW5kIHBhcnRpYWwgY29ycmVsYXRpb25zLCBtdWx0aXBsZSBSLCBSMiwgYWRqdXN0ZWQgUjIsIGNoYW5nZSBpbiBSMiwgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIGVzdGltYXRlLCBhbmFseXNpcy1vZi12YXJpYW5jZSB0YWJsZSwgcHJlZGljdGVkIHZhbHVlcyBhbmQgcmVzaWR1YWxzLiBBbHNvLCBjb25zaWRlciA5NS1wZXJjZW50LWNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciBlYWNoIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQsIHZhcmlhbmNlLWNvdmFyaWFuY2UgbWF0cml4LCB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9yLCB0b2xlcmFuY2UsIER1cmJpbi1XYXRzb24gdGVzdCwgZGlzdGFuY2UgbWVhc3VyZXMgKE1haGFsYW5vYmlzLCBDb29rIGFuZCBsZXZlcmFnZSB2YWx1ZXMpLCBEZkJldGEsIERmRml0LCBwcmVkaWN0aW9uIGludGVydmFscyBhbmQgY2FzZS13aXNlIGRpYWdub3N0aWMgaW5mb3JtYXRpb24uIA0KDQoqKlBsb3RzOioqIENvbnNpZGVyIHNjYXR0ZXJwbG90cywgcGFydGlhbCBwbG90cywgaGlzdG9ncmFtcyBhbmQgbm9ybWFsIHByb2JhYmlsaXR5IHBsb3RzLg0KDQoqKkRhdGE6KiogRGVwZW5kZW50IGFuZCBpbmRlcGVuZGVudCB2YXJpYWJsZXMgc2hvdWxkIGJlIHF1YW50aXRhdGl2ZS4gQ2F0ZWdvcmljYWwgdmFyaWFibGVzLCBzdWNoIGFzIHJlbGlnaW9uLCBtYWpvciBmaWVsZCBvZiBzdHVkeSBvciByZWdpb24gb2YgcmVzaWRlbmNlLCBuZWVkIHRvIGJlIHJlY29kZWQgdG8gYmluYXJ5IChkdW1teSkgdmFyaWFibGVzIG9yIG90aGVyIHR5cGVzIG9mIGNvbnRyYXN0IHZhcmlhYmxlcy4gIA0KDQoqKk90aGVyIGFzc3VtcHRpb25zOioqIEZvciBlYWNoIHZhbHVlIG9mIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZSwgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIG11c3QgYmUgbm9ybWFsLiBUaGUgdmFyaWFuY2Ugb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIHNob3VsZCBiZSBjb25zdGFudCBmb3IgYWxsIHZhbHVlcyBvZiB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUuIFRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGFuZCBlYWNoIGluZGVwZW5kZW50IHZhcmlhYmxlIHNob3VsZCBiZSBsaW5lYXIgYW5kIGFsbCBvYnNlcnZhdGlvbnMgc2hvdWxkIGJlIGluZGVwZW5kZW50Lg0KDQpfKEFib3V0IExpbmVhciBSZWdyZXNzaW9uIHwgSUJNLCAyMDIzKV8NCg0KQWJvdXQgTGluZWFyIFJlZ3Jlc3Npb24gfCBJQk0uICgyMDIzKS4gSWJtLmNvbS4gaHR0cHM6Ly93d3cuaWJtLmNvbS90b3BpY3MvbGluZWFyLXJlZ3Jlc3Npb24NCg==