1. Carga de librerias y datos.

1.1 Carga de librerias

library(leaps)
library(ggplot2)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
library(readr)
library(dplyr)

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
library(fastDummies)
library(glmnet)
Loading required package: Matrix
Loading required package: foreach
Loaded glmnet 2.0-18
library(caret)
Loading required package: lattice
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

1.2 Carga de datos


rawdata <- read_csv("ToyotaCorolla.csv")
Parsed with column specification:
cols(
  .default = col_double(),
  Model = col_character(),
  Fuel_Type = col_character()
)
See spec(...) for full column specifications.

1.3 Sumario de datos


summary(rawdata)
       Id            Model               Price         Age_08_04       Mfg_Month         Mfg_Year          KM        
 Min.   :   1.0   Length:1436        Min.   : 4350   Min.   : 1.00   Min.   : 1.000   Min.   :1998   Min.   :     1  
 1st Qu.: 361.8   Class :character   1st Qu.: 8450   1st Qu.:44.00   1st Qu.: 3.000   1st Qu.:1998   1st Qu.: 43000  
 Median : 721.5   Mode  :character   Median : 9900   Median :61.00   Median : 5.000   Median :1999   Median : 63390  
 Mean   : 721.6                      Mean   :10731   Mean   :55.95   Mean   : 5.549   Mean   :2000   Mean   : 68533  
 3rd Qu.:1081.2                      3rd Qu.:11950   3rd Qu.:70.00   3rd Qu.: 8.000   3rd Qu.:2001   3rd Qu.: 87021  
 Max.   :1442.0                      Max.   :32500   Max.   :80.00   Max.   :12.000   Max.   :2004   Max.   :243000  
  Fuel_Type               HP          Met_Color        Automatic             cc            Doors         Cylinders
 Length:1436        Min.   : 69.0   Min.   :0.0000   Min.   :0.00000   Min.   : 1300   Min.   :2.000   Min.   :4  
 Class :character   1st Qu.: 90.0   1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.: 1400   1st Qu.:3.000   1st Qu.:4  
 Mode  :character   Median :110.0   Median :1.0000   Median :0.00000   Median : 1600   Median :4.000   Median :4  
                    Mean   :101.5   Mean   :0.6748   Mean   :0.05571   Mean   : 1577   Mean   :4.033   Mean   :4  
                    3rd Qu.:110.0   3rd Qu.:1.0000   3rd Qu.:0.00000   3rd Qu.: 1600   3rd Qu.:5.000   3rd Qu.:4  
                    Max.   :192.0   Max.   :1.0000   Max.   :1.00000   Max.   :16000   Max.   :5.000   Max.   :4  
     Gears       Quarterly_Tax        Weight     Mfr_Guarantee    BOVAG_Guarantee  Guarantee_Period      ABS        
 Min.   :3.000   Min.   : 19.00   Min.   :1000   Min.   :0.0000   Min.   :0.0000   Min.   : 3.000   Min.   :0.0000  
 1st Qu.:5.000   1st Qu.: 69.00   1st Qu.:1040   1st Qu.:0.0000   1st Qu.:1.0000   1st Qu.: 3.000   1st Qu.:1.0000  
 Median :5.000   Median : 85.00   Median :1070   Median :0.0000   Median :1.0000   Median : 3.000   Median :1.0000  
 Mean   :5.026   Mean   : 87.12   Mean   :1072   Mean   :0.4095   Mean   :0.8955   Mean   : 3.815   Mean   :0.8134  
 3rd Qu.:5.000   3rd Qu.: 85.00   3rd Qu.:1085   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.: 3.000   3rd Qu.:1.0000  
 Max.   :6.000   Max.   :283.00   Max.   :1615   Max.   :1.0000   Max.   :1.0000   Max.   :36.000   Max.   :1.0000  
    Airbag_1         Airbag_2          Airco        Automatic_airco   Boardcomputer      CD_Player     
 Min.   :0.0000   Min.   :0.0000   Min.   :0.0000   Min.   :0.00000   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:1.0000   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.00000   1st Qu.:0.0000   1st Qu.:0.0000  
 Median :1.0000   Median :1.0000   Median :1.0000   Median :0.00000   Median :0.0000   Median :0.0000  
 Mean   :0.9708   Mean   :0.7228   Mean   :0.5084   Mean   :0.05641   Mean   :0.2946   Mean   :0.2187  
 3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:0.00000   3rd Qu.:1.0000   3rd Qu.:0.0000  
 Max.   :1.0000   Max.   :1.0000   Max.   :1.0000   Max.   :1.00000   Max.   :1.0000   Max.   :1.0000  
  Central_Lock    Powered_Windows Power_Steering       Radio          Mistlamps      Sport_Model     Backseat_Divider
 Min.   :0.0000   Min.   :0.000   Min.   :0.0000   Min.   :0.0000   Min.   :0.000   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:0.0000   1st Qu.:0.000   1st Qu.:1.0000   1st Qu.:0.0000   1st Qu.:0.000   1st Qu.:0.0000   1st Qu.:1.0000  
 Median :1.0000   Median :1.000   Median :1.0000   Median :0.0000   Median :0.000   Median :0.0000   Median :1.0000  
 Mean   :0.5801   Mean   :0.562   Mean   :0.9777   Mean   :0.1462   Mean   :0.257   Mean   :0.3001   Mean   :0.7702  
 3rd Qu.:1.0000   3rd Qu.:1.000   3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.:1.000   3rd Qu.:1.0000   3rd Qu.:1.0000  
 Max.   :1.0000   Max.   :1.000   Max.   :1.0000   Max.   :1.0000   Max.   :1.000   Max.   :1.0000   Max.   :1.0000  
  Metallic_Rim    Radio_cassette      Tow_Bar      
 Min.   :0.0000   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:0.0000  
 Median :0.0000   Median :0.0000   Median :0.0000  
 Mean   :0.2047   Mean   :0.1455   Mean   :0.2779  
 3rd Qu.:0.0000   3rd Qu.:0.0000   3rd Qu.:1.0000  
 Max.   :1.0000   Max.   :1.0000   Max.   :1.0000  

1.4 Estructura de datos

str(rawdata)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    1436 obs. of  37 variables:
 $ Id              : num  1 2 3 4 5 6 7 8 9 10 ...
 $ Model           : chr  "TOYOTA Corolla 2.0 D4D HATCHB TERRA 2/3-Doors" "TOYOTA Corolla 2.0 D4D HATCHB TERRA 2/3-Doors" "?TOYOTA Corolla 2.0 D4D HATCHB TERRA 2/3-Doors" "TOYOTA Corolla 2.0 D4D HATCHB TERRA 2/3-Doors" ...
 $ Price           : num  13500 13750 13950 14950 13750 ...
 $ Age_08_04       : num  23 23 24 26 30 32 27 30 27 23 ...
 $ Mfg_Month       : num  10 10 9 7 3 1 6 3 6 10 ...
 $ Mfg_Year        : num  2002 2002 2002 2002 2002 ...
 $ KM              : num  46986 72937 41711 48000 38500 ...
 $ Fuel_Type       : chr  "Diesel" "Diesel" "Diesel" "Diesel" ...
 $ HP              : num  90 90 90 90 90 90 90 90 192 69 ...
 $ Met_Color       : num  1 1 1 0 0 0 1 1 0 0 ...
 $ Automatic       : num  0 0 0 0 0 0 0 0 0 0 ...
 $ cc              : num  2000 2000 2000 2000 2000 2000 2000 2000 1800 1900 ...
 $ Doors           : num  3 3 3 3 3 3 3 3 3 3 ...
 $ Cylinders       : num  4 4 4 4 4 4 4 4 4 4 ...
 $ Gears           : num  5 5 5 5 5 5 5 5 5 5 ...
 $ Quarterly_Tax   : num  210 210 210 210 210 210 210 210 100 185 ...
 $ Weight          : num  1165 1165 1165 1165 1170 ...
 $ Mfr_Guarantee   : num  0 0 1 1 1 0 0 1 0 0 ...
 $ BOVAG_Guarantee : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Guarantee_Period: num  3 3 3 3 3 3 3 3 3 3 ...
 $ ABS             : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Airbag_1        : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Airbag_2        : num  1 1 1 1 1 1 1 1 0 1 ...
 $ Airco           : num  0 1 0 0 1 1 1 1 1 1 ...
 $ Automatic_airco : num  0 0 0 0 0 0 0 0 0 0 ...
 $ Boardcomputer   : num  1 1 1 1 1 1 1 1 0 1 ...
 $ CD_Player       : num  0 1 0 0 0 0 0 1 0 0 ...
 $ Central_Lock    : num  1 1 0 0 1 1 1 1 1 0 ...
 $ Powered_Windows : num  1 0 0 0 1 1 1 1 1 0 ...
 $ Power_Steering  : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Radio           : num  0 0 0 0 0 0 0 0 1 0 ...
 $ Mistlamps       : num  0 0 0 0 1 1 0 0 0 0 ...
 $ Sport_Model     : num  0 0 0 0 0 0 1 0 0 0 ...
 $ Backseat_Divider: num  1 1 1 1 1 1 1 1 0 1 ...
 $ Metallic_Rim    : num  0 0 0 0 0 0 0 0 1 0 ...
 $ Radio_cassette  : num  0 0 0 0 0 0 0 0 1 0 ...
 $ Tow_Bar         : num  0 0 0 0 0 0 0 0 0 0 ...
 - attr(*, "spec")=
  .. cols(
  ..   Id = col_double(),
  ..   Model = col_character(),
  ..   Price = col_double(),
  ..   Age_08_04 = col_double(),
  ..   Mfg_Month = col_double(),
  ..   Mfg_Year = col_double(),
  ..   KM = col_double(),
  ..   Fuel_Type = col_character(),
  ..   HP = col_double(),
  ..   Met_Color = col_double(),
  ..   Automatic = col_double(),
  ..   cc = col_double(),
  ..   Doors = col_double(),
  ..   Cylinders = col_double(),
  ..   Gears = col_double(),
  ..   Quarterly_Tax = col_double(),
  ..   Weight = col_double(),
  ..   Mfr_Guarantee = col_double(),
  ..   BOVAG_Guarantee = col_double(),
  ..   Guarantee_Period = col_double(),
  ..   ABS = col_double(),
  ..   Airbag_1 = col_double(),
  ..   Airbag_2 = col_double(),
  ..   Airco = col_double(),
  ..   Automatic_airco = col_double(),
  ..   Boardcomputer = col_double(),
  ..   CD_Player = col_double(),
  ..   Central_Lock = col_double(),
  ..   Powered_Windows = col_double(),
  ..   Power_Steering = col_double(),
  ..   Radio = col_double(),
  ..   Mistlamps = col_double(),
  ..   Sport_Model = col_double(),
  ..   Backseat_Divider = col_double(),
  ..   Metallic_Rim = col_double(),
  ..   Radio_cassette = col_double(),
  ..   Tow_Bar = col_double()
  .. )

1.5 Buscando datos faltantes


sum(is.na(rawdata))
[1] 0

1.6 quitando las variables id y model


rawdata2 <- rawdata
rawdata2$Id <- NULL
rawdata2$Model <- NULL

2. Limpieza de dataset

2.1 Transformar a factores


rawdata2$Fuel_Type <- as.factor(rawdata2$Fuel_Type)

#rawdata2$Cylinders <- as.factor(rawdata2$Cylinders)
##(rawdata, aes(x = Price, y = Weight, colour = Fuel_Type))+ geom_point()

2.2 Filtrado


rawdata3 <- rawdata2

rawdata3$cc <- ifelse(rawdata3$cc == 16000, 1600, rawdata3$cc) 
rawdata3 <- filter(rawdata3, rawdata3$Weight < 1200)
rawdata3 <- filter(rawdata3, rawdata3$KM > 1000)
rawdata3 <- filter(rawdata3, rawdata3$KM < 150000)

2.3 Eliminando variables


rawdata3$Mfg_Month <- NULL
rawdata3$Mfg_Year <- NULL
rawdata3$Cylinders <-NULL

2.4 Estructura de nuevo dataset

str(rawdata3)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    1340 obs. of  32 variables:
 $ Price           : num  13500 13750 13950 14950 13750 ...
 $ Age_08_04       : num  23 23 24 26 30 32 27 23 25 22 ...
 $ KM              : num  46986 72937 41711 48000 38500 ...
 $ Fuel_Type       : Factor w/ 3 levels "CNG","Diesel",..: 2 2 2 2 2 2 3 2 3 3 ...
 $ HP              : num  90 90 90 90 90 90 192 69 192 192 ...
 $ Met_Color       : num  1 1 1 0 0 0 0 0 0 0 ...
 $ Automatic       : num  0 0 0 0 0 0 0 0 0 0 ...
 $ cc              : num  2000 2000 2000 2000 2000 2000 1800 1900 1800 1800 ...
 $ Doors           : num  3 3 3 3 3 3 3 3 3 3 ...
 $ Gears           : num  5 5 5 5 5 5 5 5 6 6 ...
 $ Quarterly_Tax   : num  210 210 210 210 210 210 100 185 100 100 ...
 $ Weight          : num  1165 1165 1165 1165 1170 ...
 $ Mfr_Guarantee   : num  0 0 1 1 1 0 0 0 1 1 ...
 $ BOVAG_Guarantee : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Guarantee_Period: num  3 3 3 3 3 3 3 3 12 3 ...
 $ ABS             : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Airbag_1        : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Airbag_2        : num  1 1 1 1 1 1 0 1 1 1 ...
 $ Airco           : num  0 1 0 0 1 1 1 1 1 1 ...
 $ Automatic_airco : num  0 0 0 0 0 0 0 0 1 1 ...
 $ Boardcomputer   : num  1 1 1 1 1 1 0 1 0 1 ...
 $ CD_Player       : num  0 1 0 0 0 0 0 0 1 0 ...
 $ Central_Lock    : num  1 1 0 0 1 1 1 0 1 1 ...
 $ Powered_Windows : num  1 0 0 0 1 1 1 0 1 1 ...
 $ Power_Steering  : num  1 1 1 1 1 1 1 1 1 1 ...
 $ Radio           : num  0 0 0 0 0 0 1 0 0 0 ...
 $ Mistlamps       : num  0 0 0 0 1 1 0 0 0 1 ...
 $ Sport_Model     : num  0 0 0 0 0 0 0 0 0 1 ...
 $ Backseat_Divider: num  1 1 1 1 1 1 0 1 0 1 ...
 $ Metallic_Rim    : num  0 0 0 0 0 0 1 0 1 1 ...
 $ Radio_cassette  : num  0 0 0 0 0 0 1 0 0 0 ...
 $ Tow_Bar         : num  0 0 0 0 0 0 0 0 0 0 ...
 - attr(*, "spec")=
  .. cols(
  ..   Id = col_double(),
  ..   Model = col_character(),
  ..   Price = col_double(),
  ..   Age_08_04 = col_double(),
  ..   Mfg_Month = col_double(),
  ..   Mfg_Year = col_double(),
  ..   KM = col_double(),
  ..   Fuel_Type = col_character(),
  ..   HP = col_double(),
  ..   Met_Color = col_double(),
  ..   Automatic = col_double(),
  ..   cc = col_double(),
  ..   Doors = col_double(),
  ..   Cylinders = col_double(),
  ..   Gears = col_double(),
  ..   Quarterly_Tax = col_double(),
  ..   Weight = col_double(),
  ..   Mfr_Guarantee = col_double(),
  ..   BOVAG_Guarantee = col_double(),
  ..   Guarantee_Period = col_double(),
  ..   ABS = col_double(),
  ..   Airbag_1 = col_double(),
  ..   Airbag_2 = col_double(),
  ..   Airco = col_double(),
  ..   Automatic_airco = col_double(),
  ..   Boardcomputer = col_double(),
  ..   CD_Player = col_double(),
  ..   Central_Lock = col_double(),
  ..   Powered_Windows = col_double(),
  ..   Power_Steering = col_double(),
  ..   Radio = col_double(),
  ..   Mistlamps = col_double(),
  ..   Sport_Model = col_double(),
  ..   Backseat_Divider = col_double(),
  ..   Metallic_Rim = col_double(),
  ..   Radio_cassette = col_double(),
  ..   Tow_Bar = col_double()
  .. )
  • Luego de realizar la limpieza el datset contiene 1340 observaciones y 32 variables

3. Best Subset Selection

regfit.full = regsubsets(Price ~., data = rawdata3, nvmax=31)
summary(regfit.full)
Subset selection object
Call: regsubsets.formula(Price ~ ., data = rawdata3, nvmax = 31)
32 Variables  (and intercept)
                 Forced in Forced out
Age_08_04            FALSE      FALSE
KM                   FALSE      FALSE
Fuel_TypeDiesel      FALSE      FALSE
Fuel_TypePetrol      FALSE      FALSE
HP                   FALSE      FALSE
Met_Color            FALSE      FALSE
Automatic            FALSE      FALSE
cc                   FALSE      FALSE
Doors                FALSE      FALSE
Gears                FALSE      FALSE
Quarterly_Tax        FALSE      FALSE
Weight               FALSE      FALSE
Mfr_Guarantee        FALSE      FALSE
BOVAG_Guarantee      FALSE      FALSE
Guarantee_Period     FALSE      FALSE
ABS                  FALSE      FALSE
Airbag_1             FALSE      FALSE
Airbag_2             FALSE      FALSE
Airco                FALSE      FALSE
Automatic_airco      FALSE      FALSE
Boardcomputer        FALSE      FALSE
CD_Player            FALSE      FALSE
Central_Lock         FALSE      FALSE
Powered_Windows      FALSE      FALSE
Power_Steering       FALSE      FALSE
Radio                FALSE      FALSE
Mistlamps            FALSE      FALSE
Sport_Model          FALSE      FALSE
Backseat_Divider     FALSE      FALSE
Metallic_Rim         FALSE      FALSE
Radio_cassette       FALSE      FALSE
Tow_Bar              FALSE      FALSE
1 subsets of each size up to 31
Selection Algorithm: exhaustive
          Age_08_04 KM  Fuel_TypeDiesel Fuel_TypePetrol HP  Met_Color Automatic cc  Doors Gears Quarterly_Tax Weight
1  ( 1 )  "*"       " " " "             " "             " " " "       " "       " " " "   " "   " "           " "   
2  ( 1 )  "*"       " " " "             " "             " " " "       " "       " " " "   " "   " "           " "   
3  ( 1 )  "*"       " " " "             "*"             " " " "       " "       " " " "   " "   " "           "*"   
4  ( 1 )  "*"       " " " "             "*"             " " " "       " "       " " " "   " "   " "           "*"   
5  ( 1 )  "*"       "*" "*"             " "             " " " "       " "       " " " "   " "   " "           "*"   
6  ( 1 )  "*"       "*" " "             " "             "*" " "       " "       "*" " "   " "   " "           "*"   
7  ( 1 )  "*"       "*" " "             " "             "*" " "       " "       "*" " "   " "   " "           "*"   
8  ( 1 )  "*"       "*" " "             "*"             " " " "       " "       " " " "   " "   "*"           "*"   
9  ( 1 )  "*"       "*" " "             " "             "*" " "       " "       "*" " "   " "   " "           "*"   
10  ( 1 ) "*"       "*" " "             " "             "*" " "       " "       "*" " "   " "   "*"           "*"   
11  ( 1 ) "*"       "*" " "             " "             "*" " "       " "       "*" " "   " "   "*"           "*"   
12  ( 1 ) "*"       "*" " "             " "             "*" " "       " "       "*" " "   " "   "*"           "*"   
13  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" " "   " "   "*"           "*"   
14  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" " "   " "   "*"           "*"   
15  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" "*"   " "   "*"           "*"   
16  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" "*"   " "   "*"           "*"   
17  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" "*"   " "   "*"           "*"   
18  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" "*"   " "   "*"           "*"   
19  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" "*"   " "   "*"           "*"   
20  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" "*"   " "   "*"           "*"   
21  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" "*"   " "   "*"           "*"   
22  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       " "       "*" "*"   " "   "*"           "*"   
23  ( 1 ) "*"       "*" "*"             "*"             "*" " "       " "       "*" "*"   " "   "*"           "*"   
24  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       " "       "*" "*"   " "   "*"           "*"   
25  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       " "       "*" "*"   "*"   "*"           "*"   
26  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       " "       "*" "*"   "*"   "*"           "*"   
27  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       " "       "*" "*"   "*"   "*"           "*"   
28  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       "*"       "*" "*"   "*"   "*"           "*"   
29  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       "*"       "*" "*"   "*"   "*"           "*"   
30  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       "*"       "*" "*"   "*"   "*"           "*"   
31  ( 1 ) "*"       "*" "*"             "*"             "*" "*"       "*"       "*" "*"   "*"   "*"           "*"   
          Mfr_Guarantee BOVAG_Guarantee Guarantee_Period ABS Airbag_1 Airbag_2 Airco Automatic_airco Boardcomputer
1  ( 1 )  " "           " "             " "              " " " "      " "      " "   " "             " "          
2  ( 1 )  " "           " "             " "              " " " "      " "      " "   "*"             " "          
3  ( 1 )  " "           " "             " "              " " " "      " "      " "   " "             " "          
4  ( 1 )  " "           " "             " "              " " " "      " "      " "   "*"             " "          
5  ( 1 )  " "           " "             " "              " " " "      " "      " "   "*"             " "          
6  ( 1 )  " "           " "             " "              " " " "      " "      " "   "*"             " "          
7  ( 1 )  " "           " "             " "              " " " "      " "      " "   "*"             " "          
8  ( 1 )  " "           " "             "*"              " " " "      " "      " "   "*"             " "          
9  ( 1 )  " "           "*"             "*"              " " " "      " "      " "   "*"             " "          
10  ( 1 ) " "           "*"             "*"              " " " "      " "      " "   "*"             " "          
11  ( 1 ) "*"           "*"             "*"              " " " "      " "      " "   "*"             " "          
12  ( 1 ) "*"           "*"             "*"              " " " "      " "      "*"   "*"             " "          
13  ( 1 ) "*"           "*"             "*"              " " " "      " "      " "   "*"             " "          
14  ( 1 ) "*"           "*"             "*"              " " " "      " "      "*"   "*"             " "          
15  ( 1 ) "*"           "*"             "*"              " " " "      " "      "*"   "*"             " "          
16  ( 1 ) "*"           "*"             "*"              " " " "      " "      "*"   "*"             " "          
17  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             " "          
18  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             "*"          
19  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             "*"          
20  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             "*"          
21  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             "*"          
22  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             "*"          
23  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             "*"          
24  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             "*"          
25  ( 1 ) "*"           "*"             "*"              "*" " "      " "      "*"   "*"             "*"          
26  ( 1 ) "*"           "*"             "*"              "*" " "      "*"      "*"   "*"             "*"          
27  ( 1 ) "*"           "*"             "*"              "*" " "      "*"      "*"   "*"             "*"          
28  ( 1 ) "*"           "*"             "*"              "*" " "      "*"      "*"   "*"             "*"          
29  ( 1 ) "*"           "*"             "*"              "*" " "      "*"      "*"   "*"             "*"          
30  ( 1 ) "*"           "*"             "*"              "*" " "      "*"      "*"   "*"             "*"          
31  ( 1 ) "*"           "*"             "*"              "*" " "      "*"      "*"   "*"             "*"          
          CD_Player Central_Lock Powered_Windows Power_Steering Radio Mistlamps Sport_Model Backseat_Divider
1  ( 1 )  " "       " "          " "             " "            " "   " "       " "         " "             
2  ( 1 )  " "       " "          " "             " "            " "   " "       " "         " "             
3  ( 1 )  " "       " "          " "             " "            " "   " "       " "         " "             
4  ( 1 )  " "       " "          " "             " "            " "   " "       " "         " "             
5  ( 1 )  " "       " "          " "             " "            " "   " "       " "         " "             
6  ( 1 )  " "       " "          " "             " "            " "   " "       " "         " "             
7  ( 1 )  " "       " "          "*"             " "            " "   " "       " "         " "             
8  ( 1 )  " "       " "          "*"             " "            " "   " "       " "         " "             
9  ( 1 )  " "       " "          "*"             " "            " "   " "       " "         " "             
10  ( 1 ) " "       " "          "*"             " "            " "   " "       " "         " "             
11  ( 1 ) " "       " "          "*"             " "            " "   " "       " "         " "             
12  ( 1 ) " "       " "          "*"             " "            " "   " "       " "         " "             
13  ( 1 ) " "       " "          "*"             " "            " "   " "       " "         " "             
14  ( 1 ) " "       " "          "*"             " "            " "   " "       " "         " "             
15  ( 1 ) " "       " "          "*"             " "            " "   " "       " "         " "             
16  ( 1 ) "*"       " "          "*"             " "            " "   " "       " "         " "             
17  ( 1 ) "*"       " "          "*"             " "            " "   " "       " "         " "             
18  ( 1 ) "*"       " "          "*"             " "            " "   " "       " "         " "             
19  ( 1 ) "*"       " "          "*"             " "            " "   " "       " "         " "             
20  ( 1 ) "*"       " "          "*"             " "            " "   " "       " "         " "             
21  ( 1 ) "*"       " "          "*"             " "            " "   " "       " "         "*"             
22  ( 1 ) "*"       " "          "*"             " "            " "   " "       " "         "*"             
23  ( 1 ) "*"       " "          "*"             " "            "*"   " "       " "         "*"             
24  ( 1 ) "*"       " "          "*"             " "            "*"   " "       " "         "*"             
25  ( 1 ) "*"       " "          "*"             " "            "*"   " "       " "         "*"             
26  ( 1 ) "*"       " "          "*"             " "            "*"   " "       " "         "*"             
27  ( 1 ) "*"       " "          "*"             " "            "*"   " "       "*"         "*"             
28  ( 1 ) "*"       " "          "*"             " "            "*"   " "       "*"         "*"             
29  ( 1 ) "*"       "*"          "*"             " "            "*"   " "       "*"         "*"             
30  ( 1 ) "*"       "*"          "*"             " "            "*"   "*"       "*"         "*"             
31  ( 1 ) "*"       "*"          "*"             "*"            "*"   "*"       "*"         "*"             
          Metallic_Rim Radio_cassette Tow_Bar
1  ( 1 )  " "          " "            " "    
2  ( 1 )  " "          " "            " "    
3  ( 1 )  " "          " "            " "    
4  ( 1 )  " "          " "            " "    
5  ( 1 )  " "          " "            " "    
6  ( 1 )  " "          " "            " "    
7  ( 1 )  " "          " "            " "    
8  ( 1 )  " "          " "            " "    
9  ( 1 )  " "          " "            " "    
10  ( 1 ) " "          " "            " "    
11  ( 1 ) " "          " "            " "    
12  ( 1 ) " "          " "            " "    
13  ( 1 ) " "          " "            " "    
14  ( 1 ) " "          " "            " "    
15  ( 1 ) " "          " "            " "    
16  ( 1 ) " "          " "            " "    
17  ( 1 ) " "          " "            " "    
18  ( 1 ) " "          " "            " "    
19  ( 1 ) " "          " "            "*"    
20  ( 1 ) "*"          " "            "*"    
21  ( 1 ) "*"          " "            "*"    
22  ( 1 ) "*"          " "            "*"    
23  ( 1 ) "*"          "*"            "*"    
24  ( 1 ) "*"          "*"            "*"    
25  ( 1 ) "*"          "*"            "*"    
26  ( 1 ) "*"          "*"            "*"    
27  ( 1 ) "*"          "*"            "*"    
28  ( 1 ) "*"          "*"            "*"    
29  ( 1 ) "*"          "*"            "*"    
30  ( 1 ) "*"          "*"            "*"    
31  ( 1 ) "*"          "*"            "*"    
  • Best Subset Selection nos proporciona 31 modelos con sus respectivos predictores
reg.summary = summary(regfit.full)
names(reg.summary)

3.1 RSS

which.min(reg.summary$rss)
plot(reg.summary$rss, xlab="Numero de variables", ylab="RSS", type = "l")
points(which.min(reg.summary$rss), reg.summary$rss[which.min(reg.summary$rss)], col="red", cex=2, pch=20)
  • El modelo con RSS minimo es 31.

3.2 ADJR2


which.max(reg.summary$adjr2)
plot(reg.summary$adjr2, xlab="Numero de variables", ylab="Adjr2", type = "b")
points(which.max(reg.summary$adjr2), reg.summary$adjr2[which.max(reg.summary$adjr2)], col="red", cex=2, pch=20)
  • El modelo con ADJR2 maximo es 21.

3.3 CP

which.min(reg.summary$cp)

plot(reg.summary$cp, xlab="Numero de variables", ylab = "CP", type = "b")
points(which.min(reg.summary$cp), reg.summary$cp[which.min(reg.summary$cp)], col= "red", cex=2, pch=20)
  • El modelo con CP minimo es 21.

3.4 BIC

which.min(reg.summary$bic)

plot(reg.summary$bic, xlab="Numero de variables", ylab = "CP", type = "b")
points(which.min(reg.summary$bic), reg.summary$bic[which.min(reg.summary$bic)], col= "red", cex=2, pch=20)
  • El modelo con bic minimo es 15.

3.5 Conclusiones

reg.summary$adjr2[6]
reg.summary$adjr2[8]
reg.summary$adjr2[10]
reg.summary$adjr2[15]
reg.summary$adjr2[21]
  • A juzgar por los graficos pareciera que el numero adecuado de predictores es 15, sin embargo vemos que de un modelo de 8 preditores a uno de 10, 15 o 21 el R2 ajustado varia muy poco. Por lo tanto se opta por un modelo de 8 predictores.

3.5 Validacion Best Subset Selection

index <- createDataPartition(rawdata3$Price, p = 0.7, list = FALSE)
data.train <- rawdata3[index, ]
data.test <- rawdata3[-index, ]
  • Se divide el dataset en dos particiones, una de entrenamiento y otra de validacion.
set.seed(7)
dim(rawdata3)
regfit.train <- regsubsets(Price ~., data = data.train, nvmax = 31, really.big = TRUE)
#summary(regfit.train)
val.errors = rep(NA,31)
x.test <- model.matrix(Price ~., data = data.test)
for(i in 1:31) {
coeficientes <- coef(regfit.train, id = i)
predictors <- x.test[,names(coeficientes)] %*% coeficientes
val.errors[i] <- mean((data.test$Price - predictors)^2)
}
d<-which.min(val.errors)

rmse <- sqrt(val.errors)
value.min <- which.min(rmse)
value.min
rmsebss_min <- rmse[value.min]
rmsebss_min
  • El modelo con menor RMSE es el de 20 predictores con un rmse de 1018

plot(rmse, xlab = "Numero de Variables", ylab = "Root MSE Best Subset", pch = 20, type = "b")
points(value.min, rmse[value.min], col="red", cex=2, pch=20)
options(scipen = 999)
coef(regfit.train, which.min(rmse))
  • Coeficientes del modelo con menor RMSE
set.seed(7)
rmse[8]
rmse[10]
rmse[15]
rmse[20]

3.6 Validacion Cruzada Best Subset


predict.regsubsets  <- function(object, newdata, id, ...){
    # Extraer la fórmula del modelo (variable dependiente ~ predictores)
    form <- as.formula(object$call[[2]])
    # Generar una matriz modelo con los nuevos datos y la fórmula
    mat <- model.matrix(form, newdata)
    # Extraer los coeficientes del modelo
    coefi <- coef(object , id = id)
    # Almacenar el nombre de las variables predictoras del modelo
    xvars <- names(coefi)
    # Producto matricial entre los coeficientes del modelo y los valores de
    # los predictores de las nuevas observaciones para obtener las 
    # predicciones
    mat[ , xvars] %*% coefi
}
  • Funcion para predecir
set.seed(123)
folds <- sample(rep(1:10, length = nrow(rawdata3)))
table(folds)
cv.errors <- matrix(NA,10,31)
for(k in 1:10) {
  regfit.val <- regsubsets(Price~., data = rawdata3[folds !=k,], nvmax = 31,  really.big = TRUE)
   for(i in 1:31) {
    test <- rawdata3[folds == k,]
    predictions <- predict.regsubsets(object = regfit.val, newdata = rawdata3[folds == k,] , id = i)
    cv.errors[k,i] <- mean((test$Price - predictions)^2)
  }
}
rmse.cv <- sqrt(apply(cv.errors, 2, mean))
rmse.cv

set.seed(123)
value.min <- which.min(rmse.cv)
value.min
rmsebss.cv_min <- rmse.cv[value.min]
rmsebss.cv_min
  • El modelo con menor RMSE es el de 21 predictores con un rmse de 1064
set.seed(123)
plot(rmse.cv, xlab = "Numero de Variables", ylab = "Root MSE Cross Validation", pch = 20, type = "b")
value.min <- which.min(rmse.cv)
points(value.min, rmse.cv[value.min], col="red", cex=2, pch=20)
coef(regfit.val, which.min(rmse.cv))
  • Coeficientes del modelo con menor RMSE

4. Stepwise Selection: Forward Model

regfitfwd.full = regsubsets(Price ~., data = rawdata3, nvmax=31, method="forward")
summary(regfitfwd.full)
  • Stepwise Forward , 31 modelos
reg.summary = summary(regfitfwd.full)
names(reg.summary)

4.1 RSS

which.min(reg.summary$rss)
plot(reg.summary$rss, xlab="Numero de variables", ylab="RSS", type = "b")
points(which.min(reg.summary$rss), reg.summary$rss[which.min(reg.summary$rss)], col="red", cex=2, pch=20)
  • El modelo con menor rss posee 31 predictores

4.2 ADJR2

which.max(reg.summary$adjr2)
plot(reg.summary$adjr2, xlab="Numero de variables", ylab="ADJR2", type = "b")
points(which.max(reg.summary$adjr2), reg.summary$adjr2[which.max(reg.summary$adjr2)], col="red", cex=2, pch=20)
  • El modelo con mayor R2 ajustado posee 21 predictores

4.3 CP

which.min(reg.summary$cp)
plot(reg.summary$cp, xlab="Numero de variables", ylab="CP", type = "b")
points(which.min(reg.summary$cp), reg.summary$cp[which.min(reg.summary$cp)], col="red", cex=2, pch=20)
  • El modelo con menor CP posee 21 predictores

4.4 bic

which.min(reg.summary$bic)
plot(reg.summary$bic, xlab="Numero de variables", ylab="CP", type = "b")
points(which.min(reg.summary$bic), reg.summary$bic[which.min(reg.summary$bic)], col="red", cex=2, pch=20)
  • El modelo con menor bic posee 15 predictores

4.5 Conclusiones

reg.summary$adjr2[6]
reg.summary$adjr2[8]
reg.summary$adjr2[10]
reg.summary$adjr2[15]
reg.summary$adjr2[21]
  • A juzgar por los graficos pareciera que el numero adecuado de predictores es 15, el R2 ajustado varia muy poco al aumentar los predictores.

4.6 Validación Stepwise Selection Forward


index <- createDataPartition(rawdata3$Price, p = 0.7, list = FALSE)
data.train <- rawdata3[index, ]
data.test <- rawdata3[-index, ]
  • Particion del dataset en entrenamiento y validacion
set.seed(10)
dim(rawdata3)
model.fwd <- regsubsets(Price ~., data = data.train, nvmax = 31, method = "forward", really.big = TRUE)
summary(model.fwd)
val.errors = rep(NA,31)
x.test <- model.matrix(Price ~., data = data.test)
for(i in 1:31) {
coeficientes <- coef(model.fwd, id = i)
predictors <- x.test[,names(coeficientes)] %*% coeficientes
val.errors[i] <- mean((data.test$Price - predictors)^2)
}
rmse <- sqrt(val.errors)
set.seed(123)
value.min <- which.min(rmse)
value.min
rmsesfw_min <- rmse[value.min]
rmsesfw_min
  • La validacion arroja que el modelo con menor RMSE es el de 20 variables con un RMSE de 1035
set.seed(123)
rmse[15]
set.seed(123)
rmse[10]
  • La diferencia de rmse del modelo con 15 predictores y el modelo con 20 predictores es minima. Se puede optar por el modelo de 15 predictores.
set.seed(123)
plot(rmse, xlab = "Numero de Variables", ylab = "Root MSE Best Subset", pch = 20, type = "b")
points(value.min, rmse[value.min], col="red", cex=2, pch=20)
coef(model.fwd, which.min(rmse))

4.7 Validacion cruzada Stepwise Forward

set.seed(14)
folds <- sample(rep(1:10, length = nrow(rawdata3)))
table(folds)
cv.errors <- matrix(NA,10,31)
for(k in 1:10) {
  model.fwd <- regsubsets(Price~., data = rawdata3[folds !=k,], nvmax = 31, method = "forward", really.big = TRUE)
   for(i in 1:31) {
    test <- rawdata3[folds == k,]
    predictions <- predict.regsubsets(object = model.fwd, newdata = rawdata3[folds == k,] , id = i)
    cv.errors[k,i] <- mean((test$Price - predictions)^2)
  }
}
rmse.cv <- sqrt(apply(cv.errors, 2, mean))
set.seed(14)
value.min <- which.min(rmse.cv)
value.min
rmsesfw.cv_min <- rmse.cv[value.min]
rmsesfw.cv_min
  • El modelo con rmse minimo es el de 22 preditores con un valor de 1057
set.seed(14)
plot(rmse.cv, xlab = "Numero de Variables", ylab = "Root MSE Cross Validation", pch = 20, type = "b")
value.min <- which.min(rmse.cv)
points(value.min, rmse.cv[value.min], col="red", cex=2, pch=20)
coef(model.fwd, which.min(rmse.cv))
  • coeficientes del modelo con 22 predictores.

5. Stepwise Selection: Backward Model

regfitbwd.full = regsubsets(Price ~., data = rawdata3, nvmax=31, method="backward")
summary(regfit.full)
  • Stepwise backward , 31 modelos
reg.summary = summary(regfitbwd.full)
names(reg.summary)

5.1 RSS

which.min(reg.summary$rss)
plot(reg.summary$rss, xlab="Numero de variables", ylab="RSS", type = "b")
points(which.min(reg.summary$rss), reg.summary$rss[which.min(reg.summary$rss)], col="red", cex=2, pch=20)
  • El modelo con menor rss posee 31 predictores

5.2 ADJR2

which.max(reg.summary$adjr2)
plot(reg.summary$adjr2, xlab="Numero de variables", ylab="ADJR2", type = "b")
points(which.max(reg.summary$adjr2), reg.summary$adjr2[which.max(reg.summary$adjr2)], col="red", cex=2, pch=20)
  • El modelo con mayor R2 ajustado posee 21 predictores

5.3 CP

which.min(reg.summary$cp)
plot(reg.summary$cp, xlab="Numero de variables", ylab="CP", type = "b")
points(which.min(reg.summary$cp), reg.summary$cp[which.min(reg.summary$cp)], col="red", cex=2, pch=20)
  • El modelo con menor CP posee 21 predictores

5.4 bic

which.min(reg.summary$bic)
plot(reg.summary$bic, xlab="Numero de variables", ylab="CP", type = "b")
points(which.min(reg.summary$bic), reg.summary$bic[which.min(reg.summary$bic)], col="red", cex=2, pch=20)
  • El modelo con menor bic posee 15 predictores

5.5 Conclusiones

reg.summary$adjr2[6]
reg.summary$adjr2[8]
reg.summary$adjr2[10]
reg.summary$adjr2[15]
reg.summary$adjr2[21]
  • A juzgar por los graficos pareciera que el numero adecuado de predictores es 15, el R2 ajustado varia muy poco al aumentar los predictores.

5.6 Validación Stepwise Selection Backward


index <- createDataPartition(rawdata3$Price, p = 0.7, list = FALSE)
data.train <- rawdata3[index, ]
data.test <- rawdata3[-index, ]
  • Particion del dataset en entrenamiento y validacion
set.seed(191)
dim(rawdata3)
model.bwd <- regsubsets(Price ~., data = data.train, nvmax = 31, method = "backward", really.big = TRUE)
summary(model.bwd)
val.errors = rep(NA,31)
x.test <- model.matrix(Price ~., data = data.test)
for(i in 1:31) {
coeficientes <- coef(model.bwd, id = i)
predictors <- x.test[,names(coeficientes)] %*% coeficientes
val.errors[i] <- mean((data.test$Price - predictors)^2)
}
rmse <- sqrt(val.errors)
set.seed(191)
value.min <- which.min(rmse)
value.min
rmsesbw_min <- rmse[value.min]
rmsesbw_min
  • La validacion arroja que el modelo con menor RMSE es el de 16 variables con un RMSE de 1042
set.seed(191)
rmse[20]
set.seed(191)
rmse[15]
set.seed(191)
rmse[10]
  • La diferencia de rmse del modelo con 16 predictores y el modelo con 20 predictores es minima.
set.seed(191)
plot(rmse, xlab = "Numero de Variables", ylab = "Root MSE", pch = 20, type = "b")
value.min <- which.min(rmse)
points(value.min, rmse[value.min], col="red", cex=2, pch=20)
coef(model.fwd, which.min(rmse))

5.7 Validacion cruzada Stepwise Backward

set.seed(141)
folds <- sample(rep(1:10, length = nrow(rawdata3)))
table(folds)
cv.errors <- matrix(NA,10,31)
for(k in 1:10) {
  model.bwd <- regsubsets(Price~., data = rawdata3[folds !=k,], nvmax = 31, method = "backward", really.big = TRUE)
   for(i in 1:31) {
    test <- rawdata3[folds == k,]
    predictions <- predict.regsubsets(object = model.bwd, newdata = rawdata3[folds == k,] , id = i)
    cv.errors[k,i] <- mean((test$Price - predictions)^2)
  }
}
rmse.cv <- sqrt(apply(cv.errors, 2, mean))
set.seed(141)
value.min <- which.min(rmse.cv)
value.min
rmsesbw.cv_min <- rmse.cv[value.min]
rmsesbw.cv_min
  • El modelo con rmse minimo es el de 20 preditores con un valor de 1059
set.seed(141)
plot(rmse.cv, xlab = "Numero de Variables", ylab = "Root MSE Cross Validation", pch = 20, type = "b")
points(value.min, rmse.cv[value.min], col="red", cex=2, pch=20)
coef(model.bwd, which.min(rmse.cv))
  • coeficientes del modelo con 20 predictores.

6. Ridge

6.1 Estandarizacion de valores


x <- model.matrix(Price ~. , rawdata3)[,-1]
y <- rawdata3$Price
grid <- 10^seq(10,-2,length = 100)
ridge.model <- glmnet(x, y, alpha = 0, lambda = grid)
dim(coef(ridge.model))
coef(ridge.model)
plot(ridge.model, xvar ='lambda',label = TRUE)
  • A medida que lambda crece los coeficientes tienden a cero.
options(scipen=999)
predict(ridge.model, s = 10, type = "coefficients")

6.2 Validacion Cruzada

set.seed(23)
indices <- sample(c(TRUE,FALSE), nrow(rawdata3), replace = TRUE)
y.test <- y[-indices]

ridge.model.cv <- cv.glmnet(x[indices,], y[indices], alpha = 0) # Por defecto, 10-fold
coef(ridge.model.cv)
plot(ridge.model.cv)
  • Las lineas horizontales representan, la primera, el minimo de la validacion cruzada, y la segunda, el error estandar.
set.seed(23)
bestlam <- ridge.model.cv$lambda.min
bestlam
  • Calculamos el menor lambda para aplicarlo a la validacion cruzada
  • Menor lambda 285.38

6.3

set.seed(23)
ridge.pred <- predict(ridge.model, s = bestlam, newx = x[-indices,])
mse <-mean((ridge.pred - y[-indices])^2)
rmse <-sqrt(mse)
value.min <- which.min(rmse)
value.min
rmseridge <- rmse[value.min]
rmseridge
  • El modelo obtenido con Ridge Regression utilizando el lambda minimo nos da un RMSE de 1056.6
out <- glmnet(x, y, alpha = 0)
predict(out, type = "coefficients", s = bestlam)
  • Ninguno de los coeficientes es exactamente igual a cero, ya que Ridge no realiza seleccion de variables.
plot(out, xvar ='lambda',label = TRUE)
  • Los coeficientes se ajustan mas a cero

7. Lasso

lasso.model <- glmnet(x, y, alpha = 1)
plot(lasso.model, xvar ='lambda',label = TRUE)

7.1 Validacion Cruzada

indices <- sample(c(TRUE,FALSE), nrow(rawdata3), replace = TRUE)
cv.out <- cv.glmnet(x[indices,], y[indices], alpha = 1)
plot(cv.out)
  • el valor minimo de la validacion cruzada, es de tamaño 32, mientras que el error estandar es de tamaño 15.
coef(cv.out)
  • La validacion cruzada no toma en cuenta 17 variables, el modelo queda con 15 variables.
bestlam <- cv.out$lambda.min
bestlam
  • Lambda minimo es 0.79

7.2 Validacion

lasso.pred <- predict(lasso.model, s = bestlam, newx = x[-indices,])
mse <-mean((lasso.pred - y[-indices])^2)
rmse <-sqrt(mse)
value.min <- which.min(rmse)
rmselasso <- rmse[value.min]
rmselasso

-El valor de RMSE es de 1025

options(scipen=999)
out <- glmnet(x, y, alpha = 1)
lasso.coef <- predict(out, type = "coefficients", s = bestlam)
lasso.coef
plot(out, xvar='lambda', label= TRUE)
  • Se reemplaza el valor arbitrario con el mejor lambda obtenido

8. PCA


rawdata4 <- rawdata2
rawdata4 <- dummy_cols(rawdata4, select_columns = "Fuel_Type", remove_first_dummy = TRUE)
rawdata4 <- select(rawdata4, - Fuel_Type, - Mfg_Year, - Mfg_Month, - Cylinders)
apply(X = rawdata4, MARGIN = 2, FUN = mean)
  • La media de las variables, vemos que existe mucha diferencia entre algunas medias como la de Price o KM. Es necesario una estandarizacion. LA media debe ser cercana a cero.
apply(X = rawdata4, MARGIN = 2, FUN = var)
  • La varianza es la desviacion estandar al cuadrado, analizo la varianza ya que la desviacion estandar puede valores negativos. Se puede observar que algunas variables poseen varianzas dispares. La varianza debe ser cercana a uno.

8.1 Modelo PCA

pca.model <- prcomp(rawdata4[,-1], scale = TRUE)
names(pca.model) 
pca.model$center
  • Representa la media de las variables previa a la estandarizacion
pca.model$scale
  • Representa la desviacion de las variables previa a la estandarizacion
pca.model$rotation
  • Cada variable de las componentes tienen asignado un peso, las de mayor peso son las que explican dicha componente.
dim(pca.model$x)
  • Dimension del dataset, 1436 observaciones y 32 componentes.
biplot(x = pca.model, scale = 0, cex = 0.6, col = c("blue", "brown3"))
  • Grafica Primera y segunda componente

8.2 Varianza explicada de las componentes

pca.model$sdev^2
prop.variance <- pca.model$sdev^2/sum(pca.model$sdev^2)
prop.variance
  • Proporcion respecto al total
prop.cumulative <- cumsum(prop.variance)
prop.cumulative
  • Proporcion de la varianza acumulada
plot(prop.cumulative, type = "b", pch = 20, ylab = "Proporcion de la Varianza Acumulada", 
xlab = "Componente Principal" )
points(prop.cumulative, col = "red")
  • La 1era componente explica el 15%, la 2da el 27% ,etc. Se necesitan 15 componentes principales para explicar el 81%
  • Para explicar el 98% se necesitan 26 variables.
  • Con las primeras 21 componentes principales se pueden exlicar 92% de la varianza acumulada

#9. PCR ##############

9.2 Particion del dataset

set.seed(789)
indices <- createDataPartition(rawdata4$Price, p = 0.8, list = FALSE)
set.seed(789)
pcr.model <- pcr(formula = Price ~ ., data = rawdata4[indices, ], scale = TRUE, ncomp=21)
summary(pcr.model)
  • Se reraliza el ajuste del modelo, se utilizan las primeras 21 componentes principales

9.3 Validacion

pcr.model.pred <- predict(pcr.model, newdata = rawdata4[-indices,], ncomp = 21)
mse.test <- mean((pcr.model.pred - rawdata4[-indices,]$Price)^2)
mse.test
rmsepcr <- sqrt(mse.test)
rmsepcr
  • El RMSE es de 1332, se utilizan los datos de test.

9.4 Validacion cruzada

set.seed(678)
pcr.model <- pcr(formula = Price ~., data = rawdata4[indices, ], scale = TRUE, validation = "CV")
summary(pcr.model)
pcr.model.cv <- MSEP(pcr.model, estimate = "CV")
m <- which.min(pcr.model.cv$val)
m
rmsepcr.cv <- sqrt(pcr.model.cv$val[m])
rmsepcr.cv
  • LA validacion cruzada da como resultado un modelo con 31 componentes y un RMSE de 1227
plot(pcr.model.cv$val, main = "MSE vs nro componentes", type = "l",
ylab = "MSE", col = "blue", xlab = "Componentes")
  • Observando el grafico, el menor MSE se da con 31 componentes, que es el numero de predictores, concluimos que no pude reducir la dimensionalidad del dataset original.
pcr.model.pred <- predict(pcr.model, newdata = rawdata4[-indices,])
mse.test <- mean((pcr.model.pred - rawdata4[-indices,]$Price)^2)
mse.test
rmsepcr.cv <- sqrt(mse.test)
rmsepcr.cv

-Evaluacion del modelo PCR contra los datos de prueba. Calculo del MSE y RMSE correspondiente. - El modelo no es bueno dada la igualdad componentes-predictores pero los datos sirven de referencia para otros metodos.

pcr.model.pred <- predict(pcr.model, newdata = rawdata4[-indices,], ncomp = 26)
mse.test <- mean((pcr.model.pred - rawdata4[-indices,]$Price)^2)
mse.test
rmsepcr.cv <- sqrt(mse.test)
rmsepcr.cv
  • Con 26 componentes el Rmse es de 1324.

10 PLS

set.seed(332)
indices <- createDataPartition(rawdata4$Price, p = 0.8, list = FALSE)
set.seed(332)
pls.model <- plsr(formula = Price ~., data = rawdata4[indices, ], scale = TRUE, validation = "CV")
pls.model.cv <- MSEP(pls.model, estimate = "CV")
which.min(pls.model.cv$val)
plot(pls.model.cv$val, main = "MSE vs N Componentes", type = "l", ylab = "MSE", col = "blue", xlab = "Componentes")
pls.model.pred <- predict(pls.model, newdata = rawdata4[-indices,], ncomp = 5)
mse.test <- mean((pls.model.pred - rawdata4[-indices,]$Price)^2)
mse.test
rmsepcr.cv <- sqrt(mse.test)
rmsepcr.cv
  • El RMSE para 5 componentes como sugiere pls, es de 1282.

10.1 Analisis con 21 predictores.

10.1.1 Creacion de modelo

set.seed(333)
pls.model <- plsr(formula = Price ~ ., data = rawdata4[indices, ], scale = TRUE, ncomp=21)
summary(pls.model)
  • Se utilizo 21 predictores, que explican el 83%, con PCR explicaba un 92%

10.1.2 Validacion

pls.model.pred <- predict(pls.model, newdata = rawdata4[-indices,], ncomp = 21)
mse.test <- mean((pls.model.pred - rawdata4[-indices,]$Price)^2)
rmsepcr <- sqrt(mse.test)
rmsepcr
which.min(pls.model.cv$val)
  • RMSE 1196 para 21 predictores.

10.1.3 Validacion Cruzada

set.seed(333)
pls.model <- plsr(formula = Price ~., data = rawdata4[indices, ], scale = TRUE, validation = "CV")
summary(pls.model)
pls.model.cv <- MSEP(pls.model, estimate = "CV")
m <- which.min(pls.model.cv$val)
m
rmseplst.cv <- sqrt(mse.test)
rmseplst.cv

11. Conclusiones

metodo <- c("BSS","FW", "BW", "Ridge", "Lasso", "PCR", "PLS")
test_RMSE <- c(rmsebss.cv_min, rmsesfw.cv_min, rmsesbw.cv_min, rmseridge,
              rmselasso, rmsepcr.cv, rmseplst.cv)
resultados <- data.frame(metodo, test_RMSE)
resultados
ggplot(data = resultados, aes(x = reorder(metodo, test_RMSE),
                              y = test_RMSE)) +
  geom_col(width = 0.5) +
  lims(y = c(0, 1600)) +
  geom_text(aes(label = round(test_RMSE, 2)), vjust = -1) +
  theme_gray() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
  • El mejor modelo es el resultado del metodo Lasso, ya que posee el menor RMSE.
  • Se oberva una gran diferencia entre PLS, PCR, BW(Los de mas alto RMSE) y los demás metodos.

12. Modelo con Lasso

  • Las variables seleccionadas por Lasso son:

  • Age_08_04
  • KM
  • HP
  • cc
  • Gears
  • Weight
  • Mfr_Guarantee
  • BOVAG_Guarantee
  • Guarantee_Period
  • Airco
  • Automatic_airco
  • CD_Player
  • Central_Lock
  • Powered_Windows
  • Metallic_Rim

12.1 Limpieza

datalasso <- rawdata3
datalasso <- select(datalasso, Price, Age_08_04, KM, HP, cc, Gears, Weight, Mfr_Guarantee, BOVAG_Guarantee, Guarantee_Period, Airco, Automatic_airco, CD_Player, Central_Lock, Powered_Windows, Metallic_Rim)
dim(datalasso)

12.2 Regresion Lineal

lassoLM <- (lm(formula = Price ~. , data = datalasso))
summary(lassoLM)
  • La variable Central_Lock tiene un t_value que no es significativamente distinto a cero, es probable que no agregue nada al modelo.

  • R cuadrado es de 0.89

hist(lassoLM$residuals) # histograma de los residuos estandarizados 
#qqnorm(lassoLM$residuals) # gráfico de cuantiles de los residuos estandarizados 
#qqline(lassoLM$residuals)
  • Se puede observar simetria en el histograma de residuos.
par(mfrow=c(2,2)) # divide la ventana en una fila y tres columnas 
plot(lassoLM)
  • Residuals vs Fitted: El grafico muestra una distribucion de naturaleza aleatoria. El promedio de la grafica se pierde en cero.

  • QQ: Ciertas muestras no se encuentran sobre la linea, es indicativo de que se puede mejorar el modelo

  • Scale-Location: Muestra la varianza de los residuos

  • Residuals vs Leverage: No se observa ningun punto fiera de la linea punteada, es decir no se observan puntos influyentes.

LS0tDQp0aXRsZTogIlRveW90YSBDb3JvbGxhIC0gUENBLCBQQ1IiDQphdXRob3I6ICJCcnVndWVyYSBBZ3VzdGluIg0Kb3V0cHV0Og0KICAgIGh0bWxfbm90ZWJvb2s6DQogICAgICBkZl9wcmludDogcGFnZWQNCiAgICAgIGZpZzpoZWlnaHQ6IDQNCiAgICAgIGZpZzp3aWR0aDogNg0KICAgICAgdG9jOiB5ZXMNCiAgICAgIHRvY19mbG9hdDogeWVzDQogICAgaHRtbF9kb2N1bWVudDoNCiAgICAgIGRmX3ByaW50OiBwYWdlZA0KICAgICAgdG9jOiB5ZXMNCi0tLQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMNCiMgMS4gQ2FyZ2EgZGUgbGlicmVyaWFzIHkgZGF0b3MuDQojIyMjIyMjIyMjIyMjIyMjIyMgDQoNCg0KIyMgMS4xIENhcmdhIGRlIGxpYnJlcmlhcw0KYGBge3IgbGlicmVyaWFzfQ0KbGlicmFyeShsZWFwcykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShmYXN0RHVtbWllcykNCmxpYnJhcnkoZ2xtbmV0KQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkocGxzKQ0KDQoNCmBgYA0KDQojIyAxLjIgQ2FyZ2EgZGUgZGF0b3MNCmBgYHtyIGRhdG9zfQ0KDQpyYXdkYXRhIDwtIHJlYWRfY3N2KCJUb3lvdGFDb3JvbGxhLmNzdiIpDQoNCmBgYA0KDQojIyAxLjMgU3VtYXJpbyBkZSBkYXRvcw0KYGBge3Igc3VtYXJpbyBkZSBkYXRvc30NCg0Kc3VtbWFyeShyYXdkYXRhKQ0KDQpgYGANCg0KIyMgMS40IEVzdHJ1Y3R1cmEgZGUgZGF0b3MNCmBgYHtyIGVzdHJ1Y3R1cmEgZGUgZGF0b3N9DQpzdHIocmF3ZGF0YSkNCmBgYA0KDQojIyAxLjUgQnVzY2FuZG8gZGF0b3MgZmFsdGFudGVzDQpgYGB7ciBkYXRvcyBmYWx0YW50ZXN9DQoNCnN1bShpcy5uYShyYXdkYXRhKSkNCmBgYA0KDQojIyAxLjYgcXVpdGFuZG8gbGFzIHZhcmlhYmxlcyBpZCB5IG1vZGVsDQpgYGB7cn0NCg0KcmF3ZGF0YTIgPC0gcmF3ZGF0YQ0KcmF3ZGF0YTIkSWQgPC0gTlVMTA0KcmF3ZGF0YTIkTW9kZWwgPC0gTlVMTA0KYGBgDQoNCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyAyLiBMaW1waWV6YSBkZSBkYXRhc2V0DQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQoNCiMjIDIuMSBUcmFuc2Zvcm1hciBhIGZhY3RvcmVzDQpgYGB7cn0NCg0KcmF3ZGF0YTIkRnVlbF9UeXBlIDwtIGFzLmZhY3RvcihyYXdkYXRhMiRGdWVsX1R5cGUpDQoNCiNyYXdkYXRhMiRDeWxpbmRlcnMgPC0gYXMuZmFjdG9yKHJhd2RhdGEyJEN5bGluZGVycykNCiMjKHJhd2RhdGEsIGFlcyh4ID0gUHJpY2UsIHkgPSBXZWlnaHQsIGNvbG91ciA9IEZ1ZWxfVHlwZSkpKyBnZW9tX3BvaW50KCkNCg0KDQoNCmBgYA0KDQojIyAyLjIgRmlsdHJhZG8NCmBgYHtyfQ0KDQpyYXdkYXRhMyA8LSByYXdkYXRhMg0KDQpyYXdkYXRhMyRjYyA8LSBpZmVsc2UocmF3ZGF0YTMkY2MgPT0gMTYwMDAsIDE2MDAsIHJhd2RhdGEzJGNjKSANCnJhd2RhdGEzIDwtIGZpbHRlcihyYXdkYXRhMywgcmF3ZGF0YTMkV2VpZ2h0IDwgMTIwMCkNCnJhd2RhdGEzIDwtIGZpbHRlcihyYXdkYXRhMywgcmF3ZGF0YTMkS00gPiAxMDAwKQ0KcmF3ZGF0YTMgPC0gZmlsdGVyKHJhd2RhdGEzLCByYXdkYXRhMyRLTSA8IDE1MDAwMCkNCg0KDQpgYGANCg0KIyMgMi4zIEVsaW1pbmFuZG8gdmFyaWFibGVzDQpgYGB7cn0NCg0KcmF3ZGF0YTMkTWZnX01vbnRoIDwtIE5VTEwNCnJhd2RhdGEzJE1mZ19ZZWFyIDwtIE5VTEwNCnJhd2RhdGEzJEN5bGluZGVycyA8LU5VTEwNCg0KYGBgDQoNCiMjIDIuNCBFc3RydWN0dXJhIGRlIG51ZXZvIGRhdGFzZXQNCmBgYHtyfQ0KDQpzdHIocmF3ZGF0YTMpDQoNCmBgYA0KDQotIEx1ZWdvIGRlIHJlYWxpemFyIGxhIGxpbXBpZXphIGVsIGRhdHNldCBjb250aWVuZSAxMzQwIG9ic2VydmFjaW9uZXMgeSAzMiB2YXJpYWJsZXMNCg0KIyMjIyMjIyMjIyMjIyMjIyMNCiMgMy4gQmVzdCBTdWJzZXQgU2VsZWN0aW9uDQojIyMjIyMjIyMjIyMjIyMjIw0KDQpgYGB7cn0NCnJlZ2ZpdC5mdWxsID0gcmVnc3Vic2V0cyhQcmljZSB+LiwgZGF0YSA9IHJhd2RhdGEzLCBudm1heD0zMSkNCnN1bW1hcnkocmVnZml0LmZ1bGwpDQpgYGANCg0KLSBCZXN0IFN1YnNldCBTZWxlY3Rpb24gbm9zIHByb3BvcmNpb25hIDMxIG1vZGVsb3MgY29uIHN1cyByZXNwZWN0aXZvcyBwcmVkaWN0b3Jlcw0KDQpgYGB7cn0NCnJlZy5zdW1tYXJ5ID0gc3VtbWFyeShyZWdmaXQuZnVsbCkNCm5hbWVzKHJlZy5zdW1tYXJ5KQ0KDQpgYGANCg0KIyMgMy4xIFJTUw0KYGBge3J9DQp3aGljaC5taW4ocmVnLnN1bW1hcnkkcnNzKQ0KcGxvdChyZWcuc3VtbWFyeSRyc3MsIHhsYWI9Ik51bWVybyBkZSB2YXJpYWJsZXMiLCB5bGFiPSJSU1MiLCB0eXBlID0gImwiKQ0KcG9pbnRzKHdoaWNoLm1pbihyZWcuc3VtbWFyeSRyc3MpLCByZWcuc3VtbWFyeSRyc3Nbd2hpY2gubWluKHJlZy5zdW1tYXJ5JHJzcyldLCBjb2w9InJlZCIsIGNleD0yLCBwY2g9MjApDQpgYGANCg0KLSBFbCBtb2RlbG8gY29uIFJTUyBtaW5pbW8gZXMgMzEuDQoNCg0KIyMgMy4yIEFESlIyDQpgYGB7cn0NCg0Kd2hpY2gubWF4KHJlZy5zdW1tYXJ5JGFkanIyKQ0KcGxvdChyZWcuc3VtbWFyeSRhZGpyMiwgeGxhYj0iTnVtZXJvIGRlIHZhcmlhYmxlcyIsIHlsYWI9IkFkanIyIiwgdHlwZSA9ICJiIikNCnBvaW50cyh3aGljaC5tYXgocmVnLnN1bW1hcnkkYWRqcjIpLCByZWcuc3VtbWFyeSRhZGpyMlt3aGljaC5tYXgocmVnLnN1bW1hcnkkYWRqcjIpXSwgY29sPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KYGBgDQoNCi0gRWwgbW9kZWxvIGNvbiBBREpSMiBtYXhpbW8gZXMgMjEuDQoNCg0KIyMgMy4zIENQDQpgYGB7cn0NCndoaWNoLm1pbihyZWcuc3VtbWFyeSRjcCkNCg0KcGxvdChyZWcuc3VtbWFyeSRjcCwgeGxhYj0iTnVtZXJvIGRlIHZhcmlhYmxlcyIsIHlsYWIgPSAiQ1AiLCB0eXBlID0gImIiKQ0KcG9pbnRzKHdoaWNoLm1pbihyZWcuc3VtbWFyeSRjcCksIHJlZy5zdW1tYXJ5JGNwW3doaWNoLm1pbihyZWcuc3VtbWFyeSRjcCldLCBjb2w9ICJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KDQpgYGANCg0KLSBFbCBtb2RlbG8gY29uIENQIG1pbmltbyBlcyAyMS4NCg0KIyMgMy40IEJJQw0KYGBge3J9DQp3aGljaC5taW4ocmVnLnN1bW1hcnkkYmljKQ0KDQpwbG90KHJlZy5zdW1tYXJ5JGJpYywgeGxhYj0iTnVtZXJvIGRlIHZhcmlhYmxlcyIsIHlsYWIgPSAiQ1AiLCB0eXBlID0gImIiKQ0KcG9pbnRzKHdoaWNoLm1pbihyZWcuc3VtbWFyeSRiaWMpLCByZWcuc3VtbWFyeSRiaWNbd2hpY2gubWluKHJlZy5zdW1tYXJ5JGJpYyldLCBjb2w9ICJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KDQpgYGANCg0KLSBFbCBtb2RlbG8gY29uIGJpYyBtaW5pbW8gZXMgMTUuDQoNCiMjIDMuNSBDb25jbHVzaW9uZXMNCmBgYHtyfQ0KcmVnLnN1bW1hcnkkYWRqcjJbNl0NCmBgYA0KYGBge3J9DQpyZWcuc3VtbWFyeSRhZGpyMls4XQ0KYGBgDQpgYGB7cn0NCnJlZy5zdW1tYXJ5JGFkanIyWzEwXQ0KYGBgDQpgYGB7cn0NCnJlZy5zdW1tYXJ5JGFkanIyWzE1XQ0KYGBgDQpgYGB7cn0NCnJlZy5zdW1tYXJ5JGFkanIyWzIxXQ0KYGBgDQoNCi0gQSBqdXpnYXIgcG9yIGxvcyBncmFmaWNvcyBwYXJlY2llcmEgcXVlIGVsIG51bWVybyBhZGVjdWFkbyBkZSBwcmVkaWN0b3JlcyBlcyAxNSwgc2luIGVtYmFyZ28gdmVtb3MgcXVlIGRlIHVuIG1vZGVsbyBkZSA4IHByZWRpdG9yZXMgYSB1bm8gZGUgMTAsIDE1IG8gMjEgZWwgUjIgYWp1c3RhZG8gdmFyaWEgbXV5IHBvY28uIFBvciBsbyB0YW50byBzZSBvcHRhIHBvciB1biBtb2RlbG8gZGUgOCBwcmVkaWN0b3Jlcy4NCg0KDQojIyAzLjUgVmFsaWRhY2lvbiBCZXN0IFN1YnNldCBTZWxlY3Rpb24NCg0KYGBge3J9DQppbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHJhd2RhdGEzJFByaWNlLCBwID0gMC43LCBsaXN0ID0gRkFMU0UpDQpkYXRhLnRyYWluIDwtIHJhd2RhdGEzW2luZGV4LCBdDQpkYXRhLnRlc3QgPC0gcmF3ZGF0YTNbLWluZGV4LCBdDQpgYGANCg0KLSBTZSBkaXZpZGUgZWwgZGF0YXNldCBlbiBkb3MgcGFydGljaW9uZXMsIHVuYSBkZSBlbnRyZW5hbWllbnRvIHkgb3RyYSBkZSB2YWxpZGFjaW9uLg0KDQpgYGB7cn0NCnNldC5zZWVkKDcpDQpkaW0ocmF3ZGF0YTMpDQpgYGANCg0KYGBge3J9DQpyZWdmaXQudHJhaW4gPC0gcmVnc3Vic2V0cyhQcmljZSB+LiwgZGF0YSA9IGRhdGEudHJhaW4sIG52bWF4ID0gMzEsIHJlYWxseS5iaWcgPSBUUlVFKQ0KI3N1bW1hcnkocmVnZml0LnRyYWluKQ0KdmFsLmVycm9ycyA9IHJlcChOQSwzMSkNCngudGVzdCA8LSBtb2RlbC5tYXRyaXgoUHJpY2Ugfi4sIGRhdGEgPSBkYXRhLnRlc3QpDQpmb3IoaSBpbiAxOjMxKSB7DQpjb2VmaWNpZW50ZXMgPC0gY29lZihyZWdmaXQudHJhaW4sIGlkID0gaSkNCnByZWRpY3RvcnMgPC0geC50ZXN0WyxuYW1lcyhjb2VmaWNpZW50ZXMpXSAlKiUgY29lZmljaWVudGVzDQp2YWwuZXJyb3JzW2ldIDwtIG1lYW4oKGRhdGEudGVzdCRQcmljZSAtIHByZWRpY3RvcnMpXjIpDQp9DQpkPC13aGljaC5taW4odmFsLmVycm9ycykNCg0Kcm1zZSA8LSBzcXJ0KHZhbC5lcnJvcnMpDQoNCmBgYA0KDQpgYGB7cn0NCnZhbHVlLm1pbiA8LSB3aGljaC5taW4ocm1zZSkNCnZhbHVlLm1pbg0KYGBgDQoNCmBgYHtyfQ0Kcm1zZWJzc19taW4gPC0gcm1zZVt2YWx1ZS5taW5dDQpybXNlYnNzX21pbg0KYGBgDQoNCi0gRWwgbW9kZWxvIGNvbiBtZW5vciBSTVNFIGVzIGVsIGRlIDIwIHByZWRpY3RvcmVzIGNvbiB1biBybXNlIGRlIDEwMTgNCg0KYGBge3J9DQoNCnBsb3Qocm1zZSwgeGxhYiA9ICJOdW1lcm8gZGUgVmFyaWFibGVzIiwgeWxhYiA9ICJSb290IE1TRSBCZXN0IFN1YnNldCIsIHBjaCA9IDIwLCB0eXBlID0gImIiKQ0KcG9pbnRzKHZhbHVlLm1pbiwgcm1zZVt2YWx1ZS5taW5dLCBjb2w9InJlZCIsIGNleD0yLCBwY2g9MjApDQpgYGANCg0KYGBge3J9DQpvcHRpb25zKHNjaXBlbiA9IDk5OSkNCmNvZWYocmVnZml0LnRyYWluLCB3aGljaC5taW4ocm1zZSkpDQpgYGANCi0gQ29lZmljaWVudGVzIGRlbCBtb2RlbG8gY29uIG1lbm9yIFJNU0UNCg0KYGBge3J9DQpzZXQuc2VlZCg3KQ0Kcm1zZVs4XQ0KYGBgDQoNCmBgYHtyfQ0Kcm1zZVsxMF0NCmBgYA0KYGBge3J9DQpybXNlWzE1XQ0KYGBgDQpgYGB7cn0NCnJtc2VbMjBdDQpgYGANCg0KDQojIyAzLjYgVmFsaWRhY2lvbiBDcnV6YWRhIEJlc3QgU3Vic2V0DQoNCmBgYHtyfQ0KDQpwcmVkaWN0LnJlZ3N1YnNldHMgIDwtIGZ1bmN0aW9uKG9iamVjdCwgbmV3ZGF0YSwgaWQsIC4uLil7DQogICAgIyBFeHRyYWVyIGxhIGbDs3JtdWxhIGRlbCBtb2RlbG8gKHZhcmlhYmxlIGRlcGVuZGllbnRlIH4gcHJlZGljdG9yZXMpDQogICAgZm9ybSA8LSBhcy5mb3JtdWxhKG9iamVjdCRjYWxsW1syXV0pDQogICAgIyBHZW5lcmFyIHVuYSBtYXRyaXogbW9kZWxvIGNvbiBsb3MgbnVldm9zIGRhdG9zIHkgbGEgZsOzcm11bGENCiAgICBtYXQgPC0gbW9kZWwubWF0cml4KGZvcm0sIG5ld2RhdGEpDQogICAgIyBFeHRyYWVyIGxvcyBjb2VmaWNpZW50ZXMgZGVsIG1vZGVsbw0KICAgIGNvZWZpIDwtIGNvZWYob2JqZWN0ICwgaWQgPSBpZCkNCiAgICAjIEFsbWFjZW5hciBlbCBub21icmUgZGUgbGFzIHZhcmlhYmxlcyBwcmVkaWN0b3JhcyBkZWwgbW9kZWxvDQogICAgeHZhcnMgPC0gbmFtZXMoY29lZmkpDQogICAgIyBQcm9kdWN0byBtYXRyaWNpYWwgZW50cmUgbG9zIGNvZWZpY2llbnRlcyBkZWwgbW9kZWxvIHkgbG9zIHZhbG9yZXMgZGUNCiAgICAjIGxvcyBwcmVkaWN0b3JlcyBkZSBsYXMgbnVldmFzIG9ic2VydmFjaW9uZXMgcGFyYSBvYnRlbmVyIGxhcyANCiAgICAjIHByZWRpY2Npb25lcw0KICAgIG1hdFsgLCB4dmFyc10gJSolIGNvZWZpDQp9DQpgYGANCi0gRnVuY2lvbiBwYXJhIHByZWRlY2lyDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KZm9sZHMgPC0gc2FtcGxlKHJlcCgxOjEwLCBsZW5ndGggPSBucm93KHJhd2RhdGEzKSkpDQp0YWJsZShmb2xkcykNCg0KYGBgDQoNCmBgYHtyfQ0KY3YuZXJyb3JzIDwtIG1hdHJpeChOQSwxMCwzMSkNCmZvcihrIGluIDE6MTApIHsNCiAgcmVnZml0LnZhbCA8LSByZWdzdWJzZXRzKFByaWNlfi4sIGRhdGEgPSByYXdkYXRhM1tmb2xkcyAhPWssXSwgbnZtYXggPSAzMSwgIHJlYWxseS5iaWcgPSBUUlVFKQ0KICAgZm9yKGkgaW4gMTozMSkgew0KICAgIHRlc3QgPC0gcmF3ZGF0YTNbZm9sZHMgPT0gayxdDQogICAgcHJlZGljdGlvbnMgPC0gcHJlZGljdC5yZWdzdWJzZXRzKG9iamVjdCA9IHJlZ2ZpdC52YWwsIG5ld2RhdGEgPSByYXdkYXRhM1tmb2xkcyA9PSBrLF0gLCBpZCA9IGkpDQogICAgY3YuZXJyb3JzW2ssaV0gPC0gbWVhbigodGVzdCRQcmljZSAtIHByZWRpY3Rpb25zKV4yKQ0KICB9DQp9DQpybXNlLmN2IDwtIHNxcnQoYXBwbHkoY3YuZXJyb3JzLCAyLCBtZWFuKSkNCnJtc2UuY3YNCmBgYA0KDQpgYGB7cn0NCg0Kc2V0LnNlZWQoMTIzKQ0KdmFsdWUubWluIDwtIHdoaWNoLm1pbihybXNlLmN2KQ0KdmFsdWUubWluDQpgYGANCg0KYGBge3J9DQpybXNlYnNzLmN2X21pbiA8LSBybXNlLmN2W3ZhbHVlLm1pbl0NCnJtc2Vic3MuY3ZfbWluDQpgYGANCg0KLSBFbCBtb2RlbG8gY29uIG1lbm9yIFJNU0UgZXMgZWwgZGUgMjEgcHJlZGljdG9yZXMgY29uIHVuIHJtc2UgZGUgMTA2NA0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpwbG90KHJtc2UuY3YsIHhsYWIgPSAiTnVtZXJvIGRlIFZhcmlhYmxlcyIsIHlsYWIgPSAiUm9vdCBNU0UgQ3Jvc3MgVmFsaWRhdGlvbiIsIHBjaCA9IDIwLCB0eXBlID0gImIiKQ0KdmFsdWUubWluIDwtIHdoaWNoLm1pbihybXNlLmN2KQ0KcG9pbnRzKHZhbHVlLm1pbiwgcm1zZS5jdlt2YWx1ZS5taW5dLCBjb2w9InJlZCIsIGNleD0yLCBwY2g9MjApDQpgYGANCg0KYGBge3J9DQpjb2VmKHJlZ2ZpdC52YWwsIHdoaWNoLm1pbihybXNlLmN2KSkNCmBgYA0KDQotIENvZWZpY2llbnRlcyBkZWwgbW9kZWxvIGNvbiBtZW5vciBSTVNFDQoNCiMjIyMjIyMjIyMjIw0KIyA0LiBTdGVwd2lzZSBTZWxlY3Rpb246IEZvcndhcmQgTW9kZWwNCiMjIyMjIyMjIyMjIw0KDQpgYGB7cn0NCnJlZ2ZpdGZ3ZC5mdWxsID0gcmVnc3Vic2V0cyhQcmljZSB+LiwgZGF0YSA9IHJhd2RhdGEzLCBudm1heD0zMSwgbWV0aG9kPSJmb3J3YXJkIikNCnN1bW1hcnkocmVnZml0ZndkLmZ1bGwpDQpgYGANCg0KLSBTdGVwd2lzZSBGb3J3YXJkICwgMzEgbW9kZWxvcw0KDQpgYGB7cn0NCnJlZy5zdW1tYXJ5ID0gc3VtbWFyeShyZWdmaXRmd2QuZnVsbCkNCm5hbWVzKHJlZy5zdW1tYXJ5KQ0KYGBgDQoNCiMjIDQuMSBSU1MNCmBgYHtyfQ0Kd2hpY2gubWluKHJlZy5zdW1tYXJ5JHJzcykNCnBsb3QocmVnLnN1bW1hcnkkcnNzLCB4bGFiPSJOdW1lcm8gZGUgdmFyaWFibGVzIiwgeWxhYj0iUlNTIiwgdHlwZSA9ICJiIikNCnBvaW50cyh3aGljaC5taW4ocmVnLnN1bW1hcnkkcnNzKSwgcmVnLnN1bW1hcnkkcnNzW3doaWNoLm1pbihyZWcuc3VtbWFyeSRyc3MpXSwgY29sPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KYGBgDQoNCi0gRWwgbW9kZWxvIGNvbiBtZW5vciByc3MgcG9zZWUgMzEgcHJlZGljdG9yZXMNCg0KIyMgNC4yIEFESlIyDQpgYGB7cn0NCndoaWNoLm1heChyZWcuc3VtbWFyeSRhZGpyMikNCnBsb3QocmVnLnN1bW1hcnkkYWRqcjIsIHhsYWI9Ik51bWVybyBkZSB2YXJpYWJsZXMiLCB5bGFiPSJBREpSMiIsIHR5cGUgPSAiYiIpDQpwb2ludHMod2hpY2gubWF4KHJlZy5zdW1tYXJ5JGFkanIyKSwgcmVnLnN1bW1hcnkkYWRqcjJbd2hpY2gubWF4KHJlZy5zdW1tYXJ5JGFkanIyKV0sIGNvbD0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCg0KYGBgDQoNCi0gRWwgbW9kZWxvIGNvbiBtYXlvciBSMiBhanVzdGFkbyBwb3NlZSAyMSBwcmVkaWN0b3Jlcw0KDQojIyA0LjMgQ1ANCg0KYGBge3J9DQp3aGljaC5taW4ocmVnLnN1bW1hcnkkY3ApDQpwbG90KHJlZy5zdW1tYXJ5JGNwLCB4bGFiPSJOdW1lcm8gZGUgdmFyaWFibGVzIiwgeWxhYj0iQ1AiLCB0eXBlID0gImIiKQ0KcG9pbnRzKHdoaWNoLm1pbihyZWcuc3VtbWFyeSRjcCksIHJlZy5zdW1tYXJ5JGNwW3doaWNoLm1pbihyZWcuc3VtbWFyeSRjcCldLCBjb2w9InJlZCIsIGNleD0yLCBwY2g9MjApDQoNCg0KYGBgDQoNCi0gRWwgbW9kZWxvIGNvbiBtZW5vciBDUCBwb3NlZSAyMSBwcmVkaWN0b3Jlcw0KDQoNCiMjIDQuNCBiaWMNCg0KYGBge3J9DQp3aGljaC5taW4ocmVnLnN1bW1hcnkkYmljKQ0KcGxvdChyZWcuc3VtbWFyeSRiaWMsIHhsYWI9Ik51bWVybyBkZSB2YXJpYWJsZXMiLCB5bGFiPSJDUCIsIHR5cGUgPSAiYiIpDQpwb2ludHMod2hpY2gubWluKHJlZy5zdW1tYXJ5JGJpYyksIHJlZy5zdW1tYXJ5JGJpY1t3aGljaC5taW4ocmVnLnN1bW1hcnkkYmljKV0sIGNvbD0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCg0KDQpgYGANCg0KLSBFbCBtb2RlbG8gY29uIG1lbm9yIGJpYyBwb3NlZSAxNSBwcmVkaWN0b3Jlcw0KDQoNCiMjIDQuNSBDb25jbHVzaW9uZXMNCmBgYHtyfQ0KcmVnLnN1bW1hcnkkYWRqcjJbNl0NCmBgYA0KYGBge3J9DQpyZWcuc3VtbWFyeSRhZGpyMls4XQ0KYGBgDQpgYGB7cn0NCnJlZy5zdW1tYXJ5JGFkanIyWzEwXQ0KYGBgDQpgYGB7cn0NCnJlZy5zdW1tYXJ5JGFkanIyWzE1XQ0KYGBgDQpgYGB7cn0NCnJlZy5zdW1tYXJ5JGFkanIyWzIxXQ0KYGBgDQoNCi0gQSBqdXpnYXIgcG9yIGxvcyBncmFmaWNvcyBwYXJlY2llcmEgcXVlIGVsIG51bWVybyBhZGVjdWFkbyBkZSBwcmVkaWN0b3JlcyBlcyAxNSwgZWwgUjIgYWp1c3RhZG8gdmFyaWEgbXV5IHBvY28gYWwgYXVtZW50YXIgbG9zIHByZWRpY3RvcmVzLg0KDQoNCiMjIDQuNiBWYWxpZGFjacOzbiBTdGVwd2lzZSBTZWxlY3Rpb24gRm9yd2FyZA0KDQpgYGB7cn0NCg0KaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihyYXdkYXRhMyRQcmljZSwgcCA9IDAuNywgbGlzdCA9IEZBTFNFKQ0KZGF0YS50cmFpbiA8LSByYXdkYXRhM1tpbmRleCwgXQ0KZGF0YS50ZXN0IDwtIHJhd2RhdGEzWy1pbmRleCwgXQ0KYGBgDQotIFBhcnRpY2lvbiBkZWwgZGF0YXNldCBlbiBlbnRyZW5hbWllbnRvIHkgdmFsaWRhY2lvbg0KDQpgYGB7cn0NCnNldC5zZWVkKDEwKQ0KZGltKHJhd2RhdGEzKQ0KYGBgDQoNCmBgYHtyfQ0KbW9kZWwuZndkIDwtIHJlZ3N1YnNldHMoUHJpY2Ugfi4sIGRhdGEgPSBkYXRhLnRyYWluLCBudm1heCA9IDMxLCBtZXRob2QgPSAiZm9yd2FyZCIsIHJlYWxseS5iaWcgPSBUUlVFKQ0Kc3VtbWFyeShtb2RlbC5md2QpDQpgYGANCg0KYGBge3J9DQp2YWwuZXJyb3JzID0gcmVwKE5BLDMxKQ0KeC50ZXN0IDwtIG1vZGVsLm1hdHJpeChQcmljZSB+LiwgZGF0YSA9IGRhdGEudGVzdCkNCmZvcihpIGluIDE6MzEpIHsNCmNvZWZpY2llbnRlcyA8LSBjb2VmKG1vZGVsLmZ3ZCwgaWQgPSBpKQ0KcHJlZGljdG9ycyA8LSB4LnRlc3RbLG5hbWVzKGNvZWZpY2llbnRlcyldICUqJSBjb2VmaWNpZW50ZXMNCnZhbC5lcnJvcnNbaV0gPC0gbWVhbigoZGF0YS50ZXN0JFByaWNlIC0gcHJlZGljdG9ycyleMikNCn0NCnJtc2UgPC0gc3FydCh2YWwuZXJyb3JzKQ0KYGBgDQoNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQp2YWx1ZS5taW4gPC0gd2hpY2gubWluKHJtc2UpDQp2YWx1ZS5taW4NCmBgYA0KDQpgYGB7cn0NCnJtc2VzZndfbWluIDwtIHJtc2VbdmFsdWUubWluXQ0Kcm1zZXNmd19taW4NCmBgYA0KDQotIExhIHZhbGlkYWNpb24gYXJyb2phIHF1ZSBlbCBtb2RlbG8gY29uIG1lbm9yIFJNU0UgZXMgZWwgZGUgMjAgdmFyaWFibGVzIGNvbiB1biBSTVNFIGRlIDEwMzUNCg0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCnJtc2VbMTVdDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpybXNlWzEwXQ0KYGBgDQoNCi0gTGEgZGlmZXJlbmNpYSBkZSBybXNlIGRlbCBtb2RlbG8gY29uIDE1IHByZWRpY3RvcmVzIHkgZWwgbW9kZWxvIGNvbiAyMCBwcmVkaWN0b3JlcyBlcyBtaW5pbWEuIFNlIHB1ZWRlIG9wdGFyIHBvciBlbCBtb2RlbG8gZGUgMTUgcHJlZGljdG9yZXMuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KcGxvdChybXNlLCB4bGFiID0gIk4TdW1lcm8gZGUgVmFyaWFibGVzIiwgeWxhYiA9ICJSb290IE1TRSBCZXN0IFN1YnNldCIsIHBjaCA9IDIwLCB0eXBlID0gImIiKQ0KcG9pbnRzKHZhbHVlLm1pbiwgcm1zZVt2YWx1ZS5taW5dLCBjb2w9InJlZCIsIGNleD0yLCBwY2g9MjApDQpgYGANCg0KYGBge3J9DQpjb2VmKG1vZGVsLmZ3ZCwgd2hpY2gubWluKHJtc2UpKQ0KYGBgDQoNCg0KIyMgNC43IFZhbGlkYWNpb24gY3J1emFkYSBTdGVwd2lzZSBGb3J3YXJkDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTQpDQpmb2xkcyA8LSBzYW1wbGUocmVwKDE6MTAsIGxlbmd0aCA9IG5yb3cocmF3ZGF0YTMpKSkNCnRhYmxlKGZvbGRzKQ0KYGBgDQoNCmBgYHtyfQ0KY3YuZXJyb3JzIDwtIG1hdHJpeChOQSwxMCwzMSkNCmZvcihrIGluIDE6MTApIHsNCiAgbW9kZWwuZndkIDwtIHJlZ3N1YnNldHMoUHJpY2V+LiwgZGF0YSA9IHJhd2RhdGEzW2ZvbGRzICE9ayxdLCBudm1heCA9IDMxLCBtZXRob2QgPSAiZm9yd2FyZCIsIHJlYWxseS5iaWcgPSBUUlVFKQ0KICAgZm9yKGkgaW4gMTozMSkgew0KICAgIHRlc3QgPC0gcmF3ZGF0YTNbZm9sZHMgPT0gayxdDQogICAgcHJlZGljdGlvbnMgPC0gcHJlZGljdC5yZWdzdWJzZXRzKG9iamVjdCA9IG1vZGVsLmZ3ZCwgbmV3ZGF0YSA9IHJhd2RhdGEzW2ZvbGRzID09IGssXSAsIGlkID0gaSkNCiAgICBjdi5lcnJvcnNbayxpXSA8LSBtZWFuKCh0ZXN0JFByaWNlIC0gcHJlZGljdGlvbnMpXjIpDQogIH0NCn0NCnJtc2UuY3YgPC0gc3FydChhcHBseShjdi5lcnJvcnMsIDIsIG1lYW4pKQ0KYGBgDQoNCg0KYGBge3J9DQpzZXQuc2VlZCgxNCkNCnZhbHVlLm1pbiA8LSB3aGljaC5taW4ocm1zZS5jdikNCnZhbHVlLm1pbg0KYGBgDQoNCmBgYHtyfQ0Kcm1zZXNmdy5jdl9taW4gPC0gcm1zZS5jdlt2YWx1ZS5taW5dDQpybXNlc2Z3LmN2X21pbg0KYGBgDQoNCi0gRWwgbW9kZWxvIGNvbiBybXNlIG1pbmltbyBlcyBlbCBkZSAyMiBwcmVkaXRvcmVzIGNvbiB1biB2YWxvciBkZSAxMDU3DQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTQpDQpwbG90KHJtc2UuY3YsIHhsYWIgPSAiThN1bWVybyBkZSBWYXJpYWJsZXMiLCB5bGFiID0gIlJvb3QgTVNFIENyb3NzIFZhbGlkYXRpb24iLCBwY2ggPSAyMCwgdHlwZSA9ICJiIikNCnZhbHVlLm1pbiA8LSB3aGljaC5taW4ocm1zZS5jdikNCnBvaW50cyh2YWx1ZS5taW4sIHJtc2UuY3ZbdmFsdWUubWluXSwgY29sPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KYGBgDQoNCmBgYHtyfQ0KY29lZihtb2RlbC5md2QsIHdoaWNoLm1pbihybXNlLmN2KSkNCmBgYA0KDQotIGNvZWZpY2llbnRlcyBkZWwgbW9kZWxvIGNvbiAyMiBwcmVkaWN0b3Jlcy4NCg0KDQojIyMjIyMjIyMjIyMNCiMgNS4gU3RlcHdpc2UgU2VsZWN0aW9uOiBCYWNrd2FyZCBNb2RlbA0KIyMjIyMjIyMjIyMjDQoNCmBgYHtyfQ0KcmVnZml0YndkLmZ1bGwgPSByZWdzdWJzZXRzKFByaWNlIH4uLCBkYXRhID0gcmF3ZGF0YTMsIG52bWF4PTMxLCBtZXRob2Q9ImJhY2t3YXJkIikNCnN1bW1hcnkocmVnZml0LmZ1bGwpDQpgYGANCg0KLSBTdGVwd2lzZSBiYWNrd2FyZCAsIDMxIG1vZGVsb3MNCg0KYGBge3J9DQpyZWcuc3VtbWFyeSA9IHN1bW1hcnkocmVnZml0YndkLmZ1bGwpDQpuYW1lcyhyZWcuc3VtbWFyeSkNCmBgYA0KDQojIyA1LjEgUlNTDQpgYGB7cn0NCndoaWNoLm1pbihyZWcuc3VtbWFyeSRyc3MpDQpwbG90KHJlZy5zdW1tYXJ5JHJzcywgeGxhYj0iTnVtZXJvIGRlIHZhcmlhYmxlcyIsIHlsYWI9IlJTUyIsIHR5cGUgPSAiYiIpDQpwb2ludHMod2hpY2gubWluKHJlZy5zdW1tYXJ5JHJzcyksIHJlZy5zdW1tYXJ5JHJzc1t3aGljaC5taW4ocmVnLnN1bW1hcnkkcnNzKV0sIGNvbD0icmVkIiwgY2V4PTIsIHBjaD0yMCkNCmBgYA0KDQotIEVsIG1vZGVsbyBjb24gbWVub3IgcnNzIHBvc2VlIDMxIHByZWRpY3RvcmVzDQoNCiMjIDUuMiBBREpSMg0KYGBge3J9DQp3aGljaC5tYXgocmVnLnN1bW1hcnkkYWRqcjIpDQpwbG90KHJlZy5zdW1tYXJ5JGFkanIyLCB4bGFiPSJOdW1lcm8gZGUgdmFyaWFibGVzIiwgeWxhYj0iQURKUjIiLCB0eXBlID0gImIiKQ0KcG9pbnRzKHdoaWNoLm1heChyZWcuc3VtbWFyeSRhZGpyMiksIHJlZy5zdW1tYXJ5JGFkanIyW3doaWNoLm1heChyZWcuc3VtbWFyeSRhZGpyMildLCBjb2w9InJlZCIsIGNleD0yLCBwY2g9MjApDQoNCmBgYA0KDQotIEVsIG1vZGVsbyBjb24gbWF5b3IgUjIgYWp1c3RhZG8gcG9zZWUgMjEgcHJlZGljdG9yZXMNCg0KIyMgNS4zIENQDQoNCmBgYHtyfQ0Kd2hpY2gubWluKHJlZy5zdW1tYXJ5JGNwKQ0KcGxvdChyZWcuc3VtbWFyeSRjcCwgeGxhYj0iTnVtZXJvIGRlIHZhcmlhYmxlcyIsIHlsYWI9IkNQIiwgdHlwZSA9ICJiIikNCnBvaW50cyh3aGljaC5taW4ocmVnLnN1bW1hcnkkY3ApLCByZWcuc3VtbWFyeSRjcFt3aGljaC5taW4ocmVnLnN1bW1hcnkkY3ApXSwgY29sPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KDQoNCmBgYA0KDQotIEVsIG1vZGVsbyBjb24gbWVub3IgQ1AgcG9zZWUgMjEgcHJlZGljdG9yZXMNCg0KDQojIyA1LjQgYmljDQoNCmBgYHtyfQ0Kd2hpY2gubWluKHJlZy5zdW1tYXJ5JGJpYykNCnBsb3QocmVnLnN1bW1hcnkkYmljLCB4bGFiPSJOdW1lcm8gZGUgdmFyaWFibGVzIiwgeWxhYj0iQ1AiLCB0eXBlID0gImIiKQ0KcG9pbnRzKHdoaWNoLm1pbihyZWcuc3VtbWFyeSRiaWMpLCByZWcuc3VtbWFyeSRiaWNbd2hpY2gubWluKHJlZy5zdW1tYXJ5JGJpYyldLCBjb2w9InJlZCIsIGNleD0yLCBwY2g9MjApDQoNCg0KYGBgDQoNCi0gRWwgbW9kZWxvIGNvbiBtZW5vciBiaWMgcG9zZWUgMTUgcHJlZGljdG9yZXMNCg0KDQojIyA1LjUgQ29uY2x1c2lvbmVzDQpgYGB7cn0NCnJlZy5zdW1tYXJ5JGFkanIyWzZdDQpgYGANCmBgYHtyfQ0KcmVnLnN1bW1hcnkkYWRqcjJbOF0NCmBgYA0KYGBge3J9DQpyZWcuc3VtbWFyeSRhZGpyMlsxMF0NCmBgYA0KYGBge3J9DQpyZWcuc3VtbWFyeSRhZGpyMlsxNV0NCmBgYA0KYGBge3J9DQpyZWcuc3VtbWFyeSRhZGpyMlsyMV0NCmBgYA0KDQotIEEganV6Z2FyIHBvciBsb3MgZ3JhZmljb3MgcGFyZWNpZXJhIHF1ZSBlbCBudW1lcm8gYWRlY3VhZG8gZGUgcHJlZGljdG9yZXMgZXMgMTUsIGVsIFIyIGFqdXN0YWRvIHZhcmlhIG11eSBwb2NvIGFsIGF1bWVudGFyIGxvcyBwcmVkaWN0b3Jlcy4NCg0KDQojIyA1LjYgVmFsaWRhY2nDs24gU3RlcHdpc2UgU2VsZWN0aW9uIEJhY2t3YXJkDQoNCmBgYHtyfQ0KDQppbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHJhd2RhdGEzJFByaWNlLCBwID0gMC43LCBsaXN0ID0gRkFMU0UpDQpkYXRhLnRyYWluIDwtIHJhd2RhdGEzW2luZGV4LCBdDQpkYXRhLnRlc3QgPC0gcmF3ZGF0YTNbLWluZGV4LCBdDQpgYGANCi0gUGFydGljaW9uIGRlbCBkYXRhc2V0IGVuIGVudHJlbmFtaWVudG8geSB2YWxpZGFjaW9uDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTkxKQ0KZGltKHJhd2RhdGEzKQ0KYGBgDQoNCmBgYHtyfQ0KbW9kZWwuYndkIDwtIHJlZ3N1YnNldHMoUHJpY2Ugfi4sIGRhdGEgPSBkYXRhLnRyYWluLCBudm1heCA9IDMxLCBtZXRob2QgPSAiYmFja3dhcmQiLCByZWFsbHkuYmlnID0gVFJVRSkNCnN1bW1hcnkobW9kZWwuYndkKQ0KYGBgDQoNCmBgYHtyfQ0KdmFsLmVycm9ycyA9IHJlcChOQSwzMSkNCngudGVzdCA8LSBtb2RlbC5tYXRyaXgoUHJpY2Ugfi4sIGRhdGEgPSBkYXRhLnRlc3QpDQpmb3IoaSBpbiAxOjMxKSB7DQpjb2VmaWNpZW50ZXMgPC0gY29lZihtb2RlbC5id2QsIGlkID0gaSkNCnByZWRpY3RvcnMgPC0geC50ZXN0WyxuYW1lcyhjb2VmaWNpZW50ZXMpXSAlKiUgY29lZmljaWVudGVzDQp2YWwuZXJyb3JzW2ldIDwtIG1lYW4oKGRhdGEudGVzdCRQcmljZSAtIHByZWRpY3RvcnMpXjIpDQp9DQpybXNlIDwtIHNxcnQodmFsLmVycm9ycykNCmBgYA0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTkxKQ0KdmFsdWUubWluIDwtIHdoaWNoLm1pbihybXNlKQ0KdmFsdWUubWluDQpgYGANCg0KYGBge3J9DQpybXNlc2J3X21pbiA8LSBybXNlW3ZhbHVlLm1pbl0NCnJtc2VzYndfbWluDQpgYGANCg0KLSBMYSB2YWxpZGFjaW9uIGFycm9qYSBxdWUgZWwgbW9kZWxvIGNvbiBtZW5vciBSTVNFIGVzIGVsIGRlIDE2IHZhcmlhYmxlcyBjb24gdW4gUk1TRSBkZSAxMDQyDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTkxKQ0Kcm1zZVsyMF0NCmBgYA0KYGBge3J9DQpzZXQuc2VlZCgxOTEpDQpybXNlWzE1XQ0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTkxKQ0Kcm1zZVsxMF0NCmBgYA0KDQotIExhIGRpZmVyZW5jaWEgZGUgcm1zZSBkZWwgbW9kZWxvIGNvbiAxNiBwcmVkaWN0b3JlcyB5IGVsIG1vZGVsbyBjb24gMjAgcHJlZGljdG9yZXMgZXMgbWluaW1hLg0KDQpgYGB7cn0NCnNldC5zZWVkKDE5MSkNCnBsb3Qocm1zZSwgeGxhYiA9ICJOE3VtZXJvIGRlIFZhcmlhYmxlcyIsIHlsYWIgPSAiUm9vdCBNU0UiLCBwY2ggPSAyMCwgdHlwZSA9ICJiIikNCnZhbHVlLm1pbiA8LSB3aGljaC5taW4ocm1zZSkNCnBvaW50cyh2YWx1ZS5taW4sIHJtc2VbdmFsdWUubWluXSwgY29sPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KYGBgDQoNCmBgYHtyfQ0KY29lZihtb2RlbC5md2QsIHdoaWNoLm1pbihybXNlKSkNCmBgYA0KDQoNCiMjIDUuNyBWYWxpZGFjaW9uIGNydXphZGEgU3RlcHdpc2UgQmFja3dhcmQNCg0KYGBge3J9DQpzZXQuc2VlZCgxNDEpDQpmb2xkcyA8LSBzYW1wbGUocmVwKDE6MTAsIGxlbmd0aCA9IG5yb3cocmF3ZGF0YTMpKSkNCnRhYmxlKGZvbGRzKQ0KYGBgDQoNCmBgYHtyfQ0KY3YuZXJyb3JzIDwtIG1hdHJpeChOQSwxMCwzMSkNCmZvcihrIGluIDE6MTApIHsNCiAgbW9kZWwuYndkIDwtIHJlZ3N1YnNldHMoUHJpY2V+LiwgZGF0YSA9IHJhd2RhdGEzW2ZvbGRzICE9ayxdLCBudm1heCA9IDMxLCBtZXRob2QgPSAiYmFja3dhcmQiLCByZWFsbHkuYmlnID0gVFJVRSkNCiAgIGZvcihpIGluIDE6MzEpIHsNCiAgICB0ZXN0IDwtIHJhd2RhdGEzW2ZvbGRzID09IGssXQ0KICAgIHByZWRpY3Rpb25zIDwtIHByZWRpY3QucmVnc3Vic2V0cyhvYmplY3QgPSBtb2RlbC5id2QsIG5ld2RhdGEgPSByYXdkYXRhM1tmb2xkcyA9PSBrLF0gLCBpZCA9IGkpDQogICAgY3YuZXJyb3JzW2ssaV0gPC0gbWVhbigodGVzdCRQcmljZSAtIHByZWRpY3Rpb25zKV4yKQ0KICB9DQp9DQpybXNlLmN2IDwtIHNxcnQoYXBwbHkoY3YuZXJyb3JzLCAyLCBtZWFuKSkNCmBgYA0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTQxKQ0KdmFsdWUubWluIDwtIHdoaWNoLm1pbihybXNlLmN2KQ0KdmFsdWUubWluDQpgYGANCg0KYGBge3J9DQpybXNlc2J3LmN2X21pbiA8LSBybXNlLmN2W3ZhbHVlLm1pbl0NCnJtc2VzYncuY3ZfbWluDQpgYGANCg0KLSBFbCBtb2RlbG8gY29uIHJtc2UgbWluaW1vIGVzIGVsIGRlIDIwIHByZWRpdG9yZXMgY29uIHVuIHZhbG9yIGRlIDEwNTkNCg0KYGBge3J9DQpzZXQuc2VlZCgxNDEpDQpwbG90KHJtc2UuY3YsIHhsYWIgPSAiThN1bWVybyBkZSBWYXJpYWJsZXMiLCB5bGFiID0gIlJvb3QgTVNFIENyb3NzIFZhbGlkYXRpb24iLCBwY2ggPSAyMCwgdHlwZSA9ICJiIikNCnBvaW50cyh2YWx1ZS5taW4sIHJtc2UuY3ZbdmFsdWUubWluXSwgY29sPSJyZWQiLCBjZXg9MiwgcGNoPTIwKQ0KYGBgDQoNCmBgYHtyfQ0KY29lZihtb2RlbC5id2QsIHdoaWNoLm1pbihybXNlLmN2KSkNCmBgYA0KDQotIGNvZWZpY2llbnRlcyBkZWwgbW9kZWxvIGNvbiAyMCBwcmVkaWN0b3Jlcy4NCg0KDQoNCiMjIyMjIyMjIyMjIyMjIw0KIyA2LiBSaWRnZQ0KIyMjIyMjIyMjIyMjIyMjDQoNCiMjIDYuMSBFc3RhbmRhcml6YWNpb24gZGUgdmFsb3Jlcw0KDQpgYGB7cn0NCg0KeCA8LSBtb2RlbC5tYXRyaXgoUHJpY2Ugfi4gLCByYXdkYXRhMylbLC0xXQ0KeSA8LSByYXdkYXRhMyRQcmljZQ0KZ3JpZCA8LSAxMF5zZXEoMTAsLTIsbGVuZ3RoID0gMTAwKQ0KcmlkZ2UubW9kZWwgPC0gZ2xtbmV0KHgsIHksIGFscGhhID0gMCwgbGFtYmRhID0gZ3JpZCkNCmRpbShjb2VmKHJpZGdlLm1vZGVsKSkNCmBgYA0KDQoNCmBgYHtyfQ0KY29lZihyaWRnZS5tb2RlbCkNCmBgYA0KDQoNCmBgYHtyfQ0KcGxvdChyaWRnZS5tb2RlbCwgeHZhciA9J2xhbWJkYScsbGFiZWwgPSBUUlVFKQ0KYGBgDQoNCg0KLSBBIG1lZGlkYSBxdWUgbGFtYmRhIGNyZWNlIGxvcyBjb2VmaWNpZW50ZXMgdGllbmRlbiBhIGNlcm8uDQoNCg0KYGBge3J9DQpvcHRpb25zKHNjaXBlbj05OTkpDQpwcmVkaWN0KHJpZGdlLm1vZGVsLCBzID0gMTAsIHR5cGUgPSAiY29lZmZpY2llbnRzIikNCmBgYA0KDQoNCiMjIDYuMiBWYWxpZGFjaW9uIENydXphZGENCmBgYHtyfQ0Kc2V0LnNlZWQoMjMpDQppbmRpY2VzIDwtIHNhbXBsZShjKFRSVUUsRkFMU0UpLCBucm93KHJhd2RhdGEzKSwgcmVwbGFjZSA9IFRSVUUpDQp5LnRlc3QgPC0geVstaW5kaWNlc10NCg0KcmlkZ2UubW9kZWwuY3YgPC0gY3YuZ2xtbmV0KHhbaW5kaWNlcyxdLCB5W2luZGljZXNdLCBhbHBoYSA9IDApICMgUG9yIGRlZmVjdG8sIDEwLWZvbGQNCmNvZWYocmlkZ2UubW9kZWwuY3YpDQpgYGANCg0KYGBge3J9DQpwbG90KHJpZGdlLm1vZGVsLmN2KQ0KYGBgDQoNCi0gTGFzIGxpbmVhcyBob3Jpem9udGFsZXMgcmVwcmVzZW50YW4sIGxhIHByaW1lcmEsIGVsIG1pbmltbyBkZSBsYSB2YWxpZGFjaW9uIGNydXphZGEsIHkgbGEgc2VndW5kYSwgZWwgZXJyb3IgZXN0YW5kYXIuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjMpDQpiZXN0bGFtIDwtIHJpZGdlLm1vZGVsLmN2JGxhbWJkYS5taW4NCmJlc3RsYW0NCmBgYA0KDQotIENhbGN1bGFtb3MgZWwgbWVub3IgbGFtYmRhIHBhcmEgYXBsaWNhcmxvIGEgbGEgdmFsaWRhY2lvbiBjcnV6YWRhDQotIE1lbm9yIGxhbWJkYSAyODUuMzgNCg0KIyMgNi4zDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjMpDQpyaWRnZS5wcmVkIDwtIHByZWRpY3QocmlkZ2UubW9kZWwsIHMgPSBiZXN0bGFtLCBuZXd4ID0geFstaW5kaWNlcyxdKQ0KbXNlIDwtbWVhbigocmlkZ2UucHJlZCAtIHlbLWluZGljZXNdKV4yKQ0Kcm1zZSA8LXNxcnQobXNlKQ0KdmFsdWUubWluIDwtIHdoaWNoLm1pbihybXNlKQ0KdmFsdWUubWluDQpgYGANCg0KYGBge3J9DQpybXNlcmlkZ2UgPC0gcm1zZVt2YWx1ZS5taW5dDQpybXNlcmlkZ2UNCmBgYA0KDQotIEVsIG1vZGVsbyBvYnRlbmlkbyBjb24gUmlkZ2UgUmVncmVzc2lvbiB1dGlsaXphbmRvIGVsIGxhbWJkYSBtaW5pbW8gbm9zIGRhIHVuIFJNU0UgZGUgMTA1Ni42DQoNCmBgYHtyfQ0Kb3V0IDwtIGdsbW5ldCh4LCB5LCBhbHBoYSA9IDApDQpwcmVkaWN0KG91dCwgdHlwZSA9ICJjb2VmZmljaWVudHMiLCBzID0gYmVzdGxhbSkNCmBgYA0KDQotIE5pbmd1bm8gZGUgbG9zIGNvZe+sgWNpZW50ZXMgZXMgZXhhY3RhbWVudGUgaWd1YWwgYSBjZXJvLCB5YSBxdWUgUmlkZ2Ugbm8gcmVhbGl6YSBzZWxlY2Npb24gZGUgdmFyaWFibGVzLg0KDQpgYGB7cn0NCnBsb3Qob3V0LCB4dmFyID0nbGFtYmRhJyxsYWJlbCA9IFRSVUUpDQoNCmBgYA0KDQotIExvcyBjb2VmaWNpZW50ZXMgc2UgYWp1c3RhbiBtYXMgYSBjZXJvDQoNCg0KIyMjIyMjIyMjIyMjIyMjDQojIDcuIExhc3NvDQojIyMjIyMjIyMjIyMjIyMNCg0KDQpgYGB7cn0NCmxhc3NvLm1vZGVsIDwtIGdsbW5ldCh4LCB5LCBhbHBoYSA9IDEpDQpwbG90KGxhc3NvLm1vZGVsLCB4dmFyID0nbGFtYmRhJyxsYWJlbCA9IFRSVUUpDQpgYGANCg0KIyMgNy4xIFZhbGlkYWNpb24gQ3J1emFkYQ0KDQpgYGB7cn0NCmluZGljZXMgPC0gc2FtcGxlKGMoVFJVRSxGQUxTRSksIG5yb3cocmF3ZGF0YTMpLCByZXBsYWNlID0gVFJVRSkNCmN2Lm91dCA8LSBjdi5nbG1uZXQoeFtpbmRpY2VzLF0sIHlbaW5kaWNlc10sIGFscGhhID0gMSkNCnBsb3QoY3Yub3V0KQ0KYGBgDQoNCi0gZWwgdmFsb3IgbWluaW1vIGRlIGxhIHZhbGlkYWNpb24gY3J1emFkYSwgZXMgZGUgdGFtYcOxbyAzMiwgbWllbnRyYXMgcXVlIGVsIGVycm9yIGVzdGFuZGFyIGVzIGRlIHRhbWHDsW8gMTUuIA0KDQpgYGB7cn0NCmNvZWYoY3Yub3V0KQ0KYGBgDQoNCi0gTGEgdmFsaWRhY2lvbiBjcnV6YWRhIG5vIHRvbWEgZW4gY3VlbnRhIDE3IHZhcmlhYmxlcywgZWwgbW9kZWxvIHF1ZWRhIGNvbiAxNSB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KYmVzdGxhbSA8LSBjdi5vdXQkbGFtYmRhLm1pbg0KYmVzdGxhbQ0KYGBgDQoNCi0gTGFtYmRhIG1pbmltbyBlcyAwLjc5DQoNCg0KIyMgNy4yIFZhbGlkYWNpb24NCg0KYGBge3J9DQpsYXNzby5wcmVkIDwtIHByZWRpY3QobGFzc28ubW9kZWwsIHMgPSBiZXN0bGFtLCBuZXd4ID0geFstaW5kaWNlcyxdKQ0KbXNlIDwtbWVhbigobGFzc28ucHJlZCAtIHlbLWluZGljZXNdKV4yKQ0Kcm1zZSA8LXNxcnQobXNlKQ0KdmFsdWUubWluIDwtIHdoaWNoLm1pbihybXNlKQ0Kcm1zZWxhc3NvIDwtIHJtc2VbdmFsdWUubWluXQ0Kcm1zZWxhc3NvDQoNCmBgYA0KDQotRWwgdmFsb3IgZGUgUk1TRSBlcyBkZSAxMDI1IA0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuPTk5OSkNCm91dCA8LSBnbG1uZXQoeCwgeSwgYWxwaGEgPSAxKQ0KbGFzc28uY29lZiA8LSBwcmVkaWN0KG91dCwgdHlwZSA9ICJjb2VmZmljaWVudHMiLCBzID0gYmVzdGxhbSkNCmxhc3NvLmNvZWYNCmBgYA0KDQpgYGB7cn0NCnBsb3Qob3V0LCB4dmFyPSdsYW1iZGEnLCBsYWJlbD0gVFJVRSkNCmBgYA0KDQotIFNlIHJlZW1wbGF6YSBlbCB2YWxvciBhcmJpdHJhcmlvIGNvbiBlbCBtZWpvciBsYW1iZGEgb2J0ZW5pZG8NCg0KDQojIyMjIyMjIyMjIyMjIyMNCiMgOC4gUENBDQojIyMjIyMjIyMjIyMjIyMNCg0KDQpgYGB7cn0NCg0KcmF3ZGF0YTQgPC0gcmF3ZGF0YTINCnJhd2RhdGE0IDwtIGR1bW15X2NvbHMocmF3ZGF0YTQsIHNlbGVjdF9jb2x1bW5zID0gIkZ1ZWxfVHlwZSIsIHJlbW92ZV9maXJzdF9kdW1teSA9IFRSVUUpDQpyYXdkYXRhNCA8LSBzZWxlY3QocmF3ZGF0YTQsIC0gRnVlbF9UeXBlLCAtIE1mZ19ZZWFyLCAtIE1mZ19Nb250aCwgLSBDeWxpbmRlcnMpDQoNCmBgYA0KDQpgYGB7cn0NCmFwcGx5KFggPSByYXdkYXRhNCwgTUFSR0lOID0gMiwgRlVOID0gbWVhbikNCg0KYGBgDQoNCi0gTGEgbWVkaWEgZGUgbGFzIHZhcmlhYmxlcywgdmVtb3MgcXVlIGV4aXN0ZSBtdWNoYSBkaWZlcmVuY2lhIGVudHJlIGFsZ3VuYXMgbWVkaWFzIGNvbW8gbGEgZGUgUHJpY2UgbyBLTS4gRXMgbmVjZXNhcmlvIHVuYSBlc3RhbmRhcml6YWNpb24uIExBIG1lZGlhIGRlYmUgc2VyIGNlcmNhbmEgYSBjZXJvLg0KDQoNCmBgYHtyfQ0KYXBwbHkoWCA9IHJhd2RhdGE0LCBNQVJHSU4gPSAyLCBGVU4gPSB2YXIpDQpgYGANCg0KLSBMYSB2YXJpYW56YSBlcyBsYSBkZXN2aWFjaW9uIGVzdGFuZGFyIGFsIGN1YWRyYWRvLCBhbmFsaXpvIGxhIHZhcmlhbnphIHlhIHF1ZSBsYSBkZXN2aWFjaW9uIGVzdGFuZGFyIHB1ZWRlIHZhbG9yZXMgbmVnYXRpdm9zLiBTZSBwdWVkZSBvYnNlcnZhciBxdWUgYWxndW5hcyB2YXJpYWJsZXMgcG9zZWVuIHZhcmlhbnphcyBkaXNwYXJlcy4gTGEgdmFyaWFuemEgZGViZSBzZXIgY2VyY2FuYSBhIHVuby4NCg0KDQojIyA4LjEgTW9kZWxvIFBDQQ0KYGBge3J9DQpwY2EubW9kZWwgPC0gcHJjb21wKHJhd2RhdGE0WywtMV0sIHNjYWxlID0gVFJVRSkNCm5hbWVzKHBjYS5tb2RlbCkgDQpgYGANCg0KDQpgYGB7cn0NCnBjYS5tb2RlbCRjZW50ZXINCmBgYA0KDQotIFJlcHJlc2VudGEgbGEgbWVkaWEgZGUgbGFzIHZhcmlhYmxlcyBwcmV2aWEgYSBsYSBlc3RhbmRhcml6YWNpb24NCg0KYGBge3J9DQpwY2EubW9kZWwkc2NhbGUNCmBgYA0KDQotIFJlcHJlc2VudGEgbGEgZGVzdmlhY2lvbiBkZSBsYXMgdmFyaWFibGVzIHByZXZpYSBhIGxhIGVzdGFuZGFyaXphY2lvbiANCg0KYGBge3J9DQpwY2EubW9kZWwkcm90YXRpb24NCmBgYA0KDQotIENhZGEgdmFyaWFibGUgZGUgbGFzIGNvbXBvbmVudGVzIHRpZW5lbiBhc2lnbmFkbyB1biBwZXNvLCBsYXMgZGUgbWF5b3IgcGVzbyBzb24gbGFzIHF1ZSBleHBsaWNhbiBkaWNoYSBjb21wb25lbnRlLg0KDQpgYGB7cn0NCmRpbShwY2EubW9kZWwkeCkNCmBgYA0KDQotIERpbWVuc2lvbiBkZWwgZGF0YXNldCwgMTQzNiBvYnNlcnZhY2lvbmVzIHkgMzIgY29tcG9uZW50ZXMuDQoNCmBgYHtyfQ0KYmlwbG90KHggPSBwY2EubW9kZWwsIHNjYWxlID0gMCwgY2V4ID0gMC42LCBjb2wgPSBjKCJibHVlIiwgImJyb3duMyIpKQ0KYGBgDQoNCi0gR3JhZmljYSBQcmltZXJhIHkgc2VndW5kYSBjb21wb25lbnRlDQoNCg0KIyMgOC4yIFZhcmlhbnphIGV4cGxpY2FkYSBkZSBsYXMgY29tcG9uZW50ZXMNCmBgYHtyfQ0KcGNhLm1vZGVsJHNkZXZeMg0KYGBgDQoNCg0KYGBge3J9DQpwcm9wLnZhcmlhbmNlIDwtIHBjYS5tb2RlbCRzZGV2XjIvc3VtKHBjYS5tb2RlbCRzZGV2XjIpDQpwcm9wLnZhcmlhbmNlDQpgYGANCg0KLSBQcm9wb3JjaW9uIHJlc3BlY3RvIGFsIHRvdGFsDQoNCg0KYGBge3J9DQpwcm9wLmN1bXVsYXRpdmUgPC0gY3Vtc3VtKHByb3AudmFyaWFuY2UpDQpwcm9wLmN1bXVsYXRpdmUNCmBgYA0KDQotIFByb3BvcmNpb24gZGUgbGEgdmFyaWFuemEgYWN1bXVsYWRhDQoNCmBgYHtyfQ0KcGxvdChwcm9wLmN1bXVsYXRpdmUsIHR5cGUgPSAiYiIsIHBjaCA9IDIwLCB5bGFiID0gIlByb3BvcmNpb24gZGUgbGEgVmFyaWFuemEgQWN1bXVsYWRhIiwgDQp4bGFiID0gIkNvbXBvbmVudGUgUHJpbmNpcGFsIiApDQpwb2ludHMocHJvcC5jdW11bGF0aXZlLCBjb2wgPSAicmVkIikNCg0KYGBgDQoNCi0gTGEgMWVyYSBjb21wb25lbnRlIGV4cGxpY2EgZWwgMTUlLCBsYSAgMmRhIGVsIDI3JSAsZXRjLiBTZSBuZWNlc2l0YW4gMTUgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgcGFyYSBleHBsaWNhciBlbCA4MSUNCi0gUGFyYSBleHBsaWNhciBlbCA5OCUgc2UgbmVjZXNpdGFuIDI2IHZhcmlhYmxlcy4NCi0gQ29uIGxhcyBwcmltZXJhcyAyMSBjb21wb25lbnRlcyBwcmluY2lwYWxlcyBzZSBwdWVkZW4gZXhsaWNhciA5MiUgZGUgbGEgdmFyaWFuemEgYWN1bXVsYWRhDQoNCiMjIyMjIyMjIyMjIyMjDQojOS4gUENSDQojIyMjIyMjIyMjIyMjIw0KDQoNCiMjIDkuMiBQYXJ0aWNpb24gZGVsIGRhdGFzZXQNCmBgYHtyfQ0Kc2V0LnNlZWQoNzg5KQ0KaW5kaWNlcyA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHJhd2RhdGE0JFByaWNlLCBwID0gMC44LCBsaXN0ID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCg3ODkpDQpwY3IubW9kZWwgPC0gcGNyKGZvcm11bGEgPSBQcmljZSB+IC4sIGRhdGEgPSByYXdkYXRhNFtpbmRpY2VzLCBdLCBzY2FsZSA9IFRSVUUsIG5jb21wPTIxKQ0Kc3VtbWFyeShwY3IubW9kZWwpDQpgYGANCg0KLSBTZSByZXJhbGl6YSBlbCBhanVzdGUgZGVsIG1vZGVsbywgc2UgdXRpbGl6YW4gbGFzIHByaW1lcmFzIDIxIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzDQoNCiMjIDkuMyBWYWxpZGFjaW9uDQoNCmBgYHtyfQ0KcGNyLm1vZGVsLnByZWQgPC0gcHJlZGljdChwY3IubW9kZWwsIG5ld2RhdGEgPSByYXdkYXRhNFstaW5kaWNlcyxdLCBuY29tcCA9IDIxKQ0KbXNlLnRlc3QgPC0gbWVhbigocGNyLm1vZGVsLnByZWQgLSByYXdkYXRhNFstaW5kaWNlcyxdJFByaWNlKV4yKQ0KbXNlLnRlc3QNCnJtc2VwY3IgPC0gc3FydChtc2UudGVzdCkNCnJtc2VwY3INCmBgYA0KDQotIEVsIFJNU0UgZXMgZGUgMTMzMiwgc2UgdXRpbGl6YW4gbG9zIGRhdG9zIGRlIHRlc3QuDQoNCg0KIyMgOS40IFZhbGlkYWNpb24gY3J1emFkYQ0KDQpgYGB7cn0NCnNldC5zZWVkKDY3OCkNCnBjci5tb2RlbCA8LSBwY3IoZm9ybXVsYSA9IFByaWNlIH4uLCBkYXRhID0gcmF3ZGF0YTRbaW5kaWNlcywgXSwgc2NhbGUgPSBUUlVFLCB2YWxpZGF0aW9uID0gIkNWIikNCnN1bW1hcnkocGNyLm1vZGVsKQ0KYGBgDQoNCmBgYHtyfQ0KcGNyLm1vZGVsLmN2IDwtIE1TRVAocGNyLm1vZGVsLCBlc3RpbWF0ZSA9ICJDViIpDQptIDwtIHdoaWNoLm1pbihwY3IubW9kZWwuY3YkdmFsKQ0KbQ0KYGBgDQoNCmBgYHtyfQ0Kcm1zZXBjci5jdiA8LSBzcXJ0KHBjci5tb2RlbC5jdiR2YWxbbV0pDQpybXNlcGNyLmN2DQpgYGANCg0KLSBMQSB2YWxpZGFjaW9uIGNydXphZGEgZGEgY29tbyByZXN1bHRhZG8gdW4gbW9kZWxvIGNvbiAzMSBjb21wb25lbnRlcyB5IHVuIFJNU0UgZGUgMTIyNw0KDQoNCmBgYHtyfQ0KcGxvdChwY3IubW9kZWwuY3YkdmFsLCBtYWluID0gIk1TRSB2cyBucm8gY29tcG9uZW50ZXMiLCB0eXBlID0gImwiLA0KeWxhYiA9ICJNU0UiLCBjb2wgPSAiYmx1ZSIsIHhsYWIgPSAiQ29tcG9uZW50ZXMiKQ0KYGBgDQoNCi0gT2JzZXJ2YW5kbyBlbCBncmFmaWNvLCBlbCBtZW5vciBNU0Ugc2UgZGEgY29uIDMxIGNvbXBvbmVudGVzLCBxdWUgZXMgZWwgbnVtZXJvIGRlIHByZWRpY3RvcmVzLCBjb25jbHVpbW9zIHF1ZSBubyBwdWRlIHJlZHVjaXIgbGEgZGltZW5zaW9uYWxpZGFkIGRlbCBkYXRhc2V0IG9yaWdpbmFsLg0KDQoNCmBgYHtyfQ0KcGNyLm1vZGVsLnByZWQgPC0gcHJlZGljdChwY3IubW9kZWwsIG5ld2RhdGEgPSByYXdkYXRhNFstaW5kaWNlcyxdKQ0KbXNlLnRlc3QgPC0gbWVhbigocGNyLm1vZGVsLnByZWQgLSByYXdkYXRhNFstaW5kaWNlcyxdJFByaWNlKV4yKQ0KbXNlLnRlc3QNCnJtc2VwY3IuY3YgPC0gc3FydChtc2UudGVzdCkNCnJtc2VwY3IuY3YNCmBgYA0KDQotRXZhbHVhY2lvbiBkZWwgbW9kZWxvIFBDUiBjb250cmEgbG9zIGRhdG9zIGRlIHBydWViYS4gQ2FsY3VsbyBkZWwgTVNFIHkgUk1TRSBjb3JyZXNwb25kaWVudGUuDQotIEVsIG1vZGVsbyBubyBlcyBidWVubyBkYWRhIGxhIGlndWFsZGFkIGNvbXBvbmVudGVzLXByZWRpY3RvcmVzIHBlcm8gbG9zIGRhdG9zIHNpcnZlbiBkZSByZWZlcmVuY2lhIHBhcmEgb3Ryb3MgbWV0b2Rvcy4NCg0KYGBge3J9DQpwY3IubW9kZWwucHJlZCA8LSBwcmVkaWN0KHBjci5tb2RlbCwgbmV3ZGF0YSA9IHJhd2RhdGE0Wy1pbmRpY2VzLF0sIG5jb21wID0gMjYpDQptc2UudGVzdCA8LSBtZWFuKChwY3IubW9kZWwucHJlZCAtIHJhd2RhdGE0Wy1pbmRpY2VzLF0kUHJpY2UpXjIpDQptc2UudGVzdA0Kcm1zZXBjci5jdiA8LSBzcXJ0KG1zZS50ZXN0KQ0Kcm1zZXBjci5jdg0KYGBgDQoNCi0gQ29uIDI2IGNvbXBvbmVudGVzIGVsIFJtc2UgZXMgZGUgMTMyNC4NCg0KDQoNCiMjIyMjIyMjIyMjIyMjIw0KIyAxMCBQTFMNCiMjIyMjIyMjIyMjIyMjIw0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMzMyKQ0KaW5kaWNlcyA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHJhd2RhdGE0JFByaWNlLCBwID0gMC44LCBsaXN0ID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCgzMzIpDQpwbHMubW9kZWwgPC0gcGxzcihmb3JtdWxhID0gUHJpY2Ugfi4sIGRhdGEgPSByYXdkYXRhNFtpbmRpY2VzLCBdLCBzY2FsZSA9IFRSVUUsIHZhbGlkYXRpb24gPSAiQ1YiKQ0KcGxzLm1vZGVsLmN2IDwtIE1TRVAocGxzLm1vZGVsLCBlc3RpbWF0ZSA9ICJDViIpDQp3aGljaC5taW4ocGxzLm1vZGVsLmN2JHZhbCkNCg0KYGBgDQoNCmBgYHtyfQ0KcGxvdChwbHMubW9kZWwuY3YkdmFsLCBtYWluID0gIk1TRSB2cyBOIENvbXBvbmVudGVzIiwgdHlwZSA9ICJsIiwgeWxhYiA9ICJNU0UiLCBjb2wgPSAiYmx1ZSIsIHhsYWIgPSAiQ29tcG9uZW50ZXMiKQ0KYGBgDQoNCmBgYHtyfQ0KcGxzLm1vZGVsLnByZWQgPC0gcHJlZGljdChwbHMubW9kZWwsIG5ld2RhdGEgPSByYXdkYXRhNFstaW5kaWNlcyxdLCBuY29tcCA9IDUpDQptc2UudGVzdCA8LSBtZWFuKChwbHMubW9kZWwucHJlZCAtIHJhd2RhdGE0Wy1pbmRpY2VzLF0kUHJpY2UpXjIpDQptc2UudGVzdA0Kcm1zZXBjci5jdiA8LSBzcXJ0KG1zZS50ZXN0KQ0Kcm1zZXBjci5jdg0KYGBgDQoNCi0gRWwgUk1TRSBwYXJhIDUgY29tcG9uZW50ZXMgY29tbyBzdWdpZXJlIHBscywgZXMgZGUgMTI4Mi4NCg0KDQoNCiMjIDEwLjEgQW5hbGlzaXMgY29uIDIxIHByZWRpY3RvcmVzLg0KDQoNCiMjIyAxMC4xLjEgQ3JlYWNpb24gZGUgbW9kZWxvDQpgYGB7cn0NCnNldC5zZWVkKDMzMykNCnBscy5tb2RlbCA8LSBwbHNyKGZvcm11bGEgPSBQcmljZSB+IC4sIGRhdGEgPSByYXdkYXRhNFtpbmRpY2VzLCBdLCBzY2FsZSA9IFRSVUUsIG5jb21wPTIxKQ0Kc3VtbWFyeShwbHMubW9kZWwpDQpgYGANCg0KLSBTZSB1dGlsaXpvIDIxIHByZWRpY3RvcmVzLCBxdWUgZXhwbGljYW4gZWwgODMlLCBjb24gUENSIGV4cGxpY2FiYSB1biA5MiUNCg0KIyMjIDEwLjEuMiBWYWxpZGFjaW9uDQoNCmBgYHtyfQ0KcGxzLm1vZGVsLnByZWQgPC0gcHJlZGljdChwbHMubW9kZWwsIG5ld2RhdGEgPSByYXdkYXRhNFstaW5kaWNlcyxdLCBuY29tcCA9IDIxKQ0KbXNlLnRlc3QgPC0gbWVhbigocGxzLm1vZGVsLnByZWQgLSByYXdkYXRhNFstaW5kaWNlcyxdJFByaWNlKV4yKQ0Kcm1zZXBjciA8LSBzcXJ0KG1zZS50ZXN0KQ0Kcm1zZXBjcg0Kd2hpY2gubWluKHBscy5tb2RlbC5jdiR2YWwpDQpgYGANCg0KLSBSTVNFIDExOTYgcGFyYSAyMSBwcmVkaWN0b3Jlcy4gDQoNCg0KIyMjIDEwLjEuMyBWYWxpZGFjaW9uIENydXphZGENCmBgYHtyfQ0Kc2V0LnNlZWQoMzMzKQ0KcGxzLm1vZGVsIDwtIHBsc3IoZm9ybXVsYSA9IFByaWNlIH4uLCBkYXRhID0gcmF3ZGF0YTRbaW5kaWNlcywgXSwgc2NhbGUgPSBUUlVFLCB2YWxpZGF0aW9uID0gIkNWIikNCnN1bW1hcnkocGxzLm1vZGVsKQ0KYGBgDQoNCmBgYHtyfQ0KcGxzLm1vZGVsLmN2IDwtIE1TRVAocGxzLm1vZGVsLCBlc3RpbWF0ZSA9ICJDViIpDQptIDwtIHdoaWNoLm1pbihwbHMubW9kZWwuY3YkdmFsKQ0KbQ0KYGBgDQoNCmBgYHtyfQ0Kcm1zZXBsc3QuY3YgPC0gc3FydChtc2UudGVzdCkNCnJtc2VwbHN0LmN2DQpgYGANCg0KDQojIyMjIyMjIyMjIyMNCiMgMTEuIENvbmNsdXNpb25lcw0KIyMjIyMjIyMjIyMjDQoNCmBgYHtyfQ0KbWV0b2RvIDwtIGMoIkJTUyIsIkZXIiwgIkJXIiwgIlJpZGdlIiwgIkxhc3NvIiwgIlBDUiIsICJQTFMiKQ0KdGVzdF9STVNFIDwtIGMocm1zZWJzcy5jdl9taW4sIHJtc2VzZncuY3ZfbWluLCBybXNlc2J3LmN2X21pbiwgcm1zZXJpZGdlLA0KICAgICAgICAgICAgICBybXNlbGFzc28sIHJtc2VwY3IuY3YsIHJtc2VwbHN0LmN2KQ0KcmVzdWx0YWRvcyA8LSBkYXRhLmZyYW1lKG1ldG9kbywgdGVzdF9STVNFKQ0KcmVzdWx0YWRvcw0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHJlc3VsdGFkb3MsIGFlcyh4ID0gcmVvcmRlcihtZXRvZG8sIHRlc3RfUk1TRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gdGVzdF9STVNFKSkgKw0KICBnZW9tX2NvbCh3aWR0aCA9IDAuNSkgKw0KICBsaW1zKHkgPSBjKDAsIDE2MDApKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZCh0ZXN0X1JNU0UsIDIpKSwgdmp1c3QgPSAtMSkgKw0KICB0aGVtZV9ncmF5KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KLSBFbCBtZWpvciBtb2RlbG8gZXMgZWwgcmVzdWx0YWRvIGRlbCBtZXRvZG8gTGFzc28sIHlhIHF1ZSBwb3NlZSBlbCBtZW5vciBSTVNFLg0KLSBTZSBvYmVydmEgdW5hIGdyYW4gZGlmZXJlbmNpYSBlbnRyZSBQTFMsIFBDUiwgQlcoTG9zIGRlIG1hcyBhbHRvIFJNU0UpIHkgbG9zIGRlbcOhcyBtZXRvZG9zLg0KDQoNCg0KIyMjIyMjIyMNCiMgMTIuIE1vZGVsbyBjb24gTGFzc28NCiMjIyMjIyMjDQoNCi0gTGFzIHZhcmlhYmxlcyBzZWxlY2Npb25hZGFzIHBvciBMYXNzbyBzb246DQoNCi0gQWdlXzA4XzA0ICAgICAgICAgDQotIEtNICAgICAgICAgICAgICAgICAgDQotIEhQICAgICAgICAgICAgICAgICAgDQotIGNjICAgICAgICAgICAgICAgICAgDQotIEdlYXJzICAgICAgICAgICAgICANCi0gV2VpZ2h0ICAgICAgICAgICAgICANCi0gTWZyX0d1YXJhbnRlZSAgICAgIA0KLSBCT1ZBR19HdWFyYW50ZWUgICAgIA0KLSBHdWFyYW50ZWVfUGVyaW9kICAgIA0KLSBBaXJjbyAgICAgICAgICAgICAgIA0KLSBBdXRvbWF0aWNfYWlyY28gICANCi0gQ0RfUGxheWVyICAgICAgICAgICANCi0gQ2VudHJhbF9Mb2NrICAgICAgIA0KLSBQb3dlcmVkX1dpbmRvd3MgICAgDQotIE1ldGFsbGljX1JpbSAgICAgICANCg0KDQojIyAxMi4xIExpbXBpZXphDQpgYGB7cn0NCmRhdGFsYXNzbyA8LSByYXdkYXRhMw0KZGF0YWxhc3NvIDwtIHNlbGVjdChkYXRhbGFzc28sIFByaWNlLCBBZ2VfMDhfMDQsIEtNLCBIUCwgY2MsIEdlYXJzLCBXZWlnaHQsIE1mcl9HdWFyYW50ZWUsIEJPVkFHX0d1YXJhbnRlZSwgR3VhcmFudGVlX1BlcmlvZCwgQWlyY28sIEF1dG9tYXRpY19haXJjbywgQ0RfUGxheWVyLCBDZW50cmFsX0xvY2ssIFBvd2VyZWRfV2luZG93cywgTWV0YWxsaWNfUmltKQ0KDQpgYGANCg0KYGBge3J9DQpkaW0oZGF0YWxhc3NvKQ0KYGBgDQoNCiMjIDEyLjIgUmVncmVzaW9uIExpbmVhbA0KDQpgYGB7cn0NCmxhc3NvTE0gPC0gKGxtKGZvcm11bGEgPSBQcmljZSB+LiAsIGRhdGEgPSBkYXRhbGFzc28pKQ0Kc3VtbWFyeShsYXNzb0xNKQ0KYGBgDQoNCi0gTGEgdmFyaWFibGUgQ2VudHJhbF9Mb2NrIHRpZW5lIHVuIHRfdmFsdWUgcXVlIG5vIGVzIHNpZ25pZmljYXRpdmFtZW50ZSBkaXN0aW50byBhIGNlcm8sIGVzIHByb2JhYmxlIHF1ZSBubyBhZ3JlZ3VlIG5hZGEgYWwgbW9kZWxvLg0KDQotIFIgY3VhZHJhZG8gZXMgZGUgMC44OQ0KDQpgYGB7cn0NCmhpc3QobGFzc29MTSRyZXNpZHVhbHMpICMgaGlzdG9ncmFtYSBkZSBsb3MgcmVzaWR1b3MgZXN0YW5kYXJpemFkb3MgDQojcXFub3JtKGxhc3NvTE0kcmVzaWR1YWxzKSAjIGdyw6FmaWNvIGRlIGN1YW50aWxlcyBkZSBsb3MgcmVzaWR1b3MgZXN0YW5kYXJpemFkb3MgDQojcXFsaW5lKGxhc3NvTE0kcmVzaWR1YWxzKQ0KYGBgDQoNCg0KLSBTZSBwdWVkZSBvYnNlcnZhciBzaW1ldHJpYSBlbiBlbCBoaXN0b2dyYW1hIGRlIHJlc2lkdW9zLg0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsMikpICMgZGl2aWRlIGxhIHZlbnRhbmEgZW4gdW5hIGZpbGEgeSB0cmVzIGNvbHVtbmFzIA0KcGxvdChsYXNzb0xNKQ0KYGBgDQoNCi0gUmVzaWR1YWxzIHZzIEZpdHRlZDogRWwgZ3JhZmljbyBtdWVzdHJhIHVuYSBkaXN0cmlidWNpb24gZGUgbmF0dXJhbGV6YSBhbGVhdG9yaWEuIEVsIHByb21lZGlvIGRlIGxhIGdyYWZpY2Egc2UgcGllcmRlIGVuIGNlcm8uDQoNCi0gUVE6IENpZXJ0YXMgbXVlc3RyYXMgbm8gc2UgZW5jdWVudHJhbiBzb2JyZSBsYSBsaW5lYSwgZXMgaW5kaWNhdGl2byBkZSBxdWUgc2UgcHVlZGUgbWVqb3JhciBlbCBtb2RlbG8NCg0KLSBTY2FsZS1Mb2NhdGlvbjogTXVlc3RyYSBsYSB2YXJpYW56YSBkZSBsb3MgcmVzaWR1b3MNCg0KLSBSZXNpZHVhbHMgdnMgTGV2ZXJhZ2U6IE5vIHNlIG9ic2VydmEgbmluZ3VuIHB1bnRvIGZpZXJhIGRlIGxhIGxpbmVhIHB1bnRlYWRhLCBlcyBkZWNpciBubyBzZSBvYnNlcnZhbiBwdW50b3MgaW5mbHV5ZW50ZXMu