1: Carga de datos

1.A: Lectura y analisis de estructura del archivo

Procedemos a cargar el archivo de entrada y verificar la cantidad de variables y casos. Para ello tenemos que descomprimir el archivo antes de cargalo, y tabmien especificar el encoding correcto (UTF-8) para la carga correcta de caracteres especiales.

Debido a que la carga de los ids cuesta mucho mas memoria que todo el resto y no proporciona informacion de utilidad para el analisis se descarta de entrada.

# carga librerias tidyverse y ggplot2
suppressPackageStartupMessages(library(tidyverse))
suppressPackageStartupMessages(library(ggplot2))

#carga de datos
ar_properties <- read.csv(unz('ar_properties.zip', 'ar_properties.csv'), row.names = NULL, stringsAsFactors = TRUE, encoding = "UTF-8") %>% select( -c('id') )

#verificaion de casos y variables
head(ar_properties)
nrow(ar_properties)
[1] 388891

1.B: Preprocesado y filtrado

Cargamos la libreria tidyverse para realizar los filtrados, y procedemos a extraer los casos de interes:

# Filtrado de los casos deseados
ar_properties <- ar_properties %>% filter(
    l1             == 'Argentina' &
    l2             == 'Capital Federal' &
    currency       == 'USD' &
    operation_type == 'Venta' &
    property_type  %in% c('Casa', 'Departamento', 'PH'))

1.C: Preprocesado y filtrado

Una vez filtrados los casos, seleccionamos las variables deseadas y reajustamos los niveles de las variables de categoria (descartamos las categorias que ya no figuran en nuestros datos). Verificamos nuevamente la cantidad de registros y variables que quedan

# Seleccion de las variables de interes
ar_properties <- ar_properties %>% select(
  l3,
  rooms,
  bedrooms,
  bathrooms,
  surface_total,
  surface_covered,
  price,
  property_type )

# eliminacion de categorias en desuso
ar_properties <- droplevels(ar_properties)

#verificacion de casos y variables
nrow(ar_properties)
[1] 61905
ncol(ar_properties)
[1] 8

2: Analisis exploratorio (I)

2.A: Analisis de valores distintos y faltantes

calculamos la cantidad de valores distintos y faltantes, y los analizamos:

# calculamos la cantidad de valores distintos
uniques.values <- apply(ar_properties, 2, n_distinct)
as.data.frame(uniques.values)

# calculamos la cantidad de valores faltantes
missing.values <- colSums(is.na(ar_properties))
as.data.frame(missing.values)

Curiosamente, notamos que para el campo l3 que corresponde al barrio porteño, hay 57 valores distintos (58 si tomamos en cuenta NA). Esto se contradice con los 48 barrios porteños existentes, por lo que analizamos los barrios inesperados:

barrios.portenos.officiales <- c("Agronomía", "Almagro", "Balvanera", "Barracas", "Belgrano", "Boedo", "Caballito", "Chacarita", "Coghlan", "Colegiales", "Constitución", "Flores", "Floresta", "Boca", "Paternal", "Liniers", "Mataderos", "Monserrat", "Monte Castro", "Pompeya", "Nuñez", "Palermo", "Parque Avellaneda", "Parque Chacabuco", "Parque Chas", "Parque Patricios", "Puerto Madero", "Recoleta", "Retiro", "Saavedra", "San Cristobal", "San Nicolás", "San Telmo", "Velez Sarsfield", "Versalles", "Villa Crespo", "Villa del Parque", "Villa Devoto", "Villa General Mitre", "Villa Lugano", "Villa Luro", "Villa Ortuzar", "Villa Pueyrredón", "Villa Real", "Villa Riachuelo", "Villa Santa Rita", "Villa Soldati", "Villa Urquiza")
sort(setdiff(unique(ar_properties$l3), barrios.portenos.officiales))
[1] "Abasto"               "Barrio Norte"         "Catalinas"            "Centro / Microcentro"
[5] "Congreso"             "Las Cañitas"          "Once"                 "Parque Centenario"   
[9] "Tribunales"          

Observamos como los barrios ‘extra’ corresponden a denominaciones no-oficiales de ciertas regions de la CABA.

2.B: Matriz de correlacion

Para el calculo de la matriz de correlacion, necesitamos poder extraer las variables del tipo numerico. Dado que esta funcion sera utilizada en pasos posteriores, será de utilidad crear una funcion que permita automatizar esta funcion; y ademas agregamos la opcion para seleccionar columnas especificas en lugar de todas las numericas. Luego procedemos a calcular la correlacion de las variables numericas.

# funcion para obtener las columnas especificadas, o las numericas en caso de no especificarse
get.numeric.columns <- function(df, columns.to.get=NULL) {
    if (is.null(columns.to.get))
        columns.to.get <- colnames(df)[unlist(lapply(df, is.numeric))]

    return(columns.to.get)
}

# calculamos la correlacion de las variables numericas, descartando los casos incompletos
cor(ar_properties[,get.numeric.columns(ar_properties)], use="complete.obs", method="pearson")
                     rooms   bedrooms  bathrooms surface_total surface_covered      price
rooms           1.00000000 0.92138719 0.61335026    0.06828238      0.07468335 0.48748747
bedrooms        0.92138719 1.00000000 0.61578024    0.06746895      0.07206826 0.43221753
bathrooms       0.61335026 0.61578024 1.00000000    0.06234262      0.06777010 0.59904254
surface_total   0.06828238 0.06746895 0.06234262    1.00000000      0.69656225 0.05095265
surface_covered 0.07468335 0.07206826 0.06777010    0.69656225      1.00000000 0.06257960
price           0.48748747 0.43221753 0.59904254    0.05095265      0.06257960 1.00000000

Notamos como la correlacion mas alta (0.921) se da entre rooms y bedrooms (lo cual tiene sentido, siendo que la relacion tiende a ser del tipo rooms = bedrooms + 1 en la mayoria de los departamentos, correspondiendo el ‘+1’ al living ). Este dato nos permitira decidir en un paso posterior a decidir como tratar los casos faltantes. Tambien notamos otra correlacion alta entre superficie total y superficie cubierta, también esperada, ya que los departamentos / casas / PHs mas grandes tienden a tener patios / balcones o jardines tambien mas grandes. El otro caso de alta correlacion se da entre baños y ambientes, tambien siendo una relacion esperable, de naturaleza semejante a la descrita previamente (las viviendas con mas ambientes tienden a tener mayor cantidad de baños) Lo ultimo a destacar, es que el precio no parece estar tan correlacionado con la superficie (lo cual se presenta como inusual, ya que es común que uno de los factores determinantes del precio de una propiedad es la cantidad de metros cuadrados). Este aspecto se analizará luego con mas detalle.

3: Preparacion de datos

3.A: Eliminacion de variables altamente correlacionadas

Dado que las variables rooms y bedrooms presentan alta correlacion, procedemos a eliminar una de ellas, la de mayor cantaidad de faltantes (bedrooms)

# eliminacion de la variable `bedrooms`
ar_properties <- ar_properties %>% select( -c('bedrooms') )

3.B: Filtrado de los casos incompletos

Filtramos los casos incompletos y validamos la cantidad de variables y casos

# eliminacion de la variable `bedrooms`
ar_properties <- ar_properties[complete.cases(ar_properties),]

#Validamos la cantidad de casos y variables
nrow(ar_properties)
[1] 51210
ncol(ar_properties)
[1] 7

4: Analisis exploratorio (II)

4.A: Obtencion de estadisticas generales

Obtenemos estadisticas de la variable precio. Generamos funciones que reutilizaremos mas tarde.

# Funcion para generar estadisticas con la media incluida
summary.with.mean <- function(data)
{
    summary <- summary(data)
    summary['mean'] = mean(data)
    return( summary )
}

# Caluclamos las estadisticas
summary.with.mean(ar_properties$price)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    mean 
   6000  119000  170000  251577  270000 6000000  251577 
# Y realizamos un histograma
ggplot(ar_properties, aes(x = price)) + geom_histogram()

Notamos que hay un gran rango de precios, y que la mayoria de los valores se concentran en los valores mas bajos. Por ese motivo, repetimos el histograma pero utiliando una escala logaritmica para poder observar mejor la distribucion.

ggplot(ar_properties, aes(x = price)) + geom_histogram(alpha=0.5, position="identity", aes(y = ..density..)) + scale_x_log10() + geom_density(alpha=0.5)

Notamos que la distribucion final de precios parece ser bimodal, que podria significar la presencia de poblaciones distintas dentro de la muestra. Tambien se observa que la distribucion (observando la escala logaritmica) presenta una asimetria, con cola pesada a derecha.

4.B: Obtencion de estadisticas por tipo de propiedad

Realizamos el mismo analisis anterior, pero segmentado por tipo de propiedad.

# Calculamos las estadisticas, discriminando por tipo de propiedad
tapply(ar_properties$price, ar_properties$property_type, summary.with.mean)
$Casa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    mean 
  20000  235000  335000  434189  490000 5000000  434189 

$Departamento
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    mean 
   6000  115000  164000  246856  260000 6000000  246856 

$PH
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    mean 
  32000  137000  190000  218747  270000 1500000  218747 
# Realizamos el histograma, discriminando por tipo de propiedad
ggplot(ar_properties, aes(x=price, fill=property_type)) +
    geom_histogram(alpha=0.5, position="identity", aes(y = ..density..)) +
    geom_density(alpha=0.5) +
    scale_x_log10()

Al discriminar por tipo de casa, se detecta que la ‘bimodalidad’ observada antes no es solamente producto de diferentes tipos de propiedades, ya que tanto PHs como departamentos presentan un mismo “valle” alrededor de los $200.000 dolares. Tambien se puede observar que si bien se notan ciertas asimetrias en las distribuciones, individualmente no parecen ser tan acentuadas como cuando se combinan todas las poblaciones (o sea, la combinacion de las distintas dsitribuciones que estan centradas en distintos puntos acentua la asimetria de distribucion combinada)

4.C: Boxplots

Graficamos los boxplots del precio por cada tipo de propiedad. Siguiendo la misma logica, escalamos el eje de precios logaritmicamente.

# Graficamos los boxplots
ggplot(ar_properties, aes(x=property_type, y=price, fill=property_type)) +
    geom_boxplot() +
    scale_y_log10()

Observando los boxplots, se observa claramente como los precios tienen una tendencia del tipo Casa > PH > depto (que ya podia ser apreciada en los histogramas previos, pero aun mas claro aca). Tambien se observa como los departamentos parecen tener la mayor dispersion de precios, al igual que mayor cantidad de outliers.

4.D: Correlograma

suppressPackageStartupMessages(library(GGally))

# Graficamos el correlograma
ggcorr(ar_properties[,get.numeric.columns(ar_properties)], method = c("everything", "pearson")) 

Las conclusiones que se pueden obtener de este grafico son las mismas que las observadas en el punto 2.B.

5: Outliers

Analizamos como se componen los precios en los extremos superiores e inferiores de la distribucion

# Cortes de precios para los cuantiles en los extremos
quant.inf <- quantile(ar_properties$price, probs=seq(0, 0.005, length.out = 6) )
quant.sup <- quantile(ar_properties$price, probs=seq(.995, 1, length.out = 6) )

quant.inf
   0%  0.1%  0.2%  0.3%  0.4%  0.5% 
 6000 35000 45000 49900 53000 55000 
quant.sup
  99.5%   99.6%   99.7%   99.8%   99.9%    100% 
1979550 2300000 2500000 2879100 3500000 6000000 

Analizando los resultados de zonaProp, la propiedad mas barata en venta en capital federal esta por sobre 50.000 dolares. Combinando este dato con la informacion de los cuartiles obtenida previamente, podemos asegurar que descartar los valores inferiores a U$S 50.000 seria descartar menos del 0.5% de las muestras. En cuanto a valores superiores, se prosiguió a descartar el mismo porcentage de corte que para las muestras inferiores.

Tambien se procede a recortar usando los mismos porcentajes en las dimensiones de rooms y de surface_total, ya que son las variables que estaremos modelando a continuacion, y calculamos la cantidad de muestras filtradas (notar que debido a que la eliminacion de los extremos se hizo de forma simultanea, la cantidad total de muestras puede no coincidir al 3% ya que es probable que los casos que poseen valores extremos de una dimension concuerden con los extremos de otra de las dimensiones)

ar_properties.sin.outliers = ar_properties %>% filter(
    price >= quantile(ar_properties$price, 0.005) &
    price <= quantile(ar_properties$price, 0.995) &
    rooms >= quantile(ar_properties$rooms, 0.005) &
    rooms <= quantile(ar_properties$rooms, 0.995) &
    surface_total <= quantile(ar_properties$surface_total, 0.995) &
    surface_total <= quantile(ar_properties$surface_total, 0.995) )

porcentaje=nrow(ar_properties.sin.outliers)/nrow(ar_properties)

reporte.filtrado <- function( filtrado, original )
{
  df <- data.frame( original=nrow(original), filtrado=nrow(filtrado), porcentaje=(nrow(filtrado)/nrow(original)*100) )
  return(df)
}

reporte.filtrado( ar_properties.sin.outliers, ar_properties )

Como caso de interes, tambien filtramos los outliers utilizando la tecnica de isolation tree, utilizando las variables de interes y filtrando una cantidad demejante de muestras, pero utilizando como criterio su puntaje de anomalia. Para ser consistentes con las comparaciones, eliminaremos la misma cantidad de muestras.

# Cargamos la libreria necesaria
suppressPackageStartupMessages(library("solitude"))

# iniciamos el algoritmo utilizando solamente las variables que vamos a correlacionar
iso <- solitude::isolationForest$new()
iso$fit(ar_properties %>% select( c("rooms", "price", "surface_total") ) )
Building Isolation Forest ... done
Computing depth of terminal nodes ... 
done
ar_properties.sin.outliers.isolationtree <- ar_properties

# Usamos los resultados para filtras las muestras mas anomalas, y luego descartamos esta columna
ar_properties.sin.outliers.isolationtree$anomalyScore = iso$scores$anomaly_score
ar_properties.isolationtree <- ar_properties.sin.outliers.isolationtree #backup de variable
ar_properties.sin.outliers.isolationtree <- ar_properties.sin.outliers.isolationtree %>% filter( anomalyScore <= quantile(ar_properties.sin.outliers.isolationtree$anomalyScore, porcentaje) ) %>% select( -c("anomalyScore") )

#verificamos que la cantidad eliminada sea semejante en ambos casos
reporte.filtrado( ar_properties.sin.outliers.isolationtree, ar_properties )

Finalmente, tambien tomamos otro caso de interes, un filtrado aun mas ingenuo que el primero, en el que solo recortamos los valores extremos pero unicamente de la dimension precio, tomando precauciones para que el tamaño de las muestras sea comparable.

ar_properties.sin.outliers.ingenuo = ar_properties %>% filter(
    price >= quantile(ar_properties$price, (1-porcentaje)/2) &
    price <= quantile(ar_properties$price, 1-(1-porcentaje)/2) )
      
reporte.filtrado( ar_properties.sin.outliers.ingenuo, ar_properties )

6: Analisis exploratorios (III)

# Calculamos las estadisticas, discriminando por tipo de propiedad
tapply(ar_properties.sin.outliers$price, ar_properties.sin.outliers$property_type, summary.with.mean)
$Casa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    mean 
  55000  229000  320000  367484  450000 1900000  367484 

$Departamento
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    mean 
  55000  115000  163590  233262  260000 1970000  233262 

$PH
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    mean 
  55000  136000  190000  216242  270000  970000  216242 
# Realizamos el histograma, discriminando por tipo de propiedad
ggplot(ar_properties.sin.outliers, aes(x=price, fill=property_type)) +
    geom_histogram(alpha=0.5, position="identity", aes(y = ..density..)) +
    geom_density(alpha=0.5) +
    scale_x_log10()


# Graficamos los boxplots
ggplot(ar_properties.sin.outliers, aes(x=property_type, y=price, fill=property_type)) +
    geom_boxplot() +
    scale_y_log10()


# Graficamos el correlograma
ggcorr(ar_properties.sin.outliers[,get.numeric.columns(ar_properties.sin.outliers)], method = c("everything", "pearson"))

Se puede observar (con especial detalle en los boxplots) como gran cantidad de los outliers desaparecieron. Cabe destacar que esta es una tecnica un poco ingenua para la eliminacion de los mismos, ya que no considera la posibilidad de outliers debido a la relacion de las variables entre sí, y solamente descarta los extremos en cada dimension. Aun asi, obervamos como aumento la correlacion entre varios pares de variables que previamente estaban debilmente correlacionadas (precio-area total, area total-habitaciones, baños-supreficie total)

7: Modelo lineal

7.A/B: Generacion del modelo lineal / descripcion

auto.lm <- function( description, data, xVar )
{
  lm.result <- lm(data$price~data[[xVar]])
  sum <- summary( lm.result )
  print( description )
  print( sum )
  return( data.frame(
    descripcion=description,
    intercept=sum$coefficients[1],
    coeff=sum$coefficients[2],
    rsquaredAdj=sum$adj.r.squared ) )
}


result <- auto.lm('con outliers vs superficie', ar_properties, 'surface_total')
[1] "con outliers vs superficie"

Call:
lm(formula = data$price ~ data[[xVar]])

Residuals:
     Min       1Q   Median       3Q      Max 
-2942970  -131900   -81215    19731  5736925 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  2.492e+05  1.301e+03  191.51   <2e-16 ***
data[[xVar]] 2.307e+01  1.586e+00   14.55   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 292200 on 51208 degrees of freedom
Multiple R-squared:  0.004117,  Adjusted R-squared:  0.004097 
F-statistic: 211.7 on 1 and 51208 DF,  p-value: < 2.2e-16
result <- rbind(result, auto.lm('con outliers vs ambientes', ar_properties, 'rooms') )
[1] "con outliers vs ambientes"

Call:
lm(formula = data$price ~ data[[xVar]])

Residuals:
     Min       1Q   Median       3Q      Max 
-2711264   -97662   -32662    37125  5413191 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -41911.5     2520.1  -16.63   <2e-16 ***
data[[xVar]] 104786.7      805.7  130.06   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 253900 on 51208 degrees of freedom
Multiple R-squared:  0.2483,    Adjusted R-squared:  0.2483 
F-statistic: 1.691e+04 on 1 and 51208 DF,  p-value: < 2.2e-16
result <- rbind(result, auto.lm('sin outliers vs superficie', ar_properties.sin.outliers, 'surface_total') )
[1] "sin outliers vs superficie"

Call:
lm(formula = data$price ~ data[[xVar]])

Residuals:
     Min       1Q   Median       3Q      Max 
-1058553   -44173   -16794    20358  1418223 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  38194.434   1097.317   34.81   <2e-16 ***
data[[xVar]]  2260.412      9.945  227.30   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 149300 on 50389 degrees of freedom
Multiple R-squared:  0.5063,    Adjusted R-squared:  0.5062 
F-statistic: 5.167e+04 on 1 and 50389 DF,  p-value: < 2.2e-16
result <- rbind(result, auto.lm('sin outliers vs ambientes', ar_properties.sin.outliers, 'rooms')  )
[1] "sin outliers vs ambientes"

Call:
lm(formula = data$price ~ data[[xVar]])

Residuals:
    Min      1Q  Median      3Q     Max 
-570537  -85007  -25478   36346 1691346 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -20875       1853  -11.27   <2e-16 ***
data[[xVar]]    93177        608  153.24   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 175500 on 50389 degrees of freedom
Multiple R-squared:  0.3179,    Adjusted R-squared:  0.3179 
F-statistic: 2.348e+04 on 1 and 50389 DF,  p-value: < 2.2e-16
result <- rbind(result, auto.lm('sin outliers 1D vs superficie', ar_properties.sin.outliers.ingenuo, 'surface_total') )
[1] "sin outliers 1D vs superficie"

Call:
lm(formula = data$price ~ data[[xVar]])

Residuals:
     Min       1Q   Median       3Q      Max 
-1897106  -115131   -64866    34896  1369166 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  2.336e+05  8.976e+02  260.21   <2e-16 ***
data[[xVar]] 1.490e+01  1.086e+00   13.72   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2e+05 on 50388 degrees of freedom
Multiple R-squared:  0.00372,   Adjusted R-squared:  0.003701 
F-statistic: 188.2 on 1 and 50388 DF,  p-value: < 2.2e-16
result <- rbind(result, auto.lm('sin outliers 1D vs ambientes', ar_properties.sin.outliers.ingenuo, 'rooms') )
[1] "sin outliers 1D vs ambientes"

Call:
lm(formula = data$price ~ data[[xVar]])

Residuals:
     Min       1Q   Median       3Q      Max 
-2078375   -81155   -24055    30013  1352301 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)    1767.5     1666.3   1.061    0.289    
data[[xVar]]  83644.0      536.5 155.913   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 164600 on 50388 degrees of freedom
Multiple R-squared:  0.3254,    Adjusted R-squared:  0.3254 
F-statistic: 2.431e+04 on 1 and 50388 DF,  p-value: < 2.2e-16
result <- rbind(result, auto.lm('sin outliers - isolation tree vs superficie', ar_properties.sin.outliers.isolationtree, 'surface_total') )
[1] "sin outliers - isolation tree vs superficie"

Call:
lm(formula = data$price ~ data[[xVar]])

Residuals:
    Min      1Q  Median      3Q     Max 
-928924  -43626  -16564   20548 1714422 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  39115.047   1075.942   36.35   <2e-16 ***
data[[xVar]]  2234.919      9.748  229.26   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 147500 on 50390 degrees of freedom
Multiple R-squared:  0.5105,    Adjusted R-squared:  0.5105 
F-statistic: 5.256e+04 on 1 and 50390 DF,  p-value: < 2.2e-16
result <- rbind(result, auto.lm('sin outliers - isolation tree vs ambientes', ar_properties.sin.outliers.isolationtree, 'rooms') )
[1] "sin outliers - isolation tree vs ambientes"

Call:
lm(formula = data$price ~ data[[xVar]])

Residuals:
    Min      1Q  Median      3Q     Max 
-627975  -83354  -23760   37348 2055538 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -24051       1828  -13.15   <2e-16 ***
data[[xVar]]    93703        601  155.92   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 173200 on 50390 degrees of freedom
Multiple R-squared:  0.3255,    Adjusted R-squared:  0.3254 
F-statistic: 2.431e+04 on 1 and 50390 DF,  p-value: < 2.2e-16
result
Interpretacion de los parametros

Si comparamos los distintos modelos en funcion del R2 ajustado, interpretando que este parametro es el que indica cuan mejor predictor de precio es el modelo (representando el porcentaje de la variacion explicado, siendo mas alto mejor), observamos que los dos modelos que mejor ajustan son el de precio vs area, en los casos de fitlrados por extremos y filtrado por isolation tree. Tambien observamos los p-valores en todos los casos (excepto el filtrado 1D para el \(\beta_0\)) son siempre muy signifcativos (p<0.001), con lo cual podemos asegurar que existe una dependencia entre el precio y las otras 2 variables (ambientse y superficie) El modelo presenta 2 parametros para la prediccion: intercept (o \(\beta_0\)) = ~39,000, que representaria el valor que estimaria para una propiedad de 0 metros cuadradaos (que igual hay que notar que es un extremo teorico, ya que el modelo ajusta mejor en la zona donde x es igual a la media de las muestras, y en este caso carece de sentido interpretar este parametro de forma independiente), y el coefficiente (\(\beta_1\)) = ~2,200 que seria mejor interpretado como “cuanto varia el precio de una propiedad por cada m2 adicional”. Usando la misma logica, para el modelo que mejor ajusta de precio vs ambientes, se estima que cada ambiente extra varia el precio total en promedio ~ $93,000

7.C: Analisis / conclusiones

El mejor modelo predictor de todos (usando R2 como parametro para comparar modelos) de precio es el model de precio vs superficie del fitlrado mediante isolation tree, lo cual es indicador de que es un metodo mas eficiente de deteccion de outliers, debido a la posibilidad del mismo de detectar extremos en multiples dimensiones en conjunto; aunque la mejora no sea sustancial en comparacion con el metodo de filtrado de extremos en cada dimension.

Tambien podemos notar como si no se eliminan los outliers (o si se eliminana de una forma poco eficiente) la efectividad del modelo resultante puede variar drasticamente, como se puede analizar en los casos con outliers y sin outliers 1D vs superficie; y en algunos casos hasta empeorar (ver con outliers vs superficie en comparacion con sin outliers 1D vs superficie)

Cabe destacar tambien que cuando el modelo se hace contra la variable ambientes, el resultado tiende a ser mas “robusto”, desde el punto de vista que el r2 del mismo no tiende a variar de forma tan extrema como con los otros modelos al cambiar la tecnica de filtrado de outliers. Esto tiene como efecto que, dependiendo del metodo de filtrado, puede cambiar la variable que seria “mejor” predictor del precio.

Para poder visualizar mejor el efecto de los outliers en las diferentes formas de filtrado, se procede a continuacion a graficar el modelo resultando, denotando que casos son eliminados con los diferentes metodos.

auto.plot <- function( title, full.df, filtered.df, yvar='rooms' )
{
  removed <- setdiff(full.df, filtered.df)
  removed$source = 'outliers'
  filtered.df$source = 'normal'
  full.with.source <- rbind( filtered.df, removed )
  ggplot(full.with.source, aes_string(x=yvar, y="price", color="source")) +
     geom_point() +
     geom_smooth(method='lm', data=filtered.df, color="blue") +
     xlim(0, ifelse( yvar=='rooms' , max(full.df$rooms), 5000)) +
     ggtitle( title )
}

auto.plot( 'ambientes - sin outliers extremos', ar_properties, ar_properties.sin.outliers, yvar='rooms')

auto.plot( 'ambientes - sin outliers precio solamente', ar_properties, ar_properties.sin.outliers.ingenuo, yvar='rooms')

auto.plot( 'ambientes - sin outliers isolationTree', ar_properties, ar_properties.sin.outliers.isolationtree, yvar='rooms')


auto.plot( 'area - sin outliers extremos', ar_properties, ar_properties.sin.outliers, yvar='surface_total')

auto.plot( 'area - sin outliers precio solamente', ar_properties, ar_properties.sin.outliers.ingenuo, yvar='surface_total')

auto.plot( 'area - sin outliers isolationTree', ar_properties, ar_properties.sin.outliers.isolationtree, yvar='surface_total')

En estos graficos se puede apreciar como isolation tree permite detectar outliers no solamente en los extremos, sino en la mitad de los rangos tambien (aunque esto se notaria aun mejor en un grafico de 3 dimensiones, donde en las tecnicas simples se veria que los puntos normales estan confinados dentro de un rectangulo, no siendo asi en el caso de isolation tree)

LS0tDQp0aXRsZTogIlRyYWJham8gUHJhY3RpY28gMSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCg0KIyMjIDE6IENhcmdhIGRlIGRhdG9zDQoNCiMjIyMgMS5BOiBMZWN0dXJhIHkgYW5hbGlzaXMgZGUgZXN0cnVjdHVyYSBkZWwgYXJjaGl2bw0KDQpQcm9jZWRlbW9zIGEgY2FyZ2FyIGVsIGFyY2hpdm8gZGUgZW50cmFkYSB5IHZlcmlmaWNhciBsYSBjYW50aWRhZCBkZSB2YXJpYWJsZXMgeSBjYXNvcy4NClBhcmEgZWxsbyB0ZW5lbW9zIHF1ZSBkZXNjb21wcmltaXIgZWwgYXJjaGl2byBhbnRlcyBkZSBjYXJnYWxvLCB5IHRhYm1pZW4gZXNwZWNpZmljYXIgZWwgZW5jb2RpbmcgY29ycmVjdG8gKGBVVEYtOGApIHBhcmEgbGEgY2FyZ2EgY29ycmVjdGEgZGUgY2FyYWN0ZXJlcyBlc3BlY2lhbGVzLg0KDQpEZWJpZG8gYSBxdWUgbGEgY2FyZ2EgZGUgbG9zIGBpZGBzIGN1ZXN0YSBtdWNobyBtYXMgbWVtb3JpYSBxdWUgdG9kbyBlbCByZXN0byB5IG5vIHByb3BvcmNpb25hIGluZm9ybWFjaW9uIGRlIHV0aWxpZGFkIHBhcmEgZWwgYW5hbGlzaXMgc2UgZGVzY2FydGEgZGUgZW50cmFkYS4NCg0KYGBge3J9DQojIGNhcmdhIGxpYnJlcmlhcyB0aWR5dmVyc2UgeSBnZ3Bsb3QyDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZ2dwbG90MikpDQoNCiNjYXJnYSBkZSBkYXRvcw0KYXJfcHJvcGVydGllcyA8LSByZWFkLmNzdih1bnooJ2FyX3Byb3BlcnRpZXMuemlwJywgJ2FyX3Byb3BlcnRpZXMuY3N2JyksIHJvdy5uYW1lcyA9IE5VTEwsIHN0cmluZ3NBc0ZhY3RvcnMgPSBUUlVFLCBlbmNvZGluZyA9ICJVVEYtOCIpICU+JSBzZWxlY3QoIC1jKCdpZCcpICkNCg0KI3ZlcmlmaWNhaW9uIGRlIGNhc29zIHkgdmFyaWFibGVzDQpoZWFkKGFyX3Byb3BlcnRpZXMpDQpucm93KGFyX3Byb3BlcnRpZXMpDQpgYGANCg0KDQojIyMjIDEuQjogUHJlcHJvY2VzYWRvIHkgZmlsdHJhZG8NCg0KQ2FyZ2Ftb3MgbGEgbGlicmVyaWEgdGlkeXZlcnNlIHBhcmEgcmVhbGl6YXIgbG9zIGZpbHRyYWRvcywgeSBwcm9jZWRlbW9zIGEgZXh0cmFlciBsb3MgY2Fzb3MgZGUgaW50ZXJlczoNCg0KYGBge3J9DQojIEZpbHRyYWRvIGRlIGxvcyBjYXNvcyBkZXNlYWRvcw0KYXJfcHJvcGVydGllcyA8LSBhcl9wcm9wZXJ0aWVzICU+JSBmaWx0ZXIoDQogICAgbDEgICAgICAgICAgICAgPT0gJ0FyZ2VudGluYScgJg0KICAgIGwyICAgICAgICAgICAgID09ICdDYXBpdGFsIEZlZGVyYWwnICYNCiAgICBjdXJyZW5jeSAgICAgICA9PSAnVVNEJyAmDQogICAgb3BlcmF0aW9uX3R5cGUgPT0gJ1ZlbnRhJyAmDQogICAgcHJvcGVydHlfdHlwZSAgJWluJSBjKCdDYXNhJywgJ0RlcGFydGFtZW50bycsICdQSCcpKQ0KYGBgDQoNCg0KIyMjIyAxLkM6IFByZXByb2Nlc2FkbyB5IGZpbHRyYWRvDQoNClVuYSB2ZXogZmlsdHJhZG9zIGxvcyBjYXNvcywgc2VsZWNjaW9uYW1vcyBsYXMgdmFyaWFibGVzIGRlc2VhZGFzIHkgcmVhanVzdGFtb3MgbG9zIG5pdmVsZXMgZGUgbGFzIHZhcmlhYmxlcyBkZSBjYXRlZ29yaWEgKGRlc2NhcnRhbW9zIGxhcyBjYXRlZ29yaWFzIHF1ZSB5YSBubyBmaWd1cmFuIGVuIG51ZXN0cm9zIGRhdG9zKS4gVmVyaWZpY2Ftb3MgbnVldmFtZW50ZSBsYSBjYW50aWRhZCBkZSByZWdpc3Ryb3MgeSB2YXJpYWJsZXMgcXVlIHF1ZWRhbg0KDQpgYGB7cn0NCiMgU2VsZWNjaW9uIGRlIGxhcyB2YXJpYWJsZXMgZGUgaW50ZXJlcw0KYXJfcHJvcGVydGllcyA8LSBhcl9wcm9wZXJ0aWVzICU+JSBzZWxlY3QoDQogIGwzLA0KICByb29tcywNCiAgYmVkcm9vbXMsDQogIGJhdGhyb29tcywNCiAgc3VyZmFjZV90b3RhbCwNCiAgc3VyZmFjZV9jb3ZlcmVkLA0KICBwcmljZSwNCiAgcHJvcGVydHlfdHlwZSApDQoNCiMgZWxpbWluYWNpb24gZGUgY2F0ZWdvcmlhcyBlbiBkZXN1c28NCmFyX3Byb3BlcnRpZXMgPC0gZHJvcGxldmVscyhhcl9wcm9wZXJ0aWVzKQ0KDQojdmVyaWZpY2FjaW9uIGRlIGNhc29zIHkgdmFyaWFibGVzDQpucm93KGFyX3Byb3BlcnRpZXMpDQpuY29sKGFyX3Byb3BlcnRpZXMpDQpgYGANCg0KDQojIyMgMjogQW5hbGlzaXMgZXhwbG9yYXRvcmlvIChJKQ0KDQojIyMjIDIuQTogQW5hbGlzaXMgZGUgdmFsb3JlcyBkaXN0aW50b3MgeSBmYWx0YW50ZXMNCg0KY2FsY3VsYW1vcyBsYSBjYW50aWRhZCBkZSB2YWxvcmVzIGRpc3RpbnRvcyB5IGZhbHRhbnRlcywgeSBsb3MgYW5hbGl6YW1vczoNCg0KYGBge3J9DQojIGNhbGN1bGFtb3MgbGEgY2FudGlkYWQgZGUgdmFsb3JlcyBkaXN0aW50b3MNCnVuaXF1ZXMudmFsdWVzIDwtIGFwcGx5KGFyX3Byb3BlcnRpZXMsIDIsIG5fZGlzdGluY3QpDQphcy5kYXRhLmZyYW1lKHVuaXF1ZXMudmFsdWVzKQ0KDQojIGNhbGN1bGFtb3MgbGEgY2FudGlkYWQgZGUgdmFsb3JlcyBmYWx0YW50ZXMNCm1pc3NpbmcudmFsdWVzIDwtIGNvbFN1bXMoaXMubmEoYXJfcHJvcGVydGllcykpDQphcy5kYXRhLmZyYW1lKG1pc3NpbmcudmFsdWVzKQ0KYGBgDQoNCkN1cmlvc2FtZW50ZSwgbm90YW1vcyBxdWUgcGFyYSBlbCBjYW1wbyAqKmwzKiogcXVlIGNvcnJlc3BvbmRlIGFsIGJhcnJpbyBwb3J0ZcOxbywgaGF5IDU3IHZhbG9yZXMgZGlzdGludG9zICg1OCBzaSB0b21hbW9zIGVuIGN1ZW50YSBOQSkuIEVzdG8gc2UgY29udHJhZGljZSBjb24gbG9zIDQ4IGJhcnJpb3MgcG9ydGXDsW9zIGV4aXN0ZW50ZXMsIHBvciBsbyBxdWUgYW5hbGl6YW1vcyBsb3MgYmFycmlvcyBpbmVzcGVyYWRvczoNCg0KYGBge3J9DQpiYXJyaW9zLnBvcnRlbm9zLm9mZmljaWFsZXMgPC0gYygiQWdyb25vbcOtYSIsICJBbG1hZ3JvIiwgIkJhbHZhbmVyYSIsICJCYXJyYWNhcyIsICJCZWxncmFubyIsICJCb2VkbyIsICJDYWJhbGxpdG8iLCAiQ2hhY2FyaXRhIiwgIkNvZ2hsYW4iLCAiQ29sZWdpYWxlcyIsICJDb25zdGl0dWNpw7NuIiwgIkZsb3JlcyIsICJGbG9yZXN0YSIsICJCb2NhIiwgIlBhdGVybmFsIiwgIkxpbmllcnMiLCAiTWF0YWRlcm9zIiwgIk1vbnNlcnJhdCIsICJNb250ZSBDYXN0cm8iLCAiUG9tcGV5YSIsICJOdcOxZXoiLCAiUGFsZXJtbyIsICJQYXJxdWUgQXZlbGxhbmVkYSIsICJQYXJxdWUgQ2hhY2FidWNvIiwgIlBhcnF1ZSBDaGFzIiwgIlBhcnF1ZSBQYXRyaWNpb3MiLCAiUHVlcnRvIE1hZGVybyIsICJSZWNvbGV0YSIsICJSZXRpcm8iLCAiU2FhdmVkcmEiLCAiU2FuIENyaXN0b2JhbCIsICJTYW4gTmljb2zDoXMiLCAiU2FuIFRlbG1vIiwgIlZlbGV6IFNhcnNmaWVsZCIsICJWZXJzYWxsZXMiLCAiVmlsbGEgQ3Jlc3BvIiwgIlZpbGxhIGRlbCBQYXJxdWUiLCAiVmlsbGEgRGV2b3RvIiwgIlZpbGxhIEdlbmVyYWwgTWl0cmUiLCAiVmlsbGEgTHVnYW5vIiwgIlZpbGxhIEx1cm8iLCAiVmlsbGEgT3J0dXphciIsICJWaWxsYSBQdWV5cnJlZMOzbiIsICJWaWxsYSBSZWFsIiwgIlZpbGxhIFJpYWNodWVsbyIsICJWaWxsYSBTYW50YSBSaXRhIiwgIlZpbGxhIFNvbGRhdGkiLCAiVmlsbGEgVXJxdWl6YSIpDQpzb3J0KHNldGRpZmYodW5pcXVlKGFyX3Byb3BlcnRpZXMkbDMpLCBiYXJyaW9zLnBvcnRlbm9zLm9mZmljaWFsZXMpKQ0KYGBgDQoNCk9ic2VydmFtb3MgY29tbyBsb3MgYmFycmlvcyAnZXh0cmEnIGNvcnJlc3BvbmRlbiBhIGRlbm9taW5hY2lvbmVzIG5vLW9maWNpYWxlcyBkZSBjaWVydGFzIHJlZ2lvbnMgZGUgbGEgQ0FCQS4NCg0KDQojIyMjIDIuQjogTWF0cml6IGRlIGNvcnJlbGFjaW9uDQoNClBhcmEgZWwgY2FsY3VsbyBkZSBsYSBtYXRyaXogZGUgY29ycmVsYWNpb24sIG5lY2VzaXRhbW9zIHBvZGVyIGV4dHJhZXIgbGFzIHZhcmlhYmxlcyBkZWwgdGlwbyBudW1lcmljby4gRGFkbyBxdWUgZXN0YSBmdW5jaW9uIHNlcmEgdXRpbGl6YWRhIGVuIHBhc29zIHBvc3RlcmlvcmVzLCBzZXLDoSBkZSB1dGlsaWRhZCBjcmVhciB1bmEgZnVuY2lvbiBxdWUgcGVybWl0YSBhdXRvbWF0aXphciBlc3RhIGZ1bmNpb247IHkgYWRlbWFzIGFncmVnYW1vcyBsYSBvcGNpb24gcGFyYSBzZWxlY2Npb25hciBjb2x1bW5hcyBlc3BlY2lmaWNhcyBlbiBsdWdhciBkZSB0b2RhcyBsYXMgbnVtZXJpY2FzLiBMdWVnbyBwcm9jZWRlbW9zIGEgY2FsY3VsYXIgbGEgY29ycmVsYWNpb24gZGUgbGFzIHZhcmlhYmxlcyBudW1lcmljYXMuDQoNCmBgYHtyfQ0KIyBmdW5jaW9uIHBhcmEgb2J0ZW5lciBsYXMgY29sdW1uYXMgZXNwZWNpZmljYWRhcywgbyBsYXMgbnVtZXJpY2FzIGVuIGNhc28gZGUgbm8gZXNwZWNpZmljYXJzZQ0KZ2V0Lm51bWVyaWMuY29sdW1ucyA8LSBmdW5jdGlvbihkZiwgY29sdW1ucy50by5nZXQ9TlVMTCkgew0KICAgIGlmIChpcy5udWxsKGNvbHVtbnMudG8uZ2V0KSkNCiAgICAgICAgY29sdW1ucy50by5nZXQgPC0gY29sbmFtZXMoZGYpW3VubGlzdChsYXBwbHkoZGYsIGlzLm51bWVyaWMpKV0NCg0KICAgIHJldHVybihjb2x1bW5zLnRvLmdldCkNCn0NCg0KIyBjYWxjdWxhbW9zIGxhIGNvcnJlbGFjaW9uIGRlIGxhcyB2YXJpYWJsZXMgbnVtZXJpY2FzLCBkZXNjYXJ0YW5kbyBsb3MgY2Fzb3MgaW5jb21wbGV0b3MNCmNvcihhcl9wcm9wZXJ0aWVzWyxnZXQubnVtZXJpYy5jb2x1bW5zKGFyX3Byb3BlcnRpZXMpXSwgdXNlPSJjb21wbGV0ZS5vYnMiLCBtZXRob2Q9InBlYXJzb24iKQ0KYGBgDQoNCk5vdGFtb3MgY29tbyBsYSBjb3JyZWxhY2lvbiBtYXMgYWx0YSAoMC45MjEpIHNlIGRhIGVudHJlIGByb29tc2AgeSBgYmVkcm9vbXNgIChsbyBjdWFsIHRpZW5lIHNlbnRpZG8sIHNpZW5kbyBxdWUgbGEgcmVsYWNpb24gdGllbmRlIGEgc2VyIGRlbCB0aXBvIGByb29tc2AgPSBgYmVkcm9vbXNgICsgMSBlbiBsYSBtYXlvcmlhIGRlIGxvcyBkZXBhcnRhbWVudG9zLCBjb3JyZXNwb25kaWVuZG8gZWwgJysxJyBhbCAqbGl2aW5nKiApLiBFc3RlIGRhdG8gbm9zIHBlcm1pdGlyYSBkZWNpZGlyIGVuIHVuIHBhc28gcG9zdGVyaW9yIGEgZGVjaWRpciBjb21vIHRyYXRhciBsb3MgY2Fzb3MgZmFsdGFudGVzLg0KVGFtYmllbiBub3RhbW9zIG90cmEgY29ycmVsYWNpb24gYWx0YSBlbnRyZSBzdXBlcmZpY2llIHRvdGFsIHkgc3VwZXJmaWNpZSBjdWJpZXJ0YSwgdGFtYmnDqW4gZXNwZXJhZGEsIHlhIHF1ZSBsb3MgZGVwYXJ0YW1lbnRvcyAvIGNhc2FzIC8gUEhzIG1hcyBncmFuZGVzIHRpZW5kZW4gYSB0ZW5lciBwYXRpb3MgLyBiYWxjb25lcyBvIGphcmRpbmVzIHRhbWJpZW4gbWFzIGdyYW5kZXMuDQpFbCBvdHJvIGNhc28gZGUgYWx0YSBjb3JyZWxhY2lvbiBzZSBkYSBlbnRyZSBiYcOxb3MgeSBhbWJpZW50ZXMsIHRhbWJpZW4gc2llbmRvIHVuYSByZWxhY2lvbiBlc3BlcmFibGUsIGRlIG5hdHVyYWxlemEgc2VtZWphbnRlIGEgbGEgZGVzY3JpdGEgcHJldmlhbWVudGUgKGxhcyB2aXZpZW5kYXMgY29uIG1hcyBhbWJpZW50ZXMgdGllbmRlbiBhIHRlbmVyIG1heW9yIGNhbnRpZGFkIGRlIGJhw7FvcykNCkxvIHVsdGltbyBhIGRlc3RhY2FyLCBlcyBxdWUgZWwgcHJlY2lvIG5vIHBhcmVjZSBlc3RhciB0YW4gY29ycmVsYWNpb25hZG8gY29uIGxhIHN1cGVyZmljaWUgKGxvIGN1YWwgc2UgcHJlc2VudGEgY29tbyBpbnVzdWFsLCB5YSBxdWUgZXMgY29tw7puIHF1ZSB1bm8gZGUgbG9zIGZhY3RvcmVzIGRldGVybWluYW50ZXMgZGVsIHByZWNpbyBkZSB1bmEgcHJvcGllZGFkIGVzIGxhIGNhbnRpZGFkIGRlIG1ldHJvcyBjdWFkcmFkb3MpLiBFc3RlIGFzcGVjdG8gc2UgYW5hbGl6YXLDoSBsdWVnbyBjb24gbWFzIGRldGFsbGUuDQoNCg0KDQojIyMgMzogUHJlcGFyYWNpb24gZGUgZGF0b3MNCg0KIyMjIyAzLkE6IEVsaW1pbmFjaW9uIGRlIHZhcmlhYmxlcyBhbHRhbWVudGUgY29ycmVsYWNpb25hZGFzDQoNCkRhZG8gcXVlIGxhcyB2YXJpYWJsZXMgYHJvb21zYCB5IGBiZWRyb29tc2AgcHJlc2VudGFuIGFsdGEgY29ycmVsYWNpb24sIHByb2NlZGVtb3MgYSBlbGltaW5hciB1bmEgZGUgZWxsYXMsIGxhIGRlIG1heW9yIGNhbnRhaWRhZCBkZSBmYWx0YW50ZXMgKGBiZWRyb29tc2ApDQoNCmBgYHtyfQ0KIyBlbGltaW5hY2lvbiBkZSBsYSB2YXJpYWJsZSBgYmVkcm9vbXNgDQphcl9wcm9wZXJ0aWVzIDwtIGFyX3Byb3BlcnRpZXMgJT4lIHNlbGVjdCggLWMoJ2JlZHJvb21zJykgKQ0KYGBgDQoNCg0KIyMjIyAzLkI6IEZpbHRyYWRvIGRlIGxvcyBjYXNvcyBpbmNvbXBsZXRvcw0KDQpGaWx0cmFtb3MgbG9zIGNhc29zIGluY29tcGxldG9zIHkgdmFsaWRhbW9zIGxhIGNhbnRpZGFkIGRlIHZhcmlhYmxlcyB5IGNhc29zDQoNCmBgYHtyfQ0KIyBlbGltaW5hY2lvbiBkZSBsYSB2YXJpYWJsZSBgYmVkcm9vbXNgDQphcl9wcm9wZXJ0aWVzIDwtIGFyX3Byb3BlcnRpZXNbY29tcGxldGUuY2FzZXMoYXJfcHJvcGVydGllcyksXQ0KDQojVmFsaWRhbW9zIGxhIGNhbnRpZGFkIGRlIGNhc29zIHkgdmFyaWFibGVzDQpucm93KGFyX3Byb3BlcnRpZXMpDQpuY29sKGFyX3Byb3BlcnRpZXMpDQpgYGANCg0KDQojIyMgNDogQW5hbGlzaXMgZXhwbG9yYXRvcmlvIChJSSkNCg0KIyMjIyA0LkE6IE9idGVuY2lvbiBkZSBlc3RhZGlzdGljYXMgZ2VuZXJhbGVzDQoNCk9idGVuZW1vcyBlc3RhZGlzdGljYXMgZGUgbGEgdmFyaWFibGUgcHJlY2lvLiBHZW5lcmFtb3MgZnVuY2lvbmVzIHF1ZSByZXV0aWxpemFyZW1vcyBtYXMgdGFyZGUuDQoNCmBgYHtyfQ0KIyBGdW5jaW9uIHBhcmEgZ2VuZXJhciBlc3RhZGlzdGljYXMgY29uIGxhIG1lZGlhIGluY2x1aWRhDQpzdW1tYXJ5LndpdGgubWVhbiA8LSBmdW5jdGlvbihkYXRhKQ0Kew0KICAgIHN1bW1hcnkgPC0gc3VtbWFyeShkYXRhKQ0KICAgIHN1bW1hcnlbJ21lYW4nXSA9IG1lYW4oZGF0YSkNCiAgICByZXR1cm4oIHN1bW1hcnkgKQ0KfQ0KDQojIENhbHVjbGFtb3MgbGFzIGVzdGFkaXN0aWNhcw0Kc3VtbWFyeS53aXRoLm1lYW4oYXJfcHJvcGVydGllcyRwcmljZSkNCg0KIyBZIHJlYWxpemFtb3MgdW4gaGlzdG9ncmFtYQ0KZ2dwbG90KGFyX3Byb3BlcnRpZXMsIGFlcyh4ID0gcHJpY2UpKSArIGdlb21faGlzdG9ncmFtKCkNCmBgYA0KDQpOb3RhbW9zIHF1ZSBoYXkgdW4gZ3JhbiByYW5nbyBkZSBwcmVjaW9zLCB5IHF1ZSBsYSBtYXlvcmlhIGRlIGxvcyB2YWxvcmVzIHNlIGNvbmNlbnRyYW4gZW4gbG9zIHZhbG9yZXMgbWFzIGJham9zLiBQb3IgZXNlIG1vdGl2bywgcmVwZXRpbW9zIGVsIGhpc3RvZ3JhbWEgcGVybyB1dGlsaWFuZG8gdW5hIGVzY2FsYSBsb2dhcml0bWljYSBwYXJhIHBvZGVyIG9ic2VydmFyIG1lam9yIGxhIGRpc3RyaWJ1Y2lvbi4NCg0KYGBge3J9DQpnZ3Bsb3QoYXJfcHJvcGVydGllcywgYWVzKHggPSBwcmljZSkpICsgZ2VvbV9oaXN0b2dyYW0oYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiLCBhZXMoeSA9IC4uZGVuc2l0eS4uKSkgKyBzY2FsZV94X2xvZzEwKCkgKyBnZW9tX2RlbnNpdHkoYWxwaGE9MC41KQ0KYGBgDQpOb3RhbW9zIHF1ZSBsYSBkaXN0cmlidWNpb24gZmluYWwgZGUgcHJlY2lvcyBwYXJlY2Ugc2VyIGJpbW9kYWwsIHF1ZSBwb2RyaWEgc2lnbmlmaWNhciBsYSBwcmVzZW5jaWEgZGUgcG9ibGFjaW9uZXMgZGlzdGludGFzIGRlbnRybyBkZSBsYSBtdWVzdHJhLiBUYW1iaWVuIHNlIG9ic2VydmEgcXVlIGxhIGRpc3RyaWJ1Y2lvbiAob2JzZXJ2YW5kbyBsYSBlc2NhbGEgbG9nYXJpdG1pY2EpIHByZXNlbnRhIHVuYSBhc2ltZXRyaWEsIGNvbiBjb2xhIHBlc2FkYSBhIGRlcmVjaGEuDQoNCg0KIyMjIyA0LkI6IE9idGVuY2lvbiBkZSBlc3RhZGlzdGljYXMgcG9yIHRpcG8gZGUgcHJvcGllZGFkDQoNClJlYWxpemFtb3MgZWwgbWlzbW8gYW5hbGlzaXMgYW50ZXJpb3IsIHBlcm8gc2VnbWVudGFkbyBwb3IgdGlwbyBkZSBwcm9waWVkYWQuDQoNCmBgYHtyfQ0KIyBDYWxjdWxhbW9zIGxhcyBlc3RhZGlzdGljYXMsIGRpc2NyaW1pbmFuZG8gcG9yIHRpcG8gZGUgcHJvcGllZGFkDQp0YXBwbHkoYXJfcHJvcGVydGllcyRwcmljZSwgYXJfcHJvcGVydGllcyRwcm9wZXJ0eV90eXBlLCBzdW1tYXJ5LndpdGgubWVhbikNCg0KIyBSZWFsaXphbW9zIGVsIGhpc3RvZ3JhbWEsIGRpc2NyaW1pbmFuZG8gcG9yIHRpcG8gZGUgcHJvcGllZGFkDQpnZ3Bsb3QoYXJfcHJvcGVydGllcywgYWVzKHg9cHJpY2UsIGZpbGw9cHJvcGVydHlfdHlwZSkpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShhbHBoYT0wLjUsIHBvc2l0aW9uPSJpZGVudGl0eSIsIGFlcyh5ID0gLi5kZW5zaXR5Li4pKSArDQogICAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSkgKw0KICAgIHNjYWxlX3hfbG9nMTAoKQ0KYGBgDQoNCkFsIGRpc2NyaW1pbmFyIHBvciB0aXBvIGRlIGNhc2EsIHNlIGRldGVjdGEgcXVlIGxhICdiaW1vZGFsaWRhZCcgb2JzZXJ2YWRhIGFudGVzIG5vIGVzIHNvbGFtZW50ZSBwcm9kdWN0byBkZSBkaWZlcmVudGVzIHRpcG9zIGRlIHByb3BpZWRhZGVzLCB5YSBxdWUgdGFudG8gUEhzIGNvbW8gZGVwYXJ0YW1lbnRvcyBwcmVzZW50YW4gdW4gbWlzbW8gInZhbGxlIiBhbHJlZGVkb3IgZGUgbG9zICQyMDAuMDAwIGRvbGFyZXMuIFRhbWJpZW4gc2UgcHVlZGUgb2JzZXJ2YXIgcXVlIHNpIGJpZW4gc2Ugbm90YW4gY2llcnRhcyBhc2ltZXRyaWFzIGVuIGxhcyBkaXN0cmlidWNpb25lcywgaW5kaXZpZHVhbG1lbnRlIG5vIHBhcmVjZW4gc2VyIHRhbiBhY2VudHVhZGFzIGNvbW8gY3VhbmRvIHNlIGNvbWJpbmFuIHRvZGFzIGxhcyBwb2JsYWNpb25lcyAobyBzZWEsIGxhIGNvbWJpbmFjaW9uIGRlIGxhcyBkaXN0aW50YXMgZHNpdHJpYnVjaW9uZXMgcXVlIGVzdGFuIGNlbnRyYWRhcyBlbiBkaXN0aW50b3MgcHVudG9zIGFjZW50dWEgbGEgYXNpbWV0cmlhIGRlIGRpc3RyaWJ1Y2lvbiBjb21iaW5hZGEpDQoNCg0KIyMjIyA0LkM6IEJveHBsb3RzDQoNCkdyYWZpY2Ftb3MgbG9zIGJveHBsb3RzIGRlbCBwcmVjaW8gcG9yIGNhZGEgdGlwbyBkZSBwcm9waWVkYWQuIFNpZ3VpZW5kbyBsYSBtaXNtYSBsb2dpY2EsIGVzY2FsYW1vcyBlbCBlamUgZGUgcHJlY2lvcyBsb2dhcml0bWljYW1lbnRlLg0KDQpgYGB7cn0NCiMgR3JhZmljYW1vcyBsb3MgYm94cGxvdHMNCmdncGxvdChhcl9wcm9wZXJ0aWVzLCBhZXMoeD1wcm9wZXJ0eV90eXBlLCB5PXByaWNlLCBmaWxsPXByb3BlcnR5X3R5cGUpKSArDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHNjYWxlX3lfbG9nMTAoKQ0KYGBgDQoNCk9ic2VydmFuZG8gbG9zIGJveHBsb3RzLCBzZSBvYnNlcnZhIGNsYXJhbWVudGUgY29tbyBsb3MgcHJlY2lvcyB0aWVuZW4gdW5hIHRlbmRlbmNpYSBkZWwgdGlwbyBDYXNhID4gUEggPiBkZXB0byAocXVlIHlhIHBvZGlhIHNlciBhcHJlY2lhZGEgZW4gbG9zIGhpc3RvZ3JhbWFzIHByZXZpb3MsIHBlcm8gYXVuIG1hcyBjbGFybyBhY2EpLiBUYW1iaWVuIHNlIG9ic2VydmEgY29tbyBsb3MgZGVwYXJ0YW1lbnRvcyBwYXJlY2VuIHRlbmVyIGxhIG1heW9yIGRpc3BlcnNpb24gZGUgcHJlY2lvcywgYWwgaWd1YWwgcXVlIG1heW9yIGNhbnRpZGFkIGRlIG91dGxpZXJzLg0KDQoNCiMjIyMgNC5EOiBDb3JyZWxvZ3JhbWENCg0KYGBge3J9DQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShHR2FsbHkpKQ0KDQojIEdyYWZpY2Ftb3MgZWwgY29ycmVsb2dyYW1hDQpnZ2NvcnIoYXJfcHJvcGVydGllc1ssZ2V0Lm51bWVyaWMuY29sdW1ucyhhcl9wcm9wZXJ0aWVzKV0sIG1ldGhvZCA9IGMoImV2ZXJ5dGhpbmciLCAicGVhcnNvbiIpKSANCmBgYA0KDQpMYXMgY29uY2x1c2lvbmVzIHF1ZSBzZSBwdWVkZW4gb2J0ZW5lciBkZSBlc3RlIGdyYWZpY28gc29uIGxhcyBtaXNtYXMgcXVlIGxhcyBvYnNlcnZhZGFzIGVuIGVsIHB1bnRvIDIuQi4NCg0KIyMjIyA1OiBPdXRsaWVycw0KDQpBbmFsaXphbW9zIGNvbW8gc2UgY29tcG9uZW4gbG9zIHByZWNpb3MgZW4gbG9zIGV4dHJlbW9zIHN1cGVyaW9yZXMgZSBpbmZlcmlvcmVzIGRlIGxhIGRpc3RyaWJ1Y2lvbg0KDQpgYGB7cn0NCiMgQ29ydGVzIGRlIHByZWNpb3MgcGFyYSBsb3MgY3VhbnRpbGVzIGVuIGxvcyBleHRyZW1vcw0KcXVhbnQuaW5mIDwtIHF1YW50aWxlKGFyX3Byb3BlcnRpZXMkcHJpY2UsIHByb2JzPXNlcSgwLCAwLjAwNSwgbGVuZ3RoLm91dCA9IDYpICkNCnF1YW50LnN1cCA8LSBxdWFudGlsZShhcl9wcm9wZXJ0aWVzJHByaWNlLCBwcm9icz1zZXEoLjk5NSwgMSwgbGVuZ3RoLm91dCA9IDYpICkNCg0KcXVhbnQuaW5mDQpxdWFudC5zdXANCmBgYA0KDQpBbmFsaXphbmRvIGxvcyByZXN1bHRhZG9zIGRlIHpvbmFQcm9wLCBsYSBwcm9waWVkYWQgbWFzIGJhcmF0YSBlbiB2ZW50YSBlbiBjYXBpdGFsIGZlZGVyYWwgZXN0YSBwb3Igc29icmUgNTAuMDAwIGRvbGFyZXMuIENvbWJpbmFuZG8gZXN0ZSBkYXRvIGNvbiBsYSBpbmZvcm1hY2lvbiBkZSBsb3MgY3VhcnRpbGVzIG9idGVuaWRhIHByZXZpYW1lbnRlLCBwb2RlbW9zIGFzZWd1cmFyIHF1ZSBkZXNjYXJ0YXIgbG9zIHZhbG9yZXMgaW5mZXJpb3JlcyBhIFUkUyA1MC4wMDAgc2VyaWEgZGVzY2FydGFyIG1lbm9zIGRlbCAwLjUlIGRlIGxhcyBtdWVzdHJhcy4gRW4gY3VhbnRvIGEgdmFsb3JlcyBzdXBlcmlvcmVzLCBzZSBwcm9zaWd1acOzIGEgZGVzY2FydGFyIGVsIG1pc21vIHBvcmNlbnRhZ2UgZGUgY29ydGUgcXVlIHBhcmEgbGFzIG11ZXN0cmFzIGluZmVyaW9yZXMuDQoNClRhbWJpZW4gc2UgcHJvY2VkZSBhIHJlY29ydGFyIHVzYW5kbyBsb3MgbWlzbW9zIHBvcmNlbnRhamVzIGVuIGxhcyBkaW1lbnNpb25lcyBkZSBgcm9vbXNgIHkgZGUgYHN1cmZhY2VfdG90YWxgLCB5YSBxdWUgc29uIGxhcyB2YXJpYWJsZXMgcXVlIGVzdGFyZW1vcyBtb2RlbGFuZG8gYSBjb250aW51YWNpb24sIHkgY2FsY3VsYW1vcyBsYSBjYW50aWRhZCBkZSBtdWVzdHJhcyBmaWx0cmFkYXMgKG5vdGFyIHF1ZSBkZWJpZG8gYSBxdWUgbGEgZWxpbWluYWNpb24gZGUgbG9zIGV4dHJlbW9zIHNlIGhpem8gZGUgZm9ybWEgc2ltdWx0YW5lYSwgbGEgY2FudGlkYWQgdG90YWwgZGUgbXVlc3RyYXMgcHVlZGUgbm8gY29pbmNpZGlyIGFsIDMlIHlhIHF1ZSBlcyBwcm9iYWJsZSBxdWUgbG9zIGNhc29zIHF1ZSBwb3NlZW4gdmFsb3JlcyBleHRyZW1vcyBkZSB1bmEgZGltZW5zaW9uIGNvbmN1ZXJkZW4gY29uIGxvcyBleHRyZW1vcyBkZSBvdHJhIGRlIGxhcyBkaW1lbnNpb25lcykNCg0KYGBge3J9DQphcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycyA9IGFyX3Byb3BlcnRpZXMgJT4lIGZpbHRlcigNCiAgICBwcmljZSA+PSBxdWFudGlsZShhcl9wcm9wZXJ0aWVzJHByaWNlLCAwLjAwNSkgJg0KICAgIHByaWNlIDw9IHF1YW50aWxlKGFyX3Byb3BlcnRpZXMkcHJpY2UsIDAuOTk1KSAmDQogICAgcm9vbXMgPj0gcXVhbnRpbGUoYXJfcHJvcGVydGllcyRyb29tcywgMC4wMDUpICYNCiAgICByb29tcyA8PSBxdWFudGlsZShhcl9wcm9wZXJ0aWVzJHJvb21zLCAwLjk5NSkgJg0KICAgIHN1cmZhY2VfdG90YWwgPD0gcXVhbnRpbGUoYXJfcHJvcGVydGllcyRzdXJmYWNlX3RvdGFsLCAwLjk5NSkgJg0KICAgIHN1cmZhY2VfdG90YWwgPD0gcXVhbnRpbGUoYXJfcHJvcGVydGllcyRzdXJmYWNlX3RvdGFsLCAwLjk5NSkgKQ0KDQpwb3JjZW50YWplPW5yb3coYXJfcHJvcGVydGllcy5zaW4ub3V0bGllcnMpL25yb3coYXJfcHJvcGVydGllcykNCg0KcmVwb3J0ZS5maWx0cmFkbyA8LSBmdW5jdGlvbiggZmlsdHJhZG8sIG9yaWdpbmFsICkNCnsNCiAgZGYgPC0gZGF0YS5mcmFtZSggb3JpZ2luYWw9bnJvdyhvcmlnaW5hbCksIGZpbHRyYWRvPW5yb3coZmlsdHJhZG8pLCBwb3JjZW50YWplPShucm93KGZpbHRyYWRvKS9ucm93KG9yaWdpbmFsKSoxMDApICkNCiAgcmV0dXJuKGRmKQ0KfQ0KDQpyZXBvcnRlLmZpbHRyYWRvKCBhcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycywgYXJfcHJvcGVydGllcyApDQpgYGANCg0KQ29tbyBjYXNvIGRlIGludGVyZXMsIHRhbWJpZW4gZmlsdHJhbW9zIGxvcyBvdXRsaWVycyB1dGlsaXphbmRvIGxhIHRlY25pY2EgZGUgKmlzb2xhdGlvbiB0cmVlKiwgdXRpbGl6YW5kbyBsYXMgdmFyaWFibGVzIGRlIGludGVyZXMgeSBmaWx0cmFuZG8gdW5hIGNhbnRpZGFkIGRlbWVqYW50ZSBkZSBtdWVzdHJhcywgcGVybyB1dGlsaXphbmRvIGNvbW8gY3JpdGVyaW8gc3UgcHVudGFqZSBkZSAqYW5vbWFsaWEqLiBQYXJhIHNlciBjb25zaXN0ZW50ZXMgY29uIGxhcyBjb21wYXJhY2lvbmVzLCBlbGltaW5hcmVtb3MgbGEgbWlzbWEgY2FudGlkYWQgZGUgbXVlc3RyYXMuDQoNCmBgYHtyfQ0KIyBDYXJnYW1vcyBsYSBsaWJyZXJpYSBuZWNlc2FyaWENCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KCJzb2xpdHVkZSIpKQ0KDQojIGluaWNpYW1vcyBlbCBhbGdvcml0bW8gdXRpbGl6YW5kbyBzb2xhbWVudGUgbGFzIHZhcmlhYmxlcyBxdWUgdmFtb3MgYSBjb3JyZWxhY2lvbmFyDQppc28gPC0gc29saXR1ZGU6Omlzb2xhdGlvbkZvcmVzdCRuZXcoKQ0KaXNvJGZpdChhcl9wcm9wZXJ0aWVzICU+JSBzZWxlY3QoIGMoInJvb21zIiwgInByaWNlIiwgInN1cmZhY2VfdG90YWwiKSApICkNCmFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmlzb2xhdGlvbnRyZWUgPC0gYXJfcHJvcGVydGllcw0KDQojIFVzYW1vcyBsb3MgcmVzdWx0YWRvcyBwYXJhIGZpbHRyYXMgbGFzIG11ZXN0cmFzIG1hcyBhbm9tYWxhcywgeSBsdWVnbyBkZXNjYXJ0YW1vcyBlc3RhIGNvbHVtbmENCmFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmlzb2xhdGlvbnRyZWUkYW5vbWFseVNjb3JlID0gaXNvJHNjb3JlcyRhbm9tYWx5X3Njb3JlDQphcl9wcm9wZXJ0aWVzLmlzb2xhdGlvbnRyZWUgPC0gYXJfcHJvcGVydGllcy5zaW4ub3V0bGllcnMuaXNvbGF0aW9udHJlZSAjYmFja3VwIGRlIHZhcmlhYmxlDQphcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycy5pc29sYXRpb250cmVlIDwtIGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmlzb2xhdGlvbnRyZWUgJT4lIGZpbHRlciggYW5vbWFseVNjb3JlIDw9IHF1YW50aWxlKGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmlzb2xhdGlvbnRyZWUkYW5vbWFseVNjb3JlLCBwb3JjZW50YWplKSApICU+JSBzZWxlY3QoIC1jKCJhbm9tYWx5U2NvcmUiKSApDQoNCiN2ZXJpZmljYW1vcyBxdWUgbGEgY2FudGlkYWQgZWxpbWluYWRhIHNlYSBzZW1lamFudGUgZW4gYW1ib3MgY2Fzb3MNCnJlcG9ydGUuZmlsdHJhZG8oIGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmlzb2xhdGlvbnRyZWUsIGFyX3Byb3BlcnRpZXMgKQ0KYGBgDQoNCkZpbmFsbWVudGUsIHRhbWJpZW4gdG9tYW1vcyBvdHJvIGNhc28gZGUgaW50ZXJlcywgdW4gZmlsdHJhZG8gYXVuIG1hcyBpbmdlbnVvIHF1ZSBlbCBwcmltZXJvLCBlbiBlbCBxdWUgc29sbyByZWNvcnRhbW9zIGxvcyB2YWxvcmVzIGV4dHJlbW9zIHBlcm8gdW5pY2FtZW50ZSBkZSBsYSBkaW1lbnNpb24gYHByZWNpb2AsIHRvbWFuZG8gcHJlY2F1Y2lvbmVzIHBhcmEgcXVlIGVsIHRhbWHDsW8gZGUgbGFzIG11ZXN0cmFzIHNlYSBjb21wYXJhYmxlLg0KDQpgYGB7cn0NCmFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmluZ2VudW8gPSBhcl9wcm9wZXJ0aWVzICU+JSBmaWx0ZXIoDQogICAgcHJpY2UgPj0gcXVhbnRpbGUoYXJfcHJvcGVydGllcyRwcmljZSwgKDEtcG9yY2VudGFqZSkvMikgJg0KICAgIHByaWNlIDw9IHF1YW50aWxlKGFyX3Byb3BlcnRpZXMkcHJpY2UsIDEtKDEtcG9yY2VudGFqZSkvMikgKQ0KICAgICAgDQpyZXBvcnRlLmZpbHRyYWRvKCBhcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycy5pbmdlbnVvLCBhcl9wcm9wZXJ0aWVzICkNCmBgYA0KDQoNCiMjIyMgNjogQW5hbGlzaXMgZXhwbG9yYXRvcmlvcyAoSUlJKQ0KDQpgYGB7cn0NCiMgQ2FsY3VsYW1vcyBsYXMgZXN0YWRpc3RpY2FzLCBkaXNjcmltaW5hbmRvIHBvciB0aXBvIGRlIHByb3BpZWRhZA0KdGFwcGx5KGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzJHByaWNlLCBhcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycyRwcm9wZXJ0eV90eXBlLCBzdW1tYXJ5LndpdGgubWVhbikNCg0KIyBSZWFsaXphbW9zIGVsIGhpc3RvZ3JhbWEsIGRpc2NyaW1pbmFuZG8gcG9yIHRpcG8gZGUgcHJvcGllZGFkDQpnZ3Bsb3QoYXJfcHJvcGVydGllcy5zaW4ub3V0bGllcnMsIGFlcyh4PXByaWNlLCBmaWxsPXByb3BlcnR5X3R5cGUpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiLCBhZXMoeSA9IC4uZGVuc2l0eS4uKSkgKw0KICAgIGdlb21fZGVuc2l0eShhbHBoYT0wLjUpICsNCiAgICBzY2FsZV94X2xvZzEwKCkNCg0KIyBHcmFmaWNhbW9zIGxvcyBib3hwbG90cw0KZ2dwbG90KGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLCBhZXMoeD1wcm9wZXJ0eV90eXBlLCB5PXByaWNlLCBmaWxsPXByb3BlcnR5X3R5cGUpKSArDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHNjYWxlX3lfbG9nMTAoKQ0KDQojIEdyYWZpY2Ftb3MgZWwgY29ycmVsb2dyYW1hDQpnZ2NvcnIoYXJfcHJvcGVydGllcy5zaW4ub3V0bGllcnNbLGdldC5udW1lcmljLmNvbHVtbnMoYXJfcHJvcGVydGllcy5zaW4ub3V0bGllcnMpXSwgbWV0aG9kID0gYygiZXZlcnl0aGluZyIsICJwZWFyc29uIikpDQpgYGANCg0KDQpTZSBwdWVkZSBvYnNlcnZhciAoY29uIGVzcGVjaWFsIGRldGFsbGUgZW4gbG9zIGJveHBsb3RzKSBjb21vIGdyYW4gY2FudGlkYWQgZGUgbG9zIG91dGxpZXJzIGRlc2FwYXJlY2llcm9uLiBDYWJlIGRlc3RhY2FyIHF1ZSBlc3RhIGVzIHVuYSB0ZWNuaWNhIHVuIHBvY28gaW5nZW51YSBwYXJhIGxhIGVsaW1pbmFjaW9uIGRlIGxvcyBtaXNtb3MsIHlhIHF1ZSBubyBjb25zaWRlcmEgbGEgcG9zaWJpbGlkYWQgZGUgb3V0bGllcnMgZGViaWRvIGEgbGEgcmVsYWNpb24gZGUgbGFzIHZhcmlhYmxlcyBlbnRyZSBzw60sIHkgc29sYW1lbnRlIGRlc2NhcnRhIGxvcyBleHRyZW1vcyBlbiBjYWRhIGRpbWVuc2lvbi4gQXVuIGFzaSwgb2JlcnZhbW9zIGNvbW8gYXVtZW50byBsYSBjb3JyZWxhY2lvbiBlbnRyZSB2YXJpb3MgcGFyZXMgZGUgdmFyaWFibGVzIHF1ZSBwcmV2aWFtZW50ZSBlc3RhYmFuIGRlYmlsbWVudGUgY29ycmVsYWNpb25hZGFzIChwcmVjaW8tYXJlYSB0b3RhbCwgYXJlYSB0b3RhbC1oYWJpdGFjaW9uZXMsIGJhw7Fvcy1zdXByZWZpY2llIHRvdGFsKQ0KDQojIyMjIDc6IE1vZGVsbyBsaW5lYWwNCg0KIyMjIyMgNy5BL0I6IEdlbmVyYWNpb24gZGVsIG1vZGVsbyBsaW5lYWwgLyBkZXNjcmlwY2lvbg0KDQpgYGB7cn0NCg0KYXV0by5sbSA8LSBmdW5jdGlvbiggZGVzY3JpcHRpb24sIGRhdGEsIHhWYXIgKQ0Kew0KICBsbS5yZXN1bHQgPC0gbG0oZGF0YSRwcmljZX5kYXRhW1t4VmFyXV0pDQogIHN1bSA8LSBzdW1tYXJ5KCBsbS5yZXN1bHQgKQ0KICBwcmludCggZGVzY3JpcHRpb24gKQ0KICBwcmludCggc3VtICkNCiAgcmV0dXJuKCBkYXRhLmZyYW1lKA0KICAgIGRlc2NyaXBjaW9uPWRlc2NyaXB0aW9uLA0KICAgIGludGVyY2VwdD1zdW0kY29lZmZpY2llbnRzWzFdLA0KICAgIGNvZWZmPXN1bSRjb2VmZmljaWVudHNbMl0sDQogICAgcnNxdWFyZWRBZGo9c3VtJGFkai5yLnNxdWFyZWQgKSApDQp9DQoNCg0KcmVzdWx0IDwtIGF1dG8ubG0oJ2NvbiBvdXRsaWVycyB2cyBzdXBlcmZpY2llJywgYXJfcHJvcGVydGllcywgJ3N1cmZhY2VfdG90YWwnKQ0KcmVzdWx0IDwtIHJiaW5kKHJlc3VsdCwgYXV0by5sbSgnY29uIG91dGxpZXJzIHZzIGFtYmllbnRlcycsIGFyX3Byb3BlcnRpZXMsICdyb29tcycpICkNCg0KcmVzdWx0IDwtIHJiaW5kKHJlc3VsdCwgYXV0by5sbSgnc2luIG91dGxpZXJzIHZzIHN1cGVyZmljaWUnLCBhcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycywgJ3N1cmZhY2VfdG90YWwnKSApDQpyZXN1bHQgPC0gcmJpbmQocmVzdWx0LCBhdXRvLmxtKCdzaW4gb3V0bGllcnMgdnMgYW1iaWVudGVzJywgYXJfcHJvcGVydGllcy5zaW4ub3V0bGllcnMsICdyb29tcycpICApDQpyZXN1bHQgPC0gcmJpbmQocmVzdWx0LCBhdXRvLmxtKCdzaW4gb3V0bGllcnMgMUQgdnMgc3VwZXJmaWNpZScsIGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmluZ2VudW8sICdzdXJmYWNlX3RvdGFsJykgKQ0KcmVzdWx0IDwtIHJiaW5kKHJlc3VsdCwgYXV0by5sbSgnc2luIG91dGxpZXJzIDFEIHZzIGFtYmllbnRlcycsIGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmluZ2VudW8sICdyb29tcycpICkNCnJlc3VsdCA8LSByYmluZChyZXN1bHQsIGF1dG8ubG0oJ3NpbiBvdXRsaWVycyAtIGlzb2xhdGlvbiB0cmVlIHZzIHN1cGVyZmljaWUnLCBhcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycy5pc29sYXRpb250cmVlLCAnc3VyZmFjZV90b3RhbCcpICkNCnJlc3VsdCA8LSByYmluZChyZXN1bHQsIGF1dG8ubG0oJ3NpbiBvdXRsaWVycyAtIGlzb2xhdGlvbiB0cmVlIHZzIGFtYmllbnRlcycsIGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmlzb2xhdGlvbnRyZWUsICdyb29tcycpICkNCnJlc3VsdA0KYGBgDQoNCiMjIyMjIyAqKkludGVycHJldGFjaW9uIGRlIGxvcyBwYXJhbWV0cm9zKioNCg0KU2kgY29tcGFyYW1vcyBsb3MgZGlzdGludG9zIG1vZGVsb3MgZW4gZnVuY2lvbiBkZWwgUl4yXiBhanVzdGFkbywgaW50ZXJwcmV0YW5kbyBxdWUgZXN0ZSBwYXJhbWV0cm8gZXMgZWwgcXVlIGluZGljYSBjdWFuIG1lam9yIHByZWRpY3RvciBkZSBwcmVjaW8gZXMgZWwgbW9kZWxvIChyZXByZXNlbnRhbmRvIGVsIHBvcmNlbnRhamUgZGUgbGEgdmFyaWFjaW9uIGV4cGxpY2Fkbywgc2llbmRvIG1hcyBhbHRvIG1lam9yKSwgb2JzZXJ2YW1vcyBxdWUgbG9zIGRvcyBtb2RlbG9zIHF1ZSBtZWpvciBhanVzdGFuIHNvbiAgZWwgZGUgcHJlY2lvIHZzIGFyZWEsIGVuIGxvcyBjYXNvcyBkZSBmaXRscmFkb3MgcG9yIGV4dHJlbW9zIHkgZmlsdHJhZG8gcG9yIGlzb2xhdGlvbiB0cmVlLiBUYW1iaWVuIG9ic2VydmFtb3MgbG9zIHAtdmFsb3JlcyBlbiB0b2RvcyBsb3MgY2Fzb3MgKGV4Y2VwdG8gZWwgZmlsdHJhZG8gMUQgcGFyYSBlbCAkXGJldGFfMCQpIHNvbiBzaWVtcHJlIG11eSBzaWduaWZjYXRpdm9zIChwPDAuMDAxKSwgY29uIGxvIGN1YWwgcG9kZW1vcyBhc2VndXJhciBxdWUgZXhpc3RlIHVuYSBkZXBlbmRlbmNpYSBlbnRyZSBlbCBwcmVjaW8geSBsYXMgb3RyYXMgMiB2YXJpYWJsZXMgKGFtYmllbnRzZSB5IHN1cGVyZmljaWUpDQpFbCBtb2RlbG8gcHJlc2VudGEgMiBwYXJhbWV0cm9zIHBhcmEgbGEgcHJlZGljY2lvbjogaW50ZXJjZXB0IChvICRcYmV0YV8wJCkgPSB+MzksMDAwLCBxdWUgcmVwcmVzZW50YXJpYSBlbCB2YWxvciBxdWUgZXN0aW1hcmlhIHBhcmEgdW5hIHByb3BpZWRhZCBkZSAwIG1ldHJvcyBjdWFkcmFkYW9zIChxdWUgaWd1YWwgaGF5IHF1ZSBub3RhciBxdWUgZXMgdW4gZXh0cmVtbyB0ZW9yaWNvLCB5YSBxdWUgZWwgbW9kZWxvIGFqdXN0YSBtZWpvciBlbiBsYSB6b25hIGRvbmRlIHggZXMgaWd1YWwgYSBsYSBtZWRpYSBkZSBsYXMgbXVlc3RyYXMsIHkgZW4gZXN0ZSBjYXNvIGNhcmVjZSBkZSBzZW50aWRvIGludGVycHJldGFyIGVzdGUgcGFyYW1ldHJvIGRlIGZvcm1hIGluZGVwZW5kaWVudGUpLCB5IGVsIGNvZWZmaWNpZW50ZSAoJFxiZXRhXzEkKSA9IH4yLDIwMCBxdWUgc2VyaWEgbWVqb3IgaW50ZXJwcmV0YWRvIGNvbW8gImN1YW50byB2YXJpYSBlbCBwcmVjaW8gZGUgdW5hIHByb3BpZWRhZCBwb3IgY2FkYSBtXjJeIGFkaWNpb25hbCIuDQpVc2FuZG8gbGEgbWlzbWEgbG9naWNhLCBwYXJhIGVsIG1vZGVsbyBxdWUgbWVqb3IgYWp1c3RhIGRlIHByZWNpbyB2cyBhbWJpZW50ZXMsIHNlIGVzdGltYSBxdWUgY2FkYSBhbWJpZW50ZSBleHRyYSB2YXJpYSBlbCBwcmVjaW8gdG90YWwgZW4gcHJvbWVkaW8gfiAkOTMsMDAwDQoNCg0KDQojIyMjIyA3LkM6IEFuYWxpc2lzIC8gY29uY2x1c2lvbmVzDQoNCkVsIG1lam9yIG1vZGVsbyBwcmVkaWN0b3IgZGUgdG9kb3MgKHVzYW5kbyBSXjJeIGNvbW8gcGFyYW1ldHJvIHBhcmEgY29tcGFyYXIgbW9kZWxvcykgZGUgcHJlY2lvIGVzIGVsIG1vZGVsIGRlIHByZWNpbyB2cyBzdXBlcmZpY2llIGRlbCBmaXRscmFkbyBtZWRpYW50ZSAqaXNvbGF0aW9uIHRyZWUqLCBsbyBjdWFsIGVzIGluZGljYWRvciBkZSBxdWUgZXMgdW4gbWV0b2RvIG1hcyBlZmljaWVudGUgZGUgZGV0ZWNjaW9uIGRlIG91dGxpZXJzLCBkZWJpZG8gYSBsYSBwb3NpYmlsaWRhZCBkZWwgbWlzbW8gZGUgZGV0ZWN0YXIgZXh0cmVtb3MgZW4gbXVsdGlwbGVzIGRpbWVuc2lvbmVzIGVuIGNvbmp1bnRvOyBhdW5xdWUgbGEgbWVqb3JhIG5vIHNlYSBzdXN0YW5jaWFsIGVuIGNvbXBhcmFjaW9uIGNvbiBlbCBtZXRvZG8gZGUgZmlsdHJhZG8gZGUgZXh0cmVtb3MgZW4gY2FkYSBkaW1lbnNpb24uDQoNClRhbWJpZW4gcG9kZW1vcyBub3RhciBjb21vIHNpIG5vIHNlIGVsaW1pbmFuIGxvcyBvdXRsaWVycyAobyBzaSBzZSBlbGltaW5hbmEgZGUgdW5hIGZvcm1hIHBvY28gZWZpY2llbnRlKSBsYSBlZmVjdGl2aWRhZCBkZWwgbW9kZWxvIHJlc3VsdGFudGUgcHVlZGUgdmFyaWFyIGRyYXN0aWNhbWVudGUsIGNvbW8gc2UgcHVlZGUgYW5hbGl6YXIgZW4gbG9zIGNhc29zICpjb24gb3V0bGllcnMqIHkgKnNpbiBvdXRsaWVycyAxRCogdnMgc3VwZXJmaWNpZTsgeSBlbiBhbGd1bm9zIGNhc29zIGhhc3RhIGVtcGVvcmFyICh2ZXIgY29uIG91dGxpZXJzIHZzIHN1cGVyZmljaWUgZW4gY29tcGFyYWNpb24gY29uIHNpbiBvdXRsaWVycyAxRCB2cyBzdXBlcmZpY2llKQ0KDQpDYWJlIGRlc3RhY2FyIHRhbWJpZW4gcXVlIGN1YW5kbyBlbCBtb2RlbG8gc2UgaGFjZSBjb250cmEgbGEgdmFyaWFibGUgYW1iaWVudGVzLCBlbCByZXN1bHRhZG8gdGllbmRlIGEgc2VyIG1hcyAicm9idXN0byIsIGRlc2RlIGVsIHB1bnRvIGRlIHZpc3RhIHF1ZSBlbCByXjJeIGRlbCBtaXNtbyBubyB0aWVuZGUgYSB2YXJpYXIgZGUgZm9ybWEgdGFuIGV4dHJlbWEgY29tbyBjb24gbG9zIG90cm9zIG1vZGVsb3MgYWwgY2FtYmlhciBsYSB0ZWNuaWNhIGRlIGZpbHRyYWRvIGRlIG91dGxpZXJzLiBFc3RvIHRpZW5lIGNvbW8gZWZlY3RvIHF1ZSwgZGVwZW5kaWVuZG8gZGVsIG1ldG9kbyBkZSBmaWx0cmFkbywgcHVlZGUgY2FtYmlhciBsYSB2YXJpYWJsZSBxdWUgc2VyaWEgIm1lam9yIiBwcmVkaWN0b3IgZGVsIHByZWNpby4NCg0KUGFyYSBwb2RlciB2aXN1YWxpemFyIG1lam9yIGVsIGVmZWN0byBkZSBsb3Mgb3V0bGllcnMgZW4gbGFzIGRpZmVyZW50ZXMgZm9ybWFzIGRlIGZpbHRyYWRvLCBzZSBwcm9jZWRlIGEgY29udGludWFjaW9uIGEgZ3JhZmljYXIgZWwgbW9kZWxvIHJlc3VsdGFuZG8sIGRlbm90YW5kbyBxdWUgY2Fzb3Mgc29uIGVsaW1pbmFkb3MgY29uIGxvcyBkaWZlcmVudGVzIG1ldG9kb3MuDQpgYGB7cn0NCmF1dG8ucGxvdCA8LSBmdW5jdGlvbiggdGl0bGUsIGZ1bGwuZGYsIGZpbHRlcmVkLmRmLCB5dmFyPSdyb29tcycgKQ0Kew0KICByZW1vdmVkIDwtIHNldGRpZmYoZnVsbC5kZiwgZmlsdGVyZWQuZGYpDQogIHJlbW92ZWQkc291cmNlID0gJ291dGxpZXJzJw0KICBmaWx0ZXJlZC5kZiRzb3VyY2UgPSAnbm9ybWFsJw0KICBmdWxsLndpdGguc291cmNlIDwtIHJiaW5kKCBmaWx0ZXJlZC5kZiwgcmVtb3ZlZCApDQogIGdncGxvdChmdWxsLndpdGguc291cmNlLCBhZXNfc3RyaW5nKHg9eXZhciwgeT0icHJpY2UiLCBjb2xvcj0ic291cmNlIikpICsNCiAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIGRhdGE9ZmlsdGVyZWQuZGYsIGNvbG9yPSJibHVlIikgKw0KICAgICB4bGltKDAsIGlmZWxzZSggeXZhcj09J3Jvb21zJyAsIG1heChmdWxsLmRmJHJvb21zKSwgNTAwMCkpICsNCiAgICAgZ2d0aXRsZSggdGl0bGUgKQ0KfQ0KDQphdXRvLnBsb3QoICdhbWJpZW50ZXMgLSBzaW4gb3V0bGllcnMgZXh0cmVtb3MnLCBhcl9wcm9wZXJ0aWVzLCBhcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycywgeXZhcj0ncm9vbXMnKQ0KYXV0by5wbG90KCAnYW1iaWVudGVzIC0gc2luIG91dGxpZXJzIHByZWNpbyBzb2xhbWVudGUnLCBhcl9wcm9wZXJ0aWVzLCBhcl9wcm9wZXJ0aWVzLnNpbi5vdXRsaWVycy5pbmdlbnVvLCB5dmFyPSdyb29tcycpDQphdXRvLnBsb3QoICdhbWJpZW50ZXMgLSBzaW4gb3V0bGllcnMgaXNvbGF0aW9uVHJlZScsIGFyX3Byb3BlcnRpZXMsIGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmlzb2xhdGlvbnRyZWUsIHl2YXI9J3Jvb21zJykNCg0KYXV0by5wbG90KCAnYXJlYSAtIHNpbiBvdXRsaWVycyBleHRyZW1vcycsIGFyX3Byb3BlcnRpZXMsIGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLCB5dmFyPSdzdXJmYWNlX3RvdGFsJykNCmF1dG8ucGxvdCggJ2FyZWEgLSBzaW4gb3V0bGllcnMgcHJlY2lvIHNvbGFtZW50ZScsIGFyX3Byb3BlcnRpZXMsIGFyX3Byb3BlcnRpZXMuc2luLm91dGxpZXJzLmluZ2VudW8sIHl2YXI9J3N1cmZhY2VfdG90YWwnKQ0KYXV0by5wbG90KCAnYXJlYSAtIHNpbiBvdXRsaWVycyBpc29sYXRpb25UcmVlJywgYXJfcHJvcGVydGllcywgYXJfcHJvcGVydGllcy5zaW4ub3V0bGllcnMuaXNvbGF0aW9udHJlZSwgeXZhcj0nc3VyZmFjZV90b3RhbCcpDQpgYGANCkVuIGVzdG9zIGdyYWZpY29zIHNlIHB1ZWRlIGFwcmVjaWFyIGNvbW8gaXNvbGF0aW9uIHRyZWUgcGVybWl0ZSBkZXRlY3RhciBvdXRsaWVycyBubyBzb2xhbWVudGUgZW4gbG9zIGV4dHJlbW9zLCBzaW5vIGVuIGxhIG1pdGFkIGRlIGxvcyByYW5nb3MgdGFtYmllbiAoYXVucXVlIGVzdG8gc2Ugbm90YXJpYSBhdW4gbWVqb3IgZW4gdW4gZ3JhZmljbyBkZSAzIGRpbWVuc2lvbmVzLCBkb25kZSBlbiBsYXMgdGVjbmljYXMgc2ltcGxlcyBzZSB2ZXJpYSBxdWUgbG9zIHB1bnRvcyBub3JtYWxlcyBlc3RhbiBjb25maW5hZG9zIGRlbnRybyBkZSB1biByZWN0YW5ndWxvLCBubyBzaWVuZG8gYXNpIGVuIGVsIGNhc28gZGUgaXNvbGF0aW9uIHRyZWUpDQoNCg==