Pour les utilisateurs de R, on commence par charger un certain nombre de modules intéressants.

# le package moderne standard pour manipuler ses données
library(tidyverse)

# quelques couleurs manuelles
bleufonce <- "#3d5468"
bleuclair <- "#5b7c98"
rose <- "#ff5555"

Préparation des données

Simulation

Pour se focaliser sur les stats et uniquement les stats, on travaille sur un jeu de données idéalisé. Et puisqu’on bosse dans les essais cliniques, on va simuler le RCT (Randomized Controlled Trial) ayant les caractéristiques suivantes :

  • l’étude possède deux bras: control et treatment.
  • on enregistre des variables démographiques à l’inclusion, type sex, age, age_group, height, weight_t0.
  • une variable d’outcome liés à la prise de poids : diff_weight.
  • une variable d’outcome à trois modalités : feels_sleepy.
  • une variable de temps de suivi du traitement : t_compliance.
  • une variable d’outcome de nombre de chutes pendant le suivi : n_falls.

Pour ceux que ça intéresse, voici la loi dans laquelle les données sont simulées :

# data that do not depend on the group
d <- data.frame(id = seq(1,500),
                group = as.factor(c(rep("control",250), rep("treatment",250))),
                sex = ifelse(rbinom(n=500, size=1, prob=0.4) == 0, "F", "M"),
                age = round(runif(n=500, min=19, max=77)),
                height = round(rnorm(n=500, mean=1.7, sd=0.1), digits=2),
                weight_t0 = round(rnorm(n=500, mean=70, sd=10)),
                t_compliance = round(rexp(n=500, rate=0.05))  )

# possibilities for the "feels_sleepy" variable
modalites <- c("morning", "afternoon", "always")

# data that do depend on the group
d <- d %>% mutate(diff_weight = c(rnorm(n=250, mean=0, sd=2.5), rnorm(n=250, mean=0.025*d$t_compliance[251:500], sd=2.5)),
                  feels_sleepy = c( sample(modalites, 250, prob = c(0.3, 0.4, 0.3), replace = TRUE),
                                    sample(modalites, 250, prob = c(0.2, 0.3, 0.5), replace = TRUE) ),
                  n_falls = c(rpois(n=250, lambda=6), rpois(n=250, lambda=5.5)) )

d <- d %>% mutate(weight_tf = weight_t0 + diff_weight,
                  age_group = as.factor(ifelse(age < 40, "moins de 40", ifelse(age < 60, "entre 40 et 60", "plus de 60"))) ) 

Ecriture/Lecture du dataset

Pour être sûr de travailler sur le fichier tel qu’il sera lu par tout le monde, on enregistre le dataset simulé puis on écrase le dataset avec l’import.

write.csv(d, "etude_fictive.csv")
d <- read.csv("etude_fictive.csv")
head(d)
##   X id   group sex age height weight_t0 t_compliance diff_weight feels_sleepy
## 1 1  1 control   F  77   1.59        72           16   8.1030007      morning
## 2 2  2 control   M  62   1.72        72           14   1.8256008      morning
## 3 3  3 control   F  74   1.83        79            4   2.0608949       always
## 4 4  4 control   F  42   1.56        65           51   2.4725727    afternoon
## 5 5  5 control   F  75   1.51        65           22   0.2801064       always
## 6 6  6 control   F  50   1.80        64           48   2.8282484      morning
##   n_falls weight_tf      age_group
## 1       3  80.10300     plus de 60
## 2       5  73.82560     plus de 60
## 3       5  81.06089     plus de 60
## 4       7  67.47257 entre 40 et 60
## 5       7  65.28011     plus de 60
## 6       8  66.82825 entre 40 et 60

Nous sommes prêts à travailler sur ce jeu de données, et, pour ce qui nous intéresse aujourd’hui, à analyser les données continues qu’il renferme.

T test : adéquation à une norme

On observe des différences de poids \((X_i)\) chez nos individus entre le début et la fin de l’expérience. Est-ce que la différence de poids moyenne est différente de zéro ?

On souhaite réaliser le test d’hypothèse suivant :

  • H0 : la moyenne des \((X_i)\) vaut zero.
  • H1 : la moyenne des \((X_i)\) est différente de zéro.

Conditions d’application :

  • les variables aléatoires sont indépendantes et de même loi.
  • soit les variables aléatoires sont distribuées normalement, soit l’échantillon est “grand”.

On commence toujours par représenter nos données.

plot_diff_weight <- d %>% ggplot(aes(x=diff_weight)) +
    geom_histogram(alpha=0.7, position="identity", bins=20) +
    theme_bw() +
    ylab("effectif") + 
    xlab("différence de poids à 6 mois")
plot_diff_weight

Ici les conditions d’application semblent respectées.

Un résultat théorique certifie qu’une statistique de test calculée à partir de l’effectif, de la moyenne empirique et de la variance empirique, est distribuée suivant une loi de Student avec un certain nombre de “degrés de liberté”.

Il s’agit donc de calculer la valeur observée de cette statistique de test sur notre échantillon, et de la comparer aux quantiles de la loi théorique.

La fonction pour faire ça en R est la suivante :

t.test(x=d$diff_weight)
## 
##  One Sample t-test
## 
## data:  d$diff_weight
## t = 3.7315, df = 499, p-value = 0.0002122
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
##  0.1980453 0.6385225
## sample estimates:
## mean of x 
## 0.4182839

L’output de la fonction nous rappelle :

  1. en première ligne l’échantillon sur lequel on a appliqué le test,
  2. en seconde ligne on récupère la valeur observée de la statistique de test, ainsi que le nombre de “degrés de liberté” de la loi théorique.
  3. en fin de seconde ligne, super importante : la p-value !!! Probabilité que la valeur de la statistique soit au moins aussi extrême qu’observée.
  4. troisième ligne : un rappel de l’hypothèse alternative du test (modifiable avec l’argument alternative).
  5. un intervalle de confiance à 95% de la moyenne (bornes modifiables via l’argument conf.level).
  6. tout à la fin, l’estimation ponctuelle de la moyenne.

Que concluons nous ???

T test : comparaison de 2 moyennes

On observe des valeurs \((X_{i,j})\) de nos individus \(i\) dans deux groupes \(j=1\) et \(j=2\). Ici les deux groupes correspondent au control vs. treatment, et les observations correspondent à une différence de poids entre le début et la fin de l’expérience.

Est-ce que la différence de poids moyenne est différente dans les deux groupes ?

On souhaite réaliser le test d’hypothèse suivant :

  • H0 : la moyenne des \((X_{i,1})\) est identique à celle des \((X_{i,2})\).
  • H1 : les deux moyennes sont différentes.

Conditions d’application :

  • les variables aléatoires sont indépendantes et de même loi.
  • soit les variables aléatoires sont distribuées normalement, soit l’échantillon est “grand”.

On commence toujours par représenter nos données et quantités d’intérêt.

m <- d %>% group_by(group) %>% summarise(mean=mean(diff_weight)) %>% select(mean)

plot_diff_weight_by_group <- d %>% ggplot(aes(x=diff_weight, fill=group)) +
    geom_histogram(alpha=0.5, position="identity", bins=20) +
    theme_bw() +
    ylab("effectif") + 
    xlab("différence de poids à 6 mois") +
    scale_fill_manual(values=c(bleuclair, rose)) +
    geom_vline(xintercept = c(m$mean[1], m$mean[2]), color=c(bleuclair, rose))
plot_diff_weight_by_group

Ici les conditions d’application semblent respectées.

Un résultat théorique certifie qu’une statistique de test calculée à partir de l’effectif, des moyennes empiriques des deux groupes et de la variance empirique des deux groupes, est distribuée suivant une loi de Student avec un certain nombre de “degrés de liberté”.

Il s’agit donc de calculer la valeur observée de cette statistique de test sur notre échantillon, et de la comparer aux quantiles de la loi théorique.

La fonction pour faire ça en R est la suivante :

t.test(x=d$diff_weight[d$group == "control"], y=d$diff_weight[d$group == "treatment"])
## 
##  Welch Two Sample t-test
## 
## data:  d$diff_weight[d$group == "control"] and d$diff_weight[d$group == "treatment"]
## t = -1.7256, df = 495.02, p-value = 0.08505
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -0.82571065  0.05352049
## sample estimates:
## mean of x mean of y 
## 0.2252363 0.6113314

Une syntaxe alternative pratique existe également :

t.test(diff_weight ~ group, data=d)
## 
##  Welch Two Sample t-test
## 
## data:  diff_weight by group
## t = -1.7256, df = 495.02, p-value = 0.08505
## alternative hypothesis: true difference in means between group control and group treatment is not equal to 0
## 95 percent confidence interval:
##  -0.82571065  0.05352049
## sample estimates:
##   mean in group control mean in group treatment 
##               0.2252363               0.6113314

Ca se lit de façon très similaire à ce qu’on avait précédemment, les différences étant :

  1. L’IC à 95% correspond à celui de la différence des deux moyennes.
  2. Les estimations ponctuelles correspondent aux deux moyennes.

Que concluons-nous ???

T test : option “apparié”

Je rajoute une section à propos de cette option d’appariement, qui bien souvent embrouille plus qu’elle n’aide.

On possède des observations appariées, typiquement la même quantité mesurée “avant” \((X_i)\) et “après” \((Y_i)\) intervention sur chaque individu. Par exemple, le poids du patient avant intervention et après intervention. Y a t’il un changement de poids du patient avant et après intervention ?

Cela revient à tester : * H0 : la moyenne des \((Y_i - X_i)\) vaut zero. * H1 : la moyenne des \((Y_i - X_i)\) est différente de zéro.

C’est donc totalement similaire au premier test qu’on avait appliqué directement sur la différence de poids.

La façon de faire ça en R est la suivante (et on vérifie que ça donne bien le même résultat) :

t.test(x=d$weight_tf, y=d$weight_t0, paired=TRUE)
## 
##  Paired t-test
## 
## data:  d$weight_tf and d$weight_t0
## t = 3.7315, df = 499, p-value = 0.0002122
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
##  0.1980453 0.6385225
## sample estimates:
## mean difference 
##       0.4182839
t.test(d$diff_weight)
## 
##  One Sample t-test
## 
## data:  d$diff_weight
## t = 3.7315, df = 499, p-value = 0.0002122
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
##  0.1980453 0.6385225
## sample estimates:
## mean of x 
## 0.4182839

ANOVA : comparaison de moyennes

On souhaite plus précisément tester ici la différence d’au moins une moyenne parmi plusieurs groupes.

On dispose cette fois d’observations de la même quantité $(X_{i,j}) chez l’individu \(i\) dans le groupe \(j\), dans un nombre de groupes potentiellement supérieur à 2. Est-ce que la moyenne d’au moins un groupe diffère des autres groupes ?

Par exemple, ici, la différence de poids moyenne change-t-elle dans les trois classes d’âge ? (< 40, 40-60, > 60)

On teste formellement :

  • H0 : les moyennes des \((X_{i,1})\), des \((X_{i,2})\) et des \((X_{i,3})\) sont identiques.
  • H1 : au moins une des moyennes diffère.

Conditions d’application :

  • les variables aléatoires sont indépendantes suivent une loi normale.
  • il y a égalité de variance entre les différents groupes.

Une fois n’est pas coutûme, commençons par représenter les données :

m <- d %>% group_by(age_group) %>% summarise(mean=mean(diff_weight)) %>% select(mean)

plot_diff_weight_by_age_group <- d %>% ggplot(aes(x=diff_weight, fill=as.factor(age_group))) +
    geom_histogram(alpha=0.5, position="identity", bins=20) +
    theme_bw() +
    ylab("effectif") + 
    xlab("différence de poids à 6 mois") +
    scale_fill_manual(values=c(bleufonce, bleuclair, rose)) +
    geom_vline(xintercept = c(m$mean[1], m$mean[2], m$mean[3]), color=c(bleufonce, bleuclair, rose))
plot_diff_weight_by_age_group

Les conditions d’application semblent respectées (au moins graphiquement).

Un résultat théorique certifie cette fois qu’une certaine statistique de test, calculée à partir des effectifs des différents groupes et des variances intra-groupes / inter-groupes, est distribuée suivant une loi de Fisher avec un certain nombre de “degrés de liberté”.

Il s’agit donc de calculer la valeur observée de cette statistique de test sur notre échantillon, et de la comparer aux quantiles de la loi théorique.

La fonction pour faire ça en R est la suivante :

res <- aov(diff_weight ~ age_group, data=d)
summary(res)
##              Df Sum Sq Mean Sq F value Pr(>F)
## age_group     2    0.4   0.177   0.028  0.972
## Residuals   497 3134.7   6.307

Les deux points les plus intéressants de l’output sont :

  1. la “F value” qui est la valeur de la statistique de test,
  2. et la p-value : la probabilité sous H0 d’obtenir une statistique de test au moins aussi extrême qu’observée.

Que concluons-nous ???

Finissons par un petit test, permettant de se convaincre qu’une ANOVA avec deux groupes correspond à un T test.

res <- aov(diff_weight ~ group, data=d)
summary(res)
##              Df Sum Sq Mean Sq F value Pr(>F)  
## group         1   18.6  18.634   2.978  0.085 .
## Residuals   498 3116.5   6.258                 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
t.test(diff_weight ~ group, data=d)
## 
##  Welch Two Sample t-test
## 
## data:  diff_weight by group
## t = -1.7256, df = 495.02, p-value = 0.08505
## alternative hypothesis: true difference in means between group control and group treatment is not equal to 0
## 95 percent confidence interval:
##  -0.82571065  0.05352049
## sample estimates:
##   mean in group control mean in group treatment 
##               0.2252363               0.6113314

Test de corrélation de Pearson

On dispose cette fois d’observations d’une variables \((Y_i)\) d’intérêt, par exemple encore une fois la différence de poids. Et d’une autre variable quantitative \((X_i)\) correspondant à quelque chose qui pourrait être corrélé à nos \((Y_i)\), par exemple ici le temps pendant lequel le patient a strictement observé son traitement (compliance).

Est-ce que le gain de poids dépend de la compliance, dans le groupe traité ?

Le coefficient de corrélation \(\rho\) correspond à la covariance des deux quantités normalisée par les écarts-types des deux quantités. Il est compris entre -1 et 1 et renseigne sur la dispersion des données en 2D. En cas de corrélation linéaire parfaite, il vaut 1 ou -1.

Formellement on peut tester, avec le test de Bravais-Pearson :

  • H0 : \(\rho = 0\),
  • H1 : \(\rho \neq 0\).

Conditions d’application : les \((Y_i, X_i)\) doivent être appariées et distribuées normalement.

Commençons par représenter les données :

plot_diff_weight_vs_compliance <- d %>% ggplot(aes(y=diff_weight, x=t_compliance, color = group)) +
    geom_point() +
    geom_smooth(method="lm", se=F, formula="y ~ x") +
    theme_bw() +
    xlab("t_compliance") + 
    ylab("différence de poids à 6 mois") +
    scale_color_manual(values=c(bleuclair, rose))
plot_diff_weight_vs_compliance

On peut à présent effectuer un test de corrélation en prenant tous les points ensemble :

cor.test(d$diff_weight, d$t_compliance)
## 
##  Pearson's product-moment correlation
## 
## data:  d$diff_weight and d$t_compliance
## t = 1.5369, df = 498, p-value = 0.125
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.01909837  0.15546122
## sample estimates:
##        cor 
## 0.06870729

Puis un test de corrélation en prenant uniquement le groupe control :

d_control <- d %>% filter(group == "control")
cor.test(d_control$diff_weight, d_control$t_compliance)
## 
##  Pearson's product-moment correlation
## 
## data:  d_control$diff_weight and d_control$t_compliance
## t = -1.2745, df = 248, p-value = 0.2037
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.20270677  0.04383722
## sample estimates:
##         cor 
## -0.08066852

Et enfin, le même test de corrélation en prenant uniquement le groupe treatment :

d_treatment <- d %>% filter(group == "treatment")
cor.test(d_treatment$diff_weight, d_treatment$t_compliance)
## 
##  Pearson's product-moment correlation
## 
## data:  d_treatment$diff_weight and d_treatment$t_compliance
## t = 2.923, df = 248, p-value = 0.003787
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.05977989 0.29977312
## sample estimates:
##       cor 
## 0.1824934

Que concluons-nous ???

Le modèle linéaire

Le modèle linéaire a le bon goût d’unifier tous ces tests dans un unique framework.

Il consiste à modéliser une variable de réponse \(Y_i\) chez l’individu \(i\) comme une fonction linéaire de nos variables explicatrices \(X_{i,j}\), à laquelle on rajoute un bruit Gaussien centré. Une fonction linéaire n’est rien de plus qu’une somme pondérée de nos variables \(X_{i,j}\), soit :

\[ Y_i = a_1 X_{i,1} + a_2 X_{i,2} + ... + a_J X_{i,J} + \epsilon_i, ~~~\text{ avec } \epsilon_i \sim \mathcal{N}(0, \sigma^2) \]

On suppose que tous les \((Y_i)\) sont indépendantes et suivent la même loi. Le but du jeu, pour le statisticien, consiste ensuite à déterminer les valeurs des coefficients \(a_j\) grâce aux observations.

Bonne nouvelle : la syntaxe de R pour fitter un modèle linéaire est assez rapide et intuitive.

Un simple intercept

Premier cas simple : on aimerait modéliser la différence de poids comme étant uniquement une moyenne + un petit bruit Gaussien, et calculer cette moyenne comme on l’avait fait lors du premier test de Student.

res1 <- lm(formula="diff_weight ~ 1", data=d)
summary(res1)
## 
## Call:
## lm(formula = "diff_weight ~ 1", data = d)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -7.8317 -1.7631 -0.0587  1.6520  7.6847 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   0.4183     0.1121   3.731 0.000212 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.507 on 499 degrees of freedom

On vérifie qu’on retombe bien sur la même chose que précédemment !

Un effet de variable discrète

Deuxième cas simple : on aimerait modéliser la différence de poids comme étant la somme d’un coefficient particulier chez les individus du groupe “control” et un autre coefficient chez les individus du groupe “treatment”. Soit exactement ce qu’on avait fait avec un test de Student comparant les deux moyennes.

Il se trouve que R, lorsqu’on lui donne à manger une variable à plusieurs modalités, va automatiquement la “scinder” en sous-variables indicatrices de chaque modalité. La syntaxe reste donc très simple :

# première version avec moyenne commune aux deux groupes
# et un petit ajout pour le groupe traitement
res2 <- lm(formula="diff_weight ~ group", data=d)
summary(res2)
## 
## Call:
## lm(formula = "diff_weight ~ group", data = d)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -7.639 -1.705 -0.110  1.602  7.878 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)  
## (Intercept)      0.2252     0.1582   1.424    0.155  
## grouptreatment   0.3861     0.2237   1.726    0.085 .
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.502 on 498 degrees of freedom
## Multiple R-squared:  0.005944,   Adjusted R-squared:  0.003947 
## F-statistic: 2.978 on 1 and 498 DF,  p-value: 0.08505
# deuxième version avec moyenne dans chaque groupe
res3 <- lm(formula="diff_weight ~ 0 + group", data=d)
summary(res3)
## 
## Call:
## lm(formula = "diff_weight ~ 0 + group", data = d)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -7.639 -1.705 -0.110  1.602  7.878 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## groupcontrol     0.2252     0.1582   1.424 0.155185    
## grouptreatment   0.6113     0.1582   3.864 0.000126 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.502 on 498 degrees of freedom
## Multiple R-squared:  0.03293,    Adjusted R-squared:  0.02904 
## F-statistic: 8.478 on 2 and 498 DF,  p-value: 0.0002394

On a donc exactement le même modèle sous-jacent, avec les même estimations de paramètres. Cependant, ici, les p-value indiquées correspondent à un test d’adéquation du paramètre à 0. Si on souhaite comparer les deux moyennes et retomber sur le même résultat qu’au début du document, il faut donc passer par la première version et regarder le test d’adéquation du paramètre à 0.

Troisième possibilité, notre différence de poids dépend d’une variable prenant 3 modalités ou plus (comme on avait proposé lors de la réalisation du test ANOVA ci-dessus). La même syntaxe fonctionne toujours aussi bien :

res4 <- lm(formula="diff_weight ~ 0 + age_group", data=d)
summary(res4)
## 
## Call:
## lm(formula = "diff_weight ~ 0 + age_group", data = d)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -7.8008 -1.7469 -0.0475  1.6451  7.6861 
## 
## Coefficients:
##                         Estimate Std. Error t value Pr(>|t|)  
## age_groupentre 40 et 60   0.4499     0.1862   2.417   0.0160 *
## age_groupmoins de 40      0.3873     0.1872   2.069   0.0390 *
## age_groupplus de 60       0.4169     0.2138   1.950   0.0517 .
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.511 on 497 degrees of freedom
## Multiple R-squared:  0.02726,    Adjusted R-squared:  0.02138 
## F-statistic: 4.642 on 3 and 497 DF,  p-value: 0.003269

L’output standard du modèle linéaire ne nous calcule des p-values que pour les tests d’adéquation de chaque paramètre à 0. Bien que l’ANOVA soit effectivement un test réalisé au sein du même framework de modèle linéaire, la p-value associée n’est récupérable, en pratique avec R, que via la fonction aov vue précédemment.

Un effet de variable continue

Dernier exemple similaire à ce qu’on avait fait précédemment : expliquer notre variable de réponse “différence de poids” par le “temps de compliance”.

res5 <- lm(formula="diff_weight ~ t_compliance", data=d)
summary(res5)
## 
## Call:
## lm(formula = "diff_weight ~ t_compliance", data = d)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -8.048 -1.772 -0.077  1.651  7.715 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)
## (Intercept)  0.251948   0.155707   1.618    0.106
## t_compliance 0.008513   0.005539   1.537    0.125
## 
## Residual standard error: 2.503 on 498 degrees of freedom
## Multiple R-squared:  0.004721,   Adjusted R-squared:  0.002722 
## F-statistic: 2.362 on 1 and 498 DF,  p-value: 0.125

Vers plus de complexification

Et si on souhaite un effet additif du groupe et du temps de compliance :

res6 <- lm(formula="diff_weight ~ 0 + group + t_compliance", data=d)
summary(res6)
## 
## Call:
## lm(formula = "diff_weight ~ 0 + group + t_compliance", data = d)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -7.8461 -1.6987 -0.1009  1.6003  7.8840 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)  
## groupcontrol   0.101159   0.183925   0.550   0.5826  
## grouptreatment 0.447466   0.201003   2.226   0.0265 *
## t_compliance   0.007368   0.005581   1.320   0.1874  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.5 on 497 degrees of freedom
## Multiple R-squared:  0.03631,    Adjusted R-squared:  0.03049 
## F-statistic: 6.242 on 3 and 497 DF,  p-value: 0.0003639

Ce n’est pas exactement ce qu’on avait fait tout à l’heure sur le graphe : on avait alors deux régressions linéaires, une dans chaque groupe. R possède encore une fois une syntaxe pratique pour ça :

res7 <- lm(formula="diff_weight ~ 0 + group * t_compliance", data=d)
summary(res7)
## 
## Call:
## lm(formula = "diff_weight ~ 0 + group * t_compliance", data = d)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -7.3082 -1.6814 -0.1062  1.5706  7.8679 
## 
## Coefficients:
##                              Estimate Std. Error t value Pr(>|t|)   
## groupcontrol                 0.422840   0.216077   1.957  0.05092 . 
## grouptreatment               0.169405   0.223178   0.759  0.44818   
## t_compliance                -0.011734   0.008814  -1.331  0.18370   
## grouptreatment:t_compliance  0.031605   0.011337   2.788  0.00551 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.483 on 496 degrees of freedom
## Multiple R-squared:  0.05117,    Adjusted R-squared:  0.04352 
## F-statistic: 6.688 on 4 and 496 DF,  p-value: 3.011e-05

On a introduit super facilement ce qu’on appelle des interactions entre nos variables. Le modèle linéaire, et la syntaxe de R, sont donc des outils de modélisation extrêmement puissants/versatiles. Ils demandent à bien réfléchir en amont à ce qu’on souhaite étudier pour éviter une trop grande multiplication de tests statistiques.

A vous de proposer : quel modèle linéaire vous semble le plus intéressant pour “diff_weight” ?

Conclusion

Nous avons vu aujourd’hui un certain nombre de méthodes permettant d’analyser des variables continues. Les points principaux à retenir sont les suivants :

  1. Pour comparer deux moyennes, ou une moyenne à une norme, le T test est la bonne option, à condition que les données soient en grand nombre, ou qu’elles soient distribuées normalement.
  2. Pour comparer plus que deux moyennes, une ANOVA fait l’affaire, sous des conditions identiques.
  3. Pour quantifier la corrélation entre deux variables continues distribuées normalement, un test de corrélation de Pearson peut être utilisé.
  4. Le modèle linéaire unifie tous les tests précédents dans un unique framework, où une variable de réponse continue \(Y\) est modélisée comme une combinaison linéaire de variables explicatives \(X\) (discrètes ou continues).

Notez pour finir qu’il existe d’autres types de régressions bien adaptées à des types de données différentes. Parmi les plus célèbres, vous rencontrerez très certainement la régression logistique, pour des données binaires. Vous rencontrerez éventuellement également la régression softmax, pour des données catégorielles, ou la régression de Poisson, pour des données de comptage d’événements.

LS0tCnRpdGxlOiAiU2Vzc2lvbiAyIDogU3RhdGlzdGlxdWVzIHByYXRpcXVlcyBzdXIgZG9ubsOpZXMgY29udGludWVzIgphdXRob3I6ICJNYXJjIE1hbmNlYXUiCmRhdGU6ICIyMDIzLTAyIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBoaWdobGlnaHQ6ICJ0YW5nbyIKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKLS0tCgpQb3VyIGxlcyB1dGlsaXNhdGV1cnMgZGUgUiwgb24gY29tbWVuY2UgcGFyIGNoYXJnZXIgdW4gY2VydGFpbiBub21icmUgZGUgbW9kdWxlcyBpbnTDqXJlc3NhbnRzLgoKYGBge3IgbWVzc2FnZT1GfQojIGxlIHBhY2thZ2UgbW9kZXJuZSBzdGFuZGFyZCBwb3VyIG1hbmlwdWxlciBzZXMgZG9ubsOpZXMKbGlicmFyeSh0aWR5dmVyc2UpCgojIHF1ZWxxdWVzIGNvdWxldXJzIG1hbnVlbGxlcwpibGV1Zm9uY2UgPC0gIiMzZDU0NjgiCmJsZXVjbGFpciA8LSAiIzViN2M5OCIKcm9zZSA8LSAiI2ZmNTU1NSIKYGBgCgojIFByw6lwYXJhdGlvbiBkZXMgZG9ubsOpZXMKCiMjIFNpbXVsYXRpb24KClBvdXIgc2UgZm9jYWxpc2VyIHN1ciBsZXMgc3RhdHMgZXQgdW5pcXVlbWVudCBsZXMgc3RhdHMsIApvbiB0cmF2YWlsbGUgc3VyIHVuIGpldSBkZSBkb25uw6llcyBpZMOpYWxpc8OpLgpFdCBwdWlzcXUnb24gYm9zc2UgZGFucyBsZXMgZXNzYWlzIGNsaW5pcXVlcywgb24gdmEgc2ltdWxlciBsZSBSQ1QgKFJhbmRvbWl6ZWQgQ29udHJvbGxlZCBUcmlhbCkKYXlhbnQgbGVzIGNhcmFjdMOpcmlzdGlxdWVzIHN1aXZhbnRlcyA6CgotIGwnw6l0dWRlIHBvc3PDqGRlIGRldXggYnJhczogYGNvbnRyb2xgIGV0IGB0cmVhdG1lbnRgLgotIG9uIGVucmVnaXN0cmUgZGVzIHZhcmlhYmxlcyBkw6ltb2dyYXBoaXF1ZXMgw6AgbCdpbmNsdXNpb24sIHR5cGUgYHNleGAsIGBhZ2VgLCBgYWdlX2dyb3VwYCwgYGhlaWdodGAsIGB3ZWlnaHRfdDBgLgotIHVuZSB2YXJpYWJsZSBkJ291dGNvbWUgbGnDqXMgw6AgbGEgcHJpc2UgZGUgcG9pZHMgOiBgZGlmZl93ZWlnaHRgLgotIHVuZSB2YXJpYWJsZSBkJ291dGNvbWUgw6AgdHJvaXMgbW9kYWxpdMOpcyA6IGBmZWVsc19zbGVlcHlgLgotIHVuZSB2YXJpYWJsZSBkZSB0ZW1wcyBkZSBzdWl2aSBkdSB0cmFpdGVtZW50IDogYHRfY29tcGxpYW5jZWAuCi0gdW5lIHZhcmlhYmxlIGQnb3V0Y29tZSBkZSBub21icmUgZGUgY2h1dGVzIHBlbmRhbnQgbGUgc3VpdmkgOiBgbl9mYWxsc2AuCgpQb3VyIGNldXggcXVlIMOnYSBpbnTDqXJlc3NlLCB2b2ljaSBsYSBsb2kgZGFucyBsYXF1ZWxsZSBsZXMgZG9ubsOpZXMgc29udCBzaW11bMOpZXMgOgoKYGBge3J9CiMgZGF0YSB0aGF0IGRvIG5vdCBkZXBlbmQgb24gdGhlIGdyb3VwCmQgPC0gZGF0YS5mcmFtZShpZCA9IHNlcSgxLDUwMCksCiAgICAgICAgICAgICAgICBncm91cCA9IGFzLmZhY3RvcihjKHJlcCgiY29udHJvbCIsMjUwKSwgcmVwKCJ0cmVhdG1lbnQiLDI1MCkpKSwKICAgICAgICAgICAgICAgIHNleCA9IGlmZWxzZShyYmlub20obj01MDAsIHNpemU9MSwgcHJvYj0wLjQpID09IDAsICJGIiwgIk0iKSwKICAgICAgICAgICAgICAgIGFnZSA9IHJvdW5kKHJ1bmlmKG49NTAwLCBtaW49MTksIG1heD03NykpLAogICAgICAgICAgICAgICAgaGVpZ2h0ID0gcm91bmQocm5vcm0obj01MDAsIG1lYW49MS43LCBzZD0wLjEpLCBkaWdpdHM9MiksCiAgICAgICAgICAgICAgICB3ZWlnaHRfdDAgPSByb3VuZChybm9ybShuPTUwMCwgbWVhbj03MCwgc2Q9MTApKSwKICAgICAgICAgICAgICAgIHRfY29tcGxpYW5jZSA9IHJvdW5kKHJleHAobj01MDAsIHJhdGU9MC4wNSkpICApCgojIHBvc3NpYmlsaXRpZXMgZm9yIHRoZSAiZmVlbHNfc2xlZXB5IiB2YXJpYWJsZQptb2RhbGl0ZXMgPC0gYygibW9ybmluZyIsICJhZnRlcm5vb24iLCAiYWx3YXlzIikKCiMgZGF0YSB0aGF0IGRvIGRlcGVuZCBvbiB0aGUgZ3JvdXAKZCA8LSBkICU+JSBtdXRhdGUoZGlmZl93ZWlnaHQgPSBjKHJub3JtKG49MjUwLCBtZWFuPTAsIHNkPTIuNSksIHJub3JtKG49MjUwLCBtZWFuPTAuMDI1KmQkdF9jb21wbGlhbmNlWzI1MTo1MDBdLCBzZD0yLjUpKSwKICAgICAgICAgICAgICAgICAgZmVlbHNfc2xlZXB5ID0gYyggc2FtcGxlKG1vZGFsaXRlcywgMjUwLCBwcm9iID0gYygwLjMsIDAuNCwgMC4zKSwgcmVwbGFjZSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGUobW9kYWxpdGVzLCAyNTAsIHByb2IgPSBjKDAuMiwgMC4zLCAwLjUpLCByZXBsYWNlID0gVFJVRSkgKSwKICAgICAgICAgICAgICAgICAgbl9mYWxscyA9IGMocnBvaXMobj0yNTAsIGxhbWJkYT02KSwgcnBvaXMobj0yNTAsIGxhbWJkYT01LjUpKSApCgpkIDwtIGQgJT4lIG11dGF0ZSh3ZWlnaHRfdGYgPSB3ZWlnaHRfdDAgKyBkaWZmX3dlaWdodCwKICAgICAgICAgICAgICAgICAgYWdlX2dyb3VwID0gYXMuZmFjdG9yKGlmZWxzZShhZ2UgPCA0MCwgIm1vaW5zIGRlIDQwIiwgaWZlbHNlKGFnZSA8IDYwLCAiZW50cmUgNDAgZXQgNjAiLCAicGx1cyBkZSA2MCIpKSkgKSAKYGBgCgoKIyMgRWNyaXR1cmUvTGVjdHVyZSBkdSBkYXRhc2V0CgpQb3VyIMOqdHJlIHPDu3IgZGUgdHJhdmFpbGxlciBzdXIgbGUgZmljaGllciB0ZWwgcXUnaWwgc2VyYSBsdSBwYXIgdG91dCBsZSBtb25kZSwgCm9uIGVucmVnaXN0cmUgbGUgZGF0YXNldCBzaW11bMOpIHB1aXMgb24gw6ljcmFzZSBsZSBkYXRhc2V0IGF2ZWMgbCdpbXBvcnQuCgpgYGB7cn0Kd3JpdGUuY3N2KGQsICJldHVkZV9maWN0aXZlLmNzdiIpCmQgPC0gcmVhZC5jc3YoImV0dWRlX2ZpY3RpdmUuY3N2IikKaGVhZChkKQpgYGAKCk5vdXMgc29tbWVzIHByw6p0cyDDoCB0cmF2YWlsbGVyIHN1ciBjZSBqZXUgZGUgZG9ubsOpZXMsIGV0LCBwb3VyIGNlIHF1aSBub3VzIGludMOpcmVzc2UgYXVqb3VyZCdodWksCsOgIGFuYWx5c2VyIGxlcyBkb25uw6llcyBjb250aW51ZXMgcXUnaWwgcmVuZmVybWUuCgojIFQgdGVzdCA6IGFkw6lxdWF0aW9uIMOgIHVuZSBub3JtZQoKT24gb2JzZXJ2ZSBkZXMgZGlmZsOpcmVuY2VzIGRlIHBvaWRzICQoWF9pKSQgY2hleiBub3MgaW5kaXZpZHVzIGVudHJlIGxlIGTDqWJ1dCBldCBsYSBmaW4gZGUgbCdleHDDqXJpZW5jZS4KRXN0LWNlIHF1ZSBsYSBkaWZmw6lyZW5jZSBkZSBwb2lkcyBtb3llbm5lIGVzdCBkaWZmw6lyZW50ZSBkZSB6w6lybyA/CgpPbiBzb3VoYWl0ZSByw6lhbGlzZXIgbGUgdGVzdCBkJ2h5cG90aMOoc2Ugc3VpdmFudCA6CgoqIEgwIDogbGEgbW95ZW5uZSBkZXMgJChYX2kpJCB2YXV0IHplcm8uCiogSDEgOiBsYSBtb3llbm5lIGRlcyAkKFhfaSkkIGVzdCBkaWZmw6lyZW50ZSBkZSB6w6lyby4KCkNvbmRpdGlvbnMgZCdhcHBsaWNhdGlvbiA6CgoqIGxlcyB2YXJpYWJsZXMgYWzDqWF0b2lyZXMgc29udCBpbmTDqXBlbmRhbnRlcyBldCBkZSBtw6ptZSBsb2kuCiogc29pdCBsZXMgdmFyaWFibGVzIGFsw6lhdG9pcmVzIHNvbnQgZGlzdHJpYnXDqWVzIG5vcm1hbGVtZW50LCBzb2l0IGwnw6ljaGFudGlsbG9uIGVzdCAiZ3JhbmQiLgoKT24gY29tbWVuY2UgdG91am91cnMgcGFyIHJlcHLDqXNlbnRlciBub3MgZG9ubsOpZXMuCgpgYGB7ciBmaWcud2lkdGg9MTB9CnBsb3RfZGlmZl93ZWlnaHQgPC0gZCAlPiUgZ2dwbG90KGFlcyh4PWRpZmZfd2VpZ2h0KSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWxwaGE9MC43LCBwb3NpdGlvbj0iaWRlbnRpdHkiLCBiaW5zPTIwKSArCiAgICB0aGVtZV9idygpICsKICAgIHlsYWIoImVmZmVjdGlmIikgKyAKICAgIHhsYWIoImRpZmbDqXJlbmNlIGRlIHBvaWRzIMOgIDYgbW9pcyIpCnBsb3RfZGlmZl93ZWlnaHQKYGBgCgpJY2kgbGVzIGNvbmRpdGlvbnMgZCdhcHBsaWNhdGlvbiBzZW1ibGVudCByZXNwZWN0w6llcy4KClVuIHLDqXN1bHRhdCB0aMOpb3JpcXVlIGNlcnRpZmllIHF1J3VuZSBzdGF0aXN0aXF1ZSBkZSB0ZXN0IGNhbGN1bMOpZSDDoCBwYXJ0aXIgZGUgbCdlZmZlY3RpZiwgZGUgbGEgbW95ZW5uZSBlbXBpcmlxdWUgZXQgZGUgbGEgdmFyaWFuY2UgZW1waXJpcXVlLCBlc3QgZGlzdHJpYnXDqWUgc3VpdmFudCB1bmUgbG9pIGRlIFN0dWRlbnQgYXZlYyB1biBjZXJ0YWluIG5vbWJyZSBkZSAiZGVncsOpcyBkZSBsaWJlcnTDqSIuCgpJbCBzJ2FnaXQgZG9uYyBkZSBjYWxjdWxlciBsYSB2YWxldXIgb2JzZXJ2w6llIGRlIGNldHRlIHN0YXRpc3RpcXVlIGRlIHRlc3Qgc3VyIG5vdHJlIMOpY2hhbnRpbGxvbiwgZXQgZGUgbGEgY29tcGFyZXIgYXV4IHF1YW50aWxlcyBkZSBsYSBsb2kgdGjDqW9yaXF1ZS4KCkxhIGZvbmN0aW9uIHBvdXIgZmFpcmUgw6dhIGVuIFIgZXN0IGxhIHN1aXZhbnRlIDoKCmBgYHtyfQp0LnRlc3QoeD1kJGRpZmZfd2VpZ2h0KQpgYGAKCkwnb3V0cHV0IGRlIGxhIGZvbmN0aW9uIG5vdXMgcmFwcGVsbGUgOgoKMS4gZW4gcHJlbWnDqHJlIGxpZ25lIGwnw6ljaGFudGlsbG9uIHN1ciBsZXF1ZWwgb24gYSBhcHBsaXF1w6kgbGUgdGVzdCwKMi4gZW4gc2Vjb25kZSBsaWduZSBvbiByw6ljdXDDqHJlIGxhIHZhbGV1ciBvYnNlcnbDqWUgZGUgbGEgc3RhdGlzdGlxdWUgZGUgdGVzdCwgYWluc2kgcXVlIGxlIG5vbWJyZSBkZSAiZGVncsOpcyBkZSBsaWJlcnTDqSIgZGUgbGEgbG9pIHRow6lvcmlxdWUuCjMuIGVuIGZpbiBkZSBzZWNvbmRlIGxpZ25lLCBzdXBlciBpbXBvcnRhbnRlIDogbGEgcC12YWx1ZSAhISEgUHJvYmFiaWxpdMOpIHF1ZSBsYSB2YWxldXIgZGUgbGEgc3RhdGlzdGlxdWUgc29pdCBhdSBtb2lucyBhdXNzaSBleHRyw6ptZSBxdSdvYnNlcnbDqWUuCjQuIHRyb2lzacOobWUgbGlnbmUgOiB1biByYXBwZWwgZGUgbCdoeXBvdGjDqHNlIGFsdGVybmF0aXZlIGR1IHRlc3QgKG1vZGlmaWFibGUgYXZlYyBsJ2FyZ3VtZW50IGBhbHRlcm5hdGl2ZWApLgo1LiB1biBpbnRlcnZhbGxlIGRlIGNvbmZpYW5jZSDDoCA5NSUgZGUgbGEgbW95ZW5uZSAoYm9ybmVzIG1vZGlmaWFibGVzIHZpYSBsJ2FyZ3VtZW50IGBjb25mLmxldmVsYCkuCjYuIHRvdXQgw6AgbGEgZmluLCBsJ2VzdGltYXRpb24gcG9uY3R1ZWxsZSBkZSBsYSBtb3llbm5lLgoKPiBRdWUgY29uY2x1b25zIG5vdXMgPz8/CgoKIyBUIHRlc3QgOiBjb21wYXJhaXNvbiBkZSAyIG1veWVubmVzCgpPbiBvYnNlcnZlIGRlcyB2YWxldXJzICQoWF97aSxqfSkkIGRlIG5vcyBpbmRpdmlkdXMgJGkkIGRhbnMgZGV1eCBncm91cGVzICRqPTEkIGV0ICRqPTIkLiAKSWNpIGxlcyBkZXV4IGdyb3VwZXMgY29ycmVzcG9uZGVudCBhdSBjb250cm9sIHZzLiB0cmVhdG1lbnQsIApldCBsZXMgb2JzZXJ2YXRpb25zIGNvcnJlc3BvbmRlbnQgw6AgdW5lIGRpZmbDqXJlbmNlIGRlIHBvaWRzIGVudHJlIGxlIGTDqWJ1dCBldCBsYSBmaW4gZGUgbCdleHDDqXJpZW5jZS4KCkVzdC1jZSBxdWUgbGEgZGlmZsOpcmVuY2UgZGUgcG9pZHMgbW95ZW5uZSBlc3QgZGlmZsOpcmVudGUgZGFucyBsZXMgZGV1eCBncm91cGVzID8KCk9uIHNvdWhhaXRlIHLDqWFsaXNlciBsZSB0ZXN0IGQnaHlwb3Row6hzZSBzdWl2YW50IDoKCiogSDAgOiBsYSBtb3llbm5lIGRlcyAkKFhfe2ksMX0pJCBlc3QgaWRlbnRpcXVlIMOgIGNlbGxlIGRlcyAkKFhfe2ksMn0pJC4KKiBIMSA6IGxlcyBkZXV4IG1veWVubmVzIHNvbnQgZGlmZsOpcmVudGVzLgoKQ29uZGl0aW9ucyBkJ2FwcGxpY2F0aW9uIDoKCiogbGVzIHZhcmlhYmxlcyBhbMOpYXRvaXJlcyBzb250IGluZMOpcGVuZGFudGVzIGV0IGRlIG3Dqm1lIGxvaS4KKiBzb2l0IGxlcyB2YXJpYWJsZXMgYWzDqWF0b2lyZXMgc29udCBkaXN0cmlidcOpZXMgbm9ybWFsZW1lbnQsIHNvaXQgbCfDqWNoYW50aWxsb24gZXN0ICJncmFuZCIuCgpPbiBjb21tZW5jZSB0b3Vqb3VycyBwYXIgcmVwcsOpc2VudGVyIG5vcyBkb25uw6llcyBldCBxdWFudGl0w6lzIGQnaW50w6lyw6p0LgoKYGBge3IgZmlnLndpZHRoPTEwfQptIDwtIGQgJT4lIGdyb3VwX2J5KGdyb3VwKSAlPiUgc3VtbWFyaXNlKG1lYW49bWVhbihkaWZmX3dlaWdodCkpICU+JSBzZWxlY3QobWVhbikKCnBsb3RfZGlmZl93ZWlnaHRfYnlfZ3JvdXAgPC0gZCAlPiUgZ2dwbG90KGFlcyh4PWRpZmZfd2VpZ2h0LCBmaWxsPWdyb3VwKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiLCBiaW5zPTIwKSArCiAgICB0aGVtZV9idygpICsKICAgIHlsYWIoImVmZmVjdGlmIikgKyAKICAgIHhsYWIoImRpZmbDqXJlbmNlIGRlIHBvaWRzIMOgIDYgbW9pcyIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKGJsZXVjbGFpciwgcm9zZSkpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMobSRtZWFuWzFdLCBtJG1lYW5bMl0pLCBjb2xvcj1jKGJsZXVjbGFpciwgcm9zZSkpCnBsb3RfZGlmZl93ZWlnaHRfYnlfZ3JvdXAKYGBgCgpJY2kgbGVzIGNvbmRpdGlvbnMgZCdhcHBsaWNhdGlvbiBzZW1ibGVudCByZXNwZWN0w6llcy4KClVuIHLDqXN1bHRhdCB0aMOpb3JpcXVlIGNlcnRpZmllIHF1J3VuZSBzdGF0aXN0aXF1ZSBkZSB0ZXN0IGNhbGN1bMOpZSDDoCBwYXJ0aXIgZGUgbCdlZmZlY3RpZiwgCmRlcyBtb3llbm5lcyBlbXBpcmlxdWVzIGRlcyBkZXV4IGdyb3VwZXMgZXQgZGUgbGEgdmFyaWFuY2UgZW1waXJpcXVlIGRlcyBkZXV4IGdyb3VwZXMsIAplc3QgZGlzdHJpYnXDqWUgc3VpdmFudCB1bmUgbG9pIGRlIFN0dWRlbnQgYXZlYyB1biBjZXJ0YWluIG5vbWJyZSBkZSAiZGVncsOpcyBkZSBsaWJlcnTDqSIuCgpJbCBzJ2FnaXQgZG9uYyBkZSBjYWxjdWxlciBsYSB2YWxldXIgb2JzZXJ2w6llIGRlIGNldHRlIHN0YXRpc3RpcXVlIGRlIHRlc3Qgc3VyIG5vdHJlIMOpY2hhbnRpbGxvbiwgCmV0IGRlIGxhIGNvbXBhcmVyIGF1eCBxdWFudGlsZXMgZGUgbGEgbG9pIHRow6lvcmlxdWUuCgpMYSBmb25jdGlvbiBwb3VyIGZhaXJlIMOnYSBlbiBSIGVzdCBsYSBzdWl2YW50ZSA6CgpgYGB7cn0KdC50ZXN0KHg9ZCRkaWZmX3dlaWdodFtkJGdyb3VwID09ICJjb250cm9sIl0sIHk9ZCRkaWZmX3dlaWdodFtkJGdyb3VwID09ICJ0cmVhdG1lbnQiXSkKYGBgCgpVbmUgc3ludGF4ZSBhbHRlcm5hdGl2ZSBwcmF0aXF1ZSBleGlzdGUgw6lnYWxlbWVudCA6CgpgYGB7cn0KdC50ZXN0KGRpZmZfd2VpZ2h0IH4gZ3JvdXAsIGRhdGE9ZCkKYGBgCgpDYSBzZSBsaXQgZGUgZmHDp29uIHRyw6hzIHNpbWlsYWlyZSDDoCBjZSBxdSdvbiBhdmFpdCBwcsOpY8OpZGVtbWVudCwgbGVzIGRpZmbDqXJlbmNlcyDDqXRhbnQgOgoKMS4gTCdJQyDDoCA5NSUgY29ycmVzcG9uZCDDoCBjZWx1aSBkZSBsYSBkaWZmw6lyZW5jZSBkZXMgZGV1eCBtb3llbm5lcy4KMi4gTGVzIGVzdGltYXRpb25zIHBvbmN0dWVsbGVzIGNvcnJlc3BvbmRlbnQgYXV4IGRldXggbW95ZW5uZXMuCgo+IFF1ZSBjb25jbHVvbnMtbm91cyA/Pz8KCgojIFQgdGVzdCA6IG9wdGlvbiAiYXBwYXJpw6kiCgpKZSByYWpvdXRlIHVuZSBzZWN0aW9uIMOgIHByb3BvcyBkZSBjZXR0ZSBvcHRpb24gZCcqYXBwYXJpZW1lbnQqLApxdWkgYmllbiBzb3V2ZW50IGVtYnJvdWlsbGUgcGx1cyBxdSdlbGxlIG4nYWlkZS4KCk9uIHBvc3PDqGRlIGRlcyBvYnNlcnZhdGlvbnMgYXBwYXJpw6llcywgCnR5cGlxdWVtZW50IGxhIG3Dqm1lIHF1YW50aXTDqSBtZXN1csOpZSAiYXZhbnQiICQoWF9pKSQgZXQgImFwcsOocyIgJChZX2kpJCBpbnRlcnZlbnRpb24gc3VyIGNoYXF1ZSBpbmRpdmlkdS4KUGFyIGV4ZW1wbGUsIGxlIHBvaWRzIGR1IHBhdGllbnQgYXZhbnQgaW50ZXJ2ZW50aW9uIGV0IGFwcsOocyBpbnRlcnZlbnRpb24uClkgYSB0J2lsIHVuIGNoYW5nZW1lbnQgZGUgcG9pZHMgZHUgcGF0aWVudCBhdmFudCBldCBhcHLDqHMgaW50ZXJ2ZW50aW9uID8KCkNlbGEgcmV2aWVudCDDoCB0ZXN0ZXIgOgoqIEgwIDogbGEgbW95ZW5uZSBkZXMgJChZX2kgLSBYX2kpJCB2YXV0IHplcm8uCiogSDEgOiBsYSBtb3llbm5lIGRlcyAkKFlfaSAtIFhfaSkkIGVzdCBkaWZmw6lyZW50ZSBkZSB6w6lyby4KCkMnZXN0IGRvbmMgdG90YWxlbWVudCBzaW1pbGFpcmUgYXUgcHJlbWllciB0ZXN0IHF1J29uIGF2YWl0IGFwcGxpcXXDqSBkaXJlY3RlbWVudCBzdXIgbGEgZGlmZsOpcmVuY2UgZGUgcG9pZHMuCgpMYSBmYcOnb24gZGUgZmFpcmUgw6dhIGVuIFIgZXN0IGxhIHN1aXZhbnRlIChldCBvbiB2w6lyaWZpZSBxdWUgw6dhIGRvbm5lIGJpZW4gbGUgbcOqbWUgcsOpc3VsdGF0KSA6CgpgYGB7cn0KdC50ZXN0KHg9ZCR3ZWlnaHRfdGYsIHk9ZCR3ZWlnaHRfdDAsIHBhaXJlZD1UUlVFKQpgYGAKCmBgYHtyfQp0LnRlc3QoZCRkaWZmX3dlaWdodCkKYGBgCgoKIyBBTk9WQSA6IGNvbXBhcmFpc29uIGRlIG1veWVubmVzCgpPbiBzb3VoYWl0ZSBwbHVzIHByw6ljaXPDqW1lbnQgdGVzdGVyIGljaSBsYSBkaWZmw6lyZW5jZSBkJ2F1IG1vaW5zIHVuZSBtb3llbm5lIHBhcm1pIHBsdXNpZXVycyBncm91cGVzLgoKT24gZGlzcG9zZSBjZXR0ZSBmb2lzIGQnb2JzZXJ2YXRpb25zIGRlIGxhIG3Dqm1lIHF1YW50aXTDqSAkKFhfe2ksan0pIGNoZXogbCdpbmRpdmlkdSAkaSQgZGFucyBsZSBncm91cGUgJGokLCAKZGFucyB1biBub21icmUgZGUgZ3JvdXBlcyBwb3RlbnRpZWxsZW1lbnQgc3Vww6lyaWV1ciDDoCAyLgpFc3QtY2UgcXVlIGxhIG1veWVubmUgZCdhdSBtb2lucyB1biBncm91cGUgZGlmZsOocmUgZGVzIGF1dHJlcyBncm91cGVzID8KClBhciBleGVtcGxlLCBpY2ksIGxhIGRpZmbDqXJlbmNlIGRlIHBvaWRzIG1veWVubmUgY2hhbmdlLXQtZWxsZSBkYW5zIGxlcyB0cm9pcyBjbGFzc2VzIGQnw6JnZSA/ICg8IDQwLCA0MC02MCwgPiA2MCkKCk9uIHRlc3RlIGZvcm1lbGxlbWVudCA6CgoqIEgwIDogbGVzIG1veWVubmVzIGRlcyAkKFhfe2ksMX0pJCwgZGVzICQoWF97aSwyfSkkIGV0IGRlcyAkKFhfe2ksM30pJCBzb250IGlkZW50aXF1ZXMuCiogSDEgOiBhdSBtb2lucyB1bmUgZGVzIG1veWVubmVzIGRpZmbDqHJlLgoKQ29uZGl0aW9ucyBkJ2FwcGxpY2F0aW9uIDoKCiogbGVzIHZhcmlhYmxlcyBhbMOpYXRvaXJlcyBzb250IGluZMOpcGVuZGFudGVzIHN1aXZlbnQgdW5lIGxvaSBub3JtYWxlLgoqIGlsIHkgYSDDqWdhbGl0w6kgZGUgdmFyaWFuY2UgZW50cmUgbGVzIGRpZmbDqXJlbnRzIGdyb3VwZXMuCgpVbmUgZm9pcyBuJ2VzdCBwYXMgY291dMO7bWUsIGNvbW1lbsOnb25zIHBhciByZXByw6lzZW50ZXIgbGVzIGRvbm7DqWVzIDoKCmBgYHtyIGZpZy53aWR0aD0xMH0KbSA8LSBkICU+JSBncm91cF9ieShhZ2VfZ3JvdXApICU+JSBzdW1tYXJpc2UobWVhbj1tZWFuKGRpZmZfd2VpZ2h0KSkgJT4lIHNlbGVjdChtZWFuKQoKcGxvdF9kaWZmX3dlaWdodF9ieV9hZ2VfZ3JvdXAgPC0gZCAlPiUgZ2dwbG90KGFlcyh4PWRpZmZfd2VpZ2h0LCBmaWxsPWFzLmZhY3RvcihhZ2VfZ3JvdXApKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiLCBiaW5zPTIwKSArCiAgICB0aGVtZV9idygpICsKICAgIHlsYWIoImVmZmVjdGlmIikgKyAKICAgIHhsYWIoImRpZmbDqXJlbmNlIGRlIHBvaWRzIMOgIDYgbW9pcyIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKGJsZXVmb25jZSwgYmxldWNsYWlyLCByb3NlKSkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhtJG1lYW5bMV0sIG0kbWVhblsyXSwgbSRtZWFuWzNdKSwgY29sb3I9YyhibGV1Zm9uY2UsIGJsZXVjbGFpciwgcm9zZSkpCnBsb3RfZGlmZl93ZWlnaHRfYnlfYWdlX2dyb3VwCmBgYAoKTGVzIGNvbmRpdGlvbnMgZCdhcHBsaWNhdGlvbiBzZW1ibGVudCByZXNwZWN0w6llcyAoYXUgbW9pbnMgZ3JhcGhpcXVlbWVudCkuCgpVbiByw6lzdWx0YXQgdGjDqW9yaXF1ZSBjZXJ0aWZpZSBjZXR0ZSBmb2lzIHF1J3VuZSBjZXJ0YWluZSBzdGF0aXN0aXF1ZSBkZSB0ZXN0LCAKY2FsY3Vsw6llIMOgIHBhcnRpciBkZXMgZWZmZWN0aWZzIGRlcyBkaWZmw6lyZW50cyBncm91cGVzIGV0IGRlcyB2YXJpYW5jZXMgaW50cmEtZ3JvdXBlcyAvIGludGVyLWdyb3VwZXMsIAplc3QgZGlzdHJpYnXDqWUgc3VpdmFudCB1bmUgbG9pIGRlIEZpc2hlciBhdmVjIHVuIGNlcnRhaW4gbm9tYnJlIGRlICJkZWdyw6lzIGRlIGxpYmVydMOpIi4KCklsIHMnYWdpdCBkb25jIGRlIGNhbGN1bGVyIGxhIHZhbGV1ciBvYnNlcnbDqWUgZGUgY2V0dGUgc3RhdGlzdGlxdWUgZGUgdGVzdCBzdXIgbm90cmUgw6ljaGFudGlsbG9uLCAKZXQgZGUgbGEgY29tcGFyZXIgYXV4IHF1YW50aWxlcyBkZSBsYSBsb2kgdGjDqW9yaXF1ZS4KCkxhIGZvbmN0aW9uIHBvdXIgZmFpcmUgw6dhIGVuIFIgZXN0IGxhIHN1aXZhbnRlIDoKCmBgYHtyfQpyZXMgPC0gYW92KGRpZmZfd2VpZ2h0IH4gYWdlX2dyb3VwLCBkYXRhPWQpCnN1bW1hcnkocmVzKQpgYGAKCkxlcyBkZXV4IHBvaW50cyBsZXMgcGx1cyBpbnTDqXJlc3NhbnRzIGRlIGwnb3V0cHV0IHNvbnQgOgoKMS4gbGEgIkYgdmFsdWUiIHF1aSBlc3QgbGEgdmFsZXVyIGRlIGxhIHN0YXRpc3RpcXVlIGRlIHRlc3QsCjIuIGV0IGxhIHAtdmFsdWUgOiBsYSBwcm9iYWJpbGl0w6kgc291cyBIMCBkJ29idGVuaXIgdW5lIHN0YXRpc3RpcXVlIGRlIHRlc3QgYXUgbW9pbnMgYXVzc2kgZXh0csOqbWUgcXUnb2JzZXJ2w6llLgoKPiBRdWUgY29uY2x1b25zLW5vdXMgPz8/CgpGaW5pc3NvbnMgcGFyIHVuIHBldGl0IHRlc3QsIHBlcm1ldHRhbnQgZGUgc2UgY29udmFpbmNyZSBxdSd1bmUgQU5PVkEgYXZlYyBkZXV4IGdyb3VwZXMgY29ycmVzcG9uZCDDoCB1biBUIHRlc3QuCgpgYGB7cn0KcmVzIDwtIGFvdihkaWZmX3dlaWdodCB+IGdyb3VwLCBkYXRhPWQpCnN1bW1hcnkocmVzKQp0LnRlc3QoZGlmZl93ZWlnaHQgfiBncm91cCwgZGF0YT1kKQpgYGAKCgojIFRlc3QgZGUgY29ycsOpbGF0aW9uIGRlIFBlYXJzb24KCk9uIGRpc3Bvc2UgY2V0dGUgZm9pcyBkJ29ic2VydmF0aW9ucyBkJ3VuZSB2YXJpYWJsZXMgJChZX2kpJCBkJ2ludMOpcsOqdCwgCnBhciBleGVtcGxlIGVuY29yZSB1bmUgZm9pcyBsYSBkaWZmw6lyZW5jZSBkZSBwb2lkcy4gCkV0IGQndW5lIGF1dHJlIHZhcmlhYmxlIHF1YW50aXRhdGl2ZSAkKFhfaSkkIGNvcnJlc3BvbmRhbnQgw6AgcXVlbHF1ZSBjaG9zZSBxdWkgcG91cnJhaXQgw6p0cmUgY29ycsOpbMOpIMOgIG5vcyAkKFlfaSkkLCAKcGFyIGV4ZW1wbGUgaWNpIGxlIHRlbXBzIHBlbmRhbnQgbGVxdWVsIGxlIHBhdGllbnQgYSBzdHJpY3RlbWVudCBvYnNlcnbDqSBzb24gdHJhaXRlbWVudCAoY29tcGxpYW5jZSkuCgpFc3QtY2UgcXVlIGxlIGdhaW4gZGUgcG9pZHMgZMOpcGVuZCBkZSBsYSBjb21wbGlhbmNlLCBkYW5zIGxlIGdyb3VwZSB0cmFpdMOpID8KCkxlIGNvZWZmaWNpZW50IGRlIGNvcnLDqWxhdGlvbiAkXHJobyQgY29ycmVzcG9uZCDDoCBsYSBjb3ZhcmlhbmNlIGRlcyBkZXV4IHF1YW50aXTDqXMgbm9ybWFsaXPDqWUgcGFyIGxlcyDDqWNhcnRzLXR5cGVzIGRlcyBkZXV4IHF1YW50aXTDqXMuIApJbCBlc3QgY29tcHJpcyBlbnRyZSAtMSBldCAxIGV0IHJlbnNlaWduZSBzdXIgbGEgZGlzcGVyc2lvbiBkZXMgZG9ubsOpZXMgZW4gMkQuIApFbiBjYXMgZGUgY29ycsOpbGF0aW9uIGxpbsOpYWlyZSBwYXJmYWl0ZSwgaWwgdmF1dCAxIG91IC0xLgoKRm9ybWVsbGVtZW50IG9uIHBldXQgdGVzdGVyLCBhdmVjIGxlIHRlc3QgZGUgQnJhdmFpcy1QZWFyc29uIDoKCiogSDAgOiAkXHJobyA9IDAkLAoqIEgxIDogJFxyaG8gXG5lcSAwJC4KCkNvbmRpdGlvbnMgZCdhcHBsaWNhdGlvbiA6IGxlcyAkKFlfaSwgWF9pKSQgZG9pdmVudCDDqnRyZSBhcHBhcmnDqWVzIGV0IGRpc3RyaWJ1w6llcyBub3JtYWxlbWVudC4KCkNvbW1lbsOnb25zIHBhciByZXByw6lzZW50ZXIgbGVzIGRvbm7DqWVzIDoKCmBgYHtyIGZpZy53aWR0aD0xMH0KcGxvdF9kaWZmX3dlaWdodF92c19jb21wbGlhbmNlIDwtIGQgJT4lIGdncGxvdChhZXMoeT1kaWZmX3dlaWdodCwgeD10X2NvbXBsaWFuY2UsIGNvbG9yID0gZ3JvdXApKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUYsIGZvcm11bGE9InkgfiB4IikgKwogICAgdGhlbWVfYncoKSArCiAgICB4bGFiKCJ0X2NvbXBsaWFuY2UiKSArIAogICAgeWxhYigiZGlmZsOpcmVuY2UgZGUgcG9pZHMgw6AgNiBtb2lzIikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKGJsZXVjbGFpciwgcm9zZSkpCnBsb3RfZGlmZl93ZWlnaHRfdnNfY29tcGxpYW5jZQpgYGAKCk9uIHBldXQgw6AgcHLDqXNlbnQgZWZmZWN0dWVyIHVuIHRlc3QgZGUgY29ycsOpbGF0aW9uIGVuIHByZW5hbnQgdG91cyBsZXMgcG9pbnRzIGVuc2VtYmxlIDoKCmBgYHtyfQpjb3IudGVzdChkJGRpZmZfd2VpZ2h0LCBkJHRfY29tcGxpYW5jZSkKYGBgCgpQdWlzIHVuIHRlc3QgZGUgY29ycsOpbGF0aW9uIGVuIHByZW5hbnQgdW5pcXVlbWVudCBsZSBncm91cGUgYGNvbnRyb2xgIDoKCmBgYHtyfQpkX2NvbnRyb2wgPC0gZCAlPiUgZmlsdGVyKGdyb3VwID09ICJjb250cm9sIikKY29yLnRlc3QoZF9jb250cm9sJGRpZmZfd2VpZ2h0LCBkX2NvbnRyb2wkdF9jb21wbGlhbmNlKQpgYGAKCkV0IGVuZmluLCBsZSBtw6ptZSB0ZXN0IGRlIGNvcnLDqWxhdGlvbiBlbiBwcmVuYW50IHVuaXF1ZW1lbnQgbGUgZ3JvdXBlIGB0cmVhdG1lbnRgIDoKCmBgYHtyfQpkX3RyZWF0bWVudCA8LSBkICU+JSBmaWx0ZXIoZ3JvdXAgPT0gInRyZWF0bWVudCIpCmNvci50ZXN0KGRfdHJlYXRtZW50JGRpZmZfd2VpZ2h0LCBkX3RyZWF0bWVudCR0X2NvbXBsaWFuY2UpCmBgYAoKPiBRdWUgY29uY2x1b25zLW5vdXMgPz8/CgoKIyBMZSBtb2TDqGxlIGxpbsOpYWlyZQoKTGUgbW9kw6hsZSBsaW7DqWFpcmUgYSBsZSBib24gZ2/Du3QgZCd1bmlmaWVyIHRvdXMgY2VzIHRlc3RzIGRhbnMgdW4gdW5pcXVlIGZyYW1ld29yay4KCklsIGNvbnNpc3RlIMOgIG1vZMOpbGlzZXIgdW5lICp2YXJpYWJsZSBkZSByw6lwb25zZSogJFlfaSQgY2hleiBsJ2luZGl2aWR1ICRpJCAKY29tbWUgdW5lIGZvbmN0aW9uIGxpbsOpYWlyZSBkZSBub3MgKnZhcmlhYmxlcyBleHBsaWNhdHJpY2VzKiAkWF97aSxqfSQsIArDoCBsYXF1ZWxsZSBvbiByYWpvdXRlIHVuICpicnVpdCogR2F1c3NpZW4gY2VudHLDqS4gClVuZSBmb25jdGlvbiBsaW7DqWFpcmUgbidlc3QgcmllbiBkZSBwbHVzIHF1J3VuZSBzb21tZSBwb25kw6lyw6llIGRlIG5vcyB2YXJpYWJsZXMgJFhfe2ksan0kLCBzb2l0IDoKCiQkIApZX2kgPSBhXzEgWF97aSwxfSArIGFfMiBYX3tpLDJ9ICsgLi4uICsgYV9KIFhfe2ksSn0gKyBcZXBzaWxvbl9pLCB+fn5cdGV4dHsgYXZlYyB9IFxlcHNpbG9uX2kgXHNpbSBcbWF0aGNhbHtOfSgwLCBcc2lnbWFeMikKJCQKCk9uIHN1cHBvc2UgcXVlIHRvdXMgbGVzICQoWV9pKSQgc29udCBpbmTDqXBlbmRhbnRlcyBldCBzdWl2ZW50IGxhIG3Dqm1lIGxvaS4KTGUgYnV0IGR1IGpldSwgcG91ciBsZSBzdGF0aXN0aWNpZW4sIGNvbnNpc3RlIGVuc3VpdGUgw6AgZMOpdGVybWluZXIgbGVzIHZhbGV1cnMgZGVzIGNvZWZmaWNpZW50cyAkYV9qJCBncsOiY2UgYXV4IG9ic2VydmF0aW9ucy4KCkJvbm5lIG5vdXZlbGxlIDogbGEgc3ludGF4ZSBkZSBSIHBvdXIgZml0dGVyIHVuIG1vZMOobGUgbGluw6lhaXJlIGVzdCBhc3NleiByYXBpZGUgZXQgaW50dWl0aXZlLgoKIyMgVW4gc2ltcGxlIGludGVyY2VwdAoKUHJlbWllciBjYXMgc2ltcGxlIDogb24gYWltZXJhaXQgbW9kw6lsaXNlciBsYSBkaWZmw6lyZW5jZSBkZSBwb2lkcyBjb21tZSDDqXRhbnQgdW5pcXVlbWVudCB1bmUgbW95ZW5uZSArIHVuIHBldGl0IGJydWl0IEdhdXNzaWVuLCAKZXQgY2FsY3VsZXIgY2V0dGUgbW95ZW5uZSBjb21tZSBvbiBsJ2F2YWl0IGZhaXQgbG9ycyBkdSBwcmVtaWVyIHRlc3QgZGUgU3R1ZGVudC4KCmBgYHtyfQpyZXMxIDwtIGxtKGZvcm11bGE9ImRpZmZfd2VpZ2h0IH4gMSIsIGRhdGE9ZCkKc3VtbWFyeShyZXMxKQpgYGAKCk9uIHbDqXJpZmllIHF1J29uIHJldG9tYmUgYmllbiBzdXIgbGEgbcOqbWUgY2hvc2UgcXVlIHByw6ljw6lkZW1tZW50ICEKCiMjIFVuIGVmZmV0IGRlIHZhcmlhYmxlIGRpc2Nyw6h0ZQoKRGV1eGnDqG1lIGNhcyBzaW1wbGUgOiBvbiBhaW1lcmFpdCBtb2TDqWxpc2VyIGxhIGRpZmbDqXJlbmNlIGRlIHBvaWRzIGNvbW1lIMOpdGFudCAKbGEgc29tbWUgZCd1biBjb2VmZmljaWVudCBwYXJ0aWN1bGllciBjaGV6IGxlcyBpbmRpdmlkdXMgZHUgZ3JvdXBlICJjb250cm9sIiAKZXQgdW4gYXV0cmUgY29lZmZpY2llbnQgY2hleiBsZXMgaW5kaXZpZHVzIGR1IGdyb3VwZSAidHJlYXRtZW50Ii4gClNvaXQgZXhhY3RlbWVudCBjZSBxdSdvbiBhdmFpdCBmYWl0IGF2ZWMgdW4gdGVzdCBkZSBTdHVkZW50IGNvbXBhcmFudCBsZXMgZGV1eCBtb3llbm5lcy4KCklsIHNlIHRyb3V2ZSBxdWUgUiwgbG9yc3F1J29uIGx1aSBkb25uZSDDoCBtYW5nZXIgdW5lIHZhcmlhYmxlIMOgIHBsdXNpZXVycyBtb2RhbGl0w6lzLCAKdmEgYXV0b21hdGlxdWVtZW50IGxhICJzY2luZGVyIiBlbiBzb3VzLXZhcmlhYmxlcyBpbmRpY2F0cmljZXMgZGUgY2hhcXVlIG1vZGFsaXTDqS4gCkxhIHN5bnRheGUgcmVzdGUgZG9uYyB0csOocyBzaW1wbGUgOgoKYGBge3J9CiMgcHJlbWnDqHJlIHZlcnNpb24gYXZlYyBtb3llbm5lIGNvbW11bmUgYXV4IGRldXggZ3JvdXBlcwojIGV0IHVuIHBldGl0IGFqb3V0IHBvdXIgbGUgZ3JvdXBlIHRyYWl0ZW1lbnQKcmVzMiA8LSBsbShmb3JtdWxhPSJkaWZmX3dlaWdodCB+IGdyb3VwIiwgZGF0YT1kKQpzdW1tYXJ5KHJlczIpCmBgYAoKYGBge3J9CiMgZGV1eGnDqG1lIHZlcnNpb24gYXZlYyBtb3llbm5lIGRhbnMgY2hhcXVlIGdyb3VwZQpyZXMzIDwtIGxtKGZvcm11bGE9ImRpZmZfd2VpZ2h0IH4gMCArIGdyb3VwIiwgZGF0YT1kKQpzdW1tYXJ5KHJlczMpCmBgYAoKT24gYSBkb25jIGV4YWN0ZW1lbnQgbGUgbcOqbWUgbW9kw6hsZSBzb3VzLWphY2VudCwgYXZlYyBsZXMgbcOqbWUgZXN0aW1hdGlvbnMgZGUgcGFyYW3DqHRyZXMuIApDZXBlbmRhbnQsIGljaSwgbGVzIHAtdmFsdWUgaW5kaXF1w6llcyBjb3JyZXNwb25kZW50IMOgIHVuIHRlc3QgZCdhZMOpcXVhdGlvbiBkdSBwYXJhbcOodHJlIMOgIDAuIApTaSBvbiBzb3VoYWl0ZSBjb21wYXJlciBsZXMgZGV1eCBtb3llbm5lcyBldCByZXRvbWJlciBzdXIgbGUgbcOqbWUgcsOpc3VsdGF0IHF1J2F1IGTDqWJ1dCBkdSBkb2N1bWVudCwgCmlsIGZhdXQgZG9uYyBwYXNzZXIgcGFyIGxhIHByZW1pw6hyZSB2ZXJzaW9uIGV0IHJlZ2FyZGVyIGxlIHRlc3QgZCdhZMOpcXVhdGlvbiBkdSBwYXJhbcOodHJlIMOgIDAuCgpUcm9pc2nDqG1lIHBvc3NpYmlsaXTDqSwgbm90cmUgZGlmZsOpcmVuY2UgZGUgcG9pZHMgZMOpcGVuZCBkJ3VuZSB2YXJpYWJsZSBwcmVuYW50IDMgbW9kYWxpdMOpcyBvdSBwbHVzIAooY29tbWUgb24gYXZhaXQgcHJvcG9zw6kgbG9ycyBkZSBsYSByw6lhbGlzYXRpb24gZHUgdGVzdCBBTk9WQSBjaS1kZXNzdXMpLiAKTGEgbcOqbWUgc3ludGF4ZSBmb25jdGlvbm5lIHRvdWpvdXJzIGF1c3NpIGJpZW4gOgoKYGBge3J9CnJlczQgPC0gbG0oZm9ybXVsYT0iZGlmZl93ZWlnaHQgfiAwICsgYWdlX2dyb3VwIiwgZGF0YT1kKQpzdW1tYXJ5KHJlczQpCmBgYAoKTCdvdXRwdXQgc3RhbmRhcmQgZHUgbW9kw6hsZSBsaW7DqWFpcmUgbmUgbm91cyBjYWxjdWxlIGRlcyBwLXZhbHVlcwpxdWUgcG91ciBsZXMgdGVzdHMgZCdhZMOpcXVhdGlvbiBkZSBjaGFxdWUgcGFyYW3DqHRyZSDDoCAwLgpCaWVuIHF1ZSBsJ0FOT1ZBIHNvaXQgZWZmZWN0aXZlbWVudCB1biB0ZXN0IHLDqWFsaXPDqSBhdSBzZWluIGR1IG3Dqm1lCmZyYW1ld29yayBkZSBtb2TDqGxlIGxpbsOpYWlyZSwgbGEgcC12YWx1ZSBhc3NvY2nDqWUgbidlc3QgcsOpY3Vww6lyYWJsZSwKZW4gcHJhdGlxdWUgYXZlYyBSLCBxdWUgdmlhIGxhIGZvbmN0aW9uIGBhb3ZgIHZ1ZSBwcsOpY8OpZGVtbWVudC4KCiMjIFVuIGVmZmV0IGRlIHZhcmlhYmxlIGNvbnRpbnVlCgpEZXJuaWVyIGV4ZW1wbGUgc2ltaWxhaXJlIMOgIGNlIHF1J29uIGF2YWl0IGZhaXQgcHLDqWPDqWRlbW1lbnQgOiAKZXhwbGlxdWVyIG5vdHJlIHZhcmlhYmxlIGRlIHLDqXBvbnNlICJkaWZmw6lyZW5jZSBkZSBwb2lkcyIgcGFyIGxlICJ0ZW1wcyBkZSBjb21wbGlhbmNlIi4KCmBgYHtyfQpyZXM1IDwtIGxtKGZvcm11bGE9ImRpZmZfd2VpZ2h0IH4gdF9jb21wbGlhbmNlIiwgZGF0YT1kKQpzdW1tYXJ5KHJlczUpCmBgYAoKIyMgVmVycyBwbHVzIGRlIGNvbXBsZXhpZmljYXRpb24KCkV0IHNpIG9uIHNvdWhhaXRlIHVuIGVmZmV0IGFkZGl0aWYgZHUgZ3JvdXBlIGV0IGR1IHRlbXBzIGRlIGNvbXBsaWFuY2UgOgoKYGBge3J9CnJlczYgPC0gbG0oZm9ybXVsYT0iZGlmZl93ZWlnaHQgfiAwICsgZ3JvdXAgKyB0X2NvbXBsaWFuY2UiLCBkYXRhPWQpCnN1bW1hcnkocmVzNikKYGBgCgpDZSBuJ2VzdCBwYXMgZXhhY3RlbWVudCBjZSBxdSdvbiBhdmFpdCBmYWl0IHRvdXQgw6AgbCdoZXVyZSBzdXIgbGUgZ3JhcGhlIDogCm9uIGF2YWl0IGFsb3JzIGRldXggcsOpZ3Jlc3Npb25zIGxpbsOpYWlyZXMsIHVuZSBkYW5zIGNoYXF1ZSBncm91cGUuIApSIHBvc3PDqGRlIGVuY29yZSB1bmUgZm9pcyB1bmUgc3ludGF4ZSBwcmF0aXF1ZSBwb3VyIMOnYSA6CgpgYGB7cn0KcmVzNyA8LSBsbShmb3JtdWxhPSJkaWZmX3dlaWdodCB+IDAgKyBncm91cCAqIHRfY29tcGxpYW5jZSIsIGRhdGE9ZCkKc3VtbWFyeShyZXM3KQpgYGAKCk9uIGEgaW50cm9kdWl0IHN1cGVyIGZhY2lsZW1lbnQgY2UgcXUnb24gYXBwZWxsZSBkZXMgKmludGVyYWN0aW9ucyogZW50cmUgbm9zIHZhcmlhYmxlcy4KTGUgbW9kw6hsZSBsaW7DqWFpcmUsIGV0IGxhIHN5bnRheGUgZGUgUiwgc29udCBkb25jIGRlcyBvdXRpbHMgZGUgbW9kw6lsaXNhdGlvbiBleHRyw6ptZW1lbnQgcHVpc3NhbnRzL3ZlcnNhdGlsZXMuCklscyBkZW1hbmRlbnQgw6AgYmllbiByw6lmbMOpY2hpciBlbiBhbW9udCDDoCBjZSBxdSdvbiBzb3VoYWl0ZSDDqXR1ZGllciBwb3VyIMOpdml0ZXIgdW5lIHRyb3AgZ3JhbmRlIG11bHRpcGxpY2F0aW9uIGRlIHRlc3RzIHN0YXRpc3RpcXVlcy4KCj4gQSB2b3VzIGRlIHByb3Bvc2VyIDogcXVlbCBtb2TDqGxlIGxpbsOpYWlyZSB2b3VzIHNlbWJsZSBsZSBwbHVzIGludMOpcmVzc2FudCBwb3VyICJkaWZmX3dlaWdodCIgPwoKCiMgQ29uY2x1c2lvbgoKTm91cyBhdm9ucyB2dSBhdWpvdXJkJ2h1aSB1biBjZXJ0YWluIG5vbWJyZSBkZSBtw6l0aG9kZXMgcGVybWV0dGFudCBkJ2FuYWx5c2VyIGRlcyB2YXJpYWJsZXMgY29udGludWVzLgpMZXMgcG9pbnRzIHByaW5jaXBhdXggw6AgcmV0ZW5pciBzb250IGxlcyBzdWl2YW50cyA6CgoxLiBQb3VyIGNvbXBhcmVyIGRldXggbW95ZW5uZXMsIG91IHVuZSBtb3llbm5lIMOgIHVuZSBub3JtZSwgbGUgVCB0ZXN0IGVzdCBsYSBib25uZSBvcHRpb24sCiAgIMOgIGNvbmRpdGlvbiBxdWUgbGVzIGRvbm7DqWVzIHNvaWVudCBlbiBncmFuZCBub21icmUsIG91IHF1J2VsbGVzIHNvaWVudCBkaXN0cmlidcOpZXMgbm9ybWFsZW1lbnQuCjIuIFBvdXIgY29tcGFyZXIgcGx1cyBxdWUgZGV1eCBtb3llbm5lcywgdW5lIEFOT1ZBIGZhaXQgbCdhZmZhaXJlLCBzb3VzIGRlcyBjb25kaXRpb25zIGlkZW50aXF1ZXMuCjMuIFBvdXIgcXVhbnRpZmllciBsYSBjb3Jyw6lsYXRpb24gZW50cmUgZGV1eCB2YXJpYWJsZXMgY29udGludWVzIGRpc3RyaWJ1w6llcyBub3JtYWxlbWVudCwKICAgdW4gdGVzdCBkZSBjb3Jyw6lsYXRpb24gZGUgUGVhcnNvbiBwZXV0IMOqdHJlIHV0aWxpc8OpLgo0LiBMZSBtb2TDqGxlIGxpbsOpYWlyZSB1bmlmaWUgdG91cyBsZXMgdGVzdHMgcHLDqWPDqWRlbnRzIGRhbnMgdW4gdW5pcXVlIGZyYW1ld29yaywKICAgb8O5IHVuZSB2YXJpYWJsZSBkZSByw6lwb25zZSBjb250aW51ZSAkWSQgZXN0IG1vZMOpbGlzw6llIGNvbW1lIHVuZSBjb21iaW5haXNvbiBsaW7DqWFpcmUKICAgZGUgdmFyaWFibGVzIGV4cGxpY2F0aXZlcyAkWCQgKGRpc2Nyw6h0ZXMgb3UgY29udGludWVzKS4KCk5vdGV6IHBvdXIgZmluaXIgcXUnaWwgZXhpc3RlIGQnYXV0cmVzIHR5cGVzIGRlIHLDqWdyZXNzaW9ucyBiaWVuIGFkYXB0w6llcyDDoCBkZXMgdHlwZXMgZGUgZG9ubsOpZXMgZGlmZsOpcmVudGVzLgpQYXJtaSBsZXMgcGx1cyBjw6lsw6hicmVzLCB2b3VzIHJlbmNvbnRyZXJleiB0csOocyBjZXJ0YWluZW1lbnQgbGEgcsOpZ3Jlc3Npb24gbG9naXN0aXF1ZSwgcG91ciBkZXMgZG9ubsOpZXMgYmluYWlyZXMuClZvdXMgcmVuY29udHJlcmV6IMOpdmVudHVlbGxlbWVudCDDqWdhbGVtZW50IGxhICpyw6lncmVzc2lvbiBzb2Z0bWF4KiwgcG91ciBkZXMgZG9ubsOpZXMgY2F0w6lnb3JpZWxsZXMsIApvdSBsYSAqcsOpZ3Jlc3Npb24gZGUgUG9pc3NvbiosIHBvdXIgZGVzIGRvbm7DqWVzIGRlIGNvbXB0YWdlIGQnw6l2w6luZW1lbnRzLgoK