1 Calculating distance between observations

1.1 What is cluster analysis?

1.1.1 When to cluster?

Example of scenarios where clustering methods would likely be appropriate:

  • Using consumer behavior data to identify distinct segments within a market.
  • Identifying distinct groups of stocks that follow similar trading patterns.

1.2 Distance between two observations

1.2.1 Calculate & plot the distance between two players

We will plot the positions of the 2 players and manually calculate the distance between them by using the Euclidean distance formula.

two_players <- as.data.frame(list(x = c(5, 15), y = c(4, 10)), .Names = c("x", "y"))
two_players
# Plot the positions of the players
ggplot(two_players, aes(x = x, y = y)) + 
  geom_point() +
  # Assuming a 40x60 field
  lims(x = c(-30,30), y = c(-20, 20))


# Split the players data frame into two observations
player1 <- two_players[1, ]
player2 <- two_players[2, ]

Calculate the distance between player1 and player2 by using the Euclidean distance formula

\(\sqrt{(x1−x2)^2+(y1−y2)^2}\)

# Calculate and print their distance using the Euclidean Distance formula
player_distance <- sqrt( (player1$x - player2$x)^2 + (player1$y - player2$y)^2 )
player_distance
[1] 11.6619

1.2.2 Using the dist() function

Using the Euclidean formula manually may be practical for 2 observations but can get more complicated rather quickly when measuring the distance between many observations.

The dist() function simplifies this process by calculating distances between our observations (rows) using their features (columns). In this case the observations are the player positions and the dimensions are their x and y coordinates.

The default distance calculation for the dist() function is Euclidean distance

three_players <- as.data.frame(list(x = c(5, 15, 0), y = c(4, 10, 20)), .Names = c("x", 
"y"))
# Calculate the Distance Between two_players
dist_two_players <- dist(two_players)
dist_two_players
        1
2 11.6619
# Calculate the Distance Between three_players
dist_three_players <- dist(three_players)
dist_three_players
         1        2
2 11.66190         
3 16.76305 18.02776

The dist() function makes life easier when working with many dimensions and observations.

1.2.3 Who are the closest players?

Which two players are closest to one another?

four_players <- as.data.frame(list(x = c(5, 15, 0, -5), y = c(4, 10, 20, 5)), .Names = c("x", 
"y"))
(dist(four_players))
         1        2        3
2 11.66190                  
3 16.76305 18.02776         
4 10.04988 20.61553 15.81139

Players 1 and 4 are the closest to one another.

1.3 The importance of scale

1.3.1 Effects of scale

When a variable is on a larger scale than other variables in your data it may disproportionately influence the resulting distance calculated between your observations. Lets see this in action by observing a sample of data from the trees data set.

trees <- as.data.frame(list(Girth = c(8.3, 8.6, 8.8, 10.5, 10.7, 10.8, 11, 
11, 11.1, 11.2, 11.3, 11.4, 11.4, 11.7, 12, 12.9, 12.9, 13.3, 
13.7, 13.8, 14, 14.2, 14.5, 16, 16.3, 17.3, 17.5, 17.9, 18, 18, 
20.6), Height = c(70, 65, 63, 72, 81, 83, 66, 75, 80, 75, 79, 
76, 76, 69, 75, 74, 85, 86, 71, 64, 78, 80, 74, 72, 77, 81, 82, 
80, 80, 80, 87), Volume = c(10.3, 10.3, 10.2, 16.4, 18.8, 19.7, 
15.6, 18.2, 22.6, 19.9, 24.2, 21, 21.4, 21.3, 19.1, 22.2, 33.8, 
27.4, 25.7, 24.9, 34.5, 31.7, 36.3, 38.3, 42.6, 55.4, 55.7, 58.3, 
51.5, 51, 77)), .Names = c("Girth", "Height", "Volume"))

We will leverage the scale() function which by default centers & scales our column features.

Our variables are the following:

  • Girth - tree diameter in inches
  • Height - tree height in inches
three_trees <- trees[1:3, 1:2]

# Calculate distance for three_trees 
dist_trees <- dist(three_trees)

# Scale three trees & calculate the distance  
scaled_three_trees <- scale(three_trees)
dist_scaled_trees <- dist(scaled_three_trees)

# Output the results of both Matrices
print('Without Scaling')
[1] "Without Scaling"
dist_trees
         1        2
2 5.008992         
3 7.017834 2.009975
print('With Scaling')
[1] "With Scaling"
dist_scaled_trees
          1         2
2 1.8286961          
3 2.7778767 0.9691601

1.4 Measuring distance for categorical data

1.4.1 Calculating distance between categorical variables

We will explore how to calculate binary (Jaccard) distances. In order to calculate distances we will first have to dummify our categories using the dummy.data.frame() from the library dummies.

We will use a small collection of survey observations stored in the data frame job_survey with the following columns:

  • job_satisfaction Possible options: “Hi”, “Mid”, “Low”
  • is_happy Possible options: “Yes”, “No”
job_survey <- as.data.frame(list(job_satisfaction = structure(c(2L, 2L, 1L, 2L, 
3L), .Label = c("Hi", "Low", "Mid"), class = "factor"), is_happy = structure(c(1L, 
1L, 2L, 1L, 1L), .Label = c("No", "Yes"), class = "factor")), .Names = c("job_satisfaction", 
"is_happy"))
library(dummies)
# Dummify the Survey Data
dummy_survey <- dummy.data.frame(job_survey)
non-list contrasts argument ignorednon-list contrasts argument ignored
# Calculate the Distance
dist_survey <- dist(dummy_survey, method = "binary")

# Print the Original Data
job_survey

# Print the Distance Matrix
dist_survey
          1         2         3         4
2 0.0000000                              
3 1.0000000 1.0000000                    
4 0.0000000 0.0000000 1.0000000          
5 0.6666667 0.6666667 1.0000000 0.6666667

Notice that this distance metric successfully captured that observations 1 and 2 are identical (distance of 0)

2 Hierarchical clustering

2.1 Comparing more than two observations

2.1.1 Calculating linkage

Let us revisit the example with three players on a field. The distance matrix between these three players is shown below and is available as the variable dist_players.

(dist_players <- dist_three_players)
         1        2
2 11.66190         
3 16.76305 18.02776

From this we can tell that the first group that forms is between players 1 & 2, since they are the closest to one another with a Euclidean distance value of 11.

Now you want to apply the three linkage methods you have learned to determine what the distance of this group is to player 3.

# Extract the pair distances
distance_1_2 <- dist_players[1]
distance_1_3 <- dist_players[2]
distance_2_3 <- dist_players[3]
# Extract the pair distances
distance_1_2 <- dist_players[1]
distance_1_3 <- dist_players[2]
distance_2_3 <- dist_players[3]

# Calculate the complete distance between group 1-2 and 3
complete <- max(c(distance_1_3, distance_2_3))
complete
[1] 18.02776
# Calculate the single distance between group 1-2 and 3
single <- min(c(distance_1_3, distance_2_3))
single
[1] 16.76305
# Calculate the average distance between group 1-2 and 3
average <- mean(c(distance_1_3, distance_2_3))
average
[1] 17.39541

2.2 Capturing K clusters

2.2.1 Assign cluster membership

We will leverage the hclust() function to calculate the iterative linkage steps and we will use the cutree() function to extract the cluster assignments for the desired number (k) of clusters.

The positions of 12 players at the start of a 6v6 soccer match. This is stored in the lineup data frame.

lineup <- rbind(structure(list(x = c(-1, -2, 8, 7, -12, -15, -13, 15, 21, 12, 
-25, 26), y = c(1, -3, 6, -8, 8, 0, -10, 16, 2, -15, 1, 0)), .Names = c("x", 
"y"), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-12L)))

We know that this match has two teams (k = 2), let’s use the clustering methods to assign which team each player belongs in based on their position.

Notes:

  • The linkage method can be passed via the method parameter: hclust(distance_matrix, method = “complete”)
  • Remember that in soccer opposing teams start on their half of the field.
  • Because these positions are measured using the same scale we do not need to re-scale our data.
# Calculate the Distance
dist_players <- dist(lineup)

# Perform the hierarchical clustering using the complete linkage
hc_players <- hclust(dist_players, method = "complete")

# Calculate the assignment vector with a k of 2
clusters_k2 <- cutree(hc_players, k = 2)

# Create a new data frame storing these results
lineup_k2_complete <- mutate(lineup, cluster = clusters_k2)

2.2.2 Exploring the clusters

Because clustering analysis is always in part qualitative, it is incredibly important to have the necessary tools to explore the results of the clustering.

We will explore that data frame just created.

library(dplyr)
# Count the cluster assignments
count(lineup_k2_complete, cluster)

# Plot the positions of the players and color them using their cluster
ggplot(lineup_k2_complete, aes(x = x, y = y, color = factor(cluster))) +
  geom_point()

2.3 Vizualizing the dendrogram

2.3.1 Comparing average, single & complete linkage

We are now ready to analyze the clustering results of the lineup dataset using the dendrogram plot. This will give you a new perspective on the effect the decision of the linkage method has on your resulting cluster analysis.

# Prepare the Distance Matrix
dist_players <- dist(lineup)

# Generate hclust for complete, single & average linkage methods
hc_complete <- hclust(dist_players, method = "complete")
hc_single <- hclust(dist_players, method = "single")
hc_average <- hclust(dist_players, method = "average")

# Plot & Label the 3 Dendrograms Side-by-Side
# Hint: To see these Side-by-Side run the 4 lines together as one command
par(mfrow = c(1,3))
plot(hc_complete, main = 'Complete Linkage')
plot(hc_single, main = 'Single Linkage')
plot(hc_average, main = 'Average Linkage')

2.3.2 Height of the tree

An advantage of working with a clustering method like hierarchical clustering is that you can describe the relationships between your observations based on both the distance metric and the linkage metric selected (the combination of which defines the height of the tree).

2.4 Cutting the tree

2.4.1 Clusters based on height

We will leverage the visual representation of the dendrogram in order to group your observations into clusters using a maximum height (h), below which clusters form.

We will work the color_branches() function from the dendextend library in order to visually inspect the clusters that form at any height along the dendrogram.

library(dendextend)
library(dendextend)
dist_players <- dist(lineup, method = 'euclidean')
hc_players <- hclust(dist_players, method = "complete")

# Create a dendrogram object from the hclust variable
dend_players <- as.dendrogram(hc_players)

# Plot the dendrogram
plot(dend_players)


# Color branches by cluster formed from the cut at a height of 20 & plot
dend_20 <- color_branches(dend_players, h = 20)

# Plot the dendrogram with clusters colored below height 20
plot(dend_20)


# Color branches by cluster formed from the cut at a height of 40 & plot
dend_40 <- color_branches(dend_players, h = 40)

# Plot the dendrogram with clusters colored below height 40
plot(dend_40)

The height that we use to cut the tree greatly influences the number of clusters and their size

2.4.2 Exploring the branches cut from the tree

The cutree() function can also be used to cut a tree at a given height by using the h parameter.

dist_players <- dist(lineup, method = 'euclidean')
hc_players <- hclust(dist_players, method = "complete")

# Calculate the assignment vector with a h of 20
clusters_h20 <- cutree(hc_players, h = 20)

# Create a new data frame storing these results
lineup_h20_complete <- mutate(lineup, cluster = clusters_h20)

# Calculate the assignment vector with a h of 40
clusters_h40 <- cutree(hc_players, h = 40)

# Create a new data frame storing these results
lineup_h40_complete <- mutate(lineup, cluster = clusters_h40)

# Plot the positions of the players and color them using their cluster for height = 20
ggplot(lineup_h20_complete, aes(x = x, y = y, color = factor(cluster))) +
  geom_point()


# Plot the positions of the players and color them using their cluster for height = 40
ggplot(lineup_h40_complete, aes(x = x, y = y, color = factor(cluster))) +
  geom_point()

The height of any branch is determined by the linkage and distance decisions (in this case complete linkage and Euclidean distance). While the members of the clusters that form below a desired height have a maximum linkage+distance amongst themselves that is less than the desired height.

2.5 Making sense of the clusters

2.5.1 Segment wholesale customers

We’re now ready to use hierarchical clustering to perform market segmentation (i.e. use consumer characteristics to group them into subgroups).

customers_spend <- readRDS("ws_customers.rds")

We are provided with the amount spent by 45 different clients of a wholesale distributor for the food categories of Milk, Grocery & Frozen. This is stored in the data frame customers_spend. Assign these clients into meaningful clusters.

We can assume, because the data is all of the same type (amount spent) and you will not need to scale it.

# Calculate Euclidean distance between customers
dist_customers <- dist(customers_spend)

# Generate a complete linkage analysis 
hc_customers <- hclust(dist_customers, method = "complete")

# Plot the dendrogram
plot(hc_customers)


# Create a cluster assignment vector at h = 15000
clust_customers <- cutree(hc_customers, h = 15000)

# Generate the segmented customers data frame
segment_customers <- mutate(customers_spend, cluster = clust_customers)

2.5.2 Explore wholesale customer clusters

We are now ready to analyze the characteristics of these clusters.

Since we are working with more than 2 dimensions it would be challenging to visualize a scatter plot of the clusters, instead we will rely on summary statistics to explore these clusters. We will analyze the mean amount spent in each cluster for all three categories.

dist_customers <- dist(customers_spend)
hc_customers <- hclust(dist_customers)
clust_customers <- cutree(hc_customers, h = 15000)
segment_customers <- mutate(customers_spend, cluster = clust_customers)

# Count the number of customers that fall into each cluster
count(segment_customers, cluster)

# Color the dendrogram based on the height cutoff
dend_customers <- as.dendrogram(hc_customers)
dend_colored <- color_branches(dend_customers, h = 15000)

# Plot the colored dendrogram
plot(dend_colored)


# Calculate the mean for each category
segment_customers %>% 
  group_by(cluster) %>% 
  summarise_all(list(mean))
  • Customers in cluster 1 spent more money on Milk than any other cluster.
  • Customers in cluster 3 spent more money on Grocery than any other cluster.
  • Customers in cluster 4 spent more money on Frozen goods than any other cluster.
  • The majority of customers fell into cluster 2 and did not show any excessive spending in any category. press

3 K-means clustering

3.1 Introduction

3.1.1 K-means on a soccer field

We will use the lineup dataframe.

Note that in the kmeans() function k is specified using the centers parameter.

# Build a kmeans model
model_km2 <- kmeans(lineup, centers = 2)

# Extract the cluster assignment vector from the kmeans model
clust_km2 <- model_km2$cluster

# Create a new data frame appending the cluster assignment
lineup_km2 <- mutate(lineup, cluster = clust_km2)

# Plot the positions of the players and color them using their cluster
ggplot(lineup_km2, aes(x = x, y = y, color = factor(cluster))) +
  geom_point()

Knowing the desired number of clusters ahead of time can be very helpful when performing a k-means analysis.

3.1.2 K-means on a soccer field (part 2)

We successfully used the k-means algorithm to cluster the two teams from the lineup data frame. This time, let’s explore what happens when you use a k of 3.

# Build a kmeans model
model_km3 <- kmeans(lineup, centers = 3)

# Extract the cluster assignment vector from the kmeans model
clust_km3 <- model_km3$cluster

# Create a new data frame appending the cluster assignment
lineup_km3 <- mutate(lineup, cluster = clust_km3)

# Plot the positions of the players and color them using their cluster
ggplot(lineup_km3, aes(x = x, y = y, color = factor(cluster))) +
  geom_point()

Does this result make sense? Remember we only have 2 teams on the field. It’s very important to remember that k-means will run with any k that is more than 2 and less than your total observations, but it doesn’t always mean the results will be meaningful.

3.2 Evaluating different values of K by eye

3.2.1 Many K’s many models

While the lineup dataset clearly has a known value of k, often times the optimal number of clusters isn’t known and must be estimated.

We will leverage map_dbl() from the purrr library to run k-means using values of k ranging from 1 to 10 and extract the total within-cluster sum of squares metric from each one. This will be the first step towards visualizing the elbow plot.

library(purrr)

# Use map_dbl to run many models with varying value of k (centers)
tot_withinss <- map_dbl(1:10,  function(k){
  model <- kmeans(x = lineup, centers = k)
  model$tot.withinss
})

# Generate a data frame containing both k and tot_withinss
elbow_df <- data.frame(
  k = 1:10 ,
  tot_withinss = tot_withinss
)

3.2.2 Elbow (Scree) plot

We have calculated the total within-cluster sum of squares for values of k ranging from 1 to 10. We can visualize this relationship using a line plot to create what is known as an elbow plot (or scree plot).

When looking at an elbow plot we want to see a sharp decline from one k to another followed by a more gradual decrease in slope. The last value of k before the slope of the plot levels off suggests a “good” value of k.

# Use map_dbl to run many models with varying value of k (centers)
tot_withinss <- map_dbl(1:10,  function(k){
  model <- kmeans(x = lineup, centers = k)
  model$tot.withinss
})

# Generate a data frame containing both k and tot_withinss
elbow_df <- data.frame(
  k = 1:10,
  tot_withinss = tot_withinss
)

# Plot the elbow plot
ggplot(elbow_df, aes(x = k, y = tot_withinss)) +
  geom_line() +
  scale_x_continuous(breaks = 1:10)

We have learned how to create and visualize elbow plots as a tool for finding a “good” value of k.

We can see that there is a sharp change in the slope of this line that makes an "elbow" shape. Furthermore, this is supported by the prior knowledge that there are two teams in this data and a k of 2 is desired.

3.3 Silhouette analysis: observation level performance

3.3.1 Silhouette analysis

Silhouette analysis allows you to calculate how similar each observations is with the cluster it is assigned relative to other clusters. This metric (silhouette width) ranges from -1 to 1 for each observation in your data and can be interpreted as follows:

  • Values close to 1 suggest that the observation is well matched to the assigned cluster
  • Values close to 0 suggest that the observation is borderline matched between two clusters
  • Values close to -1 suggest that the observations may be assigned to the wrong cluster

We will leverage the pam() and the silhouette() functions from the cluster library to perform silhouette analysis to compare the results of models with a k of 2 and a k of 3.

library(cluster)

# Generate a k-means model using the pam() function with a k = 2
pam_k2 <- pam(lineup, k = 2)

# Plot the silhouette visual for the pam_k2 model
plot(silhouette(pam_k2))


# Generate a k-means model using the pam() function with a k = 3
pam_k3 <- pam(lineup, k = 3)

# Plot the silhouette visual for the pam_k3 model
plot(silhouette(pam_k3))

Did you notice that for k = 2, no observation has a silhouette width close to 0? What about the fact that for k = 3, observation 3 is close to 0 and is negative? This suggests that k = 3 is not the right number of clusters.

3.4 Making sense of the K-means clusters

3.4.1 Revisiting wholesale data: “Best” k

We explored wholesale distributor data customers_spend using hierarchical clustering. This time we will analyze this data using the k-means clustering tools. The first step will be to determine the “best” value of k using average silhouette width.

# Use map_dbl to run many models with varying value of k
sil_width <- map_dbl(2:10,  function(k){
  model <- pam(x = customers_spend, k = k)
  model$silinfo$avg.width
})

# Generate a data frame containing both k and sil_width
sil_df <- data.frame(
  k = 2:10,
  sil_width = sil_width
)

# Plot the relationship between k and sil_width
ggplot(sil_df, aes(x = k, y = sil_width)) +
  geom_line() +
  scale_x_continuous(breaks = 2:10)

From the plot notice that k = 2 has the highest average sillhouette width and is the “best” value of k we will move forward with.

3.4.2 Exploration

We have found that k = 2 has the highest average silhouette width. We will continue to analyze the wholesale customer data by building and exploring a kmeans model with 2 clusters.

set.seed(42)

# Build a k-means model for the customers_spend with a k of 2
model_customers <- kmeans(customers_spend, centers = 2)

# Extract the vector of cluster assignments from the model
clust_customers <- model_customers$cluster

# Build the segment_customers data frame
segment_customers <- mutate(customers_spend, cluster = clust_customers)

# Calculate the size of each cluster
count(segment_customers, cluster)

# Calculate the mean for each category
segment_customers %>% 
  group_by(cluster) %>% 
  summarise_all(list(mean))

It seems that in this case cluster 1 consists of individuals who proportionally spend more on Frozen food while cluster 2 customers spent more on Milk and Grocery.

When we explored this data using hierarchical clustering, the method resulted in 4 clusters while using k-means we got 2. Both of these results are valid, but which one is appropriate for this would require more subject matter expertise.

“Generating clusters is a science, but interpreting them is an art.”

LS0tCnRpdGxlOiAiQ2x1c3RlciBBbmFseXNpcyBpbiBSIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfY29sbGFwc2VkOiBmYWxzZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICAKdG9jX2RlcHRoOiAzCi0tLQojIENhbGN1bGF0aW5nIGRpc3RhbmNlIGJldHdlZW4gb2JzZXJ2YXRpb25zCgojIyBXaGF0IGlzIGNsdXN0ZXIgYW5hbHlzaXM/CgojIyMgV2hlbiB0byBjbHVzdGVyPwoKRXhhbXBsZSBvZiBzY2VuYXJpb3Mgd2hlcmUgY2x1c3RlcmluZyBtZXRob2RzIHdvdWxkIGxpa2VseSBiZSBhcHByb3ByaWF0ZToKCi0gVXNpbmcgY29uc3VtZXIgYmVoYXZpb3IgZGF0YSB0byBpZGVudGlmeSBkaXN0aW5jdCBzZWdtZW50cyB3aXRoaW4gYSBtYXJrZXQuCi0gSWRlbnRpZnlpbmcgZGlzdGluY3QgZ3JvdXBzIG9mIHN0b2NrcyB0aGF0IGZvbGxvdyBzaW1pbGFyIHRyYWRpbmcgcGF0dGVybnMuCgojIyBEaXN0YW5jZSBiZXR3ZWVuIHR3byBvYnNlcnZhdGlvbnMKCiMjIyBDYWxjdWxhdGUgJiBwbG90IHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHR3byBwbGF5ZXJzCgpXZSB3aWxsIHBsb3QgdGhlIHBvc2l0aW9ucyBvZiB0aGUgMiBwbGF5ZXJzIGFuZCBtYW51YWxseSBjYWxjdWxhdGUgdGhlIGRpc3RhbmNlIGJldHdlZW4gdGhlbSBieSB1c2luZyB0aGUgRXVjbGlkZWFuIGRpc3RhbmNlIGZvcm11bGEuCmBgYHtyfQp0d29fcGxheWVycyA8LSBhcy5kYXRhLmZyYW1lKGxpc3QoeCA9IGMoNSwgMTUpLCB5ID0gYyg0LCAxMCkpLCAuTmFtZXMgPSBjKCJ4IiwgInkiKSkKYGBgCmBgYHtyfQp0d29fcGxheWVycwpgYGAKYGBge3J9CiMgUGxvdCB0aGUgcG9zaXRpb25zIG9mIHRoZSBwbGF5ZXJzCmdncGxvdCh0d29fcGxheWVycywgYWVzKHggPSB4LCB5ID0geSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICAjIEFzc3VtaW5nIGEgNDB4NjAgZmllbGQKICBsaW1zKHggPSBjKC0zMCwzMCksIHkgPSBjKC0yMCwgMjApKQoKIyBTcGxpdCB0aGUgcGxheWVycyBkYXRhIGZyYW1lIGludG8gdHdvIG9ic2VydmF0aW9ucwpwbGF5ZXIxIDwtIHR3b19wbGF5ZXJzWzEsIF0KcGxheWVyMiA8LSB0d29fcGxheWVyc1syLCBdCmBgYApDYWxjdWxhdGUgdGhlIGRpc3RhbmNlIGJldHdlZW4gcGxheWVyMSBhbmQgcGxheWVyMiBieSB1c2luZyB0aGUgRXVjbGlkZWFuIGRpc3RhbmNlIGZvcm11bGEKCiRcc3FydHsoeDHiiJJ4MileMisoeTHiiJJ5MileMn0kCmBgYHtyfQojIENhbGN1bGF0ZSBhbmQgcHJpbnQgdGhlaXIgZGlzdGFuY2UgdXNpbmcgdGhlIEV1Y2xpZGVhbiBEaXN0YW5jZSBmb3JtdWxhCnBsYXllcl9kaXN0YW5jZSA8LSBzcXJ0KCAocGxheWVyMSR4IC0gcGxheWVyMiR4KV4yICsgKHBsYXllcjEkeSAtIHBsYXllcjIkeSleMiApCnBsYXllcl9kaXN0YW5jZQpgYGAKIyMjIFVzaW5nIHRoZSBkaXN0KCkgZnVuY3Rpb24KClVzaW5nIHRoZSBFdWNsaWRlYW4gZm9ybXVsYSBtYW51YWxseSBtYXkgYmUgcHJhY3RpY2FsIGZvciAyIG9ic2VydmF0aW9ucyBidXQgY2FuIGdldCBtb3JlIGNvbXBsaWNhdGVkIHJhdGhlciBxdWlja2x5IHdoZW4gbWVhc3VyaW5nIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIG1hbnkgb2JzZXJ2YXRpb25zLgoKVGhlIGRpc3QoKSBmdW5jdGlvbiBzaW1wbGlmaWVzIHRoaXMgcHJvY2VzcyBieSBjYWxjdWxhdGluZyBkaXN0YW5jZXMgYmV0d2VlbiBvdXIgb2JzZXJ2YXRpb25zIChyb3dzKSB1c2luZyB0aGVpciBmZWF0dXJlcyAoY29sdW1ucykuIEluIHRoaXMgY2FzZSB0aGUgb2JzZXJ2YXRpb25zIGFyZSB0aGUgcGxheWVyIHBvc2l0aW9ucyBhbmQgdGhlIGRpbWVuc2lvbnMgYXJlIHRoZWlyIHggYW5kIHkgY29vcmRpbmF0ZXMuCgpUaGUgZGVmYXVsdCBkaXN0YW5jZSBjYWxjdWxhdGlvbiBmb3IgdGhlIGRpc3QoKSBmdW5jdGlvbiBpcyBFdWNsaWRlYW4gZGlzdGFuY2UKYGBge3J9CnRocmVlX3BsYXllcnMgPC0gYXMuZGF0YS5mcmFtZShsaXN0KHggPSBjKDUsIDE1LCAwKSwgeSA9IGMoNCwgMTAsIDIwKSksIC5OYW1lcyA9IGMoIngiLCAKInkiKSkKYGBgCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgRGlzdGFuY2UgQmV0d2VlbiB0d29fcGxheWVycwpkaXN0X3R3b19wbGF5ZXJzIDwtIGRpc3QodHdvX3BsYXllcnMpCmRpc3RfdHdvX3BsYXllcnMKCiMgQ2FsY3VsYXRlIHRoZSBEaXN0YW5jZSBCZXR3ZWVuIHRocmVlX3BsYXllcnMKZGlzdF90aHJlZV9wbGF5ZXJzIDwtIGRpc3QodGhyZWVfcGxheWVycykKZGlzdF90aHJlZV9wbGF5ZXJzCmBgYApUaGUgZGlzdCgpIGZ1bmN0aW9uIG1ha2VzIGxpZmUgZWFzaWVyIHdoZW4gd29ya2luZyB3aXRoIG1hbnkgZGltZW5zaW9ucyBhbmQgb2JzZXJ2YXRpb25zLgoKIyMjIFdobyBhcmUgdGhlIGNsb3Nlc3QgcGxheWVycz8KCldoaWNoIHR3byBwbGF5ZXJzIGFyZSBjbG9zZXN0IHRvIG9uZSBhbm90aGVyPwpgYGB7cn0KZm91cl9wbGF5ZXJzIDwtIGFzLmRhdGEuZnJhbWUobGlzdCh4ID0gYyg1LCAxNSwgMCwgLTUpLCB5ID0gYyg0LCAxMCwgMjAsIDUpKSwgLk5hbWVzID0gYygieCIsIAoieSIpKQpgYGAKYGBge3J9CihkaXN0KGZvdXJfcGxheWVycykpCmBgYApQbGF5ZXJzIDEgYW5kIDQgYXJlIHRoZSBjbG9zZXN0IHRvIG9uZSBhbm90aGVyLgoKIyMgVGhlIGltcG9ydGFuY2Ugb2Ygc2NhbGUKCiMjIyBFZmZlY3RzIG9mIHNjYWxlCgpXaGVuIGEgdmFyaWFibGUgaXMgb24gYSBsYXJnZXIgc2NhbGUgdGhhbiBvdGhlciB2YXJpYWJsZXMgaW4geW91ciBkYXRhIGl0IG1heSBkaXNwcm9wb3J0aW9uYXRlbHkgaW5mbHVlbmNlIHRoZSByZXN1bHRpbmcgZGlzdGFuY2UgY2FsY3VsYXRlZCBiZXR3ZWVuIHlvdXIgb2JzZXJ2YXRpb25zLiBMZXRzIHNlZSB0aGlzIGluIGFjdGlvbiBieSBvYnNlcnZpbmcgYSBzYW1wbGUgb2YgZGF0YSBmcm9tIHRoZSB0cmVlcyBkYXRhIHNldC4KYGBge3J9CnRyZWVzIDwtIGFzLmRhdGEuZnJhbWUobGlzdChHaXJ0aCA9IGMoOC4zLCA4LjYsIDguOCwgMTAuNSwgMTAuNywgMTAuOCwgMTEsIAoxMSwgMTEuMSwgMTEuMiwgMTEuMywgMTEuNCwgMTEuNCwgMTEuNywgMTIsIDEyLjksIDEyLjksIDEzLjMsIAoxMy43LCAxMy44LCAxNCwgMTQuMiwgMTQuNSwgMTYsIDE2LjMsIDE3LjMsIDE3LjUsIDE3LjksIDE4LCAxOCwgCjIwLjYpLCBIZWlnaHQgPSBjKDcwLCA2NSwgNjMsIDcyLCA4MSwgODMsIDY2LCA3NSwgODAsIDc1LCA3OSwgCjc2LCA3NiwgNjksIDc1LCA3NCwgODUsIDg2LCA3MSwgNjQsIDc4LCA4MCwgNzQsIDcyLCA3NywgODEsIDgyLCAKODAsIDgwLCA4MCwgODcpLCBWb2x1bWUgPSBjKDEwLjMsIDEwLjMsIDEwLjIsIDE2LjQsIDE4LjgsIDE5LjcsIAoxNS42LCAxOC4yLCAyMi42LCAxOS45LCAyNC4yLCAyMSwgMjEuNCwgMjEuMywgMTkuMSwgMjIuMiwgMzMuOCwgCjI3LjQsIDI1LjcsIDI0LjksIDM0LjUsIDMxLjcsIDM2LjMsIDM4LjMsIDQyLjYsIDU1LjQsIDU1LjcsIDU4LjMsIAo1MS41LCA1MSwgNzcpKSwgLk5hbWVzID0gYygiR2lydGgiLCAiSGVpZ2h0IiwgIlZvbHVtZSIpKQpgYGAKV2Ugd2lsbCBsZXZlcmFnZSB0aGUgc2NhbGUoKSBmdW5jdGlvbiB3aGljaCBieSBkZWZhdWx0IGNlbnRlcnMgJiBzY2FsZXMgb3VyIGNvbHVtbiBmZWF0dXJlcy4KCk91ciB2YXJpYWJsZXMgYXJlIHRoZSBmb2xsb3dpbmc6CgotIEdpcnRoIC0gdHJlZSBkaWFtZXRlciBpbiBpbmNoZXMKLSBIZWlnaHQgLSB0cmVlIGhlaWdodCBpbiBpbmNoZXMKCmBgYHtyfQp0aHJlZV90cmVlcyA8LSB0cmVlc1sxOjMsIDE6Ml0KCiMgQ2FsY3VsYXRlIGRpc3RhbmNlIGZvciB0aHJlZV90cmVlcyAKZGlzdF90cmVlcyA8LSBkaXN0KHRocmVlX3RyZWVzKQoKIyBTY2FsZSB0aHJlZSB0cmVlcyAmIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgIApzY2FsZWRfdGhyZWVfdHJlZXMgPC0gc2NhbGUodGhyZWVfdHJlZXMpCmRpc3Rfc2NhbGVkX3RyZWVzIDwtIGRpc3Qoc2NhbGVkX3RocmVlX3RyZWVzKQoKIyBPdXRwdXQgdGhlIHJlc3VsdHMgb2YgYm90aCBNYXRyaWNlcwpwcmludCgnV2l0aG91dCBTY2FsaW5nJykKZGlzdF90cmVlcwoKcHJpbnQoJ1dpdGggU2NhbGluZycpCmRpc3Rfc2NhbGVkX3RyZWVzCmBgYAojIyBNZWFzdXJpbmcgZGlzdGFuY2UgZm9yIGNhdGVnb3JpY2FsIGRhdGEKCiMjIyBDYWxjdWxhdGluZyBkaXN0YW5jZSBiZXR3ZWVuIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwoKV2Ugd2lsbCBleHBsb3JlIGhvdyB0byBjYWxjdWxhdGUgYmluYXJ5IChKYWNjYXJkKSBkaXN0YW5jZXMuIEluIG9yZGVyIHRvIGNhbGN1bGF0ZSBkaXN0YW5jZXMgd2Ugd2lsbCBmaXJzdCBoYXZlIHRvIGR1bW1pZnkgb3VyIGNhdGVnb3JpZXMgdXNpbmcgdGhlIGR1bW15LmRhdGEuZnJhbWUoKSBmcm9tIHRoZSBsaWJyYXJ5IGR1bW1pZXMuCgpXZSB3aWxsIHVzZSBhIHNtYWxsIGNvbGxlY3Rpb24gb2Ygc3VydmV5IG9ic2VydmF0aW9ucyBzdG9yZWQgaW4gdGhlIGRhdGEgZnJhbWUgam9iX3N1cnZleSB3aXRoIHRoZSBmb2xsb3dpbmcgY29sdW1uczoKCi0gam9iX3NhdGlzZmFjdGlvbiBQb3NzaWJsZSBvcHRpb25zOiAiSGkiLCAiTWlkIiwgIkxvdyIKLSBpc19oYXBweSBQb3NzaWJsZSBvcHRpb25zOiAiWWVzIiwgIk5vIgoKYGBge3J9CmpvYl9zdXJ2ZXkgPC0gYXMuZGF0YS5mcmFtZShsaXN0KGpvYl9zYXRpc2ZhY3Rpb24gPSBzdHJ1Y3R1cmUoYygyTCwgMkwsIDFMLCAyTCwgCjNMKSwgLkxhYmVsID0gYygiSGkiLCAiTG93IiwgIk1pZCIpLCBjbGFzcyA9ICJmYWN0b3IiKSwgaXNfaGFwcHkgPSBzdHJ1Y3R1cmUoYygxTCwgCjFMLCAyTCwgMUwsIDFMKSwgLkxhYmVsID0gYygiTm8iLCAiWWVzIiksIGNsYXNzID0gImZhY3RvciIpKSwgLk5hbWVzID0gYygiam9iX3NhdGlzZmFjdGlvbiIsIAoiaXNfaGFwcHkiKSkKYGBgCmBgYHtyfQpsaWJyYXJ5KGR1bW1pZXMpCmBgYApgYGB7cn0KIyBEdW1taWZ5IHRoZSBTdXJ2ZXkgRGF0YQpkdW1teV9zdXJ2ZXkgPC0gZHVtbXkuZGF0YS5mcmFtZShqb2Jfc3VydmV5KQoKIyBDYWxjdWxhdGUgdGhlIERpc3RhbmNlCmRpc3Rfc3VydmV5IDwtIGRpc3QoZHVtbXlfc3VydmV5LCBtZXRob2QgPSAiYmluYXJ5IikKCiMgUHJpbnQgdGhlIE9yaWdpbmFsIERhdGEKam9iX3N1cnZleQoKIyBQcmludCB0aGUgRGlzdGFuY2UgTWF0cml4CmRpc3Rfc3VydmV5CmBgYApOb3RpY2UgdGhhdCB0aGlzIGRpc3RhbmNlIG1ldHJpYyBzdWNjZXNzZnVsbHkgY2FwdHVyZWQgdGhhdCBvYnNlcnZhdGlvbnMgMSBhbmQgMiBhcmUgaWRlbnRpY2FsIChkaXN0YW5jZSBvZiAwKQoKIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZwoKIyMgQ29tcGFyaW5nIG1vcmUgdGhhbiB0d28gb2JzZXJ2YXRpb25zCgojIyMgQ2FsY3VsYXRpbmcgbGlua2FnZQoKTGV0IHVzIHJldmlzaXQgdGhlIGV4YW1wbGUgd2l0aCB0aHJlZSBwbGF5ZXJzIG9uIGEgZmllbGQuIFRoZSBkaXN0YW5jZSBtYXRyaXggYmV0d2VlbiB0aGVzZSB0aHJlZSBwbGF5ZXJzIGlzIHNob3duIGJlbG93IGFuZCBpcyBhdmFpbGFibGUgYXMgdGhlIHZhcmlhYmxlIGRpc3RfcGxheWVycy4KYGBge3J9CihkaXN0X3BsYXllcnMgPC0gZGlzdF90aHJlZV9wbGF5ZXJzKQpgYGAKRnJvbSB0aGlzIHdlIGNhbiB0ZWxsIHRoYXQgdGhlIGZpcnN0IGdyb3VwIHRoYXQgZm9ybXMgaXMgYmV0d2VlbiBwbGF5ZXJzIDEgJiAyLCBzaW5jZSB0aGV5IGFyZSB0aGUgY2xvc2VzdCB0byBvbmUgYW5vdGhlciB3aXRoIGEgRXVjbGlkZWFuIGRpc3RhbmNlIHZhbHVlIG9mIDExLgoKTm93IHlvdSB3YW50IHRvIGFwcGx5IHRoZSB0aHJlZSBsaW5rYWdlIG1ldGhvZHMgeW91IGhhdmUgbGVhcm5lZCB0byBkZXRlcm1pbmUgd2hhdCB0aGUgZGlzdGFuY2Ugb2YgdGhpcyBncm91cCBpcyB0byBwbGF5ZXIgMy4KYGBge3J9CiMgRXh0cmFjdCB0aGUgcGFpciBkaXN0YW5jZXMKZGlzdGFuY2VfMV8yIDwtIGRpc3RfcGxheWVyc1sxXQpkaXN0YW5jZV8xXzMgPC0gZGlzdF9wbGF5ZXJzWzJdCmRpc3RhbmNlXzJfMyA8LSBkaXN0X3BsYXllcnNbM10KYGBgCmBgYHtyfQojIEV4dHJhY3QgdGhlIHBhaXIgZGlzdGFuY2VzCmRpc3RhbmNlXzFfMiA8LSBkaXN0X3BsYXllcnNbMV0KZGlzdGFuY2VfMV8zIDwtIGRpc3RfcGxheWVyc1syXQpkaXN0YW5jZV8yXzMgPC0gZGlzdF9wbGF5ZXJzWzNdCgojIENhbGN1bGF0ZSB0aGUgY29tcGxldGUgZGlzdGFuY2UgYmV0d2VlbiBncm91cCAxLTIgYW5kIDMKY29tcGxldGUgPC0gbWF4KGMoZGlzdGFuY2VfMV8zLCBkaXN0YW5jZV8yXzMpKQpjb21wbGV0ZQoKIyBDYWxjdWxhdGUgdGhlIHNpbmdsZSBkaXN0YW5jZSBiZXR3ZWVuIGdyb3VwIDEtMiBhbmQgMwpzaW5nbGUgPC0gbWluKGMoZGlzdGFuY2VfMV8zLCBkaXN0YW5jZV8yXzMpKQpzaW5nbGUKCiMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIGRpc3RhbmNlIGJldHdlZW4gZ3JvdXAgMS0yIGFuZCAzCmF2ZXJhZ2UgPC0gbWVhbihjKGRpc3RhbmNlXzFfMywgZGlzdGFuY2VfMl8zKSkKYXZlcmFnZQpgYGAKIyMgQ2FwdHVyaW5nIEsgY2x1c3RlcnMKCiMjIyBBc3NpZ24gY2x1c3RlciBtZW1iZXJzaGlwCgpXZSB3aWxsIGxldmVyYWdlIHRoZSBoY2x1c3QoKSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGl0ZXJhdGl2ZSBsaW5rYWdlIHN0ZXBzIGFuZCB3ZSB3aWxsIHVzZSB0aGUgY3V0cmVlKCkgZnVuY3Rpb24gdG8gZXh0cmFjdCB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBmb3IgdGhlIGRlc2lyZWQgbnVtYmVyIChrKSBvZiBjbHVzdGVycy4KClRoZSBwb3NpdGlvbnMgb2YgMTIgcGxheWVycyBhdCB0aGUgc3RhcnQgb2YgYSA2djYgc29jY2VyIG1hdGNoLiBUaGlzIGlzIHN0b3JlZCBpbiB0aGUgbGluZXVwIGRhdGEgZnJhbWUuCmBgYHtyfQpsaW5ldXAgPC0gcmJpbmQoc3RydWN0dXJlKGxpc3QoeCA9IGMoLTEsIC0yLCA4LCA3LCAtMTIsIC0xNSwgLTEzLCAxNSwgMjEsIDEyLCAKLTI1LCAyNiksIHkgPSBjKDEsIC0zLCA2LCAtOCwgOCwgMCwgLTEwLCAxNiwgMiwgLTE1LCAxLCAwKSksIC5OYW1lcyA9IGMoIngiLCAKInkiKSwgY2xhc3MgPSBjKCJ0YmxfZGYiLCAidGJsIiwgImRhdGEuZnJhbWUiKSwgcm93Lm5hbWVzID0gYyhOQSwgCi0xMkwpKSkKYGBgCldlIGtub3cgdGhhdCB0aGlzIG1hdGNoIGhhcyB0d28gdGVhbXMgKGsgPSAyKSwgbGV0J3MgdXNlIHRoZSBjbHVzdGVyaW5nIG1ldGhvZHMgdG8gYXNzaWduIHdoaWNoIHRlYW0gZWFjaCBwbGF5ZXIgYmVsb25ncyBpbiBiYXNlZCBvbiB0aGVpciBwb3NpdGlvbi4KCk5vdGVzOgoKLSBUaGUgbGlua2FnZSBtZXRob2QgY2FuIGJlIHBhc3NlZCB2aWEgdGhlIG1ldGhvZCBwYXJhbWV0ZXI6IGhjbHVzdChkaXN0YW5jZV9tYXRyaXgsIG1ldGhvZCA9ICJjb21wbGV0ZSIpCi0gUmVtZW1iZXIgdGhhdCBpbiBzb2NjZXIgb3Bwb3NpbmcgdGVhbXMgc3RhcnQgb24gdGhlaXIgaGFsZiBvZiB0aGUgZmllbGQuCi0gQmVjYXVzZSB0aGVzZSBwb3NpdGlvbnMgYXJlIG1lYXN1cmVkIHVzaW5nIHRoZSBzYW1lIHNjYWxlIHdlIGRvIG5vdCBuZWVkIHRvIHJlLXNjYWxlIG91ciBkYXRhLgoKYGBge3J9CiMgQ2FsY3VsYXRlIHRoZSBEaXN0YW5jZQpkaXN0X3BsYXllcnMgPC0gZGlzdChsaW5ldXApCgojIFBlcmZvcm0gdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHVzaW5nIHRoZSBjb21wbGV0ZSBsaW5rYWdlCmhjX3BsYXllcnMgPC0gaGNsdXN0KGRpc3RfcGxheWVycywgbWV0aG9kID0gImNvbXBsZXRlIikKCiMgQ2FsY3VsYXRlIHRoZSBhc3NpZ25tZW50IHZlY3RvciB3aXRoIGEgayBvZiAyCmNsdXN0ZXJzX2syIDwtIGN1dHJlZShoY19wbGF5ZXJzLCBrID0gMikKCiMgQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgc3RvcmluZyB0aGVzZSByZXN1bHRzCmxpbmV1cF9rMl9jb21wbGV0ZSA8LSBtdXRhdGUobGluZXVwLCBjbHVzdGVyID0gY2x1c3RlcnNfazIpCmBgYAojIyMgRXhwbG9yaW5nIHRoZSBjbHVzdGVycwoKQmVjYXVzZSBjbHVzdGVyaW5nIGFuYWx5c2lzIGlzIGFsd2F5cyBpbiBwYXJ0IHF1YWxpdGF0aXZlLCBpdCBpcyBpbmNyZWRpYmx5IGltcG9ydGFudCB0byBoYXZlIHRoZSBuZWNlc3NhcnkgdG9vbHMgdG8gZXhwbG9yZSB0aGUgcmVzdWx0cyBvZiB0aGUgY2x1c3RlcmluZy4KCldlIHdpbGwgZXhwbG9yZSB0aGF0IGRhdGEgZnJhbWUganVzdCBjcmVhdGVkLgpgYGB7cn0KbGlicmFyeShkcGx5cikKYGBgCmBgYHtyfQojIENvdW50IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnRzCmNvdW50KGxpbmV1cF9rMl9jb21wbGV0ZSwgY2x1c3RlcikKCiMgUGxvdCB0aGUgcG9zaXRpb25zIG9mIHRoZSBwbGF5ZXJzIGFuZCBjb2xvciB0aGVtIHVzaW5nIHRoZWlyIGNsdXN0ZXIKZ2dwbG90KGxpbmV1cF9rMl9jb21wbGV0ZSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3IgPSBmYWN0b3IoY2x1c3RlcikpKSArCiAgZ2VvbV9wb2ludCgpCmBgYAojIyBWaXp1YWxpemluZyB0aGUgZGVuZHJvZ3JhbQoKIyMjIENvbXBhcmluZyBhdmVyYWdlLCBzaW5nbGUgJiBjb21wbGV0ZSBsaW5rYWdlCgpXZSBhcmUgbm93IHJlYWR5IHRvIGFuYWx5emUgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBvZiB0aGUgbGluZXVwIGRhdGFzZXQgdXNpbmcgdGhlIGRlbmRyb2dyYW0gcGxvdC4gVGhpcyB3aWxsIGdpdmUgeW91IGEgbmV3IHBlcnNwZWN0aXZlIG9uIHRoZSBlZmZlY3QgdGhlIGRlY2lzaW9uIG9mIHRoZSBsaW5rYWdlIG1ldGhvZCBoYXMgb24geW91ciByZXN1bHRpbmcgY2x1c3RlciBhbmFseXNpcy4KYGBge3J9CiMgUHJlcGFyZSB0aGUgRGlzdGFuY2UgTWF0cml4CmRpc3RfcGxheWVycyA8LSBkaXN0KGxpbmV1cCkKCiMgR2VuZXJhdGUgaGNsdXN0IGZvciBjb21wbGV0ZSwgc2luZ2xlICYgYXZlcmFnZSBsaW5rYWdlIG1ldGhvZHMKaGNfY29tcGxldGUgPC0gaGNsdXN0KGRpc3RfcGxheWVycywgbWV0aG9kID0gImNvbXBsZXRlIikKaGNfc2luZ2xlIDwtIGhjbHVzdChkaXN0X3BsYXllcnMsIG1ldGhvZCA9ICJzaW5nbGUiKQpoY19hdmVyYWdlIDwtIGhjbHVzdChkaXN0X3BsYXllcnMsIG1ldGhvZCA9ICJhdmVyYWdlIikKCiMgUGxvdCAmIExhYmVsIHRoZSAzIERlbmRyb2dyYW1zIFNpZGUtYnktU2lkZQojIEhpbnQ6IFRvIHNlZSB0aGVzZSBTaWRlLWJ5LVNpZGUgcnVuIHRoZSA0IGxpbmVzIHRvZ2V0aGVyIGFzIG9uZSBjb21tYW5kCnBhcihtZnJvdyA9IGMoMSwzKSkKcGxvdChoY19jb21wbGV0ZSwgbWFpbiA9ICdDb21wbGV0ZSBMaW5rYWdlJykKcGxvdChoY19zaW5nbGUsIG1haW4gPSAnU2luZ2xlIExpbmthZ2UnKQpwbG90KGhjX2F2ZXJhZ2UsIG1haW4gPSAnQXZlcmFnZSBMaW5rYWdlJykKYGBgCiMjIyBIZWlnaHQgb2YgdGhlIHRyZWUKCkFuIGFkdmFudGFnZSBvZiB3b3JraW5nIHdpdGggYSBjbHVzdGVyaW5nIG1ldGhvZCBsaWtlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGlzIHRoYXQgeW91IGNhbiBkZXNjcmliZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHlvdXIgb2JzZXJ2YXRpb25zIGJhc2VkIG9uIGJvdGggdGhlIGRpc3RhbmNlIG1ldHJpYyBhbmQgdGhlIGxpbmthZ2UgbWV0cmljIHNlbGVjdGVkICh0aGUgY29tYmluYXRpb24gb2Ygd2hpY2ggZGVmaW5lcyB0aGUgaGVpZ2h0IG9mIHRoZSB0cmVlKS4KCiMjIEN1dHRpbmcgdGhlIHRyZWUKCiMjIyBDbHVzdGVycyBiYXNlZCBvbiBoZWlnaHQKCldlIHdpbGwgbGV2ZXJhZ2UgdGhlIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGVuZHJvZ3JhbSBpbiBvcmRlciB0byBncm91cCB5b3VyIG9ic2VydmF0aW9ucyBpbnRvIGNsdXN0ZXJzIHVzaW5nIGEgbWF4aW11bSBoZWlnaHQgKGgpLCBiZWxvdyB3aGljaCBjbHVzdGVycyBmb3JtLgoKV2Ugd2lsbCB3b3JrIHRoZSBjb2xvcl9icmFuY2hlcygpIGZ1bmN0aW9uIGZyb20gdGhlIGRlbmRleHRlbmQgbGlicmFyeSBpbiBvcmRlciB0byB2aXN1YWxseSBpbnNwZWN0IHRoZSBjbHVzdGVycyB0aGF0IGZvcm0gYXQgYW55IGhlaWdodCBhbG9uZyB0aGUgZGVuZHJvZ3JhbS4KYGBge3J9CmxpYnJhcnkoZGVuZGV4dGVuZCkKYGBgCmBgYHtyfQpsaWJyYXJ5KGRlbmRleHRlbmQpCmRpc3RfcGxheWVycyA8LSBkaXN0KGxpbmV1cCwgbWV0aG9kID0gJ2V1Y2xpZGVhbicpCmhjX3BsYXllcnMgPC0gaGNsdXN0KGRpc3RfcGxheWVycywgbWV0aG9kID0gImNvbXBsZXRlIikKCiMgQ3JlYXRlIGEgZGVuZHJvZ3JhbSBvYmplY3QgZnJvbSB0aGUgaGNsdXN0IHZhcmlhYmxlCmRlbmRfcGxheWVycyA8LSBhcy5kZW5kcm9ncmFtKGhjX3BsYXllcnMpCgojIFBsb3QgdGhlIGRlbmRyb2dyYW0KcGxvdChkZW5kX3BsYXllcnMpCgojIENvbG9yIGJyYW5jaGVzIGJ5IGNsdXN0ZXIgZm9ybWVkIGZyb20gdGhlIGN1dCBhdCBhIGhlaWdodCBvZiAyMCAmIHBsb3QKZGVuZF8yMCA8LSBjb2xvcl9icmFuY2hlcyhkZW5kX3BsYXllcnMsIGggPSAyMCkKCiMgUGxvdCB0aGUgZGVuZHJvZ3JhbSB3aXRoIGNsdXN0ZXJzIGNvbG9yZWQgYmVsb3cgaGVpZ2h0IDIwCnBsb3QoZGVuZF8yMCkKCiMgQ29sb3IgYnJhbmNoZXMgYnkgY2x1c3RlciBmb3JtZWQgZnJvbSB0aGUgY3V0IGF0IGEgaGVpZ2h0IG9mIDQwICYgcGxvdApkZW5kXzQwIDwtIGNvbG9yX2JyYW5jaGVzKGRlbmRfcGxheWVycywgaCA9IDQwKQoKIyBQbG90IHRoZSBkZW5kcm9ncmFtIHdpdGggY2x1c3RlcnMgY29sb3JlZCBiZWxvdyBoZWlnaHQgNDAKcGxvdChkZW5kXzQwKQpgYGAKVGhlIGhlaWdodCB0aGF0IHdlIHVzZSB0byBjdXQgdGhlIHRyZWUgZ3JlYXRseSBpbmZsdWVuY2VzIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgYW5kIHRoZWlyIHNpemUKCiMjIyBFeHBsb3JpbmcgdGhlIGJyYW5jaGVzIGN1dCBmcm9tIHRoZSB0cmVlCgpUaGUgY3V0cmVlKCkgZnVuY3Rpb24gY2FuIGFsc28gYmUgdXNlZCB0byBjdXQgYSB0cmVlIGF0IGEgZ2l2ZW4gaGVpZ2h0IGJ5IHVzaW5nIHRoZSBoIHBhcmFtZXRlci4KCmBgYHtyfQpkaXN0X3BsYXllcnMgPC0gZGlzdChsaW5ldXAsIG1ldGhvZCA9ICdldWNsaWRlYW4nKQpoY19wbGF5ZXJzIDwtIGhjbHVzdChkaXN0X3BsYXllcnMsIG1ldGhvZCA9ICJjb21wbGV0ZSIpCgojIENhbGN1bGF0ZSB0aGUgYXNzaWdubWVudCB2ZWN0b3Igd2l0aCBhIGggb2YgMjAKY2x1c3RlcnNfaDIwIDwtIGN1dHJlZShoY19wbGF5ZXJzLCBoID0gMjApCgojIENyZWF0ZSBhIG5ldyBkYXRhIGZyYW1lIHN0b3JpbmcgdGhlc2UgcmVzdWx0cwpsaW5ldXBfaDIwX2NvbXBsZXRlIDwtIG11dGF0ZShsaW5ldXAsIGNsdXN0ZXIgPSBjbHVzdGVyc19oMjApCgojIENhbGN1bGF0ZSB0aGUgYXNzaWdubWVudCB2ZWN0b3Igd2l0aCBhIGggb2YgNDAKY2x1c3RlcnNfaDQwIDwtIGN1dHJlZShoY19wbGF5ZXJzLCBoID0gNDApCgojIENyZWF0ZSBhIG5ldyBkYXRhIGZyYW1lIHN0b3JpbmcgdGhlc2UgcmVzdWx0cwpsaW5ldXBfaDQwX2NvbXBsZXRlIDwtIG11dGF0ZShsaW5ldXAsIGNsdXN0ZXIgPSBjbHVzdGVyc19oNDApCgojIFBsb3QgdGhlIHBvc2l0aW9ucyBvZiB0aGUgcGxheWVycyBhbmQgY29sb3IgdGhlbSB1c2luZyB0aGVpciBjbHVzdGVyIGZvciBoZWlnaHQgPSAyMApnZ3Bsb3QobGluZXVwX2gyMF9jb21wbGV0ZSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3IgPSBmYWN0b3IoY2x1c3RlcikpKSArCiAgZ2VvbV9wb2ludCgpCgojIFBsb3QgdGhlIHBvc2l0aW9ucyBvZiB0aGUgcGxheWVycyBhbmQgY29sb3IgdGhlbSB1c2luZyB0aGVpciBjbHVzdGVyIGZvciBoZWlnaHQgPSA0MApnZ3Bsb3QobGluZXVwX2g0MF9jb21wbGV0ZSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3IgPSBmYWN0b3IoY2x1c3RlcikpKSArCiAgZ2VvbV9wb2ludCgpCmBgYApUaGUgaGVpZ2h0IG9mIGFueSBicmFuY2ggaXMgZGV0ZXJtaW5lZCBieSB0aGUgbGlua2FnZSBhbmQgZGlzdGFuY2UgZGVjaXNpb25zIChpbiB0aGlzIGNhc2UgY29tcGxldGUgbGlua2FnZSBhbmQgRXVjbGlkZWFuIGRpc3RhbmNlKS4gV2hpbGUgdGhlIG1lbWJlcnMgb2YgdGhlIGNsdXN0ZXJzIHRoYXQgZm9ybSBiZWxvdyBhIGRlc2lyZWQgaGVpZ2h0IGhhdmUgYSBtYXhpbXVtIGxpbmthZ2UrZGlzdGFuY2UgYW1vbmdzdCB0aGVtc2VsdmVzIHRoYXQgaXMgbGVzcyB0aGFuIHRoZSBkZXNpcmVkIGhlaWdodC4KCiMjIE1ha2luZyBzZW5zZSBvZiB0aGUgY2x1c3RlcnMKCiMjIyBTZWdtZW50IHdob2xlc2FsZSBjdXN0b21lcnMKCldlJ3JlIG5vdyByZWFkeSB0byB1c2UgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdG8gcGVyZm9ybSBtYXJrZXQgc2VnbWVudGF0aW9uIChpLmUuIHVzZSBjb25zdW1lciBjaGFyYWN0ZXJpc3RpY3MgdG8gZ3JvdXAgdGhlbSBpbnRvIHN1Ymdyb3VwcykuCgpgYGB7cn0KY3VzdG9tZXJzX3NwZW5kIDwtIHJlYWRSRFMoIndzX2N1c3RvbWVycy5yZHMiKQpgYGAKV2UgYXJlIHByb3ZpZGVkIHdpdGggdGhlIGFtb3VudCBzcGVudCBieSA0NSBkaWZmZXJlbnQgY2xpZW50cyBvZiBhIHdob2xlc2FsZSBkaXN0cmlidXRvciBmb3IgdGhlIGZvb2QgY2F0ZWdvcmllcyBvZiBNaWxrLCBHcm9jZXJ5ICYgRnJvemVuLiBUaGlzIGlzIHN0b3JlZCBpbiB0aGUgZGF0YSBmcmFtZSBjdXN0b21lcnNfc3BlbmQuIEFzc2lnbiB0aGVzZSBjbGllbnRzIGludG8gbWVhbmluZ2Z1bCBjbHVzdGVycy4KCldlIGNhbiBhc3N1bWUsIGJlY2F1c2UgdGhlIGRhdGEgaXMgYWxsIG9mIHRoZSBzYW1lIHR5cGUgKGFtb3VudCBzcGVudCkgYW5kIHlvdSB3aWxsIG5vdCBuZWVkIHRvIHNjYWxlIGl0LgpgYGB7cn0KIyBDYWxjdWxhdGUgRXVjbGlkZWFuIGRpc3RhbmNlIGJldHdlZW4gY3VzdG9tZXJzCmRpc3RfY3VzdG9tZXJzIDwtIGRpc3QoY3VzdG9tZXJzX3NwZW5kKQoKIyBHZW5lcmF0ZSBhIGNvbXBsZXRlIGxpbmthZ2UgYW5hbHlzaXMgCmhjX2N1c3RvbWVycyA8LSBoY2x1c3QoZGlzdF9jdXN0b21lcnMsIG1ldGhvZCA9ICJjb21wbGV0ZSIpCgojIFBsb3QgdGhlIGRlbmRyb2dyYW0KcGxvdChoY19jdXN0b21lcnMpCgojIENyZWF0ZSBhIGNsdXN0ZXIgYXNzaWdubWVudCB2ZWN0b3IgYXQgaCA9IDE1MDAwCmNsdXN0X2N1c3RvbWVycyA8LSBjdXRyZWUoaGNfY3VzdG9tZXJzLCBoID0gMTUwMDApCgojIEdlbmVyYXRlIHRoZSBzZWdtZW50ZWQgY3VzdG9tZXJzIGRhdGEgZnJhbWUKc2VnbWVudF9jdXN0b21lcnMgPC0gbXV0YXRlKGN1c3RvbWVyc19zcGVuZCwgY2x1c3RlciA9IGNsdXN0X2N1c3RvbWVycykKYGBgCiMjIyBFeHBsb3JlIHdob2xlc2FsZSBjdXN0b21lciBjbHVzdGVycwoKV2UgYXJlIG5vdyByZWFkeSB0byBhbmFseXplIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlc2UgY2x1c3RlcnMuCgpTaW5jZSB3ZSBhcmUgd29ya2luZyB3aXRoIG1vcmUgdGhhbiAyIGRpbWVuc2lvbnMgaXQgd291bGQgYmUgY2hhbGxlbmdpbmcgdG8gdmlzdWFsaXplIGEgc2NhdHRlciBwbG90IG9mIHRoZSBjbHVzdGVycywgaW5zdGVhZCB3ZSB3aWxsIHJlbHkgb24gc3VtbWFyeSBzdGF0aXN0aWNzIHRvIGV4cGxvcmUgdGhlc2UgY2x1c3RlcnMuIFdlIHdpbGwgYW5hbHl6ZSB0aGUgbWVhbiBhbW91bnQgc3BlbnQgaW4gZWFjaCBjbHVzdGVyIGZvciBhbGwgdGhyZWUgY2F0ZWdvcmllcy4KYGBge3J9CmRpc3RfY3VzdG9tZXJzIDwtIGRpc3QoY3VzdG9tZXJzX3NwZW5kKQpoY19jdXN0b21lcnMgPC0gaGNsdXN0KGRpc3RfY3VzdG9tZXJzKQpjbHVzdF9jdXN0b21lcnMgPC0gY3V0cmVlKGhjX2N1c3RvbWVycywgaCA9IDE1MDAwKQpzZWdtZW50X2N1c3RvbWVycyA8LSBtdXRhdGUoY3VzdG9tZXJzX3NwZW5kLCBjbHVzdGVyID0gY2x1c3RfY3VzdG9tZXJzKQoKIyBDb3VudCB0aGUgbnVtYmVyIG9mIGN1c3RvbWVycyB0aGF0IGZhbGwgaW50byBlYWNoIGNsdXN0ZXIKY291bnQoc2VnbWVudF9jdXN0b21lcnMsIGNsdXN0ZXIpCgojIENvbG9yIHRoZSBkZW5kcm9ncmFtIGJhc2VkIG9uIHRoZSBoZWlnaHQgY3V0b2ZmCmRlbmRfY3VzdG9tZXJzIDwtIGFzLmRlbmRyb2dyYW0oaGNfY3VzdG9tZXJzKQpkZW5kX2NvbG9yZWQgPC0gY29sb3JfYnJhbmNoZXMoZGVuZF9jdXN0b21lcnMsIGggPSAxNTAwMCkKCiMgUGxvdCB0aGUgY29sb3JlZCBkZW5kcm9ncmFtCnBsb3QoZGVuZF9jb2xvcmVkKQoKIyBDYWxjdWxhdGUgdGhlIG1lYW4gZm9yIGVhY2ggY2F0ZWdvcnkKc2VnbWVudF9jdXN0b21lcnMgJT4lIAogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSAKICBzdW1tYXJpc2VfYWxsKGxpc3QobWVhbikpCmBgYAotIEN1c3RvbWVycyBpbiBjbHVzdGVyIDEgc3BlbnQgbW9yZSBtb25leSBvbiBNaWxrIHRoYW4gYW55IG90aGVyIGNsdXN0ZXIuCi0gQ3VzdG9tZXJzIGluIGNsdXN0ZXIgMyBzcGVudCBtb3JlIG1vbmV5IG9uIEdyb2NlcnkgdGhhbiBhbnkgb3RoZXIgY2x1c3Rlci4KLSBDdXN0b21lcnMgaW4gY2x1c3RlciA0IHNwZW50IG1vcmUgbW9uZXkgb24gRnJvemVuIGdvb2RzIHRoYW4gYW55IG90aGVyIGNsdXN0ZXIuCi0gVGhlIG1ham9yaXR5IG9mIGN1c3RvbWVycyBmZWxsIGludG8gY2x1c3RlciAyIGFuZCBkaWQgbm90IHNob3cgYW55IGV4Y2Vzc2l2ZSBzcGVuZGluZyBpbiBhbnkgY2F0ZWdvcnkuCnByZXNzCgojIEstbWVhbnMgY2x1c3RlcmluZwoKIyMgSW50cm9kdWN0aW9uCgojIyMgSy1tZWFucyBvbiBhIHNvY2NlciBmaWVsZAoKV2Ugd2lsbCB1c2UgdGhlIGxpbmV1cCBkYXRhZnJhbWUuCgpOb3RlIHRoYXQgaW4gdGhlIGttZWFucygpIGZ1bmN0aW9uIGsgaXMgc3BlY2lmaWVkIHVzaW5nIHRoZSBjZW50ZXJzIHBhcmFtZXRlci4KYGBge3J9CiMgQnVpbGQgYSBrbWVhbnMgbW9kZWwKbW9kZWxfa20yIDwtIGttZWFucyhsaW5ldXAsIGNlbnRlcnMgPSAyKQoKIyBFeHRyYWN0IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgdmVjdG9yIGZyb20gdGhlIGttZWFucyBtb2RlbApjbHVzdF9rbTIgPC0gbW9kZWxfa20yJGNsdXN0ZXIKCiMgQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgYXBwZW5kaW5nIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQKbGluZXVwX2ttMiA8LSBtdXRhdGUobGluZXVwLCBjbHVzdGVyID0gY2x1c3Rfa20yKQoKIyBQbG90IHRoZSBwb3NpdGlvbnMgb2YgdGhlIHBsYXllcnMgYW5kIGNvbG9yIHRoZW0gdXNpbmcgdGhlaXIgY2x1c3RlcgpnZ3Bsb3QobGluZXVwX2ttMiwgYWVzKHggPSB4LCB5ID0geSwgY29sb3IgPSBmYWN0b3IoY2x1c3RlcikpKSArCiAgZ2VvbV9wb2ludCgpCmBgYApLbm93aW5nIHRoZSBkZXNpcmVkIG51bWJlciBvZiBjbHVzdGVycyBhaGVhZCBvZiB0aW1lIGNhbiBiZSB2ZXJ5IGhlbHBmdWwgd2hlbiBwZXJmb3JtaW5nIGEgay1tZWFucyBhbmFseXNpcy4KCiMjIyBLLW1lYW5zIG9uIGEgc29jY2VyIGZpZWxkIChwYXJ0IDIpCgpXZSBzdWNjZXNzZnVsbHkgdXNlZCB0aGUgay1tZWFucyBhbGdvcml0aG0gdG8gY2x1c3RlciB0aGUgdHdvIHRlYW1zIGZyb20gdGhlIGxpbmV1cCBkYXRhIGZyYW1lLiBUaGlzIHRpbWUsIGxldCdzIGV4cGxvcmUgd2hhdCBoYXBwZW5zIHdoZW4geW91IHVzZSBhIGsgb2YgMy4KYGBge3J9CiMgQnVpbGQgYSBrbWVhbnMgbW9kZWwKbW9kZWxfa20zIDwtIGttZWFucyhsaW5ldXAsIGNlbnRlcnMgPSAzKQoKIyBFeHRyYWN0IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgdmVjdG9yIGZyb20gdGhlIGttZWFucyBtb2RlbApjbHVzdF9rbTMgPC0gbW9kZWxfa20zJGNsdXN0ZXIKCiMgQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgYXBwZW5kaW5nIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQKbGluZXVwX2ttMyA8LSBtdXRhdGUobGluZXVwLCBjbHVzdGVyID0gY2x1c3Rfa20zKQoKIyBQbG90IHRoZSBwb3NpdGlvbnMgb2YgdGhlIHBsYXllcnMgYW5kIGNvbG9yIHRoZW0gdXNpbmcgdGhlaXIgY2x1c3RlcgpnZ3Bsb3QobGluZXVwX2ttMywgYWVzKHggPSB4LCB5ID0geSwgY29sb3IgPSBmYWN0b3IoY2x1c3RlcikpKSArCiAgZ2VvbV9wb2ludCgpCmBgYApEb2VzIHRoaXMgcmVzdWx0IG1ha2Ugc2Vuc2U/IFJlbWVtYmVyIHdlIG9ubHkgaGF2ZSAyIHRlYW1zIG9uIHRoZSBmaWVsZC4gSXQncyB2ZXJ5IGltcG9ydGFudCB0byByZW1lbWJlciB0aGF0IGstbWVhbnMgd2lsbCBydW4gd2l0aCBhbnkgayB0aGF0IGlzIG1vcmUgdGhhbiAyIGFuZCBsZXNzIHRoYW4geW91ciB0b3RhbCBvYnNlcnZhdGlvbnMsIGJ1dCBpdCBkb2Vzbid0IGFsd2F5cyBtZWFuIHRoZSByZXN1bHRzIHdpbGwgYmUgbWVhbmluZ2Z1bC4KCiMjIEV2YWx1YXRpbmcgZGlmZmVyZW50IHZhbHVlcyBvZiBLIGJ5IGV5ZQoKIyMjIE1hbnkgSydzIG1hbnkgbW9kZWxzCgpXaGlsZSB0aGUgbGluZXVwIGRhdGFzZXQgY2xlYXJseSBoYXMgYSBrbm93biB2YWx1ZSBvZiBrLCBvZnRlbiB0aW1lcyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgaXNuJ3Qga25vd24gYW5kIG11c3QgYmUgZXN0aW1hdGVkLgoKV2Ugd2lsbCBsZXZlcmFnZSBtYXBfZGJsKCkgZnJvbSB0aGUgcHVycnIgbGlicmFyeSB0byBydW4gay1tZWFucyB1c2luZyB2YWx1ZXMgb2YgayByYW5naW5nIGZyb20gMSB0byAxMCBhbmQgZXh0cmFjdCB0aGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMgbWV0cmljIGZyb20gZWFjaCBvbmUuIFRoaXMgd2lsbCBiZSB0aGUgZmlyc3Qgc3RlcCB0b3dhcmRzIHZpc3VhbGl6aW5nIHRoZSBlbGJvdyBwbG90LgpgYGB7cn0KbGlicmFyeShwdXJycikKCiMgVXNlIG1hcF9kYmwgdG8gcnVuIG1hbnkgbW9kZWxzIHdpdGggdmFyeWluZyB2YWx1ZSBvZiBrIChjZW50ZXJzKQp0b3Rfd2l0aGluc3MgPC0gbWFwX2RibCgxOjEwLCAgZnVuY3Rpb24oayl7CiAgbW9kZWwgPC0ga21lYW5zKHggPSBsaW5ldXAsIGNlbnRlcnMgPSBrKQogIG1vZGVsJHRvdC53aXRoaW5zcwp9KQoKIyBHZW5lcmF0ZSBhIGRhdGEgZnJhbWUgY29udGFpbmluZyBib3RoIGsgYW5kIHRvdF93aXRoaW5zcwplbGJvd19kZiA8LSBkYXRhLmZyYW1lKAogIGsgPSAxOjEwICwKICB0b3Rfd2l0aGluc3MgPSB0b3Rfd2l0aGluc3MKKQpgYGAKIyMjIEVsYm93IChTY3JlZSkgcGxvdAoKV2UgaGF2ZSBjYWxjdWxhdGVkIHRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcyBmb3IgdmFsdWVzIG9mIGsgcmFuZ2luZyBmcm9tIDEgdG8gMTAuIFdlIGNhbiB2aXN1YWxpemUgdGhpcyByZWxhdGlvbnNoaXAgdXNpbmcgYSBsaW5lIHBsb3QgdG8gY3JlYXRlIHdoYXQgaXMga25vd24gYXMgYW4gZWxib3cgcGxvdCAob3Igc2NyZWUgcGxvdCkuCgpXaGVuIGxvb2tpbmcgYXQgYW4gZWxib3cgcGxvdCB3ZSB3YW50IHRvIHNlZSBhIHNoYXJwIGRlY2xpbmUgZnJvbSBvbmUgayB0byBhbm90aGVyIGZvbGxvd2VkIGJ5IGEgbW9yZSBncmFkdWFsIGRlY3JlYXNlIGluIHNsb3BlLiBUaGUgbGFzdCB2YWx1ZSBvZiBrIGJlZm9yZSB0aGUgc2xvcGUgb2YgdGhlIHBsb3QgbGV2ZWxzIG9mZiBzdWdnZXN0cyBhICJnb29kIiB2YWx1ZSBvZiBrLgoKYGBge3J9CiMgVXNlIG1hcF9kYmwgdG8gcnVuIG1hbnkgbW9kZWxzIHdpdGggdmFyeWluZyB2YWx1ZSBvZiBrIChjZW50ZXJzKQp0b3Rfd2l0aGluc3MgPC0gbWFwX2RibCgxOjEwLCAgZnVuY3Rpb24oayl7CiAgbW9kZWwgPC0ga21lYW5zKHggPSBsaW5ldXAsIGNlbnRlcnMgPSBrKQogIG1vZGVsJHRvdC53aXRoaW5zcwp9KQoKIyBHZW5lcmF0ZSBhIGRhdGEgZnJhbWUgY29udGFpbmluZyBib3RoIGsgYW5kIHRvdF93aXRoaW5zcwplbGJvd19kZiA8LSBkYXRhLmZyYW1lKAogIGsgPSAxOjEwLAogIHRvdF93aXRoaW5zcyA9IHRvdF93aXRoaW5zcwopCgojIFBsb3QgdGhlIGVsYm93IHBsb3QKZ2dwbG90KGVsYm93X2RmLCBhZXMoeCA9IGssIHkgPSB0b3Rfd2l0aGluc3MpKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjEwKQpgYGAKV2UgaGF2ZSBsZWFybmVkIGhvdyB0byBjcmVhdGUgYW5kIHZpc3VhbGl6ZSBlbGJvdyBwbG90cyBhcyBhIHRvb2wgZm9yIGZpbmRpbmcgYSDigJxnb29k4oCdIHZhbHVlIG9mIGsuIAoKV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgc2hhcnAgY2hhbmdlIGluIHRoZSBzbG9wZSBvZiB0aGlzIGxpbmUgdGhhdCBtYWtlcyBhbiBcImVsYm93XCIgc2hhcGUuIEZ1cnRoZXJtb3JlLCB0aGlzIGlzIHN1cHBvcnRlZCBieSB0aGUgcHJpb3Iga25vd2xlZGdlIHRoYXQgdGhlcmUgYXJlIHR3byB0ZWFtcyBpbiB0aGlzIGRhdGEgYW5kIGEgayBvZiAyIGlzIGRlc2lyZWQuCgojIyBTaWxob3VldHRlIGFuYWx5c2lzOiBvYnNlcnZhdGlvbiBsZXZlbCBwZXJmb3JtYW5jZQoKIyMjIFNpbGhvdWV0dGUgYW5hbHlzaXMKClNpbGhvdWV0dGUgYW5hbHlzaXMgYWxsb3dzIHlvdSB0byBjYWxjdWxhdGUgaG93IHNpbWlsYXIgZWFjaCBvYnNlcnZhdGlvbnMgaXMgd2l0aCB0aGUgY2x1c3RlciBpdCBpcyBhc3NpZ25lZCByZWxhdGl2ZSB0byBvdGhlciBjbHVzdGVycy4gVGhpcyBtZXRyaWMgKHNpbGhvdWV0dGUgd2lkdGgpIHJhbmdlcyBmcm9tIC0xIHRvIDEgZm9yIGVhY2ggb2JzZXJ2YXRpb24gaW4geW91ciBkYXRhIGFuZCBjYW4gYmUgaW50ZXJwcmV0ZWQgYXMgZm9sbG93czoKCi0JVmFsdWVzIGNsb3NlIHRvIDEgc3VnZ2VzdCB0aGF0IHRoZSBvYnNlcnZhdGlvbiBpcyB3ZWxsIG1hdGNoZWQgdG8gdGhlIGFzc2lnbmVkIGNsdXN0ZXIKLQlWYWx1ZXMgY2xvc2UgdG8gMCBzdWdnZXN0IHRoYXQgdGhlIG9ic2VydmF0aW9uIGlzIGJvcmRlcmxpbmUgbWF0Y2hlZCBiZXR3ZWVuIHR3byBjbHVzdGVycwotCVZhbHVlcyBjbG9zZSB0byAtMSBzdWdnZXN0IHRoYXQgdGhlIG9ic2VydmF0aW9ucyBtYXkgYmUgYXNzaWduZWQgdG8gdGhlIHdyb25nIGNsdXN0ZXIKCldlIHdpbGwgbGV2ZXJhZ2UgdGhlIHBhbSgpIGFuZCB0aGUgc2lsaG91ZXR0ZSgpIGZ1bmN0aW9ucyBmcm9tIHRoZSBjbHVzdGVyIGxpYnJhcnkgdG8gcGVyZm9ybSBzaWxob3VldHRlIGFuYWx5c2lzIHRvIGNvbXBhcmUgdGhlIHJlc3VsdHMgb2YgbW9kZWxzIHdpdGggYSBrIG9mIDIgYW5kIGEgayBvZiAzLgpgYGB7cn0KbGlicmFyeShjbHVzdGVyKQoKIyBHZW5lcmF0ZSBhIGstbWVhbnMgbW9kZWwgdXNpbmcgdGhlIHBhbSgpIGZ1bmN0aW9uIHdpdGggYSBrID0gMgpwYW1fazIgPC0gcGFtKGxpbmV1cCwgayA9IDIpCgojIFBsb3QgdGhlIHNpbGhvdWV0dGUgdmlzdWFsIGZvciB0aGUgcGFtX2syIG1vZGVsCnBsb3Qoc2lsaG91ZXR0ZShwYW1fazIpKQoKIyBHZW5lcmF0ZSBhIGstbWVhbnMgbW9kZWwgdXNpbmcgdGhlIHBhbSgpIGZ1bmN0aW9uIHdpdGggYSBrID0gMwpwYW1fazMgPC0gcGFtKGxpbmV1cCwgayA9IDMpCgojIFBsb3QgdGhlIHNpbGhvdWV0dGUgdmlzdWFsIGZvciB0aGUgcGFtX2szIG1vZGVsCnBsb3Qoc2lsaG91ZXR0ZShwYW1fazMpKQpgYGAKRGlkIHlvdSBub3RpY2UgdGhhdCBmb3IgayA9IDIsIG5vIG9ic2VydmF0aW9uIGhhcyBhIHNpbGhvdWV0dGUgd2lkdGggY2xvc2UgdG8gMD8gV2hhdCBhYm91dCB0aGUgZmFjdCB0aGF0IGZvciBrID0gMywgb2JzZXJ2YXRpb24gMyBpcyBjbG9zZSB0byAwIGFuZCBpcyBuZWdhdGl2ZT8gVGhpcyBzdWdnZXN0cyB0aGF0IGsgPSAzIGlzIG5vdCB0aGUgcmlnaHQgbnVtYmVyIG9mIGNsdXN0ZXJzLgoKIyMgTWFraW5nIHNlbnNlIG9mIHRoZSBLLW1lYW5zIGNsdXN0ZXJzCgojIyMgUmV2aXNpdGluZyB3aG9sZXNhbGUgZGF0YTogIkJlc3QiIGsKCldlIGV4cGxvcmVkIHdob2xlc2FsZSBkaXN0cmlidXRvciBkYXRhIGN1c3RvbWVyc19zcGVuZCB1c2luZyBoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4gVGhpcyB0aW1lIHdlIHdpbGwgYW5hbHl6ZSB0aGlzIGRhdGEgdXNpbmcgdGhlIGstbWVhbnMgY2x1c3RlcmluZyB0b29scy4KVGhlIGZpcnN0IHN0ZXAgd2lsbCBiZSB0byBkZXRlcm1pbmUgdGhlICJiZXN0IiB2YWx1ZSBvZiBrIHVzaW5nIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSB3aWR0aC4KYGBge3J9CiMgVXNlIG1hcF9kYmwgdG8gcnVuIG1hbnkgbW9kZWxzIHdpdGggdmFyeWluZyB2YWx1ZSBvZiBrCnNpbF93aWR0aCA8LSBtYXBfZGJsKDI6MTAsICBmdW5jdGlvbihrKXsKICBtb2RlbCA8LSBwYW0oeCA9IGN1c3RvbWVyc19zcGVuZCwgayA9IGspCiAgbW9kZWwkc2lsaW5mbyRhdmcud2lkdGgKfSkKCiMgR2VuZXJhdGUgYSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgYm90aCBrIGFuZCBzaWxfd2lkdGgKc2lsX2RmIDwtIGRhdGEuZnJhbWUoCiAgayA9IDI6MTAsCiAgc2lsX3dpZHRoID0gc2lsX3dpZHRoCikKCiMgUGxvdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gayBhbmQgc2lsX3dpZHRoCmdncGxvdChzaWxfZGYsIGFlcyh4ID0gaywgeSA9IHNpbF93aWR0aCkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDI6MTApCmBgYApGcm9tIHRoZSBwbG90IG5vdGljZSB0aGF0IGsgPSAyIGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIHNpbGxob3VldHRlIHdpZHRoIGFuZCBpcyB0aGUg4oCcYmVzdOKAnSB2YWx1ZSBvZiBrIHdlIHdpbGwgbW92ZSBmb3J3YXJkIHdpdGguCgojIyMgRXhwbG9yYXRpb24KCldlIGhhdmUgZm91bmQgdGhhdCBrID0gMiBoYXMgdGhlIGhpZ2hlc3QgYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoLiBXZSB3aWxsIGNvbnRpbnVlIHRvIGFuYWx5emUgdGhlIHdob2xlc2FsZSBjdXN0b21lciBkYXRhIGJ5IGJ1aWxkaW5nIGFuZCBleHBsb3JpbmcgYSBrbWVhbnMgbW9kZWwgd2l0aCAyIGNsdXN0ZXJzLgoKYGBge3J9CnNldC5zZWVkKDQyKQoKIyBCdWlsZCBhIGstbWVhbnMgbW9kZWwgZm9yIHRoZSBjdXN0b21lcnNfc3BlbmQgd2l0aCBhIGsgb2YgMgptb2RlbF9jdXN0b21lcnMgPC0ga21lYW5zKGN1c3RvbWVyc19zcGVuZCwgY2VudGVycyA9IDIpCgojIEV4dHJhY3QgdGhlIHZlY3RvciBvZiBjbHVzdGVyIGFzc2lnbm1lbnRzIGZyb20gdGhlIG1vZGVsCmNsdXN0X2N1c3RvbWVycyA8LSBtb2RlbF9jdXN0b21lcnMkY2x1c3RlcgoKIyBCdWlsZCB0aGUgc2VnbWVudF9jdXN0b21lcnMgZGF0YSBmcmFtZQpzZWdtZW50X2N1c3RvbWVycyA8LSBtdXRhdGUoY3VzdG9tZXJzX3NwZW5kLCBjbHVzdGVyID0gY2x1c3RfY3VzdG9tZXJzKQoKIyBDYWxjdWxhdGUgdGhlIHNpemUgb2YgZWFjaCBjbHVzdGVyCmNvdW50KHNlZ21lbnRfY3VzdG9tZXJzLCBjbHVzdGVyKQoKIyBDYWxjdWxhdGUgdGhlIG1lYW4gZm9yIGVhY2ggY2F0ZWdvcnkKc2VnbWVudF9jdXN0b21lcnMgJT4lIAogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSAKICBzdW1tYXJpc2VfYWxsKGxpc3QobWVhbikpCmBgYApJdCBzZWVtcyB0aGF0IGluIHRoaXMgY2FzZSBjbHVzdGVyIDEgY29uc2lzdHMgb2YgaW5kaXZpZHVhbHMgd2hvIHByb3BvcnRpb25hbGx5IHNwZW5kIG1vcmUgb24gRnJvemVuIGZvb2Qgd2hpbGUgY2x1c3RlciAyIGN1c3RvbWVycyBzcGVudCBtb3JlIG9uIE1pbGsgYW5kIEdyb2NlcnkuCgpXaGVuIHdlIGV4cGxvcmVkIHRoaXMgZGF0YSB1c2luZyBoaWVyYXJjaGljYWwgY2x1c3RlcmluZywgdGhlIG1ldGhvZCByZXN1bHRlZCBpbiA0IGNsdXN0ZXJzIHdoaWxlIHVzaW5nIGstbWVhbnMgd2UgZ290IDIuIEJvdGggb2YgdGhlc2UgcmVzdWx0cyBhcmUgdmFsaWQsIGJ1dCB3aGljaCBvbmUgaXMgYXBwcm9wcmlhdGUgZm9yIHRoaXMgd291bGQgcmVxdWlyZSBtb3JlIHN1YmplY3QgbWF0dGVyIGV4cGVydGlzZS4KCuKAnEdlbmVyYXRpbmcgY2x1c3RlcnMgaXMgYSBzY2llbmNlLCBidXQgaW50ZXJwcmV0aW5nIHRoZW0gaXMgYW4gYXJ0LuKAnQoKCgo=