library(dplyr)
library(tidyr)
library(ggplot2)
library(naniar)
library(randomForest)
library(purrr)

Final Exam


Final Problem 3. 30 points.

You are to compete in the House Prices: Advanced Regression Techniques competition https://www.kaggle.com/c/house-prices-advanced-regression-techniques . I want you to do the following.


3.1 Descriptive and Inferential Statistics.

Provide univariate descriptive statistics and appropriate plots for the training data set. Provide a scatterplot matrix for at least two of the independent variables and the dependent variable. Derive a correlation matrix for any three quantitative variables in the dataset. Test the hypotheses that the correlations between each pairwise set of variables is 0 and provide an 80% confidence interval. Discuss the meaning of your analysis. Would you be worried about familywise error? Why or why not? 5 points


3.1.1 Load the training data

# define url for data
urlTrain <- "https://raw.githubusercontent.com/esteban-data-enthusiast/data605/main/shared-data/house-prices-advanced-regression-techniques/train.csv"

# load the training data
rawTrain <- read.csv(urlTrain, stringsAsFactors = TRUE)


3.1.2 Univariate descriptive statistics

summary(rawTrain)
#>        Id           MSSubClass       MSZoning     LotFrontage    
#>  Min.   :   1.0   Min.   : 20.0   C (all):  10   Min.   : 21.00  
#>  1st Qu.: 365.8   1st Qu.: 20.0   FV     :  65   1st Qu.: 59.00  
#>  Median : 730.5   Median : 50.0   RH     :  16   Median : 69.00  
#>  Mean   : 730.5   Mean   : 56.9   RL     :1151   Mean   : 70.05  
#>  3rd Qu.:1095.2   3rd Qu.: 70.0   RM     : 218   3rd Qu.: 80.00  
#>  Max.   :1460.0   Max.   :190.0                  Max.   :313.00  
#>                                                  NA's   :259     
#>     LotArea        Street      Alley      LotShape  LandContour  Utilities   
#>  Min.   :  1300   Grvl:   6   Grvl:  50   IR1:484   Bnk:  63    AllPub:1459  
#>  1st Qu.:  7554   Pave:1454   Pave:  41   IR2: 41   HLS:  50    NoSeWa:   1  
#>  Median :  9478               NA's:1369   IR3: 10   Low:  36                 
#>  Mean   : 10517                           Reg:925   Lvl:1311                 
#>  3rd Qu.: 11602                                                              
#>  Max.   :215245                                                              
#>                                                                              
#>    LotConfig    LandSlope   Neighborhood   Condition1     Condition2  
#>  Corner : 263   Gtl:1382   NAmes  :225   Norm   :1260   Norm   :1445  
#>  CulDSac:  94   Mod:  65   CollgCr:150   Feedr  :  81   Feedr  :   6  
#>  FR2    :  47   Sev:  13   OldTown:113   Artery :  48   Artery :   2  
#>  FR3    :   4              Edwards:100   RRAn   :  26   PosN   :   2  
#>  Inside :1052              Somerst: 86   PosN   :  19   RRNn   :   2  
#>                            Gilbert: 79   RRAe   :  11   PosA   :   1  
#>                            (Other):707   (Other):  15   (Other):   2  
#>    BldgType      HouseStyle   OverallQual      OverallCond      YearBuilt   
#>  1Fam  :1220   1Story :726   Min.   : 1.000   Min.   :1.000   Min.   :1872  
#>  2fmCon:  31   2Story :445   1st Qu.: 5.000   1st Qu.:5.000   1st Qu.:1954  
#>  Duplex:  52   1.5Fin :154   Median : 6.000   Median :5.000   Median :1973  
#>  Twnhs :  43   SLvl   : 65   Mean   : 6.099   Mean   :5.575   Mean   :1971  
#>  TwnhsE: 114   SFoyer : 37   3rd Qu.: 7.000   3rd Qu.:6.000   3rd Qu.:2000  
#>                1.5Unf : 14   Max.   :10.000   Max.   :9.000   Max.   :2010  
#>                (Other): 19                                                  
#>   YearRemodAdd    RoofStyle       RoofMatl     Exterior1st   Exterior2nd 
#>  Min.   :1950   Flat   :  13   CompShg:1434   VinylSd:515   VinylSd:504  
#>  1st Qu.:1967   Gable  :1141   Tar&Grv:  11   HdBoard:222   MetalSd:214  
#>  Median :1994   Gambrel:  11   WdShngl:   6   MetalSd:220   HdBoard:207  
#>  Mean   :1985   Hip    : 286   WdShake:   5   Wd Sdng:206   Wd Sdng:197  
#>  3rd Qu.:2004   Mansard:   7   ClyTile:   1   Plywood:108   Plywood:142  
#>  Max.   :2010   Shed   :   2   Membran:   1   CemntBd: 61   CmentBd: 60  
#>                                (Other):   2   (Other):128   (Other):136  
#>    MasVnrType    MasVnrArea     ExterQual ExterCond  Foundation  BsmtQual  
#>  BrkCmn : 15   Min.   :   0.0   Ex: 52    Ex:   3   BrkTil:146   Ex  :121  
#>  BrkFace:445   1st Qu.:   0.0   Fa: 14    Fa:  28   CBlock:634   Fa  : 35  
#>  None   :864   Median :   0.0   Gd:488    Gd: 146   PConc :647   Gd  :618  
#>  Stone  :128   Mean   : 103.7   TA:906    Po:   1   Slab  : 24   TA  :649  
#>  NA's   :  8   3rd Qu.: 166.0             TA:1282   Stone :  6   NA's: 37  
#>                Max.   :1600.0                       Wood  :  3             
#>                NA's   :8                                                   
#>  BsmtCond    BsmtExposure BsmtFinType1   BsmtFinSF1     BsmtFinType2
#>  Fa  :  45   Av  :221     ALQ :220     Min.   :   0.0   ALQ :  19   
#>  Gd  :  65   Gd  :134     BLQ :148     1st Qu.:   0.0   BLQ :  33   
#>  Po  :   2   Mn  :114     GLQ :418     Median : 383.5   GLQ :  14   
#>  TA  :1311   No  :953     LwQ : 74     Mean   : 443.6   LwQ :  46   
#>  NA's:  37   NA's: 38     Rec :133     3rd Qu.: 712.2   Rec :  54   
#>                           Unf :430     Max.   :5644.0   Unf :1256   
#>                           NA's: 37                      NA's:  38   
#>    BsmtFinSF2        BsmtUnfSF       TotalBsmtSF      Heating     HeatingQC
#>  Min.   :   0.00   Min.   :   0.0   Min.   :   0.0   Floor:   1   Ex:741   
#>  1st Qu.:   0.00   1st Qu.: 223.0   1st Qu.: 795.8   GasA :1428   Fa: 49   
#>  Median :   0.00   Median : 477.5   Median : 991.5   GasW :  18   Gd:241   
#>  Mean   :  46.55   Mean   : 567.2   Mean   :1057.4   Grav :   7   Po:  1   
#>  3rd Qu.:   0.00   3rd Qu.: 808.0   3rd Qu.:1298.2   OthW :   2   TA:428   
#>  Max.   :1474.00   Max.   :2336.0   Max.   :6110.0   Wall :   4            
#>                                                                            
#>  CentralAir Electrical     X1stFlrSF      X2ndFlrSF     LowQualFinSF    
#>  N:  95     FuseA:  94   Min.   : 334   Min.   :   0   Min.   :  0.000  
#>  Y:1365     FuseF:  27   1st Qu.: 882   1st Qu.:   0   1st Qu.:  0.000  
#>             FuseP:   3   Median :1087   Median :   0   Median :  0.000  
#>             Mix  :   1   Mean   :1163   Mean   : 347   Mean   :  5.845  
#>             SBrkr:1334   3rd Qu.:1391   3rd Qu.: 728   3rd Qu.:  0.000  
#>             NA's :   1   Max.   :4692   Max.   :2065   Max.   :572.000  
#>                                                                         
#>    GrLivArea     BsmtFullBath     BsmtHalfBath        FullBath    
#>  Min.   : 334   Min.   :0.0000   Min.   :0.00000   Min.   :0.000  
#>  1st Qu.:1130   1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.:1.000  
#>  Median :1464   Median :0.0000   Median :0.00000   Median :2.000  
#>  Mean   :1515   Mean   :0.4253   Mean   :0.05753   Mean   :1.565  
#>  3rd Qu.:1777   3rd Qu.:1.0000   3rd Qu.:0.00000   3rd Qu.:2.000  
#>  Max.   :5642   Max.   :3.0000   Max.   :2.00000   Max.   :3.000  
#>                                                                   
#>     HalfBath       BedroomAbvGr    KitchenAbvGr   KitchenQual  TotRmsAbvGrd   
#>  Min.   :0.0000   Min.   :0.000   Min.   :0.000   Ex:100      Min.   : 2.000  
#>  1st Qu.:0.0000   1st Qu.:2.000   1st Qu.:1.000   Fa: 39      1st Qu.: 5.000  
#>  Median :0.0000   Median :3.000   Median :1.000   Gd:586      Median : 6.000  
#>  Mean   :0.3829   Mean   :2.866   Mean   :1.047   TA:735      Mean   : 6.518  
#>  3rd Qu.:1.0000   3rd Qu.:3.000   3rd Qu.:1.000               3rd Qu.: 7.000  
#>  Max.   :2.0000   Max.   :8.000   Max.   :3.000               Max.   :14.000  
#>                                                                               
#>  Functional    Fireplaces    FireplaceQu   GarageType   GarageYrBlt  
#>  Maj1:  14   Min.   :0.000   Ex  : 24    2Types :  6   Min.   :1900  
#>  Maj2:   5   1st Qu.:0.000   Fa  : 33    Attchd :870   1st Qu.:1961  
#>  Min1:  31   Median :1.000   Gd  :380    Basment: 19   Median :1980  
#>  Min2:  34   Mean   :0.613   Po  : 20    BuiltIn: 88   Mean   :1979  
#>  Mod :  15   3rd Qu.:1.000   TA  :313    CarPort:  9   3rd Qu.:2002  
#>  Sev :   1   Max.   :3.000   NA's:690    Detchd :387   Max.   :2010  
#>  Typ :1360                               NA's   : 81   NA's   :81    
#>  GarageFinish   GarageCars      GarageArea     GarageQual  GarageCond 
#>  Fin :352     Min.   :0.000   Min.   :   0.0   Ex  :   3   Ex  :   2  
#>  RFn :422     1st Qu.:1.000   1st Qu.: 334.5   Fa  :  48   Fa  :  35  
#>  Unf :605     Median :2.000   Median : 480.0   Gd  :  14   Gd  :   9  
#>  NA's: 81     Mean   :1.767   Mean   : 473.0   Po  :   3   Po  :   7  
#>               3rd Qu.:2.000   3rd Qu.: 576.0   TA  :1311   TA  :1326  
#>               Max.   :4.000   Max.   :1418.0   NA's:  81   NA's:  81  
#>                                                                       
#>  PavedDrive   WoodDeckSF      OpenPorchSF     EnclosedPorch      X3SsnPorch    
#>  N:  90     Min.   :  0.00   Min.   :  0.00   Min.   :  0.00   Min.   :  0.00  
#>  P:  30     1st Qu.:  0.00   1st Qu.:  0.00   1st Qu.:  0.00   1st Qu.:  0.00  
#>  Y:1340     Median :  0.00   Median : 25.00   Median :  0.00   Median :  0.00  
#>             Mean   : 94.24   Mean   : 46.66   Mean   : 21.95   Mean   :  3.41  
#>             3rd Qu.:168.00   3rd Qu.: 68.00   3rd Qu.:  0.00   3rd Qu.:  0.00  
#>             Max.   :857.00   Max.   :547.00   Max.   :552.00   Max.   :508.00  
#>                                                                                
#>   ScreenPorch        PoolArea        PoolQC       Fence      MiscFeature
#>  Min.   :  0.00   Min.   :  0.000   Ex  :   2   GdPrv:  59   Gar2:   2  
#>  1st Qu.:  0.00   1st Qu.:  0.000   Fa  :   2   GdWo :  54   Othr:   2  
#>  Median :  0.00   Median :  0.000   Gd  :   3   MnPrv: 157   Shed:  49  
#>  Mean   : 15.06   Mean   :  2.759   NA's:1453   MnWw :  11   TenC:   1  
#>  3rd Qu.:  0.00   3rd Qu.:  0.000               NA's :1179   NA's:1406  
#>  Max.   :480.00   Max.   :738.000                                       
#>                                                                         
#>     MiscVal             MoSold           YrSold        SaleType   
#>  Min.   :    0.00   Min.   : 1.000   Min.   :2006   WD     :1267  
#>  1st Qu.:    0.00   1st Qu.: 5.000   1st Qu.:2007   New    : 122  
#>  Median :    0.00   Median : 6.000   Median :2008   COD    :  43  
#>  Mean   :   43.49   Mean   : 6.322   Mean   :2008   ConLD  :   9  
#>  3rd Qu.:    0.00   3rd Qu.: 8.000   3rd Qu.:2009   ConLI  :   5  
#>  Max.   :15500.00   Max.   :12.000   Max.   :2010   ConLw  :   5  
#>                                                     (Other):   9  
#>  SaleCondition    SalePrice     
#>  Abnorml: 101   Min.   : 34900  
#>  AdjLand:   4   1st Qu.:129975  
#>  Alloca :  12   Median :163000  
#>  Family :  20   Mean   :180921  
#>  Normal :1198   3rd Qu.:214000  
#>  Partial: 125   Max.   :755000  
#> 


3.1.3 Scatter plot for 2 independent variables

Provide a scatterplot matrix for at least two of the independent variables and the dependent variable.

par(mfrow = c(1,3))
plot(rawTrain$OverallQual, rawTrain$SalePrice,
     xlab = 'Overall Quality', ylab = 'Sale Price')
plot(rawTrain$GarageCars,  rawTrain$SalePrice,
     xlab = 'Garage Cars', ylab = 'Sale Price')
plot(rawTrain$FullBath,    rawTrain$SalePrice,
     xlab = 'Full Baths', ylab = 'Sale Price')


3.1.4 Derive a correlation matrix

Derive a correlation matrix for any three quantitative variables in the dataset.

# Subset numerical variables that are from the "train" set
num.train <- rawTrain %>%
  select(which(sapply(.,is.integer)), which(sapply(., is.numeric)))

# select 3 specific numeric variables
num.train.three <- select(num.train, OverallQual, GarageCars, FullBath)

# generate a correlation matrix for the 3 numeric variables
corr_matrix <- round(cor(num.train.three),
      digits = 2)

corr_matrix
#>             OverallQual GarageCars FullBath
#> OverallQual        1.00       0.60     0.55
#> GarageCars         0.60       1.00     0.47
#> FullBath           0.55       0.47     1.00


3.1.5 Correlation Hypotheses Test

Test the hypotheses that the correlations between each pairwise set of variables is 0 and provide an 80% confidence interval. Discuss the meaning of your analysis. Would you be worried about familywise error? Why or why not?

The null and alternative hypothesis for the correlation test are as follows:

\(H_0: p = 0\) (meaning that there is no linear relationship between the two variables)

\(H_1: p \neq 0\) (meaning that there is a linear relationship between the two variables)

# test the correlation between 1st and 2nd variables
cor_test1 <- cor.test(formula = ~ OverallQual + GarageCars,
                      data = num.train.three,
                      method = "pearson",
                      conf.level = 0.80)

# test the correlation between 1st and 3rd variables
cor_test2 <- cor.test(formula = ~ OverallQual + FullBath,
                      data = num.train.three,
                      method = "pearson",
                      conf.level = 0.80)

# test the correlation between 2nd and 3rd variables
cor_test3 <- cor.test(formula = ~ GarageCars + FullBath,
                      data = num.train.three,
                      method = "pearson",
                      conf.level = 0.80)

Analyze the results

cor_test1
#> 
#>  Pearson's product-moment correlation
#> 
#> data:  OverallQual and GarageCars
#> t = 28.688, df = 1458, p-value < 2.2e-16
#> alternative hypothesis: true correlation is not equal to 0
#> 80 percent confidence interval:
#>  0.5787769 0.6216992
#> sample estimates:
#>       cor 
#> 0.6006707
cor_test2
#> 
#>  Pearson's product-moment correlation
#> 
#> data:  OverallQual and FullBath
#> t = 25.185, df = 1458, p-value < 2.2e-16
#> alternative hypothesis: true correlation is not equal to 0
#> 80 percent confidence interval:
#>  0.5267723 0.5735625
#> sample estimates:
#>       cor 
#> 0.5505997
cor_test3
#> 
#>  Pearson's product-moment correlation
#> 
#> data:  GarageCars and FullBath
#> t = 20.314, df = 1458, p-value < 2.2e-16
#> alternative hypothesis: true correlation is not equal to 0
#> 80 percent confidence interval:
#>  0.4430949 0.4954243
#> sample estimates:
#>      cor 
#> 0.469672

In all three tests we see that the p-value < 2.2e-16, which is less than the significance level alpha = 0.05. We can conclude that the three pairs of variables are significantly correlated among each other. We can reject the null hypothesis.

Calculate the FamilyWise error (FWE)

\(FWE \leq ( 1 - \alpha ) ^ c\)

Where:

\(\alpha =\) alpha level for an individual test (e.g. .05)

\(c =\) Number of comparisons

FWE <- 1 - (1 -.05)^3

FWE
#> [1] 0.142625

This means that the probability of a type I error is just over 14%, which is low considering only 3 tests were performed. So, we do not need to worry about Familywise Errors.


3.2 Linear Algebra and Correlation.

Invert your correlation matrix from above. (This is known as the precision matrix and contains variance inflation factors on the diagonal.) Multiply the correlation matrix by the precision matrix, and then multiply the precision matrix by the correlation matrix. Conduct LU decomposition on the matrix. 5 points


3.2.1 Compute precision matrix

precision_matrix <- solve(corr_matrix)
corr_matrix
#>             OverallQual GarageCars FullBath
#> OverallQual        1.00       0.60     0.55
#> GarageCars         0.60       1.00     0.47
#> FullBath           0.55       0.47     1.00
precision_matrix
#>             OverallQual GarageCars   FullBath
#> OverallQual   1.8254452 -0.8001406 -0.6279288
#> GarageCars   -0.8001406  1.6342549 -0.3280225
#> FullBath     -0.6279288 -0.3280225  1.4995314


3.2.2 Multiply correlation matrix by precision matrix

Multiply the correlation matrix by the precision matrix, and then multiply the precision matrix by the correlation matrix.

cor_x_precis <- corr_matrix %*% precision_matrix

cor_x_precis
#>               OverallQual   GarageCars     FullBath
#> OverallQual  1.000000e+00 5.551115e-17 0.000000e+00
#> GarageCars  -5.551115e-17 1.000000e+00 1.110223e-16
#> FullBath    -1.110223e-16 0.000000e+00 1.000000e+00
precis_x_cor <- precision_matrix %*% corr_matrix

precis_x_cor
#>               OverallQual   GarageCars      FullBath
#> OverallQual  1.000000e+00 1.665335e-16  0.000000e+00
#> GarageCars  -1.665335e-16 1.000000e+00 -1.110223e-16
#> FullBath     0.000000e+00 1.110223e-16  1.000000e+00


3.2.3 Conduct LU decomposition on the matrix cor_x_precis

library(matrixcalc)

lu_cor_x_precis <- matrixcalc::lu.decomposition(cor_x_precis)

L1 <- lu_cor_x_precis$L

U1 <- lu_cor_x_precis$U

print(L1)
#>               [,1]         [,2] [,3]
#> [1,]  1.000000e+00 0.000000e+00    0
#> [2,] -5.551115e-17 1.000000e+00    0
#> [3,] -1.110223e-16 6.162976e-33    1
print(U1)
#>      [,1]         [,2]         [,3]
#> [1,]    1 5.551115e-17 0.000000e+00
#> [2,]    0 1.000000e+00 1.110223e-16
#> [3,]    0 0.000000e+00 1.000000e+00
print( L1 %*% U1)
#>               [,1]         [,2]         [,3]
#> [1,]  1.000000e+00 5.551115e-17 0.000000e+00
#> [2,] -5.551115e-17 1.000000e+00 1.110223e-16
#> [3,] -1.110223e-16 0.000000e+00 1.000000e+00
print( cor_x_precis ) 
#>               OverallQual   GarageCars     FullBath
#> OverallQual  1.000000e+00 5.551115e-17 0.000000e+00
#> GarageCars  -5.551115e-17 1.000000e+00 1.110223e-16
#> FullBath    -1.110223e-16 0.000000e+00 1.000000e+00


3.2.4 Conduct LU decomposition on the matrix precis_x_cor

library(matrixcalc)

lu_precis_x_cor <- matrixcalc::lu.decomposition(precis_x_cor)

L2 <- lu_precis_x_cor$L

U2 <- lu_precis_x_cor$U

print(L2)
#>               [,1]         [,2] [,3]
#> [1,]  1.000000e+00 0.000000e+00    0
#> [2,] -1.665335e-16 1.000000e+00    0
#> [3,]  0.000000e+00 1.110223e-16    1
print(U2)
#>      [,1]         [,2]          [,3]
#> [1,]    1 1.665335e-16  0.000000e+00
#> [2,]    0 1.000000e+00 -1.110223e-16
#> [3,]    0 0.000000e+00  1.000000e+00
print( L2 %*% U2)
#>               [,1]         [,2]          [,3]
#> [1,]  1.000000e+00 1.665335e-16  0.000000e+00
#> [2,] -1.665335e-16 1.000000e+00 -1.110223e-16
#> [3,]  0.000000e+00 1.110223e-16  1.000000e+00
print( precis_x_cor ) 
#>               OverallQual   GarageCars      FullBath
#> OverallQual  1.000000e+00 1.665335e-16  0.000000e+00
#> GarageCars  -1.665335e-16 1.000000e+00 -1.110223e-16
#> FullBath     0.000000e+00 1.110223e-16  1.000000e+00


3.3 Calculus-Based Probability & Statistics.

Many times, it makes sense to fit a closed form distribution to data. Select a variable in the Kaggle.com training dataset that is skewed to the right, shift it so that the minimum value is absolutely above zero if necessary. Then load the MASS package and run fitdistr to fit an exponential probability density function. (See https://stat.ethz.ch/R-manual/R-devel/library/MASS/html/fitdistr.html). Find the optimal value of λ for this distribution, and then take 1000 samples from this exponential distribution using this value (e.g., rexp(1000, λ)). Plot a histogram and compare it with a histogram of your original variable. Using the exponential pdf, find the 5th and 95th percentiles using the cumulative distribution function (CDF). Also generate a 95% confidence interval from the empirical data, assuming normality. Finally, provide the empirical 5th percentile and 95th percentile of the data. Discuss. 10 points


3.3.1 Plot histograms of a few variables to find right skewed variables

# histograms of integer variables

rawTrain %>%
  keep(is.numeric) %>%
  select(BsmtFinSF1, BsmtUnfSF, GrLivArea,
         LotFrontage, GrLivArea, MasVnrArea,
         OpenPorchSF, X1stFlrSF) %>%
  gather() %>%                  
  ggplot(aes(value)) + 
  facet_wrap(~ key, scales = "free") +
  geom_histogram()


3.3.2 Select a right skewed variable

From the histograms above we can see that the variable “BsmtUnfSF” (Unfinished square feet of basement area) appears to be significantly right skewed.


3.3.3 Get basic descriptive statistics on selected variable

summary(rawTrain$BsmtUnfSF)
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>     0.0   223.0   477.5   567.2   808.0  2336.0


3.3.4 Run fitdistr to fit an exponential probability density function

X <- rawTrain$BsmtUnfSF

# fit fit an exponential probability density function
fit_dist <- MASS::fitdistr(X, "exponential")


3.3.5 Find the optimal value of λ for this distribution, and then take 1000 samples from this exponential distribution using this value (e.g., rexp(1000, λ)).

We know that probability density function (pdf) of an exponential distribution is

\[ f(x; \lambda)= \begin{cases} \lambda e^{-\lambda x} & \quad \text{when $x \geq 0$,}\\ 0 & \quad \text{when $x < 0$} \end{cases} \]


3.3.6 Plot a histogram and compare it with a histogram of your original variable. Using the exponential pdf, find the 5th and 95th percentiles using the cumulative distribution function (CDF). Also generate a 95% confidence interval from the empirical data, assuming normality. Finally, provide the empirical 5th percentile and 95th percentile of the data. Discuss. 10 points


3.4 Modeling.

Build some type of multiple regression model and submit your model to the competition board. Provide your complete model summary and results with analysis. Report your Kaggle.com user name and score. 10 points


3.4.1 Load the Test data

urlTest <- 
  "https://raw.githubusercontent.com/esteban-data-enthusiast/data605/main/shared-data/house-prices-advanced-regression-techniques/test.csv"

# load the test data
rawTest <- read.csv(urlTest, stringsAsFactors = TRUE)


3.4.2 Merge the Training and Test data

# In order to preserve the original data, store the raw Train and Test datasets
# in new variables
train <- rawTrain
test  <- rawTest


# Before merging the two data sets, let's remove the extra columns from both
# sides, so that they have the same structure


# Remove the ID columns because we do not need them until we are ready
# to submit our Prediction dataset
trainId <- train$Id
testId  <- test$Id

train$Id <- NULL
test$Id  <- NULL


# Remove the SalePrice columns because we do not need them until we are ready
# to submit our Prediction dataset
trainSalePrice <- train$SalePrice

train$SalePrice <- NULL


# Now that both datasets have the same number of variables
# let's merge both datasets
allData <- rbind(train, test)
allData <- cbind(allData, SourceSet = c(rep("Train", times = dim(train)[1]),
                                  rep("Test",  times = dim(test)[1])
                                  ))


3.4.3 Clean the Data


3.4.3.1 Remove variables which are missing more than 20% of data
allData %>%
  naniar::miss_var_summary() %>%
  arrange(desc(pct_miss)) %>%
  filter(pct_miss > 20)
#> # A tibble: 5 x 3
#>   variable    n_miss pct_miss
#>   <chr>        <int>    <dbl>
#> 1 PoolQC        2909     99.7
#> 2 MiscFeature   2814     96.4
#> 3 Alley         2721     93.2
#> 4 Fence         2348     80.4
#> 5 FireplaceQu   1420     48.6
# From full dataset remove variables with high percentage of missing values
allData$PoolQC      <- NULL
allData$MiscFeature <- NULL
allData$Alley           <- NULL
allData$Fence           <- NULL
allData$FireplaceQu <- NULL


# # From train dataset remove variables with high percentage of missing values
# train$PoolQC      <- NULL
# train$MiscFeature <- NULL
# train$Alley           <- NULL
# train$Fence           <- NULL
# train$FireplaceQu <- NULL
# 
# # From test dataset remove variables with high percentage of missing values
# test$PoolQC       <- NULL
# test$MiscFeature  <- NULL
# test$Alley            <- NULL
# test$Fence            <- NULL
# test$FireplaceQu  <- NULL


3.4.3.2 Impute variables which are missing less than 21% of data
allData %>%
  naniar::miss_var_summary() %>%
  arrange(desc(pct_miss)) %>%
  filter(pct_miss < 21)
#> # A tibble: 75 x 3
#>    variable     n_miss pct_miss
#>    <chr>         <int>    <dbl>
#>  1 LotFrontage     486    16.6 
#>  2 GarageYrBlt     159     5.45
#>  3 GarageFinish    159     5.45
#>  4 GarageQual      159     5.45
#>  5 GarageCond      159     5.45
#>  6 GarageType      157     5.38
#>  7 BsmtCond         82     2.81
#>  8 BsmtExposure     82     2.81
#>  9 BsmtQual         81     2.77
#> 10 BsmtFinType2     80     2.74
#> # ... with 65 more rows
# Perform data imputation for NAs
  
# Replacing missing values by “0” for specific numeric variables where an
#  integer value is expected
  y <- c("LotFrontage", "MasVnrArea", "BsmtFinSF2", "BsmtUnfSF",
         "TotalBsmtSF", "BsmtFullBath", "BsmtHalfBath")
  allData[,y] <- apply(allData[,y], 2, 
                    function(x) {
                            replace(x, is.na(x), 0)
                            })
  
  # For specific factor variables, replace NAs with "None"
  y <- c("BsmtQual", "BsmtExposure", "BsmtFinType1",
         "BsmtFinType2", "GarageType",
         "GarageFinish", "GarageQual", "GarageCond", "BsmtCond")
  allData[,y] <- apply(allData[,y], 2, 
                    function(x) {
                            replace(x, is.na(x), "None")
                            })
  
  # For specific non-numeric variables, replace NAs with the most common value
  y <- c("MSZoning", "Utilities", "Exterior1st", "Exterior2nd",
         "MasVnrType", "Electrical", "KitchenQual", "Functional", "SaleType")
  allData[,y] <- apply(allData[,y], 2, 
                    function(x) {
                            replace(x, is.na(x), names(which.max(table(x))))
                            })
  
  # For specific numeric variables, replace NAs with the median value
  y <- c("GarageCars", "GarageArea", "BsmtFinSF1")
  allData[,y] <- apply(allData[,y], 2, 
                    function(x) {
                            replace(x, is.na(x), median(x, na.rm = T))
                            }
                    )
  
  
  # For GarageYrBlt, we will assume that the Garage was built in the
  # same year as the house
  allData$GarageYrBlt[is.na(allData$GarageYrBlt)] <- 
    allData$YearBuilt[is.na(allData$GarageYrBlt)]

Ensure that there are no more variables with NAs

allData %>%
  naniar::miss_var_summary() %>%
  arrange(desc(pct_miss)) %>%
  filter(pct_miss > 0)
#> # A tibble: 0 x 3
#> # ... with 3 variables: variable <chr>, n_miss <int>, pct_miss <dbl>


3.4.3.3 If necessary convert necessary variables to Factors
# Colect name of variables that are character
class.list <- sapply(allData, class)
class.list.character <- names(class.list[which(class.list=="character")])

class.list.character
#>  [1] "MSZoning"     "Utilities"    "Exterior1st"  "Exterior2nd"  "MasVnrType"  
#>  [6] "BsmtQual"     "BsmtCond"     "BsmtExposure" "BsmtFinType1" "BsmtFinType2"
#> [11] "Electrical"   "KitchenQual"  "Functional"   "GarageType"   "GarageFinish"
#> [16] "GarageQual"   "GarageCond"   "SaleType"     "SourceSet"
# Convert character variables to factors
allData[class.list.character] <- lapply(allData[class.list.character], factor)

Since variable MSSubClass contains 16 unique values and it does not look like a variable on which we can apply math, convert it from numeric to factor

allData$MSSubClass <- factor(allData$MSSubClass)

Ensure that all variables are properly classified

table(sapply(allData, class))
#> 
#>  factor integer numeric 
#>      40      25      10


3.4.4 Select variables with the highest correlation


3.4.5 Fit predictive model

Generate final “Train” and “Test” data sets.

# from the full data set, filter for the "Train" records and the selected variables
final_train <- allData %>%
  filter(SourceSet == "Train") %>%
  select("OverallQual", "GarageArea",
         "YearBuilt", "Neighborhood", "MSSubClass",
         "ExterQual", "KitchenQual", "BsmtQual",
         "HouseStyle") %>%
  mutate(Id = rawTrain$Id,
         SalePrice = log(rawTrain$SalePrice))

#final_train


# from the full data set, filter for the "Test records and the selected variables
final_test <- allData %>%
  filter(SourceSet == "Test") %>%
  select("OverallQual", "GarageArea",
         "YearBuilt", "Neighborhood", "MSSubClass",
         "ExterQual", "KitchenQual", "BsmtQual",
         "HouseStyle") %>%
  mutate(Id = rawTest$Id)

#final_test


Use the select features to fit a Random Forest model to our training dataset

fit_model <- randomForest(SalePrice ~ ., data = final_train, importance = TRUE )


Use the model to predict the Sale Price on the Training data set

pred_test <- exp(predict(fit_model, newdata = final_test))

Export the prediction results to a CSV file to be used for submission to Kaggle

write.csv(x = data.frame(Id = rawTest$Id, SalePrice = pred_test),
          row.names = FALSE,
          file = "submission.csv")
LS0tDQp0aXRsZTogIkRBVEE2MDUgLSBGaW5hbCBFeGFtIC0gUHJvYmxlbSAzIg0KYXV0aG9yOiAiRXN0ZWJhbiBBcmFtYXlvIg0KZGF0ZTogIjIwMjItMDUtMjIiDQpvdXRwdXQ6IG9wZW5pbnRybzo6bGFiX3JlcG9ydA0KLS0tDQoNCmBgYHtyIGdsb2JhbC1vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xsYXBzZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSAiIz4iICkNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KG5hbmlhcikNCmxpYnJhcnkocmFuZG9tRm9yZXN0KQ0KbGlicmFyeShwdXJycikNCmBgYA0KDQoNCg0KIyBGaW5hbCBFeGFtDQoNCjxicj4NCg0KIyMgRmluYWwgUHJvYmxlbSAzLiAzMCBwb2ludHMuDQoNCllvdSBhcmUgdG8gY29tcGV0ZSBpbiB0aGUgSG91c2UgUHJpY2VzOiBBZHZhbmNlZCBSZWdyZXNzaW9uIFRlY2huaXF1ZXMgY29tcGV0aXRpb24NCmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYy9ob3VzZS1wcmljZXMtYWR2YW5jZWQtcmVncmVzc2lvbi10ZWNobmlxdWVzIC4gSSB3YW50IHlvdSB0byBkbyB0aGUgZm9sbG93aW5nLg0KDQo8YnI+DQoNCiMjIyAzLjEgRGVzY3JpcHRpdmUgYW5kIEluZmVyZW50aWFsIFN0YXRpc3RpY3MuDQoNClByb3ZpZGUgdW5pdmFyaWF0ZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGFuZCBhcHByb3ByaWF0ZSBwbG90cyBmb3IgdGhlIHRyYWluaW5nIGRhdGEgc2V0LiBQcm92aWRlIGEgc2NhdHRlcnBsb3QgbWF0cml4IGZvciBhdCBsZWFzdCB0d28gb2YgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyBhbmQgdGhlIGRlcGVuZGVudCB2YXJpYWJsZS4gRGVyaXZlIGEgY29ycmVsYXRpb24gbWF0cml4IGZvciBhbnkgdGhyZWUgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldC4gVGVzdCB0aGUgaHlwb3RoZXNlcyB0aGF0IHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBlYWNoIHBhaXJ3aXNlIHNldCBvZiB2YXJpYWJsZXMgaXMgMCBhbmQgcHJvdmlkZSBhbiA4MCUgY29uZmlkZW5jZSBpbnRlcnZhbC4gRGlzY3VzcyB0aGUgbWVhbmluZyBvZiB5b3VyIGFuYWx5c2lzLiBXb3VsZCB5b3UgYmUgd29ycmllZCBhYm91dCBmYW1pbHl3aXNlIGVycm9yPyBXaHkgb3Igd2h5IG5vdD8gNSBwb2ludHMNCg0KPGJyPg0KDQojIyMjIDMuMS4xIExvYWQgdGhlIHRyYWluaW5nIGRhdGENCg0KYGBge3J9DQoNCiMgZGVmaW5lIHVybCBmb3IgZGF0YQ0KdXJsVHJhaW4gPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9lc3RlYmFuLWRhdGEtZW50aHVzaWFzdC9kYXRhNjA1L21haW4vc2hhcmVkLWRhdGEvaG91c2UtcHJpY2VzLWFkdmFuY2VkLXJlZ3Jlc3Npb24tdGVjaG5pcXVlcy90cmFpbi5jc3YiDQoNCiMgbG9hZCB0aGUgdHJhaW5pbmcgZGF0YQ0KcmF3VHJhaW4gPC0gcmVhZC5jc3YodXJsVHJhaW4sIHN0cmluZ3NBc0ZhY3RvcnMgPSBUUlVFKQ0KDQpgYGANCg0KPGJyPg0KDQojIyMjIDMuMS4yIFVuaXZhcmlhdGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcw0KDQpgYGB7cn0NCnN1bW1hcnkocmF3VHJhaW4pDQpgYGANCg0KPGJyPg0KDQoNCiMjIyMgMy4xLjMgU2NhdHRlciBwbG90IGZvciAyIGluZGVwZW5kZW50IHZhcmlhYmxlcw0KDQpQcm92aWRlIGEgc2NhdHRlcnBsb3QgbWF0cml4IGZvciBhdCBsZWFzdCB0d28gb2YgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyBhbmQgdGhlIGRlcGVuZGVudCB2YXJpYWJsZS4NCg0KDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMSwzKSkNCnBsb3QocmF3VHJhaW4kT3ZlcmFsbFF1YWwsIHJhd1RyYWluJFNhbGVQcmljZSwNCiAgICAgeGxhYiA9ICdPdmVyYWxsIFF1YWxpdHknLCB5bGFiID0gJ1NhbGUgUHJpY2UnKQ0KcGxvdChyYXdUcmFpbiRHYXJhZ2VDYXJzLCAgcmF3VHJhaW4kU2FsZVByaWNlLA0KICAgICB4bGFiID0gJ0dhcmFnZSBDYXJzJywgeWxhYiA9ICdTYWxlIFByaWNlJykNCnBsb3QocmF3VHJhaW4kRnVsbEJhdGgsICAgIHJhd1RyYWluJFNhbGVQcmljZSwNCiAgICAgeGxhYiA9ICdGdWxsIEJhdGhzJywgeWxhYiA9ICdTYWxlIFByaWNlJykNCmBgYA0KDQo8YnI+DQoNCiMjIyMgMy4xLjQgRGVyaXZlIGEgY29ycmVsYXRpb24gbWF0cml4DQoNCkRlcml2ZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCBmb3IgYW55IHRocmVlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuDQoNCmBgYHtyfQ0KDQojIFN1YnNldCBudW1lcmljYWwgdmFyaWFibGVzIHRoYXQgYXJlIGZyb20gdGhlICJ0cmFpbiIgc2V0DQpudW0udHJhaW4gPC0gcmF3VHJhaW4gJT4lDQogIHNlbGVjdCh3aGljaChzYXBwbHkoLixpcy5pbnRlZ2VyKSksIHdoaWNoKHNhcHBseSguLCBpcy5udW1lcmljKSkpDQoNCiMgc2VsZWN0IDMgc3BlY2lmaWMgbnVtZXJpYyB2YXJpYWJsZXMNCm51bS50cmFpbi50aHJlZSA8LSBzZWxlY3QobnVtLnRyYWluLCBPdmVyYWxsUXVhbCwgR2FyYWdlQ2FycywgRnVsbEJhdGgpDQoNCiMgZ2VuZXJhdGUgYSBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIHRoZSAzIG51bWVyaWMgdmFyaWFibGVzDQpjb3JyX21hdHJpeCA8LSByb3VuZChjb3IobnVtLnRyYWluLnRocmVlKSwNCiAgICAgIGRpZ2l0cyA9IDIpDQoNCmNvcnJfbWF0cml4DQpgYGANCg0KPGJyPg0KDQojIyMjIDMuMS41IENvcnJlbGF0aW9uIEh5cG90aGVzZXMgVGVzdA0KDQpUZXN0IHRoZSBoeXBvdGhlc2VzIHRoYXQgdGhlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIGVhY2ggcGFpcndpc2Ugc2V0IG9mIHZhcmlhYmxlcyBpcyAwIGFuZCBwcm92aWRlIGFuIDgwJSBjb25maWRlbmNlIGludGVydmFsLiBEaXNjdXNzIHRoZSBtZWFuaW5nIG9mIHlvdXIgYW5hbHlzaXMuIFdvdWxkIHlvdSBiZSB3b3JyaWVkIGFib3V0IGZhbWlseXdpc2UgZXJyb3I/IFdoeSBvciB3aHkgbm90Pw0KDQoNClRoZSBudWxsIGFuZCBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIGZvciB0aGUgY29ycmVsYXRpb24gdGVzdCBhcmUgYXMgZm9sbG93czoNCg0KJEhfMDogcCA9IDAkIChtZWFuaW5nIHRoYXQgdGhlcmUgaXMgbm8gbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzKQ0KDQokSF8xOiBwIFxuZXEgMCQgKG1lYW5pbmcgdGhhdCB0aGVyZSBpcyBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcykNCg0KDQpgYGB7cn0NCg0KIyB0ZXN0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIDFzdCBhbmQgMm5kIHZhcmlhYmxlcw0KY29yX3Rlc3QxIDwtIGNvci50ZXN0KGZvcm11bGEgPSB+IE92ZXJhbGxRdWFsICsgR2FyYWdlQ2FycywNCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbnVtLnRyYWluLnRocmVlLA0KICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJwZWFyc29uIiwNCiAgICAgICAgICAgICAgICAgICAgICBjb25mLmxldmVsID0gMC44MCkNCg0KIyB0ZXN0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIDFzdCBhbmQgM3JkIHZhcmlhYmxlcw0KY29yX3Rlc3QyIDwtIGNvci50ZXN0KGZvcm11bGEgPSB+IE92ZXJhbGxRdWFsICsgRnVsbEJhdGgsDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG51bS50cmFpbi50aHJlZSwNCiAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIsDQogICAgICAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuODApDQoNCiMgdGVzdCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiAybmQgYW5kIDNyZCB2YXJpYWJsZXMNCmNvcl90ZXN0MyA8LSBjb3IudGVzdChmb3JtdWxhID0gfiBHYXJhZ2VDYXJzICsgRnVsbEJhdGgsDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IG51bS50cmFpbi50aHJlZSwNCiAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIsDQogICAgICAgICAgICAgICAgICAgICAgY29uZi5sZXZlbCA9IDAuODApDQoNCg0KYGBgDQoNCkFuYWx5emUgdGhlIHJlc3VsdHMNCg0KYGBge3J9DQpjb3JfdGVzdDENCmBgYA0KDQpgYGB7cn0NCmNvcl90ZXN0Mg0KYGBgDQoNCmBgYHtyfQ0KY29yX3Rlc3QzDQpgYGANCg0KSW4gYWxsIHRocmVlIHRlc3RzIHdlIHNlZSB0aGF0IHRoZSBwLXZhbHVlIDwgMi4yZS0xNiwgd2hpY2ggaXMgbGVzcyB0aGFuIHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgYWxwaGEgPSAwLjA1LiBXZSBjYW4gY29uY2x1ZGUgdGhhdCB0aGUgdGhyZWUgcGFpcnMgb2YgdmFyaWFibGVzIGFyZSBzaWduaWZpY2FudGx5IGNvcnJlbGF0ZWQgYW1vbmcgZWFjaCBvdGhlci4gV2UgY2FuIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLg0KDQoNCkNhbGN1bGF0ZSB0aGUgRmFtaWx5V2lzZSBlcnJvciAoRldFKQ0KDQokRldFIFxsZXEgKCAxIC0gXGFscGhhICkgXiBjJCANCg0KV2hlcmU6DQoNCiRcYWxwaGEgPSQgYWxwaGEgbGV2ZWwgZm9yIGFuIGluZGl2aWR1YWwgdGVzdCAoZS5nLiAuMDUpDQoNCiRjID0kIE51bWJlciBvZiBjb21wYXJpc29ucw0KDQoNCmBgYHtyfQ0KRldFIDwtIDEgLSAoMSAtLjA1KV4zDQoNCkZXRQ0KYGBgDQpUaGlzIG1lYW5zIHRoYXQgdGhlIHByb2JhYmlsaXR5IG9mIGEgdHlwZSBJIGVycm9yIGlzIGp1c3Qgb3ZlciAxNCUsIHdoaWNoIGlzIGxvdyBjb25zaWRlcmluZyBvbmx5IDMgdGVzdHMgd2VyZSBwZXJmb3JtZWQuIFNvLCB3ZSBkbyBub3QgbmVlZCB0byB3b3JyeSBhYm91dCBGYW1pbHl3aXNlIEVycm9ycy4NCg0KDQo8YnI+DQoNCiMjIyAzLjIgTGluZWFyIEFsZ2VicmEgYW5kIENvcnJlbGF0aW9uLg0KDQpJbnZlcnQgeW91ciBjb3JyZWxhdGlvbiBtYXRyaXggZnJvbSBhYm92ZS4gKFRoaXMgaXMga25vd24gYXMgdGhlIHByZWNpc2lvbiBtYXRyaXggYW5kIGNvbnRhaW5zIHZhcmlhbmNlIGluZmxhdGlvbiBmYWN0b3JzIG9uIHRoZSBkaWFnb25hbC4pIE11bHRpcGx5IHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggYnkgdGhlIHByZWNpc2lvbiBtYXRyaXgsIGFuZCB0aGVuIG11bHRpcGx5IHRoZSBwcmVjaXNpb24gbWF0cml4IGJ5IHRoZSBjb3JyZWxhdGlvbiBtYXRyaXguIENvbmR1Y3QgTFUgZGVjb21wb3NpdGlvbiBvbiB0aGUgbWF0cml4LiA1IHBvaW50cw0KDQo8YnI+DQoNCiMjIyMgMy4yLjEgQ29tcHV0ZSBwcmVjaXNpb24gbWF0cml4DQoNCmBgYHtyfQ0KcHJlY2lzaW9uX21hdHJpeCA8LSBzb2x2ZShjb3JyX21hdHJpeCkNCmBgYA0KDQpgYGB7cn0NCmNvcnJfbWF0cml4DQpgYGANCg0KYGBge3J9DQpwcmVjaXNpb25fbWF0cml4DQpgYGANCg0KPGJyPg0KDQojIyMjIDMuMi4yIE11bHRpcGx5IGNvcnJlbGF0aW9uIG1hdHJpeCBieSBwcmVjaXNpb24gbWF0cml4DQoNCk11bHRpcGx5IHRoZSBjb3JyZWxhdGlvbiBtYXRyaXggYnkgdGhlIHByZWNpc2lvbiBtYXRyaXgsIGFuZCB0aGVuIG11bHRpcGx5IHRoZSBwcmVjaXNpb24gbWF0cml4IGJ5IHRoZSBjb3JyZWxhdGlvbiBtYXRyaXguIA0KDQpgYGB7cn0NCg0KY29yX3hfcHJlY2lzIDwtIGNvcnJfbWF0cml4ICUqJSBwcmVjaXNpb25fbWF0cml4DQoNCmNvcl94X3ByZWNpcw0KDQpgYGANCg0KYGBge3J9DQoNCnByZWNpc194X2NvciA8LSBwcmVjaXNpb25fbWF0cml4ICUqJSBjb3JyX21hdHJpeA0KDQpwcmVjaXNfeF9jb3INCg0KYGBgDQoNCjxicj4NCg0KIyMjIyAzLjIuMyBDb25kdWN0IExVIGRlY29tcG9zaXRpb24gb24gdGhlIG1hdHJpeCBjb3JfeF9wcmVjaXMNCg0KYGBge3J9DQpsaWJyYXJ5KG1hdHJpeGNhbGMpDQoNCmx1X2Nvcl94X3ByZWNpcyA8LSBtYXRyaXhjYWxjOjpsdS5kZWNvbXBvc2l0aW9uKGNvcl94X3ByZWNpcykNCg0KTDEgPC0gbHVfY29yX3hfcHJlY2lzJEwNCg0KVTEgPC0gbHVfY29yX3hfcHJlY2lzJFUNCg0KcHJpbnQoTDEpDQoNCnByaW50KFUxKQ0KDQpwcmludCggTDEgJSolIFUxKQ0KDQpwcmludCggY29yX3hfcHJlY2lzICkgDQoNCmBgYA0KDQo8YnI+DQoNCiMjIyMgMy4yLjQgQ29uZHVjdCBMVSBkZWNvbXBvc2l0aW9uIG9uIHRoZSBtYXRyaXggcHJlY2lzX3hfY29yDQoNCmBgYHtyfQ0KbGlicmFyeShtYXRyaXhjYWxjKQ0KDQpsdV9wcmVjaXNfeF9jb3IgPC0gbWF0cml4Y2FsYzo6bHUuZGVjb21wb3NpdGlvbihwcmVjaXNfeF9jb3IpDQoNCkwyIDwtIGx1X3ByZWNpc194X2NvciRMDQoNClUyIDwtIGx1X3ByZWNpc194X2NvciRVDQoNCnByaW50KEwyKQ0KDQpwcmludChVMikNCg0KcHJpbnQoIEwyICUqJSBVMikNCg0KcHJpbnQoIHByZWNpc194X2NvciApIA0KDQpgYGANCg0KPGJyPg0KDQojIyMgMy4zIENhbGN1bHVzLUJhc2VkIFByb2JhYmlsaXR5ICYgU3RhdGlzdGljcy4NCg0KTWFueSB0aW1lcywgaXQgbWFrZXMgc2Vuc2UgdG8gZml0IGEgY2xvc2VkIGZvcm0gZGlzdHJpYnV0aW9uIHRvIGRhdGEuIFNlbGVjdCBhIHZhcmlhYmxlIGluIHRoZSBLYWdnbGUuY29tIHRyYWluaW5nIGRhdGFzZXQgdGhhdCBpcyBza2V3ZWQgdG8gdGhlIHJpZ2h0LCBzaGlmdCBpdCBzbyB0aGF0IHRoZSBtaW5pbXVtIHZhbHVlIGlzIGFic29sdXRlbHkgYWJvdmUgemVybyBpZiBuZWNlc3NhcnkuIFRoZW4gbG9hZCB0aGUgTUFTUyBwYWNrYWdlIGFuZCBydW4gZml0ZGlzdHIgdG8gZml0IGFuIGV4cG9uZW50aWFsIHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24uIChTZWUgIGh0dHBzOi8vc3RhdC5ldGh6LmNoL1ItbWFudWFsL1ItZGV2ZWwvbGlicmFyeS9NQVNTL2h0bWwvZml0ZGlzdHIuaHRtbCkuIEZpbmQgdGhlIG9wdGltYWwgdmFsdWUgb2YgzrsgZm9yIHRoaXMgZGlzdHJpYnV0aW9uLCBhbmQgdGhlbiB0YWtlIDEwMDAgc2FtcGxlcyBmcm9tIHRoaXMgZXhwb25lbnRpYWwgZGlzdHJpYnV0aW9uIHVzaW5nIHRoaXMgdmFsdWUgKGUuZy4sIHJleHAoMTAwMCwgzrspKS4gUGxvdCBhIGhpc3RvZ3JhbSBhbmQgY29tcGFyZSBpdCB3aXRoIGEgaGlzdG9ncmFtIG9mIHlvdXIgb3JpZ2luYWwgdmFyaWFibGUuIFVzaW5nIHRoZSBleHBvbmVudGlhbCBwZGYsIGZpbmQgdGhlIDV0aCBhbmQgOTV0aCBwZXJjZW50aWxlcyB1c2luZyB0aGUgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKENERikuIEFsc28gZ2VuZXJhdGUgYSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbCBmcm9tIHRoZSBlbXBpcmljYWwgZGF0YSwgYXNzdW1pbmcgbm9ybWFsaXR5LiBGaW5hbGx5LCBwcm92aWRlIHRoZSBlbXBpcmljYWwgNXRoIHBlcmNlbnRpbGUgYW5kIDk1dGggcGVyY2VudGlsZSBvZiB0aGUgZGF0YS4gRGlzY3Vzcy4gMTAgcG9pbnRzDQoNCjxicj4NCg0KIyMjIyAzLjMuMSBQbG90IGhpc3RvZ3JhbXMgb2YgYSBmZXcgdmFyaWFibGVzIHRvIGZpbmQgcmlnaHQgc2tld2VkIHZhcmlhYmxlcw0KDQpgYGB7cn0NCiMgaGlzdG9ncmFtcyBvZiBpbnRlZ2VyIHZhcmlhYmxlcw0KDQpyYXdUcmFpbiAlPiUNCiAga2VlcChpcy5udW1lcmljKSAlPiUNCiAgc2VsZWN0KEJzbXRGaW5TRjEsIEJzbXRVbmZTRiwgR3JMaXZBcmVhLA0KICAgICAgICAgTG90RnJvbnRhZ2UsIEdyTGl2QXJlYSwgTWFzVm5yQXJlYSwNCiAgICAgICAgIE9wZW5Qb3JjaFNGLCBYMXN0RmxyU0YpICU+JQ0KICBnYXRoZXIoKSAlPiUgICAgICAgICAgICAgICAgICANCiAgZ2dwbG90KGFlcyh2YWx1ZSkpICsgDQogIGZhY2V0X3dyYXAofiBrZXksIHNjYWxlcyA9ICJmcmVlIikgKw0KICBnZW9tX2hpc3RvZ3JhbSgpDQpgYGANCg0KPGJyPg0KDQojIyMgMy4zLjIgU2VsZWN0IGEgcmlnaHQgc2tld2VkIHZhcmlhYmxlDQoNCkZyb20gdGhlIGhpc3RvZ3JhbXMgYWJvdmUgd2UgY2FuIHNlZSB0aGF0IHRoZSB2YXJpYWJsZSAiQnNtdFVuZlNGIiAoVW5maW5pc2hlZCBzcXVhcmUgZmVldCBvZiBiYXNlbWVudCBhcmVhKSBhcHBlYXJzIHRvIGJlIHNpZ25pZmljYW50bHkgcmlnaHQgc2tld2VkLg0KDQo8YnI+DQoNCiMjIyAzLjMuMyBHZXQgYmFzaWMgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBvbiBzZWxlY3RlZCB2YXJpYWJsZQ0KDQoNCmBgYHtyfQ0Kc3VtbWFyeShyYXdUcmFpbiRCc210VW5mU0YpDQpgYGANCg0KPGJyPg0KDQojIyMgMy4zLjQgUnVuIGZpdGRpc3RyIHRvIGZpdCBhbiBleHBvbmVudGlhbCBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uDQogDQpgYGB7cn0NCg0KWCA8LSByYXdUcmFpbiRCc210VW5mU0YNCg0KIyBmaXQgZml0IGFuIGV4cG9uZW50aWFsIHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24NCmZpdF9kaXN0IDwtIE1BU1M6OmZpdGRpc3RyKFgsICJleHBvbmVudGlhbCIpDQoNCmBgYA0KDQo8YnI+DQoNCiMjIyAzLjMuNSBGaW5kIHRoZSBvcHRpbWFsIHZhbHVlIG9mIM67IGZvciB0aGlzIGRpc3RyaWJ1dGlvbiwgYW5kIHRoZW4gdGFrZSAxMDAwIHNhbXBsZXMgZnJvbSB0aGlzIGV4cG9uZW50aWFsIGRpc3RyaWJ1dGlvbiB1c2luZyB0aGlzIHZhbHVlIChlLmcuLCByZXhwKDEwMDAsIM67KSkuIA0KDQpXZSBrbm93IHRoYXQgcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiAocGRmKSBvZiBhbiBleHBvbmVudGlhbCBkaXN0cmlidXRpb24gaXMgDQoNCiQkDQpmKHg7IFxsYW1iZGEpPQ0KXGJlZ2lue2Nhc2VzfQ0KXGxhbWJkYSBlXnstXGxhbWJkYSB4fSAmIFxxdWFkIFx0ZXh0e3doZW4gJHggXGdlcSAwJCx9XFwgDQowICYgXHF1YWQgXHRleHR7d2hlbiAkeCA8IDAkfQ0KXGVuZHtjYXNlc30NCiQkDQoNCjxicj4NCg0KIyMjIDMuMy42IFBsb3QgYSBoaXN0b2dyYW0gYW5kIGNvbXBhcmUgaXQgd2l0aCBhIGhpc3RvZ3JhbSBvZiB5b3VyIG9yaWdpbmFsIHZhcmlhYmxlLiBVc2luZyB0aGUgZXhwb25lbnRpYWwgcGRmLCBmaW5kIHRoZSA1dGggYW5kIDk1dGggcGVyY2VudGlsZXMgdXNpbmcgdGhlIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIChDREYpLiBBbHNvIGdlbmVyYXRlIGEgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZnJvbSB0aGUgZW1waXJpY2FsIGRhdGEsIGFzc3VtaW5nIG5vcm1hbGl0eS4gRmluYWxseSwgcHJvdmlkZSB0aGUgZW1waXJpY2FsIDV0aCBwZXJjZW50aWxlIGFuZCA5NXRoIHBlcmNlbnRpbGUgb2YgdGhlIGRhdGEuIERpc2N1c3MuIDEwIHBvaW50cw0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KPGJyPg0KDQojIyMgMy40IE1vZGVsaW5nLg0KDQpCdWlsZCBzb21lIHR5cGUgb2YgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCBhbmQgc3VibWl0IHlvdXIgbW9kZWwgdG8gdGhlIGNvbXBldGl0aW9uIGJvYXJkLiBQcm92aWRlIHlvdXIgY29tcGxldGUgbW9kZWwgc3VtbWFyeSBhbmQgcmVzdWx0cyB3aXRoIGFuYWx5c2lzLiBSZXBvcnQgeW91ciBLYWdnbGUuY29tIHVzZXIgbmFtZSBhbmQgc2NvcmUuIDEwIHBvaW50cw0KDQo8YnI+DQoNCiMjIyMgMy40LjEgTG9hZCB0aGUgVGVzdCBkYXRhDQoNCmBgYHtyfQ0KdXJsVGVzdCA8LSANCiAgImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9lc3RlYmFuLWRhdGEtZW50aHVzaWFzdC9kYXRhNjA1L21haW4vc2hhcmVkLWRhdGEvaG91c2UtcHJpY2VzLWFkdmFuY2VkLXJlZ3Jlc3Npb24tdGVjaG5pcXVlcy90ZXN0LmNzdiINCg0KIyBsb2FkIHRoZSB0ZXN0IGRhdGENCnJhd1Rlc3QgPC0gcmVhZC5jc3YodXJsVGVzdCwgc3RyaW5nc0FzRmFjdG9ycyA9IFRSVUUpDQpgYGANCg0KPGJyPg0KDQojIyMjIDMuNC4yIE1lcmdlIHRoZSBUcmFpbmluZyBhbmQgVGVzdCBkYXRhDQoNCmBgYHtyfQ0KDQojIEluIG9yZGVyIHRvIHByZXNlcnZlIHRoZSBvcmlnaW5hbCBkYXRhLCBzdG9yZSB0aGUgcmF3IFRyYWluIGFuZCBUZXN0IGRhdGFzZXRzDQojIGluIG5ldyB2YXJpYWJsZXMNCnRyYWluIDwtIHJhd1RyYWluDQp0ZXN0ICA8LSByYXdUZXN0DQoNCg0KIyBCZWZvcmUgbWVyZ2luZyB0aGUgdHdvIGRhdGEgc2V0cywgbGV0J3MgcmVtb3ZlIHRoZSBleHRyYSBjb2x1bW5zIGZyb20gYm90aA0KIyBzaWRlcywgc28gdGhhdCB0aGV5IGhhdmUgdGhlIHNhbWUgc3RydWN0dXJlDQoNCg0KIyBSZW1vdmUgdGhlIElEIGNvbHVtbnMgYmVjYXVzZSB3ZSBkbyBub3QgbmVlZCB0aGVtIHVudGlsIHdlIGFyZSByZWFkeQ0KIyB0byBzdWJtaXQgb3VyIFByZWRpY3Rpb24gZGF0YXNldA0KdHJhaW5JZCA8LSB0cmFpbiRJZA0KdGVzdElkICA8LSB0ZXN0JElkDQoNCnRyYWluJElkIDwtIE5VTEwNCnRlc3QkSWQgIDwtIE5VTEwNCg0KDQojIFJlbW92ZSB0aGUgU2FsZVByaWNlIGNvbHVtbnMgYmVjYXVzZSB3ZSBkbyBub3QgbmVlZCB0aGVtIHVudGlsIHdlIGFyZSByZWFkeQ0KIyB0byBzdWJtaXQgb3VyIFByZWRpY3Rpb24gZGF0YXNldA0KdHJhaW5TYWxlUHJpY2UgPC0gdHJhaW4kU2FsZVByaWNlDQoNCnRyYWluJFNhbGVQcmljZSA8LSBOVUxMDQoNCg0KIyBOb3cgdGhhdCBib3RoIGRhdGFzZXRzIGhhdmUgdGhlIHNhbWUgbnVtYmVyIG9mIHZhcmlhYmxlcw0KIyBsZXQncyBtZXJnZSBib3RoIGRhdGFzZXRzDQphbGxEYXRhIDwtIHJiaW5kKHRyYWluLCB0ZXN0KQ0KYWxsRGF0YSA8LSBjYmluZChhbGxEYXRhLCBTb3VyY2VTZXQgPSBjKHJlcCgiVHJhaW4iLCB0aW1lcyA9IGRpbSh0cmFpbilbMV0pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiVGVzdCIsICB0aW1lcyA9IGRpbSh0ZXN0KVsxXSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKQ0KDQpgYGANCg0KPGJyPg0KDQojIyMjIDMuNC4zIENsZWFuIHRoZSBEYXRhDQoNCjxicj4NCg0KIyMjIyMgMy40LjMuMSBSZW1vdmUgdmFyaWFibGVzIHdoaWNoIGFyZSBtaXNzaW5nIG1vcmUgdGhhbiAyMCUgb2YgZGF0YQ0KDQpgYGB7cn0NCmFsbERhdGEgJT4lDQogIG5hbmlhcjo6bWlzc192YXJfc3VtbWFyeSgpICU+JQ0KICBhcnJhbmdlKGRlc2MocGN0X21pc3MpKSAlPiUNCiAgZmlsdGVyKHBjdF9taXNzID4gMjApDQpgYGANCg0KYGBge3J9DQoNCiMgRnJvbSBmdWxsIGRhdGFzZXQgcmVtb3ZlIHZhcmlhYmxlcyB3aXRoIGhpZ2ggcGVyY2VudGFnZSBvZiBtaXNzaW5nIHZhbHVlcw0KYWxsRGF0YSRQb29sUUMJICAgIDwtIE5VTEwNCmFsbERhdGEkTWlzY0ZlYXR1cmUgPC0gTlVMTA0KYWxsRGF0YSRBbGxleQkJICAgIDwtIE5VTEwNCmFsbERhdGEkRmVuY2UJCSAgICA8LSBOVUxMDQphbGxEYXRhJEZpcmVwbGFjZVF1IDwtIE5VTEwNCg0KDQojICMgRnJvbSB0cmFpbiBkYXRhc2V0IHJlbW92ZSB2YXJpYWJsZXMgd2l0aCBoaWdoIHBlcmNlbnRhZ2Ugb2YgbWlzc2luZyB2YWx1ZXMNCiMgdHJhaW4kUG9vbFFDCSAgICA8LSBOVUxMDQojIHRyYWluJE1pc2NGZWF0dXJlIDwtIE5VTEwNCiMgdHJhaW4kQWxsZXkJCSAgICA8LSBOVUxMDQojIHRyYWluJEZlbmNlCQkgICAgPC0gTlVMTA0KIyB0cmFpbiRGaXJlcGxhY2VRdSA8LSBOVUxMDQojIA0KIyAjIEZyb20gdGVzdCBkYXRhc2V0IHJlbW92ZSB2YXJpYWJsZXMgd2l0aCBoaWdoIHBlcmNlbnRhZ2Ugb2YgbWlzc2luZyB2YWx1ZXMNCiMgdGVzdCRQb29sUUMgICAgICAgPC0gTlVMTA0KIyB0ZXN0JE1pc2NGZWF0dXJlICA8LSBOVUxMDQojIHRlc3QkQWxsZXkJCSAgICA8LSBOVUxMDQojIHRlc3QkRmVuY2UJCSAgICA8LSBOVUxMDQojIHRlc3QkRmlyZXBsYWNlUXUgIDwtIE5VTEwNCmBgYA0KDQoNCjxicj4NCg0KIyMjIyMgMy40LjMuMiBJbXB1dGUgdmFyaWFibGVzIHdoaWNoIGFyZSBtaXNzaW5nIGxlc3MgdGhhbiAyMSUgb2YgZGF0YQ0KDQpgYGB7cn0NCmFsbERhdGEgJT4lDQogIG5hbmlhcjo6bWlzc192YXJfc3VtbWFyeSgpICU+JQ0KICBhcnJhbmdlKGRlc2MocGN0X21pc3MpKSAlPiUNCiAgZmlsdGVyKHBjdF9taXNzIDwgMjEpDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCmBgYHtyfQ0KDQojIFBlcmZvcm0gZGF0YSBpbXB1dGF0aW9uIGZvciBOQXMNCiAgDQojIFJlcGxhY2luZyBtaXNzaW5nIHZhbHVlcyBieSDigJww4oCdIGZvciBzcGVjaWZpYyBudW1lcmljIHZhcmlhYmxlcyB3aGVyZSBhbg0KIyAgaW50ZWdlciB2YWx1ZSBpcyBleHBlY3RlZA0KICB5IDwtIGMoIkxvdEZyb250YWdlIiwgIk1hc1ZuckFyZWEiLCAiQnNtdEZpblNGMiIsICJCc210VW5mU0YiLA0KICAgICAgICAgIlRvdGFsQnNtdFNGIiwgIkJzbXRGdWxsQmF0aCIsICJCc210SGFsZkJhdGgiKQ0KICBhbGxEYXRhWyx5XSA8LSBhcHBseShhbGxEYXRhWyx5XSwgMiwgDQogICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlKHgsIGlzLm5hKHgpLCAwKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pDQogIA0KICAjIEZvciBzcGVjaWZpYyBmYWN0b3IgdmFyaWFibGVzLCByZXBsYWNlIE5BcyB3aXRoICJOb25lIg0KICB5IDwtIGMoIkJzbXRRdWFsIiwgIkJzbXRFeHBvc3VyZSIsICJCc210RmluVHlwZTEiLA0KICAgICAgICAgIkJzbXRGaW5UeXBlMiIsICJHYXJhZ2VUeXBlIiwNCiAgICAgICAgICJHYXJhZ2VGaW5pc2giLCAiR2FyYWdlUXVhbCIsICJHYXJhZ2VDb25kIiwgIkJzbXRDb25kIikNCiAgYWxsRGF0YVsseV0gPC0gYXBwbHkoYWxsRGF0YVsseV0sIDIsIA0KICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZSh4LCBpcy5uYSh4KSwgIk5vbmUiKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pDQogIA0KICAjIEZvciBzcGVjaWZpYyBub24tbnVtZXJpYyB2YXJpYWJsZXMsIHJlcGxhY2UgTkFzIHdpdGggdGhlIG1vc3QgY29tbW9uIHZhbHVlDQogIHkgPC0gYygiTVNab25pbmciLCAiVXRpbGl0aWVzIiwgIkV4dGVyaW9yMXN0IiwgIkV4dGVyaW9yMm5kIiwNCiAgICAgICAgICJNYXNWbnJUeXBlIiwgIkVsZWN0cmljYWwiLCAiS2l0Y2hlblF1YWwiLCAiRnVuY3Rpb25hbCIsICJTYWxlVHlwZSIpDQogIGFsbERhdGFbLHldIDwtIGFwcGx5KGFsbERhdGFbLHldLCAyLCANCiAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UoeCwgaXMubmEoeCksIG5hbWVzKHdoaWNoLm1heCh0YWJsZSh4KSkpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pDQogIA0KICAjIEZvciBzcGVjaWZpYyBudW1lcmljIHZhcmlhYmxlcywgcmVwbGFjZSBOQXMgd2l0aCB0aGUgbWVkaWFuIHZhbHVlDQogIHkgPC0gYygiR2FyYWdlQ2FycyIsICJHYXJhZ2VBcmVhIiwgIkJzbXRGaW5TRjEiKQ0KICBhbGxEYXRhWyx5XSA8LSBhcHBseShhbGxEYXRhWyx5XSwgMiwgDQogICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlKHgsIGlzLm5hKHgpLCBtZWRpYW4oeCwgbmEucm0gPSBUKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICkNCiAgDQogIA0KICAjIEZvciBHYXJhZ2VZckJsdCwgd2Ugd2lsbCBhc3N1bWUgdGhhdCB0aGUgR2FyYWdlIHdhcyBidWlsdCBpbiB0aGUNCiAgIyBzYW1lIHllYXIgYXMgdGhlIGhvdXNlDQogIGFsbERhdGEkR2FyYWdlWXJCbHRbaXMubmEoYWxsRGF0YSRHYXJhZ2VZckJsdCldIDwtIA0KICAgIGFsbERhdGEkWWVhckJ1aWx0W2lzLm5hKGFsbERhdGEkR2FyYWdlWXJCbHQpXQ0KICANCiAgDQpgYGANCg0KRW5zdXJlIHRoYXQgdGhlcmUgYXJlIG5vIG1vcmUgdmFyaWFibGVzIHdpdGggTkFzDQoNCmBgYHtyfQ0KYWxsRGF0YSAlPiUNCiAgbmFuaWFyOjptaXNzX3Zhcl9zdW1tYXJ5KCkgJT4lDQogIGFycmFuZ2UoZGVzYyhwY3RfbWlzcykpICU+JQ0KICBmaWx0ZXIocGN0X21pc3MgPiAwKQ0KYGBgDQoNCjxicj4NCg0KIyMjIyMgMy40LjMuMyBJZiBuZWNlc3NhcnkgY29udmVydCBuZWNlc3NhcnkgdmFyaWFibGVzIHRvIEZhY3RvcnMNCg0KYGBge3J9DQojIENvbGVjdCBuYW1lIG9mIHZhcmlhYmxlcyB0aGF0IGFyZSBjaGFyYWN0ZXINCmNsYXNzLmxpc3QgPC0gc2FwcGx5KGFsbERhdGEsIGNsYXNzKQ0KY2xhc3MubGlzdC5jaGFyYWN0ZXIgPC0gbmFtZXMoY2xhc3MubGlzdFt3aGljaChjbGFzcy5saXN0PT0iY2hhcmFjdGVyIildKQ0KDQpjbGFzcy5saXN0LmNoYXJhY3Rlcg0KYGBgDQoNCmBgYHtyfQ0KIyBDb252ZXJ0IGNoYXJhY3RlciB2YXJpYWJsZXMgdG8gZmFjdG9ycw0KYWxsRGF0YVtjbGFzcy5saXN0LmNoYXJhY3Rlcl0gPC0gbGFwcGx5KGFsbERhdGFbY2xhc3MubGlzdC5jaGFyYWN0ZXJdLCBmYWN0b3IpDQpgYGANCg0KU2luY2UgdmFyaWFibGUgTVNTdWJDbGFzcyBjb250YWlucyAxNiB1bmlxdWUgdmFsdWVzIGFuZCBpdCBkb2VzIG5vdCBsb29rIGxpa2UgYSB2YXJpYWJsZSBvbiB3aGljaCB3ZSBjYW4gYXBwbHkgbWF0aCwgY29udmVydCBpdCBmcm9tIG51bWVyaWMgdG8gZmFjdG9yDQoNCmBgYHtyfQ0KYWxsRGF0YSRNU1N1YkNsYXNzIDwtIGZhY3RvcihhbGxEYXRhJE1TU3ViQ2xhc3MpDQpgYGANCg0KDQpFbnN1cmUgdGhhdCBhbGwgdmFyaWFibGVzIGFyZSBwcm9wZXJseSBjbGFzc2lmaWVkDQoNCmBgYHtyfQ0KdGFibGUoc2FwcGx5KGFsbERhdGEsIGNsYXNzKSkNCg0KYGBgDQoNCg0KPGJyPg0KDQojIyMjIDMuNC40IFNlbGVjdCB2YXJpYWJsZXMgd2l0aCB0aGUgaGlnaGVzdCBjb3JyZWxhdGlvbg0KDQoNCg0KDQoNCjxicj4NCg0KIyMjIyAzLjQuNSBGaXQgcHJlZGljdGl2ZSBtb2RlbA0KDQoNCkdlbmVyYXRlIGZpbmFsICJUcmFpbiIgYW5kICJUZXN0IiBkYXRhIHNldHMuDQoNCmBgYHtyfQ0KDQojIGZyb20gdGhlIGZ1bGwgZGF0YSBzZXQsIGZpbHRlciBmb3IgdGhlICJUcmFpbiIgcmVjb3JkcyBhbmQgdGhlIHNlbGVjdGVkIHZhcmlhYmxlcw0KZmluYWxfdHJhaW4gPC0gYWxsRGF0YSAlPiUNCiAgZmlsdGVyKFNvdXJjZVNldCA9PSAiVHJhaW4iKSAlPiUNCiAgc2VsZWN0KCJPdmVyYWxsUXVhbCIsICJHYXJhZ2VBcmVhIiwNCiAgICAgICAgICJZZWFyQnVpbHQiLCAiTmVpZ2hib3Job29kIiwgIk1TU3ViQ2xhc3MiLA0KICAgICAgICAgIkV4dGVyUXVhbCIsICJLaXRjaGVuUXVhbCIsICJCc210UXVhbCIsDQogICAgICAgICAiSG91c2VTdHlsZSIpICU+JQ0KICBtdXRhdGUoSWQgPSByYXdUcmFpbiRJZCwNCiAgICAgICAgIFNhbGVQcmljZSA9IGxvZyhyYXdUcmFpbiRTYWxlUHJpY2UpKQ0KDQojZmluYWxfdHJhaW4NCg0KDQojIGZyb20gdGhlIGZ1bGwgZGF0YSBzZXQsIGZpbHRlciBmb3IgdGhlICJUZXN0IHJlY29yZHMgYW5kIHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMNCmZpbmFsX3Rlc3QgPC0gYWxsRGF0YSAlPiUNCiAgZmlsdGVyKFNvdXJjZVNldCA9PSAiVGVzdCIpICU+JQ0KICBzZWxlY3QoIk92ZXJhbGxRdWFsIiwgIkdhcmFnZUFyZWEiLA0KICAgICAgICAgIlllYXJCdWlsdCIsICJOZWlnaGJvcmhvb2QiLCAiTVNTdWJDbGFzcyIsDQogICAgICAgICAiRXh0ZXJRdWFsIiwgIktpdGNoZW5RdWFsIiwgIkJzbXRRdWFsIiwNCiAgICAgICAgICJIb3VzZVN0eWxlIikgJT4lDQogIG11dGF0ZShJZCA9IHJhd1Rlc3QkSWQpDQoNCiNmaW5hbF90ZXN0DQoNCg0KYGBgDQoNCg0KPGJyPg0KDQpVc2UgdGhlIHNlbGVjdCBmZWF0dXJlcyB0byBmaXQgYSBSYW5kb20gRm9yZXN0IG1vZGVsIHRvIG91ciB0cmFpbmluZyBkYXRhc2V0DQoNCmBgYHtyfQ0KZml0X21vZGVsIDwtIHJhbmRvbUZvcmVzdChTYWxlUHJpY2UgfiAuLCBkYXRhID0gZmluYWxfdHJhaW4sIGltcG9ydGFuY2UgPSBUUlVFICkNCmBgYA0KDQo8YnI+DQoNClVzZSB0aGUgbW9kZWwgdG8gcHJlZGljdCB0aGUgU2FsZSBQcmljZSBvbiB0aGUgVHJhaW5pbmcgZGF0YSBzZXQNCg0KYGBge3J9DQpwcmVkX3Rlc3QgPC0gZXhwKHByZWRpY3QoZml0X21vZGVsLCBuZXdkYXRhID0gZmluYWxfdGVzdCkpDQpgYGANCg0KRXhwb3J0IHRoZSBwcmVkaWN0aW9uIHJlc3VsdHMgdG8gYSBDU1YgZmlsZSB0byBiZSB1c2VkIGZvciBzdWJtaXNzaW9uIHRvIEthZ2dsZQ0KDQpgYGB7cn0NCndyaXRlLmNzdih4ID0gZGF0YS5mcmFtZShJZCA9IHJhd1Rlc3QkSWQsIFNhbGVQcmljZSA9IHByZWRfdGVzdCksDQogICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UsDQogICAgICAgICAgZmlsZSA9ICJzdWJtaXNzaW9uLmNzdiIpDQpgYGANCg0KPCEtLSBNeSBrYWdnbGUgdXNlcm5hbWUgaXMgIkVzdGViYW4gQXJhbWF5byIgYW5kIG15IHNjb3JlIHdhcyAiU2NvcmU6IDAuMTc0NzciIGluIHBvc2l0aW9uIDMzMTQgb3V0IG9mIDQyNDUuIDooIC0tPg0KDQoNCg0KDQo=