4.3 La famille apply, des fonctions d’itération prédéfinies
set.seed(1234)
X <- matrix(sample(1:20,20),ncol=4)
X
[,1] [,2] [,3] [,4]
[1,] 3 10 7 9
[2,] 12 1 5 17
[3,] 11 4 20 16
[4,] 18 8 15 19
[5,] 14 6 2 13
apply(X,MARGIN=2,FUN=mean)
[1] 11.6 5.8 9.8 14.8
X[1,1] <- NA
apply(X,MARGIN=2,FUN=mean)
[1] NA 5.8 9.8 14.8
apply(X,MARGIN=2,FUN=mean,na.rm=TRUE)
[1] 13.75 5.80 9.80 14.80
colMeans(X,na.rm=TRUE)
[1] 13.75 5.80 9.80 14.80
set.seed(1234)
Y <- array(sample(24),dim=c(3,4,2))
Y
, , 1
[,1] [,2] [,3] [,4]
[1,] 3 22 1 8
[2,] 15 18 4 10
[3,] 14 13 11 23
, , 2
[,1] [,2] [,3] [,4]
[1,] 17 19 7 20
[2,] 16 21 6 9
[3,] 24 2 5 12
apply(Y,MARGIN=c(1,2),FUN=sum,na.rm=TRUE)
[,1] [,2] [,3] [,4]
[1,] 20 41 8 28
[2,] 31 39 10 19
[3,] 38 15 16 35
MaFonction <- function(x,y) {
z <- x**2 - y
return(z)
}
set.seed(1234)
X <- matrix(sample(12),ncol=4)
X
[,1] [,2] [,3] [,4]
[1,] 2 6 1 8
[2,] 7 10 12 4
[3,] 11 5 3 9
apply(X,MARGIN=c(1,2),FUN=MaFonction, y=2)
[,1] [,2] [,3] [,4]
[1,] 2 34 -1 62
[2,] 47 98 142 14
[3,] 119 23 7 79
Z <- 1:5
Z
[1] 1 2 3 4 5
vec1 <- c(rep("A1",2),rep("A2",2),rep("A3",1))
vec1
[1] "A1" "A1" "A2" "A2" "A3"
vec2 <- c(rep("B1",3),rep("B2",2))
vec2
[1] "B1" "B1" "B1" "B2" "B2"
tapply(Z,vec1,sum)
A1 A2 A3
3 7 5
tapply(Z,list(vec1,vec2),sum)
B1 B2
A1 3 NA
A2 3 4
A3 NA 5
set.seed(545)
mat1 <- matrix(sample(12),ncol=4)
mat1
[,1] [,2] [,3] [,4]
[1,] 9 4 1 11
[2,] 10 2 3 6
[3,] 7 5 8 12
mat2 <- matrix(sample(4),ncol=2)
mat2
[,1] [,2]
[1,] 4 3
[2,] 2 1
liste <- list(matrice1=mat1,matrice2=mat2)
lapply(liste,mean)
$`matrice1`
[1] 6.5
$matrice2
[1] 2.5
lapply(liste,apply,2,sum,na.rm=T)
$`matrice1`
[1] 26 11 12 29
$matrice2
[1] 6 4
Z <- 1:5
T <- 5:1
vec1 <- c(rep("A1",2),rep("A2",2),rep("A3",1))
vec2 <- c(rep("B1",3),rep("B2",2))
df <- data.frame(Z,T,vec1,vec2)
df
aggregate(df[,1:2],list(FacteurA=vec1),sum)
aggregate(df[,1:2],list(FacteurA=vec1,FacteurB=vec2),sum)
set.seed(1234)
X <- matrix(sample(12),nrow=3)
X
[,1] [,2] [,3] [,4]
[1,] 2 6 1 8
[2,] 7 10 12 4
[3,] 11 5 3 9
mean.X <- apply(X,2,mean)
mean.X
[1] 6.666667 7.000000 5.333333 7.000000
sd.X <- apply(X,2,sd)
sd.X
[1] 4.509250 2.645751 5.859465 2.645751
Xc <- sweep(X,2,mean.X,FUN="-")
Xc
[,1] [,2] [,3] [,4]
[1,] -4.6666667 -1 -4.333333 1
[2,] 0.3333333 3 6.666667 -3
[3,] 4.3333333 -2 -2.333333 2
Xcr <- sweep(Xc,2,sd.X,FUN="/")
Xcr
[,1] [,2] [,3] [,4]
[1,] -1.03490978 -0.3779645 -0.7395442 0.3779645
[2,] 0.07392213 1.1338934 1.1377602 -1.1338934
[3,] 0.96098765 -0.7559289 -0.3982161 0.7559289
set.seed(1234)
T <- rnorm(50)
Z <- rnorm(50)+3*T+5
vec1 <- c(rep("A1",20),rep("A2",30))
don <- data.frame(Z,T)
by(don,list(FacteurA=vec1),summary)
FacteurA: A1
Z T
Min. :-3.052 Min. :-2.3457
1st Qu.: 2.624 1st Qu.:-0.8504
Median : 4.539 Median :-0.5288
Mean : 4.417 Mean :-0.2507
3rd Qu.: 6.205 3rd Qu.: 0.3154
Max. :12.584 Max. : 2.4158
----------------------------------------------------------------------------------
FacteurA: A2
Z T
Min. :-0.9901 Min. :-2.1800
1st Qu.: 1.6098 1st Qu.:-1.0574
Median : 3.1922 Median :-0.6088
Mean : 3.3560 Mean :-0.5880
3rd Qu.: 5.1170 3rd Qu.:-0.3309
Max. : 9.2953 Max. : 1.4495
by(don,list(FacteurA=vec1),sum)
FacteurA: A1
[1] 83.32415
----------------------------------------------------------------------------------
FacteurA: A2
[1] 83.04172
mafonction <- function(x){
summary(lm(Z~T, data=x))$coef
}
by(don, vec1, mafonction)
vec1: A1
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.166501 0.2900179 17.81442 7.030010e-13
T 2.990575 0.2844888 10.51210 4.118736e-09
----------------------------------------------------------------------------------
vec1: A2
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.024887 0.2123503 23.66320 4.718878e-20
T 2.838283 0.2203563 12.88043 2.753530e-13
set.seed(1234)
replicate(n=8, mean(rnorm(100)))
[1] -0.1567617424 0.0412431799 0.1546036721 -0.0081051362 -0.0217858703 -0.1368770057 -0.0878617963
[8] -0.0008371926
Mois <- c("Jan","Fév","Mar")
Année <- 2008:2010
outer(Mois,Année,FUN="paste")
[,1] [,2] [,3]
[1,] "Jan 2008" "Jan 2009" "Jan 2010"
[2,] "Fév 2008" "Fév 2009" "Fév 2010"
[3,] "Mar 2008" "Mar 2009" "Mar 2010"
outer(Mois,Année,FUN="paste",sep="-")
[,1] [,2] [,3]
[1,] "Jan-2008" "Jan-2009" "Jan-2010"
[2,] "Fév-2008" "Fév-2009" "Fév-2010"
[3,] "Mar-2008" "Mar-2009" "Mar-2010"
LS0tDQp0aXRsZTogIkNoYXBpdHJlIDQgOiBwcm9ncmFtbWVyIg0KYXV0aG9yOiAiSHVzc29uIGV0IGFsLiINCmRhdGU6ICIwOS8wOS8yMDE4Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogJzMnDQogICAgdG9jX2Zsb2F0OiB5ZXMNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgY2FjaGUgPSBUUlVFKQ0KYGBgDQoNCiMgNC4xLjIgTGVzIGJvdWNsZXMgKGZvciBvdSB3aGlsZSkNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLHJlc3VsdHM9J2hpZGUnfQ0KZm9yIChpIGluIDE6OTkpIHByaW50KGkpDQpmb3IgKGkgaW4gc2VxKDEsOTksYnk9MikpIHByaW50KGkpDQpgYGANCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmVjdGV1ciA8LSBjKCJsdW5kaSIsIm1hcmRpIiwibWVyY3JlZGkiKQ0KZm9yIChpIGluIHZlY3RldXIpIHByaW50KGkpDQpgYGANCg0KYGBge3J9DQppIDwtIDENCndoaWxlIChpPDMpIHsNCiAgcHJpbnQoaSkNCiAgaSA8LSBpKzEgfQ0KYGBgDQoNCiMgNC4xLjMgTGVzIGNvbmRpdGlvbnMgKGlmLCBlbHNlKQ0KDQpgYGB7cn0NCmkgPC0gMQ0KcmVwZWF0IHsNCiAgcHJpbnQoaSkNCiAgaSA8LSBpKzENCiAgaWYgKGk+MykgYnJlYWsgfQ0KYGBgDQoNCmBgYHtyfQ0KWCA8LSBtYXRyaXgoMCw1LDUpDQpzd2l0Y2goY2xhc3MoWCksDQogICJtYXRyaXgiID0gcHJpbnQoIlggZXN0IHVuZSBtYXRyaWNlIiksDQogICJkYXRhLmZyYW1lIiA9IHByaW50KCJYIGVzdCB1biBkYXRhLmZyYW1lIiksDQogICJudW1lcmljIiA9IHByaW50KCJYIGVzdCBkZSBjbGFzc2UgbnVtw6lyaXF1ZSIpDQopDQpgYGANCg0KIyA0LjIgQ29uc3RydWlyZSB1bmUgZm9uY3Rpb24NCg0KYGBge3J9DQpzb20gPC0gZnVuY3Rpb24obikgew0KICByZXN1bHRhdCA8LSBzdW0oMTpuKQ0KICAgIHJldHVybihyZXN1bHRhdCkNCn0NCnNvbSgzKQ0KcmVzIDwtIHNvbSgzKQ0KcmVzDQpgYGANCmBgYHtyfQ0Kc29tIDwtIGZ1bmN0aW9uKG4pIHsNCiAgaWYgKG48PTApIHN0b3AoImzigJllbnRpZXIgZG9pdCDDqnRyZSBzdHJpY3RlbWVudCBwb3NpdGlmIikNCiAgaWYgKGNlaWxpbmcobikhPW4pIHdhcm5pbmcocGFzdGUoImFycm9uZGkgZGUiLG4sImVuIixjZWlsaW5nKG4pKSkNCiAgcmVzdWx0YXQgPC0gc3VtKDE6Y2VpbGluZyhuKSkNCiAgcmV0dXJuKHJlc3VsdGF0KQ0KfQ0Kc29tKDQuMzI1KQ0KYGBgDQpgYGB7cn0NCm1hZm9uYyA8LSBmdW5jdGlvbihmYWN0ZXVyMSxmYWN0ZXVyMikgew0KICByZXMxIDwtIHRhYmxlKGZhY3RldXIxLGZhY3RldXIyKQ0KICBzZWxlY3Rpb24gPC0gd2hpY2gocmVzMT09MCxhcnIuaW5kID0gVFJVRSkNCiAgcmVzMiA8LSBtYXRyaXgoIiIsbnJvdz1ucm93KHNlbGVjdGlvbiksbmNvbD0yKQ0KICByZXMyWywxXSA8LSBsZXZlbHMoZmFjdGV1cjEpW3NlbGVjdGlvblssMV1dDQogIHJlczJbLDJdIDwtIGxldmVscyhmYWN0ZXVyMilbc2VsZWN0aW9uWywyXV0NCiAgcmV0dXJuKGxpc3QodGFiPXJlczEsbml2ZWF1PXJlczIpKQ0KfQ0KYGBgDQpgYGB7cn0NCnRlbnNpb24gPC0gZmFjdG9yKGMocmVwKCJGYWlibGUiLDUpLHJlcCgiRm9ydGUiLDUpKSkNCmxhaW5lIDwtIGZhY3RvcihjKHJlcCgiTWVyIiwzKSxyZXAoIkFuZyIsMykscmVwKCJUZXgiLDQpKSkNCm1hZm9uYyh0ZW5zaW9uLGxhaW5lKQ0KYGBgDQoNCiMgNC4zIExhIGZhbWlsbGUgYXBwbHksIGRlcyBmb25jdGlvbnMgZOKAmWl0w6lyYXRpb24gcHLDqWTDqWZpbmllcw0KIA0KYGBge3J9DQpzZXQuc2VlZCgxMjM0KQ0KWCA8LSBtYXRyaXgoc2FtcGxlKDE6MjAsMjApLG5jb2w9NCkNClgNCmFwcGx5KFgsTUFSR0lOPTIsRlVOPW1lYW4pDQpgYGANCmBgYHtyfQ0KWFsxLDFdIDwtIE5BDQphcHBseShYLE1BUkdJTj0yLEZVTj1tZWFuKQ0KYXBwbHkoWCxNQVJHSU49MixGVU49bWVhbixuYS5ybT1UUlVFKQ0KY29sTWVhbnMoWCxuYS5ybT1UUlVFKQ0KYGBgDQpgYGB7cn0NCnNldC5zZWVkKDEyMzQpDQpZIDwtIGFycmF5KHNhbXBsZSgyNCksZGltPWMoMyw0LDIpKQ0KWQ0KYXBwbHkoWSxNQVJHSU49YygxLDIpLEZVTj1zdW0sbmEucm09VFJVRSkNCmBgYA0KYGBge3J9DQpNYUZvbmN0aW9uIDwtIGZ1bmN0aW9uKHgseSkgew0KICB6IDwtIHgqKjIgLSB5DQogIHJldHVybih6KQ0KfQ0Kc2V0LnNlZWQoMTIzNCkNClggPC0gbWF0cml4KHNhbXBsZSgxMiksbmNvbD00KQ0KWA0KYXBwbHkoWCxNQVJHSU49YygxLDIpLEZVTj1NYUZvbmN0aW9uLCB5PTIpDQpgYGANCmBgYHtyfQ0KWiA8LSAxOjUNCloNCnZlYzEgPC0gYyhyZXAoIkExIiwyKSxyZXAoIkEyIiwyKSxyZXAoIkEzIiwxKSkNCnZlYzENCnZlYzIgPC0gYyhyZXAoIkIxIiwzKSxyZXAoIkIyIiwyKSkNCnZlYzINCnRhcHBseShaLHZlYzEsc3VtKQ0KdGFwcGx5KFosbGlzdCh2ZWMxLHZlYzIpLHN1bSkNCmBgYA0KYGBge3J9DQpzZXQuc2VlZCg1NDUpDQptYXQxIDwtIG1hdHJpeChzYW1wbGUoMTIpLG5jb2w9NCkNCm1hdDENCm1hdDIgPC0gbWF0cml4KHNhbXBsZSg0KSxuY29sPTIpDQptYXQyDQpsaXN0ZSA8LSBsaXN0KG1hdHJpY2UxPW1hdDEsbWF0cmljZTI9bWF0MikNCmxhcHBseShsaXN0ZSxtZWFuKQ0KbGFwcGx5KGxpc3RlLGFwcGx5LDIsc3VtLG5hLnJtPVQpDQpgYGANCmBgYHtyfQ0KWiA8LSAxOjUNClQgPC0gNToxDQp2ZWMxIDwtIGMocmVwKCJBMSIsMikscmVwKCJBMiIsMikscmVwKCJBMyIsMSkpDQp2ZWMyIDwtIGMocmVwKCJCMSIsMykscmVwKCJCMiIsMikpDQpkZiA8LSBkYXRhLmZyYW1lKFosVCx2ZWMxLHZlYzIpDQpkZg0KYWdncmVnYXRlKGRmWywxOjJdLGxpc3QoRmFjdGV1ckE9dmVjMSksc3VtKQ0KYWdncmVnYXRlKGRmWywxOjJdLGxpc3QoRmFjdGV1ckE9dmVjMSxGYWN0ZXVyQj12ZWMyKSxzdW0pDQpgYGANCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzNCkNClggPC0gbWF0cml4KHNhbXBsZSgxMiksbnJvdz0zKQ0KWA0KbWVhbi5YIDwtIGFwcGx5KFgsMixtZWFuKQ0KbWVhbi5YDQpzZC5YIDwtIGFwcGx5KFgsMixzZCkNCnNkLlgNClhjIDwtIHN3ZWVwKFgsMixtZWFuLlgsRlVOPSItIikNClhjDQpYY3IgPC0gc3dlZXAoWGMsMixzZC5YLEZVTj0iLyIpDQpYY3INCmBgYA0KYGBge3J9DQpzZXQuc2VlZCgxMjM0KQ0KVCA8LSBybm9ybSg1MCkNClogPC0gcm5vcm0oNTApKzMqVCs1DQp2ZWMxIDwtIGMocmVwKCJBMSIsMjApLHJlcCgiQTIiLDMwKSkNCmRvbiA8LSBkYXRhLmZyYW1lKFosVCkNCmJ5KGRvbixsaXN0KEZhY3RldXJBPXZlYzEpLHN1bW1hcnkpDQpieShkb24sbGlzdChGYWN0ZXVyQT12ZWMxKSxzdW0pDQpgYGANCmBgYHtyfQ0KbWFmb25jdGlvbiA8LSBmdW5jdGlvbih4KXsNCiAgc3VtbWFyeShsbShaflQsIGRhdGE9eCkpJGNvZWYNCn0NCmJ5KGRvbiwgdmVjMSwgbWFmb25jdGlvbikNCnNldC5zZWVkKDEyMzQpDQpyZXBsaWNhdGUobj04LCBtZWFuKHJub3JtKDEwMCkpKQ0KTW9pcyA8LSBjKCJKYW4iLCJGw6l2IiwiTWFyIikNCkFubsOpZSA8LSAyMDA4OjIwMTANCm91dGVyKE1vaXMsQW5uw6llLEZVTj0icGFzdGUiKQ0Kb3V0ZXIoTW9pcyxBbm7DqWUsRlVOPSJwYXN0ZSIsc2VwPSItIikNCmBgYA0KDQojIDQuNC4yIExlIHBhY2thZ2UgcGFyYWxsZWwNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpyZXF1aXJlKHBhcmFsbGVsKQ0KdmlnbmV0dGUoInBhcmFsbGVsIikNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRX0NCnJlcXVpcmUocGFyYWxsZWwpDQpuYl9jb3JlcyA8LSBkZXRlY3RDb3JlcygpDQpuYl9jb3Jlcw0KY2wgPC0gbWFrZUNsdXN0ZXIobmJfY29yZXMgLSAxKQ0KcmVzIDwtIGNsdXN0ZXJDYWxsKGNsID0gY2wsIGZ1biA9IGZ1bmN0aW9uKCkgcmV0dXJuKDE6NCkpDQpzdG9wQ2x1c3RlcihjbCkNCnN0cihyZXMpDQpgYGANCg0KIyA0LjQuMyBMZSBwYWNrYWdlIGZvcmVhY2gNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9DQpyZXF1aXJlKGZvcmVhY2gpDQp2aWduZXR0ZSgiZm9yZWFjaCIpDQpgYGANCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcmVxdWlyZShmb3JlYWNoKQ0KeCA8LSBmb3JlYWNoKGkgPSAxOjMpICVkbyUgKHJvdW5kKHNxcnQoaSksMikpDQpzdHIoeCkNCmBgYA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnJlcXVpcmUoZm9yZWFjaCkNCnggPC0gZm9yZWFjaChpID0gMTozLCAuY29tYmluZSA9ICIrIikgJWRvJSBzcXJ0KGkpDQp4DQpyZXF1aXJlKG51bWJlcnMpDQpmb3JlYWNoKG4gPSAxOjUwLCAuY29tYmluZSA9ICJjIikgJTolIHdoZW4gKGlzUHJpbWUobikpICVkbyUgbg0KYGBgDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcmVxdWlyZShkb1BhcmFsbGVsKQ0KY2wgPC0gbWFrZUNsdXN0ZXIoMykNCnJlZ2lzdGVyRG9QYXJhbGxlbChjbCkgIyBlbnJlZ2lzdHJlbWVudCBkdSBjbHVzdGVyDQpyZXMgPC0gZm9yZWFjaChuID0gMTozKSAlZG9wYXIlIHJub3JtKDEwMDApDQpzdG9wQ2x1c3RlcihjbCkNCmBgYA0KDQojIDQuNC40IEV4ZW1wbGUgYXZhbmPDqQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnJlcXVpcmUoa2VybmxhYikNCmRhdGEoc3BhbSkNCnNldC5zZWVkKDEyNSkNCnNwYW0kZm9sZCA8LSBzYW1wbGUoMTo0LCBucm93KHNwYW0pLCByZXBsYWNlID0gVFJVRSkNCnRhYmxlKHNwYW0kdHlwZSwgc3BhbSRmb2xkKQ0KY3ZfcmYgPC0gZnVuY3Rpb24oZGF0YV9hcHAsIGRhdGFfdmFsKXsNCiAgcmYgPC0gcmFuZG9tRm9yZXN0KHR5cGUgfiAuLCBkYXRhPWRhdGFfYXBwKQ0KICB5X3ZhbCA8LSBkYXRhLmZyYW1lKHR5cGU9ZGF0YV92YWwkdHlwZSwgeT1wcmVkaWN0KHJmLCBuZXdkYXRhPWRhdGFfdmFsKSkNCiAgbGlzdChyZj1yZiwgeV92YWwsIGVycl9yYXRlPW1lYW4oeV92YWwkeSAhPSB5X3ZhbCR0eXBlKSkNCn0NCmBgYA0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpyZXF1aXJlKHBhcmFsbGVsKQ0KY2wgPC0gbWFrZUNsdXN0ZXIoMikNCmNsdXN0ZXJTZXRSTkdTdHJlYW0oY2wsaXNlZWQ9NzgpDQpjbHVzdGVyRXhwb3J0KGNsLCB2YXJsaXN0ID0gYygic3BhbSIsICJjdl9yZiIpKQ0KY2x1c3RlckV2YWxRKGNsLCB7cmVxdWlyZShyYW5kb21Gb3Jlc3QpfSkNCnJlcyA8LSBjbHVzdGVyQXBwbHkoY2wgPSBjbCwgeCA9IDE6NCwgZnVuID0gZnVuY3Rpb24oZm9sZCl7DQogIHNwYW1fYXBwIDwtIHNwYW1bc3BhbSRmb2xkICE9IGZvbGQsIF0gIyBjcmVhdGlvbiBhcHByZW50aXNzYWdlDQogIHNwYW1fdmFsIDwtIHNwYW1bc3BhbSRmb2xkID09IGZvbGQsIF0gIyBjcmVhdGlvbiB2YWxpZGF0aW9uDQogIHNwYW1fYXBwJGZvbGQgPC0gTlVMTCAjIHN1cHByZXNzaW9uIGZvbGQNCiAgY3ZfcmYoc3BhbV9hcHAsIHNwYW1fdmFsKSAjIGNhbGN1bHMNCn0pDQpzdG9wQ2x1c3RlcihjbCkNCnNhcHBseShyZXMsIGZ1bmN0aW9uKHgpIHgkZXJyX3JhdGUpDQpgYGANCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpyZXF1aXJlKGZvcmVhY2gpDQpyZXF1aXJlKGRvUGFyYWxsZWwpDQpjbCA8LSBtYWtlQ2x1c3RlcigyKQ0KY2x1c3RlclNldFJOR1N0cmVhbShjbCxpc2VlZD03OCkNCnJlZ2lzdGVyRG9QYXJhbGxlbChjbCkNCnJlcyA8LSBmb3JlYWNoKGZvbGQgPSAxOjQsIC5wYWNrYWdlcyA9ICJyYW5kb21Gb3Jlc3QiLA0KICAubm9leHBvcnQgPSBzZXRkaWZmKGxzKCksIGMoInNwYW0iLCAiY3ZfcmYiKSkpICVkb3BhciUgew0KICAgIHNwYW1fYXBwIDwtIHNwYW1bc3BhbSRmb2xkICE9IGZvbGQsIF0NCiAgICBzcGFtX3ZhbCA8LSBzcGFtW3NwYW0kZm9sZCA9PSBmb2xkLCBdDQogICAgc3BhbV9hcHAkZm9sZCA8LSBOVUxMDQogICAgY3ZfcmYoc3BhbV9hcHAsIHNwYW1fdmFsKQ0KICB9DQpzdG9wQ2x1c3RlcihjbCkNCmBgYA0KDQoNCg==