Teoría

El Bosque aleatorio es un algoritmo de aprendizaje que combina el resultado de múltiples arboles de decision para llegar a un resultado optimo.

Ejemplo 1. Melbourne

En esta base de datos tenemos los precios de mas de 13,000 casas de la ciudad de Melbourne:

Instalar paquetes y llamar librerías

#install.packages("rpart")
library(rpart)
#install.packages("rpart.plot")
library(rpart.plot)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(modelr)
library(randomForest)
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
## 
## Adjuntando el paquete: 'randomForest'
## 
## The following object is masked from 'package:dplyr':
## 
##     combine
## 
## The following object is masked from 'package:ggplot2':
## 
##     margin
library(caret)
## Cargando paquete requerido: lattice
## 
## Adjuntando el paquete: 'caret'
## 
## The following object is masked from 'package:purrr':
## 
##     lift

Importar la base de datos

df <- read.csv("D:\\Anton V2\\Downloads\\melbourne.csv")

Entender la base de datos

summary(df)
##     Suburb            Address              Rooms            Type          
##  Length:13580       Length:13580       Min.   : 1.000   Length:13580      
##  Class :character   Class :character   1st Qu.: 2.000   Class :character  
##  Mode  :character   Mode  :character   Median : 3.000   Mode  :character  
##                                        Mean   : 2.938                     
##                                        3rd Qu.: 3.000                     
##                                        Max.   :10.000                     
##                                                                           
##      Price            Method            SellerG              Date          
##  Min.   :  85000   Length:13580       Length:13580       Length:13580      
##  1st Qu.: 650000   Class :character   Class :character   Class :character  
##  Median : 903000   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :1075684                                                           
##  3rd Qu.:1330000                                                           
##  Max.   :9000000                                                           
##                                                                            
##     Distance        Postcode       Bedroom2         Bathroom    
##  Min.   : 0.00   Min.   :3000   Min.   : 0.000   Min.   :0.000  
##  1st Qu.: 6.10   1st Qu.:3044   1st Qu.: 2.000   1st Qu.:1.000  
##  Median : 9.20   Median :3084   Median : 3.000   Median :1.000  
##  Mean   :10.14   Mean   :3105   Mean   : 2.915   Mean   :1.534  
##  3rd Qu.:13.00   3rd Qu.:3148   3rd Qu.: 3.000   3rd Qu.:2.000  
##  Max.   :48.10   Max.   :3977   Max.   :20.000   Max.   :8.000  
##                                                                 
##       Car           Landsize         BuildingArea     YearBuilt   
##  Min.   : 0.00   Min.   :     0.0   Min.   :    0   Min.   :1196  
##  1st Qu.: 1.00   1st Qu.:   177.0   1st Qu.:   93   1st Qu.:1940  
##  Median : 2.00   Median :   440.0   Median :  126   Median :1970  
##  Mean   : 1.61   Mean   :   558.4   Mean   :  152   Mean   :1965  
##  3rd Qu.: 2.00   3rd Qu.:   651.0   3rd Qu.:  174   3rd Qu.:1999  
##  Max.   :10.00   Max.   :433014.0   Max.   :44515   Max.   :2018  
##  NA's   :62                         NA's   :6450    NA's   :5375  
##  CouncilArea          Lattitude        Longtitude     Regionname       
##  Length:13580       Min.   :-38.18   Min.   :144.4   Length:13580      
##  Class :character   1st Qu.:-37.86   1st Qu.:144.9   Class :character  
##  Mode  :character   Median :-37.80   Median :145.0   Mode  :character  
##                     Mean   :-37.81   Mean   :145.0                     
##                     3rd Qu.:-37.76   3rd Qu.:145.1                     
##                     Max.   :-37.41   Max.   :145.5                     
##                                                                        
##  Propertycount  
##  Min.   :  249  
##  1st Qu.: 4380  
##  Median : 6555  
##  Mean   : 7454  
##  3rd Qu.:10331  
##  Max.   :21650  
## 
str(df)
## 'data.frame':    13580 obs. of  21 variables:
##  $ Suburb       : chr  "Abbotsford" "Abbotsford" "Abbotsford" "Abbotsford" ...
##  $ Address      : chr  "85 Turner St" "25 Bloomburg St" "5 Charles St" "40 Federation La" ...
##  $ Rooms        : int  2 2 3 3 4 2 3 2 1 2 ...
##  $ Type         : chr  "h" "h" "h" "h" ...
##  $ Price        : num  1480000 1035000 1465000 850000 1600000 ...
##  $ Method       : chr  "S" "S" "SP" "PI" ...
##  $ SellerG      : chr  "Biggin" "Biggin" "Biggin" "Biggin" ...
##  $ Date         : chr  "3/12/2016" "4/02/2016" "4/03/2017" "4/03/2017" ...
##  $ Distance     : num  2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 ...
##  $ Postcode     : num  3067 3067 3067 3067 3067 ...
##  $ Bedroom2     : num  2 2 3 3 3 2 4 2 1 3 ...
##  $ Bathroom     : num  1 1 2 2 1 1 2 1 1 1 ...
##  $ Car          : num  1 0 0 1 2 0 0 2 1 2 ...
##  $ Landsize     : num  202 156 134 94 120 181 245 256 0 220 ...
##  $ BuildingArea : num  NA 79 150 NA 142 NA 210 107 NA 75 ...
##  $ YearBuilt    : num  NA 1900 1900 NA 2014 ...
##  $ CouncilArea  : chr  "Yarra" "Yarra" "Yarra" "Yarra" ...
##  $ Lattitude    : num  -37.8 -37.8 -37.8 -37.8 -37.8 ...
##  $ Longtitude   : num  145 145 145 145 145 ...
##  $ Regionname   : chr  "Northern Metropolitan" "Northern Metropolitan" "Northern Metropolitan" "Northern Metropolitan" ...
##  $ Propertycount: num  4019 4019 4019 4019 4019 ...
df <- na.omit(df)

Arbol de decision

arbol <- rpart(Price ~ Rooms + Distance + Bedroom2 + Bathroom + Car + Landsize + BuildingArea + YearBuilt + Propertycount, data=df)
plot(arbol, uniform=TRUE)
text(arbol, cex=.5)

predict(arbol,head(df))
##       2       3       5       7       8      10 
## 1095996 1562641 1070605 2422140 1095996 1095996
head(df$Price)
## [1] 1035000 1465000 1600000 1876000 1636000 1097000
prueba_arbol <- head(df)


mae_arbol <- mae(arbol, prueba_arbol)

Bosque aleatorio

set.seed(123)
renglones_entrenamiento <- createDataPartition(df$Price, p=0.8, list=FALSE)
entrenamiento <- df[renglones_entrenamiento, ]
prueba <- df[-renglones_entrenamiento, ]

rf <- randomForest(Price ~ Rooms + Distance + Bedroom2 + Bathroom + Car + Landsize + BuildingArea + YearBuilt + Propertycount, data= entrenamiento, ntree=500, mtry=3, importance=TRUE)

resultado_entrenamiento <- predict(rf,entrenamiento)
resultado_prueba <- predict(rf,prueba)

mae_rf <- mae(rf,prueba)

resultados <- tibble(Modelo = c("Arbol de decision", "Bosque de aleatorio"), MAE=c(mae_arbol, mae_rf))
resultados
## # A tibble: 2 × 2
##   Modelo                  MAE
##   <chr>                 <dbl>
## 1 Arbol de decision   295863.
## 2 Bosque de aleatorio 213019.

Ejercicio 1. Rendimineto automotriz

Importar la base de datos

df2 <- mtcars 

Entender la base de datos

glimpse(df2)
## Rows: 32
## Columns: 11
## $ mpg  <dbl> 21.0, 21.0, 22.8, 21.4, 18.7, 18.1, 14.3, 24.4, 22.8, 19.2, 17.8,…
## $ cyl  <dbl> 6, 6, 4, 6, 8, 6, 8, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 8,…
## $ disp <dbl> 160.0, 160.0, 108.0, 258.0, 360.0, 225.0, 360.0, 146.7, 140.8, 16…
## $ hp   <dbl> 110, 110, 93, 110, 175, 105, 245, 62, 95, 123, 123, 180, 180, 180…
## $ drat <dbl> 3.90, 3.90, 3.85, 3.08, 3.15, 2.76, 3.21, 3.69, 3.92, 3.92, 3.92,…
## $ wt   <dbl> 2.620, 2.875, 2.320, 3.215, 3.440, 3.460, 3.570, 3.190, 3.150, 3.…
## $ qsec <dbl> 16.46, 17.02, 18.61, 19.44, 17.02, 20.22, 15.84, 20.00, 22.90, 18…
## $ vs   <dbl> 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,…
## $ am   <dbl> 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,…
## $ gear <dbl> 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 4, 3, 3,…
## $ carb <dbl> 4, 4, 1, 1, 2, 1, 4, 2, 2, 4, 4, 3, 3, 3, 4, 4, 4, 1, 2, 1, 1, 2,…
summary(df2)
##       mpg             cyl             disp             hp       
##  Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0  
##  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5  
##  Median :19.20   Median :6.000   Median :196.3   Median :123.0  
##  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7  
##  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0  
##  Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0  
##       drat             wt             qsec             vs        
##  Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000  
##  1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000  
##  Median :3.695   Median :3.325   Median :17.71   Median :0.0000  
##  Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375  
##  3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000  
##  Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000  
##        am              gear            carb      
##  Min.   :0.0000   Min.   :3.000   Min.   :1.000  
##  1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
##  Median :0.0000   Median :4.000   Median :2.000  
##  Mean   :0.4062   Mean   :3.688   Mean   :2.812  
##  3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :1.0000   Max.   :5.000   Max.   :8.000

## Preparación de datos

datos <- df2 %>%
  as_tibble(rownames = "modelo") %>%
  mutate(cyl = as.factor(cyl),
         gear = as.factor(gear),
         carb = as.factor(carb),
         vs   = as.factor(vs),
         am   = as.factor(am))

part <- resample_partition(datos, c(train = 0.7, test = 0.3))
train <- as_tibble(part$train)
test  <- as_tibble(part$test)

form <- mpg ~ cyl + disp + hp + drat + wt + qsec + vs + am + gear + carb

## Árbol de decisión

arbol <- rpart(
  form, data = train, method = "anova",
  control = rpart.control(
    cp = 0.0001,
    maxdepth = 30,
    minsplit = 2,
    minbucket = 1,
    usesurrogate = 2,
    xval = 0
  )
)

rpart.plot(arbol, main = "Árbol de decisión complejo")

pred_arbol <- predict(arbol, newdata = test)
mae_arbol  <- mean(abs(pred_arbol - test$mpg))
mae_arbol
## [1] 2.875

## Bosque aleatorio

set.seed(123)

p <- length(all.vars(update(form, . ~ .)))-1

rf <- randomForest(
  form, data = train,
  ntree = 5000,
  mtry = max(2, floor(sqrt(p))*2),
  nodesize = 1,
  sampsize = nrow(train),
  replace = TRUE,
  importance = TRUE,
  keep.inbag = TRUE
)

pred_rf <- predict(rf, newdata = test)
mae_rf  <- mean(abs(pred_rf - test$mpg))
mae_rf
## [1] 1.842486
imp <- as_tibble(importance(rf), rownames = "Variable") |>
  arrange(desc(IncNodePurity))
imp
## # A tibble: 10 × 3
##    Variable `%IncMSE` IncNodePurity
##    <chr>        <dbl>         <dbl>
##  1 hp           43.0         237.  
##  2 wt           39.4         192.  
##  3 disp         34.4         186.  
##  4 cyl          24.0          91.2 
##  5 drat         12.1          43.4 
##  6 qsec          7.15         22.3 
##  7 carb         17.0          21.1 
##  8 gear          5.57          8.74
##  9 am            6.23          5.12
## 10 vs            3.87          2.47

## Comparación de modelos (MAE)

resultados <- tibble(
  Modelo = c("Árbol de decisión (complejo)", "Bosque aleatorio (complejo)"),
  MAE    = c(mae_arbol, mae_rf)
)
resultados |> arrange(MAE)
## # A tibble: 2 × 2
##   Modelo                         MAE
##   <chr>                        <dbl>
## 1 Bosque aleatorio (complejo)   1.84
## 2 Árbol de decisión (complejo)  2.87
LS0tDQp0aXRsZTogIkJvc3F1ZSBhbGVhdG9yaW8iDQphdXRob3I6ICJBbnRvbmlvIEPDoXJkZW5hcyBEdWFydGUgLSBBMDE1Njg5MDgiDQpkYXRlOiAiMjAyNS0wOC0yNyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IFRSVUUgI1RhYmxhIGRlIGNvbnRlbmlkb3MNCiAgICB0b2NfZmxvYXQ6IFRSVUUgI1RibGEgZGUgYWxnbw0KICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUgI1BvZGVyIGRlc2NhcmdhciBjw7NkaWdvDQogICAgdGhlbWU6IGpvdXJuYWwNCi0tLQ0KIVtdKGh0dHBzOi8vd3d3LmZyZWVjb2RlY2FtcC5vcmcvZXNwYW5vbC9uZXdzL2NvbnRlbnQvaW1hZ2VzLzIwMjEvMDEvcmFuZG9tX3RyZWUuanBlZykNCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJyb3duOyI+IFRlb3LDrWEgPC9zcGFuPg0KRWwgKipCb3NxdWUgYWxlYXRvcmlvKiogZXMgdW4gYWxnb3JpdG1vIGRlIGFwcmVuZGl6YWplIHF1ZSBjb21iaW5hIGVsIHJlc3VsdGFkbyBkZSBtw7psdGlwbGVzIGFyYm9sZXMgZGUgZGVjaXNpb24gcGFyYSBsbGVnYXIgYSB1biByZXN1bHRhZG8gb3B0aW1vLg0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYnJvd247Ij4gRWplbXBsbyAxLiBNZWxib3VybmUgPC9zcGFuPg0KRW4gZXN0YSBiYXNlIGRlIGRhdG9zIHRlbmVtb3MgbG9zIHByZWNpb3MgZGUgbWFzIGRlIDEzLDAwMCBjYXNhcyBkZSBsYSBjaXVkYWQgZGUgTWVsYm91cm5lOg0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJyb3duOyI+IEluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcsOtYXMgPC9zcGFuPg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygicnBhcnQiKQ0KbGlicmFyeShycGFydCkNCiNpbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShtb2RlbHIpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoY2FyZXQpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBicm93bjsiPiBJbXBvcnRhciBsYSBiYXNlIGRlIGRhdG9zIDwvc3Bhbj4NCmBgYHtyfQ0KZGYgPC0gcmVhZC5jc3YoIkQ6XFxBbnRvbiBWMlxcRG93bmxvYWRzXFxtZWxib3VybmUuY3N2IikNCmBgYA0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBicm93bjsiPiBFbnRlbmRlciBsYSBiYXNlIGRlIGRhdG9zIDwvc3Bhbj4NCg0KYGBge3J9DQpzdW1tYXJ5KGRmKQ0Kc3RyKGRmKQ0KZGYgPC0gbmEub21pdChkZikNCmBgYA0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiBicm93bjsiPiBBcmJvbCBkZSBkZWNpc2lvbiA8L3NwYW4+DQpgYGB7cn0NCmFyYm9sIDwtIHJwYXJ0KFByaWNlIH4gUm9vbXMgKyBEaXN0YW5jZSArIEJlZHJvb20yICsgQmF0aHJvb20gKyBDYXIgKyBMYW5kc2l6ZSArIEJ1aWxkaW5nQXJlYSArIFllYXJCdWlsdCArIFByb3BlcnR5Y291bnQsIGRhdGE9ZGYpDQpwbG90KGFyYm9sLCB1bmlmb3JtPVRSVUUpDQp0ZXh0KGFyYm9sLCBjZXg9LjUpDQoNCnByZWRpY3QoYXJib2wsaGVhZChkZikpDQpoZWFkKGRmJFByaWNlKQ0KcHJ1ZWJhX2FyYm9sIDwtIGhlYWQoZGYpDQoNCg0KbWFlX2FyYm9sIDwtIG1hZShhcmJvbCwgcHJ1ZWJhX2FyYm9sKQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYnJvd247Ij4gQm9zcXVlIGFsZWF0b3JpbyA8L3NwYW4+DQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCnJlbmdsb25lc19lbnRyZW5hbWllbnRvIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZGYkUHJpY2UsIHA9MC44LCBsaXN0PUZBTFNFKQ0KZW50cmVuYW1pZW50byA8LSBkZltyZW5nbG9uZXNfZW50cmVuYW1pZW50bywgXQ0KcHJ1ZWJhIDwtIGRmWy1yZW5nbG9uZXNfZW50cmVuYW1pZW50bywgXQ0KDQpyZiA8LSByYW5kb21Gb3Jlc3QoUHJpY2UgfiBSb29tcyArIERpc3RhbmNlICsgQmVkcm9vbTIgKyBCYXRocm9vbSArIENhciArIExhbmRzaXplICsgQnVpbGRpbmdBcmVhICsgWWVhckJ1aWx0ICsgUHJvcGVydHljb3VudCwgZGF0YT0gZW50cmVuYW1pZW50bywgbnRyZWU9NTAwLCBtdHJ5PTMsIGltcG9ydGFuY2U9VFJVRSkNCg0KcmVzdWx0YWRvX2VudHJlbmFtaWVudG8gPC0gcHJlZGljdChyZixlbnRyZW5hbWllbnRvKQ0KcmVzdWx0YWRvX3BydWViYSA8LSBwcmVkaWN0KHJmLHBydWViYSkNCg0KbWFlX3JmIDwtIG1hZShyZixwcnVlYmEpDQoNCnJlc3VsdGFkb3MgPC0gdGliYmxlKE1vZGVsbyA9IGMoIkFyYm9sIGRlIGRlY2lzaW9uIiwgIkJvc3F1ZSBkZSBhbGVhdG9yaW8iKSwgTUFFPWMobWFlX2FyYm9sLCBtYWVfcmYpKQ0KcmVzdWx0YWRvcw0KYGBgDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBicm93bjsiPiBFamVyY2ljaW8gMS4gUmVuZGltaW5ldG8gYXV0b21vdHJpeiA8L3NwYW4+DQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYnJvd247Ij4gSW1wb3J0YXIgbGEgYmFzZSBkZSBkYXRvcyA8L3NwYW4+DQpgYGB7cn0NCmRmMiA8LSBtdGNhcnMgDQpgYGANCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogYnJvd247Ij4gRW50ZW5kZXIgbGEgYmFzZSBkZSBkYXRvcyA8L3NwYW4+DQpgYGB7cn0NCmdsaW1wc2UoZGYyKQ0Kc3VtbWFyeShkZjIpDQpgYGANCg0KIyM8c3BhbiBzdHlsZT0iY29sb3I6IGJyb3duOyI+IFByZXBhcmFjacOzbiBkZSBkYXRvcyA8L3NwYW4+DQpgYGB7cn0NCmRhdG9zIDwtIGRmMiAlPiUNCiAgYXNfdGliYmxlKHJvd25hbWVzID0gIm1vZGVsbyIpICU+JQ0KICBtdXRhdGUoY3lsID0gYXMuZmFjdG9yKGN5bCksDQogICAgICAgICBnZWFyID0gYXMuZmFjdG9yKGdlYXIpLA0KICAgICAgICAgY2FyYiA9IGFzLmZhY3RvcihjYXJiKSwNCiAgICAgICAgIHZzICAgPSBhcy5mYWN0b3IodnMpLA0KICAgICAgICAgYW0gICA9IGFzLmZhY3RvcihhbSkpDQoNCnBhcnQgPC0gcmVzYW1wbGVfcGFydGl0aW9uKGRhdG9zLCBjKHRyYWluID0gMC43LCB0ZXN0ID0gMC4zKSkNCnRyYWluIDwtIGFzX3RpYmJsZShwYXJ0JHRyYWluKQ0KdGVzdCAgPC0gYXNfdGliYmxlKHBhcnQkdGVzdCkNCg0KZm9ybSA8LSBtcGcgfiBjeWwgKyBkaXNwICsgaHAgKyBkcmF0ICsgd3QgKyBxc2VjICsgdnMgKyBhbSArIGdlYXIgKyBjYXJiDQoNCmBgYA0KDQojIzxzcGFuIHN0eWxlPSJjb2xvcjogYnJvd247Ij4gw4FyYm9sIGRlIGRlY2lzacOzbiA8L3NwYW4+DQpgYGB7cn0NCmFyYm9sIDwtIHJwYXJ0KA0KICBmb3JtLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJhbm92YSIsDQogIGNvbnRyb2wgPSBycGFydC5jb250cm9sKA0KICAgIGNwID0gMC4wMDAxLA0KICAgIG1heGRlcHRoID0gMzAsDQogICAgbWluc3BsaXQgPSAyLA0KICAgIG1pbmJ1Y2tldCA9IDEsDQogICAgdXNlc3Vycm9nYXRlID0gMiwNCiAgICB4dmFsID0gMA0KICApDQopDQoNCnJwYXJ0LnBsb3QoYXJib2wsIG1haW4gPSAiw4FyYm9sIGRlIGRlY2lzacOzbiBjb21wbGVqbyIpDQoNCnByZWRfYXJib2wgPC0gcHJlZGljdChhcmJvbCwgbmV3ZGF0YSA9IHRlc3QpDQptYWVfYXJib2wgIDwtIG1lYW4oYWJzKHByZWRfYXJib2wgLSB0ZXN0JG1wZykpDQptYWVfYXJib2wNCg0KDQpgYGANCg0KIyM8c3BhbiBzdHlsZT0iY29sb3I6IGJyb3duOyI+IEJvc3F1ZSBhbGVhdG9yaW8gPC9zcGFuPg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQoNCnAgPC0gbGVuZ3RoKGFsbC52YXJzKHVwZGF0ZShmb3JtLCAuIH4gLikpKS0xDQoNCnJmIDwtIHJhbmRvbUZvcmVzdCgNCiAgZm9ybSwgZGF0YSA9IHRyYWluLA0KICBudHJlZSA9IDUwMDAsDQogIG10cnkgPSBtYXgoMiwgZmxvb3Ioc3FydChwKSkqMiksDQogIG5vZGVzaXplID0gMSwNCiAgc2FtcHNpemUgPSBucm93KHRyYWluKSwNCiAgcmVwbGFjZSA9IFRSVUUsDQogIGltcG9ydGFuY2UgPSBUUlVFLA0KICBrZWVwLmluYmFnID0gVFJVRQ0KKQ0KDQpwcmVkX3JmIDwtIHByZWRpY3QocmYsIG5ld2RhdGEgPSB0ZXN0KQ0KbWFlX3JmICA8LSBtZWFuKGFicyhwcmVkX3JmIC0gdGVzdCRtcGcpKQ0KbWFlX3JmDQoNCmltcCA8LSBhc190aWJibGUoaW1wb3J0YW5jZShyZiksIHJvd25hbWVzID0gIlZhcmlhYmxlIikgfD4NCiAgYXJyYW5nZShkZXNjKEluY05vZGVQdXJpdHkpKQ0KaW1wDQoNCg0KYGBgDQojIzxzcGFuIHN0eWxlPSJjb2xvcjogYnJvd247Ij4gQ29tcGFyYWNpw7NuIGRlIG1vZGVsb3MgKE1BRSkgPC9zcGFuPg0KYGBge3J9DQpyZXN1bHRhZG9zIDwtIHRpYmJsZSgNCiAgTW9kZWxvID0gYygiw4FyYm9sIGRlIGRlY2lzacOzbiAoY29tcGxlam8pIiwgIkJvc3F1ZSBhbGVhdG9yaW8gKGNvbXBsZWpvKSIpLA0KICBNQUUgICAgPSBjKG1hZV9hcmJvbCwgbWFlX3JmKQ0KKQ0KcmVzdWx0YWRvcyB8PiBhcnJhbmdlKE1BRSkNCg0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==