Introducción

Problema

Una empresa inmobiliaria líder en una gran ciudad está buscando comprender en profundidad el mercado de viviendas urbanas para tomar decisiones estratégicas más informadas. La empresa posee una base de datos extensa que contiene información detallada sobre diversas propiedades residenciales disponibles en el mercado. Se requiere realizar un análisis holístico de estos datos para identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades.

Retos

El reto principal consisten en realizar un análisis integral y multidimensional de la base de datos para obtener una comprensión del mercado inmobiliario urbano. Se requiere aplicar diversas técnicas de análisis de datos, incluyendo:

Análisis de Componentes Principales: Reducir la dimensionalidad del conjunto de datos y visualizar la estructura de las variables en componentes principales para identificar características clave que influyen en la variación de precios y oferta del mercado.

Análisis de Conglomerados: Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas de las ofertas específicas en diferentes partes de la ciudad y en diferentes estratos socio-económicos.

Análisis de Correspondencia: Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta en mercado inmobiliario.

Visualización de resultados: Presentar gráficos, mapas y otros recursos visuales para comunicar los hallazgos de manera clara y efectiva a la dirección de la empresa.

El informe final debe incluir análisis detallados de los resultados obtenidos, las conclusiones clave y las recomendaciones específicas para guiar las decisiones estratégicas de la empresa inmobiliaria. Se espera que este análisis de datos proporcione ventajas competitivas en el mercado, optimizando la inversión y maximizando los beneficios en un entorno altamente competitivo y en constante cambio.

#devtools::install_github("dgonxalex80/paqueteMODELOS", force = TRUE)
#devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
#library(paqueteMODELOS)
data(vivienda)
str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8322] 1147 1169 1350 5992 1212 ...
##  $ zona        : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr [1:8322] NA NA NA "02" ...
##  $ estrato     : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   id = col_double(),
##   ..   zona = col_character(),
##   ..   piso = col_character(),
##   ..   estrato = col_double(),
##   ..   preciom = col_double(),
##   ..   areaconst = col_double(),
##   ..   parqueaderos = col_double(),
##   ..   banios = col_double(),
##   ..   habitaciones = col_double(),
##   ..   tipo = col_character(),
##   ..   barrio = col_character(),
##   ..   longitud = col_double(),
##   ..   latitud = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

Descripción variables base de datos

A continuación se mencionan las variables que se utilizarán en el ejercicio.

Zona: Registra la zona donde esta ubicada el inmueble esta puede ser: Zona Centro,Zona Norte,Zona Oeste,Zona Oriente,Zona Sur

Piso: Registra el piso ene l cual se ubica la vivienda

Estrato: Variable con escala de medición ordinal,los estrato de las viviendas son 3,4,5,6

Preciom: Precio de la vivienda

areaconstu: Area Construida

parqueaderos: Numero de parqueaderos de la vivienda

banios: Número de baños de la propiedad

Habitaciones: Número de habitaciones que posee la vivienda

Tipo: Tipo de la vivienda, casa o apartamento

Nota: Se eliminaron variables que no contribuyen en el ejercicio ni aportan información relevante para el modelo, tales como: Id, Latitud y Longitud

1. Limpieza del Dataset

1.1 Análisis y Limpieza Inicial del Dataset

#vivienda
viviendas <- subset(vivienda, !is.na(id))
#vivienda
viviendas$latitud <- NULL
viviendas$longitud <- NULL
viviendas$id <- NULL
#vivienda
summary(viviendas)
##      zona               piso              estrato         preciom      
##  Length:8319        Length:8319        Min.   :3.000   Min.   :  58.0  
##  Class :character   Class :character   1st Qu.:4.000   1st Qu.: 220.0  
##  Mode  :character   Mode  :character   Median :5.000   Median : 330.0  
##                                        Mean   :4.634   Mean   : 433.9  
##                                        3rd Qu.:5.000   3rd Qu.: 540.0  
##                                        Max.   :6.000   Max.   :1999.0  
##                                                                        
##    areaconst       parqueaderos        banios        habitaciones   
##  Min.   :  30.0   Min.   : 1.000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000   1st Qu.: 3.000  
##  Median : 123.0   Median : 2.000   Median : 3.000   Median : 3.000  
##  Mean   : 174.9   Mean   : 1.835   Mean   : 3.111   Mean   : 3.605  
##  3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000   3rd Qu.: 4.000  
##  Max.   :1745.0   Max.   :10.000   Max.   :10.000   Max.   :10.000  
##                   NA's   :1602                                      
##      tipo              barrio         
##  Length:8319        Length:8319       
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
##                                       
## 

Se observan datos faltantes en las variables piso y parqueadero. Para verlo gráficamente, se usa el siguiente código:

missing <- colSums(is.na(viviendas)) %>%
                 as.data.frame()

#missing
VIM::aggr(viviendas, cex.axis = 0.5, cex.lab= 0.8)  

md.pattern(viviendas, rotate.names=TRUE)

##      zona estrato preciom areaconst banios habitaciones tipo barrio
## 4808    1       1       1         1      1            1    1      1
## 1909    1       1       1         1      1            1    1      1
## 876     1       1       1         1      1            1    1      1
## 726     1       1       1         1      1            1    1      1
##         0       0       0         0      0            0    0      0
##      parqueaderos piso     
## 4808            1    1    0
## 1909            1    0    1
## 876             0    1    1
## 726             0    0    2
##              1602 2635 4237

Se ven dos variables con una buena cantidad de datos faltantes, piso (2635) y parqueaderos (1602). Analicemos dichas variables. Empecemos por piso:

viviendas$piso <- as.integer(viviendas$piso)
pisos <- summarytools::descr(viviendas$piso)
pisos
## Descriptive Statistics  
## viviendas$piso  
## N: 8319  
## 
##                        piso
## ----------------- ---------
##              Mean      3.77
##           Std.Dev      2.61
##               Min      1.00
##                Q1      2.00
##            Median      3.00
##                Q3      5.00
##               Max     12.00
##               MAD      1.48
##               IQR      3.00
##                CV      0.69
##          Skewness      1.28
##       SE.Skewness      0.03
##          Kurtosis      1.05
##           N.Valid   5684.00
##         Pct.Valid     68.33
table(viviendas$piso)
## 
##    1    2    3    4    5    6    7    8    9   10   11   12 
##  860 1450 1097  607  567  245  204  211  146  130   84   83

Podemos observar algunas cosas importantes. Como vimos hay 2635 registros sin dato en la columna piso. Lo segundo, que el menor valor es 1 y el mayor es 12, con un buen número de registros con valores altos. Por el nombre de la variable no sabemos si el valor corresponde al número del piso en el que queda el inmueble en un edificio, o el número de pisos que tiene el inmueble. Si fuera lo segundo, querría decir que hay inmuebles que tienen 12 pisos, lo que tendría sentido si fueran edificios. Si fuera lo primero, significa que hay inmuebles en el doceavo piso de un edificio, al igual que en el décimo, el noveno, y así sucesivamente, Lo que tiene mucho más sentido, dado lo extremadamente raro que sería que una casa o un apartamento tuviera 12 pisos. Para tratar de entender a que se refiere, vamos a ver primero a que tipo de inmueble corresponden los registros que tienen un número alto.

pisoNna <- subset(viviendas, !is.na(viviendas$piso)) #subset pisoNna contains the records with value in column piso
table(pisoNna$tipo)
## 
## Apartamento        Casa 
##        3719        1965

Existen 3719 Apartamentos con valor en la variable piso, y 1965 Casas con igual condición. Podemos ver también que no hay inmuebles de tipo edificio.

pisona <- subset(viviendas, is.na(viviendas$piso)) #subset pisoNna contains the records with value in column piso
table(pisona$tipo)
## 
## Apartamento        Casa 
##        1381        1254

Existe una proporción bastante similar entre Casas y Apartamentos sin piso. Podemos ver también que no hay inmuebles de tipo edificio, lo que no apoya el caso mencionado de que la variable se refiere al piso en el que se encuentra el inmueble en un edificio, además de que no hay casas en edificios, sino apartamentos. Ahora veamos como se cruza el Tipo con los valores de la columna Piso.

table(viviendas$piso,viviendas$tipo)
##     
##      Apartamento Casa
##   1          430  430
##   2          512  938
##   3          573  524
##   4          545   62
##   5          564    3
##   6          243    2
##   7          200    4
##   8          211    0
##   9          146    0
##   10         128    2
##   11          84    0
##   12          83    0

Ahora, retomando el análisis sobre el significado de la variable piso, si fuera el piso en el que se encuentra, tendría sentido para los Apartamentos, pero no para las casas. Si fuera la cantidad de pisos, tendría sentido para las Casas de 1, 2 y hasta 3 pisos, pues, aunque posible, existe muy poca probabilidad de casas de 10, 7, 6 y 5 pisos, e inclusive las de 4 pisos son muy escasas. Me atrevería entonces a decir que para los registros de tipo Apartamento, el valor entre 1 y 12 tiene sentido. Para los de tipo Casa, los valores altos, mayores o iguales a 4 no tienen sentido o son muy poco probables. Veamos la moda para cada tipo:

mode_by_type <- viviendas %>%
  group_by(tipo) %>%
  summarise(
    moda_piso = mfv(viviendas$piso, na_rm = TRUE)
    )
mode_by_type
## # A tibble: 2 × 2
##   tipo        moda_piso
##   <chr>           <int>
## 1 Apartamento         2
## 2 Casa                2

Para ambos tipos el valor más común es 2 pisos.

Dado que las casas de más de 3 pisos son muy raras, en estos casos entonces lo que haremos es modificar o completar la variable piso con el valor 2, para todas las casa con piso > 3 y dejaremos los de valor igual o menor que 3 como están, pues existe mayor probabilidad de que si existan. En cuanto a los Apartamentos sin valor en piso, los eliminaremos del conjunto de datos. En cuanto a los números altos de la variable piso en los registros de tipo Apartamento, no tenemos razón para dudar de ellos, luego los dejaremos como están.

Veamos como queda el dataset en estas dos variables después de hacer el cambio:

viviendas$piso <- ifelse((viviendas$piso > 3 | is.na(viviendas$piso))  & viviendas$tipo == "Casa", 2, viviendas$piso)
viviendas <- filter(viviendas,!is.na(viviendas$piso))
missing_data <- colSums(is.na(viviendas)) %>%
                 as.data.frame() 

missing_data
##                 .
## zona            0
## piso            0
## estrato         0
## preciom         0
## areaconst       0
## parqueaderos 1270
## banios          0
## habitaciones    0
## tipo            0
## barrio          0

Ahora, analicemos los valores de la variable parqueadero, que ofrece los valores de la cantidad de parqueaderos de un inmueble. Esta es la otra variable que tiene valores en NA. Miremos la moda para los parqueaderos

moda = mfv(viviendas$parqueaderos, na_rm = TRUE)

moda
## [1] 1
table(viviendas$parqueaderos,viviendas$estrato,viviendas$tipo )
## , ,  = Apartamento
## 
##     
##        3   4   5   6
##   1  204 701 758  85
##   2    7 102 495 594
##   3    2   3  19 147
##   4    0   2  13  44
##   5    0   0   0   3
##   6    0   0   0   2
##   7    0   0   1   0
##   8    0   0   0   0
##   9    0   0   0   0
##   10   0   0   0   0
## 
## , ,  = Casa
## 
##     
##        3   4   5   6
##   1  277 317 235  28
##   2   79 197 390 225
##   3   17  47 106  98
##   4   10  21  84 181
##   5    1   4  27  32
##   6    3   4  13  46
##   7    0   2   4  11
##   8    0   0   3  14
##   9    0   1   1   2
##   10   1   1   0   4

Según esto, hay varios inmuebles que tienen desde 5 hasta 10 parqueaderos, valores que nos hacen dudar de su veracidad. Para ciertos tipos casas, es más posible que tengan muchos “lugares donde parquear” aunque técnicamente no sean parqueaderos cubiertos. Yo desconfiaría de los valores mayores de 4 inclusive para las casas. Vemos en las tablas que para los apartamentos, solo hay 6 que tienen valores mayores a 4. Se van a eliminar. En cuanto a las casas, hay muchos más registros con valores altos (parqueaderos > 4). Para eliminar los valores atípicos, vamos a asignarles el valor de 4 a dichos registros. En cuanto a los que no tienen dato, vamos a imputarlos con la moda que es 1.

viviendas$parqueaderos <- ifelse(viviendas$parqueaderos > 4, 4,  viviendas$parqueaderos)
viviendas$parqueaderos <- ifelse(is.na(viviendas$parqueaderos), 1, viviendas$parqueaderos)
table(viviendas$parqueaderos,viviendas$estrato,viviendas$tipo )
## , ,  = Apartamento
## 
##    
##       3   4   5   6
##   1 432 949 800 104
##   2   7 102 495 594
##   3   2   3  19 147
##   4   0   2  14  49
## 
## , ,  = Casa
## 
##    
##       3   4   5   6
##   1 703 448 356  83
##   2  79 197 390 225
##   3  17  47 106  98
##   4  15  33 132 290
sum(is.na(viviendas$parqueaderos))
## [1] 0

Ahora volvamos a ver si el conjunto tiene datos faltantes.

VIM::aggr(viviendas, cex.axis = 0.5, cex.lab= 0.8) 

md.pattern(viviendas, rotate.names=TRUE)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

##      zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo
## 6938    1    1       1       1         1            1      1            1    1
##         0    0       0       0         0            0      0            0    0
##      barrio  
## 6938      1 0
##           0 0

Se puede verificar que ya no tiene datos faltantes en las variables. Veamos cómo quedaron las variables con un summary del dataset.

summary(viviendas)
##      zona                piso           estrato         preciom      
##  Length:6938        Min.   : 1.000   Min.   :3.000   Min.   :  58.0  
##  Class :character   1st Qu.: 2.000   1st Qu.:4.000   1st Qu.: 225.0  
##  Mode  :character   Median : 2.000   Median :5.000   Median : 340.0  
##                     Mean   : 3.425   Mean   :4.611   Mean   : 442.2  
##                     3rd Qu.: 4.000   3rd Qu.:5.000   3rd Qu.: 550.0  
##                     Max.   :12.000   Max.   :6.000   Max.   :1999.0  
##    areaconst       parqueaderos       banios        habitaciones   
##  Min.   :  30.0   Min.   :1.000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:  84.0   1st Qu.:1.000   1st Qu.: 2.000   1st Qu.: 3.000  
##  Median : 133.5   Median :1.000   Median : 3.000   Median : 3.000  
##  Mean   : 186.2   Mean   :1.659   Mean   : 3.209   Mean   : 3.734  
##  3rd Qu.: 245.0   3rd Qu.:2.000   3rd Qu.: 4.000   3rd Qu.: 4.000  
##  Max.   :1745.0   Max.   :4.000   Max.   :10.000   Max.   :10.000  
##      tipo              barrio         
##  Length:6938        Length:6938       
##  Class :character   Class :character  
##  Mode  :character   Mode  :character  
##                                       
##                                       
## 

1.2. Análisis y limpieza de registros con valores atípicos

Ahora vamos a analizar las otras variables del conjunto. Visualizaremos en gráficos de cajas y bigotes la distribución de las variables numéricas.

numeric_columns <- names(viviendas)[sapply(viviendas, is.numeric)]
no_columns <- c("piso","estrato","parqueaderos")
numeric_columns <- setdiff(numeric_columns, no_columns)
lapply(numeric_columns, function(variable) {
  ggplot(viviendas, aes_string(x = variable)) +
    geom_boxplot() +
    labs(title = paste0("Boxplot para la columna: ", variable))
})
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

Podemos ver que las variables preciom y areaconst tienen una gran cantidad de valores atípicos, y también que según los datos, hay inmuebles con 8, 9 y 10 baños, así como también con 0 habitaciones o con hasta 10 habitaciones.

Vamos entonces a revisar si los valores de estas variables para esos registros son coherentes, por ejemplo, las casas con muchos baños o muchas habitaciones deberían ser las casas con un área construida mayor y también con más altos precios.

Antes de profundizar en el análisis, eliminaremos los registros con datos no lógicos, como areaconst = 0, habitac = 0 o baños = 0.

viviendas <- filter(viviendas, viviendas$banios != 0)
viviendas <- filter(viviendas, viviendas$habitaciones != 0)
viviendas <- filter(viviendas, viviendas$areaconst != 0)
numeric_columns <- names(viviendas)[sapply(viviendas, is.numeric)]
exclude_columns <- c("piso","estrato","parqueaderos")
numeric_columns <- setdiff(numeric_columns, exclude_columns)
cov_matrix <- cov(viviendas[, numeric_columns])
atipicos <- mahalanobis(viviendas[, numeric_columns], center = TRUE, cov = cov_matrix)
length(atipicos)
## [1] 6869

Veamos la relación entre las variables preciom, banios, areaconst y habitaciones. Se espera que entre más área construida, el valor sea más alto, o que entre más habitaciones existan, hayan más baños, y de igual manera, que entre más habitaciones y baños, más área construida tenga, al igual que mayor sea el precio. Comportamientos diferentes a estos, si bien pueden ser posibles en la práctica, son poco probables y alteran la homogeneidad de los datos. Para este estudio, si son pocos estos valores atípicos, se eliminarán.

Se representarán gráficamente las distancias de Mahalanobis, que es una medida de la distancia entre un punto y una distribución multivariante. Es una medida útil para detectar valores atípicos en un conjunto de datos multidimensional. La distancia es una medida de la distancia en unidades de desviaciones estándar, por lo tanto, un valor alto de la distancia indica que el punto esta lejos de la distribución multivariante. Típicamente, los puntos con una distancia mayor a 3 se consideran atípicos. La distancia Mahalanobis no se afecta por la escala de las variables y es sensible a la correlación entre las variables.

#ggplot(viviendas, aes_string(x = "preciom", y = "areaconst", color = "atipicos")) +
#  geom_point() +
#  scale_color_continuous(low = "white", high = "blue") +
#  labs(x = "Precio", y = "Área construida", color = "Distancia de Mahalanobis")

ggplot(viviendas, aes_string(x = 'preciom', y = 'areaconst', color = 'atipicos')) +
  geom_point() +
  scale_color_continuous(low = "white", high = "blue") +
  labs(x = "Precio", y = "Area construida", color = "Distancia de Mahalanobis")

En el gráfico anterior se pueden ver algunos datos que no son lógicos pues se asume una relación directamente proporcional entre el precio y el área construida de una casa. Por ejemplo, hay un registro de ~1750 metros cuadrados construidos con un valor de 250 millones. Se esperaría que para un área tan grande, el precio estuviera entre los más altos y por el contrario está entre los más bajos. También se ve el caso contrario, en el que el precio es muy alto para tan poca área construida. Aunque no se están teniendo en cuenta otras variables, como por ejemplo el estrato o el barrio, siempre hay una relación directa entre el tamaño y el valor que debe considerarse. En un estudio más detallado se podrían tener en cuenta los cruces entre estas cuatro variables mencionadas, o inclusive también con la cantidad de baños y de habitaciones.

ggplot(viviendas, aes_string(x = "banios", y = "areaconst", color = "atipicos")) +
  geom_point() +
  scale_color_continuous(low = "white", high = "blue") +
  labs(x = "Baños", y = "Área construida", color = "Distancia de Mahalanobis")

Se observa que hay unos registros con mucha área construida y pocos baños, como es el caso del registro que indica ~1750 metros construidos y 3 baños. También hay otros cercanos con 1600 metros construidos y 4 y 6 baños respectivamente, o uno de ~1400 metros con 1 baño. Se eliminarán estos registro.

viviendas <- filter(viviendas, viviendas$areaconst <= 1200)
atipicos <- mahalanobis(viviendas[, numeric_columns], center = TRUE, cov = cov_matrix)
length(atipicos)
## [1] 6861

Veamos la relación entre la cantidad de habitaciones y la cantidad de baños. Se espera que entre más habitaciones, más baños haya en el inmueble.

atipicos <- mahalanobis(viviendas[, numeric_columns], center = TRUE, cov = cov_matrix)
ggplot(viviendas, aes_string(x = "habitaciones", y = "banios", color = "atipicos")) +
  geom_point() +
  scale_color_continuous(low = "white", high = "blue") +
  labs(x = "Habitaciones", y = "Baños", color = "Distancia de Mahalanobis")

ggplot(viviendas, aes_string(x = "habitaciones", y = "areaconst", color = "atipicos")) +
  geom_point() +
  scale_color_continuous(low = "white", high = "blue") +
  labs(x = "Habitaciones", y = "Area", color = "Distancia de Mahalanobis")

Aparece un caso donde hay 3 habitaciones y 10 baños, y otro de 1 habitación y 6 baños. También, en el gráfico anterior, inmuebles con 10 baños y menos de 400 metros de área construida. Estos se eliminarán.

viviendas <- filter(viviendas, !(viviendas$banios == 10 & viviendas$areaconst <= 400))
viviendas <- filter(viviendas, !(viviendas$banios == 6 & viviendas$habitaciones <= 1))
table(viviendas$banios,viviendas$habitaciones)
##     
##         1    2    3    4    5    6    7    8    9   10
##   1    20   97  225   19    5    1    2    0    0    0
##   2    18  503 1509  163   56   23   10    5    1    0
##   3     4   77  888  460  105   55   34   24   14    3
##   4     0   15  378  509  187   78   36   38   12    9
##   5     0    3  135  312  175   68   42   34   22   11
##   6     0    0   13   90   82   56   25   18    9    7
##   7     0    0    2    6   42   24   14    6    7    4
##   8     0    0    0    2    3    9    8    9    9    6
##   9     0    0    0    0    1    0    1    4    7    2
##   10    0    0    0    0    0    0    0    0    1    3

De igual manera, se observa una aparente disparidad entre la cantidad de baños y el área construida. Aunque es posible que haya pocos baños en una casa grande, esto es poco probable. Tal vez una mayor relación la tienen las variables que representan las cantidades de baños y de habitaciones, pues las habitaciones dan una idea de la cantidad de personas que viven o usan la casa, lo cual muestra la necesidad de más o menos baños.

En un gráfico anterior se ve también que hay casas de 0 metros construidos con varias habitaciones, lo cual no es posible. Estos registros se eliminarán. Y también hay registros de inmuebles en el conjunto con muchos más baños que habitaciones, en una proporción de 2 a 1 e inclusive de 3 a 1. Eliminaremos estos registros problemáticos dado que no es mucha la cantidad de ellos.

1.3. Homogenización de variables cualitativas

Se revisaron los valores de las variables barrio y zona. Se notó que los nombres de las zonas están todos bien escritos, pero que en los nombres de los barrios hay algunos registros que se refieren al mismo barrio pero que están escritos de manera diferente, como por ejemplo: “alfonso lopez” y “alfonzo López”. También se nota que hay muchos barrios donde solo hay un inmueble a la venta. Con tan baja representatividad, no es posible hacer inferencias en cuanto a la situación de los inmuebles en los barrios. Habría que usar la zona que al ser solo 5, tienen más inmuebles en ellas.

Corregimos los nombres de algunos de los barrios para ver sus efectos en el dataset, sin embargo, se nota que sigue habiendo muchos barrios con tan solo un inmueble y que además son alrededor de 350 y algunos están posiblemente mal referenciados, como el que dice “cali” o el que hace referencia al nombre de un edificio. También se ve que están referenciadas las zonas en los barrios. En definitiva, esta variable requeriría de mucha limpieza e investigación para ser depurada, y teniendo también errores en la longitud y latitud, no se puede imputar a partir de los datos existentes.

unique(viviendas$zona)
## [1] "Zona Oriente" "Zona Sur"     "Zona Norte"   "Zona Oeste"   "Zona Centro"
viviendas$barrio <- tolower(viviendas$barrio)
barrios = unique(viviendas$barrio, decreasing = TRUE)
#table(viviendas$barrio, sort = TRUE)
#barrios

viviendas$barrio <- ifelse(viviendas$barrio == "alf√©rez real", "alferez real", ifelse(viviendas$barrio == "alfonso lópez", "alfonso lopez", ifelse(viviendas$barrio == "base a√©rea",  "base aerea", ifelse(viviendas$barrio == "mel√©ndez",  "melendez", ifelse(viviendas$barrio == "mel√©ndez",  "melendez", ifelse(viviendas$barrio == "mel√©ndez",  "melendez", ifelse(viviendas$barrio == "la arboleda",  "arboleda", ifelse(viviendas$barrio == "los alcázares",  "los alcazares", ifelse(viviendas$barrio == "la rivera i",  "la riverita", ifelse(viviendas$barrio == "laflora",  "la flora", ifelse(viviendas$barrio == "la rivera ii",  "la riverita", ifelse(viviendas$barrio == "la rivera",  "la riverita", ifelse(viviendas$barrio == "ciudadela paso ancho",  "ciudadela pasoancho", ifelse(viviendas$barrio == "caney especial",  "caney", ifelse(viviendas$barrio == "el ingenio i",  "ingenio", ifelse(viviendas$barrio == "el ingenio ii",  "ingenio", ifelse(viviendas$barrio == "el ingenio",  "ingenio", ifelse(viviendas$barrio == "el ingenio iii",  "el ingenio", ifelse(viviendas$barrio == "jamundi alfaguara",  "alfaguara", ifelse(viviendas$barrio == "cali canto viii",  "calicanto", ifelse(viviendas$barrio == "cali canto",  "calicanto", ifelse(viviendas$barrio == "chiminangos 1 etapa",  "chiminangos", ifelse(viviendas$barrio == "chiminangos 2 etapa",  "chiminangos", ifelse(viviendas$barrio == "cristóbal colón",  "cristobal colón", ifelse(viviendas$barrio == "marroquin iii",  "marroquin", ifelse(viviendas$barrio == "el ingenio 3",  "el ingenio", viviendas$barrio))))))))))))))))))))))))))

table(viviendas$barrio)
## 
##                   20 de julio                    3 de julio 
##                             3                             1 
##                         acopi                   agua blanca 
##                            75                             1 
##                    aguablanca                     aguacatal 
##                             2                            75 
##                       alameda               alameda del río 
##                            13                             1 
##                        alamos                      alborada 
##                             7                             1 
##                     alfaguara                  alferez real 
##                             1                             4 
##                 alfonso lopez               alfonso lópez i 
##                            21                             1 
##                   alto jordán            altos de guadalupe 
##                             1                             4 
##                altos de menga                antonio nariño 
##                             3                             2 
##                      aranjuez                      arboleda 
##                            15                             9 
## arboleda campestre candelaria                     arboledas 
##                             1                            31 
##             atanasio girardot                 autopista sur 
##                             8                             1 
##                  barranquilla             barrio 7de agosto 
##                             5                             1 
##            barrio eucarístico                 barrio obrero 
##                             1                             1 
##            barrio tranquilo y                    base aerea 
##                             1                             2 
##                    belalcazar             belisario caicedo 
##                             4                             2 
##                   bella suiza              bella suiza alta 
##                            17                             2 
##                    bellavista              benjamín herrera 
##                            28                             8 
##                        berlin           bloques del limonar 
##                             1                             1 
##                     bochalema           bosques del limonar 
##                            12                            21 
##                        boyacá                       bretaña 
##                             1                            14 
##           brisas de guadalupe                 brisas de los 
##                             1                            61 
##            brisas del guabito            brisas del limonar 
##                             1                             1 
##                  buenos aires                        caldas 
##                             6                             1 
##                          cali                     calibella 
##                            13                             1 
##                     calicanto                calicanto viii 
##                             9                             1 
##                        calima                 calimio norte 
##                             6                             5 
##                       calipso                      cambulos 
##                             9                             3 
##                   camino real                         caney 
##                            31                            69 
##                   cañasgordas                  cañaveralejo 
##                             7                            11 
##                   cañaverales       cañaverales los samanes 
##                            20                             1 
##                         capri                      cascajal 
##                            47                             1 
##                        ceibas                    centenario 
##                             1                            10 
##                        centro               cerro cristales 
##                             3                            19 
##           cerros de guadalupe                    champagnat 
##                             1                            14 
##                     chapinero                   chiminangos 
##                             7                            12 
##                    chipichape                   ciudad 2000 
##                            23                            91 
##             ciudad antejardin              ciudad bochalema 
##                             1                            44 
##                  ciudad capri                ciudad cordoba 
##                            12                            20 
##                ciudad córdoba      ciudad córdoba reservado 
##                            14                             1 
##                ciudad country              ciudad del campo 
##                             1                             1 
##                 ciudad jardin                 ciudad jardín 
##                            14                           470 
##           ciudad jardin pance             ciudad los álamos 
##                             1                            22 
##                 ciudad modelo               ciudad pacifica 
##                             7                             1 
##                   ciudad real                ciudad talanga 
##                             3                             1 
##          ciudad universitaria            ciudadela comfandi 
##                             1                            17 
##             ciudadela del río           ciudadela pasoancho 
##                             1                            22 
##            colinas del bosque               colinas del sur 
##                             1                             5 
##                         colon                    colseguros 
##                             1                            39 
##              colseguros andes                     compartir 
##                             5                             1 
##                     cristales               cristobal colón 
##                            53                            15 
##               cuarto de legua                 departamental 
##                            37                            26 
##           ed benjamin herrera                     el bosque 
##                             1                            49 
##                      el caney                   el castillo 
##                           188                             6 
##                      el cedro                   el diamante 
##                             8                             2 
##                     el dorado               el gran limonar 
##                             5                             7 
##                     el guabal                    el guabito 
##                            18                             1 
##                    el ingenio                     el jardín 
##                            18                            13 
##                       el lido                    el limonar 
##                            52                           120 
##                   el nacional                    el paraíso 
##                             1                             3 
##                      el peñon                      el prado 
##                            40                             1 
##                    el refugio                      el rodeo 
##                            99                             1 
##                       el sena                    el trébol 
##                             1                             5 
##                    el troncal                    el vallado 
##                            15                             1 
##                   eucarístico               evaristo garcía 
##                             2                             1 
##          farrallones de pance               fenalco kennedy 
##                             1                             1 
##                       fepicol              flora industrial 
##                             1                             9 
##                      floralia                  fonaviemcali 
##                             5                             1 
##      francisco eladio ramirez                 fuentes de la 
##                             1                             1 
##                  gran limonar                       granada 
##                            24                            12 
##                     guadalupe                     guayaquil 
##                            19                            15 
##         hacienda alferez real                       ingenio 
##                             1                           210 
##                     ingenio i                    ingenio ii 
##                             1                             1 
##                       jamundi          jorge eliecer gaitán 
##                             4                             1 
##                  jorge isaacs                     juanamb√∫ 
##                             1                            35 
##                      juanambu                         junin 
##                             1                            18 
##                         junín                   la alborada 
##                             6                             2 
##                    la alianza                       la base 
##                             4                            15 
##                   la buitrera                    la campiña 
##                             3                            11 
##                    la cascada                     la ceibas 
##                             6                             1 
##                  la esmeralda                      la flora 
##                             1                           315 
##                   la floresta                  la fortaleza 
##                            18                             4 
##              la gran colombia                   la hacienda 
##                             1                           148 
##              la independencia                   la libertad 
##                            12                             2 
##                     la merced                     la morada 
##                            23                             1 
##                 la nueva base                      la playa 
##                             8                             1 
##                 la portada al                  la primavera 
##                             1                             1 
##                    la reforma                   la riverita 
##                             1                            15 
##                    la riviera                      la selva 
##                             1                            10 
##                  la villa del                   las acacias 
##                             1                            12 
##                 las américas                  las camelias 
##                             2                             1 
##                    las ceibas                  las delicias 
##                            21                             3 
##                   las granjas                las quintas de 
##                             9                             1 
##                     las vegas                  las vegas de 
##                             1                             1 
##                  libertadores                 los alcazares 
##                             3                            12 
##                     los andes                  los cambulos 
##                            21                            22 
##                  los cámbulos                 los cristales 
##                             5                           123 
##            los cristales club                los farallones 
##                             1                             4 
##                 los guaduales                los guayacanes 
##                            20                             2 
##                   los jockeys              los libertadores 
##                             1                             3 
##      los parques barranquilla                       lourdes 
##                             3                             2 
##                      mamellan                    manzanares 
##                             1                             5 
##                 marroquín iii             mayapan las vegas 
##                             1                            41 
##                      melendez                         menga 
##                            59                            18 
##       metropolitano del norte         miradol del aguacatal 
##                            14                             1 
##                    miraflores          morichal de comfandi 
##                            26                             3 
##                   multicentro                     municipal 
##                            23                             3 
##                       napoles                       nápoles 
##                             1                            26 
##                     normandia                     normandía 
##                             3                           104 
##                         norte                norte la flora 
##                             1                             1 
##                    nueva base                nueva floresta 
##                             1                            15 
##              nueva tequendama             oasis de comfandi 
##                            70                             4 
##                     occidente                        pacara 
##                             1                            14 
##                        pacará            palmas del ingenio 
##                             4                             1 
##                   pampa linda                    pampalinda 
##                            22                            10 
##                  panamericano                         pance 
##                             9                           354 
##           parcelaciones pance         parque residencial el 
##                            54                             1 
##                  paseo de los             paso del comercio 
##                             2                             5 
##                     pasoancho             poblado campestre 
##                             4                             2 
##                         ponce                       popular 
##                             1                             6 
##           portada de comfandi          portales de comfandi 
##                             2                             1 
##                      porvenir             prados de oriente 
##                             3                             6 
##            prados del limonar              prados del norte 
##                            21                           104 
##                prados del sur                     primavera 
##                             2                             2 
##               primero de mayo              primitivo crespo 
##                            32                             3 
##           puente del comercio                  puente palma 
##                             6                             1 
##                quintas de don            quintas de salomia 
##                            64                             4 
##            rafael uribe uribe          rep√∫blica de israel 
##                             1                             1 
##             rincón de salomia             riveras del valle 
##                             1                             1 
##                 rozo la torre              saavedra galindo 
##                             1                             3 
##                       salomia                       samanes 
##                            35                             1 
##          samanes de guadalupe                        sameco 
##                             1                             1 
##                   san antonio                     san bosco 
##                            21                             7 
##                    san carlos                  san cayetano 
##                             4                             9 
##                  san fernando            san fernando nuevo 
##                            47                             9 
##            san fernando viejo                   san joaquin 
##                            14                             3 
##                   san joaquín                san juan bosco 
##                            16                             7 
##                     san judas               san judas tadeo 
##                             1                             2 
##                      san luis                      san luís 
##                             2                             1 
##                   san nicolas                   san nicolás 
##                             1                             1 
##                     san pedro                   san vicente 
##                             1                            47 
##                   santa anita               santa anita sur 
##                            44                             1 
##                 santa bárbara                   santa elena 
##                             3                             9 
##                      santa fe               santa helena de 
##                             8                             1 
##                  santa isabel                  santa monica 
##                            55                            47 
##                  santa mónica            santa monica norte 
##                             3                             1 
##          santa monica popular          santa mónica popular 
##                             1                             6 
##      santa monica residencial      santa mónica residencial 
##                             5                            32 
##                    santa rita                santa teresita 
##                            39                           173 
##                       santafe                     santander 
##                             1                             1 
##                 santo domingo sector cañaveralejo guadalupe 
##                             6                             1 
##                     seminario          sierras de normandía 
##                            24                             1 
##               siete de agosto                 simón bolivar 
##                             8                             1 
##             tejares cristales                tejares de san 
##                             4                            13 
##                      templete                    tequendama 
##                             4                            40 
##                    tequendema               terrón colorado 
##                             1                             1 
##            torres de comfandi             unión de vivienda 
##                            41                             3 
##     urbanización barranquilla           urbanización boyacá 
##                             3                             1 
##       urbanización colseguros         urbanizacion el saman 
##                             3                             1 
##         urbanización la flora        urbanización la merced 
##                            59                             4 
##         urbanización la nueva     urbanización las cascadas 
##                             4                             1 
##             urbanizacion lili    urbanización nueva granada 
##                             2                             3 
##         urbanización río lili      urbanización san joaquin 
##                             5                             4 
##       urbanización tequendama                 valle de lili 
##                             5                             1 
##                valle del lili                  valle grande 
##                           817                             1 
##                     versalles                villa colombia 
##                            57                             6 
##             villa de veracruz                villa del lago 
##                             4                            10 
##               villa del prado                 villa del sol 
##                            49                            21 
##                 villa del sur            villas de veracruz 
##                             2                             9 
##                        vipasa                   zona centro 
##                            30                             1 
##                    zona norte                zona norte los 
##                            28                             1 
##                    zona oeste                  zona oriente 
##                            24                            17 
##                      zona sur 
##                            71

Veo que a pesar de que se han estandarizado algunos nombres, estos y otros siguen teniendo pocos inmuebles. Aunque en un análisis más profundo o diferente el barrio podría ser necesario, para esta ocasión, usaremos para cualquier análisis la variable zona con la siguiente distribución. El barrio se eliminará del dataset.

viviendas$barrio <- NULL

1.4. Resumen de Variables y datos eliminados

Tal como hemos visto en el análisis, algunas de las variables no se utilizarán como parte del estudio. Estas variables son:

  • id
  • latitud
  • longitud
  • barrio

Las razones que se tuvieron en cuenta para su eliminación son:

  • Información improcedente para un análisis estadístico, como en el caso del id.
  • Información con muchos errores, valores atípicos o con distribuciones muy particulares que dan señal de estar equivocadas, como por ejemplo los valores en la latitud y la longitud.
  • Datos con muy poca representatividad en variables que segmentan los valores, como es el caso de la variable barrio, en donde había muchos barrios con solo un inmueble.
  • Variables completas que no aportan al objetivo del estudio (Definir su nicho, desarrollar estrategias de marketing, etc.) tal como se presenta en la descripción del trabajo.

También se eliminaron algunos registros con datos no lógicos, como los que tiene valores en cero para las variables área construida, habitaciones o baños, o casas de más 4 pisos, tal como se explicó durante el análisis.

1.5. Estado final del conjunto de datos

Veamos la distribución de inmuebles por zona

boxplot(viviendas$preciom~ viviendas$zona, data = viviendas, col = c("lightgray", "lightblue", "lightyellow","lightgreen","brown" ), ylab = "Precio", xlab = "Zona")

Se puede ver que los precios más altos se encuentran en las zonas con más valores atípicos (Norte, Oeste y Sur).

boxplot(viviendas$preciom ~ viviendas$estrato, data = viviendas, col = c("lightgray", "lightblue", "lightyellow","lightgreen"), ylab = "Precio", xlab = "Estrato")

No hay inmuebles de estratos 1 y 2. Las viviendas más costosas están en relación al estrato en el que están, lo que es esperable. Los estratos 3, 4 y 5 tienen una gran cantidad de valores atípicos, siendo el estrato 6 el de mayor rango y menos outliers.

Observamos la cantidad de datos por tipo de vivienda

## >>> suggestions
## PieChart(tipo, hole=0)  # traditional pie chart
## PieChart(tipo, labels="%")  # display %'s on the chart
## PieChart(tipo)  # bar chart
## Plot(tipo)  # bubble plot
## Plot(tipo, labels="count")  # lollipop plot 
## 
## --- tipo --- 
## 
##                Apartamento   Casa      Total 
## Frequencies:          3700   3155       6855 
## Proportions:         0.540  0.460      1.000 
## 
## Chi-squared test of null hypothesis of equal probabilities 
##   Chisq = 43.330, df = 1, p-value = 0.000

Y ahora vemos la cantidad de datos por zona

## >>> suggestions
## PieChart(zona, hole=0)  # traditional pie chart
## PieChart(zona, labels="%")  # display %'s on the chart
## PieChart(zona)  # bar chart
## Plot(zona)  # bubble plot
## Plot(zona, labels="count")  # lollipop plot 
## 
## --- zona --- 
## 
##         zona  Count   Prop 
## --------------------------- 
##  Zona Centro    112   0.016 
##   Zona Norte   1478   0.216 
##   Zona Oeste    864   0.126 
## Zona Oriente    327   0.048 
##     Zona Sur   4074   0.594 
## --------------------------- 
##        Total   6855   1.000 
## 
## Chi-squared test of null hypothesis of equal probabilities 
##   Chisq = 7476.093, df = 4, p-value = 0.000

Y por estrato:

PieChart(estrato, hole=0 , data=viviendas, fill="greens", main="Porcentajes por Estrato")

## >>> suggestions
## PieChart(estrato, hole=0)  # traditional pie chart
## PieChart(estrato, labels="%")  # display %'s on the chart
## PieChart(estrato)  # bar chart
## Plot(estrato)  # bubble plot
## Plot(estrato, labels="count")  # lollipop plot 
## 
## --- estrato --- 
## 
##                    3      4      5      6      Total 
## Frequencies:    1229   1760   2289   1577       6855 
## Proportions:   0.179  0.257  0.334  0.230      1.000 
## 
## Chi-squared test of null hypothesis of equal probabilities 
##   Chisq = 342.369, df = 3, p-value = 0.000

Ahora crearemos dos datasets uno para las variables numéricas y otro para las categóricas.

df_categorico <- data.frame(piso = viviendas$piso, zona = viviendas$zona, estrato = viviendas$estrato, tipo = viviendas$tipo, preciom = viviendas$preciom)
#df_categorico
df_numerico <- data.frame(preciom = viviendas$preciom, areaconst = viviendas$areaconst, parqueaderos = viviendas$parqueaderos, banios = viviendas$banios, habitaciones = viviendas$habitaciones)
#df_numerico

2. Informe

2.1. PCA - Análisis de componentes principales

Para el análisis de componentes principales comenzamos por estandarizar las variables a la misma escala:

df_estandarizado <- scale(df_numerico)
df_estandarizado <- as.data.frame(df_estandarizado)
summary(df_estandarizado)
##     preciom          areaconst        parqueaderos         banios       
##  Min.   :-1.1600   Min.   :-1.0738   Min.   :-0.7329   Min.   :-1.5441  
##  1st Qu.:-0.6535   1st Qu.:-0.6961   1st Qu.:-0.7329   1st Qu.:-0.8485  
##  Median :-0.3048   Median :-0.3605   Median :-0.7329   Median :-0.1530  
##  Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000  
##  3rd Qu.: 0.3321   3rd Qu.: 0.3948   3rd Qu.: 0.3762   3rd Qu.: 0.5425  
##  Max.   : 4.7267   Max.   : 7.1082   Max.   : 2.5942   Max.   : 4.7157  
##   habitaciones    
##  Min.   :-1.8510  
##  1st Qu.:-0.5105  
##  Median :-0.5105  
##  Mean   : 0.0000  
##  3rd Qu.: 0.1598  
##  Max.   : 4.1813
correlation<-round(cor(df_estandarizado), 1)
corrplot(correlation, method="number")

En esta tabla de correlaciones, podemos observar lo siguiente:

  • El preciom tiene una alta correlación con las variables areaconst, parqueaderos, y banios, lo que indica que para más área construida, más parqueaderos y más baños, se impactará al alza el precio. Se nota una baja correlación con habitaciones.
  • Para el área construida ( areaconst ), se nota alta correlación con baños ( banios ) y una correlacion moderada con parqueaderos y habitaciones.
  • Una correlación moderada se ve entre baños y habitaciones y Área construida.
  • Se puede decir que no hay correlación entre habitaciones y parqueaderos, y tampoco entre habitaciones y precio, lo que no esperado pues la cantidad de habitaciones aumenta típicamente el área construida y los baños.

Aplicamos el PCA para los datos con la escala estandarizada:

pca <- prcomp(df_estandarizado, center = TRUE, scale. = TRUE)
summary(pca)
## Importance of components:
##                           PC1    PC2     PC3     PC4     PC5
## Standard deviation     1.8001 0.9534 0.60937 0.54367 0.42870
## Proportion of Variance 0.6481 0.1818 0.07427 0.05912 0.03676
## Cumulative Proportion  0.6481 0.8299 0.90413 0.96324 1.00000
fviz_eig(pca, addlabels = TRUE)

#fviz_pca_biplot(pca, repel = TRUE, col.var = "#AA99FF") #Se comentó porque el resultado no es entendible.

En la gráfica se ve que el componente 1 representa el 64.8% de la variación, seguido del segundo con 18.2%, y entre los dos explicarían el 83% de la varianza.

fviz_contrib(pca, choice = "var", axes = 1, top = 10) # PC1

Se observa que en el componente 1 (PC1) las variables banios, areaconst y preciom tienen un mayor peso en ese orden.

fviz_contrib(pca, choice = "var", axes = 2, top = 10) # PC1

Se observa que en el componente 2 (PC2) las variables habitaciones y parqueaderos tienen un mayor peso, siendo habitaciones la que más incidencia tiene en el PC2.

get_pca_var(pca)$contrib[,1:2]
##                 Dim.1     Dim.2
## preciom      22.43416 13.529557
## areaconst    23.72177  1.080214
## parqueaderos 17.49359 25.359530
## banios       24.16256  1.918873
## habitaciones 12.18793 58.111826

Ahora se genera el siguiente gráfico para mostrar la influencia de las variables en un plano.

fviz_pca_var(pca, col.var = "contrib", gradient.cols = c("blue", "lightblue", "red"), repel = TRUE)

prop_varianza <- pca$sdev^2/sum(pca$sdev^2)
round(prop_varianza*100, 2)
## [1] 64.81 18.18  7.43  5.91  3.68
prop_varianza_acum <- cumsum(prop_varianza)
round(prop_varianza_acum*100, 2)
## [1]  64.81  82.99  90.41  96.32 100.00

Conclusiones

  • Si se aplicara únicamente las primeras dos componentes podríamos explicar el 82.99% de la varianza observada.
  • Si aplicamos el primer componente, se explicaría el 64.81% de la varianza observada.
  • En el PC1 las variables banios, areaconst y preciom, respectivamente, ejercen una mayor influencia.
  • En el PC2 las variables habitaciones y parqueaderos son las que ejercen mayor influencia.
  • Las variables del primer componente, están altamente correlacionadas entre si, como se vio en la matriz de correlación
  • Se observa que las variables areaconst, banios y habitaciones están en el cuarto cuadrante, siendo estas variables de características propias del inmueble. Las del primer cuadrante no.
  • Las variables areaconst, banios y habitaciones están relacionadas entre si, lo que indica que esots atributos suelen varias juntos, lo cual tiene sentido, pues a más baños y habitaciones, mayor es el área construida.
  • Por otro lado, las variables parqueaderos y preciom también están relacionadas entre si.
  • En cuanto a la contribución, la variable habitaciones es la que más contribuye. Esto quiere decir que esta variable es clave para explicar la variabilidad de los datos en las viviendas.
  • Todas las variables están al lado derecho del biplot, pero su ubicación en el primer o cuarto cuadrante podría implicar que entre las de variables ubicadas en estos dos cuadrantes existe poca relación o inclusibe inversamente relacionadas.
  • Las variables de cada cuadrante indican que estos grupos de suelen varias juntas.
  • Lo anterior podría implicar que inmuebles con más parqueaderos tienden a ser mas costosas, pero no necesariamente si tengo más
  • El Precio y la cantidad de habitaciones son las variables más dominantes en la explicación de las diferencias entre las viviendas.

2.2. Análisis de conglomerados

Esta análisis se realiza sobre los datos numéricos del punto anterior. Mediante el agrupamiento en base a la similaridad de los registros podemos descubrir grupos potenciales que pueden indicar nichos de mercado, que pueden ser estudiados de manera independiente. Para esto se usará k-means haciendo un clustering no jerárquico.

set.seed(689)

df_pca = df_estandarizado[, c('preciom', 'areaconst','parqueaderos', 'banios','habitaciones')]
fviz_nbclust(df_pca, kmeans, method = "silhouette") +
  labs(subtitle = "Método Silhouette")

df_pca=as.data.frame(scale(df_pca))
set.seed(689)
kmeans_result <- kmeans(df_pca, centers = 2, nstart = 25)
cluster_assigments <- kmeans_result$cluster
assigned_cluster <- df_pca %>% mutate(cluster = as.factor(cluster_assigments))
silhouette_res  = c()
for(i in 2:6){
    dist_ev <- dist(df_pca, method = 'euclidean')
    hc_ev <- hclust(dist_ev, method = 'complete')
    cluster_ev <- cutree(hc_ev, k = i)
    # Calcular el coeficiente de Silhouette
    sil <- silhouette(cluster_ev, dist(df_pca))
    sil_avg <- mean(sil[,3])
    silhouette_res  = c(silhouette_res, sil_avg)
}
sil_df <- data.frame(K = c(2,3,4,5,6),silhouette_value = silhouette_res)
sil_df
##   K silhouette_value
## 1 2        0.4973640
## 2 3        0.4872188
## 3 4        0.4692839
## 4 5        0.4496303
## 5 6        0.3316810
# kable(sil_df, "html") %>%
#   kable_styling("striped", full_width = FALSE) %>%
#   row_spec(0, bold = TRUE)

Estos resultados indican una mejor agrupación cuando se eligen 2 conglomerados (k = 2) con un valor de 0.4973, siendo este el más alto.

fviz_cluster(kmeans_result, data = df_pca, 
             geom = "point", main = "Clusters encontrados en el conjunto de datos",
             ellipse.type = "convex", 
             ggtheme = theme_minimal())

viv <- dist(df_pca, method = 'euclidean')

# Cluster jerarquico con el método complete
df_viv <- hclust(viv, method = 'complete')
cluster_f <- cutree(df_viv, k=2)
assigned_cluster_final <- df_pca %>% mutate(cluster = as.factor(cluster_f))
ggplot(assigned_cluster_final, aes(x = areaconst, y = preciom, color = cluster)) +
  geom_point(size = 2, alpha = 0.5) +
  geom_text(aes(label = cluster), vjust = -.8) +
  theme_classic()

Si hacemos el dendograma vemos lo siguiente:

plot(df_viv, cex = 0.6, main = "Dendograma de viviendas", las=1,
ylab = "Distancia euclidiana", xlab = "Grupos")
rect.hclust(df_viv, k = 2, border = 2:5)

En el dendograma no se puede observar realmente nada, así que podemos realizar un conteo de los registros de cada cluster.

cluster_counts <- table(cluster_f)
cluster_counts
## cluster_f
##    1    2 
## 6135  720
fviz_cluster(list(data = df_pca, cluster = cluster_f), 
             geom = "point", 
             ellipse.type = "convex",
             ggtheme = theme_minimal())

Conclusiones

  • Los resultados del análisis cluster muestran una división del mercado inmobiliario en dos segmentos claramente definidos.
  • Un cluster está compuesto por viviendas de mayor tamaño y valor, mientras que el otro agrupa propiedades más pequeñas y económicas.
  • La disparidad en el tamaño de los clusters sugiere una dominancia del segmento de alta gama, el cual presenta un tamaño considerablemente mayor, lo que podría indicar una tendencia hacia este tipo de propiedades en el mercado inmobiliario, con implicaciones potenciales para las políticas urbanas y las estrategias de inversión

2.3. Analisis de correspondencia

Este análisis se realizara usando el dataframe categórico creado anteriormente. La variables son: Piso, zona, estrato y tipo.

#df_categorico
tabla <- table(df_categorico$zona, df_categorico$estrato)
tabla
##               
##                   3    4    5    6
##   Zona Centro    99   11    1    1
##   Zona Norte    444  296  619  119
##   Zona Oeste     43   70  213  538
##   Zona Oriente  318    7    2    0
##   Zona Sur      325 1376 1454  919
tabla_chi <- table(df_categorico$piso, df_categorico$estrato)
chisq.test(tabla_chi)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_chi
## X-squared = 341.86, df = 33, p-value < 0.00000000000000022
# Realizar el análisis de correspondencia con FactoMineR
#resultado_ca_tz <- CA(tabla_contingencia_tz, graph=TRUE)

#valores_prop_tz <-resultado_ca_tz$eig
#valores_prop_tz
#viz_ca_biplot(resultado_ca_tz)

Este resultado indica que se rechaza la hipótesis de independencia de variables (porque el valor p es casi cero). Ahora vamos a realizar un análisis de correspondencia en donde se estiman las coordenadas para cada uno de los niveles de las variables. Veamos:

results <- CA(tabla)

Según esto, se puede ver que el estrato 6 está en la zona oeste, el 4 y 5 en la zona sur y el 3 en la zona centro y oriente. También que no hay una diferencia significativa en la zona norte

valores_prop <-results$eig ; valores_prop
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.34543799               73.69564                          73.69564
## dim 2 0.10979467               23.42356                          97.11920
## dim 3 0.01350335                2.88080                         100.00000
fviz_screeplot(results, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
  ylab("Porcentaje de varianza explicado") + xlab("Ejes")

Se puede ver que el primer componente tiene el 73.7% del total de la varianza, y que el segundo explica el 23.4% de la varianza. Si se suman estos dos componentes, explicarían el 97.1% de la varianza total de los datos.

generar_colores_aleatorios <- function(n) {
  hsv(h = runif(n), s = 0.9, v = 0.9)
}

# Asignar colores aleatorios a las zonas
colores_aleatorios <- generar_colores_aleatorios(length(unique(df_categorico$zona)))

ggplot(df_categorico, aes(x = zona, y = preciom, fill = zona)) +
 geom_boxplot() +
 scale_fill_manual(values = colores_aleatorios) +  # Asigna colores aleatorios
 labs(title = "Distribucion Precio por Zona") +
 theme_minimal()  # Personaliza el tema del gráfico

Conclusiones del Análisis de Componentes Principales

Componente Principal 1 (73.7%):

  • Explica la mayor parte de la variabilidad.
  • Diferencia principalmente entre las zonas Oriente y Centro.

Componente Principal 2 (23.4%):

  • Aporta información adicional.
  • Distingue principalmente la Zona Oeste.

Zonas Geográficas:

  • Oriente y Centro: Altamente correlacionadas con el Componente 1.
  • Oeste: Fuertemente asociado con el Componente 2.
  • Norte y Sur: Menor asociación con ambos componentes.

Estratos Socioeconómicos:

  • Estrato 6: Más cercano a la Zona Oeste.
  • Estrato 4 y 5: Cercanos a las Zonas Sur y Norte.
  • Estrato 3: Próximo a la Zona Centro.

3. Conclusiones Finales

  • Conjunto de Datos

Si bien, es posible hacer una limpieza del conjunto de datos, este tenia bastantes datos incorrectos, atípicos y faltantes. Se recomienda tener más cuidado con la fuente y generación de los datos.

  • Zonas con mejor promedio de precios

La Zona Oeste presenta un mejor promedio de precios tanto para las casas como para los apartamentos, constituyéndose en una zona de mucho interés, dado que las inmobiliarias ganan por comisión, y entre mayor sea el valor de la propiedad, más dinero se ganará. La siguen la zona sur y la zona norte. El caso de la zona centro es particular, pues hay pocas casas, pero su promedio de precio no es el más bajito.

  • Tipo de inmueble más ofertado

Los apartamentos son el tipo de inmueble con mayor cantidad de ofertas en la ciudad de Cali superando en cantidad a las casas en la zona Norte, Oeste y Sur. En las zonas Centro y Oriente, hay más casas ofertadas, en una proporción aproximada de 6 a 1.

  • Mejor tipo de Vivienda

Aunque hay más apartamentos que casas, las casas presentan mayores valores en su precio, lo que las hace más atractivas. Las zonas con más alto promedio de venta son la Oeste, la Sur, a Norte, la Centro y la Oriente. Nótese que la Centro tiene pocas ofertas, pero sus casas tienen mejor promedio que las de la zona Oriente.

  • Estrato con mayor cantidad de inmuebles

El estrato 5 es el estrato con mayor cantidad de oferta de venta de inmuebles. Si bien, esto no es en si información contundente, si es una muestra de que las casas que más se comercializan son las de valores medio-altos, y esto puede dar un indicio de la clase de compradores que se deben buscar.

LS0tDQp0aXRsZTogIk1vZGVsb3MgRXN0YWTDrXN0aWNvcyBwYXJhIGxhIFRvbWEgZGUgRGVjaXNpb25lcyINCmF1dGhvcjogIkVucmlxdWUgSm9zw6kgUGXDsWEiDQpkYXRlOiAiMjAyNC0wOC0wMSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBoaWdobGlnaHQ6IGthdGUNCiAgICBwZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICB0dWZ0ZTo6dHVmdGVfaGFuZG91dDoNCiAgICBsYXRleF9lbmdpbmU6IHhlbGF0ZXgNCiAgYm9va2Rvd246OnBkZl9kb2N1bWVudDI6DQogICAgbGF0ZXhfZW5naW5lOiBsdWFsYXRleA0KICB3b3JkX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNA0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiA0DQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4DQpudW1iZXItc2VjdGlvbnM6IHRydWUNCnN1YnRpdGxlOiAiQWN0aXZpZGFkIDEgLSBNYWVzdHLDrWEgZW4gQ2llbmNpYSBkZSBEYXRvcyAtIFBVSiBDYWxpIg0KLS0tDQojIEludHJvZHVjY2nDs24NCg0KIyMgUHJvYmxlbWENClVuYSBlbXByZXNhIGlubW9iaWxpYXJpYSBsw61kZXIgZW4gdW5hIGdyYW4gY2l1ZGFkIGVzdMOhIGJ1c2NhbmRvIGNvbXByZW5kZXIgZW4gcHJvZnVuZGlkYWQgZWwgbWVyY2FkbyBkZSB2aXZpZW5kYXMgdXJiYW5hcyBwYXJhIHRvbWFyIGRlY2lzaW9uZXMgZXN0cmF0w6lnaWNhcyBtw6FzIGluZm9ybWFkYXMuIExhIGVtcHJlc2EgcG9zZWUgdW5hIGJhc2UgZGUgZGF0b3MgZXh0ZW5zYSBxdWUgY29udGllbmUgaW5mb3JtYWNpw7NuIGRldGFsbGFkYSBzb2JyZSBkaXZlcnNhcyBwcm9waWVkYWRlcyByZXNpZGVuY2lhbGVzIGRpc3BvbmlibGVzIGVuIGVsIG1lcmNhZG8uIFNlIHJlcXVpZXJlIHJlYWxpemFyIHVuIGFuw6FsaXNpcyBob2zDrXN0aWNvIGRlIGVzdG9zIGRhdG9zIHBhcmEgaWRlbnRpZmljYXIgcGF0cm9uZXMsIHJlbGFjaW9uZXMgeSBzZWdtZW50YWNpb25lcyByZWxldmFudGVzIHF1ZSBwZXJtaXRhbiBtZWpvcmFyIGxhIHRvbWEgZGUgZGVjaXNpb25lcyBlbiBjdWFudG8gYSBsYSBjb21wcmEsIHZlbnRhIHkgdmFsb3JhY2nDs24gZGUgcHJvcGllZGFkZXMuDQoNCiMjIFJldG9zDQoNCkVsIHJldG8gcHJpbmNpcGFsIGNvbnNpc3RlbiBlbiByZWFsaXphciB1biBhbsOhbGlzaXMgaW50ZWdyYWwgeSBtdWx0aWRpbWVuc2lvbmFsIGRlIGxhIGJhc2UgZGUgZGF0b3MgcGFyYSBvYnRlbmVyIHVuYSBjb21wcmVuc2nDs24gZGVsIG1lcmNhZG8gaW5tb2JpbGlhcmlvIHVyYmFuby4gU2UgcmVxdWllcmUgYXBsaWNhciBkaXZlcnNhcyB0w6ljbmljYXMgZGUgYW7DoWxpc2lzIGRlIGRhdG9zLCBpbmNsdXllbmRvOg0KDQoqKkFuw6FsaXNpcyBkZSBDb21wb25lbnRlcyBQcmluY2lwYWxlczoqKiBSZWR1Y2lyIGxhIGRpbWVuc2lvbmFsaWRhZCBkZWwgY29uanVudG8gZGUgZGF0b3MgeSB2aXN1YWxpemFyIGxhIGVzdHJ1Y3R1cmEgZGUgbGFzIHZhcmlhYmxlcyBlbiBjb21wb25lbnRlcyBwcmluY2lwYWxlcyBwYXJhIGlkZW50aWZpY2FyIGNhcmFjdGVyw61zdGljYXMgY2xhdmUgcXVlIGluZmx1eWVuIGVuIGxhIHZhcmlhY2nDs24gZGUgcHJlY2lvcyB5IG9mZXJ0YSBkZWwgbWVyY2Fkby4NCg0KKipBbsOhbGlzaXMgZGUgQ29uZ2xvbWVyYWRvczoqKiBBZ3J1cGFyIGxhcyBwcm9waWVkYWRlcyByZXNpZGVuY2lhbGVzIGVuIHNlZ21lbnRvcyBob21vZ8OpbmVvcyBjb24gY2FyYWN0ZXLDrXN0aWNhcyBzaW1pbGFyZXMgcGFyYSBlbnRlbmRlciBsYXMgZGluw6FtaWNhcyBkZSBsYXMgb2ZlcnRhcyBlc3BlY8OtZmljYXMgZW4gZGlmZXJlbnRlcyBwYXJ0ZXMgZGUgbGEgY2l1ZGFkIHkgZW4gZGlmZXJlbnRlcyBlc3RyYXRvcyBzb2Npby1lY29uw7NtaWNvcy4NCg0KKipBbsOhbGlzaXMgZGUgQ29ycmVzcG9uZGVuY2lhOioqIEV4YW1pbmFyIGxhIHJlbGFjacOzbiBlbnRyZSBsYXMgdmFyaWFibGVzIGNhdGVnw7NyaWNhcyAodGlwbyBkZSB2aXZpZW5kYSwgem9uYSB5IGJhcnJpbyksIHBhcmEgaWRlbnRpZmljYXIgcGF0cm9uZXMgZGUgY29tcG9ydGFtaWVudG8gZGUgbGEgb2ZlcnRhIGVuIG1lcmNhZG8gaW5tb2JpbGlhcmlvLg0KDQoqKlZpc3VhbGl6YWNpw7NuIGRlIHJlc3VsdGFkb3M6KiogUHJlc2VudGFyIGdyw6FmaWNvcywgbWFwYXMgeSBvdHJvcyByZWN1cnNvcyB2aXN1YWxlcyBwYXJhIGNvbXVuaWNhciBsb3MgaGFsbGF6Z29zIGRlIG1hbmVyYSBjbGFyYSB5IGVmZWN0aXZhIGEgbGEgZGlyZWNjacOzbiBkZSBsYSBlbXByZXNhLg0KDQpFbCBpbmZvcm1lIGZpbmFsIGRlYmUgaW5jbHVpciBhbsOhbGlzaXMgZGV0YWxsYWRvcyBkZSBsb3MgcmVzdWx0YWRvcyBvYnRlbmlkb3MsIGxhcyBjb25jbHVzaW9uZXMgY2xhdmUgeSBsYXMgcmVjb21lbmRhY2lvbmVzIGVzcGVjw61maWNhcyBwYXJhIGd1aWFyIGxhcyBkZWNpc2lvbmVzIGVzdHJhdMOpZ2ljYXMgZGUgbGEgZW1wcmVzYSBpbm1vYmlsaWFyaWEuIFNlIGVzcGVyYSBxdWUgZXN0ZSBhbsOhbGlzaXMgZGUgZGF0b3MgcHJvcG9yY2lvbmUgdmVudGFqYXMgY29tcGV0aXRpdmFzIGVuIGVsIG1lcmNhZG8sIG9wdGltaXphbmRvIGxhIGludmVyc2nDs24geSBtYXhpbWl6YW5kbyBsb3MgYmVuZWZpY2lvcyBlbiB1biBlbnRvcm5vIGFsdGFtZW50ZSBjb21wZXRpdGl2byB5IGVuIGNvbnN0YW50ZSBjYW1iaW8uDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KDQpSLnZlcnNpb24NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJnZ3Bsb3QyIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiZGV2dG9vbHMiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgibW9kZWVzdCIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoIm1vZGVlc3QiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoIm91dGxpZXJzIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygib3V0bGllcnMiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoImRwbHlyIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoInRpZHl2ZXJzZSIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgibGVzc1IiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJsZXNzUiIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiVklNIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiVklNIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJnZ3RoZW1lcyIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJHR2FsbHkiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoIlJDb2xvckJyZXdlciIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgibm9ydGVzdCIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoIm5vcnRlc3QiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoInN0YXJnYXplciIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoInN0YXJnYXplciIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgicGFxdWV0ZU1FVE9ET1MiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJwYXF1ZXRlTUVUT0RPUyIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiY29ycnBsb3QiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJjb3JycGxvdCIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiTUFTUyIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoIkZhY3RvTWluZVIiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJGYWN0b01pbmVSIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJmYWN0b2V4dHJhIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiY2x1c3RlciIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoIm1pY2UiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJtaWNlIikNCn0NCmxpYnJhcnkoRmFjdG9NaW5lUikNCmxpYnJhcnkob3V0bGllcnMpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShkZXZ0b29scykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsZXNzUikNCmxpYnJhcnkoVklNKQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KG5vcnRlc3QpDQpsaWJyYXJ5KHN0YXJnYXplcikNCmxpYnJhcnkocGFxdWV0ZU1FVE9ET1MpDQpsaWJyYXJ5KGNvcnJwbG90KQ0KbGlicmFyeShNQVNTKQ0KbGlicmFyeShtb2RlZXN0KQ0KbGlicmFyeShmYWN0b2V4dHJhKQ0KbGlicmFyeShjbHVzdGVyKQ0KbGlicmFyeShtaWNlKQ0KYGBgDQoNCmBgYHtyIGxvYWRfZGF0YX0NCiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImRnb254YWxleDgwL3BhcXVldGVNT0RFTE9TIiwgZm9yY2UgPSBUUlVFKQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiY2VudHJvbWFnaXMvcGFxdWV0ZU1PREVMT1MiLCBmb3JjZSA9IFRSVUUpDQojbGlicmFyeShwYXF1ZXRlTU9ERUxPUykNCmRhdGEodml2aWVuZGEpDQpzdHIodml2aWVuZGEpDQpgYGANCiMjIERlc2NyaXBjacOzbiB2YXJpYWJsZXMgYmFzZSBkZSBkYXRvcw0KDQpBIGNvbnRpbnVhY2nDs24gc2UgbWVuY2lvbmFuIGxhcyB2YXJpYWJsZXMgcXVlIHNlIHV0aWxpemFyw6FuIGVuIGVsIGVqZXJjaWNpby4NCg0KKipab25hOioqIFJlZ2lzdHJhIGxhIHpvbmEgZG9uZGUgZXN0YSB1YmljYWRhIGVsIGlubXVlYmxlIGVzdGEgcHVlZGUgc2VyOiBab25hIENlbnRybyxab25hIE5vcnRlLFpvbmEgT2VzdGUsWm9uYSBPcmllbnRlLFpvbmEgU3VyDQoNCioqUGlzbyoqOiBSZWdpc3RyYSBlbCBwaXNvIGVuZSBsIGN1YWwgc2UgdWJpY2EgbGEgdml2aWVuZGENCg0KKipFc3RyYXRvOioqIFZhcmlhYmxlIGNvbiBlc2NhbGEgZGUgbWVkaWNpw7NuIG9yZGluYWwsbG9zIGVzdHJhdG8gZGUgbGFzIHZpdmllbmRhcyBzb24gMyw0LDUsNg0KDQoqKlByZWNpb206KiogUHJlY2lvIGRlIGxhIHZpdmllbmRhDQoNCioqYXJlYWNvbnN0dToqKiBBcmVhIENvbnN0cnVpZGENCg0KKipwYXJxdWVhZGVyb3M6KiogTnVtZXJvIGRlIHBhcnF1ZWFkZXJvcyBkZSBsYSB2aXZpZW5kYQ0KDQoqKmJhbmlvczoqKiBOw7ptZXJvIGRlIGJhw7FvcyBkZSBsYSBwcm9waWVkYWQNCg0KKipIYWJpdGFjaW9uZXM6KiogTsO6bWVybyBkZSBoYWJpdGFjaW9uZXMgcXVlIHBvc2VlIGxhIHZpdmllbmRhDQoNCioqVGlwbzoqKiBUaXBvIGRlIGxhIHZpdmllbmRhLCBjYXNhIG8gYXBhcnRhbWVudG8NCg0KKk5vdGE6KiBTZSBlbGltaW5hcm9uIHZhcmlhYmxlcyBxdWUgbm8gY29udHJpYnV5ZW4gZW4gZWwgZWplcmNpY2lvIG5pIGFwb3J0YW4gaW5mb3JtYWNpw7NuIHJlbGV2YW50ZSBwYXJhIGVsIG1vZGVsbywgdGFsZXMgY29tbzogSWQsIExhdGl0dWQgeSBMb25naXR1ZA0KDQoNCiMgMS4gTGltcGllemEgZGVsIF9EYXRhc2V0Xw0KDQojIyAxLjEgQW7DoWxpc2lzIHkgTGltcGllemEgSW5pY2lhbCBkZWwgX0RhdGFzZXRfDQoNCmBgYHtyIGNsZWFuX2VtcHR5X3JlY29yZHNfYW5kX2RlbGV0ZV9jb2x1bW5zX3BsdXNfc3VtbWFyeSB9DQojdml2aWVuZGENCnZpdmllbmRhcyA8LSBzdWJzZXQodml2aWVuZGEsICFpcy5uYShpZCkpDQojdml2aWVuZGENCnZpdmllbmRhcyRsYXRpdHVkIDwtIE5VTEwNCnZpdmllbmRhcyRsb25naXR1ZCA8LSBOVUxMDQp2aXZpZW5kYXMkaWQgPC0gTlVMTA0KI3ZpdmllbmRhDQpzdW1tYXJ5KHZpdmllbmRhcykNCmBgYA0KU2Ugb2JzZXJ2YW4gZGF0b3MgZmFsdGFudGVzIGVuIGxhcyB2YXJpYWJsZXMgcGlzbyB5IHBhcnF1ZWFkZXJvLiAgUGFyYSB2ZXJsbyBncsOhZmljYW1lbnRlLCBzZSB1c2EgZWwgc2lndWllbnRlIGPDs2RpZ286DQpgYGB7ciBwYXR0ZXJufQ0KbWlzc2luZyA8LSBjb2xTdW1zKGlzLm5hKHZpdmllbmRhcykpICU+JQ0KICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkNCg0KI21pc3NpbmcNClZJTTo6YWdncih2aXZpZW5kYXMsIGNleC5heGlzID0gMC41LCBjZXgubGFiPSAwLjgpICANCg0KbWQucGF0dGVybih2aXZpZW5kYXMsIHJvdGF0ZS5uYW1lcz1UUlVFKQ0KYGBgDQoNClNlIHZlbiBkb3MgdmFyaWFibGVzIGNvbiB1bmEgYnVlbmEgY2FudGlkYWQgZGUgZGF0b3MgZmFsdGFudGVzLCBwaXNvICgyNjM1KSB5IHBhcnF1ZWFkZXJvcyAoMTYwMikuICBBbmFsaWNlbW9zIGRpY2hhcyB2YXJpYWJsZXMuIEVtcGVjZW1vcyBwb3IgcGlzbzoNCmBgYHtyIHN1bW1hcnlfcGlzb30NCnZpdmllbmRhcyRwaXNvIDwtIGFzLmludGVnZXIodml2aWVuZGFzJHBpc28pDQpwaXNvcyA8LSBzdW1tYXJ5dG9vbHM6OmRlc2NyKHZpdmllbmRhcyRwaXNvKQ0KcGlzb3MNCnRhYmxlKHZpdmllbmRhcyRwaXNvKQ0KYGBgDQpQb2RlbW9zIG9ic2VydmFyIGFsZ3VuYXMgY29zYXMgaW1wb3J0YW50ZXMuIENvbW8gdmltb3MgaGF5IDI2MzUgcmVnaXN0cm9zIHNpbiBkYXRvIGVuIGxhIGNvbHVtbmEgcGlzby4gTG8gc2VndW5kbywgcXVlIGVsIG1lbm9yIHZhbG9yIGVzIDEgeSBlbCBtYXlvciBlcyAxMiwgY29uIHVuIGJ1ZW4gbsO6bWVybyBkZSByZWdpc3Ryb3MgY29uIHZhbG9yZXMgYWx0b3MuIFBvciBlbCBub21icmUgZGUgbGEgdmFyaWFibGUgbm8gc2FiZW1vcyBzaSBlbCB2YWxvciBjb3JyZXNwb25kZSBhbCBuw7ptZXJvIGRlbCBwaXNvIGVuIGVsIHF1ZSBxdWVkYSBlbCBpbm11ZWJsZSBlbiB1biBlZGlmaWNpbywgbyBlbCBuw7ptZXJvIGRlIHBpc29zIHF1ZSB0aWVuZSBlbCBpbm11ZWJsZS4gU2kgZnVlcmEgbG8gc2VndW5kbywgcXVlcnLDrWEgZGVjaXIgcXVlIGhheSBpbm11ZWJsZXMgcXVlIHRpZW5lbiAxMiBwaXNvcywgbG8gcXVlIHRlbmRyw61hIHNlbnRpZG8gc2kgZnVlcmFuIGVkaWZpY2lvcy4gU2kgZnVlcmEgbG8gcHJpbWVybywgc2lnbmlmaWNhIHF1ZSBoYXkgaW5tdWVibGVzIGVuIGVsIGRvY2Vhdm8gcGlzbyBkZSB1biBlZGlmaWNpbywgYWwgaWd1YWwgcXVlIGVuIGVsIGTDqWNpbW8sIGVsIG5vdmVubywgeSBhc8OtIHN1Y2VzaXZhbWVudGUsIExvIHF1ZSB0aWVuZSBtdWNobyBtw6FzIHNlbnRpZG8sIGRhZG8gbG8gZXh0cmVtYWRhbWVudGUgcmFybyBxdWUgc2Vyw61hIHF1ZSB1bmEgY2FzYSBvIHVuIGFwYXJ0YW1lbnRvIHR1dmllcmEgMTIgcGlzb3MuIFBhcmEgdHJhdGFyIGRlIGVudGVuZGVyIGEgcXVlIHNlIHJlZmllcmUsIHZhbW9zIGEgdmVyIHByaW1lcm8gYSBxdWUgdGlwbyBkZSBpbm11ZWJsZSBjb3JyZXNwb25kZW4gbG9zIHJlZ2lzdHJvcyBxdWUgdGllbmVuIHVuIG7Dum1lcm8gYWx0by4NCmBgYHtyIGFuYWxpc3lzX3dpdGhwaXNvfQ0KcGlzb05uYSA8LSBzdWJzZXQodml2aWVuZGFzLCAhaXMubmEodml2aWVuZGFzJHBpc28pKSAjc3Vic2V0IHBpc29ObmEgY29udGFpbnMgdGhlIHJlY29yZHMgd2l0aCB2YWx1ZSBpbiBjb2x1bW4gcGlzbw0KdGFibGUocGlzb05uYSR0aXBvKQ0KYGBgDQpFeGlzdGVuIDM3MTkgQXBhcnRhbWVudG9zIGNvbiB2YWxvciBlbiBsYSB2YXJpYWJsZSBwaXNvLCB5IDE5NjUgQ2FzYXMgY29uIGlndWFsIGNvbmRpY2nDs24uICBQb2RlbW9zIHZlciB0YW1iacOpbiBxdWUgbm8gaGF5IGlubXVlYmxlcyBkZSB0aXBvIGVkaWZpY2lvLg0KYGBge3IgYW5hbGlzeXNfd2l0aG91dHBpc299DQpwaXNvbmEgPC0gc3Vic2V0KHZpdmllbmRhcywgaXMubmEodml2aWVuZGFzJHBpc28pKSAjc3Vic2V0IHBpc29ObmEgY29udGFpbnMgdGhlIHJlY29yZHMgd2l0aCB2YWx1ZSBpbiBjb2x1bW4gcGlzbw0KdGFibGUocGlzb25hJHRpcG8pDQpgYGANCg0KRXhpc3RlIHVuYSBwcm9wb3JjacOzbiBiYXN0YW50ZSBzaW1pbGFyIGVudHJlIENhc2FzIHkgQXBhcnRhbWVudG9zIHNpbiBwaXNvLiBQb2RlbW9zIHZlciB0YW1iacOpbiBxdWUgbm8gaGF5IGlubXVlYmxlcyBkZSB0aXBvIGVkaWZpY2lvLCBsbyBxdWUgbm8gYXBveWEgZWwgY2FzbyBtZW5jaW9uYWRvIGRlIHF1ZSBsYSB2YXJpYWJsZSBzZSByZWZpZXJlIGFsIHBpc28gZW4gZWwgcXVlIHNlIGVuY3VlbnRyYSBlbCBpbm11ZWJsZSBlbiB1biBlZGlmaWNpbywgYWRlbcOhcyBkZSBxdWUgbm8gaGF5IGNhc2FzIGVuIGVkaWZpY2lvcywgc2lubyBhcGFydGFtZW50b3MuIEFob3JhIHZlYW1vcyBjb21vIHNlIGNydXphIGVsIFRpcG8gY29uIGxvcyB2YWxvcmVzIGRlIGxhIGNvbHVtbmEgUGlzby4NCg0KYGBge3IgUGlzb19WU19UaXBvfQ0KdGFibGUodml2aWVuZGFzJHBpc28sdml2aWVuZGFzJHRpcG8pDQpgYGANCkFob3JhLCByZXRvbWFuZG8gZWwgYW7DoWxpc2lzIHNvYnJlIGVsIHNpZ25pZmljYWRvIGRlIGxhIHZhcmlhYmxlIHBpc28sIHNpIGZ1ZXJhIGVsIHBpc28gZW4gZWwgcXVlIHNlIGVuY3VlbnRyYSwgdGVuZHLDrWEgc2VudGlkbyBwYXJhIGxvcyBBcGFydGFtZW50b3MsIHBlcm8gbm8gcGFyYSBsYXMgY2FzYXMuIFNpIGZ1ZXJhIGxhIGNhbnRpZGFkIGRlIHBpc29zLCB0ZW5kcsOtYSBzZW50aWRvIHBhcmEgbGFzIENhc2FzIGRlIDEsIDIgeSBoYXN0YSAzIHBpc29zLCBwdWVzLCBhdW5xdWUgcG9zaWJsZSwgZXhpc3RlIG11eSBwb2NhIHByb2JhYmlsaWRhZCBkZSBjYXNhcyBkZSAxMCwgNywgNiB5IDUgcGlzb3MsIGUgaW5jbHVzaXZlIGxhcyBkZSA0IHBpc29zIHNvbiBtdXkgZXNjYXNhcy4gTWUgYXRyZXZlcsOtYSBlbnRvbmNlcyBhIGRlY2lyIHF1ZSBwYXJhIGxvcyByZWdpc3Ryb3MgZGUgdGlwbyBBcGFydGFtZW50bywgZWwgdmFsb3IgZW50cmUgMSB5IDEyIHRpZW5lIHNlbnRpZG8uIFBhcmEgbG9zIGRlIHRpcG8gQ2FzYSwgbG9zIHZhbG9yZXMgYWx0b3MsIG1heW9yZXMgbyBpZ3VhbGVzIGEgNCBubyB0aWVuZW4gc2VudGlkbyBvIHNvbiBtdXkgcG9jbyBwcm9iYWJsZXMuICBWZWFtb3MgbGEgbW9kYSBwYXJhIGNhZGEgdGlwbzoNCg0KYGBge3IgbW9kZV9ieV90eXBlX3Bpc299DQptb2RlX2J5X3R5cGUgPC0gdml2aWVuZGFzICU+JQ0KICBncm91cF9ieSh0aXBvKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIG1vZGFfcGlzbyA9IG1mdih2aXZpZW5kYXMkcGlzbywgbmFfcm0gPSBUUlVFKQ0KICAgICkNCm1vZGVfYnlfdHlwZQ0KYGBgDQpQYXJhIGFtYm9zIHRpcG9zIGVsIHZhbG9yIG3DoXMgY29tw7puIGVzIDIgcGlzb3MuDQoNCkRhZG8gcXVlIGxhcyBjYXNhcyBkZSBtw6FzIGRlIDMgcGlzb3Mgc29uIG11eSByYXJhcywgZW4gZXN0b3MgY2Fzb3MgZW50b25jZXMgbG8gcXVlIGhhcmVtb3MgZXMgbW9kaWZpY2FyIG8gY29tcGxldGFyIGxhIHZhcmlhYmxlIHBpc28gY29uIGVsIHZhbG9yIDIsIHBhcmEgdG9kYXMgbGFzIGNhc2EgY29uIHBpc28gPiAzIHkgZGVqYXJlbW9zIGxvcyBkZSB2YWxvciBpZ3VhbCBvIG1lbm9yIHF1ZSAzIGNvbW8gZXN0w6FuLCBwdWVzIGV4aXN0ZSBtYXlvciBwcm9iYWJpbGlkYWQgZGUgcXVlIHNpIGV4aXN0YW4uIEVuIGN1YW50byBhIGxvcyBBcGFydGFtZW50b3Mgc2luIHZhbG9yIGVuIHBpc28sIGxvcyBlbGltaW5hcmVtb3MgZGVsIGNvbmp1bnRvIGRlIGRhdG9zLiBFbiBjdWFudG8gYSBsb3MgbsO6bWVyb3MgYWx0b3MgZGUgbGEgdmFyaWFibGUgcGlzbyBlbiBsb3MgcmVnaXN0cm9zIGRlIHRpcG8gQXBhcnRhbWVudG8sIG5vIHRlbmVtb3MgcmF6w7NuIHBhcmEgZHVkYXIgZGUgZWxsb3MsIGx1ZWdvIGxvcyBkZWphcmVtb3MgY29tbyBlc3TDoW4uDQoNClZlYW1vcyBjb21vIHF1ZWRhIGVsIGRhdGFzZXQgZW4gZXN0YXMgZG9zIHZhcmlhYmxlcyBkZXNwdcOpcyBkZSBoYWNlciBlbCBjYW1iaW86DQoNCmBgYHtyIGNsZWFuX3Bpc299DQp2aXZpZW5kYXMkcGlzbyA8LSBpZmVsc2UoKHZpdmllbmRhcyRwaXNvID4gMyB8IGlzLm5hKHZpdmllbmRhcyRwaXNvKSkgICYgdml2aWVuZGFzJHRpcG8gPT0gIkNhc2EiLCAyLCB2aXZpZW5kYXMkcGlzbykNCnZpdmllbmRhcyA8LSBmaWx0ZXIodml2aWVuZGFzLCFpcy5uYSh2aXZpZW5kYXMkcGlzbykpDQptaXNzaW5nX2RhdGEgPC0gY29sU3Vtcyhpcy5uYSh2aXZpZW5kYXMpKSAlPiUNCiAgICAgICAgICAgICAgICAgYXMuZGF0YS5mcmFtZSgpIA0KDQptaXNzaW5nX2RhdGENCmBgYA0KQWhvcmEsIGFuYWxpY2Vtb3MgbG9zIHZhbG9yZXMgZGUgbGEgdmFyaWFibGUgcGFycXVlYWRlcm8sIHF1ZSBvZnJlY2UgbG9zIHZhbG9yZXMgZGUgbGEgY2FudGlkYWQgZGUgcGFycXVlYWRlcm9zIGRlIHVuIGlubXVlYmxlLiBFc3RhIGVzIGxhIG90cmEgdmFyaWFibGUgcXVlIHRpZW5lIHZhbG9yZXMgZW4gTkEuIE1pcmVtb3MgbGEgbW9kYSBwYXJhIGxvcyBwYXJxdWVhZGVyb3MgDQoNCg0KYGBge3IgbW9kZV9wYXJxdWVhZGVyb3N9DQptb2RhID0gbWZ2KHZpdmllbmRhcyRwYXJxdWVhZGVyb3MsIG5hX3JtID0gVFJVRSkNCg0KbW9kYQ0KYGBgDQoNCmBgYHtyIGFuYWxpc3lzX3BhcnF1ZWFkcm99DQp0YWJsZSh2aXZpZW5kYXMkcGFycXVlYWRlcm9zLHZpdmllbmRhcyRlc3RyYXRvLHZpdmllbmRhcyR0aXBvICkNCmBgYA0KU2Vnw7puIGVzdG8sIGhheSB2YXJpb3MgaW5tdWVibGVzIHF1ZSB0aWVuZW4gZGVzZGUgNSBoYXN0YSAxMCBwYXJxdWVhZGVyb3MsIHZhbG9yZXMgcXVlIG5vcyBoYWNlbiBkdWRhciBkZSBzdSB2ZXJhY2lkYWQuIFBhcmEgY2llcnRvcyB0aXBvcyBjYXNhcywgZXMgbcOhcyBwb3NpYmxlIHF1ZSB0ZW5nYW4gbXVjaG9zIOKAnGx1Z2FyZXMgZG9uZGUgcGFycXVlYXLigJ0gYXVucXVlIHTDqWNuaWNhbWVudGUgbm8gc2VhbiBwYXJxdWVhZGVyb3MgY3ViaWVydG9zLiBZbyBkZXNjb25maWFyw61hIGRlIGxvcyB2YWxvcmVzIG1heW9yZXMgZGUgNCBpbmNsdXNpdmUgcGFyYSBsYXMgY2FzYXMuIFZlbW9zIGVuIGxhcyB0YWJsYXMgcXVlIHBhcmEgbG9zIGFwYXJ0YW1lbnRvcywgc29sbyBoYXkgNiBxdWUgdGllbmVuIHZhbG9yZXMgbWF5b3JlcyBhIDQuIFNlIHZhbiBhIGVsaW1pbmFyLiBFbiBjdWFudG8gYSBsYXMgY2FzYXMsIGhheSBtdWNob3MgbcOhcyByZWdpc3Ryb3MgY29uIHZhbG9yZXMgYWx0b3MgKHBhcnF1ZWFkZXJvcyA+IDQpLiBQYXJhIGVsaW1pbmFyIGxvcyB2YWxvcmVzIGF0w61waWNvcywgdmFtb3MgYSBhc2lnbmFybGVzIGVsIHZhbG9yIGRlIDQgYSBkaWNob3MgcmVnaXN0cm9zLiBFbiBjdWFudG8gYSBsb3MgcXVlIG5vIHRpZW5lbiBkYXRvLCB2YW1vcyBhIGltcHV0YXJsb3MgY29uIGxhIG1vZGEgcXVlIGVzIDEuDQoNCmBgYHtyIGNsZWFuX3BhcnF1ZWFkZXJvc30NCnZpdmllbmRhcyRwYXJxdWVhZGVyb3MgPC0gaWZlbHNlKHZpdmllbmRhcyRwYXJxdWVhZGVyb3MgPiA0LCA0LCAgdml2aWVuZGFzJHBhcnF1ZWFkZXJvcykNCnZpdmllbmRhcyRwYXJxdWVhZGVyb3MgPC0gaWZlbHNlKGlzLm5hKHZpdmllbmRhcyRwYXJxdWVhZGVyb3MpLCAxLCB2aXZpZW5kYXMkcGFycXVlYWRlcm9zKQ0KdGFibGUodml2aWVuZGFzJHBhcnF1ZWFkZXJvcyx2aXZpZW5kYXMkZXN0cmF0byx2aXZpZW5kYXMkdGlwbyApDQpgYGANCmBgYHtyIGNvdW50X3BhcnF1ZWFkZXJvc30NCnN1bShpcy5uYSh2aXZpZW5kYXMkcGFycXVlYWRlcm9zKSkNCmBgYA0KQWhvcmEgdm9sdmFtb3MgYSB2ZXIgc2kgZWwgY29uanVudG8gdGllbmUgZGF0b3MgZmFsdGFudGVzLg0KYGBge3IgbWlzc2luZ192YWx1ZXN9DQpWSU06OmFnZ3Iodml2aWVuZGFzLCBjZXguYXhpcyA9IDAuNSwgY2V4LmxhYj0gMC44KSANCm1kLnBhdHRlcm4odml2aWVuZGFzLCByb3RhdGUubmFtZXM9VFJVRSkNCmBgYA0KDQpTZSBwdWVkZSB2ZXJpZmljYXIgcXVlIHlhIG5vIHRpZW5lIGRhdG9zIGZhbHRhbnRlcyBlbiBsYXMgdmFyaWFibGVzLiBWZWFtb3MgY8OzbW8gcXVlZGFyb24gbGFzIHZhcmlhYmxlcyBjb24gdW4gc3VtbWFyeSBkZWwgZGF0YXNldC4NCg0KYGBge3J9DQpzdW1tYXJ5KHZpdmllbmRhcykNCmBgYA0KDQoNCiMjIDEuMi4gQW7DoWxpc2lzIHkgbGltcGllemEgZGUgcmVnaXN0cm9zIGNvbiB2YWxvcmVzIGF0w61waWNvcw0KDQpBaG9yYSB2YW1vcyBhIGFuYWxpemFyIGxhcyBvdHJhcyB2YXJpYWJsZXMgZGVsIGNvbmp1bnRvLiBWaXN1YWxpemFyZW1vcw0KZW4gZ3LDoWZpY29zIGRlIGNhamFzIHkgYmlnb3RlcyBsYSBkaXN0cmlidWNpw7NuIGRlIGxhcyB2YXJpYWJsZXMNCm51bcOpcmljYXMuDQoNCmBgYHtyIGJsb3hfcGxvdHN9DQpudW1lcmljX2NvbHVtbnMgPC0gbmFtZXModml2aWVuZGFzKVtzYXBwbHkodml2aWVuZGFzLCBpcy5udW1lcmljKV0NCm5vX2NvbHVtbnMgPC0gYygicGlzbyIsImVzdHJhdG8iLCJwYXJxdWVhZGVyb3MiKQ0KbnVtZXJpY19jb2x1bW5zIDwtIHNldGRpZmYobnVtZXJpY19jb2x1bW5zLCBub19jb2x1bW5zKQ0KbGFwcGx5KG51bWVyaWNfY29sdW1ucywgZnVuY3Rpb24odmFyaWFibGUpIHsNCiAgZ2dwbG90KHZpdmllbmRhcywgYWVzX3N0cmluZyh4ID0gdmFyaWFibGUpKSArDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIGxhYnModGl0bGUgPSBwYXN0ZTAoIkJveHBsb3QgcGFyYSBsYSBjb2x1bW5hOiAiLCB2YXJpYWJsZSkpDQp9KQ0KYGBgDQoNClBvZGVtb3MgdmVyIHF1ZSBsYXMgdmFyaWFibGVzICoqcHJlY2lvbSoqIHkgKiphcmVhY29uc3QqKiB0aWVuZW4gdW5hDQpncmFuIGNhbnRpZGFkIGRlIHZhbG9yZXMgYXTDrXBpY29zLCB5IHRhbWJpw6luIHF1ZSBzZWfDum4gbG9zIGRhdG9zLCBoYXkNCmlubXVlYmxlcyBjb24gOCwgOSB5IDEwIGJhw7FvcywgYXPDrSBjb21vIHRhbWJpw6luIGNvbiAwIGhhYml0YWNpb25lcyBvIGNvbg0KaGFzdGEgMTAgaGFiaXRhY2lvbmVzLg0KDQpWYW1vcyBlbnRvbmNlcyBhIHJldmlzYXIgc2kgbG9zIHZhbG9yZXMgZGUgZXN0YXMgdmFyaWFibGVzIHBhcmEgZXNvcw0KcmVnaXN0cm9zIHNvbiBjb2hlcmVudGVzLCBwb3IgZWplbXBsbywgbGFzIGNhc2FzIGNvbiBtdWNob3MgYmHDsW9zIG8NCm11Y2hhcyBoYWJpdGFjaW9uZXMgZGViZXLDrWFuIHNlciBsYXMgY2FzYXMgY29uIHVuIMOhcmVhIGNvbnN0cnVpZGEgbWF5b3INCnkgdGFtYmnDqW4gY29uIG3DoXMgYWx0b3MgcHJlY2lvcy4NCg0KQW50ZXMgZGUgcHJvZnVuZGl6YXIgZW4gZWwgYW7DoWxpc2lzLCBlbGltaW5hcmVtb3MgbG9zIHJlZ2lzdHJvcyBjb24NCmRhdG9zIG5vIGzDs2dpY29zLCBjb21vIGFyZWFjb25zdCA9IDAsIGhhYml0YWMgPSAwIG8gYmHDsW9zID0gMC4NCg0KYGBge3IgZGVsZXRlX2lsb2dpY2FsX3JlY29yZHN9DQp2aXZpZW5kYXMgPC0gZmlsdGVyKHZpdmllbmRhcywgdml2aWVuZGFzJGJhbmlvcyAhPSAwKQ0Kdml2aWVuZGFzIDwtIGZpbHRlcih2aXZpZW5kYXMsIHZpdmllbmRhcyRoYWJpdGFjaW9uZXMgIT0gMCkNCnZpdmllbmRhcyA8LSBmaWx0ZXIodml2aWVuZGFzLCB2aXZpZW5kYXMkYXJlYWNvbnN0ICE9IDApDQpgYGANCg0KYGBge3Igb3V0bGllcnNffQ0KbnVtZXJpY19jb2x1bW5zIDwtIG5hbWVzKHZpdmllbmRhcylbc2FwcGx5KHZpdmllbmRhcywgaXMubnVtZXJpYyldDQpleGNsdWRlX2NvbHVtbnMgPC0gYygicGlzbyIsImVzdHJhdG8iLCJwYXJxdWVhZGVyb3MiKQ0KbnVtZXJpY19jb2x1bW5zIDwtIHNldGRpZmYobnVtZXJpY19jb2x1bW5zLCBleGNsdWRlX2NvbHVtbnMpDQpjb3ZfbWF0cml4IDwtIGNvdih2aXZpZW5kYXNbLCBudW1lcmljX2NvbHVtbnNdKQ0KYXRpcGljb3MgPC0gbWFoYWxhbm9iaXModml2aWVuZGFzWywgbnVtZXJpY19jb2x1bW5zXSwgY2VudGVyID0gVFJVRSwgY292ID0gY292X21hdHJpeCkNCmxlbmd0aChhdGlwaWNvcykNCg0KYGBgDQoNClZlYW1vcyBsYSByZWxhY2nDs24gZW50cmUgbGFzIHZhcmlhYmxlcyAqKnByZWNpb20qKiwgKipiYW5pb3MqKiwNCioqYXJlYWNvbnN0KiogeSAqKmhhYml0YWNpb25lcyoqLiBTZSBlc3BlcmEgcXVlIGVudHJlIG3DoXMgw6FyZWEgY29uc3RydWlkYSwgZWwNCnZhbG9yIHNlYSBtw6FzIGFsdG8sIG8gcXVlIGVudHJlIG3DoXMgaGFiaXRhY2lvbmVzIGV4aXN0YW4sIGhheWFuIG3DoXMNCmJhw7FvcywgeSBkZSBpZ3VhbCBtYW5lcmEsIHF1ZSBlbnRyZSBtw6FzIGhhYml0YWNpb25lcyB5IGJhw7FvcywgbcOhcyDDoXJlYQ0KY29uc3RydWlkYSB0ZW5nYSwgYWwgaWd1YWwgcXVlIG1heW9yIHNlYSBlbCBwcmVjaW8uIENvbXBvcnRhbWllbnRvcw0KZGlmZXJlbnRlcyBhIGVzdG9zLCBzaSBiaWVuIHB1ZWRlbiBzZXIgcG9zaWJsZXMgZW4gbGEgcHLDoWN0aWNhLCBzb24gcG9jbw0KcHJvYmFibGVzIHkgYWx0ZXJhbiBsYSBob21vZ2VuZWlkYWQgZGUgbG9zIGRhdG9zLiBQYXJhIGVzdGUgZXN0dWRpbywgc2kNCnNvbiBwb2NvcyBlc3RvcyB2YWxvcmVzIGF0w61waWNvcywgc2UgZWxpbWluYXLDoW4uDQoNClNlIHJlcHJlc2VudGFyw6FuIGdyw6FmaWNhbWVudGUgbGFzICoqZGlzdGFuY2lhcyBkZSBNYWhhbGFub2JpcywqKiBxdWUgZXMgdW5hDQoqbWVkaWRhIGRlIGxhIGRpc3RhbmNpYSBlbnRyZSB1biBwdW50byB5IHVuYSBkaXN0cmlidWNpw7NuIG11bHRpdmFyaWFudGUuKg0KRXMgdW5hIG1lZGlkYSDDunRpbCBwYXJhIGRldGVjdGFyIHZhbG9yZXMgYXTDrXBpY29zIGVuIHVuIGNvbmp1bnRvIGRlDQpkYXRvcyBtdWx0aWRpbWVuc2lvbmFsLiBMYSBkaXN0YW5jaWEgZXMgdW5hIG1lZGlkYSBkZSBsYSBkaXN0YW5jaWEgZW4NCnVuaWRhZGVzIGRlIGRlc3ZpYWNpb25lcyBlc3TDoW5kYXIsIHBvciBsbyB0YW50bywgdW4gdmFsb3IgYWx0byBkZSBsYQ0KZGlzdGFuY2lhIGluZGljYSBxdWUgZWwgcHVudG8gZXN0YSBsZWpvcyBkZSBsYSBkaXN0cmlidWNpw7NuDQptdWx0aXZhcmlhbnRlLiBUw61waWNhbWVudGUsIGxvcyBwdW50b3MgY29uIHVuYSBkaXN0YW5jaWEgbWF5b3IgYSAzIHNlDQpjb25zaWRlcmFuIGF0w61waWNvcy4gTGEgZGlzdGFuY2lhIE1haGFsYW5vYmlzIG5vIHNlIGFmZWN0YSBwb3IgbGEgZXNjYWxhDQpkZSBsYXMgdmFyaWFibGVzIHkgZXMgc2Vuc2libGUgYSBsYSBjb3JyZWxhY2nDs24gZW50cmUgbGFzIHZhcmlhYmxlcy4NCg0KYGBge3IgTWFoYWxhbm9iaXNfZGlzdGFuY2VfcHJpY2VfYXJlYX0NCg0KI2dncGxvdCh2aXZpZW5kYXMsIGFlc19zdHJpbmcoeCA9ICJwcmVjaW9tIiwgeSA9ICJhcmVhY29uc3QiLCBjb2xvciA9ICJhdGlwaWNvcyIpKSArDQojICBnZW9tX3BvaW50KCkgKw0KIyAgc2NhbGVfY29sb3JfY29udGludW91cyhsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArDQojICBsYWJzKHggPSAiUHJlY2lvIiwgeSA9ICLDgXJlYSBjb25zdHJ1aWRhIiwgY29sb3IgPSAiRGlzdGFuY2lhIGRlIE1haGFsYW5vYmlzIikNCg0KZ2dwbG90KHZpdmllbmRhcywgYWVzX3N0cmluZyh4ID0gJ3ByZWNpb20nLCB5ID0gJ2FyZWFjb25zdCcsIGNvbG9yID0gJ2F0aXBpY29zJykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfY29sb3JfY29udGludW91cyhsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArDQogIGxhYnMoeCA9ICJQcmVjaW8iLCB5ID0gIkFyZWEgY29uc3RydWlkYSIsIGNvbG9yID0gIkRpc3RhbmNpYSBkZSBNYWhhbGFub2JpcyIpDQoNCg0KYGBgDQoNCkVuIGVsIGdyw6FmaWNvIGFudGVyaW9yIHNlIHB1ZWRlbiB2ZXIgYWxndW5vcyBkYXRvcyBxdWUgbm8gc29uIGzDs2dpY29zDQpwdWVzIHNlIGFzdW1lIHVuYSByZWxhY2nDs24gZGlyZWN0YW1lbnRlIHByb3BvcmNpb25hbCBlbnRyZSBlbCBwcmVjaW8geQ0KZWwgw6FyZWEgY29uc3RydWlkYSBkZSB1bmEgY2FzYS4gUG9yIGVqZW1wbG8sIGhheSB1biByZWdpc3RybyBkZSB+MTc1MA0KbWV0cm9zIGN1YWRyYWRvcyBjb25zdHJ1aWRvcyBjb24gdW4gdmFsb3IgZGUgMjUwIG1pbGxvbmVzLiBTZSBlc3BlcmFyw61hDQpxdWUgcGFyYSB1biDDoXJlYSB0YW4gZ3JhbmRlLCBlbCBwcmVjaW8gZXN0dXZpZXJhIGVudHJlIGxvcyBtw6FzIGFsdG9zIHkNCnBvciBlbCBjb250cmFyaW8gZXN0w6EgZW50cmUgbG9zIG3DoXMgYmFqb3MuIFRhbWJpw6luIHNlIHZlIGVsIGNhc28NCmNvbnRyYXJpbywgZW4gZWwgcXVlIGVsIHByZWNpbyBlcyBtdXkgYWx0byBwYXJhIHRhbiBwb2NhIMOhcmVhDQpjb25zdHJ1aWRhLiBBdW5xdWUgbm8gc2UgZXN0w6FuIHRlbmllbmRvIGVuIGN1ZW50YSBvdHJhcyB2YXJpYWJsZXMsIGNvbW8NCnBvciBlamVtcGxvIGVsIGVzdHJhdG8gbyBlbCBiYXJyaW8sIHNpZW1wcmUgaGF5IHVuYSByZWxhY2nDs24gZGlyZWN0YQ0KZW50cmUgZWwgdGFtYcOxbyB5IGVsIHZhbG9yIHF1ZSBkZWJlIGNvbnNpZGVyYXJzZS4gRW4gdW4gZXN0dWRpbyBtw6FzDQpkZXRhbGxhZG8gc2UgcG9kcsOtYW4gdGVuZXIgZW4gY3VlbnRhIGxvcyBjcnVjZXMgZW50cmUgZXN0YXMgY3VhdHJvDQp2YXJpYWJsZXMgbWVuY2lvbmFkYXMsIG8gaW5jbHVzaXZlIHRhbWJpw6luIGNvbiBsYSBjYW50aWRhZCBkZSBiYcOxb3MgeSBkZQ0KaGFiaXRhY2lvbmVzLg0KDQpgYGB7ciBNYWhhbGFub2Jpc19kaXN0YW5jZV9iYXRocm9vbXNfYXJlYX0NCmdncGxvdCh2aXZpZW5kYXMsIGFlc19zdHJpbmcoeCA9ICJiYW5pb3MiLCB5ID0gImFyZWFjb25zdCIsIGNvbG9yID0gImF0aXBpY29zIikpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfY29sb3JfY29udGludW91cyhsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArDQogIGxhYnMoeCA9ICJCYcOxb3MiLCB5ID0gIsOBcmVhIGNvbnN0cnVpZGEiLCBjb2xvciA9ICJEaXN0YW5jaWEgZGUgTWFoYWxhbm9iaXMiKQ0KDQpgYGANCg0KU2Ugb2JzZXJ2YSBxdWUgaGF5IHVub3MgcmVnaXN0cm9zIGNvbiBtdWNoYSDDoXJlYSBjb25zdHJ1aWRhIHkgcG9jb3MNCmJhw7FvcywgY29tbyBlcyBlbCBjYXNvIGRlbCByZWdpc3RybyBxdWUgaW5kaWNhIH4xNzUwIG1ldHJvcyBjb25zdHJ1aWRvcyB5DQozIGJhw7Fvcy4gVGFtYmnDqW4gaGF5IG90cm9zIGNlcmNhbm9zIGNvbiAxNjAwIG1ldHJvcyBjb25zdHJ1aWRvcyB5IDQgeSA2IGJhw7FvcyByZXNwZWN0aXZhbWVudGUsIG8gdW5vIGRlIH4xNDAwIG1ldHJvcyBjb24gMSBiYcOxby4gU2UgZWxpbWluYXLDoW4gZXN0b3MgcmVnaXN0cm8uDQoNCmBgYHtyIGRlbGV0ZV8xNzUwfQ0Kdml2aWVuZGFzIDwtIGZpbHRlcih2aXZpZW5kYXMsIHZpdmllbmRhcyRhcmVhY29uc3QgPD0gMTIwMCkNCmF0aXBpY29zIDwtIG1haGFsYW5vYmlzKHZpdmllbmRhc1ssIG51bWVyaWNfY29sdW1uc10sIGNlbnRlciA9IFRSVUUsIGNvdiA9IGNvdl9tYXRyaXgpDQpsZW5ndGgoYXRpcGljb3MpDQpgYGANCg0KVmVhbW9zIGxhIHJlbGFjacOzbiBlbnRyZSBsYSBjYW50aWRhZCBkZSBoYWJpdGFjaW9uZXMgeSBsYSBjYW50aWRhZCBkZQ0KYmHDsW9zLiBTZSBlc3BlcmEgcXVlIGVudHJlIG3DoXMgaGFiaXRhY2lvbmVzLCBtw6FzIGJhw7FvcyBoYXlhIGVuIGVsDQppbm11ZWJsZS4NCg0KYGBge3IgTWFoYWxhbm9iaXNfZGlzdGFuY2Vfcm9vbXNfYmF0aHJvb21zfQ0KYXRpcGljb3MgPC0gbWFoYWxhbm9iaXModml2aWVuZGFzWywgbnVtZXJpY19jb2x1bW5zXSwgY2VudGVyID0gVFJVRSwgY292ID0gY292X21hdHJpeCkNCmdncGxvdCh2aXZpZW5kYXMsIGFlc19zdHJpbmcoeCA9ICJoYWJpdGFjaW9uZXMiLCB5ID0gImJhbmlvcyIsIGNvbG9yID0gImF0aXBpY29zIikpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfY29sb3JfY29udGludW91cyhsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArDQogIGxhYnMoeCA9ICJIYWJpdGFjaW9uZXMiLCB5ID0gIkJhw7FvcyIsIGNvbG9yID0gIkRpc3RhbmNpYSBkZSBNYWhhbGFub2JpcyIpDQoNCmdncGxvdCh2aXZpZW5kYXMsIGFlc19zdHJpbmcoeCA9ICJoYWJpdGFjaW9uZXMiLCB5ID0gImFyZWFjb25zdCIsIGNvbG9yID0gImF0aXBpY29zIikpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfY29sb3JfY29udGludW91cyhsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArDQogIGxhYnMoeCA9ICJIYWJpdGFjaW9uZXMiLCB5ID0gIkFyZWEiLCBjb2xvciA9ICJEaXN0YW5jaWEgZGUgTWFoYWxhbm9iaXMiKQ0KYGBgDQoNCkFwYXJlY2UgdW4gY2FzbyBkb25kZSBoYXkgMyBoYWJpdGFjaW9uZXMgeSAxMCBiYcOxb3MsIHkgb3RybyBkZSAxIGhhYml0YWNpw7NuIHkgNiBiYcOxb3MuIFRhbWJpw6luLCBlbiBlbCBncsOhZmljbyBhbnRlcmlvciwgaW5tdWVibGVzIGNvbiAxMCBiYcOxb3MgeSBtZW5vcyBkZSA0MDAgbWV0cm9zIGRlIMOhcmVhDQpjb25zdHJ1aWRhLiBFc3RvcyBzZSBlbGltaW5hcsOhbi4NCg0KYGBge3IgZGVsX3Rvb19tYW55X2JhdGhyb29tc19mZXdfb3RoZXJzfQ0Kdml2aWVuZGFzIDwtIGZpbHRlcih2aXZpZW5kYXMsICEodml2aWVuZGFzJGJhbmlvcyA9PSAxMCAmIHZpdmllbmRhcyRhcmVhY29uc3QgPD0gNDAwKSkNCnZpdmllbmRhcyA8LSBmaWx0ZXIodml2aWVuZGFzLCAhKHZpdmllbmRhcyRiYW5pb3MgPT0gNiAmIHZpdmllbmRhcyRoYWJpdGFjaW9uZXMgPD0gMSkpDQpgYGANCg0KYGBge3IgcmVsYXRpb25zfQ0KdGFibGUodml2aWVuZGFzJGJhbmlvcyx2aXZpZW5kYXMkaGFiaXRhY2lvbmVzKQ0KYGBgDQoNCkRlIGlndWFsIG1hbmVyYSwgc2Ugb2JzZXJ2YSB1bmEgYXBhcmVudGUgZGlzcGFyaWRhZCBlbnRyZSBsYSBjYW50aWRhZCBkZQ0KYmHDsW9zIHkgZWwgw6FyZWEgY29uc3RydWlkYS4gQXVucXVlIGVzIHBvc2libGUgcXVlIGhheWEgcG9jb3MgYmHDsW9zIGVuDQp1bmEgY2FzYSBncmFuZGUsIGVzdG8gZXMgcG9jbyBwcm9iYWJsZS4gVGFsIHZleiB1bmEgbWF5b3IgcmVsYWNpw7NuIGxhDQp0aWVuZW4gbGFzIHZhcmlhYmxlcyBxdWUgcmVwcmVzZW50YW4gbGFzIGNhbnRpZGFkZXMgZGUgYmHDsW9zIHkgZGUNCmhhYml0YWNpb25lcywgcHVlcyBsYXMgaGFiaXRhY2lvbmVzIGRhbiB1bmEgaWRlYSBkZSBsYSBjYW50aWRhZCBkZQ0KcGVyc29uYXMgcXVlIHZpdmVuIG8gdXNhbiBsYSBjYXNhLCBsbyBjdWFsIG11ZXN0cmEgbGEgbmVjZXNpZGFkIGRlIG3DoXMgbw0KbWVub3MgYmHDsW9zLg0KDQpFbiB1biBncsOhZmljbyBhbnRlcmlvciBzZSB2ZSB0YW1iacOpbiBxdWUgaGF5IGNhc2FzIGRlIDAgbWV0cm9zIGNvbnN0cnVpZG9zIGNvbg0KdmFyaWFzIGhhYml0YWNpb25lcywgbG8gY3VhbCBubyBlcyBwb3NpYmxlLiBFc3RvcyByZWdpc3Ryb3Mgc2UNCmVsaW1pbmFyw6FuLiBZIHRhbWJpw6luIGhheSByZWdpc3Ryb3MgZGUgaW5tdWVibGVzIGVuIGVsIGNvbmp1bnRvIGNvbg0KbXVjaG9zIG3DoXMgYmHDsW9zIHF1ZSBoYWJpdGFjaW9uZXMsIGVuIHVuYSBwcm9wb3JjacOzbiBkZSAyIGEgMSBlDQppbmNsdXNpdmUgZGUgMyBhIDEuIEVsaW1pbmFyZW1vcyBlc3RvcyByZWdpc3Ryb3MgcHJvYmxlbcOhdGljb3MgZGFkbyBxdWUNCm5vIGVzIG11Y2hhIGxhIGNhbnRpZGFkIGRlIGVsbG9zLg0KDQojIyAxLjMuIEhvbW9nZW5pemFjacOzbiBkZSB2YXJpYWJsZXMgY3VhbGl0YXRpdmFzDQoNClNlIHJldmlzYXJvbiBsb3MgdmFsb3JlcyBkZSBsYXMgdmFyaWFibGVzICoqYmFycmlvKiogeSAqKnpvbmEqKi4gU2Ugbm90w7MNCnF1ZSBsb3Mgbm9tYnJlcyBkZSBsYXMgem9uYXMgZXN0w6FuIHRvZG9zIGJpZW4gZXNjcml0b3MsIHBlcm8gcXVlIGVuIGxvcw0Kbm9tYnJlcyBkZSBsb3MgYmFycmlvcyBoYXkgYWxndW5vcyByZWdpc3Ryb3MgcXVlIHNlIHJlZmllcmVuIGFsIG1pc21vDQpiYXJyaW8gcGVybyBxdWUgZXN0w6FuIGVzY3JpdG9zIGRlIG1hbmVyYSBkaWZlcmVudGUsIGNvbW8gcG9yIGVqZW1wbG86DQoiYWxmb25zbyBsb3BleiIgeSAiYWxmb256byBMw7NwZXoiLiBUYW1iacOpbiBzZSBub3RhIHF1ZSBoYXkgbXVjaG9zDQpiYXJyaW9zIGRvbmRlIHNvbG8gaGF5IHVuIGlubXVlYmxlIGEgbGEgdmVudGEuIENvbiB0YW4gYmFqYQ0KcmVwcmVzZW50YXRpdmlkYWQsIG5vIGVzIHBvc2libGUgaGFjZXIgaW5mZXJlbmNpYXMgZW4gY3VhbnRvIGEgbGENCnNpdHVhY2nDs24gZGUgbG9zIGlubXVlYmxlcyBlbiBsb3MgYmFycmlvcy4gSGFicsOtYSBxdWUgdXNhciBsYSB6b25hIHF1ZQ0KYWwgc2VyIHNvbG8gNSwgdGllbmVuIG3DoXMgaW5tdWVibGVzIGVuIGVsbGFzLg0KDQpDb3JyZWdpbW9zIGxvcyBub21icmVzIGRlIGFsZ3Vub3MgZGUgbG9zIGJhcnJpb3MgcGFyYSB2ZXIgc3VzIGVmZWN0b3MgZW4NCmVsIGRhdGFzZXQsIHNpbiBlbWJhcmdvLCBzZSBub3RhIHF1ZSBzaWd1ZSBoYWJpZW5kbyBtdWNob3MgYmFycmlvcyBjb24NCnRhbiBzb2xvIHVuIGlubXVlYmxlIHkgcXVlIGFkZW3DoXMgc29uIGFscmVkZWRvciBkZSAzNTAgeSBhbGd1bm9zIGVzdMOhbg0KcG9zaWJsZW1lbnRlIG1hbCByZWZlcmVuY2lhZG9zLCBjb21vIGVsIHF1ZSBkaWNlICJjYWxpIiBvIGVsIHF1ZSBoYWNlDQpyZWZlcmVuY2lhIGFsIG5vbWJyZSBkZSB1biBlZGlmaWNpby4gVGFtYmnDqW4gc2UgdmUgcXVlIGVzdMOhbg0KcmVmZXJlbmNpYWRhcyBsYXMgem9uYXMgZW4gbG9zIGJhcnJpb3MuIEVuIGRlZmluaXRpdmEsIGVzdGEgdmFyaWFibGUNCnJlcXVlcmlyw61hIGRlIG11Y2hhIGxpbXBpZXphIGUgaW52ZXN0aWdhY2nDs24gcGFyYSBzZXIgZGVwdXJhZGEsIHkNCnRlbmllbmRvIHRhbWJpw6luIGVycm9yZXMgZW4gbGEgbG9uZ2l0dWQgeSBsYXRpdHVkLCBubyBzZSBwdWVkZSBpbXB1dGFyIGENCnBhcnRpciBkZSBsb3MgZGF0b3MgZXhpc3RlbnRlcy4NCg0KYGBge3IgbmVpZ2hib3Job29kc30NCnVuaXF1ZSh2aXZpZW5kYXMkem9uYSkNCnZpdmllbmRhcyRiYXJyaW8gPC0gdG9sb3dlcih2aXZpZW5kYXMkYmFycmlvKQ0KYmFycmlvcyA9IHVuaXF1ZSh2aXZpZW5kYXMkYmFycmlvLCBkZWNyZWFzaW5nID0gVFJVRSkNCiN0YWJsZSh2aXZpZW5kYXMkYmFycmlvLCBzb3J0ID0gVFJVRSkNCiNiYXJyaW9zDQoNCnZpdmllbmRhcyRiYXJyaW8gPC0gaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gImFsZuKImsKpcmV6IHJlYWwiLCAiYWxmZXJleiByZWFsIiwgaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gImFsZm9uc28gbMOzcGV6IiwgImFsZm9uc28gbG9wZXoiLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAiYmFzZSBh4oiawqlyZWEiLCAgImJhc2UgYWVyZWEiLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAibWVs4oiawqluZGV6IiwgICJtZWxlbmRleiIsIGlmZWxzZSh2aXZpZW5kYXMkYmFycmlvID09ICJtZWziiJrCqW5kZXoiLCAgIm1lbGVuZGV6IiwgaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gIm1lbOKImsKpbmRleiIsICAibWVsZW5kZXoiLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAibGEgYXJib2xlZGEiLCAgImFyYm9sZWRhIiwgaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gImxvcyBhbGPDoXphcmVzIiwgICJsb3MgYWxjYXphcmVzIiwgaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gImxhIHJpdmVyYSBpIiwgICJsYSByaXZlcml0YSIsIGlmZWxzZSh2aXZpZW5kYXMkYmFycmlvID09ICJsYWZsb3JhIiwgICJsYSBmbG9yYSIsIGlmZWxzZSh2aXZpZW5kYXMkYmFycmlvID09ICJsYSByaXZlcmEgaWkiLCAgImxhIHJpdmVyaXRhIiwgaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gImxhIHJpdmVyYSIsICAibGEgcml2ZXJpdGEiLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAiY2l1ZGFkZWxhIHBhc28gYW5jaG8iLCAgImNpdWRhZGVsYSBwYXNvYW5jaG8iLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAiY2FuZXkgZXNwZWNpYWwiLCAgImNhbmV5IiwgaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gImVsIGluZ2VuaW8gaSIsICAiaW5nZW5pbyIsIGlmZWxzZSh2aXZpZW5kYXMkYmFycmlvID09ICJlbCBpbmdlbmlvIGlpIiwgICJpbmdlbmlvIiwgaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gImVsIGluZ2VuaW8iLCAgImluZ2VuaW8iLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAiZWwgaW5nZW5pbyBpaWkiLCAgImVsIGluZ2VuaW8iLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAiamFtdW5kaSBhbGZhZ3VhcmEiLCAgImFsZmFndWFyYSIsIGlmZWxzZSh2aXZpZW5kYXMkYmFycmlvID09ICJjYWxpIGNhbnRvIHZpaWkiLCAgImNhbGljYW50byIsIGlmZWxzZSh2aXZpZW5kYXMkYmFycmlvID09ICJjYWxpIGNhbnRvIiwgICJjYWxpY2FudG8iLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAiY2hpbWluYW5nb3MgMSBldGFwYSIsICAiY2hpbWluYW5nb3MiLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAiY2hpbWluYW5nb3MgMiBldGFwYSIsICAiY2hpbWluYW5nb3MiLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAiY3Jpc3TDs2JhbCBjb2zDs24iLCAgImNyaXN0b2JhbCBjb2zDs24iLCBpZmVsc2Uodml2aWVuZGFzJGJhcnJpbyA9PSAibWFycm9xdWluIGlpaSIsICAibWFycm9xdWluIiwgaWZlbHNlKHZpdmllbmRhcyRiYXJyaW8gPT0gImVsIGluZ2VuaW8gMyIsICAiZWwgaW5nZW5pbyIsIHZpdmllbmRhcyRiYXJyaW8pKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKQ0KDQp0YWJsZSh2aXZpZW5kYXMkYmFycmlvKQ0KYGBgDQoNClZlbyBxdWUgYSBwZXNhciBkZSBxdWUgc2UgaGFuIGVzdGFuZGFyaXphZG8gYWxndW5vcyBub21icmVzLCBlc3RvcyB5DQpvdHJvcyBzaWd1ZW4gdGVuaWVuZG8gcG9jb3MgaW5tdWVibGVzLiBBdW5xdWUgZW4gdW4gYW7DoWxpc2lzIG3DoXMNCnByb2Z1bmRvIG8gZGlmZXJlbnRlIGVsIGJhcnJpbyBwb2Ryw61hIHNlciBuZWNlc2FyaW8sIHBhcmEgZXN0YSBvY2FzacOzbiwNCnVzYXJlbW9zIHBhcmEgY3VhbHF1aWVyIGFuw6FsaXNpcyBsYSB2YXJpYWJsZSB6b25hIGNvbiBsYSBzaWd1aWVudGUNCmRpc3RyaWJ1Y2nDs24uIEVsIGJhcnJpbyBzZSBlbGltaW5hcsOhIGRlbCBkYXRhc2V0Lg0KDQpgYGB7ciBkZWxldGVfbmVpZ2hib3J9DQp2aXZpZW5kYXMkYmFycmlvIDwtIE5VTEwNCmBgYA0KDQojIyAxLjQuIFJlc3VtZW4gZGUgVmFyaWFibGVzIHkgZGF0b3MgZWxpbWluYWRvcw0KDQpUYWwgY29tbyBoZW1vcyB2aXN0byBlbiBlbCBhbsOhbGlzaXMsIGFsZ3VuYXMgZGUgbGFzIHZhcmlhYmxlcyBubyBzZQ0KdXRpbGl6YXLDoW4gY29tbyBwYXJ0ZSBkZWwgZXN0dWRpby4gRXN0YXMgdmFyaWFibGVzIHNvbjoNCg0KLSBpZCANCi0gbGF0aXR1ZCANCi0gbG9uZ2l0dWQgDQotIGJhcnJpbyANCg0KTGFzIHJhem9uZXMgcXVlIHNlIHR1dmllcm9uIGVuIGN1ZW50YSBwYXJhIHN1IGVsaW1pbmFjacOzbiBzb246IA0KDQotIEluZm9ybWFjacOzbiBpbXByb2NlZGVudGUgcGFyYSB1biBhbsOhbGlzaXMgZXN0YWTDrXN0aWNvLCBjb21vIGVuIGVsIGNhc28gZGVsIGlkLiANCi0gSW5mb3JtYWNpw7NuIGNvbiBtdWNob3MgZXJyb3JlcywgdmFsb3JlcyBhdMOtcGljb3MgbyBjb24gZGlzdHJpYnVjaW9uZXMgbXV5IHBhcnRpY3VsYXJlcyBxdWUgZGFuIHNlw7FhbCBkZSBlc3RhciBlcXVpdm9jYWRhcywgY29tbyBwb3IgZWplbXBsbyBsb3MgdmFsb3JlcyBlbiBsYSBsYXRpdHVkIHkgbGEgbG9uZ2l0dWQuIA0KLSBEYXRvcyBjb24gbXV5IHBvY2EgcmVwcmVzZW50YXRpdmlkYWQgZW4gdmFyaWFibGVzIHF1ZSBzZWdtZW50YW4gbG9zIHZhbG9yZXMsIGNvbW8gZXMgZWwgY2FzbyBkZSBsYSB2YXJpYWJsZSBiYXJyaW8sIGVuIGRvbmRlIGhhYsOtYSBtdWNob3MgYmFycmlvcyBjb24gc29sbyB1biBpbm11ZWJsZS4gDQotIFZhcmlhYmxlcyBjb21wbGV0YXMgcXVlIG5vIGFwb3J0YW4gYWwgb2JqZXRpdm8gZGVsIGVzdHVkaW8gKERlZmluaXIgc3UgbmljaG8sIGRlc2Fycm9sbGFyIGVzdHJhdGVnaWFzIGRlIG1hcmtldGluZywgZXRjLikgdGFsIGNvbW8gc2UgcHJlc2VudGEgZW4gbGEgZGVzY3JpcGNpw7NuIGRlbCB0cmFiYWpvLg0KDQpUYW1iacOpbiBzZSBlbGltaW5hcm9uIGFsZ3Vub3MgcmVnaXN0cm9zIGNvbiBkYXRvcyBubyBsw7NnaWNvcywgY29tbyBsb3MgcXVlIHRpZW5lIHZhbG9yZXMgZW4gY2VybyBwYXJhIGxhcyB2YXJpYWJsZXMgw6FyZWEgY29uc3RydWlkYSwgaGFiaXRhY2lvbmVzIG8gYmHDsW9zLCBvIGNhc2FzIGRlIG3DoXMgNCBwaXNvcywgdGFsIGNvbW8gc2UgZXhwbGljw7MgZHVyYW50ZSBlbCBhbsOhbGlzaXMuDQoNCiMjIDEuNS4gRXN0YWRvIGZpbmFsIGRlbCBjb25qdW50byBkZSBkYXRvcw0KDQpWZWFtb3MgbGEgZGlzdHJpYnVjacOzbiBkZSBpbm11ZWJsZXMgcG9yIHpvbmENCmBgYHtyIGJveHBsb3RfMX0NCmJveHBsb3Qodml2aWVuZGFzJHByZWNpb21+IHZpdmllbmRhcyR6b25hLCBkYXRhID0gdml2aWVuZGFzLCBjb2wgPSBjKCJsaWdodGdyYXkiLCAibGlnaHRibHVlIiwgImxpZ2h0eWVsbG93IiwibGlnaHRncmVlbiIsImJyb3duIiApLCB5bGFiID0gIlByZWNpbyIsIHhsYWIgPSAiWm9uYSIpDQpgYGANClNlIHB1ZWRlIHZlciBxdWUgbG9zIHByZWNpb3MgbcOhcyBhbHRvcyBzZSBlbmN1ZW50cmFuIGVuIGxhcyB6b25hcyBjb24gbcOhcyB2YWxvcmVzIGF0w61waWNvcyAoTm9ydGUsIE9lc3RlIHkgU3VyKS4NCg0KDQpgYGB7ciBib3hwbG90XzJ9DQpib3hwbG90KHZpdmllbmRhcyRwcmVjaW9tIH4gdml2aWVuZGFzJGVzdHJhdG8sIGRhdGEgPSB2aXZpZW5kYXMsIGNvbCA9IGMoImxpZ2h0Z3JheSIsICJsaWdodGJsdWUiLCAibGlnaHR5ZWxsb3ciLCJsaWdodGdyZWVuIiksIHlsYWIgPSAiUHJlY2lvIiwgeGxhYiA9ICJFc3RyYXRvIikNCg0KYGBgDQoNCk5vIGhheSBpbm11ZWJsZXMgZGUgZXN0cmF0b3MgMSB5IDIuICBMYXMgdml2aWVuZGFzIG3DoXMgY29zdG9zYXMgZXN0w6FuIGVuIHJlbGFjacOzbiBhbCBlc3RyYXRvIGVuIGVsIHF1ZSBlc3TDoW4sIGxvIHF1ZSBlcyBlc3BlcmFibGUuICBMb3MgZXN0cmF0b3MgMywgNCB5IDUgdGllbmVuIHVuYSBncmFuIGNhbnRpZGFkIGRlIHZhbG9yZXMgYXTDrXBpY29zLCBzaWVuZG8gZWwgZXN0cmF0byA2IGVsIGRlIG1heW9yIHJhbmdvIHkgbWVub3MgX291dGxpZXJzXy4gDQoNCk9ic2VydmFtb3MgbGEgY2FudGlkYWQgZGUgZGF0b3MgcG9yIHRpcG8gZGUgdml2aWVuZGENCg0KYGBge3IgdHlwZV9kaXN0cmlidXRpb24sIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1GQUxTRX0NClBpZUNoYXJ0KHRpcG8sIGRhdGEgPSB2aXZpZW5kYXMsIGZpbGwgPSAiZ3JlZW5zIiwgIG1haW4gPSAiRGlzdHJpYnVjacOzbiBwb3IgVGlwbyBWaXZpZW5kYSIsIGhvbGUgPSAwKQ0KYGBgDQoNClkgYWhvcmEgdmVtb3MgbGEgY2FudGlkYWQgZGUgZGF0b3MgcG9yIHpvbmENCg0KYGBge3Igem9uZV9kaXN0cmlidXRpb24sIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1GQUxTRX0NClBpZUNoYXJ0KHpvbmEsIGhvbGU9MCAsIGRhdGE9dml2aWVuZGFzLCBmaWxsPSJncmVlbnMiLCBtYWluPSJQb3JjZW50YWplcyBwb3IgWm9uYXMiKQ0KYGBgDQoNClkgcG9yIGVzdHJhdG86DQoNCmBgYHtyIGVzdHJhdG9fZGlzdHJpYnV0aW9uLCBmaWcuYWxpZ249J2NlbnRlcicgfQ0KUGllQ2hhcnQoZXN0cmF0bywgaG9sZT0wICwgZGF0YT12aXZpZW5kYXMsIGZpbGw9ImdyZWVucyIsIG1haW49IlBvcmNlbnRhamVzIHBvciBFc3RyYXRvIikNCmBgYA0KDQpBaG9yYSBjcmVhcmVtb3MgZG9zIF9kYXRhc2V0c18gdW5vIHBhcmEgbGFzIHZhcmlhYmxlcyBudW3DqXJpY2FzIHkgb3RybyBwYXJhIGxhcyBjYXRlZ8OzcmljYXMuDQoNCmBgYHtyIGRhdGFzZXRzX2NyZWF0aW9uIH0NCg0KZGZfY2F0ZWdvcmljbyA8LSBkYXRhLmZyYW1lKHBpc28gPSB2aXZpZW5kYXMkcGlzbywgem9uYSA9IHZpdmllbmRhcyR6b25hLCBlc3RyYXRvID0gdml2aWVuZGFzJGVzdHJhdG8sIHRpcG8gPSB2aXZpZW5kYXMkdGlwbywgcHJlY2lvbSA9IHZpdmllbmRhcyRwcmVjaW9tKQ0KI2RmX2NhdGVnb3JpY28NCmRmX251bWVyaWNvIDwtIGRhdGEuZnJhbWUocHJlY2lvbSA9IHZpdmllbmRhcyRwcmVjaW9tLCBhcmVhY29uc3QgPSB2aXZpZW5kYXMkYXJlYWNvbnN0LCBwYXJxdWVhZGVyb3MgPSB2aXZpZW5kYXMkcGFycXVlYWRlcm9zLCBiYW5pb3MgPSB2aXZpZW5kYXMkYmFuaW9zLCBoYWJpdGFjaW9uZXMgPSB2aXZpZW5kYXMkaGFiaXRhY2lvbmVzKQ0KI2RmX251bWVyaWNvDQoNCmBgYA0KDQojIDIuIEluZm9ybWUNCg0KIyMgMi4xLiBQQ0EgLSBBbsOhbGlzaXMgZGUgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMNCg0KUGFyYSBlbCBhbsOhbGlzaXMgZGUgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgY29tZW56YW1vcyBwb3IgZXN0YW5kYXJpemFyIGxhcyB2YXJpYWJsZXMgYSBsYSBtaXNtYSBlc2NhbGE6DQoNCmBgYHtyIEVzdGFuZGFyaXphZG9fdmFyaWFibGVzX251bcOpcmljYXMgfQ0KZGZfZXN0YW5kYXJpemFkbyA8LSBzY2FsZShkZl9udW1lcmljbykNCmRmX2VzdGFuZGFyaXphZG8gPC0gYXMuZGF0YS5mcmFtZShkZl9lc3RhbmRhcml6YWRvKQ0Kc3VtbWFyeShkZl9lc3RhbmRhcml6YWRvKQ0KYGBgDQoNCmBgYHtyIGNvcnJlbGF0aW9ufQ0KY29ycmVsYXRpb248LXJvdW5kKGNvcihkZl9lc3RhbmRhcml6YWRvKSwgMSkNCmNvcnJwbG90KGNvcnJlbGF0aW9uLCBtZXRob2Q9Im51bWJlciIpDQpgYGANCkVuIGVzdGEgdGFibGEgZGUgY29ycmVsYWNpb25lcywgcG9kZW1vcyBvYnNlcnZhciBsbyBzaWd1aWVudGU6DQoNCiogRWwgX3ByZWNpb21fIHRpZW5lIHVuYSBhbHRhIGNvcnJlbGFjacOzbiBjb24gbGFzIHZhcmlhYmxlcyBfYXJlYWNvbnN0XywgX3BhcnF1ZWFkZXJvc18sIHkgX2Jhbmlvc18sIGxvIHF1ZSBpbmRpY2EgcXVlIHBhcmEgbcOhcyDDoXJlYSBjb25zdHJ1aWRhLCBtw6FzIHBhcnF1ZWFkZXJvcyB5IG3DoXMgYmHDsW9zLCBzZSBpbXBhY3RhcsOhIGFsIGFsemEgZWwgcHJlY2lvLiBTZSBub3RhIHVuYSBiYWphIGNvcnJlbGFjacOzbiBjb24gX2hhYml0YWNpb25lc18uDQoqIFBhcmEgZWwgw6FyZWEgY29uc3RydWlkYSAoIF9hcmVhY29uc3RfICksIHNlIG5vdGEgYWx0YSBjb3JyZWxhY2nDs24gY29uIGJhw7FvcyAoIF9iYW5pb3NfICkgeSB1bmEgY29ycmVsYWNpb24gbW9kZXJhZGEgY29uIHBhcnF1ZWFkZXJvcyB5IGhhYml0YWNpb25lcy4NCiogVW5hIGNvcnJlbGFjacOzbiBtb2RlcmFkYSBzZSB2ZSBlbnRyZSBiYcOxb3MgeSBoYWJpdGFjaW9uZXMgeSDDgXJlYSBjb25zdHJ1aWRhLg0KKiBTZSBwdWVkZSBkZWNpciBxdWUgbm8gaGF5IGNvcnJlbGFjacOzbiBlbnRyZSBoYWJpdGFjaW9uZXMgeSBwYXJxdWVhZGVyb3MsIHkgdGFtcG9jbyBlbnRyZSBoYWJpdGFjaW9uZXMgeSBwcmVjaW8sIGxvIHF1ZSBubyBlc3BlcmFkbyBwdWVzIGxhIGNhbnRpZGFkIGRlIGhhYml0YWNpb25lcyBhdW1lbnRhIHTDrXBpY2FtZW50ZSBlbCDDoXJlYSBjb25zdHJ1aWRhIHkgbG9zIGJhw7Fvcy4NCg0KQXBsaWNhbW9zIGVsIFBDQSBwYXJhIGxvcyBkYXRvcyBjb24gbGEgZXNjYWxhIGVzdGFuZGFyaXphZGE6DQoNCmBgYHtyIHBjYSwgZmlnLmFsaWduPSdjZW50ZXInIH0NCnBjYSA8LSBwcmNvbXAoZGZfZXN0YW5kYXJpemFkbywgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSkNCnN1bW1hcnkocGNhKQ0KYGBgDQoNCmBgYHtyIHBjYV9ncmFwaGljXzEsIGZpZy5hbGlnbj0nY2VudGVyJyB9DQpmdml6X2VpZyhwY2EsIGFkZGxhYmVscyA9IFRSVUUpDQojZnZpel9wY2FfYmlwbG90KHBjYSwgcmVwZWwgPSBUUlVFLCBjb2wudmFyID0gIiNBQTk5RkYiKSAjU2UgY29tZW50w7MgcG9ycXVlIGVsIHJlc3VsdGFkbyBubyBlcyBlbnRlbmRpYmxlLg0KYGBgDQoNCkVuIGxhIGdyw6FmaWNhIHNlIHZlIHF1ZSBlbCBjb21wb25lbnRlIDEgcmVwcmVzZW50YSBlbCA2NC44JSBkZSBsYSB2YXJpYWNpw7NuLCBzZWd1aWRvIGRlbCBzZWd1bmRvIGNvbiAxOC4yJSwgeSBlbnRyZSBsb3MgZG9zIGV4cGxpY2Fyw61hbiBlbCA4MyUgZGUgbGEgdmFyaWFuemEuDQoNCmBgYHtyIGNvbnRyaWJ1dGlvbl9jb21wb25lbnRfMX0NCmZ2aXpfY29udHJpYihwY2EsIGNob2ljZSA9ICJ2YXIiLCBheGVzID0gMSwgdG9wID0gMTApICMgUEMxDQpgYGANClNlIG9ic2VydmEgcXVlIGVuIGVsIGNvbXBvbmVudGUgMSAoUEMxKSBsYXMgdmFyaWFibGVzIGJhbmlvcywgYXJlYWNvbnN0IHkgcHJlY2lvbSB0aWVuZW4gdW4gbWF5b3IgcGVzbyBlbiBlc2Ugb3JkZW4uDQoNCmBgYHtyIGNvbnRyaWJ1dGlvbl9jb21wb25lbnRfMn0NCmZ2aXpfY29udHJpYihwY2EsIGNob2ljZSA9ICJ2YXIiLCBheGVzID0gMiwgdG9wID0gMTApICMgUEMxDQpgYGANCg0KU2Ugb2JzZXJ2YSBxdWUgZW4gZWwgY29tcG9uZW50ZSAyIChQQzIpIGxhcyB2YXJpYWJsZXMgaGFiaXRhY2lvbmVzIHkgcGFycXVlYWRlcm9zIHRpZW5lbiB1biBtYXlvciBwZXNvLCBzaWVuZG8gaGFiaXRhY2lvbmVzIGxhIHF1ZSBtw6FzIGluY2lkZW5jaWEgdGllbmUgZW4gZWwgUEMyLg0KDQoNCmBgYHtyIHN1bW1hcnlfb2ZfY29udHJpYnV0b3JzfQ0KZ2V0X3BjYV92YXIocGNhKSRjb250cmliWywxOjJdDQpgYGANCg0KQWhvcmEgc2UgZ2VuZXJhIGVsIHNpZ3VpZW50ZSBncsOhZmljbyBwYXJhIG1vc3RyYXIgbGEgaW5mbHVlbmNpYSBkZSBsYXMgdmFyaWFibGVzIGVuIHVuIHBsYW5vLg0KDQpgYGB7ciBwY2FfZ3JhcGhpY18yLCBmaWcuYWxpZ249J2NlbnRlcicgfQ0KZnZpel9wY2FfdmFyKHBjYSwgY29sLnZhciA9ICJjb250cmliIiwgZ3JhZGllbnQuY29scyA9IGMoImJsdWUiLCAibGlnaHRibHVlIiwgInJlZCIpLCByZXBlbCA9IFRSVUUpDQpgYGANCg0KYGBge3IgdmFyaWFuemF9DQpwcm9wX3ZhcmlhbnphIDwtIHBjYSRzZGV2XjIvc3VtKHBjYSRzZGV2XjIpDQpyb3VuZChwcm9wX3ZhcmlhbnphKjEwMCwgMikNCnByb3BfdmFyaWFuemFfYWN1bSA8LSBjdW1zdW0ocHJvcF92YXJpYW56YSkNCnJvdW5kKHByb3BfdmFyaWFuemFfYWN1bSoxMDAsIDIpDQpgYGANCipDb25jbHVzaW9uZXMqDQoNCiogU2kgc2UgYXBsaWNhcmEgw7puaWNhbWVudGUgbGFzIHByaW1lcmFzIGRvcyBjb21wb25lbnRlcyBwb2Ryw61hbW9zIGV4cGxpY2FyIGVsIDgyLjk5JSBkZSBsYSB2YXJpYW56YSBvYnNlcnZhZGEuDQoqIFNpIGFwbGljYW1vcyBlbCBwcmltZXIgY29tcG9uZW50ZSwgc2UgZXhwbGljYXLDrWEgZWwgNjQuODElIGRlIGxhIHZhcmlhbnphIG9ic2VydmFkYS4NCiogRW4gZWwgUEMxIGxhcyB2YXJpYWJsZXMgYmFuaW9zLCBhcmVhY29uc3QgeSBwcmVjaW9tLCByZXNwZWN0aXZhbWVudGUsIGVqZXJjZW4gdW5hIG1heW9yIGluZmx1ZW5jaWEuDQoqIEVuIGVsIFBDMiBsYXMgdmFyaWFibGVzIGhhYml0YWNpb25lcyB5IHBhcnF1ZWFkZXJvcyBzb24gbGFzIHF1ZSBlamVyY2VuIG1heW9yIGluZmx1ZW5jaWEuDQoqIExhcyB2YXJpYWJsZXMgZGVsIHByaW1lciBjb21wb25lbnRlLCBlc3TDoW4gYWx0YW1lbnRlIGNvcnJlbGFjaW9uYWRhcyBlbnRyZSBzaSwgY29tbyBzZSB2aW8gZW4gbGEgbWF0cml6IGRlIGNvcnJlbGFjacOzbg0KKiBTZSBvYnNlcnZhIHF1ZSBsYXMgdmFyaWFibGVzIF9hcmVhY29uc3RfLCBfYmFuaW9zXyB5IF9oYWJpdGFjaW9uZXNfIGVzdMOhbiBlbiBlbCBjdWFydG8gY3VhZHJhbnRlLCBzaWVuZG8gZXN0YXMgdmFyaWFibGVzIGRlIGNhcmFjdGVyw61zdGljYXMgcHJvcGlhcyBkZWwgaW5tdWVibGUuIExhcyBkZWwgcHJpbWVyIGN1YWRyYW50ZSBuby4NCiogTGFzIHZhcmlhYmxlcyBfYXJlYWNvbnN0XywgX2Jhbmlvc18geSBfaGFiaXRhY2lvbmVzXyBlc3TDoW4gcmVsYWNpb25hZGFzIGVudHJlIHNpLCBsbyBxdWUgaW5kaWNhIHF1ZSBlc290cyBhdHJpYnV0b3Mgc3VlbGVuIHZhcmlhcyBqdW50b3MsIGxvIGN1YWwgdGllbmUgc2VudGlkbywgcHVlcyBhIG3DoXMgYmHDsW9zIHkgaGFiaXRhY2lvbmVzLCBtYXlvciBlcyBlbCDDoXJlYSBjb25zdHJ1aWRhLg0KKiBQb3Igb3RybyBsYWRvLCBsYXMgdmFyaWFibGVzIF9wYXJxdWVhZGVyb3NfIHkgX3ByZWNpb21fIHRhbWJpw6luIGVzdMOhbiByZWxhY2lvbmFkYXMgZW50cmUgc2kuDQoqIEVuIGN1YW50byBhIGxhIGNvbnRyaWJ1Y2nDs24sIGxhIHZhcmlhYmxlIF9oYWJpdGFjaW9uZXNfIGVzIGxhIHF1ZSBtw6FzIGNvbnRyaWJ1eWUuIEVzdG8gcXVpZXJlIGRlY2lyIHF1ZSBlc3RhIHZhcmlhYmxlIGVzIGNsYXZlIHBhcmEgZXhwbGljYXIgbGEgdmFyaWFiaWxpZGFkIGRlIGxvcyBkYXRvcyBlbiBsYXMgdml2aWVuZGFzLg0KKiBUb2RhcyBsYXMgdmFyaWFibGVzIGVzdMOhbiBhbCBsYWRvIGRlcmVjaG8gZGVsIF9iaXBsb3RfLCBwZXJvIHN1IHViaWNhY2nDs24gZW4gZWwgcHJpbWVyIG8gY3VhcnRvIGN1YWRyYW50ZSBwb2Ryw61hIGltcGxpY2FyIHF1ZSBlbnRyZSBsYXMgZGUgdmFyaWFibGVzIHViaWNhZGFzIGVuIGVzdG9zIGRvcyBjdWFkcmFudGVzIGV4aXN0ZSBwb2NhIHJlbGFjacOzbiBvIGluY2x1c2liZSBpbnZlcnNhbWVudGUgcmVsYWNpb25hZGFzLg0KKiBMYXMgdmFyaWFibGVzIGRlIGNhZGEgY3VhZHJhbnRlIGluZGljYW4gcXVlIGVzdG9zIGdydXBvcyBkZSBzdWVsZW4gdmFyaWFzIGp1bnRhcy4NCiogTG8gYW50ZXJpb3IgcG9kcsOtYSBpbXBsaWNhciBxdWUgaW5tdWVibGVzIGNvbiBtw6FzIHBhcnF1ZWFkZXJvcyB0aWVuZGVuIGEgc2VyIG1hcyBjb3N0b3NhcywgcGVybyBubyBuZWNlc2FyaWFtZW50ZSBzaSB0ZW5nbyBtw6FzDQoqIEVsIFByZWNpbyB5IGxhIGNhbnRpZGFkIGRlIGhhYml0YWNpb25lcyBzb24gbGFzIHZhcmlhYmxlcyBtw6FzIGRvbWluYW50ZXMgZW4gbGEgZXhwbGljYWNpw7NuIGRlIGxhcyBkaWZlcmVuY2lhcyBlbnRyZSBsYXMgdml2aWVuZGFzLg0KDQojIyAyLjIuIEFuw6FsaXNpcyBkZSBjb25nbG9tZXJhZG9zDQoNCkVzdGEgYW7DoWxpc2lzIHNlIHJlYWxpemEgc29icmUgbG9zIGRhdG9zIG51bcOpcmljb3MgZGVsIHB1bnRvIGFudGVyaW9yLg0KTWVkaWFudGUgZWwgYWdydXBhbWllbnRvIGVuIGJhc2UgYSBsYSBzaW1pbGFyaWRhZCBkZSBsb3MgcmVnaXN0cm9zIHBvZGVtb3MgZGVzY3VicmlyIGdydXBvcyBwb3RlbmNpYWxlcyBxdWUgcHVlZGVuIGluZGljYXIgbmljaG9zIGRlIG1lcmNhZG8sIHF1ZSBwdWVkZW4gc2VyIGVzdHVkaWFkb3MgZGUgbWFuZXJhIGluZGVwZW5kaWVudGUuIFBhcmEgZXN0byBzZSB1c2Fyw6Egay1tZWFucyBoYWNpZW5kbyB1biBjbHVzdGVyaW5nIG5vIGplcsOhcnF1aWNvLg0KDQpgYGB7ciBkZl9wY2F9DQpzZXQuc2VlZCg2ODkpDQoNCmRmX3BjYSA9IGRmX2VzdGFuZGFyaXphZG9bLCBjKCdwcmVjaW9tJywgJ2FyZWFjb25zdCcsJ3BhcnF1ZWFkZXJvcycsICdiYW5pb3MnLCdoYWJpdGFjaW9uZXMnKV0NCmZ2aXpfbmJjbHVzdChkZl9wY2EsIGttZWFucywgbWV0aG9kID0gInNpbGhvdWV0dGUiKSArDQogIGxhYnMoc3VidGl0bGUgPSAiTcOpdG9kbyBTaWxob3VldHRlIikNCg0KZGZfcGNhPWFzLmRhdGEuZnJhbWUoc2NhbGUoZGZfcGNhKSkNCnNldC5zZWVkKDY4OSkNCmttZWFuc19yZXN1bHQgPC0ga21lYW5zKGRmX3BjYSwgY2VudGVycyA9IDIsIG5zdGFydCA9IDI1KQ0KY2x1c3Rlcl9hc3NpZ21lbnRzIDwtIGttZWFuc19yZXN1bHQkY2x1c3Rlcg0KYXNzaWduZWRfY2x1c3RlciA8LSBkZl9wY2EgJT4lIG11dGF0ZShjbHVzdGVyID0gYXMuZmFjdG9yKGNsdXN0ZXJfYXNzaWdtZW50cykpDQpgYGANCg0KDQpgYGB7ciBzaWxob3VldHRlfQ0Kc2lsaG91ZXR0ZV9yZXMgID0gYygpDQpmb3IoaSBpbiAyOjYpew0KICAgIGRpc3RfZXYgPC0gZGlzdChkZl9wY2EsIG1ldGhvZCA9ICdldWNsaWRlYW4nKQ0KICAgIGhjX2V2IDwtIGhjbHVzdChkaXN0X2V2LCBtZXRob2QgPSAnY29tcGxldGUnKQ0KICAgIGNsdXN0ZXJfZXYgPC0gY3V0cmVlKGhjX2V2LCBrID0gaSkNCiAgICAjIENhbGN1bGFyIGVsIGNvZWZpY2llbnRlIGRlIFNpbGhvdWV0dGUNCiAgICBzaWwgPC0gc2lsaG91ZXR0ZShjbHVzdGVyX2V2LCBkaXN0KGRmX3BjYSkpDQogICAgc2lsX2F2ZyA8LSBtZWFuKHNpbFssM10pDQogICAgc2lsaG91ZXR0ZV9yZXMgID0gYyhzaWxob3VldHRlX3Jlcywgc2lsX2F2ZykNCn0NCmBgYA0KDQoNCmBgYHtyIHNpbGhvdWV0dGVfMn0NCg0Kc2lsX2RmIDwtIGRhdGEuZnJhbWUoSyA9IGMoMiwzLDQsNSw2KSxzaWxob3VldHRlX3ZhbHVlID0gc2lsaG91ZXR0ZV9yZXMpDQpzaWxfZGYNCiMga2FibGUoc2lsX2RmLCAiaHRtbCIpICU+JQ0KIyAgIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRkFMU0UpICU+JQ0KIyAgIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFKQ0KYGBgDQpFc3RvcyByZXN1bHRhZG9zIGluZGljYW4gdW5hIG1lam9yIGFncnVwYWNpw7NuIGN1YW5kbyBzZSBlbGlnZW4gMiBjb25nbG9tZXJhZG9zIChrID0gMikgY29uIHVuIHZhbG9yIGRlIDAuNDk3Mywgc2llbmRvIGVzdGUgZWwgbcOhcyBhbHRvLg0KDQpgYGB7ciAyX2NsdXN0ZXJ9DQpmdml6X2NsdXN0ZXIoa21lYW5zX3Jlc3VsdCwgZGF0YSA9IGRmX3BjYSwgDQogICAgICAgICAgICAgZ2VvbSA9ICJwb2ludCIsIG1haW4gPSAiQ2x1c3RlcnMgZW5jb250cmFkb3MgZW4gZWwgY29uanVudG8gZGUgZGF0b3MiLA0KICAgICAgICAgICAgIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiLCANCiAgICAgICAgICAgICBnZ3RoZW1lID0gdGhlbWVfbWluaW1hbCgpKQ0KYGBgDQoNCmBgYHtyIGNsdXN0ZXJ9DQp2aXYgPC0gZGlzdChkZl9wY2EsIG1ldGhvZCA9ICdldWNsaWRlYW4nKQ0KDQojIENsdXN0ZXIgamVyYXJxdWljbyBjb24gZWwgbcOpdG9kbyBjb21wbGV0ZQ0KZGZfdml2IDwtIGhjbHVzdCh2aXYsIG1ldGhvZCA9ICdjb21wbGV0ZScpDQpjbHVzdGVyX2YgPC0gY3V0cmVlKGRmX3Zpdiwgaz0yKQ0KYXNzaWduZWRfY2x1c3Rlcl9maW5hbCA8LSBkZl9wY2EgJT4lIG11dGF0ZShjbHVzdGVyID0gYXMuZmFjdG9yKGNsdXN0ZXJfZikpDQpnZ3Bsb3QoYXNzaWduZWRfY2x1c3Rlcl9maW5hbCwgYWVzKHggPSBhcmVhY29uc3QsIHkgPSBwcmVjaW9tLCBjb2xvciA9IGNsdXN0ZXIpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFscGhhID0gMC41KSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBjbHVzdGVyKSwgdmp1c3QgPSAtLjgpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KU2kgaGFjZW1vcyBlbCBkZW5kb2dyYW1hIHZlbW9zIGxvIHNpZ3VpZW50ZToNCg0KYGBge3IgZ3JhZmljYTA3LCBmaWcuYWxpZ249J2NlbnRlcicgfQ0KcGxvdChkZl92aXYsIGNleCA9IDAuNiwgbWFpbiA9ICJEZW5kb2dyYW1hIGRlIHZpdmllbmRhcyIsIGxhcz0xLA0KeWxhYiA9ICJEaXN0YW5jaWEgZXVjbGlkaWFuYSIsIHhsYWIgPSAiR3J1cG9zIikNCnJlY3QuaGNsdXN0KGRmX3ZpdiwgayA9IDIsIGJvcmRlciA9IDI6NSkNCmBgYA0KDQpFbiBlbCBkZW5kb2dyYW1hIG5vIHNlIHB1ZWRlIG9ic2VydmFyIHJlYWxtZW50ZSBuYWRhLCBhc8OtIHF1ZSBwb2RlbW9zIHJlYWxpemFyIHVuIGNvbnRlbyBkZSBsb3MgcmVnaXN0cm9zIGRlIGNhZGEgY2x1c3Rlci4NCg0KYGBge3IgY2x1c3Rlcl9jb3VudHN9DQpjbHVzdGVyX2NvdW50cyA8LSB0YWJsZShjbHVzdGVyX2YpDQpjbHVzdGVyX2NvdW50cw0KDQpmdml6X2NsdXN0ZXIobGlzdChkYXRhID0gZGZfcGNhLCBjbHVzdGVyID0gY2x1c3Rlcl9mKSwgDQogICAgICAgICAgICAgZ2VvbSA9ICJwb2ludCIsIA0KICAgICAgICAgICAgIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiLA0KICAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCkpDQpgYGANCg0KKkNvbmNsdXNpb25lcyoNCg0KKiBMb3MgcmVzdWx0YWRvcyBkZWwgYW7DoWxpc2lzIGNsdXN0ZXIgbXVlc3RyYW4gdW5hIGRpdmlzacOzbiBkZWwgbWVyY2FkbyBpbm1vYmlsaWFyaW8gZW4gZG9zIHNlZ21lbnRvcyBjbGFyYW1lbnRlIGRlZmluaWRvcy4gDQoqIFVuIGNsdXN0ZXIgZXN0w6EgY29tcHVlc3RvIHBvciB2aXZpZW5kYXMgZGUgbWF5b3IgdGFtYcOxbyB5IHZhbG9yLCBtaWVudHJhcyBxdWUgZWwgb3RybyBhZ3J1cGEgcHJvcGllZGFkZXMgbcOhcyBwZXF1ZcOxYXMgeSBlY29uw7NtaWNhcy4gDQoqIExhIGRpc3BhcmlkYWQgZW4gZWwgdGFtYcOxbyBkZSBsb3MgY2x1c3RlcnMgc3VnaWVyZSB1bmEgZG9taW5hbmNpYSBkZWwgc2VnbWVudG8gZGUgYWx0YSBnYW1hLCBlbCBjdWFsIHByZXNlbnRhIHVuIHRhbWHDsW8gY29uc2lkZXJhYmxlbWVudGUgbWF5b3IsIGxvIHF1ZSBwb2Ryw61hIGluZGljYXIgdW5hIHRlbmRlbmNpYSBoYWNpYSBlc3RlIHRpcG8gZGUgcHJvcGllZGFkZXMgZW4gZWwgbWVyY2FkbyBpbm1vYmlsaWFyaW8sIGNvbiBpbXBsaWNhY2lvbmVzIHBvdGVuY2lhbGVzIHBhcmEgbGFzIHBvbMOtdGljYXMgdXJiYW5hcyB5IGxhcyBlc3RyYXRlZ2lhcyBkZSBpbnZlcnNpw7NuDQoNCg0KIyMgMi4zLiBBbmFsaXNpcyBkZSBjb3JyZXNwb25kZW5jaWENCg0KRXN0ZSBhbsOhbGlzaXMgc2UgcmVhbGl6YXJhIHVzYW5kbyBlbCBkYXRhZnJhbWUgY2F0ZWfDs3JpY28gY3JlYWRvIGFudGVyaW9ybWVudGUuIExhIHZhcmlhYmxlcyBzb246IFBpc28sIHpvbmEsIGVzdHJhdG8geSB0aXBvLg0KDQpgYGB7ciB0YWJsZV9jYXRlZ29yaWNvLCBmaWcuYWxpZ249J2NlbnRlcicgfQ0KI2RmX2NhdGVnb3JpY28NCnRhYmxhIDwtIHRhYmxlKGRmX2NhdGVnb3JpY28kem9uYSwgZGZfY2F0ZWdvcmljbyRlc3RyYXRvKQ0KdGFibGENCnRhYmxhX2NoaSA8LSB0YWJsZShkZl9jYXRlZ29yaWNvJHBpc28sIGRmX2NhdGVnb3JpY28kZXN0cmF0bykNCmNoaXNxLnRlc3QodGFibGFfY2hpKQ0KIyBSZWFsaXphciBlbCBhbsOhbGlzaXMgZGUgY29ycmVzcG9uZGVuY2lhIGNvbiBGYWN0b01pbmVSDQojcmVzdWx0YWRvX2NhX3R6IDwtIENBKHRhYmxhX2NvbnRpbmdlbmNpYV90eiwgZ3JhcGg9VFJVRSkNCg0KI3ZhbG9yZXNfcHJvcF90eiA8LXJlc3VsdGFkb19jYV90eiRlaWcNCiN2YWxvcmVzX3Byb3BfdHoNCiN2aXpfY2FfYmlwbG90KHJlc3VsdGFkb19jYV90eikNCg0KYGBgDQoNCkVzdGUgcmVzdWx0YWRvIGluZGljYSBxdWUgc2UgcmVjaGF6YSBsYSBoaXDDs3Rlc2lzIGRlIGluZGVwZW5kZW5jaWEgZGUgdmFyaWFibGVzIChwb3JxdWUgZWwgdmFsb3IgcCBlcyBjYXNpIGNlcm8pLiAgQWhvcmEgdmFtb3MgYSByZWFsaXphciB1biBhbsOhbGlzaXMgZGUgY29ycmVzcG9uZGVuY2lhIGVuIGRvbmRlIHNlIGVzdGltYW4gbGFzIGNvb3JkZW5hZGFzIHBhcmEgY2FkYSB1bm8gZGUgbG9zIG5pdmVsZXMgZGUgbGFzIHZhcmlhYmxlcy4gIFZlYW1vczoNCg0KYGBge3IgcmVzdWx0c30NCnJlc3VsdHMgPC0gQ0EodGFibGEpDQpgYGANCg0KU2Vnw7puIGVzdG8sIHNlIHB1ZWRlIHZlciBxdWUgZWwgZXN0cmF0byA2IGVzdMOhIGVuIGxhIHpvbmEgb2VzdGUsIGVsIDQgeSA1IGVuIGxhIHpvbmEgc3VyIHkgZWwgMyBlbiBsYSB6b25hIGNlbnRybyB5IG9yaWVudGUuIFRhbWJpw6luIHF1ZSBubyBoYXkgdW5hIGRpZmVyZW5jaWEgc2lnbmlmaWNhdGl2YSBlbiBsYSB6b25hIG5vcnRlDQoNCmBgYHtyIHZhbG9yZXN9DQp2YWxvcmVzX3Byb3AgPC1yZXN1bHRzJGVpZyA7IHZhbG9yZXNfcHJvcA0KYGBgDQoNCmBgYHtyIHNjcmVlcGxvdH0NCmZ2aXpfc2NyZWVwbG90KHJlc3VsdHMsIGFkZGxhYmVscyA9IFRSVUUsIHlsaW0gPSBjKDAsIDgwKSkrZ2d0aXRsZSgiIikrDQogIHlsYWIoIlBvcmNlbnRhamUgZGUgdmFyaWFuemEgZXhwbGljYWRvIikgKyB4bGFiKCJFamVzIikNCmBgYA0KDQpTZSBwdWVkZSB2ZXIgcXVlIGVsIHByaW1lciBjb21wb25lbnRlIHRpZW5lIGVsIDczLjclIGRlbCB0b3RhbCBkZSBsYSB2YXJpYW56YSwgeSBxdWUgZWwgc2VndW5kbyBleHBsaWNhIGVsIDIzLjQlIGRlIGxhIHZhcmlhbnphLiBTaSBzZSBzdW1hbiBlc3RvcyBkb3MgY29tcG9uZW50ZXMsIGV4cGxpY2Fyw61hbiBlbCA5Ny4xJSBkZSBsYSB2YXJpYW56YSB0b3RhbCBkZSBsb3MgZGF0b3MuDQpgYGB7ciBkZl9jYXRlZ29yaWNvfQ0KZ2VuZXJhcl9jb2xvcmVzX2FsZWF0b3Jpb3MgPC0gZnVuY3Rpb24obikgew0KICBoc3YoaCA9IHJ1bmlmKG4pLCBzID0gMC45LCB2ID0gMC45KQ0KfQ0KDQojIEFzaWduYXIgY29sb3JlcyBhbGVhdG9yaW9zIGEgbGFzIHpvbmFzDQpjb2xvcmVzX2FsZWF0b3Jpb3MgPC0gZ2VuZXJhcl9jb2xvcmVzX2FsZWF0b3Jpb3MobGVuZ3RoKHVuaXF1ZShkZl9jYXRlZ29yaWNvJHpvbmEpKSkNCg0KZ2dwbG90KGRmX2NhdGVnb3JpY28sIGFlcyh4ID0gem9uYSwgeSA9IHByZWNpb20sIGZpbGwgPSB6b25hKSkgKw0KIGdlb21fYm94cGxvdCgpICsNCiBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcmVzX2FsZWF0b3Jpb3MpICsgICMgQXNpZ25hIGNvbG9yZXMgYWxlYXRvcmlvcw0KIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjaW9uIFByZWNpbyBwb3IgWm9uYSIpICsNCiB0aGVtZV9taW5pbWFsKCkgICMgUGVyc29uYWxpemEgZWwgdGVtYSBkZWwgZ3LDoWZpY28NCmBgYA0KDQoqKkNvbmNsdXNpb25lcyBkZWwgQW7DoWxpc2lzIGRlIENvbXBvbmVudGVzIFByaW5jaXBhbGVzKioNCg0KQ29tcG9uZW50ZSBQcmluY2lwYWwgMSAoNzMuNyUpOg0KDQoqIEV4cGxpY2EgbGEgbWF5b3IgcGFydGUgZGUgbGEgdmFyaWFiaWxpZGFkLg0KKiBEaWZlcmVuY2lhIHByaW5jaXBhbG1lbnRlIGVudHJlIGxhcyB6b25hcyBPcmllbnRlIHkgQ2VudHJvLg0KDQpDb21wb25lbnRlIFByaW5jaXBhbCAyICgyMy40JSk6DQoNCiogQXBvcnRhIGluZm9ybWFjacOzbiBhZGljaW9uYWwuDQoqIERpc3Rpbmd1ZSBwcmluY2lwYWxtZW50ZSBsYSBab25hIE9lc3RlLg0KDQpab25hcyBHZW9ncsOhZmljYXM6DQoNCiogT3JpZW50ZSB5IENlbnRybzogQWx0YW1lbnRlIGNvcnJlbGFjaW9uYWRhcyBjb24gZWwgQ29tcG9uZW50ZSAxLg0KKiBPZXN0ZTogRnVlcnRlbWVudGUgYXNvY2lhZG8gY29uIGVsIENvbXBvbmVudGUgMi4NCiogTm9ydGUgeSBTdXI6IE1lbm9yIGFzb2NpYWNpw7NuIGNvbiBhbWJvcyBjb21wb25lbnRlcy4NCg0KRXN0cmF0b3MgU29jaW9lY29uw7NtaWNvczoNCg0KKiBFc3RyYXRvIDY6IE3DoXMgY2VyY2FubyBhIGxhIFpvbmEgT2VzdGUuDQoqIEVzdHJhdG8gNCB5IDU6IENlcmNhbm9zIGEgbGFzIFpvbmFzIFN1ciB5IE5vcnRlLg0KKiBFc3RyYXRvIDM6IFByw7N4aW1vIGEgbGEgWm9uYSBDZW50cm8uDQoNCiMgMy4gQ29uY2x1c2lvbmVzIEZpbmFsZXMNCg0KLSAgIENvbmp1bnRvIGRlIERhdG9zDQoNClNpIGJpZW4sIGVzIHBvc2libGUgaGFjZXIgdW5hIGxpbXBpZXphIGRlbCBjb25qdW50byBkZSBkYXRvcywgZXN0ZSB0ZW5pYSBiYXN0YW50ZXMgZGF0b3MgaW5jb3JyZWN0b3MsIGF0w61waWNvcyB5IGZhbHRhbnRlcy4gIFNlIHJlY29taWVuZGEgdGVuZXIgbcOhcyBjdWlkYWRvIGNvbiBsYSBmdWVudGUgeSBnZW5lcmFjacOzbiBkZSBsb3MgZGF0b3MuDQoNCi0gICBab25hcyBjb24gbWVqb3IgcHJvbWVkaW8gZGUgcHJlY2lvcw0KDQpMYSBab25hIE9lc3RlIHByZXNlbnRhIHVuIG1lam9yIHByb21lZGlvIGRlIHByZWNpb3MgdGFudG8gcGFyYSBsYXMgY2FzYXMNCmNvbW8gcGFyYSBsb3MgYXBhcnRhbWVudG9zLCBjb25zdGl0dXnDqW5kb3NlIGVuIHVuYSB6b25hIGRlIG11Y2hvDQppbnRlcsOpcywgZGFkbyBxdWUgbGFzIGlubW9iaWxpYXJpYXMgZ2FuYW4gcG9yIGNvbWlzacOzbiwgeSBlbnRyZSBtYXlvcg0Kc2VhIGVsIHZhbG9yIGRlIGxhIHByb3BpZWRhZCwgbcOhcyBkaW5lcm8gc2UgZ2FuYXLDoS4gTGEgc2lndWVuIGxhIHpvbmENCnN1ciB5IGxhIHpvbmEgbm9ydGUuIEVsIGNhc28gZGUgbGEgem9uYSBjZW50cm8gZXMgcGFydGljdWxhciwgcHVlcyBoYXkNCnBvY2FzIGNhc2FzLCBwZXJvIHN1IHByb21lZGlvIGRlIHByZWNpbyBubyBlcyBlbCBtw6FzIGJhaml0by4NCg0KLSAgIFRpcG8gZGUgaW5tdWVibGUgbcOhcyBvZmVydGFkbw0KDQpMb3MgYXBhcnRhbWVudG9zIHNvbiBlbCB0aXBvIGRlIGlubXVlYmxlIGNvbiBtYXlvciBjYW50aWRhZCBkZSBvZmVydGFzDQplbiBsYSBjaXVkYWQgZGUgQ2FsaSBzdXBlcmFuZG8gZW4gY2FudGlkYWQgYSBsYXMgY2FzYXMgZW4gbGEgem9uYSBOb3J0ZSwNCk9lc3RlIHkgU3VyLiBFbiBsYXMgem9uYXMgQ2VudHJvIHkgT3JpZW50ZSwgaGF5IG3DoXMgY2FzYXMgb2ZlcnRhZGFzLCBlbg0KdW5hIHByb3BvcmNpw7NuIGFwcm94aW1hZGEgZGUgNiBhIDEuDQoNCi0gICBNZWpvciB0aXBvIGRlIFZpdmllbmRhDQoNCkF1bnF1ZSBoYXkgbcOhcyBhcGFydGFtZW50b3MgcXVlIGNhc2FzLCBsYXMgY2FzYXMgcHJlc2VudGFuIG1heW9yZXMNCnZhbG9yZXMgZW4gc3UgcHJlY2lvLCBsbyBxdWUgbGFzIGhhY2UgbcOhcyBhdHJhY3RpdmFzLiBMYXMgem9uYXMgY29uIG3DoXMNCmFsdG8gcHJvbWVkaW8gZGUgdmVudGEgc29uIGxhIE9lc3RlLCBsYSBTdXIsIGEgTm9ydGUsIGxhIENlbnRybyB5IGxhDQpPcmllbnRlLiBOw7N0ZXNlIHF1ZSBsYSBDZW50cm8gdGllbmUgcG9jYXMgb2ZlcnRhcywgcGVybyBzdXMgY2FzYXMgdGllbmVuDQptZWpvciBwcm9tZWRpbyBxdWUgbGFzIGRlIGxhIHpvbmEgT3JpZW50ZS4NCg0KLSAgIEVzdHJhdG8gY29uIG1heW9yIGNhbnRpZGFkIGRlIGlubXVlYmxlcw0KDQpFbCBlc3RyYXRvIDUgZXMgZWwgZXN0cmF0byBjb24gbWF5b3IgY2FudGlkYWQgZGUgb2ZlcnRhIGRlIHZlbnRhIGRlDQppbm11ZWJsZXMuIFNpIGJpZW4sIGVzdG8gbm8gZXMgZW4gc2kgaW5mb3JtYWNpw7NuIGNvbnR1bmRlbnRlLCBzaSBlcyB1bmENCm11ZXN0cmEgZGUgcXVlIGxhcyBjYXNhcyBxdWUgbcOhcyBzZSBjb21lcmNpYWxpemFuIHNvbiBsYXMgZGUgdmFsb3Jlcw0KbWVkaW8tYWx0b3MsIHkgZXN0byBwdWVkZSBkYXIgdW4gaW5kaWNpbyBkZSBsYSBjbGFzZSBkZSBjb21wcmFkb3JlcyBxdWUNCnNlIGRlYmVuIGJ1c2Nhci4NCg0K