1 Aufgaben

Wie gewohnt soll in diesem Abschnitt ein weiterer Datensatz betrachtet werden.

Der Datensatz weather aus der Bibliothek nycflights13 beschreibt Wetterdaten aus dem Jahr 2013 bezogen auf drei Flughäfen:

library(nycflights13)
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()
weather

Wie gewohnt erfahren Sie mehr über den Datensatz, wenn Sie ?weather eingeben.

1.1 Übungsaufgaben

  1. Finden Sie verschiedene Möglichkeiten, die Variablen year, month, day, hour und time_hour zu entnehmen.
  2. Entnehmen Sie alle Variablen, die wind im Namen haben.
  3. Versuchen Sie mehrfach die Variable temp auszuwählen. Was stellen Sie fest?
  4. Schlagen Sie die Bedeutung von any_of() und all_of() in Bezug zu select() nach. Wie könnte Ihnen any_of() mit dem folgenden Vektor helfen?
vars <- c("jahr", "month", "day", "Wind")
  1. Lassen Sie den folgenden Code laufen. Entspricht das Ergebnis Ihren Erwartungen?
select(weather, contains("hOuR"))
  1. Die Geschwindigkeiten liegen in Meilen pro Stunde vor. Wandeln Sie diese in Kilometer pro Stunde um (Faktor 1,609344).
  2. Die Temperaturen liegen in Fahrenheit vor. Wandeln Sie diese in Grad Celsius um (°C = (°F - 32) / 1,8).
  3. Es liegen Daten bezüglich der Windgeschwindigkeit und der Geschwindigkeit von Windstößen vor. Wo könnten hier die Unterschiede liegen? Berechnen Sie die Differenz von wind_speed und wind_gust. Welche Information erhalten Sie hieraus?
  4. Finden Sie die Einträge mit den 15 stärksten Windstößen. Versuchen Sie hierbei die min_rank() Funktion zu verwenden. Überlegen Sie sich mehrere Wege, dieses Problem anzugehen. Wie handhaben Sie Einträge mit denselben Rängen (gleiche Windgeschwindigkeit)?
  5. Geben Sie für jeden Flughafen (origin) die höchste Temperatur, die höchste Luftfeuchtigkeit und den kleinsten Luftdruck aus.
  6. Geben Sie für jedes Datum die durchschnittliche Windgeschwindigkeit (wind_speed) aus.

1.2 Lösungen

1.2.1 Finden Sie verschiedene Möglichkeiten, die Variablen year, month, day, hour und time_hour zu entnehmen.

Die Standardmethode:

select(weather, year, month, day, hour, time_hour)

Da die Zeiteinträge die zweite bis fünfte Spalte und die letzte Spalte beschreiben, können wir das auch ausnutzen:

select(weather, 2:5, 15)

Einträge lassen sich auch gemischt mit Index oder Zahl entnehmen, wenn die Position einer Variable nicht per Hand bestimmt werden soll:

select(weather, 2:5, time_hour)

Mit Hilfe von contains() lassen sich auch hour und time_hour gleichzeitig entnehmen:

select(weather, 2:4, contains("hour"))

Mit Hilfe von contains() lassen sich auch die Einträge entnehmen, die ein "a" oder ein "u" enthalten und nicht origin sind:

select(weather, contains("a"), contains("o"), -origin)

Hinweis: an dieser Stelle würden sich reguläre Expressionen (Zeichenketten filtern/auswählen) anbieten, was wir nicht behandeln wollen.

1.2.2 Entnehmen Sie alle Variablen, die wind im Namen haben.

select(weather, contains("wind"))

1.2.3 Versuchen Sie mehrfach die Variable temp auszuwählen. Was stellen Sie fest?

select(weather, temp, temp, day, temp)

Sie sehen, dass sich eine Variable nur einmal entnehmen lässt. Die Position entspricht dann der ersten Nennung der Variablen.

1.2.4 Schlagen Sie die Bedeutung von any_of() und all_of() in Bezug zu select() nach. Wie könnte Ihnen any_of() mit dem folgenden Vektor helfen?

vars <- c("jahr", "month", "day", "Wind")
  • any_of(x): Wähle “irgendwelche von x aus” aus. Wählt nur die möglichen Variablen aus und lässt fehlende aus.

  • all_of(x): Wähle “alle von x aus”. Gibt eine Fehlermeldung, falls eine Variable aus x nicht gefunden wird.

# Wähle alle (möglichen) Variablen aus vars aus:
select(weather, any_of(vars))
# Wähle alle Variablen aus:
select(weather, all_of(vars))
Error in `select()`:
! Can't subset columns that don't exist.
✖ Columns `jahr` and `Wind` don't exist.
Backtrace:
  1. dplyr::select(weather, all_of(vars))
  2. dplyr:::select.data.frame(weather, all_of(vars))
  5. tidyselect::eval_select(expr(c(...)), .data)
  6. tidyselect:::eval_select_impl(...)
 15. tidyselect:::vars_select_eval(...)
     ...
 18. tidyselect:::reduce_sels(node, data_mask, context_mask, init = init)
 19. tidyselect:::walk_data_tree(new, data_mask, context_mask)
 20. tidyselect:::as_indices_sel_impl(...)
 21. tidyselect:::as_indices_impl(x, vars, call = call, strict = strict)
 22. tidyselect:::chr_as_locations(x, vars, call = call)

1.2.5 Lassen Sie den folgenden Code laufen. Entspricht das Ergebnis Ihren Erwartungen?

select(weather, contains("hOuR"))

Wir sehen, dass die Groß-und Kleinschreibung von "hOuR" ignoriert wird. Falls dies beachtet werden soll, muss ignore.case = FALSE in contains() gesetzt werden:

select(weather, contains("hOuR", ignore.case = FALSE))

1.2.6 Die Geschwindigkeiten liegen in Meilen pro Stunde vor. Wandeln Sie diese in Kilometer pro Stunde um (Faktor 1,609344).

# Hierbei wird weather aktiv überschrieben, d. h. mit den neuen Werten ersetzt
weather <- weather %>% # durch diesen Operator wird weather in die nächste Funktion übergeben
    mutate(wind_speed = wind_speed * 1.609344,
                 wind_gust = wind_gust * 1.609344)
weather
# Derselbe Befehl ohne Pipe:
weather <- mutate(weather, wind_speed = wind_speed * 1.609344,
                                    wind_gust = wind_gust * 1.609344)

1.2.7 Die Temperaturen liegen in Fahrenheit vor. Wandeln Sie diese in Grad Celsius um (°C = (°F - 32) / 1,8).

weather <- weather %>%
    mutate(temp = (temp - 32) / 1.8)
weather

1.2.8 Es liegen Daten bezüglich der Windgeschwindigkeit und der Geschwindigkeit von Windstößen vor. Wo könnten hier die Unterschiede liegen? Berechnen Sie die Differenz von wind_speed und wind_gust. Welche Information erhalten Sie hieraus?

Die Variable wind_speed beschreibt die durchschnittliche Windgeschwindigkeit innerhalb der stündlichen Intervalle. Die Variable wind_gust hingegen beschreibt die maximale Geschwindigkeit von Windstößen, welche kurzzeitige, starke Anstiege in der Windgeschwindigkeit verursachen.

Die Differenz lässt sich wie folgt berechnen:

# Nur die relevanten Spalten werden mit transmute betrachtet:
transmute(weather, wind_speed, wind_gust, wind_diff = wind_gust - wind_speed)

Entsprechend erhalten wir hier Information darüber, wie sehr die Geschwindigkeit von Windstöße von dem Durchschnitt abweichen.

Nun ließe sich beispielsweise der Eintrag mit der höchsten Differenz ausfindig machen (sehr starker Windstoß):

weather %>%
    mutate(wind_diff = wind_gust - wind_speed) %>%
    slice_max(wind_diff)

1.2.9 Finden Sie die Einträge mit den 15 stärksten Windstößen. Versuchen Sie hierbei die min_rank() Funktion zu verwenden. Überlegen Sie sich mehrere Wege, dieses Problem anzugehen. Wie handhaben Sie Einträge mit denselben Rängen (gleiche Windgeschwindigkeit)?

Eine Möglichkeit ist die Bestimmung des Rangs von wind_gust und der anschließenden Filterung nach den ersten 1:15 Einträgen.

weather %>%
    mutate(gust_rank = min_rank(desc(wind_gust))) %>%
    filter(gust_rank %in% 1:15)

Alternativ könnte auch versucht werden, die Einträge absteigend nach wind_gust zu sortieren und dann die ersten Einträge auszuwählen:

weather %>%
    arrange(desc(wind_gust)) %>%
    slice(1:15)

Das Problem hierbei ist, dass gleiche Werte nicht beachtet werden, sodass mehrere Beobachtungen mit demselben Wert ignoriert werden.

Eine bessere Alternative mit slice_max():

weather %>%
    slice_max(wind_gust, n = 15)

1.2.10 Geben Sie für jeden Flughafen (origin) die höchste Temperatur, die höchste Luftfeuchtigkeit und den kleinsten Luftdruck aus.

Zuvor wurde gezeigt, dass für Fragestellungen dieser Art eine Zusammenfassung mit summarise() verwendet wird in Verbindung mit einer Gruppierung (nach origin):

# Vorsicht: es gibt bei allen Variablen fehlende Werte
weather %>%
    group_by(origin) %>%
    summarise(max_temp = max(temp, na.rm = TRUE),
                        max_humid = max(humid, na.rm = TRUE),
                        min_pressure = min(pressure, na.rm = TRUE))

Sie sehen, dass group_by() bewirkt, dass die folgende summarise() Funktion auf jeden Flughafen (origin) separat die einzelnen Werte berechnet. In summarise() werden die Werte zusammengefasst, indem ein neuer Variablenname angegeben wird, welcher beispielsweise dem Minimum min() oder dem Maximum max() einer vorherigen Variablen entspricht.

1.2.11 Geben Sie für jedes Datum die durchschnittliche Windgeschwindigkeit (wind_speed) aus.

Hierbei müssen Sie beachten, dass Sie für das Datum gemäß Jahr, Monat und Tag gruppieren müssen:

weather %>%
    group_by(year, month, day) %>%
    summarise(avg_speed = mean(wind_speed))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
LS0tCnRpdGxlOiAiw5xidW5nc3pldHRlbCA0IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKLS0tCgojIEF1ZmdhYmVuCgpXaWUgZ2V3b2hudCBzb2xsIGluIGRpZXNlbSBBYnNjaG5pdHQgZWluIHdlaXRlcmVyIERhdGVuc2F0eiBiZXRyYWNodGV0CndlcmRlbi4KCkRlciBEYXRlbnNhdHogYHdlYXRoZXJgIGF1cyBkZXIgQmlibGlvdGhlayBgbnljZmxpZ2h0czEzYCBiZXNjaHJlaWJ0CldldHRlcmRhdGVuIGF1cyBkZW0gSmFociAyMDEzIGJlem9nZW4gYXVmIGRyZWkgRmx1Z2jDpGZlbjoKCmBgYHtyfQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKbGlicmFyeSh0aWR5dmVyc2UpCndlYXRoZXIKYGBgCgpXaWUgZ2V3b2hudCBlcmZhaHJlbiBTaWUgbWVociDDvGJlciBkZW4gRGF0ZW5zYXR6LCB3ZW5uIFNpZSBgP3dlYXRoZXJgCmVpbmdlYmVuLgoKIyMgw5xidW5nc2F1ZmdhYmVuCgoxLiAgRmluZGVuIFNpZSB2ZXJzY2hpZWRlbmUgTcO2Z2xpY2hrZWl0ZW4sIGRpZSBWYXJpYWJsZW4gYHllYXJgLAogICAgYG1vbnRoYCwgYGRheWAsIGBob3VyYCB1bmQgYHRpbWVfaG91cmAgenUgZW50bmVobWVuLgoyLiAgRW50bmVobWVuIFNpZSBhbGxlIFZhcmlhYmxlbiwgZGllIGB3aW5kYCBpbSBOYW1lbiBoYWJlbi4KMy4gIFZlcnN1Y2hlbiBTaWUgbWVocmZhY2ggZGllIFZhcmlhYmxlIGB0ZW1wYCBhdXN6dXfDpGhsZW4uIFdhcyBzdGVsbGVuCiAgICBTaWUgZmVzdD8KNC4gIFNjaGxhZ2VuIFNpZSBkaWUgQmVkZXV0dW5nIHZvbiBgYW55X29mKClgIHVuZCBgYWxsX29mKClgIGluIEJlenVnIHp1CiAgICBgc2VsZWN0KClgIG5hY2guIFdpZSBrw7ZubnRlIElobmVuIGBhbnlfb2YoKWAgbWl0IGRlbSBmb2xnZW5kZW4KICAgIFZla3RvciBoZWxmZW4/CgpgYGB7cn0KdmFycyA8LSBjKCJqYWhyIiwgIm1vbnRoIiwgImRheSIsICJXaW5kIikKYGBgCgo1LiAgTGFzc2VuIFNpZSBkZW4gZm9sZ2VuZGVuIENvZGUgbGF1ZmVuLiBFbnRzcHJpY2h0IGRhcyBFcmdlYm5pcyBJaHJlbgogICAgRXJ3YXJ0dW5nZW4/CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpzZWxlY3Qod2VhdGhlciwgY29udGFpbnMoImhPdVIiKSkKYGBgCgo2LiAgRGllIEdlc2Nod2luZGlna2VpdGVuIGxpZWdlbiBpbiBNZWlsZW4gcHJvIFN0dW5kZSB2b3IuIFdhbmRlbG4gU2llCiAgICBkaWVzZSBpbiBLaWxvbWV0ZXIgcHJvIFN0dW5kZSB1bSAoRmFrdG9yIGAxLDYwOTM0NGApLgo3LiAgRGllIFRlbXBlcmF0dXJlbiBsaWVnZW4gaW4gRmFocmVuaGVpdCB2b3IuIFdhbmRlbG4gU2llIGRpZXNlIGluIEdyYWQKICAgIENlbHNpdXMgdW0gKMKwQyA9ICjCsEYgLSAzMikgLyAxLDgpLgo4LiAgRXMgbGllZ2VuIERhdGVuIGJlesO8Z2xpY2ggZGVyIFdpbmRnZXNjaHdpbmRpZ2tlaXQgdW5kIGRlcgogICAgR2VzY2h3aW5kaWdrZWl0IHZvbiBXaW5kc3TDtsOfZW4gdm9yLiBXbyBrw7ZubnRlbiBoaWVyIGRpZSBVbnRlcnNjaGllZGUKICAgIGxpZWdlbj8gQmVyZWNobmVuIFNpZSBkaWUgRGlmZmVyZW56IHZvbiBgd2luZF9zcGVlZGAgdW5kCiAgICBgd2luZF9ndXN0YC4gV2VsY2hlIEluZm9ybWF0aW9uIGVyaGFsdGVuIFNpZSBoaWVyYXVzPwo5LiAgRmluZGVuIFNpZSBkaWUgRWludHLDpGdlIG1pdCBkZW4gMTUgc3TDpHJrc3RlbiBXaW5kc3TDtsOfZW4uIFZlcnN1Y2hlbgogICAgU2llIGhpZXJiZWkgZGllIGBtaW5fcmFuaygpYCBGdW5rdGlvbiB6dSB2ZXJ3ZW5kZW4uIMOcYmVybGVnZW4gU2llCiAgICBzaWNoIG1laHJlcmUgV2VnZSwgZGllc2VzIFByb2JsZW0gYW56dWdlaGVuLiBXaWUgaGFuZGhhYmVuIFNpZQogICAgRWludHLDpGdlIG1pdCBkZW5zZWxiZW4gUsOkbmdlbiAoZ2xlaWNoZSBXaW5kZ2VzY2h3aW5kaWdrZWl0KT8KMTAuIEdlYmVuIFNpZSBmw7xyIGplZGVuIEZsdWdoYWZlbiAoYG9yaWdpbmApIGRpZSBow7ZjaHN0ZSBUZW1wZXJhdHVyLCBkaWUKICAgIGjDtmNoc3RlIEx1ZnRmZXVjaHRpZ2tlaXQgdW5kIGRlbiBrbGVpbnN0ZW4gTHVmdGRydWNrIGF1cy4KMTEuIEdlYmVuIFNpZSBmw7xyIGplZGVzIERhdHVtIGRpZSBkdXJjaHNjaG5pdHRsaWNoZSBXaW5kZ2VzY2h3aW5kaWdrZWl0CiAgICAoYHdpbmRfc3BlZWRgKSBhdXMuCgojIyBMw7ZzdW5nZW4KCiMjIyBGaW5kZW4gU2llIHZlcnNjaGllZGVuZSBNw7ZnbGljaGtlaXRlbiwgZGllIFZhcmlhYmxlbiBgeWVhcmAsIGBtb250aGAsIGBkYXlgLCBgaG91cmAgdW5kIGB0aW1lX2hvdXJgIHp1IGVudG5laG1lbi4KCkRpZSBTdGFuZGFyZG1ldGhvZGU6CgpgYGB7cn0Kc2VsZWN0KHdlYXRoZXIsIHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIHRpbWVfaG91cikKYGBgCgpEYSBkaWUgWmVpdGVpbnRyw6RnZSBkaWUgendlaXRlIGJpcyBmw7xuZnRlIFNwYWx0ZSB1bmQgZGllIGxldHp0ZSBTcGFsdGUKYmVzY2hyZWliZW4sIGvDtm5uZW4gd2lyIGRhcyBhdWNoIGF1c251dHplbjoKCmBgYHtyfQpzZWxlY3Qod2VhdGhlciwgMjo1LCAxNSkKYGBgCgpFaW50csOkZ2UgbGFzc2VuIHNpY2ggYXVjaCBnZW1pc2NodCBtaXQgSW5kZXggb2RlciBaYWhsIGVudG5laG1lbiwgd2VubgpkaWUgUG9zaXRpb24gZWluZXIgVmFyaWFibGUgbmljaHQgcGVyIEhhbmQgYmVzdGltbXQgd2VyZGVuIHNvbGw6CgpgYGB7cn0Kc2VsZWN0KHdlYXRoZXIsIDI6NSwgdGltZV9ob3VyKQpgYGAKCk1pdCBIaWxmZSB2b24gYGNvbnRhaW5zKClgIGxhc3NlbiBzaWNoIGF1Y2ggYGhvdXJgIHVuZCBgdGltZV9ob3VyYApnbGVpY2h6ZWl0aWcgZW50bmVobWVuOgoKYGBge3J9CnNlbGVjdCh3ZWF0aGVyLCAyOjQsIGNvbnRhaW5zKCJob3VyIikpCmBgYAoKTWl0IEhpbGZlIHZvbiBgY29udGFpbnMoKWAgbGFzc2VuIHNpY2ggYXVjaCBkaWUgRWludHLDpGdlIGVudG5laG1lbiwgZGllCmVpbiBgImEiYCBvZGVyIGVpbiBgInUiYCBlbnRoYWx0ZW4gdW5kIG5pY2h0IGBvcmlnaW5gIHNpbmQ6CgpgYGB7cn0Kc2VsZWN0KHdlYXRoZXIsIGNvbnRhaW5zKCJhIiksIGNvbnRhaW5zKCJvIiksIC1vcmlnaW4pCmBgYAoKSGlud2VpczogYW4gZGllc2VyIFN0ZWxsZSB3w7xyZGVuIHNpY2ggcmVndWzDpHJlIEV4cHJlc3Npb25lbgooWmVpY2hlbmtldHRlbiBmaWx0ZXJuL2F1c3fDpGhsZW4pIGFuYmlldGVuLCB3YXMgd2lyIG5pY2h0IGJlaGFuZGVsbgp3b2xsZW4uCgojIyMgRW50bmVobWVuIFNpZSBhbGxlIFZhcmlhYmxlbiwgZGllIGB3aW5kYCBpbSBOYW1lbiBoYWJlbi4KCmBgYHtyfQpzZWxlY3Qod2VhdGhlciwgY29udGFpbnMoIndpbmQiKSkKYGBgCgojIyMgVmVyc3VjaGVuIFNpZSBtZWhyZmFjaCBkaWUgVmFyaWFibGUgYHRlbXBgIGF1c3p1d8OkaGxlbi4gV2FzIHN0ZWxsZW4gU2llIGZlc3Q/CgpgYGB7cn0Kc2VsZWN0KHdlYXRoZXIsIHRlbXAsIHRlbXAsIGRheSwgdGVtcCkKYGBgCgpTaWUgc2VoZW4sIGRhc3Mgc2ljaCBlaW5lIFZhcmlhYmxlIG51ciBlaW5tYWwgZW50bmVobWVuIGzDpHNzdC4gRGllClBvc2l0aW9uIGVudHNwcmljaHQgZGFubiBkZXIgZXJzdGVuIE5lbm51bmcgZGVyIFZhcmlhYmxlbi4KCiMjIyBTY2hsYWdlbiBTaWUgZGllIEJlZGV1dHVuZyB2b24gYGFueV9vZigpYCB1bmQgYGFsbF9vZigpYCBpbiBCZXp1ZyB6dSBgc2VsZWN0KClgIG5hY2guIFdpZSBrw7ZubnRlIElobmVuIGBhbnlfb2YoKWAgbWl0IGRlbSBmb2xnZW5kZW4gVmVrdG9yIGhlbGZlbj8KCmBgYHtyfQp2YXJzIDwtIGMoImphaHIiLCAibW9udGgiLCAiZGF5IiwgIldpbmQiKQpgYGAKCi0gICBgYW55X29mKHgpYDogV8OkaGxlICJpcmdlbmR3ZWxjaGUgdm9uIGB4YCBhdXMiIGF1cy4gV8OkaGx0IG51ciBkaWUKICAgIG3DtmdsaWNoZW4gVmFyaWFibGVuIGF1cyB1bmQgbMOkc3N0IGZlaGxlbmRlIGF1cy4KCi0gICBgYWxsX29mKHgpYDogV8OkaGxlICJhbGxlIHZvbiBgeGAgYXVzIi4gR2lidCBlaW5lIEZlaGxlcm1lbGR1bmcsCiAgICBmYWxscyBlaW5lIFZhcmlhYmxlIGF1cyBgeGAgbmljaHQgZ2VmdW5kZW4gd2lyZC4KCmBgYHtyfQojIFfDpGhsZSBhbGxlIChtw7ZnbGljaGVuKSBWYXJpYWJsZW4gYXVzIHZhcnMgYXVzOgpzZWxlY3Qod2VhdGhlciwgYW55X29mKHZhcnMpKQojIFfDpGhsZSBhbGxlIFZhcmlhYmxlbiBhdXM6CnNlbGVjdCh3ZWF0aGVyLCBhbGxfb2YodmFycykpCmBgYAoKIyMjIExhc3NlbiBTaWUgZGVuIGZvbGdlbmRlbiBDb2RlIGxhdWZlbi4gRW50c3ByaWNodCBkYXMgRXJnZWJuaXMgSWhyZW4gRXJ3YXJ0dW5nZW4/CgpgYGB7cn0Kc2VsZWN0KHdlYXRoZXIsIGNvbnRhaW5zKCJoT3VSIikpCmBgYAoKV2lyIHNlaGVuLCBkYXNzIGRpZSBHcm/Dny11bmQgS2xlaW5zY2hyZWlidW5nIHZvbiBgImhPdVIiYCBpZ25vcmllcnQKd2lyZC4gRmFsbHMgZGllcyBiZWFjaHRldCB3ZXJkZW4gc29sbCwgbXVzcyBgaWdub3JlLmNhc2UgPSBGQUxTRWAgaW4KYGNvbnRhaW5zKClgIGdlc2V0enQgd2VyZGVuOgoKYGBge3J9CnNlbGVjdCh3ZWF0aGVyLCBjb250YWlucygiaE91UiIsIGlnbm9yZS5jYXNlID0gRkFMU0UpKQpgYGAKCiMjIyBEaWUgR2VzY2h3aW5kaWdrZWl0ZW4gbGllZ2VuIGluIE1laWxlbiBwcm8gU3R1bmRlIHZvci4gV2FuZGVsbiBTaWUgZGllc2UgaW4gS2lsb21ldGVyIHBybyBTdHVuZGUgdW0gKEZha3RvciBgMSw2MDkzNDRgKS4KCmBgYHtyfQojIEhpZXJiZWkgd2lyZCB3ZWF0aGVyIGFrdGl2IMO8YmVyc2NocmllYmVuLCBkLiBoLiBtaXQgZGVuIG5ldWVuIFdlcnRlbiBlcnNldHp0CndlYXRoZXIgPC0gd2VhdGhlciAlPiUgIyBkdXJjaCBkaWVzZW4gT3BlcmF0b3Igd2lyZCB3ZWF0aGVyIGluIGRpZSBuw6RjaHN0ZSBGdW5rdGlvbiDDvGJlcmdlYmVuCgltdXRhdGUod2luZF9zcGVlZCA9IHdpbmRfc3BlZWQgKiAxLjYwOTM0NCwKCQkJCSB3aW5kX2d1c3QgPSB3aW5kX2d1c3QgKiAxLjYwOTM0NCkKd2VhdGhlcgpgYGAKCmBgYHtyLCBldmFsID0gRkFMU0V9CiMgRGVyc2VsYmUgQmVmZWhsIG9obmUgUGlwZToKd2VhdGhlciA8LSBtdXRhdGUod2VhdGhlciwgd2luZF9zcGVlZCA9IHdpbmRfc3BlZWQgKiAxLjYwOTM0NCwKCQkJCQkJCQkJd2luZF9ndXN0ID0gd2luZF9ndXN0ICogMS42MDkzNDQpCmBgYAoKIyMjIERpZSBUZW1wZXJhdHVyZW4gbGllZ2VuIGluIEZhaHJlbmhlaXQgdm9yLiBXYW5kZWxuIFNpZSBkaWVzZSBpbiBHcmFkIENlbHNpdXMgdW0gKMKwQyA9ICjCsEYgLSAzMikgLyAxLDgpLgoKYGBge3J9CndlYXRoZXIgPC0gd2VhdGhlciAlPiUKCW11dGF0ZSh0ZW1wID0gKHRlbXAgLSAzMikgLyAxLjgpCndlYXRoZXIKYGBgCgojIyMgRXMgbGllZ2VuIERhdGVuIGJlesO8Z2xpY2ggZGVyIFdpbmRnZXNjaHdpbmRpZ2tlaXQgdW5kIGRlciBHZXNjaHdpbmRpZ2tlaXQgdm9uIFdpbmRzdMO2w59lbiB2b3IuIFdvIGvDtm5udGVuIGhpZXIgZGllIFVudGVyc2NoaWVkZSBsaWVnZW4/IEJlcmVjaG5lbiBTaWUgZGllIERpZmZlcmVueiB2b24gYHdpbmRfc3BlZWRgIHVuZCBgd2luZF9ndXN0YC4gV2VsY2hlIEluZm9ybWF0aW9uIGVyaGFsdGVuIFNpZSBoaWVyYXVzPwoKRGllIFZhcmlhYmxlIGB3aW5kX3NwZWVkYCBiZXNjaHJlaWJ0IGRpZSBkdXJjaHNjaG5pdHRsaWNoZQpXaW5kZ2VzY2h3aW5kaWdrZWl0IGlubmVyaGFsYiBkZXIgc3TDvG5kbGljaGVuIEludGVydmFsbGUuIERpZSBWYXJpYWJsZQpgd2luZF9ndXN0YCBoaW5nZWdlbiBiZXNjaHJlaWJ0IGRpZSBtYXhpbWFsZSBHZXNjaHdpbmRpZ2tlaXQgdm9uCldpbmRzdMO2w59lbiwgd2VsY2hlIGt1cnp6ZWl0aWdlLCBzdGFya2UgQW5zdGllZ2UgaW4gZGVyCldpbmRnZXNjaHdpbmRpZ2tlaXQgdmVydXJzYWNoZW4uCgpEaWUgRGlmZmVyZW56IGzDpHNzdCBzaWNoIHdpZSBmb2xndCBiZXJlY2huZW46CgpgYGB7cn0KIyBOdXIgZGllIHJlbGV2YW50ZW4gU3BhbHRlbiB3ZXJkZW4gbWl0IHRyYW5zbXV0ZSBiZXRyYWNodGV0Ogp0cmFuc211dGUod2VhdGhlciwgd2luZF9zcGVlZCwgd2luZF9ndXN0LCB3aW5kX2RpZmYgPSB3aW5kX2d1c3QgLSB3aW5kX3NwZWVkKQpgYGAKCkVudHNwcmVjaGVuZCBlcmhhbHRlbiB3aXIgaGllciBJbmZvcm1hdGlvbiBkYXLDvGJlciwgd2llIHNlaHIgZGllCkdlc2Nod2luZGlna2VpdCB2b24gV2luZHN0w7bDn2Ugdm9uIGRlbSBEdXJjaHNjaG5pdHQgYWJ3ZWljaGVuLgoKTnVuIGxpZcOfZSBzaWNoIGJlaXNwaWVsc3dlaXNlIGRlciBFaW50cmFnIG1pdCBkZXIgaMO2Y2hzdGVuIERpZmZlcmVuegphdXNmaW5kaWcgbWFjaGVuIChzZWhyIHN0YXJrZXIgV2luZHN0b8OfKToKCmBgYHtyfQp3ZWF0aGVyICU+JQoJbXV0YXRlKHdpbmRfZGlmZiA9IHdpbmRfZ3VzdCAtIHdpbmRfc3BlZWQpICU+JQoJc2xpY2VfbWF4KHdpbmRfZGlmZikKYGBgCgojIyMgRmluZGVuIFNpZSBkaWUgRWludHLDpGdlIG1pdCBkZW4gMTUgc3TDpHJrc3RlbiBXaW5kc3TDtsOfZW4uIFZlcnN1Y2hlbiBTaWUgaGllcmJlaSBkaWUgYG1pbl9yYW5rKClgIEZ1bmt0aW9uIHp1IHZlcndlbmRlbi4gw5xiZXJsZWdlbiBTaWUgc2ljaCBtZWhyZXJlIFdlZ2UsIGRpZXNlcyBQcm9ibGVtIGFuenVnZWhlbi4gV2llIGhhbmRoYWJlbiBTaWUgRWludHLDpGdlIG1pdCBkZW5zZWxiZW4gUsOkbmdlbiAoZ2xlaWNoZSBXaW5kZ2VzY2h3aW5kaWdrZWl0KT8KCkVpbmUgTcO2Z2xpY2hrZWl0IGlzdCBkaWUgQmVzdGltbXVuZyBkZXMgUmFuZ3Mgdm9uIGB3aW5kX2d1c3RgIHVuZCBkZXIKYW5zY2hsaWXDn2VuZGVuIEZpbHRlcnVuZyBuYWNoIGRlbiBlcnN0ZW4gMToxNSBFaW50csOkZ2VuLgoKYGBge3J9CndlYXRoZXIgJT4lCgltdXRhdGUoZ3VzdF9yYW5rID0gbWluX3JhbmsoZGVzYyh3aW5kX2d1c3QpKSkgJT4lCglmaWx0ZXIoZ3VzdF9yYW5rICVpbiUgMToxNSkKYGBgCgpBbHRlcm5hdGl2IGvDtm5udGUgYXVjaCB2ZXJzdWNodCB3ZXJkZW4sIGRpZSBFaW50csOkZ2UgYWJzdGVpZ2VuZCBuYWNoCmB3aW5kX2d1c3RgIHp1IHNvcnRpZXJlbiB1bmQgZGFubiBkaWUgZXJzdGVuIEVpbnRyw6RnZSBhdXN6dXfDpGhsZW46CgpgYGB7cn0Kd2VhdGhlciAlPiUKCWFycmFuZ2UoZGVzYyh3aW5kX2d1c3QpKSAlPiUKCXNsaWNlKDE6MTUpCmBgYAoKRGFzIFByb2JsZW0gaGllcmJlaSBpc3QsIGRhc3MgZ2xlaWNoZSBXZXJ0ZSBuaWNodCBiZWFjaHRldCB3ZXJkZW4sCnNvZGFzcyBtZWhyZXJlIEJlb2JhY2h0dW5nZW4gbWl0IGRlbXNlbGJlbiBXZXJ0IGlnbm9yaWVydCB3ZXJkZW4uCgpFaW5lIGJlc3NlcmUgQWx0ZXJuYXRpdmUgbWl0IGBzbGljZV9tYXgoKWA6CgpgYGB7cn0Kd2VhdGhlciAlPiUKCXNsaWNlX21heCh3aW5kX2d1c3QsIG4gPSAxNSkKYGBgCgojIyMgR2ViZW4gU2llIGbDvHIgamVkZW4gRmx1Z2hhZmVuIChgb3JpZ2luYCkgZGllIGjDtmNoc3RlIFRlbXBlcmF0dXIsIGRpZSBow7ZjaHN0ZSBMdWZ0ZmV1Y2h0aWdrZWl0IHVuZCBkZW4ga2xlaW5zdGVuIEx1ZnRkcnVjayBhdXMuCgpadXZvciB3dXJkZSBnZXplaWd0LCBkYXNzIGbDvHIgRnJhZ2VzdGVsbHVuZ2VuIGRpZXNlciBBcnQgZWluZQpadXNhbW1lbmZhc3N1bmcgbWl0IGBzdW1tYXJpc2UoKWAgdmVyd2VuZGV0IHdpcmQgaW4gVmVyYmluZHVuZyBtaXQgZWluZXIKR3J1cHBpZXJ1bmcgKG5hY2ggYG9yaWdpbmApOgoKYGBge3J9CiMgVm9yc2ljaHQ6IGVzIGdpYnQgYmVpIGFsbGVuIFZhcmlhYmxlbiBmZWhsZW5kZSBXZXJ0ZQp3ZWF0aGVyICU+JQoJZ3JvdXBfYnkob3JpZ2luKSAlPiUKCXN1bW1hcmlzZShtYXhfdGVtcCA9IG1heCh0ZW1wLCBuYS5ybSA9IFRSVUUpLAoJCQkJCQltYXhfaHVtaWQgPSBtYXgoaHVtaWQsIG5hLnJtID0gVFJVRSksCgkJCQkJCW1pbl9wcmVzc3VyZSA9IG1pbihwcmVzc3VyZSwgbmEucm0gPSBUUlVFKSkKYGBgCgpTaWUgc2VoZW4sIGRhc3MgYGdyb3VwX2J5KClgIGJld2lya3QsIGRhc3MgZGllIGZvbGdlbmRlIGBzdW1tYXJpc2UoKWAKRnVua3Rpb24gYXVmIGplZGVuIEZsdWdoYWZlbiAoYG9yaWdpbmApIHNlcGFyYXQgZGllIGVpbnplbG5lbiBXZXJ0ZQpiZXJlY2huZXQuIEluIGBzdW1tYXJpc2UoKWAgd2VyZGVuIGRpZSBXZXJ0ZSB6dXNhbW1lbmdlZmFzc3QsIGluZGVtIGVpbgpuZXVlciBWYXJpYWJsZW5uYW1lIGFuZ2VnZWJlbiB3aXJkLCB3ZWxjaGVyIGJlaXNwaWVsc3dlaXNlIGRlbSBNaW5pbXVtCmBtaW4oKWAgb2RlciBkZW0gTWF4aW11bSBgbWF4KClgIGVpbmVyIHZvcmhlcmlnZW4gVmFyaWFibGVuIGVudHNwcmljaHQuCgojIyMgR2ViZW4gU2llIGbDvHIgamVkZXMgRGF0dW0gZGllIGR1cmNoc2Nobml0dGxpY2hlIFdpbmRnZXNjaHdpbmRpZ2tlaXQgKGB3aW5kX3NwZWVkYCkgYXVzLgoKSGllcmJlaSBtw7xzc2VuIFNpZSBiZWFjaHRlbiwgZGFzcyBTaWUgZsO8ciBkYXMgRGF0dW0gZ2Vtw6TDnyBKYWhyLCBNb25hdAp1bmQgVGFnIGdydXBwaWVyZW4gbcO8c3NlbjoKCmBgYHtyfQp3ZWF0aGVyICU+JQoJZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheSkgJT4lCglzdW1tYXJpc2UoYXZnX3NwZWVkID0gbWVhbih3aW5kX3NwZWVkKSkKYGBgCg==