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. Currently dep_time and sched_dep_time are convenient to look at, but hard to compute with because they’re not really continuous numbers. Convert them to a more convenient representation of number of minutes since midnight.

To get the departure times in the number of minutes, divide dep_time by 100 to get the hours since midnight and multiply by 60 and add the remainder of dep_time divided by 100. For example, 1504 represents 15:04 (or 3:04 PM), which is 904 minutes after midnight. To generalize this approach, we need a way to split out the hour-digits from the minute-digits. Dividing by 100 and discarding the remainder using the integer division operator, %/% gives us the following.

1504 %/% 100
[1] 15

Instead of %/% could also use / along with trunc() or floor(), but round() would not work. To get the minutes, instead of discarding the remainder of the division by 100, we only want the remainder. So we use the modulo operator, %%.

1504 %% 100
[1] 4

Now, we can combine the hours (multiplied by 60 to convert them to minutes) and minutes to get the number of minutes after midnight.

1504 %/% 100 * 60 + 1504 %% 100
[1] 904

There is one remaining issue. Midnight is represented by 2400, which would correspond to 1440 minutes since midnight, but it should correspond to 0. After converting all the times to minutes after midnight, x %% 1440 will convert 1440 to zero while keeping all the other times the same.

Now we will put it all together. The following code creates a new data frame flights_times with columns dep_time_mins and sched_dep_time_mins. These columns convert dep_time and sched_dep_time, respectively, to minutes since midnight.

flights_times <- mutate(flights,
  dep_time_mins = (dep_time %/% 100 * 60 + dep_time %% 100) %% 1440,
  sched_dep_time_mins = (sched_dep_time %/% 100 * 60 +
    sched_dep_time %% 100) %% 1440
)
# view only relevant columns
select(
  flights_times, dep_time, dep_time_mins, sched_dep_time,
  sched_dep_time_mins
)

When we introduce functions, you’ll understand this is precisely the sort of situation where writing a function will allow us to avoid copying and pasting code. We could define a function time2mins(), which converts a vector of times in from the format used in flights to minutes since midnight.

time2mins <- function(x) {
  (x %/% 100 * 60 + x %% 100) %% 1440
}

Using time2mins, the previous code simplifies to the following.

flights_times <- mutate(flights,
  dep_time_mins = time2mins(dep_time),
  sched_dep_time_mins = time2mins(sched_dep_time)
)
# show only the relevant columns
select(
  flights_times, dep_time, dep_time_mins, sched_dep_time,
  sched_dep_time_mins
)

2. Compare air_time with arr_time - dep_time. What do you expect to see? What do you see? What do you need to do to fix it?

I expect that air_time is the difference between the arrival (arr_time) and departure times (dep_time). In other words, air_time = arr_time - dep_time.

To check that this relationship, I’ll first need to convert the times to a form more amenable to arithmetic operations using the same calculations as the previous exercise.

flights_airtime <-
  mutate(flights,
    dep_time = (dep_time %/% 100 * 60 + dep_time %% 100) %% 1440,
    arr_time = (arr_time %/% 100 * 60 + arr_time %% 100) %% 1440,
    air_time_diff = air_time - arr_time + dep_time
  )

So, does air_time = arr_time - dep_time? If so, there should be no flights with non-zero values of air_time_diff.

nrow(filter(flights_airtime, air_time_diff != 0))
[1] 327150

It turns out that there are many flights for which air_time != arr_time - dep_time. Other than data errors, I can think of two reasons why air_time would not equal arr_time - dep_time.

  1. The flight passes midnight, so arr_time < dep_time. In these cases, the difference in airtime should be by 24 hours (1,440 minutes).

  2. The flight crosses time zones, and the total air time will be off by hours (multiples of 60). All flights in flights departed from New York City and are domestic flights in the US. This means that flights will all be to the same or more westerly time zones. Given the time-zones in the US, the differences due to time-zone should be 60 minutes (Central) 120 minutes (Mountain), 180 minutes (Pacific), 240 minutes (Alaska), or 300 minutes (Hawaii).

Both of these explanations have clear patterns that I would expect to see if they were true. In particular, in both cases, since time-zones and crossing midnight only affects the hour part of the time, all values of air_time_diff should be divisible by 60. I’ll visually check this hypothesis by plotting the distribution of air_time_diff. If those two explanations are correct, distribution of air_time_diff should comprise only spikes at multiples of 60.

ggplot(flights_airtime, aes(x = air_time_diff)) +
  geom_histogram(binwidth = 1)

This is not the case. While, the distribution of air_time_diff has modes at multiples of 60 as hypothesized, it shows that there are many flights in which the difference between air time and local arrival and departure times is not divisible by 60.

Let’s also look at flights with Los Angeles as a destination. The discrepancy should be 180 minutes.

ggplot(filter(flights_airtime, dest == "LAX"), aes(x = air_time_diff)) +
  geom_histogram(binwidth = 1)

To fix these time-zone issues, I would want to convert all the times to a date-time to handle overnight flights, and from local time to a common time zone, most likely UTC, to handle flights crossing time-zones. The tzone column of nycflights13::airports gives the time-zone of each airport. We’ll talk a lot more about dates and times later.

But that still leaves the other differences unexplained. So what else might be going on? There seem to be too many problems for this to be data entry problems, so I’m probably missing something. So, I’ll reread the documentation to make sure that I understand the definitions of arr_time, dep_time, and air_time. The documentation contains a link to the source of the flights data, https://www.transtats.bts.gov/DL_SelectFields.asp?Table_ID=236. This documentation shows that the flights data does not contain the variables TaxiIn, TaxiOff, WheelsIn, and WheelsOff. It appears that the air_time variable refers to flight time, which is defined as the time between wheels-off (take-off) and wheels-in (landing). But the flight time does not include time spent on the runway taxiing to and from gates. With this new understanding of the data, I now know that the relationship between air_time, arr_time, and dep_time is air_time <= arr_time - dep_time, supposing that the time zones of arr_time and dep_time are in the same time zone.

4 Find the 10 most delayed flights using a ranking function. How do you want to handle ties? Carefully read the documentation for min_rank().

The dplyr package provides multiple functions for ranking, which differ in how they handle tied values: row_number(), min_rank(), dense_rank(). To see how they work, let’s create a data frame with duplicate values in a vector and see how ranking functions handle ties.

rankme <- tibble(
  x = c(10, 5, 1, 5, 5)
)
rankme <- mutate(rankme,
  x_row_number = row_number(x),
  x_min_rank = min_rank(x),
  x_dense_rank = dense_rank(x)
)
arrange(rankme, x)

The function row_number() assigns each element a unique value. The result is equivalent to the index (or row) number of each element after sorting the vector, hence its name.

The min_rank() and dense_rank() assign tied values the same rank, but differ in how they assign values to the next rank. For each set of tied values the min_rank() function assigns a rank equal to the number of values less than that tied value plus one. In contrast, the dense_rank() function assigns a rank equal to the number of distinct values less than that tied value plus one. To see the difference between dense_rank() and min_rank() compare the value of rankme$x_min_rank and rankme$x_dense_rank for x = 10.

If I had to choose one for presenting rankings to someone else, I would use min_rank() since its results correspond to the most common usage of rankings in sports or other competitions. In the code below, I use all three functions, but since there are no ties in the top 10 flights, the results don’t differ.

flights_delayed <- mutate(flights,
  dep_delay_min_rank = min_rank(desc(dep_delay)),
  dep_delay_row_number = row_number(desc(dep_delay)),
  dep_delay_dense_rank = dense_rank(desc(dep_delay))
)
flights_delayed <- filter(
  flights_delayed,
  !(dep_delay_min_rank > 10 | dep_delay_row_number > 10 |
    dep_delay_dense_rank > 10)
)
flights_delayed <- arrange(flights_delayed, dep_delay_min_rank)
print(select(
  flights_delayed, month, day, carrier, flight, dep_delay,
  dep_delay_min_rank, dep_delay_row_number, dep_delay_dense_rank
),
n = Inf
)

In addition to the functions covered here, the rank() function provides several more ways of ranking elements.

5 What does 1:3 + 1:10 return? Why?

The code given in the question returns the following.

1:3 + 1:10
longer object length is not a multiple of shorter object length
 [1]  2  4  6  5  7  9  8 10 12 11

This is equivalent to the following.

c(1 + 1, 2 + 2, 3 + 3, 1 + 4, 2 + 5, 3 + 6, 1 + 7, 2 + 8, 3 + 9, 1 + 10)
 [1]  2  4  6  5  7  9  8 10 12 11

When adding two vectors recycles the shorter vector’s values to get vectors of the same length.

The code also produces a warning that the shorter vector is not a multiple of the longer vector. A warning is provided since often, but not always, this indicates a bug in the code.

6 What trigonometric functions does R provide?

All trigonometric functions are all described in a single help page, named Trig. You can open the documentation for these functions with ?Trig or by using ? with any of the following functions, for example: ?sin.

R provides functions for the three primary trigonometric functions: sine (sin()), cosine (cos()), and tangent (tan()). The input angles to all these functions are in radians.

x <- seq(-3, 7, by = 1 / 2)
sin(pi * x)
 [1] -3.673819e-16 -1.000000e+00  2.449213e-16  1.000000e+00 -1.224606e-16
 [6] -1.000000e+00  0.000000e+00  1.000000e+00  1.224606e-16 -1.000000e+00
[11] -2.449213e-16  1.000000e+00  3.673819e-16 -1.000000e+00 -4.898425e-16
[16]  1.000000e+00  6.123032e-16 -1.000000e+00 -7.347638e-16  1.000000e+00
[21]  8.572244e-16
cos(pi * x)
 [1] -1.000000e+00  3.061516e-16  1.000000e+00 -1.836910e-16 -1.000000e+00
 [6]  6.123032e-17  1.000000e+00  6.123032e-17 -1.000000e+00 -1.836910e-16
[11]  1.000000e+00  3.061516e-16 -1.000000e+00 -4.286122e-16  1.000000e+00
[16]  5.510729e-16 -1.000000e+00 -2.449890e-15  1.000000e+00 -9.803627e-16
[21] -1.000000e+00
tan(pi * x)
 [1]  3.673940e-16 -3.266248e+15  2.449294e-16 -5.443746e+15  1.224647e-16
 [6] -1.633124e+16  0.000000e+00  1.633124e+16 -1.224647e-16  5.443746e+15
[11] -2.449294e-16  3.266248e+15 -3.673940e-16  2.333034e+15 -4.898587e-16
[16]  1.814582e+15 -6.123234e-16  4.081778e+14 -7.347881e-16 -1.020058e+15
[21] -8.572528e-16

In the previous code, I used the variable pi. R provides the variable pi which is set to the value of the mathematical constant \(\pi\)

pi
[1] 3.141593

Although R provides the pi variable, there is nothing preventing a user from changing its value For example, I could redefine pi to 3.14 or any other value.

pi <- 3.14
pi
[1] 3.14
pi <- "Apple"
pi
[1] "Apple"

For that reason, if you are using the builtin pi variable in computations and are paranoid, you may want to always reference it as base::pi.

base::pi
[1] 3.141593

In the previous code block, since the angles were in radians, I wrote them as \(\pi\) times some number. Since it is often easier to write radians multiple of \(\pi\), R provides some convenience functions that do that. The function sinpi(x), is equivalent to sin(pi * x). The functions cospi() and tanpi() are similarly defined for the sin and tan functions, respectively.

sinpi(x)
 [1]  0 -1  0  1  0 -1  0  1  0 -1  0  1  0 -1  0  1  0 -1  0  1  0
cospi(x)
 [1] -1  0  1  0 -1  0  1  0 -1  0  1  0 -1  0  1  0 -1  0  1  0 -1
tanpi(x)
NaNs produced
 [1]   0 NaN   0 NaN   0 NaN   0 NaN   0 NaN   0 NaN   0 NaN   0 NaN   0
[18] NaN   0 NaN   0

R provides the function arc-cosine (acos()), arc-sine (asin()), and arc-tangent (atan()).

x <- seq(-1, 1, by = 1 / 4)
acos(x)
[1] 3.1415927 2.4188584 2.0943951 1.8234766 1.5707963 1.3181161 1.0471976
[8] 0.7227342 0.0000000
asin(x)
[1] -1.5707963 -0.8480621 -0.5235988 -0.2526803  0.0000000  0.2526803
[7]  0.5235988  0.8480621  1.5707963
atan(x)
[1] -0.7853982 -0.6435011 -0.4636476 -0.2449787  0.0000000  0.2449787
[7]  0.4636476  0.6435011  0.7853982

Finally, R provides the function atan2(). Calling atan2(y, x) returns the angle between the x-axis and the vector from (0,0) to (x, y).

atan2(c(1, 0, -1, 0), c(0, 1, 0, -1))
[1]  1.570796  0.000000 -1.570796  3.141593
LS0tDQp0aXRsZTogIkFkZCBuZXcgdmFyaWFibGVzIHdpdGggbXV0YXRlKCkiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIGxvYWRsaWJyYXJ5fQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkobnljZmxpZ2h0czEzKSkNCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHRpZHl2ZXJzZSkpDQpgYGANCg0KIyMjIDEuIEN1cnJlbnRseSBgZGVwX3RpbWVgIGFuZCBgc2NoZWRfZGVwX3RpbWVgIGFyZSBjb252ZW5pZW50IHRvIGxvb2sgYXQsIGJ1dCBoYXJkIHRvIGNvbXB1dGUgd2l0aCBiZWNhdXNlIHRoZXnigJlyZSBub3QgcmVhbGx5IGNvbnRpbnVvdXMgbnVtYmVycy4gQ29udmVydCB0aGVtIHRvIGEgbW9yZSBjb252ZW5pZW50IHJlcHJlc2VudGF0aW9uIG9mIG51bWJlciBvZiBtaW51dGVzIHNpbmNlIG1pZG5pZ2h0Lg0KDQpUbyBnZXQgdGhlIGRlcGFydHVyZSB0aW1lcyBpbiB0aGUgbnVtYmVyIG9mIG1pbnV0ZXMsIGRpdmlkZSBgZGVwX3RpbWVgIGJ5IDEwMCB0byBnZXQgdGhlIGhvdXJzIHNpbmNlIG1pZG5pZ2h0IGFuZCBtdWx0aXBseSBieSA2MCBhbmQgYWRkIHRoZSByZW1haW5kZXIgb2YgYGRlcF90aW1lYCBkaXZpZGVkIGJ5IDEwMC4gRm9yIGV4YW1wbGUsIDE1MDQgcmVwcmVzZW50cyAxNTowNCAob3IgMzowNCBQTSksIHdoaWNoIGlzIDkwNCBtaW51dGVzIGFmdGVyIG1pZG5pZ2h0LiBUbyBnZW5lcmFsaXplIHRoaXMgYXBwcm9hY2gsIHdlIG5lZWQgYSB3YXkgdG8gc3BsaXQgb3V0IHRoZSBob3VyLWRpZ2l0cyBmcm9tIHRoZSBtaW51dGUtZGlnaXRzLiBEaXZpZGluZyBieSAxMDAgYW5kIGRpc2NhcmRpbmcgdGhlIHJlbWFpbmRlciB1c2luZyB0aGUgaW50ZWdlciBkaXZpc2lvbiBvcGVyYXRvciwgYCUvJWAgZ2l2ZXMgdXMgdGhlIGZvbGxvd2luZy4NCg0KYGBge3J9DQoxNTA0ICUvJSAxMDANCmBgYA0KDQpJbnN0ZWFkIG9mIGAlLyVgIGNvdWxkIGFsc28gdXNlIGAvYCBhbG9uZyB3aXRoIGB0cnVuYygpYCBvciBgZmxvb3IoKWAsIGJ1dCBgcm91bmQoKWAgd291bGQgbm90IHdvcmsuIFRvIGdldCB0aGUgbWludXRlcywgaW5zdGVhZCBvZiBkaXNjYXJkaW5nIHRoZSByZW1haW5kZXIgb2YgdGhlIGRpdmlzaW9uIGJ5IDEwMCwgd2Ugb25seSB3YW50IHRoZSByZW1haW5kZXIuIFNvIHdlIHVzZSB0aGUgbW9kdWxvIG9wZXJhdG9yLCBgJSVgLg0KDQpgYGB7cn0NCjE1MDQgJSUgMTAwDQpgYGANCg0KTm93LCB3ZSBjYW4gY29tYmluZSB0aGUgaG91cnMgKG11bHRpcGxpZWQgYnkgNjAgdG8gY29udmVydCB0aGVtIHRvIG1pbnV0ZXMpIGFuZCBtaW51dGVzIHRvIGdldCB0aGUgbnVtYmVyIG9mIG1pbnV0ZXMgYWZ0ZXIgbWlkbmlnaHQuDQoNCmBgYHtyfQ0KMTUwNCAlLyUgMTAwICogNjAgKyAxNTA0ICUlIDEwMA0KYGBgDQoNClRoZXJlIGlzIG9uZSByZW1haW5pbmcgaXNzdWUuIE1pZG5pZ2h0IGlzIHJlcHJlc2VudGVkIGJ5IGAyNDAwYCwgd2hpY2ggd291bGQgY29ycmVzcG9uZCB0byBgMTQ0MGAgbWludXRlcyBzaW5jZSBtaWRuaWdodCwgYnV0IGl0IHNob3VsZCBjb3JyZXNwb25kIHRvIGAwYC4gQWZ0ZXIgY29udmVydGluZyBhbGwgdGhlIHRpbWVzIHRvIG1pbnV0ZXMgYWZ0ZXIgbWlkbmlnaHQsIGB4ICUlIDE0NDBgIHdpbGwgY29udmVydCBgMTQ0MGAgdG8gemVybyB3aGlsZSBrZWVwaW5nIGFsbCB0aGUgb3RoZXIgdGltZXMgdGhlIHNhbWUuDQoNCk5vdyB3ZSB3aWxsIHB1dCBpdCBhbGwgdG9nZXRoZXIuIFRoZSBmb2xsb3dpbmcgY29kZSBjcmVhdGVzIGEgbmV3IGRhdGEgZnJhbWUgZmxpZ2h0c190aW1lcyB3aXRoIGNvbHVtbnMgZGVwX3RpbWVfbWlucyBhbmQgc2NoZWRfZGVwX3RpbWVfbWlucy4gVGhlc2UgY29sdW1ucyBjb252ZXJ0IGRlcF90aW1lIGFuZCBzY2hlZF9kZXBfdGltZSwgcmVzcGVjdGl2ZWx5LCB0byBtaW51dGVzIHNpbmNlIG1pZG5pZ2h0Lg0KDQpgYGB7cn0NCmZsaWdodHNfdGltZXMgPC0gbXV0YXRlKGZsaWdodHMsDQogIGRlcF90aW1lX21pbnMgPSAoZGVwX3RpbWUgJS8lIDEwMCAqIDYwICsgZGVwX3RpbWUgJSUgMTAwKSAlJSAxNDQwLA0KICBzY2hlZF9kZXBfdGltZV9taW5zID0gKHNjaGVkX2RlcF90aW1lICUvJSAxMDAgKiA2MCArDQogICAgc2NoZWRfZGVwX3RpbWUgJSUgMTAwKSAlJSAxNDQwDQopDQojIHZpZXcgb25seSByZWxldmFudCBjb2x1bW5zDQpzZWxlY3QoDQogIGZsaWdodHNfdGltZXMsIGRlcF90aW1lLCBkZXBfdGltZV9taW5zLCBzY2hlZF9kZXBfdGltZSwNCiAgc2NoZWRfZGVwX3RpbWVfbWlucw0KKQ0KYGBgDQoNCldoZW4gd2UgaW50cm9kdWNlIGZ1bmN0aW9ucywgeW91J2xsIHVuZGVyc3RhbmQgdGhpcyBpcyBwcmVjaXNlbHkgdGhlIHNvcnQgb2Ygc2l0dWF0aW9uIHdoZXJlIHdyaXRpbmcgYSBmdW5jdGlvbiB3aWxsIGFsbG93IHVzIHRvIGF2b2lkIGNvcHlpbmcgYW5kIHBhc3RpbmcgY29kZS4gV2UgY291bGQgZGVmaW5lIGEgZnVuY3Rpb24gdGltZTJtaW5zKCksIHdoaWNoIGNvbnZlcnRzIGEgdmVjdG9yIG9mIHRpbWVzIGluIGZyb20gdGhlIGZvcm1hdCB1c2VkIGluIGZsaWdodHMgdG8gbWludXRlcyBzaW5jZSBtaWRuaWdodC4NCg0KYGBge3J9DQp0aW1lMm1pbnMgPC0gZnVuY3Rpb24oeCkgew0KICAoeCAlLyUgMTAwICogNjAgKyB4ICUlIDEwMCkgJSUgMTQ0MA0KfQ0KYGBgDQoNClVzaW5nIHRpbWUybWlucywgdGhlIHByZXZpb3VzIGNvZGUgc2ltcGxpZmllcyB0byB0aGUgZm9sbG93aW5nLg0KDQpgYGB7cn0NCmZsaWdodHNfdGltZXMgPC0gbXV0YXRlKGZsaWdodHMsDQogIGRlcF90aW1lX21pbnMgPSB0aW1lMm1pbnMoZGVwX3RpbWUpLA0KICBzY2hlZF9kZXBfdGltZV9taW5zID0gdGltZTJtaW5zKHNjaGVkX2RlcF90aW1lKQ0KKQ0KIyBzaG93IG9ubHkgdGhlIHJlbGV2YW50IGNvbHVtbnMNCnNlbGVjdCgNCiAgZmxpZ2h0c190aW1lcywgZGVwX3RpbWUsIGRlcF90aW1lX21pbnMsIHNjaGVkX2RlcF90aW1lLA0KICBzY2hlZF9kZXBfdGltZV9taW5zDQopDQpgYGANCg0KIyMjIDIuIENvbXBhcmUgYGFpcl90aW1lYCB3aXRoIGBhcnJfdGltZSAtIGRlcF90aW1lYC4gV2hhdCBkbyB5b3UgZXhwZWN0IHRvIHNlZT8gV2hhdCBkbyB5b3Ugc2VlPyBXaGF0IGRvIHlvdSBuZWVkIHRvIGRvIHRvIGZpeCBpdD8NCg0KSSBleHBlY3QgdGhhdCBgYWlyX3RpbWVgIGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGFycml2YWwgKGBhcnJfdGltZWApIGFuZCBkZXBhcnR1cmUgdGltZXMgKGBkZXBfdGltZWApLiBJbiBvdGhlciB3b3JkcywgYGFpcl90aW1lID0gYXJyX3RpbWUgLSBkZXBfdGltZWAuDQoNClRvIGNoZWNrIHRoYXQgdGhpcyByZWxhdGlvbnNoaXAsIEnigJlsbCBmaXJzdCBuZWVkIHRvIGNvbnZlcnQgdGhlIHRpbWVzIHRvIGEgZm9ybSBtb3JlIGFtZW5hYmxlIHRvIGFyaXRobWV0aWMgb3BlcmF0aW9ucyB1c2luZyB0aGUgc2FtZSBjYWxjdWxhdGlvbnMgYXMgdGhlIHByZXZpb3VzIGV4ZXJjaXNlLg0KDQpgYGB7cn0NCmZsaWdodHNfYWlydGltZSA8LQ0KICBtdXRhdGUoZmxpZ2h0cywNCiAgICBkZXBfdGltZSA9IChkZXBfdGltZSAlLyUgMTAwICogNjAgKyBkZXBfdGltZSAlJSAxMDApICUlIDE0NDAsDQogICAgYXJyX3RpbWUgPSAoYXJyX3RpbWUgJS8lIDEwMCAqIDYwICsgYXJyX3RpbWUgJSUgMTAwKSAlJSAxNDQwLA0KICAgIGFpcl90aW1lX2RpZmYgPSBhaXJfdGltZSAtIGFycl90aW1lICsgZGVwX3RpbWUNCiAgKQ0KYGBgDQoNClNvLCBkb2VzIGBhaXJfdGltZSA9IGFycl90aW1lIC0gZGVwX3RpbWVgPyBJZiBzbywgdGhlcmUgc2hvdWxkIGJlIG5vIGZsaWdodHMgd2l0aCBub24temVybyB2YWx1ZXMgb2YgYGFpcl90aW1lX2RpZmZgLg0KDQpgYGB7cn0NCm5yb3coZmlsdGVyKGZsaWdodHNfYWlydGltZSwgYWlyX3RpbWVfZGlmZiAhPSAwKSkNCmBgYA0KDQpJdCB0dXJucyBvdXQgdGhhdCB0aGVyZSBhcmUgbWFueSBmbGlnaHRzIGZvciB3aGljaCBgYWlyX3RpbWUgIT0gYXJyX3RpbWUgLSBkZXBfdGltZWAuIE90aGVyIHRoYW4gZGF0YSBlcnJvcnMsIEkgY2FuIHRoaW5rIG9mIHR3byByZWFzb25zIHdoeSBgYWlyX3RpbWVgIHdvdWxkIG5vdCBlcXVhbCBgYXJyX3RpbWUgLSBkZXBfdGltZWAuDQoNCjEuIFRoZSBmbGlnaHQgcGFzc2VzIG1pZG5pZ2h0LCBzbyBgYXJyX3RpbWUgPCBkZXBfdGltZWAuIEluIHRoZXNlIGNhc2VzLCB0aGUgZGlmZmVyZW5jZSBpbiBhaXJ0aW1lIHNob3VsZCBiZSBieSAyNCBob3VycyAoMSw0NDAgbWludXRlcykuDQoNCjIuIFRoZSBmbGlnaHQgY3Jvc3NlcyB0aW1lIHpvbmVzLCBhbmQgdGhlIHRvdGFsIGFpciB0aW1lIHdpbGwgYmUgb2ZmIGJ5IGhvdXJzIChtdWx0aXBsZXMgb2YgNjApLiBBbGwgZmxpZ2h0cyBpbiBgZmxpZ2h0c2AgZGVwYXJ0ZWQgZnJvbSBOZXcgWW9yayBDaXR5IGFuZCBhcmUgZG9tZXN0aWMgZmxpZ2h0cyBpbiB0aGUgVVMuIFRoaXMgbWVhbnMgdGhhdCBmbGlnaHRzIHdpbGwgYWxsIGJlIHRvIHRoZSBzYW1lIG9yIG1vcmUgd2VzdGVybHkgdGltZSB6b25lcy4gR2l2ZW4gdGhlIHRpbWUtem9uZXMgaW4gdGhlIFVTLCB0aGUgZGlmZmVyZW5jZXMgZHVlIHRvIHRpbWUtem9uZSBzaG91bGQgYmUgNjAgbWludXRlcyAoQ2VudHJhbCkgMTIwIG1pbnV0ZXMgKE1vdW50YWluKSwgMTgwIG1pbnV0ZXMgKFBhY2lmaWMpLCAyNDAgbWludXRlcyAoQWxhc2thKSwgb3IgMzAwIG1pbnV0ZXMgKEhhd2FpaSkuDQoNCkJvdGggb2YgdGhlc2UgZXhwbGFuYXRpb25zIGhhdmUgY2xlYXIgcGF0dGVybnMgdGhhdCBJIHdvdWxkIGV4cGVjdCB0byBzZWUgaWYgdGhleSB3ZXJlIHRydWUuIEluIHBhcnRpY3VsYXIsIGluIGJvdGggY2FzZXMsIHNpbmNlIHRpbWUtem9uZXMgYW5kIGNyb3NzaW5nIG1pZG5pZ2h0IG9ubHkgYWZmZWN0cyB0aGUgaG91ciBwYXJ0IG9mIHRoZSB0aW1lLCBhbGwgdmFsdWVzIG9mIGBhaXJfdGltZV9kaWZmYCBzaG91bGQgYmUgZGl2aXNpYmxlIGJ5IDYwLiBJ4oCZbGwgdmlzdWFsbHkgY2hlY2sgdGhpcyBoeXBvdGhlc2lzIGJ5IHBsb3R0aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgYGFpcl90aW1lX2RpZmZgLiBJZiB0aG9zZSB0d28gZXhwbGFuYXRpb25zIGFyZSBjb3JyZWN0LCBkaXN0cmlidXRpb24gb2YgYGFpcl90aW1lX2RpZmZgIHNob3VsZCBjb21wcmlzZSBvbmx5IHNwaWtlcyBhdCBtdWx0aXBsZXMgb2YgNjAuDQoNCmBgYHtyfQ0KZ2dwbG90KGZsaWdodHNfYWlydGltZSwgYWVzKHggPSBhaXJfdGltZV9kaWZmKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpDQpgYGANCg0KVGhpcyBpcyBub3QgdGhlIGNhc2UuIFdoaWxlLCB0aGUgZGlzdHJpYnV0aW9uIG9mIGBhaXJfdGltZV9kaWZmYCBoYXMgbW9kZXMgYXQgbXVsdGlwbGVzIG9mIDYwIGFzIGh5cG90aGVzaXplZCwgaXQgc2hvd3MgdGhhdCB0aGVyZSBhcmUgbWFueSBmbGlnaHRzIGluIHdoaWNoIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYWlyIHRpbWUgYW5kIGxvY2FsIGFycml2YWwgYW5kIGRlcGFydHVyZSB0aW1lcyBpcyBub3QgZGl2aXNpYmxlIGJ5IDYwLg0KDQpMZXTigJlzIGFsc28gbG9vayBhdCBmbGlnaHRzIHdpdGggTG9zIEFuZ2VsZXMgYXMgYSBkZXN0aW5hdGlvbi4gVGhlIGRpc2NyZXBhbmN5IHNob3VsZCBiZSAxODAgbWludXRlcy4NCg0KYGBge3J9DQpnZ3Bsb3QoZmlsdGVyKGZsaWdodHNfYWlydGltZSwgZGVzdCA9PSAiTEFYIiksIGFlcyh4ID0gYWlyX3RpbWVfZGlmZikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKQ0KYGBgDQoNClRvIGZpeCB0aGVzZSB0aW1lLXpvbmUgaXNzdWVzLCBJIHdvdWxkIHdhbnQgdG8gY29udmVydCBhbGwgdGhlIHRpbWVzIHRvIGEgZGF0ZS10aW1lIHRvIGhhbmRsZSBvdmVybmlnaHQgZmxpZ2h0cywgYW5kIGZyb20gbG9jYWwgdGltZSB0byBhIGNvbW1vbiB0aW1lIHpvbmUsIG1vc3QgbGlrZWx5IFtVVENdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Nvb3JkaW5hdGVkX1VuaXZlcnNhbF9UaW1lKSwgdG8gaGFuZGxlIGZsaWdodHMgY3Jvc3NpbmcgdGltZS16b25lcy4gVGhlIHR6b25lIGNvbHVtbiBvZiBueWNmbGlnaHRzMTM6OmFpcnBvcnRzIGdpdmVzIHRoZSB0aW1lLXpvbmUgb2YgZWFjaCBhaXJwb3J0LiBXZSdsbCB0YWxrIGEgbG90IG1vcmUgYWJvdXQgZGF0ZXMgYW5kIHRpbWVzIGxhdGVyLg0KDQpCdXQgdGhhdCBzdGlsbCBsZWF2ZXMgdGhlIG90aGVyIGRpZmZlcmVuY2VzIHVuZXhwbGFpbmVkLiBTbyB3aGF0IGVsc2UgbWlnaHQgYmUgZ29pbmcgb24/IFRoZXJlIHNlZW0gdG8gYmUgdG9vIG1hbnkgcHJvYmxlbXMgZm9yIHRoaXMgdG8gYmUgZGF0YSBlbnRyeSBwcm9ibGVtcywgc28gSeKAmW0gcHJvYmFibHkgbWlzc2luZyBzb21ldGhpbmcuIFNvLCBJ4oCZbGwgcmVyZWFkIHRoZSBkb2N1bWVudGF0aW9uIHRvIG1ha2Ugc3VyZSB0aGF0IEkgdW5kZXJzdGFuZCB0aGUgZGVmaW5pdGlvbnMgb2YgYGFycl90aW1lYCwgYGRlcF90aW1lYCwgYW5kIGBhaXJfdGltZWAuIFRoZSBkb2N1bWVudGF0aW9uIGNvbnRhaW5zIGEgbGluayB0byB0aGUgc291cmNlIG9mIHRoZSBmbGlnaHRzIGRhdGEsIDxodHRwczovL3d3dy50cmFuc3RhdHMuYnRzLmdvdi9ETF9TZWxlY3RGaWVsZHMuYXNwP1RhYmxlX0lEPTIzNj4uIFRoaXMgZG9jdW1lbnRhdGlvbiBzaG93cyB0aGF0IHRoZSBmbGlnaHRzIGRhdGEgZG9lcyBub3QgY29udGFpbiB0aGUgdmFyaWFibGVzIGBUYXhpSW5gLCBgVGF4aU9mZmAsIGBXaGVlbHNJbmAsIGFuZCBgV2hlZWxzT2ZmYC4gSXQgYXBwZWFycyB0aGF0IHRoZSBgYWlyX3RpbWVgIHZhcmlhYmxlIHJlZmVycyB0byBmbGlnaHQgdGltZSwgd2hpY2ggaXMgZGVmaW5lZCBhcyB0aGUgdGltZSBiZXR3ZWVuIHdoZWVscy1vZmYgKHRha2Utb2ZmKSBhbmQgd2hlZWxzLWluIChsYW5kaW5nKS4gQnV0IHRoZSBmbGlnaHQgdGltZSBkb2VzIG5vdCBpbmNsdWRlIHRpbWUgc3BlbnQgb24gdGhlIHJ1bndheSB0YXhpaW5nIHRvIGFuZCBmcm9tIGdhdGVzLiBXaXRoIHRoaXMgbmV3IHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRhdGEsIEkgbm93IGtub3cgdGhhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYGFpcl90aW1lYCwgYGFycl90aW1lYCwgYW5kIGBkZXBfdGltZWAgaXMgYGFpcl90aW1lIDw9IGFycl90aW1lIC0gZGVwX3RpbWVgLCBzdXBwb3NpbmcgdGhhdCB0aGUgdGltZSB6b25lcyBvZiBgYXJyX3RpbWVgIGFuZCBgZGVwX3RpbWVgIGFyZSBpbiB0aGUgc2FtZSB0aW1lIHpvbmUuDQoNCiMjIyAzLiBDb21wYXJlIGBkZXBfdGltZWAsIGBzY2hlZF9kZXBfdGltZWAsIGFuZCBgZGVwX2RlbGF5YC4gSG93IHdvdWxkIHlvdSBleHBlY3QgdGhvc2UgdGhyZWUgbnVtYmVycyB0byBiZSByZWxhdGVkPw0KDQpJIHdvdWxkIGV4cGVjdCB0aGUgZGVwYXJ0dXJlIGRlbGF5IChgZGVwX2RlbGF5YCkgdG8gYmUgZXF1YWwgdG8gdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBzY2hlZHVsZWQgZGVwYXJ0dXJlIHRpbWUgKGBzY2hlZF9kZXBfdGltZWApLCBhbmQgYWN0dWFsIGRlcGFydHVyZSB0aW1lIChgZGVwX3RpbWVgKSwgYGRlcF90aW1lIC0gc2NoZWRfZGVwX3RpbWUgPSBkZXBfZGVsYXlgLg0KDQpBcyB3aXRoIHRoZSBwcmV2aW91cyBxdWVzdGlvbiwgdGhlIGZpcnN0IHN0ZXAgaXMgdG8gY29udmVydCBhbGwgdGltZXMgdG8gdGhlIG51bWJlciBvZiBtaW51dGVzIHNpbmNlIG1pZG5pZ2h0LiBUaGUgY29sdW1uLCBgZGVwX2RlbGF5X2RpZmZgIHdpbGwgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBkZXBfZGVsYXkgYW5kIGRlcGFydHVyZSBkZWxheSBjYWxjdWxhdGVkIGZyb20gdGhlIHNjaGVkdWxlZCBhbmQgYWN0dWFsIGRlcGFydHVyZSB0aW1lcy4NCg0KYGBge3J9DQpmbGlnaHRzX2RlcHRpbWUgPC0NCiAgbXV0YXRlKGZsaWdodHMsDQogICAgZGVwX3RpbWVfbWluID0gKGRlcF90aW1lICUvJSAxMDAgKiA2MCArIGRlcF90aW1lICUlIDEwMCkgJSUgMTQ0MCwNCiAgICBzY2hlZF9kZXBfdGltZV9taW4gPSAoc2NoZWRfZGVwX3RpbWUgJS8lIDEwMCAqIDYwICsNCiAgICAgIHNjaGVkX2RlcF90aW1lICUlIDEwMCkgJSUgMTQ0MCwNCiAgICBkZXBfZGVsYXlfZGlmZiA9IGRlcF9kZWxheSAtIGRlcF90aW1lX21pbiArIHNjaGVkX2RlcF90aW1lX21pbg0KICApDQpgYGANCg0KRG9lcyBgZGVwX2RlbGF5X2RpZmZgIGVxdWFsIHplcm8gZm9yIGFsbCByb3dzPw0KDQpgYGB7cn0NCmZpbHRlcihmbGlnaHRzX2RlcHRpbWUsIGRlcF9kZWxheV9kaWZmICE9IDApDQpgYGANCg0KTm8uIFVubGlrZSB0aGUgbGFzdCBxdWVzdGlvbiwgdGltZSB6b25lcyBhcmUgbm90IGFuIGlzc3VlIHNpbmNlIHdlIGFyZSBvbmx5IGNvbnNpZGVyaW5nIGRlcGFydHVyZSB0aW1lcy4yIEhvd2V2ZXIsIHRoZSBkaXNjcmVwYW5jaWVzIGNvdWxkIGJlIGJlY2F1c2UgYSBmbGlnaHQgd2FzIHNjaGVkdWxlZCB0byBkZXBhcnQgYmVmb3JlIG1pZG5pZ2h0LCBidXQgd2FzIGRlbGF5ZWQgYWZ0ZXIgbWlkbmlnaHQuIEFsbCBvZiB0aGVzZSBkaXNjcmVwYW5jaWVzIGFyZSBleGFjdGx5IGVxdWFsIHRvIDE0NDAgKDI0IGhvdXJzKSwgYW5kIHRoZSBmbGlnaHRzIHdpdGggdGhlc2UgZGlzY3JlcGFuY2llcyB3ZXJlIHNjaGVkdWxlZCB0byBkZXBhcnQgbGF0ZXIgaW4gdGhlIGRheS4NCg0KYGBge3J9DQpnZ3Bsb3QoDQogIGZpbHRlcihmbGlnaHRzX2RlcHRpbWUsIGRlcF9kZWxheV9kaWZmID4gMCksDQogIGFlcyh5ID0gc2NoZWRfZGVwX3RpbWVfbWluLCB4ID0gZGVwX2RlbGF5X2RpZmYpDQopICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KVGh1cyB0aGUgb25seSBjYXNlcyBpbiB3aGljaCB0aGUgZGVwYXJ0dXJlIGRlbGF5IGlzIG5vdCBlcXVhbCB0byB0aGUgZGlmZmVyZW5jZSBpbiBzY2hlZHVsZWQgZGVwYXJ0dXJlIGFuZCBhY3R1YWwgZGVwYXJ0dXJlIHRpbWVzIGlzIGR1ZSB0byBhIHF1aXJrIGluIGhvdyB0aGVzZSBjb2x1bW5zIHdlcmUgc3RvcmVkLg0KDQojIyMgNCBGaW5kIHRoZSAxMCBtb3N0IGRlbGF5ZWQgZmxpZ2h0cyB1c2luZyBhIHJhbmtpbmcgZnVuY3Rpb24uIEhvdyBkbyB5b3Ugd2FudCB0byBoYW5kbGUgdGllcz8gQ2FyZWZ1bGx5IHJlYWQgdGhlIGRvY3VtZW50YXRpb24gZm9yIG1pbl9yYW5rKCkuDQoNClRoZSAqKmRwbHlyKiogcGFja2FnZSBwcm92aWRlcyBtdWx0aXBsZSBmdW5jdGlvbnMgZm9yIHJhbmtpbmcsIHdoaWNoIGRpZmZlciBpbiBob3cgdGhleSBoYW5kbGUgdGllZCB2YWx1ZXM6IGByb3dfbnVtYmVyKClgLCBgbWluX3JhbmsoKWAsIGBkZW5zZV9yYW5rKClgLiBUbyBzZWUgaG93IHRoZXkgd29yaywgbGV04oCZcyBjcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggZHVwbGljYXRlIHZhbHVlcyBpbiBhIHZlY3RvciBhbmQgc2VlIGhvdyByYW5raW5nIGZ1bmN0aW9ucyBoYW5kbGUgdGllcy4NCg0KYGBge3J9DQpyYW5rbWUgPC0gdGliYmxlKA0KICB4ID0gYygxMCwgNSwgMSwgNSwgNSkNCikNCnJhbmttZSA8LSBtdXRhdGUocmFua21lLA0KICB4X3Jvd19udW1iZXIgPSByb3dfbnVtYmVyKHgpLA0KICB4X21pbl9yYW5rID0gbWluX3JhbmsoeCksDQogIHhfZGVuc2VfcmFuayA9IGRlbnNlX3JhbmsoeCkNCikNCmFycmFuZ2UocmFua21lLCB4KQ0KYGBgDQoNClRoZSBmdW5jdGlvbiBgcm93X251bWJlcigpYCBhc3NpZ25zIGVhY2ggZWxlbWVudCBhIHVuaXF1ZSB2YWx1ZS4gVGhlIHJlc3VsdCBpcyBlcXVpdmFsZW50IHRvIHRoZSBpbmRleCAob3Igcm93KSBudW1iZXIgb2YgZWFjaCBlbGVtZW50IGFmdGVyIHNvcnRpbmcgdGhlIHZlY3RvciwgaGVuY2UgaXRzIG5hbWUuDQoNClRoZSBgbWluX3JhbmsoKWAgYW5kIGBkZW5zZV9yYW5rKClgIGFzc2lnbiB0aWVkIHZhbHVlcyB0aGUgc2FtZSByYW5rLCBidXQgZGlmZmVyIGluIGhvdyB0aGV5IGFzc2lnbiB2YWx1ZXMgdG8gdGhlIG5leHQgcmFuay4gRm9yIGVhY2ggc2V0IG9mIHRpZWQgdmFsdWVzIHRoZSBgbWluX3JhbmsoKWAgZnVuY3Rpb24gYXNzaWducyBhIHJhbmsgZXF1YWwgdG8gdGhlIG51bWJlciBvZiB2YWx1ZXMgbGVzcyB0aGFuIHRoYXQgdGllZCB2YWx1ZSBwbHVzIG9uZS4gSW4gY29udHJhc3QsIHRoZSBkZW5zZV9yYW5rKCkgZnVuY3Rpb24gYXNzaWducyBhIHJhbmsgZXF1YWwgdG8gdGhlIG51bWJlciBvZiBkaXN0aW5jdCB2YWx1ZXMgbGVzcyB0aGFuIHRoYXQgdGllZCB2YWx1ZSBwbHVzIG9uZS4gVG8gc2VlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYGRlbnNlX3JhbmsoKWAgYW5kIGBtaW5fcmFuaygpYCBjb21wYXJlIHRoZSB2YWx1ZSBvZiBgcmFua21lJHhfbWluX3JhbmtgIGFuZCBgcmFua21lJHhfZGVuc2VfcmFua2AgZm9yIGB4ID0gMTBgLg0KDQpJZiBJIGhhZCB0byBjaG9vc2Ugb25lIGZvciBwcmVzZW50aW5nIHJhbmtpbmdzIHRvIHNvbWVvbmUgZWxzZSwgSSB3b3VsZCB1c2UgYG1pbl9yYW5rKClgIHNpbmNlIGl0cyByZXN1bHRzIGNvcnJlc3BvbmQgdG8gdGhlIG1vc3QgY29tbW9uIHVzYWdlIG9mIHJhbmtpbmdzIGluIHNwb3J0cyBvciBvdGhlciBjb21wZXRpdGlvbnMuIEluIHRoZSBjb2RlIGJlbG93LCBJIHVzZSBhbGwgdGhyZWUgZnVuY3Rpb25zLCBidXQgc2luY2UgdGhlcmUgYXJlIG5vIHRpZXMgaW4gdGhlIHRvcCAxMCBmbGlnaHRzLCB0aGUgcmVzdWx0cyBkb27igJl0IGRpZmZlci4NCg0KYGBge3J9DQpmbGlnaHRzX2RlbGF5ZWQgPC0gbXV0YXRlKGZsaWdodHMsDQogIGRlcF9kZWxheV9taW5fcmFuayA9IG1pbl9yYW5rKGRlc2MoZGVwX2RlbGF5KSksDQogIGRlcF9kZWxheV9yb3dfbnVtYmVyID0gcm93X251bWJlcihkZXNjKGRlcF9kZWxheSkpLA0KICBkZXBfZGVsYXlfZGVuc2VfcmFuayA9IGRlbnNlX3JhbmsoZGVzYyhkZXBfZGVsYXkpKQ0KKQ0KZmxpZ2h0c19kZWxheWVkIDwtIGZpbHRlcigNCiAgZmxpZ2h0c19kZWxheWVkLA0KICAhKGRlcF9kZWxheV9taW5fcmFuayA+IDEwIHwgZGVwX2RlbGF5X3Jvd19udW1iZXIgPiAxMCB8DQogICAgZGVwX2RlbGF5X2RlbnNlX3JhbmsgPiAxMCkNCikNCmZsaWdodHNfZGVsYXllZCA8LSBhcnJhbmdlKGZsaWdodHNfZGVsYXllZCwgZGVwX2RlbGF5X21pbl9yYW5rKQ0KcHJpbnQoc2VsZWN0KA0KICBmbGlnaHRzX2RlbGF5ZWQsIG1vbnRoLCBkYXksIGNhcnJpZXIsIGZsaWdodCwgZGVwX2RlbGF5LA0KICBkZXBfZGVsYXlfbWluX3JhbmssIGRlcF9kZWxheV9yb3dfbnVtYmVyLCBkZXBfZGVsYXlfZGVuc2VfcmFuaw0KKSwNCm4gPSBJbmYNCikNCmBgYA0KDQpJbiBhZGRpdGlvbiB0byB0aGUgZnVuY3Rpb25zIGNvdmVyZWQgaGVyZSwgdGhlIGByYW5rKClgIGZ1bmN0aW9uIHByb3ZpZGVzIHNldmVyYWwgbW9yZSB3YXlzIG9mIHJhbmtpbmcgZWxlbWVudHMuDQoNCiMjIyA1IFdoYXQgZG9lcyBgMTozICsgMToxMGAgcmV0dXJuPyBXaHk/DQoNClRoZSBjb2RlIGdpdmVuIGluIHRoZSBxdWVzdGlvbiByZXR1cm5zIHRoZSBmb2xsb3dpbmcuDQoNCmBgYHtyfQ0KMTozICsgMToxMA0KYGBgDQoNClRoaXMgaXMgZXF1aXZhbGVudCB0byB0aGUgZm9sbG93aW5nLg0KDQpgYGB7cn0NCmMoMSArIDEsIDIgKyAyLCAzICsgMywgMSArIDQsIDIgKyA1LCAzICsgNiwgMSArIDcsIDIgKyA4LCAzICsgOSwgMSArIDEwKQ0KYGBgDQoNCldoZW4gYWRkaW5nIHR3byB2ZWN0b3JzIHJlY3ljbGVzIHRoZSBzaG9ydGVyIHZlY3RvcuKAmXMgdmFsdWVzIHRvIGdldCB2ZWN0b3JzIG9mIHRoZSBzYW1lIGxlbmd0aC4NCg0KVGhlIGNvZGUgYWxzbyBwcm9kdWNlcyBhIHdhcm5pbmcgdGhhdCB0aGUgc2hvcnRlciB2ZWN0b3IgaXMgbm90IGEgbXVsdGlwbGUgb2YgdGhlIGxvbmdlciB2ZWN0b3IuIEEgd2FybmluZyBpcyBwcm92aWRlZCBzaW5jZSBvZnRlbiwgYnV0IG5vdCBhbHdheXMsIHRoaXMgaW5kaWNhdGVzIGEgYnVnIGluIHRoZSBjb2RlLg0KDQojIyMgNiBXaGF0IHRyaWdvbm9tZXRyaWMgZnVuY3Rpb25zIGRvZXMgUiBwcm92aWRlPw0KDQpBbGwgdHJpZ29ub21ldHJpYyBmdW5jdGlvbnMgYXJlIGFsbCBkZXNjcmliZWQgaW4gYSBzaW5nbGUgaGVscCBwYWdlLCBuYW1lZCBgVHJpZ2AuIFlvdSBjYW4gb3BlbiB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhlc2UgZnVuY3Rpb25zIHdpdGggYD9UcmlnYCBvciBieSB1c2luZyBgP2Agd2l0aCBhbnkgb2YgdGhlIGZvbGxvd2luZyBmdW5jdGlvbnMsIGZvciBleGFtcGxlOiBgP3NpbmAuDQoNClIgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciB0aGUgdGhyZWUgcHJpbWFyeSB0cmlnb25vbWV0cmljIGZ1bmN0aW9uczogc2luZSAoYHNpbigpYCksIGNvc2luZSAoYGNvcygpYCksIGFuZCB0YW5nZW50IChgdGFuKClgKS4gVGhlIGlucHV0IGFuZ2xlcyB0byBhbGwgdGhlc2UgZnVuY3Rpb25zIGFyZSBpbiByYWRpYW5zLg0KDQpgYGB7cn0NCnggPC0gc2VxKC0zLCA3LCBieSA9IDEgLyAyKQ0Kc2luKHBpICogeCkNCmNvcyhwaSAqIHgpDQp0YW4ocGkgKiB4KQ0KYGBgDQoNCkluIHRoZSBwcmV2aW91cyBjb2RlLCBJIHVzZWQgdGhlIHZhcmlhYmxlIGBwaWAuIFIgcHJvdmlkZXMgdGhlIHZhcmlhYmxlIGBwaWAgd2hpY2ggaXMgc2V0IHRvIHRoZSB2YWx1ZSBvZiB0aGUgbWF0aGVtYXRpY2FsIGNvbnN0YW50ICRccGkkDQoNCmBgYHtyIHBpfQ0KcGkNCmBgYA0KDQpBbHRob3VnaCBSIHByb3ZpZGVzIHRoZSBgcGlgIHZhcmlhYmxlLCB0aGVyZSBpcyBub3RoaW5nIHByZXZlbnRpbmcgYSB1c2VyIGZyb20gY2hhbmdpbmcgaXRzIHZhbHVlIEZvciBleGFtcGxlLCBJIGNvdWxkIHJlZGVmaW5lIGBwaWAgdG8gMy4xNCBvciBhbnkgb3RoZXIgdmFsdWUuDQoNCmBgYHtyfQ0KcGkgPC0gMy4xNA0KcGkNCnBpIDwtICJBcHBsZSINCnBpDQpgYGANCg0KRm9yIHRoYXQgcmVhc29uLCBpZiB5b3UgYXJlIHVzaW5nIHRoZSBidWlsdGluIHBpIHZhcmlhYmxlIGluIGNvbXB1dGF0aW9ucyBhbmQgYXJlIHBhcmFub2lkLCB5b3UgbWF5IHdhbnQgdG8gYWx3YXlzIHJlZmVyZW5jZSBpdCBhcyBgYmFzZTo6cGlgLg0KDQpgYGB7cn0NCmJhc2U6OnBpDQpgYGANCg0KDQpJbiB0aGUgcHJldmlvdXMgY29kZSBibG9jaywgc2luY2UgdGhlIGFuZ2xlcyB3ZXJlIGluIHJhZGlhbnMsIEkgd3JvdGUgdGhlbSBhcyAkXHBpJCAgdGltZXMgc29tZSBudW1iZXIuIFNpbmNlIGl0IGlzIG9mdGVuIGVhc2llciB0byB3cml0ZSByYWRpYW5zIG11bHRpcGxlIG9mICRccGkkLCBSIHByb3ZpZGVzIHNvbWUgY29udmVuaWVuY2UgZnVuY3Rpb25zIHRoYXQgZG8gdGhhdC4gVGhlIGZ1bmN0aW9uIGBzaW5waSh4KWAsIGlzIGVxdWl2YWxlbnQgdG8gYHNpbihwaSAqIHgpYC4gVGhlIGZ1bmN0aW9ucyBgY29zcGkoKWAgYW5kIGB0YW5waSgpYCBhcmUgc2ltaWxhcmx5IGRlZmluZWQgZm9yIHRoZSBzaW4gYW5kIHRhbiBmdW5jdGlvbnMsIHJlc3BlY3RpdmVseS4NCg0KYGBge3J9DQpzaW5waSh4KQ0KY29zcGkoeCkNCnRhbnBpKHgpDQpgYGANCg0KUiBwcm92aWRlcyB0aGUgZnVuY3Rpb24gYXJjLWNvc2luZSAoYGFjb3MoKWApLCBhcmMtc2luZSAoYGFzaW4oKWApLCBhbmQgYXJjLXRhbmdlbnQgKGBhdGFuKClgKS4NCg0KYGBge3J9DQp4IDwtIHNlcSgtMSwgMSwgYnkgPSAxIC8gNCkNCmFjb3MoeCkNCmFzaW4oeCkNCmF0YW4oeCkNCmBgYA0KDQpGaW5hbGx5LCBSIHByb3ZpZGVzIHRoZSBmdW5jdGlvbiBgYXRhbjIoKWAuIENhbGxpbmcgYGF0YW4yKHksIHgpYCByZXR1cm5zIHRoZSBhbmdsZSBiZXR3ZWVuIHRoZSB4LWF4aXMgYW5kIHRoZSB2ZWN0b3IgZnJvbSBgKDAsMClgIHRvIGAoeCwgeSlgLg0KDQpgYGB7cn0NCmF0YW4yKGMoMSwgMCwgLTEsIDApLCBjKDAsIDEsIDAsIC0xKSkNCmBgYA==