Exercice 1.1 : Création de vecteurs

  1. Les 3 vecteurs sont créés en utilisant la fonction rep avec les arguments each et times:
vec1 <- rep(1:5,3)
vec1
vec2 <- rep(1:5,each=3)
vec2
vec3 <- rep(1:4,times=(2:5))
vec3
  1. La fonction paste concatène les vecteurs de type character:
vec4 <- paste("A",0:10,")",sep="")
vec4
  1. La position de la lettre q est calculée et ensuite le vecteur de lettre et le vecteur d’indices sont concaténés:
pos.q <- which(letters=="q")
vec5 <- paste(letters[1:pos.q],1:pos.q,sep="") 
vec5

Exercice 1.2 : Travailler avec les valeurs manquantes NA

  1. Créons le vecteur vec1 et calculons sa moyenne et sa variance:
set.seed(007)
vec1 <- runif(100,0,7)
mean(vec1)
var(vec1) 
  1. Allouons les valeurs manquantes
set.seed(008)
vec2 <- vec1
ind <- sample(1:100,10)
vec2[ind] <- NA
indNA <- which(is.na(vec2))
  1. Afin de ne pas obtenir de valeur manquante il est nécessaire d’utiliser na.rm=TRUE:
mean(vec2)
mean(vec2,na.rm=TRUE)
var(vec2)
var(vec2,na.rm=TRUE)
  1. Nous supprimons les valeurs manquantes et nous retrouvons les mêmes valeurs que précédemment avec l’argument na.rm=TRUE:
vec3 <- vec2[-indNA]
mean(vec3)
var(vec3)
  1. Si nous remplaçons les valeurs manquantes par la moyenne, la moyenne reste la même mais la variance est sous-estimée:
vec4 <- vec2
vec4[indNA] <- mean(vec3)
mean(vec4)
var(vec4)
  1. Les valeurs manquantes sont remplacées par des valeurs issues de tirages aléatoires selon une loi normale de moyenne la moyenne empirique de vec3 et d’écart-type empirique de vec3:
vec5 <- vec2
vec5[indNA] <- rnorm(length(indNA),mean(vec3),sd(vec3))
mean(vec5)
var(vec5)
  1. Les valeurs manquantes sont remplacées par des valeurs issues de tirages aléatoires selon une loi uniforme entre le minimum et le maximum des valeurs observées:
vec6 <- vec2
vec6[indNA] <- runif(length(indNA),min(vec3),max(vec3))
mean(vec6)
var(vec6)
  1. Ici nous procédons à un tirage avec remise parmi les valeurs non-manquantes:
vec7 <- vec2
vec7[indNA] <- sample(vec3,10)
mean(vec7)
var(vec7) 

Exercice 1.3 : Création et inversion d’une matrice

  1. Créons la matrice mat} avant de lui affecter des noms de lignes et colonnes:
mat <- matrix(c(1,0,3,4,5,5,0,4,5,6,3,4,0,1,3,2),ncol=4)
rownames(mat) <- paste("ligne",1:4,sep="-")
colnames(mat) <- paste("colonne",1:4)
  1. Les éléments diagonaux sont obtenus avec:
vec <- diag(mat)
vec
  1. Les deux premières lignes de mat:
mat1 <- mat[c(1,2),]
mat1
  1. Les deux dernières colonnes de mat:
mat2 <- mat[,(ncol(mat)-1):ncol(mat)]
mat2
  1. Toutes les colonnes sauf la troisième:
mat3 <- mat[,-3]
mat3
  1. Pour calculer le déterminant, il suffit d’utiliser les fonctions det et solve:
det(mat)
solve(mat)

Exercice 1.4 : Sélection et tri dans un data-frame

  1. Chargeons le jeu de données iris puis créons un nouveau jeu de données en sélectionnant uniquement les individus qui prennent la valeur “versicolor” pour la cinquième variable:
data(iris)
iris2 <- iris[iris[,5]=="versicolor", ]
  1. On trie en fonction de la première variable à l’aide de la fonction order:
iris2[order(iris2[,1],decreasing=TRUE),]

Exercice 1.5 : Utilisation de la fonction apply

  1. Pour calculer les statistiques de base, il suffit d’utiliser la fonction summary:
library(lattice) # appel du package
data(ethanol)
summary(ethanol)
  1. Pour calculer les quantiles, nous pouvons utiliser la fonction apply:
apply(X=ethanol,MARGIN=2,FUN=quantile)
  1. L’instruction de la question précédente donne par défaut les quartiles. En effet, nous n’avons pas spécifié d’argument probs pour la fonction quantile, donc l’argument utilisé est celui défini par défaut: probs=seq(0,1,0.25) (voir l’aide de la fonction quantile). Pour obtenir les déciles, il faut donc spécifier comme argument probs=seq(0,1,by=0.1). L’aide de la fonction apply indique les arguments optionnels via …: optional arguments to ‘FUN’. Il est alors envisageable de passer comme argument probs=seq(0,1,by=0.1) à la fonction FUN=quantile:
apply(ethanol,2,quantile,probs=seq(0,1,by=0.1))

Exercice 1.6 : Sélection dans une matrice avec la fonction apply

  1. La matrice contenant les colonnes de mat qui possèdent uniquement des valeurs plus petites que 6 est obtenue par:
mat <- matrix(c(1,0,3,4,5,5,0,4,5,6,3,4,0,1,3,2),ncol=4)
mat3 <- mat[,apply((mat<6),2,all)] 
mat3
  1. Comme il n’y a qu’une seule ligne qui ne contient pas de 0, nous utilisons drop=FALSE afin que la sélection reste une matrice (et non pas un vecteur, ce qui est le comportement par défaut):
mat4 <- mat[apply((mat>0),1,all),,drop=FALSE]
mat4

Exercice 1.7 Utilisation de la fonction lapply

  1. Le package MASS et le jeu de données Aids2 sont chargés grâce à:
library(MASS)   # chargement du package
data(Aids2)
summary(Aids2)
  1. La fonction is.numeric retourne un booléen: TRUE quand l’objet sur lequel elle est appliquée est de type numeric. Nous allons donc appliquer cette fonction à chaque colonne du data-frame Aids2 et ensuite prendre la négation (opérateur !). Comme un data-frame est une liste où chaque composante est une colonne (en général), appliquer la fonction à chaque colonne est (habituellement) équivalent à appliquer la fonction à chaque composante de la liste; c’est ce que fait la fonction lapply:
ind <- !unlist(lapply(Aids2,is.numeric))
  1. Nous avons juste à sélectionner les variables (i.e. les colonnes du data-frame) avec le vecteur de booléen ind:
Aids2.qual <- Aids2[,ind]
  1. Nous utilisons la fonction levels sur chaque composante (colonne) du data-frame Aids2.qual:
lapply(Aids2.qual,levels)

Exercice 1.8 Modalités des variables qualitatives et sélection

1.Le package MASS et le jeu de données Aids2 sont chargés grâce à:

library(MASS)   # chargement du package
data(Aids2)
  1. Les sélections sont obtenues par:
res <- Aids2[(Aids2[,"sex"]=="M")&(Aids2[,"state"]!="Other"),]

Une autre méthode serait d’utiliser la fonction subset.

  1. Le résumé indique que les modalités sont toujours les mêmes, M et F, mais qu’aucun individu n’est dans la catégorie F:
summary(res)
  1. Les attributs de la variable sex sont:
attributes(res[,"sex"])
  1. Transformons la variable sex en vecteur de caractère et imprimons les attributs du résultat:
sexc <- as.character(res[,"sex"])
attributes(sexc)
  1. Transformons le vecteur de caractères sexc en vecteur de type factor:
sexf <- as.factor(sexc)
attributes(sexf)
  1. Trouvons les indices des variables non quantitatives:
ind <- !unlist(lapply(res,is.numeric))
ind
  1. Transformons ces variables en vecteurs de caractères:
res[,ind] <- lapply(res[,ind],as.character)
  1. Re-transformons ces variables en vecteur de type factor:
res[,ind] <- lapply(res[,ind],as.factor)
summary(res)
LS0tDQp0aXRsZTogIkNvcnJlY3Rpb24gZGVzIGV4ZXJjaWNlcyBkdSBjaGFwaXRyZSAxIg0KYXV0aG9yOiAiSHVzc29uIGV0IGFsLiINCmRhdGU6ICIwOS8wOS8yMDE4Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogJzMnDQogICAgdG9jX2Zsb2F0OiB5ZXMNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgY2FjaGUgPSBUUlVFKQ0KYGBgDQoNCg0KIyMgRXhlcmNpY2UgMS4xIDogQ3LDqWF0aW9uIGRlIHZlY3RldXJzDQoNCjEuIExlcyAzIHZlY3RldXJzIHNvbnQgY3LDqcOpcyBlbiB1dGlsaXNhbnQgbGEgZm9uY3Rpb24gKnJlcCogYXZlYyBsZXMgYXJndW1lbnRzICplYWNoKiBldCAqdGltZXMqOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmVjMSA8LSByZXAoMTo1LDMpDQp2ZWMxDQp2ZWMyIDwtIHJlcCgxOjUsZWFjaD0zKQ0KdmVjMg0KdmVjMyA8LSByZXAoMTo0LHRpbWVzPSgyOjUpKQ0KdmVjMw0KYGBgDQoNCjIuIExhIGZvbmN0aW9uICpwYXN0ZSogY29uY2F0w6huZSBsZXMgdmVjdGV1cnMgZGUgdHlwZSAqY2hhcmFjdGVyKjoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmVjNCA8LSBwYXN0ZSgiQSIsMDoxMCwiKSIsc2VwPSIiKQ0KdmVjNA0KYGBgDQoNCjMuIExhIHBvc2l0aW9uIGRlIGxhIGxldHRyZSBxIGVzdCBjYWxjdWzDqWUgZXQgZW5zdWl0ZSBsZSB2ZWN0ZXVyIGRlIGxldHRyZSBldCBsZSB2ZWN0ZXVyIGQnaW5kaWNlcyBzb250IGNvbmNhdMOpbsOpczoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcG9zLnEgPC0gd2hpY2gobGV0dGVycz09InEiKQ0KdmVjNSA8LSBwYXN0ZShsZXR0ZXJzWzE6cG9zLnFdLDE6cG9zLnEsc2VwPSIiKSANCnZlYzUNCmBgYA0KDQojIyBFeGVyY2ljZSAxLjIgOiBUcmF2YWlsbGVyIGF2ZWMgbGVzIHZhbGV1cnMgbWFucXVhbnRlcyAqTkEqDQoNCjEuIENyw6lvbnMgbGUgdmVjdGV1ciAqdmVjMSogZXQgY2FsY3Vsb25zIHNhIG1veWVubmUgZXQgc2EgdmFyaWFuY2U6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDAwNykNCnZlYzEgPC0gcnVuaWYoMTAwLDAsNykNCm1lYW4odmVjMSkNCnZhcih2ZWMxKSANCmBgYA0KDQoyLiBBbGxvdW9ucyBsZXMgdmFsZXVycyBtYW5xdWFudGVzDQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnNldC5zZWVkKDAwOCkNCnZlYzIgPC0gdmVjMQ0KaW5kIDwtIHNhbXBsZSgxOjEwMCwxMCkNCnZlYzJbaW5kXSA8LSBOQQ0KaW5kTkEgPC0gd2hpY2goaXMubmEodmVjMikpDQpgYGANCg0KMy4gQWZpbiBkZSBuZSBwYXMgb2J0ZW5pciBkZSB2YWxldXIgbWFucXVhbnRlIGlsIGVzdCBuw6ljZXNzYWlyZSBkJ3V0aWxpc2VyICpuYS5ybT1UUlVFKjoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbWVhbih2ZWMyKQ0KbWVhbih2ZWMyLG5hLnJtPVRSVUUpDQp2YXIodmVjMikNCnZhcih2ZWMyLG5hLnJtPVRSVUUpDQpgYGANCjQuIE5vdXMgc3VwcHJpbW9ucyBsZXMgdmFsZXVycyBtYW5xdWFudGVzIGV0IG5vdXMgcmV0cm91dm9ucyBsZXMgbcOqbWVzIHZhbGV1cnMgcXVlIHByw6ljw6lkZW1tZW50IGF2ZWMgbCdhcmd1bWVudCAqbmEucm09VFJVRSo6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnZlYzMgPC0gdmVjMlstaW5kTkFdDQptZWFuKHZlYzMpDQp2YXIodmVjMykNCmBgYA0KNS4gU2kgbm91cyByZW1wbGHDp29ucyBsZXMgdmFsZXVycyBtYW5xdWFudGVzIHBhciBsYSBtb3llbm5lLCBsYSBtb3llbm5lIHJlc3RlIGxhIG3Dqm1lIG1haXMgbGEgdmFyaWFuY2UgZXN0IHNvdXMtZXN0aW3DqWU6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnZlYzQgPC0gdmVjMg0KdmVjNFtpbmROQV0gPC0gbWVhbih2ZWMzKQ0KbWVhbih2ZWM0KQ0KdmFyKHZlYzQpDQpgYGANCg0KNi4gTGVzIHZhbGV1cnMgbWFucXVhbnRlcyBzb250IHJlbXBsYWPDqWVzIHBhciBkZXMgdmFsZXVycyBpc3N1ZXMgZGUgdGlyYWdlcyBhbMOpYXRvaXJlcyBzZWxvbiB1bmUgbG9pIG5vcm1hbGUgZGUgbW95ZW5uZSBsYSBtb3llbm5lIGVtcGlyaXF1ZSBkZSAqdmVjMyogZXQgZCfDqWNhcnQtdHlwZSBlbXBpcmlxdWUgZGUgKnZlYzMqOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmVjNSA8LSB2ZWMyDQp2ZWM1W2luZE5BXSA8LSBybm9ybShsZW5ndGgoaW5kTkEpLG1lYW4odmVjMyksc2QodmVjMykpDQptZWFuKHZlYzUpDQp2YXIodmVjNSkNCmBgYA0KNy4gTGVzIHZhbGV1cnMgbWFucXVhbnRlcyBzb250IHJlbXBsYWPDqWVzIHBhciBkZXMgdmFsZXVycyBpc3N1ZXMgZGUgdGlyYWdlcyBhbMOpYXRvaXJlcyBzZWxvbiB1bmUgbG9pIHVuaWZvcm1lIGVudHJlIGxlIG1pbmltdW0gZXQgbGUgbWF4aW11bSAgZGVzIHZhbGV1cnMgb2JzZXJ2w6llczoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnZlYzYgPC0gdmVjMg0KdmVjNltpbmROQV0gPC0gcnVuaWYobGVuZ3RoKGluZE5BKSxtaW4odmVjMyksbWF4KHZlYzMpKQ0KbWVhbih2ZWM2KQ0KdmFyKHZlYzYpDQpgYGANCjguIEljaSBub3VzIHByb2PDqWRvbnMgw6AgdW4gdGlyYWdlIGF2ZWMgcmVtaXNlIHBhcm1pIGxlcyB2YWxldXJzIG5vbi1tYW5xdWFudGVzOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KdmVjNyA8LSB2ZWMyDQp2ZWM3W2luZE5BXSA8LSBzYW1wbGUodmVjMywxMCkNCm1lYW4odmVjNykNCnZhcih2ZWM3KSANCmBgYA0KDQojIyBFeGVyY2ljZSAxLjMgOiBDcsOpYXRpb24gZXQgaW52ZXJzaW9uIGQndW5lIG1hdHJpY2UNCg0KMS4gQ3LDqW9ucyBsYSBtYXRyaWNlICptYXQqfSBhdmFudCBkZSBsdWkgYWZmZWN0ZXIgZGVzIG5vbXMgZGUgbGlnbmVzIGV0IGNvbG9ubmVzOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbWF0IDwtIG1hdHJpeChjKDEsMCwzLDQsNSw1LDAsNCw1LDYsMyw0LDAsMSwzLDIpLG5jb2w9NCkNCnJvd25hbWVzKG1hdCkgPC0gcGFzdGUoImxpZ25lIiwxOjQsc2VwPSItIikNCmNvbG5hbWVzKG1hdCkgPC0gcGFzdGUoImNvbG9ubmUiLDE6NCkNCmBgYA0KMi4gTGVzIMOpbMOpbWVudHMgZGlhZ29uYXV4IHNvbnQgb2J0ZW51cyBhdmVjOg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQp2ZWMgPC0gZGlhZyhtYXQpDQp2ZWMNCmBgYA0KDQozLiBMZXMgZGV1eCBwcmVtacOocmVzIGxpZ25lcyBkZSAqbWF0KjoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCm1hdDEgPC0gbWF0W2MoMSwyKSxdDQptYXQxDQpgYGANCg0KNC4gTGVzIGRldXggZGVybmnDqHJlcyBjb2xvbm5lcyBkZSAqbWF0KjoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRQ0KbWF0MiA8LSBtYXRbLChuY29sKG1hdCktMSk6bmNvbChtYXQpXQ0KbWF0Mg0KYGBgDQoNCjUuIFRvdXRlcyBsZXMgY29sb25uZXMgc2F1ZiBsYSB0cm9pc2nDqG1lOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbWF0MyA8LSBtYXRbLC0zXQ0KbWF0Mw0KYGBgDQo0LiBQb3VyIGNhbGN1bGVyIGxlIGTDqXRlcm1pbmFudCwgaWwgc3VmZml0IGQndXRpbGlzZXIgbGVzIGZvbmN0aW9ucyAqZGV0KiBldCAqc29sdmUqOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZGV0KG1hdCkNCnNvbHZlKG1hdCkNCmBgYA0KDQojIyBFeGVyY2ljZSAxLjQgOiBTw6lsZWN0aW9uIGV0IHRyaSBkYW5zIHVuIGRhdGEtZnJhbWUNCg0KMS4gQ2hhcmdlb25zIGxlIGpldSBkZSBkb25uw6llcyAqaXJpcyogcHVpcyBjcsOpb25zIHVuIG5vdXZlYXUgamV1IGRlIGRvbm7DqWVzIGVuIHPDqWxlY3Rpb25uYW50IHVuaXF1ZW1lbnQgbGVzIGluZGl2aWR1cyBxdWkgcHJlbm5lbnQNCmxhIHZhbGV1ciAqInZlcnNpY29sb3IiKiBwb3VyIGxhIGNpbnF1acOobWUgdmFyaWFibGU6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmRhdGEoaXJpcykNCmlyaXMyIDwtIGlyaXNbaXJpc1ssNV09PSJ2ZXJzaWNvbG9yIiwgXQ0KYGBgDQoNCjIuIE9uIHRyaWUgZW4gZm9uY3Rpb24gZGUgbGEgcHJlbWnDqHJlIHZhcmlhYmxlIMOgIGwnYWlkZSBkZSBsYSBmb25jdGlvbiAqb3JkZXIqOg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQppcmlzMltvcmRlcihpcmlzMlssMV0sZGVjcmVhc2luZz1UUlVFKSxdDQpgYGANCg0KDQojIyBFeGVyY2ljZSAxLjUgOiBVdGlsaXNhdGlvbiBkZSBsYSBmb25jdGlvbiAqYXBwbHkqDQoNCjEuIFBvdXIgY2FsY3VsZXIgbGVzIHN0YXRpc3RpcXVlcyBkZSBiYXNlLCBpbCBzdWZmaXQgZCd1dGlsaXNlciBsYSBmb25jdGlvbiAqc3VtbWFyeSo6DQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGxhdHRpY2UpICMgYXBwZWwgZHUgcGFja2FnZQ0KZGF0YShldGhhbm9sKQ0Kc3VtbWFyeShldGhhbm9sKQ0KYGBgDQoNCjIuIFBvdXIgY2FsY3VsZXIgbGVzIHF1YW50aWxlcywgbm91cyBwb3V2b25zIHV0aWxpc2VyIGxhIGZvbmN0aW9uICphcHBseSo6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmFwcGx5KFg9ZXRoYW5vbCxNQVJHSU49MixGVU49cXVhbnRpbGUpDQpgYGANCg0KMy4gTCdpbnN0cnVjdGlvbiBkZSBsYSBxdWVzdGlvbiBwcsOpY8OpZGVudGUgZG9ubmUgcGFyIGTDqWZhdXQgbGVzIHF1YXJ0aWxlcy4gRW4gZWZmZXQsIG5vdXMNCiBuJ2F2b25zIHBhcyBzcMOpY2lmacOpIGQnYXJndW1lbnQgKnByb2JzKiBwb3VyIGxhIGZvbmN0aW9uDQogKnF1YW50aWxlKiwgZG9uYyBsJ2FyZ3VtZW50IHV0aWxpc8OpIGVzdCBjZWx1aSBkw6lmaW5pIHBhcg0KIGTDqWZhdXQ6ICpwcm9icz1zZXEoMCwxLDAuMjUpKiAodm9pciBsJ2FpZGUgZGUgbGEgZm9uY3Rpb24NCiAqcXVhbnRpbGUqKS4gUG91ciBvYnRlbmlyIGxlcyBkw6ljaWxlcywgaWwgZmF1dCBkb25jIHNww6ljaWZpZXINCiBjb21tZSBhcmd1bWVudCAqcHJvYnM9c2VxKDAsMSxieT0wLjEpKi4gTCdhaWRlIGRlIGxhDQogZm9uY3Rpb24gKmFwcGx5KiBpbmRpcXVlIGxlcyBhcmd1bWVudHMgb3B0aW9ubmVscyB2aWENCiAqLi4uOiBvcHRpb25hbCBhcmd1bWVudHMgdG8gJ0ZVTicqLiBJbCBlc3QgYWxvcnMgZW52aXNhZ2VhYmxlIGRlIA0KIHBhc3NlciBjb21tZSBhcmd1bWVudCAqcHJvYnM9c2VxKDAsMSxieT0wLjEpKiDDoCBsYQ0KIGZvbmN0aW9uICpGVU49cXVhbnRpbGUqOg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQphcHBseShldGhhbm9sLDIscXVhbnRpbGUscHJvYnM9c2VxKDAsMSxieT0wLjEpKQ0KYGBgDQoNCiMjIEV4ZXJjaWNlIDEuNiA6IFPDqWxlY3Rpb24gZGFucyB1bmUgbWF0cmljZSBhdmVjIGxhIGZvbmN0aW9uICphcHBseSoNCg0KMS4gTGEgbWF0cmljZSBjb250ZW5hbnQgbGVzIGNvbG9ubmVzIGRlICptYXQqIHF1aSBwb3Nzw6hkZW50IHVuaXF1ZW1lbnQgZGVzIHZhbGV1cnMgcGx1cyBwZXRpdGVzIHF1ZSA2IGVzdCBvYnRlbnVlIHBhcjoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbWF0IDwtIG1hdHJpeChjKDEsMCwzLDQsNSw1LDAsNCw1LDYsMyw0LDAsMSwzLDIpLG5jb2w9NCkNCm1hdDMgPC0gbWF0WyxhcHBseSgobWF0PDYpLDIsYWxsKV0gDQptYXQzDQpgYGANCg0KMi4gQ29tbWUgaWwgbid5IGEgcXUndW5lIHNldWxlIGxpZ25lIHF1aSBuZSBjb250aWVudCBwYXMgZGUgMCwgbm91cyB1dGlsaXNvbnMgKmRyb3A9RkFMU0UqIGFmaW4gcXVlIGxhIHPDqWxlY3Rpb24gcmVzdGUgdW5lIG1hdHJpY2UgKGV0IG5vbiBwYXMgdW4gdmVjdGV1ciwgY2UgcXVpIGVzdCBsZSBjb21wb3J0ZW1lbnQgcGFyIGTDqWZhdXQpOg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQptYXQ0IDwtIG1hdFthcHBseSgobWF0PjApLDEsYWxsKSwsZHJvcD1GQUxTRV0NCm1hdDQNCmBgYA0KDQojIyBFeGVyY2ljZSAxLjcgVXRpbGlzYXRpb24gZGUgIGxhIGZvbmN0aW9uICpsYXBwbHkqDQoNCjEuIExlIHBhY2thZ2UgKk1BU1MqIGV0IGxlIGpldSBkZSBkb25uw6llcyAqQWlkczIqIHNvbnQgY2hhcmfDqXMgZ3LDomNlIMOgOg0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KE1BU1MpICAgIyBjaGFyZ2VtZW50IGR1IHBhY2thZ2UNCmRhdGEoQWlkczIpDQpzdW1tYXJ5KEFpZHMyKQ0KYGBgDQoNCjIuIExhIGZvbmN0aW9uICppcy5udW1lcmljKiByZXRvdXJuZSB1biBib29sw6llbjoNCiAgKlRSVUUqIHF1YW5kIGwnb2JqZXQgc3VyIGxlcXVlbCBlbGxlIGVzdCBhcHBsaXF1w6llIGVzdCBkZSB0eXBlICpudW1lcmljLiogTm91cyBhbGxvbnMgZG9uYyBhcHBsaXF1ZXIgY2V0dGUgZm9uY3Rpb24gw6AgY2hhcXVlIGNvbG9ubmUgZHUgZGF0YS1mcmFtZQ0KKkFpZHMyKiBldCBlbnN1aXRlIHByZW5kcmUgbGEgbsOpZ2F0aW9uIChvcMOpcmF0ZXVyICohKikuIENvbW1lIHVuIGRhdGEtZnJhbWUgZXN0IHVuZSBsaXN0ZSBvw7kgY2hhcXVlIGNvbXBvc2FudGUgZXN0IHVuZSBjb2xvbm5lIChlbiBnw6luw6lyYWwpLCBhcHBsaXF1ZXIgbGEgZm9uY3Rpb24gw6AgY2hhcXVlIGNvbG9ubmUgZXN0IChoYWJpdHVlbGxlbWVudCkgw6lxdWl2YWxlbnQgw6AgYXBwbGlxdWVyIGxhIGZvbmN0aW9uIMOgIGNoYXF1ZSBjb21wb3NhbnRlIGRlIGxhIGxpc3RlOyBjJ2VzdCBjZSBxdWUgZmFpdCBsYSBmb25jdGlvbiAqbGFwcGx5KjoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmluZCA8LSAhdW5saXN0KGxhcHBseShBaWRzMixpcy5udW1lcmljKSkNCmBgYA0KMy4gTm91cyBhdm9ucyBqdXN0ZSDDoCBzw6lsZWN0aW9ubmVyIGxlcyB2YXJpYWJsZXMgKGkuZS4gbGVzIGNvbG9ubmVzDQogIGR1IGRhdGEtZnJhbWUpIGF2ZWMgbGUgdmVjdGV1ciBkZSBib29sw6llbiAqaW5kKjoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCkFpZHMyLnF1YWwgPC0gQWlkczJbLGluZF0NCmBgYA0KNC4gTm91cyB1dGlsaXNvbnMgbGEgZm9uY3Rpb24gKmxldmVscyogc3VyIGNoYXF1ZSBjb21wb3NhbnRlIChjb2xvbm5lKSBkdSBkYXRhLWZyYW1lICpBaWRzMi5xdWFsKjoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRQ0KbGFwcGx5KEFpZHMyLnF1YWwsbGV2ZWxzKQ0KYGBgDQoNCiMjIEV4ZXJjaWNlIDEuOCBNb2RhbGl0w6lzIGRlcyB2YXJpYWJsZXMgcXVhbGl0YXRpdmVzIGV0IHPDqWxlY3Rpb24NCg0KMS5MZSBwYWNrYWdlICpNQVNTKiBldCBsZSBqZXUgZGUgZG9ubsOpZXMgKkFpZHMyKiBzb250IGNoYXJnw6lzIGdyw6JjZSDDoDoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoTUFTUykgICAjIGNoYXJnZW1lbnQgZHUgcGFja2FnZQ0KZGF0YShBaWRzMikNCmBgYA0KMi4gTGVzIHPDqWxlY3Rpb25zIHNvbnQgb2J0ZW51ZXMgcGFyOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcmVzIDwtIEFpZHMyWyhBaWRzMlssInNleCJdPT0iTSIpJihBaWRzMlssInN0YXRlIl0hPSJPdGhlciIpLF0NCmBgYA0KVW5lIGF1dHJlIG3DqXRob2RlIHNlcmFpdCBkJ3V0aWxpc2VyIGxhIGZvbmN0aW9uICpzdWJzZXQqLg0KDQozLiBMZSByw6lzdW3DqSBpbmRpcXVlIHF1ZSBsZXMgbW9kYWxpdMOpcyBzb250IHRvdWpvdXJzIGxlcyBtw6ptZXMsDQogICpNKiBldCAqRiosIG1haXMgcXUnYXVjdW4gaW5kaXZpZHUgbidlc3QgZGFucyBsYSBjYXTDqWdvcmllICpGKjoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnN1bW1hcnkocmVzKQ0KYGBgDQo0LiBMZXMgYXR0cmlidXRzIGRlIGxhIHZhcmlhYmxlICpzZXgqIHNvbnQ6DQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0UNCmF0dHJpYnV0ZXMocmVzWywic2V4Il0pDQpgYGANCjUuIFRyYW5zZm9ybW9ucyBsYSB2YXJpYWJsZSAqc2V4KiBlbiB2ZWN0ZXVyIGRlIGNhcmFjdMOocmUgZXQgaW1wcmltb25zIGxlcyBhdHRyaWJ1dHMgZHUgcsOpc3VsdGF0Og0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0Kc2V4YyA8LSBhcy5jaGFyYWN0ZXIocmVzWywic2V4Il0pDQphdHRyaWJ1dGVzKHNleGMpDQpgYGANCjYuIFRyYW5zZm9ybW9ucyBsZSB2ZWN0ZXVyIGRlIGNhcmFjdMOocmVzICpzZXhjKiBlbiB2ZWN0ZXVyIGRlIHR5cGUgKmZhY3Rvcio6DQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzZXhmIDwtIGFzLmZhY3RvcihzZXhjKQ0KYXR0cmlidXRlcyhzZXhmKQ0KYGBgDQo3LiBUcm91dm9ucyBsZXMgaW5kaWNlcyBkZXMgdmFyaWFibGVzIG5vbiBxdWFudGl0YXRpdmVzOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KaW5kIDwtICF1bmxpc3QobGFwcGx5KHJlcyxpcy5udW1lcmljKSkNCmluZA0KYGBgDQo4LiBUcmFuc2Zvcm1vbnMgY2VzIHZhcmlhYmxlcyBlbiB2ZWN0ZXVycyBkZSBjYXJhY3TDqHJlczoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnJlc1ssaW5kXSA8LSBsYXBwbHkocmVzWyxpbmRdLGFzLmNoYXJhY3RlcikNCmBgYA0KOS4gUmUtdHJhbnNmb3Jtb25zIGNlcyB2YXJpYWJsZXMgZW4gdmVjdGV1ciBkZSB0eXBlICpmYWN0b3IqOg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFDQpyZXNbLGluZF0gPC0gbGFwcGx5KHJlc1ssaW5kXSxhcy5mYWN0b3IpDQpzdW1tYXJ5KHJlcykNCmBgYA0K