1 R-Bibliotheken

1.1 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:

v <- c(1, 3, 5)
mean(v)
[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:

base::mean(v)
[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:

boxplot(v)

  • 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

1.2 Verwendung von externen R Bibliotheken

1.2.1 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")
  • Die Installation kann einige Momente in Anspruch nehmen.

  • Alternativ kann das Packages-Menu (Fenster rechts unten) von RStudio verwendet werden.

1.2.2 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

1.2.3 Externe R Bibliotheken laden

Sofern die Bibliothek installiert ist, gibt es generell zwei Möglichkeiten:

  1. Die Bibliothek laden und die Funktionen direkt verwenden.

    1. Hierbei muss die Bibliothek in jeder neuen Session erneut geladen werden.

    2. Hierzu gibt es den library Befehl:

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

Nun lässt sich die ggplot Funktion verwenden:

ggplot(data = iris, mapping = aes(y = Sepal.Width, x = Sepal.Length)) + geom_point()

  1. Den Namespace der Bibliothek direkt verwenden.
    1. Hierbei muss die Bibliothek nicht explizit geladen werden. Jedoch ist diese Methode etwas wortreicher obgleich präziser.

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

  • Keine Fehlermeldung …

1.2.4 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.

1.3 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

2 tidyverse

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

3 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:

3.1 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.

3.1.1 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).

mpg

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.

3.2 Grapfiken mit ggplot

3.3 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:

  1. displ (displacement): Hubraum (in Liter)

  2. hwy (highway miles per gallon): Kraftstoffverbrauch (Effizienz) auf der Autobahn

3.3.1 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.`

3.3.2 Aufbau von ggplot

Der erste Befehl ist immer ggplot(), welches das Koordinatensystem erstellt. Als erstes Argument wird immer der Datensatz ausgewählt:

ggplot(data = mpg)

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.

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

3.4 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?

3.4.1 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.

3.4.1.1 Punktgröße (size)

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

3.4.1.2 Transparenz (alpha)

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

3.4.1.3 Form (shape)

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

Beachten Sie die Warnung, die uns zeigt, dass ggplot lediglich 6 Formen auf einmal betrachtet.

Hierbei werden die SUVs außenvorgelassen.

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

4 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:

?iris

Um Hilfe zu den Aesthetics und dem Streudiagramm zu erhalten, geben Sie den folgenden Befehl ein:

?geom_point

4.1 Übungsaufgaben

  1. Wie viele Reihen und Spalten hat der Dataframe iris?

  2. Welche Variablen aus iris sind qualitativ? Welche sind quantitativ?

  3. Was bedeutet die Spalte Petal.Width?

  4. Erstellen Sie ein Streudiagramm mit Petal.Length und Petal.Width.

  5. Erstellen Sie ein Streudiagramm mit Species und Petal.Length. Ist diese Grafik sinnvoll?

  6. Erstellen Sie ein Streudiagramm mit Sepal.Length und Sepal.Width.

    1. Färben Sie zunächst alle Punkte grün ein und ändern Sie die Form.

    2. 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.

    3. Ändern Sie ebenfalls die Aesthetics alpha, fill und stroke.

    4. Was passiert, wenn Sie einer Aesthetic eine quantitative Variable zuordnen? Testen Sie beispielsweise aes(color = Sepal.Length) oder aes(size = Sepal.Length).

    5. 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=