Chargement des packages nécessaires et des données

R est un langage de programmation flexible et en constante évolution grâce à un immense échantillon de “packages”/paquets qui permettent d’importer des fonctions.

Un ensemble de paquets quasi-indispensable sur R sont regroupés sous une sorte de méta-paquet, tidyverse. Plus particulièrement nous allons surtout utiliser les packages stringr et dplyr de tidyverse. On “charge” ensuite tidyverse pour pouvoir en utiliser les fonctions.

install.packages("tidyverse", dependencies = TRUE)
library(tidyverse)

Une autre manière d’utiliser les fonctions d’un package sans le charger est d’écrire tidyverse::la_fonction_utilisée. Dans ce tutoriel, tous les packages utilisés seront chargés, mais j’essaierai tout de même d’utiliser cette formulation pour que vous puissiez voir à quel package appartie chaque fonction.

Quand on a plusieurs packages à installer et charger, il peut être plus clair de mettre ces packages dans une liste, d’installer ces packages uniquement s’ils ne sont pas déjà installés, puis de les charger.

package_list <- c(
  "tidyverse",
  "here", # use for paths creation
  "janitor", # useful functions for cleaning imported data
  "biblionetwork" # creating edges
)

for (p in package_list) {
  if (p %in% installed.packages() == FALSE) {
    install.packages(p, dependencies = TRUE)
  }
  library(p, character.only = TRUE)
}

Le package here permet de manipuler plus aisément les sentiers de vos dossiers sur votre ordinateur. Cela est important quand vous importez et exportez des fichiers. Par exemple, la commande suivante vous permet de savoir où est votre “dossier de travail” actuel:

here::here() # on enregistre le sentier dans un "objet"
[1] "C:/Users/goutsmedt/Documents/MEGAsync/Research/R"
C:/Users/goutsmedt/Documents/MEGAsync/Research/R

Pour plus de simplicité, vous pouvez mettre les données transmises par mail, dsge_articles_sample.csv dans ce dossier. Vous n’avez alors plus qu’à les importer avec readr::read_csv2(here("dsge_articles_sample.csv")). Ou bien vous pouvez mettre les données dans le même dossier que le présent fichier et l’importer comme suite:

data <- readr::read_csv2("dsge_articles_sample.csv")
i Using "','" as decimal and "'.'" as grouping mark. Use `read_delim()` for more control.
Rows: 500 Columns: 22
-- Column specification ---------------------------------------------------------------------------------------------------------------
Delimiter: ";"
chr (19): authors, title, source_title, volume, issue, art_no, page_start, page_end, doi, affiliations, authors_with_affiliations, ...
dbl  (3): year, page_count, cited_by

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.

On peut désormais regarder à quoi ressemble nos données:

data

Nettoyage des données

Il y a plusieurs choses à nettoyer :

Dans ce qui suit, nous nettoyons data afin de produire 2 data.frames supplémentaires :

(Très) brève introduction aux regex

Pour nettoyer les données nous allons utiliser un langage particulier, très rebutant au départ: les expressions régulières (regular expressions ou regex en anglais). Les regex permettent de repérer dans un ensemble de caractères certaines particularités. Vous pouvez par exemple ensuite supprimer, extraire, modifier ces particularités.

phrase_stupide <- "Nous somme en cours le vendredi 4 février et l'intervenant commence à nous parler de quelque chose d'étrange: les regex."

Donnons nous quelques tâches:

  • extraire le premier mot, ainsi que le dernier
premier_mot <- stringr::str_extract(phrase_stupide, "^[A-z]{4}")

premier_mot <- stringr::str_extract(phrase_stupide, "^[A-z]+") # peut être simplifié

dernier_mot <- stringr::str_extract(phrase_stupide, "[A-z]+$") # ne marche pas

dernier_mot <- stringr::str_extract(phrase_stupide, "[A-z]+\\.$") # peut être amélioré

dernier_mot <- stringr::str_extract(phrase_stupide, "[A-z]+(?=\\.$)")
  • extraire la date
date <- str_extract(phrase_stupide, "[A-z]+ \\d [A-z]+") # ne marche pas pour le mois

date <- str_extract(phrase_stupide, "[A-z]+ \\d [:letter:]+")
  • remplacer la dernière partie du texte après les deux points
nouvelle_phrase <- str_replace(phrase_stupide, ":.*", ": mais je m'en souviens plus.")
  • supprimer les mots de moins de 2 lettres ou moins
phrase_deconstruite <- str_remove_all(phrase_stupide, " [:letter:]{1,2} ")

phrase_deconstruite <- str_remove_all(phrase_stupide, "(?<= )[:letter:]{1,2}(?= )")

Maintenant, vous allez pouvoir tout comprendre au nettoyage des données qui suit. Ou peut-être pas…

Nettoyage des références

La première chose à faire est de créer un identifiant pour les articles citant, ce qui nous permettra de faire le pont entre les articles citant et les références citées:

data <- data %>% 
  mutate(citing_id = paste0("A", 1:n()))

Extrayons d’abord la colonne references en gardant un identifiant pour l’article citant. Nous mettons chaque référence citée par un article sur une ligne séparée, en utilisant le fait que les références sont séparées par un point-virgule. Nous créons un identifiant pour chaque référence.

#' ## Extracting and cleaning references
references_extract <- data %>% 
  filter(! is.na(references)) %>% # enlever les articles qui n'ont pas de références
  select(citing_id, references) %>% 
  separate_rows(references, sep = "; ") %>% # on met une référence par ligne
  mutate(id_ref = 1:n()) %>% # on donne un identifiant aux références
  as_tibble

references_extract

Notre but désormais est de pouvoir extraire les métadonnées de chaque référence: les auteurs, la date de publication, le titre, le journal, etc… L’objectif est d’avoir des informations relativement standardisées, pour pouvoir ensuite identifier quelles sont les références qui sont les mêmes. Une première étape est d’extraire les auteurs ainsi que l’année de publication. On enlève les auteurs de la référence principale et on stock l’info restante, qu’on va nettoyer, dans une colonne séparée.

extract_authors <- ".*[:upper:][:alpha:]+( Jr(.)?)?, ([A-Z]\\.[ -]?)?([A-Z]\\.[ -]?)?([A-Z]\\.)?[A-Z]\\."
extract_year_brackets <- "(?<=\\()\\d{4}(?=\\))"
extract_pages <- "(?<= (p)?p\\. )([A-Z])?\\d+(-([A-Z])?\\d+)?"
extract_volume_and_number <- "(?<=( |^)?)\\d+ \\(\\d+(-\\d+)?\\)"

cleaning_references <- references_extract %>% 
  mutate(authors = str_extract(references, paste0(extract_authors, "(?=, )")),
         remaining_ref = str_remove(references, paste0(extract_authors, ", ")), # cleaning from authors
         is_article = ! str_detect(remaining_ref, "^\\([:digit:]{4}"), 
         year = str_extract(references, extract_year_brackets) %>% as.integer)

cleaning_references

A partir de maintenant, il y a beaucoup trop d’étapes pour pouvoir tout détailler, mais la stratégie générale est de séparer les références suivant la position de l’année de publication: le plus simple à nettoyer (cleaning_articles) est quand la titre est avant la date de publication car cela permet d’extraire plus simplement le titre, et donc aussi, en général, le nom du journal qui va suivre la date de publication. On extrait également des informations utiles comme le volume, le numéro, les pages, et le DOI.

cleaning_articles <- cleaning_references %>% 
  filter(is_article == TRUE) %>% 
  mutate(title = str_extract(remaining_ref, ".*(?=\\(\\d{4})"), # pre date extraction
         journal_to_clean = str_extract(remaining_ref, "(?<=\\d{4}\\)).*"), # post date extraction
         journal_to_clean = str_remove(journal_to_clean, "^,") %>% str_trim("both"), # cleaning a bit the journal info column
         pages = str_extract(journal_to_clean, extract_pages), # extracting pages
         volume_and_number = str_extract(journal_to_clean, extract_volume_and_number), # extracting standard volument and number: X (X)
         journal_to_clean = str_remove(journal_to_clean, " (p)?p\\. ([A-Z])?\\d+(-([A-Z])?\\d+)?"), # clean from extracted pages
         journal_to_clean = str_remove(journal_to_clean, "( |^)?\\d+ \\(\\d+(-\\d+)?\\)"), # clean from extracted volume and number
         volume_and_number = ifelse(is.na(volume_and_number), str_extract(journal_to_clean, "(?<= )([A-Z])?\\d+(-\\d+)?"), volume_and_number), # extract remaining numbers
         journal_to_clean = str_remove(journal_to_clean, " ([A-Z])?\\d+(-\\d+)?"), # clean from remaining numbers
         journal = str_remove_all(journal_to_clean, "^[:punct:]+( )?[:punct:]+( )?|(?<=,( )?)[:punct:]+( )?([:punct:])?|[:punct:]( )?[:punct:]+( )?$"), # extract journal info by removing inappropriate punctuations
         first_page = str_extract(pages, "\\d+"),
         volume = str_extract(volume_and_number, "\\d+"),
         issue = str_extract(volume_and_number, "(?<=\\()\\d+(?=\\))"),
         publisher = ifelse(is.na(first_page) & is.na(volume) & is.na(issue) & ! str_detect(journal, "(W|w)orking (P|p)?aper"), journal, NA),
         book_title = ifelse(str_detect(journal, " (E|e)d(s)?\\.| (E|e)dite(d|urs)? "), journal, NA), # Incollection article: Title of the book here
         book_title = str_extract(book_title, "[A-z ]+(?=,)"), # keeping only the title of the book
         publisher = ifelse(!is.na(book_title), NA, publisher), # if we have an incollection article, that's not a book, so no publisher
         journal = ifelse(!is.na(book_title) | ! is.na(publisher), NA, journal), # removing journal as what we have is a book
         publisher = ifelse(is.na(publisher) & str_detect(journal, "(W|w)orking (P|p)?aper"), journal, publisher), # adding working paper publisher information in publisher column
         journal = ifelse(str_detect(journal, "(W|w)orking (P|p)?aper"), "Working Paper", journal))

cleaned_articles <- cleaning_articles %>% 
  select(citing_id, id_ref, authors, year, title, journal, volume, issue, pages, first_page, book_title, publisher, references)

cleaning_non_articles <- cleaning_references %>% 
  filter(is_article == FALSE) %>% 
  mutate(remaining_ref = str_remove(remaining_ref, "\\(\\d{4}\\)(,)? "),
         title = str_extract(remaining_ref, ".*(?=, ,)"),
         pages = str_extract(remaining_ref, "(?<= (p)?p\\. )([A-Z])?\\d+(-([A-Z])?\\d+)?"), # extracting pages
         volume_and_number = str_extract(remaining_ref, "(?<=( |^)?)\\d+ \\(\\d+(-\\d+)?\\)"), # extracting standard volument and number: X (X)
         remaining_ref = str_remove(remaining_ref, " (p)?p\\. ([A-Z])?\\d+(-([A-Z])?\\d+)?"), # clean from extracted pages
         remaining_ref = str_remove_all(remaining_ref, ".*, ,"), # clean dates and already extracted titles
         remaining_ref = str_remove(remaining_ref, "( |^)?\\d+ \\(\\d+(-\\d+)?\\)"), # clean from extracted volume and number
         volume_and_number = ifelse(is.na(volume_and_number), str_extract(remaining_ref, "(?<= )([A-Z])?\\d+(-\\d+)?"), volume_and_number), # extract remaining numbers
         remaining_ref = str_remove(remaining_ref, " ([A-Z])?\\d+(-\\d+)?"), # clean from remaining numbers
         journal = ifelse(str_detect(remaining_ref, "(W|w)orking (P|p)aper"), "Working Paper", NA),
         journal = ifelse(str_detect(remaining_ref, "(M|m)anuscript"), "Manuscript", journal),
         journal = ifelse(str_detect(remaining_ref, "(M|m)imeo"), "Mimeo", journal),
         publisher = ifelse(is.na(journal), remaining_ref, NA) %>% str_trim("both"),
         first_page = str_extract(pages, "\\d+"),
         volume = str_extract(volume_and_number, "\\d+"),
         issue = str_extract(volume_and_number, "(?<=\\()\\d+(?=\\))"),
         book_title = NA) # to be symetric with "cleaned_articles"

cleaned_non_articles <- cleaning_non_articles %>% 
  select(citing_id, id_ref, authors, year, title, journal, volume, issue, pages, first_page, book_title, publisher, references)

# merging the two files.
cleaned_ref <- rbind(cleaned_articles, cleaned_non_articles)

#' Now we have all the references, we can do a bit of cleaning on the authors name,
#' and extract useful information, like DOI, for matching later.

cleaned_ref <- cleaned_ref %>% 
  mutate(authors = str_remove(authors, " Jr\\."), # standardising authors name to favour matching later
         authors = str_remove(authors, "^\\(\\d{4}\\)(\\.)?( )?"),
         authors = str_remove(authors, "^, "),
         authors = ifelse(is.na(authors), str_extract(references, ".*[:upper:]\\.(?= \\d{4})"), authors), # specific case
         journal = str_remove(journal, "[:punct:]$"), # remove unnecessary punctuations at the end
         doi = str_extract(references, "(?<=DOI(:)? ).*|(?<=\\/doi\\.org\\/).*"),
         pii = str_extract(doi, "(?<=PII ).*"),
         doi = str_remove(doi, ",.*"), # cleaning doi
         pii = str_remove(pii, ",.*"), # cleaning pii
  )

cleaned_ref

Matcher les références ensemble

Regardons de plus prêt la première ligne de notre tableau des références pour mieux comprendre les problèmes potentiels:

cleaned_ref[1,]

Notre but est de trouver toutes les citations qui renvoient au même article. En extrayant les informations de manière systématique, on exclut le problème des “styles” de citation différent. Au-delà de l’imperfection du nettoyage ci-dessus, il reste plusieurs problèmes qui peuvent nous empêcher de matcher les références correctement:

  • Informations non-référencées;
  • Erreurs;
  • Différentes manières d’écrire le journal;
  • Variations dans l’écriture des titres (souvent lié à la ponctuation ou aux articles);
  • Différences dans l’écriture des noms (souvent alternance entre une ou deux initiales).

Le compromis consiste à faire correspondre autant de vrais positifs que possible (références identiques) tout en évitant de faire correspondre des faux positifs, c’est-à-dire des références qui ont des informations en commun, mais qui ne sont en fait pas les mêmes. Par exemple, une correspondance basée uniquement sur le nom des auteurs et l’année de publication est trop large, car ces auteurs peuvent avoir publié plusieurs articles au cours de la même année. Voici plusieurs façons d’identifier une référence commune qui comportent très peu de risques de faire correspondre des références différentes :

  • même nom de famille du premier auteur ou des auteurs, année, volume et page (ce sont les plus sûres) : appelons-les fayvp & ayvp ;
  • même journal, volume, numéro et première page : jvip ;
  • même auteur, année et titre : ayt ;
  • même titre, même année et même première page : typ ;
  • même Doi ou PII.

Nous extrayons le nom de famille du premier auteur pour favoriser la correspondance car il y a plus de possibilités de petites différences pour plusieurs auteurs qui nous empêcheraient de faire correspondre des références similaires.

cleaned_ref <- cleaned_ref %>%
  mutate(first_author = str_extract(authors, "^[[:alpha:]+[']?[ -]?]+, ([A-Z]\\.[ -]?)?([A-Z]\\.[ -]?)?([A-Z]\\.)?[A-Z]\\.(?=(,|$))"),
         first_author_surname = str_extract(first_author, ".*(?=,)"),
         across(.cols = c("authors", "first_author", "journal", "title"), ~toupper(.))) 

matching_ref <- function(data, id_ref, ..., col_name){
  match <- data %>% 
    group_by(...) %>% 
    mutate(new_id = min({{id_ref}})) %>% 
    drop_na(...) %>% 
    ungroup() %>% 
    select({{id_ref}}, new_id) %>% 
    rename_with(~ paste0(col_name, "_new_id"), .cols = new_id)
  
  data <- data %>% 
    left_join(match)
}

identifying_ref <- cleaned_ref %>%
  matching_ref(id_ref, first_author_surname, year, title, col_name = "fayt") %>% 
  matching_ref(id_ref, journal, volume, issue, first_page, col_name = "jvip") %>% 
  matching_ref(id_ref, authors, year, volume, first_page, col_name = "ayvp") %>% 
  matching_ref(id_ref, first_author_surname, year, volume, first_page, col_name = "fayvp") %>%
  matching_ref(id_ref, title, year, first_page, col_name = "typ") %>% 
  matching_ref(id_ref, pii, col_name = "pii") %>% 
  matching_ref(id_ref, doi, col_name = "doi") 
Joining, by = "id_ref"
Joining, by = "id_ref"
Joining, by = "id_ref"
Joining, by = "id_ref"
Joining, by = "id_ref"
Joining, by = "id_ref"
Joining, by = "id_ref"
View(identifying_ref)

Nous avons maintenant notre tableau de citations directes reliant les articles citant aux références. Nous avons autant de lignes que le nombre de citations par les articles citant. Les deux premières colonnes, sont les seules colonnes nécessaires pour construire les liens de notre réseau tout à l’heure.

direct_citation <- identifying_ref %>%  
  mutate(new_id_ref = select(., ends_with("new_id")) %>%  reduce(pmin, na.rm = TRUE),
         new_id_ref = ifelse(is.na(new_id_ref), id_ref, new_id_ref))  %>% 
  relocate(new_id_ref, .after = citing_id) %>% 
  select(-id_ref & ! ends_with("new_id"))

direct_citation

Nous pouvons extraire la liste de toutes les références citées. Nous avons autant de lignes que de références citées par les articles citant (c’est-à-dire qu’une référence citée plusieurs fois n’est présente qu’une seule fois dans le tableau). Comme pour les références appariées ensemble, on peut avoir des informations différentes (dues au fait que les références ont été citées différemment selon les articles citant), nous prenons une ligne où l’information semble être la plus complète, c’est-à-dire là où il y a le moins d’information manquantes dans les métadonnées.

important_info <- c("authors",
                    "year",
                    "title",
                    "journal",
                    "volume",
                    "issue",
                    "pages",
                    "book_title",
                    "publisher")
references <- direct_citation %>% 
  mutate(nb_na = rowSums(!is.na(select(., all_of(important_info))))) %>% 
  group_by(new_id_ref) %>% 
  slice_max(order_by = nb_na, n = 1, with_ties = FALSE) %>% 
  select(-citing_id) %>% 
  unique

Les données que vous avez désormais nettoyées vous permettent de faire des premières explorations statistiques. Par exemple, on peut regarder quels sont les travaux les plus cités:

direct_citation %>% 
  add_count(new_id_ref) %>% 
  select(new_id_ref, n) %>% 
  unique() %>% 
  slice_max(n, n = 10) %>%
  left_join(select(references, new_id_ref, references)) %>% 
  select(references, n)
Joining, by = "new_id_ref"

Préparer noeuds et liens pour le réseau de co-citation

Il s’agit désormais de préparer les données que nous allons utiliser dans Gephi pour construire un réseau. On veut faire un réseau de co-citation, donc on s’intéresse aux références citées (le couplage bibliographique s’intéresse aux articles citant). Pour limiter le nombre de nœuds, on peut choisir de ne prendre que les nœuds les plus cités:

citations <- direct_citation %>% 
  add_count(new_id_ref) %>% 
  select(new_id_ref, n) %>% 
  unique

nodes <- references %>% 
  left_join(citations) %>% 
  filter(n >= 10)
Joining, by = "new_id_ref"
nodes

On va ensuite créer les liens à partir des citations directes: pour chaque référence, on regarde quels articles la citent. Ensuite, on chercher quelles autres références sont citées par les mêmes articles. En d’autres mots, toutes les références citées dans une même bibliographie (d’un article citant) vont être reliées entre elles. On utilise une méthode qui permet de pondérer les liens en fonction du nombre de fois où chaque référence est citée: en effet, on considère que si deux références beaucoup citées sont citées k fois ensemble, ce lien doit être moins important que des références peu citées, qui sont citées le même nombre k de fois ensemble.

direct_citation <- direct_citation %>% 
  filter(new_id_ref %in% nodes$new_id_ref)

edges <- biblionetwork::biblio_cocitation(direct_citation, 
                                          "citing_id", 
                                          "new_id_ref",
                                          weight_threshold = 5)

edges
LS0tDQp0aXRsZTogIk5ldHRveWFnZSBkZXMgZG9ubsOpZXMgU2NvcHVzIHN1ciBsZXMgRFNHRSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQojIENoYXJnZW1lbnQgZGVzIHBhY2thZ2VzIG7DqWNlc3NhaXJlcyBldCBkZXMgZG9ubsOpZXMNCg0KUiBlc3QgdW4gbGFuZ2FnZSBkZSBwcm9ncmFtbWF0aW9uIGZsZXhpYmxlIGV0IGVuIGNvbnN0YW50ZSDDqXZvbHV0aW9uIGdyw6JjZSDDoCB1biBpbW1lbnNlIMOpY2hhbnRpbGxvbiBkZSAicGFja2FnZXMiL3BhcXVldHMgcXVpIHBlcm1ldHRlbnQgZCdpbXBvcnRlciBkZXMgZm9uY3Rpb25zLg0KDQpVbiBlbnNlbWJsZSBkZSBwYXF1ZXRzIHF1YXNpLWluZGlzcGVuc2FibGUgc3VyIFIgc29udCByZWdyb3Vww6lzIHNvdXMgdW5lIHNvcnRlIGRlIG3DqXRhLXBhcXVldCwgW3RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pLiBQbHVzIHBhcnRpY3VsacOocmVtZW50IG5vdXMgYWxsb25zIHN1cnRvdXQgdXRpbGlzZXIgbGVzIHBhY2thZ2VzIFtzdHJpbmdyXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy8pIGV0IFtkcGx5cl0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnLykgZGUgdGlkeXZlcnNlLiBPbiAiY2hhcmdlIiBlbnN1aXRlIHRpZHl2ZXJzZSBwb3VyIHBvdXZvaXIgZW4gdXRpbGlzZXIgbGVzIGZvbmN0aW9ucy4gDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNClVuZSBhdXRyZSBtYW5pw6hyZSBkJ3V0aWxpc2VyIGxlcyBmb25jdGlvbnMgZCd1biBwYWNrYWdlIHNhbnMgbGUgY2hhcmdlciBlc3QgZCfDqWNyaXJlIGB0aWR5dmVyc2U6OmxhX2ZvbmN0aW9uX3V0aWxpc8OpZWAuIERhbnMgY2UgdHV0b3JpZWwsIHRvdXMgbGVzIHBhY2thZ2VzIHV0aWxpc8OpcyBzZXJvbnQgY2hhcmfDqXMsIG1haXMgaidlc3NhaWVyYWkgdG91dCBkZSBtw6ptZSBkJ3V0aWxpc2VyIGNldHRlIGZvcm11bGF0aW9uIHBvdXIgcXVlIHZvdXMgcHVpc3NpZXogdm9pciDDoCBxdWVsIHBhY2thZ2UgYXBwYXJ0aWUgY2hhcXVlIGZvbmN0aW9uLiANCg0KUXVhbmQgb24gYSBwbHVzaWV1cnMgcGFja2FnZXMgw6AgaW5zdGFsbGVyIGV0IGNoYXJnZXIsIGlsIHBldXQgw6p0cmUgcGx1cyBjbGFpciBkZSBtZXR0cmUgY2VzIHBhY2thZ2VzIGRhbnMgdW5lIGxpc3RlLCBkJ2luc3RhbGxlciBjZXMgcGFja2FnZXMgdW5pcXVlbWVudCBzJ2lscyBuZSBzb250IHBhcyBkw6lqw6AgaW5zdGFsbMOpcywgcHVpcyBkZSBsZXMgY2hhcmdlci4NCg0KYGBge3J9DQpwYWNrYWdlX2xpc3QgPC0gYygNCiAgInRpZHl2ZXJzZSIsDQogICJoZXJlIiwgIyB1c2UgZm9yIHBhdGhzIGNyZWF0aW9uDQogICJqYW5pdG9yIiwgIyB1c2VmdWwgZnVuY3Rpb25zIGZvciBjbGVhbmluZyBpbXBvcnRlZCBkYXRhDQogICJiaWJsaW9uZXR3b3JrIiAjIGNyZWF0aW5nIGVkZ2VzDQopDQoNCmZvciAocCBpbiBwYWNrYWdlX2xpc3QpIHsNCiAgaWYgKHAgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSA9PSBGQUxTRSkgew0KICAgIGluc3RhbGwucGFja2FnZXMocCwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgfQ0KICBsaWJyYXJ5KHAsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCmBgYA0KDQpMZSBwYWNrYWdlICpoZXJlKiBwZXJtZXQgZGUgbWFuaXB1bGVyIHBsdXMgYWlzw6ltZW50IGxlcyBzZW50aWVycyBkZSB2b3MgZG9zc2llcnMgc3VyIHZvdHJlIG9yZGluYXRldXIuIENlbGEgZXN0IGltcG9ydGFudCBxdWFuZCB2b3VzIGltcG9ydGV6IGV0IGV4cG9ydGV6IGRlcyBmaWNoaWVycy4gUGFyIGV4ZW1wbGUsIGxhIGNvbW1hbmRlIHN1aXZhbnRlIHZvdXMgcGVybWV0IGRlIHNhdm9pciBvw7kgZXN0IHZvdHJlICJkb3NzaWVyIGRlIHRyYXZhaWwiIGFjdHVlbDoNCg0KYGBge3J9DQpoZXJlOjpoZXJlKCkgIyBvbiBlbnJlZ2lzdHJlIGxlIHNlbnRpZXIgZGFucyB1biAib2JqZXQiDQpgYGANCg0KUG91ciBwbHVzIGRlIHNpbXBsaWNpdMOpLCB2b3VzIHBvdXZleiBtZXR0cmUgbGVzIGRvbm7DqWVzIHRyYW5zbWlzZXMgcGFyIG1haWwsIGBkc2dlX2FydGljbGVzX3NhbXBsZS5jc3ZgIGRhbnMgY2UgZG9zc2llci4gVm91cyBuJ2F2ZXogYWxvcnMgcGx1cyBxdSfDoCBsZXMgaW1wb3J0ZXIgYXZlYyAgYHJlYWRyOjpyZWFkX2NzdjIoaGVyZSgiZHNnZV9hcnRpY2xlc19zYW1wbGUuY3N2IikpYC4gT3UgYmllbiB2b3VzIHBvdXZleiBtZXR0cmUgbGVzIGRvbm7DqWVzIGRhbnMgbGUgbcOqbWUgZG9zc2llciBxdWUgbGUgcHLDqXNlbnQgZmljaGllciBldCBsJ2ltcG9ydGVyIGNvbW1lIHN1aXRlOg0KDQpgYGB7cn0NCmRhdGEgPC0gcmVhZHI6OnJlYWRfY3N2MigiZHNnZV9hcnRpY2xlc19zYW1wbGUuY3N2IikNCmBgYA0KDQpPbiBwZXV0IGTDqXNvcm1haXMgcmVnYXJkZXIgw6AgcXVvaSByZXNzZW1ibGUgbm9zIGRvbm7DqWVzOg0KDQpgYGB7cn0NCmRhdGENCmBgYA0KDQojIE5ldHRveWFnZSBkZXMgZG9ubsOpZXMNCg0KSWwgeSBhIHBsdXNpZXVycyBjaG9zZXMgw6AgbmV0dG95ZXIgOg0KDQotIE5vdXMgYXZvbnMgcGx1c2lldXJzIGBhdXRldXJzYCBwYXIgZG9jdW1lbnQuIFBvdXIgY2VydGFpbmVzIGFuYWx5c2VzLCBwYXIgZXhlbXBsZSBsZXMgcsOpc2VhdXggZGUgY28tYXV0ZXVycywgaWwgZXN0IHByw6lmw6lyYWJsZSBkJ2F2b2lyIHVuZSAidGFibGUgZGVzIGF1dGV1cnMiIHF1aSBhc3NvY2llIGNoYXF1ZSBhdXRldXIgw6AgdW5lIGxpc3RlIGQnYXJ0aWNsZXMgOw0KLSBOb3VzIGF2b25zIHBsdXNpZXVycyBgYWZmaWxpYXRpb25zYCBwYXIgYXJ0aWNsZSBhaW5zaSBxdWUgcGx1c2lldXJzIGBhdXRob3JzX3dpdGhfYWZmaWxpYXRpb25zYC4gQ2VsYSBub3VzIHBlcm1ldCBkZSByZWxpZXIgbGVzIGF1dGV1cnMgw6AgbGV1cnMgYWZmaWxpYXRpb25zLCBtYWlzIGzDoCBlbmNvcmUgbm91cyBkZXZvbnMgbGUgc8OpcGFyZXIgZW4gYXV0YW50IGRlIGxpZ25lcyBxdWUgZCdhdXRldXJzIChzaSBqZSBuZSBtZSB0cm9tcGUgcGFzLCBpbCBzZW1ibGUgcXUnaWwgbid5IGFpdCBxdSd1bmUgc2V1bGUgYWZmaWxpYXRpb24gcGFyIGF1dGV1ciBkYW5zIGNlIGpldSBkZSBkb25uw6llcykgOw0KLSBgcsOpZsOpcmVuY2VzYCA7DQotIEV2ZW50dWVsbGVtZW50IHBvdXIgc8OpcGFyZXIgYGF1dGhvcl9rZXl3b3Jkc2AgZXQgYGluZGV4X2tleXdvcmRzYCBzaSB2b3VzIHZvdWxleiBsJ3V0aWxpc2VyLg0KDQpEYW5zIGNlIHF1aSBzdWl0LCBub3VzIG5ldHRveW9ucyBgZGF0YWAgYWZpbiBkZSBwcm9kdWlyZSAyIGBkYXRhLmZyYW1lc2Agc3VwcGzDqW1lbnRhaXJlcyA6DQoNCi0gdW4gYGRhdGEuZnJhbWVgIHF1aSBhc3NvY2llIGNoYXF1ZSBhcnRpY2xlIMOgIGxhIGxpc3RlIGRlcyByw6lmw6lyZW5jZXMgcXUnaWwgY2l0ZSAodW4gYXJ0aWNsZSBhIGF1dGFudCBkZSBsaWduZXMgcXVlIGxlIG5vbWJyZSBkZSByw6lmw6lyZW5jZXMgY2l0w6llcykgOiBjJ2VzdCB1biB0YWJsZWF1IGRlICJjaXRhdGlvbnMgZGlyZWN0ZXMiIDsNCi0gdW5lIGxpc3RlIGRlIHRvdXRlcyBsZXMgcsOpZsOpcmVuY2VzIGNpdMOpZXMsIHF1aSBwZXJtZXQgZGUgcmV0cm91dmVyIGxlcyByw6lmw6lyZW5jZXMgaWRlbnRpcXVlcyBkYW5zIGxlIHRhYmxlYXUgZGUgY2l0YXRpb25zIGRpcmVjdGVzLg0KDQoNCiMjIChUcsOocykgYnLDqHZlIGludHJvZHVjdGlvbiBhdXggcmVnZXggDQoNClBvdXIgbmV0dG95ZXIgbGVzIGRvbm7DqWVzIG5vdXMgYWxsb25zIHV0aWxpc2VyIHVuIGxhbmdhZ2UgcGFydGljdWxpZXIsIHRyw6hzIHJlYnV0YW50IGF1IGTDqXBhcnQ6IGxlcyBleHByZXNzaW9ucyByw6lndWxpw6hyZXMgKHJlZ3VsYXIgZXhwcmVzc2lvbnMgb3UgcmVnZXggZW4gYW5nbGFpcykuIExlcyByZWdleCBwZXJtZXR0ZW50IGRlIHJlcMOpcmVyIGRhbnMgdW4gZW5zZW1ibGUgZGUgY2FyYWN0w6hyZXMgY2VydGFpbmVzIHBhcnRpY3VsYXJpdMOpcy4gVm91cyBwb3V2ZXogcGFyIGV4ZW1wbGUgZW5zdWl0ZSBzdXBwcmltZXIsIGV4dHJhaXJlLCBtb2RpZmllciBjZXMgcGFydGljdWxhcml0w6lzLiANCg0KYGBge3J9DQpwaHJhc2Vfc3R1cGlkZSA8LSAiTm91cyBzb21tZSBlbiBjb3VycyBsZSB2ZW5kcmVkaSA0IGbDqXZyaWVyIGV0IGwnaW50ZXJ2ZW5hbnQgY29tbWVuY2Ugw6Agbm91cyBwYXJsZXIgZGUgcXVlbHF1ZSBjaG9zZSBkJ8OpdHJhbmdlOiBsZXMgcmVnZXguIg0KYGBgDQoNCkRvbm5vbnMgbm91cyBxdWVscXVlcyB0w6JjaGVzOg0KDQotIGV4dHJhaXJlIGxlIHByZW1pZXIgbW90LCBhaW5zaSBxdWUgbGUgZGVybmllcg0KDQpgYGB7cn0NCnByZW1pZXJfbW90IDwtIHN0cmluZ3I6OnN0cl9leHRyYWN0KHBocmFzZV9zdHVwaWRlLCAiXltBLXpdezR9IikNCg0KcHJlbWllcl9tb3QgPC0gc3RyaW5ncjo6c3RyX2V4dHJhY3QocGhyYXNlX3N0dXBpZGUsICJeW0Etel0rIikgIyBwZXV0IMOqdHJlIHNpbXBsaWZpw6kNCg0KZGVybmllcl9tb3QgPC0gc3RyaW5ncjo6c3RyX2V4dHJhY3QocGhyYXNlX3N0dXBpZGUsICJbQS16XSskIikgIyBuZSBtYXJjaGUgcGFzDQoNCmRlcm5pZXJfbW90IDwtIHN0cmluZ3I6OnN0cl9leHRyYWN0KHBocmFzZV9zdHVwaWRlLCAiW0Etel0rXFwuJCIpICMgcGV1dCDDqnRyZSBhbcOpbGlvcsOpDQoNCmRlcm5pZXJfbW90IDwtIHN0cmluZ3I6OnN0cl9leHRyYWN0KHBocmFzZV9zdHVwaWRlLCAiW0Etel0rKD89XFwuJCkiKQ0KYGBgDQoNCi0gZXh0cmFpcmUgbGEgZGF0ZSANCg0KYGBge3J9DQpkYXRlIDwtIHN0cl9leHRyYWN0KHBocmFzZV9zdHVwaWRlLCAiW0Etel0rIFxcZCBbQS16XSsiKSAjIG5lIG1hcmNoZSBwYXMgcG91ciBsZSBtb2lzDQoNCmRhdGUgPC0gc3RyX2V4dHJhY3QocGhyYXNlX3N0dXBpZGUsICJbQS16XSsgXFxkIFs6bGV0dGVyOl0rIikNCg0KYGBgDQoNCi0gcmVtcGxhY2VyIGxhIGRlcm5pw6hyZSBwYXJ0aWUgZHUgdGV4dGUgYXByw6hzIGxlcyBkZXV4IHBvaW50cw0KDQpgYGB7cn0NCm5vdXZlbGxlX3BocmFzZSA8LSBzdHJfcmVwbGFjZShwaHJhc2Vfc3R1cGlkZSwgIjouKiIsICI6IG1haXMgamUgbSdlbiBzb3V2aWVucyBwbHVzLiIpDQpgYGANCg0KLSBzdXBwcmltZXIgbGVzIG1vdHMgZGUgbW9pbnMgZGUgMiBsZXR0cmVzIG91IG1vaW5zDQoNCmBgYHtyfQ0KcGhyYXNlX2RlY29uc3RydWl0ZSA8LSBzdHJfcmVtb3ZlX2FsbChwaHJhc2Vfc3R1cGlkZSwgIiBbOmxldHRlcjpdezEsMn0gIikNCg0KcGhyYXNlX2RlY29uc3RydWl0ZSA8LSBzdHJfcmVtb3ZlX2FsbChwaHJhc2Vfc3R1cGlkZSwgIig/PD0gKVs6bGV0dGVyOl17MSwyfSg/PSApIikNCmBgYA0KDQpNYWludGVuYW50LCB2b3VzIGFsbGV6IHBvdXZvaXIgdG91dCBjb21wcmVuZHJlIGF1IG5ldHRveWFnZSBkZXMgZG9ubsOpZXMgcXVpIHN1aXQuIE91IHBldXQtw6p0cmUgcGFzLi4uDQoNCiMjIE5ldHRveWFnZSBkZXMgcsOpZsOpcmVuY2VzDQoNCkxhIHByZW1pw6hyZSBjaG9zZSDDoCBmYWlyZSBlc3QgZGUgY3LDqWVyIHVuIGlkZW50aWZpYW50IHBvdXIgbGVzIGFydGljbGVzIGNpdGFudCwgY2UgcXVpIG5vdXMgcGVybWV0dHJhIGRlIGZhaXJlIGxlIHBvbnQgZW50cmUgbGVzIGFydGljbGVzIGNpdGFudCBldCBsZXMgcsOpZsOpcmVuY2VzIGNpdMOpZXM6DQoNCmBgYHtyfQ0KZGF0YSA8LSBkYXRhICU+JSANCiAgbXV0YXRlKGNpdGluZ19pZCA9IHBhc3RlMCgiQSIsIDE6bigpKSkNCmBgYA0KDQpFeHRyYXlvbnMgZCdhYm9yZCBsYSBjb2xvbm5lIGByZWZlcmVuY2VzYCBlbiBnYXJkYW50IHVuIGlkZW50aWZpYW50IHBvdXIgbCdhcnRpY2xlIGNpdGFudC4gTm91cyBtZXR0b25zIGNoYXF1ZSByw6lmw6lyZW5jZSBjaXTDqWUgcGFyIHVuIGFydGljbGUgc3VyIHVuZSBsaWduZSBzw6lwYXLDqWUsIGVuIHV0aWxpc2FudCBsZSBmYWl0IHF1ZSBsZXMgcsOpZsOpcmVuY2VzIHNvbnQgc8OpcGFyw6llcyBwYXIgdW4gcG9pbnQtdmlyZ3VsZS4gTm91cyBjcsOpb25zIHVuIGlkZW50aWZpYW50IHBvdXIgY2hhcXVlIHLDqWbDqXJlbmNlLg0KDQpgYGB7ciBleHRyYWN0LXJlZmVyZW5jZXN9DQojJyAjIyBFeHRyYWN0aW5nIGFuZCBjbGVhbmluZyByZWZlcmVuY2VzDQpyZWZlcmVuY2VzX2V4dHJhY3QgPC0gZGF0YSAlPiUgDQogIGZpbHRlcighIGlzLm5hKHJlZmVyZW5jZXMpKSAlPiUgIyBlbmxldmVyIGxlcyBhcnRpY2xlcyBxdWkgbidvbnQgcGFzIGRlIHLDqWbDqXJlbmNlcw0KICBzZWxlY3QoY2l0aW5nX2lkLCByZWZlcmVuY2VzKSAlPiUgDQogIHNlcGFyYXRlX3Jvd3MocmVmZXJlbmNlcywgc2VwID0gIjsgIikgJT4lICMgb24gbWV0IHVuZSByw6lmw6lyZW5jZSBwYXIgbGlnbmUNCiAgbXV0YXRlKGlkX3JlZiA9IDE6bigpKSAlPiUgIyBvbiBkb25uZSB1biBpZGVudGlmaWFudCBhdXggcsOpZsOpcmVuY2VzDQogIGFzX3RpYmJsZQ0KDQpyZWZlcmVuY2VzX2V4dHJhY3QNCmBgYA0KDQpOb3RyZSBidXQgZMOpc29ybWFpcyBlc3QgZGUgcG91dm9pciBleHRyYWlyZSBsZXMgbcOpdGFkb25uw6llcyBkZSBjaGFxdWUgcsOpZsOpcmVuY2U6IGxlcyBhdXRldXJzLCBsYSBkYXRlIGRlIHB1YmxpY2F0aW9uLCBsZSB0aXRyZSwgbGUgam91cm5hbCwgZXRjLi4uIEwnb2JqZWN0aWYgZXN0IGQnYXZvaXIgZGVzIGluZm9ybWF0aW9ucyByZWxhdGl2ZW1lbnQgc3RhbmRhcmRpc8OpZXMsIHBvdXIgcG91dm9pciBlbnN1aXRlIGlkZW50aWZpZXIgcXVlbGxlcyBzb250IGxlcyByw6lmw6lyZW5jZXMgcXVpIHNvbnQgbGVzIG3Dqm1lcy4gVW5lIHByZW1pw6hyZSDDqXRhcGUgZXN0IGQnZXh0cmFpcmUgbGVzIGF1dGV1cnMgYWluc2kgcXVlIGwnYW5uw6llIGRlIHB1YmxpY2F0aW9uLiBPbiBlbmzDqHZlIGxlcyBhdXRldXJzIGRlIGxhIHLDqWbDqXJlbmNlIHByaW5jaXBhbGUgZXQgb24gc3RvY2sgbCdpbmZvIHJlc3RhbnRlLCBxdSdvbiB2YSBuZXR0b3llciwgZGFucyB1bmUgY29sb25uZSBzw6lwYXLDqWUuDQoNCmBgYHtyIHVzZWZ1bC1yZWdleH0NCmV4dHJhY3RfYXV0aG9ycyA8LSAiLipbOnVwcGVyOl1bOmFscGhhOl0rKCBKciguKT8pPywgKFtBLVpdXFwuWyAtXT8pPyhbQS1aXVxcLlsgLV0/KT8oW0EtWl1cXC4pP1tBLVpdXFwuIg0KZXh0cmFjdF95ZWFyX2JyYWNrZXRzIDwtICIoPzw9XFwoKVxcZHs0fSg/PVxcKSkiDQpleHRyYWN0X3BhZ2VzIDwtICIoPzw9IChwKT9wXFwuICkoW0EtWl0pP1xcZCsoLShbQS1aXSk/XFxkKyk/Ig0KZXh0cmFjdF92b2x1bWVfYW5kX251bWJlciA8LSAiKD88PSggfF4pPylcXGQrIFxcKFxcZCsoLVxcZCspP1xcKSINCg0KY2xlYW5pbmdfcmVmZXJlbmNlcyA8LSByZWZlcmVuY2VzX2V4dHJhY3QgJT4lIA0KICBtdXRhdGUoYXV0aG9ycyA9IHN0cl9leHRyYWN0KHJlZmVyZW5jZXMsIHBhc3RlMChleHRyYWN0X2F1dGhvcnMsICIoPz0sICkiKSksDQogICAgICAgICByZW1haW5pbmdfcmVmID0gc3RyX3JlbW92ZShyZWZlcmVuY2VzLCBwYXN0ZTAoZXh0cmFjdF9hdXRob3JzLCAiLCAiKSksICMgY2xlYW5pbmcgZnJvbSBhdXRob3JzDQogICAgICAgICBpc19hcnRpY2xlID0gISBzdHJfZGV0ZWN0KHJlbWFpbmluZ19yZWYsICJeXFwoWzpkaWdpdDpdezR9IiksIA0KICAgICAgICAgeWVhciA9IHN0cl9leHRyYWN0KHJlZmVyZW5jZXMsIGV4dHJhY3RfeWVhcl9icmFja2V0cykgJT4lIGFzLmludGVnZXIpDQoNCmNsZWFuaW5nX3JlZmVyZW5jZXMNCmBgYA0KDQpBIHBhcnRpciBkZSBtYWludGVuYW50LCBpbCB5IGEgYmVhdWNvdXAgdHJvcCBkJ8OpdGFwZXMgcG91ciBwb3V2b2lyIHRvdXQgZMOpdGFpbGxlciwgbWFpcyBsYSBzdHJhdMOpZ2llIGfDqW7DqXJhbGUgZXN0IGRlIHPDqXBhcmVyIGxlcyByw6lmw6lyZW5jZXMgc3VpdmFudCBsYSBwb3NpdGlvbiBkZSBsJ2FubsOpZSBkZSBwdWJsaWNhdGlvbjogbGUgcGx1cyBzaW1wbGUgw6AgbmV0dG95ZXIgKGBjbGVhbmluZ19hcnRpY2xlc2ApIGVzdCBxdWFuZCBsYSB0aXRyZSBlc3QgYXZhbnQgbGEgZGF0ZSBkZSBwdWJsaWNhdGlvbiBjYXIgY2VsYSBwZXJtZXQgZCdleHRyYWlyZSBwbHVzIHNpbXBsZW1lbnQgbGUgdGl0cmUsIGV0IGRvbmMgYXVzc2ksIGVuIGfDqW7DqXJhbCwgbGUgbm9tIGR1IGpvdXJuYWwgcXVpIHZhIHN1aXZyZSBsYSBkYXRlIGRlIHB1YmxpY2F0aW9uLiBPbiBleHRyYWl0IMOpZ2FsZW1lbnQgZGVzIGluZm9ybWF0aW9ucyB1dGlsZXMgY29tbWUgbGUgdm9sdW1lLCBsZSBudW3DqXJvLCBsZXMgcGFnZXMsIGV0IGxlIERPSS4NCg0KYGBge3IgY2xlYW4tYXJ0aWNsZXN9DQpjbGVhbmluZ19hcnRpY2xlcyA8LSBjbGVhbmluZ19yZWZlcmVuY2VzICU+JSANCiAgZmlsdGVyKGlzX2FydGljbGUgPT0gVFJVRSkgJT4lIA0KICBtdXRhdGUodGl0bGUgPSBzdHJfZXh0cmFjdChyZW1haW5pbmdfcmVmLCAiLiooPz1cXChcXGR7NH0pIiksICMgcHJlIGRhdGUgZXh0cmFjdGlvbg0KICAgICAgICAgam91cm5hbF90b19jbGVhbiA9IHN0cl9leHRyYWN0KHJlbWFpbmluZ19yZWYsICIoPzw9XFxkezR9XFwpKS4qIiksICMgcG9zdCBkYXRlIGV4dHJhY3Rpb24NCiAgICAgICAgIGpvdXJuYWxfdG9fY2xlYW4gPSBzdHJfcmVtb3ZlKGpvdXJuYWxfdG9fY2xlYW4sICJeLCIpICU+JSBzdHJfdHJpbSgiYm90aCIpLCAjIGNsZWFuaW5nIGEgYml0IHRoZSBqb3VybmFsIGluZm8gY29sdW1uDQogICAgICAgICBwYWdlcyA9IHN0cl9leHRyYWN0KGpvdXJuYWxfdG9fY2xlYW4sIGV4dHJhY3RfcGFnZXMpLCAjIGV4dHJhY3RpbmcgcGFnZXMNCiAgICAgICAgIHZvbHVtZV9hbmRfbnVtYmVyID0gc3RyX2V4dHJhY3Qoam91cm5hbF90b19jbGVhbiwgZXh0cmFjdF92b2x1bWVfYW5kX251bWJlciksICMgZXh0cmFjdGluZyBzdGFuZGFyZCB2b2x1bWVudCBhbmQgbnVtYmVyOiBYIChYKQ0KICAgICAgICAgam91cm5hbF90b19jbGVhbiA9IHN0cl9yZW1vdmUoam91cm5hbF90b19jbGVhbiwgIiAocCk/cFxcLiAoW0EtWl0pP1xcZCsoLShbQS1aXSk/XFxkKyk/IiksICMgY2xlYW4gZnJvbSBleHRyYWN0ZWQgcGFnZXMNCiAgICAgICAgIGpvdXJuYWxfdG9fY2xlYW4gPSBzdHJfcmVtb3ZlKGpvdXJuYWxfdG9fY2xlYW4sICIoIHxeKT9cXGQrIFxcKFxcZCsoLVxcZCspP1xcKSIpLCAjIGNsZWFuIGZyb20gZXh0cmFjdGVkIHZvbHVtZSBhbmQgbnVtYmVyDQogICAgICAgICB2b2x1bWVfYW5kX251bWJlciA9IGlmZWxzZShpcy5uYSh2b2x1bWVfYW5kX251bWJlciksIHN0cl9leHRyYWN0KGpvdXJuYWxfdG9fY2xlYW4sICIoPzw9ICkoW0EtWl0pP1xcZCsoLVxcZCspPyIpLCB2b2x1bWVfYW5kX251bWJlciksICMgZXh0cmFjdCByZW1haW5pbmcgbnVtYmVycw0KICAgICAgICAgam91cm5hbF90b19jbGVhbiA9IHN0cl9yZW1vdmUoam91cm5hbF90b19jbGVhbiwgIiAoW0EtWl0pP1xcZCsoLVxcZCspPyIpLCAjIGNsZWFuIGZyb20gcmVtYWluaW5nIG51bWJlcnMNCiAgICAgICAgIGpvdXJuYWwgPSBzdHJfcmVtb3ZlX2FsbChqb3VybmFsX3RvX2NsZWFuLCAiXls6cHVuY3Q6XSsoICk/WzpwdW5jdDpdKyggKT98KD88PSwoICk/KVs6cHVuY3Q6XSsoICk/KFs6cHVuY3Q6XSk/fFs6cHVuY3Q6XSggKT9bOnB1bmN0Ol0rKCApPyQiKSwgIyBleHRyYWN0IGpvdXJuYWwgaW5mbyBieSByZW1vdmluZyBpbmFwcHJvcHJpYXRlIHB1bmN0dWF0aW9ucw0KICAgICAgICAgZmlyc3RfcGFnZSA9IHN0cl9leHRyYWN0KHBhZ2VzLCAiXFxkKyIpLA0KICAgICAgICAgdm9sdW1lID0gc3RyX2V4dHJhY3Qodm9sdW1lX2FuZF9udW1iZXIsICJcXGQrIiksDQogICAgICAgICBpc3N1ZSA9IHN0cl9leHRyYWN0KHZvbHVtZV9hbmRfbnVtYmVyLCAiKD88PVxcKClcXGQrKD89XFwpKSIpLA0KICAgICAgICAgcHVibGlzaGVyID0gaWZlbHNlKGlzLm5hKGZpcnN0X3BhZ2UpICYgaXMubmEodm9sdW1lKSAmIGlzLm5hKGlzc3VlKSAmICEgc3RyX2RldGVjdChqb3VybmFsLCAiKFd8dylvcmtpbmcgKFB8cCk/YXBlciIpLCBqb3VybmFsLCBOQSksDQogICAgICAgICBib29rX3RpdGxlID0gaWZlbHNlKHN0cl9kZXRlY3Qoam91cm5hbCwgIiAoRXxlKWQocyk/XFwufCAoRXxlKWRpdGUoZHx1cnMpPyAiKSwgam91cm5hbCwgTkEpLCAjIEluY29sbGVjdGlvbiBhcnRpY2xlOiBUaXRsZSBvZiB0aGUgYm9vayBoZXJlDQogICAgICAgICBib29rX3RpdGxlID0gc3RyX2V4dHJhY3QoYm9va190aXRsZSwgIltBLXogXSsoPz0sKSIpLCAjIGtlZXBpbmcgb25seSB0aGUgdGl0bGUgb2YgdGhlIGJvb2sNCiAgICAgICAgIHB1Ymxpc2hlciA9IGlmZWxzZSghaXMubmEoYm9va190aXRsZSksIE5BLCBwdWJsaXNoZXIpLCAjIGlmIHdlIGhhdmUgYW4gaW5jb2xsZWN0aW9uIGFydGljbGUsIHRoYXQncyBub3QgYSBib29rLCBzbyBubyBwdWJsaXNoZXINCiAgICAgICAgIGpvdXJuYWwgPSBpZmVsc2UoIWlzLm5hKGJvb2tfdGl0bGUpIHwgISBpcy5uYShwdWJsaXNoZXIpLCBOQSwgam91cm5hbCksICMgcmVtb3Zpbmcgam91cm5hbCBhcyB3aGF0IHdlIGhhdmUgaXMgYSBib29rDQogICAgICAgICBwdWJsaXNoZXIgPSBpZmVsc2UoaXMubmEocHVibGlzaGVyKSAmIHN0cl9kZXRlY3Qoam91cm5hbCwgIihXfHcpb3JraW5nIChQfHApP2FwZXIiKSwgam91cm5hbCwgcHVibGlzaGVyKSwgIyBhZGRpbmcgd29ya2luZyBwYXBlciBwdWJsaXNoZXIgaW5mb3JtYXRpb24gaW4gcHVibGlzaGVyIGNvbHVtbg0KICAgICAgICAgam91cm5hbCA9IGlmZWxzZShzdHJfZGV0ZWN0KGpvdXJuYWwsICIoV3x3KW9ya2luZyAoUHxwKT9hcGVyIiksICJXb3JraW5nIFBhcGVyIiwgam91cm5hbCkpDQoNCmNsZWFuZWRfYXJ0aWNsZXMgPC0gY2xlYW5pbmdfYXJ0aWNsZXMgJT4lIA0KICBzZWxlY3QoY2l0aW5nX2lkLCBpZF9yZWYsIGF1dGhvcnMsIHllYXIsIHRpdGxlLCBqb3VybmFsLCB2b2x1bWUsIGlzc3VlLCBwYWdlcywgZmlyc3RfcGFnZSwgYm9va190aXRsZSwgcHVibGlzaGVyLCByZWZlcmVuY2VzKQ0KDQpjbGVhbmluZ19ub25fYXJ0aWNsZXMgPC0gY2xlYW5pbmdfcmVmZXJlbmNlcyAlPiUgDQogIGZpbHRlcihpc19hcnRpY2xlID09IEZBTFNFKSAlPiUgDQogIG11dGF0ZShyZW1haW5pbmdfcmVmID0gc3RyX3JlbW92ZShyZW1haW5pbmdfcmVmLCAiXFwoXFxkezR9XFwpKCwpPyAiKSwNCiAgICAgICAgIHRpdGxlID0gc3RyX2V4dHJhY3QocmVtYWluaW5nX3JlZiwgIi4qKD89LCAsKSIpLA0KICAgICAgICAgcGFnZXMgPSBzdHJfZXh0cmFjdChyZW1haW5pbmdfcmVmLCAiKD88PSAocCk/cFxcLiApKFtBLVpdKT9cXGQrKC0oW0EtWl0pP1xcZCspPyIpLCAjIGV4dHJhY3RpbmcgcGFnZXMNCiAgICAgICAgIHZvbHVtZV9hbmRfbnVtYmVyID0gc3RyX2V4dHJhY3QocmVtYWluaW5nX3JlZiwgIig/PD0oIHxeKT8pXFxkKyBcXChcXGQrKC1cXGQrKT9cXCkiKSwgIyBleHRyYWN0aW5nIHN0YW5kYXJkIHZvbHVtZW50IGFuZCBudW1iZXI6IFggKFgpDQogICAgICAgICByZW1haW5pbmdfcmVmID0gc3RyX3JlbW92ZShyZW1haW5pbmdfcmVmLCAiIChwKT9wXFwuIChbQS1aXSk/XFxkKygtKFtBLVpdKT9cXGQrKT8iKSwgIyBjbGVhbiBmcm9tIGV4dHJhY3RlZCBwYWdlcw0KICAgICAgICAgcmVtYWluaW5nX3JlZiA9IHN0cl9yZW1vdmVfYWxsKHJlbWFpbmluZ19yZWYsICIuKiwgLCIpLCAjIGNsZWFuIGRhdGVzIGFuZCBhbHJlYWR5IGV4dHJhY3RlZCB0aXRsZXMNCiAgICAgICAgIHJlbWFpbmluZ19yZWYgPSBzdHJfcmVtb3ZlKHJlbWFpbmluZ19yZWYsICIoIHxeKT9cXGQrIFxcKFxcZCsoLVxcZCspP1xcKSIpLCAjIGNsZWFuIGZyb20gZXh0cmFjdGVkIHZvbHVtZSBhbmQgbnVtYmVyDQogICAgICAgICB2b2x1bWVfYW5kX251bWJlciA9IGlmZWxzZShpcy5uYSh2b2x1bWVfYW5kX251bWJlciksIHN0cl9leHRyYWN0KHJlbWFpbmluZ19yZWYsICIoPzw9ICkoW0EtWl0pP1xcZCsoLVxcZCspPyIpLCB2b2x1bWVfYW5kX251bWJlciksICMgZXh0cmFjdCByZW1haW5pbmcgbnVtYmVycw0KICAgICAgICAgcmVtYWluaW5nX3JlZiA9IHN0cl9yZW1vdmUocmVtYWluaW5nX3JlZiwgIiAoW0EtWl0pP1xcZCsoLVxcZCspPyIpLCAjIGNsZWFuIGZyb20gcmVtYWluaW5nIG51bWJlcnMNCiAgICAgICAgIGpvdXJuYWwgPSBpZmVsc2Uoc3RyX2RldGVjdChyZW1haW5pbmdfcmVmLCAiKFd8dylvcmtpbmcgKFB8cClhcGVyIiksICJXb3JraW5nIFBhcGVyIiwgTkEpLA0KICAgICAgICAgam91cm5hbCA9IGlmZWxzZShzdHJfZGV0ZWN0KHJlbWFpbmluZ19yZWYsICIoTXxtKWFudXNjcmlwdCIpLCAiTWFudXNjcmlwdCIsIGpvdXJuYWwpLA0KICAgICAgICAgam91cm5hbCA9IGlmZWxzZShzdHJfZGV0ZWN0KHJlbWFpbmluZ19yZWYsICIoTXxtKWltZW8iKSwgIk1pbWVvIiwgam91cm5hbCksDQogICAgICAgICBwdWJsaXNoZXIgPSBpZmVsc2UoaXMubmEoam91cm5hbCksIHJlbWFpbmluZ19yZWYsIE5BKSAlPiUgc3RyX3RyaW0oImJvdGgiKSwNCiAgICAgICAgIGZpcnN0X3BhZ2UgPSBzdHJfZXh0cmFjdChwYWdlcywgIlxcZCsiKSwNCiAgICAgICAgIHZvbHVtZSA9IHN0cl9leHRyYWN0KHZvbHVtZV9hbmRfbnVtYmVyLCAiXFxkKyIpLA0KICAgICAgICAgaXNzdWUgPSBzdHJfZXh0cmFjdCh2b2x1bWVfYW5kX251bWJlciwgIig/PD1cXCgpXFxkKyg/PVxcKSkiKSwNCiAgICAgICAgIGJvb2tfdGl0bGUgPSBOQSkgIyB0byBiZSBzeW1ldHJpYyB3aXRoICJjbGVhbmVkX2FydGljbGVzIg0KDQpjbGVhbmVkX25vbl9hcnRpY2xlcyA8LSBjbGVhbmluZ19ub25fYXJ0aWNsZXMgJT4lIA0KICBzZWxlY3QoY2l0aW5nX2lkLCBpZF9yZWYsIGF1dGhvcnMsIHllYXIsIHRpdGxlLCBqb3VybmFsLCB2b2x1bWUsIGlzc3VlLCBwYWdlcywgZmlyc3RfcGFnZSwgYm9va190aXRsZSwgcHVibGlzaGVyLCByZWZlcmVuY2VzKQ0KDQojIG1lcmdpbmcgdGhlIHR3byBmaWxlcy4NCmNsZWFuZWRfcmVmIDwtIHJiaW5kKGNsZWFuZWRfYXJ0aWNsZXMsIGNsZWFuZWRfbm9uX2FydGljbGVzKQ0KDQojJyBOb3cgd2UgaGF2ZSBhbGwgdGhlIHJlZmVyZW5jZXMsIHdlIGNhbiBkbyBhIGJpdCBvZiBjbGVhbmluZyBvbiB0aGUgYXV0aG9ycyBuYW1lLA0KIycgYW5kIGV4dHJhY3QgdXNlZnVsIGluZm9ybWF0aW9uLCBsaWtlIERPSSwgZm9yIG1hdGNoaW5nIGxhdGVyLg0KDQpjbGVhbmVkX3JlZiA8LSBjbGVhbmVkX3JlZiAlPiUgDQogIG11dGF0ZShhdXRob3JzID0gc3RyX3JlbW92ZShhdXRob3JzLCAiIEpyXFwuIiksICMgc3RhbmRhcmRpc2luZyBhdXRob3JzIG5hbWUgdG8gZmF2b3VyIG1hdGNoaW5nIGxhdGVyDQogICAgICAgICBhdXRob3JzID0gc3RyX3JlbW92ZShhdXRob3JzLCAiXlxcKFxcZHs0fVxcKShcXC4pPyggKT8iKSwNCiAgICAgICAgIGF1dGhvcnMgPSBzdHJfcmVtb3ZlKGF1dGhvcnMsICJeLCAiKSwNCiAgICAgICAgIGF1dGhvcnMgPSBpZmVsc2UoaXMubmEoYXV0aG9ycyksIHN0cl9leHRyYWN0KHJlZmVyZW5jZXMsICIuKls6dXBwZXI6XVxcLig/PSBcXGR7NH0pIiksIGF1dGhvcnMpLCAjIHNwZWNpZmljIGNhc2UNCiAgICAgICAgIGpvdXJuYWwgPSBzdHJfcmVtb3ZlKGpvdXJuYWwsICJbOnB1bmN0Ol0kIiksICMgcmVtb3ZlIHVubmVjZXNzYXJ5IHB1bmN0dWF0aW9ucyBhdCB0aGUgZW5kDQogICAgICAgICBkb2kgPSBzdHJfZXh0cmFjdChyZWZlcmVuY2VzLCAiKD88PURPSSg6KT8gKS4qfCg/PD1cXC9kb2lcXC5vcmdcXC8pLioiKSwNCiAgICAgICAgIHBpaSA9IHN0cl9leHRyYWN0KGRvaSwgIig/PD1QSUkgKS4qIiksDQogICAgICAgICBkb2kgPSBzdHJfcmVtb3ZlKGRvaSwgIiwuKiIpLCAjIGNsZWFuaW5nIGRvaQ0KICAgICAgICAgcGlpID0gc3RyX3JlbW92ZShwaWksICIsLioiKSwgIyBjbGVhbmluZyBwaWkNCiAgKQ0KDQpjbGVhbmVkX3JlZg0KYGBgDQoNCiMjIE1hdGNoZXIgbGVzIHLDqWbDqXJlbmNlcyBlbnNlbWJsZQ0KDQpSZWdhcmRvbnMgZGUgcGx1cyBwcsOqdCBsYSBwcmVtacOocmUgbGlnbmUgZGUgbm90cmUgdGFibGVhdSBkZXMgcsOpZsOpcmVuY2VzIHBvdXIgbWlldXggY29tcHJlbmRyZSBsZXMgcHJvYmzDqG1lcyBwb3RlbnRpZWxzOg0KDQpgYGB7cn0NCmNsZWFuZWRfcmVmWzEsXQ0KYGBgDQoNCk5vdHJlIGJ1dCBlc3QgZGUgdHJvdXZlciB0b3V0ZXMgbGVzIGNpdGF0aW9ucyBxdWkgcmVudm9pZW50IGF1IG3Dqm1lIGFydGljbGUuIEVuIGV4dHJheWFudCBsZXMgaW5mb3JtYXRpb25zIGRlIG1hbmnDqHJlIHN5c3TDqW1hdGlxdWUsIG9uIGV4Y2x1dCBsZSBwcm9ibMOobWUgZGVzICJzdHlsZXMiIGRlIGNpdGF0aW9uIGRpZmbDqXJlbnQuIEF1LWRlbMOgIGRlIGwnaW1wZXJmZWN0aW9uIGR1IG5ldHRveWFnZSBjaS1kZXNzdXMsIGlsIHJlc3RlIHBsdXNpZXVycyBwcm9ibMOobWVzIHF1aSBwZXV2ZW50IG5vdXMgZW1ww6pjaGVyIGRlIG1hdGNoZXIgbGVzIHLDqWbDqXJlbmNlcyBjb3JyZWN0ZW1lbnQ6DQoNCi0gSW5mb3JtYXRpb25zIG5vbi1yw6lmw6lyZW5jw6llczsNCi0gRXJyZXVyczsNCi0gRGlmZsOpcmVudGVzIG1hbmnDqHJlcyBkJ8OpY3JpcmUgbGUgam91cm5hbDsNCi0gVmFyaWF0aW9ucyBkYW5zIGwnw6ljcml0dXJlIGRlcyB0aXRyZXMgKHNvdXZlbnQgbGnDqSDDoCBsYSBwb25jdHVhdGlvbiBvdSBhdXggYXJ0aWNsZXMpOw0KLSBEaWZmw6lyZW5jZXMgZGFucyBsJ8OpY3JpdHVyZSBkZXMgbm9tcyAoc291dmVudCBhbHRlcm5hbmNlIGVudHJlIHVuZSBvdSBkZXV4IGluaXRpYWxlcykuDQoNCkxlIGNvbXByb21pcyBjb25zaXN0ZSDDoCBmYWlyZSBjb3JyZXNwb25kcmUgYXV0YW50IGRlIHZyYWlzIHBvc2l0aWZzIHF1ZSBwb3NzaWJsZSAocsOpZsOpcmVuY2VzIGlkZW50aXF1ZXMpIHRvdXQgZW4gw6l2aXRhbnQgZGUgZmFpcmUgY29ycmVzcG9uZHJlIGRlcyBmYXV4IHBvc2l0aWZzLCBjJ2VzdC3DoC1kaXJlIGRlcyByw6lmw6lyZW5jZXMgcXVpIG9udCBkZXMgaW5mb3JtYXRpb25zIGVuIGNvbW11biwgbWFpcyBxdWkgbmUgc29udCBlbiBmYWl0IHBhcyBsZXMgbcOqbWVzLiBQYXIgZXhlbXBsZSwgdW5lIGNvcnJlc3BvbmRhbmNlIGJhc8OpZSB1bmlxdWVtZW50IHN1ciBsZSBub20gZGVzIGF1dGV1cnMgZXQgbCdhbm7DqWUgZGUgcHVibGljYXRpb24gZXN0IHRyb3AgbGFyZ2UsIGNhciBjZXMgYXV0ZXVycyBwZXV2ZW50IGF2b2lyIHB1Ymxpw6kgcGx1c2lldXJzIGFydGljbGVzIGF1IGNvdXJzIGRlIGxhIG3Dqm1lIGFubsOpZS4gVm9pY2kgcGx1c2lldXJzIGZhw6dvbnMgZCdpZGVudGlmaWVyIHVuZSByw6lmw6lyZW5jZSBjb21tdW5lIHF1aSBjb21wb3J0ZW50IHRyw6hzIHBldSBkZSByaXNxdWVzIGRlIGZhaXJlIGNvcnJlc3BvbmRyZSBkZXMgcsOpZsOpcmVuY2VzIGRpZmbDqXJlbnRlcyA6DQoNCi0gbcOqbWUgbm9tIGRlIGZhbWlsbGUgZHUgcHJlbWllciBhdXRldXIgb3UgZGVzIGF1dGV1cnMsIGFubsOpZSwgdm9sdW1lIGV0IHBhZ2UgKGNlIHNvbnQgbGVzIHBsdXMgc8O7cmVzKSA6IGFwcGVsb25zLWxlcyBgZmF5dnBgICYgYGF5dnBgIDsNCi0gbcOqbWUgam91cm5hbCwgdm9sdW1lLCBudW3DqXJvIGV0IHByZW1pw6hyZSBwYWdlIDogYGp2aXBgIDsNCi0gbcOqbWUgYXV0ZXVyLCBhbm7DqWUgZXQgdGl0cmUgOiBgYXl0YCA7DQotIG3Dqm1lIHRpdHJlLCBtw6ptZSBhbm7DqWUgZXQgbcOqbWUgcHJlbWnDqHJlIHBhZ2UgOiBgdHlwYCA7DQotIG3Dqm1lIERvaSBvdSBQSUkuDQoNCk5vdXMgZXh0cmF5b25zIGxlIG5vbSBkZSBmYW1pbGxlIGR1IHByZW1pZXIgYXV0ZXVyIHBvdXIgZmF2b3Jpc2VyIGxhIGNvcnJlc3BvbmRhbmNlIGNhciBpbCB5IGEgcGx1cyBkZSBwb3NzaWJpbGl0w6lzIGRlIHBldGl0ZXMgZGlmZsOpcmVuY2VzIHBvdXIgcGx1c2lldXJzIGF1dGV1cnMgcXVpIG5vdXMgZW1ww6pjaGVyYWllbnQgZGUgZmFpcmUgY29ycmVzcG9uZHJlIGRlcyByw6lmw6lyZW5jZXMgc2ltaWxhaXJlcy4NCg0KYGBge3IgZXh0cmFjdGluZy1maXJzdC1hdXRob3J9DQpjbGVhbmVkX3JlZiA8LSBjbGVhbmVkX3JlZiAlPiUNCiAgbXV0YXRlKGZpcnN0X2F1dGhvciA9IHN0cl9leHRyYWN0KGF1dGhvcnMsICJeW1s6YWxwaGE6XStbJ10/WyAtXT9dKywgKFtBLVpdXFwuWyAtXT8pPyhbQS1aXVxcLlsgLV0/KT8oW0EtWl1cXC4pP1tBLVpdXFwuKD89KCx8JCkpIiksDQogICAgICAgICBmaXJzdF9hdXRob3Jfc3VybmFtZSA9IHN0cl9leHRyYWN0KGZpcnN0X2F1dGhvciwgIi4qKD89LCkiKSwNCiAgICAgICAgIGFjcm9zcyguY29scyA9IGMoImF1dGhvcnMiLCAiZmlyc3RfYXV0aG9yIiwgImpvdXJuYWwiLCAidGl0bGUiKSwgfnRvdXBwZXIoLikpKSANCg0KbWF0Y2hpbmdfcmVmIDwtIGZ1bmN0aW9uKGRhdGEsIGlkX3JlZiwgLi4uLCBjb2xfbmFtZSl7DQogIG1hdGNoIDwtIGRhdGEgJT4lIA0KICAgIGdyb3VwX2J5KC4uLikgJT4lIA0KICAgIG11dGF0ZShuZXdfaWQgPSBtaW4oe3tpZF9yZWZ9fSkpICU+JSANCiAgICBkcm9wX25hKC4uLikgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgc2VsZWN0KHt7aWRfcmVmfX0sIG5ld19pZCkgJT4lIA0KICAgIHJlbmFtZV93aXRoKH4gcGFzdGUwKGNvbF9uYW1lLCAiX25ld19pZCIpLCAuY29scyA9IG5ld19pZCkNCiAgDQogIGRhdGEgPC0gZGF0YSAlPiUgDQogICAgbGVmdF9qb2luKG1hdGNoKQ0KfQ0KDQppZGVudGlmeWluZ19yZWYgPC0gY2xlYW5lZF9yZWYgJT4lDQogIG1hdGNoaW5nX3JlZihpZF9yZWYsIGZpcnN0X2F1dGhvcl9zdXJuYW1lLCB5ZWFyLCB0aXRsZSwgY29sX25hbWUgPSAiZmF5dCIpICU+JSANCiAgbWF0Y2hpbmdfcmVmKGlkX3JlZiwgam91cm5hbCwgdm9sdW1lLCBpc3N1ZSwgZmlyc3RfcGFnZSwgY29sX25hbWUgPSAianZpcCIpICU+JSANCiAgbWF0Y2hpbmdfcmVmKGlkX3JlZiwgYXV0aG9ycywgeWVhciwgdm9sdW1lLCBmaXJzdF9wYWdlLCBjb2xfbmFtZSA9ICJheXZwIikgJT4lIA0KICBtYXRjaGluZ19yZWYoaWRfcmVmLCBmaXJzdF9hdXRob3Jfc3VybmFtZSwgeWVhciwgdm9sdW1lLCBmaXJzdF9wYWdlLCBjb2xfbmFtZSA9ICJmYXl2cCIpICU+JQ0KICBtYXRjaGluZ19yZWYoaWRfcmVmLCB0aXRsZSwgeWVhciwgZmlyc3RfcGFnZSwgY29sX25hbWUgPSAidHlwIikgJT4lIA0KICBtYXRjaGluZ19yZWYoaWRfcmVmLCBwaWksIGNvbF9uYW1lID0gInBpaSIpICU+JSANCiAgbWF0Y2hpbmdfcmVmKGlkX3JlZiwgZG9pLCBjb2xfbmFtZSA9ICJkb2kiKSANCg0KVmlldyhpZGVudGlmeWluZ19yZWYpDQpgYGANCg0KTm91cyBhdm9ucyBtYWludGVuYW50IG5vdHJlIHRhYmxlYXUgZGUgY2l0YXRpb25zIGRpcmVjdGVzIHJlbGlhbnQgbGVzIGFydGljbGVzIGNpdGFudCBhdXggcsOpZsOpcmVuY2VzLiBOb3VzIGF2b25zIGF1dGFudCBkZSBsaWduZXMgcXVlIGxlIG5vbWJyZSBkZSBjaXRhdGlvbnMgcGFyIGxlcyBhcnRpY2xlcyBjaXRhbnQuIExlcyBkZXV4IHByZW1pw6hyZXMgY29sb25uZXMsIHNvbnQgbGVzIHNldWxlcyBjb2xvbm5lcyBuw6ljZXNzYWlyZXMgcG91ciBjb25zdHJ1aXJlIGxlcyBsaWVucyBkZSBub3RyZSByw6lzZWF1IHRvdXQgw6AgbCdoZXVyZS4NCg0KYGBge3IgY3JlYXRpbmctZGlyZWN0LWNpdGF0aW9ufQ0KZGlyZWN0X2NpdGF0aW9uIDwtIGlkZW50aWZ5aW5nX3JlZiAlPiUgIA0KICBtdXRhdGUobmV3X2lkX3JlZiA9IHNlbGVjdCguLCBlbmRzX3dpdGgoIm5ld19pZCIpKSAlPiUgIHJlZHVjZShwbWluLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgbmV3X2lkX3JlZiA9IGlmZWxzZShpcy5uYShuZXdfaWRfcmVmKSwgaWRfcmVmLCBuZXdfaWRfcmVmKSkgICU+JSANCiAgcmVsb2NhdGUobmV3X2lkX3JlZiwgLmFmdGVyID0gY2l0aW5nX2lkKSAlPiUgDQogIHNlbGVjdCgtaWRfcmVmICYgISBlbmRzX3dpdGgoIm5ld19pZCIpKQ0KDQpkaXJlY3RfY2l0YXRpb24NCmBgYA0KDQpOb3VzIHBvdXZvbnMgZXh0cmFpcmUgbGEgbGlzdGUgZGUgdG91dGVzIGxlcyByw6lmw6lyZW5jZXMgY2l0w6llcy4gTm91cyBhdm9ucyBhdXRhbnQgZGUgbGlnbmVzIHF1ZSBkZSByw6lmw6lyZW5jZXMgY2l0w6llcyBwYXIgbGVzIGFydGljbGVzIGNpdGFudCAoYydlc3Qtw6AtZGlyZSBxdSd1bmUgcsOpZsOpcmVuY2UgY2l0w6llIHBsdXNpZXVycyBmb2lzIG4nZXN0IHByw6lzZW50ZSBxdSd1bmUgc2V1bGUgZm9pcyBkYW5zIGxlIHRhYmxlYXUpLiBDb21tZSBwb3VyIGxlcyByw6lmw6lyZW5jZXMgYXBwYXJpw6llcyBlbnNlbWJsZSwgb24gcGV1dCBhdm9pciBkZXMgaW5mb3JtYXRpb25zIGRpZmbDqXJlbnRlcyAoZHVlcyBhdSBmYWl0IHF1ZSBsZXMgcsOpZsOpcmVuY2VzIG9udCDDqXTDqSBjaXTDqWVzIGRpZmbDqXJlbW1lbnQgc2Vsb24gbGVzIGFydGljbGVzIGNpdGFudCksIG5vdXMgcHJlbm9ucyB1bmUgbGlnbmUgb8O5IGwnaW5mb3JtYXRpb24gc2VtYmxlIMOqdHJlIGxhIHBsdXMgY29tcGzDqHRlLCBjJ2VzdC3DoC1kaXJlIGzDoCBvw7kgaWwgeSBhIGxlIG1vaW5zIGQnaW5mb3JtYXRpb24gbWFucXVhbnRlcyBkYW5zIGxlcyBtw6l0YWRvbm7DqWVzLg0KDQpgYGB7ciByZWZlcmVuY2VzLXRhYmxlfQ0KaW1wb3J0YW50X2luZm8gPC0gYygiYXV0aG9ycyIsDQogICAgICAgICAgICAgICAgICAgICJ5ZWFyIiwNCiAgICAgICAgICAgICAgICAgICAgInRpdGxlIiwNCiAgICAgICAgICAgICAgICAgICAgImpvdXJuYWwiLA0KICAgICAgICAgICAgICAgICAgICAidm9sdW1lIiwNCiAgICAgICAgICAgICAgICAgICAgImlzc3VlIiwNCiAgICAgICAgICAgICAgICAgICAgInBhZ2VzIiwNCiAgICAgICAgICAgICAgICAgICAgImJvb2tfdGl0bGUiLA0KICAgICAgICAgICAgICAgICAgICAicHVibGlzaGVyIikNCnJlZmVyZW5jZXMgPC0gZGlyZWN0X2NpdGF0aW9uICU+JSANCiAgbXV0YXRlKG5iX25hID0gcm93U3VtcyghaXMubmEoc2VsZWN0KC4sIGFsbF9vZihpbXBvcnRhbnRfaW5mbykpKSkpICU+JSANCiAgZ3JvdXBfYnkobmV3X2lkX3JlZikgJT4lIA0KICBzbGljZV9tYXgob3JkZXJfYnkgPSBuYl9uYSwgbiA9IDEsIHdpdGhfdGllcyA9IEZBTFNFKSAlPiUgDQogIHNlbGVjdCgtY2l0aW5nX2lkKSAlPiUgDQogIHVuaXF1ZQ0KYGBgDQoNCkxlcyBkb25uw6llcyBxdWUgdm91cyBhdmV6IGTDqXNvcm1haXMgbmV0dG95w6llcyB2b3VzIHBlcm1ldHRlbnQgZGUgZmFpcmUgZGVzIHByZW1pw6hyZXMgZXhwbG9yYXRpb25zIHN0YXRpc3RpcXVlcy4gUGFyIGV4ZW1wbGUsIG9uIHBldXQgcmVnYXJkZXIgcXVlbHMgc29udCBsZXMgdHJhdmF1eCBsZXMgcGx1cyBjaXTDqXM6DQoNCmBgYHtyfQ0KZGlyZWN0X2NpdGF0aW9uICU+JSANCiAgYWRkX2NvdW50KG5ld19pZF9yZWYpICU+JSANCiAgc2VsZWN0KG5ld19pZF9yZWYsIG4pICU+JSANCiAgdW5pcXVlKCkgJT4lIA0KICBzbGljZV9tYXgobiwgbiA9IDEwKSAlPiUNCiAgbGVmdF9qb2luKHNlbGVjdChyZWZlcmVuY2VzLCBuZXdfaWRfcmVmLCByZWZlcmVuY2VzKSkgJT4lIA0KICBzZWxlY3QocmVmZXJlbmNlcywgbikNCmBgYA0KDQoNCiMjIFByw6lwYXJlciBub2V1ZHMgZXQgbGllbnMgcG91ciBsZSByw6lzZWF1IGRlIGNvLWNpdGF0aW9uDQoNCklsIHMnYWdpdCBkw6lzb3JtYWlzIGRlIHByw6lwYXJlciBsZXMgZG9ubsOpZXMgcXVlIG5vdXMgYWxsb25zIHV0aWxpc2VyIGRhbnMgR2VwaGkgcG91ciBjb25zdHJ1aXJlIHVuIHLDqXNlYXUuIE9uIHZldXQgZmFpcmUgdW4gcsOpc2VhdSBkZSBjby1jaXRhdGlvbiwgZG9uYyBvbiBzJ2ludMOpcmVzc2UgYXV4IHLDqWbDqXJlbmNlcyBjaXTDqWVzIChsZSBjb3VwbGFnZSBiaWJsaW9ncmFwaGlxdWUgcydpbnTDqXJlc3NlIGF1eCBhcnRpY2xlcyBjaXRhbnQpLiBQb3VyIGxpbWl0ZXIgbGUgbm9tYnJlIGRlIG7Fk3Vkcywgb24gcGV1dCBjaG9pc2lyIGRlIG5lIHByZW5kcmUgcXVlIGxlcyBuxZN1ZHMgbGVzIHBsdXMgY2l0w6lzOg0KDQpgYGB7cn0NCmNpdGF0aW9ucyA8LSBkaXJlY3RfY2l0YXRpb24gJT4lIA0KICBhZGRfY291bnQobmV3X2lkX3JlZikgJT4lIA0KICBzZWxlY3QobmV3X2lkX3JlZiwgbikgJT4lIA0KICB1bmlxdWUNCg0Kbm9kZXMgPC0gcmVmZXJlbmNlcyAlPiUgDQogIGxlZnRfam9pbihjaXRhdGlvbnMpICU+JSANCiAgZmlsdGVyKG4gPj0gMTApDQoNCm5vZGVzDQpgYGANCg0KT24gdmEgZW5zdWl0ZSBjcsOpZXIgbGVzIGxpZW5zIMOgIHBhcnRpciBkZXMgY2l0YXRpb25zIGRpcmVjdGVzOiBwb3VyIGNoYXF1ZSByw6lmw6lyZW5jZSwgb24gcmVnYXJkZSBxdWVscyBhcnRpY2xlcyBsYSBjaXRlbnQuIEVuc3VpdGUsIG9uIGNoZXJjaGVyIHF1ZWxsZXMgYXV0cmVzIHLDqWbDqXJlbmNlcyBzb250IGNpdMOpZXMgcGFyIGxlcyBtw6ptZXMgYXJ0aWNsZXMuIEVuIGQnYXV0cmVzIG1vdHMsIHRvdXRlcyBsZXMgcsOpZsOpcmVuY2VzIGNpdMOpZXMgZGFucyB1bmUgbcOqbWUgYmlibGlvZ3JhcGhpZSAoZCd1biBhcnRpY2xlIGNpdGFudCkgdm9udCDDqnRyZSByZWxpw6llcyBlbnRyZSBlbGxlcy4gT24gdXRpbGlzZSB1bmUgbcOpdGhvZGUgcXVpIHBlcm1ldCBkZSBwb25kw6lyZXIgbGVzIGxpZW5zIGVuIGZvbmN0aW9uIGR1IG5vbWJyZSBkZSBmb2lzIG/DuSBjaGFxdWUgcsOpZsOpcmVuY2UgZXN0IGNpdMOpZTogZW4gZWZmZXQsIG9uIGNvbnNpZMOocmUgcXVlIHNpIGRldXggcsOpZsOpcmVuY2VzIGJlYXVjb3VwIGNpdMOpZXMgc29udCBjaXTDqWVzICprKiBmb2lzIGVuc2VtYmxlLCBjZSBsaWVuIGRvaXQgw6p0cmUgbW9pbnMgaW1wb3J0YW50IHF1ZSBkZXMgcsOpZsOpcmVuY2VzIHBldSBjaXTDqWVzLCBxdWkgc29udCBjaXTDqWVzIGxlIG3Dqm1lIG5vbWJyZSAqayogZGUgZm9pcyBlbnNlbWJsZS4NCg0KYGBge3J9DQpkaXJlY3RfY2l0YXRpb24gPC0gZGlyZWN0X2NpdGF0aW9uICU+JSANCiAgZmlsdGVyKG5ld19pZF9yZWYgJWluJSBub2RlcyRuZXdfaWRfcmVmKQ0KDQplZGdlcyA8LSBiaWJsaW9uZXR3b3JrOjpiaWJsaW9fY29jaXRhdGlvbihkaXJlY3RfY2l0YXRpb24sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNpdGluZ19pZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5ld19pZF9yZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0X3RocmVzaG9sZCA9IDUpDQoNCmVkZ2VzDQpgYGANCg0K