1 Einschub Streudiagramme

Zuerst müssen wir erneut tidyverse laden, da wir eine neue Session angefangen haben:

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()

Sie haben bereits Streudiagramme kennengelernt:

ggplot(data = mpg) +
    geom_point(mapping = aes(x = displ, y = hwy))

Hier sehen wir beispielsweise den Zusammenhang von Hubraum und dem Kraftstoffverbrauch von einigen Autos.

Die Fragestellung ist hierbei immer, wie die eine Variable von der anderen Variable abhängt.

In der Statistik gibt es ebenfalls eine Maßzahl, die uns diesen Zusammenhang angibt: der Korrelationskoeffizient.

cor(mpg$displ, mpg$hwy)
[1] -0.76602

Wir halten fest:

2 Vertiefung von ggplot

2.1 Facets

Es lassen sich bereits durch Aesthetics Daten auf Grund von Kategorien visuell unterscheiden. So können wir in dem mpg Datensatz die unterschiedlichen Autotypen hervorheben:

ggplot(data = mpg) +
  geom_point(mapping = aes(x = hwy, y = displ, color = class))

Falls die einzelnen Kategorien getrennt betrachtet werden sollen, werden facets (Facetten/Gitter) verwendet.

Hierbei wird die Grafik für jede Kategorie in einem Gitter angezeigt. Falls eine einzelne Kategorie betrachtet werden soll, wird ein weiterer Layer mit der Funktion facet_wrap() hinzugefügt.

Als erstes Argument wird eine formula (ein spezielle Datenstruktur) erwartet, bei der das Tilde-Symbol ~ gefolgt von einem Variablennamen (unabhängige Variable/Kategorie) eingegeben wird.

ggplot(data = mpg) +
  geom_point(mapping = aes(x = hwy, y = displ)) +
  facet_wrap(facets = ~ class, nrow = 2)

Falls mehrere Kategorien gleichzeitig betrachtet werden sollen, dann wird ein Layer mit facet_grid() benötigt.

Es lässt sich mit facet_grid() ebenfalls eine Kategorie betrachten, jedoch sieht dies meist weniger kompakt aus als facet_wrap():

ggplot(data = mpg) +
  geom_point(mapping = aes(x = hwy, y = displ)) +
  facet_grid(facets = ~ class)

Um mehrere Kategorien gleichzeitig zu betrachten, wird bei facet_grid() ebenfalls eine formula angegeben.

In diesem Fall besteht die formula aus zwei Variablen; eine Variable vor der Tilde und eine Variable nach der Tilde.

Falls die Kraftstoffeffizienz (hwy) bezüglich des Hubraums betrachtet werden soll, wobei für jeden Autotypen und jeden Antriebstypen (Frontrad, Hinterrad, Vierrad) eine Grafik erstellt werden soll, so muss in der formula in facet_grid drv ~ class spezifiert werden:

ggplot(data = mpg) +
  geom_point(mapping = aes(x = hwy, y = displ)) +
  facet_grid(facets = drv ~ class)

2.2 Geometrische Objekte

2.2.1 ggplot Objekte

Es ist ebenfalls möglich die Grafiken von ggplot weiterzuverwenden, indem sie als Objekte gespeichert werden.

Jede Funktion von ggplot liefert ein geometrisches (geom) Objekt zurück, mit dem weitergearbeitet werden kann. Da es sich um geometrische Objekte handelt, entsprechen die Namen der Funktionen von ggplot dieser Namensgebung (z. B. geom_point für Streudiagramme oder geom_bar für Balkendiagramme/Barplots).

Statt die gesamte Grafik in einem Schritt durch die Verkettung von + und das Hinzufügen von Layern zu erstellen, können die Objekte/Grafiken auch zwischengespeichert werden:

graph1 <- ggplot(data = mpg)
graph1

graph2 <- graph1 + geom_point(mapping = aes(x = displ, y = hwy))
graph2

graph3 <- graph1 + geom_point(mapping = aes(x = displ, y = hwy, color = class))
graph3

graph2 + geom_smooth(mapping = aes(x = displ, y = hwy), method = "loess", formula = y ~ x)

Da sich in ggplot die Layer Schritt für Schritt hinzufügen lassen, können Sie so Ihren Code nach Bedarf besser strukturieren oder wiederverwenden.

Abhängig von dem visuellen/geometrischen Objekt, welches verwendet wird, ändert sich die Darstellung, auch wenn dieselben Variablen verwendet werden:

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy))


ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy))

Auf der einen Seite erhalten wir ein Streudiagramm von hwy und displ und auf der anderen eine Modell-Linie, die sich denselben Datenpunkten annähert.

Alle geom Funktionen benötigen einen mapping Parameter, allerdings unterscheiden sich die Aesthetics für jede Funktion.

So lässt sich beispielsweise nicht die Punktform der Linie von geom_smooth mit shape verändern:

ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy, shape = drv))
Warnung: Ignoring unknown aesthetics: shape

Die Linien werden für die Kategorien (Front, Hinter, Vierrad) gruppiert, allerdings ändern sich die Linien nicht.

Um die Aesthetic der Linien zu ändern, kann beispielsweise der Linientyp mit linetype verändert werden:

ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy, linetype = drv))

Nun sehen wir für jeden Antriebstypen eine andere Linienart.

Um mehr über die Aesthetics einer geom Funktion zu erfahren, kann immer die Hilfsfunktion ?geom_smooth (oder eine Suchmaschine) zu Rat gezogen werden.

Selbstverständlich lassen sich die unterschiedlichen Layer der geoms auch übereinanderlegen. So können ebenfalls Datenpunkte und Linien zugleich betrachtet werden:

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
  geom_point(mapping = aes(color = drv)) +
  geom_smooth(mapping = aes(linetype = drv, color = drv))

Sie sehen, dass die Linien die Datenpunkte für den entsprechenden Antriebstyp drv annähern.

Achten Sie vor allem auch auf die unterschiedlichen Aesthetics. In der ggplot Funktion werden die grundsätzliche Aesthetics für die x-und y-Achse definiert (displ und hwy). Diese Werte werden (global) übernommen und müssen in den weiteren geom Funktionen nicht geändert werden. Auf diese Art muss nicht in jeder geom Funktion die Aesthetics neu definiert werden, sofern Sie keine anderen Variablen betrachten wollen.

In geom_point werden zusätzlich die Farben auf den Antriebstyp drv angepasst. Auch in geom_smooth werden Farben und Linientyp auf den Antriebstyp drv angepasst.

Ein gleichwertiger, aber wesentlich umständlicher Aufruf sähe wie folgt aus:

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
    geom_point(mapping = aes(x = displ, y = hwy, color = drv)) +
    geom_smooth(mapping = aes(x = displ, y = hwy, linetype = drv, color = drv))

Für weitere Beispiele, was mit ggplot möglich ist, schauen Sie in die tidyverse Gallerie.

Für einen Spickzettel zu den geom Funktionalitäten, schauen Sie auf die offiziellen Cheat-Sheets.

2.2.2 Gruppierung

Standardmäßig wird mit ggplot nur ein Objekt gezeichnet. Um mehrere Objekte zeichnen zu lassen, muss die group Aesthetic auf eine kategorische Variable gesetzt werden. In den meisten Fällen wird das automatisch von ggplot übernommen, z. B. wenn die Farbe bezogen auf eine Kategorie geändert wird:

# Eine Linie
ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy))


# Mehrere Linien gruppiert nach Antriebstyp
ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy, group = drv))


# Automatische Gruppierung nach Antriebstyp durch color Aesthetic
ggplot(data = mpg) +
geom_smooth(mapping = aes(x = displ, y = hwy, color = drv), show.legend = FALSE)

Durch die Aesthetics in mapping jeder geom Funktion lassen sich die globalen Aesthetics für den entsprechenden Layer überschreiben oder erweitern:

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + # Globale Aesthetics
  geom_point(mapping = aes(color = class)) + # Lokale Aesthetics für das Streudiagramm
  geom_smooth(fill = "red") # Lokale Aesthetics für die Linien

Sie sehen, dass die Farb-Aesthetics von geom_point in dem Layer von geom_smooth nicht übernommen werden. Ebenfalls sehen Sie, dass die fill Aesthetic in geom_smooth eine andere Bedeutung hat als in geom_point. Mit fill wird hierbei die Farbe der Wolke (Konfidenzintervall) um die Linie verändert.

Somit können Sie jeden Layer beliebig gestalten.

Tatsächlich lassen sich auch in jedem Layer andere Datenpunkte darstellen oder hervorheben (z. B. durch Filterung).

ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
  geom_point() +
  geom_point(data = filter(mpg, hwy > 30), color = "red") +
  geom_smooth(se = FALSE) # ohne Konfidenzintervall

In diesem Beispiel wurden in einem geom_point Layer die Datenpunkte rot markiert, die einen Wert über 30 bei der hwy Variable aufweisen. Später wird die filter Funktion weitergehend erläutert.

2.3 Statistische Transformation

Eine weitere Graphenart ist das Balkendiagramm, welches die absoluten Häufigkeiten von Kategorien darstellt.

Das Balkendiagramm zeigt beispielsweise, dass die meisten erfassten Autos SUVs waren und nur wenige Zweisitzer oder Minivans:

ggplot(data = mpg, mapping = aes(x = class)) +
  geom_bar()

Ein weiterer Aspekt fällt auf. Die y-Achse count gibt die Häufigkeiten an, obwohl dieses nicht als Variable in den Aesthetics überliefert wurde.

Mit Hilfe statistischer Transformationen werden hier neue Daten “erzeugt”.

Weitere solche Grafiken umfassen:

  • Balkendiagramme und Histogramme

  • Vorhersagemodelle von geom_smooth

  • Boxplots

Die zugrundeliegende Mathematik/Statistik wird hierbei von ggplot übernommen und muss meistens nicht weiter berücksichtigt werden.

In Einzelfällen ist dies notwendig (z. B. zum Darstellen von relativen Häufigkeiten bei Balkendiagrammen) und wird durch Spezifikation der stat Funktionen von ggplot übernommen. Details hierzu lassen wir an dieser Stelle aus.

2.4 Positionsanpassung

2.4.1 Balkendiagramme

Die Aesthetics fill und color haben beziehen sich auch bei Balkendiagrammen auf die Füll- und Randfarben:

ggplot(data = mpg) +
  geom_bar(mapping = aes(x = class, color = class))

ggplot(data = mpg) +
  geom_bar(mapping = aes(x = class, fill = class))

Die Häufigkeit von einer kategorischen Variable lässt sich auch anhand einer anderen kategorischen Variable aufteilen und übereinanderlegen, sofern Aesthetics entsprechend verwendet werden. So können wir auch die gemeinsamen Häufigkeiten der Autotypen und Antriebstypen betrachten.

ggplot(data = mpg) +
  geom_bar(mapping = aes(x = class, fill = drv))

Das position Argument von geom_bar erlaubt auch andere Arten, die Balken zu ordnen:

  • position = "identity" überlappt die Balken (ohne Stapeln). Hierbei sind Überlappungen ohne Transparenz nicht einsehbar:
ggplot(data = mpg) +
  geom_bar(mapping = aes(x = class, fill = drv), position = "identity")

ggplot(data = mpg) +
  geom_bar(mapping = aes(x = class, fill = drv), position = "identity", alpha = 0.2)

ggplot(data = mpg) +
  geom_bar(mapping = aes(x = class, color = drv), position = "identity", fill = NA)

Lediglich wenn die Transparenz mit der Aesthetic alpha angepasst wird oder die Füllfarbe fill entfernt wird, werden die einzelnen Balken sichtbar.

  • position = "fill" stapelt die einzelnen Balken, jedoch werden die Balkenhöhen auf 1 skaliert. Somit lassen sich die einzelnen Kategorien besser vergleichen:
ggplot(data = mpg) +
  geom_bar(mapping = aes(x = class, fill = drv), position = "fill")

  • position = "dodge" platziert die Balken der einzelnen Kategorien nebeneinander. Somit lassen sich die Gruppierungen einer Kategorie einfacher untereinander vergleichen:
ggplot(data = mpg) +
  geom_bar(mapping = aes(x = class, fill = drv), position = "dodge")

2.4.2 Streudiagramme

Auch bei Streudiagramme gibt es in geom_point den Parameter position.

Dieser ist besonders dann nützlich, wenn beispielsweise nicht alle Datenpunkte in einer Grafik erkennbar sind.

In dem folgenden Streudiagramm sind nur ungefähr die Hälfte aller Datenpunkte sichtbar, da die andere Hälfte von ihnen überdeckt wird:

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy))

Durch zufälliges “Ruckeln” dieser Datenpunkte lassen sich die verdeckten Datenpunkte sichtbar machen. Hierzu wird das Argument position = "jitter" in geom_point gesetzt:

ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy), position = "jitter")

Durch diese Transformation der Positionen der Datenpunkte wird die Grafik ungenauer, allerdings können so mehrere Datenpunkte aufgedeckt und der gesamte Trend besser betrachtet werden.

Alternativ lässt sich auch statt geom_point(position = "jitter") direkt geom_jitter() verwenden.

Weitere Information zu den Positionsanpassungen finden sich unter ?position_dodge, ?position_fill, ?position_identity, ?position_jitter und ?position_stack.

2.5 Koordinatensystem

Auch das Koordinatensystem lässt sich durch ggplot ändern:

  • coord_flip(): Wechsel der x- und y-Achse. Inbesondere nützlich für lange Label oder längere Grafiken:
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) + 
  geom_boxplot()

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

ggplot(data = mpg, mapping = aes(x = displ)) +
  geom_histogram(bins = 20)

ggplot(data = mpg, mapping = aes(x = displ)) + 
  geom_histogram(bins = 20) +
  coord_flip()

  • coord_quickmap(): Darstellung von Karten.
nz <- map_data("nz")
ggplot(nz, aes(long, lat, group = group)) +
  geom_polygon(fill = "white", colour = "black")

bar <- ggplot(data = mpg) + 
geom_bar(mapping = aes(x = class, fill = class), width = 1) +
  labs(x = NULL, y = NULL)

bar + coord_flip()

bar + coord_polar()

Anhand dieses Beispieles sehen Sie auch bereits, dass es weitere Layer für die Anpassung der Label (labs) gibt.

2.6 Layer

2.6.1 Erweitertes Kochrezept

Sie haben bereits gesehen, dass sich in ggplot aus sieben Parametern alle grundlegenden Grafiken erstellen lassen.

Dazu zählt der Datensatz in ggplot(), ein geom mit einem mapping, einer Transformation stat, einer Position position sowie einem Koordinatenystem und einem Schema für die Facetten/Gitter.

Somit kann sich das Kochrezept erweitern auf:

ggplot(data = <DATA>) + 
  <GEOM_FUNCTION>(
     mapping = aes(<MAPPINGS>),
     stat = <STAT>, 
     position = <POSITION>
  ) +
  <COORDINATE_FUNCTION> +
  <FACET_FUNCTION>

So lässt sich das Erstellen von Grafiken auf folgende Schritte beschränken:

  1. Datensatz auswählen

  2. geom auswählen (ggf. mit statistischer Transformation stat)

  3. Optional: Aesthetics definieren

  4. Optional: Koordinatensystem auswählen

  5. Optional: facet auswählen

Desweiteren lassen sich Skalierungen, Legenden, Label, usw. ebenfalls durch weitere Funktionen/Layer anpassen.

2.6.2 Weitere Anpassungsmöglichkeiten

Neben den geoms können auch weitere Layer hinzugefügt werden, um beispielsweise die Legende, Label oder Überschrift zu bearbeiten. Prinzipiell lassen sich alle Elemente der ggplot Grafiken manuell anpassen, sofern dies notwendig ist.

Weitere Layer lassen sich nach Bedarf aus dem Spickzettel entnehmen:

  • labs(): Ändere Label der Grafik:
ggplot(data = mpg) +
  geom_point(mapping = aes(x = hwy, y = displ, color = class)) +
  labs(x = "Kraftstoffersparnis (Autobahnmeilen pro Gallone)", y = "Hubraum (in Liter)",
       title = "Kraftstoffeffizienz von Autos der EPA")

  • annotate(): Text/Elemente an einer bestimmten Position einfügen:
ggplot(data = mpg) +
  geom_point(mapping = aes(x = hwy, y = displ, color = class)) +
  annotate(geom = "text", x = 30, y = 5, label = "Text einfügen")

  • theme(): Position der Legende:
ggplot(data = mpg) +
  geom_point(mapping = aes(x = hwy, y = displ, color = class)) +
  theme(legend.position = "top") # oder "bottom", "left", "right"

  • xlim() und ylim(): Grafik auf einen Bereich der x- oder y-Achse beschränken:
ggplot(data = mpg) +
  geom_point(mapping = aes(x = hwy, y = displ, color = class)) +
  xlim(15, 25) +
  ylim(3, 6)

Je nach Fragestellung oder Vorlieben lassen sich alle Elemente der Grafik anpassen. Der Aufbau der Grafik bleibt gleich und es können einfach weitere Layer hinzugefügt werden. Hierzu kann bei speziellen Anforderungen auf den Spickzettel geschaut werden oder bei einer Suchmaschine nachgeschlagen werden.

3 Aufgaben

Für die Übungsaufgaben gehen wir zu dem diamonds Datensatz über. Der Datensatz befasst sich mit Preis, Qualität und weiteren Eigenschaften von knapp 54.000 Diamanten.

Zuerst geben wir wieder die folgenden Befehle ein, um die Bibliothek tidyverse zu laden und den Datensatz einzusehen:

library(tidyverse)
diamonds

Um mehr über den Datensatz zu erfahren, geben Sie wieder den Hilfsbefehl ?diamonds ein.

3.1 Übungsaufgaben

  1. Erstellen Sie eine Grafik von den Variablen carat und price des diamonds Datensatzes, wobei Sie die Datenpunkte nach der Kategorie clarity färben.

  2. Erstellen Sie eine weitere Grafik von den Variablen carat und price des diamonds Datensatzes, wobei Sie ein facet für die Kategorie clarity hinzufügen. Worin liegen die Vor- und Nachteile der Farb-Aesthetics und der Nutzung von facets?

  3. Verwenden Sie nun als facet eine kontinuierliche Variable wie z. B. table. Was passiert hier?

  4. Verwenden Sie nun ein facet mit zwei Variablen cut und clarity, um die Variablen carat und price zu vergleichen. Was können Sie dieser Grafik entnehmen?

  5. Erstellen Sie eine passende Grafik, um die Verteilungen von cut und price darzustellen. Denken Sie auch an passende Gruppierungen, Farben und Positionen!

  6. Erstellen Sie die folgenden Grafiken:

Verschiedene Grafiken

  1. Erstellen Sie ein Balkendiagramm von der Variable cut und fügen Sie ein facet mit der Variable clarity ein.
    Ändern Sie zusätzlich den Titel und die Achsenbeschriftungen mit labs() und verschieben Sie die Legende nach unten mit theme().
LS0tCnRpdGxlOiAiU3RhdGlzdGlrIHVuZCBEYXRhIFNjaWVuY2UgLSBWZXJ0aWVmdW5nIHZvbiBnZ3Bsb3QiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCiMgRWluc2NodWIgU3RyZXVkaWFncmFtbWUKClp1ZXJzdCBtw7xzc2VuIHdpciBlcm5ldXQgYHRpZHl2ZXJzZWAgbGFkZW4sIGRhIHdpciBlaW5lIG5ldWUgU2Vzc2lvbgphbmdlZmFuZ2VuIGhhYmVuOgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKClNpZSBoYWJlbiBiZXJlaXRzIFN0cmV1ZGlhZ3JhbW1lIGtlbm5lbmdlbGVybnQ6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKCWdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQpgYGAKCkhpZXIgc2VoZW4gd2lyIGJlaXNwaWVsc3dlaXNlIGRlbiBadXNhbW1lbmhhbmcgdm9uIEh1YnJhdW0gdW5kIGRlbQpLcmFmdHN0b2ZmdmVyYnJhdWNoIHZvbiBlaW5pZ2VuIEF1dG9zLgoKRGllIEZyYWdlc3RlbGx1bmcgaXN0IGhpZXJiZWkgaW1tZXIsIHdpZSBkaWUgZWluZSBWYXJpYWJsZSB2b24gZGVyCmFuZGVyZW4gVmFyaWFibGUgYWJow6RuZ3QuCgpJbiBkZXIgU3RhdGlzdGlrIGdpYnQgZXMgZWJlbmZhbGxzIGVpbmUgTWHDn3phaGwsIGRpZSB1bnMgZGllc2VuClp1c2FtbWVuaGFuZyBhbmdpYnQ6IGRlciBLb3JyZWxhdGlvbnNrb2VmZml6aWVudC4KCi0gICBEZXIgS29lZmZpemllbnQgbGllZ3Qgendpc2NoZW4gLTEgKG5lZ2F0aXZlIEtvcnJlbGF0aW9uKSB1bmQgKzEKICAgIChwb3NpdGl2ZSBLb3JyZWxhdGlvbik6CgpgYGB7cn0KY29yKG1wZyRkaXNwbCwgbXBnJGh3eSkKYGBgCgpXaXIgaGFsdGVuIGZlc3Q6CgotICAgRGFzIFN0cmV1ZGlhZ3JhbW0gemVpZ3QgZWluZW4gYWJzdGVpZ2VuZGVuIFRyZW5kLiBXZW5uIGRpZQogICAgTW90b3JncsO2w59lIChIdWJyYXVtKSBzdGVpZ3QsIGRhbm4gc2lua3QgZGllIEtyYWZ0c3RvZmZlZmZpemllbnoKICAgIHRlbmRlbnppZWxsIGViZW5mYWxscwoKLSAgIERlciBLb3JyZWxhdGlvbnNrb2VmZml6aWVudCBpc3QgbmVnYXRpdiB1bmQgYmVzdMOkdGlndCB1bnNlcmUKICAgIEJlb2JhY2h0dW5nCgojIFZlcnRpZWZ1bmcgdm9uIGBnZ3Bsb3RgCgojIyBGYWNldHMKCkVzIGxhc3NlbiBzaWNoIGJlcmVpdHMgZHVyY2ggQWVzdGhldGljcyBEYXRlbiBhdWYgR3J1bmQgdm9uIEthdGVnb3JpZW4KdmlzdWVsbCB1bnRlcnNjaGVpZGVuLiBTbyBrw7ZubmVuIHdpciBpbiBkZW0gYG1wZ2AgRGF0ZW5zYXR6IGRpZQp1bnRlcnNjaGllZGxpY2hlbiBBdXRvdHlwZW4gaGVydm9yaGViZW46CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGh3eSwgeSA9IGRpc3BsLCBjb2xvciA9IGNsYXNzKSkKYGBgCgpGYWxscyBkaWUgZWluemVsbmVuIEthdGVnb3JpZW4gZ2V0cmVubnQgYmV0cmFjaHRldCB3ZXJkZW4gc29sbGVuLCB3ZXJkZW4KYGZhY2V0c2AgKEZhY2V0dGVuL0dpdHRlcikgdmVyd2VuZGV0LgoKSGllcmJlaSB3aXJkIGRpZSBHcmFmaWsgZsO8ciBqZWRlIEthdGVnb3JpZSBpbiBlaW5lbSBHaXR0ZXIgYW5nZXplaWd0LgpGYWxscyBlaW5lIGVpbnplbG5lIEthdGVnb3JpZSBiZXRyYWNodGV0IHdlcmRlbiBzb2xsLCB3aXJkIGVpbiB3ZWl0ZXJlcgpMYXllciBtaXQgZGVyIEZ1bmt0aW9uIGBmYWNldF93cmFwKClgIGhpbnp1Z2Vmw7xndC4KCkFscyBlcnN0ZXMgQXJndW1lbnQgd2lyZCBlaW5lIGBmb3JtdWxhYCAoZWluIHNwZXppZWxsZSBEYXRlbnN0cnVrdHVyKQplcndhcnRldCwgYmVpIGRlciBkYXMgVGlsZGUtU3ltYm9sIGB+YCBnZWZvbGd0IHZvbiBlaW5lbSBWYXJpYWJsZW5uYW1lbgoodW5hYmjDpG5naWdlIFZhcmlhYmxlL0thdGVnb3JpZSkgZWluZ2VnZWJlbiB3aXJkLgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBod3ksIHkgPSBkaXNwbCkpICsKICBmYWNldF93cmFwKGZhY2V0cyA9IH4gY2xhc3MsIG5yb3cgPSAyKQpgYGAKCkZhbGxzIG1laHJlcmUgS2F0ZWdvcmllbiBnbGVpY2h6ZWl0aWcgYmV0cmFjaHRldCB3ZXJkZW4gc29sbGVuLCBkYW5uCndpcmQgZWluIExheWVyIG1pdCBgZmFjZXRfZ3JpZCgpYCBiZW7DtnRpZ3QuCgpFcyBsw6Rzc3Qgc2ljaCBtaXQgYGZhY2V0X2dyaWQoKWAgZWJlbmZhbGxzIGVpbmUgS2F0ZWdvcmllIGJldHJhY2h0ZW4sCmplZG9jaCBzaWVodCBkaWVzIG1laXN0IHdlbmlnZXIga29tcGFrdCBhdXMgYWxzIGBmYWNldF93cmFwKClgOgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBod3ksIHkgPSBkaXNwbCkpICsKICBmYWNldF9ncmlkKGZhY2V0cyA9IH4gY2xhc3MpCmBgYAoKVW0gbWVocmVyZSBLYXRlZ29yaWVuIGdsZWljaHplaXRpZyB6dSBiZXRyYWNodGVuLCB3aXJkIGJlaQpgZmFjZXRfZ3JpZCgpYCBlYmVuZmFsbHMgZWluZSBgZm9ybXVsYWAgYW5nZWdlYmVuLgoKSW4gZGllc2VtIEZhbGwgYmVzdGVodCBkaWUgYGZvcm11bGFgIGF1cyB6d2VpIFZhcmlhYmxlbjsgZWluZSBWYXJpYWJsZQp2b3IgZGVyIFRpbGRlIHVuZCBlaW5lIFZhcmlhYmxlIG5hY2ggZGVyIFRpbGRlLgoKRmFsbHMgZGllIEtyYWZ0c3RvZmZlZmZpemllbnogKGBod3lgKSBiZXrDvGdsaWNoIGRlcyBIdWJyYXVtcyBiZXRyYWNodGV0CndlcmRlbiBzb2xsLCB3b2JlaSBmw7xyIGplZGVuIEF1dG90eXBlbiB1bmQgamVkZW4gQW50cmllYnN0eXBlbgooRnJvbnRyYWQsIEhpbnRlcnJhZCwgVmllcnJhZCkgZWluZSBHcmFmaWsgZXJzdGVsbHQgd2VyZGVuIHNvbGwsIHNvIG11c3MKaW4gZGVyIGBmb3JtdWxhYCBpbiBgZmFjZXRfZ3JpZGAgYGRydiB+IGNsYXNzYCBzcGV6aWZpZXJ0IHdlcmRlbjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gaHd5LCB5ID0gZGlzcGwpKSArCiAgZmFjZXRfZ3JpZChmYWNldHMgPSBkcnYgfiBjbGFzcykKYGBgCgojIyBHZW9tZXRyaXNjaGUgT2JqZWt0ZQoKIyMjIGBnZ3Bsb3RgIE9iamVrdGUKCkVzIGlzdCBlYmVuZmFsbHMgbcO2Z2xpY2ggZGllIEdyYWZpa2VuIHZvbiBgZ2dwbG90YCB3ZWl0ZXJ6dXZlcndlbmRlbiwKaW5kZW0gc2llIGFscyBPYmpla3RlIGdlc3BlaWNoZXJ0IHdlcmRlbi4KCkplZGUgRnVua3Rpb24gdm9uIGBnZ3Bsb3RgIGxpZWZlcnQgZWluIGdlb21ldHJpc2NoZXMgKGBnZW9tYCkgT2JqZWt0Cnp1csO8Y2ssIG1pdCBkZW0gd2VpdGVyZ2VhcmJlaXRldCB3ZXJkZW4ga2Fubi4gRGEgZXMgc2ljaCB1bSBnZW9tZXRyaXNjaGUKT2JqZWt0ZSBoYW5kZWx0LCBlbnRzcHJlY2hlbiBkaWUgTmFtZW4gZGVyIEZ1bmt0aW9uZW4gdm9uIGBnZ3Bsb3RgCmRpZXNlciBOYW1lbnNnZWJ1bmcgKHouIEIuIGBnZW9tX3BvaW50YCBmw7xyIFN0cmV1ZGlhZ3JhbW1lIG9kZXIKYGdlb21fYmFyYCBmw7xyIEJhbGtlbmRpYWdyYW1tZS9CYXJwbG90cykuCgpTdGF0dCBkaWUgZ2VzYW10ZSBHcmFmaWsgaW4gZWluZW0gU2Nocml0dCBkdXJjaCBkaWUgVmVya2V0dHVuZyB2b24gYCtgCnVuZCBkYXMgSGluenVmw7xnZW4gdm9uIExheWVybiB6dSBlcnN0ZWxsZW4sIGvDtm5uZW4gZGllIE9iamVrdGUvR3JhZmlrZW4KYXVjaCB6d2lzY2hlbmdlc3BlaWNoZXJ0IHdlcmRlbjoKCmBgYHtyfQpncmFwaDEgPC0gZ2dwbG90KGRhdGEgPSBtcGcpCmdyYXBoMQpncmFwaDIgPC0gZ3JhcGgxICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpCmdyYXBoMgpncmFwaDMgPC0gZ3JhcGgxICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSBjbGFzcykpCmdyYXBoMwpncmFwaDIgKyBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSksIG1ldGhvZCA9ICJsb2VzcyIsIGZvcm11bGEgPSB5IH4geCkKYGBgCgpEYSBzaWNoIGluIGBnZ3Bsb3RgIGRpZSBMYXllciBTY2hyaXR0IGbDvHIgU2Nocml0dCBoaW56dWbDvGdlbiBsYXNzZW4sCmvDtm5uZW4gU2llIHNvIElocmVuIENvZGUgbmFjaCBCZWRhcmYgYmVzc2VyIHN0cnVrdHVyaWVyZW4gb2Rlcgp3aWVkZXJ2ZXJ3ZW5kZW4uCgpBYmjDpG5naWcgdm9uIGRlbSB2aXN1ZWxsZW4vZ2VvbWV0cmlzY2hlbiBPYmpla3QsIHdlbGNoZXMgdmVyd2VuZGV0IHdpcmQsCsOkbmRlcnQgc2ljaCBkaWUgRGFyc3RlbGx1bmcsIGF1Y2ggd2VubiBkaWVzZWxiZW4gVmFyaWFibGVuIHZlcndlbmRldAp3ZXJkZW46CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkKCmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQpgYGAKCkF1ZiBkZXIgZWluZW4gU2VpdGUgZXJoYWx0ZW4gd2lyIGVpbiBTdHJldWRpYWdyYW1tIHZvbiBgaHd5YCB1bmQgYGRpc3BsYAp1bmQgYXVmIGRlciBhbmRlcmVuIGVpbmUgTW9kZWxsLUxpbmllLCBkaWUgc2ljaCBkZW5zZWxiZW4gRGF0ZW5wdW5rdGVuCmFubsOkaGVydC4KCkFsbGUgYGdlb21gIEZ1bmt0aW9uZW4gYmVuw7Z0aWdlbiBlaW5lbiBgbWFwcGluZ2AgUGFyYW1ldGVyLCBhbGxlcmRpbmdzCnVudGVyc2NoZWlkZW4gc2ljaCBkaWUgQWVzdGhldGljcyBmw7xyIGplZGUgRnVua3Rpb24uCgpTbyBsw6Rzc3Qgc2ljaCBiZWlzcGllbHN3ZWlzZSBuaWNodCBkaWUgUHVua3Rmb3JtIGRlciBMaW5pZSB2b24KYGdlb21fc21vb3RoYCBtaXQgYHNoYXBlYCB2ZXLDpG5kZXJuOgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIHNoYXBlID0gZHJ2KSkKYGBgCgpEaWUgTGluaWVuIHdlcmRlbiBmw7xyIGRpZSBLYXRlZ29yaWVuIChGcm9udCwgSGludGVyLCBWaWVycmFkKSBncnVwcGllcnQsCmFsbGVyZGluZ3Mgw6RuZGVybiBzaWNoIGRpZSBMaW5pZW4gbmljaHQuCgpVbSBkaWUgQWVzdGhldGljIGRlciBMaW5pZW4genUgw6RuZGVybiwga2FubiBiZWlzcGllbHN3ZWlzZSBkZXIgTGluaWVudHlwCm1pdCBgbGluZXR5cGVgIHZlcsOkbmRlcnQgd2VyZGVuOgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGxpbmV0eXBlID0gZHJ2KSkKYGBgCgpOdW4gc2VoZW4gd2lyIGbDvHIgamVkZW4gQW50cmllYnN0eXBlbiBlaW5lIGFuZGVyZSBMaW5pZW5hcnQuCgpVbSBtZWhyIMO8YmVyIGRpZSBBZXN0aGV0aWNzIGVpbmVyIGBnZW9tYCBGdW5rdGlvbiB6dSBlcmZhaHJlbiwga2FubgppbW1lciBkaWUgSGlsZnNmdW5rdGlvbiBgP2dlb21fc21vb3RoYCAob2RlciBlaW5lIFN1Y2htYXNjaGluZSkgenUgUmF0Cmdlem9nZW4gd2VyZGVuLgoKU2VsYnN0dmVyc3TDpG5kbGljaCBsYXNzZW4gc2ljaCBkaWUgdW50ZXJzY2hpZWRsaWNoZW4gTGF5ZXIgZGVyIGBnZW9tc2AKYXVjaCDDvGJlcmVpbmFuZGVybGVnZW4uIFNvIGvDtm5uZW4gZWJlbmZhbGxzIERhdGVucHVua3RlIHVuZCBMaW5pZW4KenVnbGVpY2ggYmV0cmFjaHRldCB3ZXJkZW46CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvciA9IGRydikpICsKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKGxpbmV0eXBlID0gZHJ2LCBjb2xvciA9IGRydikpCmBgYAoKU2llIHNlaGVuLCBkYXNzIGRpZSBMaW5pZW4gZGllIERhdGVucHVua3RlIGbDvHIgZGVuIGVudHNwcmVjaGVuZGVuCkFudHJpZWJzdHlwIGBkcnZgIGFubsOkaGVybi4KCkFjaHRlbiBTaWUgdm9yIGFsbGVtIGF1Y2ggYXVmIGRpZSB1bnRlcnNjaGllZGxpY2hlbiBBZXN0aGV0aWNzLiBJbiBkZXIKYGdncGxvdGAgRnVua3Rpb24gd2VyZGVuIGRpZSBncnVuZHPDpHR6bGljaGUgQWVzdGhldGljcyBmw7xyIGRpZSB4LXVuZAp5LUFjaHNlIGRlZmluaWVydCAoYGRpc3BsYCB1bmQgYGh3eWApLiBEaWVzZSBXZXJ0ZSB3ZXJkZW4gKGdsb2JhbCkKw7xiZXJub21tZW4gdW5kIG3DvHNzZW4gaW4gZGVuIHdlaXRlcmVuIGBnZW9tYCBGdW5rdGlvbmVuIG5pY2h0IGdlw6RuZGVydAp3ZXJkZW4uIEF1ZiBkaWVzZSBBcnQgbXVzcyBuaWNodCBpbiBqZWRlciBgZ2VvbWAgRnVua3Rpb24gZGllIEFlc3RoZXRpY3MKbmV1IGRlZmluaWVydCB3ZXJkZW4sIHNvZmVybiBTaWUga2VpbmUgYW5kZXJlbiBWYXJpYWJsZW4gYmV0cmFjaHRlbgp3b2xsZW4uCgpJbiBgZ2VvbV9wb2ludGAgd2VyZGVuIHp1c8OkdHpsaWNoIGRpZSBGYXJiZW4gYXVmIGRlbiBBbnRyaWVic3R5cCBgZHJ2YAphbmdlcGFzc3QuIEF1Y2ggaW4gYGdlb21fc21vb3RoYCB3ZXJkZW4gRmFyYmVuIHVuZCBMaW5pZW50eXAgYXVmIGRlbgpBbnRyaWVic3R5cCBgZHJ2YCBhbmdlcGFzc3QuCgpFaW4gZ2xlaWNod2VydGlnZXIsIGFiZXIgd2VzZW50bGljaCB1bXN0w6RuZGxpY2hlciBBdWZydWYgc8OkaGUgd2llIGZvbGd0CmF1czoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArCiAgICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGRydikpICsKICAgIGdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBsaW5ldHlwZSA9IGRydiwgY29sb3IgPSBkcnYpKQpgYGAKCkbDvHIgd2VpdGVyZSBCZWlzcGllbGUsIHdhcyBtaXQgYGdncGxvdGAgbcO2Z2xpY2ggaXN0LCBzY2hhdWVuIFNpZSBpbiBkaWUKW3RpZHl2ZXJzZSBHYWxsZXJpZV0oaHR0cHM6Ly9leHRzLmdncGxvdDIudGlkeXZlcnNlLm9yZy9nYWxsZXJ5LykuCgpGw7xyIGVpbmVuIFNwaWNremV0dGVsIHp1IGRlbiBgZ2VvbWAgRnVua3Rpb25hbGl0w6R0ZW4sIHNjaGF1ZW4gU2llIGF1ZgpkaWUgW29mZml6aWVsbGVuCkNoZWF0LVNoZWV0c10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykuCgojIyMgR3J1cHBpZXJ1bmcKClN0YW5kYXJkbcOkw59pZyB3aXJkIG1pdCBgZ2dwbG90YCBudXIgZWluIE9iamVrdCBnZXplaWNobmV0LiBVbSBtZWhyZXJlCk9iamVrdGUgemVpY2huZW4genUgbGFzc2VuLCBtdXNzIGRpZSBgZ3JvdXBgIEFlc3RoZXRpYyBhdWYgZWluZQprYXRlZ29yaXNjaGUgVmFyaWFibGUgZ2VzZXR6dCB3ZXJkZW4uIEluIGRlbiBtZWlzdGVuIEbDpGxsZW4gd2lyZCBkYXMKYXV0b21hdGlzY2ggdm9uIGBnZ3Bsb3RgIMO8YmVybm9tbWVuLCB6LiBCLiB3ZW5uIGRpZSBGYXJiZSBiZXpvZ2VuIGF1ZgplaW5lIEthdGVnb3JpZSBnZcOkbmRlcnQgd2lyZDoKCmBgYHtyfQojIEVpbmUgTGluaWUKZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpCgojIE1laHJlcmUgTGluaWVuIGdydXBwaWVydCBuYWNoIEFudHJpZWJzdHlwCmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9zbW9vdGgobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIGdyb3VwID0gZHJ2KSkKCiMgQXV0b21hdGlzY2hlIEdydXBwaWVydW5nIG5hY2ggQW50cmllYnN0eXAgZHVyY2ggY29sb3IgQWVzdGhldGljCmdncGxvdChkYXRhID0gbXBnKSArCmdlb21fc21vb3RoKG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBjb2xvciA9IGRydiksIHNob3cubGVnZW5kID0gRkFMU0UpCmBgYAoKRHVyY2ggZGllIEFlc3RoZXRpY3MgaW4gYG1hcHBpbmdgIGplZGVyIGBnZW9tYCBGdW5rdGlvbiBsYXNzZW4gc2ljaCBkaWUKZ2xvYmFsZW4gQWVzdGhldGljcyBmw7xyIGRlbiBlbnRzcHJlY2hlbmRlbiBMYXllciDDvGJlcnNjaHJlaWJlbiBvZGVyCmVyd2VpdGVybjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKSArICMgR2xvYmFsZSBBZXN0aGV0aWNzCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG9yID0gY2xhc3MpKSArICMgTG9rYWxlIEFlc3RoZXRpY3MgZsO8ciBkYXMgU3RyZXVkaWFncmFtbQogIGdlb21fc21vb3RoKGZpbGwgPSAicmVkIikgIyBMb2thbGUgQWVzdGhldGljcyBmw7xyIGRpZSBMaW5pZW4KYGBgCgpTaWUgc2VoZW4sIGRhc3MgZGllIEZhcmItQWVzdGhldGljcyB2b24gYGdlb21fcG9pbnRgIGluIGRlbSBMYXllciB2b24KYGdlb21fc21vb3RoYCBuaWNodCDDvGJlcm5vbW1lbiB3ZXJkZW4uIEViZW5mYWxscyBzZWhlbiBTaWUsIGRhc3MgZGllCmBmaWxsYCBBZXN0aGV0aWMgaW4gYGdlb21fc21vb3RoYCBlaW5lIGFuZGVyZSBCZWRldXR1bmcgaGF0IGFscyBpbgpgZ2VvbV9wb2ludGAuIE1pdCBgZmlsbGAgd2lyZCBoaWVyYmVpIGRpZSBGYXJiZSBkZXIgV29sa2UKKEtvbmZpZGVuemludGVydmFsbCkgdW0gZGllIExpbmllIHZlcsOkbmRlcnQuCgpTb21pdCBrw7ZubmVuIFNpZSBqZWRlbiBMYXllciBiZWxpZWJpZyBnZXN0YWx0ZW4uCgpUYXRzw6RjaGxpY2ggbGFzc2VuIHNpY2ggYXVjaCBpbiBqZWRlbSBMYXllciBhbmRlcmUgRGF0ZW5wdW5rdGUKZGFyc3RlbGxlbiBvZGVyIGhlcnZvcmhlYmVuICh6LiBCLiBkdXJjaCBGaWx0ZXJ1bmcpLgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGZpbHRlcihtcGcsIGh3eSA+IDMwKSwgY29sb3IgPSAicmVkIikgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICMgb2huZSBLb25maWRlbnppbnRlcnZhbGwKYGBgCgpJbiBkaWVzZW0gQmVpc3BpZWwgd3VyZGVuIGluIGVpbmVtIGBnZW9tX3BvaW50YCBMYXllciBkaWUgRGF0ZW5wdW5rdGUKcm90IG1hcmtpZXJ0LCBkaWUgZWluZW4gV2VydCDDvGJlciAzMCBiZWkgZGVyIGBod3lgIFZhcmlhYmxlIGF1ZndlaXNlbi4KU3DDpHRlciB3aXJkIGRpZSBgZmlsdGVyYCBGdW5rdGlvbiB3ZWl0ZXJnZWhlbmQgZXJsw6R1dGVydC4KCiMjIFN0YXRpc3Rpc2NoZSBUcmFuc2Zvcm1hdGlvbgoKRWluZSB3ZWl0ZXJlIEdyYXBoZW5hcnQgaXN0IGRhcyBCYWxrZW5kaWFncmFtbSwgd2VsY2hlcyBkaWUgYWJzb2x1dGVuCkjDpHVmaWdrZWl0ZW4gdm9uIEthdGVnb3JpZW4gZGFyc3RlbGx0LgoKRGFzIEJhbGtlbmRpYWdyYW1tIHplaWd0IGJlaXNwaWVsc3dlaXNlLCBkYXNzIGRpZSBtZWlzdGVuIGVyZmFzc3RlbgpBdXRvcyBTVVZzIHdhcmVuIHVuZCBudXIgd2VuaWdlIFp3ZWlzaXR6ZXIgb2RlciBNaW5pdmFuczoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZywgbWFwcGluZyA9IGFlcyh4ID0gY2xhc3MpKSArCiAgZ2VvbV9iYXIoKQpgYGAKCkVpbiB3ZWl0ZXJlciBBc3Bla3QgZsOkbGx0IGF1Zi4gRGllIHktQWNoc2UgYGNvdW50YCBnaWJ0IGRpZSBIw6R1Zmlna2VpdGVuCmFuLCBvYndvaGwgZGllc2VzIG5pY2h0IGFscyBWYXJpYWJsZSBpbiBkZW4gQWVzdGhldGljcyDDvGJlcmxpZWZlcnQKd3VyZGUuCgpNaXQgSGlsZmUgc3RhdGlzdGlzY2hlciBUcmFuc2Zvcm1hdGlvbmVuIHdlcmRlbiBoaWVyIG5ldWUgRGF0ZW4KImVyemV1Z3QiLgoKV2VpdGVyZSBzb2xjaGUgR3JhZmlrZW4gdW1mYXNzZW46CgotICAgQmFsa2VuZGlhZ3JhbW1lIHVuZCBIaXN0b2dyYW1tZQoKLSAgIFZvcmhlcnNhZ2Vtb2RlbGxlIHZvbiBgZ2VvbV9zbW9vdGhgCgotICAgQm94cGxvdHMKCkRpZSB6dWdydW5kZWxpZWdlbmRlIE1hdGhlbWF0aWsvU3RhdGlzdGlrIHdpcmQgaGllcmJlaSB2b24gYGdncGxvdGAKw7xiZXJub21tZW4gdW5kIG11c3MgbWVpc3RlbnMgbmljaHQgd2VpdGVyIGJlcsO8Y2tzaWNodGlndCB3ZXJkZW4uCgpJbiBFaW56ZWxmw6RsbGVuIGlzdCBkaWVzIG5vdHdlbmRpZyAoei4gQi4genVtIERhcnN0ZWxsZW4gdm9uIHJlbGF0aXZlbgpIw6R1Zmlna2VpdGVuIGJlaSBCYWxrZW5kaWFncmFtbWVuKSB1bmQgd2lyZCBkdXJjaCBTcGV6aWZpa2F0aW9uIGRlcgpgc3RhdGAgRnVua3Rpb25lbiB2b24gYGdncGxvdGAgw7xiZXJub21tZW4uIERldGFpbHMgaGllcnp1IGxhc3NlbiB3aXIgYW4KZGllc2VyIFN0ZWxsZSBhdXMuCgojIyBQb3NpdGlvbnNhbnBhc3N1bmcKCiMjIyBCYWxrZW5kaWFncmFtbWUKCkRpZSBBZXN0aGV0aWNzIGBmaWxsYCB1bmQgYGNvbG9yYCBoYWJlbiBiZXppZWhlbiBzaWNoIGF1Y2ggYmVpCkJhbGtlbmRpYWdyYW1tZW4gYXVmIGRpZSBGw7xsbC0gdW5kIFJhbmRmYXJiZW46CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjbGFzcywgY29sb3IgPSBjbGFzcykpCmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gY2xhc3MsIGZpbGwgPSBjbGFzcykpCmBgYAoKRGllIEjDpHVmaWdrZWl0IHZvbiBlaW5lciBrYXRlZ29yaXNjaGVuIFZhcmlhYmxlIGzDpHNzdCBzaWNoIGF1Y2ggYW5oYW5kCmVpbmVyIGFuZGVyZW4ga2F0ZWdvcmlzY2hlbiBWYXJpYWJsZSBhdWZ0ZWlsZW4gdW5kIMO8YmVyZWluYW5kZXJsZWdlbiwKc29mZXJuIEFlc3RoZXRpY3MgZW50c3ByZWNoZW5kIHZlcndlbmRldCB3ZXJkZW4uIFNvIGvDtm5uZW4gd2lyIGF1Y2ggZGllCmdlbWVpbnNhbWVuIEjDpHVmaWdrZWl0ZW4gZGVyIEF1dG90eXBlbiB1bmQgQW50cmllYnN0eXBlbiBiZXRyYWNodGVuLgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gY2xhc3MsIGZpbGwgPSBkcnYpKQpgYGAKCkRhcyBgcG9zaXRpb25gIEFyZ3VtZW50IHZvbiBgZ2VvbV9iYXJgIGVybGF1YnQgYXVjaCBhbmRlcmUgQXJ0ZW4sIGRpZQpCYWxrZW4genUgb3JkbmVuOgoKLSAgIGBwb3NpdGlvbiA9ICJpZGVudGl0eSJgIMO8YmVybGFwcHQgZGllIEJhbGtlbiAob2huZSBTdGFwZWxuKS4gSGllcmJlaQogICAgc2luZCDDnGJlcmxhcHB1bmdlbiBvaG5lIFRyYW5zcGFyZW56IG5pY2h0IGVpbnNlaGJhcjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGNsYXNzLCBmaWxsID0gZHJ2KSwgcG9zaXRpb24gPSAiaWRlbnRpdHkiKQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGNsYXNzLCBmaWxsID0gZHJ2KSwgcG9zaXRpb24gPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuMikKZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjbGFzcywgY29sb3IgPSBkcnYpLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIGZpbGwgPSBOQSkKYGBgCgpMZWRpZ2xpY2ggd2VubiBkaWUgVHJhbnNwYXJlbnogbWl0IGRlciBBZXN0aGV0aWMgYGFscGhhYCBhbmdlcGFzc3Qgd2lyZApvZGVyIGRpZSBGw7xsbGZhcmJlIGBmaWxsYCBlbnRmZXJudCB3aXJkLCB3ZXJkZW4gZGllIGVpbnplbG5lbiBCYWxrZW4Kc2ljaHRiYXIuCgotICAgYHBvc2l0aW9uID0gImZpbGwiYCBzdGFwZWx0IGRpZSBlaW56ZWxuZW4gQmFsa2VuLCBqZWRvY2ggd2VyZGVuIGRpZQogICAgQmFsa2VuaMO2aGVuIGF1ZiAxIHNrYWxpZXJ0LiBTb21pdCBsYXNzZW4gc2ljaCBkaWUgZWluemVsbmVuCiAgICBLYXRlZ29yaWVuIGJlc3NlciB2ZXJnbGVpY2hlbjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGNsYXNzLCBmaWxsID0gZHJ2KSwgcG9zaXRpb24gPSAiZmlsbCIpCmBgYAoKLSAgIGBwb3NpdGlvbiA9ICJkb2RnZSJgIHBsYXR6aWVydCBkaWUgQmFsa2VuIGRlciBlaW56ZWxuZW4gS2F0ZWdvcmllbgogICAgbmViZW5laW5hbmRlci4gU29taXQgbGFzc2VuIHNpY2ggZGllIEdydXBwaWVydW5nZW4gZWluZXIgS2F0ZWdvcmllCiAgICBlaW5mYWNoZXIgdW50ZXJlaW5hbmRlciB2ZXJnbGVpY2hlbjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fYmFyKG1hcHBpbmcgPSBhZXMoeCA9IGNsYXNzLCBmaWxsID0gZHJ2KSwgcG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCiMjIyBTdHJldWRpYWdyYW1tZQoKQXVjaCBiZWkgU3RyZXVkaWFncmFtbWUgZ2lidCBlcyBpbiBgZ2VvbV9wb2ludGAgZGVuIFBhcmFtZXRlcgpgcG9zaXRpb25gLgoKRGllc2VyIGlzdCBiZXNvbmRlcnMgZGFubiBuw7x0emxpY2gsIHdlbm4gYmVpc3BpZWxzd2Vpc2UgbmljaHQgYWxsZQpEYXRlbnB1bmt0ZSBpbiBlaW5lciBHcmFmaWsgZXJrZW5uYmFyIHNpbmQuCgpJbiBkZW0gZm9sZ2VuZGVuIFN0cmV1ZGlhZ3JhbW0gc2luZCBudXIgdW5nZWbDpGhyIGRpZSBIw6RsZnRlIGFsbGVyCkRhdGVucHVua3RlIHNpY2h0YmFyLCBkYSBkaWUgYW5kZXJlIEjDpGxmdGUgdm9uIGlobmVuIMO8YmVyZGVja3Qgd2lyZDoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQpgYGAKCkR1cmNoIHp1ZsOkbGxpZ2VzICJSdWNrZWxuIiBkaWVzZXIgRGF0ZW5wdW5rdGUgbGFzc2VuIHNpY2ggZGllIHZlcmRlY2t0ZW4KRGF0ZW5wdW5rdGUgc2ljaHRiYXIgbWFjaGVuLiBIaWVyenUgd2lyZCBkYXMgQXJndW1lbnQKYHBvc2l0aW9uID0gImppdHRlciJgIGluIGBnZW9tX3BvaW50YCBnZXNldHp0OgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSksIHBvc2l0aW9uID0gImppdHRlciIpCmBgYAoKRHVyY2ggZGllc2UgVHJhbnNmb3JtYXRpb24gZGVyIFBvc2l0aW9uZW4gZGVyIERhdGVucHVua3RlIHdpcmQgZGllCkdyYWZpayB1bmdlbmF1ZXIsIGFsbGVyZGluZ3Mga8O2bm5lbiBzbyBtZWhyZXJlIERhdGVucHVua3RlIGF1ZmdlZGVja3QKdW5kIGRlciBnZXNhbXRlIFRyZW5kIGJlc3NlciBiZXRyYWNodGV0IHdlcmRlbi4KCkFsdGVybmF0aXYgbMOkc3N0IHNpY2ggYXVjaCBzdGF0dCBgZ2VvbV9wb2ludChwb3NpdGlvbiA9ICJqaXR0ZXIiKWAKZGlyZWt0IGBnZW9tX2ppdHRlcigpYCB2ZXJ3ZW5kZW4uCgpXZWl0ZXJlIEluZm9ybWF0aW9uIHp1IGRlbiBQb3NpdGlvbnNhbnBhc3N1bmdlbiBmaW5kZW4gc2ljaCB1bnRlcgpgP3Bvc2l0aW9uX2RvZGdlYCwgYD9wb3NpdGlvbl9maWxsYCwgYD9wb3NpdGlvbl9pZGVudGl0eWAsCmA/cG9zaXRpb25faml0dGVyYCB1bmQgYD9wb3NpdGlvbl9zdGFja2AuCgojIyBLb29yZGluYXRlbnN5c3RlbQoKQXVjaCBkYXMgS29vcmRpbmF0ZW5zeXN0ZW0gbMOkc3N0IHNpY2ggZHVyY2ggYGdncGxvdGAgw6RuZGVybjoKCi0gICBgY29vcmRfZmxpcCgpYDogV2VjaHNlbCBkZXIgeC0gdW5kIHktQWNoc2UuIEluYmVzb25kZXJlIG7DvHR6bGljaCBmw7xyCiAgICBsYW5nZSBMYWJlbCBvZGVyIGzDpG5nZXJlIEdyYWZpa2VuOgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBjbGFzcywgeSA9IGh3eSkpICsgCiAgZ2VvbV9ib3hwbG90KCkKZ2dwbG90KGRhdGEgPSBtcGcsIG1hcHBpbmcgPSBhZXMoeCA9IGNsYXNzLCB5ID0gaHd5KSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjApCmdncGxvdChkYXRhID0gbXBnLCBtYXBwaW5nID0gYWVzKHggPSBkaXNwbCkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKLSAgIGBjb29yZF9xdWlja21hcCgpYDogRGFyc3RlbGx1bmcgdm9uIEthcnRlbi4KCmBgYHtyfQpueiA8LSBtYXBfZGF0YSgibnoiKQpnZ3Bsb3QobnosIGFlcyhsb25nLCBsYXQsIGdyb3VwID0gZ3JvdXApKSArCiAgZ2VvbV9wb2x5Z29uKGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAiYmxhY2siKQpgYGAKCi0gICBgY29vcmRfcG9sYXIoKWA6IFZlcndlbmR1bmcgdm9uCiAgICBbUG9sYXJrb29yZGluYXRlbl0oaHR0cHM6Ly9kZS53aWtpcGVkaWEub3JnL3dpa2kvUG9sYXJrb29yZGluYXRlbikuCgpgYGB7cn0KYmFyIDwtIGdncGxvdChkYXRhID0gbXBnKSArIApnZW9tX2JhcihtYXBwaW5nID0gYWVzKHggPSBjbGFzcywgZmlsbCA9IGNsYXNzKSwgd2lkdGggPSAxKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwpCgpiYXIgKyBjb29yZF9mbGlwKCkKYmFyICsgY29vcmRfcG9sYXIoKQpgYGAKCkFuaGFuZCBkaWVzZXMgQmVpc3BpZWxlcyBzZWhlbiBTaWUgYXVjaCBiZXJlaXRzLCBkYXNzIGVzIHdlaXRlcmUgTGF5ZXIKZsO8ciBkaWUgQW5wYXNzdW5nIGRlciBMYWJlbCAoYGxhYnNgKSBnaWJ0LgoKIyMgTGF5ZXIKCiMjIyBFcndlaXRlcnRlcyBLb2NocmV6ZXB0CgpTaWUgaGFiZW4gYmVyZWl0cyBnZXNlaGVuLCBkYXNzIHNpY2ggaW4gYGdncGxvdGAgYXVzIHNpZWJlbiBQYXJhbWV0ZXJuCmFsbGUgZ3J1bmRsZWdlbmRlbiBHcmFmaWtlbiBlcnN0ZWxsZW4gbGFzc2VuLgoKRGF6dSB6w6RobHQgZGVyIERhdGVuc2F0eiBpbiBgZ2dwbG90KClgLCBlaW4gYGdlb21gIG1pdCBlaW5lbSBgbWFwcGluZ2AsCmVpbmVyIFRyYW5zZm9ybWF0aW9uIGBzdGF0YCwgZWluZXIgUG9zaXRpb24gYHBvc2l0aW9uYCBzb3dpZSBlaW5lbQpLb29yZGluYXRlbnlzdGVtIHVuZCBlaW5lbSBTY2hlbWEgZsO8ciBkaWUgRmFjZXR0ZW4vR2l0dGVyLgoKU29taXQga2FubiBzaWNoIGRhcyBLb2NocmV6ZXB0IGVyd2VpdGVybiBhdWY6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoZGF0YSA9IDxEQVRBPikgKyAKICA8R0VPTV9GVU5DVElPTj4oCiAgICAgbWFwcGluZyA9IGFlcyg8TUFQUElOR1M+KSwKICAgICBzdGF0ID0gPFNUQVQ+LCAKICAgICBwb3NpdGlvbiA9IDxQT1NJVElPTj4KICApICsKICA8Q09PUkRJTkFURV9GVU5DVElPTj4gKwogIDxGQUNFVF9GVU5DVElPTj4KYGBgCgpTbyBsw6Rzc3Qgc2ljaCBkYXMgRXJzdGVsbGVuIHZvbiBHcmFmaWtlbiBhdWYgZm9sZ2VuZGUgU2Nocml0dGUKYmVzY2hyw6Rua2VuOgoKMS4gIERhdGVuc2F0eiBhdXN3w6RobGVuCgoyLiAgYGdlb21gIGF1c3fDpGhsZW4gKGdnZi4gbWl0IHN0YXRpc3Rpc2NoZXIgVHJhbnNmb3JtYXRpb24gYHN0YXRgKQoKMy4gIE9wdGlvbmFsOiBBZXN0aGV0aWNzIGRlZmluaWVyZW4KCjQuICBPcHRpb25hbDogS29vcmRpbmF0ZW5zeXN0ZW0gYXVzd8OkaGxlbgoKNS4gIE9wdGlvbmFsOiBgZmFjZXRgIGF1c3fDpGhsZW4KCkRlc3dlaXRlcmVuIGxhc3NlbiBzaWNoIFNrYWxpZXJ1bmdlbiwgTGVnZW5kZW4sIExhYmVsLCB1c3cuIGViZW5mYWxscwpkdXJjaCB3ZWl0ZXJlIEZ1bmt0aW9uZW4vTGF5ZXIgYW5wYXNzZW4uCgojIyMgV2VpdGVyZSBBbnBhc3N1bmdzbcO2Z2xpY2hrZWl0ZW4KCk5lYmVuIGRlbiBgZ2VvbXNgIGvDtm5uZW4gYXVjaCB3ZWl0ZXJlIExheWVyIGhpbnp1Z2Vmw7xndCB3ZXJkZW4sIHVtCmJlaXNwaWVsc3dlaXNlIGRpZSBMZWdlbmRlLCBMYWJlbCBvZGVyIMOcYmVyc2NocmlmdCB6dSBiZWFyYmVpdGVuLgpQcmluemlwaWVsbCBsYXNzZW4gc2ljaCBhbGxlIEVsZW1lbnRlIGRlciBgZ2dwbG90YCBHcmFmaWtlbiBtYW51ZWxsCmFucGFzc2VuLCBzb2Zlcm4gZGllcyBub3R3ZW5kaWcgaXN0LgoKV2VpdGVyZSBMYXllciBsYXNzZW4gc2ljaCBuYWNoIEJlZGFyZiBhdXMgZGVtCltTcGlja3pldHRlbF0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24ucGRmKQplbnRuZWhtZW46CgotICAgYGxhYnMoKWA6IMOEbmRlcmUgTGFiZWwgZGVyIEdyYWZpazoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gaHd5LCB5ID0gZGlzcGwsIGNvbG9yID0gY2xhc3MpKSArCiAgbGFicyh4ID0gIktyYWZ0c3RvZmZlcnNwYXJuaXMgKEF1dG9iYWhubWVpbGVuIHBybyBHYWxsb25lKSIsIHkgPSAiSHVicmF1bSAoaW4gTGl0ZXIpIiwKICAgICAgIHRpdGxlID0gIktyYWZ0c3RvZmZlZmZpemllbnogdm9uIEF1dG9zIGRlciBFUEEiKQpgYGAKCi0gICBgYW5ub3RhdGUoKWA6IFRleHQvRWxlbWVudGUgYW4gZWluZXIgYmVzdGltbXRlbiBQb3NpdGlvbiBlaW5mw7xnZW46CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGh3eSwgeSA9IGRpc3BsLCBjb2xvciA9IGNsYXNzKSkgKwogIGFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSAzMCwgeSA9IDUsIGxhYmVsID0gIlRleHQgZWluZsO8Z2VuIikKYGBgCgotICAgYHRoZW1lKClgOiBQb3NpdGlvbiBkZXIgTGVnZW5kZToKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gaHd5LCB5ID0gZGlzcGwsIGNvbG9yID0gY2xhc3MpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICMgb2RlciAiYm90dG9tIiwgImxlZnQiLCAicmlnaHQiCmBgYAoKLSAgIGB4bGltKClgIHVuZCBgeWxpbSgpYDogR3JhZmlrIGF1ZiBlaW5lbiBCZXJlaWNoIGRlciB4LSBvZGVyIHktQWNoc2UKICAgIGJlc2NocsOkbmtlbjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gaHd5LCB5ID0gZGlzcGwsIGNvbG9yID0gY2xhc3MpKSArCiAgeGxpbSgxNSwgMjUpICsKICB5bGltKDMsIDYpCmBgYAoKSmUgbmFjaCBGcmFnZXN0ZWxsdW5nIG9kZXIgVm9ybGllYmVuIGxhc3NlbiBzaWNoIGFsbGUgRWxlbWVudGUgZGVyCkdyYWZpayBhbnBhc3Nlbi4gRGVyIEF1ZmJhdSBkZXIgR3JhZmlrIGJsZWlidCBnbGVpY2ggdW5kIGVzIGvDtm5uZW4KZWluZmFjaCB3ZWl0ZXJlIExheWVyIGhpbnp1Z2Vmw7xndCB3ZXJkZW4uIEhpZXJ6dSBrYW5uIGJlaSBzcGV6aWVsbGVuCkFuZm9yZGVydW5nZW4gYXVmIGRlbiBTcGlja3pldHRlbCBnZXNjaGF1dCB3ZXJkZW4gb2RlciBiZWkgZWluZXIKU3VjaG1hc2NoaW5lIG5hY2hnZXNjaGxhZ2VuIHdlcmRlbi4KCiMgQXVmZ2FiZW4KCkbDvHIgZGllIMOcYnVuZ3NhdWZnYWJlbiBnZWhlbiB3aXIgenUgZGVtIGBkaWFtb25kc2AgRGF0ZW5zYXR6IMO8YmVyLiBEZXIKRGF0ZW5zYXR6IGJlZmFzc3Qgc2ljaCBtaXQgUHJlaXMsIFF1YWxpdMOkdCB1bmQgd2VpdGVyZW4gRWlnZW5zY2hhZnRlbgp2b24ga25hcHAgNTQuMDAwIERpYW1hbnRlbi4KClp1ZXJzdCBnZWJlbiB3aXIgd2llZGVyIGRpZSBmb2xnZW5kZW4gQmVmZWhsZSBlaW4sIHVtIGRpZSBCaWJsaW90aGVrCmB0aWR5dmVyc2VgIHp1IGxhZGVuIHVuZCBkZW4gRGF0ZW5zYXR6IGVpbnp1c2VoZW46CgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmRpYW1vbmRzCmBgYAoKVW0gbWVociDDvGJlciBkZW4gRGF0ZW5zYXR6IHp1IGVyZmFocmVuLCBnZWJlbiBTaWUgd2llZGVyIGRlbiBIaWxmc2JlZmVobApgP2RpYW1vbmRzYCBlaW4uCgojIyDDnGJ1bmdzYXVmZ2FiZW4KCjEuICBFcnN0ZWxsZW4gU2llIGVpbmUgR3JhZmlrIHZvbiBkZW4gVmFyaWFibGVuIGBjYXJhdGAgdW5kIGBwcmljZWAgZGVzCiAgICBgZGlhbW9uZHNgIERhdGVuc2F0emVzLCB3b2JlaSBTaWUgZGllIERhdGVucHVua3RlIG5hY2ggZGVyIEthdGVnb3JpZQogICAgYGNsYXJpdHlgIGbDpHJiZW4uCgoyLiAgRXJzdGVsbGVuIFNpZSBlaW5lIHdlaXRlcmUgR3JhZmlrIHZvbiBkZW4gVmFyaWFibGVuIGBjYXJhdGAgdW5kCiAgICBgcHJpY2VgIGRlcyBgZGlhbW9uZHNgIERhdGVuc2F0emVzLCB3b2JlaSBTaWUgZWluIGBmYWNldGAgZsO8ciBkaWUKICAgIEthdGVnb3JpZSBgY2xhcml0eWAgaGluenVmw7xnZW4uIFdvcmluIGxpZWdlbiBkaWUgVm9yLSB1bmQgTmFjaHRlaWxlCiAgICBkZXIgRmFyYi1BZXN0aGV0aWNzIHVuZCBkZXIgTnV0enVuZyB2b24gYGZhY2V0c2A/CgozLiAgVmVyd2VuZGVuIFNpZSBudW4gYWxzIGBmYWNldGAgZWluZSBrb250aW51aWVybGljaGUgVmFyaWFibGUKICAgIHdpZSB6LiBCLiBgdGFibGVgLiBXYXMgcGFzc2llcnQgaGllcj8KCjQuICBWZXJ3ZW5kZW4gU2llIG51biBlaW4gYGZhY2V0YCBtaXQgendlaSBWYXJpYWJsZW4gYGN1dGAgdW5kCiAgICBgY2xhcml0eWAsIHVtIGRpZSBWYXJpYWJsZW4gYGNhcmF0YCB1bmQgYHByaWNlYCB6dSB2ZXJnbGVpY2hlbi4gV2FzCiAgICBrw7ZubmVuIFNpZSBkaWVzZXIgR3JhZmlrIGVudG5laG1lbj8KCjUuICBFcnN0ZWxsZW4gU2llIGVpbmUgcGFzc2VuZGUgR3JhZmlrLCB1bSBkaWUgVmVydGVpbHVuZ2VuIHZvbiBgY3V0YAogICAgdW5kIGBwcmljZWAgZGFyenVzdGVsbGVuLiBEZW5rZW4gU2llIGF1Y2ggYW4gcGFzc2VuZGUgR3J1cHBpZXJ1bmdlbiwKICAgIEZhcmJlbiB1bmQgUG9zaXRpb25lbiEKCjYuICBFcnN0ZWxsZW4gU2llIGRpZSBmb2xnZW5kZW4gR3JhZmlrZW46CgohW1ZlcnNjaGllZGVuZSBHcmFmaWtlbl0oaW1hZ2VzL2dyYXBoX2dyaWQucG5nKQoKNy4gIEVyc3RlbGxlbiBTaWUgZWluIEJhbGtlbmRpYWdyYW1tIHZvbiBkZXIgVmFyaWFibGUgYGN1dGAgdW5kIGbDvGdlbgogICAgU2llIGVpbiBgZmFjZXRgIG1pdCBkZXIgVmFyaWFibGUgYGNsYXJpdHlgIGVpbi5cCiAgICDDhG5kZXJuIFNpZSB6dXPDpHR6bGljaCBkZW4gVGl0ZWwgdW5kIGRpZSBBY2hzZW5iZXNjaHJpZnR1bmdlbiBtaXQKICAgIGBsYWJzKClgIHVuZCB2ZXJzY2hpZWJlbiBTaWUgZGllIExlZ2VuZGUgbmFjaCB1bnRlbiBtaXQgYHRoZW1lKClgLgo=