1 R Grundlagen

1.1 Variablen zuweisen

Grundsätzlich lässt sich R als Taschenrechner verwenden und hat hierzu verschiedene Funktionen anzubieten:

2 * 3 + 10
[1] 16
(33 + 5 * 22) / 4
[1] 35.75
sqrt(4) + 1
[1] 3
cos(pi / 4)
[1] 0.7071068

Objekte lassen sich mit = oder <- zuweisen. Um Objekte auf der Konsole auszugeben, können Sie einfach den Namen schreiben und ausführen:

x <- 12 * 4
y = 3 * 6
x
[1] 48
y
[1] 18

Um die Verwechslung mit der Parameterzuweisung in Funktionen zu vermeiden, sollten Sie immer <- verwenden, um Objekte zu erstellen. Mit Hilfe des Shortcuts Alt + '-' können Sie ebenfalls direkt den Operator <- erhalten.

mean(x = c(1, 3, 5, 11, 2)) # '=' bei Funktionen
[1] 4.4
z <- 3 # '<-' bei Zuweisungen
z
[1] 3

Die allgemeine Syntax für Zuweisungen sieht dann so aus:

objekt_name <- wert

1.2 Variablennamen

Die Namen von Variablen müssen mit einem Buchstaben beginnen und dürfen ausschließlich Buchstaben, Zahlen, _ und . enthalten. Namen sollten bereits ihre Bedeutung beschreiben.

Verschiedene Konventionen für Variablennamen sehen so aus:

das_ist_snake_case
mancheLeuteMögenCamelCase
andere.benutzen.punkte
ein_kleinerRest.verwendet_alles.zurSelbenZeit

Verwenden Sie bevorzugt snake_case.

1.3 Automatische Vervollständigung

Ähnlich wie bei diversen Messenger-oder Keyboard-Applikationen kann RStudio Namen/Funktionen vervollständigen.

Angenommen, es liegt ein sehr langer Name vor:

das_ist_wohl_ein_sehr_langer_name <- 55.3

Wenn der Anfang eines Namens in RStudio eingetippt wird, beispielsweise das, und dann TAB gedrückt wird, werden die passenden Namen/Funktionen vorgeschlagen oder gegebenfalls direkt vervollständigt.

Ein weiterer Shortcut ist über Str + ↑ verfügbar. Wenn wir den Anfang eines Befehls eingeben (z. B. das) und Str + ↑ drücken, erhalten wir mögliche Vorschläge, welche zuvor ausgeführt worden. So könnten wir direkt den Wert von das_ist_wohl_ein_sehr_langer_name anpassen, wenn das notwendig wäre.

1.4 Syntax/Rechtschreibung zählt!

In jeder Programmiersprache, so auch in R, ist das korrekte, präzise Schreiben von Namen und Funktionen notwendig.

Tippfehler sowie Groß-und Kleinschreibung sind sehr relevant, denn sonst wird R Ihren Befehl nicht verstehen:

anzahl_steine <- 2 ^ 3 * sqrt(4)

Folgende Befehle geben Fehler aus. Warum?

anzahl_stein
Fehler: Objekt 'anzahl_stein' nicht gefunden
Anzahl_steine
Fehler: Objekt 'Anzahl_steine' nicht gefunden

Als Hilfestellung dient hier die Autovervollständigung.

1.5 Funktionen aufrufen

In R stehen bereits eine Vielzahl an Funktionen standardmäßig zur Verfügung, welche wie folgt aufgerufen werden:

funktions_name(argument1 = wert1, argument2 = wert2, ...)

Auch bei Funktionen ist die automatische Vervollständigung sehr hilfreich. Wenn Sie beispielsweise se eingeben und TAB drücken, erhalten Sie direkt Vorschläge für alle möglichen Optionen (Funktionen, Objekte, usw.), welche Sie mit den Pfeiltasten oder der Maus auswählen können. Wenn wir hierbei seq() auswählen, können wir ebenfalls Hilfestellungen zu der Funktion einsehen. Ebenfalls werden die notwendigen Klammern ( und ) bereits nach der Funktion gesetzt:

seq(1, 10)
 [1]  1  2  3  4  5  6  7  8  9 10

Ebenfalls werden Zeichenketten, die zwischen zwei Anführungszeichen " stehen müssen, automatisch von RStudio als Paar ergänzt:

hallo_welt <- "Hallo Welt!"

Anführungszeichen und Klammern müssen immer in einem Paar auftreten (Funktionen, Zeichenketten, usw.). Bei einem fehlenden Symbol wird RStudio in der Konsole ein + ergänzen, d. h. RStudio wartet auf eine geschlossene Klammer oder fehlende Anführungszeichen:

>   hallo_welt <- "Hallo Welt!
+

Falls Sie ein + in der Konsole sehen, drücken Sie ESCAPE oder vervollständigen Sie den Ausdruck und drücken Sie ENTER.

Falls Sie den Wert einer Zuweisung direkt einsehen wollen, können Sie eine von zwei Optionen auswählen:

sequenz <- seq(1, 10, length.out = 5)
sequenz
[1]  1.00  3.25  5.50  7.75 10.00
(sequenz <- seq(1, 10, length.out = 5))
[1]  1.00  3.25  5.50  7.75 10.00

Wenn Sie eine Klammer um einen Ausdruck legen, wird der Wert ebenfalls zurückgegeben.

2 Datentransformation

2.1 Der flights Datensatz

In diesem Kapitel wird der Datensatz flights betrachtet, welcher alle Abflüge aus New York City des Jahres 2013 umfasst.

Der Datensatz ist in der Bibliothek nycflights13 enthalten und weitere Information lässt sich mit ?flights erhalten.

Zuerst muss nycflights13 installiert werden:

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()
if(!"nycflights13" %in% installed.packages())
    install.packages("nycflights13")
library(nycflights13)

Dann lässt sich der Datensatz betrachten:

flights

Hier sehen wir erneut, dass es sich bei flights um ein Objekt names tibble handelt:

is(flights)
[1] "tbl_df"     "tbl"        "data.frame" "list"       "oldClass"   "vector"    

2.2 Was sind tibble?

tibble (intern tbl_df) gelten als Erweiterung von Dataframes (data.frame) und erleichtern die Einsicht und Arbeit mit Dataframes durch mehr Einheitlichkeit. Mit wenigen Ausnahmen lassen sich tibble genauso wie Dataframes verwenden.

Der tibble zeigt bei der Ausgabe bereits alle wichtigen Eigenschaften der Tabelle:

  • Die Dimension (Anzahl Spalten und Reihen) steht an oberster Stelle

  • (Abgekürzte) Spaltennamen und deren Datentyp

  • Eine begrenzte Ansicht auf die ersten Einträge

Analog zu Dataframes lassen sich bei tibbles die kompletten Tabellen mit View() (z. B. View(flights)) anzeigen.

Die Datentypen umfassen:

  • int steht für Ganzzahlen.

  • dbl steht für Gleitkommazahlen bzw. reelle Zahlen.

  • chr steht für character Vektoren oder Zeichtenketten (strings).

  • dttm steht für Datum-Zeit (Datum + Uhrzeit).

  • lgl steht für logisch, d. h. Vektoren aus TRUE oder FALSE.

  • fctr steht für Faktoren factors, was eine R-interne Repräsentation für Kategorien ist.

  • date steht für Datum.

2.2.1 Einen tibble erstellen

In den meisten Fällen liegen Daten als Dataframes vor. Diese lassen sich mit as_tibble() direkt in einen tibble umwandeln:

as_tibble(iris)

Dieses Beispiel hatten wir bereits am Anfang des Kurses gesehen.

Analog zu Dataframes lassen sich tibble mit der tibble() Funktion erstellen, wobei die Spalten als Vektoren angegeben werden und Spalten mit einer Länge von 1 automatisch wiederholt werden:

tibble(
    blumen = c("Iris", "Margerite", "Nelke", "Orchidee"),
    farbe = c("lila", "weiß", "purpur", "gelb"),
    anzahl = 3,
    kelchblatt_laenge = c(5.2, 6.1, 3.2, 2.2),
    kelchblatt_breite = c(3.5, 4.8, 4, 2.1),
    kelchblatt_flaeche = kelchblatt_laenge * kelchblatt_breite - 1
)

Zusätzlich lassen sich bei tibble auch unübliche Variablennamen verwenden, die mit Zahlen oder Leerzeichen anfangen:

tb <- tibble(
    `=)` = "longsmile",
    ` ` = "emptyspace",
    `9` = "number"
)
tb
tb$`9`
[1] "number"

Diese untypischen Namen müssen jedoch mit `` umklammert werden.

Es gibt noch weitere Möglichkeiten, tibble zu erstellen, z. B. durch tribble().

2.2.2 Vergleich: tibble vs data.frame

2.2.2.1 Ausgabe

tibble zeigen vereinfacht die ersten 10 Einträge und die Datentypen an. Mit Hilfe von print() lässt sich hierbei ebenfalls die Ausgabe verändern:

print(flights, n = 15, width = INF)

Die Ausgabeoptionen lassen sich ebenfalls nach belieben anpassen (siehe hier und hier).

Dataframes hingegen sind hier relativ unübersichtlich.

2.2.2.2 Spalten auswählen

Bei Dataframes wird der [] Operator oder $ verwendet, um die Vektoren der Spalten zu erhalten

Bei tibble verwenden wir hingegen [[]] sowie $, um Spalten durch ihre Position bzw. ihren Namen zu erhalten:

df <- tibble(var1 = 1:5, var2 = 6:10)

# tibble Spalten durch Name auswählen
df$var1
[1] 1 2 3 4 5
df[["var1"]]
[1] 1 2 3 4 5
# Spalte durch Position auswählen
df[[1]]
[1] 1 2 3 4 5

Zusätzlich gibt [] bei tibble einheitlich einen tibble wieder:

df[1]

Da die Handhabung dieser Operatoren sehr mühselig ist und ab einer bestimmten Komplexität sehr unübersichtlich, werden in dplyr Funktionen an deren Stelle verwendet (filter() oder select()). Durch die Verwendung von dyplr wird der Code übersichtlicher und durch die Funktionsnamen “spricht der Code”.

2.3 dplyr

2.3.1 Grundlegendes

Die Bibliothek dplyr weist eine Vielzahl an Funktionen auf, um Daten zu manipulieren. Einige davon sind sehr spezifisch und andere werden so gut wie in jeder Analyse verwendet.

Die wichtigsten Funktionen sind:

  • filter(): Beobachtungen (Reihen) anhand ihrer Werte filtern.

  • arrange(): Beobachtungen anordnen (sortieren).

  • select(): Variablen (Spalten) anhand ihres Namens entnehmen.

  • mutate(): Neue Variablen (anhand von Funktionen) erstellen.

  • summarise(): Werte auf eine Zusammenfassung reduzieren (z. B. durch ihren Mittelwert).

Vorgehensweise:

  1. Das erste Argument ist ein Dataframe (oder tibble)

  2. Weitere Argumente beschreiben, wie die Daten manipuliert werden anhand der Variablennamen (ohne Anführungszeichen)

  3. Als Ergebnis wird ein Dataframe geliefert (oder tibble)

2.4 Reihen filtern

Mit der Funktion filter() lassen sich Beobachtungen anhand ihrer Werte filtern. Das erste Argument ist stets der Dataframe, gefolgt von Bedinungen, mit denen gefiltert werden soll:

filter(flights, month == 1 , day == 1)

Hierbei wird ein neuer Dataframe wiedergegeben. Sie sehen, dass der erste Tag des ersten Monats entnommen wird (erster Januar).

Um den neuen Dataframe zu speichern, muss erneut eine Variable angelegt (oder die alte überschrieben) werden:

zehnter_februar <- filter(flights, month == 2, day == 10)
zehnter_februar

2.4.1 Logische Bedingungen

2.4.1.1 Vergleiche

Mit Hilfe der Operatoren >, >=, <, <=, != (ungleich), und == (gleich) lassen sich in R Beobachtungen filtern.

Um Gleichheit zu testen wird == verwendet und nicht der Zuweisungsoperator =:

filter(flights, month = 3)

Zusätzlich können Gleitkommazahlen bei Vergleichen zu Problemen führen (siehe hier).

2.4.1.2 Logische Operationen

Argumente von filter() werden mit einem “UND” verknüpft, d. h. alle Bedinungen müssen wahr sein, damit die Reihe betrachtet wird.

Um weitere Kombinationen zu betrachten, werden logische Operationen verwendet:

  • &: UND

  • |: ODER

  • !: NICHT

Die Bedeutung verschiedener logischer Operationen sehen Sie in der folgenden Grafik:

Wenn alle Abflüge im Januar oder Februar betrachtet werden sollen, so muss das logische Oder | verwendet werden:

filter(flights, month == 1 | month == 2)

Explizit muss hier nach beiden Monaten gefiltert werden und mit dem oder | verknüpft werden. Das entspricht nicht ganz dem umgangssprachlichen Gebrauch.

Eine alternative Verwendung, um nach mehreren Kategorien gleichzeitig zu filtern, ist die Verwendung von x %in% y.

filter(flights, month %in% c(1, 2))

Diese Bedingung entspricht der Umgangssprache: “Der Monat soll Januar (1) oder Februar (2) liegen.)

Weiter Informationen zu logischen Ausdrücken lässt sich der booleschen Algebra entnehmen.

2.4.1.3 Fehlende Werte NAs

Fehlende Werte werden in R durch NA markiert und sind eine Schwierigkeit bei logischen Operationen, denn ihr Ergebnis ist stets NA :

NA > 10
[1] NA
NA == 10
[1] NA
10 + NA
[1] NA
NA / 10
[1] NA
NA == NA
[1] NA

Um nach NA “Ausschau” zu halten, wird die is.na() Funktion verwendet:

x <- NA
is.na(x)
[1] TRUE

filter() entfernt Bedingungen, die FALSE oder NA sind automatisch, können aber explizit mit ausgewertet werden:

filter(flights, is.na(dep_time) & day > 3)

2.5 Reihen anordnen

Ähnlich wie filter() lassen sich mit arrange() Reihen verändern. Hierbei wird ein Dataframe nach einer oder mehrerer Spalten angeordnet (z. B. absteigende Sortierung).

Falls mehrere Spalten ausgewählt werden, wird auch nach allen Spalten sortiert und unterschieden:

arrange(flights, year, month, day)

Sie sehen, dass die Spalten nach dem Jahr, dem Monat und dem Tag (aufsteigend) sortiert werden. Angefangen wird hierbei mit den kleinsten Werten für den Monat und den Tag (hier: erster Januar).

Absteigend lässt sich ein Dataframe anhand von desc() anordnen:

arrange(flights, desc(dep_delay))

Wenn wir für mehrere Spalten gleichzeitig absteigend sortieren wollen, muss desc() für jede Spalte angewandt werden:

arrange(flights, desc(year), desc(month), desc(day))

Es kann entsprechend auch für jede Spalte unterschiedlich sortiert werden:

arrange(flights, desc(month), day)

In diesem Beispiel wird der Dataframe absteigend nach dem Monat und aufsteigend nach dem Tag angeordnet.

NA-Werte bilden hier wieder eine Ausnahme, denn sie werden an letzter Stelle angeordnet:

df <- tibble(x = c(5, 2, NA))
arrange(df, x)
arrange(df, desc(x))

3 Aufgaben

Um mit verschiedenen Daten umgehen zu können, müssen Sie auch verschiedene Daten/Datenstrukturen gesehen haben, daher betrachten wir immer unterschiedliche Datensätze.

In den folgenden Aufgaben wollen wir weiterhin den Datensatz flights betrachten, da dieser aufgrund seiner Größe etwas komplexer ist:

library(tidyverse)
flights

Machen Sie sich mit dem Datensatz und den einzelnen Variablen vertraut. Falls Sie Hilfe benötigen, verwenden Sie erneut ?flights.

3.1 Übungsaufgaben

  1. Wo liegt der Fehler?
irgendeine_variable <- 10
irgendeine_varıable
  1. Schreiben Sie die fehlenden Befehle um, sodass diese sinngemäß laufen:
lirbrary(tidyverse)

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

filter(mpg, cyl = 7)
filter(diamond, carat != 3)
  1. Filtern Sie die Flüge des flights Datensatzes anhand der folgenden Bedingungen:
    1. Flüge mit einer Ankunftsverspätung arr_delay von über 10 Stunden

    2. Flüge nach Seattle oder Kansas City (SEA und MCI)

    3. Flüge der Airlines United (UA), American (AA) oder Delta (DL)

    4. Flüge im Winter (Dezember, Januar, Februar)

    5. Flüge mit einer Verspätung arr_delay von mehr als 3 Stunden, wobei der Abflug nicht verspätet war (dep_delay)

    6. Flüge mit einer Abflugverspätung dep_delay von mehr als einer Stunde, die mehr als die Hälfte des Delays durch den Flug wettmachen konnten

    7. Flüge am späten Abend zwischen 22 Uhr und 24 Uhr

  2. Betrachten Sie die Funktion ?between() und ihre Funktionalität. Würde diese Ihnen bei den vorherigen Aufgaben helfen?
  3. Bei wie vielen Flügen fehlt die Ankunftszeit dep_time? Was fällt Ihnen auf und wie können Sie sich diese Einträge erklären?
  4. Sortieren Sie die Flüge:
    1. Finden Sie die Flüge mit der kleinsten und größten Abflugverspätung dep_delay

    2. Finden Sie den kürzesten und längsten Flug (bezogen auf die Distanz distance)

    3. Finden Sie den schnellsten Flug (bezogen auf die Geschwindigkeit)

LS0tCnRpdGxlOiAiU3RhdGlzdGlrIHVuZCBEYXRhIFNjaWVuY2UgLSBHcnVuZGxhZ2VuIGRlciBEYXRlbmFuYWx5c2UiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCiMgUiBHcnVuZGxhZ2VuCgojIyBWYXJpYWJsZW4genV3ZWlzZW4KCkdydW5kc8OkdHpsaWNoIGzDpHNzdCBzaWNoIGBSYCBhbHMgVGFzY2hlbnJlY2huZXIgdmVyd2VuZGVuIHVuZCBoYXQgaGllcnp1CnZlcnNjaGllZGVuZSBGdW5rdGlvbmVuIGFuenViaWV0ZW46CgpgYGB7cn0KMiAqIDMgKyAxMAooMzMgKyA1ICogMjIpIC8gNApzcXJ0KDQpICsgMQpjb3MocGkgLyA0KQpgYGAKCk9iamVrdGUgbGFzc2VuIHNpY2ggbWl0IGA9YCBvZGVyIGA8LWAgenV3ZWlzZW4uIFVtIE9iamVrdGUgYXVmIGRlcgpLb25zb2xlIGF1c3p1Z2ViZW4sIGvDtm5uZW4gU2llIGVpbmZhY2ggZGVuIE5hbWVuIHNjaHJlaWJlbiB1bmQKYXVzZsO8aHJlbjoKCmBgYHtyfQp4IDwtIDEyICogNAp5ID0gMyAqIDYKeAp5CmBgYAoKVW0gZGllIFZlcndlY2hzbHVuZyBtaXQgZGVyIFBhcmFtZXRlcnp1d2Vpc3VuZyBpbiBGdW5rdGlvbmVuIHp1CnZlcm1laWRlbiwgc29sbHRlbiBTaWUgaW1tZXIgYDwtYCB2ZXJ3ZW5kZW4sIHVtIE9iamVrdGUgenUgZXJzdGVsbGVuLgpNaXQgSGlsZmUgZGVzIFNob3J0Y3V0cyBgQWx0ICsgJy0nYCBrw7ZubmVuIFNpZSBlYmVuZmFsbHMgZGlyZWt0IGRlbgpPcGVyYXRvciBgPC1gIGVyaGFsdGVuLgoKYGBge3J9Cm1lYW4oeCA9IGMoMSwgMywgNSwgMTEsIDIpKSAjICc9JyBiZWkgRnVua3Rpb25lbgp6IDwtIDMgIyAnPC0nIGJlaSBadXdlaXN1bmdlbgp6CmBgYAoKRGllIGFsbGdlbWVpbmUgU3ludGF4IGbDvHIgWnV3ZWlzdW5nZW4gc2llaHQgZGFubiBzbyBhdXM6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpvYmpla3RfbmFtZSA8LSB3ZXJ0CmBgYAoKIyMgVmFyaWFibGVubmFtZW4KCkRpZSBOYW1lbiB2b24gVmFyaWFibGVuIG3DvHNzZW4gbWl0IGVpbmVtIEJ1Y2hzdGFiZW4gYmVnaW5uZW4gdW5kIGTDvHJmZW4KYXVzc2NobGllw59saWNoIEJ1Y2hzdGFiZW4sIFphaGxlbiwgYF9gIHVuZCBgLmAgZW50aGFsdGVuLiBOYW1lbiBzb2xsdGVuCmJlcmVpdHMgaWhyZSBCZWRldXR1bmcgYmVzY2hyZWliZW4uCgpWZXJzY2hpZWRlbmUgS29udmVudGlvbmVuIGbDvHIgVmFyaWFibGVubmFtZW4gc2VoZW4gc28gYXVzOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZGFzX2lzdF9zbmFrZV9jYXNlCm1hbmNoZUxldXRlTcO2Z2VuQ2FtZWxDYXNlCmFuZGVyZS5iZW51dHplbi5wdW5rdGUKZWluX2tsZWluZXJSZXN0LnZlcndlbmRldF9hbGxlcy56dXJTZWxiZW5aZWl0CmBgYAoKVmVyd2VuZGVuIFNpZSBiZXZvcnp1Z3QgYHNuYWtlX2Nhc2VgLgoKIyMgQXV0b21hdGlzY2hlIFZlcnZvbGxzdMOkbmRpZ3VuZwoKw4RobmxpY2ggd2llIGJlaSBkaXZlcnNlbiBNZXNzZW5nZXItb2RlciBLZXlib2FyZC1BcHBsaWthdGlvbmVuIGthbm4KUlN0dWRpbyBOYW1lbi9GdW5rdGlvbmVuIHZlcnZvbGxzdMOkbmRpZ2VuLgoKQW5nZW5vbW1lbiwgZXMgbGllZ3QgZWluIHNlaHIgbGFuZ2VyIE5hbWUgdm9yOgoKYGBge3J9CmRhc19pc3Rfd29obF9laW5fc2Vocl9sYW5nZXJfbmFtZSA8LSA1NS4zCmBgYAoKV2VubiBkZXIgQW5mYW5nIGVpbmVzIE5hbWVucyBpbiBSU3R1ZGlvIGVpbmdldGlwcHQgd2lyZCwgYmVpc3BpZWxzd2Vpc2UKYGRhc2AsIHVuZCBkYW5uIFRBQiBnZWRyw7xja3Qgd2lyZCwgd2VyZGVuIGRpZSBwYXNzZW5kZW4gTmFtZW4vRnVua3Rpb25lbgp2b3JnZXNjaGxhZ2VuIG9kZXIgZ2VnZWJlbmZhbGxzIGRpcmVrdCB2ZXJ2b2xsc3TDpG5kaWd0LgoKRWluIHdlaXRlcmVyIFNob3J0Y3V0IGlzdCDDvGJlciBgU3RyICsg4oaRYCB2ZXJmw7xnYmFyLiBXZW5uIHdpciBkZW4gQW5mYW5nCmVpbmVzIEJlZmVobHMgZWluZ2ViZW4gKHouIEIuIGBkYXNgKSB1bmQgYFN0ciArIOKGkWAgZHLDvGNrZW4sIGVyaGFsdGVuIHdpcgptw7ZnbGljaGUgVm9yc2NobMOkZ2UsIHdlbGNoZSB6dXZvciBhdXNnZWbDvGhydCB3b3JkZW4uIFNvIGvDtm5udGVuIHdpcgpkaXJla3QgZGVuIFdlcnQgdm9uIGBkYXNfaXN0X3dvaGxfZWluX3NlaHJfbGFuZ2VyX25hbWVgIGFucGFzc2VuLCB3ZW5uCmRhcyBub3R3ZW5kaWcgd8OkcmUuCgojIyBTeW50YXgvUmVjaHRzY2hyZWlidW5nIHrDpGhsdCEKCkluIGplZGVyIFByb2dyYW1taWVyc3ByYWNoZSwgc28gYXVjaCBpbiBSLCBpc3QgZGFzIGtvcnJla3RlLCBwcsOkemlzZQpTY2hyZWliZW4gdm9uIE5hbWVuIHVuZCBGdW5rdGlvbmVuIG5vdHdlbmRpZy4KClRpcHBmZWhsZXIgc293aWUgR3Jvw58tdW5kIEtsZWluc2NocmVpYnVuZyBzaW5kIHNlaHIgcmVsZXZhbnQsIGRlbm4gc29uc3QKd2lyZCBSIElocmVuIEJlZmVobCBuaWNodCB2ZXJzdGVoZW46CgpgYGB7cn0KYW56YWhsX3N0ZWluZSA8LSAyIF4gMyAqIHNxcnQoNCkKYGBgCgpGb2xnZW5kZSBCZWZlaGxlIGdlYmVuIEZlaGxlciBhdXMuIFdhcnVtPwoKYGBge3J9CmFuemFobF9zdGVpbgpgYGAKCmBgYHtyfQpBbnphaGxfc3RlaW5lCmBgYAoKQWxzIEhpbGZlc3RlbGx1bmcgZGllbnQgaGllciBkaWUgQXV0b3ZlcnZvbGxzdMOkbmRpZ3VuZy4KCiMjIEZ1bmt0aW9uZW4gYXVmcnVmZW4KCkluIFIgc3RlaGVuIGJlcmVpdHMgZWluZSBWaWVsemFobCBhbiBGdW5rdGlvbmVuIHN0YW5kYXJkbcOkw59pZyB6dXIKVmVyZsO8Z3VuZywgd2VsY2hlIHdpZSBmb2xndCBhdWZnZXJ1ZmVuIHdlcmRlbjoKCmBgYHtyLCBldmFsID0gRkFMU0V9CmZ1bmt0aW9uc19uYW1lKGFyZ3VtZW50MSA9IHdlcnQxLCBhcmd1bWVudDIgPSB3ZXJ0MiwgLi4uKQpgYGAKCkF1Y2ggYmVpIEZ1bmt0aW9uZW4gaXN0IGRpZSBhdXRvbWF0aXNjaGUgVmVydm9sbHN0w6RuZGlndW5nIHNlaHIKaGlsZnJlaWNoLiBXZW5uIFNpZSBiZWlzcGllbHN3ZWlzZSBgc2VgIGVpbmdlYmVuIHVuZCBUQUIgZHLDvGNrZW4sCmVyaGFsdGVuIFNpZSBkaXJla3QgVm9yc2NobMOkZ2UgZsO8ciBhbGxlIG3DtmdsaWNoZW4gT3B0aW9uZW4gKEZ1bmt0aW9uZW4sCk9iamVrdGUsIHVzdy4pLCB3ZWxjaGUgU2llIG1pdCBkZW4gUGZlaWx0YXN0ZW4gb2RlciBkZXIgTWF1cyBhdXN3w6RobGVuCmvDtm5uZW4uIFdlbm4gd2lyIGhpZXJiZWkgYHNlcSgpYCBhdXN3w6RobGVuLCBrw7ZubmVuIHdpciBlYmVuZmFsbHMKSGlsZmVzdGVsbHVuZ2VuIHp1IGRlciBGdW5rdGlvbiBlaW5zZWhlbi4gRWJlbmZhbGxzIHdlcmRlbiBkaWUKbm90d2VuZGlnZW4gS2xhbW1lcm4gYChgIHVuZCBgKWAgYmVyZWl0cyBuYWNoIGRlciBGdW5rdGlvbiBnZXNldHp0OgoKYGBge3J9CnNlcSgxLCAxMCkKYGBgCgpFYmVuZmFsbHMgd2VyZGVuIFplaWNoZW5rZXR0ZW4sIGRpZSB6d2lzY2hlbiB6d2VpIEFuZsO8aHJ1bmdzemVpY2hlbiBgImAKc3RlaGVuIG3DvHNzZW4sIGF1dG9tYXRpc2NoIHZvbiBSU3R1ZGlvIGFscyBQYWFyIGVyZ8Okbnp0OgoKYGBge3J9CmhhbGxvX3dlbHQgPC0gIkhhbGxvIFdlbHQhIgpgYGAKCkFuZsO8aHJ1bmdzemVpY2hlbiB1bmQgS2xhbW1lcm4gbcO8c3NlbiBpbW1lciBpbiBlaW5lbSBQYWFyIGF1ZnRyZXRlbgooRnVua3Rpb25lbiwgWmVpY2hlbmtldHRlbiwgdXN3LikuIEJlaSBlaW5lbSBmZWhsZW5kZW4gU3ltYm9sIHdpcmQKUlN0dWRpbyBpbiBkZXIgS29uc29sZSBlaW4gYCtgIGVyZ8OkbnplbiwgZC4gaC4gUlN0dWRpbyB3YXJ0ZXQgYXVmIGVpbmUKZ2VzY2hsb3NzZW5lIEtsYW1tZXIgb2RlciBmZWhsZW5kZSBBbmbDvGhydW5nc3plaWNoZW46CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQo+CWhhbGxvX3dlbHQgPC0gIkhhbGxvIFdlbHQhCisKYGBgCgpGYWxscyBTaWUgZWluIGArYCBpbiBkZXIgS29uc29sZSBzZWhlbiwgZHLDvGNrZW4gU2llIGBFU0NBUEVgIG9kZXIKdmVydm9sbHN0w6RuZGlnZW4gU2llIGRlbiBBdXNkcnVjayB1bmQgZHLDvGNrZW4gU2llIGBFTlRFUmAuCgpGYWxscyBTaWUgZGVuIFdlcnQgZWluZXIgWnV3ZWlzdW5nIGRpcmVrdCBlaW5zZWhlbiB3b2xsZW4sIGvDtm5uZW4gU2llCmVpbmUgdm9uIHp3ZWkgT3B0aW9uZW4gYXVzd8OkaGxlbjoKCmBgYHtyfQpzZXF1ZW56IDwtIHNlcSgxLCAxMCwgbGVuZ3RoLm91dCA9IDUpCnNlcXVlbnoKKHNlcXVlbnogPC0gc2VxKDEsIDEwLCBsZW5ndGgub3V0ID0gNSkpCmBgYAoKV2VubiBTaWUgZWluZSBLbGFtbWVyIHVtIGVpbmVuIEF1c2RydWNrIGxlZ2VuLCB3aXJkIGRlciBXZXJ0IGViZW5mYWxscwp6dXLDvGNrZ2VnZWJlbi4KCiMgRGF0ZW50cmFuc2Zvcm1hdGlvbgoKIyMgRGVyIGBmbGlnaHRzYCBEYXRlbnNhdHoKCkluIGRpZXNlbSBLYXBpdGVsIHdpcmQgZGVyIERhdGVuc2F0eiBgZmxpZ2h0c2AgYmV0cmFjaHRldCwgd2VsY2hlciBhbGxlCkFiZmzDvGdlIGF1cyBOZXcgWW9yayBDaXR5IGRlcyBKYWhyZXMgMjAxMyB1bWZhc3N0LgoKRGVyIERhdGVuc2F0eiBpc3QgaW4gZGVyIEJpYmxpb3RoZWsgYG55Y2ZsaWdodHMxM2AgZW50aGFsdGVuIHVuZCB3ZWl0ZXJlCkluZm9ybWF0aW9uIGzDpHNzdCBzaWNoIG1pdCBgP2ZsaWdodHNgIGVyaGFsdGVuLgoKWnVlcnN0IG11c3MgYG55Y2ZsaWdodHMxM2AgaW5zdGFsbGllcnQgd2VyZGVuOgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQppZighIm55Y2ZsaWdodHMxMyIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkKCWluc3RhbGwucGFja2FnZXMoIm55Y2ZsaWdodHMxMyIpCmxpYnJhcnkobnljZmxpZ2h0czEzKQpgYGAKCkRhbm4gbMOkc3N0IHNpY2ggZGVyIERhdGVuc2F0eiBiZXRyYWNodGVuOgoKYGBge3J9CmZsaWdodHMKYGBgCgpIaWVyIHNlaGVuIHdpciBlcm5ldXQsIGRhc3MgZXMgc2ljaCBiZWkgYGZsaWdodHNgIHVtIGVpbiBPYmpla3QgbmFtZXMKYHRpYmJsZWAgaGFuZGVsdDoKCmBgYHtyfQppcyhmbGlnaHRzKQpgYGAKCiMjIFdhcyBzaW5kIGB0aWJibGVgPwoKYHRpYmJsZWAgKGludGVybiBgdGJsX2RmYCkgZ2VsdGVuIGFscyBFcndlaXRlcnVuZyB2b24gRGF0YWZyYW1lcwooYGRhdGEuZnJhbWVgKSB1bmQgZXJsZWljaHRlcm4gZGllIEVpbnNpY2h0IHVuZCBBcmJlaXQgbWl0IERhdGFmcmFtZXMKZHVyY2ggbWVociBFaW5oZWl0bGljaGtlaXQuIE1pdCB3ZW5pZ2VuIEF1c25haG1lbiBsYXNzZW4gc2ljaCBgdGliYmxlYApnZW5hdXNvIHdpZSBEYXRhZnJhbWVzIHZlcndlbmRlbi4KCkRlciBgdGliYmxlYCB6ZWlndCBiZWkgZGVyIEF1c2dhYmUgYmVyZWl0cyBhbGxlIHdpY2h0aWdlbiBFaWdlbnNjaGFmdGVuCmRlciBUYWJlbGxlOgoKLSAgIERpZSBEaW1lbnNpb24gKEFuemFobCBTcGFsdGVuIHVuZCBSZWloZW4pIHN0ZWh0IGFuIG9iZXJzdGVyIFN0ZWxsZQoKLSAgIChBYmdla8O8cnp0ZSkgU3BhbHRlbm5hbWVuIHVuZCBkZXJlbiBEYXRlbnR5cAoKLSAgIEVpbmUgYmVncmVuenRlIEFuc2ljaHQgYXVmIGRpZSBlcnN0ZW4gRWludHLDpGdlCgpBbmFsb2cgenUgRGF0YWZyYW1lcyBsYXNzZW4gc2ljaCBiZWkgYHRpYmJsZXNgIGRpZSBrb21wbGV0dGVuIFRhYmVsbGVuCm1pdCBgVmlldygpYCAoei4gQi4gYFZpZXcoZmxpZ2h0cylgKSBhbnplaWdlbi4KCkRpZSBEYXRlbnR5cGVuIHVtZmFzc2VuOgoKLSAgIGBpbnRgIHN0ZWh0IGbDvHIgR2FuenphaGxlbi4KCi0gICBgZGJsYCBzdGVodCBmw7xyIEdsZWl0a29tbWF6YWhsZW4gYnp3LiByZWVsbGUgWmFobGVuLgoKLSAgIGBjaHJgIHN0ZWh0IGbDvHIgYGNoYXJhY3RlcmAgVmVrdG9yZW4gb2RlciBaZWljaHRlbmtldHRlbiAoc3RyaW5ncykuCgotICAgYGR0dG1gIHN0ZWh0IGbDvHIgRGF0dW0tWmVpdCAoRGF0dW0gKyBVaHJ6ZWl0KS4KCi0gICBgbGdsYCBzdGVodCBmw7xyIGxvZ2lzY2gsIGQuIGguIFZla3RvcmVuIGF1cyBgVFJVRWAgb2RlciBgRkFMU0VgLgoKLSAgIGBmY3RyYCBzdGVodCBmw7xyIEZha3RvcmVuIGBmYWN0b3JzYCwgd2FzIGVpbmUgUi1pbnRlcm5lCiAgICBSZXByw6RzZW50YXRpb24gZsO8ciBLYXRlZ29yaWVuIGlzdC4KCi0gICBgZGF0ZWAgc3RlaHQgZsO8ciBEYXR1bS4KCiMjIyBFaW5lbiBgdGliYmxlYCBlcnN0ZWxsZW4KCkluIGRlbiBtZWlzdGVuIEbDpGxsZW4gbGllZ2VuIERhdGVuIGFscyBEYXRhZnJhbWVzIHZvci4gRGllc2UgbGFzc2VuIHNpY2gKbWl0IGBhc190aWJibGUoKWAgZGlyZWt0IGluIGVpbmVuIGB0aWJibGVgIHVtd2FuZGVsbjoKCmBgYHtyfQphc190aWJibGUoaXJpcykKYGBgCgpEaWVzZXMgQmVpc3BpZWwgaGF0dGVuIHdpciBiZXJlaXRzIGFtIEFuZmFuZyBkZXMgS3Vyc2VzIGdlc2VoZW4uCgpBbmFsb2cgenUgRGF0YWZyYW1lcyBsYXNzZW4gc2ljaCBgdGliYmxlYCBtaXQgZGVyIGB0aWJibGUoKWAgRnVua3Rpb24KZXJzdGVsbGVuLCB3b2JlaSBkaWUgU3BhbHRlbiBhbHMgVmVrdG9yZW4gYW5nZWdlYmVuIHdlcmRlbiB1bmQgU3BhbHRlbgptaXQgZWluZXIgTMOkbmdlIHZvbiAxIGF1dG9tYXRpc2NoIHdpZWRlcmhvbHQgd2VyZGVuOgoKYGBge3J9CnRpYmJsZSgKCWJsdW1lbiA9IGMoIklyaXMiLCAiTWFyZ2VyaXRlIiwgIk5lbGtlIiwgIk9yY2hpZGVlIiksCglmYXJiZSA9IGMoImxpbGEiLCAid2Vpw58iLCAicHVycHVyIiwgImdlbGIiKSwKCWFuemFobCA9IDMsCglrZWxjaGJsYXR0X2xhZW5nZSA9IGMoNS4yLCA2LjEsIDMuMiwgMi4yKSwKCWtlbGNoYmxhdHRfYnJlaXRlID0gYygzLjUsIDQuOCwgNCwgMi4xKSwKCWtlbGNoYmxhdHRfZmxhZWNoZSA9IGtlbGNoYmxhdHRfbGFlbmdlICoga2VsY2hibGF0dF9icmVpdGUgLSAxCikKYGBgCgpadXPDpHR6bGljaCBsYXNzZW4gc2ljaCBiZWkgYHRpYmJsZWAgYXVjaCB1bsO8YmxpY2hlIFZhcmlhYmxlbm5hbWVuCnZlcndlbmRlbiwgZGllIG1pdCBaYWhsZW4gb2RlciBMZWVyemVpY2hlbiBhbmZhbmdlbjoKCmBgYHtyfQp0YiA8LSB0aWJibGUoCglgPSlgID0gImxvbmdzbWlsZSIsCglgIGAgPSAiZW1wdHlzcGFjZSIsCglgOWAgPSAibnVtYmVyIgopCnRiCnRiJGA5YApgYGAKCkRpZXNlIHVudHlwaXNjaGVuIE5hbWVuIG3DvHNzZW4gamVkb2NoIG1pdCBgYGAgYGAgYGBgIHVta2xhbW1lcnQgd2VyZGVuLgoKRXMgZ2lidCBub2NoIHdlaXRlcmUgTcO2Z2xpY2hrZWl0ZW4sIGB0aWJibGVgIHp1IGVyc3RlbGxlbiwgei4gQi4gZHVyY2gKW2B0cmliYmxlKClgXShodHRwczovL3RpYmJsZS50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS90cmliYmxlLmh0bWwpLgoKIyMjIFZlcmdsZWljaDogYHRpYmJsZWAgdnMgYGRhdGEuZnJhbWVgCgojIyMjIEF1c2dhYmUKCmB0aWJibGVgIHplaWdlbiB2ZXJlaW5mYWNodCBkaWUgZXJzdGVuIDEwIEVpbnRyw6RnZSB1bmQgZGllIERhdGVudHlwZW4KYW4uIE1pdCBIaWxmZSB2b24gYHByaW50KClgIGzDpHNzdCBzaWNoIGhpZXJiZWkgZWJlbmZhbGxzIGRpZSBBdXNnYWJlCnZlcsOkbmRlcm46CgpgYGB7cn0KcHJpbnQoZmxpZ2h0cywgbiA9IDE1LCB3aWR0aCA9IElORikKYGBgCgpEaWUgQXVzZ2FiZW9wdGlvbmVuIGxhc3NlbiBzaWNoIGViZW5mYWxscyBuYWNoIGJlbGllYmVuIGFucGFzc2VuIChzaWVoZQpbaGllcl0oaHR0cHM6Ly90aWJibGUudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvdGliYmxlX29wdGlvbnMuaHRtbCkgdW5kCltoaWVyXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvdGliYmxlL3ZlcnNpb25zLzEuNC4yL3RvcGljcy90aWJibGUtb3B0aW9ucykpLgoKRGF0YWZyYW1lcyBoaW5nZWdlbiBzaW5kIGhpZXIgcmVsYXRpdiB1bsO8YmVyc2ljaHRsaWNoLgoKIyMjIyBTcGFsdGVuIGF1c3fDpGhsZW4KCkJlaSBEYXRhZnJhbWVzIHdpcmQgZGVyIGBbXWAgT3BlcmF0b3Igb2RlciBgJGAgdmVyd2VuZGV0LCB1bSBkaWUKVmVrdG9yZW4gZGVyIFNwYWx0ZW4genUgZXJoYWx0ZW4KCkJlaSBgdGliYmxlYCB2ZXJ3ZW5kZW4gd2lyIGhpbmdlZ2VuIGBbW11dYCBzb3dpZSBgJGAsIHVtIFNwYWx0ZW4gZHVyY2gKaWhyZSBQb3NpdGlvbiBiencuIGlocmVuIE5hbWVuIHp1IGVyaGFsdGVuOgoKYGBge3J9CmRmIDwtIHRpYmJsZSh2YXIxID0gMTo1LCB2YXIyID0gNjoxMCkKCiMgdGliYmxlIFNwYWx0ZW4gZHVyY2ggTmFtZSBhdXN3w6RobGVuCmRmJHZhcjEKZGZbWyJ2YXIxIl1dCiMgU3BhbHRlIGR1cmNoIFBvc2l0aW9uIGF1c3fDpGhsZW4KZGZbWzFdXQpgYGAKClp1c8OkdHpsaWNoIGdpYnQgYFtdYCBiZWkgYHRpYmJsZWAgZWluaGVpdGxpY2ggZWluZW4gYHRpYmJsZWAgd2llZGVyOgoKYGBge3J9CmRmWzFdCmBgYAoKRGEgZGllIEhhbmRoYWJ1bmcgZGllc2VyIE9wZXJhdG9yZW4gc2VociBtw7xoc2VsaWcgaXN0IHVuZCBhYiBlaW5lcgpiZXN0aW1tdGVuIEtvbXBsZXhpdMOkdCBzZWhyIHVuw7xiZXJzaWNodGxpY2gsIHdlcmRlbiBpbiBgZHBseXJgCkZ1bmt0aW9uZW4gYW4gZGVyZW4gU3RlbGxlIHZlcndlbmRldCAoYGZpbHRlcigpYCBvZGVyIGBzZWxlY3QoKWApLiBEdXJjaApkaWUgVmVyd2VuZHVuZyB2b24gYGR5cGxyYCB3aXJkIGRlciBDb2RlIMO8YmVyc2ljaHRsaWNoZXIgdW5kIGR1cmNoIGRpZQpGdW5rdGlvbnNuYW1lbiAic3ByaWNodCBkZXIgQ29kZSIuCgojIyBgZHBseXJgCgojIyMgR3J1bmRsZWdlbmRlcwoKRGllIEJpYmxpb3RoZWsgYGRwbHlyYCB3ZWlzdCBlaW5lIFZpZWx6YWhsIGFuIEZ1bmt0aW9uZW4gYXVmLCB1bSBEYXRlbgp6dSBtYW5pcHVsaWVyZW4uIEVpbmlnZSBkYXZvbiBzaW5kIHNlaHIgc3BlemlmaXNjaCB1bmQgYW5kZXJlIHdlcmRlbiBzbwpndXQgd2llIGluIGplZGVyIEFuYWx5c2UgdmVyd2VuZGV0LgoKRGllIHdpY2h0aWdzdGVuIEZ1bmt0aW9uZW4gc2luZDoKCi0gICBgZmlsdGVyKClgOiBCZW9iYWNodHVuZ2VuIChSZWloZW4pIGFuaGFuZCBpaHJlciBXZXJ0ZSBmaWx0ZXJuLgoKLSAgIGBhcnJhbmdlKClgOiBCZW9iYWNodHVuZ2VuIGFub3JkbmVuIChzb3J0aWVyZW4pLgoKLSAgIGBzZWxlY3QoKWA6IFZhcmlhYmxlbiAoU3BhbHRlbikgYW5oYW5kIGlocmVzIE5hbWVucyBlbnRuZWhtZW4uCgotICAgYG11dGF0ZSgpYDogTmV1ZSBWYXJpYWJsZW4gKGFuaGFuZCB2b24gRnVua3Rpb25lbikgZXJzdGVsbGVuLgoKLSAgIGBzdW1tYXJpc2UoKWA6IFdlcnRlIGF1ZiBlaW5lIFp1c2FtbWVuZmFzc3VuZyByZWR1emllcmVuICh6LiBCLgogICAgZHVyY2ggaWhyZW4gTWl0dGVsd2VydCkuCgpWb3JnZWhlbnN3ZWlzZToKCjEuICBEYXMgZXJzdGUgQXJndW1lbnQgaXN0IGVpbiBEYXRhZnJhbWUgKG9kZXIgYHRpYmJsZWApCgoyLiAgV2VpdGVyZSBBcmd1bWVudGUgYmVzY2hyZWliZW4sIHdpZSBkaWUgRGF0ZW4gbWFuaXB1bGllcnQgd2VyZGVuCiAgICBhbmhhbmQgZGVyIFZhcmlhYmxlbm5hbWVuIChvaG5lIEFuZsO8aHJ1bmdzemVpY2hlbikKCjMuICBBbHMgRXJnZWJuaXMgd2lyZCBlaW4gRGF0YWZyYW1lIGdlbGllZmVydCAob2RlciBgdGliYmxlYCkKCiMjIFJlaWhlbiBmaWx0ZXJuCgpNaXQgZGVyIEZ1bmt0aW9uIGBmaWx0ZXIoKWAgbGFzc2VuIHNpY2ggQmVvYmFjaHR1bmdlbiBhbmhhbmQgaWhyZXIgV2VydGUKZmlsdGVybi4gRGFzIGVyc3RlIEFyZ3VtZW50IGlzdCBzdGV0cyBkZXIgRGF0YWZyYW1lLCBnZWZvbGd0IHZvbgpCZWRpbnVuZ2VuLCBtaXQgZGVuZW4gZ2VmaWx0ZXJ0IHdlcmRlbiBzb2xsOgoKYGBge3J9CmZpbHRlcihmbGlnaHRzLCBtb250aCA9PSAxICwgZGF5ID09IDEpCmBgYAoKSGllcmJlaSB3aXJkIGVpbiBuZXVlciBEYXRhZnJhbWUgd2llZGVyZ2VnZWJlbi4gU2llIHNlaGVuLCBkYXNzIGRlcgplcnN0ZSBUYWcgZGVzIGVyc3RlbiBNb25hdHMgZW50bm9tbWVuIHdpcmQgKGVyc3RlciBKYW51YXIpLgoKVW0gZGVuIG5ldWVuIERhdGFmcmFtZSB6dSBzcGVpY2hlcm4sIG11c3MgZXJuZXV0IGVpbmUgVmFyaWFibGUgYW5nZWxlZ3QKKG9kZXIgZGllIGFsdGUgw7xiZXJzY2hyaWViZW4pIHdlcmRlbjoKCmBgYHtyfQp6ZWhudGVyX2ZlYnJ1YXIgPC0gZmlsdGVyKGZsaWdodHMsIG1vbnRoID09IDIsIGRheSA9PSAxMCkKemVobnRlcl9mZWJydWFyCmBgYAoKIyMjIExvZ2lzY2hlIEJlZGluZ3VuZ2VuCgojIyMjIFZlcmdsZWljaGUKCk1pdCBIaWxmZSBkZXIgT3BlcmF0b3JlbiBbYD5gXShodHRwczovL3JkcnIuaW8vci9iYXNlL0NvbXBhcmlzb24uaHRtbCksCltgPj1gXShodHRwczovL3JkcnIuaW8vci9iYXNlL0NvbXBhcmlzb24uaHRtbCksCltgPGBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvQ29tcGFyaXNvbi5odG1sKSwKW2A8PWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvQ29tcGFyaXNvbi5odG1sKSwKW2AhPWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvQ29tcGFyaXNvbi5odG1sKSAodW5nbGVpY2gpLCB1bmQKW2A9PWBdKGh0dHBzOi8vcmRyci5pby9yL2Jhc2UvQ29tcGFyaXNvbi5odG1sKSAoZ2xlaWNoKSBsYXNzZW4gc2ljaCBpbiBSCkJlb2JhY2h0dW5nZW4gZmlsdGVybi4KClVtIEdsZWljaGhlaXQgenUgdGVzdGVuIHdpcmQgYD09YCB2ZXJ3ZW5kZXQgdW5kIG5pY2h0IGRlcgpadXdlaXN1bmdzb3BlcmF0b3IgYD1gOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZmlsdGVyKGZsaWdodHMsIG1vbnRoID0gMykKYGBgCgpadXPDpHR6bGljaCBrw7ZubmVuIEdsZWl0a29tbWF6YWhsZW4gYmVpIFZlcmdsZWljaGVuIHp1IFByb2JsZW1lbiBmw7xocmVuCihzaWVoZSBbaGllcl0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9uZWFyLmh0bWwpKS4KCiMjIyMgTG9naXNjaGUgT3BlcmF0aW9uZW4KCkFyZ3VtZW50ZSB2b24gYGZpbHRlcigpYCB3ZXJkZW4gbWl0IGVpbmVtICJVTkQiIHZlcmtuw7xwZnQsIGQuIGguIGFsbGUKQmVkaW51bmdlbiBtw7xzc2VuIHdhaHIgc2VpbiwgZGFtaXQgZGllIFJlaWhlIGJldHJhY2h0ZXQgd2lyZC4KClVtIHdlaXRlcmUgS29tYmluYXRpb25lbiB6dSBiZXRyYWNodGVuLCB3ZXJkZW4gbG9naXNjaGUgT3BlcmF0aW9uZW4KdmVyd2VuZGV0OgoKLSAgIGAmYDogVU5ECgotICAgYHxgOiBPREVSCgotICAgYCFgOiBOSUNIVAoKRGllIEJlZGV1dHVuZyB2ZXJzY2hpZWRlbmVyIGxvZ2lzY2hlciBPcGVyYXRpb25lbiBzZWhlbiBTaWUgaW4gZGVyCmZvbGdlbmRlbiBHcmFmaWs6CgohW10oaHR0cHM6Ly9kMzN3dWJyZmtpMGw2OC5jbG91ZGZyb250Lm5ldC8wMWY0YjZkMzlkMmJlODI2OTc0MGEzYWQ3OTQ2ZmFhNzlmNzI0M2NmLzgzNjlhL2RpYWdyYW1zL3RyYW5zZm9ybS1sb2dpY2FsLnBuZyl7d2lkdGg9IjgwMCJ9CgpXZW5uIGFsbGUgQWJmbMO8Z2UgaW0gSmFudWFyIG9kZXIgRmVicnVhciBiZXRyYWNodGV0IHdlcmRlbiBzb2xsZW4sIHNvCm11c3MgZGFzIGxvZ2lzY2hlIE9kZXIgYHxgIHZlcndlbmRldCB3ZXJkZW46CgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIG1vbnRoID09IDEgfCBtb250aCA9PSAyKQpgYGAKCkV4cGxpeml0IG11c3MgaGllciBuYWNoIGJlaWRlbiBNb25hdGVuIGdlZmlsdGVydCB3ZXJkZW4gdW5kIG1pdCBkZW0gb2RlcgpgfGAgdmVya27DvHBmdCB3ZXJkZW4uIERhcyBlbnRzcHJpY2h0IG5pY2h0IGdhbnogZGVtIHVtZ2FuZ3NzcHJhY2hsaWNoZW4KR2VicmF1Y2guCgpFaW5lIGFsdGVybmF0aXZlIFZlcndlbmR1bmcsIHVtIG5hY2ggbWVocmVyZW4gS2F0ZWdvcmllbiBnbGVpY2h6ZWl0aWcgenUKZmlsdGVybiwgaXN0IGRpZSBWZXJ3ZW5kdW5nIHZvbiBgeCAlaW4lIHlgLgoKYGBge3J9CmZpbHRlcihmbGlnaHRzLCBtb250aCAlaW4lIGMoMSwgMikpCmBgYAoKRGllc2UgQmVkaW5ndW5nIGVudHNwcmljaHQgZGVyIFVtZ2FuZ3NzcHJhY2hlOiAiRGVyIE1vbmF0IHNvbGwgSmFudWFyCigxKSBvZGVyIEZlYnJ1YXIgKDIpIGxpZWdlbi4pCgpXZWl0ZXIgSW5mb3JtYXRpb25lbiB6dSBsb2dpc2NoZW4gQXVzZHLDvGNrZW4gbMOkc3N0IHNpY2ggZGVyIFtib29sZXNjaGVuCkFsZ2VicmFdKGh0dHBzOi8vZGUud2lraXBlZGlhLm9yZy93aWtpL0Jvb2xlc2NoZV9BbGdlYnJhKSBlbnRuZWhtZW4uCgojIyMjIEZlaGxlbmRlIFdlcnRlIGBOQXNgCgpGZWhsZW5kZSBXZXJ0ZSB3ZXJkZW4gaW4gUiBkdXJjaCBgTkFgIG1hcmtpZXJ0IHVuZCBzaW5kIGVpbmUKU2Nod2llcmlna2VpdCBiZWkgbG9naXNjaGVuIE9wZXJhdGlvbmVuLCBkZW5uIGlociBFcmdlYm5pcyBpc3Qgc3RldHMKYE5BYCA6CgpgYGB7cn0KTkEgPiAxMApOQSA9PSAxMAoxMCArIE5BCk5BIC8gMTAKTkEgPT0gTkEKYGBgCgpVbSBuYWNoIGBOQWAgIkF1c3NjaGF1IiB6dSBoYWx0ZW4sIHdpcmQgZGllIGBpcy5uYSgpYCBGdW5rdGlvbgp2ZXJ3ZW5kZXQ6CgpgYGB7cn0KeCA8LSBOQQppcy5uYSh4KQpgYGAKCmBmaWx0ZXIoKWAgZW50ZmVybnQgQmVkaW5ndW5nZW4sIGRpZSBgRkFMU0VgIG9kZXIgYE5BYCBzaW5kIGF1dG9tYXRpc2NoLAprw7ZubmVuIGFiZXIgZXhwbGl6aXQgbWl0IGF1c2dld2VydGV0IHdlcmRlbjoKCmBgYHtyfQpmaWx0ZXIoZmxpZ2h0cywgaXMubmEoZGVwX3RpbWUpICYgZGF5ID4gMykKYGBgCgojIyBSZWloZW4gYW5vcmRuZW4KCsOEaG5saWNoIHdpZSBgZmlsdGVyKClgIGxhc3NlbiBzaWNoIG1pdCBgYXJyYW5nZSgpYCBSZWloZW4gdmVyw6RuZGVybi4KSGllcmJlaSB3aXJkIGVpbiBEYXRhZnJhbWUgbmFjaCBlaW5lciBvZGVyIG1laHJlcmVyIFNwYWx0ZW4gYW5nZW9yZG5ldAooei4gQi4gYWJzdGVpZ2VuZGUgU29ydGllcnVuZykuCgpGYWxscyBtZWhyZXJlIFNwYWx0ZW4gYXVzZ2V3w6RobHQgd2VyZGVuLCB3aXJkIGF1Y2ggbmFjaCBhbGxlbiBTcGFsdGVuCnNvcnRpZXJ0IHVuZCB1bnRlcnNjaGllZGVuOgoKYGBge3J9CmFycmFuZ2UoZmxpZ2h0cywgeWVhciwgbW9udGgsIGRheSkKYGBgCgpTaWUgc2VoZW4sIGRhc3MgZGllIFNwYWx0ZW4gbmFjaCBkZW0gSmFociwgZGVtIE1vbmF0IHVuZCBkZW0gVGFnCihhdWZzdGVpZ2VuZCkgc29ydGllcnQgd2VyZGVuLiBBbmdlZmFuZ2VuIHdpcmQgaGllcmJlaSBtaXQgZGVuIGtsZWluc3RlbgpXZXJ0ZW4gZsO8ciBkZW4gTW9uYXQgdW5kIGRlbiBUYWcgKGhpZXI6IGVyc3RlciBKYW51YXIpLgoKQWJzdGVpZ2VuZCBsw6Rzc3Qgc2ljaCBlaW4gRGF0YWZyYW1lIGFuaGFuZCB2b24gYGRlc2MoKWAgYW5vcmRuZW46CgpgYGB7cn0KYXJyYW5nZShmbGlnaHRzLCBkZXNjKGRlcF9kZWxheSkpCmBgYAoKV2VubiB3aXIgZsO8ciBtZWhyZXJlIFNwYWx0ZW4gZ2xlaWNoemVpdGlnIGFic3RlaWdlbmQgc29ydGllcmVuIHdvbGxlbiwKbXVzcyBgZGVzYygpYCBmw7xyIGplZGUgU3BhbHRlIGFuZ2V3YW5kdCB3ZXJkZW46CgpgYGB7cn0KYXJyYW5nZShmbGlnaHRzLCBkZXNjKHllYXIpLCBkZXNjKG1vbnRoKSwgZGVzYyhkYXkpKQpgYGAKCkVzIGthbm4gZW50c3ByZWNoZW5kIGF1Y2ggZsO8ciBqZWRlIFNwYWx0ZSB1bnRlcnNjaGllZGxpY2ggc29ydGllcnQKd2VyZGVuOgoKYGBge3J9CmFycmFuZ2UoZmxpZ2h0cywgZGVzYyhtb250aCksIGRheSkKYGBgCgpJbiBkaWVzZW0gQmVpc3BpZWwgd2lyZCBkZXIgRGF0YWZyYW1lIGFic3RlaWdlbmQgbmFjaCBkZW0gTW9uYXQgdW5kCmF1ZnN0ZWlnZW5kIG5hY2ggZGVtIFRhZyBhbmdlb3JkbmV0LgoKYE5BYC1XZXJ0ZSBiaWxkZW4gaGllciB3aWVkZXIgZWluZSBBdXNuYWhtZSwgZGVubiBzaWUgd2VyZGVuIGFuIGxldHp0ZXIKU3RlbGxlIGFuZ2VvcmRuZXQ6CgpgYGB7cn0KZGYgPC0gdGliYmxlKHggPSBjKDUsIDIsIE5BKSkKYXJyYW5nZShkZiwgeCkKYXJyYW5nZShkZiwgZGVzYyh4KSkKYGBgCgojIEF1ZmdhYmVuCgpVbSBtaXQgdmVyc2NoaWVkZW5lbiBEYXRlbiB1bWdlaGVuIHp1IGvDtm5uZW4sIG3DvHNzZW4gU2llIGF1Y2gKdmVyc2NoaWVkZW5lIERhdGVuL0RhdGVuc3RydWt0dXJlbiBnZXNlaGVuIGhhYmVuLCBkYWhlciBiZXRyYWNodGVuIHdpcgppbW1lciB1bnRlcnNjaGllZGxpY2hlIERhdGVuc8OkdHplLgoKSW4gZGVuIGZvbGdlbmRlbiBBdWZnYWJlbiB3b2xsZW4gd2lyIHdlaXRlcmhpbiBkZW4gRGF0ZW5zYXR6IGBmbGlnaHRzYApiZXRyYWNodGVuLCBkYSBkaWVzZXIgYXVmZ3J1bmQgc2VpbmVyIEdyw7bDn2UgZXR3YXMga29tcGxleGVyIGlzdDoKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKZmxpZ2h0cwpgYGAKCk1hY2hlbiBTaWUgc2ljaCBtaXQgZGVtIERhdGVuc2F0eiB1bmQgZGVuIGVpbnplbG5lbiBWYXJpYWJsZW4gdmVydHJhdXQuCkZhbGxzIFNpZSBIaWxmZSBiZW7DtnRpZ2VuLCB2ZXJ3ZW5kZW4gU2llIGVybmV1dCBgP2ZsaWdodHNgLgoKIyMgw5xidW5nc2F1ZmdhYmVuCgoxLiAgV28gbGllZ3QgZGVyIEZlaGxlcj8KCmBgYHtyLCBldmFsID0gRkFMU0V9CmlyZ2VuZGVpbmVfdmFyaWFibGUgPC0gMTAKaXJnZW5kZWluZV92YXLEsWFibGUKYGBgCgoyLiAgU2NocmVpYmVuIFNpZSBkaWUgZmVobGVuZGVuIEJlZmVobGUgdW0sIHNvZGFzcyBkaWVzZSBzaW5uZ2Vtw6TDnwogICAgbGF1ZmVuOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlyYnJhcnkodGlkeXZlcnNlKQoKZ2dwbG90KGRvdGEgPSBpcmlzKSArCglnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoKSkKCmZpbHRlcihtcGcsIGN5bCA9IDcpCmZpbHRlcihkaWFtb25kLCBjYXJhdCAhPSAzKQpgYGAKCjMuICBGaWx0ZXJuIFNpZSBkaWUgRmzDvGdlIGRlcyBgZmxpZ2h0c2AgRGF0ZW5zYXR6ZXMgYW5oYW5kIGRlciBmb2xnZW5kZW4KICAgIEJlZGluZ3VuZ2VuOgogICAgMS4gIEZsw7xnZSBtaXQgZWluZXIgQW5rdW5mdHN2ZXJzcMOkdHVuZyBgYXJyX2RlbGF5YCB2b24gw7xiZXIgMTAKICAgICAgICBTdHVuZGVuCgogICAgMi4gIEZsw7xnZSBuYWNoIFNlYXR0bGUgb2RlciBLYW5zYXMgQ2l0eSAoYFNFQWAgdW5kIGBNQ0lgKQoKICAgIDMuICBGbMO8Z2UgZGVyIEFpcmxpbmVzIFVuaXRlZCAoYFVBYCksIEFtZXJpY2FuIChgQUFgKSBvZGVyIERlbHRhCiAgICAgICAgKGBETGApCgogICAgNC4gIEZsw7xnZSBpbSBXaW50ZXIgKERlemVtYmVyLCBKYW51YXIsIEZlYnJ1YXIpCgogICAgNS4gIEZsw7xnZSBtaXQgZWluZXIgVmVyc3DDpHR1bmcgYGFycl9kZWxheWAgdm9uIG1laHIgYWxzIDMgU3R1bmRlbiwKICAgICAgICB3b2JlaSBkZXIgQWJmbHVnIG5pY2h0IHZlcnNww6R0ZXQgd2FyIChgZGVwX2RlbGF5YCkKCiAgICA2LiAgRmzDvGdlIG1pdCBlaW5lciBBYmZsdWd2ZXJzcMOkdHVuZyBgZGVwX2RlbGF5YCB2b24gbWVociBhbHMgZWluZXIKICAgICAgICBTdHVuZGUsIGRpZSBtZWhyIGFscyBkaWUgSMOkbGZ0ZSBkZXMgRGVsYXlzIGR1cmNoIGRlbiBGbHVnCiAgICAgICAgd2V0dG1hY2hlbiBrb25udGVuCgogICAgNy4gIEZsw7xnZSBhbSBzcMOkdGVuIEFiZW5kIHp3aXNjaGVuIDIyIFVociB1bmQgMjQgVWhyCjQuICBCZXRyYWNodGVuIFNpZSBkaWUgRnVua3Rpb24gYD9iZXR3ZWVuKClgIHVuZCBpaHJlIEZ1bmt0aW9uYWxpdMOkdC4KICAgIFfDvHJkZSBkaWVzZSBJaG5lbiBiZWkgZGVuIHZvcmhlcmlnZW4gQXVmZ2FiZW4gaGVsZmVuPwo1LiAgQmVpIHdpZSB2aWVsZW4gRmzDvGdlbiBmZWhsdCBkaWUgQW5rdW5mdHN6ZWl0IGBkZXBfdGltZWA/IFdhcyBmw6RsbHQKICAgIElobmVuIGF1ZiB1bmQgd2llIGvDtm5uZW4gU2llIHNpY2ggZGllc2UgRWludHLDpGdlIGVya2zDpHJlbj8KNi4gIFNvcnRpZXJlbiBTaWUgZGllIEZsw7xnZToKICAgIDEuICBGaW5kZW4gU2llIGRpZSBGbMO8Z2UgbWl0IGRlciBrbGVpbnN0ZW4gdW5kIGdyw7bDn3RlbgogICAgICAgIEFiZmx1Z3ZlcnNww6R0dW5nIGBkZXBfZGVsYXlgCgogICAgMi4gIEZpbmRlbiBTaWUgZGVuIGvDvHJ6ZXN0ZW4gdW5kIGzDpG5nc3RlbiBGbHVnIChiZXpvZ2VuIGF1ZiBkaWUKICAgICAgICBEaXN0YW56IGBkaXN0YW5jZWApCgogICAgMy4gIEZpbmRlbiBTaWUgZGVuIHNjaG5lbGxzdGVuIEZsdWcgKGJlem9nZW4gYXVmIGRpZQogICAgICAgIEdlc2Nod2luZGlna2VpdCkK