Introduction

Ce document montre comment interroger et manipuler la base Lexique en R

Il fait parti du cours Programmation pour les Sciences Cognitives. Merci de signaler d’éventuelles erreurs à christophe@pallier.org

Préparation

  1. S’ils ne sont pas déjà installés sur votre ordinateur, vous devrez installer les logiciels R et R-Studio Desktop.

  2. Télécharger http://www.lexique.org/databases/Lexique382/Lexique382.zip

  3. Créer un répertoire Lexique puis dézipper dans ce répertoire le fichier Lexique382.zip précédemment téléchargé.

  4. Démarrer le programme Rstudio

    • Sélectionner le menu File/New Project;
    • Choisir “Existing directory” et naviguer jusqu’au répertoire Lexique que vous venez de créer; cliquer sur le bouton create project.
    • Dans la fenêtre en bas à droite, parmi la liste des fichiers listés sous l’onglet Files, il doit apparaitre Lexique382.tsv. Si tel n’est pas le cas, vérifiez que vous avez bien suivi les instructions.
  5. Dans la fenêtre en bas à gauche de Rstudio, dans l’onglet Console, copier la ligne suivante puis appuyez sur ‘Entrée’

    install.packages('tidyverse')

Laissez rstudio se débrouiller (mais vous devrez peut-être sélectionner un serveur). Quand il a fini, vous êtes prêt ! Vous n’aurez plus jamais à refaire ces étapes.

Chargement de la table “Lexique.org”

Tout se déroule dans Rstudio. Assurez vous de bien travailler dans le projet “lexique” créé dans la section “préparation”. Après un redémarrage de rstudio, utilisez le menu File/Recent Projects pour retrouver ce projet.

    require(tidyverse)  # you must have ran "install.packages('tidyverse')" earlier
    lexique = read_delim("Lexique382.tsv", delim="\t")
    head(lexique, 25)

Puis, en faisant bien attention que le curseur soit à l’intérieur du bloc de code, cliquer sur Run / Run current chunk (ou bien appuyer sur la petite fleche verte, ou sur Ctrl-Shift-Enter). Vous devriez voir les premières lignes de la table lexique s’afficher. Celle doit également apparaitre dans l’onglet “Environment” en haut à droite.

Conseil: Lorsque vous quitterez rstudio, acceptez la proposition “sauvegarder le worskpace”. Ainsi, la prochaine fois que vous ouvrirez le projet ‘lexique’ dans rstudio, la table sera déjà présente dans l’environnement, et il ne sera donc pas nécessaire d’exécuter les lignes ci-dessus (read_delim…).

Sélection de mots entiers

Supposons que vous vouliez extraire les lignes de la table lexique correspondant, par exemple, aux mots ‘bateau’, ‘avion’, ‘maison’, ‘arbre’. Le code suivant fait précisemment cela:

items <- c('bateau', 'avion', 'maison', 'arbre')
selection <- subset(lexique, ortho %in% items)
head(selection)

Vous pouvez inspecter le contenu de la table selection en cliquant sur son nom dans l’onglet Environment situé dans la fenetre en haut à droite de rstudio.

Vous pouvez également sauvegarder les résultats obtenus dans un fichier, avec la commande suivante:

write_tsv(selection, 'selection.tsv')

Notez que les fichiers ayant l’extension csv (tab-separated-values) peuvent être ouverts avec Excel, ou OpenOffice Cal, ou même avec n’importe quel éditeur de texte. Note: le package readr de R fournit aussi des fonctions write_excel_csv et write_excel_csv2 qui peuvent intéresser certains.

Si vous avez une liste de mots plus longues, il serait fastidieux d’écrire la ligne items <- .... Plus simplement vous pouvez utiliser:

items = scan(what='characters')
aller
Read 1 item

Et copier la liste de mots. Entrer une ligne vide termine le processus.

La fonction scan permet aussi de lire la liste dans un fichier externe.

Sélection par critères

Supposons que vous vouliez sélectionner tous les noms de 5 lettres, singuliers, de fréquence lexicale (films) comprise entre 10 et 100. Voici la ligne magique:

selection = subset(lexique, cgram=='NOM' & nombre != 'p' & nblettres==5 & freqlivres > 10 & freqlivres < 100)
head(selection)

Sélection par “pattern”

Les expressions régulières

Les expressions réulières, ou regex, sont des “patterns” qui permettent de rechercher des mots ayant certaines propriétés. Par exemple n’importe a.b désigne un mot contenant un a et un b séparés par une lettre quelonque. Voici d’autre exemples:

  • ^maison$ : recherche le mot “maison” exactement
  • ^anti : recherche tous les mots commençant par “anti”
  • ^jour$|^nuit$|^matin$|^soir$ : “jour” ou “nuit” ou “matin” ou “soir” (permet de rechercher une liste de mots)
  • ion : recherche les mots qui contiennent la chaine “ion” dans n’importe quelle position
  • ion$ : mots se terminant par “ion”
  • ^pr : mots commençant par “pr”
  • ^p..r$ : mots de quatre lettres commençant par “p”, finisant pas “r”
  • ^p.*r$ : mots commencant par ‘”p’ et finissant par “r”
  • [aeiou][aeiou] : mots contenant 2 voyelles successives
  • ^[aeiou] : mots commençant par une voyelle
  • ^[^aeriou] : mots ne commençant pas par une voyelle

Il existe de nombreux tutoriaux sur les regex sur le web, notamment celui-ci. La bible sur le sujet est le livre Mastering Regular Expressions de Jeff Friedl.

Une expression régulière décrit un automate de transitions à états finis. Le site https://regexper.com/ vous permet de visualiser l’automate associé à une regex. Par exemple [ptk].*[aiou][aiou].?ion$ correspond à l’automate fini:

Recherches dans R avec grepl

R permet d’effectuer des recherches par pattern grâce à la fonction grepl. La syntaxe est grepl(regex, variable) pour rechercher les lignes où la variable “matche” la regex (Voir la doc R de grepl.

Cette fonction permet de localiser les lignes qui ‘matchent’ une expression, ou bien, en la niant avec le signe !, de supprimer des lignes qui matchent un pattern.

Voici quelques exemples:

  • Pour obtenir tous les mots qui finissent par tion :
lexique %>% filter(grepl("tion$", ortho)) -> selection2
head(selection2)

Encore une fois, vous pouvez sauvegarder ces résultats avec:

write_tsv(selection2, 'mots-en-tion.tsv')
  • Pour lister tous les mots contenant un cluster de consonnes plosives, mais pas debut de mot:
lexique %>% filter(grepl('.[ptkbdg][ptkbdg]', phon)) -> selection3
head(selection3)
  • L’opérateur filterpeut être appeler plusieurs fois pour affiner progressivement la recherche.

    Par exemple, pour obtenir tous les mots de 8 lettres qui ne finissent pas ent:

lexique %>% filter(nblettres == 8) %>% filter(!grepl("ent$", ortho)) -> selection4
head(selection4)
LS0tCnRpdGxlOiAiSW50ZXJyb2dlciBMZXhpcXVlIGF2ZWMgUiIKYXV0aG9yOiAiQ2hyaXN0b3BoZSBQYWxsaWVyIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCkNlIGRvY3VtZW50IG1vbnRyZSBjb21tZW50IGludGVycm9nZXIgZXQgbWFuaXB1bGVyIGxhIGJhc2UgW0xleGlxdWVdKGh0dHA6Ly93d3cubGV4aXF1ZS5vcmcpIGVuIFtSXShodHRwczovL2ZyLndpa2lwZWRpYS5vcmcvd2lraS9SXyhsYW5nYWdlKSkgCgpJbCBmYWl0IHBhcnRpIGR1IGNvdXJzIFtQcm9ncmFtbWF0aW9uIHBvdXIgbGVzIFNjaWVuY2VzIENvZ25pdGl2ZXNdKGh0dHBzOi8vY2hycGxyLmdpdGh1Yi5pby9QQ0JTLykuIE1lcmNpIGRlIHNpZ25hbGVyIGQnw6l2ZW50dWVsbGVzIGVycmV1cnMgw6AgPGNocmlzdG9waGVAcGFsbGllci5vcmc+CgoKIyMgUHLDqXBhcmF0aW9uCgoxLiBTJ2lscyBuZSBzb250IHBhcyBkw6lqw6AgaW5zdGFsbMOpcyBzdXIgdm90cmUgb3JkaW5hdGV1ciwgdm91cyBkZXZyZXogaW5zdGFsbGVyIGxlcyBsb2dpY2llbHMgW1JdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLykgZXQgIFtSLVN0dWRpbyBEZXNrdG9wXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvLyNEZXNrdG9wKS4KCjIuIFTDqWzDqWNoYXJnZXIgPGh0dHA6Ly93d3cubGV4aXF1ZS5vcmcvZGF0YWJhc2VzL0xleGlxdWUzODIvTGV4aXF1ZTM4Mi56aXA+CgozLiBDcsOpZXIgdW4gcsOpcGVydG9pcmUgYExleGlxdWVgIHB1aXMgZMOpemlwcGVyIGRhbnMgY2UgcsOpcGVydG9pcmUgbGUgZmljaGllciBgTGV4aXF1ZTM4Mi56aXBgIHByw6ljw6lkZW1tZW50IHTDqWzDqWNoYXJnw6kuCgo0LiBEw6ltYXJyZXIgbGUgcHJvZ3JhbW1lICoqUnN0dWRpbyoqIAoKICAgLSBTw6lsZWN0aW9ubmVyIGxlIG1lbnUgYEZpbGUvTmV3IFByb2plY3RgOyAKICAgLSBDaG9pc2lyICJFeGlzdGluZyBkaXJlY3RvcnkiIGV0IG5hdmlndWVyIGp1c3F1J2F1IHLDqXBlcnRvaXJlIGBMZXhpcXVlYCBxdWUgdm91cyB2ZW5leiBkZSBjcsOpZXI7IGNsaXF1ZXIgc3VyIGxlIGJvdXRvbiBgY3JlYXRlIHByb2plY3RgLiAKICAgLSBEYW5zIGxhIGZlbsOqdHJlIGVuIGJhcyDDoCBkcm9pdGUsIHBhcm1pIGxhIGxpc3RlIGRlcyBmaWNoaWVycyBsaXN0w6lzIHNvdXMgbCdvbmdsZXQgKkZpbGVzKiwgaWwgZG9pdCBhcHBhcmFpdHJlIGBMZXhpcXVlMzgyLnRzdmAuIFNpIHRlbCBuJ2VzdCBwYXMgbGUgY2FzLCB2w6lyaWZpZXogcXVlIHZvdXMgYXZleiBiaWVuIHN1aXZpIGxlcyBpbnN0cnVjdGlvbnMuCgo1LiBEYW5zIGxhIGZlbsOqdHJlIGVuIGJhcyDDoCBnYXVjaGUgZGUgUnN0dWRpbywgZGFucyBsJ29uZ2xldCAqQ29uc29sZSosIGNvcGllciBsYSBsaWduZSBzdWl2YW50ZSBwdWlzIGFwcHV5ZXogc3VyICdFbnRyw6llJwoKYGBgCiAgICBpbnN0YWxsLnBhY2thZ2VzKCd0aWR5dmVyc2UnKQpgYGAKCkxhaXNzZXogcnN0dWRpbyBzZSBkw6licm91aWxsZXIgKG1haXMgdm91cyBkZXZyZXogcGV1dC3DqnRyZSBzw6lsZWN0aW9ubmVyIHVuIHNlcnZldXIpLiBRdWFuZCBpbCBhIGZpbmksIHZvdXMgw6p0ZXMgcHLDqnQgISBWb3VzIG4nYXVyZXogcGx1cyBqYW1haXMgw6AgcmVmYWlyZSBjZXMgw6l0YXBlcy4KCiMjIENoYXJnZW1lbnQgZGUgbGEgdGFibGUgIkxleGlxdWUub3JnIgoKVG91dCBzZSBkw6lyb3VsZSBkYW5zIFJzdHVkaW8uIEFzc3VyZXogdm91cyBkZSBiaWVuIHRyYXZhaWxsZXIgZGFucyBsZSBwcm9qZXQgImxleGlxdWUiIGNyw6nDqSBkYW5zIGxhIHNlY3Rpb24gInByw6lwYXJhdGlvbiIuIEFwcsOocyB1biByZWTDqW1hcnJhZ2UgZGUgcnN0dWRpbywgdXRpbGlzZXogbGUgbWVudSBgRmlsZS9SZWNlbnQgUHJvamVjdHNgIHBvdXIgcmV0cm91dmVyIGNlIHByb2pldC4KCi0gQ2xpcXVleiBsZSBtZW51ICJGaWxlIC8gTmV3IEZpbGUgLyBSIE5vdGVib29rIi4gVW4gZG9jdW1lbnQgIlVudGl0bGVkIiBhcHBhcmFpdCBkYW5zIGxlIGZlbmV0cmUgZW4gaGF1dCDDoCBnYXVjaGUuIAotIEMnZXN0IGRhbnMgY2UgZG9jdW1lbnQgcXVlIG5vdXMgYWxsb25zIGVudHJlciBkdSBjb2RlIFI6CgogIETDqXBsYWNleiB2b3VzIMOgIGxhIGxpZ25lIDUgKGp1c3RlIGFwcsOocyBsYSBsaWduZSBjb250ZW5hbnQgJy0tLScpLCBldCBjbGlxdWV6IHN1ciBsZSBib3V0b24gKkluc2VydCogcHVpcyBjaG9pc2lzZXogKlIqICh2b3VzIHBvdXZleiBhbGxlciBwbHVzIHZpdGUgZW4gYXBwdXlhbnQgc3VyICpDdHJsK0FsdCtJKikuIAogIAogIENvcGlleiBsZXMgdHJvaXMgbGlnbmVzIHN1aXZhbnRlcyBkYW5zIGxlIGJsb2MgZGUgY29kZSBxdWkgdmllbnQgZCfDqnRyZSBjcsOpw6kuIAoKYGBge3J9CiAgICByZXF1aXJlKHRpZHl2ZXJzZSkgICMgeW91IG11c3QgaGF2ZSByYW4gImluc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpIiBlYXJsaWVyCiAgICBsZXhpcXVlID0gcmVhZF9kZWxpbSgiTGV4aXF1ZTM4Mi50c3YiLCBkZWxpbT0iXHQiKQogICAgaGVhZChsZXhpcXVlLCAyNSkKYGBgCgogICBQdWlzLCBlbiBmYWlzYW50IGJpZW4gYXR0ZW50aW9uIHF1ZSBsZSBjdXJzZXVyIHNvaXQgw6AgbCdpbnTDqXJpZXVyIGR1IGJsb2MgZGUgY29kZSwgY2xpcXVlciBzdXIgKlJ1biAvIFJ1biBjdXJyZW50IGNodW5rKiAob3UgYmllbiBhcHB1eWVyIHN1ciBsYSBwZXRpdGUgKmZsZWNoZSB2ZXJ0ZSosIG91IHN1ciAqQ3RybC1TaGlmdC1FbnRlciopLiBWb3VzIGRldnJpZXogdm9pciBsZXMgcHJlbWnDqHJlcyBsaWduZXMgZGUgbGEgdGFibGUgbGV4aXF1ZSBzJ2FmZmljaGVyLiBDZWxsZSBkb2l0IMOpZ2FsZW1lbnQgYXBwYXJhaXRyZSBkYW5zIGwnb25nbGV0ICJFbnZpcm9ubWVudCIgZW4gaGF1dCDDoCBkcm9pdGUuIAoKCkNvbnNlaWw6IExvcnNxdWUgdm91cyBxdWl0dGVyZXogcnN0dWRpbywgYWNjZXB0ZXogbGEgcHJvcG9zaXRpb24gInNhdXZlZ2FyZGVyIGxlIHdvcnNrcGFjZSIuIEFpbnNpLCBsYSBwcm9jaGFpbmUgZm9pcyBxdWUgdm91cyBvdXZyaXJleiBsZSBwcm9qZXQgJ2xleGlxdWUnIGRhbnMgcnN0dWRpbywgbGEgdGFibGUgc2VyYSBkw6lqw6AgcHLDqXNlbnRlIGRhbnMgbCdlbnZpcm9ubmVtZW50LCBldCBpbCBuZSBzZXJhIGRvbmMgcGFzIG7DqWNlc3NhaXJlIGQnZXjDqWN1dGVyIGxlcyBsaWduZXMgY2ktZGVzc3VzIChgcmVhZF9kZWxpbWAuLi4pLgoKCiMjIFPDqWxlY3Rpb24gZGUgbW90cyBlbnRpZXJzCgpTdXBwb3NvbnMgcXVlIHZvdXMgdm91bGlleiBleHRyYWlyZSBsZXMgbGlnbmVzIGRlIGxhIHRhYmxlIGxleGlxdWUgY29ycmVzcG9uZGFudCwgcGFyIGV4ZW1wbGUsIGF1eCBtb3RzICdiYXRlYXUnLCAnYXZpb24nLCAnbWFpc29uJywgJ2FyYnJlJy4gTGUgY29kZSBzdWl2YW50IGZhaXQgcHLDqWNpc2VtbWVudCBjZWxhOgoKCmBgYHtyfQppdGVtcyA8LSBjKCdiYXRlYXUnLCAnYXZpb24nLCAnbWFpc29uJywgJ2FyYnJlJykKCnNlbGVjdGlvbiA8LSBzdWJzZXQobGV4aXF1ZSwgb3J0aG8gJWluJSBpdGVtcykKCmhlYWQoc2VsZWN0aW9uKQpgYGAKClZvdXMgcG91dmV6IGluc3BlY3RlciBsZSBjb250ZW51IGRlIGxhIHRhYmxlIGBzZWxlY3Rpb25gIGVuIGNsaXF1YW50IHN1ciBzb24gbm9tIGRhbnMgbCdvbmdsZXQgKkVudmlyb25tZW50KiBzaXR1w6kgZGFucyBsYSBmZW5ldHJlIGVuIGhhdXQgw6AgZHJvaXRlIGRlIHJzdHVkaW8uCgoKVm91cyBwb3V2ZXogw6lnYWxlbWVudCBzYXV2ZWdhcmRlciBsZXMgcsOpc3VsdGF0cyBvYnRlbnVzIGRhbnMgdW4gZmljaGllciwgIGF2ZWMgbGEgY29tbWFuZGUgc3VpdmFudGU6CgpgYGB7cn0Kd3JpdGVfdHN2KHNlbGVjdGlvbiwgJ3NlbGVjdGlvbi50c3YnKQpgYGAKCk5vdGV6IHF1ZSBsZXMgZmljaGllcnMgYXlhbnQgbCdleHRlbnNpb24gYGNzdmAgKHRhYi1zZXBhcmF0ZWQtdmFsdWVzKSBwZXV2ZW50IMOqdHJlIFtvdXZlcnRzIGF2ZWMgRXhjZWxdKGh0dHBzOi8vcmlldmVudC56ZW5kZXNrLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8zNjAwMDAwMjkxNzItRkFRLUhvdy1kby1JLW9wZW4tYS10c3YtZmlsZS1pbi1FeGNlbC0pLCBvdSBPcGVuT2ZmaWNlIENhbCwgb3UgbcOqbWUgYXZlYyBuJ2ltcG9ydGUgcXVlbCDDqWRpdGV1ciBkZSB0ZXh0ZS4gTm90ZTogbGUgcGFja2FnZSBgcmVhZHJgIGRlIFIgZm91cm5pdCBhdXNzaSBkZXMgZm9uY3Rpb25zIGB3cml0ZV9leGNlbF9jc3ZgIGV0IGB3cml0ZV9leGNlbF9jc3YyYCBxdWkgcGV1dmVudCBpbnTDqXJlc3NlciBjZXJ0YWlucy4KClNpIHZvdXMgYXZleiB1bmUgbGlzdGUgZGUgbW90cyBwbHVzIGxvbmd1ZXMsIGlsIHNlcmFpdCBmYXN0aWRpZXV4IGQnw6ljcmlyZSBsYSBsaWduZSBgaXRlbXMgPC0gLi4uYC4gUGx1cyBzaW1wbGVtZW50IHZvdXMgcG91dmV6IHV0aWxpc2VyOgoKYGBge3J9Cml0ZW1zID0gc2Nhbih3aGF0PSdjaGFyYWN0ZXJzJykKYGBgCgpFdCBjb3BpZXIgbGEgbGlzdGUgZGUgbW90cy4gRW50cmVyIHVuZSBsaWduZSB2aWRlIHRlcm1pbmUgbGUgcHJvY2Vzc3VzLiAKCkxhIGZvbmN0aW9uIGBzY2FuYCBwZXJtZXQgYXVzc2kgZGUgbGlyZSBsYSBsaXN0ZSBkYW5zIHVuIGZpY2hpZXIgZXh0ZXJuZS4KCiMjIFPDqWxlY3Rpb24gcGFyIGNyaXTDqHJlcwoKU3VwcG9zb25zIHF1ZSB2b3VzIHZvdWxpZXogc8OpbGVjdGlvbm5lciB0b3VzIGxlcyBub21zIGRlIDUgbGV0dHJlcywgc2luZ3VsaWVycywgZGUgZnLDqXF1ZW5jZSBsZXhpY2FsZSAoZmlsbXMpIGNvbXByaXNlIGVudHJlIDEwIGV0IDEwMC4gVm9pY2kgbGEgbGlnbmUgbWFnaXF1ZToKCmBgYHtyfQpzZWxlY3Rpb24gPSBzdWJzZXQobGV4aXF1ZSwgY2dyYW09PSdOT00nICYgbm9tYnJlICE9ICdwJyAmIG5ibGV0dHJlcz09NSAmIGZyZXFsaXZyZXMgPiAxMCAmIGZyZXFsaXZyZXMgPCAxMDApCmhlYWQoc2VsZWN0aW9uKQpgYGAKCiMjIFPDqWxlY3Rpb24gcGFyICJwYXR0ZXJuIgoKIyMjIExlcyBleHByZXNzaW9ucyByw6lndWxpw6hyZXMKCkxlcyBleHByZXNzaW9ucyByw6l1bGnDqHJlcywgb3UgKipyZWdleCoqLCBzb250IGRlcyDigJxwYXR0ZXJuc+KAnSBxdWkgcGVybWV0dGVudCBkZSByZWNoZXJjaGVyIGRlcyBtb3RzIGF5YW50IGNlcnRhaW5lcyBwcm9wcmnDqXTDqXMuIFBhciBleGVtcGxlIG4naW1wb3J0ZSBgYS5iYCBkw6lzaWduZSB1biBtb3QgY29udGVuYW50IHVuIGBhYCBldCB1biBgYmAgc8OpcGFyw6lzIHBhciB1bmUgbGV0dHJlIHF1ZWxvbnF1ZS4gVm9pY2kgZCdhdXRyZSBleGVtcGxlczoKCi0gKipgXm1haXNvbiRgKiogOiByZWNoZXJjaGUgbGUgbW90IOKAnG1haXNvbuKAnSBleGFjdGVtZW50Ci0gKipgXmFudGlgKiogOiByZWNoZXJjaGUgdG91cyBsZXMgbW90cyBjb21tZW7Dp2FudCBwYXIg4oCcYW50aeKAnQotICoqYF5qb3VyJHxebnVpdCR8Xm1hdGluJHxec29pciRgKiogOiDigJxqb3Vy4oCdIG91IOKAnG51aXTigJ0gb3Ug4oCcbWF0aW7igJ0gb3Ug4oCcc29pcuKAnSAocGVybWV0IGRlIHJlY2hlcmNoZXIgdW5lIGxpc3RlIGRlIG1vdHMpCi0gKipgaW9uYCoqIDogcmVjaGVyY2hlIGxlcyBtb3RzIHF1aSBjb250aWVubmVudCBsYSBjaGFpbmUg4oCcaW9u4oCdIGRhbnMgbuKAmWltcG9ydGUgcXVlbGxlIHBvc2l0aW9uCi0gKipgaW9uJGAqKiA6IG1vdHMgc2UgdGVybWluYW50IHBhciDigJxpb27igJ0KLSAqKmBecHJgKiogOiBtb3RzIGNvbW1lbsOnYW50IHBhciDigJxwcuKAnQotICoqYF5wLi5yYCQqKiA6IG1vdHMgZGUgcXVhdHJlIGxldHRyZXMgY29tbWVuw6dhbnQgcGFyIOKAnHDigJ0sIGZpbmlzYW50IHBhcyDigJxy4oCdCi0gKipgXnAuKnIkYCoqIDogbW90cyBjb21tZW5jYW50IHBhciDigJjigJ1w4oCZIGV0IGZpbmlzc2FudCBwYXIg4oCccuKAnQotICoqYFthZWlvdV1bYWVpb3VdYCoqIDogbW90cyBjb250ZW5hbnQgMiB2b3llbGxlcyBzdWNjZXNzaXZlcwotICoqYF5bYWVpb3VdYCoqIDogbW90cyBjb21tZW7Dp2FudCBwYXIgdW5lIHZveWVsbGUKLSAqKmBeW15hZXJpb3VdYCoqIDogbW90cyBuZSBjb21tZW7Dp2FudCBwYXMgcGFyIHVuZSB2b3llbGxlCgpJbCBleGlzdGUgZGUgbm9tYnJldXggdHV0b3JpYXV4IHN1ciBsZXMgcmVnZXggc3VyIGxlIHdlYiwgbm90YW1tZW50IFtjZWx1aS1jaV0oaHR0cDovL3d3dy5jYW55b3VzZW9tZS5jb20vZ3VpZGUtcmVnZXgvKS4gTGEgYmlibGUgc3VyIGxlIHN1amV0IGVzdCBsZSBsaXZyZSBfW01hc3RlcmluZyBSZWd1bGFyIEV4cHJlc3Npb25zXShodHRwOi8vcmVnZXguaW5mby9ib29rLmh0bWwpXyAgZGUgSmVmZiBGcmllZGwuIAoKVW5lIGV4cHJlc3Npb24gcsOpZ3VsacOocmUgZMOpY3JpdCB1biBhdXRvbWF0ZSBkZSB0cmFuc2l0aW9ucyDDoCDDqXRhdHMgZmluaXMuIExlIHNpdGUgPGh0dHBzOi8vcmVnZXhwZXIuY29tLz4gdm91cyBwZXJtZXQgZGUgdmlzdWFsaXNlciBs4oCZYXV0b21hdGUgYXNzb2Npw6kgw6AgdW5lIHJlZ2V4LiBQYXIgZXhlbXBsZSBgW3B0a10uKlthaW91XVthaW91XS4/aW9uJGAgY29ycmVzcG9uZCDDoCBs4oCZYXV0b21hdGUgZmluaToKCiFbXShmc2EucG5nKQoKIyMjIFJlY2hlcmNoZXMgZGFucyBSIGF2ZWMgZ3JlcGwKClIgcGVybWV0IGQnZWZmZWN0dWVyIGRlcyByZWNoZXJjaGVzIHBhciBwYXR0ZXJuIGdyw6JjZSDDoCBsYSBmb25jdGlvbiBgZ3JlcGxgLiBMYSBzeW50YXhlIGVzdCBgZ3JlcGwoYF9yZWdleF8sIF92YXJpYWJsZV9gKWAgcG91ciByZWNoZXJjaGVyIGxlcyBsaWduZXMgb8O5IGxhIHZhcmlhYmxlICJtYXRjaGUiIGxhIHJlZ2V4IChWb2lyIGxhIFtkb2MgUiBkZSBncmVwbF0oaHR0cHM6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2Jhc2UvaHRtbC9ncmVwLmh0bWwpLgoKQ2V0dGUgZm9uY3Rpb24gcGVybWV0IGRlIGxvY2FsaXNlciBsZXMgbGlnbmVzIHF1aSAnbWF0Y2hlbnQnIHVuZSBleHByZXNzaW9uLCBvdSBiaWVuLCBlbiBsYSBuaWFudCBhdmVjIGxlIHNpZ25lIGAhYCwgZGUgc3VwcHJpbWVyIGRlcyBsaWduZXMgcXVpIG1hdGNoZW50IHVuIHBhdHRlcm4uCgpWb2ljaSBxdWVscXVlcyBleGVtcGxlczoKCiogUG91ciBvYnRlbmlyIHRvdXMgbGVzIG1vdHMgcXVpIGZpbmlzc2VudCBwYXIgYHRpb25gIDoKCmBgYHtyfQpsZXhpcXVlICU+JSBmaWx0ZXIoZ3JlcGwoInRpb24kIiwgb3J0aG8pKSAtPiBzZWxlY3Rpb24yCmhlYWQoc2VsZWN0aW9uMikKYGBgCgpFbmNvcmUgdW5lIGZvaXMsIHZvdXMgcG91dmV6IHNhdXZlZ2FyZGVyIGNlcyByw6lzdWx0YXRzIGF2ZWM6CgoKYGBge3J9CndyaXRlX3RzdihzZWxlY3Rpb24yLCAnbW90cy1lbi10aW9uLnRzdicpCmBgYAoKKiBQb3VyIGxpc3RlciB0b3VzIGxlcyBtb3RzIGNvbnRlbmFudCB1biBjbHVzdGVyIGRlIGNvbnNvbm5lcyBwbG9zaXZlcywgbWFpcyBwYXMgZGVidXQgZGUgbW90OgoKYGBge3J9CmxleGlxdWUgJT4lIGZpbHRlcihncmVwbCgnLltwdGtiZGddW3B0a2JkZ10nLCBwaG9uKSkgLT4gc2VsZWN0aW9uMwpoZWFkKHNlbGVjdGlvbjMpCmBgYAoKCiogTCdvcMOpcmF0ZXVyIGBmaWx0ZXJgcGV1dCDDqnRyZSBhcHBlbGVyIHBsdXNpZXVycyBmb2lzIHBvdXIgYWZmaW5lciBwcm9ncmVzc2l2ZW1lbnQgbGEgcmVjaGVyY2hlLiAKCiAgUGFyIGV4ZW1wbGUsIHBvdXIgb2J0ZW5pciB0b3VzIGxlcyBtb3RzIGRlIDggbGV0dHJlcyBxdWkgbmUgZmluaXNzZW50IHBhcyBgZW50YDoKICAKYGBge3J9CmxleGlxdWUgJT4lIGZpbHRlcihuYmxldHRyZXMgPT0gOCkgJT4lIGZpbHRlcighZ3JlcGwoImVudCQiLCBvcnRobykpIC0+IHNlbGVjdGlvbjQKaGVhZChzZWxlY3Rpb240KQpgYGAKCgoK