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.

1 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).

2 Datos, calidad y depuración

2.1 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.

2.2 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

3 Solicitud 1 — Casa en Zona Norte

3.1 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.

3.2 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.

3.3 Análisis exploratorio de datos

3.3.1 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")

3.3.2 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.

3.4 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

3.4.1 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 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 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 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 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.

3.5 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).

3.6 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)

par(mfrow = c(1, 1))

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

3.6.1 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).

3.7 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.

3.8 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.

4 Solicitud 2 — Apartamento en Zona Sur

4.1 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.

4.2 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.

4.3 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")

4.3.1 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)

4.4 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

4.4.1 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 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 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 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 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 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.

4.5 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.

4.6 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)

par(mfrow = c(1, 1))
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.

4.7 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.

4.8 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.

5 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.

6 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.

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

8 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

8.1 Material audiovisual del curso

9 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:

sessionInfo()
## 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=