1 Explorative Datenanalyse - Teil 1

In diesem Teil wollen wir uns auf die Interpretation und Kommunikation von Daten anhand von Grafiken fokussieren.

Als erster Schritt einer Datenanalyse steht stets das Einlesen von Daten, was wir an dieser Stelle aufgreifen wollen.

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.3.6      ✔ purrr   0.3.4 
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.2.1      ✔ stringr 1.4.1 
✔ readr   2.1.3      ✔ forcats 0.5.2 ── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()

1.1 Daten einlesen

Die Bibliothek readr aus tidyverse bietet die folgenden Hilfsfunktionen zum Einlesen von Dateien:

  • read_csv(): Komma separierte Dateien (CSV)

  • read_tsv(): Tabulator separierte Dateien (TSV)

  • read_delim(): generell separierte Dateien (inklusive CSV und TSV)

  • read_fwf(): Dateien mit fixierter Breite

  • read_table(): Leerzeichen separierte Dateien

  • read_log(): Log-Dateien

Die Funktionen sind hierbei selbstbeschreibend und Argumente einheitlich. Nach erfolgreichem Einlesen liegt eine Tabelle als tibble vor.

Zu den Lesefunktionen von Tabellen gibt es auch eine zugehörige Schreibfunktion: write_csv(), write_tsv() oder write_delim().

Zusätzlich können CSV-Tabellen mit write_excel_csv() in ein Excel-Format überführt werden.

Zum Einlesen von Excel-Dateien wird die Bibliothek readxl verwendet. Hierzu später mehr. Auch andere Formate können durch externe Bibliotheken eingelesen werden wie z. B. SPSS-, Stata- und SAS-Dateien durch die Bibliothek haven.

1.1.1 Die wichtigsten Parameter für die Funktionen zum Einlesen

  • col.names: ob die Datei eine Kopfzeile (Spaltennamen) hat (TRUE oder FALSE)

  • skip = x: überspringe x Reihen (nützlich wenn Reihen Kommentare enthalten)

  • für alle weiteren Argument: ?read_delim.

1.1.2 Die häufigsten Fehler beim Einlesen

Beim Einlesen treten folgende Fehler typischerweise auf:

  • Das Argument col.names wurde falsch eingegeben. Wenn es keine Spaltennamen gibt, muss explizit col.names = FALSE eingegeben werden.

  • Das Trennzeichen wurde falsch definiert oder die falsche Funktion zum Einlesen verwendet. Falls das Trennzeichen Ihrer Datei nicht explizit als read_* Funktion vorliegt, müssen Sie read_delim() verwenden und das Argument delim auf das Trennzeichen setzen.

  • Der Pfad zur Datei wurde falsch angegeben.

  • Die Datei hat eine untypische Struktur bzw. stellt keine einheitliche Tabelle dar.

Um diese Probleme zu erkennen, gibt es einige Möglichkeiten:

  • Falls beim Einlesen ein Fehler auftritt: den Fehler lesen und verstehen oder Fehler + R in eine Suchmaschine eingeben und nach einer Lösung suchen.

  • Falls die Datei eingelesen wurde, aber ihr Format nicht stimmt: lassen Sie sich den Dataframe ausgeben und prüfen Sie, ob die Kopfzeile richtig ist und ob die einzelnen Spalten tatsächlich getrennt sind. Falls nein, dann muss col.names oder delim angepasst werden.

  • Falls die Datei nicht gefunden wurde, wurde der Pfad zur Datei falsch angegeben. Dateien müssen immer relativ aus dem aktuellen Pfad angegeben werden. Schauen Sie unter RStudio, in welchem Ordner Sie sich befinden und verschieben Sie die Datei in diesen Ordner.

1.1.3 Beispiele

1.1.3.1 CSV-Dateien

Es ist immer ratsam, die Datei vorher anzuschauen, um Trennzeichen und Kopfzeile zu bestimmen.

In dem folgenden Beispiel liegt die Datei als typische CSV vor, d. h. es muss lediglich der Name angepasst werden:

huehner_gewicht <- read_csv("../resources/ChickWeight.csv")
Rows: 578 Columns: 4── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (4): weight, Time, Chick, Diet
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
huehner_gewicht

Sie sehen, dass die Datei direkt als tibble vorliegt.

Als Alternative hierzu sehen wir, dass read.table() mehrere Parameter benötigt:

huehner_gewicht2 <- read.table("../resources/ChickWeight.csv")
huehner_gewicht2

Wir sehen hier, dass kein richtiges Trennzeichen gesetzt wurde und somit alle Spalten zusammengesetzt wurden. Zusätzlich sehen wir, dass statt den Kopfzeilen lediglich V1 steht.

Um das zu beheben, muss sep und header gesetzt werden:

huehner_gewicht2 <- read.table("../resources/ChickWeight.csv", sep = ",", header = TRUE)
huehner_gewicht2

Nun liegt die Tabelle als vernünftiger Dataframe vor.

Die read_* Funktionen von tidyverse sind hiermit sehr einfach zu verwenden und lesen die Dateien direkt als tibble und nicht als data.frame ein.

1.1.3.2 Excel-Dateien

Um Excel-Dateien einzulesen, bedarf es der externen Bibliothek readxl.

Zum Installieren können wir wie gewohnt install.packages() verwenden:

install.packages("readxl")

Bei dem Start jeder R-Session muss dann wie gewohnt die Bibliothek geladen werden:

library(readxl)

Zum Einlesen der Excel-Datei kann nun read_excel() verwendet werden:

huehner_gewicht <- read_excel("../resources/ChickWeight.xlsx")
huehner_gewicht

Auch hier liegt nach dem Einlesen ein tibble vor.

1.2 Explorative Datenanalyse

Der sich wiederholende Zyklus für die explorative Datenanalyse besteht grob aus drei Schritten:

  1. Fragen über Daten erstellen

  2. Antworten durch Visualisierung, Transformation (und Modellierung) suchen

  3. Neue Erkenntnisse verwenden, um Fragen zu präzisieren oder neue Fragen zu generieren

Das Verständnis der Daten steht hierbei im Vordergrund und wird durch die Fragestellungen geleitet.

Typische wiederkehrende Fragen sind beispielsweise:

  1. Welche Variation liegt innerhalb der Variablen vor?

  2. Was für Zusammenhänge/Kovariation (Korrelation) gibt es zwischen den Variablen?

1.2.1 Variation

Variation ist die Tendenz, dass sich eine Variable von Beobachtung zu Beobachtung ändert:

  • Bei kontinuierlichen Variablen ergeben zwei Messungen meist (leicht) unterschiedliche Messwerte

  • In jeder Messung stecken (kleine) Messfehler, die sich von Messung zu Messung unterscheiden

  • Auch kategorische Variablen können sich unterscheiden (z. B. bei Messung unterschiedlicher Subjekte, an unterschiedlichen Zeiten, usw.)

  • Visualisierung der Verteilung ist hierbei essentiell

1.2.1.1 Kategorische Verteilungen

Kategorische Variablen sind beschränkt auf eine kleine Anzahl an Werten (Kategorien) und werden in R durch Vektoren von Faktoren oder Charaktere (Zeichenketten) beschrieben. Für die Darstellung der Verteilung von kategorischen Variablen verwenden wir Balkendiagramme (oder Tortendiagramme):

ggplot(data = diamonds) +
    geom_bar(mapping = aes(x = cut))

Die Höhe der Balken lässt sich wie zuvor mit count() berechnen:

diamonds %>%
    count(cut)

1.2.1.2 Kontinuierliche Verteilungen

Bei kontinuierlichen Variablen sind wir nicht auf endliche Kategorien beschränkt sondern auf Zahlen (oder Daten). Die Verteilung wird hierbei mit Hilfe von Histogrammen dargestellt:

ggplot(data = diamonds) + 
    geom_histogram(mapping = aes(x = carat), binwidth = 0.5)

Die Höhen der Klassen lassen sich ebenfalls mit count() und cut_width() berechnen, wobei in cut_width() die Klassenbreite angegeben werden muss:

diamonds %>% 
  count(cut_width(carat, 0.5))

Im Gegensatz zum Balkendiagramm werden hierbei die Daten in Klassen (bins) eingeordnet und für diese Klassen die Häufigkeiten berechnet:

  • bindwidth gibt hierbei die Breite der Klassen (relativ zu der x-Achse) an

  • Es lohnt sich, unterschiedliche Klassenbreiten zu betrachten:

kleine_diamanten <- diamonds %>%
    filter(carat < 3)

ggplot(data = kleine_diamanten, mapping = aes(x = carat)) +
    geom_histogram(binwidth = 0.1)

Um mehrere Histogramme (z. B. für unterschiedliche Gruppen) in einer Grafik anzuzeigen, lohnt es sich anstelle von geom_histogram() die Funktion geom_freqpoly() zu verwenden, welches Linien statt Blöcke verwendet:

ggplot(data = kleine_diamanten, mapping = aes(x = carat, colour = cut)) +
  geom_freqpoly(binwidth = 0.1)

Sie sehen vor allem, dass viele Diamanten mit einem idealen Schliff (cut) einen geringen Karatwert aufweisen.

1.2.1.3 Typische Fragen

Es zeigt sich, dass hohe Balken die typischen Werte einer Variable darstellen, kleine Werte weniger typische Werte. Fehlende Balken zeigen, dass diese Werte in den erhobenen Daten nicht vorliegen.

Folgende Fragen können interessante Erkenntnisse liefern:

  1. Welche Werte sind am häufigsten? Warum?

  2. Welche Werte sind selten? Wieso? Liegt das innerhalb der Erwartungen?

  3. Sehen Sie untypische Muster? Woher stammen diese?

Folgendes Histogramm soll als Beispiel gelten:

  • Warum gibt es mehr Diamanten mit ganzenzahligen Karatwerten und typischen Brüchen?

  • Warum sind rechts von einem Höchstwert mehr Diamanten als links von diesem?

  • Warum gibt es keine Diamanten mit mehr als 3 Karat?

ggplot(data = kleine_diamanten, mapping = aes(x = carat)) +
  geom_histogram(binwidth = 0.01)

1.2.1.4 Ausreißer

Ausreißer sind untypische Beobachtungen, die stark von dem Muster der Daten abweichen (besonders große oder kleine Werte).

In manchen Fällen ist es schwierig Ausreißer zu finden:

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y), binwidth = 0.5)

Die Grafiken ist hierbei untypisch weit, obwohl der Großteil der Daten zwischen 5 und 10 liegt. Das ist ein Indiz, dass ein Ausreißer vorliegt. Die Klassenhöhe der seltenen Beobachtungen (mit großen oder kleinen Werten) sind hierbei kaum zu erkennen.

Um diese Werte zu sehen, muss herangezoomt werden durch die Veränderung der y-Achse (mit coord_carstesian()):

ggplot(diamonds) + 
  geom_histogram(mapping = aes(x = y), binwidth = 0.5) +
  coord_cartesian(ylim = c(0, 50))

Mit Hilfe von filter() lassen sich diese Werte anzeigen:

Ob die Ausreißer Sinn ergeben, ist von der Variable abhängig. Der y-Wert beschreibt eine der Dimensionen (in mm) der Diamanten. Ein Wert von 0mm erscheint hierbei nicht möglich. Es ist auch fragwürdig, ob sehr große Werte korrekt sind oder Messfehler.

Typischerweise werden Analysen mit und ohne Ausreißer durchgeführt, um zu testen, ob diese einen Effekt haben.

Falls diese keinen Effekt haben, können sie entfernt werden. Ansonsten sollten diese nicht ohne Begründung entfernt oder ersetzt werden. In dem Fall müsste der Grund der Ausreißer gefunden werden.

1.2.2 Fehlende Werte

Bei Ausreißern gibt es mehrere Möglichkeiten, diese zu “beheben”:

  1. Die gesamte Reihe entfernen:
diamonds2 <- diamonds %>% 
  filter(between(y, 3, 20))

Das Problem hierbei ist, dass neben dem Ausreißer für eine Variable andere, valide Variablen der Beobachtungen ebenfalls verloren gehen.

  1. Ausreißer durch NA ersetzen. Hierbei helfen die mutate() und ifelse() Funktionen:
diamonds2 <- diamonds %>% 
  mutate(y = ifelse(y < 3 | y > 20, NA, y))

ifelse() hat drei Argumente:

  1. eine Bedingung als logischer Vektor (für welche Reihen soll etwas verändert werden)

  2. falls die Bedingung TRUE ist, dann wird der erste Teil zurückgegeben

  3. falls die Bedingung FALSE, dann wird der zweite Teil zurückgegeben

In diesem Fall werden also Einträge, bei denen y kleiner als 3 oder größer als 20 sind, durch NA ersetzt. Ansonsten bleibt der y-Wert gleich.

  1. Falls anstelle von Ausreißern fehlende Werte vorliegen, könnten diese auch durch beispielsweise den Median oder den Mittelwert ersetzt werden:

1.2.3 Zusammenhänge (Kovariation/Korrelation)

Variation beschreibt das Verhalten innerhalb einer Variable während Kovariation das Verhalten zwischen Variablen beschreibt.

Kovariation beschreibt also die Tendenz, dass zwei sich oder mehr Variablen in einem gemeinsamen Muster verändern

1.2.3.1 Kategorische und kontinuierliche Variablen

Verteilungen von kontinuierlichen Variablen, die nach Kategorien gruppiert wurden, hatten wir bereits mehrfach betrachtet.

In manchen Fällen sind die Muster der Verteilungen jedoch nicht gut einsehbar, wenn manche Gruppen weniger häufig auftreten:

ggplot(data = diamonds, mapping = aes(x = price)) + 
  geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)

ggplot(diamonds) + 
  geom_bar(mapping = aes(x = cut))

In diesem Fall bietet es sich an, die Dichte anstelle der Häufigkeiten darzustellen:

ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) + 
  geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)

Eine weitere Darstellungsmöglichkeit von kontiniuerlichen Daten sind die Boxplots, welche ebenfalls das Vergleichen von Verteilungen ermöglichen:

ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
  geom_boxplot()

Auch hier sehen wir, dass durchschnittlich die Diamanten mit gutem Schliff weniger Wert sind.

Falls die Reihenfolge der Darstellung der Kategorien verändert werden soll, wird die Funktion reorder() verwendet.

In diesem Fall ist die Darstellung der Gruppen sinnvoll, da die Kategorien nach dem cut geordnet sind (vom schlechtesten zum besten).

Einige kategorische Variablen wie cut sind also bereits geordnet. Die meisten jedoch sind nicht geordnet:

ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
  geom_boxplot()

Der Autotyp zum Beispiel lässt sich nicht wirklich ordnen.

Um dennoch einen Trend zu sehen, können die Kategorien beispielsweise aufsteigend mit reorder() angeordnet werden:

ggplot(data = mpg) +
  geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))

Die kategorische Variable class wird somit durch reorder() nach dem Median der Variable hwy angeordnet.

Bei langen Namen mag sich auch die Änderung der Achsen anbieten (durch coord_flip()).

1.2.3.2 Zwei kategorische Variablen

Um die Beziehung zweier kategorischer Variablen zu betrachte, müssen die geteilten Häufigkeiten betrachtet werden. Hierzu wird geom_count() verwendet:

ggplot(data = diamonds) +
  geom_count(mapping = aes(x = cut, y = color))

Die Häufigkeit der Farben und dem Schliff werden also durch die Größe der Punkte hervorgehoben.

Um diese Häufigkeiten zu berechnen, kann wieder count() verwendet werden:

diamonds %>% 
  count(color, cut)

Eine schönere Ansicht (heatmap) ist ebenfalls mit geom_title() möglich:

diamonds %>% 
  count(color, cut) %>%  
  ggplot(mapping = aes(x = color, y = cut)) +
    geom_tile(mapping = aes(fill = n))

Es gibt jedoch noch viele weitere externe Bibliotheken, mit denen heatmaps angenehmer erstellt werden können.

1.2.3.3 Zwei kontinuierliche Variablen

Streudiagramme sind bei der Darstellung des Zusammenhangs von zwei kontinuierlichen Variablen am besten geeignet.

Beispielsweise hatten wir bereits den exponentiellen Zusammenhang von Karat und Preis gesehen:

ggplot(data = diamonds) +
  geom_point(mapping = aes(x = carat, y = price))

Leider werden bei großen Datensätzen Streudiagramme weniger übersichtlich. Eine Möglichkeit wäre die Anpassung der Transparenz mit dem alpha Wert:

ggplot(data = diamonds) +
  geom_point(mapping = aes(x = carat, y = price), alpha = 1/100)

Eine weitere Möglichkeit wäre die Darstellung von Klassen und deren Häufigkeiten, ähnlich wie eine Heatmap:

ggplot(data = kleine_diamanten) +
  geom_bin2d(mapping = aes(x = carat, y = price))

# install.packages("hexbin")
ggplot(data = kleine_diamanten) +
  geom_hex(mapping = aes(x = carat, y = price))

Sie sehen, dass die Grafiken sich genauso einfach erstellen lassen wie ein Streudiagramm, auch wenn die Komplexität wesentlich höher ist.

Eine weitere Möglichkeit ist die Einordnung einer Variablen in Klassen und diese dann als kategorische Variable zu benutzen:

ggplot(data = kleine_diamanten, mapping = aes(x = carat, y = price)) + 
  geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))

Die Klassen werden hierbei durch eine Breite von 0.1 (wird in cut_width(x, breite festgelegt) abgetrennt.

Um die Proportionen der Breite anzupassen, kann statt cut_width() auch cut_number() verwendet werden:

ggplot(data = kleine_diamanten, mapping = aes(x = carat, y = price)) + 
  geom_boxplot(mapping = aes(group = cut_number(carat, 20)))

1.2.4 Muster

Muster (pattern) in den Daten zeigen Beziehungen auf. Es sollten sich stets die folgenden Fragen gestellt werden:

  • Ist das Muster durch Zufall entstanden?

  • Wie kann diese Beziehung beschrieben werden?

  • Wie stark ist diese Beziehung

  • Welche anderen Variablen könnten Einfluss auf diese Beziehung haben?

  • Verändert sich die Beziehung, wenn besondere Untergruppen betrachtet werden?

2 Aufgaben

In diesem Abschnitt soll ein neuer Datensatz betrachtet werden. Der Datensatz msleep behandelt das Schlafverhalten von Säugetieren und liegt uns als Excel-Datei vor. Für mehr Information tippen Sie wie gewohnt ?msleep.

library(tidyverse)

2.1 Übungsaufgaben

  1. Lesen Sie den Datensatz msleep.xlsx aus Moodle als tibble msleep in R ein. Schaffen Sie sich einen Überblick über den Datensatz.
  2. Betrachten Sie die Verteilungen der Variablen sleep_total und vore. Betrachten Sie auch die Gruppierung von sleep_total nach vore. Was fällt Ihnen auf?
  3. Betrachten Sie nun ebenfalls die Variable awake im Bezug auf vore. Entspricht die Verteilung Ihrer Erwartung im Vergleich zu dem sleep_total?
  4. Stellen Sie den Zusammenhang von awake und sleep_total grafisch dar. Was stellen Sie fest?
  5. Vergleichen Sie geom_violin() mit geom_histogram(), welches Facetten benutzt, um Gruppen darzustellen, und geom_freqpoly(), welches für jede Gruppe Farben einfügt. Verwenden Sie hierfür erneut die Variablen awake und vore.
  6. Stellen Sie die Verteilung von vore und conservation gemeinsam dar.
    1. Verwenden Sie hierfür zwei verschiedene Darstellungsarten.

    2. Welche Kombination tritt am häufigste/seltensten auf?

    3. Ändern Sie die Farben des Gradienten der vorherigen Grafik mit Hilfe von scale_fill_gradient(low = farbe1, high = farbe2).

  7. Stellen Sie die gemeinsame Verteilung von vore, brainwt und bodywt dar.
LS0tCnRpdGxlOiAiU3RhdGlzdGlrIHVuZCBEYXRhIFNjaWVuY2UgLSBFeHBsb3JhdGl2ZSBEYXRlbmFuYWx5c2UgMSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCi0tLQoKIyBFeHBsb3JhdGl2ZSBEYXRlbmFuYWx5c2UgLSBUZWlsIDEKCkluIGRpZXNlbSBUZWlsIHdvbGxlbiB3aXIgdW5zIGF1ZiBkaWUgSW50ZXJwcmV0YXRpb24gdW5kIEtvbW11bmlrYXRpb24Kdm9uIERhdGVuIGFuaGFuZCB2b24gR3JhZmlrZW4gZm9rdXNzaWVyZW4uCgpBbHMgZXJzdGVyIFNjaHJpdHQgZWluZXIgRGF0ZW5hbmFseXNlIHN0ZWh0IHN0ZXRzIGRhcyBFaW5sZXNlbiB2b24KRGF0ZW4sIHdhcyB3aXIgYW4gZGllc2VyIFN0ZWxsZSBhdWZncmVpZmVuIHdvbGxlbi4KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyBEYXRlbiBlaW5sZXNlbgoKRGllIEJpYmxpb3RoZWsgYHJlYWRyYCBhdXMgYHRpZHl2ZXJzZWAgYmlldGV0IGRpZSBmb2xnZW5kZW4KSGlsZnNmdW5rdGlvbmVuIHp1bSBFaW5sZXNlbiB2b24gRGF0ZWllbjoKCi0gICBgcmVhZF9jc3YoKWA6IEtvbW1hIHNlcGFyaWVydGUgRGF0ZWllbiAoQ1NWKQoKLSAgIGByZWFkX3RzdigpYDogVGFidWxhdG9yIHNlcGFyaWVydGUgRGF0ZWllbiAoVFNWKQoKLSAgIGByZWFkX2RlbGltKClgOiBnZW5lcmVsbCBzZXBhcmllcnRlIERhdGVpZW4gKGlua2x1c2l2ZSBDU1YgdW5kIFRTVikKCi0gICBgcmVhZF9md2YoKWA6IERhdGVpZW4gbWl0IGZpeGllcnRlciBCcmVpdGUKCi0gICBgcmVhZF90YWJsZSgpYDogTGVlcnplaWNoZW4gc2VwYXJpZXJ0ZSBEYXRlaWVuCgotICAgYHJlYWRfbG9nKClgOiBMb2ctRGF0ZWllbgoKRGllIEZ1bmt0aW9uZW4gc2luZCBoaWVyYmVpIHNlbGJzdGJlc2NocmVpYmVuZCB1bmQgQXJndW1lbnRlCmVpbmhlaXRsaWNoLiBOYWNoIGVyZm9sZ3JlaWNoZW0gRWlubGVzZW4gbGllZ3QgZWluZSBUYWJlbGxlIGFscyBgdGliYmxlYAp2b3IuCgpadSBkZW4gTGVzZWZ1bmt0aW9uZW4gdm9uIFRhYmVsbGVuIGdpYnQgZXMgYXVjaCBlaW5lIHp1Z2Vow7ZyaWdlClNjaHJlaWJmdW5rdGlvbjogYHdyaXRlX2NzdigpYCwgYHdyaXRlX3RzdigpYCBvZGVyIGB3cml0ZV9kZWxpbSgpYC4KClp1c8OkdHpsaWNoIGvDtm5uZW4gQ1NWLVRhYmVsbGVuIG1pdCBgd3JpdGVfZXhjZWxfY3N2KClgIGluIGVpbgpFeGNlbC1Gb3JtYXQgw7xiZXJmw7xocnQgd2VyZGVuLgoKWnVtIEVpbmxlc2VuIHZvbiBFeGNlbC1EYXRlaWVuIHdpcmQgZGllIEJpYmxpb3RoZWsgYHJlYWR4bGAgdmVyd2VuZGV0LgpIaWVyenUgc3DDpHRlciBtZWhyLiBBdWNoIGFuZGVyZSBGb3JtYXRlIGvDtm5uZW4gZHVyY2ggZXh0ZXJuZQpCaWJsaW90aGVrZW4gZWluZ2VsZXNlbiB3ZXJkZW4gd2llIHouIEIuIFNQU1MtLCBTdGF0YS0gdW5kIFNBUy1EYXRlaWVuCmR1cmNoIGRpZSBCaWJsaW90aGVrIGBoYXZlbmAuCgojIyMgRGllIHdpY2h0aWdzdGVuIFBhcmFtZXRlciBmw7xyIGRpZSBGdW5rdGlvbmVuIHp1bSBFaW5sZXNlbgoKLSAgIGBjb2wubmFtZXNgOiBvYiBkaWUgRGF0ZWkgZWluZSBLb3BmemVpbGUgKFNwYWx0ZW5uYW1lbikgaGF0IChgVFJVRWAKICAgIG9kZXIgYEZBTFNFYCkKCi0gICBgc2tpcCA9IHhgOiDDvGJlcnNwcmluZ2UgYHhgIFJlaWhlbiAobsO8dHpsaWNoIHdlbm4gUmVpaGVuIEtvbW1lbnRhcmUKICAgIGVudGhhbHRlbikKCi0gICBmw7xyIGFsbGUgd2VpdGVyZW4gQXJndW1lbnQ6IGA/cmVhZF9kZWxpbWAuCgojIyMgRGllIGjDpHVmaWdzdGVuIEZlaGxlciBiZWltIEVpbmxlc2VuCgpCZWltIEVpbmxlc2VuIHRyZXRlbiBmb2xnZW5kZSBGZWhsZXIgdHlwaXNjaGVyd2Vpc2UgYXVmOgoKLSAgIERhcyBBcmd1bWVudCBgY29sLm5hbWVzYCB3dXJkZSBmYWxzY2ggZWluZ2VnZWJlbi4gV2VubiBlcyBrZWluZQogICAgU3BhbHRlbm5hbWVuIGdpYnQsIG11c3MgZXhwbGl6aXQgYGNvbC5uYW1lcyA9IEZBTFNFYCBlaW5nZWdlYmVuCiAgICB3ZXJkZW4uCgotICAgRGFzIFRyZW5uemVpY2hlbiB3dXJkZSBmYWxzY2ggZGVmaW5pZXJ0IG9kZXIgZGllIGZhbHNjaGUgRnVua3Rpb24KICAgIHp1bSBFaW5sZXNlbiB2ZXJ3ZW5kZXQuIEZhbGxzIGRhcyBUcmVubnplaWNoZW4gSWhyZXIgRGF0ZWkgbmljaHQKICAgIGV4cGxpeml0IGFscyBgcmVhZF8qYCBGdW5rdGlvbiB2b3JsaWVndCwgbcO8c3NlbiBTaWUgYHJlYWRfZGVsaW0oKWAKICAgIHZlcndlbmRlbiB1bmQgZGFzIEFyZ3VtZW50IGBkZWxpbWAgYXVmIGRhcyBUcmVubnplaWNoZW4gc2V0emVuLgoKLSAgIERlciBQZmFkIHp1ciBEYXRlaSB3dXJkZSBmYWxzY2ggYW5nZWdlYmVuLgoKLSAgIERpZSBEYXRlaSBoYXQgZWluZSB1bnR5cGlzY2hlIFN0cnVrdHVyIGJ6dy4gc3RlbGx0IGtlaW5lCiAgICBlaW5oZWl0bGljaGUgVGFiZWxsZSBkYXIuCgpVbSBkaWVzZSBQcm9ibGVtZSB6dSBlcmtlbm5lbiwgZ2lidCBlcyBlaW5pZ2UgTcO2Z2xpY2hrZWl0ZW46CgotICAgRmFsbHMgYmVpbSBFaW5sZXNlbiBlaW4gRmVobGVyIGF1ZnRyaXR0OiBkZW4gRmVobGVyIGxlc2VuIHVuZAogICAgdmVyc3RlaGVuIG9kZXIgRmVobGVyICsgUiBpbiBlaW5lIFN1Y2htYXNjaGluZSBlaW5nZWJlbiB1bmQgbmFjaAogICAgZWluZXIgTMO2c3VuZyBzdWNoZW4uCgotICAgRmFsbHMgZGllIERhdGVpIGVpbmdlbGVzZW4gd3VyZGUsIGFiZXIgaWhyIEZvcm1hdCBuaWNodCBzdGltbXQ6CiAgICBsYXNzZW4gU2llIHNpY2ggZGVuIERhdGFmcmFtZSBhdXNnZWJlbiB1bmQgcHLDvGZlbiBTaWUsIG9iIGRpZQogICAgS29wZnplaWxlIHJpY2h0aWcgaXN0IHVuZCBvYiBkaWUgZWluemVsbmVuIFNwYWx0ZW4gdGF0c8OkY2hsaWNoCiAgICBnZXRyZW5udCBzaW5kLiBGYWxscyBuZWluLCBkYW5uIG11c3MgYGNvbC5uYW1lc2Agb2RlciBgZGVsaW1gCiAgICBhbmdlcGFzc3Qgd2VyZGVuLgoKLSAgIEZhbGxzIGRpZSBEYXRlaSBuaWNodCBnZWZ1bmRlbiB3dXJkZSwgd3VyZGUgZGVyIFBmYWQgenVyIERhdGVpCiAgICBmYWxzY2ggYW5nZWdlYmVuLiBEYXRlaWVuIG3DvHNzZW4gaW1tZXIgcmVsYXRpdiBhdXMgZGVtIGFrdHVlbGxlbgogICAgUGZhZCBhbmdlZ2ViZW4gd2VyZGVuLiBTY2hhdWVuIFNpZSB1bnRlciBSU3R1ZGlvLCBpbiB3ZWxjaGVtIE9yZG5lcgogICAgU2llIHNpY2ggYmVmaW5kZW4gdW5kIHZlcnNjaGllYmVuIFNpZSBkaWUgRGF0ZWkgaW4gZGllc2VuIE9yZG5lci4KCiMjIyBCZWlzcGllbGUKCiMjIyMgQ1NWLURhdGVpZW4KCkVzIGlzdCBpbW1lciByYXRzYW0sIGRpZSBEYXRlaSB2b3JoZXIgYW56dXNjaGF1ZW4sIHVtIFRyZW5uemVpY2hlbiB1bmQKS29wZnplaWxlIHp1IGJlc3RpbW1lbi4KCkluIGRlbSBmb2xnZW5kZW4gQmVpc3BpZWwgbGllZ3QgZGllIERhdGVpIGFscyB0eXBpc2NoZSBgQ1NWYCB2b3IsIGQuIGguCmVzIG11c3MgbGVkaWdsaWNoIGRlciBOYW1lIGFuZ2VwYXNzdCB3ZXJkZW46CgpgYGB7cn0KaHVlaG5lcl9nZXdpY2h0IDwtIHJlYWRfY3N2KCIuLi9yZXNvdXJjZXMvQ2hpY2tXZWlnaHQuY3N2IikKaHVlaG5lcl9nZXdpY2h0CmBgYAoKU2llIHNlaGVuLCBkYXNzIGRpZSBEYXRlaSBkaXJla3QgYWxzIGB0aWJibGVgIHZvcmxpZWd0LgoKQWxzIEFsdGVybmF0aXZlIGhpZXJ6dSBzZWhlbiB3aXIsIGRhc3MgYHJlYWQudGFibGUoKWAgbWVocmVyZSBQYXJhbWV0ZXIKYmVuw7Z0aWd0OgoKYGBge3J9Cmh1ZWhuZXJfZ2V3aWNodDIgPC0gcmVhZC50YWJsZSgiLi4vcmVzb3VyY2VzL0NoaWNrV2VpZ2h0LmNzdiIpCmh1ZWhuZXJfZ2V3aWNodDIKYGBgCgpXaXIgc2VoZW4gaGllciwgZGFzcyBrZWluIHJpY2h0aWdlcyBUcmVubnplaWNoZW4gZ2VzZXR6dCB3dXJkZSB1bmQgc29taXQKYWxsZSBTcGFsdGVuIHp1c2FtbWVuZ2VzZXR6dCB3dXJkZW4uIFp1c8OkdHpsaWNoIHNlaGVuIHdpciwgZGFzcyBzdGF0dApkZW4gS29wZnplaWxlbiBsZWRpZ2xpY2ggYFYxYCBzdGVodC4KClVtIGRhcyB6dSBiZWhlYmVuLCBtdXNzIGBzZXBgIHVuZCBgaGVhZGVyYCBnZXNldHp0IHdlcmRlbjoKCmBgYHtyfQpodWVobmVyX2dld2ljaHQyIDwtIHJlYWQudGFibGUoIi4uL3Jlc291cmNlcy9DaGlja1dlaWdodC5jc3YiLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUpCmh1ZWhuZXJfZ2V3aWNodDIKYGBgCgpOdW4gbGllZ3QgZGllIFRhYmVsbGUgYWxzIHZlcm7DvG5mdGlnZXIgRGF0YWZyYW1lIHZvci4KCkRpZSBgcmVhZF8qYCBGdW5rdGlvbmVuIHZvbiBgdGlkeXZlcnNlYCBzaW5kIGhpZXJtaXQgc2VociBlaW5mYWNoIHp1CnZlcndlbmRlbiB1bmQgbGVzZW4gZGllIERhdGVpZW4gZGlyZWt0IGFscyBgdGliYmxlYCB1bmQgbmljaHQgYWxzCmBkYXRhLmZyYW1lYCBlaW4uCgojIyMjIEV4Y2VsLURhdGVpZW4KClVtIEV4Y2VsLURhdGVpZW4gZWluenVsZXNlbiwgYmVkYXJmIGVzIGRlciBleHRlcm5lbiBCaWJsaW90aGVrIGByZWFkeGxgLgoKWnVtIEluc3RhbGxpZXJlbiBrw7ZubmVuIHdpciB3aWUgZ2V3b2hudCBgaW5zdGFsbC5wYWNrYWdlcygpYCB2ZXJ3ZW5kZW46CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQpgYGAKCkJlaSBkZW0gU3RhcnQgamVkZXIgUi1TZXNzaW9uIG11c3MgZGFubiB3aWUgZ2V3b2hudCBkaWUgQmlibGlvdGhlawpnZWxhZGVuIHdlcmRlbjoKCmBgYHtyfQpsaWJyYXJ5KHJlYWR4bCkKYGBgCgpadW0gRWlubGVzZW4gZGVyIEV4Y2VsLURhdGVpIGthbm4gbnVuIGByZWFkX2V4Y2VsKClgIHZlcndlbmRldCB3ZXJkZW46CgpgYGB7cn0KaHVlaG5lcl9nZXdpY2h0IDwtIHJlYWRfZXhjZWwoIi4uL3Jlc291cmNlcy9DaGlja1dlaWdodC54bHN4IikKaHVlaG5lcl9nZXdpY2h0CmBgYAoKQXVjaCBoaWVyIGxpZWd0IG5hY2ggZGVtIEVpbmxlc2VuIGVpbiBgdGliYmxlYCB2b3IuCgojIyBFeHBsb3JhdGl2ZSBEYXRlbmFuYWx5c2UKCkRlciBzaWNoIHdpZWRlcmhvbGVuZGUgWnlrbHVzIGbDvHIgZGllIGV4cGxvcmF0aXZlIERhdGVuYW5hbHlzZSBiZXN0ZWh0Cmdyb2IgYXVzIGRyZWkgU2Nocml0dGVuOgoKMS4gIEZyYWdlbiDDvGJlciBEYXRlbiBlcnN0ZWxsZW4KCjIuICBBbnR3b3J0ZW4gZHVyY2ggVmlzdWFsaXNpZXJ1bmcsIFRyYW5zZm9ybWF0aW9uICh1bmQgTW9kZWxsaWVydW5nKQogICAgc3VjaGVuCgozLiAgTmV1ZSBFcmtlbm50bmlzc2UgdmVyd2VuZGVuLCB1bSBGcmFnZW4genUgcHLDpHppc2llcmVuIG9kZXIgbmV1ZQogICAgRnJhZ2VuIHp1IGdlbmVyaWVyZW4KCkRhcyBWZXJzdMOkbmRuaXMgZGVyIERhdGVuIHN0ZWh0IGhpZXJiZWkgaW0gVm9yZGVyZ3J1bmQgdW5kIHdpcmQgZHVyY2gKZGllIEZyYWdlc3RlbGx1bmdlbiBnZWxlaXRldC4KClR5cGlzY2hlIHdpZWRlcmtlaHJlbmRlIEZyYWdlbiBzaW5kIGJlaXNwaWVsc3dlaXNlOgoKMS4gIFdlbGNoZSBWYXJpYXRpb24gbGllZ3QgaW5uZXJoYWxiIGRlciBWYXJpYWJsZW4gdm9yPwoKMi4gIFdhcyBmw7xyIFp1c2FtbWVuaMOkbmdlL0tvdmFyaWF0aW9uIChLb3JyZWxhdGlvbikgZ2lidCBlcyB6d2lzY2hlbiBkZW4KICAgIFZhcmlhYmxlbj8KCiMjIyBWYXJpYXRpb24KClZhcmlhdGlvbiBpc3QgZGllIFRlbmRlbnosIGRhc3Mgc2ljaCBlaW5lIFZhcmlhYmxlIHZvbiBCZW9iYWNodHVuZyB6dQpCZW9iYWNodHVuZyDDpG5kZXJ0OgoKLSAgIEJlaSBrb250aW51aWVybGljaGVuIFZhcmlhYmxlbiBlcmdlYmVuIHp3ZWkgTWVzc3VuZ2VuIG1laXN0IChsZWljaHQpCiAgICB1bnRlcnNjaGllZGxpY2hlIE1lc3N3ZXJ0ZQoKLSAgIEluIGplZGVyIE1lc3N1bmcgc3RlY2tlbiAoa2xlaW5lKSBNZXNzZmVobGVyLCBkaWUgc2ljaCB2b24gTWVzc3VuZwogICAgenUgTWVzc3VuZyB1bnRlcnNjaGVpZGVuCgotICAgQXVjaCBrYXRlZ29yaXNjaGUgVmFyaWFibGVuIGvDtm5uZW4gc2ljaCB1bnRlcnNjaGVpZGVuICh6LiBCLiBiZWkKICAgIE1lc3N1bmcgdW50ZXJzY2hpZWRsaWNoZXIgU3ViamVrdGUsIGFuIHVudGVyc2NoaWVkbGljaGVuIFplaXRlbiwKICAgIHVzdy4pCgotICAgVmlzdWFsaXNpZXJ1bmcgZGVyIFZlcnRlaWx1bmcgaXN0IGhpZXJiZWkgZXNzZW50aWVsbAoKIyMjIyBLYXRlZ29yaXNjaGUgVmVydGVpbHVuZ2VuCgpLYXRlZ29yaXNjaGUgVmFyaWFibGVuIHNpbmQgYmVzY2hyw6Rua3QgYXVmIGVpbmUga2xlaW5lIEFuemFobCBhbiBXZXJ0ZW4KKEthdGVnb3JpZW4pIHVuZCB3ZXJkZW4gaW4gUiBkdXJjaCBWZWt0b3JlbiB2b24gRmFrdG9yZW4gb2RlciBDaGFyYWt0ZXJlCihaZWljaGVua2V0dGVuKSBiZXNjaHJpZWJlbi4gRsO8ciBkaWUgRGFyc3RlbGx1bmcgZGVyIFZlcnRlaWx1bmcgdm9uCmthdGVnb3Jpc2NoZW4gVmFyaWFibGVuIHZlcndlbmRlbiB3aXIgQmFsa2VuZGlhZ3JhbW1lIChvZGVyClRvcnRlbmRpYWdyYW1tZSk6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcykgKwoJZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gY3V0KSkKYGBgCgpEaWUgSMO2aGUgZGVyIEJhbGtlbiBsw6Rzc3Qgc2ljaCB3aWUgenV2b3IgbWl0IGBjb3VudCgpYCBiZXJlY2huZW46CgpgYGB7cn0KZGlhbW9uZHMgJT4lCgljb3VudChjdXQpCmBgYAoKIyMjIyBLb250aW51aWVybGljaGUgVmVydGVpbHVuZ2VuCgpCZWkga29udGludWllcmxpY2hlbiBWYXJpYWJsZW4gc2luZCB3aXIgbmljaHQgYXVmIGVuZGxpY2hlIEthdGVnb3JpZW4KYmVzY2hyw6Rua3Qgc29uZGVybiBhdWYgWmFobGVuIChvZGVyIERhdGVuKS4gRGllIFZlcnRlaWx1bmcgd2lyZCBoaWVyYmVpCm1pdCBIaWxmZSB2b24gSGlzdG9ncmFtbWVuIGRhcmdlc3RlbGx0OgoKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsgCglnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSBjYXJhdCksIGJpbndpZHRoID0gMC41KQpgYGAKCkRpZSBIw7ZoZW4gZGVyIEtsYXNzZW4gbGFzc2VuIHNpY2ggZWJlbmZhbGxzIG1pdCBgY291bnQoKWAgdW5kCmBjdXRfd2lkdGgoKWAgYmVyZWNobmVuLCB3b2JlaSBpbiBgY3V0X3dpZHRoKClgIGRpZSBLbGFzc2VuYnJlaXRlCmFuZ2VnZWJlbiB3ZXJkZW4gbXVzczoKCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY3V0X3dpZHRoKGNhcmF0LCAwLjUpKQpgYGAKCkltIEdlZ2Vuc2F0eiB6dW0gQmFsa2VuZGlhZ3JhbW0gd2VyZGVuIGhpZXJiZWkgZGllIERhdGVuIGluIEtsYXNzZW4KKGBiaW5zYCkgZWluZ2VvcmRuZXQgdW5kIGbDvHIgZGllc2UgS2xhc3NlbiBkaWUgSMOkdWZpZ2tlaXRlbiBiZXJlY2huZXQ6CgotICAgYGJpbmR3aWR0aGAgZ2lidCBoaWVyYmVpIGRpZSBCcmVpdGUgZGVyIEtsYXNzZW4gKHJlbGF0aXYgenUgZGVyCiAgICB4LUFjaHNlKSBhbgoKLSAgIEVzIGxvaG50IHNpY2gsIHVudGVyc2NoaWVkbGljaGUgS2xhc3NlbmJyZWl0ZW4genUgYmV0cmFjaHRlbjoKCmBgYHtyfQprbGVpbmVfZGlhbWFudGVuIDwtIGRpYW1vbmRzICU+JQoJZmlsdGVyKGNhcmF0IDwgMykKCmdncGxvdChkYXRhID0ga2xlaW5lX2RpYW1hbnRlbiwgbWFwcGluZyA9IGFlcyh4ID0gY2FyYXQpKSArCglnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMSkKYGBgCgpVbSBtZWhyZXJlIEhpc3RvZ3JhbW1lICh6LiBCLiBmw7xyIHVudGVyc2NoaWVkbGljaGUgR3J1cHBlbikgaW4gZWluZXIKR3JhZmlrIGFuenV6ZWlnZW4sIGxvaG50IGVzIHNpY2ggYW5zdGVsbGUgdm9uIGBnZW9tX2hpc3RvZ3JhbSgpYCBkaWUKRnVua3Rpb24gYGdlb21fZnJlcXBvbHkoKWAgenUgdmVyd2VuZGVuLCB3ZWxjaGVzIExpbmllbiBzdGF0dCBCbMO2Y2tlCnZlcndlbmRldDoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGtsZWluZV9kaWFtYW50ZW4sIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCBjb2xvdXIgPSBjdXQpKSArCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMSkKYGBgCgpTaWUgc2VoZW4gdm9yIGFsbGVtLCBkYXNzIHZpZWxlIERpYW1hbnRlbiBtaXQgZWluZW0gaWRlYWxlbiBTY2hsaWZmCihgY3V0YCkgZWluZW4gZ2VyaW5nZW4gS2FyYXR3ZXJ0IGF1ZndlaXNlbi4KCiMjIyMgVHlwaXNjaGUgRnJhZ2VuCgpFcyB6ZWlndCBzaWNoLCBkYXNzIGhvaGUgQmFsa2VuIGRpZSB0eXBpc2NoZW4gV2VydGUgZWluZXIgVmFyaWFibGUKZGFyc3RlbGxlbiwga2xlaW5lIFdlcnRlIHdlbmlnZXIgdHlwaXNjaGUgV2VydGUuIEZlaGxlbmRlIEJhbGtlbiB6ZWlnZW4sCmRhc3MgZGllc2UgV2VydGUgaW4gZGVuIGVyaG9iZW5lbiBEYXRlbiBuaWNodCB2b3JsaWVnZW4uCgpGb2xnZW5kZSBGcmFnZW4ga8O2bm5lbiBpbnRlcmVzc2FudGUgRXJrZW5udG5pc3NlIGxpZWZlcm46CgoxLiAgV2VsY2hlIFdlcnRlIHNpbmQgYW0gaMOkdWZpZ3N0ZW4/IFdhcnVtPwoKMi4gIFdlbGNoZSBXZXJ0ZSBzaW5kIHNlbHRlbj8gV2llc28/IExpZWd0IGRhcyBpbm5lcmhhbGIgZGVyCiAgICBFcndhcnR1bmdlbj8KCjMuICBTZWhlbiBTaWUgdW50eXBpc2NoZSBNdXN0ZXI/IFdvaGVyIHN0YW1tZW4gZGllc2U/CgpGb2xnZW5kZXMgSGlzdG9ncmFtbSBzb2xsIGFscyBCZWlzcGllbCBnZWx0ZW46CgotICAgV2FydW0gZ2lidCBlcyBtZWhyIERpYW1hbnRlbiBtaXQgZ2FuemVuemFobGlnZW4gS2FyYXR3ZXJ0ZW4gdW5kCiAgICB0eXBpc2NoZW4gQnLDvGNoZW4/CgotICAgV2FydW0gc2luZCByZWNodHMgdm9uIGVpbmVtIEjDtmNoc3R3ZXJ0IG1laHIgRGlhbWFudGVuIGFscyBsaW5rcyB2b24KICAgIGRpZXNlbT8KCi0gICBXYXJ1bSBnaWJ0IGVzIGtlaW5lIERpYW1hbnRlbiBtaXQgbWVociBhbHMgMyBLYXJhdD8KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGtsZWluZV9kaWFtYW50ZW4sIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSkKYGBgCgojIyMjIEF1c3JlacOfZXIKCkF1c3JlacOfZXIgc2luZCB1bnR5cGlzY2hlIEJlb2JhY2h0dW5nZW4sIGRpZSBzdGFyayB2b24gZGVtIE11c3RlciBkZXIKRGF0ZW4gYWJ3ZWljaGVuIChiZXNvbmRlcnMgZ3Jvw59lIG9kZXIga2xlaW5lIFdlcnRlKS4KCkluIG1hbmNoZW4gRsOkbGxlbiBpc3QgZXMgc2Nod2llcmlnIEF1c3JlacOfZXIgenUgZmluZGVuOgoKYGBge3J9CmdncGxvdChkaWFtb25kcykgKyAKICBnZW9tX2hpc3RvZ3JhbShtYXBwaW5nID0gYWVzKHggPSB5KSwgYmlud2lkdGggPSAwLjUpCmBgYAoKRGllIEdyYWZpa2VuIGlzdCBoaWVyYmVpIHVudHlwaXNjaCB3ZWl0LCBvYndvaGwgZGVyIEdyb8OfdGVpbCBkZXIgRGF0ZW4Kendpc2NoZW4gNSB1bmQgMTAgbGllZ3QuIERhcyBpc3QgZWluIEluZGl6LCBkYXNzIGVpbiBBdXNyZWnDn2VyIHZvcmxpZWd0LgpEaWUgS2xhc3NlbmjDtmhlIGRlciBzZWx0ZW5lbiBCZW9iYWNodHVuZ2VuIChtaXQgZ3Jvw59lbiBvZGVyIGtsZWluZW4KV2VydGVuKSBzaW5kIGhpZXJiZWkga2F1bSB6dSBlcmtlbm5lbi4KClVtIGRpZXNlIFdlcnRlIHp1IHNlaGVuLCBtdXNzIGhlcmFuZ2V6b29tdCB3ZXJkZW4gZHVyY2ggZGllIFZlcsOkbmRlcnVuZwpkZXIgeS1BY2hzZSAobWl0IGBjb29yZF9jYXJzdGVzaWFuKClgKToKCmBgYHtyfQpnZ3Bsb3QoZGlhbW9uZHMpICsgCiAgZ2VvbV9oaXN0b2dyYW0obWFwcGluZyA9IGFlcyh4ID0geSksIGJpbndpZHRoID0gMC41KSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDUwKSkKYGBgCgpNaXQgSGlsZmUgdm9uIGBmaWx0ZXIoKWAgbGFzc2VuIHNpY2ggZGllc2UgV2VydGUgYW56ZWlnZW46CgpgYGB7cn0KZGlhbW9uZHMgJT4lCglmaWx0ZXIoeSA8PSAxIHwgeSA+PSAyMCkKYGBgCgpPYiBkaWUgQXVzcmVpw59lciBTaW5uIGVyZ2ViZW4sIGlzdCB2b24gZGVyIFZhcmlhYmxlIGFiaMOkbmdpZy4gRGVyCmB5YC1XZXJ0IGJlc2NocmVpYnQgZWluZSBkZXIgRGltZW5zaW9uZW4gKGluIG1tKSBkZXIgRGlhbWFudGVuLiBFaW4gV2VydAp2b24gMG1tIGVyc2NoZWludCBoaWVyYmVpIG5pY2h0IG3DtmdsaWNoLiBFcyBpc3QgYXVjaCBmcmFnd8O8cmRpZywgb2Igc2Vocgpncm/Dn2UgV2VydGUga29ycmVrdCBzaW5kIG9kZXIgTWVzc2ZlaGxlci4KClR5cGlzY2hlcndlaXNlIHdlcmRlbiBBbmFseXNlbiBtaXQgdW5kIG9obmUgQXVzcmVpw59lciBkdXJjaGdlZsO8aHJ0LCB1bQp6dSB0ZXN0ZW4sIG9iIGRpZXNlIGVpbmVuIEVmZmVrdCBoYWJlbi4KCkZhbGxzIGRpZXNlIGtlaW5lbiBFZmZla3QgaGFiZW4sIGvDtm5uZW4gc2llIGVudGZlcm50IHdlcmRlbi4gQW5zb25zdGVuCnNvbGx0ZW4gZGllc2UgbmljaHQgb2huZSBCZWdyw7xuZHVuZyBlbnRmZXJudCBvZGVyIGVyc2V0enQgd2VyZGVuLiBJbiBkZW0KRmFsbCBtw7xzc3RlIGRlciBHcnVuZCBkZXIgQXVzcmVpw59lciBnZWZ1bmRlbiB3ZXJkZW4uCgojIyMgRmVobGVuZGUgV2VydGUKCkJlaSBBdXNyZWnDn2VybiBnaWJ0IGVzIG1laHJlcmUgTcO2Z2xpY2hrZWl0ZW4sIGRpZXNlIHp1ICJiZWhlYmVuIjoKCjEuICBEaWUgZ2VzYW10ZSBSZWloZSBlbnRmZXJuZW46CgpgYGB7cn0KZGlhbW9uZHMyIDwtIGRpYW1vbmRzICU+JSAKICBmaWx0ZXIoYmV0d2Vlbih5LCAzLCAyMCkpCmBgYAoKRGFzIFByb2JsZW0gaGllcmJlaSBpc3QsIGRhc3MgbmViZW4gZGVtIEF1c3JlacOfZXIgZsO8ciBlaW5lIFZhcmlhYmxlCmFuZGVyZSwgdmFsaWRlIFZhcmlhYmxlbiBkZXIgQmVvYmFjaHR1bmdlbiBlYmVuZmFsbHMgdmVybG9yZW4gZ2VoZW4uCgoyLiAgQXVzcmVpw59lciBkdXJjaCBgTkFgIGVyc2V0emVuLiBIaWVyYmVpIGhlbGZlbiBkaWUgYG11dGF0ZSgpYCB1bmQKICAgIGBpZmVsc2UoKWAgRnVua3Rpb25lbjoKCmBgYHtyfQpkaWFtb25kczIgPC0gZGlhbW9uZHMgJT4lIAogIG11dGF0ZSh5ID0gaWZlbHNlKHkgPCAzIHwgeSA+IDIwLCBOQSwgeSkpCmBgYAoKYGlmZWxzZSgpYCBoYXQgZHJlaSBBcmd1bWVudGU6CgoxLiAgZWluZSBCZWRpbmd1bmcgYWxzIGxvZ2lzY2hlciBWZWt0b3IgKGbDvHIgd2VsY2hlIFJlaWhlbiBzb2xsIGV0d2FzCiAgICB2ZXLDpG5kZXJ0IHdlcmRlbikKCjIuICBmYWxscyBkaWUgQmVkaW5ndW5nIGBUUlVFYCBpc3QsIGRhbm4gd2lyZCBkZXIgZXJzdGUgVGVpbAogICAgenVyw7xja2dlZ2ViZW4KCjMuICBmYWxscyBkaWUgQmVkaW5ndW5nIGBGQUxTRWAsIGRhbm4gd2lyZCBkZXIgendlaXRlIFRlaWwgenVyw7xja2dlZ2ViZW4KCkluIGRpZXNlbSBGYWxsIHdlcmRlbiBhbHNvIEVpbnRyw6RnZSwgYmVpIGRlbmVuIGB5YCBrbGVpbmVyIGFscyAzIG9kZXIKZ3LDtsOfZXIgYWxzIDIwIHNpbmQsIGR1cmNoIGBOQWAgZXJzZXR6dC4gQW5zb25zdGVuIGJsZWlidCBkZXIgYHlgLVdlcnQKZ2xlaWNoLgoKMy4gIEZhbGxzIGFuc3RlbGxlIHZvbiBBdXNyZWnDn2VybiBmZWhsZW5kZSBXZXJ0ZSB2b3JsaWVnZW4sIGvDtm5udGVuCiAgICBkaWVzZSBhdWNoIGR1cmNoIGJlaXNwaWVsc3dlaXNlIGRlbiBNZWRpYW4gb2RlciBkZW4gTWl0dGVsd2VydAogICAgZXJzZXR6dCB3ZXJkZW46CgpgYGB7cn0KZGlhbW9uZHMyIDwtIGRpYW1vbmRzICU+JSAKICBtdXRhdGUoeSA9IGlmZWxzZShpcy5uYSh5KSwgbWVhbih5LCBuYS5ybSA9IFRSVUUpLCB5KSkKZGlhbW9uZHMyCmBgYAoKIyMjIFp1c2FtbWVuaMOkbmdlIChLb3ZhcmlhdGlvbi9Lb3JyZWxhdGlvbikKClZhcmlhdGlvbiBiZXNjaHJlaWJ0IGRhcyBWZXJoYWx0ZW4gaW5uZXJoYWxiIGVpbmVyIFZhcmlhYmxlIHfDpGhyZW5kCktvdmFyaWF0aW9uIGRhcyBWZXJoYWx0ZW4gendpc2NoZW4gVmFyaWFibGVuIGJlc2NocmVpYnQuCgpLb3ZhcmlhdGlvbiBiZXNjaHJlaWJ0IGFsc28gZGllIFRlbmRlbnosIGRhc3MgendlaSBzaWNoIG9kZXIgbWVocgpWYXJpYWJsZW4gaW4gZWluZW0gZ2VtZWluc2FtZW4gTXVzdGVyIHZlcsOkbmRlcm4KCiMjIyMgS2F0ZWdvcmlzY2hlIHVuZCBrb250aW51aWVybGljaGUgVmFyaWFibGVuCgpWZXJ0ZWlsdW5nZW4gdm9uIGtvbnRpbnVpZXJsaWNoZW4gVmFyaWFibGVuLCBkaWUgbmFjaCBLYXRlZ29yaWVuCmdydXBwaWVydCB3dXJkZW4sIGhhdHRlbiB3aXIgYmVyZWl0cyBtZWhyZmFjaCBiZXRyYWNodGV0LgoKSW4gbWFuY2hlbiBGw6RsbGVuIHNpbmQgZGllIE11c3RlciBkZXIgVmVydGVpbHVuZ2VuIGplZG9jaCBuaWNodCBndXQKZWluc2VoYmFyLCB3ZW5uIG1hbmNoZSBHcnVwcGVuIHdlbmlnZXIgaMOkdWZpZyBhdWZ0cmV0ZW46CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgbWFwcGluZyA9IGFlcyh4ID0gcHJpY2UpKSArIAogIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjdXQpLCBiaW53aWR0aCA9IDUwMCkKYGBgCgpgYGB7cn0KZ2dwbG90KGRpYW1vbmRzKSArIAogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGN1dCkpCmBgYAoKSW4gZGllc2VtIEZhbGwgYmlldGV0IGVzIHNpY2ggYW4sIGRpZSBEaWNodGUgYW5zdGVsbGUgZGVyIEjDpHVmaWdrZWl0ZW4KZGFyenVzdGVsbGVuOgoKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIG1hcHBpbmcgPSBhZXMoeCA9IHByaWNlLCB5ID0gLi5kZW5zaXR5Li4pKSArIAogIGdlb21fZnJlcXBvbHkobWFwcGluZyA9IGFlcyhjb2xvdXIgPSBjdXQpLCBiaW53aWR0aCA9IDUwMCkKYGBgCgpFaW5lIHdlaXRlcmUgRGFyc3RlbGx1bmdzbcO2Z2xpY2hrZWl0IHZvbiBrb250aW5pdWVybGljaGVuIERhdGVuIHNpbmQgZGllCkJveHBsb3RzLCB3ZWxjaGUgZWJlbmZhbGxzIGRhcyBWZXJnbGVpY2hlbiB2b24gVmVydGVpbHVuZ2VuIGVybcO2Z2xpY2hlbjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBwcmljZSkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCkF1Y2ggaGllciBzZWhlbiB3aXIsIGRhc3MgZHVyY2hzY2huaXR0bGljaCBkaWUgRGlhbWFudGVuIG1pdCBndXRlbQpTY2hsaWZmIHdlbmlnZXIgV2VydCBzaW5kLgoKRmFsbHMgZGllIFJlaWhlbmZvbGdlIGRlciBEYXJzdGVsbHVuZyBkZXIgS2F0ZWdvcmllbiB2ZXLDpG5kZXJ0IHdlcmRlbgpzb2xsLCB3aXJkIGRpZSBGdW5rdGlvbiBgcmVvcmRlcigpYCB2ZXJ3ZW5kZXQuCgpJbiBkaWVzZW0gRmFsbCBpc3QgZGllIERhcnN0ZWxsdW5nIGRlciBHcnVwcGVuIHNpbm52b2xsLCBkYSBkaWUKS2F0ZWdvcmllbiBuYWNoIGRlbSBgY3V0YCBnZW9yZG5ldCBzaW5kICh2b20gc2NobGVjaHRlc3RlbiB6dW0gYmVzdGVuKS4KCkVpbmlnZSBrYXRlZ29yaXNjaGUgVmFyaWFibGVuIHdpZSBgY3V0YCBzaW5kIGFsc28gYmVyZWl0cyBnZW9yZG5ldC4gRGllCm1laXN0ZW4gamVkb2NoIHNpbmQgbmljaHQgZ2VvcmRuZXQ6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGNsYXNzLCB5ID0gaHd5KSkgKwogIGdlb21fYm94cGxvdCgpCmBgYAoKRGVyIEF1dG90eXAgenVtIEJlaXNwaWVsIGzDpHNzdCBzaWNoIG5pY2h0IHdpcmtsaWNoIG9yZG5lbi4KClVtIGRlbm5vY2ggZWluZW4gVHJlbmQgenUgc2VoZW4sIGvDtm5uZW4gZGllIEthdGVnb3JpZW4gYmVpc3BpZWxzd2Vpc2UKYXVmc3RlaWdlbmQgbWl0IGByZW9yZGVyKClgIGFuZ2VvcmRuZXQgd2VyZGVuOgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9ib3hwbG90KG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIoY2xhc3MsIGh3eSwgRlVOID0gbWVkaWFuKSwgeSA9IGh3eSkpCmBgYAoKRGllIGthdGVnb3Jpc2NoZSBWYXJpYWJsZSBgY2xhc3NgIHdpcmQgc29taXQgZHVyY2ggYHJlb3JkZXIoKWAgbmFjaCBkZW0KTWVkaWFuIGRlciBWYXJpYWJsZSBgaHd5YCBhbmdlb3JkbmV0LgoKQmVpIGxhbmdlbiBOYW1lbiBtYWcgc2ljaCBhdWNoIGRpZSDDhG5kZXJ1bmcgZGVyIEFjaHNlbiBhbmJpZXRlbiAoZHVyY2gKYGNvb3JkX2ZsaXAoKWApLgoKIyMjIyBad2VpIGthdGVnb3Jpc2NoZSBWYXJpYWJsZW4KClVtIGRpZSBCZXppZWh1bmcgendlaWVyIGthdGVnb3Jpc2NoZXIgVmFyaWFibGVuIHp1IGJldHJhY2h0ZSwgbcO8c3NlbiBkaWUKZ2V0ZWlsdGVuIEjDpHVmaWdrZWl0ZW4gYmV0cmFjaHRldCB3ZXJkZW4uIEhpZXJ6dSB3aXJkIGBnZW9tX2NvdW50KClgCnZlcndlbmRldDoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzKSArCiAgZ2VvbV9jb3VudChtYXBwaW5nID0gYWVzKHggPSBjdXQsIHkgPSBjb2xvcikpCmBgYAoKRGllIEjDpHVmaWdrZWl0IGRlciBGYXJiZW4gdW5kIGRlbSBTY2hsaWZmIHdlcmRlbiBhbHNvIGR1cmNoIGRpZSBHcsO2w59lCmRlciBQdW5rdGUgaGVydm9yZ2Vob2Jlbi4KClVtIGRpZXNlIEjDpHVmaWdrZWl0ZW4genUgYmVyZWNobmVuLCBrYW5uIHdpZWRlciBgY291bnQoKWAgdmVyd2VuZGV0CndlcmRlbjoKCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY29sb3IsIGN1dCkKYGBgCgpFaW5lIHNjaMO2bmVyZSBBbnNpY2h0IChoZWF0bWFwKSBpc3QgZWJlbmZhbGxzIG1pdCBgZ2VvbV90aXRsZSgpYAptw7ZnbGljaDoKCmBgYHtyfQpkaWFtb25kcyAlPiUgCiAgY291bnQoY29sb3IsIGN1dCkgJT4lICAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY29sb3IsIHkgPSBjdXQpKSArCiAgICBnZW9tX3RpbGUobWFwcGluZyA9IGFlcyhmaWxsID0gbikpCmBgYAoKRXMgZ2lidCBqZWRvY2ggbm9jaCB2aWVsZSB3ZWl0ZXJlIGV4dGVybmUgQmlibGlvdGhla2VuLCBtaXQgZGVuZW4KaGVhdG1hcHMgYW5nZW5laG1lciBlcnN0ZWxsdCB3ZXJkZW4ga8O2bm5lbi4KCiMjIyMgWndlaSBrb250aW51aWVybGljaGUgVmFyaWFibGVuCgpTdHJldWRpYWdyYW1tZSBzaW5kIGJlaSBkZXIgRGFyc3RlbGx1bmcgZGVzIFp1c2FtbWVuaGFuZ3Mgdm9uIHp3ZWkKa29udGludWllcmxpY2hlbiBWYXJpYWJsZW4gYW0gYmVzdGVuIGdlZWlnbmV0LgoKQmVpc3BpZWxzd2Vpc2UgaGF0dGVuIHdpciBiZXJlaXRzIGRlbiBleHBvbmVudGllbGxlbiBadXNhbW1lbmhhbmcgdm9uCkthcmF0IHVuZCBQcmVpcyBnZXNlaGVuOgoKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKQpgYGAKCkxlaWRlciB3ZXJkZW4gYmVpIGdyb8OfZW4gRGF0ZW5zw6R0emVuIFN0cmV1ZGlhZ3JhbW1lIHdlbmlnZXIKw7xiZXJzaWNodGxpY2guIEVpbmUgTcO2Z2xpY2hrZWl0IHfDpHJlIGRpZSBBbnBhc3N1bmcgZGVyIFRyYW5zcGFyZW56IG1pdApkZW0gYGFscGhhYCBXZXJ0OgoKYGBge3J9CmdncGxvdChkYXRhID0gZGlhbW9uZHMpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpLCBhbHBoYSA9IDEvMTAwKQpgYGAKCkVpbmUgd2VpdGVyZSBNw7ZnbGljaGtlaXQgd8OkcmUgZGllIERhcnN0ZWxsdW5nIHZvbiBLbGFzc2VuIHVuZCBkZXJlbgpIw6R1Zmlna2VpdGVuLCDDpGhubGljaCB3aWUgZWluZSBIZWF0bWFwOgoKYGBge3J9CmdncGxvdChkYXRhID0ga2xlaW5lX2RpYW1hbnRlbikgKwogIGdlb21fYmluMmQobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpCiMgaW5zdGFsbC5wYWNrYWdlcygiaGV4YmluIikKZ2dwbG90KGRhdGEgPSBrbGVpbmVfZGlhbWFudGVuKSArCiAgZ2VvbV9oZXgobWFwcGluZyA9IGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSkpCmBgYAoKU2llIHNlaGVuLCBkYXNzIGRpZSBHcmFmaWtlbiBzaWNoIGdlbmF1c28gZWluZmFjaCBlcnN0ZWxsZW4gbGFzc2VuIHdpZQplaW4gU3RyZXVkaWFncmFtbSwgYXVjaCB3ZW5uIGRpZSBLb21wbGV4aXTDpHQgd2VzZW50bGljaCBow7ZoZXIgaXN0LgoKRWluZSB3ZWl0ZXJlIE3DtmdsaWNoa2VpdCBpc3QgZGllIEVpbm9yZG51bmcgZWluZXIgVmFyaWFibGVuIGluIEtsYXNzZW4KdW5kIGRpZXNlIGRhbm4gYWxzIGthdGVnb3Jpc2NoZSBWYXJpYWJsZSB6dSBiZW51dHplbjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGtsZWluZV9kaWFtYW50ZW4sIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKGdyb3VwID0gY3V0X3dpZHRoKGNhcmF0LCAwLjEpKSkKYGBgCgpEaWUgS2xhc3NlbiB3ZXJkZW4gaGllcmJlaSBkdXJjaCBlaW5lIEJyZWl0ZSB2b24gYDAuMWAgKHdpcmQgaW4KYGN1dF93aWR0aCh4LCBicmVpdGVgIGZlc3RnZWxlZ3QpIGFiZ2V0cmVubnQuCgpVbSBkaWUgUHJvcG9ydGlvbmVuIGRlciBCcmVpdGUgYW56dXBhc3Nlbiwga2FubiBzdGF0dCBgY3V0X3dpZHRoKClgIGF1Y2gKYGN1dF9udW1iZXIoKWAgdmVyd2VuZGV0IHdlcmRlbjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGtsZWluZV9kaWFtYW50ZW4sIG1hcHBpbmcgPSBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpKSArIAogIGdlb21fYm94cGxvdChtYXBwaW5nID0gYWVzKGdyb3VwID0gY3V0X251bWJlcihjYXJhdCwgMjApKSkKYGBgCgojIyMgTXVzdGVyCgpNdXN0ZXIgKHBhdHRlcm4pIGluIGRlbiBEYXRlbiB6ZWlnZW4gQmV6aWVodW5nZW4gYXVmLiBFcyBzb2xsdGVuIHNpY2gKc3RldHMgZGllIGZvbGdlbmRlbiBGcmFnZW4gZ2VzdGVsbHQgd2VyZGVuOgoKLSAgIElzdCBkYXMgTXVzdGVyIGR1cmNoIFp1ZmFsbCBlbnRzdGFuZGVuPwoKLSAgIFdpZSBrYW5uIGRpZXNlIEJlemllaHVuZyBiZXNjaHJpZWJlbiB3ZXJkZW4/CgotICAgV2llIHN0YXJrIGlzdCBkaWVzZSBCZXppZWh1bmcKCi0gICBXZWxjaGUgYW5kZXJlbiBWYXJpYWJsZW4ga8O2bm50ZW4gRWluZmx1c3MgYXVmIGRpZXNlIEJlemllaHVuZyBoYWJlbj8KCi0gICBWZXLDpG5kZXJ0IHNpY2ggZGllIEJlemllaHVuZywgd2VubiBiZXNvbmRlcmUgVW50ZXJncnVwcGVuIGJldHJhY2h0ZXQKICAgIHdlcmRlbj8KCiMgQXVmZ2FiZW4KCkluIGRpZXNlbSBBYnNjaG5pdHQgc29sbCBlaW4gbmV1ZXIgRGF0ZW5zYXR6IGJldHJhY2h0ZXQgd2VyZGVuLiBEZXIKRGF0ZW5zYXR6IGBtc2xlZXBgIGJlaGFuZGVsdCBkYXMgU2NobGFmdmVyaGFsdGVuIHZvbiBTw6R1Z2V0aWVyZW4gdW5kCmxpZWd0IHVucyBhbHMgRXhjZWwtRGF0ZWkgdm9yLiBGw7xyIG1laHIgSW5mb3JtYXRpb24gdGlwcGVuIFNpZSB3aWUKZ2V3b2hudCBgP21zbGVlcGAuCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMgw5xidW5nc2F1ZmdhYmVuCgoxLiAgTGVzZW4gU2llIGRlbiBEYXRlbnNhdHogYG1zbGVlcC54bHN4YCBhdXMgTW9vZGxlIGFscyBgdGliYmxlYAogICAgYG1zbGVlcGAgaW4gUiBlaW4uIFNjaGFmZmVuIFNpZSBzaWNoIGVpbmVuIMOcYmVyYmxpY2sgw7xiZXIgZGVuCiAgICBEYXRlbnNhdHouCjIuICBCZXRyYWNodGVuIFNpZSBkaWUgVmVydGVpbHVuZ2VuIGRlciBWYXJpYWJsZW4gYHNsZWVwX3RvdGFsYCB1bmQKICAgIGB2b3JlYC4gQmV0cmFjaHRlbiBTaWUgYXVjaCBkaWUgR3J1cHBpZXJ1bmcgdm9uIGBzbGVlcF90b3RhbGAgbmFjaAogICAgYHZvcmVgLiBXYXMgZsOkbGx0IElobmVuIGF1Zj8KMy4gIEJldHJhY2h0ZW4gU2llIG51biBlYmVuZmFsbHMgZGllIFZhcmlhYmxlIGBhd2FrZWAgaW0gQmV6dWcgYXVmCiAgICBgdm9yZWAuIEVudHNwcmljaHQgZGllIFZlcnRlaWx1bmcgSWhyZXIgRXJ3YXJ0dW5nIGltIFZlcmdsZWljaCB6dQogICAgZGVtIGBzbGVlcF90b3RhbGA/CjQuICBTdGVsbGVuIFNpZSBkZW4gWnVzYW1tZW5oYW5nIHZvbiBgYXdha2VgIHVuZCBgc2xlZXBfdG90YWxgIGdyYWZpc2NoCiAgICBkYXIuIFdhcyBzdGVsbGVuIFNpZSBmZXN0Pwo1LiAgVmVyZ2xlaWNoZW4gU2llIGBnZW9tX3Zpb2xpbigpYCBtaXQgYGdlb21faGlzdG9ncmFtKClgLCB3ZWxjaGVzCiAgICBGYWNldHRlbiBiZW51dHp0LCB1bSBHcnVwcGVuIGRhcnp1c3RlbGxlbiwgdW5kIGBnZW9tX2ZyZXFwb2x5KClgLAogICAgd2VsY2hlcyBmw7xyIGplZGUgR3J1cHBlIEZhcmJlbiBlaW5mw7xndC4gVmVyd2VuZGVuIFNpZSBoaWVyZsO8ciBlcm5ldXQKICAgIGRpZSBWYXJpYWJsZW4gYGF3YWtlYCB1bmQgYHZvcmVgLgo2LiAgU3RlbGxlbiBTaWUgZGllIFZlcnRlaWx1bmcgdm9uIGB2b3JlYCB1bmQgYGNvbnNlcnZhdGlvbmAgZ2VtZWluc2FtCiAgICBkYXIuCiAgICAxLiAgVmVyd2VuZGVuIFNpZSBoaWVyZsO8ciB6d2VpIHZlcnNjaGllZGVuZSBEYXJzdGVsbHVuZ3NhcnRlbi4KCiAgICAyLiAgV2VsY2hlIEtvbWJpbmF0aW9uIHRyaXR0IGFtIGjDpHVmaWdzdGUvc2VsdGVuc3RlbiBhdWY/CgogICAgMy4gIMOEbmRlcm4gU2llIGRpZSBGYXJiZW4gZGVzIEdyYWRpZW50ZW4gZGVyIHZvcmhlcmlnZW4gR3JhZmlrIG1pdAogICAgICAgIEhpbGZlIHZvbiBgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSBmYXJiZTEsIGhpZ2ggPSBmYXJiZTIpYC4KNy4gIFN0ZWxsZW4gU2llIGRpZSBnZW1laW5zYW1lIFZlcnRlaWx1bmcgdm9uIGB2b3JlYCwgYGJyYWlud3RgIHVuZAogICAgYGJvZHl3dGAgZGFyLgo=