Pawlowsky-Glahn, Vera, Juan José Egozcue, and Raimon Tolosana-Delgado. Modeling and analysis of compositional data. John Wiley & Sons, 2015.

http://www.stat.boogaart.de/compositionsRBook/

5.2 Sample center, total variance, and variation matrix

Definition 5.1 (Sample centre, or compositional mean).

A value of central tendency of a compositional sample is the closed geometric mean. It is called center. For a data set of size \(n\), it is defined as \[ \operatorname{cen}(\mathbf{X})=\hat{\mathrm{g}}=C\left[\hat{g}_{1}, \hat{g}_{2}, \ldots, \hat{g}_{D}\right] \] with \(\hat{g}_{j}=\left(\prod_{i=1}^{n} x_{i j}\right)^{1 / n}, j=1,2, \ldots, D\)

library(compositions)
setwd("D:/Compositional Data analysis/CODA_OCT2021")
GeoChemSed= read.csv("geochemsed.csv",header=TRUE,skip=1)
head(GeoChemSed)
##   Sample GS Qz_added  SiO2 TiO2 Al2O3   MnO  MgO  CaO Na2O  K2O P2O5 Fe2O3t
## 1 RT1-1  -1        0 73.40 0.22 14.19 0.037 0.48 1.04 4.33 4.18 0.06   1.74
## 2 RT1-1   0        0 73.86 0.26 14.00 0.038 0.51 1.14 4.32 3.95 0.07   1.72
## 3 RT1-1   1        0 73.85 0.30 13.85 0.041 0.55 1.15 4.31 3.82 0.07   1.82
## 4 RT1-1   2        0 78.12 0.32 11.36 0.043 0.57 1.02 3.41 3.16 0.06   1.81
## 5 RT1-1   3        0 78.77 0.39 10.29 0.061 0.86 1.07 2.81 2.92 0.07   2.54
## 6 RT1-1   4        0 74.43 0.54 11.88 0.073 0.94 2.22 2.95 3.20 0.29   2.96
##   Summe.HE.  Ba Co Cr Cu Ga Nb Ni Pb  Rb Sc  Sr  V  Y Zn  Zr Nd Summe.SE.
## 1     99.68 720  2 11  0 15 12  5 14 148  3 143 17 24 25 134 30      1303
## 2     99.87 689  1  8  1 15 13  4 16 137  3 148 17 23 26 145 33      1279
## 3     99.75 673  3  3  0 15 15  7 16 134  0 149 17 26 29 118 42      1247
## 4     99.87 566  1 10  1 12 17  5 17 114  1 126 21 27 32 108 44      1102
## 5     99.78 579  4  8  0 12 19  6 20 121  6 129 27 31 50 240 39      1291
## 6     99.48 736  5 10  3 16 30 12 20 136 10 221 33 67 52 767 57      2175
##   Summe.SEHE.
## 1       99.81
## 2       99.99
## 3       99.88
## 4       99.98
## 5       99.91
## 6       99.70
x = acomp(GeoChemSed, parts=c("Cr","Zn","Pb"))
 head(x)
##      Cr        Zn        Pb       
## [1,] 0.2200000 0.5000000 0.2800000
## [2,] 0.1600000 0.5200000 0.3200000
## [3,] 0.0625000 0.6041667 0.3333333
## [4,] 0.1694915 0.5423729 0.2881356
## [5,] 0.1025641 0.6410256 0.2564103
## [6,] 0.1219512 0.6341463 0.2439024
## attr(,"class")
## [1] acomp
x = acomp(x)
mean(x)
##        Cr        Zn        Pb 
## 0.1508323 0.5772793 0.2718884 
## attr(,"class")
## [1] acomp

Definition \(5.2\) (Variation matrix).

Dispersion in a compositional data set can be described either by the variation matrix, originally defined by Aitchison (1986) as \[ \mathbf{T}=\left(\begin{array}{cccc} t_{11} & t_{12} & \ldots & t_{1 D} \\ t_{21} & t_{22} & \ldots & t_{2 D} \\ \vdots & \vdots & \ddots & \vdots \\ t_{D 1} & t_{D 2} & \ldots & t_{D D} \end{array}\right), \quad t_{i j}=\operatorname{var}\left(\ln \frac{x_{i}}{x_{j}}\right) \]

or by the normalized variation matrix \[ \mathbf{T}^{*}=\left(\begin{array}{cccc} t_{11}^{*} & t_{12}^{*} & \cdots & t_{1 D}^{*} \\ t_{21}^{*} & t_{22}^{*} & \cdots & t_{2 D}^{*} \\ \vdots & \vdots & \ddots & \vdots \\ t_{D 1}^{*} & t_{D 2}^{*} & \cdots & t_{D D}^{*} \end{array}\right), \quad t_{i j}^{*}=\operatorname{var}\left(\frac{1}{\sqrt{2}} \ln \frac{x_{i}}{x_{j}}\right) \]

variation(x)
##           Cr        Zn        Pb
## Cr 0.0000000 0.2610508 0.3259089
## Zn 0.2610508 0.0000000 0.2780853
## Pb 0.3259089 0.2780853 0.0000000
summary(x)
## $mean
##        Cr        Zn        Pb 
## 0.1508323 0.5772793 0.2718884 
## attr(,"class")
## [1] acomp
## 
## $mean.ratio
##          Cr        Zn        Pb
## Cr 1.000000 0.2612814 0.5547583
## Zn 3.827291 1.0000000 2.1232216
## Pb 1.802587 0.4709824 1.0000000
## 
## $variation
##           Cr        Zn        Pb
## Cr 0.0000000 0.2610508 0.3259089
## Zn 0.2610508 0.0000000 0.2780853
## Pb 0.3259089 0.2780853 0.0000000
## 
## $expsd
##          Cr       Zn       Pb
## Cr 1.000000 1.666843 1.769831
## Zn 1.666843 1.000000 1.694416
## Pb 1.769831 1.694416 1.000000
## 
## $invexpsd
##           Cr        Zn        Pb
## Cr 1.0000000 0.5999366 0.5650255
## Zn 0.5999366 1.0000000 0.5901740
## Pb 0.5650255 0.5901740 1.0000000
## 
## $min
##           Cr         Zn         Pb
## Cr 1.0000000 0.03571429 0.05555556
## Zn 1.4838710 1.00000000 1.06666667
## Pb 0.5409836 0.12500000 1.00000000
## 
## $q1
##          Cr        Zn        Pb
## Cr 1.000000 0.1917421 0.4285714
## Zn 2.619048 1.0000000 1.4545455
## Pb 1.309524 0.3514831 1.0000000
## 
## $med
##          Cr        Zn        Pb
## Cr 1.000000 0.2857143 0.5454545
## Zn 3.500000 1.0000000 1.8125000
## Pb 1.833333 0.5517241 1.0000000
## 
## $q3
##          Cr        Zn        Pb
## Cr 1.000000 0.3819444 0.7638889
## Zn 5.215385 1.0000000 2.8453947
## Pb 2.333333 0.6875000 1.0000000
## 
## $max
##    Cr       Zn       Pb
## Cr  1 0.673913 1.848485
## Zn 28 1.000000 8.000000
## Pb 18 0.937500 1.000000
## 
## $missingness
##         missingType
## variable NMV BDL MAR MNAR SZ Err
##       Cr  87   0   0    0  0   0
##       Zn  87   0   0    0  0   0
##       Pb  87   0   0    0  0   0
## 
## attr(,"class")
## [1] "summary.acomp"
#Boxplots of all pairwise log ratios. This is the result of boxplot(x) with an acomp object
opar <- par(mar=c(3,3,1,1))
boxplot(x)

 par(opar)

5.3 Centering and scaling

# get the statistics:
 mn = mean(x)
 mvr = mvar(x)
# center the data:
 dat1 = scale(x,center=TRUE,scale=TRUE)
dat2 = (x-mn)/sqrt(mvr)
#If we only want to center the data, we can try either
 dat3 = scale(x,center=TRUE,scale=FALSE)
 dat4 = x-mn
plot(x)

plot(x, center = TRUE, axes = TRUE)

5.4 The biplot: a graphical display

  1. Two proportional variables have a quasi-constant log ratio (a small entry in the variation array); thus their link should be very short, and the arrow heads lie together.
  2. Inversely, if a link is very long, the log ratio of the two parts involved is highly variant (a large entry in the variation array). If we see three very long rays pointing towards different directions (at \(120^{\circ}\) approximately), a ternary diagram of these three parts will have a high spread, because their mutual links are also very long.
  3. The angle between two links should approximate the correlation coefficient between the two log ratios:
  • Two uncorrelated log ratios provide orthogonal rays.
  • Three or more parts lying on a common line have links forming either \(0^{\circ}\) or \(180^{\circ}\), thus being perfectly correlated (directly or inversely); in this case the subcomposition formed by these parts should show a one-dimensional pattern of variation; i.e., this subcomposition is probably collinear.
  • Two sets of collinear subcompositions which rays form \(90^{\circ}\) are (possibly) uncorrelated.
x = acomp(GeoChemSed,4:13)
pcx = princomp(x)
#The first thing we should do is to obtain the proportion of explained variance that the biplot will capture. This is obtained as
sum(pcx$sdev[1:2]^2)/mvar(x)
## [1] 0.9026089
# 0.9026 quite a good value. The biplot of the data is obtained with
opar <- par(mar=c(1,1,1,1))
dots = rep(".",times=nrow(x))
biplot(pcx, xlabs=dots)

par(opar)

A balance-dendrogram.

A balance-dendrogram is the joint representation of the ollowing elements: 1. a sequential binary partition, in the form of a tree structure; 2. the sample variance and mean of each balance, as vertical bars and inception points of these bars; 3. a box-plot, summarizing the order statistics of each balance on horizontal axes.

# first example: take the data set from the example, select only
# compositional parts
data(Hydrochem)
x = acomp(Hydrochem[,-c(1:5)])
gr = Hydrochem[,4] # river groups (useful afterwards)
# use an ilr basis coming from a clustering of parts
dd = dist(t(clr(x)))
hc1 = hclust(dd,method="ward.D")
plot(hc1)

mergetree=hc1$merge
CoDaDendrogram(X=acomp(x),mergetree=mergetree,col="red",range=c(-8,8),box.space=1)
# add the mean of each river
color=c("green3","red","blue","darkviolet")
aux = sapply(split(x,gr),mean)
aux
##             Anoia     Cardener LowerLLobregat UpperLLobregat
## H    7.681283e-12 1.741958e-11   1.419453e-11   1.950521e-11
## Na   1.057750e-01 1.223121e-01   1.406311e-01   5.440953e-02
## K    7.570733e-03 1.894806e-02   2.572564e-02   4.633610e-03
## Mg   4.087110e-02 3.800140e-02   3.464647e-02   3.037682e-02
## Ca   1.277392e-01 1.308379e-01   1.096325e-01   1.821764e-01
## Sr   2.258993e-03 2.430692e-03   1.655782e-03   2.132551e-03
## Ba   3.657214e-05 8.477833e-05   6.427606e-05   1.025198e-04
## NH4  4.696969e-04 2.568855e-04   5.946778e-04   2.338011e-04
## Cl   1.419020e-01 2.037912e-01   2.352855e-01   7.236928e-02
## NO3  4.288144e-03 8.873105e-03   6.638845e-03   4.734691e-03
## PO4  6.054526e-04 7.044785e-04   7.825075e-04   5.715776e-04
## SO4  3.210349e-01 1.784094e-01   1.655095e-01   2.146219e-01
## HCO3 2.428401e-01 2.911536e-01   2.741530e-01   4.285757e-01
## TOC  4.608154e-03 4.196326e-03   4.680093e-03   5.061633e-03
CoDaDendrogram(X=acomp(t(aux)),add=TRUE,col=color,type="points",pch=4)

# second example: box-plots by rivers (filled)
CoDaDendrogram(X=acomp(x),mergetree=mergetree,col="black",range=c(-8,8),type="l")
xsplit = split(x,gr)
for(i in 1:4){
 CoDaDendrogram(X=xsplit[[i]],col=color[i],type="box",box.pos=i-2.5,box.space=0.5,add=TRUE)
}

# third example: fewer parts, partition defined by a signary, and empty box-plots
x = acomp(Hydrochem[,c("Na","K","Mg","Ca","Sr","Ba","NH4")])
signary = t(matrix(  c(1,   1,   1,  1,   1,   1,  -1,
                       1,   1,  -1, -1,  -1,  -1,   0,
                       1,  -1,   0,  0,   0,   0,   0,
                       0,   0,  -1,  1,  -1,  -1,   0,
                       0,   0,   1,  0,  -1,   1,   0,
                       0,   0,   1,  0,   0,  -1,   0),ncol=7,nrow=6,byrow=TRUE))

CoDaDendrogram(X=acomp(x),signary=signary,col="black",range=c(-8,8),type="l")
xsplit = split(x,gr)
for(i in 1:4){
  CoDaDendrogram(X=acomp(xsplit[[i]]),border=color[i],
       type="box",box.pos=i-2.5,box.space=1.5,add=TRUE)
  CoDaDendrogram(X=acomp(xsplit[[i]]),col=color[i],
       type="line",add=TRUE)
}

LS0tDQp0aXRsZTogIkNoYXB0ZXIgNTogRXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyAiDQphdXRob3I6ICJIdW9uZyINCmRhdGU6ICI3LzExLzIwMjEiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIHdvcmRfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCi0tLQ0KDQpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkNCmBgYA0KDQpQYXdsb3dza3ktR2xhaG4sIFZlcmEsIEp1YW4gSm9zw6kgRWdvemN1ZSwgYW5kIFJhaW1vbiBUb2xvc2FuYS1EZWxnYWRvLiBNb2RlbGluZyBhbmQgYW5hbHlzaXMgb2YgY29tcG9zaXRpb25hbCBkYXRhLiBKb2huIFdpbGV5IFwmIFNvbnMsIDIwMTUuDQoNCmh0dHA6Ly93d3cuc3RhdC5ib29nYWFydC5kZS9jb21wb3NpdGlvbnNSQm9vay8gDQoNCg0KIyMgNS4yIFNhbXBsZSBjZW50ZXIsIHRvdGFsIHZhcmlhbmNlLCBhbmQgdmFyaWF0aW9uIG1hdHJpeA0KDQojIyMgRGVmaW5pdGlvbiA1LjEgKFNhbXBsZSBjZW50cmUsIG9yIGNvbXBvc2l0aW9uYWwgbWVhbikuDQoNCkEgdmFsdWUgb2YgY2VudHJhbCB0ZW5kZW5jeSBvZiBhIGNvbXBvc2l0aW9uYWwgc2FtcGxlIGlzIHRoZSBjbG9zZWQgZ2VvbWV0cmljIG1lYW4uIEl0IGlzIGNhbGxlZCBjZW50ZXIuIEZvciBhIGRhdGEgc2V0IG9mIHNpemUgJG4kLCBpdCBpcyBkZWZpbmVkIGFzDQokJA0KXG9wZXJhdG9ybmFtZXtjZW59KFxtYXRoYmZ7WH0pPVxoYXR7XG1hdGhybXtnfX09Q1xsZWZ0W1xoYXR7Z31fezF9LCBcaGF0e2d9X3syfSwgXGxkb3RzLCBcaGF0e2d9X3tEfVxyaWdodF0NCiQkDQp3aXRoICRcaGF0e2d9X3tqfT1cbGVmdChccHJvZF97aT0xfV57bn0geF97aSBqfVxyaWdodCleezEgLyBufSwgaj0xLDIsIFxsZG90cywgRCQNCg0KYGBge3J9DQpsaWJyYXJ5KGNvbXBvc2l0aW9ucykNCnNldHdkKCJEOi9Db21wb3NpdGlvbmFsIERhdGEgYW5hbHlzaXMvQ09EQV9PQ1QyMDIxIikNCkdlb0NoZW1TZWQ9IHJlYWQuY3N2KCJnZW9jaGVtc2VkLmNzdiIsaGVhZGVyPVRSVUUsc2tpcD0xKQ0KaGVhZChHZW9DaGVtU2VkKQ0KeCA9IGFjb21wKEdlb0NoZW1TZWQsIHBhcnRzPWMoIkNyIiwiWm4iLCJQYiIpKQ0KIGhlYWQoeCkNCmBgYA0KDQpgYGB7cn0NCnggPSBhY29tcCh4KQ0KbWVhbih4KQ0KYGBgDQoNCg0KIyMjIERlZmluaXRpb24gJDUuMiQgKFZhcmlhdGlvbiBtYXRyaXgpLg0KRGlzcGVyc2lvbiBpbiBhIGNvbXBvc2l0aW9uYWwgZGF0YSBzZXQgY2FuIGJlIGRlc2NyaWJlZCBlaXRoZXIgYnkgdGhlIHZhcmlhdGlvbiBtYXRyaXgsIG9yaWdpbmFsbHkgZGVmaW5lZCBieSBBaXRjaGlzb24gKDE5ODYpIGFzDQokJA0KXG1hdGhiZntUfT1cbGVmdChcYmVnaW57YXJyYXl9e2NjY2N9DQp0X3sxMX0gJiB0X3sxMn0gJiBcbGRvdHMgJiB0X3sxIER9IFxcDQp0X3syMX0gJiB0X3syMn0gJiBcbGRvdHMgJiB0X3syIER9IFxcDQpcdmRvdHMgJiBcdmRvdHMgJiBcZGRvdHMgJiBcdmRvdHMgXFwNCnRfe0QgMX0gJiB0X3tEIDJ9ICYgXGxkb3RzICYgdF97RCBEfQ0KXGVuZHthcnJheX1ccmlnaHQpLCBccXVhZCB0X3tpIGp9PVxvcGVyYXRvcm5hbWV7dmFyfVxsZWZ0KFxsbiBcZnJhY3t4X3tpfX17eF97an19XHJpZ2h0KQ0KJCQNCg0Kb3IgYnkgdGhlIG5vcm1hbGl6ZWQgdmFyaWF0aW9uIG1hdHJpeA0KJCQNClxtYXRoYmZ7VH1eeyp9PVxsZWZ0KFxiZWdpbnthcnJheX17Y2NjY30NCnRfezExfV57Kn0gJiB0X3sxMn1eeyp9ICYgXGNkb3RzICYgdF97MSBEfV57Kn0gXFwNCnRfezIxfV57Kn0gJiB0X3syMn1eeyp9ICYgXGNkb3RzICYgdF97MiBEfV57Kn0gXFwNClx2ZG90cyAmIFx2ZG90cyAmIFxkZG90cyAmIFx2ZG90cyBcXA0KdF97RCAxfV57Kn0gJiB0X3tEIDJ9XnsqfSAmIFxjZG90cyAmIHRfe0QgRH1eeyp9DQpcZW5ke2FycmF5fVxyaWdodCksIFxxdWFkIHRfe2kgan1eeyp9PVxvcGVyYXRvcm5hbWV7dmFyfVxsZWZ0KFxmcmFjezF9e1xzcXJ0ezJ9fSBcbG4gXGZyYWN7eF97aX19e3hfe2p9fVxyaWdodCkNCiQkDQoNCmBgYHtyfQ0KdmFyaWF0aW9uKHgpDQoNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkoeCkNCmBgYA0KDQpgYGB7cn0NCiNCb3hwbG90cyBvZiBhbGwgcGFpcndpc2UgbG9nIHJhdGlvcy4gVGhpcyBpcyB0aGUgcmVzdWx0IG9mIGJveHBsb3QoeCkgd2l0aCBhbiBhY29tcCBvYmplY3QNCm9wYXIgPC0gcGFyKG1hcj1jKDMsMywxLDEpKQ0KYm94cGxvdCh4KQ0KIHBhcihvcGFyKQ0KYGBgDQojIyA1LjMgQ2VudGVyaW5nIGFuZCBzY2FsaW5nDQoNCmBgYHtyfQ0KIyBnZXQgdGhlIHN0YXRpc3RpY3M6DQogbW4gPSBtZWFuKHgpDQogbXZyID0gbXZhcih4KQ0KIyBjZW50ZXIgdGhlIGRhdGE6DQogZGF0MSA9IHNjYWxlKHgsY2VudGVyPVRSVUUsc2NhbGU9VFJVRSkNCmRhdDIgPSAoeC1tbikvc3FydChtdnIpDQojSWYgd2Ugb25seSB3YW50IHRvIGNlbnRlciB0aGUgZGF0YSwgd2UgY2FuIHRyeSBlaXRoZXINCiBkYXQzID0gc2NhbGUoeCxjZW50ZXI9VFJVRSxzY2FsZT1GQUxTRSkNCiBkYXQ0ID0geC1tbg0KYGBgDQoNCmBgYHtyfQ0KcGxvdCh4KQ0KYGBgDQpgYGB7cn0NCnBsb3QoeCwgY2VudGVyID0gVFJVRSwgYXhlcyA9IFRSVUUpDQpgYGANCg0KIyMgNS40IFRoZSBiaXBsb3Q6IGEgZ3JhcGhpY2FsIGRpc3BsYXkNCg0KMS4gVHdvIHByb3BvcnRpb25hbCB2YXJpYWJsZXMgaGF2ZSBhIHF1YXNpLWNvbnN0YW50IGxvZyByYXRpbyAoYSBzbWFsbCBlbnRyeSBpbiB0aGUgdmFyaWF0aW9uIGFycmF5KTsgdGh1cyB0aGVpciBsaW5rIHNob3VsZCBiZSB2ZXJ5IHNob3J0LCBhbmQgdGhlIGFycm93IGhlYWRzIGxpZSB0b2dldGhlci4NCjIuIEludmVyc2VseSwgaWYgYSBsaW5rIGlzIHZlcnkgbG9uZywgdGhlIGxvZyByYXRpbyBvZiB0aGUgdHdvIHBhcnRzIGludm9sdmVkIGlzIGhpZ2hseSB2YXJpYW50IChhIGxhcmdlIGVudHJ5IGluIHRoZSB2YXJpYXRpb24gYXJyYXkpLiBJZiB3ZSBzZWUgdGhyZWUgdmVyeSBsb25nIHJheXMgcG9pbnRpbmcgdG93YXJkcyBkaWZmZXJlbnQgZGlyZWN0aW9ucyAoYXQgJDEyMF57XGNpcmN9JCBhcHByb3hpbWF0ZWx5KSwgYSB0ZXJuYXJ5IGRpYWdyYW0gb2YgdGhlc2UgdGhyZWUgcGFydHMgd2lsbCBoYXZlIGEgaGlnaCBzcHJlYWQsIGJlY2F1c2UgdGhlaXIgbXV0dWFsIGxpbmtzIGFyZSBhbHNvIHZlcnkgbG9uZy4NCjMuIFRoZSBhbmdsZSBiZXR3ZWVuIHR3byBsaW5rcyBzaG91bGQgYXBwcm94aW1hdGUgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGJldHdlZW4gdGhlIHR3byBsb2cgcmF0aW9zOg0KLSBUd28gdW5jb3JyZWxhdGVkIGxvZyByYXRpb3MgcHJvdmlkZSBvcnRob2dvbmFsIHJheXMuDQotIFRocmVlIG9yIG1vcmUgcGFydHMgbHlpbmcgb24gYSBjb21tb24gbGluZSBoYXZlIGxpbmtzIGZvcm1pbmcgZWl0aGVyICQwXntcY2lyY30kIG9yICQxODBee1xjaXJjfSQsIHRodXMgYmVpbmcgcGVyZmVjdGx5IGNvcnJlbGF0ZWQgKGRpcmVjdGx5IG9yIGludmVyc2VseSk7IGluIHRoaXMgY2FzZSB0aGUgc3ViY29tcG9zaXRpb24gZm9ybWVkIGJ5IHRoZXNlIHBhcnRzIHNob3VsZCBzaG93IGEgb25lLWRpbWVuc2lvbmFsIHBhdHRlcm4gb2YgdmFyaWF0aW9uOyBpLmUuLCB0aGlzIHN1YmNvbXBvc2l0aW9uIGlzIHByb2JhYmx5IGNvbGxpbmVhci4NCi0gVHdvIHNldHMgb2YgY29sbGluZWFyIHN1YmNvbXBvc2l0aW9ucyB3aGljaCByYXlzIGZvcm0gJDkwXntcY2lyY30kIGFyZSAocG9zc2libHkpIHVuY29ycmVsYXRlZC4NCg0KYGBge3J9DQp4ID0gYWNvbXAoR2VvQ2hlbVNlZCw0OjEzKQ0KcGN4ID0gcHJpbmNvbXAoeCkNCiNUaGUgZmlyc3QgdGhpbmcgd2Ugc2hvdWxkIGRvIGlzIHRvIG9idGFpbiB0aGUgcHJvcG9ydGlvbiBvZiBleHBsYWluZWQgdmFyaWFuY2UgdGhhdCB0aGUgYmlwbG90IHdpbGwgY2FwdHVyZS4gVGhpcyBpcyBvYnRhaW5lZCBhcw0Kc3VtKHBjeCRzZGV2WzE6Ml1eMikvbXZhcih4KQ0KIyAwLjkwMjYgcXVpdGUgYSBnb29kIHZhbHVlLiBUaGUgYmlwbG90IG9mIHRoZSBkYXRhIGlzIG9idGFpbmVkIHdpdGgNCm9wYXIgPC0gcGFyKG1hcj1jKDEsMSwxLDEpKQ0KZG90cyA9IHJlcCgiLiIsdGltZXM9bnJvdyh4KSkNCmJpcGxvdChwY3gsIHhsYWJzPWRvdHMpDQpwYXIob3BhcikNCmBgYA0KDQojIyMgQSBiYWxhbmNlLWRlbmRyb2dyYW0uDQoNCiBBIGJhbGFuY2UtZGVuZHJvZ3JhbSBpcyB0aGUgam9pbnQgcmVwcmVzZW50YXRpb24gb2YgdGhlIG9sbG93aW5nIGVsZW1lbnRzOg0KMS4gYSBzZXF1ZW50aWFsIGJpbmFyeSBwYXJ0aXRpb24sIGluIHRoZSBmb3JtIG9mIGEgdHJlZSBzdHJ1Y3R1cmU7DQoyLiB0aGUgc2FtcGxlIHZhcmlhbmNlIGFuZCBtZWFuIG9mIGVhY2ggYmFsYW5jZSwgYXMgdmVydGljYWwgYmFycyBhbmQgaW5jZXB0aW9uIHBvaW50cyBvZiB0aGVzZSBiYXJzOw0KMy4gYSBib3gtcGxvdCwgc3VtbWFyaXppbmcgdGhlIG9yZGVyIHN0YXRpc3RpY3Mgb2YgZWFjaCBiYWxhbmNlIG9uIGhvcml6b250YWwgYXhlcy4NCg0KYGBge3J9DQojIGZpcnN0IGV4YW1wbGU6IHRha2UgdGhlIGRhdGEgc2V0IGZyb20gdGhlIGV4YW1wbGUsIHNlbGVjdCBvbmx5DQojIGNvbXBvc2l0aW9uYWwgcGFydHMNCmRhdGEoSHlkcm9jaGVtKQ0KeCA9IGFjb21wKEh5ZHJvY2hlbVssLWMoMTo1KV0pDQpnciA9IEh5ZHJvY2hlbVssNF0gIyByaXZlciBncm91cHMgKHVzZWZ1bCBhZnRlcndhcmRzKQ0KIyB1c2UgYW4gaWxyIGJhc2lzIGNvbWluZyBmcm9tIGEgY2x1c3RlcmluZyBvZiBwYXJ0cw0KZGQgPSBkaXN0KHQoY2xyKHgpKSkNCmhjMSA9IGhjbHVzdChkZCxtZXRob2Q9IndhcmQuRCIpDQpwbG90KGhjMSkNCm1lcmdldHJlZT1oYzEkbWVyZ2UNCkNvRGFEZW5kcm9ncmFtKFg9YWNvbXAoeCksbWVyZ2V0cmVlPW1lcmdldHJlZSxjb2w9InJlZCIscmFuZ2U9YygtOCw4KSxib3guc3BhY2U9MSkNCiMgYWRkIHRoZSBtZWFuIG9mIGVhY2ggcml2ZXINCmNvbG9yPWMoImdyZWVuMyIsInJlZCIsImJsdWUiLCJkYXJrdmlvbGV0IikNCmF1eCA9IHNhcHBseShzcGxpdCh4LGdyKSxtZWFuKQ0KYXV4DQpDb0RhRGVuZHJvZ3JhbShYPWFjb21wKHQoYXV4KSksYWRkPVRSVUUsY29sPWNvbG9yLHR5cGU9InBvaW50cyIscGNoPTQpDQoNCiMgc2Vjb25kIGV4YW1wbGU6IGJveC1wbG90cyBieSByaXZlcnMgKGZpbGxlZCkNCkNvRGFEZW5kcm9ncmFtKFg9YWNvbXAoeCksbWVyZ2V0cmVlPW1lcmdldHJlZSxjb2w9ImJsYWNrIixyYW5nZT1jKC04LDgpLHR5cGU9ImwiKQ0KeHNwbGl0ID0gc3BsaXQoeCxncikNCmZvcihpIGluIDE6NCl7DQogQ29EYURlbmRyb2dyYW0oWD14c3BsaXRbW2ldXSxjb2w9Y29sb3JbaV0sdHlwZT0iYm94Iixib3gucG9zPWktMi41LGJveC5zcGFjZT0wLjUsYWRkPVRSVUUpDQp9DQoNCiMgdGhpcmQgZXhhbXBsZTogZmV3ZXIgcGFydHMsIHBhcnRpdGlvbiBkZWZpbmVkIGJ5IGEgc2lnbmFyeSwgYW5kIGVtcHR5IGJveC1wbG90cw0KeCA9IGFjb21wKEh5ZHJvY2hlbVssYygiTmEiLCJLIiwiTWciLCJDYSIsIlNyIiwiQmEiLCJOSDQiKV0pDQpzaWduYXJ5ID0gdChtYXRyaXgoICBjKDEsICAgMSwgICAxLCAgMSwgICAxLCAgIDEsICAtMSwNCiAgICAgICAgICAgICAgICAgICAgICAgMSwgICAxLCAgLTEsIC0xLCAgLTEsICAtMSwgICAwLA0KICAgICAgICAgICAgICAgICAgICAgICAxLCAgLTEsICAgMCwgIDAsICAgMCwgICAwLCAgIDAsDQogICAgICAgICAgICAgICAgICAgICAgIDAsICAgMCwgIC0xLCAgMSwgIC0xLCAgLTEsICAgMCwNCiAgICAgICAgICAgICAgICAgICAgICAgMCwgICAwLCAgIDEsICAwLCAgLTEsICAgMSwgICAwLA0KICAgICAgICAgICAgICAgICAgICAgICAwLCAgIDAsICAgMSwgIDAsICAgMCwgIC0xLCAgIDApLG5jb2w9Nyxucm93PTYsYnlyb3c9VFJVRSkpDQoNCkNvRGFEZW5kcm9ncmFtKFg9YWNvbXAoeCksc2lnbmFyeT1zaWduYXJ5LGNvbD0iYmxhY2siLHJhbmdlPWMoLTgsOCksdHlwZT0ibCIpDQp4c3BsaXQgPSBzcGxpdCh4LGdyKQ0KZm9yKGkgaW4gMTo0KXsNCiAgQ29EYURlbmRyb2dyYW0oWD1hY29tcCh4c3BsaXRbW2ldXSksYm9yZGVyPWNvbG9yW2ldLA0KICAgICAgIHR5cGU9ImJveCIsYm94LnBvcz1pLTIuNSxib3guc3BhY2U9MS41LGFkZD1UUlVFKQ0KICBDb0RhRGVuZHJvZ3JhbShYPWFjb21wKHhzcGxpdFtbaV1dKSxjb2w9Y29sb3JbaV0sDQogICAgICAgdHlwZT0ibGluZSIsYWRkPVRSVUUpDQp9DQoNCmBgYA0KDQo=