Video de sustentación: [ENLACE AL VIDEO]

Asignatura: Analítica de Negocios · Especialización en Finanzas · Pontificia Universidad Javeriana Cali · Profesora Julieth Stefens Cerón

Resumen ejecutivo

Adidas necesita saber dónde y por qué método de venta impulsar sus ventas en Estados Unidos sin sacrificar rentabilidad. Analizamos 9.644 registros de ventas (US$ 120,2 millones en ventas, US$ 47,2 millones de utilidad operativa y 39,3% de margen) y encontramos que vender más no siempre es ganar más: el margen va de 28,5% en Honolulu a 54,4% en Birmingham, y un millón de dólares adicional deja cerca de US$ 446 mil de utilidad en los siete estados que llamamos Estrella, frente a US$ 361 mil en los once estados grandes de margen bajo (California, New York y Florida, entre otros). West es la región que más vende (30,3%) y la de menor margen (35,7%); gana 3,2 puntos menos de lo que su mezcla de productos y canales permitiría, y todos los distribuidores ganan menos allí. Online es el método con mejor margen en 42 de los 50 estados, aunque su margen baja de 49,3% a 41,3% en los periodos de mayor volumen. Recomendamos crecer primero en los estados Estrella y de Oportunidad, probar de forma controlada el impulso a Online en Northeast y West, y corregir el margen de Women’s Apparel en California, Washington y Hawaii antes de buscar más volumen allí. Los beneficios estimados son escenarios, no resultados garantizados.

Datos y preparación

Alcance y unidad de observación

Cada fila de la base es el registro de ventas de un producto, vendido por un distribuidor, en una ciudad y por un método de venta. Por la estructura de la base (ver la revisión de calidad), cada fila parece corresponder a un mes, aunque la fecha no viene en el archivo. La base cubre 50 estados agrupados en 5 regiones, 54 ciudades, 6 distribuidores, 6 productos y 3 métodos de venta (In-store, Online y Outlet). La base no indica la moneda; trabajamos en dólares porque las ventas son en Estados Unidos. Tampoco indica el periodo, así que las conclusiones se limitan a lo que contiene la base.

PASO 1. Cargar librerías

library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)
library(plotly)
library(knitr)
library(kableExtra)

# Estilo común para todos los gráficos
tema <- theme_minimal() + theme(plot.title = element_text(size = 13, face = "bold"))

PASO 2. Cargar la base de datos

datos <- read_excel("DatosCaso1.modificado.xlsx",
    col_types = c("text", "text", "text",
        "text", "text", "numeric", "numeric",
        "numeric", "numeric", "numeric",
        "text"))

# Ver las primeras filas de la base
head(datos) %>% kable() %>% kable_styling(font_size = 11) %>% scroll_box(width = "100%")
distribuidor region estado ciudad producto precio_unidad unidades_vendidas ventas_total utilidad_operativa margen_operativo metodo_venta
Foot Locker Northeast New York New York Men’s Street Footwear 50 1200 60000 30000.0 0.50 In-store
Foot Locker Northeast New York New York Men’s Athletic Footwear 50 1000 50000 15000.0 0.30 In-store
Foot Locker Northeast New York New York Women’s Street Footwear 40 1000 40000 14000.0 0.35 In-store
Foot Locker Northeast New York New York Women’s Athletic Footwear 45 850 38250 13387.5 0.35 In-store
Foot Locker Northeast New York New York Men’s Apparel 60 900 54000 16200.0 0.30 In-store
Foot Locker Northeast New York New York Women’s Apparel 50 1000 50000 12500.0 0.25 In-store
# Funciones para mostrar dólares y porcentajes con coma decimal en las tablas
usd <- function(x) paste0(ifelse(x < 0, "-", ""), "US$ ", format(round(abs(x)), big.mark = ".", decimal.mark = ",", scientific = FALSE, trim = TRUE))
pct <- function(x) paste0(format(round(x, 1), nsmall = 1, decimal.mark = ",", trim = TRUE), "%")
num <- function(x, d = 0) format(round(x, d), nsmall = d, big.mark = ".", decimal.mark = ",", scientific = FALSE, trim = TRUE)
Variable Tipo Escala Unidad
distribuidor, región, estado, ciudad, producto, método de venta Cualitativa Nominal Categorías
precio_unidad Cuantitativa continua Razón US$ por unidad
unidades_vendidas Cuantitativa discreta Razón Unidades
ventas_total, utilidad_operativa Cuantitativa continua Razón US$
margen_operativo Cuantitativa continua Razón Proporción (0,35 = 35%)

PASO 3. Revisar la calidad de los datos

# 1. Formato y valores faltantes
tipos      <- sapply(datos, class)
faltantes  <- sum(is.na(datos))

# 2. Coherencia: ventas = precio x unidades y utilidad = ventas x margen (diferencias mayores a 1 dólar)
error_ventas   <- sum(abs(datos$ventas_total - datos$precio_unidad * datos$unidades_vendidas) > 1)
error_utilidad <- sum(abs(datos$utilidad_operativa - datos$ventas_total * datos$margen_operativo) > 1)

# 3. Registros con ventas en cero
ceros <- datos %>% filter(ventas_total == 0)

# 4. Filas duplicadas (las 11 columnas idénticas) y margen agregado con y sin ellas
duplicados     <- sum(duplicated(datos))
sin_duplicados <- distinct(datos)
margen_con     <- sum(datos$utilidad_operativa) / sum(datos$ventas_total) * 100
margen_sin     <- sum(sin_duplicados$utilidad_operativa) / sum(sin_duplicados$ventas_total) * 100

# 5. Ciudades con el mismo nombre en estados distintos
homonimas <- datos %>% distinct(ciudad, estado) %>% count(ciudad) %>% filter(n > 1)

# 6. Valores atípicos con la regla de Tukey (por fuera de Q1 - 1,5 RIC y Q3 + 1,5 RIC)
limite_ventas <- quantile(datos$ventas_total, 0.75) + 1.5 * IQR(datos$ventas_total)
atipicos_ventas <- datos %>% filter(ventas_total > limite_ventas)
limites_margen <- quantile(datos$margen_operativo, c(0.25, 0.75)) + c(-1.5, 1.5) * IQR(datos$margen_operativo)
atipicos_margen <- sum(datos$margen_operativo < limites_margen[1] | datos$margen_operativo > limites_margen[2])

# 7. Registros por combinación ciudad + producto + método (¿la base parece mensual?)
registros_combinacion <- datos %>% count(ciudad, estado, producto, metodo_venta)
filas_12_24 <- sum(registros_combinacion$n[registros_combinacion$n %in% c(12, 24)]) / nrow(datos) * 100

# Tabla resumen de la revisión
revision <- data.frame(
  `Revisión` = c("Formato de las variables",
                 "Valores faltantes",
                 "Ventas que no cuadran con precio × unidades",
                 "Utilidad que no cuadra con ventas × margen",
                 "Registros con ventas en cero",
                 "Filas duplicadas (11 columnas iguales)",
                 "Ciudades con el mismo nombre en dos estados",
                 "Atípicos en ventas por registro",
                 "Atípicos en margen por registro",
                 "Registros por combinación ciudad × producto × método"),
  Resultado = c(paste0(sum(tipos == "character"), " de texto y ", sum(tipos == "numeric"), " numéricas"),
                faltantes,
                error_ventas,
                error_utilidad,
                ifelse(nrow(ceros) > 0, nrow(ceros), "0 en el archivo de trabajo (4 en la base original)"),
                paste0(duplicados, " (", pct(duplicados / nrow(datos) * 100), " de las filas)"),
                paste(homonimas$ciudad, collapse = " y "),
                paste0(num(nrow(atipicos_ventas)), " registros (", pct(nrow(atipicos_ventas) / nrow(datos) * 100),
                       ") por encima de ", usd(limite_ventas), ", que suman ",
                       pct(sum(atipicos_ventas$ventas_total) / sum(datos$ventas_total) * 100), " de las ventas"),
                paste0(atipicos_margen, " registros por fuera de ", pct(limites_margen[1] * 100), " a ", pct(limites_margen[2] * 100)),
                paste0(pct(filas_12_24), " de las filas están en combinaciones con exactamente 12 o 24 registros")),
  `Decisión` = c("Se fijó el tipo de cada columna al cargar el archivo. El margen viene como proporción y se presenta en porcentaje.",
                 "No requiere tratamiento.",
                 "Las cifras son coherentes y se usan tal como vienen.",
                 "Las cifras son coherentes y se usan tal como vienen.",
                 "La base original tenía 4 registros de Foot Locker en Omaha (Women's Athletic Footwear) con 0 unidades y 0 ventas. Se eliminaron porque no representan una venta y su margen no se puede calcular. No cambian las ventas, la utilidad ni el margen agregado.",
                 paste0("Se conservan: sin fecha ni factura no hay evidencia de error. Sin ellas el margen pasaría de ",
                        pct(margen_con), " a ", pct(margen_sin), " y ninguna conclusión cambia."),
                 "Cada ciudad se identifica siempre junto con su estado.",
                 "Se conservan: son ventas reales y coherentes con precio × unidades. Por eso describimos el registro típico con la mediana.",
                 "Se conservan: el rango (10% a 80%) es posible en una operación comercial.",
                 "Es el patrón de una base mensual (12 registros por año) sin fecha. Respalda conservar los duplicados y analizar con totales."),
  check.names = FALSE)

kable(revision, caption = "Revisión de calidad de los datos", align = c("l", "l", "l")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE) %>%
  column_spec(1, bold = TRUE, width = "14em")
Revisión de calidad de los datos
Revisión Resultado Decisión
Formato de las variables 6 de texto y 5 numéricas Se fijó el tipo de cada columna al cargar el archivo. El margen viene como proporción y se presenta en porcentaje.
Valores faltantes 0 No requiere tratamiento.
Ventas que no cuadran con precio × unidades 0 Las cifras son coherentes y se usan tal como vienen.
Utilidad que no cuadra con ventas × margen 0 Las cifras son coherentes y se usan tal como vienen.
Registros con ventas en cero 0 en el archivo de trabajo (4 en la base original) La base original tenía 4 registros de Foot Locker en Omaha (Women’s Athletic Footwear) con 0 unidades y 0 ventas. Se eliminaron porque no representan una venta y su margen no se puede calcular. No cambian las ventas, la utilidad ni el margen agregado.
Filas duplicadas (11 columnas iguales) 262 (2,7% de las filas) Se conservan: sin fecha ni factura no hay evidencia de error. Sin ellas el margen pasaría de 39,3% a 39,5% y ninguna conclusión cambia.
Ciudades con el mismo nombre en dos estados Charleston y Portland Cada ciudad se identifica siempre junto con su estado.
Atípicos en ventas por registro 799 registros (8,3%) por encima de US$ 33.578, que suman 30,4% de las ventas Se conservan: son ventas reales y coherentes con precio × unidades. Por eso describimos el registro típico con la mediana.
Atípicos en margen por registro 63 registros por fuera de 14,0% a 70,0% Se conservan: el rango (10% a 80%) es posible en una operación comercial.
Registros por combinación ciudad × producto × método 89,1% de las filas están en combinaciones con exactamente 12 o 24 registros Es el patrón de una base mensual (12 registros por año) sin fecha. Respalda conservar los duplicados y analizar con totales.

Conclusión de la revisión. La base está completa y sus cifras cuadran entre sí. Solo eliminamos los 4 registros que no tenían ventas, con lo que pasamos de 9.648 a 9.644 filas. Los duplicados y los valores atípicos los dejamos, porque no hay evidencia de que sean errores.

Análisis exploratorio

Unidades, segmentos, periodo y denominadores

  • Unidades: las ventas y la utilidad están en dólares (US$), las cantidades en unidades vendidas y el margen en porcentaje.
  • Segmentos: 5 regiones, 50 estados, 54 ciudades, 6 distribuidores, 6 productos y 3 métodos de venta (In-store, Online y Outlet).
  • Periodo: la base no trae fecha. Por su estructura parece mensual, pero no sabemos qué meses cubre, así que los resultados corresponden al total acumulado de la base.
  • Denominadores: todas las participaciones (%) se calculan sobre el total de la base: US$ 120,2 millones en ventas, 2.478.861 unidades o US$ 47,2 millones de utilidad. Cuando un porcentaje usa otro denominador (por ejemplo, el peso de un método dentro de una región), se indica en el texto.

Indicadores y por qué los usamos.

  • Margen operativo agregado (utilidad total / ventas totales × 100), para medir la rentabilidad real de cada segmento. Da 39,3%; el promedio simple de los márgenes por registro (42,3%) sobrestima la rentabilidad porque los registros pequeños pesarían lo mismo que los grandes, así que no lo usamos.
  • Participación (%) en ventas, unidades y utilidad, para ver qué segmentos concentran el negocio.
  • Mediana, desviación estándar y coeficiente de variación, porque las ventas tienen valores muy altos que inflan el promedio y queremos medir la dispersión.
  • Utilidad por registro, para comparar el tamaño de la utilidad en dólares con el margen en porcentaje.
  • Correlación de Pearson (y de Spearman entre estados), para describir relaciones entre variables, sin asumir que una causa la otra.
  • Margen esperado por la mezcla de producto y método, para saber si una región gana menos por lo que vende o por cómo le va con lo que vende.

Indicadores generales

ventas_tot    <- sum(datos$ventas_total)
utilidad_tot  <- sum(datos$utilidad_operativa)
unidades_tot  <- sum(datos$unidades_vendidas)
margen_tot    <- utilidad_tot / ventas_tot * 100
margen_simple <- mean(datos$margen_operativo) * 100

tabla_kpi <- data.frame(
  Indicador = c("Registros", "Ventas totales", "Unidades vendidas", "Utilidad operativa total",
                "Margen operativo agregado (utilidad / ventas)", "Promedio simple de los márgenes (no se usa)",
                "Precio promedio por unidad (ventas / unidades)"),
  Valor = c(num(nrow(datos)), usd(ventas_tot), num(unidades_tot), usd(utilidad_tot),
            pct(margen_tot), pct(margen_simple), paste0("US$ ", num(ventas_tot / unidades_tot, 2))))

kable(tabla_kpi, caption = "Tabla 1. Indicadores generales", align = c("l", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 1. Indicadores generales
Indicador Valor
Registros 9.644
Ventas totales US$ 120.166.650
Unidades vendidas 2.478.861
Utilidad operativa total US$ 47.224.968
Margen operativo agregado (utilidad / ventas) 39,3%
Promedio simple de los márgenes (no se usa) 42,3%
Precio promedio por unidad (ventas / unidades) US$ 48,48

Lectura de la Tabla 1. Adidas vendió US$ 120,2 millones y 2.478.861 unidades, con una utilidad operativa de US$ 47,2 millones y un margen agregado de 39,3%. El precio promedio fue de US$ 48,48 por unidad.

Tendencia central y dispersión

Pregunta: ¿cómo es un registro típico y qué tanto varían las variables?

variables <- c("precio_unidad", "unidades_vendidas", "ventas_total", "utilidad_operativa", "margen_operativo")

descriptivas <- data.frame(
  Variable = c("Precio por unidad (US$)", "Unidades vendidas", "Ventas por registro (US$)",
               "Utilidad por registro (US$)", "Margen por registro (%)"),
  Media    = sapply(datos[variables], mean),
  Mediana  = sapply(datos[variables], median),
  `Desv. estándar` = sapply(datos[variables], sd),
  `Mínimo` = sapply(datos[variables], min),
  `Máximo` = sapply(datos[variables], max),
  check.names = FALSE, row.names = NULL)

# El margen se muestra en porcentaje
descriptivas[5, 2:6] <- descriptivas[5, 2:6] * 100
descriptivas$`Coef. de variación` <- descriptivas$`Desv. estándar` / descriptivas$Media

descriptivas %>%
  mutate(across(Media:`Máximo`, ~ num(.x, 1)), `Coef. de variación` = num(`Coef. de variación`, 2)) %>%
  kable(caption = "Tabla 2. Medidas de tendencia central y dispersión por registro", align = c("l", rep("r", 6))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 2. Medidas de tendencia central y dispersión por registro
Variable Media Mediana Desv. estándar Mínimo Máximo Coef. de variación
Precio por unidad (US$) 45,2 45,0 14,7 7,0 110,0 0,33
Unidades vendidas 257,0 176,0 214,2 6,0 1.275,0 0,83
Ventas por registro (US$) 12.460,2 7.812,0 12.716,5 160,0 82.500,0 1,02
Utilidad por registro (US$) 4.896,8 3.264,0 4.866,5 75,2 39.000,0 0,99
Margen por registro (%) 42,3 41,0 9,7 10,0 80,0 0,23

Lectura de la Tabla 2. Un registro típico (la mediana) tiene 176 unidades, US$ 7.812 en ventas y US$ 3.264 de utilidad. El promedio de las ventas (US$ 12.460) está muy por encima de la mediana y su coeficiente de variación es 1,02, lo que indica que hay registros muy grandes que jalan el promedio hacia arriba. El margen, en cambio, varía poco (coeficiente de 0,23).

Distribución y valores atípicos

Pregunta: ¿las ventas por registro son parecidas entre sí o hay unos pocos registros muy grandes?

ggplotly(
  ggplot(datos, aes(x = ventas_total)) +
    geom_histogram(binwidth = 5000, boundary = 0, fill = "steelblue", color = "white") +
    geom_vline(xintercept = median(datos$ventas_total), linetype = "dashed", color = "grey20") +
    geom_vline(xintercept = mean(datos$ventas_total), linetype = "dotted", color = "grey20") +
    geom_vline(xintercept = limite_ventas, color = "grey50") +
    labs(title = "Figura 1. Distribución de las ventas por registro",
         x = "Ventas por registro (US$)", y = "Número de registros",
         caption = "Línea discontinua: mediana. Línea punteada: media. Línea continua: límite de atípicos.") +
    tema
)

Lectura de la Figura 1. La mayoría de los registros se concentra en valores bajos y hay una cola larga hacia la derecha (asimetría positiva): la mayoría de registros vende menos de US$ 15 mil, pero hay 799 registros (8,3%) por encima de US$ 33.578 que suman el 30,4% de las ventas. La línea discontinua es la mediana, la punteada es la media y la continua es el límite a partir del cual un registro se considera atípico. Son ventas reales, así que las conservamos, y por eso preferimos la mediana para describir un registro típico y los totales para comparar segmentos.

Participación por segmentos

Pregunta: ¿qué segmentos concentran las ventas, las unidades y la utilidad?

resumen_region <- datos %>%
  group_by(region) %>%
  summarise(ventas = sum(ventas_total),
            unidades = sum(unidades_vendidas),
            utilidad = sum(utilidad_operativa)) %>%
  mutate(margen = utilidad / ventas * 100,
         part_ventas = ventas / sum(ventas) * 100,
         part_unidades = unidades / sum(unidades) * 100,
         part_utilidad = utilidad / sum(utilidad) * 100) %>%
  arrange(desc(ventas))

grafico_region <- resumen_region %>%
  select(region, part_ventas, part_utilidad, margen) %>%
  pivot_longer(c(part_ventas, part_utilidad), names_to = "medida", values_to = "valor") %>%
  mutate(medida = factor(ifelse(medida == "part_ventas", "% de las ventas", "% de la utilidad"),
                         levels = c("% de las ventas", "% de la utilidad")),
         region = factor(region, levels = resumen_region$region))

ggplotly(
  ggplot(grafico_region, aes(x = region, y = valor, fill = medida,
                             text = paste0(region, "\n", medida, ": ", pct(valor), "\nMargen: ", pct(margen)))) +
    geom_col(position = "dodge") +
    scale_fill_manual(values = c("% de las ventas" = "steelblue", "% de la utilidad" = "lightsteelblue")) +
    labs(title = "Figura 2. Participación por región en ventas y utilidad (%)",
         x = "Región", y = "% del total", fill = "") +
    tema,
  tooltip = "text")

Lectura de la Figura 2. Cada serie (ventas y utilidad) suma 100%. West es la región que más vende (30,3%), pero su barra de utilidad es más baja (27,6%). En South pasa lo contrario: tiene el 17,1% de las ventas y el 19,5% de la utilidad.

resumen_region %>%
  transmute(`Región` = region, Ventas = usd(ventas), `% ventas` = pct(part_ventas),
            `% unidades` = pct(part_unidades), `% utilidad` = pct(part_utilidad), Margen = pct(margen)) %>%
  bind_rows(data.frame(`Región` = "Total", Ventas = usd(ventas_tot),
                       `% ventas` = pct(sum(resumen_region$part_ventas)),
                       `% unidades` = pct(sum(resumen_region$part_unidades)),
                       `% utilidad` = pct(sum(resumen_region$part_utilidad)),
                       Margen = pct(margen_tot), check.names = FALSE)) %>%
  kable(caption = "Tabla 3. Ventas, unidades, utilidad y margen por región", align = c("l", rep("r", 5))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(6, bold = TRUE)
Tabla 3. Ventas, unidades, utilidad y margen por región
Región Ventas % ventas % unidades % utilidad Margen
West US$ 36.436.157 30,3% 27,7% 27,6% 35,7%
Northeast US$ 25.078.267 20,9% 20,2% 20,6% 38,8%
Southeast US$ 21.374.436 17,8% 16,4% 17,8% 39,3%
South US$ 20.603.356 17,1% 19,9% 19,5% 44,8%
Midwest US$ 16.674.434 13,9% 15,8% 14,5% 41,1%
Total US$ 120.166.650 100,0% 100,0% 100,0% 39,3%

Lectura de la Tabla 3. La fila de total confirma que las participaciones suman 100%. West tiene el menor margen (35,7%) y South el mayor (44,8%). South pesa más en unidades (19,9%) que en ventas (17,1%) porque vende a precios más bajos.

participacion <- function(base, columna, nombre) {
  tabla <- base %>%
    group_by(categoria = .data[[columna]]) %>%
    summarise(ventas = sum(ventas_total), unidades = sum(unidades_vendidas), utilidad = sum(utilidad_operativa)) %>%
    arrange(desc(ventas))
  total <- data.frame(categoria = "Total", ventas = sum(tabla$ventas),
                      unidades = sum(tabla$unidades), utilidad = sum(tabla$utilidad))
  bind_rows(tabla, total) %>%
    mutate(Dimensión = nombre,
           `% ventas` = pct(ventas / sum(tabla$ventas) * 100),
           `% unidades` = pct(unidades / sum(tabla$unidades) * 100),
           `% utilidad` = pct(utilidad / sum(tabla$utilidad) * 100),
           Margen = pct(utilidad / ventas * 100))
}

bind_rows(participacion(datos, "producto", "Producto"),
          participacion(datos, "distribuidor", "Distribuidor")) %>%
  select(Dimensión, Categoría = categoria, `% ventas`, `% unidades`, `% utilidad`, Margen) %>%
  kable(caption = "Tabla 4. Participación por producto y por distribuidor", align = c("l", "l", rep("r", 4))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(c(7, 14), bold = TRUE) %>%
  collapse_rows(columns = 1, valign = "top")
Tabla 4. Participación por producto y por distribuidor
Dimensión Categoría % ventas % unidades % utilidad Margen
Producto Men’s Street Footwear 23,0% 23,9% 24,6% 42,0%
Women’s Apparel 19,9% 17,5% 20,5% 40,6%
Men’s Athletic Footwear 17,1% 17,6% 15,7% 36,1%
Women’s Street Footwear 14,3% 15,8% 13,8% 37,8%
Men’s Apparel 13,7% 12,4% 13,5% 38,6%
Women’s Athletic Footwear 11,9% 12,8% 11,9% 39,1%
Total 100,0% 100,0% 100,0% 39,3%
Distribuidor West Gear 27,0% 25,2% 25,8% 37,6%
Foot Locker 24,2% 24,4% 24,0% 39,0%
Sports Direct 20,5% 22,5% 22,5% 43,2%
Kohl’s 11,2% 11,6% 11,0% 38,4%
Walmart 8,7% 8,3% 8,3% 37,1%
Amazon 8,4% 8,0% 8,4% 39,5%
Total 100,0% 100,0% 100,0% 39,3%

Lectura de la Tabla 4. Men’s Street Footwear es el producto que más vende (23,0%) y también el de mejor margen (42,0%). Men’s Athletic Footwear es el tercero en ventas, pero tiene el margen más bajo (36,1%). Entre distribuidores, West Gear y Foot Locker suman más de la mitad de las ventas (51,2%), y Sports Direct es el que mejor margen tiene (43,2%).

Métodos de venta y variabilidad

Pregunta: ¿qué método de venta deja mejor margen, es igual en todas las regiones y dónde varía más el margen?

region_metodo <- datos %>%
  group_by(region, metodo_venta) %>%
  summarise(ventas = sum(ventas_total),
            utilidad = sum(utilidad_operativa), .groups = "drop") %>%
  group_by(region) %>%
  mutate(margen = utilidad / ventas * 100,
         mezcla = ventas / sum(ventas) * 100) %>%
  ungroup()

ggplotly(
  ggplot(region_metodo, aes(x = metodo_venta, y = region, fill = margen,
                            text = paste0(region, " · ", metodo_venta, "\nMargen: ", pct(margen),
                                          "\nPeso en las ventas de la región: ", pct(mezcla)))) +
    geom_tile(color = "white") +
    geom_text(aes(label = paste0(pct(margen), "\n(", pct(mezcla), " de ventas)")), size = 3) +
    scale_fill_gradient(low = "#f2f2f2", high = "steelblue") +
    labs(title = "Figura 3. Margen por región y método de venta (%)",
         x = "Método de venta", y = "Región", fill = "Margen (%)") +
    tema,
  tooltip = "text")

Lectura de la Figura 3. Cada celda muestra el margen y, entre paréntesis, el peso de ese método en las ventas de la región; en cada región los tres pesos suman 100%. Entre más oscuro el azul, mayor el margen. La columna Online es la más oscura en todas las regiones, y la celda con mejor margen es Online en Northeast (48,6%), aunque allí solo pesa el 18,4% de las ventas. El Outlet gana 44,6% en South y 32,8% en West.

resumen_metodo <- datos %>%
  group_by(metodo_venta) %>%
  summarise(registros = n(),
            ventas = sum(ventas_total),
            unidades = sum(unidades_vendidas),
            utilidad = sum(utilidad_operativa)) %>%
  mutate(margen = utilidad / ventas * 100,
         part_ventas = ventas / sum(ventas) * 100,
         part_unidades = unidades / sum(unidades) * 100,
         utilidad_registro = utilidad / registros) %>%
  arrange(desc(margen))

resumen_metodo %>%
  transmute(`Método` = metodo_venta, `% ventas` = pct(part_ventas), `% unidades` = pct(part_unidades),
            Margen = pct(margen), `Utilidad promedio por registro` = usd(utilidad_registro)) %>%
  bind_rows(data.frame(`Método` = "Total", `% ventas` = pct(sum(resumen_metodo$part_ventas)),
                       `% unidades` = pct(sum(resumen_metodo$part_unidades)), Margen = pct(margen_tot),
                       `Utilidad promedio por registro` = usd(utilidad_tot / nrow(datos)), check.names = FALSE)) %>%
  kable(caption = "Tabla 5. Comparación de los métodos de venta", align = c("l", rep("r", 4))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(4, bold = TRUE)
Tabla 5. Comparación de los métodos de venta
Método % ventas % unidades Margen Utilidad promedio por registro
Online 37,4% 37,9% 43,5% US$ 4.001
Outlet 32,9% 34,3% 37,7% US$ 4.943
In-store 29,7% 27,8% 35,8% US$ 7.333
Total 100,0% 100,0% 39,3% US$ 4.897
# ¿En cuántos estados es cada método el de mayor margen?
datos %>%
  group_by(estado, metodo_venta) %>%
  summarise(margen = sum(utilidad_operativa) / sum(ventas_total), .groups = "drop") %>%
  group_by(estado) %>%
  slice_max(margen, n = 1) %>%
  ungroup() %>%
  count(metodo_venta, name = "estados") %>%
  rename(`Método con mayor margen` = metodo_venta, Estados = estados) %>%
  kable(caption = "Número de estados según el método con mayor margen", align = c("l", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Número de estados según el método con mayor margen
Método con mayor margen Estados
Online 42
Outlet 8

Lectura de la Tabla 5. Online tiene el mejor margen (43,5%) y es el método más rentable en 42 de los 50 estados; en los otros 8 gana Outlet. In-store tiene el margen más bajo (35,8%), pero la mayor utilidad por registro (US$ 7.333) porque sus ventas son más grandes: al comparar utilidad en dólares y margen, el orden se invierte.

Pregunta: ¿la diferencia entre métodos se ve también registro a registro, y en qué región varía más el margen?

ggplotly(
  ggplot(datos, aes(x = metodo_venta, y = margen_operativo * 100)) +
    geom_boxplot(fill = "lightsteelblue", outlier.colour = "grey40", outlier.size = 1) +
    facet_wrap(~region, nrow = 1) +
    labs(title = "Figura 4. Margen por registro según método y región",
         x = NULL, y = "Margen operativo (%)") +
    tema +
    theme(legend.position = "none", axis.text.x = element_text(angle = 45, hjust = 1))
)

Lectura de la Figura 4. Cada caja resume el margen de los registros de un método en una región, y la línea del medio es la mediana. Online tiene la mediana más alta en las cinco regiones (entre 44% y 49%), e In-store la más baja (35%, salvo en South con 40%).

# Variabilidad del margen por región
datos %>%
  group_by(region) %>%
  summarise(mediana = median(margen_operativo) * 100,
            desviacion = sd(margen_operativo) * 100) %>%
  arrange(desc(desviacion)) %>%
  transmute(`Región` = region, `Mediana del margen (%)` = num(mediana, 1), `Desviación estándar (puntos)` = num(desviacion, 1)) %>%
  kable(caption = "Tabla 6. Variabilidad del margen por registro en cada región", align = c("l", "r", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 6. Variabilidad del margen por registro en cada región
Región Mediana del margen (%) Desviación estándar (puntos)
West 40,0 10,7
Midwest 43,0 9,2
Southeast 40,0 9,2
South 45,0 9,1
Northeast 40,0 8,4

Lectura de la Tabla 6. West es la región donde más varía el margen entre registros (desviación de 10,7 puntos): allí conviven ventas muy rentables con otras muy poco rentables. Northeast es la más estable (8,4 puntos).

Diagnóstico y hallazgos

Presentamos cinco hallazgos; los dos primeros son los que más profundizamos. Debajo de cada tabla o gráfico está la evidencia cuantitativa y su interpretación, y al final de cada hallazgo, la implicación para el negocio.

Hallazgo 1 (profundización). Vender más no siempre es ganar más

Pregunta: ¿qué estados y ciudades combinan muchas ventas con buen margen, y cuáles venden mucho pero ganan poco?

Para clasificar los 50 estados nos hacemos dos preguntas sencillas:

  1. ¿El estado vende mucho o poco? Si los 50 estados vendieran lo mismo, cada uno tendría el 2% de las ventas (100% / 50), unos US$ 2,4 millones. Un estado que vende más del 2% es un mercado grande, como California (7,1%); uno que vende menos del 2% es un mercado pequeño, como Iowa (0,8%).
  2. ¿El estado gana mucho o poco por cada dólar vendido? La referencia es el margen de toda la empresa, 39,3%. Un estado con margen mayor gana más que el promedio, como Alabama (54,4%); uno con margen menor gana menos, como Washington (28,8%).

Al cruzar las dos respuestas quedan cuatro grupos:

Gana más que el promedio (margen mayor a 39,3%) Gana menos que el promedio (margen menor a 39,3%)
Vende mucho (más del 2% de las ventas) Estrella. Ejemplo: Texas, 5,5% de las ventas y 42,8% de margen Corregir. Ejemplo: California, 7,1% de las ventas y 34,5% de margen
Vende poco (menos del 2% de las ventas) Oportunidad. Ejemplo: Michigan, 1,9% de las ventas y 45,9% de margen Bajo aporte. Ejemplo: Iowa, 0,8% de las ventas y 38,0% de margen
resumen_estado <- datos %>%
  group_by(region, estado) %>%
  summarise(ventas = sum(ventas_total),
            utilidad = sum(utilidad_operativa),
            unidades = sum(unidades_vendidas), .groups = "drop") %>%
  mutate(margen = utilidad / ventas * 100,
         part_ventas = ventas / sum(ventas) * 100,
         precio_medio = ventas / unidades,
         grupo = case_when(part_ventas >= 2 & margen >= margen_tot ~ "Estrella",
                           part_ventas <  2 & margen >= margen_tot ~ "Oportunidad",
                           part_ventas >= 2 & margen <  margen_tot ~ "Corregir",
                           TRUE ~ "Bajo aporte"),
         grupo = factor(grupo, levels = c("Estrella", "Oportunidad", "Corregir", "Bajo aporte")))

colores_grupo <- c("Estrella" = "navy", "Oportunidad" = "deepskyblue3",
                   "Corregir" = "grey30", "Bajo aporte" = "grey70")

# Estados que se nombran en el gráfico
etiquetas <- c("California", "New York", "Florida", "Texas", "Alabama", "Tennessee",
               "Washington", "Michigan", "Idaho", "Louisiana")

ggplotly(
  ggplot(resumen_estado, aes(x = part_ventas, y = margen, color = grupo,
                             text = paste0(estado, " (", region, ")",
                                           "\nVentas: US$ ", round(ventas / 1e6, 2), " M (", pct(part_ventas), " del total)",
                                           "\nUtilidad: US$ ", round(utilidad / 1e6, 2), " M",
                                           "\nMargen: ", pct(margen)))) +
    geom_hline(yintercept = margen_tot, linetype = "dashed", color = "grey40") +
    geom_vline(xintercept = 2, linetype = "dashed", color = "grey40") +
    geom_point(aes(size = utilidad / 1e6), alpha = 0.75) +
    geom_text(data = filter(resumen_estado, estado %in% etiquetas), aes(label = estado),
              nudge_y = 1.3, size = 3, show.legend = FALSE) +
    geom_text(data = filter(resumen_estado, estado == "Hawaii"), aes(label = estado),
              nudge_y = -1.3, size = 3, show.legend = FALSE) +
    scale_color_manual(values = colores_grupo) +
    labs(title = "Figura 5. Estados: participación en ventas vs. margen",
         x = "% de las ventas totales", y = "Margen operativo agregado (%)", color = "Grupo", size = "") +
    tema,
  tooltip = "text")
# Correlación entre ventas y utilidad, y entre ventas y margen (por estado)
data.frame(`Relación entre estados` = c("Ventas y utilidad (Pearson)", "Ventas y margen (Spearman)"),
           `Correlación` = num(c(cor(resumen_estado$ventas, resumen_estado$utilidad),
                                 cor(resumen_estado$ventas, resumen_estado$margen, method = "spearman")), 2),
           check.names = FALSE) %>%
  kable(caption = "Correlaciones entre los 50 estados", align = c("l", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Correlaciones entre los 50 estados
Relación entre estados Correlación
Ventas y utilidad (Pearson) 0,98
Ventas y margen (Spearman) -0,05

Evidencia. Cada punto es un estado y su tamaño es la utilidad que aporta. Entre estados, ventas y utilidad tienen una correlación de 0,98, pero ventas y margen tienen una correlación de −0,05. Texas vende el 5,5% del total con un margen de 42,8%, mientras California vende el 7,1% con 34,5%.

Interpretación. Los estados que más venden aportan más utilidad en dólares, pero eso no significa que ganen más por cada dólar vendido.

resumen_grupo <- resumen_estado %>%
  group_by(grupo) %>%
  summarise(estados = n(),
            ventas = sum(ventas),
            utilidad = sum(utilidad),
            lista = paste(estado[order(-part_ventas)], collapse = ", ")) %>%
  mutate(margen = utilidad / ventas * 100,
         part_ventas = ventas / ventas_tot * 100,
         part_utilidad = utilidad / utilidad_tot * 100,
         utilidad_por_millon = margen / 100 * 1e6) %>%
  arrange(grupo)

resumen_grupo %>%
  transmute(Grupo = as.character(grupo), Estados = estados, `% ventas` = pct(part_ventas), `% utilidad` = pct(part_utilidad),
            Margen = pct(margen), `Utilidad por cada US$ 1 M vendido` = usd(utilidad_por_millon),
            `Estados (de mayor a menor venta)` = lista) %>%
  bind_rows(data.frame(Grupo = "Total", Estados = sum(resumen_grupo$estados),
                       `% ventas` = pct(sum(resumen_grupo$part_ventas)), `% utilidad` = pct(sum(resumen_grupo$part_utilidad)),
                       Margen = pct(margen_tot), `Utilidad por cada US$ 1 M vendido` = usd(margen_tot / 100 * 1e6),
                       `Estados (de mayor a menor venta)` = "", check.names = FALSE)) %>%
  kable(caption = "Tabla 7. Grupos de estados según ventas y margen", align = c("l", "r", "r", "r", "r", "r", "l")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE, font_size = 13) %>%
  row_spec(5, bold = TRUE)
Tabla 7. Grupos de estados según ventas y margen
Grupo Estados % ventas % utilidad Margen Utilidad por cada US$ 1 M vendido Estados (de mayor a menor venta)
Estrella 7 20,3% 23,0% 44,6% US$ 445.727 Texas, South Carolina, Louisiana, North Carolina, Idaho, Tennessee, Alabama
Oportunidad 15 19,7% 21,4% 42,6% US$ 426.207 Michigan, Ohio, Mississippi, Vermont, Montana, Arkansas, Oklahoma, Pennsylvania, Utah, West Virginia, Kentucky, Illinois, Missouri, Indiana, Nebraska
Corregir 11 40,1% 36,9% 36,1% US$ 361.165 New York, California, Florida, Washington, Virginia, Oregon, Nevada, New Mexico, Hawaii, Georgia, Colorado
Bajo aporte 17 19,9% 18,8% 37,1% US$ 370.613 New Hampshire, Wyoming, Arizona, Alaska, Connecticut, Massachusetts, Delaware, Kansas, New Jersey, Rhode Island, Maine, South Dakota, Maryland, North Dakota, Wisconsin, Iowa, Minnesota
Total 50 100,0% 100,0% 39,3% US$ 392.996

Evidencia. Los 7 estados Estrella tienen el 20,3% de las ventas y el 23,0% de la utilidad, con un margen de 44,6%. Los 11 estados a Corregir tienen el 40,1% de las ventas, pero solo el 36,9% de la utilidad (margen de 36,1%). Los 15 de Oportunidad ganan 42,6%.

Interpretación. Un millón de dólares adicional deja unos US$ 446 mil en un estado Estrella y unos US$ 361 mil en uno a Corregir, así que crecer en los primeros rinde más.

Pregunta: ¿el margen alto está agrupado geográficamente?

mapa_estados <- resumen_estado %>%
  mutate(codigo = state.abb[match(estado, state.name)])

plot_geo(mapa_estados, locationmode = "USA-states") %>%
  add_trace(z = ~margen, locations = ~codigo, color = ~margen, colors = "Blues",
            text = ~paste0(estado, "<br>Margen: ", pct(margen), "<br>Ventas: US$ ", round(ventas / 1e6, 2), " M<br>Grupo: ", grupo),
            hoverinfo = "text") %>%
  colorbar(title = "Margen (%)") %>%
  layout(title = list(text = "<b>Figura 6. Margen operativo por estado (%)</b>", font = list(size = 15)),
         geo = list(scope = "usa", projection = list(type = "albers usa"), showlakes = FALSE))

Evidencia. Entre más oscuro el azul, mayor el margen. Los márgenes más altos están en el Sur (Alabama 54,4%, Tennessee 49,5%) y los más bajos en el Pacífico: Hawaii (28,5%), Washington (28,8%) y California (34,5%). Según la Oficina de Análisis Económico de Estados Unidos (BEA), en 2021 Hawaii, California y New York eran los estados con el nivel de precios más alto, y Mississippi y Alabama estaban entre los más bajos.

Interpretación. El margen depende mucho de la geografía. El dato de la BEA sugiere que operar en los estados de margen bajo cuesta más, aunque con la base no lo podemos comprobar.

Pregunta: ¿qué ciudades aportan más utilidad y con qué margen?

resumen_ciudad <- datos %>%
  group_by(region, estado, ciudad) %>%
  summarise(ventas = sum(ventas_total),
            utilidad = sum(utilidad_operativa), .groups = "drop") %>%
  mutate(margen = utilidad / ventas * 100,
         part_utilidad = utilidad / sum(utilidad) * 100,
         nombre = paste0(ciudad, " (", estado, ") · ", pct(margen))) %>%
  arrange(desc(utilidad))

# Participación de las 10 ciudades con más utilidad
top10_ciudades <- sum(resumen_ciudad$part_utilidad[1:10])

top15 <- resumen_ciudad %>% slice(1:15) %>% mutate(nombre = factor(nombre, levels = rev(nombre)))

ggplotly(
  ggplot(top15, aes(x = utilidad / 1e6, y = nombre, fill = margen,
                    text = paste0(nombre, "\nUtilidad: US$ ", round(utilidad / 1e6, 2), " M",
                                  "\nVentas: US$ ", round(ventas / 1e6, 2), " M\nMargen: ", pct(margen)))) +
    geom_col() +
    scale_fill_gradient(low = "#c6dbef", high = "#08306b") +
    labs(title = "Figura 7. Las 15 ciudades con más utilidad operativa",
         x = "Utilidad operativa (millones de US$)", y = "Ciudad (margen)", fill = "Margen (%)") +
    tema,
  tooltip = "text") %>%
  layout(legend = list(orientation = "h", y = -0.15))

Evidencia. El largo de la barra es la utilidad de cada ciudad y el color su margen (entre más oscuro, mayor). Las 10 primeras ciudades aportan el 32,0% de la utilidad. New York y San Francisco son las que más aportan, con márgenes de 37,3% y 32,1%, mientras Birmingham (54,4%), Knoxville (49,5%) y Dallas (44,9%) combinan bien utilidad y margen.

Interpretación. Las ciudades que más utilidad aportan no son las más rentables por cada dólar vendido.

Implicación para el negocio. Para seguir creciendo conviene priorizar los estados Estrella y los de Oportunidad, que ya ganan 42,6% aunque vendan poco, y no buscar más volumen en los estados a Corregir hasta mejorar su margen.

Hallazgo 2 (profundización). West gana menos con lo mismo que venden las demás regiones

Pregunta: ¿West tiene menor margen porque vende productos o canales de bajo margen, o porque gana menos en lo mismo que venden las demás regiones?

Para responder, a cada registro le asignamos el margen que tiene ese mismo producto, con el mismo método de venta, a nivel nacional. Con ese margen calculamos la utilidad que se esperaría. Si West ganara menos solo porque vende productos o canales de menor margen, su utilidad real sería parecida a la esperada.

# Margen nacional de cada combinación producto x método
margen_referencia <- datos %>%
  group_by(producto, metodo_venta) %>%
  summarise(margen_ref = sum(utilidad_operativa) / sum(ventas_total), .groups = "drop")

datos_ref <- datos %>%
  left_join(margen_referencia, by = c("producto", "metodo_venta")) %>%
  mutate(utilidad_esperada = ventas_total * margen_ref)

brecha_region <- datos_ref %>%
  group_by(region) %>%
  summarise(ventas = sum(ventas_total),
            utilidad_real = sum(utilidad_operativa),
            utilidad_esp = sum(utilidad_esperada)) %>%
  mutate(margen_real = utilidad_real / ventas * 100,
         margen_esperado = utilidad_esp / ventas * 100,
         brecha_pp = margen_real - margen_esperado,
         brecha_usd = utilidad_real - utilidad_esp) %>%
  arrange(brecha_usd)

brecha_region %>%
  transmute(`Región` = region, `Margen real` = pct(margen_real), `Margen esperado` = pct(margen_esperado),
            `Diferencia (puntos)` = num(brecha_pp, 1), `Diferencia (US$)` = usd(brecha_usd)) %>%
  kable(caption = "Tabla 8. Margen real frente al esperado por la mezcla de producto y método", align = c("l", "r", "r", "r", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 8. Margen real frente al esperado por la mezcla de producto y método
Región Margen real Margen esperado Diferencia (puntos) Diferencia (US$)
West 35,7% 38,9% -3,2 -US$ 1.155.698
Southeast 39,3% 40,2% -0,9 -US$ 201.934
Northeast 38,8% 38,1% 0,7 US$ 178.055
Midwest 41,1% 39,7% 1,4 US$ 236.417
South 44,8% 40,2% 4,6 US$ 943.159

Evidencia. West tiene un margen de 35,7% cuando, por lo que vende, se esperaría 38,9%: son 3,2 puntos menos, unos US$ 1,16 millones de utilidad. South está 4,6 puntos por encima de lo esperado.

Interpretación. West vende productos y canales parecidos a los de otras regiones, pero obtiene menos utilidad con ellos.

Pregunta: ¿la brecha de West está repartida en toda la región o concentrada en algunos estados?

brecha_west <- datos_ref %>%
  filter(region == "West") %>%
  group_by(estado) %>%
  summarise(ventas = sum(ventas_total),
            brecha_usd = sum(utilidad_operativa) - sum(utilidad_esperada)) %>%
  mutate(signo = ifelse(brecha_usd < 0, "Por debajo de lo esperado", "Por encima de lo esperado"),
         estado = reorder(estado, brecha_usd))

ggplotly(
  ggplot(brecha_west, aes(x = brecha_usd / 1000, y = estado, fill = signo,
                          text = paste0(estado, "\nDiferencia: US$ ", round(brecha_usd / 1000), " mil"))) +
    geom_col() +
    scale_fill_manual(values = c("Por debajo de lo esperado" = "firebrick", "Por encima de lo esperado" = "forestgreen")) +
    labs(title = "Figura 8. West: utilidad real menos esperada, por estado",
         x = "Miles de US$", y = NULL, fill = "") +
    tema,
  tooltip = "text") %>%
  layout(legend = list(orientation = "h", y = -0.15))

Evidencia. En rojo están los estados que ganan menos de lo esperado y en verde los que ganan más. California, Hawaii y Washington explican el 78% del faltante de West, mientras Utah, Idaho, Colorado y Oregon están por encima de lo esperado.

Interpretación. El problema está en estados puntuales y no en toda la región.

Pregunta: ¿qué productos y qué distribuidores explican la brecha?

# Margen de cada producto en cada región
datos %>%
  group_by(producto, region) %>%
  summarise(margen = sum(utilidad_operativa) / sum(ventas_total) * 100, .groups = "drop") %>%
  mutate(margen = pct(margen)) %>%
  pivot_wider(names_from = region, values_from = margen) %>%
  rename(Producto = producto) %>%
  kable(caption = "Tabla 9. Margen operativo por producto y región", align = c("l", rep("r", 5))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 9. Margen operativo por producto y región
Producto Midwest Northeast South Southeast West
Men’s Apparel 42,2% 34,1% 39,2% 36,1% 41,6%
Men’s Athletic Footwear 37,0% 37,9% 41,3% 36,5% 31,9%
Men’s Street Footwear 41,4% 44,3% 42,9% 42,7% 39,3%
Women’s Apparel 45,9% 38,0% 52,1% 45,9% 29,8%
Women’s Athletic Footwear 39,4% 36,6% 48,6% 36,5% 36,6%
Women’s Street Footwear 38,0% 36,5% 43,1% 34,1% 37,3%

Evidencia. Women’s Apparel gana 29,8% en West y 52,1% en South, 22 puntos de diferencia. Es la combinación de región y producto con el mayor faltante frente a lo esperado, unos US$ 733 mil. En West el segundo producto más débil es Men’s Athletic Footwear (31,9%).

Interpretación. Un mismo producto rinde muy distinto según el mercado donde se vende.

distribuidor_region <- datos %>%
  group_by(distribuidor, region) %>%
  summarise(margen = sum(utilidad_operativa) / sum(ventas_total) * 100, .groups = "drop")

peor_region <- distribuidor_region %>%
  group_by(distribuidor) %>%
  summarise(`Región de menor margen` = region[which.min(margen)],
            `Diferencia entre su mejor y su peor región (puntos)` = num(max(margen) - min(margen), 1))

peso_west <- datos %>%
  filter(region == "West") %>%
  group_by(distribuidor) %>%
  summarise(ventas = sum(ventas_total)) %>%
  mutate(`% de las ventas de West` = pct(ventas / sum(ventas) * 100)) %>%
  select(-ventas)

distribuidor_region %>%
  mutate(margen = pct(margen)) %>%
  pivot_wider(names_from = region, values_from = margen, values_fill = "Sin ventas") %>%
  left_join(peor_region, by = "distribuidor") %>%
  left_join(peso_west, by = "distribuidor") %>%
  rename(Distribuidor = distribuidor) %>%
  kable(caption = "Tabla 10. Margen de cada distribuidor por región", align = c("l", rep("r", 8))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE, font_size = 13)
Tabla 10. Margen de cada distribuidor por región
Distribuidor Midwest Northeast South Southeast West Región de menor margen Diferencia entre su mejor y su peor región (puntos) % de las ventas de West
Amazon 42,8% 39,5% 38,4% 42,0% 33,2% West 9,5 4,5%
Foot Locker 40,3% 38,9% 42,0% 40,0% 35,4% West 6,5 13,9%
Kohl’s 40,8% 39,1% 40,6% Sin ventas 37,3% West 3,6 23,5%
Sports Direct 43,0% 36,4% 48,4% 40,5% 40,3% Northeast 12,0 4,6%
Walmart Sin ventas 36,2% 41,9% 33,1% 27,3% West 14,6 2,3%
West Gear 39,9% 41,0% 41,8% 39,5% 35,3% West 6,5 51,3%

Evidencia. Cinco de los seis distribuidores tienen su margen más bajo en West. Walmart, por ejemplo, gana 41,9% en South y 27,3% en West.

Interpretación. Como casi todos los distribuidores ganan menos allí, la causa parece estar en el mercado de West y no en un distribuidor en particular.

Explicaciones alternativas y datos que faltan. El margen bajo podría deberse a (a) costos de operación, logística o arriendo más altos en esos estados, (b) descuentos o promociones de los distribuidores, o (c) un costo de producto distinto en la región. Para saber cuál es la causa se necesitan el costo de ventas, los descuentos aplicados y la fecha de cada venta, que la base no incluye.

Implicación para el negocio. Cambiar de distribuidor probablemente no mejoraría el margen. Lo recomendable es revisar costos y precios en California, Washington y Hawaii, empezando por Women’s Apparel y por West Gear, que vende el 51,3% de lo que se vende en West.

Hallazgo 3. El canal importa, y no rinde igual en todas partes

Pregunta: ¿qué combinaciones de ciudad y método son las más y las menos rentables?

ciudad_metodo <- datos %>%
  group_by(region, ciudad, estado, metodo_venta) %>%
  summarise(ventas = sum(ventas_total),
            utilidad = sum(utilidad_operativa), .groups = "drop") %>%
  mutate(margen = utilidad / ventas * 100) %>%
  filter(ventas >= 500000)   # se dejan combinaciones con ventas relevantes (US$ 500 mil o más)

mejores <- ciudad_metodo %>% arrange(desc(margen)) %>% slice(1:8)
peores  <- ciudad_metodo %>% arrange(margen) %>% slice(1:8)

bind_rows(mejores, peores) %>%
  transmute(Ciudad = paste0(ciudad, " (", estado, ")"), `Región` = region, `Método` = metodo_venta,
            Ventas = usd(ventas), Utilidad = usd(utilidad), Margen = pct(margen)) %>%
  kable(caption = "Tabla 11. Combinaciones ciudad × método de venta con mayor y menor margen (ventas de US$ 500 mil o más)",
        align = c("l", "l", "l", "r", "r", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE, font_size = 13) %>%
  pack_rows("Mayor margen", 1, 8) %>%
  pack_rows("Menor margen", 9, 16)
Tabla 11. Combinaciones ciudad × método de venta con mayor y menor margen (ventas de US$ 500 mil o más)
Ciudad Región Método Ventas Utilidad Margen
Mayor margen
Dallas (Texas) South Online US$ 518.502 US$ 284.247 54,8%
Birmingham (Alabama) South Outlet US$ 2.071.868 US$ 1.084.261 52,3%
Miami (Florida) Southeast Online US$ 793.363 US$ 402.807 50,8%
Houston (Texas) South Online US$ 624.977 US$ 316.456 50,6%
Albany (New York) Northeast Online US$ 612.804 US$ 308.788 50,4%
Denver (Colorado) West Online US$ 521.536 US$ 254.618 48,8%
Richmond (Virginia) Southeast Online US$ 533.561 US$ 254.587 47,7%
Portland (Oregon) West Online US$ 530.442 US$ 252.336 47,6%
Menor margen
Honolulu (Hawaii) West Outlet US$ 2.172.000 US$ 563.338 25,9%
Seattle (Washington) West In-store US$ 2.567.625 US$ 673.744 26,2%
Boston (Massachusetts) Northeast In-store US$ 1.055.375 US$ 315.164 29,9%
San Francisco (California) West Outlet US$ 4.070.352 US$ 1.226.550 30,1%
Anchorage (Alaska) West Outlet US$ 1.438.075 US$ 434.021 30,2%
Orlando (Florida) Southeast In-store US$ 2.099.750 US$ 654.494 31,2%
Las Vegas (Nevada) West In-store US$ 1.155.875 US$ 365.713 31,6%
Orlando (Florida) Southeast Outlet US$ 1.164.275 US$ 391.412 33,6%

Evidencia. Las combinaciones más rentables son ventas Online en ciudades del Sur, como Dallas (54,8%), y el Outlet de Birmingham (52,3%). Online también gana bien en estados del grupo a Corregir, como Albany, en New York (50,4%). Las menos rentables están en West: el Outlet de Honolulu (25,9%) y el de San Francisco (30,1%), que además vende US$ 4,07 millones. En la Figura 3, el Outlet gana 44,6% en South y 32,8% en West, y Online gana 48,6% en Northeast aunque pesa solo 18,4% de las ventas de esa región.

Interpretación. El mismo canal puede dar casi 12 puntos más de margen según el mercado, y Online funciona bien incluso donde la región gana poco.

Implicación para el negocio. Hay espacio para impulsar Online en Northeast, y el modelo de Outlet de South es una práctica que vale la pena estudiar para West.

Hallazgo 4 (no evidente a simple vista). Precio, volumen y utilidad: lo que cambia al comparar casos parecidos

Pregunta: ¿cómo se relacionan precio, volumen y utilidad, y la relación que se ve en el total se mantiene por región, producto y estado?

Una correlación del total puede deberse a la composición de las ventas. Por eso la calculamos también dentro de cada región, de cada producto, de cada estado y de cada combinación de estado, producto y método, restando a cada registro el promedio de su grupo.

# Correlación dentro de grupos: se resta a cada valor el promedio de su grupo antes de correlacionar
cor_dentro <- function(x, y, grupo) cor(x - ave(x, grupo), y - ave(y, grupo))

grupo_region   <- datos$region
grupo_producto <- datos$producto
grupo_estado   <- datos$estado
grupo_fino     <- paste(datos$estado, datos$producto, datos$metodo_venta)

relaciones <- data.frame(
  `Relación` = c("Precio y unidades", "Precio y margen", "Unidades y margen", "Unidades y utilidad"),
  Total = c(cor(datos$precio_unidad, datos$unidades_vendidas),
            cor(datos$precio_unidad, datos$margen_operativo),
            cor(datos$unidades_vendidas, datos$margen_operativo),
            cor(datos$unidades_vendidas, datos$utilidad_operativa)),
  `Dentro de cada región` = c(cor_dentro(datos$precio_unidad, datos$unidades_vendidas, grupo_region),
                              cor_dentro(datos$precio_unidad, datos$margen_operativo, grupo_region),
                              cor_dentro(datos$unidades_vendidas, datos$margen_operativo, grupo_region),
                              cor_dentro(datos$unidades_vendidas, datos$utilidad_operativa, grupo_region)),
  `Dentro de cada producto` = c(cor_dentro(datos$precio_unidad, datos$unidades_vendidas, grupo_producto),
                                cor_dentro(datos$precio_unidad, datos$margen_operativo, grupo_producto),
                                cor_dentro(datos$unidades_vendidas, datos$margen_operativo, grupo_producto),
                                cor_dentro(datos$unidades_vendidas, datos$utilidad_operativa, grupo_producto)),
  `Dentro de cada estado` = c(cor_dentro(datos$precio_unidad, datos$unidades_vendidas, grupo_estado),
                              cor_dentro(datos$precio_unidad, datos$margen_operativo, grupo_estado),
                              cor_dentro(datos$unidades_vendidas, datos$margen_operativo, grupo_estado),
                              cor_dentro(datos$unidades_vendidas, datos$utilidad_operativa, grupo_estado)),
  `Dentro de estado, producto y método` = c(cor_dentro(datos$precio_unidad, datos$unidades_vendidas, grupo_fino),
                                            cor_dentro(datos$precio_unidad, datos$margen_operativo, grupo_fino),
                                            cor_dentro(datos$unidades_vendidas, datos$margen_operativo, grupo_fino),
                                            cor_dentro(datos$unidades_vendidas, datos$utilidad_operativa, grupo_fino)),
  check.names = FALSE)

relaciones %>%
  mutate(across(-`Relación`, ~ num(.x, 2))) %>%
  kable(caption = "Tabla 12. Correlación de Pearson en el total y dentro de segmentos", align = c("l", rep("r", 5))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 12. Correlación de Pearson en el total y dentro de segmentos
Relación Total Dentro de cada región Dentro de cada producto Dentro de cada estado Dentro de estado, producto y método
Precio y unidades 0,27 0,26 0,29 0,28 0,33
Precio y margen -0,14 -0,07 -0,16 0,00 -0,05
Unidades y margen -0,31 -0,33 -0,35 -0,39 -0,45
Unidades y utilidad 0,87 0,87 0,87 0,85 0,84

Evidencia. En el total, un precio más alto se asocia con menor margen (−0,14). Esa relación se mantiene dentro de cada producto (−0,16), se debilita dentro de cada región (−0,07) y desaparece dentro de cada estado (0,00). La relación entre precio y unidades es positiva en todos los niveles (entre 0,26 y 0,33), y la de unidades y margen se vuelve más fuerte al comparar casos parecidos (de −0,31 a −0,45).

Interpretación. La relación negativa entre precio y margen es un efecto de composición: aparece porque los estados caros son los de margen bajo. La relación entre unidades y utilidad (0,87) es alta porque la utilidad depende directamente de las ventas, así que no es un hallazgo en sí misma.

Pregunta: ¿los estados con precio más alto tienen mejor o peor margen?

ggplotly(
  ggplot(resumen_estado, aes(x = precio_medio, y = margen,
                             text = paste0(estado, "\nPrecio medio: US$ ", round(precio_medio, 1), "\nMargen: ", pct(margen)))) +
    geom_point(color = "steelblue", size = 2.5, alpha = 0.8) +
    geom_smooth(aes(group = 1), method = "lm", se = FALSE, color = "grey40") +
    labs(title = "Figura 9. Precio medio vs. margen, por estado",
         x = "Precio medio por unidad (US$)", y = "Margen operativo agregado (%)") +
    tema,
  tooltip = "text")

Evidencia. Entre estados, la correlación entre precio medio y margen es −0,57. Washington y Hawaii venden a US$ 69,1 y US$ 67,7 por unidad, con márgenes de 28,8% y 28,5%.

Interpretación. Los mercados más caros no son los más rentables. Esto confirma que la relación negativa entre precio y margen viene de la mezcla de estados.

Pregunta: dentro de un mismo estado, producto y método, ¿los periodos en que se venden más unidades dejan mejor o peor margen?

Para comparar casos similares, marcamos como de volumen alto los registros cuyas unidades superan la mediana de su mismo estado, producto y método de venta.

datos_volumen <- datos %>%
  group_by(estado, producto, metodo_venta) %>%
  mutate(volumen = ifelse(unidades_vendidas > median(unidades_vendidas), "Volumen alto", "Volumen bajo o medio")) %>%
  ungroup()

resumen_volumen <- datos_volumen %>%
  group_by(metodo_venta, volumen) %>%
  summarise(ventas = sum(ventas_total),
            utilidad = sum(utilidad_operativa),
            unidades = sum(unidades_vendidas), .groups = "drop") %>%
  mutate(margen = utilidad / ventas * 100,
         precio_medio = ventas / unidades)

ggplotly(
  ggplot(resumen_volumen, aes(x = metodo_venta, y = margen, fill = volumen,
                              text = paste0(metodo_venta, " · ", volumen, "\nMargen: ", pct(margen),
                                            "\nPrecio medio: US$ ", round(precio_medio, 1)))) +
    geom_col(position = "dodge") +
    scale_fill_manual(values = c("Volumen alto" = "navy", "Volumen bajo o medio" = "lightsteelblue")) +
    labs(title = "Figura 10. Margen según el volumen vendido",
         x = "Método de venta", y = "Margen operativo agregado (%)", fill = "") +
    tema,
  tooltip = "text") %>%
  layout(legend = list(orientation = "h", y = -0.15))

Evidencia. Comparando registros del mismo estado, producto y método, en Online los periodos de mayor volumen ganan 41,3%, frente a 49,3% en los demás. En Outlet la caída es menor (37,3% frente a 38,9%) y en In-store no hay caída (36,2% frente a 35,2%).

Interpretación. Vender más unidades en un mismo mercado se asocia con un margen menor, sobre todo en Online.

resumen_volumen %>%
  transmute(`Método` = metodo_venta, Volumen = volumen, Margen = pct(margen),
            `Precio medio por unidad` = paste0("US$ ", num(precio_medio, 1))) %>%
  kable(caption = "Tabla 13. Margen y precio según el volumen vendido", align = c("l", "l", "r", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 13. Margen y precio según el volumen vendido
Método Volumen Margen Precio medio por unidad
In-store Volumen alto 36,2% US$ 55,0
In-store Volumen bajo o medio 35,2% US$ 47,7
Online Volumen alto 41,3% US$ 49,0
Online Volumen bajo o medio 49,3% US$ 45,1
Outlet Volumen alto 37,3% US$ 49,9
Outlet Volumen bajo o medio 38,9% US$ 39,2

Evidencia. En los periodos de volumen alto el precio por unidad es más alto: en Online, US$ 49,0 frente a US$ 45,1.

Interpretación. La caída del margen no parece venir de descuentos. Podría deberse a mayores costos al atender los picos de demanda, como envíos o logística, pero es una hipótesis.

Implicación para el negocio. Si Online crece solo con más volumen, probablemente su margen baje, por eso proponemos empezar con una prueba controlada. Estas correlaciones no permiten decidir precios; para eso se necesitarían datos de costos y la fecha de cada venta.

Hallazgo 5 (no evidente a simple vista). Pocas combinaciones generan la mitad de la utilidad

Pregunta: ¿la utilidad está repartida entre muchas combinaciones de ciudad, producto y método, o concentrada en pocas?

combinaciones <- datos %>%
  group_by(region, ciudad, estado, producto, metodo_venta) %>%
  summarise(utilidad = sum(utilidad_operativa), .groups = "drop") %>%
  arrange(desc(utilidad)) %>%
  mutate(acumulado = cumsum(utilidad) / sum(utilidad) * 100)

n_mitad <- which(combinaciones$acumulado >= 50)[1]

data.frame(Indicador = c("Combinaciones de ciudad × producto × método",
                         "Combinaciones que generan el 50% de la utilidad",
                         "Proporción de combinaciones que generan el 50% de la utilidad",
                         "Región más frecuente en esas combinaciones",
                         "Método de venta más frecuente en esas combinaciones"),
           Valor = c(nrow(combinaciones), n_mitad, pct(n_mitad / nrow(combinaciones) * 100),
                     names(sort(table(head(combinaciones$region, n_mitad)), decreasing = TRUE))[1],
                     names(sort(table(head(combinaciones$metodo_venta, n_mitad)), decreasing = TRUE))[1])) %>%
  kable(caption = "Tabla 14. Concentración de la utilidad", align = c("l", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 14. Concentración de la utilidad
Indicador Valor
Combinaciones de ciudad × producto × método 690
Combinaciones que generan el 50% de la utilidad 134
Proporción de combinaciones que generan el 50% de la utilidad 19,4%
Región más frecuente en esas combinaciones West
Método de venta más frecuente en esas combinaciones Online

Evidencia. De 690 combinaciones de ciudad, producto y método, 134 (el 19%) generan la mitad de la utilidad. West es la región que más aparece en ese grupo y Online el método más frecuente.

Interpretación. Aunque West gane menos por cada dólar, sus negocios grandes siguen entre los que más utilidad aportan a la empresa.

Implicación para el negocio. Hay que cuidar esas 134 combinaciones (inventario, disponibilidad y servicio al distribuidor) y mejorar el margen de West sin perder su presencia.

Recomendaciones y límites

Con base en los hallazgos, proponemos dirigir el crecimiento de las ventas hacia los mercados y canales donde cada dólar deja más utilidad, y mejorar primero el margen de los mercados grandes que hoy ganan poco. La combinación que merece atención prioritaria es Women’s Apparel en West, sobre todo en California, donde gana 19,6% frente a un margen esperado de 38,9% y deja de capturar unos US$ 322 mil (US$ 733 mil en toda la región).

Acciones priorizadas

Acción Evidencia Beneficio esperado (escenario propuesto) Riesgo o supuesto Área responsable Indicador de seguimiento
1. Crecer en los estados Estrella y de Oportunidad, con prioridad en Texas, Alabama, Tennessee, Louisiana, las Carolinas, Michigan y Ohio Hallazgo 1: margen de 44,6% (Estrella) y 42,6% (Oportunidad), frente a 36,1% de los estados a Corregir Unos US$ 2,09 M (4,4% de la utilidad) si sus ventas crecen 10% Que el margen se mantenga al crecer, porque en los periodos de más volumen el margen baja (Hallazgo 4) Gerencia comercial y Mercadeo, con Finanzas Ventas y margen de cada grupo de estados
2. Prueba piloto de Online en Northeast, y luego en West Hallazgo 3: Online gana 48,6% en Northeast, pero pesa solo 18,4% de sus ventas Unos US$ 0,24 M si el 10% de las ventas In-store y Outlet de Northeast pasa a Online En Online el margen baja 8 puntos cuando sube el volumen (Hallazgo 4), así que el beneficio podría ser menor; hay que medirlo en dólares Comercio electrónico, con Finanzas Peso de Online en las ventas de cada región y margen por canal
3. Corregir el margen en California, Washington y Hawaii, empezando por Women’s Apparel, los Outlet de San Francisco y Honolulu, y West Gear Hallazgo 2: West gana 3,2 puntos menos de lo esperado (US$ 1,16 M); Women’s Apparel gana 29,8% en West y 52,1% en South Unos US$ 0,58 M si se cierra la mitad de la brecha de West Todavía no conocemos la causa. Como todos los distribuidores ganan menos en West, cambiar de distribuidor no parece la solución Finanzas comercial, con la gerencia de West Margen de West (hoy 35,7%) y de Women’s Apparel en West (hoy 29,8%)
# Escenario 1: 10% más de ventas en estados Estrella y Oportunidad, con su margen actual
crecer <- resumen_estado %>% filter(grupo %in% c("Estrella", "Oportunidad"))
escenario_1 <- sum(crecer$ventas) * 0.10 * (sum(crecer$utilidad) / sum(crecer$ventas))

# Escenario 2: 10% de las ventas In-store y Outlet de Northeast pasan a Online, con los márgenes actuales
ne <- region_metodo %>% filter(region == "Northeast")
ventas_movidas <- 0.10 * sum(ne$ventas[ne$metodo_venta != "Online"])
margen_offline <- sum(ne$utilidad[ne$metodo_venta != "Online"]) / sum(ne$ventas[ne$metodo_venta != "Online"])
margen_online  <- ne$utilidad[ne$metodo_venta == "Online"] / ne$ventas[ne$metodo_venta == "Online"]
escenario_2 <- ventas_movidas * (margen_online - margen_offline)

# Escenario 3: cerrar la mitad de la diferencia de West frente al margen esperado
escenario_3 <- -brecha_region$brecha_usd[brecha_region$region == "West"] / 2

data.frame(`Acción` = c("Acción 1", "Acción 2", "Acción 3"),
           Escenario = c("Las ventas de los estados Estrella y de Oportunidad crecen 10%",
                         "El 10% de las ventas In-store y Outlet de Northeast pasa a Online",
                         "Se cierra la mitad de la brecha de West frente a lo esperado"),
           `Utilidad adicional` = usd(c(escenario_1, escenario_2, escenario_3)),
           `% de la utilidad actual` = pct(c(escenario_1, escenario_2, escenario_3) / utilidad_tot * 100),
           check.names = FALSE) %>%
  kable(caption = "Tabla 15. Beneficio esperado de cada acción (escenarios propuestos, con márgenes constantes)", align = c("l", "l", "r", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 15. Beneficio esperado de cada acción (escenarios propuestos, con márgenes constantes)
Acción Escenario Utilidad adicional % de la utilidad actual
Acción 1 Las ventas de los estados Estrella y de Oportunidad crecen 10% US$ 2.094.612 4,4%
Acción 2 El 10% de las ventas In-store y Outlet de Northeast pasa a Online US$ 244.561 0,5%
Acción 3 Se cierra la mitad de la brecha de West frente a lo esperado US$ 577.849 1,2%

La Tabla 15 muestra cómo se calculó el beneficio esperado de cada acción. Son escenarios para dimensionarlas, no metas, y suponen que los márgenes actuales se mantienen.

Condiciones de aplicación

Las tres acciones deben empezar como pilotos con un indicador definido antes de arrancar. La Acción 1 puede comenzar de inmediato. Las Acciones 2 y 3 dependen de que los distribuidores entreguen datos de costos, descuentos y fecha de venta, porque sin ellos no se puede saber si el problema es de precio, de costo o del tipo de tienda.

Si los costos muestran que el margen bajo de West se debe a costos que no se pueden reducir (por ejemplo, arriendos o transporte a Hawaii), la Acción 3 pasaría de corregir el margen a ajustar precios o cobertura. Si la fecha mostrara que los estados Estrella vienen cayendo en ventas, habría que revisar la prioridad de la Acción 1.

Limitaciones

  • Sin fecha: no pudimos calcular tasas de crecimiento interanual ni identificar periodos de expansión o contracción. La base parece tener datos mensuales, pero sin la fecha el análisis solo puede ser de corte transversal.
  • Sin costos ni descuentos: las razones que damos para el margen bajo son hipótesis que habría que comprobar.
  • Periodo y mercado: las conclusiones aplican solo a Estados Unidos y al periodo que cubre la base. Los valores están en dólares.
  • Correlación no es causalidad: las correlaciones y el margen esperado describen lo que pasa, pero no explican la causa. El margen esperado es una referencia y no una meta.
  • Escenarios: suponen que los márgenes se mantienen y sirven solo como referencia.

Soporte reproducible

  • Fuente de los datos: DatosCaso1.xlsx (hoja Data Sales Adidas), suministrada por la docente del curso Analítica de Negocios, Especialización en Finanzas, Pontificia Universidad Javeriana Cali, 2026.
  • Fuente externa: U.S. Bureau of Economic Analysis (2022). Real Personal Consumption Expenditures by State and Real Personal Income by State and Metropolitan Area, 2021 (BEA 22-62), 15 de diciembre de 2022. https://www.bea.gov/sites/default/files/2022-12/rpp1222.pdf
  • Cómo reproducir: guardar el archivo de Excel en la misma carpeta de este .Rmd y ejecutar Knit. El código de cada tabla y gráfico se puede ver con el botón Show de cada bloque, y el .Rmd se puede descargar con el botón Code de arriba.

El informe se generó con R version 4.6.1 (2026-06-24 ucrt).

paquetes <- c("readxl", "dplyr", "tidyr", "ggplot2", "plotly", "knitr", "kableExtra", "rmarkdown")
data.frame(Paquete = paquetes,
           `Versión` = sapply(paquetes, function(p) as.character(packageVersion(p))),
           check.names = FALSE, row.names = NULL) %>%
  kable(caption = "Paquetes utilizados") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Paquetes utilizados
Paquete Versión
readxl 1.6.0
dplyr 1.2.1
tidyr 1.3.2
ggplot2 4.0.3
plotly 4.12.1
knitr 1.52
kableExtra 1.4.1
rmarkdown 2.32
LS0tDQp0aXRsZTogIkNhc28gMSDCtyBBbmFsw610aWNhIGRlc2NyaXB0aXZhIHkgZGlhZ27Ds3N0aWNhOiBkw7NuZGUgZ2FuYSBBZGlkYXMgeSBjw7NtbyBzZWd1aXIgaW1wdWxzYW5kbyBsYXMgdmVudGFzIg0KYXV0aG9yOiAiQ8Opc2FyIEFuZHLDqXMgR2FyY8OtYSBWYXJvbmEgeSBPc3dhbGRvIEZpZXJybyINCmRhdGU6ICIyMDI2LTExLTA4Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBkZl9wcmludDoga2FibGUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgbWRfZXh0ZW5zaW9uczogLXRleF9tYXRoX2RvbGxhcnMNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgcmVzdWx0cyA9ICJzaG93IiwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gOS41LCBmaWcuaGVpZ2h0ID0gNSwgZmlnLmFsaWduID0gImNlbnRlciIpDQpgYGANCg0KKipWaWRlbyBkZSBzdXN0ZW50YWNpw7NuOioqIFtFTkxBQ0UgQUwgVklERU9dDQoNCioqQXNpZ25hdHVyYToqKiBBbmFsw610aWNhIGRlIE5lZ29jaW9zIMK3IEVzcGVjaWFsaXphY2nDs24gZW4gRmluYW56YXMgwrcgUG9udGlmaWNpYSBVbml2ZXJzaWRhZCBKYXZlcmlhbmEgQ2FsaSDCtyBQcm9mZXNvcmEgSnVsaWV0aCBTdGVmZW5zIENlcsOzbg0KDQpgYGB7Y3NzLCBlY2hvPUZBTFNFfQ0KLyogRGlzZcOxbzogY29udGVuaWRvIGNlbnRyYWRvLCB0w610dWxvcyB5IHRhYmxhcyBsZWdpYmxlcyAqLw0KLm1haW4tY29udGFpbmVyIHsgbWF4LXdpZHRoOiAxMTUwcHg7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCmgxIHsgY29sb3I6ICMxZjNiNWE7IGJvcmRlci1ib3R0b206IDJweCBzb2xpZCAjZDllMmVjOyBwYWRkaW5nLWJvdHRvbTogNnB4OyBtYXJnaW4tdG9wOiA0MHB4OyB9DQpoMiB7IGNvbG9yOiAjMmM1MjgyOyBtYXJnaW4tdG9wOiAzMHB4OyB9DQpjYXB0aW9uIHsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiAjMzMzMzMzOyB0ZXh0LWFsaWduOiBjZW50ZXI7IGNhcHRpb24tc2lkZTogdG9wOyB9DQp0YWJsZSB7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCi5odG1sLXdpZGdldCB7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCnAgeyB0ZXh0LWFsaWduOiBqdXN0aWZ5OyB9DQpgYGANCg0KYGBge3IgbG9nbywgZWNobz1GQUxTRSwgZmlnLmFsaWduPSJjZW50ZXIiLCBvdXQud2lkdGg9IjM1JSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiNjg5MzQ3LmpwZyIpDQpgYGANCg0KIyAqKlJlc3VtZW4gZWplY3V0aXZvKioNCg0KQWRpZGFzIG5lY2VzaXRhIHNhYmVyIGTDs25kZSB5IHBvciBxdcOpIG3DqXRvZG8gZGUgdmVudGEgaW1wdWxzYXIgc3VzIHZlbnRhcyBlbiBFc3RhZG9zIFVuaWRvcyBzaW4gc2FjcmlmaWNhciByZW50YWJpbGlkYWQuIEFuYWxpemFtb3MgOS42NDQgcmVnaXN0cm9zIGRlIHZlbnRhcyAoVVMkIDEyMCwyIG1pbGxvbmVzIGVuIHZlbnRhcywgVVMkIDQ3LDIgbWlsbG9uZXMgZGUgdXRpbGlkYWQgb3BlcmF0aXZhIHkgMzksMyUgZGUgbWFyZ2VuKSB5IGVuY29udHJhbW9zIHF1ZSB2ZW5kZXIgbcOhcyBubyBzaWVtcHJlIGVzIGdhbmFyIG3DoXM6IGVsIG1hcmdlbiB2YSBkZSAyOCw1JSBlbiBIb25vbHVsdSBhIDU0LDQlIGVuIEJpcm1pbmdoYW0sIHkgdW4gbWlsbMOzbiBkZSBkw7NsYXJlcyBhZGljaW9uYWwgZGVqYSBjZXJjYSBkZSBVUyQgNDQ2IG1pbCBkZSB1dGlsaWRhZCBlbiBsb3Mgc2lldGUgZXN0YWRvcyBxdWUgbGxhbWFtb3MgRXN0cmVsbGEsIGZyZW50ZSBhIFVTJCAzNjEgbWlsIGVuIGxvcyBvbmNlIGVzdGFkb3MgZ3JhbmRlcyBkZSBtYXJnZW4gYmFqbyAoQ2FsaWZvcm5pYSwgTmV3IFlvcmsgeSBGbG9yaWRhLCBlbnRyZSBvdHJvcykuIFdlc3QgZXMgbGEgcmVnacOzbiBxdWUgbcOhcyB2ZW5kZSAoMzAsMyUpIHkgbGEgZGUgbWVub3IgbWFyZ2VuICgzNSw3JSk7IGdhbmEgMywyIHB1bnRvcyBtZW5vcyBkZSBsbyBxdWUgc3UgbWV6Y2xhIGRlIHByb2R1Y3RvcyB5IGNhbmFsZXMgcGVybWl0aXLDrWEsIHkgdG9kb3MgbG9zIGRpc3RyaWJ1aWRvcmVzIGdhbmFuIG1lbm9zIGFsbMOtLiBPbmxpbmUgZXMgZWwgbcOpdG9kbyBjb24gbWVqb3IgbWFyZ2VuIGVuIDQyIGRlIGxvcyA1MCBlc3RhZG9zLCBhdW5xdWUgc3UgbWFyZ2VuIGJhamEgZGUgNDksMyUgYSA0MSwzJSBlbiBsb3MgcGVyaW9kb3MgZGUgbWF5b3Igdm9sdW1lbi4gUmVjb21lbmRhbW9zIGNyZWNlciBwcmltZXJvIGVuIGxvcyBlc3RhZG9zIEVzdHJlbGxhIHkgZGUgT3BvcnR1bmlkYWQsIHByb2JhciBkZSBmb3JtYSBjb250cm9sYWRhIGVsIGltcHVsc28gYSBPbmxpbmUgZW4gTm9ydGhlYXN0IHkgV2VzdCwgeSBjb3JyZWdpciBlbCBtYXJnZW4gZGUgV29tZW4ncyBBcHBhcmVsIGVuIENhbGlmb3JuaWEsIFdhc2hpbmd0b24geSBIYXdhaWkgYW50ZXMgZGUgYnVzY2FyIG3DoXMgdm9sdW1lbiBhbGzDrS4gTG9zIGJlbmVmaWNpb3MgZXN0aW1hZG9zIHNvbiBlc2NlbmFyaW9zLCBubyByZXN1bHRhZG9zIGdhcmFudGl6YWRvcy4NCg0KIyAqKkRhdG9zIHkgcHJlcGFyYWNpw7NuKioNCg0KIyMgQWxjYW5jZSB5IHVuaWRhZCBkZSBvYnNlcnZhY2nDs24NCg0KQ2FkYSBmaWxhIGRlIGxhIGJhc2UgZXMgZWwgcmVnaXN0cm8gZGUgdmVudGFzIGRlIHVuIHByb2R1Y3RvLCB2ZW5kaWRvIHBvciB1biBkaXN0cmlidWlkb3IsIGVuIHVuYSBjaXVkYWQgeSBwb3IgdW4gbcOpdG9kbyBkZSB2ZW50YS4gUG9yIGxhIGVzdHJ1Y3R1cmEgZGUgbGEgYmFzZSAodmVyIGxhIHJldmlzacOzbiBkZSBjYWxpZGFkKSwgY2FkYSBmaWxhIHBhcmVjZSBjb3JyZXNwb25kZXIgYSB1biBtZXMsIGF1bnF1ZSBsYSBmZWNoYSBubyB2aWVuZSBlbiBlbCBhcmNoaXZvLiBMYSBiYXNlIGN1YnJlIDUwIGVzdGFkb3MgYWdydXBhZG9zIGVuIDUgcmVnaW9uZXMsIDU0IGNpdWRhZGVzLCA2IGRpc3RyaWJ1aWRvcmVzLCA2IHByb2R1Y3RvcyB5IDMgbcOpdG9kb3MgZGUgdmVudGEgKEluLXN0b3JlLCBPbmxpbmUgeSBPdXRsZXQpLiBMYSBiYXNlIG5vIGluZGljYSBsYSBtb25lZGE7IHRyYWJhamFtb3MgZW4gZMOzbGFyZXMgcG9ycXVlIGxhcyB2ZW50YXMgc29uIGVuIEVzdGFkb3MgVW5pZG9zLiBUYW1wb2NvIGluZGljYSBlbCBwZXJpb2RvLCBhc8OtIHF1ZSBsYXMgY29uY2x1c2lvbmVzIHNlIGxpbWl0YW4gYSBsbyBxdWUgY29udGllbmUgbGEgYmFzZS4NCg0KKipQQVNPIDEuIENhcmdhciBsaWJyZXLDrWFzKioNCg0KYGBge3IgbGlicmVyaWFzfQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCg0KIyBFc3RpbG8gY29tw7puIHBhcmEgdG9kb3MgbG9zIGdyw6FmaWNvcw0KdGVtYSA8LSB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgZmFjZSA9ICJib2xkIikpDQpgYGANCg0KKipQQVNPIDIuIENhcmdhciBsYSBiYXNlIGRlIGRhdG9zKioNCg0KYGBge3IgY2FyZ2F9DQpkYXRvcyA8LSByZWFkX2V4Y2VsKCJEYXRvc0Nhc28xLm1vZGlmaWNhZG8ueGxzeCIsDQogICAgY29sX3R5cGVzID0gYygidGV4dCIsICJ0ZXh0IiwgInRleHQiLA0KICAgICAgICAidGV4dCIsICJ0ZXh0IiwgIm51bWVyaWMiLCAibnVtZXJpYyIsDQogICAgICAgICJudW1lcmljIiwgIm51bWVyaWMiLCAibnVtZXJpYyIsDQogICAgICAgICJ0ZXh0IikpDQoNCiMgVmVyIGxhcyBwcmltZXJhcyBmaWxhcyBkZSBsYSBiYXNlDQpoZWFkKGRhdG9zKSAlPiUga2FibGUoKSAlPiUga2FibGVfc3R5bGluZyhmb250X3NpemUgPSAxMSkgJT4lIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KYGBge3IgZm9ybWF0b3N9DQojIEZ1bmNpb25lcyBwYXJhIG1vc3RyYXIgZMOzbGFyZXMgeSBwb3JjZW50YWplcyBjb24gY29tYSBkZWNpbWFsIGVuIGxhcyB0YWJsYXMNCnVzZCA8LSBmdW5jdGlvbih4KSBwYXN0ZTAoaWZlbHNlKHggPCAwLCAiLSIsICIiKSwgIlVTJCAiLCBmb3JtYXQocm91bmQoYWJzKHgpKSwgYmlnLm1hcmsgPSAiLiIsIGRlY2ltYWwubWFyayA9ICIsIiwgc2NpZW50aWZpYyA9IEZBTFNFLCB0cmltID0gVFJVRSkpDQpwY3QgPC0gZnVuY3Rpb24oeCkgcGFzdGUwKGZvcm1hdChyb3VuZCh4LCAxKSwgbnNtYWxsID0gMSwgZGVjaW1hbC5tYXJrID0gIiwiLCB0cmltID0gVFJVRSksICIlIikNCm51bSA8LSBmdW5jdGlvbih4LCBkID0gMCkgZm9ybWF0KHJvdW5kKHgsIGQpLCBuc21hbGwgPSBkLCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiLCBzY2llbnRpZmljID0gRkFMU0UsIHRyaW0gPSBUUlVFKQ0KYGBgDQoNCnwgVmFyaWFibGUgfCBUaXBvIHwgRXNjYWxhIHwgVW5pZGFkIHwNCnwtLS18LS0tfC0tLXwtLS18DQp8IGRpc3RyaWJ1aWRvciwgcmVnacOzbiwgZXN0YWRvLCBjaXVkYWQsIHByb2R1Y3RvLCBtw6l0b2RvIGRlIHZlbnRhIHwgQ3VhbGl0YXRpdmEgfCBOb21pbmFsIHwgQ2F0ZWdvcsOtYXMgfA0KfCBwcmVjaW9fdW5pZGFkIHwgQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIHwgUmF6w7NuIHwgVVMkIHBvciB1bmlkYWQgfA0KfCB1bmlkYWRlc192ZW5kaWRhcyB8IEN1YW50aXRhdGl2YSBkaXNjcmV0YSB8IFJhesOzbiB8IFVuaWRhZGVzIHwNCnwgdmVudGFzX3RvdGFsLCB1dGlsaWRhZF9vcGVyYXRpdmEgfCBDdWFudGl0YXRpdmEgY29udGludWEgfCBSYXrDs24gfCBVUyQgfA0KfCBtYXJnZW5fb3BlcmF0aXZvIHwgQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIHwgUmF6w7NuIHwgUHJvcG9yY2nDs24gKDAsMzUgPSAzNSUpIHwNCg0KKipQQVNPIDMuIFJldmlzYXIgbGEgY2FsaWRhZCBkZSBsb3MgZGF0b3MqKg0KDQpgYGB7ciBjYWxpZGFkfQ0KIyAxLiBGb3JtYXRvIHkgdmFsb3JlcyBmYWx0YW50ZXMNCnRpcG9zICAgICAgPC0gc2FwcGx5KGRhdG9zLCBjbGFzcykNCmZhbHRhbnRlcyAgPC0gc3VtKGlzLm5hKGRhdG9zKSkNCg0KIyAyLiBDb2hlcmVuY2lhOiB2ZW50YXMgPSBwcmVjaW8geCB1bmlkYWRlcyB5IHV0aWxpZGFkID0gdmVudGFzIHggbWFyZ2VuIChkaWZlcmVuY2lhcyBtYXlvcmVzIGEgMSBkw7NsYXIpDQplcnJvcl92ZW50YXMgICA8LSBzdW0oYWJzKGRhdG9zJHZlbnRhc190b3RhbCAtIGRhdG9zJHByZWNpb191bmlkYWQgKiBkYXRvcyR1bmlkYWRlc192ZW5kaWRhcykgPiAxKQ0KZXJyb3JfdXRpbGlkYWQgPC0gc3VtKGFicyhkYXRvcyR1dGlsaWRhZF9vcGVyYXRpdmEgLSBkYXRvcyR2ZW50YXNfdG90YWwgKiBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvKSA+IDEpDQoNCiMgMy4gUmVnaXN0cm9zIGNvbiB2ZW50YXMgZW4gY2Vybw0KY2Vyb3MgPC0gZGF0b3MgJT4lIGZpbHRlcih2ZW50YXNfdG90YWwgPT0gMCkNCg0KIyA0LiBGaWxhcyBkdXBsaWNhZGFzIChsYXMgMTEgY29sdW1uYXMgaWTDqW50aWNhcykgeSBtYXJnZW4gYWdyZWdhZG8gY29uIHkgc2luIGVsbGFzDQpkdXBsaWNhZG9zICAgICA8LSBzdW0oZHVwbGljYXRlZChkYXRvcykpDQpzaW5fZHVwbGljYWRvcyA8LSBkaXN0aW5jdChkYXRvcykNCm1hcmdlbl9jb24gICAgIDwtIHN1bShkYXRvcyR1dGlsaWRhZF9vcGVyYXRpdmEpIC8gc3VtKGRhdG9zJHZlbnRhc190b3RhbCkgKiAxMDANCm1hcmdlbl9zaW4gICAgIDwtIHN1bShzaW5fZHVwbGljYWRvcyR1dGlsaWRhZF9vcGVyYXRpdmEpIC8gc3VtKHNpbl9kdXBsaWNhZG9zJHZlbnRhc190b3RhbCkgKiAxMDANCg0KIyA1LiBDaXVkYWRlcyBjb24gZWwgbWlzbW8gbm9tYnJlIGVuIGVzdGFkb3MgZGlzdGludG9zDQpob21vbmltYXMgPC0gZGF0b3MgJT4lIGRpc3RpbmN0KGNpdWRhZCwgZXN0YWRvKSAlPiUgY291bnQoY2l1ZGFkKSAlPiUgZmlsdGVyKG4gPiAxKQ0KDQojIDYuIFZhbG9yZXMgYXTDrXBpY29zIGNvbiBsYSByZWdsYSBkZSBUdWtleSAocG9yIGZ1ZXJhIGRlIFExIC0gMSw1IFJJQyB5IFEzICsgMSw1IFJJQykNCmxpbWl0ZV92ZW50YXMgPC0gcXVhbnRpbGUoZGF0b3MkdmVudGFzX3RvdGFsLCAwLjc1KSArIDEuNSAqIElRUihkYXRvcyR2ZW50YXNfdG90YWwpDQphdGlwaWNvc192ZW50YXMgPC0gZGF0b3MgJT4lIGZpbHRlcih2ZW50YXNfdG90YWwgPiBsaW1pdGVfdmVudGFzKQ0KbGltaXRlc19tYXJnZW4gPC0gcXVhbnRpbGUoZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgYygwLjI1LCAwLjc1KSkgKyBjKC0xLjUsIDEuNSkgKiBJUVIoZGF0b3MkbWFyZ2VuX29wZXJhdGl2bykNCmF0aXBpY29zX21hcmdlbiA8LSBzdW0oZGF0b3MkbWFyZ2VuX29wZXJhdGl2byA8IGxpbWl0ZXNfbWFyZ2VuWzFdIHwgZGF0b3MkbWFyZ2VuX29wZXJhdGl2byA+IGxpbWl0ZXNfbWFyZ2VuWzJdKQ0KDQojIDcuIFJlZ2lzdHJvcyBwb3IgY29tYmluYWNpw7NuIGNpdWRhZCArIHByb2R1Y3RvICsgbcOpdG9kbyAowr9sYSBiYXNlIHBhcmVjZSBtZW5zdWFsPykNCnJlZ2lzdHJvc19jb21iaW5hY2lvbiA8LSBkYXRvcyAlPiUgY291bnQoY2l1ZGFkLCBlc3RhZG8sIHByb2R1Y3RvLCBtZXRvZG9fdmVudGEpDQpmaWxhc18xMl8yNCA8LSBzdW0ocmVnaXN0cm9zX2NvbWJpbmFjaW9uJG5bcmVnaXN0cm9zX2NvbWJpbmFjaW9uJG4gJWluJSBjKDEyLCAyNCldKSAvIG5yb3coZGF0b3MpICogMTAwDQoNCiMgVGFibGEgcmVzdW1lbiBkZSBsYSByZXZpc2nDs24NCnJldmlzaW9uIDwtIGRhdGEuZnJhbWUoDQogIGBSZXZpc2nDs25gID0gYygiRm9ybWF0byBkZSBsYXMgdmFyaWFibGVzIiwNCiAgICAgICAgICAgICAgICAgIlZhbG9yZXMgZmFsdGFudGVzIiwNCiAgICAgICAgICAgICAgICAgIlZlbnRhcyBxdWUgbm8gY3VhZHJhbiBjb24gcHJlY2lvIMOXIHVuaWRhZGVzIiwNCiAgICAgICAgICAgICAgICAgIlV0aWxpZGFkIHF1ZSBubyBjdWFkcmEgY29uIHZlbnRhcyDDlyBtYXJnZW4iLA0KICAgICAgICAgICAgICAgICAiUmVnaXN0cm9zIGNvbiB2ZW50YXMgZW4gY2VybyIsDQogICAgICAgICAgICAgICAgICJGaWxhcyBkdXBsaWNhZGFzICgxMSBjb2x1bW5hcyBpZ3VhbGVzKSIsDQogICAgICAgICAgICAgICAgICJDaXVkYWRlcyBjb24gZWwgbWlzbW8gbm9tYnJlIGVuIGRvcyBlc3RhZG9zIiwNCiAgICAgICAgICAgICAgICAgIkF0w61waWNvcyBlbiB2ZW50YXMgcG9yIHJlZ2lzdHJvIiwNCiAgICAgICAgICAgICAgICAgIkF0w61waWNvcyBlbiBtYXJnZW4gcG9yIHJlZ2lzdHJvIiwNCiAgICAgICAgICAgICAgICAgIlJlZ2lzdHJvcyBwb3IgY29tYmluYWNpw7NuIGNpdWRhZCDDlyBwcm9kdWN0byDDlyBtw6l0b2RvIiksDQogIFJlc3VsdGFkbyA9IGMocGFzdGUwKHN1bSh0aXBvcyA9PSAiY2hhcmFjdGVyIiksICIgZGUgdGV4dG8geSAiLCBzdW0odGlwb3MgPT0gIm51bWVyaWMiKSwgIiBudW3DqXJpY2FzIiksDQogICAgICAgICAgICAgICAgZmFsdGFudGVzLA0KICAgICAgICAgICAgICAgIGVycm9yX3ZlbnRhcywNCiAgICAgICAgICAgICAgICBlcnJvcl91dGlsaWRhZCwNCiAgICAgICAgICAgICAgICBpZmVsc2UobnJvdyhjZXJvcykgPiAwLCBucm93KGNlcm9zKSwgIjAgZW4gZWwgYXJjaGl2byBkZSB0cmFiYWpvICg0IGVuIGxhIGJhc2Ugb3JpZ2luYWwpIiksDQogICAgICAgICAgICAgICAgcGFzdGUwKGR1cGxpY2Fkb3MsICIgKCIsIHBjdChkdXBsaWNhZG9zIC8gbnJvdyhkYXRvcykgKiAxMDApLCAiIGRlIGxhcyBmaWxhcykiKSwNCiAgICAgICAgICAgICAgICBwYXN0ZShob21vbmltYXMkY2l1ZGFkLCBjb2xsYXBzZSA9ICIgeSAiKSwNCiAgICAgICAgICAgICAgICBwYXN0ZTAobnVtKG5yb3coYXRpcGljb3NfdmVudGFzKSksICIgcmVnaXN0cm9zICgiLCBwY3QobnJvdyhhdGlwaWNvc192ZW50YXMpIC8gbnJvdyhkYXRvcykgKiAxMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAiKSBwb3IgZW5jaW1hIGRlICIsIHVzZChsaW1pdGVfdmVudGFzKSwgIiwgcXVlIHN1bWFuICIsDQogICAgICAgICAgICAgICAgICAgICAgIHBjdChzdW0oYXRpcGljb3NfdmVudGFzJHZlbnRhc190b3RhbCkgLyBzdW0oZGF0b3MkdmVudGFzX3RvdGFsKSAqIDEwMCksICIgZGUgbGFzIHZlbnRhcyIpLA0KICAgICAgICAgICAgICAgIHBhc3RlMChhdGlwaWNvc19tYXJnZW4sICIgcmVnaXN0cm9zIHBvciBmdWVyYSBkZSAiLCBwY3QobGltaXRlc19tYXJnZW5bMV0gKiAxMDApLCAiIGEgIiwgcGN0KGxpbWl0ZXNfbWFyZ2VuWzJdICogMTAwKSksDQogICAgICAgICAgICAgICAgcGFzdGUwKHBjdChmaWxhc18xMl8yNCksICIgZGUgbGFzIGZpbGFzIGVzdMOhbiBlbiBjb21iaW5hY2lvbmVzIGNvbiBleGFjdGFtZW50ZSAxMiBvIDI0IHJlZ2lzdHJvcyIpKSwNCiAgYERlY2lzacOzbmAgPSBjKCJTZSBmaWrDsyBlbCB0aXBvIGRlIGNhZGEgY29sdW1uYSBhbCBjYXJnYXIgZWwgYXJjaGl2by4gRWwgbWFyZ2VuIHZpZW5lIGNvbW8gcHJvcG9yY2nDs24geSBzZSBwcmVzZW50YSBlbiBwb3JjZW50YWplLiIsDQogICAgICAgICAgICAgICAgICJObyByZXF1aWVyZSB0cmF0YW1pZW50by4iLA0KICAgICAgICAgICAgICAgICAiTGFzIGNpZnJhcyBzb24gY29oZXJlbnRlcyB5IHNlIHVzYW4gdGFsIGNvbW8gdmllbmVuLiIsDQogICAgICAgICAgICAgICAgICJMYXMgY2lmcmFzIHNvbiBjb2hlcmVudGVzIHkgc2UgdXNhbiB0YWwgY29tbyB2aWVuZW4uIiwNCiAgICAgICAgICAgICAgICAgIkxhIGJhc2Ugb3JpZ2luYWwgdGVuw61hIDQgcmVnaXN0cm9zIGRlIEZvb3QgTG9ja2VyIGVuIE9tYWhhIChXb21lbidzIEF0aGxldGljIEZvb3R3ZWFyKSBjb24gMCB1bmlkYWRlcyB5IDAgdmVudGFzLiBTZSBlbGltaW5hcm9uIHBvcnF1ZSBubyByZXByZXNlbnRhbiB1bmEgdmVudGEgeSBzdSBtYXJnZW4gbm8gc2UgcHVlZGUgY2FsY3VsYXIuIE5vIGNhbWJpYW4gbGFzIHZlbnRhcywgbGEgdXRpbGlkYWQgbmkgZWwgbWFyZ2VuIGFncmVnYWRvLiIsDQogICAgICAgICAgICAgICAgIHBhc3RlMCgiU2UgY29uc2VydmFuOiBzaW4gZmVjaGEgbmkgZmFjdHVyYSBubyBoYXkgZXZpZGVuY2lhIGRlIGVycm9yLiBTaW4gZWxsYXMgZWwgbWFyZ2VuIHBhc2Fyw61hIGRlICIsDQogICAgICAgICAgICAgICAgICAgICAgICBwY3QobWFyZ2VuX2NvbiksICIgYSAiLCBwY3QobWFyZ2VuX3NpbiksICIgeSBuaW5ndW5hIGNvbmNsdXNpw7NuIGNhbWJpYS4iKSwNCiAgICAgICAgICAgICAgICAgIkNhZGEgY2l1ZGFkIHNlIGlkZW50aWZpY2Egc2llbXByZSBqdW50byBjb24gc3UgZXN0YWRvLiIsDQogICAgICAgICAgICAgICAgICJTZSBjb25zZXJ2YW46IHNvbiB2ZW50YXMgcmVhbGVzIHkgY29oZXJlbnRlcyBjb24gcHJlY2lvIMOXIHVuaWRhZGVzLiBQb3IgZXNvIGRlc2NyaWJpbW9zIGVsIHJlZ2lzdHJvIHTDrXBpY28gY29uIGxhIG1lZGlhbmEuIiwNCiAgICAgICAgICAgICAgICAgIlNlIGNvbnNlcnZhbjogZWwgcmFuZ28gKDEwJSBhIDgwJSkgZXMgcG9zaWJsZSBlbiB1bmEgb3BlcmFjacOzbiBjb21lcmNpYWwuIiwNCiAgICAgICAgICAgICAgICAgIkVzIGVsIHBhdHLDs24gZGUgdW5hIGJhc2UgbWVuc3VhbCAoMTIgcmVnaXN0cm9zIHBvciBhw7FvKSBzaW4gZmVjaGEuIFJlc3BhbGRhIGNvbnNlcnZhciBsb3MgZHVwbGljYWRvcyB5IGFuYWxpemFyIGNvbiB0b3RhbGVzLiIpLA0KICBjaGVjay5uYW1lcyA9IEZBTFNFKQ0KDQprYWJsZShyZXZpc2lvbiwgY2FwdGlvbiA9ICJSZXZpc2nDs24gZGUgY2FsaWRhZCBkZSBsb3MgZGF0b3MiLCBhbGlnbiA9IGMoImwiLCAibCIsICJsIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBUUlVFLCB3aWR0aCA9ICIxNGVtIikNCmBgYA0KDQoqKkNvbmNsdXNpw7NuIGRlIGxhIHJldmlzacOzbi4qKiBMYSBiYXNlIGVzdMOhIGNvbXBsZXRhIHkgc3VzIGNpZnJhcyBjdWFkcmFuIGVudHJlIHPDrS4gU29sbyBlbGltaW5hbW9zIGxvcyA0IHJlZ2lzdHJvcyBxdWUgbm8gdGVuw61hbiB2ZW50YXMsIGNvbiBsbyBxdWUgcGFzYW1vcyBkZSA5LjY0OCBhIDkuNjQ0IGZpbGFzLiBMb3MgZHVwbGljYWRvcyB5IGxvcyB2YWxvcmVzIGF0w61waWNvcyBsb3MgZGVqYW1vcywgcG9ycXVlIG5vIGhheSBldmlkZW5jaWEgZGUgcXVlIHNlYW4gZXJyb3Jlcy4NCg0KIyAqKkFuw6FsaXNpcyBleHBsb3JhdG9yaW8qKg0KDQojIyBVbmlkYWRlcywgc2VnbWVudG9zLCBwZXJpb2RvIHkgZGVub21pbmFkb3Jlcw0KDQotICoqVW5pZGFkZXM6KiogbGFzIHZlbnRhcyB5IGxhIHV0aWxpZGFkIGVzdMOhbiBlbiBkw7NsYXJlcyAoVVMkKSwgbGFzIGNhbnRpZGFkZXMgZW4gdW5pZGFkZXMgdmVuZGlkYXMgeSBlbCBtYXJnZW4gZW4gcG9yY2VudGFqZS4NCi0gKipTZWdtZW50b3M6KiogNSByZWdpb25lcywgNTAgZXN0YWRvcywgNTQgY2l1ZGFkZXMsIDYgZGlzdHJpYnVpZG9yZXMsIDYgcHJvZHVjdG9zIHkgMyBtw6l0b2RvcyBkZSB2ZW50YSAoSW4tc3RvcmUsIE9ubGluZSB5IE91dGxldCkuDQotICoqUGVyaW9kbzoqKiBsYSBiYXNlIG5vIHRyYWUgZmVjaGEuIFBvciBzdSBlc3RydWN0dXJhIHBhcmVjZSBtZW5zdWFsLCBwZXJvIG5vIHNhYmVtb3MgcXXDqSBtZXNlcyBjdWJyZSwgYXPDrSBxdWUgbG9zIHJlc3VsdGFkb3MgY29ycmVzcG9uZGVuIGFsIHRvdGFsIGFjdW11bGFkbyBkZSBsYSBiYXNlLg0KLSAqKkRlbm9taW5hZG9yZXM6KiogdG9kYXMgbGFzIHBhcnRpY2lwYWNpb25lcyAoJSkgc2UgY2FsY3VsYW4gc29icmUgZWwgdG90YWwgZGUgbGEgYmFzZTogVVMkIDEyMCwyIG1pbGxvbmVzIGVuIHZlbnRhcywgMi40NzguODYxIHVuaWRhZGVzIG8gVVMkIDQ3LDIgbWlsbG9uZXMgZGUgdXRpbGlkYWQuIEN1YW5kbyB1biBwb3JjZW50YWplIHVzYSBvdHJvIGRlbm9taW5hZG9yIChwb3IgZWplbXBsbywgZWwgcGVzbyBkZSB1biBtw6l0b2RvIGRlbnRybyBkZSB1bmEgcmVnacOzbiksIHNlIGluZGljYSBlbiBlbCB0ZXh0by4NCg0KKipJbmRpY2Fkb3JlcyB5IHBvciBxdcOpIGxvcyB1c2Ftb3MuKioNCg0KLSAqKk1hcmdlbiBvcGVyYXRpdm8gYWdyZWdhZG8qKiAodXRpbGlkYWQgdG90YWwgLyB2ZW50YXMgdG90YWxlcyDDlyAxMDApLCBwYXJhIG1lZGlyIGxhIHJlbnRhYmlsaWRhZCByZWFsIGRlIGNhZGEgc2VnbWVudG8uIERhIDM5LDMlOyBlbCBwcm9tZWRpbyBzaW1wbGUgZGUgbG9zIG3DoXJnZW5lcyBwb3IgcmVnaXN0cm8gKDQyLDMlKSBzb2JyZXN0aW1hIGxhIHJlbnRhYmlsaWRhZCBwb3JxdWUgbG9zIHJlZ2lzdHJvcyBwZXF1ZcOxb3MgcGVzYXLDrWFuIGxvIG1pc21vIHF1ZSBsb3MgZ3JhbmRlcywgYXPDrSBxdWUgbm8gbG8gdXNhbW9zLg0KLSAqKlBhcnRpY2lwYWNpw7NuICglKSoqIGVuIHZlbnRhcywgdW5pZGFkZXMgeSB1dGlsaWRhZCwgcGFyYSB2ZXIgcXXDqSBzZWdtZW50b3MgY29uY2VudHJhbiBlbCBuZWdvY2lvLg0KLSAqKk1lZGlhbmEsIGRlc3ZpYWNpw7NuIGVzdMOhbmRhciB5IGNvZWZpY2llbnRlIGRlIHZhcmlhY2nDs24qKiwgcG9ycXVlIGxhcyB2ZW50YXMgdGllbmVuIHZhbG9yZXMgbXV5IGFsdG9zIHF1ZSBpbmZsYW4gZWwgcHJvbWVkaW8geSBxdWVyZW1vcyBtZWRpciBsYSBkaXNwZXJzacOzbi4NCi0gKipVdGlsaWRhZCBwb3IgcmVnaXN0cm8qKiwgcGFyYSBjb21wYXJhciBlbCB0YW1hw7FvIGRlIGxhIHV0aWxpZGFkIGVuIGTDs2xhcmVzIGNvbiBlbCBtYXJnZW4gZW4gcG9yY2VudGFqZS4NCi0gKipDb3JyZWxhY2nDs24gZGUgUGVhcnNvbioqICh5IGRlIFNwZWFybWFuIGVudHJlIGVzdGFkb3MpLCBwYXJhIGRlc2NyaWJpciByZWxhY2lvbmVzIGVudHJlIHZhcmlhYmxlcywgc2luIGFzdW1pciBxdWUgdW5hIGNhdXNhIGxhIG90cmEuDQotICoqTWFyZ2VuIGVzcGVyYWRvIHBvciBsYSBtZXpjbGEgZGUgcHJvZHVjdG8geSBtw6l0b2RvKiosIHBhcmEgc2FiZXIgc2kgdW5hIHJlZ2nDs24gZ2FuYSBtZW5vcyBwb3IgbG8gcXVlIHZlbmRlIG8gcG9yIGPDs21vIGxlIHZhIGNvbiBsbyBxdWUgdmVuZGUuDQoNCiMjIEluZGljYWRvcmVzIGdlbmVyYWxlcw0KDQpgYGB7ciBpbmRpY2Fkb3Jlc30NCnZlbnRhc190b3QgICAgPC0gc3VtKGRhdG9zJHZlbnRhc190b3RhbCkNCnV0aWxpZGFkX3RvdCAgPC0gc3VtKGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSkNCnVuaWRhZGVzX3RvdCAgPC0gc3VtKGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzKQ0KbWFyZ2VuX3RvdCAgICA8LSB1dGlsaWRhZF90b3QgLyB2ZW50YXNfdG90ICogMTAwDQptYXJnZW5fc2ltcGxlIDwtIG1lYW4oZGF0b3MkbWFyZ2VuX29wZXJhdGl2bykgKiAxMDANCg0KdGFibGFfa3BpIDwtIGRhdGEuZnJhbWUoDQogIEluZGljYWRvciA9IGMoIlJlZ2lzdHJvcyIsICJWZW50YXMgdG90YWxlcyIsICJVbmlkYWRlcyB2ZW5kaWRhcyIsICJVdGlsaWRhZCBvcGVyYXRpdmEgdG90YWwiLA0KICAgICAgICAgICAgICAgICJNYXJnZW4gb3BlcmF0aXZvIGFncmVnYWRvICh1dGlsaWRhZCAvIHZlbnRhcykiLCAiUHJvbWVkaW8gc2ltcGxlIGRlIGxvcyBtw6FyZ2VuZXMgKG5vIHNlIHVzYSkiLA0KICAgICAgICAgICAgICAgICJQcmVjaW8gcHJvbWVkaW8gcG9yIHVuaWRhZCAodmVudGFzIC8gdW5pZGFkZXMpIiksDQogIFZhbG9yID0gYyhudW0obnJvdyhkYXRvcykpLCB1c2QodmVudGFzX3RvdCksIG51bSh1bmlkYWRlc190b3QpLCB1c2QodXRpbGlkYWRfdG90KSwNCiAgICAgICAgICAgIHBjdChtYXJnZW5fdG90KSwgcGN0KG1hcmdlbl9zaW1wbGUpLCBwYXN0ZTAoIlVTJCAiLCBudW0odmVudGFzX3RvdCAvIHVuaWRhZGVzX3RvdCwgMikpKSkNCg0Ka2FibGUodGFibGFfa3BpLCBjYXB0aW9uID0gIlRhYmxhIDEuIEluZGljYWRvcmVzIGdlbmVyYWxlcyIsIGFsaWduID0gYygibCIsICJyIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDEuKiogQWRpZGFzIHZlbmRpw7MgVVMkIDEyMCwyIG1pbGxvbmVzIHkgMi40NzguODYxIHVuaWRhZGVzLCBjb24gdW5hIHV0aWxpZGFkIG9wZXJhdGl2YSBkZSBVUyQgNDcsMiBtaWxsb25lcyB5IHVuIG1hcmdlbiBhZ3JlZ2FkbyBkZSAzOSwzJS4gRWwgcHJlY2lvIHByb21lZGlvIGZ1ZSBkZSBVUyQgNDgsNDggcG9yIHVuaWRhZC4NCg0KIyMgVGVuZGVuY2lhIGNlbnRyYWwgeSBkaXNwZXJzacOzbg0KDQoqUHJlZ3VudGE6IMK/Y8OzbW8gZXMgdW4gcmVnaXN0cm8gdMOtcGljbyB5IHF1w6kgdGFudG8gdmFyw61hbiBsYXMgdmFyaWFibGVzPyoNCg0KYGBge3IgZGVzY3JpcHRpdmFzfQ0KdmFyaWFibGVzIDwtIGMoInByZWNpb191bmlkYWQiLCAidW5pZGFkZXNfdmVuZGlkYXMiLCAidmVudGFzX3RvdGFsIiwgInV0aWxpZGFkX29wZXJhdGl2YSIsICJtYXJnZW5fb3BlcmF0aXZvIikNCg0KZGVzY3JpcHRpdmFzIDwtIGRhdGEuZnJhbWUoDQogIFZhcmlhYmxlID0gYygiUHJlY2lvIHBvciB1bmlkYWQgKFVTJCkiLCAiVW5pZGFkZXMgdmVuZGlkYXMiLCAiVmVudGFzIHBvciByZWdpc3RybyAoVVMkKSIsDQogICAgICAgICAgICAgICAiVXRpbGlkYWQgcG9yIHJlZ2lzdHJvIChVUyQpIiwgIk1hcmdlbiBwb3IgcmVnaXN0cm8gKCUpIiksDQogIE1lZGlhICAgID0gc2FwcGx5KGRhdG9zW3ZhcmlhYmxlc10sIG1lYW4pLA0KICBNZWRpYW5hICA9IHNhcHBseShkYXRvc1t2YXJpYWJsZXNdLCBtZWRpYW4pLA0KICBgRGVzdi4gZXN0w6FuZGFyYCA9IHNhcHBseShkYXRvc1t2YXJpYWJsZXNdLCBzZCksDQogIGBNw61uaW1vYCA9IHNhcHBseShkYXRvc1t2YXJpYWJsZXNdLCBtaW4pLA0KICBgTcOheGltb2AgPSBzYXBwbHkoZGF0b3NbdmFyaWFibGVzXSwgbWF4KSwNCiAgY2hlY2submFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gTlVMTCkNCg0KIyBFbCBtYXJnZW4gc2UgbXVlc3RyYSBlbiBwb3JjZW50YWplDQpkZXNjcmlwdGl2YXNbNSwgMjo2XSA8LSBkZXNjcmlwdGl2YXNbNSwgMjo2XSAqIDEwMA0KZGVzY3JpcHRpdmFzJGBDb2VmLiBkZSB2YXJpYWNpw7NuYCA8LSBkZXNjcmlwdGl2YXMkYERlc3YuIGVzdMOhbmRhcmAgLyBkZXNjcmlwdGl2YXMkTWVkaWENCg0KZGVzY3JpcHRpdmFzICU+JQ0KICBtdXRhdGUoYWNyb3NzKE1lZGlhOmBNw6F4aW1vYCwgfiBudW0oLngsIDEpKSwgYENvZWYuIGRlIHZhcmlhY2nDs25gID0gbnVtKGBDb2VmLiBkZSB2YXJpYWNpw7NuYCwgMikpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDIuIE1lZGlkYXMgZGUgdGVuZGVuY2lhIGNlbnRyYWwgeSBkaXNwZXJzacOzbiBwb3IgcmVnaXN0cm8iLCBhbGlnbiA9IGMoImwiLCByZXAoInIiLCA2KSkpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDIuKiogVW4gcmVnaXN0cm8gdMOtcGljbyAobGEgbWVkaWFuYSkgdGllbmUgMTc2IHVuaWRhZGVzLCBVUyQgNy44MTIgZW4gdmVudGFzIHkgVVMkIDMuMjY0IGRlIHV0aWxpZGFkLiBFbCBwcm9tZWRpbyBkZSBsYXMgdmVudGFzIChVUyQgMTIuNDYwKSBlc3TDoSBtdXkgcG9yIGVuY2ltYSBkZSBsYSBtZWRpYW5hIHkgc3UgY29lZmljaWVudGUgZGUgdmFyaWFjacOzbiBlcyAxLDAyLCBsbyBxdWUgaW5kaWNhIHF1ZSBoYXkgcmVnaXN0cm9zIG11eSBncmFuZGVzIHF1ZSBqYWxhbiBlbCBwcm9tZWRpbyBoYWNpYSBhcnJpYmEuIEVsIG1hcmdlbiwgZW4gY2FtYmlvLCB2YXLDrWEgcG9jbyAoY29lZmljaWVudGUgZGUgMCwyMykuDQoNCiMjIERpc3RyaWJ1Y2nDs24geSB2YWxvcmVzIGF0w61waWNvcw0KDQoqUHJlZ3VudGE6IMK/bGFzIHZlbnRhcyBwb3IgcmVnaXN0cm8gc29uIHBhcmVjaWRhcyBlbnRyZSBzw60gbyBoYXkgdW5vcyBwb2NvcyByZWdpc3Ryb3MgbXV5IGdyYW5kZXM/Kg0KDQpgYGB7ciBoaXN0b2dyYW1hfQ0KZ2dwbG90bHkoDQogIGdncGxvdChkYXRvcywgYWVzKHggPSB2ZW50YXNfdG90YWwpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1MDAwLCBib3VuZGFyeSA9IDAsIGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAid2hpdGUiKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVkaWFuKGRhdG9zJHZlbnRhc190b3RhbCksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXkyMCIpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKGRhdG9zJHZlbnRhc190b3RhbCksIGxpbmV0eXBlID0gImRvdHRlZCIsIGNvbG9yID0gImdyZXkyMCIpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsaW1pdGVfdmVudGFzLCBjb2xvciA9ICJncmV5NTAiKSArDQogICAgbGFicyh0aXRsZSA9ICJGaWd1cmEgMS4gRGlzdHJpYnVjacOzbiBkZSBsYXMgdmVudGFzIHBvciByZWdpc3RybyIsDQogICAgICAgICB4ID0gIlZlbnRhcyBwb3IgcmVnaXN0cm8gKFVTJCkiLCB5ID0gIk7Dum1lcm8gZGUgcmVnaXN0cm9zIiwNCiAgICAgICAgIGNhcHRpb24gPSAiTMOtbmVhIGRpc2NvbnRpbnVhOiBtZWRpYW5hLiBMw61uZWEgcHVudGVhZGE6IG1lZGlhLiBMw61uZWEgY29udGludWE6IGzDrW1pdGUgZGUgYXTDrXBpY29zLiIpICsNCiAgICB0ZW1hDQopDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIEZpZ3VyYSAxLioqIExhIG1heW9yw61hIGRlIGxvcyByZWdpc3Ryb3Mgc2UgY29uY2VudHJhIGVuIHZhbG9yZXMgYmFqb3MgeSBoYXkgdW5hIGNvbGEgbGFyZ2EgaGFjaWEgbGEgZGVyZWNoYSAoYXNpbWV0csOtYSBwb3NpdGl2YSk6IGxhIG1heW9yw61hIGRlIHJlZ2lzdHJvcyB2ZW5kZSBtZW5vcyBkZSBVUyQgMTUgbWlsLCBwZXJvIGhheSA3OTkgcmVnaXN0cm9zICg4LDMlKSBwb3IgZW5jaW1hIGRlIFVTJCAzMy41NzggcXVlIHN1bWFuIGVsIDMwLDQlIGRlIGxhcyB2ZW50YXMuIExhIGzDrW5lYSBkaXNjb250aW51YSBlcyBsYSBtZWRpYW5hLCBsYSBwdW50ZWFkYSBlcyBsYSBtZWRpYSB5IGxhIGNvbnRpbnVhIGVzIGVsIGzDrW1pdGUgYSBwYXJ0aXIgZGVsIGN1YWwgdW4gcmVnaXN0cm8gc2UgY29uc2lkZXJhIGF0w61waWNvLiBTb24gdmVudGFzIHJlYWxlcywgYXPDrSBxdWUgbGFzIGNvbnNlcnZhbW9zLCB5IHBvciBlc28gcHJlZmVyaW1vcyBsYSBtZWRpYW5hIHBhcmEgZGVzY3JpYmlyIHVuIHJlZ2lzdHJvIHTDrXBpY28geSBsb3MgdG90YWxlcyBwYXJhIGNvbXBhcmFyIHNlZ21lbnRvcy4NCg0KIyMgUGFydGljaXBhY2nDs24gcG9yIHNlZ21lbnRvcw0KDQoqUHJlZ3VudGE6IMK/cXXDqSBzZWdtZW50b3MgY29uY2VudHJhbiBsYXMgdmVudGFzLCBsYXMgdW5pZGFkZXMgeSBsYSB1dGlsaWRhZD8qDQoNCmBgYHtyIHJlZ2lvbmVzfQ0KcmVzdW1lbl9yZWdpb24gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHVuaWRhZGVzID0gc3VtKHVuaWRhZGVzX3ZlbmRpZGFzKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwYXJ0X3ZlbnRhcyA9IHZlbnRhcyAvIHN1bSh2ZW50YXMpICogMTAwLA0KICAgICAgICAgcGFydF91bmlkYWRlcyA9IHVuaWRhZGVzIC8gc3VtKHVuaWRhZGVzKSAqIDEwMCwNCiAgICAgICAgIHBhcnRfdXRpbGlkYWQgPSB1dGlsaWRhZCAvIHN1bSh1dGlsaWRhZCkgKiAxMDApICU+JQ0KICBhcnJhbmdlKGRlc2ModmVudGFzKSkNCg0KZ3JhZmljb19yZWdpb24gPC0gcmVzdW1lbl9yZWdpb24gJT4lDQogIHNlbGVjdChyZWdpb24sIHBhcnRfdmVudGFzLCBwYXJ0X3V0aWxpZGFkLCBtYXJnZW4pICU+JQ0KICBwaXZvdF9sb25nZXIoYyhwYXJ0X3ZlbnRhcywgcGFydF91dGlsaWRhZCksIG5hbWVzX3RvID0gIm1lZGlkYSIsIHZhbHVlc190byA9ICJ2YWxvciIpICU+JQ0KICBtdXRhdGUobWVkaWRhID0gZmFjdG9yKGlmZWxzZShtZWRpZGEgPT0gInBhcnRfdmVudGFzIiwgIiUgZGUgbGFzIHZlbnRhcyIsICIlIGRlIGxhIHV0aWxpZGFkIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiJSBkZSBsYXMgdmVudGFzIiwgIiUgZGUgbGEgdXRpbGlkYWQiKSksDQogICAgICAgICByZWdpb24gPSBmYWN0b3IocmVnaW9uLCBsZXZlbHMgPSByZXN1bWVuX3JlZ2lvbiRyZWdpb24pKQ0KDQpnZ3Bsb3RseSgNCiAgZ2dwbG90KGdyYWZpY29fcmVnaW9uLCBhZXMoeCA9IHJlZ2lvbiwgeSA9IHZhbG9yLCBmaWxsID0gbWVkaWRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKHJlZ2lvbiwgIlxuIiwgbWVkaWRhLCAiOiAiLCBwY3QodmFsb3IpLCAiXG5NYXJnZW46ICIsIHBjdChtYXJnZW4pKSkpICsNCiAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIlIGRlIGxhcyB2ZW50YXMiID0gInN0ZWVsYmx1ZSIsICIlIGRlIGxhIHV0aWxpZGFkIiA9ICJsaWdodHN0ZWVsYmx1ZSIpKSArDQogICAgbGFicyh0aXRsZSA9ICJGaWd1cmEgMi4gUGFydGljaXBhY2nDs24gcG9yIHJlZ2nDs24gZW4gdmVudGFzIHkgdXRpbGlkYWQgKCUpIiwNCiAgICAgICAgIHggPSAiUmVnacOzbiIsIHkgPSAiJSBkZWwgdG90YWwiLCBmaWxsID0gIiIpICsNCiAgICB0ZW1hLA0KICB0b29sdGlwID0gInRleHQiKQ0KYGBgDQoNCioqTGVjdHVyYSBkZSBsYSBGaWd1cmEgMi4qKiBDYWRhIHNlcmllICh2ZW50YXMgeSB1dGlsaWRhZCkgc3VtYSAxMDAlLiBXZXN0IGVzIGxhIHJlZ2nDs24gcXVlIG3DoXMgdmVuZGUgKDMwLDMlKSwgcGVybyBzdSBiYXJyYSBkZSB1dGlsaWRhZCBlcyBtw6FzIGJhamEgKDI3LDYlKS4gRW4gU291dGggcGFzYSBsbyBjb250cmFyaW86IHRpZW5lIGVsIDE3LDElIGRlIGxhcyB2ZW50YXMgeSBlbCAxOSw1JSBkZSBsYSB1dGlsaWRhZC4NCg0KYGBge3IgdGFibGFfcmVnaW9ufQ0KcmVzdW1lbl9yZWdpb24gJT4lDQogIHRyYW5zbXV0ZShgUmVnacOzbmAgPSByZWdpb24sIFZlbnRhcyA9IHVzZCh2ZW50YXMpLCBgJSB2ZW50YXNgID0gcGN0KHBhcnRfdmVudGFzKSwNCiAgICAgICAgICAgIGAlIHVuaWRhZGVzYCA9IHBjdChwYXJ0X3VuaWRhZGVzKSwgYCUgdXRpbGlkYWRgID0gcGN0KHBhcnRfdXRpbGlkYWQpLCBNYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIGJpbmRfcm93cyhkYXRhLmZyYW1lKGBSZWdpw7NuYCA9ICJUb3RhbCIsIFZlbnRhcyA9IHVzZCh2ZW50YXNfdG90KSwNCiAgICAgICAgICAgICAgICAgICAgICAgYCUgdmVudGFzYCA9IHBjdChzdW0ocmVzdW1lbl9yZWdpb24kcGFydF92ZW50YXMpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgYCUgdW5pZGFkZXNgID0gcGN0KHN1bShyZXN1bWVuX3JlZ2lvbiRwYXJ0X3VuaWRhZGVzKSksDQogICAgICAgICAgICAgICAgICAgICAgIGAlIHV0aWxpZGFkYCA9IHBjdChzdW0ocmVzdW1lbl9yZWdpb24kcGFydF91dGlsaWRhZCkpLA0KICAgICAgICAgICAgICAgICAgICAgICBNYXJnZW4gPSBwY3QobWFyZ2VuX3RvdCksIGNoZWNrLm5hbWVzID0gRkFMU0UpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAzLiBWZW50YXMsIHVuaWRhZGVzLCB1dGlsaWRhZCB5IG1hcmdlbiBwb3IgcmVnacOzbiIsIGFsaWduID0gYygibCIsIHJlcCgiciIsIDUpKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lDQogIHJvd19zcGVjKDYsIGJvbGQgPSBUUlVFKQ0KYGBgDQoNCioqTGVjdHVyYSBkZSBsYSBUYWJsYSAzLioqIExhIGZpbGEgZGUgdG90YWwgY29uZmlybWEgcXVlIGxhcyBwYXJ0aWNpcGFjaW9uZXMgc3VtYW4gMTAwJS4gV2VzdCB0aWVuZSBlbCBtZW5vciBtYXJnZW4gKDM1LDclKSB5IFNvdXRoIGVsIG1heW9yICg0NCw4JSkuIFNvdXRoIHBlc2EgbcOhcyBlbiB1bmlkYWRlcyAoMTksOSUpIHF1ZSBlbiB2ZW50YXMgKDE3LDElKSBwb3JxdWUgdmVuZGUgYSBwcmVjaW9zIG3DoXMgYmFqb3MuDQoNCmBgYHtyIHByb2R1Y3RvX2Rpc3RyaWJ1aWRvcn0NCnBhcnRpY2lwYWNpb24gPC0gZnVuY3Rpb24oYmFzZSwgY29sdW1uYSwgbm9tYnJlKSB7DQogIHRhYmxhIDwtIGJhc2UgJT4lDQogICAgZ3JvdXBfYnkoY2F0ZWdvcmlhID0gLmRhdGFbW2NvbHVtbmFdXSkgJT4lDQogICAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLCB1bmlkYWRlcyA9IHN1bSh1bmlkYWRlc192ZW5kaWRhcyksIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyh2ZW50YXMpKQ0KICB0b3RhbCA8LSBkYXRhLmZyYW1lKGNhdGVnb3JpYSA9ICJUb3RhbCIsIHZlbnRhcyA9IHN1bSh0YWJsYSR2ZW50YXMpLA0KICAgICAgICAgICAgICAgICAgICAgIHVuaWRhZGVzID0gc3VtKHRhYmxhJHVuaWRhZGVzKSwgdXRpbGlkYWQgPSBzdW0odGFibGEkdXRpbGlkYWQpKQ0KICBiaW5kX3Jvd3ModGFibGEsIHRvdGFsKSAlPiUNCiAgICBtdXRhdGUoRGltZW5zacOzbiA9IG5vbWJyZSwNCiAgICAgICAgICAgYCUgdmVudGFzYCA9IHBjdCh2ZW50YXMgLyBzdW0odGFibGEkdmVudGFzKSAqIDEwMCksDQogICAgICAgICAgIGAlIHVuaWRhZGVzYCA9IHBjdCh1bmlkYWRlcyAvIHN1bSh0YWJsYSR1bmlkYWRlcykgKiAxMDApLA0KICAgICAgICAgICBgJSB1dGlsaWRhZGAgPSBwY3QodXRpbGlkYWQgLyBzdW0odGFibGEkdXRpbGlkYWQpICogMTAwKSwNCiAgICAgICAgICAgTWFyZ2VuID0gcGN0KHV0aWxpZGFkIC8gdmVudGFzICogMTAwKSkNCn0NCg0KYmluZF9yb3dzKHBhcnRpY2lwYWNpb24oZGF0b3MsICJwcm9kdWN0byIsICJQcm9kdWN0byIpLA0KICAgICAgICAgIHBhcnRpY2lwYWNpb24oZGF0b3MsICJkaXN0cmlidWlkb3IiLCAiRGlzdHJpYnVpZG9yIikpICU+JQ0KICBzZWxlY3QoRGltZW5zacOzbiwgQ2F0ZWdvcsOtYSA9IGNhdGVnb3JpYSwgYCUgdmVudGFzYCwgYCUgdW5pZGFkZXNgLCBgJSB1dGlsaWRhZGAsIE1hcmdlbikgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgNC4gUGFydGljaXBhY2nDs24gcG9yIHByb2R1Y3RvIHkgcG9yIGRpc3RyaWJ1aWRvciIsIGFsaWduID0gYygibCIsICJsIiwgcmVwKCJyIiwgNCkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKSAlPiUNCiAgcm93X3NwZWMoYyg3LCAxNCksIGJvbGQgPSBUUlVFKSAlPiUNCiAgY29sbGFwc2Vfcm93cyhjb2x1bW5zID0gMSwgdmFsaWduID0gInRvcCIpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDQuKiogTWVuJ3MgU3RyZWV0IEZvb3R3ZWFyIGVzIGVsIHByb2R1Y3RvIHF1ZSBtw6FzIHZlbmRlICgyMywwJSkgeSB0YW1iacOpbiBlbCBkZSBtZWpvciBtYXJnZW4gKDQyLDAlKS4gTWVuJ3MgQXRobGV0aWMgRm9vdHdlYXIgZXMgZWwgdGVyY2VybyBlbiB2ZW50YXMsIHBlcm8gdGllbmUgZWwgbWFyZ2VuIG3DoXMgYmFqbyAoMzYsMSUpLiBFbnRyZSBkaXN0cmlidWlkb3JlcywgV2VzdCBHZWFyIHkgRm9vdCBMb2NrZXIgc3VtYW4gbcOhcyBkZSBsYSBtaXRhZCBkZSBsYXMgdmVudGFzICg1MSwyJSksIHkgU3BvcnRzIERpcmVjdCBlcyBlbCBxdWUgbWVqb3IgbWFyZ2VuIHRpZW5lICg0MywyJSkuDQoNCiMjIE3DqXRvZG9zIGRlIHZlbnRhIHkgdmFyaWFiaWxpZGFkDQoNCipQcmVndW50YTogwr9xdcOpIG3DqXRvZG8gZGUgdmVudGEgZGVqYSBtZWpvciBtYXJnZW4sIGVzIGlndWFsIGVuIHRvZGFzIGxhcyByZWdpb25lcyB5IGTDs25kZSB2YXLDrWEgbcOhcyBlbCBtYXJnZW4/Kg0KDQpgYGB7ciByZWdpb25fbWV0b2RvfQ0KcmVnaW9uX21ldG9kbyA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uLCBtZXRvZG9fdmVudGEpICU+JQ0KICBzdW1tYXJpc2UodmVudGFzID0gc3VtKHZlbnRhc190b3RhbCksDQogICAgICAgICAgICB1dGlsaWRhZCA9IHN1bSh1dGlsaWRhZF9vcGVyYXRpdmEpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiAgbXV0YXRlKG1hcmdlbiA9IHV0aWxpZGFkIC8gdmVudGFzICogMTAwLA0KICAgICAgICAgbWV6Y2xhID0gdmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDApICU+JQ0KICB1bmdyb3VwKCkNCg0KZ2dwbG90bHkoDQogIGdncGxvdChyZWdpb25fbWV0b2RvLCBhZXMoeCA9IG1ldG9kb192ZW50YSwgeSA9IHJlZ2lvbiwgZmlsbCA9IG1hcmdlbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKHJlZ2lvbiwgIiDCtyAiLCBtZXRvZG9fdmVudGEsICJcbk1hcmdlbjogIiwgcGN0KG1hcmdlbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXG5QZXNvIGVuIGxhcyB2ZW50YXMgZGUgbGEgcmVnacOzbjogIiwgcGN0KG1lemNsYSkpKSkgKw0KICAgIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpICsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHBjdChtYXJnZW4pLCAiXG4oIiwgcGN0KG1lemNsYSksICIgZGUgdmVudGFzKSIpKSwgc2l6ZSA9IDMpICsNCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjZjJmMmYyIiwgaGlnaCA9ICJzdGVlbGJsdWUiKSArDQogICAgbGFicyh0aXRsZSA9ICJGaWd1cmEgMy4gTWFyZ2VuIHBvciByZWdpw7NuIHkgbcOpdG9kbyBkZSB2ZW50YSAoJSkiLA0KICAgICAgICAgeCA9ICJNw6l0b2RvIGRlIHZlbnRhIiwgeSA9ICJSZWdpw7NuIiwgZmlsbCA9ICJNYXJnZW4gKCUpIikgKw0KICAgIHRlbWEsDQogIHRvb2x0aXAgPSAidGV4dCIpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIEZpZ3VyYSAzLioqIENhZGEgY2VsZGEgbXVlc3RyYSBlbCBtYXJnZW4geSwgZW50cmUgcGFyw6ludGVzaXMsIGVsIHBlc28gZGUgZXNlIG3DqXRvZG8gZW4gbGFzIHZlbnRhcyBkZSBsYSByZWdpw7NuOyBlbiBjYWRhIHJlZ2nDs24gbG9zIHRyZXMgcGVzb3Mgc3VtYW4gMTAwJS4gRW50cmUgbcOhcyBvc2N1cm8gZWwgYXp1bCwgbWF5b3IgZWwgbWFyZ2VuLiBMYSBjb2x1bW5hIE9ubGluZSBlcyBsYSBtw6FzIG9zY3VyYSBlbiB0b2RhcyBsYXMgcmVnaW9uZXMsIHkgbGEgY2VsZGEgY29uIG1lam9yIG1hcmdlbiBlcyBPbmxpbmUgZW4gTm9ydGhlYXN0ICg0OCw2JSksIGF1bnF1ZSBhbGzDrSBzb2xvIHBlc2EgZWwgMTgsNCUgZGUgbGFzIHZlbnRhcy4gRWwgT3V0bGV0IGdhbmEgNDQsNiUgZW4gU291dGggeSAzMiw4JSBlbiBXZXN0Lg0KDQpgYGB7ciB0YWJsYV9tZXRvZG99DQpyZXN1bWVuX21ldG9kbyA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkobWV0b2RvX3ZlbnRhKSAlPiUNCiAgc3VtbWFyaXNlKHJlZ2lzdHJvcyA9IG4oKSwNCiAgICAgICAgICAgIHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLA0KICAgICAgICAgICAgdW5pZGFkZXMgPSBzdW0odW5pZGFkZXNfdmVuZGlkYXMpLA0KICAgICAgICAgICAgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSkgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIHBhcnRfdmVudGFzID0gdmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDAsDQogICAgICAgICBwYXJ0X3VuaWRhZGVzID0gdW5pZGFkZXMgLyBzdW0odW5pZGFkZXMpICogMTAwLA0KICAgICAgICAgdXRpbGlkYWRfcmVnaXN0cm8gPSB1dGlsaWRhZCAvIHJlZ2lzdHJvcykgJT4lDQogIGFycmFuZ2UoZGVzYyhtYXJnZW4pKQ0KDQpyZXN1bWVuX21ldG9kbyAlPiUNCiAgdHJhbnNtdXRlKGBNw6l0b2RvYCA9IG1ldG9kb192ZW50YSwgYCUgdmVudGFzYCA9IHBjdChwYXJ0X3ZlbnRhcyksIGAlIHVuaWRhZGVzYCA9IHBjdChwYXJ0X3VuaWRhZGVzKSwNCiAgICAgICAgICAgIE1hcmdlbiA9IHBjdChtYXJnZW4pLCBgVXRpbGlkYWQgcHJvbWVkaW8gcG9yIHJlZ2lzdHJvYCA9IHVzZCh1dGlsaWRhZF9yZWdpc3RybykpICU+JQ0KICBiaW5kX3Jvd3MoZGF0YS5mcmFtZShgTcOpdG9kb2AgPSAiVG90YWwiLCBgJSB2ZW50YXNgID0gcGN0KHN1bShyZXN1bWVuX21ldG9kbyRwYXJ0X3ZlbnRhcykpLA0KICAgICAgICAgICAgICAgICAgICAgICBgJSB1bmlkYWRlc2AgPSBwY3Qoc3VtKHJlc3VtZW5fbWV0b2RvJHBhcnRfdW5pZGFkZXMpKSwgTWFyZ2VuID0gcGN0KG1hcmdlbl90b3QpLA0KICAgICAgICAgICAgICAgICAgICAgICBgVXRpbGlkYWQgcHJvbWVkaW8gcG9yIHJlZ2lzdHJvYCA9IHVzZCh1dGlsaWRhZF90b3QgLyBucm93KGRhdG9zKSksIGNoZWNrLm5hbWVzID0gRkFMU0UpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSA1LiBDb21wYXJhY2nDs24gZGUgbG9zIG3DqXRvZG9zIGRlIHZlbnRhIiwgYWxpZ24gPSBjKCJsIiwgcmVwKCJyIiwgNCkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKSAlPiUNCiAgcm93X3NwZWMoNCwgYm9sZCA9IFRSVUUpDQoNCiMgwr9FbiBjdcOhbnRvcyBlc3RhZG9zIGVzIGNhZGEgbcOpdG9kbyBlbCBkZSBtYXlvciBtYXJnZW4/DQpkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoZXN0YWRvLCBtZXRvZG9fdmVudGEpICU+JQ0KICBzdW1tYXJpc2UobWFyZ2VuID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkgLyBzdW0odmVudGFzX3RvdGFsKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGdyb3VwX2J5KGVzdGFkbykgJT4lDQogIHNsaWNlX21heChtYXJnZW4sIG4gPSAxKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBjb3VudChtZXRvZG9fdmVudGEsIG5hbWUgPSAiZXN0YWRvcyIpICU+JQ0KICByZW5hbWUoYE3DqXRvZG8gY29uIG1heW9yIG1hcmdlbmAgPSBtZXRvZG9fdmVudGEsIEVzdGFkb3MgPSBlc3RhZG9zKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJOw7ptZXJvIGRlIGVzdGFkb3Mgc2Vnw7puIGVsIG3DqXRvZG8gY29uIG1heW9yIG1hcmdlbiIsIGFsaWduID0gYygibCIsICJyIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDUuKiogT25saW5lIHRpZW5lIGVsIG1lam9yIG1hcmdlbiAoNDMsNSUpIHkgZXMgZWwgbcOpdG9kbyBtw6FzIHJlbnRhYmxlIGVuIDQyIGRlIGxvcyA1MCBlc3RhZG9zOyBlbiBsb3Mgb3Ryb3MgOCBnYW5hIE91dGxldC4gSW4tc3RvcmUgdGllbmUgZWwgbWFyZ2VuIG3DoXMgYmFqbyAoMzUsOCUpLCBwZXJvIGxhIG1heW9yIHV0aWxpZGFkIHBvciByZWdpc3RybyAoVVMkIDcuMzMzKSBwb3JxdWUgc3VzIHZlbnRhcyBzb24gbcOhcyBncmFuZGVzOiBhbCBjb21wYXJhciB1dGlsaWRhZCBlbiBkw7NsYXJlcyB5IG1hcmdlbiwgZWwgb3JkZW4gc2UgaW52aWVydGUuDQoNCipQcmVndW50YTogwr9sYSBkaWZlcmVuY2lhIGVudHJlIG3DqXRvZG9zIHNlIHZlIHRhbWJpw6luIHJlZ2lzdHJvIGEgcmVnaXN0cm8sIHkgZW4gcXXDqSByZWdpw7NuIHZhcsOtYSBtw6FzIGVsIG1hcmdlbj8qDQoNCmBgYHtyIGJveHBsb3RfbWV0b2RvfQ0KZ2dwbG90bHkoDQogIGdncGxvdChkYXRvcywgYWVzKHggPSBtZXRvZG9fdmVudGEsIHkgPSBtYXJnZW5fb3BlcmF0aXZvICogMTAwKSkgKw0KICAgIGdlb21fYm94cGxvdChmaWxsID0gImxpZ2h0c3RlZWxibHVlIiwgb3V0bGllci5jb2xvdXIgPSAiZ3JleTQwIiwgb3V0bGllci5zaXplID0gMSkgKw0KICAgIGZhY2V0X3dyYXAofnJlZ2lvbiwgbnJvdyA9IDEpICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyYSA0LiBNYXJnZW4gcG9yIHJlZ2lzdHJvIHNlZ8O6biBtw6l0b2RvIHkgcmVnacOzbiIsDQogICAgICAgICB4ID0gTlVMTCwgeSA9ICJNYXJnZW4gb3BlcmF0aXZvICglKSIpICsNCiAgICB0ZW1hICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQopDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIEZpZ3VyYSA0LioqIENhZGEgY2FqYSByZXN1bWUgZWwgbWFyZ2VuIGRlIGxvcyByZWdpc3Ryb3MgZGUgdW4gbcOpdG9kbyBlbiB1bmEgcmVnacOzbiwgeSBsYSBsw61uZWEgZGVsIG1lZGlvIGVzIGxhIG1lZGlhbmEuIE9ubGluZSB0aWVuZSBsYSBtZWRpYW5hIG3DoXMgYWx0YSBlbiBsYXMgY2luY28gcmVnaW9uZXMgKGVudHJlIDQ0JSB5IDQ5JSksIGUgSW4tc3RvcmUgbGEgbcOhcyBiYWphICgzNSUsIHNhbHZvIGVuIFNvdXRoIGNvbiA0MCUpLg0KDQpgYGB7ciB2YXJpYWJpbGlkYWR9DQojIFZhcmlhYmlsaWRhZCBkZWwgbWFyZ2VuIHBvciByZWdpw7NuDQpkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKG1lZGlhbmEgPSBtZWRpYW4obWFyZ2VuX29wZXJhdGl2bykgKiAxMDAsDQogICAgICAgICAgICBkZXN2aWFjaW9uID0gc2QobWFyZ2VuX29wZXJhdGl2bykgKiAxMDApICU+JQ0KICBhcnJhbmdlKGRlc2MoZGVzdmlhY2lvbikpICU+JQ0KICB0cmFuc211dGUoYFJlZ2nDs25gID0gcmVnaW9uLCBgTWVkaWFuYSBkZWwgbWFyZ2VuICglKWAgPSBudW0obWVkaWFuYSwgMSksIGBEZXN2aWFjacOzbiBlc3TDoW5kYXIgKHB1bnRvcylgID0gbnVtKGRlc3ZpYWNpb24sIDEpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSA2LiBWYXJpYWJpbGlkYWQgZGVsIG1hcmdlbiBwb3IgcmVnaXN0cm8gZW4gY2FkYSByZWdpw7NuIiwgYWxpZ24gPSBjKCJsIiwgInIiLCAiciIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKQ0KYGBgDQoNCioqTGVjdHVyYSBkZSBsYSBUYWJsYSA2LioqIFdlc3QgZXMgbGEgcmVnacOzbiBkb25kZSBtw6FzIHZhcsOtYSBlbCBtYXJnZW4gZW50cmUgcmVnaXN0cm9zIChkZXN2aWFjacOzbiBkZSAxMCw3IHB1bnRvcyk6IGFsbMOtIGNvbnZpdmVuIHZlbnRhcyBtdXkgcmVudGFibGVzIGNvbiBvdHJhcyBtdXkgcG9jbyByZW50YWJsZXMuIE5vcnRoZWFzdCBlcyBsYSBtw6FzIGVzdGFibGUgKDgsNCBwdW50b3MpLg0KDQojICoqRGlhZ27Ds3N0aWNvIHkgaGFsbGF6Z29zKioNCg0KUHJlc2VudGFtb3MgY2luY28gaGFsbGF6Z29zOyBsb3MgZG9zIHByaW1lcm9zIHNvbiBsb3MgcXVlIG3DoXMgcHJvZnVuZGl6YW1vcy4gRGViYWpvIGRlIGNhZGEgdGFibGEgbyBncsOhZmljbyBlc3TDoSBsYSBldmlkZW5jaWEgY3VhbnRpdGF0aXZhIHkgc3UgaW50ZXJwcmV0YWNpw7NuLCB5IGFsIGZpbmFsIGRlIGNhZGEgaGFsbGF6Z28sIGxhIGltcGxpY2FjacOzbiBwYXJhIGVsIG5lZ29jaW8uDQoNCiMjIEhhbGxhemdvIDEgKHByb2Z1bmRpemFjacOzbikuIFZlbmRlciBtw6FzIG5vIHNpZW1wcmUgZXMgZ2FuYXIgbcOhcw0KDQoqUHJlZ3VudGE6IMK/cXXDqSBlc3RhZG9zIHkgY2l1ZGFkZXMgY29tYmluYW4gbXVjaGFzIHZlbnRhcyBjb24gYnVlbiBtYXJnZW4sIHkgY3XDoWxlcyB2ZW5kZW4gbXVjaG8gcGVybyBnYW5hbiBwb2NvPyoNCg0KUGFyYSBjbGFzaWZpY2FyIGxvcyA1MCBlc3RhZG9zIG5vcyBoYWNlbW9zIGRvcyBwcmVndW50YXMgc2VuY2lsbGFzOg0KDQoxLiAqKsK/RWwgZXN0YWRvIHZlbmRlIG11Y2hvIG8gcG9jbz8qKiBTaSBsb3MgNTAgZXN0YWRvcyB2ZW5kaWVyYW4gbG8gbWlzbW8sIGNhZGEgdW5vIHRlbmRyw61hIGVsIDIlIGRlIGxhcyB2ZW50YXMgKDEwMCUgLyA1MCksIHVub3MgVVMkIDIsNCBtaWxsb25lcy4gVW4gZXN0YWRvIHF1ZSB2ZW5kZSBtw6FzIGRlbCAyJSBlcyB1biBtZXJjYWRvIGdyYW5kZSwgY29tbyBDYWxpZm9ybmlhICg3LDElKTsgdW5vIHF1ZSB2ZW5kZSBtZW5vcyBkZWwgMiUgZXMgdW4gbWVyY2FkbyBwZXF1ZcOxbywgY29tbyBJb3dhICgwLDglKS4NCjIuICoqwr9FbCBlc3RhZG8gZ2FuYSBtdWNobyBvIHBvY28gcG9yIGNhZGEgZMOzbGFyIHZlbmRpZG8/KiogTGEgcmVmZXJlbmNpYSBlcyBlbCBtYXJnZW4gZGUgdG9kYSBsYSBlbXByZXNhLCAzOSwzJS4gVW4gZXN0YWRvIGNvbiBtYXJnZW4gbWF5b3IgZ2FuYSBtw6FzIHF1ZSBlbCBwcm9tZWRpbywgY29tbyBBbGFiYW1hICg1NCw0JSk7IHVubyBjb24gbWFyZ2VuIG1lbm9yIGdhbmEgbWVub3MsIGNvbW8gV2FzaGluZ3RvbiAoMjgsOCUpLg0KDQpBbCBjcnV6YXIgbGFzIGRvcyByZXNwdWVzdGFzIHF1ZWRhbiBjdWF0cm8gZ3J1cG9zOg0KDQp8IHwgKipHYW5hIG3DoXMgcXVlIGVsIHByb21lZGlvKiogKG1hcmdlbiBtYXlvciBhIDM5LDMlKSB8ICoqR2FuYSBtZW5vcyBxdWUgZWwgcHJvbWVkaW8qKiAobWFyZ2VuIG1lbm9yIGEgMzksMyUpIHwNCnwtLS18LS0tfC0tLXwNCnwgKipWZW5kZSBtdWNobyoqIChtw6FzIGRlbCAyJSBkZSBsYXMgdmVudGFzKSB8ICoqRXN0cmVsbGEuKiogRWplbXBsbzogVGV4YXMsIDUsNSUgZGUgbGFzIHZlbnRhcyB5IDQyLDglIGRlIG1hcmdlbiB8ICoqQ29ycmVnaXIuKiogRWplbXBsbzogQ2FsaWZvcm5pYSwgNywxJSBkZSBsYXMgdmVudGFzIHkgMzQsNSUgZGUgbWFyZ2VuIHwNCnwgKipWZW5kZSBwb2NvKiogKG1lbm9zIGRlbCAyJSBkZSBsYXMgdmVudGFzKSB8ICoqT3BvcnR1bmlkYWQuKiogRWplbXBsbzogTWljaGlnYW4sIDEsOSUgZGUgbGFzIHZlbnRhcyB5IDQ1LDklIGRlIG1hcmdlbiB8ICoqQmFqbyBhcG9ydGUuKiogRWplbXBsbzogSW93YSwgMCw4JSBkZSBsYXMgdmVudGFzIHkgMzgsMCUgZGUgbWFyZ2VuIHwNCg0KYGBge3IgZXN0YWRvcywgZmlnLmhlaWdodD03fQ0KcmVzdW1lbl9lc3RhZG8gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbiwgZXN0YWRvKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLA0KICAgICAgICAgICAgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSwNCiAgICAgICAgICAgIHVuaWRhZGVzID0gc3VtKHVuaWRhZGVzX3ZlbmRpZGFzKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIHBhcnRfdmVudGFzID0gdmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDAsDQogICAgICAgICBwcmVjaW9fbWVkaW8gPSB2ZW50YXMgLyB1bmlkYWRlcywNCiAgICAgICAgIGdydXBvID0gY2FzZV93aGVuKHBhcnRfdmVudGFzID49IDIgJiBtYXJnZW4gPj0gbWFyZ2VuX3RvdCB+ICJFc3RyZWxsYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJ0X3ZlbnRhcyA8ICAyICYgbWFyZ2VuID49IG1hcmdlbl90b3QgfiAiT3BvcnR1bmlkYWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFydF92ZW50YXMgPj0gMiAmIG1hcmdlbiA8ICBtYXJnZW5fdG90IH4gIkNvcnJlZ2lyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiQmFqbyBhcG9ydGUiKSwNCiAgICAgICAgIGdydXBvID0gZmFjdG9yKGdydXBvLCBsZXZlbHMgPSBjKCJFc3RyZWxsYSIsICJPcG9ydHVuaWRhZCIsICJDb3JyZWdpciIsICJCYWpvIGFwb3J0ZSIpKSkNCg0KY29sb3Jlc19ncnVwbyA8LSBjKCJFc3RyZWxsYSIgPSAibmF2eSIsICJPcG9ydHVuaWRhZCIgPSAiZGVlcHNreWJsdWUzIiwNCiAgICAgICAgICAgICAgICAgICAiQ29ycmVnaXIiID0gImdyZXkzMCIsICJCYWpvIGFwb3J0ZSIgPSAiZ3JleTcwIikNCg0KIyBFc3RhZG9zIHF1ZSBzZSBub21icmFuIGVuIGVsIGdyw6FmaWNvDQpldGlxdWV0YXMgPC0gYygiQ2FsaWZvcm5pYSIsICJOZXcgWW9yayIsICJGbG9yaWRhIiwgIlRleGFzIiwgIkFsYWJhbWEiLCAiVGVubmVzc2VlIiwNCiAgICAgICAgICAgICAgICJXYXNoaW5ndG9uIiwgIk1pY2hpZ2FuIiwgIklkYWhvIiwgIkxvdWlzaWFuYSIpDQoNCmdncGxvdGx5KA0KICBnZ3Bsb3QocmVzdW1lbl9lc3RhZG8sIGFlcyh4ID0gcGFydF92ZW50YXMsIHkgPSBtYXJnZW4sIGNvbG9yID0gZ3J1cG8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoZXN0YWRvLCAiICgiLCByZWdpb24sICIpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXG5WZW50YXM6IFVTJCAiLCByb3VuZCh2ZW50YXMgLyAxZTYsIDIpLCAiIE0gKCIsIHBjdChwYXJ0X3ZlbnRhcyksICIgZGVsIHRvdGFsKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuVXRpbGlkYWQ6IFVTJCAiLCByb3VuZCh1dGlsaWRhZCAvIDFlNiwgMiksICIgTSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuTWFyZ2VuOiAiLCBwY3QobWFyZ2VuKSkpKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWFyZ2VuX3RvdCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JleTQwIikgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXk0MCIpICsNCiAgICBnZW9tX3BvaW50KGFlcyhzaXplID0gdXRpbGlkYWQgLyAxZTYpLCBhbHBoYSA9IDAuNzUpICsNCiAgICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihyZXN1bWVuX2VzdGFkbywgZXN0YWRvICVpbiUgZXRpcXVldGFzKSwgYWVzKGxhYmVsID0gZXN0YWRvKSwNCiAgICAgICAgICAgICAgbnVkZ2VfeSA9IDEuMywgc2l6ZSA9IDMsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihyZXN1bWVuX2VzdGFkbywgZXN0YWRvID09ICJIYXdhaWkiKSwgYWVzKGxhYmVsID0gZXN0YWRvKSwNCiAgICAgICAgICAgICAgbnVkZ2VfeSA9IC0xLjMsIHNpemUgPSAzLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9yZXNfZ3J1cG8pICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyYSA1LiBFc3RhZG9zOiBwYXJ0aWNpcGFjacOzbiBlbiB2ZW50YXMgdnMuIG1hcmdlbiIsDQogICAgICAgICB4ID0gIiUgZGUgbGFzIHZlbnRhcyB0b3RhbGVzIiwgeSA9ICJNYXJnZW4gb3BlcmF0aXZvIGFncmVnYWRvICglKSIsIGNvbG9yID0gIkdydXBvIiwgc2l6ZSA9ICIiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikNCg0KIyBDb3JyZWxhY2nDs24gZW50cmUgdmVudGFzIHkgdXRpbGlkYWQsIHkgZW50cmUgdmVudGFzIHkgbWFyZ2VuIChwb3IgZXN0YWRvKQ0KZGF0YS5mcmFtZShgUmVsYWNpw7NuIGVudHJlIGVzdGFkb3NgID0gYygiVmVudGFzIHkgdXRpbGlkYWQgKFBlYXJzb24pIiwgIlZlbnRhcyB5IG1hcmdlbiAoU3BlYXJtYW4pIiksDQogICAgICAgICAgIGBDb3JyZWxhY2nDs25gID0gbnVtKGMoY29yKHJlc3VtZW5fZXN0YWRvJHZlbnRhcywgcmVzdW1lbl9lc3RhZG8kdXRpbGlkYWQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yKHJlc3VtZW5fZXN0YWRvJHZlbnRhcywgcmVzdW1lbl9lc3RhZG8kbWFyZ2VuLCBtZXRob2QgPSAic3BlYXJtYW4iKSksIDIpLA0KICAgICAgICAgICBjaGVjay5uYW1lcyA9IEZBTFNFKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJDb3JyZWxhY2lvbmVzIGVudHJlIGxvcyA1MCBlc3RhZG9zIiwgYWxpZ24gPSBjKCJsIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBDYWRhIHB1bnRvIGVzIHVuIGVzdGFkbyB5IHN1IHRhbWHDsW8gZXMgbGEgdXRpbGlkYWQgcXVlIGFwb3J0YS4gRW50cmUgZXN0YWRvcywgdmVudGFzIHkgdXRpbGlkYWQgdGllbmVuIHVuYSBjb3JyZWxhY2nDs24gZGUgMCw5OCwgcGVybyB2ZW50YXMgeSBtYXJnZW4gdGllbmVuIHVuYSBjb3JyZWxhY2nDs24gZGUg4oiSMCwwNS4gVGV4YXMgdmVuZGUgZWwgNSw1JSBkZWwgdG90YWwgY29uIHVuIG1hcmdlbiBkZSA0Miw4JSwgbWllbnRyYXMgQ2FsaWZvcm5pYSB2ZW5kZSBlbCA3LDElIGNvbiAzNCw1JS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogTG9zIGVzdGFkb3MgcXVlIG3DoXMgdmVuZGVuIGFwb3J0YW4gbcOhcyB1dGlsaWRhZCBlbiBkw7NsYXJlcywgcGVybyBlc28gbm8gc2lnbmlmaWNhIHF1ZSBnYW5lbiBtw6FzIHBvciBjYWRhIGTDs2xhciB2ZW5kaWRvLg0KDQpgYGB7ciB0YWJsYV9ncnVwb3N9DQpyZXN1bWVuX2dydXBvIDwtIHJlc3VtZW5fZXN0YWRvICU+JQ0KICBncm91cF9ieShncnVwbykgJT4lDQogIHN1bW1hcmlzZShlc3RhZG9zID0gbigpLA0KICAgICAgICAgICAgdmVudGFzID0gc3VtKHZlbnRhcyksDQogICAgICAgICAgICB1dGlsaWRhZCA9IHN1bSh1dGlsaWRhZCksDQogICAgICAgICAgICBsaXN0YSA9IHBhc3RlKGVzdGFkb1tvcmRlcigtcGFydF92ZW50YXMpXSwgY29sbGFwc2UgPSAiLCAiKSkgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIHBhcnRfdmVudGFzID0gdmVudGFzIC8gdmVudGFzX3RvdCAqIDEwMCwNCiAgICAgICAgIHBhcnRfdXRpbGlkYWQgPSB1dGlsaWRhZCAvIHV0aWxpZGFkX3RvdCAqIDEwMCwNCiAgICAgICAgIHV0aWxpZGFkX3Bvcl9taWxsb24gPSBtYXJnZW4gLyAxMDAgKiAxZTYpICU+JQ0KICBhcnJhbmdlKGdydXBvKQ0KDQpyZXN1bWVuX2dydXBvICU+JQ0KICB0cmFuc211dGUoR3J1cG8gPSBhcy5jaGFyYWN0ZXIoZ3J1cG8pLCBFc3RhZG9zID0gZXN0YWRvcywgYCUgdmVudGFzYCA9IHBjdChwYXJ0X3ZlbnRhcyksIGAlIHV0aWxpZGFkYCA9IHBjdChwYXJ0X3V0aWxpZGFkKSwNCiAgICAgICAgICAgIE1hcmdlbiA9IHBjdChtYXJnZW4pLCBgVXRpbGlkYWQgcG9yIGNhZGEgVVMkIDEgTSB2ZW5kaWRvYCA9IHVzZCh1dGlsaWRhZF9wb3JfbWlsbG9uKSwNCiAgICAgICAgICAgIGBFc3RhZG9zIChkZSBtYXlvciBhIG1lbm9yIHZlbnRhKWAgPSBsaXN0YSkgJT4lDQogIGJpbmRfcm93cyhkYXRhLmZyYW1lKEdydXBvID0gIlRvdGFsIiwgRXN0YWRvcyA9IHN1bShyZXN1bWVuX2dydXBvJGVzdGFkb3MpLA0KICAgICAgICAgICAgICAgICAgICAgICBgJSB2ZW50YXNgID0gcGN0KHN1bShyZXN1bWVuX2dydXBvJHBhcnRfdmVudGFzKSksIGAlIHV0aWxpZGFkYCA9IHBjdChzdW0ocmVzdW1lbl9ncnVwbyRwYXJ0X3V0aWxpZGFkKSksDQogICAgICAgICAgICAgICAgICAgICAgIE1hcmdlbiA9IHBjdChtYXJnZW5fdG90KSwgYFV0aWxpZGFkIHBvciBjYWRhIFVTJCAxIE0gdmVuZGlkb2AgPSB1c2QobWFyZ2VuX3RvdCAvIDEwMCAqIDFlNiksDQogICAgICAgICAgICAgICAgICAgICAgIGBFc3RhZG9zIChkZSBtYXlvciBhIG1lbm9yIHZlbnRhKWAgPSAiIiwgY2hlY2submFtZXMgPSBGQUxTRSkpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDcuIEdydXBvcyBkZSBlc3RhZG9zIHNlZ8O6biB2ZW50YXMgeSBtYXJnZW4iLCBhbGlnbiA9IGMoImwiLCAiciIsICJyIiwgInIiLCAiciIsICJyIiwgImwiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBUUlVFLCBmb250X3NpemUgPSAxMykgJT4lDQogIHJvd19zcGVjKDUsIGJvbGQgPSBUUlVFKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIExvcyA3IGVzdGFkb3MgRXN0cmVsbGEgdGllbmVuIGVsIDIwLDMlIGRlIGxhcyB2ZW50YXMgeSBlbCAyMywwJSBkZSBsYSB1dGlsaWRhZCwgY29uIHVuIG1hcmdlbiBkZSA0NCw2JS4gTG9zIDExIGVzdGFkb3MgYSBDb3JyZWdpciB0aWVuZW4gZWwgNDAsMSUgZGUgbGFzIHZlbnRhcywgcGVybyBzb2xvIGVsIDM2LDklIGRlIGxhIHV0aWxpZGFkIChtYXJnZW4gZGUgMzYsMSUpLiBMb3MgMTUgZGUgT3BvcnR1bmlkYWQgZ2FuYW4gNDIsNiUuDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIFVuIG1pbGzDs24gZGUgZMOzbGFyZXMgYWRpY2lvbmFsIGRlamEgdW5vcyBVUyQgNDQ2IG1pbCBlbiB1biBlc3RhZG8gRXN0cmVsbGEgeSB1bm9zIFVTJCAzNjEgbWlsIGVuIHVubyBhIENvcnJlZ2lyLCBhc8OtIHF1ZSBjcmVjZXIgZW4gbG9zIHByaW1lcm9zIHJpbmRlIG3DoXMuDQoNCipQcmVndW50YTogwr9lbCBtYXJnZW4gYWx0byBlc3TDoSBhZ3J1cGFkbyBnZW9ncsOhZmljYW1lbnRlPyoNCg0KYGBge3IgbWFwYSwgZmlnLmhlaWdodD02fQ0KbWFwYV9lc3RhZG9zIDwtIHJlc3VtZW5fZXN0YWRvICU+JQ0KICBtdXRhdGUoY29kaWdvID0gc3RhdGUuYWJiW21hdGNoKGVzdGFkbywgc3RhdGUubmFtZSldKQ0KDQpwbG90X2dlbyhtYXBhX2VzdGFkb3MsIGxvY2F0aW9ubW9kZSA9ICJVU0Etc3RhdGVzIikgJT4lDQogIGFkZF90cmFjZSh6ID0gfm1hcmdlbiwgbG9jYXRpb25zID0gfmNvZGlnbywgY29sb3IgPSB+bWFyZ2VuLCBjb2xvcnMgPSAiQmx1ZXMiLA0KICAgICAgICAgICAgdGV4dCA9IH5wYXN0ZTAoZXN0YWRvLCAiPGJyPk1hcmdlbjogIiwgcGN0KG1hcmdlbiksICI8YnI+VmVudGFzOiBVUyQgIiwgcm91bmQodmVudGFzIC8gMWU2LCAyKSwgIiBNPGJyPkdydXBvOiAiLCBncnVwbyksDQogICAgICAgICAgICBob3ZlcmluZm8gPSAidGV4dCIpICU+JQ0KICBjb2xvcmJhcih0aXRsZSA9ICJNYXJnZW4gKCUpIikgJT4lDQogIGxheW91dCh0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj5GaWd1cmEgNi4gTWFyZ2VuIG9wZXJhdGl2byBwb3IgZXN0YWRvICglKTwvYj4iLCBmb250ID0gbGlzdChzaXplID0gMTUpKSwNCiAgICAgICAgIGdlbyA9IGxpc3Qoc2NvcGUgPSAidXNhIiwgcHJvamVjdGlvbiA9IGxpc3QodHlwZSA9ICJhbGJlcnMgdXNhIiksIHNob3dsYWtlcyA9IEZBTFNFKSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBFbnRyZSBtw6FzIG9zY3VybyBlbCBhenVsLCBtYXlvciBlbCBtYXJnZW4uIExvcyBtw6FyZ2VuZXMgbcOhcyBhbHRvcyBlc3TDoW4gZW4gZWwgU3VyIChBbGFiYW1hIDU0LDQlLCBUZW5uZXNzZWUgNDksNSUpIHkgbG9zIG3DoXMgYmFqb3MgZW4gZWwgUGFjw61maWNvOiBIYXdhaWkgKDI4LDUlKSwgV2FzaGluZ3RvbiAoMjgsOCUpIHkgQ2FsaWZvcm5pYSAoMzQsNSUpLiBTZWfDum4gbGEgT2ZpY2luYSBkZSBBbsOhbGlzaXMgRWNvbsOzbWljbyBkZSBFc3RhZG9zIFVuaWRvcyAoQkVBKSwgZW4gMjAyMSBIYXdhaWksIENhbGlmb3JuaWEgeSBOZXcgWW9yayBlcmFuIGxvcyBlc3RhZG9zIGNvbiBlbCBuaXZlbCBkZSBwcmVjaW9zIG3DoXMgYWx0bywgeSBNaXNzaXNzaXBwaSB5IEFsYWJhbWEgZXN0YWJhbiBlbnRyZSBsb3MgbcOhcyBiYWpvcy4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogRWwgbWFyZ2VuIGRlcGVuZGUgbXVjaG8gZGUgbGEgZ2VvZ3JhZsOtYS4gRWwgZGF0byBkZSBsYSBCRUEgc3VnaWVyZSBxdWUgb3BlcmFyIGVuIGxvcyBlc3RhZG9zIGRlIG1hcmdlbiBiYWpvIGN1ZXN0YSBtw6FzLCBhdW5xdWUgY29uIGxhIGJhc2Ugbm8gbG8gcG9kZW1vcyBjb21wcm9iYXIuDQoNCipQcmVndW50YTogwr9xdcOpIGNpdWRhZGVzIGFwb3J0YW4gbcOhcyB1dGlsaWRhZCB5IGNvbiBxdcOpIG1hcmdlbj8qDQoNCmBgYHtyIGNpdWRhZGVzLCBmaWcuaGVpZ2h0PTZ9DQpyZXN1bWVuX2NpdWRhZCA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uLCBlc3RhZG8sIGNpdWRhZCkgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwYXJ0X3V0aWxpZGFkID0gdXRpbGlkYWQgLyBzdW0odXRpbGlkYWQpICogMTAwLA0KICAgICAgICAgbm9tYnJlID0gcGFzdGUwKGNpdWRhZCwgIiAoIiwgZXN0YWRvLCAiKSDCtyAiLCBwY3QobWFyZ2VuKSkpICU+JQ0KICBhcnJhbmdlKGRlc2ModXRpbGlkYWQpKQ0KDQojIFBhcnRpY2lwYWNpw7NuIGRlIGxhcyAxMCBjaXVkYWRlcyBjb24gbcOhcyB1dGlsaWRhZA0KdG9wMTBfY2l1ZGFkZXMgPC0gc3VtKHJlc3VtZW5fY2l1ZGFkJHBhcnRfdXRpbGlkYWRbMToxMF0pDQoNCnRvcDE1IDwtIHJlc3VtZW5fY2l1ZGFkICU+JSBzbGljZSgxOjE1KSAlPiUgbXV0YXRlKG5vbWJyZSA9IGZhY3Rvcihub21icmUsIGxldmVscyA9IHJldihub21icmUpKSkNCg0KZ2dwbG90bHkoDQogIGdncGxvdCh0b3AxNSwgYWVzKHggPSB1dGlsaWRhZCAvIDFlNiwgeSA9IG5vbWJyZSwgZmlsbCA9IG1hcmdlbiwNCiAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMChub21icmUsICJcblV0aWxpZGFkOiBVUyQgIiwgcm91bmQodXRpbGlkYWQgLyAxZTYsIDIpLCAiIE0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcblZlbnRhczogVVMkICIsIHJvdW5kKHZlbnRhcyAvIDFlNiwgMiksICIgTVxuTWFyZ2VuOiAiLCBwY3QobWFyZ2VuKSkpKSArDQogICAgZ2VvbV9jb2woKSArDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiI2M2ZGJlZiIsIGhpZ2ggPSAiIzA4MzA2YiIpICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyYSA3LiBMYXMgMTUgY2l1ZGFkZXMgY29uIG3DoXMgdXRpbGlkYWQgb3BlcmF0aXZhIiwNCiAgICAgICAgIHggPSAiVXRpbGlkYWQgb3BlcmF0aXZhIChtaWxsb25lcyBkZSBVUyQpIiwgeSA9ICJDaXVkYWQgKG1hcmdlbikiLCBmaWxsID0gIk1hcmdlbiAoJSkiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lDQogIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB5ID0gLTAuMTUpKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIEVsIGxhcmdvIGRlIGxhIGJhcnJhIGVzIGxhIHV0aWxpZGFkIGRlIGNhZGEgY2l1ZGFkIHkgZWwgY29sb3Igc3UgbWFyZ2VuIChlbnRyZSBtw6FzIG9zY3VybywgbWF5b3IpLiBMYXMgMTAgcHJpbWVyYXMgY2l1ZGFkZXMgYXBvcnRhbiBlbCAzMiwwJSBkZSBsYSB1dGlsaWRhZC4gTmV3IFlvcmsgeSBTYW4gRnJhbmNpc2NvIHNvbiBsYXMgcXVlIG3DoXMgYXBvcnRhbiwgY29uIG3DoXJnZW5lcyBkZSAzNywzJSB5IDMyLDElLCBtaWVudHJhcyBCaXJtaW5naGFtICg1NCw0JSksIEtub3h2aWxsZSAoNDksNSUpIHkgRGFsbGFzICg0NCw5JSkgY29tYmluYW4gYmllbiB1dGlsaWRhZCB5IG1hcmdlbi4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogTGFzIGNpdWRhZGVzIHF1ZSBtw6FzIHV0aWxpZGFkIGFwb3J0YW4gbm8gc29uIGxhcyBtw6FzIHJlbnRhYmxlcyBwb3IgY2FkYSBkw7NsYXIgdmVuZGlkby4NCg0KKipJbXBsaWNhY2nDs24gcGFyYSBlbCBuZWdvY2lvLioqIFBhcmEgc2VndWlyIGNyZWNpZW5kbyBjb252aWVuZSBwcmlvcml6YXIgbG9zIGVzdGFkb3MgRXN0cmVsbGEgeSBsb3MgZGUgT3BvcnR1bmlkYWQsIHF1ZSB5YSBnYW5hbiA0Miw2JSBhdW5xdWUgdmVuZGFuIHBvY28sIHkgbm8gYnVzY2FyIG3DoXMgdm9sdW1lbiBlbiBsb3MgZXN0YWRvcyBhIENvcnJlZ2lyIGhhc3RhIG1lam9yYXIgc3UgbWFyZ2VuLg0KDQojIyBIYWxsYXpnbyAyIChwcm9mdW5kaXphY2nDs24pLiBXZXN0IGdhbmEgbWVub3MgY29uIGxvIG1pc21vIHF1ZSB2ZW5kZW4gbGFzIGRlbcOhcyByZWdpb25lcw0KDQoqUHJlZ3VudGE6IMK/V2VzdCB0aWVuZSBtZW5vciBtYXJnZW4gcG9ycXVlIHZlbmRlIHByb2R1Y3RvcyBvIGNhbmFsZXMgZGUgYmFqbyBtYXJnZW4sIG8gcG9ycXVlIGdhbmEgbWVub3MgZW4gbG8gbWlzbW8gcXVlIHZlbmRlbiBsYXMgZGVtw6FzIHJlZ2lvbmVzPyoNCg0KUGFyYSByZXNwb25kZXIsIGEgY2FkYSByZWdpc3RybyBsZSBhc2lnbmFtb3MgZWwgbWFyZ2VuIHF1ZSB0aWVuZSBlc2UgbWlzbW8gcHJvZHVjdG8sIGNvbiBlbCBtaXNtbyBtw6l0b2RvIGRlIHZlbnRhLCBhIG5pdmVsIG5hY2lvbmFsLiBDb24gZXNlIG1hcmdlbiBjYWxjdWxhbW9zIGxhIHV0aWxpZGFkIHF1ZSBzZSBlc3BlcmFyw61hLiBTaSBXZXN0IGdhbmFyYSBtZW5vcyBzb2xvIHBvcnF1ZSB2ZW5kZSBwcm9kdWN0b3MgbyBjYW5hbGVzIGRlIG1lbm9yIG1hcmdlbiwgc3UgdXRpbGlkYWQgcmVhbCBzZXLDrWEgcGFyZWNpZGEgYSBsYSBlc3BlcmFkYS4NCg0KYGBge3IgZXNwZXJhZG99DQojIE1hcmdlbiBuYWNpb25hbCBkZSBjYWRhIGNvbWJpbmFjacOzbiBwcm9kdWN0byB4IG3DqXRvZG8NCm1hcmdlbl9yZWZlcmVuY2lhIDwtIGRhdG9zICU+JQ0KICBncm91cF9ieShwcm9kdWN0bywgbWV0b2RvX3ZlbnRhKSAlPiUNCiAgc3VtbWFyaXNlKG1hcmdlbl9yZWYgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSAvIHN1bSh2ZW50YXNfdG90YWwpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQpkYXRvc19yZWYgPC0gZGF0b3MgJT4lDQogIGxlZnRfam9pbihtYXJnZW5fcmVmZXJlbmNpYSwgYnkgPSBjKCJwcm9kdWN0byIsICJtZXRvZG9fdmVudGEiKSkgJT4lDQogIG11dGF0ZSh1dGlsaWRhZF9lc3BlcmFkYSA9IHZlbnRhc190b3RhbCAqIG1hcmdlbl9yZWYpDQoNCmJyZWNoYV9yZWdpb24gPC0gZGF0b3NfcmVmICU+JQ0KICBncm91cF9ieShyZWdpb24pICU+JQ0KICBzdW1tYXJpc2UodmVudGFzID0gc3VtKHZlbnRhc190b3RhbCksDQogICAgICAgICAgICB1dGlsaWRhZF9yZWFsID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksDQogICAgICAgICAgICB1dGlsaWRhZF9lc3AgPSBzdW0odXRpbGlkYWRfZXNwZXJhZGEpKSAlPiUNCiAgbXV0YXRlKG1hcmdlbl9yZWFsID0gdXRpbGlkYWRfcmVhbCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIG1hcmdlbl9lc3BlcmFkbyA9IHV0aWxpZGFkX2VzcCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIGJyZWNoYV9wcCA9IG1hcmdlbl9yZWFsIC0gbWFyZ2VuX2VzcGVyYWRvLA0KICAgICAgICAgYnJlY2hhX3VzZCA9IHV0aWxpZGFkX3JlYWwgLSB1dGlsaWRhZF9lc3ApICU+JQ0KICBhcnJhbmdlKGJyZWNoYV91c2QpDQoNCmJyZWNoYV9yZWdpb24gJT4lDQogIHRyYW5zbXV0ZShgUmVnacOzbmAgPSByZWdpb24sIGBNYXJnZW4gcmVhbGAgPSBwY3QobWFyZ2VuX3JlYWwpLCBgTWFyZ2VuIGVzcGVyYWRvYCA9IHBjdChtYXJnZW5fZXNwZXJhZG8pLA0KICAgICAgICAgICAgYERpZmVyZW5jaWEgKHB1bnRvcylgID0gbnVtKGJyZWNoYV9wcCwgMSksIGBEaWZlcmVuY2lhIChVUyQpYCA9IHVzZChicmVjaGFfdXNkKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgOC4gTWFyZ2VuIHJlYWwgZnJlbnRlIGFsIGVzcGVyYWRvIHBvciBsYSBtZXpjbGEgZGUgcHJvZHVjdG8geSBtw6l0b2RvIiwgYWxpZ24gPSBjKCJsIiwgInIiLCAiciIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBXZXN0IHRpZW5lIHVuIG1hcmdlbiBkZSAzNSw3JSBjdWFuZG8sIHBvciBsbyBxdWUgdmVuZGUsIHNlIGVzcGVyYXLDrWEgMzgsOSU6IHNvbiAzLDIgcHVudG9zIG1lbm9zLCB1bm9zIFVTJCAxLDE2IG1pbGxvbmVzIGRlIHV0aWxpZGFkLiBTb3V0aCBlc3TDoSA0LDYgcHVudG9zIHBvciBlbmNpbWEgZGUgbG8gZXNwZXJhZG8uDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIFdlc3QgdmVuZGUgcHJvZHVjdG9zIHkgY2FuYWxlcyBwYXJlY2lkb3MgYSBsb3MgZGUgb3RyYXMgcmVnaW9uZXMsIHBlcm8gb2J0aWVuZSBtZW5vcyB1dGlsaWRhZCBjb24gZWxsb3MuDQoNCipQcmVndW50YTogwr9sYSBicmVjaGEgZGUgV2VzdCBlc3TDoSByZXBhcnRpZGEgZW4gdG9kYSBsYSByZWdpw7NuIG8gY29uY2VudHJhZGEgZW4gYWxndW5vcyBlc3RhZG9zPyoNCg0KYGBge3Igd2VzdF9lc3RhZG9zfQ0KYnJlY2hhX3dlc3QgPC0gZGF0b3NfcmVmICU+JQ0KICBmaWx0ZXIocmVnaW9uID09ICJXZXN0IikgJT4lDQogIGdyb3VwX2J5KGVzdGFkbykgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIGJyZWNoYV91c2QgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSAtIHN1bSh1dGlsaWRhZF9lc3BlcmFkYSkpICU+JQ0KICBtdXRhdGUoc2lnbm8gPSBpZmVsc2UoYnJlY2hhX3VzZCA8IDAsICJQb3IgZGViYWpvIGRlIGxvIGVzcGVyYWRvIiwgIlBvciBlbmNpbWEgZGUgbG8gZXNwZXJhZG8iKSwNCiAgICAgICAgIGVzdGFkbyA9IHJlb3JkZXIoZXN0YWRvLCBicmVjaGFfdXNkKSkNCg0KZ2dwbG90bHkoDQogIGdncGxvdChicmVjaGFfd2VzdCwgYWVzKHggPSBicmVjaGFfdXNkIC8gMTAwMCwgeSA9IGVzdGFkbywgZmlsbCA9IHNpZ25vLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKGVzdGFkbywgIlxuRGlmZXJlbmNpYTogVVMkICIsIHJvdW5kKGJyZWNoYV91c2QgLyAxMDAwKSwgIiBtaWwiKSkpICsNCiAgICBnZW9tX2NvbCgpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJQb3IgZGViYWpvIGRlIGxvIGVzcGVyYWRvIiA9ICJmaXJlYnJpY2siLCAiUG9yIGVuY2ltYSBkZSBsbyBlc3BlcmFkbyIgPSAiZm9yZXN0Z3JlZW4iKSkgKw0KICAgIGxhYnModGl0bGUgPSAiRmlndXJhIDguIFdlc3Q6IHV0aWxpZGFkIHJlYWwgbWVub3MgZXNwZXJhZGEsIHBvciBlc3RhZG8iLA0KICAgICAgICAgeCA9ICJNaWxlcyBkZSBVUyQiLCB5ID0gTlVMTCwgZmlsbCA9ICIiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lDQogIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB5ID0gLTAuMTUpKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIEVuIHJvam8gZXN0w6FuIGxvcyBlc3RhZG9zIHF1ZSBnYW5hbiBtZW5vcyBkZSBsbyBlc3BlcmFkbyB5IGVuIHZlcmRlIGxvcyBxdWUgZ2FuYW4gbcOhcy4gQ2FsaWZvcm5pYSwgSGF3YWlpIHkgV2FzaGluZ3RvbiBleHBsaWNhbiBlbCA3OCUgZGVsIGZhbHRhbnRlIGRlIFdlc3QsIG1pZW50cmFzIFV0YWgsIElkYWhvLCBDb2xvcmFkbyB5IE9yZWdvbiBlc3TDoW4gcG9yIGVuY2ltYSBkZSBsbyBlc3BlcmFkby4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogRWwgcHJvYmxlbWEgZXN0w6EgZW4gZXN0YWRvcyBwdW50dWFsZXMgeSBubyBlbiB0b2RhIGxhIHJlZ2nDs24uDQoNCipQcmVndW50YTogwr9xdcOpIHByb2R1Y3RvcyB5IHF1w6kgZGlzdHJpYnVpZG9yZXMgZXhwbGljYW4gbGEgYnJlY2hhPyoNCg0KYGBge3IgcHJvZHVjdG9fcmVnaW9ufQ0KIyBNYXJnZW4gZGUgY2FkYSBwcm9kdWN0byBlbiBjYWRhIHJlZ2nDs24NCmRhdG9zICU+JQ0KICBncm91cF9ieShwcm9kdWN0bywgcmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKG1hcmdlbiA9IHN1bSh1dGlsaWRhZF9vcGVyYXRpdmEpIC8gc3VtKHZlbnRhc190b3RhbCkgKiAxMDAsIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gcGN0KG1hcmdlbikpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVnaW9uLCB2YWx1ZXNfZnJvbSA9IG1hcmdlbikgJT4lDQogIHJlbmFtZShQcm9kdWN0byA9IHByb2R1Y3RvKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSA5LiBNYXJnZW4gb3BlcmF0aXZvIHBvciBwcm9kdWN0byB5IHJlZ2nDs24iLCBhbGlnbiA9IGMoImwiLCByZXAoInIiLCA1KSkpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipFdmlkZW5jaWEuKiogV29tZW4ncyBBcHBhcmVsIGdhbmEgMjksOCUgZW4gV2VzdCB5IDUyLDElIGVuIFNvdXRoLCAyMiBwdW50b3MgZGUgZGlmZXJlbmNpYS4gRXMgbGEgY29tYmluYWNpw7NuIGRlIHJlZ2nDs24geSBwcm9kdWN0byBjb24gZWwgbWF5b3IgZmFsdGFudGUgZnJlbnRlIGEgbG8gZXNwZXJhZG8sIHVub3MgVVMkIDczMyBtaWwuIEVuIFdlc3QgZWwgc2VndW5kbyBwcm9kdWN0byBtw6FzIGTDqWJpbCBlcyBNZW4ncyBBdGhsZXRpYyBGb290d2VhciAoMzEsOSUpLg0KDQoqKkludGVycHJldGFjacOzbi4qKiBVbiBtaXNtbyBwcm9kdWN0byByaW5kZSBtdXkgZGlzdGludG8gc2Vnw7puIGVsIG1lcmNhZG8gZG9uZGUgc2UgdmVuZGUuDQoNCmBgYHtyIGRpc3RyaWJ1aWRvcmVzfQ0KZGlzdHJpYnVpZG9yX3JlZ2lvbiA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yLCByZWdpb24pICU+JQ0KICBzdW1tYXJpc2UobWFyZ2VuID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkgLyBzdW0odmVudGFzX3RvdGFsKSAqIDEwMCwgLmdyb3VwcyA9ICJkcm9wIikNCg0KcGVvcl9yZWdpb24gPC0gZGlzdHJpYnVpZG9yX3JlZ2lvbiAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yKSAlPiUNCiAgc3VtbWFyaXNlKGBSZWdpw7NuIGRlIG1lbm9yIG1hcmdlbmAgPSByZWdpb25bd2hpY2gubWluKG1hcmdlbildLA0KICAgICAgICAgICAgYERpZmVyZW5jaWEgZW50cmUgc3UgbWVqb3IgeSBzdSBwZW9yIHJlZ2nDs24gKHB1bnRvcylgID0gbnVtKG1heChtYXJnZW4pIC0gbWluKG1hcmdlbiksIDEpKQ0KDQpwZXNvX3dlc3QgPC0gZGF0b3MgJT4lDQogIGZpbHRlcihyZWdpb24gPT0gIldlc3QiKSAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpKSAlPiUNCiAgbXV0YXRlKGAlIGRlIGxhcyB2ZW50YXMgZGUgV2VzdGAgPSBwY3QodmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDApKSAlPiUNCiAgc2VsZWN0KC12ZW50YXMpDQoNCmRpc3RyaWJ1aWRvcl9yZWdpb24gJT4lDQogIG11dGF0ZShtYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSByZWdpb24sIHZhbHVlc19mcm9tID0gbWFyZ2VuLCB2YWx1ZXNfZmlsbCA9ICJTaW4gdmVudGFzIikgJT4lDQogIGxlZnRfam9pbihwZW9yX3JlZ2lvbiwgYnkgPSAiZGlzdHJpYnVpZG9yIikgJT4lDQogIGxlZnRfam9pbihwZXNvX3dlc3QsIGJ5ID0gImRpc3RyaWJ1aWRvciIpICU+JQ0KICByZW5hbWUoRGlzdHJpYnVpZG9yID0gZGlzdHJpYnVpZG9yKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAxMC4gTWFyZ2VuIGRlIGNhZGEgZGlzdHJpYnVpZG9yIHBvciByZWdpw7NuIiwgYWxpZ24gPSBjKCJsIiwgcmVwKCJyIiwgOCkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IFRSVUUsIGZvbnRfc2l6ZSA9IDEzKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIENpbmNvIGRlIGxvcyBzZWlzIGRpc3RyaWJ1aWRvcmVzIHRpZW5lbiBzdSBtYXJnZW4gbcOhcyBiYWpvIGVuIFdlc3QuIFdhbG1hcnQsIHBvciBlamVtcGxvLCBnYW5hIDQxLDklIGVuIFNvdXRoIHkgMjcsMyUgZW4gV2VzdC4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogQ29tbyBjYXNpIHRvZG9zIGxvcyBkaXN0cmlidWlkb3JlcyBnYW5hbiBtZW5vcyBhbGzDrSwgbGEgY2F1c2EgcGFyZWNlIGVzdGFyIGVuIGVsIG1lcmNhZG8gZGUgV2VzdCB5IG5vIGVuIHVuIGRpc3RyaWJ1aWRvciBlbiBwYXJ0aWN1bGFyLg0KDQoqKkV4cGxpY2FjaW9uZXMgYWx0ZXJuYXRpdmFzIHkgZGF0b3MgcXVlIGZhbHRhbi4qKiBFbCBtYXJnZW4gYmFqbyBwb2Ryw61hIGRlYmVyc2UgYSAoYSkgY29zdG9zIGRlIG9wZXJhY2nDs24sIGxvZ8Otc3RpY2EgbyBhcnJpZW5kbyBtw6FzIGFsdG9zIGVuIGVzb3MgZXN0YWRvcywgKGIpIGRlc2N1ZW50b3MgbyBwcm9tb2Npb25lcyBkZSBsb3MgZGlzdHJpYnVpZG9yZXMsIG8gKGMpIHVuIGNvc3RvIGRlIHByb2R1Y3RvIGRpc3RpbnRvIGVuIGxhIHJlZ2nDs24uIFBhcmEgc2FiZXIgY3XDoWwgZXMgbGEgY2F1c2Egc2UgbmVjZXNpdGFuIGVsIGNvc3RvIGRlIHZlbnRhcywgbG9zIGRlc2N1ZW50b3MgYXBsaWNhZG9zIHkgbGEgZmVjaGEgZGUgY2FkYSB2ZW50YSwgcXVlIGxhIGJhc2Ugbm8gaW5jbHV5ZS4NCg0KKipJbXBsaWNhY2nDs24gcGFyYSBlbCBuZWdvY2lvLioqIENhbWJpYXIgZGUgZGlzdHJpYnVpZG9yIHByb2JhYmxlbWVudGUgbm8gbWVqb3JhcsOtYSBlbCBtYXJnZW4uIExvIHJlY29tZW5kYWJsZSBlcyByZXZpc2FyIGNvc3RvcyB5IHByZWNpb3MgZW4gQ2FsaWZvcm5pYSwgV2FzaGluZ3RvbiB5IEhhd2FpaSwgZW1wZXphbmRvIHBvciBXb21lbidzIEFwcGFyZWwgeSBwb3IgV2VzdCBHZWFyLCBxdWUgdmVuZGUgZWwgNTEsMyUgZGUgbG8gcXVlIHNlIHZlbmRlIGVuIFdlc3QuDQoNCiMjIEhhbGxhemdvIDMuIEVsIGNhbmFsIGltcG9ydGEsIHkgbm8gcmluZGUgaWd1YWwgZW4gdG9kYXMgcGFydGVzDQoNCipQcmVndW50YTogwr9xdcOpIGNvbWJpbmFjaW9uZXMgZGUgY2l1ZGFkIHkgbcOpdG9kbyBzb24gbGFzIG3DoXMgeSBsYXMgbWVub3MgcmVudGFibGVzPyoNCg0KYGBge3IgY2l1ZGFkX21ldG9kb30NCmNpdWRhZF9tZXRvZG8gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbiwgY2l1ZGFkLCBlc3RhZG8sIG1ldG9kb192ZW50YSkgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDApICU+JQ0KICBmaWx0ZXIodmVudGFzID49IDUwMDAwMCkgICAjIHNlIGRlamFuIGNvbWJpbmFjaW9uZXMgY29uIHZlbnRhcyByZWxldmFudGVzIChVUyQgNTAwIG1pbCBvIG3DoXMpDQoNCm1lam9yZXMgPC0gY2l1ZGFkX21ldG9kbyAlPiUgYXJyYW5nZShkZXNjKG1hcmdlbikpICU+JSBzbGljZSgxOjgpDQpwZW9yZXMgIDwtIGNpdWRhZF9tZXRvZG8gJT4lIGFycmFuZ2UobWFyZ2VuKSAlPiUgc2xpY2UoMTo4KQ0KDQpiaW5kX3Jvd3MobWVqb3JlcywgcGVvcmVzKSAlPiUNCiAgdHJhbnNtdXRlKENpdWRhZCA9IHBhc3RlMChjaXVkYWQsICIgKCIsIGVzdGFkbywgIikiKSwgYFJlZ2nDs25gID0gcmVnaW9uLCBgTcOpdG9kb2AgPSBtZXRvZG9fdmVudGEsDQogICAgICAgICAgICBWZW50YXMgPSB1c2QodmVudGFzKSwgVXRpbGlkYWQgPSB1c2QodXRpbGlkYWQpLCBNYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgMTEuIENvbWJpbmFjaW9uZXMgY2l1ZGFkIMOXIG3DqXRvZG8gZGUgdmVudGEgY29uIG1heW9yIHkgbWVub3IgbWFyZ2VuICh2ZW50YXMgZGUgVVMkIDUwMCBtaWwgbyBtw6FzKSIsDQogICAgICAgIGFsaWduID0gYygibCIsICJsIiwgImwiLCAiciIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBUUlVFLCBmb250X3NpemUgPSAxMykgJT4lDQogIHBhY2tfcm93cygiTWF5b3IgbWFyZ2VuIiwgMSwgOCkgJT4lDQogIHBhY2tfcm93cygiTWVub3IgbWFyZ2VuIiwgOSwgMTYpDQpgYGANCg0KKipFdmlkZW5jaWEuKiogTGFzIGNvbWJpbmFjaW9uZXMgbcOhcyByZW50YWJsZXMgc29uIHZlbnRhcyBPbmxpbmUgZW4gY2l1ZGFkZXMgZGVsIFN1ciwgY29tbyBEYWxsYXMgKDU0LDglKSwgeSBlbCBPdXRsZXQgZGUgQmlybWluZ2hhbSAoNTIsMyUpLiBPbmxpbmUgdGFtYmnDqW4gZ2FuYSBiaWVuIGVuIGVzdGFkb3MgZGVsIGdydXBvIGEgQ29ycmVnaXIsIGNvbW8gQWxiYW55LCBlbiBOZXcgWW9yayAoNTAsNCUpLiBMYXMgbWVub3MgcmVudGFibGVzIGVzdMOhbiBlbiBXZXN0OiBlbCBPdXRsZXQgZGUgSG9ub2x1bHUgKDI1LDklKSB5IGVsIGRlIFNhbiBGcmFuY2lzY28gKDMwLDElKSwgcXVlIGFkZW3DoXMgdmVuZGUgVVMkIDQsMDcgbWlsbG9uZXMuIEVuIGxhIEZpZ3VyYSAzLCBlbCBPdXRsZXQgZ2FuYSA0NCw2JSBlbiBTb3V0aCB5IDMyLDglIGVuIFdlc3QsIHkgT25saW5lIGdhbmEgNDgsNiUgZW4gTm9ydGhlYXN0IGF1bnF1ZSBwZXNhIHNvbG8gMTgsNCUgZGUgbGFzIHZlbnRhcyBkZSBlc2EgcmVnacOzbi4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogRWwgbWlzbW8gY2FuYWwgcHVlZGUgZGFyIGNhc2kgMTIgcHVudG9zIG3DoXMgZGUgbWFyZ2VuIHNlZ8O6biBlbCBtZXJjYWRvLCB5IE9ubGluZSBmdW5jaW9uYSBiaWVuIGluY2x1c28gZG9uZGUgbGEgcmVnacOzbiBnYW5hIHBvY28uDQoNCioqSW1wbGljYWNpw7NuIHBhcmEgZWwgbmVnb2Npby4qKiBIYXkgZXNwYWNpbyBwYXJhIGltcHVsc2FyIE9ubGluZSBlbiBOb3J0aGVhc3QsIHkgZWwgbW9kZWxvIGRlIE91dGxldCBkZSBTb3V0aCBlcyB1bmEgcHLDoWN0aWNhIHF1ZSB2YWxlIGxhIHBlbmEgZXN0dWRpYXIgcGFyYSBXZXN0Lg0KDQojIyBIYWxsYXpnbyA0IChubyBldmlkZW50ZSBhIHNpbXBsZSB2aXN0YSkuIFByZWNpbywgdm9sdW1lbiB5IHV0aWxpZGFkOiBsbyBxdWUgY2FtYmlhIGFsIGNvbXBhcmFyIGNhc29zIHBhcmVjaWRvcw0KDQoqUHJlZ3VudGE6IMK/Y8OzbW8gc2UgcmVsYWNpb25hbiBwcmVjaW8sIHZvbHVtZW4geSB1dGlsaWRhZCwgeSBsYSByZWxhY2nDs24gcXVlIHNlIHZlIGVuIGVsIHRvdGFsIHNlIG1hbnRpZW5lIHBvciByZWdpw7NuLCBwcm9kdWN0byB5IGVzdGFkbz8qDQoNClVuYSBjb3JyZWxhY2nDs24gZGVsIHRvdGFsIHB1ZWRlIGRlYmVyc2UgYSBsYSBjb21wb3NpY2nDs24gZGUgbGFzIHZlbnRhcy4gUG9yIGVzbyBsYSBjYWxjdWxhbW9zIHRhbWJpw6luICoqZGVudHJvKiogZGUgY2FkYSByZWdpw7NuLCBkZSBjYWRhIHByb2R1Y3RvLCBkZSBjYWRhIGVzdGFkbyB5IGRlIGNhZGEgY29tYmluYWNpw7NuIGRlIGVzdGFkbywgcHJvZHVjdG8geSBtw6l0b2RvLCByZXN0YW5kbyBhIGNhZGEgcmVnaXN0cm8gZWwgcHJvbWVkaW8gZGUgc3UgZ3J1cG8uDQoNCmBgYHtyIHJlbGFjaW9uZXN9DQojIENvcnJlbGFjacOzbiBkZW50cm8gZGUgZ3J1cG9zOiBzZSByZXN0YSBhIGNhZGEgdmFsb3IgZWwgcHJvbWVkaW8gZGUgc3UgZ3J1cG8gYW50ZXMgZGUgY29ycmVsYWNpb25hcg0KY29yX2RlbnRybyA8LSBmdW5jdGlvbih4LCB5LCBncnVwbykgY29yKHggLSBhdmUoeCwgZ3J1cG8pLCB5IC0gYXZlKHksIGdydXBvKSkNCg0KZ3J1cG9fcmVnaW9uICAgPC0gZGF0b3MkcmVnaW9uDQpncnVwb19wcm9kdWN0byA8LSBkYXRvcyRwcm9kdWN0bw0KZ3J1cG9fZXN0YWRvICAgPC0gZGF0b3MkZXN0YWRvDQpncnVwb19maW5vICAgICA8LSBwYXN0ZShkYXRvcyRlc3RhZG8sIGRhdG9zJHByb2R1Y3RvLCBkYXRvcyRtZXRvZG9fdmVudGEpDQoNCnJlbGFjaW9uZXMgPC0gZGF0YS5mcmFtZSgNCiAgYFJlbGFjacOzbmAgPSBjKCJQcmVjaW8geSB1bmlkYWRlcyIsICJQcmVjaW8geSBtYXJnZW4iLCAiVW5pZGFkZXMgeSBtYXJnZW4iLCAiVW5pZGFkZXMgeSB1dGlsaWRhZCIpLA0KICBUb3RhbCA9IGMoY29yKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzKSwNCiAgICAgICAgICAgIGNvcihkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvKSwNCiAgICAgICAgICAgIGNvcihkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2byksDQogICAgICAgICAgICBjb3IoZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSkpLA0KICBgRGVudHJvIGRlIGNhZGEgcmVnacOzbmAgPSBjKGNvcl9kZW50cm8oZGF0b3MkcHJlY2lvX3VuaWRhZCwgZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGdydXBvX3JlZ2lvbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8sIGdydXBvX3JlZ2lvbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19yZWdpb24pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkdXRpbGlkYWRfb3BlcmF0aXZhLCBncnVwb19yZWdpb24pKSwNCiAgYERlbnRybyBkZSBjYWRhIHByb2R1Y3RvYCA9IGMoY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZ3J1cG9fcHJvZHVjdG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8sIGdydXBvX3Byb2R1Y3RvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fcHJvZHVjdG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBkYXRvcyR1dGlsaWRhZF9vcGVyYXRpdmEsIGdydXBvX3Byb2R1Y3RvKSksDQogIGBEZW50cm8gZGUgY2FkYSBlc3RhZG9gID0gYyhjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBncnVwb19lc3RhZG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19lc3RhZG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fZXN0YWRvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcl9kZW50cm8oZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSwgZ3J1cG9fZXN0YWRvKSksDQogIGBEZW50cm8gZGUgZXN0YWRvLCBwcm9kdWN0byB5IG3DqXRvZG9gID0gYyhjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBncnVwb19maW5vKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19maW5vKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fZmlubyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcl9kZW50cm8oZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSwgZ3J1cG9fZmlubykpLA0KICBjaGVjay5uYW1lcyA9IEZBTFNFKQ0KDQpyZWxhY2lvbmVzICU+JQ0KICBtdXRhdGUoYWNyb3NzKC1gUmVsYWNpw7NuYCwgfiBudW0oLngsIDIpKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgMTIuIENvcnJlbGFjacOzbiBkZSBQZWFyc29uIGVuIGVsIHRvdGFsIHkgZGVudHJvIGRlIHNlZ21lbnRvcyIsIGFsaWduID0gYygibCIsIHJlcCgiciIsIDUpKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBFbiBlbCB0b3RhbCwgdW4gcHJlY2lvIG3DoXMgYWx0byBzZSBhc29jaWEgY29uIG1lbm9yIG1hcmdlbiAo4oiSMCwxNCkuIEVzYSByZWxhY2nDs24gc2UgbWFudGllbmUgZGVudHJvIGRlIGNhZGEgcHJvZHVjdG8gKOKIkjAsMTYpLCBzZSBkZWJpbGl0YSBkZW50cm8gZGUgY2FkYSByZWdpw7NuICjiiJIwLDA3KSB5IGRlc2FwYXJlY2UgZGVudHJvIGRlIGNhZGEgZXN0YWRvICgwLDAwKS4gTGEgcmVsYWNpw7NuIGVudHJlIHByZWNpbyB5IHVuaWRhZGVzIGVzIHBvc2l0aXZhIGVuIHRvZG9zIGxvcyBuaXZlbGVzIChlbnRyZSAwLDI2IHkgMCwzMyksIHkgbGEgZGUgdW5pZGFkZXMgeSBtYXJnZW4gc2UgdnVlbHZlIG3DoXMgZnVlcnRlIGFsIGNvbXBhcmFyIGNhc29zIHBhcmVjaWRvcyAoZGUg4oiSMCwzMSBhIOKIkjAsNDUpLg0KDQoqKkludGVycHJldGFjacOzbi4qKiBMYSByZWxhY2nDs24gbmVnYXRpdmEgZW50cmUgcHJlY2lvIHkgbWFyZ2VuIGVzIHVuIGVmZWN0byBkZSBjb21wb3NpY2nDs246IGFwYXJlY2UgcG9ycXVlIGxvcyBlc3RhZG9zIGNhcm9zIHNvbiBsb3MgZGUgbWFyZ2VuIGJham8uIExhIHJlbGFjacOzbiBlbnRyZSB1bmlkYWRlcyB5IHV0aWxpZGFkICgwLDg3KSBlcyBhbHRhIHBvcnF1ZSBsYSB1dGlsaWRhZCBkZXBlbmRlIGRpcmVjdGFtZW50ZSBkZSBsYXMgdmVudGFzLCBhc8OtIHF1ZSBubyBlcyB1biBoYWxsYXpnbyBlbiBzw60gbWlzbWEuDQoNCipQcmVndW50YTogwr9sb3MgZXN0YWRvcyBjb24gcHJlY2lvIG3DoXMgYWx0byB0aWVuZW4gbWVqb3IgbyBwZW9yIG1hcmdlbj8qDQoNCmBgYHtyIHByZWNpb19tYXJnZW5fZXN0YWRvfQ0KZ2dwbG90bHkoDQogIGdncGxvdChyZXN1bWVuX2VzdGFkbywgYWVzKHggPSBwcmVjaW9fbWVkaW8sIHkgPSBtYXJnZW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoZXN0YWRvLCAiXG5QcmVjaW8gbWVkaW86IFVTJCAiLCByb3VuZChwcmVjaW9fbWVkaW8sIDEpLCAiXG5NYXJnZW46ICIsIHBjdChtYXJnZW4pKSkpICsNCiAgICBnZW9tX3BvaW50KGNvbG9yID0gInN0ZWVsYmx1ZSIsIHNpemUgPSAyLjUsIGFscGhhID0gMC44KSArDQogICAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gMSksIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gImdyZXk0MCIpICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyYSA5LiBQcmVjaW8gbWVkaW8gdnMuIG1hcmdlbiwgcG9yIGVzdGFkbyIsDQogICAgICAgICB4ID0gIlByZWNpbyBtZWRpbyBwb3IgdW5pZGFkIChVUyQpIiwgeSA9ICJNYXJnZW4gb3BlcmF0aXZvIGFncmVnYWRvICglKSIpICsNCiAgICB0ZW1hLA0KICB0b29sdGlwID0gInRleHQiKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIEVudHJlIGVzdGFkb3MsIGxhIGNvcnJlbGFjacOzbiBlbnRyZSBwcmVjaW8gbWVkaW8geSBtYXJnZW4gZXMg4oiSMCw1Ny4gV2FzaGluZ3RvbiB5IEhhd2FpaSB2ZW5kZW4gYSBVUyQgNjksMSB5IFVTJCA2Nyw3IHBvciB1bmlkYWQsIGNvbiBtw6FyZ2VuZXMgZGUgMjgsOCUgeSAyOCw1JS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogTG9zIG1lcmNhZG9zIG3DoXMgY2Fyb3Mgbm8gc29uIGxvcyBtw6FzIHJlbnRhYmxlcy4gRXN0byBjb25maXJtYSBxdWUgbGEgcmVsYWNpw7NuIG5lZ2F0aXZhIGVudHJlIHByZWNpbyB5IG1hcmdlbiB2aWVuZSBkZSBsYSBtZXpjbGEgZGUgZXN0YWRvcy4NCg0KKlByZWd1bnRhOiBkZW50cm8gZGUgdW4gbWlzbW8gZXN0YWRvLCBwcm9kdWN0byB5IG3DqXRvZG8sIMK/bG9zIHBlcmlvZG9zIGVuIHF1ZSBzZSB2ZW5kZW4gbcOhcyB1bmlkYWRlcyBkZWphbiBtZWpvciBvIHBlb3IgbWFyZ2VuPyoNCg0KUGFyYSBjb21wYXJhciBjYXNvcyBzaW1pbGFyZXMsIG1hcmNhbW9zIGNvbW8gZGUgdm9sdW1lbiBhbHRvIGxvcyByZWdpc3Ryb3MgY3V5YXMgdW5pZGFkZXMgc3VwZXJhbiBsYSBtZWRpYW5hIGRlIHN1IG1pc21vIGVzdGFkbywgcHJvZHVjdG8geSBtw6l0b2RvIGRlIHZlbnRhLg0KDQpgYGB7ciB2b2x1bWVufQ0KZGF0b3Nfdm9sdW1lbiA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoZXN0YWRvLCBwcm9kdWN0bywgbWV0b2RvX3ZlbnRhKSAlPiUNCiAgbXV0YXRlKHZvbHVtZW4gPSBpZmVsc2UodW5pZGFkZXNfdmVuZGlkYXMgPiBtZWRpYW4odW5pZGFkZXNfdmVuZGlkYXMpLCAiVm9sdW1lbiBhbHRvIiwgIlZvbHVtZW4gYmFqbyBvIG1lZGlvIikpICU+JQ0KICB1bmdyb3VwKCkNCg0KcmVzdW1lbl92b2x1bWVuIDwtIGRhdG9zX3ZvbHVtZW4gJT4lDQogIGdyb3VwX2J5KG1ldG9kb192ZW50YSwgdm9sdW1lbikgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksDQogICAgICAgICAgICB1bmlkYWRlcyA9IHN1bSh1bmlkYWRlc192ZW5kaWRhcyksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwcmVjaW9fbWVkaW8gPSB2ZW50YXMgLyB1bmlkYWRlcykNCg0KZ2dwbG90bHkoDQogIGdncGxvdChyZXN1bWVuX3ZvbHVtZW4sIGFlcyh4ID0gbWV0b2RvX3ZlbnRhLCB5ID0gbWFyZ2VuLCBmaWxsID0gdm9sdW1lbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAobWV0b2RvX3ZlbnRhLCAiIMK3ICIsIHZvbHVtZW4sICJcbk1hcmdlbjogIiwgcGN0KG1hcmdlbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcblByZWNpbyBtZWRpbzogVVMkICIsIHJvdW5kKHByZWNpb19tZWRpbywgMSkpKSkgKw0KICAgIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlZvbHVtZW4gYWx0byIgPSAibmF2eSIsICJWb2x1bWVuIGJham8gbyBtZWRpbyIgPSAibGlnaHRzdGVlbGJsdWUiKSkgKw0KICAgIGxhYnModGl0bGUgPSAiRmlndXJhIDEwLiBNYXJnZW4gc2Vnw7puIGVsIHZvbHVtZW4gdmVuZGlkbyIsDQogICAgICAgICB4ID0gIk3DqXRvZG8gZGUgdmVudGEiLCB5ID0gIk1hcmdlbiBvcGVyYXRpdm8gYWdyZWdhZG8gKCUpIiwgZmlsbCA9ICIiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lDQogIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB5ID0gLTAuMTUpKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIENvbXBhcmFuZG8gcmVnaXN0cm9zIGRlbCBtaXNtbyBlc3RhZG8sIHByb2R1Y3RvIHkgbcOpdG9kbywgZW4gT25saW5lIGxvcyBwZXJpb2RvcyBkZSBtYXlvciB2b2x1bWVuIGdhbmFuIDQxLDMlLCBmcmVudGUgYSA0OSwzJSBlbiBsb3MgZGVtw6FzLiBFbiBPdXRsZXQgbGEgY2HDrWRhIGVzIG1lbm9yICgzNywzJSBmcmVudGUgYSAzOCw5JSkgeSBlbiBJbi1zdG9yZSBubyBoYXkgY2HDrWRhICgzNiwyJSBmcmVudGUgYSAzNSwyJSkuDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIFZlbmRlciBtw6FzIHVuaWRhZGVzIGVuIHVuIG1pc21vIG1lcmNhZG8gc2UgYXNvY2lhIGNvbiB1biBtYXJnZW4gbWVub3IsIHNvYnJlIHRvZG8gZW4gT25saW5lLg0KDQpgYGB7ciB0YWJsYV92b2x1bWVufQ0KcmVzdW1lbl92b2x1bWVuICU+JQ0KICB0cmFuc211dGUoYE3DqXRvZG9gID0gbWV0b2RvX3ZlbnRhLCBWb2x1bWVuID0gdm9sdW1lbiwgTWFyZ2VuID0gcGN0KG1hcmdlbiksDQogICAgICAgICAgICBgUHJlY2lvIG1lZGlvIHBvciB1bmlkYWRgID0gcGFzdGUwKCJVUyQgIiwgbnVtKHByZWNpb19tZWRpbywgMSkpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAxMy4gTWFyZ2VuIHkgcHJlY2lvIHNlZ8O6biBlbCB2b2x1bWVuIHZlbmRpZG8iLCBhbGlnbiA9IGMoImwiLCAibCIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBFbiBsb3MgcGVyaW9kb3MgZGUgdm9sdW1lbiBhbHRvIGVsIHByZWNpbyBwb3IgdW5pZGFkIGVzIG3DoXMgYWx0bzogZW4gT25saW5lLCBVUyQgNDksMCBmcmVudGUgYSBVUyQgNDUsMS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogTGEgY2HDrWRhIGRlbCBtYXJnZW4gbm8gcGFyZWNlIHZlbmlyIGRlIGRlc2N1ZW50b3MuIFBvZHLDrWEgZGViZXJzZSBhIG1heW9yZXMgY29zdG9zIGFsIGF0ZW5kZXIgbG9zIHBpY29zIGRlIGRlbWFuZGEsIGNvbW8gZW52w61vcyBvIGxvZ8Otc3RpY2EsIHBlcm8gZXMgdW5hIGhpcMOzdGVzaXMuDQoNCioqSW1wbGljYWNpw7NuIHBhcmEgZWwgbmVnb2Npby4qKiBTaSBPbmxpbmUgY3JlY2Ugc29sbyBjb24gbcOhcyB2b2x1bWVuLCBwcm9iYWJsZW1lbnRlIHN1IG1hcmdlbiBiYWplLCBwb3IgZXNvIHByb3BvbmVtb3MgZW1wZXphciBjb24gdW5hIHBydWViYSBjb250cm9sYWRhLiBFc3RhcyBjb3JyZWxhY2lvbmVzIG5vIHBlcm1pdGVuIGRlY2lkaXIgcHJlY2lvczsgcGFyYSBlc28gc2UgbmVjZXNpdGFyw61hbiBkYXRvcyBkZSBjb3N0b3MgeSBsYSBmZWNoYSBkZSBjYWRhIHZlbnRhLg0KDQojIyBIYWxsYXpnbyA1IChubyBldmlkZW50ZSBhIHNpbXBsZSB2aXN0YSkuIFBvY2FzIGNvbWJpbmFjaW9uZXMgZ2VuZXJhbiBsYSBtaXRhZCBkZSBsYSB1dGlsaWRhZA0KDQoqUHJlZ3VudGE6IMK/bGEgdXRpbGlkYWQgZXN0w6EgcmVwYXJ0aWRhIGVudHJlIG11Y2hhcyBjb21iaW5hY2lvbmVzIGRlIGNpdWRhZCwgcHJvZHVjdG8geSBtw6l0b2RvLCBvIGNvbmNlbnRyYWRhIGVuIHBvY2FzPyoNCg0KYGBge3IgY29uY2VudHJhY2lvbn0NCmNvbWJpbmFjaW9uZXMgPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbiwgY2l1ZGFkLCBlc3RhZG8sIHByb2R1Y3RvLCBtZXRvZG9fdmVudGEpICU+JQ0KICBzdW1tYXJpc2UodXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGFycmFuZ2UoZGVzYyh1dGlsaWRhZCkpICU+JQ0KICBtdXRhdGUoYWN1bXVsYWRvID0gY3Vtc3VtKHV0aWxpZGFkKSAvIHN1bSh1dGlsaWRhZCkgKiAxMDApDQoNCm5fbWl0YWQgPC0gd2hpY2goY29tYmluYWNpb25lcyRhY3VtdWxhZG8gPj0gNTApWzFdDQoNCmRhdGEuZnJhbWUoSW5kaWNhZG9yID0gYygiQ29tYmluYWNpb25lcyBkZSBjaXVkYWQgw5cgcHJvZHVjdG8gw5cgbcOpdG9kbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkNvbWJpbmFjaW9uZXMgcXVlIGdlbmVyYW4gZWwgNTAlIGRlIGxhIHV0aWxpZGFkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiUHJvcG9yY2nDs24gZGUgY29tYmluYWNpb25lcyBxdWUgZ2VuZXJhbiBlbCA1MCUgZGUgbGEgdXRpbGlkYWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJSZWdpw7NuIG3DoXMgZnJlY3VlbnRlIGVuIGVzYXMgY29tYmluYWNpb25lcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIk3DqXRvZG8gZGUgdmVudGEgbcOhcyBmcmVjdWVudGUgZW4gZXNhcyBjb21iaW5hY2lvbmVzIiksDQogICAgICAgICAgIFZhbG9yID0gYyhucm93KGNvbWJpbmFjaW9uZXMpLCBuX21pdGFkLCBwY3Qobl9taXRhZCAvIG5yb3coY29tYmluYWNpb25lcykgKiAxMDApLA0KICAgICAgICAgICAgICAgICAgICAgbmFtZXMoc29ydCh0YWJsZShoZWFkKGNvbWJpbmFjaW9uZXMkcmVnaW9uLCBuX21pdGFkKSksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMV0sDQogICAgICAgICAgICAgICAgICAgICBuYW1lcyhzb3J0KHRhYmxlKGhlYWQoY29tYmluYWNpb25lcyRtZXRvZG9fdmVudGEsIG5fbWl0YWQpKSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxXSkpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDE0LiBDb25jZW50cmFjacOzbiBkZSBsYSB1dGlsaWRhZCIsIGFsaWduID0gYygibCIsICJyIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipFdmlkZW5jaWEuKiogRGUgNjkwIGNvbWJpbmFjaW9uZXMgZGUgY2l1ZGFkLCBwcm9kdWN0byB5IG3DqXRvZG8sIDEzNCAoZWwgMTklKSBnZW5lcmFuIGxhIG1pdGFkIGRlIGxhIHV0aWxpZGFkLiBXZXN0IGVzIGxhIHJlZ2nDs24gcXVlIG3DoXMgYXBhcmVjZSBlbiBlc2UgZ3J1cG8geSBPbmxpbmUgZWwgbcOpdG9kbyBtw6FzIGZyZWN1ZW50ZS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogQXVucXVlIFdlc3QgZ2FuZSBtZW5vcyBwb3IgY2FkYSBkw7NsYXIsIHN1cyBuZWdvY2lvcyBncmFuZGVzIHNpZ3VlbiBlbnRyZSBsb3MgcXVlIG3DoXMgdXRpbGlkYWQgYXBvcnRhbiBhIGxhIGVtcHJlc2EuDQoNCioqSW1wbGljYWNpw7NuIHBhcmEgZWwgbmVnb2Npby4qKiBIYXkgcXVlIGN1aWRhciBlc2FzIDEzNCBjb21iaW5hY2lvbmVzIChpbnZlbnRhcmlvLCBkaXNwb25pYmlsaWRhZCB5IHNlcnZpY2lvIGFsIGRpc3RyaWJ1aWRvcikgeSBtZWpvcmFyIGVsIG1hcmdlbiBkZSBXZXN0IHNpbiBwZXJkZXIgc3UgcHJlc2VuY2lhLg0KDQojICoqUmVjb21lbmRhY2lvbmVzIHkgbMOtbWl0ZXMqKg0KDQpDb24gYmFzZSBlbiBsb3MgaGFsbGF6Z29zLCBwcm9wb25lbW9zIGRpcmlnaXIgZWwgY3JlY2ltaWVudG8gZGUgbGFzIHZlbnRhcyBoYWNpYSBsb3MgbWVyY2Fkb3MgeSBjYW5hbGVzIGRvbmRlIGNhZGEgZMOzbGFyIGRlamEgbcOhcyB1dGlsaWRhZCwgeSBtZWpvcmFyIHByaW1lcm8gZWwgbWFyZ2VuIGRlIGxvcyBtZXJjYWRvcyBncmFuZGVzIHF1ZSBob3kgZ2FuYW4gcG9jby4gTGEgY29tYmluYWNpw7NuIHF1ZSBtZXJlY2UgYXRlbmNpw7NuIHByaW9yaXRhcmlhIGVzIFdvbWVuJ3MgQXBwYXJlbCBlbiBXZXN0LCBzb2JyZSB0b2RvIGVuIENhbGlmb3JuaWEsIGRvbmRlIGdhbmEgMTksNiUgZnJlbnRlIGEgdW4gbWFyZ2VuIGVzcGVyYWRvIGRlIDM4LDklIHkgZGVqYSBkZSBjYXB0dXJhciB1bm9zIFVTJCAzMjIgbWlsIChVUyQgNzMzIG1pbCBlbiB0b2RhIGxhIHJlZ2nDs24pLg0KDQojIyBBY2Npb25lcyBwcmlvcml6YWRhcw0KDQp8IEFjY2nDs24gfCBFdmlkZW5jaWEgfCBCZW5lZmljaW8gZXNwZXJhZG8gKGVzY2VuYXJpbyBwcm9wdWVzdG8pIHwgUmllc2dvIG8gc3VwdWVzdG8gfCDDgXJlYSByZXNwb25zYWJsZSB8IEluZGljYWRvciBkZSBzZWd1aW1pZW50byB8DQp8LS0tfC0tLXwtLS18LS0tfC0tLXwtLS18DQp8ICoqMS4gQ3JlY2VyIGVuIGxvcyBlc3RhZG9zIEVzdHJlbGxhIHkgZGUgT3BvcnR1bmlkYWQqKiwgY29uIHByaW9yaWRhZCBlbiBUZXhhcywgQWxhYmFtYSwgVGVubmVzc2VlLCBMb3Vpc2lhbmEsIGxhcyBDYXJvbGluYXMsIE1pY2hpZ2FuIHkgT2hpbyB8IEhhbGxhemdvIDE6IG1hcmdlbiBkZSA0NCw2JSAoRXN0cmVsbGEpIHkgNDIsNiUgKE9wb3J0dW5pZGFkKSwgZnJlbnRlIGEgMzYsMSUgZGUgbG9zIGVzdGFkb3MgYSBDb3JyZWdpciB8IFVub3MgVVMkIDIsMDkgTSAoNCw0JSBkZSBsYSB1dGlsaWRhZCkgc2kgc3VzIHZlbnRhcyBjcmVjZW4gMTAlIHwgUXVlIGVsIG1hcmdlbiBzZSBtYW50ZW5nYSBhbCBjcmVjZXIsIHBvcnF1ZSBlbiBsb3MgcGVyaW9kb3MgZGUgbcOhcyB2b2x1bWVuIGVsIG1hcmdlbiBiYWphIChIYWxsYXpnbyA0KSB8IEdlcmVuY2lhIGNvbWVyY2lhbCB5IE1lcmNhZGVvLCBjb24gRmluYW56YXMgfCBWZW50YXMgeSBtYXJnZW4gZGUgY2FkYSBncnVwbyBkZSBlc3RhZG9zIHwNCnwgKioyLiBQcnVlYmEgcGlsb3RvIGRlIE9ubGluZSBlbiBOb3J0aGVhc3QqKiwgeSBsdWVnbyBlbiBXZXN0IHwgSGFsbGF6Z28gMzogT25saW5lIGdhbmEgNDgsNiUgZW4gTm9ydGhlYXN0LCBwZXJvIHBlc2Egc29sbyAxOCw0JSBkZSBzdXMgdmVudGFzIHwgVW5vcyBVUyQgMCwyNCBNIHNpIGVsIDEwJSBkZSBsYXMgdmVudGFzIEluLXN0b3JlIHkgT3V0bGV0IGRlIE5vcnRoZWFzdCBwYXNhIGEgT25saW5lIHwgRW4gT25saW5lIGVsIG1hcmdlbiBiYWphIDggcHVudG9zIGN1YW5kbyBzdWJlIGVsIHZvbHVtZW4gKEhhbGxhemdvIDQpLCBhc8OtIHF1ZSBlbCBiZW5lZmljaW8gcG9kcsOtYSBzZXIgbWVub3I7IGhheSBxdWUgbWVkaXJsbyBlbiBkw7NsYXJlcyB8IENvbWVyY2lvIGVsZWN0csOzbmljbywgY29uIEZpbmFuemFzIHwgUGVzbyBkZSBPbmxpbmUgZW4gbGFzIHZlbnRhcyBkZSBjYWRhIHJlZ2nDs24geSBtYXJnZW4gcG9yIGNhbmFsIHwNCnwgKiozLiBDb3JyZWdpciBlbCBtYXJnZW4gZW4gQ2FsaWZvcm5pYSwgV2FzaGluZ3RvbiB5IEhhd2FpaSoqLCBlbXBlemFuZG8gcG9yIFdvbWVuJ3MgQXBwYXJlbCwgbG9zIE91dGxldCBkZSBTYW4gRnJhbmNpc2NvIHkgSG9ub2x1bHUsIHkgV2VzdCBHZWFyIHwgSGFsbGF6Z28gMjogV2VzdCBnYW5hIDMsMiBwdW50b3MgbWVub3MgZGUgbG8gZXNwZXJhZG8gKFVTJCAxLDE2IE0pOyBXb21lbidzIEFwcGFyZWwgZ2FuYSAyOSw4JSBlbiBXZXN0IHkgNTIsMSUgZW4gU291dGggfCBVbm9zIFVTJCAwLDU4IE0gc2kgc2UgY2llcnJhIGxhIG1pdGFkIGRlIGxhIGJyZWNoYSBkZSBXZXN0IHwgVG9kYXbDrWEgbm8gY29ub2NlbW9zIGxhIGNhdXNhLiBDb21vIHRvZG9zIGxvcyBkaXN0cmlidWlkb3JlcyBnYW5hbiBtZW5vcyBlbiBXZXN0LCBjYW1iaWFyIGRlIGRpc3RyaWJ1aWRvciBubyBwYXJlY2UgbGEgc29sdWNpw7NuIHwgRmluYW56YXMgY29tZXJjaWFsLCBjb24gbGEgZ2VyZW5jaWEgZGUgV2VzdCB8IE1hcmdlbiBkZSBXZXN0IChob3kgMzUsNyUpIHkgZGUgV29tZW4ncyBBcHBhcmVsIGVuIFdlc3QgKGhveSAyOSw4JSkgfA0KDQpgYGB7ciBlc2NlbmFyaW9zfQ0KIyBFc2NlbmFyaW8gMTogMTAlIG3DoXMgZGUgdmVudGFzIGVuIGVzdGFkb3MgRXN0cmVsbGEgeSBPcG9ydHVuaWRhZCwgY29uIHN1IG1hcmdlbiBhY3R1YWwNCmNyZWNlciA8LSByZXN1bWVuX2VzdGFkbyAlPiUgZmlsdGVyKGdydXBvICVpbiUgYygiRXN0cmVsbGEiLCAiT3BvcnR1bmlkYWQiKSkNCmVzY2VuYXJpb18xIDwtIHN1bShjcmVjZXIkdmVudGFzKSAqIDAuMTAgKiAoc3VtKGNyZWNlciR1dGlsaWRhZCkgLyBzdW0oY3JlY2VyJHZlbnRhcykpDQoNCiMgRXNjZW5hcmlvIDI6IDEwJSBkZSBsYXMgdmVudGFzIEluLXN0b3JlIHkgT3V0bGV0IGRlIE5vcnRoZWFzdCBwYXNhbiBhIE9ubGluZSwgY29uIGxvcyBtw6FyZ2VuZXMgYWN0dWFsZXMNCm5lIDwtIHJlZ2lvbl9tZXRvZG8gJT4lIGZpbHRlcihyZWdpb24gPT0gIk5vcnRoZWFzdCIpDQp2ZW50YXNfbW92aWRhcyA8LSAwLjEwICogc3VtKG5lJHZlbnRhc1tuZSRtZXRvZG9fdmVudGEgIT0gIk9ubGluZSJdKQ0KbWFyZ2VuX29mZmxpbmUgPC0gc3VtKG5lJHV0aWxpZGFkW25lJG1ldG9kb192ZW50YSAhPSAiT25saW5lIl0pIC8gc3VtKG5lJHZlbnRhc1tuZSRtZXRvZG9fdmVudGEgIT0gIk9ubGluZSJdKQ0KbWFyZ2VuX29ubGluZSAgPC0gbmUkdXRpbGlkYWRbbmUkbWV0b2RvX3ZlbnRhID09ICJPbmxpbmUiXSAvIG5lJHZlbnRhc1tuZSRtZXRvZG9fdmVudGEgPT0gIk9ubGluZSJdDQplc2NlbmFyaW9fMiA8LSB2ZW50YXNfbW92aWRhcyAqIChtYXJnZW5fb25saW5lIC0gbWFyZ2VuX29mZmxpbmUpDQoNCiMgRXNjZW5hcmlvIDM6IGNlcnJhciBsYSBtaXRhZCBkZSBsYSBkaWZlcmVuY2lhIGRlIFdlc3QgZnJlbnRlIGFsIG1hcmdlbiBlc3BlcmFkbw0KZXNjZW5hcmlvXzMgPC0gLWJyZWNoYV9yZWdpb24kYnJlY2hhX3VzZFticmVjaGFfcmVnaW9uJHJlZ2lvbiA9PSAiV2VzdCJdIC8gMg0KDQpkYXRhLmZyYW1lKGBBY2Npw7NuYCA9IGMoIkFjY2nDs24gMSIsICJBY2Npw7NuIDIiLCAiQWNjacOzbiAzIiksDQogICAgICAgICAgIEVzY2VuYXJpbyA9IGMoIkxhcyB2ZW50YXMgZGUgbG9zIGVzdGFkb3MgRXN0cmVsbGEgeSBkZSBPcG9ydHVuaWRhZCBjcmVjZW4gMTAlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiRWwgMTAlIGRlIGxhcyB2ZW50YXMgSW4tc3RvcmUgeSBPdXRsZXQgZGUgTm9ydGhlYXN0IHBhc2EgYSBPbmxpbmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJTZSBjaWVycmEgbGEgbWl0YWQgZGUgbGEgYnJlY2hhIGRlIFdlc3QgZnJlbnRlIGEgbG8gZXNwZXJhZG8iKSwNCiAgICAgICAgICAgYFV0aWxpZGFkIGFkaWNpb25hbGAgPSB1c2QoYyhlc2NlbmFyaW9fMSwgZXNjZW5hcmlvXzIsIGVzY2VuYXJpb18zKSksDQogICAgICAgICAgIGAlIGRlIGxhIHV0aWxpZGFkIGFjdHVhbGAgPSBwY3QoYyhlc2NlbmFyaW9fMSwgZXNjZW5hcmlvXzIsIGVzY2VuYXJpb18zKSAvIHV0aWxpZGFkX3RvdCAqIDEwMCksDQogICAgICAgICAgIGNoZWNrLm5hbWVzID0gRkFMU0UpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDE1LiBCZW5lZmljaW8gZXNwZXJhZG8gZGUgY2FkYSBhY2Npw7NuIChlc2NlbmFyaW9zIHByb3B1ZXN0b3MsIGNvbiBtw6FyZ2VuZXMgY29uc3RhbnRlcykiLCBhbGlnbiA9IGMoImwiLCAibCIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQpMYSBUYWJsYSAxNSBtdWVzdHJhIGPDs21vIHNlIGNhbGN1bMOzIGVsIGJlbmVmaWNpbyBlc3BlcmFkbyBkZSBjYWRhIGFjY2nDs24uIFNvbiBlc2NlbmFyaW9zIHBhcmEgZGltZW5zaW9uYXJsYXMsIG5vIG1ldGFzLCB5IHN1cG9uZW4gcXVlIGxvcyBtw6FyZ2VuZXMgYWN0dWFsZXMgc2UgbWFudGllbmVuLg0KDQojIyBDb25kaWNpb25lcyBkZSBhcGxpY2FjacOzbg0KDQpMYXMgdHJlcyBhY2Npb25lcyBkZWJlbiBlbXBlemFyIGNvbW8gcGlsb3RvcyBjb24gdW4gaW5kaWNhZG9yIGRlZmluaWRvIGFudGVzIGRlIGFycmFuY2FyLiBMYSBBY2Npw7NuIDEgcHVlZGUgY29tZW56YXIgZGUgaW5tZWRpYXRvLiBMYXMgQWNjaW9uZXMgMiB5IDMgZGVwZW5kZW4gZGUgcXVlIGxvcyBkaXN0cmlidWlkb3JlcyBlbnRyZWd1ZW4gZGF0b3MgZGUgY29zdG9zLCBkZXNjdWVudG9zIHkgZmVjaGEgZGUgdmVudGEsIHBvcnF1ZSBzaW4gZWxsb3Mgbm8gc2UgcHVlZGUgc2FiZXIgc2kgZWwgcHJvYmxlbWEgZXMgZGUgcHJlY2lvLCBkZSBjb3N0byBvIGRlbCB0aXBvIGRlIHRpZW5kYS4NCg0KU2kgbG9zIGNvc3RvcyBtdWVzdHJhbiBxdWUgZWwgbWFyZ2VuIGJham8gZGUgV2VzdCBzZSBkZWJlIGEgY29zdG9zIHF1ZSBubyBzZSBwdWVkZW4gcmVkdWNpciAocG9yIGVqZW1wbG8sIGFycmllbmRvcyBvIHRyYW5zcG9ydGUgYSBIYXdhaWkpLCBsYSBBY2Npw7NuIDMgcGFzYXLDrWEgZGUgY29ycmVnaXIgZWwgbWFyZ2VuIGEgYWp1c3RhciBwcmVjaW9zIG8gY29iZXJ0dXJhLiBTaSBsYSBmZWNoYSBtb3N0cmFyYSBxdWUgbG9zIGVzdGFkb3MgRXN0cmVsbGEgdmllbmVuIGNheWVuZG8gZW4gdmVudGFzLCBoYWJyw61hIHF1ZSByZXZpc2FyIGxhIHByaW9yaWRhZCBkZSBsYSBBY2Npw7NuIDEuDQoNCiMjIExpbWl0YWNpb25lcw0KDQotICoqU2luIGZlY2hhOioqIG5vIHB1ZGltb3MgY2FsY3VsYXIgdGFzYXMgZGUgY3JlY2ltaWVudG8gaW50ZXJhbnVhbCBuaSBpZGVudGlmaWNhciBwZXJpb2RvcyBkZSBleHBhbnNpw7NuIG8gY29udHJhY2Npw7NuLiBMYSBiYXNlIHBhcmVjZSB0ZW5lciBkYXRvcyBtZW5zdWFsZXMsIHBlcm8gc2luIGxhIGZlY2hhIGVsIGFuw6FsaXNpcyBzb2xvIHB1ZWRlIHNlciBkZSBjb3J0ZSB0cmFuc3ZlcnNhbC4NCi0gKipTaW4gY29zdG9zIG5pIGRlc2N1ZW50b3M6KiogbGFzIHJhem9uZXMgcXVlIGRhbW9zIHBhcmEgZWwgbWFyZ2VuIGJham8gc29uIGhpcMOzdGVzaXMgcXVlIGhhYnLDrWEgcXVlIGNvbXByb2Jhci4NCi0gKipQZXJpb2RvIHkgbWVyY2FkbzoqKiBsYXMgY29uY2x1c2lvbmVzIGFwbGljYW4gc29sbyBhIEVzdGFkb3MgVW5pZG9zIHkgYWwgcGVyaW9kbyBxdWUgY3VicmUgbGEgYmFzZS4gTG9zIHZhbG9yZXMgZXN0w6FuIGVuIGTDs2xhcmVzLg0KLSAqKkNvcnJlbGFjacOzbiBubyBlcyBjYXVzYWxpZGFkOioqIGxhcyBjb3JyZWxhY2lvbmVzIHkgZWwgbWFyZ2VuIGVzcGVyYWRvIGRlc2NyaWJlbiBsbyBxdWUgcGFzYSwgcGVybyBubyBleHBsaWNhbiBsYSBjYXVzYS4gRWwgbWFyZ2VuIGVzcGVyYWRvIGVzIHVuYSByZWZlcmVuY2lhIHkgbm8gdW5hIG1ldGEuDQotICoqRXNjZW5hcmlvczoqKiBzdXBvbmVuIHF1ZSBsb3MgbcOhcmdlbmVzIHNlIG1hbnRpZW5lbiB5IHNpcnZlbiBzb2xvIGNvbW8gcmVmZXJlbmNpYS4NCg0KIyAqKlNvcG9ydGUgcmVwcm9kdWNpYmxlKioNCg0KLSAqKkZ1ZW50ZSBkZSBsb3MgZGF0b3M6KiogRGF0b3NDYXNvMS54bHN4IChob2phIERhdGEgU2FsZXMgQWRpZGFzKSwgc3VtaW5pc3RyYWRhIHBvciBsYSBkb2NlbnRlIGRlbCBjdXJzbyBBbmFsw610aWNhIGRlIE5lZ29jaW9zLCBFc3BlY2lhbGl6YWNpw7NuIGVuIEZpbmFuemFzLCBQb250aWZpY2lhIFVuaXZlcnNpZGFkIEphdmVyaWFuYSBDYWxpLCAyMDI2Lg0KLSAqKkZ1ZW50ZSBleHRlcm5hOioqIFUuUy4gQnVyZWF1IG9mIEVjb25vbWljIEFuYWx5c2lzICgyMDIyKS4gKlJlYWwgUGVyc29uYWwgQ29uc3VtcHRpb24gRXhwZW5kaXR1cmVzIGJ5IFN0YXRlIGFuZCBSZWFsIFBlcnNvbmFsIEluY29tZSBieSBTdGF0ZSBhbmQgTWV0cm9wb2xpdGFuIEFyZWEsIDIwMjEqIChCRUEgMjItNjIpLCAxNSBkZSBkaWNpZW1icmUgZGUgMjAyMi4gaHR0cHM6Ly93d3cuYmVhLmdvdi9zaXRlcy9kZWZhdWx0L2ZpbGVzLzIwMjItMTIvcnBwMTIyMi5wZGYNCi0gKipDw7NtbyByZXByb2R1Y2lyOioqIGd1YXJkYXIgZWwgYXJjaGl2byBkZSBFeGNlbCBlbiBsYSBtaXNtYSBjYXJwZXRhIGRlIGVzdGUgLlJtZCB5IGVqZWN1dGFyICpLbml0Ki4gRWwgY8OzZGlnbyBkZSBjYWRhIHRhYmxhIHkgZ3LDoWZpY28gc2UgcHVlZGUgdmVyIGNvbiBlbCBib3TDs24gKipTaG93KiogZGUgY2FkYSBibG9xdWUsIHkgZWwgLlJtZCBzZSBwdWVkZSBkZXNjYXJnYXIgY29uIGVsIGJvdMOzbiAqKkNvZGUqKiBkZSBhcnJpYmEuDQoNCkVsIGluZm9ybWUgc2UgZ2VuZXLDsyBjb24gYHIgUi52ZXJzaW9uLnN0cmluZ2AuDQoNCmBgYHtyIHBhcXVldGVzfQ0KcGFxdWV0ZXMgPC0gYygicmVhZHhsIiwgImRwbHlyIiwgInRpZHlyIiwgImdncGxvdDIiLCAicGxvdGx5IiwgImtuaXRyIiwgImthYmxlRXh0cmEiLCAicm1hcmtkb3duIikNCmRhdGEuZnJhbWUoUGFxdWV0ZSA9IHBhcXVldGVzLA0KICAgICAgICAgICBgVmVyc2nDs25gID0gc2FwcGx5KHBhcXVldGVzLCBmdW5jdGlvbihwKSBhcy5jaGFyYWN0ZXIocGFja2FnZVZlcnNpb24ocCkpKSwNCiAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gTlVMTCkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiUGFxdWV0ZXMgdXRpbGl6YWRvcyIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg==