NB : ce qui suit est un document de travail
Nous avons récolté l’ensemble des données disponibles sur l’archive de la Linux Kernel Mailing List à l’aide de notre scraper. Il a suffi d’environ 15 heures au script pour récupérer à peu près 2,5 millions d’emails, le premier datant du 23 juin 1995 et le dernier du 31 octobre 2016. Nous avons récupéré diverses métadonnées dans un fichier csv (0.8 Go) ainsi que le corps de tous les emails (16Go bruts, 4Go compressés).
Cette base de données pourra s’avérer utile à plusieurs titres :
- Donner une perspective d’ensemble sur les circonstances qui ont mené à la création de
git entre 1995 et 2005 : le volume d’emails était-il vraiment devenu ingérable ? Peut-on discerner d’autres motifs qui confirment ou enrichissent notre compréhension de la genèse du dispositif ?
- Nous informer sur la relation entre
git et des gestionnaires de version sur les échanges entre développeurs : l’implémentation d’un gestionnaire de version change-t-elle significativement la nature des échanges ? Observe-t-on une nouvelle hiérarchie dans les conversations avant et après l’emploi de git ? Ou à l’inverse, les données de collaboration git imitent-elles celles de la LKML ?
- Être un outil pour nous assister dans le travail qualitatif de lecture des archives, pour identifier les conversations intéressantes (polémiques, mots-clés pertinents, etc.).
Pour l’instant, nous nous contentons d’une analyse exploratoire en suivant les étapes suivantes :
Préparation des données
Étant donné la taille de la base de données et les requêtes relativement coûteuses à mener, nous allons utilisons le paquet data.table dans R :
library(data.table)
library(ggplot2)
library(ggthemes)
Commençons par charger la base de données en mémoire :
# Load dataset and drop useless columns
lkml <- fread('hypermailByEmail.csv',
drop = c("emailId", "mailingList","timeSent","timeReceived"))
Pour les besoins de l’analyse, il est plus pratique d’avoir plusieurs variables pour la date qu’une unique variable année-mois-jour heures:minutes:secondes fuseau. Nous devrions d’ailleurs l’incorporer directement dans le scraper pour simplifier les analyses par la suite.
# Reorganize datetime metadata
lkml[, `:=` (sentDate = as.IDate(timestampSent),
sentTime = as.ITime(substr(timestampSent, 12, 19)),
sentTZ = substr(timestampSent, 20, 24),
receivedDate = as.IDate(timestampReceived),
receivedTime = as.ITime(substr(timestampReceived, 12, 19)),
receivedTZ = substr(timestampReceived, 20, 24),
timestampSent = NULL,
timestampReceived = NULL)]
Enfin nous indexons nos données sur la date et l’horaire de réception du message. Nous limiterons aussi notre étude exploratoire aux années 1995-2015, l’année 2016 n’étant pas encore terminée. Il nous reste environ 2,4 millions d’emails dans la base.
# Set keys by reference
setkey(lkml, receivedDate, receivedTime)
# Focus on 1995-2015
lkml <- lkml[year(receivedDate) >= 1995 & year(receivedDate) <= 2015]
Évolution du volume d’échanges sur la LKML
Comme nous l’avons montré lors d’une première exploration des archives de la LKML, le problème majeur qui a occasionné une réflexion sur le choix d’un gestionnaire de version était la croissance du nombre de contributeurs et de correctifs envoyés directement à Linus Torvalds. Bien que nous n’ayons pas accès à ses archives personnelles, il est raisonnable de penser qu’on devrait observer conjointement une augmentation du volume de messages sur la LKML. C’est ce que nous allons vérifier.
Nombre de messages
On commence par s’intéresser très simplement à l’évolution du nombre de messages sur la LKML. Nous essayons d’identifier sommairement les messages automatiques générés par git [Voir plus loin] à partir de 2005 :
msgByYear <- lkml[, .(.N,
auto = sum(grepl("\\[.*\\d{1,2}/\\d{1,2}.*\\]", subject))),
by = year(receivedDate)]
msgByYear.m <- melt(msgByYear, id.vars = "year")
ggplot(msgByYear.m, aes(x = year, y = value, fill = variable)) +
geom_bar(stat = "identity") + theme_minimal()

Nous observons très peu de messages en 1995 parce que l’archive commence en cours d’année (au mois de juin). Néanmoins, nous constatons bien une forte augmentation (facteur 2) du nombre de messages sur la LKML entre 1997 (24 225 messages) et 1998 (52 299 messages), ce qui correspond effectivement aux années qui précèdent la première crise autour des gestionnaires de version en 1998. La croissance du nombre de messages à partir de 2005-2006 est en partie liée à des messages automatiques, générés automatiquement par git.
Nombre de contributeurs
Il importe de déterminer dans quelle mesure la croissance du nombre de messages est liée à une implication plus importante des développeurs et à un nombre croissant de contributeurs. Essayons de voir comment le nombre de contributeurs évolue au fil du temps. Nous avons deux proxies possibles : le nom de l’expéditeur ou l’adresse email.
sNameByYear <- lkml[,
.(count = 1),
by = .(year(receivedDate), senderName)
][,
.(nb_sender_name = sum(count)),
by = year
]
sEmailByYear <- lkml[, .(count = 1), by = .(year(receivedDate), senderEmail)][, .(nb_sender_email = sum(count)), by = year]
senderByYear <- merge(sEmailByYear, sNameByYear)
senderByYear.m <- melt(senderByYear, id.vars = 'year')
ggplot(senderByYear.m, aes(x = year, y = value, fill = variable)) +
geom_bar(stat = "identity", position = "dodge") + theme_minimal()

Plusieurs remarques :
- On observe bien un bond important de contributeurs entre 1997 et 1998.
- L’évolution du nombre de contributeurs est loin d’être linéaire, elle diminue même assez significativement entre 2006 et 2007 (baisse d’environ 1000 contributeurs). Faut-il interpréter ces baisses comme le résultat de scissions ou de découragement, ou bien s’agit-il d’opérations de maintenance (mise en place d’un meilleur filtre anti-spam, par exemple) ? [À FAIRE]
- On a plus toujours plus d’emails uniques que de noms d’utilisateurs : il est probable que les utilisateurs aient plusieurs adresses. On remarque d’ailleurs que le nombre d’emails augmentent fortement en 2014, ce qui est lié à une innovation dans
git (publication de correctifs sur la LKML via une adresse spécifique git-bot). Pour les années antérieures, il nous faudra vérifier qu’il ne s’agit pas simplement d’un malfonctionnement de l’archive email et tirer parti de l’information pour identifier les expéditeurs à coup sûr. [À FAIRE]
Il serait également intéressant de connaître les développeurs qui ont le plus contribué à la LKML, et si leurs contributions étaient des réponses ou bien des nouveaux messages :
lkml[,
.(.N,
re = sum(grepl("Re\\s?:", subject, ignore.case = T))),
by = .(senderName)][, ratio := round(re/N, 2)][order(-N)]
De ce tableau, on peut faire l’hypothèse que les mainteneurs (Linus, Cox, Miller, etc.) ont typiquement un ratio de réponses plus important (90% et plus) que les autres contributeurs, puisque leur fonction les oblige à modérer et à corriger les correctifs soumis à la LKML.
Nombre de nouveaux sujets
Nous voudrions aussi voir quel est le ratio de messages qui commencent une conversation par rapport au nombre de réponses :
newMsgByYear <- lkml[year(receivedDate) >= 1995,
.(new = .N,
re = sum(grepl("Re\\s?:", subject, ignore.case = T))),
by = year(receivedDate)][, new := new -re ]
newMsgByYear.m <- melt(newMsgByYear, id.vars = "year")
ggplot(newMsgByYear.m, aes(x = year, y = value, fill = variable)) +
geom_bar(stat = "identity", position = "dodge") + theme_minimal()

Quelques remarques :
- 1995-1997 : le ratio est d’environ deux réponses pour un nouveau message.
- À partir de 1998, ce ratio augmente assez notablement, on a à peu près 3 réponses pour un nouveau message, la taille moyenne des discussions augmente. C’est peut-être une conséquence “logique” de l’augmentation du nombre de contributeurs ; ou bien un reflet de la nouvelle vivacité des discussions (emails avec plusieurs dizaines voire centaines de réponses lors de discussions polémiques, comme dans la crise de septembre 1998).
- À partir de 2005, on remarque que l’augmentation du nombre de nouveaux messages est plus rapide que celle des réponses. C’est probablement lié aux nouvelles fonctionnalités de
git et la capacité d’envoyer rapidement un email pour documenter son correctif [voir plus loin].
Dates et horaires des contributions
Les dates et les horaires des contributions peuvent nous fournir plusieurs informations intéressantes :
- Tout d’abord, il pourrait être intéressant de connaître les horaires d’envoi d’emails pour reconnaître les personnes qui contribuent sur leur temps libre (envoi de nuit, tôt le matin ou durant le week-end).
- Ensuite, nous pourrons utiliser les dates et les horaires d’envoi pour constater le temps de réaction à différents types d’emails, et voir si certaines discussions « s’emballent » plus que d’autres.
Nous avons collecté deux métadonnées qui datent les emails : la date d’envoi et la date de réception. Loin d’être redondantes, ces informations peuvent apporter des informations supplémentaires pour l’analyse :
- Dans les années 1990, il nous a semblé que le delta entre l’envoi et la réception était considérable (souvent plusieurs minutes, parfois plusieurs heures), ce qui témoigne probablement de la qualité des connexions Internet des développeurs ou des aléas du serveur email.
- La métadonnée de date et d’horaire d’envoi contient le fuseau horaire, ce qui pourrait nous donner une information rudimentaire sur la localisation des contributeurs.
Malheureusement, à partir de 2003, il semble que les métadonnées d’envoi sont des simples copies de celles de la réception… Notre analyse des horaires des contributions sera pour l’instant cantonné à cette période.
Horaires
Commençons par étudier les horaires des programmeurs entre 1996 et 2002. Puisque le volume d’emails évolue considérablement, nous avons choisi d’observer les variations en pourcentages :
hourByYear <- lkml[
year(sentDate) >= 1996 & year(sentDate) <= 2002,
.N,
by = .(hour = hour(sentTime), year = year(sentDate))
][, prop := N/sum(N), by = year][order(year, hour)]
ggplot(hourByYear, aes(x = hour, y = prop, fill = factor(year))) +
geom_bar(stat = "identity", position = "dodge") + theme_minimal() +
theme(legend.position = "bottom")

Nous constatons une augmentation du nombre d’emails envoyés pendant l’après-midi (14h-17h) ainsi qu’une diminution marquée du nombre d’emails envoyés entre 7h et 9h. À première vue, cela pourrait suggérer que les profils des contributeurs se professionnalisent ; il faudra le vérifier par la suite.
Jour de la semaine
Nous effectuons la même analyse pour les jours de la semaine (1 = dimanche, 2 = lundi, …, 7 = samedi) :
wdayByYear <- lkml[
year(sentDate) >= 1996 & year(sentDate) <= 2002,
.N,
by = .(wday = wday(sentDate), year = year(sentDate))
][, prop := N/sum(N), by = year][order(year, wday)]
ggplot(wdayByYear, aes(x = factor(wday), y = prop, fill = factor(year))) +
geom_bar(stat = "identity", position = "dodge") + theme_minimal() +
theme(legend.position = "bottom")

Nous constatons ici que l’activité est réduite pendant le week-end (jour 1 et 7), mais aucune évolution temporelle claire n’est perceptible.
Peut-on voir qui répond à qui ?
L’objectif est, à terme, de faire des analyses de réseau sur la LKML, de détecter les motifs par sujet, etc. Pour cela, nous aurons besoin de savoir à quel email répond un message. Malheureusement, cette question n’est pas triviale. Dans les métadonnées récupérées, nous avons bien un champ indiquant à quel email un message répond, mais ce dernier fait souvent défaut.
Notre but est donc d’évaluer la qualité du champ replyto, en le confrontant à un autre indicateur, la présence du mot-clé “Re:” dans le sujet de l’email.
msgsByYear <- lkml[,
.(messages = .N,
re_in_subject = sum(grepl("Re\\s?:", subject, ignore.case = T)),
nb_replyto = sum(!is.na(replyto))),
by = year(receivedDate)][,ratio := nb_replyto/re_in_subject]
msgsByYear
Pour plus de visibilité, traçons l’évolution du ratio \(\frac{\text{Nombre de replyto}}{\text{Nombre de "Re"}}\) :
ggplot(msgsByYear, aes(x = year, y = ratio)) + geom_point() + theme_minimal()

Nous constatons non pas un, mais deux problèmes :
- Le ratio est très bas avant les années 2000 (\(<0.4\)), ce qui est cohérent avec nos observations réalisées lors du travail d’archives.
- Plus surprenant, le ratio dépasse 1 en 2005, ce qui signifierait que des contributeurs se répondent entre eux sans utiliser le mot-clé “Re:”.
Autrement dit, il est possible que le ratio s’améliore non pas parce que la première catégorie d’emails diminue mais parce que la deuxième augmente. Vérifions cette hypothèse avec le graphique suivant :
replByYear <- lkml[,
.(re_no_reply = sum(grepl("Re\\s?:", subject, ignore.case = T) & is.na(replyto)),
reply_no_re = sum(!is.na(replyto) & !grepl("Re\\s?:", subject, ignore.case = T))),
by = year(receivedDate)]
ggplot(replByYear) +
geom_point(aes(x = year, y = re_no_reply, colour = "Re sans replyto")) +
geom_point(aes(x = year, y = reply_no_re, colour = "Replyto sans Re")) + theme_minimal()

Effectivement, on constate une grande augmentation de la deuxième catégorie après 2005. Comme notre analyse va, dans un premier temps, se concentrer sur la période qui précède 2005, ce « problème » n’est pas si urgent.
En revanche, il est clair que l’archive a connu de nombreux déboires en 1998 et 1999 : les emails étant manifestement des réponses (contenant “Re:” dans le titre) mais sans champ replyto sont très nombreux. Ces années étant cruciales pour comprendre la conception de Git, il nous faudra trouver un moyen de palier ce défaut de la base de données.
Le cas des emails “Re:” sans replyto
L’une des solutions envisagées pour reconstituer a posteriori le champ replyto est de créer un script qui explore le contenu des emails. Sur la LKML, il est de coutume de citer le passage de l’email auquel on répond dans le corps du message (voir un exemple ici).
Toutefois, cette méthode peut s’avérer très fastidieuse (temps de calcul), surtout appliquée à 272286 emails. Toute information supplémentaire nous aidera à optimiser l’algorithme. C’est pourquoi nous essayons de trouver quelques mécanismes a priori pour expliquer ce phénomène de non-réponses.
L’archive est structurée en petits dossiers qui regroupent les emails reçus selon la date. Les groupes sont déterminés selon le schéma suivant :
- 1-7 janvier 1995
- 8-15 janvier 1995
- 16-23 janvier 1995
- 24-31 janvier 1995
- 1-7 février 1995
- …
En explorant l’archive, une des hypothèses émise est que l’archive repère mieux les réponses quand une conversation se déroule dans l’un de ses intervalles, mais pas quand elle commence dans l’un et se termine dans l’autre. Un échange se tenant entre le 3 et le 6 du mois aura donc beaucoup plus de chances de conserver son replyto intact qu’une conversation ayant lieu sur plusieurs semaines.
Si cette hypothèse est vraie, et si nous supposons que la plupart des contributeurs répondent rapidement (1-2 jours) aux emails, on devrait alors observer un pic d’emails sans replyto autour du 1, 8, 16 et 24 du mois. C’est ce que nous vérifions avec le graphique suivant :
reNoReply <- lkml[grepl("Re\\s?:", subject, ignore.case = T) & is.na(replyto),
.N,
by = mday(receivedDate)]
ggplot(reNoReply, aes(mday, N)) +
geom_bar(stat = "identity") + theme_minimal()

Cette hypothèse semble confirmée. Il faudrait maintenant aller plus loin et vérifier si ce phénomène est uniquement valable pour les messages qui répondent après ces jours limites à un email reçu avant [À FAIRE]. Si c’est bien le cas, alors l’algorithme pourra fonctionner comme suit :
Pour chaque email "Re" sans replyto :
Extraire la citation de l'email
Pour chaque email dans la même conversation envoyé avant le 1, 8, 16 ou 24 :
Vérifier si la citation correspond à l'email
Si oui :
Remplir le champ `replyto`
Le cas des emails replyto sans “Re:”
Dans le cas des emails ayant un champ replyto non vide mais pas le mot-clé “Re:” dans le sujet, nous n’avons pas repéré de motifs similaires, ce qui nous laisse penser qu’ils ne sont pas un artefact de l’archive elle-même.
Pour nous faire une meilleure idée de ces cas de figure, nous regardons les 50 premiers mails de ce type pour chaque année :
replyNoRe <- lkml[!grepl("Re\\s?:", subject, ignore.case = T) & !is.na(replyto),
.(subject, year = year(receivedDate), senderName, url)]
setkey(replyNoRe, year)
replyNoRe[.(1995:2015), .SD[1:min(50,.N)], by = .EACHI]
Notre exploration de ces emails nous amène à distinguer deux sous-catégories.
Liens de filiation entre différentes conversations
Certains emails semblent être les premiers d’une conversation, mais répondent en réalité à un autre email. Il est probable que cela soit dû au comportement des clients emails des contributeurs, qui répondent à un message avant de changer le sujet, auquel cas le mail conserve la métadonnée de réponse même si le sujet ne contient pas le mot-clé « Re : ».
Il faudra décider de la façon dont nous traiterons ces emails : ouvrent-ils de nouvelles conversations ou bien prolongent-ils une même discussion avec un titre différent ? Une décision raisonnable pourrait être de les considérer comme de nouvelles conversations, et que l’information replyto joue ici le même rôle que le mot-clé “Was:” pour indiquer des liens de filiation entre différentes discussions.
Emails produits par bitkeeper et par git
D’autre part, à partir de 2003, nous remarquons que la plupart des emails contiennent des sujets de ce type :
[PATCH 1/3 RFC] Coccinelle: drop unecessary duplicated init_compltion calls
[PATCH 2/3 RFC] Coccinelle: check for incorrect DECLARE_COMPLETION use
[PATCH 3/3 RFC] Coccinelle: incorrect use of multiple init_completion
Ils sont de plus rédigés par la même personne.
Il s’agit visiblement d’une fonctionnalité des gestionnaires de version apparue en 2003 dans bitkeeper et implémentée par la suite dans git, ce qui explique que ces emails se multiplient après 2005, date d’adoption officielle de git. Pour résumer, les contributeurs du noyau peuvent soumettre un correctif à la LKML directement depuis la ligne de commande. S’ils modifient n fichiers, alors [PATCH 0/n] contient le message explicatif et les [PATCH 1/n]…[PATCH n/n] contiennent les diff pour chaque fichier modifié.
Ces emails ne sont donc ni un bug de l’archive, ni des « vrais » emails (ils n’ont pas été rédigé par leur expéditeur).
Bilan
Il reste encore un travail relativement important à effectuer pour nettoyer et standardiser la base de données, en particulier pour récupérer les replyto, mais cela nous paraît réalisable. Par ailleurs, il nous faudra travailler sur la base pour être en mesure de produire facilement des analyses à 3 niveaux : les contributeurs, les conversations (un message et ses réponses) et les messages individuels. À partir de là, nous pourrons procéder à des analyses plus complexes (analyse de réseau, clustering et classification).
Enfin, dans un second temps, il pourra être intéressant d’incorporer le contenu des emails à la base pour l’enrichir (textmining) avec de nouvelles caractèristiques (longueur des emails, mots-clés employés, etc.).
LS0tCnRpdGxlOiAiRXhwbG9yYXRpb24gZGUgbGEgYmFzZSBkZSBkb25uw6llcyBMS01MIgphdXRob3I6ICJHYWJyaWVsIEFsY2FyYXMiCmRhdGU6ICJNZXJjcmVkaSAzMCBub3ZlbWJyZSAyMDE2IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoqKk5CIDogY2UgcXVpIHN1aXQgZXN0IHVuIGRvY3VtZW50IGRlIHRyYXZhaWwqKgoKTm91cyBhdm9ucyByw6ljb2x0w6kgbCdlbnNlbWJsZSBkZXMgZG9ubsOpZXMgZGlzcG9uaWJsZXMgc3VyIGwnYXJjaGl2ZSBkZSBsYSBbTGludXggS2VybmVsIE1haWxpbmcgTGlzdF0oaHR0cDovL2xrbWwuaXUuZWR1L2h5cGVybWFpbC9saW51eC8pIMOgIGwnYWlkZSBkZSBub3RyZSBbc2NyYXBlcl0oaHR0cHM6Ly9naXRodWIuY29tL2dhYWxjYXJhcy9tYWlsaW5nTGlzdFNjcmFwZXIvKS4gSWwgYSBzdWZmaSBkJ2Vudmlyb24gMTUgaGV1cmVzIGF1IHNjcmlwdCBwb3VyIHLDqWN1cMOpcmVyIMOgIHBldSBwcsOocyAyLDUgbWlsbGlvbnMgZCdlbWFpbHMsIGxlIHByZW1pZXIgZGF0YW50IGR1IDIzIGp1aW4gMTk5NSBldCBsZSBkZXJuaWVyIGR1IDMxIG9jdG9icmUgMjAxNi4gTm91cyBhdm9ucyByw6ljdXDDqXLDqSBkaXZlcnNlcyBtw6l0YWRvbm7DqWVzIGRhbnMgdW4gZmljaGllciBjc3YgKDAuOCBHbykgYWluc2kgcXVlIGxlIGNvcnBzIGRlIHRvdXMgbGVzIGVtYWlscyAoMTZHbyBicnV0cywgNEdvIGNvbXByZXNzw6lzKS4KCkNldHRlIGJhc2UgZGUgZG9ubsOpZXMgcG91cnJhIHMnYXbDqXJlciB1dGlsZSDDoCBwbHVzaWV1cnMgdGl0cmVzIDoKCjEuIERvbm5lciB1bmUgcGVyc3BlY3RpdmUgZCdlbnNlbWJsZSBzdXIgbGVzIGNpcmNvbnN0YW5jZXMgcXVpIG9udCBtZW7DqSDDoCBsYSBjcsOpYXRpb24gZGUgYGdpdGAgZW50cmUgMTk5NSBldCAyMDA1IDogbGUgdm9sdW1lIGQnZW1haWxzIMOpdGFpdC1pbCB2cmFpbWVudCBkZXZlbnUgaW5nw6lyYWJsZSA/IFBldXQtb24gZGlzY2VybmVyIGQnYXV0cmVzIG1vdGlmcyBxdWkgY29uZmlybWVudCBvdSBlbnJpY2hpc3NlbnQgbm90cmUgY29tcHLDqWhlbnNpb24gZGUgbGEgZ2Vuw6hzZSBkdSBkaXNwb3NpdGlmID8KMi4gTm91cyBpbmZvcm1lciBzdXIgbGEgcmVsYXRpb24gZW50cmUgYGdpdGAgZXQgZGVzIGdlc3Rpb25uYWlyZXMgZGUgdmVyc2lvbiBzdXIgbGVzIMOpY2hhbmdlcyBlbnRyZSBkw6l2ZWxvcHBldXJzIDogbCdpbXBsw6ltZW50YXRpb24gZCd1biBnZXN0aW9ubmFpcmUgZGUgdmVyc2lvbiBjaGFuZ2UtdC1lbGxlIHNpZ25pZmljYXRpdmVtZW50IGxhIG5hdHVyZSBkZXMgw6ljaGFuZ2VzID8gT2JzZXJ2ZS10LW9uIHVuZSBub3V2ZWxsZSBoacOpcmFyY2hpZSBkYW5zIGxlcyBjb252ZXJzYXRpb25zIGF2YW50IGV0IGFwcsOocyBsJ2VtcGxvaSBkZSBgZ2l0YCA/IE91IMOgIGwnaW52ZXJzZSwgbGVzIGRvbm7DqWVzIGRlIGNvbGxhYm9yYXRpb24gYGdpdGAgaW1pdGVudC1lbGxlcyBjZWxsZXMgZGUgbGEgTEtNTCA/CjMuIMOKdHJlIHVuIG91dGlsIHBvdXIgbm91cyBhc3Npc3RlciBkYW5zIGxlIHRyYXZhaWwgcXVhbGl0YXRpZiBkZSBsZWN0dXJlIGRlcyBhcmNoaXZlcywgcG91ciBpZGVudGlmaWVyIGxlcyBjb252ZXJzYXRpb25zIGludMOpcmVzc2FudGVzIChwb2zDqW1pcXVlcywgbW90cy1jbMOpcyBwZXJ0aW5lbnRzLCBldGMuKS4KClBvdXIgbCdpbnN0YW50LCBub3VzIG5vdXMgY29udGVudG9ucyBkJ3VuZSBhbmFseXNlIGV4cGxvcmF0b2lyZSBlbiBzdWl2YW50IGxlcyDDqXRhcGVzIHN1aXZhbnRlcyA6CgorIFtQcsOpcGFyYXRpb24gZGVzIGRvbm7DqWVzXSgjcHJlcGFyYXRpb24tZGVzLWRvbm5lZXMpCisgW8OJdm9sdXRpb24gZHUgdm9sdW1lIGTigJnDqWNoYW5nZXMgc3VyIGxhIExLTUxdKCNldm9sdXRpb24tZHUtdm9sdW1lLWRlY2hhbmdlcy1zdXItbGEtbGttbCkKKyBbRGF0ZXMgZXQgaG9yYWlyZXMgZGVzIGNvbnRyaWJ1dGlvbnNdKCNkYXRlcy1ldC1ob3JhaXJlcy1kZXMtY29udHJpYnV0aW9ucykKKyBbUGV1dC1vbiB2b2lyIHF1aSByw6lwb25kIMOgIHF1aSA/XSgjcGV1dC1vbi12b2lyLXF1aS1yZXBvbmQtYS1xdWkpCisgW0JpbGFuXSgjYmlsYW4pCgojIFByw6lwYXJhdGlvbiBkZXMgZG9ubsOpZXMKCsOJdGFudCBkb25uw6kgbGEgdGFpbGxlIGRlIGxhIGJhc2UgZGUgZG9ubsOpZXMgZXQgbGVzIHJlcXXDqnRlcyByZWxhdGl2ZW1lbnQgY2/Du3RldXNlcyDDoCBtZW5lciwgbm91cyBhbGxvbnMgdXRpbGlzb25zIGxlIHBhcXVldCBbZGF0YS50YWJsZV0oaHR0cHM6Ly9naXRodWIuY29tL1JkYXRhdGFibGUvZGF0YS50YWJsZSkgZGFucyBSIDoKCmBgYHtyIExpYnJhaXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdndGhlbWVzKQpgYGAKCkNvbW1lbsOnb25zIHBhciBjaGFyZ2VyIGxhIGJhc2UgZGUgZG9ubsOpZXMgZW4gbcOpbW9pcmUgOgoKYGBge3IgQ2hhcmdlbWVudCBkZXMgZG9ubsOpZXMsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CiMgTG9hZCBkYXRhc2V0IGFuZCBkcm9wIHVzZWxlc3MgY29sdW1ucwpsa21sIDwtIGZyZWFkKCdoeXBlcm1haWxCeUVtYWlsLmNzdicsCiAgICAgICAgICAgICAgZHJvcCA9IGMoImVtYWlsSWQiLCAibWFpbGluZ0xpc3QiLCJ0aW1lU2VudCIsInRpbWVSZWNlaXZlZCIpKQpgYGAKClBvdXIgbGVzIGJlc29pbnMgZGUgbCdhbmFseXNlLCBpbCBlc3QgcGx1cyBwcmF0aXF1ZSBkJ2F2b2lyIHBsdXNpZXVycyB2YXJpYWJsZXMgcG91ciBsYSBkYXRlIHF1J3VuZSB1bmlxdWUgdmFyaWFibGUgYGFubsOpZS1tb2lzLWpvdXIgaGV1cmVzOm1pbnV0ZXM6c2Vjb25kZXMgZnVzZWF1YC4gTm91cyBkZXZyaW9ucyBkJ2FpbGxldXJzIGwnaW5jb3Jwb3JlciBkaXJlY3RlbWVudCBkYW5zIGxlIHNjcmFwZXIgcG91ciBzaW1wbGlmaWVyIGxlcyBhbmFseXNlcyBwYXIgbGEgc3VpdGUuCgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KIyBSZW9yZ2FuaXplIGRhdGV0aW1lIG1ldGFkYXRhCmxrbWxbLCBgOj1gIChzZW50RGF0ZSA9IGFzLklEYXRlKHRpbWVzdGFtcFNlbnQpLAogICAgICAgICAgICAgc2VudFRpbWUgPSBhcy5JVGltZShzdWJzdHIodGltZXN0YW1wU2VudCwgMTIsIDE5KSksCiAgICAgICAgICAgICBzZW50VFogPSBzdWJzdHIodGltZXN0YW1wU2VudCwgMjAsIDI0KSwKICAgICAgICAgICAgIHJlY2VpdmVkRGF0ZSA9IGFzLklEYXRlKHRpbWVzdGFtcFJlY2VpdmVkKSwKICAgICAgICAgICAgIHJlY2VpdmVkVGltZSA9IGFzLklUaW1lKHN1YnN0cih0aW1lc3RhbXBSZWNlaXZlZCwgMTIsIDE5KSksCiAgICAgICAgICAgICByZWNlaXZlZFRaID0gc3Vic3RyKHRpbWVzdGFtcFJlY2VpdmVkLCAyMCwgMjQpLAogICAgICAgICAgICAgdGltZXN0YW1wU2VudCA9IE5VTEwsCiAgICAgICAgICAgICB0aW1lc3RhbXBSZWNlaXZlZCA9IE5VTEwpXQpgYGAKCkVuZmluIG5vdXMgaW5kZXhvbnMgbm9zIGRvbm7DqWVzIHN1ciBsYSBkYXRlIGV0IGwnaG9yYWlyZSBkZSByw6ljZXB0aW9uIGR1IG1lc3NhZ2UuIE5vdXMgbGltaXRlcm9ucyBhdXNzaSBub3RyZSDDqXR1ZGUgZXhwbG9yYXRvaXJlIGF1eCBhbm7DqWVzIDE5OTUtMjAxNSwgbCdhbm7DqWUgMjAxNiBuJ8OpdGFudCBwYXMgZW5jb3JlIHRlcm1pbsOpZS4gSWwgbm91cyByZXN0ZSBlbnZpcm9uIDIsNCBtaWxsaW9ucyBkJ2VtYWlscyBkYW5zIGxhIGJhc2UuCgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KIyBTZXQga2V5cyBieSByZWZlcmVuY2UKc2V0a2V5KGxrbWwsIHJlY2VpdmVkRGF0ZSwgcmVjZWl2ZWRUaW1lKQoKIyBGb2N1cyBvbiAxOTk1LTIwMTUKbGttbCA8LSBsa21sW3llYXIocmVjZWl2ZWREYXRlKSA+PSAxOTk1ICYgeWVhcihyZWNlaXZlZERhdGUpIDw9IDIwMTVdCmBgYAoKIyDDiXZvbHV0aW9uIGR1IHZvbHVtZSBkJ8OpY2hhbmdlcyBzdXIgbGEgTEtNTAoKQ29tbWUgbm91cyBsJ2F2b25zIG1vbnRyw6kgbG9ycyBkJ3VuZSBwcmVtacOocmUgZXhwbG9yYXRpb24gZGVzIGFyY2hpdmVzIGRlIGxhIExLTUwsIGxlIHByb2Jsw6htZSBtYWpldXIgcXVpIGEgb2NjYXNpb25uw6kgdW5lIHLDqWZsZXhpb24gc3VyIGxlIGNob2l4IGQndW4gZ2VzdGlvbm5haXJlIGRlIHZlcnNpb24gw6l0YWl0IGxhIGNyb2lzc2FuY2UgZHUgbm9tYnJlIGRlIGNvbnRyaWJ1dGV1cnMgZXQgZGUgY29ycmVjdGlmcyBlbnZvecOpcyBkaXJlY3RlbWVudCDDoCBMaW51cyBUb3J2YWxkcy4gQmllbiBxdWUgbm91cyBuJ2F5b25zIHBhcyBhY2PDqHMgw6Agc2VzIGFyY2hpdmVzIHBlcnNvbm5lbGxlcywgaWwgZXN0IHJhaXNvbm5hYmxlIGRlIHBlbnNlciBxdSdvbiBkZXZyYWl0IG9ic2VydmVyIGNvbmpvaW50ZW1lbnQgdW5lIGF1Z21lbnRhdGlvbiBkdSB2b2x1bWUgZGUgbWVzc2FnZXMgc3VyIGxhIExLTUwuIEMnZXN0IGNlIHF1ZSBub3VzIGFsbG9ucyB2w6lyaWZpZXIuCgojIyBOb21icmUgZGUgbWVzc2FnZXMKCk9uIGNvbW1lbmNlIHBhciBzJ2ludMOpcmVzc2VyIHRyw6hzIHNpbXBsZW1lbnQgw6AgbCfDqXZvbHV0aW9uIGR1IG5vbWJyZSBkZSBtZXNzYWdlcyBzdXIgbGEgTEtNTC4gTm91cyBlc3NheW9ucyBkJ2lkZW50aWZpZXIgc29tbWFpcmVtZW50IGxlcyBtZXNzYWdlcyBhdXRvbWF0aXF1ZXMgZ8OpbsOpcsOpcyBwYXIgYGdpdGAgW1tWb2lyIHBsdXMgbG9pbl0oI2VtYWlscy1wcm9kdWl0cy1wYXItYml0a2VlcGVyLWV0LXBhci1naXQpXSDDoCBwYXJ0aXIgZGUgMjAwNSA6CgpgYGB7ciBNZXNzYWdlcyBwYXIgYW5uw6llfQptc2dCeVllYXIgPC0gbGttbFssIC4oLk4sCiAgICAgICAgICAgICAgICAgICAgYXV0byA9IHN1bShncmVwbCgiXFxbLipcXGR7MSwyfS9cXGR7MSwyfS4qXFxdIiwgc3ViamVjdCkpKSwKICAgICAgICAgICAgICAgICAgYnkgPSB5ZWFyKHJlY2VpdmVkRGF0ZSldCm1zZ0J5WWVhci5tIDwtIG1lbHQobXNnQnlZZWFyLCBpZC52YXJzID0gInllYXIiKQoKZ2dwbG90KG1zZ0J5WWVhci5tLCBhZXMoeCA9IHllYXIsIHkgPSB2YWx1ZSwgZmlsbCA9IHZhcmlhYmxlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHRoZW1lX21pbmltYWwoKQpgYGAKCk5vdXMgb2JzZXJ2b25zIHRyw6hzIHBldSBkZSBtZXNzYWdlcyBlbiAxOTk1IHBhcmNlIHF1ZSBsJ2FyY2hpdmUgY29tbWVuY2UgZW4gY291cnMgZCdhbm7DqWUgKGF1IG1vaXMgZGUganVpbikuIE7DqWFubW9pbnMsIG5vdXMgY29uc3RhdG9ucyBiaWVuIHVuZSBmb3J0ZSBhdWdtZW50YXRpb24gKGZhY3RldXIgMikgZHUgbm9tYnJlIGRlIG1lc3NhZ2VzIHN1ciBsYSBMS01MIGVudHJlIDE5OTcgKDI0IDIyNSBtZXNzYWdlcykgZXQgMTk5OCAoNTIgMjk5IG1lc3NhZ2VzKSwgY2UgcXVpIGNvcnJlc3BvbmQgZWZmZWN0aXZlbWVudCBhdXggYW5uw6llcyBxdWkgcHLDqWPDqGRlbnQgbGEgcHJlbWnDqHJlIGNyaXNlIGF1dG91ciBkZXMgZ2VzdGlvbm5haXJlcyBkZSB2ZXJzaW9uIGVuIDE5OTguIExhIGNyb2lzc2FuY2UgZHUgbm9tYnJlIGRlIG1lc3NhZ2VzIMOgIHBhcnRpciBkZSAyMDA1LTIwMDYgZXN0IGVuIHBhcnRpZSBsacOpZSDDoCBkZXMgbWVzc2FnZXMgYXV0b21hdGlxdWVzLCBnw6luw6lyw6lzIGF1dG9tYXRpcXVlbWVudCBwYXIgYGdpdGAuCgojIyBOb21icmUgZGUgY29udHJpYnV0ZXVycwoKSWwgaW1wb3J0ZSBkZSBkw6l0ZXJtaW5lciBkYW5zIHF1ZWxsZSBtZXN1cmUgbGEgY3JvaXNzYW5jZSBkdSBub21icmUgZGUgbWVzc2FnZXMgZXN0IGxpw6llIMOgIHVuZSBpbXBsaWNhdGlvbiBwbHVzIGltcG9ydGFudGUgZGVzIGTDqXZlbG9wcGV1cnMgZXQgw6AgdW4gbm9tYnJlIGNyb2lzc2FudCBkZSBjb250cmlidXRldXJzLiBFc3NheW9ucyBkZSB2b2lyIGNvbW1lbnQgbGUgbm9tYnJlIGRlIGNvbnRyaWJ1dGV1cnMgw6l2b2x1ZSBhdSBmaWwgZHUgdGVtcHMuIE5vdXMgYXZvbnMgZGV1eCBwcm94aWVzIHBvc3NpYmxlcyA6IGxlIG5vbSBkZSBsJ2V4cMOpZGl0ZXVyIG91IGwnYWRyZXNzZSBlbWFpbC4KCmBgYHtyIENvbnRyaWJ1dGV1cnMgcGFyIGFubsOpZSwgZWNobz1UUlVFfQpzTmFtZUJ5WWVhciA8LSBsa21sWywKICAuKGNvdW50ID0gMSksCiAgYnkgPSAuKHllYXIocmVjZWl2ZWREYXRlKSwgc2VuZGVyTmFtZSkKICBdWywKICAgIC4obmJfc2VuZGVyX25hbWUgPSBzdW0oY291bnQpKSwKICAgIGJ5ID0geWVhcgogIF0Kc0VtYWlsQnlZZWFyIDwtIGxrbWxbLCAuKGNvdW50ID0gMSksIGJ5ID0gLih5ZWFyKHJlY2VpdmVkRGF0ZSksIHNlbmRlckVtYWlsKV1bLCAuKG5iX3NlbmRlcl9lbWFpbCA9IHN1bShjb3VudCkpLCBieSA9IHllYXJdCnNlbmRlckJ5WWVhciA8LSBtZXJnZShzRW1haWxCeVllYXIsIHNOYW1lQnlZZWFyKQpzZW5kZXJCeVllYXIubSA8LSBtZWx0KHNlbmRlckJ5WWVhciwgaWQudmFycyA9ICd5ZWFyJykKCmdncGxvdChzZW5kZXJCeVllYXIubSwgYWVzKHggPSB5ZWFyLCB5ID0gdmFsdWUsIGZpbGwgPSB2YXJpYWJsZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIHRoZW1lX21pbmltYWwoKQpgYGAKClBsdXNpZXVycyByZW1hcnF1ZXMgOiAKCisgT24gb2JzZXJ2ZSBiaWVuIHVuIGJvbmQgaW1wb3J0YW50IGRlIGNvbnRyaWJ1dGV1cnMgZW50cmUgMTk5NyBldCAxOTk4LgorIEwnw6l2b2x1dGlvbiBkdSBub21icmUgZGUgY29udHJpYnV0ZXVycyBlc3QgbG9pbiBkJ8OqdHJlIGxpbsOpYWlyZSwgZWxsZSBkaW1pbnVlIG3Dqm1lIGFzc2V6IHNpZ25pZmljYXRpdmVtZW50IGVudHJlIDIwMDYgZXQgMjAwNyAoYmFpc3NlIGQnZW52aXJvbiAxMDAwIGNvbnRyaWJ1dGV1cnMpLiBGYXV0LWlsIGludGVycHLDqXRlciBjZXMgYmFpc3NlcyBjb21tZSBsZSByw6lzdWx0YXQgZGUgc2Npc3Npb25zIG91IGRlIGTDqWNvdXJhZ2VtZW50LCBvdSBiaWVuIHMnYWdpdC1pbCBkJ29ww6lyYXRpb25zIGRlIG1haW50ZW5hbmNlIChtaXNlIGVuIHBsYWNlIGQndW4gbWVpbGxldXIgZmlsdHJlIGFudGktc3BhbSwgcGFyIGV4ZW1wbGUpID8gKipbw4AgRkFJUkVdKioKKyBPbiBhIHBsdXMgdG91am91cnMgcGx1cyBkJ2VtYWlscyB1bmlxdWVzIHF1ZSBkZSBub21zIGQndXRpbGlzYXRldXJzIDogaWwgZXN0IHByb2JhYmxlIHF1ZSBsZXMgdXRpbGlzYXRldXJzIGFpZW50IHBsdXNpZXVycyBhZHJlc3Nlcy4gT24gcmVtYXJxdWUgZCdhaWxsZXVycyBxdWUgbGUgbm9tYnJlIGQnZW1haWxzIGF1Z21lbnRlbnQgZm9ydGVtZW50IGVuIDIwMTQsIGNlIHF1aSBlc3QgbGnDqSDDoCB1bmUgaW5ub3ZhdGlvbiBkYW5zIGBnaXRgIChwdWJsaWNhdGlvbiBkZSBjb3JyZWN0aWZzIHN1ciBsYSBMS01MIHZpYSB1bmUgYWRyZXNzZSBzcMOpY2lmaXF1ZSBnaXQtYm90KS4gUG91ciBsZXMgYW5uw6llcyBhbnTDqXJpZXVyZXMsIGlsIG5vdXMgZmF1ZHJhIHbDqXJpZmllciBxdSdpbCBuZSBzJ2FnaXQgcGFzIHNpbXBsZW1lbnQgZCd1biBtYWxmb25jdGlvbm5lbWVudCBkZSBsJ2FyY2hpdmUgZW1haWwgZXQgdGlyZXIgcGFydGkgZGUgbCdpbmZvcm1hdGlvbiBwb3VyIGlkZW50aWZpZXIgbGVzIGV4cMOpZGl0ZXVycyDDoCBjb3VwIHPDu3IuICoqW8OAIEZBSVJFXSoqCgpJbCBzZXJhaXQgw6lnYWxlbWVudCBpbnTDqXJlc3NhbnQgZGUgY29ubmHDrnRyZSBsZXMgZMOpdmVsb3BwZXVycyBxdWkgb250IGxlIHBsdXMgY29udHJpYnXDqSDDoCBsYSBMS01MLCBldCBzaSBsZXVycyBjb250cmlidXRpb25zIMOpdGFpZW50IGRlcyByw6lwb25zZXMgb3UgYmllbiBkZXMgbm91dmVhdXggbWVzc2FnZXMgOgoKYGBge3J9CmxrbWxbLAogICAgIC4oLk4sCiAgICAgICByZSA9IHN1bShncmVwbCgiUmVcXHM/OiIsIHN1YmplY3QsIGlnbm9yZS5jYXNlID0gVCkpKSwKICAgICBieSA9IC4oc2VuZGVyTmFtZSldWywgcmF0aW8gOj0gcm91bmQocmUvTiwgMildW29yZGVyKC1OKV0KYGBgCgpEZSBjZSB0YWJsZWF1LCBvbiBwZXV0IGZhaXJlIGwnaHlwb3Row6hzZSBxdWUgbGVzIG1haW50ZW5ldXJzIChMaW51cywgQ294LCBNaWxsZXIsIGV0Yy4pIG9udCB0eXBpcXVlbWVudCB1biByYXRpbyBkZSByw6lwb25zZXMgcGx1cyBpbXBvcnRhbnQgKDkwJSBldCBwbHVzKSBxdWUgbGVzIGF1dHJlcyBjb250cmlidXRldXJzLCBwdWlzcXVlIGxldXIgZm9uY3Rpb24gbGVzIG9ibGlnZSDDoCBtb2TDqXJlciBldCDDoCBjb3JyaWdlciBsZXMgY29ycmVjdGlmcyBzb3VtaXMgw6AgbGEgTEtNTC4KCiMjIE5vbWJyZSBkZSBub3V2ZWF1eCBzdWpldHMKCk5vdXMgdm91ZHJpb25zIGF1c3NpIHZvaXIgcXVlbCBlc3QgbGUgcmF0aW8gZGUgbWVzc2FnZXMgcXVpIGNvbW1lbmNlbnQgdW5lIGNvbnZlcnNhdGlvbiBwYXIgcmFwcG9ydCBhdSBub21icmUgZGUgcsOpcG9uc2VzIDoKCmBgYHtyfQpuZXdNc2dCeVllYXIgPC0gbGttbFt5ZWFyKHJlY2VpdmVkRGF0ZSkgPj0gMTk5NSwKICAgICAgICAgICAgICAgICAgICAgLihuZXcgPSAuTiwKICAgICAgICAgICAgICAgICAgICAgICByZSA9IHN1bShncmVwbCgiUmVcXHM/OiIsIHN1YmplY3QsIGlnbm9yZS5jYXNlID0gVCkpKSwKICAgICAgICAgICAgICAgICAgICAgYnkgPSB5ZWFyKHJlY2VpdmVkRGF0ZSldWywgbmV3IDo9IG5ldyAtcmUgXQpuZXdNc2dCeVllYXIubSA8LSBtZWx0KG5ld01zZ0J5WWVhciwgaWQudmFycyA9ICJ5ZWFyIikKCmdncGxvdChuZXdNc2dCeVllYXIubSwgYWVzKHggPSB5ZWFyLCB5ID0gdmFsdWUsIGZpbGwgPSB2YXJpYWJsZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIHRoZW1lX21pbmltYWwoKQpgYGAKClF1ZWxxdWVzIHJlbWFycXVlcyA6CgorIDE5OTUtMTk5NyA6IGxlIHJhdGlvIGVzdCBkJ2Vudmlyb24gZGV1eCByw6lwb25zZXMgcG91ciB1biBub3V2ZWF1IG1lc3NhZ2UuCisgw4AgcGFydGlyIGRlIDE5OTgsIGNlIHJhdGlvIGF1Z21lbnRlIGFzc2V6IG5vdGFibGVtZW50LCBvbiBhIMOgIHBldSBwcsOocyAzIHLDqXBvbnNlcyBwb3VyIHVuIG5vdXZlYXUgbWVzc2FnZSwgbGEgdGFpbGxlIG1veWVubmUgZGVzIGRpc2N1c3Npb25zIGF1Z21lbnRlLiBDJ2VzdCBwZXV0LcOqdHJlIHVuZSBjb25zw6lxdWVuY2UgImxvZ2lxdWUiIGRlIGwnYXVnbWVudGF0aW9uIGR1IG5vbWJyZSBkZSBjb250cmlidXRldXJzIDsgb3UgYmllbiB1biByZWZsZXQgZGUgbGEgbm91dmVsbGUgdml2YWNpdMOpIGRlcyBkaXNjdXNzaW9ucyAoZW1haWxzIGF2ZWMgcGx1c2lldXJzIGRpemFpbmVzIHZvaXJlIGNlbnRhaW5lcyBkZSByw6lwb25zZXMgbG9ycyBkZSBkaXNjdXNzaW9ucyBwb2zDqW1pcXVlcywgY29tbWUgZGFucyBsYSBjcmlzZSBkZSBzZXB0ZW1icmUgMTk5OCkuCisgw4AgcGFydGlyIGRlIDIwMDUsIG9uIHJlbWFycXVlIHF1ZSBsJ2F1Z21lbnRhdGlvbiBkdSBub21icmUgZGUgbm91dmVhdXggbWVzc2FnZXMgZXN0IHBsdXMgcmFwaWRlIHF1ZSBjZWxsZSBkZXMgcsOpcG9uc2VzLiBDJ2VzdCBwcm9iYWJsZW1lbnQgbGnDqSBhdXggbm91dmVsbGVzIGZvbmN0aW9ubmFsaXTDqXMgZGUgYGdpdGAgZXQgbGEgY2FwYWNpdMOpIGQnZW52b3llciByYXBpZGVtZW50IHVuIGVtYWlsIHBvdXIgZG9jdW1lbnRlciBzb24gY29ycmVjdGlmIFtbdm9pciBwbHVzIGxvaW5dKCNlbWFpbHMtcHJvZHVpdHMtcGFyLWJpdGtlZXBlci1ldC1wYXItZ2l0KV0uCgojIERhdGVzIGV0IGhvcmFpcmVzIGRlcyBjb250cmlidXRpb25zCgpMZXMgZGF0ZXMgZXQgbGVzIGhvcmFpcmVzIGRlcyBjb250cmlidXRpb25zIHBldXZlbnQgbm91cyBmb3VybmlyIHBsdXNpZXVycyBpbmZvcm1hdGlvbnMgaW50w6lyZXNzYW50ZXMgOgoKKyBUb3V0IGQnYWJvcmQsIGlsIHBvdXJyYWl0IMOqdHJlIGludMOpcmVzc2FudCBkZSBjb25uYcOudHJlIGxlcyBob3JhaXJlcyBkJ2Vudm9pIGQnZW1haWxzIHBvdXIgcmVjb25uYcOudHJlIGxlcyBwZXJzb25uZXMgcXVpIGNvbnRyaWJ1ZW50IHN1ciBsZXVyIHRlbXBzIGxpYnJlIChlbnZvaSBkZSBudWl0LCB0w7R0IGxlIG1hdGluIG91IGR1cmFudCBsZSB3ZWVrLWVuZCkuCisgRW5zdWl0ZSwgbm91cyBwb3Vycm9ucyB1dGlsaXNlciBsZXMgZGF0ZXMgZXQgbGVzIGhvcmFpcmVzIGQnZW52b2kgcG91ciBjb25zdGF0ZXIgbGUgdGVtcHMgZGUgcsOpYWN0aW9uIMOgIGRpZmbDqXJlbnRzIHR5cGVzIGQnZW1haWxzLCBldCB2b2lyIHNpIGNlcnRhaW5lcyBkaXNjdXNzaW9ucyDCqyBzJ2VtYmFsbGVudCDCuyBwbHVzIHF1ZSBkJ2F1dHJlcy4KCk5vdXMgYXZvbnMgY29sbGVjdMOpIGRldXggbcOpdGFkb25uw6llcyBxdWkgZGF0ZW50IGxlcyBlbWFpbHMgOiBsYSBkYXRlIGQnZW52b2kgZXQgbGEgZGF0ZSBkZSByw6ljZXB0aW9uLiBMb2luIGQnw6p0cmUgcmVkb25kYW50ZXMsIGNlcyBpbmZvcm1hdGlvbnMgcGV1dmVudCBhcHBvcnRlciBkZXMgaW5mb3JtYXRpb25zIHN1cHBsw6ltZW50YWlyZXMgcG91ciBsJ2FuYWx5c2UgOgoKKyBEYW5zIGxlcyBhbm7DqWVzIDE5OTAsIGlsIG5vdXMgYSBzZW1ibMOpIHF1ZSBsZSBkZWx0YSBlbnRyZSBsJ2Vudm9pIGV0IGxhIHLDqWNlcHRpb24gw6l0YWl0IGNvbnNpZMOpcmFibGUgKHNvdXZlbnQgcGx1c2lldXJzIG1pbnV0ZXMsIHBhcmZvaXMgcGx1c2lldXJzIGhldXJlcyksIGNlIHF1aSB0w6ltb2lnbmUgcHJvYmFibGVtZW50IGRlIGxhIHF1YWxpdMOpIGRlcyBjb25uZXhpb25zIEludGVybmV0IGRlcyBkw6l2ZWxvcHBldXJzIG91IGRlcyBhbMOpYXMgZHUgc2VydmV1ciBlbWFpbC4KKyBMYSBtw6l0YWRvbm7DqWUgZGUgZGF0ZSBldCBkJ2hvcmFpcmUgZCdlbnZvaSBjb250aWVudCBsZSBmdXNlYXUgaG9yYWlyZSwgY2UgcXVpIHBvdXJyYWl0IG5vdXMgZG9ubmVyIHVuZSBpbmZvcm1hdGlvbiBydWRpbWVudGFpcmUgc3VyIGxhIGxvY2FsaXNhdGlvbiBkZXMgY29udHJpYnV0ZXVycy4KCk1hbGhldXJldXNlbWVudCwgw6AgcGFydGlyIGRlIDIwMDMsIGlsIHNlbWJsZSBxdWUgbGVzIG3DqXRhZG9ubsOpZXMgZCdlbnZvaSBzb250IGRlcyBzaW1wbGVzIGNvcGllcyBkZSBjZWxsZXMgZGUgbGEgcsOpY2VwdGlvbi4uLiBOb3RyZSBhbmFseXNlIGRlcyBob3JhaXJlcyBkZXMgY29udHJpYnV0aW9ucyBzZXJhIHBvdXIgbCdpbnN0YW50IGNhbnRvbm7DqSDDoCBjZXR0ZSBww6lyaW9kZS4gCgojIyBIb3JhaXJlcwoKQ29tbWVuw6dvbnMgcGFyIMOpdHVkaWVyIGxlcyBob3JhaXJlcyBkZXMgcHJvZ3JhbW1ldXJzIGVudHJlIDE5OTYgZXQgMjAwMi4gUHVpc3F1ZSBsZSB2b2x1bWUgZCdlbWFpbHMgw6l2b2x1ZSBjb25zaWTDqXJhYmxlbWVudCwgbm91cyBhdm9ucyBjaG9pc2kgZCdvYnNlcnZlciBsZXMgdmFyaWF0aW9ucyBlbiBwb3VyY2VudGFnZXMgOgoKYGBge3IsIGZpZy53aWR0aD0xNX0KaG91ckJ5WWVhciA8LSBsa21sWwogIHllYXIoc2VudERhdGUpID49IDE5OTYgJiB5ZWFyKHNlbnREYXRlKSA8PSAyMDAyLAogIC5OLAogIGJ5ID0gLihob3VyID0gaG91cihzZW50VGltZSksIHllYXIgPSB5ZWFyKHNlbnREYXRlKSkKICBdWywgcHJvcCA6PSBOL3N1bShOKSwgYnkgPSB5ZWFyXVtvcmRlcih5ZWFyLCBob3VyKV0KCmdncGxvdChob3VyQnlZZWFyLCBhZXMoeCA9IGhvdXIsIHkgPSBwcm9wLCBmaWxsID0gZmFjdG9yKHllYXIpKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKTm91cyBjb25zdGF0b25zIHVuZSBhdWdtZW50YXRpb24gZHUgbm9tYnJlIGQnZW1haWxzIGVudm95w6lzIHBlbmRhbnQgbCdhcHLDqHMtbWlkaSAoMTRoLTE3aCkgYWluc2kgcXUndW5lIGRpbWludXRpb24gbWFycXXDqWUgZHUgbm9tYnJlIGQnZW1haWxzIGVudm95w6lzIGVudHJlIDdoIGV0IDloLiDDgCBwcmVtacOocmUgdnVlLCBjZWxhIHBvdXJyYWl0IHN1Z2fDqXJlciBxdWUgbGVzIHByb2ZpbHMgZGVzIGNvbnRyaWJ1dGV1cnMgc2UgcHJvZmVzc2lvbm5hbGlzZW50IDsgaWwgZmF1ZHJhIGxlIHbDqXJpZmllciBwYXIgbGEgc3VpdGUuCgojIyBKb3VyIGRlIGxhIHNlbWFpbmUKCk5vdXMgZWZmZWN0dW9ucyBsYSBtw6ptZSBhbmFseXNlIHBvdXIgbGVzIGpvdXJzIGRlIGxhIHNlbWFpbmUgKDEgPSBkaW1hbmNoZSwgMiA9IGx1bmRpLCAuLi4sIDcgPSBzYW1lZGkpIDoKCmBgYHtyLCBmaWcud2lkdGg9MTV9CndkYXlCeVllYXIgPC0gbGttbFsKICB5ZWFyKHNlbnREYXRlKSA+PSAxOTk2ICYgeWVhcihzZW50RGF0ZSkgPD0gMjAwMiwKICAuTiwKICBieSA9IC4od2RheSA9IHdkYXkoc2VudERhdGUpLCB5ZWFyID0geWVhcihzZW50RGF0ZSkpCiAgXVssIHByb3AgOj0gTi9zdW0oTiksIGJ5ID0geWVhcl1bb3JkZXIoeWVhciwgd2RheSldCgpnZ3Bsb3Qod2RheUJ5WWVhciwgYWVzKHggPSBmYWN0b3Iod2RheSksIHkgPSBwcm9wLCBmaWxsID0gZmFjdG9yKHllYXIpKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKTm91cyBjb25zdGF0b25zIGljaSBxdWUgbCdhY3Rpdml0w6kgZXN0IHLDqWR1aXRlIHBlbmRhbnQgbGUgd2Vlay1lbmQgKGpvdXIgMSBldCA3KSwgbWFpcyBhdWN1bmUgw6l2b2x1dGlvbiB0ZW1wb3JlbGxlIGNsYWlyZSBuJ2VzdCBwZXJjZXB0aWJsZS4KCiMgUGV1dC1vbiB2b2lyIHF1aSByw6lwb25kIMOgIHF1aSA/CgpMJ29iamVjdGlmIGVzdCwgw6AgdGVybWUsIGRlIGZhaXJlIGRlcyBhbmFseXNlcyBkZSByw6lzZWF1IHN1ciBsYSBMS01MLCBkZSBkw6l0ZWN0ZXIgbGVzIG1vdGlmcyBwYXIgc3VqZXQsIGV0Yy4gUG91ciBjZWxhLCBub3VzIGF1cm9ucyBiZXNvaW4gZGUgc2F2b2lyIMOgIHF1ZWwgZW1haWwgcsOpcG9uZCB1biBtZXNzYWdlLiBNYWxoZXVyZXVzZW1lbnQsIGNldHRlIHF1ZXN0aW9uIG4nZXN0IHBhcyB0cml2aWFsZS4gRGFucyBsZXMgbcOpdGFkb25uw6llcyByw6ljdXDDqXLDqWVzLCBub3VzIGF2b25zIGJpZW4gdW4gY2hhbXAgaW5kaXF1YW50IMOgIHF1ZWwgZW1haWwgdW4gbWVzc2FnZSByw6lwb25kLCBtYWlzIGNlIGRlcm5pZXIgZmFpdCBzb3V2ZW50IGTDqWZhdXQuCgpOb3RyZSBidXQgZXN0IGRvbmMgZCfDqXZhbHVlciBsYSBxdWFsaXTDqSBkdSBjaGFtcCBgcmVwbHl0b2AsIGVuIGxlIGNvbmZyb250YW50IMOgIHVuIGF1dHJlIGluZGljYXRldXIsIGxhIHByw6lzZW5jZSBkdSBtb3QtY2zDqSAiUmU6IiBkYW5zIGxlIHN1amV0IGRlIGwnZW1haWwuCgpgYGB7cn0KbXNnc0J5WWVhciA8LSBsa21sWywKICAgICAgICAgICAgICAgICAgIC4obWVzc2FnZXMgPSAuTiwKICAgICAgICAgICAgICAgICAgICAgcmVfaW5fc3ViamVjdCA9IHN1bShncmVwbCgiUmVcXHM/OiIsIHN1YmplY3QsIGlnbm9yZS5jYXNlID0gVCkpLAogICAgICAgICAgICAgICAgICAgICBuYl9yZXBseXRvID0gc3VtKCFpcy5uYShyZXBseXRvKSkpLAogICAgICAgICAgICAgICAgICAgYnkgPSB5ZWFyKHJlY2VpdmVkRGF0ZSldWyxyYXRpbyA6PSBuYl9yZXBseXRvL3JlX2luX3N1YmplY3RdCm1zZ3NCeVllYXIKYGBgCgpQb3VyIHBsdXMgZGUgdmlzaWJpbGl0w6ksIHRyYcOnb25zIGwnw6l2b2x1dGlvbiBkdSByYXRpbyAkXGZyYWN7XHRleHR7Tm9tYnJlIGRlIHJlcGx5dG99fXtcdGV4dHtOb21icmUgZGUgIlJlIn19JCA6CgpgYGB7cn0KZ2dwbG90KG1zZ3NCeVllYXIsIGFlcyh4ID0geWVhciwgeSA9IHJhdGlvKSkgKyBnZW9tX3BvaW50KCkgKyB0aGVtZV9taW5pbWFsKCkKYGBgCgpOb3VzIGNvbnN0YXRvbnMgbm9uIHBhcyB1biwgbWFpcyBkZXV4IHByb2Jsw6htZXMgOgogIAorIExlIHJhdGlvIGVzdCB0csOocyBiYXMgYXZhbnQgbGVzIGFubsOpZXMgMjAwMCAoJDwwLjQkKSwgY2UgcXVpIGVzdCBjb2jDqXJlbnQgYXZlYyBub3Mgb2JzZXJ2YXRpb25zIHLDqWFsaXPDqWVzIGxvcnMgZHUgdHJhdmFpbCBkJ2FyY2hpdmVzLgorIFBsdXMgc3VycHJlbmFudCwgbGUgcmF0aW8gZMOpcGFzc2UgMSBlbiAyMDA1LCBjZSBxdWkgc2lnbmlmaWVyYWl0IHF1ZSBkZXMgY29udHJpYnV0ZXVycyBzZSByw6lwb25kZW50IGVudHJlIGV1eCBzYW5zIHV0aWxpc2VyIGxlIG1vdC1jbMOpICJSZToiLgoKQXV0cmVtZW50IGRpdCwgaWwgZXN0IHBvc3NpYmxlIHF1ZSBsZSByYXRpbyBzJ2Ftw6lsaW9yZSBub24gcGFzIHBhcmNlIHF1ZSBsYSBwcmVtacOocmUgY2F0w6lnb3JpZSBkJ2VtYWlscyBkaW1pbnVlIG1haXMgcGFyY2UgcXVlIGxhIGRldXhpw6htZSBhdWdtZW50ZS4gVsOpcmlmaW9ucyBjZXR0ZSBoeXBvdGjDqHNlIGF2ZWMgbGUgZ3JhcGhpcXVlIHN1aXZhbnQgOgoKYGBge3IsIGVjaG89VFJVRX0KcmVwbEJ5WWVhciA8LSBsa21sWywKICAgICAgICAgICAgICAgICAgIC4ocmVfbm9fcmVwbHkgPSBzdW0oZ3JlcGwoIlJlXFxzPzoiLCBzdWJqZWN0LCBpZ25vcmUuY2FzZSA9IFQpICYgaXMubmEocmVwbHl0bykpLAogICAgICAgICAgICAgICAgICAgICByZXBseV9ub19yZSA9IHN1bSghaXMubmEocmVwbHl0bykgJiAhZ3JlcGwoIlJlXFxzPzoiLCBzdWJqZWN0LCBpZ25vcmUuY2FzZSA9IFQpKSksCiAgICAgICAgICAgICAgICAgICBieSA9IHllYXIocmVjZWl2ZWREYXRlKV0KZ2dwbG90KHJlcGxCeVllYXIpICsKICBnZW9tX3BvaW50KGFlcyh4ID0geWVhciwgeSA9IHJlX25vX3JlcGx5LCBjb2xvdXIgPSAiUmUgc2FucyByZXBseXRvIikpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHllYXIsIHkgPSByZXBseV9ub19yZSwgY29sb3VyID0gIlJlcGx5dG8gc2FucyBSZSIpKSArIHRoZW1lX21pbmltYWwoKQpgYGAKCkVmZmVjdGl2ZW1lbnQsIG9uIGNvbnN0YXRlIHVuZSBncmFuZGUgYXVnbWVudGF0aW9uIGRlIGxhIGRldXhpw6htZSBjYXTDqWdvcmllIGFwcsOocyAyMDA1LiBDb21tZSBub3RyZSBhbmFseXNlIHZhLCBkYW5zIHVuIHByZW1pZXIgdGVtcHMsIHNlIGNvbmNlbnRyZXIgc3VyIGxhIHDDqXJpb2RlIHF1aSBwcsOpY8OoZGUgMjAwNSwgY2UgwqsgcHJvYmzDqG1lIMK7IG4nZXN0IHBhcyBzaSB1cmdlbnQuCgpFbiByZXZhbmNoZSwgaWwgZXN0IGNsYWlyIHF1ZSBsJ2FyY2hpdmUgYSBjb25udSBkZSBub21icmV1eCBkw6lib2lyZXMgZW4gMTk5OCBldCAxOTk5IDogbGVzIGVtYWlscyDDqXRhbnQgbWFuaWZlc3RlbWVudCBkZXMgcsOpcG9uc2VzIChjb250ZW5hbnQgIlJlOiIgZGFucyBsZSB0aXRyZSkgbWFpcyBzYW5zIGNoYW1wIGByZXBseXRvYCBzb250IHRyw6hzIG5vbWJyZXV4LiBDZXMgYW5uw6llcyDDqXRhbnQgY3J1Y2lhbGVzIHBvdXIgY29tcHJlbmRyZSBsYSBjb25jZXB0aW9uIGRlIEdpdCwgaWwgbm91cyBmYXVkcmEgdHJvdXZlciB1biBtb3llbiBkZSBwYWxpZXIgY2UgZMOpZmF1dCBkZSBsYSBiYXNlIGRlIGRvbm7DqWVzLgoKIyMgTGUgY2FzIGRlcyBlbWFpbHMgIlJlOiIgc2FucyBgcmVwbHl0b2AKCkwndW5lIGRlcyBzb2x1dGlvbnMgZW52aXNhZ8OpZXMgcG91ciByZWNvbnN0aXR1ZXIgKmEgcG9zdGVyaW9yaSogbGUgY2hhbXAgYHJlcGx5dG9gIGVzdCBkZSBjcsOpZXIgdW4gc2NyaXB0IHF1aSBleHBsb3JlIGxlIGNvbnRlbnUgZGVzIGVtYWlscy4gU3VyIGxhIExLTUwsIGlsIGVzdCBkZSBjb3V0dW1lIGRlIGNpdGVyIGxlIHBhc3NhZ2UgZGUgbCdlbWFpbCBhdXF1ZWwgb24gcsOpcG9uZCBkYW5zIGxlIGNvcnBzIGR1IG1lc3NhZ2UgKFt2b2lyIHVuIGV4ZW1wbGUgaWNpXShodHRwOi8vbGttbC5pdS5lZHUvaHlwZXJtYWlsL2xpbnV4L2FscGhhLzk1MTEvMDE3NC5odG1sKSkuCgpUb3V0ZWZvaXMsIGNldHRlIG3DqXRob2RlIHBldXQgcydhdsOpcmVyIHRyw6hzIGZhc3RpZGlldXNlICh0ZW1wcyBkZSBjYWxjdWwpLCBzdXJ0b3V0IGFwcGxpcXXDqWUgw6AgYHIgcmVwbEJ5WWVhclssc3VtKHJlX25vX3JlcGx5KV1gIGVtYWlscy4gVG91dGUgaW5mb3JtYXRpb24gc3VwcGzDqW1lbnRhaXJlIG5vdXMgYWlkZXJhIMOgIG9wdGltaXNlciBsJ2FsZ29yaXRobWUuIEMnZXN0IHBvdXJxdW9pIG5vdXMgZXNzYXlvbnMgZGUgdHJvdXZlciBxdWVscXVlcyBtw6ljYW5pc21lcyAqYSBwcmlvcmkqIHBvdXIgZXhwbGlxdWVyIGNlIHBow6lub23DqG5lIGRlIG5vbi1yw6lwb25zZXMuCgpMJ2FyY2hpdmUgZXN0IHN0cnVjdHVyw6llIGVuIHBldGl0cyBkb3NzaWVycyBxdWkgcmVncm91cGVudCBsZXMgZW1haWxzIHJlw6d1cyBzZWxvbiBsYSBkYXRlLiBMZXMgZ3JvdXBlcyBzb250IGTDqXRlcm1pbsOpcyBzZWxvbiBsZSBzY2jDqW1hIHN1aXZhbnQgOgoKKyAxLTcgamFudmllciAxOTk1CisgOC0xNSBqYW52aWVyIDE5OTUKKyAxNi0yMyBqYW52aWVyIDE5OTUKKyAyNC0zMSBqYW52aWVyIDE5OTUKKyAxLTcgZsOpdnJpZXIgMTk5NQorIC4uLgoKRW4gZXhwbG9yYW50IGwnYXJjaGl2ZSwgdW5lIGRlcyBoeXBvdGjDqHNlcyDDqW1pc2UgZXN0IHF1ZSBsJ2FyY2hpdmUgcmVww6hyZSBtaWV1eCBsZXMgcsOpcG9uc2VzIHF1YW5kIHVuZSBjb252ZXJzYXRpb24gc2UgZMOpcm91bGUgZGFucyBsJ3VuIGRlIHNlcyBpbnRlcnZhbGxlcywgbWFpcyBwYXMgcXVhbmQgZWxsZSBjb21tZW5jZSBkYW5zIGwndW4gZXQgc2UgdGVybWluZSBkYW5zIGwnYXV0cmUuIFVuIMOpY2hhbmdlIHNlIHRlbmFudCBlbnRyZSBsZSAzIGV0IGxlIDYgZHUgbW9pcyBhdXJhIGRvbmMgYmVhdWNvdXAgcGx1cyBkZSBjaGFuY2VzIGRlIGNvbnNlcnZlciBzb24gYHJlcGx5dG9gIGludGFjdCBxdSd1bmUgY29udmVyc2F0aW9uIGF5YW50IGxpZXUgc3VyIHBsdXNpZXVycyBzZW1haW5lcy4KClNpIGNldHRlIGh5cG90aMOoc2UgZXN0IHZyYWllLCBldCBzaSBub3VzIHN1cHBvc29ucyBxdWUgbGEgcGx1cGFydCBkZXMgY29udHJpYnV0ZXVycyByw6lwb25kZW50IHJhcGlkZW1lbnQgKDEtMiBqb3VycykgYXV4IGVtYWlscywgb24gZGV2cmFpdCBhbG9ycyBvYnNlcnZlciB1biBwaWMgZCdlbWFpbHMgc2FucyBgcmVwbHl0b2AgYXV0b3VyIGR1IDEsIDgsIDE2IGV0IDI0IGR1IG1vaXMuIEMnZXN0IGNlIHF1ZSBub3VzIHbDqXJpZmlvbnMgYXZlYyBsZSBncmFwaGlxdWUgc3VpdmFudCA6CgpgYGB7cn0KcmVOb1JlcGx5IDwtIGxrbWxbZ3JlcGwoIlJlXFxzPzoiLCBzdWJqZWN0LCBpZ25vcmUuY2FzZSA9IFQpICYgaXMubmEocmVwbHl0byksCiAgICAgICAgICAgICAgICAgIC5OLAogICAgICAgICAgICAgICAgICBieSA9IG1kYXkocmVjZWl2ZWREYXRlKV0KZ2dwbG90KHJlTm9SZXBseSwgYWVzKG1kYXksIE4pKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHRoZW1lX21pbmltYWwoKQpgYGAKCkNldHRlIGh5cG90aMOoc2Ugc2VtYmxlIGNvbmZpcm3DqWUuIElsIGZhdWRyYWl0IG1haW50ZW5hbnQgYWxsZXIgcGx1cyBsb2luIGV0IHbDqXJpZmllciBzaSBjZSBwaMOpbm9tw6huZSBlc3QgdW5pcXVlbWVudCB2YWxhYmxlIHBvdXIgbGVzIG1lc3NhZ2VzIHF1aSByw6lwb25kZW50IGFwcsOocyBjZXMgam91cnMgbGltaXRlcyDDoCB1biBlbWFpbCByZcOndSBhdmFudCAqKlvDgCBGQUlSRV0qKi4gU2kgYydlc3QgYmllbiBsZSBjYXMsIGFsb3JzIGwnYWxnb3JpdGhtZSBwb3VycmEgZm9uY3Rpb25uZXIgY29tbWUgc3VpdCA6CgpgYGAKUG91ciBjaGFxdWUgZW1haWwgIlJlIiBzYW5zIHJlcGx5dG8gOgogIEV4dHJhaXJlIGxhIGNpdGF0aW9uIGRlIGwnZW1haWwKICBQb3VyIGNoYXF1ZSBlbWFpbCBkYW5zIGxhIG3Dqm1lIGNvbnZlcnNhdGlvbiBlbnZvecOpIGF2YW50IGxlIDEsIDgsIDE2IG91IDI0IDoKICAgIFbDqXJpZmllciBzaSBsYSBjaXRhdGlvbiBjb3JyZXNwb25kIMOgIGwnZW1haWwKICAgIFNpIG91aSA6CiAgICAgIFJlbXBsaXIgbGUgY2hhbXAgYHJlcGx5dG9gCmBgYAoKIyMgTGUgY2FzIGRlcyBlbWFpbHMgYHJlcGx5dG9gIHNhbnMgIlJlOiIKCkRhbnMgbGUgY2FzIGRlcyBlbWFpbHMgYXlhbnQgdW4gY2hhbXAgYHJlcGx5dG9gIG5vbiB2aWRlIG1haXMgcGFzIGxlIG1vdC1jbMOpICJSZToiIGRhbnMgbGUgc3VqZXQsIG5vdXMgbidhdm9ucyBwYXMgcmVww6lyw6kgZGUgbW90aWZzIHNpbWlsYWlyZXMsIGNlIHF1aSBub3VzIGxhaXNzZSBwZW5zZXIgcXUnaWxzIG5lIHNvbnQgcGFzIHVuIGFydGVmYWN0IGRlIGwnYXJjaGl2ZSBlbGxlLW3Dqm1lLgoKUG91ciBub3VzIGZhaXJlIHVuZSBtZWlsbGV1cmUgaWTDqWUgZGUgY2VzIGNhcyBkZSBmaWd1cmUsIG5vdXMgcmVnYXJkb25zIGxlcyA1MCBwcmVtaWVycyBtYWlscyBkZSBjZSB0eXBlIHBvdXIgY2hhcXVlIGFubsOpZSA6CgpgYGB7cn0KcmVwbHlOb1JlIDwtIGxrbWxbIWdyZXBsKCJSZVxccz86Iiwgc3ViamVjdCwgaWdub3JlLmNhc2UgPSBUKSAmICFpcy5uYShyZXBseXRvKSwKICAgICAgICAgICAgICAgICAgLihzdWJqZWN0LCB5ZWFyID0geWVhcihyZWNlaXZlZERhdGUpLCBzZW5kZXJOYW1lLCB1cmwpXQpzZXRrZXkocmVwbHlOb1JlLCB5ZWFyKQpyZXBseU5vUmVbLigxOTk1OjIwMTUpLCAuU0RbMTptaW4oNTAsLk4pXSwgYnkgPSAuRUFDSEldCmBgYAoKTm90cmUgZXhwbG9yYXRpb24gZGUgY2VzIGVtYWlscyBub3VzIGFtw6huZSDDoCBkaXN0aW5ndWVyIGRldXggc291cy1jYXTDqWdvcmllcy4KCiMjIyBMaWVucyBkZSBmaWxpYXRpb24gZW50cmUgZGlmZsOpcmVudGVzIGNvbnZlcnNhdGlvbnMKCkNlcnRhaW5zIGVtYWlscyBzZW1ibGVudCDDqnRyZSBsZXMgcHJlbWllcnMgZCd1bmUgY29udmVyc2F0aW9uLCBtYWlzIHLDqXBvbmRlbnQgZW4gcsOpYWxpdMOpIMOgIHVuIGF1dHJlIGVtYWlsLiBJbCBlc3QgcHJvYmFibGUgcXVlIGNlbGEgc29pdCBkw7sgYXUgY29tcG9ydGVtZW50IGRlcyBjbGllbnRzIGVtYWlscyBkZXMgY29udHJpYnV0ZXVycywgcXVpIHLDqXBvbmRlbnQgw6AgdW4gbWVzc2FnZSBhdmFudCBkZSBjaGFuZ2VyIGxlIHN1amV0LCBhdXF1ZWwgY2FzIGxlIG1haWwgY29uc2VydmUgbGEgbcOpdGFkb25uw6llIGRlIHLDqXBvbnNlIG3Dqm1lIHNpIGxlIHN1amV0IG5lIGNvbnRpZW50IHBhcyBsZSBtb3QtY2zDqSDCqyBSZSA6IMK7LgoKSWwgZmF1ZHJhIGTDqWNpZGVyIGRlIGxhIGZhw6dvbiBkb250IG5vdXMgdHJhaXRlcm9ucyBjZXMgZW1haWxzIDogb3V2cmVudC1pbHMgZGUgbm91dmVsbGVzIGNvbnZlcnNhdGlvbnMgb3UgYmllbiBwcm9sb25nZW50LWlscyB1bmUgbcOqbWUgZGlzY3Vzc2lvbiBhdmVjIHVuIHRpdHJlIGRpZmbDqXJlbnQgPyBVbmUgZMOpY2lzaW9uIHJhaXNvbm5hYmxlIHBvdXJyYWl0IMOqdHJlIGRlIGxlcyBjb25zaWTDqXJlciBjb21tZSBkZSBub3V2ZWxsZXMgY29udmVyc2F0aW9ucywgZXQgcXVlIGwnaW5mb3JtYXRpb24gYHJlcGx5dG9gIGpvdWUgaWNpIGxlIG3Dqm1lIHLDtGxlIHF1ZSBsZSBtb3QtY2zDqSAiV2FzOiIgcG91ciBpbmRpcXVlciBkZXMgbGllbnMgZGUgZmlsaWF0aW9uIGVudHJlIGRpZmbDqXJlbnRlcyBkaXNjdXNzaW9ucy4KCiMjIyBFbWFpbHMgcHJvZHVpdHMgcGFyIGBiaXRrZWVwZXJgIGV0IHBhciBgZ2l0YAoKRCdhdXRyZSBwYXJ0LCDDoCBwYXJ0aXIgZGUgMjAwMywgbm91cyByZW1hcnF1b25zIHF1ZSBsYSBwbHVwYXJ0IGRlcyBlbWFpbHMgY29udGllbm5lbnQgZGVzIHN1amV0cyBkZSBjZSB0eXBlIDogCgpgYGAKW1BBVENIIDEvMyBSRkNdIENvY2NpbmVsbGU6IGRyb3AgdW5lY2Vzc2FyeSBkdXBsaWNhdGVkIGluaXRfY29tcGx0aW9uIGNhbGxzCltQQVRDSCAyLzMgUkZDXSBDb2NjaW5lbGxlOiBjaGVjayBmb3IgaW5jb3JyZWN0IERFQ0xBUkVfQ09NUExFVElPTiB1c2UKW1BBVENIIDMvMyBSRkNdIENvY2NpbmVsbGU6IGluY29ycmVjdCB1c2Ugb2YgbXVsdGlwbGUgaW5pdF9jb21wbGV0aW9uCQpgYGAKCklscyBzb250IGRlIHBsdXMgcsOpZGlnw6lzIHBhciBsYSBtw6ptZSBwZXJzb25uZS4KCklsIHMnYWdpdCB2aXNpYmxlbWVudCBkJ3VuZSBmb25jdGlvbm5hbGl0w6kgZGVzIGdlc3Rpb25uYWlyZXMgZGUgdmVyc2lvbiBhcHBhcnVlIGVuIDIwMDMgZGFucyBgYml0a2VlcGVyYCBldCBpbXBsw6ltZW50w6llIHBhciBsYSBzdWl0ZSBkYW5zIGBnaXRgLCBjZSBxdWkgZXhwbGlxdWUgcXVlIGNlcyBlbWFpbHMgc2UgbXVsdGlwbGllbnQgYXByw6hzIDIwMDUsIGRhdGUgZCdhZG9wdGlvbiBvZmZpY2llbGxlIGRlIGBnaXRgLiBQb3VyIHLDqXN1bWVyLCBsZXMgY29udHJpYnV0ZXVycyBkdSBub3lhdSBwZXV2ZW50IHNvdW1ldHRyZSB1biBjb3JyZWN0aWYgw6AgbGEgTEtNTCBkaXJlY3RlbWVudCBkZXB1aXMgbGEgbGlnbmUgZGUgY29tbWFuZGUuIFMnaWxzIG1vZGlmaWVudCBuIGZpY2hpZXJzLCBhbG9ycyBbUEFUQ0ggMC9uXSBjb250aWVudCBsZSBtZXNzYWdlIGV4cGxpY2F0aWYgZXQgbGVzIFtQQVRDSCAxL25dLi4uW1BBVENIIG4vbl0gY29udGllbm5lbnQgbGVzIGBkaWZmYCBwb3VyIGNoYXF1ZSBmaWNoaWVyIG1vZGlmacOpLgoKQ2VzIGVtYWlscyBuZSBzb250IGRvbmMgbmkgdW4gYnVnIGRlIGwnYXJjaGl2ZSwgbmkgZGVzIMKrIHZyYWlzIMK7IGVtYWlscyAoaWxzIG4nb250IHBhcyDDqXTDqSByw6lkaWfDqSBwYXIgbGV1ciBleHDDqWRpdGV1cikuCgojIEJpbGFuCgpJbCByZXN0ZSBlbmNvcmUgdW4gdHJhdmFpbCByZWxhdGl2ZW1lbnQgaW1wb3J0YW50IMOgIGVmZmVjdHVlciBwb3VyIG5ldHRveWVyIGV0IHN0YW5kYXJkaXNlciBsYSBiYXNlIGRlIGRvbm7DqWVzLCBlbiBwYXJ0aWN1bGllciBwb3VyIHLDqWN1cMOpcmVyIGxlcyBgcmVwbHl0b2AsIG1haXMgY2VsYSBub3VzIHBhcmHDrnQgcsOpYWxpc2FibGUuIFBhciBhaWxsZXVycywgaWwgbm91cyBmYXVkcmEgdHJhdmFpbGxlciBzdXIgbGEgYmFzZSBwb3VyIMOqdHJlIGVuIG1lc3VyZSBkZSBwcm9kdWlyZSBmYWNpbGVtZW50IGRlcyBhbmFseXNlcyDDoCAzIG5pdmVhdXggOiBsZXMgY29udHJpYnV0ZXVycywgbGVzIGNvbnZlcnNhdGlvbnMgKHVuIG1lc3NhZ2UgZXQgc2VzIHLDqXBvbnNlcykgZXQgbGVzIG1lc3NhZ2VzIGluZGl2aWR1ZWxzLiDDgCBwYXJ0aXIgZGUgbMOgLCBub3VzIHBvdXJyb25zIHByb2PDqWRlciDDoCBkZXMgYW5hbHlzZXMgcGx1cyBjb21wbGV4ZXMgKGFuYWx5c2UgZGUgcsOpc2VhdSwgY2x1c3RlcmluZyBldCBjbGFzc2lmaWNhdGlvbikuCgpFbmZpbiwgZGFucyB1biBzZWNvbmQgdGVtcHMsIGlsIHBvdXJyYSDDqnRyZSBpbnTDqXJlc3NhbnQgZCdpbmNvcnBvcmVyIGxlIGNvbnRlbnUgZGVzIGVtYWlscyDDoCBsYSBiYXNlIHBvdXIgbCdlbnJpY2hpciAoYHRleHRtaW5pbmdgKSBhdmVjIGRlIG5vdXZlbGxlcyBjYXJhY3TDqHJpc3RpcXVlcyAobG9uZ3VldXIgZGVzIGVtYWlscywgbW90cy1jbMOpcyBlbXBsb3nDqXMsIGV0Yy4pLg==