Fiabilidad

Con 2 Clusters

  1. Seleccionar el número de clusters K.
  2. Generar K centroides: puntos aleatorios en el espacio que estamos trabajando.
  3. Calcular la distancia de cada punto al centroide.
  4. Etiquetar cada punto con el nombre del centroide más cercano.
  5. Re calcular cada centroide, calculando la media de todos los puntos asociados a cada centroide.

Del paso 5 se vuelve a pasar al paso 3.

Se usa principalmente cuando no sabemos las etiquetas que se pueden colocar, por eso se llama aprendizaje no supervisado. Con cada iteración el centro se ira corriendo despues de promediar los puntos. Lo que esto ocasiona es que se separen los clusters para que podamos identificarlo con una etiqueta.

library(dplyr)
petal_length <-
seq(min(iris$Petal.Length), max(iris$Petal.Length), by = 0.01)
petal_width <-
seq(min(iris$Petal.Width), max(iris$Petal.Width), by = 0.01)
set.seed(12345)
cluster_x <- 
  sample(petal_length, size = 2)
#donde ese 2 es el numero de clusters
cluster_y <- 
  sample(petal_width, size = 2)
#pch es el tipo de punto, 16 es punto relleno
plot(iris$Petal.Length,
     iris$Petal.Width,
     pch = 16,
     xlab = "Petal length",
     ylab = "Petal width",
     main = "Iris Dataset")
points(cluster_x, cluster_y, col = c('blue', 'red'), pch = 17, cex =2)

Dataset con: en la primer columna el length y en la segunda el width. Coordenada \(x\): length Coordenada \(y\): width

df <- tibble(petal_length = iris$Petal.Length,
             petal_width = iris$Petal.Width)

Del dataframe le agregamos 3 columnas, la primera es la distancia del punto al cluster 1, con la formula de la distancia, en cluster[1] viene la coordenada en x del petal_length. distance_2 es lo mismmo pero para el otro punto. Luego asigna el cluster (cuando son dos es facil, ifelse) Si la distancia 1 es menor que la distancia 2 se asigna como blue, si no como red.

df <- 
  df %>% 
    mutate(distance_1 = sqrt((petal_length - cluster_x[1])^2 + (petal_width - cluster_y[1])^2),
           distance_2 = sqrt((petal_length - cluster_x[2])^2 + (petal_width - cluster_y[2])^2),
           cluster = ifelse(distance_1 < distance_2, "blue", "red"))
plot(df$petal_length, df$petal_width, col=df$cluster,
     pch = 16,
     xlab = "Petal length",
     ylab = "Petal width",
     main = "Iris Dataset")
points(cluster_x, cluster_y, col = c('blue', 'red'), pch = 17, cex = 2)

El dataframe se agrupa por los clusters. Se calcula un promedio del petal_length y el petal_width (summarise) y luego desagrupa. Esto devuelve un dataframe con “cluster” en la columna 1, avg_x en la columna 2 y avg_y en la columna 3. Como esta agupado solo se tienen 2 filas: Cluster1 y Cluster2.

new_clusters <- 
  df %>% 
  group_by(cluster) %>% 
  summarise(avg_x = mean(petal_length),
            avg_y = mean(petal_width)) %>% 
  ungroup()
new_clusters

Pull es una funcion que agarra el dato y devuelve un numerico, como esta devolviendo la columna eso es un vector.

cluster_x <-
  new_clusters %>% pull(avg_x)
cluster_y <-
  new_clusters %>% pull(avg_y)

Run 1

df <- tibble(petal_length = iris$Petal.Length,
             petal_width = iris$Petal.Width)
df <- 
  df %>% 
    mutate(distance_1 = sqrt((petal_length - cluster_x[1])^2 + (petal_width - cluster_y[1])^2),
           distance_2 = sqrt((petal_length - cluster_x[2])^2 + (petal_width - cluster_y[2])^2),
           cluster = ifelse(distance_1 < distance_2, "blue", "red"))
plot(df$petal_length, df$petal_width, col=df$cluster,
     pch = 16,
     xlab = "Petal length",
     ylab = "Petal width",
     main = "Iris Dataset")
points(cluster_x, cluster_y, col = c('blue', 'red'), pch = 17, cex = 2)

new_clusters <- 
  df %>% 
  group_by(cluster) %>% 
  summarise(avg_x = mean(petal_length),
            avg_y = mean(petal_width)) %>% 
  ungroup()
cluster_x <-
  new_clusters %>% pull(avg_x)
cluster_y <-
  new_clusters %>% pull(avg_y)

Run 2

df <- tibble(petal_length = iris$Petal.Length,
             petal_width = iris$Petal.Width)
df <- 
  df %>% 
    mutate(distance_1 = sqrt((petal_length - cluster_x[1])^2 + (petal_width - cluster_y[1])^2),
           distance_2 = sqrt((petal_length - cluster_x[2])^2 + (petal_width - cluster_y[2])^2),
           cluster = ifelse(distance_1 < distance_2, "blue", "red"))
plot(df$petal_length, df$petal_width, col=df$cluster,
     pch = 16,
     xlab = "Petal length",
     ylab = "Petal width",
     main = "Iris Dataset")
points(cluster_x, cluster_y, col = c('blue', 'red'), pch = 17, cex = 2)

new_clusters <- 
  df %>% 
  group_by(cluster) %>% 
  summarise(avg_x = mean(petal_length),
            avg_y = mean(petal_width)) %>% 
  ungroup()
cluster_x <-
  new_clusters %>% pull(avg_x)
cluster_y <-
  new_clusters %>% pull(avg_y)

Run 3

df <- tibble(petal_length = iris$Petal.Length,
             petal_width = iris$Petal.Width)
df <- 
  df %>% 
    mutate(distance_1 = sqrt((petal_length - cluster_x[1])^2 + (petal_width - cluster_y[1])^2),
           distance_2 = sqrt((petal_length - cluster_x[2])^2 + (petal_width - cluster_y[2])^2),
           cluster = ifelse(distance_1 < distance_2, "blue", "red"))
plot(df$petal_length, df$petal_width, col=df$cluster,
     pch = 16,
     xlab = "Petal length",
     ylab = "Petal width",
     main = "Iris Dataset")
points(cluster_x, cluster_y, col = c('blue', 'red'), pch = 17, cex = 2)

new_clusters <- 
  df %>% 
  group_by(cluster) %>% 
  summarise(avg_x = mean(petal_length),
            avg_y = mean(petal_width)) %>% 
  ungroup()
cluster_x <-
  new_clusters %>% pull(avg_x)
cluster_y <-
  new_clusters %>% pull(avg_y)

Run 4

df <- tibble(petal_length = iris$Petal.Length,
             petal_width = iris$Petal.Width)
df <- 
  df %>% 
    mutate(distance_1 = sqrt((petal_length - cluster_x[1])^2 + (petal_width - cluster_y[1])^2),
           distance_2 = sqrt((petal_length - cluster_x[2])^2 + (petal_width - cluster_y[2])^2),
           cluster = ifelse(distance_1 < distance_2, "blue", "red"))
plot(df$petal_length, df$petal_width, col=df$cluster,
     pch = 16,
     xlab = "Petal length",
     ylab = "Petal width",
     main = "Iris Dataset")
points(cluster_x, cluster_y, col = c('blue', 'red'), pch = 17, cex = 2)

new_clusters <- 
  df %>% 
  group_by(cluster) %>% 
  summarise(avg_x = mean(petal_length),
            avg_y = mean(petal_width)) %>% 
  ungroup()
cluster_x <-
  new_clusters %>% pull(avg_x)
cluster_y <-
  new_clusters %>% pull(avg_y)

Run 5

df <- tibble(petal_length = iris$Petal.Length,
             petal_width = iris$Petal.Width)
df <- 
  df %>% 
    mutate(distance_1 = sqrt((petal_length - cluster_x[1])^2 + (petal_width - cluster_y[1])^2),
           distance_2 = sqrt((petal_length - cluster_x[2])^2 + (petal_width - cluster_y[2])^2),
           cluster = ifelse(distance_1 < distance_2, "blue", "red"))
plot(df$petal_length, df$petal_width, col=df$cluster,
     pch = 16,
     xlab = "Petal length",
     ylab = "Petal width",
     main = "Iris Dataset")
points(cluster_x, cluster_y, col = c('blue', 'red'), pch = 17, cex = 2)

LS0tDQp0aXRsZTogIksgTWVhbnM6IEFwcmVuZGl6YWplIG5vIHN1cGVydmlzYWRvIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgRmlhYmlsaWRhZA0KDQojIyMgQ29uIDIgQ2x1c3RlcnMNCg0KMS4gU2VsZWNjaW9uYXIgZWwgbsO6bWVybyBkZSBjbHVzdGVycyBLLg0KMi4gR2VuZXJhciBLIGNlbnRyb2lkZXM6IHB1bnRvcyBhbGVhdG9yaW9zIGVuIGVsIGVzcGFjaW8gcXVlIGVzdGFtb3MgdHJhYmFqYW5kby4NCjMuIENhbGN1bGFyIGxhIGRpc3RhbmNpYSBkZSBjYWRhIHB1bnRvIGFsIGNlbnRyb2lkZS4NCjQuIEV0aXF1ZXRhciBjYWRhIHB1bnRvIGNvbiBlbCBub21icmUgZGVsIGNlbnRyb2lkZSBtw6FzIGNlcmNhbm8uDQo1LiBSZSBjYWxjdWxhciBjYWRhIGNlbnRyb2lkZSwgY2FsY3VsYW5kbyBsYSBtZWRpYSBkZSB0b2RvcyBsb3MgcHVudG9zIGFzb2NpYWRvcyBhIGNhZGEgY2VudHJvaWRlLg0KDQpEZWwgcGFzbyA1IHNlIHZ1ZWx2ZSBhIHBhc2FyIGFsIHBhc28gMy4NCg0KU2UgdXNhIHByaW5jaXBhbG1lbnRlIGN1YW5kbyBubyBzYWJlbW9zIGxhcyBldGlxdWV0YXMgcXVlIHNlIHB1ZWRlbiBjb2xvY2FyLCBwb3IgZXNvIHNlIGxsYW1hIGFwcmVuZGl6YWplIG5vIHN1cGVydmlzYWRvLiANCkNvbiBjYWRhIGl0ZXJhY2nDs24gZWwgY2VudHJvIHNlIGlyYSBjb3JyaWVuZG8gZGVzcHVlcyBkZSBwcm9tZWRpYXIgbG9zIHB1bnRvcy4gTG8gcXVlIGVzdG8gb2Nhc2lvbmEgZXMgcXVlIHNlIHNlcGFyZW4gbG9zIGNsdXN0ZXJzIHBhcmEgcXVlIHBvZGFtb3MgaWRlbnRpZmljYXJsbyBjb24gdW5hIGV0aXF1ZXRhLg0KDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQpgYGB7cn0NCnBldGFsX2xlbmd0aCA8LQ0Kc2VxKG1pbihpcmlzJFBldGFsLkxlbmd0aCksIG1heChpcmlzJFBldGFsLkxlbmd0aCksIGJ5ID0gMC4wMSkNCg0KcGV0YWxfd2lkdGggPC0NCnNlcShtaW4oaXJpcyRQZXRhbC5XaWR0aCksIG1heChpcmlzJFBldGFsLldpZHRoKSwgYnkgPSAwLjAxKQ0KDQpzZXQuc2VlZCgxMjM0NSkNCmNsdXN0ZXJfeCA8LSANCiAgc2FtcGxlKHBldGFsX2xlbmd0aCwgc2l6ZSA9IDIpDQojZG9uZGUgZXNlIDIgZXMgZWwgbnVtZXJvIGRlIGNsdXN0ZXJzDQoNCmNsdXN0ZXJfeSA8LSANCiAgc2FtcGxlKHBldGFsX3dpZHRoLCBzaXplID0gMikNCmBgYA0KDQpgYGB7cn0NCiNwY2ggZXMgZWwgdGlwbyBkZSBwdW50bywgMTYgZXMgcHVudG8gcmVsbGVubw0KcGxvdChpcmlzJFBldGFsLkxlbmd0aCwNCiAgICAgaXJpcyRQZXRhbC5XaWR0aCwNCiAgICAgcGNoID0gMTYsDQogICAgIHhsYWIgPSAiUGV0YWwgbGVuZ3RoIiwNCiAgICAgeWxhYiA9ICJQZXRhbCB3aWR0aCIsDQogICAgIG1haW4gPSAiSXJpcyBEYXRhc2V0IikNCiNjZXggZXMgZWwgc2l6ZSBkZWwgdHJpYW5ndWxpdG8NCnBvaW50cyhjbHVzdGVyX3gsIGNsdXN0ZXJfeSwgY29sID0gYygnYmx1ZScsICdyZWQnKSwgcGNoID0gMTcsIGNleCA9MikNCmBgYA0KDQpEYXRhc2V0IGNvbjogZW4gbGEgcHJpbWVyIGNvbHVtbmEgZWwgbGVuZ3RoIHkgZW4gbGEgc2VndW5kYSBlbCB3aWR0aC4gDQpDb29yZGVuYWRhICR4JDogbGVuZ3RoDQpDb29yZGVuYWRhICR5JDogd2lkdGgNCmBgYHtyfQ0KZGYgPC0gdGliYmxlKHBldGFsX2xlbmd0aCA9IGlyaXMkUGV0YWwuTGVuZ3RoLA0KICAgICAgICAgICAgIHBldGFsX3dpZHRoID0gaXJpcyRQZXRhbC5XaWR0aCkNCmBgYA0KDQpEZWwgZGF0YWZyYW1lIGxlIGFncmVnYW1vcyAzIGNvbHVtbmFzLCBsYSBwcmltZXJhIGVzIGxhIGRpc3RhbmNpYSBkZWwgcHVudG8gYWwgY2x1c3RlciAxLCBjb24gbGEgZm9ybXVsYSBkZSBsYSBkaXN0YW5jaWEsIGVuIGBjbHVzdGVyWzFdYCB2aWVuZSBsYSBjb29yZGVuYWRhIGVuIHggZGVsIHBldGFsX2xlbmd0aC4NCmRpc3RhbmNlXzIgZXMgbG8gbWlzbW1vIHBlcm8gcGFyYSBlbCBvdHJvIHB1bnRvLg0KTHVlZ28gYXNpZ25hIGVsIGNsdXN0ZXIgKGN1YW5kbyBzb24gZG9zIGVzIGZhY2lsLCBpZmVsc2UpIFNpIGxhIGRpc3RhbmNpYSAxIGVzIG1lbm9yIHF1ZSBsYSBkaXN0YW5jaWEgMiBzZSBhc2lnbmEgY29tbyBibHVlLCBzaSBubyBjb21vIHJlZC4NCmBgYHtyfQ0KZGYgPC0gDQogIGRmICU+JSANCiAgICBtdXRhdGUoZGlzdGFuY2VfMSA9IHNxcnQoKHBldGFsX2xlbmd0aCAtIGNsdXN0ZXJfeFsxXSleMiArIChwZXRhbF93aWR0aCAtIGNsdXN0ZXJfeVsxXSleMiksDQogICAgICAgICAgIGRpc3RhbmNlXzIgPSBzcXJ0KChwZXRhbF9sZW5ndGggLSBjbHVzdGVyX3hbMl0pXjIgKyAocGV0YWxfd2lkdGggLSBjbHVzdGVyX3lbMl0pXjIpLA0KICAgICAgICAgICBjbHVzdGVyID0gaWZlbHNlKGRpc3RhbmNlXzEgPCBkaXN0YW5jZV8yLCAiYmx1ZSIsICJyZWQiKSkNCmBgYA0KDQoNCmBgYHtyfQ0KcGxvdChkZiRwZXRhbF9sZW5ndGgsIGRmJHBldGFsX3dpZHRoLCBjb2w9ZGYkY2x1c3RlciwNCiAgICAgcGNoID0gMTYsDQogICAgIHhsYWIgPSAiUGV0YWwgbGVuZ3RoIiwNCiAgICAgeWxhYiA9ICJQZXRhbCB3aWR0aCIsDQogICAgIG1haW4gPSAiSXJpcyBEYXRhc2V0IikNCnBvaW50cyhjbHVzdGVyX3gsIGNsdXN0ZXJfeSwgY29sID0gYygnYmx1ZScsICdyZWQnKSwgcGNoID0gMTcsIGNleCA9IDIpDQpgYGANCg0KDQpFbCBkYXRhZnJhbWUgc2UgYWdydXBhIHBvciBsb3MgY2x1c3RlcnMuIFNlIGNhbGN1bGEgdW4gcHJvbWVkaW8gZGVsIHBldGFsX2xlbmd0aCB5IGVsIHBldGFsX3dpZHRoIChzdW1tYXJpc2UpIHkgbHVlZ28gZGVzYWdydXBhLiANCkVzdG8gZGV2dWVsdmUgdW4gZGF0YWZyYW1lIGNvbiAiY2x1c3RlciIgZW4gbGEgY29sdW1uYSAxLCBhdmdfeCBlbiBsYSBjb2x1bW5hIDIgeSBhdmdfeSBlbiBsYSBjb2x1bW5hIDMuIENvbW8gZXN0YSBhZ3VwYWRvIHNvbG8gc2UgdGllbmVuIDIgZmlsYXM6IENsdXN0ZXIxIHkgQ2x1c3RlcjIuDQpgYGB7cn0NCm5ld19jbHVzdGVycyA8LSANCiAgZGYgJT4lIA0KICBncm91cF9ieShjbHVzdGVyKSAlPiUgDQogIHN1bW1hcmlzZShhdmdfeCA9IG1lYW4ocGV0YWxfbGVuZ3RoKSwNCiAgICAgICAgICAgIGF2Z195ID0gbWVhbihwZXRhbF93aWR0aCkpICU+JSANCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3J9DQpuZXdfY2x1c3RlcnMNCmBgYA0KDQoNClB1bGwgZXMgdW5hIGZ1bmNpb24gcXVlIGFnYXJyYSBlbCBkYXRvIHkgZGV2dWVsdmUgdW4gbnVtZXJpY28sIGNvbW8gZXN0YSBkZXZvbHZpZW5kbyBsYSBjb2x1bW5hIGVzbyBlcyB1biB2ZWN0b3IuDQpgYGB7cn0NCmNsdXN0ZXJfeCA8LQ0KICBuZXdfY2x1c3RlcnMgJT4lIHB1bGwoYXZnX3gpDQpjbHVzdGVyX3kgPC0NCiAgbmV3X2NsdXN0ZXJzICU+JSBwdWxsKGF2Z195KQ0KYGBgDQoNCiMjIyMgUnVuIDEgIyMjIw0KDQpgYGB7cn0NCmRmIDwtIHRpYmJsZShwZXRhbF9sZW5ndGggPSBpcmlzJFBldGFsLkxlbmd0aCwNCiAgICAgICAgICAgICBwZXRhbF93aWR0aCA9IGlyaXMkUGV0YWwuV2lkdGgpDQpgYGANCg0KYGBge3J9DQpkZiA8LSANCiAgZGYgJT4lIA0KICAgIG11dGF0ZShkaXN0YW5jZV8xID0gc3FydCgocGV0YWxfbGVuZ3RoIC0gY2x1c3Rlcl94WzFdKV4yICsgKHBldGFsX3dpZHRoIC0gY2x1c3Rlcl95WzFdKV4yKSwNCiAgICAgICAgICAgZGlzdGFuY2VfMiA9IHNxcnQoKHBldGFsX2xlbmd0aCAtIGNsdXN0ZXJfeFsyXSleMiArIChwZXRhbF93aWR0aCAtIGNsdXN0ZXJfeVsyXSleMiksDQogICAgICAgICAgIGNsdXN0ZXIgPSBpZmVsc2UoZGlzdGFuY2VfMSA8IGRpc3RhbmNlXzIsICJibHVlIiwgInJlZCIpKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChkZiRwZXRhbF9sZW5ndGgsIGRmJHBldGFsX3dpZHRoLCBjb2w9ZGYkY2x1c3RlciwNCiAgICAgcGNoID0gMTYsDQogICAgIHhsYWIgPSAiUGV0YWwgbGVuZ3RoIiwNCiAgICAgeWxhYiA9ICJQZXRhbCB3aWR0aCIsDQogICAgIG1haW4gPSAiSXJpcyBEYXRhc2V0IikNCnBvaW50cyhjbHVzdGVyX3gsIGNsdXN0ZXJfeSwgY29sID0gYygnYmx1ZScsICdyZWQnKSwgcGNoID0gMTcsIGNleCA9IDIpDQpgYGANCg0KYGBge3J9DQpuZXdfY2x1c3RlcnMgPC0gDQogIGRmICU+JSANCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIA0KICBzdW1tYXJpc2UoYXZnX3ggPSBtZWFuKHBldGFsX2xlbmd0aCksDQogICAgICAgICAgICBhdmdfeSA9IG1lYW4ocGV0YWxfd2lkdGgpKSAlPiUgDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCmBgYHtyfQ0KY2x1c3Rlcl94IDwtDQogIG5ld19jbHVzdGVycyAlPiUgcHVsbChhdmdfeCkNCmNsdXN0ZXJfeSA8LQ0KICBuZXdfY2x1c3RlcnMgJT4lIHB1bGwoYXZnX3kpDQpgYGANCg0KIyMjIyBSdW4gMiAjIyMjDQoNCmBgYHtyfQ0KZGYgPC0gdGliYmxlKHBldGFsX2xlbmd0aCA9IGlyaXMkUGV0YWwuTGVuZ3RoLA0KICAgICAgICAgICAgIHBldGFsX3dpZHRoID0gaXJpcyRQZXRhbC5XaWR0aCkNCmBgYA0KDQpgYGB7cn0NCmRmIDwtIA0KICBkZiAlPiUgDQogICAgbXV0YXRlKGRpc3RhbmNlXzEgPSBzcXJ0KChwZXRhbF9sZW5ndGggLSBjbHVzdGVyX3hbMV0pXjIgKyAocGV0YWxfd2lkdGggLSBjbHVzdGVyX3lbMV0pXjIpLA0KICAgICAgICAgICBkaXN0YW5jZV8yID0gc3FydCgocGV0YWxfbGVuZ3RoIC0gY2x1c3Rlcl94WzJdKV4yICsgKHBldGFsX3dpZHRoIC0gY2x1c3Rlcl95WzJdKV4yKSwNCiAgICAgICAgICAgY2x1c3RlciA9IGlmZWxzZShkaXN0YW5jZV8xIDwgZGlzdGFuY2VfMiwgImJsdWUiLCAicmVkIikpDQpgYGANCg0KYGBge3J9DQpwbG90KGRmJHBldGFsX2xlbmd0aCwgZGYkcGV0YWxfd2lkdGgsIGNvbD1kZiRjbHVzdGVyLA0KICAgICBwY2ggPSAxNiwNCiAgICAgeGxhYiA9ICJQZXRhbCBsZW5ndGgiLA0KICAgICB5bGFiID0gIlBldGFsIHdpZHRoIiwNCiAgICAgbWFpbiA9ICJJcmlzIERhdGFzZXQiKQ0KcG9pbnRzKGNsdXN0ZXJfeCwgY2x1c3Rlcl95LCBjb2wgPSBjKCdibHVlJywgJ3JlZCcpLCBwY2ggPSAxNywgY2V4ID0gMikNCmBgYA0KDQpgYGB7cn0NCm5ld19jbHVzdGVycyA8LSANCiAgZGYgJT4lIA0KICBncm91cF9ieShjbHVzdGVyKSAlPiUgDQogIHN1bW1hcmlzZShhdmdfeCA9IG1lYW4ocGV0YWxfbGVuZ3RoKSwNCiAgICAgICAgICAgIGF2Z195ID0gbWVhbihwZXRhbF93aWR0aCkpICU+JSANCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3J9DQpjbHVzdGVyX3ggPC0NCiAgbmV3X2NsdXN0ZXJzICU+JSBwdWxsKGF2Z194KQ0KY2x1c3Rlcl95IDwtDQogIG5ld19jbHVzdGVycyAlPiUgcHVsbChhdmdfeSkNCmBgYA0KDQojIyMjIFJ1biAzICMjIyMNCg0KYGBge3J9DQpkZiA8LSB0aWJibGUocGV0YWxfbGVuZ3RoID0gaXJpcyRQZXRhbC5MZW5ndGgsDQogICAgICAgICAgICAgcGV0YWxfd2lkdGggPSBpcmlzJFBldGFsLldpZHRoKQ0KYGBgDQoNCmBgYHtyfQ0KZGYgPC0gDQogIGRmICU+JSANCiAgICBtdXRhdGUoZGlzdGFuY2VfMSA9IHNxcnQoKHBldGFsX2xlbmd0aCAtIGNsdXN0ZXJfeFsxXSleMiArIChwZXRhbF93aWR0aCAtIGNsdXN0ZXJfeVsxXSleMiksDQogICAgICAgICAgIGRpc3RhbmNlXzIgPSBzcXJ0KChwZXRhbF9sZW5ndGggLSBjbHVzdGVyX3hbMl0pXjIgKyAocGV0YWxfd2lkdGggLSBjbHVzdGVyX3lbMl0pXjIpLA0KICAgICAgICAgICBjbHVzdGVyID0gaWZlbHNlKGRpc3RhbmNlXzEgPCBkaXN0YW5jZV8yLCAiYmx1ZSIsICJyZWQiKSkNCmBgYA0KDQpgYGB7cn0NCnBsb3QoZGYkcGV0YWxfbGVuZ3RoLCBkZiRwZXRhbF93aWR0aCwgY29sPWRmJGNsdXN0ZXIsDQogICAgIHBjaCA9IDE2LA0KICAgICB4bGFiID0gIlBldGFsIGxlbmd0aCIsDQogICAgIHlsYWIgPSAiUGV0YWwgd2lkdGgiLA0KICAgICBtYWluID0gIklyaXMgRGF0YXNldCIpDQpwb2ludHMoY2x1c3Rlcl94LCBjbHVzdGVyX3ksIGNvbCA9IGMoJ2JsdWUnLCAncmVkJyksIHBjaCA9IDE3LCBjZXggPSAyKQ0KYGBgDQoNCmBgYHtyfQ0KbmV3X2NsdXN0ZXJzIDwtIA0KICBkZiAlPiUgDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSANCiAgc3VtbWFyaXNlKGF2Z194ID0gbWVhbihwZXRhbF9sZW5ndGgpLA0KICAgICAgICAgICAgYXZnX3kgPSBtZWFuKHBldGFsX3dpZHRoKSkgJT4lIA0KICB1bmdyb3VwKCkNCmBgYA0KDQpgYGB7cn0NCmNsdXN0ZXJfeCA8LQ0KICBuZXdfY2x1c3RlcnMgJT4lIHB1bGwoYXZnX3gpDQpjbHVzdGVyX3kgPC0NCiAgbmV3X2NsdXN0ZXJzICU+JSBwdWxsKGF2Z195KQ0KYGBgDQoNCiMjIyMgUnVuIDQgIyMjIw0KDQpgYGB7cn0NCmRmIDwtIHRpYmJsZShwZXRhbF9sZW5ndGggPSBpcmlzJFBldGFsLkxlbmd0aCwNCiAgICAgICAgICAgICBwZXRhbF93aWR0aCA9IGlyaXMkUGV0YWwuV2lkdGgpDQpgYGANCg0KYGBge3J9DQpkZiA8LSANCiAgZGYgJT4lIA0KICAgIG11dGF0ZShkaXN0YW5jZV8xID0gc3FydCgocGV0YWxfbGVuZ3RoIC0gY2x1c3Rlcl94WzFdKV4yICsgKHBldGFsX3dpZHRoIC0gY2x1c3Rlcl95WzFdKV4yKSwNCiAgICAgICAgICAgZGlzdGFuY2VfMiA9IHNxcnQoKHBldGFsX2xlbmd0aCAtIGNsdXN0ZXJfeFsyXSleMiArIChwZXRhbF93aWR0aCAtIGNsdXN0ZXJfeVsyXSleMiksDQogICAgICAgICAgIGNsdXN0ZXIgPSBpZmVsc2UoZGlzdGFuY2VfMSA8IGRpc3RhbmNlXzIsICJibHVlIiwgInJlZCIpKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChkZiRwZXRhbF9sZW5ndGgsIGRmJHBldGFsX3dpZHRoLCBjb2w9ZGYkY2x1c3RlciwNCiAgICAgcGNoID0gMTYsDQogICAgIHhsYWIgPSAiUGV0YWwgbGVuZ3RoIiwNCiAgICAgeWxhYiA9ICJQZXRhbCB3aWR0aCIsDQogICAgIG1haW4gPSAiSXJpcyBEYXRhc2V0IikNCnBvaW50cyhjbHVzdGVyX3gsIGNsdXN0ZXJfeSwgY29sID0gYygnYmx1ZScsICdyZWQnKSwgcGNoID0gMTcsIGNleCA9IDIpDQpgYGANCg0KYGBge3J9DQpuZXdfY2x1c3RlcnMgPC0gDQogIGRmICU+JSANCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIA0KICBzdW1tYXJpc2UoYXZnX3ggPSBtZWFuKHBldGFsX2xlbmd0aCksDQogICAgICAgICAgICBhdmdfeSA9IG1lYW4ocGV0YWxfd2lkdGgpKSAlPiUgDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCmBgYHtyfQ0KY2x1c3Rlcl94IDwtDQogIG5ld19jbHVzdGVycyAlPiUgcHVsbChhdmdfeCkNCmNsdXN0ZXJfeSA8LQ0KICBuZXdfY2x1c3RlcnMgJT4lIHB1bGwoYXZnX3kpDQpgYGANCg0KIyMjIyBSdW4gNSAjIyMjDQoNCmBgYHtyfQ0KZGYgPC0gdGliYmxlKHBldGFsX2xlbmd0aCA9IGlyaXMkUGV0YWwuTGVuZ3RoLA0KICAgICAgICAgICAgIHBldGFsX3dpZHRoID0gaXJpcyRQZXRhbC5XaWR0aCkNCmBgYA0KDQpgYGB7cn0NCmRmIDwtIA0KICBkZiAlPiUgDQogICAgbXV0YXRlKGRpc3RhbmNlXzEgPSBzcXJ0KChwZXRhbF9sZW5ndGggLSBjbHVzdGVyX3hbMV0pXjIgKyAocGV0YWxfd2lkdGggLSBjbHVzdGVyX3lbMV0pXjIpLA0KICAgICAgICAgICBkaXN0YW5jZV8yID0gc3FydCgocGV0YWxfbGVuZ3RoIC0gY2x1c3Rlcl94WzJdKV4yICsgKHBldGFsX3dpZHRoIC0gY2x1c3Rlcl95WzJdKV4yKSwNCiAgICAgICAgICAgY2x1c3RlciA9IGlmZWxzZShkaXN0YW5jZV8xIDwgZGlzdGFuY2VfMiwgImJsdWUiLCAicmVkIikpDQpgYGANCg0KYGBge3J9DQpwbG90KGRmJHBldGFsX2xlbmd0aCwgZGYkcGV0YWxfd2lkdGgsIGNvbD1kZiRjbHVzdGVyLA0KICAgICBwY2ggPSAxNiwNCiAgICAgeGxhYiA9ICJQZXRhbCBsZW5ndGgiLA0KICAgICB5bGFiID0gIlBldGFsIHdpZHRoIiwNCiAgICAgbWFpbiA9ICJJcmlzIERhdGFzZXQiKQ0KcG9pbnRzKGNsdXN0ZXJfeCwgY2x1c3Rlcl95LCBjb2wgPSBjKCdibHVlJywgJ3JlZCcpLCBwY2ggPSAxNywgY2V4ID0gMikNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=