knitr::opts_chunk$set(echo = TRUE)
Definition of a distance
A distance function or metric on the space \(\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}.\] * A1 and A2 are 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 and A2 hold.
- A3 is also hold using the fact that \(|a+b|\leq |a|+|b|\) for any reals \(a,b\).
- There exists also a weighted version of the above 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 both \(x_i\) and \(y_i\) are zero.
- 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 ditance.
Let us 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,
\] or conversely by \[
\|\mathbf{x}\|_p=d(\mathbf{x},\mathbf{0}),
\] where \(\mathbf{0}\) is the null-vetor of \(\mathbb{R}^n\).
Chebyshev distance
- At the limit, we get the Chebyshev distance 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 P3 is based on the Minkowski inequality.
- For any nonnegative real numbers \(a_1,\cdots,a_n\); \(b_1,\cdots,b_n\), and for \(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 P3, 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>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.
- It stat
- 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\), then \[
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 the inequalities topic 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 P1 and P3 are not satisfied.
Spearman correlation distance
- To calculate the Spearman’s rank-order correlation or Spearman’s correlation coefficient for short, we need to map seperately each of the vectors to ranked data values: \(\mathbf{x}\rightarrow \mathbf{x}^r=(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 \(\mathbf{x}^r=(2,1,3,4,5)\).
x=c(3, 1, 4, 15, 92)
xr=rank(x)
xr
The Spearman’s rank correlation of two numerical variables \(\mathbf{x}\) and \(\mathbf{y}\) is simply the Pearson correlation of the two correspnding rank-oerder variables \(\mathbf{x}^r\) and \(\mathbf{y}^r\), i.e. \(\rho(\mathbf{x}^r,\mathbf{y}^r)\). 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(\mathbf{x}^r,\mathbf{y}^r)=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(\mathbf{x^r},\mathbf{y^r}).
\]
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)
xr=rank(x)
y=c(30,2 , 9, 20, 48)
yr=rank(y)
d=xr-yr
d
cor(xr,yr)
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")
Standardization of variables
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(): # Data
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
Ci0tLQp0aXRsZTogIkRpc3RhbmNlIGFuZCBkaXNzaW1pbGFyaXRpZXMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCgojIERlZmluaXRpb24gb2YgYSBkaXN0YW5jZQoKKiBBIGRpc3RhbmNlIGZ1bmN0aW9uIG9yIG1ldHJpYyBvbiB0aGUgc3BhY2UgJFxtYXRoYmJ7Un1ebixcOm5cZ2VxIDEkLCBpcyBhIGZ1bmN0aW9uICRkOlxtYXRoYmJ7Un1eblx0aW1lc1xtYXRoYmJ7Un1eblxyaWdodGFycm93IFxtYXRoYmJ7Un0kLgoqIEEgZGlzdGFuY2UgZnVuY3Rpb24gbXVzdCBzYXRpc2Z5IHNvbWUgcmVxdWlyZWQgcHJvcGVydGllcyBvciBheGlvbXMuIAoqIFRoZXJlIGFyZSB0aHJlZSBtYWluIGF4aW9tcy4KCiogQTEuICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9IDBcaWZmIFxtYXRoYmZ7eH09XG1hdGhiZnt5fSQgKGlkZW50aXR5IG9mIGluZGlzY2VybmlibGVzKTsKCiogQTIuICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9IGQoXG1hdGhiZnt5fSxcbWF0aGJme3h9KSQgKHN5bW1ldHJ5KTsKCiogQTMuICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt6fSlcbGVxIGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KStkKFxtYXRoYmZ7eX0sXG1hdGhiZnt6fSkkICAodHJpYW5nbGUgaW5lcXVhbGl0eSksCndoZXJlICRcbWF0aGJme3h9PSh4XzEsXGNkb3RzLHhfbikkLCAkXG1hdGhiZnt5fT0oeV8xLFxjZG90cyx5X24pJCBhbmQgJFxtYXRoYmZ7en09KHpfMSxcY2RvdHMsel9uKSQgYXJlIGFsbCB2ZWN0b3JzIG9mICRcbWF0aGJie1J9Xm4kLgoqIFdlIHNob3VsZCB1c2UgdGhlIHRlcm0gX2Rpc3NpbWlsYXJpdHlfIHJhdGhlciB0aGFuIF9kaXN0YW5jZV8gd2hlbiBub3QgYWxsIHRoZSB0aHJlZSBheGlvbXMgQTEtQTMgYXJlIHZhbGlkLgoqIE1vc3Qgb2YgdGhlIHRpbWUsIHdlIHNoYWxsIHVzZSwgd2l0aCBzb21lIGFidXNlIG9mIHZvY2FidWxhcnksIHRoZSB0ZXJtIGRpc3RhbmNlLgoKIyBFeGVyY2ljZSAxCgoqIFByb3ZlIHRoYXQgdGhlIHRocmVlIGF4aW9tcyBBMS1BMyBpbXBseSB0aGUgbm9uLW5lZ2F0aXZpdHkgY29uZGl0aW9uOiAkJGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KVxnZXEgMC4kJAoKCiMgRXVjbGlkZWFuIGRpc3RhbmNlCgoKKiBJdCBpcyBkZWZpbmVkIGJ5OgoKJCRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9XHNxcnR7XHN1bV97aT0xfV5uICh4X2kteV9pKV4yfS4kJAoqIEExIGFuZCBBMiBhcmUgb25idmlvdXMuIFRoZSBwcm9vZiBvZiBBMyBpcyBwcm92aWRlZCBiZWxvdy4KCgojIE1hbmhhdHRhbiBkaXN0YW5jZQoKKiBUaGUgTWFuaGF0dGFuIGRpc3RhbmNlIGFsc28gY2FsbGVkICB0YXhpLWNhYiBtZXRyaWMgb3IgY2l0eS1ibG9jayBtZXRyaWMgaXMgZGVmaW5lZCBieToKCiQkZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pCj1cc3VtX3tpPTF9Xm4gfHhfaS15X2l8LgokJAoKKiBBMSBhbmQgQTIgaG9sZC4KKiBBMyBpcyBhbHNvIGhvbGQgdXNpbmcgdGhlIGZhY3QgdGhhdCAkfGErYnxcbGVxIHxhfCt8YnwkIGZvciBhbnkgcmVhbHMgJGEsYiQuCiogVGhlcmUgZXhpc3RzIGFsc28gYSAgd2VpZ2h0ZWQgdmVyc2lvbiAgb2YgdGhlIGFib3ZlIGRpc3RhbmNlIGNhbGxlZCB0aGUgQ2FuYmVycmEgZGlzdGFuY2UuCgpbTWFuaGF0dGFuIGRpc3RhbmNlIHZzIEV1Y2xpZGVhbiBkaXN0YW5jZSBHcmFwaF0oaHR0cHM6Ly91cGxvYWQud2lraW1lZGlhLm9yZy93aWtpcGVkaWEvY29tbW9ucy8wLzA4L01hbmhhdHRhbl9kaXN0YW5jZS5zdmcpCgpgYGB7cn0KeCA9IGMoMCwgMCkKeSA9IGMoNiw2KQpkaXN0KHJiaW5kKHgsIHkpLCBtZXRob2QgPSAiZXVjbGlkaWFuIikKNipzcXJ0KDIpCmRpc3QocmJpbmQoeCwgeSksIG1ldGhvZCA9ICJtYW5oYXR0YW4iKQpgYGAKCgojIENhbmJlcnJhIGRpc3RhbmNlCgoqIEl0IGlzIGRlZmluZWQgYnk6CgokJGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KQo9XHN1bV97aT0xfV5uIFxmcmFje3x4X2kteV9pfH17fHhfaXwrfHlfaXx9LiQkCgoqIE5vdGUgdGhhdCB0aGUgdGVybSAkfHhfaSDiiJIgeV9pfC8ofHhfaXwrfHlfaXwpJCBpcyBub3QgcHJvcGVybHkgZGVmaW5lZCB3aGVuIGJvdGggJHhfaSQgYW5kICR5X2kkIGFyZSB6ZXJvLgoqIEJ5IGNvbnZlbnRpb24gd2Ugc2V0IHRoZSByYXRpbyB0byBiZSB6ZXJvIGluIHRoYXQgY2FzZS4KKiBUaGUgQ2FuYmVycmEgZGlzdGFuY2UgaXMgc3BlY2lhbGx5IHNlbnNpdGl2ZSB0byBzbWFsbCBjaGFuZ2VzIG5lYXIgemVyby4KCmBgYHtyfQp4ID0gYygwLCAwKQp5ID0gYyg2LDYpCmRpc3QocmJpbmQoeCwgeSksIG1ldGhvZCA9ICJjYW5iZXJyYSIpCjYvNis2LzYKYGBgCgoKIyBFeGVyY2ljZSAyCgoqIFByb3ZlIHRoYXQgdGhlIENhbmJlcnJhIGRpc3RhbmNlIGlzIGEgdHJ1ZSBkaXN0YW5jZS4KCiMgTWlua293c2tpIGRpc3RhbmNlCgoqIEJvdGggdGhlIEV1Y2xpZGlhbiBhbmQgdGhlIE1hbmF0dGFuIGRpc3RhbmNlcyBhcmUgc3BlY2lhbCBjYXNlcyBvZiAgdGhlIE1pbmtvd3NraSBkaXN0YW5jZSB3aGljaCBpcyBkZWZpbmVkLCBmb3IgJHBcZ2VxIDEkLCBieTogCgokJApkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9ClxsZWZ0W1xzdW1fe2k9MX0gfHhfaS15X2l8XntwfVxyaWdodF1eezEvcH0uCiQkCiAqIEZvciAkcD0xJCwgd2UgZ2V0IHRoZSBNYW5oYXR0YW4gZGlzdGFuY2UuCiAqIEZvciAkcD0yJCwgd2UgZ2V0IHRoZSBFdWNsaWRpYW4gZGl0YW5jZS4KCgoqIExldCB1cyBkZWZpbmU6IAokJFx8XG1hdGhiZnt4fVx8X3BcZXF1aXZcbGVmdFtcc3VtX3tpPTF9Xm4gfHhfaXxee3B9XHJpZ2h0XV57MS9wfSwkJAp3aGVyZSAkXHxcbWF0aGJme1xjZG90fVx8X3AkIGlzIGtub3duIGFzIHRoZSAkcCQtbm9ybSBvciBNaW5rb3dza2kgbm9ybS4KCiogTm90ZSB0aGF0IHRoZSBNaW5rb3dza2kgZGlzdGFuY2UgYW5kIG5vcm0gYXJlIHJlbGF0ZWQgYnk6CiQkCmQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KT1cfFxtYXRoYmZ7eH0tXG1hdGhiZnt5fVx8X3AsCiQkCm9yIGNvbnZlcnNlbHkgYnkKJCQKXHxcbWF0aGJme3h9XHxfcD1kKFxtYXRoYmZ7eH0sXG1hdGhiZnswfSksCiQkCndoZXJlICRcbWF0aGJmezB9JCBpcyB0aGUgbnVsbC12ZXRvciBvZiAkXG1hdGhiYntSfV5uJC4KCiMgQ2hlYnlzaGV2IGRpc3RhbmNlIAoKKiBBdCB0aGUgbGltaXQsIHdlIGdldCB0aGUgQ2hlYnlzaGV2IGRpc3RhbmNlIGRlZmluZWQgYnk6CgokJApkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9XG1heF97aT0xLFxjZG90cyxufSh8eF9pLXlfaXwpPVxsaW1fe3BccmlnaHRhcnJvd1xpbmZ0eX0KXGxlZnRbXHN1bV97aT0xfSB8eF9pLXlfaXxee3B9XHJpZ2h0XV57MS9wfS4KJCQKCiogVGhlIGNvcnJlc3BvbmRpbmcgbm9ybSBpczoKJCQKXHxcbWF0aGJme3h9fF9caW5mdHk9XG1heF97aT0xLFxjZG90cyxufSh8eF9pfCkuCiQkCgojIE1pbmtvd3NraSBpbmVxdWFsaXR5CgoqIFRoZSBwcm9vZiBvZiB0aGUgdHJpYW5ndWxhciBpbmVxdWFsaXR5IFAzIGlzIGJhc2VkIG9uIHRoZSBNaW5rb3dza2kgaW5lcXVhbGl0eS4gCiogRm9yIGFueSBub25uZWdhdGl2ZSByZWFsIG51bWJlcnMgJGFfMSxcY2RvdHMsYV9uJDsgJGJfMSxcY2RvdHMsYl9uJCwgYW5kIGZvciAkcFxnZXEgMSQsIHdlIGhhdmU6CgokJApcbGVmdFtcc3VtX3tpPTF9Xm4gKGFfaStiX2kpXntwfVxyaWdodF1eezEvcH1cbGVxClxsZWZ0W1xzdW1fe2k9MX1ebiBhX2lee3B9XHJpZ2h0XV57MS9wfQorClxsZWZ0W1xzdW1fe2k9MX1ebiBiX2lee3B9XHJpZ2h0XV57MS9wfS4KJCQKCiogVG8gcHJvdmUgdGhhdCB0aGUgTWlua293c2tpIGRpc3RhbmNlIHNhdGlzZmllcyBQMywgbm90aWNlIHRoYXQgCgokJAogXHN1bV97aT0xfV5ufHhfaS16X2l8XntwfT0gXHN1bV97aT0xfV5ufCh4X2kteV9pKSsoeV9pLXpfaSl8XntwfS4KJCQKKiBTaW5jZSBmb3IgYW55IHJlYWxzICR4LHkkLCB3ZSBoYXZlICR8eCt5fFxsZXEgfHh8K3x5fCQsIGFuZCB1c2luZyB0aGUgZmFjdCB0aGF0ICR4XnAkIGlzIGluY3JlYXNpbmcgaW4gJHg+MCQsIHdlIG9idGFpbjoKJCQKIFxzdW1fe2k9MX1ebnx4X2ktel9pfF57cH1cbGVxIFxzdW1fe2k9MX1ebih8eF9pLXlfaXwrfHlfaS16X2l8KV57cH0uCiQkCgoqIEFwcGx5aW5nIHRoZSBNaW5rb3dza2kgaW5lcXVhbGl0eSB3aXRoICRhX2k9fHhfaS15X2l8JCBhbmQgJGJfaT18eV9pLXpfaXwkLCAkaT0xLFxjZG90cyxuJCwgd2UgZ2V0OgokJAogXHN1bV97aT0xfV5ufHhfaS16X2l8XntwfVxsZXEgXGxlZnQoXHN1bV97aT0xfV5uIHx4X2kteV9pfF57cH1ccmlnaHQpXnsxL3B9K1xsZWZ0KFxzdW1fe2k9MX1ebiB8eV9pLXpfaXxee3B9XHJpZ2h0KV57MS9wfS4KJCQKCgojIEjDtmxkZXIgaW5lcXVhbGl0eQoKKiBUaGUgcHJvb2Ygb2YgdGhlIE1pbmtvd3NraSBpbmVxdWFsaXR5IGl0c2VsZiByZXF1aXJlcyB0aGUgSMO2bGRlciBpbmVxdWFsaXR5LgoqIEl0IHN0YXQKKiBGb3IgYW55IG5vbm5lZ2F0aXZlIHJlYWwgbnVtYmVycyAkYV8xLFxjZG90cyxhX24kOyAkYl8xLFxjZG90cyxiX24kLCBhbmQgYW55ICRwLHE+MSQgd2l0aCAkMS9wKzEvcT0xJCwgd2UgaGF2ZQoKJCQKXHN1bV97aT0xfV5uIGFfaWJfaVxsZXEKXGxlZnRbXHN1bV97aT0xfV5uIGFfaV57cH1ccmlnaHRdXnsxL3B9ClxsZWZ0W1xzdW1fe2k9MX1ebiBiX2lee3F9XHJpZ2h0XV57MS9xfQokJAoqIFRoZSBwcm9vZiBvZiB0aGUgSMO2bGRlciBpbmVxdWFsaXR5IHJlbGllcyBvbiB0aGUgWW91bmcgaW5lcXVhbGl0eS4KKiBGb3IgYW55ICRhLGI+MCQsIHdlIGhhdmUKJCQKYWJcbGVxIFxmcmFje2FecH17cH0rXGZyYWN7Yl5xfXtxfSwKJCQKd2l0aCBlcXVhbGl0eSBvY2N1cmluZyBpZmY6ICRhXnA9Yl5xJC4gCiogVG8gcHJvdmUgdGhlIFlvdW5nIGluZXF1YWxpdHksIG9uZSBjYW4gdXNlIHRoZSAoc3RyaWN0KSBjb252ZXhpdHkgb2YgdGhlIGV4cG9uZW50aWFsIGZ1bmN0aW9uLgoqIEZvciBhbnkgcmVhbHMgJHgseSQsIHRoZW4gCiQkCmVee1xmcmFje3h9e3B9K1xmcmFje3l9e3F9IH1cbGVxIFxmcmFje2Vee3h9fXtwfStcZnJhY3tlXnt5fX17cX0uIAokJAoqIFdlIHRoZW4gc2V0ICR4PXBcbG4gYSQgYW5kICR5PXFcbG4gYiQgdG8gZ2V0IHRoZSBZb3VuZyBpbmVxdWFsaXR5LgoqIEEgZ29vZCByZWZlcmVuY2Ugb24gdGhlIGluZXF1YWxpdGllcyB0b3BpYyBpczogWi4gQ3ZldGtvdnNraSwgIEluZXF1YWxpdGllczogdGhlb3JlbXMsIHRlY2huaXF1ZXMgYW5kIHNlbGVjdGVkIHByb2JsZW1zLCAyMDEyLCBTcHJpbmdlciBTY2llbmNlICYgQnVzaW5lc3MgTWVkaWEuCiAjIENhdWNoeS1TY2h3YXJ0eiBpbmVxdWFsaXR5CiogTm90ZSB0aGF0IHRoZSB0cmlhbmd1bGFyIGluZXF1YWxpdHkgZm9yIHRoZSBNaW5rb3dza2kgZGlzdGFuY2UgaW1wbGllcyAKCiQkClxzdW1fe2k9MX1ebiB8eF9pfFxsZXEKXGxlZnRbXHN1bV97aT0xfV5uIHx4X2l8XntwfVxyaWdodF1eezEvcH0uCiQkCiogTm90ZSB0aGF0IGZvciAkcD0yJCwgd2UgaGF2ZSAkcT0yJC4gVGhlIEjDtmxkZXIgaW5lcXVhbGl0eSBpbXBsaWVzIGZvciB0aGF0IHNwZWNpYWwgY2FzZQokJApcc3VtX3tpPTF9Xm58eF9peV9pfFxsZXFcc3FydHtcc3VtX3tpPTF9Xm4geF9pXjJ9XHNxcnR7XHN1bV97aT0xfV5uIHlfaV4yfS4gCiQkCiogU2luY2UgdGhlIExIUyBvZCB0aGVzIGFib3ZlIGluZXF1YWxpdHkgaXMgZ3JlYXRlciB0aGVuICR8XHN1bV97aT0xfV5ueF9peV9pfCQsIHdlIGdldCB0aGUgQ2F1Y2h5LVNjaHdhcnR6IGluZXF1YWxpdHkKCiQkCnxcc3VtX3tpPTF9Xm54X2l5X2l8XGxlcVxzcXJ0e1xzdW1fe2k9MX1ebiB4X2leMn1cc3FydHtcc3VtX3tpPTF9Xm4geV9pXjJ9LiAKJCQKKiBVc2luZyB0aGUgZG90IHByb2R1Y3Qgbm90YXRpb24gY2FsbGVkIGFsc28gc2NhbGFyIHByb2R1Y3Qgbm9hdGlvbiAkXG1hdGhiZnt4XGNkb3QgeX09XHN1bV97aT0xfV5ueF9peV9pJCwgYW5kIHRoZSBub3JtIG5vdGF0aW9uICRcfFxtYXRoYmZ7XGNkb3R9XHxfMiBcfCQsIHRoZSBDYXVjaHktU2Nod2FydCBpbmVxdWFsaXR5IGlzOgoKJCR8XG1hdGhiZnt4XGNkb3QgeX0gfCBcbGVxIFx8XG1hdGhiZnt4fVx8XzIgXHwgXG1hdGhiZnt5fVx8XzIuJCQKCiMgUGVhcnNvbiBjb3JyZWxhdGlvbiBkaXN0YW5jZSAKCiogVGhlIFBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQgaXMgYSBzaW1pbGFyaXR5IG1lYXN1cmUgb24gJFxtYXRoYmJ7Un1ebiQgZGVmaW5lZCBieToKJCQKXHJobyhcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPQpcZnJhY3tcc3VtX3tpPTF9Xm4gKHhfaS1cYmFye1xtYXRoYmZ7eH19KSh5X2ktXGJhcntcbWF0aGJme3l9fSl9e3tcc3FydHtcc3VtX3tpPTF9Xm4gKHhfaS1cYmFye1xtYXRoYmZ7eH19KV4yXHN1bV97aT0xfV5uICh5X2ktXGJhcntcbWF0aGJme3l9fSleMn19fSwKJCQKd2hlcmUgJFxiYXJ7XG1hdGhiZnt4fX0kIGlzIHRoZSBtZWFuIG9mIHRoZSB2ZWN0b3IgJFxtYXRoYmZ7eH0kIGRlZmluZWQgYnk6IAokJFxiYXJ7XG1hdGhiZnt4fX09XGZyYWN7MX17bn1cc3VtX3tpPTF9Xm4geF9pLCQkCiogTm90ZSB0aGF0IHRoZSBQZWFyc29uIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IHNhdGlzZmllcyBQMiBhbmQgIGlzIGludmFyaWFudCB0byBhbnkgcG9zaXRpdmUgbGluZWFyIHRyYW5zZm9ybWF0aW9uLCBpLmUuOiAkJFxyaG8oXGFscGhhXG1hdGhiZnt4fSxcbWF0aGJme3l9KT1ccmhvKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSksJCQgZm9yIGFueSAkXGFscGhhPjAkLgoqIFRoZSBQZWFyc29uIGRpc3RhbmNlIChvciBjb3JyZWxhdGlvbiBkaXN0YW5jZSkgaXMgZGVmaW5lZCBieToKJCQKZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPTEtXHJobyhcbWF0aGJme3h9LFxtYXRoYmZ7eX0pLiQkCgoqIE5vdGUgdGhhdCB0aGUgUGVhcnNvbiBkaXN0YW5jZSBkb2VzIG5vdCBzYXRpc2Z5IEExIHNpbmNlICRkKFxtYXRoYmZ7eH0sXG1hdGhiZnt4fSk9MCQgZm9yIGFueSBub24temVybyB2ZWN0b3IgJFxtYXRoYmZ7eH0kLiBJdCBuZWl0aGVyIHNhdGlzZmllcyB0aGUgdHJpYW5nbGUgaW5lcXVhbGl0eS4gSG93ZXZlciwgdGhlIHN5bW1ldHJ5IHByb3BlcnR5IGlzIGZ1bGxmaWxsZWQuIAoKIyBDb3NpbmUgY29ycmVsYXRpb24gZGlzdGFuY2UKCiogVGhlIGNvc2luZSBvZiB0aGUgYW5nbGUgJFx0aGV0YSQgYmV0d2VlbiB0d28gdmVjdG9ycyAkXG1hdGhiZnt4fSQgYW5kICRcbWF0aGJme3l9JCBpcyBhIG1lYXN1cmUgb2Ygc2ltaWxhcml0eSBnaXZlbiBieToKJCQKXGNvcyhcdGhldGEpPVxmcmFje1xtYXRoYmZ7eH1cY2RvdCBcbWF0aGJme3l9fXtcfFxtYXRoYmZ7eH1cfF8yXHxcbWF0aGJme3l9XHxfMn09XGZyYWN7XHN1bV97aT0xfV5uIHhfaSB5X2l9e3tcc3FydHtcc3VtX3tpPTF9Xm4geF9pXjJcc3VtX3tpPTF9Xm4geV9pXjJ9fX0uCiQkCiogTm90ZSB0aGF0IHRoZSBjb3NpbmUgb2YgdGhlIGFuZ2xlIGJldHdlZW4gdGhlIHR3byBjZW50cmVkIHZlY3RvcnMgJCh4XzEtXGJhcntcbWF0aGJme3h9fSxcY2RvdHMseF9uLVxiYXJ7XG1hdGhiZnt4fX0pJCBhbmQgJCh5XzEtXGJhcntcbWF0aGJme3l9fSxcY2RvdHMseV9uLVxiYXJ7XG1hdGhiZnt5fX0pJCBjb2luY2lkZXMgd2l0aCB0aGUgUGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBvZiAkXG1hdGhiZnt4fSQgYW5kICRcbWF0aGJme3l9JC4gIAoKKiBUaGUgY29zaW5lIGNvcnJlbGF0aW9uIGRpc3RhbmNlIGlzIGRlZmluZWQgYnk6CiQkCmQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KT0xLVxjb3MoXHRoZXRhKS4KJCQKKiBJdCBzaGFyZXMgc2ltaWxhciBwcm9wZXJ0aWVzIHRoYW4gdGhlIFBlYXJzb24gY29ycmVsYXRpb24gZGlzdGFuY2UuIExpa2V3aXNlLCBBeGlvbXMgUDEgYW5kIFAzIGFyZSBub3Qgc2F0aXNmaWVkLgoKIyBTcGVhcm1hbiBjb3JyZWxhdGlvbiBkaXN0YW5jZSAKCiogVG8gY2FsY3VsYXRlIHRoZSBTcGVhcm1hbidzIHJhbmstb3JkZXIgY29ycmVsYXRpb24gb3IgU3BlYXJtYW4ncyBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBmb3Igc2hvcnQsIHdlIG5lZWQgdG8gbWFwIHNlcGVyYXRlbHkgZWFjaCBvZiB0aGUgdmVjdG9ycyB0byByYW5rZWQgZGF0YSB2YWx1ZXM6ICRcbWF0aGJme3h9XHJpZ2h0YXJyb3cgXG1hdGhiZnt4fV5yPSh4XzFecixcY2RvdHMseF9uXnIpJC4gSGVyZSwgJHhfaV5yJCBpcyB0aGUgcmFuayBvZiAkeF9pJCBhbW9uZyB0aGUgc2V0IG9mIHZhbHVlcyBvZiAkXG1hdGhiZnt4fSQuCiogV2UgaWxsdXN0cmF0ZSB0aGlzIHRyYW5zZm9ybWF0aW9uIHdpdGggYSBzaW1wbGUgZXhhbXBsZS4gSWYgJFxtYXRoYmZ7eH09KDMsIDEsIDQsIDE1LCA5MikkLCB0aGVuIHRoZSByYW5rLW9yZGVyIHZlY3RvciBpcyAkXG1hdGhiZnt4fV5yPSgyLDEsMyw0LDUpJC4gIAoKCmBgYHtyfQp4PWMoMywgMSwgNCwgMTUsIDkyKQp4cj1yYW5rKHgpCnhyCmBgYAoKKiBUaGUgU3BlYXJtYW4ncyByYW5rIGNvcnJlbGF0aW9uIG9mIHR3byBudW1lcmljYWwgdmFyaWFibGVzICRcbWF0aGJme3h9JCAgYW5kICRcbWF0aGJme3l9JCBpcyBzaW1wbHkgdGhlIFBlYXJzb24gY29ycmVsYXRpb24gb2YgdGhlIHR3byBjb3JyZXNwbmRpbmcgcmFuay1vZXJkZXIgdmFyaWFibGVzICRcbWF0aGJme3h9XnIkIGFuZCAkXG1hdGhiZnt5fV5yJCwgaS5lLiAkXHJobyhcbWF0aGJme3h9XnIsXG1hdGhiZnt5fV5yKSQuIFRoaXMgbWVhc3VyZSBpcyBpcyB1c2VmdWwgYmVjYXVzZSBpdCBpcyBtb3JlIHJvYnVzdCBhZ2FpbnN0IG91dGxpZXJzIHRoYW4gdGhlIFBlYXJzb24gY29ycmVsYXRpb24uCiogSWYgYWxsICB0aGUgJG4kICByYW5rcyBhcmUgZGlzdGluY3QsIGl0IGNhbiBiZSBjb21wdXRlZCB1c2luZyB0aGUgZm9sbG93aW5nIGZvcm11bGE6CiQkClxyaG8oXG1hdGhiZnt4fV5yLFxtYXRoYmZ7eX1ecik9MS1cZnJhY3s2XHN1bV97aT0xfV5uIGRfaV4yfXtuKG5eMi0xKX0sCiQkCndoZXJlICRkX2k9eF9pXnIteV9pXnIsXDppPTEsXGNkb3RzLG4kLgogKiBUaGUgc3BlYXJtYW4gZGlzdGFuY2UgaXMgdGhlbiBkZWZpbmVkIGJ5OgokJApkKFxtYXRoYmZ7eH0sXG1hdGhiZnt5fSk9MS1ccmhvKFxtYXRoYmZ7eF5yfSxcbWF0aGJme3lecn0pLgokJAoqIEl0IGNhbiBiZSBzaG93biB0aGF0IGVhc2FseSB0aGF0IGl0IGlzIG5vdCBhIHByb3BlciBkaXN0YW5jZS4KCiogSWYgYWxsICB0aGUgJG4kICByYW5rcyBhcmUgZGlzdGluY3QsIHdlIGdldDoKJCQKZChcbWF0aGJme3h9LFxtYXRoYmZ7eX0pPVxmcmFjezZcc3VtX3tpPTF9Xm4gZF9pXjJ9e24obl4yLTEpfS4KJCQKCmBgYHtyfQp4PWMoMywgMSwgNCwgMTUsIDkyKQp4cj1yYW5rKHgpCnk9YygzMCwyICwgOSwgMjAsIDQ4KQp5cj1yYW5rKHkpCmQ9eHIteXIKZApjb3IoeHIseXIpCjEtNipzdW0oZF4yKS8oNSooNV4yLTEpKQpgYGAKCgojIEtlbmRhbGwgdGF1IGRpc3RhbmNlIAoKKiBUaGUgS2VuZGFsbCByYW5rIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGlzIGNhbGN1bGF0ZWQgZnJvbSB0aGUgbnVtYmVyIG9mIGNvcnJlc3BvbmRhbmNlcyBiZXR3ZWVuIHRoZSByYW5raW5ncyBvZiAkXG1hdGhiZnt4fSQgYW5kIHRoZSByYW5raW5ncyBvZiAkXG1hdGhiZnt5fSQuCiogICBUaGUgbnVtYmVyIG9mIHBhaXJzIG9mIG9ic2VydmF0aW9ucyBhbW9uZyAkbiQgb2JzZXJ2YXRpb25zIG9yIHZhbHVlcyBpczogCiQke24gXGNob29zZSAyfSA9XGZyYWN7bihuLTEpfXsyfS4kJAoKKiBUaGUgcGFpcnMgb2Ygb2JzZXJ2YXRpb25zICQoeF97aX0seF97an0pJCAgYW5kICAkKHlfe2l9LHlfe2p9KSQgYXJlIHNhaWQgdG8gYmUgX2NvbmNvcmRhbnRfIGlmOiAkJFx0ZXh0e3NpZ259KHhfai14X2kpPVx0ZXh0e3NpZ259KHlfai15X2kpLCQkIGFuZCB0byBiZSBfZGlzY29yZGFudF8gaWY6ICAkJFx0ZXh0e3NpZ259KHhfai14X2kpPS1cdGV4dHtzaWdufSh5X2oteV9pKSwkJAp3aGVyZSAkXHRleHR7c2lnbn0oXGNkb3QpJCByZXR1cm5zICAkMSQgZm9yIHBvc2l0aXZlIG51bWJlcnMgYW5kICAkLTEkIG5lZ2F0aXZlIG51bWJlcnMgYW5kICQwJCBvdGhlcndpc2UuCiogSWYgJHhfaT14X2okIG9yICR5X2k9eV9qJCAob3IgYm90aCksIHRoZXJlIGlzIGEgdGllLgoqIFRoZSBLZW5kYWxsICRcdGF1JCBjb2VmZmljaWVudCBpcyBkZWZpbmVkIGJ5IChuZWdsZWN0aW5nIHRpZXMpOgokJFx0YXUgPVxmcmFjIHsxfXtuKG4tMSl9XHN1bV97aT0xfV57bn1cc3VtX3tqPTF9Xm5cdGV4dHtzaWdufSh4X2oteF9pKVx0ZXh0e3NpZ259KHlfai15X2kpLiQkCiogTGV0ICRuX2MkIChyZXNwLiAkbl9kJCkgYmUgdGhlIG51bWJlciBvZiBjb25jb3JkYW50IChyZXNwLiBkaXNjb3JkYW50KSBwYWlycywgd2UgaGF2ZSAkJFx0YXUgPVxmcmFjIHsyKG5fYy1uX2QpfXtuKG4tMSl9LiQkIAoqIFRoZSBLZW5kYWxsIHRhdSBkaXN0YW5jZSBpcyB0aGVuOiAkJGQoXG1hdGhiZnt4fSxcbWF0aGJme3l9KT0xLVx0YXUuICQkCiogUmVtYXJrOiB0aGUgdHJpYW5ndWxhciBpbmVxdWFsaXR5IG1heSBmYWlsIGluIGNhc2VzIHdoZXJlIHRoZXJlIGFyZSB0aWVzLgoKYGBge3J9Cng9YygzLCAxLCA0LCAxNSwgOTIpCnk9YygzMCwyICwgOSwgMjAsIDQ4KQp0YXU9MApmb3IgKGkgaW4gMTo1KQp7ICAKdGF1PXRhdStzaWduKHggLXhbaV0pJSolc2lnbih5IC15W2ldKQp9CnRhdT10YXUvKDUqNCkKdGF1CmNvcih4LHksIG1ldGhvZD0ia2VuZGFsbCIpCmBgYAoKIyBTdGFuZGFyZGl6YXRpb24gb2YgdmFyaWFibGVzIAoqIFZhcmlhYmxlcyBhcmUgb2Z0ZW4gc3RhbmRhcmRpemVkIGJlZm9yZSBtZWFzdXJpbmcgZGlzc2ltaWxhcml0aWVzLgoqIFN0YW5kYXJkaXphdGlvbiBjb252ZXJ0cyB0aGUgb3JpZ2luYWwgdmFyaWFibGVzIGludG8gdW5pdGVsZXNzIHZhcmlhYmxlcy4KKiBBIHdlbGwga25vd24gbWV0aG9kIGlzIHRoZSB6LXNjb3JlIHRyYW5zZm9ybWF0aW9uOiAKJCQKXG1hdGhiZnt4fVxyaWdodGFycm93IChcZnJhY3t4XzEtXGJhcntcbWF0aGJme3h9fX17c19cbWF0aGJme3h9fSxcY2RvdHMsXGZyYWN7eF9uLVxiYXJ7XG1hdGhiZnt4fX19e3NfXG1hdGhiZnt4fX0pLAokJCAKd2hlcmUgJHNfXG1hdGhiZnt4fSQgaXMgdGhlIHNhbXBsZSBzdGFuZGFyZCBkZXZpYXRpb24gZ2l2ZW4gYnk6CiQkCnNfXG1hdGhiZnt4fT1cZnJhY3sxfXtuLTF9XHN1bV97aT0xfV5uKHhfaS1cYmFye1xtYXRoYmZ7eH19KV4yLgokJCAKCiogVGhlIHRyYW5zZm9ybWVkIHZhcmlhYmxlIHdpbGwgaGF2ZSBhIG1lYW4gb2YgJDAkIGFuZCBhIHZhcmlhbmNlIG9mICQxJC4KKiBUaGUgcmVzdWx0IG9idGFpbmVkIHdpdGggUGVhcnNvbiBjb3JyZWxhdGlvbiBtZWFzdXJlcyBhbmQgc3RhbmRhcmRpemVkIEV1Y2xpZGVhbiBkaXN0YW5jZXMgYXJlIGNvbXBhcmFibGUuCiogRm9yIG90aGVyIG1ldGhvZHMsIHNlZTogTWlsbGlnYW4sIEcuIFcuLCAmIENvb3BlciwgTS4gQy4gKDE5ODgpLiBBIHN0dWR5IG9mIHN0YW5kYXJkaXphdGlvbiBvZiB2YXJpYWJsZXMgaW4gY2x1c3RlciBhbmFseXNpcy4gX0pvdXJuYWwgb2YgY2xhc3NpZmljYXRpb25fLCBfNV8oMiksIDE4MS0yMDQuCgpgYGB7cn0KeD1jKDMsIDEsIDQsIDE1LCA5MikKeT1jKDMwLDIgLCA5LCAyMCwgNDgpCih4LW1lYW4oeCkpL3NkKHgpCnNjYWxlKHgpCih5LW1lYW4oeSkpL3NkKHkpCnNjYWxlKHkpCmBgYAoKIyBEaXN0YW5jZSBtYXRyaXggY29tcHV0YXRpb24KKiBXZeKAmWxsIHVzZSBhIHN1YnNldCBvZiB0aGUgZGF0YSBVU0FycmVzdHMKKiAgV2XigJlsbCB1c2Ugb25seSBhIGJ5IHRha2luZyAxNSByYW5kb20gcm93cyBhbW9uZyB0aGUgNTAgcm93cyBpbiB0aGUgZGF0YSBzZXQuIAoqIE5leHQsIHdlIHN0YW5kYXJkaXplIHRoZSBkYXRhIHVzaW5nIHRoZSBmdW5jdGlvbiBzY2FsZSgpOgojIERhdGEKCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJGYWN0b01pbmVSIikKbGlicmFyeSgiRmFjdG9NaW5lUiIpCmRhdGEoIlVTQXJyZXN0cyIpICMgTG9hZGluZwpoZWFkKFVTQXJyZXN0cywgMykgIyBQcmludCB0aGUgZmlyc3QgMyByb3dzCnNldC5zZWVkKDEyMykKc3MgPC0gc2FtcGxlKDE6NTAsIDE1KSAjIFRha2UgMTUgcmFuZG9tIHJvd3MKZGYgPC0gVVNBcnJlc3RzW3NzLCBdICMgU3Vic2V0IHRoZSAxNSByb3dzCmRmLnNjYWxlZCA8LSBzY2FsZShkZikgIyBTdGFuZGFyZGl6ZSB0aGUgdmFyaWFibGVzCmBgYAoKCgoKCjwhLS1zdGFja2VkaXRfZGF0YToKZXlKb2FYTjBiM0o1SWpwYk1USTVORE00TnpRNU1sMTkKLS0+