Pour les utilisateurs de R, on commence par charger un certain nombre de modules intéressants.
Nous sommes prêts à travailler sur ce jeu de données, et, pour ce qui nous intéresse ici, à analyser les données continues qu’il renferme.
Comparaison de moyennes
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 certaine statistique de test \(T\) calculée à partir :
- de l’effectif,
- de la moyenne empirique dans chaque groupe,
- et de la variance empirique dans chaque groupe,
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 :
##
## One Sample t-test
##
## data: d$diff_weight
## t = -11.129, df = 499, p-value < 2.2e-16
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
## -1.509690 -1.056617
## sample estimates:
## mean of x
## -1.283153
L’output de la fonction nous rappelle :
- en première ligne l’échantillon sur lequel on a appliqué le test,
- 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.
- 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, sous H0.
- troisième ligne : un rappel de l’hypothèse alternative du test (modifiable avec l’argument
alternative
).
- un intervalle de confiance à 95% de la moyenne (bornes modifiables via l’argument
conf.level
).
- 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(diff_weight ~ group, data=d)
##
## Welch Two Sample t-test
##
## data: diff_weight by group
## t = 1.9787, df = 489.92, p-value = 0.04841
## alternative hypothesis: true difference in means between group control and group treatment is not equal to 0
## 95 percent confidence interval:
## 0.003200646 0.906752183
## sample estimates:
## mean in group control mean in group treatment
## -1.055665 -1.510642
Ca se lit de façon très similaire à ce qu’on avait précédemment, les différences étant :
- L’IC à 95% correspond à celui de la différence des deux moyennes.
- Les estimations ponctuelles correspondent aux deux moyennes.
Que concluons-nous ?
ANOVA : comparaison de n 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 et 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

ggsave("../../Figures/illu_diabete_diff_weight_age_group.pdf", plot=plot_diff_weight_by_age_group, width=12, height=5)
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 <- aov(diff_weight ~ age_group, data=d)
summary(res_aov)
## Df Sum Sq Mean Sq F value Pr(>F)
## age_group 2 4 2.099 0.315 0.73
## Residuals 497 3313 6.666
Les deux points les plus intéressants de l’output sont :
- la “F value” qui est la valeur de la statistique de test,
- 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_2 <- aov(diff_weight ~ group, data=d)
summary(res_aov_2)
## Df Sum Sq Mean Sq F value Pr(>F)
## group 1 26 25.875 3.915 0.0484 *
## Residuals 498 3291 6.609
## ---
## 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.9787, df = 489.92, p-value = 0.04841
## alternative hypothesis: true difference in means between group control and group treatment is not equal to 0
## 95 percent confidence interval:
## 0.003200646 0.906752183
## sample estimates:
## mean in group control mean in group treatment
## -1.055665 -1.510642
Test de Wilcoxon
L’alter-ego non-paramétrique du T test est le test de Wilcoxon (aussi appelé Mann-Whitney ou Wilcoxon-Mann-Whitney).
- Avantage : conditions d’application toujours remplies.
- Inconvénient : moins puissant qu’un T test.
Dans notre cas, étant donné le grand effectif de notre essai clinique, le T test était un choix très approprié. On peut cependant se convaincre du fonctionnement d’un test de Wilcoxon en l’essayant :
wilcox.test(diff_weight ~ group, data=d)
##
## Wilcoxon rank sum test with continuity correction
##
## data: diff_weight by group
## W = 34330, p-value = 0.0566
## alternative hypothesis: true location shift is not equal to 0
Test de Kruskal-Wallis
De même, testons l’utilisation de l’alter-ego de l’ANOVA en version non-paramétrique :
kruskal.test(diff_weight ~ age_group, data=d)
##
## Kruskal-Wallis rank sum test
##
## data: diff_weight by age_group
## Kruskal-Wallis chi-squared = 0.60933, df = 2, p-value = 0.7374
On se convainc aussi facilement qu’un test de Kruskal-Wallis avec une variable à deux modalités revient à faire un test de Wilcoxon :
kruskal.test(diff_weight ~ group, data=d)
##
## Kruskal-Wallis rank sum test
##
## data: diff_weight by group
## Kruskal-Wallis chi-squared = 3.6355, df = 1, p-value = 0.05656
Régression linéaire univariée
Une droite de régression commune
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, avec une droite de régression partagé par tous les points :
plot_diff_weight_vs_compliance <- d %>%
ggplot(aes(y=diff_weight, x=t_compliance)) +
geom_point() +
geom_smooth(method="lm", se=F, formula="y ~ x", col=bleufonce) +
theme_bw() +
xlab("t_compliance") +
ylab("différence de poids à 6 mois")
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 = -3.5459, df = 498, p-value = 0.0004283
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.2412955 -0.0702008
## sample estimates:
## cor
## -0.1569254
Que concluons-nous ?
Une droite dans chaque groupe
Deuxième essai plus intéressant : regarder l’influence de t_compliance
séparément dans les deux bras de l’essai clinique. On commence par observer les données :
plot_diff_weight_vs_compliance_2 <- 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_2

ggsave("../../Figures/illu_diabete_diff_weight_reglin.pdf", plot=plot_diff_weight_vs_compliance_2, width=12, height=5)
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 = -0.58815, df = 248, p-value = 0.557
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.1606448 0.0871489
## sample estimates:
## cor
## -0.03732163
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 = -4.3818, df = 248, p-value = 1.739e-05
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.3795073 -0.1489484
## sample estimates:
## cor
## -0.2680618
Que concluons-nous ?
Le modèle linéaire
Description
Le modèle linéaire présente l’intérê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 à fitter 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.
res_lm_1 <- lm(formula="diff_weight ~ 1", data=d)
summary(res_lm_1)
##
## Call:
## lm(formula = "diff_weight ~ 1", data = d)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.9290 -1.7337 0.1193 1.7881 7.7890
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.2832 0.1153 -11.13 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.578 on 499 degrees of freedom
Vérifier 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
res_lm_group <- lm(formula="diff_weight ~ group", data=d)
summary(res_lm_group)
##
## Call:
## lm(formula = "diff_weight ~ group", data = d)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.7016 -1.6078 0.1307 1.7151 8.0165
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.0557 0.1626 -6.493 2.04e-10 ***
## grouptreatment -0.4550 0.2299 -1.979 0.0484 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.571 on 498 degrees of freedom
## Multiple R-squared: 0.007801, Adjusted R-squared: 0.005809
## F-statistic: 3.915 on 1 and 498 DF, p-value: 0.0484
# deuxième version avec moyenne dans chaque groupe
res_lm_group_2 <- lm(formula="diff_weight ~ 0 + group", data=d)
summary(res_lm_group_2)
##
## Call:
## lm(formula = "diff_weight ~ 0 + group", data = d)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.7016 -1.6078 0.1307 1.7151 8.0165
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## groupcontrol -1.0557 0.1626 -6.493 2.04e-10 ***
## grouptreatment -1.5106 0.1626 -9.291 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.571 on 498 degrees of freedom
## Multiple R-squared: 0.2051, Adjusted R-squared: 0.2019
## F-statistic: 64.24 on 2 and 498 DF, p-value: < 2.2e-16
On a donc exactement le même modèle sous-jacent, avec des estimations de paramètres identiques. 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 :
res_lm_group_3 <- lm(formula="diff_weight ~ 0 + age_group", data=d)
summary(res_lm_group_3)
##
## Call:
## lm(formula = "diff_weight ~ 0 + age_group", data = d)
##
## Residuals:
## Min 1Q Median 3Q Max
## -9.0199 -1.7538 0.0998 1.7780 7.6981
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## age_groupentre 40 et 60 -1.1923 0.2080 -5.731 1.73e-08 ***
## age_groupmoins de 40 -1.3993 0.1883 -7.432 4.72e-13 ***
## age_groupplus de 60 -1.2335 0.2054 -6.005 3.69e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.582 on 497 degrees of freedom
## Multiple R-squared: 0.1999, Adjusted R-squared: 0.195
## F-statistic: 41.38 on 3 and 497 DF, p-value: < 2.2e-16
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”.
res_lm_compliance <- lm(formula="diff_weight ~ t_compliance", data=d)
summary(res_lm_compliance)
##
## Call:
## lm(formula = "diff_weight ~ t_compliance", data = d)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.1795 -1.6306 0.0848 1.7868 7.6839
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.855739 0.165900 -5.158 3.61e-07 ***
## t_compliance -0.020148 0.005682 -3.546 0.000428 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.549 on 498 degrees of freedom
## Multiple R-squared: 0.02463, Adjusted R-squared: 0.02267
## F-statistic: 12.57 on 1 and 498 DF, p-value: 0.0004283
Vers plus de complexification
Et si on souhaite un effet additif du groupe et du temps de compliance :
res_lm_compliance_group <- lm(formula="diff_weight ~ 0 + group + t_compliance", data=d)
summary(res_lm_compliance_group)
##
## Call:
## lm(formula = "diff_weight ~ 0 + group + t_compliance", data = d)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.4145 -1.6278 0.0921 1.7597 7.9167
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## groupcontrol -0.618262 0.201687 -3.065 0.002292 **
## grouptreatment -1.085678 0.199611 -5.439 8.42e-08 ***
## t_compliance -0.020325 0.005664 -3.588 0.000366 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.541 on 497 degrees of freedom
## Multiple R-squared: 0.2252, Adjusted R-squared: 0.2205
## F-statistic: 48.14 on 3 and 497 DF, p-value: < 2.2e-16
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 :
res_lm_compliance_group_2 <- lm(formula="diff_weight ~ 0 + group * t_compliance", data=d)
summary(res_lm_compliance_group_2)
##
## Call:
## lm(formula = "diff_weight ~ 0 + group * t_compliance", data = d)
##
## Residuals:
## Min 1Q Median 3Q Max
## -8.2949 -1.5933 0.1597 1.6771 7.8364
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## groupcontrol -0.960433 0.233093 -4.120 4.43e-05 ***
## grouptreatment -0.743698 0.231290 -3.215 0.00139 **
## t_compliance -0.004425 0.007897 -0.560 0.57549
## grouptreatment:t_compliance -0.032257 0.011248 -2.868 0.00431 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.522 on 496 degrees of freedom
## Multiple R-squared: 0.2378, Adjusted R-squared: 0.2317
## F-statistic: 38.69 on 4 and 496 DF, p-value: < 2.2e-16
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.
Quel serait selon vous le modèle linéaire le plus approprié pour diff_weight
?
Quel serait selon vous le modèle linéaire le plus approprié pour diff_steps
?
LS0tCnRpdGxlOiAiU3RhdGlzdGlxdWVzIGF1IExBUEVDIC0tIEFuYWx5c2UgZGUgZG9ubsOpZXMgY29udGludWVzIgphdXRob3I6ICJNYXJjIE1hbmNlYXUiCmRhdGU6ICIyMDI0LTAxIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBoaWdobGlnaHQ6ICJ0YW5nbyIKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKLS0tCgpQb3VyIGxlcyB1dGlsaXNhdGV1cnMgZGUgUiwgb24gY29tbWVuY2UgcGFyIGNoYXJnZXIgdW4gY2VydGFpbiBub21icmUgZGUgbW9kdWxlcyBpbnTDqXJlc3NhbnRzLgoKYGBge3IgbWVzc2FnZT1GfQojIGxlIHBhY2thZ2UgbW9kZXJuZSBzdGFuZGFyZCBwb3VyIG1hbmlwdWxlciBzZXMgZG9ubsOpZXMKbGlicmFyeSh0aWR5dmVyc2UpCgojIHF1ZWxxdWVzIGNvdWxldXJzIG1hbnVlbGxlcwpibGV1Zm9uY2UgPC0gIiMzZDU0NjgiCmJsZXVjbGFpciA8LSAiIzViN2M5OCIKcm9zZSA8LSAiI2ZmNTU1NSIKYGBgCgpQdWlzIG9uIGltcG9ydGUgbGUgZGF0YXNldCBkYW5zIG5vdHJlIHNlc3Npb24gZGUgUiA6CgpgYGB7cn0KZCA8LSByZWFkLmNzdigiZXR1ZGVfZmljdGl2ZV9kaWFiZXRlcy5jc3YiKQpoZWFkKGQpCmBgYAoKTm91cyBzb21tZXMgcHLDqnRzIMOgIHRyYXZhaWxsZXIgc3VyIGNlIGpldSBkZSBkb25uw6llcywgZXQsIHBvdXIgY2UgcXVpIG5vdXMgaW50w6lyZXNzZSBpY2ksCsOgIGFuYWx5c2VyIGxlcyBkb25uw6llcyBjb250aW51ZXMgcXUnaWwgcmVuZmVybWUuCgoKIyBDb21wYXJhaXNvbiBkZSBtb3llbm5lcwoKIyMgVCB0ZXN0IDogYWTDqXF1YXRpb24gw6AgdW5lIG5vcm1lCgpPbiBvYnNlcnZlIGRlcyBkaWZmw6lyZW5jZXMgZGUgcG9pZHMgJChYX2kpJCBjaGV6IG5vcyBpbmRpdmlkdXMgZW50cmUgbGUgZMOpYnV0IGV0IGxhIGZpbiBkZSBsJ2V4cMOpcmllbmNlLgpFc3QtY2UgcXVlIGxhIGRpZmbDqXJlbmNlIGRlIHBvaWRzIG1veWVubmUgZXN0IGRpZmbDqXJlbnRlIGRlIHrDqXJvID8KCk9uIHNvdWhhaXRlIHLDqWFsaXNlciBsZSB0ZXN0IGQnaHlwb3Row6hzZSBzdWl2YW50IDoKCiogSDAgOiBsYSBtb3llbm5lIGRlcyAkKFhfaSkkIHZhdXQgemVyby4KKiBIMSA6IGxhIG1veWVubmUgZGVzICQoWF9pKSQgZXN0IGRpZmbDqXJlbnRlIGRlIHrDqXJvLgoKQ29uZGl0aW9ucyBkJ2FwcGxpY2F0aW9uIDoKCiogbGVzIHZhcmlhYmxlcyBhbMOpYXRvaXJlcyBzb250IGluZMOpcGVuZGFudGVzIGV0IGRlIG3Dqm1lIGxvaS4KKiBzb2l0IGxlcyB2YXJpYWJsZXMgYWzDqWF0b2lyZXMgc29udCBkaXN0cmlidcOpZXMgbm9ybWFsZW1lbnQsIHNvaXQgbCfDqWNoYW50aWxsb24gZXN0ICJncmFuZCIuCgpPbiBjb21tZW5jZSB0b3Vqb3VycyBwYXIgcmVwcsOpc2VudGVyIG5vcyBkb25uw6llcy4KCmBgYHtyIGZpZy53aWR0aD0xMH0KcGxvdF9kaWZmX3dlaWdodCA8LSBkICU+JSBnZ3Bsb3QoYWVzKHg9ZGlmZl93ZWlnaHQpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhbHBoYT0wLjcsIHBvc2l0aW9uPSJpZGVudGl0eSIsIGJpbnM9MjApICsKICAgIHRoZW1lX2J3KCkgKwogICAgeWxhYigiZWZmZWN0aWYiKSArIAogICAgeGxhYigiZGlmZsOpcmVuY2UgZGUgcG9pZHMgw6AgNiBtb2lzIikKcGxvdF9kaWZmX3dlaWdodApgYGAKCkljaSBsZXMgY29uZGl0aW9ucyBkJ2FwcGxpY2F0aW9uIHNlbWJsZW50IHJlc3BlY3TDqWVzLgoKVW4gcsOpc3VsdGF0IHRow6lvcmlxdWUgY2VydGlmaWUgcXUndW5lIGNlcnRhaW5lIHN0YXRpc3RpcXVlIGRlIHRlc3QgJFQkIGNhbGN1bMOpZSDDoCBwYXJ0aXIgOgoKKiBkZSBsJ2VmZmVjdGlmLCAKKiBkZSBsYSBtb3llbm5lIGVtcGlyaXF1ZSBkYW5zIGNoYXF1ZSBncm91cGUsIAoqIGV0IGRlIGxhIHZhcmlhbmNlIGVtcGlyaXF1ZSBkYW5zIGNoYXF1ZSBncm91cGUsIAoKZXN0IGRpc3RyaWJ1w6llIHN1aXZhbnQgdW5lIGxvaSBkZSBTdHVkZW50IGF2ZWMgdW4gY2VydGFpbiBub21icmUgZGUgImRlZ3LDqXMgZGUgbGliZXJ0w6kiLgoKSWwgcydhZ2l0IGRvbmMgZGUgY2FsY3VsZXIgbGEgdmFsZXVyIG9ic2VydsOpZSBkZSBjZXR0ZSBzdGF0aXN0aXF1ZSBkZSB0ZXN0IHN1ciBub3RyZSDDqWNoYW50aWxsb24sIApldCBkZSBsYSBjb21wYXJlciBhdXggcXVhbnRpbGVzIGRlIGxhIGxvaSB0aMOpb3JpcXVlLgoKTGEgZm9uY3Rpb24gcG91ciBmYWlyZSDDp2EgZW4gUiBlc3QgbGEgc3VpdmFudGUgOgoKYGBge3J9CnQudGVzdCh4PWQkZGlmZl93ZWlnaHQpCmBgYAoKTCdvdXRwdXQgZGUgbGEgZm9uY3Rpb24gbm91cyByYXBwZWxsZSA6CgoxLiBlbiBwcmVtacOocmUgbGlnbmUgbCfDqWNoYW50aWxsb24gc3VyIGxlcXVlbCBvbiBhIGFwcGxpcXXDqSBsZSB0ZXN0LAoyLiBlbiBzZWNvbmRlIGxpZ25lIG9uIHLDqWN1cMOocmUgbGEgdmFsZXVyIG9ic2VydsOpZSBkZSBsYSBzdGF0aXN0aXF1ZSBkZSB0ZXN0LCAKICAgYWluc2kgcXVlIGxlIG5vbWJyZSBkZSAiZGVncsOpcyBkZSBsaWJlcnTDqSIgZGUgbGEgbG9pIHRow6lvcmlxdWUuCjMuIGVuIGZpbiBkZSBzZWNvbmRlIGxpZ25lLCBzdXBlciBpbXBvcnRhbnRlIDogbGEgcC12YWx1ZSAhISEgCiAgIFByb2JhYmlsaXTDqSBxdWUgbGEgdmFsZXVyIGRlIGxhIHN0YXRpc3RpcXVlIHNvaXQgYXUgbW9pbnMgYXVzc2kgZXh0csOqbWUgcXUnb2JzZXJ2w6llLCBzb3VzIEgwLgo0LiB0cm9pc2nDqG1lIGxpZ25lIDogdW4gcmFwcGVsIGRlIGwnaHlwb3Row6hzZSBhbHRlcm5hdGl2ZSBkdSB0ZXN0IChtb2RpZmlhYmxlIGF2ZWMgbCdhcmd1bWVudCBgYWx0ZXJuYXRpdmVgKS4KNS4gdW4gaW50ZXJ2YWxsZSBkZSBjb25maWFuY2Ugw6AgOTUlIGRlIGxhIG1veWVubmUgKGJvcm5lcyBtb2RpZmlhYmxlcyB2aWEgbCdhcmd1bWVudCBgY29uZi5sZXZlbGApLgo2LiB0b3V0IMOgIGxhIGZpbiwgbCdlc3RpbWF0aW9uIHBvbmN0dWVsbGUgZGUgbGEgbW95ZW5uZS4KCj4gUXVlIGNvbmNsdW9ucy1ub3VzID8KCgojIyBUIHRlc3QgOiBjb21wYXJhaXNvbiBkZSAyIG1veWVubmVzCgpPbiBvYnNlcnZlIGRlcyB2YWxldXJzICQoWF97aSxqfSkkIGRlIG5vcyBpbmRpdmlkdXMgJGkkIGRhbnMgZGV1eCBncm91cGVzICRqPTEkIGV0ICRqPTIkLiAKSWNpIGxlcyBkZXV4IGdyb3VwZXMgY29ycmVzcG9uZGVudCBhdSBjb250cm9sIHZzLiB0cmVhdG1lbnQsIApldCBsZXMgb2JzZXJ2YXRpb25zIGNvcnJlc3BvbmRlbnQgw6AgdW5lIGRpZmbDqXJlbmNlIGRlIHBvaWRzIGVudHJlIGxlIGTDqWJ1dCBldCBsYSBmaW4gZGUgbCdleHDDqXJpZW5jZS4KCkVzdC1jZSBxdWUgbGEgZGlmZsOpcmVuY2UgZGUgcG9pZHMgbW95ZW5uZSBlc3QgZGlmZsOpcmVudGUgZGFucyBsZXMgZGV1eCBncm91cGVzID8KCk9uIHNvdWhhaXRlIHLDqWFsaXNlciBsZSB0ZXN0IGQnaHlwb3Row6hzZSBzdWl2YW50IDoKCiogSDAgOiBsYSBtb3llbm5lIGRlcyAkKFhfe2ksMX0pJCBlc3QgaWRlbnRpcXVlIMOgIGNlbGxlIGRlcyAkKFhfe2ksMn0pJC4KKiBIMSA6IGxlcyBkZXV4IG1veWVubmVzIHNvbnQgZGlmZsOpcmVudGVzLgoKQ29uZGl0aW9ucyBkJ2FwcGxpY2F0aW9uIDoKCiogbGVzIHZhcmlhYmxlcyBhbMOpYXRvaXJlcyBzb250IGluZMOpcGVuZGFudGVzIGV0IGRlIG3Dqm1lIGxvaS4KKiBzb2l0IGxlcyB2YXJpYWJsZXMgYWzDqWF0b2lyZXMgc29udCBkaXN0cmlidcOpZXMgbm9ybWFsZW1lbnQsIHNvaXQgbCfDqWNoYW50aWxsb24gZXN0ICJncmFuZCIuCgpPbiBjb21tZW5jZSB0b3Vqb3VycyBwYXIgcmVwcsOpc2VudGVyIG5vcyBkb25uw6llcyBldCBxdWFudGl0w6lzIGQnaW50w6lyw6p0LgoKYGBge3IgZmlnLndpZHRoPTEwfQptIDwtIGQgJT4lIGdyb3VwX2J5KGdyb3VwKSAlPiUgc3VtbWFyaXNlKG1lYW49bWVhbihkaWZmX3dlaWdodCkpICU+JSBzZWxlY3QobWVhbikKCnBsb3RfZGlmZl93ZWlnaHRfYnlfZ3JvdXAgPC0gZCAlPiUgZ2dwbG90KGFlcyh4PWRpZmZfd2VpZ2h0LCBmaWxsPWdyb3VwKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiLCBiaW5zPTIwKSArCiAgICB0aGVtZV9idygpICsKICAgIHlsYWIoImVmZmVjdGlmIikgKyAKICAgIHhsYWIoImRpZmbDqXJlbmNlIGRlIHBvaWRzIMOgIDYgbW9pcyIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKGJsZXVjbGFpciwgcm9zZSkpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMobSRtZWFuWzFdLCBtJG1lYW5bMl0pLCBjb2xvcj1jKGJsZXVjbGFpciwgcm9zZSkpCnBsb3RfZGlmZl93ZWlnaHRfYnlfZ3JvdXAKYGBgCgpJY2kgbGVzIGNvbmRpdGlvbnMgZCdhcHBsaWNhdGlvbiBzZW1ibGVudCByZXNwZWN0w6llcy4KClVuIHLDqXN1bHRhdCB0aMOpb3JpcXVlIGNlcnRpZmllIHF1J3VuZSBzdGF0aXN0aXF1ZSBkZSB0ZXN0IGNhbGN1bMOpZSDDoCBwYXJ0aXIgZGUgbCdlZmZlY3RpZiwgCmRlcyBtb3llbm5lcyBlbXBpcmlxdWVzIGRlcyBkZXV4IGdyb3VwZXMgZXQgZGUgbGEgdmFyaWFuY2UgZW1waXJpcXVlIGRlcyBkZXV4IGdyb3VwZXMsIAplc3QgZGlzdHJpYnXDqWUgc3VpdmFudCB1bmUgbG9pIGRlIFN0dWRlbnQgYXZlYyB1biBjZXJ0YWluIG5vbWJyZSBkZSAiZGVncsOpcyBkZSBsaWJlcnTDqSIuCgpJbCBzJ2FnaXQgZG9uYyBkZSBjYWxjdWxlciBsYSB2YWxldXIgb2JzZXJ2w6llIGRlIGNldHRlIHN0YXRpc3RpcXVlIGRlIHRlc3Qgc3VyIG5vdHJlIMOpY2hhbnRpbGxvbiwgCmV0IGRlIGxhIGNvbXBhcmVyIGF1eCBxdWFudGlsZXMgZGUgbGEgbG9pIHRow6lvcmlxdWUuCgpMYSBmb25jdGlvbiBwb3VyIGZhaXJlIMOnYSBlbiBSIGVzdCBsYSBzdWl2YW50ZSA6CgpgYGB7cn0KdC50ZXN0KGRpZmZfd2VpZ2h0IH4gZ3JvdXAsIGRhdGE9ZCkKYGBgCgpDYSBzZSBsaXQgZGUgZmHDp29uIHRyw6hzIHNpbWlsYWlyZSDDoCBjZSBxdSdvbiBhdmFpdCBwcsOpY8OpZGVtbWVudCwgbGVzIGRpZmbDqXJlbmNlcyDDqXRhbnQgOgoKMS4gTCdJQyDDoCA5NSUgY29ycmVzcG9uZCDDoCBjZWx1aSBkZSBsYSBkaWZmw6lyZW5jZSBkZXMgZGV1eCBtb3llbm5lcy4KMi4gTGVzIGVzdGltYXRpb25zIHBvbmN0dWVsbGVzIGNvcnJlc3BvbmRlbnQgYXV4IGRldXggbW95ZW5uZXMuCgo+IFF1ZSBjb25jbHVvbnMtbm91cyA/CgoKIyMgQU5PVkEgOiBjb21wYXJhaXNvbiBkZSBuIG1veWVubmVzCgpPbiBzb3VoYWl0ZSBwbHVzIHByw6ljaXPDqW1lbnQgdGVzdGVyIGljaSBsYSBkaWZmw6lyZW5jZSBkJ2F1IG1vaW5zIHVuZSBtb3llbm5lIHBhcm1pIHBsdXNpZXVycyBncm91cGVzLgoKT24gZGlzcG9zZSBjZXR0ZSBmb2lzIGQnb2JzZXJ2YXRpb25zIGRlIGxhIG3Dqm1lIHF1YW50aXTDqSAkKFhfe2ksan0pIGNoZXogbCdpbmRpdmlkdSAkaSQgZGFucyBsZSBncm91cGUgJGokLCAKZGFucyB1biBub21icmUgZGUgZ3JvdXBlcyBwb3RlbnRpZWxsZW1lbnQgc3Vww6lyaWV1ciDDoCAyLgpFc3QtY2UgcXVlIGxhIG1veWVubmUgZCdhdSBtb2lucyB1biBncm91cGUgZGlmZsOocmUgZGVzIGF1dHJlcyBncm91cGVzID8KClBhciBleGVtcGxlLCBpY2ksIGxhIGRpZmbDqXJlbmNlIGRlIHBvaWRzIG1veWVubmUgY2hhbmdlLXQtZWxsZSBkYW5zIGxlcyB0cm9pcyBjbGFzc2VzIGQnw6JnZSA/ICg8IDQwLCA0MC02MCwgPiA2MCkKCk9uIHRlc3RlIGZvcm1lbGxlbWVudCA6CgoqIEgwIDogbGVzIG1veWVubmVzIGRlcyAkKFhfe2ksMX0pJCwgZGVzICQoWF97aSwyfSkkIGV0IGRlcyAkKFhfe2ksM30pJCBzb250IGlkZW50aXF1ZXMuCiogSDEgOiBhdSBtb2lucyB1bmUgZGVzIG1veWVubmVzIGRpZmbDqHJlLgoKQ29uZGl0aW9ucyBkJ2FwcGxpY2F0aW9uIDoKCiogbGVzIHZhcmlhYmxlcyBhbMOpYXRvaXJlcyBzb250IGluZMOpcGVuZGFudGVzIGV0IHN1aXZlbnQgdW5lIGxvaSBub3JtYWxlLgoqIGlsIHkgYSDDqWdhbGl0w6kgZGUgdmFyaWFuY2UgZW50cmUgbGVzIGRpZmbDqXJlbnRzIGdyb3VwZXMuCgpVbmUgZm9pcyBuJ2VzdCBwYXMgY291dMO7bWUsIGNvbW1lbsOnb25zIHBhciByZXByw6lzZW50ZXIgbGVzIGRvbm7DqWVzIDoKCmBgYHtyIGZpZy53aWR0aD0xMH0KbSA8LSBkICU+JSBncm91cF9ieShhZ2VfZ3JvdXApICU+JSBzdW1tYXJpc2UobWVhbj1tZWFuKGRpZmZfd2VpZ2h0KSkgJT4lIHNlbGVjdChtZWFuKQoKcGxvdF9kaWZmX3dlaWdodF9ieV9hZ2VfZ3JvdXAgPC0gZCAlPiUgZ2dwbG90KGFlcyh4PWRpZmZfd2VpZ2h0LCBmaWxsPWFzLmZhY3RvcihhZ2VfZ3JvdXApKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiLCBiaW5zPTIwKSArCiAgICB0aGVtZV9idygpICsKICAgIHlsYWIoImVmZmVjdGlmIikgKyAKICAgIHhsYWIoImRpZmbDqXJlbmNlIGRlIHBvaWRzIMOgIDYgbW9pcyIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKGJsZXVmb25jZSwgYmxldWNsYWlyLCByb3NlKSkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhtJG1lYW5bMV0sIG0kbWVhblsyXSwgbSRtZWFuWzNdKSwgY29sb3I9YyhibGV1Zm9uY2UsIGJsZXVjbGFpciwgcm9zZSkpCnBsb3RfZGlmZl93ZWlnaHRfYnlfYWdlX2dyb3VwCmdnc2F2ZSgiLi4vLi4vRmlndXJlcy9pbGx1X2RpYWJldGVfZGlmZl93ZWlnaHRfYWdlX2dyb3VwLnBkZiIsIHBsb3Q9cGxvdF9kaWZmX3dlaWdodF9ieV9hZ2VfZ3JvdXAsIHdpZHRoPTEyLCBoZWlnaHQ9NSkKYGBgCgpMZXMgY29uZGl0aW9ucyBkJ2FwcGxpY2F0aW9uIHNlbWJsZW50IHJlc3BlY3TDqWVzIChhdSBtb2lucyBncmFwaGlxdWVtZW50KS4KClVuIHLDqXN1bHRhdCB0aMOpb3JpcXVlIGNlcnRpZmllIGNldHRlIGZvaXMgcXUndW5lIGNlcnRhaW5lIHN0YXRpc3RpcXVlIGRlIHRlc3QsIApjYWxjdWzDqWUgw6AgcGFydGlyIDoKCiogZGVzIGVmZmVjdGlmcyBkZXMgZGlmZsOpcmVudHMgZ3JvdXBlcwoqIGV0IGRlcyB2YXJpYW5jZXMgaW50cmEtZ3JvdXBlcyAvIGludGVyLWdyb3VwZXMsCgplc3QgZGlzdHJpYnXDqWUgc3VpdmFudCB1bmUgbG9pIGRlIEZpc2hlciBhdmVjIHVuIGNlcnRhaW4gbm9tYnJlIGRlICJkZWdyw6lzIGRlIGxpYmVydMOpIi4KCklsIHMnYWdpdCBkb25jIGRlIGNhbGN1bGVyIGxhIHZhbGV1ciBvYnNlcnbDqWUgZGUgY2V0dGUgc3RhdGlzdGlxdWUgZGUgdGVzdCBzdXIgbm90cmUgw6ljaGFudGlsbG9uLCAKZXQgZGUgbGEgY29tcGFyZXIgYXV4IHF1YW50aWxlcyBkZSBsYSBsb2kgdGjDqW9yaXF1ZS4KCkxhIGZvbmN0aW9uIHBvdXIgZmFpcmUgw6dhIGVuIFIgZXN0IGxhIHN1aXZhbnRlIDoKCmBgYHtyfQpyZXNfYW92IDwtIGFvdihkaWZmX3dlaWdodCB+IGFnZV9ncm91cCwgZGF0YT1kKQpzdW1tYXJ5KHJlc19hb3YpCmBgYAoKTGVzIGRldXggcG9pbnRzIGxlcyBwbHVzIGludMOpcmVzc2FudHMgZGUgbCdvdXRwdXQgc29udCA6CgoxLiBsYSAiRiB2YWx1ZSIgcXVpIGVzdCBsYSB2YWxldXIgZGUgbGEgc3RhdGlzdGlxdWUgZGUgdGVzdCwKMi4gZXQgbGEgcC12YWx1ZSA6IGxhIHByb2JhYmlsaXTDqSBzb3VzIEgwIGQnb2J0ZW5pciB1bmUgc3RhdGlzdGlxdWUgZGUgdGVzdCBhdSBtb2lucyBhdXNzaSBleHRyw6ptZSBxdSdvYnNlcnbDqWUuCgo+IFF1ZSBjb25jbHVvbnMtbm91cyA/CgpGaW5pc3NvbnMgcGFyIHVuIHBldGl0IHRlc3QsIHBlcm1ldHRhbnQgZGUgc2UgY29udmFpbmNyZSBxdSd1bmUgQU5PVkEgYXZlYyBkZXV4IGdyb3VwZXMgY29ycmVzcG9uZCDDoCB1biBUIHRlc3QuCgpgYGB7cn0KcmVzX2Fvdl8yIDwtIGFvdihkaWZmX3dlaWdodCB+IGdyb3VwLCBkYXRhPWQpCnN1bW1hcnkocmVzX2Fvdl8yKQp0LnRlc3QoZGlmZl93ZWlnaHQgfiBncm91cCwgZGF0YT1kKQpgYGAKCgojIyBUZXN0IGRlIFdpbGNveG9uCgpMJ2FsdGVyLWVnbyBub24tcGFyYW3DqXRyaXF1ZSBkdSBUIHRlc3QgZXN0IGxlIHRlc3QgZGUgV2lsY294b24KKGF1c3NpIGFwcGVsw6kgTWFubi1XaGl0bmV5IG91IFdpbGNveG9uLU1hbm4tV2hpdG5leSkuCgoqIEF2YW50YWdlIDogY29uZGl0aW9ucyBkJ2FwcGxpY2F0aW9uIHRvdWpvdXJzIHJlbXBsaWVzLgoqIEluY29udsOpbmllbnQgOiBtb2lucyBwdWlzc2FudCBxdSd1biBUIHRlc3QuCgpEYW5zIG5vdHJlIGNhcywgw6l0YW50IGRvbm7DqSBsZSBncmFuZCBlZmZlY3RpZiBkZSBub3RyZSBlc3NhaSBjbGluaXF1ZSwKbGUgVCB0ZXN0IMOpdGFpdCB1biBjaG9peCB0csOocyBhcHByb3ByacOpLgpPbiBwZXV0IGNlcGVuZGFudCBzZSBjb252YWluY3JlIGR1IGZvbmN0aW9ubmVtZW50IGQndW4gdGVzdCBkZSBXaWxjb3hvbgplbiBsJ2Vzc2F5YW50IDoKCmBgYHtyfQp3aWxjb3gudGVzdChkaWZmX3dlaWdodCB+IGdyb3VwLCBkYXRhPWQpCmBgYAoKIyMgVGVzdCBkZSBLcnVza2FsLVdhbGxpcwoKRGUgbcOqbWUsIHRlc3RvbnMgbCd1dGlsaXNhdGlvbiBkZSBsJ2FsdGVyLWVnbyBkZSBsJ0FOT1ZBIGVuIHZlcnNpb24gbm9uLXBhcmFtw6l0cmlxdWUgOgoKYGBge3J9CmtydXNrYWwudGVzdChkaWZmX3dlaWdodCB+IGFnZV9ncm91cCwgZGF0YT1kKQpgYGAKCk9uIHNlIGNvbnZhaW5jIGF1c3NpIGZhY2lsZW1lbnQgcXUndW4gdGVzdCBkZSBLcnVza2FsLVdhbGxpcyBhdmVjIHVuZSB2YXJpYWJsZQrDoCBkZXV4IG1vZGFsaXTDqXMgcmV2aWVudCDDoCBmYWlyZSB1biB0ZXN0IGRlIFdpbGNveG9uIDoKCmBgYHtyfQprcnVza2FsLnRlc3QoZGlmZl93ZWlnaHQgfiBncm91cCwgZGF0YT1kKQpgYGAKCgojIFLDqWdyZXNzaW9uIGxpbsOpYWlyZSB1bml2YXJpw6llCgojIyBVbmUgZHJvaXRlIGRlIHLDqWdyZXNzaW9uIGNvbW11bmUKCk9uIGRpc3Bvc2UgY2V0dGUgZm9pcyBkJ29ic2VydmF0aW9ucyBkJ3VuZSB2YXJpYWJsZXMgJChZX2kpJCBkJ2ludMOpcsOqdCwgCnBhciBleGVtcGxlIGVuY29yZSB1bmUgZm9pcyBsYSBkaWZmw6lyZW5jZSBkZSBwb2lkcy4gCkV0IGQndW5lIGF1dHJlIHZhcmlhYmxlIHF1YW50aXRhdGl2ZSAkKFhfaSkkIGNvcnJlc3BvbmRhbnQgw6AgcXVlbHF1ZSBjaG9zZSBxdWkgcG91cnJhaXQgw6p0cmUgY29ycsOpbMOpIMOgIG5vcyAkKFlfaSkkLCAKcGFyIGV4ZW1wbGUgaWNpIGxlIHRlbXBzIHBlbmRhbnQgbGVxdWVsIGxlIHBhdGllbnQgYSBzdHJpY3RlbWVudCBvYnNlcnbDqSBzb24gdHJhaXRlbWVudCAoY29tcGxpYW5jZSkuCgpFc3QtY2UgcXVlIGxlIGdhaW4gZGUgcG9pZHMgZMOpcGVuZCBkZSBsYSBjb21wbGlhbmNlLCBkYW5zIGxlIGdyb3VwZSB0cmFpdMOpID8KCkxlIGNvZWZmaWNpZW50IGRlIGNvcnLDqWxhdGlvbiAkXHJobyQgY29ycmVzcG9uZCDDoCBsYSBjb3ZhcmlhbmNlIGRlcyBkZXV4IHF1YW50aXTDqXMgbm9ybWFsaXPDqWUgcGFyIGxlcyDDqWNhcnRzLXR5cGVzIGRlcyBkZXV4IHF1YW50aXTDqXMuIApJbCBlc3QgY29tcHJpcyBlbnRyZSAtMSBldCAxIGV0IHJlbnNlaWduZSBzdXIgbGEgZGlzcGVyc2lvbiBkZXMgZG9ubsOpZXMgZW4gMkQuIApFbiBjYXMgZGUgY29ycsOpbGF0aW9uIGxpbsOpYWlyZSBwYXJmYWl0ZSwgaWwgdmF1dCAxIG91IC0xLgoKRm9ybWVsbGVtZW50IG9uIHBldXQgdGVzdGVyLCBhdmVjIGxlIHRlc3QgZGUgQnJhdmFpcy1QZWFyc29uIDoKCiogSDAgOiAkXHJobyA9IDAkLAoqIEgxIDogJFxyaG8gXG5lcSAwJC4KCkNvbmRpdGlvbnMgZCdhcHBsaWNhdGlvbiA6IGxlcyAkKFlfaSwgWF9pKSQgZG9pdmVudCDDqnRyZSBhcHBhcmnDqWVzIGV0IGRpc3RyaWJ1w6llcyBub3JtYWxlbWVudC4KCkNvbW1lbsOnb25zIHBhciByZXByw6lzZW50ZXIgbGVzIGRvbm7DqWVzLCBhdmVjIHVuZSBkcm9pdGUgZGUgcsOpZ3Jlc3Npb24KcGFydGFnw6kgcGFyIHRvdXMgbGVzIHBvaW50cyA6CgpgYGB7ciBmaWcud2lkdGg9MTB9CnBsb3RfZGlmZl93ZWlnaHRfdnNfY29tcGxpYW5jZSA8LSBkICU+JSAKCWdncGxvdChhZXMoeT1kaWZmX3dlaWdodCwgeD10X2NvbXBsaWFuY2UpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUYsIGZvcm11bGE9InkgfiB4IiwgY29sPWJsZXVmb25jZSkgKwogICAgdGhlbWVfYncoKSArCiAgICB4bGFiKCJ0X2NvbXBsaWFuY2UiKSArIAogICAgeWxhYigiZGlmZsOpcmVuY2UgZGUgcG9pZHMgw6AgNiBtb2lzIikKcGxvdF9kaWZmX3dlaWdodF92c19jb21wbGlhbmNlCmBgYAoKT24gcGV1dCDDoCBwcsOpc2VudCBlZmZlY3R1ZXIgdW4gdGVzdCBkZSBjb3Jyw6lsYXRpb24gZW4gcHJlbmFudCB0b3VzIGxlcyBwb2ludHMgZW5zZW1ibGUgOgoKYGBge3J9CmNvci50ZXN0KGQkZGlmZl93ZWlnaHQsIGQkdF9jb21wbGlhbmNlKQpgYGAKCj4gUXVlIGNvbmNsdW9ucy1ub3VzID8KCgojIyBVbmUgZHJvaXRlIGRhbnMgY2hhcXVlIGdyb3VwZQoKRGV1eGnDqG1lIGVzc2FpIHBsdXMgaW50w6lyZXNzYW50IDogcmVnYXJkZXIgbCdpbmZsdWVuY2UgZGUgYHRfY29tcGxpYW5jZWAKc8OpcGFyw6ltZW50IGRhbnMgbGVzIGRldXggYnJhcyBkZSBsJ2Vzc2FpIGNsaW5pcXVlLgpPbiBjb21tZW5jZSBwYXIgb2JzZXJ2ZXIgbGVzIGRvbm7DqWVzIDoKCmBgYHtyIGZpZy53aWR0aD0xMH0KcGxvdF9kaWZmX3dlaWdodF92c19jb21wbGlhbmNlXzIgPC0gZCAlPiUgCglnZ3Bsb3QoYWVzKHk9ZGlmZl93ZWlnaHQsIHg9dF9jb21wbGlhbmNlLCBjb2xvciA9IGdyb3VwKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZT1GLCBmb3JtdWxhPSJ5IH4geCIpICsKICAgIHRoZW1lX2J3KCkgKwogICAgeGxhYigidF9jb21wbGlhbmNlIikgKyAKICAgIHlsYWIoImRpZmbDqXJlbmNlIGRlIHBvaWRzIMOgIDYgbW9pcyIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YyhibGV1Y2xhaXIsIHJvc2UpKQpwbG90X2RpZmZfd2VpZ2h0X3ZzX2NvbXBsaWFuY2VfMgpnZ3NhdmUoIi4uLy4uL0ZpZ3VyZXMvaWxsdV9kaWFiZXRlX2RpZmZfd2VpZ2h0X3JlZ2xpbi5wZGYiLCBwbG90PXBsb3RfZGlmZl93ZWlnaHRfdnNfY29tcGxpYW5jZV8yLCB3aWR0aD0xMiwgaGVpZ2h0PTUpCmBgYAoKUHVpcyB1biB0ZXN0IGRlIGNvcnLDqWxhdGlvbiBlbiBwcmVuYW50IHVuaXF1ZW1lbnQgbGUgZ3JvdXBlIGBjb250cm9sYCA6CgpgYGB7cn0KZF9jb250cm9sIDwtIGQgJT4lIGZpbHRlcihncm91cCA9PSAiY29udHJvbCIpCmNvci50ZXN0KGRfY29udHJvbCRkaWZmX3dlaWdodCwgZF9jb250cm9sJHRfY29tcGxpYW5jZSkKYGBgCgpFdCBlbmZpbiwgbGUgbcOqbWUgdGVzdCBkZSBjb3Jyw6lsYXRpb24gZW4gcHJlbmFudCB1bmlxdWVtZW50IGxlIGdyb3VwZSBgdHJlYXRtZW50YCA6CgpgYGB7cn0KZF90cmVhdG1lbnQgPC0gZCAlPiUgZmlsdGVyKGdyb3VwID09ICJ0cmVhdG1lbnQiKQpjb3IudGVzdChkX3RyZWF0bWVudCRkaWZmX3dlaWdodCwgZF90cmVhdG1lbnQkdF9jb21wbGlhbmNlKQpgYGAKCj4gUXVlIGNvbmNsdW9ucy1ub3VzID8KCgojIExlIG1vZMOobGUgbGluw6lhaXJlCgojIyBEZXNjcmlwdGlvbgoKTGUgbW9kw6hsZSBsaW7DqWFpcmUgcHLDqXNlbnRlIGwnaW50w6lyw6p0IGQndW5pZmllciB0b3VzIGNlcyB0ZXN0cyBkYW5zIHVuIHVuaXF1ZSBmcmFtZXdvcmsuCgpJbCBjb25zaXN0ZSDDoCBtb2TDqWxpc2VyIHVuZSAqdmFyaWFibGUgZGUgcsOpcG9uc2UqICRZX2kkIGNoZXogbCdpbmRpdmlkdSAkaSQgCmNvbW1lIHVuZSBmb25jdGlvbiBsaW7DqWFpcmUgZGUgbm9zICp2YXJpYWJsZXMgZXhwbGljYXRyaWNlcyogJFhfe2ksan0kLCAKw6AgbGFxdWVsbGUgb24gcmFqb3V0ZSB1biAqYnJ1aXQqIEdhdXNzaWVuIGNlbnRyw6kuIApVbmUgZm9uY3Rpb24gbGluw6lhaXJlIG4nZXN0IHJpZW4gZGUgcGx1cyBxdSd1bmUgc29tbWUgcG9uZMOpcsOpZSBkZSBub3MgdmFyaWFibGVzICRYX3tpLGp9JCwgc29pdCA6CgokJCAKWV9pID0gYV8xIFhfe2ksMX0gKyBhXzIgWF97aSwyfSArIC4uLiArIGFfSiBYX3tpLEp9ICsgXGVwc2lsb25faSwgfn5+XHRleHR7IGF2ZWMgfSBcZXBzaWxvbl9pIFxzaW0gXG1hdGhjYWx7Tn0oMCwgXHNpZ21hXjIpCiQkCgpPbiBzdXBwb3NlIHF1ZSB0b3VzIGxlcyAkKFlfaSkkIHNvbnQgaW5kw6lwZW5kYW50ZXMgZXQgc3VpdmVudCBsYSBtw6ptZSBsb2kuCkxlIGJ1dCBkdSBqZXUsIHBvdXIgbGUgc3RhdGlzdGljaWVuLCBjb25zaXN0ZSDDoCBmaXR0ZXIgbGVzIHZhbGV1cnMgZGVzIGNvZWZmaWNpZW50cyAkYV9qJCBncsOiY2UgYXV4IG9ic2VydmF0aW9ucy4KCkJvbm5lIG5vdXZlbGxlIDogbGEgc3ludGF4ZSBkZSBSIHBvdXIgZml0dGVyIHVuIG1vZMOobGUgbGluw6lhaXJlIGVzdCBhc3NleiByYXBpZGUgZXQgaW50dWl0aXZlLgoKIyMgVW4gc2ltcGxlIGludGVyY2VwdAoKUHJlbWllciBjYXMgc2ltcGxlIDogb24gYWltZXJhaXQgbW9kw6lsaXNlciBsYSBkaWZmw6lyZW5jZSBkZSBwb2lkcyBjb21tZSDDqXRhbnQgdW5pcXVlbWVudCB1bmUgbW95ZW5uZSArIHVuIHBldGl0IGJydWl0IEdhdXNzaWVuLCAKZXQgY2FsY3VsZXIgY2V0dGUgbW95ZW5uZSBjb21tZSBvbiBsJ2F2YWl0IGZhaXQgbG9ycyBkdSBwcmVtaWVyIHRlc3QgZGUgU3R1ZGVudC4KCmBgYHtyfQpyZXNfbG1fMSA8LSBsbShmb3JtdWxhPSJkaWZmX3dlaWdodCB+IDEiLCBkYXRhPWQpCnN1bW1hcnkocmVzX2xtXzEpCmBgYAoKPiBWw6lyaWZpZXIgcXUnb24gcmV0b21iZSBiaWVuIHN1ciBsYSBtw6ptZSBjaG9zZSBxdWUgcHLDqWPDqWRlbW1lbnQuCgojIyBVbiBlZmZldCBkZSB2YXJpYWJsZSBkaXNjcsOodGUKCkRldXhpw6htZSBjYXMgc2ltcGxlIDogb24gYWltZXJhaXQgbW9kw6lsaXNlciBsYSBkaWZmw6lyZW5jZSBkZSBwb2lkcyBjb21tZSDDqXRhbnQgCmxhIHNvbW1lIGQndW4gY29lZmZpY2llbnQgcGFydGljdWxpZXIgY2hleiBsZXMgaW5kaXZpZHVzIGR1IGdyb3VwZSAiY29udHJvbCIgCmV0IHVuIGF1dHJlIGNvZWZmaWNpZW50IGNoZXogbGVzIGluZGl2aWR1cyBkdSBncm91cGUgInRyZWF0bWVudCIuIApTb2l0IGV4YWN0ZW1lbnQgY2UgcXUnb24gYXZhaXQgZmFpdCBhdmVjIHVuIHRlc3QgZGUgU3R1ZGVudCBjb21wYXJhbnQgbGVzIGRldXggbW95ZW5uZXMuCgpJbCBzZSB0cm91dmUgcXVlIFIsIGxvcnNxdSdvbiBsdWkgZG9ubmUgw6AgbWFuZ2VyIHVuZSB2YXJpYWJsZSDDoCBwbHVzaWV1cnMgbW9kYWxpdMOpcywgCnZhIGF1dG9tYXRpcXVlbWVudCBsYSAic2NpbmRlciIgZW4gc291cy12YXJpYWJsZXMgaW5kaWNhdHJpY2VzIGRlIGNoYXF1ZSBtb2RhbGl0w6kuIApMYSBzeW50YXhlIHJlc3RlIGRvbmMgdHLDqHMgc2ltcGxlIDoKCmBgYHtyfQojIHByZW1pw6hyZSB2ZXJzaW9uIGF2ZWMgbW95ZW5uZSBjb21tdW5lIGF1eCBkZXV4IGdyb3VwZXMKIyBldCB1biBwZXRpdCBham91dCBwb3VyIGxlIGdyb3VwZSB0cmFpdGVtZW50CnJlc19sbV9ncm91cCA8LSBsbShmb3JtdWxhPSJkaWZmX3dlaWdodCB+IGdyb3VwIiwgZGF0YT1kKQpzdW1tYXJ5KHJlc19sbV9ncm91cCkKYGBgCgpgYGB7cn0KIyBkZXV4acOobWUgdmVyc2lvbiBhdmVjIG1veWVubmUgZGFucyBjaGFxdWUgZ3JvdXBlCnJlc19sbV9ncm91cF8yIDwtIGxtKGZvcm11bGE9ImRpZmZfd2VpZ2h0IH4gMCArIGdyb3VwIiwgZGF0YT1kKQpzdW1tYXJ5KHJlc19sbV9ncm91cF8yKQpgYGAKCk9uIGEgZG9uYyBleGFjdGVtZW50IGxlIG3Dqm1lIG1vZMOobGUgc291cy1qYWNlbnQsIGF2ZWMgZGVzIGVzdGltYXRpb25zIGRlIHBhcmFtw6h0cmVzIGlkZW50aXF1ZXMuIApDZXBlbmRhbnQsIGljaSwgbGVzIHAtdmFsdWUgaW5kaXF1w6llcyBjb3JyZXNwb25kZW50IMOgIHVuIHRlc3QgZCdhZMOpcXVhdGlvbiBkdSBwYXJhbcOodHJlIMOgIDAuIApTaSBvbiBzb3VoYWl0ZSBjb21wYXJlciBsZXMgZGV1eCBtb3llbm5lcyBldCByZXRvbWJlciBzdXIgbGUgbcOqbWUgcsOpc3VsdGF0IHF1J2F1IGTDqWJ1dCBkdSBkb2N1bWVudCwgCmlsIGZhdXQgZG9uYyBwYXNzZXIgcGFyIGxhIHByZW1pw6hyZSB2ZXJzaW9uIGV0IHJlZ2FyZGVyIGxlIHRlc3QgZCdhZMOpcXVhdGlvbiBkdSBwYXJhbcOodHJlIMOgIDAuCgpUcm9pc2nDqG1lIHBvc3NpYmlsaXTDqSwgbm90cmUgZGlmZsOpcmVuY2UgZGUgcG9pZHMgZMOpcGVuZCBkJ3VuZSB2YXJpYWJsZSBwcmVuYW50IDMgbW9kYWxpdMOpcyBvdSBwbHVzIAooY29tbWUgb24gYXZhaXQgcHJvcG9zw6kgbG9ycyBkZSBsYSByw6lhbGlzYXRpb24gZHUgdGVzdCBBTk9WQSBjaS1kZXNzdXMpLiAKTGEgbcOqbWUgc3ludGF4ZSBmb25jdGlvbm5lIHRvdWpvdXJzIGF1c3NpIGJpZW4gOgoKYGBge3J9CnJlc19sbV9ncm91cF8zIDwtIGxtKGZvcm11bGE9ImRpZmZfd2VpZ2h0IH4gMCArIGFnZV9ncm91cCIsIGRhdGE9ZCkKc3VtbWFyeShyZXNfbG1fZ3JvdXBfMykKYGBgCgpMJ291dHB1dCBzdGFuZGFyZCBkdSBtb2TDqGxlIGxpbsOpYWlyZSBuZSBub3VzIGNhbGN1bGUgZGVzIHAtdmFsdWVzCnF1ZSBwb3VyIGxlcyB0ZXN0cyBkJ2Fkw6lxdWF0aW9uIGRlIGNoYXF1ZSBwYXJhbcOodHJlIMOgIDAuCkJpZW4gcXVlIGwnQU5PVkEgc29pdCBlZmZlY3RpdmVtZW50IHVuIHRlc3QgcsOpYWxpc8OpIGF1IHNlaW4gZHUgbcOqbWUKZnJhbWV3b3JrIGRlIG1vZMOobGUgbGluw6lhaXJlLCBsYSBwLXZhbHVlIGFzc29jacOpZSBuJ2VzdCByw6ljdXDDqXJhYmxlLAplbiBwcmF0aXF1ZSBhdmVjIFIsIHF1ZSB2aWEgbGEgZm9uY3Rpb24gYGFvdmAgdnVlIHByw6ljw6lkZW1tZW50LgoKIyMgVW4gZWZmZXQgZGUgdmFyaWFibGUgY29udGludWUKCkRlcm5pZXIgZXhlbXBsZSBzaW1pbGFpcmUgw6AgY2UgcXUnb24gYXZhaXQgZmFpdCBwcsOpY8OpZGVtbWVudCA6IApleHBsaXF1ZXIgbm90cmUgdmFyaWFibGUgZGUgcsOpcG9uc2UgImRpZmbDqXJlbmNlIGRlIHBvaWRzIiBwYXIgbGUgInRlbXBzIGRlIGNvbXBsaWFuY2UiLgoKYGBge3J9CnJlc19sbV9jb21wbGlhbmNlIDwtIGxtKGZvcm11bGE9ImRpZmZfd2VpZ2h0IH4gdF9jb21wbGlhbmNlIiwgZGF0YT1kKQpzdW1tYXJ5KHJlc19sbV9jb21wbGlhbmNlKQpgYGAKCiMjIFZlcnMgcGx1cyBkZSBjb21wbGV4aWZpY2F0aW9uCgpFdCBzaSBvbiBzb3VoYWl0ZSB1biBlZmZldCBhZGRpdGlmIGR1IGdyb3VwZSBldCBkdSB0ZW1wcyBkZSBjb21wbGlhbmNlIDoKCmBgYHtyfQpyZXNfbG1fY29tcGxpYW5jZV9ncm91cCA8LSBsbShmb3JtdWxhPSJkaWZmX3dlaWdodCB+IDAgKyBncm91cCArIHRfY29tcGxpYW5jZSIsIGRhdGE9ZCkKc3VtbWFyeShyZXNfbG1fY29tcGxpYW5jZV9ncm91cCkKYGBgCgpDZSBuJ2VzdCBwYXMgZXhhY3RlbWVudCBjZSBxdSdvbiBhdmFpdCBmYWl0IHRvdXQgw6AgbCdoZXVyZSBzdXIgbGUgZ3JhcGhlIDogCm9uIGF2YWl0IGFsb3JzIGRldXggcsOpZ3Jlc3Npb25zIGxpbsOpYWlyZXMsIHVuZSBkYW5zIGNoYXF1ZSBncm91cGUuIApSIHBvc3PDqGRlIGVuY29yZSB1bmUgZm9pcyB1bmUgc3ludGF4ZSBwcmF0aXF1ZSBwb3VyIMOnYSA6CgpgYGB7cn0KcmVzX2xtX2NvbXBsaWFuY2VfZ3JvdXBfMiA8LSBsbShmb3JtdWxhPSJkaWZmX3dlaWdodCB+IDAgKyBncm91cCAqIHRfY29tcGxpYW5jZSIsIGRhdGE9ZCkKc3VtbWFyeShyZXNfbG1fY29tcGxpYW5jZV9ncm91cF8yKQpgYGAKCk9uIGEgaW50cm9kdWl0IHN1cGVyIGZhY2lsZW1lbnQgY2UgcXUnb24gYXBwZWxsZSBkZXMgKmludGVyYWN0aW9ucyogZW50cmUgbm9zIHZhcmlhYmxlcy4KTGUgbW9kw6hsZSBsaW7DqWFpcmUsIGV0IGxhIHN5bnRheGUgZGUgUiwgc29udCBkb25jIGRlcyBvdXRpbHMgZGUgbW9kw6lsaXNhdGlvbiBleHRyw6ptZW1lbnQgcHVpc3NhbnRzL3ZlcnNhdGlsZXMuCklscyBkZW1hbmRlbnQgw6AgYmllbiByw6lmbMOpY2hpciBlbiBhbW9udCDDoCBjZSBxdSdvbiBzb3VoYWl0ZSDDqXR1ZGllciAKcG91ciDDqXZpdGVyIHVuZSB0cm9wIGdyYW5kZSBtdWx0aXBsaWNhdGlvbiBkZSB0ZXN0cyBzdGF0aXN0aXF1ZXMuCgo+IFF1ZWwgc2VyYWl0IHNlbG9uIHZvdXMgbGUgbW9kw6hsZSBsaW7DqWFpcmUgbGUgcGx1cyBhcHByb3ByacOpIHBvdXIgYGRpZmZfd2VpZ2h0YCA/Cgo+IFF1ZWwgc2VyYWl0IHNlbG9uIHZvdXMgbGUgbW9kw6hsZSBsaW7DqWFpcmUgbGUgcGx1cyBhcHByb3ByacOpIHBvdXIgYGRpZmZfc3RlcHNgID8KCiMgQ29uY2x1c2lvbgoKTm91cyBhdm9ucyB2dSBpY2kgdW4gY2VydGFpbiBub21icmUgZGUgbcOpdGhvZGVzIHBlcm1ldHRhbnQgZCdhbmFseXNlciBkZXMgdmFyaWFibGVzIGNvbnRpbnVlcy4KTGVzIHBvaW50cyBwcmluY2lwYXV4IMOgIHJldGVuaXIgc29udCBsZXMgc3VpdmFudHMgOgoKMS4gUG91ciBjb21wYXJlciBkZXV4IG1veWVubmVzLCBvdSB1bmUgbW95ZW5uZSDDoCB1bmUgbm9ybWUsIGxlIFQgdGVzdCBlc3QgbGEgYm9ubmUgb3B0aW9uLAogICDDoCBjb25kaXRpb24gcXVlIGxlcyBkb25uw6llcyBzb2llbnQgZW4gZ3JhbmQgbm9tYnJlLCBvdSBxdSdlbGxlcyBzb2llbnQgZGlzdHJpYnXDqWVzIG5vcm1hbGVtZW50LgoyLiBQb3VyIGNvbXBhcmVyIHBsdXMgcXVlIGRldXggbW95ZW5uZXMsIHVuZSBBTk9WQSBmYWl0IGwnYWZmYWlyZSwgc291cyBkZXMgY29uZGl0aW9ucyBpZGVudGlxdWVzLgozLiBMb3JzcXVlIGxlcyBjb25kaXRpb25zIGQnYXBwbGljYXRpb24gZGVzIHRlc3RzIHBhcmFtw6l0cmlxdWVzIG5lIHNvbnQgcGFzIHJlbmNvbnRyw6llcywKICAgb24gcGV1dCByZWNvdXJpciDDoCBkZXMgdGVzdHMgbm9uLXBhcmFtw6l0cmlxdWVzIDogV2lsY294b24gZXQgS3J1c2thbC1XYWxsaXMuCjQuIFBvdXIgcXVhbnRpZmllciBsYSBjb3Jyw6lsYXRpb24gZW50cmUgZGV1eCB2YXJpYWJsZXMgY29udGludWVzIGRpc3RyaWJ1w6llcyBub3JtYWxlbWVudCwKICAgdW4gdGVzdCBkZSBjb3Jyw6lsYXRpb24gZGUgUGVhcnNvbiBwZXV0IMOqdHJlIHV0aWxpc8OpLgo1LiBMZSBtb2TDqGxlIGxpbsOpYWlyZSB1bmlmaWUgdG91cyBsZXMgdGVzdHMgcGFyYW3DqXRyaXF1ZXMgcHLDqWPDqWRlbnRzIGRhbnMgdW4gdW5pcXVlIGZyYW1ld29yaywKICAgb8O5IHVuZSB2YXJpYWJsZSBkZSByw6lwb25zZSBjb250aW51ZSAkWSQgZXN0IG1vZMOpbGlzw6llIGNvbW1lIHVuZSBjb21iaW5haXNvbiBsaW7DqWFpcmUKICAgZGUgdmFyaWFibGVzIGV4cGxpY2F0aXZlcyAkWCQgKGRpc2Nyw6h0ZXMgb3UgY29udGludWVzKS4KCgo=