L’analyse de survie est centrale dans les essais cliniques, où de nombreuses mesures concernent des “temps avant événements”. De nombreux essais, particulièrement dans le domaine de la cancérologie, s’intéressent par exemple à :
- la survie globale (OS : Overall Survival),
- ou à la survie sans progression (PFS : Progression-Free Survival).
L’objectif de ce TD est d’aborder les méthodes de bases utilisées pour analyser ces “temps d’attente”, quels qu’ils soient.
On aura besoin des packages suivants :
# comme toujours, nous utiliserons dans ce tutoriel la syntaxe du "tidyverse"
library(tidyverse)
# nous utiliserons "cowplot" pour faire des figures multiples facilement
library(cowplot)
# ainsi que les deux packages suivants dédiés aux analyses de survie
library(survival)
library(survminer)
knitr::opts_chunk$set(echo = T, fig.width=10, warning=F, message=F)
Exemple du TD
On prend ici un exemple fictif d’essai clinique randomisé contrôlé dans le cancer du pancréas, en deux groupes, l’un recevant un traitement et l’autre recevant un placebo.
- L’objectif principal consiste à montrer la supériorité du traitement sur la survie globale des patients.
- L’objectif secondaire consiste à montrer la supériorité du traitement sur la survie sans progression.
L’inclusion des patients s’est faite sur une durée de deux ans. A l’issue de la période d’inclusion, l’étude a duré 3 ans. Certains patients peuvent être suivis 5 ans (premiers inclus) ou 3 (derniers inclus). Mais certains patients peuvent également être perdus de vus (peut-être ont-ils déménagé ailleurs, peut-être ne répondent-ils plus au téléphone…).
Pour chaque patient, un certain nombre de temps sont enregistrés dans la base de données :
d <- read.csv("data_survival.csv")
head(d)
## id arm age date_rando date_death date_prog date_last_follow_up
## 1 1 control 38 2018-09-08 2019-12-15 <NA> 2019-12-15
## 2 2 control 53 2018-01-20 2020-03-06 2019-03-02 2020-03-06
## 3 3 control 58 2018-02-08 <NA> 2019-05-05 2023-01-15
## 4 4 control 54 2019-01-11 2019-09-22 2019-07-14 2019-09-22
## 5 5 control 44 2018-05-25 2020-06-18 <NA> 2020-06-18
## 6 6 control 45 2018-06-01 2019-07-23 <NA> 2019-07-23
Prenez 3 min pour explorer la façon dont ce jeu de données est construit. Dans quels cas a-t-on une date de décès inconnue ? Quelle est la date de début de l’essai ? De clôture de l’essai ?
Le package lubridate
et ses fonctions ymd()
, dmy()
ou mdy()
est bien utile pour interpréter les chaînes de caractères comme des dates, de façon à pouvoir les manipuler pour en tirer des durées entre deux dates.
d <- d %>%
mutate(
date_death = ymd(date_death),
date_rando = ymd(date_rando),
date_last_follow_up = ymd(date_last_follow_up),
date_prog = ymd(date_prog)
)
Premiers essais naïfs
Avant de s’intéresser à ce qu’on peut faire de plus fancy à partir de ces temps d’attente, réfléchissons à ce que nous serions déjà en mesure de faire avec les outils que nous connaissons déjà.
Proposez ci-dessous une analyse de la probabilité de décès à 6 mois entre les deux groupes.
d <- d %>% mutate(
time_before_death = as.numeric(date_death - date_rando),
is_alive_6m = (date_last_follow_up - date_rando) > 365/2,
is_alive_24m = (date_last_follow_up - date_rando) > 365*2,
)
table(d$arm, d$is_alive_6m)
##
## FALSE TRUE
## control 20 280
## treatment 14 286
prop.test(table(d$arm, d$is_alive_6m))
##
## 2-sample test for equality of proportions with continuity correction
##
## data: table(d$arm, d$is_alive_6m)
## X-squared = 0.77946, df = 1, p-value = 0.3773
## alternative hypothesis: two.sided
## 95 percent confidence interval:
## -0.02029847 0.06029847
## sample estimates:
## prop 1 prop 2
## 0.06666667 0.04666667
Puis une analyse de la probabilité de décès à 2 ans entre les deux groupes.
table(d$arm, d$is_alive_24m)
##
## FALSE TRUE
## control 118 182
## treatment 96 204
prop.test(table(d$arm, d$is_alive_24m))
##
## 2-sample test for equality of proportions with continuity correction
##
## data: table(d$arm, d$is_alive_24m)
## X-squared = 3.2032, df = 1, p-value = 0.07349
## alternative hypothesis: two.sided
## 95 percent confidence interval:
## -0.006432081 0.153098747
## sample estimates:
## prop 1 prop 2
## 0.3933333 0.3200000
Puis une analyse des temps d’attente avant décès avec un test de Wilcoxon.
wilcox.test(time_before_death ~ arm, data=d)
##
## Wilcoxon rank sum test with continuity correction
##
## data: time_before_death by arm
## W = 18038, p-value = 0.3132
## alternative hypothesis: true location shift is not equal to 0
On a deux reproches principaux à faire à ces approches naïves :
- d’une part, si on analyse des proportions de survivants, il nous faut définir UN temps auquel on regarde la survie, ce qui est loin d’être évident à décider avant de faire l’étude. Répéter l’analyse pour un grand nombre de temps différents serait une très mauvaise pratique amenant à une inflation du risque de première espèce.
- d’autre part, on perd toute l’information à propos des durées de survie des patients qui ont été suivi un peu mais pas suffisamment pour qu’on ait observé l’événement. C’est cette information qu’on va chercher à capter grâce à la notion de censure qu’on introduit juste après.
Censure
Soit \(T_i :=\) le temps d’attente avant décès de l’individu \(i\).
Lorsqu’on traite de données de temps d’attente dans la vraie vie, on se retrouve souvent dans des cas où on ne peut pas observer exactement le temps de survenue d’un événément, mais où on possède malgré tout une information parcellaire à propos de ce temps. On dit que nos données sont censurées (à droite/à gauche).
- censure à gauche : si on sait que \(T_i < t_i^+\).
- censure à droite : si on sait que \(T_i > t_i^-\).
- censure à gauche et à droite : si on sait que \(T_i \in (t_i^-, t_i^+)\).
Dans quel cas se trouve-t-on ici avec nos perdus de vue ?
La plupart du temps, dans les essais cliniques, nous aurons des données qui correspondent à des censures à droite, c’est à dire à des temps de follow-up sans événement, qui nous apportent tout de même une information énorme sur ce qu’il ne s’est pas passé jusqu’à ce temps.
Toutes les méthodes qui suivent vont donc parvenir à gérer cette censure à droite.
Survie et estimation de Kaplan-Meier
Le problème de l’estimation de la courbe de survie consiste à estimer la courbe :
\[
S(t) = \mathbb{P}(T > t)
\]
à partir de données potentiellement censurées à droite.
Le principe de l’approche de Kaplan-Meier est le suivant :
- le temps est découpé en sous-intervalles pendant lesquels il ne se passe aucun décès.
- sur un intervalle \(i\) avec \(n_i\) individus susceptibles de décéder au début de l’intervalle et \(d_i\) décès à la fin de l’intervalle, la probabilité de survivre est estimée comme \((1 - \frac{d_i}{n_i})\).
- donc la survie jusqu’à ce temps correspond au produit des probabilités de survie de tous les intervalles précédents.
La formule d’estimation de la courbe de survie par la méthode de Kaplan-Meier s’écrit donc de façon plus concise comme :
\[
\hat{S}(t) = \prod_{i: t_i < t} \left( 1 - \frac{d_i}{n_i} \right)
\]
La censure se prend en compte très naturellement ici en réduisant \(n_i\) à chaque fois qu’un individu est perdu de vue. Un “tick” est alors visuellement noté sur la courbe de survie pour signifier qu’un individu est perdu sans être décédé.
Avec R, on utilise la fonction survfit
et ggsurvplot
qui prennent en entrée :
- un temps avant survenue d’un événément (time)
- une valeur binaire 1 si un décès survient ou 0 si une censure survient à ce temps (event)
- une éventuelle covariable correspondant à des groupes pour lesquels on souhaite tracer des courbes de survie différentes.
Préparez le jeu de données pour avoir time
et event
proprement définis (regardez l’aide de Surv
) et utilisez le morceau de code qui suit pour tracer les courbes de survie de Kaplan-Meier.
Sentez vous libres de jouer avec les différentes options listées. Les amateurs de ggplot2 pourront accéder au graphe ggplot2 modifiable avec l’objet final$plot
.
d <- d %>% mutate(
time = as.numeric(date_last_follow_up - date_rando),
event = ifelse(!is.na(date_death), 1, 0)
)
km_os <- survfit(Surv(time, event) ~ arm, data=d)
graph_os <- ggsurvplot(
km_os,
pval = F,
conf.int = FALSE,
risk.table = F, # Add risk table
risk.table.col = "strata", # Change risk table color by groups
#linetype = "strata", # Change line type by groups
surv.median.line = "hv", # Specify median survival
ggtheme = theme_bw() # Change ggplot2 theme
)
graph_os

Notez que ce graphique repose sur plusieurs hypothèses qui seront également implicites dans la suite :
- On suppose que les individus qui sont censurés ont les mêmes chances de survie que les autres, i.e. que la censure n’est pas informative d’un futur décès,
- On suppose que les survies sont identiques pour les sujets recrutés tôt et tard dans l’étude, de façon à pouvoir ré-aligner tout le monde sur un t0 qui correspond à la randomisation.
Il nous reste un dernier item à discuter en lien avec ces analyses de survie, la notion de médiane de survie qui est une quantité pratique à se représenter que tout le monde aimerait partager. Oui mais voilà, même une notion aussi simple s’avère être propice à faire n’importe quoi avec nos problèmes de temps d’attente !
Testez ce que ça donne de prendre naïvement la médiane des temps de décès.
Puis testez ce que ça donne de prendre naïvement la médiane des temps de follow-up.
d %>% group_by(arm) %>%
summarise(
median_t_death = median(time_before_death, na.rm=T),
median_t_follow = median(time)
)
## # A tibble: 2 × 3
## arm median_t_death median_t_follow
## <chr> <dbl> <dbl>
## 1 control 714 871
## 2 treatment 776 1132.
Réfléchissez à la signification de ces deux quantités. En réalité, ce qu’on cherche quand on s’intéresse à la médiane de survie, c’est la valeur \(t_m\) telle que \(S(t_m) = 1/2\).
On l’obtient avec le code qui suit :
## strata median lower upper
## 1 arm=control 902 836 1072
## 2 arm=treatment 1213 1145 1319
Hazard et hazard ratio
Le “hazard” \(h(t)\), parfois aussi appelé “hazard rate” est le taux de survenue de l’événement au temps \(t\) sachant qu’on a survécu jusqu’au temps \(t\).
\[
h(t) := lim_{dt \rightarrow 0} ~ \frac{\mathbb{P}(T \in (t, t+dt) | T > t)}{dt}
\]
Le hazard est positif en tout temps \(t\) mais n’est pas nécessairement monotone.
Si on parle tant de cette quantité en analyse de survie, c’est que les principaux tests statistiques proposés dans le domaine pour comparer les survies de deux groupes 1 et 2 reposent sur l’hypothèse suivante :
\[
\exists K \in \mathbb{R}^+, \forall t, ~ \frac{h_1(t)}{h_2(t)} = K
\]
C’est l’hypothèse des hazard proportionnels ou du hazard ratio constant.
Notez qu’on ne fait pas d’hypothèse forte sur la forme de la fonction du hazard, mais qu’on a besoin de faire l’hypothèse que cette forme est similaire dans les deux groupes d’intérêt.
Pour se fixer un peu plus les idées, voici deux illustrations de relation entre hazards et survie dans le cadre de deux lois théoriques différentes.
Hazards constants et loi exponentielle
Pour voir ce que ça donne avec une distribution que vous avez des chances de connaître, la distribution exponentielle. Comme vous l’avez sans doute déjà entendu dire, la loi exponentielle est “sans mémoire”, c’est à dire que le taux de survenue d’un événément, sachant que l’événement n’est pas encore survenu (le hazard) est constant ! Voici donc une première illustration à se mettre en tête :
t <- seq(0,10,0.1)
rates <- c(0.5,1,2)
distrib_exp <- data.frame(
t = rep(t, each=length(rates)),
density = dexp(rep(t,each=length(rates)), rate=rep(rates, length(t))),
survival = pexp(rep(t,each=length(rates)), rate=rep(rates, length(t)), lower.tail=F),
lambda = as.factor(rep(rates, length(t))),
hazard = rep(rates, length(t))
)
p1 <- distrib_exp %>% ggplot(aes(x=t, y=density, color=lambda)) +
xlab("t") +
ylab("Densité : P(t < T < t+dt)/dt") +
geom_line() +
theme_bw() +
theme(legend.position = "none") +
scale_color_manual(values=c("#548687", "#ff5555", "#8fbc94"))
p2 <- distrib_exp %>% ggplot(aes(x=t, y=survival, color=lambda)) +
xlab("t") +
ylab("Survie : P(T>t)") +
geom_line() +
theme_bw() +
theme(legend.position = "none") +
scale_color_manual(values=c("#548687", "#ff5555", "#8fbc94"))
p3 <- distrib_exp %>% ggplot(aes(x=t, y=hazard, color=lambda)) +
xlab("t") +
ylab("Hazard : P(t < T < t+dt | T > t)/dt") +
geom_line() +
theme_bw() +
scale_color_manual(values=c("#548687", "#ff5555", "#8fbc94"))
plot_grid(p1, p2, p3, ncol = 3, nrow = 1)

Hazards d’une loi de Weibull
Histoire de ne pas retenir que ce qui se passe dans le cas archi-simple de la loi exponentielle, voici une seconde loi très employée dans le domaine de l’analyse de survie : la loi de Weibull.
Elle a une propriété rigolote, qui est uniquement propre à cette famille de distribution : on peut l’utiliser à la fois dans le cadre d’une hypothèse de hazards proportionnels et dans le cadre d’une hypothèse d’accelerated failure time, c’est à dire que la structure de la distribution est telle qu’une augmentation de hazard d’un certain facteur accélère la survie d’un autre facteur.
Remarquez aussi que la loi exponentielle appartient à la famille des loi de Weibull, et que la même propriété était donc vraie sur le graphe ci-dessus. Simplement à présent on peut spécifier des hazards non-constants dans le cadre d’une loi de Weibull.
t <- seq(0,5,0.01)
shape <- 1.6
scales <- c(1, 2)
distrib_weibull <- data.frame(
t = rep(t, each=length(scales)),
density = dweibull(rep(t,each=length(scales)), shape=shape, scale=rep(scales, length(t))),
survival = pweibull(rep(t,each=length(scales)), shape=shape, scale=rep(scales, length(t)), lower.tail=F),
scale = as.factor(rep(scales, length(t)))
)
distrib_weibull <- distrib_weibull %>% mutate(hazard = density / survival)
p1 <- distrib_weibull %>% ggplot(aes(x=t, y=density, color=scale)) +
xlab("t") +
ylab("Densité : P(t < T < t+dt)/dt") +
geom_line() +
theme_bw() +
theme(legend.position = "none") +
scale_color_manual(values=c("#548687", "#ff5555", "#8fbc94"))
p2 <- distrib_weibull %>% ggplot(aes(x=t, y=survival, color=scale)) +
xlab("t") +
ylab("Survie : P(T > t)") +
geom_line() +
theme_bw() +
theme(legend.position = "none",
plot.title = element_text(hjust=0.5)) +
ggtitle("S_1(t) = S_0(0.5 x t)") +
scale_color_manual(values=c("#548687", "#ff5555", "#8fbc94"))
p3 <- distrib_weibull %>% ggplot(aes(x=t, y=hazard, color=scale)) +
xlab("t") +
ylab("Hazard : P(t < T < t+dt | T > t)/dt") +
geom_line() +
theme_bw() +
labs(color = "sigma") +
theme(plot.title = element_text(hjust=0.5)) +
ggtitle("h_1(t) = 0.33 h_0(t)") +
scale_color_manual(values=c("#548687", "#ff5555", "#8fbc94"))
plot_grid(p1, p2, p3, ncol = 3, nrow = 1)

Test du logrank
Le test du logrank permet de répondre à la question suivante :
- H0 : HR = 1
- H1 : HR \(\neq\) 1.
La principale condition d’application pour le réaliser consiste à supposer que nos hazards sont proportionnels.
La syntaxe R pour le réaliser suit.
survdiff(Surv(time, event) ~ arm, data=d)
## Call:
## survdiff(formula = Surv(time, event) ~ arm, data = d)
##
## N Observed Expected (O-E)^2/E (O-E)^2/V
## arm=control 300 213 182 5.14 9.63
## arm=treatment 300 180 211 4.45 9.63
##
## Chisq= 9.6 on 1 degrees of freedom, p= 0.002
Que concluez-vous de ce test ?
Modèle de Cox
Si \((X_1^{(i)}, X_2^{(i)}, ..., X_p^{(i)})\) sont \(p\) variables explicatives mesurées pour l’individu \(i\), le modèle de Cox consiste à relier le hazard de l’individu \(i\) avec ces covariables selon la forme suivante :
\[
h_i(t) = h_0(t) e^{\sum_{j=1}^p \beta_j X_j^{(i)}}
\]
Comme pour d’autres modèles de régression que vous connaissez déjà, vous pouvez imaginer inclure dans ce modèle tout type de variable :
- avec une variable numérique, l’exponentielle du paramètre correspond au facteur multiplicatif du hazard ratio associé à une augmentation de +1 unité de la variable.
- avec une variable catégorielle, on a un paramètre par modalité (moins la modalité de référence), et l’exponentielle du paramètre correspond au facteur multiplicatif du hazard ratio associé au fait d’avoir cette modalité.
Le code suivant vous donne la syntaxe permettant de fitter le modèle de Cox sur nos données, pour réaliser un test similaire au test du logrank présenté précédemment. Notez que ces deux tests reposent sur des théorèmes différents, mais qu’ils sont “asymptotiquement équivalents”, c’est à dire qu’ils devraient vous donner des p-values similaires pour un nombre d’observations assez grand. En pratique, pour éviter toute multiplication de tests, on décide AVANT de regarder les données quel test on réalisera, on l’écrit dans le plan d’analyse statistique, puis on l’applique.
coxph(Surv(time, event) ~ arm, data=d)
## Call:
## coxph(formula = Surv(time, event) ~ arm, data = d)
##
## coef exp(coef) se(coef) z p
## armtreatment -0.3134 0.7309 0.1014 -3.091 0.002
##
## Likelihood ratio test=9.6 on 1 df, p=0.001949
## n= 600, number of events= 393
Obtient-on un résultat cohérent avec le test du logrank dans notre cas ?
Modifiez le code précédent pour rajouter des covariables au modèle. Observez-vous un effet de l’âge des patients sur le hazard ?
coxph(Surv(time, event) ~ arm + age, data=d)
## Call:
## coxph(formula = Surv(time, event) ~ arm + age, data = d)
##
## coef exp(coef) se(coef) z p
## armtreatment -0.315136 0.729690 0.101444 -3.107 0.00189
## age 0.004706 1.004718 0.007534 0.625 0.53215
##
## Likelihood ratio test=9.99 on 2 df, p=0.006781
## n= 600, number of events= 393
Evénements intercurrents et PFS
Vous souhaitez à présent vous attaquer à la deuxième question d’intérêt de l’étude : existe-t-il un impact du traitement sur l’évolution des tumeurs ? On souhaite donc regarder \(T_i' :=\) le temps avant progression de l’individu \(i\).
MAIS : si on fait cela, on va avoir deux problèmes :
- d’une part certains individus sont perdus de vue avant de progresser, ce qu’on est prêt à prendre en compte en censurant à droite.
- d’autre part certains individus risquent de décéder AVANT de progresser.
Lorsqu’un événement (ici la mort) empêche d’observer un autre événement (la progression), on parle d’événements intercurrents. Il existe plusieurs façons de prendre en compte ce problème, mais on ne s’intéressera ici qu’à la plus simple : lorsque nos deux événements sont tous deux négatifs (ou positifs) pour le patients, le plus simple est de définir notre temps d’intérêt comme étant le premier temps auquel l’un des deux événements survient.
Ici, on définit classiquement en cancérologie la PFS comme étant la survie sans progression (ni décès). Et le temps d’intérêt associé est donc le décès ou la progression : le premier des deux qui survient.
Estimez la PFS à l’aide d’un graphe de Kaplan-Meier.
Comparez la PFS dans les deux bras à l’aide d’un test de logrank ou d’un modèle de Cox.
Que concluez-vous ?
d <- d %>% mutate(
time_prog_or_death = as.numeric(ifelse(!is.na(date_prog), date_prog - date_rando, date_last_follow_up - date_rando)),
event_prog_or_death = ifelse(!is.na(date_prog) | !is.na(date_death), 1, 0)
)
km_pfs <- survfit(Surv(time_prog_or_death, event_prog_or_death) ~ arm, data=d)
graph_pfs <- ggsurvplot(
km_pfs,
pval = F,
conf.int = FALSE,
risk.table = F, # Add risk table
risk.table.col = "strata", # Change risk table color by groups
#linetype = "strata", # Change line type by groups
surv.median.line = "hv", # Specify median survival
ggtheme = theme_bw() # Change ggplot2 theme
)
cox_pfs <- coxph(Surv(time_prog_or_death, event_prog_or_death) ~ arm, data=d)
Conclusion/Discussion
Dans les essais cliniques, la norme pour s’intéresser à un temps d’attente avant événement sera souvent le duo représentation graphique de Kaplan-Meier et modèle de Cox associé. L’utilisation de ces deux concepts stats est simple, mais il faudra toujours bien prendre garde :
- à bien spécifier les événements considérés comme censure,
- à bien réfléchir aux éventuels événements inter-currents et à la définition de notre temps d’intérêt.
Dans les études observationnelles, il est également possible de faire de l’analyse de survie. Les exemples ne manquent d’ailleurs pas dans la litérature.
MAIS ! Il vous faudra garder à l’esprit deux problèmes majeurs dans ces cas-là. Contrairement à l’essai clinique, la définition du \(t_0\) (randomisation dans l’essai clinique) est difficile à réaliser de façon “juste et équitable” pour les deux groupes. Les deux principaux problèmes rencontrés sont les suivants :

Si on définit les individus d’un groupe de traitement comme étant tous ceux qui ont déjà commencé le traitement avant une certaine date, alors on s’expose à un biais de déplétion des susceptibles. Typiquement, imaginez des fumeurs et des non-fumeurs. Les individus susceptibles de faire un cancer du poumon dans le groupe fumeur ont peut-être déjà tous fait leur cancer, ce qui risque de diminuer le nombre de cancers du poumon observés dans le groupe fumeur.
Si maintenant on souhaite éviter ce problème et qu’on définit quelqu’un dans le groupe traitement comme quelqu’un qui n’avait pas commencé le traitement à \(t_0\), mais le commence plus tard. Alors on obtient, cette fois, un biais de temps immortel, qui correspond au fait que les patients de ce groupe traitement ne peuvent pas décéder (par construction) avant la date de traitement qui les fait appartenir à leur groupe.
Un nombre é-nor-me d’études observationnelles sont concernées par l’un, l’autre, ou les deux problèmes à la fois, et plusieurs exemples historiques de désaccords entre études observationnelles et essais cliniques reposent sur ces deux problème de \(t_0\). Gardez bien ça en tête pour critiquer les futurs articles que vous lirez !
LS0tCnRpdGxlOiAiQW5hbHlzZXMgZGUgc3VydmllIgphdXRob3I6ICJNYXJjIE1hbmNlYXUiCmRhdGU6ICIyMDI1LTAzIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBoaWdobGlnaHQ6ICJ0YW5nbyIKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKLS0tCgpMJ2FuYWx5c2UgZGUgc3VydmllIGVzdCBjZW50cmFsZSBkYW5zIGxlcyBlc3NhaXMgY2xpbmlxdWVzLCBvw7kgZGUgbm9tYnJldXNlcyBtZXN1cmVzIGNvbmNlcm5lbnQgZGVzICJ0ZW1wcyBhdmFudCDDqXbDqW5lbWVudHMiLgpEZSBub21icmV1eCBlc3NhaXMsIHBhcnRpY3VsacOocmVtZW50IGRhbnMgbGUgZG9tYWluZSBkZSBsYSBjYW5jw6lyb2xvZ2llLCBzJ2ludMOpcmVzc2VudCBwYXIgZXhlbXBsZSDDoCA6CgoqIGxhIHN1cnZpZSBnbG9iYWxlIChPUyA6IE92ZXJhbGwgU3Vydml2YWwpLAoqIG91IMOgIGxhIHN1cnZpZSBzYW5zIHByb2dyZXNzaW9uIChQRlMgOiBQcm9ncmVzc2lvbi1GcmVlIFN1cnZpdmFsKS4KCkwnb2JqZWN0aWYgZGUgY2UgVEQgZXN0IGQnYWJvcmRlciBsZXMgbcOpdGhvZGVzIGRlIGJhc2VzIHV0aWxpc8OpZXMgcG91ciBhbmFseXNlciBjZXMgInRlbXBzIGQnYXR0ZW50ZSIsIHF1ZWxzIHF1J2lscyBzb2llbnQuCgpPbiBhdXJhIGJlc29pbiBkZXMgcGFja2FnZXMgc3VpdmFudHMgOgoKYGBge3IgbWVzc2FnZT1GfQojIGNvbW1lIHRvdWpvdXJzLCBub3VzIHV0aWxpc2Vyb25zIGRhbnMgY2UgdHV0b3JpZWwgbGEgc3ludGF4ZSBkdSAidGlkeXZlcnNlIgpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBub3VzIHV0aWxpc2Vyb25zICJjb3dwbG90IiBwb3VyIGZhaXJlIGRlcyBmaWd1cmVzIG11bHRpcGxlcyBmYWNpbGVtZW50CmxpYnJhcnkoY293cGxvdCkKIyBhaW5zaSBxdWUgbGVzIGRldXggcGFja2FnZXMgc3VpdmFudHMgZMOpZGnDqXMgYXV4IGFuYWx5c2VzIGRlIHN1cnZpZQpsaWJyYXJ5KHN1cnZpdmFsKQpsaWJyYXJ5KHN1cnZtaW5lcikKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBULCBmaWcud2lkdGg9MTAsIHdhcm5pbmc9RiwgbWVzc2FnZT1GKQpgYGAKCiMgRXhlbXBsZSBkdSBURAoKT24gcHJlbmQgaWNpIHVuIGV4ZW1wbGUgZmljdGlmIGQnZXNzYWkgY2xpbmlxdWUgcmFuZG9taXPDqSBjb250csO0bMOpIGRhbnMgbGUgY2FuY2VyIGR1IHBhbmNyw6lhcywgZW4gZGV1eCBncm91cGVzLCBsJ3VuIHJlY2V2YW50IHVuIHRyYWl0ZW1lbnQgZXQgbCdhdXRyZSByZWNldmFudCB1biBwbGFjZWJvLgoKMSkgTCdvYmplY3RpZiBwcmluY2lwYWwgY29uc2lzdGUgw6AgbW9udHJlciBsYSBzdXDDqXJpb3JpdMOpIGR1IHRyYWl0ZW1lbnQgc3VyIGxhIHN1cnZpZSBnbG9iYWxlIGRlcyBwYXRpZW50cy4KMikgTCdvYmplY3RpZiBzZWNvbmRhaXJlIGNvbnNpc3RlIMOgIG1vbnRyZXIgbGEgc3Vww6lyaW9yaXTDqSBkdSB0cmFpdGVtZW50IHN1ciBsYSBzdXJ2aWUgc2FucyBwcm9ncmVzc2lvbi4KCkwnaW5jbHVzaW9uIGRlcyBwYXRpZW50cyBzJ2VzdCBmYWl0ZSBzdXIgdW5lIGR1csOpZSBkZSBkZXV4IGFucy4KQSBsJ2lzc3VlIGRlIGxhIHDDqXJpb2RlIGQnaW5jbHVzaW9uLCBsJ8OpdHVkZSBhIGR1csOpIDMgYW5zLgpDZXJ0YWlucyBwYXRpZW50cyBwZXV2ZW50IMOqdHJlIHN1aXZpcyA1IGFucyAocHJlbWllcnMgaW5jbHVzKSBvdSAzIChkZXJuaWVycyBpbmNsdXMpLiBNYWlzIGNlcnRhaW5zIHBhdGllbnRzIHBldXZlbnQgw6lnYWxlbWVudCDDqnRyZSBwZXJkdXMgZGUgdnVzIChwZXV0LcOqdHJlIG9udC1pbHMgZMOpbcOpbmFnw6kgYWlsbGV1cnMsIHBldXQtw6p0cmUgbmUgcsOpcG9uZGVudC1pbHMgcGx1cyBhdSB0w6lsw6lwaG9uZS4uLikuCgpQb3VyIGNoYXF1ZSBwYXRpZW50LCB1biBjZXJ0YWluIG5vbWJyZSBkZSB0ZW1wcyBzb250IGVucmVnaXN0csOpcyBkYW5zIGxhIGJhc2UgZGUgZG9ubsOpZXMgOgoKYGBge3J9CmQgPC0gcmVhZC5jc3YoImRhdGFfc3Vydml2YWwuY3N2IikKaGVhZChkKQpgYGAKCj4gUHJlbmV6IDMgbWluIHBvdXIgZXhwbG9yZXIgbGEgZmHDp29uIGRvbnQgY2UgamV1IGRlIGRvbm7DqWVzIGVzdCBjb25zdHJ1aXQuIERhbnMgcXVlbHMgY2FzIGEtdC1vbiB1bmUgZGF0ZSBkZSBkw6ljw6hzIGluY29ubnVlID8gUXVlbGxlIGVzdCBsYSBkYXRlIGRlIGTDqWJ1dCBkZSBsJ2Vzc2FpID8gRGUgY2zDtHR1cmUgZGUgbCdlc3NhaSA/CgpMZSBwYWNrYWdlIGBsdWJyaWRhdGVgIGV0IHNlcyBmb25jdGlvbnMgYHltZCgpYCwgYGRteSgpYCBvdSBgbWR5KClgIGVzdCBiaWVuIHV0aWxlIHBvdXIgaW50ZXJwcsOpdGVyIGxlcyBjaGHDrm5lcyBkZSBjYXJhY3TDqHJlcyBjb21tZSBkZXMgZGF0ZXMsIGRlIGZhw6dvbiDDoCBwb3V2b2lyIGxlcyBtYW5pcHVsZXIgcG91ciBlbiB0aXJlciBkZXMgZHVyw6llcyBlbnRyZSBkZXV4IGRhdGVzLgoKYGBge3J9CmQgPC0gZCAlPiUKCW11dGF0ZSgKCQlkYXRlX2RlYXRoID0geW1kKGRhdGVfZGVhdGgpLAoJCWRhdGVfcmFuZG8gPSB5bWQoZGF0ZV9yYW5kbyksCgkJZGF0ZV9sYXN0X2ZvbGxvd191cCA9IHltZChkYXRlX2xhc3RfZm9sbG93X3VwKSwKCQlkYXRlX3Byb2cgPSB5bWQoZGF0ZV9wcm9nKQoJKQpgYGAKCiMgUHJlbWllcnMgZXNzYWlzIG5hw69mcwoKQXZhbnQgZGUgcydpbnTDqXJlc3NlciDDoCBjZSBxdSdvbiBwZXV0IGZhaXJlIGRlIHBsdXMgZmFuY3kgw6AgcGFydGlyIGRlIGNlcyB0ZW1wcyBkJ2F0dGVudGUsIHLDqWZsw6ljaGlzc29ucyDDoCBjZSBxdWUgbm91cyBzZXJpb25zIGTDqWrDoCBlbiBtZXN1cmUgZGUgZmFpcmUgYXZlYyBsZXMgb3V0aWxzIHF1ZSBub3VzIGNvbm5haXNzb25zIGTDqWrDoC4KCj4gUHJvcG9zZXogY2ktZGVzc291cyB1bmUgYW5hbHlzZSBkZSBsYSBwcm9iYWJpbGl0w6kgZGUgZMOpY8OocyDDoCA2IG1vaXMgZW50cmUgbGVzIGRldXggZ3JvdXBlcy4KCmBgYHtyfQpkIDwtIGQgJT4lIG11dGF0ZSgKCXRpbWVfYmVmb3JlX2RlYXRoID0gYXMubnVtZXJpYyhkYXRlX2RlYXRoIC0gZGF0ZV9yYW5kbyksCglpc19hbGl2ZV82bSA9IChkYXRlX2xhc3RfZm9sbG93X3VwIC0gZGF0ZV9yYW5kbykgPiAzNjUvMiwKCWlzX2FsaXZlXzI0bSA9IChkYXRlX2xhc3RfZm9sbG93X3VwIC0gZGF0ZV9yYW5kbykgPiAzNjUqMiwKKQoKdGFibGUoZCRhcm0sIGQkaXNfYWxpdmVfNm0pCnByb3AudGVzdCh0YWJsZShkJGFybSwgZCRpc19hbGl2ZV82bSkpCmBgYAoKPiBQdWlzIHVuZSBhbmFseXNlIGRlIGxhIHByb2JhYmlsaXTDqSBkZSBkw6ljw6hzIMOgIDIgYW5zIGVudHJlIGxlcyBkZXV4IGdyb3VwZXMuCgpgYGB7cn0KdGFibGUoZCRhcm0sIGQkaXNfYWxpdmVfMjRtKQpwcm9wLnRlc3QodGFibGUoZCRhcm0sIGQkaXNfYWxpdmVfMjRtKSkKYGBgCgo+IFB1aXMgdW5lIGFuYWx5c2UgZGVzIHRlbXBzIGQnYXR0ZW50ZSBhdmFudCBkw6ljw6hzIGF2ZWMgdW4gdGVzdCBkZSBXaWxjb3hvbi4KCmBgYHtyfQp3aWxjb3gudGVzdCh0aW1lX2JlZm9yZV9kZWF0aCB+IGFybSwgZGF0YT1kKQpgYGAKCk9uIGEgZGV1eCByZXByb2NoZXMgcHJpbmNpcGF1eCDDoCBmYWlyZSDDoCBjZXMgYXBwcm9jaGVzIG5hw692ZXMgOgoKKiBkJ3VuZSBwYXJ0LCBzaSBvbiBhbmFseXNlIGRlcyBwcm9wb3J0aW9ucyBkZSBzdXJ2aXZhbnRzLCBpbCBub3VzIGZhdXQgZMOpZmluaXIgVU4gdGVtcHMgYXVxdWVsIG9uIHJlZ2FyZGUgbGEgc3VydmllLCBjZSBxdWkgZXN0IGxvaW4gZCfDqnRyZSDDqXZpZGVudCDDoCBkw6ljaWRlciBhdmFudCBkZSBmYWlyZSBsJ8OpdHVkZS4gUsOpcMOpdGVyIGwnYW5hbHlzZSBwb3VyIHVuIGdyYW5kIG5vbWJyZSBkZSB0ZW1wcyBkaWZmw6lyZW50cyBzZXJhaXQgdW5lIHRyw6hzIG1hdXZhaXNlIHByYXRpcXVlIGFtZW5hbnQgw6AgdW5lIGluZmxhdGlvbiBkdSByaXNxdWUgZGUgcHJlbWnDqHJlIGVzcMOoY2UuCiogZCdhdXRyZSBwYXJ0LCBvbiBwZXJkIHRvdXRlIGwnaW5mb3JtYXRpb24gw6AgcHJvcG9zIGRlcyBkdXLDqWVzIGRlIHN1cnZpZSBkZXMgcGF0aWVudHMgcXVpIG9udCDDqXTDqSBzdWl2aSB1biBwZXUgbWFpcyBwYXMgc3VmZmlzYW1tZW50IHBvdXIgcXUnb24gYWl0IG9ic2VydsOpIGwnw6l2w6luZW1lbnQuIEMnZXN0IGNldHRlIGluZm9ybWF0aW9uIHF1J29uIHZhIGNoZXJjaGVyIMOgIGNhcHRlciBncsOiY2Ugw6AgbGEgbm90aW9uIGRlIGNlbnN1cmUgcXUnb24gaW50cm9kdWl0IGp1c3RlIGFwcsOocy4KCgojIENlbnN1cmUKClNvaXQgJFRfaSA6PSQgbGUgdGVtcHMgZCdhdHRlbnRlIGF2YW50IGTDqWPDqHMgZGUgbCdpbmRpdmlkdSAkaSQuCgpMb3JzcXUnb24gdHJhaXRlIGRlIGRvbm7DqWVzIGRlIHRlbXBzIGQnYXR0ZW50ZSBkYW5zIGxhIHZyYWllIHZpZSwgb24gc2UgcmV0cm91dmUgc291dmVudCBkYW5zIGRlcyBjYXMgb8O5IG9uIG5lIHBldXQgcGFzIG9ic2VydmVyIGV4YWN0ZW1lbnQgbGUgdGVtcHMgZGUgc3VydmVudWUgZCd1biDDqXbDqW7DqW1lbnQsIG1haXMgb8O5IG9uIHBvc3PDqGRlIG1hbGdyw6kgdG91dCB1bmUgaW5mb3JtYXRpb24gcGFyY2VsbGFpcmUgw6AgcHJvcG9zIGRlIGNlIHRlbXBzLiBPbiBkaXQgcXVlIG5vcyBkb25uw6llcyBzb250IGNlbnN1csOpZXMgKMOgIGRyb2l0ZS/DoCBnYXVjaGUpLgoKKiBjZW5zdXJlIMOgIGdhdWNoZSA6IHNpIG9uIHNhaXQgcXVlICRUX2kgPCB0X2leKyQuCiogY2Vuc3VyZSDDoCBkcm9pdGUgOiBzaSBvbiBzYWl0IHF1ZSAkVF9pID4gdF9pXi0kLgoqIGNlbnN1cmUgw6AgZ2F1Y2hlIGV0IMOgIGRyb2l0ZSA6IHNpIG9uIHNhaXQgcXVlICRUX2kgXGluICh0X2leLSwgdF9pXispJC4KICAKCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPSJMZXMgYmlhaXMgZGUgZMOpZmluaXRpb24gZGUgdDAiLCBvdXQud2lkdGggPSAnMTAwJSd9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJjZW5zdXJlLnBuZyIpCmBgYAoKPiBEYW5zIHF1ZWwgY2FzIHNlIHRyb3V2ZS10LW9uIGljaSBhdmVjIG5vcyBwZXJkdXMgZGUgdnVlID8KCkxhIHBsdXBhcnQgZHUgdGVtcHMsIGRhbnMgbGVzIGVzc2FpcyBjbGluaXF1ZXMsIG5vdXMgYXVyb25zIGRlcyBkb25uw6llcyBxdWkgY29ycmVzcG9uZGVudCDDoCBkZXMgY2Vuc3VyZXMgw6AgZHJvaXRlLCBjJ2VzdCDDoCBkaXJlIMOgIGRlcyB0ZW1wcyBkZSBmb2xsb3ctdXAgc2FucyDDqXbDqW5lbWVudCwgcXVpIG5vdXMgYXBwb3J0ZW50IHRvdXQgZGUgbcOqbWUgdW5lIGluZm9ybWF0aW9uIMOpbm9ybWUgc3VyIGNlIHF1J2lsIG5lIHMnZXN0IHBhcyBwYXNzw6kganVzcXUnw6AgY2UgdGVtcHMuCgpUb3V0ZXMgbGVzIG3DqXRob2RlcyBxdWkgc3VpdmVudCB2b250IGRvbmMgcGFydmVuaXIgw6AgZ8OpcmVyIGNldHRlIGNlbnN1cmUgw6AgZHJvaXRlLgoKCiMgU3VydmllIGV0IGVzdGltYXRpb24gZGUgS2FwbGFuLU1laWVyCgpMZSBwcm9ibMOobWUgZGUgbCdlc3RpbWF0aW9uIGRlIGxhIGNvdXJiZSBkZSBzdXJ2aWUgY29uc2lzdGUgw6AgZXN0aW1lciBsYSBjb3VyYmUgOgoKJCQKUyh0KSA9IFxtYXRoYmJ7UH0oVCA+IHQpCiQkCgrDoCBwYXJ0aXIgZGUgZG9ubsOpZXMgcG90ZW50aWVsbGVtZW50IGNlbnN1csOpZXMgw6AgZHJvaXRlLgoKTGUgcHJpbmNpcGUgZGUgbCdhcHByb2NoZSBkZSBLYXBsYW4tTWVpZXIgZXN0IGxlIHN1aXZhbnQgOgoKKiBsZSB0ZW1wcyBlc3QgZMOpY291cMOpIGVuIHNvdXMtaW50ZXJ2YWxsZXMgcGVuZGFudCBsZXNxdWVscyBpbCBuZSBzZSBwYXNzZSBhdWN1biBkw6ljw6hzLgoqIHN1ciB1biBpbnRlcnZhbGxlICRpJCBhdmVjICRuX2kkIGluZGl2aWR1cyBzdXNjZXB0aWJsZXMgZGUgZMOpY8OpZGVyIGF1IGTDqWJ1dCBkZSBsJ2ludGVydmFsbGUgZXQgJGRfaSQgZMOpY8OocyDDoCBsYSBmaW4gZGUgbCdpbnRlcnZhbGxlLCBsYSBwcm9iYWJpbGl0w6kgZGUgc3Vydml2cmUgZXN0IGVzdGltw6llIGNvbW1lICQoMSAtIFxmcmFje2RfaX17bl9pfSkkLgoqIGRvbmMgbGEgc3VydmllIGp1c3F1J8OgIGNlIHRlbXBzIGNvcnJlc3BvbmQgYXUgcHJvZHVpdCBkZXMgcHJvYmFiaWxpdMOpcyBkZSBzdXJ2aWUgZGUgdG91cyBsZXMgaW50ZXJ2YWxsZXMgcHLDqWPDqWRlbnRzLgoKTGEgZm9ybXVsZSBkJ2VzdGltYXRpb24gZGUgbGEgY291cmJlIGRlIHN1cnZpZSBwYXIgbGEgbcOpdGhvZGUgZGUgS2FwbGFuLU1laWVyIHMnw6ljcml0IGRvbmMgZGUgZmHDp29uIHBsdXMgY29uY2lzZSBjb21tZSA6CgokJApcaGF0e1N9KHQpID0gXHByb2Rfe2k6IHRfaSA8IHR9IFxsZWZ0KCAxIC0gXGZyYWN7ZF9pfXtuX2l9IFxyaWdodCkKJCQKCkxhIGNlbnN1cmUgc2UgcHJlbmQgZW4gY29tcHRlIHRyw6hzIG5hdHVyZWxsZW1lbnQgaWNpIGVuIHLDqWR1aXNhbnQgJG5faSQgw6AgY2hhcXVlIGZvaXMgcXUndW4gaW5kaXZpZHUgZXN0IHBlcmR1IGRlIHZ1ZS4KVW4gInRpY2siIGVzdCBhbG9ycyB2aXN1ZWxsZW1lbnQgbm90w6kgc3VyIGxhIGNvdXJiZSBkZSBzdXJ2aWUgcG91ciBzaWduaWZpZXIgcXUndW4gaW5kaXZpZHUgZXN0IHBlcmR1IHNhbnMgw6p0cmUgZMOpY8OpZMOpLgoKQXZlYyBSLCBvbiB1dGlsaXNlIGxhIGZvbmN0aW9uIGBzdXJ2Zml0YCBldCBgZ2dzdXJ2cGxvdGAgcXVpIHByZW5uZW50IGVuIGVudHLDqWUgOgoKKiB1biB0ZW1wcyBhdmFudCBzdXJ2ZW51ZSBkJ3VuIMOpdsOpbsOpbWVudCAodGltZSkKKiB1bmUgdmFsZXVyIGJpbmFpcmUgMSBzaSB1biBkw6ljw6hzIHN1cnZpZW50IG91IDAgc2kgdW5lIGNlbnN1cmUgc3VydmllbnQgw6AgY2UgdGVtcHMgKGV2ZW50KQoqIHVuZSDDqXZlbnR1ZWxsZSBjb3ZhcmlhYmxlIGNvcnJlc3BvbmRhbnQgw6AgZGVzIGdyb3VwZXMgcG91ciBsZXNxdWVscyBvbiBzb3VoYWl0ZSB0cmFjZXIgZGVzIGNvdXJiZXMgZGUgc3VydmllIGRpZmbDqXJlbnRlcy4KCj4gUHLDqXBhcmV6IGxlIGpldSBkZSBkb25uw6llcyBwb3VyIGF2b2lyIGB0aW1lYCBldCBgZXZlbnRgIHByb3ByZW1lbnQgZMOpZmluaXMgKHJlZ2FyZGV6IGwnYWlkZSBkZSBgU3VydmApIGV0IHV0aWxpc2V6IGxlIG1vcmNlYXUgZGUgY29kZSBxdWkgc3VpdCBwb3VyIHRyYWNlciBsZXMgY291cmJlcyBkZSBzdXJ2aWUgZGUgS2FwbGFuLU1laWVyLgoKPiBTZW50ZXogdm91cyBsaWJyZXMgZGUgam91ZXIgYXZlYyBsZXMgZGlmZsOpcmVudGVzIG9wdGlvbnMgbGlzdMOpZXMuIExlcyBhbWF0ZXVycyBkZSBnZ3Bsb3QyIHBvdXJyb250IGFjY8OpZGVyIGF1IGdyYXBoZSBnZ3Bsb3QyIG1vZGlmaWFibGUgYXZlYyBsJ29iamV0IGBmaW5hbCRwbG90YC4KCgpgYGB7cn0KZCA8LSBkICU+JSBtdXRhdGUoCgl0aW1lID0gYXMubnVtZXJpYyhkYXRlX2xhc3RfZm9sbG93X3VwIC0gZGF0ZV9yYW5kbyksCglldmVudCA9IGlmZWxzZSghaXMubmEoZGF0ZV9kZWF0aCksIDEsIDApCikKCmttX29zIDwtIHN1cnZmaXQoU3Vydih0aW1lLCBldmVudCkgfiBhcm0sIGRhdGE9ZCkKCmdyYXBoX29zIDwtIGdnc3VydnBsb3QoCiAgICBrbV9vcywKICAgIHB2YWwgPSBGLCAKICAgIGNvbmYuaW50ID0gRkFMU0UsCiAgICByaXNrLnRhYmxlID0gRiwgIyBBZGQgcmlzayB0YWJsZQogICAgcmlzay50YWJsZS5jb2wgPSAic3RyYXRhIiwgIyBDaGFuZ2UgcmlzayB0YWJsZSBjb2xvciBieSBncm91cHMKICAgICNsaW5ldHlwZSA9ICJzdHJhdGEiLCAjIENoYW5nZSBsaW5lIHR5cGUgYnkgZ3JvdXBzCiAgICBzdXJ2Lm1lZGlhbi5saW5lID0gImh2IiwgIyBTcGVjaWZ5IG1lZGlhbiBzdXJ2aXZhbAogICAgZ2d0aGVtZSA9IHRoZW1lX2J3KCkgIyBDaGFuZ2UgZ2dwbG90MiB0aGVtZQopCgpncmFwaF9vcwpgYGAKCk5vdGV6IHF1ZSBjZSBncmFwaGlxdWUgcmVwb3NlIHN1ciBwbHVzaWV1cnMgaHlwb3Row6hzZXMgcXVpIHNlcm9udCDDqWdhbGVtZW50IGltcGxpY2l0ZXMgZGFucyBsYSBzdWl0ZSA6CgoxKSBPbiBzdXBwb3NlIHF1ZSBsZXMgaW5kaXZpZHVzIHF1aSBzb250IGNlbnN1csOpcyBvbnQgbGVzIG3Dqm1lcyBjaGFuY2VzIGRlIHN1cnZpZSBxdWUgbGVzIGF1dHJlcywgaS5lLiBxdWUgbGEgY2Vuc3VyZSBuJ2VzdCBwYXMgaW5mb3JtYXRpdmUgZCd1biBmdXR1ciBkw6ljw6hzLAoyKSBPbiBzdXBwb3NlIHF1ZSBsZXMgc3VydmllcyBzb250IGlkZW50aXF1ZXMgcG91ciBsZXMgc3VqZXRzIHJlY3J1dMOpcyB0w7R0IGV0IHRhcmQgZGFucyBsJ8OpdHVkZSwgZGUgZmHDp29uIMOgIHBvdXZvaXIgcsOpLWFsaWduZXIgdG91dCBsZSBtb25kZSBzdXIgdW4gdDAgcXVpIGNvcnJlc3BvbmQgw6AgbGEgcmFuZG9taXNhdGlvbi4KCklsIG5vdXMgcmVzdGUgdW4gZGVybmllciBpdGVtIMOgIGRpc2N1dGVyIGVuIGxpZW4gYXZlYyBjZXMgYW5hbHlzZXMgZGUgc3VydmllLCBsYSBub3Rpb24gZGUgKm3DqWRpYW5lIGRlIHN1cnZpZSogcXVpIGVzdCB1bmUgcXVhbnRpdMOpIHByYXRpcXVlIMOgIHNlIHJlcHLDqXNlbnRlciBxdWUgdG91dCBsZSBtb25kZSBhaW1lcmFpdCBwYXJ0YWdlci4gT3VpIG1haXMgdm9pbMOgLCBtw6ptZSB1bmUgbm90aW9uIGF1c3NpIHNpbXBsZSBzJ2F2w6hyZSDDqnRyZSBwcm9waWNlIMOgIGZhaXJlIG4naW1wb3J0ZSBxdW9pIGF2ZWMgbm9zIHByb2Jsw6htZXMgZGUgdGVtcHMgZCdhdHRlbnRlICEKCj4gVGVzdGV6IGNlIHF1ZSDDp2EgZG9ubmUgZGUgcHJlbmRyZSBuYcOvdmVtZW50IGxhIG3DqWRpYW5lIGRlcyB0ZW1wcyBkZSBkw6ljw6hzLgoKPiBQdWlzIHRlc3RleiBjZSBxdWUgw6dhIGRvbm5lIGRlIHByZW5kcmUgbmHDr3ZlbWVudCBsYSBtw6lkaWFuZSBkZXMgdGVtcHMgZGUgZm9sbG93LXVwLgoKYGBge3J9CmQgJT4lIGdyb3VwX2J5KGFybSkgJT4lIAogICAgc3VtbWFyaXNlKAogICAgICAgIG1lZGlhbl90X2RlYXRoID0gbWVkaWFuKHRpbWVfYmVmb3JlX2RlYXRoLCBuYS5ybT1UKSwKICAgICAgICBtZWRpYW5fdF9mb2xsb3cgPSBtZWRpYW4odGltZSkKICAgICkKYGBgCgpSw6lmbMOpY2hpc3NleiDDoCBsYSBzaWduaWZpY2F0aW9uIGRlIGNlcyBkZXV4IHF1YW50aXTDqXMuCkVuIHLDqWFsaXTDqSwgY2UgcXUnb24gY2hlcmNoZSBxdWFuZCBvbiBzJ2ludMOpcmVzc2Ugw6AgbGEgbcOpZGlhbmUgZGUgc3VydmllLCBjJ2VzdCBsYSB2YWxldXIgJHRfbSQgdGVsbGUgcXVlICRTKHRfbSkgPSAxLzIkLgoKT24gbCdvYnRpZW50IGF2ZWMgbGUgY29kZSBxdWkgc3VpdCA6CgpgYGB7cn0Kc3Vydl9tZWRpYW4oa21fb3MpCmBgYAoKCiMgSGF6YXJkIGV0IGhhemFyZCByYXRpbwoKTGUgImhhemFyZCIgJGgodCkkLCBwYXJmb2lzIGF1c3NpIGFwcGVsw6kgImhhemFyZCByYXRlIiBlc3QgbGUgdGF1eCBkZSBzdXJ2ZW51ZSBkZSBsJ8OpdsOpbmVtZW50IGF1IHRlbXBzICR0JCBzYWNoYW50IHF1J29uIGEgc3VydsOpY3UganVzcXUnYXUgdGVtcHMgJHQkLgoKJCQKaCh0KSA6PSBsaW1fe2R0IFxyaWdodGFycm93IDB9IH4gXGZyYWN7XG1hdGhiYntQfShUIFxpbiAodCwgdCtkdCkgfCBUID4gdCl9e2R0fQokJAoKTGUgaGF6YXJkIGVzdCBwb3NpdGlmIGVuIHRvdXQgdGVtcHMgJHQkIG1haXMgbidlc3QgcGFzIG7DqWNlc3NhaXJlbWVudCBtb25vdG9uZS4KClNpIG9uIHBhcmxlIHRhbnQgZGUgY2V0dGUgcXVhbnRpdMOpIGVuIGFuYWx5c2UgZGUgc3VydmllLCBjJ2VzdCBxdWUgbGVzIHByaW5jaXBhdXggdGVzdHMgc3RhdGlzdGlxdWVzIHByb3Bvc8OpcyBkYW5zIGxlIGRvbWFpbmUgcG91ciBjb21wYXJlciBsZXMgc3VydmllcyBkZSBkZXV4IGdyb3VwZXMgMSBldCAyIHJlcG9zZW50IHN1ciBsJ2h5cG90aMOoc2Ugc3VpdmFudGUgOgoKJCQKXGV4aXN0cyBLIFxpbiBcbWF0aGJie1J9XissIFxmb3JhbGwgdCwgfiBcZnJhY3toXzEodCl9e2hfMih0KX0gPSBLCiQkCgpDJ2VzdCBsJ2h5cG90aMOoc2UgZGVzICpoYXphcmQgcHJvcG9ydGlvbm5lbHMqIG91IGR1ICpoYXphcmQgcmF0aW8gY29uc3RhbnQqLgoKTm90ZXogcXUnb24gbmUgZmFpdCBwYXMgZCdoeXBvdGjDqHNlIGZvcnRlIHN1ciBsYSBmb3JtZSBkZSBsYSBmb25jdGlvbiBkdSBoYXphcmQsIG1haXMgcXUnb24gYSBiZXNvaW4gZGUgZmFpcmUgbCdoeXBvdGjDqHNlIHF1ZSBjZXR0ZSBmb3JtZSBlc3Qgc2ltaWxhaXJlIGRhbnMgbGVzIGRldXggZ3JvdXBlcyBkJ2ludMOpcsOqdC4KClBvdXIgc2UgZml4ZXIgdW4gcGV1IHBsdXMgbGVzIGlkw6llcywgdm9pY2kgZGV1eCBpbGx1c3RyYXRpb25zIGRlIHJlbGF0aW9uIGVudHJlIGhhemFyZHMgZXQgc3VydmllIGRhbnMgbGUgY2FkcmUgZGUgZGV1eCBsb2lzIHRow6lvcmlxdWVzIGRpZmbDqXJlbnRlcy4KCiMjIEhhemFyZHMgY29uc3RhbnRzIGV0IGxvaSBleHBvbmVudGllbGxlCgpQb3VyIHZvaXIgY2UgcXVlIMOnYSBkb25uZSBhdmVjIHVuZSBkaXN0cmlidXRpb24gcXVlIHZvdXMgYXZleiBkZXMgY2hhbmNlcyBkZSBjb25uYcOudHJlLCBsYSBkaXN0cmlidXRpb24gZXhwb25lbnRpZWxsZS4KQ29tbWUgdm91cyBsJ2F2ZXogc2FucyBkb3V0ZSBkw6lqw6AgZW50ZW5kdSBkaXJlLCBsYSBsb2kgZXhwb25lbnRpZWxsZSBlc3QgInNhbnMgbcOpbW9pcmUiLCBjJ2VzdCDDoCBkaXJlIHF1ZSBsZSB0YXV4IGRlIHN1cnZlbnVlIGQndW4gw6l2w6luw6ltZW50LCBzYWNoYW50IHF1ZSBsJ8OpdsOpbmVtZW50IG4nZXN0IHBhcyBlbmNvcmUgc3VydmVudSAobGUgaGF6YXJkKSBlc3QgY29uc3RhbnQgISAKVm9pY2kgZG9uYyB1bmUgcHJlbWnDqHJlIGlsbHVzdHJhdGlvbiDDoCBzZSBtZXR0cmUgZW4gdMOqdGUgOgoKYGBge3J9CnQgPC0gc2VxKDAsMTAsMC4xKQpyYXRlcyA8LSBjKDAuNSwxLDIpCmRpc3RyaWJfZXhwIDwtIGRhdGEuZnJhbWUoCiAgICB0ID0gcmVwKHQsIGVhY2g9bGVuZ3RoKHJhdGVzKSksCiAgICBkZW5zaXR5ID0gZGV4cChyZXAodCxlYWNoPWxlbmd0aChyYXRlcykpLCByYXRlPXJlcChyYXRlcywgbGVuZ3RoKHQpKSksCiAgICBzdXJ2aXZhbCA9IHBleHAocmVwKHQsZWFjaD1sZW5ndGgocmF0ZXMpKSwgcmF0ZT1yZXAocmF0ZXMsIGxlbmd0aCh0KSksIGxvd2VyLnRhaWw9RiksCiAgICBsYW1iZGEgPSBhcy5mYWN0b3IocmVwKHJhdGVzLCBsZW5ndGgodCkpKSwKICAgIGhhemFyZCA9IHJlcChyYXRlcywgbGVuZ3RoKHQpKQopCgpwMSA8LSBkaXN0cmliX2V4cCAlPiUgZ2dwbG90KGFlcyh4PXQsIHk9ZGVuc2l0eSwgY29sb3I9bGFtYmRhKSkgKwogICAgeGxhYigidCIpICsKICAgIHlsYWIoIkRlbnNpdMOpIDogUCh0IDwgVCA8IHQrZHQpL2R0IikgKwogICAgZ2VvbV9saW5lKCkgKwogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiM1NDg2ODciLCAiI2ZmNTU1NSIsICIjOGZiYzk0IikpCgpwMiA8LSBkaXN0cmliX2V4cCAlPiUgZ2dwbG90KGFlcyh4PXQsIHk9c3Vydml2YWwsIGNvbG9yPWxhbWJkYSkpICsKICAgIHhsYWIoInQiKSArCiAgICB5bGFiKCJTdXJ2aWUgOiBQKFQ+dCkiKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICB0aGVtZV9idygpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzU0ODY4NyIsICIjZmY1NTU1IiwgIiM4ZmJjOTQiKSkKCnAzIDwtIGRpc3RyaWJfZXhwICU+JSBnZ3Bsb3QoYWVzKHg9dCwgeT1oYXphcmQsIGNvbG9yPWxhbWJkYSkpICsgCiAgICB4bGFiKCJ0IikgKwogICAgeWxhYigiSGF6YXJkIDogUCh0IDwgVCA8IHQrZHQgfCBUID4gdCkvZHQiKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICB0aGVtZV9idygpICArIAogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjNTQ4Njg3IiwgIiNmZjU1NTUiLCAiIzhmYmM5NCIpKQoKcGxvdF9ncmlkKHAxLCBwMiwgcDMsIG5jb2wgPSAzLCBucm93ID0gMSkKYGBgCgojIyBIYXphcmRzIGQndW5lIGxvaSBkZSBXZWlidWxsCgpIaXN0b2lyZSBkZSBuZSBwYXMgcmV0ZW5pciBxdWUgY2UgcXVpIHNlIHBhc3NlIGRhbnMgbGUgY2FzIGFyY2hpLXNpbXBsZSBkZSBsYSBsb2kgZXhwb25lbnRpZWxsZSwgdm9pY2kgdW5lIHNlY29uZGUgbG9pIHRyw6hzIGVtcGxvecOpZSBkYW5zIGxlIGRvbWFpbmUgZGUgbCdhbmFseXNlIGRlIHN1cnZpZSA6IGxhIGxvaSBkZSBXZWlidWxsLgoKRWxsZSBhIHVuZSBwcm9wcmnDqXTDqSByaWdvbG90ZSwgcXVpIGVzdCB1bmlxdWVtZW50IHByb3ByZSDDoCBjZXR0ZSBmYW1pbGxlIGRlIGRpc3RyaWJ1dGlvbiA6IG9uIHBldXQgbCd1dGlsaXNlciDDoCBsYSBmb2lzIGRhbnMgbGUgY2FkcmUgZCd1bmUgaHlwb3Row6hzZSBkZSAqaGF6YXJkcyBwcm9wb3J0aW9ubmVscyogZXQgZGFucyBsZSBjYWRyZSBkJ3VuZSBoeXBvdGjDqHNlIGQnKmFjY2VsZXJhdGVkIGZhaWx1cmUgdGltZSosIGMnZXN0IMOgIGRpcmUgcXVlIGxhIHN0cnVjdHVyZSBkZSBsYSBkaXN0cmlidXRpb24gZXN0IHRlbGxlIHF1J3VuZSBhdWdtZW50YXRpb24gZGUgaGF6YXJkIGQndW4gY2VydGFpbiBmYWN0ZXVyIGFjY8OpbMOocmUgbGEgc3VydmllIGQndW4gYXV0cmUgZmFjdGV1ci4KClJlbWFycXVleiBhdXNzaSBxdWUgbGEgbG9pIGV4cG9uZW50aWVsbGUgYXBwYXJ0aWVudCDDoCBsYSBmYW1pbGxlIGRlcyBsb2kgZGUgV2VpYnVsbCwgZXQgcXVlIGxhIG3Dqm1lIHByb3ByacOpdMOpIMOpdGFpdCBkb25jIHZyYWllIHN1ciBsZSBncmFwaGUgY2ktZGVzc3VzLiBTaW1wbGVtZW50IMOgIHByw6lzZW50IG9uIHBldXQgc3DDqWNpZmllciBkZXMgaGF6YXJkcyBub24tY29uc3RhbnRzIGRhbnMgbGUgY2FkcmUgZCd1bmUgbG9pIGRlIFdlaWJ1bGwuCgpgYGB7cn0KdCA8LSBzZXEoMCw1LDAuMDEpCnNoYXBlIDwtIDEuNgpzY2FsZXMgPC0gYygxLCAyKQpkaXN0cmliX3dlaWJ1bGwgPC0gZGF0YS5mcmFtZSgKICAgIHQgPSByZXAodCwgZWFjaD1sZW5ndGgoc2NhbGVzKSksCiAgICBkZW5zaXR5ID0gZHdlaWJ1bGwocmVwKHQsZWFjaD1sZW5ndGgoc2NhbGVzKSksIHNoYXBlPXNoYXBlLCBzY2FsZT1yZXAoc2NhbGVzLCBsZW5ndGgodCkpKSwKICAgIHN1cnZpdmFsID0gcHdlaWJ1bGwocmVwKHQsZWFjaD1sZW5ndGgoc2NhbGVzKSksIHNoYXBlPXNoYXBlLCBzY2FsZT1yZXAoc2NhbGVzLCBsZW5ndGgodCkpLCBsb3dlci50YWlsPUYpLAogICAgc2NhbGUgPSBhcy5mYWN0b3IocmVwKHNjYWxlcywgbGVuZ3RoKHQpKSkgCikKZGlzdHJpYl93ZWlidWxsIDwtIGRpc3RyaWJfd2VpYnVsbCAlPiUgbXV0YXRlKGhhemFyZCA9IGRlbnNpdHkgLyBzdXJ2aXZhbCkKCnAxIDwtIGRpc3RyaWJfd2VpYnVsbCAlPiUgZ2dwbG90KGFlcyh4PXQsIHk9ZGVuc2l0eSwgY29sb3I9c2NhbGUpKSArCiAgICB4bGFiKCJ0IikgKwogICAgeWxhYigiRGVuc2l0w6kgOiBQKHQgPCBUIDwgdCtkdCkvZHQiKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICB0aGVtZV9idygpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzU0ODY4NyIsICIjZmY1NTU1IiwgIiM4ZmJjOTQiKSkKCnAyIDwtIGRpc3RyaWJfd2VpYnVsbCAlPiUgZ2dwbG90KGFlcyh4PXQsIHk9c3Vydml2YWwsIGNvbG9yPXNjYWxlKSkgKwogICAgeGxhYigidCIpICsKICAgIHlsYWIoIlN1cnZpZSA6IFAoVCA+IHQpIikgKwogICAgZ2VvbV9saW5lKCkgKwogICAgdGhlbWVfYncoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSkpICsKICAgIGdndGl0bGUoIlNfMSh0KSA9IFNfMCgwLjUgeCB0KSIpICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiM1NDg2ODciLCAiI2ZmNTU1NSIsICIjOGZiYzk0IikpCgpwMyA8LSBkaXN0cmliX3dlaWJ1bGwgJT4lIGdncGxvdChhZXMoeD10LCB5PWhhemFyZCwgY29sb3I9c2NhbGUpKSArIAogICAgeGxhYigidCIpICsKICAgIHlsYWIoIkhhemFyZCA6IFAodCA8IFQgPCB0K2R0IHwgVCA+IHQpL2R0IikgKwogICAgZ2VvbV9saW5lKCkgKwogICAgdGhlbWVfYncoKSArIAogICAgbGFicyhjb2xvciA9ICJzaWdtYSIpICsKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9MC41KSkgKwogICAgZ2d0aXRsZSgiaF8xKHQpID0gMC4zMyBoXzAodCkiKSArIAogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjNTQ4Njg3IiwgIiNmZjU1NTUiLCAiIzhmYmM5NCIpKQoKcGxvdF9ncmlkKHAxLCBwMiwgcDMsIG5jb2wgPSAzLCBucm93ID0gMSkKYGBgCgoKIyBUZXN0IGR1IGxvZ3JhbmsKCkxlIHRlc3QgZHUgbG9ncmFuayBwZXJtZXQgZGUgcsOpcG9uZHJlIMOgIGxhIHF1ZXN0aW9uIHN1aXZhbnRlIDoKCiogSDAgOiBIUiA9IDEKKiBIMSA6IEhSICRcbmVxJCAxLgoKTGEgcHJpbmNpcGFsZSBjb25kaXRpb24gZCdhcHBsaWNhdGlvbiBwb3VyIGxlIHLDqWFsaXNlciBjb25zaXN0ZSDDoCBzdXBwb3NlciBxdWUgbm9zIGhhemFyZHMgc29udCBwcm9wb3J0aW9ubmVscy4KCkxhIHN5bnRheGUgUiBwb3VyIGxlIHLDqWFsaXNlciBzdWl0LgoKYGBge3J9CnN1cnZkaWZmKFN1cnYodGltZSwgZXZlbnQpIH4gYXJtLCBkYXRhPWQpCmBgYAoKPiBRdWUgY29uY2x1ZXotdm91cyBkZSBjZSB0ZXN0ID8KCgojIE1vZMOobGUgZGUgQ294CgpTaSAkKFhfMV57KGkpfSwgWF8yXnsoaSl9LCAuLi4sIFhfcF57KGkpfSkkIHNvbnQgJHAkIHZhcmlhYmxlcyBleHBsaWNhdGl2ZXMgbWVzdXLDqWVzIHBvdXIgbCdpbmRpdmlkdSAkaSQsIGxlIG1vZMOobGUgZGUgQ294IGNvbnNpc3RlIMOgIHJlbGllciBsZSBoYXphcmQgZGUgbCdpbmRpdmlkdSAkaSQgYXZlYyBjZXMgY292YXJpYWJsZXMgc2Vsb24gbGEgZm9ybWUgc3VpdmFudGUgOgoKJCQKaF9pKHQpID0gaF8wKHQpIGVee1xzdW1fe2o9MX1ecCBcYmV0YV9qIFhfal57KGkpfX0KJCQKCkNvbW1lIHBvdXIgZCdhdXRyZXMgbW9kw6hsZXMgZGUgcsOpZ3Jlc3Npb24gcXVlIHZvdXMgY29ubmFpc3NleiBkw6lqw6AsIHZvdXMgcG91dmV6IGltYWdpbmVyIGluY2x1cmUgZGFucyBjZSBtb2TDqGxlIHRvdXQgdHlwZSBkZSB2YXJpYWJsZSA6CgoqIGF2ZWMgdW5lIHZhcmlhYmxlIG51bcOpcmlxdWUsIGwnZXhwb25lbnRpZWxsZSBkdSBwYXJhbcOodHJlIGNvcnJlc3BvbmQgYXUgZmFjdGV1ciBtdWx0aXBsaWNhdGlmIGR1IGhhemFyZCByYXRpbyBhc3NvY2nDqSDDoCB1bmUgYXVnbWVudGF0aW9uIGRlICsxIHVuaXTDqSBkZSBsYSB2YXJpYWJsZS4KKiBhdmVjIHVuZSB2YXJpYWJsZSBjYXTDqWdvcmllbGxlLCBvbiBhIHVuIHBhcmFtw6h0cmUgcGFyIG1vZGFsaXTDqSAobW9pbnMgbGEgbW9kYWxpdMOpIGRlIHLDqWbDqXJlbmNlKSwgZXQgbCdleHBvbmVudGllbGxlIGR1IHBhcmFtw6h0cmUgY29ycmVzcG9uZCBhdSBmYWN0ZXVyIG11bHRpcGxpY2F0aWYgZHUgaGF6YXJkIHJhdGlvIGFzc29jacOpIGF1IGZhaXQgZCdhdm9pciBjZXR0ZSBtb2RhbGl0w6kuCgpMZSBjb2RlIHN1aXZhbnQgdm91cyBkb25uZSBsYSBzeW50YXhlIHBlcm1ldHRhbnQgZGUgZml0dGVyIGxlIG1vZMOobGUgZGUgQ294IHN1ciBub3MgZG9ubsOpZXMsIHBvdXIgcsOpYWxpc2VyIHVuIHRlc3Qgc2ltaWxhaXJlIGF1IHRlc3QgZHUgbG9ncmFuayBwcsOpc2VudMOpIHByw6ljw6lkZW1tZW50LgpOb3RleiBxdWUgY2VzIGRldXggdGVzdHMgcmVwb3NlbnQgc3VyIGRlcyB0aMOpb3LDqG1lcyBkaWZmw6lyZW50cywgbWFpcyBxdSdpbHMgc29udCAiYXN5bXB0b3RpcXVlbWVudCDDqXF1aXZhbGVudHMiLCBjJ2VzdCDDoCBkaXJlIHF1J2lscyBkZXZyYWllbnQgdm91cyBkb25uZXIgZGVzIHAtdmFsdWVzIHNpbWlsYWlyZXMgcG91ciB1biBub21icmUgZCdvYnNlcnZhdGlvbnMgYXNzZXogZ3JhbmQuCkVuIHByYXRpcXVlLCBwb3VyIMOpdml0ZXIgdG91dGUgbXVsdGlwbGljYXRpb24gZGUgdGVzdHMsIG9uIGTDqWNpZGUgQVZBTlQgZGUgcmVnYXJkZXIgbGVzIGRvbm7DqWVzIHF1ZWwgdGVzdCBvbiByw6lhbGlzZXJhLCBvbiBsJ8OpY3JpdCBkYW5zIGxlIHBsYW4gZCdhbmFseXNlIHN0YXRpc3RpcXVlLCBwdWlzIG9uIGwnYXBwbGlxdWUuCgpgYGB7cn0KY294cGgoU3Vydih0aW1lLCBldmVudCkgfiBhcm0sIGRhdGE9ZCkKYGBgCgo+IE9idGllbnQtb24gdW4gcsOpc3VsdGF0IGNvaMOpcmVudCBhdmVjIGxlIHRlc3QgZHUgbG9ncmFuayBkYW5zIG5vdHJlIGNhcyA/Cgo+IE1vZGlmaWV6IGxlIGNvZGUgcHLDqWPDqWRlbnQgcG91ciByYWpvdXRlciBkZXMgY292YXJpYWJsZXMgYXUgbW9kw6hsZS4gT2JzZXJ2ZXotdm91cyB1biBlZmZldCBkZSBsJ8OiZ2UgZGVzIHBhdGllbnRzIHN1ciBsZSBoYXphcmQgPwoKYGBge3J9CmNveHBoKFN1cnYodGltZSwgZXZlbnQpIH4gYXJtICsgYWdlLCBkYXRhPWQpCmBgYAoKCiMjIEV2w6luZW1lbnRzIGludGVyY3VycmVudHMgZXQgUEZTCgpWb3VzIHNvdWhhaXRleiDDoCBwcsOpc2VudCB2b3VzIGF0dGFxdWVyIMOgIGxhIGRldXhpw6htZSBxdWVzdGlvbiBkJ2ludMOpcsOqdCBkZSBsJ8OpdHVkZSA6IGV4aXN0ZS10LWlsIHVuIGltcGFjdCBkdSB0cmFpdGVtZW50IHN1ciBsJ8Opdm9sdXRpb24gZGVzIHR1bWV1cnMgPwpPbiBzb3VoYWl0ZSBkb25jIHJlZ2FyZGVyICRUX2knIDo9JCBsZSB0ZW1wcyBhdmFudCBwcm9ncmVzc2lvbiBkZSBsJ2luZGl2aWR1ICRpJC4KCk1BSVMgOiBzaSBvbiBmYWl0IGNlbGEsIG9uIHZhIGF2b2lyIGRldXggcHJvYmzDqG1lcyA6CgoqIGQndW5lIHBhcnQgY2VydGFpbnMgaW5kaXZpZHVzIHNvbnQgcGVyZHVzIGRlIHZ1ZSBhdmFudCBkZSBwcm9ncmVzc2VyLCBjZSBxdSdvbiBlc3QgcHLDqnQgw6AgcHJlbmRyZSBlbiBjb21wdGUgZW4gY2Vuc3VyYW50IMOgIGRyb2l0ZS4KKiBkJ2F1dHJlIHBhcnQgY2VydGFpbnMgaW5kaXZpZHVzIHJpc3F1ZW50IGRlIGTDqWPDqWRlciBBVkFOVCBkZSBwcm9ncmVzc2VyLgoKTG9yc3F1J3VuIMOpdsOpbmVtZW50IChpY2kgbGEgbW9ydCkgZW1ww6pjaGUgZCdvYnNlcnZlciB1biBhdXRyZSDDqXbDqW5lbWVudCAobGEgcHJvZ3Jlc3Npb24pLCBvbiBwYXJsZSBkJ8OpdsOpbmVtZW50cyBpbnRlcmN1cnJlbnRzLgpJbCBleGlzdGUgcGx1c2lldXJzIGZhw6dvbnMgZGUgcHJlbmRyZSBlbiBjb21wdGUgY2UgcHJvYmzDqG1lLCBtYWlzIG9uIG5lIHMnaW50w6lyZXNzZXJhIGljaSBxdSfDoCBsYSBwbHVzIHNpbXBsZSA6IGxvcnNxdWUgbm9zIGRldXggw6l2w6luZW1lbnRzIHNvbnQgdG91cyBkZXV4IG7DqWdhdGlmcyAob3UgcG9zaXRpZnMpIHBvdXIgbGUgcGF0aWVudHMsIGxlIHBsdXMgc2ltcGxlIGVzdCBkZSBkw6lmaW5pciBub3RyZSB0ZW1wcyBkJ2ludMOpcsOqdCBjb21tZSDDqXRhbnQgbGUgcHJlbWllciB0ZW1wcyBhdXF1ZWwgbCd1biBkZXMgZGV1eCDDqXbDqW5lbWVudHMgc3VydmllbnQuCgpJY2ksIG9uIGTDqWZpbml0IGNsYXNzaXF1ZW1lbnQgZW4gY2FuY8Opcm9sb2dpZSBsYSBQRlMgY29tbWUgw6l0YW50IGxhIHN1cnZpZSBzYW5zIHByb2dyZXNzaW9uIChuaSBkw6ljw6hzKS4KRXQgbGUgdGVtcHMgZCdpbnTDqXLDqnQgYXNzb2Npw6kgZXN0IGRvbmMgbGUgZMOpY8OocyBvdSBsYSBwcm9ncmVzc2lvbiA6IGxlIHByZW1pZXIgZGVzIGRldXggcXVpIHN1cnZpZW50LgoKPiBFc3RpbWV6IGxhIFBGUyDDoCBsJ2FpZGUgZCd1biBncmFwaGUgZGUgS2FwbGFuLU1laWVyLgoKPiBDb21wYXJleiBsYSBQRlMgZGFucyBsZXMgZGV1eCBicmFzIMOgIGwnYWlkZSBkJ3VuIHRlc3QgZGUgbG9ncmFuayBvdSBkJ3VuIG1vZMOobGUgZGUgQ294LgoKPiBRdWUgY29uY2x1ZXotdm91cyA/CgpgYGB7cn0KCmQgPC0gZCAlPiUgbXV0YXRlKAoJdGltZV9wcm9nX29yX2RlYXRoID0gYXMubnVtZXJpYyhpZmVsc2UoIWlzLm5hKGRhdGVfcHJvZyksIGRhdGVfcHJvZyAtIGRhdGVfcmFuZG8sIGRhdGVfbGFzdF9mb2xsb3dfdXAgLSBkYXRlX3JhbmRvKSksCglldmVudF9wcm9nX29yX2RlYXRoID0gaWZlbHNlKCFpcy5uYShkYXRlX3Byb2cpIHwgIWlzLm5hKGRhdGVfZGVhdGgpLCAxLCAwKQopCgprbV9wZnMgPC0gc3VydmZpdChTdXJ2KHRpbWVfcHJvZ19vcl9kZWF0aCwgZXZlbnRfcHJvZ19vcl9kZWF0aCkgfiBhcm0sIGRhdGE9ZCkKCmdyYXBoX3BmcyA8LSBnZ3N1cnZwbG90KAogICAga21fcGZzLAogICAgcHZhbCA9IEYsIAogICAgY29uZi5pbnQgPSBGQUxTRSwKICAgIHJpc2sudGFibGUgPSBGLCAjIEFkZCByaXNrIHRhYmxlCiAgICByaXNrLnRhYmxlLmNvbCA9ICJzdHJhdGEiLCAjIENoYW5nZSByaXNrIHRhYmxlIGNvbG9yIGJ5IGdyb3VwcwogICAgI2xpbmV0eXBlID0gInN0cmF0YSIsICMgQ2hhbmdlIGxpbmUgdHlwZSBieSBncm91cHMKICAgIHN1cnYubWVkaWFuLmxpbmUgPSAiaHYiLCAjIFNwZWNpZnkgbWVkaWFuIHN1cnZpdmFsCiAgICBnZ3RoZW1lID0gdGhlbWVfYncoKSAjIENoYW5nZSBnZ3Bsb3QyIHRoZW1lCikKCmNveF9wZnMgPC0gY294cGgoU3Vydih0aW1lX3Byb2dfb3JfZGVhdGgsIGV2ZW50X3Byb2dfb3JfZGVhdGgpIH4gYXJtLCBkYXRhPWQpCmBgYAoKCiMgQ29uY2x1c2lvbi9EaXNjdXNzaW9uCgpEYW5zIGxlcyBlc3NhaXMgY2xpbmlxdWVzLCBsYSBub3JtZSBwb3VyIHMnaW50w6lyZXNzZXIgw6AgdW4gdGVtcHMgZCdhdHRlbnRlIGF2YW50IMOpdsOpbmVtZW50IHNlcmEgc291dmVudCBsZSBkdW8gKnJlcHLDqXNlbnRhdGlvbiBncmFwaGlxdWUgZGUgS2FwbGFuLU1laWVyKiBldCAqbW9kw6hsZSBkZSBDb3gqIGFzc29jacOpLgpMJ3V0aWxpc2F0aW9uIGRlIGNlcyBkZXV4IGNvbmNlcHRzIHN0YXRzIGVzdCBzaW1wbGUsIG1haXMgaWwgZmF1ZHJhIHRvdWpvdXJzIGJpZW4gcHJlbmRyZSBnYXJkZSA6IAoKKiDDoCBiaWVuIHNww6ljaWZpZXIgbGVzIMOpdsOpbmVtZW50cyBjb25zaWTDqXLDqXMgY29tbWUgY2Vuc3VyZSwKKiDDoCBiaWVuIHLDqWZsw6ljaGlyIGF1eCDDqXZlbnR1ZWxzIMOpdsOpbmVtZW50cyBpbnRlci1jdXJyZW50cyBldCDDoCBsYSBkw6lmaW5pdGlvbiBkZSBub3RyZSB0ZW1wcyBkJ2ludMOpcsOqdC4KCkRhbnMgbGVzIMOpdHVkZXMgb2JzZXJ2YXRpb25uZWxsZXMsIGlsIGVzdCDDqWdhbGVtZW50IHBvc3NpYmxlIGRlIGZhaXJlIGRlIGwnYW5hbHlzZSBkZSBzdXJ2aWUuCkxlcyBleGVtcGxlcyBuZSBtYW5xdWVudCBkJ2FpbGxldXJzIHBhcyBkYW5zIGxhIGxpdMOpcmF0dXJlLgoKTUFJUyAhIElsIHZvdXMgZmF1ZHJhIGdhcmRlciDDoCBsJ2VzcHJpdCBkZXV4IHByb2Jsw6htZXMgbWFqZXVycyBkYW5zIGNlcyBjYXMtbMOgLgpDb250cmFpcmVtZW50IMOgIGwnZXNzYWkgY2xpbmlxdWUsIGxhIGTDqWZpbml0aW9uIGR1ICR0XzAkIChyYW5kb21pc2F0aW9uIGRhbnMgbCdlc3NhaSBjbGluaXF1ZSkgZXN0IGRpZmZpY2lsZSDDoCByw6lhbGlzZXIgZGUgZmHDp29uICJqdXN0ZSBldCDDqXF1aXRhYmxlIiBwb3VyIGxlcyBkZXV4IGdyb3VwZXMuCkxlcyBkZXV4IHByaW5jaXBhdXggcHJvYmzDqG1lcyByZW5jb250csOpcyBzb250IGxlcyBzdWl2YW50cyA6CgpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoID0gJzEwMCUnfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiYmlhaXNfdDAucG5nIikKYGBgCgoxKSBTaSBvbiBkw6lmaW5pdCBsZXMgaW5kaXZpZHVzIGQndW4gZ3JvdXBlIGRlIHRyYWl0ZW1lbnQgY29tbWUgw6l0YW50IHRvdXMgY2V1eCBxdWkgb250IGTDqWrDoCBjb21tZW5jw6kgbGUgdHJhaXRlbWVudCBhdmFudCB1bmUgY2VydGFpbmUgZGF0ZSwgYWxvcnMgb24gcydleHBvc2Ugw6AgdW4gKmJpYWlzIGRlIGTDqXBsw6l0aW9uIGRlcyBzdXNjZXB0aWJsZXMqLgpUeXBpcXVlbWVudCwgaW1hZ2luZXogZGVzIGZ1bWV1cnMgZXQgZGVzIG5vbi1mdW1ldXJzLiBMZXMgaW5kaXZpZHVzIHN1c2NlcHRpYmxlcyBkZSBmYWlyZSB1biBjYW5jZXIgZHUgcG91bW9uIGRhbnMgbGUgZ3JvdXBlIGZ1bWV1ciBvbnQgcGV1dC3DqnRyZSBkw6lqw6AgdG91cyBmYWl0IGxldXIgY2FuY2VyLCBjZSBxdWkgcmlzcXVlIGRlIGRpbWludWVyIGxlIG5vbWJyZSBkZSBjYW5jZXJzIGR1IHBvdW1vbiBvYnNlcnbDqXMgZGFucyBsZSBncm91cGUgZnVtZXVyLgoKMikgU2kgbWFpbnRlbmFudCBvbiBzb3VoYWl0ZSDDqXZpdGVyIGNlIHByb2Jsw6htZSBldCBxdSdvbiBkw6lmaW5pdCBxdWVscXUndW4gZGFucyBsZSBncm91cGUgdHJhaXRlbWVudCBjb21tZSBxdWVscXUndW4gcXVpIG4nYXZhaXQgcGFzIGNvbW1lbmPDqSBsZSB0cmFpdGVtZW50IMOgICR0XzAkLCBtYWlzIGxlIGNvbW1lbmNlIHBsdXMgdGFyZC4KQWxvcnMgb24gb2J0aWVudCwgY2V0dGUgZm9pcywgdW4gKmJpYWlzIGRlIHRlbXBzIGltbW9ydGVsKiwgcXVpIGNvcnJlc3BvbmQgYXUgZmFpdCBxdWUgbGVzIHBhdGllbnRzIGRlIGNlIGdyb3VwZSB0cmFpdGVtZW50IG5lIHBldXZlbnQgcGFzIGTDqWPDqWRlciAocGFyIGNvbnN0cnVjdGlvbikgYXZhbnQgbGEgZGF0ZSBkZSB0cmFpdGVtZW50IHF1aSBsZXMgZmFpdCBhcHBhcnRlbmlyIMOgIGxldXIgZ3JvdXBlLgoKVW4gbm9tYnJlIMOpLW5vci1tZSBkJ8OpdHVkZXMgb2JzZXJ2YXRpb25uZWxsZXMgc29udCBjb25jZXJuw6llcyBwYXIgbCd1biwgbCdhdXRyZSwgb3UgbGVzIGRldXggcHJvYmzDqG1lcyDDoCBsYSBmb2lzLCBldCBwbHVzaWV1cnMgZXhlbXBsZXMgaGlzdG9yaXF1ZXMgZGUgZMOpc2FjY29yZHMgZW50cmUgw6l0dWRlcyBvYnNlcnZhdGlvbm5lbGxlcyBldCBlc3NhaXMgY2xpbmlxdWVzIHJlcG9zZW50IHN1ciBjZXMgZGV1eCBwcm9ibMOobWUgZGUgJHRfMCQuCkdhcmRleiBiaWVuIMOnYSBlbiB0w6p0ZSBwb3VyIGNyaXRpcXVlciBsZXMgZnV0dXJzIGFydGljbGVzIHF1ZSB2b3VzIGxpcmV6ICEK