#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.
## 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
##
##
## 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
##
## 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
## # 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>
## 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.
##
## 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).
## 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==