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 quiere saber en qué mercados y con qué método de venta le conviene impulsar sus ventas en Estados Unidos sin perder rentabilidad. Analizamos 9.644 registros que suman US$ 120,2 millones en ventas y US$ 47,2 millones de utilidad operativa, con un margen de 39,3%. Lo principal es que vender más no garantiza ganar más: por cada millón de dólares vendido, los siete estados Estrella dejan US$ 446 mil de utilidad y los once estados grandes de margen bajo (entre ellos California, New York y Florida) dejan US$ 361 mil. West vende el 30,3% del total, pero tiene el menor margen (35,7%), 3,2 puntos por debajo de lo que le correspondería por sus productos y canales, y cinco de los seis distribuidores tienen allí su margen más bajo. Online es el método con mejor margen en 42 de los 50 estados, aunque en los registros de más volumen su margen baja de 49,3% a 41,3%. Recomendamos crecer primero en los estados Estrella y de Oportunidad, hacer una prueba piloto de Online en Northeast y corregir el margen de California, Washington y Hawaii, empezando por Women’s Apparel, 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 es el registro de ventas de un producto, por un distribuidor, en una ciudad y con un método de venta. La base no trae fecha, por lo que no sabemos a qué periodo corresponde. Cubre 50 estados agrupados en 5 regiones, 54 ciudades, 6 distribuidores, 6 productos y 3 métodos de venta (In-store, Online y Outlet). Las cifras están en dólares (US$), porque las ventas son en Estados Unidos. La utilidad que trae la base es la utilidad operativa; no incluye impuestos ni gastos financieros, por lo que en todo el informe “utilidad” significa utilidad operativa.

PASO 1. Cargar librerías

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

# Estilo común para todas las gráficas
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", 
        "numeric", "text", "numeric"))
# 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)

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


# Tabla resumen de la revisión
revision <- data.frame(
  `Revisión` = c("Formato y datos completos",
                 "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"),
  Resultado = c(paste0(ncol(datos), " columnas (", sum(tipos == "character"), " de texto y ", sum(tipos == "numeric"),
                       " numéricas); ", ifelse(faltantes == 0, "ninguna celda vacía", paste(faltantes, "celdas vacías"))),
                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))),
  `Decisión` = c("Todos los registros tienen sus datos completos, así que no hubo que eliminar ni completar valores. El margen viene como proporción y se presenta en porcentaje.",
                 "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: la base no trae fecha ni número de factura, así que no hay forma de saber si son errores o ventas que se repiten. 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."),
  check.names = FALSE)

kable(revision, caption = "Tabla 1. 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")
Tabla 1. Revisión de calidad de los datos
Revisión Resultado Decisión
Formato y datos completos 13 columnas (6 de texto y 7 numéricas); 3 celdas vacías Todos los registros tienen sus datos completos, así que no hubo que eliminar ni completar valores. El margen viene como proporción y se presenta en porcentaje.
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: la base no trae fecha ni número de factura, así que no hay forma de saber si son errores o ventas que se repiten. 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.

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Conclusión de la revisión. La base está completa y sus cifras cuadran. Solo eliminamos los 4 registros sin ventas, así que trabajamos con 9.644 filas (la base original tenía 9.648). Conservamos los duplicados y los atípicos porque no hay evidencia de que sean errores, y analizamos con totales para que ninguna fila pese más de la cuenta.

Análisis exploratorio

Unidades, segmentos, periodo y denominadores

  • Unidades: Ventas y utilidad en dólares (US$), cantidades en unidades vendidas y 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 y no sabemos qué periodo cubre. Todos los resultados son totales acumulados de la base.
  • Denominadores: Cada participación (%) se calcula sobre el total de la base: US$ 120,2 millones en ventas, 2.478.861 unidades o US$ 47,2 millones de utilidad. Cuando el denominador es otro, por ejemplo las ventas de una región, lo indicamos en el texto.

Indicadores que usamos

  • Margen operativo agregado (utilidad total / ventas totales × 100): Mide cuánto queda de cada dólar vendido. Para toda la base da 39,3%. No usamos el promedio simple de los márgenes por registro (42,3%), porque le da el mismo peso a un registro pequeño que a uno grande.
  • Participación (%) en ventas, unidades y utilidad: Muestra qué segmentos concentran el negocio.
  • Mediana, desviación estándar y coeficiente de variación: Las ventas tienen valores muy altos que inflan el promedio; la mediana describe mejor un registro típico y las otras dos miden la dispersión.
  • Utilidad por registro: Permite comparar la utilidad en dólares con el margen en porcentaje.
  • Correlación de Pearson: Mide qué tan relacionadas están dos variables, sin implicar que una cause la otra.
  • Margen esperado por la mezcla de producto y método: Indica si una región gana menos por lo que vende o porque gana menos con lo mismo que venden las demás.

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 2. Indicadores generales", align = c("l", "r")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 2. 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

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Tabla 2. Adidas vendió US$ 120,2 millones (2.478.861 unidades) y obtuvo US$ 47,2 millones de utilidad operativa, un margen de 39,3%. En promedio, cada unidad se vendió a US$ 48,48.

Tendencia central, dispersión y atípicos

Pregunta: ¿Cómo es un registro típico, qué tanto varían las variables y hay valores atípicos?

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

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Tabla 3. Un registro típico (la mediana) tiene 176 unidades, US$ 7.812 en ventas y US$ 3.264 de utilidad. El promedio de ventas (US$ 12.460) es mucho mayor que la mediana y el coeficiente de variación es 1,02: unos pocos registros muy grandes elevan el promedio. Por ejemplo: 799 registros (8,3%) superan US$ 33.578, el límite de atípicos de la Tabla 1, y suman el 30,4% de las ventas. Son ventas reales y comparamos los segmentos con totales. El margen varía mucho menos (coeficiente de 0,23).

Nota sobre los promedios. El precio y el margen aparecen con dos valores porque se calculan de dos formas. En la Tabla 3 cada registro pesa lo mismo: el precio promedio es US$ 45,2 y el margen promedio, 42,3%. En la Tabla 2 se divide un total entre otro (ventas / unidades y utilidad / ventas); así, cada registro pesa según lo que vendió: US$ 48,48 y 39,3%. Los dos cálculos son correctos; para comparar segmentos usamos siempre el segundo.

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

# Gráfica combinada: barras con las ventas y línea con el margen (eje derecho)
plot_ly(resumen_region, x = ~region) %>%
  add_bars(y = ~ventas / 1e6, name = "Ventas (millones de US$)", marker = list(color = "steelblue"),
           hovertext = ~paste0(region, "<br>Ventas: US$ ", round(ventas / 1e6, 1), " M (", pct(part_ventas), ")"),
           hoverinfo = "text") %>%
  add_trace(y = ~margen, name = "Margen (%)", type = "scatter", mode = "lines+markers", yaxis = "y2",
            line = list(color = "grey30"), marker = list(color = "grey30", size = 9),
            hovertext = ~paste0(region, "<br>Margen: ", pct(margen)), hoverinfo = "text") %>%
  layout(title = list(text = "<b>Gráfica 1. Ventas y margen por región</b>", font = list(size = 15)),
         xaxis = list(title = "Región", categoryorder = "array", categoryarray = resumen_region$region),
         yaxis = list(title = "Ventas (millones de US$)"),
         yaxis2 = list(title = "Margen operativo (%)", overlaying = "y", side = "right", range = c(30, 50)),
         legend = list(orientation = "h", y = -0.2),
         margin = list(r = 70))

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Gráfica 1. Las barras son las ventas de cada región (eje izquierdo) y la línea, su margen (eje derecho). West es la región que más vende, US$ 36,4 millones, y la de menor margen (35,7%). South vende US$ 20,6 millones y tiene el mayor margen (44,8%). La región que más vende no es la que más gana por cada dólar.

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

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Tabla 4. Las participaciones suman 100%. West tiene el 30,3% de las ventas, pero solo el 27,6% de la utilidad; South, el 17,1% de las ventas y el 19,5% de la utilidad. 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 5. 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 5. 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%

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Tabla 5. Men’s Street Footwear es el producto que más vende (23,0%) y el de mejor margen (42,0%). Men’s Athletic Footwear es el tercero en ventas y el de menor margen (36,1%). Entre los distribuidores, West Gear y Foot Locker suman algo más de la mitad de las ventas (51,1%), y Sports Direct tiene el mejor margen (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 = "Gráfica 2. Margen por región y método de venta (%)",
         x = "Método de venta", y = "Región", fill = "Margen (%)") +
    tema,
  tooltip = "text")

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Gráfica 2. Cada celda muestra el margen y, entre paréntesis, cuánto pesa ese método en las ventas de la región (los tres pesos de cada región suman 100%). Un azul más oscuro indica mayor margen. Online tiene el mejor margen en las cinco regiones; el más alto es el de Northeast (48,6%), aunque allí Online solo representa el 18,4% de las ventas. El Outlet cambia mucho de una región a otra: 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))

# ¿En cuántos estados es cada método el de mayor margen?
estados_metodo <- 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")

resumen_metodo <- resumen_metodo %>%
  left_join(estados_metodo, by = "metodo_venta") %>%
  mutate(estados = coalesce(estados, 0L))

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),
            `Estados donde tiene el mayor margen` = as.character(estados)) %>%
  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)),
                       `Estados donde tiene el mayor margen` = "50", check.names = FALSE)) %>%
  kable(caption = "Tabla 6. Comparación de los métodos de venta", align = c("l", rep("r", 5))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(4, bold = TRUE)
Tabla 6. Comparación de los métodos de venta
Método % ventas % unidades Margen Utilidad promedio por registro Estados donde tiene el mayor margen
Online 37,4% 37,9% 43,5% US$ 4.001 42
Outlet 32,9% 34,3% 37,7% US$ 4.943 8
In-store 29,7% 27,8% 35,8% US$ 7.333 0
Total 100,0% 100,0% 39,3% US$ 4.897 50

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Tabla 6. 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 lo es Outlet. In-store tiene el margen más bajo (35,8%), pero la mayor utilidad por registro (US$ 7.333), porque sus ventas por registro son más grandes. In-store deja más dólares por registro y Online deja más por cada dólar vendido.

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 = "Gráfica 3. 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))
)

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Gráfica 3. Cada caja resume el margen de los registros de un método en una región; la línea del centro es la mediana y el alto de la caja muestra qué tanto varía. Online tiene la mediana más alta en las cinco regiones (entre 44% y 49%) e In-store la más baja (35%, y 40% en South; en Midwest empata con Outlet). West es la región donde más varía el margen (desviación estándar de 10,7 puntos): tiene ventas muy rentables y otras poco rentables. Northeast es la más estable (8,4 puntos).

Relación entre variables

Pregunta: ¿Cómo se relacionan el precio, las unidades, las ventas, la utilidad y el margen?

nombres <- c(precio_unidad = "Precio", unidades_vendidas = "Unidades", ventas_total = "Ventas",
             utilidad_operativa = "Utilidad", margen_operativo = "Margen")

matriz <- as.data.frame(as.table(cor(datos[names(nombres)]))) %>%
  mutate(Var1 = factor(nombres[as.character(Var1)], levels = nombres),
         Var2 = factor(nombres[as.character(Var2)], levels = rev(nombres)))

ggplotly(
  ggplot(matriz, aes(x = Var1, y = Var2, fill = Freq,
                     text = paste0(Var1, " y ", Var2, ": ", num(Freq, 2)))) +
    geom_tile(color = "white") +
    geom_text(aes(label = num(Freq, 2)), size = 4) +
    scale_fill_gradient2(low = "grey45", mid = "white", high = "steelblue", midpoint = 0, limits = c(-1, 1)) +
    labs(title = "Gráfica 4. Matriz de correlaciones de Pearson", x = NULL, y = NULL, fill = "Correlación") +
    tema,
  tooltip = "text")

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Lectura de la Gráfica 4. Unidades, ventas y utilidad están muy relacionadas (entre 0,87 y 0,94), porque la utilidad sale de las ventas. El margen tiene una relación débil y negativa con las demás variables (entre −0,05 y −0,31): los registros más grandes tienden a tener un margen menor. Precio y unidades se relacionan poco (0,27). En el Hallazgo 4 revisamos si estas relaciones se mantienen al comparar registros parecidos.

La correlación. Es un número entre −1 y 1 que indica si dos variables se mueven juntas. Si es positiva, cuando una sube la otra también tiende a subir; si es negativa, cuando una sube la otra tiende a bajar. Entre más cerca de 1 o de −1, más fuerte es la relación (como guía, menos de 0,3 es débil, de 0,3 a 0,7 es moderada y más de 0,7 es fuerte), y cerca de 0 no hay relación. Una correlación muestra que dos variables se mueven juntas, no que una cause la otra.

Diagnóstico y hallazgos

Presentamos cinco hallazgos y profundizamos en los dos primeros. Debajo de cada tabla o gráfica está la evidencia con su interpretación y, al cierre de cada uno, la implicación para el negocio.

Hallazgo 1. 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 usamos dos preguntas:

  1. ¿Vende mucho o poco? Si las ventas se repartieran por igual, cada estado tendría el 2% (100% / 50), unos US$ 2,4 millones. Por encima del 2% es un mercado grande, como California (7,1%); por debajo, uno pequeño, como Iowa (0,8%).
  2. ¿Gana mucho o poco por cada dólar vendido? La referencia es el margen de toda la empresa, 39,3%. Alabama (54,4%) está por encima y Washington (28,8%), por debajo.

Al cruzar las dos respuestas resultan cuatro grupos: Estrella (vende mucho y gana más que el promedio, como Texas), Oportunidad (vende poco pero gana más, como Michigan), Corregir (vende mucho pero gana menos, como California) y Bajo aporte (vende poco y gana menos, como Iowa y Minnesota). En la Gráfica 5, las líneas punteadas marcan el 2% de las ventas y el margen de 39,3%.

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 la gráfica
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 = "Gráfica 5. Estados: participación en ventas vs. margen",
         x = "% de las ventas totales", y = "Margen operativo agregado (%)", color = "Grupo", size = "") +
    tema,
  tooltip = "text")

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

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

Interpretación de la Gráfica 5. Los estados que más venden aportan más utilidad en dólares, pero su margen no es mayor que el de los estados pequeños.

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

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

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

Interpretación. Cada millón de dólares adicional deja unos US$ 446 mil en un estado Estrella y unos US$ 361 mil en uno a Corregir. 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>Gráfica 6. Margen operativo por estado (%)</b>", font = list(size = 15)),
         geo = list(scope = "usa", projection = list(type = "albers usa"), showlakes = FALSE))

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Evidencia. Un azul más oscuro indica mayor 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 Alaska (32,7%). California, el segundo estado que más vende después de New York, tiene 34,5%.

Interpretación. El margen sigue un patrón geográfico: es alto en el Sur y bajo, sobre todo, en el Pacífico. Una posible explicación son costos de operación más altos en esos estados, pero la base de datos no trae costos para comprobarlo.

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 = "Gráfica 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))

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

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

Interpretación de la Gráfica 7. Las ciudades que más utilidad aportan no son las que más ganan por cada dólar vendido.

Implicación para el negocio. Para crecer conviene dar prioridad a los estados Estrella y de Oportunidad (estos últimos ya ganan 42,6% aunque vendan poco) y no buscar más volumen en los estados a Corregir hasta mejorar su margen.

Hallazgo 2. 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 responderla, a cada registro le asignamos el margen nacional de su mismo producto y método de venta, y con él calculamos la utilidad esperada. Si West ganara menos solo por vender 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

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Evidencia. Por lo que vende, West debería tener un margen de 38,9%, pero tiene 35,7%: 3,2 puntos menos, unos US$ 1,16 millones de utilidad. South, en cambio, está 4,6 puntos por encima de lo esperado.

Interpretación. La mezcla de productos y canales no explica el margen bajo de West: la región gana menos con lo mismo que venden las demás.

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 = "Gráfica 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))

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

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

Interpretación de la Gráfica 8. El faltante se concentra en tres estados 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%

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

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). El segundo producto más débil en West es Men’s Athletic Footwear (31,9%).

Interpretación. El mismo producto deja márgenes muy distintos según la región 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%

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Evidencia. Cinco de los seis distribuidores tienen su menor margen en West; la excepción es Sports Direct, cuyo margen más bajo está en Northeast. Walmart, por ejemplo, gana 41,9% en South y 27,3% en West.

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

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

Implicación para el negocio. Es posible que cambiar de distribuidor no mejore el margen. Se deben revisar los costos y precios en California, Washington y Hawaii, empezando por Women’s Apparel y por West Gear, que tiene el 51,3% de las ventas de West.

Hallazgo 3. El mismo método de venta rinde distinto según el mercado

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%

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

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, por ejemplo en Albany, New York (50,4%). Cinco de las ocho menos rentables están en West, entre ellas el Outlet de Honolulu (25,9%), el In-store de Seattle (26,2%) y el Outlet de San Francisco (30,1%), que además vende US$ 4,07 millones.

Interpretación. El Outlet gana 44,6% en South y 32,8% en West (Gráfica 2): casi 12 puntos de diferencia con el mismo método. Online, en cambio, deja buen margen incluso donde la región gana poco.

Implicación para el negocio. Hay espacio para impulsar Online en Northeast, donde gana 48,6% pero solo pesa el 18,4% de las ventas. La forma en que opera el Outlet en South puede servir de referencia para West.

Hallazgo 4. 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 calculada sobre el total puede reflejar solo cómo están compuestas las ventas. Por eso también la calculamos dentro de cada región, producto, estado y 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

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Evidencia. Cada fila de la Tabla 12 es una relación entre dos variables. La primera columna la mide con todos los datos juntos y las demás comparan solo ventas parecidas (de la misma región, producto o estado).

  • Precio y margen: Con todos los datos es −0,14 (a mayor precio, algo menos de margen). Dentro de un mismo estado baja a 0,00, es decir, la relación desaparece.
  • Unidades y margen: Con todos los datos es −0,31 y al comparar ventas parecidas llega a −0,45. Cuando se venden más unidades, el margen tiende a bajar.
  • Precio y unidades, y unidades y utilidad: Casi no cambian entre columnas (cerca de 0,3 y de 0,86, respectivamente).
  • Entre estados (dato aparte de la tabla): El precio medio y el margen de los 50 estados tienen una correlación de −0,57. Washington y Hawaii venden a US$ 69,1 y US$ 67,7 por unidad y tienen márgenes de 28,8% y 28,5%.

Interpretación. Vender más caro no es lo que baja el margen. Lo que pasa es que los estados donde se vende más caro, como Washington y Hawaii, son los de margen más bajo; por eso la relación aparece cuando se mezclan todos los estados y desaparece dentro de cada uno. Una posible causa es que en esos estados operar cuesta más, pero sin datos de costos no se puede comprobar. La relación alta entre unidades y utilidad es normal, porque la utilidad sale de las ventas.

Pregunta: Dentro de un mismo estado, producto y método, ¿los registros con más unidades dejan mejor o peor margen?

Para comparar casos parecidos, marcamos como de volumen alto los registros con más unidades que 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 = "Gráfica 9. 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))

Fuente: elaboración propia con datos de DatosCaso1.xlsx.

Evidencia. Dentro de un mismo estado, producto y método, los registros Online de mayor volumen ganan 41,3%, frente a 49,3% del resto, aunque su precio por unidad es mayor (US$ 49,0 frente a US$ 45,1). 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 de la Gráfica 9. En un mismo mercado, vender más unidades va acompañado de un margen menor, sobre todo en Online. Como el precio sube, la caída no parece venir de descuentos; podría deberse a los costos de atender pedidos más grandes (envíos, logística), pero es una hipótesis que la base no permite comprobar.

Implicación para el negocio. Si Online crece solo con volumen, es probable que su margen baje; por eso proponemos empezar con una prueba piloto. Estas correlaciones no bastan para fijar precios: se necesitan los costos y la fecha de cada venta.

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

# Región y método que más aparecen entre esas combinaciones
region_top <- names(sort(table(head(combinaciones$region, n_mitad)), decreasing = TRUE))[1]
metodo_top <- names(sort(table(head(combinaciones$metodo_venta, n_mitad)), decreasing = TRUE))[1]

Evidencia. De 690 combinaciones de ciudad, producto y método, 134 (el 19,4%) generan la mitad de la utilidad y el 49,1% de las ventas. No están concentradas en un solo lugar: aparecen en 41 de las 54 ciudades y en las cinco regiones (39 en West, 35 en South, 29 en Southeast, 17 en Northeast y 14 en Midwest). Por método, 53 son Online, 49 Outlet y 32 In-store, y Men’s Street Footwear es el producto que más se repite (42). Su margen (40,1%) es apenas mayor que el del resto de combinaciones (38,5%), pero las 39 de West ganan solo 36,1%.

Interpretación. Estas combinaciones pesan por su tamaño y no por ser mucho más rentables: venden mucho con un margen cercano al promedio. West aporta más combinaciones que cualquier otra región a este grupo, pero con el margen más bajo, así que es donde una mejora de margen tendría más efecto en la utilidad total.

Implicación para el negocio. Hay que proteger estas 134 combinaciones (inventario, disponibilidad y servicio al distribuidor), porque perder cualquiera de ellas afecta la mitad de la utilidad. En West la tarea es doble: mantener su volumen y subir su margen, empezando por las combinaciones de California, Washington y Hawaii señaladas en el Hallazgo 2.

Recomendaciones y límites

Proponemos llevar el crecimiento de las ventas a 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 más urgente es Women’s Apparel en West, sobre todo en California: allí gana 19,6% cuando lo esperado sería 38,9%, y deja de capturar unos US$ 322 mil (US$ 733 mil en toda la región).

Acciones priorizadas

# 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

# Formato en millones de dólares para la tabla de acciones
millones <- function(x) paste0("US$ ", num(x / 1e6, 2), " M")
Tabla 13. Acciones priorizadas y beneficio esperado
Acción Evidencia Beneficio esperado (escenario propuesto) Riesgo Área responsable Indicador (KPI)
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% Supone que el margen se mantiene al crecer, pero en los registros 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 (0,5%) 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), por lo que el beneficio podría ser menor 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 (1,2%) si se cierra la mitad de la brecha de West Aún no se conoce la causa. Como cinco de los seis distribuidores tienen su menor margen 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%)

Fuente: elaboración propia con base en los hallazgos del informe.

Los beneficios son escenarios calculados con los márgenes actuales (el cálculo está en el código de esta sección). Sirven para dimensionar las acciones; no son metas.

Condiciones de aplicación

Las tres acciones deben empezar como pilotos, con su indicador de seguimiento definido desde el inicio. La Acción 1 puede arrancar de inmediato. Las Acciones 2 y 3 requieren que los distribuidores entreguen costos, descuentos y fecha de cada venta; sin esos datos no se puede saber si el margen bajo se debe al precio, al costo o al tipo de tienda.

Dos situaciones cambiarían la recomendación. Si los costos muestran que el margen bajo de West viene de gastos difíciles de reducir (arriendos o transporte a Hawaii, por ejemplo), la Acción 3 debería enfocarse en ajustar precios o cobertura. Y si la fecha muestra que las ventas de los estados Estrella vienen cayendo, habría que revisar la prioridad de la Acción 1.

Limitaciones

  • Sin fecha: No pudimos medir crecimiento ni tendencias, ni identificar periodos de expansión o contracción. El análisis es de corte transversal.
  • Sin costos ni descuentos: Las causas que proponemos para el margen bajo son hipótesis por comprobar.
  • Periodo y mercado: Las conclusiones aplican solo a Estados Unidos y al periodo que cubre la base.
  • Correlación no es causalidad: Las correlaciones y el margen esperado describen lo que ocurre, pero no prueban la causa. El margen esperado es una referencia, no una meta.
  • Escenarios: Suponen márgenes constantes y sirven solo para dimensionar las acciones.

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.
  • Fuentes de las tablas y gráficas: Todas son de elaboración propia con datos de DatosCaso1.xlsx.
  • 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áfica se puede ver con el botón Show de cada bloque, y el .Rmd se puede descargar con el botón Code de arriba.
  • Uso de inteligencia artificial: Utilizamos Claude (Anthropic) como apoyo para organizar el análisis, elaborar las gráficas y tablas en R y revisar la redacción. Las cifras, las interpretaciones y las recomendaciones fueron revisadas por los autores.
paquetes <- c("readxl", "dplyr", "tidyr", "ggplot2", "plotly", "knitr", "kableExtra", "rmarkdown")
versiones <- sapply(paquetes, function(p) as.character(packageVersion(p)))

El informe se generó con R 4.6.1 y los paquetes 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.

Referencias

  • Anthropic. (s. f.). Claude [Modelo de lenguaje de inteligencia artificial]. https://claude.ai
  • Posit Software. (s. f.). RStudio: Integrated development environment for R. https://posit.co
  • R Core Team. (s. f.). R: A language and environment for statistical computing (versión 4.6.1). R Foundation for Statistical Computing. https://www.r-project.org
  • Stefens Cerón, J. (s. f.). Caso 1: Analítica descriptiva y diagnóstica [Instrucciones del caso y base de datos DatosCaso1.xlsx]. Analítica de Negocios, Especialización en Finanzas, Pontificia Universidad Javeriana Cali.
LS0tDQp0aXRsZTogIkNhc28gMSDCtyBBbmFsw610aWNhIGRlc2NyaXB0aXZhIHkgZGlhZ27Ds3N0aWNhOiBkw7NuZGUgZ2FuYSBBZGlkYXMgeSBjw7NtbyBzZWd1aXIgaW1wdWxzYW5kbyBsYXMgdmVudGFzIg0KYXV0aG9yOiAiQ8Opc2FyIEFuZHLDqXMgR2FyY8OtYSBWYXJvbmEgeSBPc3dhbGRvIEZpZXJybyBTYW5jaGV6Ig0KZGF0ZTogIjE0IGRlIG5vdmllbWJyZSBkZSAyMDI2Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBkZl9wcmludDoga2FibGUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgbWRfZXh0ZW5zaW9uczogLXRleF9tYXRoX2RvbGxhcnMNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgcmVzdWx0cyA9ICJzaG93IiwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gOS41LCBmaWcuaGVpZ2h0ID0gNSwgZmlnLmFsaWduID0gImNlbnRlciIpDQpgYGANCg0KKipWaWRlbyBkZSBzdXN0ZW50YWNpw7NuOioqIFtFTkxBQ0UgQUwgVklERU9dDQoNCioqQXNpZ25hdHVyYToqKiBBbmFsw610aWNhIGRlIE5lZ29jaW9zIMK3IEVzcGVjaWFsaXphY2nDs24gZW4gRmluYW56YXMgwrcgUG9udGlmaWNpYSBVbml2ZXJzaWRhZCBKYXZlcmlhbmEgQ2FsaSDCtyBQcm9mZXNvcmEgSnVsaWV0aCBTdGVmZW5zIENlcsOzbg0KDQpgYGB7Y3NzLCBlY2hvPUZBTFNFfQ0KLyogRGlzZcOxbzogY29udGVuaWRvIGNlbnRyYWRvLCB0w610dWxvcyB5IHRhYmxhcyBsZWdpYmxlcyAqLw0KLm1haW4tY29udGFpbmVyIHsgbWF4LXdpZHRoOiAxMTUwcHg7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCmgxIHsgY29sb3I6ICMxZjNiNWE7IGJvcmRlci1ib3R0b206IDJweCBzb2xpZCAjZDllMmVjOyBwYWRkaW5nLWJvdHRvbTogNnB4OyBtYXJnaW4tdG9wOiA0MHB4OyB9DQpoMiB7IGNvbG9yOiAjMmM1MjgyOyBtYXJnaW4tdG9wOiAzMHB4OyB9DQpjYXB0aW9uIHsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiAjMzMzMzMzOyB0ZXh0LWFsaWduOiBjZW50ZXI7IGNhcHRpb24tc2lkZTogdG9wOyB9DQp0YWJsZSB7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCi5odG1sLXdpZGdldCB7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCnAgeyB0ZXh0LWFsaWduOiBqdXN0aWZ5OyB9DQouZnVlbnRlIHsgZm9udC1zaXplOiAxMnB4OyBjb2xvcjogIzY2NjY2NjsgZm9udC1zdHlsZTogaXRhbGljOyB0ZXh0LWFsaWduOiBjZW50ZXI7IG1hcmdpbi10b3A6IC00cHg7IH0NCmBgYA0KDQpgYGB7ciBsb2dvLCBlY2hvPUZBTFNFLCBmaWcuYWxpZ249ImNlbnRlciIsIG91dC53aWR0aD0iMzUlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCI2ODkzNDcuanBnIikNCmBgYA0KDQojICoqUmVzdW1lbiBlamVjdXRpdm8qKg0KDQpBZGlkYXMgcXVpZXJlIHNhYmVyIGVuIHF1w6kgbWVyY2Fkb3MgeSBjb24gcXXDqSBtw6l0b2RvIGRlIHZlbnRhIGxlIGNvbnZpZW5lIGltcHVsc2FyIHN1cyB2ZW50YXMgZW4gRXN0YWRvcyBVbmlkb3Mgc2luIHBlcmRlciByZW50YWJpbGlkYWQuIEFuYWxpemFtb3MgOS42NDQgcmVnaXN0cm9zIHF1ZSBzdW1hbiBVUyQgMTIwLDIgbWlsbG9uZXMgZW4gdmVudGFzIHkgVVMkIDQ3LDIgbWlsbG9uZXMgZGUgdXRpbGlkYWQgb3BlcmF0aXZhLCBjb24gdW4gbWFyZ2VuIGRlIDM5LDMlLiBMbyBwcmluY2lwYWwgZXMgcXVlIHZlbmRlciBtw6FzIG5vIGdhcmFudGl6YSBnYW5hciBtw6FzOiBwb3IgY2FkYSBtaWxsw7NuIGRlIGTDs2xhcmVzIHZlbmRpZG8sIGxvcyBzaWV0ZSBlc3RhZG9zIEVzdHJlbGxhIGRlamFuIFVTJCA0NDYgbWlsIGRlIHV0aWxpZGFkIHkgbG9zIG9uY2UgZXN0YWRvcyBncmFuZGVzIGRlIG1hcmdlbiBiYWpvIChlbnRyZSBlbGxvcyBDYWxpZm9ybmlhLCBOZXcgWW9yayB5IEZsb3JpZGEpIGRlamFuIFVTJCAzNjEgbWlsLiBXZXN0IHZlbmRlIGVsIDMwLDMlIGRlbCB0b3RhbCwgcGVybyB0aWVuZSBlbCBtZW5vciBtYXJnZW4gKDM1LDclKSwgMywyIHB1bnRvcyBwb3IgZGViYWpvIGRlIGxvIHF1ZSBsZSBjb3JyZXNwb25kZXLDrWEgcG9yIHN1cyBwcm9kdWN0b3MgeSBjYW5hbGVzLCB5IGNpbmNvIGRlIGxvcyBzZWlzIGRpc3RyaWJ1aWRvcmVzIHRpZW5lbiBhbGzDrSBzdSBtYXJnZW4gbcOhcyBiYWpvLiBPbmxpbmUgZXMgZWwgbcOpdG9kbyBjb24gbWVqb3IgbWFyZ2VuIGVuIDQyIGRlIGxvcyA1MCBlc3RhZG9zLCBhdW5xdWUgZW4gbG9zIHJlZ2lzdHJvcyBkZSBtw6FzIHZvbHVtZW4gc3UgbWFyZ2VuIGJhamEgZGUgNDksMyUgYSA0MSwzJS4gUmVjb21lbmRhbW9zIGNyZWNlciBwcmltZXJvIGVuIGxvcyBlc3RhZG9zIEVzdHJlbGxhIHkgZGUgT3BvcnR1bmlkYWQsIGhhY2VyIHVuYSBwcnVlYmEgcGlsb3RvIGRlIE9ubGluZSBlbiBOb3J0aGVhc3QgeSBjb3JyZWdpciBlbCBtYXJnZW4gZGUgQ2FsaWZvcm5pYSwgV2FzaGluZ3RvbiB5IEhhd2FpaSwgZW1wZXphbmRvIHBvciBXb21lbidzIEFwcGFyZWwsIGFudGVzIGRlIGJ1c2NhciBtw6FzIHZvbHVtZW4gYWxsw60uIExvcyBiZW5lZmljaW9zIGVzdGltYWRvcyBzb24gZXNjZW5hcmlvcywgbm8gcmVzdWx0YWRvcyBnYXJhbnRpemFkb3MuDQoNCiMgKipEYXRvcyB5IHByZXBhcmFjacOzbioqDQoNCiMjIEFsY2FuY2UgeSB1bmlkYWQgZGUgb2JzZXJ2YWNpw7NuDQoNCkNhZGEgZmlsYSBlcyBlbCByZWdpc3RybyBkZSB2ZW50YXMgZGUgdW4gcHJvZHVjdG8sIHBvciB1biBkaXN0cmlidWlkb3IsIGVuIHVuYSBjaXVkYWQgeSBjb24gdW4gbcOpdG9kbyBkZSB2ZW50YS4gTGEgYmFzZSBubyB0cmFlIGZlY2hhLCBwb3IgbG8gcXVlIG5vIHNhYmVtb3MgYSBxdcOpIHBlcmlvZG8gY29ycmVzcG9uZGUuIEN1YnJlIDUwIGVzdGFkb3MgYWdydXBhZG9zIGVuIDUgcmVnaW9uZXMsIDU0IGNpdWRhZGVzLCA2IGRpc3RyaWJ1aWRvcmVzLCA2IHByb2R1Y3RvcyB5IDMgbcOpdG9kb3MgZGUgdmVudGEgKEluLXN0b3JlLCBPbmxpbmUgeSBPdXRsZXQpLiBMYXMgY2lmcmFzIGVzdMOhbiBlbiBkw7NsYXJlcyAoVVMkKSwgcG9ycXVlIGxhcyB2ZW50YXMgc29uIGVuIEVzdGFkb3MgVW5pZG9zLiBMYSB1dGlsaWRhZCBxdWUgdHJhZSBsYSBiYXNlIGVzIGxhIHV0aWxpZGFkIG9wZXJhdGl2YTsgbm8gaW5jbHV5ZSBpbXB1ZXN0b3MgbmkgZ2FzdG9zIGZpbmFuY2llcm9zLCBwb3IgbG8gcXVlIGVuIHRvZG8gZWwgaW5mb3JtZSAidXRpbGlkYWQiIHNpZ25pZmljYSB1dGlsaWRhZCBvcGVyYXRpdmEuDQoNCioqUEFTTyAxLiBDYXJnYXIgbGlicmVyw61hcyoqDQoNCmBgYHtyIGxpYnJlcmlhc30NCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQoNCiMgRXN0aWxvIGNvbcO6biBwYXJhIHRvZGFzIGxhcyBncsOhZmljYXMNCnRlbWEgPC0gdGhlbWVfbWluaW1hbCgpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpKQ0KYGBgDQoNCioqUEFTTyAyLiBDYXJnYXIgbGEgYmFzZSBkZSBkYXRvcyoqDQoNCmBgYHtyIGNhcmdhfQ0KZGF0b3MgPC0gcmVhZF9leGNlbCgiRGF0b3NDYXNvMS5tb2RpZmljYWRvLnhsc3giLCANCiAgICBjb2xfdHlwZXMgPSBjKCJ0ZXh0IiwgInRleHQiLCAidGV4dCIsIA0KICAgICAgICAidGV4dCIsICJ0ZXh0IiwgIm51bWVyaWMiLCAibnVtZXJpYyIsIA0KICAgICAgICAibnVtZXJpYyIsICJudW1lcmljIiwgIm51bWVyaWMiLCANCiAgICAgICAgIm51bWVyaWMiLCAidGV4dCIsICJudW1lcmljIikpDQpgYGANCg0KYGBge3IgZm9ybWF0b3N9DQojIEZ1bmNpb25lcyBwYXJhIG1vc3RyYXIgZMOzbGFyZXMgeSBwb3JjZW50YWplcyBjb24gY29tYSBkZWNpbWFsIGVuIGxhcyB0YWJsYXMNCnVzZCA8LSBmdW5jdGlvbih4KSBwYXN0ZTAoaWZlbHNlKHggPCAwLCAiLSIsICIiKSwgIlVTJCAiLCBmb3JtYXQocm91bmQoYWJzKHgpKSwgYmlnLm1hcmsgPSAiLiIsIGRlY2ltYWwubWFyayA9ICIsIiwgc2NpZW50aWZpYyA9IEZBTFNFLCB0cmltID0gVFJVRSkpDQpwY3QgPC0gZnVuY3Rpb24oeCkgcGFzdGUwKGZvcm1hdChyb3VuZCh4LCAxKSwgbnNtYWxsID0gMSwgZGVjaW1hbC5tYXJrID0gIiwiLCB0cmltID0gVFJVRSksICIlIikNCm51bSA8LSBmdW5jdGlvbih4LCBkID0gMCkgZm9ybWF0KHJvdW5kKHgsIGQpLCBuc21hbGwgPSBkLCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiLCBzY2llbnRpZmljID0gRkFMU0UsIHRyaW0gPSBUUlVFKQ0KYGBgDQoNCioqUEFTTyAzLiBSZXZpc2FyIGxhIGNhbGlkYWQgZGUgbG9zIGRhdG9zKioNCg0KYGBge3IgY2FsaWRhZH0NCiMgMS4gRm9ybWF0byB5IHZhbG9yZXMgZmFsdGFudGVzDQp0aXBvcyAgICAgIDwtIHNhcHBseShkYXRvcywgY2xhc3MpDQpmYWx0YW50ZXMgIDwtIHN1bShpcy5uYShkYXRvcykpDQoNCiMgMi4gQ29oZXJlbmNpYTogdmVudGFzID0gcHJlY2lvIHggdW5pZGFkZXMgeSB1dGlsaWRhZCA9IHZlbnRhcyB4IG1hcmdlbiAoZGlmZXJlbmNpYXMgbWF5b3JlcyBhIDEgZMOzbGFyKQ0KZXJyb3JfdmVudGFzICAgPC0gc3VtKGFicyhkYXRvcyR2ZW50YXNfdG90YWwgLSBkYXRvcyRwcmVjaW9fdW5pZGFkICogZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMpID4gMSkNCmVycm9yX3V0aWxpZGFkIDwtIHN1bShhYnMoZGF0b3MkdXRpbGlkYWRfb3BlcmF0aXZhIC0gZGF0b3MkdmVudGFzX3RvdGFsICogZGF0b3MkbWFyZ2VuX29wZXJhdGl2bykgPiAxKQ0KDQojIDMuIFJlZ2lzdHJvcyBjb24gdmVudGFzIGVuIGNlcm8NCmNlcm9zIDwtIGRhdG9zICU+JSBmaWx0ZXIodmVudGFzX3RvdGFsID09IDApDQoNCiMgNC4gRmlsYXMgZHVwbGljYWRhcyAobGFzIDExIGNvbHVtbmFzIGlkw6ludGljYXMpIHkgbWFyZ2VuIGFncmVnYWRvIGNvbiB5IHNpbiBlbGxhcw0KZHVwbGljYWRvcyAgICAgPC0gc3VtKGR1cGxpY2F0ZWQoZGF0b3MpKQ0Kc2luX2R1cGxpY2Fkb3MgPC0gZGlzdGluY3QoZGF0b3MpDQptYXJnZW5fY29uICAgICA8LSBzdW0oZGF0b3MkdXRpbGlkYWRfb3BlcmF0aXZhKSAvIHN1bShkYXRvcyR2ZW50YXNfdG90YWwpICogMTAwDQptYXJnZW5fc2luICAgICA8LSBzdW0oc2luX2R1cGxpY2Fkb3MkdXRpbGlkYWRfb3BlcmF0aXZhKSAvIHN1bShzaW5fZHVwbGljYWRvcyR2ZW50YXNfdG90YWwpICogMTAwDQoNCiMgNS4gQ2l1ZGFkZXMgY29uIGVsIG1pc21vIG5vbWJyZSBlbiBlc3RhZG9zIGRpc3RpbnRvcw0KaG9tb25pbWFzIDwtIGRhdG9zICU+JSBkaXN0aW5jdChjaXVkYWQsIGVzdGFkbykgJT4lIGNvdW50KGNpdWRhZCkgJT4lIGZpbHRlcihuID4gMSkNCg0KIyA2LiBWYWxvcmVzIGF0w61waWNvcyBjb24gbGEgcmVnbGEgZGUgVHVrZXkgKHBvciBmdWVyYSBkZSBRMSAtIDEsNSBSSUMgeSBRMyArIDEsNSBSSUMpDQpsaW1pdGVfdmVudGFzIDwtIHF1YW50aWxlKGRhdG9zJHZlbnRhc190b3RhbCwgMC43NSkgKyAxLjUgKiBJUVIoZGF0b3MkdmVudGFzX3RvdGFsKQ0KYXRpcGljb3NfdmVudGFzIDwtIGRhdG9zICU+JSBmaWx0ZXIodmVudGFzX3RvdGFsID4gbGltaXRlX3ZlbnRhcykNCmxpbWl0ZXNfbWFyZ2VuIDwtIHF1YW50aWxlKGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8sIGMoMC4yNSwgMC43NSkpICsgYygtMS41LCAxLjUpICogSVFSKGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8pDQphdGlwaWNvc19tYXJnZW4gPC0gc3VtKGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8gPCBsaW1pdGVzX21hcmdlblsxXSB8IGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8gPiBsaW1pdGVzX21hcmdlblsyXSkNCg0KDQojIFRhYmxhIHJlc3VtZW4gZGUgbGEgcmV2aXNpw7NuDQpyZXZpc2lvbiA8LSBkYXRhLmZyYW1lKA0KICBgUmV2aXNpw7NuYCA9IGMoIkZvcm1hdG8geSBkYXRvcyBjb21wbGV0b3MiLA0KICAgICAgICAgICAgICAgICAiVmVudGFzIHF1ZSBubyBjdWFkcmFuIGNvbiBwcmVjaW8gw5cgdW5pZGFkZXMiLA0KICAgICAgICAgICAgICAgICAiVXRpbGlkYWQgcXVlIG5vIGN1YWRyYSBjb24gdmVudGFzIMOXIG1hcmdlbiIsDQogICAgICAgICAgICAgICAgICJSZWdpc3Ryb3MgY29uIHZlbnRhcyBlbiBjZXJvIiwNCiAgICAgICAgICAgICAgICAgIkZpbGFzIGR1cGxpY2FkYXMgKDExIGNvbHVtbmFzIGlndWFsZXMpIiwNCiAgICAgICAgICAgICAgICAgIkNpdWRhZGVzIGNvbiBlbCBtaXNtbyBub21icmUgZW4gZG9zIGVzdGFkb3MiLA0KICAgICAgICAgICAgICAgICAiQXTDrXBpY29zIGVuIHZlbnRhcyBwb3IgcmVnaXN0cm8iLA0KICAgICAgICAgICAgICAgICAiQXTDrXBpY29zIGVuIG1hcmdlbiBwb3IgcmVnaXN0cm8iKSwNCiAgUmVzdWx0YWRvID0gYyhwYXN0ZTAobmNvbChkYXRvcyksICIgY29sdW1uYXMgKCIsIHN1bSh0aXBvcyA9PSAiY2hhcmFjdGVyIiksICIgZGUgdGV4dG8geSAiLCBzdW0odGlwb3MgPT0gIm51bWVyaWMiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIiBudW3DqXJpY2FzKTsgIiwgaWZlbHNlKGZhbHRhbnRlcyA9PSAwLCAibmluZ3VuYSBjZWxkYSB2YWPDrWEiLCBwYXN0ZShmYWx0YW50ZXMsICJjZWxkYXMgdmFjw61hcyIpKSksDQogICAgICAgICAgICAgICAgZXJyb3JfdmVudGFzLA0KICAgICAgICAgICAgICAgIGVycm9yX3V0aWxpZGFkLA0KICAgICAgICAgICAgICAgIGlmZWxzZShucm93KGNlcm9zKSA+IDAsIG5yb3coY2Vyb3MpLCAiMCBlbiBlbCBhcmNoaXZvIGRlIHRyYWJham8gKDQgZW4gbGEgYmFzZSBvcmlnaW5hbCkiKSwNCiAgICAgICAgICAgICAgICBwYXN0ZTAoZHVwbGljYWRvcywgIiAoIiwgcGN0KGR1cGxpY2Fkb3MgLyBucm93KGRhdG9zKSAqIDEwMCksICIgZGUgbGFzIGZpbGFzKSIpLA0KICAgICAgICAgICAgICAgIHBhc3RlKGhvbW9uaW1hcyRjaXVkYWQsIGNvbGxhcHNlID0gIiB5ICIpLA0KICAgICAgICAgICAgICAgIHBhc3RlMChudW0obnJvdyhhdGlwaWNvc192ZW50YXMpKSwgIiByZWdpc3Ryb3MgKCIsIHBjdChucm93KGF0aXBpY29zX3ZlbnRhcykgLyBucm93KGRhdG9zKSAqIDEwMCksDQogICAgICAgICAgICAgICAgICAgICAgICIpIHBvciBlbmNpbWEgZGUgIiwgdXNkKGxpbWl0ZV92ZW50YXMpLCAiLCBxdWUgc3VtYW4gIiwNCiAgICAgICAgICAgICAgICAgICAgICAgcGN0KHN1bShhdGlwaWNvc192ZW50YXMkdmVudGFzX3RvdGFsKSAvIHN1bShkYXRvcyR2ZW50YXNfdG90YWwpICogMTAwKSwgIiBkZSBsYXMgdmVudGFzIiksDQogICAgICAgICAgICAgICAgcGFzdGUwKGF0aXBpY29zX21hcmdlbiwgIiByZWdpc3Ryb3MgcG9yIGZ1ZXJhIGRlICIsIHBjdChsaW1pdGVzX21hcmdlblsxXSAqIDEwMCksICIgYSAiLCBwY3QobGltaXRlc19tYXJnZW5bMl0gKiAxMDApKSksDQogIGBEZWNpc2nDs25gID0gYygiVG9kb3MgbG9zIHJlZ2lzdHJvcyB0aWVuZW4gc3VzIGRhdG9zIGNvbXBsZXRvcywgYXPDrSBxdWUgbm8gaHVibyBxdWUgZWxpbWluYXIgbmkgY29tcGxldGFyIHZhbG9yZXMuIEVsIG1hcmdlbiB2aWVuZSBjb21vIHByb3BvcmNpw7NuIHkgc2UgcHJlc2VudGEgZW4gcG9yY2VudGFqZS4iLA0KICAgICAgICAgICAgICAgICAiTGFzIGNpZnJhcyBzb24gY29oZXJlbnRlcyB5IHNlIHVzYW4gdGFsIGNvbW8gdmllbmVuLiIsDQogICAgICAgICAgICAgICAgICJMYXMgY2lmcmFzIHNvbiBjb2hlcmVudGVzIHkgc2UgdXNhbiB0YWwgY29tbyB2aWVuZW4uIiwNCiAgICAgICAgICAgICAgICAgIkxhIGJhc2Ugb3JpZ2luYWwgdGVuw61hIDQgcmVnaXN0cm9zIGRlIEZvb3QgTG9ja2VyIGVuIE9tYWhhIChXb21lbidzIEF0aGxldGljIEZvb3R3ZWFyKSBjb24gMCB1bmlkYWRlcyB5IDAgdmVudGFzLiBTZSBlbGltaW5hcm9uIHBvcnF1ZSBubyByZXByZXNlbnRhbiB1bmEgdmVudGEgeSBzdSBtYXJnZW4gbm8gc2UgcHVlZGUgY2FsY3VsYXIuIE5vIGNhbWJpYW4gbGFzIHZlbnRhcywgbGEgdXRpbGlkYWQgbmkgZWwgbWFyZ2VuIGFncmVnYWRvLiIsDQogICAgICAgICAgICAgICAgIHBhc3RlMCgiU2UgY29uc2VydmFuOiBsYSBiYXNlIG5vIHRyYWUgZmVjaGEgbmkgbsO6bWVybyBkZSBmYWN0dXJhLCBhc8OtIHF1ZSBubyBoYXkgZm9ybWEgZGUgc2FiZXIgc2kgc29uIGVycm9yZXMgbyB2ZW50YXMgcXVlIHNlIHJlcGl0ZW4uIFNpbiBlbGxhcyBlbCBtYXJnZW4gcGFzYXLDrWEgZGUgIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBjdChtYXJnZW5fY29uKSwgIiBhICIsIHBjdChtYXJnZW5fc2luKSwgIiB5IG5pbmd1bmEgY29uY2x1c2nDs24gY2FtYmlhLiIpLA0KICAgICAgICAgICAgICAgICAiQ2FkYSBjaXVkYWQgc2UgaWRlbnRpZmljYSBzaWVtcHJlIGp1bnRvIGNvbiBzdSBlc3RhZG8uIiwNCiAgICAgICAgICAgICAgICAgIlNlIGNvbnNlcnZhbjogc29uIHZlbnRhcyByZWFsZXMgeSBjb2hlcmVudGVzIGNvbiBwcmVjaW8gw5cgdW5pZGFkZXMuIFBvciBlc28gZGVzY3JpYmltb3MgZWwgcmVnaXN0cm8gdMOtcGljbyBjb24gbGEgbWVkaWFuYS4iLA0KICAgICAgICAgICAgICAgICAiU2UgY29uc2VydmFuOiBlbCByYW5nbyAoMTAlIGEgODAlKSBlcyBwb3NpYmxlIGVuIHVuYSBvcGVyYWNpw7NuIGNvbWVyY2lhbC4iKSwNCiAgY2hlY2submFtZXMgPSBGQUxTRSkNCg0Ka2FibGUocmV2aXNpb24sIGNhcHRpb24gPSAiVGFibGEgMS4gUmV2aXNpw7NuIGRlIGNhbGlkYWQgZGUgbG9zIGRhdG9zIiwgYWxpZ24gPSBjKCJsIiwgImwiLCAibCIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IFRSVUUpICU+JQ0KICBjb2x1bW5fc3BlYygxLCBib2xkID0gVFJVRSwgd2lkdGggPSAiMTRlbSIpDQpgYGANCg0KPHAgY2xhc3M9ImZ1ZW50ZSI+RnVlbnRlOiBlbGFib3JhY2nDs24gcHJvcGlhIGNvbiBkYXRvcyBkZSBEYXRvc0Nhc28xLnhsc3guPC9wPg0KDQoqKkNvbmNsdXNpw7NuIGRlIGxhIHJldmlzacOzbi4qKiBMYSBiYXNlIGVzdMOhIGNvbXBsZXRhIHkgc3VzIGNpZnJhcyBjdWFkcmFuLiBTb2xvIGVsaW1pbmFtb3MgbG9zIDQgcmVnaXN0cm9zIHNpbiB2ZW50YXMsIGFzw60gcXVlIHRyYWJhamFtb3MgY29uIDkuNjQ0IGZpbGFzIChsYSBiYXNlIG9yaWdpbmFsIHRlbsOtYSA5LjY0OCkuIENvbnNlcnZhbW9zIGxvcyBkdXBsaWNhZG9zIHkgbG9zIGF0w61waWNvcyBwb3JxdWUgbm8gaGF5IGV2aWRlbmNpYSBkZSBxdWUgc2VhbiBlcnJvcmVzLCB5IGFuYWxpemFtb3MgY29uIHRvdGFsZXMgcGFyYSBxdWUgbmluZ3VuYSBmaWxhIHBlc2UgbcOhcyBkZSBsYSBjdWVudGEuDQoNCiMgKipBbsOhbGlzaXMgZXhwbG9yYXRvcmlvKioNCg0KIyMgVW5pZGFkZXMsIHNlZ21lbnRvcywgcGVyaW9kbyB5IGRlbm9taW5hZG9yZXMNCg0KLSAqKlVuaWRhZGVzOioqIFZlbnRhcyB5IHV0aWxpZGFkIGVuIGTDs2xhcmVzIChVUyQpLCBjYW50aWRhZGVzIGVuIHVuaWRhZGVzIHZlbmRpZGFzIHkgbWFyZ2VuIGVuIHBvcmNlbnRhamUuDQotICoqU2VnbWVudG9zOioqIDUgcmVnaW9uZXMsIDUwIGVzdGFkb3MsIDU0IGNpdWRhZGVzLCA2IGRpc3RyaWJ1aWRvcmVzLCA2IHByb2R1Y3RvcyB5IDMgbcOpdG9kb3MgZGUgdmVudGEgKEluLXN0b3JlLCBPbmxpbmUgeSBPdXRsZXQpLg0KLSAqKlBlcmlvZG86KiogTGEgYmFzZSBubyB0cmFlIGZlY2hhIHkgbm8gc2FiZW1vcyBxdcOpIHBlcmlvZG8gY3VicmUuIFRvZG9zIGxvcyByZXN1bHRhZG9zIHNvbiB0b3RhbGVzIGFjdW11bGFkb3MgZGUgbGEgYmFzZS4NCi0gKipEZW5vbWluYWRvcmVzOioqIENhZGEgcGFydGljaXBhY2nDs24gKCUpIHNlIGNhbGN1bGEgc29icmUgZWwgdG90YWwgZGUgbGEgYmFzZTogVVMkIDEyMCwyIG1pbGxvbmVzIGVuIHZlbnRhcywgMi40NzguODYxIHVuaWRhZGVzIG8gVVMkIDQ3LDIgbWlsbG9uZXMgZGUgdXRpbGlkYWQuIEN1YW5kbyBlbCBkZW5vbWluYWRvciBlcyBvdHJvLCBwb3IgZWplbXBsbyBsYXMgdmVudGFzIGRlIHVuYSByZWdpw7NuLCBsbyBpbmRpY2Ftb3MgZW4gZWwgdGV4dG8uDQoNCioqSW5kaWNhZG9yZXMgcXVlIHVzYW1vcyoqDQoNCi0gKipNYXJnZW4gb3BlcmF0aXZvIGFncmVnYWRvKiogKHV0aWxpZGFkIHRvdGFsIC8gdmVudGFzIHRvdGFsZXMgw5cgMTAwKTogTWlkZSBjdcOhbnRvIHF1ZWRhIGRlIGNhZGEgZMOzbGFyIHZlbmRpZG8uIFBhcmEgdG9kYSBsYSBiYXNlIGRhIDM5LDMlLiBObyB1c2Ftb3MgZWwgcHJvbWVkaW8gc2ltcGxlIGRlIGxvcyBtw6FyZ2VuZXMgcG9yIHJlZ2lzdHJvICg0MiwzJSksIHBvcnF1ZSBsZSBkYSBlbCBtaXNtbyBwZXNvIGEgdW4gcmVnaXN0cm8gcGVxdWXDsW8gcXVlIGEgdW5vIGdyYW5kZS4NCi0gKipQYXJ0aWNpcGFjacOzbiAoJSkgZW4gdmVudGFzLCB1bmlkYWRlcyB5IHV0aWxpZGFkOioqIE11ZXN0cmEgcXXDqSBzZWdtZW50b3MgY29uY2VudHJhbiBlbCBuZWdvY2lvLg0KLSAqKk1lZGlhbmEsIGRlc3ZpYWNpw7NuIGVzdMOhbmRhciB5IGNvZWZpY2llbnRlIGRlIHZhcmlhY2nDs246KiogTGFzIHZlbnRhcyB0aWVuZW4gdmFsb3JlcyBtdXkgYWx0b3MgcXVlIGluZmxhbiBlbCBwcm9tZWRpbzsgbGEgbWVkaWFuYSBkZXNjcmliZSBtZWpvciB1biByZWdpc3RybyB0w61waWNvIHkgbGFzIG90cmFzIGRvcyBtaWRlbiBsYSBkaXNwZXJzacOzbi4NCi0gKipVdGlsaWRhZCBwb3IgcmVnaXN0cm86KiogUGVybWl0ZSBjb21wYXJhciBsYSB1dGlsaWRhZCBlbiBkw7NsYXJlcyBjb24gZWwgbWFyZ2VuIGVuIHBvcmNlbnRhamUuDQotICoqQ29ycmVsYWNpw7NuIGRlIFBlYXJzb246KiogTWlkZSBxdcOpIHRhbiByZWxhY2lvbmFkYXMgZXN0w6FuIGRvcyB2YXJpYWJsZXMsIHNpbiBpbXBsaWNhciBxdWUgdW5hIGNhdXNlIGxhIG90cmEuDQotICoqTWFyZ2VuIGVzcGVyYWRvIHBvciBsYSBtZXpjbGEgZGUgcHJvZHVjdG8geSBtw6l0b2RvOioqIEluZGljYSBzaSB1bmEgcmVnacOzbiBnYW5hIG1lbm9zIHBvciBsbyBxdWUgdmVuZGUgbyBwb3JxdWUgZ2FuYSBtZW5vcyBjb24gbG8gbWlzbW8gcXVlIHZlbmRlbiBsYXMgZGVtw6FzLg0KDQojIyBJbmRpY2Fkb3JlcyBnZW5lcmFsZXMNCg0KYGBge3IgaW5kaWNhZG9yZXN9DQp2ZW50YXNfdG90ICAgIDwtIHN1bShkYXRvcyR2ZW50YXNfdG90YWwpDQp1dGlsaWRhZF90b3QgIDwtIHN1bShkYXRvcyR1dGlsaWRhZF9vcGVyYXRpdmEpDQp1bmlkYWRlc190b3QgIDwtIHN1bShkYXRvcyR1bmlkYWRlc192ZW5kaWRhcykNCm1hcmdlbl90b3QgICAgPC0gdXRpbGlkYWRfdG90IC8gdmVudGFzX3RvdCAqIDEwMA0KbWFyZ2VuX3NpbXBsZSA8LSBtZWFuKGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8pICogMTAwDQoNCnRhYmxhX2twaSA8LSBkYXRhLmZyYW1lKA0KICBJbmRpY2Fkb3IgPSBjKCJSZWdpc3Ryb3MiLCAiVmVudGFzIHRvdGFsZXMiLCAiVW5pZGFkZXMgdmVuZGlkYXMiLCAiVXRpbGlkYWQgb3BlcmF0aXZhIHRvdGFsIiwNCiAgICAgICAgICAgICAgICAiTWFyZ2VuIG9wZXJhdGl2byBhZ3JlZ2FkbyAodXRpbGlkYWQgLyB2ZW50YXMpIiwgIlByb21lZGlvIHNpbXBsZSBkZSBsb3MgbcOhcmdlbmVzIChubyBzZSB1c2EpIiwNCiAgICAgICAgICAgICAgICAiUHJlY2lvIHByb21lZGlvIHBvciB1bmlkYWQgKHZlbnRhcyAvIHVuaWRhZGVzKSIpLA0KICBWYWxvciA9IGMobnVtKG5yb3coZGF0b3MpKSwgdXNkKHZlbnRhc190b3QpLCBudW0odW5pZGFkZXNfdG90KSwgdXNkKHV0aWxpZGFkX3RvdCksDQogICAgICAgICAgICBwY3QobWFyZ2VuX3RvdCksIHBjdChtYXJnZW5fc2ltcGxlKSwgcGFzdGUwKCJVUyQgIiwgbnVtKHZlbnRhc190b3QgLyB1bmlkYWRlc190b3QsIDIpKSkpDQoNCmthYmxlKHRhYmxhX2twaSwgY2FwdGlvbiA9ICJUYWJsYSAyLiBJbmRpY2Fkb3JlcyBnZW5lcmFsZXMiLCBhbGlnbiA9IGMoImwiLCAiciIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKQ0KYGBgDQoNCjxwIGNsYXNzPSJmdWVudGUiPkZ1ZW50ZTogZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgRGF0b3NDYXNvMS54bHN4LjwvcD4NCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDIuKiogQWRpZGFzIHZlbmRpw7MgVVMkIDEyMCwyIG1pbGxvbmVzICgyLjQ3OC44NjEgdW5pZGFkZXMpIHkgb2J0dXZvIFVTJCA0NywyIG1pbGxvbmVzIGRlIHV0aWxpZGFkIG9wZXJhdGl2YSwgdW4gbWFyZ2VuIGRlIDM5LDMlLiBFbiBwcm9tZWRpbywgY2FkYSB1bmlkYWQgc2UgdmVuZGnDsyBhIFVTJCA0OCw0OC4NCg0KIyMgVGVuZGVuY2lhIGNlbnRyYWwsIGRpc3BlcnNpw7NuIHkgYXTDrXBpY29zDQoNCipQcmVndW50YTogwr9Dw7NtbyBlcyB1biByZWdpc3RybyB0w61waWNvLCBxdcOpIHRhbnRvIHZhcsOtYW4gbGFzIHZhcmlhYmxlcyB5IGhheSB2YWxvcmVzIGF0w61waWNvcz8qDQoNCmBgYHtyIGRlc2NyaXB0aXZhc30NCnZhcmlhYmxlcyA8LSBjKCJwcmVjaW9fdW5pZGFkIiwgInVuaWRhZGVzX3ZlbmRpZGFzIiwgInZlbnRhc190b3RhbCIsICJ1dGlsaWRhZF9vcGVyYXRpdmEiLCAibWFyZ2VuX29wZXJhdGl2byIpDQoNCmRlc2NyaXB0aXZhcyA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZSA9IGMoIlByZWNpbyBwb3IgdW5pZGFkIChVUyQpIiwgIlVuaWRhZGVzIHZlbmRpZGFzIiwgIlZlbnRhcyBwb3IgcmVnaXN0cm8gKFVTJCkiLA0KICAgICAgICAgICAgICAgIlV0aWxpZGFkIHBvciByZWdpc3RybyAoVVMkKSIsICJNYXJnZW4gcG9yIHJlZ2lzdHJvICglKSIpLA0KICBNZWRpYSAgICA9IHNhcHBseShkYXRvc1t2YXJpYWJsZXNdLCBtZWFuKSwNCiAgTWVkaWFuYSAgPSBzYXBwbHkoZGF0b3NbdmFyaWFibGVzXSwgbWVkaWFuKSwNCiAgYERlc3YuIGVzdMOhbmRhcmAgPSBzYXBwbHkoZGF0b3NbdmFyaWFibGVzXSwgc2QpLA0KICBgTcOtbmltb2AgPSBzYXBwbHkoZGF0b3NbdmFyaWFibGVzXSwgbWluKSwNCiAgYE3DoXhpbW9gID0gc2FwcGx5KGRhdG9zW3ZhcmlhYmxlc10sIG1heCksDQogIGNoZWNrLm5hbWVzID0gRkFMU0UsIHJvdy5uYW1lcyA9IE5VTEwpDQoNCiMgRWwgbWFyZ2VuIHNlIG11ZXN0cmEgZW4gcG9yY2VudGFqZQ0KZGVzY3JpcHRpdmFzWzUsIDI6Nl0gPC0gZGVzY3JpcHRpdmFzWzUsIDI6Nl0gKiAxMDANCmRlc2NyaXB0aXZhcyRgQ29lZi4gZGUgdmFyaWFjacOzbmAgPC0gZGVzY3JpcHRpdmFzJGBEZXN2LiBlc3TDoW5kYXJgIC8gZGVzY3JpcHRpdmFzJE1lZGlhDQoNCmRlc2NyaXB0aXZhcyAlPiUNCiAgbXV0YXRlKGFjcm9zcyhNZWRpYTpgTcOheGltb2AsIH4gbnVtKC54LCAxKSksIGBDb2VmLiBkZSB2YXJpYWNpw7NuYCA9IG51bShgQ29lZi4gZGUgdmFyaWFjacOzbmAsIDIpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAzLiBNZWRpZGFzIGRlIHRlbmRlbmNpYSBjZW50cmFsIHkgZGlzcGVyc2nDs24gcG9yIHJlZ2lzdHJvIiwgYWxpZ24gPSBjKCJsIiwgcmVwKCJyIiwgNikpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKQ0KYGBgDQoNCjxwIGNsYXNzPSJmdWVudGUiPkZ1ZW50ZTogZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgRGF0b3NDYXNvMS54bHN4LjwvcD4NCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDMuKiogVW4gcmVnaXN0cm8gdMOtcGljbyAobGEgbWVkaWFuYSkgdGllbmUgMTc2IHVuaWRhZGVzLCBVUyQgNy44MTIgZW4gdmVudGFzIHkgVVMkIDMuMjY0IGRlIHV0aWxpZGFkLiBFbCBwcm9tZWRpbyBkZSB2ZW50YXMgKFVTJCAxMi40NjApIGVzIG11Y2hvIG1heW9yIHF1ZSBsYSBtZWRpYW5hIHkgZWwgY29lZmljaWVudGUgZGUgdmFyaWFjacOzbiBlcyAxLDAyOiB1bm9zIHBvY29zIHJlZ2lzdHJvcyBtdXkgZ3JhbmRlcyBlbGV2YW4gZWwgcHJvbWVkaW8uIFBvciBlamVtcGxvOiA3OTkgcmVnaXN0cm9zICg4LDMlKSBzdXBlcmFuIFVTJCAzMy41NzgsIGVsIGzDrW1pdGUgZGUgYXTDrXBpY29zIGRlIGxhIFRhYmxhIDEsIHkgc3VtYW4gZWwgMzAsNCUgZGUgbGFzIHZlbnRhcy4gU29uIHZlbnRhcyByZWFsZXMgeSBjb21wYXJhbW9zIGxvcyBzZWdtZW50b3MgY29uIHRvdGFsZXMuIEVsIG1hcmdlbiB2YXLDrWEgbXVjaG8gbWVub3MgKGNvZWZpY2llbnRlIGRlIDAsMjMpLg0KDQoqKk5vdGEgc29icmUgbG9zIHByb21lZGlvcy4qKiBFbCBwcmVjaW8geSBlbCBtYXJnZW4gYXBhcmVjZW4gY29uIGRvcyB2YWxvcmVzIHBvcnF1ZSBzZSBjYWxjdWxhbiBkZSBkb3MgZm9ybWFzLiBFbiBsYSBUYWJsYSAzIGNhZGEgcmVnaXN0cm8gcGVzYSBsbyBtaXNtbzogZWwgcHJlY2lvIHByb21lZGlvIGVzIFVTJCA0NSwyIHkgZWwgbWFyZ2VuIHByb21lZGlvLCA0MiwzJS4gRW4gbGEgVGFibGEgMiBzZSBkaXZpZGUgdW4gdG90YWwgZW50cmUgb3RybyAodmVudGFzIC8gdW5pZGFkZXMgeSB1dGlsaWRhZCAvIHZlbnRhcyk7IGFzw60sIGNhZGEgcmVnaXN0cm8gcGVzYSBzZWfDum4gbG8gcXVlIHZlbmRpw7M6IFVTJCA0OCw0OCB5IDM5LDMlLiBMb3MgZG9zIGPDoWxjdWxvcyBzb24gY29ycmVjdG9zOyBwYXJhIGNvbXBhcmFyIHNlZ21lbnRvcyB1c2Ftb3Mgc2llbXByZSBlbCBzZWd1bmRvLg0KDQojIyBQYXJ0aWNpcGFjacOzbiBwb3Igc2VnbWVudG9zDQoNCipQcmVndW50YTogwr9RdcOpIHNlZ21lbnRvcyBjb25jZW50cmFuIGxhcyB2ZW50YXMsIGxhcyB1bmlkYWRlcyB5IGxhIHV0aWxpZGFkPyoNCg0KYGBge3IgcmVnaW9uZXN9DQpyZXN1bWVuX3JlZ2lvbiA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLA0KICAgICAgICAgICAgdW5pZGFkZXMgPSBzdW0odW5pZGFkZXNfdmVuZGlkYXMpLA0KICAgICAgICAgICAgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSkgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIHBhcnRfdmVudGFzID0gdmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDAsDQogICAgICAgICBwYXJ0X3VuaWRhZGVzID0gdW5pZGFkZXMgLyBzdW0odW5pZGFkZXMpICogMTAwLA0KICAgICAgICAgcGFydF91dGlsaWRhZCA9IHV0aWxpZGFkIC8gc3VtKHV0aWxpZGFkKSAqIDEwMCkgJT4lDQogIGFycmFuZ2UoZGVzYyh2ZW50YXMpKQ0KDQojIEdyw6FmaWNhIGNvbWJpbmFkYTogYmFycmFzIGNvbiBsYXMgdmVudGFzIHkgbMOtbmVhIGNvbiBlbCBtYXJnZW4gKGVqZSBkZXJlY2hvKQ0KcGxvdF9seShyZXN1bWVuX3JlZ2lvbiwgeCA9IH5yZWdpb24pICU+JQ0KICBhZGRfYmFycyh5ID0gfnZlbnRhcyAvIDFlNiwgbmFtZSA9ICJWZW50YXMgKG1pbGxvbmVzIGRlIFVTJCkiLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gInN0ZWVsYmx1ZSIpLA0KICAgICAgICAgICBob3ZlcnRleHQgPSB+cGFzdGUwKHJlZ2lvbiwgIjxicj5WZW50YXM6IFVTJCAiLCByb3VuZCh2ZW50YXMgLyAxZTYsIDEpLCAiIE0gKCIsIHBjdChwYXJ0X3ZlbnRhcyksICIpIiksDQogICAgICAgICAgIGhvdmVyaW5mbyA9ICJ0ZXh0IikgJT4lDQogIGFkZF90cmFjZSh5ID0gfm1hcmdlbiwgbmFtZSA9ICJNYXJnZW4gKCUpIiwgdHlwZSA9ICJzY2F0dGVyIiwgbW9kZSA9ICJsaW5lcyttYXJrZXJzIiwgeWF4aXMgPSAieTIiLA0KICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiZ3JleTMwIiksIG1hcmtlciA9IGxpc3QoY29sb3IgPSAiZ3JleTMwIiwgc2l6ZSA9IDkpLA0KICAgICAgICAgICAgaG92ZXJ0ZXh0ID0gfnBhc3RlMChyZWdpb24sICI8YnI+TWFyZ2VuOiAiLCBwY3QobWFyZ2VuKSksIGhvdmVyaW5mbyA9ICJ0ZXh0IikgJT4lDQogIGxheW91dCh0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj5HcsOhZmljYSAxLiBWZW50YXMgeSBtYXJnZW4gcG9yIHJlZ2nDs248L2I+IiwgZm9udCA9IGxpc3Qoc2l6ZSA9IDE1KSksDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiUmVnacOzbiIsIGNhdGVnb3J5b3JkZXIgPSAiYXJyYXkiLCBjYXRlZ29yeWFycmF5ID0gcmVzdW1lbl9yZWdpb24kcmVnaW9uKSwNCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJWZW50YXMgKG1pbGxvbmVzIGRlIFVTJCkiKSwNCiAgICAgICAgIHlheGlzMiA9IGxpc3QodGl0bGUgPSAiTWFyZ2VuIG9wZXJhdGl2byAoJSkiLCBvdmVybGF5aW5nID0gInkiLCBzaWRlID0gInJpZ2h0IiwgcmFuZ2UgPSBjKDMwLCA1MCkpLA0KICAgICAgICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICJoIiwgeSA9IC0wLjIpLA0KICAgICAgICAgbWFyZ2luID0gbGlzdChyID0gNzApKQ0KYGBgDQoNCjxwIGNsYXNzPSJmdWVudGUiPkZ1ZW50ZTogZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgRGF0b3NDYXNvMS54bHN4LjwvcD4NCg0KKipMZWN0dXJhIGRlIGxhIEdyw6FmaWNhIDEuKiogTGFzIGJhcnJhcyBzb24gbGFzIHZlbnRhcyBkZSBjYWRhIHJlZ2nDs24gKGVqZSBpenF1aWVyZG8pIHkgbGEgbMOtbmVhLCBzdSBtYXJnZW4gKGVqZSBkZXJlY2hvKS4gV2VzdCBlcyBsYSByZWdpw7NuIHF1ZSBtw6FzIHZlbmRlLCBVUyQgMzYsNCBtaWxsb25lcywgeSBsYSBkZSBtZW5vciBtYXJnZW4gKDM1LDclKS4gU291dGggdmVuZGUgVVMkIDIwLDYgbWlsbG9uZXMgeSB0aWVuZSBlbCBtYXlvciBtYXJnZW4gKDQ0LDglKS4gTGEgcmVnacOzbiBxdWUgbcOhcyB2ZW5kZSBubyBlcyBsYSBxdWUgbcOhcyBnYW5hIHBvciBjYWRhIGTDs2xhci4NCg0KYGBge3IgdGFibGFfcmVnaW9ufQ0KcmVzdW1lbl9yZWdpb24gJT4lDQogIHRyYW5zbXV0ZShgUmVnacOzbmAgPSByZWdpb24sIFZlbnRhcyA9IHVzZCh2ZW50YXMpLCBgJSB2ZW50YXNgID0gcGN0KHBhcnRfdmVudGFzKSwNCiAgICAgICAgICAgIGAlIHVuaWRhZGVzYCA9IHBjdChwYXJ0X3VuaWRhZGVzKSwgYCUgdXRpbGlkYWRgID0gcGN0KHBhcnRfdXRpbGlkYWQpLCBNYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIGJpbmRfcm93cyhkYXRhLmZyYW1lKGBSZWdpw7NuYCA9ICJUb3RhbCIsIFZlbnRhcyA9IHVzZCh2ZW50YXNfdG90KSwNCiAgICAgICAgICAgICAgICAgICAgICAgYCUgdmVudGFzYCA9IHBjdChzdW0ocmVzdW1lbl9yZWdpb24kcGFydF92ZW50YXMpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgYCUgdW5pZGFkZXNgID0gcGN0KHN1bShyZXN1bWVuX3JlZ2lvbiRwYXJ0X3VuaWRhZGVzKSksDQogICAgICAgICAgICAgICAgICAgICAgIGAlIHV0aWxpZGFkYCA9IHBjdChzdW0ocmVzdW1lbl9yZWdpb24kcGFydF91dGlsaWRhZCkpLA0KICAgICAgICAgICAgICAgICAgICAgICBNYXJnZW4gPSBwY3QobWFyZ2VuX3RvdCksIGNoZWNrLm5hbWVzID0gRkFMU0UpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSA0LiBWZW50YXMsIHVuaWRhZGVzLCB1dGlsaWRhZCB5IG1hcmdlbiBwb3IgcmVnacOzbiIsIGFsaWduID0gYygibCIsIHJlcCgiciIsIDUpKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lDQogIHJvd19zcGVjKDYsIGJvbGQgPSBUUlVFKQ0KYGBgDQoNCjxwIGNsYXNzPSJmdWVudGUiPkZ1ZW50ZTogZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgRGF0b3NDYXNvMS54bHN4LjwvcD4NCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDQuKiogTGFzIHBhcnRpY2lwYWNpb25lcyBzdW1hbiAxMDAlLiBXZXN0IHRpZW5lIGVsIDMwLDMlIGRlIGxhcyB2ZW50YXMsIHBlcm8gc29sbyBlbCAyNyw2JSBkZSBsYSB1dGlsaWRhZDsgU291dGgsIGVsIDE3LDElIGRlIGxhcyB2ZW50YXMgeSBlbCAxOSw1JSBkZSBsYSB1dGlsaWRhZC4gU291dGggcGVzYSBtw6FzIGVuIHVuaWRhZGVzICgxOSw5JSkgcXVlIGVuIHZlbnRhcyAoMTcsMSUpIHBvcnF1ZSB2ZW5kZSBhIHByZWNpb3MgbcOhcyBiYWpvcy4NCg0KYGBge3IgcHJvZHVjdG9fZGlzdHJpYnVpZG9yfQ0KcGFydGljaXBhY2lvbiA8LSBmdW5jdGlvbihiYXNlLCBjb2x1bW5hLCBub21icmUpIHsNCiAgdGFibGEgPC0gYmFzZSAlPiUNCiAgICBncm91cF9ieShjYXRlZ29yaWEgPSAuZGF0YVtbY29sdW1uYV1dKSAlPiUNCiAgICBzdW1tYXJpc2UodmVudGFzID0gc3VtKHZlbnRhc190b3RhbCksIHVuaWRhZGVzID0gc3VtKHVuaWRhZGVzX3ZlbmRpZGFzKSwgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSkgJT4lDQogICAgYXJyYW5nZShkZXNjKHZlbnRhcykpDQogIHRvdGFsIDwtIGRhdGEuZnJhbWUoY2F0ZWdvcmlhID0gIlRvdGFsIiwgdmVudGFzID0gc3VtKHRhYmxhJHZlbnRhcyksDQogICAgICAgICAgICAgICAgICAgICAgdW5pZGFkZXMgPSBzdW0odGFibGEkdW5pZGFkZXMpLCB1dGlsaWRhZCA9IHN1bSh0YWJsYSR1dGlsaWRhZCkpDQogIGJpbmRfcm93cyh0YWJsYSwgdG90YWwpICU+JQ0KICAgIG11dGF0ZShEaW1lbnNpw7NuID0gbm9tYnJlLA0KICAgICAgICAgICBgJSB2ZW50YXNgID0gcGN0KHZlbnRhcyAvIHN1bSh0YWJsYSR2ZW50YXMpICogMTAwKSwNCiAgICAgICAgICAgYCUgdW5pZGFkZXNgID0gcGN0KHVuaWRhZGVzIC8gc3VtKHRhYmxhJHVuaWRhZGVzKSAqIDEwMCksDQogICAgICAgICAgIGAlIHV0aWxpZGFkYCA9IHBjdCh1dGlsaWRhZCAvIHN1bSh0YWJsYSR1dGlsaWRhZCkgKiAxMDApLA0KICAgICAgICAgICBNYXJnZW4gPSBwY3QodXRpbGlkYWQgLyB2ZW50YXMgKiAxMDApKQ0KfQ0KDQpiaW5kX3Jvd3MocGFydGljaXBhY2lvbihkYXRvcywgInByb2R1Y3RvIiwgIlByb2R1Y3RvIiksDQogICAgICAgICAgcGFydGljaXBhY2lvbihkYXRvcywgImRpc3RyaWJ1aWRvciIsICJEaXN0cmlidWlkb3IiKSkgJT4lDQogIHNlbGVjdChEaW1lbnNpw7NuLCBDYXRlZ29yw61hID0gY2F0ZWdvcmlhLCBgJSB2ZW50YXNgLCBgJSB1bmlkYWRlc2AsIGAlIHV0aWxpZGFkYCwgTWFyZ2VuKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSA1LiBQYXJ0aWNpcGFjacOzbiBwb3IgcHJvZHVjdG8geSBwb3IgZGlzdHJpYnVpZG9yIiwgYWxpZ24gPSBjKCJsIiwgImwiLCByZXAoInIiLCA0KSkpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpICU+JQ0KICByb3dfc3BlYyhjKDcsIDE0KSwgYm9sZCA9IFRSVUUpICU+JQ0KICBjb2xsYXBzZV9yb3dzKGNvbHVtbnMgPSAxLCB2YWxpZ24gPSAidG9wIikNCmBgYA0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIERhdG9zQ2FzbzEueGxzeC48L3A+DQoNCioqTGVjdHVyYSBkZSBsYSBUYWJsYSA1LioqIE1lbidzIFN0cmVldCBGb290d2VhciBlcyBlbCBwcm9kdWN0byBxdWUgbcOhcyB2ZW5kZSAoMjMsMCUpIHkgZWwgZGUgbWVqb3IgbWFyZ2VuICg0MiwwJSkuIE1lbidzIEF0aGxldGljIEZvb3R3ZWFyIGVzIGVsIHRlcmNlcm8gZW4gdmVudGFzIHkgZWwgZGUgbWVub3IgbWFyZ2VuICgzNiwxJSkuIEVudHJlIGxvcyBkaXN0cmlidWlkb3JlcywgV2VzdCBHZWFyIHkgRm9vdCBMb2NrZXIgc3VtYW4gYWxnbyBtw6FzIGRlIGxhIG1pdGFkIGRlIGxhcyB2ZW50YXMgKDUxLDElKSwgeSBTcG9ydHMgRGlyZWN0IHRpZW5lIGVsIG1lam9yIG1hcmdlbiAoNDMsMiUpLg0KDQojIyBNw6l0b2RvcyBkZSB2ZW50YSB5IHZhcmlhYmlsaWRhZA0KDQoqUHJlZ3VudGE6IMK/UXXDqSBtw6l0b2RvIGRlIHZlbnRhIGRlamEgbWVqb3IgbWFyZ2VuLCBlcyBpZ3VhbCBlbiB0b2RhcyBsYXMgcmVnaW9uZXMgeSBkw7NuZGUgdmFyw61hIG3DoXMgZWwgbWFyZ2VuPyoNCg0KYGBge3IgcmVnaW9uX21ldG9kb30NCnJlZ2lvbl9tZXRvZG8gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbiwgbWV0b2RvX3ZlbnRhKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLA0KICAgICAgICAgICAgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIG1lemNsYSA9IHZlbnRhcyAvIHN1bSh2ZW50YXMpICogMTAwKSAlPiUNCiAgdW5ncm91cCgpDQoNCmdncGxvdGx5KA0KICBnZ3Bsb3QocmVnaW9uX21ldG9kbywgYWVzKHggPSBtZXRvZG9fdmVudGEsIHkgPSByZWdpb24sIGZpbGwgPSBtYXJnZW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMChyZWdpb24sICIgwrcgIiwgbWV0b2RvX3ZlbnRhLCAiXG5NYXJnZW46ICIsIHBjdChtYXJnZW4pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuUGVzbyBlbiBsYXMgdmVudGFzIGRlIGxhIHJlZ2nDs246ICIsIHBjdChtZXpjbGEpKSkpICsNCiAgICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiKSArDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwY3QobWFyZ2VuKSwgIlxuKCIsIHBjdChtZXpjbGEpLCAiIGRlIHZlbnRhcykiKSksIHNpemUgPSAzKSArDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiI2YyZjJmMiIsIGhpZ2ggPSAic3RlZWxibHVlIikgKw0KICAgIGxhYnModGl0bGUgPSAiR3LDoWZpY2EgMi4gTWFyZ2VuIHBvciByZWdpw7NuIHkgbcOpdG9kbyBkZSB2ZW50YSAoJSkiLA0KICAgICAgICAgeCA9ICJNw6l0b2RvIGRlIHZlbnRhIiwgeSA9ICJSZWdpw7NuIiwgZmlsbCA9ICJNYXJnZW4gKCUpIikgKw0KICAgIHRlbWEsDQogIHRvb2x0aXAgPSAidGV4dCIpDQpgYGANCg0KPHAgY2xhc3M9ImZ1ZW50ZSI+RnVlbnRlOiBlbGFib3JhY2nDs24gcHJvcGlhIGNvbiBkYXRvcyBkZSBEYXRvc0Nhc28xLnhsc3guPC9wPg0KDQoqKkxlY3R1cmEgZGUgbGEgR3LDoWZpY2EgMi4qKiBDYWRhIGNlbGRhIG11ZXN0cmEgZWwgbWFyZ2VuIHksIGVudHJlIHBhcsOpbnRlc2lzLCBjdcOhbnRvIHBlc2EgZXNlIG3DqXRvZG8gZW4gbGFzIHZlbnRhcyBkZSBsYSByZWdpw7NuIChsb3MgdHJlcyBwZXNvcyBkZSBjYWRhIHJlZ2nDs24gc3VtYW4gMTAwJSkuIFVuIGF6dWwgbcOhcyBvc2N1cm8gaW5kaWNhIG1heW9yIG1hcmdlbi4gT25saW5lIHRpZW5lIGVsIG1lam9yIG1hcmdlbiBlbiBsYXMgY2luY28gcmVnaW9uZXM7IGVsIG3DoXMgYWx0byBlcyBlbCBkZSBOb3J0aGVhc3QgKDQ4LDYlKSwgYXVucXVlIGFsbMOtIE9ubGluZSBzb2xvIHJlcHJlc2VudGEgZWwgMTgsNCUgZGUgbGFzIHZlbnRhcy4gRWwgT3V0bGV0IGNhbWJpYSBtdWNobyBkZSB1bmEgcmVnacOzbiBhIG90cmE6IDQ0LDYlIGVuIFNvdXRoIHkgMzIsOCUgZW4gV2VzdC4NCg0KYGBge3IgdGFibGFfbWV0b2RvfQ0KcmVzdW1lbl9tZXRvZG8gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KG1ldG9kb192ZW50YSkgJT4lDQogIHN1bW1hcmlzZShyZWdpc3Ryb3MgPSBuKCksDQogICAgICAgICAgICB2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHVuaWRhZGVzID0gc3VtKHVuaWRhZGVzX3ZlbmRpZGFzKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwYXJ0X3ZlbnRhcyA9IHZlbnRhcyAvIHN1bSh2ZW50YXMpICogMTAwLA0KICAgICAgICAgcGFydF91bmlkYWRlcyA9IHVuaWRhZGVzIC8gc3VtKHVuaWRhZGVzKSAqIDEwMCwNCiAgICAgICAgIHV0aWxpZGFkX3JlZ2lzdHJvID0gdXRpbGlkYWQgLyByZWdpc3Ryb3MpICU+JQ0KICBhcnJhbmdlKGRlc2MobWFyZ2VuKSkNCg0KIyDCv0VuIGN1w6FudG9zIGVzdGFkb3MgZXMgY2FkYSBtw6l0b2RvIGVsIGRlIG1heW9yIG1hcmdlbj8NCmVzdGFkb3NfbWV0b2RvIDwtIGRhdG9zICU+JQ0KICBncm91cF9ieShlc3RhZG8sIG1ldG9kb192ZW50YSkgJT4lDQogIHN1bW1hcmlzZShtYXJnZW4gPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSAvIHN1bSh2ZW50YXNfdG90YWwpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgZ3JvdXBfYnkoZXN0YWRvKSAlPiUNCiAgc2xpY2VfbWF4KG1hcmdlbiwgbiA9IDEpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGNvdW50KG1ldG9kb192ZW50YSwgbmFtZSA9ICJlc3RhZG9zIikNCg0KcmVzdW1lbl9tZXRvZG8gPC0gcmVzdW1lbl9tZXRvZG8gJT4lDQogIGxlZnRfam9pbihlc3RhZG9zX21ldG9kbywgYnkgPSAibWV0b2RvX3ZlbnRhIikgJT4lDQogIG11dGF0ZShlc3RhZG9zID0gY29hbGVzY2UoZXN0YWRvcywgMEwpKQ0KDQpyZXN1bWVuX21ldG9kbyAlPiUNCiAgdHJhbnNtdXRlKGBNw6l0b2RvYCA9IG1ldG9kb192ZW50YSwgYCUgdmVudGFzYCA9IHBjdChwYXJ0X3ZlbnRhcyksIGAlIHVuaWRhZGVzYCA9IHBjdChwYXJ0X3VuaWRhZGVzKSwNCiAgICAgICAgICAgIE1hcmdlbiA9IHBjdChtYXJnZW4pLCBgVXRpbGlkYWQgcHJvbWVkaW8gcG9yIHJlZ2lzdHJvYCA9IHVzZCh1dGlsaWRhZF9yZWdpc3RybyksDQogICAgICAgICAgICBgRXN0YWRvcyBkb25kZSB0aWVuZSBlbCBtYXlvciBtYXJnZW5gID0gYXMuY2hhcmFjdGVyKGVzdGFkb3MpKSAlPiUNCiAgYmluZF9yb3dzKGRhdGEuZnJhbWUoYE3DqXRvZG9gID0gIlRvdGFsIiwgYCUgdmVudGFzYCA9IHBjdChzdW0ocmVzdW1lbl9tZXRvZG8kcGFydF92ZW50YXMpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgYCUgdW5pZGFkZXNgID0gcGN0KHN1bShyZXN1bWVuX21ldG9kbyRwYXJ0X3VuaWRhZGVzKSksIE1hcmdlbiA9IHBjdChtYXJnZW5fdG90KSwNCiAgICAgICAgICAgICAgICAgICAgICAgYFV0aWxpZGFkIHByb21lZGlvIHBvciByZWdpc3Ryb2AgPSB1c2QodXRpbGlkYWRfdG90IC8gbnJvdyhkYXRvcykpLA0KICAgICAgICAgICAgICAgICAgICAgICBgRXN0YWRvcyBkb25kZSB0aWVuZSBlbCBtYXlvciBtYXJnZW5gID0gIjUwIiwgY2hlY2submFtZXMgPSBGQUxTRSkpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDYuIENvbXBhcmFjacOzbiBkZSBsb3MgbcOpdG9kb3MgZGUgdmVudGEiLCBhbGlnbiA9IGMoImwiLCByZXAoInIiLCA1KSkpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpICU+JQ0KICByb3dfc3BlYyg0LCBib2xkID0gVFJVRSkNCmBgYA0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIERhdG9zQ2FzbzEueGxzeC48L3A+DQoNCioqTGVjdHVyYSBkZSBsYSBUYWJsYSA2LioqIE9ubGluZSB0aWVuZSBlbCBtZWpvciBtYXJnZW4gKDQzLDUlKSB5IGVzIGVsIG3DqXRvZG8gbcOhcyByZW50YWJsZSBlbiA0MiBkZSBsb3MgNTAgZXN0YWRvczsgZW4gbG9zIG90cm9zIDggbG8gZXMgT3V0bGV0LiBJbi1zdG9yZSB0aWVuZSBlbCBtYXJnZW4gbcOhcyBiYWpvICgzNSw4JSksIHBlcm8gbGEgbWF5b3IgdXRpbGlkYWQgcG9yIHJlZ2lzdHJvIChVUyQgNy4zMzMpLCBwb3JxdWUgc3VzIHZlbnRhcyBwb3IgcmVnaXN0cm8gc29uIG3DoXMgZ3JhbmRlcy4gSW4tc3RvcmUgZGVqYSBtw6FzIGTDs2xhcmVzIHBvciByZWdpc3RybyB5IE9ubGluZSBkZWphIG3DoXMgcG9yIGNhZGEgZMOzbGFyIHZlbmRpZG8uDQoNCg0KYGBge3IgYm94cGxvdF9tZXRvZG99DQpnZ3Bsb3RseSgNCiAgZ2dwbG90KGRhdG9zLCBhZXMoeCA9IG1ldG9kb192ZW50YSwgeSA9IG1hcmdlbl9vcGVyYXRpdm8gKiAxMDApKSArDQogICAgZ2VvbV9ib3hwbG90KGZpbGwgPSAibGlnaHRzdGVlbGJsdWUiLCBvdXRsaWVyLmNvbG91ciA9ICJncmV5NDAiLCBvdXRsaWVyLnNpemUgPSAxKSArDQogICAgZmFjZXRfd3JhcCh+cmVnaW9uLCBucm93ID0gMSkgKw0KICAgIGxhYnModGl0bGUgPSAiR3LDoWZpY2EgMy4gTWFyZ2VuIHBvciByZWdpc3RybyBzZWfDum4gbcOpdG9kbyB5IHJlZ2nDs24iLA0KICAgICAgICAgeCA9IE5VTEwsIHkgPSAiTWFyZ2VuIG9wZXJhdGl2byAoJSkiKSArDQogICAgdGVtYSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KKQ0KYGBgDQoNCjxwIGNsYXNzPSJmdWVudGUiPkZ1ZW50ZTogZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgRGF0b3NDYXNvMS54bHN4LjwvcD4NCg0KKipMZWN0dXJhIGRlIGxhIEdyw6FmaWNhIDMuKiogQ2FkYSBjYWphIHJlc3VtZSBlbCBtYXJnZW4gZGUgbG9zIHJlZ2lzdHJvcyBkZSB1biBtw6l0b2RvIGVuIHVuYSByZWdpw7NuOyBsYSBsw61uZWEgZGVsIGNlbnRybyBlcyBsYSBtZWRpYW5hIHkgZWwgYWx0byBkZSBsYSBjYWphIG11ZXN0cmEgcXXDqSB0YW50byB2YXLDrWEuIE9ubGluZSB0aWVuZSBsYSBtZWRpYW5hIG3DoXMgYWx0YSBlbiBsYXMgY2luY28gcmVnaW9uZXMgKGVudHJlIDQ0JSB5IDQ5JSkgZSBJbi1zdG9yZSBsYSBtw6FzIGJhamEgKDM1JSwgeSA0MCUgZW4gU291dGg7IGVuIE1pZHdlc3QgZW1wYXRhIGNvbiBPdXRsZXQpLiBXZXN0IGVzIGxhIHJlZ2nDs24gZG9uZGUgbcOhcyB2YXLDrWEgZWwgbWFyZ2VuIChkZXN2aWFjacOzbiBlc3TDoW5kYXIgZGUgMTAsNyBwdW50b3MpOiB0aWVuZSB2ZW50YXMgbXV5IHJlbnRhYmxlcyB5IG90cmFzIHBvY28gcmVudGFibGVzLiBOb3J0aGVhc3QgZXMgbGEgbcOhcyBlc3RhYmxlICg4LDQgcHVudG9zKS4NCg0KIyMgUmVsYWNpw7NuIGVudHJlIHZhcmlhYmxlcw0KDQoqUHJlZ3VudGE6IMK/Q8OzbW8gc2UgcmVsYWNpb25hbiBlbCBwcmVjaW8sIGxhcyB1bmlkYWRlcywgbGFzIHZlbnRhcywgbGEgdXRpbGlkYWQgeSBlbCBtYXJnZW4/Kg0KDQpgYGB7ciBtYXRyaXpfY29ycmVsYWNpb24sIGZpZy53aWR0aD03LjUsIGZpZy5oZWlnaHQ9NS41fQ0Kbm9tYnJlcyA8LSBjKHByZWNpb191bmlkYWQgPSAiUHJlY2lvIiwgdW5pZGFkZXNfdmVuZGlkYXMgPSAiVW5pZGFkZXMiLCB2ZW50YXNfdG90YWwgPSAiVmVudGFzIiwNCiAgICAgICAgICAgICB1dGlsaWRhZF9vcGVyYXRpdmEgPSAiVXRpbGlkYWQiLCBtYXJnZW5fb3BlcmF0aXZvID0gIk1hcmdlbiIpDQoNCm1hdHJpeiA8LSBhcy5kYXRhLmZyYW1lKGFzLnRhYmxlKGNvcihkYXRvc1tuYW1lcyhub21icmVzKV0pKSkgJT4lDQogIG11dGF0ZShWYXIxID0gZmFjdG9yKG5vbWJyZXNbYXMuY2hhcmFjdGVyKFZhcjEpXSwgbGV2ZWxzID0gbm9tYnJlcyksDQogICAgICAgICBWYXIyID0gZmFjdG9yKG5vbWJyZXNbYXMuY2hhcmFjdGVyKFZhcjIpXSwgbGV2ZWxzID0gcmV2KG5vbWJyZXMpKSkNCg0KZ2dwbG90bHkoDQogIGdncGxvdChtYXRyaXosIGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIsIGZpbGwgPSBGcmVxLA0KICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMChWYXIxLCAiIHkgIiwgVmFyMiwgIjogIiwgbnVtKEZyZXEsIDIpKSkpICsNCiAgICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiKSArDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG51bShGcmVxLCAyKSksIHNpemUgPSA0KSArDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gImdyZXk0NSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAic3RlZWxibHVlIiwgbWlkcG9pbnQgPSAwLCBsaW1pdHMgPSBjKC0xLCAxKSkgKw0KICAgIGxhYnModGl0bGUgPSAiR3LDoWZpY2EgNC4gTWF0cml6IGRlIGNvcnJlbGFjaW9uZXMgZGUgUGVhcnNvbiIsIHggPSBOVUxMLCB5ID0gTlVMTCwgZmlsbCA9ICJDb3JyZWxhY2nDs24iKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikNCmBgYA0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIERhdG9zQ2FzbzEueGxzeC48L3A+DQoNCioqTGVjdHVyYSBkZSBsYSBHcsOhZmljYSA0LioqIFVuaWRhZGVzLCB2ZW50YXMgeSB1dGlsaWRhZCBlc3TDoW4gbXV5IHJlbGFjaW9uYWRhcyAoZW50cmUgMCw4NyB5IDAsOTQpLCBwb3JxdWUgbGEgdXRpbGlkYWQgc2FsZSBkZSBsYXMgdmVudGFzLiBFbCBtYXJnZW4gdGllbmUgdW5hIHJlbGFjacOzbiBkw6liaWwgeSBuZWdhdGl2YSBjb24gbGFzIGRlbcOhcyB2YXJpYWJsZXMgKGVudHJlIOKIkjAsMDUgeSDiiJIwLDMxKTogbG9zIHJlZ2lzdHJvcyBtw6FzIGdyYW5kZXMgdGllbmRlbiBhIHRlbmVyIHVuIG1hcmdlbiBtZW5vci4gUHJlY2lvIHkgdW5pZGFkZXMgc2UgcmVsYWNpb25hbiBwb2NvICgwLDI3KS4gRW4gZWwgSGFsbGF6Z28gNCByZXZpc2Ftb3Mgc2kgZXN0YXMgcmVsYWNpb25lcyBzZSBtYW50aWVuZW4gYWwgY29tcGFyYXIgcmVnaXN0cm9zIHBhcmVjaWRvcy4NCg0KKipMYSBjb3JyZWxhY2nDs24uKiogRXMgdW4gbsO6bWVybyBlbnRyZSDiiJIxIHkgMSBxdWUgaW5kaWNhIHNpIGRvcyB2YXJpYWJsZXMgc2UgbXVldmVuIGp1bnRhcy4gU2kgZXMgcG9zaXRpdmEsIGN1YW5kbyB1bmEgc3ViZSBsYSBvdHJhIHRhbWJpw6luIHRpZW5kZSBhIHN1YmlyOyBzaSBlcyBuZWdhdGl2YSwgY3VhbmRvIHVuYSBzdWJlIGxhIG90cmEgdGllbmRlIGEgYmFqYXIuIEVudHJlIG3DoXMgY2VyY2EgZGUgMSBvIGRlIOKIkjEsIG3DoXMgZnVlcnRlIGVzIGxhIHJlbGFjacOzbiAoY29tbyBndcOtYSwgbWVub3MgZGUgMCwzIGVzIGTDqWJpbCwgZGUgMCwzIGEgMCw3IGVzIG1vZGVyYWRhIHkgbcOhcyBkZSAwLDcgZXMgZnVlcnRlKSwgeSBjZXJjYSBkZSAwIG5vIGhheSByZWxhY2nDs24uIFVuYSBjb3JyZWxhY2nDs24gbXVlc3RyYSBxdWUgZG9zIHZhcmlhYmxlcyBzZSBtdWV2ZW4ganVudGFzLCBubyBxdWUgdW5hIGNhdXNlIGxhIG90cmEuDQoNCiMgKipEaWFnbsOzc3RpY28geSBoYWxsYXpnb3MqKg0KDQpQcmVzZW50YW1vcyBjaW5jbyBoYWxsYXpnb3MgeSBwcm9mdW5kaXphbW9zIGVuIGxvcyBkb3MgcHJpbWVyb3MuIERlYmFqbyBkZSBjYWRhIHRhYmxhIG8gZ3LDoWZpY2EgZXN0w6EgbGEgZXZpZGVuY2lhIGNvbiBzdSBpbnRlcnByZXRhY2nDs24geSwgYWwgY2llcnJlIGRlIGNhZGEgdW5vLCBsYSBpbXBsaWNhY2nDs24gcGFyYSBlbCBuZWdvY2lvLg0KDQojIyBIYWxsYXpnbyAxLiBWZW5kZXIgbcOhcyBubyBzaWVtcHJlIGVzIGdhbmFyIG3DoXMNCg0KKlByZWd1bnRhOiDCv1F1w6kgZXN0YWRvcyB5IGNpdWRhZGVzIGNvbWJpbmFuIG11Y2hhcyB2ZW50YXMgY29uIGJ1ZW4gbWFyZ2VuLCB5IGN1w6FsZXMgdmVuZGVuIG11Y2hvIHBlcm8gZ2FuYW4gcG9jbz8qDQoNClBhcmEgY2xhc2lmaWNhciBsb3MgNTAgZXN0YWRvcyB1c2Ftb3MgZG9zIHByZWd1bnRhczoNCg0KMS4gKirCv1ZlbmRlIG11Y2hvIG8gcG9jbz8qKiBTaSBsYXMgdmVudGFzIHNlIHJlcGFydGllcmFuIHBvciBpZ3VhbCwgY2FkYSBlc3RhZG8gdGVuZHLDrWEgZWwgMiUgKDEwMCUgLyA1MCksIHVub3MgVVMkIDIsNCBtaWxsb25lcy4gUG9yIGVuY2ltYSBkZWwgMiUgZXMgdW4gbWVyY2FkbyBncmFuZGUsIGNvbW8gQ2FsaWZvcm5pYSAoNywxJSk7IHBvciBkZWJham8sIHVubyBwZXF1ZcOxbywgY29tbyBJb3dhICgwLDglKS4NCjIuICoqwr9HYW5hIG11Y2hvIG8gcG9jbyBwb3IgY2FkYSBkw7NsYXIgdmVuZGlkbz8qKiBMYSByZWZlcmVuY2lhIGVzIGVsIG1hcmdlbiBkZSB0b2RhIGxhIGVtcHJlc2EsIDM5LDMlLiBBbGFiYW1hICg1NCw0JSkgZXN0w6EgcG9yIGVuY2ltYSB5IFdhc2hpbmd0b24gKDI4LDglKSwgcG9yIGRlYmFqby4NCg0KQWwgY3J1emFyIGxhcyBkb3MgcmVzcHVlc3RhcyByZXN1bHRhbiBjdWF0cm8gZ3J1cG9zOiAqKkVzdHJlbGxhKiogKHZlbmRlIG11Y2hvIHkgZ2FuYSBtw6FzIHF1ZSBlbCBwcm9tZWRpbywgY29tbyBUZXhhcyksICoqT3BvcnR1bmlkYWQqKiAodmVuZGUgcG9jbyBwZXJvIGdhbmEgbcOhcywgY29tbyBNaWNoaWdhbiksICoqQ29ycmVnaXIqKiAodmVuZGUgbXVjaG8gcGVybyBnYW5hIG1lbm9zLCBjb21vIENhbGlmb3JuaWEpIHkgKipCYWpvIGFwb3J0ZSoqICh2ZW5kZSBwb2NvIHkgZ2FuYSBtZW5vcywgY29tbyBJb3dhIHkgTWlubmVzb3RhKS4gRW4gbGEgR3LDoWZpY2EgNSwgbGFzIGzDrW5lYXMgcHVudGVhZGFzIG1hcmNhbiBlbCAyJSBkZSBsYXMgdmVudGFzIHkgZWwgbWFyZ2VuIGRlIDM5LDMlLg0KDQoNCmBgYHtyIGVzdGFkb3MsIGZpZy5oZWlnaHQ9N30NCnJlc3VtZW5fZXN0YWRvIDwtIGRhdG9zICU+JQ0KICBncm91cF9ieShyZWdpb24sIGVzdGFkbykgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksDQogICAgICAgICAgICB1bmlkYWRlcyA9IHN1bSh1bmlkYWRlc192ZW5kaWRhcyksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwYXJ0X3ZlbnRhcyA9IHZlbnRhcyAvIHN1bSh2ZW50YXMpICogMTAwLA0KICAgICAgICAgcHJlY2lvX21lZGlvID0gdmVudGFzIC8gdW5pZGFkZXMsDQogICAgICAgICBncnVwbyA9IGNhc2Vfd2hlbihwYXJ0X3ZlbnRhcyA+PSAyICYgbWFyZ2VuID49IG1hcmdlbl90b3QgfiAiRXN0cmVsbGEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFydF92ZW50YXMgPCAgMiAmIG1hcmdlbiA+PSBtYXJnZW5fdG90IH4gIk9wb3J0dW5pZGFkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcnRfdmVudGFzID49IDIgJiBtYXJnZW4gPCAgbWFyZ2VuX3RvdCB+ICJDb3JyZWdpciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIkJham8gYXBvcnRlIiksDQogICAgICAgICBncnVwbyA9IGZhY3RvcihncnVwbywgbGV2ZWxzID0gYygiRXN0cmVsbGEiLCAiT3BvcnR1bmlkYWQiLCAiQ29ycmVnaXIiLCAiQmFqbyBhcG9ydGUiKSkpDQoNCmNvbG9yZXNfZ3J1cG8gPC0gYygiRXN0cmVsbGEiID0gIm5hdnkiLCAiT3BvcnR1bmlkYWQiID0gImRlZXBza3libHVlMyIsDQogICAgICAgICAgICAgICAgICAgIkNvcnJlZ2lyIiA9ICJncmV5MzAiLCAiQmFqbyBhcG9ydGUiID0gImdyZXk3MCIpDQoNCiMgRXN0YWRvcyBxdWUgc2Ugbm9tYnJhbiBlbiBsYSBncsOhZmljYQ0KZXRpcXVldGFzIDwtIGMoIkNhbGlmb3JuaWEiLCAiTmV3IFlvcmsiLCAiRmxvcmlkYSIsICJUZXhhcyIsICJBbGFiYW1hIiwgIlRlbm5lc3NlZSIsDQogICAgICAgICAgICAgICAiV2FzaGluZ3RvbiIsICJNaWNoaWdhbiIsICJJZGFobyIsICJMb3Vpc2lhbmEiKQ0KDQpnZ3Bsb3RseSgNCiAgZ2dwbG90KHJlc3VtZW5fZXN0YWRvLCBhZXMoeCA9IHBhcnRfdmVudGFzLCB5ID0gbWFyZ2VuLCBjb2xvciA9IGdydXBvLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKGVzdGFkbywgIiAoIiwgcmVnaW9uLCAiKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuVmVudGFzOiBVUyQgIiwgcm91bmQodmVudGFzIC8gMWU2LCAyKSwgIiBNICgiLCBwY3QocGFydF92ZW50YXMpLCAiIGRlbCB0b3RhbCkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcblV0aWxpZGFkOiBVUyQgIiwgcm91bmQodXRpbGlkYWQgLyAxZTYsIDIpLCAiIE0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcbk1hcmdlbjogIiwgcGN0KG1hcmdlbikpKSkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1hcmdlbl90b3QsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXk0MCIpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAyLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5NDAiKSArDQogICAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IHV0aWxpZGFkIC8gMWU2KSwgYWxwaGEgPSAwLjc1KSArDQogICAgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIocmVzdW1lbl9lc3RhZG8sIGVzdGFkbyAlaW4lIGV0aXF1ZXRhcyksIGFlcyhsYWJlbCA9IGVzdGFkbyksDQogICAgICAgICAgICAgIG51ZGdlX3kgPSAxLjMsIHNpemUgPSAzLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICAgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIocmVzdW1lbl9lc3RhZG8sIGVzdGFkbyA9PSAiSGF3YWlpIiksIGFlcyhsYWJlbCA9IGVzdGFkbyksDQogICAgICAgICAgICAgIG51ZGdlX3kgPSAtMS4zLCBzaXplID0gMywgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcmVzX2dydXBvKSArDQogICAgbGFicyh0aXRsZSA9ICJHcsOhZmljYSA1LiBFc3RhZG9zOiBwYXJ0aWNpcGFjacOzbiBlbiB2ZW50YXMgdnMuIG1hcmdlbiIsDQogICAgICAgICB4ID0gIiUgZGUgbGFzIHZlbnRhcyB0b3RhbGVzIiwgeSA9ICJNYXJnZW4gb3BlcmF0aXZvIGFncmVnYWRvICglKSIsIGNvbG9yID0gIkdydXBvIiwgc2l6ZSA9ICIiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikNCmBgYA0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIERhdG9zQ2FzbzEueGxzeC48L3A+DQoNCioqRXZpZGVuY2lhLioqIENhZGEgcHVudG8gZXMgdW4gZXN0YWRvIHkgc3UgdGFtYcOxbyBpbmRpY2EgbGEgdXRpbGlkYWQgcXVlIGFwb3J0YS4gRW50cmUgZXN0YWRvcywgbGEgY29ycmVsYWNpw7NuIGVudHJlIHZlbnRhcyB5IHV0aWxpZGFkIGVzIGRlIGByIG51bShjb3IocmVzdW1lbl9lc3RhZG8kdmVudGFzLCByZXN1bWVuX2VzdGFkbyR1dGlsaWRhZCksIDIpYCB5IGVudHJlIHZlbnRhcyB5IG1hcmdlbiwgZGUgYHIgc3ViKCItIiwgIuKIkiIsIG51bShjb3IocmVzdW1lbl9lc3RhZG8kdmVudGFzLCByZXN1bWVuX2VzdGFkbyRtYXJnZW4pLCAyKSlgLiBUZXhhcyB2ZW5kZSBlbCA1LDUlIGRlbCB0b3RhbCBjb24gNDIsOCUgZGUgbWFyZ2VuOyBDYWxpZm9ybmlhIHZlbmRlIGVsIDcsMSUgY29uIDM0LDUlLg0KDQoqKkludGVycHJldGFjacOzbiBkZSBsYSBHcsOhZmljYSA1LioqIExvcyBlc3RhZG9zIHF1ZSBtw6FzIHZlbmRlbiBhcG9ydGFuIG3DoXMgdXRpbGlkYWQgZW4gZMOzbGFyZXMsIHBlcm8gc3UgbWFyZ2VuIG5vIGVzIG1heW9yIHF1ZSBlbCBkZSBsb3MgZXN0YWRvcyBwZXF1ZcOxb3MuDQoNCmBgYHtyIHRhYmxhX2dydXBvc30NCnJlc3VtZW5fZ3J1cG8gPC0gcmVzdW1lbl9lc3RhZG8gJT4lDQogIGdyb3VwX2J5KGdydXBvKSAlPiUNCiAgc3VtbWFyaXNlKGVzdGFkb3MgPSBuKCksDQogICAgICAgICAgICB2ZW50YXMgPSBzdW0odmVudGFzKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkKSwNCiAgICAgICAgICAgIGxpc3RhID0gcGFzdGUoZXN0YWRvW29yZGVyKC1wYXJ0X3ZlbnRhcyldLCBjb2xsYXBzZSA9ICIsICIpKSAlPiUNCiAgbXV0YXRlKG1hcmdlbiA9IHV0aWxpZGFkIC8gdmVudGFzICogMTAwLA0KICAgICAgICAgcGFydF92ZW50YXMgPSB2ZW50YXMgLyB2ZW50YXNfdG90ICogMTAwLA0KICAgICAgICAgcGFydF91dGlsaWRhZCA9IHV0aWxpZGFkIC8gdXRpbGlkYWRfdG90ICogMTAwLA0KICAgICAgICAgdXRpbGlkYWRfcG9yX21pbGxvbiA9IG1hcmdlbiAvIDEwMCAqIDFlNikgJT4lDQogIGFycmFuZ2UoZ3J1cG8pDQoNCnJlc3VtZW5fZ3J1cG8gJT4lDQogIHRyYW5zbXV0ZShHcnVwbyA9IGFzLmNoYXJhY3RlcihncnVwbyksIEVzdGFkb3MgPSBlc3RhZG9zLCBgJSB2ZW50YXNgID0gcGN0KHBhcnRfdmVudGFzKSwgYCUgdXRpbGlkYWRgID0gcGN0KHBhcnRfdXRpbGlkYWQpLA0KICAgICAgICAgICAgTWFyZ2VuID0gcGN0KG1hcmdlbiksIGBVdGlsaWRhZCBwb3IgY2FkYSBVUyQgMSBNIHZlbmRpZG9gID0gdXNkKHV0aWxpZGFkX3Bvcl9taWxsb24pLA0KICAgICAgICAgICAgYEVzdGFkb3MgKGRlIG1heW9yIGEgbWVub3IgdmVudGEpYCA9IGxpc3RhKSAlPiUNCiAgYmluZF9yb3dzKGRhdGEuZnJhbWUoR3J1cG8gPSAiVG90YWwiLCBFc3RhZG9zID0gc3VtKHJlc3VtZW5fZ3J1cG8kZXN0YWRvcyksDQogICAgICAgICAgICAgICAgICAgICAgIGAlIHZlbnRhc2AgPSBwY3Qoc3VtKHJlc3VtZW5fZ3J1cG8kcGFydF92ZW50YXMpKSwgYCUgdXRpbGlkYWRgID0gcGN0KHN1bShyZXN1bWVuX2dydXBvJHBhcnRfdXRpbGlkYWQpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgTWFyZ2VuID0gcGN0KG1hcmdlbl90b3QpLCBgVXRpbGlkYWQgcG9yIGNhZGEgVVMkIDEgTSB2ZW5kaWRvYCA9IHVzZChtYXJnZW5fdG90IC8gMTAwICogMWU2KSwNCiAgICAgICAgICAgICAgICAgICAgICAgYEVzdGFkb3MgKGRlIG1heW9yIGEgbWVub3IgdmVudGEpYCA9ICIiLCBjaGVjay5uYW1lcyA9IEZBTFNFKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgNy4gR3J1cG9zIGRlIGVzdGFkb3Mgc2Vnw7puIHZlbnRhcyB5IG1hcmdlbiIsIGFsaWduID0gYygibCIsICJyIiwgInIiLCAiciIsICJyIiwgInIiLCAibCIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IFRSVUUsIGZvbnRfc2l6ZSA9IDEzKSAlPiUNCiAgcm93X3NwZWMoNSwgYm9sZCA9IFRSVUUpDQpgYGANCg0KPHAgY2xhc3M9ImZ1ZW50ZSI+RnVlbnRlOiBlbGFib3JhY2nDs24gcHJvcGlhIGNvbiBkYXRvcyBkZSBEYXRvc0Nhc28xLnhsc3guPC9wPg0KDQoqKkV2aWRlbmNpYS4qKiBMb3MgNyBlc3RhZG9zIEVzdHJlbGxhIHRpZW5lbiBlbCAyMCwzJSBkZSBsYXMgdmVudGFzIHkgZWwgMjMsMCUgZGUgbGEgdXRpbGlkYWQsIGNvbiA0NCw2JSBkZSBtYXJnZW4uIExvcyAxMSBlc3RhZG9zIGEgQ29ycmVnaXIgdGllbmVuIGVsIDQwLDElIGRlIGxhcyB2ZW50YXMgeSBzb2xvIGVsIDM2LDklIGRlIGxhIHV0aWxpZGFkICgzNiwxJSBkZSBtYXJnZW4pLiBMb3MgMTUgZGUgT3BvcnR1bmlkYWQgZ2FuYW4gNDIsNiUuDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIENhZGEgbWlsbMOzbiBkZSBkw7NsYXJlcyBhZGljaW9uYWwgZGVqYSB1bm9zIFVTJCA0NDYgbWlsIGVuIHVuIGVzdGFkbyBFc3RyZWxsYSB5IHVub3MgVVMkIDM2MSBtaWwgZW4gdW5vIGEgQ29ycmVnaXIuIENyZWNlciBlbiBsb3MgcHJpbWVyb3MgcmluZGUgbcOhcy4NCg0KKlByZWd1bnRhOiDCv0VsIG1hcmdlbiBhbHRvIGVzdMOhIGFncnVwYWRvIGdlb2dyw6FmaWNhbWVudGU/Kg0KDQpgYGB7ciBtYXBhLCBmaWcuaGVpZ2h0PTZ9DQptYXBhX2VzdGFkb3MgPC0gcmVzdW1lbl9lc3RhZG8gJT4lDQogIG11dGF0ZShjb2RpZ28gPSBzdGF0ZS5hYmJbbWF0Y2goZXN0YWRvLCBzdGF0ZS5uYW1lKV0pDQoNCnBsb3RfZ2VvKG1hcGFfZXN0YWRvcywgbG9jYXRpb25tb2RlID0gIlVTQS1zdGF0ZXMiKSAlPiUNCiAgYWRkX3RyYWNlKHogPSB+bWFyZ2VuLCBsb2NhdGlvbnMgPSB+Y29kaWdvLCBjb2xvciA9IH5tYXJnZW4sIGNvbG9ycyA9ICJCbHVlcyIsDQogICAgICAgICAgICB0ZXh0ID0gfnBhc3RlMChlc3RhZG8sICI8YnI+TWFyZ2VuOiAiLCBwY3QobWFyZ2VuKSwgIjxicj5WZW50YXM6IFVTJCAiLCByb3VuZCh2ZW50YXMgLyAxZTYsIDIpLCAiIE08YnI+R3J1cG86ICIsIGdydXBvKSwNCiAgICAgICAgICAgIGhvdmVyaW5mbyA9ICJ0ZXh0IikgJT4lDQogIGNvbG9yYmFyKHRpdGxlID0gIk1hcmdlbiAoJSkiKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPkdyw6FmaWNhIDYuIE1hcmdlbiBvcGVyYXRpdm8gcG9yIGVzdGFkbyAoJSk8L2I+IiwgZm9udCA9IGxpc3Qoc2l6ZSA9IDE1KSksDQogICAgICAgICBnZW8gPSBsaXN0KHNjb3BlID0gInVzYSIsIHByb2plY3Rpb24gPSBsaXN0KHR5cGUgPSAiYWxiZXJzIHVzYSIpLCBzaG93bGFrZXMgPSBGQUxTRSkpDQpgYGANCg0KPHAgY2xhc3M9ImZ1ZW50ZSI+RnVlbnRlOiBlbGFib3JhY2nDs24gcHJvcGlhIGNvbiBkYXRvcyBkZSBEYXRvc0Nhc28xLnhsc3guPC9wPg0KDQoqKkV2aWRlbmNpYS4qKiBVbiBhenVsIG3DoXMgb3NjdXJvIGluZGljYSBtYXlvciBtYXJnZW4uIExvcyBtw6FyZ2VuZXMgbcOhcyBhbHRvcyBlc3TDoW4gZW4gZWwgU3VyIChBbGFiYW1hIDU0LDQlLCBUZW5uZXNzZWUgNDksNSUpIHkgbG9zIG3DoXMgYmFqb3MsIGVuIGVsIFBhY8OtZmljbzogSGF3YWlpICgyOCw1JSksIFdhc2hpbmd0b24gKDI4LDglKSB5IEFsYXNrYSAoMzIsNyUpLiBDYWxpZm9ybmlhLCBlbCBzZWd1bmRvIGVzdGFkbyBxdWUgbcOhcyB2ZW5kZSBkZXNwdcOpcyBkZSBOZXcgWW9yaywgdGllbmUgMzQsNSUuDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIEVsIG1hcmdlbiBzaWd1ZSB1biBwYXRyw7NuIGdlb2dyw6FmaWNvOiBlcyBhbHRvIGVuIGVsIFN1ciB5IGJham8sIHNvYnJlIHRvZG8sIGVuIGVsIFBhY8OtZmljby4gVW5hIHBvc2libGUgZXhwbGljYWNpw7NuIHNvbiBjb3N0b3MgZGUgb3BlcmFjacOzbiBtw6FzIGFsdG9zIGVuIGVzb3MgZXN0YWRvcywgcGVybyBsYSBiYXNlIGRlIGRhdG9zIG5vIHRyYWUgY29zdG9zIHBhcmEgY29tcHJvYmFybG8uDQoNCipQcmVndW50YTogwr9RdcOpIGNpdWRhZGVzIGFwb3J0YW4gbcOhcyB1dGlsaWRhZCB5IGNvbiBxdcOpIG1hcmdlbj8qDQoNCmBgYHtyIGNpdWRhZGVzLCBmaWcuaGVpZ2h0PTZ9DQpyZXN1bWVuX2NpdWRhZCA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uLCBlc3RhZG8sIGNpdWRhZCkgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwYXJ0X3V0aWxpZGFkID0gdXRpbGlkYWQgLyBzdW0odXRpbGlkYWQpICogMTAwLA0KICAgICAgICAgbm9tYnJlID0gcGFzdGUwKGNpdWRhZCwgIiAoIiwgZXN0YWRvLCAiKSDCtyAiLCBwY3QobWFyZ2VuKSkpICU+JQ0KICBhcnJhbmdlKGRlc2ModXRpbGlkYWQpKQ0KDQojIFBhcnRpY2lwYWNpw7NuIGRlIGxhcyAxMCBjaXVkYWRlcyBjb24gbcOhcyB1dGlsaWRhZA0KdG9wMTBfY2l1ZGFkZXMgPC0gc3VtKHJlc3VtZW5fY2l1ZGFkJHBhcnRfdXRpbGlkYWRbMToxMF0pDQoNCnRvcDE1IDwtIHJlc3VtZW5fY2l1ZGFkICU+JSBzbGljZSgxOjE1KSAlPiUgbXV0YXRlKG5vbWJyZSA9IGZhY3Rvcihub21icmUsIGxldmVscyA9IHJldihub21icmUpKSkNCg0KZ2dwbG90bHkoDQogIGdncGxvdCh0b3AxNSwgYWVzKHggPSB1dGlsaWRhZCAvIDFlNiwgeSA9IG5vbWJyZSwgZmlsbCA9IG1hcmdlbiwNCiAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMChub21icmUsICJcblV0aWxpZGFkOiBVUyQgIiwgcm91bmQodXRpbGlkYWQgLyAxZTYsIDIpLCAiIE0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcblZlbnRhczogVVMkICIsIHJvdW5kKHZlbnRhcyAvIDFlNiwgMiksICIgTVxuTWFyZ2VuOiAiLCBwY3QobWFyZ2VuKSkpKSArDQogICAgZ2VvbV9jb2woKSArDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiI2M2ZGJlZiIsIGhpZ2ggPSAiIzA4MzA2YiIpICsNCiAgICBsYWJzKHRpdGxlID0gIkdyw6FmaWNhIDcuIExhcyAxNSBjaXVkYWRlcyBjb24gbcOhcyB1dGlsaWRhZCBvcGVyYXRpdmEiLA0KICAgICAgICAgeCA9ICJVdGlsaWRhZCBvcGVyYXRpdmEgKG1pbGxvbmVzIGRlIFVTJCkiLCB5ID0gIkNpdWRhZCAobWFyZ2VuKSIsIGZpbGwgPSAiTWFyZ2VuICglKSIpICsNCiAgICB0ZW1hLA0KICB0b29sdGlwID0gInRleHQiKSAlPiUNCiAgbGF5b3V0KGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAiaCIsIHkgPSAtMC4xNSkpDQpgYGANCg0KPHAgY2xhc3M9ImZ1ZW50ZSI+RnVlbnRlOiBlbGFib3JhY2nDs24gcHJvcGlhIGNvbiBkYXRvcyBkZSBEYXRvc0Nhc28xLnhsc3guPC9wPg0KDQoqKkV2aWRlbmNpYS4qKiBFbCBsYXJnbyBkZSBjYWRhIGJhcnJhIGVzIGxhIHV0aWxpZGFkIGRlIGxhIGNpdWRhZCB5IGVsIGNvbG9yLCBzdSBtYXJnZW4gKG3DoXMgb3NjdXJvLCBtYXlvciBtYXJnZW4pLiBMYXMgMTAgcHJpbWVyYXMgY2l1ZGFkZXMgYXBvcnRhbiBlbCAzMiwwJSBkZSBsYSB1dGlsaWRhZC4gTmV3IFlvcmsgeSBTYW4gRnJhbmNpc2NvIGVuY2FiZXphbiBsYSBsaXN0YSBjb24gbcOhcmdlbmVzIGRlIDM3LDMlIHkgMzIsMSUsIG1pZW50cmFzIEJpcm1pbmdoYW0gKDU0LDQlKSwgS25veHZpbGxlICg0OSw1JSkgeSBEYWxsYXMgKDQ0LDglKSBjb21iaW5hbiBidWVuYSB1dGlsaWRhZCB5IGJ1ZW4gbWFyZ2VuLg0KDQoqKkludGVycHJldGFjacOzbiBkZSBsYSBHcsOhZmljYSA3LioqIExhcyBjaXVkYWRlcyBxdWUgbcOhcyB1dGlsaWRhZCBhcG9ydGFuIG5vIHNvbiBsYXMgcXVlIG3DoXMgZ2FuYW4gcG9yIGNhZGEgZMOzbGFyIHZlbmRpZG8uDQoNCioqSW1wbGljYWNpw7NuIHBhcmEgZWwgbmVnb2Npby4qKiBQYXJhIGNyZWNlciBjb252aWVuZSBkYXIgcHJpb3JpZGFkIGEgbG9zIGVzdGFkb3MgRXN0cmVsbGEgeSBkZSBPcG9ydHVuaWRhZCAoZXN0b3Mgw7psdGltb3MgeWEgZ2FuYW4gNDIsNiUgYXVucXVlIHZlbmRhbiBwb2NvKSB5IG5vIGJ1c2NhciBtw6FzIHZvbHVtZW4gZW4gbG9zIGVzdGFkb3MgYSBDb3JyZWdpciBoYXN0YSBtZWpvcmFyIHN1IG1hcmdlbi4NCg0KIyMgSGFsbGF6Z28gMi4gV2VzdCBnYW5hIG1lbm9zIGNvbiBsbyBtaXNtbyBxdWUgdmVuZGVuIGxhcyBkZW3DoXMgcmVnaW9uZXMNCg0KKlByZWd1bnRhOiDCv1dlc3QgdGllbmUgbWVub3IgbWFyZ2VuIHBvcnF1ZSB2ZW5kZSBwcm9kdWN0b3MgbyBjYW5hbGVzIGRlIGJham8gbWFyZ2VuLCBvIHBvcnF1ZSBnYW5hIG1lbm9zIGVuIGxvIG1pc21vIHF1ZSB2ZW5kZW4gbGFzIGRlbcOhcyByZWdpb25lcz8qDQoNClBhcmEgcmVzcG9uZGVybGEsIGEgY2FkYSByZWdpc3RybyBsZSBhc2lnbmFtb3MgZWwgbWFyZ2VuIG5hY2lvbmFsIGRlIHN1IG1pc21vIHByb2R1Y3RvIHkgbcOpdG9kbyBkZSB2ZW50YSwgeSBjb24gw6lsIGNhbGN1bGFtb3MgbGEgdXRpbGlkYWQgZXNwZXJhZGEuIFNpIFdlc3QgZ2FuYXJhIG1lbm9zIHNvbG8gcG9yIHZlbmRlciBwcm9kdWN0b3MgbyBjYW5hbGVzIGRlIG1lbm9yIG1hcmdlbiwgc3UgdXRpbGlkYWQgcmVhbCBzZXLDrWEgcGFyZWNpZGEgYSBsYSBlc3BlcmFkYS4NCg0KYGBge3IgZXNwZXJhZG99DQojIE1hcmdlbiBuYWNpb25hbCBkZSBjYWRhIGNvbWJpbmFjacOzbiBwcm9kdWN0byB4IG3DqXRvZG8NCm1hcmdlbl9yZWZlcmVuY2lhIDwtIGRhdG9zICU+JQ0KICBncm91cF9ieShwcm9kdWN0bywgbWV0b2RvX3ZlbnRhKSAlPiUNCiAgc3VtbWFyaXNlKG1hcmdlbl9yZWYgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSAvIHN1bSh2ZW50YXNfdG90YWwpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQpkYXRvc19yZWYgPC0gZGF0b3MgJT4lDQogIGxlZnRfam9pbihtYXJnZW5fcmVmZXJlbmNpYSwgYnkgPSBjKCJwcm9kdWN0byIsICJtZXRvZG9fdmVudGEiKSkgJT4lDQogIG11dGF0ZSh1dGlsaWRhZF9lc3BlcmFkYSA9IHZlbnRhc190b3RhbCAqIG1hcmdlbl9yZWYpDQoNCmJyZWNoYV9yZWdpb24gPC0gZGF0b3NfcmVmICU+JQ0KICBncm91cF9ieShyZWdpb24pICU+JQ0KICBzdW1tYXJpc2UodmVudGFzID0gc3VtKHZlbnRhc190b3RhbCksDQogICAgICAgICAgICB1dGlsaWRhZF9yZWFsID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksDQogICAgICAgICAgICB1dGlsaWRhZF9lc3AgPSBzdW0odXRpbGlkYWRfZXNwZXJhZGEpKSAlPiUNCiAgbXV0YXRlKG1hcmdlbl9yZWFsID0gdXRpbGlkYWRfcmVhbCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIG1hcmdlbl9lc3BlcmFkbyA9IHV0aWxpZGFkX2VzcCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIGJyZWNoYV9wcCA9IG1hcmdlbl9yZWFsIC0gbWFyZ2VuX2VzcGVyYWRvLA0KICAgICAgICAgYnJlY2hhX3VzZCA9IHV0aWxpZGFkX3JlYWwgLSB1dGlsaWRhZF9lc3ApICU+JQ0KICBhcnJhbmdlKGJyZWNoYV91c2QpDQoNCmJyZWNoYV9yZWdpb24gJT4lDQogIHRyYW5zbXV0ZShgUmVnacOzbmAgPSByZWdpb24sIGBNYXJnZW4gcmVhbGAgPSBwY3QobWFyZ2VuX3JlYWwpLCBgTWFyZ2VuIGVzcGVyYWRvYCA9IHBjdChtYXJnZW5fZXNwZXJhZG8pLA0KICAgICAgICAgICAgYERpZmVyZW5jaWEgKHB1bnRvcylgID0gbnVtKGJyZWNoYV9wcCwgMSksIGBEaWZlcmVuY2lhIChVUyQpYCA9IHVzZChicmVjaGFfdXNkKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgOC4gTWFyZ2VuIHJlYWwgZnJlbnRlIGFsIGVzcGVyYWRvIHBvciBsYSBtZXpjbGEgZGUgcHJvZHVjdG8geSBtw6l0b2RvIiwgYWxpZ24gPSBjKCJsIiwgInIiLCAiciIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIERhdG9zQ2FzbzEueGxzeC48L3A+DQoNCioqRXZpZGVuY2lhLioqIFBvciBsbyBxdWUgdmVuZGUsIFdlc3QgZGViZXLDrWEgdGVuZXIgdW4gbWFyZ2VuIGRlIDM4LDklLCBwZXJvIHRpZW5lIDM1LDclOiAzLDIgcHVudG9zIG1lbm9zLCB1bm9zIFVTJCAxLDE2IG1pbGxvbmVzIGRlIHV0aWxpZGFkLiBTb3V0aCwgZW4gY2FtYmlvLCBlc3TDoSA0LDYgcHVudG9zIHBvciBlbmNpbWEgZGUgbG8gZXNwZXJhZG8uDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIExhIG1lemNsYSBkZSBwcm9kdWN0b3MgeSBjYW5hbGVzIG5vIGV4cGxpY2EgZWwgbWFyZ2VuIGJham8gZGUgV2VzdDogbGEgcmVnacOzbiBnYW5hIG1lbm9zIGNvbiBsbyBtaXNtbyBxdWUgdmVuZGVuIGxhcyBkZW3DoXMuDQoNCipQcmVndW50YTogwr9MYSBicmVjaGEgZGUgV2VzdCBlc3TDoSByZXBhcnRpZGEgZW4gdG9kYSBsYSByZWdpw7NuIG8gY29uY2VudHJhZGEgZW4gYWxndW5vcyBlc3RhZG9zPyoNCg0KYGBge3Igd2VzdF9lc3RhZG9zfQ0KYnJlY2hhX3dlc3QgPC0gZGF0b3NfcmVmICU+JQ0KICBmaWx0ZXIocmVnaW9uID09ICJXZXN0IikgJT4lDQogIGdyb3VwX2J5KGVzdGFkbykgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIGJyZWNoYV91c2QgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSAtIHN1bSh1dGlsaWRhZF9lc3BlcmFkYSkpICU+JQ0KICBtdXRhdGUoc2lnbm8gPSBpZmVsc2UoYnJlY2hhX3VzZCA8IDAsICJQb3IgZGViYWpvIGRlIGxvIGVzcGVyYWRvIiwgIlBvciBlbmNpbWEgZGUgbG8gZXNwZXJhZG8iKSwNCiAgICAgICAgIGVzdGFkbyA9IHJlb3JkZXIoZXN0YWRvLCBicmVjaGFfdXNkKSkNCg0KZ2dwbG90bHkoDQogIGdncGxvdChicmVjaGFfd2VzdCwgYWVzKHggPSBicmVjaGFfdXNkIC8gMTAwMCwgeSA9IGVzdGFkbywgZmlsbCA9IHNpZ25vLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKGVzdGFkbywgIlxuRGlmZXJlbmNpYTogVVMkICIsIHJvdW5kKGJyZWNoYV91c2QgLyAxMDAwKSwgIiBtaWwiKSkpICsNCiAgICBnZW9tX2NvbCgpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJQb3IgZGViYWpvIGRlIGxvIGVzcGVyYWRvIiA9ICJmaXJlYnJpY2siLCAiUG9yIGVuY2ltYSBkZSBsbyBlc3BlcmFkbyIgPSAiZm9yZXN0Z3JlZW4iKSkgKw0KICAgIGxhYnModGl0bGUgPSAiR3LDoWZpY2EgOC4gV2VzdDogdXRpbGlkYWQgcmVhbCBtZW5vcyBlc3BlcmFkYSwgcG9yIGVzdGFkbyIsDQogICAgICAgICB4ID0gIk1pbGVzIGRlIFVTJCIsIHkgPSBOVUxMLCBmaWxsID0gIiIpICsNCiAgICB0ZW1hLA0KICB0b29sdGlwID0gInRleHQiKSAlPiUNCiAgbGF5b3V0KGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAiaCIsIHkgPSAtMC4xNSkpDQpgYGANCg0KPHAgY2xhc3M9ImZ1ZW50ZSI+RnVlbnRlOiBlbGFib3JhY2nDs24gcHJvcGlhIGNvbiBkYXRvcyBkZSBEYXRvc0Nhc28xLnhsc3guPC9wPg0KDQoqKkV2aWRlbmNpYS4qKiBFbiByb2pvLCBsb3MgZXN0YWRvcyBxdWUgZ2FuYW4gbWVub3MgZGUgbG8gZXNwZXJhZG87IGVuIHZlcmRlLCBsb3MgcXVlIGdhbmFuIG3DoXMuIENhbGlmb3JuaWEsIEhhd2FpaSB5IFdhc2hpbmd0b24gc3VtYW4gZWwgNzglIGRlbCBmYWx0YW50ZSBkZSBXZXN0LiBVdGFoLCBJZGFobywgQ29sb3JhZG8sIE9yZWdvbiB5LCBwb3IgbXV5IHBvY28sIFd5b21pbmcgZXN0w6FuIHBvciBlbmNpbWEgZGUgbG8gZXNwZXJhZG8uDQoNCioqSW50ZXJwcmV0YWNpw7NuIGRlIGxhIEdyw6FmaWNhIDguKiogRWwgZmFsdGFudGUgc2UgY29uY2VudHJhIGVuIHRyZXMgZXN0YWRvcyB5IG5vIGVuIHRvZGEgbGEgcmVnacOzbi4NCg0KKlByZWd1bnRhOiDCv1F1w6kgcHJvZHVjdG9zIHkgcXXDqSBkaXN0cmlidWlkb3JlcyBleHBsaWNhbiBsYSBicmVjaGE/Kg0KDQpgYGB7ciBwcm9kdWN0b19yZWdpb259DQojIE1hcmdlbiBkZSBjYWRhIHByb2R1Y3RvIGVuIGNhZGEgcmVnacOzbg0KZGF0b3MgJT4lDQogIGdyb3VwX2J5KHByb2R1Y3RvLCByZWdpb24pICU+JQ0KICBzdW1tYXJpc2UobWFyZ2VuID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkgLyBzdW0odmVudGFzX3RvdGFsKSAqIDEwMCwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIG11dGF0ZShtYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSByZWdpb24sIHZhbHVlc19mcm9tID0gbWFyZ2VuKSAlPiUNCiAgcmVuYW1lKFByb2R1Y3RvID0gcHJvZHVjdG8pICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDkuIE1hcmdlbiBvcGVyYXRpdm8gcG9yIHByb2R1Y3RvIHkgcmVnacOzbiIsIGFsaWduID0gYygibCIsIHJlcCgiciIsIDUpKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIERhdG9zQ2FzbzEueGxzeC48L3A+DQoNCioqRXZpZGVuY2lhLioqIFdvbWVuJ3MgQXBwYXJlbCBnYW5hIDI5LDglIGVuIFdlc3QgeSA1MiwxJSBlbiBTb3V0aCwgMjIgcHVudG9zIGRlIGRpZmVyZW5jaWEuIEVzIGxhIGNvbWJpbmFjacOzbiBkZSByZWdpw7NuIHkgcHJvZHVjdG8gY29uIGVsIG1heW9yIGZhbHRhbnRlIGZyZW50ZSBhIGxvIGVzcGVyYWRvICh1bm9zIFVTJCA3MzMgbWlsKS4gRWwgc2VndW5kbyBwcm9kdWN0byBtw6FzIGTDqWJpbCBlbiBXZXN0IGVzIE1lbidzIEF0aGxldGljIEZvb3R3ZWFyICgzMSw5JSkuDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIEVsIG1pc21vIHByb2R1Y3RvIGRlamEgbcOhcmdlbmVzIG11eSBkaXN0aW50b3Mgc2Vnw7puIGxhIHJlZ2nDs24gZG9uZGUgc2UgdmVuZGUuDQoNCmBgYHtyIGRpc3RyaWJ1aWRvcmVzfQ0KZGlzdHJpYnVpZG9yX3JlZ2lvbiA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yLCByZWdpb24pICU+JQ0KICBzdW1tYXJpc2UobWFyZ2VuID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkgLyBzdW0odmVudGFzX3RvdGFsKSAqIDEwMCwgLmdyb3VwcyA9ICJkcm9wIikNCg0KcGVvcl9yZWdpb24gPC0gZGlzdHJpYnVpZG9yX3JlZ2lvbiAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yKSAlPiUNCiAgc3VtbWFyaXNlKGBSZWdpw7NuIGRlIG1lbm9yIG1hcmdlbmAgPSByZWdpb25bd2hpY2gubWluKG1hcmdlbildLA0KICAgICAgICAgICAgYERpZmVyZW5jaWEgZW50cmUgc3UgbWVqb3IgeSBzdSBwZW9yIHJlZ2nDs24gKHB1bnRvcylgID0gbnVtKG1heChtYXJnZW4pIC0gbWluKG1hcmdlbiksIDEpKQ0KDQpwZXNvX3dlc3QgPC0gZGF0b3MgJT4lDQogIGZpbHRlcihyZWdpb24gPT0gIldlc3QiKSAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpKSAlPiUNCiAgbXV0YXRlKGAlIGRlIGxhcyB2ZW50YXMgZGUgV2VzdGAgPSBwY3QodmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDApKSAlPiUNCiAgc2VsZWN0KC12ZW50YXMpDQoNCmRpc3RyaWJ1aWRvcl9yZWdpb24gJT4lDQogIG11dGF0ZShtYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSByZWdpb24sIHZhbHVlc19mcm9tID0gbWFyZ2VuLCB2YWx1ZXNfZmlsbCA9ICJTaW4gdmVudGFzIikgJT4lDQogIGxlZnRfam9pbihwZW9yX3JlZ2lvbiwgYnkgPSAiZGlzdHJpYnVpZG9yIikgJT4lDQogIGxlZnRfam9pbihwZXNvX3dlc3QsIGJ5ID0gImRpc3RyaWJ1aWRvciIpICU+JQ0KICByZW5hbWUoRGlzdHJpYnVpZG9yID0gZGlzdHJpYnVpZG9yKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAxMC4gTWFyZ2VuIGRlIGNhZGEgZGlzdHJpYnVpZG9yIHBvciByZWdpw7NuIiwgYWxpZ24gPSBjKCJsIiwgcmVwKCJyIiwgOCkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IFRSVUUsIGZvbnRfc2l6ZSA9IDEzKQ0KYGBgDQoNCjxwIGNsYXNzPSJmdWVudGUiPkZ1ZW50ZTogZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgRGF0b3NDYXNvMS54bHN4LjwvcD4NCg0KKipFdmlkZW5jaWEuKiogQ2luY28gZGUgbG9zIHNlaXMgZGlzdHJpYnVpZG9yZXMgdGllbmVuIHN1IG1lbm9yIG1hcmdlbiBlbiBXZXN0OyBsYSBleGNlcGNpw7NuIGVzIFNwb3J0cyBEaXJlY3QsIGN1eW8gbWFyZ2VuIG3DoXMgYmFqbyBlc3TDoSBlbiBOb3J0aGVhc3QuIFdhbG1hcnQsIHBvciBlamVtcGxvLCBnYW5hIDQxLDklIGVuIFNvdXRoIHkgMjcsMyUgZW4gV2VzdC4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogU2kgY2FzaSB0b2RvcyBsb3MgZGlzdHJpYnVpZG9yZXMgZ2FuYW4gbWVub3MgZW4gV2VzdCwgbGEgY2F1c2EgcGFyZWNlIGVzdGFyIGVuIGVsIG1lcmNhZG8geSBubyBlbiB1biBkaXN0cmlidWlkb3IgZW4gcGFydGljdWxhci4NCg0KKipFeHBsaWNhY2lvbmVzIGFsdGVybmF0aXZhcyB5IGRhdG9zIHF1ZSBmYWx0YW4uKiogRWwgbWFyZ2VuIGJham8gZGUgV2VzdCBwb2Ryw61hIGRlYmVyc2UgYSAoYSkgY29zdG9zIGRlIG9wZXJhY2nDs24sIGxvZ8Otc3RpY2EgbyBhcnJpZW5kbyBtw6FzIGFsdG9zLCAoYikgZGVzY3VlbnRvcyBvIHByb21vY2lvbmVzIGRlIGxvcyBkaXN0cmlidWlkb3JlcywgbyAoYykgdW4gY29zdG8gZGUgcHJvZHVjdG8gZGlzdGludG8gZW4gbGEgcmVnacOzbi4gUGFyYSBzYWJlciBjdcOhbCBwZXNhIG3DoXMgc2UgbmVjZXNpdGFuIGVsIGNvc3RvIGRlIHZlbnRhcywgbG9zIGRlc2N1ZW50b3MgeSBsYSBmZWNoYSBkZSBjYWRhIHZlbnRhLCBxdWUgbGEgYmFzZSBubyBpbmNsdXllLg0KDQoqKkltcGxpY2FjacOzbiBwYXJhIGVsIG5lZ29jaW8uKiogRXMgcG9zaWJsZSBxdWUgY2FtYmlhciBkZSBkaXN0cmlidWlkb3Igbm8gbWVqb3JlIGVsIG1hcmdlbi4gU2UgZGViZW4gcmV2aXNhciBsb3MgY29zdG9zIHkgcHJlY2lvcyBlbiBDYWxpZm9ybmlhLCBXYXNoaW5ndG9uIHkgSGF3YWlpLCBlbXBlemFuZG8gcG9yIFdvbWVuJ3MgQXBwYXJlbCB5IHBvciBXZXN0IEdlYXIsIHF1ZSB0aWVuZSBlbCA1MSwzJSBkZSBsYXMgdmVudGFzIGRlIFdlc3QuDQoNCiMjIEhhbGxhemdvIDMuIEVsIG1pc21vIG3DqXRvZG8gZGUgdmVudGEgcmluZGUgZGlzdGludG8gc2Vnw7puIGVsIG1lcmNhZG8NCg0KKlByZWd1bnRhOiDCv1F1w6kgY29tYmluYWNpb25lcyBkZSBjaXVkYWQgeSBtw6l0b2RvIHNvbiBsYXMgbcOhcyB5IGxhcyBtZW5vcyByZW50YWJsZXM/Kg0KDQpgYGB7ciBjaXVkYWRfbWV0b2RvfQ0KY2l1ZGFkX21ldG9kbyA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uLCBjaXVkYWQsIGVzdGFkbywgbWV0b2RvX3ZlbnRhKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLA0KICAgICAgICAgICAgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCkgJT4lDQogIGZpbHRlcih2ZW50YXMgPj0gNTAwMDAwKSAgICMgc2UgZGVqYW4gY29tYmluYWNpb25lcyBjb24gdmVudGFzIHJlbGV2YW50ZXMgKFVTJCA1MDAgbWlsIG8gbcOhcykNCg0KbWVqb3JlcyA8LSBjaXVkYWRfbWV0b2RvICU+JSBhcnJhbmdlKGRlc2MobWFyZ2VuKSkgJT4lIHNsaWNlKDE6OCkNCnBlb3JlcyAgPC0gY2l1ZGFkX21ldG9kbyAlPiUgYXJyYW5nZShtYXJnZW4pICU+JSBzbGljZSgxOjgpDQoNCmJpbmRfcm93cyhtZWpvcmVzLCBwZW9yZXMpICU+JQ0KICB0cmFuc211dGUoQ2l1ZGFkID0gcGFzdGUwKGNpdWRhZCwgIiAoIiwgZXN0YWRvLCAiKSIpLCBgUmVnacOzbmAgPSByZWdpb24sIGBNw6l0b2RvYCA9IG1ldG9kb192ZW50YSwNCiAgICAgICAgICAgIFZlbnRhcyA9IHVzZCh2ZW50YXMpLCBVdGlsaWRhZCA9IHVzZCh1dGlsaWRhZCksIE1hcmdlbiA9IHBjdChtYXJnZW4pKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAxMS4gQ29tYmluYWNpb25lcyBjaXVkYWQgw5cgbcOpdG9kbyBkZSB2ZW50YSBjb24gbWF5b3IgeSBtZW5vciBtYXJnZW4gKHZlbnRhcyBkZSBVUyQgNTAwIG1pbCBvIG3DoXMpIiwNCiAgICAgICAgYWxpZ24gPSBjKCJsIiwgImwiLCAibCIsICJyIiwgInIiLCAiciIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IFRSVUUsIGZvbnRfc2l6ZSA9IDEzKSAlPiUNCiAgcGFja19yb3dzKCJNYXlvciBtYXJnZW4iLCAxLCA4KSAlPiUNCiAgcGFja19yb3dzKCJNZW5vciBtYXJnZW4iLCA5LCAxNikNCmBgYA0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIERhdG9zQ2FzbzEueGxzeC48L3A+DQoNCioqRXZpZGVuY2lhLioqIExhcyBjb21iaW5hY2lvbmVzIG3DoXMgcmVudGFibGVzIHNvbiB2ZW50YXMgT25saW5lIGVuIGNpdWRhZGVzIGRlbCBTdXIsIGNvbW8gRGFsbGFzICg1NCw4JSksIHkgZWwgT3V0bGV0IGRlIEJpcm1pbmdoYW0gKDUyLDMlKS4gT25saW5lIHRhbWJpw6luIGdhbmEgYmllbiBlbiBlc3RhZG9zIGRlbCBncnVwbyBhIENvcnJlZ2lyLCBwb3IgZWplbXBsbyBlbiBBbGJhbnksIE5ldyBZb3JrICg1MCw0JSkuIENpbmNvIGRlIGxhcyBvY2hvIG1lbm9zIHJlbnRhYmxlcyBlc3TDoW4gZW4gV2VzdCwgZW50cmUgZWxsYXMgZWwgT3V0bGV0IGRlIEhvbm9sdWx1ICgyNSw5JSksIGVsIEluLXN0b3JlIGRlIFNlYXR0bGUgKDI2LDIlKSB5IGVsIE91dGxldCBkZSBTYW4gRnJhbmNpc2NvICgzMCwxJSksIHF1ZSBhZGVtw6FzIHZlbmRlIFVTJCA0LDA3IG1pbGxvbmVzLg0KDQoqKkludGVycHJldGFjacOzbi4qKiBFbCBPdXRsZXQgZ2FuYSA0NCw2JSBlbiBTb3V0aCB5IDMyLDglIGVuIFdlc3QgKEdyw6FmaWNhIDIpOiBjYXNpIDEyIHB1bnRvcyBkZSBkaWZlcmVuY2lhIGNvbiBlbCBtaXNtbyBtw6l0b2RvLiBPbmxpbmUsIGVuIGNhbWJpbywgZGVqYSBidWVuIG1hcmdlbiBpbmNsdXNvIGRvbmRlIGxhIHJlZ2nDs24gZ2FuYSBwb2NvLg0KDQoqKkltcGxpY2FjacOzbiBwYXJhIGVsIG5lZ29jaW8uKiogSGF5IGVzcGFjaW8gcGFyYSBpbXB1bHNhciBPbmxpbmUgZW4gTm9ydGhlYXN0LCBkb25kZSBnYW5hIDQ4LDYlIHBlcm8gc29sbyBwZXNhIGVsIDE4LDQlIGRlIGxhcyB2ZW50YXMuIExhIGZvcm1hIGVuIHF1ZSBvcGVyYSBlbCBPdXRsZXQgZW4gU291dGggcHVlZGUgc2VydmlyIGRlIHJlZmVyZW5jaWEgcGFyYSBXZXN0Lg0KDQojIyBIYWxsYXpnbyA0LiBQcmVjaW8sIHZvbHVtZW4geSB1dGlsaWRhZDogbG8gcXVlIGNhbWJpYSBhbCBjb21wYXJhciBjYXNvcyBwYXJlY2lkb3MNCg0KKlByZWd1bnRhOiDCv0PDs21vIHNlIHJlbGFjaW9uYW4gcHJlY2lvLCB2b2x1bWVuIHkgdXRpbGlkYWQsIHkgbGEgcmVsYWNpw7NuIHF1ZSBzZSB2ZSBlbiBlbCB0b3RhbCBzZSBtYW50aWVuZSBwb3IgcmVnacOzbiwgcHJvZHVjdG8geSBlc3RhZG8/Kg0KDQpVbmEgY29ycmVsYWNpw7NuIGNhbGN1bGFkYSBzb2JyZSBlbCB0b3RhbCBwdWVkZSByZWZsZWphciBzb2xvIGPDs21vIGVzdMOhbiBjb21wdWVzdGFzIGxhcyB2ZW50YXMuIFBvciBlc28gdGFtYmnDqW4gbGEgY2FsY3VsYW1vcyBkZW50cm8gZGUgY2FkYSByZWdpw7NuLCBwcm9kdWN0bywgZXN0YWRvIHkgY29tYmluYWNpw7NuIGRlIGVzdGFkbywgcHJvZHVjdG8geSBtw6l0b2RvLCByZXN0YW5kbyBhIGNhZGEgcmVnaXN0cm8gZWwgcHJvbWVkaW8gZGUgc3UgZ3J1cG8uDQoNCmBgYHtyIHJlbGFjaW9uZXN9DQojIENvcnJlbGFjacOzbiBkZW50cm8gZGUgZ3J1cG9zOiBzZSByZXN0YSBhIGNhZGEgdmFsb3IgZWwgcHJvbWVkaW8gZGUgc3UgZ3J1cG8gYW50ZXMgZGUgY29ycmVsYWNpb25hcg0KY29yX2RlbnRybyA8LSBmdW5jdGlvbih4LCB5LCBncnVwbykgY29yKHggLSBhdmUoeCwgZ3J1cG8pLCB5IC0gYXZlKHksIGdydXBvKSkNCg0KZ3J1cG9fcmVnaW9uICAgPC0gZGF0b3MkcmVnaW9uDQpncnVwb19wcm9kdWN0byA8LSBkYXRvcyRwcm9kdWN0bw0KZ3J1cG9fZXN0YWRvICAgPC0gZGF0b3MkZXN0YWRvDQpncnVwb19maW5vICAgICA8LSBwYXN0ZShkYXRvcyRlc3RhZG8sIGRhdG9zJHByb2R1Y3RvLCBkYXRvcyRtZXRvZG9fdmVudGEpDQoNCnJlbGFjaW9uZXMgPC0gZGF0YS5mcmFtZSgNCiAgYFJlbGFjacOzbmAgPSBjKCJQcmVjaW8geSB1bmlkYWRlcyIsICJQcmVjaW8geSBtYXJnZW4iLCAiVW5pZGFkZXMgeSBtYXJnZW4iLCAiVW5pZGFkZXMgeSB1dGlsaWRhZCIpLA0KICBUb3RhbCA9IGMoY29yKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzKSwNCiAgICAgICAgICAgIGNvcihkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvKSwNCiAgICAgICAgICAgIGNvcihkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2byksDQogICAgICAgICAgICBjb3IoZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSkpLA0KICBgRGVudHJvIGRlIGNhZGEgcmVnacOzbmAgPSBjKGNvcl9kZW50cm8oZGF0b3MkcHJlY2lvX3VuaWRhZCwgZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGdydXBvX3JlZ2lvbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8sIGdydXBvX3JlZ2lvbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19yZWdpb24pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkdXRpbGlkYWRfb3BlcmF0aXZhLCBncnVwb19yZWdpb24pKSwNCiAgYERlbnRybyBkZSBjYWRhIHByb2R1Y3RvYCA9IGMoY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZ3J1cG9fcHJvZHVjdG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8sIGdydXBvX3Byb2R1Y3RvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fcHJvZHVjdG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBkYXRvcyR1dGlsaWRhZF9vcGVyYXRpdmEsIGdydXBvX3Byb2R1Y3RvKSksDQogIGBEZW50cm8gZGUgY2FkYSBlc3RhZG9gID0gYyhjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBncnVwb19lc3RhZG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19lc3RhZG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fZXN0YWRvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcl9kZW50cm8oZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSwgZ3J1cG9fZXN0YWRvKSksDQogIGBEZW50cm8gZGUgZXN0YWRvLCBwcm9kdWN0byB5IG3DqXRvZG9gID0gYyhjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBncnVwb19maW5vKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19maW5vKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fZmlubyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcl9kZW50cm8oZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSwgZ3J1cG9fZmlubykpLA0KICBjaGVjay5uYW1lcyA9IEZBTFNFKQ0KDQpyZWxhY2lvbmVzICU+JQ0KICBtdXRhdGUoYWNyb3NzKC1gUmVsYWNpw7NuYCwgfiBudW0oLngsIDIpKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgMTIuIENvcnJlbGFjacOzbiBkZSBQZWFyc29uIGVuIGVsIHRvdGFsIHkgZGVudHJvIGRlIHNlZ21lbnRvcyIsIGFsaWduID0gYygibCIsIHJlcCgiciIsIDUpKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGRhdG9zIGRlIERhdG9zQ2FzbzEueGxzeC48L3A+DQoNCioqRXZpZGVuY2lhLioqIENhZGEgZmlsYSBkZSBsYSBUYWJsYSAxMiBlcyB1bmEgcmVsYWNpw7NuIGVudHJlIGRvcyB2YXJpYWJsZXMuIExhIHByaW1lcmEgY29sdW1uYSBsYSBtaWRlIGNvbiB0b2RvcyBsb3MgZGF0b3MganVudG9zIHkgbGFzIGRlbcOhcyBjb21wYXJhbiBzb2xvIHZlbnRhcyBwYXJlY2lkYXMgKGRlIGxhIG1pc21hIHJlZ2nDs24sIHByb2R1Y3RvIG8gZXN0YWRvKS4NCg0KLSAqKlByZWNpbyB5IG1hcmdlbjoqKiBDb24gdG9kb3MgbG9zIGRhdG9zIGVzIOKIkjAsMTQgKGEgbWF5b3IgcHJlY2lvLCBhbGdvIG1lbm9zIGRlIG1hcmdlbikuIERlbnRybyBkZSB1biBtaXNtbyBlc3RhZG8gYmFqYSBhIDAsMDAsIGVzIGRlY2lyLCBsYSByZWxhY2nDs24gZGVzYXBhcmVjZS4NCi0gKipVbmlkYWRlcyB5IG1hcmdlbjoqKiBDb24gdG9kb3MgbG9zIGRhdG9zIGVzIOKIkjAsMzEgeSBhbCBjb21wYXJhciB2ZW50YXMgcGFyZWNpZGFzIGxsZWdhIGEg4oiSMCw0NS4gQ3VhbmRvIHNlIHZlbmRlbiBtw6FzIHVuaWRhZGVzLCBlbCBtYXJnZW4gdGllbmRlIGEgYmFqYXIuDQotICoqUHJlY2lvIHkgdW5pZGFkZXMsIHkgdW5pZGFkZXMgeSB1dGlsaWRhZDoqKiBDYXNpIG5vIGNhbWJpYW4gZW50cmUgY29sdW1uYXMgKGNlcmNhIGRlIDAsMyB5IGRlIDAsODYsIHJlc3BlY3RpdmFtZW50ZSkuDQotICoqRW50cmUgZXN0YWRvcyoqIChkYXRvIGFwYXJ0ZSBkZSBsYSB0YWJsYSk6IEVsIHByZWNpbyBtZWRpbyB5IGVsIG1hcmdlbiBkZSBsb3MgNTAgZXN0YWRvcyB0aWVuZW4gdW5hIGNvcnJlbGFjacOzbiBkZSDiiJIwLDU3LiBXYXNoaW5ndG9uIHkgSGF3YWlpIHZlbmRlbiBhIFVTJCA2OSwxIHkgVVMkIDY3LDcgcG9yIHVuaWRhZCB5IHRpZW5lbiBtw6FyZ2VuZXMgZGUgMjgsOCUgeSAyOCw1JS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogVmVuZGVyIG3DoXMgY2FybyBubyBlcyBsbyBxdWUgYmFqYSBlbCBtYXJnZW4uIExvIHF1ZSBwYXNhIGVzIHF1ZSBsb3MgZXN0YWRvcyBkb25kZSBzZSB2ZW5kZSBtw6FzIGNhcm8sIGNvbW8gV2FzaGluZ3RvbiB5IEhhd2FpaSwgc29uIGxvcyBkZSBtYXJnZW4gbcOhcyBiYWpvOyBwb3IgZXNvIGxhIHJlbGFjacOzbiBhcGFyZWNlIGN1YW5kbyBzZSBtZXpjbGFuIHRvZG9zIGxvcyBlc3RhZG9zIHkgZGVzYXBhcmVjZSBkZW50cm8gZGUgY2FkYSB1bm8uIFVuYSBwb3NpYmxlIGNhdXNhIGVzIHF1ZSBlbiBlc29zIGVzdGFkb3Mgb3BlcmFyIGN1ZXN0YSBtw6FzLCBwZXJvIHNpbiBkYXRvcyBkZSBjb3N0b3Mgbm8gc2UgcHVlZGUgY29tcHJvYmFyLiBMYSByZWxhY2nDs24gYWx0YSBlbnRyZSB1bmlkYWRlcyB5IHV0aWxpZGFkIGVzIG5vcm1hbCwgcG9ycXVlIGxhIHV0aWxpZGFkIHNhbGUgZGUgbGFzIHZlbnRhcy4NCg0KKlByZWd1bnRhOiBEZW50cm8gZGUgdW4gbWlzbW8gZXN0YWRvLCBwcm9kdWN0byB5IG3DqXRvZG8sIMK/bG9zIHJlZ2lzdHJvcyBjb24gbcOhcyB1bmlkYWRlcyBkZWphbiBtZWpvciBvIHBlb3IgbWFyZ2VuPyoNCg0KUGFyYSBjb21wYXJhciBjYXNvcyBwYXJlY2lkb3MsIG1hcmNhbW9zIGNvbW8gZGUgdm9sdW1lbiBhbHRvIGxvcyByZWdpc3Ryb3MgY29uIG3DoXMgdW5pZGFkZXMgcXVlIGxhIG1lZGlhbmEgZGUgc3UgbWlzbW8gZXN0YWRvLCBwcm9kdWN0byB5IG3DqXRvZG8gZGUgdmVudGEuDQoNCmBgYHtyIHZvbHVtZW59DQpkYXRvc192b2x1bWVuIDwtIGRhdG9zICU+JQ0KICBncm91cF9ieShlc3RhZG8sIHByb2R1Y3RvLCBtZXRvZG9fdmVudGEpICU+JQ0KICBtdXRhdGUodm9sdW1lbiA9IGlmZWxzZSh1bmlkYWRlc192ZW5kaWRhcyA+IG1lZGlhbih1bmlkYWRlc192ZW5kaWRhcyksICJWb2x1bWVuIGFsdG8iLCAiVm9sdW1lbiBiYWpvIG8gbWVkaW8iKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQpyZXN1bWVuX3ZvbHVtZW4gPC0gZGF0b3Nfdm9sdW1lbiAlPiUNCiAgZ3JvdXBfYnkobWV0b2RvX3ZlbnRhLCB2b2x1bWVuKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLA0KICAgICAgICAgICAgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSwNCiAgICAgICAgICAgIHVuaWRhZGVzID0gc3VtKHVuaWRhZGVzX3ZlbmRpZGFzKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIHByZWNpb19tZWRpbyA9IHZlbnRhcyAvIHVuaWRhZGVzKQ0KDQpnZ3Bsb3RseSgNCiAgZ2dwbG90KHJlc3VtZW5fdm9sdW1lbiwgYWVzKHggPSBtZXRvZG9fdmVudGEsIHkgPSBtYXJnZW4sIGZpbGwgPSB2b2x1bWVuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMChtZXRvZG9fdmVudGEsICIgwrcgIiwgdm9sdW1lbiwgIlxuTWFyZ2VuOiAiLCBwY3QobWFyZ2VuKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuUHJlY2lvIG1lZGlvOiBVUyQgIiwgcm91bmQocHJlY2lvX21lZGlvLCAxKSkpKSArDQogICAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiVm9sdW1lbiBhbHRvIiA9ICJuYXZ5IiwgIlZvbHVtZW4gYmFqbyBvIG1lZGlvIiA9ICJsaWdodHN0ZWVsYmx1ZSIpKSArDQogICAgbGFicyh0aXRsZSA9ICJHcsOhZmljYSA5LiBNYXJnZW4gc2Vnw7puIGVsIHZvbHVtZW4gdmVuZGlkbyIsDQogICAgICAgICB4ID0gIk3DqXRvZG8gZGUgdmVudGEiLCB5ID0gIk1hcmdlbiBvcGVyYXRpdm8gYWdyZWdhZG8gKCUpIiwgZmlsbCA9ICIiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lDQogIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB5ID0gLTAuMTUpKQ0KYGBgDQoNCjxwIGNsYXNzPSJmdWVudGUiPkZ1ZW50ZTogZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgRGF0b3NDYXNvMS54bHN4LjwvcD4NCg0KKipFdmlkZW5jaWEuKiogRGVudHJvIGRlIHVuIG1pc21vIGVzdGFkbywgcHJvZHVjdG8geSBtw6l0b2RvLCBsb3MgcmVnaXN0cm9zIE9ubGluZSBkZSBtYXlvciB2b2x1bWVuIGdhbmFuIDQxLDMlLCBmcmVudGUgYSA0OSwzJSBkZWwgcmVzdG8sIGF1bnF1ZSBzdSBwcmVjaW8gcG9yIHVuaWRhZCBlcyBtYXlvciAoVVMkIDQ5LDAgZnJlbnRlIGEgVVMkIDQ1LDEpLiBFbiBPdXRsZXQgbGEgY2HDrWRhIGVzIG1lbm9yICgzNywzJSBmcmVudGUgYSAzOCw5JSkgeSBlbiBJbi1zdG9yZSBubyBoYXkgY2HDrWRhICgzNiwyJSBmcmVudGUgYSAzNSwyJSkuDQoNCioqSW50ZXJwcmV0YWNpw7NuIGRlIGxhIEdyw6FmaWNhIDkuKiogRW4gdW4gbWlzbW8gbWVyY2FkbywgdmVuZGVyIG3DoXMgdW5pZGFkZXMgdmEgYWNvbXBhw7FhZG8gZGUgdW4gbWFyZ2VuIG1lbm9yLCBzb2JyZSB0b2RvIGVuIE9ubGluZS4gQ29tbyBlbCBwcmVjaW8gc3ViZSwgbGEgY2HDrWRhIG5vIHBhcmVjZSB2ZW5pciBkZSBkZXNjdWVudG9zOyBwb2Ryw61hIGRlYmVyc2UgYSBsb3MgY29zdG9zIGRlIGF0ZW5kZXIgcGVkaWRvcyBtw6FzIGdyYW5kZXMgKGVudsOtb3MsIGxvZ8Otc3RpY2EpLCBwZXJvIGVzIHVuYSBoaXDDs3Rlc2lzIHF1ZSBsYSBiYXNlIG5vIHBlcm1pdGUgY29tcHJvYmFyLg0KDQoqKkltcGxpY2FjacOzbiBwYXJhIGVsIG5lZ29jaW8uKiogU2kgT25saW5lIGNyZWNlIHNvbG8gY29uIHZvbHVtZW4sIGVzIHByb2JhYmxlIHF1ZSBzdSBtYXJnZW4gYmFqZTsgcG9yIGVzbyBwcm9wb25lbW9zIGVtcGV6YXIgY29uIHVuYSBwcnVlYmEgcGlsb3RvLiBFc3RhcyBjb3JyZWxhY2lvbmVzIG5vIGJhc3RhbiBwYXJhIGZpamFyIHByZWNpb3M6IHNlIG5lY2VzaXRhbiBsb3MgY29zdG9zIHkgbGEgZmVjaGEgZGUgY2FkYSB2ZW50YS4NCg0KIyMgSGFsbGF6Z28gNS4gUG9jYXMgY29tYmluYWNpb25lcyBnZW5lcmFuIGxhIG1pdGFkIGRlIGxhIHV0aWxpZGFkDQoNCipQcmVndW50YTogwr9MYSB1dGlsaWRhZCBlc3TDoSByZXBhcnRpZGEgZW50cmUgbXVjaGFzIGNvbWJpbmFjaW9uZXMgZGUgY2l1ZGFkLCBwcm9kdWN0byB5IG3DqXRvZG8sIG8gY29uY2VudHJhZGEgZW4gcG9jYXM/Kg0KDQpgYGB7ciBjb25jZW50cmFjaW9ufQ0KY29tYmluYWNpb25lcyA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uLCBjaXVkYWQsIGVzdGFkbywgcHJvZHVjdG8sIG1ldG9kb192ZW50YSkgJT4lDQogIHN1bW1hcmlzZSh1dGlsaWRhZCA9IHN1bSh1dGlsaWRhZF9vcGVyYXRpdmEpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgYXJyYW5nZShkZXNjKHV0aWxpZGFkKSkgJT4lDQogIG11dGF0ZShhY3VtdWxhZG8gPSBjdW1zdW0odXRpbGlkYWQpIC8gc3VtKHV0aWxpZGFkKSAqIDEwMCkNCg0Kbl9taXRhZCA8LSB3aGljaChjb21iaW5hY2lvbmVzJGFjdW11bGFkbyA+PSA1MClbMV0NCg0KIyBSZWdpw7NuIHkgbcOpdG9kbyBxdWUgbcOhcyBhcGFyZWNlbiBlbnRyZSBlc2FzIGNvbWJpbmFjaW9uZXMNCnJlZ2lvbl90b3AgPC0gbmFtZXMoc29ydCh0YWJsZShoZWFkKGNvbWJpbmFjaW9uZXMkcmVnaW9uLCBuX21pdGFkKSksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMV0NCm1ldG9kb190b3AgPC0gbmFtZXMoc29ydCh0YWJsZShoZWFkKGNvbWJpbmFjaW9uZXMkbWV0b2RvX3ZlbnRhLCBuX21pdGFkKSksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMV0NCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBEZSA2OTAgY29tYmluYWNpb25lcyBkZSBjaXVkYWQsIHByb2R1Y3RvIHkgbcOpdG9kbywgMTM0IChlbCAxOSw0JSkgZ2VuZXJhbiBsYSBtaXRhZCBkZSBsYSB1dGlsaWRhZCB5IGVsIDQ5LDElIGRlIGxhcyB2ZW50YXMuIE5vIGVzdMOhbiBjb25jZW50cmFkYXMgZW4gdW4gc29sbyBsdWdhcjogYXBhcmVjZW4gZW4gNDEgZGUgbGFzIDU0IGNpdWRhZGVzIHkgZW4gbGFzIGNpbmNvIHJlZ2lvbmVzICgzOSBlbiBXZXN0LCAzNSBlbiBTb3V0aCwgMjkgZW4gU291dGhlYXN0LCAxNyBlbiBOb3J0aGVhc3QgeSAxNCBlbiBNaWR3ZXN0KS4gUG9yIG3DqXRvZG8sIDUzIHNvbiBPbmxpbmUsIDQ5IE91dGxldCB5IDMyIEluLXN0b3JlLCB5IE1lbidzIFN0cmVldCBGb290d2VhciBlcyBlbCBwcm9kdWN0byBxdWUgbcOhcyBzZSByZXBpdGUgKDQyKS4gU3UgbWFyZ2VuICg0MCwxJSkgZXMgYXBlbmFzIG1heW9yIHF1ZSBlbCBkZWwgcmVzdG8gZGUgY29tYmluYWNpb25lcyAoMzgsNSUpLCBwZXJvIGxhcyAzOSBkZSBXZXN0IGdhbmFuIHNvbG8gMzYsMSUuDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIEVzdGFzIGNvbWJpbmFjaW9uZXMgcGVzYW4gcG9yIHN1IHRhbWHDsW8geSBubyBwb3Igc2VyIG11Y2hvIG3DoXMgcmVudGFibGVzOiB2ZW5kZW4gbXVjaG8gY29uIHVuIG1hcmdlbiBjZXJjYW5vIGFsIHByb21lZGlvLiBXZXN0IGFwb3J0YSBtw6FzIGNvbWJpbmFjaW9uZXMgcXVlIGN1YWxxdWllciBvdHJhIHJlZ2nDs24gYSBlc3RlIGdydXBvLCBwZXJvIGNvbiBlbCBtYXJnZW4gbcOhcyBiYWpvLCBhc8OtIHF1ZSBlcyBkb25kZSB1bmEgbWVqb3JhIGRlIG1hcmdlbiB0ZW5kcsOtYSBtw6FzIGVmZWN0byBlbiBsYSB1dGlsaWRhZCB0b3RhbC4NCg0KKipJbXBsaWNhY2nDs24gcGFyYSBlbCBuZWdvY2lvLioqIEhheSBxdWUgcHJvdGVnZXIgZXN0YXMgMTM0IGNvbWJpbmFjaW9uZXMgKGludmVudGFyaW8sIGRpc3BvbmliaWxpZGFkIHkgc2VydmljaW8gYWwgZGlzdHJpYnVpZG9yKSwgcG9ycXVlIHBlcmRlciBjdWFscXVpZXJhIGRlIGVsbGFzIGFmZWN0YSBsYSBtaXRhZCBkZSBsYSB1dGlsaWRhZC4gRW4gV2VzdCBsYSB0YXJlYSBlcyBkb2JsZTogbWFudGVuZXIgc3Ugdm9sdW1lbiB5IHN1YmlyIHN1IG1hcmdlbiwgZW1wZXphbmRvIHBvciBsYXMgY29tYmluYWNpb25lcyBkZSBDYWxpZm9ybmlhLCBXYXNoaW5ndG9uIHkgSGF3YWlpIHNlw7FhbGFkYXMgZW4gZWwgSGFsbGF6Z28gMi4NCg0KIyAqKlJlY29tZW5kYWNpb25lcyB5IGzDrW1pdGVzKioNCg0KUHJvcG9uZW1vcyBsbGV2YXIgZWwgY3JlY2ltaWVudG8gZGUgbGFzIHZlbnRhcyBhIGxvcyBtZXJjYWRvcyB5IGNhbmFsZXMgZG9uZGUgY2FkYSBkw7NsYXIgZGVqYSBtw6FzIHV0aWxpZGFkLCB5IG1lam9yYXIgcHJpbWVybyBlbCBtYXJnZW4gZGUgbG9zIG1lcmNhZG9zIGdyYW5kZXMgcXVlIGhveSBnYW5hbiBwb2NvLiBMYSBjb21iaW5hY2nDs24gbcOhcyB1cmdlbnRlIGVzIFdvbWVuJ3MgQXBwYXJlbCBlbiBXZXN0LCBzb2JyZSB0b2RvIGVuIENhbGlmb3JuaWE6IGFsbMOtIGdhbmEgMTksNiUgY3VhbmRvIGxvIGVzcGVyYWRvIHNlcsOtYSAzOCw5JSwgeSBkZWphIGRlIGNhcHR1cmFyIHVub3MgVVMkIDMyMiBtaWwgKFVTJCA3MzMgbWlsIGVuIHRvZGEgbGEgcmVnacOzbikuDQoNCiMjIEFjY2lvbmVzIHByaW9yaXphZGFzDQoNCmBgYHtyIGVzY2VuYXJpb3N9DQojIEVzY2VuYXJpbyAxOiAxMCUgbcOhcyBkZSB2ZW50YXMgZW4gZXN0YWRvcyBFc3RyZWxsYSB5IE9wb3J0dW5pZGFkLCBjb24gc3UgbWFyZ2VuIGFjdHVhbA0KY3JlY2VyIDwtIHJlc3VtZW5fZXN0YWRvICU+JSBmaWx0ZXIoZ3J1cG8gJWluJSBjKCJFc3RyZWxsYSIsICJPcG9ydHVuaWRhZCIpKQ0KZXNjZW5hcmlvXzEgPC0gc3VtKGNyZWNlciR2ZW50YXMpICogMC4xMCAqIChzdW0oY3JlY2VyJHV0aWxpZGFkKSAvIHN1bShjcmVjZXIkdmVudGFzKSkNCg0KIyBFc2NlbmFyaW8gMjogMTAlIGRlIGxhcyB2ZW50YXMgSW4tc3RvcmUgeSBPdXRsZXQgZGUgTm9ydGhlYXN0IHBhc2FuIGEgT25saW5lLCBjb24gbG9zIG3DoXJnZW5lcyBhY3R1YWxlcw0KbmUgPC0gcmVnaW9uX21ldG9kbyAlPiUgZmlsdGVyKHJlZ2lvbiA9PSAiTm9ydGhlYXN0IikNCnZlbnRhc19tb3ZpZGFzIDwtIDAuMTAgKiBzdW0obmUkdmVudGFzW25lJG1ldG9kb192ZW50YSAhPSAiT25saW5lIl0pDQptYXJnZW5fb2ZmbGluZSA8LSBzdW0obmUkdXRpbGlkYWRbbmUkbWV0b2RvX3ZlbnRhICE9ICJPbmxpbmUiXSkgLyBzdW0obmUkdmVudGFzW25lJG1ldG9kb192ZW50YSAhPSAiT25saW5lIl0pDQptYXJnZW5fb25saW5lICA8LSBuZSR1dGlsaWRhZFtuZSRtZXRvZG9fdmVudGEgPT0gIk9ubGluZSJdIC8gbmUkdmVudGFzW25lJG1ldG9kb192ZW50YSA9PSAiT25saW5lIl0NCmVzY2VuYXJpb18yIDwtIHZlbnRhc19tb3ZpZGFzICogKG1hcmdlbl9vbmxpbmUgLSBtYXJnZW5fb2ZmbGluZSkNCg0KIyBFc2NlbmFyaW8gMzogY2VycmFyIGxhIG1pdGFkIGRlIGxhIGRpZmVyZW5jaWEgZGUgV2VzdCBmcmVudGUgYWwgbWFyZ2VuIGVzcGVyYWRvDQplc2NlbmFyaW9fMyA8LSAtYnJlY2hhX3JlZ2lvbiRicmVjaGFfdXNkW2JyZWNoYV9yZWdpb24kcmVnaW9uID09ICJXZXN0Il0gLyAyDQoNCiMgRm9ybWF0byBlbiBtaWxsb25lcyBkZSBkw7NsYXJlcyBwYXJhIGxhIHRhYmxhIGRlIGFjY2lvbmVzDQptaWxsb25lcyA8LSBmdW5jdGlvbih4KSBwYXN0ZTAoIlVTJCAiLCBudW0oeCAvIDFlNiwgMiksICIgTSIpDQpgYGANCg0KfCBBY2Npw7NuIHwgRXZpZGVuY2lhIHwgQmVuZWZpY2lvIGVzcGVyYWRvIChlc2NlbmFyaW8gcHJvcHVlc3RvKSB8IFJpZXNnbyB8IMOBcmVhIHJlc3BvbnNhYmxlIHwgSW5kaWNhZG9yIChLUEkpIHwNCnwtLS18LS0tfC0tLXwtLS18LS0tfC0tLXwNCnwgKioxLiBDcmVjZXIgZW4gbG9zIGVzdGFkb3MgRXN0cmVsbGEgeSBkZSBPcG9ydHVuaWRhZCoqLCBjb24gcHJpb3JpZGFkIGVuIFRleGFzLCBBbGFiYW1hLCBUZW5uZXNzZWUsIExvdWlzaWFuYSwgbGFzIENhcm9saW5hcywgTWljaGlnYW4geSBPaGlvIHwgSGFsbGF6Z28gMTogbWFyZ2VuIGRlIDQ0LDYlIChFc3RyZWxsYSkgeSA0Miw2JSAoT3BvcnR1bmlkYWQpLCBmcmVudGUgYSAzNiwxJSBkZSBsb3MgZXN0YWRvcyBhIENvcnJlZ2lyIHwgVW5vcyBgciBtaWxsb25lcyhlc2NlbmFyaW9fMSlgIChgciBwY3QoZXNjZW5hcmlvXzEgLyB1dGlsaWRhZF90b3QgKiAxMDApYCBkZSBsYSB1dGlsaWRhZCkgc2kgc3VzIHZlbnRhcyBjcmVjZW4gMTAlIHwgU3Vwb25lIHF1ZSBlbCBtYXJnZW4gc2UgbWFudGllbmUgYWwgY3JlY2VyLCBwZXJvIGVuIGxvcyByZWdpc3Ryb3MgZGUgbcOhcyB2b2x1bWVuIGVsIG1hcmdlbiBiYWphIChIYWxsYXpnbyA0KSB8IEdlcmVuY2lhIGNvbWVyY2lhbCB5IE1lcmNhZGVvLCBjb24gRmluYW56YXMgfCBWZW50YXMgeSBtYXJnZW4gZGUgY2FkYSBncnVwbyBkZSBlc3RhZG9zIHwNCnwgKioyLiBQcnVlYmEgcGlsb3RvIGRlIE9ubGluZSBlbiBOb3J0aGVhc3QqKiwgeSBsdWVnbyBlbiBXZXN0IHwgSGFsbGF6Z28gMzogT25saW5lIGdhbmEgNDgsNiUgZW4gTm9ydGhlYXN0LCBwZXJvIHBlc2Egc29sbyAxOCw0JSBkZSBzdXMgdmVudGFzIHwgVW5vcyBgciBtaWxsb25lcyhlc2NlbmFyaW9fMilgIChgciBwY3QoZXNjZW5hcmlvXzIgLyB1dGlsaWRhZF90b3QgKiAxMDApYCkgc2kgZWwgMTAlIGRlIGxhcyB2ZW50YXMgSW4tc3RvcmUgeSBPdXRsZXQgZGUgTm9ydGhlYXN0IHBhc2EgYSBPbmxpbmUgfCBFbiBPbmxpbmUgZWwgbWFyZ2VuIGJhamEgOCBwdW50b3MgY3VhbmRvIHN1YmUgZWwgdm9sdW1lbiAoSGFsbGF6Z28gNCksIHBvciBsbyBxdWUgZWwgYmVuZWZpY2lvIHBvZHLDrWEgc2VyIG1lbm9yIHwgQ29tZXJjaW8gZWxlY3Ryw7NuaWNvLCBjb24gRmluYW56YXMgfCBQZXNvIGRlIE9ubGluZSBlbiBsYXMgdmVudGFzIGRlIGNhZGEgcmVnacOzbiB5IG1hcmdlbiBwb3IgY2FuYWwgfA0KfCAqKjMuIENvcnJlZ2lyIGVsIG1hcmdlbiBlbiBDYWxpZm9ybmlhLCBXYXNoaW5ndG9uIHkgSGF3YWlpKiosIGVtcGV6YW5kbyBwb3IgV29tZW4ncyBBcHBhcmVsLCBsb3MgT3V0bGV0IGRlIFNhbiBGcmFuY2lzY28geSBIb25vbHVsdSwgeSBXZXN0IEdlYXIgfCBIYWxsYXpnbyAyOiBXZXN0IGdhbmEgMywyIHB1bnRvcyBtZW5vcyBkZSBsbyBlc3BlcmFkbyAoVVMkIDEsMTYgTSk7IFdvbWVuJ3MgQXBwYXJlbCBnYW5hIDI5LDglIGVuIFdlc3QgeSA1MiwxJSBlbiBTb3V0aCB8IFVub3MgYHIgbWlsbG9uZXMoZXNjZW5hcmlvXzMpYCAoYHIgcGN0KGVzY2VuYXJpb18zIC8gdXRpbGlkYWRfdG90ICogMTAwKWApIHNpIHNlIGNpZXJyYSBsYSBtaXRhZCBkZSBsYSBicmVjaGEgZGUgV2VzdCB8IEHDum4gbm8gc2UgY29ub2NlIGxhIGNhdXNhLiBDb21vIGNpbmNvIGRlIGxvcyBzZWlzIGRpc3RyaWJ1aWRvcmVzIHRpZW5lbiBzdSBtZW5vciBtYXJnZW4gZW4gV2VzdCwgY2FtYmlhciBkZSBkaXN0cmlidWlkb3Igbm8gcGFyZWNlIGxhIHNvbHVjacOzbiB8IEZpbmFuemFzIGNvbWVyY2lhbCwgY29uIGxhIGdlcmVuY2lhIGRlIFdlc3QgfCBNYXJnZW4gZGUgV2VzdCAoaG95IDM1LDclKSB5IGRlIFdvbWVuJ3MgQXBwYXJlbCBlbiBXZXN0IChob3kgMjksOCUpIHwNCg0KVGFibGU6IFRhYmxhIDEzLiBBY2Npb25lcyBwcmlvcml6YWRhcyB5IGJlbmVmaWNpbyBlc3BlcmFkbw0KDQo8cCBjbGFzcz0iZnVlbnRlIj5GdWVudGU6IGVsYWJvcmFjacOzbiBwcm9waWEgY29uIGJhc2UgZW4gbG9zIGhhbGxhemdvcyBkZWwgaW5mb3JtZS48L3A+DQoNCkxvcyBiZW5lZmljaW9zIHNvbiBlc2NlbmFyaW9zIGNhbGN1bGFkb3MgY29uIGxvcyBtw6FyZ2VuZXMgYWN0dWFsZXMgKGVsIGPDoWxjdWxvIGVzdMOhIGVuIGVsIGPDs2RpZ28gZGUgZXN0YSBzZWNjacOzbikuIFNpcnZlbiBwYXJhIGRpbWVuc2lvbmFyIGxhcyBhY2Npb25lczsgbm8gc29uIG1ldGFzLg0KDQoNCiMjIENvbmRpY2lvbmVzIGRlIGFwbGljYWNpw7NuDQoNCkxhcyB0cmVzIGFjY2lvbmVzIGRlYmVuIGVtcGV6YXIgY29tbyBwaWxvdG9zLCBjb24gc3UgaW5kaWNhZG9yIGRlIHNlZ3VpbWllbnRvIGRlZmluaWRvIGRlc2RlIGVsIGluaWNpby4gTGEgQWNjacOzbiAxIHB1ZWRlIGFycmFuY2FyIGRlIGlubWVkaWF0by4gTGFzIEFjY2lvbmVzIDIgeSAzIHJlcXVpZXJlbiBxdWUgbG9zIGRpc3RyaWJ1aWRvcmVzIGVudHJlZ3VlbiBjb3N0b3MsIGRlc2N1ZW50b3MgeSBmZWNoYSBkZSBjYWRhIHZlbnRhOyBzaW4gZXNvcyBkYXRvcyBubyBzZSBwdWVkZSBzYWJlciBzaSBlbCBtYXJnZW4gYmFqbyBzZSBkZWJlIGFsIHByZWNpbywgYWwgY29zdG8gbyBhbCB0aXBvIGRlIHRpZW5kYS4NCg0KRG9zIHNpdHVhY2lvbmVzIGNhbWJpYXLDrWFuIGxhIHJlY29tZW5kYWNpw7NuLiBTaSBsb3MgY29zdG9zIG11ZXN0cmFuIHF1ZSBlbCBtYXJnZW4gYmFqbyBkZSBXZXN0IHZpZW5lIGRlIGdhc3RvcyBkaWbDrWNpbGVzIGRlIHJlZHVjaXIgKGFycmllbmRvcyBvIHRyYW5zcG9ydGUgYSBIYXdhaWksIHBvciBlamVtcGxvKSwgbGEgQWNjacOzbiAzIGRlYmVyw61hIGVuZm9jYXJzZSBlbiBhanVzdGFyIHByZWNpb3MgbyBjb2JlcnR1cmEuIFkgc2kgbGEgZmVjaGEgbXVlc3RyYSBxdWUgbGFzIHZlbnRhcyBkZSBsb3MgZXN0YWRvcyBFc3RyZWxsYSB2aWVuZW4gY2F5ZW5kbywgaGFicsOtYSBxdWUgcmV2aXNhciBsYSBwcmlvcmlkYWQgZGUgbGEgQWNjacOzbiAxLg0KDQojIyBMaW1pdGFjaW9uZXMNCg0KLSAqKlNpbiBmZWNoYToqKiBObyBwdWRpbW9zIG1lZGlyIGNyZWNpbWllbnRvIG5pIHRlbmRlbmNpYXMsIG5pIGlkZW50aWZpY2FyIHBlcmlvZG9zIGRlIGV4cGFuc2nDs24gbyBjb250cmFjY2nDs24uIEVsIGFuw6FsaXNpcyBlcyBkZSBjb3J0ZSB0cmFuc3ZlcnNhbC4NCi0gKipTaW4gY29zdG9zIG5pIGRlc2N1ZW50b3M6KiogTGFzIGNhdXNhcyBxdWUgcHJvcG9uZW1vcyBwYXJhIGVsIG1hcmdlbiBiYWpvIHNvbiBoaXDDs3Rlc2lzIHBvciBjb21wcm9iYXIuDQotICoqUGVyaW9kbyB5IG1lcmNhZG86KiogTGFzIGNvbmNsdXNpb25lcyBhcGxpY2FuIHNvbG8gYSBFc3RhZG9zIFVuaWRvcyB5IGFsIHBlcmlvZG8gcXVlIGN1YnJlIGxhIGJhc2UuDQotICoqQ29ycmVsYWNpw7NuIG5vIGVzIGNhdXNhbGlkYWQ6KiogTGFzIGNvcnJlbGFjaW9uZXMgeSBlbCBtYXJnZW4gZXNwZXJhZG8gZGVzY3JpYmVuIGxvIHF1ZSBvY3VycmUsIHBlcm8gbm8gcHJ1ZWJhbiBsYSBjYXVzYS4gRWwgbWFyZ2VuIGVzcGVyYWRvIGVzIHVuYSByZWZlcmVuY2lhLCBubyB1bmEgbWV0YS4NCi0gKipFc2NlbmFyaW9zOioqIFN1cG9uZW4gbcOhcmdlbmVzIGNvbnN0YW50ZXMgeSBzaXJ2ZW4gc29sbyBwYXJhIGRpbWVuc2lvbmFyIGxhcyBhY2Npb25lcy4NCg0KIyAqKlNvcG9ydGUgcmVwcm9kdWNpYmxlKioNCg0KLSAqKkZ1ZW50ZSBkZSBsb3MgZGF0b3M6KiogRGF0b3NDYXNvMS54bHN4IChob2phIERhdGEgU2FsZXMgQWRpZGFzKSwgc3VtaW5pc3RyYWRhIHBvciBsYSBkb2NlbnRlIGRlbCBjdXJzbyBBbmFsw610aWNhIGRlIE5lZ29jaW9zLCBFc3BlY2lhbGl6YWNpw7NuIGVuIEZpbmFuemFzLCBQb250aWZpY2lhIFVuaXZlcnNpZGFkIEphdmVyaWFuYSBDYWxpLg0KLSAqKkZ1ZW50ZXMgZGUgbGFzIHRhYmxhcyB5IGdyw6FmaWNhczoqKiBUb2RhcyBzb24gZGUgZWxhYm9yYWNpw7NuIHByb3BpYSBjb24gZGF0b3MgZGUgRGF0b3NDYXNvMS54bHN4Lg0KLSAqKkPDs21vIHJlcHJvZHVjaXI6KiogR3VhcmRhciBlbCBhcmNoaXZvIGRlIEV4Y2VsIGVuIGxhIG1pc21hIGNhcnBldGEgZGUgZXN0ZSAuUm1kIHkgZWplY3V0YXIgKktuaXQqLiBFbCBjw7NkaWdvIGRlIGNhZGEgdGFibGEgeSBncsOhZmljYSBzZSBwdWVkZSB2ZXIgY29uIGVsIGJvdMOzbiAqKlNob3cqKiBkZSBjYWRhIGJsb3F1ZSwgeSBlbCAuUm1kIHNlIHB1ZWRlIGRlc2NhcmdhciBjb24gZWwgYm90w7NuICoqQ29kZSoqIGRlIGFycmliYS4NCi0gKipVc28gZGUgaW50ZWxpZ2VuY2lhIGFydGlmaWNpYWw6KiogVXRpbGl6YW1vcyBDbGF1ZGUgKEFudGhyb3BpYykgY29tbyBhcG95byBwYXJhIG9yZ2FuaXphciBlbCBhbsOhbGlzaXMsIGVsYWJvcmFyIGxhcyBncsOhZmljYXMgeSB0YWJsYXMgZW4gUiB5IHJldmlzYXIgbGEgcmVkYWNjacOzbi4gTGFzIGNpZnJhcywgbGFzIGludGVycHJldGFjaW9uZXMgeSBsYXMgcmVjb21lbmRhY2lvbmVzIGZ1ZXJvbiByZXZpc2FkYXMgcG9yIGxvcyBhdXRvcmVzLg0KDQpgYGB7ciBwYXF1ZXRlc30NCnBhcXVldGVzIDwtIGMoInJlYWR4bCIsICJkcGx5ciIsICJ0aWR5ciIsICJnZ3Bsb3QyIiwgInBsb3RseSIsICJrbml0ciIsICJrYWJsZUV4dHJhIiwgInJtYXJrZG93biIpDQp2ZXJzaW9uZXMgPC0gc2FwcGx5KHBhcXVldGVzLCBmdW5jdGlvbihwKSBhcy5jaGFyYWN0ZXIocGFja2FnZVZlcnNpb24ocCkpKQ0KYGBgDQoNCkVsIGluZm9ybWUgc2UgZ2VuZXLDsyBjb24gUiBgciBnZXRSdmVyc2lvbigpYCB5IGxvcyBwYXF1ZXRlcyBgciBwYXN0ZShwYXN0ZShwYXF1ZXRlcywgdmVyc2lvbmVzKSwgY29sbGFwc2UgPSAiLCAiKWAuDQoNCiMjIFJlZmVyZW5jaWFzDQoNCi0gQW50aHJvcGljLiAocy4gZi4pLiAqQ2xhdWRlKiBbTW9kZWxvIGRlIGxlbmd1YWplIGRlIGludGVsaWdlbmNpYSBhcnRpZmljaWFsXS4gaHR0cHM6Ly9jbGF1ZGUuYWkNCi0gUG9zaXQgU29mdHdhcmUuIChzLiBmLikuICpSU3R1ZGlvOiBJbnRlZ3JhdGVkIGRldmVsb3BtZW50IGVudmlyb25tZW50IGZvciBSKi4gaHR0cHM6Ly9wb3NpdC5jbw0KLSBSIENvcmUgVGVhbS4gKHMuIGYuKS4gKlI6IEEgbGFuZ3VhZ2UgYW5kIGVudmlyb25tZW50IGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcqICh2ZXJzacOzbiBgciBnZXRSdmVyc2lvbigpYCkuIFIgRm91bmRhdGlvbiBmb3IgU3RhdGlzdGljYWwgQ29tcHV0aW5nLiBodHRwczovL3d3dy5yLXByb2plY3Qub3JnDQotIFN0ZWZlbnMgQ2Vyw7NuLCBKLiAocy4gZi4pLiAqQ2FzbyAxOiBBbmFsw610aWNhIGRlc2NyaXB0aXZhIHkgZGlhZ27Ds3N0aWNhKiBbSW5zdHJ1Y2Npb25lcyBkZWwgY2FzbyB5IGJhc2UgZGUgZGF0b3MgRGF0b3NDYXNvMS54bHN4XS4gQW5hbMOtdGljYSBkZSBOZWdvY2lvcywgRXNwZWNpYWxpemFjacOzbiBlbiBGaW5hbnphcywgUG9udGlmaWNpYSBVbml2ZXJzaWRhZCBKYXZlcmlhbmEgQ2FsaS4NCg==