1 Problema

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

2 Retos

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

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

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

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

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

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

3 Análisis Exploratorio de Datos

3.1 Carga del DataSet

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

3.2 Descripción del DataSet

diagnostico <- tibble(
  Variable  = names(vivienda),
  Tipo      = map_chr(vivienda, ~ class(.x)[1]),
  Registros = map_int(vivienda, ~ sum(!is.na(.x))),
  Faltantes = map_int(vivienda, ~ sum(is.na(.x))),
  pct_falt  = map_dbl(vivienda, ~ 100 * mean(is.na(.x))),
  Unicos    = map_int(vivienda, ~ n_distinct(.x[!is.na(.x)])),
  Ejemplo   = map_chr(vivienda, ~ as.character(.x[!is.na(.x)][1]))
)

diagnostico |>
  mutate(
    Completitud = cell_spec(barra(100 - pct_falt),
                            monospace = TRUE,
                            color = case_when(pct_falt == 0 ~ "#0f766e",
                                              pct_falt < 5  ~ "#5c8a86",
                                              pct_falt < 20 ~ "#b5751a",
                                              TRUE          ~ "#a33a3a")),
    `%` = cell_spec(sprintf("%.1f", pct_falt),
                    bold  = pct_falt >= 5,
                    color = case_when(pct_falt == 0 ~ "#0f766e",
                                      pct_falt < 5  ~ "#1c2b36",
                                      pct_falt < 20 ~ "#b5751a",
                                      TRUE          ~ "#a33a3a")),
    .after = Faltantes
  ) |>
  select(Variable, Tipo, Registros, Faltantes, `%`, Completitud,
         `Valores únicos` = Unicos, Ejemplo) |>
  tabla_estilo(
    titulo = "Tabla 1. Diagnóstico estructural de las variables del conjunto vivienda",
    grupos = c(" " = 1, "Naturaleza" = 1,
               "Completitud de la información" = 4, "Contenido" = 2),
    nota   = "Los valores únicos se calculan excluyendo los datos ausentes. El color indica la severidad de la ausencia: sin faltantes (verde), menos del 5% (neutro), entre 5% y 20% (ámbar) y más del 20% (rojo)."
  )
Tabla 1. Diagnóstico estructural de las variables del conjunto vivienda
Naturaleza
Completitud de la información
Contenido
Variable Tipo Registros Faltantes % Completitud Valores únicos Ejemplo
id numeric 8.319 3 0.0 █████████░ 8.319 1147
zona character 8.319 3 0.0 █████████░ 5 Zona Oriente
piso character 5.684 2.638 31.7 ██████░░░░ 12 02
estrato numeric 8.319 3 0.0 █████████░ 4 3
preciom numeric 8.320 2 0.0 █████████░ 539 250
areaconst numeric 8.319 3 0.0 █████████░ 652 70
parqueaderos numeric 6.717 1.605 19.3 ████████░░ 10 1
banios numeric 8.319 3 0.0 █████████░ 11 3
habitaciones numeric 8.319 3 0.0 █████████░ 11 6
tipo character 8.319 3 0.0 █████████░ 2 Casa
barrio character 8.319 3 0.0 █████████░ 436 20 de julio
longitud numeric 8.319 3 0.0 █████████░ 2.928 -76.51168
latitud numeric 8.319 3 0.0 █████████░ 3.679 3.43382
Nota. Los valores únicos se calculan excluyendo los datos ausentes. El color indica la severidad de la ausencia: sin faltantes (verde), menos del 5% (neutro), entre 5% y 20% (ámbar) y más del 20% (rojo).

La base de datos esta compuesta por 8.322 registros y 13 variables que combinan las características físicas de los inmuebles (área construida, habitaciones, baños, piso y parqueaderos), su ubicación (barrio, zona, longitud y latitud), el estrato socioeconómico y el precio de venta.

Todas las columnas registran 8.319 datos válidos y apenas tres filas aparecen vacías de manera sistemática, lo que apunta a registros en blanco.

El dataset presenta ausencias notables en dos variables: piso, con 31,7% de datos faltantes, y parqueaderos, con 19,3%.

En cuanto a los tipos de datos, hay dos que llaman la atención: piso quedó como tipo como texto, y estrato entró como numérico cuando en realidad es una categoría ordenada.

Por último, el alto número de valores unicos en la variable barrio (436) da para sospechar que se esconden errores de de tipo typo, que valdrá la pena revisar.

3.3 Revisión de registros vacios

La exploración anterior, evidencio una constante de tres registros vacios para cada variable. La Tabla 2. muestra estos registros.

# ¿Cuántos datos válidos tiene cada fila?
vivienda |>
  mutate(n_validos = rowSums(!is.na(across(everything())))) |>
  filter(n_validos <= 3) |>
  tabla_estilo(titulo = "Tabla 2. Registros con información prácticamente nula")
Tabla 2. Registros con información prácticamente nula
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud n_validos
0
0
330 1

Al no tener información, estos tres registros no aportan nada al análisis y si pueden generar problemas al momento de continuar. Por lo tanto,se opta por eliminarlos.

vivienda_01_filtro <- vivienda |>
  filter(rowSums(!is.na(across(everything()))) > 3) |>
  select(-any_of(c("n_validos", "registro_incompleto")))

cat("Originales:", nrow(vivienda),
    "| Eliminados:", nrow(vivienda) - nrow(vivienda_01_filtro),
    "| Restantes:", nrow(vivienda_01_filtro))
#> Originales: 8322 | Eliminados: 3 | Restantes: 8319

3.4 Conversión de variables

3.4.1 Variable Piso

La variable piso fue la única cuyo tipo asignado por defecto no correspondía con su naturaleza: figuraba como texto cuando en realidad expresa una cantidad. Comenzaremos explorando la variable piso y sus 12 valores únicos

vivienda_01_filtro |>
  count(piso, sort = TRUE, name = "Frecuencia") |>
  mutate(
    Porcentaje = sprintf("%.1f", 100 * Frecuencia / sum(Frecuencia)),
    `¿Convertible?` = case_when(
      is.na(piso) ~ cell_spec("Ausente", color = "#6b7a85", italic = TRUE),
      !is.na(suppressWarnings(as.numeric(piso))) ~ cell_spec("Sí", color = "#0f766e"),
      TRUE ~ cell_spec("No", color = "#a33a3a", bold = TRUE)
    )
  ) |>
  rename(Piso = piso) |>
  tabla_estilo(
    titulo = "Tabla 3. Valores registrados en la variable piso",
    nota   = "La última columna indica si el valor admite conversión numérica directa."
  )
Tabla 3. Valores registrados en la variable piso
Piso Frecuencia Porcentaje ¿Convertible?
2.635 31.7 Ausente
02 1.450 17.4
03 1.097 13.2
01 860 10.3
04 607 7.3
05 567 6.8
06 245 2.9
08 211 2.5
07 204 2.5
09 146 1.8
10 130 1.6
11 84 1.0
12 83 1.0
Nota. La última columna indica si el valor admite conversión numérica directa.

Los ceros a la izquierda hacen que la variable piso sea identificada como caracter, cuando en realidad es numérica discreta. Por lo tanto se va a cambiar su tipo a entero

vivienda_02_tipos <- vivienda_01_filtro |>
  mutate(piso = suppressWarnings(as.integer(piso)))

tibble(
  Etapa    = c("Antes de convertir", "Después de convertir", "Diferencia"),
  Ausentes = c(sum(is.na(vivienda_01_filtro$piso)),
               sum(is.na(vivienda_02_tipos$piso)),
               sum(is.na(vivienda_02_tipos$piso)) - sum(is.na(vivienda_01_filtro$piso)))
) |>
  tabla_estilo(titulo = "Tabla 4. Control de integridad en la conversión de piso")
Tabla 4. Control de integridad en la conversión de piso
Etapa Ausentes
Antes de convertir 2.635
Después de convertir 2.635
Diferencia 0

3.4.2 Variables id, zona, tipo y estrato

vivienda_02_tipos <- vivienda_01_filtro |>
  mutate(
    id      = as.character(id),                                # identificador
    piso    = suppressWarnings(as.integer(piso)),              # discreta
    zona    = factor(zona),                                    # nominal
    tipo    = factor(tipo),                                    # nominal
    estrato = factor(estrato, ordered = TRUE),                 # ordinal
    across(c(habitaciones, banios, parqueaderos), as.integer)  # conteos
  )

glimpse(vivienda_02_tipos)
#> Rows: 8,319
#> Columns: 13
#> $ id           <chr> "1147", "1169", "1350", "5992", "1212", "1724", "2326", "…
#> $ zona         <fct> Zona Oriente, Zona Oriente, Zona Oriente, Zona Sur, Zona …
#> $ piso         <int> NA, NA, NA, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, …
#> $ estrato      <ord> 3, 3, 3, 4, 5, 5, 4, 5, 5, 5, 6, 4, 5, 6, 4, 5, 5, 4, 5, …
#> $ preciom      <dbl> 250, 320, 350, 400, 260, 240, 220, 310, 320, 780, 750, 62…
#> $ areaconst    <dbl> 70, 120, 220, 280, 90, 87, 52, 137, 150, 380, 445, 355, 2…
#> $ parqueaderos <int> 1, 1, 2, 3, 1, 1, 2, 2, 2, 2, NA, 3, 2, 2, 1, 4, 2, 2, 2,…
#> $ banios       <int> 3, 2, 2, 5, 2, 3, 2, 3, 4, 3, 7, 5, 6, 2, 4, 4, 4, 3, 2, …
#> $ habitaciones <int> 6, 3, 4, 3, 3, 3, 3, 4, 6, 3, 6, 5, 6, 2, 5, 5, 4, 3, 3, …
#> $ tipo         <fct> Casa, Casa, Casa, Casa, Apartamento, Apartamento, Apartam…
#> $ barrio       <chr> "20 de julio", "20 de julio", "20 de julio", "3 de julio"…
#> $ longitud     <dbl> -76.51, -76.51, -76.52, -76.54, -76.51, -76.52, -76.52, -…
#> $ latitud      <dbl> 3.434, 3.434, 3.436, 3.435, 3.459, 3.370, 3.426, 3.383, 3…
tibble(
  Variable = names(vivienda_02_tipos),
  Final    = map_chr(vivienda_02_tipos, ~ class(.x)[1])
) |>
  left_join(diagnostico |> select(Variable, Inicial = Tipo), by = "Variable") |>
  mutate(
    Naturaleza = c("Identificador", "Nominal", "Discreta", "Ordinal",
                   "Continua", "Continua", "Discreta", "Discreta",
                   "Discreta", "Nominal", "Nominal", "Geográfica", "Geográfica"),
    Cambio = ifelse(Inicial == Final,
                    cell_spec("Sin cambio", color = "#6b7a85"),
                    cell_spec("Reclasificada", color = "#0f766e", bold = TRUE))
  ) |>
  select(Variable, Naturaleza, `Tipo inicial` = Inicial,
         `Tipo final` = Final, Cambio) |>
  tabla_estilo(
    titulo = "Tabla 5. Reclasificación de variables según su naturaleza",
    grupos = c(" " = 2, "Tipo de dato en R" = 2, " " = 1),
    nota   = "La variable barrio se mantiene como texto de manera intencional; su conversión a factor se realizará una vez depurados los errores de digitación."
  )
Tabla 5. Reclasificación de variables según su naturaleza
Tipo de dato en R
Variable Naturaleza Tipo inicial Tipo final Cambio
id Identificador numeric character Reclasificada
zona Nominal character factor Reclasificada
piso Discreta character integer Reclasificada
estrato Ordinal numeric ordered Reclasificada
preciom Continua numeric numeric Sin cambio
areaconst Continua numeric numeric Sin cambio
parqueaderos Discreta numeric integer Reclasificada
banios Discreta numeric integer Reclasificada
habitaciones Discreta numeric integer Reclasificada
tipo Nominal character factor Reclasificada
barrio Nominal character character Sin cambio
longitud Geográfica numeric numeric Sin cambio
latitud Geográfica numeric numeric Sin cambio
Nota. La variable barrio se mantiene como texto de manera intencional; su conversión a factor se realizará una vez depurados los errores de digitación.

Definido el tipo correcto de piso, se completó la reclasificación del resto de variables según su naturaleza conceptual. El estrato socioeconómico, que había ingresado como numérico, se convirtió a factor ordenado: conserva así la jerarquía entre niveles pero impide calcular promedios, operación que no tendría de sentido dado que la distancia entre el estrato 3 y el 4 no es equivalente a la que separa el 1 del 2. El identificador se transformó en texto, de modo que quede automáticamente excluido de las matrices de correlación y los resúmenes descriptivos, donde su inclusión produciría cifras aparentemente válidas pero vacías de significado.

Las variables de conteo (habitaciones, baños y parqueaderos) se declararon como enteros, y tipo y zona como factores nominales. Las coordenadas geográficas se mantuvieron en formato numérico por corresponder a mediciones continuas, si bien se excluyen de los análisis descriptivos porque su promedio o su dispersión no admiten interpretación sustantiva.

3.5 Análisis de Valores Ausentes

La distinción que organiza todo el análisis se basa en tres posibilidades. Un dato puede faltar completamente al azar (MCAR): nada explica su ausencia, es puro accidente de captura. Puede faltar al azar condicionado a lo observado (MAR): la ausencia se explica por otras variables presentes. O puede faltar no al azar (MNAR): la ausencia depende del valor que falta.

Estatus Ausencias

ausentes <- tibble(
  Variable  = names(vivienda_02_tipos),
  Faltantes = map_int(vivienda_02_tipos, ~ sum(is.na(.x))),
  pct       = map_dbl(vivienda_02_tipos, ~ 100 * mean(is.na(.x)))
) |>
  filter(Faltantes > 0) |>
  arrange(desc(pct))

ausentes |>
  mutate(
    `%` = cell_spec(sprintf("%.1f", pct),
                    bold = pct >= 20,
                    color = ifelse(pct >= 20, "#a33a3a", "#b5751a")),
    Completitud = cell_spec(barra(100 - pct), monospace = TRUE,
                            color = ifelse(pct >= 20, "#a33a3a", "#b5751a"))
  ) |>
  select(Variable, Faltantes, `%`, Completitud) |>
  tabla_estilo(
    titulo = "Tabla 6. Variables con datos ausentes tras la depuración inicial",
    nota   = "Porcentajes calculados sobre los 8.319 registros que conforman la base depurada."
  )
Tabla 6. Variables con datos ausentes tras la depuración inicial
Variable Faltantes % Completitud
piso 2.635 31.7 ██████░░░░
parqueaderos 1.602 19.3 ████████░░
Nota. Porcentajes calculados sobre los 8.319 registros que conforman la base depurada.

Despues de eliminar las filas vacias, quedamos solo con dos variables (piso y parqueadero) con resgistros ausentes.

A continuación se va a verificar si los datos ausentes coinciden en ambas variables.

vivienda_02_tipos |>
  count(`Piso` = ifelse(is.na(piso), "Ausente", "Presente"),
        `Parqueaderos` = ifelse(is.na(parqueaderos), "Ausente", "Presente")) |>
  mutate(`%` = sprintf("%.1f", 100 * n / sum(n))) |>
  rename(Registros = n) |>
  tabla_estilo(
    titulo = "Tabla 7. Coincidencia entre las ausencias de piso y parqueaderos",
    nota   = "Si las ausencias fueran independientes, la frecuencia de la celda donde ambas faltan se aproximaría al producto de sus proporciones marginales."
  )
Tabla 7. Coincidencia entre las ausencias de piso y parqueaderos
Piso Parqueaderos Registros %
Ausente Ausente 726 8.7
Ausente Presente 1.909 22.9
Presente Ausente 876 10.5
Presente Presente 4.808 57.8
Nota. Si las ausencias fueran independientes, la frecuencia de la celda donde ambas faltan se aproximaría al producto de sus proporciones marginales.

Antes de decidir cómo tratar los datos faltantes se revisó si las ausencias de piso y parqueaderos ocurren en los mismos registros o si cada variable falla por su cuenta. La distinción es importante ya que cuando dos campos se vacían juntos, lo habitual es que el problema esté en la publicación completa y no en cada dato por separado, y el tratamiento tendría que abordarlos en conjunto. El cruce muestra que ambos campos faltan simultáneamente en 726 registros, equivalentes al 8,7% de la base. Bajo independencia se habrían esperado alrededor del 6,1%, de modo que sí hay una asociación entre las dos ausencias, aunque de magnitud moderada: la probabilidad de que falte el parqueadero pasa de 15,4% cuando el piso está registrado a 27,6% cuando no lo está. Sin embargo, en la mayoria de los casos no responde a ese patrón. Hay 1.909 registros que informan parqueadero pero omiten el piso y otros 876 que hacen exactamente lo contrario, lo que indica que cada variable falta sobre todo por razones propias y no por un abandono generalizado del formulario.

Se continua analizando si la ausencias dependen de otras variables y asi determinar MCAR o MAR.

vivienda_02_tipos |>
  group_by(tipo, estrato) |>
  summarise(
    n = n(),
    `Piso ausente %` = sprintf("%.1f", 100 * mean(is.na(piso))),
    `Parq. ausente %` = sprintf("%.1f", 100 * mean(is.na(parqueaderos))),
    .groups = "drop"
  ) |>
  rename(Tipo = tipo, Estrato = estrato, Registros = n) |>
  tabla_estilo(
    titulo = "Tabla 8. Proporción de ausencias según tipo de inmueble y estrato",
    grupos = c(" " = 3, "Proporción de datos ausentes" = 2),
    nota   = "Variaciones sistemáticas entre grupos indican que la ausencia no ocurre completamente al azar."
  )
Tabla 8. Proporción de ausencias según tipo de inmueble y estrato
Proporción de datos ausentes
Tipo Estrato Registros Piso ausente % Parq. ausente %
Apartamento 3 639 31.0 53.7
Apartamento 4 1.404 24.8 25.4
Apartamento 5 1.766 24.8 6.1
Apartamento 6 1.291 30.8 4.8
Casa 3 814 46.3 52.3
Casa 4 725 36.6 18.1
Casa 5 984 36.0 12.3
Casa 6 696 37.1 7.9
Nota. Variaciones sistemáticas entre grupos indican que la ausencia no ocurre completamente al azar.

3.5.1 Parqueaderos

En parqueaderos, la proporción de datos faltantes desciende del 53,7% en apartamentos de estrato 3 al 4,8% en estrato 6, y repite el mismo comportamiento en casas, donde pasa del 52,3% al 7,9%. Este patrón se puede explicar en que en los segmentos de mayor estrato el parqueadero se asume como parte del inmueble y se reporta de manera rutinaria, mientras que en estrato 3 buena parte de las viviendas no cuenta con uno y el campo se deja en blanco en lugar de ingresar un cero. Tomando esta aproximación, el valor ausente no expresa desconocimiento sino inexistencia del atributo, y la vía adecuada sería recodificarlo como cero antes que imputar una cantidad.

Antes de recodificar, se comprueba si existen el valor cero para parqueaderos.

vivienda_02_tipos |>
  count(parqueaderos) |>
  mutate(pct = sprintf("%.1f", 100 * n / sum(n)))

Se confirma que el valor 0 no existe en todo el data set y que la variable arranca en 1. Por lo tanto, se toma la aproximación que los valores ausentes corresponden a que quien no tenía parqueadero dejó el campo vacío en lugar de escribir cero.

vivienda_03_na <- vivienda_02_tipos |>
  mutate(parqueaderos = replace_na(parqueaderos, 0L))

# Verificación
vivienda_03_na |>
  count(parqueaderos, name = "Registros") |>
  mutate(`%` = sprintf("%.1f", 100 * Registros / sum(Registros))) |>
  rename(Parqueaderos = parqueaderos) |>
  tabla_estilo(
    titulo = "Tabla 9. Distribución de parqueaderos tras la recodificación",
    nota   = "Los registros sin dato se recodificaron como cero, bajo el supuesto de que la ausencia del campo indica inexistencia del atributo y no desconocimiento del valor."
  )
Tabla 9. Distribución de parqueaderos tras la recodificación
Parqueaderos Registros %
0 1.602 19.3
1 3.155 37.9
2 2.475 29.8
3 520 6.3
4 384 4.6
5 68 0.8
6 68 0.8
7 18 0.2
8 17 0.2
9 4 0.0
10 8 0.1
Nota. Los registros sin dato se recodificaron como cero, bajo el supuesto de que la ausencia del campo indica inexistencia del atributo y no desconocimiento del valor.

Sobre esa base, los 1.602 registros sin dato se recodificaron como cero, decisión que altera de manera apreciable la distribución de la variable: la categoría de cero parqueaderos pasa a ser la segunda más frecuente del conjunto, por encima incluso de los inmuebles con dos. Esta inferencia esta apoyada en el patrón observado y en el conocimiento del mercado, no de un hecho verificable con la información disponible. Es posible que algunos de esos registros correspondan a viviendas que sí cuentan con parqueadero y cuyo anunciante omitió mencionarlo; el error que ello introduciría, sin embargo, tiene una dirección conocida (subestimar la disponibilidad del atributo) mientras que imputar un valor promedio habría introducido un sesgo en una dirección imposible de anticipar, además de atribuir un activo inexistente a viviendas que probablemente carecen de parqueadero.

3.5.2 Piso

El comportamiento de piso es completamente distinto. La Tabla 8 muestra que los porcentajes de ausencia oscilan entre 24,8% y 31,0% en apartamentos y entre 36,0% y 46,3% en casas, sin dirección alguna respecto al estrato, de modo que ninguna de las variables observadas ayuda a explicar por qué falta el dato. Por lo tanto, ante la falta de un mecanismo identificable que explique la ausencia, imputar supondría asumir un supuesto que los datos no respaldan.

Se procede a revisar la distribución por tipo de inmueble, para confirmar si la variable significa lo mismo en casas y apartamentos.

vivienda_02_tipos |>
  filter(!is.na(piso)) |>
  group_by(tipo) |>
  summarise(
    n = n(),
    Mínimo  = min(piso),
    Mediana = median(piso),
    P90     = quantile(piso, 0.90),
    Máximo  = max(piso),
    .groups = "drop"
  ) |>
  rename(Tipo = tipo, Registros = n) |>
  tabla_estilo(
    titulo = "Tabla 10. Distribución de piso según tipo de inmueble",
    nota   = "Distribuciones marcadamente distintas sugerirían que la variable no mide lo mismo en ambos tipos de inmueble."
  )
Tabla 10. Distribución de piso según tipo de inmueble
Tipo Registros Mínimo Mediana P90 Máximo
Apartamento 3.719 1 4 9 12
Casa 1.965 1 2 3 10
Nota. Distribuciones marcadamente distintas sugerirían que la variable no mide lo mismo en ambos tipos de inmueble.

La comparación de la distribución de piso entre casas y apartamentos reveló que la variable no mide lo mismo en ambos casos. En apartamentos, la mediana se sitúa en el piso 4 y el percentil 90 en el 9, con valores que alcanzan el piso 12; la distribución se extiende a lo largo de toda la escala, tal como cabría esperar de inmuebles ubicados en edificaciones de alturas diversas. En casas, en cambio, la mediana es 2 y el percentil 90 apenas llega a 3.

Sin embargo, es importante tener en cuenta que una casa no ocupa un nivel dentro de una edificación, por lo que el número ingresado solo puede referirse a la cantidad de niveles de la construcción. Por lo tanto, la variable expresa la ubicación del inmueble dentro del edificio en el caso de los apartamentos y el número de plantas de la vivienda en el caso de las casas. Son atributos conceptualmente distintos: en el primero, un valor alto indica altura respecto al suelo; en el segundo, mayor superficie construida repartida en varios niveles. Mantenerlos en una misma columna implicaría que cualquier estadístico calculado sobre ella mezclaría dos magnitudes sin relación entre sí, y que un modelo de precios estimaría un coeficiente único para dos efectos de naturaleza diferente. Por consiguiente, la variable se separará en dos, cada una restringida al tipo de inmueble.

vivienda_03_na <- vivienda_03_na |>
  mutate(
    piso_apto    = if_else(tipo == "Apartamento", piso, NA_integer_),
    niveles_casa = if_else(tipo == "Casa", piso, NA_integer_)
  )

# Resumen de las dos variables resultantes
bind_rows(
  vivienda_03_na |>
    filter(tipo == "Apartamento") |>
    summarise(
      Variable  = "piso_apto",
      Aplica_a  = "Apartamentos",
      Registros = n(),
      Con_dato  = sum(!is.na(piso_apto)),
      Sin_dato  = sum(is.na(piso_apto)),
      Mínimo    = min(piso_apto, na.rm = TRUE),
      Mediana   = median(piso_apto, na.rm = TRUE),
      P90       = quantile(piso_apto, 0.90, na.rm = TRUE),
      Máximo    = max(piso_apto, na.rm = TRUE)
    ),
  vivienda_03_na |>
    filter(tipo == "Casa") |>
    summarise(
      Variable  = "niveles_casa",
      Aplica_a  = "Casas",
      Registros = n(),
      Con_dato  = sum(!is.na(niveles_casa)),
      Sin_dato  = sum(is.na(niveles_casa)),
      Mínimo    = min(niveles_casa, na.rm = TRUE),
      Mediana   = median(niveles_casa, na.rm = TRUE),
      P90       = quantile(niveles_casa, 0.90, na.rm = TRUE),
      Máximo    = max(niveles_casa, na.rm = TRUE)
    )
) |>
  mutate(
    `% sin dato` = sprintf("%.1f", 100 * Sin_dato / Registros),
    .after = Sin_dato
  ) |>
  rename(
    `Aplica a`  = Aplica_a,
    `Con dato`  = Con_dato,
    `Sin dato`  = Sin_dato
  ) |>
  tabla_estilo(
    titulo = "Tabla 11. Desagregación de piso en dos variables según tipo de inmueble",
    grupos = c(" " = 2, "Cobertura dentro del tipo" = 4, "Distribución" = 4),
    nota   = "Cada variable se calcula únicamente sobre el tipo de inmueble en el que su significado es inequívoco. Los ausentes que resultan de aplicar la variable al tipo contrario no se contabilizan, pues corresponden a una condición que no aplica al registro."
  )
Tabla 11. Desagregación de piso en dos variables según tipo de inmueble
Cobertura dentro del tipo
Distribución
Variable Aplica a Registros Con dato Sin dato % sin dato Mínimo Mediana P90 Máximo
piso_apto Apartamentos 5.100 3.719 1.381 27.1 1 4 9 12
niveles_casa Casas 3.219 1.965 1.254 39.0 1 2 3 10
Nota. Cada variable se calcula únicamente sobre el tipo de inmueble en el que su significado es inequívoco. Los ausentes que resultan de aplicar la variable al tipo contrario no se contabilizan, pues corresponden a una condición que no aplica al registro.

Continuamos analizando si la ausencia de datos en piso esta asociada a otras variables. Primero las categoricas.

vivienda_03_na |>
  filter(tipo == "Apartamento") |>
  group_by(zona) |>
  summarise(
    Registros = n(),
    `Piso ausente %` = sprintf("%.1f", 100 * mean(is.na(piso_apto))),
    .groups = "drop"
  ) |>
  rename(Zona = zona) |>
  tabla_estilo(
    titulo = "Tabla 12. Ausencia de piso según zona (apartamentos)",
    )
Tabla 12. Ausencia de piso según zona (apartamentos)
Zona Registros Piso ausente %
Zona Centro 24 37.5
Zona Norte 1.198 34.1
Zona Oeste 1.029 31.7
Zona Oriente 62 24.2
Zona Sur 2.787 22.3
vivienda_03_na |>
  filter(tipo == "Casa") |>
  group_by(zona) |>
  summarise(
    Registros = n(),
    `Niveles ausente %` = sprintf("%.1f", 100 * mean(is.na(niveles_casa))),
    .groups = "drop"
  ) |>
  rename(Zona = zona) |>
  tabla_estilo(
    titulo = "Tabla 13. Ausencia del número de niveles según zona (casas)",
  )
Tabla 13. Ausencia del número de niveles según zona (casas)
Zona Registros Niveles ausente %
Zona Centro 100 46.0
Zona Norte 722 51.5
Zona Oeste 169 39.6
Zona Oriente 289 44.3
Zona Sur 1.939 33.1

A continuación numéricas

vivienda_03_na |>
  filter(tipo == "Apartamento") |>
  mutate(Grupo = if_else(is.na(piso_apto), "Sin dato", "Con dato")) |>
  group_by(Grupo) |>
  summarise(
    Registros              = n(),
    `Precio mediano`       = median(preciom, na.rm = TRUE),
    `Área mediana`         = median(areaconst, na.rm = TRUE),
    `Habitaciones (med.)`  = median(habitaciones, na.rm = TRUE),
    `Baños (med.)`         = median(banios, na.rm = TRUE),
    `Parqueaderos (med.)`  = median(parqueaderos, na.rm = TRUE),
    .groups = "drop"
  ) |>
  tabla_estilo(
    titulo = "Tabla 14. Perfil de los apartamentos con y sin dato de piso",
    nota   = "Diferencias apreciables entre ambos grupos indicarían que la ausencia se asocia a las características del inmueble."
  )
Tabla 14. Perfil de los apartamentos con y sin dato de piso
Grupo Registros Precio mediano Área mediana Habitaciones (med.) Baños (med.) Parqueaderos (med.)
Con dato 3.719 270 90 3 2 1
Sin dato 1.381 290 91 3 2 1
Nota. Diferencias apreciables entre ambos grupos indicarían que la ausencia se asocia a las características del inmueble.
vivienda_03_na |>
  filter(tipo == "Casa") |>
  mutate(Grupo = if_else(is.na(niveles_casa), "Sin dato", "Con dato")) |>
  group_by(Grupo) |>
  summarise(
    Registros             = n(),
    `Precio mediano`      = median(preciom, na.rm = TRUE),
    `Área mediana`        = median(areaconst, na.rm = TRUE),
    `Habitaciones (med.)` = median(habitaciones, na.rm = TRUE),
    `Baños (med.)`        = median(banios, na.rm = TRUE),
    `Parqueaderos (med.)` = median(parqueaderos, na.rm = TRUE),
    .groups = "drop"
  ) |>
  tabla_estilo(
    titulo = "Tabla 15. Perfil de las casas con y sin dato de niveles",
    nota   = "Diferencias apreciables entre ambos grupos indicarían que la ausencia se asocia a las características del inmueble."
  )
Tabla 15. Perfil de las casas con y sin dato de niveles
Grupo Registros Precio mediano Área mediana Habitaciones (med.) Baños (med.) Parqueaderos (med.)
Con dato 1.965 440 245.0 4 4 2
Sin dato 1.254 400 230.5 4 4 1
Nota. Diferencias apreciables entre ambos grupos indicarían que la ausencia se asocia a las características del inmueble.
vivienda_03_na |>
  filter(tipo == "Casa", !is.na(niveles_casa)) |>
  group_by(niveles_casa) |>
  summarise(
    Casas             = n(),
    `Área mediana`    = median(areaconst, na.rm = TRUE),
    `Precio mediano`  = median(preciom, na.rm = TRUE),
    .groups = "drop"
  ) |>
  rename(Niveles = niveles_casa) |>
  tabla_estilo(
    titulo = "Tabla 16. Área y precio según número de niveles (casas)",
    nota   = "Una relación creciente y regular entre niveles y área construida indicaría que esta última podría servir de base para imputar los registros sin dato."
  )
Tabla 16. Área y precio según número de niveles (casas)
Niveles Casas Área mediana Precio mediano
1 430 224.5 400.0
2 938 264.0 480.0
3 524 210.0 375.0
4 62 303.5 440.0
5 3 67.0 340.0
6 2 286.5 463.5
7 4 493.5 696.0
10 2 123.5 170.0
Nota. Una relación creciente y regular entre niveles y área construida indicaría que esta última podría servir de base para imputar los registros sin dato.

Por zona aparecen diferencias apreciables (22,3% a 34,1% en apartamentos y de 33,1% a 51,5% en casas, siempre con la Zona Sur como la más completa y la Zona Norte como la que más omite) pero se trata de un patrón sobre la frecuencia de la omisión y no sobre el valor omitido. Saber dónde falta más el dato no dice nada acerca de en qué piso está el apartamento ni cuántas plantas tiene la casa cuyo registro quedó vacío. Por su parte, el perfil de los inmuebles con y sin dato resultó similar en ambos tipos, con diferencias menores en área, habitaciones, baños y precio que no configuran una submuestra particular.

La búsqueda de un predictor tampoco fue efectiva. En apartamentos era previsible que fallara, porque nada en las características de una vivienda anticipa su ubicación dentro de un edificio. En casas había mayor expectativa, dado que el número de plantas sí guarda una relación esperable con el área construida, pero el contraste resultó irregular: el área mediana sube de 224,5 a 264,0 metros cuadrados entre uno y dos niveles, desciende a 210,0 en las de tres y vuelve a subir en las de cuatro, sin dirección definida. Los niveles de cinco en adelante se apoyan en apenas dos a cuatro observaciones y contienen además casos materialmente imposibles (casas de cinco plantas con 67 metros cuadrados, o de diez plantas con un precio inferior a la mediana general) que quedaron señalados para la etapa de valores atípicos.

Con ese panorama se descartó imputar en ambos casos. Rellenar los vacíos habría supuesto asignar valores sin ningún respaldo en los datos. Adicionalmente, la imputación por tendencia central comprime artificialmente la variabilidad de la variable y distorsiona cualquier estimación posterior de su efecto sobre el precio. Excluir las variables implicaria descartar 3.719 apartamentos y 1.965 casas con información válida en un atributo que sí incide en el valor del inmueble. Se optó por conservarlas con sus ausencias y declarar la base de cálculo en cada análisis que las utilice, teniendo en cuenta que la Zona Sur se encuentra sobrerrepresentada en ambos subconjuntos con dato disponible, lo que debe tenerse presente en cualquier comparación entre zonas.

vivienda_03_na <- vivienda_03_na |>
  mutate(
    piso_registrado = factor(
      if_else(is.na(piso), "Sin registro", "Con registro"),
      levels = c("Con registro", "Sin registro")
    )
  )

Prueba MCAR

mcar <- vivienda_03_na |>
  select(preciom, areaconst, habitaciones, banios, parqueaderos, piso) |>
  mcar_test()

mcar |>
  mutate(
    Decisión = if_else(p.value < 0.05, "Se rechaza MCAR", "No se rechaza MCAR")
  ) |>
  rename(`Estadístico` = statistic, `Grados de libertad` = df,
         `Valor p` = p.value, `Patrones` = missing.patterns) |>
  tabla_estilo(
    titulo = "Tabla 17. Prueba de Little para el supuesto MCAR",
  )
Tabla 17. Prueba de Little para el supuesto MCAR
Estadístico Grados de libertad Valor p Patrones Decisión
180.1 5 0 2 Se rechaza MCAR

Como complemento al análisis por patrones se aplicó la prueba de Little, cuyo propósito es contrastar formalmente si los datos faltan de manera completamente aleatoria. El resultado rechaza esa hipótesis, un estadístico de 180,1 con 5 grados de libertad arroja un valor p prácticamente nulo, de modo que la ausencia de información se asocia con las características observadas de los inmuebles.

El resultado se reporta como confirmación formal de que la ausencia no es completamente aleatoria, pero las decisiones de tratamiento se sustentan en la magnitud de las diferencias observadas y en la interpretación sustantiva de cada patrón, y no en la significancia estadística de la prueba.

3.5.3 Balance de Completitud

bind_rows(
  tibble(
    Momento   = "Antes",
    Variable  = names(vivienda_02_tipos),
    Faltantes = map_int(vivienda_02_tipos, ~ sum(is.na(.x)))
  ),
  tibble(
    Momento   = "Después",
    Variable  = names(vivienda_03_na),
    Faltantes = map_int(vivienda_03_na, ~ sum(is.na(.x)))
  )
) |>
  filter(Faltantes > 0) |>
  pivot_wider(names_from = Momento, values_from = Faltantes, values_fill = 0) |>
  mutate(
    `% después` = sprintf("%.1f", 100 * Después / nrow(vivienda_03_na)),
    `Ausencia real` = case_when(
      Variable == "piso_apto"    ~ "1.381 (27,1% de apartamentos)",
      Variable == "niveles_casa" ~ "1.254 (39,0% de casas)",
      Variable == "parqueaderos" ~ "—",
      TRUE                       ~ "2.635 (31,7% de la base)"
    ),
    Tratamiento = case_when(
      Variable == "parqueaderos" ~ cell_spec("Recodificado a cero",
                                             color = "#0f766e", bold = TRUE),
      Variable == "piso"         ~ cell_spec("Desagregado por tipo",
                                             color = "#b5751a"),
      TRUE                       ~ cell_spec("Conservado con declaración",
                                             color = "#6b7a85")
    )
  ) |>
  select(Variable, Antes, Después, `% después`, `Ausencia real`, Tratamiento) |>
  tabla_estilo(
    titulo = "Tabla 18. Balance de completitud tras el tratamiento de datos ausentes",
    grupos = c(" " = 1, "Registros ausentes" = 3, "Diagnóstico" = 2),
    nota   = "La columna de ausencia real descuenta los registros del tipo de inmueble al que la variable no aplica: de los 4.600 vacíos de piso_apto, 3.219 corresponden a casas, y de los 6.354 de niveles_casa, 5.100 corresponden a apartamentos. La cobertura efectiva se calcula dentro del universo pertinente a cada variable."
  )
Tabla 18. Balance de completitud tras el tratamiento de datos ausentes
Registros ausentes
Diagnóstico
Variable Antes Después % después Ausencia real Tratamiento
piso 2.635 2.635 31.7 2.635 (31,7% de la base) Desagregado por tipo
parqueaderos 1.602 0 0.0 Recodificado a cero
piso_apto 0 4.600 55.3 1.381 (27,1% de apartamentos) Conservado con declaración
niveles_casa 0 6.354 76.4 1.254 (39,0% de casas) Conservado con declaración
Nota. La columna de ausencia real descuenta los registros del tipo de inmueble al que la variable no aplica: de los 4.600 vacíos de piso_apto, 3.219 corresponden a casas, y de los 6.354 de niveles_casa, 5.100 corresponden a apartamentos. La cobertura efectiva se calcula dentro del universo pertinente a cada variable.

El tratamiento de los datos ausentes se cerró con un balance que contrasta el estado de las variables antes y después de las decisiones adoptadas. De las dos que presentaban vacíos, solo parqueaderos quedó completa: sus 1.602 registros sin dato se recodificaron como cero al constatarse que la variable no contenía ningún valor nulo. La variable piso conserva sus 2.635 registros incompletos, equivalentes al 31,7% de la base, ahora distribuidos entre las dos variables que resultaron de su desagregación.

La base mantuvo sus 8.319 registros, dado que ninguna decisión de esta etapa implicó eliminar observaciones. El criterio que orientó el proceso fue conservar la información disponible y hacer explícito lo que no se sabe, antes que completar los vacíos con valores estimados sin respaldo empírico.

3.6 Depuración de variables categóricas

3.6.1 Barrio

La variable Barrio muestra 436 valores unicos, por lo que se hace necesario revisar su organización

vivienda_03_na |>
  count(barrio, sort = TRUE, name = "Registros") |>
  slice_head(n = 20) |>
  tabla_estilo(titulo = "Tabla 18. Barrios más frecuentes")
Tabla 18. Barrios más frecuentes
barrio Registros
valle del lili 1.008
ciudad jardín 516
pance 409
la flora 366
santa teresita 262
el caney 208
el ingenio 202
la hacienda 164
acopi 158
los cristales 154
normandía 154
el limonar 135
prados del norte 126
el refugio 120
aguacatal 109
ciudad 2000 95
caney 88
cristales 83
urbanización la flora 83
brisas de los 81
vivienda_03_na |>
  count(barrio, name = "Registros") |>
  count(Registros, name = "Cantidad de barrios") |>
  head(10) |>
  tabla_estilo(
    titulo = "Tabla 19. Distribución de barrios según su frecuencia",
    nota   = "Los barrios que aparecen una o dos veces son candidatos naturales a errores de digitación."
  )
Tabla 19. Distribución de barrios según su frecuencia
Registros Cantidad de barrios
1 177
2 37
3 22
4 19
5 15
6 16
7 8
8 10
9 4
10 4
Nota. Los barrios que aparecen una o dos veces son candidatos naturales a errores de digitación.
library(stringdist)
library(stringi)

barrios <- vivienda_03_na |> distinct(barrio) |> pull(barrio) |> sort()

# Normalización previa: quita tildes, mayúsculas y espacios sobrantes
normalizar <- function(x) {
  x |>
    str_squish() |>
    str_to_lower() |>
    stringi::stri_trans_general("Latin-ASCII")
}

# Pares de nombres sospechosamente parecidos
pares <- expand_grid(a = barrios, b = barrios) |>
  filter(a < b) |>
  mutate(d = stringdist(normalizar(a), normalizar(b), method = "lv")) |>
  filter(d <= 2) |>
  arrange(d)

pares |>
  slice_head(n = 30) |>
  rename(`Variante A` = a, `Variante B` = b, Distancia = d) |>
  tabla_estilo(
    titulo = "Tabla 20. Pares de nombres de barrio con grafías similares",
    nota   = "La distancia indica el número de caracteres que difieren entre ambas grafías. Distancia 0 significa que solo se diferencian por tildes, mayúsculas o espacios."
  )
Tabla 20. Pares de nombres de barrio con grafías similares
Variante A Variante B Distancia
alameda del rio alameda del río 0
alfonso lopez alfonso lópez 0
belalcazar Belalcazar 0
brisas de los Brisas De Los 0
camino real Camino Real 0
centenario Centenario 0
chiminangos Chiminangos 0
ciudad 2000 Ciudad 2000 0
ciudad cordoba ciudad córdoba 0
ciudad jardin ciudad jardín 0
ciudad jardin Ciudad Jardín 0
ciudad jardín Ciudad Jardín 0
ciudad los alamos ciudad los álamos 0
ciudad pacifica Ciudad Pacifica 0
colseguros andes Colseguros Andes 0
cristobal colón cristóbal colón 0
el bosque El Bosque 0
el caney El Caney 0
el ingenio El Ingenio 0
junin junín 0
la flora La Flora 0
la hacienda La Hacienda 0
los alcazares los alcázares 0
los cambulos los cámbulos 0
los guaduales Los Guaduales 0
miraflores Miraflores 0
napoles nápoles 0
normandia normandía 0
pacara pacará 0
pance Pance 0
Nota. La distancia indica el número de caracteres que difieren entre ambas grafías. Distancia 0 significa que solo se diferencian por tildes, mayúsculas o espacios.

La revisión confirmó que buena parte de los valores únicos corresponden al mismo lugar escrito de varias maneras. El primer indicio está en la distribución de frecuencias: 177 nombres aparecen una sola vez y 37 aparecen dos, de modo que cerca del cuarenta por ciento de las categorías apenas concentra el dos por ciento de los registros.

La búsqueda sistemática de escritura parecidas identificó treinta pares que difieren únicamente en tildes, mayúsculas o espacios sobrantes (ejemplo: junin y junín, el caney y El Caney, ciudad jardin y Ciudad Jardín), casos donde la unificación no requiere ninguna decisión de fondo porque se trata literalmente del mismo nombre escrito con distinta ortografía.

La depuración se realizará en dos fases. La primera es llevar todos los nombres a minúsculas, retirar las tildes y eliminar los espacios sobrantes con lo cual las treinta variantes ortográficas se fusionan automáticamente.

vivienda_04_texto <- vivienda_03_na |>
  mutate(
    barrio_norm = barrio |>
      str_squish() |>
      str_to_lower() |>
      stri_trans_general("Latin-ASCII")
  )

cat("Categorías originales:", n_distinct(vivienda_03_na$barrio),
    "| Tras normalizar:", n_distinct(vivienda_04_texto$barrio_norm))
#> Categorías originales: 436 | Tras normalizar: 389

Por otro lado, aparecen registrados por separado el caney con 208 inmuebles y caney con 88, los cristales con 154 y cristales con 83, la flora con 366 y urbanización la flora con 83. Lo cual se considera corresponden al mismo barrio.

comparar <- vivienda_04_texto |>
  count(barrio_norm, name = "n") |>
  mutate(raiz = str_remove(barrio_norm,
           "^(el|la|los|las|urbanizacion|urb|barrio|conjunto|ciudadela)\\s")) |>
  group_by(raiz) |>
  filter(n() > 1) |>
  arrange(raiz, desc(n)) |>
  ungroup()

comparar |>
  rename(`Nombre registrado` = barrio_norm, Registros = n, `Raíz común` = raiz) |>
  tabla_estilo(
    titulo = "Tabla 21. Nombres que comparten raíz tras eliminar artículos y prefijos",
    nota   = "Agrupar por la raíz permite detectar variantes que la distancia de edición no captura, como la presencia u omisión del artículo."
  )
Tabla 21. Nombres que comparten raíz tras eliminar artículos y prefijos
Nombre registrado Registros Raíz común
alamos 14 alamos
los alamos 1 alamos
la alborada 5 alborada
alborada 1 alborada
los alcazares 22 alcazares
alcazares 2 alcazares
la arboleda 18 arboleda
arboleda 5 arboleda
barranquilla 6 barranquilla
urbanizacion barranquilla 4 barranquilla
boyaca 1 boyaca
urbanizacion boyaca 1 boyaca
los cambulos 31 cambulos
cambulos 3 cambulos
el caney 209 caney
caney 88 caney
las ceibas 23 ceibas
ceibas 1 ceibas
la ceibas 1 ceibas
colseguros 44 colseguros
urbanizacion colseguros 3 colseguros
los cristales 154 cristales
cristales 83 cristales
eucaristico 2 eucaristico
barrio eucaristico 1 eucaristico
la flora 368 flora
flora 1 flora
gran limonar 24 gran limonar
el gran limonar 8 gran limonar
los guaduales 26 guaduales
guaduales 2 guaduales
el ingenio 203 ingenio
ingenio 1 ingenio
el ingenio i 19 ingenio i
ingenio i 1 ingenio i
el ingenio ii 21 ingenio ii
ingenio ii 1 ingenio ii
los libertadores 4 libertadores
libertadores 3 libertadores
melendez 52 melendez
ciudadela melendez 1 melendez
la nueva base 8 nueva base
nueva base 1 nueva base
pacara 23 pacara
urbanizacion pacara 1 pacara
ciudadela pasoancho 21 pasoancho
pasoancho 6 pasoancho
primavera 2 primavera
la primavera 1 primavera
el refugio 120 refugio
refugio 2 refugio
san joaquin 20 san joaquin
urbanizacion san joaquin 4 san joaquin
tequendama 44 tequendama
urbanizacion tequendama 7 tequendama
Nota. Agrupar por la raíz permite detectar variantes que la distancia de edición no captura, como la presencia u omisión del artículo.

La segunda parte aborda las duplicaciones que el texto no delata a simple vista, como la presencia u omisión del artículo o de un prefijo urbanístico (caney y el caney, cristales y los cristales, tequendama y urbanización tequendama). Para resolverlas se extrae la raíz de cada nombre suprimiendo esos elementos iniciales y se adopta como forma canónica la escritura más frecuente dentro de cada grupo, criterio que preserva la correspondencia con el uso real de quienes publican los anuncios por encima de la corrección gramatical.

# 1. Normalizar: minúsculas, sin tildes, sin espacios sobrantes
vivienda_04_texto <- vivienda_03_na |>
  mutate(barrio_norm = barrio |>
           str_squish() |> str_to_lower() |>
           stri_trans_general("Latin-ASCII"))

# 2. Diccionario: la variante mayoritaria de cada raíz se vuelve la forma canónica
dicc <- vivienda_04_texto |>
  count(barrio_norm, name = "n") |>
  mutate(raiz = str_remove(barrio_norm,
           "^(el|la|los|las|urbanizacion|urb|barrio|conjunto|ciudadela)\\s")) |>
  group_by(raiz) |>
  slice_max(n, n = 1, with_ties = FALSE) |>
  ungroup() |>
  select(raiz, canonico = barrio_norm)

# 3. Aplicar
vivienda_04_texto <- vivienda_04_texto |>
  mutate(raiz = str_remove(barrio_norm,
           "^(el|la|los|las|urbanizacion|urb|barrio|conjunto|ciudadela)\\s")) |>
  left_join(dicc, by = "raiz") |>
  mutate(barrio_norm = coalesce(canonico, barrio_norm)) |>
  select(-raiz, -canonico)

cat("Categorías originales:", n_distinct(vivienda_03_na$barrio),
    "| Tras depurar:", n_distinct(vivienda_04_texto$barrio_norm))
#> Categorías originales: 436 | Tras depurar: 361
tibble(
  Etapa = c("Categorías originales",
            "Tras normalizar ortografía",
            "Tras unificar por raíz común"),
  Categorías = c(n_distinct(vivienda_03_na$barrio),
                 n_distinct(str_to_lower(stri_trans_general(str_squish(vivienda_03_na$barrio), "Latin-ASCII"))),
                 n_distinct(vivienda_04_texto$barrio_norm))
) |>
  tabla_estilo(
    titulo = "Tabla 22. Reducción de categorías en la variable barrio",
    nota   = "La unificación tomó como forma canónica la variante más frecuente de cada raíz."
  )
Tabla 22. Reducción de categorías en la variable barrio
Etapa Categorías
Categorías originales 436
Tras normalizar ortografía 389
Tras unificar por raíz común 361
Nota. La unificación tomó como forma canónica la variante más frecuente de cada raíz.

La variable pasó de 436 categorías a 361, una reducción del 17%. La normalización ortográfica por sí sola eliminó 47 duplicados, y la unificación por raíz común resolvió otros 28.

3.6.2 Zona, Tipo y Estrato

Las restantes variables cualitativas se revisaron con el mismo criterio aplicado a barrio, aunque su bajo número de categorías permitió una verificación directa mediante tablas de frecuencia. Se buscaba confirmar que cada variable contuviera únicamente los niveles esperados, sin variantes ortográficas ni categorías residuales de baja frecuencia que delataran errores de captura.

map_dfr(c("zona", "tipo", "estrato"), ~
  vivienda_04_texto |>
    count(.data[[.x]], name = "Registros") |>
    mutate(Variable = .x, Categoría = as.character(.data[[.x]])) |>
    select(Variable, Categoría, Registros)
) |>
  mutate(`%` = sprintf("%.1f", 100 * Registros / nrow(vivienda_04_texto))) |>
  tabla_estilo(
    titulo = "Tabla 23. Categorías registradas en las demás variables cualitativas",
    nota   = "Se verifica que cada variable contenga únicamente los niveles esperados, sin duplicaciones por escritura ni categorías residuales de baja frecuencia."
  )
Tabla 23. Categorías registradas en las demás variables cualitativas
Variable Categoría Registros %
zona Zona Centro 124 1.5
zona Zona Norte 1.920 23.1
zona Zona Oeste 1.198 14.4
zona Zona Oriente 351 4.2
zona Zona Sur 4.726 56.8
tipo Apartamento 5.100 61.3
tipo Casa 3.219 38.7
estrato 3 1.453 17.5
estrato 4 2.129 25.6
estrato 5 2.750 33.1
estrato 6 1.987 23.9
Nota. Se verifica que cada variable contenga únicamente los niveles esperados, sin duplicaciones por escritura ni categorías residuales de baja frecuencia.

Las tres variables no presentan inconsistencias. Zona registra cinco niveles, tipo dos y estrato cuatro, sin duplicaciones ni valores anómalos.

3.7 Estadísticos Descriptivos

num_vars <- c("preciom", "areaconst", "habitaciones", "banios", "parqueaderos")

vivienda_04_texto |>
  select(all_of(num_vars)) |>
  pivot_longer(everything(), names_to = "Variable", values_to = "v") |>
  filter(!is.na(v)) |>
  group_by(Variable) |>
  summarise(
    n         = n(),
    Media     = mean(v),
    Mediana   = median(v),
    `D. est.` = sd(v),
    CV        = sd(v) / mean(v),
    Q1        = quantile(v, .25),
    Q3        = quantile(v, .75),
    `Mínimo`  = min(v),
    `Máximo`  = max(v),
    .groups   = "drop"
  ) |>
  mutate(across(c(Media, Mediana, `D. est.`, Q1, Q3), ~ round(.x, 1)),
         CV = sprintf("%.2f", CV)) |>
  tabla_estilo(
    titulo = "Tabla 24. Estadísticos descriptivos de las variables cuantitativas",
    grupos = c(" " = 2, "Centralidad" = 2, "Dispersión" = 2, "Posición" = 4),
    nota   = "El coeficiente de variación expresa la desviación estándar como proporción de la media, permitiendo comparar la dispersión relativa entre variables de distinta escala."
  )
Tabla 24. Estadísticos descriptivos de las variables cuantitativas
Centralidad
Dispersión
Posición
Variable n Media Mediana D. est. CV Q1 Q3 Mínimo Máximo
areaconst 8.319 174.9 123 143.0 0.82 80 229 30 1.745
banios 8.319 3.1 3 1.4 0.46 2 4 0 10
habitaciones 8.319 3.6 3 1.5 0.40 3 4 0 10
parqueaderos 8.319 1.5 1 1.2 0.84 1 2 0 10
preciom 8.319 433.9 330 328.7 0.76 220 540 58 1.999
Nota. El coeficiente de variación expresa la desviación estándar como proporción de la media, permitiendo comparar la dispersión relativa entre variables de distinta escala.
vivienda_04_texto |>
  group_by(tipo) |>
  summarise(
    n                   = n(),
    `Precio mediano`    = median(preciom),
    `Precio medio`      = round(mean(preciom), 1),
    `Área mediana`      = median(areaconst),
    `Área media`        = round(mean(areaconst), 1),
    `Habitaciones`      = median(habitaciones),
    `Baños`             = median(banios),
    .groups = "drop"
  ) |>
  rename(Tipo = tipo) |>
  tabla_estilo(
    titulo = "Tabla 25. Descriptivos según tipo de inmueble",
    nota   = "La comparación entre media y mediana permite valorar el grado de asimetría de cada distribución dentro de su segmento."
  )
Tabla 25. Descriptivos según tipo de inmueble
Tipo n Precio mediano Precio medio Área mediana Área media Habitaciones Baños
Apartamento 5.100 279 366.9 90 112.8 3 2
Casa 3.219 430 540.0 240 273.4 4 4
Nota. La comparación entre media y mediana permite valorar el grado de asimetría de cada distribución dentro de su segmento.

Se calcularon medidas de centralidad, dispersión y posición para las cinco variables cuantitativas, reportando media y mediana de manera conjunta. La decisión responde a la naturaleza de los datos inmobiliarios: al no existir un límite superior de precio, un número reducido de propiedades de alto valor desplaza el promedio y lo aleja de lo que efectivamente encuentra un comprador. La distancia entre ambas medidas funciona además como indicador de asimetría. Se incorporó el coeficiente de variación, que permite comparar la dispersión relativa entre variables medidas en escalas distintas, y los estadísticos se calcularon también por separado para casas y apartamentos.

El sesgo hacia los valores altos quedó confirmado en las dos variables continuas. El precio medio alcanza 433,9 millones frente a una mediana de 330, y el área media 174,9 metros cuadrados frente a 123 de mediana; en ambos casos el promedio supera a la mediana en cerca de un tercio, lo que refleja una cola prolongada de inmuebles de gran tamaño y valor. Los máximos lo confirman: 1.999 y 1.745 metros cuadrados, cifras que multiplican por más de diez el valor mediano. La dispersión es asimismo elevada, con coeficientes de variación de 0,76 en precio y 0,82 en área, de modo que la variabilidad resulta comparable a la propia magnitud media.

La desagregación por tipo de inmueble muestra dos segmentos diferenciados. Las casas presentan una mediana de precio de 430 millones frente a 279 de los apartamentos, y una diferencia todavía más marcada en superficie: 240 metros cuadrados de mediana contra 90. Ambos segmentos conservan el sesgo hacia los valores altos, aunque más pronunciado en apartamentos, donde el precio medio supera a la mediana en un 31%. Esta separación justifica que los análisis posteriores mantengan la distinción por tipo antes que tratar la oferta como un conjunto homogéneo.

Llama la atención que los mínimos de habitaciones y baños registran cero, valor que resulta inconsistente con una vivienda habitable y apunta a errores de captura.

3.8 Gráficos de tendencia central.

3.8.1 Precio

El precio se representa mediante histogramas en dos escalas: la original y la logarítmica. Esta segunda representación responde a que los precios varían de forma multiplicativa antes que aditiva: un incremento de cien millones supone duplicar el valor de una vivienda de gama media, pero apenas un cinco por ciento en una de gama alta, de modo que en escala original ambas diferencias aparecen como equivalentes pese a representar magnitudes de cambio muy distintas. La transformación logarítmica convierte las razones en distancias, con lo cual el mismo espacio gráfico separa valores que guardan la misma proporción entre sí. Se incorporaron líneas de referencia para la media y la mediana con el fin de hacer visible la asimetría de la distribución

Figura 1. Distribución del precio de los inmuebles en escala original y logarítmica

p1 <- ggplot(vivienda_04_texto, aes(preciom)) +
  geom_histogram(bins = 60, fill = "#21384a", color = "white", linewidth = .2) +
  geom_vline(aes(xintercept = median(preciom), color = "Mediana"), linewidth = .7) +
  geom_vline(aes(xintercept = mean(preciom), color = "Media"),
             linewidth = .7, linetype = "dashed") +
  scale_color_manual(name = NULL,
                     values = c("Mediana" = "#0f766e", "Media" = "#a33a3a"),
                     guide = guide_legend(override.aes = list(
                       linetype = c("dashed", "solid")))) +
  labs(x = "Precio (millones de pesos)", y = "Frecuencia",
       subtitle = "Escala original") +
  theme_minimal() +
  theme(legend.position = "bottom")

p2 <- ggplot(vivienda_04_texto, aes(preciom)) +
  geom_histogram(bins = 60, fill = "#21384a", color = "white", linewidth = .2) +
  scale_x_log10() +
  labs(x = "Precio (escala logarítmica)", y = NULL,
       subtitle = "Escala logarítmica") +
  theme_minimal()

p1 + p2

En escala original la distribución del precio se concentra marcadamente en el extremo inferior, con la mediana situada en 330 millones a la izquierda de la media de 434 y una cola que se prolonga hasta cerca de los 2.000 millones. La representación resulta poco informativa en la zona donde se acumula el grueso de la oferta, ya que los inmuebles entre 100 y 400 millones quedan comprimidos en una franja estrecha del eje. La escala logarítmica corrige esa compresión y revela una distribución aproximadamente simétrica y unimodal, centrada en torno a los 300 millones.

El histograma logarítmico presenta un perfil irregular, con picos y valles pronunciados que no responden a la forma teórica de una distribución continua. Por otra parte, la distribución se interrumpe de manera abrupta cerca de los 2.000 millones en lugar de decaer progresivamente.

3.8.2 Area

El área construida se representa mediante curvas de densidad superpuestas, diferenciando casas de apartamentos. Se optó por la densidad en lugar del histograma porque permite mostrar ambas distribuciones en un mismo espacio gráfico y valorar directamente su grado de solapamiento, cosa que dos histogramas encimados no facilitan. La escala se mantuvo logarítmica.

Figura 2. Distribución del área construida según tipo de inmueble

ggplot(vivienda_04_texto, aes(areaconst, fill = tipo)) +
  geom_density(alpha = .55, color = NA) +
  scale_x_log10() +
  scale_fill_manual(values = c("Apartamento" = "#21384a", "Casa" = "#b5751a")) +
  labs(x = "Área construida (m², escala logarítmica)", y = "Densidad", fill = NULL) +
  theme_minimal()

Las dos distribuciones aparecen claramente diferenciadas. Los apartamentos se concentran entre los 50 y los 120 metros cuadrados, mientras que las casas se ubican predominantemente entre los 150 y los 500, con un solapamiento acotado en torno a los 120 y 200 metros cuadrados donde coinciden los apartamentos de mayor tamaño con las casas más pequeñas.

La curva correspondiente a los apartamentos presenta dos máximos locales, uno cercano a los 60 metros cuadrados y otro alrededor de los 85.

Las casas, por su parte, muestran un máximo principal en torno a los 280 metros cuadrados y un segundo relieve menos pronunciado cerca de los 500, con una distribución sensiblemente más ancha que la de los apartamentos.

3.8.3 Variables discretas

Las variables de conteo se representan mediante diagramas de barras de frecuencia absoluta y no mediante curvas de densidad, decisión que responde a su naturaleza discreta.

Figura 3. Frecuencia de baños, habitaciones y parqueaderos

vivienda_04_texto |>
  select(habitaciones, banios, parqueaderos) |>
  pivot_longer(everything(), names_to = "Variable", values_to = "v") |>
  ggplot(aes(factor(v))) +
  geom_bar(fill = "#21384a") +
  facet_wrap(~ Variable, scales = "free", ncol = 3) +
  labs(x = NULL, y = "Frecuencia") +
  theme_minimal()

Las tres variables presentan distribuciones concentradas y con cola hacia los valores altos. El número de baños alcanza su máximo en dos, con cerca de 3.000 inmuebles, y decrece de manera progresiva hacia valores superiores. Las habitaciones muestran una concentración todavía más marcada en tres, con más de 4.000 registros, de modo que los inmuebles de dos, tres y cuatro habitaciones constituyen el grueso de la oferta. En parqueaderos la barra más alta corresponde al valor cero, con aproximadamente 1.600 casos, aunque conviene recordar que esa categoría no proviene de los datos originales sino de la recodificación aplicada en la etapa de tratamiento de ausentes.

3.8.4 Piso y niveles

Las dos variables resultantes de la desagregación de piso se representaron por separado. Al igual que las demás variables de conteo, se emplearon diagramas de barras de frecuencia absoluta.

Figura 4. Distribución del número de niveles en casas y del piso en apartamentos

# Piso y niveles, cada uno en su universo
bind_rows(
  vivienda_04_texto |> filter(tipo == "Apartamento", !is.na(piso_apto)) |>
    transmute(Variable = "Piso (apartamentos)", v = piso_apto),
  vivienda_04_texto |> filter(tipo == "Casa", !is.na(niveles_casa)) |>
    transmute(Variable = "Niveles (casas)", v = niveles_casa)
) |>
  ggplot(aes(factor(v))) +
  geom_bar(fill = "#21384a") +
  facet_wrap(~ Variable, scales = "free") +
  labs(x = NULL, y = "Frecuencia") +
  theme_minimal()

Las casas presentan una concentración marcada en dos niveles, que reúne cerca de 940 registros, con uno y tres niveles como valores secundarios y un descenso abrupto a partir de cuatro; desde cinco en adelante las frecuencias son prácticamente nulas y corresponden a registros que seran revisados como inconsistentes. Los apartamentos, en cambio, muestran una meseta entre los pisos uno y cinco, con frecuencias similares cercanas a los quinientos inmuebles en cada nivel, seguida de un descenso sostenido a partir del sexto.

3.8.5 Boxplots Area y Precio por tipo de Vivienda

Las dos variables continuas se representan mediante diagramas de caja (boxplots) diferenciados por tipo de inmueble, manteniendo la escala logarítmica empleada en los histogramas. Esta representación complementa el conteo de valores extremos realizado previamente al mostrar no solo cuántos son sino dónde se sitúan respecto al cuerpo de la distribución y con qué densidad se distribuyen a lo largo de la cola.

Figura 5. Diagramas de caja del área construida y el precio según tipo de inmueble

# Precio y área, por tipo de inmueble
vivienda_04_texto |>
  select(tipo, preciom, areaconst) |>
  pivot_longer(c(preciom, areaconst), names_to = "Variable", values_to = "v") |>
  mutate(Variable = recode(Variable,
                           preciom = "Precio (millones)",
                           areaconst = "Área construida (m²)")) |>
  ggplot(aes(tipo, v, fill = tipo)) +
  geom_boxplot(outlier.alpha = .25, outlier.size = .8, width = .5) +
  scale_y_log10() +
  scale_fill_manual(values = c("Apartamento" = "#21384a", "Casa" = "#b5751a")) +
  facet_wrap(~ Variable, scales = "free_y") +
  labs(x = NULL, y = "Escala logarítmica") +
  theme_minimal() +
  theme(legend.position = "none")

El comportamiento de los valores extremos difiere entre variables. Los apartamentos presentan una cola superior densamente poblada en área construida, con una concentración notable de casos entre los 300 y los 1.000 metros cuadrados, rango que coincide precisamente con el intervalo normal de las casas. Las casas, por su parte, muestran valores atípicos en el extremo inferior del precio, con registros cercanos a los 70 millones de pesos que no encuentran correspondencia en la distribución de áreas.

3.9 Valores Atípicos

Para la revisión de valores atípicos se tendrá en cuenta dos situaciones que requieren tratamientos distintos. La primera corresponde a valores imposibles, aquellos que no pueden presentarse en la realidad con independencia del contexto. La segunda comprende los extremos legítimos, propiedades efectivamente inusuales pero reales, que no deben eliminarse sino declararse.

3.9.1 Valores imposibles.

Aca se considera que una vivienda destinada a la venta cuenta necesariamente con al menos una habitación y un baño, por lo que estos valores de 0 corresponden a errores de captura.

vivienda_04_texto |>
  filter(habitaciones == 0 | banios == 0) |>
  summarise(
    `Sin habitaciones`      = sum(habitaciones == 0),
    `Sin baños`             = sum(banios == 0),
    `Sin ninguno de los dos`= sum(habitaciones == 0 & banios == 0),
    `Total afectados`       = n()
  ) |>
  tabla_estilo(
    titulo = "Tabla 26. Registros con valores imposibles en habitaciones y baños",
    
  )
Tabla 26. Registros con valores imposibles en habitaciones y baños
Sin habitaciones Sin baños Sin ninguno de los dos Total afectados
66 45 35 76

Se identificaron 76 registros con valores imposibles, equivalentes al 0,9% de la base: 66 declaran cero habitaciones y 45 declaran cero baños. El hallazgo resulta similar con lo observado en parqueaderos, donde la ausencia del atributo se expresaba igualmente mediante un campo vacío.

En consecuencia, estos ceros se recodificaron como valores ausentes y no como cifras a corregir. La decisión mantiene el criterio aplicado a lo largo de todo el preprocesamiento: asignarles una habitación o un baño supondría fabricar información que la base no contiene, mientras que declararlos ausentes preserva la distinción entre lo que se conoce y lo que simplemente no fue registrado.

vivienda_05_atipicos <- vivienda_04_texto |>
  mutate(
    habitaciones = na_if(habitaciones, 0L),
    banios       = na_if(banios, 0L)
  )

3.9.2 Extremos

La identificación de valores extremos se apoya en el criterio del rango intercuartílico, que señala como atípica toda observación situada por encima del tercer cuartil más un múltiplo de la distancia entre cuartiles. Se adoptan dos ajustes respecto a la aplicación convencional del método. El primero fue emplear un factor de tres en lugar del habitual 1,5: dado que ambas distribuciones presentan un sesgo pronunciado hacia los valores altos, el umbral estándar marcaría como atípica una porción considerable de oferta perfectamente legítima, y el factor de tres (conocido como criterio de atipicidad severa) restringe la señalización a los casos verdaderamente alejados del cuerpo de la distribución. El segundo fue calcular los umbrales de manera independiente para casas y apartamentos, decisión que se desprende de la diferencia de escala entre ambos segmentos.

vivienda_04_texto |>
  group_by(tipo) |>
  mutate(
    lim_sup_precio = quantile(preciom, .75) + 3 * IQR(preciom),
    lim_sup_area   = quantile(areaconst, .75) + 3 * IQR(areaconst)
  ) |>
  summarise(
    n                    = n(),
    `Precio extremo`     = sum(preciom > lim_sup_precio),
    `Área extrema`       = sum(areaconst > lim_sup_area),
    `Umbral precio`      = round(first(lim_sup_precio)),
    `Umbral área`        = round(first(lim_sup_area)),
    .groups = "drop"
  ) |>
  rename(Tipo = tipo) |>
  tabla_estilo(
    titulo = "Tabla 27. Valores extremos según el criterio del rango intercuartílico",
  )
Tabla 27. Valores extremos según el criterio del rango intercuartílico
Tipo n Precio extremo Área extrema Umbral precio Umbral área
Apartamento 5.100 165 101 1.195 316
Casa 3.219 34 29 1.780 938

El procedimiento identificó 165 apartamentos y 34 casas con precios extremos, y 101 apartamentos y 29 casas con áreas extremas, lo que representa alrededor del 3,2% y el 2,0% de cada segmento respectivamente. Los umbrales resultantes reflejan con claridad la distancia entre ambos mercados: un apartamento se considera extremo a partir de 1.195 millones de pesos o 316 metros cuadrados, mientras que en las casas esos límites se sitúan en 1.780 millones y 938 metros cuadrados, prácticamente el triple en el caso de la superficie. La diferencia confirma que un criterio único aplicado al conjunto de la base habría clasificado como anómala buena parte de la oferta de casas, cuando su mayor tamaño constituye precisamente el rasgo que las distingue.

Estos registros no se eliminaron ni se modificaron. A diferencia de los valores imposibles examinados previamente, un apartamento de 1.300 millones o una casa de mil metros cuadrados describen inmuebles que existen efectivamente en el segmento alto del mercado, y su exclusión no corregiría un error sino que recortaría la oferta real precisamente en el extremo que resulta más interesante para caracterizar la estructura de precios de la ciudad.

3.10 Detección de Duplicados

Se analizó la base de datos para evaluar la precesia de registros duplicados. No se encontraron duplicados exactos, aunque si se encontraron registros con filas identicas ignorando el identificador. Aunque cabe la posibilidad de que sea el mismo inmueble publicado varias veces, no se tiene ceteza de que sea asi y podría corresponder a apartamentos con las mismas caracteristicas en un mismo edificio. Por lo tanto se opta por conservar estos registros.

tibble(
  Criterio = c("Filas completamente idénticas",
               "Identificadores repetidos",
               "Filas idénticas ignorando el identificador"),
  Duplicados = c(
    sum(duplicated(vivienda_04_texto)),
    sum(duplicated(vivienda_04_texto$id)),
    sum(duplicated(vivienda_04_texto |> select(-id)))
  )
) |>
  tabla_estilo(
    titulo = "Tabla 28. Verificación de registros duplicados",
      )
Tabla 28. Verificación de registros duplicados
Criterio Duplicados
Filas completamente idénticas 0
Identificadores repetidos 0
Filas idénticas ignorando el identificador 57
vivienda_05_atipicos <- vivienda_04_texto |>
  mutate(
    habitaciones = na_if(habitaciones, 0L),
    banios       = na_if(banios, 0L)
  )

vivienda_limpia <- vivienda_05_atipicos

3.11 Correlaciones entre variables

La elección del coeficiente de correlación exige verificar previamente el supuesto de normalidad, dado que el coeficiente de Pearson asume distribuciones aproximadamente normales y resulta sensible a la presencia de valores extremos.

3.11.1 Prueba Shapiro-Wilk

library(moments)

set.seed(123)
muestra <- vivienda_limpia |> slice_sample(n = 4000)

map_dfr(c("preciom", "areaconst", "habitaciones", "banios", "parqueaderos"), ~ {
  x  <- muestra[[.x]] |> na.omit()
  sw <- shapiro.test(x)
  tibble(
    Variable   = .x,
    Asimetría  = round(skewness(x), 2),
    Curtosis   = round(kurtosis(x), 2),
    `W`        = round(sw$statistic, 3),
    `Valor p`  = format.pval(sw$p.value, digits = 3, eps = 0.001)
  )
}) |>
  tabla_estilo(
    titulo = "Tabla 29. Prueba de normalidad de Shapiro-Wilk",
    grupos = c(" " = 1, "Forma de la distribución" = 2, "Shapiro-Wilk" = 2),
    nota   = "Calculada sobre una muestra aleatoria de 4.000 registros, dado que la prueba admite un máximo de 5.000 observaciones. En una distribución normal la asimetría es 0 y la curtosis 3."
  )
Tabla 29. Prueba de normalidad de Shapiro-Wilk
Forma de la distribución
Shapiro-Wilk
Variable Asimetría Curtosis W Valor p
preciom 1.79 6.45 0.818 <0.001
areaconst 2.67 16.00 0.757 <0.001
habitaciones 1.82 7.09 0.783 <0.001
banios 0.92 3.83 0.895 <0.001
parqueaderos 1.56 7.98 0.836 <0.001
Nota. Calculada sobre una muestra aleatoria de 4.000 registros, dado que la prueba admite un máximo de 5.000 observaciones. En una distribución normal la asimetría es 0 y la curtosis 3.

3.11.2 Q-Q Plot

Figura 6. Q-Q Plot

vivienda_limpia |>
  select(preciom, areaconst, habitaciones, banios, parqueaderos) |>
  pivot_longer(everything(), names_to = "Variable", values_to = "v") |>
  filter(!is.na(v)) |>
  ggplot(aes(sample = v)) +
  stat_qq(size = .4, alpha = .25, color = "#21384a") +
  stat_qq_line(color = "#a33a3a") +
  facet_wrap(~ Variable, scales = "free", nrow = 2) +
  labs(x = "Cuantiles teóricos", y = "Cuantiles observados") +
  theme_minimal()

La prueba de Shapiro-Wilk descartar la normalidad de las cinco variables cuantitativas rechazando la hipótesis nula en todos los casos con valores p inferiores a 0,001. El área construida presenta el caso más extremo, con una asimetría de 2,67 y una curtosis de 16,00, valor este último más de cinco veces superior al que corresponde a una distribución normal; ello indica colas considerablemente más pesadas de lo admisible bajo ese modelo, esto es, una frecuencia de valores alejados del centro muy superior a la esperada. El precio presenta un comportamiento similar aunque menos pronunciado, con 1,79 de asimetría y 6,45 de curtosis, y los estadísticos W oscilan entre 0,757 y 0,895, todos ellos distantes de la unidad.

Los graficos Q-Q plot, confirman visualmente que los datos no tienen una distribución normal.

Este conjunto de resultados justifica el empleo del coeficiente de correlación de Spearman en lugar del de Pearson.

3.11.3 Matriz de Correlaciones

Figura 7. Matriz de Correlaciones

library(corrplot)

vivienda_limpia |>
  select(preciom, areaconst, habitaciones, banios, parqueaderos) |>
  cor(use = "complete.obs", method = "spearman") |>
  corrplot(method = "color", type = "upper", addCoef.col = "white",
           tl.col = "#21384a", tl.srt = 45, number.cex = .85,
           col = colorRampPalette(c("#a33a3a", "white", "#21384a"))(200))

La matriz de correlaciones de Spearman muestra asociaciones positivas y de magnitud apreciable entre las cinco variables, sin que ninguna resulte débil. El área construida es el atributo más estrechamente vinculado al precio, con un coeficiente de 0,82, seguida del número de baños con 0,78 y del de parqueaderos con 0,67. El número de habitaciones, en cambio, alcanza únicamente 0,44.

Entre las variables predictoras se observa la asociación entre área construida y número de baños, que alcanza 0,79 siendo la más elevada del conjunto. Esta magnitud implica que ambas aportan información redundante, podria generar multicolinealidad y comprometer la estabilidad de los coeficientes. En el extremo opuesto se sitúa la relación entre habitaciones y parqueaderos, con apenas 0,24.

3.12 Verificar Dataset limpio

vivienda_limpia <- vivienda_05_atipicos

vivienda_limpia <- vivienda_limpia |>
  mutate(barrio_norm = fct_infreq(barrio_norm))

cat("Niveles de barrio_norm:", nlevels(vivienda_limpia$barrio_norm))
#> Niveles de barrio_norm: 361
cat("Registros:", nrow(vivienda_limpia), "| Variables:", ncol(vivienda_limpia))
#> Registros: 8319 | Variables: 17
tibble(
  Variable = names(vivienda_limpia),
  Tipo     = map_chr(vivienda_limpia, ~ class(.x)[1]),
  Ausentes = map_int(vivienda_limpia, ~ sum(is.na(.x)))
) |>
  mutate(`%` = sprintf("%.1f", 100 * Ausentes / nrow(vivienda_limpia))) |>
  tabla_estilo(
    titulo = "Tabla 30. Estado final del conjunto de datos depurado",
    nota   = "La base conserva los 8.319 registros resultantes de la eliminación inicial de filas sin información sustantiva."
  )
Tabla 30. Estado final del conjunto de datos depurado
Variable Tipo Ausentes %
id character 0 0.0
zona factor 0 0.0
piso integer 2.635 31.7
estrato ordered 0 0.0
preciom numeric 0 0.0
areaconst numeric 0 0.0
parqueaderos integer 0 0.0
banios integer 45 0.5
habitaciones integer 66 0.8
tipo factor 0 0.0
barrio character 0 0.0
longitud numeric 0 0.0
latitud numeric 0 0.0
piso_apto integer 4.600 55.3
niveles_casa integer 6.354 76.4
piso_registrado factor 0 0.0
barrio_norm factor 0 0.0
Nota. La base conserva los 8.319 registros resultantes de la eliminación inicial de filas sin información sustantiva.

El conjunto de datos depurado conserva los 8.319 registros resultantes de la eliminación inicial de las filas sin información sustantiva, y suma cuatro variables a las trece originales. Tres de ellas provienen de la desagregación del número de piso según el tipo de inmueble y del indicador que distingue los registros con y sin ese dato; la cuarta corresponde a la versión depurada del nombre del barrio, que se conserva junto a la original con el fin de documentar el punto de partida y permitir la verificación de las equivalencias aplicadas.

Todas las variables quedaron tipificadas conforme a su naturaleza conceptual: el estrato como factor ordenado, la zona, el tipo de inmueble y el barrio como factores nominales, el identificador como texto para excluirlo de los cálculos estadísticos, y las variables de conteo como enteros. Las ausencias remanentes se concentran en las variables derivadas del número de piso y responden en su mayor parte a inmuebles del tipo al que la variable no aplica, según se detalló en el balance de completitud. Las restantes corresponden a los registros cuyos valores imposibles en habitaciones y baños fueron recodificados como datos faltantes, decisión coherente con el criterio adoptado a lo largo de todo el preprocesamiento: preservar la distinción entre lo que se conoce y lo que no fue registrado, antes que completar los vacíos con estimaciones carentes de respaldo en los datos.

saveRDS(vivienda_limpia, "vivienda_limpia.rds")

3.13 Analisis de Componentes Principales (PCA)

3.13.1 Selección de Variables

Para el PCA se seleccionan las variables cuantitativas completas, en nuestro caso área, precio, habitaciones, baños y parqueaderos. Las coordenadas quedan por fuera al representar la ubicación del inmueble (no un atributo). La variable piso se excluye del analisis, pues dados los valores ausentes obligaria a descartar 31.7% de los registros.

library(FactoMineR)
library(factoextra)

datos_acp <- vivienda_limpia |>
  select(preciom, areaconst, habitaciones, banios, parqueaderos) |>
  drop_na()

cat("Registros para el ACP:", nrow(datos_acp),
    "de", nrow(vivienda_limpia),
    sprintf("(%.1f%%)", 100 * nrow(datos_acp) / nrow(vivienda_limpia)))
#> Registros para el ACP: 8243 de 8319 (99.1%)

3.13.2 Estandarización

Con el fin de evitar que las variables que tiene una escala con valores más grandes afecten las estimaciones realizadas (sesgos) se realiza la estandarización de las variables antes de proceder a realizar el proceso de estimación de los componentes principales. Como se mencioncionó anteriormente utilizaremos el logaritmo de precio y area para los análisis.

datos_acp <- vivienda_limpia |>
  transmute(
    log_precio   = log(preciom),
    log_area     = log(areaconst),
    habitaciones,
    banios,
    parqueaderos
  ) |>
  drop_na()

acp <- PCA(datos_acp, scale.unit = TRUE, graph = FALSE)

3.14 Porcentaje de Varianza Explicada

Figura 8. Porcentaje de Varianza Explicada

fviz_eig(acp, addlabels = TRUE, barfill = "#21384a", barcolor = "#21384a") +
  labs(title = NULL, x = "Componente", y = "% de varianza explicada") +
  theme_minimal()

acp$eig |> round(2)
#>        eigenvalue percentage of variance cumulative percentage of variance
#> comp 1       3.35                  67.03                             67.03
#> comp 2       0.89                  17.74                             84.77
#> comp 3       0.39                   7.73                             92.50
#> comp 4       0.25                   4.95                             97.45
#> comp 5       0.13                   2.55                            100.00

El primer componente concentra el 67,0% de la varianza total, con un autovalor de 3,35 que supera el umbral convencional. Una participación de esa magnitud indica que las cinco variables comparten un factor común dominante: los inmuebles de mayor superficie tienden simultáneamente a contar con más habitaciones, más baños, más parqueaderos y un precio superior, de modo que buena parte de la información contenida en las cinco variables originales puede resumirse en una única dimensión de tamaño y valor. El segundo componente aporta un 17,7% adicional y eleva la varianza acumulada al 84,8%. De acuerdo a esto, se opta por retener dos componentes.

3.14.1 Círculo de Correlaciones

Figura 9. Círculo de Correlaciones

acp$var$coord |> round(2)
#>              Dim.1 Dim.2 Dim.3 Dim.4 Dim.5
#> log_precio    0.88 -0.28 -0.28  0.07  0.24
#> log_area      0.92  0.10 -0.18  0.27 -0.22
#> habitaciones  0.65  0.70  0.27  0.07  0.12
#> banios        0.90  0.11 -0.09 -0.41 -0.08
#> parqueaderos  0.71 -0.55  0.44  0.02 -0.02
fviz_pca_var(acp, col.var = "contrib", repel = TRUE,
             gradient.cols = c("#6b7a85", "#b5751a", "#a33a3a")) +
  labs(title = NULL) +
  theme_minimal()

El círculo de correlaciones permite atribuir un significado sustantivo a cada componente. En la primera dimensión, las cinco variables se orientan en el mismo sentido y alcanzan longitudes próximas al borde del círculo, sin que ninguna se oponga a las demás. La ausencia de contraposición confirma que este componente no distingue entre atributos sino que los sintetiza: los inmuebles situados hacia la derecha del plano son simultáneamente más amplios, más costosos y mejor dotados en habitaciones, baños y parqueaderos. Las contribuciones individuales oscilan además en un rango muy estrecho, entre el 19,0% y el 21,0%, de manera que ninguna variable domina la construcción del eje y todas participan de forma equilibrada. El componente puede interpretarse, en consecuencia, como una dimensión general de tamaño y valor del inmueble.

La segunda dimensión revela una estructura distinta, pues aquí sí se produce una oposición entre variables. El número de habitaciones se proyecta con fuerza hacia el extremo positivo, mientras el precio y el número de parqueaderos lo hacen hacia el negativo; el área construida y el número de baños permanecen próximos al eje horizontal, sin participación apreciable en este componente.

3.15 Proyección de los individuos

Una vez interpretados los ejes a partir del círculo de correlaciones, se proyectaron los inmuebles sobre el plano factorial diferenciando casas de apartamentos. La posición horizontal de cada punto expresa su valor en la dimensión de tamaño y precio, mientras que la vertical refleja la relación entre el número de habitaciones y el nivel de precio y dotación. Se incorporaron elipses de confianza al 95% con el fin de resumir la ubicación de cada grupo, recurso necesario dado que la superposición de más de ocho mil puntos impide apreciar las concentraciones a simple vista.

Figura 10. Proyección de los individuos

base_acp <- vivienda_limpia |>
  drop_na(preciom, areaconst, habitaciones, banios, parqueaderos)

fviz_pca_ind(acp, geom = "point", pointsize = .6, alpha.ind = .15,
             habillage = base_acp$tipo,
             palette = c("#21384a", "#b5751a"),
             addEllipses = TRUE, ellipse.level = .95) +
  labs(title = NULL) + theme_minimal()

La proyección revela dos diferencias entre ambos tipos de inmueble. La primera es de posición: las casas se desplazan hacia la derecha del plano, resultado esperable dado que la dimensión horizontal expresa tamaño y valor, pero también hacia la parte superior, lo que indica que ofrecen un número de habitaciones más elevado en relación con su precio y su dotación de parqueaderos. La segunda es de dispersión, la elipse correspondiente a los apartamentos es estrecha y marcadamente alargada, mientras que la de las casas ocupa una superficie considerablemente mayor. Los apartamentos constituyen así un producto homogéneo, con configuraciones que se repiten dentro de márgenes estrechos, en tanto que las casas admiten combinaciones mucho más diversas de tamaño, distribución interna y precio

3.15.1 Análisis por Estrato

La misma proyección se replicó diferenciando los inmuebles según su estrato socioeconómico, con el propósito de verificar si esta clasificación administrativa se corresponde con configuraciones distintas del producto inmobiliario o si opera únicamente como un indicador de ubicación.

Figura 11. Análisis por Estrato

fviz_pca_ind(acp, geom = "point", pointsize = .6, alpha.ind = .12,
             habillage = base_acp$estrato,
             palette = c("#6b7a85", "#b5751a", "#0f766e", "#21384a"),
             addEllipses = TRUE, ellipse.level = .95) +
  labs(title = NULL) + theme_minimal()

Los cuatro estratos se ordenan a lo largo del eje horizontal en el sentido esperado: el centro de cada elipse se desplaza progresivamente hacia la derecha conforme aumenta el nivel, de manera que los inmuebles de estrato 6 presentan en promedio mayor tamaño, precio y dotación que los de estrato 3. La relación entre estrato y valor del inmueble queda por tanto confirmada. Por otro lado, las cuatro elipses comparten la mayor parte de su superficie, y la región central del plano reúne inmuebles de todos los estratos sin que ninguno predomine. La especialización solo aparece en los extremos, con algunos inmuebles de estrato 3 en el borde izquierdo y de estrato 6 en el derecho.

3.16 Análisis de Conglomerados

La segmentación de los inmuebles se realizó mediante el algoritmo de k-means aplicado sobre las coordenadas de los dos primeros componentes principales, en lugar de sobre las variables originales.La determinación del número de conglomerados se apoyó en dos criterios, la suma de cuadrados intragrupo, que evalúa la homogeneidad interna, y el ancho de silueta promedio, que mide el grado de separación entre grupos.

Figura 12. Suma de cuadrados Intragrupos y Ancho de Silueta promedio

base_acp <- base_acp |>
  mutate(dim1 = acp$ind$coord[, 1],
         dim2 = acp$ind$coord[, 2])
library(cluster)

set.seed(123)
datos_cl <- base_acp |> select(dim1, dim2)

fviz_nbclust(datos_cl, kmeans, method = "wss", k.max = 10) +
  labs(title = NULL, x = "Número de conglomerados", y = "Suma de cuadrados intragrupo") +
  theme_minimal()

fviz_nbclust(datos_cl, kmeans, method = "silhouette", k.max = 10) +
  labs(title = NULL, x = "Número de conglomerados", y = "Ancho de silueta promedio") +
  theme_minimal()

La curva de suma de cuadrados intragrupo desciende de manera pronunciada hasta el cuarto conglomerado y se aplana visiblemente a partir de ese punto, situando el codo entre tres y cuatro grupos. El ancho de silueta, por su parte, alcanza su valor máximo con dos conglomerados (0,49) y desciende con tres, para repuntar de forma apreciable en cuatro (0,435) y deteriorarse de manera sostenida a partir de ahí. Ese máximo local resulta significativo, pues indica que la estructura de cuatro grupos no es arbitraria sino que responde a una organización efectivamente presente en los datos.

3.16.1 Agrupamiento

Figura 13. Agrupamiento

set.seed(123)
km <- kmeans(datos_cl, centers = 4, nstart = 25)

base_acp <- base_acp |> mutate(cluster = factor(km$cluster))

fviz_cluster(km, data = datos_cl, geom = "point", pointsize = .5,
             alpha = .15, ellipse.type = "norm",
             palette = c("#21384a", "#b5751a", "#0f766e", "#a33a3a")) +
  labs(title = NULL) + theme_minimal()

base_acp |>
  group_by(cluster) |>
  summarise(
    n                 = n(),
    `%`               = sprintf("%.1f", 100 * n() / nrow(base_acp)),
    `Precio mediano`  = median(preciom),
    `Área mediana`    = median(areaconst),
    Habitaciones      = median(habitaciones),
    `Baños`           = median(banios),
    Parqueaderos      = median(parqueaderos),
    `% casas`         = sprintf("%.1f", 100 * mean(tipo == "Casa")),
    .groups = "drop"
  ) |>
  tabla_estilo(
    titulo = "Tabla 31. Caracterización de los conglomerados",
    nota   = "Los conglomerados se construyeron sobre las dos primeras componentes principales, que sintetizan el 84,8% de la variabilidad de las variables cuantitativas."
  )
Tabla 31. Caracterización de los conglomerados
cluster n % Precio mediano Área mediana Habitaciones Baños Parqueaderos % casas
1 2.792 33.9 420 150.0 3 3 2 40.2
2 829 10.1 430 300.0 6 4 1 97.3
3 3.501 42.5 199 75.0 3 2 1 14.0
4 1.121 13.6 960 322.4 4 5 3 66.7
Nota. Los conglomerados se construyeron sobre las dos primeras componentes principales, que sintetizan el 84,8% de la variabilidad de las variables cuantitativas.

La división en cuatro conglomerados produce segmentos diferenciados y susceptibles de caracterización. El más numeroso reúne el 42,5% de la oferta y corresponde a vivienda compacta de gama económica: 75 metros cuadrados de superficie mediana, 199 millones de precio, tres habitaciones y un solo baño y parqueadero, con predominio marcado de apartamentos, que representan el 86% del grupo.

Un segundo conglomerado agrupa el 33,9% y constituye el segmento intermedio del mercado, con 150 metros cuadrados y 420 millones; su composición mixta (40% casas y 60% apartamentos) lo identifica como la franja donde ambos tipos de inmueble compiten directamente por el mismo comprador, confirmando el solapamiento advertido en la proyección factorial.

El tercero, que agrupa el 10,1% de los inmuebles y está compuesto casi exclusivamente por casas (97,3%), presenta 300 metros cuadrados de superficie y seis habitaciones, pero un precio mediano de 430 millones, prácticamente idéntico al del segmento intermedio pese a duplicar su área.

El cuarto, con el 13,6% de la oferta, alcanza los 960 millones sobre una superficie de 322 metros cuadrados, apenas superior a la del anterior. Dos segmentos de tamaño equivalente presentan así precios que difieren en más del doble.

3.17 Análisis de Correspondencia

3.17.1 Zonas por Tipo

tabla_zt <- table(vivienda_limpia$zona, vivienda_limpia$tipo)
tabla_zt
#>               
#>                Apartamento Casa
#>   Zona Centro           24  100
#>   Zona Norte          1198  722
#>   Zona Oeste          1029  169
#>   Zona Oriente          62  289
#>   Zona Sur            2787 1939
chisq.test(tabla_zt)
#> 
#>  Pearson's Chi-squared test
#> 
#> data:  tabla_zt
#> X-squared = 691, df = 4, p-value <0.0000000000000002
round(chisq.test(tabla_zt)$residuals, 2)
#>               
#>                Apartamento   Casa
#>   Zona Centro        -5.97   7.51
#>   Zona Norte          0.61  -0.77
#>   Zona Oeste         10.87 -13.68
#>   Zona Oriente      -10.44  13.14
#>   Zona Sur           -2.05   2.58
library(FactoMineR)
library(factoextra)

barrios_top <- vivienda_limpia |>
  count(barrio_norm, sort = TRUE) |>
  filter(n >= 50) |>
  pull(barrio_norm)

tabla_bt <- vivienda_limpia |>
  filter(barrio_norm %in% barrios_top) |>
  count(barrio_norm, tipo) |>
  pivot_wider(names_from = tipo, values_from = n, values_fill = 0) |>
  column_to_rownames("barrio_norm")

cat("Barrios incluidos:", nrow(tabla_bt),
    "| Registros:", sum(tabla_bt),
    sprintf("(%.1f%% de la base)", 100 * sum(tabla_bt) / nrow(vivienda_limpia)))
#> Barrios incluidos: 36 | Registros: 5635 (67.7% de la base)

3.17.2 Barrio por Conglomerado - Biplot

Para examinar la relación entre los barrios y los segmentos de mercado identificados se aplicó un análisis de correspondencias simple sobre la tabla de contingencia que cruza ambas variables. Este método proyecta filas y columnas en un espacio común donde la proximidad entre puntos expresa asociación relativa: un barrio se sitúa cerca de un conglomerado cuando la presencia de ese segmento en su oferta supera la que cabría esperar bajo independencia. Lo que se representa son afinidades y no frecuencias absolutas, de modo que un barrio pequeño puede aparecer fuertemente asociado a un segmento si este domina su oferta. El análisis se restringió a los barrios con al menos cincuenta inmuebles registrados, dado que en aquellos con pocos registros la composición por segmentos resulta demasiado inestable para admitir interpretación.

Figura 14. Biplot Barrio por Conglomerado

tabla_bc <- base_acp |>
  filter(barrio_norm %in% barrios_top) |>
  count(barrio_norm, cluster) |>
  pivot_wider(names_from = cluster, values_from = n, values_fill = 0) |>
  column_to_rownames("barrio_norm")

ca_bc <- CA(tabla_bc, graph = FALSE)

fviz_ca_biplot(ca_bc, repel = TRUE, col.row = "#6b7a85", col.col = "#a33a3a") +
  labs(title = NULL) + theme_minimal()

ca_bc$eig |> round(2)
#>       eigenvalue percentage of variance cumulative percentage of variance
#> dim 1       0.42                  74.95                             74.95
#> dim 2       0.10                  17.68                             92.63
#> dim 3       0.04                   7.37                            100.00

Las dos primeras dimensiones recogen el 92,6% de la asociación entre barrios y segmentos, por lo que el plano ofrece una representación prácticamente completa de la estructura. La primera dimensión concentra por sí sola el 74,9% y opone el conglomerado económico compacto al de alta gama, configurándose como un eje de valor: la diferencia fundamental entre los barrios de la ciudad reside en el nivel de precio de la vivienda que ofrecen. En su extremo derecho se ubican Pance, Parcelaciones Pance, Ciudad Jardín, Santa Teresita y Normandía, sectores donde predomina la oferta de mayor valor; en el opuesto se agrupan Torres de Comfandi, Brisas de los Álamos, Villa del Prado, Valle del Lili y Meléndez, asociados a la vivienda compacta de gama económica.

La segunda dimensión, con el 17,7% de la asociación, sitúa en su extremo superior al conglomerado de casas amplias con precio moderado y, en torno a él, a San Fernando, Tequendama, El Bosque, Santa Isabel y Ciudad 2000. Se trata de barrios tradicionales de la ciudad, caracterizados por viviendas unifamiliares de superficie considerable y un número elevado de habitaciones, cuyo precio no se corresponde con esas dimensiones. Este eje no distingue por nivel de valor sino por configuración del espacio, y confirma en el plano territorial la dimensión que el análisis de componentes principales había identificado en las variables cuantitativas.La región central del gráfico reúne al segmento intermedio junto a barrios como Los Cristales, La Flora, El Ingenio y Versalles, cuya oferta no presenta especialización apreciable hacia ninguno de los segmentos.

3.18 Visualización de Resultados

Figura 15. Mapa Interactivo Segmentos del Mercado

library(leaflet)

pal <- colorFactor(c("#21384a", "#b5751a", "#0f766e", "#a33a3a"),
                   domain = mapa_datos$cluster)

leaflet(mapa_datos) |>
  addProviderTiles("CartoDB.Positron") |>
  addCircleMarkers(~longitud, ~latitud, color = ~pal(cluster),
                   radius = 2, stroke = FALSE, fillOpacity = .5,
                   popup = ~paste0(barrio_norm, "<br>",
                                   preciom, " millones · ", areaconst, " m²")) |>
  addLegend(pal = pal, values = ~cluster, title = "Segmento")

Figura 16. Mapa Segmentos del Mercado por Separado

ggplot(mapa_datos, aes(longitud, latitud)) +
  geom_point(data = mapa_datos |> select(-cluster),
             color = "grey88", size = .3) +
  geom_point(aes(color = cluster), size = .5, alpha = .5) +
  scale_color_manual(values = c("1" = "#21384a", "2" = "#b5751a",
                                "3" = "#0f766e", "4" = "#a33a3a"),
                     guide = "none") +
  facet_wrap(~ cluster, nrow = 1) +
  coord_fixed() +
  labs(x = NULL, y = NULL) +
  theme_minimal() +
  theme(axis.text = element_blank(), panel.grid = element_blank())

La representación cartográfica por segmentos revela huellas territoriales diferenciadas. El segmento de alta gama (rojo) presenta la distribución más concentrada de los cuatro: se agrupa en dos núcleos bien delimitados, uno situado en el occidente central de la ciudad y otro en el extremo sur, mientras que el resto del territorio queda prácticamente vacío de esta oferta. Estos núcleos se corresponden con la Zona Oeste y con el sector de Pance.

El segmento económico compacto (verde) exhibe el patrón complementario, con una cobertura territorial mucho más extensa que se concentra en el norte y el centro-oriente, además de una mancha particularmente densa en el sector sur-central. Las áreas donde un panel predomina son precisamente aquellas donde el otro se encuentra ausente, de modo que la ciudad presenta una segregación espacial pronunciada entre la oferta de gama alta y la de entrada.

El segmento intermedio (azul) se distribuye siguiendo una franja continua por el flanco occidental, extendida de norte a sur, configuración que sugiere su asociación con un corredor urbano específico antes que con sectores aislados. El de casas amplias con precio moderado (amarillo), en cambio, aparece disperso por el conjunto de la ciudad sin concentración definida, con presencia apreciable en el oriente. Esta dispersión resulta coherente con su naturaleza: se trata de vivienda unifamiliar tradicional presente en barrios de distintas épocas y ubicaciones, cuya afinidad no responde a una localización común sino a una configuración constructiva compartida.

3.18.1 Composición del Mercado

Con los conglomerados ya caracterizados y nombrados según su perfil, se representó el peso relativo de cada segmento sobre el total de la oferta analizada. Las barras se ordenaron de mayor a menor participación y se etiquetaron con su porcentaje correspondiente, de modo que la lectura no requiera consultar el eje. Esta representación cumple una función distinta a la de las tablas de caracterización presentadas previamente: mientras aquellas describen qué distingue a cada segmento en cuanto a superficie, precio y dotación, esta muestra cuánta oferta representa cada uno en el mercado, información necesaria para dimensionar la relevancia comercial de los hallazgos.

Figura 17. Composición del Mercado

base_acp |>
  count(cluster, name = "n") |>
  mutate(pct = 100 * n / sum(n),
         etiqueta = c("Medio", "Casas amplias\nprecio moderado",
                      "Económico\ncompacto", "Alta gama")) |>
  ggplot(aes(fct_reorder(etiqueta, pct), pct, fill = cluster)) +
  geom_col(width = .6) +
  geom_text(aes(label = sprintf("%.1f%%", pct)), hjust = -.15, size = 3.5) +
  scale_fill_manual(values = c("#21384a", "#b5751a", "#0f766e", "#a33a3a"),
                    guide = "none") +
  coord_flip(clip = "off") +
  labs(x = NULL, y = "% de la oferta") +
  theme_minimal()

El mercado analizado se encuentra dominado por sus dos segmentos de menor valor, que en conjunto reúnen el 76,4% de la oferta. La vivienda económica compacta encabeza la distribución con el 42,5%, seguida del segmento intermedio con el 33,9%. Los dos restantes ocupan posiciones considerablemente más modestas: la alta gama alcanza el 13,6% y las casas amplias de precio moderado apenas el 10,1%.

4 Concluciones

El análisis de componentes principales cumplió el propósito de reducir la dimensionalidad del conjunto y resultó particularmente eficaz en este caso: dos componentes bastaron para sintetizar el 84,8% de la variabilidad contenida en cinco variables. El primero, que recoge el 67,0%, reveló que los atributos físicos de una vivienda no aportan información independiente entre sí, sino que responden a un factor común de tamaño y valor; las cinco variables se orientan en el mismo sentido con contribuciones prácticamente equivalentes, entre el 19,0% y el 21,0%. El segundo componente aportó el hallazgo de mayor interés analítico al contraponer el número de habitaciones al precio y a la dotación de parqueaderos, distinguiendo así entre dos estrategias de aprovechamiento del espacio que operan con independencia del tamaño total del inmueble. Este eje existe fundamentalmente para acomodar la variabilidad del número de habitaciones que el primer componente no logra capturar: dicha variable alcanza una calidad de representación superior a la que obtiene en la dimensión principal. La proyección de los individuos añadió un elemento que las variables por sí solas no revelaban, al mostrar que los apartamentos ocupan una región del plano considerablemente más estrecha que las casas, reflejo de la estandarización constructiva propia de la vivienda multifamiliar.

La segmentación mediante k-medias, aplicada sobre las coordenadas factoriales en lugar de sobre las variables originales, permitió superar la redundancia que las correlaciones elevadas habrían introducido en el cálculo de distancias. En cuanto a la determinación del número de grupos, el índice de silueta señalaba dos conglomerados como solución óptima, la curva de inercia situaba el codo en cuatro, y fue este último criterio el que prevaleció por razones sustantivas antes que estadísticas. Una partición binaria habría reproducido la distinción entre inmuebles grandes y pequeños, ya conocida, sin ofrecer base alguna para diferenciar una estrategia comercial. La solución de cuatro segmentos, en cambio, resultó plenamente interpretable y produjo el hallazgo central del anaálisis: dos conglomerados de superficie prácticamente idéntica (300 y 322 metros cuadrados) presentan precios medianos de 430 y 960 millones de pesos. Este resultado, que el análisis univariado no habría permitido detectar, confirma que la superficie construida considerada aisladamente constituye un indicador insuficiente del valor de un inmueble. El método demostró así su utilidad no tanto para clasificar como para revelar estructuras: los segmentos obtenidos no coinciden con ninguna clasificación previa (ni el tipo de inmueble ni el estrato los reproducen) y su cruce posterior con esas variables aportó una lectura que ninguna de ellas ofrecía por separado.

Por último, el análisis de correspondencias incorporó las variables categóricas, cuya naturaleza impide su tratamiento mediante los métodos anteriores. Su aplicación al cruce entre barrios y segmentos de mercado resultó especialmente productiva: las dos primeras dimensiones recogieron el 92,6% de la asociación, y la primera de ellas, con el 74,9%, configuró un eje de valor que ordena los barrios de la ciudad según el nivel de precio de su oferta. La segunda dimensión reprodujo en el plano territorial la misma distinción que el análisis de componentes principales había identificado en las variables cuantitativas, situando en su extremo superior a los barrios tradicionales caracterizados por casas amplias de precio moderado; esta convergencia entre dos métodos de naturaleza distinta refuerza la solidez del hallazgo. La principal restricción del método se manifestó en la necesidad de excluir los barrios con menos de cincuenta registros, decisión que dejó fuera del mapa una porción de la oferta cuya composición resultaba demasiado inestable para admitir interpretación.

5 Referencias

Centro Magis (2026). Material del curso Modelos Estadisticos para la Toma de Desiciones, versión 02-2026. Maestría en Ciencia de Datos, Pontificia Universidad Javeriana Cali, Facultad de Ingeniería.

C. E. Pardo, Estadística descriptiva multivariada, 1.ª ed. Bogotá, Colombia: Universidad Nacional de Colombia, 2020. [En línea]. Disponible: [chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/https://bffrepositorio.unal.edu.co/server/api/core/bitstreams/a32a836b-8e54-4eb7-b2ef-aed7019be2c1/content]

Declaración sobre el uso de inteligencia artificial

Durante la elaboración de este trabajo se utilizó Claude (Anthropic, 2025) como herramienta de apoyo metodológico. Su empleo se concentró en la discusión de criterios para el tratamiento de datos ausentes y valores atípicos, la revisión de la sintaxis en R y la redacción de los apartados descriptivos. Las decisiones analíticas (la desagregación de la variable piso, la recodificación de los valores de parqueaderos, la exclusión de la imputación y la determinación del número de conglomerados) fueron adoptadas por el autor tras contrastar las alternativas planteadas. La interpretación de los resultados y las conclusiones son de su exclusiva responsabilidad.

LS0tDQp0aXRsZTogIkFjdGl2aWRhZCAxOiBFdmFsdWFjacOzbiBkZSBsYSBvZmVydGEgaW5tb2JpbGlhcmlhIHVyYmFuYSINCmF1dGhvcjogIkNhbWlsbyBBbmRyw6lzIFB1ZW50ZXMgTWFyw61uIg0KZGF0ZTogImByIHBhc3RlKGZvcm1hdChTeXMuRGF0ZSgpLCAnJWQnKSwgJ2RlJywgYygnZW5lcm8nLCdmZWJyZXJvJywnbWFyem8nLCdhYnJpbCcsJ21heW8nLCdqdW5pbycsJ2p1bGlvJywnYWdvc3RvJywnc2VwdGllbWJyZScsJ29jdHVicmUnLCdub3ZpZW1icmUnLCdkaWNpZW1icmUnKVthcy5pbnRlZ2VyKGZvcm1hdChTeXMuRGF0ZSgpLCAnJW0nKSldLCAnZGUnLCBmb3JtYXQoU3lzLkRhdGUoKSwgJyVZJykpYCINCmxhbmc6IGVzDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGZpZ19jYXB0aW9uOiB0cnVlDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KICAgMS4gVElQT0dSQUbDjUEg4oCUIGZ1ZW50ZXMgd2ViIHBhcmEgcXVlIGVsIGRvY3VtZW50byBzZSB2ZWEgaWd1YWwNCiAgICAgIGVuIGN1YWxxdWllciBjb21wdXRhZG9yIHF1ZSBsbyBhYnJhIGRlc2RlIFJQdWJzDQogICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAqLw0KQGltcG9ydCB1cmwoJ2h0dHBzOi8vZm9udHMuZ29vZ2xlYXBpcy5jb20vY3NzMj9mYW1pbHk9U3BlY3RyYWw6aXRhbCx3Z2h0QDAsNDAwOzAsNjAwOzEsNDAwJmZhbWlseT1Tb3VyY2UrU2FucyszOml0YWwsd2dodEAwLDQwMDswLDYwMDswLDcwMDsxLDQwMCZmYW1pbHk9SmV0QnJhaW5zK01vbm86d2dodEA0MDA7NTAwJmRpc3BsYXk9c3dhcCcpOw0KDQo6cm9vdCB7DQogIC0tdGludGE6ICAgICMxYzJiMzY7ICAgLyogdGV4dG8gY29ycmlkbyAgICAgICAgICAgICovDQogIC0tcGl6YXJyYTogICMyMTM4NGE7ICAgLyogdMOtdHVsb3MgeSByZWdsYXMgICAgICAgICAqLw0KICAtLWFjZW50bzogICAjMGY3NjZlOyAgIC8qIGVubGFjZXMgeSBlbGVtZW50b3Mgdml2b3MqLw0KICAtLWdyaXM6ICAgICAjZjVmN2Y4OyAgIC8qIGZvbmRvcyBzdWF2ZXMgICAgICAgICAgICAqLw0KICAtLWJvcmRlOiAgICAjZGZlNWU5OyAgIC8qIGJvcmRlcyBkaXNjcmV0b3MgICAgICAgICAqLw0KICAtLXRlbnVlOiAgICAjNmI3YTg1OyAgIC8qIGxleWVuZGFzIHkgbm90YXMgYWwgcGllICAqLw0KfQ0KDQpib2R5IHsNCiAgZm9udC1mYW1pbHk6ICdTb3VyY2UgU2FucyAzJywgJ09wZW4gU2FucycsICdTZWdvZSBVSScsIHNhbnMtc2VyaWY7DQogIGZvbnQtc2l6ZTogMTZweDsNCiAgbGluZS1oZWlnaHQ6IDEuNjg7DQogIGNvbG9yOiB2YXIoLS10aW50YSk7DQogIHRleHQtYWxpZ246IGp1c3RpZnk7DQogIGh5cGhlbnM6IGF1dG87DQp9DQoNCi8qIEFuY2hvIGRlIGxlY3R1cmEgY8OzbW9kbzogZW50cmUgNjUgeSA3NSBjYXJhY3RlcmVzIHBvciBsw61uZWEgKi8NCi5tYWluLWNvbnRhaW5lciB7DQogIG1heC13aWR0aDogMTEwMHB4Ow0KfQ0KDQovKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KICAgMi4gSkVSQVJRVcONQSBERSBUw41UVUxPUw0KICAgICAgU2VyaWYgcGFyYSBsb3MgZW5jYWJlemFkb3MsIHNhbnMgcGFyYSBlbCBjdWVycG86IGVsIGNvbnRyYXN0ZQ0KICAgICAgaGFjZSB2aXNpYmxlIGxhIGVzdHJ1Y3R1cmEgc2luIG5lY2VzaWRhZCBkZSBzdWJyYXlhZG9zIG5pIGNhamFzDQogICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAqLw0KaDEudGl0bGUgew0KICBmb250LWZhbWlseTogJ1NwZWN0cmFsJywgR2VvcmdpYSwgc2VyaWY7DQogIGZvbnQtc2l6ZTogMi4xZW07DQogIGZvbnQtd2VpZ2h0OiA2MDA7DQogIGNvbG9yOiB2YXIoLS1waXphcnJhKTsNCiAgbGluZS1oZWlnaHQ6IDEuMjU7DQogIG1hcmdpbi1ib3R0b206IDAuMmVtOw0KICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMS5zdWJ0aXRsZSB7DQogIGZvbnQtZmFtaWx5OiAnU291cmNlIFNhbnMgMycsIHNhbnMtc2VyaWY7DQogIGZvbnQtc2l6ZTogMS4wNWVtOw0KICBmb250LXdlaWdodDogNDAwOw0KICBjb2xvcjogdmFyKC0tdGVudWUpOw0KICBib3JkZXItYm90dG9tOiAycHggc29saWQgdmFyKC0tcGl6YXJyYSk7DQogIHBhZGRpbmctYm90dG9tOiAwLjhlbTsNCiAgbWFyZ2luLWJvdHRvbTogMC42ZW07DQogIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0LmF1dGhvciwgaDQuZGF0ZSB7DQogIGZvbnQtc2l6ZTogMC45MmVtOw0KICBmb250LXdlaWdodDogNDAwOw0KICBjb2xvcjogdmFyKC0tdGVudWUpOw0KICBtYXJnaW46IDAuMTVlbSAwOw0KICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMSwgaDIsIGgzLCBoNCB7DQogIGZvbnQtZmFtaWx5OiAnU3BlY3RyYWwnLCBHZW9yZ2lhLCBzZXJpZjsNCiAgY29sb3I6IHZhcigtLXBpemFycmEpOw0KICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICBmb250LXdlaWdodDogNjAwOw0KfQ0KDQpoMSB7IGZvbnQtc2l6ZTogMS42NWVtOyBtYXJnaW4tdG9wOiAxLjhlbTsgcGFkZGluZy1ib3R0b206IDAuMjVlbTsNCiAgICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkIHZhcigtLWJvcmRlKTsgfQ0KaDIgeyBmb250LXNpemU6IDEuMzJlbTsgbWFyZ2luLXRvcDogMS42ZW07IH0NCmgzIHsgZm9udC1zaXplOiAxLjEyZW07IG1hcmdpbi10b3A6IDEuM2VtOyBmb250LXdlaWdodDogNjAwOyB9DQpoNCB7IGZvbnQtc2l6ZTogMS4wMGVtOyBtYXJnaW4tdG9wOiAxLjFlbTsgZm9udC1zdHlsZTogaXRhbGljOyBmb250LXdlaWdodDogNDAwOyB9DQoNCi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQogICAzLiBUQUJMQSBERSBDT05URU5JRE8gRkxPVEFOVEUNCiAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICovDQojVE9DIHsgZm9udC1zaXplOiAwLjg2ZW07IH0NCg0KLnRvY2lmeSAudG9jaWZ5LWl0ZW0gPiBhIHsgY29sb3I6IHZhcigtLXRlbnVlKTsgfQ0KDQoudG9jaWZ5IC5saXN0LWdyb3VwLWl0ZW0uYWN0aXZlLA0KLnRvY2lmeSAubGlzdC1ncm91cC1pdGVtLmFjdGl2ZTpob3ZlciwNCi50b2NpZnkgLmxpc3QtZ3JvdXAtaXRlbS5hY3RpdmU6Zm9jdXMgew0KICBiYWNrZ3JvdW5kLWNvbG9yOiB2YXIoLS1waXphcnJhKTsNCiAgYm9yZGVyLWNvbG9yOiB2YXIoLS1waXphcnJhKTsNCiAgY29sb3I6ICNmZmZmZmY7DQp9DQoNCi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQogICA0LiBUQUJMQVMg4oCUIGVzdGlsbyBhY2Fkw6ltaWNvIHRpcG8gImJvb2t0YWJzIjoNCiAgICAgIHNvbG8gdHJlcyByZWdsYXMgaG9yaXpvbnRhbGVzLCBzaW4gY3VhZHLDrWN1bGEgY29tcGxldGENCiAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICovDQoubWFpbi1jb250YWluZXIgdGFibGU6bm90KC5kYXRhVGFibGUpIHsNCiAgd2lkdGg6IGF1dG87DQogIG1pbi13aWR0aDogNTUlOw0KICBtYXJnaW46IDEuNGVtIGF1dG87DQogIGJvcmRlci1jb2xsYXBzZTogY29sbGFwc2U7DQogIGZvbnQtc2l6ZTogMC44OGVtOw0KICBmb250LXZhcmlhbnQtbnVtZXJpYzogdGFidWxhci1udW1zOw0KfQ0KDQoubWFpbi1jb250YWluZXIgdGFibGU6bm90KC5kYXRhVGFibGUpIHRoZWFkIHRoIHsNCiAgYm9yZGVyLXRvcDogMnB4IHNvbGlkIHZhcigtLXBpemFycmEpOw0KICBib3JkZXItYm90dG9tOiAxcHggc29saWQgdmFyKC0tcGl6YXJyYSk7DQogIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50Ow0KICBmb250LXdlaWdodDogNjAwOw0KICBjb2xvcjogdmFyKC0tcGl6YXJyYSk7DQogIHBhZGRpbmc6IDhweCAxMnB4Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCi5tYWluLWNvbnRhaW5lciB0YWJsZTpub3QoLmRhdGFUYWJsZSkgdGJvZHkgdGQgew0KICBib3JkZXI6IG5vbmU7DQogIHBhZGRpbmc6IDZweCAxMnB4Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQoNCi8qIGxhIHByaW1lcmEgY29sdW1uYSBzdWVsZSBzZXIgZWwgbm9tYnJlIGRlIGxhIHZhcmlhYmxlOiB2YSBhIGxhIGl6cXVpZXJkYSAqLw0KLm1haW4tY29udGFpbmVyIHRhYmxlOm5vdCguZGF0YVRhYmxlKSB0Ym9keSB0ZDpmaXJzdC1jaGlsZCB7IHRleHQtYWxpZ246IGxlZnQ7IH0NCg0KLm1haW4tY29udGFpbmVyIHRhYmxlOm5vdCguZGF0YVRhYmxlKSB0Ym9keSB0cjpudGgtY2hpbGQoZXZlbikgeyBiYWNrZ3JvdW5kOiB2YXIoLS1ncmlzKTsgfQ0KLm1haW4tY29udGFpbmVyIHRhYmxlOm5vdCguZGF0YVRhYmxlKSB0Ym9keSB0cjpsYXN0LWNoaWxkIHRkICAgeyBib3JkZXItYm90dG9tOiAycHggc29saWQgdmFyKC0tcGl6YXJyYSk7IH0NCg0KY2FwdGlvbiwgcC5jYXB0aW9uIHsNCiAgY2FwdGlvbi1zaWRlOiB0b3A7DQogIHRleHQtYWxpZ246IGxlZnQ7DQogIGZvbnQtc2l6ZTogMC44NmVtOw0KICBmb250LXN0eWxlOiBpdGFsaWM7DQogIGNvbG9yOiB2YXIoLS10ZW51ZSk7DQogIG1hcmdpbi1ib3R0b206IDZweDsNCn0NCg0KLyogbGFzIHRhYmxhcyBwYWdpbmFkYXMgKGRmX3ByaW50OiBwYWdlZCkgY29uc2VydmFuIHN1IHByb3BpbyBkaXNlw7FvICovDQoucGFnZWR0YWJsZSB0YWJsZSwgLnBhZ2VkdGFibGUgdGgsIC5wYWdlZHRhYmxlIHRkIHsgYm9yZGVyOiBub25lICFpbXBvcnRhbnQ7IH0NCg0KLyogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiAgIDUuIEPDk0RJR08gWSBTQUxJREFTDQogICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAqLw0KcHJlLCBjb2RlIHsNCiAgZm9udC1mYW1pbHk6ICdKZXRCcmFpbnMgTW9ubycsICdDb25zb2xhcycsIG1vbm9zcGFjZTsNCiAgZm9udC1zaXplOiAwLjgyZW07DQp9DQoNCnByZSB7DQogIGJhY2tncm91bmQtY29sb3I6IHZhcigtLWdyaXMpOw0KICBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS1ib3JkZSk7DQogIGJvcmRlci1yYWRpdXM6IDVweDsNCiAgcGFkZGluZzogMTJweCAxNHB4Ow0KICBtYXgtaGVpZ2h0OiA0NjBweDsgICAgICAvKiBzYWxpZGFzIGxhcmdhcyBxdWVkYW4gY29uIHNjcm9sbCBwcm9waW8gKi8NCiAgb3ZlcmZsb3cteTogYXV0bzsNCiAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KLyogZnJhbmphIGxhdGVyYWwgcXVlIGRpc3Rpbmd1ZSBlbCBjw7NkaWdvIGZ1ZW50ZSBkZSBzdSBzYWxpZGEgKi8NCnByZS5yLCBkaXYuc291cmNlQ29kZSBwcmUgeyBib3JkZXItbGVmdDogM3B4IHNvbGlkIHZhcigtLWFjZW50byk7IH0NCg0KY29kZTpub3QocHJlIGNvZGUpIHsNCiAgYmFja2dyb3VuZC1jb2xvcjogdmFyKC0tZ3Jpcyk7DQogIGNvbG9yOiAjYTAzMDMwOw0KICBwYWRkaW5nOiAxcHggNXB4Ow0KICBib3JkZXItcmFkaXVzOiAzcHg7DQp9DQoNCi8qIGJvdG9uZXMgZGUgY29kZV9mb2xkaW5nLCBtw6FzIGRpc2NyZXRvcyAqLw0KLmJ0bi1kZWZhdWx0IHsNCiAgZm9udC1zaXplOiAwLjc4ZW07DQogIGJvcmRlci1jb2xvcjogdmFyKC0tYm9yZGUpOw0KICBjb2xvcjogdmFyKC0tdGVudWUpOw0KfQ0KDQovKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KICAgNi4gQ0lUQVMgWSBDQUpBUyBERSBBUE9ZTw0KICAgICAgRWwgYmxvY2txdW90ZSBxdWVkYSByZXNlcnZhZG8gcGFyYSBsb3MgcMOhcnJhZm9zIGRlIGludGVycHJldGFjacOzbg0KICAgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gKi8NCmJsb2NrcXVvdGUgew0KICBib3JkZXItbGVmdDogM3B4IHNvbGlkIHZhcigtLWFjZW50byk7DQogIGJhY2tncm91bmQtY29sb3I6ICNmMmY4Zjc7DQogIHBhZGRpbmc6IDE0cHggMThweDsNCiAgbWFyZ2luOiAxLjRlbSAwOw0KICBmb250LXNpemU6IDAuOTdlbTsNCiAgY29sb3I6IHZhcigtLXRpbnRhKTsNCn0NCmJsb2NrcXVvdGUgcDpsYXN0LWNoaWxkIHsgbWFyZ2luLWJvdHRvbTogMDsgfQ0KDQovKiBVc28gZW4gZWwgLlJtZDogIDo6OiB7Lm5vdGF9ICB0ZXh0byAgOjo6ICAgKi8NCi5ub3RhLCAuYWxlcnRhIHsNCiAgcGFkZGluZzogMTJweCAxNnB4Ow0KICBtYXJnaW46IDEuM2VtIDA7DQogIGJvcmRlci1yYWRpdXM6IDAgNXB4IDVweCAwOw0KICBmb250LXNpemU6IDAuOTVlbTsNCn0NCi5ub3RhICAgeyBiYWNrZ3JvdW5kLWNvbG9yOiAjZWVmM2Y3OyBib3JkZXItbGVmdDogM3B4IHNvbGlkIHZhcigtLXBpemFycmEpOyB9DQouYWxlcnRhIHsgYmFja2dyb3VuZC1jb2xvcjogI2ZkZjNlNzsgYm9yZGVyLWxlZnQ6IDNweCBzb2xpZCAjYjU3NTFhOyB9DQoNCi8qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQogICA3LiBGSUdVUkFTIFkgRU5MQUNFUw0KICAgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gKi8NCi5maWd1cmUgeyB0ZXh0LWFsaWduOiBjZW50ZXI7IG1hcmdpbjogMS42ZW0gMDsgfQ0KaW1nIHsgbWF4LXdpZHRoOiAxMDAlOyBoZWlnaHQ6IGF1dG87IH0NCg0KYSAgICAgICAgeyBjb2xvcjogdmFyKC0tYWNlbnRvKTsgdGV4dC1kZWNvcmF0aW9uOiBub25lOyB9DQphOmhvdmVyICB7IGNvbG9yOiB2YXIoLS1waXphcnJhKTsgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7IH0NCg0KaHIgeyBib3JkZXI6IDA7IGJvcmRlci10b3A6IDFweCBzb2xpZCB2YXIoLS1ib3JkZSk7IG1hcmdpbjogMi4yZW0gMDsgfQ0KDQovKiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KICAgOC4gSU1QUkVTScOTTiAvIEVYUE9SVEFDScOTTiBBIFBERiBERVNERSBFTCBOQVZFR0FET1INCiAgID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09ICovDQpAbWVkaWEgcHJpbnQgew0KICBib2R5IHsgZm9udC1zaXplOiAxMXB0OyB9DQogICNUT0MsIC5idG4geyBkaXNwbGF5OiBub25lICFpbXBvcnRhbnQ7IH0NCiAgcHJlIHsgbWF4LWhlaWdodDogbm9uZTsgb3ZlcmZsb3c6IHZpc2libGU7IH0NCn0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgZWNobyAgICAgICA9IFRSVUUsDQogIHdhcm5pbmcgICAgPSBGQUxTRSwNCiAgbWVzc2FnZSAgICA9IEZBTFNFLA0KICBjb21tZW50ICAgID0gIiM+IiwNCiAgZmlnLmFsaWduICA9ICJjZW50ZXIiLA0KICBmaWcud2lkdGggID0gOCwNCiAgZmlnLmhlaWdodCA9IDQuOCwNCiAgb3V0LndpZHRoICA9ICI4OCUiLA0KICBkcGkgICAgICAgID0gMTEwLA0KICBmaWcucmV0aW5hID0gMg0KKQ0KDQpvcHRpb25zKA0KICBzY2lwZW4gPSA5OTksICAgICAgICAgICAgICAgICAgIyBwcmVjaW9zIGVuIHBlc29zLCBubyBlbiBub3RhY2nDs24gY2llbnTDrWZpY2ENCiAgZGlnaXRzID0gNCwNCiAga25pdHIua2FibGUuTkEgPSAi4oCUIiwgICAgICAgICAgIyBsb3MgYXVzZW50ZXMgc2UgdmVuIGNvbW8gcmF5YSwgbm8gY29tbyAiTkEiDQogIGtuaXRyLnRhYmxlLmZvcm1hdCA9ICJodG1sIiAgICAjIG5lY2VzYXJpbyBwYXJhIHF1ZSBjZWxsX3NwZWMgZ2VuZXJlIEhUTUwNCikNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHN0cmluZ2Rpc3QpDQpsaWJyYXJ5KHN0cmluZ2kpDQpsaWJyYXJ5KG5hbmlhcikNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBFc3RpbG8gw7puaWNvIHBhcmEgdG9kYXMgbGFzIHRhYmxhcyBkZWwgaW5mb3JtZS4NCiMgU2kgbcOhcyBhZGVsYW50ZSBxdWllcm8gY2FtYmlhciBjb2xvcmVzIG8gdGFtYcOxbywgbG8gaGFnbyBhcXXDrQ0KIyB5IHNlIGFjdHVhbGl6YSBlbCBkb2N1bWVudG8gY29tcGxldG8uDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KdGFibGFfZXN0aWxvIDwtIGZ1bmN0aW9uKGRmLCB0aXR1bG8gPSBOVUxMLCBub3RhID0gTlVMTCwgZ3J1cG9zID0gTlVMTCkgew0KDQogIHQgPC0ga2JsKGRmLA0KICAgICAgICAgICBjYXB0aW9uICAgICA9IHRpdHVsbywNCiAgICAgICAgICAgYWxpZ24gICAgICAgPSBjKCJsIiwgcmVwKCJjIiwgbmNvbChkZikgLSAxKSksDQogICAgICAgICAgIGVzY2FwZSAgICAgID0gRkFMU0UsDQogICAgICAgICAgIGZvcm1hdC5hcmdzID0gbGlzdChiaWcubWFyayA9ICIuIikpIHw+DQogICAga2FibGVfc3R5bGluZygNCiAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwNCiAgICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwNCiAgICAgIHBvc2l0aW9uICAgPSAiY2VudGVyIiwNCiAgICAgIGZvbnRfc2l6ZSAgPSAxMw0KICAgICkgfD4NCiAgICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgY29sb3IgPSAiIzIxMzg0YSIpIHw+DQogICAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUsIGNvbG9yID0gIiMyMTM4NGEiKQ0KDQogIGlmICghaXMubnVsbChncnVwb3MpKSB7DQogICAgdCA8LSBhZGRfaGVhZGVyX2Fib3ZlKHQsIGdydXBvcywgYmFja2dyb3VuZCA9ICIjZWVmMmY1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiIzIxMzg0YSIsIGJvbGQgPSBUUlVFKQ0KICB9DQogIGlmICghaXMubnVsbChub3RhKSkgew0KICAgIHQgPC0gZm9vdG5vdGUodCwgZ2VuZXJhbCA9IG5vdGEsIGdlbmVyYWxfdGl0bGUgPSAiTm90YS4iLA0KICAgICAgICAgICAgICAgICAgZm9vdG5vdGVfYXNfY2h1bmsgPSBUUlVFLCB0aXRsZV9mb3JtYXQgPSAiaXRhbGljIikNCiAgfQ0KICB0DQp9DQoNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIEJhcnJhIGRlIGNvbXBsZXRpdHVkLiBMYSBiYXJyYSBzb2xvIHNlIGxsZW5hIHBvciBjb21wbGV0bw0KIyBjdWFuZG8gbGEgdmFyaWFibGUgbm8gdGllbmUgbmluZ8O6biBhdXNlbnRlLg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmJhcnJhIDwtIGZ1bmN0aW9uKHBjdF9jb21wbGV0bywgbiA9IDEwKSB7DQogIGxsZW5vcyA8LSBpZmVsc2UocGN0X2NvbXBsZXRvID09IDEwMCwgbiwNCiAgICAgICAgICAgICAgICAgICBwbWluKGZsb29yKHBjdF9jb21wbGV0byAvIDEwMCAqIG4pLCBuIC0gMSkpDQogIHBhc3RlMChzdHJyZXAoIuKWiCIsIGxsZW5vcyksIHN0cnJlcCgi4paRIiwgbiAtIGxsZW5vcykpDQp9DQpgYGANCg0KDQojIFByb2JsZW1hDQoNClVuYSBlbXByZXNhIGlubW9iaWxpYXJpYSBsw61kZXIgZW4gdW5hIGdyYW4gY2l1ZGFkIGVzdMOhIGJ1c2NhbmRvIGNvbXByZW5kZXIgZW4gcHJvZnVuZGlkYWQgZWwgbWVyY2FkbyBkZSB2aXZpZW5kYXMgdXJiYW5hcyBwYXJhIHRvbWFyIGRlY2lzaW9uZXMgZXN0cmF0w6lnaWNhcyBtw6FzIGluZm9ybWFkYXMuIExhIGVtcHJlc2EgcG9zZWUgdW5hIGJhc2UgZGUgZGF0b3MgZXh0ZW5zYSBxdWUgY29udGllbmUgaW5mb3JtYWNpw7NuIGRldGFsbGFkYSBzb2JyZSBkaXZlcnNhcyBwcm9waWVkYWRlcyByZXNpZGVuY2lhbGVzIGRpc3BvbmlibGVzIGVuIGVsIG1lcmNhZG8uIFNlIHJlcXVpZXJlIHJlYWxpemFyIHVuIGFuw6FsaXNpcyBob2zDrXN0aWNvIGRlIGVzdG9zIGRhdG9zIHBhcmEgaWRlbnRpZmljYXIgcGF0cm9uZXMsIHJlbGFjaW9uZXMgeSBzZWdtZW50YWNpb25lcyByZWxldmFudGVzIHF1ZSBwZXJtaXRhbiBtZWpvcmFyIGxhIHRvbWEgZGUgZGVjaXNpb25lcyBlbiBjdWFudG8gYSBsYSBjb21wcmEsIHZlbnRhIHkgdmFsb3JhY2nDs24gZGUgcHJvcGllZGFkZXMuDQoNCiMgUmV0b3MNCg0KRWwgcmV0byBwcmluY2lwYWwgY29uc2lzdGVuIGVuIHJlYWxpemFyIHVuIGFuw6FsaXNpcyBpbnRlZ3JhbCB5IG11bHRpZGltZW5zaW9uYWwgZGUgbGEgYmFzZSBkZSBkYXRvcyBwYXJhIG9idGVuZXIgdW5hIGNvbXByZW5zacOzbiBkZWwgbWVyY2FkbyBpbm1vYmlsaWFyaW8gdXJiYW5vLiBTZSByZXF1aWVyZSBhcGxpY2FyIGRpdmVyc2FzIHTDqWNuaWNhcyBkZSBhbsOhbGlzaXMgZGUgZGF0b3MsIGluY2x1eWVuZG86DQoNCjEuIEFuw6FsaXNpcyBkZSBDb21wb25lbnRlcyBQcmluY2lwYWxlczogUmVkdWNpciBsYSBkaW1lbnNpb25hbGlkYWQgZGVsIGNvbmp1bnRvIGRlIGRhdG9zIHkgdmlzdWFsaXphciBsYSBlc3RydWN0dXJhIGRlIGxhcyB2YXJpYWJsZXMgZW4gY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgcGFyYSBpZGVudGlmaWNhciBjYXJhY3RlcsOtc3RpY2FzIGNsYXZlIHF1ZSBpbmZsdXllbiBlbiBsYSB2YXJpYWNpw7NuIGRlIHByZWNpb3MgeSBvZmVydGEgZGVsIG1lcmNhZG8uDQoNCjIuIEFuw6FsaXNpcyBkZSBDb25nbG9tZXJhZG9zOiBBZ3J1cGFyIGxhcyBwcm9waWVkYWRlcyByZXNpZGVuY2lhbGVzIGVuIHNlZ21lbnRvcyBob21vZ8OpbmVvcyBjb24gY2FyYWN0ZXLDrXN0aWNhcyBzaW1pbGFyZXMgcGFyYSBlbnRlbmRlciBsYXMgZGluw6FtaWNhcyBkZSBsYXMgb2ZlcnRhcyBlc3BlY8OtZmljYXMgZW4gZGlmZXJlbnRlcyBwYXJ0ZXMgZGUgbGEgY2l1ZGFkIHkgZW4gZGlmZXJlbnRlcyBlc3RyYXRvcyBzb2Npb2Vjb27Ds21pY29zLg0KDQozLiBBbsOhbGlzaXMgZGUgQ29ycmVzcG9uZGVuY2lhIDogRXhhbWluYXIgbGEgcmVsYWNpw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMgY2F0ZWfDs3JpY2FzICh0aXBvIGRlIHZpdmllbmRhLCB6b25hIHkgYmFycmlvKSwgcGFyYSBpZGVudGlmaWNhciBwYXRyb25lcyBkZSBjb21wb3J0YW1pZW50byBkZSBsYSBvZmVydGEgZW4gbWVyY2FkbyBpbm1vYmlsaWFyaW8uDQoNCjQuIFZpc3VhbGl6YWNpw7NuIGRlIHJlc3VsdGFkb3M6IFByZXNlbnRhciBncsOhZmljb3MsIG1hcGFzIHkgb3Ryb3MgcmVjdXJzb3MgdmlzdWFsZXMgcGFyYSBjb211bmljYXIgbG9zIGhhbGxhemdvcyBkZSBtYW5lcmEgY2xhcmEgeSBlZmVjdGl2YSBhIGxhIGRpcmVjY2nDs24gZGUgbGEgZW1wcmVzYS4NCg0KRWwgaW5mb3JtZSBmaW5hbCBkZWJlIGluY2x1aXIgYW7DoWxpc2lzIGRldGFsbGFkb3MgZGUgbG9zIHJlc3VsdGFkb3Mgb2J0ZW5pZG9zLCBsYXMgY29uY2x1c2lvbmVzIGNsYXZlIHkgbGFzIHJlY29tZW5kYWNpb25lcyBlc3BlY8OtZmljYXMgcGFyYSBndWlhciBsYXMgZGVjaXNpb25lcyBlc3RyYXTDqWdpY2FzIGRlIGxhIGVtcHJlc2EgaW5tb2JpbGlhcmlhLiBTZSBlc3BlcmEgcXVlIGVzdGUgYW7DoWxpc2lzIGRlIGRhdG9zIHByb3BvcmNpb25lIHZlbnRhamFzIGNvbXBldGl0aXZhcyBlbiBlbCBtZXJjYWRvLCBvcHRpbWl6YW5kbyBsYSBpbnZlcnNpw7NuIHkgbWF4aW1pemFuZG8gbG9zIGJlbmVmaWNpb3MgZW4gdW4gZW50b3JubyBhbHRhbWVudGUgY29tcGV0aXRpdm8geSBlbiBjb25zdGFudGUgY2FtYmlvLg0KDQojIEFuw6FsaXNpcyBFeHBsb3JhdG9yaW8gZGUgRGF0b3MNCiMjIENhcmdhIGRlbCBEYXRhU2V0DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiY2VudHJvbWFnaXMvcGFxdWV0ZU1PREVMT1MiLCBmb3JjZSA9IFRSVUUpDQpsaWJyYXJ5KHBhcXVldGVNT0RFTE9TKQ0KZGF0YSgidml2ZW5kYSIpDQpzdHIodml2aWVuZGEpDQpgYGANCg0KIyMgRGVzY3JpcGNpw7NuIGRlbCBEYXRhU2V0DQoNCmBgYHtyfQ0KZGlhZ25vc3RpY28gPC0gdGliYmxlKA0KICBWYXJpYWJsZSAgPSBuYW1lcyh2aXZpZW5kYSksDQogIFRpcG8gICAgICA9IG1hcF9jaHIodml2aWVuZGEsIH4gY2xhc3MoLngpWzFdKSwNCiAgUmVnaXN0cm9zID0gbWFwX2ludCh2aXZpZW5kYSwgfiBzdW0oIWlzLm5hKC54KSkpLA0KICBGYWx0YW50ZXMgPSBtYXBfaW50KHZpdmllbmRhLCB+IHN1bShpcy5uYSgueCkpKSwNCiAgcGN0X2ZhbHQgID0gbWFwX2RibCh2aXZpZW5kYSwgfiAxMDAgKiBtZWFuKGlzLm5hKC54KSkpLA0KICBVbmljb3MgICAgPSBtYXBfaW50KHZpdmllbmRhLCB+IG5fZGlzdGluY3QoLnhbIWlzLm5hKC54KV0pKSwNCiAgRWplbXBsbyAgID0gbWFwX2Nocih2aXZpZW5kYSwgfiBhcy5jaGFyYWN0ZXIoLnhbIWlzLm5hKC54KV1bMV0pKQ0KKQ0KDQpkaWFnbm9zdGljbyB8Pg0KICBtdXRhdGUoDQogICAgQ29tcGxldGl0dWQgPSBjZWxsX3NwZWMoYmFycmEoMTAwIC0gcGN0X2ZhbHQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vbm9zcGFjZSA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjYXNlX3doZW4ocGN0X2ZhbHQgPT0gMCB+ICIjMGY3NjZlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwY3RfZmFsdCA8IDUgIH4gIiM1YzhhODYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBjdF9mYWx0IDwgMjAgfiAiI2I1NzUxYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSAgICAgICAgICB+ICIjYTMzYTNhIikpLA0KICAgIGAlYCA9IGNlbGxfc3BlYyhzcHJpbnRmKCIlLjFmIiwgcGN0X2ZhbHQpLA0KICAgICAgICAgICAgICAgICAgICBib2xkICA9IHBjdF9mYWx0ID49IDUsDQogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY2FzZV93aGVuKHBjdF9mYWx0ID09IDAgfiAiIzBmNzY2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBjdF9mYWx0IDwgNSAgfiAiIzFjMmIzNiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBjdF9mYWx0IDwgMjAgfiAiI2I1NzUxYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgICAgICAgICAgfiAiI2EzM2EzYSIpKSwNCiAgICAuYWZ0ZXIgPSBGYWx0YW50ZXMNCiAgKSB8Pg0KICBzZWxlY3QoVmFyaWFibGUsIFRpcG8sIFJlZ2lzdHJvcywgRmFsdGFudGVzLCBgJWAsIENvbXBsZXRpdHVkLA0KICAgICAgICAgYFZhbG9yZXMgw7puaWNvc2AgPSBVbmljb3MsIEVqZW1wbG8pIHw+DQogIHRhYmxhX2VzdGlsbygNCiAgICB0aXR1bG8gPSAiVGFibGEgMS4gRGlhZ27Ds3N0aWNvIGVzdHJ1Y3R1cmFsIGRlIGxhcyB2YXJpYWJsZXMgZGVsIGNvbmp1bnRvIHZpdmllbmRhIiwNCiAgICBncnVwb3MgPSBjKCIgIiA9IDEsICJOYXR1cmFsZXphIiA9IDEsDQogICAgICAgICAgICAgICAiQ29tcGxldGl0dWQgZGUgbGEgaW5mb3JtYWNpw7NuIiA9IDQsICJDb250ZW5pZG8iID0gMiksDQogICAgbm90YSAgID0gIkxvcyB2YWxvcmVzIMO6bmljb3Mgc2UgY2FsY3VsYW4gZXhjbHV5ZW5kbyBsb3MgZGF0b3MgYXVzZW50ZXMuIEVsIGNvbG9yIGluZGljYSBsYSBzZXZlcmlkYWQgZGUgbGEgYXVzZW5jaWE6IHNpbiBmYWx0YW50ZXMgKHZlcmRlKSwgbWVub3MgZGVsIDUlIChuZXV0cm8pLCBlbnRyZSA1JSB5IDIwJSAow6FtYmFyKSB5IG3DoXMgZGVsIDIwJSAocm9qbykuIg0KICApDQpgYGANCg0KTGEgYmFzZSBkZSBkYXRvcyBlc3RhIGNvbXB1ZXN0YSBwb3IgOC4zMjIgcmVnaXN0cm9zIHkgMTMgdmFyaWFibGVzIHF1ZSBjb21iaW5hbiBsYXMgY2FyYWN0ZXLDrXN0aWNhcyBmw61zaWNhcyBkZSBsb3MgaW5tdWVibGVzICjDoXJlYSBjb25zdHJ1aWRhLCBoYWJpdGFjaW9uZXMsIGJhw7FvcywgcGlzbyB5IHBhcnF1ZWFkZXJvcyksIHN1IHViaWNhY2nDs24gKGJhcnJpbywgem9uYSwgbG9uZ2l0dWQgeSBsYXRpdHVkKSwgZWwgZXN0cmF0byBzb2Npb2Vjb27Ds21pY28geSBlbCBwcmVjaW8gZGUgdmVudGEuIA0KDQpUb2RhcyBsYXMgY29sdW1uYXMgcmVnaXN0cmFuIDguMzE5IGRhdG9zIHbDoWxpZG9zIHkgYXBlbmFzIHRyZXMgZmlsYXMgYXBhcmVjZW4gdmFjw61hcyBkZSBtYW5lcmEgc2lzdGVtw6F0aWNhLCBsbyBxdWUgYXB1bnRhIGEgcmVnaXN0cm9zIGVuIGJsYW5jby4NCg0KRWwgZGF0YXNldCBwcmVzZW50YSBhdXNlbmNpYXMgbm90YWJsZXMgZW4gZG9zIHZhcmlhYmxlczogcGlzbywgY29uIDMxLDclIGRlIGRhdG9zIGZhbHRhbnRlcywgeSBwYXJxdWVhZGVyb3MsIGNvbiAxOSwzJS4NCg0KRW4gY3VhbnRvIGEgbG9zIHRpcG9zIGRlIGRhdG9zLCBoYXkgZG9zIHF1ZSBsbGFtYW4gbGEgYXRlbmNpw7NuOiBwaXNvIHF1ZWTDsyBjb21vIHRpcG8gY29tbyB0ZXh0bywgeSBlc3RyYXRvIGVudHLDsyBjb21vIG51bcOpcmljbyBjdWFuZG8gZW4gcmVhbGlkYWQgZXMgdW5hIGNhdGVnb3LDrWEgb3JkZW5hZGEuDQoNClBvciDDumx0aW1vLCBlbCBhbHRvIG7Dum1lcm8gZGUgdmFsb3JlcyB1bmljb3MgZW4gbGEgdmFyaWFibGUgYmFycmlvICg0MzYpIGRhIHBhcmEgc29zcGVjaGFyIHF1ZSBzZSBlc2NvbmRlbiBlcnJvcmVzIGRlIGRlIHRpcG8gdHlwbywgcXVlIHZhbGRyw6EgbGEgcGVuYSByZXZpc2FyLiANCg0KIyMgUmV2aXNpw7NuIGRlIHJlZ2lzdHJvcyB2YWNpb3MNCg0KTGEgZXhwbG9yYWNpw7NuIGFudGVyaW9yLCBldmlkZW5jaW8gdW5hIGNvbnN0YW50ZSBkZSB0cmVzIHJlZ2lzdHJvcyB2YWNpb3MgcGFyYSBjYWRhIHZhcmlhYmxlLiAgTGEgVGFibGEgMi4gbXVlc3RyYSBlc3RvcyByZWdpc3Ryb3MuIA0KDQpgYGB7cn0NCiMgwr9DdcOhbnRvcyBkYXRvcyB2w6FsaWRvcyB0aWVuZSBjYWRhIGZpbGE/DQp2aXZpZW5kYSB8Pg0KICBtdXRhdGUobl92YWxpZG9zID0gcm93U3VtcyghaXMubmEoYWNyb3NzKGV2ZXJ5dGhpbmcoKSkpKSkgfD4NCiAgZmlsdGVyKG5fdmFsaWRvcyA8PSAzKSB8Pg0KICB0YWJsYV9lc3RpbG8odGl0dWxvID0gIlRhYmxhIDIuIFJlZ2lzdHJvcyBjb24gaW5mb3JtYWNpw7NuIHByw6FjdGljYW1lbnRlIG51bGEiKQ0KYGBgDQpBbCBubyB0ZW5lciBpbmZvcm1hY2nDs24sIGVzdG9zIHRyZXMgcmVnaXN0cm9zIG5vIGFwb3J0YW4gbmFkYSBhbCBhbsOhbGlzaXMgeSBzaSBwdWVkZW4gZ2VuZXJhciBwcm9ibGVtYXMgYWwgbW9tZW50byBkZSBjb250aW51YXIuIFBvciBsbyB0YW50byxzZSBvcHRhIHBvciBlbGltaW5hcmxvcy4gDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDFfZmlsdHJvIDwtIHZpdmllbmRhIHw+DQogIGZpbHRlcihyb3dTdW1zKCFpcy5uYShhY3Jvc3MoZXZlcnl0aGluZygpKSkpID4gMykgfD4NCiAgc2VsZWN0KC1hbnlfb2YoYygibl92YWxpZG9zIiwgInJlZ2lzdHJvX2luY29tcGxldG8iKSkpDQoNCmNhdCgiT3JpZ2luYWxlczoiLCBucm93KHZpdmllbmRhKSwNCiAgICAifCBFbGltaW5hZG9zOiIsIG5yb3codml2aWVuZGEpIC0gbnJvdyh2aXZpZW5kYV8wMV9maWx0cm8pLA0KICAgICJ8IFJlc3RhbnRlczoiLCBucm93KHZpdmllbmRhXzAxX2ZpbHRybykpDQpgYGANCiMjIENvbnZlcnNpw7NuIGRlIHZhcmlhYmxlcw0KIyMjIFZhcmlhYmxlIFBpc28NCg0KTGEgdmFyaWFibGUgcGlzbyBmdWUgbGEgw7puaWNhIGN1eW8gdGlwbyBhc2lnbmFkbyBwb3IgZGVmZWN0byBubyBjb3JyZXNwb25kw61hIGNvbiBzdSBuYXR1cmFsZXphOiBmaWd1cmFiYSBjb21vIHRleHRvIGN1YW5kbyBlbiByZWFsaWRhZCBleHByZXNhIHVuYSBjYW50aWRhZC4gQ29tZW56YXJlbW9zIGV4cGxvcmFuZG8gbGEgdmFyaWFibGUgcGlzbyB5IHN1cyAxMiB2YWxvcmVzIMO6bmljb3MNCg0KYGBge3J9DQp2aXZpZW5kYV8wMV9maWx0cm8gfD4NCiAgY291bnQocGlzbywgc29ydCA9IFRSVUUsIG5hbWUgPSAiRnJlY3VlbmNpYSIpIHw+DQogIG11dGF0ZSgNCiAgICBQb3JjZW50YWplID0gc3ByaW50ZigiJS4xZiIsIDEwMCAqIEZyZWN1ZW5jaWEgLyBzdW0oRnJlY3VlbmNpYSkpLA0KICAgIGDCv0NvbnZlcnRpYmxlP2AgPSBjYXNlX3doZW4oDQogICAgICBpcy5uYShwaXNvKSB+IGNlbGxfc3BlYygiQXVzZW50ZSIsIGNvbG9yID0gIiM2YjdhODUiLCBpdGFsaWMgPSBUUlVFKSwNCiAgICAgICFpcy5uYShzdXBwcmVzc1dhcm5pbmdzKGFzLm51bWVyaWMocGlzbykpKSB+IGNlbGxfc3BlYygiU8OtIiwgY29sb3IgPSAiIzBmNzY2ZSIpLA0KICAgICAgVFJVRSB+IGNlbGxfc3BlYygiTm8iLCBjb2xvciA9ICIjYTMzYTNhIiwgYm9sZCA9IFRSVUUpDQogICAgKQ0KICApIHw+DQogIHJlbmFtZShQaXNvID0gcGlzbykgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAzLiBWYWxvcmVzIHJlZ2lzdHJhZG9zIGVuIGxhIHZhcmlhYmxlIHBpc28iLA0KICAgIG5vdGEgICA9ICJMYSDDumx0aW1hIGNvbHVtbmEgaW5kaWNhIHNpIGVsIHZhbG9yIGFkbWl0ZSBjb252ZXJzacOzbiBudW3DqXJpY2EgZGlyZWN0YS4iDQogICkNCmBgYA0KDQpMb3MgY2Vyb3MgYSBsYSBpenF1aWVyZGEgaGFjZW4gcXVlIGxhIHZhcmlhYmxlIHBpc28gc2VhIGlkZW50aWZpY2FkYSBjb21vIGNhcmFjdGVyLCBjdWFuZG8gZW4gcmVhbGlkYWQgZXMgbnVtw6lyaWNhIGRpc2NyZXRhLiBQb3IgbG8gdGFudG8gc2UgdmEgYSBjYW1iaWFyIHN1IHRpcG8gYSBlbnRlcm8gDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDJfdGlwb3MgPC0gdml2aWVuZGFfMDFfZmlsdHJvIHw+DQogIG11dGF0ZShwaXNvID0gc3VwcHJlc3NXYXJuaW5ncyhhcy5pbnRlZ2VyKHBpc28pKSkNCg0KdGliYmxlKA0KICBFdGFwYSAgICA9IGMoIkFudGVzIGRlIGNvbnZlcnRpciIsICJEZXNwdcOpcyBkZSBjb252ZXJ0aXIiLCAiRGlmZXJlbmNpYSIpLA0KICBBdXNlbnRlcyA9IGMoc3VtKGlzLm5hKHZpdmllbmRhXzAxX2ZpbHRybyRwaXNvKSksDQogICAgICAgICAgICAgICBzdW0oaXMubmEodml2aWVuZGFfMDJfdGlwb3MkcGlzbykpLA0KICAgICAgICAgICAgICAgc3VtKGlzLm5hKHZpdmllbmRhXzAyX3RpcG9zJHBpc28pKSAtIHN1bShpcy5uYSh2aXZpZW5kYV8wMV9maWx0cm8kcGlzbykpKQ0KKSB8Pg0KICB0YWJsYV9lc3RpbG8odGl0dWxvID0gIlRhYmxhIDQuIENvbnRyb2wgZGUgaW50ZWdyaWRhZCBlbiBsYSBjb252ZXJzacOzbiBkZSBwaXNvIikNCmBgYA0KDQojIyMgVmFyaWFibGVzIGlkLCB6b25hLCB0aXBvIHkgZXN0cmF0bw0KDQpgYGB7cn0NCnZpdmllbmRhXzAyX3RpcG9zIDwtIHZpdmllbmRhXzAxX2ZpbHRybyB8Pg0KICBtdXRhdGUoDQogICAgaWQgICAgICA9IGFzLmNoYXJhY3RlcihpZCksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGlkZW50aWZpY2Fkb3INCiAgICBwaXNvICAgID0gc3VwcHJlc3NXYXJuaW5ncyhhcy5pbnRlZ2VyKHBpc28pKSwgICAgICAgICAgICAgICMgZGlzY3JldGENCiAgICB6b25hICAgID0gZmFjdG9yKHpvbmEpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbm9taW5hbA0KICAgIHRpcG8gICAgPSBmYWN0b3IodGlwbyksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBub21pbmFsDQogICAgZXN0cmF0byA9IGZhY3Rvcihlc3RyYXRvLCBvcmRlcmVkID0gVFJVRSksICAgICAgICAgICAgICAgICAjIG9yZGluYWwNCiAgICBhY3Jvc3MoYyhoYWJpdGFjaW9uZXMsIGJhbmlvcywgcGFycXVlYWRlcm9zKSwgYXMuaW50ZWdlcikgICMgY29udGVvcw0KICApDQoNCmdsaW1wc2Uodml2aWVuZGFfMDJfdGlwb3MpDQpgYGANCmBgYHtyfQ0KdGliYmxlKA0KICBWYXJpYWJsZSA9IG5hbWVzKHZpdmllbmRhXzAyX3RpcG9zKSwNCiAgRmluYWwgICAgPSBtYXBfY2hyKHZpdmllbmRhXzAyX3RpcG9zLCB+IGNsYXNzKC54KVsxXSkNCikgfD4NCiAgbGVmdF9qb2luKGRpYWdub3N0aWNvIHw+IHNlbGVjdChWYXJpYWJsZSwgSW5pY2lhbCA9IFRpcG8pLCBieSA9ICJWYXJpYWJsZSIpIHw+DQogIG11dGF0ZSgNCiAgICBOYXR1cmFsZXphID0gYygiSWRlbnRpZmljYWRvciIsICJOb21pbmFsIiwgIkRpc2NyZXRhIiwgIk9yZGluYWwiLA0KICAgICAgICAgICAgICAgICAgICJDb250aW51YSIsICJDb250aW51YSIsICJEaXNjcmV0YSIsICJEaXNjcmV0YSIsDQogICAgICAgICAgICAgICAgICAgIkRpc2NyZXRhIiwgIk5vbWluYWwiLCAiTm9taW5hbCIsICJHZW9ncsOhZmljYSIsICJHZW9ncsOhZmljYSIpLA0KICAgIENhbWJpbyA9IGlmZWxzZShJbmljaWFsID09IEZpbmFsLA0KICAgICAgICAgICAgICAgICAgICBjZWxsX3NwZWMoIlNpbiBjYW1iaW8iLCBjb2xvciA9ICIjNmI3YTg1IiksDQogICAgICAgICAgICAgICAgICAgIGNlbGxfc3BlYygiUmVjbGFzaWZpY2FkYSIsIGNvbG9yID0gIiMwZjc2NmUiLCBib2xkID0gVFJVRSkpDQogICkgfD4NCiAgc2VsZWN0KFZhcmlhYmxlLCBOYXR1cmFsZXphLCBgVGlwbyBpbmljaWFsYCA9IEluaWNpYWwsDQogICAgICAgICBgVGlwbyBmaW5hbGAgPSBGaW5hbCwgQ2FtYmlvKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDUuIFJlY2xhc2lmaWNhY2nDs24gZGUgdmFyaWFibGVzIHNlZ8O6biBzdSBuYXR1cmFsZXphIiwNCiAgICBncnVwb3MgPSBjKCIgIiA9IDIsICJUaXBvIGRlIGRhdG8gZW4gUiIgPSAyLCAiICIgPSAxKSwNCiAgICBub3RhICAgPSAiTGEgdmFyaWFibGUgYmFycmlvIHNlIG1hbnRpZW5lIGNvbW8gdGV4dG8gZGUgbWFuZXJhIGludGVuY2lvbmFsOyBzdSBjb252ZXJzacOzbiBhIGZhY3RvciBzZSByZWFsaXphcsOhIHVuYSB2ZXogZGVwdXJhZG9zIGxvcyBlcnJvcmVzIGRlIGRpZ2l0YWNpw7NuLiINCiAgKQ0KYGBgDQoNCkRlZmluaWRvIGVsIHRpcG8gY29ycmVjdG8gZGUgcGlzbywgc2UgY29tcGxldMOzIGxhIHJlY2xhc2lmaWNhY2nDs24gZGVsIHJlc3RvIGRlIHZhcmlhYmxlcyBzZWfDum4gc3UgbmF0dXJhbGV6YSBjb25jZXB0dWFsLiBFbCBlc3RyYXRvIHNvY2lvZWNvbsOzbWljbywgcXVlIGhhYsOtYSBpbmdyZXNhZG8gY29tbyBudW3DqXJpY28sIHNlIGNvbnZpcnRpw7MgYSBmYWN0b3Igb3JkZW5hZG86IGNvbnNlcnZhIGFzw60gbGEgamVyYXJxdcOtYSBlbnRyZSBuaXZlbGVzIHBlcm8gaW1waWRlIGNhbGN1bGFyIHByb21lZGlvcywgb3BlcmFjacOzbiBxdWUgbm8gdGVuZHLDrWEgZGUgc2VudGlkbyBkYWRvIHF1ZSBsYSBkaXN0YW5jaWEgZW50cmUgZWwgZXN0cmF0byAzIHkgZWwgNCBubyBlcyBlcXVpdmFsZW50ZSBhIGxhIHF1ZSBzZXBhcmEgZWwgMSBkZWwgMi4gRWwgaWRlbnRpZmljYWRvciBzZSB0cmFuc2Zvcm3DsyBlbiB0ZXh0bywgZGUgbW9kbyBxdWUgcXVlZGUgYXV0b23DoXRpY2FtZW50ZSBleGNsdWlkbyBkZSBsYXMgbWF0cmljZXMgZGUgY29ycmVsYWNpw7NuIHkgbG9zIHJlc8O6bWVuZXMgZGVzY3JpcHRpdm9zLCBkb25kZSBzdSBpbmNsdXNpw7NuIHByb2R1Y2lyw61hIGNpZnJhcyBhcGFyZW50ZW1lbnRlIHbDoWxpZGFzIHBlcm8gdmFjw61hcyBkZSBzaWduaWZpY2Fkby4gDQoNCkxhcyB2YXJpYWJsZXMgZGUgY29udGVvIChoYWJpdGFjaW9uZXMsIGJhw7FvcyB5IHBhcnF1ZWFkZXJvcykgc2UgZGVjbGFyYXJvbiBjb21vIGVudGVyb3MsIHkgdGlwbyB5IHpvbmEgY29tbyBmYWN0b3JlcyBub21pbmFsZXMuIExhcyBjb29yZGVuYWRhcyBnZW9ncsOhZmljYXMgc2UgbWFudHV2aWVyb24gZW4gZm9ybWF0byBudW3DqXJpY28gcG9yIGNvcnJlc3BvbmRlciBhIG1lZGljaW9uZXMgY29udGludWFzLCBzaSBiaWVuIHNlIGV4Y2x1eWVuIGRlIGxvcyBhbsOhbGlzaXMgZGVzY3JpcHRpdm9zIHBvcnF1ZSBzdSBwcm9tZWRpbyBvIHN1IGRpc3BlcnNpw7NuIG5vIGFkbWl0ZW4gaW50ZXJwcmV0YWNpw7NuIHN1c3RhbnRpdmEuIA0KDQojIyBBbsOhbGlzaXMgZGUgVmFsb3JlcyBBdXNlbnRlcw0KDQpMYSBkaXN0aW5jacOzbiBxdWUgb3JnYW5pemEgdG9kbyBlbCBhbsOhbGlzaXMgc2UgYmFzYSBlbiB0cmVzIHBvc2liaWxpZGFkZXMuIFVuIGRhdG8gcHVlZGUgZmFsdGFyIGNvbXBsZXRhbWVudGUgYWwgYXphciAoTUNBUik6IG5hZGEgZXhwbGljYSBzdSBhdXNlbmNpYSwgZXMgcHVybyBhY2NpZGVudGUgZGUgY2FwdHVyYS4gUHVlZGUgZmFsdGFyIGFsIGF6YXIgY29uZGljaW9uYWRvIGEgbG8gb2JzZXJ2YWRvIChNQVIpOiBsYSBhdXNlbmNpYSBzZSBleHBsaWNhIHBvciBvdHJhcyB2YXJpYWJsZXMgcHJlc2VudGVzLiBPIHB1ZWRlIGZhbHRhciBubyBhbCBhemFyIChNTkFSKTogbGEgYXVzZW5jaWEgZGVwZW5kZSBkZWwgdmFsb3IgcXVlIGZhbHRhLiANCg0KRXN0YXR1cyBBdXNlbmNpYXMNCmBgYHtyfQ0KYXVzZW50ZXMgPC0gdGliYmxlKA0KICBWYXJpYWJsZSAgPSBuYW1lcyh2aXZpZW5kYV8wMl90aXBvcyksDQogIEZhbHRhbnRlcyA9IG1hcF9pbnQodml2aWVuZGFfMDJfdGlwb3MsIH4gc3VtKGlzLm5hKC54KSkpLA0KICBwY3QgICAgICAgPSBtYXBfZGJsKHZpdmllbmRhXzAyX3RpcG9zLCB+IDEwMCAqIG1lYW4oaXMubmEoLngpKSkNCikgfD4NCiAgZmlsdGVyKEZhbHRhbnRlcyA+IDApIHw+DQogIGFycmFuZ2UoZGVzYyhwY3QpKQ0KDQphdXNlbnRlcyB8Pg0KICBtdXRhdGUoDQogICAgYCVgID0gY2VsbF9zcGVjKHNwcmludGYoIiUuMWYiLCBwY3QpLA0KICAgICAgICAgICAgICAgICAgICBib2xkID0gcGN0ID49IDIwLA0KICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGlmZWxzZShwY3QgPj0gMjAsICIjYTMzYTNhIiwgIiNiNTc1MWEiKSksDQogICAgQ29tcGxldGl0dWQgPSBjZWxsX3NwZWMoYmFycmEoMTAwIC0gcGN0KSwgbW9ub3NwYWNlID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGlmZWxzZShwY3QgPj0gMjAsICIjYTMzYTNhIiwgIiNiNTc1MWEiKSkNCiAgKSB8Pg0KICBzZWxlY3QoVmFyaWFibGUsIEZhbHRhbnRlcywgYCVgLCBDb21wbGV0aXR1ZCkgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSA2LiBWYXJpYWJsZXMgY29uIGRhdG9zIGF1c2VudGVzIHRyYXMgbGEgZGVwdXJhY2nDs24gaW5pY2lhbCIsDQogICAgbm90YSAgID0gIlBvcmNlbnRhamVzIGNhbGN1bGFkb3Mgc29icmUgbG9zIDguMzE5IHJlZ2lzdHJvcyBxdWUgY29uZm9ybWFuIGxhIGJhc2UgZGVwdXJhZGEuIg0KICApDQpgYGANCkRlc3B1ZXMgZGUgZWxpbWluYXIgbGFzIGZpbGFzIHZhY2lhcywgcXVlZGFtb3Mgc29sbyBjb24gZG9zIHZhcmlhYmxlcyAocGlzbyB5IHBhcnF1ZWFkZXJvKSBjb24gcmVzZ2lzdHJvcyBhdXNlbnRlcy4gDQoNCkEgY29udGludWFjacOzbiBzZSB2YSBhIHZlcmlmaWNhciBzaSBsb3MgZGF0b3MgYXVzZW50ZXMgY29pbmNpZGVuIGVuIGFtYmFzIHZhcmlhYmxlcy4NCg0KYGBge3J9DQp2aXZpZW5kYV8wMl90aXBvcyB8Pg0KICBjb3VudChgUGlzb2AgPSBpZmVsc2UoaXMubmEocGlzbyksICJBdXNlbnRlIiwgIlByZXNlbnRlIiksDQogICAgICAgIGBQYXJxdWVhZGVyb3NgID0gaWZlbHNlKGlzLm5hKHBhcnF1ZWFkZXJvcyksICJBdXNlbnRlIiwgIlByZXNlbnRlIikpIHw+DQogIG11dGF0ZShgJWAgPSBzcHJpbnRmKCIlLjFmIiwgMTAwICogbiAvIHN1bShuKSkpIHw+DQogIHJlbmFtZShSZWdpc3Ryb3MgPSBuKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDcuIENvaW5jaWRlbmNpYSBlbnRyZSBsYXMgYXVzZW5jaWFzIGRlIHBpc28geSBwYXJxdWVhZGVyb3MiLA0KICAgIG5vdGEgICA9ICJTaSBsYXMgYXVzZW5jaWFzIGZ1ZXJhbiBpbmRlcGVuZGllbnRlcywgbGEgZnJlY3VlbmNpYSBkZSBsYSBjZWxkYSBkb25kZSBhbWJhcyBmYWx0YW4gc2UgYXByb3hpbWFyw61hIGFsIHByb2R1Y3RvIGRlIHN1cyBwcm9wb3JjaW9uZXMgbWFyZ2luYWxlcy4iDQogICkNCmBgYA0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KG5hbmlhcikNCmdnX21pc3NfdXBzZXQodml2aWVuZGFfMDJfdGlwb3MpDQpgYGANCkFudGVzIGRlIGRlY2lkaXIgY8OzbW8gdHJhdGFyIGxvcyBkYXRvcyBmYWx0YW50ZXMgc2UgcmV2aXPDsyBzaSBsYXMgYXVzZW5jaWFzIGRlIHBpc28geSBwYXJxdWVhZGVyb3Mgb2N1cnJlbiBlbiBsb3MgbWlzbW9zIHJlZ2lzdHJvcyBvIHNpIGNhZGEgdmFyaWFibGUgZmFsbGEgcG9yIHN1IGN1ZW50YS4gTGEgZGlzdGluY2nDs24gZXMgaW1wb3J0YW50ZSB5YSBxdWUgY3VhbmRvIGRvcyBjYW1wb3Mgc2UgdmFjw61hbiBqdW50b3MsIGxvIGhhYml0dWFsIGVzIHF1ZSBlbCBwcm9ibGVtYSBlc3TDqSBlbiBsYSBwdWJsaWNhY2nDs24gY29tcGxldGEgeSBubyBlbiBjYWRhIGRhdG8gcG9yIHNlcGFyYWRvLCB5IGVsIHRyYXRhbWllbnRvIHRlbmRyw61hIHF1ZSBhYm9yZGFybG9zIGVuIGNvbmp1bnRvLiBFbCBjcnVjZSBtdWVzdHJhIHF1ZSBhbWJvcyBjYW1wb3MgZmFsdGFuIHNpbXVsdMOhbmVhbWVudGUgZW4gNzI2IHJlZ2lzdHJvcywgZXF1aXZhbGVudGVzIGFsIDgsNyUgZGUgbGEgYmFzZS4gQmFqbyBpbmRlcGVuZGVuY2lhIHNlIGhhYnLDrWFuIGVzcGVyYWRvIGFscmVkZWRvciBkZWwgNiwxJSwgZGUgbW9kbyBxdWUgc8OtIGhheSB1bmEgYXNvY2lhY2nDs24gZW50cmUgbGFzIGRvcyBhdXNlbmNpYXMsIGF1bnF1ZSBkZSBtYWduaXR1ZCBtb2RlcmFkYTogbGEgcHJvYmFiaWxpZGFkIGRlIHF1ZSBmYWx0ZSBlbCBwYXJxdWVhZGVybyBwYXNhIGRlIDE1LDQlIGN1YW5kbyBlbCBwaXNvIGVzdMOhIHJlZ2lzdHJhZG8gYSAyNyw2JSBjdWFuZG8gbm8gbG8gZXN0w6EuIFNpbiBlbWJhcmdvLCBlbiBsYSBtYXlvcmlhIGRlIGxvcyBjYXNvcyBubyByZXNwb25kZSBhIGVzZSBwYXRyw7NuLiBIYXkgMS45MDkgcmVnaXN0cm9zIHF1ZSBpbmZvcm1hbiBwYXJxdWVhZGVybyBwZXJvIG9taXRlbiBlbCBwaXNvIHkgb3Ryb3MgODc2IHF1ZSBoYWNlbiBleGFjdGFtZW50ZSBsbyBjb250cmFyaW8sIGxvIHF1ZSBpbmRpY2EgcXVlIGNhZGEgdmFyaWFibGUgZmFsdGEgc29icmUgdG9kbyBwb3IgcmF6b25lcyBwcm9waWFzIHkgbm8gcG9yIHVuIGFiYW5kb25vIGdlbmVyYWxpemFkbyBkZWwgZm9ybXVsYXJpby4NCg0KU2UgY29udGludWEgYW5hbGl6YW5kbyBzaSBsYSBhdXNlbmNpYXMgZGVwZW5kZW4gZGUgb3RyYXMgdmFyaWFibGVzIHkgYXNpIGRldGVybWluYXIgTUNBUiBvIE1BUi4gDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDJfdGlwb3MgfD4NCiAgZ3JvdXBfYnkodGlwbywgZXN0cmF0bykgfD4NCiAgc3VtbWFyaXNlKA0KICAgIG4gPSBuKCksDQogICAgYFBpc28gYXVzZW50ZSAlYCA9IHNwcmludGYoIiUuMWYiLCAxMDAgKiBtZWFuKGlzLm5hKHBpc28pKSksDQogICAgYFBhcnEuIGF1c2VudGUgJWAgPSBzcHJpbnRmKCIlLjFmIiwgMTAwICogbWVhbihpcy5uYShwYXJxdWVhZGVyb3MpKSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApIHw+DQogIHJlbmFtZShUaXBvID0gdGlwbywgRXN0cmF0byA9IGVzdHJhdG8sIFJlZ2lzdHJvcyA9IG4pIHw+DQogIHRhYmxhX2VzdGlsbygNCiAgICB0aXR1bG8gPSAiVGFibGEgOC4gUHJvcG9yY2nDs24gZGUgYXVzZW5jaWFzIHNlZ8O6biB0aXBvIGRlIGlubXVlYmxlIHkgZXN0cmF0byIsDQogICAgZ3J1cG9zID0gYygiICIgPSAzLCAiUHJvcG9yY2nDs24gZGUgZGF0b3MgYXVzZW50ZXMiID0gMiksDQogICAgbm90YSAgID0gIlZhcmlhY2lvbmVzIHNpc3RlbcOhdGljYXMgZW50cmUgZ3J1cG9zIGluZGljYW4gcXVlIGxhIGF1c2VuY2lhIG5vIG9jdXJyZSBjb21wbGV0YW1lbnRlIGFsIGF6YXIuIg0KICApDQpgYGANCg0KIyMjIFBhcnF1ZWFkZXJvcw0KDQpFbiBwYXJxdWVhZGVyb3MsIGxhIHByb3BvcmNpw7NuIGRlIGRhdG9zIGZhbHRhbnRlcyBkZXNjaWVuZGUgZGVsIDUzLDclIGVuIGFwYXJ0YW1lbnRvcyBkZSBlc3RyYXRvIDMgYWwgNCw4JSBlbiBlc3RyYXRvIDYsIHkgcmVwaXRlIGVsIG1pc21vIGNvbXBvcnRhbWllbnRvIGVuIGNhc2FzLCBkb25kZSBwYXNhIGRlbCA1MiwzJSBhbCA3LDklLiBFc3RlIHBhdHLDs24gc2UgcHVlZGUgZXhwbGljYXIgZW4gcXVlIGVuIGxvcyBzZWdtZW50b3MgZGUgbWF5b3IgZXN0cmF0byBlbCBwYXJxdWVhZGVybyBzZSBhc3VtZSBjb21vIHBhcnRlIGRlbCBpbm11ZWJsZSB5IHNlIHJlcG9ydGEgZGUgbWFuZXJhIHJ1dGluYXJpYSwgbWllbnRyYXMgcXVlIGVuIGVzdHJhdG8gMyBidWVuYSBwYXJ0ZSBkZSBsYXMgdml2aWVuZGFzIG5vIGN1ZW50YSBjb24gdW5vIHkgZWwgY2FtcG8gc2UgZGVqYSBlbiBibGFuY28gZW4gbHVnYXIgZGUgaW5ncmVzYXIgdW4gY2Vyby4gVG9tYW5kbyBlc3RhIGFwcm94aW1hY2nDs24sIGVsIHZhbG9yIGF1c2VudGUgbm8gZXhwcmVzYSBkZXNjb25vY2ltaWVudG8gc2lubyBpbmV4aXN0ZW5jaWEgZGVsIGF0cmlidXRvLCB5IGxhIHbDrWEgYWRlY3VhZGEgc2Vyw61hIHJlY29kaWZpY2FybG8gY29tbyBjZXJvIGFudGVzIHF1ZSBpbXB1dGFyIHVuYSBjYW50aWRhZC4gIA0KDQpBbnRlcyBkZSByZWNvZGlmaWNhciwgc2UgY29tcHJ1ZWJhIHNpIGV4aXN0ZW4gZWwgdmFsb3IgY2VybyBwYXJhIHBhcnF1ZWFkZXJvcy4NCg0KYGBge3J9DQp2aXZpZW5kYV8wMl90aXBvcyB8Pg0KICBjb3VudChwYXJxdWVhZGVyb3MpIHw+DQogIG11dGF0ZShwY3QgPSBzcHJpbnRmKCIlLjFmIiwgMTAwICogbiAvIHN1bShuKSkpDQpgYGANCg0KU2UgY29uZmlybWEgcXVlIGVsIHZhbG9yIDAgbm8gZXhpc3RlIGVuIHRvZG8gZWwgZGF0YSBzZXQgeSBxdWUgbGEgdmFyaWFibGUgYXJyYW5jYSBlbiAxLiBQb3IgbG8gdGFudG8sIHNlIHRvbWEgbGEgYXByb3hpbWFjacOzbiBxdWUgbG9zIHZhbG9yZXMgYXVzZW50ZXMgY29ycmVzcG9uZGVuIGEgcXVlIHF1aWVuIG5vIHRlbsOtYSBwYXJxdWVhZGVybyBkZWrDsyBlbCBjYW1wbyB2YWPDrW8gZW4gbHVnYXIgZGUgZXNjcmliaXIgY2Vyby4NCg0KYGBge3J9DQp2aXZpZW5kYV8wM19uYSA8LSB2aXZpZW5kYV8wMl90aXBvcyB8Pg0KICBtdXRhdGUocGFycXVlYWRlcm9zID0gcmVwbGFjZV9uYShwYXJxdWVhZGVyb3MsIDBMKSkNCg0KIyBWZXJpZmljYWNpw7NuDQp2aXZpZW5kYV8wM19uYSB8Pg0KICBjb3VudChwYXJxdWVhZGVyb3MsIG5hbWUgPSAiUmVnaXN0cm9zIikgfD4NCiAgbXV0YXRlKGAlYCA9IHNwcmludGYoIiUuMWYiLCAxMDAgKiBSZWdpc3Ryb3MgLyBzdW0oUmVnaXN0cm9zKSkpIHw+DQogIHJlbmFtZShQYXJxdWVhZGVyb3MgPSBwYXJxdWVhZGVyb3MpIHw+DQogIHRhYmxhX2VzdGlsbygNCiAgICB0aXR1bG8gPSAiVGFibGEgOS4gRGlzdHJpYnVjacOzbiBkZSBwYXJxdWVhZGVyb3MgdHJhcyBsYSByZWNvZGlmaWNhY2nDs24iLA0KICAgIG5vdGEgICA9ICJMb3MgcmVnaXN0cm9zIHNpbiBkYXRvIHNlIHJlY29kaWZpY2Fyb24gY29tbyBjZXJvLCBiYWpvIGVsIHN1cHVlc3RvIGRlIHF1ZSBsYSBhdXNlbmNpYSBkZWwgY2FtcG8gaW5kaWNhIGluZXhpc3RlbmNpYSBkZWwgYXRyaWJ1dG8geSBubyBkZXNjb25vY2ltaWVudG8gZGVsIHZhbG9yLiINCiAgKQ0KYGBgDQoNClNvYnJlIGVzYSBiYXNlLCBsb3MgMS42MDIgcmVnaXN0cm9zIHNpbiBkYXRvIHNlIHJlY29kaWZpY2Fyb24gY29tbyBjZXJvLCBkZWNpc2nDs24gcXVlIGFsdGVyYSBkZSBtYW5lcmEgYXByZWNpYWJsZSBsYSBkaXN0cmlidWNpw7NuIGRlIGxhIHZhcmlhYmxlOiBsYSBjYXRlZ29yw61hIGRlIGNlcm8gcGFycXVlYWRlcm9zIHBhc2EgYSBzZXIgbGEgc2VndW5kYSBtw6FzIGZyZWN1ZW50ZSBkZWwgY29uanVudG8sIHBvciBlbmNpbWEgaW5jbHVzbyBkZSBsb3MgaW5tdWVibGVzIGNvbiBkb3MuIEVzdGEgaW5mZXJlbmNpYSBlc3RhIGFwb3lhZGEgZW4gZWwgcGF0csOzbiBvYnNlcnZhZG8geSBlbiBlbCBjb25vY2ltaWVudG8gZGVsIG1lcmNhZG8sIG5vIGRlIHVuIGhlY2hvIHZlcmlmaWNhYmxlIGNvbiBsYSBpbmZvcm1hY2nDs24gZGlzcG9uaWJsZS4gRXMgcG9zaWJsZSBxdWUgYWxndW5vcyBkZSBlc29zIHJlZ2lzdHJvcyBjb3JyZXNwb25kYW4gYSB2aXZpZW5kYXMgcXVlIHPDrSBjdWVudGFuIGNvbiBwYXJxdWVhZGVybyB5IGN1eW8gYW51bmNpYW50ZSBvbWl0acOzIG1lbmNpb25hcmxvOyBlbCBlcnJvciBxdWUgZWxsbyBpbnRyb2R1Y2lyw61hLCBzaW4gZW1iYXJnbywgdGllbmUgdW5hIGRpcmVjY2nDs24gY29ub2NpZGEgKHN1YmVzdGltYXIgbGEgZGlzcG9uaWJpbGlkYWQgZGVsIGF0cmlidXRvKSBtaWVudHJhcyBxdWUgaW1wdXRhciB1biB2YWxvciBwcm9tZWRpbyBoYWJyw61hIGludHJvZHVjaWRvIHVuIHNlc2dvIGVuIHVuYSBkaXJlY2Npw7NuIGltcG9zaWJsZSBkZSBhbnRpY2lwYXIsIGFkZW3DoXMgZGUgYXRyaWJ1aXIgdW4gYWN0aXZvIGluZXhpc3RlbnRlIGEgdml2aWVuZGFzIHF1ZSBwcm9iYWJsZW1lbnRlIGNhcmVjZW4gZGUgcGFycXVlYWRlcm8uDQoNCiMjIyBQaXNvDQoNCkVsIGNvbXBvcnRhbWllbnRvIGRlIHBpc28gZXMgY29tcGxldGFtZW50ZSBkaXN0aW50by4gTGEgVGFibGEgOCBtdWVzdHJhIHF1ZSBsb3MgcG9yY2VudGFqZXMgZGUgYXVzZW5jaWEgb3NjaWxhbiBlbnRyZSAyNCw4JSB5IDMxLDAlIGVuIGFwYXJ0YW1lbnRvcyB5IGVudHJlIDM2LDAlIHkgNDYsMyUgZW4gY2FzYXMsIHNpbiBkaXJlY2Npw7NuIGFsZ3VuYSByZXNwZWN0byBhbCBlc3RyYXRvLCBkZSBtb2RvIHF1ZSBuaW5ndW5hIGRlIGxhcyB2YXJpYWJsZXMgb2JzZXJ2YWRhcyBheXVkYSBhIGV4cGxpY2FyIHBvciBxdcOpIGZhbHRhIGVsIGRhdG8uIFBvciBsbyB0YW50bywgYW50ZSBsYSBmYWx0YSBkZSB1biBtZWNhbmlzbW8gaWRlbnRpZmljYWJsZSBxdWUgZXhwbGlxdWUgbGEgYXVzZW5jaWEsIGltcHV0YXIgc3Vwb25kcsOtYSBhc3VtaXIgdW4gc3VwdWVzdG8gcXVlIGxvcyBkYXRvcyBubyByZXNwYWxkYW4uDQoNClNlIHByb2NlZGUgYSByZXZpc2FyIGxhIGRpc3RyaWJ1Y2nDs24gcG9yIHRpcG8gZGUgaW5tdWVibGUsIHBhcmEgY29uZmlybWFyIHNpIGxhIHZhcmlhYmxlIHNpZ25pZmljYSBsbyBtaXNtbyBlbiBjYXNhcyB5IGFwYXJ0YW1lbnRvcy4NCg0KYGBge3J9DQp2aXZpZW5kYV8wMl90aXBvcyB8Pg0KICBmaWx0ZXIoIWlzLm5hKHBpc28pKSB8Pg0KICBncm91cF9ieSh0aXBvKSB8Pg0KICBzdW1tYXJpc2UoDQogICAgbiA9IG4oKSwNCiAgICBNw61uaW1vICA9IG1pbihwaXNvKSwNCiAgICBNZWRpYW5hID0gbWVkaWFuKHBpc28pLA0KICAgIFA5MCAgICAgPSBxdWFudGlsZShwaXNvLCAwLjkwKSwNCiAgICBNw6F4aW1vICA9IG1heChwaXNvKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgfD4NCiAgcmVuYW1lKFRpcG8gPSB0aXBvLCBSZWdpc3Ryb3MgPSBuKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDEwLiBEaXN0cmlidWNpw7NuIGRlIHBpc28gc2Vnw7puIHRpcG8gZGUgaW5tdWVibGUiLA0KICAgIG5vdGEgICA9ICJEaXN0cmlidWNpb25lcyBtYXJjYWRhbWVudGUgZGlzdGludGFzIHN1Z2VyaXLDrWFuIHF1ZSBsYSB2YXJpYWJsZSBubyBtaWRlIGxvIG1pc21vIGVuIGFtYm9zIHRpcG9zIGRlIGlubXVlYmxlLiINCiAgKQ0KYGBgDQoNCkxhIGNvbXBhcmFjacOzbiBkZSBsYSBkaXN0cmlidWNpw7NuIGRlIHBpc28gZW50cmUgY2FzYXMgeSBhcGFydGFtZW50b3MgcmV2ZWzDsyBxdWUgbGEgdmFyaWFibGUgbm8gbWlkZSBsbyBtaXNtbyBlbiBhbWJvcyBjYXNvcy4gRW4gYXBhcnRhbWVudG9zLCBsYSBtZWRpYW5hIHNlIHNpdMO6YSBlbiBlbCBwaXNvIDQgeSBlbCBwZXJjZW50aWwgOTAgZW4gZWwgOSwgY29uIHZhbG9yZXMgcXVlIGFsY2FuemFuIGVsIHBpc28gMTI7IGxhIGRpc3RyaWJ1Y2nDs24gc2UgZXh0aWVuZGUgYSBsbyBsYXJnbyBkZSB0b2RhIGxhIGVzY2FsYSwgdGFsIGNvbW8gY2FicsOtYSBlc3BlcmFyIGRlIGlubXVlYmxlcyB1YmljYWRvcyBlbiBlZGlmaWNhY2lvbmVzIGRlIGFsdHVyYXMgZGl2ZXJzYXMuIEVuIGNhc2FzLCBlbiBjYW1iaW8sIGxhIG1lZGlhbmEgZXMgMiB5IGVsIHBlcmNlbnRpbCA5MCBhcGVuYXMgbGxlZ2EgYSAzLiANCg0KU2luIGVtYmFyZ28sIGVzIGltcG9ydGFudGUgdGVuZXIgZW4gY3VlbnRhIHF1ZSB1bmEgY2FzYSBubyBvY3VwYSB1biBuaXZlbCBkZW50cm8gZGUgdW5hIGVkaWZpY2FjacOzbiwgcG9yIGxvIHF1ZSBlbCBuw7ptZXJvIGluZ3Jlc2FkbyBzb2xvIHB1ZWRlIHJlZmVyaXJzZSBhIGxhIGNhbnRpZGFkIGRlIG5pdmVsZXMgZGUgbGEgY29uc3RydWNjacOzbi4gUG9yIGxvIHRhbnRvLCBsYSB2YXJpYWJsZSBleHByZXNhIGxhIHViaWNhY2nDs24gZGVsIGlubXVlYmxlIGRlbnRybyBkZWwgZWRpZmljaW8gZW4gZWwgY2FzbyBkZSBsb3MgYXBhcnRhbWVudG9zIHkgZWwgbsO6bWVybyBkZSBwbGFudGFzIGRlIGxhIHZpdmllbmRhIGVuIGVsIGNhc28gZGUgbGFzIGNhc2FzLiBTb24gYXRyaWJ1dG9zIGNvbmNlcHR1YWxtZW50ZSBkaXN0aW50b3M6IGVuIGVsIHByaW1lcm8sIHVuIHZhbG9yIGFsdG8gaW5kaWNhIGFsdHVyYSByZXNwZWN0byBhbCBzdWVsbzsgZW4gZWwgc2VndW5kbywgbWF5b3Igc3VwZXJmaWNpZSBjb25zdHJ1aWRhIHJlcGFydGlkYSBlbiB2YXJpb3Mgbml2ZWxlcy4gTWFudGVuZXJsb3MgZW4gdW5hIG1pc21hIGNvbHVtbmEgaW1wbGljYXLDrWEgcXVlIGN1YWxxdWllciBlc3RhZMOtc3RpY28gY2FsY3VsYWRvIHNvYnJlIGVsbGEgbWV6Y2xhcsOtYSBkb3MgbWFnbml0dWRlcyBzaW4gcmVsYWNpw7NuIGVudHJlIHPDrSwgeSBxdWUgdW4gbW9kZWxvIGRlIHByZWNpb3MgZXN0aW1hcsOtYSB1biBjb2VmaWNpZW50ZSDDum5pY28gcGFyYSBkb3MgZWZlY3RvcyBkZSBuYXR1cmFsZXphIGRpZmVyZW50ZS4gUG9yIGNvbnNpZ3VpZW50ZSwgbGEgdmFyaWFibGUgc2Ugc2VwYXJhcsOhIGVuIGRvcywgY2FkYSB1bmEgcmVzdHJpbmdpZGEgYWwgdGlwbyBkZSBpbm11ZWJsZS4NCg0KDQpgYGB7cn0NCnZpdmllbmRhXzAzX25hIDwtIHZpdmllbmRhXzAzX25hIHw+DQogIG11dGF0ZSgNCiAgICBwaXNvX2FwdG8gICAgPSBpZl9lbHNlKHRpcG8gPT0gIkFwYXJ0YW1lbnRvIiwgcGlzbywgTkFfaW50ZWdlcl8pLA0KICAgIG5pdmVsZXNfY2FzYSA9IGlmX2Vsc2UodGlwbyA9PSAiQ2FzYSIsIHBpc28sIE5BX2ludGVnZXJfKQ0KICApDQoNCiMgUmVzdW1lbiBkZSBsYXMgZG9zIHZhcmlhYmxlcyByZXN1bHRhbnRlcw0KYmluZF9yb3dzKA0KICB2aXZpZW5kYV8wM19uYSB8Pg0KICAgIGZpbHRlcih0aXBvID09ICJBcGFydGFtZW50byIpIHw+DQogICAgc3VtbWFyaXNlKA0KICAgICAgVmFyaWFibGUgID0gInBpc29fYXB0byIsDQogICAgICBBcGxpY2FfYSAgPSAiQXBhcnRhbWVudG9zIiwNCiAgICAgIFJlZ2lzdHJvcyA9IG4oKSwNCiAgICAgIENvbl9kYXRvICA9IHN1bSghaXMubmEocGlzb19hcHRvKSksDQogICAgICBTaW5fZGF0byAgPSBzdW0oaXMubmEocGlzb19hcHRvKSksDQogICAgICBNw61uaW1vICAgID0gbWluKHBpc29fYXB0bywgbmEucm0gPSBUUlVFKSwNCiAgICAgIE1lZGlhbmEgICA9IG1lZGlhbihwaXNvX2FwdG8sIG5hLnJtID0gVFJVRSksDQogICAgICBQOTAgICAgICAgPSBxdWFudGlsZShwaXNvX2FwdG8sIDAuOTAsIG5hLnJtID0gVFJVRSksDQogICAgICBNw6F4aW1vICAgID0gbWF4KHBpc29fYXB0bywgbmEucm0gPSBUUlVFKQ0KICAgICksDQogIHZpdmllbmRhXzAzX25hIHw+DQogICAgZmlsdGVyKHRpcG8gPT0gIkNhc2EiKSB8Pg0KICAgIHN1bW1hcmlzZSgNCiAgICAgIFZhcmlhYmxlICA9ICJuaXZlbGVzX2Nhc2EiLA0KICAgICAgQXBsaWNhX2EgID0gIkNhc2FzIiwNCiAgICAgIFJlZ2lzdHJvcyA9IG4oKSwNCiAgICAgIENvbl9kYXRvICA9IHN1bSghaXMubmEobml2ZWxlc19jYXNhKSksDQogICAgICBTaW5fZGF0byAgPSBzdW0oaXMubmEobml2ZWxlc19jYXNhKSksDQogICAgICBNw61uaW1vICAgID0gbWluKG5pdmVsZXNfY2FzYSwgbmEucm0gPSBUUlVFKSwNCiAgICAgIE1lZGlhbmEgICA9IG1lZGlhbihuaXZlbGVzX2Nhc2EsIG5hLnJtID0gVFJVRSksDQogICAgICBQOTAgICAgICAgPSBxdWFudGlsZShuaXZlbGVzX2Nhc2EsIDAuOTAsIG5hLnJtID0gVFJVRSksDQogICAgICBNw6F4aW1vICAgID0gbWF4KG5pdmVsZXNfY2FzYSwgbmEucm0gPSBUUlVFKQ0KICAgICkNCikgfD4NCiAgbXV0YXRlKA0KICAgIGAlIHNpbiBkYXRvYCA9IHNwcmludGYoIiUuMWYiLCAxMDAgKiBTaW5fZGF0byAvIFJlZ2lzdHJvcyksDQogICAgLmFmdGVyID0gU2luX2RhdG8NCiAgKSB8Pg0KICByZW5hbWUoDQogICAgYEFwbGljYSBhYCAgPSBBcGxpY2FfYSwNCiAgICBgQ29uIGRhdG9gICA9IENvbl9kYXRvLA0KICAgIGBTaW4gZGF0b2AgID0gU2luX2RhdG8NCiAgKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDExLiBEZXNhZ3JlZ2FjacOzbiBkZSBwaXNvIGVuIGRvcyB2YXJpYWJsZXMgc2Vnw7puIHRpcG8gZGUgaW5tdWVibGUiLA0KICAgIGdydXBvcyA9IGMoIiAiID0gMiwgIkNvYmVydHVyYSBkZW50cm8gZGVsIHRpcG8iID0gNCwgIkRpc3RyaWJ1Y2nDs24iID0gNCksDQogICAgbm90YSAgID0gIkNhZGEgdmFyaWFibGUgc2UgY2FsY3VsYSDDum5pY2FtZW50ZSBzb2JyZSBlbCB0aXBvIGRlIGlubXVlYmxlIGVuIGVsIHF1ZSBzdSBzaWduaWZpY2FkbyBlcyBpbmVxdcOtdm9jby4gTG9zIGF1c2VudGVzIHF1ZSByZXN1bHRhbiBkZSBhcGxpY2FyIGxhIHZhcmlhYmxlIGFsIHRpcG8gY29udHJhcmlvIG5vIHNlIGNvbnRhYmlsaXphbiwgcHVlcyBjb3JyZXNwb25kZW4gYSB1bmEgY29uZGljacOzbiBxdWUgbm8gYXBsaWNhIGFsIHJlZ2lzdHJvLiINCiAgKQ0KYGBgDQoNCkNvbnRpbnVhbW9zIGFuYWxpemFuZG8gc2kgbGEgYXVzZW5jaWEgZGUgZGF0b3MgZW4gcGlzbyBlc3RhIGFzb2NpYWRhIGEgb3RyYXMgdmFyaWFibGVzLiBQcmltZXJvIGxhcyBjYXRlZ29yaWNhcy4gDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDNfbmEgfD4NCiAgZmlsdGVyKHRpcG8gPT0gIkFwYXJ0YW1lbnRvIikgfD4NCiAgZ3JvdXBfYnkoem9uYSkgfD4NCiAgc3VtbWFyaXNlKA0KICAgIFJlZ2lzdHJvcyA9IG4oKSwNCiAgICBgUGlzbyBhdXNlbnRlICVgID0gc3ByaW50ZigiJS4xZiIsIDEwMCAqIG1lYW4oaXMubmEocGlzb19hcHRvKSkpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKSB8Pg0KICByZW5hbWUoWm9uYSA9IHpvbmEpIHw+DQogIHRhYmxhX2VzdGlsbygNCiAgICB0aXR1bG8gPSAiVGFibGEgMTIuIEF1c2VuY2lhIGRlIHBpc28gc2Vnw7puIHpvbmEgKGFwYXJ0YW1lbnRvcykiLA0KICAgICkNCmBgYA0KDQpgYGB7cn0NCnZpdmllbmRhXzAzX25hIHw+DQogIGZpbHRlcih0aXBvID09ICJDYXNhIikgfD4NCiAgZ3JvdXBfYnkoem9uYSkgfD4NCiAgc3VtbWFyaXNlKA0KICAgIFJlZ2lzdHJvcyA9IG4oKSwNCiAgICBgTml2ZWxlcyBhdXNlbnRlICVgID0gc3ByaW50ZigiJS4xZiIsIDEwMCAqIG1lYW4oaXMubmEobml2ZWxlc19jYXNhKSkpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKSB8Pg0KICByZW5hbWUoWm9uYSA9IHpvbmEpIHw+DQogIHRhYmxhX2VzdGlsbygNCiAgICB0aXR1bG8gPSAiVGFibGEgMTMuIEF1c2VuY2lhIGRlbCBuw7ptZXJvIGRlIG5pdmVsZXMgc2Vnw7puIHpvbmEgKGNhc2FzKSIsDQogICkNCmBgYA0KDQpBIGNvbnRpbnVhY2nDs24gbnVtw6lyaWNhcw0KDQpgYGB7cn0NCnZpdmllbmRhXzAzX25hIHw+DQogIGZpbHRlcih0aXBvID09ICJBcGFydGFtZW50byIpIHw+DQogIG11dGF0ZShHcnVwbyA9IGlmX2Vsc2UoaXMubmEocGlzb19hcHRvKSwgIlNpbiBkYXRvIiwgIkNvbiBkYXRvIikpIHw+DQogIGdyb3VwX2J5KEdydXBvKSB8Pg0KICBzdW1tYXJpc2UoDQogICAgUmVnaXN0cm9zICAgICAgICAgICAgICA9IG4oKSwNCiAgICBgUHJlY2lvIG1lZGlhbm9gICAgICAgID0gbWVkaWFuKHByZWNpb20sIG5hLnJtID0gVFJVRSksDQogICAgYMOBcmVhIG1lZGlhbmFgICAgICAgICAgPSBtZWRpYW4oYXJlYWNvbnN0LCBuYS5ybSA9IFRSVUUpLA0KICAgIGBIYWJpdGFjaW9uZXMgKG1lZC4pYCAgPSBtZWRpYW4oaGFiaXRhY2lvbmVzLCBuYS5ybSA9IFRSVUUpLA0KICAgIGBCYcOxb3MgKG1lZC4pYCAgICAgICAgID0gbWVkaWFuKGJhbmlvcywgbmEucm0gPSBUUlVFKSwNCiAgICBgUGFycXVlYWRlcm9zIChtZWQuKWAgID0gbWVkaWFuKHBhcnF1ZWFkZXJvcywgbmEucm0gPSBUUlVFKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAxNC4gUGVyZmlsIGRlIGxvcyBhcGFydGFtZW50b3MgY29uIHkgc2luIGRhdG8gZGUgcGlzbyIsDQogICAgbm90YSAgID0gIkRpZmVyZW5jaWFzIGFwcmVjaWFibGVzIGVudHJlIGFtYm9zIGdydXBvcyBpbmRpY2Fyw61hbiBxdWUgbGEgYXVzZW5jaWEgc2UgYXNvY2lhIGEgbGFzIGNhcmFjdGVyw61zdGljYXMgZGVsIGlubXVlYmxlLiINCiAgKQ0KYGBgDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDNfbmEgfD4NCiAgZmlsdGVyKHRpcG8gPT0gIkNhc2EiKSB8Pg0KICBtdXRhdGUoR3J1cG8gPSBpZl9lbHNlKGlzLm5hKG5pdmVsZXNfY2FzYSksICJTaW4gZGF0byIsICJDb24gZGF0byIpKSB8Pg0KICBncm91cF9ieShHcnVwbykgfD4NCiAgc3VtbWFyaXNlKA0KICAgIFJlZ2lzdHJvcyAgICAgICAgICAgICA9IG4oKSwNCiAgICBgUHJlY2lvIG1lZGlhbm9gICAgICAgPSBtZWRpYW4ocHJlY2lvbSwgbmEucm0gPSBUUlVFKSwNCiAgICBgw4FyZWEgbWVkaWFuYWAgICAgICAgID0gbWVkaWFuKGFyZWFjb25zdCwgbmEucm0gPSBUUlVFKSwNCiAgICBgSGFiaXRhY2lvbmVzIChtZWQuKWAgPSBtZWRpYW4oaGFiaXRhY2lvbmVzLCBuYS5ybSA9IFRSVUUpLA0KICAgIGBCYcOxb3MgKG1lZC4pYCAgICAgICAgPSBtZWRpYW4oYmFuaW9zLCBuYS5ybSA9IFRSVUUpLA0KICAgIGBQYXJxdWVhZGVyb3MgKG1lZC4pYCA9IG1lZGlhbihwYXJxdWVhZGVyb3MsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApIHw+DQogIHRhYmxhX2VzdGlsbygNCiAgICB0aXR1bG8gPSAiVGFibGEgMTUuIFBlcmZpbCBkZSBsYXMgY2FzYXMgY29uIHkgc2luIGRhdG8gZGUgbml2ZWxlcyIsDQogICAgbm90YSAgID0gIkRpZmVyZW5jaWFzIGFwcmVjaWFibGVzIGVudHJlIGFtYm9zIGdydXBvcyBpbmRpY2Fyw61hbiBxdWUgbGEgYXVzZW5jaWEgc2UgYXNvY2lhIGEgbGFzIGNhcmFjdGVyw61zdGljYXMgZGVsIGlubXVlYmxlLiINCiAgKQ0KYGBgDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDNfbmEgfD4NCiAgZmlsdGVyKHRpcG8gPT0gIkNhc2EiLCAhaXMubmEobml2ZWxlc19jYXNhKSkgfD4NCiAgZ3JvdXBfYnkobml2ZWxlc19jYXNhKSB8Pg0KICBzdW1tYXJpc2UoDQogICAgQ2FzYXMgICAgICAgICAgICAgPSBuKCksDQogICAgYMOBcmVhIG1lZGlhbmFgICAgID0gbWVkaWFuKGFyZWFjb25zdCwgbmEucm0gPSBUUlVFKSwNCiAgICBgUHJlY2lvIG1lZGlhbm9gICA9IG1lZGlhbihwcmVjaW9tLCBuYS5ybSA9IFRSVUUpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKSB8Pg0KICByZW5hbWUoTml2ZWxlcyA9IG5pdmVsZXNfY2FzYSkgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAxNi4gw4FyZWEgeSBwcmVjaW8gc2Vnw7puIG7Dum1lcm8gZGUgbml2ZWxlcyAoY2FzYXMpIiwNCiAgICBub3RhICAgPSAiVW5hIHJlbGFjacOzbiBjcmVjaWVudGUgeSByZWd1bGFyIGVudHJlIG5pdmVsZXMgeSDDoXJlYSBjb25zdHJ1aWRhIGluZGljYXLDrWEgcXVlIGVzdGEgw7psdGltYSBwb2Ryw61hIHNlcnZpciBkZSBiYXNlIHBhcmEgaW1wdXRhciBsb3MgcmVnaXN0cm9zIHNpbiBkYXRvLiINCiAgKQ0KYGBgDQoNClBvciB6b25hIGFwYXJlY2VuIGRpZmVyZW5jaWFzIGFwcmVjaWFibGVzICgyMiwzJSBhIDM0LDElIGVuIGFwYXJ0YW1lbnRvcyB5IGRlIDMzLDElIGEgNTEsNSUgZW4gY2FzYXMsIHNpZW1wcmUgY29uIGxhIFpvbmEgU3VyIGNvbW8gbGEgbcOhcyBjb21wbGV0YSB5IGxhIFpvbmEgTm9ydGUgY29tbyBsYSBxdWUgbcOhcyBvbWl0ZSkgcGVybyBzZSB0cmF0YSBkZSB1biBwYXRyw7NuIHNvYnJlIGxhIGZyZWN1ZW5jaWEgZGUgbGEgb21pc2nDs24geSBubyBzb2JyZSBlbCB2YWxvciBvbWl0aWRvLiBTYWJlciBkw7NuZGUgZmFsdGEgbcOhcyBlbCBkYXRvIG5vIGRpY2UgbmFkYSBhY2VyY2EgZGUgZW4gcXXDqSBwaXNvIGVzdMOhIGVsIGFwYXJ0YW1lbnRvIG5pIGN1w6FudGFzIHBsYW50YXMgdGllbmUgbGEgY2FzYSBjdXlvIHJlZ2lzdHJvIHF1ZWTDsyB2YWPDrW8uIFBvciBzdSBwYXJ0ZSwgZWwgcGVyZmlsIGRlIGxvcyBpbm11ZWJsZXMgY29uIHkgc2luIGRhdG8gcmVzdWx0w7Mgc2ltaWxhciBlbiBhbWJvcyB0aXBvcywgY29uIGRpZmVyZW5jaWFzIG1lbm9yZXMgZW4gw6FyZWEsIGhhYml0YWNpb25lcywgYmHDsW9zIHkgcHJlY2lvIHF1ZSBubyBjb25maWd1cmFuIHVuYSBzdWJtdWVzdHJhIHBhcnRpY3VsYXIuDQoNCkxhIGLDunNxdWVkYSBkZSB1biBwcmVkaWN0b3IgdGFtcG9jbyBmdWUgZWZlY3RpdmEuIEVuIGFwYXJ0YW1lbnRvcyBlcmEgcHJldmlzaWJsZSBxdWUgZmFsbGFyYSwgcG9ycXVlIG5hZGEgZW4gbGFzIGNhcmFjdGVyw61zdGljYXMgZGUgdW5hIHZpdmllbmRhIGFudGljaXBhIHN1IHViaWNhY2nDs24gZGVudHJvIGRlIHVuIGVkaWZpY2lvLiBFbiBjYXNhcyBoYWLDrWEgbWF5b3IgZXhwZWN0YXRpdmEsIGRhZG8gcXVlIGVsIG7Dum1lcm8gZGUgcGxhbnRhcyBzw60gZ3VhcmRhIHVuYSByZWxhY2nDs24gZXNwZXJhYmxlIGNvbiBlbCDDoXJlYSBjb25zdHJ1aWRhLCBwZXJvIGVsIGNvbnRyYXN0ZSByZXN1bHTDsyBpcnJlZ3VsYXI6IGVsIMOhcmVhIG1lZGlhbmEgc3ViZSBkZSAyMjQsNSBhIDI2NCwwIG1ldHJvcyBjdWFkcmFkb3MgZW50cmUgdW5vIHkgZG9zIG5pdmVsZXMsIGRlc2NpZW5kZSBhIDIxMCwwIGVuIGxhcyBkZSB0cmVzIHkgdnVlbHZlIGEgc3ViaXIgZW4gbGFzIGRlIGN1YXRybywgc2luIGRpcmVjY2nDs24gZGVmaW5pZGEuIExvcyBuaXZlbGVzIGRlIGNpbmNvIGVuIGFkZWxhbnRlIHNlIGFwb3lhbiBlbiBhcGVuYXMgZG9zIGEgY3VhdHJvIG9ic2VydmFjaW9uZXMgeSBjb250aWVuZW4gYWRlbcOhcyBjYXNvcyBtYXRlcmlhbG1lbnRlIGltcG9zaWJsZXMgKGNhc2FzIGRlIGNpbmNvIHBsYW50YXMgY29uIDY3IG1ldHJvcyBjdWFkcmFkb3MsIG8gZGUgZGlleiBwbGFudGFzIGNvbiB1biBwcmVjaW8gaW5mZXJpb3IgYSBsYSBtZWRpYW5hIGdlbmVyYWwpIHF1ZSBxdWVkYXJvbiBzZcOxYWxhZG9zIHBhcmEgbGEgZXRhcGEgZGUgdmFsb3JlcyBhdMOtcGljb3MuDQoNCkNvbiBlc2UgcGFub3JhbWEgc2UgZGVzY2FydMOzIGltcHV0YXIgZW4gYW1ib3MgY2Fzb3MuIFJlbGxlbmFyIGxvcyB2YWPDrW9zIGhhYnLDrWEgc3VwdWVzdG8gYXNpZ25hciB2YWxvcmVzIHNpbiBuaW5nw7puIHJlc3BhbGRvIGVuIGxvcyBkYXRvcy4gQWRpY2lvbmFsbWVudGUsIGxhIGltcHV0YWNpw7NuIHBvciB0ZW5kZW5jaWEgY2VudHJhbCBjb21wcmltZSBhcnRpZmljaWFsbWVudGUgbGEgdmFyaWFiaWxpZGFkIGRlIGxhIHZhcmlhYmxlIHkgZGlzdG9yc2lvbmEgY3VhbHF1aWVyIGVzdGltYWNpw7NuIHBvc3RlcmlvciBkZSBzdSBlZmVjdG8gc29icmUgZWwgcHJlY2lvLiBFeGNsdWlyIGxhcyB2YXJpYWJsZXMgaW1wbGljYXJpYSBkZXNjYXJ0YXIgMy43MTkgYXBhcnRhbWVudG9zIHkgMS45NjUgY2FzYXMgY29uIGluZm9ybWFjacOzbiB2w6FsaWRhIGVuIHVuIGF0cmlidXRvIHF1ZSBzw60gaW5jaWRlIGVuIGVsIHZhbG9yIGRlbCBpbm11ZWJsZS4gU2Ugb3B0w7MgcG9yIGNvbnNlcnZhcmxhcyBjb24gc3VzIGF1c2VuY2lhcyB5IGRlY2xhcmFyIGxhIGJhc2UgZGUgY8OhbGN1bG8gZW4gY2FkYSBhbsOhbGlzaXMgcXVlIGxhcyB1dGlsaWNlLCB0ZW5pZW5kbyBlbiBjdWVudGEgcXVlIGxhIFpvbmEgU3VyIHNlIGVuY3VlbnRyYSBzb2JyZXJyZXByZXNlbnRhZGEgZW4gYW1ib3Mgc3ViY29uanVudG9zIGNvbiBkYXRvIGRpc3BvbmlibGUsIGxvIHF1ZSBkZWJlIHRlbmVyc2UgcHJlc2VudGUgZW4gY3VhbHF1aWVyIGNvbXBhcmFjacOzbiBlbnRyZSB6b25hcy4NCg0KYGBge3J9DQp2aXZpZW5kYV8wM19uYSA8LSB2aXZpZW5kYV8wM19uYSB8Pg0KICBtdXRhdGUoDQogICAgcGlzb19yZWdpc3RyYWRvID0gZmFjdG9yKA0KICAgICAgaWZfZWxzZShpcy5uYShwaXNvKSwgIlNpbiByZWdpc3RybyIsICJDb24gcmVnaXN0cm8iKSwNCiAgICAgIGxldmVscyA9IGMoIkNvbiByZWdpc3RybyIsICJTaW4gcmVnaXN0cm8iKQ0KICAgICkNCiAgKQ0KYGBgDQoNClBydWViYSBNQ0FSDQpgYGB7cn0NCm1jYXIgPC0gdml2aWVuZGFfMDNfbmEgfD4NCiAgc2VsZWN0KHByZWNpb20sIGFyZWFjb25zdCwgaGFiaXRhY2lvbmVzLCBiYW5pb3MsIHBhcnF1ZWFkZXJvcywgcGlzbykgfD4NCiAgbWNhcl90ZXN0KCkNCg0KbWNhciB8Pg0KICBtdXRhdGUoDQogICAgRGVjaXNpw7NuID0gaWZfZWxzZShwLnZhbHVlIDwgMC4wNSwgIlNlIHJlY2hhemEgTUNBUiIsICJObyBzZSByZWNoYXphIE1DQVIiKQ0KICApIHw+DQogIHJlbmFtZShgRXN0YWTDrXN0aWNvYCA9IHN0YXRpc3RpYywgYEdyYWRvcyBkZSBsaWJlcnRhZGAgPSBkZiwNCiAgICAgICAgIGBWYWxvciBwYCA9IHAudmFsdWUsIGBQYXRyb25lc2AgPSBtaXNzaW5nLnBhdHRlcm5zKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDE3LiBQcnVlYmEgZGUgTGl0dGxlIHBhcmEgZWwgc3VwdWVzdG8gTUNBUiIsDQogICkNCmBgYA0KDQpDb21vIGNvbXBsZW1lbnRvIGFsIGFuw6FsaXNpcyBwb3IgcGF0cm9uZXMgc2UgYXBsaWPDsyBsYSBwcnVlYmEgZGUgTGl0dGxlLCBjdXlvIHByb3DDs3NpdG8gZXMgY29udHJhc3RhciBmb3JtYWxtZW50ZSBzaSBsb3MgZGF0b3MgZmFsdGFuIGRlIG1hbmVyYSBjb21wbGV0YW1lbnRlIGFsZWF0b3JpYS4gRWwgcmVzdWx0YWRvIHJlY2hhemEgZXNhIGhpcMOzdGVzaXMsIHVuIGVzdGFkw61zdGljbyBkZSAxODAsMSBjb24gNSBncmFkb3MgZGUgbGliZXJ0YWQgYXJyb2phIHVuIHZhbG9yIHAgcHLDoWN0aWNhbWVudGUgbnVsbywgZGUgbW9kbyBxdWUgbGEgYXVzZW5jaWEgZGUgaW5mb3JtYWNpw7NuIHNlIGFzb2NpYSBjb24gbGFzIGNhcmFjdGVyw61zdGljYXMgb2JzZXJ2YWRhcyBkZSBsb3MgaW5tdWVibGVzLg0KDQpFbCByZXN1bHRhZG8gc2UgcmVwb3J0YSBjb21vIGNvbmZpcm1hY2nDs24gZm9ybWFsIGRlIHF1ZSBsYSBhdXNlbmNpYSBubyBlcyBjb21wbGV0YW1lbnRlIGFsZWF0b3JpYSwgcGVybyBsYXMgZGVjaXNpb25lcyBkZSB0cmF0YW1pZW50byBzZSBzdXN0ZW50YW4gZW4gbGEgbWFnbml0dWQgZGUgbGFzIGRpZmVyZW5jaWFzIG9ic2VydmFkYXMgeSBlbiBsYSBpbnRlcnByZXRhY2nDs24gc3VzdGFudGl2YSBkZSBjYWRhIHBhdHLDs24sIHkgbm8gZW4gbGEgc2lnbmlmaWNhbmNpYSBlc3RhZMOtc3RpY2EgZGUgbGEgcHJ1ZWJhLg0KDQojIyMgQmFsYW5jZSBkZSBDb21wbGV0aXR1ZA0KDQpgYGB7cn0NCmJpbmRfcm93cygNCiAgdGliYmxlKA0KICAgIE1vbWVudG8gICA9ICJBbnRlcyIsDQogICAgVmFyaWFibGUgID0gbmFtZXModml2aWVuZGFfMDJfdGlwb3MpLA0KICAgIEZhbHRhbnRlcyA9IG1hcF9pbnQodml2aWVuZGFfMDJfdGlwb3MsIH4gc3VtKGlzLm5hKC54KSkpDQogICksDQogIHRpYmJsZSgNCiAgICBNb21lbnRvICAgPSAiRGVzcHXDqXMiLA0KICAgIFZhcmlhYmxlICA9IG5hbWVzKHZpdmllbmRhXzAzX25hKSwNCiAgICBGYWx0YW50ZXMgPSBtYXBfaW50KHZpdmllbmRhXzAzX25hLCB+IHN1bShpcy5uYSgueCkpKQ0KICApDQopIHw+DQogIGZpbHRlcihGYWx0YW50ZXMgPiAwKSB8Pg0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gTW9tZW50bywgdmFsdWVzX2Zyb20gPSBGYWx0YW50ZXMsIHZhbHVlc19maWxsID0gMCkgfD4NCiAgbXV0YXRlKA0KICAgIGAlIGRlc3B1w6lzYCA9IHNwcmludGYoIiUuMWYiLCAxMDAgKiBEZXNwdcOpcyAvIG5yb3codml2aWVuZGFfMDNfbmEpKSwNCiAgICBgQXVzZW5jaWEgcmVhbGAgPSBjYXNlX3doZW4oDQogICAgICBWYXJpYWJsZSA9PSAicGlzb19hcHRvIiAgICB+ICIxLjM4MSAoMjcsMSUgZGUgYXBhcnRhbWVudG9zKSIsDQogICAgICBWYXJpYWJsZSA9PSAibml2ZWxlc19jYXNhIiB+ICIxLjI1NCAoMzksMCUgZGUgY2FzYXMpIiwNCiAgICAgIFZhcmlhYmxlID09ICJwYXJxdWVhZGVyb3MiIH4gIuKAlCIsDQogICAgICBUUlVFICAgICAgICAgICAgICAgICAgICAgICB+ICIyLjYzNSAoMzEsNyUgZGUgbGEgYmFzZSkiDQogICAgKSwNCiAgICBUcmF0YW1pZW50byA9IGNhc2Vfd2hlbigNCiAgICAgIFZhcmlhYmxlID09ICJwYXJxdWVhZGVyb3MiIH4gY2VsbF9zcGVjKCJSZWNvZGlmaWNhZG8gYSBjZXJvIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIiMwZjc2NmUiLCBib2xkID0gVFJVRSksDQogICAgICBWYXJpYWJsZSA9PSAicGlzbyIgICAgICAgICB+IGNlbGxfc3BlYygiRGVzYWdyZWdhZG8gcG9yIHRpcG8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiI2I1NzUxYSIpLA0KICAgICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICAgfiBjZWxsX3NwZWMoIkNvbnNlcnZhZG8gY29uIGRlY2xhcmFjacOzbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjNmI3YTg1IikNCiAgICApDQogICkgfD4NCiAgc2VsZWN0KFZhcmlhYmxlLCBBbnRlcywgRGVzcHXDqXMsIGAlIGRlc3B1w6lzYCwgYEF1c2VuY2lhIHJlYWxgLCBUcmF0YW1pZW50bykgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAxOC4gQmFsYW5jZSBkZSBjb21wbGV0aXR1ZCB0cmFzIGVsIHRyYXRhbWllbnRvIGRlIGRhdG9zIGF1c2VudGVzIiwNCiAgICBncnVwb3MgPSBjKCIgIiA9IDEsICJSZWdpc3Ryb3MgYXVzZW50ZXMiID0gMywgIkRpYWduw7NzdGljbyIgPSAyKSwNCiAgICBub3RhICAgPSAiTGEgY29sdW1uYSBkZSBhdXNlbmNpYSByZWFsIGRlc2N1ZW50YSBsb3MgcmVnaXN0cm9zIGRlbCB0aXBvIGRlIGlubXVlYmxlIGFsIHF1ZSBsYSB2YXJpYWJsZSBubyBhcGxpY2E6IGRlIGxvcyA0LjYwMCB2YWPDrW9zIGRlIHBpc29fYXB0bywgMy4yMTkgY29ycmVzcG9uZGVuIGEgY2FzYXMsIHkgZGUgbG9zIDYuMzU0IGRlIG5pdmVsZXNfY2FzYSwgNS4xMDAgY29ycmVzcG9uZGVuIGEgYXBhcnRhbWVudG9zLiBMYSBjb2JlcnR1cmEgZWZlY3RpdmEgc2UgY2FsY3VsYSBkZW50cm8gZGVsIHVuaXZlcnNvIHBlcnRpbmVudGUgYSBjYWRhIHZhcmlhYmxlLiINCiAgKQ0KYGBgDQoNCkVsIHRyYXRhbWllbnRvIGRlIGxvcyBkYXRvcyBhdXNlbnRlcyBzZSBjZXJyw7MgY29uIHVuIGJhbGFuY2UgcXVlIGNvbnRyYXN0YSBlbCBlc3RhZG8gZGUgbGFzIHZhcmlhYmxlcyBhbnRlcyB5IGRlc3B1w6lzIGRlIGxhcyBkZWNpc2lvbmVzIGFkb3B0YWRhcy4gRGUgbGFzIGRvcyBxdWUgcHJlc2VudGFiYW4gdmFjw61vcywgc29sbyBwYXJxdWVhZGVyb3MgcXVlZMOzIGNvbXBsZXRhOiBzdXMgMS42MDIgcmVnaXN0cm9zIHNpbiBkYXRvIHNlIHJlY29kaWZpY2Fyb24gY29tbyBjZXJvIGFsIGNvbnN0YXRhcnNlIHF1ZSBsYSB2YXJpYWJsZSBubyBjb250ZW7DrWEgbmluZ8O6biB2YWxvciBudWxvLiBMYSB2YXJpYWJsZSBwaXNvIGNvbnNlcnZhIHN1cyAyLjYzNSByZWdpc3Ryb3MgaW5jb21wbGV0b3MsIGVxdWl2YWxlbnRlcyBhbCAzMSw3JSBkZSBsYSBiYXNlLCBhaG9yYSBkaXN0cmlidWlkb3MgZW50cmUgbGFzIGRvcyB2YXJpYWJsZXMgcXVlIHJlc3VsdGFyb24gZGUgc3UgZGVzYWdyZWdhY2nDs24uDQoNCkxhIGJhc2UgbWFudHV2byBzdXMgOC4zMTkgcmVnaXN0cm9zLCBkYWRvIHF1ZSBuaW5ndW5hIGRlY2lzacOzbiBkZSBlc3RhIGV0YXBhIGltcGxpY8OzIGVsaW1pbmFyIG9ic2VydmFjaW9uZXMuIEVsIGNyaXRlcmlvIHF1ZSBvcmllbnTDsyBlbCBwcm9jZXNvIGZ1ZSBjb25zZXJ2YXIgbGEgaW5mb3JtYWNpw7NuIGRpc3BvbmlibGUgeSBoYWNlciBleHBsw61jaXRvIGxvIHF1ZSBubyBzZSBzYWJlLCBhbnRlcyBxdWUgY29tcGxldGFyIGxvcyB2YWPDrW9zIGNvbiB2YWxvcmVzIGVzdGltYWRvcyBzaW4gcmVzcGFsZG8gZW1ww61yaWNvLiANCg0KIyMgRGVwdXJhY2nDs24gZGUgdmFyaWFibGVzIGNhdGVnw7NyaWNhcw0KIyMjIEJhcnJpbw0KDQpMYSB2YXJpYWJsZSBCYXJyaW8gbXVlc3RyYSA0MzYgdmFsb3JlcyB1bmljb3MsIHBvciBsbyBxdWUgc2UgaGFjZSBuZWNlc2FyaW8gcmV2aXNhciBzdSBvcmdhbml6YWNpw7NuDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDNfbmEgfD4NCiAgY291bnQoYmFycmlvLCBzb3J0ID0gVFJVRSwgbmFtZSA9ICJSZWdpc3Ryb3MiKSB8Pg0KICBzbGljZV9oZWFkKG4gPSAyMCkgfD4NCiAgdGFibGFfZXN0aWxvKHRpdHVsbyA9ICJUYWJsYSAxOC4gQmFycmlvcyBtw6FzIGZyZWN1ZW50ZXMiKQ0KDQp2aXZpZW5kYV8wM19uYSB8Pg0KICBjb3VudChiYXJyaW8sIG5hbWUgPSAiUmVnaXN0cm9zIikgfD4NCiAgY291bnQoUmVnaXN0cm9zLCBuYW1lID0gIkNhbnRpZGFkIGRlIGJhcnJpb3MiKSB8Pg0KICBoZWFkKDEwKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDE5LiBEaXN0cmlidWNpw7NuIGRlIGJhcnJpb3Mgc2Vnw7puIHN1IGZyZWN1ZW5jaWEiLA0KICAgIG5vdGEgICA9ICJMb3MgYmFycmlvcyBxdWUgYXBhcmVjZW4gdW5hIG8gZG9zIHZlY2VzIHNvbiBjYW5kaWRhdG9zIG5hdHVyYWxlcyBhIGVycm9yZXMgZGUgZGlnaXRhY2nDs24uIg0KICApDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHN0cmluZ2Rpc3QpDQpsaWJyYXJ5KHN0cmluZ2kpDQoNCmJhcnJpb3MgPC0gdml2aWVuZGFfMDNfbmEgfD4gZGlzdGluY3QoYmFycmlvKSB8PiBwdWxsKGJhcnJpbykgfD4gc29ydCgpDQoNCiMgTm9ybWFsaXphY2nDs24gcHJldmlhOiBxdWl0YSB0aWxkZXMsIG1hecO6c2N1bGFzIHkgZXNwYWNpb3Mgc29icmFudGVzDQpub3JtYWxpemFyIDwtIGZ1bmN0aW9uKHgpIHsNCiAgeCB8Pg0KICAgIHN0cl9zcXVpc2goKSB8Pg0KICAgIHN0cl90b19sb3dlcigpIHw+DQogICAgc3RyaW5naTo6c3RyaV90cmFuc19nZW5lcmFsKCJMYXRpbi1BU0NJSSIpDQp9DQoNCiMgUGFyZXMgZGUgbm9tYnJlcyBzb3NwZWNob3NhbWVudGUgcGFyZWNpZG9zDQpwYXJlcyA8LSBleHBhbmRfZ3JpZChhID0gYmFycmlvcywgYiA9IGJhcnJpb3MpIHw+DQogIGZpbHRlcihhIDwgYikgfD4NCiAgbXV0YXRlKGQgPSBzdHJpbmdkaXN0KG5vcm1hbGl6YXIoYSksIG5vcm1hbGl6YXIoYiksIG1ldGhvZCA9ICJsdiIpKSB8Pg0KICBmaWx0ZXIoZCA8PSAyKSB8Pg0KICBhcnJhbmdlKGQpDQoNCnBhcmVzIHw+DQogIHNsaWNlX2hlYWQobiA9IDMwKSB8Pg0KICByZW5hbWUoYFZhcmlhbnRlIEFgID0gYSwgYFZhcmlhbnRlIEJgID0gYiwgRGlzdGFuY2lhID0gZCkgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAyMC4gUGFyZXMgZGUgbm9tYnJlcyBkZSBiYXJyaW8gY29uIGdyYWbDrWFzIHNpbWlsYXJlcyIsDQogICAgbm90YSAgID0gIkxhIGRpc3RhbmNpYSBpbmRpY2EgZWwgbsO6bWVybyBkZSBjYXJhY3RlcmVzIHF1ZSBkaWZpZXJlbiBlbnRyZSBhbWJhcyBncmFmw61hcy4gRGlzdGFuY2lhIDAgc2lnbmlmaWNhIHF1ZSBzb2xvIHNlIGRpZmVyZW5jaWFuIHBvciB0aWxkZXMsIG1hecO6c2N1bGFzIG8gZXNwYWNpb3MuIg0KICApDQpgYGANCg0KTGEgcmV2aXNpw7NuIGNvbmZpcm3DsyBxdWUgYnVlbmEgcGFydGUgZGUgbG9zIHZhbG9yZXMgw7puaWNvcyBjb3JyZXNwb25kZW4gYWwgbWlzbW8gbHVnYXIgZXNjcml0byBkZSB2YXJpYXMgbWFuZXJhcy4gRWwgcHJpbWVyIGluZGljaW8gZXN0w6EgZW4gbGEgZGlzdHJpYnVjacOzbiBkZSBmcmVjdWVuY2lhczogMTc3IG5vbWJyZXMgYXBhcmVjZW4gdW5hIHNvbGEgdmV6IHkgMzcgYXBhcmVjZW4gZG9zLCBkZSBtb2RvIHF1ZSBjZXJjYSBkZWwgY3VhcmVudGEgcG9yIGNpZW50byBkZSBsYXMgY2F0ZWdvcsOtYXMgYXBlbmFzIGNvbmNlbnRyYSBlbCBkb3MgcG9yIGNpZW50byBkZSBsb3MgcmVnaXN0cm9zLiANCg0KTGEgYsO6c3F1ZWRhIHNpc3RlbcOhdGljYSBkZSBlc2NyaXR1cmEgcGFyZWNpZGFzIGlkZW50aWZpY8OzIHRyZWludGEgcGFyZXMgcXVlIGRpZmllcmVuIMO6bmljYW1lbnRlIGVuIHRpbGRlcywgbWF5w7pzY3VsYXMgbyBlc3BhY2lvcyBzb2JyYW50ZXMgKGVqZW1wbG86IGp1bmluIHkganVuw61uLCBlbCBjYW5leSB5IEVsIENhbmV5LCBjaXVkYWQgamFyZGluIHkgQ2l1ZGFkIEphcmTDrW4pLCBjYXNvcyBkb25kZSBsYSB1bmlmaWNhY2nDs24gbm8gcmVxdWllcmUgbmluZ3VuYSBkZWNpc2nDs24gZGUgZm9uZG8gcG9ycXVlIHNlIHRyYXRhIGxpdGVyYWxtZW50ZSBkZWwgbWlzbW8gbm9tYnJlIGVzY3JpdG8gY29uIGRpc3RpbnRhIG9ydG9ncmFmw61hLiANCg0KTGEgZGVwdXJhY2nDs24gc2UgcmVhbGl6YXLDoSBlbiBkb3MgZmFzZXMuIExhIHByaW1lcmEgZXMgbGxldmFyIHRvZG9zIGxvcyBub21icmVzIGEgbWluw7pzY3VsYXMsIHJldGlyYXIgbGFzIHRpbGRlcyB5IGVsaW1pbmFyIGxvcyBlc3BhY2lvcyBzb2JyYW50ZXMgY29uIGxvIGN1YWwgbGFzIHRyZWludGEgdmFyaWFudGVzIG9ydG9ncsOhZmljYXMgc2UgZnVzaW9uYW4gYXV0b23DoXRpY2FtZW50ZS4gDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDRfdGV4dG8gPC0gdml2aWVuZGFfMDNfbmEgfD4NCiAgbXV0YXRlKA0KICAgIGJhcnJpb19ub3JtID0gYmFycmlvIHw+DQogICAgICBzdHJfc3F1aXNoKCkgfD4NCiAgICAgIHN0cl90b19sb3dlcigpIHw+DQogICAgICBzdHJpX3RyYW5zX2dlbmVyYWwoIkxhdGluLUFTQ0lJIikNCiAgKQ0KDQpjYXQoIkNhdGVnb3LDrWFzIG9yaWdpbmFsZXM6Iiwgbl9kaXN0aW5jdCh2aXZpZW5kYV8wM19uYSRiYXJyaW8pLA0KICAgICJ8IFRyYXMgbm9ybWFsaXphcjoiLCBuX2Rpc3RpbmN0KHZpdmllbmRhXzA0X3RleHRvJGJhcnJpb19ub3JtKSkNCmBgYA0KDQpQb3Igb3RybyBsYWRvLCBhcGFyZWNlbiByZWdpc3RyYWRvcyBwb3Igc2VwYXJhZG8gZWwgY2FuZXkgY29uIDIwOCBpbm11ZWJsZXMgeSBjYW5leSBjb24gODgsIGxvcyBjcmlzdGFsZXMgY29uIDE1NCB5IGNyaXN0YWxlcyBjb24gODMsIGxhIGZsb3JhIGNvbiAzNjYgeSB1cmJhbml6YWNpw7NuIGxhIGZsb3JhIGNvbiA4My4gTG8gY3VhbCBzZSBjb25zaWRlcmEgY29ycmVzcG9uZGVuIGFsIG1pc21vIGJhcnJpby4NCg0KYGBge3J9DQpjb21wYXJhciA8LSB2aXZpZW5kYV8wNF90ZXh0byB8Pg0KICBjb3VudChiYXJyaW9fbm9ybSwgbmFtZSA9ICJuIikgfD4NCiAgbXV0YXRlKHJhaXogPSBzdHJfcmVtb3ZlKGJhcnJpb19ub3JtLA0KICAgICAgICAgICAiXihlbHxsYXxsb3N8bGFzfHVyYmFuaXphY2lvbnx1cmJ8YmFycmlvfGNvbmp1bnRvfGNpdWRhZGVsYSlcXHMiKSkgfD4NCiAgZ3JvdXBfYnkocmFpeikgfD4NCiAgZmlsdGVyKG4oKSA+IDEpIHw+DQogIGFycmFuZ2UocmFpeiwgZGVzYyhuKSkgfD4NCiAgdW5ncm91cCgpDQoNCmNvbXBhcmFyIHw+DQogIHJlbmFtZShgTm9tYnJlIHJlZ2lzdHJhZG9gID0gYmFycmlvX25vcm0sIFJlZ2lzdHJvcyA9IG4sIGBSYcOteiBjb23Dum5gID0gcmFpeikgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAyMS4gTm9tYnJlcyBxdWUgY29tcGFydGVuIHJhw616IHRyYXMgZWxpbWluYXIgYXJ0w61jdWxvcyB5IHByZWZpam9zIiwNCiAgICBub3RhICAgPSAiQWdydXBhciBwb3IgbGEgcmHDrXogcGVybWl0ZSBkZXRlY3RhciB2YXJpYW50ZXMgcXVlIGxhIGRpc3RhbmNpYSBkZSBlZGljacOzbiBubyBjYXB0dXJhLCBjb21vIGxhIHByZXNlbmNpYSB1IG9taXNpw7NuIGRlbCBhcnTDrWN1bG8uIg0KICApDQpgYGANCg0KTGEgc2VndW5kYSBwYXJ0ZSBhYm9yZGEgbGFzIGR1cGxpY2FjaW9uZXMgcXVlIGVsIHRleHRvIG5vIGRlbGF0YSBhIHNpbXBsZSB2aXN0YSwgY29tbyBsYSBwcmVzZW5jaWEgdSBvbWlzacOzbiBkZWwgYXJ0w61jdWxvIG8gZGUgdW4gcHJlZmlqbyB1cmJhbsOtc3RpY28gKGNhbmV5IHkgZWwgY2FuZXksIGNyaXN0YWxlcyB5IGxvcyBjcmlzdGFsZXMsIHRlcXVlbmRhbWEgeSB1cmJhbml6YWNpw7NuIHRlcXVlbmRhbWEpLiBQYXJhIHJlc29sdmVybGFzIHNlIGV4dHJhZSBsYSByYcOteiBkZSBjYWRhIG5vbWJyZSBzdXByaW1pZW5kbyBlc29zIGVsZW1lbnRvcyBpbmljaWFsZXMgeSBzZSBhZG9wdGEgY29tbyBmb3JtYSBjYW7Ds25pY2EgbGEgZXNjcml0dXJhIG3DoXMgZnJlY3VlbnRlIGRlbnRybyBkZSBjYWRhIGdydXBvLCBjcml0ZXJpbyBxdWUgcHJlc2VydmEgbGEgY29ycmVzcG9uZGVuY2lhIGNvbiBlbCB1c28gcmVhbCBkZSBxdWllbmVzIHB1YmxpY2FuIGxvcyBhbnVuY2lvcyBwb3IgZW5jaW1hIGRlIGxhIGNvcnJlY2Npw7NuIGdyYW1hdGljYWwuDQoNCmBgYHtyfQ0KIyAxLiBOb3JtYWxpemFyOiBtaW7DunNjdWxhcywgc2luIHRpbGRlcywgc2luIGVzcGFjaW9zIHNvYnJhbnRlcw0Kdml2aWVuZGFfMDRfdGV4dG8gPC0gdml2aWVuZGFfMDNfbmEgfD4NCiAgbXV0YXRlKGJhcnJpb19ub3JtID0gYmFycmlvIHw+DQogICAgICAgICAgIHN0cl9zcXVpc2goKSB8PiBzdHJfdG9fbG93ZXIoKSB8Pg0KICAgICAgICAgICBzdHJpX3RyYW5zX2dlbmVyYWwoIkxhdGluLUFTQ0lJIikpDQoNCiMgMi4gRGljY2lvbmFyaW86IGxhIHZhcmlhbnRlIG1heW9yaXRhcmlhIGRlIGNhZGEgcmHDrXogc2UgdnVlbHZlIGxhIGZvcm1hIGNhbsOzbmljYQ0KZGljYyA8LSB2aXZpZW5kYV8wNF90ZXh0byB8Pg0KICBjb3VudChiYXJyaW9fbm9ybSwgbmFtZSA9ICJuIikgfD4NCiAgbXV0YXRlKHJhaXogPSBzdHJfcmVtb3ZlKGJhcnJpb19ub3JtLA0KICAgICAgICAgICAiXihlbHxsYXxsb3N8bGFzfHVyYmFuaXphY2lvbnx1cmJ8YmFycmlvfGNvbmp1bnRvfGNpdWRhZGVsYSlcXHMiKSkgfD4NCiAgZ3JvdXBfYnkocmFpeikgfD4NCiAgc2xpY2VfbWF4KG4sIG4gPSAxLCB3aXRoX3RpZXMgPSBGQUxTRSkgfD4NCiAgdW5ncm91cCgpIHw+DQogIHNlbGVjdChyYWl6LCBjYW5vbmljbyA9IGJhcnJpb19ub3JtKQ0KDQojIDMuIEFwbGljYXINCnZpdmllbmRhXzA0X3RleHRvIDwtIHZpdmllbmRhXzA0X3RleHRvIHw+DQogIG11dGF0ZShyYWl6ID0gc3RyX3JlbW92ZShiYXJyaW9fbm9ybSwNCiAgICAgICAgICAgIl4oZWx8bGF8bG9zfGxhc3x1cmJhbml6YWNpb258dXJifGJhcnJpb3xjb25qdW50b3xjaXVkYWRlbGEpXFxzIikpIHw+DQogIGxlZnRfam9pbihkaWNjLCBieSA9ICJyYWl6IikgfD4NCiAgbXV0YXRlKGJhcnJpb19ub3JtID0gY29hbGVzY2UoY2Fub25pY28sIGJhcnJpb19ub3JtKSkgfD4NCiAgc2VsZWN0KC1yYWl6LCAtY2Fub25pY28pDQoNCmNhdCgiQ2F0ZWdvcsOtYXMgb3JpZ2luYWxlczoiLCBuX2Rpc3RpbmN0KHZpdmllbmRhXzAzX25hJGJhcnJpbyksDQogICAgInwgVHJhcyBkZXB1cmFyOiIsIG5fZGlzdGluY3Qodml2aWVuZGFfMDRfdGV4dG8kYmFycmlvX25vcm0pKQ0KYGBgDQpgYGB7cn0NCnRpYmJsZSgNCiAgRXRhcGEgPSBjKCJDYXRlZ29yw61hcyBvcmlnaW5hbGVzIiwNCiAgICAgICAgICAgICJUcmFzIG5vcm1hbGl6YXIgb3J0b2dyYWbDrWEiLA0KICAgICAgICAgICAgIlRyYXMgdW5pZmljYXIgcG9yIHJhw616IGNvbcO6biIpLA0KICBDYXRlZ29yw61hcyA9IGMobl9kaXN0aW5jdCh2aXZpZW5kYV8wM19uYSRiYXJyaW8pLA0KICAgICAgICAgICAgICAgICBuX2Rpc3RpbmN0KHN0cl90b19sb3dlcihzdHJpX3RyYW5zX2dlbmVyYWwoc3RyX3NxdWlzaCh2aXZpZW5kYV8wM19uYSRiYXJyaW8pLCAiTGF0aW4tQVNDSUkiKSkpLA0KICAgICAgICAgICAgICAgICBuX2Rpc3RpbmN0KHZpdmllbmRhXzA0X3RleHRvJGJhcnJpb19ub3JtKSkNCikgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAyMi4gUmVkdWNjacOzbiBkZSBjYXRlZ29yw61hcyBlbiBsYSB2YXJpYWJsZSBiYXJyaW8iLA0KICAgIG5vdGEgICA9ICJMYSB1bmlmaWNhY2nDs24gdG9tw7MgY29tbyBmb3JtYSBjYW7Ds25pY2EgbGEgdmFyaWFudGUgbcOhcyBmcmVjdWVudGUgZGUgY2FkYSByYcOtei4iDQogICkNCmBgYA0KDQpMYSB2YXJpYWJsZSBwYXPDsyBkZSA0MzYgY2F0ZWdvcsOtYXMgYSAzNjEsIHVuYSByZWR1Y2Npw7NuIGRlbCAxNyUuIExhIG5vcm1hbGl6YWNpw7NuIG9ydG9ncsOhZmljYSBwb3Igc8OtIHNvbGEgZWxpbWluw7MgNDcgZHVwbGljYWRvcywgeSBsYSB1bmlmaWNhY2nDs24gcG9yIHJhw616IGNvbcO6biByZXNvbHZpw7Mgb3Ryb3MgMjguIA0KDQojIyMgWm9uYSwgVGlwbyB5IEVzdHJhdG8NCg0KTGFzIHJlc3RhbnRlcyB2YXJpYWJsZXMgY3VhbGl0YXRpdmFzIHNlIHJldmlzYXJvbiBjb24gZWwgbWlzbW8gY3JpdGVyaW8gYXBsaWNhZG8gYSBiYXJyaW8sIGF1bnF1ZSBzdSBiYWpvIG7Dum1lcm8gZGUgY2F0ZWdvcsOtYXMgcGVybWl0acOzIHVuYSB2ZXJpZmljYWNpw7NuIGRpcmVjdGEgbWVkaWFudGUgdGFibGFzIGRlIGZyZWN1ZW5jaWEuIFNlIGJ1c2NhYmEgY29uZmlybWFyIHF1ZSBjYWRhIHZhcmlhYmxlIGNvbnR1dmllcmEgw7puaWNhbWVudGUgbG9zIG5pdmVsZXMgZXNwZXJhZG9zLCBzaW4gdmFyaWFudGVzIG9ydG9ncsOhZmljYXMgbmkgY2F0ZWdvcsOtYXMgcmVzaWR1YWxlcyBkZSBiYWphIGZyZWN1ZW5jaWEgcXVlIGRlbGF0YXJhbiBlcnJvcmVzIGRlIGNhcHR1cmEuDQoNCmBgYHtyfQ0KbWFwX2RmcihjKCJ6b25hIiwgInRpcG8iLCAiZXN0cmF0byIpLCB+DQogIHZpdmllbmRhXzA0X3RleHRvIHw+DQogICAgY291bnQoLmRhdGFbWy54XV0sIG5hbWUgPSAiUmVnaXN0cm9zIikgfD4NCiAgICBtdXRhdGUoVmFyaWFibGUgPSAueCwgQ2F0ZWdvcsOtYSA9IGFzLmNoYXJhY3RlciguZGF0YVtbLnhdXSkpIHw+DQogICAgc2VsZWN0KFZhcmlhYmxlLCBDYXRlZ29yw61hLCBSZWdpc3Ryb3MpDQopIHw+DQogIG11dGF0ZShgJWAgPSBzcHJpbnRmKCIlLjFmIiwgMTAwICogUmVnaXN0cm9zIC8gbnJvdyh2aXZpZW5kYV8wNF90ZXh0bykpKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDIzLiBDYXRlZ29yw61hcyByZWdpc3RyYWRhcyBlbiBsYXMgZGVtw6FzIHZhcmlhYmxlcyBjdWFsaXRhdGl2YXMiLA0KICAgIG5vdGEgICA9ICJTZSB2ZXJpZmljYSBxdWUgY2FkYSB2YXJpYWJsZSBjb250ZW5nYSDDum5pY2FtZW50ZSBsb3Mgbml2ZWxlcyBlc3BlcmFkb3MsIHNpbiBkdXBsaWNhY2lvbmVzIHBvciBlc2NyaXR1cmEgbmkgY2F0ZWdvcsOtYXMgcmVzaWR1YWxlcyBkZSBiYWphIGZyZWN1ZW5jaWEuIg0KICApDQpgYGANCg0KTGFzIHRyZXMgdmFyaWFibGVzIG5vIHByZXNlbnRhbiBpbmNvbnNpc3RlbmNpYXMuIFpvbmEgcmVnaXN0cmEgY2luY28gbml2ZWxlcywgdGlwbyBkb3MgeSBlc3RyYXRvIGN1YXRybywgc2luIGR1cGxpY2FjaW9uZXMgbmkgdmFsb3JlcyBhbsOzbWFsb3MuIA0KDQojIyBFc3RhZMOtc3RpY29zIERlc2NyaXB0aXZvcw0KDQpgYGB7cn0NCm51bV92YXJzIDwtIGMoInByZWNpb20iLCAiYXJlYWNvbnN0IiwgImhhYml0YWNpb25lcyIsICJiYW5pb3MiLCAicGFycXVlYWRlcm9zIikNCg0Kdml2aWVuZGFfMDRfdGV4dG8gfD4NCiAgc2VsZWN0KGFsbF9vZihudW1fdmFycykpIHw+DQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInYiKSB8Pg0KICBmaWx0ZXIoIWlzLm5hKHYpKSB8Pg0KICBncm91cF9ieShWYXJpYWJsZSkgfD4NCiAgc3VtbWFyaXNlKA0KICAgIG4gICAgICAgICA9IG4oKSwNCiAgICBNZWRpYSAgICAgPSBtZWFuKHYpLA0KICAgIE1lZGlhbmEgICA9IG1lZGlhbih2KSwNCiAgICBgRC4gZXN0LmAgPSBzZCh2KSwNCiAgICBDViAgICAgICAgPSBzZCh2KSAvIG1lYW4odiksDQogICAgUTEgICAgICAgID0gcXVhbnRpbGUodiwgLjI1KSwNCiAgICBRMyAgICAgICAgPSBxdWFudGlsZSh2LCAuNzUpLA0KICAgIGBNw61uaW1vYCAgPSBtaW4odiksDQogICAgYE3DoXhpbW9gICA9IG1heCh2KSwNCiAgICAuZ3JvdXBzICAgPSAiZHJvcCINCiAgKSB8Pg0KICBtdXRhdGUoYWNyb3NzKGMoTWVkaWEsIE1lZGlhbmEsIGBELiBlc3QuYCwgUTEsIFEzKSwgfiByb3VuZCgueCwgMSkpLA0KICAgICAgICAgQ1YgPSBzcHJpbnRmKCIlLjJmIiwgQ1YpKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDI0LiBFc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvcyBkZSBsYXMgdmFyaWFibGVzIGN1YW50aXRhdGl2YXMiLA0KICAgIGdydXBvcyA9IGMoIiAiID0gMiwgIkNlbnRyYWxpZGFkIiA9IDIsICJEaXNwZXJzacOzbiIgPSAyLCAiUG9zaWNpw7NuIiA9IDQpLA0KICAgIG5vdGEgICA9ICJFbCBjb2VmaWNpZW50ZSBkZSB2YXJpYWNpw7NuIGV4cHJlc2EgbGEgZGVzdmlhY2nDs24gZXN0w6FuZGFyIGNvbW8gcHJvcG9yY2nDs24gZGUgbGEgbWVkaWEsIHBlcm1pdGllbmRvIGNvbXBhcmFyIGxhIGRpc3BlcnNpw7NuIHJlbGF0aXZhIGVudHJlIHZhcmlhYmxlcyBkZSBkaXN0aW50YSBlc2NhbGEuIg0KICApDQpgYGANCg0KYGBge3J9DQp2aXZpZW5kYV8wNF90ZXh0byB8Pg0KICBncm91cF9ieSh0aXBvKSB8Pg0KICBzdW1tYXJpc2UoDQogICAgbiAgICAgICAgICAgICAgICAgICA9IG4oKSwNCiAgICBgUHJlY2lvIG1lZGlhbm9gICAgID0gbWVkaWFuKHByZWNpb20pLA0KICAgIGBQcmVjaW8gbWVkaW9gICAgICAgPSByb3VuZChtZWFuKHByZWNpb20pLCAxKSwNCiAgICBgw4FyZWEgbWVkaWFuYWAgICAgICA9IG1lZGlhbihhcmVhY29uc3QpLA0KICAgIGDDgXJlYSBtZWRpYWAgICAgICAgID0gcm91bmQobWVhbihhcmVhY29uc3QpLCAxKSwNCiAgICBgSGFiaXRhY2lvbmVzYCAgICAgID0gbWVkaWFuKGhhYml0YWNpb25lcyksDQogICAgYEJhw7Fvc2AgICAgICAgICAgICAgPSBtZWRpYW4oYmFuaW9zKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgfD4NCiAgcmVuYW1lKFRpcG8gPSB0aXBvKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDI1LiBEZXNjcmlwdGl2b3Mgc2Vnw7puIHRpcG8gZGUgaW5tdWVibGUiLA0KICAgIG5vdGEgICA9ICJMYSBjb21wYXJhY2nDs24gZW50cmUgbWVkaWEgeSBtZWRpYW5hIHBlcm1pdGUgdmFsb3JhciBlbCBncmFkbyBkZSBhc2ltZXRyw61hIGRlIGNhZGEgZGlzdHJpYnVjacOzbiBkZW50cm8gZGUgc3Ugc2VnbWVudG8uIg0KICApDQpgYGANCg0KU2UgY2FsY3VsYXJvbiBtZWRpZGFzIGRlIGNlbnRyYWxpZGFkLCBkaXNwZXJzacOzbiB5IHBvc2ljacOzbiBwYXJhIGxhcyBjaW5jbyB2YXJpYWJsZXMgY3VhbnRpdGF0aXZhcywgcmVwb3J0YW5kbyBtZWRpYSB5IG1lZGlhbmEgZGUgbWFuZXJhIGNvbmp1bnRhLiBMYSBkZWNpc2nDs24gcmVzcG9uZGUgYSBsYSBuYXR1cmFsZXphIGRlIGxvcyBkYXRvcyBpbm1vYmlsaWFyaW9zOiBhbCBubyBleGlzdGlyIHVuIGzDrW1pdGUgc3VwZXJpb3IgZGUgcHJlY2lvLCB1biBuw7ptZXJvIHJlZHVjaWRvIGRlIHByb3BpZWRhZGVzIGRlIGFsdG8gdmFsb3IgZGVzcGxhemEgZWwgcHJvbWVkaW8geSBsbyBhbGVqYSBkZSBsbyBxdWUgZWZlY3RpdmFtZW50ZSBlbmN1ZW50cmEgdW4gY29tcHJhZG9yLiBMYSBkaXN0YW5jaWEgZW50cmUgYW1iYXMgbWVkaWRhcyBmdW5jaW9uYSBhZGVtw6FzIGNvbW8gaW5kaWNhZG9yIGRlIGFzaW1ldHLDrWEuIFNlIGluY29ycG9yw7MgZWwgY29lZmljaWVudGUgZGUgdmFyaWFjacOzbiwgcXVlIHBlcm1pdGUgY29tcGFyYXIgbGEgZGlzcGVyc2nDs24gcmVsYXRpdmEgZW50cmUgdmFyaWFibGVzIG1lZGlkYXMgZW4gZXNjYWxhcyBkaXN0aW50YXMsIHkgbG9zIGVzdGFkw61zdGljb3Mgc2UgY2FsY3VsYXJvbiB0YW1iacOpbiBwb3Igc2VwYXJhZG8gcGFyYSBjYXNhcyB5IGFwYXJ0YW1lbnRvcy4NCg0KRWwgc2VzZ28gaGFjaWEgbG9zIHZhbG9yZXMgYWx0b3MgcXVlZMOzIGNvbmZpcm1hZG8gZW4gbGFzIGRvcyB2YXJpYWJsZXMgY29udGludWFzLiBFbCBwcmVjaW8gbWVkaW8gYWxjYW56YSA0MzMsOSBtaWxsb25lcyAgZnJlbnRlIGEgdW5hIG1lZGlhbmEgZGUgMzMwLCB5IGVsIMOhcmVhIG1lZGlhIDE3NCw5IG1ldHJvcyBjdWFkcmFkb3MgZnJlbnRlIGEgMTIzIGRlIG1lZGlhbmE7IGVuIGFtYm9zIGNhc29zIGVsIHByb21lZGlvIHN1cGVyYSBhIGxhIG1lZGlhbmEgZW4gY2VyY2EgZGUgdW4gdGVyY2lvLCBsbyBxdWUgcmVmbGVqYSB1bmEgY29sYSBwcm9sb25nYWRhIGRlIGlubXVlYmxlcyBkZSBncmFuIHRhbWHDsW8geSB2YWxvci4gTG9zIG3DoXhpbW9zIGxvIGNvbmZpcm1hbjogMS45OTkgeSAxLjc0NSBtZXRyb3MgY3VhZHJhZG9zLCBjaWZyYXMgcXVlIG11bHRpcGxpY2FuIHBvciBtw6FzIGRlIGRpZXogZWwgdmFsb3IgbWVkaWFuby4gTGEgZGlzcGVyc2nDs24gZXMgYXNpbWlzbW8gZWxldmFkYSwgY29uIGNvZWZpY2llbnRlcyBkZSB2YXJpYWNpw7NuIGRlIDAsNzYgZW4gcHJlY2lvIHkgMCw4MiBlbiDDoXJlYSwgZGUgbW9kbyBxdWUgbGEgdmFyaWFiaWxpZGFkIHJlc3VsdGEgY29tcGFyYWJsZSBhIGxhIHByb3BpYSBtYWduaXR1ZCBtZWRpYS4NCg0KTGEgZGVzYWdyZWdhY2nDs24gcG9yIHRpcG8gZGUgaW5tdWVibGUgbXVlc3RyYSBkb3Mgc2VnbWVudG9zIGRpZmVyZW5jaWFkb3MuIExhcyBjYXNhcyBwcmVzZW50YW4gdW5hIG1lZGlhbmEgZGUgcHJlY2lvIGRlIDQzMCBtaWxsb25lcyBmcmVudGUgYSAyNzkgZGUgbG9zIGFwYXJ0YW1lbnRvcywgeSB1bmEgZGlmZXJlbmNpYSB0b2RhdsOtYSBtw6FzIG1hcmNhZGEgZW4gc3VwZXJmaWNpZTogMjQwIG1ldHJvcyBjdWFkcmFkb3MgZGUgbWVkaWFuYSBjb250cmEgOTAuIEFtYm9zIHNlZ21lbnRvcyBjb25zZXJ2YW4gZWwgc2VzZ28gaGFjaWEgbG9zIHZhbG9yZXMgYWx0b3MsIGF1bnF1ZSBtw6FzIHByb251bmNpYWRvIGVuIGFwYXJ0YW1lbnRvcywgZG9uZGUgZWwgcHJlY2lvIG1lZGlvIHN1cGVyYSBhIGxhIG1lZGlhbmEgZW4gdW4gMzElLiBFc3RhIHNlcGFyYWNpw7NuIGp1c3RpZmljYSBxdWUgbG9zIGFuw6FsaXNpcyBwb3N0ZXJpb3JlcyBtYW50ZW5nYW4gbGEgZGlzdGluY2nDs24gcG9yIHRpcG8gYW50ZXMgcXVlIHRyYXRhciBsYSBvZmVydGEgY29tbyB1biBjb25qdW50byBob21vZ8OpbmVvLg0KDQpMbGFtYSBsYSBhdGVuY2nDs24gcXVlIGxvcyBtw61uaW1vcyBkZSBoYWJpdGFjaW9uZXMgeSBiYcOxb3MgcmVnaXN0cmFuIGNlcm8sIHZhbG9yIHF1ZSByZXN1bHRhIGluY29uc2lzdGVudGUgY29uIHVuYSB2aXZpZW5kYSBoYWJpdGFibGUgeSBhcHVudGEgYSBlcnJvcmVzIGRlIGNhcHR1cmEuIA0KDQojIyBHcsOhZmljb3MgZGUgdGVuZGVuY2lhIGNlbnRyYWwuDQoNCiMjIyBQcmVjaW8NCg0KRWwgcHJlY2lvIHNlIHJlcHJlc2VudGEgbWVkaWFudGUgaGlzdG9ncmFtYXMgZW4gZG9zIGVzY2FsYXM6IGxhIG9yaWdpbmFsIHkgbGEgbG9nYXLDrXRtaWNhLiBFc3RhIHNlZ3VuZGEgcmVwcmVzZW50YWNpw7NuIHJlc3BvbmRlIGEgcXVlIGxvcyBwcmVjaW9zIHZhcsOtYW4gZGUgZm9ybWEgbXVsdGlwbGljYXRpdmEgYW50ZXMgcXVlIGFkaXRpdmE6IHVuIGluY3JlbWVudG8gZGUgY2llbiBtaWxsb25lcyBzdXBvbmUgZHVwbGljYXIgZWwgdmFsb3IgZGUgdW5hIHZpdmllbmRhIGRlIGdhbWEgbWVkaWEsIHBlcm8gYXBlbmFzIHVuIGNpbmNvIHBvciBjaWVudG8gZW4gdW5hIGRlIGdhbWEgYWx0YSwgZGUgbW9kbyBxdWUgZW4gZXNjYWxhIG9yaWdpbmFsIGFtYmFzIGRpZmVyZW5jaWFzIGFwYXJlY2VuIGNvbW8gZXF1aXZhbGVudGVzIHBlc2UgYSByZXByZXNlbnRhciBtYWduaXR1ZGVzIGRlIGNhbWJpbyBtdXkgZGlzdGludGFzLiBMYSB0cmFuc2Zvcm1hY2nDs24gbG9nYXLDrXRtaWNhIGNvbnZpZXJ0ZSBsYXMgcmF6b25lcyBlbiBkaXN0YW5jaWFzLCBjb24gbG8gY3VhbCBlbCBtaXNtbyBlc3BhY2lvIGdyw6FmaWNvIHNlcGFyYSB2YWxvcmVzIHF1ZSBndWFyZGFuIGxhIG1pc21hIHByb3BvcmNpw7NuIGVudHJlIHPDrS4gU2UgaW5jb3Jwb3Jhcm9uIGzDrW5lYXMgZGUgcmVmZXJlbmNpYSBwYXJhIGxhIG1lZGlhIHkgbGEgbWVkaWFuYSBjb24gZWwgZmluIGRlIGhhY2VyIHZpc2libGUgbGEgYXNpbWV0csOtYSBkZSBsYSBkaXN0cmlidWNpw7NuDQoNCkZpZ3VyYSAxLiBEaXN0cmlidWNpw7NuIGRlbCBwcmVjaW8gZGUgbG9zIGlubXVlYmxlcyBlbiBlc2NhbGEgb3JpZ2luYWwgeSBsb2dhcsOtdG1pY2ENCmBgYHtyfQ0KcDEgPC0gZ2dwbG90KHZpdmllbmRhXzA0X3RleHRvLCBhZXMocHJlY2lvbSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDYwLCBmaWxsID0gIiMyMTM4NGEiLCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IC4yKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWRpYW4ocHJlY2lvbSksIGNvbG9yID0gIk1lZGlhbmEiKSwgbGluZXdpZHRoID0gLjcpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW4ocHJlY2lvbSksIGNvbG9yID0gIk1lZGlhIiksDQogICAgICAgICAgICAgbGluZXdpZHRoID0gLjcsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiTWVkaWFuYSIgPSAiIzBmNzY2ZSIsICJNZWRpYSIgPSAiI2EzM2EzYSIpLA0KICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdCgNCiAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGUgPSBjKCJkYXNoZWQiLCAic29saWQiKSkpKSArDQogIGxhYnMoeCA9ICJQcmVjaW8gKG1pbGxvbmVzIGRlIHBlc29zKSIsIHkgPSAiRnJlY3VlbmNpYSIsDQogICAgICAgc3VidGl0bGUgPSAiRXNjYWxhIG9yaWdpbmFsIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KcDIgPC0gZ2dwbG90KHZpdmllbmRhXzA0X3RleHRvLCBhZXMocHJlY2lvbSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDYwLCBmaWxsID0gIiMyMTM4NGEiLCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmV3aWR0aCA9IC4yKSArDQogIHNjYWxlX3hfbG9nMTAoKSArDQogIGxhYnMoeCA9ICJQcmVjaW8gKGVzY2FsYSBsb2dhcsOtdG1pY2EpIiwgeSA9IE5VTEwsDQogICAgICAgc3VidGl0bGUgPSAiRXNjYWxhIGxvZ2Fyw610bWljYSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCnAxICsgcDINCmBgYA0KRW4gZXNjYWxhIG9yaWdpbmFsIGxhIGRpc3RyaWJ1Y2nDs24gZGVsIHByZWNpbyBzZSBjb25jZW50cmEgbWFyY2FkYW1lbnRlIGVuIGVsIGV4dHJlbW8gaW5mZXJpb3IsIGNvbiBsYSBtZWRpYW5hIHNpdHVhZGEgZW4gMzMwIG1pbGxvbmVzIGEgbGEgaXpxdWllcmRhIGRlIGxhIG1lZGlhIGRlIDQzNCB5IHVuYSBjb2xhIHF1ZSBzZSBwcm9sb25nYSBoYXN0YSBjZXJjYSBkZSBsb3MgMi4wMDAgbWlsbG9uZXMuIExhIHJlcHJlc2VudGFjacOzbiByZXN1bHRhIHBvY28gaW5mb3JtYXRpdmEgZW4gbGEgem9uYSBkb25kZSBzZSBhY3VtdWxhIGVsIGdydWVzbyBkZSBsYSBvZmVydGEsIHlhIHF1ZSBsb3MgaW5tdWVibGVzIGVudHJlIDEwMCB5IDQwMCBtaWxsb25lcyBxdWVkYW4gY29tcHJpbWlkb3MgZW4gdW5hIGZyYW5qYSBlc3RyZWNoYSBkZWwgZWplLiBMYSBlc2NhbGEgbG9nYXLDrXRtaWNhIGNvcnJpZ2UgZXNhIGNvbXByZXNpw7NuIHkgcmV2ZWxhIHVuYSBkaXN0cmlidWNpw7NuIGFwcm94aW1hZGFtZW50ZSBzaW3DqXRyaWNhIHkgdW5pbW9kYWwsIGNlbnRyYWRhIGVuIHRvcm5vIGEgbG9zIDMwMCBtaWxsb25lcy4gDQoNCkVsIGhpc3RvZ3JhbWEgbG9nYXLDrXRtaWNvIHByZXNlbnRhIHVuIHBlcmZpbCBpcnJlZ3VsYXIsIGNvbiBwaWNvcyB5IHZhbGxlcyBwcm9udW5jaWFkb3MgcXVlIG5vIHJlc3BvbmRlbiBhIGxhIGZvcm1hIHRlw7NyaWNhIGRlIHVuYSBkaXN0cmlidWNpw7NuIGNvbnRpbnVhLiBQb3Igb3RyYSBwYXJ0ZSwgbGEgZGlzdHJpYnVjacOzbiBzZSBpbnRlcnJ1bXBlIGRlIG1hbmVyYSBhYnJ1cHRhIGNlcmNhIGRlIGxvcyAyLjAwMCBtaWxsb25lcyBlbiBsdWdhciBkZSBkZWNhZXIgcHJvZ3Jlc2l2YW1lbnRlLiANCg0KIyMjIEFyZWENCg0KRWwgw6FyZWEgY29uc3RydWlkYSBzZSByZXByZXNlbnRhIG1lZGlhbnRlIGN1cnZhcyBkZSBkZW5zaWRhZCBzdXBlcnB1ZXN0YXMsIGRpZmVyZW5jaWFuZG8gY2FzYXMgZGUgYXBhcnRhbWVudG9zLiBTZSBvcHTDsyBwb3IgbGEgZGVuc2lkYWQgZW4gbHVnYXIgZGVsIGhpc3RvZ3JhbWEgcG9ycXVlIHBlcm1pdGUgbW9zdHJhciBhbWJhcyBkaXN0cmlidWNpb25lcyBlbiB1biBtaXNtbyBlc3BhY2lvIGdyw6FmaWNvIHkgdmFsb3JhciBkaXJlY3RhbWVudGUgc3UgZ3JhZG8gZGUgc29sYXBhbWllbnRvLCBjb3NhIHF1ZSBkb3MgaGlzdG9ncmFtYXMgZW5jaW1hZG9zIG5vIGZhY2lsaXRhbi4gTGEgZXNjYWxhIHNlIG1hbnR1dm8gbG9nYXLDrXRtaWNhLiANCg0KRmlndXJhIDIuIERpc3RyaWJ1Y2nDs24gZGVsIMOhcmVhIGNvbnN0cnVpZGEgc2Vnw7puIHRpcG8gZGUgaW5tdWVibGUNCmBgYHtyfQ0KZ2dwbG90KHZpdmllbmRhXzA0X3RleHRvLCBhZXMoYXJlYWNvbnN0LCBmaWxsID0gdGlwbykpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gLjU1LCBjb2xvciA9IE5BKSArDQogIHNjYWxlX3hfbG9nMTAoKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkFwYXJ0YW1lbnRvIiA9ICIjMjEzODRhIiwgIkNhc2EiID0gIiNiNTc1MWEiKSkgKw0KICBsYWJzKHggPSAiw4FyZWEgY29uc3RydWlkYSAobcKyLCBlc2NhbGEgbG9nYXLDrXRtaWNhKSIsIHkgPSAiRGVuc2lkYWQiLCBmaWxsID0gTlVMTCkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KTGFzIGRvcyBkaXN0cmlidWNpb25lcyBhcGFyZWNlbiBjbGFyYW1lbnRlIGRpZmVyZW5jaWFkYXMuIExvcyBhcGFydGFtZW50b3Mgc2UgY29uY2VudHJhbiBlbnRyZSBsb3MgNTAgeSBsb3MgMTIwIG1ldHJvcyBjdWFkcmFkb3MsIG1pZW50cmFzIHF1ZSBsYXMgY2FzYXMgc2UgdWJpY2FuIHByZWRvbWluYW50ZW1lbnRlIGVudHJlIGxvcyAxNTAgeSBsb3MgNTAwLCBjb24gdW4gc29sYXBhbWllbnRvIGFjb3RhZG8gZW4gdG9ybm8gYSBsb3MgMTIwIHkgMjAwIG1ldHJvcyBjdWFkcmFkb3MgZG9uZGUgY29pbmNpZGVuIGxvcyBhcGFydGFtZW50b3MgZGUgbWF5b3IgdGFtYcOxbyBjb24gbGFzIGNhc2FzIG3DoXMgcGVxdWXDsWFzLiANCg0KTGEgY3VydmEgY29ycmVzcG9uZGllbnRlIGEgbG9zIGFwYXJ0YW1lbnRvcyBwcmVzZW50YSBkb3MgbcOheGltb3MgbG9jYWxlcywgdW5vIGNlcmNhbm8gYSBsb3MgNjAgbWV0cm9zIGN1YWRyYWRvcyB5IG90cm8gYWxyZWRlZG9yIGRlIGxvcyA4NS4NCg0KTGFzIGNhc2FzLCBwb3Igc3UgcGFydGUsIG11ZXN0cmFuIHVuIG3DoXhpbW8gcHJpbmNpcGFsIGVuIHRvcm5vIGEgbG9zIDI4MCBtZXRyb3MgY3VhZHJhZG9zIHkgdW4gc2VndW5kbyByZWxpZXZlIG1lbm9zIHByb251bmNpYWRvIGNlcmNhIGRlIGxvcyA1MDAsIGNvbiB1bmEgZGlzdHJpYnVjacOzbiBzZW5zaWJsZW1lbnRlIG3DoXMgYW5jaGEgcXVlIGxhIGRlIGxvcyBhcGFydGFtZW50b3MuDQoNCiMjIyBWYXJpYWJsZXMgZGlzY3JldGFzIA0KDQpMYXMgdmFyaWFibGVzIGRlIGNvbnRlbyBzZSByZXByZXNlbnRhbiBtZWRpYW50ZSBkaWFncmFtYXMgZGUgYmFycmFzIGRlIGZyZWN1ZW5jaWEgYWJzb2x1dGEgeSBubyBtZWRpYW50ZSBjdXJ2YXMgZGUgZGVuc2lkYWQsIGRlY2lzacOzbiBxdWUgcmVzcG9uZGUgYSBzdSBuYXR1cmFsZXphIGRpc2NyZXRhLiANCg0KRmlndXJhIDMuIEZyZWN1ZW5jaWEgZGUgYmHDsW9zLCBoYWJpdGFjaW9uZXMgeSBwYXJxdWVhZGVyb3MNCmBgYHtyfQ0Kdml2aWVuZGFfMDRfdGV4dG8gfD4NCiAgc2VsZWN0KGhhYml0YWNpb25lcywgYmFuaW9zLCBwYXJxdWVhZGVyb3MpIHw+DQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInYiKSB8Pg0KICBnZ3Bsb3QoYWVzKGZhY3Rvcih2KSkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICIjMjEzODRhIikgKw0KICBmYWNldF93cmFwKH4gVmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIiwgbmNvbCA9IDMpICsNCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJGcmVjdWVuY2lhIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpMYXMgdHJlcyB2YXJpYWJsZXMgcHJlc2VudGFuIGRpc3RyaWJ1Y2lvbmVzIGNvbmNlbnRyYWRhcyB5IGNvbiBjb2xhIGhhY2lhIGxvcyB2YWxvcmVzIGFsdG9zLiBFbCBuw7ptZXJvIGRlIGJhw7FvcyBhbGNhbnphIHN1IG3DoXhpbW8gZW4gZG9zLCBjb24gY2VyY2EgZGUgMy4wMDAgaW5tdWVibGVzLCB5IGRlY3JlY2UgZGUgbWFuZXJhIHByb2dyZXNpdmEgaGFjaWEgdmFsb3JlcyBzdXBlcmlvcmVzLiBMYXMgaGFiaXRhY2lvbmVzIG11ZXN0cmFuIHVuYSBjb25jZW50cmFjacOzbiB0b2RhdsOtYSBtw6FzIG1hcmNhZGEgZW4gdHJlcywgY29uIG3DoXMgZGUgNC4wMDAgcmVnaXN0cm9zLCBkZSBtb2RvIHF1ZSBsb3MgaW5tdWVibGVzIGRlIGRvcywgdHJlcyB5IGN1YXRybyBoYWJpdGFjaW9uZXMgY29uc3RpdHV5ZW4gZWwgZ3J1ZXNvIGRlIGxhIG9mZXJ0YS4gRW4gcGFycXVlYWRlcm9zIGxhIGJhcnJhIG3DoXMgYWx0YSBjb3JyZXNwb25kZSBhbCB2YWxvciBjZXJvLCBjb24gYXByb3hpbWFkYW1lbnRlIDEuNjAwIGNhc29zLCBhdW5xdWUgY29udmllbmUgcmVjb3JkYXIgcXVlIGVzYSBjYXRlZ29yw61hIG5vIHByb3ZpZW5lIGRlIGxvcyBkYXRvcyBvcmlnaW5hbGVzIHNpbm8gZGUgbGEgcmVjb2RpZmljYWNpw7NuIGFwbGljYWRhIGVuIGxhIGV0YXBhIGRlIHRyYXRhbWllbnRvIGRlIGF1c2VudGVzLg0KDQojIyMgUGlzbyB5IG5pdmVsZXMNCg0KTGFzIGRvcyB2YXJpYWJsZXMgcmVzdWx0YW50ZXMgZGUgbGEgZGVzYWdyZWdhY2nDs24gZGUgcGlzbyBzZSByZXByZXNlbnRhcm9uIHBvciBzZXBhcmFkby4gQWwgaWd1YWwgcXVlIGxhcyBkZW3DoXMgdmFyaWFibGVzIGRlIGNvbnRlbywgc2UgZW1wbGVhcm9uIGRpYWdyYW1hcyBkZSBiYXJyYXMgZGUgZnJlY3VlbmNpYSBhYnNvbHV0YS4NCg0KRmlndXJhIDQuIERpc3RyaWJ1Y2nDs24gZGVsIG7Dum1lcm8gZGUgbml2ZWxlcyBlbiBjYXNhcyB5IGRlbCBwaXNvIGVuIGFwYXJ0YW1lbnRvcw0KYGBge3J9DQojIFBpc28geSBuaXZlbGVzLCBjYWRhIHVubyBlbiBzdSB1bml2ZXJzbw0KYmluZF9yb3dzKA0KICB2aXZpZW5kYV8wNF90ZXh0byB8PiBmaWx0ZXIodGlwbyA9PSAiQXBhcnRhbWVudG8iLCAhaXMubmEocGlzb19hcHRvKSkgfD4NCiAgICB0cmFuc211dGUoVmFyaWFibGUgPSAiUGlzbyAoYXBhcnRhbWVudG9zKSIsIHYgPSBwaXNvX2FwdG8pLA0KICB2aXZpZW5kYV8wNF90ZXh0byB8PiBmaWx0ZXIodGlwbyA9PSAiQ2FzYSIsICFpcy5uYShuaXZlbGVzX2Nhc2EpKSB8Pg0KICAgIHRyYW5zbXV0ZShWYXJpYWJsZSA9ICJOaXZlbGVzIChjYXNhcykiLCB2ID0gbml2ZWxlc19jYXNhKQ0KKSB8Pg0KICBnZ3Bsb3QoYWVzKGZhY3Rvcih2KSkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICIjMjEzODRhIikgKw0KICBmYWNldF93cmFwKH4gVmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikgKw0KICBsYWJzKHggPSBOVUxMLCB5ID0gIkZyZWN1ZW5jaWEiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkxhcyBjYXNhcyBwcmVzZW50YW4gdW5hIGNvbmNlbnRyYWNpw7NuIG1hcmNhZGEgZW4gZG9zIG5pdmVsZXMsIHF1ZSByZcO6bmUgY2VyY2EgZGUgOTQwIHJlZ2lzdHJvcywgY29uIHVubyB5IHRyZXMgbml2ZWxlcyBjb21vIHZhbG9yZXMgc2VjdW5kYXJpb3MgeSB1biBkZXNjZW5zbyBhYnJ1cHRvIGEgcGFydGlyIGRlIGN1YXRybzsgZGVzZGUgY2luY28gZW4gYWRlbGFudGUgbGFzIGZyZWN1ZW5jaWFzIHNvbiBwcsOhY3RpY2FtZW50ZSBudWxhcyB5IGNvcnJlc3BvbmRlbiBhIHJlZ2lzdHJvcyBxdWUgc2VyYW4gcmV2aXNhZG9zIGNvbW8gaW5jb25zaXN0ZW50ZXMuIExvcyBhcGFydGFtZW50b3MsIGVuIGNhbWJpbywgbXVlc3RyYW4gdW5hIG1lc2V0YSBlbnRyZSBsb3MgcGlzb3MgdW5vIHkgY2luY28sIGNvbiBmcmVjdWVuY2lhcyBzaW1pbGFyZXMgY2VyY2FuYXMgYSBsb3MgcXVpbmllbnRvcyBpbm11ZWJsZXMgZW4gY2FkYSBuaXZlbCwgc2VndWlkYSBkZSB1biBkZXNjZW5zbyBzb3N0ZW5pZG8gYSBwYXJ0aXIgZGVsIHNleHRvLiANCg0KIyMjIEJveHBsb3RzIEFyZWEgeSBQcmVjaW8gcG9yIHRpcG8gZGUgVml2aWVuZGENCg0KTGFzIGRvcyB2YXJpYWJsZXMgY29udGludWFzIHNlIHJlcHJlc2VudGFuIG1lZGlhbnRlIGRpYWdyYW1hcyBkZSBjYWphIChib3hwbG90cykgZGlmZXJlbmNpYWRvcyBwb3IgdGlwbyBkZSBpbm11ZWJsZSwgbWFudGVuaWVuZG8gbGEgZXNjYWxhIGxvZ2Fyw610bWljYSBlbXBsZWFkYSBlbiBsb3MgaGlzdG9ncmFtYXMuIEVzdGEgcmVwcmVzZW50YWNpw7NuIGNvbXBsZW1lbnRhIGVsIGNvbnRlbyBkZSB2YWxvcmVzIGV4dHJlbW9zIHJlYWxpemFkbyBwcmV2aWFtZW50ZSBhbCBtb3N0cmFyIG5vIHNvbG8gY3XDoW50b3Mgc29uIHNpbm8gZMOzbmRlIHNlIHNpdMO6YW4gcmVzcGVjdG8gYWwgY3VlcnBvIGRlIGxhIGRpc3RyaWJ1Y2nDs24geSBjb24gcXXDqSBkZW5zaWRhZCBzZSBkaXN0cmlidXllbiBhIGxvIGxhcmdvIGRlIGxhIGNvbGEuIA0KDQpGaWd1cmEgNS4gRGlhZ3JhbWFzIGRlIGNhamEgZGVsIMOhcmVhIGNvbnN0cnVpZGEgeSBlbCBwcmVjaW8gc2Vnw7puIHRpcG8gZGUgaW5tdWVibGUNCmBgYHtyfQ0KIyBQcmVjaW8geSDDoXJlYSwgcG9yIHRpcG8gZGUgaW5tdWVibGUNCnZpdmllbmRhXzA0X3RleHRvIHw+DQogIHNlbGVjdCh0aXBvLCBwcmVjaW9tLCBhcmVhY29uc3QpIHw+DQogIHBpdm90X2xvbmdlcihjKHByZWNpb20sIGFyZWFjb25zdCksIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInYiKSB8Pg0KICBtdXRhdGUoVmFyaWFibGUgPSByZWNvZGUoVmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVjaW9tID0gIlByZWNpbyAobWlsbG9uZXMpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFyZWFjb25zdCA9ICLDgXJlYSBjb25zdHJ1aWRhIChtwrIpIikpIHw+DQogIGdncGxvdChhZXModGlwbywgdiwgZmlsbCA9IHRpcG8pKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLmFscGhhID0gLjI1LCBvdXRsaWVyLnNpemUgPSAuOCwgd2lkdGggPSAuNSkgKw0KICBzY2FsZV95X2xvZzEwKCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJBcGFydGFtZW50byIgPSAiIzIxMzg0YSIsICJDYXNhIiA9ICIjYjU3NTFhIikpICsNCiAgZmFjZXRfd3JhcCh+IFZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZV95IikgKw0KICBsYWJzKHggPSBOVUxMLCB5ID0gIkVzY2FsYSBsb2dhcsOtdG1pY2EiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpFbCBjb21wb3J0YW1pZW50byBkZSBsb3MgdmFsb3JlcyBleHRyZW1vcyBkaWZpZXJlIGVudHJlIHZhcmlhYmxlcy4gTG9zIGFwYXJ0YW1lbnRvcyBwcmVzZW50YW4gdW5hIGNvbGEgc3VwZXJpb3IgZGVuc2FtZW50ZSBwb2JsYWRhIGVuIMOhcmVhIGNvbnN0cnVpZGEsIGNvbiB1bmEgY29uY2VudHJhY2nDs24gbm90YWJsZSBkZSBjYXNvcyBlbnRyZSBsb3MgMzAwIHkgbG9zIDEuMDAwIG1ldHJvcyBjdWFkcmFkb3MsIHJhbmdvIHF1ZSBjb2luY2lkZSBwcmVjaXNhbWVudGUgY29uIGVsIGludGVydmFsbyBub3JtYWwgZGUgbGFzIGNhc2FzLiBMYXMgY2FzYXMsIHBvciBzdSBwYXJ0ZSwgbXVlc3RyYW4gdmFsb3JlcyBhdMOtcGljb3MgZW4gZWwgZXh0cmVtbyBpbmZlcmlvciBkZWwgcHJlY2lvLCBjb24gcmVnaXN0cm9zIGNlcmNhbm9zIGEgbG9zIDcwIG1pbGxvbmVzIGRlIHBlc29zIHF1ZSBubyBlbmN1ZW50cmFuIGNvcnJlc3BvbmRlbmNpYSBlbiBsYSBkaXN0cmlidWNpw7NuIGRlIMOhcmVhcy4NCg0KIyMgVmFsb3JlcyBBdMOtcGljb3MNCg0KUGFyYSBsYSByZXZpc2nDs24gZGUgdmFsb3JlcyBhdMOtcGljb3Mgc2UgdGVuZHLDoSBlbiBjdWVudGEgZG9zIHNpdHVhY2lvbmVzIHF1ZSByZXF1aWVyZW4gdHJhdGFtaWVudG9zIGRpc3RpbnRvcy4gTGEgcHJpbWVyYSBjb3JyZXNwb25kZSBhIHZhbG9yZXMgaW1wb3NpYmxlcywgYXF1ZWxsb3MgcXVlIG5vIHB1ZWRlbiBwcmVzZW50YXJzZSBlbiBsYSByZWFsaWRhZCBjb24gaW5kZXBlbmRlbmNpYSBkZWwgY29udGV4dG8uIExhIHNlZ3VuZGEgY29tcHJlbmRlIGxvcyBleHRyZW1vcyBsZWfDrXRpbW9zLCBwcm9waWVkYWRlcyBlZmVjdGl2YW1lbnRlIGludXN1YWxlcyBwZXJvIHJlYWxlcywgcXVlIG5vIGRlYmVuIGVsaW1pbmFyc2Ugc2lubyBkZWNsYXJhcnNlLiANCg0KIyMjIFZhbG9yZXMgaW1wb3NpYmxlcy4NCg0KQWNhIHNlIGNvbnNpZGVyYSBxdWUgdW5hIHZpdmllbmRhIGRlc3RpbmFkYSBhIGxhIHZlbnRhIGN1ZW50YSBuZWNlc2FyaWFtZW50ZSBjb24gYWwgbWVub3MgdW5hIGhhYml0YWNpw7NuIHkgdW4gYmHDsW8sIHBvciBsbyBxdWUgZXN0b3MgdmFsb3JlcyBkZSAwIGNvcnJlc3BvbmRlbiBhIGVycm9yZXMgZGUgY2FwdHVyYS4NCg0KYGBge3J9DQp2aXZpZW5kYV8wNF90ZXh0byB8Pg0KICBmaWx0ZXIoaGFiaXRhY2lvbmVzID09IDAgfCBiYW5pb3MgPT0gMCkgfD4NCiAgc3VtbWFyaXNlKA0KICAgIGBTaW4gaGFiaXRhY2lvbmVzYCAgICAgID0gc3VtKGhhYml0YWNpb25lcyA9PSAwKSwNCiAgICBgU2luIGJhw7Fvc2AgICAgICAgICAgICAgPSBzdW0oYmFuaW9zID09IDApLA0KICAgIGBTaW4gbmluZ3VubyBkZSBsb3MgZG9zYD0gc3VtKGhhYml0YWNpb25lcyA9PSAwICYgYmFuaW9zID09IDApLA0KICAgIGBUb3RhbCBhZmVjdGFkb3NgICAgICAgID0gbigpDQogICkgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAyNi4gUmVnaXN0cm9zIGNvbiB2YWxvcmVzIGltcG9zaWJsZXMgZW4gaGFiaXRhY2lvbmVzIHkgYmHDsW9zIiwNCiAgICANCiAgKQ0KYGBgDQoNClNlIGlkZW50aWZpY2Fyb24gNzYgcmVnaXN0cm9zIGNvbiB2YWxvcmVzIGltcG9zaWJsZXMsIGVxdWl2YWxlbnRlcyBhbCAwLDklIGRlIGxhIGJhc2U6IDY2IGRlY2xhcmFuIGNlcm8gaGFiaXRhY2lvbmVzIHkgNDUgZGVjbGFyYW4gY2VybyBiYcOxb3MuIEVsIGhhbGxhemdvIHJlc3VsdGEgc2ltaWxhciBjb24gbG8gb2JzZXJ2YWRvIGVuIHBhcnF1ZWFkZXJvcywgZG9uZGUgbGEgYXVzZW5jaWEgZGVsIGF0cmlidXRvIHNlIGV4cHJlc2FiYSBpZ3VhbG1lbnRlIG1lZGlhbnRlIHVuIGNhbXBvIHZhY8Otby4NCg0KRW4gY29uc2VjdWVuY2lhLCBlc3RvcyBjZXJvcyBzZSByZWNvZGlmaWNhcm9uIGNvbW8gdmFsb3JlcyBhdXNlbnRlcyB5IG5vIGNvbW8gY2lmcmFzIGEgY29ycmVnaXIuIExhIGRlY2lzacOzbiBtYW50aWVuZSBlbCBjcml0ZXJpbyBhcGxpY2FkbyBhIGxvIGxhcmdvIGRlIHRvZG8gZWwgcHJlcHJvY2VzYW1pZW50bzogYXNpZ25hcmxlcyB1bmEgaGFiaXRhY2nDs24gbyB1biBiYcOxbyBzdXBvbmRyw61hIGZhYnJpY2FyIGluZm9ybWFjacOzbiBxdWUgbGEgYmFzZSBubyBjb250aWVuZSwgbWllbnRyYXMgcXVlIGRlY2xhcmFybG9zIGF1c2VudGVzIHByZXNlcnZhIGxhIGRpc3RpbmNpw7NuIGVudHJlIGxvIHF1ZSBzZSBjb25vY2UgeSBsbyBxdWUgc2ltcGxlbWVudGUgbm8gZnVlIHJlZ2lzdHJhZG8uDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDVfYXRpcGljb3MgPC0gdml2aWVuZGFfMDRfdGV4dG8gfD4NCiAgbXV0YXRlKA0KICAgIGhhYml0YWNpb25lcyA9IG5hX2lmKGhhYml0YWNpb25lcywgMEwpLA0KICAgIGJhbmlvcyAgICAgICA9IG5hX2lmKGJhbmlvcywgMEwpDQogICkNCmBgYA0KDQojIyMgRXh0cmVtb3MNCg0KTGEgaWRlbnRpZmljYWNpw7NuIGRlIHZhbG9yZXMgZXh0cmVtb3Mgc2UgYXBveWEgZW4gZWwgY3JpdGVyaW8gZGVsIHJhbmdvIGludGVyY3VhcnTDrWxpY28sIHF1ZSBzZcOxYWxhIGNvbW8gYXTDrXBpY2EgdG9kYSBvYnNlcnZhY2nDs24gc2l0dWFkYSBwb3IgZW5jaW1hIGRlbCB0ZXJjZXIgY3VhcnRpbCBtw6FzIHVuIG3Dumx0aXBsbyBkZSBsYSBkaXN0YW5jaWEgZW50cmUgY3VhcnRpbGVzLiBTZSBhZG9wdGFuIGRvcyBhanVzdGVzIHJlc3BlY3RvIGEgbGEgYXBsaWNhY2nDs24gY29udmVuY2lvbmFsIGRlbCBtw6l0b2RvLiBFbCBwcmltZXJvIGZ1ZSBlbXBsZWFyIHVuIGZhY3RvciBkZSB0cmVzIGVuIGx1Z2FyIGRlbCBoYWJpdHVhbCAxLDU6IGRhZG8gcXVlIGFtYmFzIGRpc3RyaWJ1Y2lvbmVzIHByZXNlbnRhbiB1biBzZXNnbyBwcm9udW5jaWFkbyBoYWNpYSBsb3MgdmFsb3JlcyBhbHRvcywgZWwgdW1icmFsIGVzdMOhbmRhciBtYXJjYXLDrWEgY29tbyBhdMOtcGljYSB1bmEgcG9yY2nDs24gY29uc2lkZXJhYmxlIGRlIG9mZXJ0YSBwZXJmZWN0YW1lbnRlIGxlZ8OtdGltYSwgeSBlbCBmYWN0b3IgZGUgdHJlcyAoY29ub2NpZG8gY29tbyBjcml0ZXJpbyBkZSBhdGlwaWNpZGFkIHNldmVyYSkgcmVzdHJpbmdlIGxhIHNlw7FhbGl6YWNpw7NuIGEgbG9zIGNhc29zIHZlcmRhZGVyYW1lbnRlIGFsZWphZG9zIGRlbCBjdWVycG8gZGUgbGEgZGlzdHJpYnVjacOzbi4gRWwgc2VndW5kbyBmdWUgY2FsY3VsYXIgbG9zIHVtYnJhbGVzIGRlIG1hbmVyYSBpbmRlcGVuZGllbnRlIHBhcmEgY2FzYXMgeSBhcGFydGFtZW50b3MsIGRlY2lzacOzbiBxdWUgc2UgZGVzcHJlbmRlIGRlIGxhIGRpZmVyZW5jaWEgZGUgZXNjYWxhIGVudHJlIGFtYm9zIHNlZ21lbnRvcy4gDQoNCmBgYHtyfQ0Kdml2aWVuZGFfMDRfdGV4dG8gfD4NCiAgZ3JvdXBfYnkodGlwbykgfD4NCiAgbXV0YXRlKA0KICAgIGxpbV9zdXBfcHJlY2lvID0gcXVhbnRpbGUocHJlY2lvbSwgLjc1KSArIDMgKiBJUVIocHJlY2lvbSksDQogICAgbGltX3N1cF9hcmVhICAgPSBxdWFudGlsZShhcmVhY29uc3QsIC43NSkgKyAzICogSVFSKGFyZWFjb25zdCkNCiAgKSB8Pg0KICBzdW1tYXJpc2UoDQogICAgbiAgICAgICAgICAgICAgICAgICAgPSBuKCksDQogICAgYFByZWNpbyBleHRyZW1vYCAgICAgPSBzdW0ocHJlY2lvbSA+IGxpbV9zdXBfcHJlY2lvKSwNCiAgICBgw4FyZWEgZXh0cmVtYWAgICAgICAgPSBzdW0oYXJlYWNvbnN0ID4gbGltX3N1cF9hcmVhKSwNCiAgICBgVW1icmFsIHByZWNpb2AgICAgICA9IHJvdW5kKGZpcnN0KGxpbV9zdXBfcHJlY2lvKSksDQogICAgYFVtYnJhbCDDoXJlYWAgICAgICAgID0gcm91bmQoZmlyc3QobGltX3N1cF9hcmVhKSksDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApIHw+DQogIHJlbmFtZShUaXBvID0gdGlwbykgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAyNy4gVmFsb3JlcyBleHRyZW1vcyBzZWfDum4gZWwgY3JpdGVyaW8gZGVsIHJhbmdvIGludGVyY3VhcnTDrWxpY28iLA0KICApDQpgYGANCg0KRWwgcHJvY2VkaW1pZW50byBpZGVudGlmaWPDsyAxNjUgYXBhcnRhbWVudG9zIHkgMzQgY2FzYXMgY29uIHByZWNpb3MgZXh0cmVtb3MsIHkgMTAxIGFwYXJ0YW1lbnRvcyB5IDI5IGNhc2FzIGNvbiDDoXJlYXMgZXh0cmVtYXMsIGxvIHF1ZSByZXByZXNlbnRhIGFscmVkZWRvciBkZWwgMywyJSB5IGVsIDIsMCUgZGUgY2FkYSBzZWdtZW50byByZXNwZWN0aXZhbWVudGUuIExvcyB1bWJyYWxlcyByZXN1bHRhbnRlcyByZWZsZWphbiBjb24gY2xhcmlkYWQgbGEgZGlzdGFuY2lhIGVudHJlIGFtYm9zIG1lcmNhZG9zOiB1biBhcGFydGFtZW50byBzZSBjb25zaWRlcmEgZXh0cmVtbyBhIHBhcnRpciBkZSAxLjE5NSBtaWxsb25lcyBkZSBwZXNvcyBvIDMxNiBtZXRyb3MgY3VhZHJhZG9zLCBtaWVudHJhcyBxdWUgZW4gbGFzIGNhc2FzIGVzb3MgbMOtbWl0ZXMgc2Ugc2l0w7phbiBlbiAxLjc4MCBtaWxsb25lcyB5IDkzOCBtZXRyb3MgY3VhZHJhZG9zLCBwcsOhY3RpY2FtZW50ZSBlbCB0cmlwbGUgZW4gZWwgY2FzbyBkZSBsYSBzdXBlcmZpY2llLiBMYSBkaWZlcmVuY2lhIGNvbmZpcm1hIHF1ZSB1biBjcml0ZXJpbyDDum5pY28gYXBsaWNhZG8gYWwgY29uanVudG8gZGUgbGEgYmFzZSBoYWJyw61hIGNsYXNpZmljYWRvIGNvbW8gYW7Ds21hbGEgYnVlbmEgcGFydGUgZGUgbGEgb2ZlcnRhIGRlIGNhc2FzLCBjdWFuZG8gc3UgbWF5b3IgdGFtYcOxbyBjb25zdGl0dXllIHByZWNpc2FtZW50ZSBlbCByYXNnbyBxdWUgbGFzIGRpc3Rpbmd1ZS4NCg0KRXN0b3MgcmVnaXN0cm9zIG5vIHNlIGVsaW1pbmFyb24gbmkgc2UgbW9kaWZpY2Fyb24uIEEgZGlmZXJlbmNpYSBkZSBsb3MgdmFsb3JlcyBpbXBvc2libGVzIGV4YW1pbmFkb3MgcHJldmlhbWVudGUsIHVuIGFwYXJ0YW1lbnRvIGRlIDEuMzAwIG1pbGxvbmVzIG8gdW5hIGNhc2EgZGUgbWlsIG1ldHJvcyBjdWFkcmFkb3MgZGVzY3JpYmVuIGlubXVlYmxlcyBxdWUgZXhpc3RlbiBlZmVjdGl2YW1lbnRlIGVuIGVsIHNlZ21lbnRvIGFsdG8gZGVsIG1lcmNhZG8sIHkgc3UgZXhjbHVzacOzbiBubyBjb3JyZWdpcsOtYSB1biBlcnJvciBzaW5vIHF1ZSByZWNvcnRhcsOtYSBsYSBvZmVydGEgcmVhbCBwcmVjaXNhbWVudGUgZW4gZWwgZXh0cmVtbyBxdWUgcmVzdWx0YSBtw6FzIGludGVyZXNhbnRlIHBhcmEgY2FyYWN0ZXJpemFyIGxhIGVzdHJ1Y3R1cmEgZGUgcHJlY2lvcyBkZSBsYSBjaXVkYWQuIA0KDQojIyBEZXRlY2Npw7NuIGRlIER1cGxpY2Fkb3MNCg0KU2UgYW5hbGl6w7MgbGEgYmFzZSBkZSBkYXRvcyBwYXJhIGV2YWx1YXIgbGEgcHJlY2VzaWEgZGUgcmVnaXN0cm9zIGR1cGxpY2Fkb3MuICBObyBzZSBlbmNvbnRyYXJvbiBkdXBsaWNhZG9zIGV4YWN0b3MsIGF1bnF1ZSBzaSBzZSBlbmNvbnRyYXJvbiByZWdpc3Ryb3MgY29uIGZpbGFzIGlkZW50aWNhcyBpZ25vcmFuZG8gZWwgaWRlbnRpZmljYWRvci4gIEF1bnF1ZSBjYWJlIGxhIHBvc2liaWxpZGFkIGRlIHF1ZSBzZWEgZWwgbWlzbW8gaW5tdWVibGUgcHVibGljYWRvIHZhcmlhcyB2ZWNlcywgbm8gc2UgdGllbmUgY2V0ZXphIGRlIHF1ZSBzZWEgYXNpIHkgcG9kcsOtYSBjb3JyZXNwb25kZXIgYSBhcGFydGFtZW50b3MgY29uIGxhcyBtaXNtYXMgY2FyYWN0ZXJpc3RpY2FzIGVuIHVuIG1pc21vIGVkaWZpY2lvLiAgUG9yIGxvIHRhbnRvIHNlIG9wdGEgcG9yIGNvbnNlcnZhciBlc3RvcyByZWdpc3Ryb3MuIA0KDQpgYGB7cn0NCnRpYmJsZSgNCiAgQ3JpdGVyaW8gPSBjKCJGaWxhcyBjb21wbGV0YW1lbnRlIGlkw6ludGljYXMiLA0KICAgICAgICAgICAgICAgIklkZW50aWZpY2Fkb3JlcyByZXBldGlkb3MiLA0KICAgICAgICAgICAgICAgIkZpbGFzIGlkw6ludGljYXMgaWdub3JhbmRvIGVsIGlkZW50aWZpY2Fkb3IiKSwNCiAgRHVwbGljYWRvcyA9IGMoDQogICAgc3VtKGR1cGxpY2F0ZWQodml2aWVuZGFfMDRfdGV4dG8pKSwNCiAgICBzdW0oZHVwbGljYXRlZCh2aXZpZW5kYV8wNF90ZXh0byRpZCkpLA0KICAgIHN1bShkdXBsaWNhdGVkKHZpdmllbmRhXzA0X3RleHRvIHw+IHNlbGVjdCgtaWQpKSkNCiAgKQ0KKSB8Pg0KICB0YWJsYV9lc3RpbG8oDQogICAgdGl0dWxvID0gIlRhYmxhIDI4LiBWZXJpZmljYWNpw7NuIGRlIHJlZ2lzdHJvcyBkdXBsaWNhZG9zIiwNCiAgICAgICkNCmBgYA0KDQpgYGB7cn0NCnZpdmllbmRhXzA1X2F0aXBpY29zIDwtIHZpdmllbmRhXzA0X3RleHRvIHw+DQogIG11dGF0ZSgNCiAgICBoYWJpdGFjaW9uZXMgPSBuYV9pZihoYWJpdGFjaW9uZXMsIDBMKSwNCiAgICBiYW5pb3MgICAgICAgPSBuYV9pZihiYW5pb3MsIDBMKQ0KICApDQoNCnZpdmllbmRhX2xpbXBpYSA8LSB2aXZpZW5kYV8wNV9hdGlwaWNvcw0KYGBgDQoNCg0KIyMgQ29ycmVsYWNpb25lcyBlbnRyZSB2YXJpYWJsZXMNCg0KTGEgZWxlY2Npw7NuIGRlbCBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhY2nDs24gZXhpZ2UgdmVyaWZpY2FyIHByZXZpYW1lbnRlIGVsIHN1cHVlc3RvIGRlIG5vcm1hbGlkYWQsIGRhZG8gcXVlIGVsIGNvZWZpY2llbnRlIGRlIFBlYXJzb24gYXN1bWUgZGlzdHJpYnVjaW9uZXMgYXByb3hpbWFkYW1lbnRlIG5vcm1hbGVzIHkgcmVzdWx0YSBzZW5zaWJsZSBhIGxhIHByZXNlbmNpYSBkZSB2YWxvcmVzIGV4dHJlbW9zLg0KDQojIyMgUHJ1ZWJhIFNoYXBpcm8tV2lsaw0KDQpgYGB7cn0NCmxpYnJhcnkobW9tZW50cykNCg0Kc2V0LnNlZWQoMTIzKQ0KbXVlc3RyYSA8LSB2aXZpZW5kYV9saW1waWEgfD4gc2xpY2Vfc2FtcGxlKG4gPSA0MDAwKQ0KDQptYXBfZGZyKGMoInByZWNpb20iLCAiYXJlYWNvbnN0IiwgImhhYml0YWNpb25lcyIsICJiYW5pb3MiLCAicGFycXVlYWRlcm9zIiksIH4gew0KICB4ICA8LSBtdWVzdHJhW1sueF1dIHw+IG5hLm9taXQoKQ0KICBzdyA8LSBzaGFwaXJvLnRlc3QoeCkNCiAgdGliYmxlKA0KICAgIFZhcmlhYmxlICAgPSAueCwNCiAgICBBc2ltZXRyw61hICA9IHJvdW5kKHNrZXduZXNzKHgpLCAyKSwNCiAgICBDdXJ0b3NpcyAgID0gcm91bmQoa3VydG9zaXMoeCksIDIpLA0KICAgIGBXYCAgICAgICAgPSByb3VuZChzdyRzdGF0aXN0aWMsIDMpLA0KICAgIGBWYWxvciBwYCAgPSBmb3JtYXQucHZhbChzdyRwLnZhbHVlLCBkaWdpdHMgPSAzLCBlcHMgPSAwLjAwMSkNCiAgKQ0KfSkgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAyOS4gUHJ1ZWJhIGRlIG5vcm1hbGlkYWQgZGUgU2hhcGlyby1XaWxrIiwNCiAgICBncnVwb3MgPSBjKCIgIiA9IDEsICJGb3JtYSBkZSBsYSBkaXN0cmlidWNpw7NuIiA9IDIsICJTaGFwaXJvLVdpbGsiID0gMiksDQogICAgbm90YSAgID0gIkNhbGN1bGFkYSBzb2JyZSB1bmEgbXVlc3RyYSBhbGVhdG9yaWEgZGUgNC4wMDAgcmVnaXN0cm9zLCBkYWRvIHF1ZSBsYSBwcnVlYmEgYWRtaXRlIHVuIG3DoXhpbW8gZGUgNS4wMDAgb2JzZXJ2YWNpb25lcy4gRW4gdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsIGxhIGFzaW1ldHLDrWEgZXMgMCB5IGxhIGN1cnRvc2lzIDMuIg0KICApDQpgYGANCg0KIyMjIFEtUSBQbG90DQoNCkZpZ3VyYSA2LiBRLVEgUGxvdCANCmBgYHtyfQ0Kdml2aWVuZGFfbGltcGlhIHw+DQogIHNlbGVjdChwcmVjaW9tLCBhcmVhY29uc3QsIGhhYml0YWNpb25lcywgYmFuaW9zLCBwYXJxdWVhZGVyb3MpIHw+DQogIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInYiKSB8Pg0KICBmaWx0ZXIoIWlzLm5hKHYpKSB8Pg0KICBnZ3Bsb3QoYWVzKHNhbXBsZSA9IHYpKSArDQogIHN0YXRfcXEoc2l6ZSA9IC40LCBhbHBoYSA9IC4yNSwgY29sb3IgPSAiIzIxMzg0YSIpICsNCiAgc3RhdF9xcV9saW5lKGNvbG9yID0gIiNhMzNhM2EiKSArDQogIGZhY2V0X3dyYXAofiBWYXJpYWJsZSwgc2NhbGVzID0gImZyZWUiLCBucm93ID0gMikgKw0KICBsYWJzKHggPSAiQ3VhbnRpbGVzIHRlw7NyaWNvcyIsIHkgPSAiQ3VhbnRpbGVzIG9ic2VydmFkb3MiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkxhIHBydWViYSBkZSBTaGFwaXJvLVdpbGsgZGVzY2FydGFyIGxhIG5vcm1hbGlkYWQgZGUgbGFzIGNpbmNvIHZhcmlhYmxlcyBjdWFudGl0YXRpdmFzIHJlY2hhemFuZG8gbGEgaGlww7N0ZXNpcyBudWxhIGVuIHRvZG9zIGxvcyBjYXNvcyBjb24gdmFsb3JlcyBwIGluZmVyaW9yZXMgYSAwLDAwMS4gRWwgw6FyZWEgY29uc3RydWlkYSBwcmVzZW50YSBlbCBjYXNvIG3DoXMgZXh0cmVtbywgY29uIHVuYSBhc2ltZXRyw61hIGRlIDIsNjcgeSB1bmEgY3VydG9zaXMgZGUgMTYsMDAsIHZhbG9yIGVzdGUgw7psdGltbyBtw6FzIGRlIGNpbmNvIHZlY2VzIHN1cGVyaW9yIGFsIHF1ZSBjb3JyZXNwb25kZSBhIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbDsgZWxsbyBpbmRpY2EgY29sYXMgY29uc2lkZXJhYmxlbWVudGUgbcOhcyBwZXNhZGFzIGRlIGxvIGFkbWlzaWJsZSBiYWpvIGVzZSBtb2RlbG8sIGVzdG8gZXMsIHVuYSBmcmVjdWVuY2lhIGRlIHZhbG9yZXMgYWxlamFkb3MgZGVsIGNlbnRybyBtdXkgc3VwZXJpb3IgYSBsYSBlc3BlcmFkYS4gRWwgcHJlY2lvIHByZXNlbnRhIHVuIGNvbXBvcnRhbWllbnRvIHNpbWlsYXIgYXVucXVlIG1lbm9zIHByb251bmNpYWRvLCBjb24gMSw3OSBkZSBhc2ltZXRyw61hIHkgNiw0NSBkZSBjdXJ0b3NpcywgeSBsb3MgZXN0YWTDrXN0aWNvcyBXIG9zY2lsYW4gZW50cmUgMCw3NTcgeSAwLDg5NSwgdG9kb3MgZWxsb3MgZGlzdGFudGVzIGRlIGxhIHVuaWRhZC4NCg0KTG9zIGdyYWZpY29zIFEtUSBwbG90LCBjb25maXJtYW4gdmlzdWFsbWVudGUgcXVlIGxvcyBkYXRvcyBubyB0aWVuZW4gdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLiANCg0KRXN0ZSBjb25qdW50byBkZSByZXN1bHRhZG9zIGp1c3RpZmljYSBlbCBlbXBsZW8gZGVsIGNvZWZpY2llbnRlIGRlIGNvcnJlbGFjacOzbiBkZSBTcGVhcm1hbiBlbiBsdWdhciBkZWwgZGUgUGVhcnNvbi4NCg0KIyMjIE1hdHJpeiBkZSBDb3JyZWxhY2lvbmVzDQoNCkZpZ3VyYSA3LiBNYXRyaXogZGUgQ29ycmVsYWNpb25lcw0KYGBge3J9DQpsaWJyYXJ5KGNvcnJwbG90KQ0KDQp2aXZpZW5kYV9saW1waWEgfD4NCiAgc2VsZWN0KHByZWNpb20sIGFyZWFjb25zdCwgaGFiaXRhY2lvbmVzLCBiYW5pb3MsIHBhcnF1ZWFkZXJvcykgfD4NCiAgY29yKHVzZSA9ICJjb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAic3BlYXJtYW4iKSB8Pg0KICBjb3JycGxvdChtZXRob2QgPSAiY29sb3IiLCB0eXBlID0gInVwcGVyIiwgYWRkQ29lZi5jb2wgPSAid2hpdGUiLA0KICAgICAgICAgICB0bC5jb2wgPSAiIzIxMzg0YSIsIHRsLnNydCA9IDQ1LCBudW1iZXIuY2V4ID0gLjg1LA0KICAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoIiNhMzNhM2EiLCAid2hpdGUiLCAiIzIxMzg0YSIpKSgyMDApKQ0KYGBgDQoNCkxhIG1hdHJpeiBkZSBjb3JyZWxhY2lvbmVzIGRlIFNwZWFybWFuIG11ZXN0cmEgYXNvY2lhY2lvbmVzIHBvc2l0aXZhcyB5IGRlIG1hZ25pdHVkIGFwcmVjaWFibGUgZW50cmUgbGFzIGNpbmNvIHZhcmlhYmxlcywgc2luIHF1ZSBuaW5ndW5hIHJlc3VsdGUgZMOpYmlsLiBFbCDDoXJlYSBjb25zdHJ1aWRhIGVzIGVsIGF0cmlidXRvIG3DoXMgZXN0cmVjaGFtZW50ZSB2aW5jdWxhZG8gYWwgcHJlY2lvLCBjb24gdW4gY29lZmljaWVudGUgZGUgMCw4Miwgc2VndWlkYSBkZWwgbsO6bWVybyBkZSBiYcOxb3MgY29uIDAsNzggeSBkZWwgZGUgcGFycXVlYWRlcm9zIGNvbiAwLDY3LiBFbCBuw7ptZXJvIGRlIGhhYml0YWNpb25lcywgZW4gY2FtYmlvLCBhbGNhbnphIMO6bmljYW1lbnRlIDAsNDQuIA0KDQpFbnRyZSBsYXMgdmFyaWFibGVzIHByZWRpY3RvcmFzIHNlIG9ic2VydmEgbGEgYXNvY2lhY2nDs24gZW50cmUgw6FyZWEgY29uc3RydWlkYSB5IG7Dum1lcm8gZGUgYmHDsW9zLCBxdWUgYWxjYW56YSAwLDc5IHNpZW5kbyBsYSBtw6FzIGVsZXZhZGEgZGVsIGNvbmp1bnRvLiBFc3RhIG1hZ25pdHVkIGltcGxpY2EgcXVlIGFtYmFzIGFwb3J0YW4gaW5mb3JtYWNpw7NuIHJlZHVuZGFudGUsIHBvZHJpYSBnZW5lcmFyIG11bHRpY29saW5lYWxpZGFkIHkgY29tcHJvbWV0ZXIgbGEgZXN0YWJpbGlkYWQgZGUgbG9zIGNvZWZpY2llbnRlcy4gRW4gZWwgZXh0cmVtbyBvcHVlc3RvIHNlIHNpdMO6YSBsYSByZWxhY2nDs24gZW50cmUgaGFiaXRhY2lvbmVzIHkgcGFycXVlYWRlcm9zLCBjb24gYXBlbmFzIDAsMjQuDQoNCiMjIFZlcmlmaWNhciBEYXRhc2V0IGxpbXBpbw0KDQpgYGB7cn0NCnZpdmllbmRhX2xpbXBpYSA8LSB2aXZpZW5kYV8wNV9hdGlwaWNvcw0KDQp2aXZpZW5kYV9saW1waWEgPC0gdml2aWVuZGFfbGltcGlhIHw+DQogIG11dGF0ZShiYXJyaW9fbm9ybSA9IGZjdF9pbmZyZXEoYmFycmlvX25vcm0pKQ0KDQpjYXQoIk5pdmVsZXMgZGUgYmFycmlvX25vcm06IiwgbmxldmVscyh2aXZpZW5kYV9saW1waWEkYmFycmlvX25vcm0pKQ0KDQpjYXQoIlJlZ2lzdHJvczoiLCBucm93KHZpdmllbmRhX2xpbXBpYSksICJ8IFZhcmlhYmxlczoiLCBuY29sKHZpdmllbmRhX2xpbXBpYSkpDQpgYGANCmBgYHtyfQ0KdGliYmxlKA0KICBWYXJpYWJsZSA9IG5hbWVzKHZpdmllbmRhX2xpbXBpYSksDQogIFRpcG8gICAgID0gbWFwX2Nocih2aXZpZW5kYV9saW1waWEsIH4gY2xhc3MoLngpWzFdKSwNCiAgQXVzZW50ZXMgPSBtYXBfaW50KHZpdmllbmRhX2xpbXBpYSwgfiBzdW0oaXMubmEoLngpKSkNCikgfD4NCiAgbXV0YXRlKGAlYCA9IHNwcmludGYoIiUuMWYiLCAxMDAgKiBBdXNlbnRlcyAvIG5yb3codml2aWVuZGFfbGltcGlhKSkpIHw+DQogIHRhYmxhX2VzdGlsbygNCiAgICB0aXR1bG8gPSAiVGFibGEgMzAuIEVzdGFkbyBmaW5hbCBkZWwgY29uanVudG8gZGUgZGF0b3MgZGVwdXJhZG8iLA0KICAgIG5vdGEgICA9ICJMYSBiYXNlIGNvbnNlcnZhIGxvcyA4LjMxOSByZWdpc3Ryb3MgcmVzdWx0YW50ZXMgZGUgbGEgZWxpbWluYWNpw7NuIGluaWNpYWwgZGUgZmlsYXMgc2luIGluZm9ybWFjacOzbiBzdXN0YW50aXZhLiINCiAgKQ0KYGBgDQoNCkVsIGNvbmp1bnRvIGRlIGRhdG9zIGRlcHVyYWRvIGNvbnNlcnZhIGxvcyA4LjMxOSByZWdpc3Ryb3MgcmVzdWx0YW50ZXMgZGUgbGEgZWxpbWluYWNpw7NuIGluaWNpYWwgZGUgbGFzIGZpbGFzIHNpbiBpbmZvcm1hY2nDs24gc3VzdGFudGl2YSwgeSBzdW1hIGN1YXRybyB2YXJpYWJsZXMgYSBsYXMgdHJlY2Ugb3JpZ2luYWxlcy4gVHJlcyBkZSBlbGxhcyBwcm92aWVuZW4gZGUgbGEgZGVzYWdyZWdhY2nDs24gZGVsIG7Dum1lcm8gZGUgcGlzbyBzZWfDum4gZWwgdGlwbyBkZSBpbm11ZWJsZSB5IGRlbCBpbmRpY2Fkb3IgcXVlIGRpc3Rpbmd1ZSBsb3MgcmVnaXN0cm9zIGNvbiB5IHNpbiBlc2UgZGF0bzsgbGEgY3VhcnRhIGNvcnJlc3BvbmRlIGEgbGEgdmVyc2nDs24gZGVwdXJhZGEgZGVsIG5vbWJyZSBkZWwgYmFycmlvLCBxdWUgc2UgY29uc2VydmEganVudG8gYSBsYSBvcmlnaW5hbCBjb24gZWwgZmluIGRlIGRvY3VtZW50YXIgZWwgcHVudG8gZGUgcGFydGlkYSB5IHBlcm1pdGlyIGxhIHZlcmlmaWNhY2nDs24gZGUgbGFzIGVxdWl2YWxlbmNpYXMgYXBsaWNhZGFzLg0KDQpUb2RhcyBsYXMgdmFyaWFibGVzIHF1ZWRhcm9uIHRpcGlmaWNhZGFzIGNvbmZvcm1lIGEgc3UgbmF0dXJhbGV6YSBjb25jZXB0dWFsOiBlbCBlc3RyYXRvIGNvbW8gZmFjdG9yIG9yZGVuYWRvLCBsYSB6b25hLCBlbCB0aXBvIGRlIGlubXVlYmxlIHkgZWwgYmFycmlvIGNvbW8gZmFjdG9yZXMgbm9taW5hbGVzLCBlbCBpZGVudGlmaWNhZG9yIGNvbW8gdGV4dG8gcGFyYSBleGNsdWlybG8gZGUgbG9zIGPDoWxjdWxvcyBlc3RhZMOtc3RpY29zLCB5IGxhcyB2YXJpYWJsZXMgZGUgY29udGVvIGNvbW8gZW50ZXJvcy4gTGFzIGF1c2VuY2lhcyByZW1hbmVudGVzIHNlIGNvbmNlbnRyYW4gZW4gbGFzIHZhcmlhYmxlcyBkZXJpdmFkYXMgZGVsIG7Dum1lcm8gZGUgcGlzbyB5IHJlc3BvbmRlbiBlbiBzdSBtYXlvciBwYXJ0ZSBhIGlubXVlYmxlcyBkZWwgdGlwbyBhbCBxdWUgbGEgdmFyaWFibGUgbm8gYXBsaWNhLCBzZWfDum4gc2UgZGV0YWxsw7MgZW4gZWwgYmFsYW5jZSBkZSBjb21wbGV0aXR1ZC4gTGFzIHJlc3RhbnRlcyBjb3JyZXNwb25kZW4gYSBsb3MgcmVnaXN0cm9zIGN1eW9zIHZhbG9yZXMgaW1wb3NpYmxlcyBlbiBoYWJpdGFjaW9uZXMgeSBiYcOxb3MgZnVlcm9uIHJlY29kaWZpY2Fkb3MgY29tbyBkYXRvcyBmYWx0YW50ZXMsIGRlY2lzacOzbiBjb2hlcmVudGUgY29uIGVsIGNyaXRlcmlvIGFkb3B0YWRvIGEgbG8gbGFyZ28gZGUgdG9kbyBlbCBwcmVwcm9jZXNhbWllbnRvOiBwcmVzZXJ2YXIgbGEgZGlzdGluY2nDs24gZW50cmUgbG8gcXVlIHNlIGNvbm9jZSB5IGxvIHF1ZSBubyBmdWUgcmVnaXN0cmFkbywgYW50ZXMgcXVlIGNvbXBsZXRhciBsb3MgdmFjw61vcyBjb24gZXN0aW1hY2lvbmVzIGNhcmVudGVzIGRlIHJlc3BhbGRvIGVuIGxvcyBkYXRvcy4NCg0KYGBge3J9DQpzYXZlUkRTKHZpdmllbmRhX2xpbXBpYSwgInZpdmllbmRhX2xpbXBpYS5yZHMiKQ0KYGBgDQoNCg0KIyMgQW5hbGlzaXMgZGUgQ29tcG9uZW50ZXMgUHJpbmNpcGFsZXMgKFBDQSkNCg0KIyMjIFNlbGVjY2nDs24gZGUgVmFyaWFibGVzDQoNClBhcmEgZWwgUENBIHNlIHNlbGVjY2lvbmFuIGxhcyB2YXJpYWJsZXMgY3VhbnRpdGF0aXZhcyBjb21wbGV0YXMsIGVuIG51ZXN0cm8gY2FzbyDDoXJlYSwgcHJlY2lvLCBoYWJpdGFjaW9uZXMsIGJhw7FvcyB5IHBhcnF1ZWFkZXJvcy4gTGFzIGNvb3JkZW5hZGFzIHF1ZWRhbiBwb3IgZnVlcmEgYWwgcmVwcmVzZW50YXIgbGEgdWJpY2FjacOzbiBkZWwgaW5tdWVibGUgKG5vIHVuIGF0cmlidXRvKS4gTGEgdmFyaWFibGUgcGlzbyBzZSBleGNsdXllIGRlbCBhbmFsaXNpcywgcHVlcyBkYWRvcyBsb3MgdmFsb3JlcyBhdXNlbnRlcyBvYmxpZ2FyaWEgYSBkZXNjYXJ0YXIgMzEuNyUgZGUgbG9zIHJlZ2lzdHJvcy4NCg0KYGBge3J9DQpsaWJyYXJ5KEZhY3RvTWluZVIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQoNCmRhdG9zX2FjcCA8LSB2aXZpZW5kYV9saW1waWEgfD4NCiAgc2VsZWN0KHByZWNpb20sIGFyZWFjb25zdCwgaGFiaXRhY2lvbmVzLCBiYW5pb3MsIHBhcnF1ZWFkZXJvcykgfD4NCiAgZHJvcF9uYSgpDQoNCmNhdCgiUmVnaXN0cm9zIHBhcmEgZWwgQUNQOiIsIG5yb3coZGF0b3NfYWNwKSwNCiAgICAiZGUiLCBucm93KHZpdmllbmRhX2xpbXBpYSksDQogICAgc3ByaW50ZigiKCUuMWYlJSkiLCAxMDAgKiBucm93KGRhdG9zX2FjcCkgLyBucm93KHZpdmllbmRhX2xpbXBpYSkpKQ0KYGBgDQojIyMgRXN0YW5kYXJpemFjacOzbiANCg0KQ29uIGVsIGZpbiBkZSBldml0YXIgcXVlIGxhcyB2YXJpYWJsZXMgcXVlIHRpZW5lIHVuYSBlc2NhbGEgY29uIHZhbG9yZXMgbcOhcyBncmFuZGVzIGFmZWN0ZW4gbGFzIGVzdGltYWNpb25lcyByZWFsaXphZGFzIChzZXNnb3MpIHNlIHJlYWxpemEgbGEgZXN0YW5kYXJpemFjacOzbiBkZSBsYXMgdmFyaWFibGVzIGFudGVzIGRlIHByb2NlZGVyIGEgcmVhbGl6YXIgZWwgcHJvY2VzbyBkZSBlc3RpbWFjacOzbiBkZSBsb3MgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMuIENvbW8gc2UgbWVuY2lvbmNpb27DsyBhbnRlcmlvcm1lbnRlIHV0aWxpemFyZW1vcyBlbCBsb2dhcml0bW8gZGUgcHJlY2lvIHkgYXJlYSBwYXJhIGxvcyBhbsOhbGlzaXMuIA0KDQpgYGB7cn0NCmRhdG9zX2FjcCA8LSB2aXZpZW5kYV9saW1waWEgfD4NCiAgdHJhbnNtdXRlKA0KICAgIGxvZ19wcmVjaW8gICA9IGxvZyhwcmVjaW9tKSwNCiAgICBsb2dfYXJlYSAgICAgPSBsb2coYXJlYWNvbnN0KSwNCiAgICBoYWJpdGFjaW9uZXMsDQogICAgYmFuaW9zLA0KICAgIHBhcnF1ZWFkZXJvcw0KICApIHw+DQogIGRyb3BfbmEoKQ0KDQphY3AgPC0gUENBKGRhdG9zX2FjcCwgc2NhbGUudW5pdCA9IFRSVUUsIGdyYXBoID0gRkFMU0UpDQpgYGANCg0KIyMgUG9yY2VudGFqZSBkZSBWYXJpYW56YSBFeHBsaWNhZGENCg0KRmlndXJhIDguIFBvcmNlbnRhamUgZGUgVmFyaWFuemEgRXhwbGljYWRhDQpgYGB7cn0NCmZ2aXpfZWlnKGFjcCwgYWRkbGFiZWxzID0gVFJVRSwgYmFyZmlsbCA9ICIjMjEzODRhIiwgYmFyY29sb3IgPSAiIzIxMzg0YSIpICsNCiAgbGFicyh0aXRsZSA9IE5VTEwsIHggPSAiQ29tcG9uZW50ZSIsIHkgPSAiJSBkZSB2YXJpYW56YSBleHBsaWNhZGEiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQphY3AkZWlnIHw+IHJvdW5kKDIpDQpgYGANCkVsIHByaW1lciBjb21wb25lbnRlIGNvbmNlbnRyYSBlbCA2NywwJSBkZSBsYSB2YXJpYW56YSB0b3RhbCwgY29uIHVuIGF1dG92YWxvciBkZSAzLDM1IHF1ZSBzdXBlcmEgZWwgdW1icmFsIGNvbnZlbmNpb25hbC4gVW5hIHBhcnRpY2lwYWNpw7NuIGRlIGVzYSBtYWduaXR1ZCBpbmRpY2EgcXVlIGxhcyBjaW5jbyB2YXJpYWJsZXMgY29tcGFydGVuIHVuIGZhY3RvciBjb23Dum4gZG9taW5hbnRlOiBsb3MgaW5tdWVibGVzIGRlIG1heW9yIHN1cGVyZmljaWUgdGllbmRlbiBzaW11bHTDoW5lYW1lbnRlIGEgY29udGFyIGNvbiBtw6FzIGhhYml0YWNpb25lcywgbcOhcyBiYcOxb3MsIG3DoXMgcGFycXVlYWRlcm9zIHkgdW4gcHJlY2lvIHN1cGVyaW9yLCBkZSBtb2RvIHF1ZSBidWVuYSBwYXJ0ZSBkZSBsYSBpbmZvcm1hY2nDs24gY29udGVuaWRhIGVuIGxhcyBjaW5jbyB2YXJpYWJsZXMgb3JpZ2luYWxlcyBwdWVkZSByZXN1bWlyc2UgZW4gdW5hIMO6bmljYSBkaW1lbnNpw7NuIGRlIHRhbWHDsW8geSB2YWxvci4gRWwgc2VndW5kbyBjb21wb25lbnRlIGFwb3J0YSB1biAxNyw3JSBhZGljaW9uYWwgeSBlbGV2YSBsYSB2YXJpYW56YSBhY3VtdWxhZGEgYWwgODQsOCUuIERlIGFjdWVyZG8gYSBlc3RvLCBzZSBvcHRhIHBvciByZXRlbmVyIGRvcyBjb21wb25lbnRlcy4gDQoNCiMjIyBDw61yY3VsbyBkZSBDb3JyZWxhY2lvbmVzDQoNCkZpZ3VyYSA5LiBDw61yY3VsbyBkZSBDb3JyZWxhY2lvbmVzDQpgYGB7cn0NCmFjcCR2YXIkY29vcmQgfD4gcm91bmQoMikNCg0KZnZpel9wY2FfdmFyKGFjcCwgY29sLnZhciA9ICJjb250cmliIiwgcmVwZWwgPSBUUlVFLA0KICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCIjNmI3YTg1IiwgIiNiNTc1MWEiLCAiI2EzM2EzYSIpKSArDQogIGxhYnModGl0bGUgPSBOVUxMKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCkVsIGPDrXJjdWxvIGRlIGNvcnJlbGFjaW9uZXMgcGVybWl0ZSBhdHJpYnVpciB1biBzaWduaWZpY2FkbyBzdXN0YW50aXZvIGEgY2FkYSBjb21wb25lbnRlLiBFbiBsYSBwcmltZXJhIGRpbWVuc2nDs24sIGxhcyBjaW5jbyB2YXJpYWJsZXMgc2Ugb3JpZW50YW4gZW4gZWwgbWlzbW8gc2VudGlkbyB5IGFsY2FuemFuIGxvbmdpdHVkZXMgcHLDs3hpbWFzIGFsIGJvcmRlIGRlbCBjw61yY3Vsbywgc2luIHF1ZSBuaW5ndW5hIHNlIG9wb25nYSBhIGxhcyBkZW3DoXMuIExhIGF1c2VuY2lhIGRlIGNvbnRyYXBvc2ljacOzbiBjb25maXJtYSBxdWUgZXN0ZSBjb21wb25lbnRlIG5vIGRpc3Rpbmd1ZSBlbnRyZSBhdHJpYnV0b3Mgc2lubyBxdWUgbG9zIHNpbnRldGl6YTogbG9zIGlubXVlYmxlcyBzaXR1YWRvcyBoYWNpYSBsYSBkZXJlY2hhIGRlbCBwbGFubyBzb24gc2ltdWx0w6FuZWFtZW50ZSBtw6FzIGFtcGxpb3MsIG3DoXMgY29zdG9zb3MgeSBtZWpvciBkb3RhZG9zIGVuIGhhYml0YWNpb25lcywgYmHDsW9zIHkgcGFycXVlYWRlcm9zLiBMYXMgY29udHJpYnVjaW9uZXMgaW5kaXZpZHVhbGVzIG9zY2lsYW4gYWRlbcOhcyBlbiB1biByYW5nbyBtdXkgZXN0cmVjaG8sIGVudHJlIGVsIDE5LDAlIHkgZWwgMjEsMCUsIGRlIG1hbmVyYSBxdWUgbmluZ3VuYSB2YXJpYWJsZSBkb21pbmEgbGEgY29uc3RydWNjacOzbiBkZWwgZWplIHkgdG9kYXMgcGFydGljaXBhbiBkZSBmb3JtYSBlcXVpbGlicmFkYS4gRWwgY29tcG9uZW50ZSBwdWVkZSBpbnRlcnByZXRhcnNlLCBlbiBjb25zZWN1ZW5jaWEsIGNvbW8gdW5hIGRpbWVuc2nDs24gZ2VuZXJhbCBkZSB0YW1hw7FvIHkgdmFsb3IgZGVsIGlubXVlYmxlLg0KDQpMYSBzZWd1bmRhIGRpbWVuc2nDs24gcmV2ZWxhIHVuYSBlc3RydWN0dXJhIGRpc3RpbnRhLCBwdWVzIGFxdcOtIHPDrSBzZSBwcm9kdWNlIHVuYSBvcG9zaWNpw7NuIGVudHJlIHZhcmlhYmxlcy4gRWwgbsO6bWVybyBkZSBoYWJpdGFjaW9uZXMgc2UgcHJveWVjdGEgY29uIGZ1ZXJ6YSBoYWNpYSBlbCBleHRyZW1vIHBvc2l0aXZvLCBtaWVudHJhcyBlbCBwcmVjaW8geSBlbCBuw7ptZXJvIGRlIHBhcnF1ZWFkZXJvcyBsbyBoYWNlbiBoYWNpYSBlbCBuZWdhdGl2bzsgZWwgw6FyZWEgY29uc3RydWlkYSB5IGVsIG7Dum1lcm8gZGUgYmHDsW9zIHBlcm1hbmVjZW4gcHLDs3hpbW9zIGFsIGVqZSBob3Jpem9udGFsLCBzaW4gcGFydGljaXBhY2nDs24gYXByZWNpYWJsZSBlbiBlc3RlIGNvbXBvbmVudGUuIA0KDQojIyBQcm95ZWNjacOzbiBkZSBsb3MgaW5kaXZpZHVvcw0KDQpVbmEgdmV6IGludGVycHJldGFkb3MgbG9zIGVqZXMgYSBwYXJ0aXIgZGVsIGPDrXJjdWxvIGRlIGNvcnJlbGFjaW9uZXMsIHNlIHByb3llY3Rhcm9uIGxvcyBpbm11ZWJsZXMgc29icmUgZWwgcGxhbm8gZmFjdG9yaWFsIGRpZmVyZW5jaWFuZG8gY2FzYXMgZGUgYXBhcnRhbWVudG9zLiBMYSBwb3NpY2nDs24gaG9yaXpvbnRhbCBkZSBjYWRhIHB1bnRvIGV4cHJlc2Egc3UgdmFsb3IgZW4gbGEgZGltZW5zacOzbiBkZSB0YW1hw7FvIHkgcHJlY2lvLCBtaWVudHJhcyBxdWUgbGEgdmVydGljYWwgcmVmbGVqYSBsYSByZWxhY2nDs24gZW50cmUgZWwgbsO6bWVybyBkZSBoYWJpdGFjaW9uZXMgeSBlbCBuaXZlbCBkZSBwcmVjaW8geSBkb3RhY2nDs24uIFNlIGluY29ycG9yYXJvbiBlbGlwc2VzIGRlIGNvbmZpYW56YSBhbCA5NSUgY29uIGVsIGZpbiBkZSByZXN1bWlyIGxhIHViaWNhY2nDs24gZGUgY2FkYSBncnVwbywgcmVjdXJzbyBuZWNlc2FyaW8gZGFkbyBxdWUgbGEgc3VwZXJwb3NpY2nDs24gZGUgbcOhcyBkZSBvY2hvIG1pbCBwdW50b3MgaW1waWRlIGFwcmVjaWFyIGxhcyBjb25jZW50cmFjaW9uZXMgYSBzaW1wbGUgdmlzdGEuDQoNCkZpZ3VyYSAxMC4gUHJveWVjY2nDs24gZGUgbG9zIGluZGl2aWR1b3MNCmBgYHtyfQ0KYmFzZV9hY3AgPC0gdml2aWVuZGFfbGltcGlhIHw+DQogIGRyb3BfbmEocHJlY2lvbSwgYXJlYWNvbnN0LCBoYWJpdGFjaW9uZXMsIGJhbmlvcywgcGFycXVlYWRlcm9zKQ0KDQpmdml6X3BjYV9pbmQoYWNwLCBnZW9tID0gInBvaW50IiwgcG9pbnRzaXplID0gLjYsIGFscGhhLmluZCA9IC4xNSwNCiAgICAgICAgICAgICBoYWJpbGxhZ2UgPSBiYXNlX2FjcCR0aXBvLA0KICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjMjEzODRhIiwgIiNiNTc1MWEiKSwNCiAgICAgICAgICAgICBhZGRFbGxpcHNlcyA9IFRSVUUsIGVsbGlwc2UubGV2ZWwgPSAuOTUpICsNCiAgbGFicyh0aXRsZSA9IE5VTEwpICsgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KTGEgcHJveWVjY2nDs24gcmV2ZWxhIGRvcyBkaWZlcmVuY2lhcyBlbnRyZSBhbWJvcyB0aXBvcyBkZSBpbm11ZWJsZS4gTGEgcHJpbWVyYSBlcyBkZSBwb3NpY2nDs246IGxhcyBjYXNhcyBzZSBkZXNwbGF6YW4gaGFjaWEgbGEgZGVyZWNoYSBkZWwgcGxhbm8sIHJlc3VsdGFkbyBlc3BlcmFibGUgZGFkbyBxdWUgbGEgZGltZW5zacOzbiBob3Jpem9udGFsIGV4cHJlc2EgdGFtYcOxbyB5IHZhbG9yLCBwZXJvIHRhbWJpw6luIGhhY2lhIGxhIHBhcnRlIHN1cGVyaW9yLCBsbyBxdWUgaW5kaWNhIHF1ZSBvZnJlY2VuIHVuIG7Dum1lcm8gZGUgaGFiaXRhY2lvbmVzIG3DoXMgZWxldmFkbyBlbiByZWxhY2nDs24gY29uIHN1IHByZWNpbyB5IHN1IGRvdGFjacOzbiBkZSBwYXJxdWVhZGVyb3MuIExhIHNlZ3VuZGEgZXMgZGUgZGlzcGVyc2nDs24sIGxhIGVsaXBzZSBjb3JyZXNwb25kaWVudGUgYSBsb3MgYXBhcnRhbWVudG9zIGVzIGVzdHJlY2hhIHkgbWFyY2FkYW1lbnRlIGFsYXJnYWRhLCBtaWVudHJhcyBxdWUgbGEgZGUgbGFzIGNhc2FzIG9jdXBhIHVuYSBzdXBlcmZpY2llIGNvbnNpZGVyYWJsZW1lbnRlIG1heW9yLiBMb3MgYXBhcnRhbWVudG9zIGNvbnN0aXR1eWVuIGFzw60gdW4gcHJvZHVjdG8gaG9tb2fDqW5lbywgY29uIGNvbmZpZ3VyYWNpb25lcyBxdWUgc2UgcmVwaXRlbiBkZW50cm8gZGUgbcOhcmdlbmVzIGVzdHJlY2hvcywgZW4gdGFudG8gcXVlIGxhcyBjYXNhcyBhZG1pdGVuIGNvbWJpbmFjaW9uZXMgbXVjaG8gbcOhcyBkaXZlcnNhcyBkZSB0YW1hw7FvLCBkaXN0cmlidWNpw7NuIGludGVybmEgeSBwcmVjaW8NCg0KIyMjIEFuw6FsaXNpcyBwb3IgRXN0cmF0bw0KDQpMYSBtaXNtYSBwcm95ZWNjacOzbiBzZSByZXBsaWPDsyBkaWZlcmVuY2lhbmRvIGxvcyBpbm11ZWJsZXMgc2Vnw7puIHN1IGVzdHJhdG8gc29jaW9lY29uw7NtaWNvLCBjb24gZWwgcHJvcMOzc2l0byBkZSB2ZXJpZmljYXIgc2kgZXN0YSBjbGFzaWZpY2FjacOzbiBhZG1pbmlzdHJhdGl2YSBzZSBjb3JyZXNwb25kZSBjb24gY29uZmlndXJhY2lvbmVzIGRpc3RpbnRhcyBkZWwgcHJvZHVjdG8gaW5tb2JpbGlhcmlvIG8gc2kgb3BlcmEgw7puaWNhbWVudGUgY29tbyB1biBpbmRpY2Fkb3IgZGUgdWJpY2FjacOzbi4NCg0KRmlndXJhIDExLiBBbsOhbGlzaXMgcG9yIEVzdHJhdG8NCmBgYHtyfQ0KZnZpel9wY2FfaW5kKGFjcCwgZ2VvbSA9ICJwb2ludCIsIHBvaW50c2l6ZSA9IC42LCBhbHBoYS5pbmQgPSAuMTIsDQogICAgICAgICAgICAgaGFiaWxsYWdlID0gYmFzZV9hY3AkZXN0cmF0bywNCiAgICAgICAgICAgICBwYWxldHRlID0gYygiIzZiN2E4NSIsICIjYjU3NTFhIiwgIiMwZjc2NmUiLCAiIzIxMzg0YSIpLA0KICAgICAgICAgICAgIGFkZEVsbGlwc2VzID0gVFJVRSwgZWxsaXBzZS5sZXZlbCA9IC45NSkgKw0KICBsYWJzKHRpdGxlID0gTlVMTCkgKyB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpMb3MgY3VhdHJvIGVzdHJhdG9zIHNlIG9yZGVuYW4gYSBsbyBsYXJnbyBkZWwgZWplIGhvcml6b250YWwgZW4gZWwgc2VudGlkbyBlc3BlcmFkbzogZWwgY2VudHJvIGRlIGNhZGEgZWxpcHNlIHNlIGRlc3BsYXphIHByb2dyZXNpdmFtZW50ZSBoYWNpYSBsYSBkZXJlY2hhIGNvbmZvcm1lIGF1bWVudGEgZWwgbml2ZWwsIGRlIG1hbmVyYSBxdWUgbG9zIGlubXVlYmxlcyBkZSBlc3RyYXRvIDYgcHJlc2VudGFuIGVuIHByb21lZGlvIG1heW9yIHRhbWHDsW8sIHByZWNpbyB5IGRvdGFjacOzbiBxdWUgbG9zIGRlIGVzdHJhdG8gMy4gTGEgcmVsYWNpw7NuIGVudHJlIGVzdHJhdG8geSB2YWxvciBkZWwgaW5tdWVibGUgcXVlZGEgcG9yIHRhbnRvIGNvbmZpcm1hZGEuIFBvciBvdHJvIGxhZG8sICBsYXMgY3VhdHJvIGVsaXBzZXMgY29tcGFydGVuIGxhIG1heW9yIHBhcnRlIGRlIHN1IHN1cGVyZmljaWUsIHkgbGEgcmVnacOzbiBjZW50cmFsIGRlbCBwbGFubyByZcO6bmUgaW5tdWVibGVzIGRlIHRvZG9zIGxvcyBlc3RyYXRvcyBzaW4gcXVlIG5pbmd1bm8gcHJlZG9taW5lLiBMYSBlc3BlY2lhbGl6YWNpw7NuIHNvbG8gYXBhcmVjZSBlbiBsb3MgZXh0cmVtb3MsIGNvbiBhbGd1bm9zIGlubXVlYmxlcyBkZSBlc3RyYXRvIDMgZW4gZWwgYm9yZGUgaXpxdWllcmRvIHkgZGUgZXN0cmF0byA2IGVuIGVsIGRlcmVjaG8uDQoNCiMjIEFuw6FsaXNpcyBkZSBDb25nbG9tZXJhZG9zDQoNCkxhIHNlZ21lbnRhY2nDs24gZGUgbG9zIGlubXVlYmxlcyBzZSByZWFsaXrDsyBtZWRpYW50ZSBlbCBhbGdvcml0bW8gZGUgay1tZWFucyBhcGxpY2FkbyBzb2JyZSBsYXMgY29vcmRlbmFkYXMgZGUgbG9zIGRvcyBwcmltZXJvcyBjb21wb25lbnRlcyBwcmluY2lwYWxlcywgZW4gbHVnYXIgZGUgc29icmUgbGFzIHZhcmlhYmxlcyBvcmlnaW5hbGVzLkxhIGRldGVybWluYWNpw7NuIGRlbCBuw7ptZXJvIGRlIGNvbmdsb21lcmFkb3Mgc2UgYXBvecOzIGVuIGRvcyBjcml0ZXJpb3MsIGxhIHN1bWEgZGUgY3VhZHJhZG9zIGludHJhZ3J1cG8sIHF1ZSBldmFsw7phIGxhIGhvbW9nZW5laWRhZCBpbnRlcm5hLCB5IGVsIGFuY2hvIGRlIHNpbHVldGEgcHJvbWVkaW8sIHF1ZSBtaWRlIGVsIGdyYWRvIGRlIHNlcGFyYWNpw7NuIGVudHJlIGdydXBvcy4NCg0KRmlndXJhIDEyLiBTdW1hIGRlIGN1YWRyYWRvcyBJbnRyYWdydXBvcyB5IEFuY2hvIGRlIFNpbHVldGEgcHJvbWVkaW8NCmBgYHtyfQ0KYmFzZV9hY3AgPC0gYmFzZV9hY3AgfD4NCiAgbXV0YXRlKGRpbTEgPSBhY3AkaW5kJGNvb3JkWywgMV0sDQogICAgICAgICBkaW0yID0gYWNwJGluZCRjb29yZFssIDJdKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShjbHVzdGVyKQ0KDQpzZXQuc2VlZCgxMjMpDQpkYXRvc19jbCA8LSBiYXNlX2FjcCB8PiBzZWxlY3QoZGltMSwgZGltMikNCg0KZnZpel9uYmNsdXN0KGRhdG9zX2NsLCBrbWVhbnMsIG1ldGhvZCA9ICJ3c3MiLCBrLm1heCA9IDEwKSArDQogIGxhYnModGl0bGUgPSBOVUxMLCB4ID0gIk7Dum1lcm8gZGUgY29uZ2xvbWVyYWRvcyIsIHkgPSAiU3VtYSBkZSBjdWFkcmFkb3MgaW50cmFncnVwbyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmZ2aXpfbmJjbHVzdChkYXRvc19jbCwga21lYW5zLCBtZXRob2QgPSAic2lsaG91ZXR0ZSIsIGsubWF4ID0gMTApICsNCiAgbGFicyh0aXRsZSA9IE5VTEwsIHggPSAiTsO6bWVybyBkZSBjb25nbG9tZXJhZG9zIiwgeSA9ICJBbmNobyBkZSBzaWx1ZXRhIHByb21lZGlvIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpMYSBjdXJ2YSBkZSBzdW1hIGRlIGN1YWRyYWRvcyBpbnRyYWdydXBvIGRlc2NpZW5kZSBkZSBtYW5lcmEgcHJvbnVuY2lhZGEgaGFzdGEgZWwgY3VhcnRvIGNvbmdsb21lcmFkbyB5IHNlIGFwbGFuYSB2aXNpYmxlbWVudGUgYSBwYXJ0aXIgZGUgZXNlIHB1bnRvLCBzaXR1YW5kbyBlbCBjb2RvIGVudHJlIHRyZXMgeSBjdWF0cm8gZ3J1cG9zLiBFbCBhbmNobyBkZSBzaWx1ZXRhLCBwb3Igc3UgcGFydGUsIGFsY2FuemEgc3UgdmFsb3IgbcOheGltbyBjb24gZG9zIGNvbmdsb21lcmFkb3MgKDAsNDkpIHkgZGVzY2llbmRlIGNvbiB0cmVzLCBwYXJhIHJlcHVudGFyIGRlIGZvcm1hIGFwcmVjaWFibGUgZW4gY3VhdHJvICgwLDQzNSkgeSBkZXRlcmlvcmFyc2UgZGUgbWFuZXJhIHNvc3RlbmlkYSBhIHBhcnRpciBkZSBhaMOtLiBFc2UgbcOheGltbyBsb2NhbCByZXN1bHRhIHNpZ25pZmljYXRpdm8sIHB1ZXMgaW5kaWNhIHF1ZSBsYSBlc3RydWN0dXJhIGRlIGN1YXRybyBncnVwb3Mgbm8gZXMgYXJiaXRyYXJpYSBzaW5vIHF1ZSByZXNwb25kZSBhIHVuYSBvcmdhbml6YWNpw7NuIGVmZWN0aXZhbWVudGUgcHJlc2VudGUgZW4gbG9zIGRhdG9zLg0KDQojIyMgQWdydXBhbWllbnRvIA0KDQpGaWd1cmEgMTMuIEFncnVwYW1pZW50bw0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQprbSA8LSBrbWVhbnMoZGF0b3NfY2wsIGNlbnRlcnMgPSA0LCBuc3RhcnQgPSAyNSkNCg0KYmFzZV9hY3AgPC0gYmFzZV9hY3AgfD4gbXV0YXRlKGNsdXN0ZXIgPSBmYWN0b3Ioa20kY2x1c3RlcikpDQoNCmZ2aXpfY2x1c3RlcihrbSwgZGF0YSA9IGRhdG9zX2NsLCBnZW9tID0gInBvaW50IiwgcG9pbnRzaXplID0gLjUsDQogICAgICAgICAgICAgYWxwaGEgPSAuMTUsIGVsbGlwc2UudHlwZSA9ICJub3JtIiwNCiAgICAgICAgICAgICBwYWxldHRlID0gYygiIzIxMzg0YSIsICIjYjU3NTFhIiwgIiMwZjc2NmUiLCAiI2EzM2EzYSIpKSArDQogIGxhYnModGl0bGUgPSBOVUxMKSArIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpgYGB7cn0NCmJhc2VfYWNwIHw+DQogIGdyb3VwX2J5KGNsdXN0ZXIpIHw+DQogIHN1bW1hcmlzZSgNCiAgICBuICAgICAgICAgICAgICAgICA9IG4oKSwNCiAgICBgJWAgICAgICAgICAgICAgICA9IHNwcmludGYoIiUuMWYiLCAxMDAgKiBuKCkgLyBucm93KGJhc2VfYWNwKSksDQogICAgYFByZWNpbyBtZWRpYW5vYCAgPSBtZWRpYW4ocHJlY2lvbSksDQogICAgYMOBcmVhIG1lZGlhbmFgICAgID0gbWVkaWFuKGFyZWFjb25zdCksDQogICAgSGFiaXRhY2lvbmVzICAgICAgPSBtZWRpYW4oaGFiaXRhY2lvbmVzKSwNCiAgICBgQmHDsW9zYCAgICAgICAgICAgPSBtZWRpYW4oYmFuaW9zKSwNCiAgICBQYXJxdWVhZGVyb3MgICAgICA9IG1lZGlhbihwYXJxdWVhZGVyb3MpLA0KICAgIGAlIGNhc2FzYCAgICAgICAgID0gc3ByaW50ZigiJS4xZiIsIDEwMCAqIG1lYW4odGlwbyA9PSAiQ2FzYSIpKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgfD4NCiAgdGFibGFfZXN0aWxvKA0KICAgIHRpdHVsbyA9ICJUYWJsYSAzMS4gQ2FyYWN0ZXJpemFjacOzbiBkZSBsb3MgY29uZ2xvbWVyYWRvcyIsDQogICAgbm90YSAgID0gIkxvcyBjb25nbG9tZXJhZG9zIHNlIGNvbnN0cnV5ZXJvbiBzb2JyZSBsYXMgZG9zIHByaW1lcmFzIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzLCBxdWUgc2ludGV0aXphbiBlbCA4NCw4JSBkZSBsYSB2YXJpYWJpbGlkYWQgZGUgbGFzIHZhcmlhYmxlcyBjdWFudGl0YXRpdmFzLiINCiAgKQ0KYGBgDQoNCkxhIGRpdmlzacOzbiBlbiBjdWF0cm8gY29uZ2xvbWVyYWRvcyBwcm9kdWNlIHNlZ21lbnRvcyBkaWZlcmVuY2lhZG9zIHkgc3VzY2VwdGlibGVzIGRlIGNhcmFjdGVyaXphY2nDs24uIEVsIG3DoXMgbnVtZXJvc28gcmXDum5lIGVsIDQyLDUlIGRlIGxhIG9mZXJ0YSB5IGNvcnJlc3BvbmRlIGEgdml2aWVuZGEgY29tcGFjdGEgZGUgZ2FtYSBlY29uw7NtaWNhOiA3NSBtZXRyb3MgY3VhZHJhZG9zIGRlIHN1cGVyZmljaWUgbWVkaWFuYSwgMTk5IG1pbGxvbmVzIGRlIHByZWNpbywgdHJlcyBoYWJpdGFjaW9uZXMgeSB1biBzb2xvIGJhw7FvIHkgcGFycXVlYWRlcm8sIGNvbiBwcmVkb21pbmlvIG1hcmNhZG8gZGUgYXBhcnRhbWVudG9zLCBxdWUgcmVwcmVzZW50YW4gZWwgODYlIGRlbCBncnVwby4gDQoNClVuIHNlZ3VuZG8gY29uZ2xvbWVyYWRvIGFncnVwYSBlbCAzMyw5JSB5IGNvbnN0aXR1eWUgZWwgc2VnbWVudG8gaW50ZXJtZWRpbyBkZWwgbWVyY2FkbywgY29uIDE1MCBtZXRyb3MgY3VhZHJhZG9zIHkgNDIwIG1pbGxvbmVzOyBzdSBjb21wb3NpY2nDs24gbWl4dGEgKDQwJSBjYXNhcyB5IDYwJSBhcGFydGFtZW50b3MpIGxvIGlkZW50aWZpY2EgY29tbyBsYSBmcmFuamEgZG9uZGUgYW1ib3MgdGlwb3MgZGUgaW5tdWVibGUgY29tcGl0ZW4gZGlyZWN0YW1lbnRlIHBvciBlbCBtaXNtbyBjb21wcmFkb3IsIGNvbmZpcm1hbmRvIGVsIHNvbGFwYW1pZW50byBhZHZlcnRpZG8gZW4gbGEgcHJveWVjY2nDs24gZmFjdG9yaWFsLg0KDQpFbCB0ZXJjZXJvLCBxdWUgYWdydXBhIGVsIDEwLDElIGRlIGxvcyBpbm11ZWJsZXMgeSBlc3TDoSBjb21wdWVzdG8gY2FzaSBleGNsdXNpdmFtZW50ZSBwb3IgY2FzYXMgKDk3LDMlKSwgcHJlc2VudGEgMzAwIG1ldHJvcyBjdWFkcmFkb3MgZGUgc3VwZXJmaWNpZSB5IHNlaXMgaGFiaXRhY2lvbmVzLCBwZXJvIHVuIHByZWNpbyBtZWRpYW5vIGRlIDQzMCBtaWxsb25lcywgcHLDoWN0aWNhbWVudGUgaWTDqW50aWNvIGFsIGRlbCBzZWdtZW50byBpbnRlcm1lZGlvIHBlc2UgYSBkdXBsaWNhciBzdSDDoXJlYS4gDQoNCkVsIGN1YXJ0bywgY29uIGVsIDEzLDYlIGRlIGxhIG9mZXJ0YSwgYWxjYW56YSBsb3MgOTYwIG1pbGxvbmVzIHNvYnJlIHVuYSBzdXBlcmZpY2llIGRlIDMyMiBtZXRyb3MgY3VhZHJhZG9zLCBhcGVuYXMgc3VwZXJpb3IgYSBsYSBkZWwgYW50ZXJpb3IuIERvcyBzZWdtZW50b3MgZGUgdGFtYcOxbyBlcXVpdmFsZW50ZSBwcmVzZW50YW4gYXPDrSBwcmVjaW9zIHF1ZSBkaWZpZXJlbiBlbiBtw6FzIGRlbCBkb2JsZS4NCg0KIyMgQW7DoWxpc2lzIGRlIENvcnJlc3BvbmRlbmNpYQ0KDQojIyMgWm9uYXMgcG9yIFRpcG8NCg0KYGBge3J9DQp0YWJsYV96dCA8LSB0YWJsZSh2aXZpZW5kYV9saW1waWEkem9uYSwgdml2aWVuZGFfbGltcGlhJHRpcG8pDQp0YWJsYV96dA0KDQpjaGlzcS50ZXN0KHRhYmxhX3p0KQ0KDQpyb3VuZChjaGlzcS50ZXN0KHRhYmxhX3p0KSRyZXNpZHVhbHMsIDIpDQpgYGANCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoRmFjdG9NaW5lUikNCmxpYnJhcnkoZmFjdG9leHRyYSkNCg0KYmFycmlvc190b3AgPC0gdml2aWVuZGFfbGltcGlhIHw+DQogIGNvdW50KGJhcnJpb19ub3JtLCBzb3J0ID0gVFJVRSkgfD4NCiAgZmlsdGVyKG4gPj0gNTApIHw+DQogIHB1bGwoYmFycmlvX25vcm0pDQoNCnRhYmxhX2J0IDwtIHZpdmllbmRhX2xpbXBpYSB8Pg0KICBmaWx0ZXIoYmFycmlvX25vcm0gJWluJSBiYXJyaW9zX3RvcCkgfD4NCiAgY291bnQoYmFycmlvX25vcm0sIHRpcG8pIHw+DQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB0aXBvLCB2YWx1ZXNfZnJvbSA9IG4sIHZhbHVlc19maWxsID0gMCkgfD4NCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJiYXJyaW9fbm9ybSIpDQoNCmNhdCgiQmFycmlvcyBpbmNsdWlkb3M6IiwgbnJvdyh0YWJsYV9idCksDQogICAgInwgUmVnaXN0cm9zOiIsIHN1bSh0YWJsYV9idCksDQogICAgc3ByaW50ZigiKCUuMWYlJSBkZSBsYSBiYXNlKSIsIDEwMCAqIHN1bSh0YWJsYV9idCkgLyBucm93KHZpdmllbmRhX2xpbXBpYSkpKQ0KYGBgDQoNCiMjIyBCYXJyaW8gcG9yIENvbmdsb21lcmFkbyAtIEJpcGxvdA0KDQpQYXJhIGV4YW1pbmFyIGxhIHJlbGFjacOzbiBlbnRyZSBsb3MgYmFycmlvcyB5IGxvcyBzZWdtZW50b3MgZGUgbWVyY2FkbyBpZGVudGlmaWNhZG9zIHNlIGFwbGljw7MgdW4gYW7DoWxpc2lzIGRlIGNvcnJlc3BvbmRlbmNpYXMgc2ltcGxlIHNvYnJlIGxhIHRhYmxhIGRlIGNvbnRpbmdlbmNpYSBxdWUgY3J1emEgYW1iYXMgdmFyaWFibGVzLiBFc3RlIG3DqXRvZG8gcHJveWVjdGEgZmlsYXMgeSBjb2x1bW5hcyBlbiB1biBlc3BhY2lvIGNvbcO6biBkb25kZSBsYSBwcm94aW1pZGFkIGVudHJlIHB1bnRvcyBleHByZXNhIGFzb2NpYWNpw7NuIHJlbGF0aXZhOiB1biBiYXJyaW8gc2Ugc2l0w7phIGNlcmNhIGRlIHVuIGNvbmdsb21lcmFkbyBjdWFuZG8gbGEgcHJlc2VuY2lhIGRlIGVzZSBzZWdtZW50byBlbiBzdSBvZmVydGEgc3VwZXJhIGxhIHF1ZSBjYWJyw61hIGVzcGVyYXIgYmFqbyBpbmRlcGVuZGVuY2lhLiBMbyBxdWUgc2UgcmVwcmVzZW50YSBzb24gYWZpbmlkYWRlcyB5IG5vIGZyZWN1ZW5jaWFzIGFic29sdXRhcywgZGUgbW9kbyBxdWUgdW4gYmFycmlvIHBlcXVlw7FvIHB1ZWRlIGFwYXJlY2VyIGZ1ZXJ0ZW1lbnRlIGFzb2NpYWRvIGEgdW4gc2VnbWVudG8gc2kgZXN0ZSBkb21pbmEgc3Ugb2ZlcnRhLiBFbCBhbsOhbGlzaXMgc2UgcmVzdHJpbmdpw7MgYSBsb3MgYmFycmlvcyBjb24gYWwgbWVub3MgY2luY3VlbnRhIGlubXVlYmxlcyByZWdpc3RyYWRvcywgZGFkbyBxdWUgZW4gYXF1ZWxsb3MgY29uIHBvY29zIHJlZ2lzdHJvcyBsYSBjb21wb3NpY2nDs24gcG9yIHNlZ21lbnRvcyByZXN1bHRhIGRlbWFzaWFkbyBpbmVzdGFibGUgcGFyYSBhZG1pdGlyIGludGVycHJldGFjacOzbi4NCg0KRmlndXJhIDE0LiBCaXBsb3QgQmFycmlvIHBvciBDb25nbG9tZXJhZG8NCg0KYGBge3J9DQp0YWJsYV9iYyA8LSBiYXNlX2FjcCB8Pg0KICBmaWx0ZXIoYmFycmlvX25vcm0gJWluJSBiYXJyaW9zX3RvcCkgfD4NCiAgY291bnQoYmFycmlvX25vcm0sIGNsdXN0ZXIpIHw+DQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjbHVzdGVyLCB2YWx1ZXNfZnJvbSA9IG4sIHZhbHVlc19maWxsID0gMCkgfD4NCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJiYXJyaW9fbm9ybSIpDQoNCmNhX2JjIDwtIENBKHRhYmxhX2JjLCBncmFwaCA9IEZBTFNFKQ0KDQpmdml6X2NhX2JpcGxvdChjYV9iYywgcmVwZWwgPSBUUlVFLCBjb2wucm93ID0gIiM2YjdhODUiLCBjb2wuY29sID0gIiNhMzNhM2EiKSArDQogIGxhYnModGl0bGUgPSBOVUxMKSArIHRoZW1lX21pbmltYWwoKQ0KDQpjYV9iYyRlaWcgfD4gcm91bmQoMikNCmBgYA0KDQpMYXMgZG9zIHByaW1lcmFzIGRpbWVuc2lvbmVzIHJlY29nZW4gZWwgOTIsNiUgZGUgbGEgYXNvY2lhY2nDs24gZW50cmUgYmFycmlvcyB5IHNlZ21lbnRvcywgcG9yIGxvIHF1ZSBlbCBwbGFubyBvZnJlY2UgdW5hIHJlcHJlc2VudGFjacOzbiBwcsOhY3RpY2FtZW50ZSBjb21wbGV0YSBkZSBsYSBlc3RydWN0dXJhLiBMYSBwcmltZXJhIGRpbWVuc2nDs24gY29uY2VudHJhIHBvciBzw60gc29sYSBlbCA3NCw5JSB5IG9wb25lIGVsIGNvbmdsb21lcmFkbyBlY29uw7NtaWNvIGNvbXBhY3RvIGFsIGRlIGFsdGEgZ2FtYSwgY29uZmlndXLDoW5kb3NlIGNvbW8gdW4gZWplIGRlIHZhbG9yOiBsYSBkaWZlcmVuY2lhIGZ1bmRhbWVudGFsIGVudHJlIGxvcyBiYXJyaW9zIGRlIGxhIGNpdWRhZCByZXNpZGUgZW4gZWwgbml2ZWwgZGUgcHJlY2lvIGRlIGxhIHZpdmllbmRhIHF1ZSBvZnJlY2VuLiBFbiBzdSBleHRyZW1vIGRlcmVjaG8gc2UgdWJpY2FuIFBhbmNlLCBQYXJjZWxhY2lvbmVzIFBhbmNlLCBDaXVkYWQgSmFyZMOtbiwgU2FudGEgVGVyZXNpdGEgeSBOb3JtYW5kw61hLCBzZWN0b3JlcyBkb25kZSBwcmVkb21pbmEgbGEgb2ZlcnRhIGRlIG1heW9yIHZhbG9yOyBlbiBlbCBvcHVlc3RvIHNlIGFncnVwYW4gVG9ycmVzIGRlIENvbWZhbmRpLCBCcmlzYXMgZGUgbG9zIMOBbGFtb3MsIFZpbGxhIGRlbCBQcmFkbywgVmFsbGUgZGVsIExpbGkgeSBNZWzDqW5kZXosIGFzb2NpYWRvcyBhIGxhIHZpdmllbmRhIGNvbXBhY3RhIGRlIGdhbWEgZWNvbsOzbWljYS4NCg0KTGEgc2VndW5kYSBkaW1lbnNpw7NuLCBjb24gZWwgMTcsNyUgZGUgbGEgYXNvY2lhY2nDs24sIHNpdMO6YSBlbiBzdSBleHRyZW1vIHN1cGVyaW9yIGFsIGNvbmdsb21lcmFkbyBkZSBjYXNhcyBhbXBsaWFzIGNvbiBwcmVjaW8gbW9kZXJhZG8geSwgZW4gdG9ybm8gYSDDqWwsIGEgU2FuIEZlcm5hbmRvLCBUZXF1ZW5kYW1hLCBFbCBCb3NxdWUsIFNhbnRhIElzYWJlbCB5IENpdWRhZCAyMDAwLiBTZSB0cmF0YSBkZSBiYXJyaW9zIHRyYWRpY2lvbmFsZXMgZGUgbGEgY2l1ZGFkLCBjYXJhY3Rlcml6YWRvcyBwb3Igdml2aWVuZGFzIHVuaWZhbWlsaWFyZXMgZGUgc3VwZXJmaWNpZSBjb25zaWRlcmFibGUgeSB1biBuw7ptZXJvIGVsZXZhZG8gZGUgaGFiaXRhY2lvbmVzLCBjdXlvIHByZWNpbyBubyBzZSBjb3JyZXNwb25kZSBjb24gZXNhcyBkaW1lbnNpb25lcy4gRXN0ZSBlamUgbm8gZGlzdGluZ3VlIHBvciBuaXZlbCBkZSB2YWxvciBzaW5vIHBvciBjb25maWd1cmFjacOzbiBkZWwgZXNwYWNpbywgeSBjb25maXJtYSBlbiBlbCBwbGFubyB0ZXJyaXRvcmlhbCBsYSBkaW1lbnNpw7NuIHF1ZSBlbCBhbsOhbGlzaXMgZGUgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgaGFiw61hIGlkZW50aWZpY2FkbyBlbiBsYXMgdmFyaWFibGVzIGN1YW50aXRhdGl2YXMuTGEgcmVnacOzbiBjZW50cmFsIGRlbCBncsOhZmljbyByZcO6bmUgYWwgc2VnbWVudG8gaW50ZXJtZWRpbyBqdW50byBhIGJhcnJpb3MgY29tbyBMb3MgQ3Jpc3RhbGVzLCBMYSBGbG9yYSwgRWwgSW5nZW5pbyB5IFZlcnNhbGxlcywgY3V5YSBvZmVydGEgbm8gcHJlc2VudGEgZXNwZWNpYWxpemFjacOzbiBhcHJlY2lhYmxlIGhhY2lhIG5pbmd1bm8gZGUgbG9zIHNlZ21lbnRvcy4NCg0KDQojIyBWaXN1YWxpemFjacOzbiBkZSBSZXN1bHRhZG9zDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQptYXBhX2RhdG9zIDwtIGJhc2VfYWNwIHw+DQogIGdyb3VwX2J5KGxvbmdpdHVkLCBsYXRpdHVkKSB8Pg0KICBmaWx0ZXIobl9kaXN0aW5jdChiYXJyaW9fbm9ybSkgPCA1KSB8Pg0KICB1bmdyb3VwKCkNCg0KY2F0KCJSZWdpc3Ryb3MgbWFwZWFibGVzOiIsIG5yb3cobWFwYV9kYXRvcyksICJkZSIsIG5yb3coYmFzZV9hY3ApKQ0KDQpnZ3Bsb3QobWFwYV9kYXRvcywgYWVzKGxvbmdpdHVkLCBsYXRpdHVkLCBjb2xvciA9IGNsdXN0ZXIpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAuMzUsIHNpemUgPSAuNykgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiMSIgPSAiIzIxMzg0YSIsICIyIiA9ICIjYjU3NTFhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjMiID0gIiMwZjc2NmUiLCAiNCIgPSAiI2EzM2EzYSIpLA0KICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJTZWdtZW50byIpICsNCiAgY29vcmRfZml4ZWQoKSArDQogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KRmlndXJhIDE1LiBNYXBhIEludGVyYWN0aXZvIFNlZ21lbnRvcyBkZWwgTWVyY2Fkbw0KYGBge3J9DQpsaWJyYXJ5KGxlYWZsZXQpDQoNCnBhbCA8LSBjb2xvckZhY3RvcihjKCIjMjEzODRhIiwgIiNiNTc1MWEiLCAiIzBmNzY2ZSIsICIjYTMzYTNhIiksDQogICAgICAgICAgICAgICAgICAgZG9tYWluID0gbWFwYV9kYXRvcyRjbHVzdGVyKQ0KDQpsZWFmbGV0KG1hcGFfZGF0b3MpIHw+DQogIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIuUG9zaXRyb24iKSB8Pg0KICBhZGRDaXJjbGVNYXJrZXJzKH5sb25naXR1ZCwgfmxhdGl0dWQsIGNvbG9yID0gfnBhbChjbHVzdGVyKSwNCiAgICAgICAgICAgICAgICAgICByYWRpdXMgPSAyLCBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAuNSwNCiAgICAgICAgICAgICAgICAgICBwb3B1cCA9IH5wYXN0ZTAoYmFycmlvX25vcm0sICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlY2lvbSwgIiBtaWxsb25lcyDCtyAiLCBhcmVhY29uc3QsICIgbcKyIikpIHw+DQogIGFkZExlZ2VuZChwYWwgPSBwYWwsIHZhbHVlcyA9IH5jbHVzdGVyLCB0aXRsZSA9ICJTZWdtZW50byIpDQpgYGANCg0KRmlndXJhIDE2LiBNYXBhIFNlZ21lbnRvcyBkZWwgTWVyY2FkbyBwb3IgU2VwYXJhZG8NCmBgYHtyfQ0KZ2dwbG90KG1hcGFfZGF0b3MsIGFlcyhsb25naXR1ZCwgbGF0aXR1ZCkpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gbWFwYV9kYXRvcyB8PiBzZWxlY3QoLWNsdXN0ZXIpLA0KICAgICAgICAgICAgIGNvbG9yID0gImdyZXk4OCIsIHNpemUgPSAuMykgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsdXN0ZXIpLCBzaXplID0gLjUsIGFscGhhID0gLjUpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIjEiID0gIiMyMTM4NGEiLCAiMiIgPSAiI2I1NzUxYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIzIiA9ICIjMGY3NjZlIiwgIjQiID0gIiNhMzNhM2EiKSwNCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gIm5vbmUiKSArDQogIGZhY2V0X3dyYXAofiBjbHVzdGVyLCBucm93ID0gMSkgKw0KICBjb29yZF9maXhlZCgpICsNCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCkxhIHJlcHJlc2VudGFjacOzbiBjYXJ0b2dyw6FmaWNhIHBvciBzZWdtZW50b3MgcmV2ZWxhIGh1ZWxsYXMgdGVycml0b3JpYWxlcyBkaWZlcmVuY2lhZGFzLiBFbCBzZWdtZW50byBkZSBhbHRhIGdhbWEgKHJvam8pIHByZXNlbnRhIGxhIGRpc3RyaWJ1Y2nDs24gbcOhcyBjb25jZW50cmFkYSBkZSBsb3MgY3VhdHJvOiBzZSBhZ3J1cGEgZW4gZG9zIG7DumNsZW9zIGJpZW4gZGVsaW1pdGFkb3MsIHVubyBzaXR1YWRvIGVuIGVsIG9jY2lkZW50ZSBjZW50cmFsIGRlIGxhIGNpdWRhZCB5IG90cm8gZW4gZWwgZXh0cmVtbyBzdXIsIG1pZW50cmFzIHF1ZSBlbCByZXN0byBkZWwgdGVycml0b3JpbyBxdWVkYSBwcsOhY3RpY2FtZW50ZSB2YWPDrW8gZGUgZXN0YSBvZmVydGEuIEVzdG9zIG7DumNsZW9zIHNlIGNvcnJlc3BvbmRlbiBjb24gbGEgWm9uYSBPZXN0ZSB5IGNvbiBlbCBzZWN0b3IgZGUgUGFuY2UuDQoNCkVsIHNlZ21lbnRvIGVjb27Ds21pY28gY29tcGFjdG8gKHZlcmRlKSBleGhpYmUgZWwgcGF0csOzbiBjb21wbGVtZW50YXJpbywgY29uIHVuYSBjb2JlcnR1cmEgdGVycml0b3JpYWwgbXVjaG8gbcOhcyBleHRlbnNhIHF1ZSBzZSBjb25jZW50cmEgZW4gZWwgbm9ydGUgeSBlbCBjZW50cm8tb3JpZW50ZSwgYWRlbcOhcyBkZSB1bmEgbWFuY2hhIHBhcnRpY3VsYXJtZW50ZSBkZW5zYSBlbiBlbCBzZWN0b3Igc3VyLWNlbnRyYWwuIExhcyDDoXJlYXMgZG9uZGUgdW4gcGFuZWwgcHJlZG9taW5hIHNvbiBwcmVjaXNhbWVudGUgYXF1ZWxsYXMgZG9uZGUgZWwgb3RybyBzZSBlbmN1ZW50cmEgYXVzZW50ZSwgZGUgbW9kbyBxdWUgbGEgY2l1ZGFkIHByZXNlbnRhIHVuYSBzZWdyZWdhY2nDs24gZXNwYWNpYWwgcHJvbnVuY2lhZGEgZW50cmUgbGEgb2ZlcnRhIGRlIGdhbWEgYWx0YSB5IGxhIGRlIGVudHJhZGEuDQoNCkVsIHNlZ21lbnRvIGludGVybWVkaW8gKGF6dWwpIHNlIGRpc3RyaWJ1eWUgc2lndWllbmRvIHVuYSBmcmFuamEgY29udGludWEgcG9yIGVsIGZsYW5jbyBvY2NpZGVudGFsLCBleHRlbmRpZGEgZGUgbm9ydGUgYSBzdXIsIGNvbmZpZ3VyYWNpw7NuIHF1ZSBzdWdpZXJlIHN1IGFzb2NpYWNpw7NuIGNvbiB1biBjb3JyZWRvciB1cmJhbm8gZXNwZWPDrWZpY28gYW50ZXMgcXVlIGNvbiBzZWN0b3JlcyBhaXNsYWRvcy4gRWwgZGUgY2FzYXMgYW1wbGlhcyBjb24gcHJlY2lvIG1vZGVyYWRvIChhbWFyaWxsbyksIGVuIGNhbWJpbywgYXBhcmVjZSBkaXNwZXJzbyBwb3IgZWwgY29uanVudG8gZGUgbGEgY2l1ZGFkIHNpbiBjb25jZW50cmFjacOzbiBkZWZpbmlkYSwgY29uIHByZXNlbmNpYSBhcHJlY2lhYmxlIGVuIGVsIG9yaWVudGUuIEVzdGEgZGlzcGVyc2nDs24gcmVzdWx0YSBjb2hlcmVudGUgY29uIHN1IG5hdHVyYWxlemE6IHNlIHRyYXRhIGRlIHZpdmllbmRhIHVuaWZhbWlsaWFyIHRyYWRpY2lvbmFsIHByZXNlbnRlIGVuIGJhcnJpb3MgZGUgZGlzdGludGFzIMOpcG9jYXMgeSB1YmljYWNpb25lcywgY3V5YSBhZmluaWRhZCBubyByZXNwb25kZSBhIHVuYSBsb2NhbGl6YWNpw7NuIGNvbcO6biBzaW5vIGEgdW5hIGNvbmZpZ3VyYWNpw7NuIGNvbnN0cnVjdGl2YSBjb21wYXJ0aWRhLg0KDQojIyMgQ29tcG9zaWNpw7NuIGRlbCBNZXJjYWRvDQoNCkNvbiBsb3MgY29uZ2xvbWVyYWRvcyB5YSBjYXJhY3Rlcml6YWRvcyB5IG5vbWJyYWRvcyBzZWfDum4gc3UgcGVyZmlsLCBzZSByZXByZXNlbnTDsyBlbCBwZXNvIHJlbGF0aXZvIGRlIGNhZGEgc2VnbWVudG8gc29icmUgZWwgdG90YWwgZGUgbGEgb2ZlcnRhIGFuYWxpemFkYS4gTGFzIGJhcnJhcyBzZSBvcmRlbmFyb24gZGUgbWF5b3IgYSBtZW5vciBwYXJ0aWNpcGFjacOzbiB5IHNlIGV0aXF1ZXRhcm9uIGNvbiBzdSBwb3JjZW50YWplIGNvcnJlc3BvbmRpZW50ZSwgZGUgbW9kbyBxdWUgbGEgbGVjdHVyYSBubyByZXF1aWVyYSBjb25zdWx0YXIgZWwgZWplLiBFc3RhIHJlcHJlc2VudGFjacOzbiBjdW1wbGUgdW5hIGZ1bmNpw7NuIGRpc3RpbnRhIGEgbGEgZGUgbGFzIHRhYmxhcyBkZSBjYXJhY3Rlcml6YWNpw7NuIHByZXNlbnRhZGFzIHByZXZpYW1lbnRlOiBtaWVudHJhcyBhcXVlbGxhcyBkZXNjcmliZW4gcXXDqSBkaXN0aW5ndWUgYSBjYWRhIHNlZ21lbnRvIGVuIGN1YW50byBhIHN1cGVyZmljaWUsIHByZWNpbyB5IGRvdGFjacOzbiwgZXN0YSBtdWVzdHJhIGN1w6FudGEgb2ZlcnRhIHJlcHJlc2VudGEgY2FkYSB1bm8gZW4gZWwgbWVyY2FkbywgaW5mb3JtYWNpw7NuIG5lY2VzYXJpYSBwYXJhIGRpbWVuc2lvbmFyIGxhIHJlbGV2YW5jaWEgY29tZXJjaWFsIGRlIGxvcyBoYWxsYXpnb3MuDQoNCkZpZ3VyYSAxNy4gQ29tcG9zaWNpw7NuIGRlbCBNZXJjYWRvDQpgYGB7cn0NCmJhc2VfYWNwIHw+DQogIGNvdW50KGNsdXN0ZXIsIG5hbWUgPSAibiIpIHw+DQogIG11dGF0ZShwY3QgPSAxMDAgKiBuIC8gc3VtKG4pLA0KICAgICAgICAgZXRpcXVldGEgPSBjKCJNZWRpbyIsICJDYXNhcyBhbXBsaWFzXG5wcmVjaW8gbW9kZXJhZG8iLA0KICAgICAgICAgICAgICAgICAgICAgICJFY29uw7NtaWNvXG5jb21wYWN0byIsICJBbHRhIGdhbWEiKSkgfD4NCiAgZ2dwbG90KGFlcyhmY3RfcmVvcmRlcihldGlxdWV0YSwgcGN0KSwgcGN0LCBmaWxsID0gY2x1c3RlcikpICsNCiAgZ2VvbV9jb2wod2lkdGggPSAuNikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4xZiUlIiwgcGN0KSksIGhqdXN0ID0gLS4xNSwgc2l6ZSA9IDMuNSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMjEzODRhIiwgIiNiNTc1MWEiLCAiIzBmNzY2ZSIsICIjYTMzYTNhIiksDQogICAgICAgICAgICAgICAgICAgIGd1aWRlID0gIm5vbmUiKSArDQogIGNvb3JkX2ZsaXAoY2xpcCA9ICJvZmYiKSArDQogIGxhYnMoeCA9IE5VTEwsIHkgPSAiJSBkZSBsYSBvZmVydGEiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KRWwgbWVyY2FkbyBhbmFsaXphZG8gc2UgZW5jdWVudHJhIGRvbWluYWRvIHBvciBzdXMgZG9zIHNlZ21lbnRvcyBkZSBtZW5vciB2YWxvciwgcXVlIGVuIGNvbmp1bnRvIHJlw7puZW4gZWwgNzYsNCUgZGUgbGEgb2ZlcnRhLiBMYSB2aXZpZW5kYSBlY29uw7NtaWNhIGNvbXBhY3RhIGVuY2FiZXphIGxhIGRpc3RyaWJ1Y2nDs24gY29uIGVsIDQyLDUlLCBzZWd1aWRhIGRlbCBzZWdtZW50byBpbnRlcm1lZGlvIGNvbiBlbCAzMyw5JS4gTG9zIGRvcyByZXN0YW50ZXMgb2N1cGFuIHBvc2ljaW9uZXMgY29uc2lkZXJhYmxlbWVudGUgbcOhcyBtb2Rlc3RhczogbGEgYWx0YSBnYW1hIGFsY2FuemEgZWwgMTMsNiUgeSBsYXMgY2FzYXMgYW1wbGlhcyBkZSBwcmVjaW8gbW9kZXJhZG8gYXBlbmFzIGVsIDEwLDElLg0KDQojIENvbmNsdWNpb25lcyANCg0KRWwgYW7DoWxpc2lzIGRlIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzIGN1bXBsacOzIGVsIHByb3DDs3NpdG8gZGUgcmVkdWNpciBsYSBkaW1lbnNpb25hbGlkYWQgZGVsIGNvbmp1bnRvIHkgcmVzdWx0w7MgcGFydGljdWxhcm1lbnRlIGVmaWNheiBlbiBlc3RlIGNhc286IGRvcyBjb21wb25lbnRlcyBiYXN0YXJvbiBwYXJhIHNpbnRldGl6YXIgZWwgODQsOCUgZGUgbGEgdmFyaWFiaWxpZGFkIGNvbnRlbmlkYSBlbiBjaW5jbyB2YXJpYWJsZXMuIEVsIHByaW1lcm8sIHF1ZSByZWNvZ2UgZWwgNjcsMCUsIHJldmVsw7MgcXVlIGxvcyBhdHJpYnV0b3MgZsOtc2ljb3MgZGUgdW5hIHZpdmllbmRhIG5vIGFwb3J0YW4gaW5mb3JtYWNpw7NuIGluZGVwZW5kaWVudGUgZW50cmUgc8OtLCBzaW5vIHF1ZSByZXNwb25kZW4gYSB1biBmYWN0b3IgY29tw7puIGRlIHRhbWHDsW8geSB2YWxvcjsgbGFzIGNpbmNvIHZhcmlhYmxlcyBzZSBvcmllbnRhbiBlbiBlbCBtaXNtbyBzZW50aWRvIGNvbiBjb250cmlidWNpb25lcyBwcsOhY3RpY2FtZW50ZSBlcXVpdmFsZW50ZXMsIGVudHJlIGVsIDE5LDAlIHkgZWwgMjEsMCUuIEVsIHNlZ3VuZG8gY29tcG9uZW50ZSBhcG9ydMOzIGVsIGhhbGxhemdvIGRlIG1heW9yIGludGVyw6lzIGFuYWzDrXRpY28gYWwgY29udHJhcG9uZXIgZWwgbsO6bWVybyBkZSBoYWJpdGFjaW9uZXMgYWwgcHJlY2lvIHkgYSBsYSBkb3RhY2nDs24gZGUgcGFycXVlYWRlcm9zLCBkaXN0aW5ndWllbmRvIGFzw60gZW50cmUgZG9zIGVzdHJhdGVnaWFzIGRlIGFwcm92ZWNoYW1pZW50byBkZWwgZXNwYWNpbyBxdWUgb3BlcmFuIGNvbiBpbmRlcGVuZGVuY2lhIGRlbCB0YW1hw7FvIHRvdGFsIGRlbCBpbm11ZWJsZS4gRXN0ZSBlamUgZXhpc3RlIGZ1bmRhbWVudGFsbWVudGUgcGFyYSBhY29tb2RhciBsYSB2YXJpYWJpbGlkYWQgZGVsIG7Dum1lcm8gZGUgaGFiaXRhY2lvbmVzIHF1ZSBlbCBwcmltZXIgY29tcG9uZW50ZSBubyBsb2dyYSBjYXB0dXJhcjogZGljaGEgdmFyaWFibGUgYWxjYW56YSB1bmEgY2FsaWRhZCBkZSByZXByZXNlbnRhY2nDs24gc3VwZXJpb3IgYSBsYSBxdWUgb2J0aWVuZSBlbiBsYSBkaW1lbnNpw7NuIHByaW5jaXBhbC4gTGEgcHJveWVjY2nDs24gZGUgbG9zIGluZGl2aWR1b3MgYcOxYWRpw7MgdW4gZWxlbWVudG8gcXVlIGxhcyB2YXJpYWJsZXMgcG9yIHPDrSBzb2xhcyBubyByZXZlbGFiYW4sIGFsIG1vc3RyYXIgcXVlIGxvcyBhcGFydGFtZW50b3Mgb2N1cGFuIHVuYSByZWdpw7NuIGRlbCBwbGFubyBjb25zaWRlcmFibGVtZW50ZSBtw6FzIGVzdHJlY2hhIHF1ZSBsYXMgY2FzYXMsIHJlZmxlam8gZGUgbGEgZXN0YW5kYXJpemFjacOzbiBjb25zdHJ1Y3RpdmEgcHJvcGlhIGRlIGxhIHZpdmllbmRhIG11bHRpZmFtaWxpYXIuIA0KDQpMYSBzZWdtZW50YWNpw7NuIG1lZGlhbnRlIGstbWVkaWFzLCBhcGxpY2FkYSBzb2JyZSBsYXMgY29vcmRlbmFkYXMgZmFjdG9yaWFsZXMgZW4gbHVnYXIgZGUgc29icmUgbGFzIHZhcmlhYmxlcyBvcmlnaW5hbGVzLCBwZXJtaXRpw7Mgc3VwZXJhciBsYSByZWR1bmRhbmNpYSBxdWUgbGFzIGNvcnJlbGFjaW9uZXMgZWxldmFkYXMgaGFicsOtYW4gaW50cm9kdWNpZG8gZW4gZWwgY8OhbGN1bG8gZGUgZGlzdGFuY2lhcy4gRW4gY3VhbnRvIGEgbGEgZGV0ZXJtaW5hY2nDs24gZGVsIG7Dum1lcm8gZGUgZ3J1cG9zLCBlbCDDrW5kaWNlIGRlIHNpbHVldGEgc2XDsWFsYWJhIGRvcyBjb25nbG9tZXJhZG9zIGNvbW8gc29sdWNpw7NuIMOzcHRpbWEsIGxhIGN1cnZhIGRlIGluZXJjaWEgc2l0dWFiYSBlbCBjb2RvIGVuIGN1YXRybywgeSBmdWUgZXN0ZSDDumx0aW1vIGNyaXRlcmlvIGVsIHF1ZSBwcmV2YWxlY2nDsyBwb3IgcmF6b25lcyBzdXN0YW50aXZhcyBhbnRlcyBxdWUgZXN0YWTDrXN0aWNhcy4gVW5hIHBhcnRpY2nDs24gYmluYXJpYSBoYWJyw61hIHJlcHJvZHVjaWRvIGxhIGRpc3RpbmNpw7NuIGVudHJlIGlubXVlYmxlcyBncmFuZGVzIHkgcGVxdWXDsW9zLCB5YSBjb25vY2lkYSwgc2luIG9mcmVjZXIgYmFzZSBhbGd1bmEgcGFyYSBkaWZlcmVuY2lhciB1bmEgZXN0cmF0ZWdpYSBjb21lcmNpYWwuIExhIHNvbHVjacOzbiBkZSBjdWF0cm8gc2VnbWVudG9zLCBlbiBjYW1iaW8sIHJlc3VsdMOzIHBsZW5hbWVudGUgaW50ZXJwcmV0YWJsZSB5IHByb2R1am8gZWwgaGFsbGF6Z28gY2VudHJhbCBkZWwgYW5hw6FsaXNpczogZG9zIGNvbmdsb21lcmFkb3MgZGUgc3VwZXJmaWNpZSBwcsOhY3RpY2FtZW50ZSBpZMOpbnRpY2EgKDMwMCB5IDMyMiBtZXRyb3MgY3VhZHJhZG9zKSBwcmVzZW50YW4gcHJlY2lvcyBtZWRpYW5vcyBkZSA0MzAgeSA5NjAgbWlsbG9uZXMgZGUgcGVzb3MuIEVzdGUgcmVzdWx0YWRvLCBxdWUgZWwgYW7DoWxpc2lzIHVuaXZhcmlhZG8gbm8gaGFicsOtYSBwZXJtaXRpZG8gZGV0ZWN0YXIsIGNvbmZpcm1hIHF1ZSBsYSBzdXBlcmZpY2llIGNvbnN0cnVpZGEgY29uc2lkZXJhZGEgYWlzbGFkYW1lbnRlIGNvbnN0aXR1eWUgdW4gaW5kaWNhZG9yIGluc3VmaWNpZW50ZSBkZWwgdmFsb3IgZGUgdW4gaW5tdWVibGUuIEVsIG3DqXRvZG8gZGVtb3N0csOzIGFzw60gc3UgdXRpbGlkYWQgbm8gdGFudG8gcGFyYSBjbGFzaWZpY2FyIGNvbW8gcGFyYSByZXZlbGFyIGVzdHJ1Y3R1cmFzOiBsb3Mgc2VnbWVudG9zIG9idGVuaWRvcyBubyBjb2luY2lkZW4gY29uIG5pbmd1bmEgY2xhc2lmaWNhY2nDs24gcHJldmlhIChuaSBlbCB0aXBvIGRlIGlubXVlYmxlIG5pIGVsIGVzdHJhdG8gbG9zIHJlcHJvZHVjZW4pIHkgc3UgY3J1Y2UgcG9zdGVyaW9yIGNvbiBlc2FzIHZhcmlhYmxlcyBhcG9ydMOzIHVuYSBsZWN0dXJhIHF1ZSBuaW5ndW5hIGRlIGVsbGFzIG9mcmVjw61hIHBvciBzZXBhcmFkby4NCg0KUG9yIMO6bHRpbW8sIGVsIGFuw6FsaXNpcyBkZSBjb3JyZXNwb25kZW5jaWFzIGluY29ycG9yw7MgbGFzIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMsIGN1eWEgbmF0dXJhbGV6YSBpbXBpZGUgc3UgdHJhdGFtaWVudG8gbWVkaWFudGUgbG9zIG3DqXRvZG9zIGFudGVyaW9yZXMuIFN1IGFwbGljYWNpw7NuIGFsIGNydWNlIGVudHJlIGJhcnJpb3MgeSBzZWdtZW50b3MgZGUgbWVyY2FkbyByZXN1bHTDsyBlc3BlY2lhbG1lbnRlIHByb2R1Y3RpdmE6IGxhcyBkb3MgcHJpbWVyYXMgZGltZW5zaW9uZXMgcmVjb2dpZXJvbiBlbCA5Miw2JSBkZSBsYSBhc29jaWFjacOzbiwgeSBsYSBwcmltZXJhIGRlIGVsbGFzLCBjb24gZWwgNzQsOSUsIGNvbmZpZ3Vyw7MgdW4gZWplIGRlIHZhbG9yIHF1ZSBvcmRlbmEgbG9zIGJhcnJpb3MgZGUgbGEgY2l1ZGFkIHNlZ8O6biBlbCBuaXZlbCBkZSBwcmVjaW8gZGUgc3Ugb2ZlcnRhLiBMYSBzZWd1bmRhIGRpbWVuc2nDs24gcmVwcm9kdWpvIGVuIGVsIHBsYW5vIHRlcnJpdG9yaWFsIGxhIG1pc21hIGRpc3RpbmNpw7NuIHF1ZSBlbCBhbsOhbGlzaXMgZGUgY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgaGFiw61hIGlkZW50aWZpY2FkbyBlbiBsYXMgdmFyaWFibGVzIGN1YW50aXRhdGl2YXMsIHNpdHVhbmRvIGVuIHN1IGV4dHJlbW8gc3VwZXJpb3IgYSBsb3MgYmFycmlvcyB0cmFkaWNpb25hbGVzIGNhcmFjdGVyaXphZG9zIHBvciBjYXNhcyBhbXBsaWFzIGRlIHByZWNpbyBtb2RlcmFkbzsgZXN0YSBjb252ZXJnZW5jaWEgZW50cmUgZG9zIG3DqXRvZG9zIGRlIG5hdHVyYWxlemEgZGlzdGludGEgcmVmdWVyemEgbGEgc29saWRleiBkZWwgaGFsbGF6Z28uIExhIHByaW5jaXBhbCByZXN0cmljY2nDs24gZGVsIG3DqXRvZG8gc2UgbWFuaWZlc3TDsyBlbiBsYSBuZWNlc2lkYWQgZGUgZXhjbHVpciBsb3MgYmFycmlvcyBjb24gbWVub3MgZGUgY2luY3VlbnRhIHJlZ2lzdHJvcywgZGVjaXNpw7NuIHF1ZSBkZWrDsyBmdWVyYSBkZWwgbWFwYSB1bmEgcG9yY2nDs24gZGUgbGEgb2ZlcnRhIGN1eWEgY29tcG9zaWNpw7NuIHJlc3VsdGFiYSBkZW1hc2lhZG8gaW5lc3RhYmxlIHBhcmEgYWRtaXRpciBpbnRlcnByZXRhY2nDs24uDQoNCiMgUmVmZXJlbmNpYXMNCg0KQ2VudHJvIE1hZ2lzICgyMDI2KS4gTWF0ZXJpYWwgZGVsIGN1cnNvIE1vZGVsb3MgRXN0YWRpc3RpY29zIHBhcmEgbGEgVG9tYSBkZSBEZXNpY2lvbmVzLCB2ZXJzacOzbiAwMi0yMDI2LiBNYWVzdHLDrWEgZW4gQ2llbmNpYSBkZSBEYXRvcywgUG9udGlmaWNpYSBVbml2ZXJzaWRhZCBKYXZlcmlhbmEgQ2FsaSwgRmFjdWx0YWQgZGUgSW5nZW5pZXLDrWEuDQoNCkMuIEUuIFBhcmRvLCBFc3RhZMOtc3RpY2EgZGVzY3JpcHRpdmEgbXVsdGl2YXJpYWRhLCAxLsKqIGVkLiBCb2dvdMOhLCBDb2xvbWJpYTogVW5pdmVyc2lkYWQgTmFjaW9uYWwgZGUgQ29sb21iaWEsIDIwMjAuIFtFbiBsw61uZWFdLiBEaXNwb25pYmxlOiBbY2hyb21lLWV4dGVuc2lvbjovL2VmYWlkbmJtbm5uaWJwY2FqcGNnbGNsZWZpbmRta2FqL2h0dHBzOi8vYmZmcmVwb3NpdG9yaW8udW5hbC5lZHUuY28vc2VydmVyL2FwaS9jb3JlL2JpdHN0cmVhbXMvYTMyYTgzNmItOGU1NC00ZWI3LWIyZWYtYWVkNzAxOWJlMmMxL2NvbnRlbnRdDQoNCkRlY2xhcmFjacOzbiBzb2JyZSBlbCB1c28gZGUgaW50ZWxpZ2VuY2lhIGFydGlmaWNpYWwNCg0KRHVyYW50ZSBsYSBlbGFib3JhY2nDs24gZGUgZXN0ZSB0cmFiYWpvIHNlIHV0aWxpesOzIENsYXVkZSAoQW50aHJvcGljLCAyMDI1KSBjb21vIGhlcnJhbWllbnRhIGRlIGFwb3lvIG1ldG9kb2zDs2dpY28uIFN1IGVtcGxlbyBzZSBjb25jZW50csOzIGVuIGxhIGRpc2N1c2nDs24gZGUgY3JpdGVyaW9zIHBhcmEgZWwgdHJhdGFtaWVudG8gZGUgZGF0b3MgYXVzZW50ZXMgeSB2YWxvcmVzIGF0w61waWNvcywgbGEgcmV2aXNpw7NuIGRlIGxhIHNpbnRheGlzIGVuIFIgeSBsYSByZWRhY2Npw7NuIGRlIGxvcyBhcGFydGFkb3MgZGVzY3JpcHRpdm9zLiBMYXMgZGVjaXNpb25lcyBhbmFsw610aWNhcyAobGEgZGVzYWdyZWdhY2nDs24gZGUgbGEgdmFyaWFibGUgcGlzbywgbGEgcmVjb2RpZmljYWNpw7NuIGRlIGxvcyB2YWxvcmVzIGRlIHBhcnF1ZWFkZXJvcywgbGEgZXhjbHVzacOzbiBkZSBsYSBpbXB1dGFjacOzbiB5IGxhIGRldGVybWluYWNpw7NuIGRlbCBuw7ptZXJvIGRlIGNvbmdsb21lcmFkb3MpIGZ1ZXJvbiBhZG9wdGFkYXMgcG9yIGVsIGF1dG9yIHRyYXMgY29udHJhc3RhciBsYXMgYWx0ZXJuYXRpdmFzIHBsYW50ZWFkYXMuIExhIGludGVycHJldGFjacOzbiBkZSBsb3MgcmVzdWx0YWRvcyB5IGxhcyBjb25jbHVzaW9uZXMgc29uIGRlIHN1IGV4Y2x1c2l2YSByZXNwb25zYWJpbGlkYWQuDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K