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)

Extract first n characters from a string

substr(x, 1, n)

Extract last n characters from String

n_last <- n                                # Specify number of characters to extract
substr(x, nchar(x) - n_last + 1, nchar(x)) # Extract last three characters

Importation de données

Données texte : read.table()

d <- read.table("donnees.txt", header = TRUE, sep = "", dec = ".")

Remarques : arguments dec= comme délimiteur décimal ; header= T ou Fsi le fichier contient une ligne d’en-tête.

df <- read.table(file, sep=“, header=T, na.strings=”") df <- read.csv2(file) df <- read.delim(file) df <- read.xls()

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

seq(from, to)

seq(5, 10)

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

Information sur les objects

is.na(vec); is.null(val or vec); length(vec); dim(df); nrow(df)
class(x); unclass(x)
which.max(vec); which.max(vec)
rev(vec); sort(vec); order(vec)
z <- setdiff(vec1, vec2)

Manipulation Dates et Durées

Transformation d’un jeu de données

transform(df, newvar = var1 + var2)
transform(df, newvar = ifelse())

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

  1. Tableau de contingence observé tabpct
tabpct(drep$SEXE, drep$TYPEHB)
  1. Tableau de contingence théorique
chisq.test(drep$SEXE, drep$TYPEHB)$expected
LS0tCnRpdGxlOiAiUiAtIEFpZGUgTcOpbW9pcmUiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBub25lCiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICB0aGVtZTogeWV0aQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwotLS0KCmBgYHtjc3MgZWNobz1GLCBldmFsPVR9CmJvZHkgICAgICB7LyogTm9ybWFsICovICBmb250LWZhbWlseToiQ2FsaWJyaSI7IGZvbnQtc2l6ZToxNHB4OyBmb250LXN0eWxlOiA7IGNvbG9yOmdyZXlibGFjayAgfQp0ZCAgICAgICAgey8qIFRhYmxlICovICAgZm9udC1mYW1pbHk6IkNhbGlicmkiOyBmb250LXNpemU6MTJweDsgfQoKaDEudGl0bGUgIHsvKiBUaXRsZSAqLyAgIGZvbnQtZmFtaWx5OiJDYWxpYnJpIjsgZm9udC1zaXplOjQwcHg7IGZvbnQtc3R5bGU6Ym9sZDsgY29sb3I6cmVkIH0KaDEuc3VidGl0bGUgey8qIFN1YnRpdGxlICovICAgZm9udC1mYW1pbHk6IkNhbGlicmkiOyBmb250LXNpemU6NTBweDsgZm9udC1zdHlsZTpib2xkOyBjb2xvcjpyZWQgfQpoMSAgICAgICAgey8qIEhlYWRlciAxICovICBmb250LWZhbWlseToiQ2FsaWJyaSI7IGZvbnQtc2l6ZToyMnB4OyBmb250LXN0eWxlOmJvbGQ7IGNvbG9yOmJsdWU7CiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbi10b3A6IDZweDsgbWFyZ2luLWJvdHRvbTogMTJweCAgfQpoMiAgICAgICAgey8qIEhlYWRlciAyICovICBmb250LWZhbWlseToiQ2FsaWJyaSI7IGZvbnQtc2l6ZToxOHB4OyBmb250LXN0eWxlczppdGFsaWM7IGNvbG9yOmJsYWNrZ3JleTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luLXRvcDogMHB4OyBtYXJnaW4tYm90dG9tOiA2cHggfQpoMyAgICAgICAgey8qIEhlYWRlciAzICovICBmb250LWZhbWlseToiQ2FsaWJyaSI7IGZvbnQtc2l6ZToxNnB4OyBmb250LXN0eWxlczpib2xkOyBjb2xvcjpncmVlbjsKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luLXRvcDogMHB4OyBtYXJnaW4tYm90dG9tOiA2cHh9Cgpjb2RlLnIgICAgey8qIENvZGUgYmxvY2sgKi8gZm9udC1mYW1pbHk6IkNhbGlicmkiOyBmb250LXNpemU6NTBweDsgY29sb3I6IHJlZH0KcHJlICAgICAgIHsvKiBDb2RlIGJsb2NrIC0gZGV0ZXJtaW5lcyBjb2RlIHNwYWNpbmcgYmV0d2VlbiBsaW5lcyAqLyBmb250LXNpemU6IDEycHg7IGNvbG9yOiB9CgpwOmZpcnN0LW9mLXR5cGUgey8qIERlZmluZSBhIG1hcmdpbiBhZnRlciBldmVyeSBmaXJzdCBwIGVsZW1lbnRzICAqL21hcmdpbi1ib3R0b206IDEycHg7fQpgYGAKCgpgYGB7ciwgb3V0LndpZHRoPSIwLjNcXGxpbmV3aWR0aCIsIGluY2x1ZGU9VCwgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcuY2FwPWMoInlvdXIgY2FwdGlvbiIpLCBlY2hvPUZ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIvVXNlcnMvRWxpc2VlL0dCX093bkNsb3VkL1IvQ2hlYXRTaGVldHMvYmFzZS1yLnBkZiIpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0UsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBULCBldmFsID0gRiwgaW5jbHVkZSA9IFQpCmBgYAoKIyBEQVRBIE1BTkFHRU1FTlQgIAojIyBEZWxldGUgcm93cyB3aG9zZSB2YXJpYWJsZXMgYXJlIE5BICAKYGBge3J9CmRmIDwtIGRmW2FwcGx5KGRmWyxjKCJ2YXIxIiwgInZhcjIiLCAidmFyMyIpXSwgMSwgZnVuY3Rpb24oeSkgIWFsbChpcy5uYSh5KSkpLF0KIyBvdQpkZiA8LSBkZltyb3dTdW1zKGlzLm5hKGRmKSkgIT0gbmNvbChkZiksXQpgYGAKCiMjIFN1bSBhIHZhcmlhYmxlIGJ5IGdyb3VwIApVc2luZyBgYWdncmVnYXRlYApgYGB7ciB9CmFnZ3JlZ2F0ZShkZiRWYXIxLCBieSA9IGxpc3QoVmFyMiA9IGRmJFZhcjIpLCBGVU49c3VtKQpgYGAKR3JvdXBlbWVudCBzZWxvbiBsZXMgbW9kYWxpdMOpcyBkZSBWYXIyIGV0IHNvbW1lIGRlIGxhIFZhcjEuIG11bHRpcGxlIGRpbWVuc2lvbnMgY2FuIGJlIHNwZWNpZmllZCBpbiAKdGhlIGBsaXN0YC4gTXVsdGlwbGUgYWdncmVnYXRlZCBtZXRyaWNzIG9mIHRoZSBzYW1lIGRhdGEgdHlwZSBjYW4gYmUgaW5jb3Jwb3JhdGVkIHZpYSBgY2JpbmRgOgpgYGB7cn0KYWdncmVnYXRlKGNiaW5kKHgkRnJlcXVlbmN5LCB4JE1ldHJpYzIsIHgkTWV0cmljMykgYnkgPSBsaXN0KFZhcjIgPSBkZiRWYXIyKSwgRlVOPXN1bSkKYGBgCgojIyBFeHRyYWN0IGZpcnN0IG4gY2hhcmFjdGVycyBmcm9tIGEgc3RyaW5nCmBgYHtyfQpzdWJzdHIoeCwgMSwgbikKYGBgCgojIyBFeHRyYWN0IGxhc3QgbiBjaGFyYWN0ZXJzIGZyb20gU3RyaW5nCmBgYHtyfQpuX2xhc3QgPC0gbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTcGVjaWZ5IG51bWJlciBvZiBjaGFyYWN0ZXJzIHRvIGV4dHJhY3QKc3Vic3RyKHgsIG5jaGFyKHgpIC0gbl9sYXN0ICsgMSwgbmNoYXIoeCkpICMgRXh0cmFjdCBsYXN0IHRocmVlIGNoYXJhY3RlcnMKYGBgCgoKCgoKCgojIyMgSW1wb3J0YXRpb24gZGUgZG9ubsOpZXMgICAKIyMjIyBEb25uw6llcyB0ZXh0ZSA6IHJlYWQudGFibGUoKQpgYGB7cn0KZCA8LSByZWFkLnRhYmxlKCJkb25uZWVzLnR4dCIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICIiLCBkZWMgPSAiLiIpCmBgYApSZW1hcnF1ZXMgOiBhcmd1bWVudHMgYGRlYz1gIGNvbW1lIGTDqWxpbWl0ZXVyIGTDqWNpbWFsIDsgYGhlYWRlcj0gVCBvdSBGYHNpIGxlIGZpY2hpZXIgY29udGllbnQgdW5lCmxpZ25lIGQnZW4tdMOqdGUuCgpkZiA8LSByZWFkLnRhYmxlKGZpbGUsIHNlcD0iXHQiLCBoZWFkZXI9VCwgbmEuc3RyaW5ncz0iIikKZGYgPC0gcmVhZC5jc3YyKGZpbGUpCmRmIDwtIHJlYWQuZGVsaW0oZmlsZSkKZGYgPC0gcmVhZC54bHMoKQoKCiMjIEV4cG9ydGF0aW9uIGRlIGRvbm7DqWVzCmBgYHtyfQojIFdyaXRlcyBhIGRmIGludG8gYSBmaWxlCnt1dGlsc30gd3JpdGUudGFibGUoZGYsIGZpbGUsIGFwcGVuZCwgcXVvdGUsIHNlcCwgbmEsIHJvd25hbWVzLCBjb2xuYW1lcywgLi4uKQp7dXRpbHN9IHdyaXRlLmNzdjIoZGYsIGZpbGUsIGFwcGVuZCwgcXVvdGUsIHNlcCwgbmEsIHJvd25hbWVzLCBjb2xuYW1lcywgLi4uKQoKIyBXcml0ZXMgYW4gQVNDSUkgdGV4dCByZXByZXNlbnRhdGlvbiBpbnRvIGFuIFIgb2JqZWN0CntiYXNlfSBkcHV0KGRmLCBmaWxlLCBjb250cm9sKQpgYGAKIyMjIENyw6lhdGlvbiBkZSBkb25uw6llcwojIyMjIHNlcShmcm9tLCB0bykKYGBge3J9CnNlcSg1LCAxMCkKYGBgCiMjIyMgcmVwKHgsdGltZXMpCnJlcCh4LHRpbWVzKSByw6lww6h0ZSB0aW1lcyBmb2lzIGxhIHZhbGV1ciB4OyB1dGlsaXNlegplYWNoPW4gcG91ciByw6lww6l0ZXIgbiBmb2lzIGNoYXF1ZSDDqWzDqW1lbnQgZGUgeApgYGB7cn0KcmVwKGMoMSwyLDMpLDIpCnJlcChjKDEsMiwzKSxlYWNoPTIpCmBgYAoKCntiYXNlfSBzZXEoZnJvbSwgdG8sIGJ5LCBsZW5ndGgpCntiYXNlfSByZXAoeCwgdGltZXMsIGVhY2gpCmBgYHtyfQpkZiR2YXIxIDwtIGMoMToxMDApICN7YmFzZX0Ke2Jhc2V9IGRmJHZhcjIgPC0gcmVwKGMoInZhbDEiLCBOQV9jaGFyYWN0ZXJfLCAidmFsMiIpLCB0aW1lID0gMywgZWFjaCA9IDQsIGxlbmd0aC5vdXQgPSBucm93KGRmKSkKYGBgCiMjIENyw6lhdGlvbiBkZSBkYXRhIGZyYW1lcwpgYGB7cn0Ke2Jhc2V9IGRhdGEuZnJhbWUodj0xOjQsIGNoPWMoImEiLCAiYiIsICJjIiwgImQiKSwgbGV0dHJlPSJBIikKe2Jhc2V9IGFzLmRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSAxMCwgbnJvdyA9IDEwMCkpOyBuYW1lcyhkZikgPC0gYygidmFyMSIsIC4uLikKIyBGcm9tIGFub3RoZXIgZGF0YSBmcmFtZQpkZjIgPC0gZGYxWywgYygidmFyMSIsICJ2YXIyIiwgInZhcjMiKV0KYGBgCiMjIFJlbXBsYWNlbWVudCBkZSB2YWxldXJzIGRhbnMgZGYKYGBge3J9CmRmW3doaWNoKGRmJHZhcjEgPT0gInZhbDEiKSwgInZhcjIiXSA8LSAidmFsMiIgIyB7YmFzZX0KYGBgCgojIyBJbmRleGVyIGRlcyB2ZWN0ZXVycwpgYGB7cn0KdmVjW25dOyB2ZWNbLW5dOyB2ZWNbMTpuXTsgdmVjWy0oMTpuKV07IHZlY1tjKDEsMiw0KV07IHZlY1sidmFsMSJdOyB2ZWNbdmVjPjNdOyB2ZWNbdmVjPjMgJiB2ZWM8NV0gCntiYXNlfSB2ZWMxICVpbiUgdmVjMgpgYGAKCiMjIEluZm9ybWF0aW9uIHN1ciBsZXMgb2JqZWN0cwpgYGB7cn0KaXMubmEodmVjKTsgaXMubnVsbCh2YWwgb3IgdmVjKTsgbGVuZ3RoKHZlYyk7IGRpbShkZik7IG5yb3coZGYpCmNsYXNzKHgpOyB1bmNsYXNzKHgpCndoaWNoLm1heCh2ZWMpOyB3aGljaC5tYXgodmVjKQpyZXYodmVjKTsgc29ydCh2ZWMpOyBvcmRlcih2ZWMpCnogPC0gc2V0ZGlmZih2ZWMxLCB2ZWMyKQpgYGAKCgoKIyMgTWFuaXB1bGF0aW9uIERhdGVzIGV0IER1csOpZXMKYGBge3IsIGVjaG89RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgRXh0cmFpcmUgbCdhbm7DqWUgZCd1bmUgZGF0ZSAgIAojZm9ybWF0KGFzLkRhdGUoZGYkRGF0ZSwgZm9ybWF0PSIlZC8lbS8lWSIpLCIlWSIpICAKIyBVc2UgYSBkaWZmZXJlbnQgb3JpZ2luIAphcy5EYXRlKDQxMTQ5LCBvcmlnaW4gPSAiMTkwMC0wMS0wMSIpICAKIyBUYWtlIGEgZGlmZmVyZW5jZSoqICAKU3lzLkRhdGUoKSAtIGFzLkRhdGUoIjE5NzAtMDEtMDEiKSAgCmRpZmZ0aW1lKFN5cy5EYXRlKCksIGFzLkRhdGUoIjE5NzAtMDEtMDEiKSwgdW5pdHMgPSAiZGF5cyIpICAKIyBTZWUgaW50ZXJuYWwgaW50ZWdlciByZXByZXNlbnRhdGlvbioqICAKdW5jbGFzcyhTeXMuRGF0ZSgpKQojIENvbnZlcnQgdGltZSAobW06c3MpIHRvIGRlY2ltYWwKdmVjIDwtIGMoIjQ6MzAiLCIyOjIwIiwiMzQ6MTAiKQpzYXBwbHkoc3Ryc3BsaXQodmVjLCI6IiksIGZ1bmN0aW9uKHgpIHt4IDwtIGFzLm51bWVyaWMoeCk7IHhbMV0reFsyXS82MH0pCmBgYAojIyBUcmFuc2Zvcm1hdGlvbiBkJ3VuIGpldSBkZSBkb25uw6llcyAKYGBge3J9CnRyYW5zZm9ybShkZiwgbmV3dmFyID0gdmFyMSArIHZhcjIpCnRyYW5zZm9ybShkZiwgbmV3dmFyID0gaWZlbHNlKCkpCmBgYAojIyBSZXNoYXBpbmcgZGF0YSAndGlkeXInIHBhY2thZ2UKYGBge3J9Cm15X2RhdGEgPC0gVVNBcnJlc3RzW2MoMSwgMTAsIDIwLCAzMCksIF0KbXlfZGF0YSA8LSBjYmluZChzdGF0ZSA9IHJvd25hbWVzKG15X2RhdGEpLCBteV9kYXRhKQpteV9kYXRhCmBgYApHYXRoZXIoKQpgYGB7cn0KbXlfZGF0YTIgPC0gZ2F0aGVyKG15X2RhdGEsIGtleSA9ICJhcnJlc3RfYXR0cmlidXRlIiwgdmFsdWUgPSAidGVzdCIsIC1zdGF0ZSkKbXlfZGF0YTIKYGBgCmBgYHtyfQojIEVudHJlciBtYW51ZWxsZW1lbnQgZGVzIGRvbm7DqWVzIGRhbnMgZGF0YSBmcmFtZQoKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KZGYyIDwtIGRmICU+JSBncm91cF9ieSgidmFyMyIpCmRmMgpgYGAKCkV4dHJhY3Rpb24gZGUgZG9ubsOpZXMKIyMjIyBFbGltaW5lciBsZXMgbGlnbmVzIHBvdXIgbGVzcXVlbGxlcyB2YXIxIGV0IHZhcjIgc29udCB2aWRlcyBzaW11bHRhbsOpbWVudApgYGB7cn0KZGZbYXBwbHkoZGZbLGMoInZhcjEiLCAidmFyMiIsICJ2YXIzIildLCAxLCBmdW5jdGlvbih5KSAhYWxsKGlzLm5hKHkpKSksXQpgYGAKCgoKCgojIFNUQVRTIERFU0NSSVBUSVZFUwojIyBWYXJpYWJsZXMgcXVhbGl0YXRpdmVzCmBgYHtyfQp0YWIxKGRyZXAkU0VYRSkgIyB7ZXBpRGlzcGxheX0KYGBgCiMjIFZhcmlhYmxlcyBRdWFudGl0YXRpdmVzCmBgYHtyfQojZXBpLmRlc2NyaXB0aXZlcyhkcmVwJEFHRSkKI2FnZSA8LSBlcGkuZGVzY3JpcHRpdmVzKGRyZXAkQUdFKSRhCiMgVGFibGVhdSBkZSB2YXJpYWJsZXMKdmFycXVhbiA8LSBjKCJBR0UiLCAiUE9JRFMiLCAiVEFJTExFIiwgIlBBUyIpCmNvbG5hbWVzIDwtIGMoIm4iLCAibWVhbiIsICJzZCIsICJxMjUiLCAicTUwIiwgInE3NSIsICJsb3dlciIsICJ1cHBlciIsICJtaW4iLCAibWF4IiwgIm5hIikKdGFiIDwtIGFzLmRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSAxMSwgbnJvdyA9IDAgKSk7IGNvbG5hbWVzKHRhYikgPC0gY29sbmFtZXM7CmZvciAodmFyIGluIHZhcnF1YW4pIHsKICB4IDwtIGVwaS5kZXNjcmlwdGl2ZXMoZHJlcFssdmFyXSkkYTsgdGFiIDwtIHJiaW5kKHRhYiwgeCk7IHJtKHgpCn0KdGFiJHZhcnF1YW4gPC0gdmFycXVhbjsgdGFiIDwtIHRhYiAlPiUgZHBseXI6OnNlbGVjdCgxMiwgMToxMSk7IHJtKHZhcnF1YW4sIHZhciwgY29sbmFtZXMpCnRhYgpgYGAKCiMjIEdyYXBoaXF1ZXMKIyMjIEZvbmN0aW9uIGBoaXN0YApgYGB7cn0KIyBDcsOpYXRpb24gZGUgbGEgdmFyaWFibGUKZHJlcCA8LSB0cmFuc2Zvcm0oZHJlcCwgUEFNPShQQVMrMipQQUQpLzMpCiMgSGlzdG9ncmFtbWUgRWZmZWN0aWZzCmhpc3QoZHJlcCRQQU0sIGNvbCA9ICJncmV5IiwgYm9yZGVyID0gIndoaXRlIiwgeGxpbT1jKDUwLCAxMjApLCB4bGFiID0gIlByZXNzaW9uIGFydMOpcmllbGxlIG1veWVubmUgKG1tSGcpIiwgeWxpbT1jKDAsNTApLCB5bGFiPSJFZmZlY3RpZnMiLCBtYWluPU5VTEwpCiMgSGlzdG9ncmFtbWUgRGVuc2l0w6kKaGlzdChkcmVwJFBBTSwgcHJvYj1UUlVFLCBjb2wgPSAiZ3JleSIsIGJvcmRlciA9ICJ3aGl0ZSIsIHhsaW09Yyg1MCwgMTIwKSwgeGxhYiA9ICJQcmVzc2lvbiBhcnTDqXJpZWxsZSBtb3llbm5lIChtbUhnKSIsIHlsaW09YygwLDAuMDUpLCB5bGFiPSJEZW5zaXTDqXMiLCBtYWluPU5VTEwpCiMgQ291cmJlIGRlIGRlbnNpdMOpIGRlIGxhIGRpc3RyaWJ1dGlvbgpsaW5lcyhkZW5zaXR5KGRyZXAkUEFNLG5hLnJtPVRSVUUpLGx3ZD0yLGNvbD0ib3JhbmdlIikKIyBDb3VyYmUgZGUgZGVuc2l0w6kgZGUgbGEgbG9pIG5vcm1hbGUKUEFNb3JkIDwtIGRyZXAkUEFNW29yZGVyKGRyZXAkUEFNKV0KbGluZXMoUEFNb3JkLCBkbm9ybShQQU1vcmQsIG1lYW49bWVhbihQQU1vcmQpLCBzZD1zZChQQU1vcmQpKSwgY29sPSJyZWQiKQojIFJham91ciBkJ3VuIHRleHRlCnRleHQoMTEwLDAuMDQscGFzdGUoIk4gPSIsc3VtKGNvbXBsZXRlLmNhc2VzKGRyZXAkUEFNKSkpLGNleD0wLjkpCmBgYAoKIyMjIEhpc3RvZ3JhbW1lIDogMSB2YXJpYWJsZSBjb250aW51ZQpgYGB7cn0KcGFyKG1mcm93PWMoMywzKSkKYSA8LSBnZ3Bsb3QoZGF0YXNldCwgYWVzKGltYykpCmEgKyBnZW9tX2hpc3RvZ3JhbShjb2xvciA9ICJncmV5IiwgZmlsbD0iaXZvcnkzIiwgbGluZXR5cGUgPSAxKSArCiAgI2dlb21fZnJlcXBvbHkoY29sb3IgPSAic2FsbW9uIikgKwogIHN0YXRfZGVuc2l0eSgpICsKICBsYWJzKHggPSBleHByZXNzaW9uKHBhc3RlKCJJTUMgZW4gIiwga2cvbV4yKSksIHk9IkVmZmVjdGlmIiwgICB0aXRsZT1wYXN0ZSgiUsOpcGFydGl0aW9uIGRlcyIsIG5yb3coZGF0YXNldCksImluZGl2aWR1cyBzZWxvbiBsZXVyIElNQyIpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSJncmV5MjAiLCBzaXplPTE1LCBmYWNlPSJib2xkLml0YWxpYyIsIGhqdXN0ID0wLjUpKQpgYGAKCiMjIyAxIFZhcmlhYmxlIGRpc2Nyw6h0ZSBldCAxIHZhcmlhYmxlIGNvbnRpbnVlCmBgYHtyfQphIDwtIGdncGxvdChkYXRhc2V0LCBhZXMoc2V4ZSwgaW1jKSkKYSArIHRoZW1lX2J3KCkgICsgZ2VvbV9ib3hwbG90KCkgKyB4bGFiKCJTZXhlIikgKyB5bGFiKCJJTUMgZW4gY20yIikKYGBgCgpgYGB7ciBlY2hvPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQphIDwtIGdncGxvdChkYXRhc2V0LCBhZXMoaW1jKSkKYSArIGdlb21fZGVuc2l0eShrZXJuZWw9ImdhdXNzaWFuIikKYSArIGdlb21fZnJlcXBvbHkoKQpgYGAKCiMgVEVTVFMgU1RBVElTVElRVUVTClBhY2thZ2VzIG7DqWNlc3NhaXJlcyA6IGVwaURpc3BsYXksIGVwaVIgZXQvb3UgZXBpY2FsYwpgYGB7cn0KbGlicmFyeShlcGlEaXNwbGF5LCBlcGlSLCBlcGljYWxjKQpgYGAKIyMgVmFyaWFibGVzIHF1YWxpdGF0aXZlcyA6IHRlc3QgZHUgQ2hpMgoxKSBUYWJsZWF1IGRlIGNvbnRpbmdlbmNlIG9ic2VydsOpIGB0YWJwY3RgCmBgYHtyfQp0YWJwY3QoZHJlcCRTRVhFLCBkcmVwJFRZUEVIQikKYGBgCjIpIFRhYmxlYXUgZGUgY29udGluZ2VuY2UgdGjDqW9yaXF1ZQpgYGB7cn0KY2hpc3EudGVzdChkcmVwJFNFWEUsIGRyZXAkVFlQRUhCKSRleHBlY3RlZApgYGAKMykgCgoKCgoKCgo=