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")
6*sqrt(2)
dist(rbind(x, y), method = "manhattan")
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")
6/6+6/6
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)
- 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)
y=c(30,2 , 9, 20, 48)
rank(y)
d=rank(x)-rank(y)
d
cor(rank(x),rank(y))
1-6*sum(d^2)/(5*(5^2-1))
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
cor(x,y, method="kendall")
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)
scale(x)
(y-mean(y))/sd(y)
scale(y)
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")
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
Ci0tLQp0aXRsZTogIkRpc3RhbmNlIGFuZCBkaXNzaW1pbGFyaXRpZXMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPVRSVUV9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgoKIyBEZWZpbml0aW9uIG9mIGEgZGlzdGFuY2UKCiogQSBkaXN0YW5jZSBmdW5jdGlvbiBvciBhIG1ldHJpYyBvbiAkXG1hdGhiYntSfV5uLFw6blxnZXEgMSQsIGlzIGEgZnVuY3Rpb24gJGQ6XG1hdGhiYntSfV5uXHRpbWVzXG1hdGhiYntSfV5uXHJpZ2h0YXJyb3cgXG1hdGhiYntSfSQuCiogQSBkaXN0YW5jZSBmdW5jdGlvbiBtdXN0IHNhdGlzZnkgc29tZSByZXF1aXJlZCBwcm9wZXJ0aWVzIG9yIGF4aW9tcy4gCiogVGhlcmUgYXJlIHRocmVlIG1haW4gYXhpb21zLgoqIEExLiAkZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPSAwXGlmZiBcbWF0aGJme3h9PVxtYXRoYmZ7eX0kIChpZGVudGl0eSBvZiBpbmRpc2Nlcm5pYmxlcyk7CiogQTIuICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9IGQoXG1hdGhiZnt5fSxcbWF0aGJme3h9KSQgKHN5bW1ldHJ5KTsKCiogQTMuICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt6fSlcbGVxIGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KStkKFxtYXRoYmZ7eX0sXG1hdGhiZnt6fSkkICAodHJpYW5nbGUgaW5lcXVhbGl0eSksCndoZXJlICRcbWF0aGJme3h9PSh4XzEsXGNkb3RzLHhfbikkLCAkXG1hdGhiZnt5fT0oeV8xLFxjZG90cyx5X24pJCBhbmQgJFxtYXRoYmZ7en09KHpfMSxcY2RvdHMsel9uKSQgYXJlIGFsbCB2ZWN0b3JzIG9mICRcbWF0aGJie1J9Xm4kLgoqIFdlIHNob3VsZCB1c2UgdGhlIHRlcm0gX2Rpc3NpbWlsYXJpdHlfIHJhdGhlciB0aGFuIF9kaXN0YW5jZV8gd2hlbiBub3QgYWxsIHRoZSB0aHJlZSBheGlvbXMgQTEtQTMgYXJlIHZhbGlkLgoqIE1vc3Qgb2YgdGhlIHRpbWUsIHdlIHNoYWxsIHVzZSwgd2l0aCBzb21lIGFidXNlIG9mIHZvY2FidWxhcnksIHRoZSB0ZXJtIGRpc3RhbmNlLgoKIyBFeGVyY2ljZSAxCgoqIFByb3ZlIHRoYXQgdGhlIHRocmVlIGF4aW9tcyBBMS1BMyBpbXBseSB0aGUgbm9uLW5lZ2F0aXZpdHkgY29uZGl0aW9uOiAkJGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KVxnZXEgMC4kJAoKCiMgRXVjbGlkZWFuIGRpc3RhbmNlCgoKKiBJdCBpcyBkZWZpbmVkIGJ5OgoKJCRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9XHNxcnR7XHN1bV97aT0xfV5uICh4X2kteV9pKV4yfS4kJAoqIEExQTIgYWUgb25idmlvdXMuCiogVGhlIHByb29mIG9mIEEzIGlzIHByb3ZpZGVkIGJlbG93LgoKCiMgTWFuaGF0dGFuIGRpc3RhbmNlCgoqIFRoZSBNYW5oYXR0YW4gZGlzdGFuY2UgYWxzbyBjYWxsZWQgIHRheGktY2FiIG1ldHJpYyBvciBjaXR5LWJsb2NrIG1ldHJpYyBpcyBkZWZpbmVkIGJ5OgoKJCRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSkKPVxzdW1fe2k9MX1ebiB8eF9pLXlfaXwuCiQkCgoqIEExLUEyIGhvbGQuCiogQTMgYWxzbyBob2xkcyB1c2luZyB0aGUgZmFjdCB0aGF0ICR8YStifFxsZXEgfGF8K3xifCQgZm9yIGFueSByZWFscyAkYSxiJC4KKiBUaGVyZSBleGlzdHMgYWxzbyBhIHdlaWdodGVkIHZlcnNpb24gIG9mIHRoZSBNYW5oYXR0YW4gZGlzdGFuY2UgY2FsbGVkIHRoZSBDYW5iZXJyYSBkaXN0YW5jZS4KCltNYW5oYXR0YW4gZGlzdGFuY2UgdnMgRXVjbGlkZWFuIGRpc3RhbmNlIEdyYXBoXShodHRwczovL3VwbG9hZC53aWtpbWVkaWEub3JnL3dpa2lwZWRpYS9jb21tb25zLzAvMDgvTWFuaGF0dGFuX2Rpc3RhbmNlLnN2ZykKCmBgYHtyfQp4ID0gYygwLCAwKQp5ID0gYyg2LDYpCmRpc3QocmJpbmQoeCwgeSksIG1ldGhvZCA9ICJldWNsaWRpYW4iKQo2KnNxcnQoMikKZGlzdChyYmluZCh4LCB5KSwgbWV0aG9kID0gIm1hbmhhdHRhbiIpCmBgYAoKCiMgQ2FuYmVycmEgZGlzdGFuY2UKCiogSXQgaXMgZGVmaW5lZCBieToKCiQkZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pCj1cc3VtX3tpPTF9Xm4gXGZyYWN7fHhfaS15X2l8fXt8eF9pfCt8eV9pfH0uJCQKCiogTm90ZSB0aGF0IHRoZSB0ZXJtICR8eF9pIOKIkiB5X2l8Lyh8eF9pfCt8eV9pfCkkIGlzIG5vdCBwcm9wZXJseSBkZWZpbmVkIHdoZW4gJHhfaT15X2k9MCQuCiogQnkgY29udmVudGlvbiB3ZSBzZXQgdGhlIHJhdGlvIHRvIGJlIHplcm8gaW4gdGhhdCBjYXNlLgoqIFRoZSBDYW5iZXJyYSBkaXN0YW5jZSBpcyBzcGVjaWFsbHkgc2Vuc2l0aXZlIHRvIHNtYWxsIGNoYW5nZXMgbmVhciB6ZXJvLgoKCmBgYHtyfQp4ID0gYygwLCAwKQp5ID0gYyg2LDYpCmRpc3QocmJpbmQoeCwgeSksIG1ldGhvZCA9ICJjYW5iZXJyYSIpCjYvNis2LzYKYGBgCgoKIyBFeGVyY2ljZSAyCgoqIFByb3ZlIHRoYXQgdGhlIENhbmJlcnJhIGRpc3RhbmNlIGlzIGEgdHJ1ZSBkaXN0YW5jZS4KCiMgTWlua293c2tpIGRpc3RhbmNlCiogQm90aCB0aGUgRXVjbGlkaWFuIGFuZCB0aGUgTWFuYXR0YW4gZGlzdGFuY2VzIGFyZSBzcGVjaWFsIGNhc2VzIG9mICB0aGUgTWlua293c2tpIGRpc3RhbmNlIHdoaWNoIGlzIGRlZmluZWQsIGZvciAkcFxnZXEgMSQsIGJ5OiAKJCQKZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPQpcbGVmdFtcc3VtX3tpPTF9IHx4X2kteV9pfF57cH1ccmlnaHRdXnsxL3B9LgokJAogKiBGb3IgJHA9MSQsIHdlIGdldCB0aGUgTWFuaGF0dGFuIGRpc3RhbmNlLgogKiBGb3IgJHA9MiQsIHdlIGdldCB0aGUgRXVjbGlkaWFuIGRpc3RhbmNlLgoqIExldCB1cyBhbHNvIGRlZmluZTogCiQkXHxcbWF0aGJme3h9XHxfcFxlcXVpdlxsZWZ0W1xzdW1fe2k9MX1ebiB8eF9pfF57cH1ccmlnaHRdXnsxL3B9LCQkCndoZXJlICRcfFxtYXRoYmZ7XGNkb3R9XHxfcCQgaXMga25vd24gYXMgdGhlICRwJC1ub3JtIG9yIE1pbmtvd3NraSBub3JtLgoqIE5vdGUgdGhhdCB0aGUgTWlua293c2tpIGRpc3RhbmNlIGFuZCBub3JtIGFyZSByZWxhdGVkIGJ5OgokJApkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9XHxcbWF0aGJme3h9LVxtYXRoYmZ7eX1cfF9wLgokJAoqIENvbnZlcnNlbHksIHdlIGhhdmU6CiQkClx8XG1hdGhiZnt4fVx8X3A9ZChcbWF0aGJme3h9LFxtYXRoYmZ7MH0pLAokJAp3aGVyZSAkXG1hdGhiZnswfSQgaXMgdGhlIG51bGwtdmV0b3Igb2YgJFxtYXRoYmJ7Un1ebiQuCgpgYGB7cn0KbGlicmFyeSgiZ2dwbG90MiIpCnggPSBjKDAsIDApCnkgPSBjKDYsNikKTWlua293RGlzdD1jKCkKZm9yIChwIGluIHNlcSgxLDMwLC4wMSkpCnsKTWlua293RGlzdD1jKE1pbmtvd0Rpc3QsZGlzdChyYmluZCh4LCB5KSwgbWV0aG9kID0gIm1pbmtvd3NraSIsIHAgPSBwKSkgICAgIAp9CmdncGxvdChkYXRhID1kYXRhLmZyYW1lKHggPSBzZXEoMSwzMCwuMDEpLCB5PU1pbmtvd0Rpc3QgKSAsIG1hcHBpbmcgPSBhZXMoeCA9IHgsIHkgPSB5KSkrZ2VvbV9wb2ludChzaXplPS4xLGNvbG9yPSJyZWQiKSt4bGltKDEsMTEpK3hsYWIoInAiKSt5bGFiKCJNaW5rb3dza2kgRGlzdGFuY2UiKStnZ3RpdGxlKCJNaW5rb3dza2kgZGlzdGFuY2Ugd3J0IHAiKQpgYGAKCiMgQ2hlYnlzaGV2IGRpc3RhbmNlIAoqIEF0IHRoZSBsaW1pdCwgd2UgZ2V0IHRoZSBDaGVieXNoZXYgZGlzdGFuY2Ugd2hpY2ggaXMgZGVmaW5lZCBieToKJCQKZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPVxtYXhfe2k9MSxcY2RvdHMsbn0ofHhfaS15X2l8KT1cbGltX3twXHJpZ2h0YXJyb3dcaW5mdHl9ClxsZWZ0W1xzdW1fe2k9MX0gfHhfaS15X2l8XntwfVxyaWdodF1eezEvcH0uCiQkCiogVGhlIGNvcnJlc3BvbmRpbmcgbm9ybSBpczoKJCQKXHxcbWF0aGJme3h9fF9caW5mdHk9XG1heF97aT0xLFxjZG90cyxufSh8eF9pfCkuCiQkCgojIE1pbmtvd3NraSBpbmVxdWFsaXR5CgoqIFRoZSBwcm9vZiBvZiB0aGUgdHJpYW5ndWxhciBpbmVxdWFsaXR5IEEzIGlzIGJhc2VkIG9uIHRoZSBNaW5rb3dza2kgaW5lcXVhbGl0eToKKiBGb3IgYW55IG5vbm5lZ2F0aXZlIHJlYWwgbnVtYmVycyAkYV8xLFxjZG90cyxhX24kOyAkYl8xLFxjZG90cyxiX24kLCBhbmQgZm9yIGFueSAkcFxnZXEgMSQsIHdlIGhhdmU6CiQkClxsZWZ0W1xzdW1fe2k9MX1ebiAoYV9pK2JfaSlee3B9XHJpZ2h0XV57MS9wfVxsZXEKXGxlZnRbXHN1bV97aT0xfV5uIGFfaV57cH1ccmlnaHRdXnsxL3B9CitcbGVmdFtcc3VtX3tpPTF9Xm4gYl9pXntwfVxyaWdodF1eezEvcH0uCiQkCiogVG8gcHJvdmUgdGhhdCB0aGUgTWlua293c2tpIGRpc3RhbmNlIHNhdGlzZmllcyBBMywgbm90aWNlIHRoYXQgCiQkCiBcc3VtX3tpPTF9Xm58eF9pLXpfaXxee3B9PSBcc3VtX3tpPTF9Xm58KHhfaS15X2kpKyh5X2ktel9pKXxee3B9LgokJAoqIFNpbmNlIGZvciBhbnkgcmVhbHMgJHgseSQsIHdlIGhhdmU6ICR8eCt5fFxsZXEgfHh8K3x5fCQsIGFuZCB1c2luZyB0aGUgZmFjdCB0aGF0ICR4XnAkIGlzIGluY3JlYXNpbmcgaW4gJHhcZ2VxIDAkLCB3ZSBvYnRhaW46CiQkCiBcc3VtX3tpPTF9Xm58eF9pLXpfaXxee3B9XGxlcSBcc3VtX3tpPTF9Xm4ofHhfaS15X2l8K3x5X2ktel9pfClee3B9LgokJAoKKiBBcHBseWluZyB0aGUgTWlua293c2tpIGluZXF1YWxpdHkgd2l0aCAkYV9pPXx4X2kteV9pfCQgYW5kICRiX2k9fHlfaS16X2l8JCwgJGk9MSxcY2RvdHMsbiQsIHdlIGdldDoKJCQKIFxzdW1fe2k9MX1ebnx4X2ktel9pfF57cH1cbGVxIFxsZWZ0KFxzdW1fe2k9MX1ebiB8eF9pLXlfaXxee3B9XHJpZ2h0KV57MS9wfStcbGVmdChcc3VtX3tpPTF9Xm4gfHlfaS16X2l8XntwfVxyaWdodCleezEvcH0uCiQkCgojIEjDtmxkZXIgaW5lcXVhbGl0eQoKKiBUaGUgcHJvb2Ygb2YgdGhlIE1pbmtvd3NraSBpbmVxdWFsaXR5IGl0c2VsZiByZXF1aXJlcyB0aGUgSMO2bGRlciBpbmVxdWFsaXR5OgoqIEZvciBhbnkgbm9ubmVnYXRpdmUgcmVhbCBudW1iZXJzICRhXzEsXGNkb3RzLGFfbiQ7ICRiXzEsXGNkb3RzLGJfbiQsIGFuZCBhbnkgJHAscT4xJCB3aXRoICQxL3ArMS9xPTEkLCB3ZSBoYXZlOgokJApcc3VtX3tpPTF9Xm4gYV9pYl9pXGxlcQpcbGVmdFtcc3VtX3tpPTF9Xm4gYV9pXntwfVxyaWdodF1eezEvcH0KXGxlZnRbXHN1bV97aT0xfV5uIGJfaV57cX1ccmlnaHRdXnsxL3F9CiQkCiogVGhlIHByb29mIG9mIHRoZSBIw7ZsZGVyIGluZXF1YWxpdHkgcmVsaWVzIG9uIHRoZSBZb3VuZyBpbmVxdWFsaXR5OgoqIEZvciBhbnkgJGEsYj4wJCwgd2UgaGF2ZQokJAphYlxsZXEgXGZyYWN7YV5wfXtwfStcZnJhY3tiXnF9e3F9LAokJAp3aXRoIGVxdWFsaXR5IG9jY3VyaW5nIGlmZjogJGFecD1iXnEkLiAKKiBUbyBwcm92ZSB0aGUgWW91bmcgaW5lcXVhbGl0eSwgb25lIGNhbiB1c2UgdGhlIChzdHJpY3QpIGNvbnZleGl0eSBvZiB0aGUgZXhwb25lbnRpYWwgZnVuY3Rpb24uCiogRm9yIGFueSByZWFscyAkeCx5JCwgd2UgaGF2ZToKJCQKZV57XGZyYWN7eH17cH0rXGZyYWN7eX17cX0gfVxsZXEgXGZyYWN7ZV57eH19e3B9K1xmcmFje2Vee3l9fXtxfS4gCiQkCiogV2UgdGhlbiBzZXQ6ICR4PXBcbG4gYSQgYW5kICR5PXFcbG4gYiQgdG8gZ2V0IHRoZSBZb3VuZyBpbmVxdWFsaXR5LgoqIEEgZ29vZCByZWZlcmVuY2Ugb24gaW5lcXVhbGl0aWVzIGlzOiBaLiBDdmV0a292c2tpLCAgSW5lcXVhbGl0aWVzOiB0aGVvcmVtcywgdGVjaG5pcXVlcyBhbmQgc2VsZWN0ZWQgcHJvYmxlbXMsIDIwMTIsIFNwcmluZ2VyIFNjaWVuY2UgJiBCdXNpbmVzcyBNZWRpYS4KICMgQ2F1Y2h5LVNjaHdhcnR6IGluZXF1YWxpdHkKKiBOb3RlIHRoYXQgdGhlIHRyaWFuZ3VsYXIgaW5lcXVhbGl0eSBmb3IgdGhlIE1pbmtvd3NraSBkaXN0YW5jZSBpbXBsaWVzOiAKJCQKXHN1bV97aT0xfV5uIHx4X2l8XGxlcQpcbGVmdFtcc3VtX3tpPTF9Xm4gfHhfaXxee3B9XHJpZ2h0XV57MS9wfS4KJCQKKiBOb3RlIHRoYXQgZm9yICRwPTIkLCB3ZSBoYXZlICRxPTIkLiBUaGUgSMO2bGRlciBpbmVxdWFsaXR5IGltcGxpZXMgZm9yIHRoYXQgc3BlY2lhbCBjYXNlCiQkClxzdW1fe2k9MX1ebnx4X2l5X2l8XGxlcVxzcXJ0e1xzdW1fe2k9MX1ebiB4X2leMn1cc3FydHtcc3VtX3tpPTF9Xm4geV9pXjJ9LiAKJCQKKiBTaW5jZSB0aGUgTEhTIG9kIHRoZXMgYWJvdmUgaW5lcXVhbGl0eSBpcyBncmVhdGVyIHRoZW4gJHxcc3VtX3tpPTF9Xm54X2l5X2l8JCwgd2UgZ2V0IHRoZSBDYXVjaHktU2Nod2FydHogaW5lcXVhbGl0eQoKJCQKfFxzdW1fe2k9MX1ebnhfaXlfaXxcbGVxXHNxcnR7XHN1bV97aT0xfV5uIHhfaV4yfVxzcXJ0e1xzdW1fe2k9MX1ebiB5X2leMn0uIAokJAoqIFVzaW5nIHRoZSBkb3QgcHJvZHVjdCBub3RhdGlvbiBjYWxsZWQgYWxzbyBzY2FsYXIgcHJvZHVjdCBub2F0aW9uOiAkXG1hdGhiZnt4XGNkb3QgeX09XHN1bV97aT0xfV5ueF9peV9pJCwgYW5kIHRoZSBub3JtIG5vdGF0aW9uICRcfFxtYXRoYmZ7XGNkb3R9XHxfMiBcfCQsIHRoZSBDYXVjaHktU2Nod2FydCBpbmVxdWFsaXR5IGlzOgokJAp8XG1hdGhiZnt4XGNkb3QgeX0gfCBcbGVxIFx8XG1hdGhiZnt4fVx8XzIgXHwgXG1hdGhiZnt5fVx8XzIuCiQkCgojIFBlYXJzb24gY29ycmVsYXRpb24gZGlzdGFuY2UgCgoqIFRoZSBQZWFyc29uIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGlzIGEgc2ltaWxhcml0eSBtZWFzdXJlIG9uICRcbWF0aGJie1J9Xm4kIGRlZmluZWQgYnk6CiQkClxyaG8oXG1hdGhiZnt4fSxcbWF0aGJme3l9KT0KXGZyYWN7XHN1bV97aT0xfV5uICh4X2ktXGJhcntcbWF0aGJme3h9fSkoeV9pLVxiYXJ7XG1hdGhiZnt5fX0pfXt7XHNxcnR7XHN1bV97aT0xfV5uICh4X2ktXGJhcntcbWF0aGJme3h9fSleMlxzdW1fe2k9MX1ebiAoeV9pLVxiYXJ7XG1hdGhiZnt5fX0pXjJ9fX0sCiQkCndoZXJlICRcYmFye1xtYXRoYmZ7eH19JCBpcyB0aGUgbWVhbiBvZiB0aGUgdmVjdG9yICRcbWF0aGJme3h9JCBkZWZpbmVkIGJ5OiAKJCRcYmFye1xtYXRoYmZ7eH19PVxmcmFjezF9e259XHN1bV97aT0xfV5uIHhfaSwkJAoqIE5vdGUgdGhhdCB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBzYXRpc2ZpZXMgUDIgYW5kICBpcyBpbnZhcmlhbnQgdG8gYW55IHBvc2l0aXZlIGxpbmVhciB0cmFuc2Zvcm1hdGlvbiwgaS5lLjogJCRccmhvKFxhbHBoYVxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9XHJobyhcbWF0aGJme3h9LFxtYXRoYmZ7eX0pLCQkIGZvciBhbnkgJFxhbHBoYT4wJC4KKiBUaGUgUGVhcnNvbiBkaXN0YW5jZSAob3IgY29ycmVsYXRpb24gZGlzdGFuY2UpIGlzIGRlZmluZWQgYnk6CiQkCmQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KT0xLVxyaG8oXG1hdGhiZnt4fSxcbWF0aGJme3l9KS4kJAoKKiBOb3RlIHRoYXQgdGhlIFBlYXJzb24gZGlzdGFuY2UgZG9lcyBub3Qgc2F0aXNmeSBBMSBzaW5jZSAkZChcbWF0aGJme3h9LFxtYXRoYmZ7eH0pPTAkIGZvciBhbnkgbm9uLXplcm8gdmVjdG9yICRcbWF0aGJme3h9JC4gSXQgbmVpdGhlciBzYXRpc2ZpZXMgdGhlIHRyaWFuZ2xlIGluZXF1YWxpdHkuIEhvd2V2ZXIsIHRoZSBzeW1tZXRyeSBwcm9wZXJ0eSBpcyBmdWxsZmlsbGVkLiAKCiMgQ29zaW5lIGNvcnJlbGF0aW9uIGRpc3RhbmNlCgoqIFRoZSBjb3NpbmUgb2YgdGhlIGFuZ2xlICRcdGhldGEkIGJldHdlZW4gdHdvIHZlY3RvcnMgJFxtYXRoYmZ7eH0kIGFuZCAkXG1hdGhiZnt5fSQgaXMgYSBtZWFzdXJlIG9mIHNpbWlsYXJpdHkgZ2l2ZW4gYnk6CiQkClxjb3MoXHRoZXRhKT1cZnJhY3tcbWF0aGJme3h9XGNkb3QgXG1hdGhiZnt5fX17XHxcbWF0aGJme3h9XHxfMlx8XG1hdGhiZnt5fVx8XzJ9PVxmcmFje1xzdW1fe2k9MX1ebiB4X2kgeV9pfXt7XHNxcnR7XHN1bV97aT0xfV5uIHhfaV4yXHN1bV97aT0xfV5uIHlfaV4yfX19LgokJAoqIE5vdGUgdGhhdCB0aGUgY29zaW5lIG9mIHRoZSBhbmdsZSBiZXR3ZWVuIHRoZSB0d28gY2VudHJlZCB2ZWN0b3JzICQoeF8xLVxiYXJ7XG1hdGhiZnt4fX0sXGNkb3RzLHhfbi1cYmFye1xtYXRoYmZ7eH19KSQgYW5kICQoeV8xLVxiYXJ7XG1hdGhiZnt5fX0sXGNkb3RzLHlfbi1cYmFye1xtYXRoYmZ7eX19KSQgY29pbmNpZGVzIHdpdGggdGhlIFBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQgb2YgJFxtYXRoYmZ7eH0kIGFuZCAkXG1hdGhiZnt5fSQuICAKKiBUaGUgY29zaW5lIGNvcnJlbGF0aW9uIGRpc3RhbmNlIGlzIGRlZmluZWQgYnk6CiQkCmQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KT0xLVxjb3MoXHRoZXRhKS4KJCQKKiBJdCBzaGFyZXMgc2ltaWxhciBwcm9wZXJ0aWVzIHRoYW4gdGhlIFBlYXJzb24gY29ycmVsYXRpb24gZGlzdGFuY2UuIExpa2V3aXNlLCBBeGlvbXMgQTEgYW5kIEEzIGFyZSBub3Qgc2F0aXNmaWVkLgoKIyBTcGVhcm1hbiBjb3JyZWxhdGlvbiBkaXN0YW5jZSAKCiogVG8gY2FsY3VsYXRlIHRoZSBTcGVhcm1hbidzIHJhbmstb3JkZXIgY29ycmVsYXRpb24sIHdlIG5lZWQgdG8gbWFwIHNlcGVyYXRlbHkgZWFjaCBvZiB0aGUgdmVjdG9ycyB0byByYW5rZWQgZGF0YSB2YWx1ZXM6IAokJFxtYXRoYmZ7eH1ccmlnaHRhcnJvdyBcdGV4dHtyYW5rfShcbWF0aGJme3h9KT0oeF8xXnIsXGNkb3RzLHhfbl5yKS4kJAoqIEhlcmUsICR4X2leciQgaXMgdGhlIHJhbmsgb2YgJHhfaSQgYW1vbmcgdGhlIHNldCBvZiB2YWx1ZXMgb2YgJFxtYXRoYmZ7eH0kLgoqIFdlIGlsbHVzdHJhdGUgdGhpcyB0cmFuc2Zvcm1hdGlvbiB3aXRoIGEgc2ltcGxlIGV4YW1wbGU6CiogSWYgJFxtYXRoYmZ7eH09KDMsIDEsIDQsIDE1LCA5MikkLCB0aGVuIHRoZSByYW5rLW9yZGVyIHZlY3RvciBpcyAkXHRleHR7cmFua30oXG1hdGhiZnt4fSk9KDIsMSwzLDQsNSkkLiAgCgpgYGB7cn0KeD1jKDMsIDEsIDQsIDE1LCA5MikKcmFuayh4KQpgYGAKCiogVGhlIFNwZWFybWFuJ3MgcmFuayBjb3JyZWxhdGlvbiBvZiB0d28gbnVtZXJpY2FsIHZhcmlhYmxlcyAkXG1hdGhiZnt4fSQgIGFuZCAkXG1hdGhiZnt5fSQgaXMgc2ltcGx5IHRoZSBQZWFyc29uIGNvcnJlbGF0aW9uIG9mIHRoZSB0d28gY29ycmVzcG5kaW5nIHJhbmstb3JkZXIgdmFyaWFibGVzICRcdGV4dHtyYW5rfShcbWF0aGJme3h9KSQgYW5kICRcdGV4dHtyYW5rfShcbWF0aGJme3l9KSQsIGkuZS4gJFxyaG8oXHRleHR7cmFua30oXG1hdGhiZnt4fSksXHRleHR7cmFua30oXG1hdGhiZnt5fSkpJC4gVGhpcyBtZWFzdXJlIGlzIGlzIHVzZWZ1bCBiZWNhdXNlIGl0IGlzIG1vcmUgcm9idXN0IGFnYWluc3Qgb3V0bGllcnMgdGhhbiB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbi4KKiBJZiBhbGwgIHRoZSAkbiQgIHJhbmtzIGFyZSBkaXN0aW5jdCwgaXQgY2FuIGJlIGNvbXB1dGVkIHVzaW5nIHRoZSBmb2xsb3dpbmcgZm9ybXVsYToKJCQKXHJobyhcdGV4dHtyYW5rfShcbWF0aGJme3h9KSxcdGV4dHtyYW5rfShcbWF0aGJme3l9KSk9MS1cZnJhY3s2XHN1bV97aT0xfV5uIGRfaV4yfXtuKG5eMi0xKX0sCiQkCndoZXJlICRkX2k9eF9pXnIteV9pXnIsXDppPTEsXGNkb3RzLG4kLgogKiBUaGUgc3BlYXJtYW4gZGlzdGFuY2UgaXMgdGhlbiBkZWZpbmVkIGJ5OgokJApkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9MS1ccmhvKFx0ZXh0e3Jhbmt9KFxtYXRoYmZ7eH0pLFx0ZXh0e3Jhbmt9KFxtYXRoYmZ7eX0pKS4KJCQKKiBJdCBjYW4gYmUgc2hvd24gdGhhdCBlYXNhbHkgdGhhdCBpdCBpcyBub3QgYSBwcm9wZXIgZGlzdGFuY2UuCiogSWYgYWxsICB0aGUgJG4kICByYW5rcyBhcmUgZGlzdGluY3QsIHdlIGdldDoKJCQKZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPVxmcmFjezZcc3VtX3tpPTF9Xm4gZF9pXjJ9e24obl4yLTEpfS4KJCQKCmBgYHtyfQp4PWMoMywgMSwgNCwgMTUsIDkyKQpyYW5rKHgpCnk9YygzMCwyICwgOSwgMjAsIDQ4KQpyYW5rKHkpCmQ9cmFuayh4KS1yYW5rKHkpCmQKY29yKHJhbmsoeCkscmFuayh5KSkKMS02KnN1bShkXjIpLyg1Kig1XjItMSkpCmBgYAoKCiMgS2VuZGFsbCB0YXUgZGlzdGFuY2UgCgoqIFRoZSBLZW5kYWxsIHJhbmsgY29ycmVsYXRpb24gY29lZmZpY2llbnQgaXMgY2FsY3VsYXRlZCBmcm9tIHRoZSBudW1iZXIgb2YgY29ycmVzcG9uZGFuY2VzIGJldHdlZW4gdGhlIHJhbmtpbmdzIG9mICRcbWF0aGJme3h9JCBhbmQgdGhlIHJhbmtpbmdzIG9mICRcbWF0aGJme3l9JC4KKiAgIFRoZSBudW1iZXIgb2YgcGFpcnMgb2Ygb2JzZXJ2YXRpb25zIGFtb25nICRuJCBvYnNlcnZhdGlvbnMgb3IgdmFsdWVzIGlzOiAKJCR7biBcY2hvb3NlIDJ9ID1cZnJhY3tuKG4tMSl9ezJ9LiQkCiogVGhlIHBhaXJzIG9mIG9ic2VydmF0aW9ucyAkKHhfe2l9LHhfe2p9KSQgIGFuZCAgJCh5X3tpfSx5X3tqfSkkIGFyZSBzYWlkIHRvIGJlIF9jb25jb3JkYW50XyBpZjogJCRcdGV4dHtzaWdufSh4X2oteF9pKT1cdGV4dHtzaWdufSh5X2oteV9pKSwkJCBhbmQgdG8gYmUgX2Rpc2NvcmRhbnRfIGlmOiAgJCRcdGV4dHtzaWdufSh4X2oteF9pKT0tXHRleHR7c2lnbn0oeV9qLXlfaSksJCQKd2hlcmUgJFx0ZXh0e3NpZ259KFxjZG90KSQgcmV0dXJucyAgJDEkIGZvciBwb3NpdGl2ZSBudW1iZXJzIGFuZCAgJC0xJCBuZWdhdGl2ZSBudW1iZXJzIGFuZCAkMCQgb3RoZXJ3aXNlLgoqIElmICR4X2k9eF9qJCBvciAkeV9pPXlfaiQgKG9yIGJvdGgpLCB0aGVyZSBpcyBhIHRpZS4KKiBUaGUgS2VuZGFsbCAkXHRhdSQgY29lZmZpY2llbnQgaXMgZGVmaW5lZCBieSAobmVnbGVjdGluZyB0aWVzKToKJCRcdGF1ID1cZnJhYyB7MX17bihuLTEpfVxzdW1fe2k9MX1ee259XHN1bV97aj0xfV5uXHRleHR7c2lnbn0oeF9qLXhfaSlcdGV4dHtzaWdufSh5X2oteV9pKS4kJAoqIExldCAkbl9jJCAocmVzcC4gJG5fZCQpIGJlIHRoZSBudW1iZXIgb2YgY29uY29yZGFudCAocmVzcC4gZGlzY29yZGFudCkgcGFpcnMsIHdlIGhhdmUgJCRcdGF1ID1cZnJhYyB7MihuX2Mtbl9kKX17bihuLTEpfS4kJCAKKiBUaGUgS2VuZGFsbCB0YXUgZGlzdGFuY2UgaXMgdGhlbjogJCRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9MS1cdGF1LiAkJAoqIFJlbWFyazogdGhlIHRyaWFuZ3VsYXIgaW5lcXVhbGl0eSBtYXkgZmFpbCBpbiBjYXNlcyB3aGVyZSB0aGVyZSBhcmUgdGllcy4KCmBgYHtyfQp4PWMoMywgMSwgNCwgMTUsIDkyKQp5PWMoMzAsMiAsIDksIDIwLCA0OCkKdGF1PTAKZm9yIChpIGluIDE6NSkKeyAgCnRhdT10YXUrc2lnbih4IC14W2ldKSUqJXNpZ24oeSAteVtpXSkKfQp0YXU9dGF1Lyg1KjQpCnRhdQpjb3IoeCx5LCBtZXRob2Q9ImtlbmRhbGwiKQpgYGAKCiMgVmFyaWFibGVzIHN0YW5kYXJkaXphdGlvbgoKKiBWYXJpYWJsZXMgYXJlIG9mdGVuIHN0YW5kYXJkaXplZCBiZWZvcmUgbWVhc3VyaW5nIGRpc3NpbWlsYXJpdGllcy4KKiBTdGFuZGFyZGl6YXRpb24gY29udmVydHMgdGhlIG9yaWdpbmFsIHZhcmlhYmxlcyBpbnRvIHVuaXRlbGVzcyB2YXJpYWJsZXMuCiogQSB3ZWxsIGtub3duIG1ldGhvZCBpcyB0aGUgei1zY29yZSB0cmFuc2Zvcm1hdGlvbjogCiQkClxtYXRoYmZ7eH1ccmlnaHRhcnJvdyAoXGZyYWN7eF8xLVxiYXJ7XG1hdGhiZnt4fX19e3NfXG1hdGhiZnt4fX0sXGNkb3RzLFxmcmFje3hfbi1cYmFye1xtYXRoYmZ7eH19fXtzX1xtYXRoYmZ7eH19KSwKJCQgCndoZXJlICRzX1xtYXRoYmZ7eH0kIGlzIHRoZSBzYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uIGdpdmVuIGJ5OgokJApzX1xtYXRoYmZ7eH09XGZyYWN7MX17bi0xfVxzdW1fe2k9MX1ebih4X2ktXGJhcntcbWF0aGJme3h9fSleMi4KJCQKKiBUaGUgdHJhbnNmb3JtZWQgdmFyaWFibGUgd2lsbCBoYXZlIGEgbWVhbiBvZiAkMCQgYW5kIGEgdmFyaWFuY2Ugb2YgJDEkLgoqIFRoZSByZXN1bHQgb2J0YWluZWQgd2l0aCBQZWFyc29uIGNvcnJlbGF0aW9uIG1lYXN1cmVzIGFuZCBzdGFuZGFyZGl6ZWQgRXVjbGlkZWFuIGRpc3RhbmNlcyBhcmUgY29tcGFyYWJsZS4KKiBGb3Igb3RoZXIgbWV0aG9kcywgc2VlOiBNaWxsaWdhbiwgRy4gVy4sICYgQ29vcGVyLCBNLiBDLiAoMTk4OCkuIEEgc3R1ZHkgb2Ygc3RhbmRhcmRpemF0aW9uIG9mIHZhcmlhYmxlcyBpbiBjbHVzdGVyIGFuYWx5c2lzLiBfSm91cm5hbCBvZiBjbGFzc2lmaWNhdGlvbl8sIF81XygyKSwgMTgxLTIwNC4KCmBgYHtyfQp4PWMoMywgMSwgNCwgMTUsIDkyKQp5PWMoMzAsMiAsIDksIDIwLCA0OCkKKHgtbWVhbih4KSkvc2QoeCkKc2NhbGUoeCkKKHktbWVhbih5KSkvc2QoeSkKc2NhbGUoeSkKYGBgCgojIERpc3RhbmNlIG1hdHJpeCBjb21wdXRhdGlvbgoqIFdl4oCZbGwgdXNlIGEgc3Vic2V0IG9mIHRoZSBkYXRhIFVTQXJyZXN0cwoqICBXZeKAmWxsIHVzZSBvbmx5IGEgYnkgdGFraW5nIDE1IHJhbmRvbSByb3dzIGFtb25nIHRoZSA1MCByb3dzIGluIHRoZSBkYXRhIHNldC4gCiogTmV4dCwgd2Ugc3RhbmRhcmRpemUgdGhlIGRhdGEgdXNpbmcgdGhlIGZ1bmN0aW9uIHNjYWxlKCk6CgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygiRmFjdG9NaW5lUiIpCmxpYnJhcnkoIkZhY3RvTWluZVIiKQpkYXRhKCJVU0FycmVzdHMiKSAjIExvYWRpbmcKaGVhZChVU0FycmVzdHMsIDMpICMgUHJpbnQgdGhlIGZpcnN0IDMgcm93cwpzZXQuc2VlZCgxMjMpCnNzIDwtIHNhbXBsZSgxOjUwLCAxNSkgIyBUYWtlIDE1IHJhbmRvbSByb3dzCmRmIDwtIFVTQXJyZXN0c1tzcywgXSAjIFN1YnNldCB0aGUgMTUgcm93cwpkZi5zY2FsZWQgPC0gc2NhbGUoZGYpICMgU3RhbmRhcmRpemUgdGhlIHZhcmlhYmxlcwpgYGAKCjwhLS1zdGFja2VkaXRfZGF0YToKZXlKb2FYTjBiM0o1SWpwYkxURTFNakUwT1RJeE1Ua3NNVFk0TlRNNU9Ua3hYWDA9Ci0tPg==