Introducción
Un píxel (de picture element) es la unidad
más pequeña de una imagen digital: un cuadrito de un solo color.
Una imagen digital es un arreglo de
intensidades cuyos valores representan los niveles de
color de cada píxel, la imagen también se puede ver
como una cuadrícula de píxeles organizada en filas y columnas que se
representa como un arreglo de \(n_r \times n_c
\times 3\) números (filas, columnas y los tres canales de
color).
Una red de adyacencia de regiones (RAG, region
adjacency graph) describe la relación entre sus partes:
- cada vértice es un superpíxel, es decir,
una región pequeña, conexa y de color aproximadamente homogéneo;
- cada arista une dos superpíxeles que se tocan
(comparten al menos un lado de píxel, adyacencia de 4 vecinos);
- cada arista lleva un peso de similitud visual que
indica qué tan parecidos son los colores de las dos regiones.
El objetivo no es reconocer objetos sino caracterizar la
estructura visual de la imagen. Las comunidades de la red
agrupan regiones visualmente relacionadas, que pueden coincidir o no con
objetos reales.
Superpíxeles con
SLIC
SLIC (Simple Linear Iterative Clustering) es un \(k\)-medias local en el espacio (color,
posición). Se fija el número deseado de superpíxeles \(K\), se inicializan centros en una rejilla
de paso \(S=\sqrt{N/K}\) (\(N\) = número de píxeles) y cada píxel se
asigna al centro cercano que minimiza
\[
D=\sqrt{d_c^2+\left(\frac{m}{S}\,d_s\right)^2},
\]
donde \(d_c\) es la distancia de
color, \(d_s\) la distancia espacial y
\(m\) la compactness. Valores
grandes de \(m\) producen regiones
regulares (casi cuadradas); valores pequeños hacen que las regiones
sigan mejor los bordes de color.
Espacios de color:
RGB y CIELAB
Los colores de una imagen pueden describirse en distintos
espacios de color. La imagen se lee en RGB, pero las
distancias entre colores se calculan en CIELAB. La razón es que en RGB
la distancia euclidiana no corresponde a la diferencia de color que
percibe una persona, y en CIELAB sí, de forma aproximada.
RGB es un modelo aditivo: cada
píxel se describe por la intensidad de tres luces primarias, rojo (R),
verde (G) y azul (B), cada una entre 0 y 255. El negro es (0, 0, 0), el
blanco (255, 255, 255) y los colores intermedios se obtienen sumando
luces: rojo + verde = amarillo (255, 255, 0), verde + azul = cian (0,
255, 255) y rojo + azul = magenta (255, 0, 255), como se ve en la rueda.
Es el formato natural de las cámaras y las pantallas, pero tiene dos
problemas para medir diferencias de color:
- No es perceptualmente uniforme. La misma distancia
euclidiana en RGB puede corresponder a diferencias muy visibles en una
zona del espacio y casi imperceptibles en otra. En la rueda, por
ejemplo, la franja que el ojo ve como “verde” ocupa un rango de ángulos
mucho más amplio que la del amarillo o el cian: muchos valores RGB
distintos se perciben casi iguales.
- Mezcla luminosidad y color. Oscurecer un color
reduce los tres canales a la vez, así que una misma superficie bajo luz
y bajo sombra queda lejos en RGB aunque su color sea el mismo.
CIELAB (o L*a*b*, definido por la Comisión
Internacional de la Iluminación en 1976) separa la luminosidad del color
y organiza los colores como lo hace la visión humana, mediante pares de
colores opuestos:
- L* es la luminosidad, de 0 (negro) a 100 (blanco):
el eje vertical de la esfera.
- a* va del verde (valores negativos) al rojo
(positivos).
- b* va del azul (negativos) al amarillo
(positivos).
En el plano horizontal, el ángulo indica el tono y la distancia al
eje central indica la saturación: los grises están sobre el eje y los
colores más vivos, en el borde. CIELAB se diseñó para que la distancia
euclidiana entre dos colores, llamada \(\Delta
E\), sea aproximadamente proporcional a la diferencia percibida:
un \(\Delta E\approx 2{,}3\) es, en
promedio, la menor diferencia que una persona distingue, y diferencias
mayores que 10 se perciben como colores claramente distintos. Por eso es
el espacio adecuado para definir \(d_{ij}\) y, con ella, el peso de similitud
visual.
La conversión de RGB a CIELAB (función rgb_to_lab) tiene
tres pasos:
- Linealización. Los valores RGB de una imagen
digital (sRGB) están codificados con una corrección gamma. Se pasan a
\([0,1]\) y se deshace esa corrección
para obtener intensidades de luz proporcionales a la energía
física.
- Paso a XYZ. Una matriz lineal fija transforma las
intensidades RGB en las coordenadas XYZ de la CIE, que describen el
color según la respuesta de los tres tipos de conos del ojo. Se dividen
por el blanco de referencia D65 (luz de día).
- Paso a L*a*b*. Se aplica una raíz cúbica (con un
tramo lineal cerca de cero), que imita la respuesta no lineal del ojo a
la intensidad, y se combinan los resultados: L* sale de Y, a* de la
diferencia entre X e Y y b* de la diferencia entre Y y Z.
Atributos, distancia
de color y pesos
Para cada superpíxel \(i\) se
calcula su área, su centroide y su color medio en CIELAB, \((\bar L_i,\bar a_i,\bar b_i)\). CIELAB se
usa porque las distancias euclidianas en ese espacio se aproximan a
diferencias de color percibidas. La distancia entre regiones vecinas
es
\[
d_{ij}=\sqrt{(\bar L_i-\bar L_j)^2+(\bar a_i-\bar a_j)^2+(\bar b_i-\bar
b_j)^2},
\]
y la similitud visual es
\[
w_{ij}=\exp\!\left(-\frac{d_{ij}}{h}\right),\qquad h>0,\qquad
w_{ij}\in(0,1].
\]
El parámetro \(h\) es una escala de
color: indica cuántas unidades de \(\Delta
E\) hacen caer la similitud a \(e^{-1}\approx 0.37\). Para medidas basadas
en caminos (intermediación) se usa el costo \(\ell_{ij}=1/w_{ij}\). Además de \(w_{ij}\), cada arista guarda la longitud de
frontera \(b_{ij}\) (número de lados de
píxel compartidos).
La función exponencial no es la única opción: puede usarse cualquier
similitud que decrezca con la distancia de color, por ejemplo un núcleo
gaussiano \(\exp(-d_{ij}^2/2h^2)\). Con
la elegida, si dos superpíxeles tienen el mismo color medio en CIELAB,
\(d_{ij}=0\) y \(w_{ij}=1\); a medida que \(d_{ij}\) crece, \(w_{ij}\) decrece hacia 0 sin alcanzarlo
(asíntota en 0). Por eso los pesos son intensidades: un
valor grande indica regiones parecidas, y para las medidas basadas en
caminos se usa el costo \(\ell_{ij}=1/w_{ij}\).
Filtrado por umbral
\(\tau\)
Como \(w_{ij}>0\) para todo par
de superpíxeles vecinos, la red de regiones es conexa y conserva aristas
incluso entre regiones de colores muy distintos. Para estudiar la
estructura de la red se conservan solo las aristas con \(w_{ij}\ge\tau\). Como \(w_{ij}\) es monótona en \(d_{ij}\), esto equivale a
\[
w_{ij}\ge\tau \iff d_{ij}\le -h\ln\tau .
\]
Al aumentar \(\tau\) se eliminan las
conexiones entre regiones de colores distintos y la red se fragmenta en
componentes.
Por referencia se utiliza \(\tau=0.5\).
Comunidades: Louvain
y Leiden
Ambos algoritmos maximizan la modularidad
ponderada
\[
Q=\frac{1}{2W}\sum_{i,j}\left(w_{ij}-\frac{s_i
s_j}{2W}\right)\delta(c_i,c_j),
\]
donde \(s_i=\sum_j w_{ij}\) es la
fuerza del vértice \(i\) y \(W\) la suma de pesos.
Louvain alterna movimientos locales de vértices y
agregación de comunidades. Leiden añade una fase de
refinamiento que garantiza comunidades internamente conectadas, y suele
alcanzar modularidades iguales o mayores. Proyectando la comunidad de
cada superpíxel sobre sus píxeles se obtiene una
segmentación de la imagen. Una modularidad alta no
garantiza una segmentación “buena”: \(Q\) es una medida interna de la red, no de
la calidad visual.
Evaluación y
robustez
Se distingue evaluación interna (modularidad) y externa (ARI, IoU, F1
de fronteras, que requieren una segmentación de referencia), y se
propone estudiar la sensibilidad a \(K\), \(m\), \(h\)
y \(\tau\), y la robustez frente a
ruido, suavizado o cambios de iluminación. Los experimentos de esta
relatoría se ubican precisamente en ese último bloque.
Implementación
Las siguientes funciones nos van a permitir la construcción del
análisis: preparación de la imagen, SLIC, grafo de regiones con
distancias CIELAB y pesos \(w_{ij}\),
filtrado por \(\tau\), detección de
comunidades y proyección a píxeles.
suppressPackageStartupMessages({
library(OpenImageR)
library(igraph)
library(dplyr)
library(tidyr)
library(ggplot2)
library(knitr)
})
set.seed(2026)
Funciones de
gráficos
Se definen primero para poder mostrar el resultado de cada paso en
las secciones siguientes.
boundary_mask <- function(part) {
b <- matrix(FALSE, nrow(part), ncol(part))
b[, -ncol(part)] <- b[, -ncol(part)] | part[, -ncol(part)] != part[, -1]
b[-nrow(part), ] <- b[-nrow(part), ] | part[-nrow(part), ] != part[-1, ]
b
}
# Cada píxel se pinta con el color medio de su región/comunidad
mean_color_image <- function(img, part) {
out <- img
for (k in 1:3) {
ch <- img[, , k]; mu <- tapply(ch, part, mean)
out[, , k] <- matrix(mu[as.character(part)], nrow(part))
}
out
}
plot_panel <- function(img, part = NULL, main = "", fill = TRUE,
border_col = c(0, 0, 0), interpolate = is.null(part)) {
nr <- dim(img)[1]; nc <- dim(img)[2]
ras <- if (!is.null(part) && fill) mean_color_image(img, part) else img
ras <- ras / 255
if (!is.null(part)) {
b <- boundary_mask(part)
for (k in 1:3) { tmp <- ras[, , k]; tmp[b] <- border_col[k]; ras[, , k] <- tmp }
}
plot.new()
plot.window(c(0, nc), c(0, nr), asp = 1, xaxs = "i", yaxs = "i")
rasterImage(as.raster(ras), 0, 0, nc, nr, interpolate = interpolate)
title(main, cex.main = 0.85, font.main = 1, line = 0.3)
}
# Red de centroides sobre la imagen; grosor y opacidad proporcionales a w_ij
plot_graph_overlay <- function(res, main = "") {
g <- res$graph; img <- res$img; nr <- dim(img)[1]
plot_panel(img, main = main)
el <- ends(g, E(g), names = FALSE)
x <- V(g)$col - 0.5; y <- nr - V(g)$row + 0.5
segments(x[el[, 1]], y[el[, 1]], x[el[, 2]], y[el[, 2]],
lwd = 0.4 + 2.2 * E(g)$w,
col = rgb(1, 1, 1, alpha = 0.25 + 0.75 * E(g)$w))
points(x, y, pch = 19, cex = 0.35, col = "#FFD400")
}
# Imagen usada para ilustrar cada paso de la implementación
ruta_ej <- "paisaje_rdavila.jpg"
Preparación de la
imagen y ruido
# Lee una imagen, la deja en RGB con valores en [0, 255] y la reduce de tamaño
prepare_rgb <- function(path, max_side = 240) {
img <- OpenImageR::readImage(path)
if (length(dim(img)) == 2) img <- array(img, c(dim(img), 3))
if (dim(img)[3] == 4) img <- img[, , 1:3]
if (max(img) <= 1) img <- img * 255
nr <- dim(img)[1]; nc <- dim(img)[2]
s <- max_side / max(nr, nc)
if (s < 1) {
img <- OpenImageR::resizeImage(img, round(nr * s), round(nc * s),
method = "bilinear")
if (max(img) <= 1) img <- img * 255
}
pmin(pmax(img, 0), 255)
}
# Ruido gaussiano aditivo N(0, sigma^2) en cada canal (escala 0-255)
add_noise <- function(img, sigma, seed = 1) {
if (sigma == 0) return(img)
set.seed(seed)
out <- img + array(rnorm(length(img), 0, sigma), dim(img))
pmin(pmax(out, 0), 255)
}
# sRGB (0-255) -> CIELAB (iluminante D65)
rgb_to_lab <- function(rgb) {
v <- rgb / 255
v <- ifelse(v <= 0.04045, v / 12.92, ((v + 0.055) / 1.055)^2.4)
M <- matrix(c(0.4124564, 0.3575761, 0.1804375,
0.2126729, 0.7151522, 0.0721750,
0.0193339, 0.1191920, 0.9503041), 3, byrow = TRUE)
xyz <- v %*% t(M)
xyz <- sweep(xyz, 2, c(0.95047, 1, 1.08883), "/")
f <- ifelse(xyz > (6 / 29)^3, xyz^(1 / 3), xyz / (3 * (6 / 29)^2) + 4 / 29)
cbind(L = 116 * f[, 2] - 16,
a = 500 * (f[, 1] - f[, 2]),
b = 200 * (f[, 2] - f[, 3]))
}
img_ej <- prepare_rgb(ruta_ej, max_side = 400)
lab_ej <- rgb_to_lab(matrix(img_ej, ncol = 3))
L_ej <- matrix(lab_ej[, "L"], nrow(img_ej), ncol(img_ej)) # luminancia L* (0-100)
op <- par(mfrow = c(1, 3), mar = c(0.3, 0.3, 1.6, 0.3))
plot_panel(img_ej, main = sprintf("Imagen preparada (%d x %d px)", nrow(img_ej), ncol(img_ej)))
plot_panel(add_noise(img_ej, 25), main = "Con ruido gaussiano, sigma = 25")
plot_panel(array(L_ej / 100 * 255, c(dim(L_ej), 3)), main = "Canal L* de CIELAB")

par(op)
Como ilustración se usa la fotografía del paisaje, reducida a 400
píxeles en su lado mayor como en el resto del análisis. El ruido
gaussiano se suma canal por canal y se recorta a [0, 255]. El tercer
panel muestra el canal L* de CIELAB: solo la luminosidad, sin
información de color. Las zonas iluminadas de la fachada y el cielo
tienen L* alto y los árboles y la vía, L* bajo; los canales a* y b* (no
mostrados) contienen el color.
Superpíxeles y grafo
de regiones
# SLIC: devuelve una matriz de etiquetas 1, ..., K'
run_slic <- function(img, n_superpixels = 180, compactness = 20) {
s <- OpenImageR::superpixels(img, method = "slic",
superpixel = n_superpixels,
compactness = compactness,
return_slic_data = FALSE,
return_labels = TRUE, verbose = FALSE)
lab <- s$labels
matrix(as.integer(factor(lab)), nrow(lab), ncol(lab))
}
# Pares de regiones vecinas (4 vecinos) y longitud de frontera b_ij
region_edges <- function(lab) {
hz <- cbind(as.vector(lab[, -ncol(lab)]), as.vector(lab[, -1]))
vt <- cbind(as.vector(lab[-nrow(lab), ]), as.vector(lab[-1, ]))
p <- rbind(hz, vt)
p <- p[p[, 1] != p[, 2], , drop = FALSE]
data.frame(from = pmin(p[, 1], p[, 2]), to = pmax(p[, 1], p[, 2])) |>
count(from, to, name = "border")
}
# Construye la red de regiones. Si 'labels' se da, se usa esa partición fija
image_to_graph <- function(img, labels = NULL, n_superpixels = 180,
compactness = 20, h = 20) {
if (is.null(labels)) labels <- run_slic(img, n_superpixels, compactness)
nr <- nrow(labels); nc <- ncol(labels)
px <- matrix(img, ncol = 3) # píxeles x canales
lab_px <- rgb_to_lab(px)
id <- as.vector(labels)
rr <- as.vector(row(labels)); cc <- as.vector(col(labels))
K <- max(id)
attr_v <- data.frame(
name = as.character(1:K),
area = tabulate(id, K),
row = tapply(rr, id, mean), col = tapply(cc, id, mean),
R = tapply(px[, 1], id, mean), G = tapply(px[, 2], id, mean),
B = tapply(px[, 3], id, mean),
L = tapply(lab_px[, 1], id, mean), a = tapply(lab_px[, 2], id, mean),
b = tapply(lab_px[, 3], id, mean))
e <- region_edges(labels)
e$d <- sqrt((attr_v$L[e$from] - attr_v$L[e$to])^2 +
(attr_v$a[e$from] - attr_v$a[e$to])^2 +
(attr_v$b[e$from] - attr_v$b[e$to])^2)
e$w <- exp(-e$d / h)
e$cost <- 1 / e$w
e$from <- as.character(e$from); e$to <- as.character(e$to)
g <- graph_from_data_frame(e, directed = FALSE, vertices = attr_v)
g$h <- h
list(graph = g, labels = labels, img = img)
}
# Recalcula los pesos con otra escala h (no cambia la topología)
set_h <- function(g, h) {
E(g)$w <- exp(-E(g)$d / h); E(g)$cost <- 1 / E(g)$w; g$h <- h; g
}
# Conserva solo las aristas con w_ij >= tau
filter_graph <- function(g, tau) delete_edges(g, E(g)[w < tau])
res_ej <- image_to_graph(img_ej, n_superpixels = 180, compactness = 20, h = 20)
g_ej <- res_ej$graph
op <- par(mfrow = c(2, 2), mar = c(0.3, 0.3, 1.6, 0.3))
plot_panel(img_ej, res_ej$labels, fill = FALSE, border_col = c(1, 1, 0),
main = sprintf("SLIC: %d superpíxeles", vcount(g_ej)))
plot_panel(img_ej, res_ej$labels, main = "Color medio de cada superpíxel")
plot_graph_overlay(res_ej, main = sprintf("Grafo de regiones (%d aristas)", ecount(g_ej)))
plot_graph_overlay(list(graph = filter_graph(g_ej, 0.5), img = img_ej),
main = sprintf("Grafo filtrado, tau = 0,5 (%d aristas)",
ecount(filter_graph(g_ej, 0.5))))

par(op)
op <- par(mfrow = c(1, 2), mar = c(4.2, 4.2, 2, 1))
hist(E(g_ej)$d, breaks = 30, col = "#2471A3", border = "white",
main = "Distancia de color entre vecinos", xlab = expression(d[ij] ~ "(CIELAB)"), ylab = "Aristas")
hist(E(g_ej)$w, breaks = 30, col = "#C0392B", border = "white",
main = "Pesos de las aristas (h = 20)", xlab = expression(w[ij] == exp(-d[ij] / h)), ylab = "Aristas")
abline(v = 0.5, lty = 2)

par(op)
Cada superpíxel es un vértice y dos superpíxeles vecinos (que
comparten frontera) están unidos por una arista. En el grafo sobre la
imagen, las líneas más gruesas y opacas son las de mayor peso \(w_{ij}\), es decir, las de regiones con
color parecido. El filtrado por \(\tau\) elimina las aristas entre regiones
de color muy distinto, que son las que cruzan los bordes de los
objetos.
Comunidades y
segmentación
find_communities <- function(g, method = c("louvain", "leiden"), seed = 123) {
method <- match.arg(method)
if (ecount(g) == 0) return(list(membership = seq_len(vcount(g)), Q = 0))
set.seed(seed)
cl <- switch(method,
louvain = cluster_louvain(g, weights = E(g)$w),
leiden = cluster_leiden(g, objective_function = "modularity",
weights = E(g)$w, n_iterations = -1))
m <- as.integer(membership(cl))
list(membership = m, Q = modularity(g, m, weights = E(g)$w))
}
# Proyecta la comunidad de cada superpíxel sobre sus píxeles
pixel_partition <- function(labels, membership) {
matrix(membership[labels], nrow(labels), ncol(labels))
}
# Índice de Rand ajustado entre dos segmentaciones a nivel de píxel
ari <- function(p1, p2) igraph::compare(as.vector(p1), as.vector(p2),
method = "adjusted.rand")
lv_ej <- find_communities(g_ej, "louvain")
ld_ej <- find_communities(g_ej, "leiden")
seg_lv <- pixel_partition(res_ej$labels, lv_ej$membership)
seg_ld <- pixel_partition(res_ej$labels, ld_ej$membership)
op <- par(mfrow = c(1, 3), mar = c(0.3, 0.3, 1.6, 0.3))
plot_panel(img_ej, main = "Imagen original")
plot_panel(img_ej, seg_lv, main = sprintf("Louvain: %d comunidades, Q = %.3f",
max(lv_ej$membership), lv_ej$Q))
plot_panel(img_ej, seg_ld, main = sprintf("Leiden: %d comunidades, Q = %.3f",
max(ld_ej$membership), ld_ej$Q))

par(op)
Cada comunidad del grafo de regiones se proyecta sobre los píxeles de
sus superpíxeles y se pinta con su color medio. El índice de Rand
ajustado entre las dos segmentaciones es 0.837 (1 = idénticas, cerca de
0 = no más parecidas que al azar).
Funciones de
experimentos
# Resumen estructural de la red (ponderada por w)
net_summary <- function(g) {
comp <- components(g)
data.frame(vertices = vcount(g), aristas = ecount(g),
densidad = round(edge_density(g), 4),
grado_medio = round(mean(degree(g)), 2),
w_media = round(mean(E(g)$w), 3),
transitividad = round(transitivity(g, type = "global"), 3),
componentes = comp$no,
aislados = sum(degree(g) == 0))
}
# Segmentaciones Louvain y Leiden para varios h (grafo completo)
experiment_h <- function(res, hs) {
lapply(hs, function(h) {
g <- set_h(res$graph, h)
lv <- find_communities(g, "louvain"); ld <- find_communities(g, "leiden")
list(h = h, g = g, lv = lv, ld = ld,
tab = data.frame(h = h,
w_media = round(mean(E(g)$w), 3),
w_mediana = round(median(E(g)$w), 3),
com_louvain = max(lv$membership),
Q_louvain = round(lv$Q, 3),
com_leiden = max(ld$membership),
Q_leiden = round(ld$Q, 3),
ARI_louvain_leiden = round(ari(lv$membership, ld$membership), 3)))
})
}
# Segmentaciones Louvain y Leiden sobre el grafo filtrado G_tau
experiment_tau <- function(res, taus, h = 20, min_area = 0) {
g0 <- set_h(res$graph, h)
lapply(taus, function(tau) {
g <- filter_graph(g0, tau)
lv <- find_communities(g, "louvain"); ld <- find_communities(g, "leiden")
comp <- components(g)
list(tau = tau, g = g, lv = lv, ld = ld,
tab = data.frame(tau = tau,
d_max = ifelse(tau > 0, round(-h * log(tau), 2), Inf),
aristas = ecount(g),
componentes = comp$no,
aislados = sum(degree(g) == 0),
com_louvain = max(lv$membership),
Q_louvain = round(lv$Q, 3),
com_leiden = max(ld$membership),
Q_leiden = round(ld$Q, 3),
ARI_louvain_leiden = round(ari(lv$membership, ld$membership), 3)
)
)
})
}
# Robustez frente a ruido gaussiano.
# - SLIC fijo: los superpíxeles son los de la imagen limpia; el ruido solo cambia
# los colores medios (y por tanto d_ij y w_ij).
# - SLIC variable: SLIC se recalcula sobre la imagen ruidosa.
# La referencia es la segmentación de la imagen limpia con el mismo método.
experiment_noise <- function(res, sigmas, reps = 5, h = 20,
n_superpixels = 180, compactness = 20) {
ref <- list(louvain = pixel_partition(res$labels, find_communities(res$graph, "louvain")$membership),
leiden = pixel_partition(res$labels, find_communities(res$graph, "leiden")$membership))
out <- list()
for (s in sigmas) for (r in seq_len(reps)) {
noisy <- add_noise(res$img, s, seed = 100 * r + s)
fixed <- image_to_graph(noisy, labels = res$labels, h = h)
varia <- image_to_graph(noisy, n_superpixels = n_superpixels,
compactness = compactness, h = h)
for (sl in c("fijo", "variable")) {
rr <- if (sl == "fijo") fixed else varia
for (m in c("louvain", "leiden")) {
cm <- find_communities(rr$graph, m)
pp <- pixel_partition(rr$labels, cm$membership)
out[[length(out) + 1]] <- data.frame(
sigma = s, rep = r, slic = sl, metodo = m,
comunidades = max(cm$membership), Q = cm$Q,
ARI = ari(pp, ref[[m]]))
}
}
}
bind_rows(out)
}
# Una realización por sigma para mostrar las segmentaciones (SLIC variable)
noise_examples <- function(res, sigmas, h = 20) {
lapply(sigmas, function(s) {
noisy <- add_noise(res$img, s, seed = 100 + s)
rr <- image_to_graph(noisy, h = h)
list(sigma = s, img = noisy, res = rr,
lv = pixel_partition(rr$labels, find_communities(rr$graph, "louvain")$membership),
ld = pixel_partition(rr$labels, find_communities(rr$graph, "leiden")$membership))
})
}
# Gráficos de los experimentos
plot_exp_grid <- function(res, ex, key, lab) {
op <- par(mfrow = c(2, length(ex)), mar = c(0.3, 0.3, 1.6, 0.3))
for (e in ex) plot_panel(res$img, pixel_partition(res$labels, e$lv$membership),
sprintf("Louvain, %s = %s (%d com.)", lab, e[[key]], max(e$lv$membership)))
for (e in ex) plot_panel(res$img, pixel_partition(res$labels, e$ld$membership),
sprintf("Leiden, %s = %s (%d com.)", lab, e[[key]], max(e$ld$membership)))
par(op)
}
plot_noise_grid <- function(ne) {
op <- par(mfrow = c(3, length(ne)), mar = c(0.3, 0.3, 1.6, 0.3))
for (e in ne) plot_panel(e$img, main = sprintf("Imagen, sigma = %s", e$sigma))
for (e in ne) plot_panel(e$img, e$lv, sprintf("Louvain (%d com.)", max(e$lv)))
for (e in ne) plot_panel(e$img, e$ld, sprintf("Leiden (%d com.)", max(e$ld)))
par(op)
}
plot_noise_ari <- function(nz, titulo) {
nz |>
group_by(sigma, slic, metodo) |>
summarise(media = mean(ARI), sd = sd(ARI), .groups = "drop") |>
ggplot(aes(sigma, media, colour = metodo, linetype = slic)) +
geom_line(linewidth = 0.8) + geom_point(size = 2) +
geom_errorbar(aes(ymin = media - sd, ymax = media + sd), width = 1.5) +
scale_colour_manual(values = c(leiden = "#C0392B", louvain = "#2471A3")) +
coord_cartesian(ylim = c(0, 1)) +
labs(x = expression(sigma ~ "del ruido (escala 0-255)"),
y = "ARI respecto a la imagen limpia",
colour = "Método", linetype = "SLIC", title = titulo) +
theme_minimal(base_size = 12)
}
summary_noise <- function(nz) {
nz |>
group_by(sigma, slic, metodo) |>
summarise(comunidades = round(mean(comunidades), 1),
Q = round(mean(Q), 3),
ARI_media = round(mean(ARI), 3),
ARI_sd = round(sd(ARI), 3), .groups = "drop") |>
arrange(slic, metodo, sigma)
}
Paisaje
La fotografía de Rodrigo Dávila muestra el nuevo edificio de la
Facultad de Enfermería de la Universidad Nacional de Colombia, sede
Bogotá, al atardecer.
En primer plano aparecen zonas verdes del campus, una vía húmeda con
charcos que reflejan la luz, árboles y algunos peatones, y al fondo se
alcanzan a ver los edificios de ladrillo característicos de la Ciudad
Universitaria.
Red de regiones
res_p <- image_to_graph(paisaje, n_superpixels = K, compactness = m, h = h0)
net_summary(res_p$graph)
| 152 |
407 |
0.0355 |
5.36 |
0.492 |
0.393 |
1 |
0 |
par(mfrow = c(1, 3), mar = c(0.3, 0.3, 1.6, 0.3))
plot_panel(paisaje_hd, main = "Imagen original")
plot_panel(paisaje, res_p$labels, "Superpíxeles SLIC (color medio)")
plot_graph_overlay(res_p, "Red de regiones (grosor ~ w_ij, h = 20)")

SLIC devuelve 152 superpíxeles (el número final no coincide
exactamente con los 180 solicitados porque SLIC ajusta la rejilla
inicial a las proporciones de la imagen) y la red tiene 407 aristas. Es
una red espacial, casi plana, con grado medio de 5.36, como se espera de
una teselación de la imagen. En la superposición se ve que las aristas
gruesas (alta similitud) se concentran en el cielo y en el césped, y que
las que cruzan la fachada iluminada son delgadas.
Sensibilidad a \(h\) en la similitud visual
La escala \(h\) fija cuánto
“castiga” la similitud una diferencia de color. Con \(h\) pequeño, solo las regiones casi
idénticas tienen peso alto y la mayoría de aristas valen casi cero. Con
\(h\) grande, todos los pesos se
acercan a 1 y la red se vuelve casi no ponderada.
curva <- expand.grid(d = seq(0, 80, by = 0.5), h = hs) |>
mutate(w = exp(-d / h), h = factor(h))
dist_p <- data.frame(d = E(res_p$graph)$d)
ggplot(curva, aes(d, w, colour = h)) +
geom_rug(data = dist_p, aes(x = d), inherit.aes = FALSE, alpha = 0.25) +
geom_line(linewidth = 0.9) +
labs(x = expression(d[ij] ~ "(" * Delta * E * ", CIELAB)"),
y = expression(w[ij] == exp(-d[ij] / h)), colour = "h",
title = "Similitud en función de la distancia de color (marcas: d_ij del paisaje)") +
theme_minimal(base_size = 12)

ex_h_p <- experiment_h(res_p, hs)
bind_rows(lapply(ex_h_p, `[[`, "tab"))
| 5 |
0.227 |
0.038 |
12 |
0.685 |
12 |
0.691 |
0.986 |
| 10 |
0.340 |
0.196 |
11 |
0.718 |
11 |
0.719 |
0.953 |
| 20 |
0.492 |
0.442 |
10 |
0.711 |
10 |
0.724 |
0.782 |
| 40 |
0.658 |
0.665 |
9 |
0.689 |
8 |
0.701 |
0.594 |
| 80 |
0.796 |
0.816 |
8 |
0.668 |
9 |
0.689 |
0.545 |
plot_exp_grid(res_p, ex_h_p, "h", "h")

Lectura. La modularidad es invariante a multiplicar
todos los pesos por una constante, de modo que \(h\) no actúa como un simple factor de
escala: cambia el contraste relativo entre aristas de
colores parecidos y distintos. En el paisaje:
- Con \(h=5\) la mediana de \(w_{ij}\) es apenas 0.038: solo las regiones
casi idénticas quedan fuertemente unidas y la segmentación sigue los
cambios de color. Separa los dos prados de césped, la vía con charcos,
la fachada, los árboles y el cielo en bloques distintos (12 comunidades
con ambos métodos).
- Al aumentar \(h\), los pesos
tienden a 1 (mediana 0.816 con \(h=80\)) y la partición se acerca a la de la
red no ponderada: las comunidades se convierten en bloques espaciales
compactos. Con \(h=40\) el prado de la
izquierda se une con los árboles y la parte baja de la fachada en un
gran bloque verde; con \(h=80\) ocurre
lo mismo a la derecha y la vía se une con el centro de la fachada. El
cielo y la fachada se cortan en bloques por razones geométricas, no de
color.
- La modularidad es máxima en \(h=10\) con Louvain (0.718) y en \(h=20\) con Leiden (0.724), y el número de
comunidades baja de 12 a 8–9. El acuerdo entre Louvain y Leiden es casi
perfecto con \(h=5\) (ARI = 0.99) y cae
a 0.55 con \(h=80\): cuando los pesos
se igualan aparecen muchas particiones de modularidad parecida que
difieren en cortes poco marcados.
Valores intermedios (\(h\approx
10\)–\(20\)) equilibran color y
contigüidad.
Segmentaciones para
varios valores de \(\tau\)
Fijado \(h=20\), se filtra la red
con \(w_{ij}\ge\tau\) y se aplican
Louvain y Leiden sobre la red filtrada \(G_\tau\). La columna d_max es
la máxima distancia de color permitida, \(-h\ln\tau\).
ex_t_p <- experiment_tau(res_p, taus, h = h0)
bind_rows(lapply(ex_t_p, `[[`, "tab"))
| 0.0 |
Inf |
407 |
1 |
0 |
10 |
0.711 |
10 |
0.724 |
0.782 |
| 0.3 |
24.08 |
260 |
17 |
13 |
24 |
0.733 |
24 |
0.747 |
0.873 |
| 0.5 |
13.86 |
188 |
39 |
22 |
43 |
0.757 |
43 |
0.758 |
0.975 |
| 0.7 |
7.13 |
125 |
79 |
60 |
82 |
0.677 |
84 |
0.680 |
0.762 |
plot_exp_grid(res_p, ex_t_p, "tau", "tau")

Lectura. Con \(\tau=0\) se usa la red completa. Al subir
el umbral se cortan las aristas que cruzan bordes de color, así que
ningún algoritmo puede unir regiones de componentes distintos: cada
componente de \(G_\tau\) es, como
mínimo, una comunidad. En el paisaje la red pasa de 1 componente a 17,
39 y 79 (con \(\tau=0.7\), 60 de ellos
son superpíxeles aislados), y el número de comunidades sube de 10 a 24,
43 y 82–84.
- Con \(\tau=0.3\) (\(d_{ij}\le 24\)) aparecen como comunidades
propias las ventanas iluminadas, una ventana oscura de la torre, los
charcos y el borde del prado, que la red completa absorbía en bloques
más grandes.
- Con \(\tau=0.5\) emerge la
estructura vertical de la fachada (parasoles y ventanales) y la
segmentación empieza a parecer un mapa de bordes.
- Con \(\tau=0.7\) (\(d_{ij}\le 7.1\)) se acerca al nivel de
superpíxel: el filtrado, más que los algoritmos de comunidades,
determina la partición.
La modularidad sube hasta \(\tau=0.5\), porque la red filtrada ya está
“pre-segmentada”, y baja con \(\tau=0.7\), cuando quedan demasiadas
comunidades triviales. Desde \(\tau=0.3\) Louvain y Leiden dan
prácticamente la misma partición: casi no queda margen de decisión
dentro de componentes tan pequeñas.
Robustez frente a
ruido gaussiano
Se suma ruido \(\varepsilon\sim
N(0,\sigma^2)\) a cada canal (escala 0–255) y se compara la
segmentación ruidosa con la de la imagen limpia mediante el
índice de Rand ajustado (ARI) a nivel de píxel (\(1\) = misma partición, \(\approx 0\) = coincidencia al azar). Se
consideran dos escenarios:
- SLIC fijo: se conservan los superpíxeles de la
imagen limpia; el ruido solo altera los colores medios y por tanto los
pesos.
- SLIC variable: SLIC se recalcula sobre la imagen
ruidosa; el ruido afecta también la teselación.
ne_p <- noise_examples(res_p, sigmas, h = h0)
plot_noise_grid(ne_p)

nz_p <- experiment_noise(res_p, sigmas, reps = reps, h = h0,
n_superpixels = K, compactness = m)
summary_noise(nz_p)
| 0 |
fijo |
leiden |
10.0 |
0.724 |
1.000 |
0.000 |
| 10 |
fijo |
leiden |
10.0 |
0.722 |
0.940 |
0.055 |
| 25 |
fijo |
leiden |
10.0 |
0.718 |
0.861 |
0.098 |
| 50 |
fijo |
leiden |
10.0 |
0.717 |
0.905 |
0.046 |
| 0 |
fijo |
louvain |
10.0 |
0.711 |
1.000 |
0.000 |
| 10 |
fijo |
louvain |
9.6 |
0.714 |
0.832 |
0.058 |
| 25 |
fijo |
louvain |
9.6 |
0.713 |
0.810 |
0.051 |
| 50 |
fijo |
louvain |
10.0 |
0.709 |
0.805 |
0.086 |
| 0 |
variable |
leiden |
10.0 |
0.724 |
1.000 |
0.000 |
| 10 |
variable |
leiden |
9.2 |
0.715 |
0.597 |
0.072 |
| 25 |
variable |
leiden |
8.0 |
0.682 |
0.420 |
0.071 |
| 50 |
variable |
leiden |
5.2 |
0.580 |
0.289 |
0.050 |
| 0 |
variable |
louvain |
10.0 |
0.711 |
1.000 |
0.000 |
| 10 |
variable |
louvain |
8.6 |
0.708 |
0.591 |
0.031 |
| 25 |
variable |
louvain |
7.8 |
0.678 |
0.405 |
0.074 |
| 50 |
variable |
louvain |
5.2 |
0.577 |
0.276 |
0.039 |
plot_noise_ari(nz_p, "Paisaje: estabilidad de la segmentación frente al ruido")

Lectura. Los dos escenarios se comportan de forma
muy distinta.
- SLIC fijo. El ARI se mantiene alto, entre 0.81 y
0.94 (Leiden 0.86–0.94, Louvain 0.81–0.83), sin una tendencia clara con
\(\sigma\): el de Leiden incluso pasa
de 0.86 con \(\sigma=25\) a 0.91 con
\(\sigma=50\). El número de comunidades
(9.6–10) y la modularidad (0.71–0.72) casi no cambian. El ruido se
promedia dentro de cada superpíxel (la desviación del color medio cae
como \(\sigma/\sqrt{\text{área}}\)), de
modo que los pesos apenas cambian. Las diferencias que aparecen se deben
sobre todo a la inestabilidad de la maximización de la modularidad: hay
varias particiones casi igual de buenas (por ejemplo, dónde cortar la
fachada) y una pequeña perturbación de los pesos basta para pasar de una
a otra.
- SLIC variable. Aquí sí hay un deterioro claro: el
ARI baja a ≈ 0.59–0.60 con \(\sigma=10\), ≈ 0.41–0.42 con \(\sigma=25\) y ≈ 0.28–0.29 con \(\sigma=50\); el número de comunidades cae
de 10 a unas 5 y la modularidad de 0.72 a 0.58. El ruido afecta la
teselación: el término de color de SLIC se vuelve poco informativo, los
superpíxeles dejan de seguir los bordes y la red hereda regiones que
mezclan objetos. Además, el recorte a \([0,255]\) sesga los píxeles oscuros hacia
arriba (para un píxel negro, la media de \(\max(0,\varepsilon)\) es \(\sigma/\sqrt{2\pi}\approx 0.4\sigma\)), lo
que reduce el contraste. En las figuras, con \(\sigma=25\) la fachada se mezcla con la vía
y con los árboles de la izquierda, y con \(\sigma=50\) las comunidades son bloques
casi verticales en los que el cielo apenas se distingue.
Agnus Dei
Agnus Dei (c. 1635–1640, Museo del Prado) es una de las
obras más conocidas de Francisco de Zurbarán. Muestra un cordero blanco
tendido de lado sobre una repisa de piedra, con las patas atadas y la
cabeza apoyada con mansedumbre, recortado contra un fondo negro casi
absoluto. La escena funciona como un bodegón religioso: el cordero
simboliza a Cristo como víctima del sacrificio, idea que refuerzan el
tenue halo sobre su cabeza y la inscripción latina Tamquam
agnus (“como un cordero”), que alude al pasaje de Isaías sobre el
cordero llevado al matadero.
Zurbarán emplea un tenebrismo intenso: una luz lateral modela con
enorme detalle la textura de la lana, mientras el resto del cuadro se
hunde en la penumbra. Esa paleta reducida de blancos cálidos, ocres y
negros, con un único contraste dominante entre la figura y el fondo, la
hace especialmente interesante para el análisis por redes. Su estructura
cromática es mucho más simple que la de una fotografía, pero el
claroscuro dentro del cordero genera variaciones sutiles de color.
Red de regiones
res_c <- image_to_graph(cordero, n_superpixels = K, compactness = m, h = h0)
net_summary(res_c$graph)
| 161 |
435 |
0.0338 |
5.4 |
0.635 |
0.403 |
1 |
0 |
par(mfrow = c(1, 3), mar = c(0.3, 0.3, 1.6, 0.3))
plot_panel(cordero_hd, main = "Agnus Dei (Zurbarán)")
plot_panel(cordero, res_c$labels, "Superpíxeles SLIC (color medio)")
plot_graph_overlay(res_c, "Red de regiones (grosor ~ w_ij, h = 20)")

SLIC devuelve 161 superpíxeles y 435 aristas. A diferencia del
paisaje, esta pintura tiene una paleta muy reducida (blancos cálidos,
ocres y negros), de modo que muchas aristas del fondo tienen \(d_{ij}\) pequeño y peso cercano a 1: la
similitud media es 0.635, frente a 0.492 en el paisaje.
Sensibilidad a \(h\)
ex_h_c <- experiment_h(res_c, hs)
bind_rows(lapply(ex_h_c, `[[`, "tab"))
| 5 |
0.358 |
0.277 |
10 |
0.793 |
11 |
0.794 |
0.745 |
| 10 |
0.493 |
0.526 |
10 |
0.788 |
10 |
0.791 |
0.961 |
| 20 |
0.635 |
0.726 |
10 |
0.756 |
10 |
0.759 |
0.916 |
| 40 |
0.763 |
0.852 |
9 |
0.715 |
9 |
0.721 |
0.855 |
| 80 |
0.860 |
0.923 |
9 |
0.685 |
9 |
0.692 |
0.770 |
plot_exp_grid(res_c, ex_h_c, "h", "h")

Lectura. Para \(h\le
20\) ambos algoritmos separan con claridad el cordero, la repisa
ocre y el fondo oscuro, y dividen el cordero en tres o cuatro zonas de
luz y sombra. Para \(h\ge 40\) el
contraste figura-fondo deja de dominar: la cabeza y la parte delantera
del cordero se unen con la repisa y las comunidades pasan a ser bloques
espaciales, aunque el fondo oscuro se mantiene aparte. El número de
comunidades casi no cambia (9–11), pero la modularidad baja de forma
monótona (0.79 → 0.69). El acuerdo Louvain-Leiden es máximo con \(h=10\) (ARI = 0.96) y baja en ambos
extremos: 0.75 con \(h=5\), cuando casi
todos los pesos son pequeños y la partición depende de unas pocas
aristas fuertes, y 0.86 y 0.77 con \(h=40\) y \(h=80\), cuando los pesos se igualan y
aparecen muchas particiones equivalentes, de modo que la modularidad ya
solo encuentra estructura geométrica.
Segmentaciones para
varios valores de \(\tau\)
ex_t_c <- experiment_tau(res_c, taus, h = h0)
bind_rows(lapply(ex_t_c, `[[`, "tab"))
| 0.0 |
Inf |
435 |
1 |
0 |
10 |
0.756 |
10 |
0.759 |
0.916 |
| 0.3 |
24.08 |
354 |
1 |
0 |
9 |
0.781 |
10 |
0.786 |
0.777 |
| 0.5 |
13.86 |
289 |
7 |
4 |
15 |
0.809 |
16 |
0.817 |
0.747 |
| 0.7 |
7.13 |
225 |
31 |
15 |
34 |
0.808 |
34 |
0.815 |
0.780 |
plot_exp_grid(res_c, ex_t_c, "tau", "tau")

Lectura. El patrón es opuesto al del paisaje. Con
\(\tau=0.3\) la red sigue siendo conexa
(1 componente) y la segmentación cambia poco, porque las transiciones de
color de la pintura son graduales. Con \(\tau=0.5\) hay 7 componentes y empiezan a
separarse las patas y zonas de sombra del cordero. Con \(\tau=0.7\) hay 31 componentes y 34
comunidades, pero la fragmentación ocurre dentro del
cordero: el modelado en claroscuro de la lana genera
diferencias de color mayores que 7 unidades \(\Delta E\). El fondo, en cambio, se
mantiene en pocos bloques grandes porque es oscuro y homogéneo. La
modularidad sube de 0.76 a 0.81–0.82 con \(\tau=0.5\) y se mantiene con \(\tau=0.7\); como en el paisaje, Louvain y
Leiden coinciden casi por completo con umbrales altos.
Robustez frente a
ruido gaussiano
ne_c <- noise_examples(res_c, sigmas, h = h0)
plot_noise_grid(ne_c)

nz_c <- experiment_noise(res_c, sigmas, reps = reps, h = h0,
n_superpixels = K, compactness = m)
summary_noise(nz_c)
| 0 |
fijo |
leiden |
10.0 |
0.759 |
1.000 |
0.000 |
| 10 |
fijo |
leiden |
9.8 |
0.757 |
0.922 |
0.090 |
| 25 |
fijo |
leiden |
10.0 |
0.754 |
0.930 |
0.058 |
| 50 |
fijo |
leiden |
9.6 |
0.745 |
0.835 |
0.072 |
| 0 |
fijo |
louvain |
10.0 |
0.756 |
1.000 |
0.000 |
| 10 |
fijo |
louvain |
9.8 |
0.751 |
0.889 |
0.048 |
| 25 |
fijo |
louvain |
10.0 |
0.751 |
0.883 |
0.044 |
| 50 |
fijo |
louvain |
9.0 |
0.740 |
0.807 |
0.066 |
| 0 |
variable |
leiden |
10.0 |
0.759 |
1.000 |
0.000 |
| 10 |
variable |
leiden |
9.8 |
0.754 |
0.764 |
0.085 |
| 25 |
variable |
leiden |
7.6 |
0.694 |
0.450 |
0.046 |
| 50 |
variable |
leiden |
6.4 |
0.621 |
0.258 |
0.030 |
| 0 |
variable |
louvain |
10.0 |
0.756 |
1.000 |
0.000 |
| 10 |
variable |
louvain |
9.4 |
0.750 |
0.685 |
0.088 |
| 25 |
variable |
louvain |
7.2 |
0.689 |
0.444 |
0.037 |
| 50 |
variable |
louvain |
6.4 |
0.620 |
0.260 |
0.037 |
plot_noise_ari(nz_c, "Agnus Dei: estabilidad de la segmentación frente al ruido")

Lectura.
- SLIC fijo. Ambos métodos son estables, Leiden un
poco más: ARI ≈ 0.92–0.93 con \(\sigma\le
25\) y 0.84 con \(\sigma=50\),
frente a 0.88–0.89 y 0.81 con Louvain. Como en el paisaje, la diferencia
se debe sobre todo a que Louvain cambia de solución entre particiones
casi equivalentes.
- SLIC variable. El ARI cae a ≈ 0.69–0.76, 0.44–0.45
y 0.26 para \(\sigma=10, 25, 50\): algo
mejor que en el paisaje con ruido leve y prácticamente igual con ruido
fuerte. En las figuras, con \(\sigma=10\) el cordero aún se separa del
fondo, pero la repisa se une con el cordero; con \(\sigma=25\) su silueta se pierde; con \(\sigma=50\) las comunidades son franjas
verticales sin relación con la escena.
- El recorte a \([0,255]\) pesa más
aquí: el fondo es casi negro, así que el ruido recortado lo aclara de
forma sistemática y reduce justo el contraste figura-fondo que sostenía
la segmentación.
Comparación entre
imágenes
Lectura. Las dos redes tienen tamaño y estructura
casi iguales (152 y 161 vértices, grado medio ≈ 5.4, transitividad ≈
0.40): la topología la impone SLIC, no la imagen. Lo que las distingue
son los pesos. En el Agnus Dei la mediana de \(d_{ij}\) es 6.42, frente a 16.32 en el
paisaje, y el 66.4 % de las aristas tiene \(w_{ij}\ge 0.5\), frente al 46.2 %: la
paleta reducida de la pintura produce una red mucho más homogénea. Pese
a esto, la robustez frente a ruido fuerte (\(\sigma=50\)) es muy parecida en ambas: con
SLIC fijo el ARI está entre 0.81 y 0.91, y con SLIC variable cae a
0.26–0.29, lo que confirma que el factor determinante es la teselación y
no el contenido de la imagen.
comp <- rbind(cbind(imagen = "Paisaje", net_summary(res_p$graph)),
cbind(imagen = "Agnus Dei", net_summary(res_c$graph)))
comp
| Paisaje |
152 |
407 |
0.0355 |
5.36 |
0.492 |
0.393 |
1 |
0 |
| Agnus Dei |
161 |
435 |
0.0338 |
5.40 |
0.635 |
0.403 |
1 |
0 |
dist_both <- rbind(data.frame(imagen = "Paisaje", d = E(res_p$graph)$d),
data.frame(imagen = "Agnus Dei", d = E(res_c$graph)$d))
dist_both |>
group_by(imagen) |>
summarise(d_mediana = round(median(d), 2), d_media = round(mean(d), 2),
prop_w_mayor_0.5 = round(mean(exp(-d / h0) >= 0.5), 3))
| Agnus Dei |
6.42 |
13.58 |
0.664 |
| Paisaje |
16.32 |
19.90 |
0.462 |
ggplot(dist_both, aes(exp(-d / h0), fill = imagen)) +
geom_histogram(bins = 30, alpha = 0.6, position = "identity") +
scale_fill_manual(values = c("Agnus Dei" = "#7D6608", "Paisaje" = "#2471A3")) +
labs(x = expression(w[ij] ~ "(h = 20)"), y = "Número de aristas", fill = NULL,
title = "Distribución de similitudes visuales") +
theme_minimal(base_size = 12)

bind_rows(mutate(nz_p, imagen = "Paisaje"), mutate(nz_c, imagen = "Agnus Dei")) |>
filter(sigma == max(sigmas)) |>
group_by(imagen, slic, metodo) |>
summarise(ARI_media = round(mean(ARI), 3), .groups = "drop")
| Agnus Dei |
fijo |
leiden |
0.835 |
| Agnus Dei |
fijo |
louvain |
0.807 |
| Agnus Dei |
variable |
leiden |
0.258 |
| Agnus Dei |
variable |
louvain |
0.260 |
| Paisaje |
fijo |
leiden |
0.905 |
| Paisaje |
fijo |
louvain |
0.805 |
| Paisaje |
variable |
leiden |
0.289 |
| Paisaje |
variable |
louvain |
0.276 |
LS0tDQp0aXRsZTogIlJlbGF0b3LDrWE6IEFuw6FsaXNpcyBkZSBpbcOhZ2VuZXMgbWVkaWFudGUgcmVkZXMiDQphdXRob3I6ICJcbioqSnVhbiBGZWxpcGUgTW9yZW5vIFJvZHJpZ3VleioqICBcblxuTWFlc3Ryw61hIGVuIENpZW5jaWFzIC0gRXN0YWTDrXN0aWNhDQogIFwgXG5cblVuaXZlcnNpZGFkIE5hY2lvbmFsIGRlIENvbG9tYmlhXG4iDQpkYXRlOiAiXG4qKkN1cnNvOioqIEFuw6FsaXNpcyBFc3RhZMOtc3RpY28gZGUgUmVkZXMgIFxuXG4qKlByb2Zlc29yOioqIEp1YW4gU29zYSwgUGhEDQogIFwgXG5cbioqMTcgZGUgb2N0dWJyZSBkZSAyMDI2KipcbiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0aGVtZTogY29zbW8NCiAgICBkZl9wcmludDoga2FibGUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBjYWNoZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGRwaSA9IDE1MCwgZmlnLnJldGluYSA9IDIsIG91dC53aWR0aCA9ICIxMDAlIikNCmBgYA0KDQoNCg0KDQojIEludHJvZHVjY2nDs24NCg0KVW4gKipww614ZWwqKiAoZGUgKnBpY3R1cmUgZWxlbWVudCopIGVzIGxhIHVuaWRhZCBtw6FzIHBlcXVlw7FhIGRlIHVuYSBpbWFnZW4gZGlnaXRhbDogdW4gY3VhZHJpdG8gZGUgdW4gc29sbyBjb2xvci4gDQoNClVuYSAqKmltYWdlbioqIGRpZ2l0YWwgZXMgdW4gYXJyZWdsbyBkZSAqKmludGVuc2lkYWRlcyoqIGN1eW9zIHZhbG9yZXMgcmVwcmVzZW50YW4gbG9zIG5pdmVsZXMgZGUgY29sb3IgZGUgY2FkYSBww614ZWwsIGxhICoqaW1hZ2VuKiogdGFtYmnDqW4gc2UgcHVlZGUgdmVyIGNvbW8gdW5hIGN1YWRyw61jdWxhIGRlIHDDrXhlbGVzIG9yZ2FuaXphZGEgZW4gZmlsYXMgeSBjb2x1bW5hcyBxdWUgIHNlIHJlcHJlc2VudGEgY29tbyB1biBhcnJlZ2xvIGRlICRuX3IgXHRpbWVzIG5fYyBcdGltZXMgMyQgbsO6bWVyb3MgKGZpbGFzLCBjb2x1bW5hcyB5IGxvcyB0cmVzIGNhbmFsZXMgZGUgY29sb3IpLg0KDQpVbmEgKipyZWQgZGUgYWR5YWNlbmNpYSBkZSByZWdpb25lcyoqIChSQUcsICpyZWdpb24gYWRqYWNlbmN5IGdyYXBoKikgZGVzY3JpYmUgbGEgcmVsYWNpw7NuIGVudHJlIHN1cyBwYXJ0ZXM6IA0KDQoqIGNhZGEgKip2w6lydGljZSoqIGVzIHVuICpzdXBlcnDDrXhlbCosIGVzIGRlY2lyLCB1bmEgcmVnacOzbiBwZXF1ZcOxYSwgY29uZXhhIHkgZGUgY29sb3IgYXByb3hpbWFkYW1lbnRlIGhvbW9nw6luZW87DQoqIGNhZGEgKiphcmlzdGEqKiB1bmUgZG9zIHN1cGVycMOteGVsZXMgcXVlIHNlIHRvY2FuIChjb21wYXJ0ZW4gYWwgbWVub3MgdW4gbGFkbyBkZSBww614ZWwsIGFkeWFjZW5jaWEgZGUgNCB2ZWNpbm9zKTsNCiogY2FkYSBhcmlzdGEgbGxldmEgdW4gKipwZXNvIGRlIHNpbWlsaXR1ZCB2aXN1YWwqKiBxdWUgaW5kaWNhIHF1w6kgdGFuIHBhcmVjaWRvcyBzb24gbG9zIGNvbG9yZXMgZGUgbGFzIGRvcyByZWdpb25lcy4NCg0KRWwgb2JqZXRpdm8gbm8gZXMgcmVjb25vY2VyIG9iamV0b3Mgc2lubyAqKmNhcmFjdGVyaXphciBsYSBlc3RydWN0dXJhIHZpc3VhbCoqIGRlIGxhIGltYWdlbi4gTGFzIGNvbXVuaWRhZGVzIGRlIGxhIHJlZCBhZ3J1cGFuIHJlZ2lvbmVzIHZpc3VhbG1lbnRlIHJlbGFjaW9uYWRhcywgcXVlIHB1ZWRlbiBjb2luY2lkaXIgbyBubyBjb24gb2JqZXRvcyByZWFsZXMuDQoNCg0KDQojIyBTdXBlcnDDrXhlbGVzIGNvbiBTTElDDQoNClNMSUMgKCpTaW1wbGUgTGluZWFyIEl0ZXJhdGl2ZSBDbHVzdGVyaW5nKikgZXMgdW4gJGskLW1lZGlhcyBsb2NhbCBlbiBlbCBlc3BhY2lvIChjb2xvciwgcG9zaWNpw7NuKS4gU2UgZmlqYSBlbCBuw7ptZXJvIGRlc2VhZG8gZGUgc3VwZXJww614ZWxlcyAkSyQsIHNlIGluaWNpYWxpemFuIGNlbnRyb3MgZW4gdW5hIHJlamlsbGEgZGUgcGFzbyAkUz1cc3FydHtOL0t9JCAoJE4kID0gbsO6bWVybyBkZSBww614ZWxlcykgeSBjYWRhIHDDrXhlbCBzZSBhc2lnbmEgYWwgY2VudHJvIGNlcmNhbm8gcXVlIG1pbmltaXphDQoNCiQkDQpEPVxzcXJ0e2RfY14yK1xsZWZ0KFxmcmFje219e1N9XCxkX3NccmlnaHQpXjJ9LA0KJCQNCg0KZG9uZGUgJGRfYyQgZXMgbGEgZGlzdGFuY2lhIGRlIGNvbG9yLCAkZF9zJCBsYSBkaXN0YW5jaWEgZXNwYWNpYWwgeSAkbSQgbGEgKmNvbXBhY3RuZXNzKi4gVmFsb3JlcyBncmFuZGVzIGRlICRtJCBwcm9kdWNlbiByZWdpb25lcyByZWd1bGFyZXMgKGNhc2kgY3VhZHJhZGFzKTsgdmFsb3JlcyBwZXF1ZcOxb3MgaGFjZW4gcXVlIGxhcyByZWdpb25lcyBzaWdhbiBtZWpvciBsb3MgYm9yZGVzIGRlIGNvbG9yLg0KDQojIyBFc3BhY2lvcyBkZSBjb2xvcjogUkdCIHkgQ0lFTEFCDQoNCkxvcyBjb2xvcmVzIGRlIHVuYSBpbWFnZW4gcHVlZGVuIGRlc2NyaWJpcnNlIGVuIGRpc3RpbnRvcyAqKmVzcGFjaW9zIGRlIGNvbG9yKiouIExhIGltYWdlbiBzZSBsZWUgZW4gUkdCLCBwZXJvIGxhcyBkaXN0YW5jaWFzIGVudHJlIGNvbG9yZXMgc2UgY2FsY3VsYW4gZW4gQ0lFTEFCLiBMYSByYXrDs24gZXMgcXVlIGVuIFJHQiBsYSBkaXN0YW5jaWEgZXVjbGlkaWFuYSBubyBjb3JyZXNwb25kZSBhIGxhIGRpZmVyZW5jaWEgZGUgY29sb3IgcXVlIHBlcmNpYmUgdW5hIHBlcnNvbmEsIHkgZW4gQ0lFTEFCIHPDrSwgZGUgZm9ybWEgYXByb3hpbWFkYS4NCg0KYGBge3IgZmlnLXJnYiwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI1NSUiLCBmaWcuY2FwPSJSdWVkYSBkZSB0b25vcyBjb24gc3VzIGNvb3JkZW5hZGFzIFJHQi4gQ2FkYSB0b25vIHB1cm8gdGllbmUgdW4gw6FuZ3VsbyAoMMKwIHJvam8sIDEyMMKwIHZlcmRlLCAyNDDCsCBhenVsKSB5IHVuYSB0ZXJuYSAoUiwgRywgQikgZW4gbGEgZXNjYWxhIDDigJMyNTUuIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJydWVkYV9yZ2IucG5nIikNCmBgYA0KDQoqKlJHQioqIGVzIHVuIG1vZGVsbyAqKmFkaXRpdm8qKjogY2FkYSBww614ZWwgc2UgZGVzY3JpYmUgcG9yIGxhIGludGVuc2lkYWQgZGUgdHJlcyBsdWNlcyBwcmltYXJpYXMsIHJvam8gKFIpLCB2ZXJkZSAoRykgeSBhenVsIChCKSwgY2FkYSB1bmEgZW50cmUgMCB5IDI1NS4gRWwgbmVncm8gZXMgKDAsIDAsIDApLCBlbCBibGFuY28gKDI1NSwgMjU1LCAyNTUpIHkgbG9zIGNvbG9yZXMgaW50ZXJtZWRpb3Mgc2Ugb2J0aWVuZW4gc3VtYW5kbyBsdWNlczogcm9qbyArIHZlcmRlID0gYW1hcmlsbG8gKDI1NSwgMjU1LCAwKSwgdmVyZGUgKyBhenVsID0gY2lhbiAoMCwgMjU1LCAyNTUpIHkgcm9qbyArIGF6dWwgPSBtYWdlbnRhICgyNTUsIDAsIDI1NSksIGNvbW8gc2UgdmUgZW4gbGEgcnVlZGEuIEVzIGVsIGZvcm1hdG8gbmF0dXJhbCBkZSBsYXMgY8OhbWFyYXMgeSBsYXMgcGFudGFsbGFzLCBwZXJvIHRpZW5lIGRvcyBwcm9ibGVtYXMgcGFyYSBtZWRpciBkaWZlcmVuY2lhcyBkZSBjb2xvcjoNCg0KKiAqKk5vIGVzIHBlcmNlcHR1YWxtZW50ZSB1bmlmb3JtZS4qKiBMYSBtaXNtYSBkaXN0YW5jaWEgZXVjbGlkaWFuYSBlbiBSR0IgcHVlZGUgY29ycmVzcG9uZGVyIGEgZGlmZXJlbmNpYXMgbXV5IHZpc2libGVzIGVuIHVuYSB6b25hIGRlbCBlc3BhY2lvIHkgY2FzaSBpbXBlcmNlcHRpYmxlcyBlbiBvdHJhLiBFbiBsYSBydWVkYSwgcG9yIGVqZW1wbG8sIGxhIGZyYW5qYSBxdWUgZWwgb2pvIHZlIGNvbW8gInZlcmRlIiBvY3VwYSB1biByYW5nbyBkZSDDoW5ndWxvcyBtdWNobyBtw6FzIGFtcGxpbyBxdWUgbGEgZGVsIGFtYXJpbGxvIG8gZWwgY2lhbjogbXVjaG9zIHZhbG9yZXMgUkdCIGRpc3RpbnRvcyBzZSBwZXJjaWJlbiBjYXNpIGlndWFsZXMuDQoqICoqTWV6Y2xhIGx1bWlub3NpZGFkIHkgY29sb3IuKiogT3NjdXJlY2VyIHVuIGNvbG9yIHJlZHVjZSBsb3MgdHJlcyBjYW5hbGVzIGEgbGEgdmV6LCBhc8OtIHF1ZSB1bmEgbWlzbWEgc3VwZXJmaWNpZSBiYWpvIGx1eiB5IGJham8gc29tYnJhIHF1ZWRhIGxlam9zIGVuIFJHQiBhdW5xdWUgc3UgY29sb3Igc2VhIGVsIG1pc21vLg0KDQpgYGB7ciBmaWctY2llbGFiLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjU1JSIsIGZpZy5jYXA9IlJlcHJlc2VudGFjacOzbiBkZWwgZXNwYWNpbyBDSUVMQUIuIEVsIGVqZSB2ZXJ0aWNhbCBlcyBsYSBsdW1pbm9zaWRhZCBMKiAobmVncm8gYWJham8sIGJsYW5jbyBhcnJpYmEpOyBlbCBwbGFubyBob3Jpem9udGFsIGNvbnRpZW5lIGxvcyBlamVzIGRlIGNvbG9yIGEqICh2ZXJkZSBhIHJvam8pIHkgYiogKGF6dWwgYSBhbWFyaWxsbykuIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJlc3BhY2lvX2NpZWxhYi5wbmciKQ0KYGBgDQoNCioqQ0lFTEFCKiogKG8gTFwqYVwqYlwqLCBkZWZpbmlkbyBwb3IgbGEgQ29taXNpw7NuIEludGVybmFjaW9uYWwgZGUgbGEgSWx1bWluYWNpw7NuIGVuIDE5NzYpIHNlcGFyYSBsYSBsdW1pbm9zaWRhZCBkZWwgY29sb3IgeSBvcmdhbml6YSBsb3MgY29sb3JlcyBjb21vIGxvIGhhY2UgbGEgdmlzacOzbiBodW1hbmEsIG1lZGlhbnRlIHBhcmVzIGRlIGNvbG9yZXMgb3B1ZXN0b3M6DQoNCiogKipMXCoqKiBlcyBsYSBsdW1pbm9zaWRhZCwgZGUgMCAobmVncm8pIGEgMTAwIChibGFuY28pOiBlbCBlamUgdmVydGljYWwgZGUgbGEgZXNmZXJhLg0KKiAqKmFcKioqIHZhIGRlbCB2ZXJkZSAodmFsb3JlcyBuZWdhdGl2b3MpIGFsIHJvam8gKHBvc2l0aXZvcykuDQoqICoqYlwqKiogdmEgZGVsIGF6dWwgKG5lZ2F0aXZvcykgYWwgYW1hcmlsbG8gKHBvc2l0aXZvcykuDQoNCkVuIGVsIHBsYW5vIGhvcml6b250YWwsIGVsIMOhbmd1bG8gaW5kaWNhIGVsIHRvbm8geSBsYSBkaXN0YW5jaWEgYWwgZWplIGNlbnRyYWwgaW5kaWNhIGxhIHNhdHVyYWNpw7NuOiBsb3MgZ3Jpc2VzIGVzdMOhbiBzb2JyZSBlbCBlamUgeSBsb3MgY29sb3JlcyBtw6FzIHZpdm9zLCBlbiBlbCBib3JkZS4gQ0lFTEFCIHNlIGRpc2XDscOzIHBhcmEgcXVlIGxhIGRpc3RhbmNpYSBldWNsaWRpYW5hIGVudHJlIGRvcyBjb2xvcmVzLCBsbGFtYWRhICRcRGVsdGEgRSQsIHNlYSBhcHJveGltYWRhbWVudGUgcHJvcG9yY2lvbmFsIGEgbGEgZGlmZXJlbmNpYSBwZXJjaWJpZGE6IHVuICRcRGVsdGEgRVxhcHByb3ggMnssfTMkIGVzLCBlbiBwcm9tZWRpbywgbGEgbWVub3IgZGlmZXJlbmNpYSBxdWUgdW5hIHBlcnNvbmEgZGlzdGluZ3VlLCB5IGRpZmVyZW5jaWFzIG1heW9yZXMgcXVlIDEwIHNlIHBlcmNpYmVuIGNvbW8gY29sb3JlcyBjbGFyYW1lbnRlIGRpc3RpbnRvcy4gUG9yIGVzbyBlcyBlbCBlc3BhY2lvIGFkZWN1YWRvIHBhcmEgZGVmaW5pciAkZF97aWp9JCB5LCBjb24gZWxsYSwgZWwgcGVzbyBkZSBzaW1pbGl0dWQgdmlzdWFsLg0KDQpMYSBjb252ZXJzacOzbiBkZSBSR0IgYSBDSUVMQUIgKGZ1bmNpw7NuIGByZ2JfdG9fbGFiYCkgdGllbmUgdHJlcyBwYXNvczoNCg0KMS4gKipMaW5lYWxpemFjacOzbi4qKiBMb3MgdmFsb3JlcyBSR0IgZGUgdW5hIGltYWdlbiBkaWdpdGFsIChzUkdCKSBlc3TDoW4gY29kaWZpY2Fkb3MgY29uIHVuYSBjb3JyZWNjacOzbiBnYW1tYS4gU2UgcGFzYW4gYSAkWzAsMV0kIHkgc2UgZGVzaGFjZSBlc2EgY29ycmVjY2nDs24gcGFyYSBvYnRlbmVyIGludGVuc2lkYWRlcyBkZSBsdXogcHJvcG9yY2lvbmFsZXMgYSBsYSBlbmVyZ8OtYSBmw61zaWNhLg0KMi4gKipQYXNvIGEgWFlaLioqIFVuYSBtYXRyaXogbGluZWFsIGZpamEgdHJhbnNmb3JtYSBsYXMgaW50ZW5zaWRhZGVzIFJHQiBlbiBsYXMgY29vcmRlbmFkYXMgWFlaIGRlIGxhIENJRSwgcXVlIGRlc2NyaWJlbiBlbCBjb2xvciBzZWfDum4gbGEgcmVzcHVlc3RhIGRlIGxvcyB0cmVzIHRpcG9zIGRlIGNvbm9zIGRlbCBvam8uIFNlIGRpdmlkZW4gcG9yIGVsIGJsYW5jbyBkZSByZWZlcmVuY2lhIEQ2NSAobHV6IGRlIGTDrWEpLg0KMy4gKipQYXNvIGEgTFwqYVwqYlwqLioqIFNlIGFwbGljYSB1bmEgcmHDrXogY8O6YmljYSAoY29uIHVuIHRyYW1vIGxpbmVhbCBjZXJjYSBkZSBjZXJvKSwgcXVlIGltaXRhIGxhIHJlc3B1ZXN0YSBubyBsaW5lYWwgZGVsIG9qbyBhIGxhIGludGVuc2lkYWQsIHkgc2UgY29tYmluYW4gbG9zIHJlc3VsdGFkb3M6IExcKiBzYWxlIGRlIFksIGFcKiBkZSBsYSBkaWZlcmVuY2lhIGVudHJlIFggZSBZIHkgYlwqIGRlIGxhIGRpZmVyZW5jaWEgZW50cmUgWSB5IFouDQoNCiMjIEF0cmlidXRvcywgZGlzdGFuY2lhIGRlIGNvbG9yIHkgcGVzb3MNCg0KUGFyYSBjYWRhIHN1cGVycMOteGVsICRpJCBzZSBjYWxjdWxhIHN1IMOhcmVhLCBzdSBjZW50cm9pZGUgeSBzdSBjb2xvciBtZWRpbyBlbiBDSUVMQUIsICQoXGJhciBMX2ksXGJhciBhX2ksXGJhciBiX2kpJC4gQ0lFTEFCIHNlIHVzYSBwb3JxdWUgbGFzIGRpc3RhbmNpYXMgZXVjbGlkaWFuYXMgZW4gZXNlIGVzcGFjaW8gc2UgYXByb3hpbWFuIGEgZGlmZXJlbmNpYXMgZGUgY29sb3IgcGVyY2liaWRhcy4gTGEgZGlzdGFuY2lhIGVudHJlIHJlZ2lvbmVzIHZlY2luYXMgZXMNCg0KJCQNCmRfe2lqfT1cc3FydHsoXGJhciBMX2ktXGJhciBMX2opXjIrKFxiYXIgYV9pLVxiYXIgYV9qKV4yKyhcYmFyIGJfaS1cYmFyIGJfaileMn0sDQokJA0KDQp5IGxhICoqc2ltaWxpdHVkIHZpc3VhbCoqIGVzDQoNCiQkDQp3X3tpan09XGV4cFwhXGxlZnQoLVxmcmFje2Rfe2lqfX17aH1ccmlnaHQpLFxxcXVhZCBoPjAsXHFxdWFkIHdfe2lqfVxpbigwLDFdLg0KJCQNCg0KRWwgcGFyw6FtZXRybyAkaCQgZXMgdW5hIGVzY2FsYSBkZSBjb2xvcjogaW5kaWNhIGN1w6FudGFzIHVuaWRhZGVzIGRlICRcRGVsdGEgRSQgaGFjZW4gY2FlciBsYSBzaW1pbGl0dWQgYSAkZV57LTF9XGFwcHJveCAwLjM3JC4gUGFyYSBtZWRpZGFzIGJhc2FkYXMgZW4gY2FtaW5vcyAoaW50ZXJtZWRpYWNpw7NuKSBzZSB1c2EgZWwgY29zdG8gJFxlbGxfe2lqfT0xL3dfe2lqfSQuIEFkZW3DoXMgZGUgJHdfe2lqfSQsIGNhZGEgYXJpc3RhIGd1YXJkYSBsYSBsb25naXR1ZCBkZSBmcm9udGVyYSAkYl97aWp9JCAobsO6bWVybyBkZSBsYWRvcyBkZSBww614ZWwgY29tcGFydGlkb3MpLg0KDQpMYSBmdW5jacOzbiBleHBvbmVuY2lhbCBubyBlcyBsYSDDum5pY2Egb3BjacOzbjogcHVlZGUgdXNhcnNlIGN1YWxxdWllciBzaW1pbGl0dWQgcXVlIGRlY3JlemNhIGNvbiBsYSBkaXN0YW5jaWEgZGUgY29sb3IsIHBvciBlamVtcGxvIHVuIG7DumNsZW8gZ2F1c3NpYW5vICRcZXhwKC1kX3tpan1eMi8yaF4yKSQuIENvbiBsYSBlbGVnaWRhLCBzaSBkb3Mgc3VwZXJww614ZWxlcyB0aWVuZW4gZWwgbWlzbW8gY29sb3IgbWVkaW8gZW4gQ0lFTEFCLCAkZF97aWp9PTAkIHkgJHdfe2lqfT0xJDsgYSBtZWRpZGEgcXVlICRkX3tpan0kIGNyZWNlLCAkd197aWp9JCBkZWNyZWNlIGhhY2lhIDAgc2luIGFsY2FuemFybG8gKGFzw61udG90YSBlbiAwKS4gUG9yIGVzbyBsb3MgcGVzb3Mgc29uICoqaW50ZW5zaWRhZGVzKio6IHVuIHZhbG9yIGdyYW5kZSBpbmRpY2EgcmVnaW9uZXMgcGFyZWNpZGFzLCB5IHBhcmEgbGFzIG1lZGlkYXMgYmFzYWRhcyBlbiBjYW1pbm9zIHNlIHVzYSBlbCBjb3N0byAkXGVsbF97aWp9PTEvd197aWp9JC4NCg0KDQojIyBGaWx0cmFkbyBwb3IgdW1icmFsICRcdGF1JA0KDQpDb21vICR3X3tpan0+MCQgcGFyYSB0b2RvIHBhciBkZSBzdXBlcnDDrXhlbGVzIHZlY2lub3MsIGxhIHJlZCBkZSByZWdpb25lcyBlcyBjb25leGEgeSBjb25zZXJ2YSBhcmlzdGFzIGluY2x1c28gZW50cmUgcmVnaW9uZXMgZGUgY29sb3JlcyBtdXkgZGlzdGludG9zLiBQYXJhIGVzdHVkaWFyIGxhIGVzdHJ1Y3R1cmEgZGUgbGEgcmVkIHNlIGNvbnNlcnZhbiBzb2xvIGxhcyBhcmlzdGFzIGNvbiAkd197aWp9XGdlXHRhdSQuIENvbW8gJHdfe2lqfSQgZXMgbW9uw7N0b25hIGVuICRkX3tpan0kLCBlc3RvIGVxdWl2YWxlIGENCg0KJCQNCndfe2lqfVxnZVx0YXUgXGlmZiBkX3tpan1cbGUgLWhcbG5cdGF1IC4NCiQkDQoNCkFsIGF1bWVudGFyICRcdGF1JCBzZSBlbGltaW5hbiBsYXMgY29uZXhpb25lcyBlbnRyZSByZWdpb25lcyBkZSBjb2xvcmVzIGRpc3RpbnRvcyB5IGxhIHJlZCBzZSBmcmFnbWVudGEgZW4gY29tcG9uZW50ZXMuDQoNClBvciByZWZlcmVuY2lhIHNlIHV0aWxpemEgJFx0YXU9MC41JC4NCg0KIyMgQ29tdW5pZGFkZXM6IExvdXZhaW4geSBMZWlkZW4NCg0KQW1ib3MgYWxnb3JpdG1vcyBtYXhpbWl6YW4gbGEgKiptb2R1bGFyaWRhZCBwb25kZXJhZGEqKg0KDQokJA0KUT1cZnJhY3sxfXsyV31cc3VtX3tpLGp9XGxlZnQod197aWp9LVxmcmFje3NfaSBzX2p9ezJXfVxyaWdodClcZGVsdGEoY19pLGNfaiksDQokJA0KDQpkb25kZSAkc19pPVxzdW1faiB3X3tpan0kIGVzIGxhIGZ1ZXJ6YSBkZWwgdsOpcnRpY2UgJGkkIHkgJFckIGxhIHN1bWEgZGUgcGVzb3MuICoqTG91dmFpbioqIGFsdGVybmEgbW92aW1pZW50b3MgbG9jYWxlcyBkZSB2w6lydGljZXMgeSBhZ3JlZ2FjacOzbiBkZSBjb211bmlkYWRlcy4gKipMZWlkZW4qKiBhw7FhZGUgdW5hIGZhc2UgZGUgcmVmaW5hbWllbnRvIHF1ZSBnYXJhbnRpemEgY29tdW5pZGFkZXMgaW50ZXJuYW1lbnRlIGNvbmVjdGFkYXMsIHkgc3VlbGUgYWxjYW56YXIgbW9kdWxhcmlkYWRlcyBpZ3VhbGVzIG8gbWF5b3Jlcy4gUHJveWVjdGFuZG8gbGEgY29tdW5pZGFkIGRlIGNhZGEgc3VwZXJww614ZWwgc29icmUgc3VzIHDDrXhlbGVzIHNlIG9idGllbmUgdW5hICoqc2VnbWVudGFjacOzbioqIGRlIGxhIGltYWdlbi4gVW5hIG1vZHVsYXJpZGFkIGFsdGEgbm8gZ2FyYW50aXphIHVuYSBzZWdtZW50YWNpw7NuICJidWVuYSI6ICRRJCBlcyB1bmEgbWVkaWRhIGludGVybmEgZGUgbGEgcmVkLCBubyBkZSBsYSBjYWxpZGFkIHZpc3VhbC4NCg0KIyMgRXZhbHVhY2nDs24geSByb2J1c3Rleg0KDQpTZSBkaXN0aW5ndWUgZXZhbHVhY2nDs24gaW50ZXJuYSAobW9kdWxhcmlkYWQpIHkgZXh0ZXJuYSAoQVJJLCBJb1UsIEYxIGRlIGZyb250ZXJhcywgcXVlIHJlcXVpZXJlbiB1bmEgc2VnbWVudGFjacOzbiBkZSByZWZlcmVuY2lhKSwgeSBzZSBwcm9wb25lIGVzdHVkaWFyIGxhIHNlbnNpYmlsaWRhZCBhICRLJCwgJG0kLCAkaCQgeSAkXHRhdSQsIHkgbGEgcm9idXN0ZXogZnJlbnRlIGEgcnVpZG8sIHN1YXZpemFkbyBvIGNhbWJpb3MgZGUgaWx1bWluYWNpw7NuLiBMb3MgZXhwZXJpbWVudG9zIGRlIGVzdGEgcmVsYXRvcsOtYSBzZSB1YmljYW4gcHJlY2lzYW1lbnRlIGVuIGVzZSDDumx0aW1vIGJsb3F1ZS4NCg0KIyBJbXBsZW1lbnRhY2nDs24NCg0KTGFzIHNpZ3VpZW50ZXMgZnVuY2lvbmVzIG5vcyB2YW4gYSBwZXJtaXRpciBsYSBjb25zdHJ1Y2Npw7NuIGRlbCBhbsOhbGlzaXM6IHByZXBhcmFjacOzbiBkZSBsYSBpbWFnZW4sIFNMSUMsIGdyYWZvIGRlIHJlZ2lvbmVzIGNvbiBkaXN0YW5jaWFzIENJRUxBQiB5IHBlc29zICR3X3tpan0kLCBmaWx0cmFkbyBwb3IgJFx0YXUkLCBkZXRlY2Npw7NuIGRlIGNvbXVuaWRhZGVzIHkgcHJveWVjY2nDs24gYSBww614ZWxlcy4NCg0KYGBge3IgcGFxdWV0ZXN9DQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoew0KICBsaWJyYXJ5KE9wZW5JbWFnZVIpDQogIGxpYnJhcnkoaWdyYXBoKQ0KICBsaWJyYXJ5KGRwbHlyKQ0KICBsaWJyYXJ5KHRpZHlyKQ0KICBsaWJyYXJ5KGdncGxvdDIpDQogIGxpYnJhcnkoa25pdHIpDQp9KQ0Kc2V0LnNlZWQoMjAyNikNCmBgYA0KDQojIyBGdW5jaW9uZXMgZGUgZ3LDoWZpY29zDQoNClNlIGRlZmluZW4gcHJpbWVybyBwYXJhIHBvZGVyIG1vc3RyYXIgZWwgcmVzdWx0YWRvIGRlIGNhZGEgcGFzbyBlbiBsYXMgc2VjY2lvbmVzIHNpZ3VpZW50ZXMuDQoNCmBgYHtyIGZ1bmNpb25lcy1ncmFmaWNvc30NCmJvdW5kYXJ5X21hc2sgPC0gZnVuY3Rpb24ocGFydCkgew0KICBiIDwtIG1hdHJpeChGQUxTRSwgbnJvdyhwYXJ0KSwgbmNvbChwYXJ0KSkNCiAgYlssIC1uY29sKHBhcnQpXSA8LSBiWywgLW5jb2wocGFydCldIHwgcGFydFssIC1uY29sKHBhcnQpXSAhPSBwYXJ0WywgLTFdDQogIGJbLW5yb3cocGFydCksIF0gPC0gYlstbnJvdyhwYXJ0KSwgXSB8IHBhcnRbLW5yb3cocGFydCksIF0gIT0gcGFydFstMSwgXQ0KICBiDQp9DQoNCiMgQ2FkYSBww614ZWwgc2UgcGludGEgY29uIGVsIGNvbG9yIG1lZGlvIGRlIHN1IHJlZ2nDs24vY29tdW5pZGFkDQptZWFuX2NvbG9yX2ltYWdlIDwtIGZ1bmN0aW9uKGltZywgcGFydCkgew0KICBvdXQgPC0gaW1nDQogIGZvciAoayBpbiAxOjMpIHsNCiAgICBjaCA8LSBpbWdbLCAsIGtdOyBtdSA8LSB0YXBwbHkoY2gsIHBhcnQsIG1lYW4pDQogICAgb3V0WywgLCBrXSA8LSBtYXRyaXgobXVbYXMuY2hhcmFjdGVyKHBhcnQpXSwgbnJvdyhwYXJ0KSkNCiAgfQ0KICBvdXQNCn0NCg0KcGxvdF9wYW5lbCA8LSBmdW5jdGlvbihpbWcsIHBhcnQgPSBOVUxMLCBtYWluID0gIiIsIGZpbGwgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICBib3JkZXJfY29sID0gYygwLCAwLCAwKSwgaW50ZXJwb2xhdGUgPSBpcy5udWxsKHBhcnQpKSB7DQogIG5yIDwtIGRpbShpbWcpWzFdOyBuYyA8LSBkaW0oaW1nKVsyXQ0KICByYXMgPC0gaWYgKCFpcy5udWxsKHBhcnQpICYmIGZpbGwpIG1lYW5fY29sb3JfaW1hZ2UoaW1nLCBwYXJ0KSBlbHNlIGltZw0KICByYXMgPC0gcmFzIC8gMjU1DQogIGlmICghaXMubnVsbChwYXJ0KSkgew0KICAgIGIgPC0gYm91bmRhcnlfbWFzayhwYXJ0KQ0KICAgIGZvciAoayBpbiAxOjMpIHsgdG1wIDwtIHJhc1ssICwga107IHRtcFtiXSA8LSBib3JkZXJfY29sW2tdOyByYXNbLCAsIGtdIDwtIHRtcCB9DQogIH0NCiAgcGxvdC5uZXcoKQ0KICBwbG90LndpbmRvdyhjKDAsIG5jKSwgYygwLCBuciksIGFzcCA9IDEsIHhheHMgPSAiaSIsIHlheHMgPSAiaSIpDQogIHJhc3RlckltYWdlKGFzLnJhc3RlcihyYXMpLCAwLCAwLCBuYywgbnIsIGludGVycG9sYXRlID0gaW50ZXJwb2xhdGUpDQogIHRpdGxlKG1haW4sIGNleC5tYWluID0gMC44NSwgZm9udC5tYWluID0gMSwgbGluZSA9IDAuMykNCn0NCg0KIyBSZWQgZGUgY2VudHJvaWRlcyBzb2JyZSBsYSBpbWFnZW47IGdyb3NvciB5IG9wYWNpZGFkIHByb3BvcmNpb25hbGVzIGEgd19pag0KcGxvdF9ncmFwaF9vdmVybGF5IDwtIGZ1bmN0aW9uKHJlcywgbWFpbiA9ICIiKSB7DQogIGcgPC0gcmVzJGdyYXBoOyBpbWcgPC0gcmVzJGltZzsgbnIgPC0gZGltKGltZylbMV0NCiAgcGxvdF9wYW5lbChpbWcsIG1haW4gPSBtYWluKQ0KICBlbCA8LSBlbmRzKGcsIEUoZyksIG5hbWVzID0gRkFMU0UpDQogIHggPC0gVihnKSRjb2wgLSAwLjU7IHkgPC0gbnIgLSBWKGcpJHJvdyArIDAuNQ0KICBzZWdtZW50cyh4W2VsWywgMV1dLCB5W2VsWywgMV1dLCB4W2VsWywgMl1dLCB5W2VsWywgMl1dLA0KICAgICAgICAgICBsd2QgPSAwLjQgKyAyLjIgKiBFKGcpJHcsDQogICAgICAgICAgIGNvbCA9IHJnYigxLCAxLCAxLCBhbHBoYSA9IDAuMjUgKyAwLjc1ICogRShnKSR3KSkNCiAgcG9pbnRzKHgsIHksIHBjaCA9IDE5LCBjZXggPSAwLjM1LCBjb2wgPSAiI0ZGRDQwMCIpDQp9DQpgYGANCg0KYGBge3IgaW1hZ2VuLWVqZW1wbG99DQojIEltYWdlbiB1c2FkYSBwYXJhIGlsdXN0cmFyIGNhZGEgcGFzbyBkZSBsYSBpbXBsZW1lbnRhY2nDs24NCnJ1dGFfZWogPC0gInBhaXNhamVfcmRhdmlsYS5qcGciDQpgYGANCg0KIyMgUHJlcGFyYWNpw7NuIGRlIGxhIGltYWdlbiB5IHJ1aWRvDQoNCmBgYHtyIGZ1bmNpb25lcy1pbWFnZW59DQojIExlZSB1bmEgaW1hZ2VuLCBsYSBkZWphIGVuIFJHQiBjb24gdmFsb3JlcyBlbiBbMCwgMjU1XSB5IGxhIHJlZHVjZSBkZSB0YW1hw7FvDQpwcmVwYXJlX3JnYiA8LSBmdW5jdGlvbihwYXRoLCBtYXhfc2lkZSA9IDI0MCkgew0KICBpbWcgPC0gT3BlbkltYWdlUjo6cmVhZEltYWdlKHBhdGgpDQogIGlmIChsZW5ndGgoZGltKGltZykpID09IDIpIGltZyA8LSBhcnJheShpbWcsIGMoZGltKGltZyksIDMpKQ0KICBpZiAoZGltKGltZylbM10gPT0gNCkgaW1nIDwtIGltZ1ssICwgMTozXQ0KICBpZiAobWF4KGltZykgPD0gMSkgaW1nIDwtIGltZyAqIDI1NQ0KICBuciA8LSBkaW0oaW1nKVsxXTsgbmMgPC0gZGltKGltZylbMl0NCiAgcyA8LSBtYXhfc2lkZSAvIG1heChuciwgbmMpDQogIGlmIChzIDwgMSkgew0KICAgIGltZyA8LSBPcGVuSW1hZ2VSOjpyZXNpemVJbWFnZShpbWcsIHJvdW5kKG5yICogcyksIHJvdW5kKG5jICogcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJiaWxpbmVhciIpDQogICAgaWYgKG1heChpbWcpIDw9IDEpIGltZyA8LSBpbWcgKiAyNTUNCiAgfQ0KICBwbWluKHBtYXgoaW1nLCAwKSwgMjU1KQ0KfQ0KDQojIFJ1aWRvIGdhdXNzaWFubyBhZGl0aXZvIE4oMCwgc2lnbWFeMikgZW4gY2FkYSBjYW5hbCAoZXNjYWxhIDAtMjU1KQ0KYWRkX25vaXNlIDwtIGZ1bmN0aW9uKGltZywgc2lnbWEsIHNlZWQgPSAxKSB7DQogIGlmIChzaWdtYSA9PSAwKSByZXR1cm4oaW1nKQ0KICBzZXQuc2VlZChzZWVkKQ0KICBvdXQgPC0gaW1nICsgYXJyYXkocm5vcm0obGVuZ3RoKGltZyksIDAsIHNpZ21hKSwgZGltKGltZykpDQogIHBtaW4ocG1heChvdXQsIDApLCAyNTUpDQp9DQoNCiMgc1JHQiAoMC0yNTUpIC0+IENJRUxBQiAoaWx1bWluYW50ZSBENjUpDQpyZ2JfdG9fbGFiIDwtIGZ1bmN0aW9uKHJnYikgew0KICB2IDwtIHJnYiAvIDI1NQ0KICB2IDwtIGlmZWxzZSh2IDw9IDAuMDQwNDUsIHYgLyAxMi45MiwgKCh2ICsgMC4wNTUpIC8gMS4wNTUpXjIuNCkNCiAgTSA8LSBtYXRyaXgoYygwLjQxMjQ1NjQsIDAuMzU3NTc2MSwgMC4xODA0Mzc1LA0KICAgICAgICAgICAgICAgIDAuMjEyNjcyOSwgMC43MTUxNTIyLCAwLjA3MjE3NTAsDQogICAgICAgICAgICAgICAgMC4wMTkzMzM5LCAwLjExOTE5MjAsIDAuOTUwMzA0MSksIDMsIGJ5cm93ID0gVFJVRSkNCiAgeHl6IDwtIHYgJSolIHQoTSkNCiAgeHl6IDwtIHN3ZWVwKHh5eiwgMiwgYygwLjk1MDQ3LCAxLCAxLjA4ODgzKSwgIi8iKQ0KICBmIDwtIGlmZWxzZSh4eXogPiAoNiAvIDI5KV4zLCB4eXpeKDEgLyAzKSwgeHl6IC8gKDMgKiAoNiAvIDI5KV4yKSArIDQgLyAyOSkNCiAgY2JpbmQoTCA9IDExNiAqIGZbLCAyXSAtIDE2LA0KICAgICAgICBhID0gNTAwICogKGZbLCAxXSAtIGZbLCAyXSksDQogICAgICAgIGIgPSAyMDAgKiAoZlssIDJdIC0gZlssIDNdKSkNCn0NCmBgYA0KDQpgYGB7ciBlai1pbWFnZW4sIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0zLjZ9DQppbWdfZWogPC0gcHJlcGFyZV9yZ2IocnV0YV9laiwgbWF4X3NpZGUgPSA0MDApDQpsYWJfZWogPC0gcmdiX3RvX2xhYihtYXRyaXgoaW1nX2VqLCBuY29sID0gMykpDQpMX2VqICAgPC0gbWF0cml4KGxhYl9lalssICJMIl0sIG5yb3coaW1nX2VqKSwgbmNvbChpbWdfZWopKSAgICMgbHVtaW5hbmNpYSBMKiAoMC0xMDApDQoNCm9wIDwtIHBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoMC4zLCAwLjMsIDEuNiwgMC4zKSkNCnBsb3RfcGFuZWwoaW1nX2VqLCBtYWluID0gc3ByaW50ZigiSW1hZ2VuIHByZXBhcmFkYSAoJWQgeCAlZCBweCkiLCBucm93KGltZ19laiksIG5jb2woaW1nX2VqKSkpDQpwbG90X3BhbmVsKGFkZF9ub2lzZShpbWdfZWosIDI1KSwgbWFpbiA9ICJDb24gcnVpZG8gZ2F1c3NpYW5vLCBzaWdtYSA9IDI1IikNCnBsb3RfcGFuZWwoYXJyYXkoTF9laiAvIDEwMCAqIDI1NSwgYyhkaW0oTF9laiksIDMpKSwgbWFpbiA9ICJDYW5hbCBMKiBkZSBDSUVMQUIiKQ0KcGFyKG9wKQ0KYGBgDQoNCkNvbW8gaWx1c3RyYWNpw7NuIHNlIHVzYSBsYSBmb3RvZ3JhZsOtYSBkZWwgcGFpc2FqZSwgcmVkdWNpZGEgYSA0MDAgcMOteGVsZXMgZW4gc3UgbGFkbyBtYXlvciBjb21vIGVuIGVsIHJlc3RvIGRlbCBhbsOhbGlzaXMuIEVsIHJ1aWRvIGdhdXNzaWFubyBzZSBzdW1hIGNhbmFsIHBvciBjYW5hbCB5IHNlIHJlY29ydGEgYSBbMCwgMjU1XS4gRWwgdGVyY2VyIHBhbmVsIG11ZXN0cmEgZWwgY2FuYWwgTFwqIGRlIENJRUxBQjogc29sbyBsYSBsdW1pbm9zaWRhZCwgc2luIGluZm9ybWFjacOzbiBkZSBjb2xvci4gTGFzIHpvbmFzIGlsdW1pbmFkYXMgZGUgbGEgZmFjaGFkYSB5IGVsIGNpZWxvIHRpZW5lbiBMXCogYWx0byB5IGxvcyDDoXJib2xlcyB5IGxhIHbDrWEsIExcKiBiYWpvOyBsb3MgY2FuYWxlcyBhXCogeSBiXCogKG5vIG1vc3RyYWRvcykgY29udGllbmVuIGVsIGNvbG9yLg0KDQojIyBTdXBlcnDDrXhlbGVzIHkgZ3JhZm8gZGUgcmVnaW9uZXMNCg0KYGBge3IgZnVuY2lvbmVzLWdyYWZvfQ0KIyBTTElDOiBkZXZ1ZWx2ZSB1bmEgbWF0cml6IGRlIGV0aXF1ZXRhcyAxLCAuLi4sIEsnDQpydW5fc2xpYyA8LSBmdW5jdGlvbihpbWcsIG5fc3VwZXJwaXhlbHMgPSAxODAsIGNvbXBhY3RuZXNzID0gMjApIHsNCiAgcyA8LSBPcGVuSW1hZ2VSOjpzdXBlcnBpeGVscyhpbWcsIG1ldGhvZCA9ICJzbGljIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdXBlcnBpeGVsID0gbl9zdXBlcnBpeGVscywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYWN0bmVzcyA9IGNvbXBhY3RuZXNzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybl9zbGljX2RhdGEgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm5fbGFiZWxzID0gVFJVRSwgdmVyYm9zZSA9IEZBTFNFKQ0KICBsYWIgPC0gcyRsYWJlbHMNCiAgbWF0cml4KGFzLmludGVnZXIoZmFjdG9yKGxhYikpLCBucm93KGxhYiksIG5jb2wobGFiKSkNCn0NCg0KIyBQYXJlcyBkZSByZWdpb25lcyB2ZWNpbmFzICg0IHZlY2lub3MpIHkgbG9uZ2l0dWQgZGUgZnJvbnRlcmEgYl9pag0KcmVnaW9uX2VkZ2VzIDwtIGZ1bmN0aW9uKGxhYikgew0KICBoeiA8LSBjYmluZChhcy52ZWN0b3IobGFiWywgLW5jb2wobGFiKV0pLCBhcy52ZWN0b3IobGFiWywgLTFdKSkNCiAgdnQgPC0gY2JpbmQoYXMudmVjdG9yKGxhYlstbnJvdyhsYWIpLCBdKSwgYXMudmVjdG9yKGxhYlstMSwgXSkpDQogIHAgIDwtIHJiaW5kKGh6LCB2dCkNCiAgcCAgPC0gcFtwWywgMV0gIT0gcFssIDJdLCAsIGRyb3AgPSBGQUxTRV0NCiAgZGF0YS5mcmFtZShmcm9tID0gcG1pbihwWywgMV0sIHBbLCAyXSksIHRvID0gcG1heChwWywgMV0sIHBbLCAyXSkpIHw+DQogICAgY291bnQoZnJvbSwgdG8sIG5hbWUgPSAiYm9yZGVyIikNCn0NCg0KIyBDb25zdHJ1eWUgbGEgcmVkIGRlIHJlZ2lvbmVzLiBTaSAnbGFiZWxzJyBzZSBkYSwgc2UgdXNhIGVzYSBwYXJ0aWNpw7NuIGZpamENCmltYWdlX3RvX2dyYXBoIDwtIGZ1bmN0aW9uKGltZywgbGFiZWxzID0gTlVMTCwgbl9zdXBlcnBpeGVscyA9IDE4MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhY3RuZXNzID0gMjAsIGggPSAyMCkgew0KICBpZiAoaXMubnVsbChsYWJlbHMpKSBsYWJlbHMgPC0gcnVuX3NsaWMoaW1nLCBuX3N1cGVycGl4ZWxzLCBjb21wYWN0bmVzcykNCiAgbnIgPC0gbnJvdyhsYWJlbHMpOyBuYyA8LSBuY29sKGxhYmVscykNCiAgcHggIDwtIG1hdHJpeChpbWcsIG5jb2wgPSAzKSAgICAgICAgICAgICAgICAgIyBww614ZWxlcyB4IGNhbmFsZXMNCiAgbGFiX3B4IDwtIHJnYl90b19sYWIocHgpDQogIGlkICA8LSBhcy52ZWN0b3IobGFiZWxzKQ0KICByciAgPC0gYXMudmVjdG9yKHJvdyhsYWJlbHMpKTsgY2MgPC0gYXMudmVjdG9yKGNvbChsYWJlbHMpKQ0KICBLICAgPC0gbWF4KGlkKQ0KICBhdHRyX3YgPC0gZGF0YS5mcmFtZSgNCiAgICBuYW1lID0gYXMuY2hhcmFjdGVyKDE6SyksDQogICAgYXJlYSA9IHRhYnVsYXRlKGlkLCBLKSwNCiAgICByb3cgID0gdGFwcGx5KHJyLCBpZCwgbWVhbiksIGNvbCA9IHRhcHBseShjYywgaWQsIG1lYW4pLA0KICAgIFIgPSB0YXBwbHkocHhbLCAxXSwgaWQsIG1lYW4pLCBHID0gdGFwcGx5KHB4WywgMl0sIGlkLCBtZWFuKSwNCiAgICBCID0gdGFwcGx5KHB4WywgM10sIGlkLCBtZWFuKSwNCiAgICBMID0gdGFwcGx5KGxhYl9weFssIDFdLCBpZCwgbWVhbiksIGEgPSB0YXBwbHkobGFiX3B4WywgMl0sIGlkLCBtZWFuKSwNCiAgICBiID0gdGFwcGx5KGxhYl9weFssIDNdLCBpZCwgbWVhbikpDQogIGUgPC0gcmVnaW9uX2VkZ2VzKGxhYmVscykNCiAgZSRkIDwtIHNxcnQoKGF0dHJfdiRMW2UkZnJvbV0gLSBhdHRyX3YkTFtlJHRvXSleMiArDQogICAgICAgICAgICAgIChhdHRyX3YkYVtlJGZyb21dIC0gYXR0cl92JGFbZSR0b10pXjIgKw0KICAgICAgICAgICAgICAoYXR0cl92JGJbZSRmcm9tXSAtIGF0dHJfdiRiW2UkdG9dKV4yKQ0KICBlJHcgICAgPC0gZXhwKC1lJGQgLyBoKQ0KICBlJGNvc3QgPC0gMSAvIGUkdw0KICBlJGZyb20gPC0gYXMuY2hhcmFjdGVyKGUkZnJvbSk7IGUkdG8gPC0gYXMuY2hhcmFjdGVyKGUkdG8pDQogIGcgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGUsIGRpcmVjdGVkID0gRkFMU0UsIHZlcnRpY2VzID0gYXR0cl92KQ0KICBnJGggPC0gaA0KICBsaXN0KGdyYXBoID0gZywgbGFiZWxzID0gbGFiZWxzLCBpbWcgPSBpbWcpDQp9DQoNCiMgUmVjYWxjdWxhIGxvcyBwZXNvcyBjb24gb3RyYSBlc2NhbGEgaCAobm8gY2FtYmlhIGxhIHRvcG9sb2fDrWEpDQpzZXRfaCA8LSBmdW5jdGlvbihnLCBoKSB7DQogIEUoZykkdyA8LSBleHAoLUUoZykkZCAvIGgpOyBFKGcpJGNvc3QgPC0gMSAvIEUoZykkdzsgZyRoIDwtIGg7IGcNCn0NCg0KIyBDb25zZXJ2YSBzb2xvIGxhcyBhcmlzdGFzIGNvbiB3X2lqID49IHRhdQ0KZmlsdGVyX2dyYXBoIDwtIGZ1bmN0aW9uKGcsIHRhdSkgZGVsZXRlX2VkZ2VzKGcsIEUoZylbdyA8IHRhdV0pDQpgYGANCg0KYGBge3IgZWotZ3JhZm8sIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQ0KcmVzX2VqIDwtIGltYWdlX3RvX2dyYXBoKGltZ19laiwgbl9zdXBlcnBpeGVscyA9IDE4MCwgY29tcGFjdG5lc3MgPSAyMCwgaCA9IDIwKQ0KZ19laiAgIDwtIHJlc19laiRncmFwaA0KDQpvcCA8LSBwYXIobWZyb3cgPSBjKDIsIDIpLCBtYXIgPSBjKDAuMywgMC4zLCAxLjYsIDAuMykpDQpwbG90X3BhbmVsKGltZ19laiwgcmVzX2VqJGxhYmVscywgZmlsbCA9IEZBTFNFLCBib3JkZXJfY29sID0gYygxLCAxLCAwKSwNCiAgICAgICAgICAgbWFpbiA9IHNwcmludGYoIlNMSUM6ICVkIHN1cGVycMOteGVsZXMiLCB2Y291bnQoZ19laikpKQ0KcGxvdF9wYW5lbChpbWdfZWosIHJlc19laiRsYWJlbHMsIG1haW4gPSAiQ29sb3IgbWVkaW8gZGUgY2FkYSBzdXBlcnDDrXhlbCIpDQpwbG90X2dyYXBoX292ZXJsYXkocmVzX2VqLCBtYWluID0gc3ByaW50ZigiR3JhZm8gZGUgcmVnaW9uZXMgKCVkIGFyaXN0YXMpIiwgZWNvdW50KGdfZWopKSkNCnBsb3RfZ3JhcGhfb3ZlcmxheShsaXN0KGdyYXBoID0gZmlsdGVyX2dyYXBoKGdfZWosIDAuNSksIGltZyA9IGltZ19laiksDQogICAgICAgICAgICAgICAgICAgbWFpbiA9IHNwcmludGYoIkdyYWZvIGZpbHRyYWRvLCB0YXUgPSAwLDUgKCVkIGFyaXN0YXMpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY291bnQoZmlsdGVyX2dyYXBoKGdfZWosIDAuNSkpKSkNCnBhcihvcCkNCmBgYA0KDQpgYGB7ciBlai1wZXNvcywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTMuNX0NCm9wIDwtIHBhcihtZnJvdyA9IGMoMSwgMiksIG1hciA9IGMoNC4yLCA0LjIsIDIsIDEpKQ0KaGlzdChFKGdfZWopJGQsIGJyZWFrcyA9IDMwLCBjb2wgPSAiIzI0NzFBMyIsIGJvcmRlciA9ICJ3aGl0ZSIsDQogICAgIG1haW4gPSAiRGlzdGFuY2lhIGRlIGNvbG9yIGVudHJlIHZlY2lub3MiLCB4bGFiID0gZXhwcmVzc2lvbihkW2lqXSB+ICIoQ0lFTEFCKSIpLCB5bGFiID0gIkFyaXN0YXMiKQ0KaGlzdChFKGdfZWopJHcsIGJyZWFrcyA9IDMwLCBjb2wgPSAiI0MwMzkyQiIsIGJvcmRlciA9ICJ3aGl0ZSIsDQogICAgIG1haW4gPSAiUGVzb3MgZGUgbGFzIGFyaXN0YXMgKGggPSAyMCkiLCB4bGFiID0gZXhwcmVzc2lvbih3W2lqXSA9PSBleHAoLWRbaWpdIC8gaCkpLCB5bGFiID0gIkFyaXN0YXMiKQ0KYWJsaW5lKHYgPSAwLjUsIGx0eSA9IDIpDQpwYXIob3ApDQpgYGANCg0KQ2FkYSBzdXBlcnDDrXhlbCBlcyB1biB2w6lydGljZSB5IGRvcyBzdXBlcnDDrXhlbGVzIHZlY2lub3MgKHF1ZSBjb21wYXJ0ZW4gZnJvbnRlcmEpIGVzdMOhbiB1bmlkb3MgcG9yIHVuYSBhcmlzdGEuIEVuIGVsIGdyYWZvIHNvYnJlIGxhIGltYWdlbiwgbGFzIGzDrW5lYXMgbcOhcyBncnVlc2FzIHkgb3BhY2FzIHNvbiBsYXMgZGUgbWF5b3IgcGVzbyAkd197aWp9JCwgZXMgZGVjaXIsIGxhcyBkZSByZWdpb25lcyBjb24gY29sb3IgcGFyZWNpZG8uIEVsIGZpbHRyYWRvIHBvciAkXHRhdSQgZWxpbWluYSBsYXMgYXJpc3RhcyBlbnRyZSByZWdpb25lcyBkZSBjb2xvciBtdXkgZGlzdGludG8sIHF1ZSBzb24gbGFzIHF1ZSBjcnV6YW4gbG9zIGJvcmRlcyBkZSBsb3Mgb2JqZXRvcy4NCg0KIyMgQ29tdW5pZGFkZXMgeSBzZWdtZW50YWNpw7NuDQoNCmBgYHtyIGZ1bmNpb25lcy1jb211bmlkYWRlc30NCmZpbmRfY29tbXVuaXRpZXMgPC0gZnVuY3Rpb24oZywgbWV0aG9kID0gYygibG91dmFpbiIsICJsZWlkZW4iKSwgc2VlZCA9IDEyMykgew0KICBtZXRob2QgPC0gbWF0Y2guYXJnKG1ldGhvZCkNCiAgaWYgKGVjb3VudChnKSA9PSAwKSByZXR1cm4obGlzdChtZW1iZXJzaGlwID0gc2VxX2xlbih2Y291bnQoZykpLCBRID0gMCkpDQogIHNldC5zZWVkKHNlZWQpDQogIGNsIDwtIHN3aXRjaChtZXRob2QsDQogICAgbG91dmFpbiA9IGNsdXN0ZXJfbG91dmFpbihnLCB3ZWlnaHRzID0gRShnKSR3KSwNCiAgICBsZWlkZW4gID0gY2x1c3Rlcl9sZWlkZW4oZywgb2JqZWN0aXZlX2Z1bmN0aW9uID0gIm1vZHVsYXJpdHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRzID0gRShnKSR3LCBuX2l0ZXJhdGlvbnMgPSAtMSkpDQogIG0gPC0gYXMuaW50ZWdlcihtZW1iZXJzaGlwKGNsKSkNCiAgbGlzdChtZW1iZXJzaGlwID0gbSwgUSA9IG1vZHVsYXJpdHkoZywgbSwgd2VpZ2h0cyA9IEUoZykkdykpDQp9DQoNCiMgUHJveWVjdGEgbGEgY29tdW5pZGFkIGRlIGNhZGEgc3VwZXJww614ZWwgc29icmUgc3VzIHDDrXhlbGVzDQpwaXhlbF9wYXJ0aXRpb24gPC0gZnVuY3Rpb24obGFiZWxzLCBtZW1iZXJzaGlwKSB7DQogIG1hdHJpeChtZW1iZXJzaGlwW2xhYmVsc10sIG5yb3cobGFiZWxzKSwgbmNvbChsYWJlbHMpKQ0KfQ0KDQojIMONbmRpY2UgZGUgUmFuZCBhanVzdGFkbyBlbnRyZSBkb3Mgc2VnbWVudGFjaW9uZXMgYSBuaXZlbCBkZSBww614ZWwNCmFyaSA8LSBmdW5jdGlvbihwMSwgcDIpIGlncmFwaDo6Y29tcGFyZShhcy52ZWN0b3IocDEpLCBhcy52ZWN0b3IocDIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJhZGp1c3RlZC5yYW5kIikNCmBgYA0KDQpgYGB7ciBlai1jb211bmlkYWRlcywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTMuNn0NCmx2X2VqIDwtIGZpbmRfY29tbXVuaXRpZXMoZ19laiwgImxvdXZhaW4iKQ0KbGRfZWogPC0gZmluZF9jb21tdW5pdGllcyhnX2VqLCAibGVpZGVuIikNCnNlZ19sdiA8LSBwaXhlbF9wYXJ0aXRpb24ocmVzX2VqJGxhYmVscywgbHZfZWokbWVtYmVyc2hpcCkNCnNlZ19sZCA8LSBwaXhlbF9wYXJ0aXRpb24ocmVzX2VqJGxhYmVscywgbGRfZWokbWVtYmVyc2hpcCkNCg0Kb3AgPC0gcGFyKG1mcm93ID0gYygxLCAzKSwgbWFyID0gYygwLjMsIDAuMywgMS42LCAwLjMpKQ0KcGxvdF9wYW5lbChpbWdfZWosIG1haW4gPSAiSW1hZ2VuIG9yaWdpbmFsIikNCnBsb3RfcGFuZWwoaW1nX2VqLCBzZWdfbHYsIG1haW4gPSBzcHJpbnRmKCJMb3V2YWluOiAlZCBjb211bmlkYWRlcywgUSA9ICUuM2YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGx2X2VqJG1lbWJlcnNoaXApLCBsdl9laiRRKSkNCnBsb3RfcGFuZWwoaW1nX2VqLCBzZWdfbGQsIG1haW4gPSBzcHJpbnRmKCJMZWlkZW46ICVkIGNvbXVuaWRhZGVzLCBRID0gJS4zZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgobGRfZWokbWVtYmVyc2hpcCksIGxkX2VqJFEpKQ0KcGFyKG9wKQ0KYGBgDQoNCkNhZGEgY29tdW5pZGFkIGRlbCBncmFmbyBkZSByZWdpb25lcyBzZSBwcm95ZWN0YSBzb2JyZSBsb3MgcMOteGVsZXMgZGUgc3VzIHN1cGVycMOteGVsZXMgeSBzZSBwaW50YSBjb24gc3UgY29sb3IgbWVkaW8uIEVsIMOtbmRpY2UgZGUgUmFuZCBhanVzdGFkbyBlbnRyZSBsYXMgZG9zIHNlZ21lbnRhY2lvbmVzIGVzIGByIHJvdW5kKGFyaShzZWdfbHYsIHNlZ19sZCksIDMpYCAoMSA9IGlkw6ludGljYXMsIGNlcmNhIGRlIDAgPSBubyBtw6FzIHBhcmVjaWRhcyBxdWUgYWwgYXphcikuDQoNCiMjIEZ1bmNpb25lcyBkZSBleHBlcmltZW50b3MNCg0KYGBge3IgZnVuY2lvbmVzLWV4cGVyaW1lbnRvc30NCiMgUmVzdW1lbiBlc3RydWN0dXJhbCBkZSBsYSByZWQgKHBvbmRlcmFkYSBwb3IgdykNCm5ldF9zdW1tYXJ5IDwtIGZ1bmN0aW9uKGcpIHsNCiAgY29tcCA8LSBjb21wb25lbnRzKGcpDQogIGRhdGEuZnJhbWUodmVydGljZXMgPSB2Y291bnQoZyksIGFyaXN0YXMgPSBlY291bnQoZyksDQogICAgICAgICAgICAgZGVuc2lkYWQgPSByb3VuZChlZGdlX2RlbnNpdHkoZyksIDQpLA0KICAgICAgICAgICAgIGdyYWRvX21lZGlvID0gcm91bmQobWVhbihkZWdyZWUoZykpLCAyKSwNCiAgICAgICAgICAgICB3X21lZGlhID0gcm91bmQobWVhbihFKGcpJHcpLCAzKSwNCiAgICAgICAgICAgICB0cmFuc2l0aXZpZGFkID0gcm91bmQodHJhbnNpdGl2aXR5KGcsIHR5cGUgPSAiZ2xvYmFsIiksIDMpLA0KICAgICAgICAgICAgIGNvbXBvbmVudGVzID0gY29tcCRubywNCiAgICAgICAgICAgICBhaXNsYWRvcyA9IHN1bShkZWdyZWUoZykgPT0gMCkpDQp9DQoNCiMgU2VnbWVudGFjaW9uZXMgTG91dmFpbiB5IExlaWRlbiBwYXJhIHZhcmlvcyBoIChncmFmbyBjb21wbGV0bykNCmV4cGVyaW1lbnRfaCA8LSBmdW5jdGlvbihyZXMsIGhzKSB7DQogIGxhcHBseShocywgZnVuY3Rpb24oaCkgew0KICAgIGcgPC0gc2V0X2gocmVzJGdyYXBoLCBoKQ0KICAgIGx2IDwtIGZpbmRfY29tbXVuaXRpZXMoZywgImxvdXZhaW4iKTsgbGQgPC0gZmluZF9jb21tdW5pdGllcyhnLCAibGVpZGVuIikNCiAgICBsaXN0KGggPSBoLCBnID0gZywgbHYgPSBsdiwgbGQgPSBsZCwNCiAgICAgICAgIHRhYiA9IGRhdGEuZnJhbWUoaCA9IGgsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHdfbWVkaWEgPSByb3VuZChtZWFuKEUoZykkdyksIDMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB3X21lZGlhbmEgPSByb3VuZChtZWRpYW4oRShnKSR3KSwgMyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbV9sb3V2YWluID0gbWF4KGx2JG1lbWJlcnNoaXApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBRX2xvdXZhaW4gPSByb3VuZChsdiRRLCAzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29tX2xlaWRlbiA9IG1heChsZCRtZW1iZXJzaGlwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgUV9sZWlkZW4gPSByb3VuZChsZCRRLCAzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgQVJJX2xvdXZhaW5fbGVpZGVuID0gcm91bmQoYXJpKGx2JG1lbWJlcnNoaXAsIGxkJG1lbWJlcnNoaXApLCAzKSkpDQogIH0pDQp9DQoNCiMgU2VnbWVudGFjaW9uZXMgTG91dmFpbiB5IExlaWRlbiBzb2JyZSBlbCBncmFmbyBmaWx0cmFkbyBHX3RhdQ0KZXhwZXJpbWVudF90YXUgPC0gZnVuY3Rpb24ocmVzLCB0YXVzLCBoID0gMjAsIG1pbl9hcmVhID0gMCkgew0KICBnMCA8LSBzZXRfaChyZXMkZ3JhcGgsIGgpDQogIGxhcHBseSh0YXVzLCBmdW5jdGlvbih0YXUpIHsNCiAgICBnIDwtIGZpbHRlcl9ncmFwaChnMCwgdGF1KQ0KICAgIGx2IDwtIGZpbmRfY29tbXVuaXRpZXMoZywgImxvdXZhaW4iKTsgbGQgPC0gZmluZF9jb21tdW5pdGllcyhnLCAibGVpZGVuIikNCiAgICBjb21wIDwtIGNvbXBvbmVudHMoZykNCiAgICBsaXN0KHRhdSA9IHRhdSwgZyA9IGcsIGx2ID0gbHYsIGxkID0gbGQsDQogICAgICAgICB0YWIgPSBkYXRhLmZyYW1lKHRhdSA9IHRhdSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZF9tYXggPSBpZmVsc2UodGF1ID4gMCwgcm91bmQoLWggKiBsb2codGF1KSwgMiksIEluZiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFyaXN0YXMgPSBlY291bnQoZyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudGVzID0gY29tcCRubywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWlzbGFkb3MgPSBzdW0oZGVncmVlKGcpID09IDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb21fbG91dmFpbiA9IG1heChsdiRtZW1iZXJzaGlwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgUV9sb3V2YWluID0gcm91bmQobHYkUSwgMyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbV9sZWlkZW4gPSBtYXgobGQkbWVtYmVyc2hpcCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIFFfbGVpZGVuID0gcm91bmQobGQkUSwgMyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgQVJJX2xvdXZhaW5fbGVpZGVuID0gcm91bmQoYXJpKGx2JG1lbWJlcnNoaXAsIGxkJG1lbWJlcnNoaXApLCAzKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KICAgICAgICAgDQogICAgICAgICApDQogICAgDQogIH0pDQp9DQoNCiMgUm9idXN0ZXogZnJlbnRlIGEgcnVpZG8gZ2F1c3NpYW5vLg0KIyAtIFNMSUMgZmlqbzogbG9zIHN1cGVycMOteGVsZXMgc29uIGxvcyBkZSBsYSBpbWFnZW4gbGltcGlhOyBlbCBydWlkbyBzb2xvIGNhbWJpYQ0KIyAgIGxvcyBjb2xvcmVzIG1lZGlvcyAoeSBwb3IgdGFudG8gZF9paiB5IHdfaWopLg0KIyAtIFNMSUMgdmFyaWFibGU6IFNMSUMgc2UgcmVjYWxjdWxhIHNvYnJlIGxhIGltYWdlbiBydWlkb3NhLg0KIyBMYSByZWZlcmVuY2lhIGVzIGxhIHNlZ21lbnRhY2nDs24gZGUgbGEgaW1hZ2VuIGxpbXBpYSBjb24gZWwgbWlzbW8gbcOpdG9kby4NCmV4cGVyaW1lbnRfbm9pc2UgPC0gZnVuY3Rpb24ocmVzLCBzaWdtYXMsIHJlcHMgPSA1LCBoID0gMjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fc3VwZXJwaXhlbHMgPSAxODAsIGNvbXBhY3RuZXNzID0gMjApIHsNCiAgcmVmIDwtIGxpc3QobG91dmFpbiA9IHBpeGVsX3BhcnRpdGlvbihyZXMkbGFiZWxzLCBmaW5kX2NvbW11bml0aWVzKHJlcyRncmFwaCwgImxvdXZhaW4iKSRtZW1iZXJzaGlwKSwNCiAgICAgICAgICAgICAgbGVpZGVuICA9IHBpeGVsX3BhcnRpdGlvbihyZXMkbGFiZWxzLCBmaW5kX2NvbW11bml0aWVzKHJlcyRncmFwaCwgImxlaWRlbiIpJG1lbWJlcnNoaXApKQ0KICBvdXQgPC0gbGlzdCgpDQogIGZvciAocyBpbiBzaWdtYXMpIGZvciAociBpbiBzZXFfbGVuKHJlcHMpKSB7DQogICAgbm9pc3kgPC0gYWRkX25vaXNlKHJlcyRpbWcsIHMsIHNlZWQgPSAxMDAgKiByICsgcykNCiAgICBmaXhlZCA8LSBpbWFnZV90b19ncmFwaChub2lzeSwgbGFiZWxzID0gcmVzJGxhYmVscywgaCA9IGgpDQogICAgdmFyaWEgPC0gaW1hZ2VfdG9fZ3JhcGgobm9pc3ksIG5fc3VwZXJwaXhlbHMgPSBuX3N1cGVycGl4ZWxzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBhY3RuZXNzID0gY29tcGFjdG5lc3MsIGggPSBoKQ0KICAgIGZvciAoc2wgaW4gYygiZmlqbyIsICJ2YXJpYWJsZSIpKSB7DQogICAgICByciA8LSBpZiAoc2wgPT0gImZpam8iKSBmaXhlZCBlbHNlIHZhcmlhDQogICAgICBmb3IgKG0gaW4gYygibG91dmFpbiIsICJsZWlkZW4iKSkgew0KICAgICAgICBjbSA8LSBmaW5kX2NvbW11bml0aWVzKHJyJGdyYXBoLCBtKQ0KICAgICAgICBwcCA8LSBwaXhlbF9wYXJ0aXRpb24ocnIkbGFiZWxzLCBjbSRtZW1iZXJzaGlwKQ0KICAgICAgICBvdXRbW2xlbmd0aChvdXQpICsgMV1dIDwtIGRhdGEuZnJhbWUoDQogICAgICAgICAgc2lnbWEgPSBzLCByZXAgPSByLCBzbGljID0gc2wsIG1ldG9kbyA9IG0sDQogICAgICAgICAgY29tdW5pZGFkZXMgPSBtYXgoY20kbWVtYmVyc2hpcCksIFEgPSBjbSRRLA0KICAgICAgICAgIEFSSSA9IGFyaShwcCwgcmVmW1ttXV0pKQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICBiaW5kX3Jvd3Mob3V0KQ0KfQ0KDQojIFVuYSByZWFsaXphY2nDs24gcG9yIHNpZ21hIHBhcmEgbW9zdHJhciBsYXMgc2VnbWVudGFjaW9uZXMgKFNMSUMgdmFyaWFibGUpDQpub2lzZV9leGFtcGxlcyA8LSBmdW5jdGlvbihyZXMsIHNpZ21hcywgaCA9IDIwKSB7DQogIGxhcHBseShzaWdtYXMsIGZ1bmN0aW9uKHMpIHsNCiAgICBub2lzeSA8LSBhZGRfbm9pc2UocmVzJGltZywgcywgc2VlZCA9IDEwMCArIHMpDQogICAgcnIgPC0gaW1hZ2VfdG9fZ3JhcGgobm9pc3ksIGggPSBoKQ0KICAgIGxpc3Qoc2lnbWEgPSBzLCBpbWcgPSBub2lzeSwgcmVzID0gcnIsDQogICAgICAgICBsdiA9IHBpeGVsX3BhcnRpdGlvbihyciRsYWJlbHMsIGZpbmRfY29tbXVuaXRpZXMocnIkZ3JhcGgsICJsb3V2YWluIikkbWVtYmVyc2hpcCksDQogICAgICAgICBsZCA9IHBpeGVsX3BhcnRpdGlvbihyciRsYWJlbHMsIGZpbmRfY29tbXVuaXRpZXMocnIkZ3JhcGgsICJsZWlkZW4iKSRtZW1iZXJzaGlwKSkNCiAgfSkNCn0NCg0KIyBHcsOhZmljb3MgZGUgbG9zIGV4cGVyaW1lbnRvcw0KcGxvdF9leHBfZ3JpZCA8LSBmdW5jdGlvbihyZXMsIGV4LCBrZXksIGxhYikgew0KICBvcCA8LSBwYXIobWZyb3cgPSBjKDIsIGxlbmd0aChleCkpLCBtYXIgPSBjKDAuMywgMC4zLCAxLjYsIDAuMykpDQogIGZvciAoZSBpbiBleCkgcGxvdF9wYW5lbChyZXMkaW1nLCBwaXhlbF9wYXJ0aXRpb24ocmVzJGxhYmVscywgZSRsdiRtZW1iZXJzaGlwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwcmludGYoIkxvdXZhaW4sICVzID0gJXMgKCVkIGNvbS4pIiwgbGFiLCBlW1trZXldXSwgbWF4KGUkbHYkbWVtYmVyc2hpcCkpKQ0KICBmb3IgKGUgaW4gZXgpIHBsb3RfcGFuZWwocmVzJGltZywgcGl4ZWxfcGFydGl0aW9uKHJlcyRsYWJlbHMsIGUkbGQkbWVtYmVyc2hpcCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKCJMZWlkZW4sICVzID0gJXMgKCVkIGNvbS4pIiwgbGFiLCBlW1trZXldXSwgbWF4KGUkbGQkbWVtYmVyc2hpcCkpKQ0KICBwYXIob3ApDQp9DQoNCnBsb3Rfbm9pc2VfZ3JpZCA8LSBmdW5jdGlvbihuZSkgew0KICBvcCA8LSBwYXIobWZyb3cgPSBjKDMsIGxlbmd0aChuZSkpLCBtYXIgPSBjKDAuMywgMC4zLCAxLjYsIDAuMykpDQogIGZvciAoZSBpbiBuZSkgcGxvdF9wYW5lbChlJGltZywgbWFpbiA9IHNwcmludGYoIkltYWdlbiwgc2lnbWEgPSAlcyIsIGUkc2lnbWEpKQ0KICBmb3IgKGUgaW4gbmUpIHBsb3RfcGFuZWwoZSRpbWcsIGUkbHYsIHNwcmludGYoIkxvdXZhaW4gKCVkIGNvbS4pIiwgbWF4KGUkbHYpKSkNCiAgZm9yIChlIGluIG5lKSBwbG90X3BhbmVsKGUkaW1nLCBlJGxkLCBzcHJpbnRmKCJMZWlkZW4gKCVkIGNvbS4pIiwgbWF4KGUkbGQpKSkNCiAgcGFyKG9wKQ0KfQ0KDQpwbG90X25vaXNlX2FyaSA8LSBmdW5jdGlvbihueiwgdGl0dWxvKSB7DQogIG56IHw+DQogICAgZ3JvdXBfYnkoc2lnbWEsIHNsaWMsIG1ldG9kbykgfD4NCiAgICBzdW1tYXJpc2UobWVkaWEgPSBtZWFuKEFSSSksIHNkID0gc2QoQVJJKSwgLmdyb3VwcyA9ICJkcm9wIikgfD4NCiAgICBnZ3Bsb3QoYWVzKHNpZ21hLCBtZWRpYSwgY29sb3VyID0gbWV0b2RvLCBsaW5ldHlwZSA9IHNsaWMpKSArDQogICAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDAuOCkgKyBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lZGlhIC0gc2QsIHltYXggPSBtZWRpYSArIHNkKSwgd2lkdGggPSAxLjUpICsNCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMobGVpZGVuID0gIiNDMDM5MkIiLCBsb3V2YWluID0gIiMyNDcxQTMiKSkgKw0KICAgIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAxKSkgKw0KICAgIGxhYnMoeCA9IGV4cHJlc3Npb24oc2lnbWEgfiAiZGVsIHJ1aWRvIChlc2NhbGEgMC0yNTUpIiksDQogICAgICAgICB5ID0gIkFSSSByZXNwZWN0byBhIGxhIGltYWdlbiBsaW1waWEiLA0KICAgICAgICAgY29sb3VyID0gIk3DqXRvZG8iLCBsaW5ldHlwZSA9ICJTTElDIiwgdGl0bGUgPSB0aXR1bG8pICsNCiAgICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEyKQ0KfQ0KDQpzdW1tYXJ5X25vaXNlIDwtIGZ1bmN0aW9uKG56KSB7DQogIG56IHw+DQogICAgZ3JvdXBfYnkoc2lnbWEsIHNsaWMsIG1ldG9kbykgfD4NCiAgICBzdW1tYXJpc2UoY29tdW5pZGFkZXMgPSByb3VuZChtZWFuKGNvbXVuaWRhZGVzKSwgMSksDQogICAgICAgICAgICAgIFEgPSByb3VuZChtZWFuKFEpLCAzKSwNCiAgICAgICAgICAgICAgQVJJX21lZGlhID0gcm91bmQobWVhbihBUkkpLCAzKSwNCiAgICAgICAgICAgICAgQVJJX3NkID0gcm91bmQoc2QoQVJJKSwgMyksIC5ncm91cHMgPSAiZHJvcCIpIHw+DQogICAgYXJyYW5nZShzbGljLCBtZXRvZG8sIHNpZ21hKQ0KfQ0KYGBgDQoNCiMgSW3DoWdlbmVzIGRlIHBydWViYQ0KDQoNCiogKipQYWlzYWplIGFycXVpdGVjdMOzbmljbyoqOiBlZGlmaWNpbyBlbiBjb25jcmV0byBjb24gZmFjaGFkYSBpbHVtaW5hZGEsIHpvbmFzIHZlcmRlcywgdsOtYSBjb24gY2hhcmNvcyB5IGNpZWxvIG51YmxhZG8uICpGb3RvZ3JhZsOtYSDCqSBSb2RyaWdvIETDoXZpbGEqOyBzZSB1c2EgYXF1w60gw7puaWNhbWVudGUgY29uIGZpbmVzIGFjYWTDqW1pY29zLg0KKiAqKkFnbnVzIERlaSoqLCBGcmFuY2lzY28gZGUgWnVyYmFyw6FuLCBjLiAxNjM14oCTMTY0MCwgTXVzZW8gZGVsIFByYWRvLiBPYnJhIGRlIGRvbWluaW8gcMO6YmxpY28uIEVzIHVuYSBpbWFnZW4gZGUgYmFqbyBjb250cmFzdGUgY3JvbcOhdGljbzogdW4gY29yZGVybyBjbGFybyBzb2JyZSB1biBmb25kbyBvc2N1cm8gY2FzaSB1bmlmb3JtZSwgY29uIGlsdW1pbmFjacOzbiB0ZW5lYnJpc3RhLg0KDQpQYXJhIGVsIGFuw6FsaXNpcyBsYXMgaW3DoWdlbmVzIHNlIHJlZHVjZW4gYSA0MDAgcMOteGVsZXMgZW4gc3UgbGFkbyBtYXlvciwgcGFyYSBxdWUgU0xJQyB5IGxvcyBhbGdvcml0bW9zIGRlIHJlZCBzZWFuIHLDoXBpZG9zOyBsYXMgaW3DoWdlbmVzIG9yaWdpbmFsZXMgc2UgbXVlc3RyYW4gZW4gYWx0YSByZXNvbHVjacOzbi4NCg0KYGBge3IgY2FyZ2F9DQpwYWlzYWplIDwtIHByZXBhcmVfcmdiKCJwYWlzYWplX3JkYXZpbGEuanBnIiwgbWF4X3NpZGUgPSA0MDApDQpjb3JkZXJvIDwtIHByZXBhcmVfcmdiKCJhZ251c19kZWlfenVyYmFyYW4uanBnIiwgbWF4X3NpZGUgPSA0MDApDQojIFZlcnNpb25lcyBlbiBhbHRhIHJlc29sdWNpw7NuLCBzb2xvIHBhcmEgbW9zdHJhciBsYXMgaW3DoWdlbmVzIG9yaWdpbmFsZXMNCnBhaXNhamVfaGQgPC0gcHJlcGFyZV9yZ2IoInBhaXNhamVfcmRhdmlsYS5qcGciLCBtYXhfc2lkZSA9IDEwMDApDQpjb3JkZXJvX2hkIDwtIHByZXBhcmVfcmdiKCJhZ251c19kZWlfenVyYmFyYW4uanBnIiwgbWF4X3NpZGUgPSAxMDAwKQ0KcmJpbmQocGFpc2FqZSA9IGRpbShwYWlzYWplKSwgYWdudXNfZGVpID0gZGltKGNvcmRlcm8pKQ0KYGBgDQoNClBhcsOhbWV0cm9zIGNvbXVuZXMgOiAkSz0xODAkIHN1cGVycMOteGVsZXMgc29saWNpdGFkb3MsICpjb21wYWN0bmVzcyogJG09MjAkIHkgJGg9MjAkIGNvbW8gdmFsb3IgZGUgcmVmZXJlbmNpYS4NCg0KYGBge3IgcGFyYW1ldHJvc30NCksgPC0gMTgwOyBtIDwtIDIwOyBoMCA8LSAyMA0KaHMgICAgIDwtIGMoNSwgMTAsIDIwLCA0MCwgODApICAgICAgICMgZXNjYWxhcyBkZSBjb2xvciBwYXJhIHdfaWoNCnRhdXMgICA8LSBjKDAsIDAuMywgMC41LCAwLjcpICAgICAgICAjIHVtYnJhbGVzIGRlIGZpbHRyYWRvDQpzaWdtYXMgPC0gYygwLCAxMCwgMjUsIDUwKSAgICAgICAgICAgIyBkZXN2aWFjacOzbiBlc3TDoW5kYXIgZGVsIHJ1aWRvDQpyZXBzICAgPC0gNSAgICAgICAgICAgICAgICAgICAgICAgICAgIyByw6lwbGljYXMgZGUgcnVpZG8gcG9yIHNpZ21hDQpgYGANCg0KIyBQYWlzYWplDQoNCkxhIGZvdG9ncmFmw61hIGRlIFJvZHJpZ28gRMOhdmlsYSBtdWVzdHJhIGVsIG51ZXZvIGVkaWZpY2lvIGRlIGxhIEZhY3VsdGFkIGRlIEVuZmVybWVyw61hIGRlIGxhIFVuaXZlcnNpZGFkIE5hY2lvbmFsIGRlIENvbG9tYmlhLCBzZWRlIEJvZ290w6EsIGFsIGF0YXJkZWNlci4NCg0KRW4gcHJpbWVyIHBsYW5vIGFwYXJlY2VuIHpvbmFzIHZlcmRlcyBkZWwgY2FtcHVzLCB1bmEgdsOtYSBow7ptZWRhIGNvbiBjaGFyY29zIHF1ZSByZWZsZWphbiBsYSBsdXosIMOhcmJvbGVzIHkgYWxndW5vcyBwZWF0b25lcywgeSBhbCBmb25kbyBzZSBhbGNhbnphbiBhIHZlciBsb3MgZWRpZmljaW9zIGRlIGxhZHJpbGxvIGNhcmFjdGVyw61zdGljb3MgZGUgbGEgQ2l1ZGFkIFVuaXZlcnNpdGFyaWEuDQoNCiMjIFJlZCBkZSByZWdpb25lcw0KDQpgYGB7ciBwYWlzYWplLXJlZH0NCnJlc19wIDwtIGltYWdlX3RvX2dyYXBoKHBhaXNhamUsIG5fc3VwZXJwaXhlbHMgPSBLLCBjb21wYWN0bmVzcyA9IG0sIGggPSBoMCkNCm5ldF9zdW1tYXJ5KHJlc19wJGdyYXBoKQ0KYGBgDQoNCmBgYHtyIHBhaXNhamUtcmVkLWZpZywgZmlnLndpZHRoPTExLCBmaWcuaGVpZ2h0PTMuMn0NCnBhcihtZnJvdyA9IGMoMSwgMyksIG1hciA9IGMoMC4zLCAwLjMsIDEuNiwgMC4zKSkNCnBsb3RfcGFuZWwocGFpc2FqZV9oZCwgbWFpbiA9ICJJbWFnZW4gb3JpZ2luYWwiKQ0KcGxvdF9wYW5lbChwYWlzYWplLCByZXNfcCRsYWJlbHMsICJTdXBlcnDDrXhlbGVzIFNMSUMgKGNvbG9yIG1lZGlvKSIpDQpwbG90X2dyYXBoX292ZXJsYXkocmVzX3AsICJSZWQgZGUgcmVnaW9uZXMgKGdyb3NvciB+IHdfaWosIGggPSAyMCkiKQ0KYGBgDQoNClNMSUMgZGV2dWVsdmUgYHIgdmNvdW50KHJlc19wJGdyYXBoKWAgc3VwZXJww614ZWxlcyAoZWwgbsO6bWVybyBmaW5hbCBubyBjb2luY2lkZSBleGFjdGFtZW50ZSBjb24gbG9zIGByIEtgIHNvbGljaXRhZG9zIHBvcnF1ZSBTTElDIGFqdXN0YSBsYSByZWppbGxhIGluaWNpYWwgYSBsYXMgcHJvcG9yY2lvbmVzIGRlIGxhIGltYWdlbikgeSBsYSByZWQgdGllbmUgYHIgZWNvdW50KHJlc19wJGdyYXBoKWAgYXJpc3Rhcy4gRXMgdW5hIHJlZCBlc3BhY2lhbCwgY2FzaSBwbGFuYSwgY29uIGdyYWRvIG1lZGlvIGRlIGByIHJvdW5kKG1lYW4oZGVncmVlKHJlc19wJGdyYXBoKSksIDIpYCwgY29tbyBzZSBlc3BlcmEgZGUgdW5hIHRlc2VsYWNpw7NuIGRlIGxhIGltYWdlbi4gRW4gbGEgc3VwZXJwb3NpY2nDs24gc2UgdmUgcXVlIGxhcyBhcmlzdGFzIGdydWVzYXMgKGFsdGEgc2ltaWxpdHVkKSBzZSBjb25jZW50cmFuIGVuIGVsIGNpZWxvIHkgZW4gZWwgY8Opc3BlZCwgeSBxdWUgbGFzIHF1ZSBjcnV6YW4gbGEgZmFjaGFkYSBpbHVtaW5hZGEgc29uIGRlbGdhZGFzLg0KDQojIyBTZW5zaWJpbGlkYWQgYSAkaCQgZW4gbGEgc2ltaWxpdHVkIHZpc3VhbA0KDQpMYSBlc2NhbGEgJGgkIGZpamEgY3XDoW50byAiY2FzdGlnYSIgbGEgc2ltaWxpdHVkIHVuYSBkaWZlcmVuY2lhIGRlIGNvbG9yLiBDb24gJGgkIHBlcXVlw7FvLCBzb2xvIGxhcyByZWdpb25lcyBjYXNpIGlkw6ludGljYXMgdGllbmVuIHBlc28gYWx0byB5IGxhIG1heW9yw61hIGRlIGFyaXN0YXMgdmFsZW4gY2FzaSBjZXJvLiBDb24gJGgkIGdyYW5kZSwgdG9kb3MgbG9zIHBlc29zIHNlIGFjZXJjYW4gYSAxIHkgbGEgcmVkIHNlIHZ1ZWx2ZSBjYXNpIG5vIHBvbmRlcmFkYS4NCg0KYGBge3IgY3VydmEtaCwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9My40fQ0KY3VydmEgPC0gZXhwYW5kLmdyaWQoZCA9IHNlcSgwLCA4MCwgYnkgPSAwLjUpLCBoID0gaHMpIHw+DQogIG11dGF0ZSh3ID0gZXhwKC1kIC8gaCksIGggPSBmYWN0b3IoaCkpDQpkaXN0X3AgPC0gZGF0YS5mcmFtZShkID0gRShyZXNfcCRncmFwaCkkZCkNCmdncGxvdChjdXJ2YSwgYWVzKGQsIHcsIGNvbG91ciA9IGgpKSArDQogIGdlb21fcnVnKGRhdGEgPSBkaXN0X3AsIGFlcyh4ID0gZCksIGluaGVyaXQuYWVzID0gRkFMU0UsIGFscGhhID0gMC4yNSkgKw0KICBnZW9tX2xpbmUobGluZXdpZHRoID0gMC45KSArDQogIGxhYnMoeCA9IGV4cHJlc3Npb24oZFtpal0gfiAiKCIgKiBEZWx0YSAqIEUgKiAiLCBDSUVMQUIpIiksDQogICAgICAgeSA9IGV4cHJlc3Npb24od1tpal0gPT0gZXhwKC1kW2lqXSAvIGgpKSwgY29sb3VyID0gImgiLA0KICAgICAgIHRpdGxlID0gIlNpbWlsaXR1ZCBlbiBmdW5jacOzbiBkZSBsYSBkaXN0YW5jaWEgZGUgY29sb3IgKG1hcmNhczogZF9paiBkZWwgcGFpc2FqZSkiKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpDQpgYGANCg0KYGBge3IgcGFpc2FqZS1ofQ0KZXhfaF9wIDwtIGV4cGVyaW1lbnRfaChyZXNfcCwgaHMpDQpiaW5kX3Jvd3MobGFwcGx5KGV4X2hfcCwgYFtbYCwgInRhYiIpKQ0KYGBgDQoNCmBgYHtyIHBhaXNhamUtaC1maWcsIGZpZy53aWR0aD0xMywgZmlnLmhlaWdodD00LjZ9DQpwbG90X2V4cF9ncmlkKHJlc19wLCBleF9oX3AsICJoIiwgImgiKQ0KYGBgDQoNCioqTGVjdHVyYS4qKiBMYSBtb2R1bGFyaWRhZCBlcyBpbnZhcmlhbnRlIGEgbXVsdGlwbGljYXIgdG9kb3MgbG9zIHBlc29zIHBvciB1bmEgY29uc3RhbnRlLCBkZSBtb2RvIHF1ZSAkaCQgbm8gYWN0w7phIGNvbW8gdW4gc2ltcGxlIGZhY3RvciBkZSBlc2NhbGE6IGNhbWJpYSBlbCAqKmNvbnRyYXN0ZSByZWxhdGl2byoqIGVudHJlIGFyaXN0YXMgZGUgY29sb3JlcyBwYXJlY2lkb3MgeSBkaXN0aW50b3MuIEVuIGVsIHBhaXNhamU6DQoNCiogQ29uICRoPTUkIGxhIG1lZGlhbmEgZGUgJHdfe2lqfSQgZXMgYXBlbmFzIGByIGV4X2hfcFtbMV1dJHRhYiR3X21lZGlhbmFgOiBzb2xvIGxhcyByZWdpb25lcyBjYXNpIGlkw6ludGljYXMgcXVlZGFuIGZ1ZXJ0ZW1lbnRlIHVuaWRhcyB5IGxhIHNlZ21lbnRhY2nDs24gc2lndWUgbG9zIGNhbWJpb3MgZGUgY29sb3IuIFNlcGFyYSBsb3MgZG9zIHByYWRvcyBkZSBjw6lzcGVkLCBsYSB2w61hIGNvbiBjaGFyY29zLCBsYSBmYWNoYWRhLCBsb3Mgw6FyYm9sZXMgeSBlbCBjaWVsbyBlbiBibG9xdWVzIGRpc3RpbnRvcyAoMTIgY29tdW5pZGFkZXMgY29uIGFtYm9zIG3DqXRvZG9zKS4NCiogQWwgYXVtZW50YXIgJGgkLCBsb3MgcGVzb3MgdGllbmRlbiBhIDEgKG1lZGlhbmEgYHIgZXhfaF9wW1s1XV0kdGFiJHdfbWVkaWFuYWAgY29uICRoPTgwJCkgeSBsYSBwYXJ0aWNpw7NuIHNlIGFjZXJjYSBhIGxhIGRlIGxhIHJlZCBubyBwb25kZXJhZGE6IGxhcyBjb211bmlkYWRlcyBzZSBjb252aWVydGVuIGVuIGJsb3F1ZXMgZXNwYWNpYWxlcyBjb21wYWN0b3MuIENvbiAkaD00MCQgZWwgcHJhZG8gZGUgbGEgaXpxdWllcmRhIHNlIHVuZSBjb24gbG9zIMOhcmJvbGVzIHkgbGEgcGFydGUgYmFqYSBkZSBsYSBmYWNoYWRhIGVuIHVuIGdyYW4gYmxvcXVlIHZlcmRlOyBjb24gJGg9ODAkIG9jdXJyZSBsbyBtaXNtbyBhIGxhIGRlcmVjaGEgeSBsYSB2w61hIHNlIHVuZSBjb24gZWwgY2VudHJvIGRlIGxhIGZhY2hhZGEuIEVsIGNpZWxvIHkgbGEgZmFjaGFkYSBzZSBjb3J0YW4gZW4gYmxvcXVlcyBwb3IgcmF6b25lcyBnZW9tw6l0cmljYXMsIG5vIGRlIGNvbG9yLg0KKiBMYSBtb2R1bGFyaWRhZCBlcyBtw6F4aW1hIGVuICRoPTEwJCBjb24gTG91dmFpbiAoMC43MTgpIHkgZW4gJGg9MjAkIGNvbiBMZWlkZW4gKDAuNzI0KSwgeSBlbCBuw7ptZXJvIGRlIGNvbXVuaWRhZGVzIGJhamEgZGUgMTIgYSA44oCTOS4gRWwgYWN1ZXJkbyBlbnRyZSBMb3V2YWluIHkgTGVpZGVuIGVzIGNhc2kgcGVyZmVjdG8gY29uICRoPTUkIChBUkkgPSAwLjk5KSB5IGNhZSBhIDAuNTUgY29uICRoPTgwJDogY3VhbmRvIGxvcyBwZXNvcyBzZSBpZ3VhbGFuIGFwYXJlY2VuIG11Y2hhcyBwYXJ0aWNpb25lcyBkZSBtb2R1bGFyaWRhZCBwYXJlY2lkYSBxdWUgZGlmaWVyZW4gZW4gY29ydGVzIHBvY28gbWFyY2Fkb3MuDQoNClZhbG9yZXMgaW50ZXJtZWRpb3MgKCRoXGFwcHJveCAxMCTigJMkMjAkKSBlcXVpbGlicmFuIGNvbG9yIHkgY29udGlnw7xpZGFkLg0KDQojIyBTZWdtZW50YWNpb25lcyBwYXJhIHZhcmlvcyB2YWxvcmVzIGRlICRcdGF1JA0KDQpGaWphZG8gJGg9MjAkLCBzZSBmaWx0cmEgbGEgcmVkIGNvbiAkd197aWp9XGdlXHRhdSQgeSBzZSBhcGxpY2FuIExvdXZhaW4geSBMZWlkZW4gKipzb2JyZSBsYSByZWQgZmlsdHJhZGEqKiAkR19cdGF1JC4gTGEgY29sdW1uYSBgZF9tYXhgIGVzIGxhIG3DoXhpbWEgZGlzdGFuY2lhIGRlIGNvbG9yIHBlcm1pdGlkYSwgJC1oXGxuXHRhdSQuDQoNCmBgYHtyIHBhaXNhamUtdGF1fQ0KZXhfdF9wIDwtIGV4cGVyaW1lbnRfdGF1KHJlc19wLCB0YXVzLCBoID0gaDApDQpiaW5kX3Jvd3MobGFwcGx5KGV4X3RfcCwgYFtbYCwgInRhYiIpKQ0KYGBgDQoNCmBgYHtyIHBhaXNhamUtdGF1LWZpZywgZmlnLndpZHRoPTExLCBmaWcuaGVpZ2h0PTQuNn0NCnBsb3RfZXhwX2dyaWQocmVzX3AsIGV4X3RfcCwgInRhdSIsICJ0YXUiKQ0KYGBgDQoNCioqTGVjdHVyYS4qKiBDb24gJFx0YXU9MCQgc2UgdXNhIGxhIHJlZCBjb21wbGV0YS4gQWwgc3ViaXIgZWwgdW1icmFsIHNlIGNvcnRhbiBsYXMgYXJpc3RhcyBxdWUgY3J1emFuIGJvcmRlcyBkZSBjb2xvciwgYXPDrSBxdWUgbmluZ8O6biBhbGdvcml0bW8gcHVlZGUgdW5pciByZWdpb25lcyBkZSBjb21wb25lbnRlcyBkaXN0aW50b3M6IGNhZGEgY29tcG9uZW50ZSBkZSAkR19cdGF1JCBlcywgY29tbyBtw61uaW1vLCB1bmEgY29tdW5pZGFkLiBFbiBlbCBwYWlzYWplIGxhIHJlZCBwYXNhIGRlIDEgY29tcG9uZW50ZSBhIDE3LCAzOSB5IDc5IChjb24gJFx0YXU9MC43JCwgNjAgZGUgZWxsb3Mgc29uIHN1cGVycMOteGVsZXMgYWlzbGFkb3MpLCB5IGVsIG7Dum1lcm8gZGUgY29tdW5pZGFkZXMgc3ViZSBkZSAxMCBhIDI0LCA0MyB5IDgy4oCTODQuDQoNCiogQ29uICRcdGF1PTAuMyQgKCRkX3tpan1cbGUgMjQkKSBhcGFyZWNlbiBjb21vIGNvbXVuaWRhZGVzIHByb3BpYXMgbGFzIHZlbnRhbmFzIGlsdW1pbmFkYXMsIHVuYSB2ZW50YW5hIG9zY3VyYSBkZSBsYSB0b3JyZSwgbG9zIGNoYXJjb3MgeSBlbCBib3JkZSBkZWwgcHJhZG8sIHF1ZSBsYSByZWQgY29tcGxldGEgYWJzb3Jiw61hIGVuIGJsb3F1ZXMgbcOhcyBncmFuZGVzLg0KKiBDb24gJFx0YXU9MC41JCBlbWVyZ2UgbGEgZXN0cnVjdHVyYSB2ZXJ0aWNhbCBkZSBsYSBmYWNoYWRhIChwYXJhc29sZXMgeSB2ZW50YW5hbGVzKSB5IGxhIHNlZ21lbnRhY2nDs24gZW1waWV6YSBhIHBhcmVjZXIgdW4gbWFwYSBkZSBib3JkZXMuDQoqIENvbiAkXHRhdT0wLjckICgkZF97aWp9XGxlIDcuMSQpIHNlIGFjZXJjYSBhbCBuaXZlbCBkZSBzdXBlcnDDrXhlbDogZWwgZmlsdHJhZG8sIG3DoXMgcXVlIGxvcyBhbGdvcml0bW9zIGRlIGNvbXVuaWRhZGVzLCBkZXRlcm1pbmEgbGEgcGFydGljacOzbi4NCg0KTGEgbW9kdWxhcmlkYWQgc3ViZSBoYXN0YSAkXHRhdT0wLjUkLCBwb3JxdWUgbGEgcmVkIGZpbHRyYWRhIHlhIGVzdMOhICJwcmUtc2VnbWVudGFkYSIsIHkgYmFqYSBjb24gJFx0YXU9MC43JCwgY3VhbmRvIHF1ZWRhbiBkZW1hc2lhZGFzIGNvbXVuaWRhZGVzIHRyaXZpYWxlcy4gRGVzZGUgJFx0YXU9MC4zJCBMb3V2YWluIHkgTGVpZGVuIGRhbiBwcsOhY3RpY2FtZW50ZSBsYSBtaXNtYSBwYXJ0aWNpw7NuOiBjYXNpIG5vIHF1ZWRhIG1hcmdlbiBkZSBkZWNpc2nDs24gZGVudHJvIGRlIGNvbXBvbmVudGVzIHRhbiBwZXF1ZcOxYXMuDQoNCiMjIFJvYnVzdGV6IGZyZW50ZSBhIHJ1aWRvIGdhdXNzaWFubw0KDQpTZSBzdW1hIHJ1aWRvICRcdmFyZXBzaWxvblxzaW0gTigwLFxzaWdtYV4yKSQgYSBjYWRhIGNhbmFsIChlc2NhbGEgMOKAkzI1NSkgeSBzZSBjb21wYXJhIGxhIHNlZ21lbnRhY2nDs24gcnVpZG9zYSBjb24gbGEgZGUgbGEgaW1hZ2VuIGxpbXBpYSBtZWRpYW50ZSBlbCAqKsOtbmRpY2UgZGUgUmFuZCBhanVzdGFkbyAoQVJJKSoqIGEgbml2ZWwgZGUgcMOteGVsICgkMSQgPSBtaXNtYSBwYXJ0aWNpw7NuLCAkXGFwcHJveCAwJCA9IGNvaW5jaWRlbmNpYSBhbCBhemFyKS4gU2UgY29uc2lkZXJhbiBkb3MgZXNjZW5hcmlvczoNCg0KKiAqKlNMSUMgZmlqbzoqKiBzZSBjb25zZXJ2YW4gbG9zIHN1cGVycMOteGVsZXMgZGUgbGEgaW1hZ2VuIGxpbXBpYTsgZWwgcnVpZG8gc29sbyBhbHRlcmEgbG9zIGNvbG9yZXMgbWVkaW9zIHkgcG9yIHRhbnRvIGxvcyBwZXNvcy4NCiogKipTTElDIHZhcmlhYmxlOioqIFNMSUMgc2UgcmVjYWxjdWxhIHNvYnJlIGxhIGltYWdlbiBydWlkb3NhOyBlbCBydWlkbyBhZmVjdGEgdGFtYmnDqW4gbGEgdGVzZWxhY2nDs24uDQoNCmBgYHtyIHBhaXNhamUtcnVpZG8tZWosIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02LjZ9DQpuZV9wIDwtIG5vaXNlX2V4YW1wbGVzKHJlc19wLCBzaWdtYXMsIGggPSBoMCkNCnBsb3Rfbm9pc2VfZ3JpZChuZV9wKQ0KYGBgDQoNCmBgYHtyIHBhaXNhamUtcnVpZG99DQpuel9wIDwtIGV4cGVyaW1lbnRfbm9pc2UocmVzX3AsIHNpZ21hcywgcmVwcyA9IHJlcHMsIGggPSBoMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBuX3N1cGVycGl4ZWxzID0gSywgY29tcGFjdG5lc3MgPSBtKQ0Kc3VtbWFyeV9ub2lzZShuel9wKQ0KYGBgDQoNCmBgYHtyIHBhaXNhamUtcnVpZG8tZmlnLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00fQ0KcGxvdF9ub2lzZV9hcmkobnpfcCwgIlBhaXNhamU6IGVzdGFiaWxpZGFkIGRlIGxhIHNlZ21lbnRhY2nDs24gZnJlbnRlIGFsIHJ1aWRvIikNCmBgYA0KDQoqKkxlY3R1cmEuKiogTG9zIGRvcyBlc2NlbmFyaW9zIHNlIGNvbXBvcnRhbiBkZSBmb3JtYSBtdXkgZGlzdGludGEuDQoNCiogKipTTElDIGZpam8uKiogRWwgQVJJIHNlIG1hbnRpZW5lIGFsdG8sIGVudHJlIDAuODEgeSAwLjk0IChMZWlkZW4gMC44NuKAkzAuOTQsIExvdXZhaW4gMC44MeKAkzAuODMpLCBzaW4gdW5hIHRlbmRlbmNpYSBjbGFyYSBjb24gJFxzaWdtYSQ6IGVsIGRlIExlaWRlbiBpbmNsdXNvIHBhc2EgZGUgMC44NiBjb24gJFxzaWdtYT0yNSQgYSAwLjkxIGNvbiAkXHNpZ21hPTUwJC4gRWwgbsO6bWVybyBkZSBjb211bmlkYWRlcyAoOS424oCTMTApIHkgbGEgbW9kdWxhcmlkYWQgKDAuNzHigJMwLjcyKSBjYXNpIG5vIGNhbWJpYW4uIEVsIHJ1aWRvIHNlIHByb21lZGlhIGRlbnRybyBkZSBjYWRhIHN1cGVycMOteGVsIChsYSBkZXN2aWFjacOzbiBkZWwgY29sb3IgbWVkaW8gY2FlIGNvbW8gJFxzaWdtYS9cc3FydHtcdGV4dHvDoXJlYX19JCksIGRlIG1vZG8gcXVlIGxvcyBwZXNvcyBhcGVuYXMgY2FtYmlhbi4gTGFzIGRpZmVyZW5jaWFzIHF1ZSBhcGFyZWNlbiBzZSBkZWJlbiBzb2JyZSB0b2RvIGEgbGEgaW5lc3RhYmlsaWRhZCBkZSBsYSBtYXhpbWl6YWNpw7NuIGRlIGxhIG1vZHVsYXJpZGFkOiBoYXkgdmFyaWFzIHBhcnRpY2lvbmVzIGNhc2kgaWd1YWwgZGUgYnVlbmFzIChwb3IgZWplbXBsbywgZMOzbmRlIGNvcnRhciBsYSBmYWNoYWRhKSB5IHVuYSBwZXF1ZcOxYSBwZXJ0dXJiYWNpw7NuIGRlIGxvcyBwZXNvcyBiYXN0YSBwYXJhIHBhc2FyIGRlIHVuYSBhIG90cmEuDQoqICoqU0xJQyB2YXJpYWJsZS4qKiBBcXXDrSBzw60gaGF5IHVuIGRldGVyaW9ybyBjbGFybzogZWwgQVJJIGJhamEgYSDiiYggMC41OeKAkzAuNjAgY29uICRcc2lnbWE9MTAkLCDiiYggMC40MeKAkzAuNDIgY29uICRcc2lnbWE9MjUkIHkg4omIIDAuMjjigJMwLjI5IGNvbiAkXHNpZ21hPTUwJDsgZWwgbsO6bWVybyBkZSBjb211bmlkYWRlcyBjYWUgZGUgMTAgYSB1bmFzIDUgeSBsYSBtb2R1bGFyaWRhZCBkZSAwLjcyIGEgMC41OC4gRWwgcnVpZG8gYWZlY3RhIGxhIHRlc2VsYWNpw7NuOiBlbCB0w6lybWlubyBkZSBjb2xvciBkZSBTTElDIHNlIHZ1ZWx2ZSBwb2NvIGluZm9ybWF0aXZvLCBsb3Mgc3VwZXJww614ZWxlcyBkZWphbiBkZSBzZWd1aXIgbG9zIGJvcmRlcyB5IGxhIHJlZCBoZXJlZGEgcmVnaW9uZXMgcXVlIG1lemNsYW4gb2JqZXRvcy4gQWRlbcOhcywgZWwgcmVjb3J0ZSBhICRbMCwyNTVdJCBzZXNnYSBsb3MgcMOteGVsZXMgb3NjdXJvcyBoYWNpYSBhcnJpYmEgKHBhcmEgdW4gcMOteGVsIG5lZ3JvLCBsYSBtZWRpYSBkZSAkXG1heCgwLFx2YXJlcHNpbG9uKSQgZXMgJFxzaWdtYS9cc3FydHsyXHBpfVxhcHByb3ggMC40XHNpZ21hJCksIGxvIHF1ZSByZWR1Y2UgZWwgY29udHJhc3RlLiBFbiBsYXMgZmlndXJhcywgY29uICRcc2lnbWE9MjUkIGxhIGZhY2hhZGEgc2UgbWV6Y2xhIGNvbiBsYSB2w61hIHkgY29uIGxvcyDDoXJib2xlcyBkZSBsYSBpenF1aWVyZGEsIHkgY29uICRcc2lnbWE9NTAkIGxhcyBjb211bmlkYWRlcyBzb24gYmxvcXVlcyBjYXNpIHZlcnRpY2FsZXMgZW4gbG9zIHF1ZSBlbCBjaWVsbyBhcGVuYXMgc2UgZGlzdGluZ3VlLg0KDQoNCiMgQWdudXMgRGVpDQoNCipBZ251cyBEZWkqIChjLiAxNjM14oCTMTY0MCwgTXVzZW8gZGVsIFByYWRvKSBlcyB1bmEgZGUgbGFzIG9icmFzIG3DoXMgY29ub2NpZGFzIGRlIEZyYW5jaXNjbyBkZSBadXJiYXLDoW4uIE11ZXN0cmEgdW4gY29yZGVybyBibGFuY28gdGVuZGlkbyBkZSBsYWRvIHNvYnJlIHVuYSByZXBpc2EgZGUgcGllZHJhLCBjb24gbGFzIHBhdGFzIGF0YWRhcyB5IGxhIGNhYmV6YSBhcG95YWRhIGNvbiBtYW5zZWR1bWJyZSwgcmVjb3J0YWRvIGNvbnRyYSB1biBmb25kbyBuZWdybyBjYXNpIGFic29sdXRvLiBMYSBlc2NlbmEgZnVuY2lvbmEgY29tbyB1biBib2RlZ8OzbiByZWxpZ2lvc286IGVsIGNvcmRlcm8gc2ltYm9saXphIGEgQ3Jpc3RvIGNvbW8gdsOtY3RpbWEgZGVsIHNhY3JpZmljaW8sIGlkZWEgcXVlIHJlZnVlcnphbiBlbCB0ZW51ZSBoYWxvIHNvYnJlIHN1IGNhYmV6YSB5IGxhIGluc2NyaXBjacOzbiBsYXRpbmEgKlRhbXF1YW0gYWdudXMqICjigJxjb21vIHVuIGNvcmRlcm/igJ0pLCBxdWUgYWx1ZGUgYWwgcGFzYWplIGRlIElzYcOtYXMgc29icmUgZWwgY29yZGVybyBsbGV2YWRvIGFsIG1hdGFkZXJvLiANCg0KWnVyYmFyw6FuIGVtcGxlYSB1biB0ZW5lYnJpc21vIGludGVuc286IHVuYSBsdXogbGF0ZXJhbCBtb2RlbGEgY29uIGVub3JtZSBkZXRhbGxlIGxhIHRleHR1cmEgZGUgbGEgbGFuYSwgbWllbnRyYXMgZWwgcmVzdG8gZGVsIGN1YWRybyBzZSBodW5kZSBlbiBsYSBwZW51bWJyYS4gRXNhIHBhbGV0YSByZWR1Y2lkYSBkZSBibGFuY29zIGPDoWxpZG9zLCBvY3JlcyB5IG5lZ3JvcywgY29uIHVuIMO6bmljbyBjb250cmFzdGUgZG9taW5hbnRlIGVudHJlIGxhIGZpZ3VyYSB5IGVsIGZvbmRvLCBsYSBoYWNlIGVzcGVjaWFsbWVudGUgaW50ZXJlc2FudGUgcGFyYSBlbCBhbsOhbGlzaXMgcG9yIHJlZGVzLiBTdSBlc3RydWN0dXJhIGNyb23DoXRpY2EgZXMgbXVjaG8gbcOhcyBzaW1wbGUgcXVlIGxhIGRlIHVuYSBmb3RvZ3JhZsOtYSwgcGVybyBlbCBjbGFyb3NjdXJvIGRlbnRybyBkZWwgY29yZGVybyBnZW5lcmEgdmFyaWFjaW9uZXMgc3V0aWxlcyBkZSBjb2xvci4NCg0KIyMgUmVkIGRlIHJlZ2lvbmVzDQoNCmBgYHtyIGNvcmRlcm8tcmVkfQ0KcmVzX2MgPC0gaW1hZ2VfdG9fZ3JhcGgoY29yZGVybywgbl9zdXBlcnBpeGVscyA9IEssIGNvbXBhY3RuZXNzID0gbSwgaCA9IGgwKQ0KbmV0X3N1bW1hcnkocmVzX2MkZ3JhcGgpDQpgYGANCg0KYGBge3IgY29yZGVyby1yZWQtZmlnLCBmaWcud2lkdGg9MTEsIGZpZy5oZWlnaHQ9My42fQ0KcGFyKG1mcm93ID0gYygxLCAzKSwgbWFyID0gYygwLjMsIDAuMywgMS42LCAwLjMpKQ0KcGxvdF9wYW5lbChjb3JkZXJvX2hkLCBtYWluID0gIkFnbnVzIERlaSAoWnVyYmFyw6FuKSIpDQpwbG90X3BhbmVsKGNvcmRlcm8sIHJlc19jJGxhYmVscywgIlN1cGVycMOteGVsZXMgU0xJQyAoY29sb3IgbWVkaW8pIikNCnBsb3RfZ3JhcGhfb3ZlcmxheShyZXNfYywgIlJlZCBkZSByZWdpb25lcyAoZ3Jvc29yIH4gd19paiwgaCA9IDIwKSIpDQpgYGANCg0KU0xJQyBkZXZ1ZWx2ZSBgciB2Y291bnQocmVzX2MkZ3JhcGgpYCBzdXBlcnDDrXhlbGVzIHkgYHIgZWNvdW50KHJlc19jJGdyYXBoKWAgYXJpc3Rhcy4gQSBkaWZlcmVuY2lhIGRlbCBwYWlzYWplLCBlc3RhIHBpbnR1cmEgdGllbmUgdW5hIHBhbGV0YSBtdXkgcmVkdWNpZGEgKGJsYW5jb3MgY8OhbGlkb3MsIG9jcmVzIHkgbmVncm9zKSwgZGUgbW9kbyBxdWUgbXVjaGFzIGFyaXN0YXMgZGVsIGZvbmRvIHRpZW5lbiAkZF97aWp9JCBwZXF1ZcOxbyB5IHBlc28gY2VyY2FubyBhIDE6IGxhIHNpbWlsaXR1ZCBtZWRpYSBlcyBgciByb3VuZChtZWFuKEUocmVzX2MkZ3JhcGgpJHcpLCAzKWAsIGZyZW50ZSBhIGByIHJvdW5kKG1lYW4oRShyZXNfcCRncmFwaCkkdyksIDMpYCBlbiBlbCBwYWlzYWplLg0KDQojIyBTZW5zaWJpbGlkYWQgYSAkaCQNCg0KYGBge3IgY29yZGVyby1ofQ0KZXhfaF9jIDwtIGV4cGVyaW1lbnRfaChyZXNfYywgaHMpDQpiaW5kX3Jvd3MobGFwcGx5KGV4X2hfYywgYFtbYCwgInRhYiIpKQ0KYGBgDQoNCmBgYHtyIGNvcmRlcm8taC1maWcsIGZpZy53aWR0aD0xMywgZmlnLmhlaWdodD01LjR9DQpwbG90X2V4cF9ncmlkKHJlc19jLCBleF9oX2MsICJoIiwgImgiKQ0KYGBgDQoNCioqTGVjdHVyYS4qKiBQYXJhICRoXGxlIDIwJCBhbWJvcyBhbGdvcml0bW9zIHNlcGFyYW4gY29uIGNsYXJpZGFkIGVsIGNvcmRlcm8sIGxhIHJlcGlzYSBvY3JlIHkgZWwgZm9uZG8gb3NjdXJvLCB5IGRpdmlkZW4gZWwgY29yZGVybyBlbiB0cmVzIG8gY3VhdHJvIHpvbmFzIGRlIGx1eiB5IHNvbWJyYS4gUGFyYSAkaFxnZSA0MCQgZWwgY29udHJhc3RlIGZpZ3VyYS1mb25kbyBkZWphIGRlIGRvbWluYXI6IGxhIGNhYmV6YSB5IGxhIHBhcnRlIGRlbGFudGVyYSBkZWwgY29yZGVybyBzZSB1bmVuIGNvbiBsYSByZXBpc2EgeSBsYXMgY29tdW5pZGFkZXMgcGFzYW4gYSBzZXIgYmxvcXVlcyBlc3BhY2lhbGVzLCBhdW5xdWUgZWwgZm9uZG8gb3NjdXJvIHNlIG1hbnRpZW5lIGFwYXJ0ZS4gRWwgbsO6bWVybyBkZSBjb211bmlkYWRlcyBjYXNpIG5vIGNhbWJpYSAoOeKAkzExKSwgcGVybyBsYSBtb2R1bGFyaWRhZCBiYWphIGRlIGZvcm1hIG1vbsOzdG9uYSAoMC43OSDihpIgMC42OSkuIEVsIGFjdWVyZG8gTG91dmFpbi1MZWlkZW4gZXMgbcOheGltbyBjb24gJGg9MTAkIChBUkkgPSAwLjk2KSB5IGJhamEgZW4gYW1ib3MgZXh0cmVtb3M6IDAuNzUgY29uICRoPTUkLCBjdWFuZG8gY2FzaSB0b2RvcyBsb3MgcGVzb3Mgc29uIHBlcXVlw7FvcyB5IGxhIHBhcnRpY2nDs24gZGVwZW5kZSBkZSB1bmFzIHBvY2FzIGFyaXN0YXMgZnVlcnRlcywgeSAwLjg2IHkgMC43NyBjb24gJGg9NDAkIHkgJGg9ODAkLCBjdWFuZG8gbG9zIHBlc29zIHNlIGlndWFsYW4geSBhcGFyZWNlbiBtdWNoYXMgcGFydGljaW9uZXMgZXF1aXZhbGVudGVzLCBkZSBtb2RvIHF1ZSBsYSBtb2R1bGFyaWRhZCB5YSBzb2xvIGVuY3VlbnRyYSBlc3RydWN0dXJhIGdlb23DqXRyaWNhLg0KDQojIyBTZWdtZW50YWNpb25lcyBwYXJhIHZhcmlvcyB2YWxvcmVzIGRlICRcdGF1JA0KDQpgYGB7ciBjb3JkZXJvLXRhdX0NCmV4X3RfYyA8LSBleHBlcmltZW50X3RhdShyZXNfYywgdGF1cywgaCA9IGgwKQ0KYmluZF9yb3dzKGxhcHBseShleF90X2MsIGBbW2AsICJ0YWIiKSkNCmBgYA0KDQpgYGB7ciBjb3JkZXJvLXRhdS1maWcsIGZpZy53aWR0aD0xMSwgZmlnLmhlaWdodD01LjR9DQpwbG90X2V4cF9ncmlkKHJlc19jLCBleF90X2MsICJ0YXUiLCAidGF1IikNCmBgYA0KDQoqKkxlY3R1cmEuKiogRWwgcGF0csOzbiBlcyBvcHVlc3RvIGFsIGRlbCBwYWlzYWplLiBDb24gJFx0YXU9MC4zJCBsYSByZWQgc2lndWUgc2llbmRvIGNvbmV4YSAoMSBjb21wb25lbnRlKSB5IGxhIHNlZ21lbnRhY2nDs24gY2FtYmlhIHBvY28sIHBvcnF1ZSBsYXMgdHJhbnNpY2lvbmVzIGRlIGNvbG9yIGRlIGxhIHBpbnR1cmEgc29uIGdyYWR1YWxlcy4gQ29uICRcdGF1PTAuNSQgaGF5IDcgY29tcG9uZW50ZXMgeSBlbXBpZXphbiBhIHNlcGFyYXJzZSBsYXMgcGF0YXMgeSB6b25hcyBkZSBzb21icmEgZGVsIGNvcmRlcm8uIENvbiAkXHRhdT0wLjckIGhheSAzMSBjb21wb25lbnRlcyB5IDM0IGNvbXVuaWRhZGVzLCBwZXJvIGxhIGZyYWdtZW50YWNpw7NuIG9jdXJyZSAqKmRlbnRybyBkZWwgY29yZGVybyoqOiBlbCBtb2RlbGFkbyBlbiBjbGFyb3NjdXJvIGRlIGxhIGxhbmEgZ2VuZXJhIGRpZmVyZW5jaWFzIGRlIGNvbG9yIG1heW9yZXMgcXVlIDcgdW5pZGFkZXMgJFxEZWx0YSBFJC4gRWwgZm9uZG8sIGVuIGNhbWJpbywgc2UgbWFudGllbmUgZW4gcG9jb3MgYmxvcXVlcyBncmFuZGVzIHBvcnF1ZSBlcyBvc2N1cm8geSBob21vZ8OpbmVvLiBMYSBtb2R1bGFyaWRhZCBzdWJlIGRlIDAuNzYgYSAwLjgx4oCTMC44MiBjb24gJFx0YXU9MC41JCB5IHNlIG1hbnRpZW5lIGNvbiAkXHRhdT0wLjckOyBjb21vIGVuIGVsIHBhaXNhamUsIExvdXZhaW4geSBMZWlkZW4gY29pbmNpZGVuIGNhc2kgcG9yIGNvbXBsZXRvIGNvbiB1bWJyYWxlcyBhbHRvcy4NCg0KIyMgUm9idXN0ZXogZnJlbnRlIGEgcnVpZG8gZ2F1c3NpYW5vDQoNCmBgYHtyIGNvcmRlcm8tcnVpZG8tZWosIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD03LjZ9DQpuZV9jIDwtIG5vaXNlX2V4YW1wbGVzKHJlc19jLCBzaWdtYXMsIGggPSBoMCkNCnBsb3Rfbm9pc2VfZ3JpZChuZV9jKQ0KYGBgDQoNCmBgYHtyIGNvcmRlcm8tcnVpZG99DQpuel9jIDwtIGV4cGVyaW1lbnRfbm9pc2UocmVzX2MsIHNpZ21hcywgcmVwcyA9IHJlcHMsIGggPSBoMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBuX3N1cGVycGl4ZWxzID0gSywgY29tcGFjdG5lc3MgPSBtKQ0Kc3VtbWFyeV9ub2lzZShuel9jKQ0KYGBgDQoNCmBgYHtyIGNvcmRlcm8tcnVpZG8tZmlnLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00fQ0KcGxvdF9ub2lzZV9hcmkobnpfYywgIkFnbnVzIERlaTogZXN0YWJpbGlkYWQgZGUgbGEgc2VnbWVudGFjacOzbiBmcmVudGUgYWwgcnVpZG8iKQ0KYGBgDQoNCioqTGVjdHVyYS4qKg0KDQoqICoqU0xJQyBmaWpvLioqIEFtYm9zIG3DqXRvZG9zIHNvbiBlc3RhYmxlcywgTGVpZGVuIHVuIHBvY28gbcOhczogQVJJIOKJiCAwLjky4oCTMC45MyBjb24gJFxzaWdtYVxsZSAyNSQgeSAwLjg0IGNvbiAkXHNpZ21hPTUwJCwgZnJlbnRlIGEgMC44OOKAkzAuODkgeSAwLjgxIGNvbiBMb3V2YWluLiBDb21vIGVuIGVsIHBhaXNhamUsIGxhIGRpZmVyZW5jaWEgc2UgZGViZSBzb2JyZSB0b2RvIGEgcXVlIExvdXZhaW4gY2FtYmlhIGRlIHNvbHVjacOzbiBlbnRyZSBwYXJ0aWNpb25lcyBjYXNpIGVxdWl2YWxlbnRlcy4NCiogKipTTElDIHZhcmlhYmxlLioqIEVsIEFSSSBjYWUgYSDiiYggMC42OeKAkzAuNzYsIDAuNDTigJMwLjQ1IHkgMC4yNiBwYXJhICRcc2lnbWE9MTAsIDI1LCA1MCQ6IGFsZ28gbWVqb3IgcXVlIGVuIGVsIHBhaXNhamUgY29uIHJ1aWRvIGxldmUgeSBwcsOhY3RpY2FtZW50ZSBpZ3VhbCBjb24gcnVpZG8gZnVlcnRlLiBFbiBsYXMgZmlndXJhcywgY29uICRcc2lnbWE9MTAkIGVsIGNvcmRlcm8gYcO6biBzZSBzZXBhcmEgZGVsIGZvbmRvLCBwZXJvIGxhIHJlcGlzYSBzZSB1bmUgY29uIGVsIGNvcmRlcm87IGNvbiAkXHNpZ21hPTI1JCBzdSBzaWx1ZXRhIHNlIHBpZXJkZTsgY29uICRcc2lnbWE9NTAkIGxhcyBjb211bmlkYWRlcyBzb24gZnJhbmphcyB2ZXJ0aWNhbGVzIHNpbiByZWxhY2nDs24gY29uIGxhIGVzY2VuYS4NCiogRWwgcmVjb3J0ZSBhICRbMCwyNTVdJCBwZXNhIG3DoXMgYXF1w606IGVsIGZvbmRvIGVzIGNhc2kgbmVncm8sIGFzw60gcXVlIGVsIHJ1aWRvIHJlY29ydGFkbyBsbyBhY2xhcmEgZGUgZm9ybWEgc2lzdGVtw6F0aWNhIHkgcmVkdWNlIGp1c3RvIGVsIGNvbnRyYXN0ZSBmaWd1cmEtZm9uZG8gcXVlIHNvc3RlbsOtYSBsYSBzZWdtZW50YWNpw7NuLg0KDQojIENvbXBhcmFjacOzbiBlbnRyZSBpbcOhZ2VuZXMNCioqTGVjdHVyYS4qKiBMYXMgZG9zIHJlZGVzIHRpZW5lbiB0YW1hw7FvIHkgZXN0cnVjdHVyYSBjYXNpIGlndWFsZXMgKDE1MiB5IDE2MSB2w6lydGljZXMsIGdyYWRvIG1lZGlvIOKJiCA1LjQsIHRyYW5zaXRpdmlkYWQg4omIIDAuNDApOiBsYSB0b3BvbG9nw61hIGxhIGltcG9uZSBTTElDLCBubyBsYSBpbWFnZW4uIExvIHF1ZSBsYXMgZGlzdGluZ3VlIHNvbiBsb3MgcGVzb3MuIEVuIGVsICpBZ251cyBEZWkqIGxhIG1lZGlhbmEgZGUgJGRfe2lqfSQgZXMgNi40MiwgZnJlbnRlIGEgMTYuMzIgZW4gZWwgcGFpc2FqZSwgeSBlbCA2Ni40ICUgZGUgbGFzIGFyaXN0YXMgdGllbmUgJHdfe2lqfVxnZSAwLjUkLCBmcmVudGUgYWwgNDYuMiAlOiBsYSBwYWxldGEgcmVkdWNpZGEgZGUgbGEgcGludHVyYSBwcm9kdWNlIHVuYSByZWQgbXVjaG8gbcOhcyBob21vZ8OpbmVhLiBQZXNlIGEgZXN0bywgbGEgcm9idXN0ZXogZnJlbnRlIGEgcnVpZG8gZnVlcnRlICgkXHNpZ21hPTUwJCkgZXMgbXV5IHBhcmVjaWRhIGVuIGFtYmFzOiBjb24gU0xJQyBmaWpvIGVsIEFSSSBlc3TDoSBlbnRyZSAwLjgxIHkgMC45MSwgeSBjb24gU0xJQyB2YXJpYWJsZSBjYWUgYSAwLjI24oCTMC4yOSwgbG8gcXVlIGNvbmZpcm1hIHF1ZSBlbCBmYWN0b3IgZGV0ZXJtaW5hbnRlIGVzIGxhIHRlc2VsYWNpw7NuIHkgbm8gZWwgY29udGVuaWRvIGRlIGxhIGltYWdlbi4NCmBgYHtyIGNvbXBhcmFjaW9ufQ0KY29tcCA8LSByYmluZChjYmluZChpbWFnZW4gPSAiUGFpc2FqZSIsIG5ldF9zdW1tYXJ5KHJlc19wJGdyYXBoKSksDQogICAgICAgICAgICAgIGNiaW5kKGltYWdlbiA9ICJBZ251cyBEZWkiLCBuZXRfc3VtbWFyeShyZXNfYyRncmFwaCkpKQ0KY29tcA0KDQpkaXN0X2JvdGggPC0gcmJpbmQoZGF0YS5mcmFtZShpbWFnZW4gPSAiUGFpc2FqZSIsIGQgPSBFKHJlc19wJGdyYXBoKSRkKSwNCiAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKGltYWdlbiA9ICJBZ251cyBEZWkiLCBkID0gRShyZXNfYyRncmFwaCkkZCkpDQpkaXN0X2JvdGggfD4NCiAgZ3JvdXBfYnkoaW1hZ2VuKSB8Pg0KICBzdW1tYXJpc2UoZF9tZWRpYW5hID0gcm91bmQobWVkaWFuKGQpLCAyKSwgZF9tZWRpYSA9IHJvdW5kKG1lYW4oZCksIDIpLA0KICAgICAgICAgICAgcHJvcF93X21heW9yXzAuNSA9IHJvdW5kKG1lYW4oZXhwKC1kIC8gaDApID49IDAuNSksIDMpKQ0KYGBgDQoNCmBgYHtyIGNvbXBhcmFjaW9uLWZpZywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9My42fQ0KZ2dwbG90KGRpc3RfYm90aCwgYWVzKGV4cCgtZCAvIGgwKSwgZmlsbCA9IGltYWdlbikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBhbHBoYSA9IDAuNiwgcG9zaXRpb24gPSAiaWRlbnRpdHkiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkFnbnVzIERlaSIgPSAiIzdENjYwOCIsICJQYWlzYWplIiA9ICIjMjQ3MUEzIikpICsNCiAgbGFicyh4ID0gZXhwcmVzc2lvbih3W2lqXSB+ICIoaCA9IDIwKSIpLCB5ID0gIk7Dum1lcm8gZGUgYXJpc3RhcyIsIGZpbGwgPSBOVUxMLA0KICAgICAgIHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gZGUgc2ltaWxpdHVkZXMgdmlzdWFsZXMiKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpDQpgYGANCg0KYGBge3IgY29tcGFyYWNpb24tcnVpZG99DQpiaW5kX3Jvd3MobXV0YXRlKG56X3AsIGltYWdlbiA9ICJQYWlzYWplIiksIG11dGF0ZShuel9jLCBpbWFnZW4gPSAiQWdudXMgRGVpIikpIHw+DQogIGZpbHRlcihzaWdtYSA9PSBtYXgoc2lnbWFzKSkgfD4NCiAgZ3JvdXBfYnkoaW1hZ2VuLCBzbGljLCBtZXRvZG8pIHw+DQogIHN1bW1hcmlzZShBUklfbWVkaWEgPSByb3VuZChtZWFuKEFSSSksIDMpLCAuZ3JvdXBzID0gImRyb3AiKQ0KYGBgDQoNCiMgQ29uY2x1c2lvbmVzDQoNCjEuICoqRWwgcGFyw6FtZXRybyAkaCQgY29udHJvbGEgZWwgYmFsYW5jZSBlbnRyZSBjb2xvciB5IGdlb21ldHLDrWEuKiogVmFsb3JlcyBwZXF1ZcOxb3MgaGFjZW4gcXVlIGxhcyBjb211bmlkYWRlcyBzaWdhbiBsb3MgYm9yZGVzIGRlIGNvbG9yOyB2YWxvcmVzIGdyYW5kZXMgYXBsYW5hbiBsb3MgcGVzb3MgeSBsYSBwYXJ0aWNpw7NuIHNlIGFwcm94aW1hIGEgbGEgZGUgbGEgcmVkIG5vIHBvbmRlcmFkYSwgcXVlIHNvbG8gcmVmbGVqYSBsYSBlc3RydWN0dXJhIGVzcGFjaWFsIGRlIGxhIHRlc2VsYWNpw7NuLg0KMi4gKipFbCB1bWJyYWwgJFx0YXUkIGltcG9uZSBmcm9udGVyYXMgZHVyYXMuKiogRmlsdHJhciBhbnRlcyBkZSBkZXRlY3RhciBjb211bmlkYWRlcyBnYXJhbnRpemEgcXVlIGxhcyByZWdpb25lcyBkZSBjb2xvciBtdXkgZGlzdGludG8gbnVuY2EgcXVlZGVuIGp1bnRhcywgcGVybyBwcm9kdWNlIG11Y2hhcyBjb211bmlkYWRlcyBkaW1pbnV0YXMgKHN1cGVycMOteGVsZXMgYWlzbGFkb3MpIGN1YW5kbyAkXHRhdSQgZXMgYWx0by4gRW4gbGEgcHLDoWN0aWNhIGNvbnZpZW5lIHVzYXIgJFx0YXUkIG1vZGVyYWRvcyBvIGZ1c2lvbmFyIGRlc3B1w6lzIGxhcyBjb211bmlkYWRlcyBwZXF1ZcOxYXMuDQozLiAqKkxvdXZhaW4geSBMZWlkZW4gZGFuIHBhcnRpY2lvbmVzIHBhcmVjaWRhcyoqLCBjb24gTGVpZGVuIHNpZW1wcmUgaWd1YWwgbyBsaWdlcmFtZW50ZSBzdXBlcmlvciBlbiBtb2R1bGFyaWRhZCB5IGNvbiBsYSBnYXJhbnTDrWEgZGUgY29tdW5pZGFkZXMgY29uZXhhcy4gU3VzIGRpZmVyZW5jaWFzIHNlIGNvbmNlbnRyYW4gZW4gem9uYXMgaG9tb2fDqW5lYXMsIGRvbmRlIGhheSBtdWNoYXMgcGFydGljaW9uZXMgY2FzaSBlcXVpdmFsZW50ZXMgKHBvciBlamVtcGxvLCBlbCBmb25kbyBkZWwgKkFnbnVzIERlaSogY29uICRoPTgwJCBlbCBBUkkgZW50cmUgYW1ib3MgZXMgMC41NSBlbiBlbCBwYWlzYWplIHkgMC43NyBlbiBlbCBBZ251cyBEZWkpLCB5IGRlc2FwYXJlY2VuIGN1YW5kbyBlbCBmaWx0cmFkbyBwb3IgJFx0YXUkIHlhIGZpamEgbGEgZXN0cnVjdHVyYS4NCjQuICoqUm9idXN0ZXogZnJlbnRlIGFsIHJ1aWRvLioqIEVsIGFncnVwYW1pZW50byBjb24gTG91dmFpbiB5IExlaWRlbiAqKnNlIG1hbnRpZW5lIHJhem9uYWJsZW1lbnRlIHNpIGVsIHJ1aWRvIHNvbG8gYWZlY3RhIGEgbG9zIHBlc29zKiogKFNMSUMgZmlqbyk6IGVsIHByb21lZGlvIGRlbnRybyBkZSBjYWRhIHN1cGVycMOteGVsIGFtb3J0aWd1YSBlbCBydWlkbywgeSBsYXMgZGlmZXJlbmNpYXMgcXVlIHF1ZWRhbiByZWZsZWphbiBzb2JyZSB0b2RvIGxhIGluZXN0YWJpbGlkYWQgZGUgbGEgb3B0aW1pemFjacOzbiwgbWVub3IgZW4gTGVpZGVuLiAqKk5vIHNlIG1hbnRpZW5lIGN1YW5kbyBlbCBydWlkbyB0YW1iacOpbiBhbHRlcmEgbGEgdGVzZWxhY2nDs24qKiAoU0xJQyB2YXJpYWJsZSk6IGVsIEFSSSBjYWUgYSDiiYggMC4yNuKAkzAuMjkgY29uICRcc2lnbWE9NTAkIGVuIGFtYmFzIGltw6FnZW5lcyB5IGNvbiBhbWJvcyBtw6l0b2Rvcy4gRWwgcHVudG8gZMOpYmlsIGRlbCBmbHVqbyBlcyBTTElDLCBubyBlbCBhbGdvcml0bW8gZGUgY29tdW5pZGFkZXMuIFVuIHN1YXZpemFkbyBwcmV2aW8gKHBvciBlamVtcGxvLCB1biBmaWx0cm8gZ2F1c3NpYW5vIG8gZGUgbWVkaWFuYSBhbnRlcyBkZSBTTElDKSBzZXLDrWEgbGEgZXh0ZW5zacOzbiBuYXR1cmFsIHBhcmEgbWVqb3JhciBsYSByb2J1c3Rlei4NCjUuICoqTW9kdWxhcmlkYWQgbm8gZXMgY2FsaWRhZCB2aXN1YWwuKiogRW4gZWwgZm9uZG8gdW5pZm9ybWUgZGVsICpBZ251cyBEZWkqIHNlIG9idGllbmVuIG1vZHVsYXJpZGFkZXMgYWx0YXMgY29uIGNvbXVuaWRhZGVzIHF1ZSBubyBjb3JyZXNwb25kZW4gYSBuaW5ndW5hIGVzdHJ1Y3R1cmEgZGUgbGEgaW1hZ2VuLg0KDQojIFJlZmVyZW5jaWFzDQoNCiogQWNoYW50YSwgUi4sIFNoYWppLCBBLiwgU21pdGgsIEsuLCBMdWNjaGksIEEuLCBGdWEsIFAuIHkgU8O8c3N0cnVuaywgUy4gKDIwMTIpLiBTTElDIHN1cGVycGl4ZWxzIGNvbXBhcmVkIHRvIHN0YXRlLW9mLXRoZS1hcnQgc3VwZXJwaXhlbCBtZXRob2RzLiAqSUVFRSBUUEFNSSosIDM0KDExKSwgMjI3NOKAkzIyODIuDQoqIEJsb25kZWwsIFYuIEQuLCBHdWlsbGF1bWUsIEouLUwuLCBMYW1iaW90dGUsIFIuIHkgTGVmZWJ2cmUsIEUuICgyMDA4KS4gRmFzdCB1bmZvbGRpbmcgb2YgY29tbXVuaXRpZXMgaW4gbGFyZ2UgbmV0d29ya3MuICpKb3VybmFsIG9mIFN0YXRpc3RpY2FsIE1lY2hhbmljcyosIFAxMDAwOC4NCiogVHJhYWcsIFYuIEEuLCBXYWx0bWFuLCBMLiB5IHZhbiBFY2ssIE4uIEouICgyMDE5KS4gRnJvbSBMb3V2YWluIHRvIExlaWRlbjogZ3VhcmFudGVlaW5nIHdlbGwtY29ubmVjdGVkIGNvbW11bml0aWVzLiAqU2NpZW50aWZpYyBSZXBvcnRzKiwgOSwgNTIzMy4NCiogSHViZXJ0LCBMLiB5IEFyYWJpZSwgUC4gKDE5ODUpLiBDb21wYXJpbmcgcGFydGl0aW9ucy4gKkpvdXJuYWwgb2YgQ2xhc3NpZmljYXRpb24qLCAyLCAxOTPigJMyMTguDQoqIFNvc2EsIEouICpBbsOhbGlzaXMgZGUgaW3DoWdlbmVzIG1lZGlhbnRlIHJlZGVzKi4gTm90YXMgZGUgY2xhc2UsIFJQdWJzLg0KKiBJbcOhZ2VuZXM6IGZvdG9ncmFmw61hIMKpIFJvZHJpZ28gRMOhdmlsYSAodXNvIGFjYWTDqW1pY28pOyAqQWdudXMgRGVpKiwgRnJhbmNpc2NvIGRlIFp1cmJhcsOhbiwgTXVzZW8gTmFjaW9uYWwgZGVsIFByYWRvIChkb21pbmlvIHDDumJsaWNvKS4NCiogQW50aHJvcGljLiAoMjAyNikuICpDbGF1ZGUqICh2ZXJzacOzbiBPcHVzIDUuNSkgW01vZGVsbyBkZSBsZW5ndWFqZSBkZSBncmFuIHRhbWHDsW9dLiBodHRwczovL2NsYXVkZS5haQ==