#devtools::install_github("dgonxalex80/paqueteMODELOS", force = TRUE)
#devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
#data <- paqueteMODELOS::vivienda
data(vivienda)

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 aptos 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

Barrio: Barrio donde está la vivienda

Longitud: Longitud geográfica

Latitud: Latitud geográfica

Nota: Se eliminó el ID por ser una variable que no contribuye en el ejercicio ni aporta información relevante para el modelo.

0. Reconocimiento del Data set y prelimpieza.

summary(vivienda)
##        id           zona               piso              estrato     
##  Min.   :   1   Length:8322        Length:8322        Min.   :3.000  
##  1st Qu.:2080   Class :character   Class :character   1st Qu.:4.000  
##  Median :4160   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4160                                         Mean   :4.634  
##  3rd Qu.:6240                                         3rd Qu.:5.000  
##  Max.   :8319                                         Max.   :6.000  
##  NA's   :3                                            NA's   :3      
##     preciom         areaconst       parqueaderos        banios      
##  Min.   :  58.0   Min.   :  30.0   Min.   : 1.000   Min.   : 0.000  
##  1st Qu.: 220.0   1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000  
##  Median : 330.0   Median : 123.0   Median : 2.000   Median : 3.000  
##  Mean   : 433.9   Mean   : 174.9   Mean   : 1.835   Mean   : 3.111  
##  3rd Qu.: 540.0   3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :1745.0   Max.   :10.000   Max.   :10.000  
##  NA's   :2        NA's   :3        NA's   :1605     NA's   :3       
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:8322        Length:8322        Min.   :-76.59  
##  1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   : 3.605                                         Mean   :-76.53  
##  3rd Qu.: 4.000                                         3rd Qu.:-76.52  
##  Max.   :10.000                                         Max.   :-76.46  
##  NA's   :3                                              NA's   :3       
##     latitud     
##  Min.   :3.333  
##  1st Qu.:3.381  
##  Median :3.416  
##  Mean   :3.418  
##  3rd Qu.:3.452  
##  Max.   :3.498  
##  NA's   :3

Vemos que para casi todos los atributos hay 3 registros con valores perdidos”. Esos los vamos a quitar asumiendo que son los mismos 3 para todas las columnas. Debemos revisar si persisten en el subconjunto de trabajo. Para los parqueaderos hay 1605 de ese tipo. También los observaremos cuando creemos el subconjunto. Vamos a borrar la columna ID pues no aporta nada para nuestro objetivo.

aptos <- subset(vivienda, !is.na(vivienda$estrato))
aptos$id <- NULL
summary(aptos)
##      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             longitud         latitud     
##  Length:8319        Length:8319        Min.   :-76.59   Min.   :3.333  
##  Class :character   Class :character   1st Qu.:-76.54   1st Qu.:3.381  
##  Mode  :character   Mode  :character   Median :-76.53   Median :3.416  
##                                        Mean   :-76.53   Mean   :3.418  
##                                        3rd Qu.:-76.52   3rd Qu.:3.452  
##                                        Max.   :-76.46   Max.   :3.498  
## 
table(aptos$tipo)
## 
## Apartamento        Casa 
##        5100        3219

Vemos que hay 5100 Apartamentos y 3219 Casas en el dataset inicial

1. Filtrar la base de datos

Realice un filtro a la base de datos e incluya sólo las ofertas de apartamentos. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta.

Para lograr esto, se procede a eliminar todos los registros de las casas quitando del dataset todo lo que no sea de ese tipo.

aptos <- subset(aptos, tipo == 'Apartamento')
table(aptos$tipo)
## 
## Apartamento 
##        5100
table(aptos$banios)
## 
##    0    1    2    3    4    5    6    7    8 
##   14  400 2502 1200  635  301   39    8    1
table(aptos$habitaciones)
## 
##    0    1    2    3    4    5    6    7    9 
##   21   49  859 3384  714   63    8    1    1
head(aptos,3)
## # A tibble: 3 × 12
##   zona    piso  estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1 Zona N… 01          5     260        90            1      2            3 Apar…
## 2 Zona N… 01          5     240        87            1      3            3 Apar…
## 3 Zona N… 01          4     220        52            2      2            3 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
summary(aptos)
##      zona               piso              estrato         preciom      
##  Length:5100        Length:5100        Min.   :3.000   Min.   :  58.0  
##  Class :character   Class :character   1st Qu.:4.000   1st Qu.: 175.0  
##  Mode  :character   Mode  :character   Median :5.000   Median : 279.0  
##                                        Mean   :4.727   Mean   : 366.9  
##                                        3rd Qu.:6.000   3rd Qu.: 430.0  
##                                        Max.   :6.000   Max.   :1950.0  
##                                                                        
##    areaconst      parqueaderos        banios       habitaciones  
##  Min.   : 35.0   Min.   : 1.000   Min.   :0.000   Min.   :0.000  
##  1st Qu.: 68.0   1st Qu.: 1.000   1st Qu.:2.000   1st Qu.:3.000  
##  Median : 90.0   Median : 1.000   Median :2.000   Median :3.000  
##  Mean   :112.8   Mean   : 1.568   Mean   :2.617   Mean   :2.971  
##  3rd Qu.:130.0   3rd Qu.: 2.000   3rd Qu.:3.000   3rd Qu.:3.000  
##  Max.   :932.0   Max.   :10.000   Max.   :8.000   Max.   :9.000  
##                  NA's   :869                                     
##      tipo              barrio             longitud         latitud     
##  Length:5100        Length:5100        Min.   :-76.59   Min.   :3.334  
##  Class :character   Class :character   1st Qu.:-76.54   1st Qu.:3.380  
##  Mode  :character   Mode  :character   Median :-76.53   Median :3.419  
##                                        Mean   :-76.53   Mean   :3.419  
##                                        3rd Qu.:-76.52   3rd Qu.:3.453  
##                                        Max.   :-76.46   Max.   :3.498  
## 

Vemos que ya no hay casas en el dataset, sin embargo, si hay apartamentos con 0 baños y con 0 habitaciones, algo que es improbable. Estos registros se eliminarán.

aptos <- subset(aptos, banios != 0)
aptos <- subset(aptos, habitaciones != 0)
table(aptos$banios)
## 
##    1    2    3    4    5    6    7    8 
##  399 2497 1199  632  299   39    8    1
table(aptos$habitaciones)
## 
##    1    2    3    4    5    6    7    9 
##   48  859 3381  714   62    8    1    1

Si bien no sabemos todavía nada más en profundidad sobre el dataset, ya hemos eliminado registros vacíos e improbables, al igual que una columna que no sirve para el análisis. Hagamos un último análisis comparando atributos como el precio y el área, la cantidad de baños y el área, y la cantidad de habitaciones y el área, para identificar si hay registros que no parezcan tener información real.

many_rooms <- aptos %>%
  filter(habitaciones >= 7)
many_rooms
## # A tibble: 2 × 12
##   zona    piso  estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1 Zona O… <NA>        3     240       315            2      5            9 Apar…
## 2 Zona O… 01          5    1500       500            7      7            7 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Vemos que hay un apartamento de 315 metros cuadrados, con 9 habitaciones y 5 baños, que solo cuesta 240 millones. No parece lógico. Se eliminará. El de 7 habitaciones, con 7 baños de 1500 millones si parece posible. Se mantendrá.

many_bathrooms <- aptos %>%
  filter(banios >= 7)
many_bathrooms
## # A tibble: 9 × 12
##   zona    piso  estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1 Zona S… <NA>        5     730       573            3      8            5 Apar…
## 2 Zona O… <NA>        6    1500       530            4      7            4 Apar…
## 3 Zona S… 03          6     980       250            4      7            4 Apar…
## 4 Zona O… 01          5    1500       500            7      7            7 Apar…
## 5 Zona O… 08          5    1300       300            3      7            5 Apar…
## 6 Zona O… 10          6    1400       459            3      7            6 Apar…
## 7 Zona O… 12          6    1400       475            3      7            5 Apar…
## 8 Zona O… 12          6    1400       475            3      7            5 Apar…
## 9 Zona O… <NA>        6    1350       474            3      7            5 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

La mayoría de estos registro si parecen posibles por el área y el precio, aunque el de 250 m2 a 980 millones con 7 baños y 4 habitaciones no parece coherente con el resto. Se eliminará disminuyendo estos valores atípicos.

too_expensive <- aptos %>%
  filter(areaconst >= 500 & preciom <= 500)
too_expensive
## # A tibble: 2 × 12
##   zona    piso  estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1 Zona S… 03          5     170       605            1      2            2 Apar…
## 2 Zona S… 07          5     299       932            1      3            3 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
#aptos <- aptos %>%
#  filter(preciom != 7 | habitaciones != 4)

Por último, vemos un inmueble de valor 299 millones con un área construida de 932 metros cuadrados. Este registro también se considera atípico y se eliminará.

aptos <- aptos %>%
  filter(habitaciones != 9)
aptos <- aptos %>%
  filter(!(areaconst >= 500 & preciom <= 500))
aptos <- aptos %>%
  filter(banios != 7 | habitaciones != 4)
table(aptos$banios)
## 
##    1    2    3    4    5    6    7    8 
##  399 2496 1198  632  298   39    6    1
table(aptos$habitaciones)
## 
##    1    2    3    4    5    6    7 
##   48  858 3380  712   62    8    1

2. Análisis Exploratorio y Correlaciones

Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa) en función del área construida, estrato, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

Empezaremos convirtiendo las variables categóricas presentes en el dataset en factores. Estas son: Tipo, Piso, Zona y Barrio.

aptos$tipo <- as.factor(aptos$tipo)
aptos$piso <- as.factor(aptos$piso)
aptos$zona <- as.factor(aptos$zona)
aptos$barrio <- as.factor(aptos$barrio)
summary(aptos)
##            zona           piso         estrato         preciom      
##  Zona Centro :  24   03     : 569   Min.   :3.000   Min.   :  58.0  
##  Zona Norte  :1188   05     : 563   1st Qu.:4.000   1st Qu.: 175.0  
##  Zona Oeste  :1023   04     : 545   Median :5.000   Median : 276.0  
##  Zona Oriente:  60   02     : 507   Mean   :4.727   Mean   : 366.4  
##  Zona Sur    :2774   01     : 429   3rd Qu.:6.000   3rd Qu.: 430.0  
##                      (Other):1084   Max.   :6.000   Max.   :1950.0  
##                      NA's   :1372                                   
##    areaconst      parqueaderos        banios       habitaciones  
##  Min.   : 40.0   Min.   : 1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.: 68.0   1st Qu.: 1.000   1st Qu.:2.000   1st Qu.:3.000  
##  Median : 90.0   Median : 1.000   Median :2.000   Median :3.000  
##  Mean   :112.1   Mean   : 1.565   Mean   :2.621   Mean   :2.982  
##  3rd Qu.:130.0   3rd Qu.: 2.000   3rd Qu.:3.000   3rd Qu.:3.000  
##  Max.   :600.0   Max.   :10.000   Max.   :8.000   Max.   :7.000  
##                  NA's   :857                                     
##           tipo                 barrio        longitud         latitud     
##  Apartamento:5069   valle del lili: 839   Min.   :-76.59   Min.   :3.334  
##                     la flora      : 265   1st Qu.:-76.54   1st Qu.:3.380  
##                     santa teresita: 249   Median :-76.53   Median :3.419  
##                     ciudad jardín : 219   Mean   :-76.53   Mean   :3.419  
##                     pance         : 204   3rd Qu.:-76.52   3rd Qu.:3.453  
##                     normandía     : 149   Max.   :-76.46   Max.   :3.498  
##                     (Other)       :3144

Veamos la distribución de los Apartamentos según su estrato y su zona.

ggplot(aptos, aes(x = estrato, fill = zona)) +
  geom_bar() +
  scale_fill_brewer(palette = "Set2") +
  theme(legend.position = "bottom")

  labs(title = "Distribución de inmuebles por estrato",
       x = "Estrato",
       y = "Cantidad de inmuebles")
## $x
## [1] "Estrato"
## 
## $y
## [1] "Cantidad de inmuebles"
## 
## $title
## [1] "Distribución de inmuebles por estrato"
## 
## attr(,"class")
## [1] "labels"

Se ve que en el estrato 5 es donde más apartamentos hay, seguido por el 4 y el 6. También se ve que la zona Sur es la que más cuenta con apartamentos, seguido de la Norte y de la Oeste.

Veamos ahora como es la distribución de las zonas, según su relación entre el precio y el área construida. Se esperaría que entre más área, más precio, pero no sabemos en qué zonas se presentan dichos apartamentos.

area_zone_price <- ggplot(aptos, aes(x = preciom, y = areaconst)) +
  geom_point(aes(color = zona)) +
  scale_fill_brewer(palette = "Set2") +
  labs(title = "Área vs Precio por Zona",
       x = "Precio (millones)",
       y = "Área Construida (m2)") +
  theme(plot.margin = margin(1, 1, 1, 1, "cm")) #+
  #xlim(0, 2500) +  # Ajusta el límite inferior y superior del eje x
  #ylim(0, 1000)   # Ajusta el límite inferior y superior del eje y
ggplotly(area_zone_price)

En el eje X, la gran mayoría de los datos se encuentra por debajo de los mil millones de pesos. En el eje y, la mayoría de los datos esta por debajo de los 300 metros cuadrados construidos. En cuanto a las zonas, se ve que en la zona Oeste hay apartamentos de mayor tamaño y mayor precio, siendo más comunes los apartamentos de mas de mil millones en esta zona.

Veamos ahora, con el conjunto de datos que hemos depurado, el diagrama de cajas y bigotes que nos mostrará los valores atípicos para estos datos.

boxplot_zones <- ggplot(aptos, aes(x = estrato, y = preciom)) +
  geom_boxplot(aes(fill = zona)) +
  scale_fill_brewer(palette = "Set1") +
  labs(title = "Distribución de Precios por Estrato y Zona",
       x = "Estrato",
       y = "Precio (millones)") +
  xlim(2, 7) #+  # Ajusta el límite inferior y superior del eje x
  #ylim(0, 1000)   # Ajusta el límite inferior y superior del eje y

ggplotly(boxplot_zones)

Los estratos 5 y 6 están prácticamente conformados por apartamentos de la zona Oeste u Sur, habiendo gran cantidad de apartamentos de la zona oeste también en los estratos 3 y 4. También se ve en la gráfica que a medida que aumenta el estrato, también aumenta el precio mínimo y máximo de los apartamentos.

La gráfica anterior no nos muestra información referente al precio con respecto a la cantidad de baños o habitaciones de los apartamentos. Veámoslo en la siguiente gráfica:

price_rooms_bathrooms_distribution <- ggplot(aptos, aes(x = preciom, y = banios)) +
  geom_point(aes(color = factor(habitaciones))) +
  scale_fill_brewer(palette = "Set3") +
  labs(title = "Distribución de Precio por cantidad de Baños y Habitaciones",
       x = "Precio en millones",
       y = "Cantidad de Baños",
       color = "Habitaciones") 
ggplotly(price_rooms_bathrooms_distribution)

En la gráfica se ve un comportamiento que podría esperarse, y es que a medida en que va subiendo la cantidad de baños, va subiendo el precio, con una excepción clara en el apartamento de 8 baños y un valor de 730 millones de pesos. También se ve que no necesariamente el que tenga más baños es el más costoso, pero si se ve una clara relación.

Veamos ahora si la correlación numérica entre las variables graficadas anteriormente.

round_cor <- round(cor(aptos[, c("areaconst", "estrato", "banios", "habitaciones", "preciom")], use = "complete.obs"),2)
print(round_cor)
##              areaconst estrato banios habitaciones preciom
## areaconst         1.00    0.57   0.76         0.45    0.85
## estrato           0.57    1.00   0.62         0.19    0.67
## banios            0.76    0.62   1.00         0.51    0.75
## habitaciones      0.45    0.19   0.51         1.00    0.31
## preciom           0.85    0.67   0.75         0.31    1.00

La tabla nos muestra que las correlaciones más altas son del área construida con el precio (0.85), y de la cantidad de baños con el área construida (0.76), y también con el precio (0.75). Le sigue en magnitud, la correlación entre el estrato y el precio (0.67). La correlación más baja de una variable con el precio es la de la cantidad de habitaciones (0.31), y en general, la del número de habitaciones con las demás variables.

3. Modelo de Regresión Lineal Múltiple

Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo)

reg_lin_mult <- lm(preciom ~ habitaciones + parqueaderos + banios + areaconst + estrato , data = aptos)
summary(reg_lin_mult)
## 
## Call:
## lm(formula = preciom ~ habitaciones + parqueaderos + banios + 
##     areaconst + estrato, data = aptos)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1020.31   -53.83     1.09    47.84   989.42 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -230.75852   15.59658  -14.79   <2e-16 ***
## habitaciones  -53.26845    3.83082  -13.90   <2e-16 ***
## parqueaderos   78.52672    4.16567   18.85   <2e-16 ***
## banios         47.91478    3.36796   14.23   <2e-16 ***
## areaconst       2.39131    0.05074   47.13   <2e-16 ***
## estrato        51.65929    2.94317   17.55   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 131.6 on 4206 degrees of freedom
##   (857 observations deleted due to missingness)
## Multiple R-squared:  0.803,  Adjusted R-squared:  0.8027 
## F-statistic:  3428 on 5 and 4206 DF,  p-value: < 2.2e-16

Nota Aclaratoria: Aunque el modelo de regresión lineal múltiple puede proporcionar una buena explicación de la variabilidad en los precios de las propiedades, es importante recordar que la correlación no implica causalidad, es decir, aunque las variables estén asociadas con el precio, no necesariamente lo causan. Otros factores no incluidos en el modelo podrían también influir en el precio. Se me ocurren por ejemplo otras características del inmueble como el estado de la fachada, de la pintura, del techo/goteras, ruido de la zona, acceso vehicular, congestión de la zona, etc.

Interpretación de los Coeficientes

El Intercepto (-224.477): Representa el precio promedio de una propiedad que no tiene ninguna de las características consideradas, es decir, ninguna habitación, baño, parqueadero, etc. En la vida real esto no tendría sentido pero es la referencia para la ecuación resultante del modelo.

Los Coeficientes de las variables independientes: que se muestran en la tabla son:

Habitaciones (-42.027): Cada habitación adicional disminuye el precio en promedio $42, lo que no suena muy lógico, pero puede deberse a otros factores que no se consideraron. Esto me deja pensativo. Parqueaderos (89.079): Cada parqueadero adicional aumenta el precio en promedio $89. A diferencia del coeficiente anterior, este si parece tener sentido. Es el coeficiente más alto de todos. Banios (40.994): Cada baño adicional aumenta el precio en promedio $41 pesos. Areaconst (2.374): Cada metro cuadrado adicional aumenta el precio en promedio $2.3 pesos. Esto también parece contraintuitivo, pero puede ser porque los baños y parqueaderos aumentan el área construida, y parecen contribuir más al precio que la misma área. Estrato (44.135): Cada nivel más alto de estrato socio económico aumenta el precio en promedio $44.

Significancia de los Coeficientes Los valores de p (Pr(>|t|)) asociados a cada coeficiente indican la probabilidad de que el coeficiente sea realmente cero en la población. En este caso, todos los valores de p son extremadamente pequeños (menores a 0.001), lo que significa que podemos rechazar la hipótesis nula de que el coeficiente es cero con un alto nivel de confianza. En otras palabras, todas las variables incluidas en el modelo son estadísticamente significativas en la predicción del precio, lo que a su vez implica que cada variable independiente (habitaciones, parqueaderos, baños, área construida y estrato) tiene un efecto significativo en el precio de las propiedades.

Bondad de Ajuste del Modelo R² (0.8183): Indica que el modelo explica aproximadamente el 81.83% de la variabilidad en el precio de las propiedades. Esto es un valor bastante alto, lo que sugiere que el modelo se ajusta razonablemente bien a los datos. Esto significa que las variables incluidas en el modelo explican una gran proporción de la variación en los precios. En otras palabras, el modelo se ajusta bastante bien a los datos.

F-estadístico: Este valor indica si el modelo en su conjunto es significativo. Un valor de F muy alto y un valor de p muy bajo (como en este caso) sugieren que el modelo es significativo.

¿Qué se puede hacer para mejorar el modelo?

El modelo resultante esta bastante bueno, sin embargo, se podrían ejecutar estrategias adicionales o diferentes para mejorar el resultado.

Incluir variables adicionales: Aunque en el conjunto de datos no hay más variables, un estudio más detallado podría conseguir otras variables de los inmuebles que pueden influir en el valor e incluirlas en el conjunto de datos. Algunas opciones podrían ser: Antigüedad del inmueble, el estado de la fachada, estado de la pintura, estado o antigüedad del techo, ruido de la zona, acceso vehicular, congestión de la zona, entre otras.

Transformar variables: En algunos casos, transformar las variables (por ejemplo, tomando logaritmos) puede mejorar el ajuste del modelo. Esto habría que probarlo y ver si tiene efectos positivos.

Interacciones entre variables: Podría haber interacciones entre las variables independientes que no están siendo capturadas por el modelo lineal. Por ejemplo, el efecto del número de habitaciones podría variar dependiendo del estrato socioeconómico. Sabemos que, como lo dije anteriormente, a más habitaciones, baños y parqueaderos, el área construida aumentará. Eso produce que el coeficiente de la variable de área, sea tan bajito.

Modelos no lineales: También es posible revisar si la relación entre las variables no es lineal, lo que en ese caso abriría la puerta para considerar modelos no lineales como la regresión polinomial o los modelos de árboles de decisión.

Finalmente, podrías realizar una Validación cruzada: para evaluar la generalización del modelo, y revisar si hay sobreajuste (para evitarlo), estimar de forma más precisa el error y seleccionar el modelo que mejores resultados arroje.

4. Validación de supuestos

**Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer)*.**

4.1. Linealidad

El supuesto de linealidad en una regresión lineal múltiple establece que la relación entre las variables independientes y la variable dependiente es lineal, es decir, que existe una relación lineal entre la variable que queremos predecir (en este caso el precio) y las variables “predictoras” o independientes. Al graficar los residuos contra los valores ajustados, estos deben estar distribuidos aleatoriamente alrededor de cero.

El supuesto de linealidad establece que la relación entre las variables independientes y la variable dependiente es lineal. Para validar este supuesto, se puede graficar los residuos contra los valores ajustados. En un análisis adecuado, los residuos no deberían mostrar patrones sistemáticos, sino que deben estar distribuidos aleatoriamente alrededor de cero. Si se observa algún patrón, como una tendencia curvilínea, esto sugiere que la relación entre la variable dependiente y las independientes no es completamente lineal. En tal caso, sería recomendable considerar transformaciones en las variables o modelos no lineales para ajustar el modelo.

plot(reg_lin_mult$fitted.values, resid(reg_lin_mult),
     main = "Residuos vs Valores Ajustados",
     xlab = "Valores Ajustados",
     ylab = "Residuos",
     pch = 1, col = "blue")
abline(h = 0, col = "black", lwd = 2)

El gráfico no muestra de manera contundente una violación directa al supuesto de linealidad, entonces se podría dar por cumplido ese supuesto. Sin embargo, la forma cónica podría mostrar un patrón que indicaría una posible relación no lineal entre las variables. Si esta sospecha es fuerte, se pueden hacer gráficos adicionales de los residuos contra las variables predictoras individuales para identificar patrones que no fueran lineales. También se pueden explorar transformaciones a las variables predictoras o cambiar a modelos no lineales, como los polinómicos o el de regresión spline, los cuales capturarían mejor la relación entre las variables en el caso de que no fuera lineal.

4.2. Homoscedasticidad

El supuesto de homoscedasticidad espera que la varianza de los residuos sea constante a lo largo de los valores que se predijeron. Para su validación graficaremos los residuos estandarizados contra los valores ajustados, tal como lo hicimos con la linealidad. Si los residuos presentan un patrón de embudo, aumentando o disminuyendo la varianza con los valores ajustados, se estaría ante un problema de heteroscedasticidad. Se aplicará la prueba Breusch-Pagan para hacer esta verificación.

#En tal caso, se pueden aplicar técnicas como la transformación logarítmica de la variable dependiente o utilizar estimadores robustos para manejar la heteroscedasticidad.

bptest(reg_lin_mult)
## 
##  studentized Breusch-Pagan test
## 
## data:  reg_lin_mult
## BP = 1071.7, df = 5, p-value < 2.2e-16

El estadístico de prueba BP fue: 1071.7 con 5 Grados de libertad (df) y un valor p < 0.00000000000000022

Como el valor p es extremadamente bajo indica que hay una evidencia muy fuerte en contra de la hipótesis nula, es decir, que la varianza de los errores es constante (homocedasticidad). En conclusión, dado que el valor p es significativamente menor que el nivel de significancia convencional (típicamente, 0.05), rechazamos la hipótesis nula. Esto significa que hay evidencia de heterocedasticidad en tus datos.

Lo anterior implica Invalidez de las pruebas de hipótesis. También indica que los estimadores son ineficientes, es decir, que pueden tener una varianza mayor de lo esperado. Otra implicación es que los intervalos de confianza y valores p son incorrectos. Los intervalos de confianza y los valores p calculados bajo el supuesto de homocedasticidad pueden ser demasiado estrechos o demasiado amplios, respectivamente.

Para corregirlo, nuevamente se debe establecer la causa del problema, identificando las variables que podrían estar generando la heterocedasticidad (Por ejemplo, cuando la varianza de los errores esta relacionada con el tamaño de alguna variable independiente). También se pueden transformar las variables y utilizar modelos de regresión como los modelos de mínimos cuadrados ponderados (WLS). Por último, ajustar los errores estándar utilizando técnicas como la corrección de White puede mejorar la inferencia estadística.

4.3. Independencia de Errores

Para realizar esta validación aplicaré la prueba Durbin-Watson

lmtest::dwtest(reg_lin_mult)
## 
##  Durbin-Watson test
## 
## data:  reg_lin_mult
## DW = 1.6569, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

El estadístico de prueba es DW = 1.6569 con un valor p muy pequeño. Este valor p indica que hay evidencia significativa para rechazar la hipótesis nula de que no hay autocorrelación en los residuos, es decir, que si hay autocorrelación de errores. El DW esta por debajo de 2, lo que indica una posible autocorrelación positiva. Lo anterior podría invalidar las inferencias basadas en el modelo de regresión, e indicar ineficiencia en los estimadores, lo que significa que pueden tener una varianza mayor de lo esperado. También puede indicar que las hipótesis son incorrectas.

Para solucionar lo anterior, la sugerencia es identificar la causa de la autocorrelación examinando las variables independientes y buscando posibles patrones que puedan estar causando la autocorrelación. Una vez se identifique la causa, para corregirla se pueden realizar transformaciones de las variables, o usar modelos de regresión autorregresivos o modelos de errores autorregresivos. Después de esto, se debe evaluar el modelo nuevamente para ver si se superó la autocorrelación.

4.4. Normalidad de los errores

El supuesto de normalidad establece que los residuos del modelo deben seguir una distribución normal. Para comprobarlo usaré el gráfico Q-Q (cuantiles-cuantiles) y realizaré la prueba estadística conocida como el test de Shapiro-Wilk. Si los puntos en el gráfico Q-Q siguen una línea recta, se puede decir que los residuos se distribuyen normalmente. Si existen desviaciones significativas de esta línea puede indicar que los residuos no siguen una distribución normal. Si esto llegase a ocurrir (residuos no normales), se deberían considerar transformaciones en la variable dependiente o emplear técnicas más robustas que no requieran asumir esta normalidad de los residuos.

qqPlot(reg_lin_mult, main = "Normal Q-Q Plot")

## [1] 1770 2827
shapiro.test(residuals(reg_lin_mult))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(reg_lin_mult)
## W = 0.86145, p-value < 2.2e-16

Con la prueba Shapiro-Wilk dio como resultado 0.86145, lo que es menor que 1, e indica una desviación de la normalidad. Un valor mas cercano a uno sugeriría una distribución más normal. El valor p tan pequeño (por debajo de 0.05) rechaza la hipótesis nula de que los datos provienen de una distribución normal, es decir, que hay evidencia significativa de que los datos no se distribuyen normalmente.

Al igual que con la Normalidad de los Errores, lo anterior implica invalidez en las inferencias, unos estimadores ineficientes y unas posibles hipótesis incorrectas. Para corregir esto se deben realizar transformaciones a las variables (Logaritmos, raíces cuadradas, etc.) buscando mejorar la normalidad de los residuos. También se pueden revisar los valores atípicos que puedan estar afectando al modelo.

4.5. Multicolinealidad

El supuesto de multicolinealidad espera que no haya una correlación significativa entre las variables independientes. Para realizar la verificación, utilizaré el Factor de Inflación de la Varianza (VIF). Si los valores son mayores a 10 esto indicaria problemas de multicolinealidad, indicando que las variables independientes están altamente correlacionadas entre sí. En caso de que esto sea detectado, se recomienda considerar la eliminación de variables redundantes o aplicar técnicas de reducción de dimensiones como el PCA (Análisis de Componentes Principales).

vif(reg_lin_mult)
## habitaciones parqueaderos       banios    areaconst      estrato 
##     1.455713     2.251191     3.029712     2.916277     1.705821

Todos los resultados son pequeños, siendo el mas alto 3, lo que indica que no hay problemas de multicolinealidad.

5. Partición 70%-30% del modelo

Realice una partición en los datos de forma aleatoria donde 70% sea un set para entrenar el modelo y 30% para prueba. Estime el modelo con la muestra del 70%. Muestre los resultados.

set.seed(12) 
train_index <- createDataPartition(aptos$preciom, p = 0.7, list = FALSE)
train_data <- aptos[train_index, ]
test_data <- aptos[-train_index, ]
new_model <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train_data)

summary(new_model)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = train_data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1035.24   -53.16     1.93    47.20   984.52 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -234.60572   18.71563  -12.54   <2e-16 ***
## areaconst       2.44225    0.06064   40.27   <2e-16 ***
## estrato        52.19268    3.49324   14.94   <2e-16 ***
## habitaciones  -51.66291    4.58479  -11.27   <2e-16 ***
## parqueaderos   75.24936    4.86369   15.47   <2e-16 ***
## banios         46.50431    3.97761   11.69   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 131 on 2935 degrees of freedom
##   (609 observations deleted due to missingness)
## Multiple R-squared:  0.8029, Adjusted R-squared:  0.8026 
## F-statistic:  2392 on 5 and 2935 DF,  p-value: < 2.2e-16

Los resultados son parecidos al modelo inicial.

El Intercepto (-234.61): Representa el valor esperado de preciom cuando todas las variables independientes son cero. Sin embargo, en este caso, no tiene una interpretación directa ya que no es realista que una propiedad tenga área cero, estrato cero, etc. areaconst (2.442): muestra que por cada unidad de aumento en el área construida, se espera que el precio de la propiedad aumente en 2.442 unidades, manteniendo constantes las demás variables. estrato (52.19): muestra que por cada unidad de aumento en el estrato socioeconómico, se espera que el precio de la propiedad aumente en 52.19 unidades, manteniendo constantes las demás variables. habitaciones (-51.66): Por cada habitación adicional, se espera que el precio de la propiedad disminuya en 51.66 unidades, manteniendo constantes las demás variables. Esto podría indicar que las habitaciones adicionales pueden tener un efecto negativo sobre el precio, posiblemente debido a factores como la distribución o el tamaño de las habitaciones. Esto es contrario a la lógica común en donde se espera que entre más habitaciones haya, mayor valor tenga un apartamento. parqueaderos (75.25): indica que por cada parqueadero adicional, se espera que el precio de la propiedad aumente en 75.25 unidades, manteniendo constantes las demás variables. banios (46.50): Por cada baño adicional, se espera que el precio de la propiedad aumente en 46.50 unidades, manteniendo constantes las demás variables.

Evaluación del Modelo

R-cuadrado (0.8029): El modelo explica aproximadamente el 80.29% de la variabilidad en el precio de las propiedades. Esto indica un buen ajuste del modelo a los datos. F-estadístico (2392): El F-estadístico es altamente significativo (p-valor < 0.00000000000000022), lo que sugiere que al menos una de las variables independientes es significativa en el modelo. Significancia individual de los coeficientes: Todos los coeficientes son altamente significativos (p-valor < 0.00000000000000022), lo que indica que todas las variables independientes contribuyen significativamente al modelo

6. Predicciones

Realice predicciones con el modelo anterior usando los datos de prueba (30%)

predictions <- predict(new_model, newdata = test_data)
resultados <- data.frame(Real = test_data$preciom, Prediccion = predictions)
head(resultados)
##   Real Prediccion
## 1  240   298.6074
## 2  220   189.6808
## 3  320   372.9514
## 4  385   366.4285
## 5  175   130.6219
## 6  820  1053.8952

7. Errores

Calcule el error cuadrático medio, el error absoluto medio y el R2, interprete.

7.1. Error Cuádrático Medio

# Calcular los errores de predicción
errores <- test_data$preciom - predictions

# Calcular el Error Cuadrático Medio (MSE)
mse <- mean(errores^2)

# Calcular el Error Absoluto Medio (MAE)
mae <- mean(abs(errores))

# Calcular el R-cuadrado (R²)
r2 <- caret::postResample(pred = predictions, obs = test_data$preciom)[2]

# Mostrar los resultados
cat("El Error Cuadrático Medio (MSE) es:", mse, "\n")
## El Error Cuadrático Medio (MSE) es: 17686.04

El Error Cuadrático Medio (MSE) mide el promedio de los cuadrados de los errores entre los valores predichos por el modelo y los valores reales. Cuanto más bajo sea el MSE, mejor se ajusta el modelo a los datos. En este caso dio un MSE de 17686.04, lo que significa que en promedio, el modelo se equivoca en aproximadamente 17686 unidades al predecir el valor de la variable dependiente. La magnitud de este valor dependerá de la escala de tu variable dependiente. Un MSE bajo indica un mejor ajuste del modelo.

7.2. Error Absoluto Medio

cat("El Error Absoluto Medio (MAE) es:", mae, "\n")
## El Error Absoluto Medio (MAE) es: 84.4133

El Error Absoluto Medio (MAE) mide el promedio de los valores absolutos de los errores. Es una medida más intuitiva del error promedio, ya que no penaliza los errores grandes tanto como el MSE. En este caso dio un MAE de 84.4133, lo que indica que, en promedio, el modelo se equivoca en 84.4133 unidades al hacer una predicción. Al igual que el MSE, un valor bajo indica un mejor ajuste. El MAE es más fácil de interpretar que el MSE porque está en las mismas unidades que la variable dependiente.

7.3. R²

cat("El R-cuadrado (R²) es:", r2, "\n")
## El R-cuadrado (R²) es: 0.8028947

El Coeficiente de determinación (R²) indica la proporción de la variabilidad de la variable dependiente que es explicada por el modelo. Un valor de R² cercano a 1 indica un mejor ajuste del modelo. PAra este caso, el R² fue igual a 0.8028947, esto significa que el modelo explica aproximadamente el 80.29% de la variabilidad en la variable dependiente. Es decir, el 80.29% de las variaciones en la variable dependiente pueden ser atribuidas a las variables independientes incluidas en el modelo. Un R² de 0.8029 es generalmente considerado como un buen ajuste, sin embargo, yo creo que debería buscarse una mejora.

Conclusión Final

Los indicadores muestran que el modelo es razonablemente bueno, pero considero que aun se podría mejorar más.

LS0tDQp0aXRsZTogIk1vZGVsb3MgRXN0YWTDrXN0aWNvcyBwYXJhIGxhIFRvbWEgZGUgRGVjaXNpb25lcyINCmF1dGhvcjogIkVucmlxdWUgSm9zw6kgUGXDsWEiDQpkYXRlOiAiMjAyNC0wOC0yNCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBoaWdobGlnaHQ6IGthdGUNCiAgICBwZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICB0dWZ0ZTo6dHVmdGVfaGFuZG91dDoNCiAgICBsYXRleF9lbmdpbmU6IHhlbGF0ZXgNCiAgYm9va2Rvd246OnBkZl9kb2N1bWVudDI6DQogICAgbGF0ZXhfZW5naW5lOiBsdWFsYXRleA0KICB3b3JkX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNA0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiA0DQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4DQpudW1iZXItc2VjdGlvbnM6IHRydWUNCnN1YnRpdGxlOiAiQWN0aXZpZGFkIDIgLSBNYWVzdHLDrWEgZW4gQ2llbmNpYSBkZSBEYXRvcyAtIFBVSiBDYWxpIg0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KI1IudmVyc2lvbg0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoInBsb3RseSIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiY2FyZXQiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgic3RhcmdhemVyIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygic3RhcmdhemVyIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJkZXZ0b29scyIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJwYXF1ZXRlTUVUT0RPUyIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoInBhcXVldGVNRVRPRE9TIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJkcGx5ciIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJuYW5pYXIiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJuYW5pYXIiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoImthYmxlRXh0cmEiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJrYWJsZUV4dHJhIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJndCIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImd0IikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJnZ3RoZW1lcyIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImdndGhlbWVzIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJHR2FsbHkiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoIm1pY2UiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJtaWNlIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJmb3JtYXR0YWJsZSIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImZvcm1hdHRhYmxlIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJHR2FsbHkiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoImdncGxvdDIiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJub3J0ZXN0IiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygibm9ydGVzdCIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiTUFTUyIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoIkZhY3RvTWluZVIiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJGYWN0b01pbmVSIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJmYWN0b2V4dHJhIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiY2x1c3RlciIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKQ0KfQ0KaWYoIXJlcXVpcmVOYW1lc3BhY2UoImxtdGVzdCIsIHF1aWV0bHkgPSBUUlVFKSl7DQogIGluc3RhbGwucGFja2FnZXMoImxtdGVzdCIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgiY2FyIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyIikNCn0NCmlmKCFyZXF1aXJlTmFtZXNwYWNlKCJ4ZnVuIiwgcXVpZXRseSA9IFRSVUUpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygieGZ1biIpDQp9DQppZighcmVxdWlyZU5hbWVzcGFjZSgicmVzaGFwZTIiLCBxdWlldGx5ID0gVFJVRSkpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJyZXNoYXBlMiIpDQp9DQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJjZW50cm9tYWdpcy9wYXF1ZXRlTU9ERUxPUyIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHN0YXJnYXplcikNCmxpYnJhcnkocGFxdWV0ZU1FVE9ET1MpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShuYW5pYXIpDQpsaWJyYXJ5KGdndGhlbWVzKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShndCkNCmxpYnJhcnkobWljZSkNCmxpYnJhcnkoZm9ybWF0dGFibGUpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KEdHYWxseSkNCiNsaWJyYXJ5KG5vcm10ZXN0KQ0KbGlicmFyeShmYWN0b2V4dHJhKQ0KbGlicmFyeShub3J0ZXN0KQ0KbGlicmFyeShGYWN0b01pbmVSKQ0KbGlicmFyeShjbHVzdGVyKQ0KbGlicmFyeShsbXRlc3QpDQpsaWJyYXJ5KGNhcikNCmxpYnJhcnkoeGZ1bikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KYGBgDQoNCmBgYHtyIGxvYWRfZGF0YX0NCiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImRnb254YWxleDgwL3BhcXVldGVNT0RFTE9TIiwgZm9yY2UgPSBUUlVFKQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiY2VudHJvbWFnaXMvcGFxdWV0ZU1PREVMT1MiLCBmb3JjZSA9IFRSVUUpDQojZGF0YSA8LSBwYXF1ZXRlTU9ERUxPUzo6dml2aWVuZGENCmRhdGEodml2aWVuZGEpDQpgYGANCiMjIERlc2NyaXBjacOzbiB2YXJpYWJsZXMgYmFzZSBkZSBkYXRvcw0KDQpBIGNvbnRpbnVhY2nDs24gc2UgbWVuY2lvbmFuIGxhcyB2YXJpYWJsZXMgcXVlIHNlIHV0aWxpemFyw6FuIGVuIGVsIGVqZXJjaWNpby4NCg0KKipab25hOioqIFJlZ2lzdHJhIGxhIHpvbmEgZG9uZGUgZXN0YSB1YmljYWRhIGVsIGlubXVlYmxlIGVzdGEgcHVlZGUgc2VyOiBab25hIENlbnRybyxab25hIE5vcnRlLFpvbmEgT2VzdGUsWm9uYSBPcmllbnRlLFpvbmEgU3VyDQoNCioqUGlzbyoqOiBSZWdpc3RyYSBlbCBwaXNvIGVuZSBsIGN1YWwgc2UgdWJpY2EgbGEgdml2aWVuZGENCg0KKipFc3RyYXRvOioqIFZhcmlhYmxlIGNvbiBlc2NhbGEgZGUgbWVkaWNpw7NuIG9yZGluYWwsbG9zIGVzdHJhdG8gZGUgbGFzIGFwdG9zIHNvbiAzLDQsNSw2DQoNCioqUHJlY2lvbToqKiBQcmVjaW8gZGUgbGEgdml2aWVuZGENCg0KKiphcmVhY29uc3R1OioqIEFyZWEgQ29uc3RydWlkYQ0KDQoqKnBhcnF1ZWFkZXJvczoqKiBOdW1lcm8gZGUgcGFycXVlYWRlcm9zIGRlIGxhIHZpdmllbmRhDQoNCioqYmFuaW9zOioqIE7Dum1lcm8gZGUgYmHDsW9zIGRlIGxhIHByb3BpZWRhZA0KDQoqKkhhYml0YWNpb25lczoqKiBOw7ptZXJvIGRlIGhhYml0YWNpb25lcyBxdWUgcG9zZWUgbGEgdml2aWVuZGENCg0KKipUaXBvOioqIFRpcG8gZGUgbGEgdml2aWVuZGEsIGNhc2EgbyBhcGFydGFtZW50bw0KDQoqKkJhcnJpbzoqKiBCYXJyaW8gZG9uZGUgZXN0w6EgbGEgdml2aWVuZGENCg0KKipMb25naXR1ZDoqKiBMb25naXR1ZCBnZW9ncsOhZmljYQ0KDQoqKkxhdGl0dWQ6KiogTGF0aXR1ZCBnZW9ncsOhZmljYQ0KDQoqTm90YToqIFNlIGVsaW1pbsOzIGVsIElEIHBvciBzZXIgdW5hIHZhcmlhYmxlIHF1ZSBubyBjb250cmlidXllIGVuIGVsIGVqZXJjaWNpbyBuaSBhcG9ydGEgaW5mb3JtYWNpw7NuIHJlbGV2YW50ZSBwYXJhIGVsIG1vZGVsby4NCg0KDQojIDAuIFJlY29ub2NpbWllbnRvIGRlbCBEYXRhIHNldCB5IHByZWxpbXBpZXphLg0KYGBge3IgU3VtbWFyeX0NCnN1bW1hcnkodml2aWVuZGEpDQpgYGANCg0KVmVtb3MgcXVlIHBhcmEgY2FzaSB0b2RvcyBsb3MgYXRyaWJ1dG9zIGhheSAzIHJlZ2lzdHJvcyBjb24gdmFsb3JlcyBwZXJkaWRvcyIuIEVzb3MgbG9zIHZhbW9zIGEgcXVpdGFyIGFzdW1pZW5kbyBxdWUgc29uIGxvcyBtaXNtb3MgMyBwYXJhIHRvZGFzIGxhcyBjb2x1bW5hcy4gRGViZW1vcyByZXZpc2FyIHNpIHBlcnNpc3RlbiBlbiBlbCBzdWJjb25qdW50byBkZSB0cmFiYWpvLiBQYXJhIGxvcyBwYXJxdWVhZGVyb3MgaGF5IDE2MDUgZGUgZXNlIHRpcG8uIFRhbWJpw6luIGxvcyBvYnNlcnZhcmVtb3MgY3VhbmRvIGNyZWVtb3MgZWwgc3ViY29uanVudG8uICBWYW1vcyBhIGJvcnJhciBsYSBjb2x1bW5hIElEIHB1ZXMgbm8gYXBvcnRhIG5hZGEgcGFyYSBudWVzdHJvIG9iamV0aXZvLg0KDQpgYGB7ciBwcmVjbGVhbn0NCmFwdG9zIDwtIHN1YnNldCh2aXZpZW5kYSwgIWlzLm5hKHZpdmllbmRhJGVzdHJhdG8pKQ0KYXB0b3MkaWQgPC0gTlVMTA0Kc3VtbWFyeShhcHRvcykNCmBgYA0KDQpgYGB7ciBUeXBlc30NCnRhYmxlKGFwdG9zJHRpcG8pDQpgYGANClZlbW9zIHF1ZSBoYXkgNTEwMCBBcGFydGFtZW50b3MgeSAzMjE5IENhc2FzIGVuIGVsIGRhdGFzZXQgaW5pY2lhbA0KDQojIDEuIEZpbHRyYXIgbGEgYmFzZSBkZSBkYXRvcw0KDQoqKlJlYWxpY2UgdW4gZmlsdHJvIGEgbGEgYmFzZSBkZSBkYXRvcyBlIGluY2x1eWEgc8OzbG8gbGFzIG9mZXJ0YXMgZGUgYXBhcnRhbWVudG9zLiBQcmVzZW50ZSBsb3MgcHJpbWVyb3MgMyByZWdpc3Ryb3MgZGUgbGFzIGJhc2VzIHkgYWxndW5hcyB0YWJsYXMgcXVlIGNvbXBydWViZW4gbGEgY29uc3VsdGEuKiogDQoNCg0KUGFyYSBsb2dyYXIgZXN0bywgc2UgcHJvY2VkZSBhIGVsaW1pbmFyIHRvZG9zIGxvcyByZWdpc3Ryb3MgZGUgbGFzIGNhc2FzIHF1aXRhbmRvIGRlbCBkYXRhc2V0IHRvZG8gbG8gcXVlIG5vIHNlYSBkZSBlc2UgdGlwby4NCg0KDQpgYGB7ciBkZWxldGVfaG91c2VzfQ0KYXB0b3MgPC0gc3Vic2V0KGFwdG9zLCB0aXBvID09ICdBcGFydGFtZW50bycpDQp0YWJsZShhcHRvcyR0aXBvKQ0KdGFibGUoYXB0b3MkYmFuaW9zKQ0KdGFibGUoYXB0b3MkaGFiaXRhY2lvbmVzKQ0KaGVhZChhcHRvcywzKQ0Kc3VtbWFyeShhcHRvcykNCmBgYA0KDQpWZW1vcyBxdWUgeWEgbm8gaGF5IGNhc2FzIGVuIGVsIGRhdGFzZXQsIHNpbiBlbWJhcmdvLCBzaSBoYXkgYXBhcnRhbWVudG9zIGNvbiAwIGJhw7FvcyB5IGNvbiAwIGhhYml0YWNpb25lcywgYWxnbyBxdWUgZXMgaW1wcm9iYWJsZS4gRXN0b3MgcmVnaXN0cm9zIHNlIGVsaW1pbmFyw6FuLg0KDQpgYGB7ciBjbGVhbl9pbXByb2JhYmxlX2Nhc2VzfQ0KYXB0b3MgPC0gc3Vic2V0KGFwdG9zLCBiYW5pb3MgIT0gMCkNCmFwdG9zIDwtIHN1YnNldChhcHRvcywgaGFiaXRhY2lvbmVzICE9IDApDQp0YWJsZShhcHRvcyRiYW5pb3MpDQp0YWJsZShhcHRvcyRoYWJpdGFjaW9uZXMpDQpgYGANCg0KU2kgYmllbiBubyBzYWJlbW9zIHRvZGF2w61hIG5hZGEgbcOhcyBlbiBwcm9mdW5kaWRhZCBzb2JyZSBlbCBkYXRhc2V0LCB5YSBoZW1vcyBlbGltaW5hZG8gcmVnaXN0cm9zIHZhY8Otb3MgZSBpbXByb2JhYmxlcywgYWwgaWd1YWwgcXVlIHVuYSBjb2x1bW5hIHF1ZSBubyBzaXJ2ZSBwYXJhIGVsIGFuw6FsaXNpcy4gSGFnYW1vcyB1biDDumx0aW1vIGFuw6FsaXNpcyBjb21wYXJhbmRvIGF0cmlidXRvcyBjb21vIGVsIHByZWNpbyB5IGVsIMOhcmVhLCBsYSBjYW50aWRhZCBkZSBiYcOxb3MgeSBlbCDDoXJlYSwgeSBsYSBjYW50aWRhZCBkZSBoYWJpdGFjaW9uZXMgeSBlbCDDoXJlYSwgcGFyYSBpZGVudGlmaWNhciBzaSBoYXkgcmVnaXN0cm9zIHF1ZSBubyBwYXJlemNhbiB0ZW5lciBpbmZvcm1hY2nDs24gcmVhbC4NCg0KYGBge3IgbWFueV9yb29tc30NCm1hbnlfcm9vbXMgPC0gYXB0b3MgJT4lDQogIGZpbHRlcihoYWJpdGFjaW9uZXMgPj0gNykNCm1hbnlfcm9vbXMNCmBgYA0KVmVtb3MgcXVlIGhheSB1biBhcGFydGFtZW50byBkZSAzMTUgbWV0cm9zIGN1YWRyYWRvcywgY29uIDkgaGFiaXRhY2lvbmVzIHkgNSBiYcOxb3MsIHF1ZSBzb2xvIGN1ZXN0YSAyNDAgbWlsbG9uZXMuIE5vIHBhcmVjZSBsw7NnaWNvLiBTZSBlbGltaW5hcsOhLiAgRWwgZGUgNyBoYWJpdGFjaW9uZXMsIGNvbiA3IGJhw7FvcyBkZSAxNTAwIG1pbGxvbmVzIHNpIHBhcmVjZSBwb3NpYmxlLiBTZSBtYW50ZW5kcsOhLg0KDQoNCmBgYHtyIG1hbnlfYmF0aHJvb21zfQ0KbWFueV9iYXRocm9vbXMgPC0gYXB0b3MgJT4lDQogIGZpbHRlcihiYW5pb3MgPj0gNykNCm1hbnlfYmF0aHJvb21zDQpgYGANCg0KTGEgbWF5b3LDrWEgZGUgZXN0b3MgcmVnaXN0cm8gc2kgcGFyZWNlbiBwb3NpYmxlcyBwb3IgZWwgw6FyZWEgeSBlbCBwcmVjaW8sIGF1bnF1ZSBlbCBkZSAyNTAgbTIgYSA5ODAgbWlsbG9uZXMgY29uIDcgYmHDsW9zIHkgNCBoYWJpdGFjaW9uZXMgbm8gcGFyZWNlIGNvaGVyZW50ZSBjb24gZWwgcmVzdG8uIFNlIGVsaW1pbmFyw6EgZGlzbWludXllbmRvIGVzdG9zIHZhbG9yZXMgYXTDrXBpY29zLg0KDQpgYGB7ciBkZWxldGluZ19leHBlbnNpdmVzX291dGxpZXJzfQ0KDQp0b29fZXhwZW5zaXZlIDwtIGFwdG9zICU+JQ0KICBmaWx0ZXIoYXJlYWNvbnN0ID49IDUwMCAmIHByZWNpb20gPD0gNTAwKQ0KdG9vX2V4cGVuc2l2ZQ0KI2FwdG9zIDwtIGFwdG9zICU+JQ0KIyAgZmlsdGVyKHByZWNpb20gIT0gNyB8IGhhYml0YWNpb25lcyAhPSA0KQ0KYGBgDQoNClBvciDDumx0aW1vLCB2ZW1vcyB1biBpbm11ZWJsZSBkZSB2YWxvciAyOTkgbWlsbG9uZXMgY29uIHVuIMOhcmVhIGNvbnN0cnVpZGEgZGUgOTMyIG1ldHJvcyBjdWFkcmFkb3MuIEVzdGUgcmVnaXN0cm8gdGFtYmnDqW4gc2UgY29uc2lkZXJhIGF0w61waWNvIHkgc2UgZWxpbWluYXLDoS4NCg0KYGBge3IgZGVsZXRpbmdfb2J2aW91c19vdXRsaWVyc30NCmFwdG9zIDwtIGFwdG9zICU+JQ0KICBmaWx0ZXIoaGFiaXRhY2lvbmVzICE9IDkpDQphcHRvcyA8LSBhcHRvcyAlPiUNCiAgZmlsdGVyKCEoYXJlYWNvbnN0ID49IDUwMCAmIHByZWNpb20gPD0gNTAwKSkNCmFwdG9zIDwtIGFwdG9zICU+JQ0KICBmaWx0ZXIoYmFuaW9zICE9IDcgfCBoYWJpdGFjaW9uZXMgIT0gNCkNCnRhYmxlKGFwdG9zJGJhbmlvcykNCnRhYmxlKGFwdG9zJGhhYml0YWNpb25lcykNCg0KYGBgDQoNCg0KDQojIDIuCUFuw6FsaXNpcyBFeHBsb3JhdG9yaW8geSBDb3JyZWxhY2lvbmVzDQoNCioqUmVhbGljZSB1biBhbsOhbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGRhdG9zIGVuZm9jYWRvIGVuIGxhIGNvcnJlbGFjacOzbiBlbnRyZSBsYSB2YXJpYWJsZSByZXNwdWVzdGEgKHByZWNpbyBkZSBsYSBjYXNhKSBlbiBmdW5jacOzbiBkZWwgw6FyZWEgY29uc3RydWlkYSwgZXN0cmF0bywgbnVtZXJvIGRlIGJhw7FvcywgbnVtZXJvIGRlIGhhYml0YWNpb25lcyB5IHpvbmEgZG9uZGUgc2UgdWJpY2EgbGEgdml2aWVuZGEuIFVzZSBncsOhZmljb3MgaW50ZXJhY3Rpdm9zIGNvbiBlbCBwYXF1ZXRlIF9wbG90bHlfIGUgaW50ZXJwcmV0ZSBsb3MgcmVzdWx0YWRvcy4qKg0KDQpFbXBlemFyZW1vcyBjb252aXJ0aWVuZG8gbGFzIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgcHJlc2VudGVzIGVuIGVsIGRhdGFzZXQgZW4gZmFjdG9yZXMuICBFc3RhcyBzb246IFRpcG8sIFBpc28sIFpvbmEgeSBCYXJyaW8uDQoNCmBgYHtyfQ0KYXB0b3MkdGlwbyA8LSBhcy5mYWN0b3IoYXB0b3MkdGlwbykNCmFwdG9zJHBpc28gPC0gYXMuZmFjdG9yKGFwdG9zJHBpc28pDQphcHRvcyR6b25hIDwtIGFzLmZhY3RvcihhcHRvcyR6b25hKQ0KYXB0b3MkYmFycmlvIDwtIGFzLmZhY3RvcihhcHRvcyRiYXJyaW8pDQpzdW1tYXJ5KGFwdG9zKQ0KYGBgDQpWZWFtb3MgbGEgZGlzdHJpYnVjacOzbiBkZSBsb3MgQXBhcnRhbWVudG9zIHNlZ8O6biBzdSBlc3RyYXRvIHkgc3Ugem9uYS4NCg0KYGBge3IgYXB0b3NfYnlfem9uZXNfYW5kX3N0cmF0dW19DQpnZ3Bsb3QoYXB0b3MsIGFlcyh4ID0gZXN0cmF0bywgZmlsbCA9IHpvbmEpKSArDQogIGdlb21fYmFyKCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gZGUgaW5tdWVibGVzIHBvciBlc3RyYXRvIiwNCiAgICAgICB4ID0gIkVzdHJhdG8iLA0KICAgICAgIHkgPSAiQ2FudGlkYWQgZGUgaW5tdWVibGVzIikNCmBgYA0KDQpTZSB2ZSBxdWUgZW4gZWwgZXN0cmF0byA1IGVzIGRvbmRlIG3DoXMgYXBhcnRhbWVudG9zIGhheSwgc2VndWlkbyBwb3IgZWwgNCB5IGVsIDYuICBUYW1iacOpbiBzZSB2ZSBxdWUgbGEgem9uYSBTdXIgZXMgbGEgcXVlIG3DoXMgY3VlbnRhIGNvbiBhcGFydGFtZW50b3MsIHNlZ3VpZG8gZGUgbGEgTm9ydGUgeSBkZSBsYSBPZXN0ZS4NCg0KVmVhbW9zIGFob3JhIGNvbW8gZXMgbGEgZGlzdHJpYnVjacOzbiBkZSBsYXMgem9uYXMsIHNlZ8O6biBzdSByZWxhY2nDs24gZW50cmUgZWwgcHJlY2lvIHkgZWwgw6FyZWEgY29uc3RydWlkYS4gIFNlIGVzcGVyYXLDrWEgcXVlIGVudHJlIG3DoXMgw6FyZWEsIG3DoXMgcHJlY2lvLCBwZXJvIG5vIHNhYmVtb3MgZW4gcXXDqSB6b25hcyBzZSBwcmVzZW50YW4gZGljaG9zIGFwYXJ0YW1lbnRvcy4gDQoNCg0KYGBge3IgZGlzdHJpYnV0aW9uX29mX3pvbmVzX3Blcl9hcmVhX2FuZF96b25lfQ0KYXJlYV96b25lX3ByaWNlIDwtIGdncGxvdChhcHRvcywgYWVzKHggPSBwcmVjaW9tLCB5ID0gYXJlYWNvbnN0KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHpvbmEpKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgbGFicyh0aXRsZSA9ICLDgXJlYSB2cyBQcmVjaW8gcG9yIFpvbmEiLA0KICAgICAgIHggPSAiUHJlY2lvIChtaWxsb25lcykiLA0KICAgICAgIHkgPSAiw4FyZWEgQ29uc3RydWlkYSAobTIpIikgKw0KICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCAxLCAxLCAxLCAiY20iKSkgIysNCiAgI3hsaW0oMCwgMjUwMCkgKyAgIyBBanVzdGEgZWwgbMOtbWl0ZSBpbmZlcmlvciB5IHN1cGVyaW9yIGRlbCBlamUgeA0KICAjeWxpbSgwLCAxMDAwKSAgICMgQWp1c3RhIGVsIGzDrW1pdGUgaW5mZXJpb3IgeSBzdXBlcmlvciBkZWwgZWplIHkNCmdncGxvdGx5KGFyZWFfem9uZV9wcmljZSkNCg0KYGBgDQoNCkVuIGVsIGVqZSBYLCBsYSBncmFuIG1heW9yw61hIGRlIGxvcyBkYXRvcyBzZSBlbmN1ZW50cmEgcG9yIGRlYmFqbyBkZSBsb3MgbWlsIG1pbGxvbmVzIGRlIHBlc29zLiAgRW4gZWwgZWplIHksIGxhIG1heW9yw61hIGRlIGxvcyBkYXRvcyBlc3RhIHBvciBkZWJham8gZGUgbG9zIDMwMCBtZXRyb3MgY3VhZHJhZG9zIGNvbnN0cnVpZG9zLiBFbiBjdWFudG8gYSBsYXMgem9uYXMsIHNlIHZlIHF1ZSBlbiBsYSB6b25hIE9lc3RlIGhheSBhcGFydGFtZW50b3MgZGUgbWF5b3IgdGFtYcOxbyB5IG1heW9yIHByZWNpbywgc2llbmRvIG3DoXMgY29tdW5lcyBsb3MgYXBhcnRhbWVudG9zIGRlIG1hcyBkZSBtaWwgbWlsbG9uZXMgZW4gZXN0YSB6b25hLg0KDQpWZWFtb3MgYWhvcmEsIGNvbiBlbCBjb25qdW50byBkZSBkYXRvcyBxdWUgaGVtb3MgZGVwdXJhZG8sIGVsIGRpYWdyYW1hIGRlIGNhamFzIHkgYmlnb3RlcyBxdWUgbm9zIG1vc3RyYXLDoSBsb3MgdmFsb3JlcyBhdMOtcGljb3MgcGFyYSBlc3RvcyBkYXRvcy4NCg0KYGBge3IgYm94cGxvdF96b25lc30NCmJveHBsb3Rfem9uZXMgPC0gZ2dwbG90KGFwdG9zLCBhZXMoeCA9IGVzdHJhdG8sIHkgPSBwcmVjaW9tKSkgKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSB6b25hKSkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArDQogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBkZSBQcmVjaW9zIHBvciBFc3RyYXRvIHkgWm9uYSIsDQogICAgICAgeCA9ICJFc3RyYXRvIiwNCiAgICAgICB5ID0gIlByZWNpbyAobWlsbG9uZXMpIikgKw0KICB4bGltKDIsIDcpICMrICAjIEFqdXN0YSBlbCBsw61taXRlIGluZmVyaW9yIHkgc3VwZXJpb3IgZGVsIGVqZSB4DQogICN5bGltKDAsIDEwMDApICAgIyBBanVzdGEgZWwgbMOtbWl0ZSBpbmZlcmlvciB5IHN1cGVyaW9yIGRlbCBlamUgeQ0KDQpnZ3Bsb3RseShib3hwbG90X3pvbmVzKQ0KYGBgDQoNCkxvcyBlc3RyYXRvcyA1IHkgNiBlc3TDoW4gcHLDoWN0aWNhbWVudGUgY29uZm9ybWFkb3MgcG9yIGFwYXJ0YW1lbnRvcyBkZSBsYSB6b25hIE9lc3RlIHUgU3VyLCBoYWJpZW5kbyBncmFuIGNhbnRpZGFkIGRlIGFwYXJ0YW1lbnRvcyBkZSBsYSB6b25hIG9lc3RlIHRhbWJpw6luIGVuIGxvcyBlc3RyYXRvcyAzIHkgNC4gIFRhbWJpw6luIHNlIHZlIGVuIGxhIGdyw6FmaWNhIHF1ZSBhIG1lZGlkYSBxdWUgYXVtZW50YSBlbCBlc3RyYXRvLCB0YW1iacOpbiBhdW1lbnRhIGVsIHByZWNpbyBtw61uaW1vIHkgbcOheGltbyBkZSBsb3MgYXBhcnRhbWVudG9zLiANCg0KTGEgZ3LDoWZpY2EgYW50ZXJpb3Igbm8gbm9zIG11ZXN0cmEgaW5mb3JtYWNpw7NuIHJlZmVyZW50ZSBhbCBwcmVjaW8gY29uIHJlc3BlY3RvIGEgbGEgY2FudGlkYWQgZGUgYmHDsW9zIG8gaGFiaXRhY2lvbmVzIGRlIGxvcyBhcGFydGFtZW50b3MuICBWZcOhbW9zbG8gZW4gbGEgc2lndWllbnRlIGdyw6FmaWNhOg0KDQpgYGB7ciBwcmljZV9yb29tc19iYXRocm9vbXNfZGlzdHJpYnV0aW9ufQ0KcHJpY2Vfcm9vbXNfYmF0aHJvb21zX2Rpc3RyaWJ1dGlvbiA8LSBnZ3Bsb3QoYXB0b3MsIGFlcyh4ID0gcHJlY2lvbSwgeSA9IGJhbmlvcykpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IoaGFiaXRhY2lvbmVzKSkpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gZGUgUHJlY2lvIHBvciBjYW50aWRhZCBkZSBCYcOxb3MgeSBIYWJpdGFjaW9uZXMiLA0KICAgICAgIHggPSAiUHJlY2lvIGVuIG1pbGxvbmVzIiwNCiAgICAgICB5ID0gIkNhbnRpZGFkIGRlIEJhw7FvcyIsDQogICAgICAgY29sb3IgPSAiSGFiaXRhY2lvbmVzIikgDQpnZ3Bsb3RseShwcmljZV9yb29tc19iYXRocm9vbXNfZGlzdHJpYnV0aW9uKQ0KYGBgDQoNCkVuIGxhIGdyw6FmaWNhIHNlIHZlIHVuIGNvbXBvcnRhbWllbnRvIHF1ZSBwb2Ryw61hIGVzcGVyYXJzZSwgeSBlcyBxdWUgYSBtZWRpZGEgZW4gcXVlIHZhIHN1YmllbmRvIGxhIGNhbnRpZGFkIGRlIGJhw7FvcywgdmEgc3ViaWVuZG8gZWwgcHJlY2lvLCBjb24gdW5hIGV4Y2VwY2nDs24gY2xhcmEgZW4gZWwgYXBhcnRhbWVudG8gZGUgOCBiYcOxb3MgeSB1biB2YWxvciBkZSA3MzAgbWlsbG9uZXMgZGUgcGVzb3MuIFRhbWJpw6luIHNlIHZlIHF1ZSBubyBuZWNlc2FyaWFtZW50ZSBlbCBxdWUgdGVuZ2EgbcOhcyBiYcOxb3MgZXMgZWwgbcOhcyBjb3N0b3NvLCBwZXJvIHNpIHNlIHZlIHVuYSBjbGFyYSByZWxhY2nDs24uICANCg0KVmVhbW9zIGFob3JhIHNpIGxhIGNvcnJlbGFjacOzbiBudW3DqXJpY2EgZW50cmUgbGFzIHZhcmlhYmxlcyBncmFmaWNhZGFzIGFudGVyaW9ybWVudGUuDQoNCmBgYHtyIGNvcnJlbGF0aW9ufQ0Kcm91bmRfY29yIDwtIHJvdW5kKGNvcihhcHRvc1ssIGMoImFyZWFjb25zdCIsICJlc3RyYXRvIiwgImJhbmlvcyIsICJoYWJpdGFjaW9uZXMiLCAicHJlY2lvbSIpXSwgdXNlID0gImNvbXBsZXRlLm9icyIpLDIpDQpwcmludChyb3VuZF9jb3IpDQpgYGANCg0KTGEgdGFibGEgbm9zIG11ZXN0cmEgcXVlIGxhcyBjb3JyZWxhY2lvbmVzIG3DoXMgYWx0YXMgc29uIGRlbCDDoXJlYSBjb25zdHJ1aWRhIGNvbiBlbCBwcmVjaW8gKDAuODUpLCB5IGRlIGxhIGNhbnRpZGFkIGRlIGJhw7FvcyBjb24gZWwgw6FyZWEgY29uc3RydWlkYSAoMC43NiksIHkgdGFtYmnDqW4gY29uIGVsIHByZWNpbyAoMC43NSkuIExlIHNpZ3VlIGVuIG1hZ25pdHVkLCBsYSBjb3JyZWxhY2nDs24gZW50cmUgZWwgZXN0cmF0byB5IGVsIHByZWNpbyAoMC42NykuICBMYSBjb3JyZWxhY2nDs24gbcOhcyBiYWphIGRlIHVuYSB2YXJpYWJsZSBjb24gZWwgcHJlY2lvIGVzIGxhIGRlIGxhIGNhbnRpZGFkIGRlIGhhYml0YWNpb25lcyAoMC4zMSksIHkgZW4gZ2VuZXJhbCwgbGEgZGVsIG7Dum1lcm8gZGUgaGFiaXRhY2lvbmVzIGNvbiBsYXMgZGVtw6FzIHZhcmlhYmxlcy4NCg0KIyAzLiBNb2RlbG8gZGUgUmVncmVzacOzbiBMaW5lYWwgTcO6bHRpcGxlICANCg0KKipFc3RpbWUgdW4gbW9kZWxvIGRlIHJlZ3Jlc2nDs24gbGluZWFsIG3Dumx0aXBsZSBjb24gbGFzIHZhcmlhYmxlcyBkZWwgcHVudG8gYW50ZXJpb3IgKHByZWNpbyA9IGYow6FyZWEgY29uc3RydWlkYSwgZXN0cmF0bywgbsO6bWVybyBkZSBjdWFydG9zLCBuw7ptZXJvIGRlIHBhcnF1ZWFkZXJvcywgbsO6bWVybyBkZSBiYcOxb3MgKSApIGUgaW50ZXJwcmV0ZSBsb3MgY29lZmljaWVudGVzIHNpIHNvbiBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZvcy4gTGFzIGludGVycHJldGFjaW9uZXMgZGViZXIgZXN0w6FuIGNvbnRleHR1YWxpemFkYXMgeSBkaXNjdXRpciBzaSBsb3MgcmVzdWx0YWRvcyBzb24gbMOzZ2ljb3MuIEFkaWNpb25hbG1lbnRlIGludGVycHJldGUgZWwgY29lZmljaWVudGUgUjIgeSBkaXNjdXRhIGVsIGFqdXN0ZSBkZWwgbW9kZWxvIGUgaW1wbGljYWNpb25lcyAocXVlIHBvZHLDrWFuIGhhY2VyIHBhcmEgbWVqb3JhcmxvKSoqDQoNCmBgYHtyIHJlZ19saW5fbXVsdH0gDQpyZWdfbGluX211bHQgPC0gbG0ocHJlY2lvbSB+IGhhYml0YWNpb25lcyArIHBhcnF1ZWFkZXJvcyArIGJhbmlvcyArIGFyZWFjb25zdCArIGVzdHJhdG8gLCBkYXRhID0gYXB0b3MpDQpzdW1tYXJ5KHJlZ19saW5fbXVsdCkNCmBgYA0KDQpfTm90YSBBY2xhcmF0b3JpYTpfIEF1bnF1ZSBlbCBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgbcO6bHRpcGxlIHB1ZWRlIHByb3BvcmNpb25hciB1bmEgYnVlbmEgZXhwbGljYWNpw7NuIGRlIGxhIHZhcmlhYmlsaWRhZCBlbiBsb3MgcHJlY2lvcyBkZSBsYXMgcHJvcGllZGFkZXMsIGVzIGltcG9ydGFudGUgcmVjb3JkYXIgcXVlIGxhIGNvcnJlbGFjacOzbiBubyBpbXBsaWNhIGNhdXNhbGlkYWQsIGVzIGRlY2lyLCBhdW5xdWUgbGFzIHZhcmlhYmxlcyBlc3TDqW4gYXNvY2lhZGFzIGNvbiBlbCBwcmVjaW8sIG5vIG5lY2VzYXJpYW1lbnRlIGxvIGNhdXNhbi4gT3Ryb3MgZmFjdG9yZXMgbm8gaW5jbHVpZG9zIGVuIGVsIG1vZGVsbyBwb2Ryw61hbiB0YW1iacOpbiBpbmZsdWlyIGVuIGVsIHByZWNpby4gIFNlIG1lIG9jdXJyZW4gcG9yIGVqZW1wbG8gb3RyYXMgY2FyYWN0ZXLDrXN0aWNhcyBkZWwgaW5tdWVibGUgY29tbyBlbCBlc3RhZG8gZGUgbGEgZmFjaGFkYSwgZGUgbGEgcGludHVyYSwgZGVsIHRlY2hvL2dvdGVyYXMsIHJ1aWRvIGRlIGxhIHpvbmEsIGFjY2VzbyB2ZWhpY3VsYXIsIGNvbmdlc3Rpw7NuIGRlIGxhIHpvbmEsIGV0Yy4NCg0KKipJbnRlcnByZXRhY2nDs24gZGUgbG9zIENvZWZpY2llbnRlcyoqDQoNCkVsICoqSW50ZXJjZXB0byAoLTIyNC40NzcpOioqIFJlcHJlc2VudGEgZWwgcHJlY2lvIHByb21lZGlvIGRlIHVuYSBwcm9waWVkYWQgcXVlIG5vIHRpZW5lIG5pbmd1bmEgZGUgbGFzIGNhcmFjdGVyw61zdGljYXMgY29uc2lkZXJhZGFzLCBlcyBkZWNpciwgbmluZ3VuYSBoYWJpdGFjacOzbiwgYmHDsW8sIHBhcnF1ZWFkZXJvLCBldGMuIEVuIGxhIHZpZGEgcmVhbCBlc3RvIG5vIHRlbmRyw61hIHNlbnRpZG8gcGVybyBlcyBsYSByZWZlcmVuY2lhIHBhcmEgbGEgZWN1YWNpw7NuIHJlc3VsdGFudGUgZGVsIG1vZGVsby4NCg0KTG9zICoqQ29lZmljaWVudGVzIGRlIGxhcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXM6KiogcXVlIHNlIG11ZXN0cmFuIGVuIGxhIHRhYmxhIHNvbjoNCg0KKipIYWJpdGFjaW9uZXMgKC00Mi4wMjcpOioqIENhZGEgaGFiaXRhY2nDs24gYWRpY2lvbmFsIGRpc21pbnV5ZSBlbCBwcmVjaW8gZW4gcHJvbWVkaW8gJDQyLCBsbyBxdWUgbm8gc3VlbmEgbXV5IGzDs2dpY28sIHBlcm8gcHVlZGUgZGViZXJzZSBhIG90cm9zIGZhY3RvcmVzIHF1ZSBubyBzZSBjb25zaWRlcmFyb24uIEVzdG8gbWUgZGVqYSBwZW5zYXRpdm8uDQoqKlBhcnF1ZWFkZXJvcyAoODkuMDc5KToqKiBDYWRhIHBhcnF1ZWFkZXJvIGFkaWNpb25hbCBhdW1lbnRhIGVsIHByZWNpbyBlbiBwcm9tZWRpbyAkODkuIEEgZGlmZXJlbmNpYSBkZWwgY29lZmljaWVudGUgYW50ZXJpb3IsIGVzdGUgc2kgcGFyZWNlIHRlbmVyIHNlbnRpZG8uIEVzIGVsIGNvZWZpY2llbnRlIG3DoXMgYWx0byBkZSB0b2Rvcy4NCioqQmFuaW9zICg0MC45OTQpOioqIENhZGEgYmHDsW8gYWRpY2lvbmFsIGF1bWVudGEgZWwgcHJlY2lvIGVuIHByb21lZGlvICQ0MSBwZXNvcy4NCioqQXJlYWNvbnN0ICgyLjM3NCk6KiogQ2FkYSBtZXRybyBjdWFkcmFkbyBhZGljaW9uYWwgYXVtZW50YSBlbCBwcmVjaW8gZW4gcHJvbWVkaW8gJDIuMyBwZXNvcy4gRXN0byB0YW1iacOpbiBwYXJlY2UgY29udHJhaW50dWl0aXZvLCBwZXJvIHB1ZWRlIHNlciBwb3JxdWUgbG9zIGJhw7FvcyB5IHBhcnF1ZWFkZXJvcyBhdW1lbnRhbiBlbCDDoXJlYSBjb25zdHJ1aWRhLCB5IHBhcmVjZW4gY29udHJpYnVpciBtw6FzIGFsIHByZWNpbyBxdWUgbGEgbWlzbWEgw6FyZWEuDQoqKkVzdHJhdG8gKDQ0LjEzNSk6KiogQ2FkYSBuaXZlbCBtw6FzIGFsdG8gZGUgZXN0cmF0byBzb2NpbyBlY29uw7NtaWNvIGF1bWVudGEgZWwgcHJlY2lvIGVuIHByb21lZGlvICQ0NC4NCg0KKipTaWduaWZpY2FuY2lhIGRlIGxvcyBDb2VmaWNpZW50ZXMqKg0KTG9zIHZhbG9yZXMgZGUgcCAoUHIoPnx0fCkpIGFzb2NpYWRvcyBhIGNhZGEgY29lZmljaWVudGUgaW5kaWNhbiBsYSBwcm9iYWJpbGlkYWQgZGUgcXVlIGVsIGNvZWZpY2llbnRlIHNlYSByZWFsbWVudGUgY2VybyBlbiBsYSBwb2JsYWNpw7NuLiBFbiBlc3RlIGNhc28sIHRvZG9zIGxvcyB2YWxvcmVzIGRlIHAgc29uIGV4dHJlbWFkYW1lbnRlIHBlcXVlw7FvcyAobWVub3JlcyBhIDAuMDAxKSwgbG8gcXVlIHNpZ25pZmljYSBxdWUgcG9kZW1vcyByZWNoYXphciBsYSBoaXDDs3Rlc2lzIG51bGEgZGUgcXVlIGVsIGNvZWZpY2llbnRlIGVzIGNlcm8gY29uIHVuIGFsdG8gbml2ZWwgZGUgY29uZmlhbnphLiBFbiBvdHJhcyBwYWxhYnJhcywgX3RvZGFzIGxhcyB2YXJpYWJsZXMgaW5jbHVpZGFzIGVuIGVsIG1vZGVsbyBzb24gZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2YXMgZW4gbGEgcHJlZGljY2nDs24gZGVsIHByZWNpbyxfIGxvIHF1ZSBhIHN1IHZleiBpbXBsaWNhIHF1ZSBjYWRhIHZhcmlhYmxlIGluZGVwZW5kaWVudGUgKGhhYml0YWNpb25lcywgcGFycXVlYWRlcm9zLCBiYcOxb3MsIMOhcmVhIGNvbnN0cnVpZGEgeSBlc3RyYXRvKSB0aWVuZSB1biBlZmVjdG8gc2lnbmlmaWNhdGl2byBlbiBlbCBwcmVjaW8gZGUgbGFzIHByb3BpZWRhZGVzLg0KDQoqQm9uZGFkIGRlIEFqdXN0ZSBkZWwgTW9kZWxvKg0KKipSwrIgKDAuODE4Myk6KiogSW5kaWNhIHF1ZSBlbCBtb2RlbG8gZXhwbGljYSBhcHJveGltYWRhbWVudGUgZWwgODEuODMlIGRlIGxhIHZhcmlhYmlsaWRhZCBlbiBlbCBwcmVjaW8gZGUgbGFzIHByb3BpZWRhZGVzLiBFc3RvIGVzIHVuIHZhbG9yIGJhc3RhbnRlIGFsdG8sIGxvIHF1ZSBzdWdpZXJlIHF1ZSBlbCBtb2RlbG8gc2UgYWp1c3RhIHJhem9uYWJsZW1lbnRlIGJpZW4gYSBsb3MgZGF0b3MuIEVzdG8gc2lnbmlmaWNhIHF1ZSBsYXMgdmFyaWFibGVzIGluY2x1aWRhcyBlbiBlbCBtb2RlbG8gZXhwbGljYW4gdW5hIGdyYW4gcHJvcG9yY2nDs24gZGUgbGEgdmFyaWFjacOzbiBlbiBsb3MgcHJlY2lvcy4gRW4gb3RyYXMgcGFsYWJyYXMsIGVsIG1vZGVsbyBzZSBhanVzdGEgYmFzdGFudGUgYmllbiBhIGxvcyBkYXRvcy4NCg0KKipGLWVzdGFkw61zdGljbzoqKiBFc3RlIHZhbG9yIGluZGljYSBzaSBlbCBtb2RlbG8gZW4gc3UgY29uanVudG8gZXMgc2lnbmlmaWNhdGl2by4gVW4gdmFsb3IgZGUgRiBtdXkgYWx0byB5IHVuIHZhbG9yIGRlIHAgbXV5IGJham8gKGNvbW8gZW4gZXN0ZSBjYXNvKSBzdWdpZXJlbiBxdWUgZWwgbW9kZWxvIGVzIHNpZ25pZmljYXRpdm8uDQoNCg0KKirCv1F1w6kgc2UgcHVlZGUgaGFjZXIgcGFyYSBtZWpvcmFyIGVsIG1vZGVsbz8qKg0KDQpFbCBtb2RlbG8gcmVzdWx0YW50ZSBlc3RhIGJhc3RhbnRlIGJ1ZW5vLCBzaW4gZW1iYXJnbywgc2UgcG9kcsOtYW4gZWplY3V0YXIgZXN0cmF0ZWdpYXMgYWRpY2lvbmFsZXMgbyBkaWZlcmVudGVzIHBhcmEgbWVqb3JhciBlbCByZXN1bHRhZG8uDQoNCioqSW5jbHVpciB2YXJpYWJsZXMgYWRpY2lvbmFsZXM6KiogQXVucXVlIGVuIGVsIGNvbmp1bnRvIGRlIGRhdG9zIG5vIGhheSBtw6FzIHZhcmlhYmxlcywgdW4gZXN0dWRpbyBtw6FzIGRldGFsbGFkbyBwb2Ryw61hIGNvbnNlZ3VpciBvdHJhcyB2YXJpYWJsZXMgZGUgbG9zIGlubXVlYmxlcyBxdWUgcHVlZGVuIGluZmx1aXIgZW4gZWwgdmFsb3IgZSBpbmNsdWlybGFzIGVuIGVsIGNvbmp1bnRvIGRlIGRhdG9zLiBBbGd1bmFzIG9wY2lvbmVzIHBvZHLDrWFuIHNlcjogQW50aWfDvGVkYWQgZGVsIGlubXVlYmxlLCBlbCBlc3RhZG8gZGUgbGEgZmFjaGFkYSwgZXN0YWRvIGRlIGxhIHBpbnR1cmEsIGVzdGFkbyBvIGFudGlnw7xlZGFkIGRlbCB0ZWNobywgcnVpZG8gZGUgbGEgem9uYSwgYWNjZXNvIHZlaGljdWxhciwgY29uZ2VzdGnDs24gZGUgbGEgem9uYSwgZW50cmUgb3RyYXMuDQoNCioqVHJhbnNmb3JtYXIgdmFyaWFibGVzOioqIEVuIGFsZ3Vub3MgY2Fzb3MsIHRyYW5zZm9ybWFyIGxhcyB2YXJpYWJsZXMgKHBvciBlamVtcGxvLCB0b21hbmRvIGxvZ2FyaXRtb3MpIHB1ZWRlIG1lam9yYXIgZWwgYWp1c3RlIGRlbCBtb2RlbG8uIEVzdG8gaGFicsOtYSBxdWUgcHJvYmFybG8geSB2ZXIgc2kgdGllbmUgZWZlY3RvcyBwb3NpdGl2b3MuDQoNCioqSW50ZXJhY2Npb25lcyBlbnRyZSB2YXJpYWJsZXM6KiogUG9kcsOtYSBoYWJlciBpbnRlcmFjY2lvbmVzIGVudHJlIGxhcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMgcXVlIG5vIGVzdMOhbiBzaWVuZG8gY2FwdHVyYWRhcyBwb3IgZWwgbW9kZWxvIGxpbmVhbC4gUG9yIGVqZW1wbG8sIGVsIGVmZWN0byBkZWwgbsO6bWVybyBkZSBoYWJpdGFjaW9uZXMgcG9kcsOtYSB2YXJpYXIgZGVwZW5kaWVuZG8gZGVsIGVzdHJhdG8gc29jaW9lY29uw7NtaWNvLiBTYWJlbW9zIHF1ZSwgY29tbyBsbyBkaWplIGFudGVyaW9ybWVudGUsIGEgbcOhcyBoYWJpdGFjaW9uZXMsIGJhw7FvcyB5IHBhcnF1ZWFkZXJvcywgZWwgw6FyZWEgY29uc3RydWlkYSBhdW1lbnRhcsOhLiBFc28gcHJvZHVjZSBxdWUgZWwgY29lZmljaWVudGUgZGUgbGEgdmFyaWFibGUgZGUgw6FyZWEsIHNlYSB0YW4gYmFqaXRvLg0KDQoqKk1vZGVsb3Mgbm8gbGluZWFsZXM6KiogVGFtYmnDqW4gZXMgcG9zaWJsZSByZXZpc2FyIHNpIGxhIHJlbGFjacOzbiBlbnRyZSBsYXMgdmFyaWFibGVzIG5vIGVzIGxpbmVhbCwgbG8gcXVlIGVuIGVzZSBjYXNvIGFicmlyw61hIGxhIHB1ZXJ0YSBwYXJhIGNvbnNpZGVyYXIgbW9kZWxvcyBubyBsaW5lYWxlcyBjb21vIGxhIHJlZ3Jlc2nDs24gcG9saW5vbWlhbCBvIGxvcyBtb2RlbG9zIGRlIMOhcmJvbGVzIGRlIGRlY2lzacOzbi4gDQoNCkZpbmFsbWVudGUsIHBvZHLDrWFzIHJlYWxpemFyIHVuYSAqVmFsaWRhY2nDs24gY3J1emFkYToqIHBhcmEgZXZhbHVhciBsYSBnZW5lcmFsaXphY2nDs24gZGVsIG1vZGVsbywgeSByZXZpc2FyIHNpIGhheSBzb2JyZWFqdXN0ZSAocGFyYSBldml0YXJsbyksIGVzdGltYXIgZGUgZm9ybWEgbcOhcyBwcmVjaXNhIGVsIGVycm9yIHkgc2VsZWNjaW9uYXIgZWwgbW9kZWxvIHF1ZSBtZWpvcmVzIHJlc3VsdGFkb3MgYXJyb2plLg0KDQojIDQuIFZhbGlkYWNpw7NuIGRlIHN1cHVlc3Rvcw0KDQoqKlJlYWxpY2UgbGEgdmFsaWRhY2nDs24gZGUgc3VwdWVzdG9zIGRlbCBtb2RlbG8gZSBpbnRlcnByZXRlIGxvcyByZXN1bHRhZG9zIChubyBlcyBuZWNlc2FyaW8gY29ycmVnaXIgZW4gY2FzbyBkZSBwcmVzZW50YXIgcHJvYmxlbWFzLCBzb2xvIHJlYWxpemFyIHN1Z2VyZW5jaWFzIGRlIHF1ZSBzZSBwb2Ryw61hIGhhY2VyKSouKioNCg0KDQojIyA0LjEuIExpbmVhbGlkYWQNCg0KRWwgc3VwdWVzdG8gZGUgbGluZWFsaWRhZCBlbiB1bmEgcmVncmVzacOzbiBsaW5lYWwgbcO6bHRpcGxlIGVzdGFibGVjZSBxdWUgbGEgcmVsYWNpw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMgeSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSBlcyBsaW5lYWwsIGVzIGRlY2lyLCBxdWUgZXhpc3RlIHVuYSByZWxhY2nDs24gbGluZWFsIGVudHJlIGxhIHZhcmlhYmxlIHF1ZSBxdWVyZW1vcyBwcmVkZWNpciAoZW4gZXN0ZSBjYXNvIGVsIHByZWNpbykgeSBsYXMgdmFyaWFibGVzICJwcmVkaWN0b3JhcyIgbyBpbmRlcGVuZGllbnRlcy4NCkFsIGdyYWZpY2FyIGxvcyByZXNpZHVvcyBjb250cmEgbG9zIHZhbG9yZXMgYWp1c3RhZG9zLCBlc3RvcyBkZWJlbiBlc3RhciBkaXN0cmlidWlkb3MgYWxlYXRvcmlhbWVudGUgYWxyZWRlZG9yIGRlIGNlcm8uIA0KDQpFbCBzdXB1ZXN0byBkZSBsaW5lYWxpZGFkIGVzdGFibGVjZSBxdWUgbGEgcmVsYWNpw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMgeSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSBlcyBsaW5lYWwuIFBhcmEgdmFsaWRhciBlc3RlIHN1cHVlc3RvLCBzZSBwdWVkZSBncmFmaWNhciBsb3MgcmVzaWR1b3MgY29udHJhIGxvcyB2YWxvcmVzIGFqdXN0YWRvcy4gRW4gdW4gYW7DoWxpc2lzIGFkZWN1YWRvLCBsb3MgcmVzaWR1b3Mgbm8gZGViZXLDrWFuIG1vc3RyYXIgcGF0cm9uZXMgc2lzdGVtw6F0aWNvcywgc2lubyBxdWUgZGViZW4gZXN0YXIgZGlzdHJpYnVpZG9zIGFsZWF0b3JpYW1lbnRlIGFscmVkZWRvciBkZSBjZXJvLiBTaSBzZSBvYnNlcnZhIGFsZ8O6biBwYXRyw7NuLCBjb21vIHVuYSB0ZW5kZW5jaWEgY3Vydmlsw61uZWEsIGVzdG8gc3VnaWVyZSBxdWUgbGEgcmVsYWNpw7NuIGVudHJlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlIHkgbGFzIGluZGVwZW5kaWVudGVzIG5vIGVzIGNvbXBsZXRhbWVudGUgbGluZWFsLiBFbiB0YWwgY2Fzbywgc2Vyw61hIHJlY29tZW5kYWJsZSBjb25zaWRlcmFyIHRyYW5zZm9ybWFjaW9uZXMgZW4gbGFzIHZhcmlhYmxlcyBvIG1vZGVsb3Mgbm8gbGluZWFsZXMgcGFyYSBhanVzdGFyIGVsIG1vZGVsby4NCg0KYGBge3IgbGluZWFyaXR5fQ0KcGxvdChyZWdfbGluX211bHQkZml0dGVkLnZhbHVlcywgcmVzaWQocmVnX2xpbl9tdWx0KSwNCiAgICAgbWFpbiA9ICJSZXNpZHVvcyB2cyBWYWxvcmVzIEFqdXN0YWRvcyIsDQogICAgIHhsYWIgPSAiVmFsb3JlcyBBanVzdGFkb3MiLA0KICAgICB5bGFiID0gIlJlc2lkdW9zIiwNCiAgICAgcGNoID0gMSwgY29sID0gImJsdWUiKQ0KYWJsaW5lKGggPSAwLCBjb2wgPSAiYmxhY2siLCBsd2QgPSAyKQ0KDQpgYGANCg0KRWwgZ3LDoWZpY28gbm8gbXVlc3RyYSBkZSBtYW5lcmEgY29udHVuZGVudGUgdW5hIHZpb2xhY2nDs24gZGlyZWN0YSBhbCBzdXB1ZXN0byBkZSBsaW5lYWxpZGFkLCBlbnRvbmNlcyBzZSBwb2Ryw61hIGRhciBwb3IgY3VtcGxpZG8gZXNlIHN1cHVlc3RvLiAgU2luIGVtYmFyZ28sIGxhIGZvcm1hIF9jw7NuaWNhXyBwb2Ryw61hIG1vc3RyYXIgdW4gcGF0csOzbiBxdWUgaW5kaWNhcsOtYSB1bmEgcG9zaWJsZSByZWxhY2nDs24gbm8gbGluZWFsIGVudHJlIGxhcyB2YXJpYWJsZXMuICBTaSBlc3RhIHNvc3BlY2hhIGVzIGZ1ZXJ0ZSwgc2UgcHVlZGVuIGhhY2VyIGdyw6FmaWNvcyBhZGljaW9uYWxlcyBkZSBsb3MgcmVzaWR1b3MgY29udHJhIGxhcyB2YXJpYWJsZXMgcHJlZGljdG9yYXMgaW5kaXZpZHVhbGVzIHBhcmEgaWRlbnRpZmljYXIgcGF0cm9uZXMgcXVlIG5vIGZ1ZXJhbiBsaW5lYWxlcy4gIFRhbWJpw6luIHNlIHB1ZWRlbiBleHBsb3JhciB0cmFuc2Zvcm1hY2lvbmVzIGEgbGFzIHZhcmlhYmxlcyBwcmVkaWN0b3JhcyBvIGNhbWJpYXIgYSBtb2RlbG9zIG5vIGxpbmVhbGVzLCBjb21vIGxvcyBwb2xpbsOzbWljb3MgbyBlbCBkZSByZWdyZXNpw7NuIHNwbGluZSwgbG9zIGN1YWxlcyBjYXB0dXJhcsOtYW4gbWVqb3IgbGEgcmVsYWNpw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMgZW4gZWwgY2FzbyBkZSBxdWUgbm8gZnVlcmEgbGluZWFsLiAgDQoNCiMgNC4yLiBIb21vc2NlZGFzdGljaWRhZA0KDQpFbCBzdXB1ZXN0byBkZSBob21vc2NlZGFzdGljaWRhZCBlc3BlcmEgcXVlIGxhIHZhcmlhbnphIGRlIGxvcyByZXNpZHVvcyBzZWEgY29uc3RhbnRlIGEgbG8gbGFyZ28gZGUgbG9zIHZhbG9yZXMgcXVlIHNlIHByZWRpamVyb24uIFBhcmEgc3UgdmFsaWRhY2nDs24gZ3JhZmljYXJlbW9zIGxvcyByZXNpZHVvcyBlc3RhbmRhcml6YWRvcyBjb250cmEgbG9zIHZhbG9yZXMgYWp1c3RhZG9zLCB0YWwgY29tbyBsbyBoaWNpbW9zIGNvbiBsYSBsaW5lYWxpZGFkLiBTaSBsb3MgcmVzaWR1b3MgcHJlc2VudGFuIHVuIHBhdHLDs24gZGUgZW1idWRvLCBhdW1lbnRhbmRvIG8gZGlzbWludXllbmRvIGxhIHZhcmlhbnphIGNvbiBsb3MgdmFsb3JlcyBhanVzdGFkb3MsIHNlIGVzdGFyw61hIGFudGUgdW4gcHJvYmxlbWEgZGUgaGV0ZXJvc2NlZGFzdGljaWRhZC4gU2UgYXBsaWNhcsOhIGxhIHBydWViYSBCcmV1c2NoLVBhZ2FuIHBhcmEgaGFjZXIgZXN0YSB2ZXJpZmljYWNpw7NuLg0KDQojRW4gdGFsIGNhc28sIHNlIHB1ZWRlbiBhcGxpY2FyIHTDqWNuaWNhcyBjb21vIGxhIHRyYW5zZm9ybWFjacOzbiBsb2dhcsOtdG1pY2EgZGUgbGEgdmFyaWFibGUgZGVwZW5kaWVudGUgbyB1dGlsaXphciBlc3RpbWFkb3JlcyByb2J1c3RvcyBwYXJhIG1hbmVqYXIgbGEgaGV0ZXJvc2NlZGFzdGljaWRhZC4NCg0KDQpgYGB7ciBob21vc2NlZGFzdGljaWRhZH0NCmJwdGVzdChyZWdfbGluX211bHQpDQpgYGANCg0KRWwgZXN0YWTDrXN0aWNvIGRlIHBydWViYSBCUCBmdWU6IDEwNzEuNyBjb24gNSBHcmFkb3MgZGUgbGliZXJ0YWQgKGRmKSB5IHVuIHZhbG9yIF9wXyA8IDAuMDAwMDAwMDAwMDAwMDAwMjINCg0KQ29tbyBlbCB2YWxvciBfcF8gZXMgZXh0cmVtYWRhbWVudGUgYmFqbyBpbmRpY2EgcXVlIGhheSB1bmEgZXZpZGVuY2lhIG11eSBmdWVydGUgZW4gY29udHJhIGRlIGxhIGhpcMOzdGVzaXMgbnVsYSwgZXMgZGVjaXIsIHF1ZSBsYSB2YXJpYW56YSBkZSBsb3MgZXJyb3JlcyBlcyBjb25zdGFudGUgKGhvbW9jZWRhc3RpY2lkYWQpLiBFbiBjb25jbHVzacOzbiwgZGFkbyBxdWUgZWwgdmFsb3IgcCBlcyBzaWduaWZpY2F0aXZhbWVudGUgbWVub3IgcXVlIGVsIG5pdmVsIGRlIHNpZ25pZmljYW5jaWEgY29udmVuY2lvbmFsICh0w61waWNhbWVudGUsIDAuMDUpLCByZWNoYXphbW9zIGxhIGhpcMOzdGVzaXMgbnVsYS4gRXN0byBzaWduaWZpY2EgcXVlIGhheSBldmlkZW5jaWEgZGUgaGV0ZXJvY2VkYXN0aWNpZGFkIGVuIHR1cyBkYXRvcy4NCg0KTG8gYW50ZXJpb3IgaW1wbGljYSBJbnZhbGlkZXogZGUgbGFzIHBydWViYXMgZGUgaGlww7N0ZXNpcy4gVGFtYmnDqW4gaW5kaWNhIHF1ZSBsb3MgZXN0aW1hZG9yZXMgc29uIGluZWZpY2llbnRlcywgZXMgZGVjaXIsIHF1ZSBwdWVkZW4gdGVuZXIgdW5hIHZhcmlhbnphIG1heW9yIGRlIGxvIGVzcGVyYWRvLiBPdHJhIGltcGxpY2FjacOzbiBlcyBxdWUgbG9zIGludGVydmFsb3MgZGUgY29uZmlhbnphIHkgdmFsb3JlcyBwIHNvbiBpbmNvcnJlY3Rvcy4gTG9zIGludGVydmFsb3MgZGUgY29uZmlhbnphIHkgbG9zIHZhbG9yZXMgcCBjYWxjdWxhZG9zIGJham8gZWwgc3VwdWVzdG8gZGUgaG9tb2NlZGFzdGljaWRhZCBwdWVkZW4gc2VyIGRlbWFzaWFkbyBlc3RyZWNob3MgbyBkZW1hc2lhZG8gYW1wbGlvcywgcmVzcGVjdGl2YW1lbnRlLg0KDQpQYXJhIGNvcnJlZ2lybG8sIG51ZXZhbWVudGUgc2UgZGViZSBlc3RhYmxlY2VyIGxhIGNhdXNhIGRlbCBwcm9ibGVtYSwgaWRlbnRpZmljYW5kbyBsYXMgdmFyaWFibGVzIHF1ZSBwb2Ryw61hbiBlc3RhciBnZW5lcmFuZG8gbGEgaGV0ZXJvY2VkYXN0aWNpZGFkIChQb3IgZWplbXBsbywgY3VhbmRvIGxhIHZhcmlhbnphIGRlIGxvcyBlcnJvcmVzIGVzdGEgcmVsYWNpb25hZGEgY29uIGVsIHRhbWHDsW8gZGUgYWxndW5hIHZhcmlhYmxlIGluZGVwZW5kaWVudGUpLiBUYW1iacOpbiBzZSBwdWVkZW4gdHJhbnNmb3JtYXIgbGFzIHZhcmlhYmxlcyB5IHV0aWxpemFyIG1vZGVsb3MgZGUgcmVncmVzacOzbiBjb21vIGxvcyBtb2RlbG9zIGRlIG3DrW5pbW9zIGN1YWRyYWRvcyBwb25kZXJhZG9zIChXTFMpLiBQb3Igw7psdGltbywgYWp1c3RhciBsb3MgZXJyb3JlcyBlc3TDoW5kYXIgdXRpbGl6YW5kbyB0w6ljbmljYXMgY29tbyBsYSBjb3JyZWNjacOzbiBkZSBXaGl0ZSBwdWVkZSBtZWpvcmFyIGxhIGluZmVyZW5jaWEgZXN0YWTDrXN0aWNhLg0KDQojIDQuMy4gSW5kZXBlbmRlbmNpYSBkZSBFcnJvcmVzDQoNClBhcmEgcmVhbGl6YXIgZXN0YSB2YWxpZGFjacOzbiBhcGxpY2Fyw6kgbGEgcHJ1ZWJhICBEdXJiaW4tV2F0c29uDQoNCmBgYHtyIGR1cmJpbl93YXRzb259DQpsbXRlc3Q6OmR3dGVzdChyZWdfbGluX211bHQpDQpgYGANCg0KRWwgZXN0YWTDrXN0aWNvIGRlIHBydWViYSBlcyBEVyA9IDEuNjU2OSBjb24gdW4gdmFsb3IgX3BfIG11eSBwZXF1ZcOxby4gRXN0ZSB2YWxvciBfcF8gaW5kaWNhIHF1ZSBoYXkgZXZpZGVuY2lhIHNpZ25pZmljYXRpdmEgcGFyYSByZWNoYXphciBsYSBoaXDDs3Rlc2lzIG51bGEgZGUgcXVlIG5vIGhheSBhdXRvY29ycmVsYWNpw7NuIGVuIGxvcyByZXNpZHVvcywgZXMgZGVjaXIsIHF1ZSBzaSBoYXkgYXV0b2NvcnJlbGFjacOzbiBkZSBlcnJvcmVzLiBFbCBEVyBlc3RhIHBvciBkZWJham8gZGUgMiwgbG8gcXVlIGluZGljYSB1bmEgcG9zaWJsZSBhdXRvY29ycmVsYWNpw7NuIHBvc2l0aXZhLg0KTG8gYW50ZXJpb3IgcG9kcsOtYSBpbnZhbGlkYXIgbGFzIGluZmVyZW5jaWFzIGJhc2FkYXMgZW4gZWwgbW9kZWxvIGRlIHJlZ3Jlc2nDs24sIGUgaW5kaWNhciBpbmVmaWNpZW5jaWEgZW4gbG9zIGVzdGltYWRvcmVzLCBsbyBxdWUgc2lnbmlmaWNhIHF1ZSBwdWVkZW4gdGVuZXIgdW5hIHZhcmlhbnphIG1heW9yIGRlIGxvIGVzcGVyYWRvLiBUYW1iacOpbiBwdWVkZSBpbmRpY2FyIHF1ZSBsYXMgaGlww7N0ZXNpcyBzb24gaW5jb3JyZWN0YXMuDQoNClBhcmEgc29sdWNpb25hciBsbyBhbnRlcmlvciwgbGEgc3VnZXJlbmNpYSBlcyBpZGVudGlmaWNhciBsYSBjYXVzYSBkZSBsYSBhdXRvY29ycmVsYWNpw7NuIGV4YW1pbmFuZG8gbGFzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyB5IGJ1c2NhbmRvIHBvc2libGVzIHBhdHJvbmVzIHF1ZSBwdWVkYW4gZXN0YXIgY2F1c2FuZG8gbGEgYXV0b2NvcnJlbGFjacOzbi4gIFVuYSB2ZXogc2UgaWRlbnRpZmlxdWUgbGEgY2F1c2EsIHBhcmEgY29ycmVnaXJsYSBzZSBwdWVkZW4gcmVhbGl6YXIgdHJhbnNmb3JtYWNpb25lcyBkZSBsYXMgdmFyaWFibGVzLCBvIHVzYXIgbW9kZWxvcyBkZSByZWdyZXNpw7NuIGF1dG9ycmVncmVzaXZvcyBvIG1vZGVsb3MgZGUgZXJyb3JlcyBhdXRvcnJlZ3Jlc2l2b3MuICBEZXNwdcOpcyBkZSBlc3RvLCBzZSBkZWJlIGV2YWx1YXIgZWwgbW9kZWxvIG51ZXZhbWVudGUgcGFyYSB2ZXIgc2kgc2Ugc3VwZXLDsyBsYSBhdXRvY29ycmVsYWNpw7NuLg0KDQojIDQuNC4gTm9ybWFsaWRhZCBkZSBsb3MgZXJyb3Jlcw0KDQpFbCBzdXB1ZXN0byBkZSBub3JtYWxpZGFkIGVzdGFibGVjZSBxdWUgbG9zIHJlc2lkdW9zIGRlbCBtb2RlbG8gZGViZW4gc2VndWlyIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbC4gUGFyYSBjb21wcm9iYXJsbyB1c2Fyw6kgZWwgZ3LDoWZpY28gUS1RIChjdWFudGlsZXMtY3VhbnRpbGVzKSB5IHJlYWxpemFyw6kgbGEgcHJ1ZWJhIGVzdGFkw61zdGljYSBjb25vY2lkYSBjb21vIGVsIHRlc3QgZGUgU2hhcGlyby1XaWxrLiANClNpIGxvcyBwdW50b3MgZW4gZWwgZ3LDoWZpY28gUS1RIHNpZ3VlbiB1bmEgbMOtbmVhIHJlY3RhLCBzZSBwdWVkZSBkZWNpciBxdWUgbG9zIHJlc2lkdW9zIHNlIGRpc3RyaWJ1eWVuIG5vcm1hbG1lbnRlLiBTaSBleGlzdGVuIGRlc3ZpYWNpb25lcyBzaWduaWZpY2F0aXZhcyBkZSBlc3RhIGzDrW5lYSBwdWVkZSBpbmRpY2FyIHF1ZSBsb3MgcmVzaWR1b3Mgbm8gc2lndWVuIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbC4gU2kgZXN0byBsbGVnYXNlIGEgb2N1cnJpciAocmVzaWR1b3Mgbm8gbm9ybWFsZXMpLCBzZSBkZWJlcsOtYW4gY29uc2lkZXJhciB0cmFuc2Zvcm1hY2lvbmVzIGVuIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlIG8gZW1wbGVhciB0w6ljbmljYXMgbcOhcyByb2J1c3RhcyBxdWUgbm8gcmVxdWllcmFuIGFzdW1pciBlc3RhIG5vcm1hbGlkYWQgZGUgbG9zIHJlc2lkdW9zLg0KDQpgYGB7ciBub3JtYWxpdHl9DQpxcVBsb3QocmVnX2xpbl9tdWx0LCBtYWluID0gIk5vcm1hbCBRLVEgUGxvdCIpDQoNCnNoYXBpcm8udGVzdChyZXNpZHVhbHMocmVnX2xpbl9tdWx0KSkNCmBgYA0KDQpDb24gbGEgcHJ1ZWJhIFNoYXBpcm8tV2lsayBkaW8gY29tbyByZXN1bHRhZG8gMC44NjE0NSwgbG8gcXVlIGVzIG1lbm9yIHF1ZSAxLCBlIGluZGljYSB1bmEgZGVzdmlhY2nDs24gZGUgbGEgbm9ybWFsaWRhZC4gVW4gdmFsb3IgbWFzIGNlcmNhbm8gYSB1bm8gc3VnZXJpcsOtYSB1bmEgZGlzdHJpYnVjacOzbiBtw6FzIG5vcm1hbC4NCkVsIHZhbG9yIHAgdGFuIHBlcXVlw7FvIChwb3IgZGViYWpvIGRlIDAuMDUpIHJlY2hhemEgbGEgaGlww7N0ZXNpcyBudWxhIGRlIHF1ZSBsb3MgZGF0b3MgcHJvdmllbmVuIGRlIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbCwgZXMgZGVjaXIsIHF1ZSBoYXkgZXZpZGVuY2lhIHNpZ25pZmljYXRpdmEgZGUgcXVlIGxvcyBkYXRvcyBubyBzZSBkaXN0cmlidXllbiBub3JtYWxtZW50ZS4NCg0KQWwgaWd1YWwgcXVlIGNvbiBsYSBOb3JtYWxpZGFkIGRlIGxvcyBFcnJvcmVzLCBsbyBhbnRlcmlvciBpbXBsaWNhIGludmFsaWRleiBlbiBsYXMgaW5mZXJlbmNpYXMsIHVub3MgZXN0aW1hZG9yZXMgaW5lZmljaWVudGVzIHkgdW5hcyBwb3NpYmxlcyBoaXDDs3Rlc2lzIGluY29ycmVjdGFzLiBQYXJhIGNvcnJlZ2lyIGVzdG8gc2UgZGViZW4gcmVhbGl6YXIgdHJhbnNmb3JtYWNpb25lcyBhIGxhcyB2YXJpYWJsZXMgKExvZ2FyaXRtb3MsIHJhw61jZXMgY3VhZHJhZGFzLCBldGMuKSBidXNjYW5kbyBtZWpvcmFyIGxhIG5vcm1hbGlkYWQgZGUgbG9zIHJlc2lkdW9zLiBUYW1iacOpbiBzZSBwdWVkZW4gcmV2aXNhciBsb3MgdmFsb3JlcyBhdMOtcGljb3MgcXVlIHB1ZWRhbiBlc3RhciBhZmVjdGFuZG8gYWwgbW9kZWxvLg0KDQoNCiMgNC41LiBNdWx0aWNvbGluZWFsaWRhZA0KDQpFbCBzdXB1ZXN0byBkZSBtdWx0aWNvbGluZWFsaWRhZCBlc3BlcmEgcXVlIG5vIGhheWEgdW5hIGNvcnJlbGFjacOzbiBzaWduaWZpY2F0aXZhIGVudHJlIGxhcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMuIFBhcmEgcmVhbGl6YXIgbGEgdmVyaWZpY2FjacOzbiwgdXRpbGl6YXLDqSBlbCBGYWN0b3IgZGUgSW5mbGFjacOzbiBkZSBsYSBWYXJpYW56YSAoVklGKS4gU2kgbG9zIHZhbG9yZXMgc29uIG1heW9yZXMgYSAxMCBlc3RvIGluZGljYXJpYSBwcm9ibGVtYXMgZGUgbXVsdGljb2xpbmVhbGlkYWQsIGluZGljYW5kbyBxdWUgbGFzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyBlc3TDoW4gYWx0YW1lbnRlIGNvcnJlbGFjaW9uYWRhcyBlbnRyZSBzw60uIEVuIGNhc28gZGUgcXVlIGVzdG8gc2VhIGRldGVjdGFkbywgc2UgcmVjb21pZW5kYSBjb25zaWRlcmFyIGxhIGVsaW1pbmFjacOzbiBkZSB2YXJpYWJsZXMgcmVkdW5kYW50ZXMgbyBhcGxpY2FyIHTDqWNuaWNhcyBkZSByZWR1Y2Npw7NuIGRlIGRpbWVuc2lvbmVzIGNvbW8gZWwgUENBIChBbsOhbGlzaXMgZGUgQ29tcG9uZW50ZXMgUHJpbmNpcGFsZXMpLg0KDQoNCmBgYHtyfQ0KdmlmKHJlZ19saW5fbXVsdCkNCmBgYA0KDQpUb2RvcyBsb3MgcmVzdWx0YWRvcyBzb24gcGVxdWXDsW9zLCBzaWVuZG8gZWwgbWFzIGFsdG8gMywgbG8gcXVlIGluZGljYSBxdWUgbm8gaGF5IHByb2JsZW1hcyBkZSBtdWx0aWNvbGluZWFsaWRhZC4NCg0KDQojIDUuIFBhcnRpY2nDs24gNzAlLTMwJSBkZWwgbW9kZWxvDQoNCioqUmVhbGljZSB1bmEgcGFydGljacOzbiBlbiBsb3MgZGF0b3MgZGUgZm9ybWEgYWxlYXRvcmlhIGRvbmRlIDcwJSBzZWEgdW4gc2V0IHBhcmEgZW50cmVuYXIgZWwgbW9kZWxvIHkgMzAlIHBhcmEgcHJ1ZWJhLiBFc3RpbWUgZWwgbW9kZWxvIGNvbiBsYSBtdWVzdHJhIGRlbCA3MCUuIE11ZXN0cmUgbG9zIHJlc3VsdGFkb3MuKioNCg0KDQpgYGB7ciBtb2RlbF9wYXJ0aXRpb24gfQ0Kc2V0LnNlZWQoMTIpIA0KdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihhcHRvcyRwcmVjaW9tLCBwID0gMC43LCBsaXN0ID0gRkFMU0UpDQp0cmFpbl9kYXRhIDwtIGFwdG9zW3RyYWluX2luZGV4LCBdDQp0ZXN0X2RhdGEgPC0gYXB0b3NbLXRyYWluX2luZGV4LCBdDQpuZXdfbW9kZWwgPC0gbG0ocHJlY2lvbSB+IGFyZWFjb25zdCArIGVzdHJhdG8gKyBoYWJpdGFjaW9uZXMgKyBwYXJxdWVhZGVyb3MgKyBiYW5pb3MsIGRhdGEgPSB0cmFpbl9kYXRhKQ0KDQpzdW1tYXJ5KG5ld19tb2RlbCkNCmBgYA0KTG9zIHJlc3VsdGFkb3Mgc29uIHBhcmVjaWRvcyBhbCBtb2RlbG8gaW5pY2lhbC4NCg0KRWwgKipJbnRlcmNlcHRvICgtMjM0LjYxKToqKiBSZXByZXNlbnRhIGVsIHZhbG9yIGVzcGVyYWRvIGRlIHByZWNpb20gY3VhbmRvIHRvZGFzIGxhcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMgc29uIGNlcm8uIFNpbiBlbWJhcmdvLCBlbiBlc3RlIGNhc28sIG5vIHRpZW5lIHVuYSBpbnRlcnByZXRhY2nDs24gZGlyZWN0YSB5YSBxdWUgbm8gZXMgcmVhbGlzdGEgcXVlIHVuYSBwcm9waWVkYWQgdGVuZ2Egw6FyZWEgY2VybywgZXN0cmF0byBjZXJvLCBldGMuDQoqKmFyZWFjb25zdCAoMi40NDIpOioqIG11ZXN0cmEgcXVlIHBvciBjYWRhIHVuaWRhZCBkZSBhdW1lbnRvIGVuIGVsIMOhcmVhIGNvbnN0cnVpZGEsIHNlIGVzcGVyYSBxdWUgZWwgcHJlY2lvIGRlIGxhIHByb3BpZWRhZCBhdW1lbnRlIGVuIDIuNDQyIHVuaWRhZGVzLCBtYW50ZW5pZW5kbyBjb25zdGFudGVzIGxhcyBkZW3DoXMgdmFyaWFibGVzLg0KKiplc3RyYXRvICg1Mi4xOSk6KiogbXVlc3RyYSBxdWUgcG9yIGNhZGEgdW5pZGFkIGRlIGF1bWVudG8gZW4gZWwgZXN0cmF0byBzb2Npb2Vjb27Ds21pY28sIHNlIGVzcGVyYSBxdWUgZWwgcHJlY2lvIGRlIGxhIHByb3BpZWRhZCBhdW1lbnRlIGVuIDUyLjE5IHVuaWRhZGVzLCBtYW50ZW5pZW5kbyBjb25zdGFudGVzIGxhcyBkZW3DoXMgdmFyaWFibGVzLg0KKipoYWJpdGFjaW9uZXMgKC01MS42Nik6KiogUG9yIGNhZGEgaGFiaXRhY2nDs24gYWRpY2lvbmFsLCBzZSBlc3BlcmEgcXVlIGVsIHByZWNpbyBkZSBsYSBwcm9waWVkYWQgZGlzbWludXlhIGVuIDUxLjY2IHVuaWRhZGVzLCBtYW50ZW5pZW5kbyBjb25zdGFudGVzIGxhcyBkZW3DoXMgdmFyaWFibGVzLiBFc3RvIHBvZHLDrWEgaW5kaWNhciBxdWUgbGFzIGhhYml0YWNpb25lcyBhZGljaW9uYWxlcyBwdWVkZW4gdGVuZXIgdW4gZWZlY3RvIG5lZ2F0aXZvIHNvYnJlIGVsIHByZWNpbywgcG9zaWJsZW1lbnRlIGRlYmlkbyBhIGZhY3RvcmVzIGNvbW8gbGEgZGlzdHJpYnVjacOzbiBvIGVsIHRhbWHDsW8gZGUgbGFzIGhhYml0YWNpb25lcy4gRXN0byBlcyBjb250cmFyaW8gYSBsYSBsw7NnaWNhIGNvbcO6biBlbiBkb25kZSBzZSBlc3BlcmEgcXVlIGVudHJlIG3DoXMgaGFiaXRhY2lvbmVzIGhheWEsIG1heW9yIHZhbG9yIHRlbmdhIHVuIGFwYXJ0YW1lbnRvLg0KKipwYXJxdWVhZGVyb3MgKDc1LjI1KToqKiBpbmRpY2EgcXVlIHBvciBjYWRhIHBhcnF1ZWFkZXJvIGFkaWNpb25hbCwgc2UgZXNwZXJhIHF1ZSBlbCBwcmVjaW8gZGUgbGEgcHJvcGllZGFkIGF1bWVudGUgZW4gNzUuMjUgdW5pZGFkZXMsIG1hbnRlbmllbmRvIGNvbnN0YW50ZXMgbGFzIGRlbcOhcyB2YXJpYWJsZXMuDQoqKmJhbmlvcyAoNDYuNTApOioqIFBvciBjYWRhIGJhw7FvIGFkaWNpb25hbCwgc2UgZXNwZXJhIHF1ZSBlbCBwcmVjaW8gZGUgbGEgcHJvcGllZGFkIGF1bWVudGUgZW4gNDYuNTAgdW5pZGFkZXMsIG1hbnRlbmllbmRvIGNvbnN0YW50ZXMgbGFzIGRlbcOhcyB2YXJpYWJsZXMuDQoNCioqRXZhbHVhY2nDs24gZGVsIE1vZGVsbyoqDQoNCioqUi1jdWFkcmFkbyAoMC44MDI5KToqKiBFbCBtb2RlbG8gZXhwbGljYSBhcHJveGltYWRhbWVudGUgZWwgODAuMjklIGRlIGxhIHZhcmlhYmlsaWRhZCBlbiBlbCBwcmVjaW8gZGUgbGFzIHByb3BpZWRhZGVzLiBFc3RvIGluZGljYSB1biBidWVuIGFqdXN0ZSBkZWwgbW9kZWxvIGEgbG9zIGRhdG9zLg0KKipGLWVzdGFkw61zdGljbyAoMjM5Mik6KiogRWwgRi1lc3RhZMOtc3RpY28gZXMgYWx0YW1lbnRlIHNpZ25pZmljYXRpdm8gKHAtdmFsb3IgPCAwLjAwMDAwMDAwMDAwMDAwMDIyKSwgbG8gcXVlIHN1Z2llcmUgcXVlIGFsIG1lbm9zIHVuYSBkZSBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIGVzIHNpZ25pZmljYXRpdmEgZW4gZWwgbW9kZWxvLg0KKipTaWduaWZpY2FuY2lhIGluZGl2aWR1YWwgZGUgbG9zIGNvZWZpY2llbnRlczoqKiBUb2RvcyBsb3MgY29lZmljaWVudGVzIHNvbiBhbHRhbWVudGUgc2lnbmlmaWNhdGl2b3MgKHAtdmFsb3IgPCAwLjAwMDAwMDAwMDAwMDAwMDIyKSwgbG8gcXVlIGluZGljYSBxdWUgdG9kYXMgbGFzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyBjb250cmlidXllbiBzaWduaWZpY2F0aXZhbWVudGUgYWwgbW9kZWxvDQoNCiMgNi4gUHJlZGljY2lvbmVzDQoNCioqUmVhbGljZSBwcmVkaWNjaW9uZXMgY29uIGVsIG1vZGVsbyBhbnRlcmlvciB1c2FuZG8gbG9zIGRhdG9zIGRlIHBydWViYSAoMzAlKSoqDQoNCg0KYGBge3IgcHJlZGljdGlvbnN9DQpwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KG5ld19tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSkNCnJlc3VsdGFkb3MgPC0gZGF0YS5mcmFtZShSZWFsID0gdGVzdF9kYXRhJHByZWNpb20sIFByZWRpY2Npb24gPSBwcmVkaWN0aW9ucykNCmhlYWQocmVzdWx0YWRvcykNCmBgYA0KDQoNCiMgNy4gRXJyb3Jlcw0KDQoqKkNhbGN1bGUgZWwgZXJyb3IgY3VhZHLDoXRpY28gbWVkaW8sIGVsIGVycm9yIGFic29sdXRvIG1lZGlvIHkgZWwgUjIsIGludGVycHJldGUuKioNCg0KIyMgNy4xLiBFcnJvciBDdcOhZHLDoXRpY28gTWVkaW8NCmBgYHtyfQ0KIyBDYWxjdWxhciBsb3MgZXJyb3JlcyBkZSBwcmVkaWNjacOzbg0KZXJyb3JlcyA8LSB0ZXN0X2RhdGEkcHJlY2lvbSAtIHByZWRpY3Rpb25zDQoNCiMgQ2FsY3VsYXIgZWwgRXJyb3IgQ3VhZHLDoXRpY28gTWVkaW8gKE1TRSkNCm1zZSA8LSBtZWFuKGVycm9yZXNeMikNCg0KIyBDYWxjdWxhciBlbCBFcnJvciBBYnNvbHV0byBNZWRpbyAoTUFFKQ0KbWFlIDwtIG1lYW4oYWJzKGVycm9yZXMpKQ0KDQojIENhbGN1bGFyIGVsIFItY3VhZHJhZG8gKFLCsikNCnIyIDwtIGNhcmV0Ojpwb3N0UmVzYW1wbGUocHJlZCA9IHByZWRpY3Rpb25zLCBvYnMgPSB0ZXN0X2RhdGEkcHJlY2lvbSlbMl0NCg0KIyBNb3N0cmFyIGxvcyByZXN1bHRhZG9zDQpjYXQoIkVsIEVycm9yIEN1YWRyw6F0aWNvIE1lZGlvIChNU0UpIGVzOiIsIG1zZSwgIlxuIikNCmBgYA0KDQpFbCBFcnJvciBDdWFkcsOhdGljbyBNZWRpbyAoTVNFKSBtaWRlIGVsIHByb21lZGlvIGRlIGxvcyBjdWFkcmFkb3MgZGUgbG9zIGVycm9yZXMgZW50cmUgbG9zIHZhbG9yZXMgcHJlZGljaG9zIHBvciBlbCBtb2RlbG8geSBsb3MgdmFsb3JlcyByZWFsZXMuIEN1YW50byBtw6FzIGJham8gc2VhIGVsIE1TRSwgbWVqb3Igc2UgYWp1c3RhIGVsIG1vZGVsbyBhIGxvcyBkYXRvcy4gRW4gZXN0ZSBjYXNvIGRpbyB1biBNU0UgZGUgMTc2ODYuMDQsIGxvIHF1ZSBzaWduaWZpY2EgcXVlIGVuIHByb21lZGlvLCBlbCBtb2RlbG8gc2UgZXF1aXZvY2EgZW4gYXByb3hpbWFkYW1lbnRlIDE3Njg2IHVuaWRhZGVzIGFsIHByZWRlY2lyIGVsIHZhbG9yIGRlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlLiBMYSBtYWduaXR1ZCBkZSBlc3RlIHZhbG9yIGRlcGVuZGVyw6EgZGUgbGEgZXNjYWxhIGRlIHR1IHZhcmlhYmxlIGRlcGVuZGllbnRlLiBVbiBNU0UgYmFqbyBpbmRpY2EgdW4gbWVqb3IgYWp1c3RlIGRlbCBtb2RlbG8uDQoNCiMjIDcuMi4gRXJyb3IgQWJzb2x1dG8gTWVkaW8NCmBgYHtyfQ0KY2F0KCJFbCBFcnJvciBBYnNvbHV0byBNZWRpbyAoTUFFKSBlczoiLCBtYWUsICJcbiIpDQpgYGANCg0KRWwgRXJyb3IgQWJzb2x1dG8gTWVkaW8gKE1BRSkgbWlkZSBlbCBwcm9tZWRpbyBkZSBsb3MgdmFsb3JlcyBhYnNvbHV0b3MgZGUgbG9zIGVycm9yZXMuIEVzIHVuYSBtZWRpZGEgbcOhcyBpbnR1aXRpdmEgZGVsIGVycm9yIHByb21lZGlvLCB5YSBxdWUgbm8gcGVuYWxpemEgbG9zIGVycm9yZXMgZ3JhbmRlcyB0YW50byBjb21vIGVsIE1TRS4gRW4gZXN0ZSBjYXNvIGRpbyB1biBNQUUgZGUgODQuNDEzMywgbG8gcXVlIGluZGljYSBxdWUsIGVuIHByb21lZGlvLCBlbCBtb2RlbG8gc2UgZXF1aXZvY2EgZW4gODQuNDEzMyB1bmlkYWRlcyBhbCBoYWNlciB1bmEgcHJlZGljY2nDs24uIEFsIGlndWFsIHF1ZSBlbCBNU0UsIHVuIHZhbG9yIGJham8gaW5kaWNhIHVuIG1lam9yIGFqdXN0ZS4gRWwgTUFFIGVzIG3DoXMgZsOhY2lsIGRlIGludGVycHJldGFyIHF1ZSBlbCBNU0UgcG9ycXVlIGVzdMOhIGVuIGxhcyBtaXNtYXMgdW5pZGFkZXMgcXVlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlLg0KDQojIyA3LjMuIFLCsg0KYGBge3J9DQpjYXQoIkVsIFItY3VhZHJhZG8gKFLCsikgZXM6IiwgcjIsICJcbiIpDQpgYGANCg0KRWwgQ29lZmljaWVudGUgZGUgZGV0ZXJtaW5hY2nDs24gKFLCsikgaW5kaWNhIGxhIHByb3BvcmNpw7NuIGRlIGxhIHZhcmlhYmlsaWRhZCBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSBxdWUgZXMgZXhwbGljYWRhIHBvciBlbCBtb2RlbG8uIFVuIHZhbG9yIGRlIFLCsiBjZXJjYW5vIGEgMSBpbmRpY2EgdW4gbWVqb3IgYWp1c3RlIGRlbCBtb2RlbG8uIFBBcmEgZXN0ZSBjYXNvLCBlbCBSwrIgZnVlIGlndWFsIGEgMC44MDI4OTQ3LCBlc3RvIHNpZ25pZmljYSBxdWUgZWwgbW9kZWxvIGV4cGxpY2EgYXByb3hpbWFkYW1lbnRlIGVsIDgwLjI5JSBkZSBsYSB2YXJpYWJpbGlkYWQgZW4gbGEgdmFyaWFibGUgZGVwZW5kaWVudGUuIEVzIGRlY2lyLCBlbCA4MC4yOSUgZGUgbGFzIHZhcmlhY2lvbmVzIGVuIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlIHB1ZWRlbiBzZXIgYXRyaWJ1aWRhcyBhIGxhcyB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMgaW5jbHVpZGFzIGVuIGVsIG1vZGVsby4gVW4gUsKyIGRlIDAuODAyOSBlcyBnZW5lcmFsbWVudGUgY29uc2lkZXJhZG8gY29tbyB1biBidWVuIGFqdXN0ZSwgc2luIGVtYmFyZ28sIHlvIGNyZW8gcXVlIGRlYmVyw61hIGJ1c2NhcnNlIHVuYSBtZWpvcmEuDQoNCg0KIyBDb25jbHVzacOzbiBGaW5hbA0KDQpMb3MgaW5kaWNhZG9yZXMgbXVlc3RyYW4gcXVlIGVsIG1vZGVsbyBlcyByYXpvbmFibGVtZW50ZSBidWVubywgcGVybyBjb25zaWRlcm8gcXVlIGF1biBzZSBwb2Ryw61hIG1lam9yYXIgbcOhcy4NCg==