knitr::opts_chunk$set(echo = TRUE)
Definition of a distance
A distance function or a metric on \(\mathbb{R}^n,\:n\geq 1\), is a function \(d:\mathbb{R}^n\times\mathbb{R}^n\rightarrow \mathbb{R}\).
A distance function must satisfy some required properties or axioms.
There are three main axioms.
A1. \(d(\mathbf{x},\mathbf{y})= 0\iff \mathbf{x}=\mathbf{y}\) (identity of indiscernibles);
A2. \(d(\mathbf{x},\mathbf{y})= d(\mathbf{y},\mathbf{x})\) (symmetry);
A3. \(d(\mathbf{x},\mathbf{z})\leq d(\mathbf{x},\mathbf{y})+d(\mathbf{y},\mathbf{z})\) (triangle inequality), where \(\mathbf{x}=(x_1,\cdots,x_n)\), \(\mathbf{y}=(y_1,\cdots,y_n)\) and \(\mathbf{z}=(z_1,\cdots,z_n)\) are all vectors of \(\mathbb{R}^n\).
We should use the term dissimilarity rather than distance when not all the three axioms A1-A3 are valid.
Most of the time, we shall use, with some abuse of vocabulary, the term distance.
Exercice 1
- Prove that the three axioms A1-A3 imply the non-negativity condition: \[d(\mathbf{x},\mathbf{y})\geq 0.\]
Euclidean distance
\[d(\mathbf{x},\mathbf{y})=\sqrt{\sum_{i=1}^n (x_i-y_i)^2}.\] * A1A2 ae onbvious. * The proof of A3 is provided below.
Manhattan distance
- The Manhattan distance also called taxi-cab metric or city-block metric is defined by:
\[d(\mathbf{x},\mathbf{y})
=\sum_{i=1}^n |x_i-y_i|.
\]
- A1-A2 hold.
- A3 also holds using the fact that \(|a+b|\leq |a|+|b|\) for any reals \(a,b\).
- There exists also a weighted version of the Manhattan distance called the Canberra distance.
Manhattan distance vs Euclidean distance Graph
x = c(0, 0)
y = c(6,6)
dist(rbind(x, y), method = "euclidian")
x
y 8.485281
6*sqrt(2)
[1] 8.485281
dist(rbind(x, y), method = "manhattan")
x
y 12
Canberra distance
\[d(\mathbf{x},\mathbf{y})
=\sum_{i=1}^n \frac{|x_i-y_i|}{|x_i|+|y_i|}.\]
- Note that the term \(|x_i − y_i|/(|x_i|+|y_i|)\) is not properly defined when \(x_i=y_i=0\).
- By convention we set the ratio to be zero in that case.
- The Canberra distance is specially sensitive to small changes near zero.
x = c(0, 0)
y = c(6,6)
dist(rbind(x, y), method = "canberra")
x
y 2
6/6+6/6
[1] 2
Exercice 2
- Prove that the Canberra distance is a true distance.
Minkowski distance
- Both the Euclidian and the Manattan distances are special cases of the Minkowski distance which is defined, for \(p\geq 1\), by: \[
d(\mathbf{x},\mathbf{y})=
\left[\sum_{i=1} |x_i-y_i|^{p}\right]^{1/p}.
\]
- For \(p=1\), we get the Manhattan distance.
- For \(p=2\), we get the Euclidian distance.
- Let us also define: \[\|\mathbf{x}\|_p\equiv\left[\sum_{i=1}^n |x_i|^{p}\right]^{1/p},\] where \(\|\mathbf{\cdot}\|_p\) is known as the \(p\)-norm or Minkowski norm.
- Note that the Minkowski distance and norm are related by: \[
d(\mathbf{x},\mathbf{y})=\|\mathbf{x}-\mathbf{y}\|_p.
\]
- Conversely, we have: \[
\|\mathbf{x}\|_p=d(\mathbf{x},\mathbf{0}),
\] where \(\mathbf{0}\) is the null-vetor of \(\mathbb{R}^n\).
library("ggplot2")
x = c(0, 0)
y = c(6,6)
MinkowDist=c()
for (p in seq(1,30,.01))
{
MinkowDist=c(MinkowDist,dist(rbind(x, y), method = "minkowski", p = p))
}
ggplot(data =data.frame(x = seq(1,30,.01), y=MinkowDist ) , mapping = aes(x = x, y = y))+geom_point(size=.1,color="red")+xlim(1,11)+xlab("p")+ylab("Minkowski Distance")+ggtitle("Minkowski distance wrt p")

Chebyshev distance
- At the limit, we get the Chebyshev distance which is defined by: \[
d(\mathbf{x},\mathbf{y})=\max_{i=1,\cdots,n}(|x_i-y_i|)=\lim_{p\rightarrow\infty}
\left[\sum_{i=1} |x_i-y_i|^{p}\right]^{1/p}.
\]
- The corresponding norm is: \[
\|\mathbf{x}|_\infty=\max_{i=1,\cdots,n}(|x_i|).
\]
Minkowski inequality
The proof of the triangular inequality A3 is based on the Minkowski inequality:
For any nonnegative real numbers \(a_1,\cdots,a_n\); \(b_1,\cdots,b_n\), and for any \(p\geq 1\), we have: \[
\left[\sum_{i=1}^n (a_i+b_i)^{p}\right]^{1/p}\leq
\left[\sum_{i=1}^n a_i^{p}\right]^{1/p}
+\left[\sum_{i=1}^n b_i^{p}\right]^{1/p}.
\]
To prove that the Minkowski distance satisfies A3, notice that \[
\sum_{i=1}^n|x_i-z_i|^{p}= \sum_{i=1}^n|(x_i-y_i)+(y_i-z_i)|^{p}.
\]
Since for any reals \(x,y\), we have: \(|x+y|\leq |x|+|y|\), and using the fact that \(x^p\) is increasing in \(x\geq 0\), we obtain: \[
\sum_{i=1}^n|x_i-z_i|^{p}\leq \sum_{i=1}^n(|x_i-y_i|+|y_i-z_i|)^{p}.
\]
Applying the Minkowski inequality with \(a_i=|x_i-y_i|\) and \(b_i=|y_i-z_i|\), \(i=1,\cdots,n\), we get: \[
\sum_{i=1}^n|x_i-z_i|^{p}\leq \left(\sum_{i=1}^n |x_i-y_i|^{p}\right)^{1/p}+\left(\sum_{i=1}^n |y_i-z_i|^{p}\right)^{1/p}.
\]
Hölder inequality
- The proof of the Minkowski inequality itself requires the Hölder inequality:
- For any nonnegative real numbers \(a_1,\cdots,a_n\); \(b_1,\cdots,b_n\), and any \(p,q>1\) with \(1/p+1/q=1\), we have: \[
\sum_{i=1}^n a_ib_i\leq
\left[\sum_{i=1}^n a_i^{p}\right]^{1/p}
\left[\sum_{i=1}^n b_i^{q}\right]^{1/q}
\]
- The proof of the Hölder inequality relies on the Young inequality:
- For any \(a,b>0\), we have \[
ab\leq \frac{a^p}{p}+\frac{b^q}{q},
\] with equality occuring iff: \(a^p=b^q\).
- To prove the Young inequality, one can use the (strict) convexity of the exponential function.
- For any reals \(x,y\), we have: \[
e^{\frac{x}{p}+\frac{y}{q} }\leq \frac{e^{x}}{p}+\frac{e^{y}}{q}.
\]
- We then set: \(x=p\ln a\) and \(y=q\ln b\) to get the Young inequality.
- A good reference on inequalities is: Z. Cvetkovski, Inequalities: theorems, techniques and selected problems, 2012, Springer Science & Business Media. # Cauchy-Schwartz inequality
- Note that the triangular inequality for the Minkowski distance implies: \[
\sum_{i=1}^n |x_i|\leq
\left[\sum_{i=1}^n |x_i|^{p}\right]^{1/p}.
\]
- Note that for \(p=2\), we have \(q=2\). The Hölder inequality implies for that special case \[
\sum_{i=1}^n|x_iy_i|\leq\sqrt{\sum_{i=1}^n x_i^2}\sqrt{\sum_{i=1}^n y_i^2}.
\]
- Since the LHS od thes above inequality is greater then \(|\sum_{i=1}^nx_iy_i|\), we get the Cauchy-Schwartz inequality
\[
|\sum_{i=1}^nx_iy_i|\leq\sqrt{\sum_{i=1}^n x_i^2}\sqrt{\sum_{i=1}^n y_i^2}.
\] * Using the dot product notation called also scalar product noation: \(\mathbf{x\cdot y}=\sum_{i=1}^nx_iy_i\), and the norm notation \(\|\mathbf{\cdot}\|_2 \|\), the Cauchy-Schwart inequality is: \[
|\mathbf{x\cdot y} | \leq \|\mathbf{x}\|_2 \| \mathbf{y}\|_2.
\]
Pearson correlation distance
The Pearson correlation coefficient is a similarity measure on \(\mathbb{R}^n\) defined by: \[
\rho(\mathbf{x},\mathbf{y})=
\frac{\sum_{i=1}^n (x_i-\bar{\mathbf{x}})(y_i-\bar{\mathbf{y}})}{{\sqrt{\sum_{i=1}^n (x_i-\bar{\mathbf{x}})^2\sum_{i=1}^n (y_i-\bar{\mathbf{y}})^2}}},
\] where \(\bar{\mathbf{x}}\) is the mean of the vector \(\mathbf{x}\) defined by: \[\bar{\mathbf{x}}=\frac{1}{n}\sum_{i=1}^n x_i,\]
Note that the Pearson correlation coefficient satisfies P2 and is invariant to any positive linear transformation, i.e.: \[\rho(\alpha\mathbf{x},\mathbf{y})=\rho(\mathbf{x},\mathbf{y}),\] for any \(\alpha>0\).
The Pearson distance (or correlation distance) is defined by: \[
d(\mathbf{x},\mathbf{y})=1-\rho(\mathbf{x},\mathbf{y}).\]
Note that the Pearson distance does not satisfy A1 since \(d(\mathbf{x},\mathbf{x})=0\) for any non-zero vector \(\mathbf{x}\). It neither satisfies the triangle inequality. However, the symmetry property is fullfilled.
Cosine correlation distance
- The cosine of the angle \(\theta\) between two vectors \(\mathbf{x}\) and \(\mathbf{y}\) is a measure of similarity given by: \[
\cos(\theta)=\frac{\mathbf{x}\cdot \mathbf{y}}{\|\mathbf{x}\|_2\|\mathbf{y}\|_2}=\frac{\sum_{i=1}^n x_i y_i}{{\sqrt{\sum_{i=1}^n x_i^2\sum_{i=1}^n y_i^2}}}.
\]
- Note that the cosine of the angle between the two centred vectors \((x_1-\bar{\mathbf{x}},\cdots,x_n-\bar{\mathbf{x}})\) and \((y_1-\bar{\mathbf{y}},\cdots,y_n-\bar{\mathbf{y}})\) coincides with the Pearson correlation coefficient of \(\mathbf{x}\) and \(\mathbf{y}\).
- The cosine correlation distance is defined by: \[
d(\mathbf{x},\mathbf{y})=1-\cos(\theta).
\]
- It shares similar properties than the Pearson correlation distance. Likewise, Axioms A1 and A3 are not satisfied.
Spearman correlation distance
- To calculate the Spearman’s rank-order correlation, we need to map seperately each of the vectors to ranked data values: \[\mathbf{x}\rightarrow \text{rank}(\mathbf{x})=(x_1^r,\cdots,x_n^r).\]
- Here, \(x_i^r\) is the rank of \(x_i\) among the set of values of \(\mathbf{x}\).
- We illustrate this transformation with a simple example:
- If \(\mathbf{x}=(3, 1, 4, 15, 92)\), then the rank-order vector is \(\text{rank}(\mathbf{x})=(2,1,3,4,5)\).
x=c(3, 1, 4, 15, 92)
rank(x)
[1] 2 1 3 4 5
- The Spearman’s rank correlation of two numerical variables \(\mathbf{x}\) and \(\mathbf{y}\) is simply the Pearson correlation of the two correspnding rank-order variables \(\text{rank}(\mathbf{x})\) and \(\text{rank}(\mathbf{y})\), i.e. \(\rho(\text{rank}(\mathbf{x}),\text{rank}(\mathbf{y}))\). This measure is is useful because it is more robust against outliers than the Pearson correlation.
- If all the \(n\) ranks are distinct, it can be computed using the following formula: \[
\rho(\text{rank}(\mathbf{x}),\text{rank}(\mathbf{y}))=1-\frac{6\sum_{i=1}^n d_i^2}{n(n^2-1)},
\] where \(d_i=x_i^r-y_i^r,\:i=1,\cdots,n\).
- The spearman distance is then defined by: \[
d(\mathbf{x},\mathbf{y})=1-\rho(\text{rank}(\mathbf{x}),\text{rank}(\mathbf{y})).
\]
- It can be shown that easaly that it is not a proper distance.
- If all the \(n\) ranks are distinct, we get: \[
d(\mathbf{x},\mathbf{y})=\frac{6\sum_{i=1}^n d_i^2}{n(n^2-1)}.
\]
x=c(3, 1, 4, 15, 92)
rank(x)
[1] 2 1 3 4 5
y=c(30,2 , 9, 20, 48)
rank(y)
[1] 4 1 2 3 5
d=rank(x)-rank(y)
d
[1] -2 0 1 1 0
cor(rank(x),rank(y))
[1] 0.7
1-6*sum(d^2)/(5*(5^2-1))
[1] 0.7
Kendall tau distance
- The Kendall rank correlation coefficient is calculated from the number of correspondances between the rankings of \(\mathbf{x}\) and the rankings of \(\mathbf{y}\).
- The number of pairs of observations among \(n\) observations or values is: \[{n \choose 2} =\frac{n(n-1)}{2}.\]
- The pairs of observations \((x_{i},x_{j})\) and \((y_{i},y_{j})\) are said to be concordant if: \[\text{sign}(x_j-x_i)=\text{sign}(y_j-y_i),\] and to be discordant if: \[\text{sign}(x_j-x_i)=-\text{sign}(y_j-y_i),\] where \(\text{sign}(\cdot)\) returns \(1\) for positive numbers and \(-1\) negative numbers and \(0\) otherwise.
- If \(x_i=x_j\) or \(y_i=y_j\) (or both), there is a tie.
- The Kendall \(\tau\) coefficient is defined by (neglecting ties): \[\tau =\frac {1}{n(n-1)}\sum_{i=1}^{n}\sum_{j=1}^n\text{sign}(x_j-x_i)\text{sign}(y_j-y_i).\]
- Let \(n_c\) (resp. \(n_d\)) be the number of concordant (resp. discordant) pairs, we have \[\tau =\frac {2(n_c-n_d)}{n(n-1)}.\]
- The Kendall tau distance is then: \[d(\mathbf{x},\mathbf{y})=1-\tau. \]
- Remark: the triangular inequality may fail in cases where there are ties.
x=c(3, 1, 4, 15, 92)
y=c(30,2 , 9, 20, 48)
tau=0
for (i in 1:5)
{
tau=tau+sign(x -x[i])%*%sign(y -y[i])
}
tau=tau/(5*4)
tau
[,1]
[1,] 0.6
cor(x,y, method="kendall")
[1] 0.6
Variables standardization
- Variables are often standardized before measuring dissimilarities.
- Standardization converts the original variables into uniteless variables.
- A well known method is the z-score transformation: \[
\mathbf{x}\rightarrow (\frac{x_1-\bar{\mathbf{x}}}{s_\mathbf{x}},\cdots,\frac{x_n-\bar{\mathbf{x}}}{s_\mathbf{x}}),
\] where \(s_\mathbf{x}\) is the sample standard deviation given by: \[
s_\mathbf{x}=\frac{1}{n-1}\sum_{i=1}^n(x_i-\bar{\mathbf{x}})^2.
\]
- The transformed variable will have a mean of \(0\) and a variance of \(1\).
- The result obtained with Pearson correlation measures and standardized Euclidean distances are comparable.
- For other methods, see: Milligan, G. W., & Cooper, M. C. (1988). A study of standardization of variables in cluster analysis. Journal of classification, 5(2), 181-204.
x=c(3, 1, 4, 15, 92)
y=c(30,2 , 9, 20, 48)
(x-mean(x))/sd(x)
[1] -0.5134116 -0.5647527 -0.4877410 -0.2053646 1.7712699
scale(x)
[,1]
[1,] -0.5134116
[2,] -0.5647527
[3,] -0.4877410
[4,] -0.2053646
[5,] 1.7712699
attr(,"scaled:center")
[1] 23
attr(,"scaled:scale")
[1] 38.9551
(y-mean(y))/sd(y)
[1] 0.45263128 -1.09293895 -0.70654639 -0.09935809
[5] 1.44621214
scale(y)
[,1]
[1,] 0.45263128
[2,] -1.09293895
[3,] -0.70654639
[4,] -0.09935809
[5,] 1.44621214
attr(,"scaled:center")
[1] 21.8
attr(,"scaled:scale")
[1] 18.11629
Distance matrix computation
- We’ll use a subset of the data USArrests
- We’ll use only a by taking 15 random rows among the 50 rows in the data set.
- Next, we standardize the data using the function scale():
install.packages("FactoMineR")
Error in install.packages : Updating loaded packages
library("FactoMineR")
data("USArrests") # Loading
head(USArrests, 3) # Print the first 3 rows
set.seed(123)
ss <- sample(1:50, 15) # Take 15 random rows
df <- USArrests[ss, ] # Subset the 15 rows
df.scaled <- scale(df) # Standardize the variables
LS0tCnRpdGxlOiAiRGlzdGFuY2UgYW5kIGRpc3NpbWlsYXJpdGllcyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCgoKIyBEZWZpbml0aW9uIG9mIGEgZGlzdGFuY2UKCiogQSBkaXN0YW5jZSBmdW5jdGlvbiBvciBhIG1ldHJpYyBvbiAkXG1hdGhiYntSfV5uLFw6blxnZXEgMSQsIGlzIGEgZnVuY3Rpb24gJGQ6XG1hdGhiYntSfV5uXHRpbWVzXG1hdGhiYntSfV5uXHJpZ2h0YXJyb3cgXG1hdGhiYntSfSQuCiogQSBkaXN0YW5jZSBmdW5jdGlvbiBtdXN0IHNhdGlzZnkgc29tZSByZXF1aXJlZCBwcm9wZXJ0aWVzIG9yIGF4aW9tcy4gCiogVGhlcmUgYXJlIHRocmVlIG1haW4gYXhpb21zLgoqIEExLiAkZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPSAwXGlmZiBcbWF0aGJme3h9PVxtYXRoYmZ7eX0kIChpZGVudGl0eSBvZiBpbmRpc2Nlcm5pYmxlcyk7CiogQTIuICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9IGQoXG1hdGhiZnt5fSxcbWF0aGJme3h9KSQgKHN5bW1ldHJ5KTsKCiogQTMuICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt6fSlcbGVxIGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KStkKFxtYXRoYmZ7eX0sXG1hdGhiZnt6fSkkICAodHJpYW5nbGUgaW5lcXVhbGl0eSksCndoZXJlICRcbWF0aGJme3h9PSh4XzEsXGNkb3RzLHhfbikkLCAkXG1hdGhiZnt5fT0oeV8xLFxjZG90cyx5X24pJCBhbmQgJFxtYXRoYmZ7en09KHpfMSxcY2RvdHMsel9uKSQgYXJlIGFsbCB2ZWN0b3JzIG9mICRcbWF0aGJie1J9Xm4kLgoqIFdlIHNob3VsZCB1c2UgdGhlIHRlcm0gX2Rpc3NpbWlsYXJpdHlfIHJhdGhlciB0aGFuIF9kaXN0YW5jZV8gd2hlbiBub3QgYWxsIHRoZSB0aHJlZSBheGlvbXMgQTEtQTMgYXJlIHZhbGlkLgoqIE1vc3Qgb2YgdGhlIHRpbWUsIHdlIHNoYWxsIHVzZSwgd2l0aCBzb21lIGFidXNlIG9mIHZvY2FidWxhcnksIHRoZSB0ZXJtIGRpc3RhbmNlLgoKIyMgRXhlcmNpY2UgMQoKKiBQcm92ZSB0aGF0IHRoZSB0aHJlZSBheGlvbXMgQTEtQTMgaW1wbHkgdGhlIG5vbi1uZWdhdGl2aXR5IGNvbmRpdGlvbjogJCRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSlcZ2VxIDAuJCQKCgojIEV1Y2xpZGVhbiBkaXN0YW5jZQoKCiogSXQgaXMgZGVmaW5lZCBieToKCiQkZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPVxzcXJ0e1xzdW1fe2k9MX1ebiAoeF9pLXlfaSleMn0uJCQKKiBBMUEyIGFlIG9uYnZpb3VzLgoqIFRoZSBwcm9vZiBvZiBBMyBpcyBwcm92aWRlZCBiZWxvdy4KCgojIE1hbmhhdHRhbiBkaXN0YW5jZQoKKiBUaGUgTWFuaGF0dGFuIGRpc3RhbmNlIGFsc28gY2FsbGVkICB0YXhpLWNhYiBtZXRyaWMgb3IgY2l0eS1ibG9jayBtZXRyaWMgaXMgZGVmaW5lZCBieToKCiQkZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pCj1cc3VtX3tpPTF9Xm4gfHhfaS15X2l8LgokJAoKKiBBMS1BMiBob2xkLgoqIEEzIGFsc28gaG9sZHMgdXNpbmcgdGhlIGZhY3QgdGhhdCAkfGErYnxcbGVxIHxhfCt8YnwkIGZvciBhbnkgcmVhbHMgJGEsYiQuCiogVGhlcmUgZXhpc3RzIGFsc28gYSB3ZWlnaHRlZCB2ZXJzaW9uICBvZiB0aGUgTWFuaGF0dGFuIGRpc3RhbmNlIGNhbGxlZCB0aGUgQ2FuYmVycmEgZGlzdGFuY2UuCgpbTWFuaGF0dGFuIGRpc3RhbmNlIHZzIEV1Y2xpZGVhbiBkaXN0YW5jZSBHcmFwaF0oaHR0cHM6Ly91cGxvYWQud2lraW1lZGlhLm9yZy93aWtpcGVkaWEvY29tbW9ucy8wLzA4L01hbmhhdHRhbl9kaXN0YW5jZS5zdmcpCgpgYGB7cn0KeCA9IGMoMCwgMCkKeSA9IGMoNiw2KQpkaXN0KHJiaW5kKHgsIHkpLCBtZXRob2QgPSAiZXVjbGlkaWFuIikKNipzcXJ0KDIpCmRpc3QocmJpbmQoeCwgeSksIG1ldGhvZCA9ICJtYW5oYXR0YW4iKQpgYGAKCgojIENhbmJlcnJhIGRpc3RhbmNlCgoqIEl0IGlzIGRlZmluZWQgYnk6CgokJGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KQo9XHN1bV97aT0xfV5uIFxmcmFje3x4X2kteV9pfH17fHhfaXwrfHlfaXx9LiQkCgoqIE5vdGUgdGhhdCB0aGUgdGVybSAkfHhfaSDiiJIgeV9pfC8ofHhfaXwrfHlfaXwpJCBpcyBub3QgcHJvcGVybHkgZGVmaW5lZCB3aGVuICR4X2k9eV9pPTAkLgoqIEJ5IGNvbnZlbnRpb24gd2Ugc2V0IHRoZSByYXRpbyB0byBiZSB6ZXJvIGluIHRoYXQgY2FzZS4KKiBUaGUgQ2FuYmVycmEgZGlzdGFuY2UgaXMgc3BlY2lhbGx5IHNlbnNpdGl2ZSB0byBzbWFsbCBjaGFuZ2VzIG5lYXIgemVyby4KCgpgYGB7cn0KeCA9IGMoMCwgMCkKeSA9IGMoNiw2KQpkaXN0KHJiaW5kKHgsIHkpLCBtZXRob2QgPSAiY2FuYmVycmEiKQo2LzYrNi82CmBgYAoKCiMjIEV4ZXJjaWNlIDIKCiogUHJvdmUgdGhhdCB0aGUgQ2FuYmVycmEgZGlzdGFuY2UgaXMgYSB0cnVlIGRpc3RhbmNlLgoKIyBNaW5rb3dza2kgZGlzdGFuY2UKKiBCb3RoIHRoZSBFdWNsaWRpYW4gYW5kIHRoZSBNYW5hdHRhbiBkaXN0YW5jZXMgYXJlIHNwZWNpYWwgY2FzZXMgb2YgIHRoZSBNaW5rb3dza2kgZGlzdGFuY2Ugd2hpY2ggaXMgZGVmaW5lZCwgZm9yICRwXGdlcSAxJCwgYnk6IAokJApkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9ClxsZWZ0W1xzdW1fe2k9MX0gfHhfaS15X2l8XntwfVxyaWdodF1eezEvcH0uCiQkCiAqIEZvciAkcD0xJCwgd2UgZ2V0IHRoZSBNYW5oYXR0YW4gZGlzdGFuY2UuCiAqIEZvciAkcD0yJCwgd2UgZ2V0IHRoZSBFdWNsaWRpYW4gZGlzdGFuY2UuCiogTGV0IHVzIGFsc28gZGVmaW5lOiAKJCRcfFxtYXRoYmZ7eH1cfF9wXGVxdWl2XGxlZnRbXHN1bV97aT0xfV5uIHx4X2l8XntwfVxyaWdodF1eezEvcH0sJCQKd2hlcmUgJFx8XG1hdGhiZntcY2RvdH1cfF9wJCBpcyBrbm93biBhcyB0aGUgJHAkLW5vcm0gb3IgTWlua293c2tpIG5vcm0uCiogTm90ZSB0aGF0IHRoZSBNaW5rb3dza2kgZGlzdGFuY2UgYW5kIG5vcm0gYXJlIHJlbGF0ZWQgYnk6CiQkCmQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KT1cfFxtYXRoYmZ7eH0tXG1hdGhiZnt5fVx8X3AuCiQkCiogQ29udmVyc2VseSwgd2UgaGF2ZToKJCQKXHxcbWF0aGJme3h9XHxfcD1kKFxtYXRoYmZ7eH0sXG1hdGhiZnswfSksCiQkCndoZXJlICRcbWF0aGJmezB9JCBpcyB0aGUgbnVsbC12ZXRvciBvZiAkXG1hdGhiYntSfV5uJC4KCmBgYHtyfQpsaWJyYXJ5KCJnZ3Bsb3QyIikKeCA9IGMoMCwgMCkKeSA9IGMoNiw2KQpNaW5rb3dEaXN0PWMoKQpmb3IgKHAgaW4gc2VxKDEsMzAsLjAxKSkKewpNaW5rb3dEaXN0PWMoTWlua293RGlzdCxkaXN0KHJiaW5kKHgsIHkpLCBtZXRob2QgPSAibWlua293c2tpIiwgcCA9IHApKSAgICAgCn0KZ2dwbG90KGRhdGEgPWRhdGEuZnJhbWUoeCA9IHNlcSgxLDMwLC4wMSksIHk9TWlua293RGlzdCApICwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHkpKStnZW9tX3BvaW50KHNpemU9LjEsY29sb3I9InJlZCIpK3hsaW0oMSwxMSkreGxhYigicCIpK3lsYWIoIk1pbmtvd3NraSBEaXN0YW5jZSIpK2dndGl0bGUoIk1pbmtvd3NraSBkaXN0YW5jZSB3cnQgcCIpCmBgYAoKIyBDaGVieXNoZXYgZGlzdGFuY2UgCiogQXQgdGhlIGxpbWl0LCB3ZSBnZXQgdGhlIENoZWJ5c2hldiBkaXN0YW5jZSB3aGljaCBpcyBkZWZpbmVkIGJ5OgokJApkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9XG1heF97aT0xLFxjZG90cyxufSh8eF9pLXlfaXwpPVxsaW1fe3BccmlnaHRhcnJvd1xpbmZ0eX0KXGxlZnRbXHN1bV97aT0xfSB8eF9pLXlfaXxee3B9XHJpZ2h0XV57MS9wfS4KJCQKKiBUaGUgY29ycmVzcG9uZGluZyBub3JtIGlzOgokJApcfFxtYXRoYmZ7eH18X1xpbmZ0eT1cbWF4X3tpPTEsXGNkb3RzLG59KHx4X2l8KS4KJCQKCiMgTWlua293c2tpIGluZXF1YWxpdHkKCiogVGhlIHByb29mIG9mIHRoZSB0cmlhbmd1bGFyIGluZXF1YWxpdHkgQTMgaXMgYmFzZWQgb24gdGhlIE1pbmtvd3NraSBpbmVxdWFsaXR5OgoqIEZvciBhbnkgbm9ubmVnYXRpdmUgcmVhbCBudW1iZXJzICRhXzEsXGNkb3RzLGFfbiQ7ICRiXzEsXGNkb3RzLGJfbiQsIGFuZCBmb3IgYW55ICRwXGdlcSAxJCwgd2UgaGF2ZToKJCQKXGxlZnRbXHN1bV97aT0xfV5uIChhX2krYl9pKV57cH1ccmlnaHRdXnsxL3B9XGxlcQpcbGVmdFtcc3VtX3tpPTF9Xm4gYV9pXntwfVxyaWdodF1eezEvcH0KK1xsZWZ0W1xzdW1fe2k9MX1ebiBiX2lee3B9XHJpZ2h0XV57MS9wfS4KJCQKKiBUbyBwcm92ZSB0aGF0IHRoZSBNaW5rb3dza2kgZGlzdGFuY2Ugc2F0aXNmaWVzIEEzLCBub3RpY2UgdGhhdCAKJCQKIFxzdW1fe2k9MX1ebnx4X2ktel9pfF57cH09IFxzdW1fe2k9MX1ebnwoeF9pLXlfaSkrKHlfaS16X2kpfF57cH0uCiQkCiogU2luY2UgZm9yIGFueSByZWFscyAkeCx5JCwgd2UgaGF2ZTogJHx4K3l8XGxlcSB8eHwrfHl8JCwgYW5kIHVzaW5nIHRoZSBmYWN0IHRoYXQgJHhecCQgaXMgaW5jcmVhc2luZyBpbiAkeFxnZXEgMCQsIHdlIG9idGFpbjoKJCQKIFxzdW1fe2k9MX1ebnx4X2ktel9pfF57cH1cbGVxIFxzdW1fe2k9MX1ebih8eF9pLXlfaXwrfHlfaS16X2l8KV57cH0uCiQkCgoqIEFwcGx5aW5nIHRoZSBNaW5rb3dza2kgaW5lcXVhbGl0eSB3aXRoICRhX2k9fHhfaS15X2l8JCBhbmQgJGJfaT18eV9pLXpfaXwkLCAkaT0xLFxjZG90cyxuJCwgd2UgZ2V0OgokJAogXHN1bV97aT0xfV5ufHhfaS16X2l8XntwfVxsZXEgXGxlZnQoXHN1bV97aT0xfV5uIHx4X2kteV9pfF57cH1ccmlnaHQpXnsxL3B9K1xsZWZ0KFxzdW1fe2k9MX1ebiB8eV9pLXpfaXxee3B9XHJpZ2h0KV57MS9wfS4KJCQKCiMgSMO2bGRlciBpbmVxdWFsaXR5CgoqIFRoZSBwcm9vZiBvZiB0aGUgTWlua293c2tpIGluZXF1YWxpdHkgaXRzZWxmIHJlcXVpcmVzIHRoZSBIw7ZsZGVyIGluZXF1YWxpdHk6CiogRm9yIGFueSBub25uZWdhdGl2ZSByZWFsIG51bWJlcnMgJGFfMSxcY2RvdHMsYV9uJDsgJGJfMSxcY2RvdHMsYl9uJCwgYW5kIGFueSAkcCxxPjEkIHdpdGggJDEvcCsxL3E9MSQsIHdlIGhhdmU6CiQkClxzdW1fe2k9MX1ebiBhX2liX2lcbGVxClxsZWZ0W1xzdW1fe2k9MX1ebiBhX2lee3B9XHJpZ2h0XV57MS9wfQpcbGVmdFtcc3VtX3tpPTF9Xm4gYl9pXntxfVxyaWdodF1eezEvcX0KJCQKKiBUaGUgcHJvb2Ygb2YgdGhlIEjDtmxkZXIgaW5lcXVhbGl0eSByZWxpZXMgb24gdGhlIFlvdW5nIGluZXF1YWxpdHk6CiogRm9yIGFueSAkYSxiPjAkLCB3ZSBoYXZlCiQkCmFiXGxlcSBcZnJhY3thXnB9e3B9K1xmcmFje2JecX17cX0sCiQkCndpdGggZXF1YWxpdHkgb2NjdXJpbmcgaWZmOiAkYV5wPWJecSQuIAoqIFRvIHByb3ZlIHRoZSBZb3VuZyBpbmVxdWFsaXR5LCBvbmUgY2FuIHVzZSB0aGUgKHN0cmljdCkgY29udmV4aXR5IG9mIHRoZSBleHBvbmVudGlhbCBmdW5jdGlvbi4KKiBGb3IgYW55IHJlYWxzICR4LHkkLCB3ZSBoYXZlOgokJAplXntcZnJhY3t4fXtwfStcZnJhY3t5fXtxfSB9XGxlcSBcZnJhY3tlXnt4fX17cH0rXGZyYWN7ZV57eX19e3F9LiAKJCQKKiBXZSB0aGVuIHNldDogJHg9cFxsbiBhJCBhbmQgJHk9cVxsbiBiJCB0byBnZXQgdGhlIFlvdW5nIGluZXF1YWxpdHkuCiogQSBnb29kIHJlZmVyZW5jZSBvbiBpbmVxdWFsaXRpZXMgaXM6IFouIEN2ZXRrb3Zza2ksICBJbmVxdWFsaXRpZXM6IHRoZW9yZW1zLCB0ZWNobmlxdWVzIGFuZCBzZWxlY3RlZCBwcm9ibGVtcywgMjAxMiwgU3ByaW5nZXIgU2NpZW5jZSAmIEJ1c2luZXNzIE1lZGlhLgogIyBDYXVjaHktU2Nod2FydHogaW5lcXVhbGl0eQoqIE5vdGUgdGhhdCB0aGUgdHJpYW5ndWxhciBpbmVxdWFsaXR5IGZvciB0aGUgTWlua293c2tpIGRpc3RhbmNlIGltcGxpZXM6IAokJApcc3VtX3tpPTF9Xm4gfHhfaXxcbGVxClxsZWZ0W1xzdW1fe2k9MX1ebiB8eF9pfF57cH1ccmlnaHRdXnsxL3B9LgokJAoqIE5vdGUgdGhhdCBmb3IgJHA9MiQsIHdlIGhhdmUgJHE9MiQuIFRoZSBIw7ZsZGVyIGluZXF1YWxpdHkgaW1wbGllcyBmb3IgdGhhdCBzcGVjaWFsIGNhc2UKJCQKXHN1bV97aT0xfV5ufHhfaXlfaXxcbGVxXHNxcnR7XHN1bV97aT0xfV5uIHhfaV4yfVxzcXJ0e1xzdW1fe2k9MX1ebiB5X2leMn0uIAokJAoqIFNpbmNlIHRoZSBMSFMgb2QgdGhlcyBhYm92ZSBpbmVxdWFsaXR5IGlzIGdyZWF0ZXIgdGhlbiAkfFxzdW1fe2k9MX1ebnhfaXlfaXwkLCB3ZSBnZXQgdGhlIENhdWNoeS1TY2h3YXJ0eiBpbmVxdWFsaXR5CgokJAp8XHN1bV97aT0xfV5ueF9peV9pfFxsZXFcc3FydHtcc3VtX3tpPTF9Xm4geF9pXjJ9XHNxcnR7XHN1bV97aT0xfV5uIHlfaV4yfS4gCiQkCiogVXNpbmcgdGhlIGRvdCBwcm9kdWN0IG5vdGF0aW9uIGNhbGxlZCBhbHNvIHNjYWxhciBwcm9kdWN0IG5vYXRpb246ICRcbWF0aGJme3hcY2RvdCB5fT1cc3VtX3tpPTF9Xm54X2l5X2kkLCBhbmQgdGhlIG5vcm0gbm90YXRpb24gJFx8XG1hdGhiZntcY2RvdH1cfF8yIFx8JCwgdGhlIENhdWNoeS1TY2h3YXJ0IGluZXF1YWxpdHkgaXM6CiQkCnxcbWF0aGJme3hcY2RvdCB5fSB8IFxsZXEgXHxcbWF0aGJme3h9XHxfMiBcfCBcbWF0aGJme3l9XHxfMi4KJCQKCiMgUGVhcnNvbiBjb3JyZWxhdGlvbiBkaXN0YW5jZSAKCiogVGhlIFBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQgaXMgYSBzaW1pbGFyaXR5IG1lYXN1cmUgb24gJFxtYXRoYmJ7Un1ebiQgZGVmaW5lZCBieToKJCQKXHJobyhcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPQpcZnJhY3tcc3VtX3tpPTF9Xm4gKHhfaS1cYmFye1xtYXRoYmZ7eH19KSh5X2ktXGJhcntcbWF0aGJme3l9fSl9e3tcc3FydHtcc3VtX3tpPTF9Xm4gKHhfaS1cYmFye1xtYXRoYmZ7eH19KV4yXHN1bV97aT0xfV5uICh5X2ktXGJhcntcbWF0aGJme3l9fSleMn19fSwKJCQKd2hlcmUgJFxiYXJ7XG1hdGhiZnt4fX0kIGlzIHRoZSBtZWFuIG9mIHRoZSB2ZWN0b3IgJFxtYXRoYmZ7eH0kIGRlZmluZWQgYnk6IAokJFxiYXJ7XG1hdGhiZnt4fX09XGZyYWN7MX17bn1cc3VtX3tpPTF9Xm4geF9pLCQkCiogTm90ZSB0aGF0IHRoZSBQZWFyc29uIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IHNhdGlzZmllcyBQMiBhbmQgIGlzIGludmFyaWFudCB0byBhbnkgcG9zaXRpdmUgbGluZWFyIHRyYW5zZm9ybWF0aW9uLCBpLmUuOiAkJFxyaG8oXGFscGhhXG1hdGhiZnt4fSxcbWF0aGJme3l9KT1ccmhvKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSksJCQgZm9yIGFueSAkXGFscGhhPjAkLgoqIFRoZSBQZWFyc29uIGRpc3RhbmNlIChvciBjb3JyZWxhdGlvbiBkaXN0YW5jZSkgaXMgZGVmaW5lZCBieToKJCQKZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPTEtXHJobyhcbWF0aGJme3h9LFxtYXRoYmZ7eX0pLiQkCgoqIE5vdGUgdGhhdCB0aGUgUGVhcnNvbiBkaXN0YW5jZSBkb2VzIG5vdCBzYXRpc2Z5IEExIHNpbmNlICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt4fSk9MCQgZm9yIGFueSBub24temVybyB2ZWN0b3IgJFxtYXRoYmZ7eH0kLiBJdCBuZWl0aGVyIHNhdGlzZmllcyB0aGUgdHJpYW5nbGUgaW5lcXVhbGl0eS4gSG93ZXZlciwgdGhlIHN5bW1ldHJ5IHByb3BlcnR5IGlzIGZ1bGxmaWxsZWQuIAoKIyBDb3NpbmUgY29ycmVsYXRpb24gZGlzdGFuY2UKCiogVGhlIGNvc2luZSBvZiB0aGUgYW5nbGUgJFx0aGV0YSQgYmV0d2VlbiB0d28gdmVjdG9ycyAkXG1hdGhiZnt4fSQgYW5kICRcbWF0aGJme3l9JCBpcyBhIG1lYXN1cmUgb2Ygc2ltaWxhcml0eSBnaXZlbiBieToKJCQKXGNvcyhcdGhldGEpPVxmcmFje1xtYXRoYmZ7eH1cY2RvdCBcbWF0aGJme3l9fXtcfFxtYXRoYmZ7eH1cfF8yXHxcbWF0aGJme3l9XHxfMn09XGZyYWN7XHN1bV97aT0xfV5uIHhfaSB5X2l9e3tcc3FydHtcc3VtX3tpPTF9Xm4geF9pXjJcc3VtX3tpPTF9Xm4geV9pXjJ9fX0uCiQkCiogTm90ZSB0aGF0IHRoZSBjb3NpbmUgb2YgdGhlIGFuZ2xlIGJldHdlZW4gdGhlIHR3byBjZW50cmVkIHZlY3RvcnMgJCh4XzEtXGJhcntcbWF0aGJme3h9fSxcY2RvdHMseF9uLVxiYXJ7XG1hdGhiZnt4fX0pJCBhbmQgJCh5XzEtXGJhcntcbWF0aGJme3l9fSxcY2RvdHMseV9uLVxiYXJ7XG1hdGhiZnt5fX0pJCBjb2luY2lkZXMgd2l0aCB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBvZiAkXG1hdGhiZnt4fSQgYW5kICRcbWF0aGJme3l9JC4gIAoqIFRoZSBjb3NpbmUgY29ycmVsYXRpb24gZGlzdGFuY2UgaXMgZGVmaW5lZCBieToKJCQKZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPTEtXGNvcyhcdGhldGEpLgokJAoqIEl0IHNoYXJlcyBzaW1pbGFyIHByb3BlcnRpZXMgdGhhbiB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBkaXN0YW5jZS4gTGlrZXdpc2UsIEF4aW9tcyBBMSBhbmQgQTMgYXJlIG5vdCBzYXRpc2ZpZWQuCgojIFNwZWFybWFuIGNvcnJlbGF0aW9uIGRpc3RhbmNlIAoKKiBUbyBjYWxjdWxhdGUgdGhlIFNwZWFybWFuJ3MgcmFuay1vcmRlciBjb3JyZWxhdGlvbiwgd2UgbmVlZCB0byBtYXAgc2VwZXJhdGVseSBlYWNoIG9mIHRoZSB2ZWN0b3JzIHRvIHJhbmtlZCBkYXRhIHZhbHVlczogCiQkXG1hdGhiZnt4fVxyaWdodGFycm93IFx0ZXh0e3Jhbmt9KFxtYXRoYmZ7eH0pPSh4XzFecixcY2RvdHMseF9uXnIpLiQkCiogSGVyZSwgJHhfaV5yJCBpcyB0aGUgcmFuayBvZiAkeF9pJCBhbW9uZyB0aGUgc2V0IG9mIHZhbHVlcyBvZiAkXG1hdGhiZnt4fSQuCiogV2UgaWxsdXN0cmF0ZSB0aGlzIHRyYW5zZm9ybWF0aW9uIHdpdGggYSBzaW1wbGUgZXhhbXBsZToKKiBJZiAkXG1hdGhiZnt4fT0oMywgMSwgNCwgMTUsIDkyKSQsIHRoZW4gdGhlIHJhbmstb3JkZXIgdmVjdG9yIGlzICRcdGV4dHtyYW5rfShcbWF0aGJme3h9KT0oMiwxLDMsNCw1KSQuICAKCmBgYHtyfQp4PWMoMywgMSwgNCwgMTUsIDkyKQpyYW5rKHgpCmBgYAoKKiBUaGUgU3BlYXJtYW4ncyByYW5rIGNvcnJlbGF0aW9uIG9mIHR3byBudW1lcmljYWwgdmFyaWFibGVzICRcbWF0aGJme3h9JCAgYW5kICRcbWF0aGJme3l9JCBpcyBzaW1wbHkgdGhlIFBlYXJzb24gY29ycmVsYXRpb24gb2YgdGhlIHR3byBjb3JyZXNwbmRpbmcgcmFuay1vcmRlciB2YXJpYWJsZXMgJFx0ZXh0e3Jhbmt9KFxtYXRoYmZ7eH0pJCBhbmQgJFx0ZXh0e3Jhbmt9KFxtYXRoYmZ7eX0pJCwgaS5lLiAkXHJobyhcdGV4dHtyYW5rfShcbWF0aGJme3h9KSxcdGV4dHtyYW5rfShcbWF0aGJme3l9KSkkLiBUaGlzIG1lYXN1cmUgaXMgaXMgdXNlZnVsIGJlY2F1c2UgaXQgaXMgbW9yZSByb2J1c3QgYWdhaW5zdCBvdXRsaWVycyB0aGFuIHRoZSBQZWFyc29uIGNvcnJlbGF0aW9uLgoqIElmIGFsbCAgdGhlICRuJCAgcmFua3MgYXJlIGRpc3RpbmN0LCBpdCBjYW4gYmUgY29tcHV0ZWQgdXNpbmcgdGhlIGZvbGxvd2luZyBmb3JtdWxhOgokJApccmhvKFx0ZXh0e3Jhbmt9KFxtYXRoYmZ7eH0pLFx0ZXh0e3Jhbmt9KFxtYXRoYmZ7eX0pKT0xLVxmcmFjezZcc3VtX3tpPTF9Xm4gZF9pXjJ9e24obl4yLTEpfSwKJCQKd2hlcmUgJGRfaT14X2leci15X2lecixcOmk9MSxcY2RvdHMsbiQuCiAqIFRoZSBzcGVhcm1hbiBkaXN0YW5jZSBpcyB0aGVuIGRlZmluZWQgYnk6CiQkCmQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KT0xLVxyaG8oXHRleHR7cmFua30oXG1hdGhiZnt4fSksXHRleHR7cmFua30oXG1hdGhiZnt5fSkpLgokJAoqIEl0IGNhbiBiZSBzaG93biB0aGF0IGVhc2FseSB0aGF0IGl0IGlzIG5vdCBhIHByb3BlciBkaXN0YW5jZS4KKiBJZiBhbGwgIHRoZSAkbiQgIHJhbmtzIGFyZSBkaXN0aW5jdCwgd2UgZ2V0OgokJApkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9XGZyYWN7NlxzdW1fe2k9MX1ebiBkX2leMn17bihuXjItMSl9LgokJAoKYGBge3J9Cng9YygzLCAxLCA0LCAxNSwgOTIpCnJhbmsoeCkKeT1jKDMwLDIgLCA5LCAyMCwgNDgpCnJhbmsoeSkKZD1yYW5rKHgpLXJhbmsoeSkKZApjb3IocmFuayh4KSxyYW5rKHkpKQoxLTYqc3VtKGReMikvKDUqKDVeMi0xKSkKYGBgCgoKIyBLZW5kYWxsIHRhdSBkaXN0YW5jZSAKCiogVGhlIEtlbmRhbGwgcmFuayBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBpcyBjYWxjdWxhdGVkIGZyb20gdGhlIG51bWJlciBvZiBjb3JyZXNwb25kYW5jZXMgYmV0d2VlbiB0aGUgcmFua2luZ3Mgb2YgJFxtYXRoYmZ7eH0kIGFuZCB0aGUgcmFua2luZ3Mgb2YgJFxtYXRoYmZ7eX0kLgoqICAgVGhlIG51bWJlciBvZiBwYWlycyBvZiBvYnNlcnZhdGlvbnMgYW1vbmcgJG4kIG9ic2VydmF0aW9ucyBvciB2YWx1ZXMgaXM6IAokJHtuIFxjaG9vc2UgMn0gPVxmcmFje24obi0xKX17Mn0uJCQKKiBUaGUgcGFpcnMgb2Ygb2JzZXJ2YXRpb25zICQoeF97aX0seF97an0pJCAgYW5kICAkKHlfe2l9LHlfe2p9KSQgYXJlIHNhaWQgdG8gYmUgX2NvbmNvcmRhbnRfIGlmOiAkJFx0ZXh0e3NpZ259KHhfai14X2kpPVx0ZXh0e3NpZ259KHlfai15X2kpLCQkIGFuZCB0byBiZSBfZGlzY29yZGFudF8gaWY6ICAkJFx0ZXh0e3NpZ259KHhfai14X2kpPS1cdGV4dHtzaWdufSh5X2oteV9pKSwkJAp3aGVyZSAkXHRleHR7c2lnbn0oXGNkb3QpJCByZXR1cm5zICAkMSQgZm9yIHBvc2l0aXZlIG51bWJlcnMgYW5kICAkLTEkIG5lZ2F0aXZlIG51bWJlcnMgYW5kICQwJCBvdGhlcndpc2UuCiogSWYgJHhfaT14X2okIG9yICR5X2k9eV9qJCAob3IgYm90aCksIHRoZXJlIGlzIGEgdGllLgoqIFRoZSBLZW5kYWxsICRcdGF1JCBjb2VmZmljaWVudCBpcyBkZWZpbmVkIGJ5IChuZWdsZWN0aW5nIHRpZXMpOgokJFx0YXUgPVxmcmFjIHsxfXtuKG4tMSl9XHN1bV97aT0xfV57bn1cc3VtX3tqPTF9Xm5cdGV4dHtzaWdufSh4X2oteF9pKVx0ZXh0e3NpZ259KHlfai15X2kpLiQkCiogTGV0ICRuX2MkIChyZXNwLiAkbl9kJCkgYmUgdGhlIG51bWJlciBvZiBjb25jb3JkYW50IChyZXNwLiBkaXNjb3JkYW50KSBwYWlycywgd2UgaGF2ZSAkJFx0YXUgPVxmcmFjIHsyKG5fYy1uX2QpfXtuKG4tMSl9LiQkIAoqIFRoZSBLZW5kYWxsIHRhdSBkaXN0YW5jZSBpcyB0aGVuOiAkJGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KT0xLVx0YXUuICQkCiogUmVtYXJrOiB0aGUgdHJpYW5ndWxhciBpbmVxdWFsaXR5IG1heSBmYWlsIGluIGNhc2VzIHdoZXJlIHRoZXJlIGFyZSB0aWVzLgoKYGBge3J9Cng9YygzLCAxLCA0LCAxNSwgOTIpCnk9YygzMCwyICwgOSwgMjAsIDQ4KQp0YXU9MApmb3IgKGkgaW4gMTo1KQp7ICAKdGF1PXRhdStzaWduKHggLXhbaV0pJSolc2lnbih5IC15W2ldKQp9CnRhdT10YXUvKDUqNCkKdGF1CmNvcih4LHksIG1ldGhvZD0ia2VuZGFsbCIpCmBgYAoKIyBWYXJpYWJsZXMgc3RhbmRhcmRpemF0aW9uCgoqIFZhcmlhYmxlcyBhcmUgb2Z0ZW4gc3RhbmRhcmRpemVkIGJlZm9yZSBtZWFzdXJpbmcgZGlzc2ltaWxhcml0aWVzLgoqIFN0YW5kYXJkaXphdGlvbiBjb252ZXJ0cyB0aGUgb3JpZ2luYWwgdmFyaWFibGVzIGludG8gdW5pdGVsZXNzIHZhcmlhYmxlcy4KKiBBIHdlbGwga25vd24gbWV0aG9kIGlzIHRoZSB6LXNjb3JlIHRyYW5zZm9ybWF0aW9uOiAKJCQKXG1hdGhiZnt4fVxyaWdodGFycm93IChcZnJhY3t4XzEtXGJhcntcbWF0aGJme3h9fX17c19cbWF0aGJme3h9fSxcY2RvdHMsXGZyYWN7eF9uLVxiYXJ7XG1hdGhiZnt4fX19e3NfXG1hdGhiZnt4fX0pLAokJCAKd2hlcmUgJHNfXG1hdGhiZnt4fSQgaXMgdGhlIHNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb24gZ2l2ZW4gYnk6CiQkCnNfXG1hdGhiZnt4fT1cZnJhY3sxfXtuLTF9XHN1bV97aT0xfV5uKHhfaS1cYmFye1xtYXRoYmZ7eH19KV4yLgokJAoqIFRoZSB0cmFuc2Zvcm1lZCB2YXJpYWJsZSB3aWxsIGhhdmUgYSBtZWFuIG9mICQwJCBhbmQgYSB2YXJpYW5jZSBvZiAkMSQuCiogVGhlIHJlc3VsdCBvYnRhaW5lZCB3aXRoIFBlYXJzb24gY29ycmVsYXRpb24gbWVhc3VyZXMgYW5kIHN0YW5kYXJkaXplZCBFdWNsaWRlYW4gZGlzdGFuY2VzIGFyZSBjb21wYXJhYmxlLgoqIEZvciBvdGhlciBtZXRob2RzLCBzZWU6IE1pbGxpZ2FuLCBHLiBXLiwgJiBDb29wZXIsIE0uIEMuICgxOTg4KS4gQSBzdHVkeSBvZiBzdGFuZGFyZGl6YXRpb24gb2YgdmFyaWFibGVzIGluIGNsdXN0ZXIgYW5hbHlzaXMuIF9Kb3VybmFsIG9mIGNsYXNzaWZpY2F0aW9uXywgXzVfKDIpLCAxODEtMjA0LgoKYGBge3J9Cng9YygzLCAxLCA0LCAxNSwgOTIpCnk9YygzMCwyICwgOSwgMjAsIDQ4KQooeC1tZWFuKHgpKS9zZCh4KQpzY2FsZSh4KQooeS1tZWFuKHkpKS9zZCh5KQpzY2FsZSh5KQpgYGAKCiMgRGlzdGFuY2UgbWF0cml4IGNvbXB1dGF0aW9uCiogV2XigJlsbCB1c2UgYSBzdWJzZXQgb2YgdGhlIGRhdGEgVVNBcnJlc3RzCiogIFdl4oCZbGwgdXNlIG9ubHkgYSBieSB0YWtpbmcgMTUgcmFuZG9tIHJvd3MgYW1vbmcgdGhlIDUwIHJvd3MgaW4gdGhlIGRhdGEgc2V0LiAKKiBOZXh0LCB3ZSBzdGFuZGFyZGl6ZSB0aGUgZGF0YSB1c2luZyB0aGUgZnVuY3Rpb24gc2NhbGUoKToKCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJGYWN0b01pbmVSIikKbGlicmFyeSgiRmFjdG9NaW5lUiIpCmRhdGEoIlVTQXJyZXN0cyIpICMgTG9hZGluZwpoZWFkKFVTQXJyZXN0cywgMykgIyBQcmludCB0aGUgZmlyc3QgMyByb3dzCnNldC5zZWVkKDEyMykKc3MgPC0gc2FtcGxlKDE6NTAsIDE1KSAjIFRha2UgMTUgcmFuZG9tIHJvd3MKZGYgPC0gVVNBcnJlc3RzW3NzLCBdICMgU3Vic2V0IHRoZSAxNSByb3dzCmRmLnNjYWxlZCA8LSBzY2FsZShkZikgIyBTdGFuZGFyZGl6ZSB0aGUgdmFyaWFibGVzCmBgYAoKCgo=