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:
- ¿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%).
- ¿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.
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==