inhaltsanalyse-mit-r.de

In diesem zweiten Kapitel steht nun die Analyse vor Wörtern und Texten im Mittelpunkt. Auf den ersten Blick erscheinen die Metriken, die hier vorgestellt werden, möglicherweise nicht als besonders relevant für sozialwissenschaftliche Fragestellung. Das liegt zum einen daran, das wir uns an dieser Stelle noch nicht mit abstrakten Konzepten wie Themen oder Sentiment beschäftigen, die in den folgenden Kapitel im Mittelpunkt stehen werden, sondern mit Aspekten wie der Frequenz von Begriffen und der Ähnlichkeit von Texten, die augenscheinlich vielleicht der Linguistik näher sind. Wort- und Textmetriken sind aber aus zwei Gründen von Bedeutung: erstens bilden sie die Grundlage der höherstufigen Verfahren, egal ob Lexikon-, Themen- oder Sentimentanalyse, und zu anderen lassen sich auch schon mit ihnen interessante sozialwissenschaftliche Fragestellungen bearbeiten.

Einige Beispiele:

Diese und ähnliche Fragen werden in den folgenden Kapiteln aufgegriffen – zunächst werden aber die Funktionen vorgestellt, welche die Arbeit mit Wörtern und Texten in quanteda ermöglichen.

Installation und Laden der benötigten R-Bibliotheken, Erstellen des Korpus, Berechnen einer DFM

Zunächst werden wieder die notwendigen Bibliotheken geladen. Dann wird in einem zweiten Schritt das Sherlock-Korpus eingelesen und aufbereitet und dann Korpus-Statistiken berechnet. Schließlich erstellen wir wieder eine DFM (vgl. Kapitel 1), da wir diese später noch benötigen.

# Installation und Laden der Bibliotheken
if(!require("quanteda")) install.packages("quanteda")
Lade nötiges Paket: quanteda
Package version: 1.3.4
Parallel computing: 2 of 4 threads used.
See https://quanteda.io for tutorials and examples.

Attache Paket: ‘quanteda’

The following object is masked from ‘package:utils’:

    View
if(!require("readtext")) install.packages("readtext")
Lade nötiges Paket: readtext
if(!require("tidyverse")) install.packages("tidyverse")
Lade nötiges Paket: tidyverse
── Attaching packages ─────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.0.0     ✔ purrr   0.2.5
✔ tibble  1.4.2     ✔ dplyr   0.7.6
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
theme_set(theme_bw())
# Laden der Sherlock Holmes-Daten (bereits als RData-File gespeichert)
load("daten/sherlock/sherlock.korpus.RData")
# Berechnen einer DFM
meine.dfm <- dfm(korpus, remove_numbers = TRUE, remove_punct = TRUE, remove_symbols = TRUE, remove = stopwords("english"))

Korkordanzen erstellen

Zu den einfachsten Funktionen von quanteda gehört die Möglichkeit, Konkordanzen (auch KWIC genannt) zu erstellen, also die Textstelle eines Suchterms sowie dessen umgebenden Satzekontext zu extrahieren. Konkordanzen lassen sich in quanteda für einzelen Wörter, aber auch für Phrasen erzeugen. Oftmals ist der Export einer Konkodanz (etwa als CSV-Datei, die mit Excel geöffnet werden kann) neben der Darstellung innerhalb von R besonders nützlich. Dies geschieht hier mit der Funktion write_delim().

Anmerkung: Die Konkordanz kann mit dem kleinen Pfeil rechts oben gescrollt werden.

konkordanz <- kwic(korpus, "data")
konkordanz
konkordanz <- kwic(korpus, phrase("John|Mary [A-Z]+"), valuetype = "regex", case_insensitive = FALSE)
konkordanz
konkordanz <- kwic(korpus, c("log*", "emot*"), window = 10, case_insensitive = FALSE)
konkordanz
write_delim(konkordanz, path = "konkordanz.csv", delim = ";") # Datei ist Excel-kompatibel

Die Konkordanzen bestehen aus den Metadaten (Textname und Position), dem linken Kontext, dem Suchterm, sowie dem rechten Kontext. Die erste Konkordanz enthält alle Vorkommnisse des Begriffs ‘data’, die zweite alle Vorkommnisse der Namen ‘John’ und ‘Mary’ gefolgt von einem weiteren Wort in Großschreibung (i.d.R. der Nachname). Die dritte Konkordanz enthält schließlich die Wortfragmente ‘log’ und ‘emot’, also Wörter wie ‘logical’ und ‘emotional’, aber auch die Pluralform ‘emotions’. Strenggenommen handelt es sich hierbei nicht um Wortstämme, weil die Flexionsform bei unregelmäßigen Wörtern ganz vom Lemma abweicht (vgl. ‘go’ und ‘went’). In den meisten sozialwissenschaftlichen Anwendungsszenarien ist es aber bereits ausreichend, durch die Verwendung von Platzhaltern (*) verschieden Wortvarianten zu identifizieren. Hier bringt quanteda eine Reihe nützlicher Eigenschaften mit, die in der Dokumentation von kwic() genau beschrieben werden.

Als nächstes berechnen wir die Häufigkeit und Dispersion von Tokens pro Erzählung, welche die Begriffe ‘dark’ und ‘light’ enthalten.

term1 <- kwic(korpus, "dark", valuetype = "regex", case_insensitive = FALSE) %>% group_by(docname) %>% summarise(Treffer = n()) %>% mutate(Prozentanteil = Treffer/(korpus.stats$Tokens/100), Suchterm = "dark") %>% arrange(desc(Prozentanteil))
term2 <- kwic(korpus, "light", valuetype = "regex", case_insensitive = FALSE) %>% group_by(docname) %>% summarise(Treffer = n()) %>% mutate(Prozentanteil = Treffer/(korpus.stats$Tokens/100), Suchterm = "light") %>% arrange(desc(Prozentanteil))
term1
term2

Wieder wenden wir zunächst die Funktion kwic() an, allerdings hier in Kombination mit mehreren Funktionen aus dem Paket dplyr (tidyverse). Diese Funktionen haben nichts mit quanteda zu tun, sondern sind für die Umformung jeglicher Daten in R nützlich (wer mehr wissen möchte, sollte sich dieses Buch anschauen). Während zuvor einfach die resultierende Konkordanz ausgegeben wurde, wird das Ergebnis jetzt mit Hilfe der Funktionen group_by(), summarise(), mutate() und arrange() weiter verarbeitet. Dabei machen wir uns die Tatsache zunutze, dass in einem KWIC-Ergebnis bereits alle Informationen vorliegen, um die absolute und relative Frequenz eines Begriffs (hier ‘light’ und ‘dark’) in einer Reihe von Dokumenten zu berechnen. Den Prozentanteil haben wir dabei einfach mittels Dreisatz abgeleitet (mit Treffer/(korpus.stats$Tokens/100)).

Wortfrequenzen lassen sich allerdings wesentlich einfacher durch die quanteda-eigenen Funktion textstat_frequency() umsetzen, die wir folgend auch konsequent nutzen werden – auch dazu gleich noch etwas mehr.

Zunächst plotten wie die absolute und relativen Häufigkeit der beiden Begriffe.

terme.kombiniert <- bind_rows(term1, term2)
terme.kombiniert$docname <- factor(terme.kombiniert$docname, levels = levels(korpus.stats$Text))
ggplot(terme.kombiniert, aes(docname, Treffer, group = Suchterm, col = Suchterm)) + geom_line(size = 1) + theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) + ggtitle("Häufigkeit der Suchbegriffe \"dark\" und \"light\" pro Roman (absolut)") + xlab("Roman") + ylab("Wörter (gesamt)")

ggplot(terme.kombiniert, aes(docname, Prozentanteil, group = Suchterm, col = Suchterm)) + geom_line(size = 1) + theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) + ggtitle("Häufigkeit der Suchbegriffe \"dark\" und \"light\" pro Roman (relativ)") + xlab("Roman") + ylab("Wörter (%)")

Wir sehen zwei unterschiedliche Berechnungen: das erste Plot zeigt die absolute Häufigkeit der beiden Begriffe, das zweite hingegen den relativen Prozenzanteil des Begriffs an der Gesamtwortzahl des jeweiligen Romans. Wieso sind die beiden Plots nahezu identisch? Dies hat mit der relativ gleichen Wortanzahl der Romane untereinander zu tun (zwischen 8,500 und 12,000 Tokens). Sind zwei Teilkorpora von sehr unterschiedlicher Größe, ist eine Normalisierung der Wortfrequenzen extrem wichtig, da sonst die Ergebnisse massiv verzerrt werden. Auch so ergeben sich durchaus Unterschiede, wenn man etwa den Anteil von ‘The Adventure of the Speckled Band’ und ‘The Adventure of the Copper Beeches’ vergleicht. Während die Anszahl der absoluten Treffer auf ‘light’ in beiden Romanen identisch ist, fällt der relative Anteil bei ‘Speckled Band’ im Vergleich ab.

Was tun, wenn man sich weniger für die Häufigkeit als für die Position der Suchterme interessiert? Dazu kann das Plotten der Begiffsdispersion als ‘xray-plot’ nützlich sein, wozu die Funktion textplot_xray existiert. Die X-Achse stellt hierbei die Position innerhalb des Textes dar, an dem der Suchbegriff vorkommt.

textplot_xray(kwic(korpus, "dark", valuetype = "regex", case_insensitive = FALSE)) + ggtitle("Lexikalische Dispersion von \"dark\" in Sherlock Holmes")

textplot_xray(kwic(korpus, "light", valuetype = "regex", case_insensitive = FALSE)) + ggtitle("Lexikalische Dispersion von \"light\" in Sherlock Holmes")

Kollokationen

Wir gehen nun zu den sogenannten Textstatistken über. Dabei handelt es sich um Funktionen anhand derer sich Wörter und Texte mit Blick auf ihre Ähnlichkeit mit oder Distanz zu anderen Wörtern oder Texte analysieren lassen. Eine wichtige Funktion in diesem Zusammenhang ist die Extraktion von Kollokationen. Die Kollokate eines Begriffs sind solche Begriffe, die häufig gemeinsam mit dem Term vorkommen. Der Prozess ist dabei induktiv.

Folgend wenden wir die Funktion textstat_collocations() an, die häufige Kollokate im Sherlock Holmes-Korpus ermittelt. Anhand von write_delim wird das Ergebnis dann noch als Excel-kompatible CSV-Datei gespeichert.

meine.tokens <- tokens(korpus)
kollokationen <- textstat_collocations(meine.tokens, min_count = 10)
arrange(kollokationen, desc(count))
arrange(kollokationen, desc(lambda))
write_delim(kollokationen, path = "kollokationen.csv", delim = ";") # Datei ist Excel-kompatibel

In den beiden Tabellen zeigt uns collocation das Kollokat und count dessen absolute Häufigkeit an. Die Assoziationsstärker der Kollokation wird mit lambda und z gemessen (genauer ist z ein z-standardisiertes lambda). Lambda beschreibt dabei die Wahrscheinlichkeit, dass genau diese zwei Begriffe auf einander folgen, was insofern von der absoluten Häufigkeit zu differenzieren ist, als das diese nicht das auftreten eines Teilbegriffs mit allen anderen Wörtern im Korpus berücksichtigt.

Die beiden Tabellen illustrieren diesen Unterschied. Während die erste nach der absoluten Häufigkeit sortiert ist, so dass gängige Kollokate wie ‘of the’ oder ‘it is’ ganz vorne liegen, ist die zweite absteigend nach Lambda sortiert, so dass eine Reihe von Eigennamen wie ‘hosmer angel’ oder ‘briony lodge’ die Liste anführen. Praktisch betrachtet ist es meist sinnvoller, Eigennamen als Phrasen zu betrachten, statt als einzelne Begriffe, deren gemeinsames Auftreten wirklich etwas über den Text verrät. Echte Kollokate sind hingegen ‘no doubt’ oder ‘young lady’.

Wort- und Textähnlichkeit und -Distanz

Wie sich im ersten Kapitel bereits angedeutet hat, lassen sich auf Grundlage einer DFM zahlreiche Metriken berechnen, welche die Nähe und Distanz von Wörtern und Dokumenten zu einander relektieren. Dies geschieht mit textstat_simil(). Zunächst konstruieren wir dazu eine DFM, in der jeder Satz einem Dokument entspricht. Dies wird deshalb notwendig, weil sich Wortähnlichkeiten bei einer geringen Dokumentanzahl nicht besonders zuverlässig berechnen lassen, da Ähnlichkeit als Kookurenz innerhalb des gleichen Dokuments operationalisiert wird. Dann Berechnen wir die Wortähnlichkeit zum Begriff ‘love’ mittels [Kosinusdistanz](https://de.wikipedia.org/wiki/Kosinus-%C3%84hnlichkeit. Andere verfügbare Metriken sind ‘correlation’, ‘jaccard’, ‘eJaccard’, ‘dice’, ‘eDice’, ‘simple matching’, ‘hamann’ und ‘faith’, welche Wortähnlichkeit jeweils unterschiedlich operationalisieren.

korpus.saetze <- corpus_reshape(korpus, to = "sentences")
meine.dfm.saetze <- dfm(korpus.saetze, remove_numbers = TRUE, remove_punct = TRUE, remove_symbols = TRUE, remove = stopwords("english"))
meine.dfm.saetze <- dfm_trim(meine.dfm.saetze, min_docfreq = 5)
aehnlichkeit.woerter <- textstat_simil(meine.dfm.saetze, "love", margin = "features", method = "cosine")
head(aehnlichkeit.woerter[order(aehnlichkeit.woerter[,1], decreasing = T),], 10)
     love     loved     lover   working    ceased      wife   majesty   account   emotion     share 
1.0000000 0.2108185 0.1781742 0.1781742 0.1666667 0.1295048 0.1178511 0.1081476 0.1054093 0.1054093 

Analog zur Ähnlichkeit funktionert auch die Berechnung von Wortdistanzen mit der Funktion textstat_dist(). Auch hier haben wir wieder eine große Anzahl von Distanzmaßen zur Auswahl (‘euclidean’, ‘chisquared’, ‘chisquared2’, ‘hamming’, ‘kullback’. ‘manhattan’, ‘maximum’, ‘canberra’, ‘minkowski’).

distanz.woerter <- textstat_dist(meine.dfm.saetze, "love", margin = "features", method = "euclidean")
head(distanz.woerter[order(distanz.woerter[,1], decreasing = T),], 10)
    upon     said   holmes      one      man       mr   little      now      see      may 
23.60085 22.60531 21.47091 20.97618 18.22087 17.63519 17.34935 16.12452 15.71623 15.19868 

Was sagt das Ergebnis aus? Vor allem das (wenig überraschend) Wörter wie ‘upon’ und ‘said’ sehr weit von ‘love’ entfernt sind – allerdings nicht in dem Sinne, dass sie das logische Gegenteil von ‘love’ darstellen würden (in der Linguistik spricht man von Antonymie). Das liegt daran, dass diese Begriffe im Korpus nahezu gleich verteilt sind, also überall vorkommen. Mit dem Verfahren der Wortvektoren (welches wir hier nicht behandeln) und sehr großen Datenbeständen lassen sich allerdings auch solche und andere semantisch Beziehungen indentifizieren. Die Filterung, die wir zuvor an der DFM vorgenommen haben, schließt Begriffe aus, die vielleicht nie gemeinsam mit ‘love’ vorkommen, und insofern noch distanzierter wären, allerdings gibt es derer auch sehr viele. Zusammenfassend kann man sagen, dass Nähe- und Dinstanzmaße immer ausreichend viele Daten benötigen, um ein zuverlässiges Resultat liefern zu können, und dass der Suchterm selbst ausreichend oft vorkommen muss.

Wer die Dokumentation von textstat_simil() und textstat_dist() anschaut, wird feststellen, dass es dort den etwas kryptischen Hinweis auf das Argument ‘margin’ gibt. Dieses hat zwei mögliche erinstellungen: ‘documents’ oder ‘features’. Stellt man hier ‘documents’ ein, werden die besprochenen Metriken nicht auf Wörter, sondern auf Texte angewandt. Folgend plotten wir die Textnähe via Kosinusähnlichkeit (hier ausgehend vom ersten Roman, ‘A Case of Identity’).

aehnlichkeit.texte <- data.frame(Text = factor(korpus.stats$Text, levels = rev(korpus.stats$Text)), as.matrix(textstat_simil(meine.dfm, selection = 3, margin = "documents", method = "cosine")))
ggplot(aehnlichkeit.texte, aes(A.Case.of.Identity, Text)) + geom_point(size = 2.5) + ggtitle("Text-Kosinusähnlichkeit (hier für 'A Case of Identity')") + xlab("Kosinunsähnlichkeit")

Wie wir sehen, ist die Ähnlichkeit der Romane ‘The Red-headed League’ und ‘The Adventure of the Copper Beeches’ etwas größer, als dies bei den anderen Erzählungen der Fall ist.

Keyness

Bei der sog. Keyness handelt es sich um ein Maß für die Distinktivität eines Begriffs für einen bestimmten Text, also wie stark der Begriff den jeweiligen Text im Vergleich zum gesamten Korpus kennzeichnet. Während wir zuvor die Distanz von Wörtern und Texten zu einander untersucht haben, macht sich die Keyness die Verteilungshäufigkeit von Wörtern auf Texte zunutze, ohne deren Position zu berücksichtigen. Keyness funktioniert daher auch mit längeren Texten gut, solange diese sich ausreichend markant unterscheiden. Folgend berechnen wir zunächst die Keyness für vier Texte mit textstat_keyness() und plotten wir diese Keyness-Statistiken dann für vier Erzählungen mit Hilfe von textplot_keyness().

keyness <- textstat_keyness(meine.dfm, target = "A Scandal in Bohemia", measure = "lr")
textplot_keyness(keyness)

keyness <- textstat_keyness(meine.dfm, target = "A Case of Identity", measure = "lr")
textplot_keyness(keyness)

keyness <- textstat_keyness(meine.dfm, target = "The Five Orange Pips", measure = "lr")
textplot_keyness(keyness)

keyness <- textstat_keyness(meine.dfm, target = "The Adventure of the Noble Bachelor", measure = "lr")
textplot_keyness(keyness)

Schaut man sich die vier Beispieltexte einmal genauer an, so wird schnell klar, dass die Begriffe mit einem hohen Keyness-Wert tatsächlich sehr distinktiv für den jeweiligen Text sind, also Begriffe wie ‘majesty’ und ‘photograph’ tatsächlich nur in ‘A Scandal in Bohemia’ eine Rolle spielen. Wenig distinktive Begriffe sind hingegen solche, die zwar in anderen Texten, nicht aber dem Zieltext vorkommen. Nützlich wird diese Funktion vor allem dann, wenn man Texte nach einen Kriterium wie Medium, Sprecher, Partei, Zeitpunkt oder manuell zugeordnete Inhaltskategorie gruppiert.

Lexikalische Vielfalt

Unter Maßen lexikalischer Vielfalt versteht man Metriken, welche die Diversität eines Textes mit Blick auf den Wortgebrauch wiedergeben. Ein Beispiel ist die bereits in Kapitel 1 berechtete Typ-Token-Relation. Diese beschreibt die Wortvielfalt und geben so auch Aufschluss über die Komplexität eines Textes. Wir berechnen folgend zahlreiche Metriken für die lexikalische Diversität der zwölf Romane mit mit der Funktion textstat_lexdiv().

lexdiversitaet <- textstat_lexdiv(meine.dfm)
lexdiversitaet
write_delim(lexdiversitaet, path = "lexdiversitaet.csv", delim = ";") # Datei ist Excel-kompatibel

Bei einem oberflächliche Vergleich der Metriken fällt auf, dass sich die Texte nicht sehr stark unterscheiden, was ihre jeweilige lexikalische Vielfalt betrifft, ganz unabhängig davon, welche Metrik verwendet wird. Dies ist nicht unbedingt verwunderlich, da es sich um Texte des selben Genres und Autors handelt. Interessanter werden solche Metriken dann, wenn wir sehr unterschiedliche Genres oder Autoren vergleichen wollen, etwa die Programmen von Parteien, Texte aus unterschiedlichen Medien, oder Tweets von unterschiedlichen Nutzern.

Lesbarkeitsindizes

Eine weitere Klasse von Text-Metriken, die sich für ein Dokument aufgrund seiner Wortzusammensetzung berechnen lassen, sind die sog. Lesbarkeitsindizes. Darunter versteht man Metriken, die anhand von textlichen Eigenschaften einen Zahlenwert berechnen, der die Leseschwierigkeit eines Dokumentes möglichst akkurat wiedergeben soll. Anwendung finden solche Indizes etwa im Bildungsbereich, wenn es um die Frage geht, welches Schwierigkeitsniveau eines Textes für Schüler angemessen ist, aber auch in der öffentlichen Verwaltung, wenn möglichst klare und zugängliche Sprache bspw. auf einer berhördlichen Website verwendet werden soll.

Die Kalkulation zahlreicher Lesbarkeitsindizes erfolgt in quanteda mit textstat_readability(), Auch hier wird das Ergebnis in einer Excel-kompatible CSV-Datei gespeichert.

lesbarkeit <- textstat_readability(korpus)
lesbarkeit
write_delim(lesbarkeit, path = "lesbarkeit.csv", delim = ";") # Datei ist Excel-kompatibel

Ein schönes Beispiel für den Nutzen solcher Metriken findet sich in der Dokumentation der Funktion textstat_readability(). Hatte die Antrittsrede von George Washington im Jahr 1789 noch einen Flesh-Kincaid-Index von 28, so betrug der Wert bei der Antrittsrede von Donald Trump in 2017 nur noch 9 (was allerdings dem Trend seit Mitte des 20. Jhd. entspricht).

LS0tCnRpdGxlOiAiQXV0b21hdGlzaWVydGUgSW5oYWx0c2FuYWx5c2UgbWl0IFIiCmF1dGhvcjogIkNvcm5lbGl1cyBQdXNjaG1hbm4iCnN1YnRpdGxlOiAiV29ydC0gdW5kIFRleHRtZXRyaWtlbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKKipbaW5oYWx0c2FuYWx5c2UtbWl0LXIuZGVdKGh0dHA6Ly9pbmhhbHRzYW5hbHlzZS1taXQtci5kZS8pKioKCkluIGRpZXNlbSB6d2VpdGVuIEthcGl0ZWwgc3RlaHQgbnVuIGRpZSBBbmFseXNlIHZvciBXw7ZydGVybiB1bmQgVGV4dGVuIGltIE1pdHRlbHB1bmt0LiBBdWYgZGVuIGVyc3RlbiBCbGljayBlcnNjaGVpbmVuIGRpZSBNZXRyaWtlbiwgZGllIGhpZXIgdm9yZ2VzdGVsbHQgd2VyZGVuLCBtw7ZnbGljaGVyd2Vpc2UgbmljaHQgYWxzIGJlc29uZGVycyByZWxldmFudCBmw7xyIHNvemlhbHdpc3NlbnNjaGFmdGxpY2hlIEZyYWdlc3RlbGx1bmcuIERhcyBsaWVndCB6dW0gZWluZW4gZGFyYW4sIGRhcyB3aXIgdW5zIGFuIGRpZXNlciBTdGVsbGUgbm9jaCBuaWNodCBtaXQgYWJzdHJha3RlbiBLb256ZXB0ZW4gd2llIFRoZW1lbiBvZGVyIFNlbnRpbWVudCBiZXNjaMOkZnRpZ2VuLCBkaWUgaW4gZGVuIGZvbGdlbmRlbiBLYXBpdGVsIGltIE1pdHRlbHB1bmt0IHN0ZWhlbiB3ZXJkZW4sIHNvbmRlcm4gbWl0IEFzcGVrdGVuIHdpZSBkZXIgRnJlcXVlbnogdm9uIEJlZ3JpZmZlbiB1bmQgZGVyIMOEaG5saWNoa2VpdCB2b24gVGV4dGVuLCBkaWUgYXVnZW5zY2hlaW5saWNoIHZpZWxsZWljaHQgZGVyIExpbmd1aXN0aWsgbsOkaGVyIHNpbmQuIFdvcnQtIHVuZCBUZXh0bWV0cmlrZW4gc2luZCBhYmVyIGF1cyB6d2VpIEdyw7xuZGVuIHZvbiBCZWRldXR1bmc6IGVyc3RlbnMgYmlsZGVuIHNpZSBkaWUgR3J1bmRsYWdlIGRlciBow7ZoZXJzdHVmaWdlbiBWZXJmYWhyZW4sIGVnYWwgb2IgTGV4aWtvbi0sIFRoZW1lbi0gb2RlciBTZW50aW1lbnRhbmFseXNlLCB1bmQgenUgYW5kZXJlbiBsYXNzZW4gc2ljaCBhdWNoIHNjaG9uIG1pdCBpaG5lbiBpbnRlcmVzc2FudGUgc296aWFsd2lzc2Vuc2NoYWZ0bGljaGUgRnJhZ2VzdGVsbHVuZ2VuIGJlYXJiZWl0ZW4uCgpFaW5pZ2UgQmVpc3BpZWxlOgoKKiBXZWxjaGUgQmVncmlmZmUgc2luZCBiZXNvbmRlcnMgZGlzdGlua3RpdiBmw7xyIGVpbmVuIHBvbGl0aXNjaGUgUGFydGVpPwoqIFdpZSBzcHJhY2hsaWNoIGtvbXBsZXggc2luZCBOYWNocmljaHRlbmJlaXRyw6RnZSBpbiB1bnRlcnNjaGllZGxpY2hlbiBNZWRpZW4/CiogV2VsY2hlIGFuZGVyZW4gQmVncmlmZmUgc2luZCBtaXQgZWluZW0gZ2VzZWxsc2NoYWZ0bGljaGVuIFNjaGzDvHNzZWxiZWdyaWZmICgnS2xpbWEnLCAnTWlncmF0aW9uJywgJ0dlcmVjaHRpZ2tlaXQnLCAnRGlnaXRhbGlzaWVydW5nJykgdmVya27DvHBmdCB1bmQgd2llIHZlcsOkbmRlciBzaWNoIGRpZXMgw7xiZXIgZGllIFplaXQ/CiogV2llIMOkaG5saWNoIHNpbmQgc2ljaCBPbmxpbmUtS29tbWVudGFyZSB6dSB1bnRlcnNjaGllZGxpY2hlbiBUaGVtZW4/CgpEaWVzZSB1bmQgw6RobmxpY2hlIEZyYWdlbiB3ZXJkZW4gaW4gZGVuIGZvbGdlbmRlbiBLYXBpdGVsbiBhdWZnZWdyaWZmZW4gLS0genVuw6RjaHN0IHdlcmRlbiBhYmVyIGRpZSBGdW5rdGlvbmVuIHZvcmdlc3RlbGx0LCB3ZWxjaGUgZGllIEFyYmVpdCBtaXQgV8O2cnRlcm4gdW5kIFRleHRlbiBpbiBxdWFudGVkYSBlcm3DtmdsaWNoZW4uIAoKIyMjIyBJbnN0YWxsYXRpb24gdW5kIExhZGVuIGRlciBiZW7DtnRpZ3RlbiBSLUJpYmxpb3RoZWtlbiwgRXJzdGVsbGVuIGRlcyBLb3JwdXMsIEJlcmVjaG5lbiBlaW5lciBERk0KClp1bsOkY2hzdCB3ZXJkZW4gd2llZGVyIGRpZSBub3R3ZW5kaWdlbiBCaWJsaW90aGVrZW4gZ2VsYWRlbi4gRGFubiB3aXJkIGluIGVpbmVtIHp3ZWl0ZW4gU2Nocml0dCBkYXMgU2hlcmxvY2stS29ycHVzIGVpbmdlbGVzZW4gdW5kIGF1ZmJlcmVpdGV0IHVuZCBkYW5uIEtvcnB1cy1TdGF0aXN0aWtlbiBiZXJlY2huZXQuIFNjaGxpZcOfbGljaCBlcnN0ZWxsZW4gd2lyIHdpZWRlciBlaW5lIERGTSAodmdsLiBbS2FwaXRlbCAxXSgxX2dydW5kbGFnZW4uaHRtbCkpLCBkYSB3aXIgZGllc2Ugc3DDpHRlciBub2NoIGJlbsO2dGlnZW4uIAoKYGBge3J9CiMgSW5zdGFsbGF0aW9uIHVuZCBMYWRlbiBkZXIgQmlibGlvdGhla2VuCmlmKCFyZXF1aXJlKCJxdWFudGVkYSIpKSBpbnN0YWxsLnBhY2thZ2VzKCJxdWFudGVkYSIpCmlmKCFyZXF1aXJlKCJyZWFkdGV4dCIpKSBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkdGV4dCIpCmlmKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojIExhZGVuIGRlciBTaGVybG9jayBIb2xtZXMtRGF0ZW4gKGJlcmVpdHMgYWxzIFJEYXRhLUZpbGUgZ2VzcGVpY2hlcnQpCmxvYWQoImRhdGVuL3NoZXJsb2NrL3NoZXJsb2NrLmtvcnB1cy5SRGF0YSIpCgojIEJlcmVjaG5lbiBlaW5lciBERk0KbWVpbmUuZGZtIDwtIGRmbShrb3JwdXMsIHJlbW92ZV9udW1iZXJzID0gVFJVRSwgcmVtb3ZlX3B1bmN0ID0gVFJVRSwgcmVtb3ZlX3N5bWJvbHMgPSBUUlVFLCByZW1vdmUgPSBzdG9wd29yZHMoImVuZ2xpc2giKSkKYGBgCgoKIyMjIyBLb3Jrb3JkYW56ZW4gZXJzdGVsbGVuCgpadSBkZW4gZWluZmFjaHN0ZW4gRnVua3Rpb25lbiB2b24gcXVhbnRlZGEgZ2Vow7ZydCBkaWUgTcO2Z2xpY2hrZWl0LCBbS29ua29yZGFuemVuXShodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9Lb25rb3JkYW56XyhUZXh0d2lzc2Vuc2NoYWZ0KSkgKGF1Y2ggS1dJQyBnZW5hbm50KSB6dSBlcnN0ZWxsZW4sIGFsc28gZGllIFRleHRzdGVsbGUgZWluZXMgU3VjaHRlcm1zIHNvd2llIGRlc3NlbiB1bWdlYmVuZGVuIFNhdHpla29udGV4dCB6dSBleHRyYWhpZXJlbi4gS29ua29yZGFuemVuIGxhc3NlbiBzaWNoIGluIHF1YW50ZWRhIGbDvHIgZWluemVsZW4gV8O2cnRlciwgYWJlciBhdWNoIGbDvHIgUGhyYXNlbiBlcnpldWdlbi4gT2Z0bWFscyBpc3QgZGVyIEV4cG9ydCBlaW5lciBLb25rb2RhbnogKGV0d2EgYWxzIENTVi1EYXRlaSwgZGllIG1pdCBFeGNlbCBnZcO2ZmZuZXQgd2VyZGVuIGthbm4pIG5lYmVuIGRlciBEYXJzdGVsbHVuZyBpbm5lcmhhbGIgdm9uIFIgYmVzb25kZXJzIG7DvHR6bGljaC4gRGllcyBnZXNjaGllaHQgaGllciBtaXQgZGVyIEZ1bmt0aW9uIFt3cml0ZV9kZWxpbSgpXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3dyaXRlX2RlbGltLmh0bWwpLgoKKkFubWVya3VuZzogRGllIEtvbmtvcmRhbnoga2FubiBtaXQgZGVtIGtsZWluZW4gUGZlaWwgcmVjaHRzIG9iZW4gZ2VzY3JvbGx0IHdlcmRlbi4qCgpgYGB7cn0Ka29ua29yZGFueiA8LSBrd2ljKGtvcnB1cywgImRhdGEiKQprb25rb3JkYW56CmtvbmtvcmRhbnogPC0ga3dpYyhrb3JwdXMsIHBocmFzZSgiSm9obnxNYXJ5IFtBLVpdKyIpLCB2YWx1ZXR5cGUgPSAicmVnZXgiLCBjYXNlX2luc2Vuc2l0aXZlID0gRkFMU0UpCmtvbmtvcmRhbnoKa29ua29yZGFueiA8LSBrd2ljKGtvcnB1cywgYygibG9nKiIsICJlbW90KiIpLCB3aW5kb3cgPSAxMCwgY2FzZV9pbnNlbnNpdGl2ZSA9IEZBTFNFKQprb25rb3JkYW56CndyaXRlX2RlbGltKGtvbmtvcmRhbnosIHBhdGggPSAia29ua29yZGFuei5jc3YiLCBkZWxpbSA9ICI7IikgIyBEYXRlaSBpc3QgRXhjZWwta29tcGF0aWJlbApgYGAKCkRpZSBLb25rb3JkYW56ZW4gYmVzdGVoZW4gYXVzIGRlbiBNZXRhZGF0ZW4gKFRleHRuYW1lIHVuZCBQb3NpdGlvbiksIGRlbSBsaW5rZW4gS29udGV4dCwgZGVtIFN1Y2h0ZXJtLCBzb3dpZSBkZW0gcmVjaHRlbiBLb250ZXh0LiBEaWUgZXJzdGUgS29ua29yZGFueiBlbnRow6RsdCBhbGxlIFZvcmtvbW1uaXNzZSBkZXMgQmVncmlmZnMgJ2RhdGEnLCBkaWUgendlaXRlIGFsbGUgVm9ya29tbW5pc3NlIGRlciBOYW1lbiAnSm9obicgdW5kICdNYXJ5JyBnZWZvbGd0IHZvbiBlaW5lbSB3ZWl0ZXJlbiBXb3J0IGluIEdyb8Ofc2NocmVpYnVuZyAoaS5kLlIuIGRlciBOYWNobmFtZSkuIERpZSBkcml0dGUgS29ua29yZGFueiBlbnRow6RsdCBzY2hsaWXDn2xpY2ggZGllIFdvcnRmcmFnbWVudGUgJ2xvZycgdW5kICdlbW90JywgYWxzbyBXw7ZydGVyIHdpZSAnbG9naWNhbCcgdW5kICdlbW90aW9uYWwnLCBhYmVyIGF1Y2ggZGllIFBsdXJhbGZvcm0gJ2Vtb3Rpb25zJy4gU3RyZW5nZ2Vub21tZW4gaGFuZGVsdCBlcyBzaWNoIGhpZXJiZWkgbmljaHQgdW0gV29ydHN0w6RtbWUsIHdlaWwgZGllIEZsZXhpb25zZm9ybSBiZWkgdW5yZWdlbG3DpMOfaWdlbiBXw7ZydGVybiBnYW56IHZvbSBMZW1tYSBhYndlaWNodCAodmdsLiAnZ28nIHVuZCAnd2VudCcpLiBJbiBkZW4gbWVpc3RlbiBzb3ppYWx3aXNzZW5zY2hhZnRsaWNoZW4gQW53ZW5kdW5nc3N6ZW5hcmllbiBpc3QgZXMgYWJlciBiZXJlaXRzIGF1c3JlaWNoZW5kLCBkdXJjaCBkaWUgVmVyd2VuZHVuZyB2b24gUGxhdHpoYWx0ZXJuICgqKSB2ZXJzY2hpZWRlbiBXb3J0dmFyaWFudGVuIHp1IGlkZW50aWZpemllcmVuLiBIaWVyIGJyaW5ndCBxdWFudGVkYSBlaW5lIFJlaWhlIG7DvHR6bGljaGVyIEVpZ2Vuc2NoYWZ0ZW4gbWl0LCBkaWUgaW4gZGVyIERva3VtZW50YXRpb24gdm9uIFtrd2ljKCldKGh0dHA6Ly9kb2NzLnF1YW50ZWRhLmlvL3JlZmVyZW5jZS9rd2ljLmh0bWwpIGdlbmF1IGJlc2NocmllYmVuIHdlcmRlbi4gCgpBbHMgbsOkY2hzdGVzIGJlcmVjaG5lbiB3aXIgZGllIEjDpHVmaWdrZWl0IHVuZCBEaXNwZXJzaW9uIHZvbiBUb2tlbnMgcHJvIEVyesOkaGx1bmcsIHdlbGNoZSBkaWUgQmVncmlmZmUgJ2RhcmsnIHVuZCAnbGlnaHQnIGVudGhhbHRlbi4gCgpgYGB7cn0KdGVybTEgPC0ga3dpYyhrb3JwdXMsICJkYXJrIiwgdmFsdWV0eXBlID0gInJlZ2V4IiwgY2FzZV9pbnNlbnNpdGl2ZSA9IEZBTFNFKSAlPiUgZ3JvdXBfYnkoZG9jbmFtZSkgJT4lIHN1bW1hcmlzZShUcmVmZmVyID0gbigpKSAlPiUgbXV0YXRlKFByb3plbnRhbnRlaWwgPSBUcmVmZmVyLyhrb3JwdXMuc3RhdHMkVG9rZW5zLzEwMCksIFN1Y2h0ZXJtID0gImRhcmsiKSAlPiUgYXJyYW5nZShkZXNjKFByb3plbnRhbnRlaWwpKQp0ZXJtMiA8LSBrd2ljKGtvcnB1cywgImxpZ2h0IiwgdmFsdWV0eXBlID0gInJlZ2V4IiwgY2FzZV9pbnNlbnNpdGl2ZSA9IEZBTFNFKSAlPiUgZ3JvdXBfYnkoZG9jbmFtZSkgJT4lIHN1bW1hcmlzZShUcmVmZmVyID0gbigpKSAlPiUgbXV0YXRlKFByb3plbnRhbnRlaWwgPSBUcmVmZmVyLyhrb3JwdXMuc3RhdHMkVG9rZW5zLzEwMCksIFN1Y2h0ZXJtID0gImxpZ2h0IikgJT4lIGFycmFuZ2UoZGVzYyhQcm96ZW50YW50ZWlsKSkKdGVybTEKdGVybTIKYGBgCgpXaWVkZXIgd2VuZGVuIHdpciB6dW7DpGNoc3QgZGllIEZ1bmt0aW9uIGt3aWMoKSBhbiwgYWxsZXJkaW5ncyBoaWVyIGluIEtvbWJpbmF0aW9uIG1pdCBtZWhyZXJlbiBGdW5rdGlvbmVuIGF1cyBkZW0gUGFrZXQgZHBseXIgKHRpZHl2ZXJzZSkuIERpZXNlIEZ1bmt0aW9uZW4gaGFiZW4gbmljaHRzIG1pdCBxdWFudGVkYSB6dSB0dW4sIHNvbmRlcm4gc2luZCBmw7xyIGRpZSBVbWZvcm11bmcgamVnbGljaGVyIERhdGVuIGluIFIgbsO8dHpsaWNoICh3ZXIgbWVociB3aXNzZW4gbcO2Y2h0ZSwgc29sbHRlIHNpY2ggW2RpZXNlcyBCdWNoXShodHRwOi8vcjRkcy5oYWQuY28ubnovKSBhbnNjaGF1ZW4pLiBXw6RocmVuZCB6dXZvciBlaW5mYWNoIGRpZSByZXN1bHRpZXJlbmRlIEtvbmtvcmRhbnogYXVzZ2VnZWJlbiB3dXJkZSwgd2lyZCBkYXMgRXJnZWJuaXMgamV0enQgbWl0IEhpbGZlIGRlciBGdW5rdGlvbmVuIFtncm91cF9ieSgpXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZHBseXIvdmVyc2lvbnMvMC43LjYvdG9waWNzL2dyb3VwX2J5KSwgW3N1bW1hcmlzZSgpXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZHBseXIvdmVyc2lvbnMvMC43LjYvdG9waWNzL3N1bW1hcmlzZSksIFttdXRhdGUoKV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2RwbHlyL3ZlcnNpb25zLzAuNy42L3RvcGljcy9tdXRhdGUpIHVuZCBbYXJyYW5nZSgpXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZHBseXIvdmVyc2lvbnMvMC43LjYvdG9waWNzL2FycmFuZ2UpIHdlaXRlciB2ZXJhcmJlaXRldC4gRGFiZWkgbWFjaGVuIHdpciB1bnMgZGllIFRhdHNhY2hlIHp1bnV0emUsIGRhc3MgaW4gZWluZW0gS1dJQy1FcmdlYm5pcyBiZXJlaXRzIGFsbGUgSW5mb3JtYXRpb25lbiB2b3JsaWVnZW4sIHVtIGRpZSBhYnNvbHV0ZSB1bmQgcmVsYXRpdmUgRnJlcXVlbnogZWluZXMgQmVncmlmZnMgKGhpZXIgJ2xpZ2h0JyB1bmQgJ2RhcmsnKSBpbiBlaW5lciBSZWloZSB2b24gRG9rdW1lbnRlbiB6dSBiZXJlY2huZW4uIERlbiBQcm96ZW50YW50ZWlsIGhhYmVuIHdpciBkYWJlaSBlaW5mYWNoIG1pdHRlbHMgRHJlaXNhdHogYWJnZWxlaXRldCAobWl0ICpUcmVmZmVyLyhrb3JwdXMuc3RhdHMkVG9rZW5zLzEwMCkqKS4KCldvcnRmcmVxdWVuemVuIGxhc3NlbiBzaWNoIGFsbGVyZGluZ3Mgd2VzZW50bGljaCBlaW5mYWNoZXIgZHVyY2ggZGllIHF1YW50ZWRhLWVpZ2VuZW4gRnVua3Rpb24gW3RleHRzdGF0X2ZyZXF1ZW5jeSgpXShodHRwczovL2RvY3MucXVhbnRlZGEuaW8vcmVmZXJlbmNlL3RleHRzdGF0X2ZyZXF1ZW5jeS5odG1sKSB1bXNldHplbiwgZGllIHdpciBmb2xnZW5kIGF1Y2gga29uc2VxdWVudCBudXR6ZW4gd2VyZGVuIC0tIGF1Y2ggZGF6dSBnbGVpY2ggbm9jaCBldHdhcyBtZWhyLiAKClp1bsOkY2hzdCBwbG90dGVuIHdpZSBkaWUgYWJzb2x1dGUgdW5kIHJlbGF0aXZlbiBIw6R1Zmlna2VpdCBkZXIgYmVpZGVuIEJlZ3JpZmZlLgoKYGBge3J9CnRlcm1lLmtvbWJpbmllcnQgPC0gYmluZF9yb3dzKHRlcm0xLCB0ZXJtMikKdGVybWUua29tYmluaWVydCRkb2NuYW1lIDwtIGZhY3Rvcih0ZXJtZS5rb21iaW5pZXJ0JGRvY25hbWUsIGxldmVscyA9IGxldmVscyhrb3JwdXMuc3RhdHMkVGV4dCkpCmdncGxvdCh0ZXJtZS5rb21iaW5pZXJ0LCBhZXMoZG9jbmFtZSwgVHJlZmZlciwgZ3JvdXAgPSBTdWNodGVybSwgY29sID0gU3VjaHRlcm0pKSArIGdlb21fbGluZShzaXplID0gMSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0ID0gMSkpICsgZ2d0aXRsZSgiSMOkdWZpZ2tlaXQgZGVyIFN1Y2hiZWdyaWZmZSBcImRhcmtcIiB1bmQgXCJsaWdodFwiIHBybyBSb21hbiAoYWJzb2x1dCkiKSArIHhsYWIoIlJvbWFuIikgKyB5bGFiKCJXw7ZydGVyIChnZXNhbXQpIikKZ2dwbG90KHRlcm1lLmtvbWJpbmllcnQsIGFlcyhkb2NuYW1lLCBQcm96ZW50YW50ZWlsLCBncm91cCA9IFN1Y2h0ZXJtLCBjb2wgPSBTdWNodGVybSkpICsgZ2VvbV9saW5lKHNpemUgPSAxKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSkgKyBnZ3RpdGxlKCJIw6R1Zmlna2VpdCBkZXIgU3VjaGJlZ3JpZmZlIFwiZGFya1wiIHVuZCBcImxpZ2h0XCIgcHJvIFJvbWFuIChyZWxhdGl2KSIpICsgeGxhYigiUm9tYW4iKSArIHlsYWIoIlfDtnJ0ZXIgKCUpIikKYGBgCgpXaXIgc2VoZW4gendlaSB1bnRlcnNjaGllZGxpY2hlIEJlcmVjaG51bmdlbjogZGFzIGVyc3RlIFBsb3QgemVpZ3QgZGllIGFic29sdXRlIEjDpHVmaWdrZWl0IGRlciBiZWlkZW4gQmVncmlmZmUsIGRhcyB6d2VpdGUgaGluZ2VnZW4gZGVuIHJlbGF0aXZlbiBQcm96ZW56YW50ZWlsIGRlcyBCZWdyaWZmcyBhbiBkZXIgR2VzYW10d29ydHphaGwgZGVzIGpld2VpbGlnZW4gUm9tYW5zLiBXaWVzbyBzaW5kIGRpZSBiZWlkZW4gUGxvdHMgbmFoZXp1IGlkZW50aXNjaD8gRGllcyBoYXQgbWl0IGRlciByZWxhdGl2IGdsZWljaGVuIFdvcnRhbnphaGwgZGVyIFJvbWFuZSB1bnRlcmVpbmFuZGVyIHp1IHR1biAoendpc2NoZW4gOCw1MDAgdW5kIDEyLDAwMCBUb2tlbnMpLiBTaW5kIHp3ZWkgVGVpbGtvcnBvcmEgdm9uIHNlaHIgdW50ZXJzY2hpZWRsaWNoZXIgR3LDtsOfZSwgaXN0IGVpbmUgTm9ybWFsaXNpZXJ1bmcgZGVyIFdvcnRmcmVxdWVuemVuIGV4dHJlbSB3aWNodGlnLCBkYSBzb25zdCBkaWUgRXJnZWJuaXNzZSBtYXNzaXYgdmVyemVycnQgd2VyZGVuLiBBdWNoIHNvIGVyZ2ViZW4gc2ljaCBkdXJjaGF1cyBVbnRlcnNjaGllZGUsIHdlbm4gbWFuIGV0d2EgZGVuIEFudGVpbCB2b24gJ1RoZSBBZHZlbnR1cmUgb2YgdGhlIFNwZWNrbGVkIEJhbmQnIHVuZCAnVGhlIEFkdmVudHVyZSBvZiB0aGUgQ29wcGVyIEJlZWNoZXMnIHZlcmdsZWljaHQuIFfDpGhyZW5kIGRpZSBBbnN6YWhsIGRlciBhYnNvbHV0ZW4gVHJlZmZlciBhdWYgJ2xpZ2h0JyBpbiBiZWlkZW4gUm9tYW5lbiBpZGVudGlzY2ggaXN0LCBmw6RsbHQgZGVyIHJlbGF0aXZlIEFudGVpbCBiZWkgJ1NwZWNrbGVkIEJhbmQnIGltIFZlcmdsZWljaCBhYi4gCgpXYXMgdHVuLCB3ZW5uIG1hbiBzaWNoIHdlbmlnZXIgZsO8ciBkaWUgSMOkdWZpZ2tlaXQgYWxzIGbDvHIgZGllIFBvc2l0aW9uIGRlciBTdWNodGVybWUgaW50ZXJlc3NpZXJ0PyBEYXp1IGthbm4gZGFzIFBsb3R0ZW4gZGVyIEJlZ2lmZnNkaXNwZXJzaW9uIGFscyAneHJheS1wbG90JyBuw7x0emxpY2ggc2Vpbiwgd296dSBkaWUgRnVua3Rpb24gW3RleHRwbG90X3hyYXldKGh0dHBzOi8vZG9jcy5xdWFudGVkYS5pby9yZWZlcmVuY2UvdGV4dHBsb3RfeHJheS5odG1sKSBleGlzdGllcnQuIERpZSBYLUFjaHNlIHN0ZWxsdCBoaWVyYmVpIGRpZSBQb3NpdGlvbiBpbm5lcmhhbGIgZGVzIFRleHRlcyBkYXIsIGFuIGRlbSBkZXIgU3VjaGJlZ3JpZmYgdm9ya29tbXQuIAoKYGBge3J9CnRleHRwbG90X3hyYXkoa3dpYyhrb3JwdXMsICJkYXJrIiwgdmFsdWV0eXBlID0gInJlZ2V4IiwgY2FzZV9pbnNlbnNpdGl2ZSA9IEZBTFNFKSkgKyBnZ3RpdGxlKCJMZXhpa2FsaXNjaGUgRGlzcGVyc2lvbiB2b24gXCJkYXJrXCIgaW4gU2hlcmxvY2sgSG9sbWVzIikKdGV4dHBsb3RfeHJheShrd2ljKGtvcnB1cywgImxpZ2h0IiwgdmFsdWV0eXBlID0gInJlZ2V4IiwgY2FzZV9pbnNlbnNpdGl2ZSA9IEZBTFNFKSkgKyBnZ3RpdGxlKCJMZXhpa2FsaXNjaGUgRGlzcGVyc2lvbiB2b24gXCJsaWdodFwiIGluIFNoZXJsb2NrIEhvbG1lcyIpCmBgYAoKCiMjIyMgS29sbG9rYXRpb25lbiAKCldpciBnZWhlbiBudW4genUgZGVuIHNvZ2VuYW5udGVuIFRleHRzdGF0aXN0a2VuIMO8YmVyLiBEYWJlaSBoYW5kZWx0IGVzIHNpY2ggdW0gRnVua3Rpb25lbiBhbmhhbmQgZGVyZXIgc2ljaCBXw7ZydGVyIHVuZCBUZXh0ZSBtaXQgQmxpY2sgYXVmIGlocmUgw4RobmxpY2hrZWl0IG1pdCBvZGVyIERpc3RhbnogenUgYW5kZXJlbiBXw7ZydGVybiBvZGVyIFRleHRlIGFuYWx5c2llcmVuIGxhc3Nlbi4gRWluZSB3aWNodGlnZSBGdW5rdGlvbiBpbiBkaWVzZW0gWnVzYW1tZW5oYW5nIGlzdCBkaWUgRXh0cmFrdGlvbiB2b24gW0tvbGxva2F0aW9uZW5dKGh0dHBzOi8vZGUud2lraXBlZGlhLm9yZy93aWtpL0tvbGxva2F0aW9uKS4gRGllIEtvbGxva2F0ZSBlaW5lcyBCZWdyaWZmcyBzaW5kIHNvbGNoZSBCZWdyaWZmZSwgZGllIGjDpHVmaWcgZ2VtZWluc2FtIG1pdCBkZW0gVGVybSB2b3Jrb21tZW4uIERlciBQcm96ZXNzIGlzdCBkYWJlaSBpbmR1a3Rpdi4KCkZvbGdlbmQgd2VuZGVuIHdpciBkaWUgRnVua3Rpb24gW3RleHRzdGF0X2NvbGxvY2F0aW9ucygpXShodHRwczovL2RvY3MucXVhbnRlZGEuaW8vcmVmZXJlbmNlL3RleHRzdGF0X2NvbGxvY2F0aW9ucy5odG1sKSBhbiwgZGllIGjDpHVmaWdlIEtvbGxva2F0ZSBpbSBTaGVybG9jayBIb2xtZXMtS29ycHVzIGVybWl0dGVsdC4gQW5oYW5kIHZvbiB3cml0ZV9kZWxpbSB3aXJkIGRhcyBFcmdlYm5pcyBkYW5uIG5vY2ggYWxzIEV4Y2VsLWtvbXBhdGlibGUgQ1NWLURhdGVpIGdlc3BlaWNoZXJ0LgoKYGBge3J9Cm1laW5lLnRva2VucyA8LSB0b2tlbnMoa29ycHVzKQprb2xsb2thdGlvbmVuIDwtIHRleHRzdGF0X2NvbGxvY2F0aW9ucyhtZWluZS50b2tlbnMsIG1pbl9jb3VudCA9IDEwKQphcnJhbmdlKGtvbGxva2F0aW9uZW4sIGRlc2MoY291bnQpKQphcnJhbmdlKGtvbGxva2F0aW9uZW4sIGRlc2MobGFtYmRhKSkKd3JpdGVfZGVsaW0oa29sbG9rYXRpb25lbiwgcGF0aCA9ICJrb2xsb2thdGlvbmVuLmNzdiIsIGRlbGltID0gIjsiKSAjIERhdGVpIGlzdCBFeGNlbC1rb21wYXRpYmVsCmBgYAoKSW4gZGVuIGJlaWRlbiBUYWJlbGxlbiB6ZWlndCB1bnMgKmNvbGxvY2F0aW9uKiBkYXMgS29sbG9rYXQgdW5kICpjb3VudCogZGVzc2VuIGFic29sdXRlIEjDpHVmaWdrZWl0IGFuLiBEaWUgQXNzb3ppYXRpb25zc3TDpHJrZXIgZGVyIEtvbGxva2F0aW9uIHdpcmQgbWl0ICpsYW1iZGEqIHVuZCAqeiogZ2VtZXNzZW4gKGdlbmF1ZXIgaXN0IHogZWluIFt6LXN0YW5kYXJkaXNpZXJ0ZXMgbGFtYmRhXShodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9TdGFuZGFyZGlzaWVydW5nXyhTdGF0aXN0aWspKSkuIExhbWJkYSBiZXNjaHJlaWJ0IGRhYmVpIGRpZSBXYWhyc2NoZWlubGljaGtlaXQsIGRhc3MgZ2VuYXUgZGllc2UgendlaSBCZWdyaWZmZSBhdWYgZWluYW5kZXIgZm9sZ2VuLCB3YXMgaW5zb2Zlcm4gdm9uIGRlciBhYnNvbHV0ZW4gSMOkdWZpZ2tlaXQgenUgZGlmZmVyZW56aWVyZW4gaXN0LCBhbHMgZGFzIGRpZXNlIG5pY2h0IGRhcyBhdWZ0cmV0ZW4gZWluZXMgVGVpbGJlZ3JpZmZzIG1pdCBhbGxlbiBhbmRlcmVuIFfDtnJ0ZXJuIGltIEtvcnB1cyBiZXLDvGNrc2ljaHRpZ3QuCgpEaWUgYmVpZGVuIFRhYmVsbGVuIGlsbHVzdHJpZXJlbiBkaWVzZW4gVW50ZXJzY2hpZWQuIFfDpGhyZW5kIGRpZSBlcnN0ZSBuYWNoIGRlciBhYnNvbHV0ZW4gSMOkdWZpZ2tlaXQgc29ydGllcnQgaXN0LCBzbyBkYXNzIGfDpG5naWdlIEtvbGxva2F0ZSB3aWUgJ29mIHRoZScgb2RlciAnaXQgaXMnIGdhbnogdm9ybmUgbGllZ2VuLCBpc3QgZGllIHp3ZWl0ZSBhYnN0ZWlnZW5kIG5hY2ggTGFtYmRhIHNvcnRpZXJ0LCBzbyBkYXNzIGVpbmUgUmVpaGUgdm9uIEVpZ2VubmFtZW4gd2llICdob3NtZXIgYW5nZWwnIG9kZXIgJ2JyaW9ueSBsb2RnZScgZGllIExpc3RlIGFuZsO8aHJlbi4gUHJha3Rpc2NoIGJldHJhY2h0ZXQgaXN0IGVzIG1laXN0IHNpbm52b2xsZXIsIEVpZ2VubmFtZW4gYWxzIFBocmFzZW4genUgYmV0cmFjaHRlbiwgc3RhdHQgYWxzIGVpbnplbG5lIEJlZ3JpZmZlLCBkZXJlbiBnZW1laW5zYW1lcyBBdWZ0cmV0ZW4gd2lya2xpY2ggZXR3YXMgw7xiZXIgZGVuIFRleHQgdmVycsOkdC4gRWNodGUgS29sbG9rYXRlIHNpbmQgaGluZ2VnZW4gJ25vIGRvdWJ0JyBvZGVyICd5b3VuZyBsYWR5Jy4gCgoKIyMjIyBXb3J0LSB1bmQgVGV4dMOkaG5saWNoa2VpdCB1bmQgLURpc3RhbnoKCldpZSBzaWNoIGltIGVyc3RlbiBLYXBpdGVsIGJlcmVpdHMgYW5nZWRldXRldCBoYXQsIGxhc3NlbiBzaWNoIGF1ZiBHcnVuZGxhZ2UgZWluZXIgREZNIHphaGxyZWljaGUgTWV0cmlrZW4gYmVyZWNobmVuLCB3ZWxjaGUgZGllIE7DpGhlIHVuZCBEaXN0YW56IHZvbiBXw7ZydGVybiB1bmQgRG9rdW1lbnRlbiB6dSBlaW5hbmRlciByZWxla3RpZXJlbi4gRGllcyBnZXNjaGllaHQgbWl0IFt0ZXh0c3RhdF9zaW1pbCgpXShodHRwczovL2RvY3MucXVhbnRlZGEuaW8vcmVmZXJlbmNlL3RleHRzdGF0X2ZyZXF1ZW5jeS5odG1sKS4gWnVuw6RjaHN0IGtvbnN0cnVpZXJlbiB3aXIgZGF6dSBlaW5lIERGTSwgaW4gZGVyIGplZGVyIFNhdHogZWluZW0gRG9rdW1lbnQgZW50c3ByaWNodC4gRGllcyB3aXJkIGRlc2hhbGIgbm90d2VuZGlnLCB3ZWlsIHNpY2ggV29ydMOkaG5saWNoa2VpdGVuIGJlaSBlaW5lciBnZXJpbmdlbiBEb2t1bWVudGFuemFobCBuaWNodCBiZXNvbmRlcnMgenV2ZXJsw6Rzc2lnIGJlcmVjaG5lbiBsYXNzZW4sIGRhIMOEaG5saWNoa2VpdCBhbHMgS29va3VyZW56IGlubmVyaGFsYiBkZXMgZ2xlaWNoZW4gRG9rdW1lbnRzIG9wZXJhdGlvbmFsaXNpZXJ0IHdpcmQuIERhbm4gQmVyZWNobmVuIHdpciBkaWUgV29ydMOkaG5saWNoa2VpdCB6dW0gQmVncmlmZiAnbG92ZScgbWl0dGVscyBbS29zaW51c2Rpc3RhbnpdKGh0dHBzOi8vZGUud2lraXBlZGlhLm9yZy93aWtpL0tvc2ludXMtJUMzJTg0aG5saWNoa2VpdC4gQW5kZXJlIHZlcmbDvGdiYXJlIE1ldHJpa2VuIHNpbmQgJ2NvcnJlbGF0aW9uJywgJ2phY2NhcmQnLCAnZUphY2NhcmQnLCAnZGljZScsICdlRGljZScsICdzaW1wbGUgbWF0Y2hpbmcnLCAnaGFtYW5uJyB1bmQgJ2ZhaXRoJywgd2VsY2hlIFdvcnTDpGhubGljaGtlaXQgamV3ZWlscyB1bnRlcnNjaGllZGxpY2ggb3BlcmF0aW9uYWxpc2llcmVuLiAKCmBgYHtyfQprb3JwdXMuc2FldHplIDwtIGNvcnB1c19yZXNoYXBlKGtvcnB1cywgdG8gPSAic2VudGVuY2VzIikKbWVpbmUuZGZtLnNhZXR6ZSA8LSBkZm0oa29ycHVzLnNhZXR6ZSwgcmVtb3ZlX251bWJlcnMgPSBUUlVFLCByZW1vdmVfcHVuY3QgPSBUUlVFLCByZW1vdmVfc3ltYm9scyA9IFRSVUUsIHJlbW92ZSA9IHN0b3B3b3JkcygiZW5nbGlzaCIpKQptZWluZS5kZm0uc2FldHplIDwtIGRmbV90cmltKG1laW5lLmRmbS5zYWV0emUsIG1pbl9kb2NmcmVxID0gNSkKYWVobmxpY2hrZWl0LndvZXJ0ZXIgPC0gdGV4dHN0YXRfc2ltaWwobWVpbmUuZGZtLnNhZXR6ZSwgImxvdmUiLCBtYXJnaW4gPSAiZmVhdHVyZXMiLCBtZXRob2QgPSAiY29zaW5lIikKaGVhZChhZWhubGljaGtlaXQud29lcnRlcltvcmRlcihhZWhubGljaGtlaXQud29lcnRlclssMV0sIGRlY3JlYXNpbmcgPSBUKSxdLCAxMCkKYGBgCgpBbmFsb2cgenVyIMOEaG5saWNoa2VpdCBmdW5rdGlvbmVydCBhdWNoIGRpZSBCZXJlY2hudW5nIHZvbiBXb3J0ZGlzdGFuemVuIG1pdCBkZXIgRnVua3Rpb24gW3RleHRzdGF0X2Rpc3QoKV0oaHR0cHM6Ly9kb2NzLnF1YW50ZWRhLmlvL3JlZmVyZW5jZS90ZXh0c3RhdF9mcmVxdWVuY3kuaHRtbCkuIEF1Y2ggaGllciBoYWJlbiB3aXIgd2llZGVyIGVpbmUgZ3Jvw59lIEFuemFobCB2b24gRGlzdGFuem1hw59lbiB6dXIgQXVzd2FobCAoJ2V1Y2xpZGVhbicsICdjaGlzcXVhcmVkJywgJ2NoaXNxdWFyZWQyJywgJ2hhbW1pbmcnLCAna3VsbGJhY2snLiAnbWFuaGF0dGFuJywgJ21heGltdW0nLCAnY2FuYmVycmEnLCAnbWlua293c2tpJykuCgpgYGB7cn0KZGlzdGFuei53b2VydGVyIDwtIHRleHRzdGF0X2Rpc3QobWVpbmUuZGZtLnNhZXR6ZSwgImxvdmUiLCBtYXJnaW4gPSAiZmVhdHVyZXMiLCBtZXRob2QgPSAiZXVjbGlkZWFuIikKaGVhZChkaXN0YW56LndvZXJ0ZXJbb3JkZXIoZGlzdGFuei53b2VydGVyWywxXSwgZGVjcmVhc2luZyA9IFQpLF0sIDEwKQpgYGAKCldhcyBzYWd0IGRhcyBFcmdlYm5pcyBhdXM/IFZvciBhbGxlbSBkYXMgKHdlbmlnIMO8YmVycmFzY2hlbmQpIFfDtnJ0ZXIgd2llICd1cG9uJyB1bmQgJ3NhaWQnIHNlaHIgd2VpdCB2b24gJ2xvdmUnIGVudGZlcm50IHNpbmQgLS0gYWxsZXJkaW5ncyBuaWNodCBpbiBkZW0gU2lubmUsIGRhc3Mgc2llIGRhcyBsb2dpc2NoZSBHZWdlbnRlaWwgdm9uICdsb3ZlJyBkYXJzdGVsbGVuIHfDvHJkZW4gKGluIGRlciBMaW5ndWlzdGlrIHNwcmljaHQgbWFuIHZvbiBbQW50b255bWllXShodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9BbnRvbnltKSkuIERhcyBsaWVndCBkYXJhbiwgZGFzcyBkaWVzZSBCZWdyaWZmZSBpbSBLb3JwdXMgbmFoZXp1IGdsZWljaCB2ZXJ0ZWlsdCBzaW5kLCBhbHNvIMO8YmVyYWxsIHZvcmtvbW1lbi4gTWl0IGRlbSBWZXJmYWhyZW4gZGVyIFtXb3J0dmVrdG9yZW5dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1dvcmRfZW1iZWRkaW5nKSAod2VsY2hlcyB3aXIgaGllciBuaWNodCBiZWhhbmRlbG4pIHVuZCBzZWhyIGdyb8OfZW4gRGF0ZW5iZXN0w6RuZGVuIGxhc3NlbiBzaWNoIGFsbGVyZGluZ3MgYXVjaCBzb2xjaGUgdW5kIGFuZGVyZSBzZW1hbnRpc2NoIEJlemllaHVuZ2VuIGluZGVudGlmaXppZXJlbi4gRGllIEZpbHRlcnVuZywgZGllIHdpciB6dXZvciBhbiBkZXIgREZNIHZvcmdlbm9tbWVuIGhhYmVuLCBzY2hsaWXDn3QgQmVncmlmZmUgYXVzLCBkaWUgdmllbGxlaWNodCBuaWUgZ2VtZWluc2FtIG1pdCAnbG92ZScgdm9ya29tbWVuLCB1bmQgaW5zb2Zlcm4gbm9jaCBkaXN0YW56aWVydGVyIHfDpHJlbiwgYWxsZXJkaW5ncyBnaWJ0IGVzIGRlcmVyIGF1Y2ggc2VociB2aWVsZS4gWnVzYW1tZW5mYXNzZW5kIGthbm4gbWFuIHNhZ2VuLCBkYXNzIE7DpGhlLSB1bmQgRGluc3RhbnptYcOfZSBpbW1lciBhdXNyZWljaGVuZCB2aWVsZSBEYXRlbiBiZW7DtnRpZ2VuLCB1bSBlaW4genV2ZXJsw6Rzc2lnZXMgUmVzdWx0YXQgbGllZmVybiB6dSBrw7ZubmVuLCB1bmQgZGFzcyBkZXIgU3VjaHRlcm0gc2VsYnN0IGF1c3JlaWNoZW5kIG9mdCB2b3Jrb21tZW4gbXVzcy4gCgpXZXIgZGllIERva3VtZW50YXRpb24gdm9uIHRleHRzdGF0X3NpbWlsKCkgdW5kIHRleHRzdGF0X2Rpc3QoKSBhbnNjaGF1dCwgd2lyZCBmZXN0c3RlbGxlbiwgZGFzcyBlcyBkb3J0IGRlbiBldHdhcyBrcnlwdGlzY2hlbiBIaW53ZWlzIGF1ZiBkYXMgQXJndW1lbnQgJ21hcmdpbicgZ2lidC4gRGllc2VzIGhhdCB6d2VpIG3DtmdsaWNoZSBlcmluc3RlbGx1bmdlbjogJ2RvY3VtZW50cycgb2RlciAnZmVhdHVyZXMnLiBTdGVsbHQgbWFuIGhpZXIgJ2RvY3VtZW50cycgZWluLCB3ZXJkZW4gZGllIGJlc3Byb2NoZW5lbiBNZXRyaWtlbiBuaWNodCBhdWYgV8O2cnRlciwgc29uZGVybiBhdWYgVGV4dGUgYW5nZXdhbmR0LiBGb2xnZW5kIHBsb3R0ZW4gd2lyIGRpZSBUZXh0bsOkaGUgdmlhIEtvc2ludXPDpGhubGljaGtlaXQgKGhpZXIgYXVzZ2VoZW5kIHZvbSBlcnN0ZW4gUm9tYW4sICdBIENhc2Ugb2YgSWRlbnRpdHknKS4KCmBgYHtyfQphZWhubGljaGtlaXQudGV4dGUgPC0gZGF0YS5mcmFtZShUZXh0ID0gZmFjdG9yKGtvcnB1cy5zdGF0cyRUZXh0LCBsZXZlbHMgPSByZXYoa29ycHVzLnN0YXRzJFRleHQpKSwgYXMubWF0cml4KHRleHRzdGF0X3NpbWlsKG1laW5lLmRmbSwgc2VsZWN0aW9uID0gMywgbWFyZ2luID0gImRvY3VtZW50cyIsIG1ldGhvZCA9ICJjb3NpbmUiKSkpCmdncGxvdChhZWhubGljaGtlaXQudGV4dGUsIGFlcyhBLkNhc2Uub2YuSWRlbnRpdHksIFRleHQpKSArIGdlb21fcG9pbnQoc2l6ZSA9IDIuNSkgKyBnZ3RpdGxlKCJUZXh0LUtvc2ludXPDpGhubGljaGtlaXQgKGhpZXIgZsO8ciAnQSBDYXNlIG9mIElkZW50aXR5JykiKSArIHhsYWIoIktvc2ludW5zw6RobmxpY2hrZWl0IikKYGBgCgpXaWUgd2lyIHNlaGVuLCBpc3QgZGllIMOEaG5saWNoa2VpdCBkZXIgUm9tYW5lICdUaGUgUmVkLWhlYWRlZCBMZWFndWUnIHVuZCAnVGhlIEFkdmVudHVyZSBvZiB0aGUgQ29wcGVyIEJlZWNoZXMnIGV0d2FzIGdyw7bDn2VyLCBhbHMgZGllcyBiZWkgZGVuIGFuZGVyZW4gRXJ6w6RobHVuZ2VuIGRlciBGYWxsIGlzdC4KCgojIyMjIEtleW5lc3MKCkJlaSBkZXIgc29nLiBLZXluZXNzIGhhbmRlbHQgZXMgc2ljaCB1bSBlaW4gTWHDnyBmw7xyIGRpZSBEaXN0aW5rdGl2aXTDpHQgZWluZXMgQmVncmlmZnMgZsO8ciBlaW5lbiBiZXN0aW1tdGVuIFRleHQsIGFsc28gd2llIHN0YXJrIGRlciBCZWdyaWZmIGRlbiBqZXdlaWxpZ2VuIFRleHQgaW0gVmVyZ2xlaWNoIHp1bSBnZXNhbXRlbiBLb3JwdXMga2VubnplaWNobmV0LiBXw6RocmVuZCB3aXIgenV2b3IgZGllIERpc3Rhbnogdm9uIFfDtnJ0ZXJuIHVuZCBUZXh0ZW4genUgZWluYW5kZXIgdW50ZXJzdWNodCBoYWJlbiwgbWFjaHQgc2ljaCBkaWUgS2V5bmVzcyBkaWUgVmVydGVpbHVuZ3Now6R1Zmlna2VpdCB2b24gV8O2cnRlcm4gYXVmIFRleHRlIHp1bnV0emUsIG9obmUgZGVyZW4gUG9zaXRpb24genUgYmVyw7xja3NpY2h0aWdlbi4gS2V5bmVzcyBmdW5rdGlvbmllcnQgZGFoZXIgYXVjaCBtaXQgbMOkbmdlcmVuIFRleHRlbiBndXQsIHNvbGFuZ2UgZGllc2Ugc2ljaCBhdXNyZWljaGVuZCBtYXJrYW50IHVudGVyc2NoZWlkZW4uIEZvbGdlbmQgYmVyZWNobmVuIHdpciB6dW7DpGNoc3QgZGllIEtleW5lc3MgZsO8ciB2aWVyIFRleHRlIG1pdCBbdGV4dHN0YXRfa2V5bmVzcygpXShodHRwczovL2RvY3MucXVhbnRlZGEuaW8vcmVmZXJlbmNlL3RleHRzdGF0X2tleW5lc3MuaHRtbCkgdW5kIHBsb3R0ZW4gd2lyIGRpZXNlIEtleW5lc3MtU3RhdGlzdGlrZW4gZGFubiBmw7xyIHZpZXIgRXJ6w6RobHVuZ2VuIG1pdCBIaWxmZSB2b24gW3RleHRwbG90X2tleW5lc3MoKV0oaHR0cHM6Ly9kb2NzLnF1YW50ZWRhLmlvL3JlZmVyZW5jZS90ZXh0cGxvdF9rZXluZXNzLmh0bWwpLgoKYGBge3J9CmtleW5lc3MgPC0gdGV4dHN0YXRfa2V5bmVzcyhtZWluZS5kZm0sIHRhcmdldCA9ICJBIFNjYW5kYWwgaW4gQm9oZW1pYSIsIG1lYXN1cmUgPSAibHIiKQp0ZXh0cGxvdF9rZXluZXNzKGtleW5lc3MpCmtleW5lc3MgPC0gdGV4dHN0YXRfa2V5bmVzcyhtZWluZS5kZm0sIHRhcmdldCA9ICJBIENhc2Ugb2YgSWRlbnRpdHkiLCBtZWFzdXJlID0gImxyIikKdGV4dHBsb3Rfa2V5bmVzcyhrZXluZXNzKQprZXluZXNzIDwtIHRleHRzdGF0X2tleW5lc3MobWVpbmUuZGZtLCB0YXJnZXQgPSAiVGhlIEZpdmUgT3JhbmdlIFBpcHMiLCBtZWFzdXJlID0gImxyIikKdGV4dHBsb3Rfa2V5bmVzcyhrZXluZXNzKQprZXluZXNzIDwtIHRleHRzdGF0X2tleW5lc3MobWVpbmUuZGZtLCB0YXJnZXQgPSAiVGhlIEFkdmVudHVyZSBvZiB0aGUgTm9ibGUgQmFjaGVsb3IiLCBtZWFzdXJlID0gImxyIikKdGV4dHBsb3Rfa2V5bmVzcyhrZXluZXNzKQpgYGAKClNjaGF1dCBtYW4gc2ljaCBkaWUgdmllciBCZWlzcGllbHRleHRlIGVpbm1hbCBnZW5hdWVyIGFuLCBzbyB3aXJkIHNjaG5lbGwga2xhciwgZGFzcyBkaWUgQmVncmlmZmUgbWl0IGVpbmVtIGhvaGVuIEtleW5lc3MtV2VydCB0YXRzw6RjaGxpY2ggc2VociBkaXN0aW5rdGl2IGbDvHIgZGVuIGpld2VpbGlnZW4gVGV4dCBzaW5kLCBhbHNvIEJlZ3JpZmZlIHdpZSAnbWFqZXN0eScgdW5kICdwaG90b2dyYXBoJyB0YXRzw6RjaGxpY2ggbnVyIGluICdBIFNjYW5kYWwgaW4gQm9oZW1pYScgZWluZSBSb2xsZSBzcGllbGVuLiBXZW5pZyBkaXN0aW5rdGl2ZSBCZWdyaWZmZSBzaW5kIGhpbmdlZ2VuIHNvbGNoZSwgZGllIHp3YXIgaW4gYW5kZXJlbiBUZXh0ZW4sIG5pY2h0IGFiZXIgZGVtIFppZWx0ZXh0IHZvcmtvbW1lbi4gTsO8dHpsaWNoIHdpcmQgZGllc2UgRnVua3Rpb24gdm9yIGFsbGVtIGRhbm4sIHdlbm4gbWFuIFRleHRlIG5hY2ggZWluZW4gS3JpdGVyaXVtIHdpZSBNZWRpdW0sIFNwcmVjaGVyLCBQYXJ0ZWksIFplaXRwdW5rdCBvZGVyIG1hbnVlbGwgenVnZW9yZG5ldGUgSW5oYWx0c2thdGVnb3JpZSBncnVwcGllcnQuIAoKCiMjIyMgTGV4aWthbGlzY2hlIFZpZWxmYWx0CgpVbnRlciBNYcOfZW4gbGV4aWthbGlzY2hlciBWaWVsZmFsdCB2ZXJzdGVodCBtYW4gTWV0cmlrZW4sIHdlbGNoZSBkaWUgRGl2ZXJzaXTDpHQgZWluZXMgVGV4dGVzIG1pdCBCbGljayBhdWYgZGVuIFdvcnRnZWJyYXVjaCB3aWVkZXJnZWJlbi4gRWluIEJlaXNwaWVsIGlzdCBkaWUgYmVyZWl0cyBpbiBLYXBpdGVsIDEgYmVyZWNodGV0ZSBbVHlwLVRva2VuLVJlbGF0aW9uXShodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9UeXBlLVRva2VuLVJlbGF0aW9uKS4gRGllc2UgYmVzY2hyZWlidCBkaWUgV29ydHZpZWxmYWx0IHVuZCBnZWJlbiBzbyBhdWNoIEF1ZnNjaGx1c3Mgw7xiZXIgZGllIEtvbXBsZXhpdMOkdCBlaW5lcyBUZXh0ZXMuIFdpciBiZXJlY2huZW4gZm9sZ2VuZCB6YWhscmVpY2hlIE1ldHJpa2VuIGbDvHIgZGllIGxleGlrYWxpc2NoZSBEaXZlcnNpdMOkdCBkZXIgenfDtmxmIFJvbWFuZSBtaXQgbWl0IGRlciBGdW5rdGlvbiBbdGV4dHN0YXRfbGV4ZGl2KCldKGh0dHBzOi8vZG9jcy5xdWFudGVkYS5pby9yZWZlcmVuY2UvdGV4dHN0YXRfbGV4ZGl2Lmh0bWwpLgoKYGBge3J9CmxleGRpdmVyc2l0YWV0IDwtIHRleHRzdGF0X2xleGRpdihtZWluZS5kZm0pCmxleGRpdmVyc2l0YWV0CndyaXRlX2RlbGltKGxleGRpdmVyc2l0YWV0LCBwYXRoID0gImxleGRpdmVyc2l0YWV0LmNzdiIsIGRlbGltID0gIjsiKSAjIERhdGVpIGlzdCBFeGNlbC1rb21wYXRpYmVsCmBgYAoKQmVpIGVpbmVtIG9iZXJmbMOkY2hsaWNoZSBWZXJnbGVpY2ggZGVyIE1ldHJpa2VuIGbDpGxsdCBhdWYsIGRhc3Mgc2ljaCBkaWUgVGV4dGUgbmljaHQgc2VociBzdGFyayB1bnRlcnNjaGVpZGVuLCB3YXMgaWhyZSBqZXdlaWxpZ2UgbGV4aWthbGlzY2hlIFZpZWxmYWx0IGJldHJpZmZ0LCBnYW56IHVuYWJow6RuZ2lnIGRhdm9uLCB3ZWxjaGUgTWV0cmlrIHZlcndlbmRldCB3aXJkLiBEaWVzIGlzdCBuaWNodCB1bmJlZGluZ3QgdmVyd3VuZGVybGljaCwgZGEgZXMgc2ljaCB1bSBUZXh0ZSBkZXMgc2VsYmVuIEdlbnJlcyB1bmQgQXV0b3JzIGhhbmRlbHQuIEludGVyZXNzYW50ZXIgd2VyZGVuIHNvbGNoZSBNZXRyaWtlbiBkYW5uLCB3ZW5uIHdpciBzZWhyIHVudGVyc2NoaWVkbGljaGUgR2VucmVzIG9kZXIgQXV0b3JlbiB2ZXJnbGVpY2hlbiB3b2xsZW4sIGV0d2EgZGllIFByb2dyYW1tZW4gdm9uIFBhcnRlaWVuLCBUZXh0ZSBhdXMgdW50ZXJzY2hpZWRsaWNoZW4gTWVkaWVuLCBvZGVyIFR3ZWV0cyB2b24gdW50ZXJzY2hpZWRsaWNoZW4gTnV0emVybi4gCgoKIyMjIyBMZXNiYXJrZWl0c2luZGl6ZXMKCkVpbmUgd2VpdGVyZSBLbGFzc2Ugdm9uIFRleHQtTWV0cmlrZW4sIGRpZSBzaWNoIGbDvHIgZWluIERva3VtZW50IGF1ZmdydW5kIHNlaW5lciBXb3J0enVzYW1tZW5zZXR6dW5nIGJlcmVjaG5lbiBsYXNzZW4sIHNpbmQgZGllIHNvZy4gW0xlc2JhcmtlaXRzaW5kaXplc10oaHR0cHM6Ly9kZS5tLndpa2lwZWRpYS5vcmcvd2lraS9MZXNiYXJrZWl0c2luZGV4KS4gRGFydW50ZXIgdmVyc3RlaHQgbWFuIE1ldHJpa2VuLCBkaWUgYW5oYW5kIHZvbiB0ZXh0bGljaGVuIEVpZ2Vuc2NoYWZ0ZW4gZWluZW4gWmFobGVud2VydCBiZXJlY2huZW4sIGRlciBkaWUgTGVzZXNjaHdpZXJpZ2tlaXQgZWluZXMgRG9rdW1lbnRlcyBtw7ZnbGljaHN0IGFra3VyYXQgd2llZGVyZ2ViZW4gc29sbC4gQW53ZW5kdW5nIGZpbmRlbiBzb2xjaGUgSW5kaXplcyBldHdhIGltIEJpbGR1bmdzYmVyZWljaCwgd2VubiBlcyB1bSBkaWUgRnJhZ2UgZ2VodCwgd2VsY2hlcyBTY2h3aWVyaWdrZWl0c25pdmVhdSBlaW5lcyBUZXh0ZXMgZsO8ciBTY2jDvGxlciBhbmdlbWVzc2VuIGlzdCwgYWJlciBhdWNoIGluIGRlciDDtmZmZW50bGljaGVuIFZlcndhbHR1bmcsIHdlbm4gbcO2Z2xpY2hzdCBrbGFyZSB1bmQgenVnw6RuZ2xpY2hlIFNwcmFjaGUgYnNwdy4gYXVmIGVpbmVyIGJlcmjDtnJkbGljaGVuIFdlYnNpdGUgdmVyd2VuZGV0IHdlcmRlbiBzb2xsLiAKCkRpZSBLYWxrdWxhdGlvbiB6YWhscmVpY2hlciBMZXNiYXJrZWl0c2luZGl6ZXMgZXJmb2xndCBpbiBxdWFudGVkYSBtaXQgW3RleHRzdGF0X3JlYWRhYmlsaXR5KCldKGh0dHBzOi8vZG9jcy5xdWFudGVkYS5pby9yZWZlcmVuY2UvdGV4dHN0YXRfcmVhZGFiaWxpdHkuaHRtbCksIEF1Y2ggaGllciB3aXJkIGRhcyBFcmdlYm5pcyBpbiBlaW5lciBFeGNlbC1rb21wYXRpYmxlIENTVi1EYXRlaSBnZXNwZWljaGVydC4KCmBgYHtyfQpsZXNiYXJrZWl0IDwtIHRleHRzdGF0X3JlYWRhYmlsaXR5KGtvcnB1cykKbGVzYmFya2VpdAp3cml0ZV9kZWxpbShsZXNiYXJrZWl0LCBwYXRoID0gImxlc2JhcmtlaXQuY3N2IiwgZGVsaW0gPSAiOyIpICMgRGF0ZWkgaXN0IEV4Y2VsLWtvbXBhdGliZWwKYGBgCgpFaW4gc2Now7ZuZXMgQmVpc3BpZWwgZsO8ciBkZW4gTnV0emVuIHNvbGNoZXIgTWV0cmlrZW4gZmluZGV0IHNpY2ggaW4gZGVyIERva3VtZW50YXRpb24gZGVyIEZ1bmt0aW9uIHRleHRzdGF0X3JlYWRhYmlsaXR5KCkuIEhhdHRlIGRpZSBBbnRyaXR0c3JlZGUgdm9uIEdlb3JnZSBXYXNoaW5ndG9uIGltIEphaHIgMTc4OSBub2NoIGVpbmVuIEZsZXNoLUtpbmNhaWQtSW5kZXggdm9uIDI4LCBzbyBiZXRydWcgZGVyIFdlcnQgYmVpIGRlciBBbnRyaXR0c3JlZGUgdm9uIERvbmFsZCBUcnVtcCBpbiAyMDE3IG51ciBub2NoIDkgKHdhcyBhbGxlcmRpbmdzIGRlbSBUcmVuZCBzZWl0IE1pdHRlIGRlcyAyMC4gSmhkLiBlbnRzcHJpY2h0KS4gCgoKCgoK