suppressPackageStartupMessages(library(nycflights13))
package 㤼㸱nycflights13㤼㸲 was built under R version 3.6.3
suppressPackageStartupMessages(library(tidyverse))
package 㤼㸱tidyverse㤼㸲 was built under R version 3.6.3

1. Brainstorm at least 5 different ways to assess the typical delay characteristics of a group of flights. Consider the following scenarios:

  • A flight is 15 minutes early 50% of the time, and 15 minutes late 50% of the time.
  • A flight is always 10 minutes late.
  • A flight is 30 minutes early 50% of the time, and 30 minutes late 50% of the time.
  • 99% of the time a flight is on time. 1% of the time it’s 2 hours late.
  • Which is more important: arrival delay or departure delay?

What this question gets at is a fundamental question of data analysis: the cost function. As analysts, the reason we are interested in flight delay because it is costly to passengers. But it is worth thinking carefully about how it is costly and use that information in ranking and measuring these scenarios.

In many scenarios, arrival delay is more important. In most cases, arriving late is more costly to the passenger since it could disrupt the next stages of their travel, such as connecting flights or scheduled meetings.

If a departure is delayed without affecting the arrival time, this delay will not have those affects plans nor does it affect the total time spent traveling. This delay could be beneficial, if less time is spent in the cramped confines of the airplane itself, or a negative, if that delayed time is still spent in the cramped confines of the airplane on the runway.

Variation in arrival time is worse than consistency. If a flight is always 30 minutes late and that delay is known, then it is as if the arrival time is that delayed time. The traveler could easily plan for this. But higher variation in flight times makes it harder to plan.

2. Come up with another approach that will give you the same output as not_cancelled %>% count(dest) and not_cancelled %>% count(tailnum, wt = distance) (without using count()).

not_cancelled <- flights %>%
  filter(!is.na(dep_delay), !is.na(arr_delay))

The first expression is the following.

not_cancelled %>%
  count(dest)

The count() function counts the number of instances within each group of variables. Instead of using the count() function,we can combine the group_by() and summarise() verbs.

not_cancelled %>%
  group_by(dest) %>%
  summarise(n = length(dest))

An alternative method for getting the number of observations in a data frame is the function n().

not_cancelled %>%
  group_by(dest) %>%
  summarise(n = n())

Another alternative to count() is to use the combination of the group_by() and tally() verbs. In fact, count() is effectively a short-cut for group_by() followed by tally().

not_cancelled %>%
  group_by(tailnum) %>%
  tally()

The second expression also uses the count() function, but adds a wt argument.

not_cancelled %>%
  count(tailnum, wt = distance)

As before, we can replicate count() by combining the group_by() and summarise() verbs. But this time instead of using length(), we will use sum() with the weighting variable.

not_cancelled %>%
  group_by(tailnum) %>%
  summarise(n = sum(distance))

Like the previous example, we can also use the combination group_by() and tally(). Any arguments to tally() are summed.

not_cancelled %>%
  group_by(tailnum) %>%
  tally(distance)

Exercise 3. Our definition of cancelled flights (is.na(dep_delay) | is.na(arr_delay)) is slightly suboptimal. Why? Which is the most important column?

If a flight never departs, then it won’t arrive. A flight could also depart and not arrive if it crashes, or if it is redirected and lands in an airport other than its intended destination. So the most important column is arr_delay, which indicates the amount of delay in arrival.

filter(flights, !is.na(dep_delay), is.na(arr_delay)) %>%
  select(dep_time, arr_time, sched_arr_time, dep_delay, arr_delay)

Okay, I’m not sure what’s going on in this data. dep_time can be non-missing and arr_delay missing but arr_time not missing. They may be combining different flights?

5. Which carrier has the worst delays? Challenge: can you disentangle the effects of bad airports vs. bad carriers? Why/why not? (Hint: think about flights %>% group_by(carrier, dest) %>% summarise(n()))

flights %>%
  group_by(carrier) %>%
  summarise(arr_delay = mean(arr_delay, na.rm = TRUE)) %>%
  arrange(desc(arr_delay))

What airline corresponds to the “F9” carrier code?

filter(airlines, carrier == "F9")

You can get part of the way to disentangling the effects of airports versus bad carriers by comparing the average delay of each carrier to the average delay of flights within a route (flights from the same origin to the same destination). Comparing delays between carriers and within each route disentangles the effect of carriers and airports. A better analysis would compare the average delay of a carrier’s flights to the average delay of all other carrier’s flights within a route.

flights %>%
  filter(!is.na(arr_delay)) %>%
  # Total delay by carrier within each origin, dest
  group_by(origin, dest, carrier) %>%
  summarise(
    arr_delay = sum(arr_delay),
    flights = n()
  ) %>%
  # Total delay within each origin dest
  group_by(origin, dest) %>%
  mutate(
    arr_delay_total = sum(arr_delay),
    flights_total = sum(flights)
  ) %>%
  # average delay of each carrier - average delay of other carriers
  ungroup() %>%
  mutate(
    arr_delay_others = (arr_delay_total - arr_delay) /
      (flights_total - flights),
    arr_delay_mean = arr_delay / flights,
    arr_delay_diff = arr_delay_mean - arr_delay_others
  ) %>%
  # remove NaN values (when there is only one carrier)
  filter(is.finite(arr_delay_diff)) %>%
  # average over all airports it flies to
  group_by(carrier) %>%
  summarise(arr_delay_diff = mean(arr_delay_diff)) %>%
  arrange(desc(arr_delay_diff))

There are more sophisticated ways to do this analysis, however comparing the delay of flights within each route goes a long ways toward disentangling airport and carrier effects. To see a more complete example of this analysis, see this FiveThirtyEight piece.

6. What does the sort argument to count() do? When might you use it?

The sort argument to count() sorts the results in order of n. You could use this anytime you would run count() followed by arrange().

LS0tDQp0aXRsZTogIkdyb3VwZWQgc3VtbWFyaWVzIHdpdGggc3VtbWFyaXNlKCkiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIGxvYWRsaWJyYXJ5fQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkobnljZmxpZ2h0czEzKSkNCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHRpZHl2ZXJzZSkpDQpgYGANCg0KIyMjIDEuIEJyYWluc3Rvcm0gYXQgbGVhc3QgNSBkaWZmZXJlbnQgd2F5cyB0byBhc3Nlc3MgdGhlIHR5cGljYWwgZGVsYXkgY2hhcmFjdGVyaXN0aWNzIG9mIGEgZ3JvdXAgb2YgZmxpZ2h0cy4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBzY2VuYXJpb3M6DQoNCiAtIEEgZmxpZ2h0IGlzIDE1IG1pbnV0ZXMgZWFybHkgNTAlIG9mIHRoZSB0aW1lLCBhbmQgMTUgbWludXRlcyBsYXRlIDUwJSBvZiB0aGUgdGltZS4NCiAtIEEgZmxpZ2h0IGlzIGFsd2F5cyAxMCBtaW51dGVzIGxhdGUuDQogLSBBIGZsaWdodCBpcyAzMCBtaW51dGVzIGVhcmx5IDUwJSBvZiB0aGUgdGltZSwgYW5kIDMwIG1pbnV0ZXMgbGF0ZSA1MCUgb2YgdGhlIHRpbWUuDQogLSA5OSUgb2YgdGhlIHRpbWUgYSBmbGlnaHQgaXMgb24gdGltZS4gMSUgb2YgdGhlIHRpbWUgaXTigJlzIDIgaG91cnMgbGF0ZS4NCiAtIFdoaWNoIGlzIG1vcmUgaW1wb3J0YW50OiBhcnJpdmFsIGRlbGF5IG9yIGRlcGFydHVyZSBkZWxheT8NCg0KV2hhdCB0aGlzIHF1ZXN0aW9uIGdldHMgYXQgaXMgYSBmdW5kYW1lbnRhbCBxdWVzdGlvbiBvZiBkYXRhIGFuYWx5c2lzOiB0aGUgY29zdCBmdW5jdGlvbi4gQXMgYW5hbHlzdHMsIHRoZSByZWFzb24gd2UgYXJlIGludGVyZXN0ZWQgaW4gZmxpZ2h0IGRlbGF5IGJlY2F1c2UgaXQgaXMgY29zdGx5IHRvIHBhc3NlbmdlcnMuIEJ1dCBpdCBpcyB3b3J0aCB0aGlua2luZyBjYXJlZnVsbHkgYWJvdXQgaG93IGl0IGlzIGNvc3RseSBhbmQgdXNlIHRoYXQgaW5mb3JtYXRpb24gaW4gcmFua2luZyBhbmQgbWVhc3VyaW5nIHRoZXNlIHNjZW5hcmlvcy4NCg0KSW4gbWFueSBzY2VuYXJpb3MsIGFycml2YWwgZGVsYXkgaXMgbW9yZSBpbXBvcnRhbnQuIEluIG1vc3QgY2FzZXMsIGFycml2aW5nIGxhdGUgaXMgbW9yZSBjb3N0bHkgdG8gdGhlIHBhc3NlbmdlciBzaW5jZSBpdCBjb3VsZCBkaXNydXB0IHRoZSBuZXh0IHN0YWdlcyBvZiB0aGVpciB0cmF2ZWwsIHN1Y2ggYXMgY29ubmVjdGluZyBmbGlnaHRzIG9yIHNjaGVkdWxlZCBtZWV0aW5ncy4NCg0KSWYgYSBkZXBhcnR1cmUgaXMgZGVsYXllZCB3aXRob3V0IGFmZmVjdGluZyB0aGUgYXJyaXZhbCB0aW1lLCB0aGlzIGRlbGF5IHdpbGwgbm90IGhhdmUgdGhvc2UgYWZmZWN0cyBwbGFucyBub3IgZG9lcyBpdCBhZmZlY3QgdGhlIHRvdGFsIHRpbWUgc3BlbnQgdHJhdmVsaW5nLiBUaGlzIGRlbGF5IGNvdWxkIGJlIGJlbmVmaWNpYWwsIGlmIGxlc3MgdGltZSBpcyBzcGVudCBpbiB0aGUgY3JhbXBlZCBjb25maW5lcyBvZiB0aGUgYWlycGxhbmUgaXRzZWxmLCBvciBhIG5lZ2F0aXZlLCBpZiB0aGF0IGRlbGF5ZWQgdGltZSBpcyBzdGlsbCBzcGVudCBpbiB0aGUgY3JhbXBlZCBjb25maW5lcyBvZiB0aGUgYWlycGxhbmUgb24gdGhlIHJ1bndheS4NCg0KVmFyaWF0aW9uIGluIGFycml2YWwgdGltZSBpcyB3b3JzZSB0aGFuIGNvbnNpc3RlbmN5LiBJZiBhIGZsaWdodCBpcyBhbHdheXMgMzAgbWludXRlcyBsYXRlIGFuZCB0aGF0IGRlbGF5IGlzIGtub3duLCB0aGVuIGl0IGlzIGFzIGlmIHRoZSBhcnJpdmFsIHRpbWUgaXMgdGhhdCBkZWxheWVkIHRpbWUuIFRoZSB0cmF2ZWxlciBjb3VsZCBlYXNpbHkgcGxhbiBmb3IgdGhpcy4gQnV0IGhpZ2hlciB2YXJpYXRpb24gaW4gZmxpZ2h0IHRpbWVzIG1ha2VzIGl0IGhhcmRlciB0byBwbGFuLg0KDQojIyMgMi4gQ29tZSB1cCB3aXRoIGFub3RoZXIgYXBwcm9hY2ggdGhhdCB3aWxsIGdpdmUgeW91IHRoZSBzYW1lIG91dHB1dCBhcyBgbm90X2NhbmNlbGxlZCAlPiUgY291bnQoZGVzdClgIGFuZCBgbm90X2NhbmNlbGxlZCAlPiUgY291bnQodGFpbG51bSwgd3QgPSBkaXN0YW5jZSlgICh3aXRob3V0IHVzaW5nIGBjb3VudCgpYCkuDQoNCmBgYHtyfQ0Kbm90X2NhbmNlbGxlZCA8LSBmbGlnaHRzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGRlcF9kZWxheSksICFpcy5uYShhcnJfZGVsYXkpKQ0KYGBgDQoNClRoZSBmaXJzdCBleHByZXNzaW9uIGlzIHRoZSBmb2xsb3dpbmcuDQoNCmBgYHtyfQ0Kbm90X2NhbmNlbGxlZCAlPiUNCiAgY291bnQoZGVzdCkNCmBgYA0KDQpUaGUgYGNvdW50KClgIGZ1bmN0aW9uIGNvdW50cyB0aGUgbnVtYmVyIG9mIGluc3RhbmNlcyB3aXRoaW4gZWFjaCBncm91cCBvZiB2YXJpYWJsZXMuIEluc3RlYWQgb2YgdXNpbmcgdGhlIGBjb3VudCgpYCBmdW5jdGlvbix3ZSBjYW4gY29tYmluZSB0aGUgYGdyb3VwX2J5KClgIGFuZCBgc3VtbWFyaXNlKClgIHZlcmJzLg0KDQpgYGB7cn0NCm5vdF9jYW5jZWxsZWQgJT4lDQogIGdyb3VwX2J5KGRlc3QpICU+JQ0KICBzdW1tYXJpc2UobiA9IGxlbmd0aChkZXN0KSkNCmBgYA0KDQpBbiBhbHRlcm5hdGl2ZSBtZXRob2QgZm9yIGdldHRpbmcgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gYSBkYXRhIGZyYW1lIGlzIHRoZSBmdW5jdGlvbiBgbigpYC4NCg0KYGBge3J9DQpub3RfY2FuY2VsbGVkICU+JQ0KICBncm91cF9ieShkZXN0KSAlPiUNCiAgc3VtbWFyaXNlKG4gPSBuKCkpDQpgYGANCg0KQW5vdGhlciBhbHRlcm5hdGl2ZSB0byBgY291bnQoKWAgaXMgdG8gdXNlIHRoZSBjb21iaW5hdGlvbiBvZiB0aGUgYGdyb3VwX2J5KClgIGFuZCBgdGFsbHkoKWAgdmVyYnMuIEluIGZhY3QsIGBjb3VudCgpYCBpcyBlZmZlY3RpdmVseSBhIHNob3J0LWN1dCBmb3IgYGdyb3VwX2J5KClgIGZvbGxvd2VkIGJ5IGB0YWxseSgpYC4NCg0KYGBge3J9DQpub3RfY2FuY2VsbGVkICU+JQ0KICBncm91cF9ieSh0YWlsbnVtKSAlPiUNCiAgdGFsbHkoKQ0KYGBgDQoNClRoZSBzZWNvbmQgZXhwcmVzc2lvbiBhbHNvIHVzZXMgdGhlIGBjb3VudCgpYCBmdW5jdGlvbiwgYnV0IGFkZHMgYSBgd3RgIGFyZ3VtZW50Lg0KDQpgYGB7cn0NCm5vdF9jYW5jZWxsZWQgJT4lDQogIGNvdW50KHRhaWxudW0sIHd0ID0gZGlzdGFuY2UpDQpgYGANCg0KQXMgYmVmb3JlLCB3ZSBjYW4gcmVwbGljYXRlIGBjb3VudCgpYCBieSBjb21iaW5pbmcgdGhlIGBncm91cF9ieSgpYCBhbmQgYHN1bW1hcmlzZSgpYCB2ZXJicy4gQnV0IHRoaXMgdGltZSBpbnN0ZWFkIG9mIHVzaW5nIGBsZW5ndGgoKWAsIHdlIHdpbGwgdXNlIGBzdW0oKWAgd2l0aCB0aGUgd2VpZ2h0aW5nIHZhcmlhYmxlLg0KDQpgYGB7cn0NCm5vdF9jYW5jZWxsZWQgJT4lDQogIGdyb3VwX2J5KHRhaWxudW0pICU+JQ0KICBzdW1tYXJpc2UobiA9IHN1bShkaXN0YW5jZSkpDQpgYGANCg0KTGlrZSB0aGUgcHJldmlvdXMgZXhhbXBsZSwgd2UgY2FuIGFsc28gdXNlIHRoZSBjb21iaW5hdGlvbiBgZ3JvdXBfYnkoKWAgYW5kIGB0YWxseSgpYC4gQW55IGFyZ3VtZW50cyB0byBgdGFsbHkoKWAgYXJlIHN1bW1lZC4NCg0KYGBge3J9DQpub3RfY2FuY2VsbGVkICU+JQ0KICBncm91cF9ieSh0YWlsbnVtKSAlPiUNCiAgdGFsbHkoZGlzdGFuY2UpDQpgYGANCg0KIyMjIEV4ZXJjaXNlIDMuIE91ciBkZWZpbml0aW9uIG9mIGNhbmNlbGxlZCBmbGlnaHRzIGAoaXMubmEoZGVwX2RlbGF5KSB8IGlzLm5hKGFycl9kZWxheSkpYCBpcyBzbGlnaHRseSBzdWJvcHRpbWFsLiBXaHk/IFdoaWNoIGlzIHRoZSBtb3N0IGltcG9ydGFudCBjb2x1bW4/DQoNCklmIGEgZmxpZ2h0IG5ldmVyIGRlcGFydHMsIHRoZW4gaXQgd29u4oCZdCBhcnJpdmUuIEEgZmxpZ2h0IGNvdWxkIGFsc28gZGVwYXJ0IGFuZCBub3QgYXJyaXZlIGlmIGl0IGNyYXNoZXMsIG9yIGlmIGl0IGlzIHJlZGlyZWN0ZWQgYW5kIGxhbmRzIGluIGFuIGFpcnBvcnQgb3RoZXIgdGhhbiBpdHMgaW50ZW5kZWQgZGVzdGluYXRpb24uIFNvIHRoZSBtb3N0IGltcG9ydGFudCBjb2x1bW4gaXMgYGFycl9kZWxheWAsIHdoaWNoIGluZGljYXRlcyB0aGUgYW1vdW50IG9mIGRlbGF5IGluIGFycml2YWwuDQoNCmBgYHtyfQ0KZmlsdGVyKGZsaWdodHMsICFpcy5uYShkZXBfZGVsYXkpLCBpcy5uYShhcnJfZGVsYXkpKSAlPiUNCiAgc2VsZWN0KGRlcF90aW1lLCBhcnJfdGltZSwgc2NoZWRfYXJyX3RpbWUsIGRlcF9kZWxheSwgYXJyX2RlbGF5KQ0KYGBgDQoNCk9rYXksIEnigJltIG5vdCBzdXJlIHdoYXTigJlzIGdvaW5nIG9uIGluIHRoaXMgZGF0YS4gYGRlcF90aW1lYCBjYW4gYmUgbm9uLW1pc3NpbmcgYW5kIGBhcnJfZGVsYXlgIG1pc3NpbmcgYnV0IGBhcnJfdGltZWAgbm90IG1pc3NpbmcuIFRoZXkgbWF5IGJlIGNvbWJpbmluZyBkaWZmZXJlbnQgZmxpZ2h0cz8NCg0KIyMjIDQuIExvb2sgYXQgdGhlIG51bWJlciBvZiBjYW5jZWxsZWQgZmxpZ2h0cyBwZXIgZGF5LiBJcyB0aGVyZSBhIHBhdHRlcm4/IElzIHRoZSBwcm9wb3J0aW9uIG9mIGNhbmNlbGxlZCBmbGlnaHRzIHJlbGF0ZWQgdG8gdGhlIGF2ZXJhZ2UgZGVsYXk/DQoNCk9uZSBwYXR0ZXJuIGluIGNhbmNlbGxlZCBmbGlnaHRzIHBlciBkYXkgaXMgdGhhdCB0aGUgbnVtYmVyIG9mIGNhbmNlbGxlZCBmbGlnaHRzIGluY3JlYXNlcyB3aXRoIHRoZSB0b3RhbCBudW1iZXIgb2YgZmxpZ2h0cyBwZXIgZGF5LiBUaGUgcHJvcG9ydGlvbiBvZiBjYW5jZWxsZWQgZmxpZ2h0cyBpbmNyZWFzZXMgd2l0aCB0aGUgYXZlcmFnZSBkZWxheSBvZiBmbGlnaHRzLg0KDQpUbyBhbnN3ZXIgdGhlc2UgcXVlc3Rpb25zLCBJJ2xsIHVzZSBgIShpcy5uYShhcnJfZGVsYXkpICYgaXMubmEoZGVwX2RlbGF5KSlgIGFzIHRoZSBkZWZpbml0aW9uIG9mIGNhbmNlbGxlZC4NCg0KVGhlIGZpcnN0IHBhcnQgb2YgdGhlIHF1ZXN0aW9uIGFza3MgZm9yIGFueSBwYXR0ZXJuIGluIHRoZSBudW1iZXIgb2YgY2FuY2VsbGVkIGZsaWdodHMgcGVyIGRheS4gSeKAmWxsIGxvb2sgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgY2FuY2VsbGVkIGZsaWdodHMgcGVyIGRheSBhbmQgdGhlIHRvdGFsIG51bWJlciBvZiBmbGlnaHRzIGluIGEgZGF5LiBUaGVyZSBzaG91bGQgYmUgYW4gaW5jcmVhc2luZyByZWxhdGlvbnNoaXAgZm9yIHR3byByZWFzb25zLiBGaXJzdCwgaWYgYWxsIGZsaWdodHMgYXJlIGVxdWFsbHkgbGlrZWx5IHRvIGJlIGNhbmNlbGxlZCwgdGhlbiBkYXlzIHdpdGggbW9yZSBmbGlnaHRzIHNob3VsZCBoYXZlIGEgaGlnaGVyIG51bWJlciBvZiBjYW5jZWxsYXRpb25zLiBTZWNvbmQsIGl0IGlzIGxpa2VseSB0aGF0IGRheXMgd2l0aCBtb3JlIGZsaWdodHMgd291bGQgaGF2ZSBhIGhpZ2hlciBwcm9iYWJpbGl0eSBvZiBjYW5jZWxsYXRpb25zIGJlY2F1c2UgY29uZ2VzdGlvbiBpdHNlbGYgY2FuIGNhdXNlIGRlbGF5cyBhbmQgYW55IGRlbGF5IHdvdWxkIGFmZmVjdCBtb3JlIGZsaWdodHMsIGFuZCBsYXJnZSBkZWxheXMgY2FuIGxlYWQgdG8gY2FuY2VsbGF0aW9ucy4NCg0KYGBge3J9DQpjYW5jZWxsZWRfcGVyX2RheSA8LQ0KICBmbGlnaHRzICU+JQ0KICBtdXRhdGUoY2FuY2VsbGVkID0gKGlzLm5hKGFycl9kZWxheSkgfCBpcy5uYShkZXBfZGVsYXkpKSkgJT4lDQogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBkYXkpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgY2FuY2VsbGVkX251bSA9IHN1bShjYW5jZWxsZWQpLA0KICAgIGZsaWdodHNfbnVtID0gbigpLA0KICApDQpgYGANCg0KUGxvdHRpbmcgYGZsaWdodHNfbnVtYCBhZ2FpbnN0IGBjYW5jZWxsZWRfbnVtYCBzaG93cyB0aGF0IHRoZSBudW1iZXIgb2YgZmxpZ2h0cyBjYW5jZWxsZWQgaW5jcmVhc2VzIHdpdGggdGhlIHRvdGFsIG51bWJlciBvZiBmbGlnaHRzLg0KDQpgYGB7cn0NCmdncGxvdChjYW5jZWxsZWRfcGVyX2RheSkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gZmxpZ2h0c19udW0sIHkgPSBjYW5jZWxsZWRfbnVtKSkNCmBgYA0KDQpUaGUgc2Vjb25kIHBhcnQgb2YgdGhlIHF1ZXN0aW9uIGFza3Mgd2hldGhlciB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBwcm9wb3J0aW9uIG9mIGZsaWdodHMgY2FuY2VsbGVkIGFuZCB0aGUgYXZlcmFnZSBkZXBhcnR1cmUgZGVsYXkuIEkgaW1wbGllZCB0aGlzIGluIG15IGFuc3dlciB0byB0aGUgZmlyc3QgcGFydCBvZiB0aGUgcXVlc3Rpb24sIHdoZW4gSSBub3RlZCB0aGF0IGluY3JlYXNpbmcgZGVsYXlzIGNvdWxkIHJlc3VsdCBpbiBpbmNyZWFzZWQgY2FuY2VsbGF0aW9ucy4gVGhlIHF1ZXN0aW9uIGRvZXMgbm90IHNwZWNpZnkgd2hpY2ggZGVsYXksIHNvIEkgd2lsbCBzaG93IHRoZSByZWxhdGlvbnNoaXAgZm9yIGJvdGguDQoNCmBgYHtyfQ0KY2FuY2VsbGVkX2FuZF9kZWxheXMgPC0NCiAgZmxpZ2h0cyAlPiUNCiAgbXV0YXRlKGNhbmNlbGxlZCA9IChpcy5uYShhcnJfZGVsYXkpIHwgaXMubmEoZGVwX2RlbGF5KSkpICU+JQ0KICBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5KSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGNhbmNlbGxlZF9wcm9wID0gbWVhbihjYW5jZWxsZWQpLA0KICAgIGF2Z19kZXBfZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKSwNCiAgICBhdmdfYXJyX2RlbGF5ID0gbWVhbihhcnJfZGVsYXksIG5hLnJtID0gVFJVRSkNCiAgKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANClRoZXJlIGlzIGEgc3Ryb25nIGluY3JlYXNpbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gYm90aCBhdmVyYWdlIGRlcGFydHVyZSBkZWxheSBhbmQgYW5kIGF2ZXJhZ2UgYXJyaXZhbCBkZWxheSBhbmQgdGhlIHByb3BvcnRpb24gb2YgY2FuY2VsbGVkIGZsaWdodHMuDQoNCmBgYHtyfQ0KZ2dwbG90KGNhbmNlbGxlZF9hbmRfZGVsYXlzKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBhdmdfZGVwX2RlbGF5LCB5ID0gY2FuY2VsbGVkX3Byb3ApKQ0KDQpnZ3Bsb3QoY2FuY2VsbGVkX2FuZF9kZWxheXMpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGF2Z19hcnJfZGVsYXksIHkgPSBjYW5jZWxsZWRfcHJvcCkpDQpgYGANCg0KIyMjIDUuIFdoaWNoIGNhcnJpZXIgaGFzIHRoZSB3b3JzdCBkZWxheXM/IENoYWxsZW5nZTogY2FuIHlvdSBkaXNlbnRhbmdsZSB0aGUgZWZmZWN0cyBvZiBiYWQgYWlycG9ydHMgdnMuIGJhZCBjYXJyaWVycz8gV2h5L3doeSBub3Q/IChIaW50OiB0aGluayBhYm91dCBgZmxpZ2h0cyAlPiUgZ3JvdXBfYnkoY2FycmllciwgZGVzdCkgJT4lIHN1bW1hcmlzZShuKCkpYCkNCg0KYGBge3J9DQpmbGlnaHRzICU+JQ0KICBncm91cF9ieShjYXJyaWVyKSAlPiUNCiAgc3VtbWFyaXNlKGFycl9kZWxheSA9IG1lYW4oYXJyX2RlbGF5LCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGFycl9kZWxheSkpDQpgYGANCg0KV2hhdCBhaXJsaW5lIGNvcnJlc3BvbmRzIHRvIHRoZSAiRjkiIGNhcnJpZXIgY29kZT8NCg0KYGBge3J9DQpmaWx0ZXIoYWlybGluZXMsIGNhcnJpZXIgPT0gIkY5IikNCmBgYA0KDQpZb3UgY2FuIGdldCBwYXJ0IG9mIHRoZSB3YXkgdG8gZGlzZW50YW5nbGluZyB0aGUgZWZmZWN0cyBvZiBhaXJwb3J0cyB2ZXJzdXMgYmFkIGNhcnJpZXJzIGJ5IGNvbXBhcmluZyB0aGUgYXZlcmFnZSBkZWxheSBvZiBlYWNoIGNhcnJpZXIgdG8gdGhlIGF2ZXJhZ2UgZGVsYXkgb2YgZmxpZ2h0cyB3aXRoaW4gYSByb3V0ZSAoZmxpZ2h0cyBmcm9tIHRoZSBzYW1lIG9yaWdpbiB0byB0aGUgc2FtZSBkZXN0aW5hdGlvbikuIENvbXBhcmluZyBkZWxheXMgYmV0d2VlbiBjYXJyaWVycyBhbmQgd2l0aGluIGVhY2ggcm91dGUgZGlzZW50YW5nbGVzIHRoZSBlZmZlY3Qgb2YgY2FycmllcnMgYW5kIGFpcnBvcnRzLiBBIGJldHRlciBhbmFseXNpcyB3b3VsZCBjb21wYXJlIHRoZSBhdmVyYWdlIGRlbGF5IG9mIGEgY2FycmllcuKAmXMgZmxpZ2h0cyB0byB0aGUgYXZlcmFnZSBkZWxheSBvZiBhbGwgb3RoZXIgY2FycmllcuKAmXMgZmxpZ2h0cyB3aXRoaW4gYSByb3V0ZS4NCg0KYGBge3J9DQpmbGlnaHRzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGFycl9kZWxheSkpICU+JQ0KICAjIFRvdGFsIGRlbGF5IGJ5IGNhcnJpZXIgd2l0aGluIGVhY2ggb3JpZ2luLCBkZXN0DQogIGdyb3VwX2J5KG9yaWdpbiwgZGVzdCwgY2FycmllcikgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBhcnJfZGVsYXkgPSBzdW0oYXJyX2RlbGF5KSwNCiAgICBmbGlnaHRzID0gbigpDQogICkgJT4lDQogICMgVG90YWwgZGVsYXkgd2l0aGluIGVhY2ggb3JpZ2luIGRlc3QNCiAgZ3JvdXBfYnkob3JpZ2luLCBkZXN0KSAlPiUNCiAgbXV0YXRlKA0KICAgIGFycl9kZWxheV90b3RhbCA9IHN1bShhcnJfZGVsYXkpLA0KICAgIGZsaWdodHNfdG90YWwgPSBzdW0oZmxpZ2h0cykNCiAgKSAlPiUNCiAgIyBhdmVyYWdlIGRlbGF5IG9mIGVhY2ggY2FycmllciAtIGF2ZXJhZ2UgZGVsYXkgb2Ygb3RoZXIgY2FycmllcnMNCiAgdW5ncm91cCgpICU+JQ0KICBtdXRhdGUoDQogICAgYXJyX2RlbGF5X290aGVycyA9IChhcnJfZGVsYXlfdG90YWwgLSBhcnJfZGVsYXkpIC8NCiAgICAgIChmbGlnaHRzX3RvdGFsIC0gZmxpZ2h0cyksDQogICAgYXJyX2RlbGF5X21lYW4gPSBhcnJfZGVsYXkgLyBmbGlnaHRzLA0KICAgIGFycl9kZWxheV9kaWZmID0gYXJyX2RlbGF5X21lYW4gLSBhcnJfZGVsYXlfb3RoZXJzDQogICkgJT4lDQogICMgcmVtb3ZlIE5hTiB2YWx1ZXMgKHdoZW4gdGhlcmUgaXMgb25seSBvbmUgY2FycmllcikNCiAgZmlsdGVyKGlzLmZpbml0ZShhcnJfZGVsYXlfZGlmZikpICU+JQ0KICAjIGF2ZXJhZ2Ugb3ZlciBhbGwgYWlycG9ydHMgaXQgZmxpZXMgdG8NCiAgZ3JvdXBfYnkoY2FycmllcikgJT4lDQogIHN1bW1hcmlzZShhcnJfZGVsYXlfZGlmZiA9IG1lYW4oYXJyX2RlbGF5X2RpZmYpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGFycl9kZWxheV9kaWZmKSkNCmBgYA0KDQpUaGVyZSBhcmUgbW9yZSBzb3BoaXN0aWNhdGVkIHdheXMgdG8gZG8gdGhpcyBhbmFseXNpcywgaG93ZXZlciBjb21wYXJpbmcgdGhlIGRlbGF5IG9mIGZsaWdodHMgd2l0aGluIGVhY2ggcm91dGUgZ29lcyBhIGxvbmcgd2F5cyB0b3dhcmQgZGlzZW50YW5nbGluZyBhaXJwb3J0IGFuZCBjYXJyaWVyIGVmZmVjdHMuIFRvIHNlZSBhIG1vcmUgY29tcGxldGUgZXhhbXBsZSBvZiB0aGlzIGFuYWx5c2lzLCBzZWUgW3RoaXMgRml2ZVRoaXJ0eUVpZ2h0IHBpZWNlXShodHRwczovL2ZpdmV0aGlydHllaWdodC5jb20vZmVhdHVyZXMvdGhlLWJlc3QtYW5kLXdvcnN0LWFpcmxpbmVzLWFpcnBvcnRzLWFuZC1mbGlnaHRzLXN1bW1lci0yMDE1LXVwZGF0ZS8pLg0KDQojIyMgNi4gV2hhdCBkb2VzIHRoZSBzb3J0IGFyZ3VtZW50IHRvIGBjb3VudCgpYCBkbz8gV2hlbiBtaWdodCB5b3UgdXNlIGl0Pw0KDQpUaGUgc29ydCBhcmd1bWVudCB0byBgY291bnQoKWAgc29ydHMgdGhlIHJlc3VsdHMgaW4gb3JkZXIgb2Ygbi4gWW91IGNvdWxkIHVzZSB0aGlzIGFueXRpbWUgeW91IHdvdWxkIHJ1biBgY291bnQoKWAgZm9sbG93ZWQgYnkgYGFycmFuZ2UoKWAu