R-Bibliotheken
Standardbibliotheken
Die übliche R-Installation bietet bereits durch Bibliotheken wie z.
B. base
, stats
und graphics
einige grundlegende Funktionalitäten.
Unter anderem stehen Ihnen diverse statistische Methoden wie die
Berechnung des Mittelwerts zur Verfügung:
[1] 3
Funktionen sind hierbei durch die Klammern ersichtlich, wie z. B.
mean()
.
Andere Objekte (Daten, Funktionsargumente) haben keine Klammern,
wie z. B. der Vektor v
.
Um bei dem Funktionsaufruf auf die Bibliothek zu verweisen, kann
diese auch explizit angegeben werden durch den Bibliotheknamen und zwei
Doppelpunkte:
[1] 3
Auch das Erstellen von Grafiken ist Teil der Grundfunktionalität von
R
und ist in der Bibliothek graphics
implementiert.
So lassen sich diverse Grafiken direkt erstellen:
- Auch komplexere mathematische Parameter und Modelle lassen sich
relativ einfach mit
R
bestimmen.
Beispielsweise lässt sich ein Regressionsmodell in wenigen Schritten
erstellen:
x <- c(1, 3, 5)
y <- c(15, 40, 45)
model <- lm(y ~ x)
summary(model)
Call:
lm(formula = y ~ x)
Residuals:
1 2 3
-3.333 6.667 -3.333
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 10.833 9.860 1.099 0.470
x 7.500 2.887 2.598 0.234
Residual standard error: 8.165 on 1 degrees of freedom
Multiple R-squared: 0.871, Adjusted R-squared: 0.7419
F-statistic: 6.75 on 1 and 1 DF, p-value: 0.2339
Verwendung von
externen R Bibliotheken
Installation
Standard-Bibliotheken wie base
stehen in
R
stets zur Verfügung und müssen nicht explizit geladen
werden.
Externe Bibliotheken müssen hingegen heruntergeladen, installiert
und explizit geladen werden, damit sie in R
verwendet
werden können.
Ein Beispiel ist hier die tidyverse
Bibliothek,
welche die grundlegenden Funktionalität von R im Bereich Data Science
erweitert.
Mit dem folgenden Befehl kann die externe Bibliothek heruntergeladen
und installiert werden:
install.packages("tidyverse")
Nach der
Installation
Die Bibliothek ist aber noch nicht verwendbar:
ggplot(data = iris, mapping = aes(y = Sepal.Width, x = Sepal.Length)) + geom_point()
Fehler in ggplot(data = iris, mapping = aes(y = Sepal.Width, x = Sepal.Length)) :
konnte Funktion "ggplot" nicht finden
Externe R
Bibliotheken laden
Sofern die Bibliothek installiert ist, gibt es generell zwei
Möglichkeiten:
Die Bibliothek laden und die Funktionen direkt verwenden.
Hierbei muss die Bibliothek in jeder neuen Session erneut geladen
werden.
Hierzu gibt es den library
Befehl:
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()
Nun lässt sich die ggplot
Funktion verwenden:
ggplot(data = iris, mapping = aes(y = Sepal.Width, x = Sepal.Length)) + geom_point()
- Den
Namespace
der Bibliothek direkt verwenden.
Hierbei muss die Bibliothek nicht explizit geladen werden. Jedoch
ist diese Methode etwas wortreicher obgleich präziser.
Hierzu kann folgende Syntax verwendet werden, sofern die
Bibliothek bereits installiert wurde:
ggplot2::ggplot(data = iris, mapping = aes(y = Sepal.Width, x = Sepal.Length)) + ggplot2::geom_point()
Kurzer Einschub:
Namespaces
Namespaces werden verwendet, um Funktionen oder Objekte aus
Bibliotheken zu verwenden.
Namespaces lassens sich über ::
oder
:::
verwenden. Vor dem Operator steht der Name der
Bibliothek und hinter dem Operator der Funktionsname.
Vor allem kann mit Namespaces verhindert werden, dass bei
unterschiedlichen Bibliotheken, welche die denselben Funktionsnamen
enthalten, die zuletzt geladenen die vorherigen überdecken.
Hilfe/Problemlösung
Fehlermeldungen: Suchmaschine mit Anhängsel
R
.
Allgemeine Probleme: Suchmaschine oder stackoverflow.
Hilfe zu Funktionen: ?funktionsname
in
R
eingeben
Schauen Sie genau auf Ihren Code und vergleichen Sie, wo das
Problem liegt
Häufige Fehlerquellen:
Falsch geschriebene Namen/Funktionen. Achten Sie auf Groß-und
Kleinschreibung
Falsche Klammerung: Auf jede geöffnete Klammer (
muss eine geschlossene Klammer )
folgen
Falsche Zeichensetzung: Auf jedes Anführungszeichen
"
folgt ein weiteres "
für
Zeichenketten
Syntaxhighlighting ist hier hilfreich!
Falls unten in der Konsole ein +
steht und nichts
ausgeführt wird, wurde ein Befehl nicht vollendet:
- Drücken Sie Escape und suchen Sie nach Fehlern in dem Code
In ggplot
muss nach jedem layer ein +
stehen, nicht vor dem layer (hierzu später mehr)
Suchmaschinen sind Ihr Freund!
Fehler sind normal und natürlich
Frustration ist ebenso natürlich, wird sich aber mit der Zeit
legen, wenn Sie den einzelnen Fehler häufiger begegnen
tidyverse
Was ist
tidyverse?
tidyverse umfasst R-Bibliotheken aus dem Bereich Data Science:
ggplot2
für Datenvisualisierung
tidyr
für Datenbereinigung (fehlende Werte,
Transformationen, usw.)
tibble
als Erweiterung von Dataframes
readr
zum Einlesen verschiedener
Datentabellen
purrr
für funktionelle Programmierung und
Listenverarbeitung (map, reduce, combine)
dplyr
für Datenmanipulation von Dataframes
stringr
für Manipulation von Strings
(Zeichenketten)
forcats
für Manipulation von Faktoren (kategoriale
Variablen)
Visualisierung
Um Fragestellungen in einem ersten Schritt nachzugehen und Daten für
sich und andere verständlicher zu machen, bieten sich Grafiken immer
an:
“Ein Bild sagt mehr als tausend Worte.”
“The simple graph has brought more information to the data analyst’s
mind than any other device.” — John Tukey
Zunächst sollte sich eine Übersicht über die Daten geschafft
werden:
Erste Schritte
Zunächst muss sich überlegt werden, welche Fragestellungen
beantwortet werden sollen.
Dann können Verteilungen und Zusammenhänge direkt in Grafiken
veranschaulicht werden.
Der
mpg
Datensatz
Betrachten wir den mpg
Datensatz, welcher Eckdaten
bezüglich Autos im Rahmen von Treibstoffersparnis erfasst hat.
Der Datensatz ist ein Dataframe, das bedeutet es liegt eine Tabelle
vor, bei der die Spalten Variablen beschreiben und die Reihen die
Beobachtungen (Autos).
Tatsächlich liegt hier ein tibble
vor, eine Erweiterung
des standardmäßigen Dataframes, die einiges vereinf.
Hierzu werden wir im Verlaufe des Kurses mehr sehen.
Falls Sie mehr über den Datensatz mpg
wissen wollen,
tippen Sie ?mpg
ein.
Grapfiken mit
ggplot
Die erste
Fragestellung
Nehmen wir an, wir wollen den Zusammenhang der Größe des
Verbrennungsmotors und dem Kraftstoffverbrauch betrachten.
Welche Beziehung liegt hier vor? Positiv? Negativ? Linear?
Quadratisch?
Intuitiv sollten wir bereits erahnen, dass ein großer Motor mit
einem erhöhten Verbrauch einhergeht.
Betrachten wir hierzu zwei Variablen aus dem Datensatz
mpg
:
displ
(displacement): Hubraum (in Liter)
hwy
(highway miles per gallon): Kraftstoffverbrauch
(Effizienz) auf der Autobahn
Die erste
Grafik
Um die Variable displ
auf die x-Achse abzubilden und die
Variable hwy
auf die y-Achse, kann der folgende Befehl
genutzt werden:
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy))
Wir sehen den Trend, dass ein erhöhter Hubraum (x-Achse) mit einem
sinkenden Kraftstoffverbrauch (y-Achse) einhergeht.
Eine ähnliche, aber weniger anschauliche Grafik lässt sich auch mit
den Standardfunktionen aus R
erstellen:
plot(x = mpg$displ, y = mpg$hwy)
Im Laufe der Zeit werden Sie sehen, dass ggplot
das
Erstellen von ansehnlich aber auch komplexen Grafiken ermöglicht.`
Aufbau von
ggplot
Der erste Befehl ist immer ggplot()
, welches das
Koordinatensystem erstellt. Als erstes Argument wird immer der Datensatz
ausgewählt:
Sie sehen, dass nur ein leerer Graph erstellt wird.
Dann werden Schritt für Schritt weitere Komponenten (layer,
Schichten) hinzugefügt.
Hier wurde ein weiterer Layer durch die Funktion
geom_point()
hinzugefügt, sodass Punkte in die Grafik
gezeichnet wurde.
Als Resultat entsteht ein einfacher Scatterplot. Weitere Layer werden
wir im Laufe des Kurses noch kennenlernen, z. B. um Label hinzuzufügen,
Punkte hervorzuheben oder Farben zu ändern.
In jeder dieser “geom”-Funktionen wird ein mapping
Parameter mitgegeben. Dieser gibt an, welche Variablen in die Grafik
gezeichnet werden sollen. Diese Angabe findet immer mit Hilfe der
aes() (= aesthetics)
Funktion statt, wo x
und
y
Werte definiert werden.
Kochrezept
Die Verwendung von ggplot
läuft immer ähnlich ab.
Zuerst wird die ggplot()
Funktion aufgerufen gefolgt von
der “geom”-Funktion für die entsprechende Grafik.
Dementsprechend könnte eine Vorlage so aussehen:
ggplot(data = <DATA>) +
<GEOM_FUNKTION>(mapping = aes(x = <X>, y = <Y>))
Aesthetics
Aesthetics sind visuelle Eigenschaft der Objekte einer Grafik. Mit
Hilfe von Aesthetics können die Achsen verändert werden, aber auch die
Größe, Form oder Farbe der Objekte (z.B. Punkte) in der Grafik.
Durch das Abbilden der Datenpunkte/Variablen auf die Aesthetics
lassen sich bestimmte Aspekte hervorheben.
Beispielsweise können wir die Datenpunkte entsprechend der
class
Variable färben:
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, color = class))
Hierzu muss lediglich in der Aesthetic aes()
eine
Eigenschaft (color
) der Variable class
zugeordnet werden.
Die restlichen Zuweisungen der Farben und das Einfügen der Legende
werden automatisch durch ggplot
ausgeführt.
Nun stellt sich die Frage, ob es einen Informationsgewinn liefert,
die unterschiedlichen Klassen der Autos anzuschauen. Fällt Ihnen etwas
auf?
Weitere
Aesthetics
In dem vorherigem Graphen wurde der Klasse der Autos eine Farbe
zugeordnet. Darüber hinaus gibt es noch weitere Aesthetics.
Betrachten wir einige weitere Beispiele.
Punktgröße
(size
)
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, size = class))
Transparenz
(alpha
)
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy, alpha = class))
Weitere
Optionen
Aesthetics lassen sich ebenfalls außerhalb von aes()
verwenden.
Hierbei sind die Aesthetics unabhängig von Variablen, d. h. für alle
Datenpunkte gültig:
ggplot(data = mpg) +
geom_point(mapping = aes(x = displ, y = hwy), color = "blue", size = 3, shape = "0")
Weitere Optionen umfassen:
color
: Randfarbe als Zeichenkette (Rand der
Punkte)
size
: Füllgröße der Datenpunkte in mm (Innerer
Bereich der Punkte)
shape
: Form der Datenpunkte gemäß dieser
Tabelle
alpha
: Transparenz (in Prozent zwischen 0 und
1)
fill
: Füllfarbe als Zeichenkette (Innerer Bereich
der Punkte)
group
: Gruppierung der Daten (relevant für weitere
geom
Funktionen)
stroke
: Randgröße der Datenpunkte in mm (Rand der
Punkte)
Aufgaben
In diesen Aufgaben sollen Sie den Datensatz iris
betrachten.
Geben Sie zunächst den folgenden Befehl ein, um sich einen Übersicht
über die Daten zu schaffen:
library(tidyverse)
iris <- as_tibble(iris)
iris
Um mehr über den Datensatz zu erfahren, geben Sie den folgenden
Befehl ein:
Um Hilfe zu den Aesthetics und dem Streudiagramm zu erhalten, geben
Sie den folgenden Befehl ein:
Übungsaufgaben
Wie viele Reihen und Spalten hat der Dataframe
iris
?
Welche Variablen aus iris
sind qualitativ? Welche
sind quantitativ?
Was bedeutet die Spalte Petal.Width
?
Erstellen Sie ein Streudiagramm mit Petal.Length
und
Petal.Width
.
Erstellen Sie ein Streudiagramm mit Species
und
Petal.Length
. Ist diese Grafik sinnvoll?
Erstellen Sie ein Streudiagramm mit Sepal.Length
und
Sepal.Width
.
Färben Sie zunächst alle Punkte grün ein und ändern Sie die
Form.
Nutzen Sie nun Aesthetics, um die Farbe und Form der Punkte gemäß
der Spezies (Variable Species
) zu ändern.
Ändern Sie zusätzlich die Größe aller Punkte.
Ändern Sie ebenfalls die Aesthetics alpha
,
fill
und stroke
.
Was passiert, wenn Sie einer Aesthetic eine quantitative Variable
zuordnen? Testen Sie beispielsweise
aes(color = Sepal.Length)
oder
aes(size = Sepal.Length)
.
Was passiert, wenn Sie zusätzlich in den Aesthetics
aes(color = Sepal.Length < 6)
angeben?
Hierbei handelt es sich nicht direkt um die Zuordnung einer Variablen zu
einer Aesthetic.
LS0tCnRpdGxlOiAiU3RhdGlzdGlrIHVuZCBEYXRhIFNjaWVuY2UgLSBFaW5mw7xocnVuZyBpbiBnZ3Bsb3QiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCiMgUi1CaWJsaW90aGVrZW4KCiMjIFN0YW5kYXJkYmlibGlvdGhla2VuCgpEaWUgw7xibGljaGUgUi1JbnN0YWxsYXRpb24gYmlldGV0IGJlcmVpdHMgZHVyY2ggQmlibGlvdGhla2VuIHdpZSB6LiBCLgpgYmFzZWAsIGBzdGF0c2AgdW5kIGBncmFwaGljc2AgZWluaWdlIGdydW5kbGVnZW5kZSBGdW5rdGlvbmFsaXTDpHRlbi4KClVudGVyIGFuZGVyZW0gc3RlaGVuIElobmVuIGRpdmVyc2Ugc3RhdGlzdGlzY2hlIE1ldGhvZGVuIHdpZSBkaWUKQmVyZWNobnVuZyBkZXMgTWl0dGVsd2VydHMgenVyIFZlcmbDvGd1bmc6CgpgYGB7cn0KdiA8LSBjKDEsIDMsIDUpCm1lYW4odikKYGBgCgotICAgRnVua3Rpb25lbiBzaW5kIGhpZXJiZWkgZHVyY2ggZGllIEtsYW1tZXJuIGVyc2ljaHRsaWNoLCB3aWUgei4gQi4KICAgIGBtZWFuKClgLgoKLSAgIEFuZGVyZSBPYmpla3RlIChEYXRlbiwgRnVua3Rpb25zYXJndW1lbnRlKSBoYWJlbiBrZWluZSBLbGFtbWVybiwKICAgIHdpZSB6LiBCLiBkZXIgVmVrdG9yIGB2YC4KCi0gICBVbSBiZWkgZGVtIEZ1bmt0aW9uc2F1ZnJ1ZiBhdWYgZGllIEJpYmxpb3RoZWsgenUgdmVyd2Vpc2VuLCBrYW5uCiAgICBkaWVzZSBhdWNoIGV4cGxpeml0IGFuZ2VnZWJlbiB3ZXJkZW4gZHVyY2ggZGVuIEJpYmxpb3RoZWtuYW1lbiB1bmQKICAgIHp3ZWkgRG9wcGVscHVua3RlOgoKYGBge3J9CmJhc2U6Om1lYW4odikKYGBgCgpBdWNoIGRhcyBFcnN0ZWxsZW4gdm9uIEdyYWZpa2VuIGlzdCBUZWlsIGRlciBHcnVuZGZ1bmt0aW9uYWxpdMOkdCB2b24gYFJgCnVuZCBpc3QgaW4gZGVyIEJpYmxpb3RoZWsgYGdyYXBoaWNzYCBpbXBsZW1lbnRpZXJ0LgoKU28gbGFzc2VuIHNpY2ggZGl2ZXJzZSBHcmFmaWtlbiBkaXJla3QgZXJzdGVsbGVuOgoKYGBge3Igb3V0LndpZHRoPSczaW4nLCBkcGk9NSwgZmlnLmFsaWduPSJjZW50ZXIifQpib3hwbG90KHYpCmBgYAoKLSAgIEF1Y2gga29tcGxleGVyZSBtYXRoZW1hdGlzY2hlIFBhcmFtZXRlciB1bmQgTW9kZWxsZSBsYXNzZW4gc2ljaAogICAgcmVsYXRpdiBlaW5mYWNoIG1pdCBgUmAgYmVzdGltbWVuLgoKQmVpc3BpZWxzd2Vpc2UgbMOkc3N0IHNpY2ggZWluIFJlZ3Jlc3Npb25zbW9kZWxsIGluIHdlbmlnZW4gU2Nocml0dGVuCmVyc3RlbGxlbjoKCmBgYHtyfQp4IDwtIGMoMSwgMywgNSkKeSA8LSBjKDE1LCA0MCwgNDUpCm1vZGVsIDwtIGxtKHkgfiB4KQpzdW1tYXJ5KG1vZGVsKQpgYGAKCiMjIFZlcndlbmR1bmcgdm9uIGV4dGVybmVuIFIgQmlibGlvdGhla2VuCgojIyMgSW5zdGFsbGF0aW9uCgotICAgU3RhbmRhcmQtQmlibGlvdGhla2VuIHdpZSBgYmFzZWAgc3RlaGVuIGluIGBSYCBzdGV0cyB6dXIgVmVyZsO8Z3VuZwogICAgdW5kIG3DvHNzZW4gbmljaHQgZXhwbGl6aXQgZ2VsYWRlbiB3ZXJkZW4uCgotICAgRXh0ZXJuZSBCaWJsaW90aGVrZW4gbcO8c3NlbiBoaW5nZWdlbiBoZXJ1bnRlcmdlbGFkZW4sIGluc3RhbGxpZXJ0CiAgICB1bmQgZXhwbGl6aXQgZ2VsYWRlbiB3ZXJkZW4sIGRhbWl0IHNpZSBpbiBgUmAgdmVyd2VuZGV0IHdlcmRlbgogICAga8O2bm5lbi4KCi0gICBFaW4gQmVpc3BpZWwgaXN0IGhpZXIgZGllIGB0aWR5dmVyc2VgIEJpYmxpb3RoZWssIHdlbGNoZSBkaWUKICAgIGdydW5kbGVnZW5kZW4gRnVua3Rpb25hbGl0w6R0IHZvbiBSIGltIEJlcmVpY2ggRGF0YSBTY2llbmNlCiAgICBlcndlaXRlcnQuCgpNaXQgZGVtIGZvbGdlbmRlbiBCZWZlaGwga2FubiBkaWUgZXh0ZXJuZSBCaWJsaW90aGVrIGhlcnVudGVyZ2VsYWRlbiB1bmQKaW5zdGFsbGllcnQgd2VyZGVuOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKYGBgCgotICAgRGllIEluc3RhbGxhdGlvbiBrYW5uIGVpbmlnZSBNb21lbnRlIGluIEFuc3BydWNoIG5laG1lbi4KCi0gICBBbHRlcm5hdGl2IGthbm4gZGFzIFBhY2thZ2VzLU1lbnUgKEZlbnN0ZXIgcmVjaHRzIHVudGVuKSB2b24gUlN0dWRpbwogICAgdmVyd2VuZGV0IHdlcmRlbi4KCiMjIyBOYWNoIGRlciBJbnN0YWxsYXRpb24KCkRpZSBCaWJsaW90aGVrIGlzdCBhYmVyIG5vY2ggbmljaHQgdmVyd2VuZGJhcjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsIG1hcHBpbmcgPSBhZXMoeSA9IFNlcGFsLldpZHRoLCB4ID0gU2VwYWwuTGVuZ3RoKSkgKyBnZW9tX3BvaW50KCkKYGBgCgojIyMgRXh0ZXJuZSBSIEJpYmxpb3RoZWtlbiBsYWRlbgoKU29mZXJuIGRpZSBCaWJsaW90aGVrIGluc3RhbGxpZXJ0IGlzdCwgZ2lidCBlcyBnZW5lcmVsbCB6d2VpCk3DtmdsaWNoa2VpdGVuOgoKMS4gIERpZSBCaWJsaW90aGVrIGxhZGVuIHVuZCBkaWUgRnVua3Rpb25lbiBkaXJla3QgdmVyd2VuZGVuLgoKICAgIDEuICBIaWVyYmVpIG11c3MgZGllIEJpYmxpb3RoZWsgaW4gamVkZXIgbmV1ZW4gU2Vzc2lvbiBlcm5ldXQKICAgICAgICBnZWxhZGVuIHdlcmRlbi4KCiAgICAyLiAgSGllcnp1IGdpYnQgZXMgZGVuIGBsaWJyYXJ5YCBCZWZlaGw6CgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKTnVuIGzDpHNzdCBzaWNoIGRpZSBgZ2dwbG90YCBGdW5rdGlvbiB2ZXJ3ZW5kZW46CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLCBtYXBwaW5nID0gYWVzKHkgPSBTZXBhbC5XaWR0aCwgeCA9IFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKMi4gIERlbiBgTmFtZXNwYWNlYCBkZXIgQmlibGlvdGhlayBkaXJla3QgdmVyd2VuZGVuLgogICAgMS4gIEhpZXJiZWkgbXVzcyBkaWUgQmlibGlvdGhlayBuaWNodCBleHBsaXppdCBnZWxhZGVuIHdlcmRlbi4KICAgICAgICBKZWRvY2ggaXN0IGRpZXNlIE1ldGhvZGUgZXR3YXMgd29ydHJlaWNoZXIgb2JnbGVpY2ggcHLDpHppc2VyLgoKICAgIDIuICBIaWVyenUga2FubiBmb2xnZW5kZSBTeW50YXggdmVyd2VuZGV0IHdlcmRlbiwgc29mZXJuIGRpZQogICAgICAgIEJpYmxpb3RoZWsgYmVyZWl0cyBpbnN0YWxsaWVydCB3dXJkZToKCmBgYHtyfQpnZ3Bsb3QyOjpnZ3Bsb3QoZGF0YSA9IGlyaXMsIG1hcHBpbmcgPSBhZXMoeSA9IFNlcGFsLldpZHRoLCB4ID0gU2VwYWwuTGVuZ3RoKSkgKyBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkKYGBgCgotICAgS2VpbmUgRmVobGVybWVsZHVuZyAuLi4KCiMjIyBLdXJ6ZXIgRWluc2NodWI6IE5hbWVzcGFjZXMKCi0gICBOYW1lc3BhY2VzIHdlcmRlbiB2ZXJ3ZW5kZXQsIHVtIEZ1bmt0aW9uZW4gb2RlciBPYmpla3RlIGF1cwogICAgQmlibGlvdGhla2VuIHp1IHZlcndlbmRlbi4KCi0gICBOYW1lc3BhY2VzIGxhc3NlbnMgc2ljaCDDvGJlciBgOjpgIG9kZXIgYDo6OmAgdmVyd2VuZGVuLiBWb3IgZGVtCiAgICBPcGVyYXRvciBzdGVodCBkZXIgTmFtZSBkZXIgQmlibGlvdGhlayB1bmQgaGludGVyIGRlbSBPcGVyYXRvciBkZXIKICAgIEZ1bmt0aW9uc25hbWUuCgotICAgVm9yIGFsbGVtIGthbm4gbWl0IE5hbWVzcGFjZXMgdmVyaGluZGVydCB3ZXJkZW4sIGRhc3MgYmVpCiAgICB1bnRlcnNjaGllZGxpY2hlbiBCaWJsaW90aGVrZW4sIHdlbGNoZSBkaWUgZGVuc2VsYmVuIEZ1bmt0aW9uc25hbWVuCiAgICBlbnRoYWx0ZW4sIGRpZSB6dWxldHp0IGdlbGFkZW5lbiBkaWUgdm9yaGVyaWdlbiDDvGJlcmRlY2tlbi4KCiMjIEhpbGZlL1Byb2JsZW1sw7ZzdW5nCgotICAgRmVobGVybWVsZHVuZ2VuOiBTdWNobWFzY2hpbmUgbWl0IEFuaMOkbmdzZWwgYFJgLgoKLSAgIEFsbGdlbWVpbmUgUHJvYmxlbWU6IFN1Y2htYXNjaGluZSBvZGVyCiAgICBbc3RhY2tvdmVyZmxvd10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS8pLgoKLSAgIEhpbGZlIHp1IEZ1bmt0aW9uZW46IGA/ZnVua3Rpb25zbmFtZWAgaW4gYFJgIGVpbmdlYmVuCgotICAgU2NoYXVlbiBTaWUgZ2VuYXUgYXVmIElocmVuIENvZGUgdW5kIHZlcmdsZWljaGVuIFNpZSwgd28gZGFzIFByb2JsZW0KICAgIGxpZWd0CgotICAgSMOkdWZpZ2UgRmVobGVycXVlbGxlbjoKCiAgICAtICAgRmFsc2NoIGdlc2NocmllYmVuZSBOYW1lbi9GdW5rdGlvbmVuLiBBY2h0ZW4gU2llIGF1ZiBHcm/Dny11bmQKICAgICAgICBLbGVpbnNjaHJlaWJ1bmcKCiAgICAtICAgRmFsc2NoZSBLbGFtbWVydW5nOiBBdWYgamVkZSBnZcO2ZmZuZXRlIEtsYW1tZXIgYChgIG11c3MgZWluZQogICAgICAgIGdlc2NobG9zc2VuZSBLbGFtbWVyIGApYCBmb2xnZW4KCiAgICAtICAgRmFsc2NoZSBaZWljaGVuc2V0enVuZzogQXVmIGplZGVzIEFuZsO8aHJ1bmdzemVpY2hlbiBgImAgZm9sZ3QKICAgICAgICBlaW4gd2VpdGVyZXMgYCJgIGbDvHIgWmVpY2hlbmtldHRlbgoKICAgIFN5bnRheGhpZ2hsaWdodGluZyBpc3QgaGllciBoaWxmcmVpY2ghCgotICAgRmFsbHMgdW50ZW4gaW4gZGVyIEtvbnNvbGUgZWluIGArYCBzdGVodCB1bmQgbmljaHRzIGF1c2dlZsO8aHJ0IHdpcmQsCiAgICB3dXJkZSBlaW4gQmVmZWhsIG5pY2h0IHZvbGxlbmRldDoKCiAgICAtICAgRHLDvGNrZW4gU2llIEVzY2FwZSB1bmQgc3VjaGVuIFNpZSBuYWNoIEZlaGxlcm4gaW4gZGVtIENvZGUKCi0gICBJbiBgZ2dwbG90YCBtdXNzIG5hY2ggamVkZW0gbGF5ZXIgZWluIGArYCBzdGVoZW4sIG5pY2h0IHZvciBkZW0KICAgIGxheWVyIChoaWVyenUgc3DDpHRlciBtZWhyKQoKLSAgIFN1Y2htYXNjaGluZW4gc2luZCBJaHIgRnJldW5kIQoKLSAgIEZlaGxlciBzaW5kIG5vcm1hbCB1bmQgbmF0w7xybGljaAoKLSAgIEZydXN0cmF0aW9uIGlzdCBlYmVuc28gbmF0w7xybGljaCwgd2lyZCBzaWNoIGFiZXIgbWl0IGRlciBaZWl0IGxlZ2VuLAogICAgd2VubiBTaWUgZGVuIGVpbnplbG5lbiBGZWhsZXIgaMOkdWZpZ2VyIGJlZ2VnbmVuCgojIHRpZHl2ZXJzZQoKIyMgV2FzIGlzdCB0aWR5dmVyc2U/Cgp0aWR5dmVyc2UgdW1mYXNzdCBSLUJpYmxpb3RoZWtlbiBhdXMgZGVtIEJlcmVpY2ggRGF0YSBTY2llbmNlOgoKLSAgIGBnZ3Bsb3QyYCBmw7xyIERhdGVudmlzdWFsaXNpZXJ1bmcKCi0gICBgdGlkeXJgIGbDvHIgRGF0ZW5iZXJlaW5pZ3VuZyAoZmVobGVuZGUgV2VydGUsIFRyYW5zZm9ybWF0aW9uZW4sCiAgICB1c3cuKQoKLSAgIGB0aWJibGVgIGFscyBFcndlaXRlcnVuZyB2b24gRGF0YWZyYW1lcwoKLSAgIGByZWFkcmAgenVtIEVpbmxlc2VuIHZlcnNjaGllZGVuZXIgRGF0ZW50YWJlbGxlbgoKLSAgIGBwdXJycmAgZsO8ciBmdW5rdGlvbmVsbGUgUHJvZ3JhbW1pZXJ1bmcgdW5kIExpc3RlbnZlcmFyYmVpdHVuZyAobWFwLAogICAgcmVkdWNlLCBjb21iaW5lKQoKLSAgIGBkcGx5cmAgZsO8ciBEYXRlbm1hbmlwdWxhdGlvbiB2b24gRGF0YWZyYW1lcwoKLSAgIGBzdHJpbmdyYCBmw7xyIE1hbmlwdWxhdGlvbiB2b24gU3RyaW5ncyAoWmVpY2hlbmtldHRlbikKCi0gICBgZm9yY2F0c2AgZsO8ciBNYW5pcHVsYXRpb24gdm9uIEZha3RvcmVuIChrYXRlZ29yaWFsZSBWYXJpYWJsZW4pCgojIFZpc3VhbGlzaWVydW5nCgpVbSBGcmFnZXN0ZWxsdW5nZW4gaW4gZWluZW0gZXJzdGVuIFNjaHJpdHQgbmFjaHp1Z2VoZW4gdW5kIERhdGVuIGbDvHIKc2ljaCB1bmQgYW5kZXJlIHZlcnN0w6RuZGxpY2hlciB6dSBtYWNoZW4sIGJpZXRlbiBzaWNoIEdyYWZpa2VuIGltbWVyIGFuOgoKPiAiRWluIEJpbGQgc2FndCBtZWhyIGFscyB0YXVzZW5kIFdvcnRlLiIKPgo+ICJUaGUgc2ltcGxlIGdyYXBoIGhhcyBicm91Z2h0IG1vcmUgaW5mb3JtYXRpb24gdG8gdGhlIGRhdGEgYW5hbHlzdCdzCj4gbWluZCB0aGFuIGFueSBvdGhlciBkZXZpY2UuIiAtLS0gSm9obiBUdWtleQoKWnVuw6RjaHN0IHNvbGx0ZSBzaWNoIGVpbmUgw5xiZXJzaWNodCDDvGJlciBkaWUgRGF0ZW4gZ2VzY2hhZmZ0IHdlcmRlbjoKCi0gICBXYXMgZsO8ciBEYXRlbnR5cGVuIGxpZWdlbiB2b3I/ID1cPiBRdWFsaXRhdGl2IG9kZXIgUXVhbnRpdGF0aXYKCi0gICBXYXJ1bSB3dXJkZW4gZGllIERhdGVuIGVyZmFzc3QgPVw+IEZyYWdlc3RlbGx1bmdlbj8KCiMjIEVyc3RlIFNjaHJpdHRlCgpadW7DpGNoc3QgbXVzcyBzaWNoIMO8YmVybGVndCB3ZXJkZW4sIHdlbGNoZSBGcmFnZXN0ZWxsdW5nZW4gYmVhbnR3b3J0ZXQKd2VyZGVuIHNvbGxlbi4KCkRhbm4ga8O2bm5lbiBWZXJ0ZWlsdW5nZW4gdW5kIFp1c2FtbWVuaMOkbmdlIGRpcmVrdCBpbiBHcmFmaWtlbgp2ZXJhbnNjaGF1bGljaHQgd2VyZGVuLgoKIyMjIERlciBgbXBnYCBEYXRlbnNhdHoKCkJldHJhY2h0ZW4gd2lyIGRlbiBgbXBnYCBEYXRlbnNhdHosIHdlbGNoZXIgRWNrZGF0ZW4gYmV6w7xnbGljaCBBdXRvcyBpbQpSYWhtZW4gdm9uIFRyZWlic3RvZmZlcnNwYXJuaXMgZXJmYXNzdCBoYXQuCgpEZXIgRGF0ZW5zYXR6IGlzdCBlaW4gRGF0YWZyYW1lLCBkYXMgYmVkZXV0ZXQgZXMgbGllZ3QgZWluZSBUYWJlbGxlIHZvciwKYmVpIGRlciBkaWUgU3BhbHRlbiBWYXJpYWJsZW4gYmVzY2hyZWliZW4gdW5kIGRpZSBSZWloZW4gZGllCkJlb2JhY2h0dW5nZW4gKEF1dG9zKS4KCmBgYHtyfQptcGcKYGBgCgpUYXRzw6RjaGxpY2ggbGllZ3QgaGllciBlaW4gYHRpYmJsZWAgdm9yLCBlaW5lIEVyd2VpdGVydW5nIGRlcwpzdGFuZGFyZG3DpMOfaWdlbiBEYXRhZnJhbWVzLCBkaWUgZWluaWdlcyB2ZXJlaW5mLgoKSGllcnp1IHdlcmRlbiB3aXIgaW0gVmVybGF1ZmUgZGVzIEt1cnNlcyBtZWhyIHNlaGVuLgoKRmFsbHMgU2llIG1laHIgw7xiZXIgZGVuIERhdGVuc2F0eiBgbXBnYCB3aXNzZW4gd29sbGVuLCB0aXBwZW4gU2llIGA/bXBnYAplaW4uCgojIyBHcmFwZmlrZW4gbWl0IGBnZ3Bsb3RgCgojIyBEaWUgZXJzdGUgRnJhZ2VzdGVsbHVuZwoKTmVobWVuIHdpciBhbiwgd2lyIHdvbGxlbiBkZW4gWnVzYW1tZW5oYW5nIGRlciBHcsO2w59lIGRlcwpWZXJicmVubnVuZ3Ntb3RvcnMgdW5kIGRlbSBLcmFmdHN0b2ZmdmVyYnJhdWNoIGJldHJhY2h0ZW4uCgotICAgV2VsY2hlIEJlemllaHVuZyBsaWVndCBoaWVyIHZvcj8gUG9zaXRpdj8gTmVnYXRpdj8gTGluZWFyPwogICAgUXVhZHJhdGlzY2g/CgotICAgSW50dWl0aXYgc29sbHRlbiB3aXIgYmVyZWl0cyBlcmFobmVuLCBkYXNzIGVpbiBncm/Dn2VyIE1vdG9yIG1pdAogICAgZWluZW0gZXJow7ZodGVuIFZlcmJyYXVjaCBlaW5oZXJnZWh0LgoKQmV0cmFjaHRlbiB3aXIgaGllcnp1IHp3ZWkgVmFyaWFibGVuIGF1cyBkZW0gRGF0ZW5zYXR6IGBtcGdgOgoKMS4gIGBkaXNwbGAgKGRpc3BsYWNlbWVudCk6IEh1YnJhdW0gKGluIExpdGVyKQoKMi4gIGBod3lgIChoaWdod2F5IG1pbGVzIHBlciBnYWxsb24pOiBLcmFmdHN0b2ZmdmVyYnJhdWNoIChFZmZpemllbnopCiAgICBhdWYgZGVyIEF1dG9iYWhuCgojIyMgRGllIGVyc3RlIEdyYWZpawoKVW0gZGllIFZhcmlhYmxlIGBkaXNwbGAgYXVmIGRpZSB4LUFjaHNlIGFienViaWxkZW4gdW5kIGRpZSBWYXJpYWJsZQpgaHd5YCBhdWYgZGllIHktQWNoc2UsIGthbm4gZGVyIGZvbGdlbmRlIEJlZmVobCBnZW51dHp0IHdlcmRlbjoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3kpKQpgYGAKCldpciBzZWhlbiBkZW4gVHJlbmQsIGRhc3MgZWluIGVyaMO2aHRlciBIdWJyYXVtICh4LUFjaHNlKSBtaXQgZWluZW0Kc2lua2VuZGVuIEtyYWZ0c3RvZmZ2ZXJicmF1Y2ggKHktQWNoc2UpIGVpbmhlcmdlaHQuCgpFaW5lIMOkaG5saWNoZSwgYWJlciB3ZW5pZ2VyIGFuc2NoYXVsaWNoZSBHcmFmaWsgbMOkc3N0IHNpY2ggYXVjaCBtaXQgZGVuClN0YW5kYXJkZnVua3Rpb25lbiBhdXMgYFJgIGVyc3RlbGxlbjoKCmBgYHtyfQpwbG90KHggPSBtcGckZGlzcGwsIHkgPSBtcGckaHd5KQpgYGAKCkltIExhdWZlIGRlciBaZWl0IHdlcmRlbiBTaWUgc2VoZW4sIGRhc3MgYGdncGxvdGAgZGFzIEVyc3RlbGxlbiB2b24KYW5zZWhubGljaCBhYmVyIGF1Y2gga29tcGxleGVuIEdyYWZpa2VuIGVybcO2Z2xpY2h0LlxgCgojIyMgQXVmYmF1IHZvbiBgZ2dwbG90YAoKRGVyIGVyc3RlIEJlZmVobCBpc3QgaW1tZXIgYGdncGxvdCgpYCwgd2VsY2hlcyBkYXMgS29vcmRpbmF0ZW5zeXN0ZW0KZXJzdGVsbHQuIEFscyBlcnN0ZXMgQXJndW1lbnQgd2lyZCBpbW1lciBkZXIgRGF0ZW5zYXR6IGF1c2dld8OkaGx0OgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKQpgYGAKClNpZSBzZWhlbiwgZGFzcyBudXIgZWluIGxlZXJlciBHcmFwaCBlcnN0ZWxsdCB3aXJkLgoKRGFubiB3ZXJkZW4gU2Nocml0dCBmw7xyIFNjaHJpdHQgd2VpdGVyZSBLb21wb25lbnRlbiAobGF5ZXIsIFNjaGljaHRlbikKaGluenVnZWbDvGd0LgoKSGllciB3dXJkZSBlaW4gd2VpdGVyZXIgTGF5ZXIgZHVyY2ggZGllIEZ1bmt0aW9uIGBnZW9tX3BvaW50KClgCmhpbnp1Z2Vmw7xndCwgc29kYXNzIFB1bmt0ZSBpbiBkaWUgR3JhZmlrIGdlemVpY2huZXQgd3VyZGUuCgpBbHMgUmVzdWx0YXQgZW50c3RlaHQgZWluIGVpbmZhY2hlciBTY2F0dGVycGxvdC4gV2VpdGVyZSBMYXllciB3ZXJkZW4Kd2lyIGltIExhdWZlIGRlcyBLdXJzZXMgbm9jaCBrZW5uZW5sZXJuZW4sIHouIEIuIHVtIExhYmVsIGhpbnp1enVmw7xnZW4sClB1bmt0ZSBoZXJ2b3J6dWhlYmVuIG9kZXIgRmFyYmVuIHp1IMOkbmRlcm4uCgpJbiBqZWRlciBkaWVzZXIgImdlb20iLUZ1bmt0aW9uZW4gd2lyZCBlaW4gYG1hcHBpbmdgIFBhcmFtZXRlcgptaXRnZWdlYmVuLiBEaWVzZXIgZ2lidCBhbiwgd2VsY2hlIFZhcmlhYmxlbiBpbiBkaWUgR3JhZmlrIGdlemVpY2huZXQKd2VyZGVuIHNvbGxlbi4gRGllc2UgQW5nYWJlIGZpbmRldCBpbW1lciBtaXQgSGlsZmUgZGVyCmBhZXMoKSAoPSBhZXN0aGV0aWNzKWAgRnVua3Rpb24gc3RhdHQsIHdvIGB4YCB1bmQgYHlgIFdlcnRlIGRlZmluaWVydAp3ZXJkZW4uCgojIyMgS29jaHJlemVwdAoKRGllIFZlcndlbmR1bmcgdm9uIGBnZ3Bsb3RgIGzDpHVmdCBpbW1lciDDpGhubGljaCBhYi4KClp1ZXJzdCB3aXJkIGRpZSBgZ2dwbG90KClgIEZ1bmt0aW9uIGF1ZmdlcnVmZW4gZ2Vmb2xndCB2b24gZGVyCiJnZW9tIi1GdW5rdGlvbiBmw7xyIGRpZSBlbnRzcHJlY2hlbmRlIEdyYWZpay4KCkRlbWVudHNwcmVjaGVuZCBrw7ZubnRlIGVpbmUgVm9ybGFnZSBzbyBhdXNzZWhlbjoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmdncGxvdChkYXRhID0gPERBVEE+KSArCiAgPEdFT01fRlVOS1RJT04+KG1hcHBpbmcgPSBhZXMoeCA9IDxYPiwgeSA9IDxZPikpCmBgYAoKIyMgQWVzdGhldGljcwoKQWVzdGhldGljcyBzaW5kIHZpc3VlbGxlIEVpZ2Vuc2NoYWZ0IGRlciBPYmpla3RlIGVpbmVyIEdyYWZpay4gTWl0IEhpbGZlCnZvbiBBZXN0aGV0aWNzIGvDtm5uZW4gZGllIEFjaHNlbiB2ZXLDpG5kZXJ0IHdlcmRlbiwgYWJlciBhdWNoIGRpZSBHcsO2w59lLApGb3JtIG9kZXIgRmFyYmUgZGVyIE9iamVrdGUgKHouQi4gUHVua3RlKSBpbiBkZXIgR3JhZmlrLgoKRHVyY2ggZGFzIEFiYmlsZGVuIGRlciBEYXRlbnB1bmt0ZS9WYXJpYWJsZW4gYXVmIGRpZSBBZXN0aGV0aWNzIGxhc3NlbgpzaWNoIGJlc3RpbW10ZSBBc3Bla3RlIGhlcnZvcmhlYmVuLgoKQmVpc3BpZWxzd2Vpc2Uga8O2bm5lbiB3aXIgZGllIERhdGVucHVua3RlIGVudHNwcmVjaGVuZCBkZXIgYGNsYXNzYApWYXJpYWJsZSBmw6RyYmVuOgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSwgY29sb3IgPSBjbGFzcykpCmBgYAoKSGllcnp1IG11c3MgbGVkaWdsaWNoIGluIGRlciBBZXN0aGV0aWMgYGFlcygpYCBlaW5lIEVpZ2Vuc2NoYWZ0CihgY29sb3JgKSBkZXIgVmFyaWFibGUgYGNsYXNzYCB6dWdlb3JkbmV0IHdlcmRlbi4KCkRpZSByZXN0bGljaGVuIFp1d2Vpc3VuZ2VuIGRlciBGYXJiZW4gdW5kIGRhcyBFaW5mw7xnZW4gZGVyIExlZ2VuZGUKd2VyZGVuIGF1dG9tYXRpc2NoIGR1cmNoIGBnZ3Bsb3RgIGF1c2dlZsO8aHJ0LgoKTnVuIHN0ZWxsdCBzaWNoIGRpZSBGcmFnZSwgb2IgZXMgZWluZW4gSW5mb3JtYXRpb25zZ2V3aW5uIGxpZWZlcnQsIGRpZQp1bnRlcnNjaGllZGxpY2hlbiBLbGFzc2VuIGRlciBBdXRvcyBhbnp1c2NoYXVlbi4gRsOkbGx0IElobmVuIGV0d2FzIGF1Zj8KCiMjIyBXZWl0ZXJlIEFlc3RoZXRpY3MKCkluIGRlbSB2b3JoZXJpZ2VtIEdyYXBoZW4gd3VyZGUgZGVyIEtsYXNzZSBkZXIgQXV0b3MgZWluZSBGYXJiZQp6dWdlb3JkbmV0LiBEYXLDvGJlciBoaW5hdXMgZ2lidCBlcyBub2NoIHdlaXRlcmUgQWVzdGhldGljcy4KCkJldHJhY2h0ZW4gd2lyIGVpbmlnZSB3ZWl0ZXJlIEJlaXNwaWVsZS4KCiMjIyMgUHVua3RncsO2w59lIChgc2l6ZWApCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBzaXplID0gY2xhc3MpKQpgYGAKCiMjIyMgVHJhbnNwYXJlbnogKGBhbHBoYWApCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtcGcpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5LCBhbHBoYSA9IGNsYXNzKSkKYGBgCgojIyMjIEZvcm0gKGBzaGFwZWApCgpgYGB7ciwgd2FybmluZz1UUlVFfQpnZ3Bsb3QoZGF0YSA9IG1wZykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGlzcGwsIHkgPSBod3ksIHNoYXBlID0gY2xhc3MpKQpgYGAKCkJlYWNodGVuIFNpZSBkaWUgV2FybnVuZywgZGllIHVucyB6ZWlndCwgZGFzcyBgZ2dwbG90YCBsZWRpZ2xpY2ggNgpGb3JtZW4gYXVmIGVpbm1hbCBiZXRyYWNodGV0LgoKSGllcmJlaSB3ZXJkZW4gZGllIFNVVnMgYXXDn2Vudm9yZ2VsYXNzZW4uCgojIyMjIFdlaXRlcmUgT3B0aW9uZW4KCkFlc3RoZXRpY3MgbGFzc2VuIHNpY2ggZWJlbmZhbGxzIGF1w59lcmhhbGIgdm9uIGBhZXMoKWAgdmVyd2VuZGVuLgoKSGllcmJlaSBzaW5kIGRpZSBBZXN0aGV0aWNzIHVuYWJow6RuZ2lnIHZvbiBWYXJpYWJsZW4sIGQuIGguIGbDvHIgYWxsZQpEYXRlbnB1bmt0ZSBnw7xsdGlnOgoKYGBge3J9CmdncGxvdChkYXRhID0gbXBnKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkaXNwbCwgeSA9IGh3eSksIGNvbG9yID0gImJsdWUiLCBzaXplID0gMywgc2hhcGUgPSAiMCIpCmBgYAoKV2VpdGVyZSBPcHRpb25lbiB1bWZhc3NlbjoKCi0gICBgY29sb3JgOiBSYW5kZmFyYmUgYWxzIFplaWNoZW5rZXR0ZSAoUmFuZCBkZXIgUHVua3RlKQoKLSAgIGBzaXplYDogRsO8bGxncsO2w59lIGRlciBEYXRlbnB1bmt0ZSBpbiBtbSAoSW5uZXJlciBCZXJlaWNoIGRlciBQdW5rdGUpCgotICAgYHNoYXBlYDogRm9ybSBkZXIgRGF0ZW5wdW5rdGUgZ2Vtw6TDnyBbZGllc2VyCiAgICBUYWJlbGxlXShodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0L2UyOGExYjU3YjY2MjJjZjY3ZmQ4YTdlMDFjNmE5OTU1OTE0ZjhmZTkvNjM1YmUvdmlzdWFsaXplX2ZpbGVzL2ZpZ3VyZS1odG1sL3NoYXBlcy0xLnBuZykKCi0gICBgYWxwaGFgOiBUcmFuc3BhcmVueiAoaW4gUHJvemVudCB6d2lzY2hlbiAwIHVuZCAxKQoKLSAgIGBmaWxsYDogRsO8bGxmYXJiZSBhbHMgWmVpY2hlbmtldHRlIChJbm5lcmVyIEJlcmVpY2ggZGVyIFB1bmt0ZSkKCi0gICBgZ3JvdXBgOiBHcnVwcGllcnVuZyBkZXIgRGF0ZW4gKHJlbGV2YW50IGbDvHIgd2VpdGVyZSBgZ2VvbWAKICAgIEZ1bmt0aW9uZW4pCgotICAgYHN0cm9rZWA6IFJhbmRncsO2w59lIGRlciBEYXRlbnB1bmt0ZSBpbiBtbSAoUmFuZCBkZXIgUHVua3RlKQoKIyBBdWZnYWJlbgoKSW4gZGllc2VuIEF1ZmdhYmVuIHNvbGxlbiBTaWUgZGVuIERhdGVuc2F0eiBgaXJpc2AgYmV0cmFjaHRlbi4KCkdlYmVuIFNpZSB6dW7DpGNoc3QgZGVuIGZvbGdlbmRlbiBCZWZlaGwgZWluLCB1bSBzaWNoIGVpbmVuIMOcYmVyc2ljaHQKw7xiZXIgZGllIERhdGVuIHp1IHNjaGFmZmVuOgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQppcmlzIDwtIGFzX3RpYmJsZShpcmlzKQppcmlzCmBgYAoKVW0gbWVociDDvGJlciBkZW4gRGF0ZW5zYXR6IHp1IGVyZmFocmVuLCBnZWJlbiBTaWUgZGVuIGZvbGdlbmRlbiBCZWZlaGwKZWluOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KP2lyaXMKYGBgCgpVbSBIaWxmZSB6dSBkZW4gQWVzdGhldGljcyB1bmQgZGVtIFN0cmV1ZGlhZ3JhbW0genUgZXJoYWx0ZW4sIGdlYmVuIFNpZQpkZW4gZm9sZ2VuZGVuIEJlZmVobCBlaW46CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQo/Z2VvbV9wb2ludApgYGAKCiMjIMOcYnVuZ3NhdWZnYWJlbgoKMS4gIFdpZSB2aWVsZSBSZWloZW4gdW5kIFNwYWx0ZW4gaGF0IGRlciBEYXRhZnJhbWUgYGlyaXNgPwoKMi4gIFdlbGNoZSBWYXJpYWJsZW4gYXVzIGBpcmlzYCBzaW5kIHF1YWxpdGF0aXY/IFdlbGNoZSBzaW5kCiAgICBxdWFudGl0YXRpdj8KCjMuICBXYXMgYmVkZXV0ZXQgZGllIFNwYWx0ZSBgUGV0YWwuV2lkdGhgPwoKNC4gIEVyc3RlbGxlbiBTaWUgZWluIFN0cmV1ZGlhZ3JhbW0gbWl0IGBQZXRhbC5MZW5ndGhgIHVuZAogICAgYFBldGFsLldpZHRoYC4KCjUuICBFcnN0ZWxsZW4gU2llIGVpbiBTdHJldWRpYWdyYW1tIG1pdCBgU3BlY2llc2AgdW5kIGBQZXRhbC5MZW5ndGhgLgogICAgSXN0IGRpZXNlIEdyYWZpayBzaW5udm9sbD8KCjYuICBFcnN0ZWxsZW4gU2llIGVpbiBTdHJldWRpYWdyYW1tIG1pdCBgU2VwYWwuTGVuZ3RoYCB1bmQKICAgIGBTZXBhbC5XaWR0aGAuCgogICAgMS4gIEbDpHJiZW4gU2llIHp1bsOkY2hzdCBhbGxlIFB1bmt0ZSBncsO8biBlaW4gdW5kIMOkbmRlcm4gU2llIGRpZQogICAgICAgIEZvcm0uCgogICAgMi4gIE51dHplbiBTaWUgbnVuIEFlc3RoZXRpY3MsIHVtIGRpZSBGYXJiZSB1bmQgRm9ybSBkZXIgUHVua3RlCiAgICAgICAgZ2Vtw6TDnyBkZXIgU3BlemllcyAoVmFyaWFibGUgYFNwZWNpZXNgKSB6dSDDpG5kZXJuLlwKICAgICAgICDDhG5kZXJuIFNpZSB6dXPDpHR6bGljaCBkaWUgR3LDtsOfZSBhbGxlciBQdW5rdGUuCgogICAgMy4gIMOEbmRlcm4gU2llIGViZW5mYWxscyBkaWUgQWVzdGhldGljcyBgYWxwaGFgLCBgZmlsbGAgdW5kCiAgICAgICAgYHN0cm9rZWAuCgogICAgNC4gIFdhcyBwYXNzaWVydCwgd2VubiBTaWUgZWluZXIgQWVzdGhldGljIGVpbmUgcXVhbnRpdGF0aXZlCiAgICAgICAgVmFyaWFibGUgenVvcmRuZW4/IFRlc3RlbiBTaWUgYmVpc3BpZWxzd2Vpc2UKICAgICAgICBgYWVzKGNvbG9yID0gU2VwYWwuTGVuZ3RoKWAgb2RlciBgYWVzKHNpemUgPSBTZXBhbC5MZW5ndGgpYC4KCiAgICA1LiAgV2FzIHBhc3NpZXJ0LCB3ZW5uIFNpZSB6dXPDpHR6bGljaCBpbiBkZW4gQWVzdGhldGljcwogICAgICAgIGBhZXMoY29sb3IgPSBTZXBhbC5MZW5ndGggPCA2KWAgYW5nZWJlbj9cCiAgICAgICAgSGllcmJlaSBoYW5kZWx0IGVzIHNpY2ggbmljaHQgZGlyZWt0IHVtIGRpZSBadW9yZG51bmcgZWluZXIKICAgICAgICBWYXJpYWJsZW4genUgZWluZXIgQWVzdGhldGljLgo=