Programa: Maestría en Ciencia de Datos — Pontificia
Universidad Javeriana Cali
Asignatura: Modelos Estadísticos para la Toma de Decisiones
Actividad: Informe de regresión lineal múltiple — Caso
C&A
Estudiante: Iván Darío García Ramos
Resumen ejecutivo
C&A debe recomendar dos inmuebles en Cali bajo restricciones
explícitas de tipo, zona, características físicas y presupuesto. Para
reducir el riesgo de una recomendación basada únicamente en intuición,
se construyeron modelos de regresión lineal múltiple sobre la base
vivienda, complementados con análisis exploratorio,
validación de supuestos, comparación de especificaciones mediante
validación cruzada de 10 pliegues y una búsqueda reproducible de ofertas
observadas.
Para la Vivienda 1 (Casa, Zona Norte, crédito máximo de $350
millones), el modelo completo requerido explica 60,4% de la
variabilidad observada del precio y alcanza un \(R^2\) ajustado de 0,599. La estimación
puntual para el perfil solicitado se ubica entre $312,1 millones y
$392,7 millones al considerar los estratos 4 y 5. Se identificaron
cinco ofertas potenciales dentro del presupuesto,
priorizadas por similitud multivariable con el perfil solicitado.
Para la Vivienda 2 (Apartamento, Zona Sur, crédito máximo de
$850 millones), el modelo completo explica 74,9% de la
variabilidad y presenta un \(R^2\)
ajustado de 0,748. La estimación puntual para los estratos 5 y 6 se
ubica entre $675,0 millones y $735,9 millones. También se entregan
cinco alternativas ordenadas por cercanía al perfil y
con verificación del margen frente al crédito aprobado.
Recomendación ejecutiva. Utilizar el modelo como herramienta
de valoración y priorización, no como sustituto de la visita técnica ni
de la negociación. La dispersión de precios y los diagnósticos
residuales muestran que características no observadas
—micro-localización, estado de conservación, antigüedad, acabados,
administración y condiciones de negociación— pueden explicar una
fracción relevante del precio. Por ello, las cinco ofertas de cada caso
deben tomarse como una lista corta para validación comercial y
técnica.
Anexo técnico: estimaciones, validaciones y
comparación de modelos
Las secciones siguientes contienen el soporte reproducible del
informe ejecutivo: filtros, EDA, modelos, inferencia, validación,
predicciones y mapas de ofertas.
Contexto, objetivo y
enfoque analítico
El objetivo es estimar el precio de mercado de dos perfiles de
vivienda y proponer ofertas concretas compatibles con las restricciones
del cliente. Se usa regresión lineal múltiple porque permite cuantificar
el efecto marginal de varias características del inmueble sobre el
precio, manteniendo constantes las demás variables. Esta lógica es
consistente con el enfoque de valoración hedónica utilizado en estudios
inmobiliarios recientes, donde área, habitaciones, baños, parqueaderos y
localización explican diferencias de valor entre propiedades (Agudelo
Torres et al., 2022; Wang, 2024; Zhong, 2024).
La especificación principal solicitada es:
\[
Precio_i = \beta_0 + \beta_1 Area_i + \beta_2 Estrato_i + \beta_3
Habitaciones_i + \beta_4 Parqueaderos_i + \beta_5 Baños_i +
\varepsilon_i
\]
El análisis se desarrolló con una secuencia reproducible: depuración
y filtro, EDA, estimación por mínimos cuadrados ordinarios,
interpretación de coeficientes, evaluación de ajuste, diagnóstico de
supuestos, validación cruzada, predicción con intervalo al 95% y
priorización de ofertas. La comparación entre ajuste dentro de muestra y
desempeño fuera de muestra sigue buenas prácticas modernas de modelación
en R (Kuhn & Silge, 2022; Heumann et al., 2023).
Datos, calidad y
depuración
Fuente y
estructura
La base vivienda proviene del paquete
paqueteMODELOS suministrado en el curso. Contiene
información sobre tipo de inmueble, zona, estrato, precio en millones de
pesos, área construida, parqueaderos, baños, habitaciones, barrio y
coordenadas geográficas.
kbl_prof(tabla_calidad, "Tabla 1. Controles generales de calidad de datos", 0)
Tabla 1. Controles generales de calidad de datos
|
Indicador
|
Valor
|
|
Registros originales
|
8322
|
|
Registros tras retirar duplicados exactos
|
8321
|
|
Duplicados exactos detectados
|
1
|
|
Variables
|
13
|
|
Precios no positivos
|
0
|
|
Áreas no positivas
|
0
|
|
Coordenadas faltantes
|
2
|
kbl_prof(tabla_na, "Tabla 2. Valores faltantes por variable", 0)
Tabla 2. Valores faltantes por variable
|
|
Variable
|
Faltantes
|
|
id
|
id
|
2
|
|
zona
|
zona
|
2
|
|
piso
|
piso
|
2637
|
|
estrato
|
estrato
|
2
|
|
preciom
|
preciom
|
1
|
|
areaconst
|
areaconst
|
2
|
|
parqueaderos
|
parqueaderos
|
1604
|
|
banios
|
banios
|
2
|
|
habitaciones
|
habitaciones
|
2
|
|
tipo
|
tipo
|
2
|
|
barrio
|
barrio
|
2
|
|
longitud
|
longitud
|
2
|
|
latitud
|
latitud
|
2
|
La depuración evita eliminar observaciones por variables irrelevantes
para el modelo. Por ejemplo, los faltantes de piso no
justifican excluir una casa si el resto de variables requeridas está
completo. Para la regresión se emplean únicamente casos completos en
precio, área, estrato, parqueaderos, baños y habitaciones. Esta decisión
conserva la mayor cantidad de información útil y hace explícito el
universo efectivo de modelación.
Comprobación de
categorías
tabla_cat <- vivienda |>
count(tipo, zona, name = "n") |>
arrange(tipo, zona)
kbl_prof(tabla_cat, "Tabla 3. Distribución de registros por tipo de vivienda y zona", 0)
Tabla 3. Distribución de registros por tipo de vivienda y zona
|
tipo
|
zona
|
n
|
|
Apartamento
|
Zona Centro
|
24
|
|
Apartamento
|
Zona Norte
|
1198
|
|
Apartamento
|
Zona Oeste
|
1029
|
|
Apartamento
|
Zona Oriente
|
62
|
|
Apartamento
|
Zona Sur
|
2787
|
|
Casa
|
Zona Centro
|
100
|
|
Casa
|
Zona Norte
|
722
|
|
Casa
|
Zona Oeste
|
169
|
|
Casa
|
Zona Oriente
|
289
|
|
Casa
|
Zona Sur
|
1939
|
|
NA
|
NA
|
2
|
Solicitud 1 — Casa en
Zona Norte
Filtro inicial y
verificación
La primera solicitud exige: Casa, Zona
Norte, 200 m², 1 parqueadero,
2 baños, 4 habitaciones,
estrato 4 o 5 y crédito máximo de $350
millones.
base1 <- vivienda |>
filter(tipo == "Casa", zona == "Zona Norte")
head(base1, 3) |>
kbl_prof("Tabla 4. Primeros tres registros de casas en Zona Norte", 2)
Tabla 4. Primeros tres registros de casas en Zona Norte
|
id
|
zona
|
piso
|
estrato
|
preciom
|
areaconst
|
parqueaderos
|
banios
|
habitaciones
|
tipo
|
barrio
|
longitud
|
latitud
|
|
1209
|
Zona Norte
|
02
|
5
|
320
|
150
|
2
|
4
|
6
|
Casa
|
acopi
|
-76.51
|
3.48
|
|
1592
|
Zona Norte
|
02
|
5
|
780
|
380
|
2
|
3
|
3
|
Casa
|
acopi
|
-76.52
|
3.49
|
|
4057
|
Zona Norte
|
02
|
6
|
750
|
445
|
NA
|
7
|
6
|
Casa
|
acopi
|
-76.53
|
3.39
|
control1 <- bind_rows(
base1 |> count(tipo, name = "n") |> mutate(Control = "Tipo", Categoria = tipo) |> select(Control, Categoria, n),
base1 |> count(zona, name = "n") |> mutate(Control = "Zona", Categoria = zona) |> select(Control, Categoria, n),
base1 |> count(estrato, name = "n") |> mutate(Control = "Estrato", Categoria = as.character(estrato)) |> select(Control, Categoria, n)
)
kbl_prof(control1, "Tabla 5. Verificación del filtro inicial — Solicitud 1", 0)
Tabla 5. Verificación del filtro inicial — Solicitud 1
|
Control
|
Categoria
|
n
|
|
Tipo
|
Casa
|
722
|
|
Zona
|
Zona Norte
|
722
|
|
Estrato
|
3
|
235
|
|
Estrato
|
4
|
161
|
|
Estrato
|
5
|
271
|
|
Estrato
|
6
|
55
|
El filtro produce 722 casas clasificadas como Zona Norte; 435 tienen
información completa en todas las variables requeridas por la regresión.
La comprobación anterior asegura que el modelo no mezcle apartamentos ni
registros clasificados en otra zona.
Mapa de todas las
ofertas y consistencia geográfica
mapa_segmento(base1, "Casas — Zona Norte")
El mapa incluye todos los registros con coordenadas
válidas del segmento. Se marcan como atípicos los puntos que se
apartan del patrón espacial del propio segmento mediante un criterio
robusto basado en 1,5 rangos intercuartílicos. Se identifican 70 puntos
geográficamente atípicos sobre 722 observaciones mapeables.
Esto no implica automáticamente que la clasificación
Zona Norte sea incorrecta. La variable
zona es una categoría comercial de la fuente, mientras que
latitud/longitud puede corresponder a coordenadas aproximadas,
centroides de barrio, errores de geocodificación o captura. Como la base
no incorpora polígonos oficiales de delimitación de zonas, no es
metodológicamente válido recodificar la zona solo por inspección visual.
Para un modelo de producción se recomienda validar las coordenadas
contra cartografía oficial y modelar explícitamente la
micro-localización.
Análisis exploratorio
de datos
Correlación entre
precio y características
cor1_show <- cor1 |>
mutate(across(c(Pearson, Spearman), ~round(.x, 3)))
kbl_prof(cor1_show, "Tabla 6. Correlación del precio con los predictores — Casas Zona Norte", 3)
Tabla 6. Correlación del precio con los predictores — Casas Zona Norte
|
Variable
|
Pearson
|
Spearman
|
Intensidad
|
Direccion
|
|
areaconst
|
0.685
|
0.777
|
moderada
|
positiva
|
|
estrato
|
0.528
|
0.601
|
moderada
|
positiva
|
|
banios
|
0.509
|
0.563
|
moderada
|
positiva
|
|
parqueaderos
|
0.412
|
0.478
|
moderada
|
positiva
|
|
habitaciones
|
0.365
|
0.373
|
débil
|
positiva
|
Se reportan Pearson y Spearman. Pearson resume asociación lineal;
Spearman complementa el análisis cuando las variables son ordinales o
discretas, como estrato, baños o habitaciones. La variable con mayor
asociación lineal absoluta con el precio es areaconst,
con una correlación de Pearson de 0,685, clasificada como moderada y
positiva.
plot_ly(
x = colnames(cor_mat1), y = rownames(cor_mat1), z = cor_mat1,
type = "heatmap", zmin = -1, zmax = 1,
hovertemplate = "X: %{x}<br>Y: %{y}<br>r: %{z:.3f}<extra></extra>"
) |>
layout(title = "Matriz de correlación — Casas Zona Norte", xaxis = list(title = ""), yaxis = list(title = ""))
p_area1 <- ggplot(base1_model, aes(areaconst, preciom)) +
geom_point(aes(text = paste0(
"ID: ", id,
"<br>Barrio: ", barrio,
"<br>Área: ", areaconst, " m²",
"<br>Precio: $", preciom, " M",
"<br>Estrato: ", estrato
)), alpha = 0.50) +
geom_smooth(method = "lm", se = TRUE) +
labs(
title = "Precio vs. área construida — Casas Zona Norte",
x = "Área construida (m²)", y = "Precio (millones COP)"
) +
theme_minimal(base_size = 12)
ggplotly(p_area1, tooltip = "text")
Efecto contextual
de la zona
Dentro de base1, la variable zona es constante por
construcción y, por tanto, no puede correlacionarse ni estimarse
como predictor interno del modelo. Para no perder su
importancia económica, se compara el precio de todas las casas de Cali
por zona:
kbl_prof(zone_house, "Tabla 7. Precio de casas por zona — contexto de mercado", 1)
Tabla 7. Precio de casas por zona — contexto de mercado
|
zona
|
n
|
Precio_mediano
|
Precio_promedio
|
|
Zona Centro
|
100
|
310
|
339.2
|
|
Zona Norte
|
722
|
390
|
445.9
|
|
Zona Oeste
|
169
|
680
|
736.4
|
|
Zona Oriente
|
289
|
235
|
244.8
|
|
Zona Sur
|
1939
|
480
|
612.3
|
p_z1 <- vivienda |>
filter(tipo == "Casa", !is.na(zona), !is.na(preciom)) |>
ggplot(aes(x = reorder(zona, preciom, FUN = median), y = preciom)) +
geom_boxplot(outlier.alpha = 0.20) +
labs(title = "Distribución del precio de casas por zona", x = "Zona", y = "Precio (millones COP)") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 25, hjust = 1))
ggplotly(p_z1)
La comparación confirma por qué la ubicación debe considerarse en la
interpretación: aun cuando el modelo de la solicitud se estima
exclusivamente dentro de Zona Norte, el mercado general presenta niveles
y dispersiones de precio diferentes entre zonas.
Estimación del modelo
de regresión lineal múltiple
m1_full <- lm(
preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
data = base1_model
)
summary(m1_full)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = base1_model)
##
## Residuals:
## Min 1Q Median 3Q Max
## -784.3 -77.6 -16.0 47.7 978.6
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -238.1709 44.4055 -5.36 0.0000001337033031 ***
## areaconst 0.6767 0.0528 12.81 < 0.0000000000000002 ***
## estrato 80.6349 9.8263 8.21 0.0000000000000027 ***
## habitaciones 7.6451 5.6587 1.35 0.177
## parqueaderos 24.0060 5.8689 4.09 0.0000514440170084 ***
## banios 18.8994 7.4880 2.52 0.012 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 155 on 429 degrees of freedom
## Multiple R-squared: 0.604, Adjusted R-squared: 0.599
## F-statistic: 131 on 5 and 429 DF, p-value: <0.0000000000000002
Coeficientes e
interpretación
coef1_tab <- coef1 |>
mutate(
estimate = round(estimate, 3), std.error = round(std.error, 3),
conf.low = round(conf.low, 3), conf.high = round(conf.high, 3),
p.value = ifelse(p.value < 0.001, "<0,001", format(round(p.value, 3), decimal.mark = ","))
) |>
rename(
Estimacion = estimate, `Error estándar` = std.error,
`IC95% inferior` = conf.low, `IC95% superior` = conf.high,
`p-valor` = p.value, `Significativo al 5%` = Significativo_5pct,
Interpretación = Interpretacion
)
kbl_prof(coef1_tab, "Tabla 8. Coeficientes del modelo completo — Casas Zona Norte", 3)
Tabla 8. Coeficientes del modelo completo — Casas Zona Norte
|
Variable
|
Estimacion
|
Error estándar
|
IC95% inferior
|
IC95% superior
|
p-valor
|
Significativo al 5%
|
Interpretación
|
|
área construida (m²)
|
0.677
|
0.053
|
0.573
|
0.781
|
<0,001
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en área
construida (m²) se asocia con $ 0,68 millones más en el precio esperado.
El efecto es estadísticamente significativo al 5%.
|
|
estrato
|
80.635
|
9.826
|
61.321
|
99.949
|
<0,001
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en
estrato se asocia con $80,63 millones más en el precio esperado. El
efecto es estadísticamente significativo al 5%.
|
|
habitaciones
|
7.645
|
5.659
|
-3.477
|
18.767
|
0,177
|
No
|
Manteniendo constantes las demás variables, una unidad adicional en
habitaciones se asocia con $ 7,65 millones más en el precio esperado. No
hay evidencia suficiente de un efecto distinto de cero al 5%.
|
|
parqueaderos
|
24.006
|
5.869
|
12.471
|
35.541
|
<0,001
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en
parqueaderos se asocia con $24,01 millones más en el precio esperado. El
efecto es estadísticamente significativo al 5%.
|
|
baños
|
18.899
|
7.488
|
4.182
|
33.617
|
0,012
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en
baños se asocia con $18,90 millones más en el precio esperado. El efecto
es estadísticamente significativo al 5%.
|
El intercepto no se interpreta económicamente porque representa una
vivienda con área, estrato, habitaciones, parqueaderos y baños iguales a
cero, combinación fuera del dominio real del mercado. La interpretación
sustantiva se concentra en las pendientes: cada coeficiente representa
el cambio esperado en millones de pesos ante una unidad adicional del
predictor, manteniendo constantes las demás
características.
El modelo completo alcanza \(R^2 =
0,604\) y \(R^2_{aj} = 0,599\).
En otras palabras, las cinco características explican 60,4% de la
variación observada de precios dentro del segmento. La diferencia entre
\(R^2\) y \(R^2_{aj}\) es pequeña cuando los
predictores agregan señal útil; si fuera amplia, sugeriría complejidad
innecesaria.
No obstante, un \(R^2\) alto no
garantiza buenas predicciones fuera de muestra ni cumplimiento de
supuestos. Por eso se complementa con validación cruzada, AIC/BIC y
diagnóstico residual.
Comparación de
modelos y validación cruzada
Se compara un modelo base de solo área, el modelo múltiple requerido
y una versión parsimoniosa obtenida por eliminación hacia atrás con AIC.
La selección final se basa en el RMSE de validación cruzada de
10 pliegues y, en segundo término, parsimonia.
comp1_show <- comp1 |>
mutate(across(where(is.numeric), ~round(.x, 3)))
kbl_prof(comp1_show, "Tabla 9. Comparación de modelos — Casas Zona Norte", 3)
Tabla 9. Comparación de modelos — Casas Zona Norte
|
Modelo
|
R2
|
R2_ajustado
|
AIC
|
BIC
|
RMSE_CV
|
MAE_CV
|
R2_CV
|
|
M0: solo área
|
0.470
|
0.469
|
5750
|
5762
|
184.3
|
116.7
|
0.434
|
|
M1: completo requerido
|
0.604
|
0.599
|
5631
|
5659
|
160.4
|
101.8
|
0.570
|
|
M2: parsimonioso AIC
|
0.602
|
0.599
|
5631
|
5655
|
160.5
|
102.0
|
0.570
|
El modelo seleccionado para predicción es M1: completo
requerido. Su uso evita elegir una especificación solo por
ajuste dentro de muestra y favorece capacidad de generalización,
principio central en la evaluación de modelos predictivos (Kuhn &
Silge, 2022).
Validación de
supuestos
kbl_prof(diag1, "Tabla 10. Diagnóstico de supuestos — modelo seleccionado, Casas Zona Norte", 2)
Tabla 10. Diagnóstico de supuestos — modelo seleccionado, Casas Zona
Norte
|
Supuesto
|
Diagnostico
|
Lectura
|
|
Linealidad / especificación
|
RESET: p = < 0,001
|
Hay evidencia de posible no linealidad o términos omitidos.
|
|
Homoscedasticidad
|
Breusch–Pagan: p = < 0,001
|
Se detecta heterocedasticidad; conviene contrastar con errores
robustos/transformaciones.
|
|
Independencia secuencial*
|
Durbin–Watson = 1,76; p = 0,005
|
Se detecta patrón secuencial; en datos transversales debe interpretarse
con cautela.
|
|
Normalidad de residuos
|
Shapiro–Wilk: p = < 0,001
|
Se rechaza normalidad; revisar Q-Q y colas, especialmente para
intervalos.
|
|
Multicolinealidad
|
VIF máximo = 1,97
|
No hay multicolinealidad severa bajo el umbral VIF < 5.
|
|
Influencia
|
25 observaciones con Cook > 4/n
|
Existen casos influyentes que deben revisarse antes de decisiones de
alto impacto.
|
*Durbin–Watson se reporta porque hace parte
del diagnóstico clásico, pero las observaciones son transversales y el
orden de la base no constituye una serie temporal. Por ello, un
resultado significativo no debe confundirse con evidencia formal de
dependencia espacial.
par(mfrow = c(2, 2))
plot(m1_final)

La lectura de los supuestos se realiza de forma conjunta. Si
Breusch–Pagan resulta significativo, los coeficientes MCO siguen siendo
útiles como estimadores de tendencia bajo exogeneidad, pero los errores
estándar convencionales pueden distorsionarse. Como análisis de
sensibilidad se reportan errores estándar HC3, sin reemplazar el modelo
requerido.
kbl_prof(rob1, "Tabla 11. Sensibilidad inferencial con errores estándar robustos HC3 — Casas Zona Norte", 4)
Tabla 11. Sensibilidad inferencial con errores estándar robustos HC3 —
Casas Zona Norte
|
Termino
|
Estimacion
|
EE_HC3
|
t
|
p_HC3
|
|
(Intercept)
|
-238.1709
|
45.7844
|
-5.202
|
0.0000
|
|
areaconst
|
0.6767
|
0.1479
|
4.575
|
0.0000
|
|
estrato
|
80.6349
|
12.1550
|
6.634
|
0.0000
|
|
habitaciones
|
7.6451
|
6.9791
|
1.095
|
0.2739
|
|
parqueaderos
|
24.0060
|
7.0696
|
3.396
|
0.0007
|
|
banios
|
18.8994
|
10.4622
|
1.806
|
0.0715
|
Sugerencias de
mejora del modelo
Si los diagnósticos muestran heterocedasticidad, no linealidad,
residuos de cola pesada o casos influyentes, las siguientes extensiones
son técnicamente razonables: transformar el precio (por ejemplo,
logaritmo), modelar relaciones no lineales del área, incorporar
interacciones, añadir barrio o coordenadas, incluir
antigüedad/estado/acabados/administración, estimar errores robustos y
realizar validación externa con nuevas ofertas. La literatura reciente
sobre precios de vivienda destaca precisamente la relevancia de la
localización y de variables físicas adicionales para mejorar la
capacidad explicativa y predictiva (Agudelo Torres et al., 2022; Wang,
2024).
Estimación puntual e
intervalo de predicción — Vivienda 1
La solicitud permite estrato 4 o 5. En consecuencia, se predicen
ambos escenarios en lugar de imponer arbitrariamente uno solo.
pred1_show <- pred1 |>
transmute(
Estrato = estrato,
`Estimación puntual (M)` = round(fit, 1),
`Límite inferior IP95% (M)` = round(lwr, 1),
`Límite superior IP95% (M)` = round(upr, 1),
`Crédito máximo (M)` = presupuesto,
`Margen vs. estimación (M)` = round(margen, 1),
Decisión = decision_presupuesto
)
kbl_prof(pred1_show, "Tabla 12. Predicción de precio para la Vivienda 1", 1)
Tabla 12. Predicción de precio para la Vivienda 1
|
Estrato
|
Estimación puntual (M)
|
Límite inferior IP95% (M)
|
Límite superior IP95% (M)
|
Crédito máximo (M)
|
Margen vs. estimación (M)
|
Decisión
|
|
4
|
312.1
|
6.2
|
618.0
|
350
|
37.9
|
Estimación puntual dentro del crédito
|
|
5
|
392.7
|
86.2
|
699.3
|
350
|
-42.7
|
Estimación puntual supera el crédito
|
La estimación puntual representa el precio promedio
esperado para una vivienda con esas características según el modelo. El
intervalo de predicción al 95% es más amplio porque
incorpora tanto la incertidumbre de estimación como la variabilidad
individual de una nueva vivienda. Para una decisión de compra es más
informativo que limitarse a un único número.
Cinco ofertas
potenciales dentro del crédito
La selección se restringe a casas de Zona Norte, estrato 4 o 5 y
precio observado no superior a $350 millones. Luego se ordenan por una
distancia ponderada frente al perfil solicitado: 40% área, 20%
parqueaderos, 20% baños y 20% habitaciones. Este criterio evita escoger
únicamente por precio y favorece similitud funcional.
ofertas1_show <- ofertas1 |>
transmute(
Ranking, ID = id, Barrio = barrio, Estrato = estrato,
`Precio oferta (M)` = preciom, `Área (m²)` = areaconst,
Parqueaderos = parqueaderos, Baños = banios, Habitaciones = habitaciones,
`Precio modelo (M)` = round(precio_modelo, 1),
`Margen crédito (M)` = round(margen_credito, 1),
`Score perfil` = round(score_perfil, 3)
)
kbl_prof(ofertas1_show, "Tabla 13. Top 5 de ofertas potenciales — Vivienda 1", 2)
Tabla 13. Top 5 de ofertas potenciales — Vivienda 1
|
Ranking
|
ID
|
Barrio
|
Estrato
|
Precio oferta (M)
|
Área (m²)
|
Parqueaderos
|
Baños
|
Habitaciones
|
Precio modelo (M)
|
Margen crédito (M)
|
Score perfil
|
|
1
|
3586
|
la merced
|
4
|
330
|
240
|
1
|
2
|
3
|
331.5
|
20
|
0.13
|
|
2
|
1666
|
alamos
|
4
|
275
|
120
|
1
|
2
|
4
|
258.0
|
75
|
0.16
|
|
3
|
4779
|
prados del norte
|
5
|
280
|
140
|
1
|
2
|
3
|
344.5
|
70
|
0.17
|
|
4
|
1352
|
la flora
|
5
|
350
|
190
|
1
|
3
|
3
|
397.2
|
0
|
0.17
|
|
5
|
94
|
zona norte
|
4
|
265
|
162
|
1
|
3
|
4
|
305.3
|
85
|
0.18
|
mapa_ofertas(ofertas1, "Top 5 — Vivienda 1")
La prioridad inicial es la oferta ID 3586, ubicada
en la merced, porque presenta la menor distancia
multivariable al perfil entre los inmuebles elegibles. Su precio
publicado es $330,0 millones, dejando un margen de $20,0 millones frente
al crédito. Un gap_modelo positivo indica que el precio
publicado está por debajo de lo que predice el modelo para sus
características; se interpreta como señal para investigar una posible
oportunidad, no como prueba de subvaloración.
Solicitud 2 —
Apartamento en Zona Sur
Filtro inicial y
verificación
La segunda solicitud exige: Apartamento,
Zona Sur, 300 m², 3
parqueaderos, 3 baños, 5
habitaciones, estrato 5 o 6 y crédito máximo
de $850 millones.
base2 <- vivienda |>
filter(tipo == "Apartamento", zona == "Zona Sur")
head(base2, 3) |>
kbl_prof("Tabla 14. Primeros tres registros de apartamentos en Zona Sur", 2)
Tabla 14. Primeros tres registros de apartamentos en Zona Sur
|
id
|
zona
|
piso
|
estrato
|
preciom
|
areaconst
|
parqueaderos
|
banios
|
habitaciones
|
tipo
|
barrio
|
longitud
|
latitud
|
|
5098
|
Zona Sur
|
05
|
4
|
290
|
96
|
1
|
2
|
3
|
Apartamento
|
acopi
|
-76.53
|
3.45
|
|
698
|
Zona Sur
|
02
|
3
|
78
|
40
|
1
|
1
|
2
|
Apartamento
|
aguablanca
|
-76.50
|
3.40
|
|
8199
|
Zona Sur
|
NA
|
6
|
875
|
194
|
2
|
5
|
3
|
Apartamento
|
aguacatal
|
-76.56
|
3.46
|
control2 <- bind_rows(
base2 |> count(tipo, name = "n") |> mutate(Control = "Tipo", Categoria = tipo) |> select(Control, Categoria, n),
base2 |> count(zona, name = "n") |> mutate(Control = "Zona", Categoria = zona) |> select(Control, Categoria, n),
base2 |> count(estrato, name = "n") |> mutate(Control = "Estrato", Categoria = as.character(estrato)) |> select(Control, Categoria, n)
)
kbl_prof(control2, "Tabla 15. Verificación del filtro inicial — Solicitud 2", 0)
Tabla 15. Verificación del filtro inicial — Solicitud 2
|
Control
|
Categoria
|
n
|
|
Tipo
|
Apartamento
|
2787
|
|
Zona
|
Zona Sur
|
2787
|
|
Estrato
|
3
|
201
|
|
Estrato
|
4
|
1091
|
|
Estrato
|
5
|
1033
|
|
Estrato
|
6
|
462
|
El filtro produce 2787 apartamentos clasificados como Zona Sur; 2381
tienen datos completos en las variables del modelo.
Mapa de todas las
ofertas y consistencia geográfica
mapa_segmento(base2, "Apartamentos — Zona Sur")
Se identifican 116 puntos geográficamente atípicos sobre 2787
observaciones mapeables. Al igual que en la Zona Norte, estos puntos
deben revisarse, pero no recodificarse automáticamente sin una capa
geográfica oficial de zonas.
Análisis exploratorio
de datos
cor2_show <- cor2 |>
mutate(across(c(Pearson, Spearman), ~round(.x, 3)))
kbl_prof(cor2_show, "Tabla 16. Correlación del precio con los predictores — Apartamentos Zona Sur", 3)
Tabla 16. Correlación del precio con los predictores — Apartamentos Zona
Sur
|
Variable
|
Pearson
|
Spearman
|
Intensidad
|
Direccion
|
|
areaconst
|
0.741
|
0.851
|
fuerte
|
positiva
|
|
banios
|
0.711
|
0.680
|
fuerte
|
positiva
|
|
parqueaderos
|
0.693
|
0.688
|
moderada
|
positiva
|
|
estrato
|
0.650
|
0.721
|
moderada
|
positiva
|
|
habitaciones
|
0.296
|
0.334
|
débil
|
positiva
|
La asociación lineal absoluta más alta con el precio corresponde a
areaconst, con Pearson = 0,741, relación fuerte y
positiva.
plot_ly(
x = colnames(cor_mat2), y = rownames(cor_mat2), z = cor_mat2,
type = "heatmap", zmin = -1, zmax = 1,
hovertemplate = "X: %{x}<br>Y: %{y}<br>r: %{z:.3f}<extra></extra>"
) |>
layout(title = "Matriz de correlación — Apartamentos Zona Sur", xaxis = list(title = ""), yaxis = list(title = ""))
p_area2 <- ggplot(base2_model, aes(areaconst, preciom)) +
geom_point(aes(text = paste0(
"ID: ", id,
"<br>Barrio: ", barrio,
"<br>Área: ", areaconst, " m²",
"<br>Precio: $", preciom, " M",
"<br>Estrato: ", estrato
)), alpha = 0.50) +
geom_smooth(method = "lm", se = TRUE) +
labs(
title = "Precio vs. área construida — Apartamentos Zona Sur",
x = "Área construida (m²)", y = "Precio (millones COP)"
) +
theme_minimal(base_size = 12)
ggplotly(p_area2, tooltip = "text")
Efecto contextual
de la zona
kbl_prof(zone_apto, "Tabla 17. Precio de apartamentos por zona — contexto de mercado", 1)
Tabla 17. Precio de apartamentos por zona — contexto de mercado
|
zona
|
n
|
Precio_mediano
|
Precio_promedio
|
|
Zona Centro
|
24
|
152.5
|
186.6
|
|
Zona Norte
|
1198
|
250.0
|
285.2
|
|
Zona Oeste
|
1029
|
570.0
|
667.9
|
|
Zona Oriente
|
62
|
115.0
|
152.6
|
|
Zona Sur
|
2787
|
245.0
|
297.3
|
p_z2 <- vivienda |>
filter(tipo == "Apartamento", !is.na(zona), !is.na(preciom)) |>
ggplot(aes(x = reorder(zona, preciom, FUN = median), y = preciom)) +
geom_boxplot(outlier.alpha = 0.20) +
labs(title = "Distribución del precio de apartamentos por zona", x = "Zona", y = "Precio (millones COP)") +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 25, hjust = 1))
ggplotly(p_z2)
Estimación del modelo
de regresión lineal múltiple
m2_full <- lm(
preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
data = base2_model
)
summary(m2_full)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = base2_model)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1092.0 -42.3 -1.3 40.6 926.6
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -261.625 15.632 -16.74 < 0.0000000000000002 ***
## areaconst 1.285 0.054 23.79 < 0.0000000000000002 ***
## estrato 60.897 3.084 19.75 < 0.0000000000000002 ***
## habitaciones -24.837 3.892 -6.38 0.00000000021 ***
## parqueaderos 72.915 3.958 18.42 < 0.0000000000000002 ***
## banios 50.697 3.396 14.93 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 98 on 2375 degrees of freedom
## Multiple R-squared: 0.749, Adjusted R-squared: 0.748
## F-statistic: 1.41e+03 on 5 and 2375 DF, p-value: <0.0000000000000002
Coeficientes e
interpretación
coef2_tab <- coef2 |>
mutate(
estimate = round(estimate, 3), std.error = round(std.error, 3),
conf.low = round(conf.low, 3), conf.high = round(conf.high, 3),
p.value = ifelse(p.value < 0.001, "<0,001", format(round(p.value, 3), decimal.mark = ","))
) |>
rename(
Estimacion = estimate, `Error estándar` = std.error,
`IC95% inferior` = conf.low, `IC95% superior` = conf.high,
`p-valor` = p.value, `Significativo al 5%` = Significativo_5pct,
Interpretación = Interpretacion
)
kbl_prof(coef2_tab, "Tabla 18. Coeficientes del modelo completo — Apartamentos Zona Sur", 3)
Tabla 18. Coeficientes del modelo completo — Apartamentos Zona Sur
|
Variable
|
Estimacion
|
Error estándar
|
IC95% inferior
|
IC95% superior
|
p-valor
|
Significativo al 5%
|
Interpretación
|
|
área construida (m²)
|
1.285
|
0.054
|
1.179
|
1.391
|
<0,001
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en área
construida (m²) se asocia con $ 1,29 millones más en el precio esperado.
El efecto es estadísticamente significativo al 5%.
|
|
estrato
|
60.897
|
3.084
|
54.849
|
66.945
|
<0,001
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en
estrato se asocia con $60,90 millones más en el precio esperado. El
efecto es estadísticamente significativo al 5%.
|
|
habitaciones
|
-24.837
|
3.892
|
-32.470
|
-17.204
|
<0,001
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en
habitaciones se asocia con $24,84 millones menos en el precio esperado.
El efecto es estadísticamente significativo al 5%.
|
|
parqueaderos
|
72.915
|
3.958
|
65.153
|
80.676
|
<0,001
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en
parqueaderos se asocia con $72,91 millones más en el precio esperado. El
efecto es estadísticamente significativo al 5%.
|
|
baños
|
50.697
|
3.396
|
44.037
|
57.357
|
<0,001
|
Sí
|
Manteniendo constantes las demás variables, una unidad adicional en
baños se asocia con $50,70 millones más en el precio esperado. El efecto
es estadísticamente significativo al 5%.
|
El modelo completo obtiene \(R^2 =
0,749\) y \(R^2_{aj} = 0,748\),
por lo que explica 74,9% de la variabilidad del precio en apartamentos
de Zona Sur. La interpretación de cada pendiente es condicional a las
demás características, lo que evita confundir una correlación bivariada
con un efecto parcial dentro del modelo.
Comparación de
modelos y validación cruzada
comp2_show <- comp2 |>
mutate(across(where(is.numeric), ~round(.x, 3)))
kbl_prof(comp2_show, "Tabla 19. Comparación de modelos — Apartamentos Zona Sur", 3)
Tabla 19. Comparación de modelos — Apartamentos Zona Sur
|
Modelo
|
R2
|
R2_ajustado
|
AIC
|
BIC
|
RMSE_CV
|
MAE_CV
|
R2_CV
|
|
M0: solo área
|
0.549
|
0.548
|
29984
|
30001
|
138.6
|
75.68
|
0.496
|
|
M1: completo requerido
|
0.749
|
0.748
|
28600
|
28640
|
104.0
|
60.73
|
0.716
|
|
M2: parsimonioso AIC
|
0.749
|
0.748
|
28600
|
28640
|
104.0
|
60.73
|
0.716
|
El modelo seleccionado para predicción es M1: completo
requerido, al ofrecer el mejor balance entre error de
validación y complejidad entre las especificaciones múltiples
evaluadas.
Validación de
supuestos
kbl_prof(diag2, "Tabla 20. Diagnóstico de supuestos — modelo seleccionado, Apartamentos Zona Sur", 2)
Tabla 20. Diagnóstico de supuestos — modelo seleccionado, Apartamentos
Zona Sur
|
Supuesto
|
Diagnostico
|
Lectura
|
|
Linealidad / especificación
|
RESET: p = < 0,001
|
Hay evidencia de posible no linealidad o términos omitidos.
|
|
Homoscedasticidad
|
Breusch–Pagan: p = < 0,001
|
Se detecta heterocedasticidad; conviene contrastar con errores
robustos/transformaciones.
|
|
Independencia secuencial*
|
Durbin–Watson = 1,53; p = < 0,001
|
Se detecta patrón secuencial; en datos transversales debe interpretarse
con cautela.
|
|
Normalidad de residuos
|
Shapiro–Wilk: p = < 0,001
|
Se rechaza normalidad; revisar Q-Q y colas, especialmente para
intervalos.
|
|
Multicolinealidad
|
VIF máximo = 2,53
|
No hay multicolinealidad severa bajo el umbral VIF < 5.
|
|
Influencia
|
118 observaciones con Cook > 4/n
|
Existen casos influyentes que deben revisarse antes de decisiones de
alto impacto.
|
par(mfrow = c(2, 2))
plot(m2_final)

kbl_prof(rob2, "Tabla 21. Sensibilidad inferencial con errores estándar robustos HC3 — Apartamentos Zona Sur", 4)
Tabla 21. Sensibilidad inferencial con errores estándar robustos HC3 —
Apartamentos Zona Sur
|
Termino
|
Estimacion
|
EE_HC3
|
t
|
p_HC3
|
|
(Intercept)
|
-261.625
|
23.3820
|
-11.189
|
0.0000
|
|
areaconst
|
1.285
|
0.3987
|
3.223
|
0.0013
|
|
estrato
|
60.897
|
3.9981
|
15.231
|
0.0000
|
|
habitaciones
|
-24.837
|
6.9558
|
-3.571
|
0.0004
|
|
parqueaderos
|
72.915
|
15.1745
|
4.805
|
0.0000
|
|
banios
|
50.697
|
9.1456
|
5.543
|
0.0000
|
Cuando la heterocedasticidad es significativa, los errores estándar
HC3 proporcionan una verificación de sensibilidad de la inferencia. Si
las conclusiones de significancia cambian de forma sustancial frente a
MCO, la decisión debe apoyarse más en predicción y validación que en
p-valores individuales.
Estimación puntual e
intervalo de predicción — Vivienda 2
pred2_show <- pred2 |>
transmute(
Estrato = estrato,
`Estimación puntual (M)` = round(fit, 1),
`Límite inferior IP95% (M)` = round(lwr, 1),
`Límite superior IP95% (M)` = round(upr, 1),
`Crédito máximo (M)` = presupuesto,
`Margen vs. estimación (M)` = round(margen, 1),
Decisión = decision_presupuesto
)
kbl_prof(pred2_show, "Tabla 22. Predicción de precio para la Vivienda 2", 1)
Tabla 22. Predicción de precio para la Vivienda 2
|
Estrato
|
Estimación puntual (M)
|
Límite inferior IP95% (M)
|
Límite superior IP95% (M)
|
Crédito máximo (M)
|
Margen vs. estimación (M)
|
Decisión
|
|
5
|
675.0
|
481.5
|
868.6
|
850
|
175.0
|
Estimación puntual dentro del crédito
|
|
6
|
735.9
|
542.3
|
929.5
|
850
|
114.1
|
Estimación puntual dentro del crédito
|
La doble predicción para estratos 5 y 6 muestra el efecto de una de
las condiciones más relevantes del cliente. La estimación puntual debe
leerse junto con el intervalo del 95% y con la disponibilidad real de
ofertas bajo $850 millones.
Cinco ofertas
potenciales dentro del crédito
ofertas2_show <- ofertas2 |>
transmute(
Ranking, ID = id, Barrio = barrio, Estrato = estrato,
`Precio oferta (M)` = preciom, `Área (m²)` = areaconst,
Parqueaderos = parqueaderos, Baños = banios, Habitaciones = habitaciones,
`Precio modelo (M)` = round(precio_modelo, 1),
`Margen crédito (M)` = round(margen_credito, 1),
`Score perfil` = round(score_perfil, 3)
)
kbl_prof(ofertas2_show, "Tabla 23. Top 5 de ofertas potenciales — Vivienda 2", 2)
Tabla 23. Top 5 de ofertas potenciales — Vivienda 2
|
Ranking
|
ID
|
Barrio
|
Estrato
|
Precio oferta (M)
|
Área (m²)
|
Parqueaderos
|
Baños
|
Habitaciones
|
Precio modelo (M)
|
Margen crédito (M)
|
Score perfil
|
|
1
|
6175
|
capri
|
5
|
350
|
270
|
3
|
3
|
4
|
661.3
|
500
|
0.08
|
|
2
|
7680
|
pampa linda
|
5
|
450
|
267
|
3
|
3
|
3
|
682.3
|
400
|
0.12
|
|
3
|
6205
|
capri
|
5
|
350
|
260
|
3
|
3
|
3
|
673.3
|
500
|
0.13
|
|
4
|
7512
|
seminario
|
5
|
670
|
300
|
3
|
5
|
6
|
751.6
|
180
|
0.17
|
|
5
|
5423
|
ciudad jardín
|
6
|
695
|
227
|
3
|
3
|
3
|
691.8
|
155
|
0.18
|
mapa_ofertas(ofertas2, "Top 5 — Vivienda 2")
La primera alternativa es la oferta ID 6175, en
capri, con precio de $350,0 millones y margen de $500,0
millones frente al crédito. Las demás opciones permiten comparar cuánto
se sacrifica o gana en área, parqueaderos, baños o habitaciones sin
exceder el presupuesto.
Comparación ejecutiva
de las dos solicitudes
resumen_final <- data.frame(
Solicitud = c("Vivienda 1", "Vivienda 2"),
Segmento = c("Casa — Zona Norte", "Apartamento — Zona Sur"),
Presupuesto_M = c(350, 850),
N_segmento = c(nrow(base1), nrow(base2)),
N_modelo = c(nrow(base1_model), nrow(base2_model)),
R2_modelo_completo = c(s1$r.squared, s2$r.squared),
R2_ajustado = c(s1$adj.r.squared, s2$adj.r.squared),
Modelo_seleccionado = c(best1_name, best2_name),
Prediccion_min_M = c(minfit1, minfit2),
Prediccion_max_M = c(maxfit1, maxfit2),
Ofertas_recomendadas = c(nrow(ofertas1), nrow(ofertas2))
) |>
mutate(across(where(is.numeric), ~round(.x, 3)))
kbl_prof(resumen_final, "Tabla 24. Síntesis comparativa para la toma de decisión", 3)
Tabla 24. Síntesis comparativa para la toma de decisión
|
Solicitud
|
Segmento
|
Presupuesto_M
|
N_segmento
|
N_modelo
|
R2_modelo_completo
|
R2_ajustado
|
Modelo_seleccionado
|
Prediccion_min_M
|
Prediccion_max_M
|
Ofertas_recomendadas
|
|
Vivienda 1
|
Casa — Zona Norte
|
350
|
722
|
435
|
0.604
|
0.599
|
M1: completo requerido
|
312.1
|
392.7
|
5
|
|
Vivienda 2
|
Apartamento — Zona Sur
|
850
|
2787
|
2381
|
0.749
|
0.748
|
M1: completo requerido
|
675.0
|
735.9
|
5
|
En ambos segmentos, el área construida, el estrato y la dotación
física capturan una parte sustantiva del valor, pero no agotan la
explicación del precio. La comparación entre el modelo de solo área y el
modelo múltiple permite cuantificar el valor adicional de considerar
simultáneamente varias características; la validación cruzada evita
sobrevalorar mejoras que solo aparecen dentro de la muestra.
Conclusiones y
recomendaciones
1. El filtro y la depuración son determinantes para la
validez del análisis. Se trabajó por segmentos homogéneos de
tipo y zona, evitando mezclar mercados estructuralmente diferentes. Los
casos incompletos se excluyeron únicamente cuando faltaba alguna
variable necesaria para la regresión.
2. La relación precio–características es
multivariable. El área tiene una relación relevante con el
precio, pero estrato, baños, habitaciones y parqueaderos aportan
información adicional. Por ello, una valoración basada solo en precio
por metro cuadrado puede ser insuficiente.
3. La significancia estadística no debe confundirse con
importancia económica. Un coeficiente puede ser
estadísticamente no significativo por solapamiento entre predictores,
aun cuando la característica sea comercialmente importante. La lectura
conjunta de intervalos, VIF, \(R^2\),
validación cruzada y contexto del negocio es más informativa que un
p-valor aislado.
4. Para la Vivienda 1, el modelo sitúa el precio
esperado entre $312,1 millones y $392,7 millones según el estrato
considerado. Las cinco alternativas seleccionadas cumplen el límite
financiero y deben priorizarse por similitud de perfil, empezando por la
opción ID 3586.
5. Para la Vivienda 2, el precio esperado se ubica
entre $675,0 millones y $735,9 millones para estratos 5 y 6. Se proponen
cinco alternativas dentro del crédito, encabezadas por la opción ID
6175.
6. Los intervalos de predicción son esenciales. Una
sola estimación puntual puede transmitir una falsa sensación de
precisión. El intervalo al 95% refleja la incertidumbre inherente a una
vivienda nueva y respalda una negociación con margen de seguridad.
7. La localización debe modelarse con mayor granularidad en
futuras versiones. La dispersión de coordenadas y la
heterogeneidad por barrio sugieren que una versión más avanzada debería
incorporar barrio, latitud/longitud o métodos espaciales. Un estudio
colombiano reciente muestra que atributos espaciales pueden generar
diferenciales de precio que un modelo puramente físico no captura
completamente (Agudelo Torres et al., 2022).
8. Recomendación final a C&A. Usar el ranking
cuantitativo para seleccionar inmuebles a visitar y, antes de cerrar la
compra, verificar estado estructural, acabados, antigüedad,
administración, seguridad, accesibilidad, documentación jurídica y
condiciones de negociación. El modelo reduce incertidumbre, pero la
decisión final debe integrar evidencia estadística y debida diligencia
inmobiliaria.
Limitaciones
La base representa ofertas observadas durante un periodo corto y no
necesariamente precios finales de transacción. El modelo tampoco dispone
de antigüedad, estado de conservación, calidad de acabados, piso/altura
como variable consistente para ambos tipos, administración, vista,
distancia a servicios ni variables jurídicas. Además, la independencia
espacial no se valida formalmente con polígonos o matrices de vecindad.
Estas limitaciones deben considerarse antes de extrapolar resultados a
otros periodos o zonas.
Referencias
Agudelo Torres, J. E., Ospina Espinoza, O. A., & González
Virviescas, E. P. (2022). El valor de la vista sobre la ciudad: el
caso los apartamentos en Envigado. Cuadernos de Vivienda y
Urbanismo. https://doi.org/10.11144/averiana.cvu14.vvsc
Heumann, C., Schomaker, M., & Shalabh. (2023). Introduction
to Statistics and Data Analysis: With Exercises, Solutions and
Applications in R (2nd ed.). Springer. https://doi.org/10.1007/978-3-031-11833-3
Kuhn, M., & Silge, J. (2022). Tidy Modeling with R.
O’Reilly Media.
Wang, Y. (2024). Research on housing prices forecasts based on a
multiple linear regression model. In Proceedings of the 1st
International Conference on Innovations in Applied Mathematics, Physics
and Astronomy (IAMPA 2024) (pp. 53–59). SciTePress. https://doi.org/10.5220/0012991000004601
Wickham, H., Çetinkaya-Rundel, M., & Grolemund, G. (2023). R
for Data Science (2nd ed.). O’Reilly Media.
Zhong, S. (2024). Factors influencing housing prices: A comparative
study using multiple linear regression and random forest.
Theoretical and Natural Science, 51, 73–79. https://doi.org/10.54254/2753-8818/51/2024CH0174
Material audiovisual
del curso
Anexo de
reproducibilidad
El informe fija set.seed(9044951) para que la asignación
de pliegues de validación cruzada sea reproducible. La versión exacta de
R y de los paquetes puede verificarse con:
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8 LC_CTYPE=Spanish_Colombia.utf8
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Colombia.utf8
##
## time zone: America/Bogota
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] htmltools_0.5.9 purrr_1.2.1 stringr_1.6.0 scales_1.4.0
## [5] sandwich_3.1-3 lmtest_0.9-40 zoo_1.8-15 broom_1.0.12
## [9] kableExtra_1.4.0 knitr_1.51 leaflet_2.2.3 plotly_4.12.0
## [13] ggplot2_4.0.2 tidyr_1.3.2 dplyr_1.2.0
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.56 bslib_0.10.0
## [4] htmlwidgets_1.6.4 remotes_2.5.0 processx_3.8.6
## [7] lattice_0.22-7 callr_3.7.6 leaflet.providers_3.0.0
## [10] vctrs_0.7.1 tools_4.5.2 crosstalk_1.2.2
## [13] ps_1.9.1 generics_0.1.4 curl_7.0.0
## [16] tibble_3.3.1 pkgconfig_2.0.3 Matrix_1.7-4
## [19] data.table_1.18.2.1 RColorBrewer_1.1-3 S7_0.2.1
## [22] desc_1.4.3 lifecycle_1.0.5 compiler_4.5.2
## [25] farver_2.1.2 textshaping_1.0.4 sass_0.4.10
## [28] yaml_2.3.12 lazyeval_0.2.3 pillar_1.11.1
## [31] jquerylib_0.1.4 cachem_1.1.0 nlme_3.1-168
## [34] tidyselect_1.2.1 digest_0.6.39 stringi_1.8.7
## [37] labeling_0.4.3 splines_4.5.2 fastmap_1.2.0
## [40] grid_4.5.2 cli_3.6.5 magrittr_2.0.4
## [43] pkgbuild_1.4.8 withr_3.0.2 backports_1.5.0
## [46] rmarkdown_2.30 httr_1.4.8 otel_0.2.0
## [49] evaluate_1.0.5 viridisLite_0.4.3 mgcv_1.9-3
## [52] rlang_1.1.7 glue_1.8.0 xml2_1.5.2
## [55] svglite_2.2.2 rstudioapi_0.18.0 jsonlite_2.0.0
## [58] R6_2.6.1 systemfonts_1.3.1
LS0tCnRpdGxlOiAiQWN0aXZpZGFkIDIg4oCUIFJlZ3Jlc2nDs24gbGluZWFsIG3Dumx0aXBsZTogQ2FzbyBDJkEiCmF1dGhvcjogIkl2w6FuIERhcsOtbyBHYXJjw61hIFJhbW9zIgpkYXRlOiAiMDYgZGUgc2VwdGllbWJyZSBkZSAyMDI2IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBmaWdfd2lkdGg6IDkKICAgIGZpZ19oZWlnaHQ6IDUuNQogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKPHN0eWxlPgpib2R5IHsgZm9udC1zaXplOiAxNXB4OyBsaW5lLWhlaWdodDogMS42MDsgfQpoMSwgaDIsIGgzIHsgY29sb3I6ICMwQjRGOUM7IGZvbnQtd2VpZ2h0OiA3MDA7IH0KaDQgeyBjb2xvcjogI0U4NzUwMDsgZm9udC13ZWlnaHQ6IDcwMDsgfQojaGVhZGVyIHsgYm9yZGVyLWJvdHRvbTogNHB4IHNvbGlkICMwQjRGOUM7IHBhZGRpbmctYm90dG9tOiAxNHB4OyBtYXJnaW4tYm90dG9tOiAyOHB4OyB9CiNUT0MgeyBsZWZ0OiBhdXRvICFpbXBvcnRhbnQ7IHJpZ2h0OiAxOHB4ICFpbXBvcnRhbnQ7IH0KLm1haW4tY29udGFpbmVyIHsgbWF4LXdpZHRoOiAxMTgwcHggIWltcG9ydGFudDsgbWFyZ2luLWxlZnQ6IDM2cHggIWltcG9ydGFudDsgbWFyZ2luLXJpZ2h0OiAzMTBweCAhaW1wb3J0YW50OyB9Ci5jYWxsb3V0IHsgYmFja2dyb3VuZDogI2Y0ZjhmYzsgYm9yZGVyLWxlZnQ6IDVweCBzb2xpZCAjMEI0RjlDOyBwYWRkaW5nOiAxNHB4IDE4cHg7IG1hcmdpbjogMThweCAwOyBib3JkZXItcmFkaXVzOiA0cHg7IH0KLmNhbGxvdXQtb3JhbmdlIHsgYmFja2dyb3VuZDogI2ZmZjhmMDsgYm9yZGVyLWxlZnQ6IDVweCBzb2xpZCAjRTg3NTAwOyBwYWRkaW5nOiAxNHB4IDE4cHg7IG1hcmdpbjogMThweCAwOyBib3JkZXItcmFkaXVzOiA0cHg7IH0KLnNtYWxsLW5vdGUgeyBmb250LXNpemU6IDAuOTJlbTsgY29sb3I6ICM1NTU7IH0KdGFibGUgeyBmb250LXNpemU6IDAuOTNlbTsgfQpAbWVkaWEgKG1heC13aWR0aDogMTIwMHB4KSB7CiAgLm1haW4tY29udGFpbmVyIHsgbWFyZ2luLWxlZnQ6IGF1dG8gIWltcG9ydGFudDsgbWFyZ2luLXJpZ2h0OiBhdXRvICFpbXBvcnRhbnQ7IG1heC13aWR0aDogOTQwcHggIWltcG9ydGFudDsgfQogICNUT0MgeyBwb3NpdGlvbjogcmVsYXRpdmUgIWltcG9ydGFudDsgcmlnaHQ6IGF1dG8gIWltcG9ydGFudDsgfQp9Cjwvc3R5bGU+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLAogIHdhcm5pbmcgPSBGQUxTRSwKICBtZXNzYWdlID0gRkFMU0UsCiAgZmlnLmFsaWduID0gImNlbnRlciIsCiAgb3V0LndpZHRoID0gIjEwMCUiCikKb3B0aW9ucyhzY2lwZW4gPSA5OTksIGRpZ2l0cyA9IDQpCnNldC5zZWVkKDkwNDQ5NTEpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgSW5zdGFsYWNpw7NuL2NhcmdhIHJvYnVzdGEgZGUgcGFxdWV0ZXMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpwYXF1ZXRlcyA8LSBjKAogICJkcGx5ciIsICJ0aWR5ciIsICJnZ3Bsb3QyIiwgInBsb3RseSIsICJsZWFmbGV0IiwgImtuaXRyIiwKICAia2FibGVFeHRyYSIsICJicm9vbSIsICJsbXRlc3QiLCAic2FuZHdpY2giLCAic2NhbGVzIiwKICAic3RyaW5nciIsICJwdXJyciIsICJodG1sdG9vbHMiCikKCmZhbHRhbnRlc19wa2cgPC0gcGFxdWV0ZXNbIXZhcHBseShwYXF1ZXRlcywgcmVxdWlyZU5hbWVzcGFjZSwgbG9naWNhbCgxKSwgcXVpZXRseSA9IFRSVUUpXQppZiAobGVuZ3RoKGZhbHRhbnRlc19wa2cpID4gMCkgewogIGluc3RhbGwucGFja2FnZXMoZmFsdGFudGVzX3BrZywgcmVwb3MgPSAiaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnIikKfQoKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJwYXF1ZXRlTU9ERUxPUyIsIHF1aWV0bHkgPSBUUlVFKSkgewogIGlmICghcmVxdWlyZU5hbWVzcGFjZSgicmVtb3RlcyIsIHF1aWV0bHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcygicmVtb3RlcyIsIHJlcG9zID0gImh0dHBzOi8vY2xvdWQuci1wcm9qZWN0Lm9yZyIpCiAgfQogIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJDZW50cm9tYWdpcy9wYXF1ZXRlTU9ERUxPUyIsIHVwZ3JhZGUgPSAibmV2ZXIiKQp9CgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGxlYWZsZXQpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShicm9vbSkKbGlicmFyeShsbXRlc3QpCmxpYnJhcnkoc2FuZHdpY2gpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkoaHRtbHRvb2xzKQoKZGF0YSgidml2aWVuZGEiLCBwYWNrYWdlID0gInBhcXVldGVNT0RFTE9TIikKdml2aWVuZGFfcmF3IDwtIGFzLmRhdGEuZnJhbWUodml2aWVuZGEpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgRnVuY2lvbmVzIGF1eGlsaWFyZXMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpmbXRfbnVtIDwtIGZ1bmN0aW9uKHgsIGQgPSAyKSBmb3JtYXQocm91bmQoeCwgZCksIGJpZy5tYXJrID0gIi4iLCBkZWNpbWFsLm1hcmsgPSAiLCIsIG5zbWFsbCA9IGQpCmZtdF9taWxsIDwtIGZ1bmN0aW9uKHgsIGQgPSAxKSBwYXN0ZTAoIiQiLCBmbXRfbnVtKHgsIGQpLCAiIG1pbGxvbmVzIikKZm10X3BjdCA8LSBmdW5jdGlvbih4LCBkID0gMSkgcGFzdGUwKGZtdF9udW0oMTAwICogeCwgZCksICIlIikKZm10X3AgPC0gZnVuY3Rpb24ocCkgewogIGlmZWxzZShpcy5uYShwKSwgIk5BIiwgaWZlbHNlKHAgPCAwLjAwMSwgIjwgMCwwMDEiLCBzdWIoIlxcLiIsICIsIiwgc3ByaW50ZigiJS4zZiIsIHApKSkpCn0KCmtibF9wcm9mIDwtIGZ1bmN0aW9uKHgsIGNhcHRpb24gPSBOVUxMLCBkaWdpdHMgPSAyKSB7CiAga25pdHI6OmthYmxlKHgsIGNhcHRpb24gPSBjYXB0aW9uLCBkaWdpdHMgPSBkaWdpdHMsIGFsaWduID0gImMiKSB8PgogICAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygKICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksCiAgICAgIGZ1bGxfd2lkdGggPSBUUlVFLAogICAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgICApCn0KCmNsYXNpZmljYV9jb3IgPC0gZnVuY3Rpb24ocikgewogIGEgPC0gYWJzKHIpCiAgZHBseXI6OmNhc2Vfd2hlbigKICAgIGEgPj0gMC43MCB+ICJmdWVydGUiLAogICAgYSA+PSAwLjQwIH4gIm1vZGVyYWRhIiwKICAgIGEgPj0gMC4yMCB+ICJkw6liaWwiLAogICAgVFJVRSB+ICJtdXkgZMOpYmlsIgogICkKfQoKdmlmX21hbnVhbCA8LSBmdW5jdGlvbihtb2RlbG8pIHsKICBYIDwtIG1vZGVsLmZyYW1lKG1vZGVsbylbLCAtMSwgZHJvcCA9IEZBTFNFXQogIGlmIChuY29sKFgpIDwgMikgcmV0dXJuKGRhdGEuZnJhbWUodmFyaWFibGUgPSBuYW1lcyhYKSwgVklGID0gMSkpCiAgb3V0IDwtIHNhcHBseShuYW1lcyhYKSwgZnVuY3Rpb24odikgewogICAgb3RyYXMgPC0gc2V0ZGlmZihuYW1lcyhYKSwgdikKICAgIGF1eCA8LSBsbShyZWZvcm11bGF0ZShvdHJhcywgcmVzcG9uc2UgPSB2KSwgZGF0YSA9IFgpCiAgICAxIC8gKDEgLSBzdW1tYXJ5KGF1eCkkci5zcXVhcmVkKQogIH0pCiAgZGF0YS5mcmFtZSh2YXJpYWJsZSA9IG5hbWVzKG91dCksIFZJRiA9IGFzLm51bWVyaWMob3V0KSwgcm93Lm5hbWVzID0gTlVMTCkKfQoKY3ZfbG0gPC0gZnVuY3Rpb24oZm9ybXVsYSwgZGF0YSwgayA9IDEwLCBzZWVkID0gOTA0NDk1MSkgewogIHNldC5zZWVkKHNlZWQpCiAgbiA8LSBucm93KGRhdGEpCiAgZm9sZCA8LSBzYW1wbGUocmVwKHNlcV9sZW4oayksIGxlbmd0aC5vdXQgPSBuKSkKICBwcmVkIDwtIHJlcChOQV9yZWFsXywgbikKICB5IDwtIGRhdGFbW2FsbC52YXJzKGZvcm11bGEpWzFdXV0KICBmb3IgKGYgaW4gc2VxX2xlbihrKSkgewogICAgZml0IDwtIGxtKGZvcm11bGEsIGRhdGEgPSBkYXRhW2ZvbGQgIT0gZiwgLCBkcm9wID0gRkFMU0VdKQogICAgcHJlZFtmb2xkID09IGZdIDwtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gZGF0YVtmb2xkID09IGYsICwgZHJvcCA9IEZBTFNFXSkKICB9CiAgZGF0YS5mcmFtZSgKICAgIFJNU0VfQ1YgPSBzcXJ0KG1lYW4oKHkgLSBwcmVkKV4yLCBuYS5ybSA9IFRSVUUpKSwKICAgIE1BRV9DViA9IG1lYW4oYWJzKHkgLSBwcmVkKSwgbmEucm0gPSBUUlVFKSwKICAgIFIyX0NWID0gMSAtIHN1bSgoeSAtIHByZWQpXjIsIG5hLnJtID0gVFJVRSkgLyBzdW0oKHkgLSBtZWFuKHksIG5hLnJtID0gVFJVRSkpXjIsIG5hLnJtID0gVFJVRSkKICApCn0KCmNvbXBhcmFyX21vZGVsb3MgPC0gZnVuY3Rpb24obW9kZWxvcywgZGF0YSkgewogIHB1cnJyOjppbWFwX2Rmcihtb2RlbG9zLCBmdW5jdGlvbihtLCBubSkgewogICAgZyA8LSBicm9vbTo6Z2xhbmNlKG0pCiAgICBjdiA8LSBjdl9sbShmb3JtdWxhKG0pLCBkYXRhKQogICAgZGF0YS5mcmFtZSgKICAgICAgTW9kZWxvID0gbm0sCiAgICAgIFIyID0gZyRyLnNxdWFyZWQsCiAgICAgIFIyX2FqdXN0YWRvID0gZyRhZGouci5zcXVhcmVkLAogICAgICBBSUMgPSBBSUMobSksCiAgICAgIEJJQyA9IEJJQyhtKSwKICAgICAgUk1TRV9DViA9IGN2JFJNU0VfQ1YsCiAgICAgIE1BRV9DViA9IGN2JE1BRV9DViwKICAgICAgUjJfQ1YgPSBjdiRSMl9DVgogICAgKQogIH0pCn0KCmRpYWdub3N0aWNhcl9tb2RlbG8gPC0gZnVuY3Rpb24obW9kZWxvKSB7CiAgcmVzIDwtIHJlc2lkdWFscyhtb2RlbG8pCiAgbiA8LSBsZW5ndGgocmVzKQogIGJwIDwtIGxtdGVzdDo6YnB0ZXN0KG1vZGVsbykKICBkdyA8LSBsbXRlc3Q6OmR3dGVzdChtb2RlbG8pCiAgcmVzZXQgPC0gbG10ZXN0OjpyZXNldHRlc3QobW9kZWxvLCBwb3dlciA9IDI6MywgdHlwZSA9ICJmaXR0ZWQiKQogIHNldC5zZWVkKDkwNDQ5NTEpCiAgcmVzX3NoIDwtIGlmIChuID4gNTAwMCkgc2FtcGxlKHJlcywgNTAwMCkgZWxzZSByZXMKICBzaCA8LSBzaGFwaXJvLnRlc3QocmVzX3NoKQogIHZpZl90IDwtIHZpZl9tYW51YWwobW9kZWxvKQogIGNvb2sgPC0gY29va3MuZGlzdGFuY2UobW9kZWxvKQogIGNvb2tfdGhyIDwtIDQgLyBuCgogIGRhdGEuZnJhbWUoCiAgICBTdXB1ZXN0byA9IGMoCiAgICAgICJMaW5lYWxpZGFkIC8gZXNwZWNpZmljYWNpw7NuIiwKICAgICAgIkhvbW9zY2VkYXN0aWNpZGFkIiwKICAgICAgIkluZGVwZW5kZW5jaWEgc2VjdWVuY2lhbCoiLAogICAgICAiTm9ybWFsaWRhZCBkZSByZXNpZHVvcyIsCiAgICAgICJNdWx0aWNvbGluZWFsaWRhZCIsCiAgICAgICJJbmZsdWVuY2lhIgogICAgKSwKICAgIERpYWdub3N0aWNvID0gYygKICAgICAgcGFzdGUwKCJSRVNFVDogcCA9ICIsIGZtdF9wKHJlc2V0JHAudmFsdWUpKSwKICAgICAgcGFzdGUwKCJCcmV1c2No4oCTUGFnYW46IHAgPSAiLCBmbXRfcChicCRwLnZhbHVlKSksCiAgICAgIHBhc3RlMCgiRHVyYmlu4oCTV2F0c29uID0gIiwgZm10X251bSh1bm5hbWUoZHckc3RhdGlzdGljKSwgMiksICI7IHAgPSAiLCBmbXRfcChkdyRwLnZhbHVlKSksCiAgICAgIHBhc3RlMCgiU2hhcGlyb+KAk1dpbGs6IHAgPSAiLCBmbXRfcChzaCRwLnZhbHVlKSksCiAgICAgIHBhc3RlMCgiVklGIG3DoXhpbW8gPSAiLCBmbXRfbnVtKG1heCh2aWZfdCRWSUYsIG5hLnJtID0gVFJVRSksIDIpKSwKICAgICAgcGFzdGUwKHN1bShjb29rID4gY29va190aHIsIG5hLnJtID0gVFJVRSksICIgb2JzZXJ2YWNpb25lcyBjb24gQ29vayA+IDQvbiIpCiAgICApLAogICAgTGVjdHVyYSA9IGMoCiAgICAgIGlmZWxzZShyZXNldCRwLnZhbHVlID49IDAuMDUsICJTaW4gZXZpZGVuY2lhIGVzdGFkw61zdGljYSBmdWVydGUgZGUgbWFsYSBlc3BlY2lmaWNhY2nDs24gZnVuY2lvbmFsLiIsICJIYXkgZXZpZGVuY2lhIGRlIHBvc2libGUgbm8gbGluZWFsaWRhZCBvIHTDqXJtaW5vcyBvbWl0aWRvcy4iKSwKICAgICAgaWZlbHNlKGJwJHAudmFsdWUgPj0gMC4wNSwgIk5vIHNlIHJlY2hhemEgdmFyaWFuemEgY29uc3RhbnRlLiIsICJTZSBkZXRlY3RhIGhldGVyb2NlZGFzdGljaWRhZDsgY29udmllbmUgY29udHJhc3RhciBjb24gZXJyb3JlcyByb2J1c3Rvcy90cmFuc2Zvcm1hY2lvbmVzLiIpLAogICAgICBpZmVsc2UoZHckcC52YWx1ZSA+PSAwLjA1LCAiTm8gc2UgZGV0ZWN0YSBwYXRyw7NuIHNlY3VlbmNpYWwgZnVlcnRlLiIsICJTZSBkZXRlY3RhIHBhdHLDs24gc2VjdWVuY2lhbDsgZW4gZGF0b3MgdHJhbnN2ZXJzYWxlcyBkZWJlIGludGVycHJldGFyc2UgY29uIGNhdXRlbGEuIiksCiAgICAgIGlmZWxzZShzaCRwLnZhbHVlID49IDAuMDUsICJObyBzZSByZWNoYXphIG5vcm1hbGlkYWQuIiwgIlNlIHJlY2hhemEgbm9ybWFsaWRhZDsgcmV2aXNhciBRLVEgeSBjb2xhcywgZXNwZWNpYWxtZW50ZSBwYXJhIGludGVydmFsb3MuIiksCiAgICAgIGlmZWxzZShtYXgodmlmX3QkVklGLCBuYS5ybSA9IFRSVUUpIDwgNSwgIk5vIGhheSBtdWx0aWNvbGluZWFsaWRhZCBzZXZlcmEgYmFqbyBlbCB1bWJyYWwgVklGIDwgNS4iLCAiRXhpc3RlIG11bHRpY29saW5lYWxpZGFkIHJlbGV2YW50ZTsgcmV2aXNhciByZWR1bmRhbmNpYSBlbnRyZSBwcmVkaWN0b3Jlcy4iKSwKICAgICAgaWZlbHNlKHN1bShjb29rID4gY29va190aHIsIG5hLnJtID0gVFJVRSkgPT0gMCwgIk5vIHNlIGlkZW50aWZpY2FuIGNhc29zIGVzcGVjaWFsbWVudGUgaW5mbHV5ZW50ZXMgY29uIGVsIGNyaXRlcmlvIDQvbi4iLCAiRXhpc3RlbiBjYXNvcyBpbmZsdXllbnRlcyBxdWUgZGViZW4gcmV2aXNhcnNlIGFudGVzIGRlIGRlY2lzaW9uZXMgZGUgYWx0byBpbXBhY3RvLiIpCiAgICApCiAgKQp9CgpiYW5kZXJhX2dlbyA8LSBmdW5jdGlvbihkZikgewogIGcgPC0gZGYgfD4KICAgIGZpbHRlcihpcy5maW5pdGUobGF0aXR1ZCksIGlzLmZpbml0ZShsb25naXR1ZCkpCiAgaWYgKG5yb3coZykgPCAxMCkgcmV0dXJuKGcgfD4gbXV0YXRlKGdlb19hdGlwaWNvID0gRkFMU0UpKQogIHFsYXQgPC0gcXVhbnRpbGUoZyRsYXRpdHVkLCBjKC4yNSwgLjc1KSwgbmEucm0gPSBUUlVFKQogIHFsb24gPC0gcXVhbnRpbGUoZyRsb25naXR1ZCwgYyguMjUsIC43NSksIG5hLnJtID0gVFJVRSkKICBpbGF0IDwtIElRUihnJGxhdGl0dWQsIG5hLnJtID0gVFJVRSkKICBpbG9uIDwtIElRUihnJGxvbmdpdHVkLCBuYS5ybSA9IFRSVUUpCiAgZyB8PgogICAgbXV0YXRlKAogICAgICBnZW9fYXRpcGljbyA9IGxhdGl0dWQgPCBxbGF0WzFdIC0gMS41ICogaWxhdCB8CiAgICAgICAgbGF0aXR1ZCA+IHFsYXRbMl0gKyAxLjUgKiBpbGF0IHwKICAgICAgICBsb25naXR1ZCA8IHFsb25bMV0gLSAxLjUgKiBpbG9uIHwKICAgICAgICBsb25naXR1ZCA+IHFsb25bMl0gKyAxLjUgKiBpbG9uCiAgICApCn0KCmNvcl9wcmVjaW8gPC0gZnVuY3Rpb24oZGF0YSkgewogIHZhcnMgPC0gYygiYXJlYWNvbnN0IiwgImVzdHJhdG8iLCAicGFycXVlYWRlcm9zIiwgImJhbmlvcyIsICJoYWJpdGFjaW9uZXMiKQogIHB1cnJyOjptYXBfZGZyKHZhcnMsIGZ1bmN0aW9uKHYpIHsKICAgIGRhdGEuZnJhbWUoCiAgICAgIFZhcmlhYmxlID0gdiwKICAgICAgUGVhcnNvbiA9IGNvcihkYXRhJHByZWNpb20sIGRhdGFbW3ZdXSwgdXNlID0gImNvbXBsZXRlLm9icyIsIG1ldGhvZCA9ICJwZWFyc29uIiksCiAgICAgIFNwZWFybWFuID0gY29yKGRhdGEkcHJlY2lvbSwgZGF0YVtbdl1dLCB1c2UgPSAiY29tcGxldGUub2JzIiwgbWV0aG9kID0gInNwZWFybWFuIikKICAgICkKICB9KSB8PgogICAgbXV0YXRlKAogICAgICBJbnRlbnNpZGFkID0gdmFwcGx5KFBlYXJzb24sIGNsYXNpZmljYV9jb3IsIGNoYXJhY3RlcigxKSksCiAgICAgIERpcmVjY2lvbiA9IGlmZWxzZShQZWFyc29uID49IDAsICJwb3NpdGl2YSIsICJuZWdhdGl2YSIpCiAgICApIHw+CiAgICBhcnJhbmdlKGRlc2MoYWJzKFBlYXJzb24pKSkKfQoKaW50ZXJwcmV0YXJfY29lZiA8LSBmdW5jdGlvbihtb2RlbG8pIHsKICBkaWMgPC0gYygKICAgIGFyZWFjb25zdCA9ICLDoXJlYSBjb25zdHJ1aWRhIChtwrIpIiwKICAgIGVzdHJhdG8gPSAiZXN0cmF0byIsCiAgICBoYWJpdGFjaW9uZXMgPSAiaGFiaXRhY2lvbmVzIiwKICAgIHBhcnF1ZWFkZXJvcyA9ICJwYXJxdWVhZGVyb3MiLAogICAgYmFuaW9zID0gImJhw7FvcyIKICApCiAgYnJvb206OnRpZHkobW9kZWxvLCBjb25mLmludCA9IFRSVUUpIHw+CiAgICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSB8PgogICAgbXV0YXRlKAogICAgICBWYXJpYWJsZSA9IHVubmFtZShkaWNbdGVybV0pLAogICAgICBTaWduaWZpY2F0aXZvXzVwY3QgPSBpZmVsc2UocC52YWx1ZSA8IDAuMDUsICJTw60iLCAiTm8iKSwKICAgICAgSW50ZXJwcmV0YWNpb24gPSBwYXN0ZTAoCiAgICAgICAgIk1hbnRlbmllbmRvIGNvbnN0YW50ZXMgbGFzIGRlbcOhcyB2YXJpYWJsZXMsIHVuYSB1bmlkYWQgYWRpY2lvbmFsIGVuICIsIFZhcmlhYmxlLAogICAgICAgICIgc2UgYXNvY2lhIGNvbiAiLCBmbXRfbWlsbChhYnMoZXN0aW1hdGUpLCAyKSwKICAgICAgICBpZmVsc2UoZXN0aW1hdGUgPj0gMCwgIiBtw6FzIiwgIiBtZW5vcyIpLAogICAgICAgICIgZW4gZWwgcHJlY2lvIGVzcGVyYWRvLiAiLAogICAgICAgIGlmZWxzZShwLnZhbHVlIDwgMC4wNSwgIkVsIGVmZWN0byBlcyBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZvIGFsIDUlLiIsICJObyBoYXkgZXZpZGVuY2lhIHN1ZmljaWVudGUgZGUgdW4gZWZlY3RvIGRpc3RpbnRvIGRlIGNlcm8gYWwgNSUuIikKICAgICAgKQogICAgKSB8PgogICAgc2VsZWN0KFZhcmlhYmxlLCBlc3RpbWF0ZSwgc3RkLmVycm9yLCBjb25mLmxvdywgY29uZi5oaWdoLCBwLnZhbHVlLCBTaWduaWZpY2F0aXZvXzVwY3QsIEludGVycHJldGFjaW9uKQp9CgpzZWxlY2Npb25hcl9vZmVydGFzIDwtIGZ1bmN0aW9uKGRmLCB0YXJnZXQsIHByZXN1cHVlc3RvLCBlc3RyYXRvcywgbW9kZWxvLCBuID0gNSkgewogIGNhbmQgPC0gZGYgfD4KICAgIGZpbHRlcigKICAgICAgZXN0cmF0byAlaW4lIGVzdHJhdG9zLAogICAgICBwcmVjaW9tIDw9IHByZXN1cHVlc3RvLAogICAgICAhaXMubmEoYXJlYWNvbnN0KSwgIWlzLm5hKHBhcnF1ZWFkZXJvcyksICFpcy5uYShiYW5pb3MpLCAhaXMubmEoaGFiaXRhY2lvbmVzKQogICAgKSB8PgogICAgbXV0YXRlKAogICAgICBzY29yZV9wZXJmaWwgPQogICAgICAgIDAuNDAgKiBhYnMoYXJlYWNvbnN0IC0gdGFyZ2V0JGFyZWFjb25zdCkgLyBtYXgodGFyZ2V0JGFyZWFjb25zdCwgMSkgKwogICAgICAgIDAuMjAgKiBhYnMocGFycXVlYWRlcm9zIC0gdGFyZ2V0JHBhcnF1ZWFkZXJvcykgLyBtYXgodGFyZ2V0JHBhcnF1ZWFkZXJvcywgMSkgKwogICAgICAgIDAuMjAgKiBhYnMoYmFuaW9zIC0gdGFyZ2V0JGJhbmlvcykgLyBtYXgodGFyZ2V0JGJhbmlvcywgMSkgKwogICAgICAgIDAuMjAgKiBhYnMoaGFiaXRhY2lvbmVzIC0gdGFyZ2V0JGhhYml0YWNpb25lcykgLyBtYXgodGFyZ2V0JGhhYml0YWNpb25lcywgMSkKICAgICkKCiAgaWYgKG5yb3coY2FuZCkgPCBuKSB7CiAgICBjYW5kIDwtIGRmIHw+CiAgICAgIGZpbHRlcigKICAgICAgICBwcmVjaW9tIDw9IHByZXN1cHVlc3RvLAogICAgICAgICFpcy5uYShlc3RyYXRvKSwgIWlzLm5hKGFyZWFjb25zdCksICFpcy5uYShwYXJxdWVhZGVyb3MpLAogICAgICAgICFpcy5uYShiYW5pb3MpLCAhaXMubmEoaGFiaXRhY2lvbmVzKQogICAgICApIHw+CiAgICAgIG11dGF0ZSgKICAgICAgICBzY29yZV9wZXJmaWwgPQogICAgICAgICAgMC40MCAqIGFicyhhcmVhY29uc3QgLSB0YXJnZXQkYXJlYWNvbnN0KSAvIG1heCh0YXJnZXQkYXJlYWNvbnN0LCAxKSArCiAgICAgICAgICAwLjIwICogYWJzKHBhcnF1ZWFkZXJvcyAtIHRhcmdldCRwYXJxdWVhZGVyb3MpIC8gbWF4KHRhcmdldCRwYXJxdWVhZGVyb3MsIDEpICsKICAgICAgICAgIDAuMjAgKiBhYnMoYmFuaW9zIC0gdGFyZ2V0JGJhbmlvcykgLyBtYXgodGFyZ2V0JGJhbmlvcywgMSkgKwogICAgICAgICAgMC4yMCAqIGFicyhoYWJpdGFjaW9uZXMgLSB0YXJnZXQkaGFiaXRhY2lvbmVzKSAvIG1heCh0YXJnZXQkaGFiaXRhY2lvbmVzLCAxKSArCiAgICAgICAgICAwLjUwICogaWZlbHNlKGVzdHJhdG8gJWluJSBlc3RyYXRvcywgMCwgMSkKICAgICAgKQogIH0KCiAgY2FuZCRwcmVjaW9fbW9kZWxvIDwtIGFzLm51bWVyaWMocHJlZGljdChtb2RlbG8sIG5ld2RhdGEgPSBjYW5kKSkKICBjYW5kIDwtIGNhbmQgfD4KICAgIG11dGF0ZSgKICAgICAgZ2FwX21vZGVsbyA9IHByZWNpb19tb2RlbG8gLSBwcmVjaW9tLAogICAgICBtYXJnZW5fY3JlZGl0byA9IHByZXN1cHVlc3RvIC0gcHJlY2lvbQogICAgKSB8PgogICAgYXJyYW5nZShzY29yZV9wZXJmaWwsIGRlc2MoZ2FwX21vZGVsbyksIHByZWNpb20pCgogIGNhbmQgfD4gc2xpY2VfaGVhZChuID0gbikgfD4gbXV0YXRlKFJhbmtpbmcgPSByb3dfbnVtYmVyKCkpCn0KCm1hcGFfc2VnbWVudG8gPC0gZnVuY3Rpb24oZGYsIHRpdHVsbykgewogIGdlbyA8LSBiYW5kZXJhX2dlbyhkZikKICBsZWFmbGV0KGdlbykgfD4KICAgIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb24pIHw+CiAgICBhZGRDaXJjbGVNYXJrZXJzKAogICAgICBsbmcgPSB+bG9uZ2l0dWQsIGxhdCA9IH5sYXRpdHVkLAogICAgICByYWRpdXMgPSA0LAogICAgICBjb2xvciA9IH5pZmVsc2UoZ2VvX2F0aXBpY28sICIjRTg3NTAwIiwgIiMwQjRGOUMiKSwKICAgICAgc3Ryb2tlID0gRkFMU0UsIGZpbGxPcGFjaXR5ID0gMC42NSwKICAgICAgY2x1c3Rlck9wdGlvbnMgPSBtYXJrZXJDbHVzdGVyT3B0aW9ucygpLAogICAgICBwb3B1cCA9IH5wYXN0ZTAoCiAgICAgICAgIjxiPklEOjwvYj4gIiwgaWQsCiAgICAgICAgIjxicj48Yj5CYXJyaW86PC9iPiAiLCBodG1sRXNjYXBlKGJhcnJpbyksCiAgICAgICAgIjxicj48Yj5Fc3RyYXRvOjwvYj4gIiwgZXN0cmF0bywKICAgICAgICAiPGJyPjxiPlByZWNpbzo8L2I+ICQiLCBwcmVjaW9tLCAiIE0iLAogICAgICAgICI8YnI+PGI+w4FyZWE6PC9iPiAiLCBhcmVhY29uc3QsICIgbcKyIiwKICAgICAgICAiPGJyPjxiPkF0w61waWNvIGdlb2dyw6FmaWNvIHJvYnVzdG86PC9iPiAiLCBpZmVsc2UoZ2VvX2F0aXBpY28sICJTw60iLCAiTm8iKQogICAgICApCiAgICApIHw+CiAgICBhZGRMZWdlbmQoCiAgICAgIHBvc2l0aW9uID0gImJvdHRvbXJpZ2h0IiwKICAgICAgY29sb3JzID0gYygiIzBCNEY5QyIsICIjRTg3NTAwIiksCiAgICAgIGxhYmVscyA9IGMoIlBhdHLDs24gZ2VvZ3LDoWZpY28gZGVsIHNlZ21lbnRvIiwgIkF0w61waWNvIGdlb2dyw6FmaWNvIHJvYnVzdG8iKSwKICAgICAgdGl0bGUgPSB0aXR1bG8KICAgICkKfQoKbWFwYV9vZmVydGFzIDwtIGZ1bmN0aW9uKGRmLCB0aXR1bG8pIHsKICBwYWwgPC0gY29sb3JGYWN0b3IoIlNldDIiLCBkb21haW4gPSBhcy5mYWN0b3IoZGYkUmFua2luZykpCiAgbGVhZmxldChkZiB8PiBmaWx0ZXIoaXMuZmluaXRlKGxhdGl0dWQpLCBpcy5maW5pdGUobG9uZ2l0dWQpKSkgfD4KICAgIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb24pIHw+CiAgICBhZGRDaXJjbGVNYXJrZXJzKAogICAgICBsbmcgPSB+bG9uZ2l0dWQsIGxhdCA9IH5sYXRpdHVkLAogICAgICByYWRpdXMgPSA5LAogICAgICBjb2xvciA9IH5wYWwoYXMuZmFjdG9yKFJhbmtpbmcpKSwKICAgICAgZmlsbE9wYWNpdHkgPSAwLjg1LAogICAgICBwb3B1cCA9IH5wYXN0ZTAoCiAgICAgICAgIjxiPk9wY2nDs24gIyIsIFJhbmtpbmcsICI8L2I+IiwKICAgICAgICAiPGJyPjxiPklEOjwvYj4gIiwgaWQsCiAgICAgICAgIjxicj48Yj5CYXJyaW86PC9iPiAiLCBodG1sRXNjYXBlKGJhcnJpbyksCiAgICAgICAgIjxicj48Yj5QcmVjaW86PC9iPiAkIiwgcHJlY2lvbSwgIiBNIiwKICAgICAgICAiPGJyPjxiPsOBcmVhOjwvYj4gIiwgYXJlYWNvbnN0LCAiIG3CsiIsCiAgICAgICAgIjxicj48Yj5QYXJxdWVhZGVyb3M6PC9iPiAiLCBwYXJxdWVhZGVyb3MsCiAgICAgICAgIjxicj48Yj5CYcOxb3M6PC9iPiAiLCBiYW5pb3MsCiAgICAgICAgIjxicj48Yj5IYWJpdGFjaW9uZXM6PC9iPiAiLCBoYWJpdGFjaW9uZXMsCiAgICAgICAgIjxicj48Yj5Fc3RyYXRvOjwvYj4gIiwgZXN0cmF0bywKICAgICAgICAiPGJyPjxiPk1hcmdlbiBkZSBjcsOpZGl0bzo8L2I+ICQiLCByb3VuZChtYXJnZW5fY3JlZGl0bywgMSksICIgTSIKICAgICAgKQogICAgKSB8PgogICAgYWRkTGVnZW5kKCJib3R0b21yaWdodCIsIHBhbCA9IHBhbCwgdmFsdWVzID0gfmFzLmZhY3RvcihSYW5raW5nKSwgdGl0bGUgPSB0aXR1bG8pCn0KCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBMaW1waWV6YSBnZW5lcmFsIHkgY29udHJvbCBkZSBjYWxpZGFkCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZHVwbGljYWRvc19leGFjdG9zIDwtIHN1bShkdXBsaWNhdGVkKHZpdmllbmRhX3JhdykpCgp2aXZpZW5kYSA8LSB2aXZpZW5kYV9yYXcgfD4KICBtdXRhdGUoCiAgICB6b25hID0gc3RyX3NxdWlzaCh6b25hKSwKICAgIHRpcG8gPSBzdHJfc3F1aXNoKHRpcG8pLAogICAgYmFycmlvID0gc3RyX3NxdWlzaChiYXJyaW8pCiAgKSB8PgogIGRpc3RpbmN0KCkKCnZhcnNfbW9kZWxvIDwtIGMoInByZWNpb20iLCAiYXJlYWNvbnN0IiwgImVzdHJhdG8iLCAicGFycXVlYWRlcm9zIiwgImJhbmlvcyIsICJoYWJpdGFjaW9uZXMiKQoKdGFibGFfbmEgPC0gZGF0YS5mcmFtZSgKICBWYXJpYWJsZSA9IG5hbWVzKHZpdmllbmRhKSwKICBGYWx0YW50ZXMgPSBzYXBwbHkodml2aWVuZGEsIGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCikKCnRhYmxhX2NhbGlkYWQgPC0gZGF0YS5mcmFtZSgKICBJbmRpY2Fkb3IgPSBjKAogICAgIlJlZ2lzdHJvcyBvcmlnaW5hbGVzIiwgIlJlZ2lzdHJvcyB0cmFzIHJldGlyYXIgZHVwbGljYWRvcyBleGFjdG9zIiwKICAgICJEdXBsaWNhZG9zIGV4YWN0b3MgZGV0ZWN0YWRvcyIsICJWYXJpYWJsZXMiLCAiUHJlY2lvcyBubyBwb3NpdGl2b3MiLAogICAgIsOBcmVhcyBubyBwb3NpdGl2YXMiLCAiQ29vcmRlbmFkYXMgZmFsdGFudGVzIgogICksCiAgVmFsb3IgPSBjKAogICAgbnJvdyh2aXZpZW5kYV9yYXcpLCBucm93KHZpdmllbmRhKSwgZHVwbGljYWRvc19leGFjdG9zLCBuY29sKHZpdmllbmRhKSwKICAgIHN1bSh2aXZpZW5kYSRwcmVjaW9tIDw9IDAsIG5hLnJtID0gVFJVRSksCiAgICBzdW0odml2aWVuZGEkYXJlYWNvbnN0IDw9IDAsIG5hLnJtID0gVFJVRSksCiAgICBzdW0oIWlzLmZpbml0ZSh2aXZpZW5kYSRsYXRpdHVkKSB8ICFpcy5maW5pdGUodml2aWVuZGEkbG9uZ2l0dWQpKQogICkKKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENhc28gMTogQ2FzYSwgWm9uYSBOb3J0ZQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmJhc2UxIDwtIHZpdmllbmRhIHw+CiAgZmlsdGVyKHRpcG8gPT0gIkNhc2EiLCB6b25hID09ICJab25hIE5vcnRlIikKCmJhc2UxX21vZGVsIDwtIGJhc2UxIHw+CiAgc2VsZWN0KGFsbF9vZihjKHZhcnNfbW9kZWxvLCAiaWQiLCAiYmFycmlvIiwgImxhdGl0dWQiLCAibG9uZ2l0dWQiLCAiem9uYSIsICJ0aXBvIikpKSB8PgogIGRyb3BfbmEoYWxsX29mKHZhcnNfbW9kZWxvKSkKCmNvcjEgPC0gY29yX3ByZWNpbyhiYXNlMV9tb2RlbCkKY29yX21hdDEgPC0gY29yKGJhc2UxX21vZGVsWywgdmFyc19tb2RlbG9dLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikKCm0xX2FyZWEgPC0gbG0ocHJlY2lvbSB+IGFyZWFjb25zdCwgZGF0YSA9IGJhc2UxX21vZGVsKQptMV9mdWxsIDwtIGxtKHByZWNpb20gfiBhcmVhY29uc3QgKyBlc3RyYXRvICsgaGFiaXRhY2lvbmVzICsgcGFycXVlYWRlcm9zICsgYmFuaW9zLCBkYXRhID0gYmFzZTFfbW9kZWwpCm0xX3N0ZXAgPC0gc3RlcChtMV9mdWxsLCBkaXJlY3Rpb24gPSAiYmFja3dhcmQiLCB0cmFjZSA9IDApCmNvbXAxIDwtIGNvbXBhcmFyX21vZGVsb3MobGlzdCgiTTA6IHNvbG8gw6FyZWEiID0gbTFfYXJlYSwgIk0xOiBjb21wbGV0byByZXF1ZXJpZG8iID0gbTFfZnVsbCwgIk0yOiBwYXJzaW1vbmlvc28gQUlDIiA9IG0xX3N0ZXApLCBiYXNlMV9tb2RlbCkKCmJlc3QxX25hbWUgPC0gY29tcDEgfD4KICBmaWx0ZXIoTW9kZWxvICE9ICJNMDogc29sbyDDoXJlYSIpIHw+CiAgYXJyYW5nZShSTVNFX0NWLCBCSUMpIHw+CiAgc2xpY2UoMSkgfD4KICBwdWxsKE1vZGVsbykKCm0xX2ZpbmFsIDwtIGlmIChiZXN0MV9uYW1lID09ICJNMjogcGFyc2ltb25pb3NvIEFJQyIpIG0xX3N0ZXAgZWxzZSBtMV9mdWxsCmNvZWYxIDwtIGludGVycHJldGFyX2NvZWYobTFfZnVsbCkKZGlhZzEgPC0gZGlhZ25vc3RpY2FyX21vZGVsbyhtMV9maW5hbCkKdmlmMSA8LSB2aWZfbWFudWFsKG0xX2ZpbmFsKQoKc29sMSA8LSBkYXRhLmZyYW1lKAogIGFyZWFjb25zdCA9IGMoMjAwLCAyMDApLAogIGVzdHJhdG8gPSBjKDQsIDUpLAogIGhhYml0YWNpb25lcyA9IGMoNCwgNCksCiAgcGFycXVlYWRlcm9zID0gYygxLCAxKSwKICBiYW5pb3MgPSBjKDIsIDIpCikKcHJlZDFfcmF3IDwtIGFzLmRhdGEuZnJhbWUocHJlZGljdChtMV9maW5hbCwgbmV3ZGF0YSA9IHNvbDEsIGludGVydmFsID0gInByZWRpY3Rpb24iLCBsZXZlbCA9IDAuOTUpKQpwcmVkMSA8LSBiaW5kX2NvbHMoc29sMSwgcHJlZDFfcmF3KSB8PgogIG11dGF0ZSgKICAgIHByZXN1cHVlc3RvID0gMzUwLAogICAgbWFyZ2VuID0gcHJlc3VwdWVzdG8gLSBmaXQsCiAgICBkZWNpc2lvbl9wcmVzdXB1ZXN0byA9IGlmZWxzZShmaXQgPD0gcHJlc3VwdWVzdG8sICJFc3RpbWFjacOzbiBwdW50dWFsIGRlbnRybyBkZWwgY3LDqWRpdG8iLCAiRXN0aW1hY2nDs24gcHVudHVhbCBzdXBlcmEgZWwgY3LDqWRpdG8iKQogICkKCnRhcmdldDEgPC0gbGlzdChhcmVhY29uc3QgPSAyMDAsIHBhcnF1ZWFkZXJvcyA9IDEsIGJhbmlvcyA9IDIsIGhhYml0YWNpb25lcyA9IDQpCm9mZXJ0YXMxIDwtIHNlbGVjY2lvbmFyX29mZXJ0YXMoYmFzZTEsIHRhcmdldDEsIDM1MCwgYyg0LCA1KSwgbTFfZmluYWwsIDUpCmdlbzEgPC0gYmFuZGVyYV9nZW8oYmFzZTEpCgojIEVycm9yZXMgcm9idXN0b3MgSEMzIGNvbW8gYW7DoWxpc2lzIGRlIHNlbnNpYmlsaWRhZApyb2IxIDwtIGxtdGVzdDo6Y29lZnRlc3QobTFfZmluYWwsIHZjb3YuID0gc2FuZHdpY2g6OnZjb3ZIQyhtMV9maW5hbCwgdHlwZSA9ICJIQzMiKSkKcm9iMSA8LSBkYXRhLmZyYW1lKAogIFRlcm1pbm8gPSByb3duYW1lcyhyb2IxKSwKICBFc3RpbWFjaW9uID0gcm9iMVssIDFdLAogIEVFX0hDMyA9IHJvYjFbLCAyXSwKICB0ID0gcm9iMVssIDNdLAogIHBfSEMzID0gcm9iMVssIDRdLAogIHJvdy5uYW1lcyA9IE5VTEwKKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIENhc28gMjogQXBhcnRhbWVudG8sIFpvbmEgU3VyCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYmFzZTIgPC0gdml2aWVuZGEgfD4KICBmaWx0ZXIodGlwbyA9PSAiQXBhcnRhbWVudG8iLCB6b25hID09ICJab25hIFN1ciIpCgpiYXNlMl9tb2RlbCA8LSBiYXNlMiB8PgogIHNlbGVjdChhbGxfb2YoYyh2YXJzX21vZGVsbywgImlkIiwgImJhcnJpbyIsICJsYXRpdHVkIiwgImxvbmdpdHVkIiwgInpvbmEiLCAidGlwbyIpKSkgfD4KICBkcm9wX25hKGFsbF9vZih2YXJzX21vZGVsbykpCgpjb3IyIDwtIGNvcl9wcmVjaW8oYmFzZTJfbW9kZWwpCmNvcl9tYXQyIDwtIGNvcihiYXNlMl9tb2RlbFssIHZhcnNfbW9kZWxvXSwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCgptMl9hcmVhIDwtIGxtKHByZWNpb20gfiBhcmVhY29uc3QsIGRhdGEgPSBiYXNlMl9tb2RlbCkKbTJfZnVsbCA8LSBsbShwcmVjaW9tIH4gYXJlYWNvbnN0ICsgZXN0cmF0byArIGhhYml0YWNpb25lcyArIHBhcnF1ZWFkZXJvcyArIGJhbmlvcywgZGF0YSA9IGJhc2UyX21vZGVsKQptMl9zdGVwIDwtIHN0ZXAobTJfZnVsbCwgZGlyZWN0aW9uID0gImJhY2t3YXJkIiwgdHJhY2UgPSAwKQpjb21wMiA8LSBjb21wYXJhcl9tb2RlbG9zKGxpc3QoIk0wOiBzb2xvIMOhcmVhIiA9IG0yX2FyZWEsICJNMTogY29tcGxldG8gcmVxdWVyaWRvIiA9IG0yX2Z1bGwsICJNMjogcGFyc2ltb25pb3NvIEFJQyIgPSBtMl9zdGVwKSwgYmFzZTJfbW9kZWwpCgpiZXN0Ml9uYW1lIDwtIGNvbXAyIHw+CiAgZmlsdGVyKE1vZGVsbyAhPSAiTTA6IHNvbG8gw6FyZWEiKSB8PgogIGFycmFuZ2UoUk1TRV9DViwgQklDKSB8PgogIHNsaWNlKDEpIHw+CiAgcHVsbChNb2RlbG8pCgptMl9maW5hbCA8LSBpZiAoYmVzdDJfbmFtZSA9PSAiTTI6IHBhcnNpbW9uaW9zbyBBSUMiKSBtMl9zdGVwIGVsc2UgbTJfZnVsbApjb2VmMiA8LSBpbnRlcnByZXRhcl9jb2VmKG0yX2Z1bGwpCmRpYWcyIDwtIGRpYWdub3N0aWNhcl9tb2RlbG8obTJfZmluYWwpCnZpZjIgPC0gdmlmX21hbnVhbChtMl9maW5hbCkKCnNvbDIgPC0gZGF0YS5mcmFtZSgKICBhcmVhY29uc3QgPSBjKDMwMCwgMzAwKSwKICBlc3RyYXRvID0gYyg1LCA2KSwKICBoYWJpdGFjaW9uZXMgPSBjKDUsIDUpLAogIHBhcnF1ZWFkZXJvcyA9IGMoMywgMyksCiAgYmFuaW9zID0gYygzLCAzKQopCnByZWQyX3JhdyA8LSBhcy5kYXRhLmZyYW1lKHByZWRpY3QobTJfZmluYWwsIG5ld2RhdGEgPSBzb2wyLCBpbnRlcnZhbCA9ICJwcmVkaWN0aW9uIiwgbGV2ZWwgPSAwLjk1KSkKcHJlZDIgPC0gYmluZF9jb2xzKHNvbDIsIHByZWQyX3JhdykgfD4KICBtdXRhdGUoCiAgICBwcmVzdXB1ZXN0byA9IDg1MCwKICAgIG1hcmdlbiA9IHByZXN1cHVlc3RvIC0gZml0LAogICAgZGVjaXNpb25fcHJlc3VwdWVzdG8gPSBpZmVsc2UoZml0IDw9IHByZXN1cHVlc3RvLCAiRXN0aW1hY2nDs24gcHVudHVhbCBkZW50cm8gZGVsIGNyw6lkaXRvIiwgIkVzdGltYWNpw7NuIHB1bnR1YWwgc3VwZXJhIGVsIGNyw6lkaXRvIikKICApCgp0YXJnZXQyIDwtIGxpc3QoYXJlYWNvbnN0ID0gMzAwLCBwYXJxdWVhZGVyb3MgPSAzLCBiYW5pb3MgPSAzLCBoYWJpdGFjaW9uZXMgPSA1KQpvZmVydGFzMiA8LSBzZWxlY2Npb25hcl9vZmVydGFzKGJhc2UyLCB0YXJnZXQyLCA4NTAsIGMoNSwgNiksIG0yX2ZpbmFsLCA1KQpnZW8yIDwtIGJhbmRlcmFfZ2VvKGJhc2UyKQoKcm9iMiA8LSBsbXRlc3Q6OmNvZWZ0ZXN0KG0yX2ZpbmFsLCB2Y292LiA9IHNhbmR3aWNoOjp2Y292SEMobTJfZmluYWwsIHR5cGUgPSAiSEMzIikpCnJvYjIgPC0gZGF0YS5mcmFtZSgKICBUZXJtaW5vID0gcm93bmFtZXMocm9iMiksCiAgRXN0aW1hY2lvbiA9IHJvYjJbLCAxXSwKICBFRV9IQzMgPSByb2IyWywgMl0sCiAgdCA9IHJvYjJbLCAzXSwKICBwX0hDMyA9IHJvYjJbLCA0XSwKICByb3cubmFtZXMgPSBOVUxMCikKCiMgUmVzw7ptZW5lcyBwYXJhIG5hcnJhdGl2YSBkaW7DoW1pY2EKczEgPC0gc3VtbWFyeShtMV9mdWxsKQpzMiA8LSBzdW1tYXJ5KG0yX2Z1bGwpCm1pbmZpdDEgPC0gbWluKHByZWQxJGZpdCk7IG1heGZpdDEgPC0gbWF4KHByZWQxJGZpdCkKbWluZml0MiA8LSBtaW4ocHJlZDIkZml0KTsgbWF4Zml0MiA8LSBtYXgocHJlZDIkZml0KQoKem9uZV9ob3VzZSA8LSB2aXZpZW5kYSB8PgogIGZpbHRlcih0aXBvID09ICJDYXNhIiwgIWlzLm5hKHpvbmEpLCAhaXMubmEocHJlY2lvbSkpIHw+CiAgZ3JvdXBfYnkoem9uYSkgfD4KICBzdW1tYXJpc2UobiA9IG4oKSwgUHJlY2lvX21lZGlhbm8gPSBtZWRpYW4ocHJlY2lvbSksIFByZWNpb19wcm9tZWRpbyA9IG1lYW4ocHJlY2lvbSksIC5ncm91cHMgPSAiZHJvcCIpCgp6b25lX2FwdG8gPC0gdml2aWVuZGEgfD4KICBmaWx0ZXIodGlwbyA9PSAiQXBhcnRhbWVudG8iLCAhaXMubmEoem9uYSksICFpcy5uYShwcmVjaW9tKSkgfD4KICBncm91cF9ieSh6b25hKSB8PgogIHN1bW1hcmlzZShuID0gbigpLCBQcmVjaW9fbWVkaWFubyA9IG1lZGlhbihwcmVjaW9tKSwgUHJlY2lvX3Byb21lZGlvID0gbWVhbihwcmVjaW9tKSwgLmdyb3VwcyA9ICJkcm9wIikKYGBgCgo8ZGl2IGNsYXNzPSJjYWxsb3V0Ij4KPGI+UHJvZ3JhbWE6PC9iPiBNYWVzdHLDrWEgZW4gQ2llbmNpYSBkZSBEYXRvcyDigJQgUG9udGlmaWNpYSBVbml2ZXJzaWRhZCBKYXZlcmlhbmEgQ2FsaSAgCjxiPkFzaWduYXR1cmE6PC9iPiBNb2RlbG9zIEVzdGFkw61zdGljb3MgcGFyYSBsYSBUb21hIGRlIERlY2lzaW9uZXMgIAo8Yj5BY3RpdmlkYWQ6PC9iPiBJbmZvcm1lIGRlIHJlZ3Jlc2nDs24gbGluZWFsIG3Dumx0aXBsZSDigJQgQ2FzbyBDJkEgIAo8Yj5Fc3R1ZGlhbnRlOjwvYj4gSXbDoW4gRGFyw61vIEdhcmPDrWEgUmFtb3MKPC9kaXY+CgojIFJlc3VtZW4gZWplY3V0aXZvIHstfQoKQyZBIGRlYmUgcmVjb21lbmRhciBkb3MgaW5tdWVibGVzIGVuIENhbGkgYmFqbyByZXN0cmljY2lvbmVzIGV4cGzDrWNpdGFzIGRlIHRpcG8sIHpvbmEsIGNhcmFjdGVyw61zdGljYXMgZsOtc2ljYXMgeSBwcmVzdXB1ZXN0by4gUGFyYSByZWR1Y2lyIGVsIHJpZXNnbyBkZSB1bmEgcmVjb21lbmRhY2nDs24gYmFzYWRhIMO6bmljYW1lbnRlIGVuIGludHVpY2nDs24sIHNlIGNvbnN0cnV5ZXJvbiBtb2RlbG9zIGRlIHJlZ3Jlc2nDs24gbGluZWFsIG3Dumx0aXBsZSBzb2JyZSBsYSBiYXNlIGB2aXZpZW5kYWAsIGNvbXBsZW1lbnRhZG9zIGNvbiBhbsOhbGlzaXMgZXhwbG9yYXRvcmlvLCB2YWxpZGFjacOzbiBkZSBzdXB1ZXN0b3MsIGNvbXBhcmFjacOzbiBkZSBlc3BlY2lmaWNhY2lvbmVzIG1lZGlhbnRlIHZhbGlkYWNpw7NuIGNydXphZGEgZGUgMTAgcGxpZWd1ZXMgeSB1bmEgYsO6c3F1ZWRhIHJlcHJvZHVjaWJsZSBkZSBvZmVydGFzIG9ic2VydmFkYXMuCgpQYXJhIGxhICoqVml2aWVuZGEgMSAoQ2FzYSwgWm9uYSBOb3J0ZSwgY3LDqWRpdG8gbcOheGltbyBkZSAkMzUwIG1pbGxvbmVzKSoqLCBlbCBtb2RlbG8gY29tcGxldG8gcmVxdWVyaWRvIGV4cGxpY2EgYHIgZm10X3BjdChzMSRyLnNxdWFyZWQpYCBkZSBsYSB2YXJpYWJpbGlkYWQgb2JzZXJ2YWRhIGRlbCBwcmVjaW8geSBhbGNhbnphIHVuICRSXjIkIGFqdXN0YWRvIGRlIGByIGZtdF9udW0oczEkYWRqLnIuc3F1YXJlZCwgMylgLiBMYSBlc3RpbWFjacOzbiBwdW50dWFsIHBhcmEgZWwgcGVyZmlsIHNvbGljaXRhZG8gc2UgdWJpY2EgZW50cmUgYHIgZm10X21pbGwobWluZml0MSlgIHkgYHIgZm10X21pbGwobWF4Zml0MSlgIGFsIGNvbnNpZGVyYXIgbG9zIGVzdHJhdG9zIDQgeSA1LiBTZSBpZGVudGlmaWNhcm9uICoqY2luY28gb2ZlcnRhcyBwb3RlbmNpYWxlcyoqIGRlbnRybyBkZWwgcHJlc3VwdWVzdG8sIHByaW9yaXphZGFzIHBvciBzaW1pbGl0dWQgbXVsdGl2YXJpYWJsZSBjb24gZWwgcGVyZmlsIHNvbGljaXRhZG8uCgpQYXJhIGxhICoqVml2aWVuZGEgMiAoQXBhcnRhbWVudG8sIFpvbmEgU3VyLCBjcsOpZGl0byBtw6F4aW1vIGRlICQ4NTAgbWlsbG9uZXMpKiosIGVsIG1vZGVsbyBjb21wbGV0byBleHBsaWNhIGByIGZtdF9wY3QoczIkci5zcXVhcmVkKWAgZGUgbGEgdmFyaWFiaWxpZGFkIHkgcHJlc2VudGEgdW4gJFJeMiQgYWp1c3RhZG8gZGUgYHIgZm10X251bShzMiRhZGouci5zcXVhcmVkLCAzKWAuIExhIGVzdGltYWNpw7NuIHB1bnR1YWwgcGFyYSBsb3MgZXN0cmF0b3MgNSB5IDYgc2UgdWJpY2EgZW50cmUgYHIgZm10X21pbGwobWluZml0MilgIHkgYHIgZm10X21pbGwobWF4Zml0MilgLiBUYW1iacOpbiBzZSBlbnRyZWdhbiAqKmNpbmNvIGFsdGVybmF0aXZhcyoqIG9yZGVuYWRhcyBwb3IgY2VyY2Fuw61hIGFsIHBlcmZpbCB5IGNvbiB2ZXJpZmljYWNpw7NuIGRlbCBtYXJnZW4gZnJlbnRlIGFsIGNyw6lkaXRvIGFwcm9iYWRvLgoKPGRpdiBjbGFzcz0iY2FsbG91dC1vcmFuZ2UiPgo8Yj5SZWNvbWVuZGFjacOzbiBlamVjdXRpdmEuPC9iPiBVdGlsaXphciBlbCBtb2RlbG8gY29tbyBoZXJyYW1pZW50YSBkZSB2YWxvcmFjacOzbiB5IHByaW9yaXphY2nDs24sIG5vIGNvbW8gc3VzdGl0dXRvIGRlIGxhIHZpc2l0YSB0w6ljbmljYSBuaSBkZSBsYSBuZWdvY2lhY2nDs24uIExhIGRpc3BlcnNpw7NuIGRlIHByZWNpb3MgeSBsb3MgZGlhZ27Ds3N0aWNvcyByZXNpZHVhbGVzIG11ZXN0cmFuIHF1ZSBjYXJhY3RlcsOtc3RpY2FzIG5vIG9ic2VydmFkYXMg4oCUbWljcm8tbG9jYWxpemFjacOzbiwgZXN0YWRvIGRlIGNvbnNlcnZhY2nDs24sIGFudGlnw7xlZGFkLCBhY2FiYWRvcywgYWRtaW5pc3RyYWNpw7NuIHkgY29uZGljaW9uZXMgZGUgbmVnb2NpYWNpw7Nu4oCUIHB1ZWRlbiBleHBsaWNhciB1bmEgZnJhY2Npw7NuIHJlbGV2YW50ZSBkZWwgcHJlY2lvLiBQb3IgZWxsbywgbGFzIGNpbmNvIG9mZXJ0YXMgZGUgY2FkYSBjYXNvIGRlYmVuIHRvbWFyc2UgY29tbyB1bmEgbGlzdGEgY29ydGEgcGFyYSB2YWxpZGFjacOzbiBjb21lcmNpYWwgeSB0w6ljbmljYS4KPC9kaXY+CgojIEFuZXhvIHTDqWNuaWNvOiBlc3RpbWFjaW9uZXMsIHZhbGlkYWNpb25lcyB5IGNvbXBhcmFjacOzbiBkZSBtb2RlbG9zIHstfQoKTGFzIHNlY2Npb25lcyBzaWd1aWVudGVzIGNvbnRpZW5lbiBlbCBzb3BvcnRlIHJlcHJvZHVjaWJsZSBkZWwgaW5mb3JtZSBlamVjdXRpdm86IGZpbHRyb3MsIEVEQSwgbW9kZWxvcywgaW5mZXJlbmNpYSwgdmFsaWRhY2nDs24sIHByZWRpY2Npb25lcyB5IG1hcGFzIGRlIG9mZXJ0YXMuCgojIENvbnRleHRvLCBvYmpldGl2byB5IGVuZm9xdWUgYW5hbMOtdGljbwoKRWwgb2JqZXRpdm8gZXMgZXN0aW1hciBlbCBwcmVjaW8gZGUgbWVyY2FkbyBkZSBkb3MgcGVyZmlsZXMgZGUgdml2aWVuZGEgeSBwcm9wb25lciBvZmVydGFzIGNvbmNyZXRhcyBjb21wYXRpYmxlcyBjb24gbGFzIHJlc3RyaWNjaW9uZXMgZGVsIGNsaWVudGUuIFNlIHVzYSByZWdyZXNpw7NuIGxpbmVhbCBtw7psdGlwbGUgcG9ycXVlIHBlcm1pdGUgY3VhbnRpZmljYXIgZWwgZWZlY3RvIG1hcmdpbmFsIGRlIHZhcmlhcyBjYXJhY3RlcsOtc3RpY2FzIGRlbCBpbm11ZWJsZSBzb2JyZSBlbCBwcmVjaW8sIG1hbnRlbmllbmRvIGNvbnN0YW50ZXMgbGFzIGRlbcOhcyB2YXJpYWJsZXMuIEVzdGEgbMOzZ2ljYSBlcyBjb25zaXN0ZW50ZSBjb24gZWwgZW5mb3F1ZSBkZSB2YWxvcmFjacOzbiBoZWTDs25pY2EgdXRpbGl6YWRvIGVuIGVzdHVkaW9zIGlubW9iaWxpYXJpb3MgcmVjaWVudGVzLCBkb25kZSDDoXJlYSwgaGFiaXRhY2lvbmVzLCBiYcOxb3MsIHBhcnF1ZWFkZXJvcyB5IGxvY2FsaXphY2nDs24gZXhwbGljYW4gZGlmZXJlbmNpYXMgZGUgdmFsb3IgZW50cmUgcHJvcGllZGFkZXMgKEFndWRlbG8gVG9ycmVzIGV0IGFsLiwgMjAyMjsgV2FuZywgMjAyNDsgWmhvbmcsIDIwMjQpLgoKTGEgZXNwZWNpZmljYWNpw7NuIHByaW5jaXBhbCBzb2xpY2l0YWRhIGVzOgoKJCQKUHJlY2lvX2kgPSBcYmV0YV8wICsgXGJldGFfMSBBcmVhX2kgKyBcYmV0YV8yIEVzdHJhdG9faSArIFxiZXRhXzMgSGFiaXRhY2lvbmVzX2kgKyBcYmV0YV80IFBhcnF1ZWFkZXJvc19pICsgXGJldGFfNSBCYcOxb3NfaSArIFx2YXJlcHNpbG9uX2kKJCQKCkVsIGFuw6FsaXNpcyBzZSBkZXNhcnJvbGzDsyBjb24gdW5hIHNlY3VlbmNpYSByZXByb2R1Y2libGU6IGRlcHVyYWNpw7NuIHkgZmlsdHJvLCBFREEsIGVzdGltYWNpw7NuIHBvciBtw61uaW1vcyBjdWFkcmFkb3Mgb3JkaW5hcmlvcywgaW50ZXJwcmV0YWNpw7NuIGRlIGNvZWZpY2llbnRlcywgZXZhbHVhY2nDs24gZGUgYWp1c3RlLCBkaWFnbsOzc3RpY28gZGUgc3VwdWVzdG9zLCB2YWxpZGFjacOzbiBjcnV6YWRhLCBwcmVkaWNjacOzbiBjb24gaW50ZXJ2YWxvIGFsIDk1JSB5IHByaW9yaXphY2nDs24gZGUgb2ZlcnRhcy4gTGEgY29tcGFyYWNpw7NuIGVudHJlIGFqdXN0ZSBkZW50cm8gZGUgbXVlc3RyYSB5IGRlc2VtcGXDsW8gZnVlcmEgZGUgbXVlc3RyYSBzaWd1ZSBidWVuYXMgcHLDoWN0aWNhcyBtb2Rlcm5hcyBkZSBtb2RlbGFjacOzbiBlbiBSIChLdWhuICYgU2lsZ2UsIDIwMjI7IEhldW1hbm4gZXQgYWwuLCAyMDIzKS4KCiMgRGF0b3MsIGNhbGlkYWQgeSBkZXB1cmFjacOzbgoKIyMgRnVlbnRlIHkgZXN0cnVjdHVyYQoKTGEgYmFzZSBgdml2aWVuZGFgIHByb3ZpZW5lIGRlbCBwYXF1ZXRlIGBwYXF1ZXRlTU9ERUxPU2Agc3VtaW5pc3RyYWRvIGVuIGVsIGN1cnNvLiBDb250aWVuZSBpbmZvcm1hY2nDs24gc29icmUgdGlwbyBkZSBpbm11ZWJsZSwgem9uYSwgZXN0cmF0bywgcHJlY2lvIGVuIG1pbGxvbmVzIGRlIHBlc29zLCDDoXJlYSBjb25zdHJ1aWRhLCBwYXJxdWVhZGVyb3MsIGJhw7FvcywgaGFiaXRhY2lvbmVzLCBiYXJyaW8geSBjb29yZGVuYWRhcyBnZW9ncsOhZmljYXMuCgpgYGB7ciBjYWxpZGFkLWdlbmVyYWx9CmtibF9wcm9mKHRhYmxhX2NhbGlkYWQsICJUYWJsYSAxLiBDb250cm9sZXMgZ2VuZXJhbGVzIGRlIGNhbGlkYWQgZGUgZGF0b3MiLCAwKQpgYGAKCmBgYHtyIGZhbHRhbnRlcy1nZW5lcmFsfQprYmxfcHJvZih0YWJsYV9uYSwgIlRhYmxhIDIuIFZhbG9yZXMgZmFsdGFudGVzIHBvciB2YXJpYWJsZSIsIDApCmBgYAoKTGEgZGVwdXJhY2nDs24gZXZpdGEgZWxpbWluYXIgb2JzZXJ2YWNpb25lcyBwb3IgdmFyaWFibGVzIGlycmVsZXZhbnRlcyBwYXJhIGVsIG1vZGVsby4gUG9yIGVqZW1wbG8sIGxvcyBmYWx0YW50ZXMgZGUgYHBpc29gIG5vIGp1c3RpZmljYW4gZXhjbHVpciB1bmEgY2FzYSBzaSBlbCByZXN0byBkZSB2YXJpYWJsZXMgcmVxdWVyaWRhcyBlc3TDoSBjb21wbGV0by4gUGFyYSBsYSByZWdyZXNpw7NuIHNlIGVtcGxlYW4gw7puaWNhbWVudGUgY2Fzb3MgY29tcGxldG9zIGVuIHByZWNpbywgw6FyZWEsIGVzdHJhdG8sIHBhcnF1ZWFkZXJvcywgYmHDsW9zIHkgaGFiaXRhY2lvbmVzLiBFc3RhIGRlY2lzacOzbiBjb25zZXJ2YSBsYSBtYXlvciBjYW50aWRhZCBkZSBpbmZvcm1hY2nDs24gw7p0aWwgeSBoYWNlIGV4cGzDrWNpdG8gZWwgdW5pdmVyc28gZWZlY3Rpdm8gZGUgbW9kZWxhY2nDs24uCgojIyBDb21wcm9iYWNpw7NuIGRlIGNhdGVnb3LDrWFzCgpgYGB7ciBjYXRlZ29yaWFzfQp0YWJsYV9jYXQgPC0gdml2aWVuZGEgfD4KICBjb3VudCh0aXBvLCB6b25hLCBuYW1lID0gIm4iKSB8PgogIGFycmFuZ2UodGlwbywgem9uYSkKa2JsX3Byb2YodGFibGFfY2F0LCAiVGFibGEgMy4gRGlzdHJpYnVjacOzbiBkZSByZWdpc3Ryb3MgcG9yIHRpcG8gZGUgdml2aWVuZGEgeSB6b25hIiwgMCkKYGBgCgojIFNvbGljaXR1ZCAxIOKAlCBDYXNhIGVuIFpvbmEgTm9ydGUKCiMjIEZpbHRybyBpbmljaWFsIHkgdmVyaWZpY2FjacOzbgoKTGEgcHJpbWVyYSBzb2xpY2l0dWQgZXhpZ2U6ICoqQ2FzYSoqLCAqKlpvbmEgTm9ydGUqKiwgKioyMDAgbcKyKiosICoqMSBwYXJxdWVhZGVybyoqLCAqKjIgYmHDsW9zKiosICoqNCBoYWJpdGFjaW9uZXMqKiwgKiplc3RyYXRvIDQgbyA1KiogeSBjcsOpZGl0byBtw6F4aW1vIGRlICoqJDM1MCBtaWxsb25lcyoqLgoKYGBge3IgZmlsdHJvLW5vcnRlfQpiYXNlMSA8LSB2aXZpZW5kYSB8PgogIGZpbHRlcih0aXBvID09ICJDYXNhIiwgem9uYSA9PSAiWm9uYSBOb3J0ZSIpCgpoZWFkKGJhc2UxLCAzKSB8PgogIGtibF9wcm9mKCJUYWJsYSA0LiBQcmltZXJvcyB0cmVzIHJlZ2lzdHJvcyBkZSBjYXNhcyBlbiBab25hIE5vcnRlIiwgMikKYGBgCgpgYGB7ciBjb250cm9sLWZpbHRyby1ub3J0ZX0KY29udHJvbDEgPC0gYmluZF9yb3dzKAogIGJhc2UxIHw+IGNvdW50KHRpcG8sIG5hbWUgPSAibiIpIHw+IG11dGF0ZShDb250cm9sID0gIlRpcG8iLCBDYXRlZ29yaWEgPSB0aXBvKSB8PiBzZWxlY3QoQ29udHJvbCwgQ2F0ZWdvcmlhLCBuKSwKICBiYXNlMSB8PiBjb3VudCh6b25hLCBuYW1lID0gIm4iKSB8PiBtdXRhdGUoQ29udHJvbCA9ICJab25hIiwgQ2F0ZWdvcmlhID0gem9uYSkgfD4gc2VsZWN0KENvbnRyb2wsIENhdGVnb3JpYSwgbiksCiAgYmFzZTEgfD4gY291bnQoZXN0cmF0bywgbmFtZSA9ICJuIikgfD4gbXV0YXRlKENvbnRyb2wgPSAiRXN0cmF0byIsIENhdGVnb3JpYSA9IGFzLmNoYXJhY3Rlcihlc3RyYXRvKSkgfD4gc2VsZWN0KENvbnRyb2wsIENhdGVnb3JpYSwgbikKKQprYmxfcHJvZihjb250cm9sMSwgIlRhYmxhIDUuIFZlcmlmaWNhY2nDs24gZGVsIGZpbHRybyBpbmljaWFsIOKAlCBTb2xpY2l0dWQgMSIsIDApCmBgYAoKRWwgZmlsdHJvIHByb2R1Y2UgYHIgbnJvdyhiYXNlMSlgIGNhc2FzIGNsYXNpZmljYWRhcyBjb21vIFpvbmEgTm9ydGU7IGByIG5yb3coYmFzZTFfbW9kZWwpYCB0aWVuZW4gaW5mb3JtYWNpw7NuIGNvbXBsZXRhIGVuIHRvZGFzIGxhcyB2YXJpYWJsZXMgcmVxdWVyaWRhcyBwb3IgbGEgcmVncmVzacOzbi4gTGEgY29tcHJvYmFjacOzbiBhbnRlcmlvciBhc2VndXJhIHF1ZSBlbCBtb2RlbG8gbm8gbWV6Y2xlIGFwYXJ0YW1lbnRvcyBuaSByZWdpc3Ryb3MgY2xhc2lmaWNhZG9zIGVuIG90cmEgem9uYS4KCiMjIE1hcGEgZGUgdG9kYXMgbGFzIG9mZXJ0YXMgeSBjb25zaXN0ZW5jaWEgZ2VvZ3LDoWZpY2EKCmBgYHtyIG1hcGEtbm9ydGV9Cm1hcGFfc2VnbWVudG8oYmFzZTEsICJDYXNhcyDigJQgWm9uYSBOb3J0ZSIpCmBgYAoKRWwgbWFwYSBpbmNsdXllICoqdG9kb3MgbG9zIHJlZ2lzdHJvcyBjb24gY29vcmRlbmFkYXMgdsOhbGlkYXMqKiBkZWwgc2VnbWVudG8uIFNlIG1hcmNhbiBjb21vIGF0w61waWNvcyBsb3MgcHVudG9zIHF1ZSBzZSBhcGFydGFuIGRlbCBwYXRyw7NuIGVzcGFjaWFsIGRlbCBwcm9waW8gc2VnbWVudG8gbWVkaWFudGUgdW4gY3JpdGVyaW8gcm9idXN0byBiYXNhZG8gZW4gMSw1IHJhbmdvcyBpbnRlcmN1YXJ0w61saWNvcy4gU2UgaWRlbnRpZmljYW4gYHIgc3VtKGdlbzEkZ2VvX2F0aXBpY28sIG5hLnJtID0gVFJVRSlgIHB1bnRvcyBnZW9ncsOhZmljYW1lbnRlIGF0w61waWNvcyBzb2JyZSBgciBucm93KGdlbzEpYCBvYnNlcnZhY2lvbmVzIG1hcGVhYmxlcy4KCkVzdG8gKipubyBpbXBsaWNhIGF1dG9tw6F0aWNhbWVudGUgcXVlIGxhIGNsYXNpZmljYWNpw7NuIGBab25hIE5vcnRlYCBzZWEgaW5jb3JyZWN0YSoqLiBMYSB2YXJpYWJsZSBgem9uYWAgZXMgdW5hIGNhdGVnb3LDrWEgY29tZXJjaWFsIGRlIGxhIGZ1ZW50ZSwgbWllbnRyYXMgcXVlIGxhdGl0dWQvbG9uZ2l0dWQgcHVlZGUgY29ycmVzcG9uZGVyIGEgY29vcmRlbmFkYXMgYXByb3hpbWFkYXMsIGNlbnRyb2lkZXMgZGUgYmFycmlvLCBlcnJvcmVzIGRlIGdlb2NvZGlmaWNhY2nDs24gbyBjYXB0dXJhLiBDb21vIGxhIGJhc2Ugbm8gaW5jb3Jwb3JhIHBvbMOtZ29ub3Mgb2ZpY2lhbGVzIGRlIGRlbGltaXRhY2nDs24gZGUgem9uYXMsIG5vIGVzIG1ldG9kb2zDs2dpY2FtZW50ZSB2w6FsaWRvIHJlY29kaWZpY2FyIGxhIHpvbmEgc29sbyBwb3IgaW5zcGVjY2nDs24gdmlzdWFsLiBQYXJhIHVuIG1vZGVsbyBkZSBwcm9kdWNjacOzbiBzZSByZWNvbWllbmRhIHZhbGlkYXIgbGFzIGNvb3JkZW5hZGFzIGNvbnRyYSBjYXJ0b2dyYWbDrWEgb2ZpY2lhbCB5IG1vZGVsYXIgZXhwbMOtY2l0YW1lbnRlIGxhIG1pY3JvLWxvY2FsaXphY2nDs24uCgojIyBBbsOhbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGRhdG9zCgojIyMgQ29ycmVsYWNpw7NuIGVudHJlIHByZWNpbyB5IGNhcmFjdGVyw61zdGljYXMKCmBgYHtyIGNvci1ub3J0ZS10YWJsYX0KY29yMV9zaG93IDwtIGNvcjEgfD4KICBtdXRhdGUoYWNyb3NzKGMoUGVhcnNvbiwgU3BlYXJtYW4pLCB+cm91bmQoLngsIDMpKSkKa2JsX3Byb2YoY29yMV9zaG93LCAiVGFibGEgNi4gQ29ycmVsYWNpw7NuIGRlbCBwcmVjaW8gY29uIGxvcyBwcmVkaWN0b3JlcyDigJQgQ2FzYXMgWm9uYSBOb3J0ZSIsIDMpCmBgYAoKU2UgcmVwb3J0YW4gUGVhcnNvbiB5IFNwZWFybWFuLiBQZWFyc29uIHJlc3VtZSBhc29jaWFjacOzbiBsaW5lYWw7IFNwZWFybWFuIGNvbXBsZW1lbnRhIGVsIGFuw6FsaXNpcyBjdWFuZG8gbGFzIHZhcmlhYmxlcyBzb24gb3JkaW5hbGVzIG8gZGlzY3JldGFzLCBjb21vIGVzdHJhdG8sIGJhw7FvcyBvIGhhYml0YWNpb25lcy4gTGEgdmFyaWFibGUgY29uIG1heW9yIGFzb2NpYWNpw7NuIGxpbmVhbCBhYnNvbHV0YSBjb24gZWwgcHJlY2lvIGVzICoqYHIgY29yMSRWYXJpYWJsZVsxXWAqKiwgY29uIHVuYSBjb3JyZWxhY2nDs24gZGUgUGVhcnNvbiBkZSBgciBmbXRfbnVtKGNvcjEkUGVhcnNvblsxXSwgMylgLCBjbGFzaWZpY2FkYSBjb21vIGByIGNvcjEkSW50ZW5zaWRhZFsxXWAgeSBgciBjb3IxJERpcmVjY2lvblsxXWAuCgpgYGB7ciBoZWF0bWFwLW5vcnRlfQpwbG90X2x5KAogIHggPSBjb2xuYW1lcyhjb3JfbWF0MSksIHkgPSByb3duYW1lcyhjb3JfbWF0MSksIHogPSBjb3JfbWF0MSwKICB0eXBlID0gImhlYXRtYXAiLCB6bWluID0gLTEsIHptYXggPSAxLAogIGhvdmVydGVtcGxhdGUgPSAiWDogJXt4fTxicj5ZOiAle3l9PGJyPnI6ICV7ejouM2Z9PGV4dHJhPjwvZXh0cmE+IgopIHw+CiAgbGF5b3V0KHRpdGxlID0gIk1hdHJpeiBkZSBjb3JyZWxhY2nDs24g4oCUIENhc2FzIFpvbmEgTm9ydGUiLCB4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksIHlheGlzID0gbGlzdCh0aXRsZSA9ICIiKSkKYGBgCgpgYGB7ciBzY2F0dGVyLW5vcnRlfQpwX2FyZWExIDwtIGdncGxvdChiYXNlMV9tb2RlbCwgYWVzKGFyZWFjb25zdCwgcHJlY2lvbSkpICsKICBnZW9tX3BvaW50KGFlcyh0ZXh0ID0gcGFzdGUwKAogICAgIklEOiAiLCBpZCwKICAgICI8YnI+QmFycmlvOiAiLCBiYXJyaW8sCiAgICAiPGJyPsOBcmVhOiAiLCBhcmVhY29uc3QsICIgbcKyIiwKICAgICI8YnI+UHJlY2lvOiAkIiwgcHJlY2lvbSwgIiBNIiwKICAgICI8YnI+RXN0cmF0bzogIiwgZXN0cmF0bwogICkpLCBhbHBoYSA9IDAuNTApICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUpICsKICBsYWJzKAogICAgdGl0bGUgPSAiUHJlY2lvIHZzLiDDoXJlYSBjb25zdHJ1aWRhIOKAlCBDYXNhcyBab25hIE5vcnRlIiwKICAgIHggPSAiw4FyZWEgY29uc3RydWlkYSAobcKyKSIsIHkgPSAiUHJlY2lvIChtaWxsb25lcyBDT1ApIgogICkgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpCgpnZ3Bsb3RseShwX2FyZWExLCB0b29sdGlwID0gInRleHQiKQpgYGAKCiMjIyBFZmVjdG8gY29udGV4dHVhbCBkZSBsYSB6b25hCgpEZW50cm8gZGUgYGJhc2UxYCwgbGEgdmFyaWFibGUgem9uYSBlcyBjb25zdGFudGUgcG9yIGNvbnN0cnVjY2nDs24geSwgcG9yIHRhbnRvLCAqKm5vIHB1ZWRlIGNvcnJlbGFjaW9uYXJzZSBuaSBlc3RpbWFyc2UgY29tbyBwcmVkaWN0b3IgaW50ZXJubyBkZWwgbW9kZWxvKiouIFBhcmEgbm8gcGVyZGVyIHN1IGltcG9ydGFuY2lhIGVjb27Ds21pY2EsIHNlIGNvbXBhcmEgZWwgcHJlY2lvIGRlIHRvZGFzIGxhcyBjYXNhcyBkZSBDYWxpIHBvciB6b25hOgoKYGBge3Igem9uYS1jYXNhc30Ka2JsX3Byb2Yoem9uZV9ob3VzZSwgIlRhYmxhIDcuIFByZWNpbyBkZSBjYXNhcyBwb3Igem9uYSDigJQgY29udGV4dG8gZGUgbWVyY2FkbyIsIDEpCmBgYAoKYGBge3IgcGxvdC16b25hLWNhc2FzfQpwX3oxIDwtIHZpdmllbmRhIHw+CiAgZmlsdGVyKHRpcG8gPT0gIkNhc2EiLCAhaXMubmEoem9uYSksICFpcy5uYShwcmVjaW9tKSkgfD4KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKHpvbmEsIHByZWNpb20sIEZVTiA9IG1lZGlhbiksIHkgPSBwcmVjaW9tKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLmFscGhhID0gMC4yMCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBkZWwgcHJlY2lvIGRlIGNhc2FzIHBvciB6b25hIiwgeCA9ICJab25hIiwgeSA9ICJQcmVjaW8gKG1pbGxvbmVzIENPUCkiKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMjUsIGhqdXN0ID0gMSkpCmdncGxvdGx5KHBfejEpCmBgYAoKTGEgY29tcGFyYWNpw7NuIGNvbmZpcm1hIHBvciBxdcOpIGxhIHViaWNhY2nDs24gZGViZSBjb25zaWRlcmFyc2UgZW4gbGEgaW50ZXJwcmV0YWNpw7NuOiBhdW4gY3VhbmRvIGVsIG1vZGVsbyBkZSBsYSBzb2xpY2l0dWQgc2UgZXN0aW1hIGV4Y2x1c2l2YW1lbnRlIGRlbnRybyBkZSBab25hIE5vcnRlLCBlbCBtZXJjYWRvIGdlbmVyYWwgcHJlc2VudGEgbml2ZWxlcyB5IGRpc3BlcnNpb25lcyBkZSBwcmVjaW8gZGlmZXJlbnRlcyBlbnRyZSB6b25hcy4KCiMjIEVzdGltYWNpw7NuIGRlbCBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgbcO6bHRpcGxlCgpgYGB7ciBtb2RlbG8tbm9ydGV9Cm0xX2Z1bGwgPC0gbG0oCiAgcHJlY2lvbSB+IGFyZWFjb25zdCArIGVzdHJhdG8gKyBoYWJpdGFjaW9uZXMgKyBwYXJxdWVhZGVyb3MgKyBiYW5pb3MsCiAgZGF0YSA9IGJhc2UxX21vZGVsCikKc3VtbWFyeShtMV9mdWxsKQpgYGAKCiMjIyBDb2VmaWNpZW50ZXMgZSBpbnRlcnByZXRhY2nDs24KCmBgYHtyIGNvZWYtbm9ydGV9CmNvZWYxX3RhYiA8LSBjb2VmMSB8PgogIG11dGF0ZSgKICAgIGVzdGltYXRlID0gcm91bmQoZXN0aW1hdGUsIDMpLCBzdGQuZXJyb3IgPSByb3VuZChzdGQuZXJyb3IsIDMpLAogICAgY29uZi5sb3cgPSByb3VuZChjb25mLmxvdywgMyksIGNvbmYuaGlnaCA9IHJvdW5kKGNvbmYuaGlnaCwgMyksCiAgICBwLnZhbHVlID0gaWZlbHNlKHAudmFsdWUgPCAwLjAwMSwgIjwwLDAwMSIsIGZvcm1hdChyb3VuZChwLnZhbHVlLCAzKSwgZGVjaW1hbC5tYXJrID0gIiwiKSkKICApIHw+CiAgcmVuYW1lKAogICAgRXN0aW1hY2lvbiA9IGVzdGltYXRlLCBgRXJyb3IgZXN0w6FuZGFyYCA9IHN0ZC5lcnJvciwKICAgIGBJQzk1JSBpbmZlcmlvcmAgPSBjb25mLmxvdywgYElDOTUlIHN1cGVyaW9yYCA9IGNvbmYuaGlnaCwKICAgIGBwLXZhbG9yYCA9IHAudmFsdWUsIGBTaWduaWZpY2F0aXZvIGFsIDUlYCA9IFNpZ25pZmljYXRpdm9fNXBjdCwKICAgIEludGVycHJldGFjacOzbiA9IEludGVycHJldGFjaW9uCiAgKQprYmxfcHJvZihjb2VmMV90YWIsICJUYWJsYSA4LiBDb2VmaWNpZW50ZXMgZGVsIG1vZGVsbyBjb21wbGV0byDigJQgQ2FzYXMgWm9uYSBOb3J0ZSIsIDMpCmBgYAoKRWwgaW50ZXJjZXB0byBubyBzZSBpbnRlcnByZXRhIGVjb27Ds21pY2FtZW50ZSBwb3JxdWUgcmVwcmVzZW50YSB1bmEgdml2aWVuZGEgY29uIMOhcmVhLCBlc3RyYXRvLCBoYWJpdGFjaW9uZXMsIHBhcnF1ZWFkZXJvcyB5IGJhw7FvcyBpZ3VhbGVzIGEgY2VybywgY29tYmluYWNpw7NuIGZ1ZXJhIGRlbCBkb21pbmlvIHJlYWwgZGVsIG1lcmNhZG8uIExhIGludGVycHJldGFjacOzbiBzdXN0YW50aXZhIHNlIGNvbmNlbnRyYSBlbiBsYXMgcGVuZGllbnRlczogY2FkYSBjb2VmaWNpZW50ZSByZXByZXNlbnRhIGVsIGNhbWJpbyBlc3BlcmFkbyBlbiBtaWxsb25lcyBkZSBwZXNvcyBhbnRlIHVuYSB1bmlkYWQgYWRpY2lvbmFsIGRlbCBwcmVkaWN0b3IsICoqbWFudGVuaWVuZG8gY29uc3RhbnRlcyBsYXMgZGVtw6FzIGNhcmFjdGVyw61zdGljYXMqKi4KCkVsIG1vZGVsbyBjb21wbGV0byBhbGNhbnphICRSXjIgPSBgciBmbXRfbnVtKHMxJHIuc3F1YXJlZCwgMylgJCB5ICRSXjJfe2FqfSA9IGByIGZtdF9udW0oczEkYWRqLnIuc3F1YXJlZCwgMylgJC4gRW4gb3RyYXMgcGFsYWJyYXMsIGxhcyBjaW5jbyBjYXJhY3RlcsOtc3RpY2FzIGV4cGxpY2FuIGByIGZtdF9wY3QoczEkci5zcXVhcmVkKWAgZGUgbGEgdmFyaWFjacOzbiBvYnNlcnZhZGEgZGUgcHJlY2lvcyBkZW50cm8gZGVsIHNlZ21lbnRvLiBMYSBkaWZlcmVuY2lhIGVudHJlICRSXjIkIHkgJFJeMl97YWp9JCBlcyBwZXF1ZcOxYSBjdWFuZG8gbG9zIHByZWRpY3RvcmVzIGFncmVnYW4gc2XDsWFsIMO6dGlsOyBzaSBmdWVyYSBhbXBsaWEsIHN1Z2VyaXLDrWEgY29tcGxlamlkYWQgaW5uZWNlc2FyaWEuCgpObyBvYnN0YW50ZSwgdW4gJFJeMiQgYWx0byBubyBnYXJhbnRpemEgYnVlbmFzIHByZWRpY2Npb25lcyBmdWVyYSBkZSBtdWVzdHJhIG5pIGN1bXBsaW1pZW50byBkZSBzdXB1ZXN0b3MuIFBvciBlc28gc2UgY29tcGxlbWVudGEgY29uIHZhbGlkYWNpw7NuIGNydXphZGEsIEFJQy9CSUMgeSBkaWFnbsOzc3RpY28gcmVzaWR1YWwuCgojIyBDb21wYXJhY2nDs24gZGUgbW9kZWxvcyB5IHZhbGlkYWNpw7NuIGNydXphZGEKClNlIGNvbXBhcmEgdW4gbW9kZWxvIGJhc2UgZGUgc29sbyDDoXJlYSwgZWwgbW9kZWxvIG3Dumx0aXBsZSByZXF1ZXJpZG8geSB1bmEgdmVyc2nDs24gcGFyc2ltb25pb3NhIG9idGVuaWRhIHBvciBlbGltaW5hY2nDs24gaGFjaWEgYXRyw6FzIGNvbiBBSUMuIExhIHNlbGVjY2nDs24gZmluYWwgc2UgYmFzYSBlbiBlbCAqKlJNU0UgZGUgdmFsaWRhY2nDs24gY3J1emFkYSBkZSAxMCBwbGllZ3VlcyoqIHksIGVuIHNlZ3VuZG8gdMOpcm1pbm8sIHBhcnNpbW9uaWEuCgpgYGB7ciBjb21wYXJhY2lvbi1ub3J0ZX0KY29tcDFfc2hvdyA8LSBjb21wMSB8PgogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH5yb3VuZCgueCwgMykpKQprYmxfcHJvZihjb21wMV9zaG93LCAiVGFibGEgOS4gQ29tcGFyYWNpw7NuIGRlIG1vZGVsb3Mg4oCUIENhc2FzIFpvbmEgTm9ydGUiLCAzKQpgYGAKCkVsIG1vZGVsbyBzZWxlY2Npb25hZG8gcGFyYSBwcmVkaWNjacOzbiBlcyAqKmByIGJlc3QxX25hbWVgKiouIFN1IHVzbyBldml0YSBlbGVnaXIgdW5hIGVzcGVjaWZpY2FjacOzbiBzb2xvIHBvciBhanVzdGUgZGVudHJvIGRlIG11ZXN0cmEgeSBmYXZvcmVjZSBjYXBhY2lkYWQgZGUgZ2VuZXJhbGl6YWNpw7NuLCBwcmluY2lwaW8gY2VudHJhbCBlbiBsYSBldmFsdWFjacOzbiBkZSBtb2RlbG9zIHByZWRpY3Rpdm9zIChLdWhuICYgU2lsZ2UsIDIwMjIpLgoKIyMgVmFsaWRhY2nDs24gZGUgc3VwdWVzdG9zCgpgYGB7ciBzdXB1ZXN0b3Mtbm9ydGV9CmtibF9wcm9mKGRpYWcxLCAiVGFibGEgMTAuIERpYWduw7NzdGljbyBkZSBzdXB1ZXN0b3Mg4oCUIG1vZGVsbyBzZWxlY2Npb25hZG8sIENhc2FzIFpvbmEgTm9ydGUiLCAyKQpgYGAKCjxzcGFuIGNsYXNzPSJzbWFsbC1ub3RlIj4qRHVyYmlu4oCTV2F0c29uIHNlIHJlcG9ydGEgcG9ycXVlIGhhY2UgcGFydGUgZGVsIGRpYWduw7NzdGljbyBjbMOhc2ljbywgcGVybyBsYXMgb2JzZXJ2YWNpb25lcyBzb24gdHJhbnN2ZXJzYWxlcyB5IGVsIG9yZGVuIGRlIGxhIGJhc2Ugbm8gY29uc3RpdHV5ZSB1bmEgc2VyaWUgdGVtcG9yYWwuIFBvciBlbGxvLCB1biByZXN1bHRhZG8gc2lnbmlmaWNhdGl2byBubyBkZWJlIGNvbmZ1bmRpcnNlIGNvbiBldmlkZW5jaWEgZm9ybWFsIGRlIGRlcGVuZGVuY2lhIGVzcGFjaWFsLjwvc3Bhbj4KCmBgYHtyIGRpYWdub3N0aWNvcy1ncmFmaWNvcy1ub3J0ZSwgZmlnLmhlaWdodD03fQpwYXIobWZyb3cgPSBjKDIsIDIpKQpwbG90KG0xX2ZpbmFsKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCkxhIGxlY3R1cmEgZGUgbG9zIHN1cHVlc3RvcyBzZSByZWFsaXphIGRlIGZvcm1hIGNvbmp1bnRhLiBTaSBCcmV1c2No4oCTUGFnYW4gcmVzdWx0YSBzaWduaWZpY2F0aXZvLCBsb3MgY29lZmljaWVudGVzIE1DTyBzaWd1ZW4gc2llbmRvIMO6dGlsZXMgY29tbyBlc3RpbWFkb3JlcyBkZSB0ZW5kZW5jaWEgYmFqbyBleG9nZW5laWRhZCwgcGVybyBsb3MgZXJyb3JlcyBlc3TDoW5kYXIgY29udmVuY2lvbmFsZXMgcHVlZGVuIGRpc3RvcnNpb25hcnNlLiBDb21vIGFuw6FsaXNpcyBkZSBzZW5zaWJpbGlkYWQgc2UgcmVwb3J0YW4gZXJyb3JlcyBlc3TDoW5kYXIgSEMzLCBzaW4gcmVlbXBsYXphciBlbCBtb2RlbG8gcmVxdWVyaWRvLgoKYGBge3Igcm9idXN0b3Mtbm9ydGV9CmtibF9wcm9mKHJvYjEsICJUYWJsYSAxMS4gU2Vuc2liaWxpZGFkIGluZmVyZW5jaWFsIGNvbiBlcnJvcmVzIGVzdMOhbmRhciByb2J1c3RvcyBIQzMg4oCUIENhc2FzIFpvbmEgTm9ydGUiLCA0KQpgYGAKCiMjIyBTdWdlcmVuY2lhcyBkZSBtZWpvcmEgZGVsIG1vZGVsbwoKU2kgbG9zIGRpYWduw7NzdGljb3MgbXVlc3RyYW4gaGV0ZXJvY2VkYXN0aWNpZGFkLCBubyBsaW5lYWxpZGFkLCByZXNpZHVvcyBkZSBjb2xhIHBlc2FkYSBvIGNhc29zIGluZmx1eWVudGVzLCBsYXMgc2lndWllbnRlcyBleHRlbnNpb25lcyBzb24gdMOpY25pY2FtZW50ZSByYXpvbmFibGVzOiB0cmFuc2Zvcm1hciBlbCBwcmVjaW8gKHBvciBlamVtcGxvLCBsb2dhcml0bW8pLCBtb2RlbGFyIHJlbGFjaW9uZXMgbm8gbGluZWFsZXMgZGVsIMOhcmVhLCBpbmNvcnBvcmFyIGludGVyYWNjaW9uZXMsIGHDsWFkaXIgYmFycmlvIG8gY29vcmRlbmFkYXMsIGluY2x1aXIgYW50aWfDvGVkYWQvZXN0YWRvL2FjYWJhZG9zL2FkbWluaXN0cmFjacOzbiwgZXN0aW1hciBlcnJvcmVzIHJvYnVzdG9zIHkgcmVhbGl6YXIgdmFsaWRhY2nDs24gZXh0ZXJuYSBjb24gbnVldmFzIG9mZXJ0YXMuIExhIGxpdGVyYXR1cmEgcmVjaWVudGUgc29icmUgcHJlY2lvcyBkZSB2aXZpZW5kYSBkZXN0YWNhIHByZWNpc2FtZW50ZSBsYSByZWxldmFuY2lhIGRlIGxhIGxvY2FsaXphY2nDs24geSBkZSB2YXJpYWJsZXMgZsOtc2ljYXMgYWRpY2lvbmFsZXMgcGFyYSBtZWpvcmFyIGxhIGNhcGFjaWRhZCBleHBsaWNhdGl2YSB5IHByZWRpY3RpdmEgKEFndWRlbG8gVG9ycmVzIGV0IGFsLiwgMjAyMjsgV2FuZywgMjAyNCkuCgojIyBFc3RpbWFjacOzbiBwdW50dWFsIGUgaW50ZXJ2YWxvIGRlIHByZWRpY2Npw7NuIOKAlCBWaXZpZW5kYSAxCgpMYSBzb2xpY2l0dWQgcGVybWl0ZSBlc3RyYXRvIDQgbyA1LiBFbiBjb25zZWN1ZW5jaWEsIHNlIHByZWRpY2VuIGFtYm9zIGVzY2VuYXJpb3MgZW4gbHVnYXIgZGUgaW1wb25lciBhcmJpdHJhcmlhbWVudGUgdW5vIHNvbG8uCgpgYGB7ciBwcmVkaWNjaW9uLW5vcnRlfQpwcmVkMV9zaG93IDwtIHByZWQxIHw+CiAgdHJhbnNtdXRlKAogICAgRXN0cmF0byA9IGVzdHJhdG8sCiAgICBgRXN0aW1hY2nDs24gcHVudHVhbCAoTSlgID0gcm91bmQoZml0LCAxKSwKICAgIGBMw61taXRlIGluZmVyaW9yIElQOTUlIChNKWAgPSByb3VuZChsd3IsIDEpLAogICAgYEzDrW1pdGUgc3VwZXJpb3IgSVA5NSUgKE0pYCA9IHJvdW5kKHVwciwgMSksCiAgICBgQ3LDqWRpdG8gbcOheGltbyAoTSlgID0gcHJlc3VwdWVzdG8sCiAgICBgTWFyZ2VuIHZzLiBlc3RpbWFjacOzbiAoTSlgID0gcm91bmQobWFyZ2VuLCAxKSwKICAgIERlY2lzacOzbiA9IGRlY2lzaW9uX3ByZXN1cHVlc3RvCiAgKQprYmxfcHJvZihwcmVkMV9zaG93LCAiVGFibGEgMTIuIFByZWRpY2Npw7NuIGRlIHByZWNpbyBwYXJhIGxhIFZpdmllbmRhIDEiLCAxKQpgYGAKCkxhICoqZXN0aW1hY2nDs24gcHVudHVhbCoqIHJlcHJlc2VudGEgZWwgcHJlY2lvIHByb21lZGlvIGVzcGVyYWRvIHBhcmEgdW5hIHZpdmllbmRhIGNvbiBlc2FzIGNhcmFjdGVyw61zdGljYXMgc2Vnw7puIGVsIG1vZGVsby4gRWwgKippbnRlcnZhbG8gZGUgcHJlZGljY2nDs24gYWwgOTUlKiogZXMgbcOhcyBhbXBsaW8gcG9ycXVlIGluY29ycG9yYSB0YW50byBsYSBpbmNlcnRpZHVtYnJlIGRlIGVzdGltYWNpw7NuIGNvbW8gbGEgdmFyaWFiaWxpZGFkIGluZGl2aWR1YWwgZGUgdW5hIG51ZXZhIHZpdmllbmRhLiBQYXJhIHVuYSBkZWNpc2nDs24gZGUgY29tcHJhIGVzIG3DoXMgaW5mb3JtYXRpdm8gcXVlIGxpbWl0YXJzZSBhIHVuIMO6bmljbyBuw7ptZXJvLgoKIyMgQ2luY28gb2ZlcnRhcyBwb3RlbmNpYWxlcyBkZW50cm8gZGVsIGNyw6lkaXRvCgpMYSBzZWxlY2Npw7NuIHNlIHJlc3RyaW5nZSBhIGNhc2FzIGRlIFpvbmEgTm9ydGUsIGVzdHJhdG8gNCBvIDUgeSBwcmVjaW8gb2JzZXJ2YWRvIG5vIHN1cGVyaW9yIGEgJDM1MCBtaWxsb25lcy4gTHVlZ28gc2Ugb3JkZW5hbiBwb3IgdW5hIGRpc3RhbmNpYSBwb25kZXJhZGEgZnJlbnRlIGFsIHBlcmZpbCBzb2xpY2l0YWRvOiA0MCUgw6FyZWEsIDIwJSBwYXJxdWVhZGVyb3MsIDIwJSBiYcOxb3MgeSAyMCUgaGFiaXRhY2lvbmVzLiBFc3RlIGNyaXRlcmlvIGV2aXRhIGVzY29nZXIgw7puaWNhbWVudGUgcG9yIHByZWNpbyB5IGZhdm9yZWNlIHNpbWlsaXR1ZCBmdW5jaW9uYWwuCgpgYGB7ciBvZmVydGFzLW5vcnRlfQpvZmVydGFzMV9zaG93IDwtIG9mZXJ0YXMxIHw+CiAgdHJhbnNtdXRlKAogICAgUmFua2luZywgSUQgPSBpZCwgQmFycmlvID0gYmFycmlvLCBFc3RyYXRvID0gZXN0cmF0bywKICAgIGBQcmVjaW8gb2ZlcnRhIChNKWAgPSBwcmVjaW9tLCBgw4FyZWEgKG3CsilgID0gYXJlYWNvbnN0LAogICAgUGFycXVlYWRlcm9zID0gcGFycXVlYWRlcm9zLCBCYcOxb3MgPSBiYW5pb3MsIEhhYml0YWNpb25lcyA9IGhhYml0YWNpb25lcywKICAgIGBQcmVjaW8gbW9kZWxvIChNKWAgPSByb3VuZChwcmVjaW9fbW9kZWxvLCAxKSwKICAgIGBNYXJnZW4gY3LDqWRpdG8gKE0pYCA9IHJvdW5kKG1hcmdlbl9jcmVkaXRvLCAxKSwKICAgIGBTY29yZSBwZXJmaWxgID0gcm91bmQoc2NvcmVfcGVyZmlsLCAzKQogICkKa2JsX3Byb2Yob2ZlcnRhczFfc2hvdywgIlRhYmxhIDEzLiBUb3AgNSBkZSBvZmVydGFzIHBvdGVuY2lhbGVzIOKAlCBWaXZpZW5kYSAxIiwgMikKYGBgCgpgYGB7ciBtYXBhLW9mZXJ0YXMtbm9ydGV9Cm1hcGFfb2ZlcnRhcyhvZmVydGFzMSwgIlRvcCA1IOKAlCBWaXZpZW5kYSAxIikKYGBgCgpMYSBwcmlvcmlkYWQgaW5pY2lhbCBlcyBsYSBvZmVydGEgKipJRCBgciBvZmVydGFzMSRpZFsxXWAqKiwgdWJpY2FkYSBlbiAqKmByIG9mZXJ0YXMxJGJhcnJpb1sxXWAqKiwgcG9ycXVlIHByZXNlbnRhIGxhIG1lbm9yIGRpc3RhbmNpYSBtdWx0aXZhcmlhYmxlIGFsIHBlcmZpbCBlbnRyZSBsb3MgaW5tdWVibGVzIGVsZWdpYmxlcy4gU3UgcHJlY2lvIHB1YmxpY2FkbyBlcyBgciBmbXRfbWlsbChvZmVydGFzMSRwcmVjaW9tWzFdKWAsIGRlamFuZG8gdW4gbWFyZ2VuIGRlIGByIGZtdF9taWxsKG9mZXJ0YXMxJG1hcmdlbl9jcmVkaXRvWzFdKWAgZnJlbnRlIGFsIGNyw6lkaXRvLiBVbiBgZ2FwX21vZGVsb2AgcG9zaXRpdm8gaW5kaWNhIHF1ZSBlbCBwcmVjaW8gcHVibGljYWRvIGVzdMOhIHBvciBkZWJham8gZGUgbG8gcXVlIHByZWRpY2UgZWwgbW9kZWxvIHBhcmEgc3VzIGNhcmFjdGVyw61zdGljYXM7IHNlIGludGVycHJldGEgY29tbyBzZcOxYWwgcGFyYSBpbnZlc3RpZ2FyIHVuYSBwb3NpYmxlIG9wb3J0dW5pZGFkLCBubyBjb21vIHBydWViYSBkZSBzdWJ2YWxvcmFjacOzbi4KCiMgU29saWNpdHVkIDIg4oCUIEFwYXJ0YW1lbnRvIGVuIFpvbmEgU3VyCgojIyBGaWx0cm8gaW5pY2lhbCB5IHZlcmlmaWNhY2nDs24KCkxhIHNlZ3VuZGEgc29saWNpdHVkIGV4aWdlOiAqKkFwYXJ0YW1lbnRvKiosICoqWm9uYSBTdXIqKiwgKiozMDAgbcKyKiosICoqMyBwYXJxdWVhZGVyb3MqKiwgKiozIGJhw7FvcyoqLCAqKjUgaGFiaXRhY2lvbmVzKiosICoqZXN0cmF0byA1IG8gNioqIHkgY3LDqWRpdG8gbcOheGltbyBkZSAqKiQ4NTAgbWlsbG9uZXMqKi4KCmBgYHtyIGZpbHRyby1zdXJ9CmJhc2UyIDwtIHZpdmllbmRhIHw+CiAgZmlsdGVyKHRpcG8gPT0gIkFwYXJ0YW1lbnRvIiwgem9uYSA9PSAiWm9uYSBTdXIiKQoKaGVhZChiYXNlMiwgMykgfD4KICBrYmxfcHJvZigiVGFibGEgMTQuIFByaW1lcm9zIHRyZXMgcmVnaXN0cm9zIGRlIGFwYXJ0YW1lbnRvcyBlbiBab25hIFN1ciIsIDIpCmBgYAoKYGBge3IgY29udHJvbC1maWx0cm8tc3VyfQpjb250cm9sMiA8LSBiaW5kX3Jvd3MoCiAgYmFzZTIgfD4gY291bnQodGlwbywgbmFtZSA9ICJuIikgfD4gbXV0YXRlKENvbnRyb2wgPSAiVGlwbyIsIENhdGVnb3JpYSA9IHRpcG8pIHw+IHNlbGVjdChDb250cm9sLCBDYXRlZ29yaWEsIG4pLAogIGJhc2UyIHw+IGNvdW50KHpvbmEsIG5hbWUgPSAibiIpIHw+IG11dGF0ZShDb250cm9sID0gIlpvbmEiLCBDYXRlZ29yaWEgPSB6b25hKSB8PiBzZWxlY3QoQ29udHJvbCwgQ2F0ZWdvcmlhLCBuKSwKICBiYXNlMiB8PiBjb3VudChlc3RyYXRvLCBuYW1lID0gIm4iKSB8PiBtdXRhdGUoQ29udHJvbCA9ICJFc3RyYXRvIiwgQ2F0ZWdvcmlhID0gYXMuY2hhcmFjdGVyKGVzdHJhdG8pKSB8PiBzZWxlY3QoQ29udHJvbCwgQ2F0ZWdvcmlhLCBuKQopCmtibF9wcm9mKGNvbnRyb2wyLCAiVGFibGEgMTUuIFZlcmlmaWNhY2nDs24gZGVsIGZpbHRybyBpbmljaWFsIOKAlCBTb2xpY2l0dWQgMiIsIDApCmBgYAoKRWwgZmlsdHJvIHByb2R1Y2UgYHIgbnJvdyhiYXNlMilgIGFwYXJ0YW1lbnRvcyBjbGFzaWZpY2Fkb3MgY29tbyBab25hIFN1cjsgYHIgbnJvdyhiYXNlMl9tb2RlbClgIHRpZW5lbiBkYXRvcyBjb21wbGV0b3MgZW4gbGFzIHZhcmlhYmxlcyBkZWwgbW9kZWxvLgoKIyMgTWFwYSBkZSB0b2RhcyBsYXMgb2ZlcnRhcyB5IGNvbnNpc3RlbmNpYSBnZW9ncsOhZmljYQoKYGBge3IgbWFwYS1zdXJ9Cm1hcGFfc2VnbWVudG8oYmFzZTIsICJBcGFydGFtZW50b3Mg4oCUIFpvbmEgU3VyIikKYGBgCgpTZSBpZGVudGlmaWNhbiBgciBzdW0oZ2VvMiRnZW9fYXRpcGljbywgbmEucm0gPSBUUlVFKWAgcHVudG9zIGdlb2dyw6FmaWNhbWVudGUgYXTDrXBpY29zIHNvYnJlIGByIG5yb3coZ2VvMilgIG9ic2VydmFjaW9uZXMgbWFwZWFibGVzLiBBbCBpZ3VhbCBxdWUgZW4gbGEgWm9uYSBOb3J0ZSwgZXN0b3MgcHVudG9zIGRlYmVuIHJldmlzYXJzZSwgcGVybyBubyByZWNvZGlmaWNhcnNlIGF1dG9tw6F0aWNhbWVudGUgc2luIHVuYSBjYXBhIGdlb2dyw6FmaWNhIG9maWNpYWwgZGUgem9uYXMuCgojIyBBbsOhbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGRhdG9zCgpgYGB7ciBjb3Itc3VyLXRhYmxhfQpjb3IyX3Nob3cgPC0gY29yMiB8PgogIG11dGF0ZShhY3Jvc3MoYyhQZWFyc29uLCBTcGVhcm1hbiksIH5yb3VuZCgueCwgMykpKQprYmxfcHJvZihjb3IyX3Nob3csICJUYWJsYSAxNi4gQ29ycmVsYWNpw7NuIGRlbCBwcmVjaW8gY29uIGxvcyBwcmVkaWN0b3JlcyDigJQgQXBhcnRhbWVudG9zIFpvbmEgU3VyIiwgMykKYGBgCgpMYSBhc29jaWFjacOzbiBsaW5lYWwgYWJzb2x1dGEgbcOhcyBhbHRhIGNvbiBlbCBwcmVjaW8gY29ycmVzcG9uZGUgYSAqKmByIGNvcjIkVmFyaWFibGVbMV1gKiosIGNvbiBQZWFyc29uID0gYHIgZm10X251bShjb3IyJFBlYXJzb25bMV0sIDMpYCwgcmVsYWNpw7NuIGByIGNvcjIkSW50ZW5zaWRhZFsxXWAgeSBgciBjb3IyJERpcmVjY2lvblsxXWAuCgpgYGB7ciBoZWF0bWFwLXN1cn0KcGxvdF9seSgKICB4ID0gY29sbmFtZXMoY29yX21hdDIpLCB5ID0gcm93bmFtZXMoY29yX21hdDIpLCB6ID0gY29yX21hdDIsCiAgdHlwZSA9ICJoZWF0bWFwIiwgem1pbiA9IC0xLCB6bWF4ID0gMSwKICBob3ZlcnRlbXBsYXRlID0gIlg6ICV7eH08YnI+WTogJXt5fTxicj5yOiAle3o6LjNmfTxleHRyYT48L2V4dHJhPiIKKSB8PgogIGxheW91dCh0aXRsZSA9ICJNYXRyaXogZGUgY29ycmVsYWNpw7NuIOKAlCBBcGFydGFtZW50b3MgWm9uYSBTdXIiLCB4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksIHlheGlzID0gbGlzdCh0aXRsZSA9ICIiKSkKYGBgCgpgYGB7ciBzY2F0dGVyLXN1cn0KcF9hcmVhMiA8LSBnZ3Bsb3QoYmFzZTJfbW9kZWwsIGFlcyhhcmVhY29uc3QsIHByZWNpb20pKSArCiAgZ2VvbV9wb2ludChhZXModGV4dCA9IHBhc3RlMCgKICAgICJJRDogIiwgaWQsCiAgICAiPGJyPkJhcnJpbzogIiwgYmFycmlvLAogICAgIjxicj7DgXJlYTogIiwgYXJlYWNvbnN0LCAiIG3CsiIsCiAgICAiPGJyPlByZWNpbzogJCIsIHByZWNpb20sICIgTSIsCiAgICAiPGJyPkVzdHJhdG86ICIsIGVzdHJhdG8KICApKSwgYWxwaGEgPSAwLjUwKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFKSArCiAgbGFicygKICAgIHRpdGxlID0gIlByZWNpbyB2cy4gw6FyZWEgY29uc3RydWlkYSDigJQgQXBhcnRhbWVudG9zIFpvbmEgU3VyIiwKICAgIHggPSAiw4FyZWEgY29uc3RydWlkYSAobcKyKSIsIHkgPSAiUHJlY2lvIChtaWxsb25lcyBDT1ApIgogICkgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpCgpnZ3Bsb3RseShwX2FyZWEyLCB0b29sdGlwID0gInRleHQiKQpgYGAKCiMjIyBFZmVjdG8gY29udGV4dHVhbCBkZSBsYSB6b25hCgpgYGB7ciB6b25hLWFwYXJ0YW1lbnRvc30Ka2JsX3Byb2Yoem9uZV9hcHRvLCAiVGFibGEgMTcuIFByZWNpbyBkZSBhcGFydGFtZW50b3MgcG9yIHpvbmEg4oCUIGNvbnRleHRvIGRlIG1lcmNhZG8iLCAxKQpgYGAKCmBgYHtyIHBsb3Qtem9uYS1hcGFydGFtZW50b3N9CnBfejIgPC0gdml2aWVuZGEgfD4KICBmaWx0ZXIodGlwbyA9PSAiQXBhcnRhbWVudG8iLCAhaXMubmEoem9uYSksICFpcy5uYShwcmVjaW9tKSkgfD4KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKHpvbmEsIHByZWNpb20sIEZVTiA9IG1lZGlhbiksIHkgPSBwcmVjaW9tKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLmFscGhhID0gMC4yMCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBkZWwgcHJlY2lvIGRlIGFwYXJ0YW1lbnRvcyBwb3Igem9uYSIsIHggPSAiWm9uYSIsIHkgPSAiUHJlY2lvIChtaWxsb25lcyBDT1ApIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI1LCBoanVzdCA9IDEpKQpnZ3Bsb3RseShwX3oyKQpgYGAKCiMjIEVzdGltYWNpw7NuIGRlbCBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgbcO6bHRpcGxlCgpgYGB7ciBtb2RlbG8tc3VyfQptMl9mdWxsIDwtIGxtKAogIHByZWNpb20gfiBhcmVhY29uc3QgKyBlc3RyYXRvICsgaGFiaXRhY2lvbmVzICsgcGFycXVlYWRlcm9zICsgYmFuaW9zLAogIGRhdGEgPSBiYXNlMl9tb2RlbAopCnN1bW1hcnkobTJfZnVsbCkKYGBgCgojIyMgQ29lZmljaWVudGVzIGUgaW50ZXJwcmV0YWNpw7NuCgpgYGB7ciBjb2VmLXN1cn0KY29lZjJfdGFiIDwtIGNvZWYyIHw+CiAgbXV0YXRlKAogICAgZXN0aW1hdGUgPSByb3VuZChlc3RpbWF0ZSwgMyksIHN0ZC5lcnJvciA9IHJvdW5kKHN0ZC5lcnJvciwgMyksCiAgICBjb25mLmxvdyA9IHJvdW5kKGNvbmYubG93LCAzKSwgY29uZi5oaWdoID0gcm91bmQoY29uZi5oaWdoLCAzKSwKICAgIHAudmFsdWUgPSBpZmVsc2UocC52YWx1ZSA8IDAuMDAxLCAiPDAsMDAxIiwgZm9ybWF0KHJvdW5kKHAudmFsdWUsIDMpLCBkZWNpbWFsLm1hcmsgPSAiLCIpKQogICkgfD4KICByZW5hbWUoCiAgICBFc3RpbWFjaW9uID0gZXN0aW1hdGUsIGBFcnJvciBlc3TDoW5kYXJgID0gc3RkLmVycm9yLAogICAgYElDOTUlIGluZmVyaW9yYCA9IGNvbmYubG93LCBgSUM5NSUgc3VwZXJpb3JgID0gY29uZi5oaWdoLAogICAgYHAtdmFsb3JgID0gcC52YWx1ZSwgYFNpZ25pZmljYXRpdm8gYWwgNSVgID0gU2lnbmlmaWNhdGl2b181cGN0LAogICAgSW50ZXJwcmV0YWNpw7NuID0gSW50ZXJwcmV0YWNpb24KICApCmtibF9wcm9mKGNvZWYyX3RhYiwgIlRhYmxhIDE4LiBDb2VmaWNpZW50ZXMgZGVsIG1vZGVsbyBjb21wbGV0byDigJQgQXBhcnRhbWVudG9zIFpvbmEgU3VyIiwgMykKYGBgCgpFbCBtb2RlbG8gY29tcGxldG8gb2J0aWVuZSAkUl4yID0gYHIgZm10X251bShzMiRyLnNxdWFyZWQsIDMpYCQgeSAkUl4yX3than0gPSBgciBmbXRfbnVtKHMyJGFkai5yLnNxdWFyZWQsIDMpYCQsIHBvciBsbyBxdWUgZXhwbGljYSBgciBmbXRfcGN0KHMyJHIuc3F1YXJlZClgIGRlIGxhIHZhcmlhYmlsaWRhZCBkZWwgcHJlY2lvIGVuIGFwYXJ0YW1lbnRvcyBkZSBab25hIFN1ci4gTGEgaW50ZXJwcmV0YWNpw7NuIGRlIGNhZGEgcGVuZGllbnRlIGVzIGNvbmRpY2lvbmFsIGEgbGFzIGRlbcOhcyBjYXJhY3RlcsOtc3RpY2FzLCBsbyBxdWUgZXZpdGEgY29uZnVuZGlyIHVuYSBjb3JyZWxhY2nDs24gYml2YXJpYWRhIGNvbiB1biBlZmVjdG8gcGFyY2lhbCBkZW50cm8gZGVsIG1vZGVsby4KCiMjIENvbXBhcmFjacOzbiBkZSBtb2RlbG9zIHkgdmFsaWRhY2nDs24gY3J1emFkYQoKYGBge3IgY29tcGFyYWNpb24tc3VyfQpjb21wMl9zaG93IDwtIGNvbXAyIHw+CiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfnJvdW5kKC54LCAzKSkpCmtibF9wcm9mKGNvbXAyX3Nob3csICJUYWJsYSAxOS4gQ29tcGFyYWNpw7NuIGRlIG1vZGVsb3Mg4oCUIEFwYXJ0YW1lbnRvcyBab25hIFN1ciIsIDMpCmBgYAoKRWwgbW9kZWxvIHNlbGVjY2lvbmFkbyBwYXJhIHByZWRpY2Npw7NuIGVzICoqYHIgYmVzdDJfbmFtZWAqKiwgYWwgb2ZyZWNlciBlbCBtZWpvciBiYWxhbmNlIGVudHJlIGVycm9yIGRlIHZhbGlkYWNpw7NuIHkgY29tcGxlamlkYWQgZW50cmUgbGFzIGVzcGVjaWZpY2FjaW9uZXMgbcO6bHRpcGxlcyBldmFsdWFkYXMuCgojIyBWYWxpZGFjacOzbiBkZSBzdXB1ZXN0b3MKCmBgYHtyIHN1cHVlc3Rvcy1zdXJ9CmtibF9wcm9mKGRpYWcyLCAiVGFibGEgMjAuIERpYWduw7NzdGljbyBkZSBzdXB1ZXN0b3Mg4oCUIG1vZGVsbyBzZWxlY2Npb25hZG8sIEFwYXJ0YW1lbnRvcyBab25hIFN1ciIsIDIpCmBgYAoKYGBge3IgZGlhZ25vc3RpY29zLWdyYWZpY29zLXN1ciwgZmlnLmhlaWdodD03fQpwYXIobWZyb3cgPSBjKDIsIDIpKQpwbG90KG0yX2ZpbmFsKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCmBgYHtyIHJvYnVzdG9zLXN1cn0Ka2JsX3Byb2Yocm9iMiwgIlRhYmxhIDIxLiBTZW5zaWJpbGlkYWQgaW5mZXJlbmNpYWwgY29uIGVycm9yZXMgZXN0w6FuZGFyIHJvYnVzdG9zIEhDMyDigJQgQXBhcnRhbWVudG9zIFpvbmEgU3VyIiwgNCkKYGBgCgpDdWFuZG8gbGEgaGV0ZXJvY2VkYXN0aWNpZGFkIGVzIHNpZ25pZmljYXRpdmEsIGxvcyBlcnJvcmVzIGVzdMOhbmRhciBIQzMgcHJvcG9yY2lvbmFuIHVuYSB2ZXJpZmljYWNpw7NuIGRlIHNlbnNpYmlsaWRhZCBkZSBsYSBpbmZlcmVuY2lhLiBTaSBsYXMgY29uY2x1c2lvbmVzIGRlIHNpZ25pZmljYW5jaWEgY2FtYmlhbiBkZSBmb3JtYSBzdXN0YW5jaWFsIGZyZW50ZSBhIE1DTywgbGEgZGVjaXNpw7NuIGRlYmUgYXBveWFyc2UgbcOhcyBlbiBwcmVkaWNjacOzbiB5IHZhbGlkYWNpw7NuIHF1ZSBlbiBwLXZhbG9yZXMgaW5kaXZpZHVhbGVzLgoKIyMgRXN0aW1hY2nDs24gcHVudHVhbCBlIGludGVydmFsbyBkZSBwcmVkaWNjacOzbiDigJQgVml2aWVuZGEgMgoKYGBge3IgcHJlZGljY2lvbi1zdXJ9CnByZWQyX3Nob3cgPC0gcHJlZDIgfD4KICB0cmFuc211dGUoCiAgICBFc3RyYXRvID0gZXN0cmF0bywKICAgIGBFc3RpbWFjacOzbiBwdW50dWFsIChNKWAgPSByb3VuZChmaXQsIDEpLAogICAgYEzDrW1pdGUgaW5mZXJpb3IgSVA5NSUgKE0pYCA9IHJvdW5kKGx3ciwgMSksCiAgICBgTMOtbWl0ZSBzdXBlcmlvciBJUDk1JSAoTSlgID0gcm91bmQodXByLCAxKSwKICAgIGBDcsOpZGl0byBtw6F4aW1vIChNKWAgPSBwcmVzdXB1ZXN0bywKICAgIGBNYXJnZW4gdnMuIGVzdGltYWNpw7NuIChNKWAgPSByb3VuZChtYXJnZW4sIDEpLAogICAgRGVjaXNpw7NuID0gZGVjaXNpb25fcHJlc3VwdWVzdG8KICApCmtibF9wcm9mKHByZWQyX3Nob3csICJUYWJsYSAyMi4gUHJlZGljY2nDs24gZGUgcHJlY2lvIHBhcmEgbGEgVml2aWVuZGEgMiIsIDEpCmBgYAoKTGEgZG9ibGUgcHJlZGljY2nDs24gcGFyYSBlc3RyYXRvcyA1IHkgNiBtdWVzdHJhIGVsIGVmZWN0byBkZSB1bmEgZGUgbGFzIGNvbmRpY2lvbmVzIG3DoXMgcmVsZXZhbnRlcyBkZWwgY2xpZW50ZS4gTGEgZXN0aW1hY2nDs24gcHVudHVhbCBkZWJlIGxlZXJzZSBqdW50byBjb24gZWwgaW50ZXJ2YWxvIGRlbCA5NSUgeSBjb24gbGEgZGlzcG9uaWJpbGlkYWQgcmVhbCBkZSBvZmVydGFzIGJham8gJDg1MCBtaWxsb25lcy4KCiMjIENpbmNvIG9mZXJ0YXMgcG90ZW5jaWFsZXMgZGVudHJvIGRlbCBjcsOpZGl0bwoKYGBge3Igb2ZlcnRhcy1zdXJ9Cm9mZXJ0YXMyX3Nob3cgPC0gb2ZlcnRhczIgfD4KICB0cmFuc211dGUoCiAgICBSYW5raW5nLCBJRCA9IGlkLCBCYXJyaW8gPSBiYXJyaW8sIEVzdHJhdG8gPSBlc3RyYXRvLAogICAgYFByZWNpbyBvZmVydGEgKE0pYCA9IHByZWNpb20sIGDDgXJlYSAobcKyKWAgPSBhcmVhY29uc3QsCiAgICBQYXJxdWVhZGVyb3MgPSBwYXJxdWVhZGVyb3MsIEJhw7FvcyA9IGJhbmlvcywgSGFiaXRhY2lvbmVzID0gaGFiaXRhY2lvbmVzLAogICAgYFByZWNpbyBtb2RlbG8gKE0pYCA9IHJvdW5kKHByZWNpb19tb2RlbG8sIDEpLAogICAgYE1hcmdlbiBjcsOpZGl0byAoTSlgID0gcm91bmQobWFyZ2VuX2NyZWRpdG8sIDEpLAogICAgYFNjb3JlIHBlcmZpbGAgPSByb3VuZChzY29yZV9wZXJmaWwsIDMpCiAgKQprYmxfcHJvZihvZmVydGFzMl9zaG93LCAiVGFibGEgMjMuIFRvcCA1IGRlIG9mZXJ0YXMgcG90ZW5jaWFsZXMg4oCUIFZpdmllbmRhIDIiLCAyKQpgYGAKCmBgYHtyIG1hcGEtb2ZlcnRhcy1zdXJ9Cm1hcGFfb2ZlcnRhcyhvZmVydGFzMiwgIlRvcCA1IOKAlCBWaXZpZW5kYSAyIikKYGBgCgpMYSBwcmltZXJhIGFsdGVybmF0aXZhIGVzIGxhIG9mZXJ0YSAqKklEIGByIG9mZXJ0YXMyJGlkWzFdYCoqLCBlbiAqKmByIG9mZXJ0YXMyJGJhcnJpb1sxXWAqKiwgY29uIHByZWNpbyBkZSBgciBmbXRfbWlsbChvZmVydGFzMiRwcmVjaW9tWzFdKWAgeSBtYXJnZW4gZGUgYHIgZm10X21pbGwob2ZlcnRhczIkbWFyZ2VuX2NyZWRpdG9bMV0pYCBmcmVudGUgYWwgY3LDqWRpdG8uIExhcyBkZW3DoXMgb3BjaW9uZXMgcGVybWl0ZW4gY29tcGFyYXIgY3XDoW50byBzZSBzYWNyaWZpY2EgbyBnYW5hIGVuIMOhcmVhLCBwYXJxdWVhZGVyb3MsIGJhw7FvcyBvIGhhYml0YWNpb25lcyBzaW4gZXhjZWRlciBlbCBwcmVzdXB1ZXN0by4KCiMgQ29tcGFyYWNpw7NuIGVqZWN1dGl2YSBkZSBsYXMgZG9zIHNvbGljaXR1ZGVzCgpgYGB7ciByZXN1bWVuLWNvbXBhcmF0aXZvfQpyZXN1bWVuX2ZpbmFsIDwtIGRhdGEuZnJhbWUoCiAgU29saWNpdHVkID0gYygiVml2aWVuZGEgMSIsICJWaXZpZW5kYSAyIiksCiAgU2VnbWVudG8gPSBjKCJDYXNhIOKAlCBab25hIE5vcnRlIiwgIkFwYXJ0YW1lbnRvIOKAlCBab25hIFN1ciIpLAogIFByZXN1cHVlc3RvX00gPSBjKDM1MCwgODUwKSwKICBOX3NlZ21lbnRvID0gYyhucm93KGJhc2UxKSwgbnJvdyhiYXNlMikpLAogIE5fbW9kZWxvID0gYyhucm93KGJhc2UxX21vZGVsKSwgbnJvdyhiYXNlMl9tb2RlbCkpLAogIFIyX21vZGVsb19jb21wbGV0byA9IGMoczEkci5zcXVhcmVkLCBzMiRyLnNxdWFyZWQpLAogIFIyX2FqdXN0YWRvID0gYyhzMSRhZGouci5zcXVhcmVkLCBzMiRhZGouci5zcXVhcmVkKSwKICBNb2RlbG9fc2VsZWNjaW9uYWRvID0gYyhiZXN0MV9uYW1lLCBiZXN0Ml9uYW1lKSwKICBQcmVkaWNjaW9uX21pbl9NID0gYyhtaW5maXQxLCBtaW5maXQyKSwKICBQcmVkaWNjaW9uX21heF9NID0gYyhtYXhmaXQxLCBtYXhmaXQyKSwKICBPZmVydGFzX3JlY29tZW5kYWRhcyA9IGMobnJvdyhvZmVydGFzMSksIG5yb3cob2ZlcnRhczIpKQopIHw+CiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfnJvdW5kKC54LCAzKSkpCmtibF9wcm9mKHJlc3VtZW5fZmluYWwsICJUYWJsYSAyNC4gU8OtbnRlc2lzIGNvbXBhcmF0aXZhIHBhcmEgbGEgdG9tYSBkZSBkZWNpc2nDs24iLCAzKQpgYGAKCkVuIGFtYm9zIHNlZ21lbnRvcywgZWwgw6FyZWEgY29uc3RydWlkYSwgZWwgZXN0cmF0byB5IGxhIGRvdGFjacOzbiBmw61zaWNhIGNhcHR1cmFuIHVuYSBwYXJ0ZSBzdXN0YW50aXZhIGRlbCB2YWxvciwgcGVybyBubyBhZ290YW4gbGEgZXhwbGljYWNpw7NuIGRlbCBwcmVjaW8uIExhIGNvbXBhcmFjacOzbiBlbnRyZSBlbCBtb2RlbG8gZGUgc29sbyDDoXJlYSB5IGVsIG1vZGVsbyBtw7psdGlwbGUgcGVybWl0ZSBjdWFudGlmaWNhciBlbCB2YWxvciBhZGljaW9uYWwgZGUgY29uc2lkZXJhciBzaW11bHTDoW5lYW1lbnRlIHZhcmlhcyBjYXJhY3RlcsOtc3RpY2FzOyBsYSB2YWxpZGFjacOzbiBjcnV6YWRhIGV2aXRhIHNvYnJldmFsb3JhciBtZWpvcmFzIHF1ZSBzb2xvIGFwYXJlY2VuIGRlbnRybyBkZSBsYSBtdWVzdHJhLgoKIyBDb25jbHVzaW9uZXMgeSByZWNvbWVuZGFjaW9uZXMKCioqMS4gRWwgZmlsdHJvIHkgbGEgZGVwdXJhY2nDs24gc29uIGRldGVybWluYW50ZXMgcGFyYSBsYSB2YWxpZGV6IGRlbCBhbsOhbGlzaXMuKiogU2UgdHJhYmFqw7MgcG9yIHNlZ21lbnRvcyBob21vZ8OpbmVvcyBkZSB0aXBvIHkgem9uYSwgZXZpdGFuZG8gbWV6Y2xhciBtZXJjYWRvcyBlc3RydWN0dXJhbG1lbnRlIGRpZmVyZW50ZXMuIExvcyBjYXNvcyBpbmNvbXBsZXRvcyBzZSBleGNsdXllcm9uIMO6bmljYW1lbnRlIGN1YW5kbyBmYWx0YWJhIGFsZ3VuYSB2YXJpYWJsZSBuZWNlc2FyaWEgcGFyYSBsYSByZWdyZXNpw7NuLgoKKioyLiBMYSByZWxhY2nDs24gcHJlY2lv4oCTY2FyYWN0ZXLDrXN0aWNhcyBlcyBtdWx0aXZhcmlhYmxlLioqIEVsIMOhcmVhIHRpZW5lIHVuYSByZWxhY2nDs24gcmVsZXZhbnRlIGNvbiBlbCBwcmVjaW8sIHBlcm8gZXN0cmF0bywgYmHDsW9zLCBoYWJpdGFjaW9uZXMgeSBwYXJxdWVhZGVyb3MgYXBvcnRhbiBpbmZvcm1hY2nDs24gYWRpY2lvbmFsLiBQb3IgZWxsbywgdW5hIHZhbG9yYWNpw7NuIGJhc2FkYSBzb2xvIGVuIHByZWNpbyBwb3IgbWV0cm8gY3VhZHJhZG8gcHVlZGUgc2VyIGluc3VmaWNpZW50ZS4KCioqMy4gTGEgc2lnbmlmaWNhbmNpYSBlc3RhZMOtc3RpY2Egbm8gZGViZSBjb25mdW5kaXJzZSBjb24gaW1wb3J0YW5jaWEgZWNvbsOzbWljYS4qKiBVbiBjb2VmaWNpZW50ZSBwdWVkZSBzZXIgZXN0YWTDrXN0aWNhbWVudGUgbm8gc2lnbmlmaWNhdGl2byBwb3Igc29sYXBhbWllbnRvIGVudHJlIHByZWRpY3RvcmVzLCBhdW4gY3VhbmRvIGxhIGNhcmFjdGVyw61zdGljYSBzZWEgY29tZXJjaWFsbWVudGUgaW1wb3J0YW50ZS4gTGEgbGVjdHVyYSBjb25qdW50YSBkZSBpbnRlcnZhbG9zLCBWSUYsICRSXjIkLCB2YWxpZGFjacOzbiBjcnV6YWRhIHkgY29udGV4dG8gZGVsIG5lZ29jaW8gZXMgbcOhcyBpbmZvcm1hdGl2YSBxdWUgdW4gcC12YWxvciBhaXNsYWRvLgoKKio0LiBQYXJhIGxhIFZpdmllbmRhIDEqKiwgZWwgbW9kZWxvIHNpdMO6YSBlbCBwcmVjaW8gZXNwZXJhZG8gZW50cmUgYHIgZm10X21pbGwobWluZml0MSlgIHkgYHIgZm10X21pbGwobWF4Zml0MSlgIHNlZ8O6biBlbCBlc3RyYXRvIGNvbnNpZGVyYWRvLiBMYXMgY2luY28gYWx0ZXJuYXRpdmFzIHNlbGVjY2lvbmFkYXMgY3VtcGxlbiBlbCBsw61taXRlIGZpbmFuY2llcm8geSBkZWJlbiBwcmlvcml6YXJzZSBwb3Igc2ltaWxpdHVkIGRlIHBlcmZpbCwgZW1wZXphbmRvIHBvciBsYSBvcGNpw7NuIElEIGByIG9mZXJ0YXMxJGlkWzFdYC4KCioqNS4gUGFyYSBsYSBWaXZpZW5kYSAyKiosIGVsIHByZWNpbyBlc3BlcmFkbyBzZSB1YmljYSBlbnRyZSBgciBmbXRfbWlsbChtaW5maXQyKWAgeSBgciBmbXRfbWlsbChtYXhmaXQyKWAgcGFyYSBlc3RyYXRvcyA1IHkgNi4gU2UgcHJvcG9uZW4gY2luY28gYWx0ZXJuYXRpdmFzIGRlbnRybyBkZWwgY3LDqWRpdG8sIGVuY2FiZXphZGFzIHBvciBsYSBvcGNpw7NuIElEIGByIG9mZXJ0YXMyJGlkWzFdYC4KCioqNi4gTG9zIGludGVydmFsb3MgZGUgcHJlZGljY2nDs24gc29uIGVzZW5jaWFsZXMuKiogVW5hIHNvbGEgZXN0aW1hY2nDs24gcHVudHVhbCBwdWVkZSB0cmFuc21pdGlyIHVuYSBmYWxzYSBzZW5zYWNpw7NuIGRlIHByZWNpc2nDs24uIEVsIGludGVydmFsbyBhbCA5NSUgcmVmbGVqYSBsYSBpbmNlcnRpZHVtYnJlIGluaGVyZW50ZSBhIHVuYSB2aXZpZW5kYSBudWV2YSB5IHJlc3BhbGRhIHVuYSBuZWdvY2lhY2nDs24gY29uIG1hcmdlbiBkZSBzZWd1cmlkYWQuCgoqKjcuIExhIGxvY2FsaXphY2nDs24gZGViZSBtb2RlbGFyc2UgY29uIG1heW9yIGdyYW51bGFyaWRhZCBlbiBmdXR1cmFzIHZlcnNpb25lcy4qKiBMYSBkaXNwZXJzacOzbiBkZSBjb29yZGVuYWRhcyB5IGxhIGhldGVyb2dlbmVpZGFkIHBvciBiYXJyaW8gc3VnaWVyZW4gcXVlIHVuYSB2ZXJzacOzbiBtw6FzIGF2YW56YWRhIGRlYmVyw61hIGluY29ycG9yYXIgYmFycmlvLCBsYXRpdHVkL2xvbmdpdHVkIG8gbcOpdG9kb3MgZXNwYWNpYWxlcy4gVW4gZXN0dWRpbyBjb2xvbWJpYW5vIHJlY2llbnRlIG11ZXN0cmEgcXVlIGF0cmlidXRvcyBlc3BhY2lhbGVzIHB1ZWRlbiBnZW5lcmFyIGRpZmVyZW5jaWFsZXMgZGUgcHJlY2lvIHF1ZSB1biBtb2RlbG8gcHVyYW1lbnRlIGbDrXNpY28gbm8gY2FwdHVyYSBjb21wbGV0YW1lbnRlIChBZ3VkZWxvIFRvcnJlcyBldCBhbC4sIDIwMjIpLgoKKio4LiBSZWNvbWVuZGFjacOzbiBmaW5hbCBhIEMmQS4qKiBVc2FyIGVsIHJhbmtpbmcgY3VhbnRpdGF0aXZvIHBhcmEgc2VsZWNjaW9uYXIgaW5tdWVibGVzIGEgdmlzaXRhciB5LCBhbnRlcyBkZSBjZXJyYXIgbGEgY29tcHJhLCB2ZXJpZmljYXIgZXN0YWRvIGVzdHJ1Y3R1cmFsLCBhY2FiYWRvcywgYW50aWfDvGVkYWQsIGFkbWluaXN0cmFjacOzbiwgc2VndXJpZGFkLCBhY2Nlc2liaWxpZGFkLCBkb2N1bWVudGFjacOzbiBqdXLDrWRpY2EgeSBjb25kaWNpb25lcyBkZSBuZWdvY2lhY2nDs24uIEVsIG1vZGVsbyByZWR1Y2UgaW5jZXJ0aWR1bWJyZSwgcGVybyBsYSBkZWNpc2nDs24gZmluYWwgZGViZSBpbnRlZ3JhciBldmlkZW5jaWEgZXN0YWTDrXN0aWNhIHkgZGViaWRhIGRpbGlnZW5jaWEgaW5tb2JpbGlhcmlhLgoKIyBMaW1pdGFjaW9uZXMKCkxhIGJhc2UgcmVwcmVzZW50YSBvZmVydGFzIG9ic2VydmFkYXMgZHVyYW50ZSB1biBwZXJpb2RvIGNvcnRvIHkgbm8gbmVjZXNhcmlhbWVudGUgcHJlY2lvcyBmaW5hbGVzIGRlIHRyYW5zYWNjacOzbi4gRWwgbW9kZWxvIHRhbXBvY28gZGlzcG9uZSBkZSBhbnRpZ8O8ZWRhZCwgZXN0YWRvIGRlIGNvbnNlcnZhY2nDs24sIGNhbGlkYWQgZGUgYWNhYmFkb3MsIHBpc28vYWx0dXJhIGNvbW8gdmFyaWFibGUgY29uc2lzdGVudGUgcGFyYSBhbWJvcyB0aXBvcywgYWRtaW5pc3RyYWNpw7NuLCB2aXN0YSwgZGlzdGFuY2lhIGEgc2VydmljaW9zIG5pIHZhcmlhYmxlcyBqdXLDrWRpY2FzLiBBZGVtw6FzLCBsYSBpbmRlcGVuZGVuY2lhIGVzcGFjaWFsIG5vIHNlIHZhbGlkYSBmb3JtYWxtZW50ZSBjb24gcG9sw61nb25vcyBvIG1hdHJpY2VzIGRlIHZlY2luZGFkLiBFc3RhcyBsaW1pdGFjaW9uZXMgZGViZW4gY29uc2lkZXJhcnNlIGFudGVzIGRlIGV4dHJhcG9sYXIgcmVzdWx0YWRvcyBhIG90cm9zIHBlcmlvZG9zIG8gem9uYXMuCgojIFJlZmVyZW5jaWFzCgpBZ3VkZWxvIFRvcnJlcywgSi4gRS4sIE9zcGluYSBFc3Bpbm96YSwgTy4gQS4sICYgR29uesOhbGV6IFZpcnZpZXNjYXMsIEUuIFAuICgyMDIyKS4gKkVsIHZhbG9yIGRlIGxhIHZpc3RhIHNvYnJlIGxhIGNpdWRhZDogZWwgY2FzbyBsb3MgYXBhcnRhbWVudG9zIGVuIEVudmlnYWRvKi4gKipDdWFkZXJub3MgZGUgVml2aWVuZGEgeSBVcmJhbmlzbW8qKi4gaHR0cHM6Ly9kb2kub3JnLzEwLjExMTQ0L2F2ZXJpYW5hLmN2dTE0LnZ2c2MKCkhldW1hbm4sIEMuLCBTY2hvbWFrZXIsIE0uLCAmIFNoYWxhYmguICgyMDIzKS4gKkludHJvZHVjdGlvbiB0byBTdGF0aXN0aWNzIGFuZCBEYXRhIEFuYWx5c2lzOiBXaXRoIEV4ZXJjaXNlcywgU29sdXRpb25zIGFuZCBBcHBsaWNhdGlvbnMgaW4gUiogKDJuZCBlZC4pLiBTcHJpbmdlci4gaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvOTc4LTMtMDMxLTExODMzLTMKCkt1aG4sIE0uLCAmIFNpbGdlLCBKLiAoMjAyMikuICpUaWR5IE1vZGVsaW5nIHdpdGggUiouIE/igJlSZWlsbHkgTWVkaWEuCgpXYW5nLCBZLiAoMjAyNCkuIFJlc2VhcmNoIG9uIGhvdXNpbmcgcHJpY2VzIGZvcmVjYXN0cyBiYXNlZCBvbiBhIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBJbiAqUHJvY2VlZGluZ3Mgb2YgdGhlIDFzdCBJbnRlcm5hdGlvbmFsIENvbmZlcmVuY2Ugb24gSW5ub3ZhdGlvbnMgaW4gQXBwbGllZCBNYXRoZW1hdGljcywgUGh5c2ljcyBhbmQgQXN0cm9ub215IChJQU1QQSAyMDI0KSogKHBwLiA1M+KAkzU5KS4gU2NpVGVQcmVzcy4gaHR0cHM6Ly9kb2kub3JnLzEwLjUyMjAvMDAxMjk5MTAwMDAwNDYwMQoKV2lja2hhbSwgSC4sIMOHZXRpbmtheWEtUnVuZGVsLCBNLiwgJiBHcm9sZW11bmQsIEcuICgyMDIzKS4gKlIgZm9yIERhdGEgU2NpZW5jZSogKDJuZCBlZC4pLiBP4oCZUmVpbGx5IE1lZGlhLgoKWmhvbmcsIFMuICgyMDI0KS4gRmFjdG9ycyBpbmZsdWVuY2luZyBob3VzaW5nIHByaWNlczogQSBjb21wYXJhdGl2ZSBzdHVkeSB1c2luZyBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBhbmQgcmFuZG9tIGZvcmVzdC4gKlRoZW9yZXRpY2FsIGFuZCBOYXR1cmFsIFNjaWVuY2UsIDUxKiwgNzPigJM3OS4gaHR0cHM6Ly9kb2kub3JnLzEwLjU0MjU0LzI3NTMtODgxOC81MS8yMDI0Q0gwMTc0CgojIyBNYXRlcmlhbCBhdWRpb3Zpc3VhbCBkZWwgY3Vyc28KCi0gKipNMVUyIOKAlCBSTE0gTm90YWNpw7NuOioqIGh0dHBzOi8veW91dHUuYmUvTUVscV8tZmQ0ZDQgIAotICoqTTFVMiDigJQgRXN0aW1hY2nDs24gUkxNOioqIGh0dHBzOi8veW91dHUuYmUvUGFoSGR3Tm1kUkUKCiMgQW5leG8gZGUgcmVwcm9kdWNpYmlsaWRhZAoKRWwgaW5mb3JtZSBmaWphIGBzZXQuc2VlZCg5MDQ0OTUxKWAgcGFyYSBxdWUgbGEgYXNpZ25hY2nDs24gZGUgcGxpZWd1ZXMgZGUgdmFsaWRhY2nDs24gY3J1emFkYSBzZWEgcmVwcm9kdWNpYmxlLiBMYSB2ZXJzacOzbiBleGFjdGEgZGUgUiB5IGRlIGxvcyBwYXF1ZXRlcyBwdWVkZSB2ZXJpZmljYXJzZSBjb246CgpgYGB7ciBzZXNzaW9uLWluZm99CnNlc3Npb25JbmZvKCkKYGBgCgo=