
Contexto
Los datos utilizados son el resultado de un ánalisis químico de
vinos. Estos vinos fueron cultivados en la misma región de Italia pero
derivado de tres cultivares diferentes. El analisis determinó las
cantidades que se encuentran en cada uno de los tres tipos, basado en 13
componentes.
El vino es una bebida alcohólica obtenida de la uva mediante
fermentación alcohólica de su mosto o zumo; la fermentación se produce
por la acción de levaduras que transforman los azúcares del fruto en
alcohol etílico y anhídrido carbónico (MAPA, 2010). Acorde a esto, se
definen las siguientes variables:
- Alcohol: La cantidad de alcohol en el vino, medida en porcentaje de
volumen.
- Malic_Acid: Ácido málico presente en el vino, un ácido orgánico que
afecta la acidez y el sabor.
- Ash: La cantidad de cenizas en el vino, medida en gramos por
litro.
- Ash_Alcanity: La alcalinidad de las cenizas del vino, que puede
afectar la percepción del sabor y la estabilidad del vino.
- Magnesium: La cantidad de magnesio en el vino, un mineral que puede
influir en la fermentación y la estructura del vino.
- Total_Phenol: La cantidad total de compuestos fenólicos en el vino,
que contribuyen a su color, sabor y aroma.
- Flavanoids: La cantidad de flavonoides en el vino, compuestos que
pueden tener efectos antioxidantes y contribuir al sabor y aroma.
- Nonflavanoid_Phenols: La cantidad de fenoles no flavonoides en el
vino, que también pueden afectar el sabor y la calidad. -
Proanthocyanins: La cantidad de proantocianidinas en el vino, compuestos
que contribuyen a la astringencia y estabilidad del color.
- Color_Intensity: La intensidad del color del vino, medida en
unidades absorbancia a 420 nm.
- Hue: El tono del color del vino, que puede variar desde rojo hasta
amarillo. OD280: La absorbancia óptica a 280 nm, que puede proporcionar
información sobre la concentración de compuestos fenólicos y la madurez
del vino.
- Proline: La cantidad de prolinas en el vino, un aminoácido que puede
influir en el sabor y la estructura del vino.
Paso 1. Instalar paquetes y llamar librerias
#install.packages("cluster")
library(cluster)
#install.packages("ggplot2")
library(ggplot2)
#install.packages("data.table")
library(data.table)
#install.packages("factoextra")
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:data.table':
##
## between, first, last
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(readxl)
Paso 2. Obtener los datos
df <- read_excel("wine.xlsx")
df_scaled <- scale(df)
summary(df)
## Alcohol Malic_Acid Ash Ash_Alcanity
## Min. :11.03 Min. :0.740 Min. :1.360 Min. :10.60
## 1st Qu.:12.36 1st Qu.:1.603 1st Qu.:2.210 1st Qu.:17.20
## Median :13.05 Median :1.865 Median :2.360 Median :19.50
## Mean :13.00 Mean :2.336 Mean :2.367 Mean :19.49
## 3rd Qu.:13.68 3rd Qu.:3.083 3rd Qu.:2.558 3rd Qu.:21.50
## Max. :14.83 Max. :5.800 Max. :3.230 Max. :30.00
## Magnesium Total_Phenols Flavanoids Nonflavanoid_Phenols
## Min. : 70.00 Min. :0.980 Min. :0.340 Min. :0.1300
## 1st Qu.: 88.00 1st Qu.:1.742 1st Qu.:1.205 1st Qu.:0.2700
## Median : 98.00 Median :2.355 Median :2.135 Median :0.3400
## Mean : 99.74 Mean :2.295 Mean :2.029 Mean :0.3619
## 3rd Qu.:107.00 3rd Qu.:2.800 3rd Qu.:2.875 3rd Qu.:0.4375
## Max. :162.00 Max. :3.880 Max. :5.080 Max. :0.6600
## Proanthocyanins Color_Intensity Hue OD280
## Min. :0.410 Min. : 1.280 Min. :0.4800 Min. :1.270
## 1st Qu.:1.250 1st Qu.: 3.220 1st Qu.:0.7825 1st Qu.:1.938
## Median :1.555 Median : 4.690 Median :0.9650 Median :2.780
## Mean :1.591 Mean : 5.058 Mean :0.9574 Mean :2.612
## 3rd Qu.:1.950 3rd Qu.: 6.200 3rd Qu.:1.1200 3rd Qu.:3.170
## Max. :3.580 Max. :13.000 Max. :1.7100 Max. :4.000
## Proline
## Min. : 278.0
## 1st Qu.: 500.5
## Median : 673.5
## Mean : 746.9
## 3rd Qu.: 985.0
## Max. :1680.0
Paso 3. Cantidad de grupos
Inicialmente se puede poner el num que quieras. No obstante, después
de realizar la optimización se conoce cuál es la cantidad correcta de
segmentos.
set.seed(123)
wcss <- vector()
max_k <- 10 #se puede modificar
for (k in 1:max_k) {
kmeans_model <- kmeans(df_scaled, centers = k, nstart = 25)
wcss[k] <- kmeans_model$tot.withinss
}
# Graficar el método del codo
par(mar = c(4, 4, 1, 1))
plot(1:max_k, wcss, type = "b", xlab = "Numero de Clusters", ylab = "WCSS", main = "Metodo del Codo", pch = 19, frame = FALSE)

grupos <- 3
Paso 4. Generar los segmentos
segmentos <- kmeans(df_scaled, grupos)
segmentos # en los resultados x es el centroide del segmento
## K-means clustering with 3 clusters of sizes 51, 62, 65
##
## Cluster means:
## Alcohol Malic_Acid Ash Ash_Alcanity Magnesium Total_Phenols
## 1 0.1644436 0.8690954 0.1863726 0.5228924 -0.07526047 -0.97657548
## 2 0.8328826 -0.3029551 0.3636801 -0.6084749 0.57596208 0.88274724
## 3 -0.9234669 -0.3929331 -0.4931257 0.1701220 -0.49032869 -0.07576891
## Flavanoids Nonflavanoid_Phenols Proanthocyanins Color_Intensity Hue
## 1 -1.21182921 0.72402116 -0.77751312 0.9388902 -1.1615122
## 2 0.97506900 -0.56050853 0.57865427 0.1705823 0.4726504
## 3 0.02075402 -0.03343924 0.05810161 -0.8993770 0.4605046
## OD280 Proline
## 1 -1.2887761 -0.4059428
## 2 0.7770551 1.1220202
## 3 0.2700025 -0.7517257
##
## Clustering vector:
## [1] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [38] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 1 3 3 3 3 3 3 3 3 3 3 3 2
## [75] 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [112] 3 3 3 3 3 3 3 1 3 3 2 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [149] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##
## Within cluster sum of squares by cluster:
## [1] 326.3537 385.6983 558.6971
## (between_SS / total_SS = 44.8 %)
##
## Available components:
##
## [1] "cluster" "centers" "totss" "withinss" "tot.withinss"
## [6] "betweenss" "size" "iter" "ifault"
Paso 5. Asignar el grupo al que pertenece cada
observación
asignacion <- cbind(df, cluster = segmentos$cluster)
Paso 6. Graficar los clusters
fviz_cluster(segmentos, data=df_scaled)

Paso 7. Optimización de grupos
set.seed(123)
optimizacion <- clusGap(df_scaled, FUN=kmeans, nstart=1, K.max=10)
plot(optimizacion, xlab="Número de clusters k")

Paso 8. Comparar segmentos
promedio <- aggregate(asignacion, by=list(asignacion$cluster), FUN=mean)
promedio
## Group.1 Alcohol Malic_Acid Ash Ash_Alcanity Magnesium Total_Phenols
## 1 1 13.13412 3.307255 2.417647 21.24118 98.66667 1.683922
## 2 2 13.67677 1.997903 2.466290 17.46290 107.96774 2.847581
## 3 3 12.25092 1.897385 2.231231 20.06308 92.73846 2.247692
## Flavanoids Nonflavanoid_Phenols Proanthocyanins Color_Intensity Hue
## 1 0.8188235 0.4519608 1.145882 7.234706 0.6919608
## 2 3.0032258 0.2920968 1.922097 5.453548 1.0654839
## 3 2.0500000 0.3576923 1.624154 2.973077 1.0627077
## OD280 Proline cluster
## 1 1.696667 619.0588 1
## 2 3.163387 1100.2258 2
## 3 2.803385 510.1692 3
Conclusión
La segmentación es una herramienta útil para generar la agrupación de
datos y en este caso nos sirvio para identificar el cultivar
correspondiente a cada vino. Sin embargo, es imporante escalar los datos
antes de realizar un ánalisis para evitar que la magnitud de alguna
influya más en el proceso. Acorde a esto, se determinó que el númro de
cluster óptimo para este caso es 3, ya que despues este número
representa un punto de inflexión en la curva o el “codo”; es decir, los
clusters o números siguientes no minimizan la varianza dentro de los
clusters de forma suficientemente significativa como para justificar
agrupaciones adicionales en el conjunto de datos. Por último, se
establece una comparación de promedios entre segmentos con la función
aggregate que es útil para resumir datos en función de uno o más
factores.
LS0tCnRpdGxlOiAiU2VnbWVudGFjacOzbiAtIFZpbm8iCmF1dGhvcjogIkxpc3NldCBIZXJuw6FuZGV6IEEwMTI4NDYxMSB5IEF2cmlsIExvYmF0byBBMDA4MzMxMTMiCmRhdGU6ICIyMDI0LTAyLTE5IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCi0tLQoKIVtdKGh0dHBzOi8vc2FsdWRjb25sdXBhLmNvbS9tZWRpYS9pbWFnZXMvcmVkLXdpbmUtcG91cmluZy1mcm9tLWJvdHRsZS1nbGFzcy53aWR0aC0xOTIwLmpwZykKCiMgKipDb250ZXh0byoqCkxvcyBkYXRvcyB1dGlsaXphZG9zIHNvbiBlbCByZXN1bHRhZG8gZGUgdW4gw6FuYWxpc2lzIHF1w61taWNvIGRlIHZpbm9zLiBFc3RvcyB2aW5vcyBmdWVyb24gY3VsdGl2YWRvcyBlbiBsYSBtaXNtYSByZWdpw7NuIGRlIEl0YWxpYSBwZXJvIGRlcml2YWRvIGRlIHRyZXMgY3VsdGl2YXJlcyBkaWZlcmVudGVzLiBFbCBhbmFsaXNpcyBkZXRlcm1pbsOzIGxhcyBjYW50aWRhZGVzIHF1ZSBzZSBlbmN1ZW50cmFuIGVuIGNhZGEgdW5vIGRlIGxvcyB0cmVzIHRpcG9zLCBiYXNhZG8gZW4gMTMgY29tcG9uZW50ZXMuCgpFbCB2aW5vIGVzIHVuYSBiZWJpZGEgYWxjb2jDs2xpY2Egb2J0ZW5pZGEgZGUgbGEgdXZhIG1lZGlhbnRlIGZlcm1lbnRhY2nDs24gYWxjb2jDs2xpY2EgZGUgc3UgbW9zdG8gbyB6dW1vOyBsYSBmZXJtZW50YWNpw7NuIHNlIHByb2R1Y2UgcG9yIGxhIGFjY2nDs24gZGUgbGV2YWR1cmFzIHF1ZSB0cmFuc2Zvcm1hbiBsb3MgYXrDumNhcmVzIGRlbCBmcnV0byBlbiBhbGNvaG9sIGV0w61saWNvIHkgYW5ow61kcmlkbyBjYXJiw7NuaWNvIChNQVBBLCAyMDEwKS4gQWNvcmRlIGEgZXN0bywgc2UgZGVmaW5lbiBsYXMgc2lndWllbnRlcyB2YXJpYWJsZXM6ICAKCi0gQWxjb2hvbDogTGEgY2FudGlkYWQgZGUgYWxjb2hvbCBlbiBlbCB2aW5vLCBtZWRpZGEgZW4gcG9yY2VudGFqZSBkZSB2b2x1bWVuLiAgCi0gTWFsaWNfQWNpZDogw4FjaWRvIG3DoWxpY28gcHJlc2VudGUgZW4gZWwgdmlubywgdW4gw6FjaWRvIG9yZ8OhbmljbyBxdWUgYWZlY3RhIGxhIGFjaWRleiB5IGVsIHNhYm9yLiAgCi0gQXNoOiBMYSBjYW50aWRhZCBkZSBjZW5pemFzIGVuIGVsIHZpbm8sIG1lZGlkYSBlbiBncmFtb3MgcG9yIGxpdHJvLiAgCi0gQXNoX0FsY2FuaXR5OiBMYSBhbGNhbGluaWRhZCBkZSBsYXMgY2VuaXphcyBkZWwgdmlubywgcXVlIHB1ZWRlIGFmZWN0YXIgbGEgcGVyY2VwY2nDs24gZGVsIHNhYm9yIHkgbGEgZXN0YWJpbGlkYWQgZGVsIHZpbm8uICAKLSBNYWduZXNpdW06IExhIGNhbnRpZGFkIGRlIG1hZ25lc2lvIGVuIGVsIHZpbm8sIHVuIG1pbmVyYWwgcXVlIHB1ZWRlIGluZmx1aXIgZW4gbGEgZmVybWVudGFjacOzbiB5IGxhIGVzdHJ1Y3R1cmEgZGVsIHZpbm8uCi0gVG90YWxfUGhlbm9sOiBMYSBjYW50aWRhZCB0b3RhbCBkZSBjb21wdWVzdG9zIGZlbsOzbGljb3MgZW4gZWwgdmlubywgcXVlIGNvbnRyaWJ1eWVuIGEgc3UgY29sb3IsIHNhYm9yIHkgYXJvbWEuCi0gRmxhdmFub2lkczogTGEgY2FudGlkYWQgZGUgZmxhdm9ub2lkZXMgZW4gZWwgdmlubywgY29tcHVlc3RvcyBxdWUgcHVlZGVuIHRlbmVyIGVmZWN0b3MgYW50aW94aWRhbnRlcyB5IGNvbnRyaWJ1aXIgYWwgc2Fib3IgeSBhcm9tYS4gIAotIE5vbmZsYXZhbm9pZF9QaGVub2xzOiBMYSBjYW50aWRhZCBkZSBmZW5vbGVzIG5vIGZsYXZvbm9pZGVzIGVuIGVsIHZpbm8sIHF1ZSB0YW1iacOpbiBwdWVkZW4gYWZlY3RhciBlbCBzYWJvciB5IGxhIGNhbGlkYWQuIC0gUHJvYW50aG9jeWFuaW5zOiBMYSBjYW50aWRhZCBkZSBwcm9hbnRvY2lhbmlkaW5hcyBlbiBlbCB2aW5vLCBjb21wdWVzdG9zIHF1ZSBjb250cmlidXllbiBhIGxhIGFzdHJpbmdlbmNpYSB5IGVzdGFiaWxpZGFkIGRlbCBjb2xvci4gIAotIENvbG9yX0ludGVuc2l0eTogTGEgaW50ZW5zaWRhZCBkZWwgY29sb3IgZGVsIHZpbm8sIG1lZGlkYSBlbiB1bmlkYWRlcyBhYnNvcmJhbmNpYSBhIDQyMCBubS4gIAotIEh1ZTogRWwgdG9ubyBkZWwgY29sb3IgZGVsIHZpbm8sIHF1ZSBwdWVkZSB2YXJpYXIgZGVzZGUgcm9qbyBoYXN0YSBhbWFyaWxsby4KT0QyODA6IExhIGFic29yYmFuY2lhIMOzcHRpY2EgYSAyODAgbm0sIHF1ZSBwdWVkZSBwcm9wb3JjaW9uYXIgaW5mb3JtYWNpw7NuIHNvYnJlIGxhIGNvbmNlbnRyYWNpw7NuIGRlIGNvbXB1ZXN0b3MgZmVuw7NsaWNvcyB5IGxhIG1hZHVyZXogZGVsIHZpbm8uICAKLSBQcm9saW5lOiBMYSBjYW50aWRhZCBkZSBwcm9saW5hcyBlbiBlbCB2aW5vLCB1biBhbWlub8OhY2lkbyBxdWUgcHVlZGUgaW5mbHVpciBlbiBlbCBzYWJvciB5IGxhIGVzdHJ1Y3R1cmEgZGVsIHZpbm8uCgojICoqUGFzbyAxLioqIEluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcmlhcwpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKQpsaWJyYXJ5KGNsdXN0ZXIpCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKbGlicmFyeShnZ3Bsb3QyKQojaW5zdGFsbC5wYWNrYWdlcygiZGF0YS50YWJsZSIpCmxpYnJhcnkoZGF0YS50YWJsZSkKI2luc3RhbGwucGFja2FnZXMoImZhY3RvZXh0cmEiKQpsaWJyYXJ5KGZhY3RvZXh0cmEpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVhZHhsKQpgYGAKCiMgKipQYXNvIDIuKiogT2J0ZW5lciBsb3MgZGF0b3MKYGBge3J9CmRmIDwtIHJlYWRfZXhjZWwoIndpbmUueGxzeCIpCmRmX3NjYWxlZCA8LSBzY2FsZShkZikKYGBgCmBgYHtyfQpzdW1tYXJ5KGRmKQpgYGAKCgojICoqUGFzbyAzLioqIENhbnRpZGFkIGRlIGdydXBvcwpJbmljaWFsbWVudGUgc2UgcHVlZGUgcG9uZXIgZWwgbnVtIHF1ZSBxdWllcmFzLiBObyBvYnN0YW50ZSwgZGVzcHXDqXMgZGUgcmVhbGl6YXIgbGEgb3B0aW1pemFjacOzbiBzZSBjb25vY2UgY3XDoWwgZXMgbGEgY2FudGlkYWQgY29ycmVjdGEgZGUgc2VnbWVudG9zLgpgYGB7cn0KCnNldC5zZWVkKDEyMykKd2NzcyA8LSB2ZWN0b3IoKQptYXhfayA8LSAxMCAjc2UgcHVlZGUgbW9kaWZpY2FyCmZvciAoayBpbiAxOm1heF9rKSB7CiAga21lYW5zX21vZGVsIDwtIGttZWFucyhkZl9zY2FsZWQsIGNlbnRlcnMgPSBrLCBuc3RhcnQgPSAyNSkKICB3Y3NzW2tdIDwtIGttZWFuc19tb2RlbCR0b3Qud2l0aGluc3MKfQoKIyBHcmFmaWNhciBlbCBtw6l0b2RvIGRlbCBjb2RvCnBhcihtYXIgPSBjKDQsIDQsIDEsIDEpKQpwbG90KDE6bWF4X2ssIHdjc3MsIHR5cGUgPSAiYiIsIHhsYWIgPSAiTnVtZXJvIGRlIENsdXN0ZXJzIiwgeWxhYiA9ICJXQ1NTIiwgbWFpbiA9ICJNZXRvZG8gZGVsIENvZG8iLCBwY2ggPSAxOSwgZnJhbWUgPSBGQUxTRSkKYGBgCgpgYGB7cn0KZ3J1cG9zIDwtIDMKYGBgCgoKIyAqKlBhc28gNC4qKiBHZW5lcmFyIGxvcyBzZWdtZW50b3MKYGBge3J9CnNlZ21lbnRvcyA8LSBrbWVhbnMoZGZfc2NhbGVkLCBncnVwb3MpCnNlZ21lbnRvcyAjIGVuIGxvcyByZXN1bHRhZG9zIHggZXMgZWwgY2VudHJvaWRlIGRlbCBzZWdtZW50bwpgYGAKCiMgKipQYXNvIDUuKiogQXNpZ25hciBlbCBncnVwbyBhbCBxdWUgcGVydGVuZWNlIGNhZGEgb2JzZXJ2YWNpw7NuCmBgYHtyfQphc2lnbmFjaW9uIDwtIGNiaW5kKGRmLCBjbHVzdGVyID0gc2VnbWVudG9zJGNsdXN0ZXIpCmBgYAoKIyAqKlBhc28gNi4qKiBHcmFmaWNhciBsb3MgY2x1c3RlcnMKYGBge3J9CmZ2aXpfY2x1c3RlcihzZWdtZW50b3MsIGRhdGE9ZGZfc2NhbGVkKQpgYGAKCgojICoqUGFzbyA3LioqIE9wdGltaXphY2nDs24gZGUgZ3J1cG9zIApgYGB7cn0Kc2V0LnNlZWQoMTIzKQpvcHRpbWl6YWNpb24gPC0gY2x1c0dhcChkZl9zY2FsZWQsIEZVTj1rbWVhbnMsIG5zdGFydD0xLCBLLm1heD0xMCkKcGxvdChvcHRpbWl6YWNpb24sIHhsYWI9Ik7Dum1lcm8gZGUgY2x1c3RlcnMgayIpCmBgYAoKIyAqKlBhc28gOC4qKiBDb21wYXJhciBzZWdtZW50b3MKYGBge3J9CnByb21lZGlvIDwtIGFnZ3JlZ2F0ZShhc2lnbmFjaW9uLCBieT1saXN0KGFzaWduYWNpb24kY2x1c3RlciksIEZVTj1tZWFuKQpwcm9tZWRpbwpgYGAKCgojICoqQ29uY2x1c2nDs24qKgpMYSBzZWdtZW50YWNpw7NuIGVzIHVuYSBoZXJyYW1pZW50YSDDunRpbCBwYXJhIGdlbmVyYXIgbGEgYWdydXBhY2nDs24gZGUgZGF0b3MgeSBlbiBlc3RlIGNhc28gbm9zIHNpcnZpbyBwYXJhIGlkZW50aWZpY2FyIGVsIGN1bHRpdmFyIGNvcnJlc3BvbmRpZW50ZSBhIGNhZGEgdmluby4gU2luIGVtYmFyZ28sIGVzIGltcG9yYW50ZSBlc2NhbGFyIGxvcyBkYXRvcyBhbnRlcyBkZSByZWFsaXphciB1biDDoW5hbGlzaXMgcGFyYSBldml0YXIgcXVlIGxhIG1hZ25pdHVkIGRlIGFsZ3VuYSBpbmZsdXlhIG3DoXMgZW4gZWwgcHJvY2Vzby4gQWNvcmRlIGEgZXN0bywgc2UgZGV0ZXJtaW7DsyBxdWUgZWwgbsO6bXJvIGRlIGNsdXN0ZXIgw7NwdGltbyBwYXJhIGVzdGUgY2FzbyBlcyAzLCB5YSBxdWUgZGVzcHVlcyBlc3RlIG7Dum1lcm8gcmVwcmVzZW50YSB1biBwdW50byBkZSBpbmZsZXhpw7NuIGVuIGxhIGN1cnZhIG8gZWwgImNvZG8iOyBlcyBkZWNpciwgbG9zIGNsdXN0ZXJzIG8gbsO6bWVyb3Mgc2lndWllbnRlcyBubyBtaW5pbWl6YW4gbGEgdmFyaWFuemEgZGVudHJvIGRlIGxvcyBjbHVzdGVycyBkZSBmb3JtYSBzdWZpY2llbnRlbWVudGUgc2lnbmlmaWNhdGl2YSBjb21vIHBhcmEganVzdGlmaWNhciBhZ3J1cGFjaW9uZXMgYWRpY2lvbmFsZXMgZW4gZWwgY29uanVudG8gZGUgZGF0b3MuIFBvciDDumx0aW1vLCBzZSBlc3RhYmxlY2UgdW5hIGNvbXBhcmFjacOzbiBkZSBwcm9tZWRpb3MgZW50cmUgc2VnbWVudG9zIGNvbiBsYSBmdW5jacOzbiBhZ2dyZWdhdGUgcXVlIGVzIMO6dGlsIHBhcmEgcmVzdW1pciBkYXRvcyBlbiBmdW5jacOzbiBkZSB1bm8gbyBtw6FzIGZhY3RvcmVzLgoKIyAqKlJlZmVyZW5jaWFzKioKW1IgZm9yIERhdGEgU2NpZW5jZSAoMmRhIGVkaWNpb24pXShodHRwczovL2VzLnI0ZHMuaGFkbGV5Lm56LykKCltXaW5lIGRhdGFzZXRdKGh0dHBzOi8vc2VhcmNoLnItcHJvamVjdC5vcmcvQ1JBTi9yZWZtYW5zL0hEY2xhc3NpZi9odG1sL3dpbmUuaHRtbCkgCgpNQVBBLiAoMjAxMCkuIE1pbmlzdGVyaW8gZGUgU2Vydmljb3MgZGUgQWdyaWN1bHR1cmEsIFBlc2NhIHkgQWxpbWVudGFjaW9uLiBSZWN1cGVyYWRvIGRlOiBodHRwczovL3d3dy5tYXBhLmdvYi5lcy9lcy9taW5pc3RlcmlvL3NlcnZpY2lvcy9pbmZvcm1hY2lvbi92aW5vX3RjbTMwLTEwMjg2OC5wZGYK