DATA MANAGEMENT
Delete rows whose variables are NA
df <- df[apply(df[,c("var1", "var2", "var3")], 1, function(y) !all(is.na(y))),]
# ou
df <- df[rowSums(is.na(df)) != ncol(df),]
Sum a variable by group
Using aggregate
aggregate(df$Var1, by = list(Var2 = df$Var2), FUN=sum)
Groupement selon les modalités de Var2 et somme de la Var1. multiple dimensions can be specified in the list. Multiple aggregated metrics of the same data type can be incorporated via cbind:
aggregate(cbind(x$Frequency, x$Metric2, x$Metric3) by = list(Var2 = df$Var2), FUN=sum)
Exportation de données
# Writes a df into a file
{utils} write.table(df, file, append, quote, sep, na, rownames, colnames, ...)
{utils} write.csv2(df, file, append, quote, sep, na, rownames, colnames, ...)
# Writes an ASCII text representation into an R object
{base} dput(df, file, control)
Création de données
rep(x,times)
rep(x,times) répète times fois la valeur x; utilisez each=n pour répéter n fois chaque élément de x
rep(c(1,2,3),2)
rep(c(1,2,3),each=2)
{base} seq(from, to, by, length) {base} rep(x, times, each)
df$var1 <- c(1:100) #{base}
{base} df$var2 <- rep(c("val1", NA_character_, "val2"), time = 3, each = 4, length.out = nrow(df))
Création de data frames
{base} data.frame(v=1:4, ch=c("a", "b", "c", "d"), lettre="A")
{base} as.data.frame(matrix(ncol = 10, nrow = 100)); names(df) <- c("var1", ...)
# From another data frame
df2 <- df1[, c("var1", "var2", "var3")]
Remplacement de valeurs dans df
df[which(df$var1 == "val1"), "var2"] <- "val2" # {base}
Indexer des vecteurs
vec[n]; vec[-n]; vec[1:n]; vec[-(1:n)]; vec[c(1,2,4)]; vec["val1"]; vec[vec>3]; vec[vec>3 & vec<5]
{base} vec1 %in% vec2
Manipulation Dates et Durées
Reshaping data ‘tidyr’ package
my_data <- USArrests[c(1, 10, 20, 30), ]
my_data <- cbind(state = rownames(my_data), my_data)
my_data
Gather()
my_data2 <- gather(my_data, key = "arrest_attribute", value = "test", -state)
my_data2
# Entrer manuellement des données dans data frame
Extraction de données #### Eliminer les lignes pour lesquelles var1 et var2 sont vides simultanément
df[apply(df[,c("var1", "var2", "var3")], 1, function(y) !all(is.na(y))),]
STATS DESCRIPTIVES
Variables qualitatives
tab1(drep$SEXE) # {epiDisplay}
Variables Quantitatives
#epi.descriptives(drep$AGE)
#age <- epi.descriptives(drep$AGE)$a
# Tableau de variables
varquan <- c("AGE", "POIDS", "TAILLE", "PAS")
colnames <- c("n", "mean", "sd", "q25", "q50", "q75", "lower", "upper", "min", "max", "na")
tab <- as.data.frame(matrix(ncol = 11, nrow = 0 )); colnames(tab) <- colnames;
for (var in varquan) {
x <- epi.descriptives(drep[,var])$a; tab <- rbind(tab, x); rm(x)
}
tab$varquan <- varquan; tab <- tab %>% dplyr::select(12, 1:11); rm(varquan, var, colnames)
tab
Graphiques
Fonction hist
# Création de la variable
drep <- transform(drep, PAM=(PAS+2*PAD)/3)
# Histogramme Effectifs
hist(drep$PAM, col = "grey", border = "white", xlim=c(50, 120), xlab = "Pression artérielle moyenne (mmHg)", ylim=c(0,50), ylab="Effectifs", main=NULL)
# Histogramme Densité
hist(drep$PAM, prob=TRUE, col = "grey", border = "white", xlim=c(50, 120), xlab = "Pression artérielle moyenne (mmHg)", ylim=c(0,0.05), ylab="Densités", main=NULL)
# Courbe de densité de la distribution
lines(density(drep$PAM,na.rm=TRUE),lwd=2,col="orange")
# Courbe de densité de la loi normale
PAMord <- drep$PAM[order(drep$PAM)]
lines(PAMord, dnorm(PAMord, mean=mean(PAMord), sd=sd(PAMord)), col="red")
# Rajour d'un texte
text(110,0.04,paste("N =",sum(complete.cases(drep$PAM))),cex=0.9)
Histogramme : 1 variable continue
par(mfrow=c(3,3))
a <- ggplot(dataset, aes(imc))
a + geom_histogram(color = "grey", fill="ivory3", linetype = 1) +
#geom_freqpoly(color = "salmon") +
stat_density() +
labs(x = expression(paste("IMC en ", kg/m^2)), y="Effectif", title=paste("Répartition des", nrow(dataset),"individus selon leur IMC")) +
theme_classic() +
theme(plot.title = element_text(color="grey20", size=15, face="bold.italic", hjust =0.5))
1 Variable discrète et 1 variable continue
a <- ggplot(dataset, aes(sexe, imc))
a + theme_bw() + geom_boxplot() + xlab("Sexe") + ylab("IMC en cm2")
TESTS STATISTIQUES
Packages nécessaires : epiDisplay, epiR et/ou epicalc
library(epiDisplay, epiR, epicalc)
Variables qualitatives : test du Chi2
- Tableau de contingence observé
tabpct
tabpct(drep$SEXE, drep$TYPEHB)
- Tableau de contingence théorique
chisq.test(drep$SEXE, drep$TYPEHB)$expected
LS0tCnRpdGxlOiAiUiAtIEFpZGUgTcOpbW9pcmUiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBub25lCiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICB0aGVtZTogeWV0aQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwotLS0KCmBgYHtjc3MgZWNobz1GLCBldmFsPVR9CmJvZHkgICAgICB7LyogTm9ybWFsICovICBmb250LWZhbWlseToiQ2FsaWJyaSI7IGZvbnQtc2l6ZToxNHB4OyBmb250LXN0eWxlOiA7IGNvbG9yOmdyZXlibGFjayAgfQp0ZCAgICAgICAgey8qIFRhYmxlICovICAgZm9udC1mYW1pbHk6IkNhbGlicmkiOyBmb250LXNpemU6MTJweDsgfQoKaDEudGl0bGUgIHsvKiBUaXRsZSAqLyAgIGZvbnQtZmFtaWx5OiJDYWxpYnJpIjsgZm9udC1zaXplOjQwcHg7IGZvbnQtc3R5bGU6Ym9sZDsgY29sb3I6cmVkIH0KaDEuc3VidGl0bGUgey8qIFN1YnRpdGxlICovICAgZm9udC1mYW1pbHk6IkNhbGlicmkiOyBmb250LXNpemU6NTBweDsgZm9udC1zdHlsZTpib2xkOyBjb2xvcjpyZWQgfQpoMSAgICAgICAgey8qIEhlYWRlciAxICovICBmb250LWZhbWlseToiQ2FsaWJyaSI7IGZvbnQtc2l6ZToyMnB4OyBmb250LXN0eWxlOmJvbGQ7IGNvbG9yOmJsdWU7CiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbi10b3A6IDZweDsgbWFyZ2luLWJvdHRvbTogMTJweCAgfQpoMiAgICAgICAgey8qIEhlYWRlciAyICovICBmb250LWZhbWlseToiQ2FsaWJyaSI7IGZvbnQtc2l6ZToxOHB4OyBmb250LXN0eWxlczppdGFsaWM7IGNvbG9yOmJsYWNrZ3JleTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luLXRvcDogMHB4OyBtYXJnaW4tYm90dG9tOiA2cHggfQpoMyAgICAgICAgey8qIEhlYWRlciAzICovICBmb250LWZhbWlseToiQ2FsaWJyaSI7IGZvbnQtc2l6ZToxNnB4OyBmb250LXN0eWxlczpib2xkOyBjb2xvcjpncmVlbjsKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luLXRvcDogMHB4OyBtYXJnaW4tYm90dG9tOiA2cHh9Cgpjb2RlLnIgICAgey8qIENvZGUgYmxvY2sgKi8gZm9udC1mYW1pbHk6IkNhbGlicmkiOyBmb250LXNpemU6NTBweDsgY29sb3I6IHJlZH0KcHJlICAgICAgIHsvKiBDb2RlIGJsb2NrIC0gZGV0ZXJtaW5lcyBjb2RlIHNwYWNpbmcgYmV0d2VlbiBsaW5lcyAqLyBmb250LXNpemU6IDEycHg7IGNvbG9yOiB9CgpwOmZpcnN0LW9mLXR5cGUgey8qIERlZmluZSBhIG1hcmdpbiBhZnRlciBldmVyeSBmaXJzdCBwIGVsZW1lbnRzICAqL21hcmdpbi1ib3R0b206IDEycHg7fQpgYGAKCgpgYGB7ciwgb3V0LndpZHRoPSIwLjNcXGxpbmV3aWR0aCIsIGluY2x1ZGU9VCwgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcuY2FwPWMoInlvdXIgY2FwdGlvbiIpLCBlY2hvPUZ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIvVXNlcnMvRWxpc2VlL0dCX093bkNsb3VkL1IvQ2hlYXRTaGVldHMvYmFzZS1yLnBkZiIpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0UsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBULCBldmFsID0gRiwgaW5jbHVkZSA9IFQpCmBgYAoKIyBEQVRBIE1BTkFHRU1FTlQgIAojIyBEZWxldGUgcm93cyB3aG9zZSB2YXJpYWJsZXMgYXJlIE5BICAKYGBge3J9CmRmIDwtIGRmW2FwcGx5KGRmWyxjKCJ2YXIxIiwgInZhcjIiLCAidmFyMyIpXSwgMSwgZnVuY3Rpb24oeSkgIWFsbChpcy5uYSh5KSkpLF0KIyBvdQpkZiA8LSBkZltyb3dTdW1zKGlzLm5hKGRmKSkgIT0gbmNvbChkZiksXQpgYGAKCiMjIFN1bSBhIHZhcmlhYmxlIGJ5IGdyb3VwIApVc2luZyBgYWdncmVnYXRlYApgYGB7ciB9CmFnZ3JlZ2F0ZShkZiRWYXIxLCBieSA9IGxpc3QoVmFyMiA9IGRmJFZhcjIpLCBGVU49c3VtKQpgYGAKR3JvdXBlbWVudCBzZWxvbiBsZXMgbW9kYWxpdMOpcyBkZSBWYXIyIGV0IHNvbW1lIGRlIGxhIFZhcjEuIG11bHRpcGxlIGRpbWVuc2lvbnMgY2FuIGJlIHNwZWNpZmllZCBpbiAKdGhlIGBsaXN0YC4gTXVsdGlwbGUgYWdncmVnYXRlZCBtZXRyaWNzIG9mIHRoZSBzYW1lIGRhdGEgdHlwZSBjYW4gYmUgaW5jb3Jwb3JhdGVkIHZpYSBgY2JpbmRgOgpgYGB7cn0KYWdncmVnYXRlKGNiaW5kKHgkRnJlcXVlbmN5LCB4JE1ldHJpYzIsIHgkTWV0cmljMykgYnkgPSBsaXN0KFZhcjIgPSBkZiRWYXIyKSwgRlVOPXN1bSkKYGBgCgojIyBFeHRyYWN0IGZpcnN0IG4gY2hhcmFjdGVycyBmcm9tIGEgc3RyaW5nCmBgYHtyfQpzdWJzdHIoeCwgMSwgbikKYGBgCgojIyBFeHRyYWN0IGxhc3QgbiBjaGFyYWN0ZXJzIGZyb20gU3RyaW5nCmBgYHtyfQpuX2xhc3QgPC0gbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTcGVjaWZ5IG51bWJlciBvZiBjaGFyYWN0ZXJzIHRvIGV4dHJhY3QKc3Vic3RyKHgsIG5jaGFyKHgpIC0gbl9sYXN0ICsgMSwgbmNoYXIoeCkpICMgRXh0cmFjdCBsYXN0IHRocmVlIGNoYXJhY3RlcnMKYGBgCgoKCgoKCgojIyMgSW1wb3J0YXRpb24gZGUgZG9ubsOpZXMgICAKIyMjIyBEb25uw6llcyB0ZXh0ZSA6IHJlYWQudGFibGUoKQpgYGB7cn0KZCA8LSByZWFkLnRhYmxlKCJkb25uZWVzLnR4dCIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICIiLCBkZWMgPSAiLiIpCmBgYApSZW1hcnF1ZXMgOiBhcmd1bWVudHMgYGRlYz1gIGNvbW1lIGTDqWxpbWl0ZXVyIGTDqWNpbWFsIDsgYGhlYWRlcj0gVCBvdSBGYHNpIGxlIGZpY2hpZXIgY29udGllbnQgdW5lCmxpZ25lIGQnZW4tdMOqdGUuCgpkZiA8LSByZWFkLnRhYmxlKGZpbGUsIHNlcD0iXHQiLCBoZWFkZXI9VCwgbmEuc3RyaW5ncz0iIikKZGYgPC0gcmVhZC5jc3YyKGZpbGUpCmRmIDwtIHJlYWQuZGVsaW0oZmlsZSkKZGYgPC0gcmVhZC54bHMoKQoKCiMjIEV4cG9ydGF0aW9uIGRlIGRvbm7DqWVzCmBgYHtyfQojIFdyaXRlcyBhIGRmIGludG8gYSBmaWxlCnt1dGlsc30gd3JpdGUudGFibGUoZGYsIGZpbGUsIGFwcGVuZCwgcXVvdGUsIHNlcCwgbmEsIHJvd25hbWVzLCBjb2xuYW1lcywgLi4uKQp7dXRpbHN9IHdyaXRlLmNzdjIoZGYsIGZpbGUsIGFwcGVuZCwgcXVvdGUsIHNlcCwgbmEsIHJvd25hbWVzLCBjb2xuYW1lcywgLi4uKQoKIyBXcml0ZXMgYW4gQVNDSUkgdGV4dCByZXByZXNlbnRhdGlvbiBpbnRvIGFuIFIgb2JqZWN0CntiYXNlfSBkcHV0KGRmLCBmaWxlLCBjb250cm9sKQpgYGAKIyMjIENyw6lhdGlvbiBkZSBkb25uw6llcwojIyMjIHNlcShmcm9tLCB0bykKYGBge3J9CnNlcSg1LCAxMCkKYGBgCiMjIyMgcmVwKHgsdGltZXMpCnJlcCh4LHRpbWVzKSByw6lww6h0ZSB0aW1lcyBmb2lzIGxhIHZhbGV1ciB4OyB1dGlsaXNlegplYWNoPW4gcG91ciByw6lww6l0ZXIgbiBmb2lzIGNoYXF1ZSDDqWzDqW1lbnQgZGUgeApgYGB7cn0KcmVwKGMoMSwyLDMpLDIpCnJlcChjKDEsMiwzKSxlYWNoPTIpCmBgYAoKCntiYXNlfSBzZXEoZnJvbSwgdG8sIGJ5LCBsZW5ndGgpCntiYXNlfSByZXAoeCwgdGltZXMsIGVhY2gpCmBgYHtyfQpkZiR2YXIxIDwtIGMoMToxMDApICN7YmFzZX0Ke2Jhc2V9IGRmJHZhcjIgPC0gcmVwKGMoInZhbDEiLCBOQV9jaGFyYWN0ZXJfLCAidmFsMiIpLCB0aW1lID0gMywgZWFjaCA9IDQsIGxlbmd0aC5vdXQgPSBucm93KGRmKSkKYGBgCiMjIENyw6lhdGlvbiBkZSBkYXRhIGZyYW1lcwpgYGB7cn0Ke2Jhc2V9IGRhdGEuZnJhbWUodj0xOjQsIGNoPWMoImEiLCAiYiIsICJjIiwgImQiKSwgbGV0dHJlPSJBIikKe2Jhc2V9IGFzLmRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSAxMCwgbnJvdyA9IDEwMCkpOyBuYW1lcyhkZikgPC0gYygidmFyMSIsIC4uLikKIyBGcm9tIGFub3RoZXIgZGF0YSBmcmFtZQpkZjIgPC0gZGYxWywgYygidmFyMSIsICJ2YXIyIiwgInZhcjMiKV0KYGBgCiMjIFJlbXBsYWNlbWVudCBkZSB2YWxldXJzIGRhbnMgZGYKYGBge3J9CmRmW3doaWNoKGRmJHZhcjEgPT0gInZhbDEiKSwgInZhcjIiXSA8LSAidmFsMiIgIyB7YmFzZX0KYGBgCgojIyBJbmRleGVyIGRlcyB2ZWN0ZXVycwpgYGB7cn0KdmVjW25dOyB2ZWNbLW5dOyB2ZWNbMTpuXTsgdmVjWy0oMTpuKV07IHZlY1tjKDEsMiw0KV07IHZlY1sidmFsMSJdOyB2ZWNbdmVjPjNdOyB2ZWNbdmVjPjMgJiB2ZWM8NV0gCntiYXNlfSB2ZWMxICVpbiUgdmVjMgpgYGAKCiMjIEluZm9ybWF0aW9uIHN1ciBsZXMgb2JqZWN0cwpgYGB7cn0KaXMubmEodmVjKTsgaXMubnVsbCh2YWwgb3IgdmVjKTsgbGVuZ3RoKHZlYyk7IGRpbShkZik7IG5yb3coZGYpCmNsYXNzKHgpOyB1bmNsYXNzKHgpCndoaWNoLm1heCh2ZWMpOyB3aGljaC5tYXgodmVjKQpyZXYodmVjKTsgc29ydCh2ZWMpOyBvcmRlcih2ZWMpCnogPC0gc2V0ZGlmZih2ZWMxLCB2ZWMyKQpgYGAKCgoKIyMgTWFuaXB1bGF0aW9uIERhdGVzIGV0IER1csOpZXMKYGBge3IsIGVjaG89RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgRXh0cmFpcmUgbCdhbm7DqWUgZCd1bmUgZGF0ZSAgIAojZm9ybWF0KGFzLkRhdGUoZGYkRGF0ZSwgZm9ybWF0PSIlZC8lbS8lWSIpLCIlWSIpICAKIyBVc2UgYSBkaWZmZXJlbnQgb3JpZ2luIAphcy5EYXRlKDQxMTQ5LCBvcmlnaW4gPSAiMTkwMC0wMS0wMSIpICAKIyBUYWtlIGEgZGlmZmVyZW5jZSoqICAKU3lzLkRhdGUoKSAtIGFzLkRhdGUoIjE5NzAtMDEtMDEiKSAgCmRpZmZ0aW1lKFN5cy5EYXRlKCksIGFzLkRhdGUoIjE5NzAtMDEtMDEiKSwgdW5pdHMgPSAiZGF5cyIpICAKIyBTZWUgaW50ZXJuYWwgaW50ZWdlciByZXByZXNlbnRhdGlvbioqICAKdW5jbGFzcyhTeXMuRGF0ZSgpKQojIENvbnZlcnQgdGltZSAobW06c3MpIHRvIGRlY2ltYWwKdmVjIDwtIGMoIjQ6MzAiLCIyOjIwIiwiMzQ6MTAiKQpzYXBwbHkoc3Ryc3BsaXQodmVjLCI6IiksIGZ1bmN0aW9uKHgpIHt4IDwtIGFzLm51bWVyaWMoeCk7IHhbMV0reFsyXS82MH0pCmBgYAojIyBUcmFuc2Zvcm1hdGlvbiBkJ3VuIGpldSBkZSBkb25uw6llcyAKYGBge3J9CnRyYW5zZm9ybShkZiwgbmV3dmFyID0gdmFyMSArIHZhcjIpCnRyYW5zZm9ybShkZiwgbmV3dmFyID0gaWZlbHNlKCkpCmBgYAojIyBSZXNoYXBpbmcgZGF0YSAndGlkeXInIHBhY2thZ2UKYGBge3J9Cm15X2RhdGEgPC0gVVNBcnJlc3RzW2MoMSwgMTAsIDIwLCAzMCksIF0KbXlfZGF0YSA8LSBjYmluZChzdGF0ZSA9IHJvd25hbWVzKG15X2RhdGEpLCBteV9kYXRhKQpteV9kYXRhCmBgYApHYXRoZXIoKQpgYGB7cn0KbXlfZGF0YTIgPC0gZ2F0aGVyKG15X2RhdGEsIGtleSA9ICJhcnJlc3RfYXR0cmlidXRlIiwgdmFsdWUgPSAidGVzdCIsIC1zdGF0ZSkKbXlfZGF0YTIKYGBgCmBgYHtyfQojIEVudHJlciBtYW51ZWxsZW1lbnQgZGVzIGRvbm7DqWVzIGRhbnMgZGF0YSBmcmFtZQoKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KZGYyIDwtIGRmICU+JSBncm91cF9ieSgidmFyMyIpCmRmMgpgYGAKCkV4dHJhY3Rpb24gZGUgZG9ubsOpZXMKIyMjIyBFbGltaW5lciBsZXMgbGlnbmVzIHBvdXIgbGVzcXVlbGxlcyB2YXIxIGV0IHZhcjIgc29udCB2aWRlcyBzaW11bHRhbsOpbWVudApgYGB7cn0KZGZbYXBwbHkoZGZbLGMoInZhcjEiLCAidmFyMiIsICJ2YXIzIildLCAxLCBmdW5jdGlvbih5KSAhYWxsKGlzLm5hKHkpKSksXQpgYGAKCgoKCgojIFNUQVRTIERFU0NSSVBUSVZFUwojIyBWYXJpYWJsZXMgcXVhbGl0YXRpdmVzCmBgYHtyfQp0YWIxKGRyZXAkU0VYRSkgIyB7ZXBpRGlzcGxheX0KYGBgCiMjIFZhcmlhYmxlcyBRdWFudGl0YXRpdmVzCmBgYHtyfQojZXBpLmRlc2NyaXB0aXZlcyhkcmVwJEFHRSkKI2FnZSA8LSBlcGkuZGVzY3JpcHRpdmVzKGRyZXAkQUdFKSRhCiMgVGFibGVhdSBkZSB2YXJpYWJsZXMKdmFycXVhbiA8LSBjKCJBR0UiLCAiUE9JRFMiLCAiVEFJTExFIiwgIlBBUyIpCmNvbG5hbWVzIDwtIGMoIm4iLCAibWVhbiIsICJzZCIsICJxMjUiLCAicTUwIiwgInE3NSIsICJsb3dlciIsICJ1cHBlciIsICJtaW4iLCAibWF4IiwgIm5hIikKdGFiIDwtIGFzLmRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSAxMSwgbnJvdyA9IDAgKSk7IGNvbG5hbWVzKHRhYikgPC0gY29sbmFtZXM7CmZvciAodmFyIGluIHZhcnF1YW4pIHsKICB4IDwtIGVwaS5kZXNjcmlwdGl2ZXMoZHJlcFssdmFyXSkkYTsgdGFiIDwtIHJiaW5kKHRhYiwgeCk7IHJtKHgpCn0KdGFiJHZhcnF1YW4gPC0gdmFycXVhbjsgdGFiIDwtIHRhYiAlPiUgZHBseXI6OnNlbGVjdCgxMiwgMToxMSk7IHJtKHZhcnF1YW4sIHZhciwgY29sbmFtZXMpCnRhYgpgYGAKCiMjIEdyYXBoaXF1ZXMKIyMjIEZvbmN0aW9uIGBoaXN0YApgYGB7cn0KIyBDcsOpYXRpb24gZGUgbGEgdmFyaWFibGUKZHJlcCA8LSB0cmFuc2Zvcm0oZHJlcCwgUEFNPShQQVMrMipQQUQpLzMpCiMgSGlzdG9ncmFtbWUgRWZmZWN0aWZzCmhpc3QoZHJlcCRQQU0sIGNvbCA9ICJncmV5IiwgYm9yZGVyID0gIndoaXRlIiwgeGxpbT1jKDUwLCAxMjApLCB4bGFiID0gIlByZXNzaW9uIGFydMOpcmllbGxlIG1veWVubmUgKG1tSGcpIiwgeWxpbT1jKDAsNTApLCB5bGFiPSJFZmZlY3RpZnMiLCBtYWluPU5VTEwpCiMgSGlzdG9ncmFtbWUgRGVuc2l0w6kKaGlzdChkcmVwJFBBTSwgcHJvYj1UUlVFLCBjb2wgPSAiZ3JleSIsIGJvcmRlciA9ICJ3aGl0ZSIsIHhsaW09Yyg1MCwgMTIwKSwgeGxhYiA9ICJQcmVzc2lvbiBhcnTDqXJpZWxsZSBtb3llbm5lIChtbUhnKSIsIHlsaW09YygwLDAuMDUpLCB5bGFiPSJEZW5zaXTDqXMiLCBtYWluPU5VTEwpCiMgQ291cmJlIGRlIGRlbnNpdMOpIGRlIGxhIGRpc3RyaWJ1dGlvbgpsaW5lcyhkZW5zaXR5KGRyZXAkUEFNLG5hLnJtPVRSVUUpLGx3ZD0yLGNvbD0ib3JhbmdlIikKIyBDb3VyYmUgZGUgZGVuc2l0w6kgZGUgbGEgbG9pIG5vcm1hbGUKUEFNb3JkIDwtIGRyZXAkUEFNW29yZGVyKGRyZXAkUEFNKV0KbGluZXMoUEFNb3JkLCBkbm9ybShQQU1vcmQsIG1lYW49bWVhbihQQU1vcmQpLCBzZD1zZChQQU1vcmQpKSwgY29sPSJyZWQiKQojIFJham91ciBkJ3VuIHRleHRlCnRleHQoMTEwLDAuMDQscGFzdGUoIk4gPSIsc3VtKGNvbXBsZXRlLmNhc2VzKGRyZXAkUEFNKSkpLGNleD0wLjkpCmBgYAoKIyMjIEhpc3RvZ3JhbW1lIDogMSB2YXJpYWJsZSBjb250aW51ZQpgYGB7cn0KcGFyKG1mcm93PWMoMywzKSkKYSA8LSBnZ3Bsb3QoZGF0YXNldCwgYWVzKGltYykpCmEgKyBnZW9tX2hpc3RvZ3JhbShjb2xvciA9ICJncmV5IiwgZmlsbD0iaXZvcnkzIiwgbGluZXR5cGUgPSAxKSArCiAgI2dlb21fZnJlcXBvbHkoY29sb3IgPSAic2FsbW9uIikgKwogIHN0YXRfZGVuc2l0eSgpICsKICBsYWJzKHggPSBleHByZXNzaW9uKHBhc3RlKCJJTUMgZW4gIiwga2cvbV4yKSksIHk9IkVmZmVjdGlmIiwgICB0aXRsZT1wYXN0ZSgiUsOpcGFydGl0aW9uIGRlcyIsIG5yb3coZGF0YXNldCksImluZGl2aWR1cyBzZWxvbiBsZXVyIElNQyIpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSJncmV5MjAiLCBzaXplPTE1LCBmYWNlPSJib2xkLml0YWxpYyIsIGhqdXN0ID0wLjUpKQpgYGAKCiMjIyAxIFZhcmlhYmxlIGRpc2Nyw6h0ZSBldCAxIHZhcmlhYmxlIGNvbnRpbnVlCmBgYHtyfQphIDwtIGdncGxvdChkYXRhc2V0LCBhZXMoc2V4ZSwgaW1jKSkKYSArIHRoZW1lX2J3KCkgICsgZ2VvbV9ib3hwbG90KCkgKyB4bGFiKCJTZXhlIikgKyB5bGFiKCJJTUMgZW4gY20yIikKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQphIDwtIGdncGxvdChkYXRhc2V0LCBhZXMoaW1jKSkKYSArIGdlb21fZGVuc2l0eShrZXJuZWw9ImdhdXNzaWFuIikKYSArIGdlb21fZnJlcXBvbHkoKQpgYGAKCiMgVEVTVFMgU1RBVElTVElRVUVTClBhY2thZ2VzIG7DqWNlc3NhaXJlcyA6IGVwaURpc3BsYXksIGVwaVIgZXQvb3UgZXBpY2FsYwpgYGB7cn0KbGlicmFyeShlcGlEaXNwbGF5LCBlcGlSLCBlcGljYWxjKQpgYGAKIyMgVmFyaWFibGVzIHF1YWxpdGF0aXZlcyA6IHRlc3QgZHUgQ2hpMgoxKSBUYWJsZWF1IGRlIGNvbnRpbmdlbmNlIG9ic2VydsOpIGB0YWJwY3RgCmBgYHtyfQp0YWJwY3QoZHJlcCRTRVhFLCBkcmVwJFRZUEVIQikKYGBgCjIpIFRhYmxlYXUgZGUgY29udGluZ2VuY2UgdGjDqW9yaXF1ZQpgYGB7cn0KY2hpc3EudGVzdChkcmVwJFNFWEUsIGRyZXAkVFlQRUhCKSRleHBlY3RlZApgYGAKMykgCgoKCgoKCgo=