Introduction
This note overviews the basic unsupervised machine learning
algorithms (also known as knowledge discovery) in which models are not
supervised using a training data set. Instead, models themselves find
the hidden patterns and insights from the given
data.
The goal of unsupervised learning is to find the underlying structure
of the data set and group that data according to similarities. Common
algorithms used in unsupervised learning include clustering, anomaly
detection, neural networks, and approaches for learning latent variable
models.
The following types of unsupervised machine learning algorithms are
commonly used in practice.
- K-means Clustering
- Hierarchical Clustering
- Anomaly Detection
- Principal Component Analysis
There are many methods to calculate this distance information; the
choice of distance measures is a critical step in clustering. It defines
how the similarity of two data points (x, y) is calculated and it will
influence the shape of the clusters. The choice of distance measures is
a critical step in clustering. It defines how the similarity of two data
points (x, y) is calculated and it will influence the shape of the
clusters.
Here are a few sites you check for these “distances”.
In the next few sections, we will describe each of these
algorithms.
K-means Clustering
K-means algorithm is an iterative algorithm that partitions the data
set into K pre-defined,
distinct, and
non-overlapping subgroups (clusters) where each data point
belongs to only one group. Data points within each subgroup are
similar while data points across the subgroups are
“different** according to a selected dissimilarity measure used in the
algorithm.
In other words, k-means clustering consists of defining clusters so
that the total intra-cluster variation (known as a total within-cluster
variation) is minimized. There are several k-means algorithms available.
The standard algorithm is to define the total within-cluster variation
as the sum of squared (SS) distances (Euclidean distances) between data
points and the corresponding centroid. To be more specific, let \(x_i\) be the data point in cluster k,
denoted by \(C_k\) and \(\mu_k\) is the center of cluster \(C_k\) (i.e. the mean value of the points
when Euclidean distance is used). The within-cluster SS is defined
by
\[
W(C_k) = \sum_{x_i\in C_k} (x_i - \mu_i)^2
\]
Each observation (\(x_i\)) is
assigned to a given cluster such that the sum of squares (SS) distance
of the observation to their assigned cluster centers \(\mu_k\) is a minimum.
We define the total within-cluster variation as follows.
\[
\mbox{TW} = \sum_{k=1}^k W(C_l) = \sum_{i=1}^k\sum_{x_k\in C_k}(x_i -
\mu_k)^2
\]
The total within-cluster sum of square measures the compactness
(i.e. goodness) of the clustering and we want it to be as small as
possible.
Two fundamental questions to answer are: (1) how many initial
clusters should be selected; (2) how to choose the initial
“centers”.
K-means clustering algorithm works in the following three steps.
Optimal Number of
Clusters Determination
Several algorithms can be used to find the optimal number of
clusters. Elbow and Silhouette algorithms are commonly used and are
implemented in R.
Elbow Method
The Elbow method gives us an idea of what a good k number of clusters
would be based on the sum of squared distance (SSE) between data points
and their assigned clusters’ centroids. We pick k at the spot where SSE
starts to flatten out and form an elbow. We’ll use the geyser dataset
and evaluate SSE for different values of k and see where the curve might
form an elbow and flatten out.
Elbow is one of the most famous methods for selecting the right value
of k. We also perform the hyper-parameter tuning to choose the best
value of k. The Elbow method is an empirical method to find out the best
value of k.
Silhouette Method
The Silhouette Method uses a similarity measure (Silhouette
coefficient) that is defined in the following
\[
S_i = \frac{b_i-a_i}{\max{ \{a_i, b_i\}}},
\]
where \(S_i\) is the silhouette
coefficient of the data point \(i\);
\(a_i\) is the average distance between
\(i\) and all the other data points in
the cluster to which \(i\) belongs, and
\(b_i\) is the average distance from
\(i\) to all clusters to which \(i\) does not belong.
We can plot the Silhouette coefficient against the pre-determined
clusters \(k\). The plot of the
silhouette is between \(-1\) to \(1\).
A high average silhouette width indicates good clustering. The
average silhouette method computes the average silhouette of
observations for different values of k. The optimal number of clusters k
is the one that maximizes the average silhouette over a range of
possible values for k.
Observe the plot and choose the k value that is closer to 1 as the
optimal number of clusters.
Steps for K-mean
Algorithm
There different versions of k-mean algorithms. The most commonly used
are three: Lloyd, McQueen and Hartigan-Wong. Lloyd algorithms is easy to
understand. We will use it to illustrate the step of K-mean clustering.
R implemeted all three algorithms in the R function
kmean()
.
Initialize
Centroids.
Initialize centroids by first shuffling the data set and then
randomly selecting K data points for the centroids without
replacement.
Update
Centroids
Updating centroids is an iterative process:
Compute the sum of the squared distance between data points and
all (initial) centroids. Assign each data point to the closest cluster
(centroid).
Compute the (updating) centroids for the clusters by taking the
average of all data points that belong to each cluster.
Keep iterating until there is no change to the centroids. i.e., the
assignment of data points to clusters isn’t changing.
The following figure illustrates how to find the updated centroids
immediately after the initial centroids.
Case Study
For illustrative purposes, we only use two numerical variables in a
simple data set that is publically available in Github https://raw.githubusercontent.com/satishgunjal/datasets/master/Mall_Customers.csv.
df = read.csv("https://raw.githubusercontent.com/satishgunjal/datasets/master/Mall_Customers.csv")
## rename the two variables and then subset the data
names(df)[names(df)=="Annual.Income..k.."] = "AnnualIncome"
names(df)[names(df)=="Spending.Score..1.100."] = "SpendingScore"
clust.data = df[, c("AnnualIncome", "SpendingScore")]
scaled.data = as.data.frame(scale(clust.data)[,1:2])
distance = get_dist(scaled.data)
fviz_dist(distance, gradient = list(low = "yellow", mid = "orange", high = "darkred"), show_labels = FALSE)
The above heatmap indicates that different clusters exist in this
data (based on the two numerical variables).
- The syntax of kmeans() is given in the following
code chunk.
k2 <- kmeans(x = scaled.data,
centers = 2,
iter.max = 10,
nstart = 25,
algorithm = "Lloyd", #"Hartigan-Wong",
trace = FALSE)
- Determination of optimal class.
We use the elbow method to find the optimal number of clusters.
wss = NULL
K = 15
for (i in 1:K){
wss[i] = kmeans(scaled.data, i, 1 )$tot.withinss
}
## elbow plot
plot(1:K, wss, type ="b",
col= "blue",
xlab="Number of Clusters",
ylab = "WSS",
main = "Elbow Plot for Selecting Optimal Number of Clusters")
From the above elbow plot, it seems that the optimal number of
clusters is 5. So select k - 5 hereafter.
- Cluster the data with 5 centroids
We will cluster the data into 5 groups and then add the cluster ID to
the data. Since only two continuous feature variables were used to
cluster the data. After we added the cluster ID to the data, we used
color coding to make a scatter plot and view the clusters.
k5 <- kmeans(x = scaled.data,
centers = 5,
iter.max = 10,
nstart = 25,
algorithm = "Lloyd", # "Hartigan-Wong",
trace = FALSE)
scaled.data$group = k5$cluster
### Plot the clusters
# Scatter plot
plot(scaled.data$AnnualIncome, scaled.data$SpendingScore,
pch = 19,
col = factor(scaled.data$group),
xlab ="Spending Score",
ylab = "Annual Income",
main = "Clustering Performance Visual Check")
# Legend
legend("topright",
legend = levels(factor(scaled.data$group)),
pch = 19,
col = factor(levels(factor(scaled.data$group))))
Hierarchical
Clustering
In this section, we introduce one of the most popular clustering
methods: ** Hierarchical Clustering **. Hierarchical clustering is an
alternative approach to k-means clustering for identifying groups in the
dataset. It does not require us to pre-specify the number of clusters to
be generated as is required by the k-means approach. Furthermore,
hierarchical clustering has an added advantage over K-means clustering
in that it results in an attractive tree-based representation of the
observations, called a dendrogram.
Types of Hierarchical
Clustering
There are two types of hierarchical clustering: agglomerative and
divisive.
Agglomerative*: An agglomerative approach begins
with each observation in a distinct (singleton) cluster, and
successively merges clusters until a stopping criterion is
satisfied.
Divisive: A divisive method begins with all
patterns in a single cluster and performs splitting until a stopping
criterion is met.
As an example, we look at how agglomerative clustering works using
five data points in the following figure.
Case Study I –
Clustering with Two Features
We still use the same data set that we used in the previous case
study of K-means clustering but will include age
variable in the data frame for following hierarchical clustering.
df = read.csv("https://pengdsci.github.io/STA551/w11/Mall_Customers.csv")
## Rename the two variables and then subset the data
names(df)[names(df)=="Annual.Income..k.."] = "AnnualIncome"
names(df)[names(df)=="Spending.Score..1.100."] = "SpendingScore"
hierarch.data = df[, c("Age", "AnnualIncome", "SpendingScore")]
Pre-processing
Operations for Clustering
There are a couple of things you should take care of before
starting.
Scaling is imperative that we normalize the scale of
feature values in order to start with the clustering process. This is
because each observation’s feature values are represented as coordinates
in n-dimensional space (n is the number of features) and then the
distances between these coordinates are calculated. If these coordinates
are not normalized, then it may lead to false results. R has functions
scale() and normalize().
Missing Value imputation is also important to deal
with missing/null/inf values in your data set beforehand. There are many
ways to deal with such values, one is to either remove them or impute
them with mean, median, mode, or use some advanced regression
techniques. R has many packages and functions to deal with missing value
imputations like impute().
Hierarchical
Clustering with R
There are different functions available in R for computing
hierarchical clustering. The commonly used functions are:
scales.hierarch = as.data.frame(hierarch.data)
distance <- dist(scales.hierarch, method = "euclidean")
# Hierarchical clustering using Complete Linkage
hc1 <- hclust(distance, method = "complete" )
# Plot the obtained dendrogram
plot(hc1, cex = 0.6, labels = FALSE, hang = -1, xlab = "", main = "Dendrogram: hierarchical clustering")
rect.hclust(hc1, k = 5, border = 2:9)
# Figured this out by coloring the labels white to the background
avg_dend_obj <- as.dendrogram(hc1)
labels_colors(avg_dend_obj) <- "white"
plot(avg_dend_obj, cex = 0.6,
labels = FALSE,
hang = -1,
xlab = "",
ylab= "Height",
main = "Dendrogram: hierarchical clustering: No X-Labels")
rect.hclust(hc1, k = 5, border = 2:9)
Determination
Optimal Number of Clusters
The determination of the optimal number of clusters is an important
and challenging problem. In hierarchical clustering, different
similarity measures impact the number of optimal clusters. We will not
discuss this topic in detail. To know more about this topic, you are
referred to the following article with examples in R https://www.jstatsoft.org/article/view/2194/798.
We can use the same elbow and silhouette methods to plot.
fviz_nbclust(scales.hierarch, FUN = hcut, method = "wss")
fviz_nbclust(scales.hierarch, FUN = hcut, method = "silhouette")
Case Study II:
Multi-class Clustering
The Iris Dataset contains four features (length and width of sepals
and petals) of 50 samples of three species of Iris (Iris setosa, Iris
virginica, and Iris versicolor). These measures were used to create a
linear discriminant model to classify the species. The dataset is often
used in data mining, classification, and clustering examples and to test
algorithms.
This 100-year-old data set has been included in the R base package.
The first few records of the data set are displayed in the following
table.
pander(head(iris))
5.1 |
3.5 |
1.4 |
0.2 |
setosa |
4.9 |
3 |
1.4 |
0.2 |
setosa |
4.7 |
3.2 |
1.3 |
0.2 |
setosa |
4.6 |
3.1 |
1.5 |
0.2 |
setosa |
5 |
3.6 |
1.4 |
0.2 |
setosa |
5.4 |
3.9 |
1.7 |
0.4 |
setosa |
We use k-means method to perform cluster analysis on
the iris data with the four numerical feature
variables.
myClusteredIris = iris
# We start with 3 clusters since we know there are 3 species in the data.
# In practice, we need relatively try different numbers of clusters and then
# use the elbow plot to determine the best number of clusters.
km.iris <- kmeans( x = myClusteredIris[, -5] , centers = 3)
clust.ID <- km.iris$cluster # extracting cluster IDs
table(clust.ID) # frequency of clusters
## clust.ID
## 1 2 3
## 50 62 38
Since this clustering task involves 4 numerical feature variables, we
cannot create a 2D plot to show the clustering performance with all four
original feature variables. However, we can so-called PCA (to be
discussed in the next section) to create two new feature variables and
then plot the new features to show the performance of the clustering
algorithm.
clusplot(iris[, -5],
clust.ID,
lines = 0,
shade = TRUE,
color = TRUE,
labels = 1,
plotchar = FALSE,
span = TRUE,
main = paste("Clusters of Iris Flowers")
)

Memory Usage of
Clustering
One of the potential issues in clustering analysis is the use of
memory. If the data size is too large,
Dimensionality
Reduction Algorithms
Like clustering methods, dimension reduction seeks and explores the
inherent structure in the data, but in this case in an unsupervised
manner or to summarize or describe data using less information.
This can be useful to visualize high-dimensional data or to simplify
data which can then be used in a supervised learning method. Many of
these methods can be adapted for use in classification and regression.
The following are the frequently used algorithms.
- Principal Component Analysis (PCA)
- Linear Discriminant Analysis (LDA)
- Quadratic Discriminant Analysis (QDA)
- Independent Component Analysis (ICA)
In this section, we introduce the most commonly used PCA.
The Logic of PCA
We use a two-variable animation as an example to illustrate the idea
of principal component analysis (PCA).
The above animated graph shows that two or more numerical feature
variables are highly correlated, the PCA can be used to aggregate the
information in the correlated feature variables by transforming them
into a set of uncorrelated new feature variables such
that the majority of the total information is captured by the first few
new feature variables.
Case Study - Iris
Data
We have used the well-known Iris Data set in clustering algorithms.
The data set has 4 correlated numerical variables(sepal width and
length, petal width and length) and a categorical variable. The four
variables measure the size of the flowers. We use PCA to see whether
reducing the number of feature variables for related modeling.
Fitting PCA model
to Iris data
We want to PCA method to reduce the dimensions from 4 (numerical
variables) to a smaller number. The R function prcomp()
to the factor loadings associated with the four numerical variables.
log.iris = log(iris[,-5]) # drop the categorical variable in the original
# data set and transform all numerical to the
# log-scale
ir.pca <- prcomp(log.iris, center = TRUE, scale = TRUE)
# summary(ir.pca)[6] # use the command to explore the possible information
# available in the output of the summary.
In the above R function, three arguments are explained in the
following.
log.iris = log of the four variables
cater = TRUE, this means the variables are centered, i.e., you move the origin of the original coordinate system to the center of the data cloud.
scale = TRUE, divide the difference between the value of each variable and its mean by the standard deviation of the corresponding variable.
Next, we find the factor loading of the above fitted PCA. We can
write an explicit system of linear transformation by using the
loadings.
kable(round(ir.pca$rotation, 2), caption="Factor loadings of the PCA")
Factor loadings of the PCA
Sepal.Length |
0.50 |
-0.45 |
0.71 |
0.19 |
Sepal.Width |
-0.30 |
-0.89 |
-0.33 |
-0.09 |
Petal.Length |
0.58 |
-0.03 |
-0.22 |
-0.79 |
Petal.Width |
0.57 |
-0.04 |
-0.58 |
0.58 |
The explicit expression of the predictive system of PC is given
by
\[
\begin{aligned}
PC_1 & = 0.50 Sepal.Length -0.30 Sepal.Width + 0.58 Petal.Length +
0.57 Petal.Width \\
PC_2 & = -0.45 Sepal.Length - 0.89 Sepal.Width -0.03 Petal.Length -
0.04 Petal.Width \\
PC_3 & = 0.71 Sepal.Length -0.33 Sepal.Width - 0.22 Petal.Length -
0.58 Petal.Width \\
PC_4 & = 0.19 Sepal.Length -0.09 Sepal.Width -0.79 Petal.Length +
0.58 Petal.Width \\
\end{aligned}
\]
The magnitude of factor loadings indicates the amount of information
that original variables contribute to the corresponding principal
components. For example, the absolute value of loadings associated with
petal width and length and sepal length in \(PC_1\) is greater than or equal to 0.5. We
can simply call \(PC_1\) the
size of iris flowers. Similarly, sepal length and width
are major contributors to \(PC_2\), we
can name \(PC_2\) as sepal
size.
Optimal number of
PCs to be retained
The object of PCA is to reduce the dimension without losing a
significant amount of information. In PCA, we look at how much total
variation is captured by each principal component. Most of the libraries
that are capable of performing PCA automatically rank the PCA based on
the variation captured by each principal component.
The following summary table gives the importance of the principal
components.
kable(summary(ir.pca)$importance, caption="The importance of each principal component")
The importance of each principal component
Standard deviation |
1.712458 |
0.9523797 |
0.3647029 |
0.165684 |
Proportion of Variance |
0.733130 |
0.2267600 |
0.0332500 |
0.006860 |
Cumulative Proportion |
0.733130 |
0.9598900 |
0.9931400 |
1.000000 |
From the above table, we can see that the first PC explains about
\(73.33\%\) of the variation. But we
first two principal components explain about \(96\%\) of the total variation. In the data
analysis, you only need to use the first two PCs that lose about \(4\%\) of the information.
We can also make a scree plot as a visual tool to show the number of
principal components to retain for future analysis.
screeplot(ir.pca,
type = "lines",
main = "Scree Plot of PCA Iris Flower Sizes")
Note that the vertical axis in the above scree plot uses the
variances of PCs. The standard deviation was used in the above summary
table.
LS0tDQp0aXRsZTogIkFuIE92ZXJ2aWV3IG9mIENvbW1vbmx5IFVzZWQgVW5zdXBlcnZpc2VkIE1MIEFsZ29yaXRobXMiDQphdXRob3I6ICJDaGVuZyBQZW5nIg0KZGF0ZTogIlNUQSA1NTEgRm91bmRhdGlvbnMgb2YgRGF0YSBTY2llbmNlICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ193aWR0aDogNg0KICAgIGZpZ19oZWlnaHQ6IDQNCiAgICBmaWdfY2FwdGlvbjogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgICBkZl9wcmludDoga2FibGUNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiA1DQogICAgZmlnX2hlaWdodDogNA0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQpkaXYjVE9DIGxpIHsNCiAgICBsaXN0LXN0eWxlOm5vbmU7DQogICAgYmFja2dyb3VuZC1pbWFnZTpub25lOw0KICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7DQogICAgYmFja2dyb3VuZC1wb3NpdGlvbjowOw0KfQ0KDQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMiB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE1cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KPC9zdHlsZT4NCmBgYA0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQoNCmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCiAgIGxpYnJhcnkoZ2dwbG90MikNCn0NCmlmICghcmVxdWlyZSgiZmFjdG9leHRyYSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJmYWN0b2V4dHJhIikNCiAgIGxpYnJhcnkoZmFjdG9leHRyYSkNCn0NCmlmICghcmVxdWlyZSgiY2x1c3RlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjbHVzdGVyIikNCiAgIGxpYnJhcnkoY2x1c3RlcikNCn0NCmlmICghcmVxdWlyZSgiZGVuZGV4dGVuZCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJkZW5kZXh0ZW5kIikNCiAgIGxpYnJhcnkoZGVuZGV4dGVuZCkNCn0NCmlmICghcmVxdWlyZSgiZ2dkZW5kcm8iKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dkZW5kcm8iKQ0KICAgbGlicmFyeShnZ2RlbmRybykNCn0NCmlmICghcmVxdWlyZSgiYnJvb20iKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiYnJvb20iKQ0KICAgbGlicmFyeShicm9vbSkNCn0NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpDQogICBsaWJyYXJ5KHBhbmRlcikNCn0NCiMga25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAiQzovVXNlcnMvNzVDUEVORy9PbmVEcml2ZSAtIFdlc3QgQ2hlc3RlciBVbml2ZXJzaXR5IG9mIFBBL0RvY3VtZW50cyIpDQojIGtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIkM6XFxTVEE0OTBcXHcwNSIpDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJywgDQogICAgICAgICAgICAgICAgICAgICAgZmlnLnBvcyA9ICdodCcpDQpgYGANCg0KDQpcDQoNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyBub3RlIG92ZXJ2aWV3cyB0aGUgYmFzaWMgdW5zdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcyAoYWxzbyBrbm93biBhcyBrbm93bGVkZ2UgZGlzY292ZXJ5KSBpbiB3aGljaCBtb2RlbHMgYXJlIG5vdCBzdXBlcnZpc2VkIHVzaW5nIGEgdHJhaW5pbmcgZGF0YSBzZXQuIEluc3RlYWQsIG1vZGVscyB0aGVtc2VsdmVzIGZpbmQgdGhlICoqaGlkZGVuIHBhdHRlcm5zKiogYW5kIGluc2lnaHRzIGZyb20gdGhlIGdpdmVuIGRhdGEuIA0KDQpUaGUgZ29hbCBvZiB1bnN1cGVydmlzZWQgbGVhcm5pbmcgaXMgdG8gZmluZCB0aGUgdW5kZXJseWluZyBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgc2V0IGFuZCBncm91cCB0aGF0IGRhdGEgYWNjb3JkaW5nIHRvIHNpbWlsYXJpdGllcy4gQ29tbW9uIGFsZ29yaXRobXMgdXNlZCBpbiB1bnN1cGVydmlzZWQgbGVhcm5pbmcgaW5jbHVkZSBjbHVzdGVyaW5nLCBhbm9tYWx5IGRldGVjdGlvbiwgbmV1cmFsIG5ldHdvcmtzLCBhbmQgYXBwcm9hY2hlcyBmb3IgbGVhcm5pbmcgbGF0ZW50IHZhcmlhYmxlIG1vZGVscy4NCg0KVGhlIGZvbGxvd2luZyB0eXBlcyBvZiB1bnN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zIGFyZSBjb21tb25seSB1c2VkIGluIHByYWN0aWNlLiANCg0KKiBLLW1lYW5zIENsdXN0ZXJpbmcNCiogSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcNCiogQW5vbWFseSBEZXRlY3Rpb24NCiogUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcw0KDQpUaGVyZSBhcmUgbWFueSBtZXRob2RzIHRvIGNhbGN1bGF0ZSB0aGlzIGRpc3RhbmNlIGluZm9ybWF0aW9uOyB0aGUgY2hvaWNlIG9mIGRpc3RhbmNlIG1lYXN1cmVzIGlzIGEgY3JpdGljYWwgc3RlcCBpbiBjbHVzdGVyaW5nLiBJdCBkZWZpbmVzIGhvdyB0aGUgc2ltaWxhcml0eSBvZiB0d28gZGF0YSBwb2ludHMgKHgsIHkpIGlzIGNhbGN1bGF0ZWQgYW5kIGl0IHdpbGwgaW5mbHVlbmNlIHRoZSBzaGFwZSBvZiB0aGUgY2x1c3RlcnMuIFRoZSBjaG9pY2Ugb2YgZGlzdGFuY2UgbWVhc3VyZXMgaXMgYSBjcml0aWNhbCBzdGVwIGluIGNsdXN0ZXJpbmcuIEl0IGRlZmluZXMgaG93IHRoZSBzaW1pbGFyaXR5IG9mIHR3byBkYXRhIHBvaW50cyAoeCwgeSkgaXMgY2FsY3VsYXRlZCBhbmQgaXQgd2lsbCBpbmZsdWVuY2UgdGhlIHNoYXBlIG9mIHRoZSBjbHVzdGVycy4gDQoNCg0KYGBge3IgZWNobz1GQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjgwJSIsIGZpZy5jYXA9IkZpZ3VyZSAxLiAgRGVtb25zdHJhdGlvbiBvZiB0aGUgY2x1c3RlcmluZyBwcm9jZXNzLiJ9DQppZiAoa25pdHI6Ojppc19sYXRleF9vdXRwdXQoKSkgew0KICBrbml0cjo6YXNpc19vdXRwdXQoJ1xcdXJse2h0dHBzOi8vZ2l0aHViLmNvbS9wZW5nZHNjaS9TVEE1NTEvYmxvYi9tYWluL3cwOC9pbWcvdzA4LWtNZWFucy1naWYuZ2lmfScpDQp9IGVsc2Ugew0KICBrbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1nL3cwOC1rTWVhbnMtZ2lmLmdpZiIpDQp9DQpgYGANCg0KDQogSGVyZSBhcmUgYSBmZXcgc2l0ZXMgeW91IGNoZWNrIGZvciB0aGVzZSAiZGlzdGFuY2VzIi4NCg0KKiA8aHR0cHM6Ly9lbGtpLXByb2plY3QuZ2l0aHViLmlvL2FsZ29yaXRobXMvZGlzdGFuY2VzPg0KKiA8aHR0cHM6Ly9pZWVleHBsb3JlLmllZWUub3JnL3N0YW1wL3N0YW1wLmpzcD9hcm51bWJlcj02ODUzMzM4Pg0KKiA8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1NpbWlsYXJpdHlNZWFzdXJlcy9TaW1pbGFyaXR5TWVhc3VyZXMucGRmPg0KDQoNCkluIHRoZSBuZXh0IGZldyBzZWN0aW9ucywgd2Ugd2lsbCBkZXNjcmliZSBlYWNoIG9mIHRoZXNlIGFsZ29yaXRobXMuIA0KDQojIEstbWVhbnMgQ2x1c3RlcmluZw0KDQpLLW1lYW5zIGFsZ29yaXRobSBpcyBhbiBpdGVyYXRpdmUgYWxnb3JpdGhtIHRoYXQgcGFydGl0aW9ucyB0aGUgZGF0YSBzZXQgaW50byBLIDxmb250IGNvbG9yID0gInJlZCI+KnByZS1kZWZpbmVkKjwvZm9udD4sIDxmb250IGNvbG9yID0gImJsdWUiPiBkaXN0aW5jdDwvZm9udD4sIGFuZCA8Zm9udCBjb2xvciA9ICJkZWVwcGluayI+IG5vbi1vdmVybGFwcGluZzwvZm9udD4gc3ViZ3JvdXBzIChjbHVzdGVycykgd2hlcmUgZWFjaCBkYXRhIHBvaW50IGJlbG9uZ3MgdG8gb25seSBvbmUgZ3JvdXAuIERhdGEgcG9pbnRzIHdpdGhpbiBlYWNoIHN1Ymdyb3VwIGFyZSAqKnNpbWlsYXIqKiB3aGlsZSBkYXRhIHBvaW50cyBhY3Jvc3MgdGhlIHN1Ymdyb3VwcyBhcmUgImRpZmZlcmVudCoqIGFjY29yZGluZyB0byBhIHNlbGVjdGVkIGRpc3NpbWlsYXJpdHkgbWVhc3VyZSB1c2VkIGluIHRoZSBhbGdvcml0aG0uDQoNCkluIG90aGVyIHdvcmRzLCBrLW1lYW5zIGNsdXN0ZXJpbmcgY29uc2lzdHMgb2YgZGVmaW5pbmcgY2x1c3RlcnMgc28gdGhhdCB0aGUgdG90YWwgaW50cmEtY2x1c3RlciB2YXJpYXRpb24gKGtub3duIGFzIGEgdG90YWwgd2l0aGluLWNsdXN0ZXIgdmFyaWF0aW9uKSBpcyBtaW5pbWl6ZWQuIFRoZXJlIGFyZSBzZXZlcmFsIGstbWVhbnMgYWxnb3JpdGhtcyBhdmFpbGFibGUuIFRoZSBzdGFuZGFyZCBhbGdvcml0aG0gaXMgdG8gZGVmaW5lIHRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciB2YXJpYXRpb24gYXMgdGhlIHN1bSBvZiBzcXVhcmVkIChTUykgZGlzdGFuY2VzIChFdWNsaWRlYW4gZGlzdGFuY2VzKSBiZXR3ZWVuIGRhdGEgcG9pbnRzIGFuZCB0aGUgY29ycmVzcG9uZGluZyBjZW50cm9pZC4gVG8gYmUgbW9yZSBzcGVjaWZpYywgbGV0ICR4X2kkIGJlIHRoZSBkYXRhIHBvaW50IGluIGNsdXN0ZXIgaywgZGVub3RlZCBieSAkQ19rJCBhbmQgJFxtdV9rJCBpcyB0aGUgY2VudGVyIG9mIGNsdXN0ZXIgJENfayQgKGkuZS4gdGhlIG1lYW4gdmFsdWUgb2YgdGhlIHBvaW50cyB3aGVuIEV1Y2xpZGVhbiBkaXN0YW5jZSBpcyB1c2VkKS4gVGhlIHdpdGhpbi1jbHVzdGVyIFNTIGlzIGRlZmluZWQgYnkNCg0KJCQNClcoQ19rKSA9IFxzdW1fe3hfaVxpbiBDX2t9ICh4X2kgLSBcbXVfaSleMg0KJCQNCg0KPiBFYWNoIG9ic2VydmF0aW9uICgkeF9pJCkgaXMgYXNzaWduZWQgdG8gYSBnaXZlbiBjbHVzdGVyIHN1Y2ggdGhhdCB0aGUgc3VtIG9mIHNxdWFyZXMgKFNTKSBkaXN0YW5jZSBvZiB0aGUgb2JzZXJ2YXRpb24gdG8gdGhlaXIgYXNzaWduZWQgY2x1c3RlciBjZW50ZXJzICRcbXVfayQgaXMgYSBtaW5pbXVtLg0KDQpXZSBkZWZpbmUgdGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHZhcmlhdGlvbiBhcyBmb2xsb3dzLg0KDQokJA0KXG1ib3h7VFd9ID0gXHN1bV97az0xfV5rIFcoQ19sKSA9IFxzdW1fe2k9MX1ea1xzdW1fe3hfa1xpbiBDX2t9KHhfaSAtIFxtdV9rKV4yDQokJA0KDQo+IFRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlIG1lYXN1cmVzIHRoZSBjb21wYWN0bmVzcyAoaS5lLiBnb29kbmVzcykgb2YgdGhlIGNsdXN0ZXJpbmcgYW5kIHdlIHdhbnQgaXQgdG8gYmUgYXMgc21hbGwgYXMgcG9zc2libGUuDQoNClR3byBmdW5kYW1lbnRhbCBxdWVzdGlvbnMgdG8gYW5zd2VyIGFyZTogKDEpIGhvdyBtYW55IGluaXRpYWwgY2x1c3RlcnMgc2hvdWxkIGJlIHNlbGVjdGVkOyAoMikgaG93IHRvIGNob29zZSB0aGUgaW5pdGlhbCAiY2VudGVycyIuIA0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iODAlIiwgZmlnLmNhcD0iRmlndXJlIDIuIGlsbHVzdHJhdGlvbiBvZiBjbHVzdGVyIGFuYWx5c2lzLiJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA4LWstTWVhbnMuanBnIikNCmBgYA0KDQoNCkstbWVhbnMgY2x1c3RlcmluZyBhbGdvcml0aG0gd29ya3MgaW4gdGhlIGZvbGxvd2luZyB0aHJlZSBzdGVwcy4gDQoNCg0KIyMgT3B0aW1hbCBOdW1iZXIgb2YgQ2x1c3RlcnMgRGV0ZXJtaW5hdGlvbg0KDQpTZXZlcmFsIGFsZ29yaXRobXMgY2FuIGJlIHVzZWQgdG8gZmluZCB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMuIEVsYm93IGFuZCBTaWxob3VldHRlIGFsZ29yaXRobXMgYXJlIGNvbW1vbmx5IHVzZWQgYW5kIGFyZSBpbXBsZW1lbnRlZCBpbiBSLg0KDQoNCioqRWxib3cgTWV0aG9kKioNCg0KVGhlIEVsYm93IG1ldGhvZCBnaXZlcyB1cyBhbiBpZGVhIG9mIHdoYXQgYSBnb29kIGsgbnVtYmVyIG9mIGNsdXN0ZXJzIHdvdWxkIGJlIGJhc2VkIG9uIHRoZSBzdW0gb2Ygc3F1YXJlZCBkaXN0YW5jZSAoU1NFKSBiZXR3ZWVuIGRhdGEgcG9pbnRzIGFuZCB0aGVpciBhc3NpZ25lZCBjbHVzdGVyc+KAmSBjZW50cm9pZHMuIFdlIHBpY2sgayBhdCB0aGUgc3BvdCB3aGVyZSBTU0Ugc3RhcnRzIHRvIGZsYXR0ZW4gb3V0IGFuZCBmb3JtIGFuIGVsYm93LiBXZeKAmWxsIHVzZSB0aGUgZ2V5c2VyIGRhdGFzZXQgYW5kIGV2YWx1YXRlIFNTRSBmb3IgZGlmZmVyZW50IHZhbHVlcyBvZiBrIGFuZCBzZWUgd2hlcmUgdGhlIGN1cnZlIG1pZ2h0IGZvcm0gYW4gZWxib3cgYW5kIGZsYXR0ZW4gb3V0Lg0KDQpFbGJvdyBpcyBvbmUgb2YgdGhlIG1vc3QgZmFtb3VzIG1ldGhvZHMgZm9yIHNlbGVjdGluZyB0aGUgcmlnaHQgdmFsdWUgb2Ygay4gV2UgYWxzbyBwZXJmb3JtIHRoZSBoeXBlci1wYXJhbWV0ZXIgdHVuaW5nIHRvIGNob29zZSB0aGUgYmVzdCB2YWx1ZSBvZiBrLiBUaGUgRWxib3cgbWV0aG9kIGlzIGFuIGVtcGlyaWNhbCBtZXRob2QgdG8gZmluZCBvdXQgdGhlIGJlc3QgdmFsdWUgb2Ygay4gDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI4MCUiLCBmaWcuY2FwPSJGaWd1cmUgMy4gKGEpIEEgdmlzdWFsIGN1cnZlIHdpdGggYW4gZXhwbGljaXQgZWxib3cgcG9pbnQuIChiKSBBIHZpc3VhbCBjdXJ2ZSBiZWluZyBmYWlybHkgc21vb3RoIHdpdGggYW4gYW1iaWd1b3VzIGVsYm93IHBvaW50LiJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA4LWVsYm93LmpwZyIpDQpgYGANCg0KDQoqKlNpbGhvdWV0dGUgTWV0aG9kKioNCg0KVGhlIFNpbGhvdWV0dGUgTWV0aG9kIHVzZXMgYSBzaW1pbGFyaXR5IG1lYXN1cmUgKFNpbGhvdWV0dGUgY29lZmZpY2llbnQpIHRoYXQgaXMgZGVmaW5lZCBpbiB0aGUgZm9sbG93aW5nDQoNCiQkDQpTX2kgPSBcZnJhY3tiX2ktYV9pfXtcbWF4eyBce2FfaSwgYl9pXH19fSwNCiQkDQoNCndoZXJlICRTX2kkIGlzIHRoZSBzaWxob3VldHRlIGNvZWZmaWNpZW50IG9mIHRoZSBkYXRhIHBvaW50ICRpJDsgJGFfaSQgaXMgdGhlIGF2ZXJhZ2UgZGlzdGFuY2UgYmV0d2VlbiAkaSQgYW5kIGFsbCB0aGUgb3RoZXIgZGF0YSBwb2ludHMgaW4gdGhlIGNsdXN0ZXIgdG8gd2hpY2ggJGkkIGJlbG9uZ3MsIGFuZCAkYl9pJCBpcyB0aGUgYXZlcmFnZSBkaXN0YW5jZSBmcm9tICRpJCB0byBhbGwgY2x1c3RlcnMgdG8gd2hpY2ggJGkkIGRvZXMgbm90IGJlbG9uZy4NCg0KDQpXZSBjYW4gcGxvdCB0aGUgU2lsaG91ZXR0ZSBjb2VmZmljaWVudCBhZ2FpbnN0IHRoZSBwcmUtZGV0ZXJtaW5lZCBjbHVzdGVycyAkayQuIFRoZSBwbG90IG9mIHRoZSBzaWxob3VldHRlIGlzIGJldHdlZW4gJC0xJCB0byAkMSQuIA0KDQpBIGhpZ2ggYXZlcmFnZSBzaWxob3VldHRlIHdpZHRoIGluZGljYXRlcyBnb29kIGNsdXN0ZXJpbmcuIFRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgbWV0aG9kIGNvbXB1dGVzIHRoZSBhdmVyYWdlIHNpbGhvdWV0dGUgb2Ygb2JzZXJ2YXRpb25zIGZvciBkaWZmZXJlbnQgdmFsdWVzIG9mIGsuIFRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyBrIGlzIHRoZSBvbmUgdGhhdCBtYXhpbWl6ZXMgdGhlIGF2ZXJhZ2Ugc2lsaG91ZXR0ZSBvdmVyIGEgcmFuZ2Ugb2YgcG9zc2libGUgdmFsdWVzIGZvciBrLg0KDQpPYnNlcnZlIHRoZSBwbG90IGFuZCBjaG9vc2UgdGhlIGsgdmFsdWUgdGhhdCBpcyBjbG9zZXIgdG8gMSBhcyB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMuDQoNCiMjIFN0ZXBzIGZvciBLLW1lYW4gQWxnb3JpdGhtDQoNClRoZXJlIGRpZmZlcmVudCB2ZXJzaW9ucyBvZiBrLW1lYW4gYWxnb3JpdGhtcy4gVGhlIG1vc3QgY29tbW9ubHkgdXNlZCBhcmUgdGhyZWU6IExsb3lkLCBNY1F1ZWVuIGFuZCBIYXJ0aWdhbi1Xb25nLiBMbG95ZCBhbGdvcml0aG1zIGlzIGVhc3kgdG8gdW5kZXJzdGFuZC4gV2Ugd2lsbCB1c2UgaXQgdG8gaWxsdXN0cmF0ZSB0aGUgc3RlcCBvZiBLLW1lYW4gY2x1c3RlcmluZy4gUiBpbXBsZW1ldGVkIGFsbCB0aHJlZSBhbGdvcml0aG1zIGluIHRoZSBSIGZ1bmN0aW9uIGBrbWVhbigpYC4NCg0KIyMjIEluaXRpYWxpemUgQ2VudHJvaWRzLg0KDQpJbml0aWFsaXplIGNlbnRyb2lkcyBieSBmaXJzdCBzaHVmZmxpbmcgdGhlIGRhdGEgc2V0IGFuZCB0aGVuIHJhbmRvbWx5IHNlbGVjdGluZyBLIGRhdGEgcG9pbnRzIGZvciB0aGUgY2VudHJvaWRzIHdpdGhvdXQgcmVwbGFjZW1lbnQuDQoNCiMjIyBVcGRhdGUgQ2VudHJvaWRzDQoNClVwZGF0aW5nIGNlbnRyb2lkcyBpcyBhbiBpdGVyYXRpdmUgcHJvY2VzczoNCg0KMS4gQ29tcHV0ZSB0aGUgc3VtIG9mIHRoZSBzcXVhcmVkIGRpc3RhbmNlIGJldHdlZW4gZGF0YSBwb2ludHMgYW5kIGFsbCAoaW5pdGlhbCkgY2VudHJvaWRzLiBBc3NpZ24gZWFjaCBkYXRhIHBvaW50IHRvIHRoZSBjbG9zZXN0IGNsdXN0ZXIgKGNlbnRyb2lkKS4NCg0KMi4gQ29tcHV0ZSB0aGUgKHVwZGF0aW5nKSBjZW50cm9pZHMgZm9yIHRoZSBjbHVzdGVycyBieSB0YWtpbmcgdGhlIGF2ZXJhZ2Ugb2YgYWxsIGRhdGEgcG9pbnRzIHRoYXQgYmVsb25nIHRvIGVhY2ggY2x1c3Rlci4NCg0KS2VlcCBpdGVyYXRpbmcgdW50aWwgdGhlcmUgaXMgbm8gY2hhbmdlIHRvIHRoZSBjZW50cm9pZHMuIGkuZS4sICB0aGUgYXNzaWdubWVudCBvZiBkYXRhIHBvaW50cyB0byBjbHVzdGVycyBpc27igJl0IGNoYW5naW5nLg0KDQoNClRoZSBmb2xsb3dpbmcgZmlndXJlIGlsbHVzdHJhdGVzIGhvdyB0byBmaW5kIHRoZSB1cGRhdGVkIGNlbnRyb2lkcyBpbW1lZGlhdGVseSBhZnRlciB0aGUgaW5pdGlhbCBjZW50cm9pZHMuDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI4MCUiLCBmaWcuY2FwPSJGaWd1cmUgNC4gVXBkYXRpbmcgY2VudHJvaWRzIGluIHRoZSBwcm9jZXNzIG9mIGZpbmRpbmcgdGhlIGZpbmFsIGNlbnRyb2lkcyJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA4LUstbWVhbnMtaXRlcmF0aW9uLmpwZyIpDQpgYGANCg0KDQojIyBTb21lIFJlbWFya3Mgb24gSy1tZWFucw0KDQoxLiBLLW1lYW5zIGNsdXN0ZXJpbmcgYXNzdW1lcyBudW1lcmljYWwgZmVhdHVyZXMgc2luY2UgdGhlIEV1Y2xpZGVhbiBkaXN0YW5jZSBpcyB1c2VkIHRvIGRlZmluZSBzaW1pbGFyaXR5IG1lYXN1cmVzLg0KDQoyLiBLLU1lYW5zIGNsdXN0ZXJpbmcgcGVyZm9ybXMgd2VsbCBvbmx5IGZvciBhIGNvbnZleCBzZXQgb2YgY2x1c3RlcnMgYW5kIG5vdCBmb3Igbm9uLWNvbnZleCBzZXRzLg0KDQozLiBSZWNlbnQgZGV2ZWxvcG1lbnQgYWxsb3dzIGNhdGVnb3JpY2FsIGZlYXR1cmUgdmFyaWFibGVzIHdpdGggbm9uLUV1Y2xpZGVhbiBkaXN0YW5jZS4NCg0KNC4gVGhlIGstbWVhbnMgYWxnb3JpdGhtIGRvZXMgbm90IGd1YXJhbnRlZSBmaW5kaW5nIHRoZSBvcHRpbWFsIHNvbHV0aW9uLiBrLW1lYW5zIGlzIGEgZmFpcmx5IHNpbXBsZSBzZXF1ZW5jZSBvZiB0YXNrcyBhbmQgaXRzIGNsdXN0ZXJpbmcgcXVhbGl0eSBkZXBlbmRzIGEgbG90IG9uIHR3byBmYWN0b3JzOiB0aGUgbnVtYmVyIG9mIGsgY2x1c3RlcnMgYW5kIGluaXRpYWwgY2VudHJvaWRzLg0KDQoNCiMjIENhc2UgU3R1ZHkNCg0KRm9yIGlsbHVzdHJhdGl2ZSBwdXJwb3Nlcywgd2Ugb25seSB1c2UgdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMgaW4gYSBzaW1wbGUgZGF0YSBzZXQgdGhhdCBpcyBwdWJsaWNhbGx5IGF2YWlsYWJsZSBpbiBHaXRodWIgPGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zYXRpc2hndW5qYWwvZGF0YXNldHMvbWFzdGVyL01hbGxfQ3VzdG9tZXJzLmNzdj4uDQoNCmBgYHtyfQ0KZGYgPSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3NhdGlzaGd1bmphbC9kYXRhc2V0cy9tYXN0ZXIvTWFsbF9DdXN0b21lcnMuY3N2IikNCiMjIHJlbmFtZSB0aGUgdHdvIHZhcmlhYmxlcyBhbmQgdGhlbiBzdWJzZXQgdGhlIGRhdGENCm5hbWVzKGRmKVtuYW1lcyhkZik9PSJBbm51YWwuSW5jb21lLi5rLi4iXSA9ICJBbm51YWxJbmNvbWUiDQpuYW1lcyhkZilbbmFtZXMoZGYpPT0iU3BlbmRpbmcuU2NvcmUuLjEuMTAwLiJdID0gIlNwZW5kaW5nU2NvcmUiDQpjbHVzdC5kYXRhID0gZGZbLCBjKCJBbm51YWxJbmNvbWUiLCAiU3BlbmRpbmdTY29yZSIpXQ0Kc2NhbGVkLmRhdGEgPSBhcy5kYXRhLmZyYW1lKHNjYWxlKGNsdXN0LmRhdGEpWywxOjJdKQ0KYGBgDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NSwgIGZpZy5jYXA9IkZpZ3VyZSA1OiBIZWF0bWFwIHJlcHJlc2VudGF0aW9uIG9mIHBvdGVudGlhbCBjbHVzdGVycyJ9DQpkaXN0YW5jZSA9IGdldF9kaXN0KHNjYWxlZC5kYXRhKQ0KZnZpel9kaXN0KGRpc3RhbmNlLCBncmFkaWVudCA9IGxpc3QobG93ID0gInllbGxvdyIsIG1pZCA9ICJvcmFuZ2UiLCBoaWdoID0gImRhcmtyZWQiKSwgc2hvd19sYWJlbHMgPSBGQUxTRSkNCmBgYA0KDQpUaGUgYWJvdmUgaGVhdG1hcCBpbmRpY2F0ZXMgdGhhdCBkaWZmZXJlbnQgY2x1c3RlcnMgZXhpc3QgaW4gdGhpcyBkYXRhIChiYXNlZCBvbiB0aGUgdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMpLg0KDQoNCiogVGhlIHN5bnRheCBvZiAqKmttZWFucygpKiogaXMgZ2l2ZW4gaW4gdGhlIGZvbGxvd2luZyBjb2RlIGNodW5rLg0KDQpgYGB7cn0NCmsyIDwtIGttZWFucyh4ID0gc2NhbGVkLmRhdGEsIA0KICAgICAgICAgICAgIGNlbnRlcnMgPSAyLCANCiAgICAgICAgICAgICBpdGVyLm1heCA9IDEwLA0KICAgICAgICAgICAgIG5zdGFydCA9IDI1LA0KICAgICAgICAgICAgIGFsZ29yaXRobSA9ICJMbG95ZCIsICMiSGFydGlnYW4tV29uZyIsDQogICAgICAgICAgICAgdHJhY2UgPSBGQUxTRSkNCmBgYA0KDQoqIERldGVybWluYXRpb24gb2Ygb3B0aW1hbCBjbGFzcy4NCg0KV2UgdXNlIHRoZSBlbGJvdyBtZXRob2QgdG8gZmluZCB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMuIA0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9IkZpZ3VyZSA2OiBFbGJvdyBwbG90IGZvciBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycy4ifQ0Kd3NzID0gTlVMTA0KSyA9IDE1DQpmb3IgKGkgaW4gMTpLKXsNCiAgd3NzW2ldID0ga21lYW5zKHNjYWxlZC5kYXRhLCBpLCAxICkkdG90LndpdGhpbnNzDQogfQ0KIyMgZWxib3cgcGxvdA0KcGxvdCgxOkssIHdzcywgdHlwZSA9ImIiLA0KICAgICAgICAgIGNvbD0gImJsdWUiLA0KICAgICAgICAgIHhsYWI9Ik51bWJlciBvZiBDbHVzdGVycyIsDQogICAgICAgICAgeWxhYiA9ICJXU1MiLA0KICAgICAgICAgIG1haW4gPSAiRWxib3cgUGxvdCBmb3IgU2VsZWN0aW5nIE9wdGltYWwgTnVtYmVyIG9mIENsdXN0ZXJzIikNCmBgYA0KDQpGcm9tIHRoZSBhYm92ZSBlbGJvdyBwbG90LCBpdCBzZWVtcyB0aGF0IHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyBpcyA1LiBTbyBzZWxlY3QgayAtIDUgaGVyZWFmdGVyLg0KDQoqIENsdXN0ZXIgdGhlIGRhdGEgd2l0aCA1IGNlbnRyb2lkcw0KDQpXZSB3aWxsIGNsdXN0ZXIgdGhlIGRhdGEgaW50byA1IGdyb3VwcyBhbmQgdGhlbiBhZGQgdGhlIGNsdXN0ZXIgSUQgdG8gdGhlIGRhdGEuIFNpbmNlIG9ubHkgdHdvIGNvbnRpbnVvdXMgZmVhdHVyZSB2YXJpYWJsZXMgd2VyZSB1c2VkIHRvIGNsdXN0ZXIgdGhlIGRhdGEuIEFmdGVyIHdlIGFkZGVkIHRoZSBjbHVzdGVyIElEIHRvIHRoZSBkYXRhLCB3ZSB1c2VkIGNvbG9yIGNvZGluZyB0byBtYWtlIGEgc2NhdHRlciBwbG90IGFuZCB2aWV3IHRoZSBjbHVzdGVycy4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NSwgIGZpZy5jYXA9ICJGaWd1cmUgNy4gRmluYWwgY2x1c3RlciByZXN1bHRzOiB2aXN1YWwgaW5zcGVjdGlvbiJ9DQprNSA8LSBrbWVhbnMoeCA9IHNjYWxlZC5kYXRhLCANCiAgICAgICAgICAgICBjZW50ZXJzID0gNSwgDQogICAgICAgICAgICAgaXRlci5tYXggPSAxMCwNCiAgICAgICAgICAgICBuc3RhcnQgPSAyNSwNCiAgICAgICAgICAgICBhbGdvcml0aG0gPSAiTGxveWQiLCAjICJIYXJ0aWdhbi1Xb25nIiwNCiAgICAgICAgICAgICB0cmFjZSA9IEZBTFNFKQ0Kc2NhbGVkLmRhdGEkZ3JvdXAgPSBrNSRjbHVzdGVyDQojIyMgUGxvdCB0aGUgY2x1c3RlcnMNCiMgU2NhdHRlciBwbG90DQpwbG90KHNjYWxlZC5kYXRhJEFubnVhbEluY29tZSwgc2NhbGVkLmRhdGEkU3BlbmRpbmdTY29yZSwNCiAgICAgcGNoID0gMTksDQogICAgIGNvbCA9IGZhY3RvcihzY2FsZWQuZGF0YSRncm91cCksDQogICAgIHhsYWIgPSJTcGVuZGluZyBTY29yZSIsDQogICAgIHlsYWIgPSAiQW5udWFsIEluY29tZSIsDQogICAgIG1haW4gPSAiQ2x1c3RlcmluZyBQZXJmb3JtYW5jZSBWaXN1YWwgQ2hlY2siKQ0KDQojIExlZ2VuZA0KbGVnZW5kKCJ0b3ByaWdodCIsDQogICAgICAgbGVnZW5kID0gbGV2ZWxzKGZhY3RvcihzY2FsZWQuZGF0YSRncm91cCkpLA0KICAgICAgIHBjaCA9IDE5LA0KICAgICAgIGNvbCA9IGZhY3RvcihsZXZlbHMoZmFjdG9yKHNjYWxlZC5kYXRhJGdyb3VwKSkpKQ0KYGBgDQoNCiMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBpbnRyb2R1Y2Ugb25lIG9mIHRoZSBtb3N0IHBvcHVsYXIgY2x1c3RlcmluZyBtZXRob2RzOiAqKiBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyAqKi4gIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGlzIGFuIGFsdGVybmF0aXZlIGFwcHJvYWNoIHRvIGstbWVhbnMgY2x1c3RlcmluZyBmb3IgaWRlbnRpZnlpbmcgZ3JvdXBzIGluIHRoZSBkYXRhc2V0LiBJdCBkb2VzIG5vdCByZXF1aXJlIHVzIHRvIHByZS1zcGVjaWZ5IHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgdG8gYmUgZ2VuZXJhdGVkIGFzIGlzIHJlcXVpcmVkIGJ5IHRoZSBrLW1lYW5zIGFwcHJvYWNoLiBGdXJ0aGVybW9yZSwgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgaGFzIGFuIGFkZGVkIGFkdmFudGFnZSBvdmVyIEstbWVhbnMgY2x1c3RlcmluZyBpbiB0aGF0IGl0IHJlc3VsdHMgaW4gYW4gYXR0cmFjdGl2ZSB0cmVlLWJhc2VkIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBvYnNlcnZhdGlvbnMsIGNhbGxlZCBhIGRlbmRyb2dyYW0uDQoNCiMjIFR5cGVzIG9mIEhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nDQoNClRoZXJlIGFyZSB0d28gdHlwZXMgb2YgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmc6IGFnZ2xvbWVyYXRpdmUgYW5kIGRpdmlzaXZlLiANCg0KKiAqKkFnZ2xvbWVyYXRpdmUqKio6IEFuIGFnZ2xvbWVyYXRpdmUgYXBwcm9hY2ggYmVnaW5zIHdpdGggZWFjaCBvYnNlcnZhdGlvbiBpbiBhIGRpc3RpbmN0IChzaW5nbGV0b24pIGNsdXN0ZXIsIGFuZCBzdWNjZXNzaXZlbHkgbWVyZ2VzIGNsdXN0ZXJzIHVudGlsIGEgc3RvcHBpbmcgY3JpdGVyaW9uIGlzIHNhdGlzZmllZC4NCg0KKiAqKkRpdmlzaXZlKio6IEEgZGl2aXNpdmUgbWV0aG9kIGJlZ2lucyB3aXRoIGFsbCBwYXR0ZXJucyBpbiBhIHNpbmdsZSBjbHVzdGVyIGFuZCBwZXJmb3JtcyBzcGxpdHRpbmcgdW50aWwgYSBzdG9wcGluZyBjcml0ZXJpb24gaXMgbWV0Lg0KDQpgYGB7ciBlY2hvPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iODAlIiwgZmlnLmNhcD0iRmlndXJlIDguIElsbHVzdHJhdGlvbiBvZiB0eXBlcyBvZiBoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4ifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3cwOC10eXBlcy1wZi1oaXJhcmNoaWNhbC1jbHVzdGVyaW5nLmpwZyIpDQpgYGANCg0KQXMgYW4gZXhhbXBsZSwgd2UgbG9vayBhdCBob3cgYWdnbG9tZXJhdGl2ZSBjbHVzdGVyaW5nIHdvcmtzIHVzaW5nIGZpdmUgZGF0YSBwb2ludHMgaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUuDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5hbGlnbj0nY2VudGVyJywgIG91dC53aWR0aD0iODAlIiwgZmlnLmNhcD0iRmlndXJlIDkuIElsbHVzdHJhdGlvbiBvZiBzdGVwcyBvZiBhZ2dsb21lcmF0aXZlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLiJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA4LUFnbGxvbWVyYXRpdmUtQ2x1c3RlcmluZy5qcGciKQ0KYGBgDQoNClwNCg0KIyMgQ2FzZSBTdHVkeSBJIOKAkyBDbHVzdGVyaW5nIHdpdGggVHdvIEZlYXR1cmVzDQoNCldlIHN0aWxsIHVzZSB0aGUgc2FtZSBkYXRhIHNldCB0aGF0IHdlIHVzZWQgaW4gdGhlIHByZXZpb3VzIGNhc2Ugc3R1ZHkgb2YgSy1tZWFucyBjbHVzdGVyaW5nIGJ1dCB3aWxsIGluY2x1ZGUgKiphZ2UqKiB2YXJpYWJsZSBpbiB0aGUgZGF0YSBmcmFtZSBmb3IgZm9sbG93aW5nIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLg0KDQpgYGB7cn0NCmRmID0gcmVhZC5jc3YoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1MS93MTEvTWFsbF9DdXN0b21lcnMuY3N2IikNCiMjIFJlbmFtZSB0aGUgdHdvIHZhcmlhYmxlcyBhbmQgdGhlbiBzdWJzZXQgdGhlIGRhdGENCm5hbWVzKGRmKVtuYW1lcyhkZik9PSJBbm51YWwuSW5jb21lLi5rLi4iXSA9ICJBbm51YWxJbmNvbWUiDQpuYW1lcyhkZilbbmFtZXMoZGYpPT0iU3BlbmRpbmcuU2NvcmUuLjEuMTAwLiJdID0gIlNwZW5kaW5nU2NvcmUiDQpoaWVyYXJjaC5kYXRhID0gZGZbLCBjKCJBZ2UiLCAiQW5udWFsSW5jb21lIiwgIlNwZW5kaW5nU2NvcmUiKV0NCmBgYA0KDQoNClwNCg0KIyMjIFByZS1wcm9jZXNzaW5nIE9wZXJhdGlvbnMgZm9yIENsdXN0ZXJpbmcNCg0KVGhlcmUgYXJlIGEgY291cGxlIG9mIHRoaW5ncyB5b3Ugc2hvdWxkIHRha2UgY2FyZSBvZiBiZWZvcmUgc3RhcnRpbmcuDQoNCioqU2NhbGluZyoqIGlzIGltcGVyYXRpdmUgdGhhdCB3ZSBub3JtYWxpemUgdGhlIHNjYWxlIG9mIGZlYXR1cmUgdmFsdWVzIGluIG9yZGVyIHRvIHN0YXJ0IHdpdGggdGhlIGNsdXN0ZXJpbmcgcHJvY2Vzcy4gVGhpcyBpcyBiZWNhdXNlIGVhY2ggb2JzZXJ2YXRpb24ncyBmZWF0dXJlIHZhbHVlcyBhcmUgcmVwcmVzZW50ZWQgYXMgY29vcmRpbmF0ZXMgaW4gbi1kaW1lbnNpb25hbCBzcGFjZSAobiBpcyB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzKSBhbmQgdGhlbiB0aGUgZGlzdGFuY2VzIGJldHdlZW4gdGhlc2UgY29vcmRpbmF0ZXMgYXJlIGNhbGN1bGF0ZWQuIElmIHRoZXNlIGNvb3JkaW5hdGVzIGFyZSBub3Qgbm9ybWFsaXplZCwgdGhlbiBpdCBtYXkgbGVhZCB0byBmYWxzZSByZXN1bHRzLiBSIGhhcyBmdW5jdGlvbnMgKipzY2FsZSgpKiogYW5kICoqbm9ybWFsaXplKCkqKi4NCg0KDQoqKk1pc3NpbmcgVmFsdWUgaW1wdXRhdGlvbioqIGlzIGFsc28gaW1wb3J0YW50IHRvIGRlYWwgd2l0aCBtaXNzaW5nL251bGwvaW5mIHZhbHVlcyBpbiB5b3VyIGRhdGEgc2V0IGJlZm9yZWhhbmQuIFRoZXJlIGFyZSBtYW55IHdheXMgdG8gZGVhbCB3aXRoIHN1Y2ggdmFsdWVzLCBvbmUgaXMgdG8gZWl0aGVyIHJlbW92ZSB0aGVtIG9yIGltcHV0ZSB0aGVtIHdpdGggbWVhbiwgbWVkaWFuLCBtb2RlLCBvciB1c2Ugc29tZSBhZHZhbmNlZCByZWdyZXNzaW9uIHRlY2huaXF1ZXMuIFIgaGFzIG1hbnkgcGFja2FnZXMgYW5kIGZ1bmN0aW9ucyB0byBkZWFsIHdpdGggbWlzc2luZyB2YWx1ZSBpbXB1dGF0aW9ucyBsaWtlICoqaW1wdXRlKCkqKi4NCg0KIyMjIEhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nIHdpdGggUg0KDQpUaGVyZSBhcmUgZGlmZmVyZW50IGZ1bmN0aW9ucyBhdmFpbGFibGUgaW4gUiBmb3IgY29tcHV0aW5nIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLiBUaGUgY29tbW9ubHkgdXNlZCBmdW5jdGlvbnMgYXJlOg0KDQoqICoqaGNsdXN0KCkqKiBbaW4gc3RhdHMgcGFja2FnZV0gYW5kICoqYWduZXMoKSoqICBbaW4gY2x1c3RlciBwYWNrYWdlXSBmb3IgYWdnbG9tZXJhdGl2ZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyAoSEMpLg0KDQoqICoqZGlhbmEoKSoqIFtpbiBjbHVzdGVyIHBhY2thZ2VdIGZvciBkaXZpc2l2ZSBIQy4NCg0KDQoNCmBgYHtyLCBmaWcuYWxpZ24gPSAnY2VudGVyJywgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NSwgICBmaWcuY2FwPSAiRmlndXJlIDEwLiBEZW5kcm9ncmFtOiBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyJ9DQpzY2FsZXMuaGllcmFyY2ggPSBhcy5kYXRhLmZyYW1lKGhpZXJhcmNoLmRhdGEpDQpkaXN0YW5jZSA8LSBkaXN0KHNjYWxlcy5oaWVyYXJjaCwgbWV0aG9kID0gImV1Y2xpZGVhbiIpDQojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHVzaW5nIENvbXBsZXRlIExpbmthZ2UNCmhjMSA8LSBoY2x1c3QoZGlzdGFuY2UsIG1ldGhvZCA9ICJjb21wbGV0ZSIgKQ0KIyBQbG90IHRoZSBvYnRhaW5lZCBkZW5kcm9ncmFtDQpwbG90KGhjMSwgY2V4ID0gMC42LCBsYWJlbHMgPSBGQUxTRSwgaGFuZyA9IC0xLCB4bGFiID0gIiIsIG1haW4gPSAiRGVuZHJvZ3JhbTogaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmciKQ0KcmVjdC5oY2x1c3QoaGMxLCBrID0gNSwgYm9yZGVyID0gMjo5KQ0KYGBgDQoNCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTUsICBmaWcuY2FwPSJGaWd1cmUgMTEuIERlbmRyb2dyYW06IGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nOiBObyBYLUxhYmVscyJ9DQojIEZpZ3VyZWQgdGhpcyBvdXQgYnkgY29sb3JpbmcgdGhlIGxhYmVscyB3aGl0ZSB0byB0aGUgYmFja2dyb3VuZA0KYXZnX2RlbmRfb2JqIDwtIGFzLmRlbmRyb2dyYW0oaGMxKQ0KbGFiZWxzX2NvbG9ycyhhdmdfZGVuZF9vYmopIDwtICJ3aGl0ZSINCnBsb3QoYXZnX2RlbmRfb2JqLCBjZXggPSAwLjYsIA0KICAgICBsYWJlbHMgPSBGQUxTRSwgDQogICAgIGhhbmcgPSAtMSwgDQogICAgIHhsYWIgPSAiIiwgDQogICAgIHlsYWI9ICJIZWlnaHQiLA0KICAgICBtYWluID0gIkRlbmRyb2dyYW06IGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nOiBObyBYLUxhYmVscyIpDQpyZWN0LmhjbHVzdChoYzEsIGsgPSA1LCBib3JkZXIgPSAyOjkpDQpgYGANCg0KIyMjIERldGVybWluYXRpb24gT3B0aW1hbCBOdW1iZXIgb2YgQ2x1c3RlcnMNCg0KVGhlIGRldGVybWluYXRpb24gb2YgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGlzIGFuIGltcG9ydGFudCBhbmQgY2hhbGxlbmdpbmcgcHJvYmxlbS4gSW4gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcsIGRpZmZlcmVudCBzaW1pbGFyaXR5IG1lYXN1cmVzIGltcGFjdCB0aGUgbnVtYmVyIG9mIG9wdGltYWwgY2x1c3RlcnMuIFdlIHdpbGwgbm90IGRpc2N1c3MgdGhpcyB0b3BpYyBpbiBkZXRhaWwuIFRvIGtub3cgbW9yZSBhYm91dCB0aGlzIHRvcGljLCB5b3UgYXJlIHJlZmVycmVkIHRvIHRoZSBmb2xsb3dpbmcgYXJ0aWNsZSB3aXRoIGV4YW1wbGVzIGluIFIgPGh0dHBzOi8vd3d3LmpzdGF0c29mdC5vcmcvYXJ0aWNsZS92aWV3LzIxOTQvNzk4Pi4NCg0KV2UgY2FuIHVzZSB0aGUgc2FtZSBlbGJvdyBhbmQgc2lsaG91ZXR0ZSBtZXRob2RzIHRvIHBsb3QuIA0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01LCAgZmlnLmNhcD0gIkZpZ3VyZSAxMi4gRWxib3cgcGxvdDogT3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMifQ0KZnZpel9uYmNsdXN0KHNjYWxlcy5oaWVyYXJjaCwgRlVOID0gaGN1dCwgbWV0aG9kID0gIndzcyIpDQpgYGANCg0KYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01LCBmaWcuY2FwPSAiRmlndXJlIDEzLiBTaWxob3VldHRlIHBsb3Q6IE9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIn0NCmZ2aXpfbmJjbHVzdChzY2FsZXMuaGllcmFyY2gsIEZVTiA9IGhjdXQsIG1ldGhvZCA9ICJzaWxob3VldHRlIikNCmBgYA0KDQojIyMgRXh0cmFjdGluZyBDbHVzdGVyIElEDQoNClRoZSBhYm92ZSBlbGJvdyBwbG90IGluZGljYXRlcyB0aGF0IGNob29zaW5nIDQgY2x1c3RlcnMgaXMgYXBwcm9wcmlhdGUuIE5leHQsIHdlIHBlcmZvcm0gYSA0LWNsdXN0ZXIgYW5hbHlzaXMgYW5kIGV4dHJhY3QgdGhlIGNsdXN0ZXIgSUQgdG8gYWRkIHRoZW0gdG8gdGhlIGRhdGEgZnJhbWUuICoqVGhpcyBjbHVzdGVyIElEIGNvdWxkIGJlIHVzZWQgYXMgYSBuZXcgZmVhdHVyZSB2YXJpYWJsZSBpbiBzdWJzZXF1ZW50IG1vZGVsaW5nKiouDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcicsZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NSwgIGZpZy5jYXA9ICJGaWd1cmUgMTQuIFZpc3VhbCBjaGVjayB0aGUgcmVzdWx0aW5nIGNsdXN0ZXJzIG9idGFpbmVkIGZyb20gYWdnbG9tZXJhdGl2ZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4ifQ0KaGM0IDwtIGhjbHVzdChkaXN0YW5jZSwgbWV0aG9kID0gImNvbXBsZXRlIiApDQpncm91cCA9IGN1dHJlZShoYzQsIGsgPSA2KQ0Kc2NhbGVzLmhpZXJhcmNoJGdyb3VwID0gZ3JvdXANCiMjIA0KcGxvdChzY2FsZXMuaGllcmFyY2gkQW5udWFsSW5jb21lLCBzY2FsZXMuaGllcmFyY2gkU3BlbmRpbmdTY29yZSwNCiAgICAgcGNoID0gMTksDQogICAgIGNvbCA9IGZhY3RvcihzY2FsZXMuaGllcmFyY2gkZ3JvdXApLA0KICAgICB4bGFiID0iU3BlbmRpbmcgU2NvcmUiLA0KICAgICB5bGFiID0gIkFubnVhbCBJbmNvbWUiLA0KICAgICBtYWluID0gIkhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nIFBlcmZvcm1hbmNlIFZpc3VhbCBDaGVjayIpDQoNCiMjIExlZ2VuZA0KbGVnZW5kKCJ0b3ByaWdodCIsDQogICAgICAgbGVnZW5kID0gbGV2ZWxzKGZhY3RvcihzY2FsZXMuaGllcmFyY2gkZ3JvdXApKSwNCiAgICAgICBwY2ggPSAxOSwNCiAgICAgICBjb2wgPSBmYWN0b3IobGV2ZWxzKGZhY3RvcihzY2FsZXMuaGllcmFyY2gkZ3JvdXApKSkpDQpgYGANCg0KXA0KDQojIyBDYXNlIFN0dWR5IElJOiBNdWx0aS1jbGFzcyBDbHVzdGVyaW5nDQoNClRoZSBJcmlzIERhdGFzZXQgY29udGFpbnMgZm91ciBmZWF0dXJlcyAobGVuZ3RoIGFuZCB3aWR0aCBvZiBzZXBhbHMgYW5kIHBldGFscykgb2YgNTAgc2FtcGxlcyBvZiB0aHJlZSBzcGVjaWVzIG9mIElyaXMgKElyaXMgc2V0b3NhLCBJcmlzIHZpcmdpbmljYSwgYW5kIElyaXMgdmVyc2ljb2xvcikuIFRoZXNlIG1lYXN1cmVzIHdlcmUgdXNlZCB0byBjcmVhdGUgYSBsaW5lYXIgZGlzY3JpbWluYW50IG1vZGVsIHRvIGNsYXNzaWZ5IHRoZSBzcGVjaWVzLiBUaGUgZGF0YXNldCBpcyBvZnRlbiB1c2VkIGluIGRhdGEgbWluaW5nLCBjbGFzc2lmaWNhdGlvbiwgYW5kIGNsdXN0ZXJpbmcgZXhhbXBsZXMgYW5kIHRvIHRlc3QgYWxnb3JpdGhtcy4NCg0KYGBge3IgZWNobz1GQUxTRSwgZmlnLmNhcD0iRmlndXJlIDE1LiBJcmlzIGRhdGEgc2V0OiB2YXJpYWJsZXMgaWxsdXN0cmF0aW9uIC0gcGVkYWwgYW5kIHNlcGFsIiwgb3V0LndpZHRoPSI2MCUiLCAgZmlnLmFsaWduID0gImNlbnRlciJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA4LWlyaXMuanBnIikNCmBgYA0KDQpUaGlzIDEwMC15ZWFyLW9sZCBkYXRhIHNldCBoYXMgYmVlbiBpbmNsdWRlZCBpbiB0aGUgUiBiYXNlIHBhY2thZ2UuIFRoZSBmaXJzdCBmZXcgcmVjb3JkcyBvZiB0aGUgZGF0YSBzZXQgYXJlIGRpc3BsYXllZCBpbiB0aGUgZm9sbG93aW5nIHRhYmxlLg0KDQpgYGB7cn0NCnBhbmRlcihoZWFkKGlyaXMpKQ0KYGBgDQoNCldlIHVzZSAqKmstbWVhbnMqKiBtZXRob2QgdG8gcGVyZm9ybSBjbHVzdGVyIGFuYWx5c2lzIG9uIHRoZSAqKmlyaXMgZGF0YSoqIHdpdGggdGhlIGZvdXIgbnVtZXJpY2FsIGZlYXR1cmUgdmFyaWFibGVzLg0KDQpgYGB7cn0NCm15Q2x1c3RlcmVkSXJpcyA9IGlyaXMNCiMgV2Ugc3RhcnQgd2l0aCAzIGNsdXN0ZXJzIHNpbmNlIHdlIGtub3cgdGhlcmUgYXJlIDMgc3BlY2llcyBpbiB0aGUgZGF0YS4NCiMgSW4gcHJhY3RpY2UsIHdlIG5lZWQgcmVsYXRpdmVseSB0cnkgZGlmZmVyZW50IG51bWJlcnMgb2YgY2x1c3RlcnMgYW5kIHRoZW4NCiMgdXNlIHRoZSBlbGJvdyBwbG90IHRvIGRldGVybWluZSB0aGUgYmVzdCBudW1iZXIgb2YgY2x1c3RlcnMuDQprbS5pcmlzIDwtIGttZWFucyggeCA9IG15Q2x1c3RlcmVkSXJpc1ssIC01XSAsIGNlbnRlcnMgPSAzKSAgDQpjbHVzdC5JRCA8LSBrbS5pcmlzJGNsdXN0ZXIgICAgICAgICMgZXh0cmFjdGluZyBjbHVzdGVyIElEcw0KDQp0YWJsZShjbHVzdC5JRCkgICAgICAgICAgICAgICAgICAgICMgZnJlcXVlbmN5IG9mIGNsdXN0ZXJzDQpgYGANCg0KU2luY2UgdGhpcyBjbHVzdGVyaW5nIHRhc2sgaW52b2x2ZXMgNCBudW1lcmljYWwgZmVhdHVyZSB2YXJpYWJsZXMsIHdlIGNhbm5vdCBjcmVhdGUgYSAyRCBwbG90IHRvIHNob3cgdGhlIGNsdXN0ZXJpbmcgcGVyZm9ybWFuY2Ugd2l0aCBhbGwgZm91ciBvcmlnaW5hbCBmZWF0dXJlIHZhcmlhYmxlcy4gSG93ZXZlciwgd2UgY2FuIHNvLWNhbGxlZCBQQ0EgKHRvIGJlIGRpc2N1c3NlZCBpbiB0aGUgbmV4dCBzZWN0aW9uKSB0byBjcmVhdGUgdHdvIG5ldyBmZWF0dXJlIHZhcmlhYmxlcyBhbmQgdGhlbiBwbG90IHRoZSBuZXcgZmVhdHVyZXMgdG8gc2hvdyB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIGNsdXN0ZXJpbmcgYWxnb3JpdGhtLiANCg0KDQoNCmBgYHtyfQ0KY2x1c3Bsb3QoaXJpc1ssIC01XSwNCiBjbHVzdC5JRCwNCiBsaW5lcyA9IDAsDQogc2hhZGUgPSBUUlVFLA0KIGNvbG9yID0gVFJVRSwNCiBsYWJlbHMgPSAxLA0KIHBsb3RjaGFyID0gRkFMU0UsDQogc3BhbiA9IFRSVUUsDQogbWFpbiA9IHBhc3RlKCJDbHVzdGVycyBvZiBJcmlzIEZsb3dlcnMiKQ0KKQ0KYGBgDQoNCiMjIE1lbW9yeSBVc2FnZSBvZiBDbHVzdGVyaW5nDQoNCk9uZSBvZiB0aGUgcG90ZW50aWFsIGlzc3VlcyBpbiBjbHVzdGVyaW5nIGFuYWx5c2lzIGlzIHRoZSB1c2Ugb2YgbWVtb3J5LiBJZiB0aGUgZGF0YSBzaXplIGlzIHRvbyBsYXJnZSwgDQoNClwNCg0KIyBEaW1lbnNpb25hbGl0eSBSZWR1Y3Rpb24gQWxnb3JpdGhtcw0KDQpMaWtlIGNsdXN0ZXJpbmcgbWV0aG9kcywgZGltZW5zaW9uIHJlZHVjdGlvbiBzZWVrcyBhbmQgZXhwbG9yZXMgdGhlIGluaGVyZW50IHN0cnVjdHVyZSBpbiB0aGUgZGF0YSwgYnV0IGluIHRoaXMgY2FzZSBpbiBhbiB1bnN1cGVydmlzZWQgbWFubmVyIG9yIHRvIHN1bW1hcml6ZSBvciBkZXNjcmliZSBkYXRhIHVzaW5nIGxlc3MgaW5mb3JtYXRpb24uDQogDQpUaGlzIGNhbiBiZSB1c2VmdWwgdG8gdmlzdWFsaXplIGhpZ2gtZGltZW5zaW9uYWwgZGF0YSBvciB0byBzaW1wbGlmeSBkYXRhIHdoaWNoIGNhbiB0aGVuIGJlIHVzZWQgaW4gYSBzdXBlcnZpc2VkIGxlYXJuaW5nIG1ldGhvZC4gTWFueSBvZiB0aGVzZSBtZXRob2RzIGNhbiBiZSBhZGFwdGVkIGZvciB1c2UgaW4gY2xhc3NpZmljYXRpb24gYW5kIHJlZ3Jlc3Npb24uIFRoZSBmb2xsb3dpbmcgYXJlIHRoZSBmcmVxdWVudGx5IHVzZWQgYWxnb3JpdGhtcy4NCg0KKglQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpDQoqCUxpbmVhciBEaXNjcmltaW5hbnQgQW5hbHlzaXMgKExEQSkNCioJUXVhZHJhdGljIERpc2NyaW1pbmFudCBBbmFseXNpcyAoUURBKQ0KKiBJbmRlcGVuZGVudCBDb21wb25lbnQgQW5hbHlzaXMgKElDQSkNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBpbnRyb2R1Y2UgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBQQ0EuDQoNCiMjIFRoZSBMb2dpYyBvZiBQQ0ENCg0KV2UgdXNlIGEgdHdvLXZhcmlhYmxlIGFuaW1hdGlvbiBhcyBhbiBleGFtcGxlIHRvIGlsbHVzdHJhdGUgdGhlIGlkZWEgb2YgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyAoUENBKS4NCg0KYGBge3IgZWNobz1GQUxTRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjE2MCUiLCBmaWcuY2FwPSJGaWd1cmUgMTYuIElsbHVzdHJhdGlvbiBQQ0EuIn0NCmlmIChrbml0cjo6OmlzX2xhdGV4X291dHB1dCgpKSB7DQogIGtuaXRyOjphc2lzX291dHB1dCgnXFx1cmx7aHR0cHM6Ly9naXRodWIuY29tL3Blbmdkc2NpL1NUQTU1MS9ibG9iL21haW4vdzA4L2ltZy93MDgtUENBLUFuaW1hdGlvbi0wMS5naWZ9JykNCn0gZWxzZSB7DQogIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA4LVBDQS1BbmltYXRpb24tMDEuZ2lmIikNCn0NCmBgYA0KDQpUaGUgYWJvdmUgYW5pbWF0ZWQgZ3JhcGggc2hvd3MgdGhhdCB0d28gb3IgbW9yZSBudW1lcmljYWwgZmVhdHVyZSB2YXJpYWJsZXMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCB0aGUgUENBIGNhbiBiZSB1c2VkIHRvIGFnZ3JlZ2F0ZSB0aGUgaW5mb3JtYXRpb24gaW4gdGhlIGNvcnJlbGF0ZWQgZmVhdHVyZSB2YXJpYWJsZXMgYnkgdHJhbnNmb3JtaW5nIHRoZW0gaW50byBhIHNldCBvZiB1bmNvcnJlbGF0ZWQgKipuZXcgZmVhdHVyZSB2YXJpYWJsZXMqKiBzdWNoIHRoYXQgdGhlIG1ham9yaXR5IG9mIHRoZSB0b3RhbCBpbmZvcm1hdGlvbiBpcyBjYXB0dXJlZCBieSB0aGUgZmlyc3QgZmV3IG5ldyBmZWF0dXJlIHZhcmlhYmxlcy4NCg0KYGBge3IgZWNobz1GQUxTRSwgZmlnLmNhcD0iRmlndXJlIDE3LiBHcmFwaGljYWwgaW50ZXJwcmV0YXRpb24gb2YgUENBIHdpdGggdHdvIGNvcnJlbGF0ZWQgdmFyaWFibGVzIiwgb3V0LndpZHRoPSI2MCUiLCAgZmlnLmFsaWduID0gImNlbnRlciJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvdzA4LTJWYXJQQ0FFeGFtcGxlLmpwZyIpDQpgYGANCg0KDQojIyBDYXNlIFN0dWR5IC0gSXJpcyBEYXRhDQoNCldlIGhhdmUgdXNlZCB0aGUgd2VsbC1rbm93biBJcmlzIERhdGEgc2V0IGluIGNsdXN0ZXJpbmcgYWxnb3JpdGhtcy4gVGhlIGRhdGEgc2V0IGhhcyA0IGNvcnJlbGF0ZWQgbnVtZXJpY2FsIHZhcmlhYmxlcyhzZXBhbCB3aWR0aCBhbmQgbGVuZ3RoLCBwZXRhbCB3aWR0aCBhbmQgbGVuZ3RoKSBhbmQgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gVGhlIGZvdXIgdmFyaWFibGVzIG1lYXN1cmUgdGhlIHNpemUgb2YgdGhlIGZsb3dlcnMuIFdlIHVzZSBQQ0EgdG8gc2VlIHdoZXRoZXIgcmVkdWNpbmcgdGhlIG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcyBmb3IgcmVsYXRlZCBtb2RlbGluZy4gIA0KDQoNCg0KIyMjIEZpdHRpbmcgUENBIG1vZGVsIHRvIElyaXMgZGF0YQ0KDQpXZSB3YW50IHRvIFBDQSBtZXRob2QgdG8gcmVkdWNlIHRoZSBkaW1lbnNpb25zIGZyb20gNCAobnVtZXJpY2FsIHZhcmlhYmxlcykgdG8gYSBzbWFsbGVyIG51bWJlci4gVGhlIFIgZnVuY3Rpb24gKipwcmNvbXAoKSoqIHRvIHRoZSBmYWN0b3IgbG9hZGluZ3MgYXNzb2NpYXRlZCB3aXRoIHRoZSBmb3VyIG51bWVyaWNhbCB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KbG9nLmlyaXMgPSBsb2coaXJpc1ssLTVdKSAgICMgZHJvcCB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgaW4gdGhlIG9yaWdpbmFsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZGF0YSBzZXQgYW5kIHRyYW5zZm9ybSBhbGwgbnVtZXJpY2FsIHRvIHRoZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbG9nLXNjYWxlDQppci5wY2EgPC0gcHJjb21wKGxvZy5pcmlzLCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpDQojIHN1bW1hcnkoaXIucGNhKVs2XSAgICMgdXNlIHRoZSBjb21tYW5kIHRvIGV4cGxvcmUgdGhlIHBvc3NpYmxlIGluZm9ybWF0aW9uDQogICAgICAgICAgICAgICAgICAgICAgICMgYXZhaWxhYmxlIGluIHRoZSBvdXRwdXQgb2YgdGhlIHN1bW1hcnkuDQpgYGANCg0KSW4gdGhlIGFib3ZlIFIgZnVuY3Rpb24sIHRocmVlIGFyZ3VtZW50cyBhcmUgZXhwbGFpbmVkIGluIHRoZSBmb2xsb3dpbmcuDQoNCmBgYHt9DQpsb2cuaXJpcyA9IGxvZyBvZiB0aGUgZm91ciB2YXJpYWJsZXMNCmNhdGVyID0gVFJVRSwgdGhpcyBtZWFucyB0aGUgdmFyaWFibGVzIGFyZSBjZW50ZXJlZCwgaS5lLiwgIHlvdSBtb3ZlIHRoZSBvcmlnaW4gb2YgdGhlIG9yaWdpbmFsIGNvb3JkaW5hdGUgc3lzdGVtIHRvIHRoZSBjZW50ZXIgb2YgdGhlIGRhdGEgY2xvdWQuDQpzY2FsZSA9IFRSVUUsIGRpdmlkZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB2YWx1ZSBvZiBlYWNoIHZhcmlhYmxlIGFuZCBpdHMgbWVhbiBieSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBjb3JyZXNwb25kaW5nIHZhcmlhYmxlLiANCmBgYA0KDQpOZXh0LCB3ZSBmaW5kIHRoZSBmYWN0b3IgbG9hZGluZyBvZiB0aGUgYWJvdmUgZml0dGVkIFBDQS4gV2UgY2FuIHdyaXRlIGFuIGV4cGxpY2l0IHN5c3RlbSBvZiBsaW5lYXIgdHJhbnNmb3JtYXRpb24gYnkgdXNpbmcgdGhlIGxvYWRpbmdzLg0KDQpgYGB7cn0NCmthYmxlKHJvdW5kKGlyLnBjYSRyb3RhdGlvbiwgMiksIGNhcHRpb249IkZhY3RvciBsb2FkaW5ncyBvZiB0aGUgUENBIikNCmBgYA0KVGhlIGV4cGxpY2l0IGV4cHJlc3Npb24gb2YgdGhlIHByZWRpY3RpdmUgc3lzdGVtIG9mIFBDIGlzIGdpdmVuIGJ5DQoNCiQkDQogXGJlZ2lue2FsaWduZWR9DQpQQ18xICYgPSAwLjUwIFNlcGFsLkxlbmd0aCAtMC4zMCBTZXBhbC5XaWR0aCArIDAuNTggUGV0YWwuTGVuZ3RoICsgMC41NyBQZXRhbC5XaWR0aCAgXFwNClBDXzIgJiA9IC0wLjQ1IFNlcGFsLkxlbmd0aCAtIDAuODkgU2VwYWwuV2lkdGggLTAuMDMgUGV0YWwuTGVuZ3RoIC0gMC4wNCBQZXRhbC5XaWR0aCBcXA0KUENfMyAmID0gMC43MSBTZXBhbC5MZW5ndGggLTAuMzMgU2VwYWwuV2lkdGggLSAwLjIyICBQZXRhbC5MZW5ndGggLSAwLjU4IFBldGFsLldpZHRoIFxcDQpQQ180ICYgPSAwLjE5IFNlcGFsLkxlbmd0aCAtMC4wOSBTZXBhbC5XaWR0aCAtMC43OSBQZXRhbC5MZW5ndGggKyAwLjU4IFBldGFsLldpZHRoICAgXFwNClxlbmR7YWxpZ25lZH0NCiQkDQoNClRoZSBtYWduaXR1ZGUgb2YgZmFjdG9yIGxvYWRpbmdzIGluZGljYXRlcyB0aGUgYW1vdW50IG9mIGluZm9ybWF0aW9uIHRoYXQgb3JpZ2luYWwgdmFyaWFibGVzIGNvbnRyaWJ1dGUgdG8gdGhlIGNvcnJlc3BvbmRpbmcgcHJpbmNpcGFsIGNvbXBvbmVudHMuIEZvciBleGFtcGxlLCB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgbG9hZGluZ3MgYXNzb2NpYXRlZCB3aXRoIHBldGFsIHdpZHRoIGFuZCBsZW5ndGggYW5kIHNlcGFsIGxlbmd0aCBpbiAkUENfMSQgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDAuNS4gV2UgY2FuIHNpbXBseSBjYWxsICRQQ18xJCB0aGUgKipzaXplKiogb2YgaXJpcyBmbG93ZXJzLiBTaW1pbGFybHksIHNlcGFsIGxlbmd0aCBhbmQgd2lkdGggYXJlIG1ham9yIGNvbnRyaWJ1dG9ycyB0byAkUENfMiQsIHdlIGNhbiBuYW1lICRQQ18yJCBhcyAqKnNlcGFsIHNpemUqKi4NCg0KDQojIyMgT3B0aW1hbCBudW1iZXIgb2YgUENzIHRvIGJlIHJldGFpbmVkDQoNClRoZSBvYmplY3Qgb2YgUENBIGlzIHRvIHJlZHVjZSB0aGUgZGltZW5zaW9uIHdpdGhvdXQgbG9zaW5nIGEgc2lnbmlmaWNhbnQgYW1vdW50IG9mIGluZm9ybWF0aW9uLiBJbiBQQ0EsIHdlIGxvb2sgYXQgaG93IG11Y2ggdG90YWwgdmFyaWF0aW9uIGlzIGNhcHR1cmVkIGJ5IGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudC4gTW9zdCBvZiB0aGUgbGlicmFyaWVzIHRoYXQgYXJlIGNhcGFibGUgb2YgcGVyZm9ybWluZyBQQ0EgYXV0b21hdGljYWxseSByYW5rIHRoZSBQQ0EgYmFzZWQgb24gdGhlIHZhcmlhdGlvbiBjYXB0dXJlZCBieSBlYWNoIHByaW5jaXBhbCBjb21wb25lbnQuDQoNClRoZSBmb2xsb3dpbmcgc3VtbWFyeSB0YWJsZSBnaXZlcyB0aGUgaW1wb3J0YW5jZSBvZiB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMuDQoNCmBgYHtyfQ0Ka2FibGUoc3VtbWFyeShpci5wY2EpJGltcG9ydGFuY2UsIGNhcHRpb249IlRoZSBpbXBvcnRhbmNlIG9mIGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCIpDQpgYGANCg0KRnJvbSB0aGUgYWJvdmUgdGFibGUsIHdlIGNhbiBzZWUgdGhhdCB0aGUgZmlyc3QgUEMgZXhwbGFpbnMgYWJvdXQgJDczLjMzXCUkIG9mIHRoZSB2YXJpYXRpb24uIEJ1dCB3ZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgZXhwbGFpbiBhYm91dCAkOTZcJSQgb2YgdGhlIHRvdGFsIHZhcmlhdGlvbi4gSW4gdGhlIGRhdGEgYW5hbHlzaXMsIHlvdSBvbmx5IG5lZWQgdG8gdXNlIHRoZSBmaXJzdCB0d28gUENzIHRoYXQgbG9zZSBhYm91dCAkNFwlJCBvZiB0aGUgaW5mb3JtYXRpb24uDQoNCldlIGNhbiBhbHNvIG1ha2UgYSBzY3JlZSBwbG90IGFzIGEgdmlzdWFsIHRvb2wgdG8gc2hvdyB0aGUgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHJldGFpbiBmb3IgZnV0dXJlIGFuYWx5c2lzLg0KDQpgYGB7ciwgZmlnLmFsaWduPSAnY2VudGVyJywgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0iRmlndXJlIDE4LiBTY3JlZSBwbG90IG9mIFBDQSBvbiBJcmlzIERhdGEifQ0Kc2NyZWVwbG90KGlyLnBjYSwgDQogICAgICAgICAgdHlwZSA9ICJsaW5lcyIsDQogICAgICAgICAgbWFpbiA9ICJTY3JlZSBQbG90IG9mIFBDQSBJcmlzIEZsb3dlciBTaXplcyIpDQpgYGANCg0KTm90ZSB0aGF0IHRoZSB2ZXJ0aWNhbCBheGlzIGluIHRoZSBhYm92ZSBzY3JlZSBwbG90IHVzZXMgdGhlIHZhcmlhbmNlcyBvZiBQQ3MuIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gd2FzIHVzZWQgaW4gdGhlIGFib3ZlIHN1bW1hcnkgdGFibGUuDQoNCg0KDQojIyMgRXh0cmFjdGluZyBQQyBTY29yZXMNCg0KVGhlIHByZWRpY3RpdmUgcHJpbmNpcGxlIHNjb3JlcyBhcmUgdmFsdWVzIG9mIHRoZSBuZXdseSB0cmFuc2Zvcm1lZCB2YXJpYWJsZXMuIFdlIGNhbiBjaG9vc2UgdGhlIGZpcnN0IGZldyBwcmluY2lwYWwgY29tcG9uZW50cyB0byB1c2UgYXMgcmVzcG9uc2UgdmFyaWFibGVzIHRvIGRvIHJlbGV2YW50IG1vZGVsaW5nLg0KDQpUaGUgY29tbWFuZCBgaXIucGNzJHhgIGV4dHJhY3RzIHRoZSBQQyBzY29yZXMgZnJvbSB0aGUgUENBIHByb2NlZHVyZS4gVGhlc2Ugc2NvcmVzIGFyZSB0aGUgdmFsdWVzIG9mIHRoZSBuZXcgdHJhbnNmb3JtZWQgdmFyaWFibGVzLiBUaGV5IGNhbiBiZSB1c2VkIGFzIHJlc3BvbnNlIG9yIHByZWRpY3RvciB2YXJpYWJsZXMgaW4gc3RhdGlzdGljYWwgbW9kZWxzLiBUaGUgZm9sbG93aW5nIHRhYmxlIHNob3dzIHRoZSANCg0KDQpgYGB7cn0NCmthYmxlKGlyLnBjYSR4WzE6MTUsXSwgY2FwdGlvbiA9ICJUaGUgZmlyc3QgMTUgUEMgc2NvcmVzIHRyYW5zZm9ybWVkIGZyb20gdGhlIG9yaWdpbmFsIHZhcmlhYmxlLiBJbiB0aGUgYW5hbHlzaXMsIHlvdSB3YW50IHRvIGVpdGhlciB0aGUgZmlyc3QgUEMgb3IgdGhlIGZpcnN0IHR3byBQQ3MuIikNCmBgYA0KDQpBcyB0aGUgZmluYWwgc3RlcCwgd2UgcmVuYW1lIHRoZSB0d28gUENzIGFuZCB0aGVuIGFkZCB0aGUgdHdvIG5ldyB2YXJpYWJsZXMgdG8gdGhlIG9yaWdpbmFsIGRhdGEgc2V0IGZvciBmdXR1cmUgYW5hbHlzaXMuIFNpbmNlICRQQ18xJCBjYXB0dXJlcyB2YXJpYXRpb24gb2YgYm90aCBzZXBhbCBhbmQgcGVkYWwsIHdlIHJlbmFtZSAkUENfMSQgYXMgKippcmlzLnNpemUqKi4gU2ltaWxhcmx5LCB3ZSByZW5hbWUgJFBDXzIkIGFzICoqc2VwYWwuc2l6ZSoqLg0KDQpgYGB7cn0NCm15LmZpbmFsLmlyaXMuZGF0YSA9IGlyaXMNCm15LmZpbmFsLmlyaXMuZGF0YSRpcmlzLnNpemUgPSBpci5wY2EkeFssIDFdDQpteS5maW5hbC5pcmlzLmRhdGEkc2VwYWwuc2l6ZSA9IGlyLnBjYSR4WywgMl0NCiMjIHdyaXRlIHRoZSBmaW5hbCBkYXRhIHNldCB0byBhIGxvY2FsIGZvbGRlcg0Kd3JpdGUuY3N2KG15LmZpbmFsLmlyaXMuZGF0YSwgZmlsZSA9ICJDOlxcVXNlcnNcXDc1Q1BFTkdcXE9uZURyaXZlIC0gV2VzdCBDaGVzdGVyIFVuaXZlcnNpdHkgb2YgUEFcXERlc2t0b3BcXGNwZW5nXFxXQ1UtVGVhY2hpbmdcXDIwMjNTdW1tZXJcXFNUQTU1MVxcdzA4XFxGaW5hbC1JcmlzLURhdGEuY3N2IikNCmBgYA0KDQpUaGUgZm9sbG93aW5nIHNjcmVlbnNob3Qgc2hvd3MgdGhlIGZpbmFsIGRhdGEgZmlsZSB3YXMgc2F2ZWQgaW4gYSBsb2NhbCBmb2xkZXIgYW5kIHRoZSB0d28gcmVuYW1lZCBwcmluY2lwYWwgY29tcG9uZW50cyB3ZXJlIGFkZGVkIHRvIHRoZSBmaW5hbCBkYXRhIHNldC4NCg0KYGBge3IgZWNobz1GQUxTRSwgZmlnLmNhcD0iRmlndXJlIDE5LiBTY3JlZW5zaG90IG9mIHRoZSBmaW5hbCBpcmlzIGRhdGEgc2V0IHdpdGggbmV3IHZhcmlhYmxlcyBkZWZpbmVkIGJhc2VkIG9uIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cyIsIG91dC53aWR0aD0iODAlIiwgIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3cwOC13cml0ZTJsb2NhbC1mb2xkZXIuanBnIikNCmBgYA0KDQoNCg==