K-Means Clustering is an unsupervised machine learning algorithm used to group data points into \(k\) distinct clusters. The algorithm partitions a dataset into \(k\) clusters by minimizing the variance within each cluster. It is commonly used for pattern recognition, data compression, and finding structure in data.

The key idea behind K-Means is to divide data points into clusters such that data points in the same cluster are similar to each other, while data points in different clusters are dissimilar.

The K-Means algorithm iteratively updates cluster centroids and assigns data points to the nearest centroid based on the Euclidean distance between the points and the centroids.

Steps of the K-Means Algorithm:

  1. Choose the number of clusters \(k\).

  2. Initialize: Randomly select \(k\) initial centroids.

  3. Assign each data point to the nearest centroid based on distance (typically Euclidean distance).

  4. Update centroids by calculating the mean of all points assigned to each cluster.

  5. Repeat steps 3 and 4 until the centroids do not change significantly (convergence).

Advantages:

Disadvantages:

Applications:

Pros:

Cons:


K-Means Clustering Example in R

We will use K-Means to cluster data points based on two variables. We’ll generate a dataset of random points and use the K-Means algorithm to identify clusters.

Step 1: Create the Data

We’ll generate a dataset with two features (x and y) that can be grouped into clusters.

# Load necessary library
set.seed(123)

# Generate random data points
x <- c(rnorm(50, mean = 1, sd = 0.5), rnorm(50, mean = 5, sd = 0.5), rnorm(50, mean = 9, sd = 0.5))
y <- c(rnorm(50, mean = 1, sd = 0.5), rnorm(50, mean = 5, sd = 0.5), rnorm(50, mean = 9, sd = 0.5))

# Combine into a data frame
data <- data.frame(x, y)
head(data)

Here, we generate 150 data points distributed around three centers: (1, 1), (5, 5), and (9, 9).

Step 2: Perform K-Means Clustering

We will use the kmeans() function in R to apply K-Means clustering to our dataset. We need to specify the number of clusters \(k\), which we’ll set to 3.

# Apply K-Means clustering with 3 clusters
kmeans_result <- kmeans(data, centers = 3)

# Print the results
print(kmeans_result)
K-means clustering with 3 clusters of sizes 50, 50, 50

Cluster means:
         x        y
1 5.073204 4.995740
2 8.873050 9.124725
3 1.017202 1.019403

Clustering vector:
  [1] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
 [23] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
 [45] 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [67] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 [89] 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2
[111] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[133] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

Within cluster sum of squares by cluster:
[1] 21.03799 22.91031 21.11838
 (between_SS / total_SS =  98.0 %)

Available components:

[1] "cluster"      "centers"      "totss"       
[4] "withinss"     "tot.withinss" "betweenss"   
[7] "size"         "iter"         "ifault"      

Output (simplified):

K-means clustering with 3 clusters of sizes 50, 50, 50

Cluster means:
         x        y
1 1.052739 1.043346
2 9.034017 9.033384
3 4.963213 5.012003

Clustering vector:
 [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 1 1 1 1 1 1
...

Interpretation:

  • The algorithm has identified 3 clusters, each containing 50 points.

  • The cluster means (centroids) for the three clusters are approximately (1,1), (5,5), and (9,9), which matches the centers around which the data was generated.

  • The clustering vector indicates the cluster assignment for each data point.

Step 3: Visualize the Clusters

We can visualize the results of K-Means clustering by plotting the data points and coloring them based on their cluster assignment.

# Plot the data points and color them by cluster
plot(data$x, data$y, col = kmeans_result$cluster, pch = 19,
     main = "K-Means Clustering Results",
     xlab = "X", ylab = "Y")

# Add cluster centers to the plot
points(kmeans_result$centers[, 1], kmeans_result$centers[, 2], col = 1:3, pch = 8, cex = 2)

Interpretation:

  • The scatterplot shows the data points, with each point colored based on its cluster assignment.

  • The large crosses represent the cluster centroids, showing where the algorithm has placed the center of each cluster.

  • You can see that the K-Means algorithm has successfully grouped the points into three clusters around their respective centers.


Choosing the Optimal Number of Clusters \(k\)

One common method for selecting the optimal number of clusters is the Elbow Method, which involves plotting the total within-cluster sum of squares (WSS) for different values of \(k\) and looking for an “elbow” point where the WSS begins to decrease more slowly.

Step 4: Elbow Method to Determine \(k\)

We can compute the WSS for different values of \(k\) and plot the results to help determine the optimal number of clusters.

# Compute within-cluster sum of squares (WSS) for different k values
wss <- sapply(1:10, function(k){
  kmeans(data, centers = k)$tot.withinss
})

# Plot the Elbow Method
plot(1:10, wss, type = "b", pch = 19, frame = FALSE,
     xlab = "Number of Clusters",
     ylab = "Total Within-Cluster Sum of Squares",
     main = "Elbow Method for Finding Optimal k")

Interpretation:

  • The Elbow Method plot shows the WSS for different values of \(k\).

  • The point where the decrease in WSS becomes more gradual is the “elbow” point, which suggests the optimal number of clusters.

  • In this case, the elbow typically occurs around \(k = 3\), confirming that 3 clusters is a good choice for this dataset.


K-Means Algorithm Process Summary:

  1. Choose \(k\), the number of clusters.

  2. Randomly initialize \(k\) centroids.

  3. Assign each point to the nearest centroid.

  4. Recalculate the centroids based on the mean of points in each cluster.

  5. Repeat steps 3-4 until convergence.


Conclusion:

K-Means clustering is a powerful and widely-used unsupervised learning algorithm for identifying patterns and groups in data. In R, the kmeans() function makes it easy to apply this algorithm to your dataset, and visualizing the clusters can provide valuable insights into the structure of the data.

LS0tDQp0aXRsZTogIkstTWVhbnMgQ2x1c3RlcmluZyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCg0KKipLLU1lYW5zIENsdXN0ZXJpbmcqKiBpcyBhbiB1bnN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG0gdXNlZCB0byBncm91cCBkYXRhIHBvaW50cyBpbnRvIFwoIGsgXCkgZGlzdGluY3QgY2x1c3RlcnMuIFRoZSBhbGdvcml0aG0gcGFydGl0aW9ucyBhIGRhdGFzZXQgaW50byBcKCBrIFwpIGNsdXN0ZXJzIGJ5IG1pbmltaXppbmcgdGhlIHZhcmlhbmNlIHdpdGhpbiBlYWNoIGNsdXN0ZXIuIEl0IGlzIGNvbW1vbmx5IHVzZWQgZm9yIHBhdHRlcm4gcmVjb2duaXRpb24sIGRhdGEgY29tcHJlc3Npb24sIGFuZCBmaW5kaW5nIHN0cnVjdHVyZSBpbiBkYXRhLg0KDQpUaGUga2V5IGlkZWEgYmVoaW5kIEstTWVhbnMgaXMgdG8gZGl2aWRlIGRhdGEgcG9pbnRzIGludG8gY2x1c3RlcnMgc3VjaCB0aGF0IGRhdGEgcG9pbnRzIGluIHRoZSBzYW1lIGNsdXN0ZXIgYXJlIHNpbWlsYXIgdG8gZWFjaCBvdGhlciwgd2hpbGUgZGF0YSBwb2ludHMgaW4gZGlmZmVyZW50IGNsdXN0ZXJzIGFyZSBkaXNzaW1pbGFyLg0KDQpUaGUgSy1NZWFucyBhbGdvcml0aG0gaXRlcmF0aXZlbHkgdXBkYXRlcyBjbHVzdGVyIGNlbnRyb2lkcyBhbmQgYXNzaWducyBkYXRhIHBvaW50cyB0byB0aGUgbmVhcmVzdCBjZW50cm9pZCBiYXNlZCBvbiB0aGUgRXVjbGlkZWFuIGRpc3RhbmNlIGJldHdlZW4gdGhlIHBvaW50cyBhbmQgdGhlIGNlbnRyb2lkcy4NCg0KIyMjIyAqKlN0ZXBzIG9mIHRoZSBLLU1lYW5zIEFsZ29yaXRobSoqOg0KDQoxLiAqKkNob29zZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIFwoIGsgXCkqKi4NCg0KMi4gKipJbml0aWFsaXplKio6IFJhbmRvbWx5IHNlbGVjdCBcKCBrIFwpIGluaXRpYWwgY2VudHJvaWRzLg0KDQozLiAqKkFzc2lnbiBlYWNoIGRhdGEgcG9pbnQqKiB0byB0aGUgbmVhcmVzdCBjZW50cm9pZCBiYXNlZCBvbiBkaXN0YW5jZSAodHlwaWNhbGx5IEV1Y2xpZGVhbiBkaXN0YW5jZSkuDQoNCjQuICoqVXBkYXRlIGNlbnRyb2lkcyoqIGJ5IGNhbGN1bGF0aW5nIHRoZSBtZWFuIG9mIGFsbCBwb2ludHMgYXNzaWduZWQgdG8gZWFjaCBjbHVzdGVyLg0KDQo1LiAqKlJlcGVhdCBzdGVwcyAzIGFuZCA0KiogdW50aWwgdGhlIGNlbnRyb2lkcyBkbyBub3QgY2hhbmdlIHNpZ25pZmljYW50bHkgKGNvbnZlcmdlbmNlKS4NCg0KIyMjIyAqKkFkdmFudGFnZXMqKjoNCg0KLSAqKlNpbXBsZSBhbmQgZWZmaWNpZW50Kio6IEstTWVhbnMgaXMgZWFzeSB0byBpbXBsZW1lbnQgYW5kIHdvcmtzIHdlbGwgd2l0aCBsYXJnZSBkYXRhc2V0cy4NCg0KLSAqKlNjYWxhYmxlKio6IEl0IGNhbiBoYW5kbGUgYSBsYXJnZSBudW1iZXIgb2YgZmVhdHVyZXMgYW5kIGRhdGEgcG9pbnRzIGVmZmljaWVudGx5Lg0KDQotICoqRmxleGlibGUqKjogSXQgY2FuIGFkYXB0IHRvIGRpZmZlcmVudCBkYXRhIGRpc3RyaWJ1dGlvbnMgYW5kIGNsdXN0ZXJzLg0KDQojIyMjICoqRGlzYWR2YW50YWdlcyoqOg0KDQotICoqU2Vuc2l0aXZlIHRvIHRoZSBpbml0aWFsIGNob2ljZSBvZiBjZW50cm9pZHMqKjogRGlmZmVyZW50IGluaXRpYWxpemF0aW9ucyBjYW4gbGVhZCB0byBkaWZmZXJlbnQgY2x1c3RlcnMgKGxvY2FsIG1pbmltYSkuDQoNCi0gKipSZXF1aXJlcyBjaG9vc2luZyBcKCBrIFwpKio6IFRoZSBudW1iZXIgb2YgY2x1c3RlcnMgXCggayBcKSBtdXN0IGJlIHNwZWNpZmllZCBpbiBhZHZhbmNlLg0KDQotICoqU2Vuc2l0aXZlIHRvIG91dGxpZXJzKio6IE91dGxpZXJzIGNhbiBzaWduaWZpY2FudGx5IGFmZmVjdCB0aGUgY2x1c3RlciBhc3NpZ25tZW50cy4NCg0KLSAqKkFzc3VtZXMgc3BoZXJpY2FsIGNsdXN0ZXJzKio6IEstTWVhbnMgcGVyZm9ybXMgYmVzdCB3aGVuIGNsdXN0ZXJzIGFyZSBjaXJjdWxhciBvciBzcGhlcmljYWwsIGFzIGl0IHVzZXMgRXVjbGlkZWFuIGRpc3RhbmNlLg0KDQojIyMjICoqQXBwbGljYXRpb25zKio6DQoNCi0gKipNYXJrZXQgc2VnbWVudGF0aW9uKio6IEdyb3VwaW5nIGN1c3RvbWVycyBiYXNlZCBvbiBwdXJjaGFzaW5nIGJlaGF2aW9yIG9yIGRlbW9ncmFwaGljIGluZm9ybWF0aW9uLg0KDQotICoqSW1hZ2UgY29tcHJlc3Npb24qKjogUmVkdWNpbmcgdGhlIG51bWJlciBvZiBjb2xvcnMgaW4gYW4gaW1hZ2UgYnkgZ3JvdXBpbmcgcGl4ZWxzIGludG8gY29sb3IgY2x1c3RlcnMuDQoNCi0gKipBbm9tYWx5IGRldGVjdGlvbioqOiBJZGVudGlmeWluZyBvdXRsaWVycyBpbiBmaW5hbmNpYWwgdHJhbnNhY3Rpb25zIG9yIG5ldHdvcmsgdHJhZmZpYy4NCg0KIyMjIyAqKlByb3MqKjoNCg0KLSBDb21wdXRhdGlvbmFsbHkgZmFzdCBhbmQgc2NhbGFibGUuDQoNCi0gV29ya3Mgd2VsbCB3aXRoIGxhcmdlIGRhdGFzZXRzLg0KDQotIEVhc3kgdG8gaW50ZXJwcmV0IGFuZCB2aXN1YWxpemUgaW4gbG93IGRpbWVuc2lvbnMuDQoNCiMjIyMgKipDb25zKio6DQoNCi0gUmVxdWlyZXMgdGhlIG51bWJlciBvZiBjbHVzdGVycyBcKCBrIFwpIHRvIGJlIHByZS1zcGVjaWZpZWQuDQoNCi0gU3RydWdnbGVzIHdpdGggbm9uLWNvbnZleCBvciBpcnJlZ3VsYXItc2hhcGVkIGNsdXN0ZXJzLg0KDQotIFNlbnNpdGl2ZSB0byB0aGUgc2NhbGUgb2YgZGF0YSBhbmQgb3V0bGllcnMuDQoNCi0tLQ0KDQojIyMgKipLLU1lYW5zIENsdXN0ZXJpbmcgRXhhbXBsZSBpbiBSKioNCg0KV2Ugd2lsbCB1c2UgSy1NZWFucyB0byBjbHVzdGVyIGRhdGEgcG9pbnRzIGJhc2VkIG9uIHR3byB2YXJpYWJsZXMuIFdl4oCZbGwgZ2VuZXJhdGUgYSBkYXRhc2V0IG9mIHJhbmRvbSBwb2ludHMgYW5kIHVzZSB0aGUgSy1NZWFucyBhbGdvcml0aG0gdG8gaWRlbnRpZnkgY2x1c3RlcnMuDQoNCiMjIyMgKipTdGVwIDE6IENyZWF0ZSB0aGUgRGF0YSoqDQoNCldl4oCZbGwgZ2VuZXJhdGUgYSBkYXRhc2V0IHdpdGggdHdvIGZlYXR1cmVzICh4IGFuZCB5KSB0aGF0IGNhbiBiZSBncm91cGVkIGludG8gY2x1c3RlcnMuDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJ5DQpzZXQuc2VlZCgxMjMpDQoNCiMgR2VuZXJhdGUgcmFuZG9tIGRhdGEgcG9pbnRzDQp4IDwtIGMocm5vcm0oNTAsIG1lYW4gPSAxLCBzZCA9IDAuNSksIHJub3JtKDUwLCBtZWFuID0gNSwgc2QgPSAwLjUpLCBybm9ybSg1MCwgbWVhbiA9IDksIHNkID0gMC41KSkNCnkgPC0gYyhybm9ybSg1MCwgbWVhbiA9IDEsIHNkID0gMC41KSwgcm5vcm0oNTAsIG1lYW4gPSA1LCBzZCA9IDAuNSksIHJub3JtKDUwLCBtZWFuID0gOSwgc2QgPSAwLjUpKQ0KDQojIENvbWJpbmUgaW50byBhIGRhdGEgZnJhbWUNCmRhdGEgPC0gZGF0YS5mcmFtZSh4LCB5KQ0KaGVhZChkYXRhKQ0KYGBgDQoNCg0KSGVyZSwgd2UgZ2VuZXJhdGUgMTUwIGRhdGEgcG9pbnRzIGRpc3RyaWJ1dGVkIGFyb3VuZCB0aHJlZSBjZW50ZXJzOiAoMSwgMSksICg1LCA1KSwgYW5kICg5LCA5KS4NCg0KIyMjIyAqKlN0ZXAgMjogUGVyZm9ybSBLLU1lYW5zIENsdXN0ZXJpbmcqKg0KDQpXZSB3aWxsIHVzZSB0aGUgYGttZWFucygpYCBmdW5jdGlvbiBpbiBSIHRvIGFwcGx5IEstTWVhbnMgY2x1c3RlcmluZyB0byBvdXIgZGF0YXNldC4gV2UgbmVlZCB0byBzcGVjaWZ5IHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgXCggayBcKSwgd2hpY2ggd2XigJlsbCBzZXQgdG8gMy4NCg0KYGBge3J9DQojIEFwcGx5IEstTWVhbnMgY2x1c3RlcmluZyB3aXRoIDMgY2x1c3RlcnMNCmttZWFuc19yZXN1bHQgPC0ga21lYW5zKGRhdGEsIGNlbnRlcnMgPSAzKQ0KDQojIFByaW50IHRoZSByZXN1bHRzDQpwcmludChrbWVhbnNfcmVzdWx0KQ0KYGBgDQoNCg0KIyMjIyAqKk91dHB1dCoqIChzaW1wbGlmaWVkKToNCmBgYA0KSy1tZWFucyBjbHVzdGVyaW5nIHdpdGggMyBjbHVzdGVycyBvZiBzaXplcyA1MCwgNTAsIDUwDQoNCkNsdXN0ZXIgbWVhbnM6DQogICAgICAgICB4ICAgICAgICB5DQoxIDEuMDUyNzM5IDEuMDQzMzQ2DQoyIDkuMDM0MDE3IDkuMDMzMzg0DQozIDQuOTYzMjEzIDUuMDEyMDAzDQoNCkNsdXN0ZXJpbmcgdmVjdG9yOg0KIFsxXSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDEgMSAxIDENCi4uLg0KYGBgDQoNCiMjIyMgKipJbnRlcnByZXRhdGlvbioqOg0KDQotIFRoZSBhbGdvcml0aG0gaGFzIGlkZW50aWZpZWQgMyBjbHVzdGVycywgZWFjaCBjb250YWluaW5nIDUwIHBvaW50cy4NCg0KLSBUaGUgKipjbHVzdGVyIG1lYW5zKiogKGNlbnRyb2lkcykgZm9yIHRoZSB0aHJlZSBjbHVzdGVycyBhcmUgYXBwcm94aW1hdGVseSAoMSwxKSwgKDUsNSksIGFuZCAoOSw5KSwgd2hpY2ggbWF0Y2hlcyB0aGUgY2VudGVycyBhcm91bmQgd2hpY2ggdGhlIGRhdGEgd2FzIGdlbmVyYXRlZC4NCg0KLSBUaGUgKipjbHVzdGVyaW5nIHZlY3RvcioqIGluZGljYXRlcyB0aGUgY2x1c3RlciBhc3NpZ25tZW50IGZvciBlYWNoIGRhdGEgcG9pbnQuDQoNCiMjIyMgKipTdGVwIDM6IFZpc3VhbGl6ZSB0aGUgQ2x1c3RlcnMqKg0KDQpXZSBjYW4gdmlzdWFsaXplIHRoZSByZXN1bHRzIG9mIEstTWVhbnMgY2x1c3RlcmluZyBieSBwbG90dGluZyB0aGUgZGF0YSBwb2ludHMgYW5kIGNvbG9yaW5nIHRoZW0gYmFzZWQgb24gdGhlaXIgY2x1c3RlciBhc3NpZ25tZW50Lg0KDQpgYGB7cn0NCiMgUGxvdCB0aGUgZGF0YSBwb2ludHMgYW5kIGNvbG9yIHRoZW0gYnkgY2x1c3Rlcg0KcGxvdChkYXRhJHgsIGRhdGEkeSwgY29sID0ga21lYW5zX3Jlc3VsdCRjbHVzdGVyLCBwY2ggPSAxOSwNCiAgICAgbWFpbiA9ICJLLU1lYW5zIENsdXN0ZXJpbmcgUmVzdWx0cyIsDQogICAgIHhsYWIgPSAiWCIsIHlsYWIgPSAiWSIpDQoNCiMgQWRkIGNsdXN0ZXIgY2VudGVycyB0byB0aGUgcGxvdA0KcG9pbnRzKGttZWFuc19yZXN1bHQkY2VudGVyc1ssIDFdLCBrbWVhbnNfcmVzdWx0JGNlbnRlcnNbLCAyXSwgY29sID0gMTozLCBwY2ggPSA4LCBjZXggPSAyKQ0KYGBgDQoNCg0KIyMjIyAqKkludGVycHJldGF0aW9uKio6DQoNCi0gVGhlIHNjYXR0ZXJwbG90IHNob3dzIHRoZSBkYXRhIHBvaW50cywgd2l0aCBlYWNoIHBvaW50IGNvbG9yZWQgYmFzZWQgb24gaXRzIGNsdXN0ZXIgYXNzaWdubWVudC4NCg0KLSBUaGUgbGFyZ2UgY3Jvc3NlcyByZXByZXNlbnQgdGhlIGNsdXN0ZXIgY2VudHJvaWRzLCBzaG93aW5nIHdoZXJlIHRoZSBhbGdvcml0aG0gaGFzIHBsYWNlZCB0aGUgY2VudGVyIG9mIGVhY2ggY2x1c3Rlci4NCg0KLSBZb3UgY2FuIHNlZSB0aGF0IHRoZSBLLU1lYW5zIGFsZ29yaXRobSBoYXMgc3VjY2Vzc2Z1bGx5IGdyb3VwZWQgdGhlIHBvaW50cyBpbnRvIHRocmVlIGNsdXN0ZXJzIGFyb3VuZCB0aGVpciByZXNwZWN0aXZlIGNlbnRlcnMuDQoNCi0tLQ0KDQojIyMgKipDaG9vc2luZyB0aGUgT3B0aW1hbCBOdW1iZXIgb2YgQ2x1c3RlcnMgXCggayBcKSoqDQoNCk9uZSBjb21tb24gbWV0aG9kIGZvciBzZWxlY3RpbmcgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGlzIHRoZSAqKkVsYm93IE1ldGhvZCoqLCB3aGljaCBpbnZvbHZlcyBwbG90dGluZyB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgKFdTUykgZm9yIGRpZmZlcmVudCB2YWx1ZXMgb2YgXCggayBcKSBhbmQgbG9va2luZyBmb3IgYW4gImVsYm93IiBwb2ludCB3aGVyZSB0aGUgV1NTIGJlZ2lucyB0byBkZWNyZWFzZSBtb3JlIHNsb3dseS4NCg0KIyMjIyAqKlN0ZXAgNDogRWxib3cgTWV0aG9kIHRvIERldGVybWluZSBcKCBrIFwpKioNCg0KV2UgY2FuIGNvbXB1dGUgdGhlIFdTUyBmb3IgZGlmZmVyZW50IHZhbHVlcyBvZiBcKCBrIFwpIGFuZCBwbG90IHRoZSByZXN1bHRzIHRvIGhlbHAgZGV0ZXJtaW5lIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycy4NCg0KYGBge3J9DQojIENvbXB1dGUgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgKFdTUykgZm9yIGRpZmZlcmVudCBrIHZhbHVlcw0Kd3NzIDwtIHNhcHBseSgxOjEwLCBmdW5jdGlvbihrKXsNCiAga21lYW5zKGRhdGEsIGNlbnRlcnMgPSBrKSR0b3Qud2l0aGluc3MNCn0pDQoNCiMgUGxvdCB0aGUgRWxib3cgTWV0aG9kDQpwbG90KDE6MTAsIHdzcywgdHlwZSA9ICJiIiwgcGNoID0gMTksIGZyYW1lID0gRkFMU0UsDQogICAgIHhsYWIgPSAiTnVtYmVyIG9mIENsdXN0ZXJzIiwNCiAgICAgeWxhYiA9ICJUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlcyIsDQogICAgIG1haW4gPSAiRWxib3cgTWV0aG9kIGZvciBGaW5kaW5nIE9wdGltYWwgayIpDQpgYGANCg0KDQojIyMjICoqSW50ZXJwcmV0YXRpb24qKjoNCg0KLSBUaGUgKipFbGJvdyBNZXRob2QqKiBwbG90IHNob3dzIHRoZSBXU1MgZm9yIGRpZmZlcmVudCB2YWx1ZXMgb2YgXCggayBcKS4gDQoNCi0gVGhlIHBvaW50IHdoZXJlIHRoZSBkZWNyZWFzZSBpbiBXU1MgYmVjb21lcyBtb3JlIGdyYWR1YWwgaXMgdGhlICJlbGJvdyIgcG9pbnQsIHdoaWNoIHN1Z2dlc3RzIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycy4gDQoNCi0gSW4gdGhpcyBjYXNlLCB0aGUgZWxib3cgdHlwaWNhbGx5IG9jY3VycyBhcm91bmQgXCggayA9IDMgXCksIGNvbmZpcm1pbmcgdGhhdCAzIGNsdXN0ZXJzIGlzIGEgZ29vZCBjaG9pY2UgZm9yIHRoaXMgZGF0YXNldC4NCg0KLS0tDQoNCiMjIyAqKkstTWVhbnMgQWxnb3JpdGhtIFByb2Nlc3MgU3VtbWFyeSoqOg0KDQoxLiBDaG9vc2UgXCggayBcKSwgdGhlIG51bWJlciBvZiBjbHVzdGVycy4NCg0KMi4gUmFuZG9tbHkgaW5pdGlhbGl6ZSBcKCBrIFwpIGNlbnRyb2lkcy4NCg0KMy4gQXNzaWduIGVhY2ggcG9pbnQgdG8gdGhlIG5lYXJlc3QgY2VudHJvaWQuDQoNCjQuIFJlY2FsY3VsYXRlIHRoZSBjZW50cm9pZHMgYmFzZWQgb24gdGhlIG1lYW4gb2YgcG9pbnRzIGluIGVhY2ggY2x1c3Rlci4NCg0KNS4gUmVwZWF0IHN0ZXBzIDMtNCB1bnRpbCBjb252ZXJnZW5jZS4NCg0KLS0tDQoNCiMjIyAqKkNvbmNsdXNpb24qKjoNCkstTWVhbnMgY2x1c3RlcmluZyBpcyBhIHBvd2VyZnVsIGFuZCB3aWRlbHktdXNlZCB1bnN1cGVydmlzZWQgbGVhcm5pbmcgYWxnb3JpdGhtIGZvciBpZGVudGlmeWluZyBwYXR0ZXJucyBhbmQgZ3JvdXBzIGluIGRhdGEuIEluIFIsIHRoZSBga21lYW5zKClgIGZ1bmN0aW9uIG1ha2VzIGl0IGVhc3kgdG8gYXBwbHkgdGhpcyBhbGdvcml0aG0gdG8geW91ciBkYXRhc2V0LCBhbmQgdmlzdWFsaXppbmcgdGhlIGNsdXN0ZXJzIGNhbiBwcm92aWRlIHZhbHVhYmxlIGluc2lnaHRzIGludG8gdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YS4=