Análisis exploratorio
Unidades, segmentos, periodo y denominadores
- Unidades: las ventas y la utilidad están en dólares
(US$), las cantidades en unidades vendidas y el margen en
porcentaje.
- Segmentos: 5 regiones, 50 estados, 54 ciudades, 6
distribuidores, 6 productos y 3 métodos de venta (In-store, Online y
Outlet).
- Periodo: la base no trae fecha. Por su estructura
parece mensual, pero no sabemos qué meses cubre, así que los resultados
corresponden al total acumulado de la base.
- Denominadores: todas las participaciones (%) se
calculan sobre el total de la base: US$ 120,2 millones en ventas,
2.478.861 unidades o US$ 47,2 millones de utilidad. Cuando un porcentaje
usa otro denominador (por ejemplo, el peso de un método dentro de una
región), se indica en el texto.
Indicadores y por qué los usamos.
- Margen operativo agregado (utilidad total / ventas
totales × 100), para medir la rentabilidad real de cada segmento. Da
39,3%; el promedio simple de los márgenes por registro (42,3%)
sobrestima la rentabilidad porque los registros pequeños pesarían lo
mismo que los grandes, así que no lo usamos.
- Participación (%) en ventas, unidades y utilidad,
para ver qué segmentos concentran el negocio.
- Mediana, desviación estándar y coeficiente de
variación, porque las ventas tienen valores muy altos que
inflan el promedio y queremos medir la dispersión.
- Utilidad por registro, para comparar el tamaño de
la utilidad en dólares con el margen en porcentaje.
- Correlación de Pearson (y de Spearman entre
estados), para describir relaciones entre variables, sin asumir que una
causa la otra.
- Margen esperado por la mezcla de producto y método,
para saber si una región gana menos por lo que vende o por cómo le va
con lo que vende.
Indicadores generales
ventas_tot <- sum(datos$ventas_total)
utilidad_tot <- sum(datos$utilidad_operativa)
unidades_tot <- sum(datos$unidades_vendidas)
margen_tot <- utilidad_tot / ventas_tot * 100
margen_simple <- mean(datos$margen_operativo) * 100
tabla_kpi <- data.frame(
Indicador = c("Registros", "Ventas totales", "Unidades vendidas", "Utilidad operativa total",
"Margen operativo agregado (utilidad / ventas)", "Promedio simple de los márgenes (no se usa)",
"Precio promedio por unidad (ventas / unidades)"),
Valor = c(num(nrow(datos)), usd(ventas_tot), num(unidades_tot), usd(utilidad_tot),
pct(margen_tot), pct(margen_simple), paste0("US$ ", num(ventas_tot / unidades_tot, 2))))
kable(tabla_kpi, caption = "Tabla 1. Indicadores generales", align = c("l", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 1. Indicadores generales
|
Indicador
|
Valor
|
|
Registros
|
9.644
|
|
Ventas totales
|
US$ 120.166.650
|
|
Unidades vendidas
|
2.478.861
|
|
Utilidad operativa total
|
US$ 47.224.968
|
|
Margen operativo agregado (utilidad / ventas)
|
39,3%
|
|
Promedio simple de los márgenes (no se usa)
|
42,3%
|
|
Precio promedio por unidad (ventas / unidades)
|
US$ 48,48
|
Lectura de la Tabla 1. Adidas vendió US$ 120,2
millones y 2.478.861 unidades, con una utilidad operativa de US$ 47,2
millones y un margen agregado de 39,3%. El precio promedio fue de US$
48,48 por unidad.
Tendencia central y dispersión
Pregunta: ¿cómo es un registro típico y qué tanto varían las
variables?
variables <- c("precio_unidad", "unidades_vendidas", "ventas_total", "utilidad_operativa", "margen_operativo")
descriptivas <- data.frame(
Variable = c("Precio por unidad (US$)", "Unidades vendidas", "Ventas por registro (US$)",
"Utilidad por registro (US$)", "Margen por registro (%)"),
Media = sapply(datos[variables], mean),
Mediana = sapply(datos[variables], median),
`Desv. estándar` = sapply(datos[variables], sd),
`Mínimo` = sapply(datos[variables], min),
`Máximo` = sapply(datos[variables], max),
check.names = FALSE, row.names = NULL)
# El margen se muestra en porcentaje
descriptivas[5, 2:6] <- descriptivas[5, 2:6] * 100
descriptivas$`Coef. de variación` <- descriptivas$`Desv. estándar` / descriptivas$Media
descriptivas %>%
mutate(across(Media:`Máximo`, ~ num(.x, 1)), `Coef. de variación` = num(`Coef. de variación`, 2)) %>%
kable(caption = "Tabla 2. Medidas de tendencia central y dispersión por registro", align = c("l", rep("r", 6))) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 2. Medidas de tendencia central y dispersión por registro
|
Variable
|
Media
|
Mediana
|
Desv. estándar
|
Mínimo
|
Máximo
|
Coef. de variación
|
|
Precio por unidad (US$)
|
45,2
|
45,0
|
14,7
|
7,0
|
110,0
|
0,33
|
|
Unidades vendidas
|
257,0
|
176,0
|
214,2
|
6,0
|
1.275,0
|
0,83
|
|
Ventas por registro (US$)
|
12.460,2
|
7.812,0
|
12.716,5
|
160,0
|
82.500,0
|
1,02
|
|
Utilidad por registro (US$)
|
4.896,8
|
3.264,0
|
4.866,5
|
75,2
|
39.000,0
|
0,99
|
|
Margen por registro (%)
|
42,3
|
41,0
|
9,7
|
10,0
|
80,0
|
0,23
|
Lectura de la Tabla 2. Un registro típico (la
mediana) tiene 176 unidades, US$ 7.812 en ventas y US$ 3.264 de
utilidad. El promedio de las ventas (US$ 12.460) está muy por encima de
la mediana y su coeficiente de variación es 1,02, lo que indica que hay
registros muy grandes que jalan el promedio hacia arriba. El margen, en
cambio, varía poco (coeficiente de 0,23).
Distribución y valores atípicos
Pregunta: ¿las ventas por registro son parecidas entre sí o hay
unos pocos registros muy grandes?
ggplotly(
ggplot(datos, aes(x = ventas_total)) +
geom_histogram(binwidth = 5000, boundary = 0, fill = "steelblue", color = "white") +
geom_vline(xintercept = median(datos$ventas_total), linetype = "dashed", color = "grey20") +
geom_vline(xintercept = mean(datos$ventas_total), linetype = "dotted", color = "grey20") +
geom_vline(xintercept = limite_ventas, color = "grey50") +
labs(title = "Figura 1. Distribución de las ventas por registro",
x = "Ventas por registro (US$)", y = "Número de registros",
caption = "Línea discontinua: mediana. Línea punteada: media. Línea continua: límite de atípicos.") +
tema
)
Lectura de la Figura 1. La mayoría de los registros
se concentra en valores bajos y hay una cola larga hacia la derecha
(asimetría positiva): la mayoría de registros vende menos de US$ 15 mil,
pero hay 799 registros (8,3%) por encima de US$ 33.578 que suman el
30,4% de las ventas. La línea discontinua es la mediana, la punteada es
la media y la continua es el límite a partir del cual un registro se
considera atípico. Son ventas reales, así que las conservamos, y por eso
preferimos la mediana para describir un registro típico y los totales
para comparar segmentos.
Participación por segmentos
Pregunta: ¿qué segmentos concentran las ventas, las unidades y la
utilidad?
resumen_region <- datos %>%
group_by(region) %>%
summarise(ventas = sum(ventas_total),
unidades = sum(unidades_vendidas),
utilidad = sum(utilidad_operativa)) %>%
mutate(margen = utilidad / ventas * 100,
part_ventas = ventas / sum(ventas) * 100,
part_unidades = unidades / sum(unidades) * 100,
part_utilidad = utilidad / sum(utilidad) * 100) %>%
arrange(desc(ventas))
grafico_region <- resumen_region %>%
select(region, part_ventas, part_utilidad, margen) %>%
pivot_longer(c(part_ventas, part_utilidad), names_to = "medida", values_to = "valor") %>%
mutate(medida = factor(ifelse(medida == "part_ventas", "% de las ventas", "% de la utilidad"),
levels = c("% de las ventas", "% de la utilidad")),
region = factor(region, levels = resumen_region$region))
ggplotly(
ggplot(grafico_region, aes(x = region, y = valor, fill = medida,
text = paste0(region, "\n", medida, ": ", pct(valor), "\nMargen: ", pct(margen)))) +
geom_col(position = "dodge") +
scale_fill_manual(values = c("% de las ventas" = "steelblue", "% de la utilidad" = "lightsteelblue")) +
labs(title = "Figura 2. Participación por región en ventas y utilidad (%)",
x = "Región", y = "% del total", fill = "") +
tema,
tooltip = "text")
Lectura de la Figura 2. Cada serie (ventas y
utilidad) suma 100%. West es la región que más vende (30,3%), pero su
barra de utilidad es más baja (27,6%). En South pasa lo contrario: tiene
el 17,1% de las ventas y el 19,5% de la utilidad.
resumen_region %>%
transmute(`Región` = region, Ventas = usd(ventas), `% ventas` = pct(part_ventas),
`% unidades` = pct(part_unidades), `% utilidad` = pct(part_utilidad), Margen = pct(margen)) %>%
bind_rows(data.frame(`Región` = "Total", Ventas = usd(ventas_tot),
`% ventas` = pct(sum(resumen_region$part_ventas)),
`% unidades` = pct(sum(resumen_region$part_unidades)),
`% utilidad` = pct(sum(resumen_region$part_utilidad)),
Margen = pct(margen_tot), check.names = FALSE)) %>%
kable(caption = "Tabla 3. Ventas, unidades, utilidad y margen por región", align = c("l", rep("r", 5))) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
row_spec(6, bold = TRUE)
Tabla 3. Ventas, unidades, utilidad y margen por región
|
Región
|
Ventas
|
% ventas
|
% unidades
|
% utilidad
|
Margen
|
|
West
|
US$ 36.436.157
|
30,3%
|
27,7%
|
27,6%
|
35,7%
|
|
Northeast
|
US$ 25.078.267
|
20,9%
|
20,2%
|
20,6%
|
38,8%
|
|
Southeast
|
US$ 21.374.436
|
17,8%
|
16,4%
|
17,8%
|
39,3%
|
|
South
|
US$ 20.603.356
|
17,1%
|
19,9%
|
19,5%
|
44,8%
|
|
Midwest
|
US$ 16.674.434
|
13,9%
|
15,8%
|
14,5%
|
41,1%
|
|
Total
|
US$ 120.166.650
|
100,0%
|
100,0%
|
100,0%
|
39,3%
|
Lectura de la Tabla 3. La fila de total confirma que
las participaciones suman 100%. West tiene el menor margen (35,7%) y
South el mayor (44,8%). South pesa más en unidades (19,9%) que en ventas
(17,1%) porque vende a precios más bajos.
participacion <- function(base, columna, nombre) {
tabla <- base %>%
group_by(categoria = .data[[columna]]) %>%
summarise(ventas = sum(ventas_total), unidades = sum(unidades_vendidas), utilidad = sum(utilidad_operativa)) %>%
arrange(desc(ventas))
total <- data.frame(categoria = "Total", ventas = sum(tabla$ventas),
unidades = sum(tabla$unidades), utilidad = sum(tabla$utilidad))
bind_rows(tabla, total) %>%
mutate(Dimensión = nombre,
`% ventas` = pct(ventas / sum(tabla$ventas) * 100),
`% unidades` = pct(unidades / sum(tabla$unidades) * 100),
`% utilidad` = pct(utilidad / sum(tabla$utilidad) * 100),
Margen = pct(utilidad / ventas * 100))
}
bind_rows(participacion(datos, "producto", "Producto"),
participacion(datos, "distribuidor", "Distribuidor")) %>%
select(Dimensión, Categoría = categoria, `% ventas`, `% unidades`, `% utilidad`, Margen) %>%
kable(caption = "Tabla 4. Participación por producto y por distribuidor", align = c("l", "l", rep("r", 4))) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
row_spec(c(7, 14), bold = TRUE) %>%
collapse_rows(columns = 1, valign = "top")
Tabla 4. Participación por producto y por distribuidor
|
Dimensión
|
Categoría
|
% ventas
|
% unidades
|
% utilidad
|
Margen
|
|
Producto
|
Men’s Street Footwear
|
23,0%
|
23,9%
|
24,6%
|
42,0%
|
|
Women’s Apparel
|
19,9%
|
17,5%
|
20,5%
|
40,6%
|
|
Men’s Athletic Footwear
|
17,1%
|
17,6%
|
15,7%
|
36,1%
|
|
Women’s Street Footwear
|
14,3%
|
15,8%
|
13,8%
|
37,8%
|
|
Men’s Apparel
|
13,7%
|
12,4%
|
13,5%
|
38,6%
|
|
Women’s Athletic Footwear
|
11,9%
|
12,8%
|
11,9%
|
39,1%
|
|
Total
|
100,0%
|
100,0%
|
100,0%
|
39,3%
|
|
Distribuidor
|
West Gear
|
27,0%
|
25,2%
|
25,8%
|
37,6%
|
|
Foot Locker
|
24,2%
|
24,4%
|
24,0%
|
39,0%
|
|
Sports Direct
|
20,5%
|
22,5%
|
22,5%
|
43,2%
|
|
Kohl’s
|
11,2%
|
11,6%
|
11,0%
|
38,4%
|
|
Walmart
|
8,7%
|
8,3%
|
8,3%
|
37,1%
|
|
Amazon
|
8,4%
|
8,0%
|
8,4%
|
39,5%
|
|
Total
|
100,0%
|
100,0%
|
100,0%
|
39,3%
|
Lectura de la Tabla 4. Men’s Street Footwear es el
producto que más vende (23,0%) y también el de mejor margen (42,0%).
Men’s Athletic Footwear es el tercero en ventas, pero tiene el margen
más bajo (36,1%). Entre distribuidores, West Gear y Foot Locker suman
más de la mitad de las ventas (51,2%), y Sports Direct es el que mejor
margen tiene (43,2%).
Métodos de venta y variabilidad
Pregunta: ¿qué método de venta deja mejor margen, es igual en
todas las regiones y dónde varía más el margen?
region_metodo <- datos %>%
group_by(region, metodo_venta) %>%
summarise(ventas = sum(ventas_total),
utilidad = sum(utilidad_operativa), .groups = "drop") %>%
group_by(region) %>%
mutate(margen = utilidad / ventas * 100,
mezcla = ventas / sum(ventas) * 100) %>%
ungroup()
ggplotly(
ggplot(region_metodo, aes(x = metodo_venta, y = region, fill = margen,
text = paste0(region, " · ", metodo_venta, "\nMargen: ", pct(margen),
"\nPeso en las ventas de la región: ", pct(mezcla)))) +
geom_tile(color = "white") +
geom_text(aes(label = paste0(pct(margen), "\n(", pct(mezcla), " de ventas)")), size = 3) +
scale_fill_gradient(low = "#f2f2f2", high = "steelblue") +
labs(title = "Figura 3. Margen por región y método de venta (%)",
x = "Método de venta", y = "Región", fill = "Margen (%)") +
tema,
tooltip = "text")
Lectura de la Figura 3. Cada celda muestra el margen
y, entre paréntesis, el peso de ese método en las ventas de la región;
en cada región los tres pesos suman 100%. Entre más oscuro el azul,
mayor el margen. La columna Online es la más oscura en todas las
regiones, y la celda con mejor margen es Online en Northeast (48,6%),
aunque allí solo pesa el 18,4% de las ventas. El Outlet gana 44,6% en
South y 32,8% en West.
resumen_metodo <- datos %>%
group_by(metodo_venta) %>%
summarise(registros = n(),
ventas = sum(ventas_total),
unidades = sum(unidades_vendidas),
utilidad = sum(utilidad_operativa)) %>%
mutate(margen = utilidad / ventas * 100,
part_ventas = ventas / sum(ventas) * 100,
part_unidades = unidades / sum(unidades) * 100,
utilidad_registro = utilidad / registros) %>%
arrange(desc(margen))
resumen_metodo %>%
transmute(`Método` = metodo_venta, `% ventas` = pct(part_ventas), `% unidades` = pct(part_unidades),
Margen = pct(margen), `Utilidad promedio por registro` = usd(utilidad_registro)) %>%
bind_rows(data.frame(`Método` = "Total", `% ventas` = pct(sum(resumen_metodo$part_ventas)),
`% unidades` = pct(sum(resumen_metodo$part_unidades)), Margen = pct(margen_tot),
`Utilidad promedio por registro` = usd(utilidad_tot / nrow(datos)), check.names = FALSE)) %>%
kable(caption = "Tabla 5. Comparación de los métodos de venta", align = c("l", rep("r", 4))) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
row_spec(4, bold = TRUE)
Tabla 5. Comparación de los métodos de venta
|
Método
|
% ventas
|
% unidades
|
Margen
|
Utilidad promedio por registro
|
|
Online
|
37,4%
|
37,9%
|
43,5%
|
US$ 4.001
|
|
Outlet
|
32,9%
|
34,3%
|
37,7%
|
US$ 4.943
|
|
In-store
|
29,7%
|
27,8%
|
35,8%
|
US$ 7.333
|
|
Total
|
100,0%
|
100,0%
|
39,3%
|
US$ 4.897
|
# ¿En cuántos estados es cada método el de mayor margen?
datos %>%
group_by(estado, metodo_venta) %>%
summarise(margen = sum(utilidad_operativa) / sum(ventas_total), .groups = "drop") %>%
group_by(estado) %>%
slice_max(margen, n = 1) %>%
ungroup() %>%
count(metodo_venta, name = "estados") %>%
rename(`Método con mayor margen` = metodo_venta, Estados = estados) %>%
kable(caption = "Número de estados según el método con mayor margen", align = c("l", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Número de estados según el método con mayor margen
|
Método con mayor margen
|
Estados
|
|
Online
|
42
|
|
Outlet
|
8
|
Lectura de la Tabla 5. Online tiene el mejor margen
(43,5%) y es el método más rentable en 42 de los 50 estados; en los
otros 8 gana Outlet. In-store tiene el margen más bajo (35,8%), pero la
mayor utilidad por registro (US$ 7.333) porque sus ventas son más
grandes: al comparar utilidad en dólares y margen, el orden se
invierte.
Pregunta: ¿la diferencia entre métodos se ve también registro a
registro, y en qué región varía más el margen?
ggplotly(
ggplot(datos, aes(x = metodo_venta, y = margen_operativo * 100)) +
geom_boxplot(fill = "lightsteelblue", outlier.colour = "grey40", outlier.size = 1) +
facet_wrap(~region, nrow = 1) +
labs(title = "Figura 4. Margen por registro según método y región",
x = NULL, y = "Margen operativo (%)") +
tema +
theme(legend.position = "none", axis.text.x = element_text(angle = 45, hjust = 1))
)
Lectura de la Figura 4. Cada caja resume el margen
de los registros de un método en una región, y la línea del medio es la
mediana. Online tiene la mediana más alta en las cinco regiones (entre
44% y 49%), e In-store la más baja (35%, salvo en South con 40%).
# Variabilidad del margen por región
datos %>%
group_by(region) %>%
summarise(mediana = median(margen_operativo) * 100,
desviacion = sd(margen_operativo) * 100) %>%
arrange(desc(desviacion)) %>%
transmute(`Región` = region, `Mediana del margen (%)` = num(mediana, 1), `Desviación estándar (puntos)` = num(desviacion, 1)) %>%
kable(caption = "Tabla 6. Variabilidad del margen por registro en cada región", align = c("l", "r", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 6. Variabilidad del margen por registro en cada región
|
Región
|
Mediana del margen (%)
|
Desviación estándar (puntos)
|
|
West
|
40,0
|
10,7
|
|
Midwest
|
43,0
|
9,2
|
|
Southeast
|
40,0
|
9,2
|
|
South
|
45,0
|
9,1
|
|
Northeast
|
40,0
|
8,4
|
Lectura de la Tabla 6. West es la región donde más
varía el margen entre registros (desviación de 10,7 puntos): allí
conviven ventas muy rentables con otras muy poco rentables. Northeast es
la más estable (8,4 puntos).
Diagnóstico y hallazgos
Presentamos cinco hallazgos; los dos primeros son los que más
profundizamos. Debajo de cada tabla o gráfico está la evidencia
cuantitativa y su interpretación, y al final de cada hallazgo, la
implicación para el negocio.
Hallazgo 1 (profundización). Vender más no siempre es ganar más
Pregunta: ¿qué estados y ciudades combinan muchas ventas con buen
margen, y cuáles venden mucho pero ganan poco?
Para clasificar los 50 estados nos hacemos dos preguntas
sencillas:
- ¿El estado vende mucho o poco? Si los 50 estados
vendieran lo mismo, cada uno tendría el 2% de las ventas (100% / 50),
unos US$ 2,4 millones. Un estado que vende más del 2% es un mercado
grande, como California (7,1%); uno que vende menos del 2% es un mercado
pequeño, como Iowa (0,8%).
- ¿El estado gana mucho o poco por cada dólar
vendido? La referencia es el margen de toda la empresa, 39,3%.
Un estado con margen mayor gana más que el promedio, como Alabama
(54,4%); uno con margen menor gana menos, como Washington (28,8%).
Al cruzar las dos respuestas quedan cuatro grupos:
| Vende mucho (más del 2% de las ventas) |
Estrella. Ejemplo: Texas, 5,5% de las ventas y
42,8% de margen |
Corregir. Ejemplo: California, 7,1% de las ventas y
34,5% de margen |
| Vende poco (menos del 2% de las ventas) |
Oportunidad. Ejemplo: Michigan, 1,9% de las ventas
y 45,9% de margen |
Bajo aporte. Ejemplo: Iowa, 0,8% de las ventas y
38,0% de margen |
resumen_estado <- datos %>%
group_by(region, estado) %>%
summarise(ventas = sum(ventas_total),
utilidad = sum(utilidad_operativa),
unidades = sum(unidades_vendidas), .groups = "drop") %>%
mutate(margen = utilidad / ventas * 100,
part_ventas = ventas / sum(ventas) * 100,
precio_medio = ventas / unidades,
grupo = case_when(part_ventas >= 2 & margen >= margen_tot ~ "Estrella",
part_ventas < 2 & margen >= margen_tot ~ "Oportunidad",
part_ventas >= 2 & margen < margen_tot ~ "Corregir",
TRUE ~ "Bajo aporte"),
grupo = factor(grupo, levels = c("Estrella", "Oportunidad", "Corregir", "Bajo aporte")))
colores_grupo <- c("Estrella" = "navy", "Oportunidad" = "deepskyblue3",
"Corregir" = "grey30", "Bajo aporte" = "grey70")
# Estados que se nombran en el gráfico
etiquetas <- c("California", "New York", "Florida", "Texas", "Alabama", "Tennessee",
"Washington", "Michigan", "Idaho", "Louisiana")
ggplotly(
ggplot(resumen_estado, aes(x = part_ventas, y = margen, color = grupo,
text = paste0(estado, " (", region, ")",
"\nVentas: US$ ", round(ventas / 1e6, 2), " M (", pct(part_ventas), " del total)",
"\nUtilidad: US$ ", round(utilidad / 1e6, 2), " M",
"\nMargen: ", pct(margen)))) +
geom_hline(yintercept = margen_tot, linetype = "dashed", color = "grey40") +
geom_vline(xintercept = 2, linetype = "dashed", color = "grey40") +
geom_point(aes(size = utilidad / 1e6), alpha = 0.75) +
geom_text(data = filter(resumen_estado, estado %in% etiquetas), aes(label = estado),
nudge_y = 1.3, size = 3, show.legend = FALSE) +
geom_text(data = filter(resumen_estado, estado == "Hawaii"), aes(label = estado),
nudge_y = -1.3, size = 3, show.legend = FALSE) +
scale_color_manual(values = colores_grupo) +
labs(title = "Figura 5. Estados: participación en ventas vs. margen",
x = "% de las ventas totales", y = "Margen operativo agregado (%)", color = "Grupo", size = "") +
tema,
tooltip = "text")
# Correlación entre ventas y utilidad, y entre ventas y margen (por estado)
data.frame(`Relación entre estados` = c("Ventas y utilidad (Pearson)", "Ventas y margen (Spearman)"),
`Correlación` = num(c(cor(resumen_estado$ventas, resumen_estado$utilidad),
cor(resumen_estado$ventas, resumen_estado$margen, method = "spearman")), 2),
check.names = FALSE) %>%
kable(caption = "Correlaciones entre los 50 estados", align = c("l", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Correlaciones entre los 50 estados
|
Relación entre estados
|
Correlación
|
|
Ventas y utilidad (Pearson)
|
0,98
|
|
Ventas y margen (Spearman)
|
-0,05
|
Evidencia. Cada punto es un estado y su tamaño es la
utilidad que aporta. Entre estados, ventas y utilidad tienen una
correlación de 0,98, pero ventas y margen tienen una correlación de
−0,05. Texas vende el 5,5% del total con un margen de 42,8%, mientras
California vende el 7,1% con 34,5%.
Interpretación. Los estados que más venden aportan
más utilidad en dólares, pero eso no significa que ganen más por cada
dólar vendido.
resumen_grupo <- resumen_estado %>%
group_by(grupo) %>%
summarise(estados = n(),
ventas = sum(ventas),
utilidad = sum(utilidad),
lista = paste(estado[order(-part_ventas)], collapse = ", ")) %>%
mutate(margen = utilidad / ventas * 100,
part_ventas = ventas / ventas_tot * 100,
part_utilidad = utilidad / utilidad_tot * 100,
utilidad_por_millon = margen / 100 * 1e6) %>%
arrange(grupo)
resumen_grupo %>%
transmute(Grupo = as.character(grupo), Estados = estados, `% ventas` = pct(part_ventas), `% utilidad` = pct(part_utilidad),
Margen = pct(margen), `Utilidad por cada US$ 1 M vendido` = usd(utilidad_por_millon),
`Estados (de mayor a menor venta)` = lista) %>%
bind_rows(data.frame(Grupo = "Total", Estados = sum(resumen_grupo$estados),
`% ventas` = pct(sum(resumen_grupo$part_ventas)), `% utilidad` = pct(sum(resumen_grupo$part_utilidad)),
Margen = pct(margen_tot), `Utilidad por cada US$ 1 M vendido` = usd(margen_tot / 100 * 1e6),
`Estados (de mayor a menor venta)` = "", check.names = FALSE)) %>%
kable(caption = "Tabla 7. Grupos de estados según ventas y margen", align = c("l", "r", "r", "r", "r", "r", "l")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE, font_size = 13) %>%
row_spec(5, bold = TRUE)
Tabla 7. Grupos de estados según ventas y margen
|
Grupo
|
Estados
|
% ventas
|
% utilidad
|
Margen
|
Utilidad por cada US$ 1 M vendido
|
Estados (de mayor a menor venta)
|
|
Estrella
|
7
|
20,3%
|
23,0%
|
44,6%
|
US$ 445.727
|
Texas, South Carolina, Louisiana, North Carolina, Idaho, Tennessee,
Alabama
|
|
Oportunidad
|
15
|
19,7%
|
21,4%
|
42,6%
|
US$ 426.207
|
Michigan, Ohio, Mississippi, Vermont, Montana, Arkansas, Oklahoma,
Pennsylvania, Utah, West Virginia, Kentucky, Illinois, Missouri,
Indiana, Nebraska
|
|
Corregir
|
11
|
40,1%
|
36,9%
|
36,1%
|
US$ 361.165
|
New York, California, Florida, Washington, Virginia, Oregon, Nevada, New
Mexico, Hawaii, Georgia, Colorado
|
|
Bajo aporte
|
17
|
19,9%
|
18,8%
|
37,1%
|
US$ 370.613
|
New Hampshire, Wyoming, Arizona, Alaska, Connecticut, Massachusetts,
Delaware, Kansas, New Jersey, Rhode Island, Maine, South Dakota,
Maryland, North Dakota, Wisconsin, Iowa, Minnesota
|
|
Total
|
50
|
100,0%
|
100,0%
|
39,3%
|
US$ 392.996
|
|
Evidencia. Los 7 estados Estrella tienen el 20,3% de
las ventas y el 23,0% de la utilidad, con un margen de 44,6%. Los 11
estados a Corregir tienen el 40,1% de las ventas, pero solo el 36,9% de
la utilidad (margen de 36,1%). Los 15 de Oportunidad ganan 42,6%.
Interpretación. Un millón de dólares adicional deja
unos US$ 446 mil en un estado Estrella y unos US$ 361 mil en uno a
Corregir, así que crecer en los primeros rinde más.
Pregunta: ¿el margen alto está agrupado geográficamente?
mapa_estados <- resumen_estado %>%
mutate(codigo = state.abb[match(estado, state.name)])
plot_geo(mapa_estados, locationmode = "USA-states") %>%
add_trace(z = ~margen, locations = ~codigo, color = ~margen, colors = "Blues",
text = ~paste0(estado, "<br>Margen: ", pct(margen), "<br>Ventas: US$ ", round(ventas / 1e6, 2), " M<br>Grupo: ", grupo),
hoverinfo = "text") %>%
colorbar(title = "Margen (%)") %>%
layout(title = list(text = "<b>Figura 6. Margen operativo por estado (%)</b>", font = list(size = 15)),
geo = list(scope = "usa", projection = list(type = "albers usa"), showlakes = FALSE))
Evidencia. Entre más oscuro el azul, mayor el
margen. Los márgenes más altos están en el Sur (Alabama 54,4%, Tennessee
49,5%) y los más bajos en el Pacífico: Hawaii (28,5%), Washington
(28,8%) y California (34,5%). Según la Oficina de Análisis Económico de
Estados Unidos (BEA), en 2021 Hawaii, California y New York eran los
estados con el nivel de precios más alto, y Mississippi y Alabama
estaban entre los más bajos.
Interpretación. El margen depende mucho de la
geografía. El dato de la BEA sugiere que operar en los estados de margen
bajo cuesta más, aunque con la base no lo podemos comprobar.
Pregunta: ¿qué ciudades aportan más utilidad y con qué
margen?
resumen_ciudad <- datos %>%
group_by(region, estado, ciudad) %>%
summarise(ventas = sum(ventas_total),
utilidad = sum(utilidad_operativa), .groups = "drop") %>%
mutate(margen = utilidad / ventas * 100,
part_utilidad = utilidad / sum(utilidad) * 100,
nombre = paste0(ciudad, " (", estado, ") · ", pct(margen))) %>%
arrange(desc(utilidad))
# Participación de las 10 ciudades con más utilidad
top10_ciudades <- sum(resumen_ciudad$part_utilidad[1:10])
top15 <- resumen_ciudad %>% slice(1:15) %>% mutate(nombre = factor(nombre, levels = rev(nombre)))
ggplotly(
ggplot(top15, aes(x = utilidad / 1e6, y = nombre, fill = margen,
text = paste0(nombre, "\nUtilidad: US$ ", round(utilidad / 1e6, 2), " M",
"\nVentas: US$ ", round(ventas / 1e6, 2), " M\nMargen: ", pct(margen)))) +
geom_col() +
scale_fill_gradient(low = "#c6dbef", high = "#08306b") +
labs(title = "Figura 7. Las 15 ciudades con más utilidad operativa",
x = "Utilidad operativa (millones de US$)", y = "Ciudad (margen)", fill = "Margen (%)") +
tema,
tooltip = "text") %>%
layout(legend = list(orientation = "h", y = -0.15))
Evidencia. El largo de la barra es la utilidad de
cada ciudad y el color su margen (entre más oscuro, mayor). Las 10
primeras ciudades aportan el 32,0% de la utilidad. New York y San
Francisco son las que más aportan, con márgenes de 37,3% y 32,1%,
mientras Birmingham (54,4%), Knoxville (49,5%) y Dallas (44,9%) combinan
bien utilidad y margen.
Interpretación. Las ciudades que más utilidad
aportan no son las más rentables por cada dólar vendido.
Implicación para el negocio. Para seguir creciendo
conviene priorizar los estados Estrella y los de Oportunidad, que ya
ganan 42,6% aunque vendan poco, y no buscar más volumen en los estados a
Corregir hasta mejorar su margen.
Hallazgo 2 (profundización). West gana menos con lo mismo que venden
las demás regiones
Pregunta: ¿West tiene menor margen porque vende productos o
canales de bajo margen, o porque gana menos en lo mismo que venden las
demás regiones?
Para responder, a cada registro le asignamos el margen que tiene ese
mismo producto, con el mismo método de venta, a nivel nacional. Con ese
margen calculamos la utilidad que se esperaría. Si West ganara menos
solo porque vende productos o canales de menor margen, su utilidad real
sería parecida a la esperada.
# Margen nacional de cada combinación producto x método
margen_referencia <- datos %>%
group_by(producto, metodo_venta) %>%
summarise(margen_ref = sum(utilidad_operativa) / sum(ventas_total), .groups = "drop")
datos_ref <- datos %>%
left_join(margen_referencia, by = c("producto", "metodo_venta")) %>%
mutate(utilidad_esperada = ventas_total * margen_ref)
brecha_region <- datos_ref %>%
group_by(region) %>%
summarise(ventas = sum(ventas_total),
utilidad_real = sum(utilidad_operativa),
utilidad_esp = sum(utilidad_esperada)) %>%
mutate(margen_real = utilidad_real / ventas * 100,
margen_esperado = utilidad_esp / ventas * 100,
brecha_pp = margen_real - margen_esperado,
brecha_usd = utilidad_real - utilidad_esp) %>%
arrange(brecha_usd)
brecha_region %>%
transmute(`Región` = region, `Margen real` = pct(margen_real), `Margen esperado` = pct(margen_esperado),
`Diferencia (puntos)` = num(brecha_pp, 1), `Diferencia (US$)` = usd(brecha_usd)) %>%
kable(caption = "Tabla 8. Margen real frente al esperado por la mezcla de producto y método", align = c("l", "r", "r", "r", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 8. Margen real frente al esperado por la mezcla de producto y
método
|
Región
|
Margen real
|
Margen esperado
|
Diferencia (puntos)
|
Diferencia (US$)
|
|
West
|
35,7%
|
38,9%
|
-3,2
|
-US$ 1.155.698
|
|
Southeast
|
39,3%
|
40,2%
|
-0,9
|
-US$ 201.934
|
|
Northeast
|
38,8%
|
38,1%
|
0,7
|
US$ 178.055
|
|
Midwest
|
41,1%
|
39,7%
|
1,4
|
US$ 236.417
|
|
South
|
44,8%
|
40,2%
|
4,6
|
US$ 943.159
|
Evidencia. West tiene un margen de 35,7% cuando, por
lo que vende, se esperaría 38,9%: son 3,2 puntos menos, unos US$ 1,16
millones de utilidad. South está 4,6 puntos por encima de lo
esperado.
Interpretación. West vende productos y canales
parecidos a los de otras regiones, pero obtiene menos utilidad con
ellos.
Pregunta: ¿la brecha de West está repartida en toda la región o
concentrada en algunos estados?
brecha_west <- datos_ref %>%
filter(region == "West") %>%
group_by(estado) %>%
summarise(ventas = sum(ventas_total),
brecha_usd = sum(utilidad_operativa) - sum(utilidad_esperada)) %>%
mutate(signo = ifelse(brecha_usd < 0, "Por debajo de lo esperado", "Por encima de lo esperado"),
estado = reorder(estado, brecha_usd))
ggplotly(
ggplot(brecha_west, aes(x = brecha_usd / 1000, y = estado, fill = signo,
text = paste0(estado, "\nDiferencia: US$ ", round(brecha_usd / 1000), " mil"))) +
geom_col() +
scale_fill_manual(values = c("Por debajo de lo esperado" = "firebrick", "Por encima de lo esperado" = "forestgreen")) +
labs(title = "Figura 8. West: utilidad real menos esperada, por estado",
x = "Miles de US$", y = NULL, fill = "") +
tema,
tooltip = "text") %>%
layout(legend = list(orientation = "h", y = -0.15))
Evidencia. En rojo están los estados que ganan menos
de lo esperado y en verde los que ganan más. California, Hawaii y
Washington explican el 78% del faltante de West, mientras Utah, Idaho,
Colorado y Oregon están por encima de lo esperado.
Interpretación. El problema está en estados
puntuales y no en toda la región.
Pregunta: ¿qué productos y qué distribuidores explican la
brecha?
# Margen de cada producto en cada región
datos %>%
group_by(producto, region) %>%
summarise(margen = sum(utilidad_operativa) / sum(ventas_total) * 100, .groups = "drop") %>%
mutate(margen = pct(margen)) %>%
pivot_wider(names_from = region, values_from = margen) %>%
rename(Producto = producto) %>%
kable(caption = "Tabla 9. Margen operativo por producto y región", align = c("l", rep("r", 5))) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 9. Margen operativo por producto y región
|
Producto
|
Midwest
|
Northeast
|
South
|
Southeast
|
West
|
|
Men’s Apparel
|
42,2%
|
34,1%
|
39,2%
|
36,1%
|
41,6%
|
|
Men’s Athletic Footwear
|
37,0%
|
37,9%
|
41,3%
|
36,5%
|
31,9%
|
|
Men’s Street Footwear
|
41,4%
|
44,3%
|
42,9%
|
42,7%
|
39,3%
|
|
Women’s Apparel
|
45,9%
|
38,0%
|
52,1%
|
45,9%
|
29,8%
|
|
Women’s Athletic Footwear
|
39,4%
|
36,6%
|
48,6%
|
36,5%
|
36,6%
|
|
Women’s Street Footwear
|
38,0%
|
36,5%
|
43,1%
|
34,1%
|
37,3%
|
Evidencia. Women’s Apparel gana 29,8% en West y
52,1% en South, 22 puntos de diferencia. Es la combinación de región y
producto con el mayor faltante frente a lo esperado, unos US$ 733 mil.
En West el segundo producto más débil es Men’s Athletic Footwear
(31,9%).
Interpretación. Un mismo producto rinde muy distinto
según el mercado donde se vende.
distribuidor_region <- datos %>%
group_by(distribuidor, region) %>%
summarise(margen = sum(utilidad_operativa) / sum(ventas_total) * 100, .groups = "drop")
peor_region <- distribuidor_region %>%
group_by(distribuidor) %>%
summarise(`Región de menor margen` = region[which.min(margen)],
`Diferencia entre su mejor y su peor región (puntos)` = num(max(margen) - min(margen), 1))
peso_west <- datos %>%
filter(region == "West") %>%
group_by(distribuidor) %>%
summarise(ventas = sum(ventas_total)) %>%
mutate(`% de las ventas de West` = pct(ventas / sum(ventas) * 100)) %>%
select(-ventas)
distribuidor_region %>%
mutate(margen = pct(margen)) %>%
pivot_wider(names_from = region, values_from = margen, values_fill = "Sin ventas") %>%
left_join(peor_region, by = "distribuidor") %>%
left_join(peso_west, by = "distribuidor") %>%
rename(Distribuidor = distribuidor) %>%
kable(caption = "Tabla 10. Margen de cada distribuidor por región", align = c("l", rep("r", 8))) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE, font_size = 13)
Tabla 10. Margen de cada distribuidor por región
|
Distribuidor
|
Midwest
|
Northeast
|
South
|
Southeast
|
West
|
Región de menor margen
|
Diferencia entre su mejor y su peor región (puntos)
|
% de las ventas de West
|
|
Amazon
|
42,8%
|
39,5%
|
38,4%
|
42,0%
|
33,2%
|
West
|
9,5
|
4,5%
|
|
Foot Locker
|
40,3%
|
38,9%
|
42,0%
|
40,0%
|
35,4%
|
West
|
6,5
|
13,9%
|
|
Kohl’s
|
40,8%
|
39,1%
|
40,6%
|
Sin ventas
|
37,3%
|
West
|
3,6
|
23,5%
|
|
Sports Direct
|
43,0%
|
36,4%
|
48,4%
|
40,5%
|
40,3%
|
Northeast
|
12,0
|
4,6%
|
|
Walmart
|
Sin ventas
|
36,2%
|
41,9%
|
33,1%
|
27,3%
|
West
|
14,6
|
2,3%
|
|
West Gear
|
39,9%
|
41,0%
|
41,8%
|
39,5%
|
35,3%
|
West
|
6,5
|
51,3%
|
Evidencia. Cinco de los seis distribuidores tienen
su margen más bajo en West. Walmart, por ejemplo, gana 41,9% en South y
27,3% en West.
Interpretación. Como casi todos los distribuidores
ganan menos allí, la causa parece estar en el mercado de West y no en un
distribuidor en particular.
Explicaciones alternativas y datos que faltan. El
margen bajo podría deberse a (a) costos de operación, logística o
arriendo más altos en esos estados, (b) descuentos o promociones de los
distribuidores, o (c) un costo de producto distinto en la región. Para
saber cuál es la causa se necesitan el costo de ventas, los descuentos
aplicados y la fecha de cada venta, que la base no incluye.
Implicación para el negocio. Cambiar de distribuidor
probablemente no mejoraría el margen. Lo recomendable es revisar costos
y precios en California, Washington y Hawaii, empezando por Women’s
Apparel y por West Gear, que vende el 51,3% de lo que se vende en
West.
Hallazgo 3. El canal importa, y no rinde igual en todas partes
Pregunta: ¿qué combinaciones de ciudad y método son las más y las
menos rentables?
ciudad_metodo <- datos %>%
group_by(region, ciudad, estado, metodo_venta) %>%
summarise(ventas = sum(ventas_total),
utilidad = sum(utilidad_operativa), .groups = "drop") %>%
mutate(margen = utilidad / ventas * 100) %>%
filter(ventas >= 500000) # se dejan combinaciones con ventas relevantes (US$ 500 mil o más)
mejores <- ciudad_metodo %>% arrange(desc(margen)) %>% slice(1:8)
peores <- ciudad_metodo %>% arrange(margen) %>% slice(1:8)
bind_rows(mejores, peores) %>%
transmute(Ciudad = paste0(ciudad, " (", estado, ")"), `Región` = region, `Método` = metodo_venta,
Ventas = usd(ventas), Utilidad = usd(utilidad), Margen = pct(margen)) %>%
kable(caption = "Tabla 11. Combinaciones ciudad × método de venta con mayor y menor margen (ventas de US$ 500 mil o más)",
align = c("l", "l", "l", "r", "r", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE, font_size = 13) %>%
pack_rows("Mayor margen", 1, 8) %>%
pack_rows("Menor margen", 9, 16)
Tabla 11. Combinaciones ciudad × método de venta con mayor y menor
margen (ventas de US$ 500 mil o más)
|
Ciudad
|
Región
|
Método
|
Ventas
|
Utilidad
|
Margen
|
|
Mayor margen
|
|
Dallas (Texas)
|
South
|
Online
|
US$ 518.502
|
US$ 284.247
|
54,8%
|
|
Birmingham (Alabama)
|
South
|
Outlet
|
US$ 2.071.868
|
US$ 1.084.261
|
52,3%
|
|
Miami (Florida)
|
Southeast
|
Online
|
US$ 793.363
|
US$ 402.807
|
50,8%
|
|
Houston (Texas)
|
South
|
Online
|
US$ 624.977
|
US$ 316.456
|
50,6%
|
|
Albany (New York)
|
Northeast
|
Online
|
US$ 612.804
|
US$ 308.788
|
50,4%
|
|
Denver (Colorado)
|
West
|
Online
|
US$ 521.536
|
US$ 254.618
|
48,8%
|
|
Richmond (Virginia)
|
Southeast
|
Online
|
US$ 533.561
|
US$ 254.587
|
47,7%
|
|
Portland (Oregon)
|
West
|
Online
|
US$ 530.442
|
US$ 252.336
|
47,6%
|
|
Menor margen
|
|
Honolulu (Hawaii)
|
West
|
Outlet
|
US$ 2.172.000
|
US$ 563.338
|
25,9%
|
|
Seattle (Washington)
|
West
|
In-store
|
US$ 2.567.625
|
US$ 673.744
|
26,2%
|
|
Boston (Massachusetts)
|
Northeast
|
In-store
|
US$ 1.055.375
|
US$ 315.164
|
29,9%
|
|
San Francisco (California)
|
West
|
Outlet
|
US$ 4.070.352
|
US$ 1.226.550
|
30,1%
|
|
Anchorage (Alaska)
|
West
|
Outlet
|
US$ 1.438.075
|
US$ 434.021
|
30,2%
|
|
Orlando (Florida)
|
Southeast
|
In-store
|
US$ 2.099.750
|
US$ 654.494
|
31,2%
|
|
Las Vegas (Nevada)
|
West
|
In-store
|
US$ 1.155.875
|
US$ 365.713
|
31,6%
|
|
Orlando (Florida)
|
Southeast
|
Outlet
|
US$ 1.164.275
|
US$ 391.412
|
33,6%
|
Evidencia. Las combinaciones más rentables son
ventas Online en ciudades del Sur, como Dallas (54,8%), y el Outlet de
Birmingham (52,3%). Online también gana bien en estados del grupo a
Corregir, como Albany, en New York (50,4%). Las menos rentables están en
West: el Outlet de Honolulu (25,9%) y el de San Francisco (30,1%), que
además vende US$ 4,07 millones. En la Figura 3, el Outlet gana 44,6% en
South y 32,8% en West, y Online gana 48,6% en Northeast aunque pesa solo
18,4% de las ventas de esa región.
Interpretación. El mismo canal puede dar casi 12
puntos más de margen según el mercado, y Online funciona bien incluso
donde la región gana poco.
Implicación para el negocio. Hay espacio para
impulsar Online en Northeast, y el modelo de Outlet de South es una
práctica que vale la pena estudiar para West.
Hallazgo 4 (no evidente a simple vista). Precio, volumen y utilidad:
lo que cambia al comparar casos parecidos
Pregunta: ¿cómo se relacionan precio, volumen y utilidad, y la
relación que se ve en el total se mantiene por región, producto y
estado?
Una correlación del total puede deberse a la composición de las
ventas. Por eso la calculamos también dentro de cada
región, de cada producto, de cada estado y de cada combinación de
estado, producto y método, restando a cada registro el promedio de su
grupo.
# Correlación dentro de grupos: se resta a cada valor el promedio de su grupo antes de correlacionar
cor_dentro <- function(x, y, grupo) cor(x - ave(x, grupo), y - ave(y, grupo))
grupo_region <- datos$region
grupo_producto <- datos$producto
grupo_estado <- datos$estado
grupo_fino <- paste(datos$estado, datos$producto, datos$metodo_venta)
relaciones <- data.frame(
`Relación` = c("Precio y unidades", "Precio y margen", "Unidades y margen", "Unidades y utilidad"),
Total = c(cor(datos$precio_unidad, datos$unidades_vendidas),
cor(datos$precio_unidad, datos$margen_operativo),
cor(datos$unidades_vendidas, datos$margen_operativo),
cor(datos$unidades_vendidas, datos$utilidad_operativa)),
`Dentro de cada región` = c(cor_dentro(datos$precio_unidad, datos$unidades_vendidas, grupo_region),
cor_dentro(datos$precio_unidad, datos$margen_operativo, grupo_region),
cor_dentro(datos$unidades_vendidas, datos$margen_operativo, grupo_region),
cor_dentro(datos$unidades_vendidas, datos$utilidad_operativa, grupo_region)),
`Dentro de cada producto` = c(cor_dentro(datos$precio_unidad, datos$unidades_vendidas, grupo_producto),
cor_dentro(datos$precio_unidad, datos$margen_operativo, grupo_producto),
cor_dentro(datos$unidades_vendidas, datos$margen_operativo, grupo_producto),
cor_dentro(datos$unidades_vendidas, datos$utilidad_operativa, grupo_producto)),
`Dentro de cada estado` = c(cor_dentro(datos$precio_unidad, datos$unidades_vendidas, grupo_estado),
cor_dentro(datos$precio_unidad, datos$margen_operativo, grupo_estado),
cor_dentro(datos$unidades_vendidas, datos$margen_operativo, grupo_estado),
cor_dentro(datos$unidades_vendidas, datos$utilidad_operativa, grupo_estado)),
`Dentro de estado, producto y método` = c(cor_dentro(datos$precio_unidad, datos$unidades_vendidas, grupo_fino),
cor_dentro(datos$precio_unidad, datos$margen_operativo, grupo_fino),
cor_dentro(datos$unidades_vendidas, datos$margen_operativo, grupo_fino),
cor_dentro(datos$unidades_vendidas, datos$utilidad_operativa, grupo_fino)),
check.names = FALSE)
relaciones %>%
mutate(across(-`Relación`, ~ num(.x, 2))) %>%
kable(caption = "Tabla 12. Correlación de Pearson en el total y dentro de segmentos", align = c("l", rep("r", 5))) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 12. Correlación de Pearson en el total y dentro de segmentos
|
Relación
|
Total
|
Dentro de cada región
|
Dentro de cada producto
|
Dentro de cada estado
|
Dentro de estado, producto y método
|
|
Precio y unidades
|
0,27
|
0,26
|
0,29
|
0,28
|
0,33
|
|
Precio y margen
|
-0,14
|
-0,07
|
-0,16
|
0,00
|
-0,05
|
|
Unidades y margen
|
-0,31
|
-0,33
|
-0,35
|
-0,39
|
-0,45
|
|
Unidades y utilidad
|
0,87
|
0,87
|
0,87
|
0,85
|
0,84
|
Evidencia. En el total, un precio más alto se asocia
con menor margen (−0,14). Esa relación se mantiene dentro de cada
producto (−0,16), se debilita dentro de cada región (−0,07) y desaparece
dentro de cada estado (0,00). La relación entre precio y unidades es
positiva en todos los niveles (entre 0,26 y 0,33), y la de unidades y
margen se vuelve más fuerte al comparar casos parecidos (de −0,31 a
−0,45).
Interpretación. La relación negativa entre precio y
margen es un efecto de composición: aparece porque los estados caros son
los de margen bajo. La relación entre unidades y utilidad (0,87) es alta
porque la utilidad depende directamente de las ventas, así que no es un
hallazgo en sí misma.
Pregunta: ¿los estados con precio más alto tienen mejor o peor
margen?
ggplotly(
ggplot(resumen_estado, aes(x = precio_medio, y = margen,
text = paste0(estado, "\nPrecio medio: US$ ", round(precio_medio, 1), "\nMargen: ", pct(margen)))) +
geom_point(color = "steelblue", size = 2.5, alpha = 0.8) +
geom_smooth(aes(group = 1), method = "lm", se = FALSE, color = "grey40") +
labs(title = "Figura 9. Precio medio vs. margen, por estado",
x = "Precio medio por unidad (US$)", y = "Margen operativo agregado (%)") +
tema,
tooltip = "text")
Evidencia. Entre estados, la correlación entre
precio medio y margen es −0,57. Washington y Hawaii venden a US$ 69,1 y
US$ 67,7 por unidad, con márgenes de 28,8% y 28,5%.
Interpretación. Los mercados más caros no son los
más rentables. Esto confirma que la relación negativa entre precio y
margen viene de la mezcla de estados.
Pregunta: dentro de un mismo estado, producto y método, ¿los
periodos en que se venden más unidades dejan mejor o peor
margen?
Para comparar casos similares, marcamos como de volumen alto los
registros cuyas unidades superan la mediana de su mismo estado, producto
y método de venta.
datos_volumen <- datos %>%
group_by(estado, producto, metodo_venta) %>%
mutate(volumen = ifelse(unidades_vendidas > median(unidades_vendidas), "Volumen alto", "Volumen bajo o medio")) %>%
ungroup()
resumen_volumen <- datos_volumen %>%
group_by(metodo_venta, volumen) %>%
summarise(ventas = sum(ventas_total),
utilidad = sum(utilidad_operativa),
unidades = sum(unidades_vendidas), .groups = "drop") %>%
mutate(margen = utilidad / ventas * 100,
precio_medio = ventas / unidades)
ggplotly(
ggplot(resumen_volumen, aes(x = metodo_venta, y = margen, fill = volumen,
text = paste0(metodo_venta, " · ", volumen, "\nMargen: ", pct(margen),
"\nPrecio medio: US$ ", round(precio_medio, 1)))) +
geom_col(position = "dodge") +
scale_fill_manual(values = c("Volumen alto" = "navy", "Volumen bajo o medio" = "lightsteelblue")) +
labs(title = "Figura 10. Margen según el volumen vendido",
x = "Método de venta", y = "Margen operativo agregado (%)", fill = "") +
tema,
tooltip = "text") %>%
layout(legend = list(orientation = "h", y = -0.15))
Evidencia. Comparando registros del mismo estado,
producto y método, en Online los periodos de mayor volumen ganan 41,3%,
frente a 49,3% en los demás. En Outlet la caída es menor (37,3% frente a
38,9%) y en In-store no hay caída (36,2% frente a 35,2%).
Interpretación. Vender más unidades en un mismo
mercado se asocia con un margen menor, sobre todo en Online.
resumen_volumen %>%
transmute(`Método` = metodo_venta, Volumen = volumen, Margen = pct(margen),
`Precio medio por unidad` = paste0("US$ ", num(precio_medio, 1))) %>%
kable(caption = "Tabla 13. Margen y precio según el volumen vendido", align = c("l", "l", "r", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 13. Margen y precio según el volumen vendido
|
Método
|
Volumen
|
Margen
|
Precio medio por unidad
|
|
In-store
|
Volumen alto
|
36,2%
|
US$ 55,0
|
|
In-store
|
Volumen bajo o medio
|
35,2%
|
US$ 47,7
|
|
Online
|
Volumen alto
|
41,3%
|
US$ 49,0
|
|
Online
|
Volumen bajo o medio
|
49,3%
|
US$ 45,1
|
|
Outlet
|
Volumen alto
|
37,3%
|
US$ 49,9
|
|
Outlet
|
Volumen bajo o medio
|
38,9%
|
US$ 39,2
|
Evidencia. En los periodos de volumen alto el precio
por unidad es más alto: en Online, US$ 49,0 frente a US$ 45,1.
Interpretación. La caída del margen no parece venir
de descuentos. Podría deberse a mayores costos al atender los picos de
demanda, como envíos o logística, pero es una hipótesis.
Implicación para el negocio. Si Online crece solo
con más volumen, probablemente su margen baje, por eso proponemos
empezar con una prueba controlada. Estas correlaciones no permiten
decidir precios; para eso se necesitarían datos de costos y la fecha de
cada venta.
Hallazgo 5 (no evidente a simple vista). Pocas combinaciones generan
la mitad de la utilidad
Pregunta: ¿la utilidad está repartida entre muchas combinaciones
de ciudad, producto y método, o concentrada en pocas?
combinaciones <- datos %>%
group_by(region, ciudad, estado, producto, metodo_venta) %>%
summarise(utilidad = sum(utilidad_operativa), .groups = "drop") %>%
arrange(desc(utilidad)) %>%
mutate(acumulado = cumsum(utilidad) / sum(utilidad) * 100)
n_mitad <- which(combinaciones$acumulado >= 50)[1]
data.frame(Indicador = c("Combinaciones de ciudad × producto × método",
"Combinaciones que generan el 50% de la utilidad",
"Proporción de combinaciones que generan el 50% de la utilidad",
"Región más frecuente en esas combinaciones",
"Método de venta más frecuente en esas combinaciones"),
Valor = c(nrow(combinaciones), n_mitad, pct(n_mitad / nrow(combinaciones) * 100),
names(sort(table(head(combinaciones$region, n_mitad)), decreasing = TRUE))[1],
names(sort(table(head(combinaciones$metodo_venta, n_mitad)), decreasing = TRUE))[1])) %>%
kable(caption = "Tabla 14. Concentración de la utilidad", align = c("l", "r")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tabla 14. Concentración de la utilidad
|
Indicador
|
Valor
|
|
Combinaciones de ciudad × producto × método
|
690
|
|
Combinaciones que generan el 50% de la utilidad
|
134
|
|
Proporción de combinaciones que generan el 50% de la utilidad
|
19,4%
|
|
Región más frecuente en esas combinaciones
|
West
|
|
Método de venta más frecuente en esas combinaciones
|
Online
|
Evidencia. De 690 combinaciones de ciudad, producto
y método, 134 (el 19%) generan la mitad de la utilidad. West es la
región que más aparece en ese grupo y Online el método más
frecuente.
Interpretación. Aunque West gane menos por cada
dólar, sus negocios grandes siguen entre los que más utilidad aportan a
la empresa.
Implicación para el negocio. Hay que cuidar esas 134
combinaciones (inventario, disponibilidad y servicio al distribuidor) y
mejorar el margen de West sin perder su presencia.
LS0tDQp0aXRsZTogIkNhc28gMSDCtyBBbmFsw610aWNhIGRlc2NyaXB0aXZhIHkgZGlhZ27Ds3N0aWNhOiBkw7NuZGUgZ2FuYSBBZGlkYXMgeSBjw7NtbyBzZWd1aXIgaW1wdWxzYW5kbyBsYXMgdmVudGFzIg0KYXV0aG9yOiAiQ8Opc2FyIEFuZHLDqXMgR2FyY8OtYSBWYXJvbmEgeSBPc3dhbGRvIEZpZXJybyINCmRhdGU6ICIyMDI2LTExLTA4Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICBkZl9wcmludDoga2FibGUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgbWRfZXh0ZW5zaW9uczogLXRleF9tYXRoX2RvbGxhcnMNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgcmVzdWx0cyA9ICJzaG93IiwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gOS41LCBmaWcuaGVpZ2h0ID0gNSwgZmlnLmFsaWduID0gImNlbnRlciIpDQpgYGANCg0KKipWaWRlbyBkZSBzdXN0ZW50YWNpw7NuOioqIFtFTkxBQ0UgQUwgVklERU9dDQoNCioqQXNpZ25hdHVyYToqKiBBbmFsw610aWNhIGRlIE5lZ29jaW9zIMK3IEVzcGVjaWFsaXphY2nDs24gZW4gRmluYW56YXMgwrcgUG9udGlmaWNpYSBVbml2ZXJzaWRhZCBKYXZlcmlhbmEgQ2FsaSDCtyBQcm9mZXNvcmEgSnVsaWV0aCBTdGVmZW5zIENlcsOzbg0KDQpgYGB7Y3NzLCBlY2hvPUZBTFNFfQ0KLyogRGlzZcOxbzogY29udGVuaWRvIGNlbnRyYWRvLCB0w610dWxvcyB5IHRhYmxhcyBsZWdpYmxlcyAqLw0KLm1haW4tY29udGFpbmVyIHsgbWF4LXdpZHRoOiAxMTUwcHg7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCmgxIHsgY29sb3I6ICMxZjNiNWE7IGJvcmRlci1ib3R0b206IDJweCBzb2xpZCAjZDllMmVjOyBwYWRkaW5nLWJvdHRvbTogNnB4OyBtYXJnaW4tdG9wOiA0MHB4OyB9DQpoMiB7IGNvbG9yOiAjMmM1MjgyOyBtYXJnaW4tdG9wOiAzMHB4OyB9DQpjYXB0aW9uIHsgZm9udC13ZWlnaHQ6IGJvbGQ7IGNvbG9yOiAjMzMzMzMzOyB0ZXh0LWFsaWduOiBjZW50ZXI7IGNhcHRpb24tc2lkZTogdG9wOyB9DQp0YWJsZSB7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCi5odG1sLXdpZGdldCB7IG1hcmdpbi1sZWZ0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IGF1dG87IH0NCnAgeyB0ZXh0LWFsaWduOiBqdXN0aWZ5OyB9DQpgYGANCg0KYGBge3IgbG9nbywgZWNobz1GQUxTRSwgZmlnLmFsaWduPSJjZW50ZXIiLCBvdXQud2lkdGg9IjM1JSJ9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiNjg5MzQ3LmpwZyIpDQpgYGANCg0KIyAqKlJlc3VtZW4gZWplY3V0aXZvKioNCg0KQWRpZGFzIG5lY2VzaXRhIHNhYmVyIGTDs25kZSB5IHBvciBxdcOpIG3DqXRvZG8gZGUgdmVudGEgaW1wdWxzYXIgc3VzIHZlbnRhcyBlbiBFc3RhZG9zIFVuaWRvcyBzaW4gc2FjcmlmaWNhciByZW50YWJpbGlkYWQuIEFuYWxpemFtb3MgOS42NDQgcmVnaXN0cm9zIGRlIHZlbnRhcyAoVVMkIDEyMCwyIG1pbGxvbmVzIGVuIHZlbnRhcywgVVMkIDQ3LDIgbWlsbG9uZXMgZGUgdXRpbGlkYWQgb3BlcmF0aXZhIHkgMzksMyUgZGUgbWFyZ2VuKSB5IGVuY29udHJhbW9zIHF1ZSB2ZW5kZXIgbcOhcyBubyBzaWVtcHJlIGVzIGdhbmFyIG3DoXM6IGVsIG1hcmdlbiB2YSBkZSAyOCw1JSBlbiBIb25vbHVsdSBhIDU0LDQlIGVuIEJpcm1pbmdoYW0sIHkgdW4gbWlsbMOzbiBkZSBkw7NsYXJlcyBhZGljaW9uYWwgZGVqYSBjZXJjYSBkZSBVUyQgNDQ2IG1pbCBkZSB1dGlsaWRhZCBlbiBsb3Mgc2lldGUgZXN0YWRvcyBxdWUgbGxhbWFtb3MgRXN0cmVsbGEsIGZyZW50ZSBhIFVTJCAzNjEgbWlsIGVuIGxvcyBvbmNlIGVzdGFkb3MgZ3JhbmRlcyBkZSBtYXJnZW4gYmFqbyAoQ2FsaWZvcm5pYSwgTmV3IFlvcmsgeSBGbG9yaWRhLCBlbnRyZSBvdHJvcykuIFdlc3QgZXMgbGEgcmVnacOzbiBxdWUgbcOhcyB2ZW5kZSAoMzAsMyUpIHkgbGEgZGUgbWVub3IgbWFyZ2VuICgzNSw3JSk7IGdhbmEgMywyIHB1bnRvcyBtZW5vcyBkZSBsbyBxdWUgc3UgbWV6Y2xhIGRlIHByb2R1Y3RvcyB5IGNhbmFsZXMgcGVybWl0aXLDrWEsIHkgdG9kb3MgbG9zIGRpc3RyaWJ1aWRvcmVzIGdhbmFuIG1lbm9zIGFsbMOtLiBPbmxpbmUgZXMgZWwgbcOpdG9kbyBjb24gbWVqb3IgbWFyZ2VuIGVuIDQyIGRlIGxvcyA1MCBlc3RhZG9zLCBhdW5xdWUgc3UgbWFyZ2VuIGJhamEgZGUgNDksMyUgYSA0MSwzJSBlbiBsb3MgcGVyaW9kb3MgZGUgbWF5b3Igdm9sdW1lbi4gUmVjb21lbmRhbW9zIGNyZWNlciBwcmltZXJvIGVuIGxvcyBlc3RhZG9zIEVzdHJlbGxhIHkgZGUgT3BvcnR1bmlkYWQsIHByb2JhciBkZSBmb3JtYSBjb250cm9sYWRhIGVsIGltcHVsc28gYSBPbmxpbmUgZW4gTm9ydGhlYXN0IHkgV2VzdCwgeSBjb3JyZWdpciBlbCBtYXJnZW4gZGUgV29tZW4ncyBBcHBhcmVsIGVuIENhbGlmb3JuaWEsIFdhc2hpbmd0b24geSBIYXdhaWkgYW50ZXMgZGUgYnVzY2FyIG3DoXMgdm9sdW1lbiBhbGzDrS4gTG9zIGJlbmVmaWNpb3MgZXN0aW1hZG9zIHNvbiBlc2NlbmFyaW9zLCBubyByZXN1bHRhZG9zIGdhcmFudGl6YWRvcy4NCg0KIyAqKkRhdG9zIHkgcHJlcGFyYWNpw7NuKioNCg0KIyMgQWxjYW5jZSB5IHVuaWRhZCBkZSBvYnNlcnZhY2nDs24NCg0KQ2FkYSBmaWxhIGRlIGxhIGJhc2UgZXMgZWwgcmVnaXN0cm8gZGUgdmVudGFzIGRlIHVuIHByb2R1Y3RvLCB2ZW5kaWRvIHBvciB1biBkaXN0cmlidWlkb3IsIGVuIHVuYSBjaXVkYWQgeSBwb3IgdW4gbcOpdG9kbyBkZSB2ZW50YS4gUG9yIGxhIGVzdHJ1Y3R1cmEgZGUgbGEgYmFzZSAodmVyIGxhIHJldmlzacOzbiBkZSBjYWxpZGFkKSwgY2FkYSBmaWxhIHBhcmVjZSBjb3JyZXNwb25kZXIgYSB1biBtZXMsIGF1bnF1ZSBsYSBmZWNoYSBubyB2aWVuZSBlbiBlbCBhcmNoaXZvLiBMYSBiYXNlIGN1YnJlIDUwIGVzdGFkb3MgYWdydXBhZG9zIGVuIDUgcmVnaW9uZXMsIDU0IGNpdWRhZGVzLCA2IGRpc3RyaWJ1aWRvcmVzLCA2IHByb2R1Y3RvcyB5IDMgbcOpdG9kb3MgZGUgdmVudGEgKEluLXN0b3JlLCBPbmxpbmUgeSBPdXRsZXQpLiBMYSBiYXNlIG5vIGluZGljYSBsYSBtb25lZGE7IHRyYWJhamFtb3MgZW4gZMOzbGFyZXMgcG9ycXVlIGxhcyB2ZW50YXMgc29uIGVuIEVzdGFkb3MgVW5pZG9zLiBUYW1wb2NvIGluZGljYSBlbCBwZXJpb2RvLCBhc8OtIHF1ZSBsYXMgY29uY2x1c2lvbmVzIHNlIGxpbWl0YW4gYSBsbyBxdWUgY29udGllbmUgbGEgYmFzZS4NCg0KKipQQVNPIDEuIENhcmdhciBsaWJyZXLDrWFzKioNCg0KYGBge3IgbGlicmVyaWFzfQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCg0KIyBFc3RpbG8gY29tw7puIHBhcmEgdG9kb3MgbG9zIGdyw6FmaWNvcw0KdGVtYSA8LSB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgZmFjZSA9ICJib2xkIikpDQpgYGANCg0KKipQQVNPIDIuIENhcmdhciBsYSBiYXNlIGRlIGRhdG9zKioNCg0KYGBge3IgY2FyZ2F9DQpkYXRvcyA8LSByZWFkX2V4Y2VsKCJEYXRvc0Nhc28xLm1vZGlmaWNhZG8ueGxzeCIsDQogICAgY29sX3R5cGVzID0gYygidGV4dCIsICJ0ZXh0IiwgInRleHQiLA0KICAgICAgICAidGV4dCIsICJ0ZXh0IiwgIm51bWVyaWMiLCAibnVtZXJpYyIsDQogICAgICAgICJudW1lcmljIiwgIm51bWVyaWMiLCAibnVtZXJpYyIsDQogICAgICAgICJ0ZXh0IikpDQoNCiMgVmVyIGxhcyBwcmltZXJhcyBmaWxhcyBkZSBsYSBiYXNlDQpoZWFkKGRhdG9zKSAlPiUga2FibGUoKSAlPiUga2FibGVfc3R5bGluZyhmb250X3NpemUgPSAxMSkgJT4lIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KYGBge3IgZm9ybWF0b3N9DQojIEZ1bmNpb25lcyBwYXJhIG1vc3RyYXIgZMOzbGFyZXMgeSBwb3JjZW50YWplcyBjb24gY29tYSBkZWNpbWFsIGVuIGxhcyB0YWJsYXMNCnVzZCA8LSBmdW5jdGlvbih4KSBwYXN0ZTAoaWZlbHNlKHggPCAwLCAiLSIsICIiKSwgIlVTJCAiLCBmb3JtYXQocm91bmQoYWJzKHgpKSwgYmlnLm1hcmsgPSAiLiIsIGRlY2ltYWwubWFyayA9ICIsIiwgc2NpZW50aWZpYyA9IEZBTFNFLCB0cmltID0gVFJVRSkpDQpwY3QgPC0gZnVuY3Rpb24oeCkgcGFzdGUwKGZvcm1hdChyb3VuZCh4LCAxKSwgbnNtYWxsID0gMSwgZGVjaW1hbC5tYXJrID0gIiwiLCB0cmltID0gVFJVRSksICIlIikNCm51bSA8LSBmdW5jdGlvbih4LCBkID0gMCkgZm9ybWF0KHJvdW5kKHgsIGQpLCBuc21hbGwgPSBkLCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiLCBzY2llbnRpZmljID0gRkFMU0UsIHRyaW0gPSBUUlVFKQ0KYGBgDQoNCnwgVmFyaWFibGUgfCBUaXBvIHwgRXNjYWxhIHwgVW5pZGFkIHwNCnwtLS18LS0tfC0tLXwtLS18DQp8IGRpc3RyaWJ1aWRvciwgcmVnacOzbiwgZXN0YWRvLCBjaXVkYWQsIHByb2R1Y3RvLCBtw6l0b2RvIGRlIHZlbnRhIHwgQ3VhbGl0YXRpdmEgfCBOb21pbmFsIHwgQ2F0ZWdvcsOtYXMgfA0KfCBwcmVjaW9fdW5pZGFkIHwgQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIHwgUmF6w7NuIHwgVVMkIHBvciB1bmlkYWQgfA0KfCB1bmlkYWRlc192ZW5kaWRhcyB8IEN1YW50aXRhdGl2YSBkaXNjcmV0YSB8IFJhesOzbiB8IFVuaWRhZGVzIHwNCnwgdmVudGFzX3RvdGFsLCB1dGlsaWRhZF9vcGVyYXRpdmEgfCBDdWFudGl0YXRpdmEgY29udGludWEgfCBSYXrDs24gfCBVUyQgfA0KfCBtYXJnZW5fb3BlcmF0aXZvIHwgQ3VhbnRpdGF0aXZhIGNvbnRpbnVhIHwgUmF6w7NuIHwgUHJvcG9yY2nDs24gKDAsMzUgPSAzNSUpIHwNCg0KKipQQVNPIDMuIFJldmlzYXIgbGEgY2FsaWRhZCBkZSBsb3MgZGF0b3MqKg0KDQpgYGB7ciBjYWxpZGFkfQ0KIyAxLiBGb3JtYXRvIHkgdmFsb3JlcyBmYWx0YW50ZXMNCnRpcG9zICAgICAgPC0gc2FwcGx5KGRhdG9zLCBjbGFzcykNCmZhbHRhbnRlcyAgPC0gc3VtKGlzLm5hKGRhdG9zKSkNCg0KIyAyLiBDb2hlcmVuY2lhOiB2ZW50YXMgPSBwcmVjaW8geCB1bmlkYWRlcyB5IHV0aWxpZGFkID0gdmVudGFzIHggbWFyZ2VuIChkaWZlcmVuY2lhcyBtYXlvcmVzIGEgMSBkw7NsYXIpDQplcnJvcl92ZW50YXMgICA8LSBzdW0oYWJzKGRhdG9zJHZlbnRhc190b3RhbCAtIGRhdG9zJHByZWNpb191bmlkYWQgKiBkYXRvcyR1bmlkYWRlc192ZW5kaWRhcykgPiAxKQ0KZXJyb3JfdXRpbGlkYWQgPC0gc3VtKGFicyhkYXRvcyR1dGlsaWRhZF9vcGVyYXRpdmEgLSBkYXRvcyR2ZW50YXNfdG90YWwgKiBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvKSA+IDEpDQoNCiMgMy4gUmVnaXN0cm9zIGNvbiB2ZW50YXMgZW4gY2Vybw0KY2Vyb3MgPC0gZGF0b3MgJT4lIGZpbHRlcih2ZW50YXNfdG90YWwgPT0gMCkNCg0KIyA0LiBGaWxhcyBkdXBsaWNhZGFzIChsYXMgMTEgY29sdW1uYXMgaWTDqW50aWNhcykgeSBtYXJnZW4gYWdyZWdhZG8gY29uIHkgc2luIGVsbGFzDQpkdXBsaWNhZG9zICAgICA8LSBzdW0oZHVwbGljYXRlZChkYXRvcykpDQpzaW5fZHVwbGljYWRvcyA8LSBkaXN0aW5jdChkYXRvcykNCm1hcmdlbl9jb24gICAgIDwtIHN1bShkYXRvcyR1dGlsaWRhZF9vcGVyYXRpdmEpIC8gc3VtKGRhdG9zJHZlbnRhc190b3RhbCkgKiAxMDANCm1hcmdlbl9zaW4gICAgIDwtIHN1bShzaW5fZHVwbGljYWRvcyR1dGlsaWRhZF9vcGVyYXRpdmEpIC8gc3VtKHNpbl9kdXBsaWNhZG9zJHZlbnRhc190b3RhbCkgKiAxMDANCg0KIyA1LiBDaXVkYWRlcyBjb24gZWwgbWlzbW8gbm9tYnJlIGVuIGVzdGFkb3MgZGlzdGludG9zDQpob21vbmltYXMgPC0gZGF0b3MgJT4lIGRpc3RpbmN0KGNpdWRhZCwgZXN0YWRvKSAlPiUgY291bnQoY2l1ZGFkKSAlPiUgZmlsdGVyKG4gPiAxKQ0KDQojIDYuIFZhbG9yZXMgYXTDrXBpY29zIGNvbiBsYSByZWdsYSBkZSBUdWtleSAocG9yIGZ1ZXJhIGRlIFExIC0gMSw1IFJJQyB5IFEzICsgMSw1IFJJQykNCmxpbWl0ZV92ZW50YXMgPC0gcXVhbnRpbGUoZGF0b3MkdmVudGFzX3RvdGFsLCAwLjc1KSArIDEuNSAqIElRUihkYXRvcyR2ZW50YXNfdG90YWwpDQphdGlwaWNvc192ZW50YXMgPC0gZGF0b3MgJT4lIGZpbHRlcih2ZW50YXNfdG90YWwgPiBsaW1pdGVfdmVudGFzKQ0KbGltaXRlc19tYXJnZW4gPC0gcXVhbnRpbGUoZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgYygwLjI1LCAwLjc1KSkgKyBjKC0xLjUsIDEuNSkgKiBJUVIoZGF0b3MkbWFyZ2VuX29wZXJhdGl2bykNCmF0aXBpY29zX21hcmdlbiA8LSBzdW0oZGF0b3MkbWFyZ2VuX29wZXJhdGl2byA8IGxpbWl0ZXNfbWFyZ2VuWzFdIHwgZGF0b3MkbWFyZ2VuX29wZXJhdGl2byA+IGxpbWl0ZXNfbWFyZ2VuWzJdKQ0KDQojIDcuIFJlZ2lzdHJvcyBwb3IgY29tYmluYWNpw7NuIGNpdWRhZCArIHByb2R1Y3RvICsgbcOpdG9kbyAowr9sYSBiYXNlIHBhcmVjZSBtZW5zdWFsPykNCnJlZ2lzdHJvc19jb21iaW5hY2lvbiA8LSBkYXRvcyAlPiUgY291bnQoY2l1ZGFkLCBlc3RhZG8sIHByb2R1Y3RvLCBtZXRvZG9fdmVudGEpDQpmaWxhc18xMl8yNCA8LSBzdW0ocmVnaXN0cm9zX2NvbWJpbmFjaW9uJG5bcmVnaXN0cm9zX2NvbWJpbmFjaW9uJG4gJWluJSBjKDEyLCAyNCldKSAvIG5yb3coZGF0b3MpICogMTAwDQoNCiMgVGFibGEgcmVzdW1lbiBkZSBsYSByZXZpc2nDs24NCnJldmlzaW9uIDwtIGRhdGEuZnJhbWUoDQogIGBSZXZpc2nDs25gID0gYygiRm9ybWF0byBkZSBsYXMgdmFyaWFibGVzIiwNCiAgICAgICAgICAgICAgICAgIlZhbG9yZXMgZmFsdGFudGVzIiwNCiAgICAgICAgICAgICAgICAgIlZlbnRhcyBxdWUgbm8gY3VhZHJhbiBjb24gcHJlY2lvIMOXIHVuaWRhZGVzIiwNCiAgICAgICAgICAgICAgICAgIlV0aWxpZGFkIHF1ZSBubyBjdWFkcmEgY29uIHZlbnRhcyDDlyBtYXJnZW4iLA0KICAgICAgICAgICAgICAgICAiUmVnaXN0cm9zIGNvbiB2ZW50YXMgZW4gY2VybyIsDQogICAgICAgICAgICAgICAgICJGaWxhcyBkdXBsaWNhZGFzICgxMSBjb2x1bW5hcyBpZ3VhbGVzKSIsDQogICAgICAgICAgICAgICAgICJDaXVkYWRlcyBjb24gZWwgbWlzbW8gbm9tYnJlIGVuIGRvcyBlc3RhZG9zIiwNCiAgICAgICAgICAgICAgICAgIkF0w61waWNvcyBlbiB2ZW50YXMgcG9yIHJlZ2lzdHJvIiwNCiAgICAgICAgICAgICAgICAgIkF0w61waWNvcyBlbiBtYXJnZW4gcG9yIHJlZ2lzdHJvIiwNCiAgICAgICAgICAgICAgICAgIlJlZ2lzdHJvcyBwb3IgY29tYmluYWNpw7NuIGNpdWRhZCDDlyBwcm9kdWN0byDDlyBtw6l0b2RvIiksDQogIFJlc3VsdGFkbyA9IGMocGFzdGUwKHN1bSh0aXBvcyA9PSAiY2hhcmFjdGVyIiksICIgZGUgdGV4dG8geSAiLCBzdW0odGlwb3MgPT0gIm51bWVyaWMiKSwgIiBudW3DqXJpY2FzIiksDQogICAgICAgICAgICAgICAgZmFsdGFudGVzLA0KICAgICAgICAgICAgICAgIGVycm9yX3ZlbnRhcywNCiAgICAgICAgICAgICAgICBlcnJvcl91dGlsaWRhZCwNCiAgICAgICAgICAgICAgICBpZmVsc2UobnJvdyhjZXJvcykgPiAwLCBucm93KGNlcm9zKSwgIjAgZW4gZWwgYXJjaGl2byBkZSB0cmFiYWpvICg0IGVuIGxhIGJhc2Ugb3JpZ2luYWwpIiksDQogICAgICAgICAgICAgICAgcGFzdGUwKGR1cGxpY2Fkb3MsICIgKCIsIHBjdChkdXBsaWNhZG9zIC8gbnJvdyhkYXRvcykgKiAxMDApLCAiIGRlIGxhcyBmaWxhcykiKSwNCiAgICAgICAgICAgICAgICBwYXN0ZShob21vbmltYXMkY2l1ZGFkLCBjb2xsYXBzZSA9ICIgeSAiKSwNCiAgICAgICAgICAgICAgICBwYXN0ZTAobnVtKG5yb3coYXRpcGljb3NfdmVudGFzKSksICIgcmVnaXN0cm9zICgiLCBwY3QobnJvdyhhdGlwaWNvc192ZW50YXMpIC8gbnJvdyhkYXRvcykgKiAxMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAiKSBwb3IgZW5jaW1hIGRlICIsIHVzZChsaW1pdGVfdmVudGFzKSwgIiwgcXVlIHN1bWFuICIsDQogICAgICAgICAgICAgICAgICAgICAgIHBjdChzdW0oYXRpcGljb3NfdmVudGFzJHZlbnRhc190b3RhbCkgLyBzdW0oZGF0b3MkdmVudGFzX3RvdGFsKSAqIDEwMCksICIgZGUgbGFzIHZlbnRhcyIpLA0KICAgICAgICAgICAgICAgIHBhc3RlMChhdGlwaWNvc19tYXJnZW4sICIgcmVnaXN0cm9zIHBvciBmdWVyYSBkZSAiLCBwY3QobGltaXRlc19tYXJnZW5bMV0gKiAxMDApLCAiIGEgIiwgcGN0KGxpbWl0ZXNfbWFyZ2VuWzJdICogMTAwKSksDQogICAgICAgICAgICAgICAgcGFzdGUwKHBjdChmaWxhc18xMl8yNCksICIgZGUgbGFzIGZpbGFzIGVzdMOhbiBlbiBjb21iaW5hY2lvbmVzIGNvbiBleGFjdGFtZW50ZSAxMiBvIDI0IHJlZ2lzdHJvcyIpKSwNCiAgYERlY2lzacOzbmAgPSBjKCJTZSBmaWrDsyBlbCB0aXBvIGRlIGNhZGEgY29sdW1uYSBhbCBjYXJnYXIgZWwgYXJjaGl2by4gRWwgbWFyZ2VuIHZpZW5lIGNvbW8gcHJvcG9yY2nDs24geSBzZSBwcmVzZW50YSBlbiBwb3JjZW50YWplLiIsDQogICAgICAgICAgICAgICAgICJObyByZXF1aWVyZSB0cmF0YW1pZW50by4iLA0KICAgICAgICAgICAgICAgICAiTGFzIGNpZnJhcyBzb24gY29oZXJlbnRlcyB5IHNlIHVzYW4gdGFsIGNvbW8gdmllbmVuLiIsDQogICAgICAgICAgICAgICAgICJMYXMgY2lmcmFzIHNvbiBjb2hlcmVudGVzIHkgc2UgdXNhbiB0YWwgY29tbyB2aWVuZW4uIiwNCiAgICAgICAgICAgICAgICAgIkxhIGJhc2Ugb3JpZ2luYWwgdGVuw61hIDQgcmVnaXN0cm9zIGRlIEZvb3QgTG9ja2VyIGVuIE9tYWhhIChXb21lbidzIEF0aGxldGljIEZvb3R3ZWFyKSBjb24gMCB1bmlkYWRlcyB5IDAgdmVudGFzLiBTZSBlbGltaW5hcm9uIHBvcnF1ZSBubyByZXByZXNlbnRhbiB1bmEgdmVudGEgeSBzdSBtYXJnZW4gbm8gc2UgcHVlZGUgY2FsY3VsYXIuIE5vIGNhbWJpYW4gbGFzIHZlbnRhcywgbGEgdXRpbGlkYWQgbmkgZWwgbWFyZ2VuIGFncmVnYWRvLiIsDQogICAgICAgICAgICAgICAgIHBhc3RlMCgiU2UgY29uc2VydmFuOiBzaW4gZmVjaGEgbmkgZmFjdHVyYSBubyBoYXkgZXZpZGVuY2lhIGRlIGVycm9yLiBTaW4gZWxsYXMgZWwgbWFyZ2VuIHBhc2Fyw61hIGRlICIsDQogICAgICAgICAgICAgICAgICAgICAgICBwY3QobWFyZ2VuX2NvbiksICIgYSAiLCBwY3QobWFyZ2VuX3NpbiksICIgeSBuaW5ndW5hIGNvbmNsdXNpw7NuIGNhbWJpYS4iKSwNCiAgICAgICAgICAgICAgICAgIkNhZGEgY2l1ZGFkIHNlIGlkZW50aWZpY2Egc2llbXByZSBqdW50byBjb24gc3UgZXN0YWRvLiIsDQogICAgICAgICAgICAgICAgICJTZSBjb25zZXJ2YW46IHNvbiB2ZW50YXMgcmVhbGVzIHkgY29oZXJlbnRlcyBjb24gcHJlY2lvIMOXIHVuaWRhZGVzLiBQb3IgZXNvIGRlc2NyaWJpbW9zIGVsIHJlZ2lzdHJvIHTDrXBpY28gY29uIGxhIG1lZGlhbmEuIiwNCiAgICAgICAgICAgICAgICAgIlNlIGNvbnNlcnZhbjogZWwgcmFuZ28gKDEwJSBhIDgwJSkgZXMgcG9zaWJsZSBlbiB1bmEgb3BlcmFjacOzbiBjb21lcmNpYWwuIiwNCiAgICAgICAgICAgICAgICAgIkVzIGVsIHBhdHLDs24gZGUgdW5hIGJhc2UgbWVuc3VhbCAoMTIgcmVnaXN0cm9zIHBvciBhw7FvKSBzaW4gZmVjaGEuIFJlc3BhbGRhIGNvbnNlcnZhciBsb3MgZHVwbGljYWRvcyB5IGFuYWxpemFyIGNvbiB0b3RhbGVzLiIpLA0KICBjaGVjay5uYW1lcyA9IEZBTFNFKQ0KDQprYWJsZShyZXZpc2lvbiwgY2FwdGlvbiA9ICJSZXZpc2nDs24gZGUgY2FsaWRhZCBkZSBsb3MgZGF0b3MiLCBhbGlnbiA9IGMoImwiLCAibCIsICJsIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBUUlVFLCB3aWR0aCA9ICIxNGVtIikNCmBgYA0KDQoqKkNvbmNsdXNpw7NuIGRlIGxhIHJldmlzacOzbi4qKiBMYSBiYXNlIGVzdMOhIGNvbXBsZXRhIHkgc3VzIGNpZnJhcyBjdWFkcmFuIGVudHJlIHPDrS4gU29sbyBlbGltaW5hbW9zIGxvcyA0IHJlZ2lzdHJvcyBxdWUgbm8gdGVuw61hbiB2ZW50YXMsIGNvbiBsbyBxdWUgcGFzYW1vcyBkZSA5LjY0OCBhIDkuNjQ0IGZpbGFzLiBMb3MgZHVwbGljYWRvcyB5IGxvcyB2YWxvcmVzIGF0w61waWNvcyBsb3MgZGVqYW1vcywgcG9ycXVlIG5vIGhheSBldmlkZW5jaWEgZGUgcXVlIHNlYW4gZXJyb3Jlcy4NCg0KIyAqKkFuw6FsaXNpcyBleHBsb3JhdG9yaW8qKg0KDQojIyBVbmlkYWRlcywgc2VnbWVudG9zLCBwZXJpb2RvIHkgZGVub21pbmFkb3Jlcw0KDQotICoqVW5pZGFkZXM6KiogbGFzIHZlbnRhcyB5IGxhIHV0aWxpZGFkIGVzdMOhbiBlbiBkw7NsYXJlcyAoVVMkKSwgbGFzIGNhbnRpZGFkZXMgZW4gdW5pZGFkZXMgdmVuZGlkYXMgeSBlbCBtYXJnZW4gZW4gcG9yY2VudGFqZS4NCi0gKipTZWdtZW50b3M6KiogNSByZWdpb25lcywgNTAgZXN0YWRvcywgNTQgY2l1ZGFkZXMsIDYgZGlzdHJpYnVpZG9yZXMsIDYgcHJvZHVjdG9zIHkgMyBtw6l0b2RvcyBkZSB2ZW50YSAoSW4tc3RvcmUsIE9ubGluZSB5IE91dGxldCkuDQotICoqUGVyaW9kbzoqKiBsYSBiYXNlIG5vIHRyYWUgZmVjaGEuIFBvciBzdSBlc3RydWN0dXJhIHBhcmVjZSBtZW5zdWFsLCBwZXJvIG5vIHNhYmVtb3MgcXXDqSBtZXNlcyBjdWJyZSwgYXPDrSBxdWUgbG9zIHJlc3VsdGFkb3MgY29ycmVzcG9uZGVuIGFsIHRvdGFsIGFjdW11bGFkbyBkZSBsYSBiYXNlLg0KLSAqKkRlbm9taW5hZG9yZXM6KiogdG9kYXMgbGFzIHBhcnRpY2lwYWNpb25lcyAoJSkgc2UgY2FsY3VsYW4gc29icmUgZWwgdG90YWwgZGUgbGEgYmFzZTogVVMkIDEyMCwyIG1pbGxvbmVzIGVuIHZlbnRhcywgMi40NzguODYxIHVuaWRhZGVzIG8gVVMkIDQ3LDIgbWlsbG9uZXMgZGUgdXRpbGlkYWQuIEN1YW5kbyB1biBwb3JjZW50YWplIHVzYSBvdHJvIGRlbm9taW5hZG9yIChwb3IgZWplbXBsbywgZWwgcGVzbyBkZSB1biBtw6l0b2RvIGRlbnRybyBkZSB1bmEgcmVnacOzbiksIHNlIGluZGljYSBlbiBlbCB0ZXh0by4NCg0KKipJbmRpY2Fkb3JlcyB5IHBvciBxdcOpIGxvcyB1c2Ftb3MuKioNCg0KLSAqKk1hcmdlbiBvcGVyYXRpdm8gYWdyZWdhZG8qKiAodXRpbGlkYWQgdG90YWwgLyB2ZW50YXMgdG90YWxlcyDDlyAxMDApLCBwYXJhIG1lZGlyIGxhIHJlbnRhYmlsaWRhZCByZWFsIGRlIGNhZGEgc2VnbWVudG8uIERhIDM5LDMlOyBlbCBwcm9tZWRpbyBzaW1wbGUgZGUgbG9zIG3DoXJnZW5lcyBwb3IgcmVnaXN0cm8gKDQyLDMlKSBzb2JyZXN0aW1hIGxhIHJlbnRhYmlsaWRhZCBwb3JxdWUgbG9zIHJlZ2lzdHJvcyBwZXF1ZcOxb3MgcGVzYXLDrWFuIGxvIG1pc21vIHF1ZSBsb3MgZ3JhbmRlcywgYXPDrSBxdWUgbm8gbG8gdXNhbW9zLg0KLSAqKlBhcnRpY2lwYWNpw7NuICglKSoqIGVuIHZlbnRhcywgdW5pZGFkZXMgeSB1dGlsaWRhZCwgcGFyYSB2ZXIgcXXDqSBzZWdtZW50b3MgY29uY2VudHJhbiBlbCBuZWdvY2lvLg0KLSAqKk1lZGlhbmEsIGRlc3ZpYWNpw7NuIGVzdMOhbmRhciB5IGNvZWZpY2llbnRlIGRlIHZhcmlhY2nDs24qKiwgcG9ycXVlIGxhcyB2ZW50YXMgdGllbmVuIHZhbG9yZXMgbXV5IGFsdG9zIHF1ZSBpbmZsYW4gZWwgcHJvbWVkaW8geSBxdWVyZW1vcyBtZWRpciBsYSBkaXNwZXJzacOzbi4NCi0gKipVdGlsaWRhZCBwb3IgcmVnaXN0cm8qKiwgcGFyYSBjb21wYXJhciBlbCB0YW1hw7FvIGRlIGxhIHV0aWxpZGFkIGVuIGTDs2xhcmVzIGNvbiBlbCBtYXJnZW4gZW4gcG9yY2VudGFqZS4NCi0gKipDb3JyZWxhY2nDs24gZGUgUGVhcnNvbioqICh5IGRlIFNwZWFybWFuIGVudHJlIGVzdGFkb3MpLCBwYXJhIGRlc2NyaWJpciByZWxhY2lvbmVzIGVudHJlIHZhcmlhYmxlcywgc2luIGFzdW1pciBxdWUgdW5hIGNhdXNhIGxhIG90cmEuDQotICoqTWFyZ2VuIGVzcGVyYWRvIHBvciBsYSBtZXpjbGEgZGUgcHJvZHVjdG8geSBtw6l0b2RvKiosIHBhcmEgc2FiZXIgc2kgdW5hIHJlZ2nDs24gZ2FuYSBtZW5vcyBwb3IgbG8gcXVlIHZlbmRlIG8gcG9yIGPDs21vIGxlIHZhIGNvbiBsbyBxdWUgdmVuZGUuDQoNCiMjIEluZGljYWRvcmVzIGdlbmVyYWxlcw0KDQpgYGB7ciBpbmRpY2Fkb3Jlc30NCnZlbnRhc190b3QgICAgPC0gc3VtKGRhdG9zJHZlbnRhc190b3RhbCkNCnV0aWxpZGFkX3RvdCAgPC0gc3VtKGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSkNCnVuaWRhZGVzX3RvdCAgPC0gc3VtKGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzKQ0KbWFyZ2VuX3RvdCAgICA8LSB1dGlsaWRhZF90b3QgLyB2ZW50YXNfdG90ICogMTAwDQptYXJnZW5fc2ltcGxlIDwtIG1lYW4oZGF0b3MkbWFyZ2VuX29wZXJhdGl2bykgKiAxMDANCg0KdGFibGFfa3BpIDwtIGRhdGEuZnJhbWUoDQogIEluZGljYWRvciA9IGMoIlJlZ2lzdHJvcyIsICJWZW50YXMgdG90YWxlcyIsICJVbmlkYWRlcyB2ZW5kaWRhcyIsICJVdGlsaWRhZCBvcGVyYXRpdmEgdG90YWwiLA0KICAgICAgICAgICAgICAgICJNYXJnZW4gb3BlcmF0aXZvIGFncmVnYWRvICh1dGlsaWRhZCAvIHZlbnRhcykiLCAiUHJvbWVkaW8gc2ltcGxlIGRlIGxvcyBtw6FyZ2VuZXMgKG5vIHNlIHVzYSkiLA0KICAgICAgICAgICAgICAgICJQcmVjaW8gcHJvbWVkaW8gcG9yIHVuaWRhZCAodmVudGFzIC8gdW5pZGFkZXMpIiksDQogIFZhbG9yID0gYyhudW0obnJvdyhkYXRvcykpLCB1c2QodmVudGFzX3RvdCksIG51bSh1bmlkYWRlc190b3QpLCB1c2QodXRpbGlkYWRfdG90KSwNCiAgICAgICAgICAgIHBjdChtYXJnZW5fdG90KSwgcGN0KG1hcmdlbl9zaW1wbGUpLCBwYXN0ZTAoIlVTJCAiLCBudW0odmVudGFzX3RvdCAvIHVuaWRhZGVzX3RvdCwgMikpKSkNCg0Ka2FibGUodGFibGFfa3BpLCBjYXB0aW9uID0gIlRhYmxhIDEuIEluZGljYWRvcmVzIGdlbmVyYWxlcyIsIGFsaWduID0gYygibCIsICJyIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDEuKiogQWRpZGFzIHZlbmRpw7MgVVMkIDEyMCwyIG1pbGxvbmVzIHkgMi40NzguODYxIHVuaWRhZGVzLCBjb24gdW5hIHV0aWxpZGFkIG9wZXJhdGl2YSBkZSBVUyQgNDcsMiBtaWxsb25lcyB5IHVuIG1hcmdlbiBhZ3JlZ2FkbyBkZSAzOSwzJS4gRWwgcHJlY2lvIHByb21lZGlvIGZ1ZSBkZSBVUyQgNDgsNDggcG9yIHVuaWRhZC4NCg0KIyMgVGVuZGVuY2lhIGNlbnRyYWwgeSBkaXNwZXJzacOzbg0KDQoqUHJlZ3VudGE6IMK/Y8OzbW8gZXMgdW4gcmVnaXN0cm8gdMOtcGljbyB5IHF1w6kgdGFudG8gdmFyw61hbiBsYXMgdmFyaWFibGVzPyoNCg0KYGBge3IgZGVzY3JpcHRpdmFzfQ0KdmFyaWFibGVzIDwtIGMoInByZWNpb191bmlkYWQiLCAidW5pZGFkZXNfdmVuZGlkYXMiLCAidmVudGFzX3RvdGFsIiwgInV0aWxpZGFkX29wZXJhdGl2YSIsICJtYXJnZW5fb3BlcmF0aXZvIikNCg0KZGVzY3JpcHRpdmFzIDwtIGRhdGEuZnJhbWUoDQogIFZhcmlhYmxlID0gYygiUHJlY2lvIHBvciB1bmlkYWQgKFVTJCkiLCAiVW5pZGFkZXMgdmVuZGlkYXMiLCAiVmVudGFzIHBvciByZWdpc3RybyAoVVMkKSIsDQogICAgICAgICAgICAgICAiVXRpbGlkYWQgcG9yIHJlZ2lzdHJvIChVUyQpIiwgIk1hcmdlbiBwb3IgcmVnaXN0cm8gKCUpIiksDQogIE1lZGlhICAgID0gc2FwcGx5KGRhdG9zW3ZhcmlhYmxlc10sIG1lYW4pLA0KICBNZWRpYW5hICA9IHNhcHBseShkYXRvc1t2YXJpYWJsZXNdLCBtZWRpYW4pLA0KICBgRGVzdi4gZXN0w6FuZGFyYCA9IHNhcHBseShkYXRvc1t2YXJpYWJsZXNdLCBzZCksDQogIGBNw61uaW1vYCA9IHNhcHBseShkYXRvc1t2YXJpYWJsZXNdLCBtaW4pLA0KICBgTcOheGltb2AgPSBzYXBwbHkoZGF0b3NbdmFyaWFibGVzXSwgbWF4KSwNCiAgY2hlY2submFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gTlVMTCkNCg0KIyBFbCBtYXJnZW4gc2UgbXVlc3RyYSBlbiBwb3JjZW50YWplDQpkZXNjcmlwdGl2YXNbNSwgMjo2XSA8LSBkZXNjcmlwdGl2YXNbNSwgMjo2XSAqIDEwMA0KZGVzY3JpcHRpdmFzJGBDb2VmLiBkZSB2YXJpYWNpw7NuYCA8LSBkZXNjcmlwdGl2YXMkYERlc3YuIGVzdMOhbmRhcmAgLyBkZXNjcmlwdGl2YXMkTWVkaWENCg0KZGVzY3JpcHRpdmFzICU+JQ0KICBtdXRhdGUoYWNyb3NzKE1lZGlhOmBNw6F4aW1vYCwgfiBudW0oLngsIDEpKSwgYENvZWYuIGRlIHZhcmlhY2nDs25gID0gbnVtKGBDb2VmLiBkZSB2YXJpYWNpw7NuYCwgMikpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDIuIE1lZGlkYXMgZGUgdGVuZGVuY2lhIGNlbnRyYWwgeSBkaXNwZXJzacOzbiBwb3IgcmVnaXN0cm8iLCBhbGlnbiA9IGMoImwiLCByZXAoInIiLCA2KSkpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDIuKiogVW4gcmVnaXN0cm8gdMOtcGljbyAobGEgbWVkaWFuYSkgdGllbmUgMTc2IHVuaWRhZGVzLCBVUyQgNy44MTIgZW4gdmVudGFzIHkgVVMkIDMuMjY0IGRlIHV0aWxpZGFkLiBFbCBwcm9tZWRpbyBkZSBsYXMgdmVudGFzIChVUyQgMTIuNDYwKSBlc3TDoSBtdXkgcG9yIGVuY2ltYSBkZSBsYSBtZWRpYW5hIHkgc3UgY29lZmljaWVudGUgZGUgdmFyaWFjacOzbiBlcyAxLDAyLCBsbyBxdWUgaW5kaWNhIHF1ZSBoYXkgcmVnaXN0cm9zIG11eSBncmFuZGVzIHF1ZSBqYWxhbiBlbCBwcm9tZWRpbyBoYWNpYSBhcnJpYmEuIEVsIG1hcmdlbiwgZW4gY2FtYmlvLCB2YXLDrWEgcG9jbyAoY29lZmljaWVudGUgZGUgMCwyMykuDQoNCiMjIERpc3RyaWJ1Y2nDs24geSB2YWxvcmVzIGF0w61waWNvcw0KDQoqUHJlZ3VudGE6IMK/bGFzIHZlbnRhcyBwb3IgcmVnaXN0cm8gc29uIHBhcmVjaWRhcyBlbnRyZSBzw60gbyBoYXkgdW5vcyBwb2NvcyByZWdpc3Ryb3MgbXV5IGdyYW5kZXM/Kg0KDQpgYGB7ciBoaXN0b2dyYW1hfQ0KZ2dwbG90bHkoDQogIGdncGxvdChkYXRvcywgYWVzKHggPSB2ZW50YXNfdG90YWwpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1MDAwLCBib3VuZGFyeSA9IDAsIGZpbGwgPSAic3RlZWxibHVlIiwgY29sb3IgPSAid2hpdGUiKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVkaWFuKGRhdG9zJHZlbnRhc190b3RhbCksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXkyMCIpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKGRhdG9zJHZlbnRhc190b3RhbCksIGxpbmV0eXBlID0gImRvdHRlZCIsIGNvbG9yID0gImdyZXkyMCIpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsaW1pdGVfdmVudGFzLCBjb2xvciA9ICJncmV5NTAiKSArDQogICAgbGFicyh0aXRsZSA9ICJGaWd1cmEgMS4gRGlzdHJpYnVjacOzbiBkZSBsYXMgdmVudGFzIHBvciByZWdpc3RybyIsDQogICAgICAgICB4ID0gIlZlbnRhcyBwb3IgcmVnaXN0cm8gKFVTJCkiLCB5ID0gIk7Dum1lcm8gZGUgcmVnaXN0cm9zIiwNCiAgICAgICAgIGNhcHRpb24gPSAiTMOtbmVhIGRpc2NvbnRpbnVhOiBtZWRpYW5hLiBMw61uZWEgcHVudGVhZGE6IG1lZGlhLiBMw61uZWEgY29udGludWE6IGzDrW1pdGUgZGUgYXTDrXBpY29zLiIpICsNCiAgICB0ZW1hDQopDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIEZpZ3VyYSAxLioqIExhIG1heW9yw61hIGRlIGxvcyByZWdpc3Ryb3Mgc2UgY29uY2VudHJhIGVuIHZhbG9yZXMgYmFqb3MgeSBoYXkgdW5hIGNvbGEgbGFyZ2EgaGFjaWEgbGEgZGVyZWNoYSAoYXNpbWV0csOtYSBwb3NpdGl2YSk6IGxhIG1heW9yw61hIGRlIHJlZ2lzdHJvcyB2ZW5kZSBtZW5vcyBkZSBVUyQgMTUgbWlsLCBwZXJvIGhheSA3OTkgcmVnaXN0cm9zICg4LDMlKSBwb3IgZW5jaW1hIGRlIFVTJCAzMy41NzggcXVlIHN1bWFuIGVsIDMwLDQlIGRlIGxhcyB2ZW50YXMuIExhIGzDrW5lYSBkaXNjb250aW51YSBlcyBsYSBtZWRpYW5hLCBsYSBwdW50ZWFkYSBlcyBsYSBtZWRpYSB5IGxhIGNvbnRpbnVhIGVzIGVsIGzDrW1pdGUgYSBwYXJ0aXIgZGVsIGN1YWwgdW4gcmVnaXN0cm8gc2UgY29uc2lkZXJhIGF0w61waWNvLiBTb24gdmVudGFzIHJlYWxlcywgYXPDrSBxdWUgbGFzIGNvbnNlcnZhbW9zLCB5IHBvciBlc28gcHJlZmVyaW1vcyBsYSBtZWRpYW5hIHBhcmEgZGVzY3JpYmlyIHVuIHJlZ2lzdHJvIHTDrXBpY28geSBsb3MgdG90YWxlcyBwYXJhIGNvbXBhcmFyIHNlZ21lbnRvcy4NCg0KIyMgUGFydGljaXBhY2nDs24gcG9yIHNlZ21lbnRvcw0KDQoqUHJlZ3VudGE6IMK/cXXDqSBzZWdtZW50b3MgY29uY2VudHJhbiBsYXMgdmVudGFzLCBsYXMgdW5pZGFkZXMgeSBsYSB1dGlsaWRhZD8qDQoNCmBgYHtyIHJlZ2lvbmVzfQ0KcmVzdW1lbl9yZWdpb24gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbikgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHVuaWRhZGVzID0gc3VtKHVuaWRhZGVzX3ZlbmRpZGFzKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwYXJ0X3ZlbnRhcyA9IHZlbnRhcyAvIHN1bSh2ZW50YXMpICogMTAwLA0KICAgICAgICAgcGFydF91bmlkYWRlcyA9IHVuaWRhZGVzIC8gc3VtKHVuaWRhZGVzKSAqIDEwMCwNCiAgICAgICAgIHBhcnRfdXRpbGlkYWQgPSB1dGlsaWRhZCAvIHN1bSh1dGlsaWRhZCkgKiAxMDApICU+JQ0KICBhcnJhbmdlKGRlc2ModmVudGFzKSkNCg0KZ3JhZmljb19yZWdpb24gPC0gcmVzdW1lbl9yZWdpb24gJT4lDQogIHNlbGVjdChyZWdpb24sIHBhcnRfdmVudGFzLCBwYXJ0X3V0aWxpZGFkLCBtYXJnZW4pICU+JQ0KICBwaXZvdF9sb25nZXIoYyhwYXJ0X3ZlbnRhcywgcGFydF91dGlsaWRhZCksIG5hbWVzX3RvID0gIm1lZGlkYSIsIHZhbHVlc190byA9ICJ2YWxvciIpICU+JQ0KICBtdXRhdGUobWVkaWRhID0gZmFjdG9yKGlmZWxzZShtZWRpZGEgPT0gInBhcnRfdmVudGFzIiwgIiUgZGUgbGFzIHZlbnRhcyIsICIlIGRlIGxhIHV0aWxpZGFkIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiJSBkZSBsYXMgdmVudGFzIiwgIiUgZGUgbGEgdXRpbGlkYWQiKSksDQogICAgICAgICByZWdpb24gPSBmYWN0b3IocmVnaW9uLCBsZXZlbHMgPSByZXN1bWVuX3JlZ2lvbiRyZWdpb24pKQ0KDQpnZ3Bsb3RseSgNCiAgZ2dwbG90KGdyYWZpY29fcmVnaW9uLCBhZXMoeCA9IHJlZ2lvbiwgeSA9IHZhbG9yLCBmaWxsID0gbWVkaWRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKHJlZ2lvbiwgIlxuIiwgbWVkaWRhLCAiOiAiLCBwY3QodmFsb3IpLCAiXG5NYXJnZW46ICIsIHBjdChtYXJnZW4pKSkpICsNCiAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIlIGRlIGxhcyB2ZW50YXMiID0gInN0ZWVsYmx1ZSIsICIlIGRlIGxhIHV0aWxpZGFkIiA9ICJsaWdodHN0ZWVsYmx1ZSIpKSArDQogICAgbGFicyh0aXRsZSA9ICJGaWd1cmEgMi4gUGFydGljaXBhY2nDs24gcG9yIHJlZ2nDs24gZW4gdmVudGFzIHkgdXRpbGlkYWQgKCUpIiwNCiAgICAgICAgIHggPSAiUmVnacOzbiIsIHkgPSAiJSBkZWwgdG90YWwiLCBmaWxsID0gIiIpICsNCiAgICB0ZW1hLA0KICB0b29sdGlwID0gInRleHQiKQ0KYGBgDQoNCioqTGVjdHVyYSBkZSBsYSBGaWd1cmEgMi4qKiBDYWRhIHNlcmllICh2ZW50YXMgeSB1dGlsaWRhZCkgc3VtYSAxMDAlLiBXZXN0IGVzIGxhIHJlZ2nDs24gcXVlIG3DoXMgdmVuZGUgKDMwLDMlKSwgcGVybyBzdSBiYXJyYSBkZSB1dGlsaWRhZCBlcyBtw6FzIGJhamEgKDI3LDYlKS4gRW4gU291dGggcGFzYSBsbyBjb250cmFyaW86IHRpZW5lIGVsIDE3LDElIGRlIGxhcyB2ZW50YXMgeSBlbCAxOSw1JSBkZSBsYSB1dGlsaWRhZC4NCg0KYGBge3IgdGFibGFfcmVnaW9ufQ0KcmVzdW1lbl9yZWdpb24gJT4lDQogIHRyYW5zbXV0ZShgUmVnacOzbmAgPSByZWdpb24sIFZlbnRhcyA9IHVzZCh2ZW50YXMpLCBgJSB2ZW50YXNgID0gcGN0KHBhcnRfdmVudGFzKSwNCiAgICAgICAgICAgIGAlIHVuaWRhZGVzYCA9IHBjdChwYXJ0X3VuaWRhZGVzKSwgYCUgdXRpbGlkYWRgID0gcGN0KHBhcnRfdXRpbGlkYWQpLCBNYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIGJpbmRfcm93cyhkYXRhLmZyYW1lKGBSZWdpw7NuYCA9ICJUb3RhbCIsIFZlbnRhcyA9IHVzZCh2ZW50YXNfdG90KSwNCiAgICAgICAgICAgICAgICAgICAgICAgYCUgdmVudGFzYCA9IHBjdChzdW0ocmVzdW1lbl9yZWdpb24kcGFydF92ZW50YXMpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgYCUgdW5pZGFkZXNgID0gcGN0KHN1bShyZXN1bWVuX3JlZ2lvbiRwYXJ0X3VuaWRhZGVzKSksDQogICAgICAgICAgICAgICAgICAgICAgIGAlIHV0aWxpZGFkYCA9IHBjdChzdW0ocmVzdW1lbl9yZWdpb24kcGFydF91dGlsaWRhZCkpLA0KICAgICAgICAgICAgICAgICAgICAgICBNYXJnZW4gPSBwY3QobWFyZ2VuX3RvdCksIGNoZWNrLm5hbWVzID0gRkFMU0UpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAzLiBWZW50YXMsIHVuaWRhZGVzLCB1dGlsaWRhZCB5IG1hcmdlbiBwb3IgcmVnacOzbiIsIGFsaWduID0gYygibCIsIHJlcCgiciIsIDUpKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkgJT4lDQogIHJvd19zcGVjKDYsIGJvbGQgPSBUUlVFKQ0KYGBgDQoNCioqTGVjdHVyYSBkZSBsYSBUYWJsYSAzLioqIExhIGZpbGEgZGUgdG90YWwgY29uZmlybWEgcXVlIGxhcyBwYXJ0aWNpcGFjaW9uZXMgc3VtYW4gMTAwJS4gV2VzdCB0aWVuZSBlbCBtZW5vciBtYXJnZW4gKDM1LDclKSB5IFNvdXRoIGVsIG1heW9yICg0NCw4JSkuIFNvdXRoIHBlc2EgbcOhcyBlbiB1bmlkYWRlcyAoMTksOSUpIHF1ZSBlbiB2ZW50YXMgKDE3LDElKSBwb3JxdWUgdmVuZGUgYSBwcmVjaW9zIG3DoXMgYmFqb3MuDQoNCmBgYHtyIHByb2R1Y3RvX2Rpc3RyaWJ1aWRvcn0NCnBhcnRpY2lwYWNpb24gPC0gZnVuY3Rpb24oYmFzZSwgY29sdW1uYSwgbm9tYnJlKSB7DQogIHRhYmxhIDwtIGJhc2UgJT4lDQogICAgZ3JvdXBfYnkoY2F0ZWdvcmlhID0gLmRhdGFbW2NvbHVtbmFdXSkgJT4lDQogICAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLCB1bmlkYWRlcyA9IHN1bSh1bmlkYWRlc192ZW5kaWRhcyksIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkpICU+JQ0KICAgIGFycmFuZ2UoZGVzYyh2ZW50YXMpKQ0KICB0b3RhbCA8LSBkYXRhLmZyYW1lKGNhdGVnb3JpYSA9ICJUb3RhbCIsIHZlbnRhcyA9IHN1bSh0YWJsYSR2ZW50YXMpLA0KICAgICAgICAgICAgICAgICAgICAgIHVuaWRhZGVzID0gc3VtKHRhYmxhJHVuaWRhZGVzKSwgdXRpbGlkYWQgPSBzdW0odGFibGEkdXRpbGlkYWQpKQ0KICBiaW5kX3Jvd3ModGFibGEsIHRvdGFsKSAlPiUNCiAgICBtdXRhdGUoRGltZW5zacOzbiA9IG5vbWJyZSwNCiAgICAgICAgICAgYCUgdmVudGFzYCA9IHBjdCh2ZW50YXMgLyBzdW0odGFibGEkdmVudGFzKSAqIDEwMCksDQogICAgICAgICAgIGAlIHVuaWRhZGVzYCA9IHBjdCh1bmlkYWRlcyAvIHN1bSh0YWJsYSR1bmlkYWRlcykgKiAxMDApLA0KICAgICAgICAgICBgJSB1dGlsaWRhZGAgPSBwY3QodXRpbGlkYWQgLyBzdW0odGFibGEkdXRpbGlkYWQpICogMTAwKSwNCiAgICAgICAgICAgTWFyZ2VuID0gcGN0KHV0aWxpZGFkIC8gdmVudGFzICogMTAwKSkNCn0NCg0KYmluZF9yb3dzKHBhcnRpY2lwYWNpb24oZGF0b3MsICJwcm9kdWN0byIsICJQcm9kdWN0byIpLA0KICAgICAgICAgIHBhcnRpY2lwYWNpb24oZGF0b3MsICJkaXN0cmlidWlkb3IiLCAiRGlzdHJpYnVpZG9yIikpICU+JQ0KICBzZWxlY3QoRGltZW5zacOzbiwgQ2F0ZWdvcsOtYSA9IGNhdGVnb3JpYSwgYCUgdmVudGFzYCwgYCUgdW5pZGFkZXNgLCBgJSB1dGlsaWRhZGAsIE1hcmdlbikgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgNC4gUGFydGljaXBhY2nDs24gcG9yIHByb2R1Y3RvIHkgcG9yIGRpc3RyaWJ1aWRvciIsIGFsaWduID0gYygibCIsICJsIiwgcmVwKCJyIiwgNCkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKSAlPiUNCiAgcm93X3NwZWMoYyg3LCAxNCksIGJvbGQgPSBUUlVFKSAlPiUNCiAgY29sbGFwc2Vfcm93cyhjb2x1bW5zID0gMSwgdmFsaWduID0gInRvcCIpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDQuKiogTWVuJ3MgU3RyZWV0IEZvb3R3ZWFyIGVzIGVsIHByb2R1Y3RvIHF1ZSBtw6FzIHZlbmRlICgyMywwJSkgeSB0YW1iacOpbiBlbCBkZSBtZWpvciBtYXJnZW4gKDQyLDAlKS4gTWVuJ3MgQXRobGV0aWMgRm9vdHdlYXIgZXMgZWwgdGVyY2VybyBlbiB2ZW50YXMsIHBlcm8gdGllbmUgZWwgbWFyZ2VuIG3DoXMgYmFqbyAoMzYsMSUpLiBFbnRyZSBkaXN0cmlidWlkb3JlcywgV2VzdCBHZWFyIHkgRm9vdCBMb2NrZXIgc3VtYW4gbcOhcyBkZSBsYSBtaXRhZCBkZSBsYXMgdmVudGFzICg1MSwyJSksIHkgU3BvcnRzIERpcmVjdCBlcyBlbCBxdWUgbWVqb3IgbWFyZ2VuIHRpZW5lICg0MywyJSkuDQoNCiMjIE3DqXRvZG9zIGRlIHZlbnRhIHkgdmFyaWFiaWxpZGFkDQoNCipQcmVndW50YTogwr9xdcOpIG3DqXRvZG8gZGUgdmVudGEgZGVqYSBtZWpvciBtYXJnZW4sIGVzIGlndWFsIGVuIHRvZGFzIGxhcyByZWdpb25lcyB5IGTDs25kZSB2YXLDrWEgbcOhcyBlbCBtYXJnZW4/Kg0KDQpgYGB7ciByZWdpb25fbWV0b2RvfQ0KcmVnaW9uX21ldG9kbyA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uLCBtZXRvZG9fdmVudGEpICU+JQ0KICBzdW1tYXJpc2UodmVudGFzID0gc3VtKHZlbnRhc190b3RhbCksDQogICAgICAgICAgICB1dGlsaWRhZCA9IHN1bSh1dGlsaWRhZF9vcGVyYXRpdmEpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiAgbXV0YXRlKG1hcmdlbiA9IHV0aWxpZGFkIC8gdmVudGFzICogMTAwLA0KICAgICAgICAgbWV6Y2xhID0gdmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDApICU+JQ0KICB1bmdyb3VwKCkNCg0KZ2dwbG90bHkoDQogIGdncGxvdChyZWdpb25fbWV0b2RvLCBhZXMoeCA9IG1ldG9kb192ZW50YSwgeSA9IHJlZ2lvbiwgZmlsbCA9IG1hcmdlbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKHJlZ2lvbiwgIiDCtyAiLCBtZXRvZG9fdmVudGEsICJcbk1hcmdlbjogIiwgcGN0KG1hcmdlbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXG5QZXNvIGVuIGxhcyB2ZW50YXMgZGUgbGEgcmVnacOzbjogIiwgcGN0KG1lemNsYSkpKSkgKw0KICAgIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIpICsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHBjdChtYXJnZW4pLCAiXG4oIiwgcGN0KG1lemNsYSksICIgZGUgdmVudGFzKSIpKSwgc2l6ZSA9IDMpICsNCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjZjJmMmYyIiwgaGlnaCA9ICJzdGVlbGJsdWUiKSArDQogICAgbGFicyh0aXRsZSA9ICJGaWd1cmEgMy4gTWFyZ2VuIHBvciByZWdpw7NuIHkgbcOpdG9kbyBkZSB2ZW50YSAoJSkiLA0KICAgICAgICAgeCA9ICJNw6l0b2RvIGRlIHZlbnRhIiwgeSA9ICJSZWdpw7NuIiwgZmlsbCA9ICJNYXJnZW4gKCUpIikgKw0KICAgIHRlbWEsDQogIHRvb2x0aXAgPSAidGV4dCIpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIEZpZ3VyYSAzLioqIENhZGEgY2VsZGEgbXVlc3RyYSBlbCBtYXJnZW4geSwgZW50cmUgcGFyw6ludGVzaXMsIGVsIHBlc28gZGUgZXNlIG3DqXRvZG8gZW4gbGFzIHZlbnRhcyBkZSBsYSByZWdpw7NuOyBlbiBjYWRhIHJlZ2nDs24gbG9zIHRyZXMgcGVzb3Mgc3VtYW4gMTAwJS4gRW50cmUgbcOhcyBvc2N1cm8gZWwgYXp1bCwgbWF5b3IgZWwgbWFyZ2VuLiBMYSBjb2x1bW5hIE9ubGluZSBlcyBsYSBtw6FzIG9zY3VyYSBlbiB0b2RhcyBsYXMgcmVnaW9uZXMsIHkgbGEgY2VsZGEgY29uIG1lam9yIG1hcmdlbiBlcyBPbmxpbmUgZW4gTm9ydGhlYXN0ICg0OCw2JSksIGF1bnF1ZSBhbGzDrSBzb2xvIHBlc2EgZWwgMTgsNCUgZGUgbGFzIHZlbnRhcy4gRWwgT3V0bGV0IGdhbmEgNDQsNiUgZW4gU291dGggeSAzMiw4JSBlbiBXZXN0Lg0KDQpgYGB7ciB0YWJsYV9tZXRvZG99DQpyZXN1bWVuX21ldG9kbyA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkobWV0b2RvX3ZlbnRhKSAlPiUNCiAgc3VtbWFyaXNlKHJlZ2lzdHJvcyA9IG4oKSwNCiAgICAgICAgICAgIHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLA0KICAgICAgICAgICAgdW5pZGFkZXMgPSBzdW0odW5pZGFkZXNfdmVuZGlkYXMpLA0KICAgICAgICAgICAgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSkgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIHBhcnRfdmVudGFzID0gdmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDAsDQogICAgICAgICBwYXJ0X3VuaWRhZGVzID0gdW5pZGFkZXMgLyBzdW0odW5pZGFkZXMpICogMTAwLA0KICAgICAgICAgdXRpbGlkYWRfcmVnaXN0cm8gPSB1dGlsaWRhZCAvIHJlZ2lzdHJvcykgJT4lDQogIGFycmFuZ2UoZGVzYyhtYXJnZW4pKQ0KDQpyZXN1bWVuX21ldG9kbyAlPiUNCiAgdHJhbnNtdXRlKGBNw6l0b2RvYCA9IG1ldG9kb192ZW50YSwgYCUgdmVudGFzYCA9IHBjdChwYXJ0X3ZlbnRhcyksIGAlIHVuaWRhZGVzYCA9IHBjdChwYXJ0X3VuaWRhZGVzKSwNCiAgICAgICAgICAgIE1hcmdlbiA9IHBjdChtYXJnZW4pLCBgVXRpbGlkYWQgcHJvbWVkaW8gcG9yIHJlZ2lzdHJvYCA9IHVzZCh1dGlsaWRhZF9yZWdpc3RybykpICU+JQ0KICBiaW5kX3Jvd3MoZGF0YS5mcmFtZShgTcOpdG9kb2AgPSAiVG90YWwiLCBgJSB2ZW50YXNgID0gcGN0KHN1bShyZXN1bWVuX21ldG9kbyRwYXJ0X3ZlbnRhcykpLA0KICAgICAgICAgICAgICAgICAgICAgICBgJSB1bmlkYWRlc2AgPSBwY3Qoc3VtKHJlc3VtZW5fbWV0b2RvJHBhcnRfdW5pZGFkZXMpKSwgTWFyZ2VuID0gcGN0KG1hcmdlbl90b3QpLA0KICAgICAgICAgICAgICAgICAgICAgICBgVXRpbGlkYWQgcHJvbWVkaW8gcG9yIHJlZ2lzdHJvYCA9IHVzZCh1dGlsaWRhZF90b3QgLyBucm93KGRhdG9zKSksIGNoZWNrLm5hbWVzID0gRkFMU0UpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSA1LiBDb21wYXJhY2nDs24gZGUgbG9zIG3DqXRvZG9zIGRlIHZlbnRhIiwgYWxpZ24gPSBjKCJsIiwgcmVwKCJyIiwgNCkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKSAlPiUNCiAgcm93X3NwZWMoNCwgYm9sZCA9IFRSVUUpDQoNCiMgwr9FbiBjdcOhbnRvcyBlc3RhZG9zIGVzIGNhZGEgbcOpdG9kbyBlbCBkZSBtYXlvciBtYXJnZW4/DQpkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoZXN0YWRvLCBtZXRvZG9fdmVudGEpICU+JQ0KICBzdW1tYXJpc2UobWFyZ2VuID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkgLyBzdW0odmVudGFzX3RvdGFsKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGdyb3VwX2J5KGVzdGFkbykgJT4lDQogIHNsaWNlX21heChtYXJnZW4sIG4gPSAxKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBjb3VudChtZXRvZG9fdmVudGEsIG5hbWUgPSAiZXN0YWRvcyIpICU+JQ0KICByZW5hbWUoYE3DqXRvZG8gY29uIG1heW9yIG1hcmdlbmAgPSBtZXRvZG9fdmVudGEsIEVzdGFkb3MgPSBlc3RhZG9zKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJOw7ptZXJvIGRlIGVzdGFkb3Mgc2Vnw7puIGVsIG3DqXRvZG8gY29uIG1heW9yIG1hcmdlbiIsIGFsaWduID0gYygibCIsICJyIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIFRhYmxhIDUuKiogT25saW5lIHRpZW5lIGVsIG1lam9yIG1hcmdlbiAoNDMsNSUpIHkgZXMgZWwgbcOpdG9kbyBtw6FzIHJlbnRhYmxlIGVuIDQyIGRlIGxvcyA1MCBlc3RhZG9zOyBlbiBsb3Mgb3Ryb3MgOCBnYW5hIE91dGxldC4gSW4tc3RvcmUgdGllbmUgZWwgbWFyZ2VuIG3DoXMgYmFqbyAoMzUsOCUpLCBwZXJvIGxhIG1heW9yIHV0aWxpZGFkIHBvciByZWdpc3RybyAoVVMkIDcuMzMzKSBwb3JxdWUgc3VzIHZlbnRhcyBzb24gbcOhcyBncmFuZGVzOiBhbCBjb21wYXJhciB1dGlsaWRhZCBlbiBkw7NsYXJlcyB5IG1hcmdlbiwgZWwgb3JkZW4gc2UgaW52aWVydGUuDQoNCipQcmVndW50YTogwr9sYSBkaWZlcmVuY2lhIGVudHJlIG3DqXRvZG9zIHNlIHZlIHRhbWJpw6luIHJlZ2lzdHJvIGEgcmVnaXN0cm8sIHkgZW4gcXXDqSByZWdpw7NuIHZhcsOtYSBtw6FzIGVsIG1hcmdlbj8qDQoNCmBgYHtyIGJveHBsb3RfbWV0b2RvfQ0KZ2dwbG90bHkoDQogIGdncGxvdChkYXRvcywgYWVzKHggPSBtZXRvZG9fdmVudGEsIHkgPSBtYXJnZW5fb3BlcmF0aXZvICogMTAwKSkgKw0KICAgIGdlb21fYm94cGxvdChmaWxsID0gImxpZ2h0c3RlZWxibHVlIiwgb3V0bGllci5jb2xvdXIgPSAiZ3JleTQwIiwgb3V0bGllci5zaXplID0gMSkgKw0KICAgIGZhY2V0X3dyYXAofnJlZ2lvbiwgbnJvdyA9IDEpICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyYSA0LiBNYXJnZW4gcG9yIHJlZ2lzdHJvIHNlZ8O6biBtw6l0b2RvIHkgcmVnacOzbiIsDQogICAgICAgICB4ID0gTlVMTCwgeSA9ICJNYXJnZW4gb3BlcmF0aXZvICglKSIpICsNCiAgICB0ZW1hICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQopDQpgYGANCg0KKipMZWN0dXJhIGRlIGxhIEZpZ3VyYSA0LioqIENhZGEgY2FqYSByZXN1bWUgZWwgbWFyZ2VuIGRlIGxvcyByZWdpc3Ryb3MgZGUgdW4gbcOpdG9kbyBlbiB1bmEgcmVnacOzbiwgeSBsYSBsw61uZWEgZGVsIG1lZGlvIGVzIGxhIG1lZGlhbmEuIE9ubGluZSB0aWVuZSBsYSBtZWRpYW5hIG3DoXMgYWx0YSBlbiBsYXMgY2luY28gcmVnaW9uZXMgKGVudHJlIDQ0JSB5IDQ5JSksIGUgSW4tc3RvcmUgbGEgbcOhcyBiYWphICgzNSUsIHNhbHZvIGVuIFNvdXRoIGNvbiA0MCUpLg0KDQpgYGB7ciB2YXJpYWJpbGlkYWR9DQojIFZhcmlhYmlsaWRhZCBkZWwgbWFyZ2VuIHBvciByZWdpw7NuDQpkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKG1lZGlhbmEgPSBtZWRpYW4obWFyZ2VuX29wZXJhdGl2bykgKiAxMDAsDQogICAgICAgICAgICBkZXN2aWFjaW9uID0gc2QobWFyZ2VuX29wZXJhdGl2bykgKiAxMDApICU+JQ0KICBhcnJhbmdlKGRlc2MoZGVzdmlhY2lvbikpICU+JQ0KICB0cmFuc211dGUoYFJlZ2nDs25gID0gcmVnaW9uLCBgTWVkaWFuYSBkZWwgbWFyZ2VuICglKWAgPSBudW0obWVkaWFuYSwgMSksIGBEZXN2aWFjacOzbiBlc3TDoW5kYXIgKHB1bnRvcylgID0gbnVtKGRlc3ZpYWNpb24sIDEpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSA2LiBWYXJpYWJpbGlkYWQgZGVsIG1hcmdlbiBwb3IgcmVnaXN0cm8gZW4gY2FkYSByZWdpw7NuIiwgYWxpZ24gPSBjKCJsIiwgInIiLCAiciIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFKQ0KYGBgDQoNCioqTGVjdHVyYSBkZSBsYSBUYWJsYSA2LioqIFdlc3QgZXMgbGEgcmVnacOzbiBkb25kZSBtw6FzIHZhcsOtYSBlbCBtYXJnZW4gZW50cmUgcmVnaXN0cm9zIChkZXN2aWFjacOzbiBkZSAxMCw3IHB1bnRvcyk6IGFsbMOtIGNvbnZpdmVuIHZlbnRhcyBtdXkgcmVudGFibGVzIGNvbiBvdHJhcyBtdXkgcG9jbyByZW50YWJsZXMuIE5vcnRoZWFzdCBlcyBsYSBtw6FzIGVzdGFibGUgKDgsNCBwdW50b3MpLg0KDQojICoqRGlhZ27Ds3N0aWNvIHkgaGFsbGF6Z29zKioNCg0KUHJlc2VudGFtb3MgY2luY28gaGFsbGF6Z29zOyBsb3MgZG9zIHByaW1lcm9zIHNvbiBsb3MgcXVlIG3DoXMgcHJvZnVuZGl6YW1vcy4gRGViYWpvIGRlIGNhZGEgdGFibGEgbyBncsOhZmljbyBlc3TDoSBsYSBldmlkZW5jaWEgY3VhbnRpdGF0aXZhIHkgc3UgaW50ZXJwcmV0YWNpw7NuLCB5IGFsIGZpbmFsIGRlIGNhZGEgaGFsbGF6Z28sIGxhIGltcGxpY2FjacOzbiBwYXJhIGVsIG5lZ29jaW8uDQoNCiMjIEhhbGxhemdvIDEgKHByb2Z1bmRpemFjacOzbikuIFZlbmRlciBtw6FzIG5vIHNpZW1wcmUgZXMgZ2FuYXIgbcOhcw0KDQoqUHJlZ3VudGE6IMK/cXXDqSBlc3RhZG9zIHkgY2l1ZGFkZXMgY29tYmluYW4gbXVjaGFzIHZlbnRhcyBjb24gYnVlbiBtYXJnZW4sIHkgY3XDoWxlcyB2ZW5kZW4gbXVjaG8gcGVybyBnYW5hbiBwb2NvPyoNCg0KUGFyYSBjbGFzaWZpY2FyIGxvcyA1MCBlc3RhZG9zIG5vcyBoYWNlbW9zIGRvcyBwcmVndW50YXMgc2VuY2lsbGFzOg0KDQoxLiAqKsK/RWwgZXN0YWRvIHZlbmRlIG11Y2hvIG8gcG9jbz8qKiBTaSBsb3MgNTAgZXN0YWRvcyB2ZW5kaWVyYW4gbG8gbWlzbW8sIGNhZGEgdW5vIHRlbmRyw61hIGVsIDIlIGRlIGxhcyB2ZW50YXMgKDEwMCUgLyA1MCksIHVub3MgVVMkIDIsNCBtaWxsb25lcy4gVW4gZXN0YWRvIHF1ZSB2ZW5kZSBtw6FzIGRlbCAyJSBlcyB1biBtZXJjYWRvIGdyYW5kZSwgY29tbyBDYWxpZm9ybmlhICg3LDElKTsgdW5vIHF1ZSB2ZW5kZSBtZW5vcyBkZWwgMiUgZXMgdW4gbWVyY2FkbyBwZXF1ZcOxbywgY29tbyBJb3dhICgwLDglKS4NCjIuICoqwr9FbCBlc3RhZG8gZ2FuYSBtdWNobyBvIHBvY28gcG9yIGNhZGEgZMOzbGFyIHZlbmRpZG8/KiogTGEgcmVmZXJlbmNpYSBlcyBlbCBtYXJnZW4gZGUgdG9kYSBsYSBlbXByZXNhLCAzOSwzJS4gVW4gZXN0YWRvIGNvbiBtYXJnZW4gbWF5b3IgZ2FuYSBtw6FzIHF1ZSBlbCBwcm9tZWRpbywgY29tbyBBbGFiYW1hICg1NCw0JSk7IHVubyBjb24gbWFyZ2VuIG1lbm9yIGdhbmEgbWVub3MsIGNvbW8gV2FzaGluZ3RvbiAoMjgsOCUpLg0KDQpBbCBjcnV6YXIgbGFzIGRvcyByZXNwdWVzdGFzIHF1ZWRhbiBjdWF0cm8gZ3J1cG9zOg0KDQp8IHwgKipHYW5hIG3DoXMgcXVlIGVsIHByb21lZGlvKiogKG1hcmdlbiBtYXlvciBhIDM5LDMlKSB8ICoqR2FuYSBtZW5vcyBxdWUgZWwgcHJvbWVkaW8qKiAobWFyZ2VuIG1lbm9yIGEgMzksMyUpIHwNCnwtLS18LS0tfC0tLXwNCnwgKipWZW5kZSBtdWNobyoqIChtw6FzIGRlbCAyJSBkZSBsYXMgdmVudGFzKSB8ICoqRXN0cmVsbGEuKiogRWplbXBsbzogVGV4YXMsIDUsNSUgZGUgbGFzIHZlbnRhcyB5IDQyLDglIGRlIG1hcmdlbiB8ICoqQ29ycmVnaXIuKiogRWplbXBsbzogQ2FsaWZvcm5pYSwgNywxJSBkZSBsYXMgdmVudGFzIHkgMzQsNSUgZGUgbWFyZ2VuIHwNCnwgKipWZW5kZSBwb2NvKiogKG1lbm9zIGRlbCAyJSBkZSBsYXMgdmVudGFzKSB8ICoqT3BvcnR1bmlkYWQuKiogRWplbXBsbzogTWljaGlnYW4sIDEsOSUgZGUgbGFzIHZlbnRhcyB5IDQ1LDklIGRlIG1hcmdlbiB8ICoqQmFqbyBhcG9ydGUuKiogRWplbXBsbzogSW93YSwgMCw4JSBkZSBsYXMgdmVudGFzIHkgMzgsMCUgZGUgbWFyZ2VuIHwNCg0KYGBge3IgZXN0YWRvcywgZmlnLmhlaWdodD03fQ0KcmVzdW1lbl9lc3RhZG8gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbiwgZXN0YWRvKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpLA0KICAgICAgICAgICAgdXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSwNCiAgICAgICAgICAgIHVuaWRhZGVzID0gc3VtKHVuaWRhZGVzX3ZlbmRpZGFzKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIHBhcnRfdmVudGFzID0gdmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDAsDQogICAgICAgICBwcmVjaW9fbWVkaW8gPSB2ZW50YXMgLyB1bmlkYWRlcywNCiAgICAgICAgIGdydXBvID0gY2FzZV93aGVuKHBhcnRfdmVudGFzID49IDIgJiBtYXJnZW4gPj0gbWFyZ2VuX3RvdCB+ICJFc3RyZWxsYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJ0X3ZlbnRhcyA8ICAyICYgbWFyZ2VuID49IG1hcmdlbl90b3QgfiAiT3BvcnR1bmlkYWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFydF92ZW50YXMgPj0gMiAmIG1hcmdlbiA8ICBtYXJnZW5fdG90IH4gIkNvcnJlZ2lyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiQmFqbyBhcG9ydGUiKSwNCiAgICAgICAgIGdydXBvID0gZmFjdG9yKGdydXBvLCBsZXZlbHMgPSBjKCJFc3RyZWxsYSIsICJPcG9ydHVuaWRhZCIsICJDb3JyZWdpciIsICJCYWpvIGFwb3J0ZSIpKSkNCg0KY29sb3Jlc19ncnVwbyA8LSBjKCJFc3RyZWxsYSIgPSAibmF2eSIsICJPcG9ydHVuaWRhZCIgPSAiZGVlcHNreWJsdWUzIiwNCiAgICAgICAgICAgICAgICAgICAiQ29ycmVnaXIiID0gImdyZXkzMCIsICJCYWpvIGFwb3J0ZSIgPSAiZ3JleTcwIikNCg0KIyBFc3RhZG9zIHF1ZSBzZSBub21icmFuIGVuIGVsIGdyw6FmaWNvDQpldGlxdWV0YXMgPC0gYygiQ2FsaWZvcm5pYSIsICJOZXcgWW9yayIsICJGbG9yaWRhIiwgIlRleGFzIiwgIkFsYWJhbWEiLCAiVGVubmVzc2VlIiwNCiAgICAgICAgICAgICAgICJXYXNoaW5ndG9uIiwgIk1pY2hpZ2FuIiwgIklkYWhvIiwgIkxvdWlzaWFuYSIpDQoNCmdncGxvdGx5KA0KICBnZ3Bsb3QocmVzdW1lbl9lc3RhZG8sIGFlcyh4ID0gcGFydF92ZW50YXMsIHkgPSBtYXJnZW4sIGNvbG9yID0gZ3J1cG8sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoZXN0YWRvLCAiICgiLCByZWdpb24sICIpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXG5WZW50YXM6IFVTJCAiLCByb3VuZCh2ZW50YXMgLyAxZTYsIDIpLCAiIE0gKCIsIHBjdChwYXJ0X3ZlbnRhcyksICIgZGVsIHRvdGFsKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuVXRpbGlkYWQ6IFVTJCAiLCByb3VuZCh1dGlsaWRhZCAvIDFlNiwgMiksICIgTSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlxuTWFyZ2VuOiAiLCBwY3QobWFyZ2VuKSkpKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWFyZ2VuX3RvdCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JleTQwIikgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXk0MCIpICsNCiAgICBnZW9tX3BvaW50KGFlcyhzaXplID0gdXRpbGlkYWQgLyAxZTYpLCBhbHBoYSA9IDAuNzUpICsNCiAgICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihyZXN1bWVuX2VzdGFkbywgZXN0YWRvICVpbiUgZXRpcXVldGFzKSwgYWVzKGxhYmVsID0gZXN0YWRvKSwNCiAgICAgICAgICAgICAgbnVkZ2VfeSA9IDEuMywgc2l6ZSA9IDMsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihyZXN1bWVuX2VzdGFkbywgZXN0YWRvID09ICJIYXdhaWkiKSwgYWVzKGxhYmVsID0gZXN0YWRvKSwNCiAgICAgICAgICAgICAgbnVkZ2VfeSA9IC0xLjMsIHNpemUgPSAzLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9yZXNfZ3J1cG8pICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyYSA1LiBFc3RhZG9zOiBwYXJ0aWNpcGFjacOzbiBlbiB2ZW50YXMgdnMuIG1hcmdlbiIsDQogICAgICAgICB4ID0gIiUgZGUgbGFzIHZlbnRhcyB0b3RhbGVzIiwgeSA9ICJNYXJnZW4gb3BlcmF0aXZvIGFncmVnYWRvICglKSIsIGNvbG9yID0gIkdydXBvIiwgc2l6ZSA9ICIiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikNCg0KIyBDb3JyZWxhY2nDs24gZW50cmUgdmVudGFzIHkgdXRpbGlkYWQsIHkgZW50cmUgdmVudGFzIHkgbWFyZ2VuIChwb3IgZXN0YWRvKQ0KZGF0YS5mcmFtZShgUmVsYWNpw7NuIGVudHJlIGVzdGFkb3NgID0gYygiVmVudGFzIHkgdXRpbGlkYWQgKFBlYXJzb24pIiwgIlZlbnRhcyB5IG1hcmdlbiAoU3BlYXJtYW4pIiksDQogICAgICAgICAgIGBDb3JyZWxhY2nDs25gID0gbnVtKGMoY29yKHJlc3VtZW5fZXN0YWRvJHZlbnRhcywgcmVzdW1lbl9lc3RhZG8kdXRpbGlkYWQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yKHJlc3VtZW5fZXN0YWRvJHZlbnRhcywgcmVzdW1lbl9lc3RhZG8kbWFyZ2VuLCBtZXRob2QgPSAic3BlYXJtYW4iKSksIDIpLA0KICAgICAgICAgICBjaGVjay5uYW1lcyA9IEZBTFNFKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJDb3JyZWxhY2lvbmVzIGVudHJlIGxvcyA1MCBlc3RhZG9zIiwgYWxpZ24gPSBjKCJsIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBDYWRhIHB1bnRvIGVzIHVuIGVzdGFkbyB5IHN1IHRhbWHDsW8gZXMgbGEgdXRpbGlkYWQgcXVlIGFwb3J0YS4gRW50cmUgZXN0YWRvcywgdmVudGFzIHkgdXRpbGlkYWQgdGllbmVuIHVuYSBjb3JyZWxhY2nDs24gZGUgMCw5OCwgcGVybyB2ZW50YXMgeSBtYXJnZW4gdGllbmVuIHVuYSBjb3JyZWxhY2nDs24gZGUg4oiSMCwwNS4gVGV4YXMgdmVuZGUgZWwgNSw1JSBkZWwgdG90YWwgY29uIHVuIG1hcmdlbiBkZSA0Miw4JSwgbWllbnRyYXMgQ2FsaWZvcm5pYSB2ZW5kZSBlbCA3LDElIGNvbiAzNCw1JS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogTG9zIGVzdGFkb3MgcXVlIG3DoXMgdmVuZGVuIGFwb3J0YW4gbcOhcyB1dGlsaWRhZCBlbiBkw7NsYXJlcywgcGVybyBlc28gbm8gc2lnbmlmaWNhIHF1ZSBnYW5lbiBtw6FzIHBvciBjYWRhIGTDs2xhciB2ZW5kaWRvLg0KDQpgYGB7ciB0YWJsYV9ncnVwb3N9DQpyZXN1bWVuX2dydXBvIDwtIHJlc3VtZW5fZXN0YWRvICU+JQ0KICBncm91cF9ieShncnVwbykgJT4lDQogIHN1bW1hcmlzZShlc3RhZG9zID0gbigpLA0KICAgICAgICAgICAgdmVudGFzID0gc3VtKHZlbnRhcyksDQogICAgICAgICAgICB1dGlsaWRhZCA9IHN1bSh1dGlsaWRhZCksDQogICAgICAgICAgICBsaXN0YSA9IHBhc3RlKGVzdGFkb1tvcmRlcigtcGFydF92ZW50YXMpXSwgY29sbGFwc2UgPSAiLCAiKSkgJT4lDQogIG11dGF0ZShtYXJnZW4gPSB1dGlsaWRhZCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIHBhcnRfdmVudGFzID0gdmVudGFzIC8gdmVudGFzX3RvdCAqIDEwMCwNCiAgICAgICAgIHBhcnRfdXRpbGlkYWQgPSB1dGlsaWRhZCAvIHV0aWxpZGFkX3RvdCAqIDEwMCwNCiAgICAgICAgIHV0aWxpZGFkX3Bvcl9taWxsb24gPSBtYXJnZW4gLyAxMDAgKiAxZTYpICU+JQ0KICBhcnJhbmdlKGdydXBvKQ0KDQpyZXN1bWVuX2dydXBvICU+JQ0KICB0cmFuc211dGUoR3J1cG8gPSBhcy5jaGFyYWN0ZXIoZ3J1cG8pLCBFc3RhZG9zID0gZXN0YWRvcywgYCUgdmVudGFzYCA9IHBjdChwYXJ0X3ZlbnRhcyksIGAlIHV0aWxpZGFkYCA9IHBjdChwYXJ0X3V0aWxpZGFkKSwNCiAgICAgICAgICAgIE1hcmdlbiA9IHBjdChtYXJnZW4pLCBgVXRpbGlkYWQgcG9yIGNhZGEgVVMkIDEgTSB2ZW5kaWRvYCA9IHVzZCh1dGlsaWRhZF9wb3JfbWlsbG9uKSwNCiAgICAgICAgICAgIGBFc3RhZG9zIChkZSBtYXlvciBhIG1lbm9yIHZlbnRhKWAgPSBsaXN0YSkgJT4lDQogIGJpbmRfcm93cyhkYXRhLmZyYW1lKEdydXBvID0gIlRvdGFsIiwgRXN0YWRvcyA9IHN1bShyZXN1bWVuX2dydXBvJGVzdGFkb3MpLA0KICAgICAgICAgICAgICAgICAgICAgICBgJSB2ZW50YXNgID0gcGN0KHN1bShyZXN1bWVuX2dydXBvJHBhcnRfdmVudGFzKSksIGAlIHV0aWxpZGFkYCA9IHBjdChzdW0ocmVzdW1lbl9ncnVwbyRwYXJ0X3V0aWxpZGFkKSksDQogICAgICAgICAgICAgICAgICAgICAgIE1hcmdlbiA9IHBjdChtYXJnZW5fdG90KSwgYFV0aWxpZGFkIHBvciBjYWRhIFVTJCAxIE0gdmVuZGlkb2AgPSB1c2QobWFyZ2VuX3RvdCAvIDEwMCAqIDFlNiksDQogICAgICAgICAgICAgICAgICAgICAgIGBFc3RhZG9zIChkZSBtYXlvciBhIG1lbm9yIHZlbnRhKWAgPSAiIiwgY2hlY2submFtZXMgPSBGQUxTRSkpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDcuIEdydXBvcyBkZSBlc3RhZG9zIHNlZ8O6biB2ZW50YXMgeSBtYXJnZW4iLCBhbGlnbiA9IGMoImwiLCAiciIsICJyIiwgInIiLCAiciIsICJyIiwgImwiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBUUlVFLCBmb250X3NpemUgPSAxMykgJT4lDQogIHJvd19zcGVjKDUsIGJvbGQgPSBUUlVFKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIExvcyA3IGVzdGFkb3MgRXN0cmVsbGEgdGllbmVuIGVsIDIwLDMlIGRlIGxhcyB2ZW50YXMgeSBlbCAyMywwJSBkZSBsYSB1dGlsaWRhZCwgY29uIHVuIG1hcmdlbiBkZSA0NCw2JS4gTG9zIDExIGVzdGFkb3MgYSBDb3JyZWdpciB0aWVuZW4gZWwgNDAsMSUgZGUgbGFzIHZlbnRhcywgcGVybyBzb2xvIGVsIDM2LDklIGRlIGxhIHV0aWxpZGFkIChtYXJnZW4gZGUgMzYsMSUpLiBMb3MgMTUgZGUgT3BvcnR1bmlkYWQgZ2FuYW4gNDIsNiUuDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIFVuIG1pbGzDs24gZGUgZMOzbGFyZXMgYWRpY2lvbmFsIGRlamEgdW5vcyBVUyQgNDQ2IG1pbCBlbiB1biBlc3RhZG8gRXN0cmVsbGEgeSB1bm9zIFVTJCAzNjEgbWlsIGVuIHVubyBhIENvcnJlZ2lyLCBhc8OtIHF1ZSBjcmVjZXIgZW4gbG9zIHByaW1lcm9zIHJpbmRlIG3DoXMuDQoNCipQcmVndW50YTogwr9lbCBtYXJnZW4gYWx0byBlc3TDoSBhZ3J1cGFkbyBnZW9ncsOhZmljYW1lbnRlPyoNCg0KYGBge3IgbWFwYSwgZmlnLmhlaWdodD02fQ0KbWFwYV9lc3RhZG9zIDwtIHJlc3VtZW5fZXN0YWRvICU+JQ0KICBtdXRhdGUoY29kaWdvID0gc3RhdGUuYWJiW21hdGNoKGVzdGFkbywgc3RhdGUubmFtZSldKQ0KDQpwbG90X2dlbyhtYXBhX2VzdGFkb3MsIGxvY2F0aW9ubW9kZSA9ICJVU0Etc3RhdGVzIikgJT4lDQogIGFkZF90cmFjZSh6ID0gfm1hcmdlbiwgbG9jYXRpb25zID0gfmNvZGlnbywgY29sb3IgPSB+bWFyZ2VuLCBjb2xvcnMgPSAiQmx1ZXMiLA0KICAgICAgICAgICAgdGV4dCA9IH5wYXN0ZTAoZXN0YWRvLCAiPGJyPk1hcmdlbjogIiwgcGN0KG1hcmdlbiksICI8YnI+VmVudGFzOiBVUyQgIiwgcm91bmQodmVudGFzIC8gMWU2LCAyKSwgIiBNPGJyPkdydXBvOiAiLCBncnVwbyksDQogICAgICAgICAgICBob3ZlcmluZm8gPSAidGV4dCIpICU+JQ0KICBjb2xvcmJhcih0aXRsZSA9ICJNYXJnZW4gKCUpIikgJT4lDQogIGxheW91dCh0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj5GaWd1cmEgNi4gTWFyZ2VuIG9wZXJhdGl2byBwb3IgZXN0YWRvICglKTwvYj4iLCBmb250ID0gbGlzdChzaXplID0gMTUpKSwNCiAgICAgICAgIGdlbyA9IGxpc3Qoc2NvcGUgPSAidXNhIiwgcHJvamVjdGlvbiA9IGxpc3QodHlwZSA9ICJhbGJlcnMgdXNhIiksIHNob3dsYWtlcyA9IEZBTFNFKSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBFbnRyZSBtw6FzIG9zY3VybyBlbCBhenVsLCBtYXlvciBlbCBtYXJnZW4uIExvcyBtw6FyZ2VuZXMgbcOhcyBhbHRvcyBlc3TDoW4gZW4gZWwgU3VyIChBbGFiYW1hIDU0LDQlLCBUZW5uZXNzZWUgNDksNSUpIHkgbG9zIG3DoXMgYmFqb3MgZW4gZWwgUGFjw61maWNvOiBIYXdhaWkgKDI4LDUlKSwgV2FzaGluZ3RvbiAoMjgsOCUpIHkgQ2FsaWZvcm5pYSAoMzQsNSUpLiBTZWfDum4gbGEgT2ZpY2luYSBkZSBBbsOhbGlzaXMgRWNvbsOzbWljbyBkZSBFc3RhZG9zIFVuaWRvcyAoQkVBKSwgZW4gMjAyMSBIYXdhaWksIENhbGlmb3JuaWEgeSBOZXcgWW9yayBlcmFuIGxvcyBlc3RhZG9zIGNvbiBlbCBuaXZlbCBkZSBwcmVjaW9zIG3DoXMgYWx0bywgeSBNaXNzaXNzaXBwaSB5IEFsYWJhbWEgZXN0YWJhbiBlbnRyZSBsb3MgbcOhcyBiYWpvcy4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogRWwgbWFyZ2VuIGRlcGVuZGUgbXVjaG8gZGUgbGEgZ2VvZ3JhZsOtYS4gRWwgZGF0byBkZSBsYSBCRUEgc3VnaWVyZSBxdWUgb3BlcmFyIGVuIGxvcyBlc3RhZG9zIGRlIG1hcmdlbiBiYWpvIGN1ZXN0YSBtw6FzLCBhdW5xdWUgY29uIGxhIGJhc2Ugbm8gbG8gcG9kZW1vcyBjb21wcm9iYXIuDQoNCipQcmVndW50YTogwr9xdcOpIGNpdWRhZGVzIGFwb3J0YW4gbcOhcyB1dGlsaWRhZCB5IGNvbiBxdcOpIG1hcmdlbj8qDQoNCmBgYHtyIGNpdWRhZGVzLCBmaWcuaGVpZ2h0PTZ9DQpyZXN1bWVuX2NpdWRhZCA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkocmVnaW9uLCBlc3RhZG8sIGNpdWRhZCkgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwYXJ0X3V0aWxpZGFkID0gdXRpbGlkYWQgLyBzdW0odXRpbGlkYWQpICogMTAwLA0KICAgICAgICAgbm9tYnJlID0gcGFzdGUwKGNpdWRhZCwgIiAoIiwgZXN0YWRvLCAiKSDCtyAiLCBwY3QobWFyZ2VuKSkpICU+JQ0KICBhcnJhbmdlKGRlc2ModXRpbGlkYWQpKQ0KDQojIFBhcnRpY2lwYWNpw7NuIGRlIGxhcyAxMCBjaXVkYWRlcyBjb24gbcOhcyB1dGlsaWRhZA0KdG9wMTBfY2l1ZGFkZXMgPC0gc3VtKHJlc3VtZW5fY2l1ZGFkJHBhcnRfdXRpbGlkYWRbMToxMF0pDQoNCnRvcDE1IDwtIHJlc3VtZW5fY2l1ZGFkICU+JSBzbGljZSgxOjE1KSAlPiUgbXV0YXRlKG5vbWJyZSA9IGZhY3Rvcihub21icmUsIGxldmVscyA9IHJldihub21icmUpKSkNCg0KZ2dwbG90bHkoDQogIGdncGxvdCh0b3AxNSwgYWVzKHggPSB1dGlsaWRhZCAvIDFlNiwgeSA9IG5vbWJyZSwgZmlsbCA9IG1hcmdlbiwNCiAgICAgICAgICAgICAgICAgICAgdGV4dCA9IHBhc3RlMChub21icmUsICJcblV0aWxpZGFkOiBVUyQgIiwgcm91bmQodXRpbGlkYWQgLyAxZTYsIDIpLCAiIE0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcblZlbnRhczogVVMkICIsIHJvdW5kKHZlbnRhcyAvIDFlNiwgMiksICIgTVxuTWFyZ2VuOiAiLCBwY3QobWFyZ2VuKSkpKSArDQogICAgZ2VvbV9jb2woKSArDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiI2M2ZGJlZiIsIGhpZ2ggPSAiIzA4MzA2YiIpICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyYSA3LiBMYXMgMTUgY2l1ZGFkZXMgY29uIG3DoXMgdXRpbGlkYWQgb3BlcmF0aXZhIiwNCiAgICAgICAgIHggPSAiVXRpbGlkYWQgb3BlcmF0aXZhIChtaWxsb25lcyBkZSBVUyQpIiwgeSA9ICJDaXVkYWQgKG1hcmdlbikiLCBmaWxsID0gIk1hcmdlbiAoJSkiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lDQogIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB5ID0gLTAuMTUpKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIEVsIGxhcmdvIGRlIGxhIGJhcnJhIGVzIGxhIHV0aWxpZGFkIGRlIGNhZGEgY2l1ZGFkIHkgZWwgY29sb3Igc3UgbWFyZ2VuIChlbnRyZSBtw6FzIG9zY3VybywgbWF5b3IpLiBMYXMgMTAgcHJpbWVyYXMgY2l1ZGFkZXMgYXBvcnRhbiBlbCAzMiwwJSBkZSBsYSB1dGlsaWRhZC4gTmV3IFlvcmsgeSBTYW4gRnJhbmNpc2NvIHNvbiBsYXMgcXVlIG3DoXMgYXBvcnRhbiwgY29uIG3DoXJnZW5lcyBkZSAzNywzJSB5IDMyLDElLCBtaWVudHJhcyBCaXJtaW5naGFtICg1NCw0JSksIEtub3h2aWxsZSAoNDksNSUpIHkgRGFsbGFzICg0NCw5JSkgY29tYmluYW4gYmllbiB1dGlsaWRhZCB5IG1hcmdlbi4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogTGFzIGNpdWRhZGVzIHF1ZSBtw6FzIHV0aWxpZGFkIGFwb3J0YW4gbm8gc29uIGxhcyBtw6FzIHJlbnRhYmxlcyBwb3IgY2FkYSBkw7NsYXIgdmVuZGlkby4NCg0KKipJbXBsaWNhY2nDs24gcGFyYSBlbCBuZWdvY2lvLioqIFBhcmEgc2VndWlyIGNyZWNpZW5kbyBjb252aWVuZSBwcmlvcml6YXIgbG9zIGVzdGFkb3MgRXN0cmVsbGEgeSBsb3MgZGUgT3BvcnR1bmlkYWQsIHF1ZSB5YSBnYW5hbiA0Miw2JSBhdW5xdWUgdmVuZGFuIHBvY28sIHkgbm8gYnVzY2FyIG3DoXMgdm9sdW1lbiBlbiBsb3MgZXN0YWRvcyBhIENvcnJlZ2lyIGhhc3RhIG1lam9yYXIgc3UgbWFyZ2VuLg0KDQojIyBIYWxsYXpnbyAyIChwcm9mdW5kaXphY2nDs24pLiBXZXN0IGdhbmEgbWVub3MgY29uIGxvIG1pc21vIHF1ZSB2ZW5kZW4gbGFzIGRlbcOhcyByZWdpb25lcw0KDQoqUHJlZ3VudGE6IMK/V2VzdCB0aWVuZSBtZW5vciBtYXJnZW4gcG9ycXVlIHZlbmRlIHByb2R1Y3RvcyBvIGNhbmFsZXMgZGUgYmFqbyBtYXJnZW4sIG8gcG9ycXVlIGdhbmEgbWVub3MgZW4gbG8gbWlzbW8gcXVlIHZlbmRlbiBsYXMgZGVtw6FzIHJlZ2lvbmVzPyoNCg0KUGFyYSByZXNwb25kZXIsIGEgY2FkYSByZWdpc3RybyBsZSBhc2lnbmFtb3MgZWwgbWFyZ2VuIHF1ZSB0aWVuZSBlc2UgbWlzbW8gcHJvZHVjdG8sIGNvbiBlbCBtaXNtbyBtw6l0b2RvIGRlIHZlbnRhLCBhIG5pdmVsIG5hY2lvbmFsLiBDb24gZXNlIG1hcmdlbiBjYWxjdWxhbW9zIGxhIHV0aWxpZGFkIHF1ZSBzZSBlc3BlcmFyw61hLiBTaSBXZXN0IGdhbmFyYSBtZW5vcyBzb2xvIHBvcnF1ZSB2ZW5kZSBwcm9kdWN0b3MgbyBjYW5hbGVzIGRlIG1lbm9yIG1hcmdlbiwgc3UgdXRpbGlkYWQgcmVhbCBzZXLDrWEgcGFyZWNpZGEgYSBsYSBlc3BlcmFkYS4NCg0KYGBge3IgZXNwZXJhZG99DQojIE1hcmdlbiBuYWNpb25hbCBkZSBjYWRhIGNvbWJpbmFjacOzbiBwcm9kdWN0byB4IG3DqXRvZG8NCm1hcmdlbl9yZWZlcmVuY2lhIDwtIGRhdG9zICU+JQ0KICBncm91cF9ieShwcm9kdWN0bywgbWV0b2RvX3ZlbnRhKSAlPiUNCiAgc3VtbWFyaXNlKG1hcmdlbl9yZWYgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSAvIHN1bSh2ZW50YXNfdG90YWwpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQpkYXRvc19yZWYgPC0gZGF0b3MgJT4lDQogIGxlZnRfam9pbihtYXJnZW5fcmVmZXJlbmNpYSwgYnkgPSBjKCJwcm9kdWN0byIsICJtZXRvZG9fdmVudGEiKSkgJT4lDQogIG11dGF0ZSh1dGlsaWRhZF9lc3BlcmFkYSA9IHZlbnRhc190b3RhbCAqIG1hcmdlbl9yZWYpDQoNCmJyZWNoYV9yZWdpb24gPC0gZGF0b3NfcmVmICU+JQ0KICBncm91cF9ieShyZWdpb24pICU+JQ0KICBzdW1tYXJpc2UodmVudGFzID0gc3VtKHZlbnRhc190b3RhbCksDQogICAgICAgICAgICB1dGlsaWRhZF9yZWFsID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksDQogICAgICAgICAgICB1dGlsaWRhZF9lc3AgPSBzdW0odXRpbGlkYWRfZXNwZXJhZGEpKSAlPiUNCiAgbXV0YXRlKG1hcmdlbl9yZWFsID0gdXRpbGlkYWRfcmVhbCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIG1hcmdlbl9lc3BlcmFkbyA9IHV0aWxpZGFkX2VzcCAvIHZlbnRhcyAqIDEwMCwNCiAgICAgICAgIGJyZWNoYV9wcCA9IG1hcmdlbl9yZWFsIC0gbWFyZ2VuX2VzcGVyYWRvLA0KICAgICAgICAgYnJlY2hhX3VzZCA9IHV0aWxpZGFkX3JlYWwgLSB1dGlsaWRhZF9lc3ApICU+JQ0KICBhcnJhbmdlKGJyZWNoYV91c2QpDQoNCmJyZWNoYV9yZWdpb24gJT4lDQogIHRyYW5zbXV0ZShgUmVnacOzbmAgPSByZWdpb24sIGBNYXJnZW4gcmVhbGAgPSBwY3QobWFyZ2VuX3JlYWwpLCBgTWFyZ2VuIGVzcGVyYWRvYCA9IHBjdChtYXJnZW5fZXNwZXJhZG8pLA0KICAgICAgICAgICAgYERpZmVyZW5jaWEgKHB1bnRvcylgID0gbnVtKGJyZWNoYV9wcCwgMSksIGBEaWZlcmVuY2lhIChVUyQpYCA9IHVzZChicmVjaGFfdXNkKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgOC4gTWFyZ2VuIHJlYWwgZnJlbnRlIGFsIGVzcGVyYWRvIHBvciBsYSBtZXpjbGEgZGUgcHJvZHVjdG8geSBtw6l0b2RvIiwgYWxpZ24gPSBjKCJsIiwgInIiLCAiciIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBXZXN0IHRpZW5lIHVuIG1hcmdlbiBkZSAzNSw3JSBjdWFuZG8sIHBvciBsbyBxdWUgdmVuZGUsIHNlIGVzcGVyYXLDrWEgMzgsOSU6IHNvbiAzLDIgcHVudG9zIG1lbm9zLCB1bm9zIFVTJCAxLDE2IG1pbGxvbmVzIGRlIHV0aWxpZGFkLiBTb3V0aCBlc3TDoSA0LDYgcHVudG9zIHBvciBlbmNpbWEgZGUgbG8gZXNwZXJhZG8uDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIFdlc3QgdmVuZGUgcHJvZHVjdG9zIHkgY2FuYWxlcyBwYXJlY2lkb3MgYSBsb3MgZGUgb3RyYXMgcmVnaW9uZXMsIHBlcm8gb2J0aWVuZSBtZW5vcyB1dGlsaWRhZCBjb24gZWxsb3MuDQoNCipQcmVndW50YTogwr9sYSBicmVjaGEgZGUgV2VzdCBlc3TDoSByZXBhcnRpZGEgZW4gdG9kYSBsYSByZWdpw7NuIG8gY29uY2VudHJhZGEgZW4gYWxndW5vcyBlc3RhZG9zPyoNCg0KYGBge3Igd2VzdF9lc3RhZG9zfQ0KYnJlY2hhX3dlc3QgPC0gZGF0b3NfcmVmICU+JQ0KICBmaWx0ZXIocmVnaW9uID09ICJXZXN0IikgJT4lDQogIGdyb3VwX2J5KGVzdGFkbykgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIGJyZWNoYV91c2QgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSAtIHN1bSh1dGlsaWRhZF9lc3BlcmFkYSkpICU+JQ0KICBtdXRhdGUoc2lnbm8gPSBpZmVsc2UoYnJlY2hhX3VzZCA8IDAsICJQb3IgZGViYWpvIGRlIGxvIGVzcGVyYWRvIiwgIlBvciBlbmNpbWEgZGUgbG8gZXNwZXJhZG8iKSwNCiAgICAgICAgIGVzdGFkbyA9IHJlb3JkZXIoZXN0YWRvLCBicmVjaGFfdXNkKSkNCg0KZ2dwbG90bHkoDQogIGdncGxvdChicmVjaGFfd2VzdCwgYWVzKHggPSBicmVjaGFfdXNkIC8gMTAwMCwgeSA9IGVzdGFkbywgZmlsbCA9IHNpZ25vLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKGVzdGFkbywgIlxuRGlmZXJlbmNpYTogVVMkICIsIHJvdW5kKGJyZWNoYV91c2QgLyAxMDAwKSwgIiBtaWwiKSkpICsNCiAgICBnZW9tX2NvbCgpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJQb3IgZGViYWpvIGRlIGxvIGVzcGVyYWRvIiA9ICJmaXJlYnJpY2siLCAiUG9yIGVuY2ltYSBkZSBsbyBlc3BlcmFkbyIgPSAiZm9yZXN0Z3JlZW4iKSkgKw0KICAgIGxhYnModGl0bGUgPSAiRmlndXJhIDguIFdlc3Q6IHV0aWxpZGFkIHJlYWwgbWVub3MgZXNwZXJhZGEsIHBvciBlc3RhZG8iLA0KICAgICAgICAgeCA9ICJNaWxlcyBkZSBVUyQiLCB5ID0gTlVMTCwgZmlsbCA9ICIiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lDQogIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB5ID0gLTAuMTUpKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIEVuIHJvam8gZXN0w6FuIGxvcyBlc3RhZG9zIHF1ZSBnYW5hbiBtZW5vcyBkZSBsbyBlc3BlcmFkbyB5IGVuIHZlcmRlIGxvcyBxdWUgZ2FuYW4gbcOhcy4gQ2FsaWZvcm5pYSwgSGF3YWlpIHkgV2FzaGluZ3RvbiBleHBsaWNhbiBlbCA3OCUgZGVsIGZhbHRhbnRlIGRlIFdlc3QsIG1pZW50cmFzIFV0YWgsIElkYWhvLCBDb2xvcmFkbyB5IE9yZWdvbiBlc3TDoW4gcG9yIGVuY2ltYSBkZSBsbyBlc3BlcmFkby4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogRWwgcHJvYmxlbWEgZXN0w6EgZW4gZXN0YWRvcyBwdW50dWFsZXMgeSBubyBlbiB0b2RhIGxhIHJlZ2nDs24uDQoNCipQcmVndW50YTogwr9xdcOpIHByb2R1Y3RvcyB5IHF1w6kgZGlzdHJpYnVpZG9yZXMgZXhwbGljYW4gbGEgYnJlY2hhPyoNCg0KYGBge3IgcHJvZHVjdG9fcmVnaW9ufQ0KIyBNYXJnZW4gZGUgY2FkYSBwcm9kdWN0byBlbiBjYWRhIHJlZ2nDs24NCmRhdG9zICU+JQ0KICBncm91cF9ieShwcm9kdWN0bywgcmVnaW9uKSAlPiUNCiAgc3VtbWFyaXNlKG1hcmdlbiA9IHN1bSh1dGlsaWRhZF9vcGVyYXRpdmEpIC8gc3VtKHZlbnRhc190b3RhbCkgKiAxMDAsIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gcGN0KG1hcmdlbikpICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVnaW9uLCB2YWx1ZXNfZnJvbSA9IG1hcmdlbikgJT4lDQogIHJlbmFtZShQcm9kdWN0byA9IHByb2R1Y3RvKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSA5LiBNYXJnZW4gb3BlcmF0aXZvIHBvciBwcm9kdWN0byB5IHJlZ2nDs24iLCBhbGlnbiA9IGMoImwiLCByZXAoInIiLCA1KSkpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipFdmlkZW5jaWEuKiogV29tZW4ncyBBcHBhcmVsIGdhbmEgMjksOCUgZW4gV2VzdCB5IDUyLDElIGVuIFNvdXRoLCAyMiBwdW50b3MgZGUgZGlmZXJlbmNpYS4gRXMgbGEgY29tYmluYWNpw7NuIGRlIHJlZ2nDs24geSBwcm9kdWN0byBjb24gZWwgbWF5b3IgZmFsdGFudGUgZnJlbnRlIGEgbG8gZXNwZXJhZG8sIHVub3MgVVMkIDczMyBtaWwuIEVuIFdlc3QgZWwgc2VndW5kbyBwcm9kdWN0byBtw6FzIGTDqWJpbCBlcyBNZW4ncyBBdGhsZXRpYyBGb290d2VhciAoMzEsOSUpLg0KDQoqKkludGVycHJldGFjacOzbi4qKiBVbiBtaXNtbyBwcm9kdWN0byByaW5kZSBtdXkgZGlzdGludG8gc2Vnw7puIGVsIG1lcmNhZG8gZG9uZGUgc2UgdmVuZGUuDQoNCmBgYHtyIGRpc3RyaWJ1aWRvcmVzfQ0KZGlzdHJpYnVpZG9yX3JlZ2lvbiA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yLCByZWdpb24pICU+JQ0KICBzdW1tYXJpc2UobWFyZ2VuID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSkgLyBzdW0odmVudGFzX3RvdGFsKSAqIDEwMCwgLmdyb3VwcyA9ICJkcm9wIikNCg0KcGVvcl9yZWdpb24gPC0gZGlzdHJpYnVpZG9yX3JlZ2lvbiAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yKSAlPiUNCiAgc3VtbWFyaXNlKGBSZWdpw7NuIGRlIG1lbm9yIG1hcmdlbmAgPSByZWdpb25bd2hpY2gubWluKG1hcmdlbildLA0KICAgICAgICAgICAgYERpZmVyZW5jaWEgZW50cmUgc3UgbWVqb3IgeSBzdSBwZW9yIHJlZ2nDs24gKHB1bnRvcylgID0gbnVtKG1heChtYXJnZW4pIC0gbWluKG1hcmdlbiksIDEpKQ0KDQpwZXNvX3dlc3QgPC0gZGF0b3MgJT4lDQogIGZpbHRlcihyZWdpb24gPT0gIldlc3QiKSAlPiUNCiAgZ3JvdXBfYnkoZGlzdHJpYnVpZG9yKSAlPiUNCiAgc3VtbWFyaXNlKHZlbnRhcyA9IHN1bSh2ZW50YXNfdG90YWwpKSAlPiUNCiAgbXV0YXRlKGAlIGRlIGxhcyB2ZW50YXMgZGUgV2VzdGAgPSBwY3QodmVudGFzIC8gc3VtKHZlbnRhcykgKiAxMDApKSAlPiUNCiAgc2VsZWN0KC12ZW50YXMpDQoNCmRpc3RyaWJ1aWRvcl9yZWdpb24gJT4lDQogIG11dGF0ZShtYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSByZWdpb24sIHZhbHVlc19mcm9tID0gbWFyZ2VuLCB2YWx1ZXNfZmlsbCA9ICJTaW4gdmVudGFzIikgJT4lDQogIGxlZnRfam9pbihwZW9yX3JlZ2lvbiwgYnkgPSAiZGlzdHJpYnVpZG9yIikgJT4lDQogIGxlZnRfam9pbihwZXNvX3dlc3QsIGJ5ID0gImRpc3RyaWJ1aWRvciIpICU+JQ0KICByZW5hbWUoRGlzdHJpYnVpZG9yID0gZGlzdHJpYnVpZG9yKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAxMC4gTWFyZ2VuIGRlIGNhZGEgZGlzdHJpYnVpZG9yIHBvciByZWdpw7NuIiwgYWxpZ24gPSBjKCJsIiwgcmVwKCJyIiwgOCkpKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IFRSVUUsIGZvbnRfc2l6ZSA9IDEzKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIENpbmNvIGRlIGxvcyBzZWlzIGRpc3RyaWJ1aWRvcmVzIHRpZW5lbiBzdSBtYXJnZW4gbcOhcyBiYWpvIGVuIFdlc3QuIFdhbG1hcnQsIHBvciBlamVtcGxvLCBnYW5hIDQxLDklIGVuIFNvdXRoIHkgMjcsMyUgZW4gV2VzdC4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogQ29tbyBjYXNpIHRvZG9zIGxvcyBkaXN0cmlidWlkb3JlcyBnYW5hbiBtZW5vcyBhbGzDrSwgbGEgY2F1c2EgcGFyZWNlIGVzdGFyIGVuIGVsIG1lcmNhZG8gZGUgV2VzdCB5IG5vIGVuIHVuIGRpc3RyaWJ1aWRvciBlbiBwYXJ0aWN1bGFyLg0KDQoqKkV4cGxpY2FjaW9uZXMgYWx0ZXJuYXRpdmFzIHkgZGF0b3MgcXVlIGZhbHRhbi4qKiBFbCBtYXJnZW4gYmFqbyBwb2Ryw61hIGRlYmVyc2UgYSAoYSkgY29zdG9zIGRlIG9wZXJhY2nDs24sIGxvZ8Otc3RpY2EgbyBhcnJpZW5kbyBtw6FzIGFsdG9zIGVuIGVzb3MgZXN0YWRvcywgKGIpIGRlc2N1ZW50b3MgbyBwcm9tb2Npb25lcyBkZSBsb3MgZGlzdHJpYnVpZG9yZXMsIG8gKGMpIHVuIGNvc3RvIGRlIHByb2R1Y3RvIGRpc3RpbnRvIGVuIGxhIHJlZ2nDs24uIFBhcmEgc2FiZXIgY3XDoWwgZXMgbGEgY2F1c2Egc2UgbmVjZXNpdGFuIGVsIGNvc3RvIGRlIHZlbnRhcywgbG9zIGRlc2N1ZW50b3MgYXBsaWNhZG9zIHkgbGEgZmVjaGEgZGUgY2FkYSB2ZW50YSwgcXVlIGxhIGJhc2Ugbm8gaW5jbHV5ZS4NCg0KKipJbXBsaWNhY2nDs24gcGFyYSBlbCBuZWdvY2lvLioqIENhbWJpYXIgZGUgZGlzdHJpYnVpZG9yIHByb2JhYmxlbWVudGUgbm8gbWVqb3JhcsOtYSBlbCBtYXJnZW4uIExvIHJlY29tZW5kYWJsZSBlcyByZXZpc2FyIGNvc3RvcyB5IHByZWNpb3MgZW4gQ2FsaWZvcm5pYSwgV2FzaGluZ3RvbiB5IEhhd2FpaSwgZW1wZXphbmRvIHBvciBXb21lbidzIEFwcGFyZWwgeSBwb3IgV2VzdCBHZWFyLCBxdWUgdmVuZGUgZWwgNTEsMyUgZGUgbG8gcXVlIHNlIHZlbmRlIGVuIFdlc3QuDQoNCiMjIEhhbGxhemdvIDMuIEVsIGNhbmFsIGltcG9ydGEsIHkgbm8gcmluZGUgaWd1YWwgZW4gdG9kYXMgcGFydGVzDQoNCipQcmVndW50YTogwr9xdcOpIGNvbWJpbmFjaW9uZXMgZGUgY2l1ZGFkIHkgbcOpdG9kbyBzb24gbGFzIG3DoXMgeSBsYXMgbWVub3MgcmVudGFibGVzPyoNCg0KYGBge3IgY2l1ZGFkX21ldG9kb30NCmNpdWRhZF9tZXRvZG8gPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbiwgY2l1ZGFkLCBlc3RhZG8sIG1ldG9kb192ZW50YSkgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDApICU+JQ0KICBmaWx0ZXIodmVudGFzID49IDUwMDAwMCkgICAjIHNlIGRlamFuIGNvbWJpbmFjaW9uZXMgY29uIHZlbnRhcyByZWxldmFudGVzIChVUyQgNTAwIG1pbCBvIG3DoXMpDQoNCm1lam9yZXMgPC0gY2l1ZGFkX21ldG9kbyAlPiUgYXJyYW5nZShkZXNjKG1hcmdlbikpICU+JSBzbGljZSgxOjgpDQpwZW9yZXMgIDwtIGNpdWRhZF9tZXRvZG8gJT4lIGFycmFuZ2UobWFyZ2VuKSAlPiUgc2xpY2UoMTo4KQ0KDQpiaW5kX3Jvd3MobWVqb3JlcywgcGVvcmVzKSAlPiUNCiAgdHJhbnNtdXRlKENpdWRhZCA9IHBhc3RlMChjaXVkYWQsICIgKCIsIGVzdGFkbywgIikiKSwgYFJlZ2nDs25gID0gcmVnaW9uLCBgTcOpdG9kb2AgPSBtZXRvZG9fdmVudGEsDQogICAgICAgICAgICBWZW50YXMgPSB1c2QodmVudGFzKSwgVXRpbGlkYWQgPSB1c2QodXRpbGlkYWQpLCBNYXJnZW4gPSBwY3QobWFyZ2VuKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgMTEuIENvbWJpbmFjaW9uZXMgY2l1ZGFkIMOXIG3DqXRvZG8gZGUgdmVudGEgY29uIG1heW9yIHkgbWVub3IgbWFyZ2VuICh2ZW50YXMgZGUgVVMkIDUwMCBtaWwgbyBtw6FzKSIsDQogICAgICAgIGFsaWduID0gYygibCIsICJsIiwgImwiLCAiciIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBUUlVFLCBmb250X3NpemUgPSAxMykgJT4lDQogIHBhY2tfcm93cygiTWF5b3IgbWFyZ2VuIiwgMSwgOCkgJT4lDQogIHBhY2tfcm93cygiTWVub3IgbWFyZ2VuIiwgOSwgMTYpDQpgYGANCg0KKipFdmlkZW5jaWEuKiogTGFzIGNvbWJpbmFjaW9uZXMgbcOhcyByZW50YWJsZXMgc29uIHZlbnRhcyBPbmxpbmUgZW4gY2l1ZGFkZXMgZGVsIFN1ciwgY29tbyBEYWxsYXMgKDU0LDglKSwgeSBlbCBPdXRsZXQgZGUgQmlybWluZ2hhbSAoNTIsMyUpLiBPbmxpbmUgdGFtYmnDqW4gZ2FuYSBiaWVuIGVuIGVzdGFkb3MgZGVsIGdydXBvIGEgQ29ycmVnaXIsIGNvbW8gQWxiYW55LCBlbiBOZXcgWW9yayAoNTAsNCUpLiBMYXMgbWVub3MgcmVudGFibGVzIGVzdMOhbiBlbiBXZXN0OiBlbCBPdXRsZXQgZGUgSG9ub2x1bHUgKDI1LDklKSB5IGVsIGRlIFNhbiBGcmFuY2lzY28gKDMwLDElKSwgcXVlIGFkZW3DoXMgdmVuZGUgVVMkIDQsMDcgbWlsbG9uZXMuIEVuIGxhIEZpZ3VyYSAzLCBlbCBPdXRsZXQgZ2FuYSA0NCw2JSBlbiBTb3V0aCB5IDMyLDglIGVuIFdlc3QsIHkgT25saW5lIGdhbmEgNDgsNiUgZW4gTm9ydGhlYXN0IGF1bnF1ZSBwZXNhIHNvbG8gMTgsNCUgZGUgbGFzIHZlbnRhcyBkZSBlc2EgcmVnacOzbi4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogRWwgbWlzbW8gY2FuYWwgcHVlZGUgZGFyIGNhc2kgMTIgcHVudG9zIG3DoXMgZGUgbWFyZ2VuIHNlZ8O6biBlbCBtZXJjYWRvLCB5IE9ubGluZSBmdW5jaW9uYSBiaWVuIGluY2x1c28gZG9uZGUgbGEgcmVnacOzbiBnYW5hIHBvY28uDQoNCioqSW1wbGljYWNpw7NuIHBhcmEgZWwgbmVnb2Npby4qKiBIYXkgZXNwYWNpbyBwYXJhIGltcHVsc2FyIE9ubGluZSBlbiBOb3J0aGVhc3QsIHkgZWwgbW9kZWxvIGRlIE91dGxldCBkZSBTb3V0aCBlcyB1bmEgcHLDoWN0aWNhIHF1ZSB2YWxlIGxhIHBlbmEgZXN0dWRpYXIgcGFyYSBXZXN0Lg0KDQojIyBIYWxsYXpnbyA0IChubyBldmlkZW50ZSBhIHNpbXBsZSB2aXN0YSkuIFByZWNpbywgdm9sdW1lbiB5IHV0aWxpZGFkOiBsbyBxdWUgY2FtYmlhIGFsIGNvbXBhcmFyIGNhc29zIHBhcmVjaWRvcw0KDQoqUHJlZ3VudGE6IMK/Y8OzbW8gc2UgcmVsYWNpb25hbiBwcmVjaW8sIHZvbHVtZW4geSB1dGlsaWRhZCwgeSBsYSByZWxhY2nDs24gcXVlIHNlIHZlIGVuIGVsIHRvdGFsIHNlIG1hbnRpZW5lIHBvciByZWdpw7NuLCBwcm9kdWN0byB5IGVzdGFkbz8qDQoNClVuYSBjb3JyZWxhY2nDs24gZGVsIHRvdGFsIHB1ZWRlIGRlYmVyc2UgYSBsYSBjb21wb3NpY2nDs24gZGUgbGFzIHZlbnRhcy4gUG9yIGVzbyBsYSBjYWxjdWxhbW9zIHRhbWJpw6luICoqZGVudHJvKiogZGUgY2FkYSByZWdpw7NuLCBkZSBjYWRhIHByb2R1Y3RvLCBkZSBjYWRhIGVzdGFkbyB5IGRlIGNhZGEgY29tYmluYWNpw7NuIGRlIGVzdGFkbywgcHJvZHVjdG8geSBtw6l0b2RvLCByZXN0YW5kbyBhIGNhZGEgcmVnaXN0cm8gZWwgcHJvbWVkaW8gZGUgc3UgZ3J1cG8uDQoNCmBgYHtyIHJlbGFjaW9uZXN9DQojIENvcnJlbGFjacOzbiBkZW50cm8gZGUgZ3J1cG9zOiBzZSByZXN0YSBhIGNhZGEgdmFsb3IgZWwgcHJvbWVkaW8gZGUgc3UgZ3J1cG8gYW50ZXMgZGUgY29ycmVsYWNpb25hcg0KY29yX2RlbnRybyA8LSBmdW5jdGlvbih4LCB5LCBncnVwbykgY29yKHggLSBhdmUoeCwgZ3J1cG8pLCB5IC0gYXZlKHksIGdydXBvKSkNCg0KZ3J1cG9fcmVnaW9uICAgPC0gZGF0b3MkcmVnaW9uDQpncnVwb19wcm9kdWN0byA8LSBkYXRvcyRwcm9kdWN0bw0KZ3J1cG9fZXN0YWRvICAgPC0gZGF0b3MkZXN0YWRvDQpncnVwb19maW5vICAgICA8LSBwYXN0ZShkYXRvcyRlc3RhZG8sIGRhdG9zJHByb2R1Y3RvLCBkYXRvcyRtZXRvZG9fdmVudGEpDQoNCnJlbGFjaW9uZXMgPC0gZGF0YS5mcmFtZSgNCiAgYFJlbGFjacOzbmAgPSBjKCJQcmVjaW8geSB1bmlkYWRlcyIsICJQcmVjaW8geSBtYXJnZW4iLCAiVW5pZGFkZXMgeSBtYXJnZW4iLCAiVW5pZGFkZXMgeSB1dGlsaWRhZCIpLA0KICBUb3RhbCA9IGMoY29yKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzKSwNCiAgICAgICAgICAgIGNvcihkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvKSwNCiAgICAgICAgICAgIGNvcihkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2byksDQogICAgICAgICAgICBjb3IoZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSkpLA0KICBgRGVudHJvIGRlIGNhZGEgcmVnacOzbmAgPSBjKGNvcl9kZW50cm8oZGF0b3MkcHJlY2lvX3VuaWRhZCwgZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGdydXBvX3JlZ2lvbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8sIGdydXBvX3JlZ2lvbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19yZWdpb24pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkdXRpbGlkYWRfb3BlcmF0aXZhLCBncnVwb19yZWdpb24pKSwNCiAgYERlbnRybyBkZSBjYWRhIHByb2R1Y3RvYCA9IGMoY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZ3J1cG9fcHJvZHVjdG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJG1hcmdlbl9vcGVyYXRpdm8sIGdydXBvX3Byb2R1Y3RvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fcHJvZHVjdG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3JfZGVudHJvKGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBkYXRvcyR1dGlsaWRhZF9vcGVyYXRpdmEsIGdydXBvX3Byb2R1Y3RvKSksDQogIGBEZW50cm8gZGUgY2FkYSBlc3RhZG9gID0gYyhjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBncnVwb19lc3RhZG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19lc3RhZG8pLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fZXN0YWRvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcl9kZW50cm8oZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSwgZ3J1cG9fZXN0YWRvKSksDQogIGBEZW50cm8gZGUgZXN0YWRvLCBwcm9kdWN0byB5IG3DqXRvZG9gID0gYyhjb3JfZGVudHJvKGRhdG9zJHByZWNpb191bmlkYWQsIGRhdG9zJHVuaWRhZGVzX3ZlbmRpZGFzLCBncnVwb19maW5vKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyRwcmVjaW9fdW5pZGFkLCBkYXRvcyRtYXJnZW5fb3BlcmF0aXZvLCBncnVwb19maW5vKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yX2RlbnRybyhkYXRvcyR1bmlkYWRlc192ZW5kaWRhcywgZGF0b3MkbWFyZ2VuX29wZXJhdGl2bywgZ3J1cG9fZmlubyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcl9kZW50cm8oZGF0b3MkdW5pZGFkZXNfdmVuZGlkYXMsIGRhdG9zJHV0aWxpZGFkX29wZXJhdGl2YSwgZ3J1cG9fZmlubykpLA0KICBjaGVjay5uYW1lcyA9IEZBTFNFKQ0KDQpyZWxhY2lvbmVzICU+JQ0KICBtdXRhdGUoYWNyb3NzKC1gUmVsYWNpw7NuYCwgfiBudW0oLngsIDIpKSkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiVGFibGEgMTIuIENvcnJlbGFjacOzbiBkZSBQZWFyc29uIGVuIGVsIHRvdGFsIHkgZGVudHJvIGRlIHNlZ21lbnRvcyIsIGFsaWduID0gYygibCIsIHJlcCgiciIsIDUpKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBFbiBlbCB0b3RhbCwgdW4gcHJlY2lvIG3DoXMgYWx0byBzZSBhc29jaWEgY29uIG1lbm9yIG1hcmdlbiAo4oiSMCwxNCkuIEVzYSByZWxhY2nDs24gc2UgbWFudGllbmUgZGVudHJvIGRlIGNhZGEgcHJvZHVjdG8gKOKIkjAsMTYpLCBzZSBkZWJpbGl0YSBkZW50cm8gZGUgY2FkYSByZWdpw7NuICjiiJIwLDA3KSB5IGRlc2FwYXJlY2UgZGVudHJvIGRlIGNhZGEgZXN0YWRvICgwLDAwKS4gTGEgcmVsYWNpw7NuIGVudHJlIHByZWNpbyB5IHVuaWRhZGVzIGVzIHBvc2l0aXZhIGVuIHRvZG9zIGxvcyBuaXZlbGVzIChlbnRyZSAwLDI2IHkgMCwzMyksIHkgbGEgZGUgdW5pZGFkZXMgeSBtYXJnZW4gc2UgdnVlbHZlIG3DoXMgZnVlcnRlIGFsIGNvbXBhcmFyIGNhc29zIHBhcmVjaWRvcyAoZGUg4oiSMCwzMSBhIOKIkjAsNDUpLg0KDQoqKkludGVycHJldGFjacOzbi4qKiBMYSByZWxhY2nDs24gbmVnYXRpdmEgZW50cmUgcHJlY2lvIHkgbWFyZ2VuIGVzIHVuIGVmZWN0byBkZSBjb21wb3NpY2nDs246IGFwYXJlY2UgcG9ycXVlIGxvcyBlc3RhZG9zIGNhcm9zIHNvbiBsb3MgZGUgbWFyZ2VuIGJham8uIExhIHJlbGFjacOzbiBlbnRyZSB1bmlkYWRlcyB5IHV0aWxpZGFkICgwLDg3KSBlcyBhbHRhIHBvcnF1ZSBsYSB1dGlsaWRhZCBkZXBlbmRlIGRpcmVjdGFtZW50ZSBkZSBsYXMgdmVudGFzLCBhc8OtIHF1ZSBubyBlcyB1biBoYWxsYXpnbyBlbiBzw60gbWlzbWEuDQoNCipQcmVndW50YTogwr9sb3MgZXN0YWRvcyBjb24gcHJlY2lvIG3DoXMgYWx0byB0aWVuZW4gbWVqb3IgbyBwZW9yIG1hcmdlbj8qDQoNCmBgYHtyIHByZWNpb19tYXJnZW5fZXN0YWRvfQ0KZ2dwbG90bHkoDQogIGdncGxvdChyZXN1bWVuX2VzdGFkbywgYWVzKHggPSBwcmVjaW9fbWVkaW8sIHkgPSBtYXJnZW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoZXN0YWRvLCAiXG5QcmVjaW8gbWVkaW86IFVTJCAiLCByb3VuZChwcmVjaW9fbWVkaW8sIDEpLCAiXG5NYXJnZW46ICIsIHBjdChtYXJnZW4pKSkpICsNCiAgICBnZW9tX3BvaW50KGNvbG9yID0gInN0ZWVsYmx1ZSIsIHNpemUgPSAyLjUsIGFscGhhID0gMC44KSArDQogICAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gMSksIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gImdyZXk0MCIpICsNCiAgICBsYWJzKHRpdGxlID0gIkZpZ3VyYSA5LiBQcmVjaW8gbWVkaW8gdnMuIG1hcmdlbiwgcG9yIGVzdGFkbyIsDQogICAgICAgICB4ID0gIlByZWNpbyBtZWRpbyBwb3IgdW5pZGFkIChVUyQpIiwgeSA9ICJNYXJnZW4gb3BlcmF0aXZvIGFncmVnYWRvICglKSIpICsNCiAgICB0ZW1hLA0KICB0b29sdGlwID0gInRleHQiKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIEVudHJlIGVzdGFkb3MsIGxhIGNvcnJlbGFjacOzbiBlbnRyZSBwcmVjaW8gbWVkaW8geSBtYXJnZW4gZXMg4oiSMCw1Ny4gV2FzaGluZ3RvbiB5IEhhd2FpaSB2ZW5kZW4gYSBVUyQgNjksMSB5IFVTJCA2Nyw3IHBvciB1bmlkYWQsIGNvbiBtw6FyZ2VuZXMgZGUgMjgsOCUgeSAyOCw1JS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogTG9zIG1lcmNhZG9zIG3DoXMgY2Fyb3Mgbm8gc29uIGxvcyBtw6FzIHJlbnRhYmxlcy4gRXN0byBjb25maXJtYSBxdWUgbGEgcmVsYWNpw7NuIG5lZ2F0aXZhIGVudHJlIHByZWNpbyB5IG1hcmdlbiB2aWVuZSBkZSBsYSBtZXpjbGEgZGUgZXN0YWRvcy4NCg0KKlByZWd1bnRhOiBkZW50cm8gZGUgdW4gbWlzbW8gZXN0YWRvLCBwcm9kdWN0byB5IG3DqXRvZG8sIMK/bG9zIHBlcmlvZG9zIGVuIHF1ZSBzZSB2ZW5kZW4gbcOhcyB1bmlkYWRlcyBkZWphbiBtZWpvciBvIHBlb3IgbWFyZ2VuPyoNCg0KUGFyYSBjb21wYXJhciBjYXNvcyBzaW1pbGFyZXMsIG1hcmNhbW9zIGNvbW8gZGUgdm9sdW1lbiBhbHRvIGxvcyByZWdpc3Ryb3MgY3V5YXMgdW5pZGFkZXMgc3VwZXJhbiBsYSBtZWRpYW5hIGRlIHN1IG1pc21vIGVzdGFkbywgcHJvZHVjdG8geSBtw6l0b2RvIGRlIHZlbnRhLg0KDQpgYGB7ciB2b2x1bWVufQ0KZGF0b3Nfdm9sdW1lbiA8LSBkYXRvcyAlPiUNCiAgZ3JvdXBfYnkoZXN0YWRvLCBwcm9kdWN0bywgbWV0b2RvX3ZlbnRhKSAlPiUNCiAgbXV0YXRlKHZvbHVtZW4gPSBpZmVsc2UodW5pZGFkZXNfdmVuZGlkYXMgPiBtZWRpYW4odW5pZGFkZXNfdmVuZGlkYXMpLCAiVm9sdW1lbiBhbHRvIiwgIlZvbHVtZW4gYmFqbyBvIG1lZGlvIikpICU+JQ0KICB1bmdyb3VwKCkNCg0KcmVzdW1lbl92b2x1bWVuIDwtIGRhdG9zX3ZvbHVtZW4gJT4lDQogIGdyb3VwX2J5KG1ldG9kb192ZW50YSwgdm9sdW1lbikgJT4lDQogIHN1bW1hcmlzZSh2ZW50YXMgPSBzdW0odmVudGFzX3RvdGFsKSwNCiAgICAgICAgICAgIHV0aWxpZGFkID0gc3VtKHV0aWxpZGFkX29wZXJhdGl2YSksDQogICAgICAgICAgICB1bmlkYWRlcyA9IHN1bSh1bmlkYWRlc192ZW5kaWRhcyksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBtdXRhdGUobWFyZ2VuID0gdXRpbGlkYWQgLyB2ZW50YXMgKiAxMDAsDQogICAgICAgICBwcmVjaW9fbWVkaW8gPSB2ZW50YXMgLyB1bmlkYWRlcykNCg0KZ2dwbG90bHkoDQogIGdncGxvdChyZXN1bWVuX3ZvbHVtZW4sIGFlcyh4ID0gbWV0b2RvX3ZlbnRhLCB5ID0gbWFyZ2VuLCBmaWxsID0gdm9sdW1lbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAobWV0b2RvX3ZlbnRhLCAiIMK3ICIsIHZvbHVtZW4sICJcbk1hcmdlbjogIiwgcGN0KG1hcmdlbiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcblByZWNpbyBtZWRpbzogVVMkICIsIHJvdW5kKHByZWNpb19tZWRpbywgMSkpKSkgKw0KICAgIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlZvbHVtZW4gYWx0byIgPSAibmF2eSIsICJWb2x1bWVuIGJham8gbyBtZWRpbyIgPSAibGlnaHRzdGVlbGJsdWUiKSkgKw0KICAgIGxhYnModGl0bGUgPSAiRmlndXJhIDEwLiBNYXJnZW4gc2Vnw7puIGVsIHZvbHVtZW4gdmVuZGlkbyIsDQogICAgICAgICB4ID0gIk3DqXRvZG8gZGUgdmVudGEiLCB5ID0gIk1hcmdlbiBvcGVyYXRpdm8gYWdyZWdhZG8gKCUpIiwgZmlsbCA9ICIiKSArDQogICAgdGVtYSwNCiAgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lDQogIGxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB5ID0gLTAuMTUpKQ0KYGBgDQoNCioqRXZpZGVuY2lhLioqIENvbXBhcmFuZG8gcmVnaXN0cm9zIGRlbCBtaXNtbyBlc3RhZG8sIHByb2R1Y3RvIHkgbcOpdG9kbywgZW4gT25saW5lIGxvcyBwZXJpb2RvcyBkZSBtYXlvciB2b2x1bWVuIGdhbmFuIDQxLDMlLCBmcmVudGUgYSA0OSwzJSBlbiBsb3MgZGVtw6FzLiBFbiBPdXRsZXQgbGEgY2HDrWRhIGVzIG1lbm9yICgzNywzJSBmcmVudGUgYSAzOCw5JSkgeSBlbiBJbi1zdG9yZSBubyBoYXkgY2HDrWRhICgzNiwyJSBmcmVudGUgYSAzNSwyJSkuDQoNCioqSW50ZXJwcmV0YWNpw7NuLioqIFZlbmRlciBtw6FzIHVuaWRhZGVzIGVuIHVuIG1pc21vIG1lcmNhZG8gc2UgYXNvY2lhIGNvbiB1biBtYXJnZW4gbWVub3IsIHNvYnJlIHRvZG8gZW4gT25saW5lLg0KDQpgYGB7ciB0YWJsYV92b2x1bWVufQ0KcmVzdW1lbl92b2x1bWVuICU+JQ0KICB0cmFuc211dGUoYE3DqXRvZG9gID0gbWV0b2RvX3ZlbnRhLCBWb2x1bWVuID0gdm9sdW1lbiwgTWFyZ2VuID0gcGN0KG1hcmdlbiksDQogICAgICAgICAgICBgUHJlY2lvIG1lZGlvIHBvciB1bmlkYWRgID0gcGFzdGUwKCJVUyQgIiwgbnVtKHByZWNpb19tZWRpbywgMSkpKSAlPiUNCiAga2FibGUoY2FwdGlvbiA9ICJUYWJsYSAxMy4gTWFyZ2VuIHkgcHJlY2lvIHNlZ8O6biBlbCB2b2x1bWVuIHZlbmRpZG8iLCBhbGlnbiA9IGMoImwiLCAibCIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQoqKkV2aWRlbmNpYS4qKiBFbiBsb3MgcGVyaW9kb3MgZGUgdm9sdW1lbiBhbHRvIGVsIHByZWNpbyBwb3IgdW5pZGFkIGVzIG3DoXMgYWx0bzogZW4gT25saW5lLCBVUyQgNDksMCBmcmVudGUgYSBVUyQgNDUsMS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogTGEgY2HDrWRhIGRlbCBtYXJnZW4gbm8gcGFyZWNlIHZlbmlyIGRlIGRlc2N1ZW50b3MuIFBvZHLDrWEgZGViZXJzZSBhIG1heW9yZXMgY29zdG9zIGFsIGF0ZW5kZXIgbG9zIHBpY29zIGRlIGRlbWFuZGEsIGNvbW8gZW52w61vcyBvIGxvZ8Otc3RpY2EsIHBlcm8gZXMgdW5hIGhpcMOzdGVzaXMuDQoNCioqSW1wbGljYWNpw7NuIHBhcmEgZWwgbmVnb2Npby4qKiBTaSBPbmxpbmUgY3JlY2Ugc29sbyBjb24gbcOhcyB2b2x1bWVuLCBwcm9iYWJsZW1lbnRlIHN1IG1hcmdlbiBiYWplLCBwb3IgZXNvIHByb3BvbmVtb3MgZW1wZXphciBjb24gdW5hIHBydWViYSBjb250cm9sYWRhLiBFc3RhcyBjb3JyZWxhY2lvbmVzIG5vIHBlcm1pdGVuIGRlY2lkaXIgcHJlY2lvczsgcGFyYSBlc28gc2UgbmVjZXNpdGFyw61hbiBkYXRvcyBkZSBjb3N0b3MgeSBsYSBmZWNoYSBkZSBjYWRhIHZlbnRhLg0KDQojIyBIYWxsYXpnbyA1IChubyBldmlkZW50ZSBhIHNpbXBsZSB2aXN0YSkuIFBvY2FzIGNvbWJpbmFjaW9uZXMgZ2VuZXJhbiBsYSBtaXRhZCBkZSBsYSB1dGlsaWRhZA0KDQoqUHJlZ3VudGE6IMK/bGEgdXRpbGlkYWQgZXN0w6EgcmVwYXJ0aWRhIGVudHJlIG11Y2hhcyBjb21iaW5hY2lvbmVzIGRlIGNpdWRhZCwgcHJvZHVjdG8geSBtw6l0b2RvLCBvIGNvbmNlbnRyYWRhIGVuIHBvY2FzPyoNCg0KYGBge3IgY29uY2VudHJhY2lvbn0NCmNvbWJpbmFjaW9uZXMgPC0gZGF0b3MgJT4lDQogIGdyb3VwX2J5KHJlZ2lvbiwgY2l1ZGFkLCBlc3RhZG8sIHByb2R1Y3RvLCBtZXRvZG9fdmVudGEpICU+JQ0KICBzdW1tYXJpc2UodXRpbGlkYWQgPSBzdW0odXRpbGlkYWRfb3BlcmF0aXZhKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGFycmFuZ2UoZGVzYyh1dGlsaWRhZCkpICU+JQ0KICBtdXRhdGUoYWN1bXVsYWRvID0gY3Vtc3VtKHV0aWxpZGFkKSAvIHN1bSh1dGlsaWRhZCkgKiAxMDApDQoNCm5fbWl0YWQgPC0gd2hpY2goY29tYmluYWNpb25lcyRhY3VtdWxhZG8gPj0gNTApWzFdDQoNCmRhdGEuZnJhbWUoSW5kaWNhZG9yID0gYygiQ29tYmluYWNpb25lcyBkZSBjaXVkYWQgw5cgcHJvZHVjdG8gw5cgbcOpdG9kbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIkNvbWJpbmFjaW9uZXMgcXVlIGdlbmVyYW4gZWwgNTAlIGRlIGxhIHV0aWxpZGFkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiUHJvcG9yY2nDs24gZGUgY29tYmluYWNpb25lcyBxdWUgZ2VuZXJhbiBlbCA1MCUgZGUgbGEgdXRpbGlkYWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJSZWdpw7NuIG3DoXMgZnJlY3VlbnRlIGVuIGVzYXMgY29tYmluYWNpb25lcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIk3DqXRvZG8gZGUgdmVudGEgbcOhcyBmcmVjdWVudGUgZW4gZXNhcyBjb21iaW5hY2lvbmVzIiksDQogICAgICAgICAgIFZhbG9yID0gYyhucm93KGNvbWJpbmFjaW9uZXMpLCBuX21pdGFkLCBwY3Qobl9taXRhZCAvIG5yb3coY29tYmluYWNpb25lcykgKiAxMDApLA0KICAgICAgICAgICAgICAgICAgICAgbmFtZXMoc29ydCh0YWJsZShoZWFkKGNvbWJpbmFjaW9uZXMkcmVnaW9uLCBuX21pdGFkKSksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMV0sDQogICAgICAgICAgICAgICAgICAgICBuYW1lcyhzb3J0KHRhYmxlKGhlYWQoY29tYmluYWNpb25lcyRtZXRvZG9fdmVudGEsIG5fbWl0YWQpKSwgZGVjcmVhc2luZyA9IFRSVUUpKVsxXSkpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDE0LiBDb25jZW50cmFjacOzbiBkZSBsYSB1dGlsaWRhZCIsIGFsaWduID0gYygibCIsICJyIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KKipFdmlkZW5jaWEuKiogRGUgNjkwIGNvbWJpbmFjaW9uZXMgZGUgY2l1ZGFkLCBwcm9kdWN0byB5IG3DqXRvZG8sIDEzNCAoZWwgMTklKSBnZW5lcmFuIGxhIG1pdGFkIGRlIGxhIHV0aWxpZGFkLiBXZXN0IGVzIGxhIHJlZ2nDs24gcXVlIG3DoXMgYXBhcmVjZSBlbiBlc2UgZ3J1cG8geSBPbmxpbmUgZWwgbcOpdG9kbyBtw6FzIGZyZWN1ZW50ZS4NCg0KKipJbnRlcnByZXRhY2nDs24uKiogQXVucXVlIFdlc3QgZ2FuZSBtZW5vcyBwb3IgY2FkYSBkw7NsYXIsIHN1cyBuZWdvY2lvcyBncmFuZGVzIHNpZ3VlbiBlbnRyZSBsb3MgcXVlIG3DoXMgdXRpbGlkYWQgYXBvcnRhbiBhIGxhIGVtcHJlc2EuDQoNCioqSW1wbGljYWNpw7NuIHBhcmEgZWwgbmVnb2Npby4qKiBIYXkgcXVlIGN1aWRhciBlc2FzIDEzNCBjb21iaW5hY2lvbmVzIChpbnZlbnRhcmlvLCBkaXNwb25pYmlsaWRhZCB5IHNlcnZpY2lvIGFsIGRpc3RyaWJ1aWRvcikgeSBtZWpvcmFyIGVsIG1hcmdlbiBkZSBXZXN0IHNpbiBwZXJkZXIgc3UgcHJlc2VuY2lhLg0KDQojICoqUmVjb21lbmRhY2lvbmVzIHkgbMOtbWl0ZXMqKg0KDQpDb24gYmFzZSBlbiBsb3MgaGFsbGF6Z29zLCBwcm9wb25lbW9zIGRpcmlnaXIgZWwgY3JlY2ltaWVudG8gZGUgbGFzIHZlbnRhcyBoYWNpYSBsb3MgbWVyY2Fkb3MgeSBjYW5hbGVzIGRvbmRlIGNhZGEgZMOzbGFyIGRlamEgbcOhcyB1dGlsaWRhZCwgeSBtZWpvcmFyIHByaW1lcm8gZWwgbWFyZ2VuIGRlIGxvcyBtZXJjYWRvcyBncmFuZGVzIHF1ZSBob3kgZ2FuYW4gcG9jby4gTGEgY29tYmluYWNpw7NuIHF1ZSBtZXJlY2UgYXRlbmNpw7NuIHByaW9yaXRhcmlhIGVzIFdvbWVuJ3MgQXBwYXJlbCBlbiBXZXN0LCBzb2JyZSB0b2RvIGVuIENhbGlmb3JuaWEsIGRvbmRlIGdhbmEgMTksNiUgZnJlbnRlIGEgdW4gbWFyZ2VuIGVzcGVyYWRvIGRlIDM4LDklIHkgZGVqYSBkZSBjYXB0dXJhciB1bm9zIFVTJCAzMjIgbWlsIChVUyQgNzMzIG1pbCBlbiB0b2RhIGxhIHJlZ2nDs24pLg0KDQojIyBBY2Npb25lcyBwcmlvcml6YWRhcw0KDQp8IEFjY2nDs24gfCBFdmlkZW5jaWEgfCBCZW5lZmljaW8gZXNwZXJhZG8gKGVzY2VuYXJpbyBwcm9wdWVzdG8pIHwgUmllc2dvIG8gc3VwdWVzdG8gfCDDgXJlYSByZXNwb25zYWJsZSB8IEluZGljYWRvciBkZSBzZWd1aW1pZW50byB8DQp8LS0tfC0tLXwtLS18LS0tfC0tLXwtLS18DQp8ICoqMS4gQ3JlY2VyIGVuIGxvcyBlc3RhZG9zIEVzdHJlbGxhIHkgZGUgT3BvcnR1bmlkYWQqKiwgY29uIHByaW9yaWRhZCBlbiBUZXhhcywgQWxhYmFtYSwgVGVubmVzc2VlLCBMb3Vpc2lhbmEsIGxhcyBDYXJvbGluYXMsIE1pY2hpZ2FuIHkgT2hpbyB8IEhhbGxhemdvIDE6IG1hcmdlbiBkZSA0NCw2JSAoRXN0cmVsbGEpIHkgNDIsNiUgKE9wb3J0dW5pZGFkKSwgZnJlbnRlIGEgMzYsMSUgZGUgbG9zIGVzdGFkb3MgYSBDb3JyZWdpciB8IFVub3MgVVMkIDIsMDkgTSAoNCw0JSBkZSBsYSB1dGlsaWRhZCkgc2kgc3VzIHZlbnRhcyBjcmVjZW4gMTAlIHwgUXVlIGVsIG1hcmdlbiBzZSBtYW50ZW5nYSBhbCBjcmVjZXIsIHBvcnF1ZSBlbiBsb3MgcGVyaW9kb3MgZGUgbcOhcyB2b2x1bWVuIGVsIG1hcmdlbiBiYWphIChIYWxsYXpnbyA0KSB8IEdlcmVuY2lhIGNvbWVyY2lhbCB5IE1lcmNhZGVvLCBjb24gRmluYW56YXMgfCBWZW50YXMgeSBtYXJnZW4gZGUgY2FkYSBncnVwbyBkZSBlc3RhZG9zIHwNCnwgKioyLiBQcnVlYmEgcGlsb3RvIGRlIE9ubGluZSBlbiBOb3J0aGVhc3QqKiwgeSBsdWVnbyBlbiBXZXN0IHwgSGFsbGF6Z28gMzogT25saW5lIGdhbmEgNDgsNiUgZW4gTm9ydGhlYXN0LCBwZXJvIHBlc2Egc29sbyAxOCw0JSBkZSBzdXMgdmVudGFzIHwgVW5vcyBVUyQgMCwyNCBNIHNpIGVsIDEwJSBkZSBsYXMgdmVudGFzIEluLXN0b3JlIHkgT3V0bGV0IGRlIE5vcnRoZWFzdCBwYXNhIGEgT25saW5lIHwgRW4gT25saW5lIGVsIG1hcmdlbiBiYWphIDggcHVudG9zIGN1YW5kbyBzdWJlIGVsIHZvbHVtZW4gKEhhbGxhemdvIDQpLCBhc8OtIHF1ZSBlbCBiZW5lZmljaW8gcG9kcsOtYSBzZXIgbWVub3I7IGhheSBxdWUgbWVkaXJsbyBlbiBkw7NsYXJlcyB8IENvbWVyY2lvIGVsZWN0csOzbmljbywgY29uIEZpbmFuemFzIHwgUGVzbyBkZSBPbmxpbmUgZW4gbGFzIHZlbnRhcyBkZSBjYWRhIHJlZ2nDs24geSBtYXJnZW4gcG9yIGNhbmFsIHwNCnwgKiozLiBDb3JyZWdpciBlbCBtYXJnZW4gZW4gQ2FsaWZvcm5pYSwgV2FzaGluZ3RvbiB5IEhhd2FpaSoqLCBlbXBlemFuZG8gcG9yIFdvbWVuJ3MgQXBwYXJlbCwgbG9zIE91dGxldCBkZSBTYW4gRnJhbmNpc2NvIHkgSG9ub2x1bHUsIHkgV2VzdCBHZWFyIHwgSGFsbGF6Z28gMjogV2VzdCBnYW5hIDMsMiBwdW50b3MgbWVub3MgZGUgbG8gZXNwZXJhZG8gKFVTJCAxLDE2IE0pOyBXb21lbidzIEFwcGFyZWwgZ2FuYSAyOSw4JSBlbiBXZXN0IHkgNTIsMSUgZW4gU291dGggfCBVbm9zIFVTJCAwLDU4IE0gc2kgc2UgY2llcnJhIGxhIG1pdGFkIGRlIGxhIGJyZWNoYSBkZSBXZXN0IHwgVG9kYXbDrWEgbm8gY29ub2NlbW9zIGxhIGNhdXNhLiBDb21vIHRvZG9zIGxvcyBkaXN0cmlidWlkb3JlcyBnYW5hbiBtZW5vcyBlbiBXZXN0LCBjYW1iaWFyIGRlIGRpc3RyaWJ1aWRvciBubyBwYXJlY2UgbGEgc29sdWNpw7NuIHwgRmluYW56YXMgY29tZXJjaWFsLCBjb24gbGEgZ2VyZW5jaWEgZGUgV2VzdCB8IE1hcmdlbiBkZSBXZXN0IChob3kgMzUsNyUpIHkgZGUgV29tZW4ncyBBcHBhcmVsIGVuIFdlc3QgKGhveSAyOSw4JSkgfA0KDQpgYGB7ciBlc2NlbmFyaW9zfQ0KIyBFc2NlbmFyaW8gMTogMTAlIG3DoXMgZGUgdmVudGFzIGVuIGVzdGFkb3MgRXN0cmVsbGEgeSBPcG9ydHVuaWRhZCwgY29uIHN1IG1hcmdlbiBhY3R1YWwNCmNyZWNlciA8LSByZXN1bWVuX2VzdGFkbyAlPiUgZmlsdGVyKGdydXBvICVpbiUgYygiRXN0cmVsbGEiLCAiT3BvcnR1bmlkYWQiKSkNCmVzY2VuYXJpb18xIDwtIHN1bShjcmVjZXIkdmVudGFzKSAqIDAuMTAgKiAoc3VtKGNyZWNlciR1dGlsaWRhZCkgLyBzdW0oY3JlY2VyJHZlbnRhcykpDQoNCiMgRXNjZW5hcmlvIDI6IDEwJSBkZSBsYXMgdmVudGFzIEluLXN0b3JlIHkgT3V0bGV0IGRlIE5vcnRoZWFzdCBwYXNhbiBhIE9ubGluZSwgY29uIGxvcyBtw6FyZ2VuZXMgYWN0dWFsZXMNCm5lIDwtIHJlZ2lvbl9tZXRvZG8gJT4lIGZpbHRlcihyZWdpb24gPT0gIk5vcnRoZWFzdCIpDQp2ZW50YXNfbW92aWRhcyA8LSAwLjEwICogc3VtKG5lJHZlbnRhc1tuZSRtZXRvZG9fdmVudGEgIT0gIk9ubGluZSJdKQ0KbWFyZ2VuX29mZmxpbmUgPC0gc3VtKG5lJHV0aWxpZGFkW25lJG1ldG9kb192ZW50YSAhPSAiT25saW5lIl0pIC8gc3VtKG5lJHZlbnRhc1tuZSRtZXRvZG9fdmVudGEgIT0gIk9ubGluZSJdKQ0KbWFyZ2VuX29ubGluZSAgPC0gbmUkdXRpbGlkYWRbbmUkbWV0b2RvX3ZlbnRhID09ICJPbmxpbmUiXSAvIG5lJHZlbnRhc1tuZSRtZXRvZG9fdmVudGEgPT0gIk9ubGluZSJdDQplc2NlbmFyaW9fMiA8LSB2ZW50YXNfbW92aWRhcyAqIChtYXJnZW5fb25saW5lIC0gbWFyZ2VuX29mZmxpbmUpDQoNCiMgRXNjZW5hcmlvIDM6IGNlcnJhciBsYSBtaXRhZCBkZSBsYSBkaWZlcmVuY2lhIGRlIFdlc3QgZnJlbnRlIGFsIG1hcmdlbiBlc3BlcmFkbw0KZXNjZW5hcmlvXzMgPC0gLWJyZWNoYV9yZWdpb24kYnJlY2hhX3VzZFticmVjaGFfcmVnaW9uJHJlZ2lvbiA9PSAiV2VzdCJdIC8gMg0KDQpkYXRhLmZyYW1lKGBBY2Npw7NuYCA9IGMoIkFjY2nDs24gMSIsICJBY2Npw7NuIDIiLCAiQWNjacOzbiAzIiksDQogICAgICAgICAgIEVzY2VuYXJpbyA9IGMoIkxhcyB2ZW50YXMgZGUgbG9zIGVzdGFkb3MgRXN0cmVsbGEgeSBkZSBPcG9ydHVuaWRhZCBjcmVjZW4gMTAlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiRWwgMTAlIGRlIGxhcyB2ZW50YXMgSW4tc3RvcmUgeSBPdXRsZXQgZGUgTm9ydGhlYXN0IHBhc2EgYSBPbmxpbmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJTZSBjaWVycmEgbGEgbWl0YWQgZGUgbGEgYnJlY2hhIGRlIFdlc3QgZnJlbnRlIGEgbG8gZXNwZXJhZG8iKSwNCiAgICAgICAgICAgYFV0aWxpZGFkIGFkaWNpb25hbGAgPSB1c2QoYyhlc2NlbmFyaW9fMSwgZXNjZW5hcmlvXzIsIGVzY2VuYXJpb18zKSksDQogICAgICAgICAgIGAlIGRlIGxhIHV0aWxpZGFkIGFjdHVhbGAgPSBwY3QoYyhlc2NlbmFyaW9fMSwgZXNjZW5hcmlvXzIsIGVzY2VuYXJpb18zKSAvIHV0aWxpZGFkX3RvdCAqIDEwMCksDQogICAgICAgICAgIGNoZWNrLm5hbWVzID0gRkFMU0UpICU+JQ0KICBrYWJsZShjYXB0aW9uID0gIlRhYmxhIDE1LiBCZW5lZmljaW8gZXNwZXJhZG8gZGUgY2FkYSBhY2Npw7NuIChlc2NlbmFyaW9zIHByb3B1ZXN0b3MsIGNvbiBtw6FyZ2VuZXMgY29uc3RhbnRlcykiLCBhbGlnbiA9IGMoImwiLCAibCIsICJyIiwgInIiKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQpMYSBUYWJsYSAxNSBtdWVzdHJhIGPDs21vIHNlIGNhbGN1bMOzIGVsIGJlbmVmaWNpbyBlc3BlcmFkbyBkZSBjYWRhIGFjY2nDs24uIFNvbiBlc2NlbmFyaW9zIHBhcmEgZGltZW5zaW9uYXJsYXMsIG5vIG1ldGFzLCB5IHN1cG9uZW4gcXVlIGxvcyBtw6FyZ2VuZXMgYWN0dWFsZXMgc2UgbWFudGllbmVuLg0KDQojIyBDb25kaWNpb25lcyBkZSBhcGxpY2FjacOzbg0KDQpMYXMgdHJlcyBhY2Npb25lcyBkZWJlbiBlbXBlemFyIGNvbW8gcGlsb3RvcyBjb24gdW4gaW5kaWNhZG9yIGRlZmluaWRvIGFudGVzIGRlIGFycmFuY2FyLiBMYSBBY2Npw7NuIDEgcHVlZGUgY29tZW56YXIgZGUgaW5tZWRpYXRvLiBMYXMgQWNjaW9uZXMgMiB5IDMgZGVwZW5kZW4gZGUgcXVlIGxvcyBkaXN0cmlidWlkb3JlcyBlbnRyZWd1ZW4gZGF0b3MgZGUgY29zdG9zLCBkZXNjdWVudG9zIHkgZmVjaGEgZGUgdmVudGEsIHBvcnF1ZSBzaW4gZWxsb3Mgbm8gc2UgcHVlZGUgc2FiZXIgc2kgZWwgcHJvYmxlbWEgZXMgZGUgcHJlY2lvLCBkZSBjb3N0byBvIGRlbCB0aXBvIGRlIHRpZW5kYS4NCg0KU2kgbG9zIGNvc3RvcyBtdWVzdHJhbiBxdWUgZWwgbWFyZ2VuIGJham8gZGUgV2VzdCBzZSBkZWJlIGEgY29zdG9zIHF1ZSBubyBzZSBwdWVkZW4gcmVkdWNpciAocG9yIGVqZW1wbG8sIGFycmllbmRvcyBvIHRyYW5zcG9ydGUgYSBIYXdhaWkpLCBsYSBBY2Npw7NuIDMgcGFzYXLDrWEgZGUgY29ycmVnaXIgZWwgbWFyZ2VuIGEgYWp1c3RhciBwcmVjaW9zIG8gY29iZXJ0dXJhLiBTaSBsYSBmZWNoYSBtb3N0cmFyYSBxdWUgbG9zIGVzdGFkb3MgRXN0cmVsbGEgdmllbmVuIGNheWVuZG8gZW4gdmVudGFzLCBoYWJyw61hIHF1ZSByZXZpc2FyIGxhIHByaW9yaWRhZCBkZSBsYSBBY2Npw7NuIDEuDQoNCiMjIExpbWl0YWNpb25lcw0KDQotICoqU2luIGZlY2hhOioqIG5vIHB1ZGltb3MgY2FsY3VsYXIgdGFzYXMgZGUgY3JlY2ltaWVudG8gaW50ZXJhbnVhbCBuaSBpZGVudGlmaWNhciBwZXJpb2RvcyBkZSBleHBhbnNpw7NuIG8gY29udHJhY2Npw7NuLiBMYSBiYXNlIHBhcmVjZSB0ZW5lciBkYXRvcyBtZW5zdWFsZXMsIHBlcm8gc2luIGxhIGZlY2hhIGVsIGFuw6FsaXNpcyBzb2xvIHB1ZWRlIHNlciBkZSBjb3J0ZSB0cmFuc3ZlcnNhbC4NCi0gKipTaW4gY29zdG9zIG5pIGRlc2N1ZW50b3M6KiogbGFzIHJhem9uZXMgcXVlIGRhbW9zIHBhcmEgZWwgbWFyZ2VuIGJham8gc29uIGhpcMOzdGVzaXMgcXVlIGhhYnLDrWEgcXVlIGNvbXByb2Jhci4NCi0gKipQZXJpb2RvIHkgbWVyY2FkbzoqKiBsYXMgY29uY2x1c2lvbmVzIGFwbGljYW4gc29sbyBhIEVzdGFkb3MgVW5pZG9zIHkgYWwgcGVyaW9kbyBxdWUgY3VicmUgbGEgYmFzZS4gTG9zIHZhbG9yZXMgZXN0w6FuIGVuIGTDs2xhcmVzLg0KLSAqKkNvcnJlbGFjacOzbiBubyBlcyBjYXVzYWxpZGFkOioqIGxhcyBjb3JyZWxhY2lvbmVzIHkgZWwgbWFyZ2VuIGVzcGVyYWRvIGRlc2NyaWJlbiBsbyBxdWUgcGFzYSwgcGVybyBubyBleHBsaWNhbiBsYSBjYXVzYS4gRWwgbWFyZ2VuIGVzcGVyYWRvIGVzIHVuYSByZWZlcmVuY2lhIHkgbm8gdW5hIG1ldGEuDQotICoqRXNjZW5hcmlvczoqKiBzdXBvbmVuIHF1ZSBsb3MgbcOhcmdlbmVzIHNlIG1hbnRpZW5lbiB5IHNpcnZlbiBzb2xvIGNvbW8gcmVmZXJlbmNpYS4NCg0KIyAqKlNvcG9ydGUgcmVwcm9kdWNpYmxlKioNCg0KLSAqKkZ1ZW50ZSBkZSBsb3MgZGF0b3M6KiogRGF0b3NDYXNvMS54bHN4IChob2phIERhdGEgU2FsZXMgQWRpZGFzKSwgc3VtaW5pc3RyYWRhIHBvciBsYSBkb2NlbnRlIGRlbCBjdXJzbyBBbmFsw610aWNhIGRlIE5lZ29jaW9zLCBFc3BlY2lhbGl6YWNpw7NuIGVuIEZpbmFuemFzLCBQb250aWZpY2lhIFVuaXZlcnNpZGFkIEphdmVyaWFuYSBDYWxpLCAyMDI2Lg0KLSAqKkZ1ZW50ZSBleHRlcm5hOioqIFUuUy4gQnVyZWF1IG9mIEVjb25vbWljIEFuYWx5c2lzICgyMDIyKS4gKlJlYWwgUGVyc29uYWwgQ29uc3VtcHRpb24gRXhwZW5kaXR1cmVzIGJ5IFN0YXRlIGFuZCBSZWFsIFBlcnNvbmFsIEluY29tZSBieSBTdGF0ZSBhbmQgTWV0cm9wb2xpdGFuIEFyZWEsIDIwMjEqIChCRUEgMjItNjIpLCAxNSBkZSBkaWNpZW1icmUgZGUgMjAyMi4gaHR0cHM6Ly93d3cuYmVhLmdvdi9zaXRlcy9kZWZhdWx0L2ZpbGVzLzIwMjItMTIvcnBwMTIyMi5wZGYNCi0gKipDw7NtbyByZXByb2R1Y2lyOioqIGd1YXJkYXIgZWwgYXJjaGl2byBkZSBFeGNlbCBlbiBsYSBtaXNtYSBjYXJwZXRhIGRlIGVzdGUgLlJtZCB5IGVqZWN1dGFyICpLbml0Ki4gRWwgY8OzZGlnbyBkZSBjYWRhIHRhYmxhIHkgZ3LDoWZpY28gc2UgcHVlZGUgdmVyIGNvbiBlbCBib3TDs24gKipTaG93KiogZGUgY2FkYSBibG9xdWUsIHkgZWwgLlJtZCBzZSBwdWVkZSBkZXNjYXJnYXIgY29uIGVsIGJvdMOzbiAqKkNvZGUqKiBkZSBhcnJpYmEuDQoNCkVsIGluZm9ybWUgc2UgZ2VuZXLDsyBjb24gYHIgUi52ZXJzaW9uLnN0cmluZ2AuDQoNCmBgYHtyIHBhcXVldGVzfQ0KcGFxdWV0ZXMgPC0gYygicmVhZHhsIiwgImRwbHlyIiwgInRpZHlyIiwgImdncGxvdDIiLCAicGxvdGx5IiwgImtuaXRyIiwgImthYmxlRXh0cmEiLCAicm1hcmtkb3duIikNCmRhdGEuZnJhbWUoUGFxdWV0ZSA9IHBhcXVldGVzLA0KICAgICAgICAgICBgVmVyc2nDs25gID0gc2FwcGx5KHBhcXVldGVzLCBmdW5jdGlvbihwKSBhcy5jaGFyYWN0ZXIocGFja2FnZVZlcnNpb24ocCkpKSwNCiAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gTlVMTCkgJT4lDQogIGthYmxlKGNhcHRpb24gPSAiUGFxdWV0ZXMgdXRpbGl6YWRvcyIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg==